From ff5ef727530ba350183a6f8feaa9f7db5b2de57e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 12 Jun 2013 21:11:12 +0100 Subject: [PATCH 0001/2656] Initial commit --- README.md | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..b29ba747 --- /dev/null +++ b/README.md @@ -0,0 +1,105 @@ + +**cocotb** is a coroutine based cosimulation library for writing VHDL and Verilog testbenches in Python. + +TODO +==== + + - [ ] Block align log messages that span multiple lines + - [ ] Common functions for dumping a diff, nicely coloured (see scapy.utils.hexdiff) + + +Rationale +========= +VHDL and Verilog are both unsuitable for writing complex testbenches. eRM, SystemVerilog and the various SV based methodologies have emerged to address this deficiency. These verification methodologies are large and cumbersome, requiring specialist knowledge, significant time investment and expensive toolchains to achieve satisfactory verification. The overhead of setting up testbenches is onerous so often designers write small throwaway testbenches at a block level and defer the majority of verification to a large system level testbench. Cocotb intends to bridge this gap in order to make block-level testing useful, reusable, fast, accessible to a much wider skill-set. The net effect is that bugs will be discovered earlier in the design cycle which equates to significant time and cost saving. + + +Overview +======== + + +A typical cocotb testbench requires no additional RTL code. The Design Under Test (DUT) is instantiated as the toplevel in the simulator without any wrapper code. Cocotb drives stimulus onto the inputs to the DUT (or further down the hierarchy) and monitors the outputs directly from Python. + +Cocotb comprises 3 layers: + +### GPI (Generic Procedural Interface) + +This layer abstracts the simulator language interface to provide a common set of functionality. Supports VHDL via VHPI and Verilog/SystemVerilog via VPI. Modelsim FLI may be supported in the future. + +### simulatormodule + +A CPython extension which utilises GPI to expose the simulator to Python. + +### cocotb Python package + +Python infrastructure including coroutine scheduler, base classes etc. The cocotb package provides a mechanism to traverse the hierarchy, query signal values, force signals to particular values and control simulator execution. + +Example +======= + +A simplistic example of generating a clock with 10ns period: +```python +@cocotb.coroutine +def clock(signal): + while True: + signal <= 1 + yield Timer(5) + signal <= 0 + yield Timer(5) +``` + +The python "yield" keyword is used to suspend the coroutine and return control back to the simulator until a condition is met. In this case, the condition is a simple timer that fires after 5ns of simulation time. + +This may seem pretty pointless, in fact a Verilog process to perform the same operation requires fewer lines of code. Let's examine a more useful example: + +```python +@cocotb.coroutine +def pcap_replay(bus, filename): + prevtime = 0 + for timestamp, packet in dpkt.pcap.Reader(open(filename, 'r')): + yield Timer((timestamp - prevtime) * 1000) + yield bus.send(packet) +``` + +Here we utilise a thirdparty python library called dpkt to iterate over packets in a PCAP packet capture. This becomes trivially easy in Python when compared to a VHDL/Verilog/SystemVerilog implementation. The argument "bus" is an instance of a Driver class which provides a send method to serialise the packet transaction over multiple cycles on the bus. Although the majority of the complexity is hidden in this (reusable) Driver class the actual implementation is once again straightforward: + +```python +def send(self, packet): + + words = len(packet) / (len(self.data) / 8) + + yield RisingEdge(self.clk) # Synchronise to bus clock + self.sop <= 1 # First word is start-of-packet + self.valid <= 1 + + for index, word in enumerate(packet): + self.data <= word + yield rising_edge(self.clk) + self.sop <= 0 + if index == words - 1: + self.eop <= 1 + self.len <= len(word) + + yield rising_edge(self.clk) + self.eop <= 0 + self.valid <= 0 +``` + +Advantages +========== +* Low overhead to creating testbenches to facilitate block-level verification +* Favourable learning curve compared to eRM/OVM/UVM +* Leverage existing Python and C libraries easily +* Multi-language (Verilog/VHDL) and cross multi-simulator compatible +* Supports directed and randomised testing + + +Disadvantages +============= +* Simulation is slower than native language testbench +* Non-standard + + +Similar projects +================ + +Several examples exist of projects providing a VPI interface into a language other than C and (most notably http://pyhvl.sourceforge.net/ and http://snk.tuxfamily.org/lib/ruby-vpi/). MyHDL (http://www.myhdl.org/) seeks to displace VHDL and Verilog with a Python framework that can generate synthesiable RTL. From 55023b9644aaf0574579edc07e30a1166f166550 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 12 Jun 2013 21:19:08 +0100 Subject: [PATCH 0002/2656] Add code --- MANIFEST.in | 10 + Makefile | 63 ++ bin/cocotbenv.py | 129 ++++ bin/create_project.py | 48 ++ cocotb/ANSI.py | 51 ++ cocotb/__init__.py | 123 ++++ cocotb/binary.py | 169 +++++ cocotb/bus.py | 63 ++ cocotb/clock.py | 57 ++ cocotb/decorators.py | 109 ++++ cocotb/drivers/__init__.py | 226 +++++++ cocotb/generators/__init__.py | 0 cocotb/generators/byte_generators.py | 51 ++ cocotb/generators/feeds/__init__.py | 60 ++ cocotb/generators/feeds/itch_40.py | 326 ++++++++++ cocotb/generators/feeds/itch_feed.py | 65 ++ cocotb/generators/feeds/moldudp64.py | 87 +++ cocotb/generators/feeds/opra_binary.py | 221 +++++++ cocotb/generators/feeds/packet_util.py | 94 +++ .../opra_binary_payload_generators.py | 57 ++ cocotb/generators/packet_generators.py | 75 +++ cocotb/handle.py | 120 ++++ cocotb/monitors/__init__.py | 205 ++++++ cocotb/scheduler.py | 144 +++++ cocotb/scoreboard.py | 82 +++ cocotb/triggers.py | 244 +++++++ cocotb/utils.py | 213 +++++++ embed/Makefile | 53 ++ embed/gpi_embed.c | 207 ++++++ examples/Makefile | 39 ++ examples/comb_seq/Makefile | 35 + examples/comb_seq/issue12.py | 123 ++++ examples/comb_seq/issue12.tcl | 43 ++ examples/comb_seq/issue12.v | 63 ++ examples/demo/Makefile | 36 ++ examples/demo/counter.v | 87 +++ examples/demo/demo.py | 103 +++ examples/demo/demo.tcl | 43 ++ examples/demo/library.cfg | 2 + examples/simple/Makefile | 36 ++ examples/simple/packet_tap.v | 111 ++++ examples/simple/smoketest.py | 83 +++ gpi/Makefile | 46 ++ gpi/gpi.h | 148 +++++ gpi/gpi_logging.c | 132 ++++ gpi/gpi_logging.h | 78 +++ makefiles/Makefile.aldec | 97 +++ makefiles/Makefile.doc | 50 ++ makefiles/Makefile.icarus | 53 ++ makefiles/Makefile.inc | 43 ++ makefiles/Makefile.paths | 30 + makefiles/Makefile.pylib | 77 +++ makefiles/Makefile.sim | 41 ++ modules/__init__.py | 0 modules/sf_streaming/__init__.py | 0 modules/sf_streaming/model/__init__.py | 0 modules/sf_streaming/model/sf_streaming.py | 202 ++++++ scripts/set_env.sh | 40 ++ setup.py | 33 + simulator/Makefile | 47 ++ simulator/simulatormodule.c | 598 ++++++++++++++++++ simulator/simulatormodule.h | 96 +++ vpi_shim/Makefile | 46 ++ vpi_shim/gpi_vpi.c | 560 ++++++++++++++++ 64 files changed, 6573 insertions(+) create mode 100644 MANIFEST.in create mode 100644 Makefile create mode 100755 bin/cocotbenv.py create mode 100755 bin/create_project.py create mode 100644 cocotb/ANSI.py create mode 100644 cocotb/__init__.py create mode 100644 cocotb/binary.py create mode 100644 cocotb/bus.py create mode 100644 cocotb/clock.py create mode 100644 cocotb/decorators.py create mode 100644 cocotb/drivers/__init__.py create mode 100644 cocotb/generators/__init__.py create mode 100644 cocotb/generators/byte_generators.py create mode 100644 cocotb/generators/feeds/__init__.py create mode 100644 cocotb/generators/feeds/itch_40.py create mode 100644 cocotb/generators/feeds/itch_feed.py create mode 100644 cocotb/generators/feeds/moldudp64.py create mode 100644 cocotb/generators/feeds/opra_binary.py create mode 100644 cocotb/generators/feeds/packet_util.py create mode 100644 cocotb/generators/opra_binary_payload_generators.py create mode 100644 cocotb/generators/packet_generators.py create mode 100644 cocotb/handle.py create mode 100644 cocotb/monitors/__init__.py create mode 100644 cocotb/scheduler.py create mode 100644 cocotb/scoreboard.py create mode 100644 cocotb/triggers.py create mode 100644 cocotb/utils.py create mode 100644 embed/Makefile create mode 100644 embed/gpi_embed.c create mode 100644 examples/Makefile create mode 100644 examples/comb_seq/Makefile create mode 100644 examples/comb_seq/issue12.py create mode 100644 examples/comb_seq/issue12.tcl create mode 100644 examples/comb_seq/issue12.v create mode 100644 examples/demo/Makefile create mode 100644 examples/demo/counter.v create mode 100644 examples/demo/demo.py create mode 100644 examples/demo/demo.tcl create mode 100644 examples/demo/library.cfg create mode 100644 examples/simple/Makefile create mode 100644 examples/simple/packet_tap.v create mode 100644 examples/simple/smoketest.py create mode 100644 gpi/Makefile create mode 100644 gpi/gpi.h create mode 100644 gpi/gpi_logging.c create mode 100644 gpi/gpi_logging.h create mode 100644 makefiles/Makefile.aldec create mode 100644 makefiles/Makefile.doc create mode 100644 makefiles/Makefile.icarus create mode 100644 makefiles/Makefile.inc create mode 100644 makefiles/Makefile.paths create mode 100644 makefiles/Makefile.pylib create mode 100644 makefiles/Makefile.sim create mode 100644 modules/__init__.py create mode 100644 modules/sf_streaming/__init__.py create mode 100644 modules/sf_streaming/model/__init__.py create mode 100644 modules/sf_streaming/model/sf_streaming.py create mode 100755 scripts/set_env.sh create mode 100644 setup.py create mode 100644 simulator/Makefile create mode 100644 simulator/simulatormodule.c create mode 100644 simulator/simulatormodule.h create mode 100644 vpi_shim/Makefile create mode 100644 vpi_shim/gpi_vpi.c diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 00000000..75c6c5c8 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,10 @@ +recursive-include bin * +recursive-include cocotb *.py *.c Makefile +recursive-include gpi *.c *.h Makefile +recursive-include vpi_shim *.c Makefile +recursive-include examples *.v *.py Makefile *.tcl *.cfg +recursive-include scripts * +recursive-include simulator *.c *.h Makefile +recursive-include modules *.py +recursive-include makefiles Makefile.* +include Makefile README.md diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..e461e6c7 --- /dev/null +++ b/Makefile @@ -0,0 +1,63 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +include makefiles/Makefile.inc + +export BUILD_DIR=$(shell pwd)/build + +INSTALL_DIR:=/tmp/test-install + +LIBS:= simulator embed vpi_shim gpi + +.PHONY: $(LIBS) + +all: $(LIBS) + +$(LIBS): dirs + $(MAKE) -C $@ + +vpi_shim: gpi +simulator: vpi_shim + +dirs: + @mkdir -p $(LIB_DIR) + +clean: + @rm -rf $(BUILD_DIR) + @find . -name "obj" | xargs rm -rf + +test: + $(MAKE) -C examples + +pycode: + easy_install --install-dir=$(INSTALL_DIR) $(SIM_ROOT) + +lib_install: libs + -mkdir -p $(INSTALL_DIR)/lib + cp -R $(LIB_DIR)/* $(INSTALL_DIR)/lib + +install: all lib_install pycode diff --git a/bin/cocotbenv.py b/bin/cocotbenv.py new file mode 100755 index 00000000..92e89eab --- /dev/null +++ b/bin/cocotbenv.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" + Determines how to execute the cocotb world +""" + +import os, sys, inspect +from subprocess import call + +class SimType(): + def __init__(self, name=None): + self._name = name; + + def execute(self): + print("Running cocotb against %s simulator" % self._name) + +class SimIcarus(SimType): + def __init__(self, name=None): + SimType.__init__(self, "Icarus") + self._base_cmd = ' vvp -m cocotb' + + def execute(self, py_path, lib_path, module, function, finput): + SimType.execute(self) + cmd = 'PYTHONPATH=' + py_path + cmd = cmd + ' LD_LIBRARY_PATH=' + lib_path + cmd = cmd + ' MODULE=' + module + cmd = cmd + ' FUNCTION=' + function + cmd = cmd + self._base_cmd + cmd = cmd + ' -M ' + lib_path + cmd = cmd + ' ' + finput + print(cmd) + call(cmd, shell=True) + +def main(): + + """ Start the desired simulator with cocotb embedded within. + +Options: + -h, --help Show this message and exit + -f, --function Function within Module to execute + -m, --module Module to load with test case + -s, --simtype The Simulator to use for execution + -i, --input Input file, type depends on simtype +""" + + from optparse import OptionParser + parser = OptionParser() + parser.add_option("-m", "--module", dest="module_str", + help="Module to load with test case") + parser.add_option("-f", "--function", dest="function_str", + help="Function within Module to execute") + parser.add_option("-s", "--simtype", dest="sim_str", + help="The Simulator to use for execution") + parser.add_option("-i", "--input", dest="input_str", + help="Input file, type depends on simtype") + + (options, args) = parser.parse_args() + + if not options.module_str: + print main.__doc__ + sys.exit(1) + + if not options.function_str: + print main.__doc__ + sys.exit(1) + + if not options.sim_str: + print main.__doc__ + sys.exit(1) + + if not options.input_str: + print main.__doc__ + sys.exit(1) + + class_name_l = options.sim_str.lower() + class_name = 'Sim' + class_name_l[0].upper() + class_name_l[1:] + + try: + ctype = globals()[class_name] + except KeyError as e: + print ("Specified name is not valid (%s)" % class_name) + sys.exit(1) + + # Get the library paths from the current location of the cocotbenv binary + # and the arch that it is running on + exec_path = inspect.getfile(inspect.currentframe()) + base_path = exec_path.split('/bin', 1)[0] + lib_path = base_path + '/build/libs/i686' + py_path = base_path + py_path = py_path + ':' + lib_path + py_path = py_path + ':' + os.getcwd() + + # Add the module and function as command line options that are passed + # as env vars to the underlying code + + sim_world = ctype() + sim_world.execute(py_path, + lib_path, + options.module_str, + options.function_str, + options.input_str) + +if __name__ == "__main__": + main() diff --git a/bin/create_project.py b/bin/create_project.py new file mode 100755 index 00000000..b3319884 --- /dev/null +++ b/bin/create_project.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" +Script takes a list of source files and outputs a TCL script suitable +for compiling using Aldec Riviera + +Note we only handle the file-list here, any PLI / coverage is done by the Makefile + +""" +import sys + +_DEBUG=__debug__ + +_debug_str = "-dbg" if _DEBUG else "" + +def riviera(source_files): + for filename in source_files: + print "alog %s %s" % (_debug_str, filename) + + +if __name__ == "__main__": + riviera(sys.argv[1:]) + diff --git a/cocotb/ANSI.py b/cocotb/ANSI.py new file mode 100644 index 00000000..3c70f1d4 --- /dev/null +++ b/cocotb/ANSI.py @@ -0,0 +1,51 @@ +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +''' + Some constants for doing ANSI stuff. +''' + +_ESCAPE = "\033[" + + +BLACK_FG = _ESCAPE + "30m" +RED_FG = _ESCAPE + "31m" +GREEN_FG = _ESCAPE + "32m" +YELLOW_FG = _ESCAPE + "33m" +BLUE_FG = _ESCAPE + "34m" +MAGENTA_FG = _ESCAPE + "35m" +CYAN_FG = _ESCAPE + "36m" +WHITE_FG = _ESCAPE + "37m" +DEFAULT_FG = _ESCAPE + "39m" + +BLACK_BG = _ESCAPE + "40m" +RED_BG = _ESCAPE + "41m" +GREEN_BG = _ESCAPE + "42m" +YELLOW_BG = _ESCAPE + "43m" +BLUE_BG = _ESCAPE + "44m" +MAGENTA_BG = _ESCAPE + "45m" +CYAN_BG = _ESCAPE + "46m" +WHITE_BG = _ESCAPE + "47m" +DEFAULT_BG = _ESCAPE + "49m" diff --git a/cocotb/__init__.py b/cocotb/__init__.py new file mode 100644 index 00000000..a917c841 --- /dev/null +++ b/cocotb/__init__.py @@ -0,0 +1,123 @@ +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" + TODO: + Seed random from environment variable if provided +""" +import os +import sys +import logging +import threading +from functools import wraps + +import ANSI +import cocotb.handle +from cocotb.scheduler import Scheduler +import simulator + +log = logging.getLogger('cocotb') +log.setLevel(logging.INFO) + + +class SimLogFormatter(logging.Formatter): + + """Log formatter to provide consistent log message handling. + + TODO: + - Move somewhere sensible + """ + loglevel2colour = { + logging.DEBUG : "%s", + logging.INFO : ANSI.BLUE_FG + "%s" + ANSI.DEFAULT_FG, + logging.WARNING : ANSI.YELLOW_FG + "%s" + ANSI.DEFAULT_FG, + logging.ERROR : ANSI.RED_FG + "%s" + ANSI.DEFAULT_FG, + logging.CRITICAL: ANSI.RED_BG + ANSI.BLACK_FG + "%s" + ANSI.DEFAULT_FG + ANSI.DEFAULT_BG} + + + def format(self, record): + """pretify the log output, annotate with simulation time""" + if record.args: msg = record.msg % record.args + else: msg = record.msg + + level = record.levelname.ljust(8) + + msg = SimLogFormatter.loglevel2colour[record.levelno] % msg + level = SimLogFormatter.loglevel2colour[record.levelno] % record.levelname.ljust(8) + + timeh, timel = simulator.get_sim_time() + simtime = "% 6d.%02dns" % ((timel/1000), (timel%1000)/10) + + prefix = simtime + ' ' + level + record.name.ljust(35) + os.path.split(record.filename)[1].rjust(20) + ':' + str(record.lineno).ljust(4) + ' in ' + str(record.funcName).ljust(25) + ' ' + pad = "\n" + " " * (len(prefix) - 10) + return prefix + pad.join(msg.split('\n')) + +hdlr = logging.StreamHandler(sys.stdout) +hdlr.setFormatter(SimLogFormatter()) +log.addHandler(hdlr) + +# FIXME is this really required? +_rlock = threading.RLock() + +# Singleton scheduler instance +# TODO: Ensure we only ever have a single scheduler +scheduler = Scheduler() + +def _initialise_testbench(root_handle): + """ + This function is called after the simulator has elaborated all + entities and is ready to run the test. + + The test must be defined by the environment variables + MODULE + FUNCTION + + """ + _rlock.acquire() + + def my_import(name): + mod = __import__(name) + components = name.split('.') + for comp in components[1:]: + mod = getattr(mod, comp) + return mod + + # Create the base handle type + dut = cocotb.handle.SimHandle(root_handle) + module_str = os.getenv('MODULE') + function_str = os.getenv('FUNCTION') + + if not module_str or not function_str: + raise ImportError("Environment variables with test information not provided. MODULE=\"%s\" and FUNCTION=\"%s\"" % (module_str, function_str)) + + testmod = my_import(module_str) + log.info("Starting testcase %s.%s" % (testmod.__name__, function_str)) + + coroutine = getattr(testmod, function_str)(dut) + log.debug("Got %s %s" % (coroutine.__name__, str(coroutine))) + + scheduler.add(coroutine) + _rlock.release() + return True diff --git a/cocotb/binary.py b/cocotb/binary.py new file mode 100644 index 00000000..05c1da73 --- /dev/null +++ b/cocotb/binary.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +def resolve(string): + for char in "xXzZ": + string = string.replace(char, "0") + return string + + +class BinaryValue(object): + """Represenatation of values in binary format. + + The underlying value can be set or accessed using three aliasing attributes, + + - BinaryValue.value is an integer + - BinaryValue.binstr is a string of "01xXzZ" + - BinaryValue.buff is a binary buffer of bytes + + For example: + + >>> vec = BinaryValue() + >>> vec.value = 42 + >>> print vec.binstr + 101010 + >>> print repr(vec.buff) + '*' + + """ + _permitted_chars = "01xXzZ" + + + def __init__(self, value=None, bits=None): + self._str = "" + self._bits = bits + + if value is not None: + self.assign(value) + + def assign(self, value): + """Decides how best to assign the value to the vector + + We possibly try to be a bit too clever here by first of + all trying to assign the raw string as a binstring, however + if the string contains any characters that aren't 0, 1, X or Z + then we interpret the string as a binary buffer... + """ + if isinstance(value, int): + self.value = value + elif isinstance(value, str): + try: + self.binstr = value + except ValueError: + self.buff = value + + def get_value(self): + """value is an integer representaion of the underlying vector""" + return int(resolve(self._str), 2) + + def set_value(self, integer): + self._str = "{:b}".format(integer) + self._adjust() + + value = property(get_value, set_value, None, "Integer access to the value") + + def get_buff(self): + """Attribute self.buff represents the value as a binary string buffer + e.g. vector "0000000100011111".buff == "\x01\x1F" + TODO: Doctest this! + """ + bits = self._str + if len(bits) % 8: bits = "0" * (8 - len(bits) % 8) + bits + bits = resolve(bits) + buff = "" + while bits: + byte = bits[:8] + bits = bits[8:] + val = int(byte, 2) + buff += chr(val) + return buff + + def set_buff(self, buff): + self._str = "" + for char in buff: + self._str = "{:08b}".format(ord(char)) + self._str + self._adjust() + + buff = property(get_buff, set_buff, None, "Access to the value as a buffer") + + def get_binstr(self): + """Attribute binstr is the binary representation stored as a string of 1s and 0s""" + return self._str + + def set_binstr(self, string): + for char in string: + if char not in BinaryValue._permitted_chars: + raise ValueError("Attempting to assign character %s to a %s" % (char, self.__class__.__name__)) + self._str = string + self._adjust() + + binstr = property(get_binstr, set_binstr, None, "Access to the binary string") + + def _adjust(self): + """Pad/truncate the bit string to the correct length""" + if self._bits is None: + return + l = len(self._str) + if l < self._bits: + self._str = "0" * (l-self._bits) + self._str + elif l > self._bits: + print "WARNING truncating value to match requested number of bits (%d)" % l + self._str = self._str[l - self._bits:] + + def __le__(self, other): + self.assign(other) + + def __str__(self): + return "%d" % (self.value) + + def __nonzero__(self): + """Provide boolean testing of a binstr. + + >>> val = BinaryValue("0000") + >>> if val: print "True" + ... else: print "False" + False + >>> val.value = 42 + >>> if val: print "True" + ... else: print "False" + True + + """ + for char in self._str: + if char != "0": return True + return False + + def __cmp__(self, other): + """Comparison against other values""" + if isinstance(other, BinaryValue): + other = other.value + return self.value.__cmp__(other) + + +if __name__ == "__main__": + import doctest + doctest.testmod() diff --git a/cocotb/bus.py b/cocotb/bus.py new file mode 100644 index 00000000..2406f739 --- /dev/null +++ b/cocotb/bus.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" + Common bus related functionality + + A bus is simply defined as a collection of signals +""" + +class Bus(object): + """ + Wraps up a collection of signals + """ + def __init__(self, entity, name, signals): + """ + entity: SimHandle instance to the entity containing the bus + name: name of the bus + signals: array of signal names + """ + for signal in signals: + signame = name + "_" + signal + setattr(self, signal, getattr(entity, signame)) + + self._signals = {} + self._signals[signal] = getattr(self, signal) + self._entity = entity + self._name = name + + def drive(self, obj): + """ + Drives values onto the bus. + + obj is an object with attribute names that match the bus signals + """ + for name, hdl in self._signals.items(): + if not hasattr(obj, name): + raise AttributeError("Unable to drive onto %s.%s because %s is missing attribute %s" % + (self._entity.name, self._name, obj.__class__.__name__, name)) + hdl <= getattr(obj, name) diff --git a/cocotb/clock.py b/cocotb/clock.py new file mode 100644 index 00000000..407a33fd --- /dev/null +++ b/cocotb/clock.py @@ -0,0 +1,57 @@ +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" + A clock class +""" +import logging +import simulator + + +class BaseClock(object): + """Base class to derive from""" + def __init__(self, signal): + self.signal = signal + self.log = logging.getLogger("cocotb.%s.%s" % (self.__class__.__name__, self.signal.name)) + +class Clock(BaseClock): + """ + simple 50:50 duty cycle clock + """ + def __init__(self, signal, period): + BaseClock.__init__(self, signal) + self.period = period + self.frequency = 1.0 / period * 1000000 + + + def start(self, cycles=0): + self.hdl = simulator.create_clock(self.signal._handle, self.period, cycles) + self.log.debug("Clock %s Started with period %d" % (str(self.signal), self.period)) + + def stop(self): + simulator.stop_clock(self.hdl) + + def __str__(self): + return self.__class__.__name__ + "(%3.1fMHz)" % self.frequency diff --git a/cocotb/decorators.py b/cocotb/decorators.py new file mode 100644 index 00000000..d4b3493e --- /dev/null +++ b/cocotb/decorators.py @@ -0,0 +1,109 @@ +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +import logging + +from cocotb.triggers import Join + + +class CoroutineComplete(StopIteration): + """ + To ensure that a coroutine has completed before we fire any triggers that + are blocked waiting for the coroutine to end, we create a subclass exception + that the Scheduler catches and the callbacks are attached here. + """ + def __init__(self, text="", callback=None): + StopIteration.__init__(self, text) + self.callback = callback + + def __call__(self): + if self.callback is not None: self.callback() + + + +class coroutine(object): + """Decorator class that allows us to provide common coroutine mechanisms: + + log methods will will log to cocotb.coroutines.name + + join() method returns an event which will fire when the coroutine exits + """ + + def __init__(self, func): + self._func = func + self.__name__ = func.__name__ # FIXME should use functools.wraps? + self._callbacks = [] + self.log = logging.getLogger("cocotb.coroutine.%s" % func.__name__) + self._finished = False + + def __call__(self, *args, **kwargs): + self._coro = self._func(*args, **kwargs) + return self + + def __get__(self, obj, type=None): + """Permit the decorator to be used on class methods + and standalone functions""" + return self.__class__(self._func.__get__(obj, type)) + + def __iter__(self): return self + + def next(self): + try: + return self._coro.next() + except StopIteration: + raise CoroutineComplete(callback=self._finished_cb) + + def send(self, value): + """FIXME: problem here is that we don't let the call stack unwind...""" + try: + return self._coro.send(value) + except StopIteration: + raise CoroutineComplete(callback=self._finished_cb) + + def throw(self, exc): + return self._coro.throw(exc) + + def kill(self): + self.log.warning("kill() called on coroutine") + self.throw(StopIteration) + + def _finished_cb(self): + """Called when the coroutine completes. + Allows us to mark the coroutine as finished so that boolean testing works. + Also call any callbacks, usually the result of coroutine.join()""" + self._finished = True + self.log.debug("Coroutine finished calling pending callbacks (%d pending)" % len(self._callbacks)) + for cb in self._callbacks: + cb() + + def join(self): + """Return a trigger that will fire when the wrapped coroutine exits""" + return Join(self) + + def __nonzero__(self): + """Provide boolean testing + if the coroutine has finished return false + otherwise return true""" + return not self._finished diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py new file mode 100644 index 00000000..cac92504 --- /dev/null +++ b/cocotb/drivers/__init__.py @@ -0,0 +1,226 @@ +#!/bin/env python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" + + Class defining the standard interface for a driver within a testbench + + The driver is responsible for serialising transactions onto the physical pins + of the interface. This may consume simulation time. + +""" + +import logging +import math + +import cocotb +from cocotb.decorators import coroutine +from cocotb.triggers import Edge, Event, RisingEdge +from cocotb.bus import Bus + +class Driver(object): + + + def __init__(self): + """ + Constructor for a driver instance + """ + #self._busy = Lock() + self._pending = Event(name="Driver._pending") + self._sendQ = [] + + # Subclasses may already set up logging + if not hasattr(self, "log"): + self.log = logging.getLogger("cocotb.driver.%s" % (self.__class__.__name__)) + + # Create an independent coroutine which can send stuff + self._thread = cocotb.scheduler.add(self._send_thread()) + + + def kill(self): + if self._thread: + self._thread.kill() + self._thread = None + + def append(self, transaction, callback=None, event=None): + """ + Queue up a transaction to be sent over the bus. + + Mechanisms are provided to permit the caller to know when the transaction is processed + + callback: optional function to be called when the transaction has been sent + + event: event to be set when the tansaction has been sent + """ + self._sendQ.append((transaction, callback, event)) + self._pending.set() + + @coroutine + def send(self, transaction): + """ + Blocking send call (hence must be "yielded" rather than called) + + Sends the transaction over the bus + """ + #yield self.busy.acquire() + yield self._send(transaction, None, None) + + + def _driver_send(self, transaction): + """ + actual impementation of the send. + + subclasses should override this method to implement the actual send routine + """ + pass + + + @coroutine + def _send(self, transaction, callback, event): + """ + assumes the caller has already acquired the busy lock + + releases busy lock once sending is complete + """ + yield self._driver_send(transaction) + + # Notify the world that this transaction is complete + if event: event.set() + if callback: callback(transaction) + + # No longer hogging the bus + #self.busy.release() + + @coroutine + def _send_thread(self): + while True: + + # Sleep until we have something to send + while not self._sendQ: + yield self._pending.wait() + + transaction, callback, event = self._sendQ.pop(0) + # Send the pending transaction + self.log.info("Sending packet...") + yield self._send(transaction, callback, event) + self.log.info("Done, shouldn't be waiting on _send.join() anymore..") + + +class BusDriver(Driver): + """Tickets please! + + Wrapper around common functionality for busses which have: + a list of _signals (class attribute) + a clock + a name + an entity + """ + + def __init__(self, entity, name, clock): + self.log = logging.getLogger("cocotb.%s.%s" % (entity.name, name)) + Driver.__init__(self) + self.entity = entity + self.name = name + self.clock = clock + self.bus = Bus(self.entity, self.name, self._signals) + + +class AvalonMM(BusDriver): + """Avalon-MM Driver + + Currently we only support the mode required to communicate with SF avalon_mapper which + is a limited subset of all the signals + + + This needs some thought... do we do a transaction based mechanism or 'blocking' read/write calls? + """ + _signals = ["readdata", "read", "write", "waitrequest", "writedata", "address"] + + def __init__(self, entity, name, clock): + BusDriver.__init__(self, entity, name, clock) + + # Drive some sensible defaults + self.bus.read <= 0 + self.bus.write <= 0 + + + def read(self, address): + """ + """ + pass + + def write(self, address): + """ + """ + pass + + + + +class AvalonST(Driver): + _signals = ["valid", "data"] + +class SFStreaming(BusDriver): + """This is the Solarflare Streaming bus as defined by the FDK. + + Expect to see a 72-bit bus (bottom 64 bits data, top 8 bits are ECC) + """ + _signals = AvalonST._signals + ["startofpacket", "endofpacket", "ready", "empty", "channel", "error"] + + def __init__(self, entity, name, clock): + BusDriver.__init__(self, entity, name, clock) + + # Drive some sensible defaults onto the bus + self.bus.startofpacket <= 0 + self.bus.endofpacket <= 0 + self.bus.valid <= 0 + self.bus.empty <= 0 + self.bus.channel <= 0 + self.bus.error <= 0 + + @coroutine + def _driver_send(self, sfpkt): + """Send a packet over the bus + + sfpkt should be an instance of SFStreamingPacket + """ + # Avoid spurious object creation by recycling + clkedge = RisingEdge(self.clock) + + self.log.info("Sending packet of length %d bytes" % len(sfpkt)) + + for word in sfpkt: + yield clkedge + while self.bus.ready != 1: + yield clkedge + self.bus.drive(word) + + yield clkedge + self.bus.endofpacket <= 0 + self.bus.valid <= 0 + + self.log.info("Packet sent successfully") diff --git a/cocotb/generators/__init__.py b/cocotb/generators/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/cocotb/generators/byte_generators.py b/cocotb/generators/byte_generators.py new file mode 100644 index 00000000..b1f93ad2 --- /dev/null +++ b/cocotb/generators/byte_generators.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + + +""" + Collection of generators for creating byte streams +""" +import random + + +def random_data(): + """Random bytes""" + while True: + yield chr(random.randint(0,255)) + +def incrementing_data(increment=1): + """Incrementing bytes""" + val = 0 + while True: + yield chr(val) + val += increment + val = val & 0xFF + +def repeating_bytes(pattern="\x00"): + """Repeat a pattern of bytes""" + while True: + for byte in pattern: yield byte diff --git a/cocotb/generators/feeds/__init__.py b/cocotb/generators/feeds/__init__.py new file mode 100644 index 00000000..7bc7df90 --- /dev/null +++ b/cocotb/generators/feeds/__init__.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +# Set log level to benefit from Scapy warnings +import logging +logging.getLogger("scapy").setLevel(1) + +import cocotb +from scapy.all import * + +class Feed(object): + def __init__(self, name, filepath=None): + self.name = name + self._packets = {} + self._filepath = filepath + self.fullname = '\'' + self.name + '\'' + self.log = logging.getLogger('cocotb.' + self.name) + if self._filepath: + self._source = open(self._filepath) + self.log.debug("loaded file %s" % self._filepath) + self.log.debug("Created feed!") + + def addmsg(self, tag, data): + """ Add a defined message to the internal feed store """ + self._packets[tag] = data + + def getmsg(self): + """ Get a string representation of the current list head + This packet will be ready to send + """ + if self._packets: + tag, packet = self._packets.popitem() + return str(packet) + else: + self.log.warn("No packets in feed %s" % self.fullname) + return None + diff --git a/cocotb/generators/feeds/itch_40.py b/cocotb/generators/feeds/itch_40.py new file mode 100644 index 00000000..90ec00ad --- /dev/null +++ b/cocotb/generators/feeds/itch_40.py @@ -0,0 +1,326 @@ +#! /usr/bin/env python +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +# Set log level to benefit from Scapy warnings +from scapy.all import * +from packet_util import * + +message_types = { + 'T' : "Timestamp", + 'S' : "System Event", + 'R' : "Stock Delivery", + 'H' : "Stock Trading Action", + 'Y' : "Reg SHO Short Sale Price Test", + 'L' : "Maktet Participant Position", + 'A' : "Add order without MPID", + 'F' : "Add order with MPID", + 'E' : "Order Executed", + 'C' : "Order Executed with Price", + 'X' : "Order Cancel", + 'D' : "Order Delete", + 'U' : "Order Replace", + 'P' : "Trade Message", + 'Q' : "Cross Trade Message", + 'B' : "Broken Trade", + 'I' : "Net Order Imbalance", + 'N' : "Retail Price Improvement Indicator" +} + +event_codes = { + 'O' : "Start of Message", + 'S' : "Start of System Hours", + 'Q' : "Start of Market Hours", + 'M' : "End of Market Hours", + 'E' : "End of System Hours", + 'C' : "End of Message", + 'A' : "Emergency Market Condition - HALT", + 'R' : "Emergency Market Condition - Quote Only Period", + 'B' : "Emergency Market Condition - Resumption", +} + +market_catagories = { + 'N' : "New York Stock Exchange (NYSE)", + 'A' : "NYSE Amex", + 'P' : "NYSE Arca", + 'Q' : "NASDAQ Global Select Market", + 'R' : "NASDAQ Global Market", + 'S' : "NASDAQ Capital Market", + 'Z' : "BATS BZX Exchange" +} + +status_indicators = { + 'D' : "Deficient", + 'E' : "Delinquent", + 'Q' : "Bankrupt", + 'S' : "Suspended", + 'G' : "Deficient and Bankrupt", + 'H' : "Deficient and Delinquent", + 'J' : "Delinquent and Bankrupt", + 'K' : "Deficient, Delinquent and Bankrupt", + ' ' : "Compliant" +} + +trading_state = { + 'H' : "Halted Across all U.S", + 'P' : "Paused Across all U.S", + 'Q' : "Quotation only period", + 'T' : "Trading on NASDAQ" +} + +market_mode = { + 'N' : "Normal", + 'P' : "Passive", + 'S' : "Syndicate", + 'R' : "Pre-Syndicate", + 'L' : "Penalty" +} + +market_state = { + 'A' : "Active", + 'E' : "Excused/Withdrawn", + 'W' : "Withdrawn", + 'S' : "Suspended", + 'D' : "Deleted" +} + +buy_sell = { + 'B' : "Buy", + 'S' : "Sell" +} + +cross_type = { + 'O' : "NASDAQ Opening Cross", + 'C' : "NASDAQ Closing Cross", + 'H' : "Cross for IPO halted/pause securities", + 'I' : "NASDAQ Cross Network: Intrdat/Postclose" +} + +imbalance_dir = { + 'B' : "Buy Imbalance", + 'S' : "Sell Imbalance", + 'N' : "No Imbalance", + 'O' : "Insufficent orders" +} + +price_var = { + 'L' : "Less than 1%", + '1' : "1 - 1.99%", + '2' : "2 - 2.99%", + '3' : "3 - 3.99%", + '4' : "4 - 4.99%", + '5' : "5 - 5.99%", + '6' : "6 - 6.99%", + '7' : "7 - 7.99%", + '8' : "8 - 8.99%", + '9' : "9 - 9.99%", + 'A' : "10 - 19.99%", + 'B' : "20 - 29.99%", + 'C' : "30% or greater", + ' ' : "Cannot calculate" +} + +interest_flag = { + 'B' : "RPI Orders available on the buy side", + 'S' : "RPT Orders available on the sell side", + 'A' : "RPI Orders available on both sides", + 'N' : "Not RPI orders available" +} + +class ItchMessage(Packet): + name = "ITCH 4.1" + +class ItchTimeStamp(ItchMessage): + name = "ITCH 4.1 Timestamp" + fields_desc = [ + CharEnumField("message", 'T', message_types), + IntField("seconds", 0)] + +class ItchEvent(ItchMessage): + name = "ITCH 4.1 System Event Message" + fields_desc = [ + CharEnumField("header", 'S', message_types), + IntField("nanoseconds", 0), + CharEnumField("eventcode", " ", event_codes)] + +class ItchStockDelivery(ItchMessage): + name = "ITCH 4.1 Stock Delivery" + fields_desc = [ + CharEnumField("header", 'R', message_types), + IntField("nanoseconds", 0), + StrFixedLenField("stock", " ", length=8), + CharEnumField("mktcat", ' ', market_catagories), + CharEnumField("status", ' ', status_indicators), + IntField("lotsize", 0), + CharEnumField("lotsonly", ' ', {'Y' : "Only round lots", 'N' : "Odd/Mixed allowed"})] + +class ItchStockAction(ItchMessage): + name = "ITCH 4.1 Stock Trading Action" + fields_desc = [ + CharEnumField("header", 'H', message_types), + IntField("nanoseconds", 0), + StrFixedLenField("stock", " ", length=8), + CharEnumField("state", ' ', trading_state), + ByteField("reserved", 0), + StrFixedLenField("reason", " ", length=4)] + +class ItchRegSHO(ItchMessage): + name = "ITCH 4.1 Reg SHO Short Sale Price Test Restricted Indicator" + fields_desc = [ + CharEnumField("header", 'Y', message_types), + IntField("nanoseconds", 0), + StrFixedLenField("stock", " ", length=8), + CharEnumField("reason", ' ', {'0' : "No test", '1' : "SHO on", '2' : "SHO remains"})] + +class ItchMarketPartPos(ItchMessage): + name = "ITCH 4.1 Matket Participant Position" + fields_desc = [ + CharEnumField("header", 'L', message_types), + IntField("nanoseconds", 0), + StrFixedLenField("mpid", " ", length=4), + StrFixedLenField("stock", " ", length=8), + CharEnumField("primary", ' ', {'Y' : "Primary Maker", 'N' : "Non-primary"}), + CharEnumField("mkmode", ' ' , market_mode), + CharEnumField("mkstate", ' ', market_state)] + +class ItchAddOrder(ItchMessage): + name = "ITCH 4.1 Add order without MPID" + fields_desc = [ + CharEnumField("header", 'A', message_types), + IntField("nanoseconds", 0), + LongDecimalField("order", 0, 8), + CharEnumField("type", ' ', buy_sell), + IntField("shares", 0), + StrFixedLenField("stock", " ", length=8), + IntField("price", 0)] + +class ItchAddOrderMPID(ItchMessage): + name = "ITCH 4.1 Add order with MPID" + fields_desc = [ + CharEnumField("header", 'F', message_types), + IntField("nanoseconds", 0), + LongDecimalField("order", 0, 8), + CharEnumField("type", ' ', buy_sell), + IntField("shares", 0), + StrFixedLenField("stock", " ", length=8), + IntField("price", 0), + StrFixedLenField("attrubution", " ", length=4)] + +class ItchOrderExec(ItchMessage): + name = "ITCH 4.1 Order Executed" + fields_desc = [ + CharEnumField("header", 'E', message_types), + IntField("nanoseconds", 0), + LongDecimalField("order", 0, 8), + IntField("shares", 0), + LongDecimalField("matchnum", 0, 8)] + +class ItchOrderExecPrice(ItchMessage): + name = "ITCH 4.1 Order Executed with price" + fields_desc = [ + CharEnumField("header", 'C', message_types), + IntField("nanoseconds", 0), + LongDecimalField("order", 0, 8), + IntField("shares", 0), + LongDecimalField("matchnum", 0, 8), + CharEnumField("printable", ' ', {'Y' : "Printable", 'N' : "Non-Printable"}), + IntField("price", 0)] + +class ItchOrderCancel(ItchMessage): + name = "ITCH 4.1 Order Cancle" + fields_desc = [ + CharEnumField("header", 'X', message_types), + IntField("nanoseconds", 0), + LongDecimalField("order", 0, 8), + IntField("cancelnum", 0)] + +class ItchOrderDelete(ItchMessage): + name = "ITCH 4.1 Order Delete" + filds_desc = [ + CharEnumField("header", 'D', message_types), + IntField("nanoseconds", 0), + LongDecimalField("order", 0, 8)] + +class ItchOrderReplace(ItchMessage): + name = "ITCH 4.1 Order Replace" + fields_desc = [ + CharEnumField("header", 'U', message_types), + IntField("nanoseconds", 0), + LongDecimalField("oldorder", 0, 8), + LongDecimalField("neworder", 0, 8), + IntField("shares", 0), + IntField("price", 0)] + +class ItchTrade(ItchMessage): + name = "ITCH 4.1 Trade Message Non-Cross" + fields_desc = [ + CharEnumField("header", 'P', message_types), + IntField("nanoseconds", 0), + LongDecimalField("order", 0, 8), + CharEnumField("type", ' ', buy_sell), + IntField("shares", 0), + StrFixedLenField("stock", " ", length=8), + IntField("price", 0), + LongDecimalField("matchnum", 0, 8)] + +class ItchTradeCross(ItchMessage): + name = "ITCH 4.1 Cross Trade Message" + fields_desc = [ + CharEnumField("header", 'P', message_types), + IntField("nanoseconds", 0), + LongDecimalField("shares", 0, 8), + StrFixedLenField("stock", " ", length=8), + IntField("crossprice", 0), + LongDecimalField("matchnum", 0, 8), + CharEnumField("ctype", ' ', cross_type)] + +class ItchBrokenTrade(ItchMessage): + name = "ITCH 4.1 Broken Trade" + fields_desc = [ + CharEnumField("header", 'B', message_types), + IntField("nanoseconds", 0), + LongDecimalField("matchnum", 0, 8)] + +class ItchNOII(ItchMessage): + name = "ITCH 4.1 NOII" + fields_desc = [ + CharEnumField("header", 'B', message_types), + IntField("nanoseconds", 0), + LongDecimalField("paired", 0, 8), + CharEnumField("Imdir", ' ', imbalance_dir), + StrFixedLenField("stock", " ", length=8), + IntField("farprice", 0), + IntField("nearprice", 0), + IntField("currref", 0), + CharEnumField("ctype", ' ', cross_type), + CharEnumField("var", ' ', price_var)] + +class ItchRPII(ItchMessage): + name = "ITCH 4.1 RPII" + fields_desc = [ + CharEnumField("header", 'B', message_types), + IntField("nanoseconds", 0), + StrFixedLenField("stock", " ", length=8), + CharEnumField("intflag", ' ', interest_flag)] diff --git a/cocotb/generators/feeds/itch_feed.py b/cocotb/generators/feeds/itch_feed.py new file mode 100644 index 00000000..7c1969cb --- /dev/null +++ b/cocotb/generators/feeds/itch_feed.py @@ -0,0 +1,65 @@ +#! /usr/bin/env python +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +# Set log level to benefit from Scapy warnings +import cocotb +from scapy.all import * +from cocotb.generators.feeds import Feed +from cocotb.generators.feeds.moldudp64 import Moldudp64 + +""" Todo + Specify max number of blocks per underlying mold +""" + +class ItchFeed(Feed): + """ Class to represent a steam of Itch4.0 messages """ + + def __init__(self, name="default_itch", session=0, seqnum=0): + """ Session is the session for the current stream + Seqnum is the starting sequence number + """ + Feed.__init__(self, name, None) + self._session = session; + self._seqnum = seqnum + + def addmsg(self, msg): + """ Add a mold64 encapsulated msg to the queue """ + packet = Ether()/IP()/UDP()/Moldudp64(session=self._session, seqnum=self._seqnum, msgblock=[]) + mold = packet.getlayer(Moldudp64) + mold.insertblock(msg) + ret = self._seqnum + self._seqnum += 1 + super(self.__class__, self).addmsg(ret, packet) + return ret + + def appendmsg(self, seqnum, msg): + """ Append another msg to the specified sequence number """ + + def getmsg(self): + return Feed.getmsg(self) + +if __name__ == "__main__": + interact(mydict=globals(), mybanner="ITCH 4.0 packet generator") diff --git a/cocotb/generators/feeds/moldudp64.py b/cocotb/generators/feeds/moldudp64.py new file mode 100644 index 00000000..7ee39ed6 --- /dev/null +++ b/cocotb/generators/feeds/moldudp64.py @@ -0,0 +1,87 @@ +#! /usr/bin/env python +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +# Set log level to benefit from Scapy warnings +import logging +logging.getLogger("scapy").setLevel(1) + +from scapy.all import * +from packet_util import * + +class MsgDataLen(FieldLenField): + def __init__(self, name, default, fld): + FieldLenField.__init__(self, name, default) + self.fld = fld + + def i2len(self, pkt, x): + return len(self.i2len(pkt, x)) + + def i2m(self, pkt, x): + if x is None: + f = pkt.get_field(self.fld) + x = f.i2len(pkt, pkt.getfieldval(self.fld)) + return int_to_packed(x, 16, 8) + + def m2i(self, pkt, x): + if x is None: + return None, 0 + return x[0:2] + + def getfield(self, pkt, s): + i = len(s) - 1 + return s[i:], self.m2i(pkt, s[:i]) + + def addfield(self, pkt, s, val): + return s+self.i2m(pkt, val) + +class MoldCountField(FieldLenField): + def __init__(self, name, length, count): + FieldLenField.__init__(self, name, None, count_of=count) + self.length = length + + def i2m(self, pkt, x): + fld,fval = pkt.getfield_and_val(self.count_of) + f = fld.i2count(pkt, fval) + return int_to_packed(f, self.length * 8, 8) + + def addfield(self, pkt, s, val): + return s+self.i2m(pkt, val) + + +class MessageBlock(Packet): + fields_desc = [ MsgDataLen("length", None, "data"), + StrLenField("data", "", length_from="length") ] + + +class Moldudp64(Packet): + name = "Moldudp64" + fields_desc = [ LongDecimalField("session", 0, 10), + LongDecimalField("seqnum", 0, 8), + MoldCountField("msgcnt", 2, "msgblock"), + PacketListField("msgblock", [], MessageBlock) ] + + def insertblock(self, payload): + self.msgblock.append(MessageBlock(data=payload)) diff --git a/cocotb/generators/feeds/opra_binary.py b/cocotb/generators/feeds/opra_binary.py new file mode 100644 index 00000000..c59c3ac5 --- /dev/null +++ b/cocotb/generators/feeds/opra_binary.py @@ -0,0 +1,221 @@ +#! /usr/bin/env python +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +# Set log level to benefit from Scapy warnings +import logging +logging.getLogger("scapy").setLevel(1) + +from scapy.all import * + + +# TODO: Full implemenation + +class DenominatorField(): pass + +class OPRAValueField(): + """Representation of a value, adjusted for the appropriate denominator""" + def __init__(self, denominator): + pass + + + + +def hdr_to_msg_len(pkt): + if pkt.header.msg_category == "a": return 35 + if pkt.header.msg_category == "d": return 22 + if pkt.header.msg_category == "f": return 64 + if pkt.header.msg_category == "k": raise Exception("Don't support msg category k yet") + if pkt.header.msg_category == "q": raise Exception("Don't support msg category q yet") + if pkt.header.msg_category == "Y": return 19 + if pkt.header.msg_category == "C": raise Exception("Don't support msg category C yet") + if pkt.header.msg_category == "H": raise Exception("Don't support msg category H yet") + raise Exception("Uknown message category %c" % chr(msg.category)) + +#class OPRAEquityAndIndexLastSale(Packet): + #name = "EILS" + #fields_desc = [ + #OPRABinaryMessageHeader(Header, None), + #StrFixedLenField("symbol", " ", length=5), + #StrFixedLenField("padding", " ", length=26)] + #ByteField("reserved", 0), + #3ByteField("expiration", 0), + #DenominatorField("strike_denom", default), + #OPRAValueField("strike_price", "strike_denom"), + #IntField("volume", 0), + #DenominatorField("premium_denom", default), + #OPRAValueField("premium_price", "premium_denom"), + #IntField("trade_id", 0), + #IntField("reserved1", 0)] + + +class OPRABinaryMessageHeader(Packet): + name = "MSG" + fields_desc = [ + StrFixedLenField("participant_id", " ", length=1), + StrFixedLenField("msg_category", " ", length=1), + StrFixedLenField("msg_type", " ", length=1), + StrFixedLenField("msg_indicator", " ", length=1)] + + +class OPRAMessage(Packet): + fields_desc = [ + OPRABinaryMessageHeader("header", None), + StrFixedLenField("symbol", " ", length=5), + StrLenField("length", "", length_from=hdr_to_msg_len)] # Doesn't actually exist in protocol + + +class OPRABinaryBlock(Packet): + name = "OPRA" + fields_desc = [ + ByteField("version", 0), + ShortField("block_size", 0), + StrFixedLenField("feed_indicator", "O", length=1), + CharEnumField("retransmission_indicator", " ", {" " : "Original", "V": "Retransmitted"}), + ByteField("reserved", 0), + IntField("block_sequence_number", 0), + ByteField("nmsgs", 0), + LongField("timestamp", 0), + XShortField("checksum", 0), + PacketListField("msgs", [], OPRAMessage)] + #FieldLenField("length", None, count_of="msgs")] + + + + + +# The Participant ID field is a 1 Byte, ASCII character that identifies the Participant or Processor that initiated the message +participant_ids = { + 'A' : "NYSE AMEX", + 'B' : "Boston Options Exchange", + 'C' : "Chicago Board Options Exchange", + 'I' : "International Securities Exchange", + 'M' : "Miami International Securities Exchange", + 'N' : "NYSE ARCA", + 'O' : "Options Price Reporting Authority", + 'Q' : "NASDAQ Stock Market", + 'T' : "NASDAQ OMX BX Options*", + 'W' : "C2", + 'X' : "NASDAQ OMX PHLX", + 'Z' : "BATS"} + + + +# The Message Category field is a 1 Byte, ASCII character, either an upper or lower case letter. +message_category_ids = { + 'a' : "EQUITY AND INDEX LAST SALE", + 'd' : "OPEN INTEREST", + 'f' : "EQUITY AND INDEX END OF DAY SUMMARY", + 'k' : "LONG EQUITY AND INDEX QUOTE", + 'q' : "SHORT EQUITY AND INDEX QUOTE", + 'C' : "ADMINISTRATIVE", + 'H' : "CONTROL", + 'Y' : "UNDERLYING VALUE MESSAGE"} + +message_types_category_a = { + " " : "REGULAR" , + "A" : "CANC" , + "B" : "OSEQ" , + "C" : "CNCL" , + "D" : "LATE" , + "E" : "CNCO" , + "F" : "OPEN" , + "G" : "CNOL" , + "H" : "OPNL" , + "I" : "AUTO" , + "J" : "REOP" , + "K" : "AJST" , + "L" : "SPRD" , + "M" : "STDL" , + "N" : "STPD" , + "O" : "CSTP" , + "P" : "BWRT" , + "Q" : "CMBO" , + "R" : "SPIM" , + "S" : "ISOI" , + "T" : "BNMT" , + "X" : "XMPT" ,} + + + +type_2_description = { + "REGULAR" : "Indicates that the transaction was a regular sale and was made without stated conditions.", + "CANC" : "Transaction previously reported (other than as the last or opening report for the particular option contract) is now to be cancelled.", + "OSEQ" : "Transaction is being reported late and is out of sequence; i.e., later transactions have been reported for the particular option contract.", + "CNCL" : "Transaction is the last reported for the particular option contract and is now cancelled.", + "LATE" : "Transaction is being reported late, but is in the correct sequence; i.e., no later transactions have been reported for the particular option contract.", + "CNCO" : "Transaction was the first one (opening) reported this day for the particular option contract. Although later transactions have been reported, this transaction is now to be cancelled.", + "OPEN" : "Transaction is a late report of the opening trade and is out of sequence; i.e., other transactions have been reported for the particular option contract.", + "CNOL" : "Transaction was the only one reported this day for the particular option contract and is now to be cancelled.", + "OPNL" : "Transaction is a late report of the opening trade, but is in the correct sequence; i.e., no other transactions have been reported for the particular option contract.", + "AUTO" : "Transaction was executed electronically. Prefix appears solely for information; process as a regular transaction.", + "REOP" : "Transaction is a reopening of an option contract in which trading has been previously halted. Prefix appears solely for information; process as a regular transaction.", + "AJST" : "Transaction is an option contract for which the terms have been adjusted to reflect a stock dividend, stock split, or similar event. Prefix appears solely for information; process as a regular transaction.", + "SPRD" : "Transaction represents a trade in two options in the same class (a buy and a sell in the same class). Prefix appears solely for information; process as a regular transaction.", + "STDL" : "Transaction represents a trade in two options in the same class (a buy and a sell in a put and a call). Prefix appears solely for information; process as a regular transaction.", + "STPD" : "Transaction is the execution of a sale at a price agreed upon by the floor personnel involved, where a condition of the trade is that it reported following a non-stopped trade of the same series at the same price.", + "CSTP" : "Cancel stopped transaction.", + "BWRT" : "Transaction represents the option portion of an order involving a single option leg (buy or sell of a call or put) and stock. Prefix appears solely for information: process as a regular transaction.", + "CMBO" : "Transaction represents the buying of a call and the selling of a put for the same underlying stock or index. Prefix appears solely for information; process as a regular transaction.", + "SPIM" : "Transaction was the execution of an order which was \"stopped\" at a price that did not constitute a Trade-Through on another market at the time of the stop. Process like a normal transaction except don't update \"last\".", + "ISOI" : "Transaction was the execution of an order identified as an Intermarket Sweep Order. Process like normal transaction", + "BNMT" : "Transaction reflects the execution of a \"benchmark trade\". A \"Benchmark Trade\" is a trade resulting from the matching of \"Benchmark Orders\". A \"Benchmark Order\" is an order for which the price is not based, directly or indirectly, on the quote price of the option at the time of the order's execution and for which the material terms were not reasonably determinable at the time a commitment to trade the order was made. Process like a normal transaction except don't update \"last\".", + "XMPT" : "Transaction is Trade Through Exempt. The transaction should be treated like a regular sale."} + + +message_types_category_h = { + "A" : "Start of Test Cycle", + "B" : "End of Test Cycle", + "C" : "Start of Day", + "D" : "Good Morning", + "E" : "Start of Summary", + "F" : "End of Summary", + "G" : "Early Market Close", + "H" : "End of Transaction Reporting", + "I" : "Good Night", + "J" : "End of Day", + "K" : "Reset Block Sequence Number", + "L" : "Start of Open Interest", + "M" : "End of Open Interest", + "N" : "Line Integrity", + } + +message_types_category_kq = { + " " : "Regular Trading", + "F" : "Non-Firm Quote", + "R" : "Rotation", + "T" : "Trading Halted", + "A" : "Eligible for Automatic Execution", + "B" : "Bid contains Customer Trading Interest", + "O" : "Offer contains Customer Trading Interest", + "C" : "Both Bid and Offer contain Customer Trading Interest", + "X" : "Offer side of Quote Not Firm; Bid Side Firm", + "Y" : "Bid Side of Quote Not Firm; Offer Side Firm", + } + +message_types_category_Y = { + " " : "Index based on Last Sale", + "I" : "Index based on Bid and Offer", + } diff --git a/cocotb/generators/feeds/packet_util.py b/cocotb/generators/feeds/packet_util.py new file mode 100644 index 00000000..b32a526d --- /dev/null +++ b/cocotb/generators/feeds/packet_util.py @@ -0,0 +1,94 @@ +#! /usr/bin/env python +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +from scapy.all import * + +#define all the fields as extensions of the current base classes that need it + +STRUCT_FMT = { + 8 : 'B', # unsigned char + 16 : 'H', # unsigned short + 32 : 'I', # unsigned int +} + +def int_to_words(int_val, num_words=4, word_size=32): + max_int = 2 ** (word_size*num_words) - 1 + max_word_size = 2 ** word_size - 1 + + if not 0 <= int_val <= max_int: + raise IndexError('integer %r is out of bounds!' % hex(int_val)) + + words = [] + for _ in range(num_words): + word = int_val & max_word_size + words.append(int(word)) + int_val >>= word_size + words.reverse() + + return words + +def int_to_packed(int_val, width=128, word_size=32): + num_words = width / word_size + words = int_to_words(int_val, num_words, word_size) + + try: + fmt = '>%d%s' % (num_words, STRUCT_FMT[word_size]) + #DEBUG: print 'format:', fmt + except KeyError: + raise ValueError('unsupported word size: %d!' % word_size) + + return struct.pack(fmt, *words) + +def packed_to_int(packed_int, width=128, word_size=32): + num_words = width / word_size + + try: + fmt = '>%d%s' % (num_words, STRUCT_FMT[word_size]) + #DEBUG: print 'format:', fmt + except KeyError: + raise ValueError('unsupported word size: %d!' % word_size) + + words = list(struct.unpack(fmt, packed_int)) + words.reverse() + + int_val = 0 + for i, num in enumerate(words): + word = num + word = word << word_size * i + int_val = int_val | word + + return int_val + +class LongDecimalField(StrFixedLenField): + def __init__(self, name, default, length): + StrFixedLenField.__init__(self, name, default, length, None) + self.length = length + + def addfield(self, pkt, s, val): + return s + int_to_packed(val, self.length * 8, 8) + + def extract_padding(self, s): + return '', s diff --git a/cocotb/generators/opra_binary_payload_generators.py b/cocotb/generators/opra_binary_payload_generators.py new file mode 100644 index 00000000..f8ae7a51 --- /dev/null +++ b/cocotb/generators/opra_binary_payload_generators.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + + +""" + +Selection of generators for OPRA binary payload data + +Comprises a block followed by a list of messages + +""" +from dpkt.pcap import Reader +from scapy.all import Ether + +__all__ = ["from_pcap"] + +def from_pcap(fname="/home/chiggs/market-data/nysetech-data-feeds/opra_binary_3.20130320.pcap", npackets=50): + """reads in a pcap file and spits out the UDP payload + + FIXME assumes all UDP packets are OPRA + + """ + sent = 0 + with open(fname, 'r') as f: + pcap = Reader(f) + for index, (timestamp, packet) in enumerate(pcap): + if sent > npackets: break + p = Ether(packet) + try: + yield str(p["UDP"].payload) + sent += 1 + except AttributeError: + continue diff --git a/cocotb/generators/packet_generators.py b/cocotb/generators/packet_generators.py new file mode 100644 index 00000000..0e6d84d0 --- /dev/null +++ b/cocotb/generators/packet_generators.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + + +""" + Collection of Ethernet Packet generators to use for testdata generation + + Most generators take the keyword argument "payload" which can be + used to control the payload contents if required. Defaults to random data. +""" +import random + +from scapy.all import Ether, IP, UDP +from byte_generators import * + +__all__ = ["udp_all_sizes", "udp_random_sizes"] + +_default_payload = random_data + +def _get_payload(gen, bytes): + """Helper function to pull a chunk of bytes from a generator""" + payload = "" + while len(payload) < bytes: + payload += gen.next() + return payload + + +# UDP packet generators + +def udp_all_sizes(max_size=1500, payload=_default_payload()): + """UDP packets of every supported size""" + header = Ether() / IP() / UDP () + + for size in range(0, max_size-len(header)): + yield header / _get_payload(payload, size) + +def udp_random_sizes(npackets=100, payload=_default_payload()): + """UDP packets with random sizes""" + header = Ether() / IP() / UDP () + max_size = 1500 - len(header) + + for pkt in range(npackets): + yield header / _get_payload(payload, random.randint(0,max_size)) + + + +if __name__ == "__main__": + for pkt in udp_all_sizes(max_size=64): + print repr(pkt) + for pkt in udp_random_sizes(npackets=2): + print repr(pkt) diff --git a/cocotb/handle.py b/cocotb/handle.py new file mode 100644 index 00000000..ca914d7e --- /dev/null +++ b/cocotb/handle.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +# -*- coding: utf-8 -*- + +import logging +import simulator as simulator +import cocotb +from cocotb.binary import BinaryValue + +class SimHandle(object): + + def __init__(self, handle): + """ + Args: + _handle [integer] : vpi/vhpi handle to the simulator object + """ + self._handle = handle # handle used for future simulator transactions + self._sub_handles = {} # Dictionary of SimHandle objects created by getattr + + self.name = simulator.get_name_string(self._handle) + self.fullname = self.name + '(%s)' % simulator.get_type_string(self._handle) + self.log = logging.getLogger('cocotb.' + self.name) + self.log.debug("Created!") + + def __str__(self): + return "%s @0x%x" % (self.name, self._handle) + + def __getattr__(self, name): + if name in self._sub_handles: + return self._sub_handles[name] + new_handle = simulator.get_handle_by_name(self._handle, name) + if not new_handle: + raise AttributeError("%s contains no object named %s" % (self.name, name)) + self._sub_handles[name] = SimHandle(new_handle) + return self._sub_handles[name] + + def getvalue(self): + result = BinaryValue() + result.binstr = self._get_value_str() + return result + + def setimeadiatevalue(self, value): + """Set the value of the underlying simulation object to value. + + This operation will fail unless the handle refers to a modifiable object eg signal or variable. + We determine the library call to make based on the type of the value""" + if isinstance(value, BinaryValue): + simulator.set_signal_val_str(self._handle, value.binstr) + elif isinstance(value, int): + simulator.set_signal_val(self._handle, value) + else: + self.log.critical("Unsupported type for value assignment: %s (%s)" % (type(value), repr(value))) + raise TypeError("Unable to set simulator value with type %s" % (type(value))) + + def setcachedvalue(self, value): + """Intercept the store of a value and hold in cache. + + This operation is to enable all of the scheduled callbacks to completed + with the same read data and for the writes to occour on the next + sim time""" + cocotb.scheduler.save_write(self, value) + + # We want to maintain compatability with python 2.5 so we can't use @property with a setter + value = property(getvalue, setcachedvalue, None, "A reference to the value") + + def _get_value_str(self): + return simulator.get_signal_val(self._handle) + + def __le__(self, value): + """Overload the less than or equal to operator to + provide an hdl-like shortcut + module.signal <= 2 + """ + self.value = value + + + def __len__(self): + """Returns the 'length' of the underlying object. + + For vectors this is the number of bits. + + TODO: Handle other types (loops, generate etc) + """ + return len(self._get_value_str()) + + + def __cmp__(self, other): + + # Permits comparison of handles i.e. if clk == dut.clk + if isinstance(other, SimHandle): + if self._handle == other._handle: return 0 + return 1 + + # Use the comparison method of the other object against our value + return self.value.__cmp__(other) diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py new file mode 100644 index 00000000..0e3391bc --- /dev/null +++ b/cocotb/monitors/__init__.py @@ -0,0 +1,205 @@ +#!/bin/env python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" + + Class defining the standard interface for a monitor within a testbench + + The monitor is responsible for watching the pins of the DUT and recreating the + transactions +""" + +import logging +import math + +import cocotb +from cocotb.decorators import coroutine +from cocotb.triggers import Edge, Event, RisingEdge +from cocotb.binary import BinaryValue +from cocotb.bus import Bus +from cocotb.utils import hexdump + +class Monitor(object): + + + def __init__(self, callback=None, event=None): + """ + Constructor for a monitor instance + + callback will be called with each recovered transaction as the argument + + If the callback isn't used, received transactions will be placed on a queue and the + event used to notify any consumers. + """ + self._event = event + self._recvQ = [] + self._callbacks = [] + + # Subclasses may already set up logging + if not hasattr(self, "log"): + self.log = logging.getLogger("cocotb.monitor.%s" % (self.__class__.__name__)) + + if callback is not None: + self.add_callback(callback) + + # Create an independent coroutine which can receive stuff + self._thread = cocotb.scheduler.add(self._monitor_recv()) + + def kill(self): + if self._thread: + self._thread.kill() + self._thread = None + + def __len__(self): + return len(self._recvQ) + + def add_callback(self, callback): + self.log.debug("Adding callback of function %s to monitor" % (callback.__name__)) + self._callbacks.append(callback) + + @coroutine + def _monitor_recv(self): + """ + actual impementation of the receiver + + subclasses should override this method to implement the actual receive routine and + call self._recv() with the recovered transaction + """ + raise NotImplementedError("Attempt to use base monitor class without providing a _monitor_recv method") + + + def _recv(self, transaction): + """Common handling of a received transaction.""" + # either callback based consumer + for callback in self._callbacks: + callback(transaction) + + # Or queued with a notification + if not self._callbacks: + self._recvQ.append(transaction) + + if self._event is not None: + self._event.set() + + +class AvalonST(Monitor): + _signals = ["valid", "data"] + + + +class AvalonSTPkts(Monitor): + """ + Packetised AvalonST bus + """ + _signals = AvalonST._signals + ["startofpacket", "endofpacket", "ready", "empty", "error"] + + def __init__(self, entity, name, clock, callback=None, event=None): + self.entity = entity + self.name = name + self.clock = clock + self.bus = Bus(self.entity, self.name, self._signals) + self.log = logging.getLogger("cocotb.%s.%s" % (self.entity.name, self.name)) + + Monitor.__init__(self, callback=callback, event=event) + + @coroutine + def _monitor_recv(self): + """Watch the pins and reconstruct transactions""" + + # Avoid spurious object creation by recycling + clkedge = RisingEdge(self.clock) + pkt = "" + + while True: + yield clkedge + + if self.bus.valid.value and self.bus.startofpacket.value: + vec = self.bus.data.value + self.bus.data.log.info("%s %s" % (vec.binstr, repr(vec.buff))) + pkt += vec.buff + while True: + yield clkedge + if self.bus.valid.value: + vec = self.bus.data.value + self.bus.data.log.debug("%s %s" % (vec.binstr, repr(vec.buff))) + pkt += vec.buff + if self.bus.endofpacket.value: + self.log.info("Recieved a packet of %d bytes" % len(pkt)) + self.log.debug(hexdump(str((pkt)))) + self._recv(pkt) + pkt = "" + break + + + +class SFStreaming(Monitor): + """This is the Solarflare Streaming bus as defined by the FDK. + + Expect to see a 72-bit bus (bottom 64 bits data, top 8 bits are ECC) + + TODO: + Metaword / channel bits + ECC checking + """ + _signals = AvalonST._signals + ["startofpacket", "endofpacket", "ready", "empty", "channel", "error"] + + def __init__(self, entity, name, clock, callback=None, event=None): + self.entity = entity + self.name = name + self.clock = clock + self.bus = Bus(self.entity, self.name, self._signals) + self.log = logging.getLogger("cocotb.%s.%s" % (self.entity.name, self.name)) + + Monitor.__init__(self, callback=callback, event=event) + + @coroutine + def _monitor_recv(self): + """Watch the pins and reconstruct transactions""" + + # Avoid spurious object creation by recycling + clkedge = RisingEdge(self.clock) + pkt = "" + + while True: + yield clkedge + + if self.bus.valid.value and self.bus.startofpacket.value: + vec = self.bus.data.value + self.bus.data.log.info("%s %s" % (vec.binstr, repr(vec.buff))) + pkt += vec.buff + while True: + yield clkedge + if self.bus.valid.value: + vec = self.bus.data.value + self.bus.data.log.info("%s %s" % (vec.binstr, repr(vec.buff))) + pkt += vec.buff + if self.bus.endofpacket.value: + self.log.warning("Recieved a packet!!") + self.log.info(repr(pkt)) + self._recv(pkt) + pkt = "" + break diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py new file mode 100644 index 00000000..abd665d0 --- /dev/null +++ b/cocotb/scheduler.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" + Coroutine scheduler. +""" +import types +import threading +import collections +import os +import logging + +import cocotb +import cocotb.decorators +from cocotb import triggers +from cocotb.handle import SimHandle +from cocotb.decorators import coroutine +from cocotb.triggers import Timer, ReadOnly, NextTimeStep, ReadWrite + + +class Scheduler(object): + + def __init__(self): + self.waiting = collections.defaultdict(list) + self.log = logging.getLogger("cocotb.scheduler") + self.writes = {} + self.writes_lock = threading.RLock() + self._readwrite = self.add(self.move_to_rw()) + + def react(self, trigger): + """ + React called when a trigger fires. We find any coroutines that are waiting on the particular + trigger and schedule them. + """ + trigger.log.debug("Fired!") + + if trigger not in self.waiting: + # This isn't actually an error - often might do event.set() without knowing + # whether any coroutines are actually waiting on this event + # NB should catch a GPI trigger cause that would be catestrophic + self.log.debug("Not waiting on triggger that fired! (%s)" % (str(trigger))) + return + + # Scheduled coroutines may append to our waiting list + # so the first thing to do is pop all entries waiting + # on this trigger. + self._scheduling = self.waiting.pop(trigger) + to_run = len(self._scheduling) + + self.log.debug("%d pending coroutines for event %s" % (to_run, trigger)) + + while self._scheduling: + coroutine = self._scheduling.pop(0) + self.schedule(coroutine, trigger=trigger) + self.log.debug("Scheduled coroutine %s" % (coroutine.__name__)) + + self.log.debug("Completed scheduling loop, still waiting on:") + for trig, routines in self.waiting.items(): + self.log.debug("\t%s: [%s]" % (str(trig).ljust(30), " ".join([routine.__name__ for routine in routines]))) + + # If we've performed any writes that are cached then schedule + # another callback for the read-only part of the sim cycle + if len(self.writes) and self._readwrite is None: + self._readwrite = self.add(self.move_to_rw()) + return + + def playout_writes(self): + if self.writes: + if self._readwrite is None: + self._readwrite = self.add(self.move_to_rw()) + while self.writes: + handle, args = self.writes.popitem() + handle.setimeadiatevalue(args) + + + def save_write(self, handle, args): + self.writes[handle]=args + + def _add_trigger(self, trigger, coroutine): + """Adds a new trigger which will cause the coroutine to continue when fired""" + self.waiting[trigger].append(coroutine) + trigger.prime(self.react) + + + def add(self, coroutine): + """Add a new coroutine. Required because we cant send to a just started generator (FIXME)""" + self.log.debug("Queuing new coroutine %s" % coroutine.__name__) + self.schedule(coroutine) + return coroutine + + + def schedule(self, coroutine, trigger=None): + coroutine.log.debug("Scheduling (%s)" % str(trigger)) + try: + result = coroutine.send(trigger) + except cocotb.decorators.CoroutineComplete as exc: + self.log.debug("Coroutine completed execution with CoroutineComplete: %s" % coroutine.__name__) + + # Call any pending callbacks that were waiting for this coroutine to exit + exc() + return + if isinstance(result, triggers.Trigger): + self._add_trigger(result, coroutine) + elif isinstance(result, cocotb.decorators.coroutine): + self.log.debug("Scheduling nested co-routine: %s" % result.__name__) + # Queue this routine to schedule when the nested routine exits + self._add_trigger(result.join(), coroutine) + self.schedule(result) + elif isinstance(result, list): + for trigger in result: + self._add_trigger(trigger, coroutine) + else: + self.log.warning("Unable to schedule coroutine since it's returning stuff %s" % repr(result)) + coroutine.log.debug("Finished sheduling coroutine (%s)" % str(trigger)) + + @coroutine + def move_to_rw(self): + yield ReadWrite() + self._readwrite = None + self.playout_writes() diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py new file mode 100644 index 00000000..d38a5aae --- /dev/null +++ b/cocotb/scoreboard.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" + Common scoreboarding capability. +""" +import logging +from cocotb.utils import hexdiffs + +from cocotb.monitor import Monitor + +class Scoreboard(object): + """Generic scorboarding class + + We can add interfaces by providing a monitor and an expected output queue + + The expected output can either be a function which provides a transaction + or a simple list containing the expected output. + + TODO: + Statistics for end-of-test summary etc. + """ + + def __init__(self, dut, reorder_depth=0): + self.dut = dut + self.log = logging.getLogger("cocotb.scoreboard.%s" % self.dut.name) + + def add_interface(self, monitor, expected_output): + """Add an interface to be scoreboarded. + + Provides a function which the monitor will callback with received transactions + + Simply check against the expected output. + + """ + + # Enforce some type checking as we only work with a real monitor + if not isinstance(monitor, Monitor): + raise TypeError("Expected monitor on the interface but got %s" % (monitor.__class__.__name__)) + + def check_received_transaction(transaction): + """Called back by the monitor when a new transaction has been received""" + + if not expected_output: + self.log.error("%s" % (transaction)) # TODO hexdump + raise TestFailure("Recieved a transaction but wasn't expecting anything") + + if callable(expected_output): exp = expected_output() + else: exp = expected_output.pop(0) + + if transaction != exp: + self.log.error("Received transaction differed from expected output") + self.log.warning(hexdiffs(exp, transaction)) + else: + self.log.debug("Received expected transaction %d bytes" % (len(transaction))) + self.log.debug(repr(transaction)) + + monitor.add_callback(check_received_transaction) diff --git a/cocotb/triggers.py b/cocotb/triggers.py new file mode 100644 index 00000000..42ca9af5 --- /dev/null +++ b/cocotb/triggers.py @@ -0,0 +1,244 @@ +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" + A collections of triggers which a testbench can 'yield' +""" +import logging +import simulator + + +class TriggerException(Exception): + pass + +class Trigger(object): + """Base class to derive from""" + def __init__(self): + self.log = logging.getLogger("cocotb.%s.0x%x" % (self.__class__.__name__, id(self))) + +class Timer(Trigger): + """ + Execution will resume when the specified time period expires + + Consumes simulation time + """ + def __init__(self, time_ps): + Trigger.__init__(self) + self.time_ps = time_ps + self.cbhdl = None + + def prime(self, callback): + self.cbhdl = simulator.register_timed_callback(self.time_ps, callback, self) + + def __str__(self): + return self.__class__.__name__ + "(%dps)" % self.time_ps + +class Edge(Trigger): + """ + Execution will resume when an edge occurs on the provided signal + """ + def __init__(self, signal): + Trigger.__init__(self) + self.cbhdl = None + self.signal = signal + + def prime(self, callback): + self.cbhdl = simulator.register_value_change_callback(self.signal._handle, callback, self) + + def __str__(self): + return self.__class__.__name__ + "(%s)" % self.signal.name + +class ReadOnly(Trigger): + """ + Execution will resume when the readonly portion of the sim cycles is + readched + """ + def __init__(self): + Trigger.__init__(self) + self.cbhdl = None + + def prime(self, callback): + self.cbhdl = simulator.register_readonly_callback(callback, self) + + def __str__(self): + return self.__class__.__name__ + "(readonly)" + +class ReadWrite(Trigger): + """ + Execution will resume when the readwrite porttion of the sim cycles is + reached + """ + def __init__(self): + Trigger.__init__(self) + self.cbhdl = None + + def prime(self, callback): + self.cbhdl = simulator.register_rwsynch_callback(callback, self) + + def __str__(self): + return self.__class__.__name__ + "(readwritesync)" + +class NextTimeStep(Trigger): + """ + Execution will resume when the next time step is started + """ + def __init__(self): + Trigger.__init__(self) + self.cbhdl = None + + def prime(self, callback): + self.cbhdl = simulator.register_nextstep_callback(callback, self) + + def __str__(self): + return self.__class__.__name__ + "(nexttimestep)" + +class RisingEdge(Trigger): + """ + Execution will resume when a rising edge occurs on the provided signal + """ + def __init__(self, signal): + Trigger.__init__(self) + self.cbhdl = None + self.signal = signal + + def prime(self, callback): + self._callback = callback + + def _check(obj): + if self.signal.value: + self._callback(self) + else: + self.cbhdl = simulator.register_value_change_callback(self.signal._handle, _check, self) + + self.cbhdl = simulator.register_value_change_callback(self.signal._handle, _check, self) + + def __str__(self): + return self.__class__.__name__ + "(%s)" % self.signal.name + +class ClockCycles(Trigger): + """ + Execution will resume after N rising edges + """ + def __init__(self, signal, num_cycles): + Trigger.__init__(self) + self.cbhdl = None + self.signal = signal + self.num_cycles = num_cycles + + def prime(self, callback): + self._callback = callback + + def _check(obj): + if self.signal.value: + self.num_cycles -= 1 + + if self.num_cycles <= 0: + self._callback(self) + return + + self.cbhdl = simulator.register_value_change_callback(self.signal._handle, _check, self) + + self.cbhdl = simulator.register_value_change_callback(self.signal._handle, _check, self) + + def __str__(self): + return self.__class__.__name__ + "(%s)" % self.signal.name + + +class Combine(Trigger): + """ + Combines multiple triggers together. Coroutine will continue when all + triggers have fired + """ + + def __init__(self, *args): + Trigger.__init__(self) + self._triggers = args + # TODO: check that trigger is an iterable containing only Trigger objects + try: + for trigger in self._triggers: + if not isinstance(trigger, Trigger): + raise TriggerException("All combined triggers must be instances of Trigger! Got: %s" % trigger.__class__.__name__) + except Exception: + raise TriggerException("%s requires a list of Trigger objects" % self.__class__.__name__) + + def prime(self, callback): + self._callback = callback + self._fired = [] + for trigger in self._triggers: + trigger.prime(self._check_all_fired) + + def _check_all_fired(self, trigger): + self._fired.append(trigger) + if self._fired == self._triggers: + self._callback(self) + + def unprime(self): + for trigger in self._triggers: + trigger.unprime() + + +class Event(Trigger): + """ + Event to permit synchronisation between two coroutines + """ + def __init__(self, name=""): + Trigger.__init__(self) + self._callback = None + self.name = name + + def prime(self, callback): + self._callback = callback + + def set(self): + """Wake up any coroutines blocked on this event""" + if not self._callback: + pass # nobody waiting + self._callback(self) + + def wait(self): + """This can be yielded to block this coroutine until another wakes it""" + return self + + def __str__(self): + return self.__class__.__name__ + "(%s)" % self.name + +class Join(Trigger): + """ + Join a coroutine, firing when it exits + """ + def __init__(self, coroutine): + Trigger.__init__(self) + self._coroutine = coroutine + + def prime(self, callback): + """Register our calback for when the coroutine exits""" + def _cb(): + callback(self) + + self._coroutine._callbacks.append(_cb) + self.log.debug("Primed on %s" % self._coroutine.__name__) + + def __str__(self): + return self.__class__.__name__ + "(%s)" % self._coroutine.__name__ diff --git a/cocotb/utils.py b/cocotb/utils.py new file mode 100644 index 00000000..3f611ad5 --- /dev/null +++ b/cocotb/utils.py @@ -0,0 +1,213 @@ +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +"""Collection of handy functions""" + +import ANSI + + +def _sane_color(x): + r="" + for i in x: + j = ord(i) + if (j < 32) or (j >= 127): + r+="." + else: + r+=i + return r + + +def hexdump(x): + """Hexdump a buffer""" + # adapted from scapy.utils.hexdump + rs = "" + x=str(x) + l = len(x) + i = 0 + while i < l: + rs += "%04x " % i + for j in range(16): + if i+j < l: + rs += "%02X " % ord(x[i+j]) + else: + rs += " " + if j%16 == 7: + rs += "" + rs += " " + rs += _sane_color(x[i:i+16]) + "\n" + i += 16 + return rs + +def hexdiffs(x,y): + """Return a diff string showing differences between 2 binary strings""" + # adapted from scapy.utils.hexdiff + + def sane(x): + r="" + for i in x: + j = ord(i) + if (j < 32) or (j >= 127): + r=r+"." + else: + r=r+i + return r + + def highlight(string, colour=ANSI.YELLOW_FG): + return colour + string + ANSI.DEFAULT_FG + ANSI.DEFAULT_BG + + rs = "" + + x=str(x)[::-1] + y=str(y)[::-1] + SUBST=1 + INSERT=1 + d={} + d[-1,-1] = 0,(-1,-1) + for j in range(len(y)): + d[-1,j] = d[-1,j-1][0]+INSERT, (-1,j-1) + for i in range(len(x)): + d[i,-1] = d[i-1,-1][0]+INSERT, (i-1,-1) + + for j in range(len(y)): + for i in range(len(x)): + d[i,j] = min( ( d[i-1,j-1][0]+SUBST*(x[i] != y[j]), (i-1,j-1) ), + ( d[i-1,j][0]+INSERT, (i-1,j) ), + ( d[i,j-1][0]+INSERT, (i,j-1) ) ) + + + backtrackx = [] + backtracky = [] + i=len(x)-1 + j=len(y)-1 + while not (i == j == -1): + i2,j2 = d[i,j][1] + backtrackx.append(x[i2+1:i+1]) + backtracky.append(y[j2+1:j+1]) + i,j = i2,j2 + + + + x = y = i = 0 + colorize = { 0: lambda x:x, + -1: lambda x:x, + 1: lambda x:x } + + dox=1 + doy=0 + l = len(backtrackx) + while i < l: + separate=0 + linex = backtrackx[i:i+16] + liney = backtracky[i:i+16] + xx = sum(len(k) for k in linex) + yy = sum(len(k) for k in liney) + if dox and not xx: + dox = 0 + doy = 1 + if dox and linex == liney: + doy=1 + + if dox: + xd = y + j = 0 + while not linex[j]: + j += 1 + xd -= 1 + if dox != doy: + rs += highlight("%04x" % xd) + " " + else: + rs += highlight("%04x" % xd, colour=ANSI.CYAN_FG) + " " + x += xx + line=linex + else: + rs += " " + if doy: + yd = y + j = 0 + while not liney[j]: + j += 1 + yd -= 1 + if doy-dox != 0: + rs += " " + highlight("%04x" % yd) + else: + rs += highlight("%04x" % yd, colour=ANSI.CYAN_FG) + y += yy + line=liney + else: + rs += " " + + rs += " " + + cl = "" + for j in range(16): + if i+j < l: + if line[j]: + if linex[j] != liney[j]: + rs += highlight("%02X" % ord(line[j]), colour=ANSI.RED_FG) + else: + rs += "%02X" % ord(line[j]) + if linex[j]==liney[j]: + cl += highlight(_sane_color(line[j]), colour=ANSI.MAGENTA_FG) + else: + cl += highlight(sane(line[j]), colour=ANSI.CYAN_BG+ANSI.BLACK_FG) + else: + rs += " " + cl += " " + else: + rs += " " + if j == 7: + rs += " " + + + rs += " " + cl + '\n' + + if doy or not yy: + doy=0 + dox=1 + i += 16 + else: + if yy: + dox=0 + doy=1 + else: + i += 16 + return rs + + +if __name__ == "__main__": + import random + a = "" + for char in range(random.randint(250,500)): + a += chr(random.randint(0,255)) + b = a + for error in range(random.randint(2,9)): + offset = random.randint(0, len(a)) + b = b[:offset] + chr(random.randint(0,255)) + b[offset+1:] + + diff = hexdiffs(a,b) + print diff + + space = '\n' + (" " * 20) + print space.join(diff.split('\n')) diff --git a/embed/Makefile b/embed/Makefile new file mode 100644 index 00000000..ce78d090 --- /dev/null +++ b/embed/Makefile @@ -0,0 +1,53 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +include $(SIM_ROOT)/makefiles/Makefile.inc + +OBJ_DIR := obj +INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ -I/mnt/sources/tools/icarus/include/iverilog -I../gpi +GCC_ARGS := -Werror -g +LIBS := -lgpi -lpython2.7 $(PYLIBS) +LD_PATH := -L$(LIB_DIR) + + +all: $(LIB_DIR)/cocotb.vpi + +$(OBJ_DIR): + mkdir -p $(OBJ_DIR) + +$(LIB_DIR)/libcocotb.so: $(OBJ_DIR) + gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $(OBJ_DIR)/cocotb.o gpi_embed.c + gcc $(GCC_ARGS) -shared -Xlinker -export-dynamic -o $@ $(OBJ_DIR)/cocotb.o $(LIBS) $(LD_PATH) + +# More rules such that this may be needed depending on the requirements of +# different simulators, icarus for instance loads a .vpi libraray to use the vpi +# inerface at the start of the simulation +$(LIB_DIR)/cocotb.vpi: $(LIB_DIR)/libcocotb.so + ln -sf libcocotb.so $@ + +clean: + rm -rf $(OBJ_DIR) diff --git a/embed/gpi_embed.c b/embed/gpi_embed.c new file mode 100644 index 00000000..79eff5d0 --- /dev/null +++ b/embed/gpi_embed.c @@ -0,0 +1,207 @@ +/****************************************************************************** +* Copyright (c) 2013 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +// Embed Python into the simulator using GPI + +#include +#include "gpi.h" +#include "gpi_logging.h" + +static PyThreadState *gtstate; +static gpi_cb_hdl sim_init_cb; +static gpi_cb_hdl sim_finish_cb; + + + +static char *progname = "cocotb"; +static PyObject *thread_dict; +static PyObject *lock; + + + +/** + * @name Initialise the python interpreter + * @brief Create and initialise the python interpreter + * @ingroup python_c_api + * + * GILState before calling: N/A + * + * GILState after calling: released + * + * Stores the thread state for cocotb in static variable gtstate + */ +void init_python(void) +{ + FENTER; + + // Don't initialise python if already running + if (gtstate) + return; + + Py_SetProgramName(progname); + Py_Initialize(); /* Initialize the interpreter */ + PyEval_InitThreads(); /* Create (and acquire) the interpreter lock */ + + /* Swap out and return current thread state and release the GIL */ + gtstate = PyEval_SaveThread(); + FEXIT; +} + + + +/** + * @name Initialisation + * @brief Called by the simulator on initialisation. Load cocotb python module + * @ingroup python_c_api + * + * GILState before calling: Not held + * + * GILState after calling: Not held + * + * Makes one call to PyGILState_Ensure and one call to PyGILState_Release + * + * Loads the Python module called cocotb and calls the _initialise_testbench function + */ +int handle_sim_init(void *gpi_cb_data) +{ + FENTER + + // Find the simulation root + gpi_sim_hdl dut = gpi_get_root_handle(); + + PyObject *pName, *pModule, *pDict, *pFunc, *pArgs; + PyObject *pValue, *pLogger; + + + //Ensure that the current thread is ready to callthe Python C API + PyGILState_STATE gstate = PyGILState_Ensure(); + // Python allowed + + pModule = PyImport_Import(PyString_FromString("cocotb")); + + if (pModule == NULL) + { + PyErr_Print(); + fprintf(stderr, "Failed to load \"%s\"\n", "cocotb"); + PyGILState_Release(gstate); + return 1; + } + + + // Extact a reference to the logger object to unitfy logging mechanism + pLogger = PyObject_GetAttrString(pModule, "log"); + PyObject *pHandler= PyObject_GetAttrString(pLogger, "handle"); // New reference + PyObject *pRecordFn= PyObject_GetAttrString(pLogger, "makeRecord"); + PyObject *pFilterFn= PyObject_GetAttrString(pLogger, "filter"); + + if (pLogger == NULL || pHandler == NULL || pRecordFn == NULL) + { + PyErr_Print(); + fprintf(stderr, "Failed to find handle to logging object \"log\" from module cocotb\n"); + PyGILState_Release(gstate); + return 1; + } + + set_log_handler(pHandler); + set_make_record(pRecordFn); + set_log_filter(pFilterFn); + + + Py_DECREF(pLogger); + Py_DECREF(pHandler); + Py_DECREF(pRecordFn); + Py_DECREF(pFilterFn); + + // Save a handle to the lock object + lock = PyObject_GetAttrString(pModule, "_rlock"); + + LOG_INFO("Python interpreter initialised and cocotb loaded!"); + + pFunc = PyObject_GetAttrString(pModule, "_initialise_testbench"); // New reference + + if (pFunc == NULL || !PyCallable_Check(pFunc)) + { + if (PyErr_Occurred()) + PyErr_Print(); + fprintf(stderr, "Cannot find function \"%s\"\n", "_initialise_testbench"); + Py_DECREF(pFunc); + Py_DECREF(pModule); + PyGILState_Release(gstate); + return 1; + } + + pArgs = PyTuple_New(1); + PyTuple_SetItem(pArgs, 0, PyLong_FromLong((long)dut)); // Note: This function “steals†a reference to o. + pValue = PyObject_CallObject(pFunc, pArgs); + + if (pValue != NULL) + { + LOG_INFO("_initialise_testbench successful"); + Py_DECREF(pValue); + } else { + PyErr_Print(); + fprintf(stderr,"Call failed\n"); + gpi_sim_end(); + } + + Py_DECREF(pFunc); + Py_DECREF(pModule); + + PyGILState_Release(gstate); + + FEXIT + return(0); +} + +void register_initial_callback() +{ + FENTER + sim_init_cb = gpi_register_sim_start_callback(handle_sim_init, (void *)NULL); + FEXIT +} + + +// NB We don't attempt to log here since the world has fallen apart +int handle_sim_end(void *gpi_cb_data) +{ + return(0); +} + +void register_final_callback() +{ + FENTER + sim_finish_cb = gpi_register_sim_end_callback(handle_sim_end, (void *)NULL); + FEXIT +} + + +void (*vlog_startup_routines[])() = { + init_python, + register_initial_callback, + register_final_callback, + 0 +}; diff --git a/examples/Makefile b/examples/Makefile new file mode 100644 index 00000000..5a13e864 --- /dev/null +++ b/examples/Makefile @@ -0,0 +1,39 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + + +SMOKE_TESTS := simple \ + demo \ + comb_seq + +.PHONY: $(SMOKE_TESTS) + +all: $(SMOKE_TESTS) + +$(SMOKE_TESTS): + @cd $@ && $(MAKE) diff --git a/examples/comb_seq/Makefile b/examples/comb_seq/Makefile new file mode 100644 index 00000000..387cd068 --- /dev/null +++ b/examples/comb_seq/Makefile @@ -0,0 +1,35 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +TOPLEVEL = issue12 + +VERILOG_SOURCES = issue12.v +MODULE=issue12 +FUNCTION=issue12 + +include $(SIM_ROOT)/makefiles/Makefile.sim diff --git a/examples/comb_seq/issue12.py b/examples/comb_seq/issue12.py new file mode 100644 index 00000000..89371a80 --- /dev/null +++ b/examples/comb_seq/issue12.py @@ -0,0 +1,123 @@ +#!/bin/python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + + +""" + Test bench for issue 12 + + + Expected behaviour over 6 clock cycles + + start of day + ready <= 0 + + + clock tick 1 + drive ready to 1 + drive 1 onto data in + data_out_comb changes to 1 + + clock tick 2 + drive ready to 0 + data_out_registered changes to 1 (driven by process inside the sim) + + etc. + +""" + +import cocotb +from cocotb.decorators import coroutine +from cocotb.triggers import Timer, Edge, Event, RisingEdge, ReadOnly +from cocotb.clock import Clock + +@coroutine +def ready_fiddler(clock, ready): + """Assert ready every other clock cycle""" + v = 0 + ready <= v + while True: + yield RisingEdge(clock) + v = not v + ready <= v + +@coroutine +def driver(clock, ready, data): + """Drives incrementing values onto a bus each time ready is high""" + data <= 0 + while True: + yield RisingEdge(ready) + data <= data.value.value + 1 + +@coroutine +def issue12(dut): + dut.log.info("Test got DUT:" + str(dut)) + + # convenience + clock = dut.clk + ready = dut.stream_in_ready + dout_comb = dut.stream_out_data_comb + dout_regd = dut.stream_out_data_registered + + clock <= 0 + + dut.stream_out_ready <= 0 + + real_clock = Clock(clock, 1000) + yield Timer(1000) + + # kick off the coroutines + rdys = cocotb.scheduler.add(ready_fiddler(dut.clk, dut.stream_out_ready)) + drvr = cocotb.scheduler.add(driver(dut.clk, dut.stream_in_ready, dut.stream_in_data)) + + yield Timer(1000) + real_clock.start(12) + + expected_comb = 0 + expected_regd = 0 + failed = 0 + + for clock_tick in range(6): + yield RisingEdge(dut.clk) + yield ReadOnly() + if ready.value.value: expected_comb += 1 + dut.log.info("ready: %s\tdout_comb: %d\tdout_regd: %d" % (ready.value.value, dout_comb.value.value, dout_regd.value.value)) + if dout_comb.value.value != expected_comb: + dut.log.error("Expected dout_comb to be %d but got %d" % (expected_comb, dout_comb.value.value)) + failed += 1 + if dout_regd.value.value != expected_regd: + dut.log.error("Expected dout_regd to be %d but got %d" % (expected_regd, dout_regd.value.value)) + failed += 1 + expected_regd = expected_comb + + # FIXME the sim exits when the cgen finishes but we still want this code to run + # If caching writes then this code is run, if not then we exit prematurely + dut.log.info("test complete!") + if failed: + dut.log.critical("%d failures!" % (failed)) + else: + dut.log.warn("Test Passed") + diff --git a/examples/comb_seq/issue12.tcl b/examples/comb_seq/issue12.tcl new file mode 100644 index 00000000..84e875e8 --- /dev/null +++ b/examples/comb_seq/issue12.tcl @@ -0,0 +1,43 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +# create project library and clear its contents +adel -all +alib work +set worklib work + +alog -dbg ../issue12.v + +# initialize simulation +asim +access +w -O2 -dbg -pli libcocotb issue12 + +# advance simulation +run -all + +# uncomment following line to terminate simulation automatically from script +endsim + diff --git a/examples/comb_seq/issue12.v b/examples/comb_seq/issue12.v new file mode 100644 index 00000000..ea176e13 --- /dev/null +++ b/examples/comb_seq/issue12.v @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2013 Potential Ventures Ltd +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Potential Ventures Ltd nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + + +`timescale 1 ps / 1 ps +/* + + Looking at combinatorial and registered outputs. + +*/ + +module issue12 ( + input clk, + + output reg stream_in_ready, + input stream_in_valid, + input [7:0] stream_in_data, + + input stream_out_ready, + output reg [7:0] stream_out_data_comb, + output reg [7:0] stream_out_data_registered +); + +always @(posedge clk) + stream_out_data_registered <= stream_in_data; + +always @(stream_in_data) + stream_out_data_comb = stream_in_data; + +always @(stream_out_ready) + stream_in_ready = stream_out_ready; + + +initial begin + $dumpfile("waveform.vcd"); + $dumpvars(0,issue12); +end + +endmodule diff --git a/examples/demo/Makefile b/examples/demo/Makefile new file mode 100644 index 00000000..5369fd26 --- /dev/null +++ b/examples/demo/Makefile @@ -0,0 +1,36 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +# FIXME these should come from some cunning script, possibly from the FDK :) +TOPLEVEL = first_counter + +VERILOG_SOURCES = counter.v +MODULE=demo +FUNCTION=example_test + +include $(SIM_ROOT)/makefiles/Makefile.sim diff --git a/examples/demo/counter.v b/examples/demo/counter.v new file mode 100644 index 00000000..7e95350a --- /dev/null +++ b/examples/demo/counter.v @@ -0,0 +1,87 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2013 Potential Ventures Ltd +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Potential Ventures Ltd nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +//----------------------------------------------------- +// This is my second Verilog Design +// Design Name : first_counter +// File Name : first_counter.v +// Function : This is a 4 bit up-counter with +// Synchronous active high reset and +// with active high enable signal +//----------------------------------------------------- +module first_counter ( +clock , // Clock input of the design +reset , // active high, synchronous Reset input +enable , // Active high enable signal for counter +counter_out, // 4 bit vector output of the counter +test_in, // test input +test_out // test output +); // End of port list +//-------------Input Ports----------------------------- +input clock ; +input reset ; +input enable ; +input test_in ; +//-------------Output Ports---------------------------- +output [3:0] counter_out ; +output test_out ; +//-------------Input ports Data Type------------------- +// By rule all the input ports should be wires +wire clock ; +wire reset ; +wire enable ; +wire test_in ; +//-------------Output Ports Data Type------------------ +// Output port can be a storage element (reg) or a wire +reg [3:0] counter_out ; +reg test_out ; + +initial begin + $dumpfile("dump.vcd"); + $dumpvars(0,first_counter); +end + +//------------Code Starts Here------------------------- +// Since this counter is a positive edge trigged one, +// We trigger the below block with respect to positive +// edge of the clock. +always @ (posedge clock) +begin : COUNTER // Block Name + // At every rising edge of clock we check if reset is active + // If active, we load the counter output with 4'b0000 + if (reset == 1'b1) begin + counter_out <= #1 4'b0000; + test_out <= #1 0; + end + // If enable is active, then we increment the counter + else if (enable == 1'b1) begin + counter_out <= #1 counter_out + 1; + test_out <= test_in; + end +end // End of Block COUNTER + +endmodule // End of Module counter diff --git a/examples/demo/demo.py b/examples/demo/demo.py new file mode 100644 index 00000000..a40d012a --- /dev/null +++ b/examples/demo/demo.py @@ -0,0 +1,103 @@ +#!/bin/python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" + Example usage of the testbench framework +""" + +import cocotb +from cocotb.decorators import coroutine +from cocotb.triggers import Timer, Edge, Event + +@coroutine +def clock_generator(signal): + for i in range(10): + signal <= 0 + yield Timer(1000) + signal <= 1 + yield Timer(1000) + signal.log.warning("Clock generator finished!") + +@coroutine +def clock_monitor(signal, output): + while True: + yield Edge(signal) + signal.log.info("Edge triggered: %s : %s = %s" % (signal.getvalue().value, str(output), output.getvalue().value)) + + +@coroutine +def reset_dut(clock, reset, enable): + clock_ticks = 0 + reset <= 1 + enable <= 0 + while True: + yield Edge(clock) + clock_ticks += 1 + if clock_ticks >= 2: + reset <= 0 + enable <= 1 + break + reset.log.info("Released reset: %s" % reset.getvalue()) + + +@coroutine +def waiting_coroutine(some_event): + some_event.log.info("Putting waiting coroutine to sleep until this event fires") + yield some_event.wait() + some_event.log.info("Coroutine woke up again! Awesome") + +@coroutine +def example_test(dut): + dut.log.info("Example test got DUT:" + str(dut)) + + yield Timer(10000) + + clk = dut.clock + enable = dut.enable + reset = dut.reset + count = dut.counter_out + + dut.log.info(str(clk)) + + cgen = cocotb.scheduler.add(clock_generator(clk)) + yield reset_dut(clk, reset, enable) + dut.log.info("Reset DUT complete, continuing test...") + cmon = cocotb.scheduler.add(clock_monitor(clk, count)) + + dut.log.info("Blocking test until the clock generator finishes...") + yield cgen.join() + + sync = Event() + cocotb.scheduler.add(waiting_coroutine(sync)) + yield Timer(10000) + dut.log.info("Waking up the waiting coroutine with an event...") + sync.set() + yield Timer(10000) + + + result = yield Timer(1000000) + dut.log.warning("test complete!") diff --git a/examples/demo/demo.tcl b/examples/demo/demo.tcl new file mode 100644 index 00000000..e7a74d81 --- /dev/null +++ b/examples/demo/demo.tcl @@ -0,0 +1,43 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +# create project library and clear its contents +adel -all +alib work +set worklib work + +alog -dbg ../counter.v + +# initialize simulation +asim +access +w -O2 -dbg -pli libcocotb first_counter + +# advance simulation +run -all + +# uncomment following line to terminate simulation automatically from script +endsim + diff --git a/examples/demo/library.cfg b/examples/demo/library.cfg new file mode 100644 index 00000000..97565eea --- /dev/null +++ b/examples/demo/library.cfg @@ -0,0 +1,2 @@ +$INCLUDE = "$VSIMSALIBRARYCFG" +work = "./work/work.lib" 1367432733288 diff --git a/examples/simple/Makefile b/examples/simple/Makefile new file mode 100644 index 00000000..673dfc7f --- /dev/null +++ b/examples/simple/Makefile @@ -0,0 +1,36 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +# FIXME these should come from some cunning script, possibly from the FDK :) +TOPLEVEL = packet_tap + +VERILOG_SOURCES = packet_tap.v +MODULE=smoketest +FUNCTION=smoketest + +include $(SIM_ROOT)/makefiles/Makefile.sim diff --git a/examples/simple/packet_tap.v b/examples/simple/packet_tap.v new file mode 100644 index 00000000..c322adb6 --- /dev/null +++ b/examples/simple/packet_tap.v @@ -0,0 +1,111 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2013 Potential Ventures Ltd +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Potential Ventures Ltd nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- +`timescale 1 ps / 1 ps + +module pv_packet_tap ( + input clk, + input reset_n, + + // Stream_in bus + input stream_in_startofpacket, + input stream_in_endofpacket, + input stream_in_valid, + output stream_in_ready, + input [71:0] stream_in_data, + input [2:0] stream_in_empty, + input [1:0] stream_in_error, + input stream_in_channel, + + // Stream_out bus + output reg stream_out_startofpacket, + output reg stream_out_endofpacket, + output reg stream_out_valid, + input stream_out_ready, + output reg [71:0] stream_out_data, + output reg [2:0] stream_out_empty, + output reg [1:0] stream_out_error, + output reg stream_out_channel, + + // Tap point + output tap_out_startofpacket, + output tap_out_endofpacket, + output tap_out_valid, + input tap_out_ready, + output [71:0] tap_out_data, + output [2:0] tap_out_empty, + output [1:0] tap_out_error, + output tap_out_channel, + + + // Avalon-MM interface + input [6:0] csr_address, + output [31:0] csr_readdata, + input csr_read, + input csr_write, + output csr_waitrequest, + input [31:0] csr_writedata +); + +// Cross-wire the Avalon-ST bus +always @(posedge clk) begin + stream_out_startofpacket <= stream_in_startofpacket; + stream_out_endofpacket <= stream_in_endofpacket; + stream_out_valid <= stream_in_valid; + stream_out_data <= stream_in_data; + stream_out_empty <= stream_in_empty; + stream_out_error <= stream_in_error; + stream_out_channel <= stream_in_channel; +end + +// assign stream_out_startofpacket = stream_in_startofpacket; +// assign stream_out_endofpacket = stream_in_endofpacket; +// assign stream_out_valid = stream_in_valid; +// assign stream_out_data = stream_in_data; +// assign stream_out_empty = stream_in_empty; +// assign stream_out_error = stream_in_error; +// assign stream_out_channel = stream_in_channel; + + +// For now just tap everything +assign tap_out_startofpacket = stream_in_startofpacket; +assign tap_out_endofpacket = stream_in_endofpacket; +assign tap_out_valid = stream_in_valid; +assign tap_out_data = stream_in_data; +assign tap_out_empty = stream_in_empty; +assign tap_out_error = stream_in_error; +assign tap_out_channel = stream_in_channel; + +assign stream_in_ready = stream_out_ready & tap_out_ready; + + +initial begin + $dumpfile("waveform.vcd"); + $dumpvars(0,pv_packet_tap); +end + + +endmodule diff --git a/examples/simple/smoketest.py b/examples/simple/smoketest.py new file mode 100644 index 00000000..58e51b87 --- /dev/null +++ b/examples/simple/smoketest.py @@ -0,0 +1,83 @@ +#!/bin/python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" + Smoketest to bring up drivers and monitors +""" + +import cocotb +from cocotb.generators import feeds +from cocotb.decorators import coroutine +from cocotb.triggers import Timer, Edge, Event +from cocotb.drivers import SFStreaming +from cocotb.monitors import SFStreaming as SFMon +from cocotb.generators.feeds.itch_feed import * + +from modules.sf_streaming.model.sf_streaming import SFStreamingPacket + +@coroutine +def clock_generator(signal, period_ps): + t = Timer(period_ps) + while True: + signal <= 0 + yield t + signal <= 1 + yield t + +@coroutine +def smoketest(dut): + dut.log.info("Test started, got DUT:" + str(dut)) + + clock_gen = cocotb.scheduler.add(clock_generator(dut.clk, 3200)) + dut.log.info("Clock started...") + + dut.stream_in_ready <= 1 + + yield Timer(32000) + + stream_in = SFStreaming(dut, "stream_in", dut.clk) + stream_out = SFMon(dut, "stream_out", dut.clk) + + yield Timer(32000) + + test_feed = ItchFeed("test itch", 1234, 13) + test_feed.addmsg("An Itch format message") + test_feed.addmsg("Another Itch format message") + test_feed.addmsg("The last Itch test") + + for repeat in range(2): + stream_in.append(SFStreamingPacket(test_feed.getmsg())) + + final_pkt = Event("final_packet") + stream_in.append(SFStreamingPacket(test_feed.getmsg()), event=final_pkt) + + dut.log.info("Waiting for all packets to be sent...") + yield final_pkt.wait() + dut.log.info("All packets sent, cleaning up...") + yield Timer(32000) + clock_gen.kill() + dut.log.warning("Test complete!") diff --git a/gpi/Makefile b/gpi/Makefile new file mode 100644 index 00000000..bcdcda1a --- /dev/null +++ b/gpi/Makefile @@ -0,0 +1,46 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +include $(SIM_ROOT)/makefiles/Makefile.inc + +OBJ_DIR := obj +INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ +GCC_ARGS := -Werror -g + +all : $(LIB_DIR)/gpi_logging.so + +$(OBJ_DIR): + mkdir -p $(OBJ_DIR) + +$(LIB_DIR)/gpi_logging.so: $(OBJ_DIR) + gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $(OBJ_DIR)/gpi_logging.o gpi_logging.c + gcc $(GCC_ARGS) -shared -export-dynamic -o $(LIB_DIR)/gpi_logging.so $(OBJ_DIR)/gpi_logging.o -lpython2.7 + ln -sf gpi_logging.so $(LIB_DIR)/libgpilog.so + +clean : + rm -rf $(OBJ_DIR) diff --git a/gpi/gpi.h b/gpi/gpi.h new file mode 100644 index 00000000..896b5740 --- /dev/null +++ b/gpi/gpi.h @@ -0,0 +1,148 @@ +/****************************************************************************** +* Copyright (c) 2013 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +/* +gpi.h + +Generic Language Interface + +This header file defines a Generic Language Interface into any simulator. +Implementations should include this header file and MUST + +The functions are essentially a limited subset of VPI/VHPI/FLI. + +Implementation specific notes +============================= + +By amazing coincidence, VPI and VHPI are strikingly similar which is obviously +reflected by this header file. Unfortunately, this means that proprietry, +non-standard, less featured language interfaces (for example Mentor FLI) may have +to resort to some hackery, or may not even be capable of implementing a GPI layer. + +Because of the lack of ability to register a callback on event change using the FLI, +we have to create a process with the signal on the sensitivity list to imitate a callback. + +*/ + +#ifndef __GPI_H +#define __GPI_H + +#include +#include +#include +#include + +#if defined(__MINGW32__) || defined (__CYGWIN32__) +# define DLLEXPORT __declspec(dllexport) +#else +# define DLLEXPORT +#endif + +#ifdef __cplusplus +# define EXTERN_C_START extern "C" { +# define EXTERN_C_END } +#else +# define EXTERN_C_START +# define EXTERN_C_END +#endif + +#ifndef __GNUC__ +# undef __attribute__ +# define __attribute__(x) +#endif + + +EXTERN_C_START + +// Define a type for our simulation handle. +typedef struct __gpi_sim_hdl *gpi_sim_hdl; +// Define a callback handle type for registered callbacks. +typedef struct __gpi_cb_hdl *gpi_cb_hdl; +// Define a handle type for iterators +typedef struct __gpi_iterator_hdl *gpi_iterator_hdl; +// Define a type of a clock object +typedef struct gpi_clock_s { + int period; + int value; + unsigned int max_cycles; + unsigned int curr_cycle; + bool exit; + gpi_sim_hdl sim_hdl; + gpi_cb_hdl cb_hdl; +} gpi_clock_t; + +typedef gpi_clock_t *gpi_clock_hdl; + + +// Functions for controlling/querying the simulation state + +// Stop the simulator +void gpi_sim_end(); + + +// Returns simulation time as a float. Units are default sim units +void gpi_get_sim_time(uint32_t *high, uint32_t *low); + + +// Functions for extracting a gpi_sim_hdl to an object +// Returns a handle to the root simulation object +gpi_sim_hdl gpi_get_root_handle(); +gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent); + + + +// Functions for querying the properties of a handle + +// Caller responsible for freeing the returned string. +// This is all slightly verbose but it saves having to enumerate various value types +// We only care about a limited subset of values. +char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl); +char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl); +char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl); + + + +// Functions for setting the properties of a handle +void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value); +void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str); // String of binary char(s) [1, 0, x, z] + +// The callback registering functions all return a gpi_cb_hdl; +gpi_cb_hdl gpi_register_sim_start_callback (int (*gpi_function)(void *), void *gpi_cb_data); +gpi_cb_hdl gpi_register_sim_end_callback (int (*gpi_function)(void *), void *gpi_cb_data); +gpi_cb_hdl gpi_register_timed_callback (int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps); +gpi_cb_hdl gpi_register_value_change_callback (int (*gpi_function)(void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl); +gpi_cb_hdl gpi_register_readonly_callback (int (*gpi_function)(void *), void *gpi_cb_data); +gpi_cb_hdl gpi_register_nexttime_callback (int (*gpi_function)(void *), void *gpi_cb_data); +gpi_cb_hdl gpi_register_readwrite_callback (int (*gpi_function)(void *), void *gpi_cb_data); + +int gpi_deregister_callback(gpi_cb_hdl gpi_hdl); +gpi_clock_hdl gpi_clock_register(gpi_sim_hdl sim_hdl, int period, unsigned int cycles); +void gpi_clock_unregister(gpi_clock_hdl clock); + +EXTERN_C_END + +#endif // __GPI_H diff --git a/gpi/gpi_logging.c b/gpi/gpi_logging.c new file mode 100644 index 00000000..c209aa6d --- /dev/null +++ b/gpi/gpi_logging.c @@ -0,0 +1,132 @@ +/****************************************************************************** +* Copyright (c) 2013 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +#include + +// Used to log using the standard python mechanism +static PyObject *pLogHandler; +static PyObject *pLogFilter; +static PyObject *pLogMakeRecord; + +void set_log_handler(void *handler) +{ + pLogHandler = (PyObject *)handler; + Py_INCREF(pLogHandler); +} + +void set_make_record(void *makerecord) +{ + pLogMakeRecord = (PyObject *)makerecord; + Py_INCREF(pLogMakeRecord); +} + +void set_log_filter(void *filter) +{ + pLogFilter = (PyObject *)filter; + Py_INCREF(pLogFilter); +} + + +/** + * @name GPI logging + * @brief Write a log message using Python logging module + * @ingroup python_c_api + * + * GILState before calling: Unknown + * + * GILState after calling: Unknown + * + * Makes one call to PyGILState_Ensure and one call to PyGILState_Release + * + * If the Python logging mechanism is not initialised, dumps to stderr. + * + * TODO: correct handling of VARARGS for format strings + */ +void gpi_log(const char *name, long level, const char *pathname, const char *funcname, long lineno, const char *msg, ...) +{ + + if (pLogMakeRecord != NULL && pLogHandler != NULL && pLogFilter != NULL) { + + //Ensure that the current thread is ready to callthe Python C API + PyGILState_STATE gstate = PyGILState_Ensure(); + + if (PyCallable_Check(pLogMakeRecord) && PyCallable_Check(pLogHandler)) { + PyObject *pArgs = PyTuple_New(7); + PyTuple_SetItem(pArgs, 0, PyString_FromString(name)); // Note: This function “steals†a reference to o. + PyTuple_SetItem(pArgs, 1, PyInt_FromLong(level)); // Note: This function “steals†a reference to o. + PyTuple_SetItem(pArgs, 2, PyString_FromString(pathname)); // Note: This function “steals†a reference to o. + PyTuple_SetItem(pArgs, 3, PyInt_FromLong(lineno)); // Note: This function “steals†a reference to o. + PyTuple_SetItem(pArgs, 4, PyString_FromString(msg)); // Note: This function “steals†a reference to o. + PyTuple_SetItem(pArgs, 5, Py_None); //NONE + PyTuple_SetItem(pArgs, 6, Py_None); //NONE + + Py_INCREF(Py_None); // Need to provide a reference to steal + Py_INCREF(Py_None); // Need to provide a reference to steal + + PyObject *pDict; + pDict = Py_BuildValue("{s:s}", "func", funcname); + + PyObject *pLogRecord = PyObject_Call(pLogMakeRecord, pArgs, pDict); + Py_DECREF(pArgs); + Py_DECREF(pDict); + + PyObject *pLogArgs = PyTuple_Pack(1, pLogRecord); + Py_DECREF(pLogRecord); + + // Filter here + PyObject *pShouldFilter = PyObject_CallObject(pLogFilter, pLogArgs); + if (pShouldFilter == Py_True) { + PyObject *pLogResult = PyObject_CallObject(pLogHandler, pLogArgs); + Py_DECRE(pLogResult); + } + + Py_DECREF(pShouldFilter); + Py_DECREF(pLogArgs); + + } else { + fprintf(stderr, "ERROR: Unable to log into python - logging functions aren't callable\n"); + fprintf(stderr, "%s", msg); + fprintf(stderr, "\n"); + } + + // Matching call to release GIL + PyGILState_Release(gstate); + + // Python logging not available, just dump to stdout (No filtering) + } else { + fprintf(stdout, " -.--ns"); + fprintf(stdout, " %2ld", level); // FIXME: Print msglevel DEBUG INFO etc. + fprintf(stdout, "%16s", name); + fprintf(stdout, "%45s:", pathname); + fprintf(stdout, "%4ld", lineno); + fprintf(stdout, " in %s\t", funcname); + fprintf(stdout, "%25s", msg); + fprintf(stdout, "\n"); + } + + return; +} diff --git a/gpi/gpi_logging.h b/gpi/gpi_logging.h new file mode 100644 index 00000000..db93a2a4 --- /dev/null +++ b/gpi/gpi_logging.h @@ -0,0 +1,78 @@ +/****************************************************************************** +* Copyright (c) 2013 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +/* +gpi_logging.h + +*/ +#ifndef __GPI_LOGGING_H +#define __GPI_LOGGING_H + +#ifdef __cplusplus +# define EXTERN_C_START extern "C" { +# define EXTERN_C_END } +#else +# define EXTERN_C_START +# define EXTERN_C_END +#endif + +#define DEBUG + +EXTERN_C_START + +enum gpi_log_levels { + GPIDebug= 10, + GPIInfo = 20, + GPIWarning = 30, + GPIError = 40, + GPICritical = 50 +}; + + +#define LOG_DEBUG(...) gpi_log("cocotb.gpi", GPIDebug, __FILE__, __func__, __LINE__, __VA_ARGS__); +#define LOG_INFO(...) gpi_log("cocotb.gpi", GPIInfo, __FILE__, __func__, __LINE__, __VA_ARGS__); +#define LOG_WARN(...) gpi_log("cocotb.gpi", GPIWarning, __FILE__, __func__, __LINE__, __VA_ARGS__); +#define LOG_ERROR(...) gpi_log("cocotb.gpi", GPIError, __FILE__, __func__, __LINE__, __VA_ARGS__); +#define LOG_CRITICAL(...) gpi_log("cocotb.gpi", GPICritical, __FILE__, __func__, __LINE__, __VA_ARGS__); + +#ifdef DEBUG +#define FENTER LOG_DEBUG(__func__) +#define FEXIT LOG_DEBUG(__func__) +#else +#define FENTER +#define FEXIT +#endif + +void set_log_handler(void *handler); +void set_make_record(void *makerecord); +void set_log_filter(void *filter); + +void gpi_log(const char *name, long level, const char *pathname, const char *funcname, long lineno, const char *msg, ...); + +EXTERN_C_END + +#endif // __GPI_H diff --git a/makefiles/Makefile.aldec b/makefiles/Makefile.aldec new file mode 100644 index 00000000..1980169c --- /dev/null +++ b/makefiles/Makefile.aldec @@ -0,0 +1,97 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +include $(SIM_ROOT)/scripts/Makefile.inc + +COCOTB = $(SIM_ROOT)/sim_core/cocotb +VPI_FILE = $(SIM_ROOT)/build/cocotb.vpi +OBJ_DIR := obj +VPI_LIB := aldecpli +VPI_LD_PATH := $(RIVERA_BASE)/lib + +all : sim + +$(OBJ_DIR) : + mkdir -p $(OBJ_DIR) + +core: + make -C $(SIM_ROOT)/sim_core + +# By default create output goes in a build directory +SIM_DIR ?= sim_output + +# Common Makefile for Aldec Riviera-PRO simulator + +ifeq ($(GUI),1) + CMD := riviera -nosplash +else + CMD := vsimsa +endif + +$(SIM_DIR): + mkdir -p $(SIM_DIR) + + + +# Aldec requires a process in the verilog - if the only +# timesteps are PLI initiated then the sim finishes immediately +# +# FIXME this doesn't work, still need this code in the toplevel GRRRR + $(SIM_DIR)/initial.v : $(SIM_DIR) + echo -e "module fake_initial;\n\ +initial begin\n\ + $display (\"Initial statement began\");\n\ + #99999999999 $finish;\n\ +end\n\ +endmodule\n" > $@ + +# Create a TCL script based on the list of $(VERILOG_SOURCES) +$(SIM_DIR)/runsim.tcl : $(VERILOG_SOURCES) $(SIM_DIR) + echo "alib work" > $@ + echo "set worklib work" >> $@ + cd $(SIM_DIR) && create_project.py $(VERILOG_SOURCES) >> runsim.tcl + echo "asim +access +w -O2 -dbg -pli libcocotb $(TOPLEVEL)" >> $@ +ifeq ($(GUI),1) + echo "wave -rec *" >> $@ +else + echo "run -all" >> $@ + echo "endsim" >> $@ +endif + +# Note it's the redirection of the output rather than the 'do' command +# that turns on batch mode (i.e. exit on completion/error) +.PHONY: sim + +sim: $(SIM_DIR)/runsim.tcl core + -cd $(SIM_DIR) && ln -sf $(VPI_FILE) libcocotb.so + cd $(SIM_DIR) && PYTHONPATH=$(LIB_DIR):$(PWD)/testbench:$(SIM_ROOT)/sim_core:$(PWD):$(PYTHONPATH) \ + LD_LIBRARY_PATH=$(LD_LIBRARY_PATH):$(VSIMSABIN)/Linux64 MODULE=$(MODULE) FUNCTION=$(FUNCTION) $(CMD) -do runsim.tcl | tee sim.log + +clean:: + rm -rf $(SIM_DIR) + diff --git a/makefiles/Makefile.doc b/makefiles/Makefile.doc new file mode 100644 index 00000000..3b6ef03b --- /dev/null +++ b/makefiles/Makefile.doc @@ -0,0 +1,50 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +# Set of common rules for building documentation + +# Assumes a subfolder called "documentation" containing: +# JSON waveforms named xxx.wavedrom +# Diagrams in SVG format named xxx.svg +# Markdown documents named xxx.md + +# Find the dependencies +WAVEFORMS = $(wildcard documentation/*.wavedrom) +DIAGRAMS = $(wildcard documentation/*.svg) + +# Define the default target as the documentation/$(dirname).html +documentation/$(shell basename $(shell pwd)).html : + + +documentation/%.html: documentation/%.md $(WAVEFORMS) $(DIAGRAMS) + cd documentation && python -m markdown -x wavedrom $(notdir $<) > $(notdir $@) + +clean:: + rm -rf documentation/*.html + + diff --git a/makefiles/Makefile.icarus b/makefiles/Makefile.icarus new file mode 100644 index 00000000..448cccf4 --- /dev/null +++ b/makefiles/Makefile.icarus @@ -0,0 +1,53 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +include $(SIM_ROOT)/makefiles/Makefile.inc + +OBJ_DIR := obj +VPI_LIB := vpi +VPI_LD_PATH := $(ICARUS_BASE)/lib + +all: $(OBJ_DIR) sim + +$(OBJ_DIR): + mkdir -p $(OBJ_DIR) + +.PHONY: sim +sim: $(OBJ_DIR) $(VERILOG_SOURCES) + iverilog -o $(OBJ_DIR)/sim.vvp $(VERILOG_SOURCES) + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ + vvp -M $(LIB_DIR) -m cocotb $(OBJ_DIR)/sim.vvp +.PHONY: gdb +gdb: $(OBJ_DIR) $(VERILOG_SOURCES) + iverilog -o $(OBJ_DIR)/sim.vvp $(VERILOG_SOURCES) + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ + gdb --args vvp -M $(LIB_DIR) -m cocotb $(OBJ_DIR)/sim.vvp + + +clean: + rm -rf $(OBJ_DIR) + diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc new file mode 100644 index 00000000..73df1d4c --- /dev/null +++ b/makefiles/Makefile.inc @@ -0,0 +1,43 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################## + +ifeq ($(SIM_ROOT),) +export SIM_ROOT:=$(pwd) +endif + +BUILD_DIR=$(SIM_ROOT)/build +ARCH=$(shell uname -m) + +VPI_INCLUDE_PATH := -I/home/smh/Downloads/ + +include $(SIM_ROOT)/makefiles/Makefile.paths + +export ARCH +export BUILD_DIR +export LIB_DIR=$(BUILD_DIR)/libs/$(ARCH) + +include $(SIM_ROOT)/makefiles/Makefile.pylib diff --git a/makefiles/Makefile.paths b/makefiles/Makefile.paths new file mode 100644 index 00000000..8b52a6fd --- /dev/null +++ b/makefiles/Makefile.paths @@ -0,0 +1,30 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +# Please set the paths to the various simulators here + +ICARUS_BASE:=/usr/local/icarus diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib new file mode 100644 index 00000000..5909339c --- /dev/null +++ b/makefiles/Makefile.pylib @@ -0,0 +1,77 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +# All common pyhon related rules + +ifeq ($(ARCH),x86_64) +LIBDIR:=lib64 +else +LIBDIR:=lib +endif + +PYTHON_VERSION:=2.7 +PYTHON_DIR:=/usr/$(LIBDIR)/python$(PYTHON_VERSION) + +# Stupid Python doesn't seem to have a consistent naming convention + +PYMODULE_CASE:=$(shell if [ -f $(PYTHON_DIR)/lib-dynload/_functoolsmodule.so ]; then echo 1; else echo 0; fi) + +ifeq ($(PYMODULE_CASE),1) +PYLIBS = $(PYTHON_DIR)/lib-dynload/_functoolsmodule.so \ + $(PYTHON_DIR)/lib-dynload/_socketmodule.so \ + $(PYTHON_DIR)/lib-dynload/_collectionsmodule.so \ + $(PYTHON_DIR)/lib-dynload/operator.so \ + $(PYTHON_DIR)/lib-dynload/itertoolsmodule.so \ + $(PYTHON_DIR)/lib-dynload/timemodule.so \ + $(PYTHON_DIR)/lib-dynload/cStringIO.so \ + $(PYTHON_DIR)/lib-dynload/selectmodule.so \ + $(PYTHON_DIR)/lib-dynload/fcntlmodule.so \ + $(PYTHON_DIR)/lib-dynload/_struct.so \ + $(PYTHON_DIR)/lib-dynload/binascii.so \ + $(PYTHON_DIR)/lib-dynload/math.so \ + $(PYTHON_DIR)/lib-dynload/_randommodule.so \ + $(PYTHON_DIR)/lib-dynload/zlibmodule.so \ + $(PYTHON_DIR)/lib-dynload/cPickle.so \ + $(PYTHON_DIR)/lib-dynload/arraymodule.so +else +PYLIBS = $(PYTHON_DIR)/lib-dynload/_functools.so \ + $(PYTHON_DIR)/lib-dynload/_socket.so \ + $(PYTHON_DIR)/lib-dynload/_collections.so \ + $(PYTHON_DIR)/lib-dynload/operator.so \ + $(PYTHON_DIR)/lib-dynload/itertools.so \ + $(PYTHON_DIR)/lib-dynload/time.so \ + $(PYTHON_DIR)/lib-dynload/cStringIO.so \ + $(PYTHON_DIR)/lib-dynload/select.so \ + $(PYTHON_DIR)/lib-dynload/fcntl.so \ + $(PYTHON_DIR)/lib-dynload/_struct.so \ + $(PYTHON_DIR)/lib-dynload/binascii.so \ + $(PYTHON_DIR)/lib-dynload/math.so \ + $(PYTHON_DIR)/lib-dynload/_random.so \ + $(PYTHON_DIR)/lib-dynload/zlib.so \ + $(PYTHON_DIR)/lib-dynload/cPickle.so \ + $(PYTHON_DIR)/lib-dynload/array.so +endif diff --git a/makefiles/Makefile.sim b/makefiles/Makefile.sim new file mode 100644 index 00000000..3cb69ccd --- /dev/null +++ b/makefiles/Makefile.sim @@ -0,0 +1,41 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +SIM?=ICARUS + +ifeq ($(SIM),ICARUS) +include $(SIM_ROOT)/makefiles/Makefile.icarus +else +ifeq ($(SIM),ALDEC) +include $(SIM_ROOT)/makefiles/Makefile.aldec +else +ifeq ($(SIM),VCS) +include $(SIM_ROOT)/makefiles/Makefile.vcs +endif +endif +endif + diff --git a/modules/__init__.py b/modules/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/modules/sf_streaming/__init__.py b/modules/sf_streaming/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/modules/sf_streaming/model/__init__.py b/modules/sf_streaming/model/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/modules/sf_streaming/model/sf_streaming.py b/modules/sf_streaming/model/sf_streaming.py new file mode 100644 index 00000000..d07b869d --- /dev/null +++ b/modules/sf_streaming/model/sf_streaming.py @@ -0,0 +1,202 @@ +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" + All things sf_streaming bus related go here +""" + +from ctypes import * +from scapy.all import * + +# The only thing we export is the SFStreamingPacket +__all__ = ["SFStreamingPacket", "SFStreamingBusWord"] + +# Helper functions FIXME move somewhere common +def pack(ctypes_obj): + """Convert a ctypes structure into a python string""" + return string_at(addressof(ctypes_obj), sizeof(ctypes_obj)) + + +def unpack(ctypes_obj, string, bytes=None): + """Unpack a python string into a ctypes structure + + If the length of the string is not the correct size for the memory footprint of the + ctypes structure then the bytes keyword argument must be used + """ + if bytes is None and len(string) != sizeof(ctypes_obj): + raise ValueError("Attempt to unpack a string of size %d into a struct of size %d" % (len(string), sizeof(ctypes_obj))) + if bytes is None: bytes = len(string) + memmove(addressof(ctypes_obj), string, bytes) + + +# Enumerations for the detected protocol +SF_PKT_PROTO_RAW = 0 +SF_PKT_PROTO_TCP = 1 +SF_PKT_PROTO_UDP = 2 +SF_PKT_PROTO_IP_UNKNOWN = 3 + +class SFPktDesc(Structure): + """Descriptor containing the information about the payload of the packet. + + see sf_streaming_pkg.sv for full details + """ + _fields_ = [ + ("protocol", c_uint8, 2), + ("payload_offset", c_uint8, 6)] + +class SFMetaWord(Structure): + """First word prepended to each packet""" + _pack_ = 1 + _fields_ = [ + ("timestamp", c_uint32), + ("descriptor", SFPktDesc), + ("lookup_context", c_uint16), + ("scratch", c_uint8)] + + +class SFStreamingData(Structure): + _pack_ = 1 + _fields_ = [ + ("data", c_uint64), + ("ecc", c_uint8)] + +class SFStreamingBusWord(Structure): + _pack_ = 1 + _fields_ = [ + ("data", SFStreamingData), + ("empty", c_uint8, 3), + ("startofpacket", c_uint8, 1), + ("endofpacket", c_uint8, 1), + ("error", c_uint8, 2), + ("channel", c_uint8, 1)] + + def __str__(self): + return "sop: %d\teop: %d\tchannel: %d\tempty: %d\tdata: %016x" % (self.startofpacket, self.endofpacket, self.channel, self.empty, self.data.data) + +class SFStreamingPacket(object): + """Useful container class to make using the sf_streaming bus more convenient + + TODO: + Don't currently handle metawords in the middle of the packet. + + Probably want to make the underlying data structure an array of SFStreamingBusWord. + + We could then alias self.pkt to pull out the packet contents from the array. + """ + + def __init__(self, pkt): + """pkt is a string""" + self.metaword = SFMetaWord() + self.pkt = pkt + self.parse() + self._ptr = 0 + + def parse(self): + """Parse the packet and populate the metaword descriptor field + + FIXME: need to handle GRE here + """ + p = Ether(self.pkt) + + if p.payload.name != 'IP': + self.metaword.descriptor.protocol = SF_PKT_PROTO_RAW + self.metaword.descriptor.payload_offset = self.pkt.find(str(p.payload)) + return + + ip = p.payload + + if ip.payload.name == "UDP": + self.metaword.descriptor.protocol = SF_PKT_PROTO_UDP + self.metaword.descriptor.payload_offset = self.pkt.find(str(ip.payload.payload)) + return + + # For TCP we only point to the start of the IP payload since we don't + # currently parse out the TCP header + if ip.payload.name == "TCP": + self.metaword.descriptor.protocol = SF_PKT_PROTO_TCP + else: + self.metaword.descriptor.protocol = SF_PKT_PROTO_IP_UNKNOWN + + self.metaword.descriptor.payload_offset = self.pkt.find(str(ip.payload)) + return + + def __len__(self): + return len(self.pkt) + 8 + + def __iter__(self): + self._ptr = None + return self + + @property + def payload(self): + """Returns the payload of the packet as defined by the descriptor field""" + return str(self.pkt)[self.metaword.descriptor.payload_offset * 4 + 14:] + + def next(self): + if self._ptr >= len(self.pkt): raise StopIteration + + word = SFStreamingBusWord() + data = c_uint64() + + # Metaword first on channel 1 + if self._ptr is None: + unpack(data, pack(self.metaword)) + word.data.data = data + word.empty = 0 + word.startofpacket = 1 + word.endofpacket = 0 + word.channel = 1 + word.error = 0 + self._ptr = 8 + return word + + # Into the packet data + if self._ptr + 8 > len(self.pkt): + chunk = self.pkt[self._ptr:] + else: + chunk = self.pkt[self._ptr:self._ptr + 8] + unpack(data, chunk, bytes=len(chunk)) + word.data.data = data + word.empty = 8 - len(chunk) + word.channel = 0 + word.error = 0 + word.startofpacket = 0 + word.endofpacket = 0 + self._ptr += 8 + if self._ptr >= len(self.pkt): + word.endofpacket = 1 + return word + + +def test_stuff(): + pkt = Ether() / IP() / UDP() / "Here is some payload" + + wrapped_pkt = SFStreamingPacket(str(pkt)) + + for word in wrapped_pkt: + print str(word) + +if __name__ == "__main__": + test_stuff() diff --git a/scripts/set_env.sh b/scripts/set_env.sh new file mode 100755 index 00000000..780bcf65 --- /dev/null +++ b/scripts/set_env.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +# Source this to set up the sauce + +export SIM_ROOT="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd )" +export BUILD_DIR=$SIM_ROOT/build + + +export PATH=$SIM_ROOT/bin:$PATH +export PYTHONPATH=$BUILD_DIR:$SIM_ROOT/sim_core:$SIM_ROOT:$PYTHONPATH +export LD_LIBRARY_PATH=$BUILD_DIR:$LD_LIBRARY_PATH + diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..b534ebca --- /dev/null +++ b/setup.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +from setuptools import setup, find_packages +from distutils.command.install import install as DistutilsInstall + +class CocotbInstall(DistutilsInstall): + def run(self): + print("Build support libs\n") + DistutilsInstall.run(self) + +setup( + name='cocotb', + version='0.0.1', + description='Coroutine Cosimulation Test Bench', + author='Potential Ventures', + author_email='coctob@potentialventures.com', + packages=find_packages('.'), + cmdclass={'install': CocotbInstall}, + include_package_data = True, + long_description="""\ + Cocotb is a coroutines-based cosimulation test bench framework for rapid + development of FPGA components + """, + classifiers=[ + "License :: BSD", + "Programming Language :: Python", + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Topic :: Internet", + ], + keywords='simulation', + license='BSD', + ) diff --git a/simulator/Makefile b/simulator/Makefile new file mode 100644 index 00000000..ecc93253 --- /dev/null +++ b/simulator/Makefile @@ -0,0 +1,47 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +include $(SIM_ROOT)/makefiles/Makefile.inc +OBJ_DIR := obj +INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ -I../gpi +GCC_ARGS := -Werror -g +LIBS := -lgpi -lpython2.7 $(PYLIBS) +LD_PATH := -L$(LIB_DIR) + + +all : $(LIB_DIR)/simulator.so + +$(OBJ_DIR) : + mkdir -p $(OBJ_DIR) + +$(LIB_DIR)/simulator.so : $(OBJ_DIR) + gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $(OBJ_DIR)/simulatormodule.o simulatormodule.c + gcc $(GCC_ARGS) -shared -Xlinker -export-dynamic -o $(LIB_DIR)/simulator.so $(OBJ_DIR)/simulatormodule.o $(LIBS) $(LD_PATH) + ln -sf simulator.so $(LIB_DIR)/libsim.so + +clean : + @rm -rf $(OBJ_DIR) diff --git a/simulator/simulatormodule.c b/simulator/simulatormodule.c new file mode 100644 index 00000000..bbd7555f --- /dev/null +++ b/simulator/simulatormodule.c @@ -0,0 +1,598 @@ +/****************************************************************************** +* Copyright (c) 2013 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +/** +* @file simulatormodule.c +* @brief Python extension to provide access to the simulator +* +* Uses GPI calls to interface to the simulator. +*/ + +#include "simulatormodule.h" + +/** + * @name Callback Handling + * @brief Handle a callback coming from GPI + * @ingroup python_c_api + * + * GILState before calling: Unknown + * + * GILState after calling: Unknown + * + * Makes one call to PyGILState_Ensure and one call to PyGILState_Release + * + * Returns 0 on success or 1 on a failure. + * + * Handles a callback from the simulator, all of which call this function. + * + * We extract the associated context and find the Python function (usually + * cocotb.scheduler.react) calling it with a reference to the trigger that + * fired. The scheduler can then call next() on all the coroutines that + * are waiting on that particular trigger. + * + * TODO: + * - Tidy up return values + * - Ensure cleanup correctly in exception cases + * + */ +int handle_gpi_callback(void *user_data) +{ + p_callback_data callback_data_p = (p_callback_data)user_data; + + if (callback_data_p->id_value != COCOTB_ACTIVE_ID) { + fprintf(stderr, "Userdata corrupted!\n"); + return 1; + } + callback_data_p->id_value = COCOTB_INACTIVE_ID; + + + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); + + // Python allowed + + if (!PyCallable_Check(callback_data_p->function)) { + fprintf(stderr, "Callback fired but function isn't callable?!\n"); + PyGILState_Release(gstate); + return 1; + } + + // Call the callback + PyObject *pValue = PyObject_Call(callback_data_p->function, callback_data_p->args, callback_data_p->kwargs); + + // If the return value is NULL a Python exception has occurred + if (pValue == NULL) + { + fprintf(stderr, "ERROR: called callback function returned NULL\n"); + PyErr_Print(); + fprintf(stderr, "Failed to execute callback\n"); + PyGILState_Release(gstate); + gpi_sim_end(); + return 1; + } + + // Free up our mess + Py_DECREF(pValue); + Py_DECREF(callback_data_p->function); + Py_DECREF(callback_data_p->args); + + // Free the callback data + free(callback_data_p); + + PyGILState_Release(gstate); + + return 0; +} + + +static PyObject *log_msg(PyObject *self, PyObject *args) +{ + const char *name; + const char *path; + const char *msg; + const char *funcname; + int lineno; + + if (!PyArg_ParseTuple(args, "sssis", &name, &path, &funcname, &lineno, &msg)) + return NULL; + + gpi_log(name, GPIInfo, path, funcname, lineno, msg); + + return Py_BuildValue("s", "OK!"); +} + +// Register a callback for read only state of sim +// First argument is the function to call +// Remaining arguments are keyword arguments to be passed to the callback +static PyObject *register_readonly_callback(PyObject *self, PyObject *args) +{ + FENTER + + PyObject *fArgs; + PyObject *function; + uint64_t time_ps; + gpi_cb_hdl cb_hdl; + gpi_sim_hdl sig_hdl; + char *result; + PyObject *retstr; + + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); + + p_callback_data callback_data_p; + + Py_ssize_t numargs = PyTuple_Size(args); + + if (numargs < 1) { + fprintf(stderr, "Attempt to register ReadOnly callback with!\n"); + return NULL; + } + + // Extract the callback function + function = PyTuple_GetItem(args, 0); + if (!PyCallable_Check(function)) { + fprintf(stderr, "Attempt to register ReadOnly without supplying a callback!\n"); + return NULL; + } + Py_INCREF(function); + + // Remaining args for function + if (numargs > 1) + fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference + else + fArgs = PyTuple_New(0); // args must not be NULL, use an empty tuple if no arguments are needed. + + callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); + if (callback_data_p == NULL) { + printf("Failed to allocate user data\n"); + } + + // Set up the user data (no more python API calls after this! + callback_data_p->_saved_thread_state = PyThreadState_Get(); + callback_data_p->id_value = COCOTB_ACTIVE_ID; + callback_data_p->function = function; + callback_data_p->args = fArgs; + callback_data_p->kwargs = NULL; + callback_data_p->cb_hdl = gpi_register_readonly_callback(handle_gpi_callback, (void *)callback_data_p); + + PyObject *rv = Py_BuildValue("l", callback_data_p->cb_hdl); + PyGILState_Release(gstate); + FEXIT + + return rv; +} + +static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) +{ + FENTER + + PyObject *fArgs; + PyObject *function; + uint64_t time_ps; + gpi_cb_hdl cb_hdl; + gpi_sim_hdl sig_hdl; + char *result; + PyObject *retstr; + + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); + + p_callback_data callback_data_p; + + Py_ssize_t numargs = PyTuple_Size(args); + + if (numargs < 1) { + fprintf(stderr, "Attempt to register ReadOnly callback with!\n"); + return NULL; + } + + // Extract the callback function + function = PyTuple_GetItem(args, 0); + if (!PyCallable_Check(function)) { + fprintf(stderr, "Attempt to register ReadOnly without supplying a callback!\n"); + return NULL; + } + Py_INCREF(function); + + // Remaining args for function + if (numargs > 1) + fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference + else + fArgs = PyTuple_New(0); // args must not be NULL, use an empty tuple if no arguments are needed. + + callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); + if (callback_data_p == NULL) { + printf("Failed to allocate user data\n"); + } + + // Set up the user data (no more python API calls after this! + callback_data_p->_saved_thread_state = PyThreadState_Get(); + callback_data_p->id_value = COCOTB_ACTIVE_ID; + callback_data_p->function = function; + callback_data_p->args = fArgs; + callback_data_p->kwargs = NULL; + callback_data_p->cb_hdl = gpi_register_readwrite_callback(handle_gpi_callback, (void *)callback_data_p); + + PyObject *rv = Py_BuildValue("l", callback_data_p->cb_hdl); + PyGILState_Release(gstate); + FEXIT + + return rv; +} + +static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) +{ + FENTER + + PyObject *fArgs; + PyObject *function; + uint64_t time_ps; + gpi_cb_hdl cb_hdl; + gpi_sim_hdl sig_hdl; + char *result; + PyObject *retstr; + + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); + + p_callback_data callback_data_p; + + Py_ssize_t numargs = PyTuple_Size(args); + + if (numargs < 1) { + fprintf(stderr, "Attempt to register ReadOnly callback with!\n"); + return NULL; + } + + // Extract the callback function + function = PyTuple_GetItem(args, 0); + if (!PyCallable_Check(function)) { + fprintf(stderr, "Attempt to register ReadOnly without supplying a callback!\n"); + return NULL; + } + Py_INCREF(function); + + // Remaining args for function + if (numargs > 1) + fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference + else + fArgs = PyTuple_New(0); // args must not be NULL, use an empty tuple if no arguments are needed. + + callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); + if (callback_data_p == NULL) { + printf("Failed to allocate user data\n"); + } + + // Set up the user data (no more python API calls after this! + callback_data_p->_saved_thread_state = PyThreadState_Get(); + callback_data_p->id_value = COCOTB_ACTIVE_ID; + callback_data_p->function = function; + callback_data_p->args = fArgs; + callback_data_p->kwargs = NULL; + callback_data_p->cb_hdl = gpi_register_nexttime_callback(handle_gpi_callback, (void *)callback_data_p); + + PyObject *rv = Py_BuildValue("l", callback_data_p->cb_hdl); + PyGILState_Release(gstate); + FEXIT + + return rv; +} + + +// Register a timed callback. +// First argument should be the time in picoseconds +// Second argument is the function to call +// Remaining arguments and keyword arguments are to be passed to the callback +static PyObject *register_timed_callback(PyObject *self, PyObject *args) +{ + FENTER + + PyObject *fArgs; + PyObject *function; + uint64_t time_ps; + gpi_cb_hdl cb_hdl; + + p_callback_data callback_data_p; + + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); + + Py_ssize_t numargs = PyTuple_Size(args); + + if (numargs < 2) { + fprintf(stderr, "Attempt to register timed callback without enough arguments!\n"); + return NULL; + } + + // Extract the time + PyObject *pTime = PyTuple_GetItem(args, 0); + time_ps = PyLong_AsLongLong(pTime); + + // Extract the callback function + function = PyTuple_GetItem(args, 1); + if (!PyCallable_Check(function)) { + fprintf(stderr, "Attempt to register timed callback without passing a callable callback!\n"); + return NULL; + } + Py_INCREF(function); + + // Remaining args for function + if (numargs > 2) + fArgs = PyTuple_GetSlice(args, 2, numargs); // New reference + else + fArgs = PyTuple_New(0); // args must not be NULL, use an empty tuple if no arguments are needed. + + + callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); + if (callback_data_p == NULL) { + printf("Failed to allocate user data\n"); + } + + // Set up the user data (no more python API calls after this! + callback_data_p->_saved_thread_state = PyThreadState_Get(); + callback_data_p->id_value = COCOTB_ACTIVE_ID; + callback_data_p->function = function; + callback_data_p->args = fArgs; + callback_data_p->kwargs = NULL; + callback_data_p->cb_hdl = gpi_register_timed_callback(handle_gpi_callback, (void *)callback_data_p, time_ps); + + // Check success + PyObject *rv = Py_BuildValue("l", callback_data_p->cb_hdl); + PyGILState_Release(gstate); + FEXIT + + return rv; +} + +// Register signal change callback +// First argument should be the signal handle +// Second argument is the function to call +// Remaining arguments and keyword arguments are to be passed to the callback +static PyObject *register_value_change_callback(PyObject *self, PyObject *args) //, PyObject *keywds) +{ + FENTER + + PyObject *fArgs; + PyObject *function; + uint64_t time_ps; + gpi_cb_hdl cb_hdl; + gpi_sim_hdl sig_hdl; + char *result; + PyObject *retstr; + + + + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); + + p_callback_data callback_data_p; + + Py_ssize_t numargs = PyTuple_Size(args); + + if (numargs < 2) { + fprintf(stderr, "Attempt to register timed callback without enough arguments!\n"); + return NULL; + } + + PyObject *pSihHdl = PyTuple_GetItem(args, 0); + sig_hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(pSihHdl); + + // Extract the callback function + function = PyTuple_GetItem(args, 1); + if (!PyCallable_Check(function)) { + fprintf(stderr, "Attempt to register value change callback without passing a callable callback!\n"); + return NULL; + } + Py_INCREF(function); + + // Remaining args for function + if (numargs > 2) + fArgs = PyTuple_GetSlice(args, 2, numargs); // New reference + else + fArgs = PyTuple_New(0); // args must not be NULL, use an empty tuple if no arguments are needed. + + + callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); + if (callback_data_p == NULL) { + printf("Failed to allocate user data\n"); + } + // Set up the user data (no more python API calls after this! + // Causes segfault? + callback_data_p->_saved_thread_state = PyThreadState_Get();//PyThreadState_Get(); + callback_data_p->id_value = COCOTB_ACTIVE_ID; + callback_data_p->function = function; + callback_data_p->args = fArgs; + callback_data_p->kwargs = NULL; + callback_data_p->cb_hdl = gpi_register_value_change_callback(handle_gpi_callback, (void *)callback_data_p, sig_hdl); + + // Check success + PyObject *rv = Py_BuildValue("l", callback_data_p->cb_hdl); + + PyGILState_Release(gstate); + FEXIT + + return rv; +} + + + + +static PyObject *get_signal_val(PyObject *self, PyObject *args) +{ + gpi_sim_hdl hdl; + char *result; + PyObject *retstr; + + if (!PyArg_ParseTuple(args, "l", &hdl)) + return NULL; + + result = gpi_get_signal_value_binstr((gpi_sim_hdl)hdl); + retstr = Py_BuildValue("s", result); + free(result); + + return retstr; +} + +static PyObject *set_signal_val(PyObject *self, PyObject *args) +{ + gpi_sim_hdl hdl; + long value; + + if (!PyArg_ParseTuple(args, "ll", &hdl, &value)) + return NULL; + + gpi_set_signal_value_int(hdl,value); + return Py_BuildValue("s", "OK!"); +} + + +static PyObject *set_signal_val_str(PyObject *self, PyObject *args) +{ + gpi_sim_hdl hdl; + const char *binstr; + + if (!PyArg_ParseTuple(args, "ls", &hdl, &binstr)) + return NULL; + + gpi_set_signal_value_str(hdl,binstr); + return Py_BuildValue("s", "OK!"); +} + + +static PyObject *get_handle_by_name(PyObject *self, PyObject *args) +{ + const char *name; + gpi_sim_hdl hdl; + gpi_sim_hdl result; + + if (!PyArg_ParseTuple(args, "ls", &hdl, &name)) + return NULL; + + result = gpi_get_handle_by_name(name, (gpi_sim_hdl)hdl); + + return Py_BuildValue("l", result); +} + +static PyObject *get_name_string(PyObject *self, PyObject *args) +{ + char *result; + gpi_sim_hdl hdl; + PyObject *retstr; + + if (!PyArg_ParseTuple(args, "l", &hdl)) + return NULL; + + result = gpi_get_signal_name_str((gpi_sim_hdl)hdl); + retstr = Py_BuildValue("s", result); + free(result); + + return retstr; +} + +static PyObject *get_type_string(PyObject *self, PyObject *args) +{ + char *result; + gpi_sim_hdl hdl; + PyObject *retstr; + + if (!PyArg_ParseTuple(args, "l", &hdl)) + return NULL; + + result = gpi_get_signal_type_str((gpi_sim_hdl)hdl); + retstr = Py_BuildValue("s", result); + free(result); + + return retstr; +} + +// Returns a high, low tuple of simulator time +// Note we can never log from this function since the logging mechanism calls this to annotate +// log messages with the current simulation time +static PyObject *get_sim_time(PyObject *self, PyObject *args) +{ + + uint32_t high, low; + + gpi_get_sim_time(&high, &low); + + PyObject *pTuple = PyTuple_New(2); + PyTuple_SetItem(pTuple, 0, PyLong_FromUnsignedLong(high)); // Note: This function “steals†a reference to o. + PyTuple_SetItem(pTuple, 1, PyLong_FromUnsignedLong(low)); // Note: This function “steals†a reference to o. + + return pTuple; +} + +static PyObject *create_clock(PyObject *self, PyObject *args) +{ + gpi_sim_hdl hdl; + int period; + unsigned int mcycles; + + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); + + Py_ssize_t numargs = PyTuple_Size(args); + + if (numargs < 3) { + fprintf(stderr, "Attempt to create a clock with without enough arguments!\n"); + return NULL; + } + + PyObject *pSihHdl = PyTuple_GetItem(args, 0); + hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(pSihHdl); + + PyObject *pPeriod = PyTuple_GetItem(args, 1); + period = (int)PyInt_AsLong(pPeriod); + + PyObject *pCycles = PyTuple_GetItem(args, 2); + mcycles = (unsigned int)PyLong_AsUnsignedLong(pCycles); + + gpi_clock_hdl clk_hdl = gpi_clock_register(hdl, period, mcycles); + PyObject *rv = Py_BuildValue("l", clk_hdl); + + PyGILState_Release(gstate); + return rv; +} + +static PyObject *stop_clock(PyObject *self, PyObject *args) +{ + gpi_clock_hdl clk_hdl; + + if (!PyArg_ParseTuple(args, "l", &clk_hdl)) + return NULL; + + gpi_clock_unregister(clk_hdl); + PyObject *rv = Py_BuildValue("l", NULL); + return rv; +} + + +PyMODINIT_FUNC +initsimulator(void) +{ + (void) Py_InitModule("simulator", SimulatorMethods); +} diff --git a/simulator/simulatormodule.h b/simulator/simulatormodule.h new file mode 100644 index 00000000..93885299 --- /dev/null +++ b/simulator/simulatormodule.h @@ -0,0 +1,96 @@ +/****************************************************************************** +* Copyright (c) 2013 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +#ifndef _SIMULATOR_MODULE_H +#define _SIMULATOR_MODULE_H + +#include +#include "gpi_logging.h" +#include "gpi.h" + +// This file defines the routines available to python + +#define COCOTB_ACTIVE_ID 0xC0C07B // User data flag to indicate callback is active +#define COCOTB_INACTIVE_ID 0xDEADB175 // User data flag set when callback has been deregistered + + +// callback user data +typedef struct t_callback_data { + PyThreadState *_saved_thread_state; // Thread state of the calling thread FIXME is this required? + uint32_t id_value; // COCOTB_ACTIVE_ID or COCOTB_INACTIVE_ID + PyObject *function; // Fuction to call when the callback fires + PyObject *args; // The arguments to call the function with + PyObject *kwargs; // Keyword arguments to call the function with + gpi_cb_hdl cb_hdl; +} s_callback_data, *p_callback_data; + +static PyObject *log_msg(PyObject *self, PyObject *args); + +// Raise an exception on failure +// Return None if for example get bin_string on enum? +static PyObject *get_signal_val(PyObject *self, PyObject *args); +static PyObject *set_signal_val(PyObject *self, PyObject *args); +static PyObject *set_signal_val_str(PyObject *self, PyObject *args); +static PyObject *get_handle_by_name(PyObject *self, PyObject *args); +static PyObject *get_name_string(PyObject *self, PyObject *args); +static PyObject *get_type_string(PyObject *self, PyObject *args); +static PyObject *register_timed_callback(PyObject *self, PyObject *args); +static PyObject *register_value_change_callback(PyObject *self, PyObject *args); +static PyObject *register_readonly_callback(PyObject *self, PyObject *args); +static PyObject *register_nextstep_callback(PyObject *self, PyObject *args); +static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args); +static PyObject *create_clock(PyObject *self, PyObject *args); +static PyObject *stop_clock(PyObject *self, PyObject *args); + + +static PyObject *get_sim_time(PyObject *self, PyObject *args); +// static PyObject *deregister_callback(PyObject *self, PyObject *args); +// static PyObject *register_timed_callback(PyObject *self, PyObject *args, PyObject *keywds); + +static PyMethodDef SimulatorMethods[] = { + {"log_msg", log_msg, METH_VARARGS, "Log a message"}, + {"get_signal_val", get_signal_val, METH_VARARGS, "Get the value of a signal as a binary string"}, + {"set_signal_val", set_signal_val, METH_VARARGS, "Set the value of a signal"}, + {"set_signal_val_str", set_signal_val_str, METH_VARARGS, "Set the value of a signal using a binary string"}, + {"get_handle_by_name", get_handle_by_name, METH_VARARGS, "Get handle of a named object"}, + {"get_name_string", get_name_string, METH_VARARGS, "Get the name of an object"}, + {"get_type_string", get_type_string, METH_VARARGS, "Get the type of an object"}, + {"register_timed_callback", register_timed_callback, METH_VARARGS, "Register a timed callback"}, + {"register_value_change_callback", register_value_change_callback, METH_VARARGS, "Register a signal change callback"}, + {"register_readonly_callback", register_readonly_callback, METH_VARARGS, "Register a callback for readonly section"}, + {"register_nextstep_callback", register_nextstep_callback, METH_VARARGS, "Register a cllback for the nextsimtime callback"}, + {"register_rwsynch_callback", register_rwsynch_callback, METH_VARARGS, "Register a callback for the readwrite section"}, + {"create_clock", create_clock, METH_VARARGS, "Register a clock object"}, + {"stop_clock", stop_clock, METH_VARARGS, "Terminate a clock"}, + + // FIXME METH_NOARGS => initialization from incompatible pointer type + {"get_sim_time", get_sim_time, METH_VARARGS, "Get the current simulation time as a float"}, + // {"deregister_callback", deregister_callback, METH_VARARGS, "Register"}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +#endif diff --git a/vpi_shim/Makefile b/vpi_shim/Makefile new file mode 100644 index 00000000..8ef1e3ef --- /dev/null +++ b/vpi_shim/Makefile @@ -0,0 +1,46 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +include $(SIM_ROOT)/makefiles/Makefile.inc +OBJ_DIR := obj +INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ -I../gpi/ $(VPI_INCLUDE_PATH) +GCC_ARGS := -Werror -g +LIBS := -lgpilog +LD_PATH := -L$(LIB_DIR) + +all: $(LIB_DIR)/gpi_lib + +$(OBJ_DIR): + mkdir -p $(OBJ_DIR) + +$(LIB_DIR)/gpi_lib: $(OBJ_DIR) + gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $(OBJ_DIR)/gpi_vpi.o gpi_vpi.c + gcc $(GCC_ARGS) -shared -export-dynamic -o $(LIB_DIR)/gpi_lib.so $(OBJ_DIR)/gpi_vpi.o $(LIBS) $(LD_PATH) + ln -sf gpi_lib.so $(LIB_DIR)/libgpi.so + +clean : + rm -rf $(OBJ_DIR) diff --git a/vpi_shim/gpi_vpi.c b/vpi_shim/gpi_vpi.c new file mode 100644 index 00000000..0bb2fc6d --- /dev/null +++ b/vpi_shim/gpi_vpi.c @@ -0,0 +1,560 @@ +/****************************************************************************** +* Copyright (c) 2013 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + + +#include +#include +#include + +#include "gpi.h" +#include "gpi_logging.h" +#include + +// Handle related functions +gpi_sim_hdl gpi_get_root_handle() +{ + FENTER + vpiHandle root; + vpiHandle iterator; + + // vpi_iterate with a ref of NULL returns the top level module + iterator = vpi_iterate(vpiModule, NULL); + root = vpi_scan(iterator); + + // Need to free the iterator if it didn't return NULL + if (root != NULL && !vpi_free_object(iterator)) { + LOG_WARN("VPI: Attempting to free root iterator failed!"); + } + FEXIT + return (gpi_sim_hdl)root; +} + +gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) +{ + FENTER + gpi_sim_hdl rv; + int len; + char *buff; + if (name) + len = strlen(name) + 1; + + buff = (char *)malloc(len); + if (buff== NULL) { + LOG_CRITICAL("VPI: Attempting allocate string buffer failed!"); + return; + } + + strncpy(buff, name, len); + rv = (gpi_sim_hdl)vpi_handle_by_name(buff, (vpiHandle)parent); + free(buff); + FEXIT + return rv; + +} + + +void gpi_sim_end() +{ + vpi_control(vpiFinish); +} + +// double gpi_get_sim_time() +void gpi_get_sim_time(uint32_t *high, uint32_t *low) +{ +// FENTERD + s_vpi_time vpi_time_s; + vpi_time_s.type = vpiSimTime;//vpiScaledRealTime; //vpiSimTime; + vpi_get_time(NULL, &vpi_time_s); +// FEXIT +// return vpi_time_s.real; + *high = vpi_time_s.high; + *low = vpi_time_s.low; +} + +// Value related functions +void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) +{ + FENTER + s_vpi_value value_s; + p_vpi_value value_p = &value_s; + + value_p->value.integer = value; + value_p->format = vpiIntVal; + +// * vpiNoDelay -- Set the value immediately. The p_vpi_time parameter +// * may be NULL, in this case. This is like a blocking assignment +// * in behavioral code. + vpi_put_value((vpiHandle)gpi_hdl, value_p, NULL, vpiNoDelay); + + FEXIT +} + +void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) +{ + FENTER + s_vpi_value value_s; + p_vpi_value value_p = &value_s; + + int len; + char *buff; + if (str) + len = strlen(str) + 1; + + buff = (char *)malloc(len); + if (buff== NULL) { + LOG_CRITICAL("VPI: Attempting allocate string buffer failed!"); + return; + } + + strncpy(buff, str, len); + + value_p->value.str = buff; + value_p->format = vpiBinStrVal; + +// * vpiNoDelay -- Set the value immediately. The p_vpi_time parameter +// * may be NULL, in this case. This is like a blocking assignment +// * in behavioral code. + vpi_put_value((vpiHandle)gpi_hdl, value_p, NULL, vpiNoDelay); + + free(buff); + FEXIT +} + + + + + + +static char *gpi_copy_name(const char *name) +{ + int len; + char *result; + + if (name) + len = strlen(name) + 1; + else { + LOG_CRITICAL("NULL came back from VPI\n"); + len = 20; + } + + result = (char *)malloc(len); + if (result == NULL) { + LOG_CRITICAL("VPI: Attempting allocate string buffer failed!"); + return NULL; + } + + snprintf(result, len, "%s\0", name ? name : "UNKNOWN"); + + return result; +} + + +char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) +{ + FENTER + s_vpi_value value_s = {vpiBinStrVal}; + p_vpi_value value_p = &value_s; + + vpi_get_value((vpiHandle)gpi_hdl, value_p); + + char *result = gpi_copy_name(value_p->value.str); + FEXIT + return result; +} + +char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl) +{ + FENTER + const char *name = vpi_get_str(vpiFullName, (vpiHandle)gpi_hdl); + char *result = gpi_copy_name(name); + FEXIT + return result; +} + +char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl) +{ + FENTER + const char *name = vpi_get_str(vpiType, (vpiHandle)gpi_hdl); + char *result = gpi_copy_name(name); + FEXIT + return result; +} + + +// Callback related functions + +// callback user data used for VPI callbacks +// (mostly just a thin wrapper around the gpi_callback) +typedef struct t_vpi_cb_user_data { + void *gpi_cb_data; + int (*gpi_function)(void *); + int (*gpi_cleanup)(struct t_vpi_cb_user_data *); + vpiHandle cb_hdl; +} s_vpi_cb_user_data, *p_vpi_cb_user_data; + + +PLI_INT32 handle_vpi_callback(p_cb_data cb_data) +{ + FENTER + int rv = 0; + + p_vpi_cb_user_data user_data; + user_data = (p_vpi_cb_user_data)cb_data->user_data; + + rv = user_data->gpi_function(user_data->gpi_cb_data); + + // Call destuctor + if (user_data->gpi_cleanup) + user_data->gpi_cleanup(user_data); + + FEXIT + return rv; +}; + +// remove a callback without freeing the user data +// (called by callback to clean up) +// vpi_get_cb_info could be used to free user data if +// the callback hasn't fired... +int gpi_deregister_callback(gpi_cb_hdl gpi_hdl) +{ + FENTER + // This destroys them memory allocated for the handle + PLI_INT32 rc = vpi_remove_cb((vpiHandle)gpi_hdl); + FEXIT + return rc; +} + +// Call when the handle relates to a one time callback +// No need to call vpi_deregister_cb as the sim will +// do this but do need to destroy the handle +static int gpi_free_one_time(p_vpi_cb_user_data user_data) +{ + FENTER + PLI_INT32 rc; + vpiHandle cb_hdl = user_data->cb_hdl; + if (!cb_hdl) { + LOG_ERR("VPI: %s passed a NULL pointer\n", __func__); + exit(1); + } + + rc = vpi_free_object(cb_hdl); + free(user_data); + FEXIT + return rc; +} + +// Call when the handle relates to recurring callback +// Unregister must be called when not needed and this +// will clean all memory allocated by the sim +static int gpi_free_recurring(p_vpi_cb_user_data user_data) +{ + FENTER + PLI_INT32 rc; + vpiHandle cb_hdl = user_data->cb_hdl; + if (!cb_hdl) { + LOG_ERR("VPI: %s passed a NULL pointer\n", __func__); + exit(1); + } + + rc = vpi_remove_cb(cb_hdl); + free(user_data); + FEXIT + return rc; +} + +gpi_cb_hdl gpi_register_value_change_callback(int (*gpi_function)(void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl) +{ + FENTER + s_cb_data cb_data_s; + s_vpi_time vpi_time_s; + s_vpi_value vpi_value_s; + p_vpi_cb_user_data user_data; + + // Freed when callback fires or the callback is deregistered + user_data = (p_vpi_cb_user_data)malloc(sizeof(s_vpi_cb_user_data)); + if (user_data == NULL) { + LOG_WARN("VPI: Attempting allocate user_data for %s failed!", __func__); + } + + user_data->gpi_cb_data = gpi_cb_data; + user_data->gpi_function = gpi_function; + user_data->gpi_cleanup = gpi_free_recurring; + + vpi_time_s.type = vpiSuppressTime; + vpi_value_s.format = vpiIntVal; + + cb_data_s.reason = cbValueChange; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = (vpiHandle)gpi_hdl; + cb_data_s.time = &vpi_time_s; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)user_data; + + user_data->cb_hdl = vpi_register_cb(&cb_data_s); + FEXIT + + return (gpi_cb_hdl)user_data->cb_hdl; +} + + +gpi_cb_hdl gpi_register_readonly_callback(int (*gpi_function)(void *), void *gpi_cb_data) +{ + FENTER + s_cb_data cb_data_s; + s_vpi_time vpi_time_s; + p_vpi_cb_user_data user_data; + + // Freed when callback fires or the callback is deregistered + user_data = (p_vpi_cb_user_data)malloc(sizeof(s_vpi_cb_user_data)); + if (user_data == NULL) { + LOG_WARN("VPI: Attempting allocate user_data for %s failed!", __func__); + } + + user_data->gpi_cb_data = gpi_cb_data; + user_data->gpi_function = gpi_function; + user_data->gpi_cleanup = gpi_free_one_time; + + vpi_time_s.type = vpiSimTime; + vpi_time_s.high = 0; + vpi_time_s.low = 0; + + cb_data_s.reason = cbReadOnlySynch; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &vpi_time_s; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)user_data; + + user_data->cb_hdl = vpi_register_cb(&cb_data_s); + + FEXIT + return (gpi_cb_hdl)user_data->cb_hdl; +} + +gpi_cb_hdl gpi_register_readwrite_callback(int (*gpi_function)(void *), void *gpi_cb_data) +{ + FENTER + s_cb_data cb_data_s; + s_vpi_time vpi_time_s; + p_vpi_cb_user_data user_data; + + // Freed when callback fires or the callback is deregistered + user_data = (p_vpi_cb_user_data)malloc(sizeof(s_vpi_cb_user_data)); + if (user_data == NULL) { + LOG_WARN("VPI: Attempting allocate user_data for %s failed!", __func__); + } + + user_data->gpi_cb_data = gpi_cb_data; + user_data->gpi_function = gpi_function; + user_data->gpi_cleanup = gpi_free_one_time; + + vpi_time_s.type = vpiSimTime; + vpi_time_s.high = 0; + vpi_time_s.low = 0; + + cb_data_s.reason = cbReadWriteSynch; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &vpi_time_s; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)user_data; + + user_data->cb_hdl = vpi_register_cb(&cb_data_s); + + FEXIT + return (gpi_cb_hdl)user_data->cb_hdl; +} + +gpi_cb_hdl gpi_register_nexttime_callback(int (*gpi_function)(void *), void *gpi_cb_data) +{ + FENTER + s_cb_data cb_data_s; + s_vpi_time vpi_time_s; + p_vpi_cb_user_data user_data; + + // Freed when callback fires or the callback is deregistered + user_data = (p_vpi_cb_user_data)malloc(sizeof(s_vpi_cb_user_data)); + if (user_data == NULL) { + LOG_WARN("VPI: Attempting allocate user_data for %s failed!", __func__); + } + + user_data->gpi_cb_data = gpi_cb_data; + user_data->gpi_function = gpi_function; + user_data->gpi_cleanup = gpi_free_one_time; + + vpi_time_s.type = vpiSimTime; + vpi_time_s.high = 0; + vpi_time_s.low = 0; + + cb_data_s.reason = cbNextSimTime; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &vpi_time_s; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)user_data; + + user_data->cb_hdl = vpi_register_cb(&cb_data_s); + + FEXIT + return (gpi_cb_hdl)user_data->cb_hdl; +} + +gpi_cb_hdl gpi_register_timed_callback(int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps) +{ + FENTER + s_cb_data cb_data_s; + s_vpi_time vpi_time_s; + p_vpi_cb_user_data user_data; + + // Freed when callback fires or the callback is deregistered + user_data = (p_vpi_cb_user_data)malloc(sizeof(s_vpi_cb_user_data)); + if (user_data == NULL) { + LOG_WARN("VPI: Attempting allocate user_data for %s failed!", __func__); + } + + user_data->gpi_cb_data = gpi_cb_data; + user_data->gpi_function = gpi_function; + user_data->gpi_cleanup = gpi_free_one_time; + + vpi_time_s.type = vpiSimTime; + vpi_time_s.high = (PLI_UINT32)(time_ps>>32); + vpi_time_s.low = (PLI_UINT32)(time_ps); + + cb_data_s.reason = cbAfterDelay; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &vpi_time_s; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)user_data; + + user_data->cb_hdl = vpi_register_cb(&cb_data_s); + FEXIT + + return (gpi_cb_hdl)user_data->cb_hdl; +} + +gpi_cb_hdl gpi_register_sim_start_callback(int (*gpi_function)(void *), void *gpi_cb_data) +{ + FENTER + + p_vpi_cb_user_data user_data; + s_cb_data cb_data_s; + + // Freed when callback fires or the callback is deregistered + user_data = (p_vpi_cb_user_data)malloc(sizeof(s_vpi_cb_user_data)); + if (user_data == NULL) { + LOG_WARN("VPI: Attempting allocate user_data for %s failed!", __func__); + } + + user_data->gpi_cb_data = gpi_cb_data; + user_data->gpi_function = gpi_function; + user_data->gpi_cleanup = gpi_free_one_time; + + cb_data_s.reason = cbStartOfSimulation; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = NULL; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)user_data; + + user_data->cb_hdl = vpi_register_cb(&cb_data_s); + FEXIT + return (gpi_cb_hdl)user_data->cb_hdl; + +} + +gpi_cb_hdl gpi_register_sim_end_callback(int (*gpi_function)(void *), void *gpi_cb_data) +{ + FENTER + + p_vpi_cb_user_data user_data; + s_cb_data cb_data_s; + + // Freed when callback fires or the callback is deregistered + user_data = (p_vpi_cb_user_data)malloc(sizeof(s_vpi_cb_user_data)); + if (user_data == NULL) { + LOG_WARN("VPI: Attempting allocate user_data for %s failed!", __func__); + } + + user_data->gpi_cb_data = gpi_cb_data; + user_data->gpi_function = gpi_function; + user_data->gpi_cleanup = gpi_free_one_time; + + cb_data_s.reason = cbEndOfSimulation; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = NULL; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)user_data; + + user_data->cb_hdl = vpi_register_cb(&cb_data_s); + FEXIT + return (gpi_cb_hdl)user_data->cb_hdl; + +} + +int gpi_clock_handler(void *clock) +{ + gpi_clock_hdl hdl = (gpi_clock_hdl)clock; + + if (hdl->exit || (hdl->max_cycles == hdl->curr_cycle)) + return; + + hdl->value = !hdl->value; + gpi_set_signal_value_int(hdl->sim_hdl, hdl->value); + gpi_cb_hdl edge = gpi_register_timed_callback(gpi_clock_handler, hdl, hdl->period); + hdl->cb_hdl = edge; + hdl->curr_cycle++; +} + +gpi_clock_hdl gpi_clock_register(gpi_sim_hdl sim_hdl, int period, unsigned int cycles) +{ + FENTER + + gpi_clock_hdl hdl = malloc(sizeof(gpi_clock_t)); + if (!hdl) + LOG_WARN("Unable to allocate memory"); + + hdl->period = period; + hdl->value = 0; + hdl->sim_hdl = sim_hdl; + hdl->exit = false; + hdl->max_cycles = cycles; + hdl->curr_cycle = 0; + + gpi_set_signal_value_int(hdl->sim_hdl, hdl->value); + gpi_cb_hdl edge = gpi_register_timed_callback(gpi_clock_handler, hdl, hdl->period); + hdl->cb_hdl = edge; + + FEXIT + return hdl; +} + +void gpi_clock_unregister(gpi_clock_hdl clock) +{ + clock->exit = true; +} From 63dbdef0b0eb20ea3bd6a6ebcae60cce22fb5022 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 12 Jun 2013 21:25:03 +0100 Subject: [PATCH 0003/2656] Add ability to call sfsim simulator --- bin/cocotbenv.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/bin/cocotbenv.py b/bin/cocotbenv.py index 92e89eab..1995970c 100755 --- a/bin/cocotbenv.py +++ b/bin/cocotbenv.py @@ -56,6 +56,21 @@ def execute(self, py_path, lib_path, module, function, finput): print(cmd) call(cmd, shell=True) +class SimSfsim(SimType): + def __init__(self, name=None): + SimType.__init__(self, "SolarFlare Model") + + def execute(self, py_path, lib_path, module, function, finput): + SimType.execute(self) + cmd = 'PYTHONPATH=' + py_path + cmd = cmd + ' LD_LIBRARY_PATH=' + lib_path + cmd = cmd + ' MODULE=' + module + cmd = cmd + ' FUNCTION=' + function + cmd = cmd + ' ' + finput + cmd = cmd + ' -l ' + lib_path + '/libcocotb.so' + print(cmd) + call(cmd, shell=True) + def main(): """ Start the desired simulator with cocotb embedded within. From 630efa95a40570c544216c16975d23ffd78a5aaf Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 13 Jun 2013 10:16:44 +0100 Subject: [PATCH 0004/2656] Remove deprecated TODOs from README.md --- README.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/README.md b/README.md index b29ba747..6fab7b01 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,6 @@ **cocotb** is a coroutine based cosimulation library for writing VHDL and Verilog testbenches in Python. -TODO -==== - - - [ ] Block align log messages that span multiple lines - - [ ] Common functions for dumping a diff, nicely coloured (see scapy.utils.hexdiff) - - Rationale ========= VHDL and Verilog are both unsuitable for writing complex testbenches. eRM, SystemVerilog and the various SV based methodologies have emerged to address this deficiency. These verification methodologies are large and cumbersome, requiring specialist knowledge, significant time investment and expensive toolchains to achieve satisfactory verification. The overhead of setting up testbenches is onerous so often designers write small throwaway testbenches at a block level and defer the majority of verification to a large system level testbench. Cocotb intends to bridge this gap in order to make block-level testing useful, reusable, fast, accessible to a much wider skill-set. The net effect is that bugs will be discovered earlier in the design cycle which equates to significant time and cost saving. From c39932bc31b5a2223bbaf4d5e038708a00369872 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 13 Jun 2013 12:30:22 +0100 Subject: [PATCH 0005/2656] Correct syntax error --- bin/cocotbenv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/cocotbenv.py b/bin/cocotbenv.py index 1995970c..4feb597f 100755 --- a/bin/cocotbenv.py +++ b/bin/cocotbenv.py @@ -58,7 +58,7 @@ def execute(self, py_path, lib_path, module, function, finput): class SimSfsim(SimType): def __init__(self, name=None): - SimType.__init__(self, "SolarFlare Model") + SimType.__init__(self, "SolarFlare Model") def execute(self, py_path, lib_path, module, function, finput): SimType.execute(self) From 819c5bf310203eaddc4733ae4ee0eab65696a557 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 13 Jun 2013 14:19:54 +0100 Subject: [PATCH 0006/2656] Updated Makefiles to avoid needing a SIM_ROOT environment variable --- examples/demo/Makefile | 4 +++- makefiles/Makefile.aldec | 2 -- makefiles/Makefile.icarus | 2 -- makefiles/Makefile.inc | 7 ++++--- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/examples/demo/Makefile b/examples/demo/Makefile index 5369fd26..9e939d53 100644 --- a/examples/demo/Makefile +++ b/examples/demo/Makefile @@ -33,4 +33,6 @@ VERILOG_SOURCES = counter.v MODULE=demo FUNCTION=example_test -include $(SIM_ROOT)/makefiles/Makefile.sim +include ../../makefiles/Makefile.inc +include ../../makefiles/Makefile.sim + diff --git a/makefiles/Makefile.aldec b/makefiles/Makefile.aldec index 1980169c..497f0588 100644 --- a/makefiles/Makefile.aldec +++ b/makefiles/Makefile.aldec @@ -26,8 +26,6 @@ ############################################################################### -include $(SIM_ROOT)/scripts/Makefile.inc - COCOTB = $(SIM_ROOT)/sim_core/cocotb VPI_FILE = $(SIM_ROOT)/build/cocotb.vpi OBJ_DIR := obj diff --git a/makefiles/Makefile.icarus b/makefiles/Makefile.icarus index 448cccf4..1f63976d 100644 --- a/makefiles/Makefile.icarus +++ b/makefiles/Makefile.icarus @@ -25,8 +25,6 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -include $(SIM_ROOT)/makefiles/Makefile.inc - OBJ_DIR := obj VPI_LIB := vpi VPI_LD_PATH := $(ICARUS_BASE)/lib diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 73df1d4c..a2777f72 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -25,15 +25,15 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################## +# Common makefile included by everything + ifeq ($(SIM_ROOT),) -export SIM_ROOT:=$(pwd) +export SIM_ROOT:=$(abspath $(dir $(lastword $(MAKEFILE_LIST)))/..) endif BUILD_DIR=$(SIM_ROOT)/build ARCH=$(shell uname -m) -VPI_INCLUDE_PATH := -I/home/smh/Downloads/ - include $(SIM_ROOT)/makefiles/Makefile.paths export ARCH @@ -41,3 +41,4 @@ export BUILD_DIR export LIB_DIR=$(BUILD_DIR)/libs/$(ARCH) include $(SIM_ROOT)/makefiles/Makefile.pylib + From 94188e253aea1a37aa669d22d5dabaed1dea7b1a Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 13 Jun 2013 15:30:56 +0100 Subject: [PATCH 0007/2656] Fix undefined symbols seem on dlopen --- gpi/gpi_logging.c | 2 +- vpi_shim/gpi_vpi.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gpi/gpi_logging.c b/gpi/gpi_logging.c index c209aa6d..25de1457 100644 --- a/gpi/gpi_logging.c +++ b/gpi/gpi_logging.c @@ -101,7 +101,7 @@ void gpi_log(const char *name, long level, const char *pathname, const char *fun PyObject *pShouldFilter = PyObject_CallObject(pLogFilter, pLogArgs); if (pShouldFilter == Py_True) { PyObject *pLogResult = PyObject_CallObject(pLogHandler, pLogArgs); - Py_DECRE(pLogResult); + Py_DECREF(pLogResult); } Py_DECREF(pShouldFilter); diff --git a/vpi_shim/gpi_vpi.c b/vpi_shim/gpi_vpi.c index 0bb2fc6d..fb25fda0 100644 --- a/vpi_shim/gpi_vpi.c +++ b/vpi_shim/gpi_vpi.c @@ -257,7 +257,7 @@ static int gpi_free_one_time(p_vpi_cb_user_data user_data) PLI_INT32 rc; vpiHandle cb_hdl = user_data->cb_hdl; if (!cb_hdl) { - LOG_ERR("VPI: %s passed a NULL pointer\n", __func__); + LOG_ERROR("VPI: %s passed a NULL pointer\n", __func__); exit(1); } @@ -276,7 +276,7 @@ static int gpi_free_recurring(p_vpi_cb_user_data user_data) PLI_INT32 rc; vpiHandle cb_hdl = user_data->cb_hdl; if (!cb_hdl) { - LOG_ERR("VPI: %s passed a NULL pointer\n", __func__); + LOG_ERROR("VPI: %s passed a NULL pointer\n", __func__); exit(1); } From 856f7da3a17b79baf5c2b1864206636a5167c049 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 14 Jun 2013 09:15:27 +0100 Subject: [PATCH 0008/2656] Fix dependency for vpi_shim so rebuild does not always occur --- vpi_shim/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vpi_shim/Makefile b/vpi_shim/Makefile index 8ef1e3ef..b0f4c147 100644 --- a/vpi_shim/Makefile +++ b/vpi_shim/Makefile @@ -32,12 +32,12 @@ GCC_ARGS := -Werror -g LIBS := -lgpilog LD_PATH := -L$(LIB_DIR) -all: $(LIB_DIR)/gpi_lib +all: $(LIB_DIR)/gpi_lib.so $(OBJ_DIR): mkdir -p $(OBJ_DIR) -$(LIB_DIR)/gpi_lib: $(OBJ_DIR) +$(LIB_DIR)/gpi_lib.so: $(OBJ_DIR) gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $(OBJ_DIR)/gpi_vpi.o gpi_vpi.c gcc $(GCC_ARGS) -shared -export-dynamic -o $(LIB_DIR)/gpi_lib.so $(OBJ_DIR)/gpi_vpi.o $(LIBS) $(LD_PATH) ln -sf gpi_lib.so $(LIB_DIR)/libgpi.so From 902972c70c3d0dd3bf62c791a66b3f010234c8b8 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Fri, 14 Jun 2013 16:13:28 +0100 Subject: [PATCH 0009/2656] Issue #2 - started to add support for Synopsys VCS --- embed/gpi_embed.c | 12 +++++++++++ makefiles/Makefile.sim | 3 +++ makefiles/Makefile.vcs | 49 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 makefiles/Makefile.vcs diff --git a/embed/gpi_embed.c b/embed/gpi_embed.c index 79eff5d0..706ac5b1 100644 --- a/embed/gpi_embed.c +++ b/embed/gpi_embed.c @@ -199,9 +199,21 @@ void register_final_callback() } +// FIXME this is VPI specific, should be in gpi_vpi.c void (*vlog_startup_routines[])() = { init_python, register_initial_callback, register_final_callback, 0 }; + + +// For non-VPI compliant applications that cannot find vlog_startup_routines symbol +void vlog_startup_routines_bootstrap() { + unsigned int i; + for (i = 0; vlog_startup_routines[i]; i++) { + vlog_startup_routines[i](); + } +} + + diff --git a/makefiles/Makefile.sim b/makefiles/Makefile.sim index 3cb69ccd..87dd8ec7 100644 --- a/makefiles/Makefile.sim +++ b/makefiles/Makefile.sim @@ -25,6 +25,9 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +# This is a simple wrapper Makefile to pull in the appropriate Makefile for +# the desired simulator + SIM?=ICARUS ifeq ($(SIM),ICARUS) diff --git a/makefiles/Makefile.vcs b/makefiles/Makefile.vcs new file mode 100644 index 00000000..edc25dea --- /dev/null +++ b/makefiles/Makefile.vcs @@ -0,0 +1,49 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +OBJ_DIR := obj +VPI_LIB := vpi + +all: $(OBJ_DIR) sim + +$(OBJ_DIR): + mkdir -p $(OBJ_DIR) + + +# TODO: +# investigate +vpi+1 option which reduces memory requirements + +pli.tab : + echo "acc+=read_write,callback,force:*" > $@ + +.PHONY: sim +sim: $(OBJ_DIR) $(VERILOG_SOURCES) pli.tab + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ + vcs -R -debug_all +acc+1 -diag vpi +vpi -P pli.tab -load cocotb.vpi $(VERILOG_SOURCES) +clean: + rm -rf $(OBJ_DIR) + From e706b1ea24db64d487ee0197fc4a24a639194be3 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Fri, 14 Jun 2013 16:16:50 +0100 Subject: [PATCH 0010/2656] Issue #2 - VCS provides signal value when registering a value change cb --- vpi_shim/gpi_vpi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vpi_shim/gpi_vpi.c b/vpi_shim/gpi_vpi.c index 0bb2fc6d..6df2471b 100644 --- a/vpi_shim/gpi_vpi.c +++ b/vpi_shim/gpi_vpi.c @@ -214,6 +214,7 @@ typedef struct t_vpi_cb_user_data { int (*gpi_function)(void *); int (*gpi_cleanup)(struct t_vpi_cb_user_data *); vpiHandle cb_hdl; + s_vpi_value cb_value; } s_vpi_cb_user_data, *p_vpi_cb_user_data; @@ -311,7 +312,7 @@ gpi_cb_hdl gpi_register_value_change_callback(int (*gpi_function)(void *), void cb_data_s.cb_rtn = handle_vpi_callback; cb_data_s.obj = (vpiHandle)gpi_hdl; cb_data_s.time = &vpi_time_s; - cb_data_s.value = NULL; + cb_data_s.value = &user_data->cb_value; cb_data_s.user_data = (char *)user_data; user_data->cb_hdl = vpi_register_cb(&cb_data_s); From d4c5c9d20ec2d1ad4de341d5416ff99af0f9613f Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 14 Jun 2013 16:39:53 +0100 Subject: [PATCH 0011/2656] Add debug option to parse to the sim class --- bin/cocotbenv.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/bin/cocotbenv.py b/bin/cocotbenv.py index 4feb597f..7c16543c 100755 --- a/bin/cocotbenv.py +++ b/bin/cocotbenv.py @@ -33,15 +33,16 @@ from subprocess import call class SimType(): - def __init__(self, name=None): + def __init__(self, name=None, sdebug=False): self._name = name; + self._sdebug = sdebug def execute(self): print("Running cocotb against %s simulator" % self._name) class SimIcarus(SimType): - def __init__(self, name=None): - SimType.__init__(self, "Icarus") + def __init__(self, name=None, sdebug=False): + SimType.__init__(self, "Icarus", sdebug) self._base_cmd = ' vvp -m cocotb' def execute(self, py_path, lib_path, module, function, finput): @@ -50,6 +51,8 @@ def execute(self, py_path, lib_path, module, function, finput): cmd = cmd + ' LD_LIBRARY_PATH=' + lib_path cmd = cmd + ' MODULE=' + module cmd = cmd + ' FUNCTION=' + function + if self._sdebug is True: + cmd = cmd + ' gdb --args' cmd = cmd + self._base_cmd cmd = cmd + ' -M ' + lib_path cmd = cmd + ' ' + finput @@ -57,8 +60,8 @@ def execute(self, py_path, lib_path, module, function, finput): call(cmd, shell=True) class SimSfsim(SimType): - def __init__(self, name=None): - SimType.__init__(self, "SolarFlare Model") + def __init__(self, name=None, sdebug=False): + SimType.__init__(self, "SolarFlare Model", sdebug) def execute(self, py_path, lib_path, module, function, finput): SimType.execute(self) @@ -66,6 +69,8 @@ def execute(self, py_path, lib_path, module, function, finput): cmd = cmd + ' LD_LIBRARY_PATH=' + lib_path cmd = cmd + ' MODULE=' + module cmd = cmd + ' FUNCTION=' + function + if self._sdebug is True: + cmd = cmd + ' gdb --args' cmd = cmd + ' ' + finput cmd = cmd + ' -l ' + lib_path + '/libcocotb.so' print(cmd) @@ -81,6 +86,7 @@ def main(): -m, --module Module to load with test case -s, --simtype The Simulator to use for execution -i, --input Input file, type depends on simtype + -d, --debug Start the environment in gdb """ from optparse import OptionParser @@ -93,6 +99,8 @@ def main(): help="The Simulator to use for execution") parser.add_option("-i", "--input", dest="input_str", help="Input file, type depends on simtype") + parser.add_option("-d", action="store_true", dest="debug", + help="Debug option") (options, args) = parser.parse_args() @@ -133,7 +141,7 @@ def main(): # Add the module and function as command line options that are passed # as env vars to the underlying code - sim_world = ctype() + sim_world = ctype(sdebug=options.debug) sim_world.execute(py_path, lib_path, options.module_str, From 7670b6f68a53235ddbf6b437e2a63a59732b6457 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Fri, 14 Jun 2013 17:16:24 +0100 Subject: [PATCH 0012/2656] Tidy-up of repository top level * C code moved to "lib" subdirectory * Removed deprecated "scripts" directory --- Makefile | 7 ++-- {embed => lib/embed}/Makefile | 0 {embed => lib/embed}/gpi_embed.c | 0 {gpi => lib/gpi}/Makefile | 0 {gpi => lib/gpi}/gpi.h | 0 {gpi => lib/gpi}/gpi_logging.c | 0 {gpi => lib/gpi}/gpi_logging.h | 0 {simulator => lib/simulator}/Makefile | 0 .../simulator}/simulatormodule.c | 0 .../simulator}/simulatormodule.h | 0 {vpi_shim => lib/vpi_shim}/Makefile | 0 {vpi_shim => lib/vpi_shim}/gpi_vpi.c | 0 scripts/set_env.sh | 40 ------------------- 13 files changed, 4 insertions(+), 43 deletions(-) rename {embed => lib/embed}/Makefile (100%) rename {embed => lib/embed}/gpi_embed.c (100%) rename {gpi => lib/gpi}/Makefile (100%) rename {gpi => lib/gpi}/gpi.h (100%) rename {gpi => lib/gpi}/gpi_logging.c (100%) rename {gpi => lib/gpi}/gpi_logging.h (100%) rename {simulator => lib/simulator}/Makefile (100%) rename {simulator => lib/simulator}/simulatormodule.c (100%) rename {simulator => lib/simulator}/simulatormodule.h (100%) rename {vpi_shim => lib/vpi_shim}/Makefile (100%) rename {vpi_shim => lib/vpi_shim}/gpi_vpi.c (100%) delete mode 100755 scripts/set_env.sh diff --git a/Makefile b/Makefile index e461e6c7..38fd50f6 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ export BUILD_DIR=$(shell pwd)/build INSTALL_DIR:=/tmp/test-install -LIBS:= simulator embed vpi_shim gpi +LIBS:= lib/simulator lib/embed lib/vpi_shim lib/gpi .PHONY: $(LIBS) @@ -40,8 +40,8 @@ all: $(LIBS) $(LIBS): dirs $(MAKE) -C $@ -vpi_shim: gpi -simulator: vpi_shim +lib/vpi_shim: lib/gpi +lib/simulator: lib/vpi_shim dirs: @mkdir -p $(LIB_DIR) @@ -61,3 +61,4 @@ lib_install: libs cp -R $(LIB_DIR)/* $(INSTALL_DIR)/lib install: all lib_install pycode + diff --git a/embed/Makefile b/lib/embed/Makefile similarity index 100% rename from embed/Makefile rename to lib/embed/Makefile diff --git a/embed/gpi_embed.c b/lib/embed/gpi_embed.c similarity index 100% rename from embed/gpi_embed.c rename to lib/embed/gpi_embed.c diff --git a/gpi/Makefile b/lib/gpi/Makefile similarity index 100% rename from gpi/Makefile rename to lib/gpi/Makefile diff --git a/gpi/gpi.h b/lib/gpi/gpi.h similarity index 100% rename from gpi/gpi.h rename to lib/gpi/gpi.h diff --git a/gpi/gpi_logging.c b/lib/gpi/gpi_logging.c similarity index 100% rename from gpi/gpi_logging.c rename to lib/gpi/gpi_logging.c diff --git a/gpi/gpi_logging.h b/lib/gpi/gpi_logging.h similarity index 100% rename from gpi/gpi_logging.h rename to lib/gpi/gpi_logging.h diff --git a/simulator/Makefile b/lib/simulator/Makefile similarity index 100% rename from simulator/Makefile rename to lib/simulator/Makefile diff --git a/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c similarity index 100% rename from simulator/simulatormodule.c rename to lib/simulator/simulatormodule.c diff --git a/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h similarity index 100% rename from simulator/simulatormodule.h rename to lib/simulator/simulatormodule.h diff --git a/vpi_shim/Makefile b/lib/vpi_shim/Makefile similarity index 100% rename from vpi_shim/Makefile rename to lib/vpi_shim/Makefile diff --git a/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c similarity index 100% rename from vpi_shim/gpi_vpi.c rename to lib/vpi_shim/gpi_vpi.c diff --git a/scripts/set_env.sh b/scripts/set_env.sh deleted file mode 100755 index 780bcf65..00000000 --- a/scripts/set_env.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/sh - -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - - -# Source this to set up the sauce - -export SIM_ROOT="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd )" -export BUILD_DIR=$SIM_ROOT/build - - -export PATH=$SIM_ROOT/bin:$PATH -export PYTHONPATH=$BUILD_DIR:$SIM_ROOT/sim_core:$SIM_ROOT:$PYTHONPATH -export LD_LIBRARY_PATH=$BUILD_DIR:$LD_LIBRARY_PATH - From 0f264ef09794ef22423d3797111ccfd2464db53c Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Mon, 17 Jun 2013 09:29:03 +0100 Subject: [PATCH 0013/2656] Removed accidental file --- examples/demo/library.cfg | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 examples/demo/library.cfg diff --git a/examples/demo/library.cfg b/examples/demo/library.cfg deleted file mode 100644 index 97565eea..00000000 --- a/examples/demo/library.cfg +++ /dev/null @@ -1,2 +0,0 @@ -$INCLUDE = "$VSIMSALIBRARYCFG" -work = "./work/work.lib" 1367432733288 From b70daf0f01f288962defda43a69e8da067f17eba Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Mon, 17 Jun 2013 09:30:11 +0100 Subject: [PATCH 0014/2656] Tweaks to VCS Makefile, fixes issue #2 --- makefiles/Makefile.sim | 2 ++ makefiles/Makefile.vcs | 11 +++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/makefiles/Makefile.sim b/makefiles/Makefile.sim index 87dd8ec7..4c247e50 100644 --- a/makefiles/Makefile.sim +++ b/makefiles/Makefile.sim @@ -38,6 +38,8 @@ include $(SIM_ROOT)/makefiles/Makefile.aldec else ifeq ($(SIM),VCS) include $(SIM_ROOT)/makefiles/Makefile.vcs +else +$(error "Unknown simulator selected. Set SIM to either ICARUS, ALDEC or VCS") endif endif endif diff --git a/makefiles/Makefile.vcs b/makefiles/Makefile.vcs index edc25dea..44a7e632 100644 --- a/makefiles/Makefile.vcs +++ b/makefiles/Makefile.vcs @@ -37,13 +37,20 @@ $(OBJ_DIR): # TODO: # investigate +vpi+1 option which reduces memory requirements +# Can't do this using an argument, we have to create a PLI table file +# enabling write access to the design pli.tab : - echo "acc+=read_write,callback,force:*" > $@ + echo "acc+=rw,wn:*" > $@ .PHONY: sim sim: $(OBJ_DIR) $(VERILOG_SOURCES) pli.tab PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ vcs -R -debug_all +acc+1 -diag vpi +vpi -P pli.tab -load cocotb.vpi $(VERILOG_SOURCES) clean: - rm -rf $(OBJ_DIR) + -rm -rf $(OBJ_DIR) + -rm wn.tab + -rm -rf csrc + -rm -rf simv.daidir + -rm simv + -rm ucli.key From 06c6ff110fdba9be37797988fa9efcc0da181a16 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Mon, 17 Jun 2013 09:30:51 +0100 Subject: [PATCH 0015/2656] Correct reset on DUT in example --- examples/demo/demo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/demo/demo.py b/examples/demo/demo.py index a40d012a..f1eeea39 100644 --- a/examples/demo/demo.py +++ b/examples/demo/demo.py @@ -57,7 +57,7 @@ def reset_dut(clock, reset, enable): while True: yield Edge(clock) clock_ticks += 1 - if clock_ticks >= 2: + if clock_ticks >= 4: reset <= 0 enable <= 1 break From 63a359106aee09ca832d44d550e672245243c412 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Mon, 17 Jun 2013 09:33:38 +0100 Subject: [PATCH 0016/2656] Tidied up clean rules in Makefile --- Makefile | 5 +++-- makefiles/Makefile.vcs | 12 ++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 38fd50f6..ba79f7b1 100644 --- a/Makefile +++ b/Makefile @@ -47,8 +47,9 @@ dirs: @mkdir -p $(LIB_DIR) clean: - @rm -rf $(BUILD_DIR) - @find . -name "obj" | xargs rm -rf + -@rm -rf $(BUILD_DIR) + -@find . -name "obj" | xargs rm -rf + -@find . -name "*.pyc" | xargs rm -rf test: $(MAKE) -C examples diff --git a/makefiles/Makefile.vcs b/makefiles/Makefile.vcs index 44a7e632..81db784e 100644 --- a/makefiles/Makefile.vcs +++ b/makefiles/Makefile.vcs @@ -47,10 +47,10 @@ sim: $(OBJ_DIR) $(VERILOG_SOURCES) pli.tab PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ vcs -R -debug_all +acc+1 -diag vpi +vpi -P pli.tab -load cocotb.vpi $(VERILOG_SOURCES) clean: - -rm -rf $(OBJ_DIR) - -rm wn.tab - -rm -rf csrc - -rm -rf simv.daidir - -rm simv - -rm ucli.key + -@rm -rf $(OBJ_DIR) + -@rm -f pli.tab + -@rm -rf csrc + -@rm -rf simv.daidir + -@rm -f simv + -@rm -f ucli.key From 159da02b931ca152d8ca448edb0fea6b65a4cc3e Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Mon, 17 Jun 2013 13:57:46 +0100 Subject: [PATCH 0017/2656] Added LICENSE file --- LICENSE | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..cd61e247 --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +Cocotb is licensed under the Revised BSD License. Full license text below. + +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + From d0e5a5991f9d9d2803649018024fe9b6b21b4595 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 17 Jun 2013 16:45:07 +0100 Subject: [PATCH 0018/2656] Move concreate drivers from base class file --- cocotb/drivers/AvalonMM.py | 73 ++++++++++++++++++++++++++ cocotb/drivers/AvalonST.py | 47 +++++++++++++++++ cocotb/drivers/BusDriver.py | 92 +++++++++++++++++++++++++++++++++ cocotb/drivers/SFStreaming.py | 86 +++++++++++++++++++++++++++++++ cocotb/drivers/__init__.py | 97 ----------------------------------- 5 files changed, 298 insertions(+), 97 deletions(-) create mode 100644 cocotb/drivers/AvalonMM.py create mode 100644 cocotb/drivers/AvalonST.py create mode 100644 cocotb/drivers/BusDriver.py create mode 100644 cocotb/drivers/SFStreaming.py diff --git a/cocotb/drivers/AvalonMM.py b/cocotb/drivers/AvalonMM.py new file mode 100644 index 00000000..ee10e092 --- /dev/null +++ b/cocotb/drivers/AvalonMM.py @@ -0,0 +1,73 @@ +#!/bin/env python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" + + Class defining the standard interface for a driver within a testbench + + The driver is responsible for serialising transactions onto the physical pins + of the interface. This may consume simulation time. + +""" + +import logging +import math + +import cocotb +from cocotb.decorators import coroutine +from cocotb.triggers import Edge, Event, RisingEdge +from cocotb.bus import Bus +from cocotb.drivers import BusDriver + +class AvalonMM(BusDriver): + """Avalon-MM Driver + + Currently we only support the mode required to communicate with SF avalon_mapper which + is a limited subset of all the signals + + + This needs some thought... do we do a transaction based mechanism or 'blocking' read/write calls? + """ + _signals = ["readdata", "read", "write", "waitrequest", "writedata", "address"] + + def __init__(self, entity, name, clock): + BusDriver.__init__(self, entity, name, clock) + + # Drive some sensible defaults + self.bus.read <= 0 + self.bus.write <= 0 + + + def read(self, address): + """ + """ + pass + + def write(self, address): + """ + """ + pass diff --git a/cocotb/drivers/AvalonST.py b/cocotb/drivers/AvalonST.py new file mode 100644 index 00000000..adfcfab0 --- /dev/null +++ b/cocotb/drivers/AvalonST.py @@ -0,0 +1,47 @@ +#!/bin/env python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" + + Class defining the standard interface for a driver within a testbench + + The driver is responsible for serialising transactions onto the physical pins + of the interface. This may consume simulation time. + +""" + +import logging +import math + +import cocotb +from cocotb.decorators import coroutine +from cocotb.triggers import Edge, Event, RisingEdge +from cocotb.bus import Bus +from cocotb.drivers import Driver + +class AvalonST(Driver): + _signals = ["valid", "data"] diff --git a/cocotb/drivers/BusDriver.py b/cocotb/drivers/BusDriver.py new file mode 100644 index 00000000..7ae54fa8 --- /dev/null +++ b/cocotb/drivers/BusDriver.py @@ -0,0 +1,92 @@ +#!/bin/env python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" + + Class defining the standard interface for a driver within a testbench + + The driver is responsible for serialising transactions onto the physical pins + of the interface. This may consume simulation time. + +""" + +import logging +import math + +import cocotb +from cocotb.decorators import coroutine +from cocotb.triggers import Edge, Event, RisingEdge +from cocotb.bus import Bus +from cocotb.drivers import Driver + +class BusDriver(Driver): + """Tickets please! + + Wrapper around common functionality for busses which have: + a list of _signals (class attribute) + a clock + a name + an entity + """ + + def __init__(self, entity, name, clock): + self.log = logging.getLogger("cocotb.%s.%s" % (entity.name, name)) + Driver.__init__(self) + self.entity = entity + self.name = name + self.clock = clock + self.bus = Bus(self.entity, self.name, self._signals) + + +class AvalonMM(BusDriver): + """Avalon-MM Driver + + Currently we only support the mode required to communicate with SF avalon_mapper which + is a limited subset of all the signals + + + This needs some thought... do we do a transaction based mechanism or 'blocking' read/write calls? + """ + _signals = ["readdata", "read", "write", "waitrequest", "writedata", "address"] + + def __init__(self, entity, name, clock): + BusDriver.__init__(self, entity, name, clock) + + # Drive some sensible defaults + self.bus.read <= 0 + self.bus.write <= 0 + + + def read(self, address): + """ + """ + pass + + def write(self, address): + """ + """ + pass diff --git a/cocotb/drivers/SFStreaming.py b/cocotb/drivers/SFStreaming.py new file mode 100644 index 00000000..00a05d63 --- /dev/null +++ b/cocotb/drivers/SFStreaming.py @@ -0,0 +1,86 @@ +#!/bin/env python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" + + Class defining the standard interface for a driver within a testbench + + The driver is responsible for serialising transactions onto the physical pins + of the interface. This may consume simulation time. + +""" + +import logging +import math + +import cocotb +from cocotb.decorators import coroutine +from cocotb.triggers import Edge, Event, RisingEdge +from cocotb.bus import Bus +from cocotb.drivers import BusDriver +from cocotb.drivers import AvalonST + +class SFStreaming(BusDriver): + """This is the Solarflare Streaming bus as defined by the FDK. + + Expect to see a 72-bit bus (bottom 64 bits data, top 8 bits are ECC) + """ + _signals = AvalonST._signals + ["startofpacket", "endofpacket", "ready", "empty", "channel", "error"] + + def __init__(self, entity, name, clock): + BusDriver.__init__(self, entity, name, clock) + + # Drive some sensible defaults onto the bus + self.bus.startofpacket <= 0 + self.bus.endofpacket <= 0 + self.bus.valid <= 0 + self.bus.empty <= 0 + self.bus.channel <= 0 + self.bus.error <= 0 + + @coroutine + def _driver_send(self, sfpkt): + """Send a packet over the bus + + sfpkt should be an instance of SFStreamingPacket + """ + # Avoid spurious object creation by recycling + clkedge = RisingEdge(self.clock) + + self.log.info("Sending packet of length %d bytes" % len(sfpkt)) + + for word in sfpkt: + yield clkedge + while self.bus.ready != 1: + yield clkedge + self.bus.drive(word) + + yield clkedge + self.bus.endofpacket <= 0 + self.bus.valid <= 0 + + self.log.info("Packet sent successfully") diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index cac92504..6d2270b7 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -44,7 +44,6 @@ class Driver(object): - def __init__(self): """ Constructor for a driver instance @@ -128,99 +127,3 @@ def _send_thread(self): self.log.info("Sending packet...") yield self._send(transaction, callback, event) self.log.info("Done, shouldn't be waiting on _send.join() anymore..") - - -class BusDriver(Driver): - """Tickets please! - - Wrapper around common functionality for busses which have: - a list of _signals (class attribute) - a clock - a name - an entity - """ - - def __init__(self, entity, name, clock): - self.log = logging.getLogger("cocotb.%s.%s" % (entity.name, name)) - Driver.__init__(self) - self.entity = entity - self.name = name - self.clock = clock - self.bus = Bus(self.entity, self.name, self._signals) - - -class AvalonMM(BusDriver): - """Avalon-MM Driver - - Currently we only support the mode required to communicate with SF avalon_mapper which - is a limited subset of all the signals - - - This needs some thought... do we do a transaction based mechanism or 'blocking' read/write calls? - """ - _signals = ["readdata", "read", "write", "waitrequest", "writedata", "address"] - - def __init__(self, entity, name, clock): - BusDriver.__init__(self, entity, name, clock) - - # Drive some sensible defaults - self.bus.read <= 0 - self.bus.write <= 0 - - - def read(self, address): - """ - """ - pass - - def write(self, address): - """ - """ - pass - - - - -class AvalonST(Driver): - _signals = ["valid", "data"] - -class SFStreaming(BusDriver): - """This is the Solarflare Streaming bus as defined by the FDK. - - Expect to see a 72-bit bus (bottom 64 bits data, top 8 bits are ECC) - """ - _signals = AvalonST._signals + ["startofpacket", "endofpacket", "ready", "empty", "channel", "error"] - - def __init__(self, entity, name, clock): - BusDriver.__init__(self, entity, name, clock) - - # Drive some sensible defaults onto the bus - self.bus.startofpacket <= 0 - self.bus.endofpacket <= 0 - self.bus.valid <= 0 - self.bus.empty <= 0 - self.bus.channel <= 0 - self.bus.error <= 0 - - @coroutine - def _driver_send(self, sfpkt): - """Send a packet over the bus - - sfpkt should be an instance of SFStreamingPacket - """ - # Avoid spurious object creation by recycling - clkedge = RisingEdge(self.clock) - - self.log.info("Sending packet of length %d bytes" % len(sfpkt)) - - for word in sfpkt: - yield clkedge - while self.bus.ready != 1: - yield clkedge - self.bus.drive(word) - - yield clkedge - self.bus.endofpacket <= 0 - self.bus.valid <= 0 - - self.log.info("Packet sent successfully") From 0c4b40d9a2362d6a033a78854ce93270a857bf3a Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 17 Jun 2013 16:45:35 +0100 Subject: [PATCH 0019/2656] Update makefiles to remove SIM_ROOT --- examples/comb_seq/Makefile | 3 ++- examples/simple/Makefile | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/comb_seq/Makefile b/examples/comb_seq/Makefile index 387cd068..6725f632 100644 --- a/examples/comb_seq/Makefile +++ b/examples/comb_seq/Makefile @@ -32,4 +32,5 @@ VERILOG_SOURCES = issue12.v MODULE=issue12 FUNCTION=issue12 -include $(SIM_ROOT)/makefiles/Makefile.sim +include ../../makefiles/Makefile.inc +include ../../makefiles/Makefile.sim diff --git a/examples/simple/Makefile b/examples/simple/Makefile index 673dfc7f..67dcfd5d 100644 --- a/examples/simple/Makefile +++ b/examples/simple/Makefile @@ -33,4 +33,5 @@ VERILOG_SOURCES = packet_tap.v MODULE=smoketest FUNCTION=smoketest -include $(SIM_ROOT)/makefiles/Makefile.sim +include ../../makefiles/Makefile.inc +include ../../makefiles/Makefile.sim From 0584b0f316ea579cb71188dbc1a64d649e016f1a Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 17 Jun 2013 16:54:25 +0100 Subject: [PATCH 0020/2656] Issue #3 - Set format field --- lib/vpi_shim/gpi_vpi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 42ed197f..717aa271 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -296,7 +296,7 @@ gpi_cb_hdl gpi_register_value_change_callback(int (*gpi_function)(void *), void p_vpi_cb_user_data user_data; // Freed when callback fires or the callback is deregistered - user_data = (p_vpi_cb_user_data)malloc(sizeof(s_vpi_cb_user_data)); + user_data = (p_vpi_cb_user_data)calloc(sizeof(s_vpi_cb_user_data), 1); if (user_data == NULL) { LOG_WARN("VPI: Attempting allocate user_data for %s failed!", __func__); } @@ -304,6 +304,7 @@ gpi_cb_hdl gpi_register_value_change_callback(int (*gpi_function)(void *), void user_data->gpi_cb_data = gpi_cb_data; user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_recurring; + user_data->cb_value.format = vpiIntVal; vpi_time_s.type = vpiSuppressTime; vpi_value_s.format = vpiIntVal; From ba2ef3a9fcaa82c3e4cfea61a03935b50aee92e8 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 17 Jun 2013 17:29:41 +0100 Subject: [PATCH 0021/2656] Fix lacking dependency on the source files --- lib/embed/Makefile | 13 +++++++++---- lib/gpi/Makefile | 15 ++++++++++----- lib/simulator/Makefile | 10 +++++++--- lib/vpi_shim/Makefile | 13 +++++++++---- makefiles/Makefile.inc | 7 +++++++ 5 files changed, 42 insertions(+), 16 deletions(-) diff --git a/lib/embed/Makefile b/lib/embed/Makefile index ce78d090..bd970ba5 100644 --- a/lib/embed/Makefile +++ b/lib/embed/Makefile @@ -28,20 +28,25 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj -INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ -I/mnt/sources/tools/icarus/include/iverilog -I../gpi +INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ \ + -I/mnt/sources/tools/icarus/include/iverilog -I../gpi GCC_ARGS := -Werror -g LIBS := -lgpi -lpython2.7 $(PYLIBS) LD_PATH := -L$(LIB_DIR) +SRCS := gpi_embed.c +OBJS := $(SRCS:%.c=$(OBJ_DIR)/%.o) all: $(LIB_DIR)/cocotb.vpi +$(OBJ_DIR)/%.o: %.c + gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $@ $< + $(OBJ_DIR): mkdir -p $(OBJ_DIR) -$(LIB_DIR)/libcocotb.so: $(OBJ_DIR) - gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $(OBJ_DIR)/cocotb.o gpi_embed.c - gcc $(GCC_ARGS) -shared -Xlinker -export-dynamic -o $@ $(OBJ_DIR)/cocotb.o $(LIBS) $(LD_PATH) +$(LIB_DIR)/libcocotb.so: $(OBJ_DIR) $(OBJS) + gcc $(GCC_ARGS) -shared -Xlinker -export-dynamic -o $@ $(OBJS) $(LIBS) $(LD_PATH) # More rules such that this may be needed depending on the requirements of # different simulators, icarus for instance loads a .vpi libraray to use the vpi diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index bcdcda1a..9916badd 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -32,15 +32,20 @@ OBJ_DIR := obj INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ GCC_ARGS := -Werror -g -all : $(LIB_DIR)/gpi_logging.so +SRCS := gpi_logging.c +OBJS := $(SRCS:%.c=$(OBJ_DIR)/%.o) + +all: $(LIB_DIR)/gpi_logging.so + +$(OBJ_DIR)/%.o: %.c + gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $@ $< $(OBJ_DIR): mkdir -p $(OBJ_DIR) -$(LIB_DIR)/gpi_logging.so: $(OBJ_DIR) - gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $(OBJ_DIR)/gpi_logging.o gpi_logging.c - gcc $(GCC_ARGS) -shared -export-dynamic -o $(LIB_DIR)/gpi_logging.so $(OBJ_DIR)/gpi_logging.o -lpython2.7 +$(LIB_DIR)/gpi_logging.so: $(OBJ_DIR) $(OBJS) + gcc $(GCC_ARGS) -shared -export-dynamic -o $(LIB_DIR)/gpi_logging.so $(OBJS) -lpython2.7 ln -sf gpi_logging.so $(LIB_DIR)/libgpilog.so -clean : +clean: rm -rf $(OBJ_DIR) diff --git a/lib/simulator/Makefile b/lib/simulator/Makefile index ecc93253..fb12c3aa 100644 --- a/lib/simulator/Makefile +++ b/lib/simulator/Makefile @@ -32,15 +32,19 @@ GCC_ARGS := -Werror -g LIBS := -lgpi -lpython2.7 $(PYLIBS) LD_PATH := -L$(LIB_DIR) +SRCS := simulatormodule.c +OBJS := $(SRCS:%.c=$(OBJ_DIR)/%.o) all : $(LIB_DIR)/simulator.so +$(OBJ_DIR)/%.o: %.c + gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $@ $< + $(OBJ_DIR) : mkdir -p $(OBJ_DIR) -$(LIB_DIR)/simulator.so : $(OBJ_DIR) - gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $(OBJ_DIR)/simulatormodule.o simulatormodule.c - gcc $(GCC_ARGS) -shared -Xlinker -export-dynamic -o $(LIB_DIR)/simulator.so $(OBJ_DIR)/simulatormodule.o $(LIBS) $(LD_PATH) +$(LIB_DIR)/simulator.so : $(OBJ_DIR) $(OBJS) + gcc $(GCC_ARGS) -shared -Xlinker -export-dynamic -o $(LIB_DIR)/simulator.so $(OBJS) $(LIBS) $(LD_PATH) ln -sf simulator.so $(LIB_DIR)/libsim.so clean : diff --git a/lib/vpi_shim/Makefile b/lib/vpi_shim/Makefile index b0f4c147..955fa29f 100644 --- a/lib/vpi_shim/Makefile +++ b/lib/vpi_shim/Makefile @@ -32,15 +32,20 @@ GCC_ARGS := -Werror -g LIBS := -lgpilog LD_PATH := -L$(LIB_DIR) +SRCS := gpi_vpi.c +OBJS := $(SRCS:%.c=$(OBJ_DIR)/%.o) + all: $(LIB_DIR)/gpi_lib.so +$(OBJ_DIR)/%.o: %.c + gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $@ $< + $(OBJ_DIR): mkdir -p $(OBJ_DIR) -$(LIB_DIR)/gpi_lib.so: $(OBJ_DIR) - gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $(OBJ_DIR)/gpi_vpi.o gpi_vpi.c - gcc $(GCC_ARGS) -shared -export-dynamic -o $(LIB_DIR)/gpi_lib.so $(OBJ_DIR)/gpi_vpi.o $(LIBS) $(LD_PATH) +$(LIB_DIR)/gpi_lib.so: $(OBJ_DIR) $(OBJS) + gcc $(GCC_ARGS) -shared -export-dynamic -o $(LIB_DIR)/gpi_lib.so $(OBJS) $(LIBS) $(LD_PATH) ln -sf gpi_lib.so $(LIB_DIR)/libgpi.so -clean : +clean: rm -rf $(OBJ_DIR) diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index a2777f72..dc406bd4 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -42,3 +42,10 @@ export LIB_DIR=$(BUILD_DIR)/libs/$(ARCH) include $(SIM_ROOT)/makefiles/Makefile.pylib +# Disable some inbuild rules +%: %.c +%: %.o + +# Define some of our own +$(OBJ_DIR)/%.o: %.c + gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $@ $< From 3414c240e1f3c60eaff5a911868729096be8e203 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Mon, 17 Jun 2013 18:53:18 +0100 Subject: [PATCH 0022/2656] Updates to driver organisation --- cocotb/drivers/AvalonST.py | 47 ------------ cocotb/drivers/BusDriver.py | 92 ----------------------- cocotb/drivers/SFStreaming.py | 10 +-- cocotb/drivers/__init__.py | 39 ++++++++-- cocotb/drivers/{AvalonMM.py => avalon.py} | 21 +----- 5 files changed, 38 insertions(+), 171 deletions(-) delete mode 100644 cocotb/drivers/AvalonST.py delete mode 100644 cocotb/drivers/BusDriver.py rename cocotb/drivers/{AvalonMM.py => avalon.py} (85%) diff --git a/cocotb/drivers/AvalonST.py b/cocotb/drivers/AvalonST.py deleted file mode 100644 index adfcfab0..00000000 --- a/cocotb/drivers/AvalonST.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/env python - -''' Copyright (c) 2013 Potential Ventures Ltd -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - -""" - - Class defining the standard interface for a driver within a testbench - - The driver is responsible for serialising transactions onto the physical pins - of the interface. This may consume simulation time. - -""" - -import logging -import math - -import cocotb -from cocotb.decorators import coroutine -from cocotb.triggers import Edge, Event, RisingEdge -from cocotb.bus import Bus -from cocotb.drivers import Driver - -class AvalonST(Driver): - _signals = ["valid", "data"] diff --git a/cocotb/drivers/BusDriver.py b/cocotb/drivers/BusDriver.py deleted file mode 100644 index 7ae54fa8..00000000 --- a/cocotb/drivers/BusDriver.py +++ /dev/null @@ -1,92 +0,0 @@ -#!/bin/env python - -''' Copyright (c) 2013 Potential Ventures Ltd -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - -""" - - Class defining the standard interface for a driver within a testbench - - The driver is responsible for serialising transactions onto the physical pins - of the interface. This may consume simulation time. - -""" - -import logging -import math - -import cocotb -from cocotb.decorators import coroutine -from cocotb.triggers import Edge, Event, RisingEdge -from cocotb.bus import Bus -from cocotb.drivers import Driver - -class BusDriver(Driver): - """Tickets please! - - Wrapper around common functionality for busses which have: - a list of _signals (class attribute) - a clock - a name - an entity - """ - - def __init__(self, entity, name, clock): - self.log = logging.getLogger("cocotb.%s.%s" % (entity.name, name)) - Driver.__init__(self) - self.entity = entity - self.name = name - self.clock = clock - self.bus = Bus(self.entity, self.name, self._signals) - - -class AvalonMM(BusDriver): - """Avalon-MM Driver - - Currently we only support the mode required to communicate with SF avalon_mapper which - is a limited subset of all the signals - - - This needs some thought... do we do a transaction based mechanism or 'blocking' read/write calls? - """ - _signals = ["readdata", "read", "write", "waitrequest", "writedata", "address"] - - def __init__(self, entity, name, clock): - BusDriver.__init__(self, entity, name, clock) - - # Drive some sensible defaults - self.bus.read <= 0 - self.bus.write <= 0 - - - def read(self, address): - """ - """ - pass - - def write(self, address): - """ - """ - pass diff --git a/cocotb/drivers/SFStreaming.py b/cocotb/drivers/SFStreaming.py index 00a05d63..e254d7cd 100644 --- a/cocotb/drivers/SFStreaming.py +++ b/cocotb/drivers/SFStreaming.py @@ -34,15 +34,11 @@ """ -import logging -import math - -import cocotb from cocotb.decorators import coroutine -from cocotb.triggers import Edge, Event, RisingEdge -from cocotb.bus import Bus +from cocotb.triggers import RisingEdge + from cocotb.drivers import BusDriver -from cocotb.drivers import AvalonST +from cocotb.drivers.avalon import AvalonST class SFStreaming(BusDriver): """This is the Solarflare Streaming bus as defined by the FDK. diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index 6d2270b7..b6e4c3ae 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -26,24 +26,25 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' """ - - Class defining the standard interface for a driver within a testbench - - The driver is responsible for serialising transactions onto the physical pins - of the interface. This may consume simulation time. - + Set of common driver base classes """ + import logging -import math import cocotb from cocotb.decorators import coroutine -from cocotb.triggers import Edge, Event, RisingEdge +from cocotb.triggers import Event from cocotb.bus import Bus class Driver(object): + """ + + Class defining the standard interface for a driver within a testbench + The driver is responsible for serialising transactions onto the physical pins + of the interface. This may consume simulation time. + """ def __init__(self): """ Constructor for a driver instance @@ -127,3 +128,25 @@ def _send_thread(self): self.log.info("Sending packet...") yield self._send(transaction, callback, event) self.log.info("Done, shouldn't be waiting on _send.join() anymore..") + + + +class BusDriver(Driver): + """Tickets please! + + Wrapper around common functionality for busses which have: + a list of _signals (class attribute) + a clock + a name + an entity + """ + + def __init__(self, entity, name, clock): + self.log = logging.getLogger("cocotb.%s.%s" % (entity.name, name)) + Driver.__init__(self) + self.entity = entity + self.name = name + self.clock = clock + self.bus = Bus(self.entity, self.name, self._signals) + + diff --git a/cocotb/drivers/AvalonMM.py b/cocotb/drivers/avalon.py similarity index 85% rename from cocotb/drivers/AvalonMM.py rename to cocotb/drivers/avalon.py index ee10e092..e1c400af 100644 --- a/cocotb/drivers/AvalonMM.py +++ b/cocotb/drivers/avalon.py @@ -1,5 +1,3 @@ -#!/bin/env python - ''' Copyright (c) 2013 Potential Ventures Ltd All rights reserved. @@ -25,22 +23,7 @@ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' -""" - - Class defining the standard interface for a driver within a testbench - - The driver is responsible for serialising transactions onto the physical pins - of the interface. This may consume simulation time. - -""" -import logging -import math - -import cocotb -from cocotb.decorators import coroutine -from cocotb.triggers import Edge, Event, RisingEdge -from cocotb.bus import Bus from cocotb.drivers import BusDriver class AvalonMM(BusDriver): @@ -71,3 +54,7 @@ def write(self, address): """ """ pass + + +class AvalonST(BusDriver): + _signals = ["valid", "data"] From 94e028cbfc627d656425cfcfd4aced69bab39bcd Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 18 Jun 2013 15:49:16 +0100 Subject: [PATCH 0023/2656] Fixed broken down bus --- cocotb/bus.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/cocotb/bus.py b/cocotb/bus.py index 2406f739..953d20cf 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -27,8 +27,6 @@ """ Common bus related functionality - - A bus is simply defined as a collection of signals """ class Bus(object): @@ -41,23 +39,36 @@ def __init__(self, entity, name, signals): name: name of the bus signals: array of signal names """ + self._entity = entity + self._name = name + self._signals = {} + for signal in signals: signame = name + "_" + signal setattr(self, signal, getattr(entity, signame)) + self._signals[signal] = getattr(self, signal) - self._signals = {} - self._signals[signal] = getattr(self, signal) - self._entity = entity - self._name = name - def drive(self, obj): + def drive(self, obj, strict=False): """ Drives values onto the bus. - obj is an object with attribute names that match the bus signals + Args: + obj (any type) : object with attribute names that match the bus signals + + Kwargs: + strict (bool) : Check that all signals are being assigned + + Raises: + AttributeError """ + + print self._signals for name, hdl in self._signals.items(): if not hasattr(obj, name): - raise AttributeError("Unable to drive onto %s.%s because %s is missing attribute %s" % + if strict: + raise AttributeError("Unable to drive onto %s.%s because %s is missing attribute %s" % (self._entity.name, self._name, obj.__class__.__name__, name)) - hdl <= getattr(obj, name) + else: continue + val = getattr(obj, name) + hdl <= val From cbdb7ce1571d0341698b66cf8b6476da071a55cd Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 18 Jun 2013 15:50:48 +0100 Subject: [PATCH 0024/2656] Moved ctypes pack/unpack functions into cocotb.utils --- cocotb/utils.py | 50 ++++++++++++++++++++++ modules/sf_streaming/model/sf_streaming.py | 38 ++++++++-------- 2 files changed, 67 insertions(+), 21 deletions(-) diff --git a/cocotb/utils.py b/cocotb/utils.py index 3f611ad5..429b3aed 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -25,6 +25,56 @@ """Collection of handy functions""" +import ctypes + +# Ctypes helper functions + +def pack(ctypes_obj): + """Convert a ctypes structure into a python string + + + Args: + ctypes_obj (ctypes.Structure): ctypes structure to convert to a string + + + Returns: + New python string containing the bytes from memory holding ctypes_obj + """ + return ctypes.string_at(ctypes.addressof(ctypes_obj), ctypes.sizeof(ctypes_obj)) + + +def unpack(ctypes_obj, string, bytes=None): + """Unpack a python string into a ctypes structure + + Args: + ctypes_obj (ctypes.Structure): ctypes structure to pack into + + string (str): String to copy over the ctypes_obj memory space + + Kwargs: + bytes: Number of bytes to copy + + Raises: + ValueError, MemoryError + + If the length of the string is not the correct size for the memory footprint of the + ctypes structure then the bytes keyword argument must be used + """ + if bytes is None: + if len(string) != ctypes.sizeof(ctypes_obj): + raise ValueError("Attempt to unpack a string of size %d into a \ + struct of size %d" % (len(string), ctypes.sizeof(ctypes_obj))) + bytes = len(string) + + if bytes > ctypes.sizeof(ctypes_obj): + raise MemoryError("Attempt to unpack %d bytes over an object \ + of size %d" % (bytes, ctypes.sizeof(ctypes_obj))) + + ctypes.memmove(ctypes.addressof(ctypes.ctypes_obj), string, bytes) + + + + import ANSI diff --git a/modules/sf_streaming/model/sf_streaming.py b/modules/sf_streaming/model/sf_streaming.py index d07b869d..cf1159f6 100644 --- a/modules/sf_streaming/model/sf_streaming.py +++ b/modules/sf_streaming/model/sf_streaming.py @@ -28,27 +28,13 @@ """ from ctypes import * -from scapy.all import * - -# The only thing we export is the SFStreamingPacket -__all__ = ["SFStreamingPacket", "SFStreamingBusWord"] - -# Helper functions FIXME move somewhere common -def pack(ctypes_obj): - """Convert a ctypes structure into a python string""" - return string_at(addressof(ctypes_obj), sizeof(ctypes_obj)) +from scapy.all import Ether, IP, UDP -def unpack(ctypes_obj, string, bytes=None): - """Unpack a python string into a ctypes structure +from cocotb.utils import pack, unpack - If the length of the string is not the correct size for the memory footprint of the - ctypes structure then the bytes keyword argument must be used - """ - if bytes is None and len(string) != sizeof(ctypes_obj): - raise ValueError("Attempt to unpack a string of size %d into a struct of size %d" % (len(string), sizeof(ctypes_obj))) - if bytes is None: bytes = len(string) - memmove(addressof(ctypes_obj), string, bytes) +# The only thing we export is the SFStreamingPacket +__all__ = ["SFStreamingPacket", "SFStreamingBusWord"] # Enumerations for the detected protocol @@ -106,12 +92,22 @@ class SFStreamingPacket(object): We could then alias self.pkt to pull out the packet contents from the array. """ - def __init__(self, pkt): + def __init__(self, pkt=None): """pkt is a string""" self.metaword = SFMetaWord() self.pkt = pkt - self.parse() self._ptr = 0 + if pkt is not None: + self.wrap(pkt) + + + def wrap(self, pkt): + """ + Wrap a packet, parsing the packet and setting up the descriptor field + accordingly + """ + self.pkt = pkt + self.parse() def parse(self): """Parse the packet and populate the metaword descriptor field @@ -143,7 +139,7 @@ def parse(self): return def __len__(self): - return len(self.pkt) + 8 + return len(self.pkt) + 8 # FIXME: + len(self.metaword) def __iter__(self): self._ptr = None From 3d9e942d4f5b9d1de324f3e4d03bb3ab06bcc2e7 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 18 Jun 2013 15:51:13 +0100 Subject: [PATCH 0025/2656] Permit assignment of ctypes structs to handles, cache length --- cocotb/handle.py | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index ca914d7e..7fb63a86 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -28,6 +28,8 @@ # -*- coding: utf-8 -*- import logging +import ctypes + import simulator as simulator import cocotb from cocotb.binary import BinaryValue @@ -41,6 +43,7 @@ def __init__(self, handle): """ self._handle = handle # handle used for future simulator transactions self._sub_handles = {} # Dictionary of SimHandle objects created by getattr + self._len = None self.name = simulator.get_name_string(self._handle) self.fullname = self.name + '(%s)' % simulator.get_type_string(self._handle) @@ -65,11 +68,25 @@ def getvalue(self): return result def setimeadiatevalue(self, value): - """Set the value of the underlying simulation object to value. + """ + Set the value of the underlying simulation object to value. + + Args: + value (ctypes.Structure, cocotb.binary.BinaryValue, int) + The value to drive onto the simulator object - This operation will fail unless the handle refers to a modifiable object eg signal or variable. - We determine the library call to make based on the type of the value""" - if isinstance(value, BinaryValue): + Raises: + TypeError + + This operation will fail unless the handle refers to a modifiable + object eg net, signal or variable. + + We determine the library call to make based on the type of the value + """ + if isinstance(value, ctypes.Structure): + simulator.set_signal_val_str(self._handle, \ + BinaryValue(value=cocotb.utils.pack(value), bits=len(self)).binstr) + elif isinstance(value, BinaryValue): simulator.set_signal_val_str(self._handle, value.binstr) elif isinstance(value, int): simulator.set_signal_val(self._handle, value) @@ -106,7 +123,9 @@ def __len__(self): TODO: Handle other types (loops, generate etc) """ - return len(self._get_value_str()) + if self._len is None: + self._len = len(self._get_value_str()) + return self._len def __cmp__(self, other): From 639730cc0e5012037e1add2c74fe3633771d48c9 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 18 Jun 2013 15:52:06 +0100 Subject: [PATCH 0026/2656] Cleanup: correct out-of-date path --- cocotb/scoreboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index d38a5aae..b8556daa 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -31,7 +31,7 @@ import logging from cocotb.utils import hexdiffs -from cocotb.monitor import Monitor +from cocotb.monitors import Monitor class Scoreboard(object): """Generic scorboarding class From 9400bb73307079c17b2deb9dd97fb50941d618bf Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 18 Jun 2013 17:04:34 +0100 Subject: [PATCH 0027/2656] Start of @test decorator (issue #7) --- cocotb/__init__.py | 6 ++++++ cocotb/decorators.py | 47 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index a917c841..7299592f 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -41,6 +41,9 @@ log = logging.getLogger('cocotb') log.setLevel(logging.INFO) +class TestFailed(Exception): + pass + class SimLogFormatter(logging.Formatter): @@ -121,3 +124,6 @@ def my_import(name): scheduler.add(coroutine) _rlock.release() return True + +from cocotb.decorators import test + diff --git a/cocotb/decorators.py b/cocotb/decorators.py index d4b3493e..ef2ecf5f 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -25,6 +25,7 @@ import logging +import cocotb from cocotb.triggers import Join @@ -42,6 +43,13 @@ def __call__(self): if self.callback is not None: self.callback() +class TestComplete(StopIteration): + """ + Indicate that a test has finished + """ + def __init__(self, result): + self.result = result + class coroutine(object): """Decorator class that allows us to provide common coroutine mechanisms: @@ -70,6 +78,7 @@ def __get__(self, obj, type=None): def __iter__(self): return self def next(self): + """FIXME: deprecated by send method?""" try: return self._coro.next() except StopIteration: @@ -107,3 +116,41 @@ def __nonzero__(self): if the coroutine has finished return false otherwise return true""" return not self._finished + + +class test(coroutine): + """Decorator to mark a fucntion as a test + + All tests are coroutines. The test decorator provides + some common reporting etc, a test timeout and allows + us to mark tests as expected failures. + """ + def __init__(self, timeout=None, expect_fail=False): + self.timeout = timeout + self.expect_fail = expect_fail + self.started = False + + def __call__(self, f): + """ + ping + + """ + super(test, self).__init__(f) + def _wrapped_test(*args, **kwargs): + super(test, self).__call__(*args, **kwargs) + return self + return _wrapped_test + + + def send(self, value): + """FIXME: problem here is that we don't let the call stack unwind...""" + if not self.started: + self.log.info("Starting test: \"%s\"\nDescription: %s" % (self.__name__, self._func.__doc__)) + self.started = True + try: + self.log.info("sending %s" % (str(value))) + return self._coro.send(value) + except StopIteration: + raise TestComplete(result="Passed") + except cocotb.TestFailed: + raise TestComplete(result="Failed") From 8d385c4d196b455bcfc97dad211038dd9e43d89b Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 18 Jun 2013 17:05:35 +0100 Subject: [PATCH 0028/2656] Cleanup: correct typo --- cocotb/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/utils.py b/cocotb/utils.py index 429b3aed..56206337 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -70,7 +70,7 @@ def unpack(ctypes_obj, string, bytes=None): raise MemoryError("Attempt to unpack %d bytes over an object \ of size %d" % (bytes, ctypes.sizeof(ctypes_obj))) - ctypes.memmove(ctypes.addressof(ctypes.ctypes_obj), string, bytes) + ctypes.memmove(ctypes.addressof(ctypes_obj), string, bytes) From dd192a710620c03d628da09f49b126ec1d8cf7e6 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 18 Jun 2013 17:05:58 +0100 Subject: [PATCH 0029/2656] Placeholder unprime() method for removing triggers --- cocotb/triggers.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 42ca9af5..e5907e5f 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -38,6 +38,14 @@ class Trigger(object): def __init__(self): self.log = logging.getLogger("cocotb.%s.0x%x" % (self.__class__.__name__, id(self))) + def unprime(self): + """Remove any pending callbacks if necessary""" + pass + + def __del__(self): + """Ensure if a trigger drops out of scope we remove any pending callbacks""" + self.unprime() + class Timer(Trigger): """ Execution will resume when the specified time period expires From 85df3f92d24f1b30f1e08e0707f0a548af9e4919 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 18 Jun 2013 17:06:31 +0100 Subject: [PATCH 0030/2656] Keep track of test errors in the scoreboard --- cocotb/scoreboard.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index b8556daa..c8a8d41d 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -48,6 +48,7 @@ class Scoreboard(object): def __init__(self, dut, reorder_depth=0): self.dut = dut self.log = logging.getLogger("cocotb.scoreboard.%s" % self.dut.name) + self.errors = 0 def add_interface(self, monitor, expected_output): """Add an interface to be scoreboarded. @@ -73,6 +74,7 @@ def check_received_transaction(transaction): else: exp = expected_output.pop(0) if transaction != exp: + self.errors += 1 self.log.error("Received transaction differed from expected output") self.log.warning(hexdiffs(exp, transaction)) else: From 02c168299f42082712a5d823aff49c7ffc69c309 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 18 Jun 2013 17:15:12 +0100 Subject: [PATCH 0031/2656] Cleanup: remove left-over debug --- cocotb/bus.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/cocotb/bus.py b/cocotb/bus.py index 953d20cf..38928fa9 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -62,8 +62,6 @@ def drive(self, obj, strict=False): Raises: AttributeError """ - - print self._signals for name, hdl in self._signals.items(): if not hasattr(obj, name): if strict: From c022b872d3779c97bb7670a77c3081f39b3404d1 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 19 Jun 2013 11:34:13 +0100 Subject: [PATCH 0032/2656] Add gitignore --- .gitignore | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..33029f90 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +*.py[cod] + +# C extensions +*.so + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +sdist +obj +develop-eggs +.installed.cfg +__pycache__ + +# Vim tmp files +*.swp + +# Waveforms +*.vcd From 14372205e4719e9644e2d920e2f5b918d3c126a4 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 19 Jun 2013 14:24:01 +0100 Subject: [PATCH 0033/2656] Added comment --- cocotb/bus.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cocotb/bus.py b/cocotb/bus.py index 38928fa9..d6827840 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -27,6 +27,8 @@ """ Common bus related functionality + + A bus is simply defined as a collection of signals """ class Bus(object): From a0ab307049d82d6cdd6c49e8e9b8aa7e6ecb6c5d Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 19 Jun 2013 14:24:19 +0100 Subject: [PATCH 0034/2656] Added FIXME for later --- cocotb/decorators.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index ef2ecf5f..ca100fdd 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -95,6 +95,10 @@ def throw(self, exc): return self._coro.throw(exc) def kill(self): + """Kill a coroutine + + FIXME: Do we want to call all the pending callbacks? + """ self.log.warning("kill() called on coroutine") self.throw(StopIteration) From 2567ef3e38034748ef1b3bfd809af8e134b733bc Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 19 Jun 2013 14:24:54 +0100 Subject: [PATCH 0035/2656] Add support for killing a gererator --- cocotb/drivers/__init__.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index b6e4c3ae..ec8f00b8 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -117,18 +117,20 @@ def _send(self, transaction, callback, event): @coroutine def _send_thread(self): - while True: - - # Sleep until we have something to send - while not self._sendQ: - yield self._pending.wait() - - transaction, callback, event = self._sendQ.pop(0) - # Send the pending transaction - self.log.info("Sending packet...") - yield self._send(transaction, callback, event) - self.log.info("Done, shouldn't be waiting on _send.join() anymore..") - + try: + while True: + + # Sleep until we have something to send + while not self._sendQ: + yield self._pending.wait() + + transaction, callback, event = self._sendQ.pop(0) + # Send the pending transaction + self.log.info("Sending packet...") + yield self._send(transaction, callback, event) + self.log.info("Done, shouldn't be waiting on _send.join() anymore..") + except StopIteration: + self.log.info("Stopping send thread on driver") class BusDriver(Driver): From 65452954255d9fab29c0248e57eea6779e1ab56a Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 19 Jun 2013 14:25:55 +0100 Subject: [PATCH 0036/2656] Dump the expected and received transactions in the scoreboard --- cocotb/scoreboard.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index c8a8d41d..03a07824 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -29,7 +29,7 @@ Common scoreboarding capability. """ import logging -from cocotb.utils import hexdiffs +from cocotb.utils import hexdump, hexdiffs from cocotb.monitors import Monitor @@ -71,11 +71,13 @@ def check_received_transaction(transaction): raise TestFailure("Recieved a transaction but wasn't expecting anything") if callable(expected_output): exp = expected_output() - else: exp = expected_output.pop(0) + else: exp = str(expected_output.pop(0)) if transaction != exp: self.errors += 1 self.log.error("Received transaction differed from expected output") + self.log.info(hexdump(exp)) + self.log.info(hexdump(transaction)) self.log.warning(hexdiffs(exp, transaction)) else: self.log.debug("Received expected transaction %d bytes" % (len(transaction))) From 848352c86df2af9a9d30955cb0a1cb92bcb4b55a Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 19 Jun 2013 14:26:30 +0100 Subject: [PATCH 0037/2656] More work on issue #7 - cleanup after a test --- cocotb/scheduler.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index abd665d0..ed5e5bb3 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -123,6 +123,21 @@ def schedule(self, coroutine, trigger=None): # Call any pending callbacks that were waiting for this coroutine to exit exc() return + + except cocotb.decorators.TestComplete as test: + self.log.info("Test completed") + # Unprime all pending triggers: + for trigger, waiting in self.waiting.items(): + trigger.unprime() + for coro in waiting: + try: coro.kill() + except StopIteration: pass + self.waiting = [] + self.log.info("Test result: %s" % str(test.result)) + + # FIXME: proper teardown + stop + if isinstance(result, triggers.Trigger): self._add_trigger(result, coroutine) elif isinstance(result, cocotb.decorators.coroutine): From fc66ec8fee492f7fea452afd7b8e6559797600cd Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 19 Jun 2013 14:27:45 +0100 Subject: [PATCH 0038/2656] Simplify if statement --- cocotb/handle.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 7fb63a86..f7797aeb 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -84,9 +84,8 @@ def setimeadiatevalue(self, value): We determine the library call to make based on the type of the value """ if isinstance(value, ctypes.Structure): - simulator.set_signal_val_str(self._handle, \ - BinaryValue(value=cocotb.utils.pack(value), bits=len(self)).binstr) - elif isinstance(value, BinaryValue): + value = BinaryValue(value=cocotb.utils.pack(value), bits=len(self)) + if isinstance(value, BinaryValue): simulator.set_signal_val_str(self._handle, value.binstr) elif isinstance(value, int): simulator.set_signal_val(self._handle, value) From 4613b7bee9ac5ad7e0a0e48298be70ef17fba89f Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 19 Jun 2013 14:28:08 +0100 Subject: [PATCH 0039/2656] Issue #4: Add method to call sim stop via vpi --- lib/simulator/simulatormodule.c | 6 ++++++ lib/simulator/simulatormodule.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index bbd7555f..97e338be 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -546,6 +546,12 @@ static PyObject *get_sim_time(PyObject *self, PyObject *args) return pTuple; } +static PyObject *stop_simulator(PyObject *self, PyObject *args) +{ + gpi_sim_end(); + return Py_BuildValue("s", "OK!"); +} + static PyObject *create_clock(PyObject *self, PyObject *args) { gpi_sim_hdl hdl; diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 93885299..5065577b 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -65,6 +65,7 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args); static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args); static PyObject *create_clock(PyObject *self, PyObject *args); static PyObject *stop_clock(PyObject *self, PyObject *args); +static Pyobject *stop_simulator(PyObject *self, PyObject *args); static PyObject *get_sim_time(PyObject *self, PyObject *args); @@ -86,6 +87,7 @@ static PyMethodDef SimulatorMethods[] = { {"register_rwsynch_callback", register_rwsynch_callback, METH_VARARGS, "Register a callback for the readwrite section"}, {"create_clock", create_clock, METH_VARARGS, "Register a clock object"}, {"stop_clock", stop_clock, METH_VARARGS, "Terminate a clock"}, + {"stop_simulator", stop_simulator, METH_VARARGS, "Instruct the attached simulator to stop"}, // FIXME METH_NOARGS => initialization from incompatible pointer type {"get_sim_time", get_sim_time, METH_VARARGS, "Get the current simulation time as a float"}, From 5a787fe92fcf48573cde0756816c486f153cfe1c Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 19 Jun 2013 14:30:16 +0100 Subject: [PATCH 0040/2656] Issue #7 - convert existing examples to use @test decorator --- examples/comb_seq/issue12.py | 3 ++- examples/demo/demo.py | 3 ++- examples/simple/smoketest.py | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/comb_seq/issue12.py b/examples/comb_seq/issue12.py index 89371a80..848f80ef 100644 --- a/examples/comb_seq/issue12.py +++ b/examples/comb_seq/issue12.py @@ -72,8 +72,9 @@ def driver(clock, ready, data): yield RisingEdge(ready) data <= data.value.value + 1 -@coroutine +@cocotb.test() def issue12(dut): + """Test highlighting the ReadWriteSync issue (issue12 in testsauce repo)""" dut.log.info("Test got DUT:" + str(dut)) # convenience diff --git a/examples/demo/demo.py b/examples/demo/demo.py index f1eeea39..5c10bed4 100644 --- a/examples/demo/demo.py +++ b/examples/demo/demo.py @@ -70,8 +70,9 @@ def waiting_coroutine(some_event): yield some_event.wait() some_event.log.info("Coroutine woke up again! Awesome") -@coroutine +@cocotb.test() def example_test(dut): + """This is an example test""" dut.log.info("Example test got DUT:" + str(dut)) yield Timer(10000) diff --git a/examples/simple/smoketest.py b/examples/simple/smoketest.py index 58e51b87..3f3ee62f 100644 --- a/examples/simple/smoketest.py +++ b/examples/simple/smoketest.py @@ -48,8 +48,9 @@ def clock_generator(signal, period_ps): signal <= 1 yield t -@coroutine +@cocotb.test() def smoketest(dut): + """Smoke test to help get cocotb up and running""" dut.log.info("Test started, got DUT:" + str(dut)) clock_gen = cocotb.scheduler.add(clock_generator(dut.clk, 3200)) From d5857b073446355e1e0a3f08886d877e735f2c0b Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 19 Jun 2013 14:42:18 +0100 Subject: [PATCH 0041/2656] Issue #4: Correct typo in simulatormodule.h --- lib/simulator/simulatormodule.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 5065577b..f017ffdf 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -65,7 +65,7 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args); static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args); static PyObject *create_clock(PyObject *self, PyObject *args); static PyObject *stop_clock(PyObject *self, PyObject *args); -static Pyobject *stop_simulator(PyObject *self, PyObject *args); +static PyObject *stop_simulator(PyObject *self, PyObject *args); static PyObject *get_sim_time(PyObject *self, PyObject *args); From dd8625babc7c7560bafccc42d7710843b83046c6 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 19 Jun 2013 14:43:14 +0100 Subject: [PATCH 0042/2656] Issue #4: Call stop_simulation when test has exited --- cocotb/scheduler.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index ed5e5bb3..3f0e6434 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -36,6 +36,7 @@ import cocotb import cocotb.decorators +import simulator as simulator from cocotb import triggers from cocotb.handle import SimHandle from cocotb.decorators import coroutine @@ -132,11 +133,12 @@ def schedule(self, coroutine, trigger=None): for coro in waiting: try: coro.kill() except StopIteration: pass - self.waiting = [] + self.waiting = {} self.log.info("Test result: %s" % str(test.result)) # FIXME: proper teardown - stop + simulator.stop_simulator(self) + return if isinstance(result, triggers.Trigger): self._add_trigger(result, coroutine) From ea808a2c03eecccd10753841b98631bcfd2f7085 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 19 Jun 2013 14:54:28 +0100 Subject: [PATCH 0043/2656] Empty set of Sphinx based documentation --- documentation/Makefile | 177 +++++++++++++++++++++++ documentation/source/conf.py | 248 +++++++++++++++++++++++++++++++++ documentation/source/index.rst | 22 +++ 3 files changed, 447 insertions(+) create mode 100644 documentation/Makefile create mode 100644 documentation/source/conf.py create mode 100644 documentation/source/index.rst diff --git a/documentation/Makefile b/documentation/Makefile new file mode 100644 index 00000000..2ec084c3 --- /dev/null +++ b/documentation/Makefile @@ -0,0 +1,177 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/cocotb.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/cocotb.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/cocotb" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/cocotb" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/documentation/source/conf.py b/documentation/source/conf.py new file mode 100644 index 00000000..a0a95004 --- /dev/null +++ b/documentation/source/conf.py @@ -0,0 +1,248 @@ +# -*- coding: utf-8 -*- +# +# cocotb documentation build configuration file, created by +# sphinx-quickstart on Wed Jun 19 14:44:09 2013. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.pngmath', 'sphinx.ext.viewcode'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'cocotb' +copyright = u'2013, PotentialVentures' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.1' +# The full version, including alpha/beta/rc tags. +release = '0.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'cocotbdoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'cocotb.tex', u'cocotb Documentation', + u'PotentialVentures', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'cocotb', u'cocotb Documentation', + [u'PotentialVentures'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'cocotb', u'cocotb Documentation', + u'PotentialVentures', 'cocotb', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False diff --git a/documentation/source/index.rst b/documentation/source/index.rst new file mode 100644 index 00000000..617215d4 --- /dev/null +++ b/documentation/source/index.rst @@ -0,0 +1,22 @@ +.. cocotb documentation master file, created by + sphinx-quickstart on Wed Jun 19 14:44:09 2013. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to cocotb's documentation! +================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + From 431bf0b2cd4423524e5eea3a6a2e8a195c10bb9f Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 19 Jun 2013 15:19:33 +0100 Subject: [PATCH 0044/2656] Skeletal documentation outline, testing post-commit hook --- documentation/source/coroutines.rst | 6 ++++++ documentation/source/drivers.rst | 5 +++++ documentation/source/index.rst | 7 +++++++ documentation/source/introduction.rst | 4 ++++ documentation/source/monitors.rst | 5 +++++ documentation/source/scoreboards.rst | 5 +++++ documentation/source/tests.rst | 7 +++++++ documentation/source/triggers.rst | 5 +++++ 8 files changed, 44 insertions(+) create mode 100644 documentation/source/coroutines.rst create mode 100644 documentation/source/drivers.rst create mode 100644 documentation/source/introduction.rst create mode 100644 documentation/source/monitors.rst create mode 100644 documentation/source/scoreboards.rst create mode 100644 documentation/source/tests.rst create mode 100644 documentation/source/triggers.rst diff --git a/documentation/source/coroutines.rst b/documentation/source/coroutines.rst new file mode 100644 index 00000000..b1ced294 --- /dev/null +++ b/documentation/source/coroutines.rst @@ -0,0 +1,6 @@ +Coroutines +========== + +Information about coroutines + + diff --git a/documentation/source/drivers.rst b/documentation/source/drivers.rst new file mode 100644 index 00000000..181a5357 --- /dev/null +++ b/documentation/source/drivers.rst @@ -0,0 +1,5 @@ +Drivers +======= + +Drivers abstract bus functionality + diff --git a/documentation/source/index.rst b/documentation/source/index.rst index 617215d4..7b284975 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -11,6 +11,13 @@ Contents: .. toctree:: :maxdepth: 2 + introduction + coroutines + triggers + tests + drivers + monitors + scoreboards Indices and tables diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst new file mode 100644 index 00000000..bba8ca29 --- /dev/null +++ b/documentation/source/introduction.rst @@ -0,0 +1,4 @@ +Introduction +============ + +Brief introduction diff --git a/documentation/source/monitors.rst b/documentation/source/monitors.rst new file mode 100644 index 00000000..a99a22ec --- /dev/null +++ b/documentation/source/monitors.rst @@ -0,0 +1,5 @@ +Monitors +======== + +Monitors abstract bus functionlity for reconstructing transactions + diff --git a/documentation/source/scoreboards.rst b/documentation/source/scoreboards.rst new file mode 100644 index 00000000..046fdc40 --- /dev/null +++ b/documentation/source/scoreboards.rst @@ -0,0 +1,5 @@ +Scoreboards +=========== + +Placeholder + diff --git a/documentation/source/tests.rst b/documentation/source/tests.rst new file mode 100644 index 00000000..e938e399 --- /dev/null +++ b/documentation/source/tests.rst @@ -0,0 +1,7 @@ +Tests +===== + +Tests are defined using the @test decorator + +.. autoclass:: cocotb.decorators.test + diff --git a/documentation/source/triggers.rst b/documentation/source/triggers.rst new file mode 100644 index 00000000..00aad38e --- /dev/null +++ b/documentation/source/triggers.rst @@ -0,0 +1,5 @@ +Triggers +======== + + +All stuff about triggers From 933db62ec9643111949b1de0ccab6a1465920493 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 19 Jun 2013 16:27:34 +0100 Subject: [PATCH 0045/2656] Cleanup: Fix broken test --- examples/simple/smoketest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/simple/smoketest.py b/examples/simple/smoketest.py index 3f3ee62f..be1304e2 100644 --- a/examples/simple/smoketest.py +++ b/examples/simple/smoketest.py @@ -33,7 +33,7 @@ from cocotb.generators import feeds from cocotb.decorators import coroutine from cocotb.triggers import Timer, Edge, Event -from cocotb.drivers import SFStreaming +from cocotb.drivers.SFStreaming import SFStreaming from cocotb.monitors import SFStreaming as SFMon from cocotb.generators.feeds.itch_feed import * From 96e029d8f2d278494da36c8d8bb6fb062ecdc658 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 19 Jun 2013 16:30:26 +0100 Subject: [PATCH 0046/2656] Removed deprecated file --- cocotb/generators/__init__.py | 29 ++++++++++ .../opra_binary_payload_generators.py | 57 ------------------- 2 files changed, 29 insertions(+), 57 deletions(-) delete mode 100644 cocotb/generators/opra_binary_payload_generators.py diff --git a/cocotb/generators/__init__.py b/cocotb/generators/__init__.py index e69de29b..fc266938 100644 --- a/cocotb/generators/__init__.py +++ b/cocotb/generators/__init__.py @@ -0,0 +1,29 @@ +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" + Set of general generic generators +""" + diff --git a/cocotb/generators/opra_binary_payload_generators.py b/cocotb/generators/opra_binary_payload_generators.py deleted file mode 100644 index f8ae7a51..00000000 --- a/cocotb/generators/opra_binary_payload_generators.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env python - -''' Copyright (c) 2013 Potential Ventures Ltd -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - - -""" - -Selection of generators for OPRA binary payload data - -Comprises a block followed by a list of messages - -""" -from dpkt.pcap import Reader -from scapy.all import Ether - -__all__ = ["from_pcap"] - -def from_pcap(fname="/home/chiggs/market-data/nysetech-data-feeds/opra_binary_3.20130320.pcap", npackets=50): - """reads in a pcap file and spits out the UDP payload - - FIXME assumes all UDP packets are OPRA - - """ - sent = 0 - with open(fname, 'r') as f: - pcap = Reader(f) - for index, (timestamp, packet) in enumerate(pcap): - if sent > npackets: break - p = Ether(packet) - try: - yield str(p["UDP"].payload) - sent += 1 - except AttributeError: - continue From e96367200ef7c3d297fc40376bd4bcbefcda3069 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 19 Jun 2013 16:53:12 +0100 Subject: [PATCH 0047/2656] Added *~ to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 33029f90..deedff4e 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ __pycache__ # Vim tmp files *.swp +*~ # Waveforms *.vcd From 3f0a14b1cd0d6930db60a8d52138f5ded056d643 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 19 Jun 2013 16:54:47 +0100 Subject: [PATCH 0048/2656] Further housekeeping of drivers and monitors --- cocotb/drivers/__init__.py | 10 +- cocotb/drivers/avalon.py | 6 ++ .../drivers/{SFStreaming.py => solarflare.py} | 16 ++-- cocotb/monitors/__init__.py | 94 +------------------ cocotb/monitors/avalon.py | 93 ++++++++++++++++++ cocotb/monitors/solarflare.py | 82 ++++++++++++++++ examples/simple/smoketest.py | 6 +- 7 files changed, 202 insertions(+), 105 deletions(-) rename cocotb/drivers/{SFStreaming.py => solarflare.py} (90%) create mode 100644 cocotb/monitors/avalon.py create mode 100644 cocotb/monitors/solarflare.py diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index ec8f00b8..023aafea 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -134,8 +134,7 @@ def _send_thread(self): class BusDriver(Driver): - """Tickets please! - + """ Wrapper around common functionality for busses which have: a list of _signals (class attribute) a clock @@ -151,4 +150,11 @@ def __init__(self, entity, name, clock): self.clock = clock self.bus = Bus(self.entity, self.name, self._signals) +class ValidatedBusDriver(BusDriver): + """ + + + """ + + diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index e1c400af..cf5e71a7 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -22,7 +22,13 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' +""" +Drivers for Altera Avalon interfaces. +See http://www.altera.co.uk/literature/manual/mnl_avalon_spec.pdf + +NB Currently we only support a very small subset of functionality +""" from cocotb.drivers import BusDriver diff --git a/cocotb/drivers/SFStreaming.py b/cocotb/drivers/solarflare.py similarity index 90% rename from cocotb/drivers/SFStreaming.py rename to cocotb/drivers/solarflare.py index e254d7cd..868b0d4d 100644 --- a/cocotb/drivers/SFStreaming.py +++ b/cocotb/drivers/solarflare.py @@ -1,5 +1,3 @@ -#!/bin/env python - ''' Copyright (c) 2013 Potential Ventures Ltd All rights reserved. @@ -24,24 +22,20 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - """ +Drivers for Solarflare bus format. - Class defining the standard interface for a driver within a testbench - - The driver is responsible for serialising transactions onto the physical pins - of the interface. This may consume simulation time. - +A specialisation of the AvalonST bus """ - from cocotb.decorators import coroutine from cocotb.triggers import RisingEdge from cocotb.drivers import BusDriver from cocotb.drivers.avalon import AvalonST + class SFStreaming(BusDriver): - """This is the Solarflare Streaming bus as defined by the FDK. + """This is the Solarflare Streaming bus as defined by the Solarflare FDK. Expect to see a 72-bit bus (bottom 64 bits data, top 8 bits are ECC) """ @@ -70,9 +64,11 @@ def _driver_send(self, sfpkt): self.log.info("Sending packet of length %d bytes" % len(sfpkt)) for word in sfpkt: + word.valid = 1 yield clkedge while self.bus.ready != 1: yield clkedge + self.log.debug("Writing word onto bus: %s" % str(word)) self.bus.drive(word) yield clkedge diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index 0e3391bc..dcde2bc4 100644 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -38,10 +38,9 @@ import cocotb from cocotb.decorators import coroutine -from cocotb.triggers import Edge, Event, RisingEdge +from cocotb.triggers import Edge, Event, RisingEdge, ReadOnly from cocotb.binary import BinaryValue from cocotb.bus import Bus -from cocotb.utils import hexdump class Monitor(object): @@ -106,100 +105,15 @@ def _recv(self, transaction): self._event.set() -class AvalonST(Monitor): - _signals = ["valid", "data"] - - - -class AvalonSTPkts(Monitor): +class BusMonitor(Monitor): """ - Packetised AvalonST bus + Wrapper providing common functionality for monitoring busses """ - _signals = AvalonST._signals + ["startofpacket", "endofpacket", "ready", "empty", "error"] - def __init__(self, entity, name, clock, callback=None, event=None): + self.log = logging.getLogger("cocotb.%s.%s" % (entity.name, name)) self.entity = entity self.name = name self.clock = clock self.bus = Bus(self.entity, self.name, self._signals) - self.log = logging.getLogger("cocotb.%s.%s" % (self.entity.name, self.name)) - Monitor.__init__(self, callback=callback, event=event) - @coroutine - def _monitor_recv(self): - """Watch the pins and reconstruct transactions""" - - # Avoid spurious object creation by recycling - clkedge = RisingEdge(self.clock) - pkt = "" - - while True: - yield clkedge - - if self.bus.valid.value and self.bus.startofpacket.value: - vec = self.bus.data.value - self.bus.data.log.info("%s %s" % (vec.binstr, repr(vec.buff))) - pkt += vec.buff - while True: - yield clkedge - if self.bus.valid.value: - vec = self.bus.data.value - self.bus.data.log.debug("%s %s" % (vec.binstr, repr(vec.buff))) - pkt += vec.buff - if self.bus.endofpacket.value: - self.log.info("Recieved a packet of %d bytes" % len(pkt)) - self.log.debug(hexdump(str((pkt)))) - self._recv(pkt) - pkt = "" - break - - - -class SFStreaming(Monitor): - """This is the Solarflare Streaming bus as defined by the FDK. - - Expect to see a 72-bit bus (bottom 64 bits data, top 8 bits are ECC) - - TODO: - Metaword / channel bits - ECC checking - """ - _signals = AvalonST._signals + ["startofpacket", "endofpacket", "ready", "empty", "channel", "error"] - - def __init__(self, entity, name, clock, callback=None, event=None): - self.entity = entity - self.name = name - self.clock = clock - self.bus = Bus(self.entity, self.name, self._signals) - self.log = logging.getLogger("cocotb.%s.%s" % (self.entity.name, self.name)) - - Monitor.__init__(self, callback=callback, event=event) - - @coroutine - def _monitor_recv(self): - """Watch the pins and reconstruct transactions""" - - # Avoid spurious object creation by recycling - clkedge = RisingEdge(self.clock) - pkt = "" - - while True: - yield clkedge - - if self.bus.valid.value and self.bus.startofpacket.value: - vec = self.bus.data.value - self.bus.data.log.info("%s %s" % (vec.binstr, repr(vec.buff))) - pkt += vec.buff - while True: - yield clkedge - if self.bus.valid.value: - vec = self.bus.data.value - self.bus.data.log.info("%s %s" % (vec.binstr, repr(vec.buff))) - pkt += vec.buff - if self.bus.endofpacket.value: - self.log.warning("Recieved a packet!!") - self.log.info(repr(pkt)) - self._recv(pkt) - pkt = "" - break diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py new file mode 100644 index 00000000..740bbdb6 --- /dev/null +++ b/cocotb/monitors/avalon.py @@ -0,0 +1,93 @@ +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' +""" +Monitors for Altera Avalon interfaces. + +See http://www.altera.co.uk/literature/manual/mnl_avalon_spec.pdf + +NB Currently we only support a very small subset of functionality +""" +from cocotb.decorators import coroutine +from cocotb.monitors import BusMonitor + +class AvalonST(BusMonitor): + """ + AvalonST bus. + + Non-packetised so each valid word is a separate transaction + """ + _signals = ["valid", "data"] + + @coroutine + def _monitor_recv(self): + """Watch the pins and reconstruct transactions""" + + # Avoid spurious object creation by recycling + clkedge = RisingEdge(self.clock) + rdonly = ReadOnly() + + # NB could yield on valid here more efficiently? + while True: + yield clkedge + yield rdonly + if self.bus.valid.value + vec = self.bus.data.value + self._recv(vec.buff) + +class AvalonSTPkts(BusMonitor): + """ + Packetised AvalonST bus + """ + _signals = AvalonST._signals + ["startofpacket", "endofpacket", "ready", "empty", "error"] + + @coroutine + def _monitor_recv(self): + """Watch the pins and reconstruct transactions""" + + # Avoid spurious object creation by recycling + clkedge = RisingEdge(self.clock) + rdonly = ReadOnly() + pkt = "" + + while True: + yield clkedge + yield rdonly + if self.bus.valid.value and self.bus.startofpacket.value: + vec = self.bus.data.value + self.bus.data.log.info("%s %s" % (vec.binstr, repr(vec.buff))) + pkt += vec.buff + while True: + yield clkedge + yield rdonly + if self.bus.valid.value: + vec = self.bus.data.value + self.bus.data.log.debug("%s %s" % (vec.binstr, repr(vec.buff))) + pkt += vec.buff + if self.bus.endofpacket.value: + self.log.info("Recieved a packet of %d bytes" % len(pkt)) + self.log.debug(hexdump(str((pkt)))) + self._recv(pkt) + pkt = "" + break diff --git a/cocotb/monitors/solarflare.py b/cocotb/monitors/solarflare.py new file mode 100644 index 00000000..189b611f --- /dev/null +++ b/cocotb/monitors/solarflare.py @@ -0,0 +1,82 @@ +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' +""" +Monitors for Altera Avalon interfaces. + +See http://www.altera.co.uk/literature/manual/mnl_avalon_spec.pdf + +NB Currently we only support a very small subset of functionality +""" + +from cocotb.decorators import coroutine +from cocotb.triggers import RisingEdge, ReadOnly +from cocotb.monitors import BusMonitor +from cocotb.utils import hexdump + +# Solarflare specific +from modules.sf_streaming.model.sf_streaming import SFStreamingData + +class SFStreaming(BusMonitor): + """This is the Solarflare Streaming bus as defined by the FDK. + + Expect to see a 72-bit bus (bottom 64 bits data, top 8 bits are ECC) + + TODO: + Metaword / channel bits + ECC checking + """ + _signals = ["startofpacket", "endofpacket", "ready", "empty", "channel", "error", "valid", "data"] + + + @coroutine + def _monitor_recv(self): + """Watch the pins and reconstruct transactions""" + + # Avoid spurious object creation by recycling + clkedge = RisingEdge(self.clock) + rdonly = ReadOnly() + word = SFStreamingData() + + pkt = "" + + while True: + yield clkedge + yield rdonly + + if self.bus.valid.value and self.bus.startofpacket.value: + vec = self.bus.data.value + pkt += vec.buff[1:][::-1] + while True: + yield clkedge + yield rdonly + if self.bus.valid.value: + vec = self.bus.data.value + pkt += vec.buff[1:][::-1] + if self.bus.endofpacket.value: + self.log.info("Recieved a packet of %d bytes", len(pkt)) + self.log.debug(hexdump(pkt)) + self._recv(pkt) + pkt = "" + break diff --git a/examples/simple/smoketest.py b/examples/simple/smoketest.py index be1304e2..92cd4b36 100644 --- a/examples/simple/smoketest.py +++ b/examples/simple/smoketest.py @@ -33,8 +33,8 @@ from cocotb.generators import feeds from cocotb.decorators import coroutine from cocotb.triggers import Timer, Edge, Event -from cocotb.drivers.SFStreaming import SFStreaming -from cocotb.monitors import SFStreaming as SFMon +from cocotb.drivers.solarflare import SFStreaming as SFDrv +from cocotb.monitors.solarflare import SFStreaming as SFMon from cocotb.generators.feeds.itch_feed import * from modules.sf_streaming.model.sf_streaming import SFStreamingPacket @@ -60,7 +60,7 @@ def smoketest(dut): yield Timer(32000) - stream_in = SFStreaming(dut, "stream_in", dut.clk) + stream_in = SFDrv(dut, "stream_in", dut.clk) stream_out = SFMon(dut, "stream_out", dut.clk) yield Timer(32000) From 4b53432c89adf9c5d551b9736424bd2dc791273e Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 19 Jun 2013 17:29:36 +0100 Subject: [PATCH 0049/2656] Pass EXTRA_ARGS to simulator --- makefiles/Makefile.vcs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/Makefile.vcs b/makefiles/Makefile.vcs index 81db784e..6fc1f675 100644 --- a/makefiles/Makefile.vcs +++ b/makefiles/Makefile.vcs @@ -45,7 +45,7 @@ pli.tab : .PHONY: sim sim: $(OBJ_DIR) $(VERILOG_SOURCES) pli.tab PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ - vcs -R -debug_all +acc+1 -diag vpi +vpi -P pli.tab -load cocotb.vpi $(VERILOG_SOURCES) + vcs -R -debug_all +acc+1 -diag vpi +vpi -P pli.tab -sverilog $(EXTRA_ARGS) -load cocotb.vpi $(VERILOG_SOURCES) clean: -@rm -rf $(OBJ_DIR) -@rm -f pli.tab From f0267b313303e4c08a4bc8cf9595bf02792b0855 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 20 Jun 2013 14:02:08 +0100 Subject: [PATCH 0050/2656] Cleanup: remove auto-generated TCL files --- examples/comb_seq/issue12.tcl | 43 ----------------------------------- examples/demo/demo.tcl | 43 ----------------------------------- 2 files changed, 86 deletions(-) delete mode 100644 examples/comb_seq/issue12.tcl delete mode 100644 examples/demo/demo.tcl diff --git a/examples/comb_seq/issue12.tcl b/examples/comb_seq/issue12.tcl deleted file mode 100644 index 84e875e8..00000000 --- a/examples/comb_seq/issue12.tcl +++ /dev/null @@ -1,43 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -# create project library and clear its contents -adel -all -alib work -set worklib work - -alog -dbg ../issue12.v - -# initialize simulation -asim +access +w -O2 -dbg -pli libcocotb issue12 - -# advance simulation -run -all - -# uncomment following line to terminate simulation automatically from script -endsim - diff --git a/examples/demo/demo.tcl b/examples/demo/demo.tcl deleted file mode 100644 index e7a74d81..00000000 --- a/examples/demo/demo.tcl +++ /dev/null @@ -1,43 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -# create project library and clear its contents -adel -all -alib work -set worklib work - -alog -dbg ../counter.v - -# initialize simulation -asim +access +w -O2 -dbg -pli libcocotb first_counter - -# advance simulation -run -all - -# uncomment following line to terminate simulation automatically from script -endsim - From 810f3fc8d95ae987eb44450b2ec9f61bf964ad95 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 20 Jun 2013 15:58:53 +0100 Subject: [PATCH 0051/2656] Start fleshing out some of the generators --- cocotb/generators/__init__.py | 15 +++++++++ cocotb/generators/bit_generators.py | 50 +++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 cocotb/generators/bit_generators.py diff --git a/cocotb/generators/__init__.py b/cocotb/generators/__init__.py index fc266938..be643e26 100644 --- a/cocotb/generators/__init__.py +++ b/cocotb/generators/__init__.py @@ -27,3 +27,18 @@ Set of general generic generators """ +def repeat(obj, nrepeat=None): + """Generator to repeatedly yield the same object + + Args: + obj (any): The object to yield + + Kwargs: + nrepeat (int): The number of times to repeatedly yield obj + """ + if nrepeat is None: + while True: + yield obj + else: + for i in range(nrepeat): + yield obj diff --git a/cocotb/generators/bit_generators.py b/cocotb/generators/bit_generators.py new file mode 100644 index 00000000..7ea003ae --- /dev/null +++ b/cocotb/generators/bit_generators.py @@ -0,0 +1,50 @@ +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" + Collection of generators for creating bit signals. + + Typically we use a single bit to control backpressure or insert IDLE + cycles onto a bus. + + These yield a tuple which is intended to be interpreted as a number of + cycles (ON,OFF) +""" +import random + + +def intermittent_single_cycles(mean=100, sigma=None): + """Generator to intermittently insert a single cycle pulse + + Kwargs: + mean (int): Average number of cycles in between single cycle gaps + + sigma (int): Standard deviation of gaps. mean/4 if sigma is None + """ + if sigma is None: + sigma = mean/4.0 + while True: + yield (abs(int(random.gauss(mean, sigma))), 1) + From 6812d3c39005ed4e35fc07071235646b82471c64 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 20 Jun 2013 15:59:28 +0100 Subject: [PATCH 0052/2656] Add support for "optional" bus signals --- cocotb/bus.py | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/cocotb/bus.py b/cocotb/bus.py index d6827840..ded508e5 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -34,12 +34,27 @@ class Bus(object): """ Wraps up a collection of signals + + Assumes we have a set of signals/nets named: + + entity.bus_name_signal + + for example a bus named "stream_in" with signals ["valid", "data"] + dut.stream_in_valid + dut.stream_in_data + + TODO: + Support for struct/record ports where signals are member names """ - def __init__(self, entity, name, signals): + def __init__(self, entity, name, signals, optional_signals=[]): """ - entity: SimHandle instance to the entity containing the bus - name: name of the bus - signals: array of signal names + Args: + entity (SimHandle): SimHandle instance to the entity containing the bus + name (str): name of the bus + signals (list): array of signal names + + Kwargs: + optiona_signals (list): array of optional signal names """ self._entity = entity self._name = name @@ -50,6 +65,17 @@ def __init__(self, entity, name, signals): setattr(self, signal, getattr(entity, signame)) self._signals[signal] = getattr(self, signal) + # Also support a set of optional signals that don't have to be present + for signal in optional_signals: + signame = name + "_" + signal + try: + hdl = getattr(entity, signame) + except AttributeError: + self._entity.log.debug("Ignoring optional missing signal %s \ + on bus %s" % signal, name) + continue + setattr(self, signal, hdl) + self._signals[signal] = getattr(self, signal) def drive(self, obj, strict=False): """ @@ -72,3 +98,7 @@ def drive(self, obj, strict=False): else: continue val = getattr(obj, name) hdl <= val + + def __le__(self, value): + """Overload the less than or equal to operator for value assignment""" + self.drive(value) From 110285c0578a26fd5f18563db24c55cc2493963e Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 20 Jun 2013 16:00:10 +0100 Subject: [PATCH 0053/2656] Cleanup: change log message verbosity --- cocotb/decorators.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index ca100fdd..6ee4aa95 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -99,7 +99,7 @@ def kill(self): FIXME: Do we want to call all the pending callbacks? """ - self.log.warning("kill() called on coroutine") + self.log.debug("kill() called on coroutine") self.throw(StopIteration) def _finished_cb(self): @@ -152,7 +152,7 @@ def send(self, value): self.log.info("Starting test: \"%s\"\nDescription: %s" % (self.__name__, self._func.__doc__)) self.started = True try: - self.log.info("sending %s" % (str(value))) + self.log.debug("Sending trigger %s" % (str(value))) return self._coro.send(value) except StopIteration: raise TestComplete(result="Passed") From b2df29eb88909eaaaaa147cb60e60a27add0d948 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 20 Jun 2013 16:40:11 +0100 Subject: [PATCH 0054/2656] Cleanup: rename generator modules --- cocotb/generators/{bit_generators.py => bit.py} | 0 cocotb/generators/{packet_generators.py => packet.py} | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) rename cocotb/generators/{bit_generators.py => bit.py} (100%) rename cocotb/generators/{packet_generators.py => packet.py} (96%) diff --git a/cocotb/generators/bit_generators.py b/cocotb/generators/bit.py similarity index 100% rename from cocotb/generators/bit_generators.py rename to cocotb/generators/bit.py diff --git a/cocotb/generators/packet_generators.py b/cocotb/generators/packet.py similarity index 96% rename from cocotb/generators/packet_generators.py rename to cocotb/generators/packet.py index 0e6d84d0..5fc8478f 100644 --- a/cocotb/generators/packet_generators.py +++ b/cocotb/generators/packet.py @@ -35,16 +35,16 @@ import random from scapy.all import Ether, IP, UDP -from byte_generators import * +from cocotb.generators.byte import * __all__ = ["udp_all_sizes", "udp_random_sizes"] _default_payload = random_data -def _get_payload(gen, bytes): +def _get_payload(gen, nbytes): """Helper function to pull a chunk of bytes from a generator""" payload = "" - while len(payload) < bytes: + while len(payload) < nbytes: payload += gen.next() return payload From 11d20e0cdf8ddc6393ee94db6666a87e8171da05 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 20 Jun 2013 16:40:54 +0100 Subject: [PATCH 0055/2656] Cleanup: rename another generator module --- cocotb/generators/{byte_generators.py => byte.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cocotb/generators/{byte_generators.py => byte.py} (100%) diff --git a/cocotb/generators/byte_generators.py b/cocotb/generators/byte.py similarity index 100% rename from cocotb/generators/byte_generators.py rename to cocotb/generators/byte.py From cdc1b439ad084b0a083ed53c3055943d92693924 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 20 Jun 2013 17:42:51 +0100 Subject: [PATCH 0056/2656] Added ValidatedBusDriver functionality --- cocotb/drivers/__init__.py | 56 ++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index 023aafea..ecc07697 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -34,7 +34,7 @@ import cocotb from cocotb.decorators import coroutine -from cocotb.triggers import Event +from cocotb.triggers import Event, RisingEdge from cocotb.bus import Bus class Driver(object): @@ -130,31 +130,77 @@ def _send_thread(self): yield self._send(transaction, callback, event) self.log.info("Done, shouldn't be waiting on _send.join() anymore..") except StopIteration: - self.log.info("Stopping send thread on driver") + self.log.debug("Stopping send thread on driver") class BusDriver(Driver): """ Wrapper around common functionality for busses which have: a list of _signals (class attribute) + a list of _optional_signals (class attribute) a clock a name an entity """ + _optional_signals = [] def __init__(self, entity, name, clock): + """ + Args: + entity (SimHandle) : a handle to the simulator entity + + name (str) : name of this bus + + clock (SimHandle) : A handle to the clock associated with this bus + """ self.log = logging.getLogger("cocotb.%s.%s" % (entity.name, name)) Driver.__init__(self) self.entity = entity self.name = name self.clock = clock - self.bus = Bus(self.entity, self.name, self._signals) + self.bus = Bus(self.entity, self.name, self._signals, self._optional_signals) + + + @coroutine + def _driver_send(self, transaction): + yield RisingEdge(self.clock) + self.bus <= transaction + class ValidatedBusDriver(BusDriver): """ + Same as a BusDriver except we support an optional generator to control + which cycles are valid + """ + def __init__(self, entity, name, clock, valid_generator=None): + """ + Args: + entity (SimHandle) : a handle to the simulator entity - """ + name (str) : name of this bus + + clock (SimHandle) : A handle to the clock associated with this bus + Kwargs: + valid_generator (generator): a generator that yields tuples of + (valid, invalid) cycles to insert + """ + BusDriver.__init__(self, entity, name, clock) + self.set_valid_generator(valid_generator=valid_generator) + + + def set_valid_generator(self, valid_generator=None): + """ + Set a new valid generator for this bus + """ + self.valid_generator = valid_generator - + # Optionally insert invalid cycles every N + if self.valid_generator is not None: + self.on, self.off = valid_generator.next() + self.log.debug("Will be on for %d cycles, off for %s" % (self.on, self.off)) + else: + # Valid every clock cycle + self.on, self.off = True, False + self.log.debug("Not using valid generator") From 3b10696f1b7b91aaf80da995b0d43a63a26cfd77 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 20 Jun 2013 18:08:22 +0100 Subject: [PATCH 0057/2656] Migrated Avalon driver to use new ValidatedBusDriver --- cocotb/drivers/avalon.py | 138 ++++++++++++++++++++++++++++++++++- cocotb/drivers/solarflare.py | 35 ++------- 2 files changed, 140 insertions(+), 33 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index cf5e71a7..1a1d03ee 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -29,8 +29,10 @@ NB Currently we only support a very small subset of functionality """ - -from cocotb.drivers import BusDriver +from cocotb.decorators import coroutine +from cocotb.triggers import RisingEdge, ReadOnly +from cocotb.drivers import BusDriver, ValidatedBusDriver +from cocotb.utils import hexdump class AvalonMM(BusDriver): """Avalon-MM Driver @@ -62,5 +64,135 @@ def write(self, address): pass -class AvalonST(BusDriver): +class AvalonST(ValidatedBusDriver): _signals = ["valid", "data"] + + +class AvalonSTPkts(ValidatedBusDriver): + _signals = ["valid", "data", "startofpacket", "endofpacket", "empty"] + _optional_signals = ["error", "channel", "ready"] + + @coroutine + def _wait_ready(self): + """Wait for a ready cycle on the bus before continuing + + Can no longer drive values this cycle... + + FIXME assumes readyLatency of 0 + """ + yield ReadOnly() + while not self.bus.ready.value: + yield RisingEdge(self.clock) + yield ReadOnly() + + @coroutine + def _send_string(self, string): + """ + Args: + string (str): A string of bytes to send over the bus + """ + # Avoid spurious object creation by recycling + clkedge = RisingEdge(self.clock) + firstword = True + + # FIXME busses that aren't integer numbers of bytes + bus_width = len(self.bus.data) / 8 + word = BinaryValue(nbits=len(self.bus.data)) + + while string: + yield clkedge + + # Insert a gap where valid is low + if not self.on: + self.bus.valid <= 0 + for i in range(self.off): + yield clkedge + self.on, self.off = self.valid_generator.next() + + # Consume a valid cycle + if self.on is not True: + self.on -= 1 + + self.bus.valid <= 1 + + if firstword: + self.bus.startofpacket <= 1 + firstword = False + + if len(string) <= bus_width: + self.bus.endofpacket <= 1 + self.bus.empty <= bus_width - len(string) + string = "" + else: + word.buff = string[:bus_width] + string = string[bus_width:] + + # If this is a bus with a ready signal, wait for this word to + # be acknowledged + if hasattr(self.bus, "ready"): + yield self._wait_ready() + + yield clkedge + self.bus.valid <= 0 + + + + @coroutine + def _send_iterable(self, pkt): + """ + Args: + pkt (iterable): Will yield objects with attributes matching the + signal names for each individual bus cycle + """ + clkedge = RisingEdge(self.clock) + + for word in pkt: + yield clkedge + + # Insert a gap where valid is low + if not self.on: + self.log.debug("Inserting %d non-valid cycles" % (self.off)) + self.bus.valid <= 0 + for i in range(self.off): + yield clkedge + self.on, self.off = self.valid_generator.next() + + # Consume a valid cycle + if self.on is not True: + self.on -= 1 + + self.bus <= word + self.bus.valid <= 1 + + # If this is a bus with a ready signal, wait for this word to + # be acknowledged + if hasattr(self.bus, "ready"): + yield self._wait_ready() + + yield clkedge + self.bus.valid <= 0 + + @coroutine + def _driver_send(self, pkt): + """Send a packet over the bus + + Args: + pkt (str or iterable): packet to drive onto the bus + + If pkt is a string, we simply send it word by word + + If pkt is an iterable, it's assumed to yield objects with attributes + matching the signal names + """ + + # Avoid spurious object creation by recycling + + self.log.info("Sending packet of length %d bytes" % len(pkt)) + self.log.debug(hexdump(pkt)) + + if isinstance(pkt, str): + yield self._send_string(pkt) + else: + yield self._send_iterable(pkt) + + self.log.info("Packet sent successfully") diff --git a/cocotb/drivers/solarflare.py b/cocotb/drivers/solarflare.py index 868b0d4d..9ccd38f7 100644 --- a/cocotb/drivers/solarflare.py +++ b/cocotb/drivers/solarflare.py @@ -30,19 +30,18 @@ from cocotb.decorators import coroutine from cocotb.triggers import RisingEdge -from cocotb.drivers import BusDriver -from cocotb.drivers.avalon import AvalonST +from cocotb.drivers.avalon import AvalonSTPkts -class SFStreaming(BusDriver): +class SFStreaming(AvalonSTPkts): """This is the Solarflare Streaming bus as defined by the Solarflare FDK. Expect to see a 72-bit bus (bottom 64 bits data, top 8 bits are ECC) """ - _signals = AvalonST._signals + ["startofpacket", "endofpacket", "ready", "empty", "channel", "error"] + _signals = ["valid", "data", "startofpacket", "endofpacket", "ready", "empty", "channel", "error"] - def __init__(self, entity, name, clock): - BusDriver.__init__(self, entity, name, clock) + def __init__(self, *args, **kwargs): + AvalonSTPkts.__init__(self, *args, **kwargs) # Drive some sensible defaults onto the bus self.bus.startofpacket <= 0 @@ -52,27 +51,3 @@ def __init__(self, entity, name, clock): self.bus.channel <= 0 self.bus.error <= 0 - @coroutine - def _driver_send(self, sfpkt): - """Send a packet over the bus - - sfpkt should be an instance of SFStreamingPacket - """ - # Avoid spurious object creation by recycling - clkedge = RisingEdge(self.clock) - - self.log.info("Sending packet of length %d bytes" % len(sfpkt)) - - for word in sfpkt: - word.valid = 1 - yield clkedge - while self.bus.ready != 1: - yield clkedge - self.log.debug("Writing word onto bus: %s" % str(word)) - self.bus.drive(word) - - yield clkedge - self.bus.endofpacket <= 0 - self.bus.valid <= 0 - - self.log.info("Packet sent successfully") From d4de92bcdc6754e4727aeef5450cde7983e404a8 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 20 Jun 2013 18:14:24 +0100 Subject: [PATCH 0058/2656] Added @public decorator for controlling what goes into __all__ --- cocotb/decorators.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 6ee4aa95..ab5b4379 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -23,12 +23,30 @@ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' +import sys import logging import cocotb from cocotb.triggers import Join +def public(f): + """Use a decorator to avoid retyping function/class names. + + * Based on an idea by Duncan Booth: + http://groups.google.com/group/comp.lang.python/msg/11cbb03e09611b8a + * Improved via a suggestion by Dave Angel: + http://groups.google.com/group/comp.lang.python/msg/3d400fb22d8a42e1 + """ + all = sys.modules[f.__module__].__dict__.setdefault('__all__', []) + if f.__name__ not in all: # Prevent duplicates if run from an IDE. + all.append(f.__name__) + return f + +public(public) # Emulate decorating ourself + + +@public class CoroutineComplete(StopIteration): """ To ensure that a coroutine has completed before we fire any triggers that @@ -122,6 +140,7 @@ def __nonzero__(self): return not self._finished +@public class test(coroutine): """Decorator to mark a fucntion as a test From 7a428826088b4b01c77d1c6508e1fd5c9ea82624 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 20 Jun 2013 18:16:49 +0100 Subject: [PATCH 0059/2656] Tidy-up of generators * Use new @public decorator to indicate which generators are available * provide new get_generators function for iterating over generators in a module --- cocotb/generators/__init__.py | 11 +++++++++++ cocotb/generators/bit.py | 4 +++- cocotb/generators/byte.py | 5 ++++- cocotb/generators/packet.py | 14 ++++---------- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/cocotb/generators/__init__.py b/cocotb/generators/__init__.py index be643e26..38400bf2 100644 --- a/cocotb/generators/__init__.py +++ b/cocotb/generators/__init__.py @@ -26,7 +26,9 @@ """ Set of general generic generators """ +from cocotb.decorators import public +@public def repeat(obj, nrepeat=None): """Generator to repeatedly yield the same object @@ -42,3 +44,12 @@ def repeat(obj, nrepeat=None): else: for i in range(nrepeat): yield obj + + +def get_generators(module): + """Return an iterator which yields all the generators in a module + + Args: + module (python module): The module to get the generators from + """ + return (getattr(module, gen) for gen in module.__all__) diff --git a/cocotb/generators/bit.py b/cocotb/generators/bit.py index 7ea003ae..15cbc05b 100644 --- a/cocotb/generators/bit.py +++ b/cocotb/generators/bit.py @@ -34,8 +34,10 @@ """ import random +from cocotb.decorators import public -def intermittent_single_cycles(mean=100, sigma=None): +@public +def intermittent_single_cycles(mean=10, sigma=None): """Generator to intermittently insert a single cycle pulse Kwargs: diff --git a/cocotb/generators/byte.py b/cocotb/generators/byte.py index b1f93ad2..e86c7ccf 100644 --- a/cocotb/generators/byte.py +++ b/cocotb/generators/byte.py @@ -30,13 +30,15 @@ Collection of generators for creating byte streams """ import random +from cocotb.decorators import public - +@public def random_data(): """Random bytes""" while True: yield chr(random.randint(0,255)) +@public def incrementing_data(increment=1): """Incrementing bytes""" val = 0 @@ -45,6 +47,7 @@ def incrementing_data(increment=1): val += increment val = val & 0xFF +@public def repeating_bytes(pattern="\x00"): """Repeat a pattern of bytes""" while True: diff --git a/cocotb/generators/packet.py b/cocotb/generators/packet.py index 5fc8478f..0ae20dc1 100644 --- a/cocotb/generators/packet.py +++ b/cocotb/generators/packet.py @@ -35,9 +35,9 @@ import random from scapy.all import Ether, IP, UDP -from cocotb.generators.byte import * -__all__ = ["udp_all_sizes", "udp_random_sizes"] +from cocotb.decorators import public +from cocotb.generators.byte import * _default_payload = random_data @@ -50,7 +50,7 @@ def _get_payload(gen, nbytes): # UDP packet generators - +@public def udp_all_sizes(max_size=1500, payload=_default_payload()): """UDP packets of every supported size""" header = Ether() / IP() / UDP () @@ -58,6 +58,7 @@ def udp_all_sizes(max_size=1500, payload=_default_payload()): for size in range(0, max_size-len(header)): yield header / _get_payload(payload, size) +@public def udp_random_sizes(npackets=100, payload=_default_payload()): """UDP packets with random sizes""" header = Ether() / IP() / UDP () @@ -66,10 +67,3 @@ def udp_random_sizes(npackets=100, payload=_default_payload()): for pkt in range(npackets): yield header / _get_payload(payload, random.randint(0,max_size)) - - -if __name__ == "__main__": - for pkt in udp_all_sizes(max_size=64): - print repr(pkt) - for pkt in udp_random_sizes(npackets=2): - print repr(pkt) From d3f912f83e9d3032026a602fd8b961d3154d1abf Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 20 Jun 2013 18:18:27 +0100 Subject: [PATCH 0060/2656] Remove some overly verbose debug --- cocotb/monitors/avalon.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index 740bbdb6..4b92e318 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -76,14 +76,12 @@ def _monitor_recv(self): yield rdonly if self.bus.valid.value and self.bus.startofpacket.value: vec = self.bus.data.value - self.bus.data.log.info("%s %s" % (vec.binstr, repr(vec.buff))) pkt += vec.buff while True: yield clkedge yield rdonly if self.bus.valid.value: vec = self.bus.data.value - self.bus.data.log.debug("%s %s" % (vec.binstr, repr(vec.buff))) pkt += vec.buff if self.bus.endofpacket.value: self.log.info("Recieved a packet of %d bytes" % len(pkt)) From c51015212e474941d97e30d49b7606d45c55ed18 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 20 Jun 2013 18:19:41 +0100 Subject: [PATCH 0061/2656] Truncate the packet according to empty signal --- cocotb/monitors/avalon.py | 3 +++ cocotb/monitors/solarflare.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index 4b92e318..78b2999c 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -84,6 +84,9 @@ def _monitor_recv(self): vec = self.bus.data.value pkt += vec.buff if self.bus.endofpacket.value: + # Truncate the empty bits + if self.bus.empty.value.value: + pkt = pkt[:-self.bus.empty.value.value] self.log.info("Recieved a packet of %d bytes" % len(pkt)) self.log.debug(hexdump(str((pkt)))) self._recv(pkt) diff --git a/cocotb/monitors/solarflare.py b/cocotb/monitors/solarflare.py index 189b611f..f45e69f7 100644 --- a/cocotb/monitors/solarflare.py +++ b/cocotb/monitors/solarflare.py @@ -75,6 +75,9 @@ def _monitor_recv(self): vec = self.bus.data.value pkt += vec.buff[1:][::-1] if self.bus.endofpacket.value: + # Truncate the empty bits + if self.bus.empty.value.value: + pkt = pkt[:-self.bus.empty.value.value] self.log.info("Recieved a packet of %d bytes", len(pkt)) self.log.debug(hexdump(pkt)) self._recv(pkt) From 5781b3c2550a9299b11de1560cc3c93c6e469d58 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 20 Jun 2013 23:47:53 +0200 Subject: [PATCH 0062/2656] Update coroutines.rst --- documentation/source/coroutines.rst | 35 ++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/documentation/source/coroutines.rst b/documentation/source/coroutines.rst index b1ced294..08e87d22 100644 --- a/documentation/source/coroutines.rst +++ b/documentation/source/coroutines.rst @@ -1,6 +1,39 @@ Coroutines ========== -Information about coroutines +Testbenches built using Cocotb use coroutines. While the coroutine is executing +the simulation is paused. The coroutine use the :keyword:`yield` keyword to +pass control of execution back to the simulator and simulation time can advance +again. +Typically coroutines :keyword:`yield` a :py:class:`Trigger` object which +indicates to the simulator some which will cause the coroutine to be woken +when it occurs. For example: +.. codeblock:: python + + @cocotb.coroutine + def wait_10ns(): + cocotb.log.info("About to wait for 10ns") + yield TimerNS(10) + cocotb.log.info("Simulation time has advanced by 10 ns") + +Coroutines may also yield other coroutines: + +.. codeblock:: python + + @cocotb.coroutine + def wait_100ns(): + for i in range(10): + yield wait_10ns() + + +Coroutines may also yield a list of triggers to indicate that execution should +resume if *any* of them fires: + +.. codeblock:: python + + @cocotb.coroutine + def packet_with_timeout(monitor, timeout): + """Wait for a packet but timeout if nothing arrives""" + yield [TimerNS(timeout), monitor.recv()] From 8ea958291b877a32805fd9b711b855f9e09f4cb7 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Fri, 21 Jun 2013 13:49:20 +0100 Subject: [PATCH 0063/2656] Correct erroneous sphinx markup --- documentation/source/coroutines.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/source/coroutines.rst b/documentation/source/coroutines.rst index 08e87d22..0f377af8 100644 --- a/documentation/source/coroutines.rst +++ b/documentation/source/coroutines.rst @@ -10,7 +10,7 @@ Typically coroutines :keyword:`yield` a :py:class:`Trigger` object which indicates to the simulator some which will cause the coroutine to be woken when it occurs. For example: -.. codeblock:: python +.. code-block:: python @cocotb.coroutine def wait_10ns(): @@ -20,7 +20,7 @@ when it occurs. For example: Coroutines may also yield other coroutines: -.. codeblock:: python +.. code-block:: python @cocotb.coroutine def wait_100ns(): @@ -31,7 +31,7 @@ Coroutines may also yield other coroutines: Coroutines may also yield a list of triggers to indicate that execution should resume if *any* of them fires: -.. codeblock:: python +.. code-block:: python @cocotb.coroutine def packet_with_timeout(monitor, timeout): From 59dc0514f866162930159fb14bb22783362efba2 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 23 Jun 2013 10:17:44 +0200 Subject: [PATCH 0064/2656] Update introduction.rst --- documentation/source/introduction.rst | 42 +++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index bba8ca29..e2756414 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -1,4 +1,42 @@ +############ Introduction -============ +############ + +What is cocotb? +=============== + +**Cocotb** is a *coroutine* based *cosimulation* *testbench* environment for testing VHDL/Verilog RTL using Python. + +**Cocotb** is completely free, open source (under the `BSD License `_) and hosted on `GitHub `_. +Cocotb still requires a simulator to simulate the RTL. Simulators that have been tested and known to work with cocotb: +* Icarus +* Aldec Riviera-PRO +* Synopsys VCS + +See the :doc:`Simulator Support` page for full details of supported simulators and any gotchas. + +Why create cocotb? +================== + +Verification is the hardest part of realising a working design. Throughout the industry quite a broad spectrum of verification techniques exist, of varying degrees of complexity: + +# Waveform inspection (non self-checking testbenches) +# VHDL/Verilog testbenches +# File-based testbenches using another language to generate the test-vectors and check the output +# SystemVerilog or SystemC testbench +# UVM based testbench +# Custom PLI/DPI framework + + +How should I use cocotb? +======================== + +Utilise the simulator features +------------------------------ + +If you are paying for a simulator, make the most of all the fucntionlity it provides. + +* Code coverage +* Functional coverage +* Assersions -Brief introduction From b0b325278323e64d7857549c202c7d951c407a86 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 23 Jun 2013 10:20:37 +0200 Subject: [PATCH 0065/2656] Update introduction.rst --- documentation/source/introduction.rst | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index e2756414..13118d96 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -9,6 +9,7 @@ What is cocotb? **Cocotb** is completely free, open source (under the `BSD License `_) and hosted on `GitHub `_. Cocotb still requires a simulator to simulate the RTL. Simulators that have been tested and known to work with cocotb: + * Icarus * Aldec Riviera-PRO * Synopsys VCS @@ -20,12 +21,12 @@ Why create cocotb? Verification is the hardest part of realising a working design. Throughout the industry quite a broad spectrum of verification techniques exist, of varying degrees of complexity: -# Waveform inspection (non self-checking testbenches) -# VHDL/Verilog testbenches -# File-based testbenches using another language to generate the test-vectors and check the output -# SystemVerilog or SystemC testbench -# UVM based testbench -# Custom PLI/DPI framework +#. Waveform inspection (non self-checking testbenches) +#. VHDL/Verilog testbenches +#. File-based testbenches using another language to generate the test-vectors and check the output +#. SystemVerilog or SystemC testbench +#. UVM based testbench +#. Custom PLI/DPI framework How should I use cocotb? From 02a93549c0e496ec6aae1a44381ac20558d63a9d Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 23 Jun 2013 12:33:56 +0200 Subject: [PATCH 0066/2656] Update introduction.rst --- documentation/source/introduction.rst | 46 +++++++++++++++++++++------ 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index 13118d96..892ed1aa 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -8,25 +8,34 @@ What is cocotb? **Cocotb** is a *coroutine* based *cosimulation* *testbench* environment for testing VHDL/Verilog RTL using Python. **Cocotb** is completely free, open source (under the `BSD License `_) and hosted on `GitHub `_. -Cocotb still requires a simulator to simulate the RTL. Simulators that have been tested and known to work with cocotb: + +**Cocotb** still requires a simulator to simulate the RTL. Simulators that have been tested and known to work with cocotb: * Icarus * Aldec Riviera-PRO * Synopsys VCS -See the :doc:`Simulator Support` page for full details of supported simulators and any gotchas. +.. note:: + See the `Simulator Support`_ page for full details of supported simulators. + Why create cocotb? ================== -Verification is the hardest part of realising a working design. Throughout the industry quite a broad spectrum of verification techniques exist, of varying degrees of complexity: +Verification is the hardest part of realising a working design. Throughout the industry quite a broad spectrum of verification techniques exist, of increasing degrees of complexity: + +1. Waveform inspection (non self-checking testbenches) +2. VHDL/Verilog testbenches (self-checking testbenches) +3. File-based testbenches using another language to generate the test-vectors and check the output +4. SystemVerilog or SystemC testbench +5. "e" or UVM based testbench +6. Custom PLI/DPI framework + +The EDA tool industry has recognised the limitations of + + + -#. Waveform inspection (non self-checking testbenches) -#. VHDL/Verilog testbenches -#. File-based testbenches using another language to generate the test-vectors and check the output -#. SystemVerilog or SystemC testbench -#. UVM based testbench -#. Custom PLI/DPI framework How should I use cocotb? @@ -35,9 +44,26 @@ How should I use cocotb? Utilise the simulator features ------------------------------ -If you are paying for a simulator, make the most of all the fucntionlity it provides. +If you are paying for a simulator, make the most of all the fucntionlity it provides. Use metrics to asses how verification is progressing. It's surprisingly easy to write a test that doesn't actually test anything. * Code coverage * Functional coverage * Assersions + +Structure your testbench +------------------------ + +Drivers, Monitors, Scoreboards. + + +Make use of both directed and randomised tests +---------------------------------------------- + + +Use a regression system +----------------------- + +`Jenkins `_ + + From 165d198b029731deb4f9d0849f1e6605e1a99941 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 23 Jun 2013 13:03:27 +0200 Subject: [PATCH 0067/2656] Fold drivers/monitors/scoreboard into structure --- documentation/source/index.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/documentation/source/index.rst b/documentation/source/index.rst index 7b284975..6af43418 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -12,12 +12,12 @@ Contents: :maxdepth: 2 introduction + quickstart coroutines triggers - tests - drivers - monitors - scoreboards + structure + roadmap + Indices and tables From 658f31e48a3944f0d2a0658c37d55e294bb62960 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 23 Jun 2013 13:47:08 +0200 Subject: [PATCH 0068/2656] rant about UVM --- documentation/source/introduction.rst | 55 ++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index 892ed1aa..ed00f9ab 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -22,7 +22,7 @@ What is cocotb? Why create cocotb? ================== -Verification is the hardest part of realising a working design. Throughout the industry quite a broad spectrum of verification techniques exist, of increasing degrees of complexity: +Throughout the industry quite a broad spectrum of verification techniques exist, of increasing degrees of complexity: 1. Waveform inspection (non self-checking testbenches) 2. VHDL/Verilog testbenches (self-checking testbenches) @@ -31,11 +31,62 @@ Verification is the hardest part of realising a working design. Throughout the i 5. "e" or UVM based testbench 6. Custom PLI/DPI framework -The EDA tool industry has recognised the limitations of +Verification is the hardest part of realising a working design. +The EDA industry has invested heavily in helping us verify designs, +adding software contructs to verilog when creating SystemVerilog, +mandating that simulators need to implement constrain solvers, +converging on UVM as a standard methodology. Why is this inadequate? +Fundamentally the process of verification is writing software, specifically software to test a design, +where the design happens to be synthesisable into real hardware. If VHDL, Verilog or +SystemVerilog excelled as software languages would there not be examples of their use outside of EDA? +UVM +--- +UVM is a prime example of how the EDA industry solves a problem and the solution isn't pretty. While the ideas driving +UVM are valid (defining a common testbench structure, promoting code re-use, using constrained-random testing) and good, +the outcome is a step backwards for the following reasons: + +UVM is ugly +^^^^^^^^^^^ + +Given the rapid progress being made in software development and evolution of new languages, creating a framework that requires +so much boilerplate and relies so heavily on macros is actually impressive! This highlights why bashing SystemVerilog +to behave like a software language is midguided. + + +UVM is nich +^^^^^^^^^^^ + +We now have another niche within an already highly specialised area. Finding good UVM guys is difficult and expensive. +in the time it takes you to find 1 UVM developer I can hire 5 Python developers (and probabaly for the same total cost). + + +UVM is expensive +^^^^^^^^^^^^^^^^ + +I have to pay for a simulator. I then have to pay more to enable SystemVerilog verification features. I have to hire expensive people. This is good for EDA vendors but bad for innovation. + + +So EDA development is a bit backward and the tools suck, why is cocotb any better? +---------------------------------------------------------------------------------- + +Ranting aside, what is the idea behind cocotb any why is it different? + +Use the right tool for the job +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In cocotb VHDL/Verilog/SystemVerilog are only used for the synthesisable design. All verification is done using Python. + +Python is ideal for rapid development of complex systems, integrating multiple languages, +utilising the capabilites of the standard libraries and extensions like +`constraint solvers `_ and `packet parsing/generation `_ libraries. + + +Lower the overhead of creating a test +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ How should I use cocotb? From ef2e46a8fc61b0e5916e79da24b6db07454f65a5 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 23 Jun 2013 14:04:08 +0200 Subject: [PATCH 0069/2656] Update introduction.rst --- documentation/source/introduction.rst | 30 +++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index ed00f9ab..b8140c31 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -57,11 +57,11 @@ so much boilerplate and relies so heavily on macros is actually impressive! This to behave like a software language is midguided. -UVM is nich -^^^^^^^^^^^ +UVM is niche +^^^^^^^^^^^^ -We now have another niche within an already highly specialised area. Finding good UVM guys is difficult and expensive. -in the time it takes you to find 1 UVM developer I can hire 5 Python developers (and probabaly for the same total cost). +UVM is a niche within an already highly specialised area. Finding good UVM guys is difficult and expensive. +In the time it takes you to find 1 UVM developer I can hire 3 Python developers (and probabaly for the same total cost). UVM is expensive @@ -70,8 +70,8 @@ UVM is expensive I have to pay for a simulator. I then have to pay more to enable SystemVerilog verification features. I have to hire expensive people. This is good for EDA vendors but bad for innovation. -So EDA development is a bit backward and the tools suck, why is cocotb any better? ----------------------------------------------------------------------------------- +What makes cocotb any better? +----------------------------- Ranting aside, what is the idea behind cocotb any why is it different? @@ -85,9 +85,27 @@ utilising the capabilites of the standard libraries and extensions like `constraint solvers `_ and `packet parsing/generation `_ libraries. +Because Python is an interpreted language, I can actually edit my tests and re-run them without having to recompile the design or even exit the simulator GUI. + + Lower the overhead of creating a test ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Using cocotb the DUT hangs in "free space" in the simulator, you don't even have to create an instance and wire it up. +Tests themselves are very terse and easy to create. This lower overhead encourages creation of regression tests even for +sub-blocks where usually the overhead of writing a testbench is too onerous. + + +Open verification to a wider audience +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +ASIC/FPGA designs always involve some software development. +Often the hardware team is responsible for creating and verifying the RTL with little interaction +with the software team although as the boundaries blur the software teams are tending to get more involved (often with exclamations of `WTF?! `_). +Cocotb encourages more people to get more involved in RTL design by lowering the barrier to entry. Python is a common language accessible to a larger audience. + + + How should I use cocotb? ======================== From dbe5acb6d104ab70028a5a8232a0c70a7e114567 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 23 Jun 2013 14:13:22 +0200 Subject: [PATCH 0070/2656] Update introduction.rst --- documentation/source/introduction.rst | 33 +++++++++++---------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index b8140c31..8f78538f 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -42,41 +42,35 @@ where the design happens to be synthesisable into real hardware. If VHDL, Verilo SystemVerilog excelled as software languages would there not be examples of their use outside of EDA? -UVM ---- +Case study: UVM +--------------- UVM is a prime example of how the EDA industry solves a problem and the solution isn't pretty. While the ideas driving -UVM are valid (defining a common testbench structure, promoting code re-use, using constrained-random testing) and good, +UVM are valid and good (defining a common testbench structure, promoting code re-use, using constrained-random testing), the outcome is a step backwards for the following reasons: -UVM is ugly -^^^^^^^^^^^ +**UVM is ugly** Given the rapid progress being made in software development and evolution of new languages, creating a framework that requires -so much boilerplate and relies so heavily on macros is actually impressive! This highlights why bashing SystemVerilog -to behave like a software language is midguided. +so much boilerplate and relies so heavily on macros is actually impressive! - -UVM is niche -^^^^^^^^^^^^ +**UVM is niche** UVM is a niche within an already highly specialised area. Finding good UVM guys is difficult and expensive. -In the time it takes you to find 1 UVM developer I can hire 3 Python developers (and probabaly for the same total cost). - -UVM is expensive -^^^^^^^^^^^^^^^^ +* UVM is expensive* I have to pay for a simulator. I then have to pay more to enable SystemVerilog verification features. I have to hire expensive people. This is good for EDA vendors but bad for innovation. -What makes cocotb any better? ------------------------------ +How is cocotb different? +------------------------ -Ranting aside, what is the idea behind cocotb any why is it different? +Cocotb encourages the same philosophy of design re-use and randomised testing as UVM, however is implemented in Python rather than SystemVerilog. -Use the right tool for the job -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The right tool for the job +^^^^^^^^^^^^^^^^^^^^^^^^^^ In cocotb VHDL/Verilog/SystemVerilog are only used for the synthesisable design. All verification is done using Python. @@ -84,7 +78,6 @@ Python is ideal for rapid development of complex systems, integrating multiple l utilising the capabilites of the standard libraries and extensions like `constraint solvers `_ and `packet parsing/generation `_ libraries. - Because Python is an interpreted language, I can actually edit my tests and re-run them without having to recompile the design or even exit the simulator GUI. From 5d1f377ae98837ace488efa0ebf7054f287e84fc Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 23 Jun 2013 14:15:36 +0200 Subject: [PATCH 0071/2656] Update introduction.rst --- documentation/source/introduction.rst | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index 8f78538f..84f2ac73 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -22,15 +22,6 @@ What is cocotb? Why create cocotb? ================== -Throughout the industry quite a broad spectrum of verification techniques exist, of increasing degrees of complexity: - -1. Waveform inspection (non self-checking testbenches) -2. VHDL/Verilog testbenches (self-checking testbenches) -3. File-based testbenches using another language to generate the test-vectors and check the output -4. SystemVerilog or SystemC testbench -5. "e" or UVM based testbench -6. Custom PLI/DPI framework - Verification is the hardest part of realising a working design. The EDA industry has invested heavily in helping us verify designs, adding software contructs to verilog when creating SystemVerilog, @@ -58,7 +49,7 @@ so much boilerplate and relies so heavily on macros is actually impressive! UVM is a niche within an already highly specialised area. Finding good UVM guys is difficult and expensive. -* UVM is expensive* +**UVM is expensive** I have to pay for a simulator. I then have to pay more to enable SystemVerilog verification features. I have to hire expensive people. This is good for EDA vendors but bad for innovation. From 2e2c5a6e7ccd263c7ac5f1d7f5d5f9868af0bcf4 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 23 Jun 2013 13:21:29 +0100 Subject: [PATCH 0072/2656] create quickstart.rst --- documentation/source/quickstart.rst | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 documentation/source/quickstart.rst diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst new file mode 100644 index 00000000..e69de29b From 2358cc831921f445daba3dd1f773a428c6dc3c9e Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 23 Jun 2013 14:54:57 +0200 Subject: [PATCH 0073/2656] Update quickstart.rst --- documentation/source/quickstart.rst | 41 +++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index e69de29b..99f22170 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -0,0 +1,41 @@ +################ +Quickstart Guide +################ + +A typical cocotb testbench requires no additional RTL code. The Design Under Test (DUT) is instantiated as the toplevel in the simulator without any wrapper code. Cocotb drives stimulus onto the inputs to the DUT (or further down the hierarchy) and monitors the outputs directly from Python. + + +Assigning values to signals +--------------------------- + +The Python __le__ method is overloaded to provide a convenient (and familiar to RTL developers) of assigning signals: + +.. code-block:: python + # Assign the value 12 + dut.input <= 12 + + +Reading values from signals +--------------------------- + + + +Parallel and sequential execution of coroutines +----------------------------------------------- + + + +Creating a test +--------------- + +.. code-block:: python + import cocotb + from cocotb.triggers import Timer + + @cocotb.test(timeout=None) + def my_first_test(dut): + + # drive the reset signal on the dut + dut.reset_n <= 0 + yield Timer(12345) + dut.reset_n <= 1 From 02a78aac1dd268e87836ed594b6e166e7387790e Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 23 Jun 2013 15:06:10 +0200 Subject: [PATCH 0074/2656] Update introduction.rst --- documentation/source/introduction.rst | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index 84f2ac73..cdc7f06c 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -40,18 +40,12 @@ UVM is a prime example of how the EDA industry solves a problem and the solution UVM are valid and good (defining a common testbench structure, promoting code re-use, using constrained-random testing), the outcome is a step backwards for the following reasons: -**UVM is ugly** - -Given the rapid progress being made in software development and evolution of new languages, creating a framework that requires +**UVM is ugly:** Given the rapid progress being made in software development and evolution of new languages, creating a framework that requires so much boilerplate and relies so heavily on macros is actually impressive! -**UVM is niche** - -UVM is a niche within an already highly specialised area. Finding good UVM guys is difficult and expensive. - -**UVM is expensive** +**UVM is niche:** UVM is a niche within an already highly specialised area. Finding good UVM guys is difficult and expensive. -I have to pay for a simulator. I then have to pay more to enable SystemVerilog verification features. I have to hire expensive people. This is good for EDA vendors but bad for innovation. +**UVM is expensive:** I have to pay for a simulator. I then have to pay more to enable SystemVerilog verification features. I have to hire expensive people. This is good for EDA vendors but bad for innovation. How is cocotb different? From 1c9a465b16d5db7a51bcf875786356e58f13f89b Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 23 Jun 2013 16:17:55 +0200 Subject: [PATCH 0075/2656] Update quickstart.rst --- documentation/source/quickstart.rst | 32 ++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index 99f22170..60cc0347 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -2,7 +2,28 @@ Quickstart Guide ################ -A typical cocotb testbench requires no additional RTL code. The Design Under Test (DUT) is instantiated as the toplevel in the simulator without any wrapper code. Cocotb drives stimulus onto the inputs to the DUT (or further down the hierarchy) and monitors the outputs directly from Python. +A typical cocotb testbench requires no additional RTL code. +The Design Under Test (DUT) is instantiated as the toplevel in the simulator without any wrapper code. +Cocotb drives stimulus onto the inputs to the DUT (or further down the hierarchy) and monitors the outputs directly from Python. + + +Accessing the design +-------------------- + +When cocotb initialises it finds the top-level instantiation in the simulator and creates a handle called **dut**. +Top-level signals can be accessed using the "dot" notation used for accessing object attributes in Python: + +.. code-block:: python + + # Get a reference to the "clk" signal on the top-level + clk = dut.clk + +The same notation can be used to access signals inside the design: + +.. code-block:: python + + # Get a regerence to a register "count" in a sub-block "inst_sub_block" + count = dut.inst_sub_block.count Assigning values to signals @@ -11,8 +32,12 @@ Assigning values to signals The Python __le__ method is overloaded to provide a convenient (and familiar to RTL developers) of assigning signals: .. code-block:: python - # Assign the value 12 - dut.input <= 12 + + # Assign the value 12 to signal "input_signal" on DUT + dut.input_signal.value = 12 + + # Use the overloaded less than or equal to operator + dut.input_signal <= 12 Reading values from signals @@ -29,6 +54,7 @@ Creating a test --------------- .. code-block:: python + import cocotb from cocotb.triggers import Timer From 4054c508d47cd5ab645a283dfce92e916d26a33a Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 23 Jun 2013 16:30:07 +0200 Subject: [PATCH 0076/2656] Added join shortcut to scheduler.add, improved comments --- cocotb/__init__.py | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 7299592f..9f7ab99e 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -24,8 +24,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' """ - TODO: - Seed random from environment variable if provided +Cocotb is a coroutine, cosimulation framework for writing testbenches in Python. + +See http://cocotb.readthedocs.org for full documentation """ import os import sys @@ -33,14 +34,30 @@ import threading from functools import wraps -import ANSI +import simulator + +import cocotb.ANSI import cocotb.handle from cocotb.scheduler import Scheduler -import simulator + +# Things we want in the cocotb namespace +from cocotb.decorators import test + +# Singleton scheduler instance +# NB this cheekily ensures a singleton since we're replacing the reference +# so that cocotb.scheduler gives you the singleton instance and not the +# scheduler package +scheduler = Scheduler() + +# To save typing provide an alias to scheduler.add +join = scheduler.add + +# Top level logger object log = logging.getLogger('cocotb') log.setLevel(logging.INFO) + class TestFailed(Exception): pass @@ -84,9 +101,6 @@ def format(self, record): # FIXME is this really required? _rlock = threading.RLock() -# Singleton scheduler instance -# TODO: Ensure we only ever have a single scheduler -scheduler = Scheduler() def _initialise_testbench(root_handle): """ @@ -125,5 +139,3 @@ def my_import(name): _rlock.release() return True -from cocotb.decorators import test - From b022f97dc49775d9aceb18ee666209bfd4806293 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 23 Jun 2013 16:31:15 +0200 Subject: [PATCH 0077/2656] Cleanup: correct idiocy of previous commit --- cocotb/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 9f7ab99e..54e0565c 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -51,7 +51,7 @@ scheduler = Scheduler() # To save typing provide an alias to scheduler.add -join = scheduler.add +fork = scheduler.add # Top level logger object log = logging.getLogger('cocotb') From c89540551bc780e7bc22711aecdbf4822877b3c2 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 23 Jun 2013 16:44:34 +0200 Subject: [PATCH 0078/2656] Update quickstart.rst --- documentation/source/quickstart.rst | 40 +++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index 60cc0347..b3da3bb2 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -11,25 +11,22 @@ Accessing the design -------------------- When cocotb initialises it finds the top-level instantiation in the simulator and creates a handle called **dut**. -Top-level signals can be accessed using the "dot" notation used for accessing object attributes in Python: +Top-level signals can be accessed using the "dot" notation used for accessing object attributes in Python. +The same mechanism can be used to access signals inside the design. .. code-block:: python # Get a reference to the "clk" signal on the top-level clk = dut.clk -The same notation can be used to access signals inside the design: - -.. code-block:: python - - # Get a regerence to a register "count" in a sub-block "inst_sub_block" - count = dut.inst_sub_block.count + # Get a regerence to a register "count" in a sub-block "inst_sub_block" + count = dut.inst_sub_block.count Assigning values to signals --------------------------- -The Python __le__ method is overloaded to provide a convenient (and familiar to RTL developers) of assigning signals: +Values can be assigned to signals using either the .value property of a handle object or using the overloaded less than or equal to operator .. code-block:: python @@ -48,6 +45,33 @@ Reading values from signals Parallel and sequential execution of coroutines ----------------------------------------------- +.. code-block:: python + + @cocotb.coroutine + def reset_dut(reset_n, duration): + reset_n <= 0 + yield Timer(duration) + reset_n <= 1 + reset_n.log.debug("Reset complete") + + @cocotb.test() + def parallel_example(dut): + reset_n = dut.reset + + # This will call reset_dut sequentially + # Execution will block until reset_dut has completed + yield reset_dut(reset_n, 500) + dut.log.debug("After reset") + + # Call reset_dut in parallel with this coroutine + reset_thread = cocotb.fork(reset_dut(reset_n, 500) + + yield Timer(250) + dut.log.debug("During reset (reset_n = %s)" % reset_n.value) + + # Wait for the other thread to complete + yield reset_thread.join() + dut.log.debug("After reset") Creating a test From 9486c32f40282477cf9c385b2894831fdcb4e7b2 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 23 Jun 2013 17:07:34 +0200 Subject: [PATCH 0079/2656] Update quickstart.rst --- documentation/source/quickstart.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index b3da3bb2..e5ec1516 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -40,6 +40,19 @@ Values can be assigned to signals using either the .value property of a handle o Reading values from signals --------------------------- +Accessing the .value property of a handle object will return a :class:`BinaryValue` object. Any unresolved bits are preserved and can be accessed using the binstr attribute, or a resolved integer value can be accessed using the value attribute. + +.. code-block:: python + + >>> # Read a value back from the dut + >>> count = dut.counter.value + >>> + >>> print count.binstr + 1X1010 + >>> # Resolve the value (X or Z treated as 0) + >>> print count.value + 42 + Parallel and sequential execution of coroutines From 7302c6aea75a108c393f833b39a618ac173a8230 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 23 Jun 2013 16:12:38 +0100 Subject: [PATCH 0080/2656] Shuffle rst files --- documentation/source/drivers.rst | 5 ----- documentation/source/monitors.rst | 5 ----- documentation/source/roadmap.rst | 0 documentation/source/scoreboards.rst | 5 ----- documentation/source/structure.rst | 0 5 files changed, 15 deletions(-) delete mode 100644 documentation/source/drivers.rst delete mode 100644 documentation/source/monitors.rst create mode 100644 documentation/source/roadmap.rst delete mode 100644 documentation/source/scoreboards.rst create mode 100644 documentation/source/structure.rst diff --git a/documentation/source/drivers.rst b/documentation/source/drivers.rst deleted file mode 100644 index 181a5357..00000000 --- a/documentation/source/drivers.rst +++ /dev/null @@ -1,5 +0,0 @@ -Drivers -======= - -Drivers abstract bus functionality - diff --git a/documentation/source/monitors.rst b/documentation/source/monitors.rst deleted file mode 100644 index a99a22ec..00000000 --- a/documentation/source/monitors.rst +++ /dev/null @@ -1,5 +0,0 @@ -Monitors -======== - -Monitors abstract bus functionlity for reconstructing transactions - diff --git a/documentation/source/roadmap.rst b/documentation/source/roadmap.rst new file mode 100644 index 00000000..e69de29b diff --git a/documentation/source/scoreboards.rst b/documentation/source/scoreboards.rst deleted file mode 100644 index 046fdc40..00000000 --- a/documentation/source/scoreboards.rst +++ /dev/null @@ -1,5 +0,0 @@ -Scoreboards -=========== - -Placeholder - diff --git a/documentation/source/structure.rst b/documentation/source/structure.rst new file mode 100644 index 00000000..e69de29b From 0b62595c51dfbb7c660d6a143889b7aaefa687dd Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 23 Jun 2013 18:00:08 +0200 Subject: [PATCH 0081/2656] Update introduction.rst --- documentation/source/introduction.rst | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index cdc7f06c..2e799dc2 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -38,32 +38,34 @@ Case study: UVM UVM is a prime example of how the EDA industry solves a problem and the solution isn't pretty. While the ideas driving UVM are valid and good (defining a common testbench structure, promoting code re-use, using constrained-random testing), -the outcome is a step backwards for the following reasons: +the outcome is a not the much needed giant leap forward for the following reasons: -**UVM is ugly:** Given the rapid progress being made in software development and evolution of new languages, creating a framework that requires +* **UVM is ugly** Given the rapid progress being made in software development and evolution of new languages, creating a framework that requires so much boilerplate and relies so heavily on macros is actually impressive! - -**UVM is niche:** UVM is a niche within an already highly specialised area. Finding good UVM guys is difficult and expensive. - -**UVM is expensive:** I have to pay for a simulator. I then have to pay more to enable SystemVerilog verification features. I have to hire expensive people. This is good for EDA vendors but bad for innovation. +* **UVM is niche** UVM is a niche within an already highly specialised area. Finding good UVM guys is difficult and expensive. +* **UVM is expensive** I have to pay for a simulator. I then have to pay more to enable SystemVerilog verification features. I have to hire expensive people. This is good for EDA vendors but bad for innovation. How is cocotb different? ------------------------ -Cocotb encourages the same philosophy of design re-use and randomised testing as UVM, however is implemented in Python rather than SystemVerilog. +Cocotb encourages the same philosophy of design re-use and randomised testing as UVM, however is implemented in +Python rather than SystemVerilog. The right tool for the job ^^^^^^^^^^^^^^^^^^^^^^^^^^ -In cocotb VHDL/Verilog/SystemVerilog are only used for the synthesisable design. All verification is done using Python. +In cocotb VHDL/Verilog/SystemVerilog are only used for the synthesisable design. All verification is done using Python which is isideal for rapid development of complex systems and integrating multiple languages. -Python is ideal for rapid development of complex systems, integrating multiple languages, -utilising the capabilites of the standard libraries and extensions like -`constraint solvers `_ and `packet parsing/generation `_ libraries. +* It's easy to interface to other languages from Python +* Python has a huge **library** of existing code like `constraint solvers `_ and `packet parsing/generation `_ libraries. +* Python is **interpreted**. Tests can be edited and re-run them without having to recompile the design or even exit the simulator GUI. +* Writing Python is **fast**, it's *easy to understand* and *everybody* knows it. -Because Python is an interpreted language, I can actually edit my tests and re-run them without having to recompile the design or even exit the simulator GUI. +.. note:: + + That last statement me be a slight exaggeration, however a brief peruse of `Google Trends `_ suggests that compared to UVM, everybody knows Python. Lower the overhead of creating a test @@ -84,7 +86,6 @@ Cocotb encourages more people to get more involved in RTL design by lowering the - How should I use cocotb? ======================== From d13af5ba995d08925a10adce4fa99bb5b4982b48 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 23 Jun 2013 18:04:56 +0200 Subject: [PATCH 0082/2656] Update roadmap.rst --- documentation/source/roadmap.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/documentation/source/roadmap.rst b/documentation/source/roadmap.rst index e69de29b..a1668681 100644 --- a/documentation/source/roadmap.rst +++ b/documentation/source/roadmap.rst @@ -0,0 +1,11 @@ +####### +Roadmap +####### + +Cocotb is in active development. + + +* `Jenkins `_ itegration + +** Test auto-discovery +** XML output format From 76cce17421193395d9ec0127b1073d1e7de16114 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 23 Jun 2013 18:07:26 +0200 Subject: [PATCH 0083/2656] Update introduction.rst --- documentation/source/introduction.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index 2e799dc2..86985ab7 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -40,8 +40,7 @@ UVM is a prime example of how the EDA industry solves a problem and the solution UVM are valid and good (defining a common testbench structure, promoting code re-use, using constrained-random testing), the outcome is a not the much needed giant leap forward for the following reasons: -* **UVM is ugly** Given the rapid progress being made in software development and evolution of new languages, creating a framework that requires -so much boilerplate and relies so heavily on macros is actually impressive! +* **UVM is ugly** Given the rapid progress being made in software development and evolution of new languages, creating a framework that requires so much boilerplate and relies so heavily on macros is actually impressive! * **UVM is niche** UVM is a niche within an already highly specialised area. Finding good UVM guys is difficult and expensive. * **UVM is expensive** I have to pay for a simulator. I then have to pay more to enable SystemVerilog verification features. I have to hire expensive people. This is good for EDA vendors but bad for innovation. @@ -58,8 +57,8 @@ The right tool for the job In cocotb VHDL/Verilog/SystemVerilog are only used for the synthesisable design. All verification is done using Python which is isideal for rapid development of complex systems and integrating multiple languages. -* It's easy to interface to other languages from Python -* Python has a huge **library** of existing code like `constraint solvers `_ and `packet parsing/generation `_ libraries. +* It's **easy** to interface to other languages from Python +* Python has a huge library of existing code to **re-use** like `constraint solvers `_ and `packet parsing/generation `_ libraries. * Python is **interpreted**. Tests can be edited and re-run them without having to recompile the design or even exit the simulator GUI. * Writing Python is **fast**, it's *easy to understand* and *everybody* knows it. From d00b59420c0952fc2b31b6a0898bbe67d3c9f954 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 23 Jun 2013 18:08:07 +0200 Subject: [PATCH 0084/2656] Update roadmap.rst --- documentation/source/roadmap.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/source/roadmap.rst b/documentation/source/roadmap.rst index a1668681..bd10b513 100644 --- a/documentation/source/roadmap.rst +++ b/documentation/source/roadmap.rst @@ -7,5 +7,5 @@ Cocotb is in active development. * `Jenkins `_ itegration -** Test auto-discovery -** XML output format + * Test auto-discovery + * XML output format From 391edfc040322bbf7f79cbeeb40368274164f4f7 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 24 Jun 2013 16:36:57 +0100 Subject: [PATCH 0085/2656] Make base class SimTest execute the command --- bin/cocotbenv.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/bin/cocotbenv.py b/bin/cocotbenv.py index 7c16543c..72356042 100755 --- a/bin/cocotbenv.py +++ b/bin/cocotbenv.py @@ -37,8 +37,13 @@ def __init__(self, name=None, sdebug=False): self._name = name; self._sdebug = sdebug - def execute(self): + def execute(self, cmd): print("Running cocotb against %s simulator" % self._name) + print(cmd) + try: + call(cmd, shell=True) + except KeyboardInterrupt: + print("Closing Test Bench") class SimIcarus(SimType): def __init__(self, name=None, sdebug=False): @@ -46,7 +51,6 @@ def __init__(self, name=None, sdebug=False): self._base_cmd = ' vvp -m cocotb' def execute(self, py_path, lib_path, module, function, finput): - SimType.execute(self) cmd = 'PYTHONPATH=' + py_path cmd = cmd + ' LD_LIBRARY_PATH=' + lib_path cmd = cmd + ' MODULE=' + module @@ -56,15 +60,14 @@ def execute(self, py_path, lib_path, module, function, finput): cmd = cmd + self._base_cmd cmd = cmd + ' -M ' + lib_path cmd = cmd + ' ' + finput - print(cmd) - call(cmd, shell=True) + + SimType.execute(self, cmd) class SimSfsim(SimType): def __init__(self, name=None, sdebug=False): SimType.__init__(self, "SolarFlare Model", sdebug) def execute(self, py_path, lib_path, module, function, finput): - SimType.execute(self) cmd = 'PYTHONPATH=' + py_path cmd = cmd + ' LD_LIBRARY_PATH=' + lib_path cmd = cmd + ' MODULE=' + module @@ -73,8 +76,8 @@ def execute(self, py_path, lib_path, module, function, finput): cmd = cmd + ' gdb --args' cmd = cmd + ' ' + finput cmd = cmd + ' -l ' + lib_path + '/libcocotb.so' - print(cmd) - call(cmd, shell=True) + + SimType.execute(self, cmd) def main(): From 71e922b6a6b20141d579882b1a89d871f88120d9 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 24 Jun 2013 16:41:08 +0100 Subject: [PATCH 0086/2656] Push debug options down to common makefile --- lib/embed/Makefile | 2 +- lib/gpi/Makefile | 2 +- lib/gpi/gpi_logging.h | 2 -- lib/simulator/Makefile | 2 +- lib/vpi_shim/Makefile | 2 +- makefiles/Makefile.inc | 3 +++ 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/embed/Makefile b/lib/embed/Makefile index bd970ba5..69794385 100644 --- a/lib/embed/Makefile +++ b/lib/embed/Makefile @@ -30,7 +30,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ \ -I/mnt/sources/tools/icarus/include/iverilog -I../gpi -GCC_ARGS := -Werror -g +GCC_ARGS += LIBS := -lgpi -lpython2.7 $(PYLIBS) LD_PATH := -L$(LIB_DIR) diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index 9916badd..16787362 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -30,7 +30,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ -GCC_ARGS := -Werror -g +GCC_ARGS += SRCS := gpi_logging.c OBJS := $(SRCS:%.c=$(OBJ_DIR)/%.o) diff --git a/lib/gpi/gpi_logging.h b/lib/gpi/gpi_logging.h index db93a2a4..09ffd355 100644 --- a/lib/gpi/gpi_logging.h +++ b/lib/gpi/gpi_logging.h @@ -40,8 +40,6 @@ gpi_logging.h # define EXTERN_C_END #endif -#define DEBUG - EXTERN_C_START enum gpi_log_levels { diff --git a/lib/simulator/Makefile b/lib/simulator/Makefile index fb12c3aa..9126f14a 100644 --- a/lib/simulator/Makefile +++ b/lib/simulator/Makefile @@ -28,7 +28,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ -I../gpi -GCC_ARGS := -Werror -g +GCC_ARGS += LIBS := -lgpi -lpython2.7 $(PYLIBS) LD_PATH := -L$(LIB_DIR) diff --git a/lib/vpi_shim/Makefile b/lib/vpi_shim/Makefile index 955fa29f..bc0dafac 100644 --- a/lib/vpi_shim/Makefile +++ b/lib/vpi_shim/Makefile @@ -28,7 +28,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ -I../gpi/ $(VPI_INCLUDE_PATH) -GCC_ARGS := -Werror -g +GCC_ARGS += LIBS := -lgpilog LD_PATH := -L$(LIB_DIR) diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index dc406bd4..81f86caf 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -42,6 +42,9 @@ export LIB_DIR=$(BUILD_DIR)/libs/$(ARCH) include $(SIM_ROOT)/makefiles/Makefile.pylib +# Base GCC flags +GCC_ARGS := -Werror -g -DDEBUG + # Disable some inbuild rules %: %.c %: %.o From 9a2b16be4b81ed626a16491be5970f72b2afe029 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 24 Jun 2013 19:17:31 +0100 Subject: [PATCH 0087/2656] Fix up routine for optional access via non VPI compliant interface --- lib/embed/gpi_embed.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 706ac5b1..e32c6fd3 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -210,10 +210,13 @@ void (*vlog_startup_routines[])() = { // For non-VPI compliant applications that cannot find vlog_startup_routines symbol void vlog_startup_routines_bootstrap() { - unsigned int i; - for (i = 0; vlog_startup_routines[i]; i++) { - vlog_startup_routines[i](); - } + void (*routine)(void); + int i; + routine = vlog_startup_routines[0]; + for (i = 0, routine = vlog_startup_routines[i]; + routine; + routine = vlog_startup_routines[++i]) { + printf("routine is %p\n", routine); + routine(); + } } - - From 9ef3f62352e18008fa71ee029489f41d169ab064 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 24 Jun 2013 19:18:47 +0100 Subject: [PATCH 0088/2656] Issue #11: Fixup makefile to have correct options for dve and sim mode --- makefiles/Makefile.vcs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/makefiles/Makefile.vcs b/makefiles/Makefile.vcs index 6fc1f675..bc59c08a 100644 --- a/makefiles/Makefile.vcs +++ b/makefiles/Makefile.vcs @@ -45,7 +45,11 @@ pli.tab : .PHONY: sim sim: $(OBJ_DIR) $(VERILOG_SOURCES) pli.tab PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ - vcs -R -debug_all +acc+1 -diag vpi +vpi -P pli.tab -sverilog $(EXTRA_ARGS) -load cocotb.vpi $(VERILOG_SOURCES) + vcs -R +acc+1 +vpi -P pli.tab -sverilog $(EXTRA_ARGS) -debug -load cocotb.vpi $(VERILOG_SOURCES) + +dve: $(OBJ_DIR) $(VERILOG_SOURCES) pli.tab + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ + vcs -R +acc+1 +vpi+1+assertion -P pli.tab -sverilog $(EXTRA_ARGS) -gui -load cocotb.vpi $(VERILOG_SOURCES) clean: -@rm -rf $(OBJ_DIR) -@rm -f pli.tab From f3cb9511ddfed28f43683e2f5c7b4edf7c0d4aa9 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 25 Jun 2013 09:24:22 +0100 Subject: [PATCH 0089/2656] Improved debug for coroutines that aren't coroutines --- cocotb/decorators.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index ab5b4379..cf0b6b2f 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -102,8 +102,20 @@ def next(self): except StopIteration: raise CoroutineComplete(callback=self._finished_cb) + def _pre_send(self): + """Provide some useful debug if our coroutine isn't a real coroutine + + NB better than catching AttributeError on _coro.send since then we + can't distinguish between an AttributeError in the coroutine itself + """ + if not hasattr(self._coro, "send"): + self.log.error("%s isn't a value coroutine! Did you use the yield keyword?" + % self.__name__) + raise CoroutineComplete(callback=self._finished_cb) + def send(self, value): """FIXME: problem here is that we don't let the call stack unwind...""" + self._pre_send() try: return self._coro.send(value) except StopIteration: @@ -167,6 +179,7 @@ def _wrapped_test(*args, **kwargs): def send(self, value): """FIXME: problem here is that we don't let the call stack unwind...""" + self._pre_send() if not self.started: self.log.info("Starting test: \"%s\"\nDescription: %s" % (self.__name__, self._func.__doc__)) self.started = True From 4629934fa0964dd350984863715361d90681dc85 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 25 Jun 2013 09:27:24 +0100 Subject: [PATCH 0090/2656] Tidy up --- cocotb/scheduler.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 3f0e6434..ceaeff5e 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -34,13 +34,10 @@ import os import logging +import simulator import cocotb import cocotb.decorators -import simulator as simulator -from cocotb import triggers -from cocotb.handle import SimHandle -from cocotb.decorators import coroutine -from cocotb.triggers import Timer, ReadOnly, NextTimeStep, ReadWrite +from cocotb.triggers import Trigger, Timer, ReadOnly, NextTimeStep, ReadWrite class Scheduler(object): @@ -115,6 +112,15 @@ def add(self, coroutine): def schedule(self, coroutine, trigger=None): + """ + Schedule a coroutine by calling the send method + + Args: + coroutine (cocotb.decorators.coroutine): The coroutine to schedule + + trigger (cocotb.triggers.Trigger): The trigger that caused this + coroutine to be scheduled + """ coroutine.log.debug("Scheduling (%s)" % str(trigger)) try: result = coroutine.send(trigger) @@ -140,7 +146,7 @@ def schedule(self, coroutine, trigger=None): simulator.stop_simulator(self) return - if isinstance(result, triggers.Trigger): + if isinstance(result, Trigger): self._add_trigger(result, coroutine) elif isinstance(result, cocotb.decorators.coroutine): self.log.debug("Scheduling nested co-routine: %s" % result.__name__) @@ -154,7 +160,7 @@ def schedule(self, coroutine, trigger=None): self.log.warning("Unable to schedule coroutine since it's returning stuff %s" % repr(result)) coroutine.log.debug("Finished sheduling coroutine (%s)" % str(trigger)) - @coroutine + @cocotb.decorators.coroutine def move_to_rw(self): yield ReadWrite() self._readwrite = None From ecd8b69a3266b780aee2c39bbe7524b5c2d3a4d4 Mon Sep 17 00:00:00 2001 From: stuarthodgson Date: Tue, 25 Jun 2013 10:58:32 +0200 Subject: [PATCH 0091/2656] Update coroutines.rst --- documentation/source/coroutines.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/coroutines.rst b/documentation/source/coroutines.rst index 0f377af8..fe987d4d 100644 --- a/documentation/source/coroutines.rst +++ b/documentation/source/coroutines.rst @@ -7,7 +7,7 @@ pass control of execution back to the simulator and simulation time can advance again. Typically coroutines :keyword:`yield` a :py:class:`Trigger` object which -indicates to the simulator some which will cause the coroutine to be woken +indicates to the simulator some event which will cause the coroutine to be woken when it occurs. For example: .. code-block:: python From 8e6baf286fb55177508e499b28eef0f530960b45 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 25 Jun 2013 12:17:18 +0100 Subject: [PATCH 0092/2656] Remove extraneous debug --- lib/embed/gpi_embed.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index e32c6fd3..efc69477 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -216,7 +216,6 @@ void vlog_startup_routines_bootstrap() { for (i = 0, routine = vlog_startup_routines[i]; routine; routine = vlog_startup_routines[++i]) { - printf("routine is %p\n", routine); routine(); } } From 837c87f1911503a832447c82c213a7e9eb2edb56 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 25 Jun 2013 21:54:25 +0100 Subject: [PATCH 0093/2656] Issue #12: Disable filtering for now --- lib/gpi/Makefile | 2 +- lib/gpi/gpi_logging.c | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index 16787362..08991a81 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -30,7 +30,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ -GCC_ARGS += +GCC_ARGS += -DNO_FILTER SRCS := gpi_logging.c OBJS := $(SRCS:%.c=$(OBJ_DIR)/%.o) diff --git a/lib/gpi/gpi_logging.c b/lib/gpi/gpi_logging.c index 25de1457..6cbe223c 100644 --- a/lib/gpi/gpi_logging.c +++ b/lib/gpi/gpi_logging.c @@ -98,19 +98,27 @@ void gpi_log(const char *name, long level, const char *pathname, const char *fun Py_DECREF(pLogRecord); // Filter here +#ifdef NO_FILTER PyObject *pShouldFilter = PyObject_CallObject(pLogFilter, pLogArgs); if (pShouldFilter == Py_True) { +#endif PyObject *pLogResult = PyObject_CallObject(pLogHandler, pLogArgs); Py_DECREF(pLogResult); +#ifdef NO_FILTER } Py_DECREF(pShouldFilter); +#endif Py_DECREF(pLogArgs); } else { + PyGILState_Release(gstate); + goto clog; + fprintf(stderr, "ERROR: Unable to log into python - logging functions aren't callable\n"); fprintf(stderr, "%s", msg); fprintf(stderr, "\n"); + } // Matching call to release GIL @@ -118,6 +126,7 @@ void gpi_log(const char *name, long level, const char *pathname, const char *fun // Python logging not available, just dump to stdout (No filtering) } else { +clog: fprintf(stdout, " -.--ns"); fprintf(stdout, " %2ld", level); // FIXME: Print msglevel DEBUG INFO etc. fprintf(stdout, "%16s", name); From bc057e08f56b53daa12be0f66aad1fb6874deef7 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 25 Jun 2013 21:55:28 +0100 Subject: [PATCH 0094/2656] Tidy up source indent --- lib/embed/gpi_embed.c | 29 ++++++++++++++++------------- lib/vpi_shim/gpi_vpi.c | 17 ++++++++--------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index efc69477..1da496e9 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -185,9 +185,12 @@ void register_initial_callback() } -// NB We don't attempt to log here since the world has fallen apart int handle_sim_end(void *gpi_cb_data) { + FENTER + sim_finish_cb = NULL; + LOG_WARN("Closing down cocotb at simulator request!"); + FEXIT return(0); } @@ -201,21 +204,21 @@ void register_final_callback() // FIXME this is VPI specific, should be in gpi_vpi.c void (*vlog_startup_routines[])() = { - init_python, - register_initial_callback, - register_final_callback, - 0 + init_python, + register_initial_callback, + register_final_callback, + 0 }; // For non-VPI compliant applications that cannot find vlog_startup_routines symbol void vlog_startup_routines_bootstrap() { - void (*routine)(void); - int i; - routine = vlog_startup_routines[0]; - for (i = 0, routine = vlog_startup_routines[i]; - routine; - routine = vlog_startup_routines[++i]) { - routine(); - } + void (*routine)(void); + int i; + routine = vlog_startup_routines[0]; + for (i = 0, routine = vlog_startup_routines[i]; + routine; + routine = vlog_startup_routines[++i]) { + routine(); + } } diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 717aa271..56a47129 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -85,12 +85,9 @@ void gpi_sim_end() // double gpi_get_sim_time() void gpi_get_sim_time(uint32_t *high, uint32_t *low) { -// FENTERD s_vpi_time vpi_time_s; vpi_time_s.type = vpiSimTime;//vpiScaledRealTime; //vpiSimTime; vpi_get_time(NULL, &vpi_time_s); -// FEXIT -// return vpi_time_s.real; *high = vpi_time_s.high; *low = vpi_time_s.low; } @@ -105,9 +102,10 @@ void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) value_p->value.integer = value; value_p->format = vpiIntVal; -// * vpiNoDelay -- Set the value immediately. The p_vpi_time parameter -// * may be NULL, in this case. This is like a blocking assignment -// * in behavioral code. + /* vpiNoDelay -- Set the value immediately. The p_vpi_time parameter + * may be NULL, in this case. This is like a blocking assignment + * in behavioral code. + */ vpi_put_value((vpiHandle)gpi_hdl, value_p, NULL, vpiNoDelay); FEXIT @@ -135,9 +133,10 @@ void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) value_p->value.str = buff; value_p->format = vpiBinStrVal; -// * vpiNoDelay -- Set the value immediately. The p_vpi_time parameter -// * may be NULL, in this case. This is like a blocking assignment -// * in behavioral code. + /* vpiNoDelay -- Set the value immediately. The p_vpi_time parameter + * may be NULL, in this case. This is like a blocking assignment + * in behavioral code. + */ vpi_put_value((vpiHandle)gpi_hdl, value_p, NULL, vpiNoDelay); free(buff); From 309dd3a4965e38fe1ca3a8b247bea918d4a8c8c9 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 25 Jun 2013 23:05:24 +0100 Subject: [PATCH 0095/2656] Issue #12: Actually disable filtering --- lib/gpi/Makefile | 2 +- lib/gpi/gpi_logging.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index 08991a81..16787362 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -30,7 +30,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ -GCC_ARGS += -DNO_FILTER +GCC_ARGS += SRCS := gpi_logging.c OBJS := $(SRCS:%.c=$(OBJ_DIR)/%.o) diff --git a/lib/gpi/gpi_logging.c b/lib/gpi/gpi_logging.c index 6cbe223c..7130605e 100644 --- a/lib/gpi/gpi_logging.c +++ b/lib/gpi/gpi_logging.c @@ -98,13 +98,13 @@ void gpi_log(const char *name, long level, const char *pathname, const char *fun Py_DECREF(pLogRecord); // Filter here -#ifdef NO_FILTER +#ifdef FILTER PyObject *pShouldFilter = PyObject_CallObject(pLogFilter, pLogArgs); if (pShouldFilter == Py_True) { #endif PyObject *pLogResult = PyObject_CallObject(pLogHandler, pLogArgs); Py_DECREF(pLogResult); -#ifdef NO_FILTER +#ifdef FILTER } Py_DECREF(pShouldFilter); From 50ee92ab20086eb4b89e035ce1a96cbd664fb32d Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 25 Jun 2013 23:21:07 +0100 Subject: [PATCH 0096/2656] Issue #21: Move vpi registration and closedown out of embed lib --- lib/embed/embed.h | 33 ++++++++++++++++ lib/embed/gpi_embed.c | 59 ++++------------------------ lib/vpi_shim/Makefile | 2 +- lib/vpi_shim/gpi_vpi.c | 88 ++++++++++++++++++++++++++++++++++++------ 4 files changed, 117 insertions(+), 65 deletions(-) create mode 100644 lib/embed/embed.h diff --git a/lib/embed/embed.h b/lib/embed/embed.h new file mode 100644 index 00000000..c16eb090 --- /dev/null +++ b/lib/embed/embed.h @@ -0,0 +1,33 @@ +/****************************************************************************** +* Copyright (c) 2013 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +#include +#include + +void embed_init_python(void); +void embed_sim_init(void); +void embed_sim_end(void); diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 1da496e9..f0c82164 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -28,14 +28,9 @@ // Embed Python into the simulator using GPI #include -#include "gpi.h" -#include "gpi_logging.h" +#include "embed.h" static PyThreadState *gtstate; -static gpi_cb_hdl sim_init_cb; -static gpi_cb_hdl sim_finish_cb; - - static char *progname = "cocotb"; static PyObject *thread_dict; @@ -54,7 +49,7 @@ static PyObject *lock; * * Stores the thread state for cocotb in static variable gtstate */ -void init_python(void) +void embed_init_python(void) { FENTER; @@ -86,7 +81,7 @@ void init_python(void) * * Loads the Python module called cocotb and calls the _initialise_testbench function */ -int handle_sim_init(void *gpi_cb_data) +void embed_sim_init(void) { FENTER @@ -108,7 +103,7 @@ int handle_sim_init(void *gpi_cb_data) PyErr_Print(); fprintf(stderr, "Failed to load \"%s\"\n", "cocotb"); PyGILState_Release(gstate); - return 1; + return; } @@ -123,7 +118,7 @@ int handle_sim_init(void *gpi_cb_data) PyErr_Print(); fprintf(stderr, "Failed to find handle to logging object \"log\" from module cocotb\n"); PyGILState_Release(gstate); - return 1; + return; } set_log_handler(pHandler); @@ -151,7 +146,7 @@ int handle_sim_init(void *gpi_cb_data) Py_DECREF(pFunc); Py_DECREF(pModule); PyGILState_Release(gstate); - return 1; + return; } pArgs = PyTuple_New(1); @@ -174,51 +169,11 @@ int handle_sim_init(void *gpi_cb_data) PyGILState_Release(gstate); FEXIT - return(0); -} - -void register_initial_callback() -{ - FENTER - sim_init_cb = gpi_register_sim_start_callback(handle_sim_init, (void *)NULL); - FEXIT } - -int handle_sim_end(void *gpi_cb_data) +void embed_sim_end(void) { FENTER - sim_finish_cb = NULL; LOG_WARN("Closing down cocotb at simulator request!"); FEXIT - return(0); -} - -void register_final_callback() -{ - FENTER - sim_finish_cb = gpi_register_sim_end_callback(handle_sim_end, (void *)NULL); - FEXIT -} - - -// FIXME this is VPI specific, should be in gpi_vpi.c -void (*vlog_startup_routines[])() = { - init_python, - register_initial_callback, - register_final_callback, - 0 -}; - - -// For non-VPI compliant applications that cannot find vlog_startup_routines symbol -void vlog_startup_routines_bootstrap() { - void (*routine)(void); - int i; - routine = vlog_startup_routines[0]; - for (i = 0, routine = vlog_startup_routines[i]; - routine; - routine = vlog_startup_routines[++i]) { - routine(); - } } diff --git a/lib/vpi_shim/Makefile b/lib/vpi_shim/Makefile index bc0dafac..723831cc 100644 --- a/lib/vpi_shim/Makefile +++ b/lib/vpi_shim/Makefile @@ -27,7 +27,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj -INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ -I../gpi/ $(VPI_INCLUDE_PATH) +INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ -I../gpi/ -I../embed -I$(VPI_INCLUDE_PATH) GCC_ARGS += LIBS := -lgpilog LD_PATH := -L$(LIB_DIR) diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 56a47129..03ba0cd2 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -30,10 +30,14 @@ #include #include -#include "gpi.h" -#include "gpi_logging.h" +#include +#include +#include #include +static gpi_cb_hdl sim_init_cb; +static gpi_cb_hdl sim_finish_cb; + // Handle related functions gpi_sim_hdl gpi_get_root_handle() { @@ -76,12 +80,6 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) } - -void gpi_sim_end() -{ - vpi_control(vpiFinish); -} - // double gpi_get_sim_time() void gpi_get_sim_time(uint32_t *high, uint32_t *low) { @@ -144,10 +142,6 @@ void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) } - - - - static char *gpi_copy_name(const char *name) { int len; @@ -559,3 +553,73 @@ void gpi_clock_unregister(gpi_clock_hdl clock) { clock->exit = true; } + +void register_embed(void) +{ + FENTER + embed_init_python(); + FEXIT +} + + +int handle_sim_init(void *gpi_cb_data) +{ + FENTER + sim_init_cb = NULL; + embed_sim_init(); + FEXIT +} + +void register_initial_callback(void) +{ + FENTER + sim_init_cb = gpi_register_sim_start_callback(handle_sim_init, (void *)NULL); + FEXIT +} + +int handle_sim_end(void *gpi_cb_data) +{ + FENTER + if (sim_finish_cb) + embed_sim_end(); + FEXIT +} + +void register_final_callback(void) +{ + FENTER + sim_finish_cb = gpi_register_sim_end_callback(handle_sim_end, (void *)NULL); + FEXIT +} + +// If the Pything world wants things to shut down then unregister +// the callback for end of sim +void gpi_sim_end() +{ + FENTER + + sim_finish_cb = NULL; + vpi_control(vpiFinish); + + FEXIT +} + +void (*vlog_startup_routines[])() = { + register_embed, + register_initial_callback, + register_final_callback, + 0 +}; + + +// For non-VPI compliant applications that cannot find vlog_startup_routines symbol +void vlog_startup_routines_bootstrap() { + void (*routine)(void); + int i; + routine = vlog_startup_routines[0]; + for (i = 0, routine = vlog_startup_routines[i]; + routine; + routine = vlog_startup_routines[++i]) { + routine(); + } +} From 5ddc0d9a2f3ae11383f95e1dc4514e4010fa8e8c Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 26 Jun 2013 09:50:57 +0100 Subject: [PATCH 0097/2656] Modifications for Issue #17 * Add iterator methods * TODO: Need to think about what objects we iterate over currently only signals... --- lib/gpi/gpi.h | 8 ++++++++ lib/simulator/simulatormodule.c | 36 +++++++++++++++++++++++++++++++++ lib/simulator/simulatormodule.h | 4 ++++ lib/vpi_shim/gpi_vpi.c | 30 +++++++++++++++++++++++++++ 4 files changed, 78 insertions(+) diff --git a/lib/gpi/gpi.h b/lib/gpi/gpi.h index 896b5740..cf85b5c6 100644 --- a/lib/gpi/gpi.h +++ b/lib/gpi/gpi.h @@ -114,6 +114,14 @@ gpi_sim_hdl gpi_get_root_handle(); gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent); +// Functions for iterating over entries of a handle +// Returns an iterator handle which can then be used in gpi_next calls +gpi_iterator_hdl gpi_iterate(gpi_sim_hdl base); + +// Returns NULL when there are no more objects +gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator); + + // Functions for querying the properties of a handle diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 97e338be..70206ae4 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -440,6 +440,42 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) +static PyObject *iterate_signals(PyObject *self, PyObject *args) +{ + gpi_sim_hdl hdl; + gpi_iterator_hdl result; + + if (!PyArg_ParseTuple(args, "l", &hdl)) + return NULL; + + result = gpi_iterate(hdl); + + return Py_BuildValue("l", result); +} + + + +static PyObject *next(PyObject *self, PyObject *args) +{ + gpi_iterator_hdl hdl; + gpi_sim_hdl result; + + if (!PyArg_ParseTuple(args, "l", &hdl)) + return NULL; + + result = gpi_next(hdl); + + // Raise stopiteration when we're done + if (!result) { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + + return Py_BuildValue("l", result); +} + + + static PyObject *get_signal_val(PyObject *self, PyObject *args) { diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index f017ffdf..4794336a 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -67,6 +67,8 @@ static PyObject *create_clock(PyObject *self, PyObject *args); static PyObject *stop_clock(PyObject *self, PyObject *args); static PyObject *stop_simulator(PyObject *self, PyObject *args); +static PyObject *iterate_signals(PyObject *self, PyObject *args); +static PyObject *next(PyObject *self, PyObject *args); static PyObject *get_sim_time(PyObject *self, PyObject *args); // static PyObject *deregister_callback(PyObject *self, PyObject *args); @@ -88,6 +90,8 @@ static PyMethodDef SimulatorMethods[] = { {"create_clock", create_clock, METH_VARARGS, "Register a clock object"}, {"stop_clock", stop_clock, METH_VARARGS, "Terminate a clock"}, {"stop_simulator", stop_simulator, METH_VARARGS, "Instruct the attached simulator to stop"}, + {"iterate_signals", iterate_signals, METH_VARARGS, "Get an iterator handle to loop over all signals in an object"}, + {"next", next, METH_VARARGS, "Get the next object from the iterator"}, // FIXME METH_NOARGS => initialization from incompatible pointer type {"get_sim_time", get_sim_time, METH_VARARGS, "Get the current simulation time as a float"}, diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 717aa271..a4e6421d 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -73,7 +73,37 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) free(buff); FEXIT return rv; +} + + +// Functions for iterating over entries of a handle +// Returns an iterator handle which can then be used in gpi_next calls +gpi_iterator_hdl gpi_iterate(gpi_sim_hdl base) { + FENTER + + vpiHandle iterator; + + iterator = vpi_iterate(vpiNet, (vpiHandle)base); + FEXIT + return (gpi_iterator_hdl)iterator; +} + +// Returns NULL when there are no more objects +gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) { + FENTER + vpiHandle result; + + result = vpi_scan((vpiHandle) iterator); + +// FIXME do we need to free the iterator handle? +// Icarus complains about this +// if (result == NULL) && !vpi_free_object((vpiHandle)iterator)) { +// LOG_WARN("VPI: Attempting to free iterator failed!"); +// } + + FEXIT + return (gpi_sim_hdl)result; } From b5a8324036ffdabb48760795f94c0099b320eae6 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 26 Jun 2013 09:54:04 +0100 Subject: [PATCH 0098/2656] Start of examples/regression for issue #19 --- examples/functionality/hdl/sample_module.v | 58 +++++++++++++++++++ examples/functionality/tests/Makefile | 37 ++++++++++++ examples/functionality/tests/test_cocotb.py | 64 +++++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 examples/functionality/hdl/sample_module.v create mode 100644 examples/functionality/tests/Makefile create mode 100644 examples/functionality/tests/test_cocotb.py diff --git a/examples/functionality/hdl/sample_module.v b/examples/functionality/hdl/sample_module.v new file mode 100644 index 00000000..8a85af2f --- /dev/null +++ b/examples/functionality/hdl/sample_module.v @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2013 Potential Ventures Ltd +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Potential Ventures Ltd nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + + +`timescale 1 ps / 1 ps + +module sample_module ( + input clk, + + output reg stream_in_ready, + input stream_in_valid, + input [7:0] stream_in_data, + + input stream_out_ready, + output reg [7:0] stream_out_data_comb, + output reg [7:0] stream_out_data_registered +); + +always @(posedge clk) + stream_out_data_registered <= stream_in_data; + +always @(stream_in_data) + stream_out_data_comb = stream_in_data; + +always @(stream_out_ready) + stream_in_ready = stream_out_ready; + + +initial begin + $dumpfile("waveform.vcd"); + $dumpvars(0,sample_module); +end + +endmodule diff --git a/examples/functionality/tests/Makefile b/examples/functionality/tests/Makefile new file mode 100644 index 00000000..071c7be9 --- /dev/null +++ b/examples/functionality/tests/Makefile @@ -0,0 +1,37 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +TOPLEVEL = sample_module + +VERILOG_SOURCES = ../hdl/sample_module.v +MODULE=test_cocotb +FUNCTION=test_not_a_coroutine + +include ../../../makefiles/Makefile.inc +include ../../../makefiles/Makefile.sim + diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py new file mode 100644 index 00000000..069ae8d8 --- /dev/null +++ b/examples/functionality/tests/test_cocotb.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" +A set of tests that demonstrate cocotb functionality + +Also used a regression test of cocotb capabilities +""" + +import cocotb +from cocotb.triggers import Timer + + + + +# Tests relating to providing meaningful errors if we forget to use the +# yield keyword correctly to turn a function into a coroutine + +@cocotb.test(expect_fail=True) +def test_not_a_coroutine(dut): + """Example of a failing to use the yield keyword in a test""" + dut.log.warning("This test will fail because we don't yield anything") + +@cocotb.coroutine +def function_not_a_coroutine(): + """If we don't yield, this isn't a coroutine""" + return "This should fail" + +@cocotb.test(expect_fail=True) +def test_function_not_a_coroutine(dut): + """Example of trying to yield a coroutine that isn't a coroutine""" + yield Timer(500) + yield function_not_a_coroutine() + +@cocotb.test(expect_fail=True) +def test_function_not_a_coroutine_fork(dut): + """Example of trying to fork a coroutine that isn't a coroutine""" + yield Timer(500) + cocotb.fork(function_not_a_coroutine()) + yield Timer(500) From 831e3c56a6947f2be5696dee0664fe174070ba45 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 26 Jun 2013 11:42:57 +0100 Subject: [PATCH 0099/2656] Issue #21: Fix up Makefiles to restore functionality of vcs and icarus --- Makefile | 2 +- bin/cocotbenv.py | 4 ++-- lib/embed/Makefile | 14 +++++--------- lib/embed/embed.h | 6 +++--- lib/gpi/Makefile | 7 +++---- lib/simulator/Makefile | 10 +++++++--- lib/vpi_shim/Makefile | 19 +++++++++++++------ makefiles/Makefile.icarus | 6 +++--- makefiles/Makefile.vcs | 4 ++-- 9 files changed, 39 insertions(+), 33 deletions(-) diff --git a/Makefile b/Makefile index ba79f7b1..a231c8d7 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ all: $(LIBS) $(LIBS): dirs $(MAKE) -C $@ -lib/vpi_shim: lib/gpi +lib/vpi_shim: lib/gpi lib/embed lib/simulator: lib/vpi_shim dirs: diff --git a/bin/cocotbenv.py b/bin/cocotbenv.py index 72356042..3c464db1 100755 --- a/bin/cocotbenv.py +++ b/bin/cocotbenv.py @@ -48,7 +48,7 @@ def execute(self, cmd): class SimIcarus(SimType): def __init__(self, name=None, sdebug=False): SimType.__init__(self, "Icarus", sdebug) - self._base_cmd = ' vvp -m cocotb' + self._base_cmd = ' vvp -m gpivpi' def execute(self, py_path, lib_path, module, function, finput): cmd = 'PYTHONPATH=' + py_path @@ -75,7 +75,7 @@ def execute(self, py_path, lib_path, module, function, finput): if self._sdebug is True: cmd = cmd + ' gdb --args' cmd = cmd + ' ' + finput - cmd = cmd + ' -l ' + lib_path + '/libcocotb.so' + cmd = cmd + ' -l ' + lib_path + '/libgpi.so' SimType.execute(self, cmd) diff --git a/lib/embed/Makefile b/lib/embed/Makefile index 69794385..88069c56 100644 --- a/lib/embed/Makefile +++ b/lib/embed/Makefile @@ -31,13 +31,15 @@ OBJ_DIR := obj INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ \ -I/mnt/sources/tools/icarus/include/iverilog -I../gpi GCC_ARGS += -LIBS := -lgpi -lpython2.7 $(PYLIBS) +LIBS := -lpython2.7 $(PYLIBS) LD_PATH := -L$(LIB_DIR) SRCS := gpi_embed.c OBJS := $(SRCS:%.c=$(OBJ_DIR)/%.o) -all: $(LIB_DIR)/cocotb.vpi +LIB_NAME := libcocotb.so + +all: $(LIB_DIR)/$(LIB_NAME) $(OBJ_DIR)/%.o: %.c gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $@ $< @@ -45,14 +47,8 @@ $(OBJ_DIR)/%.o: %.c $(OBJ_DIR): mkdir -p $(OBJ_DIR) -$(LIB_DIR)/libcocotb.so: $(OBJ_DIR) $(OBJS) +$(LIB_DIR)/$(LIB_NAME): $(OBJ_DIR) $(OBJS) gcc $(GCC_ARGS) -shared -Xlinker -export-dynamic -o $@ $(OBJS) $(LIBS) $(LD_PATH) -# More rules such that this may be needed depending on the requirements of -# different simulators, icarus for instance loads a .vpi libraray to use the vpi -# inerface at the start of the simulation -$(LIB_DIR)/cocotb.vpi: $(LIB_DIR)/libcocotb.so - ln -sf libcocotb.so $@ - clean: rm -rf $(OBJ_DIR) diff --git a/lib/embed/embed.h b/lib/embed/embed.h index c16eb090..8b9a91da 100644 --- a/lib/embed/embed.h +++ b/lib/embed/embed.h @@ -28,6 +28,6 @@ #include #include -void embed_init_python(void); -void embed_sim_init(void); -void embed_sim_end(void); +extern void embed_init_python(void); +extern void embed_sim_init(void); +extern void embed_sim_end(void); diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index 16787362..670774e4 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -35,7 +35,7 @@ GCC_ARGS += SRCS := gpi_logging.c OBJS := $(SRCS:%.c=$(OBJ_DIR)/%.o) -all: $(LIB_DIR)/gpi_logging.so +all: $(LIB_DIR)/libgpilog.so $(OBJ_DIR)/%.o: %.c gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $@ $< @@ -43,9 +43,8 @@ $(OBJ_DIR)/%.o: %.c $(OBJ_DIR): mkdir -p $(OBJ_DIR) -$(LIB_DIR)/gpi_logging.so: $(OBJ_DIR) $(OBJS) - gcc $(GCC_ARGS) -shared -export-dynamic -o $(LIB_DIR)/gpi_logging.so $(OBJS) -lpython2.7 - ln -sf gpi_logging.so $(LIB_DIR)/libgpilog.so +$(LIB_DIR)/libgpilog.so: $(OBJ_DIR) $(OBJS) + gcc $(GCC_ARGS) -shared -export-dynamic -o $@ $(OBJS) -lpython2.7 clean: rm -rf $(OBJ_DIR) diff --git a/lib/simulator/Makefile b/lib/simulator/Makefile index 9126f14a..4ac50931 100644 --- a/lib/simulator/Makefile +++ b/lib/simulator/Makefile @@ -35,6 +35,8 @@ LD_PATH := -L$(LIB_DIR) SRCS := simulatormodule.c OBJS := $(SRCS:%.c=$(OBJ_DIR)/%.o) +LIB_NAME := libsim.so + all : $(LIB_DIR)/simulator.so $(OBJ_DIR)/%.o: %.c @@ -43,9 +45,11 @@ $(OBJ_DIR)/%.o: %.c $(OBJ_DIR) : mkdir -p $(OBJ_DIR) -$(LIB_DIR)/simulator.so : $(OBJ_DIR) $(OBJS) - gcc $(GCC_ARGS) -shared -Xlinker -export-dynamic -o $(LIB_DIR)/simulator.so $(OBJS) $(LIBS) $(LD_PATH) - ln -sf simulator.so $(LIB_DIR)/libsim.so +$(LIB_DIR)/$(LIB_NAME) : $(OBJ_DIR) $(OBJS) + gcc $(GCC_ARGS) -shared -Xlinker -export-dynamic -o $@ $(OBJS) $(LIBS) $(LD_PATH) + +$(LIB_DIR)/simulator.so: $(LIB_DIR)/$(LIB_NAME) + ln -sf $(LIB_NAME) $@ clean : @rm -rf $(OBJ_DIR) diff --git a/lib/vpi_shim/Makefile b/lib/vpi_shim/Makefile index 723831cc..d0f61a89 100644 --- a/lib/vpi_shim/Makefile +++ b/lib/vpi_shim/Makefile @@ -27,15 +27,17 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj -INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ -I../gpi/ -I../embed -I$(VPI_INCLUDE_PATH) +INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ -I../gpi/ -I../embed/ -I$(VPI_INCLUDE_PATH) GCC_ARGS += -LIBS := -lgpilog +LIBS := -lgpilog -lcocotb LD_PATH := -L$(LIB_DIR) SRCS := gpi_vpi.c OBJS := $(SRCS:%.c=$(OBJ_DIR)/%.o) -all: $(LIB_DIR)/gpi_lib.so +LIB_NAME := libgpi.so + +all: $(LIB_DIR)/gpivpi.vpi $(OBJ_DIR)/%.o: %.c gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $@ $< @@ -43,9 +45,14 @@ $(OBJ_DIR)/%.o: %.c $(OBJ_DIR): mkdir -p $(OBJ_DIR) -$(LIB_DIR)/gpi_lib.so: $(OBJ_DIR) $(OBJS) - gcc $(GCC_ARGS) -shared -export-dynamic -o $(LIB_DIR)/gpi_lib.so $(OBJS) $(LIBS) $(LD_PATH) - ln -sf gpi_lib.so $(LIB_DIR)/libgpi.so +$(LIB_DIR)/$(LIB_NAME): $(OBJ_DIR) $(OBJS) + gcc $(GCC_ARGS) -shared -export-dynamic -o $@ $(OBJS) $(LIBS) $(LD_PATH) + +# More rules such that this may be needed depending on the requirements of +# different simulators, icarus for instance loads a .vpi libraray to use the vpi +# inerface at the start of the simulation +$(LIB_DIR)/gpivpi.vpi: $(LIB_DIR)/$(LIB_NAME) + ln -sf $(LIB_NAME) $@ clean: rm -rf $(OBJ_DIR) diff --git a/makefiles/Makefile.icarus b/makefiles/Makefile.icarus index 1f63976d..1a2e8cf7 100644 --- a/makefiles/Makefile.icarus +++ b/makefiles/Makefile.icarus @@ -38,14 +38,14 @@ $(OBJ_DIR): sim: $(OBJ_DIR) $(VERILOG_SOURCES) iverilog -o $(OBJ_DIR)/sim.vvp $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ - vvp -M $(LIB_DIR) -m cocotb $(OBJ_DIR)/sim.vvp + vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp .PHONY: gdb gdb: $(OBJ_DIR) $(VERILOG_SOURCES) iverilog -o $(OBJ_DIR)/sim.vvp $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ - gdb --args vvp -M $(LIB_DIR) -m cocotb $(OBJ_DIR)/sim.vvp + gdb --args vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp clean: rm -rf $(OBJ_DIR) - + rm -rf simv diff --git a/makefiles/Makefile.vcs b/makefiles/Makefile.vcs index bc59c08a..d54d98de 100644 --- a/makefiles/Makefile.vcs +++ b/makefiles/Makefile.vcs @@ -45,11 +45,11 @@ pli.tab : .PHONY: sim sim: $(OBJ_DIR) $(VERILOG_SOURCES) pli.tab PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ - vcs -R +acc+1 +vpi -P pli.tab -sverilog $(EXTRA_ARGS) -debug -load cocotb.vpi $(VERILOG_SOURCES) + vcs -R +acc+1 +vpi -P pli.tab -sverilog $(EXTRA_ARGS) -debug -load libgpi.so $(VERILOG_SOURCES) dve: $(OBJ_DIR) $(VERILOG_SOURCES) pli.tab PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ - vcs -R +acc+1 +vpi+1+assertion -P pli.tab -sverilog $(EXTRA_ARGS) -gui -load cocotb.vpi $(VERILOG_SOURCES) + vcs -R +acc+1 +vpi+1+assertion -P pli.tab -sverilog $(EXTRA_ARGS) -gui -load libgpi.so $(VERILOG_SOURCES) clean: -@rm -rf $(OBJ_DIR) -@rm -f pli.tab From 703fdc610e89c88c1470eecc8801fa87411f2e66 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 26 Jun 2013 11:44:20 +0100 Subject: [PATCH 0100/2656] Issue #21: Remove accidental clean recipe addition to icarus makefile --- makefiles/Makefile.icarus | 1 - 1 file changed, 1 deletion(-) diff --git a/makefiles/Makefile.icarus b/makefiles/Makefile.icarus index 1a2e8cf7..e4a1caa2 100644 --- a/makefiles/Makefile.icarus +++ b/makefiles/Makefile.icarus @@ -48,4 +48,3 @@ gdb: $(OBJ_DIR) $(VERILOG_SOURCES) clean: rm -rf $(OBJ_DIR) - rm -rf simv From 2aec57cb6f67411c0e1959f5fe2caf692cb2c312 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 26 Jun 2013 14:12:22 +0100 Subject: [PATCH 0101/2656] Cleanup: Pull "coroutine" into cocotb namespace --- cocotb/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 54e0565c..f7de347f 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -42,7 +42,7 @@ # Things we want in the cocotb namespace -from cocotb.decorators import test +from cocotb.decorators import test, coroutine # Singleton scheduler instance # NB this cheekily ensures a singleton since we're replacing the reference From fc67a1b9914c5230f2861862e1375a0b36bf2f54 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 26 Jun 2013 14:17:50 +0100 Subject: [PATCH 0102/2656] Add diagram of cocotb overview --- documentation/source/diagrams/svg/cocotb_overview.svg | 1 + documentation/source/diagrams/xml/cocotb_overview.xml | 1 + 2 files changed, 2 insertions(+) create mode 100644 documentation/source/diagrams/svg/cocotb_overview.svg create mode 100644 documentation/source/diagrams/xml/cocotb_overview.xml diff --git a/documentation/source/diagrams/svg/cocotb_overview.svg b/documentation/source/diagrams/svg/cocotb_overview.svg new file mode 100644 index 00000000..f973f99b --- /dev/null +++ b/documentation/source/diagrams/svg/cocotb_overview.svg @@ -0,0 +1 @@ +
DUT
(Verilog / VHDL)
[Object]
VPI
[Object]
VHPI
[Object]
FLI
[Object]
GPI
[Object]
Scheduler
Embed
[Object]
Extend
[Object]
SchedulerPythonSimulator
Coroutine
[Object]
Coroutine
[Object]
Coroutine
[Object]
Coroutine
[Object]
Coroutine
[Object]
Coroutine
[Object]
Test
\ No newline at end of file diff --git a/documentation/source/diagrams/xml/cocotb_overview.xml b/documentation/source/diagrams/xml/cocotb_overview.xml new file mode 100644 index 00000000..242dd8bb --- /dev/null +++ b/documentation/source/diagrams/xml/cocotb_overview.xml @@ -0,0 +1 @@ + \ No newline at end of file From 9557ad281af59d33ffb3ca58fac07c9bc3d0ab9a Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 26 Jun 2013 14:22:12 +0100 Subject: [PATCH 0103/2656] Added overview page to documentation including diagram --- documentation/source/index.rst | 2 +- documentation/source/overview.rst | 6 ++++++ documentation/source/structure.rst | 0 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 documentation/source/overview.rst delete mode 100644 documentation/source/structure.rst diff --git a/documentation/source/index.rst b/documentation/source/index.rst index 6af43418..62f6b0d9 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -12,10 +12,10 @@ Contents: :maxdepth: 2 introduction + overview quickstart coroutines triggers - structure roadmap diff --git a/documentation/source/overview.rst b/documentation/source/overview.rst new file mode 100644 index 00000000..a25ee158 --- /dev/null +++ b/documentation/source/overview.rst @@ -0,0 +1,6 @@ +################## +Overview of cocotb +################## + +.. image:: diagrams/svg/cocotb_overview.svg + diff --git a/documentation/source/structure.rst b/documentation/source/structure.rst deleted file mode 100644 index e69de29b..00000000 From e1de43379c3a98d03ae3c1abee05d04be6af8e98 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 26 Jun 2013 14:27:20 +0100 Subject: [PATCH 0104/2656] Added some additional generic generators --- cocotb/generators/__init__.py | 46 +++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/cocotb/generators/__init__.py b/cocotb/generators/__init__.py index 38400bf2..238e7c5f 100644 --- a/cocotb/generators/__init__.py +++ b/cocotb/generators/__init__.py @@ -26,6 +26,8 @@ """ Set of general generic generators """ +import math +import random from cocotb.decorators import public @public @@ -45,6 +47,50 @@ def repeat(obj, nrepeat=None): for i in range(nrepeat): yield obj +@public +def combine(generators): + """ + Generator for serially combining multiple generators together + + Args: + generators (iterable): Generators to combine together + """ + for gen in generators: + for item in gen: + yield item + + +@public +def gaussian(mean, sigma): + """ + Generate a guasian distribution indefinitely + + Args: + mean (int/float): mean value + + signma (int/float): Standard deviation + """ + while True: + yield random.gauss(mean, sigma) + + +@public +def sine_wave(amplitude, w, offset=0): + """ + Generates a sine wave that repeats forever + + Args: + amplitude (int/float): peak deviation of the function from zero + + w (int/float): is the rate of change of the function argument + + Yields: + floats that form a sine wave + """ + twoPiF_DIV_sampleRate = math.pi * 2 + while True: + for idx in ( i / float(w) for i in range(int(w)) ): + yield amplitude*math.sin(twoPiF_DIV_sampleRate * idx) + offset def get_generators(module): """Return an iterator which yields all the generators in a module From ef32ee65e19586691922effddf859374f1e371fb Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 26 Jun 2013 14:30:25 +0100 Subject: [PATCH 0105/2656] Updated bit generators to use new generic generators --- cocotb/generators/bit.py | 41 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/cocotb/generators/bit.py b/cocotb/generators/bit.py index 15cbc05b..f84dc958 100644 --- a/cocotb/generators/bit.py +++ b/cocotb/generators/bit.py @@ -35,6 +35,19 @@ import random from cocotb.decorators import public +from cocotb.generators import * + + +def bit_toggler(gen_on, gen_off): + """Combines two generators to provide cycles_on, cycles_off tuples + + Args: + gen_on (generator): generator that yields number of cycles on + + gen_off (generator): generator that yields number of cycles off + """ + while True: + yield int(abs(gen_on.next())), int(abs(gen_off.next())) @public def intermittent_single_cycles(mean=10, sigma=None): @@ -47,6 +60,30 @@ def intermittent_single_cycles(mean=10, sigma=None): """ if sigma is None: sigma = mean/4.0 - while True: - yield (abs(int(random.gauss(mean, sigma))), 1) + + return bit_toggler(gaussian(mean, sigma), repeat(1)) + + +@public +def random_50_percent(mean=10, sigma=None): + """50% duty cycle with random width + Kwargs: + mean (int): Average number of cycles on/off + + sigma (int): Standard deviation of gaps. mean/4 if sigma is None + """ + if sigma is None: + sigma = mean/4.0 + for duration in gaussian(mean, sigma): + yield int(abs(duration)), int(abs(duration)) + +@public +def wave(on_ampl=30, on_freq=200, off_ampl=10, off_freq=100): + """ + Drive a repeating sine_wave pattern + + TODO: + Adjust args so we just specify a repeat duration and overall throughput + """ + return bit_toggler(sine_wave(on_ampl, on_freq), sine_wave(off_ampl, off_freq)) From f456bcd5febc4bae5b63bfca199b431145e31baa Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 26 Jun 2013 14:32:45 +0100 Subject: [PATCH 0106/2656] Added ipv4 generator, disabled scapy warnings --- cocotb/generators/packet.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cocotb/generators/packet.py b/cocotb/generators/packet.py index 0ae20dc1..c0efdead 100644 --- a/cocotb/generators/packet.py +++ b/cocotb/generators/packet.py @@ -36,6 +36,10 @@ from scapy.all import Ether, IP, UDP +# Supress SCAPY warning messages +import logging +logging.getLogger("scapy").setLevel(logging.ERROR) + from cocotb.decorators import public from cocotb.generators.byte import * @@ -67,3 +71,8 @@ def udp_random_sizes(npackets=100, payload=_default_payload()): for pkt in range(npackets): yield header / _get_payload(payload, random.randint(0,max_size)) +@public +def ipv4_small_packets(npackets=100, payload=_default_payload()): + """Small (<100bytes payload) IPV4 packets""" + for pkt in range(npackets): + yield Ether() / IP() / _get_payload(payload, random.randint(0, 100)) From 13128df6bcb6062b12b660ad67b50b2e36df8680 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 28 Jun 2013 12:01:14 +0100 Subject: [PATCH 0107/2656] Handle removing the unfired triggers that where added as part of a list when one fires --- cocotb/scheduler.py | 2 ++ cocotb/triggers.py | 17 ++++++++++++++++- examples/functionality/tests/test_cocotb.py | 9 +++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index ceaeff5e..bbc030c3 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -73,6 +73,7 @@ def react(self, trigger): while self._scheduling: coroutine = self._scheduling.pop(0) + trigger.clearpeers() self.schedule(coroutine, trigger=trigger) self.log.debug("Scheduled coroutine %s" % (coroutine.__name__)) @@ -155,6 +156,7 @@ def schedule(self, coroutine, trigger=None): self.schedule(result) elif isinstance(result, list): for trigger in result: + trigger.addpeers(result) self._add_trigger(trigger, coroutine) else: self.log.warning("Unable to schedule coroutine since it's returning stuff %s" % repr(result)) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index e5907e5f..81586af6 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -37,10 +37,25 @@ class Trigger(object): """Base class to derive from""" def __init__(self): self.log = logging.getLogger("cocotb.%s.0x%x" % (self.__class__.__name__, id(self))) + self.peers = [] def unprime(self): """Remove any pending callbacks if necessary""" - pass + if self.peers: + self.peers = None + + + def addpeers(self, peers): + """Store any relate triggers""" + self.peers = peers + + def clearpeers(self): + """Call _clearpeers on each trigger that is not me""" + if self.peers: + while self.peers: + trigger = self.peers.pop(0) + if trigger is not self: + trigger.unprime() def __del__(self): """Ensure if a trigger drops out of scope we remove any pending callbacks""" diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 069ae8d8..93e84359 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -62,3 +62,12 @@ def test_function_not_a_coroutine_fork(dut): yield Timer(500) cocotb.fork(function_not_a_coroutine()) yield Timer(500) + +@cocotb.test(expect_fail=False) +def test_yield_list(dut): + """Example of yeilding on a list of triggers""" + yield [Timer(500), Timer(1000)] + +@cocotb.test(expect_fail=True) +def test_duplicate_yield(dut): + """A trigger can not be yielded on twice""" From 137d029863b65ad93bdd9d66ddd0840fa9eff8e2 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 28 Jun 2013 12:13:35 +0100 Subject: [PATCH 0108/2656] Issue #16: Skeleton for deregister_callback --- cocotb/triggers.py | 6 ++++++ lib/simulator/simulatormodule.c | 23 +++++++++++++++++++---- lib/simulator/simulatormodule.h | 5 ++--- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 81586af6..d74fbaa6 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -73,8 +73,14 @@ def __init__(self, time_ps): self.cbhdl = None def prime(self, callback): + """Register for a timed callback""" self.cbhdl = simulator.register_timed_callback(self.time_ps, callback, self) + def unprime(self): + """Unregister a prior registered timed callback""" + simulator.deregister_callback(self.cbhdl) + Trigger.unprime(self) + def __str__(self): return self.__class__.__name__ + "(%dps)" % self.time_ps diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 70206ae4..44981738 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -125,6 +125,7 @@ static PyObject *log_msg(PyObject *self, PyObject *args) return Py_BuildValue("s", "OK!"); } + // Register a callback for read only state of sim // First argument is the function to call // Remaining arguments are keyword arguments to be passed to the callback @@ -186,6 +187,7 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) return rv; } + static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) { FENTER @@ -244,6 +246,7 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) return rv; } + static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) { FENTER @@ -368,6 +371,7 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) return rv; } + // Register signal change callback // First argument should be the signal handle // Second argument is the function to call @@ -439,7 +443,6 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) } - static PyObject *iterate_signals(PyObject *self, PyObject *args) { gpi_sim_hdl hdl; @@ -454,7 +457,6 @@ static PyObject *iterate_signals(PyObject *self, PyObject *args) } - static PyObject *next(PyObject *self, PyObject *args) { gpi_iterator_hdl hdl; @@ -475,8 +477,6 @@ static PyObject *next(PyObject *self, PyObject *args) } - - static PyObject *get_signal_val(PyObject *self, PyObject *args) { gpi_sim_hdl hdl; @@ -493,6 +493,7 @@ static PyObject *get_signal_val(PyObject *self, PyObject *args) return retstr; } + static PyObject *set_signal_val(PyObject *self, PyObject *args) { gpi_sim_hdl hdl; @@ -533,6 +534,7 @@ static PyObject *get_handle_by_name(PyObject *self, PyObject *args) return Py_BuildValue("l", result); } + static PyObject *get_name_string(PyObject *self, PyObject *args) { char *result; @@ -549,6 +551,7 @@ static PyObject *get_name_string(PyObject *self, PyObject *args) return retstr; } + static PyObject *get_type_string(PyObject *self, PyObject *args) { char *result; @@ -565,6 +568,7 @@ static PyObject *get_type_string(PyObject *self, PyObject *args) return retstr; } + // Returns a high, low tuple of simulator time // Note we can never log from this function since the logging mechanism calls this to annotate // log messages with the current simulation time @@ -582,12 +586,22 @@ static PyObject *get_sim_time(PyObject *self, PyObject *args) return pTuple; } + static PyObject *stop_simulator(PyObject *self, PyObject *args) { gpi_sim_end(); return Py_BuildValue("s", "OK!"); } + +static PyObject *deregister_callback(PyObject *self, PyObject *args) +{ + FENTER + FEXIT + return Py_BuildValue("s", "OK!"); +} + + static PyObject *create_clock(PyObject *self, PyObject *args) { gpi_sim_hdl hdl; @@ -620,6 +634,7 @@ static PyObject *create_clock(PyObject *self, PyObject *args) return rv; } + static PyObject *stop_clock(PyObject *self, PyObject *args) { gpi_clock_hdl clk_hdl; diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 4794336a..27060439 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -71,8 +71,7 @@ static PyObject *iterate_signals(PyObject *self, PyObject *args); static PyObject *next(PyObject *self, PyObject *args); static PyObject *get_sim_time(PyObject *self, PyObject *args); -// static PyObject *deregister_callback(PyObject *self, PyObject *args); -// static PyObject *register_timed_callback(PyObject *self, PyObject *args, PyObject *keywds); +static PyObject *deregister_callback(PyObject *self, PyObject *args); static PyMethodDef SimulatorMethods[] = { {"log_msg", log_msg, METH_VARARGS, "Log a message"}, @@ -95,7 +94,7 @@ static PyMethodDef SimulatorMethods[] = { // FIXME METH_NOARGS => initialization from incompatible pointer type {"get_sim_time", get_sim_time, METH_VARARGS, "Get the current simulation time as a float"}, - // {"deregister_callback", deregister_callback, METH_VARARGS, "Register"}, + {"deregister_callback", deregister_callback, METH_VARARGS, "Remove a registered callback"}, {NULL, NULL, 0, NULL} /* Sentinel */ }; From 5b043206c4c78f912ac244c42419518c8f650559 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 28 Jun 2013 12:19:39 +0100 Subject: [PATCH 0109/2656] Issue #16: Derive ClockCycles and RisingEdge from Edge to inherit unprime method --- cocotb/triggers.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index d74fbaa6..18d39150 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -94,8 +94,14 @@ def __init__(self, signal): self.signal = signal def prime(self, callback): + """Register notification of a value change via a callback""" self.cbhdl = simulator.register_value_change_callback(self.signal._handle, callback, self) + def unprime(self): + """Unregister a prior registered value change callback""" + simulator.deregister_callback(self.cbhdl) + Trigger.unprime(self) + def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal.name @@ -143,7 +149,7 @@ def prime(self, callback): def __str__(self): return self.__class__.__name__ + "(nexttimestep)" -class RisingEdge(Trigger): +class RisingEdge(Edge): """ Execution will resume when a rising edge occurs on the provided signal """ @@ -166,7 +172,7 @@ def _check(obj): def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal.name -class ClockCycles(Trigger): +class ClockCycles(Edge): """ Execution will resume after N rising edges """ From b5c11915694eddc38c22606b1c263903bd86f8f6 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 28 Jun 2013 16:07:37 +0100 Subject: [PATCH 0110/2656] Issue #16: Convert the world to have one handle type to the vpi world, clock still to do --- lib/gpi/gpi.h | 26 +++--- lib/simulator/simulatormodule.c | 21 +++-- lib/simulator/simulatormodule.h | 2 +- lib/vpi_shim/gpi_vpi.c | 145 +++++++++++++++++++++++--------- 4 files changed, 136 insertions(+), 58 deletions(-) diff --git a/lib/gpi/gpi.h b/lib/gpi/gpi.h index cf85b5c6..1857bc7d 100644 --- a/lib/gpi/gpi.h +++ b/lib/gpi/gpi.h @@ -79,9 +79,12 @@ we have to create a process with the signal on the sensitivity list to imitate a EXTERN_C_START // Define a type for our simulation handle. -typedef struct __gpi_sim_hdl *gpi_sim_hdl; +typedef struct gpi_sim_hdl_s { + void *sim_hdl; +} gpi_sim_hdl_t, *gpi_sim_hdl; + // Define a callback handle type for registered callbacks. -typedef struct __gpi_cb_hdl *gpi_cb_hdl; +//typedef struct __gpi_sim_hdl *gpi_sim_hdl; // Define a handle type for iterators typedef struct __gpi_iterator_hdl *gpi_iterator_hdl; // Define a type of a clock object @@ -92,7 +95,6 @@ typedef struct gpi_clock_s { unsigned int curr_cycle; bool exit; gpi_sim_hdl sim_hdl; - gpi_cb_hdl cb_hdl; } gpi_clock_t; typedef gpi_clock_t *gpi_clock_hdl; @@ -138,16 +140,16 @@ char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl); void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value); void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str); // String of binary char(s) [1, 0, x, z] -// The callback registering functions all return a gpi_cb_hdl; -gpi_cb_hdl gpi_register_sim_start_callback (int (*gpi_function)(void *), void *gpi_cb_data); -gpi_cb_hdl gpi_register_sim_end_callback (int (*gpi_function)(void *), void *gpi_cb_data); -gpi_cb_hdl gpi_register_timed_callback (int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps); -gpi_cb_hdl gpi_register_value_change_callback (int (*gpi_function)(void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl); -gpi_cb_hdl gpi_register_readonly_callback (int (*gpi_function)(void *), void *gpi_cb_data); -gpi_cb_hdl gpi_register_nexttime_callback (int (*gpi_function)(void *), void *gpi_cb_data); -gpi_cb_hdl gpi_register_readwrite_callback (int (*gpi_function)(void *), void *gpi_cb_data); +// The callback registering functions all return a gpi_sim_hdl; +gpi_sim_hdl gpi_register_sim_start_callback (int (*gpi_function)(void *), void *gpi_cb_data); +gpi_sim_hdl gpi_register_sim_end_callback (int (*gpi_function)(void *), void *gpi_cb_data); +gpi_sim_hdl gpi_register_timed_callback (int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps); +gpi_sim_hdl gpi_register_value_change_callback (int (*gpi_function)(void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl); +gpi_sim_hdl gpi_register_readonly_callback (int (*gpi_function)(void *), void *gpi_cb_data); +gpi_sim_hdl gpi_register_nexttime_callback (int (*gpi_function)(void *), void *gpi_cb_data); +gpi_sim_hdl gpi_register_readwrite_callback (int (*gpi_function)(void *), void *gpi_cb_data); -int gpi_deregister_callback(gpi_cb_hdl gpi_hdl); +int gpi_deregister_callback(gpi_sim_hdl gpi_hdl); gpi_clock_hdl gpi_clock_register(gpi_sim_hdl sim_hdl, int period, unsigned int cycles); void gpi_clock_unregister(gpi_clock_hdl clock); diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 44981738..a4d03620 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -136,7 +136,7 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; uint64_t time_ps; - gpi_cb_hdl cb_hdl; + gpi_sim_hdl cb_hdl; gpi_sim_hdl sig_hdl; char *result; PyObject *retstr; @@ -195,7 +195,7 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; uint64_t time_ps; - gpi_cb_hdl cb_hdl; + gpi_sim_hdl cb_hdl; gpi_sim_hdl sig_hdl; char *result; PyObject *retstr; @@ -254,7 +254,7 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; uint64_t time_ps; - gpi_cb_hdl cb_hdl; + gpi_sim_hdl cb_hdl; gpi_sim_hdl sig_hdl; char *result; PyObject *retstr; @@ -317,7 +317,7 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; uint64_t time_ps; - gpi_cb_hdl cb_hdl; + gpi_sim_hdl cb_hdl; p_callback_data callback_data_p; @@ -383,7 +383,7 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; uint64_t time_ps; - gpi_cb_hdl cb_hdl; + gpi_sim_hdl cb_hdl; gpi_sim_hdl sig_hdl; char *result; PyObject *retstr; @@ -596,7 +596,18 @@ static PyObject *stop_simulator(PyObject *self, PyObject *args) static PyObject *deregister_callback(PyObject *self, PyObject *args) { + gpi_sim_hdl hdl; + PyObject *pSihHdl; + int ret; + FENTER + + pSihHdl = PyTuple_GetItem(args, 0); + hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(pSihHdl); + + if (!gpi_deregister_callback(hdl)) + return NULL; + FEXIT return Py_BuildValue("s", "OK!"); } diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 27060439..6006393c 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -45,7 +45,7 @@ typedef struct t_callback_data { PyObject *function; // Fuction to call when the callback fires PyObject *args; // The arguments to call the function with PyObject *kwargs; // Keyword arguments to call the function with - gpi_cb_hdl cb_hdl; + gpi_sim_hdl cb_hdl; } s_callback_data, *p_callback_data; static PyObject *log_msg(PyObject *self, PyObject *args); diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 409fdada..30618669 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -35,8 +35,28 @@ #include #include -static gpi_cb_hdl sim_init_cb; -static gpi_cb_hdl sim_finish_cb; +#define gpi_container_of(_address, _type, _member) \ + ((_type *)((uintptr_t)(_address) - \ + (uintptr_t)(&((_type *)0)->_member))) + +static gpi_sim_hdl sim_init_cb; +static gpi_sim_hdl sim_finish_cb; + +void gpi_free_handle(gpi_sim_hdl gpi_hdl) +{ + free(gpi_hdl); +} + +static gpi_sim_hdl gpi_alloc_handle() +{ + gpi_sim_hdl new_hdl = malloc(sizeof(*new_hdl)); + if (!new_hdl) { + LOG_CRITICAL("VPI: Could not allocate handle\n"); + exit(1); + } + + return new_hdl; +} // Handle related functions gpi_sim_hdl gpi_get_root_handle() @@ -44,6 +64,7 @@ gpi_sim_hdl gpi_get_root_handle() FENTER vpiHandle root; vpiHandle iterator; + gpi_sim_hdl rv; // vpi_iterate with a ref of NULL returns the top level module iterator = vpi_iterate(vpiModule, NULL); @@ -53,14 +74,19 @@ gpi_sim_hdl gpi_get_root_handle() if (root != NULL && !vpi_free_object(iterator)) { LOG_WARN("VPI: Attempting to free root iterator failed!"); } + + rv = gpi_alloc_handle(); + rv->sim_hdl = root; + FEXIT - return (gpi_sim_hdl)root; + return rv; } gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) { FENTER gpi_sim_hdl rv; + vpiHandle obj; int len; char *buff; if (name) @@ -73,8 +99,12 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) } strncpy(buff, name, len); - rv = (gpi_sim_hdl)vpi_handle_by_name(buff, (vpiHandle)parent); + obj = vpi_handle_by_name(buff, (vpiHandle)(parent->sim_hdl)); free(buff); + + rv = gpi_alloc_handle(); + rv->sim_hdl = obj; + FEXIT return rv; } @@ -87,18 +117,24 @@ gpi_iterator_hdl gpi_iterate(gpi_sim_hdl base) { vpiHandle iterator; - iterator = vpi_iterate(vpiNet, (vpiHandle)base); + iterator = vpi_iterate(vpiNet, (vpiHandle)(base->sim_hdl)); FEXIT return (gpi_iterator_hdl)iterator; } // Returns NULL when there are no more objects -gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) { +gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) +{ FENTER vpiHandle result; + gpi_sim_hdl rv = gpi_alloc_handle(); - result = vpi_scan((vpiHandle) iterator); + rv->sim_hdl = vpi_scan((vpiHandle) iterator); + if (!rv->sim_hdl) { + gpi_free_handle(rv); + rv = NULL; + } // FIXME do we need to free the iterator handle? // Icarus complains about this @@ -107,7 +143,7 @@ gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) { // } FEXIT - return (gpi_sim_hdl)result; + return rv; } // double gpi_get_sim_time() @@ -134,7 +170,7 @@ void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) * may be NULL, in this case. This is like a blocking assignment * in behavioral code. */ - vpi_put_value((vpiHandle)gpi_hdl, value_p, NULL, vpiNoDelay); + vpi_put_value((vpiHandle)(gpi_hdl->sim_hdl), value_p, NULL, vpiNoDelay); FEXIT } @@ -165,7 +201,7 @@ void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) * may be NULL, in this case. This is like a blocking assignment * in behavioral code. */ - vpi_put_value((vpiHandle)gpi_hdl, value_p, NULL, vpiNoDelay); + vpi_put_value((vpiHandle)(gpi_hdl->sim_hdl), value_p, NULL, vpiNoDelay); free(buff); FEXIT @@ -202,7 +238,7 @@ char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) s_vpi_value value_s = {vpiBinStrVal}; p_vpi_value value_p = &value_s; - vpi_get_value((vpiHandle)gpi_hdl, value_p); + vpi_get_value((vpiHandle)(gpi_hdl->sim_hdl), value_p); char *result = gpi_copy_name(value_p->value.str); FEXIT @@ -212,7 +248,7 @@ char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl) { FENTER - const char *name = vpi_get_str(vpiFullName, (vpiHandle)gpi_hdl); + const char *name = vpi_get_str(vpiFullName, (vpiHandle)(gpi_hdl->sim_hdl)); char *result = gpi_copy_name(name); FEXIT return result; @@ -221,7 +257,7 @@ char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl) char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl) { FENTER - const char *name = vpi_get_str(vpiType, (vpiHandle)gpi_hdl); + const char *name = vpi_get_str(vpiType, (vpiHandle)(gpi_hdl->sim_hdl)); char *result = gpi_copy_name(name); FEXIT return result; @@ -238,8 +274,27 @@ typedef struct t_vpi_cb_user_data { int (*gpi_cleanup)(struct t_vpi_cb_user_data *); vpiHandle cb_hdl; s_vpi_value cb_value; + gpi_sim_hdl_t gpi_hdl; } s_vpi_cb_user_data, *p_vpi_cb_user_data; +// Ask the attached simulator to return the user pointer +// that was given when the callback was registered +// Useful for operating on the data before the callback +// has fired since we only have the handle in hand +static p_vpi_cb_user_data gpi_get_user_data(gpi_sim_hdl hdl) +{ + p_vpi_cb_user_data user_data; + s_cb_data cbdata; + FENTER + + vpi_get_cb_info((vpiHandle)hdl, &cbdata); + + user_data = (p_vpi_cb_user_data)cbdata.user_data; + + FEXIT + return user_data; +} + PLI_INT32 handle_vpi_callback(p_cb_data cb_data) { @@ -259,15 +314,26 @@ PLI_INT32 handle_vpi_callback(p_cb_data cb_data) return rv; }; -// remove a callback without freeing the user data -// (called by callback to clean up) -// vpi_get_cb_info could be used to free user data if -// the callback hasn't fired... -int gpi_deregister_callback(gpi_cb_hdl gpi_hdl) +// Deregisters a callback and removes the user +// data, if we do not remove the user data +// then all references to this are lost +// Only needs to be called before a callback +// fires, see gpr_free_on_time and gpi_free_recurring +// for way to handle this during callback execution +int gpi_deregister_callback(gpi_sim_hdl gpi_hdl) { + p_vpi_cb_user_data user_data; FENTER - // This destroys them memory allocated for the handle - PLI_INT32 rc = vpi_remove_cb((vpiHandle)gpi_hdl); + // We should be able to user gpi_get_user_data + // but this is not implemented in ICARUS + // and gets upset on VCS. So instead we + // do some pointer magic. + + user_data = gpi_container_of(gpi_hdl, s_vpi_cb_user_data, gpi_hdl); + PLI_INT32 rc = vpi_remove_cb(user_data->cb_hdl); + + free(user_data); + FEXIT return rc; } @@ -310,7 +376,7 @@ static int gpi_free_recurring(p_vpi_cb_user_data user_data) return rc; } -gpi_cb_hdl gpi_register_value_change_callback(int (*gpi_function)(void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl) +gpi_sim_hdl gpi_register_value_change_callback(int (*gpi_function)(void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl) { FENTER s_cb_data cb_data_s; @@ -334,7 +400,7 @@ gpi_cb_hdl gpi_register_value_change_callback(int (*gpi_function)(void *), void cb_data_s.reason = cbValueChange; cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = (vpiHandle)gpi_hdl; + cb_data_s.obj = (vpiHandle)(gpi_hdl->sim_hdl); cb_data_s.time = &vpi_time_s; cb_data_s.value = &user_data->cb_value; cb_data_s.user_data = (char *)user_data; @@ -342,11 +408,11 @@ gpi_cb_hdl gpi_register_value_change_callback(int (*gpi_function)(void *), void user_data->cb_hdl = vpi_register_cb(&cb_data_s); FEXIT - return (gpi_cb_hdl)user_data->cb_hdl; + return &user_data->gpi_hdl; } -gpi_cb_hdl gpi_register_readonly_callback(int (*gpi_function)(void *), void *gpi_cb_data) +gpi_sim_hdl gpi_register_readonly_callback(int (*gpi_function)(void *), void *gpi_cb_data) { FENTER s_cb_data cb_data_s; @@ -377,10 +443,10 @@ gpi_cb_hdl gpi_register_readonly_callback(int (*gpi_function)(void *), void *gpi user_data->cb_hdl = vpi_register_cb(&cb_data_s); FEXIT - return (gpi_cb_hdl)user_data->cb_hdl; + return &user_data->gpi_hdl; } -gpi_cb_hdl gpi_register_readwrite_callback(int (*gpi_function)(void *), void *gpi_cb_data) +gpi_sim_hdl gpi_register_readwrite_callback(int (*gpi_function)(void *), void *gpi_cb_data) { FENTER s_cb_data cb_data_s; @@ -409,12 +475,11 @@ gpi_cb_hdl gpi_register_readwrite_callback(int (*gpi_function)(void *), void *gp cb_data_s.user_data = (char *)user_data; user_data->cb_hdl = vpi_register_cb(&cb_data_s); - FEXIT - return (gpi_cb_hdl)user_data->cb_hdl; + return &user_data->gpi_hdl; } -gpi_cb_hdl gpi_register_nexttime_callback(int (*gpi_function)(void *), void *gpi_cb_data) +gpi_sim_hdl gpi_register_nexttime_callback(int (*gpi_function)(void *), void *gpi_cb_data) { FENTER s_cb_data cb_data_s; @@ -445,10 +510,10 @@ gpi_cb_hdl gpi_register_nexttime_callback(int (*gpi_function)(void *), void *gpi user_data->cb_hdl = vpi_register_cb(&cb_data_s); FEXIT - return (gpi_cb_hdl)user_data->cb_hdl; + return &user_data->gpi_hdl; } -gpi_cb_hdl gpi_register_timed_callback(int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps) +gpi_sim_hdl gpi_register_timed_callback(int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps) { FENTER s_cb_data cb_data_s; @@ -479,10 +544,10 @@ gpi_cb_hdl gpi_register_timed_callback(int (*gpi_function)(void *), void *gpi_cb user_data->cb_hdl = vpi_register_cb(&cb_data_s); FEXIT - return (gpi_cb_hdl)user_data->cb_hdl; + return &user_data->gpi_hdl; } -gpi_cb_hdl gpi_register_sim_start_callback(int (*gpi_function)(void *), void *gpi_cb_data) +gpi_sim_hdl gpi_register_sim_start_callback(int (*gpi_function)(void *), void *gpi_cb_data) { FENTER @@ -508,11 +573,11 @@ gpi_cb_hdl gpi_register_sim_start_callback(int (*gpi_function)(void *), void *gp user_data->cb_hdl = vpi_register_cb(&cb_data_s); FEXIT - return (gpi_cb_hdl)user_data->cb_hdl; + return &user_data->gpi_hdl; } -gpi_cb_hdl gpi_register_sim_end_callback(int (*gpi_function)(void *), void *gpi_cb_data) +gpi_sim_hdl gpi_register_sim_end_callback(int (*gpi_function)(void *), void *gpi_cb_data) { FENTER @@ -538,7 +603,7 @@ gpi_cb_hdl gpi_register_sim_end_callback(int (*gpi_function)(void *), void *gpi_ user_data->cb_hdl = vpi_register_cb(&cb_data_s); FEXIT - return (gpi_cb_hdl)user_data->cb_hdl; + return &user_data->gpi_hdl; } @@ -551,8 +616,8 @@ int gpi_clock_handler(void *clock) hdl->value = !hdl->value; gpi_set_signal_value_int(hdl->sim_hdl, hdl->value); - gpi_cb_hdl edge = gpi_register_timed_callback(gpi_clock_handler, hdl, hdl->period); - hdl->cb_hdl = edge; + gpi_sim_hdl edge = gpi_register_timed_callback(gpi_clock_handler, hdl, hdl->period); + hdl->sim_hdl = edge; hdl->curr_cycle++; } @@ -572,8 +637,8 @@ gpi_clock_hdl gpi_clock_register(gpi_sim_hdl sim_hdl, int period, unsigned int c hdl->curr_cycle = 0; gpi_set_signal_value_int(hdl->sim_hdl, hdl->value); - gpi_cb_hdl edge = gpi_register_timed_callback(gpi_clock_handler, hdl, hdl->period); - hdl->cb_hdl = edge; + gpi_sim_hdl edge = gpi_register_timed_callback(gpi_clock_handler, hdl, hdl->period); + hdl->sim_hdl = edge; FEXIT return hdl; From c0f62752781efd9cf74d6c3f4da512c2bed96358 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 28 Jun 2013 17:59:05 +0100 Subject: [PATCH 0111/2656] Issue #16: Move cleanup of callbacks to destruction not after the callback has happend --- lib/vpi_shim/gpi_vpi.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 30618669..46a1e77b 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -275,6 +275,7 @@ typedef struct t_vpi_cb_user_data { vpiHandle cb_hdl; s_vpi_value cb_value; gpi_sim_hdl_t gpi_hdl; + bool called; } s_vpi_cb_user_data, *p_vpi_cb_user_data; // Ask the attached simulator to return the user pointer @@ -304,11 +305,12 @@ PLI_INT32 handle_vpi_callback(p_cb_data cb_data) p_vpi_cb_user_data user_data; user_data = (p_vpi_cb_user_data)cb_data->user_data; + user_data->called = true; rv = user_data->gpi_function(user_data->gpi_cb_data); - // Call destuctor - if (user_data->gpi_cleanup) - user_data->gpi_cleanup(user_data); + // We delay freeing until the top level owner + // calls gpi_deregister_callback + // this gives an implicit serialisation FEXIT return rv; @@ -330,12 +332,12 @@ int gpi_deregister_callback(gpi_sim_hdl gpi_hdl) // do some pointer magic. user_data = gpi_container_of(gpi_hdl, s_vpi_cb_user_data, gpi_hdl); - PLI_INT32 rc = vpi_remove_cb(user_data->cb_hdl); - free(user_data); + if (user_data->gpi_cleanup) + user_data->gpi_cleanup(user_data); FEXIT - return rc; + return 1; } // Call when the handle relates to a one time callback @@ -351,6 +353,11 @@ static int gpi_free_one_time(p_vpi_cb_user_data user_data) exit(1); } + // If the callback has not been called we also need to call + // remove as well + if (!user_data->called) + vpi_remove_cb(cb_hdl); + rc = vpi_free_object(cb_hdl); free(user_data); FEXIT @@ -385,7 +392,7 @@ gpi_sim_hdl gpi_register_value_change_callback(int (*gpi_function)(void *), void p_vpi_cb_user_data user_data; // Freed when callback fires or the callback is deregistered - user_data = (p_vpi_cb_user_data)calloc(sizeof(s_vpi_cb_user_data), 1); + user_data = (p_vpi_cb_user_data)malloc(sizeof(s_vpi_cb_user_data)); if (user_data == NULL) { LOG_WARN("VPI: Attempting allocate user_data for %s failed!", __func__); } @@ -394,6 +401,7 @@ gpi_sim_hdl gpi_register_value_change_callback(int (*gpi_function)(void *), void user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_recurring; user_data->cb_value.format = vpiIntVal; + user_data->called = false; vpi_time_s.type = vpiSuppressTime; vpi_value_s.format = vpiIntVal; @@ -428,6 +436,7 @@ gpi_sim_hdl gpi_register_readonly_callback(int (*gpi_function)(void *), void *gp user_data->gpi_cb_data = gpi_cb_data; user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_one_time; + user_data->called = false; vpi_time_s.type = vpiSimTime; vpi_time_s.high = 0; @@ -462,6 +471,7 @@ gpi_sim_hdl gpi_register_readwrite_callback(int (*gpi_function)(void *), void *g user_data->gpi_cb_data = gpi_cb_data; user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_one_time; + user_data->called = false; vpi_time_s.type = vpiSimTime; vpi_time_s.high = 0; @@ -495,6 +505,7 @@ gpi_sim_hdl gpi_register_nexttime_callback(int (*gpi_function)(void *), void *gp user_data->gpi_cb_data = gpi_cb_data; user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_one_time; + user_data->called = false; vpi_time_s.type = vpiSimTime; vpi_time_s.high = 0; @@ -529,6 +540,7 @@ gpi_sim_hdl gpi_register_timed_callback(int (*gpi_function)(void *), void *gpi_c user_data->gpi_cb_data = gpi_cb_data; user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_one_time; + user_data->called = false; vpi_time_s.type = vpiSimTime; vpi_time_s.high = (PLI_UINT32)(time_ps>>32); @@ -563,6 +575,7 @@ gpi_sim_hdl gpi_register_sim_start_callback(int (*gpi_function)(void *), void *g user_data->gpi_cb_data = gpi_cb_data; user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_one_time; + user_data->called = false; cb_data_s.reason = cbStartOfSimulation; cb_data_s.cb_rtn = handle_vpi_callback; @@ -593,6 +606,7 @@ gpi_sim_hdl gpi_register_sim_end_callback(int (*gpi_function)(void *), void *gpi user_data->gpi_cb_data = gpi_cb_data; user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_one_time; + user_data->called = false; cb_data_s.reason = cbEndOfSimulation; cb_data_s.cb_rtn = handle_vpi_callback; From 935c9c5735eb7b33765e539bddf493b8396737a3 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 28 Jun 2013 18:27:28 +0100 Subject: [PATCH 0112/2656] Issue #16: Implement unprime from the scheduler --- cocotb/scheduler.py | 12 +++++++-- cocotb/triggers.py | 27 ++++++++++++++------- examples/functionality/tests/test_cocotb.py | 16 +++++++++++- 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index bbc030c3..386f6f95 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -73,11 +73,14 @@ def react(self, trigger): while self._scheduling: coroutine = self._scheduling.pop(0) - trigger.clearpeers() + del_list = trigger.clearpeers() + while del_list: + self.remove(del_list.pop(0)) self.schedule(coroutine, trigger=trigger) self.log.debug("Scheduled coroutine %s" % (coroutine.__name__)) - self.log.debug("Completed scheduling loop, still waiting on:") + if self.waiting: + self.log.debug("Completed scheduling loop, still waiting on:") for trig, routines in self.waiting.items(): self.log.debug("\t%s: [%s]" % (str(trig).ljust(30), " ".join([routine.__name__ for routine in routines]))) @@ -111,6 +114,11 @@ def add(self, coroutine): self.schedule(coroutine) return coroutine + def remove(self, trigger): + """Remove a trigger from the list of pending coroutines""" + self.waiting.pop(trigger) + trigger.unprime() + def schedule(self, coroutine, trigger=None): """ diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 18d39150..83dd6e99 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -30,6 +30,7 @@ import simulator + class TriggerException(Exception): pass @@ -44,18 +45,15 @@ def unprime(self): if self.peers: self.peers = None - def addpeers(self, peers): """Store any relate triggers""" - self.peers = peers + for elem in peers: + if elem is not self: + self.peers.append(elem) def clearpeers(self): """Call _clearpeers on each trigger that is not me""" - if self.peers: - while self.peers: - trigger = self.peers.pop(0) - if trigger is not self: - trigger.unprime() + return self.peers def __del__(self): """Ensure if a trigger drops out of scope we remove any pending callbacks""" @@ -78,8 +76,10 @@ def prime(self, callback): def unprime(self): """Unregister a prior registered timed callback""" - simulator.deregister_callback(self.cbhdl) Trigger.unprime(self) + if self.cbhdl is not None: + simulator.deregister_callback(self.cbhdl) + self.cbhdl = None def __str__(self): return self.__class__.__name__ + "(%dps)" % self.time_ps @@ -99,8 +99,10 @@ def prime(self, callback): def unprime(self): """Unregister a prior registered value change callback""" - simulator.deregister_callback(self.cbhdl) Trigger.unprime(self) + if self.cbhdl is not None: + simulator.deregister_callback(self.cbhdl) + self.cbhdl = None def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal.name @@ -132,6 +134,13 @@ def __init__(self): def prime(self, callback): self.cbhdl = simulator.register_rwsynch_callback(callback, self) + def unprime(self): + """Unregister a prior registered timed callback""" + if self.cbhdl is not None: + simulator.deregister_callback(self.cbhdl) + self.cbhdl = None + Trigger.unprime(self) + def __str__(self): return self.__class__.__name__ + "(readwritesync)" diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 93e84359..89b1901d 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -63,10 +63,24 @@ def test_function_not_a_coroutine_fork(dut): cocotb.fork(function_not_a_coroutine()) yield Timer(500) +@cocotb.coroutine +def clock_gen(clock): + """Example clock gen for test use""" + for i in range(5): + clock <= 0 + yield Timer(100) + clock <= 1 + yield Timer(100) + clock.log.warning("Clock generator finished!") + @cocotb.test(expect_fail=False) def test_yield_list(dut): """Example of yeilding on a list of triggers""" - yield [Timer(500), Timer(1000)] + clock = dut.clk; + cocotb.scheduler.add(clock_gen(clock)) + yield [Timer(5000), Timer(6000)] + + yield Timer(10000) @cocotb.test(expect_fail=True) def test_duplicate_yield(dut): From f7eecb127368332d2e3da36bd3a0fd0402aef2d5 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 28 Jun 2013 18:34:28 +0100 Subject: [PATCH 0113/2656] Improve class hiearachy for triggers --- cocotb/triggers.py | 67 +++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 39 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 83dd6e99..30eac6ed 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -59,21 +59,16 @@ def __del__(self): """Ensure if a trigger drops out of scope we remove any pending callbacks""" self.unprime() -class Timer(Trigger): +class GPITrigger(Trigger): """ Execution will resume when the specified time period expires Consumes simulation time """ - def __init__(self, time_ps): + def __init__(self): Trigger.__init__(self) - self.time_ps = time_ps self.cbhdl = None - def prime(self, callback): - """Register for a timed callback""" - self.cbhdl = simulator.register_timed_callback(self.time_ps, callback, self) - def unprime(self): """Unregister a prior registered timed callback""" Trigger.unprime(self) @@ -81,40 +76,46 @@ def unprime(self): simulator.deregister_callback(self.cbhdl) self.cbhdl = None + +class Timer(GPITrigger): + """ + Execution will resume when the specified time period expires + + Consumes simulation time + """ + def __init__(self, time_ps): + GPITrigger.__init__(self) + self.time_ps = time_ps + + def prime(self, callback): + """Register for a timed callback""" + self.cbhdl = simulator.register_timed_callback(self.time_ps, callback, self) + def __str__(self): return self.__class__.__name__ + "(%dps)" % self.time_ps -class Edge(Trigger): +class Edge(GPITrigger): """ Execution will resume when an edge occurs on the provided signal """ def __init__(self, signal): - Trigger.__init__(self) - self.cbhdl = None + GPITrigger.__init__(self) self.signal = signal def prime(self, callback): """Register notification of a value change via a callback""" self.cbhdl = simulator.register_value_change_callback(self.signal._handle, callback, self) - def unprime(self): - """Unregister a prior registered value change callback""" - Trigger.unprime(self) - if self.cbhdl is not None: - simulator.deregister_callback(self.cbhdl) - self.cbhdl = None - def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal.name -class ReadOnly(Trigger): +class ReadOnly(GPITrigger): """ Execution will resume when the readonly portion of the sim cycles is readched """ def __init__(self): - Trigger.__init__(self) - self.cbhdl = None + GPITrigger.__init__(self) def prime(self, callback): self.cbhdl = simulator.register_readonly_callback(callback, self) @@ -122,35 +123,26 @@ def prime(self, callback): def __str__(self): return self.__class__.__name__ + "(readonly)" -class ReadWrite(Trigger): +class ReadWrite(GPITrigger): """ Execution will resume when the readwrite porttion of the sim cycles is reached """ def __init__(self): - Trigger.__init__(self) - self.cbhdl = None + GPITrigger.__init__(self) def prime(self, callback): self.cbhdl = simulator.register_rwsynch_callback(callback, self) - def unprime(self): - """Unregister a prior registered timed callback""" - if self.cbhdl is not None: - simulator.deregister_callback(self.cbhdl) - self.cbhdl = None - Trigger.unprime(self) - def __str__(self): return self.__class__.__name__ + "(readwritesync)" -class NextTimeStep(Trigger): +class NextTimeStep(GPITrigger): """ Execution will resume when the next time step is started """ def __init__(self): - Trigger.__init__(self) - self.cbhdl = None + GPITrigger.__init__(self) def prime(self, callback): self.cbhdl = simulator.register_nextstep_callback(callback, self) @@ -163,9 +155,7 @@ class RisingEdge(Edge): Execution will resume when a rising edge occurs on the provided signal """ def __init__(self, signal): - Trigger.__init__(self) - self.cbhdl = None - self.signal = signal + Edge.__init__(self, signal) def prime(self, callback): self._callback = callback @@ -186,9 +176,7 @@ class ClockCycles(Edge): Execution will resume after N rising edges """ def __init__(self, signal, num_cycles): - Trigger.__init__(self) - self.cbhdl = None - self.signal = signal + Edge.__init__(self, signal) self.num_cycles = num_cycles def prime(self, callback): @@ -285,4 +273,5 @@ def _cb(): self.log.debug("Primed on %s" % self._coroutine.__name__) def __str__(self): + self.signal = signal return self.__class__.__name__ + "(%s)" % self._coroutine.__name__ From 1013c82a73d4c24a29722ca5dc909e3412204f6c Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 1 Jul 2013 15:08:30 +0100 Subject: [PATCH 0114/2656] cleanup --- cocotb/triggers.py | 2 +- examples/functionality/tests/test_cocotb.py | 2 +- lib/vpi_shim/gpi_vpi.c | 10 ++++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 30eac6ed..d1ba5c11 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -39,6 +39,7 @@ class Trigger(object): def __init__(self): self.log = logging.getLogger("cocotb.%s.0x%x" % (self.__class__.__name__, id(self))) self.peers = [] + self.signal = None def unprime(self): """Remove any pending callbacks if necessary""" @@ -273,5 +274,4 @@ def _cb(): self.log.debug("Primed on %s" % self._coroutine.__name__) def __str__(self): - self.signal = signal return self.__class__.__name__ + "(%s)" % self._coroutine.__name__ diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 89b1901d..995fcaf2 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -78,7 +78,7 @@ def test_yield_list(dut): """Example of yeilding on a list of triggers""" clock = dut.clk; cocotb.scheduler.add(clock_gen(clock)) - yield [Timer(5000), Timer(6000)] + yield [Timer(1000), Timer(2000)] yield Timer(10000) diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 46a1e77b..1f7b1c3e 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -346,7 +346,6 @@ int gpi_deregister_callback(gpi_sim_hdl gpi_hdl) static int gpi_free_one_time(p_vpi_cb_user_data user_data) { FENTER - PLI_INT32 rc; vpiHandle cb_hdl = user_data->cb_hdl; if (!cb_hdl) { LOG_ERROR("VPI: %s passed a NULL pointer\n", __func__); @@ -355,13 +354,16 @@ static int gpi_free_one_time(p_vpi_cb_user_data user_data) // If the callback has not been called we also need to call // remove as well - if (!user_data->called) + + if (!user_data->called) { vpi_remove_cb(cb_hdl); + } else { + vpi_free_object(cb_hdl); + } - rc = vpi_free_object(cb_hdl); free(user_data); FEXIT - return rc; + return 1; } // Call when the handle relates to recurring callback From aef822b0f99ec3ffd8d489dde0ce9319dbfce194 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 1 Jul 2013 15:08:59 +0100 Subject: [PATCH 0115/2656] change clock driver to use common sim handle from Issue #16 --- lib/gpi/gpi.h | 18 +-------- lib/simulator/simulatormodule.c | 16 +------- lib/vpi_shim/gpi_vpi.c | 67 ++++++++++++++++++++++----------- 3 files changed, 48 insertions(+), 53 deletions(-) diff --git a/lib/gpi/gpi.h b/lib/gpi/gpi.h index 1857bc7d..adc045cd 100644 --- a/lib/gpi/gpi.h +++ b/lib/gpi/gpi.h @@ -83,22 +83,8 @@ typedef struct gpi_sim_hdl_s { void *sim_hdl; } gpi_sim_hdl_t, *gpi_sim_hdl; -// Define a callback handle type for registered callbacks. -//typedef struct __gpi_sim_hdl *gpi_sim_hdl; // Define a handle type for iterators typedef struct __gpi_iterator_hdl *gpi_iterator_hdl; -// Define a type of a clock object -typedef struct gpi_clock_s { - int period; - int value; - unsigned int max_cycles; - unsigned int curr_cycle; - bool exit; - gpi_sim_hdl sim_hdl; -} gpi_clock_t; - -typedef gpi_clock_t *gpi_clock_hdl; - // Functions for controlling/querying the simulation state @@ -150,8 +136,8 @@ gpi_sim_hdl gpi_register_nexttime_callback (int (*gpi_function)(vo gpi_sim_hdl gpi_register_readwrite_callback (int (*gpi_function)(void *), void *gpi_cb_data); int gpi_deregister_callback(gpi_sim_hdl gpi_hdl); -gpi_clock_hdl gpi_clock_register(gpi_sim_hdl sim_hdl, int period, unsigned int cycles); -void gpi_clock_unregister(gpi_clock_hdl clock); +gpi_sim_hdl gpi_clock_register(gpi_sim_hdl sim_hdl, int period, unsigned int cycles); +void gpi_clock_unregister(gpi_sim_hdl clock); EXTERN_C_END diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index a4d03620..e1a12ce8 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -135,11 +135,7 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; - uint64_t time_ps; - gpi_sim_hdl cb_hdl; - gpi_sim_hdl sig_hdl; char *result; - PyObject *retstr; PyGILState_STATE gstate; gstate = PyGILState_Ensure(); @@ -194,11 +190,7 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; - uint64_t time_ps; - gpi_sim_hdl cb_hdl; - gpi_sim_hdl sig_hdl; char *result; - PyObject *retstr; PyGILState_STATE gstate; gstate = PyGILState_Ensure(); @@ -254,8 +246,6 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; uint64_t time_ps; - gpi_sim_hdl cb_hdl; - gpi_sim_hdl sig_hdl; char *result; PyObject *retstr; @@ -317,7 +307,6 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; uint64_t time_ps; - gpi_sim_hdl cb_hdl; p_callback_data callback_data_p; @@ -383,7 +372,6 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; uint64_t time_ps; - gpi_sim_hdl cb_hdl; gpi_sim_hdl sig_hdl; char *result; PyObject *retstr; @@ -638,7 +626,7 @@ static PyObject *create_clock(PyObject *self, PyObject *args) PyObject *pCycles = PyTuple_GetItem(args, 2); mcycles = (unsigned int)PyLong_AsUnsignedLong(pCycles); - gpi_clock_hdl clk_hdl = gpi_clock_register(hdl, period, mcycles); + gpi_sim_hdl clk_hdl = gpi_clock_register(hdl, period, mcycles); PyObject *rv = Py_BuildValue("l", clk_hdl); PyGILState_Release(gstate); @@ -648,7 +636,7 @@ static PyObject *create_clock(PyObject *self, PyObject *args) static PyObject *stop_clock(PyObject *self, PyObject *args) { - gpi_clock_hdl clk_hdl; + gpi_sim_hdl clk_hdl; if (!PyArg_ParseTuple(args, "l", &clk_hdl)) return NULL; diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 1f7b1c3e..e0a23908 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -42,6 +42,32 @@ static gpi_sim_hdl sim_init_cb; static gpi_sim_hdl sim_finish_cb; +// callback user data used for VPI callbacks +// (mostly just a thin wrapper around the gpi_callback) +typedef struct t_vpi_cb_user_data { + void *gpi_cb_data; + int (*gpi_function)(void *); + int (*gpi_cleanup)(struct t_vpi_cb_user_data *); + vpiHandle cb_hdl; + s_vpi_value cb_value; + gpi_sim_hdl_t gpi_hdl; + bool called; +} s_vpi_cb_user_data, *p_vpi_cb_user_data; + +// Define a type of a clock object +typedef struct gpi_clock_s { + int period; + int value; + unsigned int max_cycles; + unsigned int curr_cycle; + bool exit; + gpi_sim_hdl_t gpi_hdl; /* Handle to pass back to called */ + gpi_sim_hdl clk_hdl; /* Handle for signal to operate on */ + gpi_sim_hdl cb_hdl; /* Handle for the current pending callback */ +} gpi_clock_t; + +typedef gpi_clock_t *gpi_clock_hdl; + void gpi_free_handle(gpi_sim_hdl gpi_hdl) { free(gpi_hdl); @@ -266,18 +292,6 @@ char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl) // Callback related functions -// callback user data used for VPI callbacks -// (mostly just a thin wrapper around the gpi_callback) -typedef struct t_vpi_cb_user_data { - void *gpi_cb_data; - int (*gpi_function)(void *); - int (*gpi_cleanup)(struct t_vpi_cb_user_data *); - vpiHandle cb_hdl; - s_vpi_value cb_value; - gpi_sim_hdl_t gpi_hdl; - bool called; -} s_vpi_cb_user_data, *p_vpi_cb_user_data; - // Ask the attached simulator to return the user pointer // that was given when the callback was registered // Useful for operating on the data before the callback @@ -626,18 +640,25 @@ gpi_sim_hdl gpi_register_sim_end_callback(int (*gpi_function)(void *), void *gpi int gpi_clock_handler(void *clock) { gpi_clock_hdl hdl = (gpi_clock_hdl)clock; + gpi_sim_hdl old_hdl; if (hdl->exit || (hdl->max_cycles == hdl->curr_cycle)) return; + /* Unregister/free the last callback that just fired */ + old_hdl = hdl->cb_hdl; + + printf("Clock triggered %d->%d\n", hdl->value, !hdl->value); + hdl->value = !hdl->value; - gpi_set_signal_value_int(hdl->sim_hdl, hdl->value); - gpi_sim_hdl edge = gpi_register_timed_callback(gpi_clock_handler, hdl, hdl->period); - hdl->sim_hdl = edge; + gpi_set_signal_value_int(hdl->clk_hdl, hdl->value); + hdl->cb_hdl = gpi_register_timed_callback(gpi_clock_handler, hdl, hdl->period); hdl->curr_cycle++; + + gpi_deregister_callback(old_hdl); } -gpi_clock_hdl gpi_clock_register(gpi_sim_hdl sim_hdl, int period, unsigned int cycles) +gpi_sim_hdl gpi_clock_register(gpi_sim_hdl sim_hdl, int period, unsigned int cycles) { FENTER @@ -647,22 +668,22 @@ gpi_clock_hdl gpi_clock_register(gpi_sim_hdl sim_hdl, int period, unsigned int c hdl->period = period; hdl->value = 0; - hdl->sim_hdl = sim_hdl; + hdl->clk_hdl = sim_hdl; hdl->exit = false; hdl->max_cycles = cycles; hdl->curr_cycle = 0; - gpi_set_signal_value_int(hdl->sim_hdl, hdl->value); - gpi_sim_hdl edge = gpi_register_timed_callback(gpi_clock_handler, hdl, hdl->period); - hdl->sim_hdl = edge; + gpi_set_signal_value_int(hdl->clk_hdl, hdl->value); + hdl->cb_hdl = gpi_register_timed_callback(gpi_clock_handler, hdl, hdl->period); FEXIT - return hdl; + return &hdl->gpi_hdl; } -void gpi_clock_unregister(gpi_clock_hdl clock) +void gpi_clock_unregister(gpi_sim_hdl clock) { - clock->exit = true; + gpi_clock_hdl hdl = gpi_container_of(clock, gpi_clock_t, gpi_hdl); + hdl->exit = true; } void register_embed(void) From 5a57e15c0c2093ff57e63906b9dca0581960eac7 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Mon, 1 Jul 2013 15:14:47 +0100 Subject: [PATCH 0116/2656] Preserve original PYTHONPATH variable --- makefiles/Makefile.icarus | 4 ++-- makefiles/Makefile.vcs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/makefiles/Makefile.icarus b/makefiles/Makefile.icarus index e4a1caa2..bcc22b2e 100644 --- a/makefiles/Makefile.icarus +++ b/makefiles/Makefile.icarus @@ -37,12 +37,12 @@ $(OBJ_DIR): .PHONY: sim sim: $(OBJ_DIR) $(VERILOG_SOURCES) iverilog -o $(OBJ_DIR)/sim.vvp $(VERILOG_SOURCES) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp .PHONY: gdb gdb: $(OBJ_DIR) $(VERILOG_SOURCES) iverilog -o $(OBJ_DIR)/sim.vvp $(VERILOG_SOURCES) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ gdb --args vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp diff --git a/makefiles/Makefile.vcs b/makefiles/Makefile.vcs index d54d98de..61198cc9 100644 --- a/makefiles/Makefile.vcs +++ b/makefiles/Makefile.vcs @@ -44,11 +44,11 @@ pli.tab : .PHONY: sim sim: $(OBJ_DIR) $(VERILOG_SOURCES) pli.tab - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ vcs -R +acc+1 +vpi -P pli.tab -sverilog $(EXTRA_ARGS) -debug -load libgpi.so $(VERILOG_SOURCES) dve: $(OBJ_DIR) $(VERILOG_SOURCES) pli.tab - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ vcs -R +acc+1 +vpi+1+assertion -P pli.tab -sverilog $(EXTRA_ARGS) -gui -load libgpi.so $(VERILOG_SOURCES) clean: -@rm -rf $(OBJ_DIR) From b5e57d785565f88075bfe04affa7a03c02ffe30f Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 1 Jul 2013 16:29:26 +0100 Subject: [PATCH 0117/2656] Issue #16: More fix ups to deregister --- lib/vpi_shim/gpi_vpi.c | 57 ++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index e0a23908..e6fab504 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -52,6 +52,7 @@ typedef struct t_vpi_cb_user_data { s_vpi_value cb_value; gpi_sim_hdl_t gpi_hdl; bool called; + bool cleared; } s_vpi_cb_user_data, *p_vpi_cb_user_data; // Define a type of a clock object @@ -319,23 +320,30 @@ PLI_INT32 handle_vpi_callback(p_cb_data cb_data) p_vpi_cb_user_data user_data; user_data = (p_vpi_cb_user_data)cb_data->user_data; + user_data->called = true; rv = user_data->gpi_function(user_data->gpi_cb_data); - // We delay freeing until the top level owner - // calls gpi_deregister_callback - // this gives an implicit serialisation + // We call into deregister to remove from the connected + // simulator, the freeing on data will not be done + // until there is not reference left though + + gpi_deregister_callback(&user_data->gpi_hdl); FEXIT return rv; }; -// Deregisters a callback and removes the user -// data, if we do not remove the user data -// then all references to this are lost -// Only needs to be called before a callback -// fires, see gpr_free_on_time and gpi_free_recurring -// for way to handle this during callback execution +// Cleaing up is a bit complex +// 1. We need to remove the callback and +// it's associated handle internally so that +// there is a not a duplicate trigger event +// 2. The user data needs to stay around until +// thre are no more handles to it. +// Thus this function calls into the sim +// to close down if able to. Or if there +// is no internal state it closes down +// the user data. int gpi_deregister_callback(gpi_sim_hdl gpi_hdl) { p_vpi_cb_user_data user_data; @@ -347,8 +355,13 @@ int gpi_deregister_callback(gpi_sim_hdl gpi_hdl) user_data = gpi_container_of(gpi_hdl, s_vpi_cb_user_data, gpi_hdl); - if (user_data->gpi_cleanup) + if (user_data->cleared) { + memset(user_data, 0x0, sizeof(*user_data)); + free(user_data); + } else if (user_data->gpi_cleanup) { user_data->gpi_cleanup(user_data); + user_data->cleared = true; + } FEXIT return 1; @@ -360,6 +373,7 @@ int gpi_deregister_callback(gpi_sim_hdl gpi_hdl) static int gpi_free_one_time(p_vpi_cb_user_data user_data) { FENTER + int32_t rc; vpiHandle cb_hdl = user_data->cb_hdl; if (!cb_hdl) { LOG_ERROR("VPI: %s passed a NULL pointer\n", __func__); @@ -368,14 +382,11 @@ static int gpi_free_one_time(p_vpi_cb_user_data user_data) // If the callback has not been called we also need to call // remove as well - - if (!user_data->called) { - vpi_remove_cb(cb_hdl); - } else { - vpi_free_object(cb_hdl); - } + if (!user_data->called) + rc = vpi_remove_cb(cb_hdl); + else + rc = vpi_free_object(cb_hdl); - free(user_data); FEXIT return 1; } @@ -394,7 +405,6 @@ static int gpi_free_recurring(p_vpi_cb_user_data user_data) } rc = vpi_remove_cb(cb_hdl); - free(user_data); FEXIT return rc; } @@ -418,6 +428,7 @@ gpi_sim_hdl gpi_register_value_change_callback(int (*gpi_function)(void *), void user_data->gpi_cleanup = gpi_free_recurring; user_data->cb_value.format = vpiIntVal; user_data->called = false; + user_data->cleared = false; vpi_time_s.type = vpiSuppressTime; vpi_value_s.format = vpiIntVal; @@ -453,6 +464,7 @@ gpi_sim_hdl gpi_register_readonly_callback(int (*gpi_function)(void *), void *gp user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_one_time; user_data->called = false; + user_data->cleared = false; vpi_time_s.type = vpiSimTime; vpi_time_s.high = 0; @@ -488,6 +500,7 @@ gpi_sim_hdl gpi_register_readwrite_callback(int (*gpi_function)(void *), void *g user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_one_time; user_data->called = false; + user_data->cleared = false; vpi_time_s.type = vpiSimTime; vpi_time_s.high = 0; @@ -522,6 +535,7 @@ gpi_sim_hdl gpi_register_nexttime_callback(int (*gpi_function)(void *), void *gp user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_one_time; user_data->called = false; + user_data->cleared = false; vpi_time_s.type = vpiSimTime; vpi_time_s.high = 0; @@ -535,7 +549,7 @@ gpi_sim_hdl gpi_register_nexttime_callback(int (*gpi_function)(void *), void *gp cb_data_s.user_data = (char *)user_data; user_data->cb_hdl = vpi_register_cb(&cb_data_s); - + FEXIT return &user_data->gpi_hdl; } @@ -557,6 +571,7 @@ gpi_sim_hdl gpi_register_timed_callback(int (*gpi_function)(void *), void *gpi_c user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_one_time; user_data->called = false; + user_data->cleared = false; vpi_time_s.type = vpiSimTime; vpi_time_s.high = (PLI_UINT32)(time_ps>>32); @@ -592,6 +607,7 @@ gpi_sim_hdl gpi_register_sim_start_callback(int (*gpi_function)(void *), void *g user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_one_time; user_data->called = false; + user_data->cleared = false; cb_data_s.reason = cbStartOfSimulation; cb_data_s.cb_rtn = handle_vpi_callback; @@ -623,6 +639,7 @@ gpi_sim_hdl gpi_register_sim_end_callback(int (*gpi_function)(void *), void *gpi user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_one_time; user_data->called = false; + user_data->cleared = false; cb_data_s.reason = cbEndOfSimulation; cb_data_s.cb_rtn = handle_vpi_callback; @@ -648,8 +665,6 @@ int gpi_clock_handler(void *clock) /* Unregister/free the last callback that just fired */ old_hdl = hdl->cb_hdl; - printf("Clock triggered %d->%d\n", hdl->value, !hdl->value); - hdl->value = !hdl->value; gpi_set_signal_value_int(hdl->clk_hdl, hdl->value); hdl->cb_hdl = gpi_register_timed_callback(gpi_clock_handler, hdl, hdl->period); From a0228033a0bc089586e20ce26be0c50e943c7923 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 2 Jul 2013 08:27:39 +0100 Subject: [PATCH 0118/2656] Resolve binary signals when comparing for boolean equivalence --- cocotb/binary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 05c1da73..8e0b7b11 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -154,7 +154,7 @@ def __nonzero__(self): """ for char in self._str: - if char != "0": return True + if char == "1": return True return False def __cmp__(self, other): From 5c6dfbf38a5ee5ed98992e1f11e37cbbba3902d7 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 2 Jul 2013 08:28:46 +0100 Subject: [PATCH 0119/2656] Issue #7 - Print out failure message --- cocotb/decorators.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index cf0b6b2f..721e98fd 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -171,6 +171,7 @@ def __call__(self, f): """ super(test, self).__init__(f) + self.log = logging.getLogger("cocotb.test.%s" % self._func.__name__) def _wrapped_test(*args, **kwargs): super(test, self).__call__(*args, **kwargs) return self @@ -188,5 +189,6 @@ def send(self, value): return self._coro.send(value) except StopIteration: raise TestComplete(result="Passed") - except cocotb.TestFailed: + except cocotb.TestFailed as e: + self.log.error(str(e)) raise TestComplete(result="Failed") From 18f437503715def922c48345b8817520ac4cb580 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 2 Jul 2013 08:30:08 +0100 Subject: [PATCH 0120/2656] Add bit twiddling driver for non-transactional driving --- cocotb/drivers/__init__.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index ecc07697..bd3c049c 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -37,6 +37,43 @@ from cocotb.triggers import Event, RisingEdge from cocotb.bus import Bus + +class BitDriver(object): + """ + Drives a signal onto a single bit + + Useful for exercising ready / valid + """ + def __init__(self, signal, clk, generator=None): + self._signal = signal + self._clk = clk + self._generator = generator + + def start(self, generator=None): + self._cr = cocotb.fork(self._cr_twiddler(generator=generator)) + + def stop(self): + self._cr.kill() + + @cocotb.coroutine + def _cr_twiddler(self, generator=None): + if generator is None and self._generator is None: + raise Exception("No generator provided!") + if generator is not None: + self._generator = generator + + edge = RisingEdge(self._clk) + + # Actual thread + while True: + on,off = self._generator.next() + self._signal <= 1 + for i in range(on): + yield edge + self._signal <= 0 + for i in range(off): + yield edge + class Driver(object): """ From 74f1d35e60cf9e6e3cb04c48f2cdda1a41555513 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 2 Jul 2013 08:33:00 +0100 Subject: [PATCH 0121/2656] Helper function for getting a load of bytes from a generator --- cocotb/generators/byte.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cocotb/generators/byte.py b/cocotb/generators/byte.py index e86c7ccf..c197514c 100644 --- a/cocotb/generators/byte.py +++ b/cocotb/generators/byte.py @@ -32,6 +32,15 @@ import random from cocotb.decorators import public + +@public +def get_bytes(nbytes, generator): + """Get nbytes from generator""" + result = "" + for i in range(nbytes): + result += generator.next() + return result + @public def random_data(): """Random bytes""" From 8d166344dd423a7bc4099e2196a7878017b4ae0b Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 2 Jul 2013 08:33:30 +0100 Subject: [PATCH 0122/2656] Cleanup: Use new common helper function instead of private one --- cocotb/generators/packet.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/cocotb/generators/packet.py b/cocotb/generators/packet.py index c0efdead..7689d7e7 100644 --- a/cocotb/generators/packet.py +++ b/cocotb/generators/packet.py @@ -45,14 +45,6 @@ _default_payload = random_data -def _get_payload(gen, nbytes): - """Helper function to pull a chunk of bytes from a generator""" - payload = "" - while len(payload) < nbytes: - payload += gen.next() - return payload - - # UDP packet generators @public def udp_all_sizes(max_size=1500, payload=_default_payload()): @@ -60,7 +52,7 @@ def udp_all_sizes(max_size=1500, payload=_default_payload()): header = Ether() / IP() / UDP () for size in range(0, max_size-len(header)): - yield header / _get_payload(payload, size) + yield header / _get_payload(size, payload) @public def udp_random_sizes(npackets=100, payload=_default_payload()): @@ -69,10 +61,11 @@ def udp_random_sizes(npackets=100, payload=_default_payload()): max_size = 1500 - len(header) for pkt in range(npackets): - yield header / _get_payload(payload, random.randint(0,max_size)) + yield header / get_bytes(random.randint(0,max_size), payload) +# IPV4 generator @public def ipv4_small_packets(npackets=100, payload=_default_payload()): """Small (<100bytes payload) IPV4 packets""" for pkt in range(npackets): - yield Ether() / IP() / _get_payload(payload, random.randint(0, 100)) + yield Ether() / IP() / get_bytes(random.randint(0, 100), payload) From 797e045491d8fbd6f62bbd709f0c21282d71f9d0 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 2 Jul 2013 08:45:51 +0100 Subject: [PATCH 0123/2656] Cleanup of test pass/fail reporting mechanism --- cocotb/decorators.py | 16 ++++++++++++---- cocotb/scheduler.py | 7 +++++-- cocotb/scoreboard.py | 19 +++++++++++++++---- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 721e98fd..47b5af55 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -65,10 +65,15 @@ class TestComplete(StopIteration): """ Indicate that a test has finished """ - def __init__(self, result): - self.result = result + pass +class TestCompleteFail(TestComplete): + pass + +class TestCompleteOK(TestComplete): + pass + class coroutine(object): """Decorator class that allows us to provide common coroutine mechanisms: @@ -120,6 +125,9 @@ def send(self, value): return self._coro.send(value) except StopIteration: raise CoroutineComplete(callback=self._finished_cb) + except cocotb.TestFailed as e: + self.log.error(str(e)) + raise TestCompleteFail() def throw(self, exc): return self._coro.throw(exc) @@ -188,7 +196,7 @@ def send(self, value): self.log.debug("Sending trigger %s" % (str(value))) return self._coro.send(value) except StopIteration: - raise TestComplete(result="Passed") + raise TestCompleteOK() except cocotb.TestFailed as e: self.log.error(str(e)) - raise TestComplete(result="Failed") + raise TestCompleteFail() diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 386f6f95..91c50aaf 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -140,7 +140,7 @@ def schedule(self, coroutine, trigger=None): exc() return - except cocotb.decorators.TestComplete as test: + except cocotb.decorators.TestComplete as test_result: self.log.info("Test completed") # Unprime all pending triggers: for trigger, waiting in self.waiting.items(): @@ -149,7 +149,10 @@ def schedule(self, coroutine, trigger=None): try: coro.kill() except StopIteration: pass self.waiting = {} - self.log.info("Test result: %s" % str(test.result)) + if isinstance(test_result, cocotb.decorators.TestCompleteOK): + self.log.info("Test passed!") + else: + self.log.error("Test failed!") # FIXME: proper teardown simulator.stop_simulator(self) diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index 03a07824..8665d250 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -29,10 +29,13 @@ Common scoreboarding capability. """ import logging +import cocotb + from cocotb.utils import hexdump, hexdiffs from cocotb.monitors import Monitor + class Scoreboard(object): """Generic scorboarding class @@ -67,18 +70,26 @@ def check_received_transaction(transaction): """Called back by the monitor when a new transaction has been received""" if not expected_output: + self.errors += 1 self.log.error("%s" % (transaction)) # TODO hexdump - raise TestFailure("Recieved a transaction but wasn't expecting anything") + raise cocotb.TestFailed("Recieved a transaction but wasn't expecting anything") if callable(expected_output): exp = expected_output() - else: exp = str(expected_output.pop(0)) + else: exp = expected_output.pop(0) + + if type(transaction) != type(exp): + self.errors += 1 + self.log.error("Received transaction is a different type to expected transaction") + self.log.info("Got: %s but expected %s" % (str(type(transaction)), str(type(exp)))) + raise cocotb.TestFailed("Received transaction of wrong type") if transaction != exp: self.errors += 1 self.log.error("Received transaction differed from expected output") - self.log.info(hexdump(exp)) - self.log.info(hexdump(transaction)) + self.log.info("Expected:\n" + hexdump(exp)) + self.log.info("Received:\n" + hexdump(transaction)) self.log.warning(hexdiffs(exp, transaction)) + raise cocotb.TestFailed("Received transaction differed from expected transaction") else: self.log.debug("Received expected transaction %d bytes" % (len(transaction))) self.log.debug(repr(transaction)) From 9238bca63f1ea74b81545c4efbe0b51b3377043b Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 2 Jul 2013 08:46:42 +0100 Subject: [PATCH 0124/2656] Placeholder for statistics on the monitor --- cocotb/monitors/__init__.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index dcde2bc4..d7e02a1a 100644 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -42,6 +42,15 @@ from cocotb.binary import BinaryValue from cocotb.bus import Bus + + + +class MonitorStatistics(object): + """Wrapper class for storing Monitor statistics""" + def __init__(self): + self.received_transactions = 0 + + class Monitor(object): @@ -57,6 +66,7 @@ def __init__(self, callback=None, event=None): self._event = event self._recvQ = [] self._callbacks = [] + self.stats = MonitorStatistics() # Subclasses may already set up logging if not hasattr(self, "log"): @@ -76,6 +86,9 @@ def kill(self): def __len__(self): return len(self._recvQ) + def __getitem__(self, idx): + return self._recvQ[idx] + def add_callback(self, callback): self.log.debug("Adding callback of function %s to monitor" % (callback.__name__)) self._callbacks.append(callback) @@ -93,6 +106,9 @@ def _monitor_recv(self): def _recv(self, transaction): """Common handling of a received transaction.""" + + self.stats.received_transactions += 1 + # either callback based consumer for callback in self._callbacks: callback(transaction) From 88a22c20cc811ef2874da48e93b039f560a4b036 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 2 Jul 2013 08:59:46 +0100 Subject: [PATCH 0125/2656] Ensure repository stays generic --- cocotb/drivers/solarflare.py | 53 ------ cocotb/monitors/solarflare.py | 85 --------- examples/simple/Makefile | 37 ---- examples/simple/packet_tap.v | 111 ------------ examples/simple/smoketest.py | 84 --------- modules/__init__.py | 0 modules/sf_streaming/__init__.py | 0 modules/sf_streaming/model/__init__.py | 0 modules/sf_streaming/model/sf_streaming.py | 198 --------------------- 9 files changed, 568 deletions(-) delete mode 100644 cocotb/drivers/solarflare.py delete mode 100644 cocotb/monitors/solarflare.py delete mode 100644 examples/simple/Makefile delete mode 100644 examples/simple/packet_tap.v delete mode 100644 examples/simple/smoketest.py delete mode 100644 modules/__init__.py delete mode 100644 modules/sf_streaming/__init__.py delete mode 100644 modules/sf_streaming/model/__init__.py delete mode 100644 modules/sf_streaming/model/sf_streaming.py diff --git a/cocotb/drivers/solarflare.py b/cocotb/drivers/solarflare.py deleted file mode 100644 index 9ccd38f7..00000000 --- a/cocotb/drivers/solarflare.py +++ /dev/null @@ -1,53 +0,0 @@ -''' Copyright (c) 2013 Potential Ventures Ltd -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' -""" -Drivers for Solarflare bus format. - -A specialisation of the AvalonST bus -""" -from cocotb.decorators import coroutine -from cocotb.triggers import RisingEdge - -from cocotb.drivers.avalon import AvalonSTPkts - - -class SFStreaming(AvalonSTPkts): - """This is the Solarflare Streaming bus as defined by the Solarflare FDK. - - Expect to see a 72-bit bus (bottom 64 bits data, top 8 bits are ECC) - """ - _signals = ["valid", "data", "startofpacket", "endofpacket", "ready", "empty", "channel", "error"] - - def __init__(self, *args, **kwargs): - AvalonSTPkts.__init__(self, *args, **kwargs) - - # Drive some sensible defaults onto the bus - self.bus.startofpacket <= 0 - self.bus.endofpacket <= 0 - self.bus.valid <= 0 - self.bus.empty <= 0 - self.bus.channel <= 0 - self.bus.error <= 0 - diff --git a/cocotb/monitors/solarflare.py b/cocotb/monitors/solarflare.py deleted file mode 100644 index f45e69f7..00000000 --- a/cocotb/monitors/solarflare.py +++ /dev/null @@ -1,85 +0,0 @@ -''' Copyright (c) 2013 Potential Ventures Ltd -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' -""" -Monitors for Altera Avalon interfaces. - -See http://www.altera.co.uk/literature/manual/mnl_avalon_spec.pdf - -NB Currently we only support a very small subset of functionality -""" - -from cocotb.decorators import coroutine -from cocotb.triggers import RisingEdge, ReadOnly -from cocotb.monitors import BusMonitor -from cocotb.utils import hexdump - -# Solarflare specific -from modules.sf_streaming.model.sf_streaming import SFStreamingData - -class SFStreaming(BusMonitor): - """This is the Solarflare Streaming bus as defined by the FDK. - - Expect to see a 72-bit bus (bottom 64 bits data, top 8 bits are ECC) - - TODO: - Metaword / channel bits - ECC checking - """ - _signals = ["startofpacket", "endofpacket", "ready", "empty", "channel", "error", "valid", "data"] - - - @coroutine - def _monitor_recv(self): - """Watch the pins and reconstruct transactions""" - - # Avoid spurious object creation by recycling - clkedge = RisingEdge(self.clock) - rdonly = ReadOnly() - word = SFStreamingData() - - pkt = "" - - while True: - yield clkedge - yield rdonly - - if self.bus.valid.value and self.bus.startofpacket.value: - vec = self.bus.data.value - pkt += vec.buff[1:][::-1] - while True: - yield clkedge - yield rdonly - if self.bus.valid.value: - vec = self.bus.data.value - pkt += vec.buff[1:][::-1] - if self.bus.endofpacket.value: - # Truncate the empty bits - if self.bus.empty.value.value: - pkt = pkt[:-self.bus.empty.value.value] - self.log.info("Recieved a packet of %d bytes", len(pkt)) - self.log.debug(hexdump(pkt)) - self._recv(pkt) - pkt = "" - break diff --git a/examples/simple/Makefile b/examples/simple/Makefile deleted file mode 100644 index 67dcfd5d..00000000 --- a/examples/simple/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - - -# FIXME these should come from some cunning script, possibly from the FDK :) -TOPLEVEL = packet_tap - -VERILOG_SOURCES = packet_tap.v -MODULE=smoketest -FUNCTION=smoketest - -include ../../makefiles/Makefile.inc -include ../../makefiles/Makefile.sim diff --git a/examples/simple/packet_tap.v b/examples/simple/packet_tap.v deleted file mode 100644 index c322adb6..00000000 --- a/examples/simple/packet_tap.v +++ /dev/null @@ -1,111 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2013 Potential Ventures Ltd -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of Potential Ventures Ltd nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -//----------------------------------------------------------------------------- -`timescale 1 ps / 1 ps - -module pv_packet_tap ( - input clk, - input reset_n, - - // Stream_in bus - input stream_in_startofpacket, - input stream_in_endofpacket, - input stream_in_valid, - output stream_in_ready, - input [71:0] stream_in_data, - input [2:0] stream_in_empty, - input [1:0] stream_in_error, - input stream_in_channel, - - // Stream_out bus - output reg stream_out_startofpacket, - output reg stream_out_endofpacket, - output reg stream_out_valid, - input stream_out_ready, - output reg [71:0] stream_out_data, - output reg [2:0] stream_out_empty, - output reg [1:0] stream_out_error, - output reg stream_out_channel, - - // Tap point - output tap_out_startofpacket, - output tap_out_endofpacket, - output tap_out_valid, - input tap_out_ready, - output [71:0] tap_out_data, - output [2:0] tap_out_empty, - output [1:0] tap_out_error, - output tap_out_channel, - - - // Avalon-MM interface - input [6:0] csr_address, - output [31:0] csr_readdata, - input csr_read, - input csr_write, - output csr_waitrequest, - input [31:0] csr_writedata -); - -// Cross-wire the Avalon-ST bus -always @(posedge clk) begin - stream_out_startofpacket <= stream_in_startofpacket; - stream_out_endofpacket <= stream_in_endofpacket; - stream_out_valid <= stream_in_valid; - stream_out_data <= stream_in_data; - stream_out_empty <= stream_in_empty; - stream_out_error <= stream_in_error; - stream_out_channel <= stream_in_channel; -end - -// assign stream_out_startofpacket = stream_in_startofpacket; -// assign stream_out_endofpacket = stream_in_endofpacket; -// assign stream_out_valid = stream_in_valid; -// assign stream_out_data = stream_in_data; -// assign stream_out_empty = stream_in_empty; -// assign stream_out_error = stream_in_error; -// assign stream_out_channel = stream_in_channel; - - -// For now just tap everything -assign tap_out_startofpacket = stream_in_startofpacket; -assign tap_out_endofpacket = stream_in_endofpacket; -assign tap_out_valid = stream_in_valid; -assign tap_out_data = stream_in_data; -assign tap_out_empty = stream_in_empty; -assign tap_out_error = stream_in_error; -assign tap_out_channel = stream_in_channel; - -assign stream_in_ready = stream_out_ready & tap_out_ready; - - -initial begin - $dumpfile("waveform.vcd"); - $dumpvars(0,pv_packet_tap); -end - - -endmodule diff --git a/examples/simple/smoketest.py b/examples/simple/smoketest.py deleted file mode 100644 index 92cd4b36..00000000 --- a/examples/simple/smoketest.py +++ /dev/null @@ -1,84 +0,0 @@ -#!/bin/python - -''' Copyright (c) 2013 Potential Ventures Ltd -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - -""" - Smoketest to bring up drivers and monitors -""" - -import cocotb -from cocotb.generators import feeds -from cocotb.decorators import coroutine -from cocotb.triggers import Timer, Edge, Event -from cocotb.drivers.solarflare import SFStreaming as SFDrv -from cocotb.monitors.solarflare import SFStreaming as SFMon -from cocotb.generators.feeds.itch_feed import * - -from modules.sf_streaming.model.sf_streaming import SFStreamingPacket - -@coroutine -def clock_generator(signal, period_ps): - t = Timer(period_ps) - while True: - signal <= 0 - yield t - signal <= 1 - yield t - -@cocotb.test() -def smoketest(dut): - """Smoke test to help get cocotb up and running""" - dut.log.info("Test started, got DUT:" + str(dut)) - - clock_gen = cocotb.scheduler.add(clock_generator(dut.clk, 3200)) - dut.log.info("Clock started...") - - dut.stream_in_ready <= 1 - - yield Timer(32000) - - stream_in = SFDrv(dut, "stream_in", dut.clk) - stream_out = SFMon(dut, "stream_out", dut.clk) - - yield Timer(32000) - - test_feed = ItchFeed("test itch", 1234, 13) - test_feed.addmsg("An Itch format message") - test_feed.addmsg("Another Itch format message") - test_feed.addmsg("The last Itch test") - - for repeat in range(2): - stream_in.append(SFStreamingPacket(test_feed.getmsg())) - - final_pkt = Event("final_packet") - stream_in.append(SFStreamingPacket(test_feed.getmsg()), event=final_pkt) - - dut.log.info("Waiting for all packets to be sent...") - yield final_pkt.wait() - dut.log.info("All packets sent, cleaning up...") - yield Timer(32000) - clock_gen.kill() - dut.log.warning("Test complete!") diff --git a/modules/__init__.py b/modules/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/modules/sf_streaming/__init__.py b/modules/sf_streaming/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/modules/sf_streaming/model/__init__.py b/modules/sf_streaming/model/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/modules/sf_streaming/model/sf_streaming.py b/modules/sf_streaming/model/sf_streaming.py deleted file mode 100644 index cf1159f6..00000000 --- a/modules/sf_streaming/model/sf_streaming.py +++ /dev/null @@ -1,198 +0,0 @@ -''' Copyright (c) 2013 Potential Ventures Ltd -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - -""" - All things sf_streaming bus related go here -""" - -from ctypes import * - -from scapy.all import Ether, IP, UDP - -from cocotb.utils import pack, unpack - -# The only thing we export is the SFStreamingPacket -__all__ = ["SFStreamingPacket", "SFStreamingBusWord"] - - -# Enumerations for the detected protocol -SF_PKT_PROTO_RAW = 0 -SF_PKT_PROTO_TCP = 1 -SF_PKT_PROTO_UDP = 2 -SF_PKT_PROTO_IP_UNKNOWN = 3 - -class SFPktDesc(Structure): - """Descriptor containing the information about the payload of the packet. - - see sf_streaming_pkg.sv for full details - """ - _fields_ = [ - ("protocol", c_uint8, 2), - ("payload_offset", c_uint8, 6)] - -class SFMetaWord(Structure): - """First word prepended to each packet""" - _pack_ = 1 - _fields_ = [ - ("timestamp", c_uint32), - ("descriptor", SFPktDesc), - ("lookup_context", c_uint16), - ("scratch", c_uint8)] - - -class SFStreamingData(Structure): - _pack_ = 1 - _fields_ = [ - ("data", c_uint64), - ("ecc", c_uint8)] - -class SFStreamingBusWord(Structure): - _pack_ = 1 - _fields_ = [ - ("data", SFStreamingData), - ("empty", c_uint8, 3), - ("startofpacket", c_uint8, 1), - ("endofpacket", c_uint8, 1), - ("error", c_uint8, 2), - ("channel", c_uint8, 1)] - - def __str__(self): - return "sop: %d\teop: %d\tchannel: %d\tempty: %d\tdata: %016x" % (self.startofpacket, self.endofpacket, self.channel, self.empty, self.data.data) - -class SFStreamingPacket(object): - """Useful container class to make using the sf_streaming bus more convenient - - TODO: - Don't currently handle metawords in the middle of the packet. - - Probably want to make the underlying data structure an array of SFStreamingBusWord. - - We could then alias self.pkt to pull out the packet contents from the array. - """ - - def __init__(self, pkt=None): - """pkt is a string""" - self.metaword = SFMetaWord() - self.pkt = pkt - self._ptr = 0 - if pkt is not None: - self.wrap(pkt) - - - def wrap(self, pkt): - """ - Wrap a packet, parsing the packet and setting up the descriptor field - accordingly - """ - self.pkt = pkt - self.parse() - - def parse(self): - """Parse the packet and populate the metaword descriptor field - - FIXME: need to handle GRE here - """ - p = Ether(self.pkt) - - if p.payload.name != 'IP': - self.metaword.descriptor.protocol = SF_PKT_PROTO_RAW - self.metaword.descriptor.payload_offset = self.pkt.find(str(p.payload)) - return - - ip = p.payload - - if ip.payload.name == "UDP": - self.metaword.descriptor.protocol = SF_PKT_PROTO_UDP - self.metaword.descriptor.payload_offset = self.pkt.find(str(ip.payload.payload)) - return - - # For TCP we only point to the start of the IP payload since we don't - # currently parse out the TCP header - if ip.payload.name == "TCP": - self.metaword.descriptor.protocol = SF_PKT_PROTO_TCP - else: - self.metaword.descriptor.protocol = SF_PKT_PROTO_IP_UNKNOWN - - self.metaword.descriptor.payload_offset = self.pkt.find(str(ip.payload)) - return - - def __len__(self): - return len(self.pkt) + 8 # FIXME: + len(self.metaword) - - def __iter__(self): - self._ptr = None - return self - - @property - def payload(self): - """Returns the payload of the packet as defined by the descriptor field""" - return str(self.pkt)[self.metaword.descriptor.payload_offset * 4 + 14:] - - def next(self): - if self._ptr >= len(self.pkt): raise StopIteration - - word = SFStreamingBusWord() - data = c_uint64() - - # Metaword first on channel 1 - if self._ptr is None: - unpack(data, pack(self.metaword)) - word.data.data = data - word.empty = 0 - word.startofpacket = 1 - word.endofpacket = 0 - word.channel = 1 - word.error = 0 - self._ptr = 8 - return word - - # Into the packet data - if self._ptr + 8 > len(self.pkt): - chunk = self.pkt[self._ptr:] - else: - chunk = self.pkt[self._ptr:self._ptr + 8] - unpack(data, chunk, bytes=len(chunk)) - word.data.data = data - word.empty = 8 - len(chunk) - word.channel = 0 - word.error = 0 - word.startofpacket = 0 - word.endofpacket = 0 - self._ptr += 8 - if self._ptr >= len(self.pkt): - word.endofpacket = 1 - return word - - -def test_stuff(): - pkt = Ether() / IP() / UDP() / "Here is some payload" - - wrapped_pkt = SFStreamingPacket(str(pkt)) - - for word in wrapped_pkt: - print str(word) - -if __name__ == "__main__": - test_stuff() From cdb7c9030261aa08ffc81aef556adcb0af445088 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 2 Jul 2013 10:01:43 +0100 Subject: [PATCH 0126/2656] Tidy up logging output --- cocotb/__init__.py | 43 ++++------------------- cocotb/log.py | 79 +++++++++++++++++++++++++++++++++++++++++++ lib/gpi/gpi_logging.c | 42 +++++++++++++++++++---- 3 files changed, 121 insertions(+), 43 deletions(-) create mode 100644 cocotb/log.py diff --git a/cocotb/__init__.py b/cocotb/__init__.py index f7de347f..b0f4f33e 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -34,11 +34,10 @@ import threading from functools import wraps -import simulator -import cocotb.ANSI import cocotb.handle from cocotb.scheduler import Scheduler +from cocotb.log import SimLogFormatter # Things we want in the cocotb namespace @@ -57,46 +56,16 @@ log = logging.getLogger('cocotb') log.setLevel(logging.INFO) +# Add our default log handler +hdlr = logging.StreamHandler(sys.stdout) +hdlr.setFormatter(SimLogFormatter()) +log.addHandler(hdlr) + class TestFailed(Exception): pass -class SimLogFormatter(logging.Formatter): - - """Log formatter to provide consistent log message handling. - - TODO: - - Move somewhere sensible - """ - loglevel2colour = { - logging.DEBUG : "%s", - logging.INFO : ANSI.BLUE_FG + "%s" + ANSI.DEFAULT_FG, - logging.WARNING : ANSI.YELLOW_FG + "%s" + ANSI.DEFAULT_FG, - logging.ERROR : ANSI.RED_FG + "%s" + ANSI.DEFAULT_FG, - logging.CRITICAL: ANSI.RED_BG + ANSI.BLACK_FG + "%s" + ANSI.DEFAULT_FG + ANSI.DEFAULT_BG} - - - def format(self, record): - """pretify the log output, annotate with simulation time""" - if record.args: msg = record.msg % record.args - else: msg = record.msg - - level = record.levelname.ljust(8) - - msg = SimLogFormatter.loglevel2colour[record.levelno] % msg - level = SimLogFormatter.loglevel2colour[record.levelno] % record.levelname.ljust(8) - - timeh, timel = simulator.get_sim_time() - simtime = "% 6d.%02dns" % ((timel/1000), (timel%1000)/10) - - prefix = simtime + ' ' + level + record.name.ljust(35) + os.path.split(record.filename)[1].rjust(20) + ':' + str(record.lineno).ljust(4) + ' in ' + str(record.funcName).ljust(25) + ' ' - pad = "\n" + " " * (len(prefix) - 10) - return prefix + pad.join(msg.split('\n')) - -hdlr = logging.StreamHandler(sys.stdout) -hdlr.setFormatter(SimLogFormatter()) -log.addHandler(hdlr) # FIXME is this really required? _rlock = threading.RLock() diff --git a/cocotb/log.py b/cocotb/log.py new file mode 100644 index 00000000..391a58ad --- /dev/null +++ b/cocotb/log.py @@ -0,0 +1,79 @@ +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" +Everything related to logging +""" + +import os +import logging + +import simulator + +import cocotb.ANSI as ANSI + +# Column alignment +_LEVEL_CHARS = len("CRITICAL") +_RECORD_CHARS = 35 +_FILENAME_CHARS = 20 +_LINENO_CHARS = 4 +_FUNCNAME_CHARS = 32 + + +class SimLogFormatter(logging.Formatter): + + """Log formatter to provide consistent log message handling. + + TODO: + - Move somewhere sensible + """ + loglevel2colour = { + logging.DEBUG : "%s", + logging.INFO : ANSI.BLUE_FG + "%s" + ANSI.DEFAULT_FG, + logging.WARNING : ANSI.YELLOW_FG + "%s" + ANSI.DEFAULT_FG, + logging.ERROR : ANSI.RED_FG + "%s" + ANSI.DEFAULT_FG, + logging.CRITICAL: ANSI.RED_BG + ANSI.BLACK_FG + "%s" + + ANSI.DEFAULT_FG + ANSI.DEFAULT_BG} + + + def format(self, record): + """pretify the log output, annotate with simulation time""" + if record.args: msg = record.msg % record.args + else: msg = record.msg + + msg = SimLogFormatter.loglevel2colour[record.levelno] % msg + level = SimLogFormatter.loglevel2colour[record.levelno] % \ + record.levelname.ljust(_LEVEL_CHARS) + + timeh, timel = simulator.get_sim_time() + simtime = "% 6d.%02dns" % ((timel/1000), (timel%1000)/10) + + prefix = simtime + ' ' + level + record.name.ljust(_RECORD_CHARS) + \ + os.path.split(record.filename)[1].rjust(_FILENAME_CHARS) + \ + ':' + str(record.lineno).ljust(_LINENO_CHARS) + \ + ' in ' + str(record.funcName).ljust(_FUNCNAME_CHARS) + ' ' + + pad = "\n" + " " * (len(prefix) - 10) + return prefix + pad.join(msg.split('\n')) diff --git a/lib/gpi/gpi_logging.c b/lib/gpi/gpi_logging.c index 7130605e..b40de40c 100644 --- a/lib/gpi/gpi_logging.c +++ b/lib/gpi/gpi_logging.c @@ -50,6 +50,36 @@ void set_log_filter(void *filter) Py_INCREF(pLogFilter); } +// Decode the level into a string matching the Python interpretation +struct _log_level_table { + long level; + const char *levelname; +}; + +static struct _log_level_table log_level_table [] = { + { 10, "DEBUG" }, + { 20, "INFO" }, + { 30, "WARNING" }, + { 40, "ERROR" }, + { 50, "CRITICAL" }, + { 0, NULL} +}; + +const char *log_level(long level) +{ + struct _log_level_table *p; + const char *str = "------"; + + for (p=log_level_table; p->levelname; p++) { + if (level == p->level) { + str = p->levelname; + break; + } + } + return str; +} + + /** * @name GPI logging @@ -127,13 +157,13 @@ void gpi_log(const char *name, long level, const char *pathname, const char *fun // Python logging not available, just dump to stdout (No filtering) } else { clog: - fprintf(stdout, " -.--ns"); - fprintf(stdout, " %2ld", level); // FIXME: Print msglevel DEBUG INFO etc. - fprintf(stdout, "%16s", name); - fprintf(stdout, "%45s:", pathname); + fprintf(stdout, " -.--ns "); + fprintf(stdout, "%-8s", log_level(level)); + fprintf(stdout, "%-35s", name); + fprintf(stdout, "%20s:", pathname); fprintf(stdout, "%4ld", lineno); - fprintf(stdout, " in %s\t", funcname); - fprintf(stdout, "%25s", msg); + fprintf(stdout, " in %-31s ", funcname); + fprintf(stdout, "%s", msg); fprintf(stdout, "\n"); } From 867199c24f034ccf9bd141c1aa886f5395260368 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 2 Jul 2013 10:17:19 +0100 Subject: [PATCH 0127/2656] Tweak log output --- cocotb/log.py | 2 +- lib/gpi/gpi_logging.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/log.py b/cocotb/log.py index 391a58ad..d9956215 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -39,7 +39,7 @@ _RECORD_CHARS = 35 _FILENAME_CHARS = 20 _LINENO_CHARS = 4 -_FUNCNAME_CHARS = 32 +_FUNCNAME_CHARS = 31 class SimLogFormatter(logging.Formatter): diff --git a/lib/gpi/gpi_logging.c b/lib/gpi/gpi_logging.c index b40de40c..aaa8c3f3 100644 --- a/lib/gpi/gpi_logging.c +++ b/lib/gpi/gpi_logging.c @@ -161,7 +161,7 @@ void gpi_log(const char *name, long level, const char *pathname, const char *fun fprintf(stdout, "%-8s", log_level(level)); fprintf(stdout, "%-35s", name); fprintf(stdout, "%20s:", pathname); - fprintf(stdout, "%4ld", lineno); + fprintf(stdout, "%-4ld", lineno); fprintf(stdout, " in %-31s ", funcname); fprintf(stdout, "%s", msg); fprintf(stdout, "\n"); From 5046cb2c483114739219f44b9df53041c7bf6a77 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 2 Jul 2013 10:59:47 +0100 Subject: [PATCH 0128/2656] Turn on log filtering from C world by default --- lib/gpi/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index 670774e4..1a0903ea 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -30,7 +30,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ -GCC_ARGS += +GCC_ARGS += -DFILTER SRCS := gpi_logging.c OBJS := $(SRCS:%.c=$(OBJ_DIR)/%.o) From 983f7e6da9be11f8d16ec14e4d013f534cd8e698 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 2 Jul 2013 11:00:36 +0100 Subject: [PATCH 0129/2656] Correct comment --- cocotb/log.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/cocotb/log.py b/cocotb/log.py index d9956215..ec2134f5 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -44,11 +44,7 @@ class SimLogFormatter(logging.Formatter): - """Log formatter to provide consistent log message handling. - - TODO: - - Move somewhere sensible - """ + """Log formatter to provide consistent log message handling.""" loglevel2colour = { logging.DEBUG : "%s", logging.INFO : ANSI.BLUE_FG + "%s" + ANSI.DEFAULT_FG, From 954682e651da13b4a7420d9d4d0091ee521e3510 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 2 Jul 2013 11:01:48 +0100 Subject: [PATCH 0130/2656] Issue #28 - Initial implementation of XML output (single test) --- cocotb/__init__.py | 5 ++- cocotb/decorators.py | 35 ++++++++++++++++++++- cocotb/regression.py | 73 ++++++++++++++++++++++++++++++++++++++++++++ cocotb/scheduler.py | 1 + 4 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 cocotb/regression.py diff --git a/cocotb/__init__.py b/cocotb/__init__.py index b0f4f33e..ae1b0f11 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -38,7 +38,7 @@ import cocotb.handle from cocotb.scheduler import Scheduler from cocotb.log import SimLogFormatter - +from cocotb.regression import xunit_header # Things we want in the cocotb namespace from cocotb.decorators import test, coroutine @@ -98,6 +98,9 @@ def my_import(name): if not module_str or not function_str: raise ImportError("Environment variables with test information not provided. MODULE=\"%s\" and FUNCTION=\"%s\"" % (module_str, function_str)) + with open("results.xml", 'w') as f: + f.write(xunit_header()) + testmod = my_import(module_str) log.info("Starting testcase %s.%s" % (testmod.__name__, function_str)) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 47b5af55..5c847898 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -24,11 +24,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' import sys +import time import logging import cocotb from cocotb.triggers import Join - +from cocotb.regression import xunit_output def public(f): """Use a decorator to avoid retyping function/class names. @@ -168,10 +169,29 @@ class test(coroutine): some common reporting etc, a test timeout and allows us to mark tests as expected failures. """ + + + class ErrorLogHandler(logging.Handler): + def __init__(self, fn): + self.fn = fn + logging.Handler.__init__(self, level=logging.ERROR) + + def handle(self, record): + self.fn(self.format(record)) + def __init__(self, timeout=None, expect_fail=False): self.timeout = timeout self.expect_fail = expect_fail self.started = False + self.error_messages = [] + + # Capture all log messages with ERROR or higher + def handle_error_msg(msg): + self.error_messages.append(msg) + + self.handler = test.ErrorLogHandler(handle_error_msg) + cocotb.log.addHandler(self.handler) + def __call__(self, f): """ @@ -180,6 +200,8 @@ def __call__(self, f): """ super(test, self).__init__(f) self.log = logging.getLogger("cocotb.test.%s" % self._func.__name__) + self.start_time = time.time() + def _wrapped_test(*args, **kwargs): super(test, self).__call__(*args, **kwargs) return self @@ -200,3 +222,14 @@ def send(self, value): except cocotb.TestFailed as e: self.log.error(str(e)) raise TestCompleteFail() + + def write_test_output(self, fname): + + duration = time.time() - self.start_time + + with open(fname, 'a') as f: + if self.error_messages: + f.write(xunit_output(self._func.__name__, "bing", duration, + failure="\n".join(self.error_messages))) + else: + f.write(xunit_output(self._func.__name__, "bing", duration)) diff --git a/cocotb/regression.py b/cocotb/regression.py new file mode 100644 index 00000000..28def2f6 --- /dev/null +++ b/cocotb/regression.py @@ -0,0 +1,73 @@ +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" +All things relating to regression capabilities +""" + +def xunit_header(): + return """\n""" + +def xunit_output(name, classname, time, skipped=False, failure="", error=""): + """ + Format at xunit test output in XML + + Args: + name (str): the name of the test + + classname (str): the name of the class + + time (float): duration of the test in seconds + + Kwargs: + skipped (bool): this test was skipped + + failure (str): failure message to report + + error (str): error message to report + + Returns an XML string + + """ + xml = """" + else: + xml += ">\n" + + if skipped: + xml += " \n" + + if failure: + xml += " %s\n \n" % \ + failure + + if error: + xml += " %s\n \n" % \ + error + + return xml + "" diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 91c50aaf..79225d04 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -155,6 +155,7 @@ def schedule(self, coroutine, trigger=None): self.log.error("Test failed!") # FIXME: proper teardown + coroutine.write_test_output("results.xml") simulator.stop_simulator(self) return From b19f113ee02c409bb53ab1b8f3474dbc99e1b634 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 2 Jul 2013 11:02:33 +0100 Subject: [PATCH 0131/2656] Correct erroneous import statement --- cocotb/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/utils.py b/cocotb/utils.py index 56206337..f124e141 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -75,7 +75,7 @@ def unpack(ctypes_obj, string, bytes=None): -import ANSI +import cocotb.ANSI as ANSI def _sane_color(x): From 0862ec005ea9688af223ee1986bc24380a0f732d Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 2 Jul 2013 11:05:34 +0100 Subject: [PATCH 0132/2656] Issue #28: need to quote time --- cocotb/regression.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 28def2f6..498f4050 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -51,7 +51,7 @@ def xunit_output(name, classname, time, skipped=False, failure="", error=""): Returns an XML string """ - xml = """ Date: Tue, 2 Jul 2013 11:21:04 +0100 Subject: [PATCH 0133/2656] Tweaked Jenkins output --- cocotb/regression.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 498f4050..38f7141a 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -51,7 +51,9 @@ def xunit_output(name, classname, time, skipped=False, failure="", error=""): Returns an XML string """ - xml = """""" % \ + (name, time) + xml += """%s\n \n" % \ error - return xml + "" + return xml + "\n" From 368063f28bdfe22b28fa3fc122900381558f07f1 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 2 Jul 2013 11:22:11 +0100 Subject: [PATCH 0134/2656] More tweaking of Jenkins output --- cocotb/regression.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 38f7141a..5cbbd525 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -57,7 +57,7 @@ def xunit_output(name, classname, time, skipped=False, failure="", error=""): (classname, name, time) if not skipped and not failure and not error: - return xml + " />" + return xml + " />\n" else: xml += ">\n" From c3813f82872b1b65c55fddaea8f0b91e144bf511 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 2 Jul 2013 12:06:12 +0100 Subject: [PATCH 0135/2656] Issue #28: Further clean-up of XML test output --- cocotb/decorators.py | 7 ++++--- cocotb/regression.py | 2 +- cocotb/scheduler.py | 9 ++++++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 5c847898..0b269080 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -229,7 +229,8 @@ def write_test_output(self, fname): with open(fname, 'a') as f: if self.error_messages: - f.write(xunit_output(self._func.__name__, "bing", duration, - failure="\n".join(self.error_messages))) + f.write(xunit_output(self._func.__name__, self._func.__module__, + duration, failure="\n".join(self.error_messages))) else: - f.write(xunit_output(self._func.__name__, "bing", duration)) + f.write(xunit_output(self._func.__name__, self._func.__module__, + duration)) diff --git a/cocotb/regression.py b/cocotb/regression.py index 5cbbd525..2a796366 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -51,7 +51,7 @@ def xunit_output(name, classname, time, skipped=False, failure="", error=""): Returns an XML string """ - xml = """""" % \ + xml = """\n""" % \ (name, time) xml += """ Date: Tue, 2 Jul 2013 12:12:27 +0100 Subject: [PATCH 0136/2656] Issue 26: Correctly remove pending triggers that have been killed --- cocotb/decorators.py | 10 +++++----- cocotb/scheduler.py | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index cf0b6b2f..6b05c4e6 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -95,6 +95,9 @@ def __get__(self, obj, type=None): def __iter__(self): return self + def __str__(self): + return str(self.__name__) + def next(self): """FIXME: deprecated by send method?""" try: @@ -125,12 +128,9 @@ def throw(self, exc): return self._coro.throw(exc) def kill(self): - """Kill a coroutine - - FIXME: Do we want to call all the pending callbacks? - """ + """Kill a coroutine""" self.log.debug("kill() called on coroutine") - self.throw(StopIteration) + cocotb.scheduler.schedule_remove(self, self._finished_cb) def _finished_cb(self): """Called when the coroutine completes. diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 386f6f95..a916160f 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -47,6 +47,7 @@ def __init__(self): self.log = logging.getLogger("cocotb.scheduler") self.writes = {} self.writes_lock = threading.RLock() + self._remove = [] self._readwrite = self.add(self.move_to_rw()) def react(self, trigger): @@ -119,6 +120,12 @@ def remove(self, trigger): self.waiting.pop(trigger) trigger.unprime() + def schedule_remove(self, coroutine, callback): + """Adds the specified coroutine to the list of routines + That will be removed at the end of the current loop + """ + self._remove.append((coroutine, callback)) + def schedule(self, coroutine, trigger=None): """ @@ -155,6 +162,23 @@ def schedule(self, coroutine, trigger=None): simulator.stop_simulator(self) return + # Entries may have been added to the remove list while the + # coroutine was running, clear these down and deschedule + # before resuming + while self._remove: + delroutine, cb = self._remove.pop(0) + for trigger, waiting in self.waiting.items(): + for coro in waiting: + if coro is delroutine: + cb() + self.waiting[trigger].remove(coro) + # Clean up any triggers that no longer have pending coroutines + for trigger, waiting in self.waiting.items(): + if not waiting: + trigger.unprime() + del self.waiting[trigger] + + if isinstance(result, Trigger): self._add_trigger(result, coroutine) elif isinstance(result, cocotb.decorators.coroutine): @@ -170,6 +194,8 @@ def schedule(self, coroutine, trigger=None): self.log.warning("Unable to schedule coroutine since it's returning stuff %s" % repr(result)) coroutine.log.debug("Finished sheduling coroutine (%s)" % str(trigger)) + self._remove = [] + @cocotb.decorators.coroutine def move_to_rw(self): yield ReadWrite() From 31113b6624de9cdb586b82ec44d67c230a366842 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 2 Jul 2013 12:19:37 +0100 Subject: [PATCH 0137/2656] Issue #12: Don't log debug stuff before Python is initialised --- lib/gpi/gpi_logging.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/gpi/gpi_logging.c b/lib/gpi/gpi_logging.c index aaa8c3f3..1832b6bd 100644 --- a/lib/gpi/gpi_logging.c +++ b/lib/gpi/gpi_logging.c @@ -157,6 +157,7 @@ void gpi_log(const char *name, long level, const char *pathname, const char *fun // Python logging not available, just dump to stdout (No filtering) } else { clog: + if (level < 20) return; fprintf(stdout, " -.--ns "); fprintf(stdout, "%-8s", log_level(level)); fprintf(stdout, "%-35s", name); From 4f0046729a93e032e04899de2913bdd8988da349 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 2 Jul 2013 12:47:04 +0100 Subject: [PATCH 0138/2656] Issue 26: Add test case --- examples/functionality/tests/test_cocotb.py | 25 ++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 995fcaf2..074c418f 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -32,7 +32,7 @@ """ import cocotb -from cocotb.triggers import Timer +from cocotb.triggers import Timer, Join @@ -82,6 +82,29 @@ def test_yield_list(dut): yield Timer(10000) +test_flag = False + +@cocotb.coroutine +def clock_yield(clock_gen): + global test_flag + yield Join(clock_gen) + test_flag = True + @cocotb.test(expect_fail=True) def test_duplicate_yield(dut): """A trigger can not be yielded on twice""" + +@cocotb.test(expect_fail=False) +def test_coroutine_kill(dut): + """Test that killing a coroutine causes pending routine continue""" + global test_flag + clk_gen = cocotb.scheduler.add(clock_gen(dut.clk)) + yield Timer(100) + clk_gen_two = cocotb.fork(clock_yield(clock_gen)) + yield Timer(100) + clk_gen.kill() + if test_flag is not False: + raise cocotb.TestFailed + yield Timer(1000) + if test_flag is not True: + raise cocotb.TestFailed From c4ed66fa698c57b6297800babfe5664c19bf5989 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 2 Jul 2013 12:50:34 +0100 Subject: [PATCH 0139/2656] Remove simple from examples --- examples/Makefile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 5a13e864..d5c7a87b 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -27,9 +27,8 @@ -SMOKE_TESTS := simple \ - demo \ - comb_seq +SMOKE_TESTS := demo \ + comb_seq .PHONY: $(SMOKE_TESTS) From b604f5af01553981ef66256f2a16a9a7c2444ade Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 2 Jul 2013 13:02:37 +0100 Subject: [PATCH 0140/2656] Move include files to common folder and adjust paths --- {lib/embed => include}/embed.h | 0 {lib/gpi => include}/gpi.h | 0 {lib/gpi => include}/gpi_logging.h | 0 lib/embed/Makefile | 2 +- lib/gpi/Makefile | 2 +- lib/simulator/Makefile | 2 +- lib/vpi_shim/Makefile | 2 +- makefiles/Makefile.inc | 1 + 8 files changed, 5 insertions(+), 4 deletions(-) rename {lib/embed => include}/embed.h (100%) rename {lib/gpi => include}/gpi.h (100%) rename {lib/gpi => include}/gpi_logging.h (100%) diff --git a/lib/embed/embed.h b/include/embed.h similarity index 100% rename from lib/embed/embed.h rename to include/embed.h diff --git a/lib/gpi/gpi.h b/include/gpi.h similarity index 100% rename from lib/gpi/gpi.h rename to include/gpi.h diff --git a/lib/gpi/gpi_logging.h b/include/gpi_logging.h similarity index 100% rename from lib/gpi/gpi_logging.h rename to include/gpi_logging.h diff --git a/lib/embed/Makefile b/lib/embed/Makefile index 88069c56..d48a8a8d 100644 --- a/lib/embed/Makefile +++ b/lib/embed/Makefile @@ -28,7 +28,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj -INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ \ +INCLUDES += -I/usr/include/python$(PYTHON_VERSION)/ \ -I/mnt/sources/tools/icarus/include/iverilog -I../gpi GCC_ARGS += LIBS := -lpython2.7 $(PYLIBS) diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index 1a0903ea..3a6c983e 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -29,7 +29,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj -INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ +INCLUDES += -I/usr/include/python$(PYTHON_VERSION)/ GCC_ARGS += -DFILTER SRCS := gpi_logging.c diff --git a/lib/simulator/Makefile b/lib/simulator/Makefile index 4ac50931..9006459d 100644 --- a/lib/simulator/Makefile +++ b/lib/simulator/Makefile @@ -27,7 +27,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj -INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ -I../gpi +INCLUDES += -I/usr/include/python$(PYTHON_VERSION)/ GCC_ARGS += LIBS := -lgpi -lpython2.7 $(PYLIBS) LD_PATH := -L$(LIB_DIR) diff --git a/lib/vpi_shim/Makefile b/lib/vpi_shim/Makefile index d0f61a89..932e4c7f 100644 --- a/lib/vpi_shim/Makefile +++ b/lib/vpi_shim/Makefile @@ -27,7 +27,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj -INCLUDES := -I. -I/usr/include/python$(PYTHON_VERSION)/ -I../gpi/ -I../embed/ -I$(VPI_INCLUDE_PATH) +INCLUDES += -I/usr/include/python$(PYTHON_VERSION)/ -I$(VPI_INCLUDE_PATH) GCC_ARGS += LIBS := -lgpilog -lcocotb LD_PATH := -L$(LIB_DIR) diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 81f86caf..37f936c1 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -39,6 +39,7 @@ include $(SIM_ROOT)/makefiles/Makefile.paths export ARCH export BUILD_DIR export LIB_DIR=$(BUILD_DIR)/libs/$(ARCH) +export INCLUDES := -I$(SIM_ROOT)/include include $(SIM_ROOT)/makefiles/Makefile.pylib From eed023a68d055345d5e691986f4d9f7ee2301b55 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 2 Jul 2013 13:04:13 +0100 Subject: [PATCH 0141/2656] Issue #29: Remove VPI_INCLUDE_PATH and use common include folder --- lib/embed/Makefile | 3 +-- lib/vpi_shim/Makefile | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/embed/Makefile b/lib/embed/Makefile index d48a8a8d..f4a6d9c8 100644 --- a/lib/embed/Makefile +++ b/lib/embed/Makefile @@ -28,8 +28,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj -INCLUDES += -I/usr/include/python$(PYTHON_VERSION)/ \ - -I/mnt/sources/tools/icarus/include/iverilog -I../gpi +INCLUDES += -I/usr/include/python$(PYTHON_VERSION)/ GCC_ARGS += LIBS := -lpython2.7 $(PYLIBS) LD_PATH := -L$(LIB_DIR) diff --git a/lib/vpi_shim/Makefile b/lib/vpi_shim/Makefile index 932e4c7f..15e72412 100644 --- a/lib/vpi_shim/Makefile +++ b/lib/vpi_shim/Makefile @@ -27,7 +27,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj -INCLUDES += -I/usr/include/python$(PYTHON_VERSION)/ -I$(VPI_INCLUDE_PATH) +INCLUDES += -I/usr/include/python$(PYTHON_VERSION)/ GCC_ARGS += LIBS := -lgpilog -lcocotb LD_PATH := -L$(LIB_DIR) From 453b1c9d816e965961d84a498ad59f0fbef28cd8 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 2 Jul 2013 14:19:34 +0100 Subject: [PATCH 0142/2656] Fixup header guards to common style --- include/embed.h | 5 +++++ include/gpi.h | 10 ++++------ include/gpi_logging.h | 10 +++------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/include/embed.h b/include/embed.h index 8b9a91da..f0c2c144 100644 --- a/include/embed.h +++ b/include/embed.h @@ -25,9 +25,14 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ +#ifndef COCOTB_EMBED_H_ +#define COCOTB_EMBED_H_ + #include #include extern void embed_init_python(void); extern void embed_sim_init(void); extern void embed_sim_end(void); + +#endif /* COCOTB_EMBED_H_ */ diff --git a/include/gpi.h b/include/gpi.h index adc045cd..4531f73a 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -25,9 +25,10 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ -/* -gpi.h +#ifndef COCOTB_GPI_H_ +#define COCOTB_GPI_H_ +/* Generic Language Interface This header file defines a Generic Language Interface into any simulator. @@ -48,9 +49,6 @@ we have to create a process with the signal on the sensitivity list to imitate a */ -#ifndef __GPI_H -#define __GPI_H - #include #include #include @@ -141,4 +139,4 @@ void gpi_clock_unregister(gpi_sim_hdl clock); EXTERN_C_END -#endif // __GPI_H +#endif /* COCOTB_GPI_H_ */ diff --git a/include/gpi_logging.h b/include/gpi_logging.h index 09ffd355..1cdbeeba 100644 --- a/include/gpi_logging.h +++ b/include/gpi_logging.h @@ -25,12 +25,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ -/* -gpi_logging.h - -*/ -#ifndef __GPI_LOGGING_H -#define __GPI_LOGGING_H +#ifndef COCOTB_GPI_LOGGING_H_ +#define COCOTB_GPI_LOGGING_H_ #ifdef __cplusplus # define EXTERN_C_START extern "C" { @@ -73,4 +69,4 @@ void gpi_log(const char *name, long level, const char *pathname, const char *fun EXTERN_C_END -#endif // __GPI_H +#endif /* COCOTB_GPI_LOGGING_H_ */ From 778f00117043554279f48ce4ae7d662283c76d19 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 2 Jul 2013 15:20:15 +0100 Subject: [PATCH 0143/2656] Issue #29: Add initial vpi_user.h file --- include/vpi_user.h | 217 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 include/vpi_user.h diff --git a/include/vpi_user.h b/include/vpi_user.h new file mode 100644 index 00000000..65475393 --- /dev/null +++ b/include/vpi_user.h @@ -0,0 +1,217 @@ +/****************************************************************************** +* Copyright (c) 2013 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +#ifndef COCOTB_VPI_USER_H_ +#define COCOTB_VPI_USER_H_ + +/* This file (vpi_user.h) contains a limited subset of the IEEE 1394 + * standard that is required for the library to build against + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint32_t *vpiHandle; + +#define vpiNet 36 /* scalar or vector net */ +#define vpiModule 32 /* module instance */ + +#define vpiStop 66 /* execute simulator's $stop */ +#define vpiFinish 67 /* execute simulator's $finish */ +#define vpiReset 68 /* execute simulator's $reset */ + +#define vpiType 1 /* type of object */ +#define vpiFullName 3 /* full hierarchical name */ + +#define vpiNoDelay 1 + +typedef struct t_vpi_time +{ + int32_t type; /* [vpiScaledRealTime, + vpiSimTime, + vpiSuppressTime] */ + int32_t high; /* vpiSimTime, high */ + int32_t low; /* vpiSimTime, low */ + double real; /* vpiScaledRealTime */ +} s_vpi_time, *p_vpi_time; + +/* time types */ +#define vpiScaledRealTime 1 +#define vpiSimTime 2 +#define vpiSuppressTime 3 + + +/* generic value */ +typedef struct t_vpi_value +{ + int32_t format; /* vpi[[Bin, + Oct, + Dec, + Hex]Str, + Scalar, + Int, + Real, + String, + Vector, + Strength, + Suppress, + Time, + ObjType]Val */ + union + { + char *str; /* string value */ + int32_t scalar; /* vpi[0,1,X,Z] */ + int32_t integer; /* integer value */ + double real; /* real value */ + struct t_vpi_time *time; /* time value */ + struct t_vpi_vecval *vector; /* vector value */ + struct t_vpi_strengthval *strength; /* strength value */ + void *p_agg_value_handle; /* agg valHandle */ + } value; +} s_vpi_value, *p_vpi_value; + +/* value formats */ +#define vpiBinStrVal 1 +#define vpiOctStrVal 2 +#define vpiDecStrVal 3 +#define vpiHexStrVal 4 +#define vpiScalarVal 5 +#define vpiIntVal 6 +#define vpiRealVal 7 +#define vpiStringVal 8 +#define vpiVectorVal 9 +#define vpiStrengthVal 10 +#define vpiTimeVal 11 +#define vpiObjTypeVal 12 +#define vpiSuppressVal 13 +#define vpiShortIntVal 14 +#define vpiLongIntVal 15 +#define vpiShortRealVal 16 +#define vpiRawTwoStateVal 17 +#define vpiRawFourStateVal 18 + +/* scalar values */ +#define vpi0 0 +#define vpi1 1 +#define vpiZ 2 +#define vpiX 3 +#define vpiH 4 +#define vpiL 5 +#define vpiDontCare 6 + +/* normal callback structure */ +typedef struct t_cb_data +{ + int32_t reason; /* callback reason */ + int32_t (*cb_rtn)(struct t_cb_data *); /* call routine */ + vpiHandle obj; /* trigger object */ + p_vpi_time time; /* callback time */ + p_vpi_value value; /* trigger object value */ + int32_t index; /* index of the memory word or + var select that changed */ + char *user_data; +} s_cb_data, *p_cb_data; + +#define cbValueChange 1 +#define cbStmt 2 +#define cbForce 3 +#define cbRelease 4 + +#define cbAtStartOfSimTime 5 +#define cbReadWriteSynch 6 +#define cbReadOnlySynch 7 +#define cbNextSimTime 8 +#define cbAfterDelay 9 + +#define cbEndOfCompile 10 +#define cbStartOfSimulation 11 +#define cbEndOfSimulation 12 +#define cbError 13 +#define cbTchkViolation 14 +#define cbStartOfSave 15 +#define cbEndOfSave 16 +#define cbStartOfRestart 17 +#define cbEndOfRestart 18 +#define cbStartOfReset 19 +#define cbEndOfReset 20 +#define cbEnterInteractive 21 +#define cbExitInteractive 22 +#define cbInteractiveScopeChange 23 +#define cbUnresolvedSystf 24 + + +extern vpiHandle vpi_register_cb(p_cb_data cb_data_p); + +extern int32_t vpi_remove_cb(vpiHandle cb_obj); + +extern vpiHandle vpi_handle_by_name(char *name, + vpiHandle scope); + +extern vpiHandle vpi_handle_by_index(vpiHandle object, + int32_t indx); + +extern vpiHandle vpi_handle(int32_t type, + vpiHandle refHandle); + +extern vpiHandle vpi_iterate(int32_t type, + vpiHandle refHandle); + +extern vpiHandle vpi_scan(vpiHandle iterator); + +extern char *vpi_get_str(int32_t property, + vpiHandle object); + +extern void vpi_get_value(vpiHandle expr, + p_vpi_value value_p); + +extern vpiHandle vpi_put_value(vpiHandle object, + p_vpi_value value_p, + p_vpi_time time_p, + int32_t flags); + +extern void vpi_get_time(vpiHandle object, + p_vpi_time time_p); + +extern int32_t vpi_free_object(vpiHandle object); + +extern int32_t vpi_control(int32_t operation, ...); +extern vpiHandle vpi_handle_by_multi_index(vpiHandle obj, + int32_t num_index, + int32_t *index_array); + + + +void (*vlog_startup_routines[])(); + +#ifdef __cplusplus +} +#endif + +#endif /* COCOTB_VPI_USER_H_ */ From 09a74486933b3cfebbc27576b7e101657fd68789 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 2 Jul 2013 15:20:53 +0100 Subject: [PATCH 0144/2656] Issue #29: Fixup gpi_vpi.c to user local vpi_user.h --- lib/vpi_shim/gpi_vpi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index e6fab504..4d10af8d 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -312,7 +312,7 @@ static p_vpi_cb_user_data gpi_get_user_data(gpi_sim_hdl hdl) } -PLI_INT32 handle_vpi_callback(p_cb_data cb_data) +int32_t handle_vpi_callback(p_cb_data cb_data) { FENTER int rv = 0; @@ -397,7 +397,7 @@ static int gpi_free_one_time(p_vpi_cb_user_data user_data) static int gpi_free_recurring(p_vpi_cb_user_data user_data) { FENTER - PLI_INT32 rc; + int32_t rc; vpiHandle cb_hdl = user_data->cb_hdl; if (!cb_hdl) { LOG_ERROR("VPI: %s passed a NULL pointer\n", __func__); @@ -574,8 +574,8 @@ gpi_sim_hdl gpi_register_timed_callback(int (*gpi_function)(void *), void *gpi_c user_data->cleared = false; vpi_time_s.type = vpiSimTime; - vpi_time_s.high = (PLI_UINT32)(time_ps>>32); - vpi_time_s.low = (PLI_UINT32)(time_ps); + vpi_time_s.high = (uint32_t)(time_ps>>32); + vpi_time_s.low = (uint32_t)(time_ps); cb_data_s.reason = cbAfterDelay; cb_data_s.cb_rtn = handle_vpi_callback; From 80030a68c27eb4c4f940620dcde4e5084d056dd5 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 2 Jul 2013 16:17:06 +0100 Subject: [PATCH 0145/2656] Cleanup: Fixup comment in Triggers --- cocotb/triggers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index d1ba5c11..bbd40135 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -62,7 +62,7 @@ def __del__(self): class GPITrigger(Trigger): """ - Execution will resume when the specified time period expires + Base Trigger class for GPI triggers Consumes simulation time """ From 4c53299f3c7e6f83903a8b4a9335a84132acee02 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 2 Jul 2013 16:51:09 +0100 Subject: [PATCH 0146/2656] Fix whitespace --- cocotb/triggers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index d1ba5c11..eae6cb65 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -43,9 +43,9 @@ def __init__(self): def unprime(self): """Remove any pending callbacks if necessary""" - if self.peers: + if self.peers: self.peers = None - + def addpeers(self, peers): """Store any relate triggers""" for elem in peers: From 52e251b70c959cca3af42687166ef828c2004ff7 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 2 Jul 2013 17:14:08 +0100 Subject: [PATCH 0147/2656] Really fix whitespace --- cocotb/triggers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index eae6cb65..ba917c9c 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -43,7 +43,7 @@ def __init__(self): def unprime(self): """Remove any pending callbacks if necessary""" - if self.peers: + if self.peers: self.peers = None def addpeers(self, peers): From 66b50adcc831240c05ec5ed0fe90f9472fcc09cf Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 2 Jul 2013 18:35:32 +0100 Subject: [PATCH 0148/2656] Remove unprime method from ReadWrite trigger (leaky) * Also adjusted Python inheritance --- cocotb/triggers.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index ba917c9c..a5ba7e48 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -60,6 +60,16 @@ def __del__(self): """Ensure if a trigger drops out of scope we remove any pending callbacks""" self.unprime() + def __str__(self): + return self.__class__.__name__ + +class PythonTrigger(Trigger): + """Python triggers don't use GPI at all + + For example notification of coroutine completion etc""" + pass + + class GPITrigger(Trigger): """ Execution will resume when the specified time period expires @@ -135,6 +145,9 @@ def __init__(self): def prime(self, callback): self.cbhdl = simulator.register_rwsynch_callback(callback, self) + def unprime(self): + return + def __str__(self): return self.__class__.__name__ + "(readwritesync)" @@ -199,7 +212,7 @@ def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal.name -class Combine(Trigger): +class Combine(PythonTrigger): """ Combines multiple triggers together. Coroutine will continue when all triggers have fired @@ -232,7 +245,7 @@ def unprime(self): trigger.unprime() -class Event(Trigger): +class Event(PythonTrigger): """ Event to permit synchronisation between two coroutines """ @@ -257,7 +270,7 @@ def wait(self): def __str__(self): return self.__class__.__name__ + "(%s)" % self.name -class Join(Trigger): +class Join(PythonTrigger): """ Join a coroutine, firing when it exits """ From 27daa342b9130872bb8cdcca4a248ab351f0a4b9 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 2 Jul 2013 18:38:07 +0100 Subject: [PATCH 0149/2656] Issue #30: Consecutive execution of tests --- cocotb/__init__.py | 27 +++++------- cocotb/decorators.py | 26 ++++++----- cocotb/regression.py | 103 ++++++++++++++++++++++++++++++++++++++++--- cocotb/scheduler.py | 56 ++++++++++++++--------- 4 files changed, 156 insertions(+), 56 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index ae1b0f11..dc585781 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -38,7 +38,7 @@ import cocotb.handle from cocotb.scheduler import Scheduler from cocotb.log import SimLogFormatter -from cocotb.regression import xunit_header +from cocotb.regression import RegressionManager # Things we want in the cocotb namespace from cocotb.decorators import test, coroutine @@ -48,6 +48,7 @@ # so that cocotb.scheduler gives you the singleton instance and not the # scheduler package scheduler = Scheduler() +regression = None # To save typing provide an alias to scheduler.add fork = scheduler.add @@ -83,31 +84,23 @@ def _initialise_testbench(root_handle): """ _rlock.acquire() - def my_import(name): - mod = __import__(name) - components = name.split('.') - for comp in components[1:]: - mod = getattr(mod, comp) - return mod - # Create the base handle type dut = cocotb.handle.SimHandle(root_handle) module_str = os.getenv('MODULE') function_str = os.getenv('FUNCTION') - if not module_str or not function_str: - raise ImportError("Environment variables with test information not provided. MODULE=\"%s\" and FUNCTION=\"%s\"" % (module_str, function_str)) + if not module_str: + raise ImportError("Environment variables defining the module(s) to \ + execute not defined. MODULE=\"%s\"\"" % (module_str)) - with open("results.xml", 'w') as f: - f.write(xunit_header()) + modules = module_str.split() - testmod = my_import(module_str) - log.info("Starting testcase %s.%s" % (testmod.__name__, function_str)) + global regression - coroutine = getattr(testmod, function_str)(dut) - log.debug("Got %s %s" % (coroutine.__name__, str(coroutine))) + regression = RegressionManager(dut, modules, function=function_str) + regression.initialise() + regression.execute() - scheduler.add(coroutine) _rlock.release() return True diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 07f402a7..07c678e3 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -205,9 +205,23 @@ def __call__(self, f): def _wrapped_test(*args, **kwargs): super(test, self).__call__(*args, **kwargs) return self + + _wrapped_test.im_test = True # For auto-regressions return _wrapped_test + def _pre_send(self): + """Provide some useful debug if our coroutine isn't a real coroutine + + NB better than catching AttributeError on _coro.send since then we + can't distinguish between an AttributeError in the coroutine itself + """ + if not hasattr(self._coro, "send"): + self.log.error("%s isn't a value coroutine! Did you use the yield keyword?" + % self.__name__) + raise TestCompleteFail() + + def send(self, value): """FIXME: problem here is that we don't let the call stack unwind...""" self._pre_send() @@ -222,15 +236,3 @@ def send(self, value): except cocotb.TestFailed as e: self.log.error(str(e)) raise TestCompleteFail() - - def write_test_output(self, fname): - - duration = time.time() - self.start_time - - with open(fname, 'a') as f: - if self.error_messages: - f.write(xunit_output(self._func.__name__, self._func.__module__, - duration, failure="\n".join(self.error_messages))) - else: - f.write(xunit_output(self._func.__name__, self._func.__module__, - duration)) diff --git a/cocotb/regression.py b/cocotb/regression.py index 2a796366..807ff25b 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -27,8 +27,99 @@ All things relating to regression capabilities """ -def xunit_header(): - return """\n""" +import time +import logging + +import simulator + +import cocotb.decorators + + +def _my_import(name): + mod = __import__(name) + components = name.split('.') + for comp in components[1:]: + mod = getattr(mod, comp) + return mod + + +class RegressionManager(object): + """Encapsulates all regression capability into a single place""" + + def __init__(self, dut, modules, function=None): + """ + Args: + modules (list): A list of python module names to run + + Kwargs + """ + self._queue = [] + self._dut = dut + self._modules = modules + self._function = function + self._running_test = None + self.log = logging.getLogger("cocotb.regression") + + def initialise(self): + + ntests = 0 + + for module_name in self._modules: + module = _my_import(module_name) + + for thing in vars(module).values(): + if hasattr(thing, "im_test"): + self._queue.append(thing(self._dut)) + ntests += 1 + self.log.info("Found test %s.%s" % + (self._queue[-1]._func.__module__, + self._queue[-1]._func.__name__)) + + # XML output format + self._fout = open("results.xml", 'w') + self._fout.write("""\n""") + self._fout.write("""\n""" % ntests) + + def tear_down(self): + """It's the end of the world as we know it""" + self._fout.write("") + self._fout.close() + self.log.info("Shutting down...") + simulator.stop_simulator() + + def next_test(self): + """Get the next test to run""" + if not self._queue: return None + return self._queue.pop(0) + + + def handle_result(self, result): + """Handle a test result + + Dumps result to XML and schedules the next test (if any) + + Args: result (TestComplete exception) + """ + + if isinstance(result, cocotb.decorators.TestCompleteFail): + self._fout.write(xunit_output(self._running_test._func.__name__, + self._running_test._func.__module__, + time.time() - self._running_test.start_time, + failure="\n".join(self._running_test.error_messages))) + else: + self._fout.write(xunit_output(self._running_test._func.__name__, + self._running_test._func.__module__, + time.time() - self._running_test.start_time)) + + self.execute() + + def execute(self): + self._running_test = self.next_test() + if not self._running_test: + self.tear_down() + return + cocotb.scheduler.queue(self._running_test) + def xunit_output(name, classname, time, skipped=False, failure="", error=""): """ @@ -51,13 +142,11 @@ def xunit_output(name, classname, time, skipped=False, failure="", error=""): Returns an XML string """ - xml = """\n""" % \ - (name, time) - xml += """\n" + return xml + " />\n" else: xml += ">\n" @@ -72,4 +161,4 @@ def xunit_output(name, classname, time, skipped=False, failure="", error=""): xml += " %s\n \n" % \ error - return xml + "\n" + return xml + " \n" diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 4a626d22..b4875083 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -48,6 +48,7 @@ def __init__(self): self.writes = {} self.writes_lock = threading.RLock() self._remove = [] + self._pending_adds = [] self._readwrite = self.add(self.move_to_rw()) def react(self, trigger): @@ -89,12 +90,18 @@ def react(self, trigger): # another callback for the read-only part of the sim cycle if len(self.writes) and self._readwrite is None: self._readwrite = self.add(self.move_to_rw()) + + # If the python has caused any subsequent events to fire we might + # need to schedule more coroutines before we drop back into the + # simulator + while self._pending_adds: + coroutine = self._pending_adds.pop(0) + self.add(coroutine) + return def playout_writes(self): if self.writes: - if self._readwrite is None: - self._readwrite = self.add(self.move_to_rw()) while self.writes: handle, args = self.writes.popitem() handle.setimeadiatevalue(args) @@ -108,6 +115,9 @@ def _add_trigger(self, trigger, coroutine): self.waiting[trigger].append(coroutine) trigger.prime(self.react) + def queue(self, coroutine): + """Queue a coroutine for execution""" + self._pending_adds.append(coroutine) def add(self, coroutine): """Add a new coroutine. Required because we cant send to a just started generator (FIXME)""" @@ -126,6 +136,8 @@ def schedule_remove(self, coroutine, callback): """ self._remove.append((coroutine, callback)) + def schedule_react(self, trigger): + self._ def schedule(self, coroutine, trigger=None): """ @@ -147,27 +159,15 @@ def schedule(self, coroutine, trigger=None): exc() return + # TestComplete indication is game over, tidy up except cocotb.decorators.TestComplete as test_result: - test = coroutine - # Unprime all pending triggers: - for trigger, waiting in self.waiting.items(): - trigger.unprime() - for coro in waiting: - # Singleton test instance - if isinstance(coro, cocotb.decorators.test): - test = coro - try: coro.kill() - except StopIteration: pass - self.waiting = {} - if isinstance(test_result, cocotb.decorators.TestCompleteOK): - self.log.info("Test passed!") - else: - self.log.error("Test failed!") - - test.write_test_output("results.xml") - simulator.stop_simulator(self) + self.cleanup() + + # Indicate to the test manager that we're done for this test + cocotb.regression.handle_result(test_result) return + #except: # Entries may have been added to the remove list while the # coroutine was running, clear these down and deschedule @@ -199,10 +199,26 @@ def schedule(self, coroutine, trigger=None): self._add_trigger(trigger, coroutine) else: self.log.warning("Unable to schedule coroutine since it's returning stuff %s" % repr(result)) + self.cleanup() + cocotb.regression.handle_result(cocotb.decorators.TestCompleteFail()) + coroutine.log.debug("Finished sheduling coroutine (%s)" % str(trigger)) self._remove = [] + def cleanup(self): + """Clear up all our state + + Unprime all pending triggers and kill off any coroutines""" + for trigger, waiting in self.waiting.items(): + trigger.unprime() + for coro in waiting: + try: coro.kill() + except StopIteration: pass + self.waiting = collections.defaultdict(list) + self.writes = {} + self._readwrite = self.add(self.move_to_rw()) + @cocotb.decorators.coroutine def move_to_rw(self): yield ReadWrite() From b97e833ee40a9532b072ad320cae8d7eddee9cd8 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 3 Jul 2013 12:23:04 +0100 Subject: [PATCH 0150/2656] Replace ability to specify a single test (but using TESTCASE rather than FUNCTION) --- bin/cocotbenv.py | 4 ++-- cocotb/__init__.py | 7 +++---- cocotb/regression.py | 21 ++++++++++++++++++--- makefiles/Makefile.aldec | 2 +- makefiles/Makefile.icarus | 4 ++-- makefiles/Makefile.vcs | 4 ++-- 6 files changed, 28 insertions(+), 14 deletions(-) diff --git a/bin/cocotbenv.py b/bin/cocotbenv.py index 3c464db1..b551e802 100755 --- a/bin/cocotbenv.py +++ b/bin/cocotbenv.py @@ -54,7 +54,7 @@ def execute(self, py_path, lib_path, module, function, finput): cmd = 'PYTHONPATH=' + py_path cmd = cmd + ' LD_LIBRARY_PATH=' + lib_path cmd = cmd + ' MODULE=' + module - cmd = cmd + ' FUNCTION=' + function + cmd = cmd + ' TESTCASE=' + function if self._sdebug is True: cmd = cmd + ' gdb --args' cmd = cmd + self._base_cmd @@ -71,7 +71,7 @@ def execute(self, py_path, lib_path, module, function, finput): cmd = 'PYTHONPATH=' + py_path cmd = cmd + ' LD_LIBRARY_PATH=' + lib_path cmd = cmd + ' MODULE=' + module - cmd = cmd + ' FUNCTION=' + function + cmd = cmd + ' TESTCASE=' + function if self._sdebug is True: cmd = cmd + ' gdb --args' cmd = cmd + ' ' + finput diff --git a/cocotb/__init__.py b/cocotb/__init__.py index dc585781..b4a71694 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -79,15 +79,14 @@ def _initialise_testbench(root_handle): The test must be defined by the environment variables MODULE - FUNCTION - + TESTCASE """ _rlock.acquire() # Create the base handle type dut = cocotb.handle.SimHandle(root_handle) module_str = os.getenv('MODULE') - function_str = os.getenv('FUNCTION') + test_str = os.getenv('TESTCASE') if not module_str: raise ImportError("Environment variables defining the module(s) to \ @@ -97,7 +96,7 @@ def _initialise_testbench(root_handle): global regression - regression = RegressionManager(dut, modules, function=function_str) + regression = RegressionManager(dut, modules, test=test_str) regression.initialise() regression.execute() diff --git a/cocotb/regression.py b/cocotb/regression.py index 807ff25b..c0f05847 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -46,7 +46,7 @@ def _my_import(name): class RegressionManager(object): """Encapsulates all regression capability into a single place""" - def __init__(self, dut, modules, function=None): + def __init__(self, dut, modules, test=None): """ Args: modules (list): A list of python module names to run @@ -56,7 +56,7 @@ def __init__(self, dut, modules, function=None): self._queue = [] self._dut = dut self._modules = modules - self._function = function + self._function = test self._running_test = None self.log = logging.getLogger("cocotb.regression") @@ -64,9 +64,21 @@ def initialise(self): ntests = 0 + # Auto discovery for module_name in self._modules: module = _my_import(module_name) + if self._function: + + # Single function specified, don't auto discover + if not hasattr(module, self._function): + raise AttributeError("Test %s doesn't exist in %s" % + (self._function, module_name)) + + self._queue.append(getattr(module, self._function)(self._dut)) + ntests = 1 + break + for thing in vars(module).values(): if hasattr(thing, "im_test"): self._queue.append(thing(self._dut)) @@ -75,7 +87,10 @@ def initialise(self): (self._queue[-1]._func.__module__, self._queue[-1]._func.__name__)) - # XML output format + self.start_xml(ntests) + + def start_xml(self, ntests): + """Write the XML header into results.txt""" self._fout = open("results.xml", 'w') self._fout.write("""\n""") self._fout.write("""\n""" % ntests) diff --git a/makefiles/Makefile.aldec b/makefiles/Makefile.aldec index 497f0588..822d3b70 100644 --- a/makefiles/Makefile.aldec +++ b/makefiles/Makefile.aldec @@ -88,7 +88,7 @@ endif sim: $(SIM_DIR)/runsim.tcl core -cd $(SIM_DIR) && ln -sf $(VPI_FILE) libcocotb.so cd $(SIM_DIR) && PYTHONPATH=$(LIB_DIR):$(PWD)/testbench:$(SIM_ROOT)/sim_core:$(PWD):$(PYTHONPATH) \ - LD_LIBRARY_PATH=$(LD_LIBRARY_PATH):$(VSIMSABIN)/Linux64 MODULE=$(MODULE) FUNCTION=$(FUNCTION) $(CMD) -do runsim.tcl | tee sim.log + LD_LIBRARY_PATH=$(LD_LIBRARY_PATH):$(VSIMSABIN)/Linux64 MODULE=$(MODULE) TESTCASE=$(TESTCASE) $(CMD) -do runsim.tcl | tee sim.log clean:: rm -rf $(SIM_DIR) diff --git a/makefiles/Makefile.icarus b/makefiles/Makefile.icarus index bcc22b2e..7163bfeb 100644 --- a/makefiles/Makefile.icarus +++ b/makefiles/Makefile.icarus @@ -37,12 +37,12 @@ $(OBJ_DIR): .PHONY: sim sim: $(OBJ_DIR) $(VERILOG_SOURCES) iverilog -o $(OBJ_DIR)/sim.vvp $(VERILOG_SOURCES) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp .PHONY: gdb gdb: $(OBJ_DIR) $(VERILOG_SOURCES) iverilog -o $(OBJ_DIR)/sim.vvp $(VERILOG_SOURCES) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ gdb --args vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp diff --git a/makefiles/Makefile.vcs b/makefiles/Makefile.vcs index 61198cc9..4a940ab8 100644 --- a/makefiles/Makefile.vcs +++ b/makefiles/Makefile.vcs @@ -44,11 +44,11 @@ pli.tab : .PHONY: sim sim: $(OBJ_DIR) $(VERILOG_SOURCES) pli.tab - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ vcs -R +acc+1 +vpi -P pli.tab -sverilog $(EXTRA_ARGS) -debug -load libgpi.so $(VERILOG_SOURCES) dve: $(OBJ_DIR) $(VERILOG_SOURCES) pli.tab - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) FUNCTION=$(FUNCTION) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ vcs -R +acc+1 +vpi+1+assertion -P pli.tab -sverilog $(EXTRA_ARGS) -gui -load libgpi.so $(VERILOG_SOURCES) clean: -@rm -rf $(OBJ_DIR) From 5b5193307626cd17aa5ec800743e70702bc39913 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 3 Jul 2013 12:24:14 +0100 Subject: [PATCH 0151/2656] Provide better debug output for mismatches --- cocotb/scoreboard.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index 8665d250..5bbef2bb 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -87,7 +87,15 @@ def check_received_transaction(transaction): self.errors += 1 self.log.error("Received transaction differed from expected output") self.log.info("Expected:\n" + hexdump(exp)) + if not isinstance(exp, str): + try: + for word in exp: self.log.info(str(word)) + except: pass self.log.info("Received:\n" + hexdump(transaction)) + if not isinstance(transaction, str): + try: + for word in transaction: self.log.info(str(word)) + except: pass self.log.warning(hexdiffs(exp, transaction)) raise cocotb.TestFailed("Received transaction differed from expected transaction") else: From f790c0bc4185a180c062017664ab57509efc7634 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 3 Jul 2013 12:25:09 +0100 Subject: [PATCH 0152/2656] Add another test to demonstrate regression capability --- examples/demo/demo.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/demo/demo.py b/examples/demo/demo.py index 5c10bed4..7d82c261 100644 --- a/examples/demo/demo.py +++ b/examples/demo/demo.py @@ -102,3 +102,10 @@ def example_test(dut): result = yield Timer(1000000) dut.log.warning("test complete!") + + +@cocotb.test() +def example_test2(dut): + """This is another example test""" + result = yield Timer(1000000) + dut.log.warning("test complete!") From 87e4cad357611a4a6fc995899f34de7c2218a503 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 3 Jul 2013 12:25:52 +0100 Subject: [PATCH 0153/2656] Remove FUNCTION specifiers in example Makefiles --- examples/comb_seq/Makefile | 1 - examples/demo/Makefile | 1 - examples/functionality/tests/Makefile | 1 - 3 files changed, 3 deletions(-) diff --git a/examples/comb_seq/Makefile b/examples/comb_seq/Makefile index 6725f632..5fc9c5ec 100644 --- a/examples/comb_seq/Makefile +++ b/examples/comb_seq/Makefile @@ -30,7 +30,6 @@ TOPLEVEL = issue12 VERILOG_SOURCES = issue12.v MODULE=issue12 -FUNCTION=issue12 include ../../makefiles/Makefile.inc include ../../makefiles/Makefile.sim diff --git a/examples/demo/Makefile b/examples/demo/Makefile index 9e939d53..af9305a5 100644 --- a/examples/demo/Makefile +++ b/examples/demo/Makefile @@ -31,7 +31,6 @@ TOPLEVEL = first_counter VERILOG_SOURCES = counter.v MODULE=demo -FUNCTION=example_test include ../../makefiles/Makefile.inc include ../../makefiles/Makefile.sim diff --git a/examples/functionality/tests/Makefile b/examples/functionality/tests/Makefile index 071c7be9..bd71e364 100644 --- a/examples/functionality/tests/Makefile +++ b/examples/functionality/tests/Makefile @@ -30,7 +30,6 @@ TOPLEVEL = sample_module VERILOG_SOURCES = ../hdl/sample_module.v MODULE=test_cocotb -FUNCTION=test_not_a_coroutine include ../../../makefiles/Makefile.inc include ../../../makefiles/Makefile.sim From bf4a19a8c0fb06a9c660e4c9bde94be3027f34ce Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 3 Jul 2013 14:14:43 +0100 Subject: [PATCH 0154/2656] Documentation tweaks - added links to documentation to README.md --- README.md | 47 ++++++++++++--------------- documentation/source/introduction.rst | 2 +- documentation/source/overview.rst | 24 ++++++++++++++ 3 files changed, 45 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 6fab7b01..acdde5cf 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,30 @@ **cocotb** is a coroutine based cosimulation library for writing VHDL and Verilog testbenches in Python. -Rationale -========= -VHDL and Verilog are both unsuitable for writing complex testbenches. eRM, SystemVerilog and the various SV based methodologies have emerged to address this deficiency. These verification methodologies are large and cumbersome, requiring specialist knowledge, significant time investment and expensive toolchains to achieve satisfactory verification. The overhead of setting up testbenches is onerous so often designers write small throwaway testbenches at a block level and defer the majority of verification to a large system level testbench. Cocotb intends to bridge this gap in order to make block-level testing useful, reusable, fast, accessible to a much wider skill-set. The net effect is that bugs will be discovered earlier in the design cycle which equates to significant time and cost saving. - - Overview ======== +VHDL and Verilog are both unsuitable for writing complex testbenches. eRM, SystemVerilog and the various SV based methodologies have emerged to address this deficiency. These verification methodologies are large and cumbersome, requiring specialist knowledge, significant time investment and expensive toolchains to achieve satisfactory verification. The overhead of setting up testbenches is onerous so often designers write small throwaway testbenches at a block level and defer the majority of verification to a large system level testbench. -A typical cocotb testbench requires no additional RTL code. The Design Under Test (DUT) is instantiated as the toplevel in the simulator without any wrapper code. Cocotb drives stimulus onto the inputs to the DUT (or further down the hierarchy) and monitors the outputs directly from Python. +Cocotb intends to bridge this gap in order to make block-level testing useful, reusable, fast, accessible to a much wider skill-set. The net effect is that bugs will be discovered earlier in the design cycle which equates to significant time and cost saving. -Cocotb comprises 3 layers: +Why verify in Python? +===================== -### GPI (Generic Procedural Interface) +* It's **easy** to interface to other languages from Python +* Python has a huge library of existing code to **re-use** like [constraint solvers](https://code.google.com/p/or-tools/) [packet parsing/generation](http://www.secdev.org/projects/scapy/) libraries. +* Python is **interpreted**. Tests can be edited and re-run them without having to recompile the design or even exit the simulator GUI. +* Writing Python is **fast**, it's *easy to understand* and *everybody* knows it. -This layer abstracts the simulator language interface to provide a common set of functionality. Supports VHDL via VHPI and Verilog/SystemVerilog via VPI. Modelsim FLI may be supported in the future. -### simulatormodule -A CPython extension which utilises GPI to expose the simulator to Python. +Useful links +============ -### cocotb Python package +* Read the [documentation](http://cocotb.readthedocs.org) +* Get involved: [Raise a bug / request an enhancement](https://github.com/potentialventures/cocotb/issues/new) (Requires a GitHub account) +* Get in contact: [E-mail us](mailto:cocotb@potentialventures.com) -Python infrastructure including coroutine scheduler, base classes etc. The cocotb package provides a mechanism to traverse the hierarchy, query signal values, force signals to particular values and control simulator execution. Example ======= @@ -77,22 +77,15 @@ def send(self, packet): self.valid <= 0 ``` -Advantages -========== -* Low overhead to creating testbenches to facilitate block-level verification -* Favourable learning curve compared to eRM/OVM/UVM -* Leverage existing Python and C libraries easily -* Multi-language (Verilog/VHDL) and cross multi-simulator compatible -* Supports directed and randomised testing +Similar projects +================ +[MyHDL](http://www.myhdl.org/) seeks to displace VHDL and Verilog with a Python framework that can generate synthesiable RTL. -Disadvantages -============= -* Simulation is slower than native language testbench -* Non-standard +Several examples exist of projects providing a VPI interface into a language other than C: +* [PyHVL](http://pyhvl.sourceforge.net/) +* [Ruby-vpi](http://snk.tuxfamily.org/lib/ruby-vpi/) -Similar projects -================ +Cocotb tries to build on the advances made by these projects while learning from the direction of UVM and other verification standards. -Several examples exist of projects providing a VPI interface into a language other than C and (most notably http://pyhvl.sourceforge.net/ and http://snk.tuxfamily.org/lib/ruby-vpi/). MyHDL (http://www.myhdl.org/) seeks to displace VHDL and Verilog with a Python framework that can generate synthesiable RTL. diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index 86985ab7..1bbb3c10 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -55,7 +55,7 @@ Python rather than SystemVerilog. The right tool for the job ^^^^^^^^^^^^^^^^^^^^^^^^^^ -In cocotb VHDL/Verilog/SystemVerilog are only used for the synthesisable design. All verification is done using Python which is isideal for rapid development of complex systems and integrating multiple languages. +In cocotb VHDL/Verilog/SystemVerilog are only used for the synthesisable design. All verification is done using Python which is ideal for rapid development of complex systems and integrating multiple languages. * It's **easy** to interface to other languages from Python * Python has a huge library of existing code to **re-use** like `constraint solvers `_ and `packet parsing/generation `_ libraries. diff --git a/documentation/source/overview.rst b/documentation/source/overview.rst index a25ee158..c91d6464 100644 --- a/documentation/source/overview.rst +++ b/documentation/source/overview.rst @@ -4,3 +4,27 @@ Overview of cocotb .. image:: diagrams/svg/cocotb_overview.svg + +Overview +======== + + +A typical cocotb testbench requires no additional RTL code. The Design Under Test (DUT) is instantiated as the toplevel in the simulator without any wrapper code. Cocotb drives stimulus onto the inputs to the DUT (or further down the hierarchy) and monitors the outputs directly from Python. + +Cocotb comprises 3 layers: + +GPI (Generic Procedural Interface) +---------------------------------- + +This layer abstracts the simulator language interface to provide a common set of functionality. Supports VHDL via VHPI and Verilog/SystemVerilog via VPI. Modelsim FLI may be supported in the future. + +simulatormodule +--------------- + +A CPython extension which utilises GPI to expose the simulator to Python. + +cocotb Python package +--------------------- + +Python infrastructure including coroutine scheduler, base classes etc. The cocotb package provides a mechanism to traverse the hierarchy, query signal values, force signals to particular values and control simulator execution. + From c25c10ef0ada7626d0ce9ebd35a4456feba45623 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 3 Jul 2013 14:16:36 +0100 Subject: [PATCH 0155/2656] Shuffle README.md --- README.md | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index acdde5cf..77177547 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,11 @@ **cocotb** is a coroutine based cosimulation library for writing VHDL and Verilog testbenches in Python. +* Read the [documentation](http://cocotb.readthedocs.org) +* Get involved: [Raise a bug / request an enhancement](https://github.com/potentialventures/cocotb/issues/new) (Requires a GitHub account) +* Get in contact: [E-mail us](mailto:cocotb@potentialventures.com) + + Overview ======== @@ -9,7 +14,7 @@ VHDL and Verilog are both unsuitable for writing complex testbenches. eRM, Syste Cocotb intends to bridge this gap in order to make block-level testing useful, reusable, fast, accessible to a much wider skill-set. The net effect is that bugs will be discovered earlier in the design cycle which equates to significant time and cost saving. Why verify in Python? -===================== +--------------------- * It's **easy** to interface to other languages from Python * Python has a huge library of existing code to **re-use** like [constraint solvers](https://code.google.com/p/or-tools/) [packet parsing/generation](http://www.secdev.org/projects/scapy/) libraries. @@ -17,17 +22,8 @@ Why verify in Python? * Writing Python is **fast**, it's *easy to understand* and *everybody* knows it. - -Useful links -============ - -* Read the [documentation](http://cocotb.readthedocs.org) -* Get involved: [Raise a bug / request an enhancement](https://github.com/potentialventures/cocotb/issues/new) (Requires a GitHub account) -* Get in contact: [E-mail us](mailto:cocotb@potentialventures.com) - - Example -======= +------- A simplistic example of generating a clock with 10ns period: ```python @@ -78,7 +74,7 @@ def send(self, packet): ``` Similar projects -================ +---------------- [MyHDL](http://www.myhdl.org/) seeks to displace VHDL and Verilog with a Python framework that can generate synthesiable RTL. From 3d63af7de9b79e9bfa8946e1b2728667e8f037e2 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 3 Jul 2013 14:18:35 +0100 Subject: [PATCH 0156/2656] Issue #30: All tests specified in a file can now run --- cocotb/scheduler.py | 90 +++++++++++++++++++++++++++++++-------------- cocotb/triggers.py | 3 -- 2 files changed, 62 insertions(+), 31 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index b4875083..01f89bd8 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -49,6 +49,10 @@ def __init__(self): self.writes_lock = threading.RLock() self._remove = [] self._pending_adds = [] + self._terminate = False + self._test_result = None + self._readonly = None + # Keep this last self._readwrite = self.add(self.move_to_rw()) def react(self, trigger): @@ -81,22 +85,38 @@ def react(self, trigger): self.schedule(coroutine, trigger=trigger) self.log.debug("Scheduled coroutine %s" % (coroutine.__name__)) - if self.waiting: - self.log.debug("Completed scheduling loop, still waiting on:") + self.log.debug("Completed scheduling loop, still waiting on:") + for trig, routines in self.waiting.items(): self.log.debug("\t%s: [%s]" % (str(trig).ljust(30), " ".join([routine.__name__ for routine in routines]))) # If we've performed any writes that are cached then schedule - # another callback for the read-only part of the sim cycle - if len(self.writes) and self._readwrite is None: + # another callback for the read-write part of the sim cycle, but + # if we are terminating then do not allow another callback to be + # scheduled + if len(self.writes) and self._readwrite is None and self._terminate is False: self._readwrite = self.add(self.move_to_rw()) + # A request for termination may have happened, + # This is where we really handle to informing of the + # regression handler that everything from the current + # test is done and that it can go on with more + if self._terminate is True: + if self._readonly is None and not self.waiting: + if self._test_result: + cocotb.regression.handle_result(self._test_result) + self._terminate = False + else: + self.log.error("Seemed to have scorchedd the earth") + die + # If the python has caused any subsequent events to fire we might # need to schedule more coroutines before we drop back into the # simulator - while self._pending_adds: - coroutine = self._pending_adds.pop(0) - self.add(coroutine) + if self._terminate is False: + while self._pending_adds: + coroutine = self._pending_adds.pop(0) + self.add(coroutine) return @@ -136,8 +156,23 @@ def schedule_remove(self, coroutine, callback): """ self._remove.append((coroutine, callback)) - def schedule_react(self, trigger): - self._ + def prune_routines(self): + """ + Process the remove list that can have accumulatad during the + execution of a parent routine + """ + while self._remove: + delroutine, cb = self._remove.pop(0) + for trigger, waiting in self.waiting.items(): + for coro in waiting: + if coro is delroutine: + cb() + self.waiting[trigger].remove(coro) + # Clean up any triggers that no longer have pending coroutines + for trigger, waiting in self.waiting.items(): + if not waiting: + trigger.unprime() + del self.waiting[trigger] def schedule(self, coroutine, trigger=None): """ @@ -149,6 +184,12 @@ def schedule(self, coroutine, trigger=None): trigger (cocotb.triggers.Trigger): The trigger that caused this coroutine to be scheduled """ + + if self._terminate is True and self._readonly is None: + self._readonly = True + readonly = self.add(self.move_to_ro()) + self._readonly = readonly + coroutine.log.debug("Scheduling (%s)" % str(trigger)) try: result = coroutine.send(trigger) @@ -157,33 +198,23 @@ def schedule(self, coroutine, trigger=None): # Call any pending callbacks that were waiting for this coroutine to exit exc() + self.prune_routines() return # TestComplete indication is game over, tidy up except cocotb.decorators.TestComplete as test_result: - self.cleanup() + # Tag that close down is needed, save the test_result + # for later use in cleanup handler + self._terminate = True + self._test_result = test_result - # Indicate to the test manager that we're done for this test - cocotb.regression.handle_result(test_result) return - #except: # Entries may have been added to the remove list while the # coroutine was running, clear these down and deschedule # before resuming - while self._remove: - delroutine, cb = self._remove.pop(0) - for trigger, waiting in self.waiting.items(): - for coro in waiting: - if coro is delroutine: - cb() - self.waiting[trigger].remove(coro) - # Clean up any triggers that no longer have pending coroutines - for trigger, waiting in self.waiting.items(): - if not waiting: - trigger.unprime() - del self.waiting[trigger] + self.prune_routines() if isinstance(result, Trigger): @@ -215,9 +246,12 @@ def cleanup(self): for coro in waiting: try: coro.kill() except StopIteration: pass - self.waiting = collections.defaultdict(list) - self.writes = {} - self._readwrite = self.add(self.move_to_rw()) + + @cocotb.decorators.coroutine + def move_to_ro(self): + yield ReadOnly() + self.cleanup() + self._readonly = None @cocotb.decorators.coroutine def move_to_rw(self): diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 111b57b4..1a4d1d5b 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -145,9 +145,6 @@ def __init__(self): def prime(self, callback): self.cbhdl = simulator.register_rwsynch_callback(callback, self) - def unprime(self): - return - def __str__(self): return self.__class__.__name__ + "(readwritesync)" From 845bd3a71ab33555cc73c2af6ae8007495d295ed Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 3 Jul 2013 14:21:10 +0100 Subject: [PATCH 0157/2656] Roadmap points to GitHub --- documentation/source/roadmap.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/documentation/source/roadmap.rst b/documentation/source/roadmap.rst index bd10b513..da8e2b76 100644 --- a/documentation/source/roadmap.rst +++ b/documentation/source/roadmap.rst @@ -4,8 +4,7 @@ Roadmap Cocotb is in active development. +We use GitHub issues to track our pending tasks. Take a look at the `open Enchancements `_ to see the work that's lined up. -* `Jenkins `_ itegration +If you have a GitHub account you can also `raise an enhancement request `_ to suggest new features. - * Test auto-discovery - * XML output format From 75ce6df5f3fdac15f08e53aa42df195557cbbca8 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 3 Jul 2013 22:14:11 +0100 Subject: [PATCH 0158/2656] Issue #33: Find the Python modules on filesystem --- makefiles/Makefile.pylib | 67 +++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 38 deletions(-) diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib index 5909339c..cff655ee 100644 --- a/makefiles/Makefile.pylib +++ b/makefiles/Makefile.pylib @@ -33,45 +33,36 @@ else LIBDIR:=lib endif -PYTHON_VERSION:=2.7 +# We might work with other Python versions +PYTHON_VERSION?=2.7 PYTHON_DIR:=/usr/$(LIBDIR)/python$(PYTHON_VERSION) -# Stupid Python doesn't seem to have a consistent naming convention -PYMODULE_CASE:=$(shell if [ -f $(PYTHON_DIR)/lib-dynload/_functoolsmodule.so ]; then echo 1; else echo 0; fi) +# Use a function to determine the name and location of the module +# Whether modules are dynamic or statically linked varias per install, +# as does the module name. Therefore we have to search at build time. +# NB assume anything we don't find is staticaly linked. +module_name_fn = $(shell if [ -f $(1)/$(2).so ]; then echo $(1)/$(2).so; \ + elif [ -f $(1)/$(2)module.so ]; then echo $(1)/$(2)module.so; fi) + +REQUIRED_PYTHON_LIBS = _functools \ + _socket \ + _collections \ + operator \ + itertools \ + time \ + cStringIO \ + select \ + fcntl \ + _struct \ + binascii \ + math \ + _random \ + zlib \ + cPickle \ + arraymodule + + + +PYLIBS = $(foreach PYLIB,$(REQUIRED_PYTHON_LIBS),$(call module_name_fn,$(PYTHON_DIR)/lib-dynload,$(PYLIB))) -ifeq ($(PYMODULE_CASE),1) -PYLIBS = $(PYTHON_DIR)/lib-dynload/_functoolsmodule.so \ - $(PYTHON_DIR)/lib-dynload/_socketmodule.so \ - $(PYTHON_DIR)/lib-dynload/_collectionsmodule.so \ - $(PYTHON_DIR)/lib-dynload/operator.so \ - $(PYTHON_DIR)/lib-dynload/itertoolsmodule.so \ - $(PYTHON_DIR)/lib-dynload/timemodule.so \ - $(PYTHON_DIR)/lib-dynload/cStringIO.so \ - $(PYTHON_DIR)/lib-dynload/selectmodule.so \ - $(PYTHON_DIR)/lib-dynload/fcntlmodule.so \ - $(PYTHON_DIR)/lib-dynload/_struct.so \ - $(PYTHON_DIR)/lib-dynload/binascii.so \ - $(PYTHON_DIR)/lib-dynload/math.so \ - $(PYTHON_DIR)/lib-dynload/_randommodule.so \ - $(PYTHON_DIR)/lib-dynload/zlibmodule.so \ - $(PYTHON_DIR)/lib-dynload/cPickle.so \ - $(PYTHON_DIR)/lib-dynload/arraymodule.so -else -PYLIBS = $(PYTHON_DIR)/lib-dynload/_functools.so \ - $(PYTHON_DIR)/lib-dynload/_socket.so \ - $(PYTHON_DIR)/lib-dynload/_collections.so \ - $(PYTHON_DIR)/lib-dynload/operator.so \ - $(PYTHON_DIR)/lib-dynload/itertools.so \ - $(PYTHON_DIR)/lib-dynload/time.so \ - $(PYTHON_DIR)/lib-dynload/cStringIO.so \ - $(PYTHON_DIR)/lib-dynload/select.so \ - $(PYTHON_DIR)/lib-dynload/fcntl.so \ - $(PYTHON_DIR)/lib-dynload/_struct.so \ - $(PYTHON_DIR)/lib-dynload/binascii.so \ - $(PYTHON_DIR)/lib-dynload/math.so \ - $(PYTHON_DIR)/lib-dynload/_random.so \ - $(PYTHON_DIR)/lib-dynload/zlib.so \ - $(PYTHON_DIR)/lib-dynload/cPickle.so \ - $(PYTHON_DIR)/lib-dynload/array.so -endif From 751f8cb4dc813a9458d982ebfbd8277825550713 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 3 Jul 2013 22:18:49 +0100 Subject: [PATCH 0159/2656] Issue #33: Link against all libs in $(PYTHON_DIR)/lib-dynload --- makefiles/Makefile.pylib | 32 ++++---------------------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib index cff655ee..7d19e0e4 100644 --- a/makefiles/Makefile.pylib +++ b/makefiles/Makefile.pylib @@ -37,32 +37,8 @@ endif PYTHON_VERSION?=2.7 PYTHON_DIR:=/usr/$(LIBDIR)/python$(PYTHON_VERSION) - -# Use a function to determine the name and location of the module -# Whether modules are dynamic or statically linked varias per install, -# as does the module name. Therefore we have to search at build time. -# NB assume anything we don't find is staticaly linked. -module_name_fn = $(shell if [ -f $(1)/$(2).so ]; then echo $(1)/$(2).so; \ - elif [ -f $(1)/$(2)module.so ]; then echo $(1)/$(2)module.so; fi) - -REQUIRED_PYTHON_LIBS = _functools \ - _socket \ - _collections \ - operator \ - itertools \ - time \ - cStringIO \ - select \ - fcntl \ - _struct \ - binascii \ - math \ - _random \ - zlib \ - cPickle \ - arraymodule - - - -PYLIBS = $(foreach PYLIB,$(REQUIRED_PYTHON_LIBS),$(call module_name_fn,$(PYTHON_DIR)/lib-dynload,$(PYLIB))) +# Since we don't know which modules we might need or whether the simulator +# we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports +# is we simply link against all dynamic libraries in the Python installation +PYLIBS = $(wildcard $(PYTHON_DIR)/lib-dynload/*.so) From 62032606d202bbf0b7004a0eec11dbf1b6e1d30e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 4 Jul 2013 16:08:33 +0100 Subject: [PATCH 0160/2656] Issue #30: Multiple tests can inconsistently run --- cocotb/decorators.py | 2 ++ cocotb/regression.py | 13 ++++++++++ cocotb/scheduler.py | 58 +++++++++++++++++++++++++++++--------------- 3 files changed, 53 insertions(+), 20 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 07c678e3..f9455535 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -236,3 +236,5 @@ def send(self, value): except cocotb.TestFailed as e: self.log.error(str(e)) raise TestCompleteFail() + + raise StopIteration() diff --git a/cocotb/regression.py b/cocotb/regression.py index c0f05847..3ad4a83a 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -135,6 +135,19 @@ def execute(self): return cocotb.scheduler.queue(self._running_test) +#@cocotb.decorators.test +#def test_runner(self): +# self._running_test = cocotb.regression.next_test() +# while self._running_test: +# try: +# test = cocotb.scheduler.add(self._running_test) +# yield Join(test) +## except StopIteration: +# self.log.warn("Caught the bugger") + + # cocotb.regression.tear_down() +# return + def xunit_output(name, classname, time, skipped=False, failure="", error=""): """ diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 01f89bd8..59b5b001 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -104,6 +104,7 @@ def react(self, trigger): if self._terminate is True: if self._readonly is None and not self.waiting: if self._test_result: + self.log.warn("Issue test result to regresssion object") cocotb.regression.handle_result(self._test_result) self._terminate = False else: @@ -161,18 +162,23 @@ def prune_routines(self): Process the remove list that can have accumulatad during the execution of a parent routine """ + self.log.warn("In prune_routines") while self._remove: delroutine, cb = self._remove.pop(0) for trigger, waiting in self.waiting.items(): for coro in waiting: if coro is delroutine: + self.log.warn("Trowing into %s" % str(coro)) cb() + try:coro.throw(StopIteration) + except StopIteration: pass self.waiting[trigger].remove(coro) # Clean up any triggers that no longer have pending coroutines for trigger, waiting in self.waiting.items(): if not waiting: trigger.unprime() del self.waiting[trigger] + self.log.warn("Exit prune_routines") def schedule(self, coroutine, trigger=None): """ @@ -185,20 +191,20 @@ def schedule(self, coroutine, trigger=None): coroutine to be scheduled """ - if self._terminate is True and self._readonly is None: - self._readonly = True - readonly = self.add(self.move_to_ro()) - self._readonly = readonly +# if self._terminate is True and self._readonly is None: +# self._readonly = True +# readonly = self.add(self.move_to_ro()) +# self._readonly = readonly coroutine.log.debug("Scheduling (%s)" % str(trigger)) try: result = coroutine.send(trigger) except cocotb.decorators.CoroutineComplete as exc: - self.log.debug("Coroutine completed execution with CoroutineComplete: %s" % coroutine.__name__) + self.log.debug("Coroutine completed execution with CoroutineComplete: %s" % str(coroutine)) # Call any pending callbacks that were waiting for this coroutine to exit exc() - self.prune_routines() +# self.prune_routines() return # TestComplete indication is game over, tidy up @@ -208,15 +214,19 @@ def schedule(self, coroutine, trigger=None): # for later use in cleanup handler self._terminate = True self._test_result = test_result - + self.log.debug("Coroutine completed execution with TestComplete: %s" % str(coroutine)) +# self.prune_routines() + self.cleanup() +# self.prune_routines() + self._readonly = self.add(self.move_to_ro()) return # Entries may have been added to the remove list while the # coroutine was running, clear these down and deschedule # before resuming - self.prune_routines() - - + if self._terminate is False: + self.prune_routines() + if isinstance(result, Trigger): self._add_trigger(result, coroutine) elif isinstance(result, cocotb.decorators.coroutine): @@ -235,26 +245,34 @@ def schedule(self, coroutine, trigger=None): coroutine.log.debug("Finished sheduling coroutine (%s)" % str(trigger)) - self._remove = [] +# self._remove = [] def cleanup(self): - """Clear up all our state + """ Clear up all our state Unprime all pending triggers and kill off any coroutines""" for trigger, waiting in self.waiting.items(): - trigger.unprime() +# trigger.unprime() for coro in waiting: - try: coro.kill() - except StopIteration: pass + self.log.warn("Killing %s" % str(coro)) + coro.kill() +# self.log.warn("Trowing on %s" % str(coro)) +# try: coro.throw(StopIteration) +# except StopIteration: pass @cocotb.decorators.coroutine def move_to_ro(self): - yield ReadOnly() - self.cleanup() + yield Timer(1) + self.log.warn("out of next time step") +# self.cleanup() + self.prune_routines() self._readonly = None @cocotb.decorators.coroutine def move_to_rw(self): - yield ReadWrite() - self._readwrite = None - self.playout_writes() + try: + yield ReadWrite() + self._readwrite = None + self.playout_writes() + except StopIteration: + self.log.warn("Exception caught in read_rw_handler") From 20ad59850c1d7676e67a7ba98d68b56c9819d497 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 5 Jul 2013 15:08:37 +0100 Subject: [PATCH 0161/2656] Issue #30: Multiple tests now run --- cocotb/decorators.py | 17 +++++----- cocotb/drivers/__init__.py | 11 +++---- cocotb/regression.py | 53 +++++++++++++++--------------- cocotb/scheduler.py | 67 +++++++++++++++++++------------------- cocotb/triggers.py | 23 +++++++++++++ 5 files changed, 94 insertions(+), 77 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index f9455535..69dc3602 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -29,7 +29,6 @@ import cocotb from cocotb.triggers import Join -from cocotb.regression import xunit_output def public(f): """Use a decorator to avoid retyping function/class names. @@ -185,14 +184,6 @@ def __init__(self, timeout=None, expect_fail=False): self.started = False self.error_messages = [] - # Capture all log messages with ERROR or higher - def handle_error_msg(msg): - self.error_messages.append(msg) - - self.handler = test.ErrorLogHandler(handle_error_msg) - cocotb.log.addHandler(self.handler) - - def __call__(self, f): """ ping @@ -202,6 +193,13 @@ def __call__(self, f): self.log = logging.getLogger("cocotb.test.%s" % self._func.__name__) self.start_time = time.time() + # Capture all log messages with ERROR or higher + def handle_error_msg(msg): + self.error_messages.append(msg) + + self.handler = test.ErrorLogHandler(handle_error_msg) + cocotb.log.addHandler(self.handler) + def _wrapped_test(*args, **kwargs): super(test, self).__call__(*args, **kwargs) return self @@ -223,6 +221,7 @@ def _pre_send(self): def send(self, value): + self.error_messages = [] """FIXME: problem here is that we don't let the call stack unwind...""" self._pre_send() if not self.started: diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index bd3c049c..eb206e94 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -154,20 +154,17 @@ def _send(self, transaction, callback, event): @coroutine def _send_thread(self): - try: - while True: + while True: - # Sleep until we have something to send - while not self._sendQ: - yield self._pending.wait() + # Sleep until we have something to send + while not self._sendQ: + yield self._pending.wait() transaction, callback, event = self._sendQ.pop(0) # Send the pending transaction self.log.info("Sending packet...") yield self._send(transaction, callback, event) self.log.info("Done, shouldn't be waiting on _send.join() anymore..") - except StopIteration: - self.log.debug("Stopping send thread on driver") class BusDriver(Driver): diff --git a/cocotb/regression.py b/cocotb/regression.py index 3ad4a83a..86b4d829 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -29,12 +29,11 @@ import time import logging +import cocotb +from cocotb.triggers import NullTrigger import simulator -import cocotb.decorators - - def _my_import(name): mod = __import__(name) components = name.split('.') @@ -62,7 +61,7 @@ def __init__(self, dut, modules, test=None): def initialise(self): - ntests = 0 + self.ntests = 0 # Auto discovery for module_name in self._modules: @@ -76,18 +75,18 @@ def initialise(self): (self._function, module_name)) self._queue.append(getattr(module, self._function)(self._dut)) - ntests = 1 + self.ntests = 1 break for thing in vars(module).values(): if hasattr(thing, "im_test"): self._queue.append(thing(self._dut)) - ntests += 1 + self.ntests += 1 self.log.info("Found test %s.%s" % (self._queue[-1]._func.__module__, self._queue[-1]._func.__name__)) - self.start_xml(ntests) + self.start_xml(self.ntests) def start_xml(self, ntests): """Write the XML header into results.txt""" @@ -126,27 +125,27 @@ def handle_result(self, result): self._running_test._func.__module__, time.time() - self._running_test.start_time)) - self.execute() - def execute(self): - self._running_test = self.next_test() - if not self._running_test: - self.tear_down() - return - cocotb.scheduler.queue(self._running_test) - -#@cocotb.decorators.test -#def test_runner(self): -# self._running_test = cocotb.regression.next_test() -# while self._running_test: -# try: -# test = cocotb.scheduler.add(self._running_test) -# yield Join(test) -## except StopIteration: -# self.log.warn("Caught the bugger") - - # cocotb.regression.tear_down() -# return + cocotb.scheduler.add(self.test_runner()) + + @cocotb.decorators.coroutine + def test_runner(self): + self._running_test = cocotb.regression.next_test() + count = 1 + while self._running_test: + try: + self.log.warn("Running test %s of %d/%d" % (self._running_test, count, self.ntests)) + if count is 1: + test = cocotb.scheduler.add(self._running_test) + else: + test = cocotb.scheduler.new_test(self._running_test) + yield NullTrigger() + except StopIteration: + count+=1 + self._running_test = cocotb.regression.next_test() + + self.tear_down() + return def xunit_output(name, classname, time, skipped=False, failure="", error=""): diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 59b5b001..d6e90c82 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -52,6 +52,8 @@ def __init__(self): self._terminate = False self._test_result = None self._readonly = None + self._restart = None + self._restart_coro = None # Keep this last self._readwrite = self.add(self.move_to_rw()) @@ -102,14 +104,12 @@ def react(self, trigger): # regression handler that everything from the current # test is done and that it can go on with more if self._terminate is True: - if self._readonly is None and not self.waiting: - if self._test_result: - self.log.warn("Issue test result to regresssion object") - cocotb.regression.handle_result(self._test_result) - self._terminate = False - else: - self.log.error("Seemed to have scorchedd the earth") - die + if self._readonly is None: + self._terminate = False + if self._startpoint is not None: + self.add(self._startpoint) + self._startpoint = None + self._test_result = None # If the python has caused any subsequent events to fire we might # need to schedule more coroutines before we drop back into the @@ -146,6 +146,9 @@ def add(self, coroutine): self.schedule(coroutine) return coroutine + def new_test(self, coroutine): + self._startpoint = coroutine + def remove(self, trigger): """Remove a trigger from the list of pending coroutines""" self.waiting.pop(trigger) @@ -162,15 +165,20 @@ def prune_routines(self): Process the remove list that can have accumulatad during the execution of a parent routine """ - self.log.warn("In prune_routines") + restart = None + restart_coro = None while self._remove: delroutine, cb = self._remove.pop(0) for trigger, waiting in self.waiting.items(): for coro in waiting: if coro is delroutine: - self.log.warn("Trowing into %s" % str(coro)) + self.log.debug("Trowing into %s" % str(coro)) cb() - try:coro.throw(StopIteration) + try: + result = coro.throw(StopIteration) + if result: + self._restart = result + self._restart_coro = coro except StopIteration: pass self.waiting[trigger].remove(coro) # Clean up any triggers that no longer have pending coroutines @@ -178,7 +186,6 @@ def prune_routines(self): if not waiting: trigger.unprime() del self.waiting[trigger] - self.log.warn("Exit prune_routines") def schedule(self, coroutine, trigger=None): """ @@ -191,11 +198,6 @@ def schedule(self, coroutine, trigger=None): coroutine to be scheduled """ -# if self._terminate is True and self._readonly is None: -# self._readonly = True -# readonly = self.add(self.move_to_ro()) -# self._readonly = readonly - coroutine.log.debug("Scheduling (%s)" % str(trigger)) try: result = coroutine.send(trigger) @@ -204,7 +206,6 @@ def schedule(self, coroutine, trigger=None): # Call any pending callbacks that were waiting for this coroutine to exit exc() -# self.prune_routines() return # TestComplete indication is game over, tidy up @@ -212,13 +213,15 @@ def schedule(self, coroutine, trigger=None): # Tag that close down is needed, save the test_result # for later use in cleanup handler - self._terminate = True - self._test_result = test_result + if self._terminate is False: + self._terminate = True + self._test_result = test_result + self.log.debug("Issue test result to regresssion object") + cocotb.regression.handle_result(test_result) + self._readonly = self.add(self.move_to_cleanup()) + self.cleanup() + self.log.debug("Coroutine completed execution with TestComplete: %s" % str(coroutine)) -# self.prune_routines() - self.cleanup() -# self.prune_routines() - self._readonly = self.add(self.move_to_ro()) return # Entries may have been added to the remove list while the @@ -245,28 +248,24 @@ def schedule(self, coroutine, trigger=None): coroutine.log.debug("Finished sheduling coroutine (%s)" % str(trigger)) -# self._remove = [] - def cleanup(self): """ Clear up all our state Unprime all pending triggers and kill off any coroutines""" for trigger, waiting in self.waiting.items(): -# trigger.unprime() for coro in waiting: - self.log.warn("Killing %s" % str(coro)) + self.log.debug("Killing %s" % str(coro)) coro.kill() -# self.log.warn("Trowing on %s" % str(coro)) -# try: coro.throw(StopIteration) -# except StopIteration: pass @cocotb.decorators.coroutine - def move_to_ro(self): + def move_to_cleanup(self): yield Timer(1) - self.log.warn("out of next time step") -# self.cleanup() self.prune_routines() self._readonly = None + if self._restart is not None: + self._add_trigger(self._restart, self._restart_coro) + self._restart = None + self.log.debug("Out of delay cleanup") @cocotb.decorators.coroutine def move_to_rw(self): diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 1a4d1d5b..42f3937d 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -285,3 +285,26 @@ def _cb(): def __str__(self): return self.__class__.__name__ + "(%s)" % self._coroutine.__name__ + + +class NullTrigger(PythonTrigger): + """ + Don't do anything + """ + def __init__(self): + Trigger.__init__(self) + + + def prime(self, callback): + pass + + +#class WaitException(PythonTriggers): +# """" +# Wait for an exception to be thrown into this coroutine +# """ +# def __init__(self, exception): +# super(self).__init__(self) + + + From 04e63d92911dace9eb521107f0fa1104ce7f826c Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Fri, 5 Jul 2013 15:35:59 +0100 Subject: [PATCH 0162/2656] Truncate filenames, functions etc if they're too long --- cocotb/log.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/cocotb/log.py b/cocotb/log.py index ec2134f5..4f252f3e 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -53,9 +53,18 @@ class SimLogFormatter(logging.Formatter): logging.CRITICAL: ANSI.RED_BG + ANSI.BLACK_FG + "%s" + ANSI.DEFAULT_FG + ANSI.DEFAULT_BG} - def format(self, record): """pretify the log output, annotate with simulation time""" + + # Justify and truncate + def ljust(string, chars): + if len(string) > chars: return string[:chars-2] + ".." + return string.ljust(chars) + + def rjust(string, chars): + if len(string) > chars: return string[:chars-2] + ".." + return string.rjust(chars) + if record.args: msg = record.msg % record.args else: msg = record.msg @@ -66,10 +75,10 @@ def format(self, record): timeh, timel = simulator.get_sim_time() simtime = "% 6d.%02dns" % ((timel/1000), (timel%1000)/10) - prefix = simtime + ' ' + level + record.name.ljust(_RECORD_CHARS) + \ - os.path.split(record.filename)[1].rjust(_FILENAME_CHARS) + \ - ':' + str(record.lineno).ljust(_LINENO_CHARS) + \ - ' in ' + str(record.funcName).ljust(_FUNCNAME_CHARS) + ' ' + prefix = simtime + ' ' + level + ljust(record.name, _RECORD_CHARS) + \ + rjust(os.path.split(record.filename)[1], _FILENAME_CHARS) + \ + ':' + ljust(str(record.lineno), _LINENO_CHARS) + \ + ' in ' + ljust(str(record.funcName), _FUNCNAME_CHARS) + ' ' pad = "\n" + " " * (len(prefix) - 10) return prefix + pad.join(msg.split('\n')) From e831d9206de2445947a96d18f6be298e42ae6427 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Fri, 5 Jul 2013 15:36:33 +0100 Subject: [PATCH 0163/2656] Remove warning, support expected failures --- cocotb/regression.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 86b4d829..a00a3c2b 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -29,11 +29,15 @@ import time import logging -import cocotb -from cocotb.triggers import NullTrigger + import simulator +import cocotb +from cocotb.triggers import NullTrigger + +import cocotb.ANSI as ANSI + def _my_import(name): mod = __import__(name) components = name.split('.') @@ -115,7 +119,8 @@ def handle_result(self, result): Args: result (TestComplete exception) """ - if isinstance(result, cocotb.decorators.TestCompleteFail): + if isinstance(result, cocotb.decorators.TestCompleteFail) and not \ + self._running_test.expect_fail: self._fout.write(xunit_output(self._running_test._func.__name__, self._running_test._func.__module__, time.time() - self._running_test.start_time, @@ -134,7 +139,12 @@ def test_runner(self): count = 1 while self._running_test: try: - self.log.warn("Running test %s of %d/%d" % (self._running_test, count, self.ntests)) + # Want this to stand out a little bit + self.log.info("%sRunning test %d/%d:%s %s" % ( + ANSI.BLUE_BG +ANSI.BLACK_FG, + count, self.ntests, + ANSI.DEFAULT_FG + ANSI.DEFAULT_BG, + self._running_test)) if count is 1: test = cocotb.scheduler.add(self._running_test) else: From 193d4704ea954fc1ad3d7304715a8f8ff1ddec66 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Fri, 5 Jul 2013 15:45:47 +0100 Subject: [PATCH 0164/2656] Change regression XML output to have a package name --- cocotb/regression.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index a00a3c2b..b050fecf 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -96,7 +96,7 @@ def start_xml(self, ntests): """Write the XML header into results.txt""" self._fout = open("results.xml", 'w') self._fout.write("""\n""") - self._fout.write("""\n""" % ntests) + self._fout.write("""\n""" % ntests) def tear_down(self): """It's the end of the world as we know it""" From 54c3e196f06adb1b914def0d2e34d3a1e9937e74 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Fri, 5 Jul 2013 15:46:19 +0100 Subject: [PATCH 0165/2656] Example of combining tests from multiple modules together --- cocotb/__init__.py | 2 +- examples/functionality/tests/Makefile | 2 +- examples/functionality/tests/test_multiple_modules.py | 9 +++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 examples/functionality/tests/test_multiple_modules.py diff --git a/cocotb/__init__.py b/cocotb/__init__.py index b4a71694..6c4a7646 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -92,7 +92,7 @@ def _initialise_testbench(root_handle): raise ImportError("Environment variables defining the module(s) to \ execute not defined. MODULE=\"%s\"\"" % (module_str)) - modules = module_str.split() + modules = module_str.split(',') global regression diff --git a/examples/functionality/tests/Makefile b/examples/functionality/tests/Makefile index bd71e364..b0d67264 100644 --- a/examples/functionality/tests/Makefile +++ b/examples/functionality/tests/Makefile @@ -29,7 +29,7 @@ TOPLEVEL = sample_module VERILOG_SOURCES = ../hdl/sample_module.v -MODULE=test_cocotb +MODULE=test_cocotb,test_multiple_modules include ../../../makefiles/Makefile.inc include ../../../makefiles/Makefile.sim diff --git a/examples/functionality/tests/test_multiple_modules.py b/examples/functionality/tests/test_multiple_modules.py new file mode 100644 index 00000000..f3c2286d --- /dev/null +++ b/examples/functionality/tests/test_multiple_modules.py @@ -0,0 +1,9 @@ +import cocotb +from cocotb.triggers import Timer, Join + +@cocotb.test(expect_fail=False) +def simple_test(dut): + """Simple test in another module""" + yield Timer(10000) + + From ffda634fb3796d5bef9606925f2c8fb5d65c0158 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 5 Jul 2013 15:55:58 +0100 Subject: [PATCH 0166/2656] Issue #30: Do not schedule nested routines again if in terminate --- cocotb/scheduler.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index d6e90c82..751acc95 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -236,11 +236,13 @@ def schedule(self, coroutine, trigger=None): self.log.debug("Scheduling nested co-routine: %s" % result.__name__) # Queue this routine to schedule when the nested routine exits self._add_trigger(result.join(), coroutine) - self.schedule(result) + if self._terminate is False: + self.schedule(result) elif isinstance(result, list): - for trigger in result: - trigger.addpeers(result) - self._add_trigger(trigger, coroutine) + if self._terminate is False: + for trigger in result: + trigger.addpeers(result) + self._add_trigger(trigger, coroutine) else: self.log.warning("Unable to schedule coroutine since it's returning stuff %s" % repr(result)) self.cleanup() From 3a8426571b78d30a0ef4559e1d9887a0d0f84177 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Fri, 5 Jul 2013 07:42:57 -0500 Subject: [PATCH 0167/2656] Moved LINKER ARGS and PYINCLUDE defines to top level makefiles. Added OS differentiation for OS X/Darwin. Use python-config in Makefile.pylib to find Python install --- Makefile | 2 +- examples/comb_seq/results.xml | 2 ++ examples/demo/results.xml | 2 ++ lib/embed/Makefile | 6 +++--- lib/gpi/Makefile | 6 +++--- lib/simulator/Makefile | 6 +++--- lib/vpi_shim/Makefile | 10 +++++----- makefiles/Makefile.inc | 24 +++++++++++++++++++++--- makefiles/Makefile.pylib | 13 ++++++++++--- makefiles/Makefile.vcs | 2 +- 10 files changed, 51 insertions(+), 22 deletions(-) create mode 100644 examples/comb_seq/results.xml create mode 100644 examples/demo/results.xml diff --git a/Makefile b/Makefile index a231c8d7..389be7ce 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ clean: -@find . -name "obj" | xargs rm -rf -@find . -name "*.pyc" | xargs rm -rf -test: +test: $(LIBS) $(MAKE) -C examples pycode: diff --git a/examples/comb_seq/results.xml b/examples/comb_seq/results.xml new file mode 100644 index 00000000..88ff6976 --- /dev/null +++ b/examples/comb_seq/results.xml @@ -0,0 +1,2 @@ + + diff --git a/examples/demo/results.xml b/examples/demo/results.xml new file mode 100644 index 00000000..d35878de --- /dev/null +++ b/examples/demo/results.xml @@ -0,0 +1,2 @@ + + diff --git a/lib/embed/Makefile b/lib/embed/Makefile index f4a6d9c8..9dd19bf1 100644 --- a/lib/embed/Makefile +++ b/lib/embed/Makefile @@ -28,7 +28,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj -INCLUDES += -I/usr/include/python$(PYTHON_VERSION)/ +INCLUDES += $(PYINCLUDES) GCC_ARGS += LIBS := -lpython2.7 $(PYLIBS) LD_PATH := -L$(LIB_DIR) @@ -41,13 +41,13 @@ LIB_NAME := libcocotb.so all: $(LIB_DIR)/$(LIB_NAME) $(OBJ_DIR)/%.o: %.c - gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $@ $< + gcc $(GCC_ARGS) -c $(INCLUDES) -o $@ $< $(OBJ_DIR): mkdir -p $(OBJ_DIR) $(LIB_DIR)/$(LIB_NAME): $(OBJ_DIR) $(OBJS) - gcc $(GCC_ARGS) -shared -Xlinker -export-dynamic -o $@ $(OBJS) $(LIBS) $(LD_PATH) + gcc $(GCC_ARGS) $(LINKER_ARGS) -o $@ $(OBJS) $(LIBS) $(LD_PATH) clean: rm -rf $(OBJ_DIR) diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index 3a6c983e..3d033891 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -29,7 +29,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj -INCLUDES += -I/usr/include/python$(PYTHON_VERSION)/ +INCLUDES += GCC_ARGS += -DFILTER SRCS := gpi_logging.c @@ -38,13 +38,13 @@ OBJS := $(SRCS:%.c=$(OBJ_DIR)/%.o) all: $(LIB_DIR)/libgpilog.so $(OBJ_DIR)/%.o: %.c - gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $@ $< + gcc $(GCC_ARGS) -c $(INCLUDES) -o $@ $< $(OBJ_DIR): mkdir -p $(OBJ_DIR) $(LIB_DIR)/libgpilog.so: $(OBJ_DIR) $(OBJS) - gcc $(GCC_ARGS) -shared -export-dynamic -o $@ $(OBJS) -lpython2.7 + gcc $(GCC_ARGS) $(LINKER_ARGS) -o $@ $(OBJS) -lpython2.7 clean: rm -rf $(OBJ_DIR) diff --git a/lib/simulator/Makefile b/lib/simulator/Makefile index 9006459d..6da92eab 100644 --- a/lib/simulator/Makefile +++ b/lib/simulator/Makefile @@ -27,7 +27,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj -INCLUDES += -I/usr/include/python$(PYTHON_VERSION)/ +INCLUDES += $(PYINCLUDES) GCC_ARGS += LIBS := -lgpi -lpython2.7 $(PYLIBS) LD_PATH := -L$(LIB_DIR) @@ -40,13 +40,13 @@ LIB_NAME := libsim.so all : $(LIB_DIR)/simulator.so $(OBJ_DIR)/%.o: %.c - gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $@ $< + gcc $(GCC_ARGS) -c $(INCLUDES) -o $@ $< $(OBJ_DIR) : mkdir -p $(OBJ_DIR) $(LIB_DIR)/$(LIB_NAME) : $(OBJ_DIR) $(OBJS) - gcc $(GCC_ARGS) -shared -Xlinker -export-dynamic -o $@ $(OBJS) $(LIBS) $(LD_PATH) + gcc $(GCC_ARGS) $(LINKER_ARGS) -o $@ $(OBJS) $(LIBS) $(LD_PATH) $(LIB_DIR)/simulator.so: $(LIB_DIR)/$(LIB_NAME) ln -sf $(LIB_NAME) $@ diff --git a/lib/vpi_shim/Makefile b/lib/vpi_shim/Makefile index 15e72412..76554fde 100644 --- a/lib/vpi_shim/Makefile +++ b/lib/vpi_shim/Makefile @@ -27,7 +27,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj -INCLUDES += -I/usr/include/python$(PYTHON_VERSION)/ +INCLUDES += $(PYINCLUDES) GCC_ARGS += LIBS := -lgpilog -lcocotb LD_PATH := -L$(LIB_DIR) @@ -37,21 +37,21 @@ OBJS := $(SRCS:%.c=$(OBJ_DIR)/%.o) LIB_NAME := libgpi.so -all: $(LIB_DIR)/gpivpi.vpi +all: $(LIB_DIR)/gpivpi.vpl $(OBJ_DIR)/%.o: %.c - gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $@ $< + gcc $(GCC_ARGS) -c $(INCLUDES) -o $@ $< $(OBJ_DIR): mkdir -p $(OBJ_DIR) $(LIB_DIR)/$(LIB_NAME): $(OBJ_DIR) $(OBJS) - gcc $(GCC_ARGS) -shared -export-dynamic -o $@ $(OBJS) $(LIBS) $(LD_PATH) + gcc $(GCC_ARGS) $(LINKER_ARGS) -o $@ $(OBJS) $(LIBS) $(LD_PATH) # More rules such that this may be needed depending on the requirements of # different simulators, icarus for instance loads a .vpi libraray to use the vpi # inerface at the start of the simulation -$(LIB_DIR)/gpivpi.vpi: $(LIB_DIR)/$(LIB_NAME) +$(LIB_DIR)/gpivpi.vpl: $(LIB_DIR)/$(LIB_NAME) ln -sf $(LIB_NAME) $@ clean: diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 37f936c1..c89d5eed 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -34,17 +34,35 @@ endif BUILD_DIR=$(SIM_ROOT)/build ARCH=$(shell uname -m) +OS=$(shell uname) +export OS + include $(SIM_ROOT)/makefiles/Makefile.paths export ARCH + +include $(SIM_ROOT)/makefiles/Makefile.pylib + export BUILD_DIR export LIB_DIR=$(BUILD_DIR)/libs/$(ARCH) -export INCLUDES := -I$(SIM_ROOT)/include +export INCLUDES := -I$(SIM_ROOT)/include $(PYTHON_INCLUDEDIR) -include $(SIM_ROOT)/makefiles/Makefile.pylib # Base GCC flags -GCC_ARGS := -Werror -g -DDEBUG +ifeq ($(OS),Darwin) +GCC_ARGS := -g -DDEBUG -fpic +else +GCC_ARGS := -Werror -g -DDEBUG -fpic +endif + + +ifeq ($(OS),Darwin) +LINKER_ARGS := -shared -undefined suppress -flat_namespace -L$(PYTHON_LIBDIR) + +#/Users/gmcgregor/src/IMP/python_install/lib/ +else +LINKER_ARGS := -shared -Xlinker -export-dynamic +endif # Disable some inbuild rules %: %.c diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib index 7d19e0e4..af57d79c 100644 --- a/makefiles/Makefile.pylib +++ b/makefiles/Makefile.pylib @@ -27,18 +27,25 @@ # All common pyhon related rules +PYTHON_PREFIX = $(shell python-config --prefix) + ifeq ($(ARCH),x86_64) -LIBDIR:=lib64 +LIBDIR:=lib else LIBDIR:=lib endif # We might work with other Python versions PYTHON_VERSION?=2.7 -PYTHON_DIR:=/usr/$(LIBDIR)/python$(PYTHON_VERSION) +PYTHON_LIBDIR:=$(PYTHON_PREFIX)/$(LIBDIR)/ # Since we don't know which modules we might need or whether the simulator # we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports # is we simply link against all dynamic libraries in the Python installation -PYLIBS = $(wildcard $(PYTHON_DIR)/lib-dynload/*.so) +ifeq ($(OS),Linux) +PYLIBS = $(wildcard $(PYTHON_LIBDIR)/python$(PYTHON_VERSION)/lib-dynload/*.so) +else +PYLIBS = +endif +PYTHON_INCLUDEDIR := -I$(PYTHON_PREFIX)/include/python$(PYTHON_VERSION)/ \ No newline at end of file diff --git a/makefiles/Makefile.vcs b/makefiles/Makefile.vcs index 4a940ab8..1bcbbcde 100644 --- a/makefiles/Makefile.vcs +++ b/makefiles/Makefile.vcs @@ -1,4 +1,4 @@ -############################################################################### +:q############################################################################### # Copyright (c) 2013 Potential Ventures Ltd # All rights reserved. # From 6b2ee4180594cbf3545ee6b74ec5f2d3f9c39202 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Fri, 5 Jul 2013 07:44:34 -0500 Subject: [PATCH 0168/2656] cleaning results.xml --- Makefile | 1 + examples/comb_seq/results.xml | 2 -- examples/demo/results.xml | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 examples/comb_seq/results.xml delete mode 100644 examples/demo/results.xml diff --git a/Makefile b/Makefile index 389be7ce..f0a8f968 100644 --- a/Makefile +++ b/Makefile @@ -50,6 +50,7 @@ clean: -@rm -rf $(BUILD_DIR) -@find . -name "obj" | xargs rm -rf -@find . -name "*.pyc" | xargs rm -rf + -@find . -name "results.xml" | xargs rm -rf test: $(LIBS) $(MAKE) -C examples diff --git a/examples/comb_seq/results.xml b/examples/comb_seq/results.xml deleted file mode 100644 index 88ff6976..00000000 --- a/examples/comb_seq/results.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/examples/demo/results.xml b/examples/demo/results.xml deleted file mode 100644 index d35878de..00000000 --- a/examples/demo/results.xml +++ /dev/null @@ -1,2 +0,0 @@ - - From a230b8f5d651acb9194810891457eea018525433 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Fri, 5 Jul 2013 12:50:49 +0000 Subject: [PATCH 0169/2656] Supporting other versions of Python than 2.7 --- lib/embed/Makefile | 2 +- lib/gpi/Makefile | 2 +- lib/simulator/Makefile | 2 +- makefiles/Makefile.inc | 2 +- makefiles/Makefile.pylib | 6 +++--- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/embed/Makefile b/lib/embed/Makefile index 9dd19bf1..1f9abd87 100644 --- a/lib/embed/Makefile +++ b/lib/embed/Makefile @@ -30,7 +30,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj INCLUDES += $(PYINCLUDES) GCC_ARGS += -LIBS := -lpython2.7 $(PYLIBS) +LIBS := $(PYLIBS) LD_PATH := -L$(LIB_DIR) SRCS := gpi_embed.c diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index 3d033891..9259544e 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -44,7 +44,7 @@ $(OBJ_DIR): mkdir -p $(OBJ_DIR) $(LIB_DIR)/libgpilog.so: $(OBJ_DIR) $(OBJS) - gcc $(GCC_ARGS) $(LINKER_ARGS) -o $@ $(OBJS) -lpython2.7 + gcc $(GCC_ARGS) $(LINKER_ARGS) -o $@ $(OBJS) clean: rm -rf $(OBJ_DIR) diff --git a/lib/simulator/Makefile b/lib/simulator/Makefile index 6da92eab..fbe5a09c 100644 --- a/lib/simulator/Makefile +++ b/lib/simulator/Makefile @@ -29,7 +29,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj INCLUDES += $(PYINCLUDES) GCC_ARGS += -LIBS := -lgpi -lpython2.7 $(PYLIBS) +LIBS := -lgpi $(PYLIBS) LD_PATH := -L$(LIB_DIR) SRCS := simulatormodule.c diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index c89d5eed..d28184f8 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -57,7 +57,7 @@ endif ifeq ($(OS),Darwin) -LINKER_ARGS := -shared -undefined suppress -flat_namespace -L$(PYTHON_LIBDIR) +LINKER_ARGS := -shared -undefined suppress -flat_namespace -L$(PYTHON_LIBDIR) #/Users/gmcgregor/src/IMP/python_install/lib/ else diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib index af57d79c..e36d7952 100644 --- a/makefiles/Makefile.pylib +++ b/makefiles/Makefile.pylib @@ -36,16 +36,16 @@ LIBDIR:=lib endif # We might work with other Python versions -PYTHON_VERSION?=2.7 +PYTHON_VERSION?=2.6 PYTHON_LIBDIR:=$(PYTHON_PREFIX)/$(LIBDIR)/ # Since we don't know which modules we might need or whether the simulator # we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports # is we simply link against all dynamic libraries in the Python installation ifeq ($(OS),Linux) -PYLIBS = $(wildcard $(PYTHON_LIBDIR)/python$(PYTHON_VERSION)/lib-dynload/*.so) +PYLIBS = $(wildcard $(PYTHON_LIBDIR)/python$(PYTHON_VERSION)/lib-dynload/*.so) -lpython$(PYTHON_VERSION) else PYLIBS = endif -PYTHON_INCLUDEDIR := -I$(PYTHON_PREFIX)/include/python$(PYTHON_VERSION)/ \ No newline at end of file +PYTHON_INCLUDEDIR := -I$(PYTHON_PREFIX)/include/python$(PYTHON_VERSION)/ From 70df0b10bfe1acbdbfbaa08b595c671324795c72 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Fri, 5 Jul 2013 12:56:21 +0000 Subject: [PATCH 0170/2656] Using Python platform lib to find major release/ version number --- makefiles/Makefile.pylib | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib index e36d7952..53ba06bf 100644 --- a/makefiles/Makefile.pylib +++ b/makefiles/Makefile.pylib @@ -36,14 +36,14 @@ LIBDIR:=lib endif # We might work with other Python versions -PYTHON_VERSION?=2.6 +PYTHON_VERSION?=$(shell python -c 'import platform; print ".".join(platform.python_version_tuple()[0:2])') PYTHON_LIBDIR:=$(PYTHON_PREFIX)/$(LIBDIR)/ # Since we don't know which modules we might need or whether the simulator # we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports # is we simply link against all dynamic libraries in the Python installation ifeq ($(OS),Linux) -PYLIBS = $(wildcard $(PYTHON_LIBDIR)/python$(PYTHON_VERSION)/lib-dynload/*.so) -lpython$(PYTHON_VERSION) +PYLIBS = $(wildcard $(PYTHON_LIBDIR)/python$(PYTHON_VERSION)/lib-dynload/*.so) $(shell python-config --libs) else PYLIBS = endif From f3b5e5d583c100deb703fa8946893ca42dee337e Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Fri, 5 Jul 2013 08:02:33 -0500 Subject: [PATCH 0171/2656] testing on OS X adding PYLIBS python-config --libs for both OS --- makefiles/Makefile.pylib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib index 53ba06bf..a0dc5eae 100644 --- a/makefiles/Makefile.pylib +++ b/makefiles/Makefile.pylib @@ -45,7 +45,7 @@ PYTHON_LIBDIR:=$(PYTHON_PREFIX)/$(LIBDIR)/ ifeq ($(OS),Linux) PYLIBS = $(wildcard $(PYTHON_LIBDIR)/python$(PYTHON_VERSION)/lib-dynload/*.so) $(shell python-config --libs) else -PYLIBS = +PYLIBS = $(shell python-config --libs) endif PYTHON_INCLUDEDIR := -I$(PYTHON_PREFIX)/include/python$(PYTHON_VERSION)/ From 7687331a4828d09c2abe66942c497e0890beb6a9 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Fri, 5 Jul 2013 08:04:11 -0500 Subject: [PATCH 0172/2656] removing vim typo --- makefiles/Makefile.vcs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/Makefile.vcs b/makefiles/Makefile.vcs index 1bcbbcde..4a940ab8 100644 --- a/makefiles/Makefile.vcs +++ b/makefiles/Makefile.vcs @@ -1,4 +1,4 @@ -:q############################################################################### +############################################################################### # Copyright (c) 2013 Potential Ventures Ltd # All rights reserved. # From 0a343282aa62077e8c7d457a82756ed03b0a0f0d Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Fri, 5 Jul 2013 08:06:29 -0500 Subject: [PATCH 0173/2656] removing redundant PYINCLUDES --- lib/embed/Makefile | 2 +- lib/simulator/Makefile | 2 +- lib/vpi_shim/Makefile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/embed/Makefile b/lib/embed/Makefile index 1f9abd87..6aa3818e 100644 --- a/lib/embed/Makefile +++ b/lib/embed/Makefile @@ -28,7 +28,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj -INCLUDES += $(PYINCLUDES) +INCLUDES += GCC_ARGS += LIBS := $(PYLIBS) LD_PATH := -L$(LIB_DIR) diff --git a/lib/simulator/Makefile b/lib/simulator/Makefile index fbe5a09c..833798d2 100644 --- a/lib/simulator/Makefile +++ b/lib/simulator/Makefile @@ -27,7 +27,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj -INCLUDES += $(PYINCLUDES) +INCLUDES += GCC_ARGS += LIBS := -lgpi $(PYLIBS) LD_PATH := -L$(LIB_DIR) diff --git a/lib/vpi_shim/Makefile b/lib/vpi_shim/Makefile index 76554fde..c1db7fb9 100644 --- a/lib/vpi_shim/Makefile +++ b/lib/vpi_shim/Makefile @@ -27,7 +27,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc OBJ_DIR := obj -INCLUDES += $(PYINCLUDES) +INCLUDES += GCC_ARGS += LIBS := -lgpilog -lcocotb LD_PATH := -L$(LIB_DIR) From a50bba3732990be4ea6e0a46684a638f27041315 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Fri, 5 Jul 2013 09:02:06 -0500 Subject: [PATCH 0174/2656] using distutils to find lib dir --- makefiles/Makefile.pylib | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib index a0dc5eae..05b8530b 100644 --- a/makefiles/Makefile.pylib +++ b/makefiles/Makefile.pylib @@ -29,23 +29,24 @@ PYTHON_PREFIX = $(shell python-config --prefix) -ifeq ($(ARCH),x86_64) -LIBDIR:=lib -else -LIBDIR:=lib -endif - # We might work with other Python versions -PYTHON_VERSION?=$(shell python -c 'import platform; print ".".join(platform.python_version_tuple()[0:2])') -PYTHON_LIBDIR:=$(PYTHON_PREFIX)/$(LIBDIR)/ +PYTHON_VERSION?=$(shell python -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_version()') +PYTHON_LIBDIR:=$(shell python -c 'import os.path,distutils.sysconfig;print os.path.sep+os.path.join(*list(distutils.sysconfig.get_python_lib(standard_lib=True).split(os.path.sep)[:-1]))') # Since we don't know which modules we might need or whether the simulator # we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports # is we simply link against all dynamic libraries in the Python installation ifeq ($(OS),Linux) -PYLIBS = $(wildcard $(PYTHON_LIBDIR)/python$(PYTHON_VERSION)/lib-dynload/*.so) $(shell python-config --libs) +PYLIBS = $(wildcard $(PYTHON_LIBDIR)/python$(PYTHON_VERSION)lib-dynload/*.so) $(shell python-config --libs) else PYLIBS = $(shell python-config --libs) endif -PYTHON_INCLUDEDIR := -I$(PYTHON_PREFIX)/include/python$(PYTHON_VERSION)/ +PYTHON_INCLUDEDIR := -I$(shell python -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_inc()') + + + + + + + From e6ea1be2e729cc77cec4894f46ddb81e0bcf2442 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Fri, 5 Jul 2013 17:05:38 +0100 Subject: [PATCH 0175/2656] Issue #30: Test for _startpoint attribute before using --- cocotb/scheduler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 751acc95..e1244047 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -106,7 +106,7 @@ def react(self, trigger): if self._terminate is True: if self._readonly is None: self._terminate = False - if self._startpoint is not None: + if hasattr(self, "_startpoint") and self._startpoint is not None: self.add(self._startpoint) self._startpoint = None self._test_result = None From 68deeb2d55ec9eab2e6554d57596c2c1a6987600 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Fri, 5 Jul 2013 17:13:46 +0100 Subject: [PATCH 0176/2656] Cleanup (issue #30): Switch to default assignment rather than hasattr test --- cocotb/scheduler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index e1244047..708e1720 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -49,6 +49,7 @@ def __init__(self): self.writes_lock = threading.RLock() self._remove = [] self._pending_adds = [] + self._startpoint = None self._terminate = False self._test_result = None self._readonly = None @@ -106,7 +107,7 @@ def react(self, trigger): if self._terminate is True: if self._readonly is None: self._terminate = False - if hasattr(self, "_startpoint") and self._startpoint is not None: + if self._startpoint is not None: self.add(self._startpoint) self._startpoint = None self._test_result = None From 80b3ac8e5217e9eb640d312b1987cbfc0a00b7f5 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Fri, 5 Jul 2013 17:20:32 +0100 Subject: [PATCH 0177/2656] Added new test which exposes a bug in the regression manager --- examples/functionality/tests/test_cocotb.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 074c418f..2609b391 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -108,3 +108,15 @@ def test_coroutine_kill(dut): yield Timer(1000) if test_flag is not True: raise cocotb.TestFailed + +@cocotb.test() +def test_adding_a_coroutine_without_starting(dut): + """Catch (and provide useful error) for attempts to fork coroutines incorrectly""" + yield Timer(100) + forked = cocotb.fork(clock_gen) + yield Timer(100) + yield Join(forked) + yield Timer(100) + + + From 81d4864f362e1fe34b3e420fdfe90a5987c27bc0 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Fri, 5 Jul 2013 09:14:46 -0500 Subject: [PATCH 0178/2656] XUnit regression logging --- cocotb/regression.py | 75 +++++------------------------- cocotb/xunit_reporter.py | 98 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 64 deletions(-) create mode 100644 cocotb/xunit_reporter.py diff --git a/cocotb/regression.py b/cocotb/regression.py index b050fecf..a7b32b25 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -37,6 +37,8 @@ from cocotb.triggers import NullTrigger import cocotb.ANSI as ANSI +import cocotb.decorators +from xunit_reporter import XUnitReporter def _my_import(name): mod = __import__(name) @@ -90,45 +92,33 @@ def initialise(self): (self._queue[-1]._func.__module__, self._queue[-1]._func.__name__)) - self.start_xml(self.ntests) - def start_xml(self, ntests): - """Write the XML header into results.txt""" - self._fout = open("results.xml", 'w') - self._fout.write("""\n""") - self._fout.write("""\n""" % ntests) + self.xunit = XUnitReporter() + self.xunit.add_testsuite(name="all", tests=repr(self.ntests), package="all") def tear_down(self): """It's the end of the world as we know it""" - self._fout.write("") - self._fout.close() self.log.info("Shutting down...") + self.xunit.write() simulator.stop_simulator() def next_test(self): """Get the next test to run""" - if not self._queue: return None + print 'next_test ', len(self._queue) return self._queue.pop(0) def handle_result(self, result): """Handle a test result - Dumps result to XML and schedules the next test (if any) - Args: result (TestComplete exception) """ - if isinstance(result, cocotb.decorators.TestCompleteFail) and not \ - self._running_test.expect_fail: - self._fout.write(xunit_output(self._running_test._func.__name__, - self._running_test._func.__module__, - time.time() - self._running_test.start_time, - failure="\n".join(self._running_test.error_messages))) - else: - self._fout.write(xunit_output(self._running_test._func.__name__, - self._running_test._func.__module__, - time.time() - self._running_test.start_time)) + self.xunit.add_testcase(name = self._running_test._func.__name__, + classname=self._running_test._func.__module__, + time=time.time() - self._running_test.start_time) + if isinstance(result, cocotb.decorators.TestCompleteFail): + self.xunit.add_failure("\n".join(self._running_test.error_messages)) def execute(self): cocotb.scheduler.add(self.test_runner()) @@ -156,46 +146,3 @@ def test_runner(self): self.tear_down() return - - -def xunit_output(name, classname, time, skipped=False, failure="", error=""): - """ - Format at xunit test output in XML - - Args: - name (str): the name of the test - - classname (str): the name of the class - - time (float): duration of the test in seconds - - Kwargs: - skipped (bool): this test was skipped - - failure (str): failure message to report - - error (str): error message to report - - Returns an XML string - - """ - xml = """ \n" - else: - xml += ">\n" - - if skipped: - xml += " \n" - - if failure: - xml += " %s\n \n" % \ - failure - - if error: - xml += " %s\n \n" % \ - error - - return xml + " \n" diff --git a/cocotb/xunit_reporter.py b/cocotb/xunit_reporter.py new file mode 100644 index 00000000..249185d9 --- /dev/null +++ b/cocotb/xunit_reporter.py @@ -0,0 +1,98 @@ +from xml.etree.ElementTree import Element, SubElement +import xml.etree.ElementTree as ET + +TRUNCATE_LINES = 100 + +# file from http://stackoverflow.com/questions/136168/get-last-n-lines-of-a-file-with-python-similar-to-tail +class File(file): + + def countlines(self): + buf = mmap.mmap(self.fileno(), 0) + lines = 0 + while buf.readline(): + lines += 1 + return lines + + def head(self, lines_2find=1): + self.seek(0) #Rewind file + return [self.next() for x in xrange(lines_2find)] + + def tail(self, lines_2find=1): + self.seek(0, 2) #go to end of file + bytes_in_file = self.tell() + lines_found, total_bytes_scanned = 0, 0 + while (lines_2find+1 > lines_found and + bytes_in_file > total_bytes_scanned): + byte_block = min(1024, bytes_in_file-total_bytes_scanned) + self.seek(-(byte_block+total_bytes_scanned), 2) + total_bytes_scanned += byte_block + lines_found += self.read(1024).count('\n') + self.seek(-total_bytes_scanned, 2) + line_list = list(self.readlines()) + return line_list[-lines_2find:] + + +class XUnitReporter: + + def __init__(self, filename="results.xml"): + self.results = Element("testsuites", name="results") + + def add_testsuite(self, **kwargs): + self.last_testsuite = SubElement(self.results, "testsuite", **kwargs) + return self.last_testsuite + + def add_testcase(self, testsuite=None, **kwargs): + if testsuite == None: + testsuite = self.last_testsuite + self.last_testcase = SubElement(testsuite, "testcase", **kwargs) + return self.last_testcase + + def update_testsuite(self, testsuite=None, **kwargs): + if testsuite == None: + testsuite = self.last_testsuite + for k in kwargs: + testsuite.set(k, str(kwargs[k])) + + def update_testsuites(self, **kwargs): + for k in kwargs: + self.results.set(k, str(kwargs[k])) + + def add_log(self, logfile, testcase=None): + if testcase == None: + testcase = self.last_testcase + log = SubElement(testcase, "system-out") + f = File(logfile, 'r+') + lines = f.countlines() + if lines > (TRUNCATE_LINES * 2): + head = f.head(TRUNCATE_LINES) + tail = f.tail(TRUNCATE_LINES) + log.text = "".join(head + list("[...truncated %d lines...]\n" % ( (lines - (TRUNCATE_LINES*2)) )) + tail) + else: + log.text = "".join(f.readlines()) + + def add_failure(self, testcase=None, **kwargs): + if testcase == None: + testcase = self.last_testcase + log = SubElement(testcase, "failure", **kwargs) + + + def indent(self, elem, level=0): + i = "\n" + level*" " + if len(elem): + if not elem.text or not elem.text.strip(): + elem.text = i + " " + if not elem.tail or not elem.tail.strip(): + elem.tail = i + for elem in elem: + self.indent(elem, level+1) + if not elem.tail or not elem.tail.strip(): + elem.tail = i + else: + if level and (not elem.tail or not elem.tail.strip()): + elem.tail = i + + def write(self): + print 'Capturing results to ', self.filename + self.indent(self.results) + ET.ElementTree(self.results).write(self.filename, xml_declaration = True, method = "xml", encoding="UTF-8") + From 18a7952dfa136199cca5fb7cffe31745df8b98a7 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Fri, 5 Jul 2013 11:44:57 -0500 Subject: [PATCH 0179/2656] Cleaned up XUnitReporter inherits from object --- cocotb/regression.py | 2 +- cocotb/xunit_reporter.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index a7b32b25..48c4397e 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -104,7 +104,7 @@ def tear_down(self): def next_test(self): """Get the next test to run""" - print 'next_test ', len(self._queue) + if not self._queue: return None return self._queue.pop(0) diff --git a/cocotb/xunit_reporter.py b/cocotb/xunit_reporter.py index 249185d9..7f73c10e 100644 --- a/cocotb/xunit_reporter.py +++ b/cocotb/xunit_reporter.py @@ -32,7 +32,7 @@ def tail(self, lines_2find=1): return line_list[-lines_2find:] -class XUnitReporter: +class XUnitReporter(object): def __init__(self, filename="results.xml"): self.results = Element("testsuites", name="results") From bc2e435f0ec155fc912bf3eb91efbaab1ca7bfb9 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Fri, 5 Jul 2013 12:06:41 -0500 Subject: [PATCH 0180/2656] adding filename to XUnitReporter --- cocotb/xunit_reporter.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cocotb/xunit_reporter.py b/cocotb/xunit_reporter.py index 7f73c10e..6b29526e 100644 --- a/cocotb/xunit_reporter.py +++ b/cocotb/xunit_reporter.py @@ -36,6 +36,7 @@ class XUnitReporter(object): def __init__(self, filename="results.xml"): self.results = Element("testsuites", name="results") + self.filename = filename def add_testsuite(self, **kwargs): self.last_testsuite = SubElement(self.results, "testsuite", **kwargs) From 0dfaa90317c9817da19cc0b0e574cd004e493c1b Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Fri, 5 Jul 2013 12:09:00 -0500 Subject: [PATCH 0181/2656] adding repr wrapper around time value --- cocotb/regression.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 48c4397e..7e7c2ca6 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -116,7 +116,7 @@ def handle_result(self, result): self.xunit.add_testcase(name = self._running_test._func.__name__, classname=self._running_test._func.__module__, - time=time.time() - self._running_test.start_time) + time=repr(time.time() - self._running_test.start_time)) if isinstance(result, cocotb.decorators.TestCompleteFail): self.xunit.add_failure("\n".join(self._running_test.error_messages)) From 5d247239ce25c054d239bab51b3a2b2919e3528c Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Fri, 5 Jul 2013 15:56:31 -0500 Subject: [PATCH 0182/2656] expose argv/argc from simulation command line to cocotb --- examples/plusargs/Makefile | 37 ++++++++++++ examples/plusargs/counter.v | 87 +++++++++++++++++++++++++++ examples/plusargs/demo.py | 116 ++++++++++++++++++++++++++++++++++++ include/embed.h | 3 +- include/vpi_user.h | 9 +++ lib/embed/gpi_embed.c | 21 ++++++- lib/vpi_shim/gpi_vpi.c | 4 +- makefiles/Makefile.icarus | 4 +- 8 files changed, 275 insertions(+), 6 deletions(-) create mode 100644 examples/plusargs/Makefile create mode 100644 examples/plusargs/counter.v create mode 100644 examples/plusargs/demo.py diff --git a/examples/plusargs/Makefile b/examples/plusargs/Makefile new file mode 100644 index 00000000..af9305a5 --- /dev/null +++ b/examples/plusargs/Makefile @@ -0,0 +1,37 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +# FIXME these should come from some cunning script, possibly from the FDK :) +TOPLEVEL = first_counter + +VERILOG_SOURCES = counter.v +MODULE=demo + +include ../../makefiles/Makefile.inc +include ../../makefiles/Makefile.sim + diff --git a/examples/plusargs/counter.v b/examples/plusargs/counter.v new file mode 100644 index 00000000..7e95350a --- /dev/null +++ b/examples/plusargs/counter.v @@ -0,0 +1,87 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2013 Potential Ventures Ltd +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Potential Ventures Ltd nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +//----------------------------------------------------- +// This is my second Verilog Design +// Design Name : first_counter +// File Name : first_counter.v +// Function : This is a 4 bit up-counter with +// Synchronous active high reset and +// with active high enable signal +//----------------------------------------------------- +module first_counter ( +clock , // Clock input of the design +reset , // active high, synchronous Reset input +enable , // Active high enable signal for counter +counter_out, // 4 bit vector output of the counter +test_in, // test input +test_out // test output +); // End of port list +//-------------Input Ports----------------------------- +input clock ; +input reset ; +input enable ; +input test_in ; +//-------------Output Ports---------------------------- +output [3:0] counter_out ; +output test_out ; +//-------------Input ports Data Type------------------- +// By rule all the input ports should be wires +wire clock ; +wire reset ; +wire enable ; +wire test_in ; +//-------------Output Ports Data Type------------------ +// Output port can be a storage element (reg) or a wire +reg [3:0] counter_out ; +reg test_out ; + +initial begin + $dumpfile("dump.vcd"); + $dumpvars(0,first_counter); +end + +//------------Code Starts Here------------------------- +// Since this counter is a positive edge trigged one, +// We trigger the below block with respect to positive +// edge of the clock. +always @ (posedge clock) +begin : COUNTER // Block Name + // At every rising edge of clock we check if reset is active + // If active, we load the counter output with 4'b0000 + if (reset == 1'b1) begin + counter_out <= #1 4'b0000; + test_out <= #1 0; + end + // If enable is active, then we increment the counter + else if (enable == 1'b1) begin + counter_out <= #1 counter_out + 1; + test_out <= test_in; + end +end // End of Block COUNTER + +endmodule // End of Module counter diff --git a/examples/plusargs/demo.py b/examples/plusargs/demo.py new file mode 100644 index 00000000..7cda68ff --- /dev/null +++ b/examples/plusargs/demo.py @@ -0,0 +1,116 @@ +#!/bin/python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" + Example usage of the testbench framework +""" + +import cocotb +from cocotb.decorators import coroutine +from cocotb.triggers import Timer, Edge, Event + +import sys + +@coroutine +def clock_generator(signal): + for i in range(10): + signal <= 0 + yield Timer(1000) + signal <= 1 + yield Timer(1000) + signal.log.warning("Clock generator finished!") + +@coroutine +def clock_monitor(signal, output): + while True: + yield Edge(signal) + signal.log.info("Edge triggered: %s : %s = %s" % (signal.getvalue().value, str(output), output.getvalue().value)) + + +@coroutine +def reset_dut(clock, reset, enable): + clock_ticks = 0 + reset <= 1 + enable <= 0 + while True: + yield Edge(clock) + clock_ticks += 1 + if clock_ticks >= 4: + reset <= 0 + enable <= 1 + break + reset.log.info("Released reset: %s" % reset.getvalue()) + + +@coroutine +def waiting_coroutine(some_event): + some_event.log.info("Putting waiting coroutine to sleep until this event fires") + yield some_event.wait() + some_event.log.info("Coroutine woke up again! Awesome") + +@cocotb.test() +def example_test(dut): + """This is an example test""" + dut.log.info("Example test got DUT:" + str(dut)) + + yield Timer(10000) + + clk = dut.clock + enable = dut.enable + reset = dut.reset + count = dut.counter_out + + dut.log.info(str(clk)) + + print cocotb.argv + print cocotb.argc + + cgen = cocotb.scheduler.add(clock_generator(clk)) + yield reset_dut(clk, reset, enable) + dut.log.info("Reset DUT complete, continuing test...") + cmon = cocotb.scheduler.add(clock_monitor(clk, count)) + + dut.log.info("Blocking test until the clock generator finishes...") + yield cgen.join() + + sync = Event() + cocotb.scheduler.add(waiting_coroutine(sync)) + yield Timer(10000) + dut.log.info("Waking up the waiting coroutine with an event...") + sync.set() + yield Timer(10000) + + + result = yield Timer(1000000) + dut.log.warning("test complete!") + + +@cocotb.test() +def example_test2(dut): + """This is another example test""" + result = yield Timer(1000000) + dut.log.warning("test complete!") diff --git a/include/embed.h b/include/embed.h index f0c2c144..2d5eeaea 100644 --- a/include/embed.h +++ b/include/embed.h @@ -30,9 +30,10 @@ #include #include +#include extern void embed_init_python(void); -extern void embed_sim_init(void); +extern void embed_sim_init(p_vpi_vlog_info info_p); extern void embed_sim_end(void); #endif /* COCOTB_EMBED_H_ */ diff --git a/include/vpi_user.h b/include/vpi_user.h index 65475393..b72e0f6d 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -67,6 +67,15 @@ typedef struct t_vpi_time #define vpiSimTime 2 #define vpiSuppressTime 3 +typedef struct t_vpi_vlog_info +{ + int32_t argc; + char **argv; + char *product; + char *version; +} s_vpi_vlog_info, *p_vpi_vlog_info; + + /* generic value */ typedef struct t_vpi_value diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index f0c82164..be14e963 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -81,10 +81,15 @@ void embed_init_python(void) * * Loads the Python module called cocotb and calls the _initialise_testbench function */ -void embed_sim_init(void) +void embed_sim_init(p_vpi_vlog_info info_p) { FENTER + int i; + PyObject *argv_list; + PyObject *argc; + + // Find the simulation root gpi_sim_hdl dut = gpi_get_root_handle(); @@ -106,6 +111,17 @@ void embed_sim_init(void) return; } + argv_list = PyList_New(0); + for (i = 0; i < info_p->argc; i++) { + pValue = PyString_FromString(info_p->argv[i]); + PyList_Append(argv_list, pValue); + } + + pDict = PyModule_GetDict(pModule); + PyDict_SetItemString(pDict, "argv", argv_list); + + argc = PyInt_FromLong(info_p->argc); + PyDict_SetItemString(pDict, "argc", argc); // Extact a reference to the logger object to unitfy logging mechanism pLogger = PyObject_GetAttrString(pModule, "log"); @@ -130,7 +146,8 @@ void embed_sim_init(void) Py_DECREF(pHandler); Py_DECREF(pRecordFn); Py_DECREF(pFilterFn); - + Py_DECREF(pDict); + // Save a handle to the lock object lock = PyObject_GetAttrString(pModule, "_rlock"); diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 4d10af8d..bcaf69f8 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -712,8 +712,10 @@ void register_embed(void) int handle_sim_init(void *gpi_cb_data) { FENTER + s_vpi_vlog_info info_s; sim_init_cb = NULL; - embed_sim_init(); + vpi_get_vlog_info(&info_s); + embed_sim_init(&info_s); FEXIT } diff --git a/makefiles/Makefile.icarus b/makefiles/Makefile.icarus index 7163bfeb..b5aa02a3 100644 --- a/makefiles/Makefile.icarus +++ b/makefiles/Makefile.icarus @@ -38,12 +38,12 @@ $(OBJ_DIR): sim: $(OBJ_DIR) $(VERILOG_SOURCES) iverilog -o $(OBJ_DIR)/sim.vvp $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ - vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp + vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp $(PLUSARGS) .PHONY: gdb gdb: $(OBJ_DIR) $(VERILOG_SOURCES) iverilog -o $(OBJ_DIR)/sim.vvp $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ - gdb --args vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp + gdb --args vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp $(PLUSARGS) clean: From ff6d9d3dbe8c25839b095ecfcce7909d65c2292e Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Fri, 5 Jul 2013 16:27:34 -0500 Subject: [PATCH 0183/2656] Process command line and populate cocotb.plusargs with any plusarg values or flags --- cocotb/__init__.py | 22 ++++++++++++++++++++++ examples/plusargs/Makefile | 2 ++ examples/plusargs/demo.py | 6 +++--- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 6c4a7646..8a282332 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -50,6 +50,8 @@ scheduler = Scheduler() regression = None +plusargs = {} + # To save typing provide an alias to scheduler.add fork = scheduler.add @@ -85,6 +87,9 @@ def _initialise_testbench(root_handle): # Create the base handle type dut = cocotb.handle.SimHandle(root_handle) + + process_plusargs() + module_str = os.getenv('MODULE') test_str = os.getenv('TESTCASE') @@ -103,3 +108,20 @@ def _initialise_testbench(root_handle): _rlock.release() return True + +def process_plusargs(): + + global plusargs + + plusargs = {} + + for option in cocotb.argv: + if option.startswith('+'): + if option.find('=') != -1: + (name, value) = option[1:].split('=') + plusargs[name] = value + else: + plusargs[option[1:]] = True + + print plusargs + diff --git a/examples/plusargs/Makefile b/examples/plusargs/Makefile index af9305a5..84ffc659 100644 --- a/examples/plusargs/Makefile +++ b/examples/plusargs/Makefile @@ -32,6 +32,8 @@ TOPLEVEL = first_counter VERILOG_SOURCES = counter.v MODULE=demo +PLUSARGS=+foo=bar +test1 +test2 +options=fubar + include ../../makefiles/Makefile.inc include ../../makefiles/Makefile.sim diff --git a/examples/plusargs/demo.py b/examples/plusargs/demo.py index 7cda68ff..9867764e 100644 --- a/examples/plusargs/demo.py +++ b/examples/plusargs/demo.py @@ -86,9 +86,9 @@ def example_test(dut): dut.log.info(str(clk)) - print cocotb.argv - print cocotb.argc - + for name in cocotb.plusargs: + print name, cocotb.plusargs[name] + cgen = cocotb.scheduler.add(clock_generator(clk)) yield reset_dut(clk, reset, enable) dut.log.info("Reset DUT complete, continuing test...") From c73b220cd2833781c453a220c52dacb536120343 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Fri, 5 Jul 2013 18:43:21 -0500 Subject: [PATCH 0184/2656] Cleaned up plusarg demo --- examples/plusargs/Makefile | 7 +- examples/plusargs/counter.v | 87 ------------------------- examples/plusargs/demo.py | 116 ---------------------------------- examples/plusargs/plusargs.py | 23 +++++++ examples/plusargs/tb_top.v | 12 ++++ 5 files changed, 38 insertions(+), 207 deletions(-) delete mode 100644 examples/plusargs/counter.v delete mode 100644 examples/plusargs/demo.py create mode 100644 examples/plusargs/plusargs.py create mode 100644 examples/plusargs/tb_top.v diff --git a/examples/plusargs/Makefile b/examples/plusargs/Makefile index 84ffc659..0f017c47 100644 --- a/examples/plusargs/Makefile +++ b/examples/plusargs/Makefile @@ -26,11 +26,10 @@ ############################################################################### -# FIXME these should come from some cunning script, possibly from the FDK :) -TOPLEVEL = first_counter +TOPLEVEL = tb_top -VERILOG_SOURCES = counter.v -MODULE=demo +VERILOG_SOURCES = tb_top.v +MODULE=plusargs PLUSARGS=+foo=bar +test1 +test2 +options=fubar diff --git a/examples/plusargs/counter.v b/examples/plusargs/counter.v deleted file mode 100644 index 7e95350a..00000000 --- a/examples/plusargs/counter.v +++ /dev/null @@ -1,87 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2013 Potential Ventures Ltd -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of Potential Ventures Ltd nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -//----------------------------------------------------------------------------- - -//----------------------------------------------------- -// This is my second Verilog Design -// Design Name : first_counter -// File Name : first_counter.v -// Function : This is a 4 bit up-counter with -// Synchronous active high reset and -// with active high enable signal -//----------------------------------------------------- -module first_counter ( -clock , // Clock input of the design -reset , // active high, synchronous Reset input -enable , // Active high enable signal for counter -counter_out, // 4 bit vector output of the counter -test_in, // test input -test_out // test output -); // End of port list -//-------------Input Ports----------------------------- -input clock ; -input reset ; -input enable ; -input test_in ; -//-------------Output Ports---------------------------- -output [3:0] counter_out ; -output test_out ; -//-------------Input ports Data Type------------------- -// By rule all the input ports should be wires -wire clock ; -wire reset ; -wire enable ; -wire test_in ; -//-------------Output Ports Data Type------------------ -// Output port can be a storage element (reg) or a wire -reg [3:0] counter_out ; -reg test_out ; - -initial begin - $dumpfile("dump.vcd"); - $dumpvars(0,first_counter); -end - -//------------Code Starts Here------------------------- -// Since this counter is a positive edge trigged one, -// We trigger the below block with respect to positive -// edge of the clock. -always @ (posedge clock) -begin : COUNTER // Block Name - // At every rising edge of clock we check if reset is active - // If active, we load the counter output with 4'b0000 - if (reset == 1'b1) begin - counter_out <= #1 4'b0000; - test_out <= #1 0; - end - // If enable is active, then we increment the counter - else if (enable == 1'b1) begin - counter_out <= #1 counter_out + 1; - test_out <= test_in; - end -end // End of Block COUNTER - -endmodule // End of Module counter diff --git a/examples/plusargs/demo.py b/examples/plusargs/demo.py deleted file mode 100644 index 9867764e..00000000 --- a/examples/plusargs/demo.py +++ /dev/null @@ -1,116 +0,0 @@ -#!/bin/python - -''' Copyright (c) 2013 Potential Ventures Ltd -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - -""" - Example usage of the testbench framework -""" - -import cocotb -from cocotb.decorators import coroutine -from cocotb.triggers import Timer, Edge, Event - -import sys - -@coroutine -def clock_generator(signal): - for i in range(10): - signal <= 0 - yield Timer(1000) - signal <= 1 - yield Timer(1000) - signal.log.warning("Clock generator finished!") - -@coroutine -def clock_monitor(signal, output): - while True: - yield Edge(signal) - signal.log.info("Edge triggered: %s : %s = %s" % (signal.getvalue().value, str(output), output.getvalue().value)) - - -@coroutine -def reset_dut(clock, reset, enable): - clock_ticks = 0 - reset <= 1 - enable <= 0 - while True: - yield Edge(clock) - clock_ticks += 1 - if clock_ticks >= 4: - reset <= 0 - enable <= 1 - break - reset.log.info("Released reset: %s" % reset.getvalue()) - - -@coroutine -def waiting_coroutine(some_event): - some_event.log.info("Putting waiting coroutine to sleep until this event fires") - yield some_event.wait() - some_event.log.info("Coroutine woke up again! Awesome") - -@cocotb.test() -def example_test(dut): - """This is an example test""" - dut.log.info("Example test got DUT:" + str(dut)) - - yield Timer(10000) - - clk = dut.clock - enable = dut.enable - reset = dut.reset - count = dut.counter_out - - dut.log.info(str(clk)) - - for name in cocotb.plusargs: - print name, cocotb.plusargs[name] - - cgen = cocotb.scheduler.add(clock_generator(clk)) - yield reset_dut(clk, reset, enable) - dut.log.info("Reset DUT complete, continuing test...") - cmon = cocotb.scheduler.add(clock_monitor(clk, count)) - - dut.log.info("Blocking test until the clock generator finishes...") - yield cgen.join() - - sync = Event() - cocotb.scheduler.add(waiting_coroutine(sync)) - yield Timer(10000) - dut.log.info("Waking up the waiting coroutine with an event...") - sync.set() - yield Timer(10000) - - - result = yield Timer(1000000) - dut.log.warning("test complete!") - - -@cocotb.test() -def example_test2(dut): - """This is another example test""" - result = yield Timer(1000000) - dut.log.warning("test complete!") diff --git a/examples/plusargs/plusargs.py b/examples/plusargs/plusargs.py new file mode 100644 index 00000000..0076c820 --- /dev/null +++ b/examples/plusargs/plusargs.py @@ -0,0 +1,23 @@ +#!/bin/python + +""" + plusarg testing +""" + +import cocotb +from cocotb.decorators import coroutine +from cocotb.triggers import Timer, Edge, Event + +import sys + +@cocotb.test() +def plusargs_test(dut): + """Demonstrates plusarg access from Python test""" + + yield Timer(10000) + + for name in cocotb.plusargs: + print name, cocotb.plusargs[name] + + yield Timer(10000) + diff --git a/examples/plusargs/tb_top.v b/examples/plusargs/tb_top.v new file mode 100644 index 00000000..f1c40cc9 --- /dev/null +++ b/examples/plusargs/tb_top.v @@ -0,0 +1,12 @@ +module tb_top ; + + reg [1000:0] foo_string; + integer result; + +initial begin + $display("Plusargs test"); + result = $value$plusargs("foo=%s", foo_string); + $display("Plusarg foo has value %0s", foo_string); + +end +endmodule //: tb_top From 804a1f393cb22a12f0501da438d3c85f7ef86fa8 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Sat, 6 Jul 2013 16:54:08 +0000 Subject: [PATCH 0185/2656] first attempt with Questa/ ncverilog --- makefiles/Makefile.questa | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 makefiles/Makefile.questa diff --git a/makefiles/Makefile.questa b/makefiles/Makefile.questa new file mode 100644 index 00000000..bdef34c1 --- /dev/null +++ b/makefiles/Makefile.questa @@ -0,0 +1,26 @@ + +OBJ_DIR := obj +VPI_LIB := vpi +VPI_FILE := $(SIM_ROOT)/build/libs/x86_64/libgpi.so + +all: $(OBJ_DIR) sim + +$(OBJ_DIR): + mkdir -p $(OBJ_DIR) + + + +.PHONY: sim +sim: $(OBJ_DIR) $(VERILOG_SOURCES) + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(PYTHON_LIBDIR):$(PYTHON_DYNLIBDIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ + ncverilog +nc64bit +loadvpi=$(VPI_FILE):vlog_startup_routines_bootstrap +access+rw $(EXTRA_ARGS) $(VERILOG_SOURCES) + +dve: $(OBJ_DIR) $(VERILOG_SOURCES) + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ + ncverilog +loadvpi=$(VPI_FILE):vlog_startup_routines_bootstrap +access+rw $(EXTRA_ARGS) $(VERILOG_SOURCES) +clean: + -@rm -rf $(OBJ_DIR) + -@rm -rf INCA_libs + -@rm -rf ncverilog.log + + From 46e468c4c3aa241d7788171f6c8f39f924ea62db Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Sat, 6 Jul 2013 16:56:29 +0000 Subject: [PATCH 0186/2656] tweaks to add Questa support --- makefiles/Makefile.pylib | 4 +++- makefiles/Makefile.sim | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib index 05b8530b..9c2b5b73 100644 --- a/makefiles/Makefile.pylib +++ b/makefiles/Makefile.pylib @@ -33,11 +33,13 @@ PYTHON_PREFIX = $(shell python-config --prefix) PYTHON_VERSION?=$(shell python -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_version()') PYTHON_LIBDIR:=$(shell python -c 'import os.path,distutils.sysconfig;print os.path.sep+os.path.join(*list(distutils.sysconfig.get_python_lib(standard_lib=True).split(os.path.sep)[:-1]))') +PYTHON_DYNLIBDIR:=$(PYTHON_LIBDIR)/python$(PYTHON_VERSION)/lib-dynload/ + # Since we don't know which modules we might need or whether the simulator # we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports # is we simply link against all dynamic libraries in the Python installation ifeq ($(OS),Linux) -PYLIBS = $(wildcard $(PYTHON_LIBDIR)/python$(PYTHON_VERSION)lib-dynload/*.so) $(shell python-config --libs) +PYLIBS = $(wildcard $(PYTHON_DYNLIBDIR)/*.so) $(shell python-config --libs) else PYLIBS = $(shell python-config --libs) endif diff --git a/makefiles/Makefile.sim b/makefiles/Makefile.sim index 4c247e50..8f498169 100644 --- a/makefiles/Makefile.sim +++ b/makefiles/Makefile.sim @@ -39,7 +39,11 @@ else ifeq ($(SIM),VCS) include $(SIM_ROOT)/makefiles/Makefile.vcs else -$(error "Unknown simulator selected. Set SIM to either ICARUS, ALDEC or VCS") +ifeq ($(SIM),QUESTA) +include $(SIM_ROOT)/makefiles/Makefile.questa +else +$(error "Unknown simulator selected. Set SIM to either ICARUS, ALDEC, QUESTA or VCS") +endif endif endif endif From 37f3c4d0263c77e67403ad54a3128893eada0cd8 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Sat, 6 Jul 2013 17:06:01 +0000 Subject: [PATCH 0187/2656] adding to Makefile.sim --- makefiles/Makefile.sim | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/makefiles/Makefile.sim b/makefiles/Makefile.sim index 4c247e50..7ee81510 100644 --- a/makefiles/Makefile.sim +++ b/makefiles/Makefile.sim @@ -39,8 +39,11 @@ else ifeq ($(SIM),VCS) include $(SIM_ROOT)/makefiles/Makefile.vcs else -$(error "Unknown simulator selected. Set SIM to either ICARUS, ALDEC or VCS") +ifeq ($(SIM),IUS) +include $(SIM_ROOT)/makefiles/Makefile.ius +else +$(error "Unknown simulator selected. Set SIM to either ICARUS, ALDEC, IUS or VCS") +endif endif endif endif - From 5e79d823e9dc576bcd03605bc535b8bdc9ebfe5f Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Sat, 6 Jul 2013 17:07:07 +0000 Subject: [PATCH 0188/2656] switching to qverilog --- makefiles/Makefile.questa | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makefiles/Makefile.questa b/makefiles/Makefile.questa index bdef34c1..dbd6fa30 100644 --- a/makefiles/Makefile.questa +++ b/makefiles/Makefile.questa @@ -13,11 +13,11 @@ $(OBJ_DIR): .PHONY: sim sim: $(OBJ_DIR) $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(PYTHON_LIBDIR):$(PYTHON_DYNLIBDIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ - ncverilog +nc64bit +loadvpi=$(VPI_FILE):vlog_startup_routines_bootstrap +access+rw $(EXTRA_ARGS) $(VERILOG_SOURCES) + qverilog +nc64bit +loadvpi=$(VPI_FILE):vlog_startup_routines_bootstrap +access+rw $(EXTRA_ARGS) $(VERILOG_SOURCES) dve: $(OBJ_DIR) $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ - ncverilog +loadvpi=$(VPI_FILE):vlog_startup_routines_bootstrap +access+rw $(EXTRA_ARGS) $(VERILOG_SOURCES) + qverilog +loadvpi=$(VPI_FILE):vlog_startup_routines_bootstrap +access+rw $(EXTRA_ARGS) $(VERILOG_SOURCES) clean: -@rm -rf $(OBJ_DIR) -@rm -rf INCA_libs From fe1ba584da0173df640807da68e9cd1f8bf598cd Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Sat, 6 Jul 2013 17:18:01 +0000 Subject: [PATCH 0189/2656] Non-working IUS Makefile for irun --- makefiles/Makefile.ius | 52 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 makefiles/Makefile.ius diff --git a/makefiles/Makefile.ius b/makefiles/Makefile.ius new file mode 100644 index 00000000..9f5dfc05 --- /dev/null +++ b/makefiles/Makefile.ius @@ -0,0 +1,52 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +OBJ_DIR := obj +VPI_LIB := vpi + +all: $(OBJ_DIR) sim + +$(OBJ_DIR): + mkdir -p $(OBJ_DIR) + + +.PHONY: sim +sim: $(OBJ_DIR) $(VERILOG_SOURCES) + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ + irun -sv $(EXTRA_ARGS) -64bit -loadvpi /home/gmcgregor/src/cocotb/build/libs/x86_64/libpgi:vlog_startup_routines_bootstrap +access+rw $(VERILOG_SOURCES) + +dve: $(OBJ_DIR) $(VERILOG_SOURCES) + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ + irun -R +acc+1 +vpi+1+assertion -P pli.tab -sverilog $(EXTRA_ARGS) -gui -load libgpi.so $(VERILOG_SOURCES) +clean: + -@rm -rf $(OBJ_DIR) + -@rm -f pli.tab + -@rm -rf csrc + -@rm -rf simv.daidir + -@rm -f simv + -@rm -f ucli.key + From 1dc16a7a88b8ac53d024d4b7a9ac6e09fc915b04 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Sun, 7 Jul 2013 11:21:55 +0000 Subject: [PATCH 0190/2656] Basic Cadence/ IUS support - library problems --- makefiles/Makefile.inc | 4 ++-- makefiles/Makefile.ius | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index d28184f8..699fb4ab 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -57,11 +57,11 @@ endif ifeq ($(OS),Darwin) -LINKER_ARGS := -shared -undefined suppress -flat_namespace -L$(PYTHON_LIBDIR) +LINKER_ARGS := -shared -undefined suppress -flat_namespace -L$(PYTHON_LIBDIR) $(PYLIBS) #/Users/gmcgregor/src/IMP/python_install/lib/ else -LINKER_ARGS := -shared -Xlinker -export-dynamic +LINKER_ARGS := -shared -Xlinker -export-dynamic $(PYLIBS) endif # Disable some inbuild rules diff --git a/makefiles/Makefile.ius b/makefiles/Makefile.ius index 9f5dfc05..b15480c4 100644 --- a/makefiles/Makefile.ius +++ b/makefiles/Makefile.ius @@ -37,7 +37,7 @@ $(OBJ_DIR): .PHONY: sim sim: $(OBJ_DIR) $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ - irun -sv $(EXTRA_ARGS) -64bit -loadvpi /home/gmcgregor/src/cocotb/build/libs/x86_64/libpgi:vlog_startup_routines_bootstrap +access+rw $(VERILOG_SOURCES) + irun -sv $(EXTRA_ARGS) -64bit -loadvpi /home/gmcgregor/src/cocotb/build/libs/x86_64/libgpi:vlog_startup_routines_bootstrap +access+rwc $(VERILOG_SOURCES) dve: $(OBJ_DIR) $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ From 5935bb64437ab32db5393a115a4d06ea975ce205 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Sun, 7 Jul 2013 11:46:18 +0000 Subject: [PATCH 0191/2656] working IUS/ QUESTA makes --- makefiles/Makefile.ius | 7 +------ makefiles/Makefile.questa | 7 ++++--- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/makefiles/Makefile.ius b/makefiles/Makefile.ius index b15480c4..684e61fc 100644 --- a/makefiles/Makefile.ius +++ b/makefiles/Makefile.ius @@ -41,12 +41,7 @@ sim: $(OBJ_DIR) $(VERILOG_SOURCES) dve: $(OBJ_DIR) $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ - irun -R +acc+1 +vpi+1+assertion -P pli.tab -sverilog $(EXTRA_ARGS) -gui -load libgpi.so $(VERILOG_SOURCES) + irun -sv $(EXTRA_ARGS) -64bit -loadvpi /home/gmcgregor/src/cocotb/build/libs/x86_64/libgpi:vlog_startup_routines_bootstrap +access+rwc $(VER clean: -@rm -rf $(OBJ_DIR) - -@rm -f pli.tab - -@rm -rf csrc - -@rm -rf simv.daidir - -@rm -f simv - -@rm -f ucli.key diff --git a/makefiles/Makefile.questa b/makefiles/Makefile.questa index dbd6fa30..5eb766ba 100644 --- a/makefiles/Makefile.questa +++ b/makefiles/Makefile.questa @@ -13,14 +13,15 @@ $(OBJ_DIR): .PHONY: sim sim: $(OBJ_DIR) $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(PYTHON_LIBDIR):$(PYTHON_DYNLIBDIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ - qverilog +nc64bit +loadvpi=$(VPI_FILE):vlog_startup_routines_bootstrap +access+rw $(EXTRA_ARGS) $(VERILOG_SOURCES) + qverilog -64 -R -pli $(VPI_FILE) - +acc $(EXTRA_ARGS) $(VERILOG_SOURCES) dve: $(OBJ_DIR) $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ - qverilog +loadvpi=$(VPI_FILE):vlog_startup_routines_bootstrap +access+rw $(EXTRA_ARGS) $(VERILOG_SOURCES) + qverilog -64 -R -pli $(VPI_FILE) - +acc $(EXTRA_ARGS) $(VERILOG_SOURCES) + clean: -@rm -rf $(OBJ_DIR) -@rm -rf INCA_libs - -@rm -rf ncverilog.log + -@rm -rf qverilog.log From 2e7e45292fa2ed52cba1c6a6ed903d16f4144ee1 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Sun, 7 Jul 2013 08:14:52 -0500 Subject: [PATCH 0192/2656] fixed a couple of typos --- documentation/source/quickstart.rst | 2 +- documentation/source/roadmap.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index e5ec1516..d2de1c56 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -19,7 +19,7 @@ The same mechanism can be used to access signals inside the design. # Get a reference to the "clk" signal on the top-level clk = dut.clk - # Get a regerence to a register "count" in a sub-block "inst_sub_block" + # Get a reference to a register "count" in a sub-block "inst_sub_block" count = dut.inst_sub_block.count diff --git a/documentation/source/roadmap.rst b/documentation/source/roadmap.rst index da8e2b76..807ebcaf 100644 --- a/documentation/source/roadmap.rst +++ b/documentation/source/roadmap.rst @@ -4,7 +4,7 @@ Roadmap Cocotb is in active development. -We use GitHub issues to track our pending tasks. Take a look at the `open Enchancements `_ to see the work that's lined up. +We use GitHub issues to track our pending tasks. Take a look at the `open Enhancements `_ to see the work that's lined up. If you have a GitHub account you can also `raise an enhancement request `_ to suggest new features. From 110a143c4a287cdee200db314a0df8c3934a24dc Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Mon, 8 Jul 2013 11:02:52 +0100 Subject: [PATCH 0193/2656] Cleanup: Fix missing forward slash in Makefile.pylib --- makefiles/Makefile.pylib | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib index 05b8530b..7a4266ca 100644 --- a/makefiles/Makefile.pylib +++ b/makefiles/Makefile.pylib @@ -37,7 +37,7 @@ PYTHON_LIBDIR:=$(shell python -c 'import os.path,distutils.sysconfig;print os.pa # we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports # is we simply link against all dynamic libraries in the Python installation ifeq ($(OS),Linux) -PYLIBS = $(wildcard $(PYTHON_LIBDIR)/python$(PYTHON_VERSION)lib-dynload/*.so) $(shell python-config --libs) +PYLIBS = $(wildcard $(PYTHON_LIBDIR)/python$(PYTHON_VERSION)/lib-dynload/*.so) $(shell python-config --libs) else PYLIBS = $(shell python-config --libs) endif @@ -45,8 +45,3 @@ endif PYTHON_INCLUDEDIR := -I$(shell python -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_inc()') - - - - - From 03f4dcca1124f898d14b9d6ec73ad7879de4584a Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Mon, 8 Jul 2013 11:01:35 -0500 Subject: [PATCH 0194/2656] removing personal paths --- makefiles/Makefile.inc | 1 - makefiles/Makefile.ius | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 769b84fd..3f8f2dc6 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -59,7 +59,6 @@ endif ifeq ($(OS),Darwin) LINKER_ARGS := -shared -undefined suppress -flat_namespace -L$(PYTHON_LIBDIR) $(PYLIBS) -#/Users/gmcgregor/src/IMP/python_install/lib/ else LINKER_ARGS := -shared -Xlinker -export-dynamic $(PYLIBS) endif diff --git a/makefiles/Makefile.ius b/makefiles/Makefile.ius index 684e61fc..5ab98a74 100644 --- a/makefiles/Makefile.ius +++ b/makefiles/Makefile.ius @@ -37,11 +37,11 @@ $(OBJ_DIR): .PHONY: sim sim: $(OBJ_DIR) $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ - irun -sv $(EXTRA_ARGS) -64bit -loadvpi /home/gmcgregor/src/cocotb/build/libs/x86_64/libgpi:vlog_startup_routines_bootstrap +access+rwc $(VERILOG_SOURCES) + irun -sv $(EXTRA_ARGS) -64bit -loadvpi $(BUILD_DIR)/libgpi:vlog_startup_routines_bootstrap +access+rwc $(VERILOG_SOURCES) dve: $(OBJ_DIR) $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ - irun -sv $(EXTRA_ARGS) -64bit -loadvpi /home/gmcgregor/src/cocotb/build/libs/x86_64/libgpi:vlog_startup_routines_bootstrap +access+rwc $(VER + irun -sv $(EXTRA_ARGS) -64bit -loadvpi $(BUILD_DIR)/libgpi:vlog_startup_routines_bootstrap +access+rwc $(VER clean: -@rm -rf $(OBJ_DIR) From 8584d609c0b32760ad0bc8250fe9bd2743e362c0 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Mon, 8 Jul 2013 18:46:43 +0100 Subject: [PATCH 0195/2656] Issue #46: Test result definitions --- cocotb/result.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 cocotb/result.py diff --git a/cocotb/result.py b/cocotb/result.py new file mode 100644 index 00000000..dcf46526 --- /dev/null +++ b/cocotb/result.py @@ -0,0 +1,33 @@ +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +# Exceptions used to indicate test results +class TestComplete(StopIteration): pass + +class TestError(TestComplete): pass + +class TestFailure(TestComplete): pass + +class TestSuccess(TestComplete): pass From 4b7d3dd663497202460e2595900fc6cc643b6d16 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Mon, 8 Jul 2013 18:52:01 +0100 Subject: [PATCH 0196/2656] Fix major issue with coroutines that caused subtle bugs * Issues #47, #48, #36, #30, #26 all affected * Still needs tidying up, scheduler could be simplified further --- cocotb/decorators.py | 212 ++++++++++---------- cocotb/regression.py | 39 ++-- cocotb/scheduler.py | 110 ++++++---- cocotb/scoreboard.py | 7 +- examples/functionality/tests/test_cocotb.py | 6 +- 5 files changed, 204 insertions(+), 170 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 69dc3602..29cdeaf0 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -27,9 +27,14 @@ import time import logging +import traceback + import cocotb from cocotb.triggers import Join +from cocotb.result import TestComplete, TestError, TestFailure, TestSuccess + + def public(f): """Use a decorator to avoid retyping function/class names. @@ -61,80 +66,53 @@ def __call__(self): if self.callback is not None: self.callback() -class TestComplete(StopIteration): - """ - Indicate that a test has finished - """ - pass - +class RunningCoroutine(object): + """Per instance wrapper around an function to turn it into a coroutine -class TestCompleteFail(TestComplete): - pass -class TestCompleteOK(TestComplete): - pass + Provides the following: -class coroutine(object): - """Decorator class that allows us to provide common coroutine mechanisms: + coro.join() creates a Trigger that will fire when this coroutine completes - log methods will will log to cocotb.coroutines.name - - join() method returns an event which will fire when the coroutine exits + coro.kill() will destroy a coroutine instance (and cause any Join triggers to fire """ - - def __init__(self, func): - self._func = func - self.__name__ = func.__name__ # FIXME should use functools.wraps? - self._callbacks = [] - self.log = logging.getLogger("cocotb.coroutine.%s" % func.__name__) + def __init__(self, inst, parent): + self.__name__ = "%s.0x%x" % (inst.__name__, id(self)) + self.log = logging.getLogger("cocotb.coroutine.%s" % self.__name__) + self._coro = inst self._finished = False + self._callbacks = [] + self._parent = parent + self.__doc__ = parent._func.__doc__ + self.module = parent._func.__module__ + self.funcname = parent._func.__name__ - def __call__(self, *args, **kwargs): - self._coro = self._func(*args, **kwargs) - return self - - def __get__(self, obj, type=None): - """Permit the decorator to be used on class methods - and standalone functions""" - return self.__class__(self._func.__get__(obj, type)) - - def __iter__(self): return self - - def __str__(self): - return str(self.__name__) - - def next(self): - """FIXME: deprecated by send method?""" - try: - return self._coro.next() - except StopIteration: - raise CoroutineComplete(callback=self._finished_cb) - - def _pre_send(self): - """Provide some useful debug if our coroutine isn't a real coroutine - - NB better than catching AttributeError on _coro.send since then we - can't distinguish between an AttributeError in the coroutine itself - """ if not hasattr(self._coro, "send"): self.log.error("%s isn't a value coroutine! Did you use the yield keyword?" % self.__name__) raise CoroutineComplete(callback=self._finished_cb) + def __iter__(self): + return self + + def __str__(self): + return str(self.__name__) + def send(self, value): - """FIXME: problem here is that we don't let the call stack unwind...""" - self._pre_send() try: return self._coro.send(value) + except TestComplete as e: + self.log.info(str(e)) + raise except StopIteration: raise CoroutineComplete(callback=self._finished_cb) - except cocotb.TestFailed as e: - self.log.error(str(e)) - raise TestCompleteFail() def throw(self, exc): return self._coro.throw(exc) + def close(self): + return self._coro.close() + def kill(self): """Kill a coroutine""" self.log.debug("kill() called on coroutine") @@ -160,15 +138,8 @@ def __nonzero__(self): return not self._finished -@public -class test(coroutine): - """Decorator to mark a fucntion as a test - - All tests are coroutines. The test decorator provides - some common reporting etc, a test timeout and allows - us to mark tests as expected failures. - """ - +class RunningTest(RunningCoroutine): + """Add some useful Test functionality to a RunningCoroutine""" class ErrorLogHandler(logging.Handler): def __init__(self, fn): @@ -178,62 +149,87 @@ def __init__(self, fn): def handle(self, record): self.fn(self.format(record)) - def __init__(self, timeout=None, expect_fail=False): - self.timeout = timeout - self.expect_fail = expect_fail - self.started = False - self.error_messages = [] - def __call__(self, f): - """ - ping + def __init__(self, inst, parent): + RunningCoroutine.__init__(self, inst, parent) + self.error_messages = [] + self.started = False + self.start_time = 0 + self.expect_fail = parent.expect_fail - """ - super(test, self).__init__(f) - self.log = logging.getLogger("cocotb.test.%s" % self._func.__name__) - self.start_time = time.time() + self.handler = RunningTest.ErrorLogHandler(self._handle_error_message) + cocotb.log.addHandler(self.handler) - # Capture all log messages with ERROR or higher - def handle_error_msg(msg): - self.error_messages.append(msg) + def send(self, value): + if not self.started: + self.log.info("Starting test: \"%s\"\nDescription: %s" % (self.funcname, self.__doc__)) + self.start_time = time.time() + self.started = True - self.handler = test.ErrorLogHandler(handle_error_msg) - cocotb.log.addHandler(self.handler) + try: + self.log.debug("Sending trigger %s" % (str(value))) + return self._coro.send(value) + except TestFailure as e: + self.log.info(str(e)) + raise + except StopIteration: + raise TestSuccess() - def _wrapped_test(*args, **kwargs): - super(test, self).__call__(*args, **kwargs) - return self + def _handle_error_message(self, msg): + self.error_messages.append(msg) - _wrapped_test.im_test = True # For auto-regressions - return _wrapped_test +class coroutine(object): + """Decorator class that allows us to provide common coroutine mechanisms: - def _pre_send(self): - """Provide some useful debug if our coroutine isn't a real coroutine + log methods will will log to cocotb.coroutines.name - NB better than catching AttributeError on _coro.send since then we - can't distinguish between an AttributeError in the coroutine itself - """ - if not hasattr(self._coro, "send"): - self.log.error("%s isn't a value coroutine! Did you use the yield keyword?" - % self.__name__) - raise TestCompleteFail() + join() method returns an event which will fire when the coroutine exits + """ + def __init__(self, func): + self._func = func + self.log = logging.getLogger("cocotb.function.%s" % self._func.__name__) - def send(self, value): - self.error_messages = [] - """FIXME: problem here is that we don't let the call stack unwind...""" - self._pre_send() - if not self.started: - self.log.info("Starting test: \"%s\"\nDescription: %s" % (self.__name__, self._func.__doc__)) - self.started = True + def __call__(self, *args, **kwargs): try: - self.log.debug("Sending trigger %s" % (str(value))) - return self._coro.send(value) - except StopIteration: - raise TestCompleteOK() - except cocotb.TestFailed as e: - self.log.error(str(e)) - raise TestCompleteFail() + return RunningCoroutine(self._func(*args, **kwargs), self) + except Exception as e: + traceback.print_exc() + raise TestError(str(e)) + + def __get__(self, obj, type=None): + """Permit the decorator to be used on class methods + and standalone functions""" + return self.__class__(self._func.__get__(obj, type)) + + def __iter__(self): return self - raise StopIteration() + def __str__(self): + return str(self._func.__name__) + +@public +class test(coroutine): + """Decorator to mark a fucntion as a test + + All tests are coroutines. The test decorator provides + some common reporting etc, a test timeout and allows + us to mark tests as expected failures. + """ + def __init__(self, timeout=None, expect_fail=False): + self.timeout = timeout + self.expect_fail = expect_fail + + def __call__(self, f): + super(test, self).__init__(f) + + def _wrapped_test(*args, **kwargs): + try: + return RunningTest(self._func(*args, **kwargs), self) + except Exception as e: + traceback.print_exc() + raise TestError(str(e)) + + _wrapped_test.im_test = True # For auto-regressions + _wrapped_test.name = self._func.__name__ + return _wrapped_test diff --git a/cocotb/regression.py b/cocotb/regression.py index b050fecf..dc772048 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -38,6 +38,8 @@ import cocotb.ANSI as ANSI +from cocotb.result import TestError, TestFailure, TestSuccess + def _my_import(name): mod = __import__(name) components = name.split('.') @@ -67,6 +69,8 @@ def initialise(self): self.ntests = 0 + xml = "" + # Auto discovery for module_name in self._modules: module = _my_import(module_name) @@ -84,13 +88,19 @@ def initialise(self): for thing in vars(module).values(): if hasattr(thing, "im_test"): - self._queue.append(thing(self._dut)) + try: + self._queue.append(thing(self._dut)) + except TestError: + self.log.warning("Skipping test %s" % thing.name) + xml += xunit_output(thing.name, module_name, 0.0, skipped=True) + continue self.ntests += 1 self.log.info("Found test %s.%s" % - (self._queue[-1]._func.__module__, - self._queue[-1]._func.__name__)) + (self._queue[-1].module, + self._queue[-1].funcname)) self.start_xml(self.ntests) + self._fout.write(xml + '\n') def start_xml(self, ntests): """Write the XML header into results.txt""" @@ -119,15 +129,15 @@ def handle_result(self, result): Args: result (TestComplete exception) """ - if isinstance(result, cocotb.decorators.TestCompleteFail) and not \ + if isinstance(result, TestFailure) and not \ self._running_test.expect_fail: - self._fout.write(xunit_output(self._running_test._func.__name__, - self._running_test._func.__module__, + self._fout.write(xunit_output(self._running_test.funcname, + self._running_test.module, time.time() - self._running_test.start_time, failure="\n".join(self._running_test.error_messages))) else: - self._fout.write(xunit_output(self._running_test._func.__name__, - self._running_test._func.__module__, + self._fout.write(xunit_output(self._running_test.funcname, + self._running_test.module, time.time() - self._running_test.start_time)) def execute(self): @@ -137,8 +147,8 @@ def execute(self): def test_runner(self): self._running_test = cocotb.regression.next_test() count = 1 - while self._running_test: - try: + try: + while self._running_test: # Want this to stand out a little bit self.log.info("%sRunning test %d/%d:%s %s" % ( ANSI.BLUE_BG +ANSI.BLACK_FG, @@ -150,11 +160,10 @@ def test_runner(self): else: test = cocotb.scheduler.new_test(self._running_test) yield NullTrigger() - except StopIteration: - count+=1 - self._running_test = cocotb.regression.next_test() - - self.tear_down() + count+=1 + self._running_test = cocotb.regression.next_test() + finally: + self.tear_down() return diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 708e1720..f87953a4 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -37,8 +37,9 @@ import simulator import cocotb import cocotb.decorators -from cocotb.triggers import Trigger, Timer, ReadOnly, NextTimeStep, ReadWrite +from cocotb.triggers import Trigger, Timer, ReadOnly, NextTimeStep, ReadWrite, NullTrigger +from cocotb.result import TestComplete, TestError class Scheduler(object): @@ -134,8 +135,11 @@ def save_write(self, handle, args): def _add_trigger(self, trigger, coroutine): """Adds a new trigger which will cause the coroutine to continue when fired""" - self.waiting[trigger].append(coroutine) - trigger.prime(self.react) + try: + trigger.prime(self.react) + self.waiting[trigger].append(coroutine) + except Exception as e: + raise TestError("Unable to prime a trigger: %s" % str(e)) def queue(self, coroutine): """Queue a coroutine for execution""" @@ -143,6 +147,24 @@ def queue(self, coroutine): def add(self, coroutine): """Add a new coroutine. Required because we cant send to a just started generator (FIXME)""" + + if isinstance(coroutine, cocotb.decorators.coroutine): + self.log.critical("Attempt to schedule a coroutine that hasn't started") + coroutine.log.error("This is the failing coroutine") + self.log.warning("Did you forget to add paranthesis to the @test decorator?") + cocotb.regression.handle_result(TestError("Attempt to schedule a coroutine that hasn't started")) + self.cleanup() + return + + elif not isinstance(coroutine, cocotb.decorators.RunningCoroutine): + self.log.critical("Attempt to add something to the scheduler which isn't a coroutine") + self.log.warning("Got: %s (%s)" % (str(type(coroutine)), repr(coroutine))) + self.log.warning("Did you use the @coroutine decorator?") + cocotb.regression.handle_result(TestError("Attempt to schedule a coroutine that hasn't started")) + self.cleanup() + return + + self.log.debug("Queuing new coroutine %s" % coroutine.__name__) self.schedule(coroutine) return coroutine @@ -171,16 +193,12 @@ def prune_routines(self): while self._remove: delroutine, cb = self._remove.pop(0) for trigger, waiting in self.waiting.items(): + if isinstance(trigger, NullTrigger): continue for coro in waiting: if coro is delroutine: self.log.debug("Trowing into %s" % str(coro)) cb() - try: - result = coro.throw(StopIteration) - if result: - self._restart = result - self._restart_coro = coro - except StopIteration: pass + coro.close() self.waiting[trigger].remove(coro) # Clean up any triggers that no longer have pending coroutines for trigger, waiting in self.waiting.items(): @@ -201,16 +219,46 @@ def schedule(self, coroutine, trigger=None): coroutine.log.debug("Scheduling (%s)" % str(trigger)) try: - result = coroutine.send(trigger) - except cocotb.decorators.CoroutineComplete as exc: - self.log.debug("Coroutine completed execution with CoroutineComplete: %s" % str(coroutine)) - # Call any pending callbacks that were waiting for this coroutine to exit - exc() - return + try: + result = coroutine.send(trigger) + + # Normal co-routine completion + except cocotb.decorators.CoroutineComplete as exc: + self.log.debug("Coroutine completed execution with CoroutineComplete: %s" % str(coroutine)) + + # Call any pending callbacks that were waiting for this coroutine to exit + exc() + return + + # Entries may have been added to the remove list while the + # coroutine was running, clear these down and deschedule + # before resuming + if self._terminate is False: + self.prune_routines() + + if isinstance(result, Trigger): + self._add_trigger(result, coroutine) + elif isinstance(result, cocotb.decorators.RunningCoroutine): + self.log.debug("Scheduling nested co-routine: %s" % result.__name__) + + # Queue current routine to schedule when the nested routine exits + self._add_trigger(result.join(), coroutine) + if self._terminate is False: + self.add(result) + + elif isinstance(result, list): + if self._terminate is False: + for trigger in result: + trigger.addpeers(result) + self._add_trigger(trigger, coroutine) + else: + raise TestError(("Unable to schedule coroutine since it's returning stuff %s" % repr(result))) # TestComplete indication is game over, tidy up - except cocotb.decorators.TestComplete as test_result: + except TestComplete as test_result: + + self.log.error(str(test_result)) # Tag that close down is needed, save the test_result # for later use in cleanup handler @@ -223,32 +271,8 @@ def schedule(self, coroutine, trigger=None): self.cleanup() self.log.debug("Coroutine completed execution with TestComplete: %s" % str(coroutine)) - return + return - # Entries may have been added to the remove list while the - # coroutine was running, clear these down and deschedule - # before resuming - if self._terminate is False: - self.prune_routines() - - if isinstance(result, Trigger): - self._add_trigger(result, coroutine) - elif isinstance(result, cocotb.decorators.coroutine): - self.log.debug("Scheduling nested co-routine: %s" % result.__name__) - # Queue this routine to schedule when the nested routine exits - self._add_trigger(result.join(), coroutine) - if self._terminate is False: - self.schedule(result) - elif isinstance(result, list): - if self._terminate is False: - for trigger in result: - trigger.addpeers(result) - self._add_trigger(trigger, coroutine) - else: - self.log.warning("Unable to schedule coroutine since it's returning stuff %s" % repr(result)) - self.cleanup() - cocotb.regression.handle_result(cocotb.decorators.TestCompleteFail()) - coroutine.log.debug("Finished sheduling coroutine (%s)" % str(trigger)) def cleanup(self): @@ -270,6 +294,10 @@ def move_to_cleanup(self): self._restart = None self.log.debug("Out of delay cleanup") + # Carry on running the test + self.react(self.waiting.keys()[0]) + + @cocotb.decorators.coroutine def move_to_rw(self): try: diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index 5bbef2bb..cee34901 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -34,6 +34,7 @@ from cocotb.utils import hexdump, hexdiffs from cocotb.monitors import Monitor +from cocotb.result import TestFailure class Scoreboard(object): @@ -72,7 +73,7 @@ def check_received_transaction(transaction): if not expected_output: self.errors += 1 self.log.error("%s" % (transaction)) # TODO hexdump - raise cocotb.TestFailed("Recieved a transaction but wasn't expecting anything") + raise TestFailure("Recieved a transaction but wasn't expecting anything") if callable(expected_output): exp = expected_output() else: exp = expected_output.pop(0) @@ -81,7 +82,7 @@ def check_received_transaction(transaction): self.errors += 1 self.log.error("Received transaction is a different type to expected transaction") self.log.info("Got: %s but expected %s" % (str(type(transaction)), str(type(exp)))) - raise cocotb.TestFailed("Received transaction of wrong type") + raise TestFailure("Received transaction of wrong type") if transaction != exp: self.errors += 1 @@ -97,7 +98,7 @@ def check_received_transaction(transaction): for word in transaction: self.log.info(str(word)) except: pass self.log.warning(hexdiffs(exp, transaction)) - raise cocotb.TestFailed("Received transaction differed from expected transaction") + raise TestFailure("Received transaction differed from expected transaction") else: self.log.debug("Received expected transaction %d bytes" % (len(transaction))) self.log.debug(repr(transaction)) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 2609b391..14edb899 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -85,9 +85,9 @@ def test_yield_list(dut): test_flag = False @cocotb.coroutine -def clock_yield(clock_gen): +def clock_yield(generator): global test_flag - yield Join(clock_gen) + yield Join(generator) test_flag = True @cocotb.test(expect_fail=True) @@ -100,7 +100,7 @@ def test_coroutine_kill(dut): global test_flag clk_gen = cocotb.scheduler.add(clock_gen(dut.clk)) yield Timer(100) - clk_gen_two = cocotb.fork(clock_yield(clock_gen)) + clk_gen_two = cocotb.fork(clock_yield(clk_gen)) yield Timer(100) clk_gen.kill() if test_flag is not False: From 8111d900a54739e8dfa98a413264eaa23a3c7823 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Mon, 8 Jul 2013 22:27:23 +0100 Subject: [PATCH 0197/2656] Cleanup: Fix broken Avalon monitor --- cocotb/monitors/avalon.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index 78b2999c..f94e8ffa 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -31,6 +31,7 @@ """ from cocotb.decorators import coroutine from cocotb.monitors import BusMonitor +from cocotb.triggers import RisingEdge, ReadOnly class AvalonST(BusMonitor): """ @@ -52,7 +53,7 @@ def _monitor_recv(self): while True: yield clkedge yield rdonly - if self.bus.valid.value + if self.bus.valid.value: vec = self.bus.data.value self._recv(vec.buff) From 6b22630698de0000e49bb62e87a56168df44e7e5 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 8 Jul 2013 22:57:02 +0100 Subject: [PATCH 0198/2656] Issue #46: Serialize the starting of new tests better --- cocotb/decorators.py | 3 +- cocotb/regression.py | 40 +++++++++----------- cocotb/scheduler.py | 88 ++++++++++++++++++-------------------------- 3 files changed, 55 insertions(+), 76 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 29cdeaf0..c373ab72 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -151,8 +151,8 @@ def handle(self, record): def __init__(self, inst, parent): - RunningCoroutine.__init__(self, inst, parent) self.error_messages = [] + RunningCoroutine.__init__(self, inst, parent) self.started = False self.start_time = 0 self.expect_fail = parent.expect_fail @@ -162,6 +162,7 @@ def __init__(self, inst, parent): def send(self, value): if not self.started: + self.error_messages = [] self.log.info("Starting test: \"%s\"\nDescription: %s" % (self.funcname, self.__doc__)) self.start_time = time.time() self.started = True diff --git a/cocotb/regression.py b/cocotb/regression.py index dc772048..da8d438d 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -68,6 +68,7 @@ def __init__(self, dut, modules, test=None): def initialise(self): self.ntests = 0 + self.count = 1 xml = "" @@ -140,31 +141,24 @@ def handle_result(self, result): self._running_test.module, time.time() - self._running_test.start_time)) - def execute(self): - cocotb.scheduler.add(self.test_runner()) + self.execute() - @cocotb.decorators.coroutine - def test_runner(self): + def execute(self): self._running_test = cocotb.regression.next_test() - count = 1 - try: - while self._running_test: - # Want this to stand out a little bit - self.log.info("%sRunning test %d/%d:%s %s" % ( - ANSI.BLUE_BG +ANSI.BLACK_FG, - count, self.ntests, - ANSI.DEFAULT_FG + ANSI.DEFAULT_BG, - self._running_test)) - if count is 1: - test = cocotb.scheduler.add(self._running_test) - else: - test = cocotb.scheduler.new_test(self._running_test) - yield NullTrigger() - count+=1 - self._running_test = cocotb.regression.next_test() - finally: - self.tear_down() - return + if self._running_test: + # Want this to stand out a little bit + self.log.info("%sRunning test %d/%d:%s %s" % ( + ANSI.BLUE_BG +ANSI.BLACK_FG, + self.count, self.ntests, + ANSI.DEFAULT_FG + ANSI.DEFAULT_BG, + self._running_test)) + if self.count is 1: + test = cocotb.scheduler.add(self._running_test) + else: + test = cocotb.scheduler.new_test(self._running_test) + self.count+=1 + else: + self.tear_down() def xunit_output(name, classname, time, skipped=False, failure="", error=""): diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index f87953a4..79a86bd8 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -54,8 +54,6 @@ def __init__(self): self._terminate = False self._test_result = None self._readonly = None - self._restart = None - self._restart_coro = None # Keep this last self._readwrite = self.add(self.move_to_rw()) @@ -101,25 +99,13 @@ def react(self, trigger): if len(self.writes) and self._readwrite is None and self._terminate is False: self._readwrite = self.add(self.move_to_rw()) - # A request for termination may have happened, - # This is where we really handle to informing of the - # regression handler that everything from the current - # test is done and that it can go on with more - if self._terminate is True: - if self._readonly is None: - self._terminate = False - if self._startpoint is not None: - self.add(self._startpoint) - self._startpoint = None - self._test_result = None # If the python has caused any subsequent events to fire we might # need to schedule more coroutines before we drop back into the # simulator - if self._terminate is False: - while self._pending_adds: - coroutine = self._pending_adds.pop(0) - self.add(coroutine) + while self._pending_adds: + coroutine = self._pending_adds.pop(0) + self.add(coroutine) return @@ -152,7 +138,7 @@ def add(self, coroutine): self.log.critical("Attempt to schedule a coroutine that hasn't started") coroutine.log.error("This is the failing coroutine") self.log.warning("Did you forget to add paranthesis to the @test decorator?") - cocotb.regression.handle_result(TestError("Attempt to schedule a coroutine that hasn't started")) + self._result = TestError("Attempt to schedule a coroutine that hasn't started") self.cleanup() return @@ -160,7 +146,7 @@ def add(self, coroutine): self.log.critical("Attempt to add something to the scheduler which isn't a coroutine") self.log.warning("Got: %s (%s)" % (str(type(coroutine)), repr(coroutine))) self.log.warning("Did you use the @coroutine decorator?") - cocotb.regression.handle_result(TestError("Attempt to schedule a coroutine that hasn't started")) + self._resulti = TestError("Attempt to schedule a coroutine that hasn't started") self.cleanup() return @@ -188,18 +174,16 @@ def prune_routines(self): Process the remove list that can have accumulatad during the execution of a parent routine """ - restart = None - restart_coro = None while self._remove: delroutine, cb = self._remove.pop(0) for trigger, waiting in self.waiting.items(): if isinstance(trigger, NullTrigger): continue for coro in waiting: if coro is delroutine: - self.log.debug("Trowing into %s" % str(coro)) + self.log.debug("Closing %s" % str(coro)) cb() - coro.close() self.waiting[trigger].remove(coro) + coro.close() # Clean up any triggers that no longer have pending coroutines for trigger, waiting in self.waiting.items(): if not waiting: @@ -240,35 +224,31 @@ def schedule(self, coroutine, trigger=None): if isinstance(result, Trigger): self._add_trigger(result, coroutine) elif isinstance(result, cocotb.decorators.RunningCoroutine): - self.log.debug("Scheduling nested co-routine: %s" % result.__name__) - - # Queue current routine to schedule when the nested routine exits - self._add_trigger(result.join(), coroutine) if self._terminate is False: - self.add(result) + self.log.debug("Scheduling nested co-routine: %s" % result.__name__) + + # Queue current routine to schedule when the nested routine exits + self.queue(result) + self._add_trigger(result.join(), coroutine) elif isinstance(result, list): - if self._terminate is False: - for trigger in result: - trigger.addpeers(result) - self._add_trigger(trigger, coroutine) + for trigger in result: + trigger.addpeers(result) + self._add_trigger(trigger, coroutine) else: raise TestError(("Unable to schedule coroutine since it's returning stuff %s" % repr(result))) # TestComplete indication is game over, tidy up except TestComplete as test_result: - self.log.error(str(test_result)) + # self.log.error(str(test_result)) # Tag that close down is needed, save the test_result # for later use in cleanup handler - if self._terminate is False: - self._terminate = True - self._test_result = test_result - self.log.debug("Issue test result to regresssion object") - cocotb.regression.handle_result(test_result) - self._readonly = self.add(self.move_to_cleanup()) - self.cleanup() + self._terminate = True + self._test_result = test_result + self.cleanup() + self._readonly = self.add(self.move_to_cleanup()) self.log.debug("Coroutine completed execution with TestComplete: %s" % str(coroutine)) return @@ -289,20 +269,24 @@ def move_to_cleanup(self): yield Timer(1) self.prune_routines() self._readonly = None - if self._restart is not None: - self._add_trigger(self._restart, self._restart_coro) - self._restart = None - self.log.debug("Out of delay cleanup") - # Carry on running the test - self.react(self.waiting.keys()[0]) + # Tell the handler what the result was + self.log.debug("Issue test result to regresssion object") + cocotb.regression.handle_result(self._test_result) + self._test_result = None + + # If another test was added to queue kick it off + self.log.debug("Cleanup done") + if self._startpoint is not None: + newstart = self._startpoint + self._startpoint = None + self.queue(newstart) + + self._terminate = False @cocotb.decorators.coroutine def move_to_rw(self): - try: - yield ReadWrite() - self._readwrite = None - self.playout_writes() - except StopIteration: - self.log.warn("Exception caught in read_rw_handler") + yield ReadWrite() + self._readwrite = None + self.playout_writes() From df1c4eb198e026b3a6a8207ba595983e33828c61 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 8 Jul 2013 23:22:58 +0100 Subject: [PATCH 0199/2656] Issue #46: Fix typo and ensure restart order is correct --- cocotb/scheduler.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 79a86bd8..0745963f 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -146,7 +146,7 @@ def add(self, coroutine): self.log.critical("Attempt to add something to the scheduler which isn't a coroutine") self.log.warning("Got: %s (%s)" % (str(type(coroutine)), repr(coroutine))) self.log.warning("Did you use the @coroutine decorator?") - self._resulti = TestError("Attempt to schedule a coroutine that hasn't started") + self._result = TestError("Attempt to schedule a coroutine that hasn't started") self.cleanup() return @@ -276,13 +276,13 @@ def move_to_cleanup(self): self._test_result = None # If another test was added to queue kick it off - self.log.debug("Cleanup done") + self._terminate = False if self._startpoint is not None: newstart = self._startpoint self._startpoint = None - self.queue(newstart) + self.add(newstart) - self._terminate = False + self.log.debug("Cleanup done") @cocotb.decorators.coroutine From b5edff4f3341cd3286ace837319819156b2fcb94 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 9 Jul 2013 11:34:33 +0100 Subject: [PATCH 0200/2656] Cleanup: Fix broken Avalon driver --- cocotb/drivers/avalon.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 1a1d03ee..9e274f7b 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -33,6 +33,7 @@ from cocotb.triggers import RisingEdge, ReadOnly from cocotb.drivers import BusDriver, ValidatedBusDriver from cocotb.utils import hexdump +from cocotb.binary import BinaryValue class AvalonMM(BusDriver): """Avalon-MM Driver @@ -97,7 +98,16 @@ def _send_string(self, string): # FIXME busses that aren't integer numbers of bytes bus_width = len(self.bus.data) / 8 - word = BinaryValue(nbits=len(self.bus.data)) + word = BinaryValue(bits=len(self.bus.data)) + + + # Drive some defaults since we don't know what state we're in + self.bus.empty <= 0 + self.bus.startofpacket <= 0 + self.bus.endofpacket <= 0 + self.bus.error <= 0 + self.bus.valid <= 0 + while string: yield clkedge @@ -116,17 +126,26 @@ def _send_string(self, string): self.bus.valid <= 1 if firstword: + self.bus.empty <= 0 self.bus.startofpacket <= 1 firstword = False + else: + self.bus.startofpacket <= 0 + + nbytes = min(len(string), bus_width) + data = string[:nbytes] + word.buff = data[::-1] # Big Endian FIXME + if len(string) <= bus_width: self.bus.endofpacket <= 1 self.bus.empty <= bus_width - len(string) string = "" else: - word.buff = string[:bus_width] string = string[bus_width:] + self.bus.data <= word + # If this is a bus with a ready signal, wait for this word to # be acknowledged if hasattr(self.bus, "ready"): @@ -134,7 +153,7 @@ def _send_string(self, string): yield clkedge self.bus.valid <= 0 - + self.bus.endofpacket <= 0 @coroutine From e2cf7accd8b2a5ec4ca9c71431bd3adb7c87c524 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 9 Jul 2013 18:12:30 +0100 Subject: [PATCH 0201/2656] Issue #27: Tar based installation, sure this will expand in time --- Makefile | 36 ++++++++++++++++++++++++++++++------ makefiles/Makefile.inc | 4 +--- version | 1 + 3 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 version diff --git a/Makefile b/Makefile index f0a8f968..3c800ffc 100644 --- a/Makefile +++ b/Makefile @@ -26,10 +26,12 @@ ############################################################################### include makefiles/Makefile.inc +include version export BUILD_DIR=$(shell pwd)/build -INSTALL_DIR:=/tmp/test-install +INSTALL_DIR?=/usr/local +FULL_INSTALL_DIR=$(INSTALL_DIR)/cocotb-$(VERSION) LIBS:= lib/simulator lib/embed lib/vpi_shim lib/gpi @@ -56,11 +58,33 @@ test: $(LIBS) $(MAKE) -C examples pycode: - easy_install --install-dir=$(INSTALL_DIR) $(SIM_ROOT) + cp -R $(SIM_ROOT)/cocotb $(FULL_INSTALL_DIR)/ -lib_install: libs - -mkdir -p $(INSTALL_DIR)/lib - cp -R $(LIB_DIR)/* $(INSTALL_DIR)/lib +lib_install: $(LIBS) + @mkdir -p $(FULL_INSTALL_DIR)/lib + @mkdir -p $(FULL_INSTALL_DIR)/bin + @cp -R $(LIB_DIR)/* $(FULL_INSTALL_DIR)/lib + @cp -R bin/cocotbenv.py $(FULL_INSTALL_DIR)/bin/ + @cp -R makefiles $(FULL_INSTALL_DIR)/ + @rm -rf $(FULL_INSTALL_DIR)/makefiles/Makefile.inc -install: all lib_install pycode +$(FULL_INSTALL_DIR)/makefiles/Makefile.inc: + @echo "export SIM_ROOT:=$(FULL_INSTALL_DIR)" > $@ + @echo "export LIB_DIR=$(FULL_INSTALL_DIR)/lib" >> $@ +$(FULL_INSTALL_DIR)/bin/cocotb_uninstall: + @echo "#!/bin/bash" > bin/cocotb_uninstall + @echo "rm -rf $(FULL_INSTALL_DIR)" >> bin/cocotb_uninstall + install -m 544 bin/cocotb_uninstall $@ + rm -rf bin/cocotb_uninstall + +scripts: $(FULL_INSTALL_DIR)/bin/cocotb_uninstall $(FULL_INSTALL_DIR)/makefiles/Makefile.inc + +install: all lib_install pycode scripts + @echo -e "\nInstalled to $(FULL_INSTALL_DIR)" + @echo -e "To uninstall run $(FULL_INSTALL_DIR)/bin/cocotb_uninstall\n" + +help: + @echo -e "\nCoCoTB make help\n\nall\t- Build libaries" + @echo -e "install\t- Build and install libaries to FULL_INSTALL_DIR (default=$(FULL_INSTALL_DIR))" + @echo -e "clean\t- Clean the build dir\n\n" diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index d28184f8..f9754fdd 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -32,7 +32,7 @@ export SIM_ROOT:=$(abspath $(dir $(lastword $(MAKEFILE_LIST)))/..) endif BUILD_DIR=$(SIM_ROOT)/build -ARCH=$(shell uname -m) +ARCH?=$(shell uname -m) OS=$(shell uname) export OS @@ -58,8 +58,6 @@ endif ifeq ($(OS),Darwin) LINKER_ARGS := -shared -undefined suppress -flat_namespace -L$(PYTHON_LIBDIR) - -#/Users/gmcgregor/src/IMP/python_install/lib/ else LINKER_ARGS := -shared -Xlinker -export-dynamic endif diff --git a/version b/version new file mode 100644 index 00000000..2e94ead3 --- /dev/null +++ b/version @@ -0,0 +1 @@ +VERSION=0.1 From 7c2718df5a303868e0c1f4373d11579c923a9060 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 10 Jul 2013 11:38:55 +0100 Subject: [PATCH 0202/2656] Issue #53: Expand execution of single test to have a comma seperated list. To aid testing --- cocotb/__init__.py | 2 +- cocotb/regression.py | 29 ++++++++++++++++------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 6c4a7646..ac6c7103 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -96,7 +96,7 @@ def _initialise_testbench(root_handle): global regression - regression = RegressionManager(dut, modules, test=test_str) + regression = RegressionManager(dut, modules, tests=test_str) regression.initialise() regression.execute() diff --git a/cocotb/regression.py b/cocotb/regression.py index da8d438d..aa9f1b84 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -51,7 +51,7 @@ def _my_import(name): class RegressionManager(object): """Encapsulates all regression capability into a single place""" - def __init__(self, dut, modules, test=None): + def __init__(self, dut, modules, tests=None): """ Args: modules (list): A list of python module names to run @@ -61,7 +61,7 @@ def __init__(self, dut, modules, test=None): self._queue = [] self._dut = dut self._modules = modules - self._function = test + self._functions = tests self._running_test = None self.log = logging.getLogger("cocotb.regression") @@ -76,15 +76,16 @@ def initialise(self): for module_name in self._modules: module = _my_import(module_name) - if self._function: + if self._functions: - # Single function specified, don't auto discover - if not hasattr(module, self._function): - raise AttributeError("Test %s doesn't exist in %s" % - (self._function, module_name)) + # Specific functions specified, don't auto discover + for test in self._functions.rsplit(','): + if not hasattr(module, test): + raise AttributeError("Test %s doesn't exist in %s" % + (test, module_name)) - self._queue.append(getattr(module, self._function)(self._dut)) - self.ntests = 1 + self._queue.append(getattr(module, test)(self._dut)) + self.ntests += 1 break for thing in vars(module).values(): @@ -96,9 +97,11 @@ def initialise(self): xml += xunit_output(thing.name, module_name, 0.0, skipped=True) continue self.ntests += 1 - self.log.info("Found test %s.%s" % - (self._queue[-1].module, - self._queue[-1].funcname)) + + for valid_tests in self._queue: + self.log.info("Found test %s.%s" % + (valid_tests.module, + valid_tests.funcname)) self.start_xml(self.ntests) self._fout.write(xml + '\n') @@ -158,7 +161,7 @@ def execute(self): test = cocotb.scheduler.new_test(self._running_test) self.count+=1 else: - self.tear_down() + self.tear_down() def xunit_output(name, classname, time, skipped=False, failure="", error=""): From 928e051dcf1d4475cdbdf9e34718e09c35fe2049 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Fri, 5 Jul 2013 09:14:46 -0500 Subject: [PATCH 0203/2656] XUnit regression logging --- cocotb/regression.py | 91 +++++++----------------------------- cocotb/xunit_reporter.py | 99 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 74 deletions(-) create mode 100644 cocotb/xunit_reporter.py diff --git a/cocotb/regression.py b/cocotb/regression.py index aa9f1b84..415c2d71 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -37,6 +37,7 @@ from cocotb.triggers import NullTrigger import cocotb.ANSI as ANSI +from xunit_reporter import XUnitReporter from cocotb.result import TestError, TestFailure, TestSuccess @@ -69,8 +70,8 @@ def initialise(self): self.ntests = 0 self.count = 1 - - xml = "" + self.xunit = XUnitReporter() + self.xunit.add_testsuite(name="all", tests=repr(self.ntests), package="all") # Auto discovery for module_name in self._modules: @@ -94,29 +95,21 @@ def initialise(self): self._queue.append(thing(self._dut)) except TestError: self.log.warning("Skipping test %s" % thing.name) - xml += xunit_output(thing.name, module_name, 0.0, skipped=True) + self.xunit.add_testcase(name=thing.name, classname=module_name, time="0.0", skipped=True) continue self.ntests += 1 for valid_tests in self._queue: self.log.info("Found test %s.%s" % - (valid_tests.module, - valid_tests.funcname)) + (valid_tests.module, + valid_tests.funcname)) - self.start_xml(self.ntests) - self._fout.write(xml + '\n') - def start_xml(self, ntests): - """Write the XML header into results.txt""" - self._fout = open("results.xml", 'w') - self._fout.write("""\n""") - self._fout.write("""\n""" % ntests) def tear_down(self): """It's the end of the world as we know it""" - self._fout.write("") - self._fout.close() self.log.info("Shutting down...") + self.xunit.write() simulator.stop_simulator() def next_test(self): @@ -127,33 +120,26 @@ def next_test(self): def handle_result(self, result): """Handle a test result - Dumps result to XML and schedules the next test (if any) - Args: result (TestComplete exception) """ - if isinstance(result, TestFailure) and not \ - self._running_test.expect_fail: - self._fout.write(xunit_output(self._running_test.funcname, - self._running_test.module, - time.time() - self._running_test.start_time, - failure="\n".join(self._running_test.error_messages))) - else: - self._fout.write(xunit_output(self._running_test.funcname, - self._running_test.module, - time.time() - self._running_test.start_time)) + self.xunit.add_testcase(name =self._running_test.funcname, + classname=self._running_test.module, + time=repr(time.time() - self._running_test.start_time) ) + if isinstance(result, TestFailure): + self.xunit.add_failure("\n".join(self._running_test.error_messages)) self.execute() def execute(self): self._running_test = cocotb.regression.next_test() if self._running_test: - # Want this to stand out a little bit + # Want this to stand out a little bit self.log.info("%sRunning test %d/%d:%s %s" % ( - ANSI.BLUE_BG +ANSI.BLACK_FG, - self.count, self.ntests, - ANSI.DEFAULT_FG + ANSI.DEFAULT_BG, + ANSI.BLUE_BG +ANSI.BLACK_FG, + self.count, self.ntests, + ANSI.DEFAULT_FG + ANSI.DEFAULT_BG, self._running_test)) if self.count is 1: test = cocotb.scheduler.add(self._running_test) @@ -161,47 +147,4 @@ def execute(self): test = cocotb.scheduler.new_test(self._running_test) self.count+=1 else: - self.tear_down() - - -def xunit_output(name, classname, time, skipped=False, failure="", error=""): - """ - Format at xunit test output in XML - - Args: - name (str): the name of the test - - classname (str): the name of the class - - time (float): duration of the test in seconds - - Kwargs: - skipped (bool): this test was skipped - - failure (str): failure message to report - - error (str): error message to report - - Returns an XML string - - """ - xml = """ \n" - else: - xml += ">\n" - - if skipped: - xml += " \n" - - if failure: - xml += " %s\n \n" % \ - failure - - if error: - xml += " %s\n \n" % \ - error - - return xml + " \n" + self.tear_down() diff --git a/cocotb/xunit_reporter.py b/cocotb/xunit_reporter.py new file mode 100644 index 00000000..36c56d18 --- /dev/null +++ b/cocotb/xunit_reporter.py @@ -0,0 +1,99 @@ +from xml.etree.ElementTree import Element, SubElement +import xml.etree.ElementTree as ET + +TRUNCATE_LINES = 100 + +# file from http://stackoverflow.com/questions/136168/get-last-n-lines-of-a-file-with-python-similar-to-tail +class File(file): + + def countlines(self): + buf = mmap.mmap(self.fileno(), 0) + lines = 0 + while buf.readline(): + lines += 1 + return lines + + def head(self, lines_2find=1): + self.seek(0) #Rewind file + return [self.next() for x in xrange(lines_2find)] + + def tail(self, lines_2find=1): + self.seek(0, 2) #go to end of file + bytes_in_file = self.tell() + lines_found, total_bytes_scanned = 0, 0 + while (lines_2find+1 > lines_found and + bytes_in_file > total_bytes_scanned): + byte_block = min(1024, bytes_in_file-total_bytes_scanned) + self.seek(-(byte_block+total_bytes_scanned), 2) + total_bytes_scanned += byte_block + lines_found += self.read(1024).count('\n') + self.seek(-total_bytes_scanned, 2) + line_list = list(self.readlines()) + return line_list[-lines_2find:] + + +class XUnitReporter: + + def __init__(self, filename="results.xml"): + self.results = Element("testsuites", name="results") + self.filename = filename + + def add_testsuite(self, **kwargs): + self.last_testsuite = SubElement(self.results, "testsuite", **kwargs) + return self.last_testsuite + + def add_testcase(self, testsuite=None, **kwargs): + if testsuite == None: + testsuite = self.last_testsuite + self.last_testcase = SubElement(testsuite, "testcase", **kwargs) + return self.last_testcase + + def update_testsuite(self, testsuite=None, **kwargs): + if testsuite == None: + testsuite = self.last_testsuite + for k in kwargs: + testsuite.set(k, str(kwargs[k])) + + def update_testsuites(self, **kwargs): + for k in kwargs: + self.results.set(k, str(kwargs[k])) + + def add_log(self, logfile, testcase=None): + if testcase == None: + testcase = self.last_testcase + log = SubElement(testcase, "system-out") + f = File(logfile, 'r+') + lines = f.countlines() + if lines > (TRUNCATE_LINES * 2): + head = f.head(TRUNCATE_LINES) + tail = f.tail(TRUNCATE_LINES) + log.text = "".join(head + list("[...truncated %d lines...]\n" % ( (lines - (TRUNCATE_LINES*2)) )) + tail) + else: + log.text = "".join(f.readlines()) + + def add_failure(self, testcase=None, **kwargs): + if testcase == None: + testcase = self.last_testcase + log = SubElement(testcase, "failure", **kwargs) + + + def indent(self, elem, level=0): + i = "\n" + level*" " + if len(elem): + if not elem.text or not elem.text.strip(): + elem.text = i + " " + if not elem.tail or not elem.tail.strip(): + elem.tail = i + for elem in elem: + self.indent(elem, level+1) + if not elem.tail or not elem.tail.strip(): + elem.tail = i + else: + if level and (not elem.tail or not elem.tail.strip()): + elem.tail = i + + def write(self): + print 'Capturing results to ', self.filename + self.indent(self.results) + ET.ElementTree(self.results).write(self.filename, xml_declaration = True, method = "xml", encoding="UTF-8") + From 9c1e8b19d57bc26f456bc54d24c2ec8f16e7c4b1 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Fri, 5 Jul 2013 11:44:57 -0500 Subject: [PATCH 0204/2656] Cleaned up XUnitReporter inherits from object --- cocotb/xunit_reporter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/xunit_reporter.py b/cocotb/xunit_reporter.py index 36c56d18..bc551c39 100644 --- a/cocotb/xunit_reporter.py +++ b/cocotb/xunit_reporter.py @@ -32,7 +32,7 @@ def tail(self, lines_2find=1): return line_list[-lines_2find:] -class XUnitReporter: +class XUnitReporter(object): def __init__(self, filename="results.xml"): self.results = Element("testsuites", name="results") From a5c9d746644353d4bc88e2ab334de0efa2bf813e Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Wed, 10 Jul 2013 18:20:17 -0500 Subject: [PATCH 0205/2656] vpi_chk_error calls --- examples/demo/demo.py | 7 +++- include/vpi_user.h | 18 +++++++++ lib/vpi_shim/gpi_vpi.c | 88 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 101 insertions(+), 12 deletions(-) diff --git a/examples/demo/demo.py b/examples/demo/demo.py index 7d82c261..8c985d69 100644 --- a/examples/demo/demo.py +++ b/examples/demo/demo.py @@ -29,7 +29,8 @@ Example usage of the testbench framework """ -import cocotb +from cocotb.handle import * +import cocotb, simulator from cocotb.decorators import coroutine from cocotb.triggers import Timer, Edge, Event @@ -107,5 +108,7 @@ def example_test(dut): @cocotb.test() def example_test2(dut): """This is another example test""" - result = yield Timer(1000000) + s = simulator.get_handle_by_name(dut._handle, "foo") + simulator.get_name_string(s) + result = yield Timer(1000000) dut.log.warning("test complete!") diff --git a/include/vpi_user.h b/include/vpi_user.h index 65475393..7f456676 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -166,6 +166,23 @@ typedef struct t_cb_data #define cbInteractiveScopeChange 23 #define cbUnresolvedSystf 24 +/* error severity levels */ +#define vpiNotice 1 +#define vpiWarning 2 +#define vpiError 3 +#define vpiSystem 4 +#define vpiInternal 5 + +typedef struct t_vpi_error_info +{ + int32_t state; + int32_t level; + char *message; + char *product; + char *code; + char *file; + int32_t line; +} s_vpi_error_info, *p_vpi_error_info; extern vpiHandle vpi_register_cb(p_cb_data cb_data_p); @@ -207,6 +224,7 @@ extern vpiHandle vpi_handle_by_multi_index(vpiHandle obj, int32_t *index_array); +extern int32_t vpi_chk_error(p_vpi_error_info); void (*vlog_startup_routines[])(); diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 4d10af8d..9659561c 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -39,6 +39,8 @@ ((_type *)((uintptr_t)(_address) - \ (uintptr_t)(&((_type *)0)->_member))) +#define VPI_CHECKING 1 + static gpi_sim_hdl sim_init_cb; static gpi_sim_hdl sim_finish_cb; @@ -69,6 +71,52 @@ typedef struct gpi_clock_s { typedef gpi_clock_t *gpi_clock_hdl; + +// Should be run after every VPI call to check error status +static int check_vpi_error(void) +{ + int level=0; +#if VPI_CHECKING + s_vpi_error_info info; + int loglevel; + char msg_buf [100]; + level = vpi_chk_error(&info); + + if (level == 0) { + return; + } + + switch (level) { + case vpiNotice: loglevel = GPIInfo; + break; + case vpiWarning: loglevel = GPIWarning; + break; + case vpiError: loglevel = GPIError; + break; + case vpiSystem: + case vpiInternal: loglevel = GPICritical; + break; + } + + snprintf(msg_buf, 100, "VPI Error level %d\n", level ); + gpi_log("cocotb.gpi", loglevel, __FILE__, __func__, __LINE__, msg_buf); + + snprintf(msg_buf, 100, "MESG %s\n", info.message ); + gpi_log("cocotb.gpi", loglevel, __FILE__, __func__, __LINE__, msg_buf); + + snprintf(msg_buf, 100, "PROD %s\n", info.product ); + gpi_log("cocotb.gpi", loglevel, __FILE__, __func__, __LINE__, msg_buf); + + snprintf(msg_buf, 100, "CODE %s\n", info.code ); + gpi_log("cocotb.gpi", loglevel, __FILE__, __func__, __LINE__, msg_buf); + + snprintf(msg_buf, 100, "FILE %s\n", info.file ); + gpi_log("cocotb.gpi", loglevel, __FILE__, __func__, __LINE__, msg_buf); + +#endif + return level; +} + void gpi_free_handle(gpi_sim_hdl gpi_hdl) { free(gpi_hdl); @@ -95,12 +143,16 @@ gpi_sim_hdl gpi_get_root_handle() // vpi_iterate with a ref of NULL returns the top level module iterator = vpi_iterate(vpiModule, NULL); + check_vpi_error(); + root = vpi_scan(iterator); + check_vpi_error(); // Need to free the iterator if it didn't return NULL if (root != NULL && !vpi_free_object(iterator)) { LOG_WARN("VPI: Attempting to free root iterator failed!"); } + check_vpi_error(); rv = gpi_alloc_handle(); rv->sim_hdl = root; @@ -127,6 +179,7 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) strncpy(buff, name, len); obj = vpi_handle_by_name(buff, (vpiHandle)(parent->sim_hdl)); + check_vpi_error(); free(buff); rv = gpi_alloc_handle(); @@ -145,7 +198,7 @@ gpi_iterator_hdl gpi_iterate(gpi_sim_hdl base) { vpiHandle iterator; iterator = vpi_iterate(vpiNet, (vpiHandle)(base->sim_hdl)); - + check_vpi_error(); FEXIT return (gpi_iterator_hdl)iterator; } @@ -158,6 +211,7 @@ gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) gpi_sim_hdl rv = gpi_alloc_handle(); rv->sim_hdl = vpi_scan((vpiHandle) iterator); + check_vpi_error(); if (!rv->sim_hdl) { gpi_free_handle(rv); rv = NULL; @@ -179,6 +233,7 @@ void gpi_get_sim_time(uint32_t *high, uint32_t *low) s_vpi_time vpi_time_s; vpi_time_s.type = vpiSimTime;//vpiScaledRealTime; //vpiSimTime; vpi_get_time(NULL, &vpi_time_s); + check_vpi_error(); *high = vpi_time_s.high; *low = vpi_time_s.low; } @@ -198,7 +253,7 @@ void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) * in behavioral code. */ vpi_put_value((vpiHandle)(gpi_hdl->sim_hdl), value_p, NULL, vpiNoDelay); - + check_vpi_error(); FEXIT } @@ -229,7 +284,7 @@ void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) * in behavioral code. */ vpi_put_value((vpiHandle)(gpi_hdl->sim_hdl), value_p, NULL, vpiNoDelay); - + check_vpi_error(); free(buff); FEXIT } @@ -266,6 +321,7 @@ char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) p_vpi_value value_p = &value_s; vpi_get_value((vpiHandle)(gpi_hdl->sim_hdl), value_p); + check_vpi_error(); char *result = gpi_copy_name(value_p->value.str); FEXIT @@ -274,8 +330,9 @@ char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl) { - FENTER + FENTER const char *name = vpi_get_str(vpiFullName, (vpiHandle)(gpi_hdl->sim_hdl)); + check_vpi_error(); char *result = gpi_copy_name(name); FEXIT return result; @@ -285,6 +342,7 @@ char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl) { FENTER const char *name = vpi_get_str(vpiType, (vpiHandle)(gpi_hdl->sim_hdl)); + check_vpi_error(); char *result = gpi_copy_name(name); FEXIT return result; @@ -304,7 +362,7 @@ static p_vpi_cb_user_data gpi_get_user_data(gpi_sim_hdl hdl) FENTER vpi_get_cb_info((vpiHandle)hdl, &cbdata); - + check_vpi_error(); user_data = (p_vpi_cb_user_data)cbdata.user_data; FEXIT @@ -382,11 +440,13 @@ static int gpi_free_one_time(p_vpi_cb_user_data user_data) // If the callback has not been called we also need to call // remove as well - if (!user_data->called) + if (!user_data->called) { rc = vpi_remove_cb(cb_hdl); - else + check_vpi_error(); + } else { rc = vpi_free_object(cb_hdl); - + check_vpi_error(); + } FEXIT return 1; } @@ -405,6 +465,7 @@ static int gpi_free_recurring(p_vpi_cb_user_data user_data) } rc = vpi_remove_cb(cb_hdl); + check_vpi_error(); FEXIT return rc; } @@ -441,6 +502,7 @@ gpi_sim_hdl gpi_register_value_change_callback(int (*gpi_function)(void *), void cb_data_s.user_data = (char *)user_data; user_data->cb_hdl = vpi_register_cb(&cb_data_s); + check_vpi_error(); FEXIT return &user_data->gpi_hdl; @@ -478,6 +540,7 @@ gpi_sim_hdl gpi_register_readonly_callback(int (*gpi_function)(void *), void *gp cb_data_s.user_data = (char *)user_data; user_data->cb_hdl = vpi_register_cb(&cb_data_s); + check_vpi_error(); FEXIT return &user_data->gpi_hdl; @@ -514,6 +577,7 @@ gpi_sim_hdl gpi_register_readwrite_callback(int (*gpi_function)(void *), void *g cb_data_s.user_data = (char *)user_data; user_data->cb_hdl = vpi_register_cb(&cb_data_s); + check_vpi_error(); FEXIT return &user_data->gpi_hdl; } @@ -549,7 +613,8 @@ gpi_sim_hdl gpi_register_nexttime_callback(int (*gpi_function)(void *), void *gp cb_data_s.user_data = (char *)user_data; user_data->cb_hdl = vpi_register_cb(&cb_data_s); - + check_vpi_error(); + FEXIT return &user_data->gpi_hdl; } @@ -585,6 +650,7 @@ gpi_sim_hdl gpi_register_timed_callback(int (*gpi_function)(void *), void *gpi_c cb_data_s.user_data = (char *)user_data; user_data->cb_hdl = vpi_register_cb(&cb_data_s); + check_vpi_error(); FEXIT return &user_data->gpi_hdl; @@ -617,6 +683,7 @@ gpi_sim_hdl gpi_register_sim_start_callback(int (*gpi_function)(void *), void *g cb_data_s.user_data = (char *)user_data; user_data->cb_hdl = vpi_register_cb(&cb_data_s); + check_vpi_error(); FEXIT return &user_data->gpi_hdl; @@ -649,6 +716,7 @@ gpi_sim_hdl gpi_register_sim_end_callback(int (*gpi_function)(void *), void *gpi cb_data_s.user_data = (char *)user_data; user_data->cb_hdl = vpi_register_cb(&cb_data_s); + check_vpi_error(); FEXIT return &user_data->gpi_hdl; @@ -747,7 +815,7 @@ void gpi_sim_end() sim_finish_cb = NULL; vpi_control(vpiFinish); - + check_vpi_error(); FEXIT } From 6a09de34e84d2cbfd84474060faed420f1740f12 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Wed, 10 Jul 2013 19:14:26 -0500 Subject: [PATCH 0206/2656] chk_vpi_error --- lib/vpi_shim/gpi_vpi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 9659561c..624f9934 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -81,7 +81,6 @@ static int check_vpi_error(void) int loglevel; char msg_buf [100]; level = vpi_chk_error(&info); - if (level == 0) { return; } From 1b00a405064675a95196cdc46af3adb0877a78aa Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Wed, 10 Jul 2013 19:16:53 -0500 Subject: [PATCH 0207/2656] cleaned demo --- examples/demo/demo.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/demo/demo.py b/examples/demo/demo.py index 8c985d69..7b2bc4a0 100644 --- a/examples/demo/demo.py +++ b/examples/demo/demo.py @@ -108,7 +108,5 @@ def example_test(dut): @cocotb.test() def example_test2(dut): """This is another example test""" - s = simulator.get_handle_by_name(dut._handle, "foo") - simulator.get_name_string(s) result = yield Timer(1000000) dut.log.warning("test complete!") From 4f469f283f50b2be158bfc021c4efe97c42c6412 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Wed, 10 Jul 2013 19:17:36 -0500 Subject: [PATCH 0208/2656] cleaned demo again --- examples/demo/demo.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/demo/demo.py b/examples/demo/demo.py index 7b2bc4a0..0d8b5104 100644 --- a/examples/demo/demo.py +++ b/examples/demo/demo.py @@ -29,7 +29,6 @@ Example usage of the testbench framework """ -from cocotb.handle import * import cocotb, simulator from cocotb.decorators import coroutine from cocotb.triggers import Timer, Edge, Event @@ -108,5 +107,5 @@ def example_test(dut): @cocotb.test() def example_test2(dut): """This is another example test""" - result = yield Timer(1000000) + result = yield Timer(1000000) dut.log.warning("test complete!") From f0fdf098da0c1c7caa033c25b9b07b6c11211652 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Wed, 10 Jul 2013 19:18:19 -0500 Subject: [PATCH 0209/2656] cleaned demo again third time lucky --- examples/demo/demo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/demo/demo.py b/examples/demo/demo.py index 0d8b5104..7d82c261 100644 --- a/examples/demo/demo.py +++ b/examples/demo/demo.py @@ -29,7 +29,7 @@ Example usage of the testbench framework """ -import cocotb, simulator +import cocotb from cocotb.decorators import coroutine from cocotb.triggers import Timer, Edge, Event From 3926683fb08e95042ad383505c42b008a299ba63 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 11 Jul 2013 09:27:59 +0100 Subject: [PATCH 0210/2656] Issue #9: Some very primative documentation about Triggers --- documentation/source/conf.py | 6 ++- documentation/source/triggers.rst | 62 ++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/documentation/source/conf.py b/documentation/source/conf.py index a0a95004..f7a0038d 100644 --- a/documentation/source/conf.py +++ b/documentation/source/conf.py @@ -231,7 +231,8 @@ # dir menu entry, description, category) texinfo_documents = [ ('index', 'cocotb', u'cocotb Documentation', - u'PotentialVentures', 'cocotb', 'One line description of project.', + u'PotentialVentures', 'cocotb', 'Coroutine Cosimulation TestBench \ + environment for efficient verification of RTL using Python.', 'Miscellaneous'), ] @@ -246,3 +247,6 @@ # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False + +# For now show the todoy +todo_include_todos = True diff --git a/documentation/source/triggers.rst b/documentation/source/triggers.rst index 00aad38e..edcae0b6 100644 --- a/documentation/source/triggers.rst +++ b/documentation/source/triggers.rst @@ -1,5 +1,65 @@ Triggers ======== +Triggers are used to indicate when the scheduler should resume coroutine execution. Typically a coroutine will **yield** a trigger or a list of triggers. + +Simulation Timing +----------------- + +Timer(time) +^^^^^^^^^^^ + +Registers a timed callback with the simulator to continue execution of the coroutine after a specified simulation time period has elapsed. + +.. todo:: + What is the behaviour if time=0? + + +ReadOnly() +^^^^^^^^^^ + +Registers a callback which will continue execution of the coroutine when the current simulation timestep moves to the ReadOnly phase. Useful for monitors which need to wait for all processes to execute (both RTL and cocotb) to ensure sampled signal values are final. + + + +Signal related +-------------- + +Edge(signal) +^^^^^^^^^^^^ + +Registers a callback that will continue execution of the coroutine on any value change of a signal. + +.. todo:: + Behaviour for vectors + + + +RisingEdge(signal) +^^^^^^^^^^^^^^^^^^ + +Registers a callback that will continue execution of the coroutine on a transition from 0 to 1 of signal. + + +ClockCycles(signal, num_cycles) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Registers a callback that will continue execution of the coroutine when num_cycles transistions from 0 to 1 have occured. + + +Python Triggers +--------------- + +Event() +^^^^^^^ + +Can be used to synchronise between coroutines. yielding Event.wait() will block the coroutine until Event.set() is called somewhere else. + + + +Join(coroutine) +^^^^^^^^^^^^^^^ + +Will block the coroutine until another coroutine has completed. + -All stuff about triggers From cb6994e6fbd54a524f679d1b37f23370bcc12027 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 11 Jul 2013 10:33:15 +0100 Subject: [PATCH 0211/2656] Fixes issue #55: Tidy up simulator Makefiles --- makefiles/Makefile.sim | 33 ++++++++-------------- makefiles/{ => simulators}/Makefile.aldec | 0 makefiles/{ => simulators}/Makefile.icarus | 0 makefiles/{ => simulators}/Makefile.ius | 0 makefiles/{ => simulators}/Makefile.questa | 0 makefiles/{ => simulators}/Makefile.vcs | 0 6 files changed, 12 insertions(+), 21 deletions(-) rename makefiles/{ => simulators}/Makefile.aldec (100%) rename makefiles/{ => simulators}/Makefile.icarus (100%) rename makefiles/{ => simulators}/Makefile.ius (100%) rename makefiles/{ => simulators}/Makefile.questa (100%) rename makefiles/{ => simulators}/Makefile.vcs (100%) diff --git a/makefiles/Makefile.sim b/makefiles/Makefile.sim index 111c904c..10ee47ba 100644 --- a/makefiles/Makefile.sim +++ b/makefiles/Makefile.sim @@ -28,27 +28,18 @@ # This is a simple wrapper Makefile to pull in the appropriate Makefile for # the desired simulator -SIM?=ICARUS +# Default to Icarus if no simulator is defined +SIM ?= icarus -ifeq ($(SIM),ICARUS) -include $(SIM_ROOT)/makefiles/Makefile.icarus -else -ifeq ($(SIM),ALDEC) -include $(SIM_ROOT)/makefiles/Makefile.aldec -else -ifeq ($(SIM),VCS) -include $(SIM_ROOT)/makefiles/Makefile.vcs -else -ifeq ($(SIM),IUS) -include $(SIM_ROOT)/makefiles/Makefile.ius -else -ifeq ($(SIM),QUESTA) -include $(SIM_ROOT)/makefiles/Makefile.questa -else -$(error "Unknown simulator selected. Set SIM to either ICARUS, ALDEC, IUS, QUESTA or VCS") -endif -endif -endif -endif +# Maintain backwards compatibility by supporting upper and lower case SIM variable +SIM_LOWERCASE := $(shell echo $(SIM) | tr A-Z a-z) + +HAVE_SIMULATOR = $(shell if [ -f $(SIM_ROOT)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) ]; then echo 1; else echo 0; fi;) +AVAILABLE_SIMULATORS = $(patsubst .%,%,$(suffix $(wildcard $(SIM_ROOT)/makefiles/simulators/Makefile.*))) + +ifeq ($(HAVE_SIMULATOR),0) +$(error "Couldn't find makefile for simulator: "$(SIM_LOWERCASE)"! Available simualtors: $(AVAILABLE_SIMULATORS)") endif +include $(SIM_ROOT)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) + diff --git a/makefiles/Makefile.aldec b/makefiles/simulators/Makefile.aldec similarity index 100% rename from makefiles/Makefile.aldec rename to makefiles/simulators/Makefile.aldec diff --git a/makefiles/Makefile.icarus b/makefiles/simulators/Makefile.icarus similarity index 100% rename from makefiles/Makefile.icarus rename to makefiles/simulators/Makefile.icarus diff --git a/makefiles/Makefile.ius b/makefiles/simulators/Makefile.ius similarity index 100% rename from makefiles/Makefile.ius rename to makefiles/simulators/Makefile.ius diff --git a/makefiles/Makefile.questa b/makefiles/simulators/Makefile.questa similarity index 100% rename from makefiles/Makefile.questa rename to makefiles/simulators/Makefile.questa diff --git a/makefiles/Makefile.vcs b/makefiles/simulators/Makefile.vcs similarity index 100% rename from makefiles/Makefile.vcs rename to makefiles/simulators/Makefile.vcs From 0efaf2f991146aa901b3387782d582577dd7168e Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 11 Jul 2013 11:54:09 +0100 Subject: [PATCH 0212/2656] Revert "Merge pull request #37 from GordonMcGregor/xunit_reporter" This reverts commit 252602ae01319068dd413098c82b8c21b028a600, reversing changes made to cb6994e6fbd54a524f679d1b37f23370bcc12027. --- cocotb/regression.py | 91 +++++++++++++++++++++++++++++------- cocotb/xunit_reporter.py | 99 ---------------------------------------- 2 files changed, 74 insertions(+), 116 deletions(-) delete mode 100644 cocotb/xunit_reporter.py diff --git a/cocotb/regression.py b/cocotb/regression.py index 415c2d71..aa9f1b84 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -37,7 +37,6 @@ from cocotb.triggers import NullTrigger import cocotb.ANSI as ANSI -from xunit_reporter import XUnitReporter from cocotb.result import TestError, TestFailure, TestSuccess @@ -70,8 +69,8 @@ def initialise(self): self.ntests = 0 self.count = 1 - self.xunit = XUnitReporter() - self.xunit.add_testsuite(name="all", tests=repr(self.ntests), package="all") + + xml = "" # Auto discovery for module_name in self._modules: @@ -95,21 +94,29 @@ def initialise(self): self._queue.append(thing(self._dut)) except TestError: self.log.warning("Skipping test %s" % thing.name) - self.xunit.add_testcase(name=thing.name, classname=module_name, time="0.0", skipped=True) + xml += xunit_output(thing.name, module_name, 0.0, skipped=True) continue self.ntests += 1 for valid_tests in self._queue: self.log.info("Found test %s.%s" % - (valid_tests.module, - valid_tests.funcname)) + (valid_tests.module, + valid_tests.funcname)) + self.start_xml(self.ntests) + self._fout.write(xml + '\n') + def start_xml(self, ntests): + """Write the XML header into results.txt""" + self._fout = open("results.xml", 'w') + self._fout.write("""\n""") + self._fout.write("""\n""" % ntests) def tear_down(self): """It's the end of the world as we know it""" + self._fout.write("") + self._fout.close() self.log.info("Shutting down...") - self.xunit.write() simulator.stop_simulator() def next_test(self): @@ -120,26 +127,33 @@ def next_test(self): def handle_result(self, result): """Handle a test result + Dumps result to XML and schedules the next test (if any) + Args: result (TestComplete exception) """ - self.xunit.add_testcase(name =self._running_test.funcname, - classname=self._running_test.module, - time=repr(time.time() - self._running_test.start_time) ) - if isinstance(result, TestFailure): - self.xunit.add_failure("\n".join(self._running_test.error_messages)) + if isinstance(result, TestFailure) and not \ + self._running_test.expect_fail: + self._fout.write(xunit_output(self._running_test.funcname, + self._running_test.module, + time.time() - self._running_test.start_time, + failure="\n".join(self._running_test.error_messages))) + else: + self._fout.write(xunit_output(self._running_test.funcname, + self._running_test.module, + time.time() - self._running_test.start_time)) self.execute() def execute(self): self._running_test = cocotb.regression.next_test() if self._running_test: - # Want this to stand out a little bit + # Want this to stand out a little bit self.log.info("%sRunning test %d/%d:%s %s" % ( - ANSI.BLUE_BG +ANSI.BLACK_FG, - self.count, self.ntests, - ANSI.DEFAULT_FG + ANSI.DEFAULT_BG, + ANSI.BLUE_BG +ANSI.BLACK_FG, + self.count, self.ntests, + ANSI.DEFAULT_FG + ANSI.DEFAULT_BG, self._running_test)) if self.count is 1: test = cocotb.scheduler.add(self._running_test) @@ -147,4 +161,47 @@ def execute(self): test = cocotb.scheduler.new_test(self._running_test) self.count+=1 else: - self.tear_down() + self.tear_down() + + +def xunit_output(name, classname, time, skipped=False, failure="", error=""): + """ + Format at xunit test output in XML + + Args: + name (str): the name of the test + + classname (str): the name of the class + + time (float): duration of the test in seconds + + Kwargs: + skipped (bool): this test was skipped + + failure (str): failure message to report + + error (str): error message to report + + Returns an XML string + + """ + xml = """ \n" + else: + xml += ">\n" + + if skipped: + xml += " \n" + + if failure: + xml += " %s\n \n" % \ + failure + + if error: + xml += " %s\n \n" % \ + error + + return xml + " \n" diff --git a/cocotb/xunit_reporter.py b/cocotb/xunit_reporter.py deleted file mode 100644 index bc551c39..00000000 --- a/cocotb/xunit_reporter.py +++ /dev/null @@ -1,99 +0,0 @@ -from xml.etree.ElementTree import Element, SubElement -import xml.etree.ElementTree as ET - -TRUNCATE_LINES = 100 - -# file from http://stackoverflow.com/questions/136168/get-last-n-lines-of-a-file-with-python-similar-to-tail -class File(file): - - def countlines(self): - buf = mmap.mmap(self.fileno(), 0) - lines = 0 - while buf.readline(): - lines += 1 - return lines - - def head(self, lines_2find=1): - self.seek(0) #Rewind file - return [self.next() for x in xrange(lines_2find)] - - def tail(self, lines_2find=1): - self.seek(0, 2) #go to end of file - bytes_in_file = self.tell() - lines_found, total_bytes_scanned = 0, 0 - while (lines_2find+1 > lines_found and - bytes_in_file > total_bytes_scanned): - byte_block = min(1024, bytes_in_file-total_bytes_scanned) - self.seek(-(byte_block+total_bytes_scanned), 2) - total_bytes_scanned += byte_block - lines_found += self.read(1024).count('\n') - self.seek(-total_bytes_scanned, 2) - line_list = list(self.readlines()) - return line_list[-lines_2find:] - - -class XUnitReporter(object): - - def __init__(self, filename="results.xml"): - self.results = Element("testsuites", name="results") - self.filename = filename - - def add_testsuite(self, **kwargs): - self.last_testsuite = SubElement(self.results, "testsuite", **kwargs) - return self.last_testsuite - - def add_testcase(self, testsuite=None, **kwargs): - if testsuite == None: - testsuite = self.last_testsuite - self.last_testcase = SubElement(testsuite, "testcase", **kwargs) - return self.last_testcase - - def update_testsuite(self, testsuite=None, **kwargs): - if testsuite == None: - testsuite = self.last_testsuite - for k in kwargs: - testsuite.set(k, str(kwargs[k])) - - def update_testsuites(self, **kwargs): - for k in kwargs: - self.results.set(k, str(kwargs[k])) - - def add_log(self, logfile, testcase=None): - if testcase == None: - testcase = self.last_testcase - log = SubElement(testcase, "system-out") - f = File(logfile, 'r+') - lines = f.countlines() - if lines > (TRUNCATE_LINES * 2): - head = f.head(TRUNCATE_LINES) - tail = f.tail(TRUNCATE_LINES) - log.text = "".join(head + list("[...truncated %d lines...]\n" % ( (lines - (TRUNCATE_LINES*2)) )) + tail) - else: - log.text = "".join(f.readlines()) - - def add_failure(self, testcase=None, **kwargs): - if testcase == None: - testcase = self.last_testcase - log = SubElement(testcase, "failure", **kwargs) - - - def indent(self, elem, level=0): - i = "\n" + level*" " - if len(elem): - if not elem.text or not elem.text.strip(): - elem.text = i + " " - if not elem.tail or not elem.tail.strip(): - elem.tail = i - for elem in elem: - self.indent(elem, level+1) - if not elem.tail or not elem.tail.strip(): - elem.tail = i - else: - if level and (not elem.tail or not elem.tail.strip()): - elem.tail = i - - def write(self): - print 'Capturing results to ', self.filename - self.indent(self.results) - ET.ElementTree(self.results).write(self.filename, xml_declaration = True, method = "xml", encoding="UTF-8") - From 8614c037d6ec0415d8b753968332fcfdc0d86947 Mon Sep 17 00:00:00 2001 From: gordonmcgregor Date: Thu, 11 Jul 2013 06:08:28 -0500 Subject: [PATCH 0213/2656] reset to clean history --- examples/demo/demo.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/demo/demo.py b/examples/demo/demo.py index 7b2bc4a0..7d82c261 100644 --- a/examples/demo/demo.py +++ b/examples/demo/demo.py @@ -29,8 +29,7 @@ Example usage of the testbench framework """ -from cocotb.handle import * -import cocotb, simulator +import cocotb from cocotb.decorators import coroutine from cocotb.triggers import Timer, Edge, Event @@ -108,5 +107,5 @@ def example_test(dut): @cocotb.test() def example_test2(dut): """This is another example test""" - result = yield Timer(1000000) + result = yield Timer(1000000) dut.log.warning("test complete!") From 4d257467a7a0eaaa48d6c179f739af29df56564a Mon Sep 17 00:00:00 2001 From: gordonmcgregor Date: Thu, 11 Jul 2013 06:21:25 -0500 Subject: [PATCH 0214/2656] tidy up based on feedback --- include/vpi_user.h | 14 ++++----- lib/vpi_shim/gpi_vpi.c | 64 +++++++++++++++++++++--------------------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/include/vpi_user.h b/include/vpi_user.h index 7f456676..58be1115 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -175,13 +175,13 @@ typedef struct t_cb_data typedef struct t_vpi_error_info { - int32_t state; - int32_t level; - char *message; - char *product; - char *code; - char *file; - int32_t line; + int32_t state; + int32_t level; + char *message; + char *product; + char *code; + char *file; + int32_t line; } s_vpi_error_info, *p_vpi_error_info; extern vpiHandle vpi_register_cb(p_cb_data cb_data_p); diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 624f9934..089ec6fc 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -75,45 +75,45 @@ typedef gpi_clock_t *gpi_clock_hdl; // Should be run after every VPI call to check error status static int check_vpi_error(void) { - int level=0; + int level=0; #if VPI_CHECKING - s_vpi_error_info info; - int loglevel; - char msg_buf [100]; - level = vpi_chk_error(&info); - if (level == 0) { - return; + s_vpi_error_info info; + int loglevel; + char msg_buf [100]; + level = vpi_chk_error(&info); + if (level == 0) { + return; } - switch (level) { - case vpiNotice: loglevel = GPIInfo; - break; - case vpiWarning: loglevel = GPIWarning; - break; - case vpiError: loglevel = GPIError; - break; - case vpiSystem: - case vpiInternal: loglevel = GPICritical; - break; + switch (level) { + case vpiNotice: loglevel = GPIInfo; + break; + case vpiWarning: loglevel = GPIWarning; + break; + case vpiError: loglevel = GPIError; + break; + case vpiSystem: + case vpiInternal: loglevel = GPICritical; + break; } - snprintf(msg_buf, 100, "VPI Error level %d\n", level ); - gpi_log("cocotb.gpi", loglevel, __FILE__, __func__, __LINE__, msg_buf); + snprintf(msg_buf, 100, "VPI Error level %d\n", level ); + gpi_log("cocotb.gpi", loglevel, __FILE__, __func__, __LINE__, msg_buf); - snprintf(msg_buf, 100, "MESG %s\n", info.message ); - gpi_log("cocotb.gpi", loglevel, __FILE__, __func__, __LINE__, msg_buf); + snprintf(msg_buf, 100, "MESG %s\n", info.message ); + gpi_log("cocotb.gpi", loglevel, __FILE__, __func__, __LINE__, msg_buf); - snprintf(msg_buf, 100, "PROD %s\n", info.product ); - gpi_log("cocotb.gpi", loglevel, __FILE__, __func__, __LINE__, msg_buf); + snprintf(msg_buf, 100, "PROD %s\n", info.product ); + gpi_log("cocotb.gpi", loglevel, __FILE__, __func__, __LINE__, msg_buf); - snprintf(msg_buf, 100, "CODE %s\n", info.code ); - gpi_log("cocotb.gpi", loglevel, __FILE__, __func__, __LINE__, msg_buf); + snprintf(msg_buf, 100, "CODE %s\n", info.code ); + gpi_log("cocotb.gpi", loglevel, __FILE__, __func__, __LINE__, msg_buf); - snprintf(msg_buf, 100, "FILE %s\n", info.file ); - gpi_log("cocotb.gpi", loglevel, __FILE__, __func__, __LINE__, msg_buf); + snprintf(msg_buf, 100, "FILE %s\n", info.file ); + gpi_log("cocotb.gpi", loglevel, __FILE__, __func__, __LINE__, msg_buf); #endif - return level; + return level; } void gpi_free_handle(gpi_sim_hdl gpi_hdl) @@ -150,8 +150,8 @@ gpi_sim_hdl gpi_get_root_handle() // Need to free the iterator if it didn't return NULL if (root != NULL && !vpi_free_object(iterator)) { LOG_WARN("VPI: Attempting to free root iterator failed!"); + check_vpi_error(); } - check_vpi_error(); rv = gpi_alloc_handle(); rv->sim_hdl = root; @@ -329,7 +329,7 @@ char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl) { - FENTER + FENTER const char *name = vpi_get_str(vpiFullName, (vpiHandle)(gpi_hdl->sim_hdl)); check_vpi_error(); char *result = gpi_copy_name(name); @@ -442,10 +442,10 @@ static int gpi_free_one_time(p_vpi_cb_user_data user_data) if (!user_data->called) { rc = vpi_remove_cb(cb_hdl); check_vpi_error(); - } else { + } else { rc = vpi_free_object(cb_hdl); check_vpi_error(); - } + } FEXIT return 1; } From 0fb17acc20a3ff862b7727985edd7105a5de9331 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 11 Jul 2013 12:32:53 +0100 Subject: [PATCH 0215/2656] Issue #7, #46, #30, #28 etc. Improved test result reporting --- cocotb/decorators.py | 13 +++++++++---- cocotb/regression.py | 22 ++++++++++++---------- cocotb/result.py | 13 +++++++++++-- cocotb/scheduler.py | 4 +--- 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index c373ab72..17633ec6 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -164,14 +164,15 @@ def send(self, value): if not self.started: self.error_messages = [] self.log.info("Starting test: \"%s\"\nDescription: %s" % (self.funcname, self.__doc__)) - self.start_time = time.time() + self.start_time = time.time() self.started = True try: self.log.debug("Sending trigger %s" % (str(value))) return self._coro.send(value) - except TestFailure as e: + except TestComplete as e: self.log.info(str(e)) + e.stderr.write("\n".join(self.error_messages)) raise except StopIteration: raise TestSuccess() @@ -197,7 +198,9 @@ def __call__(self, *args, **kwargs): return RunningCoroutine(self._func(*args, **kwargs), self) except Exception as e: traceback.print_exc() - raise TestError(str(e)) + result = TestError(str(e)) + traceback.print_exc(file=result.stderr) + raise result def __get__(self, obj, type=None): """Permit the decorator to be used on class methods @@ -229,7 +232,9 @@ def _wrapped_test(*args, **kwargs): return RunningTest(self._func(*args, **kwargs), self) except Exception as e: traceback.print_exc() - raise TestError(str(e)) + result = TestError(str(e)) + traceback.print_exc(file=result.stderr) + raise result _wrapped_test.im_test = True # For auto-regressions _wrapped_test.name = self._func.__name__ diff --git a/cocotb/regression.py b/cocotb/regression.py index aa9f1b84..3e905047 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -92,9 +92,9 @@ def initialise(self): if hasattr(thing, "im_test"): try: self._queue.append(thing(self._dut)) - except TestError: + except TestError as result: self.log.warning("Skipping test %s" % thing.name) - xml += xunit_output(thing.name, module_name, 0.0, skipped=True) + xml += xunit_output(thing.name, module_name, 0.0, skipped=True, error=result.stderr.getvalue()) continue self.ntests += 1 @@ -132,17 +132,19 @@ def handle_result(self, result): Args: result (TestComplete exception) """ - - if isinstance(result, TestFailure) and not \ - self._running_test.expect_fail: + if isinstance(result, TestSuccess) or self._running_test.expect_fail: self._fout.write(xunit_output(self._running_test.funcname, self._running_test.module, - time.time() - self._running_test.start_time, - failure="\n".join(self._running_test.error_messages))) - else: + time.time() - self._running_test.start_time)) + self.log.info("Test Passed: %s" % self._running_test.funcname) + if isinstance(result, (TestFailure, TestError)) and not \ + self._running_test.expect_fail: self._fout.write(xunit_output(self._running_test.funcname, self._running_test.module, - time.time() - self._running_test.start_time)) + time.time() - self._running_test.start_time, + failure=result.stderr.getvalue())) + self.log.warning("Test Failed: %s (result was %s)" % ( + self._running_test.funcname, result.__class__.__name__)) self.execute() @@ -154,7 +156,7 @@ def execute(self): ANSI.BLUE_BG +ANSI.BLACK_FG, self.count, self.ntests, ANSI.DEFAULT_FG + ANSI.DEFAULT_BG, - self._running_test)) + self._running_test.funcname)) if self.count is 1: test = cocotb.scheduler.add(self._running_test) else: diff --git a/cocotb/result.py b/cocotb/result.py index dcf46526..555606a0 100644 --- a/cocotb/result.py +++ b/cocotb/result.py @@ -23,8 +23,17 @@ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' -# Exceptions used to indicate test results -class TestComplete(StopIteration): pass +# TODO: Coule use cStringIO? +from StringIO import StringIO + +class TestComplete(StopIteration): + """ + Exceptions are used to pass test results around. + """ + def __init__(self, *args, **kwargs): + super(TestComplete, self).__init__(*args, **kwargs) + self.stdout = StringIO() + self.stderr = StringIO() class TestError(TestComplete): pass diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 0745963f..f8d3c5bb 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -241,8 +241,6 @@ def schedule(self, coroutine, trigger=None): # TestComplete indication is game over, tidy up except TestComplete as test_result: - # self.log.error(str(test_result)) - # Tag that close down is needed, save the test_result # for later use in cleanup handler self._terminate = True @@ -251,7 +249,7 @@ def schedule(self, coroutine, trigger=None): self._readonly = self.add(self.move_to_cleanup()) self.log.debug("Coroutine completed execution with TestComplete: %s" % str(coroutine)) - return + return coroutine.log.debug("Finished sheduling coroutine (%s)" % str(trigger)) From 1bb647afc52f4a763adaa76e025bdf23f2072d78 Mon Sep 17 00:00:00 2001 From: gordonmcgregor Date: Thu, 11 Jul 2013 06:34:07 -0500 Subject: [PATCH 0216/2656] creating 'add_skipped' method and fixing skipped test --- cocotb/regression.py | 9 +++++---- cocotb/xunit_reporter.py | 6 +++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 415c2d71..e1ca04ee 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -1,4 +1,4 @@ -''' Copyright (c) 2013 Potential Ventures Ltd + ''' Copyright (c) 2013 Potential Ventures Ltd All rights reserved. Redistribution and use in source and binary forms, with or without @@ -95,7 +95,8 @@ def initialise(self): self._queue.append(thing(self._dut)) except TestError: self.log.warning("Skipping test %s" % thing.name) - self.xunit.add_testcase(name=thing.name, classname=module_name, time="0.0", skipped=True) + self.xunit.add_testcase(name=thing.name, classname=module_name, time="0.0") + self.xunit_add_skipped() continue self.ntests += 1 @@ -105,13 +106,13 @@ def initialise(self): valid_tests.funcname)) - def tear_down(self): """It's the end of the world as we know it""" self.log.info("Shutting down...") self.xunit.write() simulator.stop_simulator() + def next_test(self): """Get the next test to run""" if not self._queue: return None @@ -130,7 +131,7 @@ def handle_result(self, result): if isinstance(result, TestFailure): self.xunit.add_failure("\n".join(self._running_test.error_messages)) - self.execute() + self.execute() def execute(self): self._running_test = cocotb.regression.next_test() diff --git a/cocotb/xunit_reporter.py b/cocotb/xunit_reporter.py index bc551c39..df409be3 100644 --- a/cocotb/xunit_reporter.py +++ b/cocotb/xunit_reporter.py @@ -76,7 +76,11 @@ def add_failure(self, testcase=None, **kwargs): testcase = self.last_testcase log = SubElement(testcase, "failure", **kwargs) - + def add_skipped(self, testcase=None, **kwargs): + if testcase ==None + testcase = self.last_testcase + log = SubElement(testcase, "skipped", **kwargs) + def indent(self, elem, level=0): i = "\n" + level*" " if len(elem): From 9e2a5e0c6f9ab5cd55f9ada6f4f5e973ad7561b7 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Thu, 11 Jul 2013 11:37:09 +0000 Subject: [PATCH 0217/2656] reset --- cocotb/regression.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index e1ca04ee..76313062 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -1,4 +1,4 @@ - ''' Copyright (c) 2013 Potential Ventures Ltd +''' Copyright (c) 2013 Potential Ventures Ltd All rights reserved. Redistribution and use in source and binary forms, with or without From b0d01fed801bcbf4d6fff0183a21d0aaa3afadc3 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Thu, 11 Jul 2013 11:41:10 +0000 Subject: [PATCH 0218/2656] adding add_skipped . cleaned up after testing --- cocotb/regression.py | 2 +- cocotb/xunit_reporter.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 76313062..3b815edd 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -131,7 +131,7 @@ def handle_result(self, result): if isinstance(result, TestFailure): self.xunit.add_failure("\n".join(self._running_test.error_messages)) - self.execute() + self.execute() def execute(self): self._running_test = cocotb.regression.next_test() diff --git a/cocotb/xunit_reporter.py b/cocotb/xunit_reporter.py index df409be3..2ff8cf79 100644 --- a/cocotb/xunit_reporter.py +++ b/cocotb/xunit_reporter.py @@ -77,7 +77,7 @@ def add_failure(self, testcase=None, **kwargs): log = SubElement(testcase, "failure", **kwargs) def add_skipped(self, testcase=None, **kwargs): - if testcase ==None + if testcase == None: testcase = self.last_testcase log = SubElement(testcase, "skipped", **kwargs) From 7c05291c74ae5c61c0d6cfdae5b3dccaf35eb3cf Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Thu, 11 Jul 2013 11:47:58 +0000 Subject: [PATCH 0219/2656] removing print statement --- cocotb/xunit_reporter.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cocotb/xunit_reporter.py b/cocotb/xunit_reporter.py index 2ff8cf79..5fa0e7e2 100644 --- a/cocotb/xunit_reporter.py +++ b/cocotb/xunit_reporter.py @@ -97,7 +97,6 @@ def indent(self, elem, level=0): elem.tail = i def write(self): - print 'Capturing results to ', self.filename self.indent(self.results) ET.ElementTree(self.results).write(self.filename, xml_declaration = True, method = "xml", encoding="UTF-8") From 7572efdc32c0c3da50e0e771f5fe8663c26fa326 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 11 Jul 2013 13:09:26 +0100 Subject: [PATCH 0220/2656] Cleanup: Ensure we always interpret every test result correctly --- cocotb/regression.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 3e905047..e60a61d2 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -132,13 +132,19 @@ def handle_result(self, result): Args: result (TestComplete exception) """ - if isinstance(result, TestSuccess) or self._running_test.expect_fail: + if isinstance(result, TestSuccess): self._fout.write(xunit_output(self._running_test.funcname, self._running_test.module, time.time() - self._running_test.start_time)) self.log.info("Test Passed: %s" % self._running_test.funcname) - if isinstance(result, (TestFailure, TestError)) and not \ - self._running_test.expect_fail: + + elif self._running_test.expect_fail: + self._fout.write(xunit_output(self._running_test.funcname, + self._running_test.module, + time.time() - self._running_test.start_time)) + self.log.info("Test failed as expected: %s (result was %s)" % ( + self._running_test.funcname, result.__class__.__name__)) + else: self._fout.write(xunit_output(self._running_test.funcname, self._running_test.module, time.time() - self._running_test.start_time, From c23ad736c315c0b1a2c81c73e9d843e385ba9af8 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 11 Jul 2013 13:10:43 +0100 Subject: [PATCH 0221/2656] Workaround for issue #57 --- cocotb/scheduler.py | 19 +++++++++++-------- cocotb/triggers.py | 6 +++++- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index f8d3c5bb..4acbcb3f 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -210,7 +210,7 @@ def schedule(self, coroutine, trigger=None): # Normal co-routine completion except cocotb.decorators.CoroutineComplete as exc: self.log.debug("Coroutine completed execution with CoroutineComplete: %s" % str(coroutine)) - + # Call any pending callbacks that were waiting for this coroutine to exit exc() return @@ -243,13 +243,16 @@ def schedule(self, coroutine, trigger=None): # Tag that close down is needed, save the test_result # for later use in cleanup handler - self._terminate = True - self._test_result = test_result - self.cleanup() - self._readonly = self.add(self.move_to_cleanup()) - - self.log.debug("Coroutine completed execution with TestComplete: %s" % str(coroutine)) - return + # If we're already tearing down we ignore any further test results + # that may be raised. Required because currently Python triggers don't unprime + if not self._terminate: + self._terminate = True + self._test_result = test_result + self.cleanup() + self._readonly = self.add(self.move_to_cleanup()) + + self.log.warning("Coroutine completed execution with %s: %s" % (test_result.__class__.__name__, str(coroutine))) + return coroutine.log.debug("Finished sheduling coroutine (%s)" % str(trigger)) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 42f3937d..17660df9 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -66,7 +66,11 @@ def __str__(self): class PythonTrigger(Trigger): """Python triggers don't use GPI at all - For example notification of coroutine completion etc""" + For example notification of coroutine completion etc + + TODO: + Still need to implement unprime + """ pass From b047e9fcf3ac2b66aac3973bef37239adf13163f Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 11 Jul 2013 15:00:37 +0100 Subject: [PATCH 0222/2656] Cleanup: Ensure failures get logged with exception string --- cocotb/regression.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index e60a61d2..9dcd6045 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -145,10 +145,11 @@ def handle_result(self, result): self.log.info("Test failed as expected: %s (result was %s)" % ( self._running_test.funcname, result.__class__.__name__)) else: + msg = str(result) + '\n' + result.stderr.getvalue() self._fout.write(xunit_output(self._running_test.funcname, self._running_test.module, time.time() - self._running_test.start_time, - failure=result.stderr.getvalue())) + failure=msg)) self.log.warning("Test Failed: %s (result was %s)" % ( self._running_test.funcname, result.__class__.__name__)) From 992af15f36760a1cafcf205c218e72fbcfae1a09 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 11 Jul 2013 16:06:19 +0100 Subject: [PATCH 0223/2656] Cleanup: Mark expected failing test as a failure --- examples/functionality/tests/test_cocotb.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 14edb899..c6689bd9 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -109,7 +109,7 @@ def test_coroutine_kill(dut): if test_flag is not True: raise cocotb.TestFailed -@cocotb.test() +@cocotb.test(expect_fail=True) def test_adding_a_coroutine_without_starting(dut): """Catch (and provide useful error) for attempts to fork coroutines incorrectly""" yield Timer(100) @@ -119,4 +119,3 @@ def test_adding_a_coroutine_without_starting(dut): yield Timer(100) - From 3b7a7c29128cfad9fdbba00baaa7db4d8bfe6ec3 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 11 Jul 2013 17:00:29 +0100 Subject: [PATCH 0224/2656] Partial implementation of issue #17 - GPI iterator --- cocotb/handle.py | 20 ++++++++++++- examples/functionality/tests/Makefile | 2 +- .../functionality/tests/test_discovery.py | 11 +++++++ include/gpi.h | 14 ++++++++- lib/simulator/simulatormodule.c | 30 ++++++++++++++++--- lib/simulator/simulatormodule.h | 4 +-- lib/vpi_shim/gpi_vpi.c | 16 +++++----- 7 files changed, 80 insertions(+), 17 deletions(-) create mode 100644 examples/functionality/tests/test_discovery.py diff --git a/cocotb/handle.py b/cocotb/handle.py index f7797aeb..0b5ab52d 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -30,7 +30,7 @@ import logging import ctypes -import simulator as simulator +import simulator import cocotb from cocotb.binary import BinaryValue @@ -136,3 +136,21 @@ def __cmp__(self, other): # Use the comparison method of the other object against our value return self.value.__cmp__(other) + + + def __iter__(self): + """Iterates over all known types defined by simulator module""" + for handle_type in [simulator.MODULE, + simulator.PARAMETER, + simulator.REG, + simulator.NET, + simulator.NETARRAY]: + iterator = simulator.iterate(handle_type, self._handle) + while True: + try: + thing = simulator.next(iterator) + except StopIteration: + break + hdl = SimHandle(thing) + self._sub_handles[hdl.name] = hdl + yield hdl diff --git a/examples/functionality/tests/Makefile b/examples/functionality/tests/Makefile index b0d67264..edd0ef60 100644 --- a/examples/functionality/tests/Makefile +++ b/examples/functionality/tests/Makefile @@ -29,7 +29,7 @@ TOPLEVEL = sample_module VERILOG_SOURCES = ../hdl/sample_module.v -MODULE=test_cocotb,test_multiple_modules +MODULE=test_cocotb,test_multiple_modules,test_discovery include ../../../makefiles/Makefile.inc include ../../../makefiles/Makefile.sim diff --git a/examples/functionality/tests/test_discovery.py b/examples/functionality/tests/test_discovery.py new file mode 100644 index 00000000..d7369f2b --- /dev/null +++ b/examples/functionality/tests/test_discovery.py @@ -0,0 +1,11 @@ + +import cocotb +from cocotb.triggers import Timer + +@cocotb.test() +def discover_module_values(dut): + """Discover everything in the dut""" + yield Timer(0) + for thing in dut: + thing.log.info("Found something: %s" % thing.fullname) + diff --git a/include/gpi.h b/include/gpi.h index 4531f73a..9d0631b6 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -99,10 +99,22 @@ void gpi_get_sim_time(uint32_t *high, uint32_t *low); gpi_sim_hdl gpi_get_root_handle(); gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent); +// Types that can be passed to the iterator. +// +// Note these are strikingly similar to the VPI types... +#define gpiMemory 29 +#define gpiModule 32 +#define gpiNet 36 +#define gpiParameter 41 +#define gpiReg 48 +#define gpiNetArray 114 // Functions for iterating over entries of a handle // Returns an iterator handle which can then be used in gpi_next calls -gpi_iterator_hdl gpi_iterate(gpi_sim_hdl base); +// +// NB the iterator handle may be NULL if no objects of the requested type are +// found +gpi_iterator_hdl gpi_iterate(uint32_t type, gpi_sim_hdl base); // Returns NULL when there are no more objects gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator); diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index e1a12ce8..e582d598 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -431,15 +431,16 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) } -static PyObject *iterate_signals(PyObject *self, PyObject *args) +static PyObject *iterate(PyObject *self, PyObject *args) { gpi_sim_hdl hdl; + uint32_t type; gpi_iterator_hdl result; - if (!PyArg_ParseTuple(args, "l", &hdl)) + if (!PyArg_ParseTuple(args, "il", &type, &hdl)) return NULL; - result = gpi_iterate(hdl); + result = gpi_iterate(type, hdl); return Py_BuildValue("l", result); } @@ -453,6 +454,13 @@ static PyObject *next(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "l", &hdl)) return NULL; + // It's valid for iterate to return a NULL handle, to make the Python + // intuitive we simply raise StopIteration on the first iteration + if (!hdl) { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + result = gpi_next(hdl); // Raise stopiteration when we're done @@ -647,8 +655,22 @@ static PyObject *stop_clock(PyObject *self, PyObject *args) } + + PyMODINIT_FUNC initsimulator(void) { - (void) Py_InitModule("simulator", SimulatorMethods); + PyObject* simulator; + simulator = Py_InitModule("simulator", SimulatorMethods); + + // Make the GPI constants accessible from the C world + int rc = 0; + rc |= PyModule_AddIntConstant(simulator, "MEMORY", gpiMemory); + rc |= PyModule_AddIntConstant(simulator, "MODULE", gpiModule); + rc |= PyModule_AddIntConstant(simulator, "PARAMETER", gpiParameter); + rc |= PyModule_AddIntConstant(simulator, "REG", gpiReg); + rc |= PyModule_AddIntConstant(simulator, "NET", gpiNet); + rc |= PyModule_AddIntConstant(simulator, "NETARRAY", gpiNetArray); + if (rc != 0) + fprintf(stderr, "Failed to add module constants!\n"); } diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 6006393c..320a0052 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -67,7 +67,7 @@ static PyObject *create_clock(PyObject *self, PyObject *args); static PyObject *stop_clock(PyObject *self, PyObject *args); static PyObject *stop_simulator(PyObject *self, PyObject *args); -static PyObject *iterate_signals(PyObject *self, PyObject *args); +static PyObject *iterate(PyObject *self, PyObject *args); static PyObject *next(PyObject *self, PyObject *args); static PyObject *get_sim_time(PyObject *self, PyObject *args); @@ -89,7 +89,7 @@ static PyMethodDef SimulatorMethods[] = { {"create_clock", create_clock, METH_VARARGS, "Register a clock object"}, {"stop_clock", stop_clock, METH_VARARGS, "Terminate a clock"}, {"stop_simulator", stop_simulator, METH_VARARGS, "Instruct the attached simulator to stop"}, - {"iterate_signals", iterate_signals, METH_VARARGS, "Get an iterator handle to loop over all signals in an object"}, + {"iterate", iterate, METH_VARARGS, "Get an iterator handle to loop over all members in an object"}, {"next", next, METH_VARARGS, "Get the next object from the iterator"}, // FIXME METH_NOARGS => initialization from incompatible pointer type diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 4d10af8d..321aa8a6 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -139,12 +139,13 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) // Functions for iterating over entries of a handle // Returns an iterator handle which can then be used in gpi_next calls -gpi_iterator_hdl gpi_iterate(gpi_sim_hdl base) { +// NB May return NULL if no objects of the request type exist +gpi_iterator_hdl gpi_iterate(uint32_t type, gpi_sim_hdl base) { FENTER vpiHandle iterator; - iterator = vpi_iterate(vpiNet, (vpiHandle)(base->sim_hdl)); + iterator = vpi_iterate(type, (vpiHandle)(base->sim_hdl)); FEXIT return (gpi_iterator_hdl)iterator; @@ -163,12 +164,11 @@ gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) rv = NULL; } -// FIXME do we need to free the iterator handle? -// Icarus complains about this -// if (result == NULL) && !vpi_free_object((vpiHandle)iterator)) { -// LOG_WARN("VPI: Attempting to free iterator failed!"); -// } - + // Don't need to call vpi_free_object on the iterator handle + // From VPI spec: + // After returning NULL, memory associated with the iteratod handle is + // freed, making the handle invalid. + FEXIT return rv; } From dda2321a041e1befe0df6ceb0a2b4209dd03a71c Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 12 Jul 2013 00:00:52 +0100 Subject: [PATCH 0225/2656] Issue #53: Make user data persistant over a handles lifetime --- cocotb/triggers.py | 30 +- examples/functionality/tests/test_cocotb.py | 16 +- include/gpi.h | 26 +- lib/simulator/simulatormodule.c | 122 ++++++-- lib/simulator/simulatormodule.h | 6 +- lib/vpi_shim/gpi_vpi.c | 314 ++++++++++++-------- 6 files changed, 343 insertions(+), 171 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 42f3937d..62ad5a33 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -78,14 +78,20 @@ class GPITrigger(Trigger): """ def __init__(self): Trigger.__init__(self) - self.cbhdl = None + self.cbhdl = simulator.create_callback(self) def unprime(self): """Unregister a prior registered timed callback""" Trigger.unprime(self) if self.cbhdl is not None: simulator.deregister_callback(self.cbhdl) - self.cbhdl = None + + def __del__(self): + """Remove knowledge of the trigger""" + if self.cbhdl is not None: + simulator.remove_callback(self.cbhdl) + self.chhdl = None + Trigger.__del__(self) class Timer(GPITrigger): @@ -100,7 +106,7 @@ def __init__(self, time_ps): def prime(self, callback): """Register for a timed callback""" - self.cbhdl = simulator.register_timed_callback(self.time_ps, callback, self) + simulator.register_timed_callback(self.cbhdl, self.time_ps, callback, self) def __str__(self): return self.__class__.__name__ + "(%dps)" % self.time_ps @@ -115,7 +121,7 @@ def __init__(self, signal): def prime(self, callback): """Register notification of a value change via a callback""" - self.cbhdl = simulator.register_value_change_callback(self.signal._handle, callback, self) + simulator.register_value_change_callback(self.chbdl, self.signal._handle, callback, self) def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal.name @@ -129,7 +135,7 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): - self.cbhdl = simulator.register_readonly_callback(callback, self) + simulator.register_readonly_callback(self.cbhdl, callback, self) def __str__(self): return self.__class__.__name__ + "(readonly)" @@ -143,7 +149,7 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): - self.cbhdl = simulator.register_rwsynch_callback(callback, self) + simulator.register_rwsynch_callback(self.cbhdl, callback, self) def __str__(self): return self.__class__.__name__ + "(readwritesync)" @@ -156,7 +162,7 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): - self.cbhdl = simulator.register_nextstep_callback(callback, self) + simulator.register_nextstep_callback(self.cbhdl, callback, self) def __str__(self): return self.__class__.__name__ + "(nexttimestep)" @@ -175,9 +181,9 @@ def _check(obj): if self.signal.value: self._callback(self) else: - self.cbhdl = simulator.register_value_change_callback(self.signal._handle, _check, self) + simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self) - self.cbhdl = simulator.register_value_change_callback(self.signal._handle, _check, self) + simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self) def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal.name @@ -201,9 +207,9 @@ def _check(obj): self._callback(self) return - self.cbhdl = simulator.register_value_change_callback(self.signal._handle, _check, self) + simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self) - self.cbhdl = simulator.register_value_change_callback(self.signal._handle, _check, self) + simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self) def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal.name @@ -306,5 +312,5 @@ def prime(self, callback): # def __init__(self, exception): # super(self).__init__(self) - + diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 14edb899..cfe87f43 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -63,6 +63,18 @@ def test_function_not_a_coroutine_fork(dut): cocotb.fork(function_not_a_coroutine()) yield Timer(500) +@cocotb.test(expect_fail=False) +def test_function_reentrant_clock(dut): + """Test yielding a reentrant clock""" + clock = dut.clk + timer = Timer(100) + for i in range(10): + clock <= 0 + yield timer + clock <= 1 + yield timer + + @cocotb.coroutine def clock_gen(clock): """Example clock gen for test use""" @@ -71,7 +83,7 @@ def clock_gen(clock): yield Timer(100) clock <= 1 yield Timer(100) - clock.log.warning("Clock generator finished!") + clock.log.warning("Clock generator finished!") @cocotb.test(expect_fail=False) def test_yield_list(dut): @@ -88,7 +100,7 @@ def test_yield_list(dut): def clock_yield(generator): global test_flag yield Join(generator) - test_flag = True + test_flag = True @cocotb.test(expect_fail=True) def test_duplicate_yield(dut): diff --git a/include/gpi.h b/include/gpi.h index 4531f73a..be3925ac 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -125,18 +125,28 @@ void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value); void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str); // String of binary char(s) [1, 0, x, z] // The callback registering functions all return a gpi_sim_hdl; -gpi_sim_hdl gpi_register_sim_start_callback (int (*gpi_function)(void *), void *gpi_cb_data); -gpi_sim_hdl gpi_register_sim_end_callback (int (*gpi_function)(void *), void *gpi_cb_data); -gpi_sim_hdl gpi_register_timed_callback (int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps); -gpi_sim_hdl gpi_register_value_change_callback (int (*gpi_function)(void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl); -gpi_sim_hdl gpi_register_readonly_callback (int (*gpi_function)(void *), void *gpi_cb_data); -gpi_sim_hdl gpi_register_nexttime_callback (int (*gpi_function)(void *), void *gpi_cb_data); -gpi_sim_hdl gpi_register_readwrite_callback (int (*gpi_function)(void *), void *gpi_cb_data); - +int gpi_register_sim_start_callback (gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); +int gpi_register_sim_end_callback (gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); +int gpi_register_timed_callback (gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps); +int gpi_register_value_change_callback (gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl); +int gpi_register_readonly_callback (gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); +int gpi_register_nexttime_callback (gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); +int gpi_register_readwrite_callback (gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); + +// Calling convention is that 0 = success and negative numbers a failure +// For implementers of GPI the provided macro GPI_RET(x) is provided +gpi_sim_hdl gpi_create_cb_handle(void); +void gpi_destroy_cb_handle(gpi_sim_hdl gpi_hdl); int gpi_deregister_callback(gpi_sim_hdl gpi_hdl); gpi_sim_hdl gpi_clock_register(gpi_sim_hdl sim_hdl, int period, unsigned int cycles); void gpi_clock_unregister(gpi_sim_hdl clock); +#define GPI_RET(_code) \ + if (_code == 1) \ + return 0; \ + else \ + return -1 + EXTERN_C_END #endif /* COCOTB_GPI_H_ */ diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index e1a12ce8..1f0ff552 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -135,6 +135,8 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; + PyObject *handle; + gpi_sim_hdl hdl; char *result; PyGILState_STATE gstate; @@ -149,8 +151,11 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) return NULL; } + handle = PyTuple_GetItem(args, 0); + hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(handle); + // Extract the callback function - function = PyTuple_GetItem(args, 0); + function = PyTuple_GetItem(args, 1); if (!PyCallable_Check(function)) { fprintf(stderr, "Attempt to register ReadOnly without supplying a callback!\n"); return NULL; @@ -158,8 +163,8 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) Py_INCREF(function); // Remaining args for function - if (numargs > 1) - fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference + if (numargs > 2) + fArgs = PyTuple_GetSlice(args, 2, numargs); // New reference else fArgs = PyTuple_New(0); // args must not be NULL, use an empty tuple if no arguments are needed. @@ -174,9 +179,10 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) callback_data_p->function = function; callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; - callback_data_p->cb_hdl = gpi_register_readonly_callback(handle_gpi_callback, (void *)callback_data_p); + callback_data_p->cb_hdl = hdl; + gpi_register_readonly_callback(hdl, handle_gpi_callback, (void *)callback_data_p); - PyObject *rv = Py_BuildValue("l", callback_data_p->cb_hdl); + PyObject *rv = Py_BuildValue("s", "OK!"); PyGILState_Release(gstate); FEXIT @@ -190,6 +196,8 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; + PyObject *handle; + gpi_sim_hdl hdl; char *result; PyGILState_STATE gstate; @@ -204,8 +212,11 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) return NULL; } + handle = PyTuple_GetItem(args, 0); + hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(handle); + // Extract the callback function - function = PyTuple_GetItem(args, 0); + function = PyTuple_GetItem(args, 1); if (!PyCallable_Check(function)) { fprintf(stderr, "Attempt to register ReadOnly without supplying a callback!\n"); return NULL; @@ -213,8 +224,8 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) Py_INCREF(function); // Remaining args for function - if (numargs > 1) - fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference + if (numargs > 2) + fArgs = PyTuple_GetSlice(args, 2, numargs); // New reference else fArgs = PyTuple_New(0); // args must not be NULL, use an empty tuple if no arguments are needed. @@ -229,9 +240,10 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) callback_data_p->function = function; callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; - callback_data_p->cb_hdl = gpi_register_readwrite_callback(handle_gpi_callback, (void *)callback_data_p); + callback_data_p->cb_hdl = hdl; + gpi_register_readwrite_callback(hdl, handle_gpi_callback, (void *)callback_data_p); - PyObject *rv = Py_BuildValue("l", callback_data_p->cb_hdl); + PyObject *rv = Py_BuildValue("s", "OK!"); PyGILState_Release(gstate); FEXIT @@ -248,6 +260,8 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) uint64_t time_ps; char *result; PyObject *retstr; + PyObject *handle; + gpi_sim_hdl hdl; PyGILState_STATE gstate; gstate = PyGILState_Ensure(); @@ -261,8 +275,11 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) return NULL; } + handle = PyTuple_GetItem(args, 0); + hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(handle); + // Extract the callback function - function = PyTuple_GetItem(args, 0); + function = PyTuple_GetItem(args, 1); if (!PyCallable_Check(function)) { fprintf(stderr, "Attempt to register ReadOnly without supplying a callback!\n"); return NULL; @@ -270,8 +287,8 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) Py_INCREF(function); // Remaining args for function - if (numargs > 1) - fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference + if (numargs > 2) + fArgs = PyTuple_GetSlice(args, 2, numargs); // New reference else fArgs = PyTuple_New(0); // args must not be NULL, use an empty tuple if no arguments are needed. @@ -286,9 +303,10 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) callback_data_p->function = function; callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; - callback_data_p->cb_hdl = gpi_register_nexttime_callback(handle_gpi_callback, (void *)callback_data_p); + callback_data_p->cb_hdl = hdl; + gpi_register_nexttime_callback(hdl, handle_gpi_callback, (void *)callback_data_p); - PyObject *rv = Py_BuildValue("l", callback_data_p->cb_hdl); + PyObject *rv = Py_BuildValue("s", "OK!"); PyGILState_Release(gstate); FEXIT @@ -306,6 +324,8 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; + PyObject *handle; + gpi_sim_hdl hdl; uint64_t time_ps; p_callback_data callback_data_p; @@ -320,12 +340,16 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) return NULL; } + // Get the handle + handle = PyTuple_GetItem(args, 0); + hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(handle); + // Extract the time - PyObject *pTime = PyTuple_GetItem(args, 0); + PyObject *pTime = PyTuple_GetItem(args, 1); time_ps = PyLong_AsLongLong(pTime); // Extract the callback function - function = PyTuple_GetItem(args, 1); + function = PyTuple_GetItem(args, 2); if (!PyCallable_Check(function)) { fprintf(stderr, "Attempt to register timed callback without passing a callable callback!\n"); return NULL; @@ -333,8 +357,8 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) Py_INCREF(function); // Remaining args for function - if (numargs > 2) - fArgs = PyTuple_GetSlice(args, 2, numargs); // New reference + if (numargs > 3) + fArgs = PyTuple_GetSlice(args, 3, numargs); // New reference else fArgs = PyTuple_New(0); // args must not be NULL, use an empty tuple if no arguments are needed. @@ -350,10 +374,11 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) callback_data_p->function = function; callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; - callback_data_p->cb_hdl = gpi_register_timed_callback(handle_gpi_callback, (void *)callback_data_p, time_ps); + callback_data_p->cb_hdl = hdl; + gpi_register_timed_callback(hdl, handle_gpi_callback, (void *)callback_data_p, time_ps); // Check success - PyObject *rv = Py_BuildValue("l", callback_data_p->cb_hdl); + PyObject *rv = Py_BuildValue("s", "OK!"); PyGILState_Release(gstate); FEXIT @@ -375,6 +400,8 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) gpi_sim_hdl sig_hdl; char *result; PyObject *retstr; + PyObject *handle; + gpi_sim_hdl hdl; @@ -385,16 +412,19 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) Py_ssize_t numargs = PyTuple_Size(args); - if (numargs < 2) { - fprintf(stderr, "Attempt to register timed callback without enough arguments!\n"); + if (numargs < 3) { + fprintf(stderr, "Attempt to register value change callback without enough arguments!\n"); return NULL; } - PyObject *pSihHdl = PyTuple_GetItem(args, 0); + handle = PyTuple_GetItem(args, 0); + hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(handle); + + PyObject *pSihHdl = PyTuple_GetItem(args, 1); sig_hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(pSihHdl); // Extract the callback function - function = PyTuple_GetItem(args, 1); + function = PyTuple_GetItem(args, 2); if (!PyCallable_Check(function)) { fprintf(stderr, "Attempt to register value change callback without passing a callable callback!\n"); return NULL; @@ -402,8 +432,8 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) Py_INCREF(function); // Remaining args for function - if (numargs > 2) - fArgs = PyTuple_GetSlice(args, 2, numargs); // New reference + if (numargs > 3) + fArgs = PyTuple_GetSlice(args, 3, numargs); // New reference else fArgs = PyTuple_New(0); // args must not be NULL, use an empty tuple if no arguments are needed. @@ -412,6 +442,7 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) if (callback_data_p == NULL) { printf("Failed to allocate user data\n"); } + // Set up the user data (no more python API calls after this! // Causes segfault? callback_data_p->_saved_thread_state = PyThreadState_Get();//PyThreadState_Get(); @@ -419,10 +450,11 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) callback_data_p->function = function; callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; - callback_data_p->cb_hdl = gpi_register_value_change_callback(handle_gpi_callback, (void *)callback_data_p, sig_hdl); + callback_data_p->cb_hdl = hdl; + gpi_register_value_change_callback(hdl, handle_gpi_callback, (void *)callback_data_p, sig_hdl); // Check success - PyObject *rv = Py_BuildValue("l", callback_data_p->cb_hdl); + PyObject *rv = Py_BuildValue("s", "OK!"); PyGILState_Release(gstate); FEXIT @@ -586,20 +618,46 @@ static PyObject *deregister_callback(PyObject *self, PyObject *args) { gpi_sim_hdl hdl; PyObject *pSihHdl; - int ret; FENTER pSihHdl = PyTuple_GetItem(args, 0); hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(pSihHdl); - if (!gpi_deregister_callback(hdl)) - return NULL; + gpi_deregister_callback(hdl); + + FEXIT + return Py_BuildValue("s", "OK!"); +} + +static PyObject *remove_callback(PyObject *self, PyObject *args) +{ + gpi_sim_hdl hdl; + PyObject *pSihHdl; + + FENTER + + pSihHdl = PyTuple_GetItem(args, 0); + hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(pSihHdl); + + gpi_destroy_cb_handle(hdl); FEXIT return Py_BuildValue("s", "OK!"); } +static PyObject *create_callback(PyObject *self, PyObject *args) +{ + FENTER + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); + gpi_sim_hdl cb_hdl = gpi_create_cb_handle(); + + PyGILState_Release(gstate); + + FENTER + return Py_BuildValue("l", cb_hdl); +} static PyObject *create_clock(PyObject *self, PyObject *args) { diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 6006393c..62d8a28c 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -72,6 +72,8 @@ static PyObject *next(PyObject *self, PyObject *args); static PyObject *get_sim_time(PyObject *self, PyObject *args); static PyObject *deregister_callback(PyObject *self, PyObject *args); +static PyObject *remove_callback(PyObject *self, PyObject *args); +static PyObject *create_callback(PyObject *self, PyObject *args); static PyMethodDef SimulatorMethods[] = { {"log_msg", log_msg, METH_VARARGS, "Log a message"}, @@ -94,7 +96,9 @@ static PyMethodDef SimulatorMethods[] = { // FIXME METH_NOARGS => initialization from incompatible pointer type {"get_sim_time", get_sim_time, METH_VARARGS, "Get the current simulation time as a float"}, - {"deregister_callback", deregister_callback, METH_VARARGS, "Remove a registered callback"}, + {"deregister_callback", deregister_callback, METH_VARARGS, "Deregister a callback"}, + {"remove_callback", remove_callback, METH_VARARGS, "Remove a callback"}, + {"create_callback", create_callback, METH_VARARGS, "Creates a callback"}, {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 4d10af8d..372f86fc 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -42,6 +42,19 @@ static gpi_sim_hdl sim_init_cb; static gpi_sim_hdl sim_finish_cb; +static int alloc_count = 0; +static int dealloc_count = 0; +static int clear_count = 0; +static int total_count = 0; + +typedef enum vpi_cb_state_e { + VPI_FREE = 0, + VPI_PRIMED = 1, + VPI_PRE_CALL = 2, + VPI_POST_CALL = 3, + VPI_DELETE = 4, +} vpi_cb_state_t; + // callback user data used for VPI callbacks // (mostly just a thin wrapper around the gpi_callback) typedef struct t_vpi_cb_user_data { @@ -51,8 +64,7 @@ typedef struct t_vpi_cb_user_data { vpiHandle cb_hdl; s_vpi_value cb_value; gpi_sim_hdl_t gpi_hdl; - bool called; - bool cleared; + vpi_cb_state_t state; } s_vpi_cb_user_data, *p_vpi_cb_user_data; // Define a type of a clock object @@ -69,6 +81,45 @@ typedef struct gpi_clock_s { typedef gpi_clock_t *gpi_clock_hdl; +static inline int __gpi_register_cb(p_vpi_cb_user_data user, p_cb_data cb_data) +{ + /* If the user data already has a callback handle then deregister + * before getting the new one + */ + vpiHandle new_hdl = vpi_register_cb(cb_data); + + if (user->cb_hdl != NULL) + gpi_deregister_callback(&user->gpi_hdl); + + if (!new_hdl) { + printf("Unable to get a handle %d\n", new_hdl); + } + + user->cb_hdl = new_hdl; + + return 0; +} + +static inline p_vpi_cb_user_data __gpi_alloc_user(void) +{ + p_vpi_cb_user_data new_data = calloc(1, sizeof(*new_data)); + if (new_data == NULL) { + printf("VPI: Attempting allocate user_data for %s failed!", __func__); + } + + return new_data; +} + +static inline void __gpi_free_callback(gpi_sim_hdl gpi_hdl) +{ + FENTER + p_vpi_cb_user_data user_data; + user_data = gpi_container_of(gpi_hdl, s_vpi_cb_user_data, gpi_hdl); + + free(user_data); + FEXIT +} + void gpi_free_handle(gpi_sim_hdl gpi_hdl) { free(gpi_hdl); @@ -76,7 +127,7 @@ void gpi_free_handle(gpi_sim_hdl gpi_hdl) static gpi_sim_hdl gpi_alloc_handle() { - gpi_sim_hdl new_hdl = malloc(sizeof(*new_hdl)); + gpi_sim_hdl new_hdl = calloc(1, sizeof(*new_hdl)); if (!new_hdl) { LOG_CRITICAL("VPI: Could not allocate handle\n"); exit(1); @@ -312,59 +363,96 @@ static p_vpi_cb_user_data gpi_get_user_data(gpi_sim_hdl hdl) } -int32_t handle_vpi_callback(p_cb_data cb_data) +static int32_t handle_vpi_callback(p_cb_data cb_data) { FENTER int rv = 0; + vpiHandle old_cb; p_vpi_cb_user_data user_data; user_data = (p_vpi_cb_user_data)cb_data->user_data; + if (!user_data) + printf("USER DATA NULL\n"); - user_data->called = true; + user_data->state = VPI_PRE_CALL; + old_cb = user_data->cb_hdl; rv = user_data->gpi_function(user_data->gpi_cb_data); - // We call into deregister to remove from the connected - // simulator, the freeing on data will not be done - // until there is not reference left though + if (old_cb == user_data->cb_hdl) + gpi_deregister_callback(&user_data->gpi_hdl); - gpi_deregister_callback(&user_data->gpi_hdl); + /* A request to delete could have been done + * inside gpi_function + */ + if (user_data->state == VPI_DELETE) + gpi_destroy_cb_handle(&user_data->gpi_hdl); + else + user_data->state = VPI_POST_CALL; FEXIT return rv; }; -// Cleaing up is a bit complex -// 1. We need to remove the callback and -// it's associated handle internally so that -// there is a not a duplicate trigger event -// 2. The user data needs to stay around until -// thre are no more handles to it. -// Thus this function calls into the sim -// to close down if able to. Or if there -// is no internal state it closes down -// the user data. +/* Allocates memory that will persist for the lifetime of the + * handle, this may be short or long. A call to create + * must have a matching call to destroy at some point + */ +gpi_sim_hdl gpi_create_cb_handle(void) +{ + FENTER + p_vpi_cb_user_data user_data = __gpi_alloc_user(); + user_data->state = VPI_FREE; + FEXIT + return &user_data->gpi_hdl; +} + +/* Destroys the memory associated with the sim handle + * this can only be called on a handle that has been + * returned by a call to gpi_create_cb_handle + */ +void gpi_destroy_cb_handle(gpi_sim_hdl gpi_hdl) +{ + /* Check that is has been called, if this has not + * happend then also close down the sim data as well + */ + FENTER + p_vpi_cb_user_data user_data; + user_data = gpi_container_of(gpi_hdl, s_vpi_cb_user_data, gpi_hdl); + + if (user_data->state == VPI_PRE_CALL) { + user_data->state = VPI_DELETE; + } else { + gpi_deregister_callback(gpi_hdl); + __gpi_free_callback(gpi_hdl); + } + FEXIT +} + +/* Deregister a prior set up callback with the simulator + * The handle must have been allocated with gpi_create_cb_handle + * This can be called at any point between + * gpi_create_cb_handle and gpi_destroy_cb_handle + */ int gpi_deregister_callback(gpi_sim_hdl gpi_hdl) { p_vpi_cb_user_data user_data; + int rc = 1; FENTER // We should be able to user gpi_get_user_data // but this is not implemented in ICARUS // and gets upset on VCS. So instead we // do some pointer magic. - + user_data = gpi_container_of(gpi_hdl, s_vpi_cb_user_data, gpi_hdl); - if (user_data->cleared) { - memset(user_data, 0x0, sizeof(*user_data)); - free(user_data); - } else if (user_data->gpi_cleanup) { - user_data->gpi_cleanup(user_data); - user_data->cleared = true; + if (user_data->cb_hdl) { + rc = user_data->gpi_cleanup(user_data); + user_data->cb_hdl = NULL; } FEXIT - return 1; + GPI_RET(rc); } // Call when the handle relates to a one time callback @@ -373,22 +461,22 @@ int gpi_deregister_callback(gpi_sim_hdl gpi_hdl) static int gpi_free_one_time(p_vpi_cb_user_data user_data) { FENTER - int32_t rc; + int rc; vpiHandle cb_hdl = user_data->cb_hdl; if (!cb_hdl) { - LOG_ERROR("VPI: %s passed a NULL pointer\n", __func__); + printf("VPI: %s passed a NULL pointer\n", __func__); exit(1); } // If the callback has not been called we also need to call // remove as well - if (!user_data->called) + if (!user_data->state == VPI_PRIMED) { rc = vpi_remove_cb(cb_hdl); - else +// } else { rc = vpi_free_object(cb_hdl); - + } FEXIT - return 1; + return rc; } // Call when the handle relates to recurring callback @@ -397,10 +485,10 @@ static int gpi_free_one_time(p_vpi_cb_user_data user_data) static int gpi_free_recurring(p_vpi_cb_user_data user_data) { FENTER - int32_t rc; + int rc; vpiHandle cb_hdl = user_data->cb_hdl; if (!cb_hdl) { - LOG_ERROR("VPI: %s passed a NULL pointer\n", __func__); + printf("VPI: %s passed a NULL pointer\n", __func__); exit(1); } @@ -409,7 +497,15 @@ static int gpi_free_recurring(p_vpi_cb_user_data user_data) return rc; } -gpi_sim_hdl gpi_register_value_change_callback(int (*gpi_function)(void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl) +/* These functions request a callback to be active with the current + * handle and associated data. A callback handle needs to have been + * allocated with gpi_create_cb_handle first + */ + +int gpi_register_value_change_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data, + gpi_sim_hdl gpi_hdl) { FENTER s_cb_data cb_data_s; @@ -417,18 +513,12 @@ gpi_sim_hdl gpi_register_value_change_callback(int (*gpi_function)(void *), void s_vpi_value vpi_value_s; p_vpi_cb_user_data user_data; - // Freed when callback fires or the callback is deregistered - user_data = (p_vpi_cb_user_data)malloc(sizeof(s_vpi_cb_user_data)); - if (user_data == NULL) { - LOG_WARN("VPI: Attempting allocate user_data for %s failed!", __func__); - } + user_data = gpi_container_of(cb, s_vpi_cb_user_data, gpi_hdl); user_data->gpi_cb_data = gpi_cb_data; user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_recurring; user_data->cb_value.format = vpiIntVal; - user_data->called = false; - user_data->cleared = false; vpi_time_s.type = vpiSuppressTime; vpi_value_s.format = vpiIntVal; @@ -440,31 +530,29 @@ gpi_sim_hdl gpi_register_value_change_callback(int (*gpi_function)(void *), void cb_data_s.value = &user_data->cb_value; cb_data_s.user_data = (char *)user_data; - user_data->cb_hdl = vpi_register_cb(&cb_data_s); + __gpi_register_cb(user_data, &cb_data_s); + user_data->state = VPI_PRIMED; + FEXIT - return &user_data->gpi_hdl; + return 0; } -gpi_sim_hdl gpi_register_readonly_callback(int (*gpi_function)(void *), void *gpi_cb_data) +int gpi_register_readonly_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) { FENTER s_cb_data cb_data_s; s_vpi_time vpi_time_s; p_vpi_cb_user_data user_data; - // Freed when callback fires or the callback is deregistered - user_data = (p_vpi_cb_user_data)malloc(sizeof(s_vpi_cb_user_data)); - if (user_data == NULL) { - LOG_WARN("VPI: Attempting allocate user_data for %s failed!", __func__); - } + user_data = gpi_container_of(cb, s_vpi_cb_user_data, gpi_hdl); user_data->gpi_cb_data = gpi_cb_data; user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_one_time; - user_data->called = false; - user_data->cleared = false; vpi_time_s.type = vpiSimTime; vpi_time_s.high = 0; @@ -477,30 +565,27 @@ gpi_sim_hdl gpi_register_readonly_callback(int (*gpi_function)(void *), void *gp cb_data_s.value = NULL; cb_data_s.user_data = (char *)user_data; - user_data->cb_hdl = vpi_register_cb(&cb_data_s); + __gpi_register_cb(user_data, &cb_data_s); + user_data->state = VPI_PRIMED; FEXIT - return &user_data->gpi_hdl; + return 0; } -gpi_sim_hdl gpi_register_readwrite_callback(int (*gpi_function)(void *), void *gpi_cb_data) +int gpi_register_readwrite_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) { FENTER s_cb_data cb_data_s; s_vpi_time vpi_time_s; p_vpi_cb_user_data user_data; - // Freed when callback fires or the callback is deregistered - user_data = (p_vpi_cb_user_data)malloc(sizeof(s_vpi_cb_user_data)); - if (user_data == NULL) { - LOG_WARN("VPI: Attempting allocate user_data for %s failed!", __func__); - } + user_data = gpi_container_of(cb, s_vpi_cb_user_data, gpi_hdl); user_data->gpi_cb_data = gpi_cb_data; user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_one_time; - user_data->called = false; - user_data->cleared = false; vpi_time_s.type = vpiSimTime; vpi_time_s.high = 0; @@ -513,29 +598,27 @@ gpi_sim_hdl gpi_register_readwrite_callback(int (*gpi_function)(void *), void *g cb_data_s.value = NULL; cb_data_s.user_data = (char *)user_data; - user_data->cb_hdl = vpi_register_cb(&cb_data_s); + __gpi_register_cb(user_data, &cb_data_s); + user_data->state = VPI_PRIMED; + FEXIT - return &user_data->gpi_hdl; + return 0; } -gpi_sim_hdl gpi_register_nexttime_callback(int (*gpi_function)(void *), void *gpi_cb_data) +int gpi_register_nexttime_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) { FENTER s_cb_data cb_data_s; s_vpi_time vpi_time_s; p_vpi_cb_user_data user_data; - // Freed when callback fires or the callback is deregistered - user_data = (p_vpi_cb_user_data)malloc(sizeof(s_vpi_cb_user_data)); - if (user_data == NULL) { - LOG_WARN("VPI: Attempting allocate user_data for %s failed!", __func__); - } + user_data = gpi_container_of(cb, s_vpi_cb_user_data, gpi_hdl); user_data->gpi_cb_data = gpi_cb_data; user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_one_time; - user_data->called = false; - user_data->cleared = false; vpi_time_s.type = vpiSimTime; vpi_time_s.high = 0; @@ -548,30 +631,28 @@ gpi_sim_hdl gpi_register_nexttime_callback(int (*gpi_function)(void *), void *gp cb_data_s.value = NULL; cb_data_s.user_data = (char *)user_data; - user_data->cb_hdl = vpi_register_cb(&cb_data_s); + __gpi_register_cb(user_data, &cb_data_s); + user_data->state = VPI_PRIMED; FEXIT - return &user_data->gpi_hdl; + return 0; } -gpi_sim_hdl gpi_register_timed_callback(int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps) +int gpi_register_timed_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data, + uint64_t time_ps) { FENTER s_cb_data cb_data_s; s_vpi_time vpi_time_s; p_vpi_cb_user_data user_data; - // Freed when callback fires or the callback is deregistered - user_data = (p_vpi_cb_user_data)malloc(sizeof(s_vpi_cb_user_data)); - if (user_data == NULL) { - LOG_WARN("VPI: Attempting allocate user_data for %s failed!", __func__); - } + user_data = gpi_container_of(cb, s_vpi_cb_user_data, gpi_hdl); user_data->gpi_cb_data = gpi_cb_data; user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_one_time; - user_data->called = false; - user_data->cleared = false; vpi_time_s.type = vpiSimTime; vpi_time_s.high = (uint32_t)(time_ps>>32); @@ -584,30 +665,28 @@ gpi_sim_hdl gpi_register_timed_callback(int (*gpi_function)(void *), void *gpi_c cb_data_s.value = NULL; cb_data_s.user_data = (char *)user_data; - user_data->cb_hdl = vpi_register_cb(&cb_data_s); + __gpi_register_cb(user_data, &cb_data_s); + user_data->state = VPI_PRIMED; + FEXIT - return &user_data->gpi_hdl; + return 0; } -gpi_sim_hdl gpi_register_sim_start_callback(int (*gpi_function)(void *), void *gpi_cb_data) +int gpi_register_sim_start_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) { FENTER p_vpi_cb_user_data user_data; s_cb_data cb_data_s; - // Freed when callback fires or the callback is deregistered - user_data = (p_vpi_cb_user_data)malloc(sizeof(s_vpi_cb_user_data)); - if (user_data == NULL) { - LOG_WARN("VPI: Attempting allocate user_data for %s failed!", __func__); - } + user_data = gpi_container_of(cb, s_vpi_cb_user_data, gpi_hdl); user_data->gpi_cb_data = gpi_cb_data; user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_one_time; - user_data->called = false; - user_data->cleared = false; cb_data_s.reason = cbStartOfSimulation; cb_data_s.cb_rtn = handle_vpi_callback; @@ -616,30 +695,28 @@ gpi_sim_hdl gpi_register_sim_start_callback(int (*gpi_function)(void *), void *g cb_data_s.value = NULL; cb_data_s.user_data = (char *)user_data; - user_data->cb_hdl = vpi_register_cb(&cb_data_s); + __gpi_register_cb(user_data, &cb_data_s); + user_data->state = VPI_PRIMED; + FEXIT - return &user_data->gpi_hdl; + return 0; } -gpi_sim_hdl gpi_register_sim_end_callback(int (*gpi_function)(void *), void *gpi_cb_data) +int gpi_register_sim_end_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) { FENTER p_vpi_cb_user_data user_data; s_cb_data cb_data_s; - // Freed when callback fires or the callback is deregistered - user_data = (p_vpi_cb_user_data)malloc(sizeof(s_vpi_cb_user_data)); - if (user_data == NULL) { - LOG_WARN("VPI: Attempting allocate user_data for %s failed!", __func__); - } + user_data = gpi_container_of(cb, s_vpi_cb_user_data, gpi_hdl); user_data->gpi_cb_data = gpi_cb_data; user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_one_time; - user_data->called = false; - user_data->cleared = false; cb_data_s.reason = cbEndOfSimulation; cb_data_s.cb_rtn = handle_vpi_callback; @@ -648,29 +725,29 @@ gpi_sim_hdl gpi_register_sim_end_callback(int (*gpi_function)(void *), void *gpi cb_data_s.value = NULL; cb_data_s.user_data = (char *)user_data; - user_data->cb_hdl = vpi_register_cb(&cb_data_s); + __gpi_register_cb(user_data, &cb_data_s); + user_data->state = VPI_PRIMED; + FEXIT - return &user_data->gpi_hdl; + return 0; } int gpi_clock_handler(void *clock) { gpi_clock_hdl hdl = (gpi_clock_hdl)clock; - gpi_sim_hdl old_hdl; + gpi_sim_hdl cb_hdl; if (hdl->exit || (hdl->max_cycles == hdl->curr_cycle)) return; /* Unregister/free the last callback that just fired */ - old_hdl = hdl->cb_hdl; + cb_hdl = hdl->cb_hdl; hdl->value = !hdl->value; gpi_set_signal_value_int(hdl->clk_hdl, hdl->value); - hdl->cb_hdl = gpi_register_timed_callback(gpi_clock_handler, hdl, hdl->period); + gpi_register_timed_callback(cb_hdl, gpi_clock_handler, hdl, hdl->period); hdl->curr_cycle++; - - gpi_deregister_callback(old_hdl); } gpi_sim_hdl gpi_clock_register(gpi_sim_hdl sim_hdl, int period, unsigned int cycles) @@ -689,7 +766,9 @@ gpi_sim_hdl gpi_clock_register(gpi_sim_hdl sim_hdl, int period, unsigned int cyc hdl->curr_cycle = 0; gpi_set_signal_value_int(hdl->clk_hdl, hdl->value); - hdl->cb_hdl = gpi_register_timed_callback(gpi_clock_handler, hdl, hdl->period); + hdl->cb_hdl = gpi_create_cb_handle(); + + gpi_register_timed_callback(hdl->cb_hdl, gpi_clock_handler, hdl, hdl->period); FEXIT return &hdl->gpi_hdl; @@ -712,7 +791,6 @@ void register_embed(void) int handle_sim_init(void *gpi_cb_data) { FENTER - sim_init_cb = NULL; embed_sim_init(); FEXIT } @@ -720,22 +798,26 @@ int handle_sim_init(void *gpi_cb_data) void register_initial_callback(void) { FENTER - sim_init_cb = gpi_register_sim_start_callback(handle_sim_init, (void *)NULL); + sim_init_cb = gpi_create_cb_handle(); + gpi_register_sim_start_callback(sim_init_cb, handle_sim_init, (void *)NULL); FEXIT } int handle_sim_end(void *gpi_cb_data) { FENTER - if (sim_finish_cb) - embed_sim_end(); + if (sim_finish_cb) { + sim_finish_cb = NULL; + } + __gpi_free_callback(sim_init_cb); FEXIT } void register_final_callback(void) { FENTER - sim_finish_cb = gpi_register_sim_end_callback(handle_sim_end, (void *)NULL); + sim_finish_cb = gpi_create_cb_handle(); + gpi_register_sim_end_callback(sim_finish_cb, handle_sim_end, (void *)NULL); FEXIT } From 17f3dd345dfc8ed209123e152c5d891ee060e483 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Mon, 15 Jul 2013 11:33:27 +0100 Subject: [PATCH 0226/2656] Issue #9 Added more detail to quickstart guide, toned back hyperbole in introduction --- documentation/source/introduction.rst | 38 +++------------------- documentation/source/quickstart.rst | 46 +++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 33 deletions(-) diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index 1bbb3c10..9c191122 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -11,45 +11,17 @@ What is cocotb? **Cocotb** still requires a simulator to simulate the RTL. Simulators that have been tested and known to work with cocotb: -* Icarus -* Aldec Riviera-PRO +* Icarus Verilog * Synopsys VCS - -.. note:: - See the `Simulator Support`_ page for full details of supported simulators. - - -Why create cocotb? -================== - -Verification is the hardest part of realising a working design. -The EDA industry has invested heavily in helping us verify designs, -adding software contructs to verilog when creating SystemVerilog, -mandating that simulators need to implement constrain solvers, -converging on UVM as a standard methodology. Why is this inadequate? - -Fundamentally the process of verification is writing software, specifically software to test a design, -where the design happens to be synthesisable into real hardware. If VHDL, Verilog or -SystemVerilog excelled as software languages would there not be examples of their use outside of EDA? - - -Case study: UVM ---------------- - -UVM is a prime example of how the EDA industry solves a problem and the solution isn't pretty. While the ideas driving -UVM are valid and good (defining a common testbench structure, promoting code re-use, using constrained-random testing), -the outcome is a not the much needed giant leap forward for the following reasons: - -* **UVM is ugly** Given the rapid progress being made in software development and evolution of new languages, creating a framework that requires so much boilerplate and relies so heavily on macros is actually impressive! -* **UVM is niche** UVM is a niche within an already highly specialised area. Finding good UVM guys is difficult and expensive. -* **UVM is expensive** I have to pay for a simulator. I then have to pay more to enable SystemVerilog verification features. I have to hire expensive people. This is good for EDA vendors but bad for innovation. +* Aldec Riviera-PRO +* Mentor Questa +* Cadance Incisive How is cocotb different? ------------------------ -Cocotb encourages the same philosophy of design re-use and randomised testing as UVM, however is implemented in -Python rather than SystemVerilog. +Cocotb encourages the same philosophy of design re-use and randomised testing as UVM, however is implemented in Python rather than SystemVerilog. The right tool for the job diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index d2de1c56..60578794 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -2,6 +2,52 @@ Quickstart Guide ################ + +Installing cocotb +================= + +Pre-requisites +-------------- + +Cocotb has the following requirements: + +* Python 2.6+ +* Python-dev packages +* A verilog simulator + + +Running an example +------------------ + +.. code-block:: bash + + $> git clone https://github.com/potentialventures/cocotb + $> cd cocotb + $> make + $> cd examples/demo + $> make + +To run a test using a different simulator: + +.. code-block:: bash + + $> make SIM=vcs + + +Supported simulators +-------------------- + +* Icarus Verilog +* Synopsys VCS +* Aldec Riviera-PRO +* Mentor Questa +* Cadance Incisive + + + +Using cocotb +============ + A typical cocotb testbench requires no additional RTL code. The Design Under Test (DUT) is instantiated as the toplevel in the simulator without any wrapper code. Cocotb drives stimulus onto the inputs to the DUT (or further down the hierarchy) and monitors the outputs directly from Python. From 353583f732ad56af96ad9ab6d9a6c82558005773 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 15 Jul 2013 15:06:50 +0100 Subject: [PATCH 0227/2656] Issue #53: Move logging a new derived object --- cocotb/__init__.py | 21 ++++++------- cocotb/clock.py | 4 +-- cocotb/decorators.py | 7 ++--- cocotb/drivers/__init__.py | 7 +++-- cocotb/generators/feeds/__init__.py | 3 +- cocotb/handle.py | 3 +- cocotb/log.py | 48 +++++++++++++++++++++++++++-- cocotb/monitors/__init__.py | 8 ++--- cocotb/regression.py | 15 +++++---- cocotb/scheduler.py | 5 ++- cocotb/scoreboard.py | 5 ++- cocotb/triggers.py | 8 ++--- lib/embed/gpi_embed.c | 3 +- 13 files changed, 87 insertions(+), 50 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index ac6c7103..8e1cd08d 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -34,35 +34,32 @@ import threading from functools import wraps - import cocotb.handle from cocotb.scheduler import Scheduler -from cocotb.log import SimLogFormatter +from cocotb.log import SimLogFormatter, SimBaseLog, SimLog from cocotb.regression import RegressionManager # Things we want in the cocotb namespace from cocotb.decorators import test, coroutine +from guppy import hpy + # Singleton scheduler instance # NB this cheekily ensures a singleton since we're replacing the reference # so that cocotb.scheduler gives you the singleton instance and not the # scheduler package + +# Top level logger object +logging.basicConfig() +logging.setLoggerClass(SimBaseLog) +log = SimLog('cocotb') + scheduler = Scheduler() regression = None # To save typing provide an alias to scheduler.add fork = scheduler.add -# Top level logger object -log = logging.getLogger('cocotb') -log.setLevel(logging.INFO) - -# Add our default log handler -hdlr = logging.StreamHandler(sys.stdout) -hdlr.setFormatter(SimLogFormatter()) -log.addHandler(hdlr) - - class TestFailed(Exception): pass diff --git a/cocotb/clock.py b/cocotb/clock.py index 407a33fd..cdd42091 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -26,15 +26,15 @@ """ A clock class """ -import logging import simulator +from cocotb.log import SimLog class BaseClock(object): """Base class to derive from""" def __init__(self, signal): self.signal = signal - self.log = logging.getLogger("cocotb.%s.%s" % (self.__class__.__name__, self.signal.name)) + self.log = SimLog("cocotb.%s.%s" % (self.__class__.__name__, self.signal.name)) class Clock(BaseClock): """ diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 17633ec6..0a65eaef 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -26,12 +26,11 @@ import sys import time import logging - import traceback import cocotb +from cocotb.log import SimLog from cocotb.triggers import Join - from cocotb.result import TestComplete, TestError, TestFailure, TestSuccess @@ -78,7 +77,7 @@ class RunningCoroutine(object): """ def __init__(self, inst, parent): self.__name__ = "%s.0x%x" % (inst.__name__, id(self)) - self.log = logging.getLogger("cocotb.coroutine.%s" % self.__name__) + self.log = SimLog("cocotb.coroutine.%s" % self.__name__, "0x%x" % id(self)) self._coro = inst self._finished = False self._callbacks = [] @@ -191,7 +190,7 @@ class coroutine(object): def __init__(self, func): self._func = func - self.log = logging.getLogger("cocotb.function.%s" % self._func.__name__) + self.log = SimLog("cocotb.function.%s" % self._func.__name__, id(self)) def __call__(self, *args, **kwargs): try: diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index eb206e94..79296f76 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -36,6 +36,7 @@ from cocotb.decorators import coroutine from cocotb.triggers import Event, RisingEdge from cocotb.bus import Bus +from cocotb.log import SimLog class BitDriver(object): @@ -64,7 +65,7 @@ def _cr_twiddler(self, generator=None): edge = RisingEdge(self._clk) - # Actual thread + # Actual thread while True: on,off = self._generator.next() self._signal <= 1 @@ -92,7 +93,7 @@ def __init__(self): # Subclasses may already set up logging if not hasattr(self, "log"): - self.log = logging.getLogger("cocotb.driver.%s" % (self.__class__.__name__)) + self.log = SimLog("cocotb.driver.%s" % (self.__class__.__name__)) # Create an independent coroutine which can send stuff self._thread = cocotb.scheduler.add(self._send_thread()) @@ -187,7 +188,7 @@ def __init__(self, entity, name, clock): clock (SimHandle) : A handle to the clock associated with this bus """ - self.log = logging.getLogger("cocotb.%s.%s" % (entity.name, name)) + self.log = SimLog("cocotb.%s.%s" % (entity.name, name)) Driver.__init__(self) self.entity = entity self.name = name diff --git a/cocotb/generators/feeds/__init__.py b/cocotb/generators/feeds/__init__.py index 7bc7df90..58cf8850 100644 --- a/cocotb/generators/feeds/__init__.py +++ b/cocotb/generators/feeds/__init__.py @@ -29,6 +29,7 @@ logging.getLogger("scapy").setLevel(1) import cocotb +from cocotb.log import SimLog from scapy.all import * class Feed(object): @@ -37,7 +38,7 @@ def __init__(self, name, filepath=None): self._packets = {} self._filepath = filepath self.fullname = '\'' + self.name + '\'' - self.log = logging.getLogger('cocotb.' + self.name) + self.log = SimLog('cocotb.' + self.name) if self._filepath: self._source = open(self._filepath) self.log.debug("loaded file %s" % self._filepath) diff --git a/cocotb/handle.py b/cocotb/handle.py index 0b5ab52d..3d130323 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -33,6 +33,7 @@ import simulator import cocotb from cocotb.binary import BinaryValue +from cocotb.log import SimLog class SimHandle(object): @@ -47,7 +48,7 @@ def __init__(self, handle): self.name = simulator.get_name_string(self._handle) self.fullname = self.name + '(%s)' % simulator.get_type_string(self._handle) - self.log = logging.getLogger('cocotb.' + self.name) + self.log = SimLog('cocotb.' + self.name, "0x%x" % id(self)) self.log.debug("Created!") def __str__(self): diff --git a/cocotb/log.py b/cocotb/log.py index 4f252f3e..2ed15578 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -28,11 +28,12 @@ """ import os +import sys import logging - import simulator import cocotb.ANSI as ANSI +from pdb import set_trace # Column alignment _LEVEL_CHARS = len("CRITICAL") @@ -41,6 +42,47 @@ _LINENO_CHARS = 4 _FUNCNAME_CHARS = 31 +class SimBaseLog(logging.getLoggerClass()): + def __init__(self, name): + hdlr = logging.StreamHandler(sys.stdout) + hdlr.setFormatter(SimLogFormatter()) + self.name = name + self.handlers = [] + self.disabled = False + self.filters = [] + self.propagate = False + logging.__init__(name) + self.addHandler(hdlr) + self.setLevel(logging.INFO) + +""" Need to play with this to get the path of the called back, + construct our own makeRecord for this """ + +class SimLog(): + def __init__(self, name, ident=None): + self._ident = ident + self.logger = logging.getLogger(name) + + def warn(self, msg): + self.logger.warn(self, msg) + + def warning(self, msg): + self.warn(msg) + + def debug(self, msg): + self.logger.debug(self, msg) + + def error(self, msg): + self.logger.error(self, msg) + + def critical(self, msg): + self.logger.critical(self, msg) + + def info(self, msg): + self.logger.info(self, msg) + + def addHandler(self, handler): + self.logger.addHandler(handler) class SimLogFormatter(logging.Formatter): @@ -65,8 +107,8 @@ def rjust(string, chars): if len(string) > chars: return string[:chars-2] + ".." return string.rjust(chars) - if record.args: msg = record.msg % record.args - else: msg = record.msg + if record.args: msg = record.args + else: msg = record.msg msg = SimLogFormatter.loglevel2colour[record.levelno] % msg level = SimLogFormatter.loglevel2colour[record.levelno] % \ diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index d7e02a1a..c1b43b74 100644 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -33,7 +33,6 @@ transactions """ -import logging import math import cocotb @@ -41,8 +40,7 @@ from cocotb.triggers import Edge, Event, RisingEdge, ReadOnly from cocotb.binary import BinaryValue from cocotb.bus import Bus - - +from cocotb.log import SimLog class MonitorStatistics(object): @@ -70,7 +68,7 @@ def __init__(self, callback=None, event=None): # Subclasses may already set up logging if not hasattr(self, "log"): - self.log = logging.getLogger("cocotb.monitor.%s" % (self.__class__.__name__)) + self.log = SimLog("cocotb.monitor.%s" % (self.__class__.__name__)) if callback is not None: self.add_callback(callback) @@ -126,7 +124,7 @@ class BusMonitor(Monitor): Wrapper providing common functionality for monitoring busses """ def __init__(self, entity, name, clock, callback=None, event=None): - self.log = logging.getLogger("cocotb.%s.%s" % (entity.name, name)) + self.log = SimLog("cocotb.%s.%s" % (entity.name, name)) self.entity = entity self.name = name self.clock = clock diff --git a/cocotb/regression.py b/cocotb/regression.py index 9dcd6045..c3f4d77e 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -30,14 +30,12 @@ import time import logging - import simulator import cocotb -from cocotb.triggers import NullTrigger - import cocotb.ANSI as ANSI - +from cocotb.log import SimLog +from cocotb.triggers import NullTrigger from cocotb.result import TestError, TestFailure, TestSuccess def _my_import(name): @@ -63,7 +61,7 @@ def __init__(self, dut, modules, tests=None): self._modules = modules self._functions = tests self._running_test = None - self.log = logging.getLogger("cocotb.regression") + self.log = SimLog("cocotb.regression", id(self)) def initialise(self): @@ -93,7 +91,7 @@ def initialise(self): try: self._queue.append(thing(self._dut)) except TestError as result: - self.log.warning("Skipping test %s" % thing.name) + self.log.warn("Skipping test %s" % thing.name) xml += xunit_output(thing.name, module_name, 0.0, skipped=True, error=result.stderr.getvalue()) continue self.ntests += 1 @@ -118,6 +116,7 @@ def tear_down(self): self._fout.close() self.log.info("Shutting down...") simulator.stop_simulator() + self.hplog.close() def next_test(self): """Get the next test to run""" @@ -150,10 +149,10 @@ def handle_result(self, result): self._running_test.module, time.time() - self._running_test.start_time, failure=msg)) - self.log.warning("Test Failed: %s (result was %s)" % ( + self.log.warn("Test Failed: %s (result was %s)" % ( self._running_test.funcname, result.__class__.__name__)) - self.execute() + self.execute() def execute(self): self._running_test = cocotb.regression.next_test() diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 4acbcb3f..91b2d9fd 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -32,20 +32,19 @@ import threading import collections import os -import logging import simulator import cocotb import cocotb.decorators from cocotb.triggers import Trigger, Timer, ReadOnly, NextTimeStep, ReadWrite, NullTrigger - +from cocotb.log import SimLog from cocotb.result import TestComplete, TestError class Scheduler(object): def __init__(self): self.waiting = collections.defaultdict(list) - self.log = logging.getLogger("cocotb.scheduler") + self.log = SimLog("cocotb.scheduler") self.writes = {} self.writes_lock = threading.RLock() self._remove = [] diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index cee34901..95b7eb62 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -28,11 +28,10 @@ """ Common scoreboarding capability. """ -import logging import cocotb from cocotb.utils import hexdump, hexdiffs - +from cocotb.log import SimLog from cocotb.monitors import Monitor from cocotb.result import TestFailure @@ -51,7 +50,7 @@ class Scoreboard(object): def __init__(self, dut, reorder_depth=0): self.dut = dut - self.log = logging.getLogger("cocotb.scoreboard.%s" % self.dut.name) + self.log = SimLog("cocotb.scoreboard.%s" % self.dut.name) self.errors = 0 def add_interface(self, monitor, expected_output): diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 33a60a42..2177f19f 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -26,8 +26,9 @@ """ A collections of triggers which a testbench can 'yield' """ -import logging import simulator +import cocotb +from cocotb.log import SimLog @@ -37,14 +38,13 @@ class TriggerException(Exception): class Trigger(object): """Base class to derive from""" def __init__(self): - self.log = logging.getLogger("cocotb.%s.0x%x" % (self.__class__.__name__, id(self))) + self.log = SimLog("cocotb.%s" % (self.__class__.__name__), "0x%x" % id(self)) self.peers = [] self.signal = None def unprime(self): """Remove any pending callbacks if necessary""" - if self.peers: - self.peers = None + self.peers = [] def addpeers(self, peers): """Store any relate triggers""" diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index f0c82164..1a4ebe72 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -106,7 +106,7 @@ void embed_sim_init(void) return; } - +#if 0 // Extact a reference to the logger object to unitfy logging mechanism pLogger = PyObject_GetAttrString(pModule, "log"); PyObject *pHandler= PyObject_GetAttrString(pLogger, "handle"); // New reference @@ -130,6 +130,7 @@ void embed_sim_init(void) Py_DECREF(pHandler); Py_DECREF(pRecordFn); Py_DECREF(pFilterFn); +#endif // Save a handle to the lock object lock = PyObject_GetAttrString(pModule, "_rlock"); From 767fad82ad8ffffc183fe862855f7bc2cd481b1f Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 15 Jul 2013 16:06:46 +0100 Subject: [PATCH 0228/2656] Issue #51: Update to use full64 flag for 64 bit builds --- makefiles/simulators/Makefile.vcs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index 4a940ab8..bbd6723b 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -28,6 +28,10 @@ OBJ_DIR := obj VPI_LIB := vpi +ifeq ($(ARCH),x86_64) +EXTRA_ARGS += -full64 +endif + all: $(OBJ_DIR) sim $(OBJ_DIR): From b98a0a2f03a14eb3c3d9cab37c0564ec9afe90d8 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 15 Jul 2013 16:13:12 +0100 Subject: [PATCH 0229/2656] Issue #61: Add starting value to substring length for pythong 2.6 --- cocotb/binary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 8e0b7b11..f7a88c16 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -105,7 +105,7 @@ def get_buff(self): def set_buff(self, buff): self._str = "" for char in buff: - self._str = "{:08b}".format(ord(char)) + self._str + self._str = "{0:08b}".format(ord(char)) + self._str self._adjust() buff = property(get_buff, set_buff, None, "Access to the value as a buffer") From d3a9b2a2db8f9282cc248ecfb2cb8c022b57cb2d Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 15 Jul 2013 16:15:42 +0100 Subject: [PATCH 0230/2656] Issue #53: cleanup --- cocotb/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 8e1cd08d..dd0fb5cd 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -42,8 +42,6 @@ # Things we want in the cocotb namespace from cocotb.decorators import test, coroutine -from guppy import hpy - # Singleton scheduler instance # NB this cheekily ensures a singleton since we're replacing the reference # so that cocotb.scheduler gives you the singleton instance and not the From e3b482329c0e5ca54a10d946bcc3bd5606fa757d Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 15 Jul 2013 17:50:41 +0100 Subject: [PATCH 0231/2656] Issue #53: Add source of logging back to message --- cocotb/log.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/cocotb/log.py b/cocotb/log.py index 2ed15578..e4941cb7 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -31,6 +31,7 @@ import sys import logging import simulator +import inspect import cocotb.ANSI as ANSI from pdb import set_trace @@ -61,25 +62,40 @@ def __init__(self, name): class SimLog(): def __init__(self, name, ident=None): self._ident = ident + self._name = name self.logger = logging.getLogger(name) + def _makeRecord(self, msg, level): + if self.logger.isEnabledFor(level): + frame = inspect.stack()[2] + info = inspect.getframeinfo(frame[0]) + record = self.logger.makeRecord(self._name, + level, + info.filename, + info.lineno, + msg, + None, + None, + info.function) + self.logger.handle(record) + def warn(self, msg): - self.logger.warn(self, msg) + self._makeRecord(msg, logging.WARNING) def warning(self, msg): - self.warn(msg) + self._makeRecord(msg, logging.WARNING) def debug(self, msg): - self.logger.debug(self, msg) + self._makeRecord(msg, logging.DEBUG) def error(self, msg): - self.logger.error(self, msg) + self._makeRecord(msg, logging.ERROR) def critical(self, msg): - self.logger.critical(self, msg) + self._makeRecord(msg, logging.CRITICAL) def info(self, msg): - self.logger.info(self, msg) + self._makeRecord(msg, logging.INFO) def addHandler(self, handler): self.logger.addHandler(handler) From 1b6203673454067bdfe894c6ffb3a938077678a1 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 15 Jul 2013 17:55:31 +0100 Subject: [PATCH 0232/2656] Cleanup: Remove left over log file operation from debugging --- cocotb/regression.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index c3f4d77e..66d326b8 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -116,7 +116,6 @@ def tear_down(self): self._fout.close() self.log.info("Shutting down...") simulator.stop_simulator() - self.hplog.close() def next_test(self): """Get the next test to run""" From 0e293f35b3e23b4c190b9fafbc913f230aa9de5c Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 15 Jul 2013 18:45:20 +0100 Subject: [PATCH 0233/2656] Issue #60: Return a NULL value if a name is not found and raise TestFailure exception --- cocotb/handle.py | 3 ++- examples/functionality/tests/test_discovery.py | 5 +++++ lib/vpi_shim/gpi_vpi.c | 18 +++++++++++++----- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 3d130323..1525171b 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -34,6 +34,7 @@ import cocotb from cocotb.binary import BinaryValue from cocotb.log import SimLog +from cocotb.result import TestFailure class SimHandle(object): @@ -59,7 +60,7 @@ def __getattr__(self, name): return self._sub_handles[name] new_handle = simulator.get_handle_by_name(self._handle, name) if not new_handle: - raise AttributeError("%s contains no object named %s" % (self.name, name)) + raise TestFailure("%s contains no object named %s" % (self.name, name)) self._sub_handles[name] = SimHandle(new_handle) return self._sub_handles[name] diff --git a/examples/functionality/tests/test_discovery.py b/examples/functionality/tests/test_discovery.py index d7369f2b..20111956 100644 --- a/examples/functionality/tests/test_discovery.py +++ b/examples/functionality/tests/test_discovery.py @@ -9,3 +9,8 @@ def discover_module_values(dut): for thing in dut: thing.log.info("Found something: %s" % thing.fullname) +@cocotb.test(expect_fail=True) +def discover_value_not_in_dut(dut): + """Try and get a value from the DUT that is not there""" + yield Timer(0) + fake_signal = dut.fake_signal diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index f3900011..ca8d151d 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -171,13 +171,18 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) len = strlen(name) + 1; buff = (char *)malloc(len); - if (buff== NULL) { + if (buff == NULL) { LOG_CRITICAL("VPI: Attempting allocate string buffer failed!"); - return; + return NULL; } strncpy(buff, name, len); obj = vpi_handle_by_name(buff, (vpiHandle)(parent->sim_hdl)); + if (!obj) { + LOG_CRITICAL("VPI: Handle not found!"); + return NULL; + } + free(buff); rv = gpi_alloc_handle(); @@ -290,21 +295,24 @@ static char *gpi_copy_name(const char *name) { int len; char *result; + char *null = "NULL"; if (name) len = strlen(name) + 1; else { LOG_CRITICAL("NULL came back from VPI\n"); - len = 20; + len = strlen(null); + name = null; } result = (char *)malloc(len); if (result == NULL) { LOG_CRITICAL("VPI: Attempting allocate string buffer failed!"); - return NULL; + len = strlen(null); + name = null; } - snprintf(result, len, "%s\0", name ? name : "UNKNOWN"); + snprintf(result, len, "%s\0", name); return result; } From dd7df4141ad3a4dd62997679dc3c49e8df5ead8b Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 15 Jul 2013 19:23:30 +0100 Subject: [PATCH 0234/2656] Issue #53: Print id with the source of the message --- cocotb/decorators.py | 2 +- cocotb/handle.py | 2 +- cocotb/log.py | 10 +++++++--- cocotb/triggers.py | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 0a65eaef..cac15556 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -77,7 +77,7 @@ class RunningCoroutine(object): """ def __init__(self, inst, parent): self.__name__ = "%s.0x%x" % (inst.__name__, id(self)) - self.log = SimLog("cocotb.coroutine.%s" % self.__name__, "0x%x" % id(self)) + self.log = SimLog("cocotb.coroutine.%s" % self.__name__, id(self)) self._coro = inst self._finished = False self._callbacks = [] diff --git a/cocotb/handle.py b/cocotb/handle.py index 1525171b..9c5d41c7 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -49,7 +49,7 @@ def __init__(self, handle): self.name = simulator.get_name_string(self._handle) self.fullname = self.name + '(%s)' % simulator.get_type_string(self._handle) - self.log = SimLog('cocotb.' + self.name, "0x%x" % id(self)) + self.log = SimLog('cocotb.' + self.name, id(self)) self.log.debug("Created!") def __str__(self): diff --git a/cocotb/log.py b/cocotb/log.py index e4941cb7..00ed4783 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -54,22 +54,26 @@ def __init__(self, name): self.propagate = False logging.__init__(name) self.addHandler(hdlr) - self.setLevel(logging.INFO) + self.setLevel(logging.DEBUG) """ Need to play with this to get the path of the called back, construct our own makeRecord for this """ -class SimLog(): +class SimLog(object): def __init__(self, name, ident=None): self._ident = ident self._name = name self.logger = logging.getLogger(name) + if self._ident is not None: + self._log_name = "%s.0x%x" % (self._name, self._ident) + else: + self._log_name = name def _makeRecord(self, msg, level): if self.logger.isEnabledFor(level): frame = inspect.stack()[2] info = inspect.getframeinfo(frame[0]) - record = self.logger.makeRecord(self._name, + record = self.logger.makeRecord(self._log_name, level, info.filename, info.lineno, diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 2177f19f..e97474e6 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -38,7 +38,7 @@ class TriggerException(Exception): class Trigger(object): """Base class to derive from""" def __init__(self): - self.log = SimLog("cocotb.%s" % (self.__class__.__name__), "0x%x" % id(self)) + self.log = SimLog("cocotb.%s" % (self.__class__.__name__), id(self)) self.peers = [] self.signal = None From c3d31c42eda048d9f7f63fd735b60d8f3ffdba78 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 16 Jul 2013 09:59:36 +0100 Subject: [PATCH 0235/2656] Closes #25: Use TOPLEVEL environment variable to find root module --- include/gpi.h | 2 +- lib/embed/gpi_embed.c | 8 +++++++- lib/vpi_shim/gpi_vpi.c | 46 ++++++++++++++++++++++++++++++++++++++---- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/include/gpi.h b/include/gpi.h index e3e69b61..d7791f19 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -96,7 +96,7 @@ void gpi_get_sim_time(uint32_t *high, uint32_t *low); // Functions for extracting a gpi_sim_hdl to an object // Returns a handle to the root simulation object -gpi_sim_hdl gpi_get_root_handle(); +gpi_sim_hdl gpi_get_root_handle(const char *name); gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent); // Types that can be passed to the iterator. diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 1a4ebe72..45bceaac 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -86,7 +86,13 @@ void embed_sim_init(void) FENTER // Find the simulation root - gpi_sim_hdl dut = gpi_get_root_handle(); + gpi_sim_hdl dut = gpi_get_root_handle(getenv("TOPLEVEL")); + + if (dut == NULL) + { + fprintf(stderr, "Unable to find root instance!\n"); + gpi_sim_end(); + } PyObject *pName, *pModule, *pDict, *pFunc, *pArgs; PyObject *pValue, *pLogger; diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index ca8d151d..43d88ea1 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -137,27 +137,65 @@ static gpi_sim_hdl gpi_alloc_handle() } // Handle related functions -gpi_sim_hdl gpi_get_root_handle() +/** + * @name Find the root handle + * @brief Find the root handle using a optional name + * + * Get a handle to the root simulator object. This is usually the toplevel. + * + * If no name is defined, we return the first root instance. + * + * If name is provided, we check the name against the available objects until + * we find a match. If no match is found we return NULL + */ +gpi_sim_hdl gpi_get_root_handle(const char* name) { FENTER vpiHandle root; vpiHandle iterator; gpi_sim_hdl rv; + const char* found; + // vpi_iterate with a ref of NULL returns the top level module iterator = vpi_iterate(vpiModule, NULL); - root = vpi_scan(iterator); + + for (root = vpi_scan(iterator); root != NULL; root = vpi_scan(iterator)) { + + if (name == NULL || !strcmp(name, vpi_get_str(vpiFullName, root))) + break; + } + + if (!root) + goto error; // Need to free the iterator if it didn't return NULL - if (root != NULL && !vpi_free_object(iterator)) { + if (!vpi_free_object(iterator)) { LOG_WARN("VPI: Attempting to free root iterator failed!"); } - + rv = gpi_alloc_handle(); rv->sim_hdl = root; FEXIT return rv; + + error: + + fprintf(stderr, "Couldn't find root handle %s\n", name); + + iterator = vpi_iterate(vpiModule, NULL); + + for (root = vpi_scan(iterator); root != NULL; root = vpi_scan(iterator)) { + + fprintf(stderr, "Toplevel instances: %s != %s...\n", name, vpi_get_str(vpiFullName, root)); + + if (name == NULL || !strcmp(name, vpi_get_str(vpiFullName, root))) + break; + } + + FEXIT + return NULL; } gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) From a131fa6060c39e3ffe714af02097e9f8135ea6c4 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 16 Jul 2013 10:23:45 +0100 Subject: [PATCH 0236/2656] Issue #32: Add API to remove handles from simulator --- cocotb/handle.py | 7 +++++++ include/gpi.h | 4 +++- lib/simulator/simulatormodule.c | 12 ++++++++++++ lib/simulator/simulatormodule.h | 2 ++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 9c5d41c7..1c8d9860 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -56,6 +56,9 @@ def __str__(self): return "%s @0x%x" % (self.name, self._handle) def __getattr__(self, name): + """ Query the simulator for a object with the specified name + and cache the result to build a tree + """ if name in self._sub_handles: return self._sub_handles[name] new_handle = simulator.get_handle_by_name(self._handle, name) @@ -156,3 +159,7 @@ def __iter__(self): hdl = SimHandle(thing) self._sub_handles[hdl.name] = hdl yield hdl + def __del__(self): + """Free handle from gpi that was allocated on construction""" + if self.handle is not None: + simulator.free_handle(self.handle) diff --git a/include/gpi.h b/include/gpi.h index d7791f19..9684cc5d 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -95,9 +95,11 @@ void gpi_get_sim_time(uint32_t *high, uint32_t *low); // Functions for extracting a gpi_sim_hdl to an object -// Returns a handle to the root simulation object +// Returns a handle to the root simulation object, +// Should be freed with gpi_free_handle gpi_sim_hdl gpi_get_root_handle(const char *name); gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent); +void gpi_free_handle(gpi_sim_hdl); // Types that can be passed to the iterator. // diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index e3ebe968..397143f2 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -614,6 +614,18 @@ static PyObject *get_sim_time(PyObject *self, PyObject *args) return pTuple; } +static PyObject *free_handle(PyObject *self, PyObject *args) +{ + gpi_sim_hdl hdl; + + if (!PyArg_ParseTuple(args, "l", &hdl)) + return NULL; + + gpi_free_handle(hdl); + + return Py_BuildValue("s", "OK!"); +} + static PyObject *stop_simulator(PyObject *self, PyObject *args) { diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 43dc8cc6..b0a64645 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -74,6 +74,7 @@ static PyObject *get_sim_time(PyObject *self, PyObject *args); static PyObject *deregister_callback(PyObject *self, PyObject *args); static PyObject *remove_callback(PyObject *self, PyObject *args); static PyObject *create_callback(PyObject *self, PyObject *args); +static PyObject *free_handle(PyObject *self, PyObject *args); static PyMethodDef SimulatorMethods[] = { {"log_msg", log_msg, METH_VARARGS, "Log a message"}, @@ -93,6 +94,7 @@ static PyMethodDef SimulatorMethods[] = { {"stop_simulator", stop_simulator, METH_VARARGS, "Instruct the attached simulator to stop"}, {"iterate", iterate, METH_VARARGS, "Get an iterator handle to loop over all members in an object"}, {"next", next, METH_VARARGS, "Get the next object from the iterator"}, + {"free_handle", free_handle, METH_VARARGS, "Free a handle"}, // FIXME METH_NOARGS => initialization from incompatible pointer type {"get_sim_time", get_sim_time, METH_VARARGS, "Get the current simulation time as a float"}, From c4f0c2aa6b67ecd92304b7515732c5849f7fe871 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 16 Jul 2013 10:24:43 +0100 Subject: [PATCH 0237/2656] Cleanup: change exception on handle lookup failure to TestError --- cocotb/handle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 1c8d9860..1b3e2008 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -34,7 +34,7 @@ import cocotb from cocotb.binary import BinaryValue from cocotb.log import SimLog -from cocotb.result import TestFailure +from cocotb.result import TestError class SimHandle(object): @@ -63,7 +63,7 @@ def __getattr__(self, name): return self._sub_handles[name] new_handle = simulator.get_handle_by_name(self._handle, name) if not new_handle: - raise TestFailure("%s contains no object named %s" % (self.name, name)) + raise TestError("%s contains no object named %s" % (self.name, name)) self._sub_handles[name] = SimHandle(new_handle) return self._sub_handles[name] From ddc2cec1fc2cd30268a9c54a235920690cde93f3 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 16 Jul 2013 10:25:09 +0100 Subject: [PATCH 0238/2656] Cleanup: Set logging level back to default of logging.INFO --- cocotb/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/log.py b/cocotb/log.py index 00ed4783..397de755 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -54,7 +54,7 @@ def __init__(self, name): self.propagate = False logging.__init__(name) self.addHandler(hdlr) - self.setLevel(logging.DEBUG) + self.setLevel(logging.INFO) """ Need to play with this to get the path of the called back, construct our own makeRecord for this """ From 5235bf6442c24252aa46ea685fbaf3fc21ec4ecf Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 16 Jul 2013 10:48:29 +0100 Subject: [PATCH 0239/2656] Fixes #62: Access indices using __getitem__ on SimHandle --- cocotb/handle.py | 10 ++++++++++ include/gpi.h | 1 + lib/simulator/simulatormodule.c | 15 +++++++++++++++ lib/simulator/simulatormodule.h | 2 ++ lib/vpi_shim/gpi_vpi.c | 28 ++++++++++++++++++++++++++++ 5 files changed, 56 insertions(+) diff --git a/cocotb/handle.py b/cocotb/handle.py index 1b3e2008..6f8d5e7a 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -67,6 +67,16 @@ def __getattr__(self, name): self._sub_handles[name] = SimHandle(new_handle) return self._sub_handles[name] + def __getitem__(self, index): + if index in self._sub_handles: + return self._sub_handles[index] + new_handle = simulator.get_handle_by_index(self._handle, index) + if not new_handle: + raise TestError("%s contains no object at index %d" % (self.name, index)) + self._sub_handles[index] = SimHandle(new_handle) + return self._sub_handles[index] + + def getvalue(self): result = BinaryValue() result.binstr = self._get_value_str() diff --git a/include/gpi.h b/include/gpi.h index 9684cc5d..44872325 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -99,6 +99,7 @@ void gpi_get_sim_time(uint32_t *high, uint32_t *low); // Should be freed with gpi_free_handle gpi_sim_hdl gpi_get_root_handle(const char *name); gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent); +gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index); void gpi_free_handle(gpi_sim_hdl); // Types that can be passed to the iterator. diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 397143f2..7c4a88f6 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -562,6 +562,21 @@ static PyObject *get_handle_by_name(PyObject *self, PyObject *args) return Py_BuildValue("l", result); } +static PyObject *get_handle_by_index(PyObject *self, PyObject *args) +{ + uint32_t index; + gpi_sim_hdl hdl; + gpi_sim_hdl result; + + if (!PyArg_ParseTuple(args, "li", &hdl, &index)) + return NULL; + + result = gpi_get_handle_by_index((gpi_sim_hdl)hdl, index); + + return Py_BuildValue("l", result); +} + + static PyObject *get_name_string(PyObject *self, PyObject *args) { diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index b0a64645..67e1f2c8 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -56,6 +56,7 @@ static PyObject *get_signal_val(PyObject *self, PyObject *args); static PyObject *set_signal_val(PyObject *self, PyObject *args); static PyObject *set_signal_val_str(PyObject *self, PyObject *args); static PyObject *get_handle_by_name(PyObject *self, PyObject *args); +static PyObject *get_handle_by_index(PyObject *self, PyObject *args); static PyObject *get_name_string(PyObject *self, PyObject *args); static PyObject *get_type_string(PyObject *self, PyObject *args); static PyObject *register_timed_callback(PyObject *self, PyObject *args); @@ -82,6 +83,7 @@ static PyMethodDef SimulatorMethods[] = { {"set_signal_val", set_signal_val, METH_VARARGS, "Set the value of a signal"}, {"set_signal_val_str", set_signal_val_str, METH_VARARGS, "Set the value of a signal using a binary string"}, {"get_handle_by_name", get_handle_by_name, METH_VARARGS, "Get handle of a named object"}, + {"get_handle_by_index", get_handle_by_index, METH_VARARGS, "Get handle of a object at an index in a parent"}, {"get_name_string", get_name_string, METH_VARARGS, "Get the name of an object"}, {"get_type_string", get_type_string, METH_VARARGS, "Get the type of an object"}, {"register_timed_callback", register_timed_callback, METH_VARARGS, "Register a timed callback"}, diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 43d88ea1..17d2da18 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -230,6 +230,34 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) return rv; } +/** + * @brief Get a handle for an object based on its index within a parent + * + * @param parent handle to the parent + * @param indext Index to retrieve + * + * Can be used on bit-vectors to access a specific bit or + * memories to access an address + */ +gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) +{ + FENTER + gpi_sim_hdl rv; + vpiHandle obj; + + obj = vpi_handle_by_index((vpiHandle)(parent->sim_hdl), index); + if (!obj) { + LOG_CRITICAL("VPI: Handle not found!"); + return NULL; + } + + rv = gpi_alloc_handle(); + rv->sim_hdl = obj; + + FEXIT + return rv; +} + // Functions for iterating over entries of a handle // Returns an iterator handle which can then be used in gpi_next calls From 09581b57f7990dcbfaab3ad29248bc7c121a75f2 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 16 Jul 2013 11:29:20 +0100 Subject: [PATCH 0240/2656] Issue #53: Remove id from name of coroutine --- cocotb/decorators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index cac15556..5e7431a1 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -76,7 +76,7 @@ class RunningCoroutine(object): coro.kill() will destroy a coroutine instance (and cause any Join triggers to fire """ def __init__(self, inst, parent): - self.__name__ = "%s.0x%x" % (inst.__name__, id(self)) + self.__name__ = "%s" % inst.__name__ self.log = SimLog("cocotb.coroutine.%s" % self.__name__, id(self)) self._coro = inst self._finished = False From 808940be1689bcbefedf48667b09545dfd50937b Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 16 Jul 2013 14:59:07 +0100 Subject: [PATCH 0241/2656] Fixes #63: Use vpiInertialDelay instead of vpiNoDelay on assigments --- include/vpi_user.h | 1 + lib/vpi_shim/gpi_vpi.c | 14 +++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/include/vpi_user.h b/include/vpi_user.h index 65475393..7ff95eee 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -51,6 +51,7 @@ typedef uint32_t *vpiHandle; #define vpiFullName 3 /* full hierarchical name */ #define vpiNoDelay 1 +#define vpiInertialDelay 2 typedef struct t_vpi_time { diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 17d2da18..2497fc5c 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -315,11 +315,15 @@ void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) value_p->value.integer = value; value_p->format = vpiIntVal; - /* vpiNoDelay -- Set the value immediately. The p_vpi_time parameter - * may be NULL, in this case. This is like a blocking assignment - * in behavioral code. - */ - vpi_put_value((vpiHandle)(gpi_hdl->sim_hdl), value_p, NULL, vpiNoDelay); + s_vpi_time vpi_time_s; + p_vpi_time vpi_time_p = &vpi_time_s; + + vpi_time_p->type = vpiSimTime; + vpi_time_p->high = 0; + vpi_time_p->low = 0; + + // Use Inertial delay to schedule an event, thus behaving like a verilog testbench + vpi_put_value((vpiHandle)(gpi_hdl->sim_hdl), value_p, vpi_time_p, vpiInertialDelay); FEXIT } From cc502109a603e07db750b874bd23c7146e7a2e23 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 16 Jul 2013 15:07:47 +0100 Subject: [PATCH 0242/2656] Issue #53: Clear down the callback list for a co-routine, self rference was keeping objects around --- cocotb/decorators.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 5e7431a1..683aaf82 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -125,6 +125,7 @@ def _finished_cb(self): self.log.debug("Coroutine finished calling pending callbacks (%d pending)" % len(self._callbacks)) for cb in self._callbacks: cb() + self._callbacks = [] def join(self): """Return a trigger that will fire when the wrapped coroutine exits""" From 5d4d957a8d1fd04c67a91f499f4d00bf3625667f Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 16 Jul 2013 15:27:32 +0100 Subject: [PATCH 0243/2656] Fixes #13: Seed the Python random module --- cocotb/__init__.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index dd0fb5cd..9074b73a 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -32,7 +32,9 @@ import sys import logging import threading -from functools import wraps +import random +import time + import cocotb.handle from cocotb.scheduler import Scheduler @@ -78,6 +80,16 @@ def _initialise_testbench(root_handle): """ _rlock.acquire() + # Seed the Python random number generator to make this repeatable + seed = os.getenv('RANDOM_SEED') + if seed is None: + seed = int(time.time()) + log.info("Seeding Python random module with %d" % (seed)) + else: + seed = int(seed) + log.info("Seeding Python random module with supplied seed %d" % (seed)) + random.seed(seed) + # Create the base handle type dut = cocotb.handle.SimHandle(root_handle) module_str = os.getenv('MODULE') From e8d54dd129b7856daba3614002dba12424f03f1e Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 16 Jul 2013 16:21:58 +0100 Subject: [PATCH 0244/2656] Issue #60: Improve error reporting on attempt to access non-existent signals --- cocotb/handle.py | 17 ++++++++++-- .../functionality/tests/test_discovery.py | 27 +++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 6f8d5e7a..c0f566bc 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -29,6 +29,9 @@ import logging import ctypes +import traceback +import sys +from StringIO import StringIO import simulator import cocotb @@ -63,16 +66,26 @@ def __getattr__(self, name): return self._sub_handles[name] new_handle = simulator.get_handle_by_name(self._handle, name) if not new_handle: - raise TestError("%s contains no object named %s" % (self.name, name)) + self._raise_testerror("%s contains no object named %s" % (self.name, name)) self._sub_handles[name] = SimHandle(new_handle) return self._sub_handles[name] + def _raise_testerror(self, msg): + buff = StringIO() + lastframe = sys._getframe(2) + traceback.print_stack(lastframe, file=buff) + self.log.error("%s\n%s" % (msg, buff.getvalue())) + exception = TestError(msg) + exception.stderr.write(buff.getvalue()) + buff.close() + raise exception + def __getitem__(self, index): if index in self._sub_handles: return self._sub_handles[index] new_handle = simulator.get_handle_by_index(self._handle, index) if not new_handle: - raise TestError("%s contains no object at index %d" % (self.name, index)) + self._raise_testerror("%s contains no object at index %d" % (self.name, index)) self._sub_handles[index] = SimHandle(new_handle) return self._sub_handles[index] diff --git a/examples/functionality/tests/test_discovery.py b/examples/functionality/tests/test_discovery.py index 20111956..4d188e9a 100644 --- a/examples/functionality/tests/test_discovery.py +++ b/examples/functionality/tests/test_discovery.py @@ -14,3 +14,30 @@ def discover_value_not_in_dut(dut): """Try and get a value from the DUT that is not there""" yield Timer(0) fake_signal = dut.fake_signal + yield Timer(0) + + +@cocotb.test() +def access_single_bit(dut): + """Access a single bit in a vector of the dut""" + # FIXME this test fails on Icarus but works on VCS + dut.stream_in_data <= 0 + yield Timer(10) + dut.log.info("%s = %d bits" % (str(dut.stream_in_data), len(dut.stream_in_data))) + dut.stream_in_data[2] <= 1 + yield Timer(10) + if dut.stream_out_data_comb.value.value != (1<<2): + raise TestError("%s.%s != %d" % + (str(dut.stream_out_data_comb), + dut.stream_out_data_comb.value.value, (1<<2))) + + + +@cocotb.test(expect_fail=True) +def access_single_bit_erroneous(dut): + """Access a non-existent single bit""" + yield Timer(10) + dut.log.info("%s = %d bits" % (str(dut.stream_in_data), len(dut.stream_in_data))) + bit = len(dut.stream_in_data) + 4 + dut.stream_in_data[bit] <= 1 + yield Timer(10) From 1e4d0fb0f62f925f68008c8597c3027990f9e26c Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 16 Jul 2013 16:51:19 +0100 Subject: [PATCH 0245/2656] Cleanup: Escape XML output for Jenkins --- cocotb/regression.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 66d326b8..c30fefd7 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -29,6 +29,7 @@ import time import logging +from xml.sax.saxutils import escape import simulator @@ -205,10 +206,10 @@ def xunit_output(name, classname, time, skipped=False, failure="", error=""): if failure: xml += " %s\n \n" % \ - failure + escape(failure) if error: xml += " %s\n \n" % \ - error + escape(error) return xml + " \n" From de3bcb2bb05d6cf28f94431248b3ce84945f7895 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 16 Jul 2013 17:01:14 +0100 Subject: [PATCH 0246/2656] Issue #9: Start documenting simulator support --- documentation/source/index.rst | 1 + documentation/source/simulator_support.rst | 30 ++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 documentation/source/simulator_support.rst diff --git a/documentation/source/index.rst b/documentation/source/index.rst index 62f6b0d9..da4b1dce 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -17,6 +17,7 @@ Contents: coroutines triggers roadmap + simulator_support diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst new file mode 100644 index 00000000..a4491324 --- /dev/null +++ b/documentation/source/simulator_support.rst @@ -0,0 +1,30 @@ +################# +Simulator Support +################# + +This page documents any known quirks and gotchas in the various simulators. + +Icarus +------ + +Accessing bits of a vector doesn't work: + +.. code-block:: python + + dut.stream_in_data[2] <= 1 + +See "access_single_bit" test in examples/functionality/tests/test_discovery.py. + + +Synopsys VCS +------------ + +Aldec Riviera-PRO +----------------- + +Mentor Questa +------------- + +Cadance Incisive +---------------- + From 86eec59338c0414928e4db5e0ac81f53ec351326 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 16 Jul 2013 17:13:19 +0100 Subject: [PATCH 0247/2656] Issue #25: Pass TOPLEVEL variable from Makefiles into sim environment --- makefiles/simulators/Makefile.aldec | 2 +- makefiles/simulators/Makefile.icarus | 4 ++-- makefiles/simulators/Makefile.ius | 4 ++-- makefiles/simulators/Makefile.questa | 4 ++-- makefiles/simulators/Makefile.vcs | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 822d3b70..7cb8949e 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -88,7 +88,7 @@ endif sim: $(SIM_DIR)/runsim.tcl core -cd $(SIM_DIR) && ln -sf $(VPI_FILE) libcocotb.so cd $(SIM_DIR) && PYTHONPATH=$(LIB_DIR):$(PWD)/testbench:$(SIM_ROOT)/sim_core:$(PWD):$(PYTHONPATH) \ - LD_LIBRARY_PATH=$(LD_LIBRARY_PATH):$(VSIMSABIN)/Linux64 MODULE=$(MODULE) TESTCASE=$(TESTCASE) $(CMD) -do runsim.tcl | tee sim.log + LD_LIBRARY_PATH=$(LD_LIBRARY_PATH):$(VSIMSABIN)/Linux64 MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do runsim.tcl | tee sim.log clean:: rm -rf $(SIM_DIR) diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index 7163bfeb..2cf8586b 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -37,12 +37,12 @@ $(OBJ_DIR): .PHONY: sim sim: $(OBJ_DIR) $(VERILOG_SOURCES) iverilog -o $(OBJ_DIR)/sim.vvp $(VERILOG_SOURCES) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp .PHONY: gdb gdb: $(OBJ_DIR) $(VERILOG_SOURCES) iverilog -o $(OBJ_DIR)/sim.vvp $(VERILOG_SOURCES) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ gdb --args vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 5ab98a74..de2aa8bb 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -36,11 +36,11 @@ $(OBJ_DIR): .PHONY: sim sim: $(OBJ_DIR) $(VERILOG_SOURCES) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ irun -sv $(EXTRA_ARGS) -64bit -loadvpi $(BUILD_DIR)/libgpi:vlog_startup_routines_bootstrap +access+rwc $(VERILOG_SOURCES) dve: $(OBJ_DIR) $(VERILOG_SOURCES) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ irun -sv $(EXTRA_ARGS) -64bit -loadvpi $(BUILD_DIR)/libgpi:vlog_startup_routines_bootstrap +access+rwc $(VER clean: -@rm -rf $(OBJ_DIR) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 5eb766ba..aeb37e34 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -12,11 +12,11 @@ $(OBJ_DIR): .PHONY: sim sim: $(OBJ_DIR) $(VERILOG_SOURCES) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(PYTHON_LIBDIR):$(PYTHON_DYNLIBDIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(PYTHON_LIBDIR):$(PYTHON_DYNLIBDIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ qverilog -64 -R -pli $(VPI_FILE) - +acc $(EXTRA_ARGS) $(VERILOG_SOURCES) dve: $(OBJ_DIR) $(VERILOG_SOURCES) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ qverilog -64 -R -pli $(VPI_FILE) - +acc $(EXTRA_ARGS) $(VERILOG_SOURCES) clean: diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index bbd6723b..0ef322d7 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -48,11 +48,11 @@ pli.tab : .PHONY: sim sim: $(OBJ_DIR) $(VERILOG_SOURCES) pli.tab - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ vcs -R +acc+1 +vpi -P pli.tab -sverilog $(EXTRA_ARGS) -debug -load libgpi.so $(VERILOG_SOURCES) dve: $(OBJ_DIR) $(VERILOG_SOURCES) pli.tab - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ vcs -R +acc+1 +vpi+1+assertion -P pli.tab -sverilog $(EXTRA_ARGS) -gui -load libgpi.so $(VERILOG_SOURCES) clean: -@rm -rf $(OBJ_DIR) From 5f074ada3bcaeae3700afc29b614019e84673505 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 17 Jul 2013 10:56:31 +0100 Subject: [PATCH 0248/2656] Issue #23: Don't print ANSI colours to non TTYs --- cocotb/log.py | 73 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 23 deletions(-) diff --git a/cocotb/log.py b/cocotb/log.py index 397de755..63f36cba 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -30,8 +30,8 @@ import os import sys import logging -import simulator import inspect +import simulator import cocotb.ANSI as ANSI from pdb import set_trace @@ -46,7 +46,10 @@ class SimBaseLog(logging.getLoggerClass()): def __init__(self, name): hdlr = logging.StreamHandler(sys.stdout) - hdlr.setFormatter(SimLogFormatter()) + if sys.stdout.isatty(): + hdlr.setFormatter(SimColourLogFormatter()) + else: + hdlr.setFormatter(SimLogFormatter()) self.name = name self.handlers = [] self.disabled = False @@ -104,8 +107,49 @@ def info(self, msg): def addHandler(self, handler): self.logger.addHandler(handler) + class SimLogFormatter(logging.Formatter): + """Log formatter to provide consistent log message handling.""" + + # Justify and truncate + @staticmethod + def ljust(string, chars): + if len(string) > chars: return string[:chars-2] + ".." + return string.ljust(chars) + + @staticmethod + def rjust(string, chars): + if len(string) > chars: return string[:chars-2] + ".." + return string.rjust(chars) + + def _format(self, timeh, timel, level, record, msg): + simtime = "% 6d.%02dns" % ((timel/1000), (timel%1000)/10) + prefix = simtime + ' ' + level + ' ' + \ + self.ljust(record.name, _RECORD_CHARS) + \ + self.rjust(os.path.split(record.filename)[1], _FILENAME_CHARS) + \ + ':' + self.ljust(str(record.lineno), _LINENO_CHARS) + \ + ' in ' + self.ljust(str(record.funcName), _FUNCNAME_CHARS) + ' ' + + pad = "\n" + " " * (len(prefix) - 10) + return prefix + pad.join(msg.split('\n')) + + + def format(self, record): + """pretify the log output, annotate with simulation time""" + if record.args: msg = record.msg % record.args + else: msg = record.msg + + msg = str(msg) + level = record.levelname.ljust(_LEVEL_CHARS) + timeh, timel = simulator.get_sim_time() + + return self._format(timeh, timel, level, record, msg) + + + +class SimColourLogFormatter(SimLogFormatter): + """Log formatter to provide consistent log message handling.""" loglevel2colour = { logging.DEBUG : "%s", @@ -118,29 +162,12 @@ class SimLogFormatter(logging.Formatter): def format(self, record): """pretify the log output, annotate with simulation time""" - # Justify and truncate - def ljust(string, chars): - if len(string) > chars: return string[:chars-2] + ".." - return string.ljust(chars) - - def rjust(string, chars): - if len(string) > chars: return string[:chars-2] + ".." - return string.rjust(chars) - - if record.args: msg = record.args + if record.args: msg = record.msg % record.args else: msg = record.msg - msg = SimLogFormatter.loglevel2colour[record.levelno] % msg - level = SimLogFormatter.loglevel2colour[record.levelno] % \ + msg = SimColourLogFormatter.loglevel2colour[record.levelno] % msg + level = SimColourLogFormatter.loglevel2colour[record.levelno] % \ record.levelname.ljust(_LEVEL_CHARS) timeh, timel = simulator.get_sim_time() - simtime = "% 6d.%02dns" % ((timel/1000), (timel%1000)/10) - - prefix = simtime + ' ' + level + ljust(record.name, _RECORD_CHARS) + \ - rjust(os.path.split(record.filename)[1], _FILENAME_CHARS) + \ - ':' + ljust(str(record.lineno), _LINENO_CHARS) + \ - ' in ' + ljust(str(record.funcName), _FUNCNAME_CHARS) + ' ' - - pad = "\n" + " " * (len(prefix) - 10) - return prefix + pad.join(msg.split('\n')) + return self._format(timeh, timel, level, record, msg) From 8fea6d93b69cf9e734d322272849aaf4aa3c5a99 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 17 Jul 2013 11:36:06 +0100 Subject: [PATCH 0249/2656] Fixed #12: Refactor logging to not use a filter but check level --- cocotb/__init__.py | 4 +- cocotb/log.py | 23 ++++++++ lib/embed/gpi_embed.c | 117 ++++++++++++++++++++++---------------- lib/gpi/gpi_logging.c | 129 ++++++++++++++++++------------------------ 4 files changed, 149 insertions(+), 124 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index dd0fb5cd..1e9bfbb0 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -47,10 +47,10 @@ # so that cocotb.scheduler gives you the singleton instance and not the # scheduler package -# Top level logger object +# GPI logging instance logging.basicConfig() logging.setLoggerClass(SimBaseLog) -log = SimLog('cocotb') +log = SimLog('cocotb.gpi') scheduler = Scheduler() regression = None diff --git a/cocotb/log.py b/cocotb/log.py index 397de755..a3bd42cf 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -83,6 +83,29 @@ def _makeRecord(self, msg, level): info.function) self.logger.handle(record) + def _willLog(self, level): + """ This is for user from the C world + it allows a check on if the message will + be printed. Saves doing lots of work + for no reason. + """ + return self.logger.isEnabledFor(level) + + def _printRecord(self, level, filename, lineno, msg, function): + """ This is for use from the C world and will + be printed regardless + """ + if self.logger.isEnabledFor(level): + record = self.logger.makeRecord(self._log_name, + level, + filename, + lineno, + msg, + None, + None, + function) + self.logger.handle(record) + def warn(self, msg): self._makeRecord(msg, logging.WARNING) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 45bceaac..6e18324f 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -81,6 +81,23 @@ void embed_init_python(void) * * Loads the Python module called cocotb and calls the _initialise_testbench function */ + +#define COCOTB_MODULE "cocotb" + +int get_module_ref(const char *modname, PyObject **mod) +{ + PyObject *pModule = PyImport_Import(PyString_FromString(modname)); + + if (pModule == NULL) { + PyErr_Print(); + fprintf(stderr, "Failed to load \"%s\"\n", modname); + return -1; + } + + *mod = pModule; + return 0; +} + void embed_sim_init(void) { FENTER @@ -88,94 +105,98 @@ void embed_sim_init(void) // Find the simulation root gpi_sim_hdl dut = gpi_get_root_handle(getenv("TOPLEVEL")); - if (dut == NULL) - { + if (dut == NULL) { fprintf(stderr, "Unable to find root instance!\n"); gpi_sim_end(); } - PyObject *pName, *pModule, *pDict, *pFunc, *pArgs; - PyObject *pValue, *pLogger; + PyObject *cocotb_module, *cocotb_init, *cocotb_args, *cocotb_retval; + PyObject *simlog_class, *simlog_obj, *simlog_args, *simlog_func; //Ensure that the current thread is ready to callthe Python C API PyGILState_STATE gstate = PyGILState_Ensure(); - // Python allowed - pModule = PyImport_Import(PyString_FromString("cocotb")); + if (get_module_ref(COCOTB_MODULE, &cocotb_module)) + goto cleanup; - if (pModule == NULL) - { + // Create a logger object + simlog_obj = PyObject_GetAttrString(cocotb_module, "log"); + + if (simlog_obj == NULL) { PyErr_Print(); - fprintf(stderr, "Failed to load \"%s\"\n", "cocotb"); - PyGILState_Release(gstate); - return; + fprintf(stderr, "Failed to to get simlog object\n"); } -#if 0 - // Extact a reference to the logger object to unitfy logging mechanism - pLogger = PyObject_GetAttrString(pModule, "log"); - PyObject *pHandler= PyObject_GetAttrString(pLogger, "handle"); // New reference - PyObject *pRecordFn= PyObject_GetAttrString(pLogger, "makeRecord"); - PyObject *pFilterFn= PyObject_GetAttrString(pLogger, "filter"); + simlog_func = PyObject_GetAttrString(simlog_obj, "_printRecord"); + if (simlog_func == NULL) { + PyErr_Print(); + fprintf(stderr, "Failed to get the _printRecord method"); + goto cleanup; + } - if (pLogger == NULL || pHandler == NULL || pRecordFn == NULL) - { + if (!PyCallable_Check(simlog_func)) { PyErr_Print(); - fprintf(stderr, "Failed to find handle to logging object \"log\" from module cocotb\n"); - PyGILState_Release(gstate); - return; + fprintf(stderr, "_printRecord is not callable"); + goto cleanup; } - set_log_handler(pHandler); - set_make_record(pRecordFn); - set_log_filter(pFilterFn); + set_log_handler(simlog_func); + + Py_DECREF(simlog_func); + simlog_func = PyObject_GetAttrString(simlog_obj, "_willLog"); + if (simlog_func == NULL) { + PyErr_Print(); + fprintf(stderr, "Failed to get the _willLog method"); + goto cleanup; + } + + if (!PyCallable_Check(simlog_func)) { + PyErr_Print(); + fprintf(stderr, "_willLog is not callable"); + goto cleanup; + } - Py_DECREF(pLogger); - Py_DECREF(pHandler); - Py_DECREF(pRecordFn); - Py_DECREF(pFilterFn); -#endif + set_log_filter(simlog_func); + + // Now that logging has been set up ok we initialise the testbench // Save a handle to the lock object - lock = PyObject_GetAttrString(pModule, "_rlock"); + lock = PyObject_GetAttrString(cocotb_module, "_rlock"); LOG_INFO("Python interpreter initialised and cocotb loaded!"); - pFunc = PyObject_GetAttrString(pModule, "_initialise_testbench"); // New reference + cocotb_init = PyObject_GetAttrString(cocotb_module, "_initialise_testbench"); // New reference - if (pFunc == NULL || !PyCallable_Check(pFunc)) - { + if (cocotb_init == NULL || !PyCallable_Check(cocotb_init)) { if (PyErr_Occurred()) PyErr_Print(); fprintf(stderr, "Cannot find function \"%s\"\n", "_initialise_testbench"); - Py_DECREF(pFunc); - Py_DECREF(pModule); - PyGILState_Release(gstate); - return; + Py_DECREF(cocotb_init); + goto cleanup; } - pArgs = PyTuple_New(1); - PyTuple_SetItem(pArgs, 0, PyLong_FromLong((long)dut)); // Note: This function “steals†a reference to o. - pValue = PyObject_CallObject(pFunc, pArgs); + cocotb_args = PyTuple_New(1); + PyTuple_SetItem(cocotb_args, 0, PyLong_FromLong((long)dut)); // Note: This function “steals†a reference to o. + cocotb_retval = PyObject_CallObject(cocotb_init, cocotb_args); - if (pValue != NULL) - { + if (cocotb_retval != NULL) { LOG_INFO("_initialise_testbench successful"); - Py_DECREF(pValue); + Py_DECREF(cocotb_retval); } else { PyErr_Print(); fprintf(stderr,"Call failed\n"); gpi_sim_end(); + goto cleanup; } - Py_DECREF(pFunc); - Py_DECREF(pModule); + FEXIT +cleanup: + if (cocotb_module) + Py_DECREF(cocotb_module); PyGILState_Release(gstate); - - FEXIT } void embed_sim_end(void) diff --git a/lib/gpi/gpi_logging.c b/lib/gpi/gpi_logging.c index 1832b6bd..d6701082 100644 --- a/lib/gpi/gpi_logging.c +++ b/lib/gpi/gpi_logging.c @@ -30,7 +30,6 @@ // Used to log using the standard python mechanism static PyObject *pLogHandler; static PyObject *pLogFilter; -static PyObject *pLogMakeRecord; void set_log_handler(void *handler) { @@ -38,12 +37,6 @@ void set_log_handler(void *handler) Py_INCREF(pLogHandler); } -void set_make_record(void *makerecord) -{ - pLogMakeRecord = (PyObject *)makerecord; - Py_INCREF(pLogMakeRecord); -} - void set_log_filter(void *filter) { pLogFilter = (PyObject *)filter; @@ -79,11 +72,16 @@ const char *log_level(long level) return str; } - +// We keep this module global to avoid reallocation +// we do not need to worry about locking here as +// are single threaded and can not have to calls +// into gpi_log at once. +#define LOG_SIZE 512 +static char log_buff[LOG_SIZE]; /** * @name GPI logging - * @brief Write a log message using Python logging module + * @brief Write a log message using cocotb SimLog class * @ingroup python_c_api * * GILState before calling: Unknown @@ -94,79 +92,62 @@ const char *log_level(long level) * * If the Python logging mechanism is not initialised, dumps to stderr. * - * TODO: correct handling of VARARGS for format strings */ void gpi_log(const char *name, long level, const char *pathname, const char *funcname, long lineno, const char *msg, ...) { + /* We first check that the log level means this will be printed + * before going to the expense of processing the variable + * arguments + */ + va_list ap; + int n; + int curr_level; + + if (!pLogHandler) { + if (level >= 20) { + va_start(ap, msg); + n = vsnprintf(log_buff, LOG_SIZE, msg, ap); + va_end(ap); + + fprintf(stdout, " -.--ns "); + fprintf(stdout, "%-8s", log_level(level)); + fprintf(stdout, "%-35s", name); + fprintf(stdout, "%20s:", pathname); + fprintf(stdout, "%-4ld", lineno); + fprintf(stdout, " in %-31s ", funcname); + fprintf(stdout, "%s", msg); + fprintf(stdout, "\n"); + } + return; + } - if (pLogMakeRecord != NULL && pLogHandler != NULL && pLogFilter != NULL) { - - //Ensure that the current thread is ready to callthe Python C API - PyGILState_STATE gstate = PyGILState_Ensure(); - - if (PyCallable_Check(pLogMakeRecord) && PyCallable_Check(pLogHandler)) { - PyObject *pArgs = PyTuple_New(7); - PyTuple_SetItem(pArgs, 0, PyString_FromString(name)); // Note: This function “steals†a reference to o. - PyTuple_SetItem(pArgs, 1, PyInt_FromLong(level)); // Note: This function “steals†a reference to o. - PyTuple_SetItem(pArgs, 2, PyString_FromString(pathname)); // Note: This function “steals†a reference to o. - PyTuple_SetItem(pArgs, 3, PyInt_FromLong(lineno)); // Note: This function “steals†a reference to o. - PyTuple_SetItem(pArgs, 4, PyString_FromString(msg)); // Note: This function “steals†a reference to o. - PyTuple_SetItem(pArgs, 5, Py_None); //NONE - PyTuple_SetItem(pArgs, 6, Py_None); //NONE - - Py_INCREF(Py_None); // Need to provide a reference to steal - Py_INCREF(Py_None); // Need to provide a reference to steal - - PyObject *pDict; - pDict = Py_BuildValue("{s:s}", "func", funcname); - - PyObject *pLogRecord = PyObject_Call(pLogMakeRecord, pArgs, pDict); - Py_DECREF(pArgs); - Py_DECREF(pDict); - - PyObject *pLogArgs = PyTuple_Pack(1, pLogRecord); - Py_DECREF(pLogRecord); - - // Filter here -#ifdef FILTER - PyObject *pShouldFilter = PyObject_CallObject(pLogFilter, pLogArgs); - if (pShouldFilter == Py_True) { -#endif - PyObject *pLogResult = PyObject_CallObject(pLogHandler, pLogArgs); - Py_DECREF(pLogResult); -#ifdef FILTER - } + // Ignore truncation + // calling args is level, filename, lineno, msg, function + + PyGILState_STATE gstate = PyGILState_Ensure(); - Py_DECREF(pShouldFilter); -#endif - Py_DECREF(pLogArgs); + PyObject *check_args = PyTuple_New(1); + PyTuple_SetItem(check_args, 0, PyInt_FromLong(level)); + PyObject *retuple = PyObject_CallObject(pLogFilter, check_args); - } else { - PyGILState_Release(gstate); - goto clog; + if (retuple != Py_True) + return; - fprintf(stderr, "ERROR: Unable to log into python - logging functions aren't callable\n"); - fprintf(stderr, "%s", msg); - fprintf(stderr, "\n"); + va_start(ap, msg); + n = vsnprintf(log_buff, LOG_SIZE, msg, ap); + va_end(ap); - } + PyObject *call_args = PyTuple_New(5); + PyTuple_SetItem(call_args, 0, PyInt_FromLong(level)); // Note: This function steals a reference. + PyTuple_SetItem(call_args, 1, PyString_FromString(pathname)); // Note: This function steals a reference. + PyTuple_SetItem(call_args, 2, PyInt_FromLong(lineno)); // Note: This function steals a reference. + PyTuple_SetItem(call_args, 3, PyString_FromString(log_buff)); // Note: This function steals a reference. + PyTuple_SetItem(call_args, 4, PyString_FromString(funcname)); - // Matching call to release GIL - PyGILState_Release(gstate); - - // Python logging not available, just dump to stdout (No filtering) - } else { -clog: - if (level < 20) return; - fprintf(stdout, " -.--ns "); - fprintf(stdout, "%-8s", log_level(level)); - fprintf(stdout, "%-35s", name); - fprintf(stdout, "%20s:", pathname); - fprintf(stdout, "%-4ld", lineno); - fprintf(stdout, " in %-31s ", funcname); - fprintf(stdout, "%s", msg); - fprintf(stdout, "\n"); - } + PyObject_CallObject(pLogHandler, call_args); + Py_DECREF(call_args); + Py_DECREF(retuple); + Py_DECREF(check_args); - return; + PyGILState_Release(gstate); } From 4441827fd8ef4c7440dade98fbbb59f33b4c3681 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 17 Jul 2013 11:43:28 +0100 Subject: [PATCH 0250/2656] Cleanup: Increase warning levels in build and fix up files --- include/gpi.h | 2 +- include/vpi_user.h | 2 +- lib/embed/gpi_embed.c | 2 +- lib/vpi_shim/gpi_vpi.c | 10 +++++----- makefiles/Makefile.inc | 4 +++- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/include/gpi.h b/include/gpi.h index 44872325..2522ee79 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -87,7 +87,7 @@ typedef struct __gpi_iterator_hdl *gpi_iterator_hdl; // Functions for controlling/querying the simulation state // Stop the simulator -void gpi_sim_end(); +void gpi_sim_end(void); // Returns simulation time as a float. Units are default sim units diff --git a/include/vpi_user.h b/include/vpi_user.h index 7ff95eee..13b7842e 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -209,7 +209,7 @@ extern vpiHandle vpi_handle_by_multi_index(vpiHandle obj, -void (*vlog_startup_routines[])(); +void (*vlog_startup_routines[])(void); #ifdef __cplusplus } diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 6e18324f..c368a0a0 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -32,7 +32,7 @@ static PyThreadState *gtstate; -static char *progname = "cocotb"; +static char progname[] = "cocotb"; static PyObject *thread_dict; static PyObject *lock; diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 2497fc5c..5fc582e5 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -125,7 +125,7 @@ void gpi_free_handle(gpi_sim_hdl gpi_hdl) free(gpi_hdl); } -static gpi_sim_hdl gpi_alloc_handle() +static gpi_sim_hdl gpi_alloc_handle(void) { gpi_sim_hdl new_hdl = calloc(1, sizeof(*new_hdl)); if (!new_hdl) { @@ -365,7 +365,7 @@ static char *gpi_copy_name(const char *name) { int len; char *result; - char *null = "NULL"; + const char null[] = "NULL"; if (name) len = strlen(name) + 1; @@ -901,7 +901,7 @@ void register_final_callback(void) // If the Pything world wants things to shut down then unregister // the callback for end of sim -void gpi_sim_end() +void gpi_sim_end(void) { FENTER @@ -911,7 +911,7 @@ void gpi_sim_end() FEXIT } -void (*vlog_startup_routines[])() = { +void (*vlog_startup_routines[])(void) = { register_embed, register_initial_callback, register_final_callback, @@ -920,7 +920,7 @@ void (*vlog_startup_routines[])() = { // For non-VPI compliant applications that cannot find vlog_startup_routines symbol -void vlog_startup_routines_bootstrap() { +void vlog_startup_routines_bootstrap(void) { void (*routine)(void); int i; routine = vlog_startup_routines[0]; diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 0cb739b5..a4a51d93 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -52,7 +52,9 @@ export INCLUDES := -I$(SIM_ROOT)/include $(PYTHON_INCLUDEDIR) ifeq ($(OS),Darwin) GCC_ARGS := -g -DDEBUG -fpic else -GCC_ARGS := -Werror -g -DDEBUG -fpic +GCC_ARGS := -Werror -Wcast-qual -Wcast-align -Wwrite-strings \ + -Waggregate-return -Wstrict-prototypes \ + -Wno-unused-parameter -fno-common -g -DDEBUG -fpic endif From 9061dcd1c0d35ec2cd02539aa4064a7093b9d29a Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 17 Jul 2013 12:01:55 +0100 Subject: [PATCH 0251/2656] Fixes #12: Fix up warning levels and message now var arg parsing works --- lib/vpi_shim/gpi_vpi.c | 48 ++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 5fc582e5..8e621fc9 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -81,6 +81,31 @@ typedef struct gpi_clock_s { typedef gpi_clock_t *gpi_clock_hdl; +// Add to this over time +static const char * vpi_reason_to_string(int reason) +{ + switch (reason) { + case cbValueChange: + return "cbValueChange"; + case cbAtStartOfSimTime: + return "cbAtStartOfSimTime"; + case cbReadWriteSynch: + return "cbReadWriteSynch"; + case cbReadOnlySynch: + return "cbReadOnlySynch"; + case cbNextSimTime: + return "cbNextSimTime"; + case cbAfterDelay: + return "cbAfterDelay"; + case cbStartOfSimulation: + return "cbStartOfSimulation"; + case cbEndOfSimulation: + return "cbEndOfSimulation"; + default: + return "unknown"; + } +} + static inline int __gpi_register_cb(p_vpi_cb_user_data user, p_cb_data cb_data) { /* If the user data already has a callback handle then deregister @@ -92,7 +117,8 @@ static inline int __gpi_register_cb(p_vpi_cb_user_data user, p_cb_data cb_data) gpi_deregister_callback(&user->gpi_hdl); if (!new_hdl) { - printf("Unable to get a handle %d\n", new_hdl); + LOG_CRITICAL("VPI: Unable to register callback a handle for VPI type %s(%d)\n", + vpi_reason_to_string(cb_data->reason), cb_data->reason); } user->cb_hdl = new_hdl; @@ -104,7 +130,7 @@ static inline p_vpi_cb_user_data __gpi_alloc_user(void) { p_vpi_cb_user_data new_data = calloc(1, sizeof(*new_data)); if (new_data == NULL) { - printf("VPI: Attempting allocate user_data for %s failed!", __func__); + LOG_CRITICAL("VPI: Attempting allocate user_data failed!"); } return new_data; @@ -182,13 +208,13 @@ gpi_sim_hdl gpi_get_root_handle(const char* name) error: - fprintf(stderr, "Couldn't find root handle %s\n", name); + LOG_CRITICAL("VPI: Couldn't find root handle %s\n", name); iterator = vpi_iterate(vpiModule, NULL); for (root = vpi_scan(iterator); root != NULL; root = vpi_scan(iterator)) { - fprintf(stderr, "Toplevel instances: %s != %s...\n", name, vpi_get_str(vpiFullName, root)); + LOG_CRITICAL("VPI: Toplevel instances: %s != %s...\n", name, vpi_get_str(vpiFullName, root)); if (name == NULL || !strcmp(name, vpi_get_str(vpiFullName, root))) break; @@ -217,7 +243,7 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) strncpy(buff, name, len); obj = vpi_handle_by_name(buff, (vpiHandle)(parent->sim_hdl)); if (!obj) { - LOG_CRITICAL("VPI: Handle not found!"); + LOG_ERROR("VPI: Handle '%s' not found!", name); return NULL; } @@ -247,7 +273,7 @@ gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) obj = vpi_handle_by_index((vpiHandle)(parent->sim_hdl), index); if (!obj) { - LOG_CRITICAL("VPI: Handle not found!"); + LOG_ERROR("VPI: Handle idx '%d' not found!", index); return NULL; } @@ -370,7 +396,7 @@ static char *gpi_copy_name(const char *name) if (name) len = strlen(name) + 1; else { - LOG_CRITICAL("NULL came back from VPI\n"); + LOG_CRITICAL("VPI: NULL came back from VPI\n"); len = strlen(null); name = null; } @@ -451,7 +477,7 @@ static int32_t handle_vpi_callback(p_cb_data cb_data) user_data = (p_vpi_cb_user_data)cb_data->user_data; if (!user_data) - printf("USER DATA NULL\n"); + LOG_CRITICAL("VPI: Callback data corrupted"); user_data->state = VPI_PRE_CALL; old_cb = user_data->cb_hdl; @@ -542,7 +568,7 @@ static int gpi_free_one_time(p_vpi_cb_user_data user_data) int rc; vpiHandle cb_hdl = user_data->cb_hdl; if (!cb_hdl) { - printf("VPI: %s passed a NULL pointer\n", __func__); + LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING\n"); exit(1); } @@ -566,7 +592,7 @@ static int gpi_free_recurring(p_vpi_cb_user_data user_data) int rc; vpiHandle cb_hdl = user_data->cb_hdl; if (!cb_hdl) { - printf("VPI: %s passed a NULL pointer\n", __func__); + printf("VPI: %s passed a NULL pointer : ABORTING"); exit(1); } @@ -834,7 +860,7 @@ gpi_sim_hdl gpi_clock_register(gpi_sim_hdl sim_hdl, int period, unsigned int cyc gpi_clock_hdl hdl = malloc(sizeof(gpi_clock_t)); if (!hdl) - LOG_WARN("Unable to allocate memory"); + LOG_CRITICAL("VPI: Unable to allocate memory"); hdl->period = period; hdl->value = 0; From e994dfb0b216951256f6927f0d4497108537ae59 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 17 Jul 2013 15:20:42 +0100 Subject: [PATCH 0252/2656] Cleanup: Fix memory leak introduced by Issue #12 --- lib/gpi/gpi_logging.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/gpi/gpi_logging.c b/lib/gpi/gpi_logging.c index d6701082..ab6d6f0e 100644 --- a/lib/gpi/gpi_logging.c +++ b/lib/gpi/gpi_logging.c @@ -123,15 +123,20 @@ void gpi_log(const char *name, long level, const char *pathname, const char *fun // Ignore truncation // calling args is level, filename, lineno, msg, function - + // PyGILState_STATE gstate = PyGILState_Ensure(); PyObject *check_args = PyTuple_New(1); PyTuple_SetItem(check_args, 0, PyInt_FromLong(level)); PyObject *retuple = PyObject_CallObject(pLogFilter, check_args); - if (retuple != Py_True) + if (retuple != Py_True) { + Py_DECREF(retuple); + Py_DECREF(check_args); return; + } + + Py_DECREF(retuple); va_start(ap, msg); n = vsnprintf(log_buff, LOG_SIZE, msg, ap); @@ -144,7 +149,7 @@ void gpi_log(const char *name, long level, const char *pathname, const char *fun PyTuple_SetItem(call_args, 3, PyString_FromString(log_buff)); // Note: This function steals a reference. PyTuple_SetItem(call_args, 4, PyString_FromString(funcname)); - PyObject_CallObject(pLogHandler, call_args); + retuple = PyObject_CallObject(pLogHandler, call_args); Py_DECREF(call_args); Py_DECREF(retuple); Py_DECREF(check_args); From 7152b037055f6d307fefe3cc90e21cf561b26238 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 17 Jul 2013 16:16:35 +0100 Subject: [PATCH 0253/2656] Cleanup: Use correct string for logging from C when python not available --- lib/gpi/gpi_logging.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gpi/gpi_logging.c b/lib/gpi/gpi_logging.c index ab6d6f0e..5a177286 100644 --- a/lib/gpi/gpi_logging.c +++ b/lib/gpi/gpi_logging.c @@ -115,7 +115,7 @@ void gpi_log(const char *name, long level, const char *pathname, const char *fun fprintf(stdout, "%20s:", pathname); fprintf(stdout, "%-4ld", lineno); fprintf(stdout, " in %-31s ", funcname); - fprintf(stdout, "%s", msg); + fprintf(stdout, "%s", log_buff); fprintf(stdout, "\n"); } return; From 1bbb2dca51c6eec90b42781561b0a3df5af41301 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 17 Jul 2013 16:17:11 +0100 Subject: [PATCH 0254/2656] Issue #50: Add ARCH to obj path for concurrent builds --- lib/embed/Makefile | 1 - lib/gpi/Makefile | 1 - lib/simulator/Makefile | 2 +- lib/vpi_shim/Makefile | 2 +- makefiles/Makefile.inc | 2 ++ 5 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/embed/Makefile b/lib/embed/Makefile index 6aa3818e..11619366 100644 --- a/lib/embed/Makefile +++ b/lib/embed/Makefile @@ -27,7 +27,6 @@ include $(SIM_ROOT)/makefiles/Makefile.inc -OBJ_DIR := obj INCLUDES += GCC_ARGS += LIBS := $(PYLIBS) diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index 9259544e..9d0f17cf 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -28,7 +28,6 @@ include $(SIM_ROOT)/makefiles/Makefile.inc -OBJ_DIR := obj INCLUDES += GCC_ARGS += -DFILTER diff --git a/lib/simulator/Makefile b/lib/simulator/Makefile index 833798d2..b9e17604 100644 --- a/lib/simulator/Makefile +++ b/lib/simulator/Makefile @@ -26,7 +26,7 @@ ############################################################################### include $(SIM_ROOT)/makefiles/Makefile.inc -OBJ_DIR := obj + INCLUDES += GCC_ARGS += LIBS := -lgpi $(PYLIBS) diff --git a/lib/vpi_shim/Makefile b/lib/vpi_shim/Makefile index c1db7fb9..684c4a08 100644 --- a/lib/vpi_shim/Makefile +++ b/lib/vpi_shim/Makefile @@ -26,7 +26,7 @@ ############################################################################### include $(SIM_ROOT)/makefiles/Makefile.inc -OBJ_DIR := obj + INCLUDES += GCC_ARGS += LIBS := -lgpilog -lcocotb diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index a4a51d93..5af12423 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -64,6 +64,8 @@ else LINKER_ARGS := -shared -Xlinker -export-dynamic $(PYLIBS) endif +OBJ_DIR := obj/$(ARCH) + # Disable some inbuild rules %: %.c %: %.o From a263482c7b4a17c6a2cf75861dddd55a1f3e12ba Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 17 Jul 2013 16:52:53 +0100 Subject: [PATCH 0255/2656] Cleanup: Remove deprecated code --- cocotb/regression.py | 1 - cocotb/scheduler.py | 3 +-- cocotb/triggers.py | 21 --------------------- 3 files changed, 1 insertion(+), 24 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index c30fefd7..783d1771 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -36,7 +36,6 @@ import cocotb import cocotb.ANSI as ANSI from cocotb.log import SimLog -from cocotb.triggers import NullTrigger from cocotb.result import TestError, TestFailure, TestSuccess def _my_import(name): diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 91b2d9fd..682d8d0c 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -36,7 +36,7 @@ import simulator import cocotb import cocotb.decorators -from cocotb.triggers import Trigger, Timer, ReadOnly, NextTimeStep, ReadWrite, NullTrigger +from cocotb.triggers import Trigger, Timer, ReadOnly, NextTimeStep, ReadWrite from cocotb.log import SimLog from cocotb.result import TestComplete, TestError @@ -176,7 +176,6 @@ def prune_routines(self): while self._remove: delroutine, cb = self._remove.pop(0) for trigger, waiting in self.waiting.items(): - if isinstance(trigger, NullTrigger): continue for coro in waiting: if coro is delroutine: self.log.debug("Closing %s" % str(coro)) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index e97474e6..a9a20ea0 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -297,24 +297,3 @@ def __str__(self): return self.__class__.__name__ + "(%s)" % self._coroutine.__name__ -class NullTrigger(PythonTrigger): - """ - Don't do anything - """ - def __init__(self): - Trigger.__init__(self) - - - def prime(self, callback): - pass - - -#class WaitException(PythonTriggers): -# """" -# Wait for an exception to be thrown into this coroutine -# """ -# def __init__(self, exception): -# super(self).__init__(self) - - - From fc45a21b4f7bb491768fb28f2c9f7d5a12f4ac5b Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 17 Jul 2013 17:53:54 +0100 Subject: [PATCH 0256/2656] Issue #50: Build two sets of libs for make libs --- Makefile | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 3c800ffc..00a0eab4 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,21 @@ LIBS:= lib/simulator lib/embed lib/vpi_shim lib/gpi .PHONY: $(LIBS) -all: $(LIBS) +libs_native: $(LIBS) + +libs_64_32: + ARCH=i686 make + +inst_64_32: + ARCH=i686 make lib_install + +ifeq ($(ARCH),x86_64) +libs: libs_native libs_64_32 +install_lib: lib_install inst_64_32 +else +libs: libs_native +install_lib: lib_install +endif $(LIBS): dirs $(MAKE) -C $@ @@ -60,10 +74,12 @@ test: $(LIBS) pycode: cp -R $(SIM_ROOT)/cocotb $(FULL_INSTALL_DIR)/ -lib_install: $(LIBS) - @mkdir -p $(FULL_INSTALL_DIR)/lib - @mkdir -p $(FULL_INSTALL_DIR)/bin - @cp -R $(LIB_DIR)/* $(FULL_INSTALL_DIR)/lib +lib_install: + @mkdir -p $(FULL_INSTALL_DIR)/lib/$(ARCH) + @mkdir -p $(FULL_INSTALL_DIR)/bin/$(ARCH) + @cp -R $(LIB_DIR)/* $(FULL_INSTALL_DIR)/lib/$(ARCH) + +common_install: @cp -R bin/cocotbenv.py $(FULL_INSTALL_DIR)/bin/ @cp -R makefiles $(FULL_INSTALL_DIR)/ @rm -rf $(FULL_INSTALL_DIR)/makefiles/Makefile.inc @@ -80,11 +96,12 @@ $(FULL_INSTALL_DIR)/bin/cocotb_uninstall: scripts: $(FULL_INSTALL_DIR)/bin/cocotb_uninstall $(FULL_INSTALL_DIR)/makefiles/Makefile.inc -install: all lib_install pycode scripts +install: install_lib common_install pycode scripts @echo -e "\nInstalled to $(FULL_INSTALL_DIR)" @echo -e "To uninstall run $(FULL_INSTALL_DIR)/bin/cocotb_uninstall\n" help: - @echo -e "\nCoCoTB make help\n\nall\t- Build libaries" + @echo -e "\nCoCoTB make help\n\nall\t- Build libaries for native" + @echo -e "libs\t- Build libs for possible ARCHs" @echo -e "install\t- Build and install libaries to FULL_INSTALL_DIR (default=$(FULL_INSTALL_DIR))" @echo -e "clean\t- Clean the build dir\n\n" From 2d060d706938a341e2bdc1de0767e9ab9413ce6e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 18 Jul 2013 12:06:01 +0100 Subject: [PATCH 0257/2656] Issue #50: Add new script to create files based on install path and update top level Makefile --- Makefile | 20 ++++--------- bin/create_files.py | 71 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 14 deletions(-) create mode 100755 bin/create_files.py diff --git a/Makefile b/Makefile index 00a0eab4..d4c5b3a0 100644 --- a/Makefile +++ b/Makefile @@ -72,31 +72,23 @@ test: $(LIBS) $(MAKE) -C examples pycode: - cp -R $(SIM_ROOT)/cocotb $(FULL_INSTALL_DIR)/ + @cp -R $(SIM_ROOT)/cocotb $(FULL_INSTALL_DIR)/ lib_install: @mkdir -p $(FULL_INSTALL_DIR)/lib/$(ARCH) - @mkdir -p $(FULL_INSTALL_DIR)/bin/$(ARCH) + @mkdir -p $(FULL_INSTALL_DIR)/bin @cp -R $(LIB_DIR)/* $(FULL_INSTALL_DIR)/lib/$(ARCH) common_install: @cp -R bin/cocotbenv.py $(FULL_INSTALL_DIR)/bin/ + @cp -R bin/create_project.py $(FULL_INSTALL_DIR)/bin/ @cp -R makefiles $(FULL_INSTALL_DIR)/ @rm -rf $(FULL_INSTALL_DIR)/makefiles/Makefile.inc -$(FULL_INSTALL_DIR)/makefiles/Makefile.inc: - @echo "export SIM_ROOT:=$(FULL_INSTALL_DIR)" > $@ - @echo "export LIB_DIR=$(FULL_INSTALL_DIR)/lib" >> $@ +create_files: + bin/create_files.py $(FULL_INSTALL_DIR) -$(FULL_INSTALL_DIR)/bin/cocotb_uninstall: - @echo "#!/bin/bash" > bin/cocotb_uninstall - @echo "rm -rf $(FULL_INSTALL_DIR)" >> bin/cocotb_uninstall - install -m 544 bin/cocotb_uninstall $@ - rm -rf bin/cocotb_uninstall - -scripts: $(FULL_INSTALL_DIR)/bin/cocotb_uninstall $(FULL_INSTALL_DIR)/makefiles/Makefile.inc - -install: install_lib common_install pycode scripts +install: install_lib common_install pycode create_files @echo -e "\nInstalled to $(FULL_INSTALL_DIR)" @echo -e "To uninstall run $(FULL_INSTALL_DIR)/bin/cocotb_uninstall\n" diff --git a/bin/create_files.py b/bin/create_files.py new file mode 100755 index 00000000..58518f42 --- /dev/null +++ b/bin/create_files.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" +script takes an input path and creates + Makefile.inc, used to provide common information once installed + cocotb_uninstall, uninstall script +""" +import sys +import platform +from subprocess import call + +def print_make_inc(path): + makefile = open("/tmp/Makefile.inc", "w") + makefile.write("export ARCH:=$(shell uname -m)\n") + makefile.write("export SIM_ROOT:=" + path + "\n") + makefile.write("export LIB_DIR:=" + path + "/lib/$(ARCH)\n") + makefile.close() + +def print_uninstall(path): + uninstall = open("/tmp/cocotb_uninstall", "w") + file_contents = "#!/usr/bin/env python\n" + file_contents = file_contents + "import sys\nfrom subprocess import call\n\n" + file_contents = file_contents + "def remove():\n" + file_contents = file_contents + " print(\"Removing cocotb from " + path + "\")\n" + file_contents = file_contents + " call(\"rm -rf " + path + "\", shell=True)\n\n" + file_contents = file_contents + "if __name__ == \"__main__\":\n" + file_contents = file_contents + " remove()\n" + + uninstall.write(file_contents) + +def print_files(path): + print_make_inc(path) + print_uninstall(path) + + call("install -m 544 /tmp/cocotb_uninstall " + path + "/bin/cocotb_uninstall", shell=True) + call("install -m 666 /tmp/Makefile.inc " + path + "/makefiles/Makefile.inc", shell=True) + +def check_args(args): + if len(args) is not 1: + print "Please specify a path" + return + + print_files(args[0]) + +if __name__ == "__main__": + check_args(sys.argv[1:]) From 17d1955036e91e72de986c08d4812af724fc634a Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 18 Jul 2013 12:21:01 +0100 Subject: [PATCH 0258/2656] Issue #50: Add -m32 if ARCH is 32 bit --- makefiles/Makefile.inc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 5af12423..92bc8f8f 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -55,6 +55,9 @@ else GCC_ARGS := -Werror -Wcast-qual -Wcast-align -Wwrite-strings \ -Waggregate-return -Wstrict-prototypes \ -Wno-unused-parameter -fno-common -g -DDEBUG -fpic +ifeq ($(ARCH),i686) +GCC_ARGS += -m32 +endif endif From 390efeaf360d5235c111eda152c8a9fe9f57eed1 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 18 Jul 2013 17:02:51 +0100 Subject: [PATCH 0259/2656] Issue #50: Build libs on local box from source at run time if required --- Makefile | 45 ++++------------------- examples/Makefile | 3 +- examples/functionality/tests/Makefile | 1 - lib/Makefile | 53 +++++++++++++++++++++++++++ lib/embed/Makefile | 12 +----- lib/gpi/Makefile | 16 +++----- lib/simulator/Makefile | 13 ++----- lib/vpi_shim/Makefile | 12 +----- makefiles/Makefile.inc | 27 ++++++-------- makefiles/Makefile.rules | 40 ++++++++++++++++++++ makefiles/Makefile.sim | 1 + makefiles/simulators/Makefile.aldec | 2 +- makefiles/simulators/Makefile.icarus | 4 +- makefiles/simulators/Makefile.ius | 4 +- makefiles/simulators/Makefile.questa | 4 +- makefiles/simulators/Makefile.vcs | 8 ++-- 16 files changed, 138 insertions(+), 107 deletions(-) create mode 100644 lib/Makefile create mode 100644 makefiles/Makefile.rules diff --git a/Makefile b/Makefile index d4c5b3a0..c272ab86 100644 --- a/Makefile +++ b/Makefile @@ -25,42 +25,13 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -include makefiles/Makefile.inc -include version - -export BUILD_DIR=$(shell pwd)/build - INSTALL_DIR?=/usr/local FULL_INSTALL_DIR=$(INSTALL_DIR)/cocotb-$(VERSION) -LIBS:= lib/simulator lib/embed lib/vpi_shim lib/gpi - -.PHONY: $(LIBS) - -libs_native: $(LIBS) - -libs_64_32: - ARCH=i686 make +all: test -inst_64_32: - ARCH=i686 make lib_install - -ifeq ($(ARCH),x86_64) -libs: libs_native libs_64_32 -install_lib: lib_install inst_64_32 -else -libs: libs_native -install_lib: lib_install -endif - -$(LIBS): dirs - $(MAKE) -C $@ - -lib/vpi_shim: lib/gpi lib/embed -lib/simulator: lib/vpi_shim - -dirs: - @mkdir -p $(LIB_DIR) +include makefiles/Makefile.inc +include version clean: -@rm -rf $(BUILD_DIR) @@ -68,16 +39,16 @@ clean: -@find . -name "*.pyc" | xargs rm -rf -@find . -name "results.xml" | xargs rm -rf -test: $(LIBS) +test: $(MAKE) -C examples pycode: @cp -R $(SIM_ROOT)/cocotb $(FULL_INSTALL_DIR)/ -lib_install: - @mkdir -p $(FULL_INSTALL_DIR)/lib/$(ARCH) +src_install: + @mkdir -p $(FULL_INSTALL_DIR)/lib @mkdir -p $(FULL_INSTALL_DIR)/bin - @cp -R $(LIB_DIR)/* $(FULL_INSTALL_DIR)/lib/$(ARCH) + @cp -R lib/* $(FULL_INSTALL_DIR)/lib/ common_install: @cp -R bin/cocotbenv.py $(FULL_INSTALL_DIR)/bin/ @@ -88,7 +59,7 @@ common_install: create_files: bin/create_files.py $(FULL_INSTALL_DIR) -install: install_lib common_install pycode create_files +install: src_install common_install pycode create_files @echo -e "\nInstalled to $(FULL_INSTALL_DIR)" @echo -e "To uninstall run $(FULL_INSTALL_DIR)/bin/cocotb_uninstall\n" diff --git a/examples/Makefile b/examples/Makefile index d5c7a87b..666970f6 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -28,7 +28,8 @@ SMOKE_TESTS := demo \ - comb_seq + comb_seq \ + functionality/tests .PHONY: $(SMOKE_TESTS) diff --git a/examples/functionality/tests/Makefile b/examples/functionality/tests/Makefile index edd0ef60..b9ec876a 100644 --- a/examples/functionality/tests/Makefile +++ b/examples/functionality/tests/Makefile @@ -33,4 +33,3 @@ MODULE=test_cocotb,test_multiple_modules,test_discovery include ../../../makefiles/Makefile.inc include ../../../makefiles/Makefile.sim - diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 00000000..eca3b6d7 --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,53 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +include $(SIM_ROOT)/makefiles/Makefile.inc + +CLIBS:= $(SIM_ROOT)/lib/simulator \ + $(SIM_ROOT)/lib/embed \ + $(SIM_ROOT)/lib/vpi_shim \ + $(SIM_ROOT)/lib/gpi + +libs_native: $(CLIBS) + +force_32: + ARCH=i686 make + +$(SIM_ROOT)/lib/vpi_shim: $(SIM_ROOT)/lib/embed $(SIM_ROOT)/lib/gpi +$(SIM_ROOT)/lib/simulator: $(SIM_ROOT)/lib/vpi_shim + +$(LIB_OBJ_DIR): + mkdir -p $@ + +$(LIB_DIR): $(LIB_OBJ_DIR) + mkdir -p $@ + +$(CLIBS): $(LIB_DIR) + $(MAKE) -C $@ + +clean_lib: + -@rm -rf $(BUILD_DIR) diff --git a/lib/embed/Makefile b/lib/embed/Makefile index 11619366..5752dc8c 100644 --- a/lib/embed/Makefile +++ b/lib/embed/Makefile @@ -33,20 +33,12 @@ LIBS := $(PYLIBS) LD_PATH := -L$(LIB_DIR) SRCS := gpi_embed.c -OBJS := $(SRCS:%.c=$(OBJ_DIR)/%.o) LIB_NAME := libcocotb.so all: $(LIB_DIR)/$(LIB_NAME) -$(OBJ_DIR)/%.o: %.c - gcc $(GCC_ARGS) -c $(INCLUDES) -o $@ $< - -$(OBJ_DIR): - mkdir -p $(OBJ_DIR) - -$(LIB_DIR)/$(LIB_NAME): $(OBJ_DIR) $(OBJS) - gcc $(GCC_ARGS) $(LINKER_ARGS) -o $@ $(OBJS) $(LIBS) $(LD_PATH) - clean: rm -rf $(OBJ_DIR) + +include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index 9d0f17cf..5565bb12 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -30,20 +30,14 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += GCC_ARGS += -DFILTER - +LIBS := $(PYLIBS) SRCS := gpi_logging.c -OBJS := $(SRCS:%.c=$(OBJ_DIR)/%.o) - -all: $(LIB_DIR)/libgpilog.so -$(OBJ_DIR)/%.o: %.c - gcc $(GCC_ARGS) -c $(INCLUDES) -o $@ $< +LIB_NAME := libgpilog.so -$(OBJ_DIR): - mkdir -p $(OBJ_DIR) - -$(LIB_DIR)/libgpilog.so: $(OBJ_DIR) $(OBJS) - gcc $(GCC_ARGS) $(LINKER_ARGS) -o $@ $(OBJS) +all: $(LIB_DIR)/$(LIB_NAME) clean: rm -rf $(OBJ_DIR) + +include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/lib/simulator/Makefile b/lib/simulator/Makefile index b9e17604..80b37ef3 100644 --- a/lib/simulator/Makefile +++ b/lib/simulator/Makefile @@ -37,19 +37,12 @@ OBJS := $(SRCS:%.c=$(OBJ_DIR)/%.o) LIB_NAME := libsim.so -all : $(LIB_DIR)/simulator.so - -$(OBJ_DIR)/%.o: %.c - gcc $(GCC_ARGS) -c $(INCLUDES) -o $@ $< - -$(OBJ_DIR) : - mkdir -p $(OBJ_DIR) - -$(LIB_DIR)/$(LIB_NAME) : $(OBJ_DIR) $(OBJS) - gcc $(GCC_ARGS) $(LINKER_ARGS) -o $@ $(OBJS) $(LIBS) $(LD_PATH) +all: $(LIB_DIR)/simulator.so $(LIB_DIR)/simulator.so: $(LIB_DIR)/$(LIB_NAME) ln -sf $(LIB_NAME) $@ clean : @rm -rf $(OBJ_DIR) + +include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/lib/vpi_shim/Makefile b/lib/vpi_shim/Makefile index 684c4a08..48f2dcfa 100644 --- a/lib/vpi_shim/Makefile +++ b/lib/vpi_shim/Makefile @@ -33,21 +33,11 @@ LIBS := -lgpilog -lcocotb LD_PATH := -L$(LIB_DIR) SRCS := gpi_vpi.c -OBJS := $(SRCS:%.c=$(OBJ_DIR)/%.o) LIB_NAME := libgpi.so all: $(LIB_DIR)/gpivpi.vpl -$(OBJ_DIR)/%.o: %.c - gcc $(GCC_ARGS) -c $(INCLUDES) -o $@ $< - -$(OBJ_DIR): - mkdir -p $(OBJ_DIR) - -$(LIB_DIR)/$(LIB_NAME): $(OBJ_DIR) $(OBJS) - gcc $(GCC_ARGS) $(LINKER_ARGS) -o $@ $(OBJS) $(LIBS) $(LD_PATH) - # More rules such that this may be needed depending on the requirements of # different simulators, icarus for instance loads a .vpi libraray to use the vpi # inerface at the start of the simulation @@ -56,3 +46,5 @@ $(LIB_DIR)/gpivpi.vpl: $(LIB_DIR)/$(LIB_NAME) clean: rm -rf $(OBJ_DIR) + +include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 92bc8f8f..410b3fb4 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -31,19 +31,22 @@ ifeq ($(SIM_ROOT),) export SIM_ROOT:=$(abspath $(dir $(lastword $(MAKEFILE_LIST)))/..) endif -BUILD_DIR=$(SIM_ROOT)/build +ifeq ($(USER_DIR),) +export USER_DIR:=/tmp/cocotb +endif + +BUILD_DIR=$(USER_DIR)/build +export BUILD_DIR + ARCH?=$(shell uname -m) +export ARCH OS=$(shell uname) export OS include $(SIM_ROOT)/makefiles/Makefile.paths - -export ARCH - include $(SIM_ROOT)/makefiles/Makefile.pylib -export BUILD_DIR export LIB_DIR=$(BUILD_DIR)/libs/$(ARCH) export INCLUDES := -I$(SIM_ROOT)/include $(PYTHON_INCLUDEDIR) @@ -62,17 +65,9 @@ endif ifeq ($(OS),Darwin) -LINKER_ARGS := -shared -undefined suppress -flat_namespace -L$(PYTHON_LIBDIR) $(PYLIBS) +LINKER_ARGS := -shared -undefined suppress -flat_namespace -L$(PYTHON_LIBDIR) else -LINKER_ARGS := -shared -Xlinker -export-dynamic $(PYLIBS) +LINKER_ARGS := -shared -Xlinker -export-dynamic endif -OBJ_DIR := obj/$(ARCH) - -# Disable some inbuild rules -%: %.c -%: %.o - -# Define some of our own -$(OBJ_DIR)/%.o: %.c - gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $@ $< +LIB_OBJ_DIR:= $(BUILD_DIR)/obj/$(ARCH) diff --git a/makefiles/Makefile.rules b/makefiles/Makefile.rules new file mode 100644 index 00000000..24849b7f --- /dev/null +++ b/makefiles/Makefile.rules @@ -0,0 +1,40 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################## + + +# Disable some inbuild rules +%: %.c +%: %.o + +# Define some of our own +OBJS:= $(SRCS:%.c=$(LIB_OBJ_DIR)/%.o) + +$(LIB_OBJ_DIR)/%.o: %.c + gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $@ $< + +$(LIB_DIR)/$(LIB_NAME): $(LIB_OBJ_DIR) $(OBJS) + gcc $(GCC_ARGS) $(LINKER_ARGS) -o $@ $(OBJS) $(LIBS) $(LD_PATH) diff --git a/makefiles/Makefile.sim b/makefiles/Makefile.sim index 10ee47ba..f97c3b2e 100644 --- a/makefiles/Makefile.sim +++ b/makefiles/Makefile.sim @@ -42,4 +42,5 @@ $(error "Couldn't find makefile for simulator: "$(SIM_LOWERCASE)"! Available sim endif include $(SIM_ROOT)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) +include $(SIM_ROOT)/lib/Makefile diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 7cb8949e..1cc8cecb 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -69,7 +69,7 @@ end\n\ endmodule\n" > $@ # Create a TCL script based on the list of $(VERILOG_SOURCES) -$(SIM_DIR)/runsim.tcl : $(VERILOG_SOURCES) $(SIM_DIR) +$(SIM_DIR)/runsim.tcl : $(VERILOG_SOURCES) $(SIM_DIR) libs_native echo "alib work" > $@ echo "set worklib work" >> $@ cd $(SIM_DIR) && create_project.py $(VERILOG_SOURCES) >> runsim.tcl diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index 5895aef1..ba7d7b7c 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -35,13 +35,13 @@ $(OBJ_DIR): mkdir -p $(OBJ_DIR) .PHONY: sim -sim: $(OBJ_DIR) $(VERILOG_SOURCES) +sim: $(OBJ_DIR) $(VERILOG_SOURCES) libs_native iverilog -o $(OBJ_DIR)/sim.vvp $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp $(PLUSARGS) .PHONY: gdb -gdb: $(OBJ_DIR) $(VERILOG_SOURCES) +gdb: $(OBJ_DIR) $(VERILOG_SOURCES) libs_native iverilog -o $(OBJ_DIR)/sim.vvp $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index de2aa8bb..f64b8e2c 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -35,11 +35,11 @@ $(OBJ_DIR): .PHONY: sim -sim: $(OBJ_DIR) $(VERILOG_SOURCES) +sim: $(OBJ_DIR) $(VERILOG_SOURCES) libs_native PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ irun -sv $(EXTRA_ARGS) -64bit -loadvpi $(BUILD_DIR)/libgpi:vlog_startup_routines_bootstrap +access+rwc $(VERILOG_SOURCES) -dve: $(OBJ_DIR) $(VERILOG_SOURCES) +dve: $(OBJ_DIR) $(VERILOG_SOURCES) libs_native PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ irun -sv $(EXTRA_ARGS) -64bit -loadvpi $(BUILD_DIR)/libgpi:vlog_startup_routines_bootstrap +access+rwc $(VER clean: diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index aeb37e34..d33b7cdc 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -11,11 +11,11 @@ $(OBJ_DIR): .PHONY: sim -sim: $(OBJ_DIR) $(VERILOG_SOURCES) +sim: $(OBJ_DIR) $(VERILOG_SOURCES) libs_native PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(PYTHON_LIBDIR):$(PYTHON_DYNLIBDIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ qverilog -64 -R -pli $(VPI_FILE) - +acc $(EXTRA_ARGS) $(VERILOG_SOURCES) -dve: $(OBJ_DIR) $(VERILOG_SOURCES) +dve: $(OBJ_DIR) $(VERILOG_SOURCES) libs_native PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ qverilog -64 -R -pli $(VPI_FILE) - +acc $(EXTRA_ARGS) $(VERILOG_SOURCES) diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index 0ef322d7..b073c5eb 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -32,9 +32,9 @@ ifeq ($(ARCH),x86_64) EXTRA_ARGS += -full64 endif -all: $(OBJ_DIR) sim +all: $(SIM_OBJ_DIR) sim -$(OBJ_DIR): +$(SIM_OBJ_DIR): mkdir -p $(OBJ_DIR) @@ -47,11 +47,11 @@ pli.tab : echo "acc+=rw,wn:*" > $@ .PHONY: sim -sim: $(OBJ_DIR) $(VERILOG_SOURCES) pli.tab +sim: $(OBJ_DIR) $(VERILOG_SOURCES) pli.tab libs_native PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ vcs -R +acc+1 +vpi -P pli.tab -sverilog $(EXTRA_ARGS) -debug -load libgpi.so $(VERILOG_SOURCES) -dve: $(OBJ_DIR) $(VERILOG_SOURCES) pli.tab +dve: $(OBJ_DIR) $(VERILOG_SOURCES) pli.tab libs_native PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ vcs -R +acc+1 +vpi+1+assertion -P pli.tab -sverilog $(EXTRA_ARGS) -gui -load libgpi.so $(VERILOG_SOURCES) clean: From d3238ebee7f8f4da2de15c915fe2e62bf0f216f9 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 18 Jul 2013 18:14:21 +0100 Subject: [PATCH 0260/2656] Issue #50: Some cleanups and path fixes for installation --- Makefile | 3 ++- bin/create_files.py | 3 +-- lib/Makefile | 6 +++--- makefiles/Makefile.inc | 6 +++--- makefiles/simulators/Makefile.vcs | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index c272ab86..a114e44d 100644 --- a/Makefile +++ b/Makefile @@ -48,13 +48,14 @@ pycode: src_install: @mkdir -p $(FULL_INSTALL_DIR)/lib @mkdir -p $(FULL_INSTALL_DIR)/bin + @mkdir -p $(FULL_INSTALL_DIR)/include @cp -R lib/* $(FULL_INSTALL_DIR)/lib/ + @cp -R include/* $(FULL_INSTALL_DIR)/include/ common_install: @cp -R bin/cocotbenv.py $(FULL_INSTALL_DIR)/bin/ @cp -R bin/create_project.py $(FULL_INSTALL_DIR)/bin/ @cp -R makefiles $(FULL_INSTALL_DIR)/ - @rm -rf $(FULL_INSTALL_DIR)/makefiles/Makefile.inc create_files: bin/create_files.py $(FULL_INSTALL_DIR) diff --git a/bin/create_files.py b/bin/create_files.py index 58518f42..0544422e 100755 --- a/bin/create_files.py +++ b/bin/create_files.py @@ -54,11 +54,10 @@ def print_uninstall(path): uninstall.write(file_contents) def print_files(path): - print_make_inc(path) print_uninstall(path) call("install -m 544 /tmp/cocotb_uninstall " + path + "/bin/cocotb_uninstall", shell=True) - call("install -m 666 /tmp/Makefile.inc " + path + "/makefiles/Makefile.inc", shell=True) + call("rm -rf /tmp/cocotb_uninstall", shell=True) def check_args(args): if len(args) is not 1: diff --git a/lib/Makefile b/lib/Makefile index eca3b6d7..a80f86e6 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -27,17 +27,17 @@ include $(SIM_ROOT)/makefiles/Makefile.inc -CLIBS:= $(SIM_ROOT)/lib/simulator \ +CLIBS:= $(SIM_ROOT)/lib/gpi \ $(SIM_ROOT)/lib/embed \ $(SIM_ROOT)/lib/vpi_shim \ - $(SIM_ROOT)/lib/gpi + $(SIM_ROOT)/lib/simulator libs_native: $(CLIBS) force_32: ARCH=i686 make -$(SIM_ROOT)/lib/vpi_shim: $(SIM_ROOT)/lib/embed $(SIM_ROOT)/lib/gpi +$(SIM_ROOT)/lib/vpi_shim: $(SIM_ROOT)/lib/gpi $(SIM_ROOT)/lib/embed $(SIM_ROOT)/lib/simulator: $(SIM_ROOT)/lib/vpi_shim $(LIB_OBJ_DIR): diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 410b3fb4..321a595b 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -32,7 +32,7 @@ export SIM_ROOT:=$(abspath $(dir $(lastword $(MAKEFILE_LIST)))/..) endif ifeq ($(USER_DIR),) -export USER_DIR:=/tmp/cocotb +export USER_DIR:=$(SIM_ROOT) endif BUILD_DIR=$(USER_DIR)/build @@ -48,9 +48,11 @@ include $(SIM_ROOT)/makefiles/Makefile.paths include $(SIM_ROOT)/makefiles/Makefile.pylib export LIB_DIR=$(BUILD_DIR)/libs/$(ARCH) +export LIB_OBJ_DIR:= $(BUILD_DIR)/obj/$(ARCH) export INCLUDES := -I$(SIM_ROOT)/include $(PYTHON_INCLUDEDIR) + # Base GCC flags ifeq ($(OS),Darwin) GCC_ARGS := -g -DDEBUG -fpic @@ -69,5 +71,3 @@ LINKER_ARGS := -shared -undefined suppress -flat_namespace -L$(PYTHON_LIBDIR) else LINKER_ARGS := -shared -Xlinker -export-dynamic endif - -LIB_OBJ_DIR:= $(BUILD_DIR)/obj/$(ARCH) diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index b073c5eb..396df719 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -34,7 +34,7 @@ endif all: $(SIM_OBJ_DIR) sim -$(SIM_OBJ_DIR): +$(OBJ_DIR): mkdir -p $(OBJ_DIR) From 2b6ab5d04978c91c2d1edf52b9b7cc992068d229 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 19 Jul 2013 10:56:46 +0100 Subject: [PATCH 0261/2656] Issue #50: Remove libs rule from Makefile --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index a114e44d..0fcd0305 100644 --- a/Makefile +++ b/Makefile @@ -66,6 +66,5 @@ install: src_install common_install pycode create_files help: @echo -e "\nCoCoTB make help\n\nall\t- Build libaries for native" - @echo -e "libs\t- Build libs for possible ARCHs" @echo -e "install\t- Build and install libaries to FULL_INSTALL_DIR (default=$(FULL_INSTALL_DIR))" @echo -e "clean\t- Clean the build dir\n\n" From 6f5ad56c084e06606176396353e2d2fa3c644636 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 19 Jul 2013 11:29:15 +0100 Subject: [PATCH 0262/2656] Issue #9: Add installation instructions --- documentation/source/installation.rst | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 documentation/source/installation.rst diff --git a/documentation/source/installation.rst b/documentation/source/installation.rst new file mode 100644 index 00000000..9f7ceb19 --- /dev/null +++ b/documentation/source/installation.rst @@ -0,0 +1,6 @@ +############ +Installation +############ + +Get the Source +============== From b456ba1702aa9f76367098377e343d7b8ae9f085 Mon Sep 17 00:00:00 2001 From: stuarthodgson Date: Fri, 19 Jul 2013 12:24:15 +0100 Subject: [PATCH 0263/2656] Update installation.rst --- documentation/source/installation.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/documentation/source/installation.rst b/documentation/source/installation.rst index 9f7ceb19..10a6ebef 100644 --- a/documentation/source/installation.rst +++ b/documentation/source/installation.rst @@ -4,3 +4,21 @@ Installation Get the Source ============== + +Source can be obtained as a tar ball for the current `release `_. + +Or by cloning the repository `git@github.com:potentialventures/cocotb.git` + +There are two supported installation options for Cocotb, standalone or centralised. + +Standalone Usage +================ + +Simply check out the code and hit make at the root of the tree. This will run the test cases and exampkles against `Icarus `_. + +The list of supported simulators for the version you have can be found by *make help*. + +Centralised Usage +================= + +A build can be installed in a centralised location with *make install INSTALL_PATH=". This will also generate and uninstall script. From 91df61523599999ef64fcc739235bf38c4fddb49 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 19 Jul 2013 12:25:03 +0100 Subject: [PATCH 0264/2656] Cleaup: Add list of simulators to make help --- Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Makefile b/Makefile index 0fcd0305..c002b243 100644 --- a/Makefile +++ b/Makefile @@ -68,3 +68,9 @@ help: @echo -e "\nCoCoTB make help\n\nall\t- Build libaries for native" @echo -e "install\t- Build and install libaries to FULL_INSTALL_DIR (default=$(FULL_INSTALL_DIR))" @echo -e "clean\t- Clean the build dir\n\n" + @echo -e "To build natively just run make.\nTo build for 32bit on a 64 bit system set ARCH=i686\n" + @echo -e "Default simulator is Icarus. To use another set environment variable SIM as below\n" + @for X in $(shell ls makefiles/simulators/); do \ + echo $$X | sed 's/^[^.]*./export SIM=/';\ + done + @echo -e "\n\n" From bc4ac597ebe9caf179abb5f2711dd11c67dceb84 Mon Sep 17 00:00:00 2001 From: stuarthodgson Date: Fri, 19 Jul 2013 12:26:11 +0100 Subject: [PATCH 0265/2656] Update introduction.rst --- documentation/source/introduction.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index 9c191122..2e682746 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -17,6 +17,8 @@ What is cocotb? * Mentor Questa * Cadance Incisive +**Cocotb** has recently been developed with the support of `SolarFlare Communications Ltd `_ for use on thier `AOE product line `_. + How is cocotb different? ------------------------ From db59cc32100d2b75f17dc216d7ae4762d5a4a9a6 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 30 Jul 2013 17:54:11 +0100 Subject: [PATCH 0266/2656] Cleanup: Add clean rule to top level --- Makefile | 2 +- examples/Makefile | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5c1802c8..6a95dee0 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,7 @@ clean: $(MAKE) -C examples clean test: - $(MAKE) -C examples + $(MAKE) -k -C examples pycode: @cp -R $(SIM_ROOT)/cocotb $(FULL_INSTALL_DIR)/ diff --git a/examples/Makefile b/examples/Makefile index 666970f6..97aab620 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -29,6 +29,7 @@ SMOKE_TESTS := demo \ comb_seq \ + plusargs \ functionality/tests .PHONY: $(SMOKE_TESTS) @@ -37,3 +38,6 @@ all: $(SMOKE_TESTS) $(SMOKE_TESTS): @cd $@ && $(MAKE) + +clean: + $(foreach TEST, $(SMOKE_TESTS), $(MAKE) -C $(TEST) clean;) From 87f1bda416397249330749528c91e38764cef663 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 31 Jul 2013 16:30:30 -0700 Subject: [PATCH 0267/2656] Issue #7: Add support for telling regression to skip a test --- cocotb/decorators.py | 12 +++++++++++- cocotb/regression.py | 9 ++++++++- cocotb/xunit_reporter.py | 2 +- examples/functionality/tests/test_discovery.py | 10 ++++++++++ 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 683aaf82..1f786a1a 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -156,6 +156,7 @@ def __init__(self, inst, parent): self.started = False self.start_time = 0 self.expect_fail = parent.expect_fail + self.skip = parent.skip self.handler = RunningTest.ErrorLogHandler(self._handle_error_message) cocotb.log.addHandler(self.handler) @@ -219,10 +220,19 @@ class test(coroutine): All tests are coroutines. The test decorator provides some common reporting etc, a test timeout and allows us to mark tests as expected failures. + + KWargs: + timeout: (int) + value representing simulation timeout (not implemented) + expect_fail: (bool): + Don't mark the result as a failure if the test fails + skip: (bool): + Don't execute this test as part of the regression """ - def __init__(self, timeout=None, expect_fail=False): + def __init__(self, timeout=None, expect_fail=False, skip=False): self.timeout = timeout self.expect_fail = expect_fail + self.skip = skip def __call__(self, f): super(test, self).__init__(f) diff --git a/cocotb/regression.py b/cocotb/regression.py index 4d93b667..9f164ee4 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -89,13 +89,20 @@ def initialise(self): for thing in vars(module).values(): if hasattr(thing, "im_test"): try: - self._queue.append(thing(self._dut)) + test = thing(self._dut) except TestError: self.log.warning("Skipping test %s" % thing.name) self.xunit.add_testcase(name=thing.name, classname=module_name, time="0.0") self.xunit.add_skipped() continue + self.ntests += 1 + if test.skip: + self.log.info("Skipping test %s" % thing.name) + self.xunit.add_testcase(name=thing.name, classname=module_name, time="0.0") + self.xunit.add_skipped() + else: + self._queue.append(test) for valid_tests in self._queue: self.log.info("Found test %s.%s" % diff --git a/cocotb/xunit_reporter.py b/cocotb/xunit_reporter.py index 5fa0e7e2..e1db2dc5 100644 --- a/cocotb/xunit_reporter.py +++ b/cocotb/xunit_reporter.py @@ -98,5 +98,5 @@ def indent(self, elem, level=0): def write(self): self.indent(self.results) - ET.ElementTree(self.results).write(self.filename, xml_declaration = True, method = "xml", encoding="UTF-8") + ET.ElementTree(self.results).write(self.filename, encoding="UTF-8") diff --git a/examples/functionality/tests/test_discovery.py b/examples/functionality/tests/test_discovery.py index 4d188e9a..7aae6771 100644 --- a/examples/functionality/tests/test_discovery.py +++ b/examples/functionality/tests/test_discovery.py @@ -41,3 +41,13 @@ def access_single_bit_erroneous(dut): bit = len(dut.stream_in_data) + 4 dut.stream_in_data[bit] <= 1 yield Timer(10) + + +@cocotb.test(skip=True) +def skip_a_test(dut): + """This test shouldn't execute""" + yield Timer(10) + dut.log.info("%s = %d bits" % (str(dut.stream_in_data), len(dut.stream_in_data))) + bit = len(dut.stream_in_data) + 4 + dut.stream_in_data[bit] <= 1 + yield Timer(10) From 18d7671dc33e3388879bc6904355c516f227ca59 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 1 Aug 2013 17:08:45 -0700 Subject: [PATCH 0268/2656] Regression cleanup: Apply stricter rules to checking (expected failures must fail) --- cocotb/regression.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 9f164ee4..d7d679d0 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -129,21 +129,26 @@ def handle_result(self, result): Args: result (TestComplete exception) """ - if isinstance(result, TestSuccess): + self.xunit.add_testcase(name =self._running_test.funcname, + classname=self._running_test.module, + time=repr(time.time() - self._running_test.start_time) ) + + if isinstance(result, TestSuccess) and not self._running_test.expect_fail: self.log.info("Test Passed: %s" % self._running_test.funcname) - elif self._running_test.expect_fail: + elif isinstance(result, TestFailure) and self._running_test.expect_fail: self.log.info("Test failed as expected: %s (result was %s)" % ( self._running_test.funcname, result.__class__.__name__)) - else: - self.log.warn("Test Failed: %s (result was %s)" % ( + + elif isinstance(result, TestSuccess): + self.log.error("Test passed but we expected a failure: %s (result was %s)" % ( self._running_test.funcname, result.__class__.__name__)) + self.xunit.add_failure(stdout=str(result), stderr="\n".join(self._running_test.error_messages)) - self.xunit.add_testcase(name =self._running_test.funcname, - classname=self._running_test.module, - time=repr(time.time() - self._running_test.start_time) ) - if isinstance(result, TestFailure): - self.xunit.add_failure("\n".join(self._running_test.error_messages)) + else: + self.log.error("Test Failed: %s (result was %s)" % ( + self._running_test.funcname, result.__class__.__name__)) + self.xunit.add_failure(stdout=str(result), stderr="\n".join(self._running_test.error_messages)) self.execute() From 7df9e101342bd86226525dc87f9f26dda1abb090 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 1 Aug 2013 17:15:53 -0700 Subject: [PATCH 0269/2656] Issue #65 - expose a $fail_test() system task to mark the current test as a failure --- cocotb/__init__.py | 8 ++++ cocotb/scheduler.py | 13 ++++--- examples/functionality/hdl/sample_module.v | 2 + examples/functionality/tests/test_cocotb.py | 5 +++ include/gpi_logging.h | 10 ++--- include/vpi_user.h | 18 +++++++++ lib/embed/gpi_embed.c | 43 +++++++++++++++++---- lib/vpi_shim/gpi_vpi.c | 34 ++++++++++++++++ 8 files changed, 115 insertions(+), 18 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index bb296ddc..b9685450 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -41,6 +41,7 @@ from cocotb.log import SimLogFormatter, SimBaseLog, SimLog from cocotb.regression import RegressionManager + # Things we want in the cocotb namespace from cocotb.decorators import test, coroutine @@ -116,6 +117,13 @@ def _initialise_testbench(root_handle): return True +def _fail_test(message): + """Function that can be called externally to fail a test""" + from cocotb.result import TestFailure + scheduler.log.error("Failing test at simulator request") + scheduler.finish_test(TestFailure("Failure from external source: %s" % message)) + + def process_plusargs(): global plusargs diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 682d8d0c..f04dc2a8 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -244,16 +244,19 @@ def schedule(self, coroutine, trigger=None): # If we're already tearing down we ignore any further test results # that may be raised. Required because currently Python triggers don't unprime if not self._terminate: - self._terminate = True - self._test_result = test_result - self.cleanup() - self._readonly = self.add(self.move_to_cleanup()) - + self.finish_test(test_result) self.log.warning("Coroutine completed execution with %s: %s" % (test_result.__class__.__name__, str(coroutine))) return coroutine.log.debug("Finished sheduling coroutine (%s)" % str(trigger)) + def finish_test(self, test_result): + if not self._terminate: + self._terminate = True + self._test_result = test_result + self.cleanup() + self._readonly = self.add(self.move_to_cleanup()) + def cleanup(self): """ Clear up all our state diff --git a/examples/functionality/hdl/sample_module.v b/examples/functionality/hdl/sample_module.v index 8a85af2f..0f586f81 100644 --- a/examples/functionality/hdl/sample_module.v +++ b/examples/functionality/hdl/sample_module.v @@ -53,6 +53,8 @@ always @(stream_out_ready) initial begin $dumpfile("waveform.vcd"); $dumpvars(0,sample_module); + + #500000 $fail_test("Test timed out, failing..."); end endmodule diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index b89b9d4c..ef5005fe 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -130,4 +130,9 @@ def test_adding_a_coroutine_without_starting(dut): yield Join(forked) yield Timer(100) +@cocotb.test(expect_fail=True) +def test_failure_from_system_task(dut): + """Allow the dut to call $fail_test() from verilog""" + yield Timer(10000000) + diff --git a/include/gpi_logging.h b/include/gpi_logging.h index 1cdbeeba..44ac3521 100644 --- a/include/gpi_logging.h +++ b/include/gpi_logging.h @@ -53,13 +53,13 @@ enum gpi_log_levels { #define LOG_ERROR(...) gpi_log("cocotb.gpi", GPIError, __FILE__, __func__, __LINE__, __VA_ARGS__); #define LOG_CRITICAL(...) gpi_log("cocotb.gpi", GPICritical, __FILE__, __func__, __LINE__, __VA_ARGS__); -#ifdef DEBUG -#define FENTER LOG_DEBUG(__func__) -#define FEXIT LOG_DEBUG(__func__) -#else +// #ifdef DEBUG +// #define FENTER LOG_DEBUG(__func__) +// #define FEXIT LOG_DEBUG(__func__) +// #else #define FENTER #define FEXIT -#endif +// #endif void set_log_handler(void *handler); void set_make_record(void *makerecord); diff --git a/include/vpi_user.h b/include/vpi_user.h index 377ce470..0ae1d6e9 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -193,6 +193,24 @@ typedef struct t_vpi_error_info int32_t line; } s_vpi_error_info, *p_vpi_error_info; + +typedef struct t_vpi_systf_data { + int32_t type; + int32_t sysfunctype; + const char *tfname; + int32_t (*calltf) (char*); + int32_t (*compiletf)(char*); + int32_t (*sizetf) (char*); + char *user_data; +} s_vpi_systf_data, *p_vpi_systf_data; + +#define vpiSysTask 1 +#define vpiSysFunc 2 +#define vpiIntFunc 1 +#define vpiSysTfCall 85 +#define vpiArgument 89 + + extern vpiHandle vpi_register_cb(p_cb_data cb_data_p); extern int32_t vpi_remove_cb(vpiHandle cb_obj); diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index e7f5b110..063717dc 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -34,8 +34,7 @@ static PyThreadState *gtstate; static char progname[] = "cocotb"; static PyObject *thread_dict; -static PyObject *lock; - +static PyObject *pFailTestFn; /** @@ -103,7 +102,7 @@ void embed_sim_init(gpi_sim_info_t *info) FENTER int i; - + // Find the simulation root gpi_sim_hdl dut = gpi_get_root_handle(getenv("TOPLEVEL")); @@ -153,13 +152,13 @@ void embed_sim_init(gpi_sim_info_t *info) PyErr_Print(); fprintf(stderr, "Failed to get the _willLog method"); goto cleanup; - } + } if (!PyCallable_Check(simlog_func)) { PyErr_Print(); fprintf(stderr, "_willLog is not callable"); goto cleanup; - } + } set_log_filter(simlog_func); @@ -181,11 +180,19 @@ void embed_sim_init(gpi_sim_info_t *info) goto cleanup; } + LOG_INFO("Python interpreter initialised and cocotb loaded!"); + // Now that logging has been set up ok we initialise the testbench - // Save a handle to the lock object - lock = PyObject_GetAttrString(cocotb_module, "_rlock"); - LOG_INFO("Python interpreter initialised and cocotb loaded!"); + // Hold onto a reference to our _fail_test function + pFailTestFn = PyObject_GetAttrString(cocotb_module, "_fail_test"); + + if (!PyCallable_Check(pFailTestFn)) { + PyErr_Print(); + fprintf(stderr, "cocotb._fail_test is not callable"); + goto cleanup; + } + Py_INCREF(pFailTestFn); cocotb_init = PyObject_GetAttrString(cocotb_module, "_initialise_testbench"); // New reference @@ -227,3 +234,23 @@ void embed_sim_end(void) LOG_WARN("Closing down cocotb at simulator request!"); FEXIT } + + +void fail_test(const char *message) +{ + FENTER + + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); + + LOG_WARN("Failing the test at simulator request!"); + + PyObject *fArgs = PyTuple_New(1); + PyTuple_SetItem(fArgs, 0, PyString_FromString(message)); + PyObject *pValue = PyObject_Call(pFailTestFn, fArgs, NULL); + + Py_DECREF(fArgs); + PyGILState_Release(gstate); + + FEXIT +} \ No newline at end of file diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 69038953..b89963c4 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -997,6 +997,39 @@ void register_final_callback(void) FEXIT } + +// System function to permit code in the simulator to fail a test +// TODO: Pass in an error string +static int system_function_fail_test(char *userdata) +{ + + vpiHandle systfref, args_iter, argh; + struct t_vpi_value argval; + + // Obtain a handle to the argument list + systfref = vpi_handle(vpiSysTfCall, NULL); + args_iter = vpi_iterate(vpiArgument, systfref); + + // Grab the value of the first argument + argh = vpi_scan(args_iter); + argval.format = vpiStringVal; + vpi_get_value(argh, &argval); + + fail_test(argval.value.str); + + // Cleanup and return + vpi_free_object(args_iter); + return 0; +} + +void register_system_functions(void) +{ + FENTER + s_vpi_systf_data data = {vpiSysTask, vpiIntFunc, "$fail_test", system_function_fail_test, NULL, NULL, NULL}; + vpi_register_systf(&data); + FEXIT +} + // If the Pything world wants things to shut down then unregister // the callback for end of sim void gpi_sim_end(void) @@ -1011,6 +1044,7 @@ void gpi_sim_end(void) void (*vlog_startup_routines[])(void) = { register_embed, + register_system_functions, register_initial_callback, register_final_callback, 0 From 2eb5f5ba5f72f71e43bcce07baed4c6142e24559 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 2 Aug 2013 16:48:25 +0100 Subject: [PATCH 0270/2656] Issue #58: Initial test case addition --- cocotb/__init__.py | 2 +- cocotb/decorators.py | 35 ++++++++++++++++++++++++++- examples/functionality/tests/Makefile | 2 +- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index bb296ddc..044cd3f4 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -42,7 +42,7 @@ from cocotb.regression import RegressionManager # Things we want in the cocotb namespace -from cocotb.decorators import test, coroutine +from cocotb.decorators import test, coroutine, function # Singleton scheduler instance # NB this cheekily ensures a singleton since we're replacing the reference diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 1f786a1a..7ce9846f 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -27,10 +27,11 @@ import time import logging import traceback +import threading import cocotb from cocotb.log import SimLog -from cocotb.triggers import Join +from cocotb.triggers import Join, PythonTrigger, Timer from cocotb.result import TestComplete, TestError, TestFailure, TestSuccess @@ -213,6 +214,38 @@ def __iter__(self): return self def __str__(self): return str(self._func.__name__) +#@coroutine +def block_handler(block_function, event, *args, **kwargs): + """To get back from the threading context just triggers the event + for the moment + """ + # Bit of a hack here +# yield Timer(0) + event.result = block_function(*args, **kwargs) + event.set() + + +@public +class function(object): + """Decorator class that allows a a function to block + + This allows a function to internally block while + externally appear to yield + + """ + def __init__(self, func): + self._func = func + self.log = SimLog("cocotb.function.%s" % self._func.__name__, id(self)) + + def __call__(self, *args, **kwargs): + self._event = threading.Event() + #cocotb.scheduler.queue(block_handler(self._func, self._event, *args, **kwargs)) + block_handler(self._func, self._event, *args, **kwargs) + self._event.wait() + return self._event.result + + + @public class test(coroutine): """Decorator to mark a fucntion as a test diff --git a/examples/functionality/tests/Makefile b/examples/functionality/tests/Makefile index b9ec876a..0517f5ec 100644 --- a/examples/functionality/tests/Makefile +++ b/examples/functionality/tests/Makefile @@ -29,7 +29,7 @@ TOPLEVEL = sample_module VERILOG_SOURCES = ../hdl/sample_module.v -MODULE=test_cocotb,test_multiple_modules,test_discovery +MODULE=test_cocotb,test_multiple_modules,test_discovery,test_calling include ../../../makefiles/Makefile.inc include ../../../makefiles/Makefile.sim From 51335a998ca3e5d70e1a376477b040606665baef Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 2 Aug 2013 16:54:40 +0100 Subject: [PATCH 0271/2656] Issue #58: Add the test case --- examples/functionality/tests/test_calling.py | 107 +++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 examples/functionality/tests/test_calling.py diff --git a/examples/functionality/tests/test_calling.py b/examples/functionality/tests/test_calling.py new file mode 100644 index 00000000..bb537fe9 --- /dev/null +++ b/examples/functionality/tests/test_calling.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python + +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" +A set of tests that demonstrate cocotb functionality + +Also used a regression test of cocotb capabilities +""" + +import threading +import time +import cocotb +from cocotb.triggers import Timer, Join, RisingEdge, ReadOnly + +signal = None + +# Tests relating to calling convention and operation + +def create_thread(): + new_thread = threading.Thread(group=None, target=blocking_function, name="Test_thread", args=(), kwargs={}) + new_thread.start() + +@cocotb.function +def blocking_function(): + global signal + stime = 2 + print("Blocking for %d seconds then asserting clock" % stime) + time.sleep(stime) + signal <= 1 + print("Block finished") + +clock_count = 0 + +@cocotb.coroutine +def clock_gen(clock): + """Drive the clock signal""" + global clock_count + + for i in range(10000): + clock <= 0 + yield Timer(1000) + clock <= 1 + yield Timer(1000) + clock_count += 1 + + clock.log.warning("Clock generator finished!") + +signal_count = 0 + +@cocotb.coroutine +def signal_monitor(signal): + """Check that the clock is moving and increment + a counter + """ + global signal_count + + yield RisingEdge(signal) + signal_count += 1 + + print("Clock mon exiting") + + +@cocotb.test(expect_fail=False) +def test_callable(dut): + """Test ability to call a blocking function that will block but allow other coroutines to continue + + The test creates another thread that will block for a period of time. This would normally + mean that the simulator could not progress since control would not pass back to the simulator + + In this test the clock driver should continue to be able to toggle pins + we monitor this as well and count that the number of observed transitions matches the number of sets + """ + global clock_count + global signal + signal = dut.stream_in_valid + signal_mon = cocotb.scheduler.add(signal_monitor(signal)) + clk_gen = cocotb.scheduler.add(clock_gen(dut.clk)) + create_thread() + #blocking_function() + yield Timer(1000) + yield Join(signal_mon) + clk_gen.kill() + print("Have had %d transitions" % clock_count) From c248a4b5c84c7c136b1e945fcf4d8e41b4dd6016 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 2 Aug 2013 14:49:57 -0700 Subject: [PATCH 0272/2656] Cleanup: Provide mechanism to query scoreboard at end of test * Tests should fail if any expected data is still pending. --- cocotb/scoreboard.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index 95b7eb62..837d1239 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -33,7 +33,7 @@ from cocotb.utils import hexdump, hexdiffs from cocotb.log import SimLog from cocotb.monitors import Monitor -from cocotb.result import TestFailure +from cocotb.result import TestFailure, TestSuccess class Scoreboard(object): @@ -52,6 +52,23 @@ def __init__(self, dut, reorder_depth=0): self.dut = dut self.log = SimLog("cocotb.scoreboard.%s" % self.dut.name) self.errors = 0 + self.expected = {} + + @property + def result(self): + """determine the test result - do we have any pending data remaining?""" + fail = False + for monitor, expected_output in self.expected.iteritems(): + if callable(expected_output): + self.log.debug("Can't check all data returned for %s since expected output is \ + callable function rather than a list" % str(monitor)) + continue + if len(expected_output): + self.log.warn("Still expecting %d transaction on %s" % (len(expected_output), str(monitor))) + fail = True + if fail: + return TestFailure("Not all expected output was received") + return TestSuccess() def add_interface(self, monitor, expected_output): """Add an interface to be scoreboarded. @@ -61,6 +78,9 @@ def add_interface(self, monitor, expected_output): Simply check against the expected output. """ + # save a handle to the expected output so we can check if all expected data has + # been received at the end of a test. + self.expected[monitor] = expected_output # Enforce some type checking as we only work with a real monitor if not isinstance(monitor, Monitor): @@ -74,7 +94,8 @@ def check_received_transaction(transaction): self.log.error("%s" % (transaction)) # TODO hexdump raise TestFailure("Recieved a transaction but wasn't expecting anything") - if callable(expected_output): exp = expected_output() + if callable(expected_output): + exp = expected_output() else: exp = expected_output.pop(0) if type(transaction) != type(exp): From e45c8e84b4c57252d9b52cc498ff5f34afb4029b Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 2 Aug 2013 16:36:07 -0700 Subject: [PATCH 0273/2656] Cleanup: Python 2.6 support --- cocotb/binary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index f7a88c16..4dc2c720 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -81,7 +81,7 @@ def get_value(self): return int(resolve(self._str), 2) def set_value(self, integer): - self._str = "{:b}".format(integer) + self._str = bin(integer)[2:] self._adjust() value = property(get_value, set_value, None, "Integer access to the value") From 7ce68f1e745b14c1a2e5a07e3ac1845636ea19be Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 2 Aug 2013 16:37:08 -0700 Subject: [PATCH 0274/2656] Cleanup: Provide hasattr() like peek method on handle * avoid printing to the log if we just want to test for existence --- cocotb/bus.py | 17 ++++++++++------- cocotb/handle.py | 6 ++++++ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/cocotb/bus.py b/cocotb/bus.py index ded508e5..7c188a9a 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -30,6 +30,7 @@ A bus is simply defined as a collection of signals """ +from cocotb.result import TestError class Bus(object): """ @@ -68,14 +69,16 @@ def __init__(self, entity, name, signals, optional_signals=[]): # Also support a set of optional signals that don't have to be present for signal in optional_signals: signame = name + "_" + signal - try: + # Attempts to access a signal that doesn't exist will print a + # backtrace so we 'peek' first, slightly un-pythonic + if entity.__hasattr__(signame): hdl = getattr(entity, signame) - except AttributeError: - self._entity.log.debug("Ignoring optional missing signal %s \ - on bus %s" % signal, name) - continue - setattr(self, signal, hdl) - self._signals[signal] = getattr(self, signal) + setattr(self, signal, hdl) + self._signals[signal] = getattr(self, signal) + else: + self._entity.log.info("Ignoring optional missing signal %s on bus %s" + % (signal, name)) + def drive(self, obj, strict=False): """ diff --git a/cocotb/handle.py b/cocotb/handle.py index c0f566bc..70bb4325 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -80,6 +80,12 @@ def _raise_testerror(self, msg): buff.close() raise exception + def __hasattr__(self, name): + """Since calling hasattr(handle, "something") will print out a + backtrace to the log since usually attempting to access a + non-existent member is an error we provide a 'peek function""" + return bool(simulator.get_handle_by_name(self._handle, name)) + def __getitem__(self, index): if index in self._sub_handles: return self._sub_handles[index] From 5f0bd4b1d5fe7b7a0f84c347cb528c4883224e00 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 2 Aug 2013 16:38:38 -0700 Subject: [PATCH 0275/2656] Cleanup: Tweaks to monitors --- cocotb/monitors/__init__.py | 2 ++ cocotb/monitors/avalon.py | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index c1b43b74..1d6ec1f9 100644 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -131,3 +131,5 @@ def __init__(self, entity, name, clock, callback=None, event=None): self.bus = Bus(self.entity, self.name, self._signals) Monitor.__init__(self, callback=callback, event=event) + def __str__(self): + return "%s(%s)" % (self.__class__.__name__, self.name) diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index f94e8ffa..b4777509 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -29,6 +29,7 @@ NB Currently we only support a very small subset of functionality """ +from cocotb.utils import hexdump from cocotb.decorators import coroutine from cocotb.monitors import BusMonitor from cocotb.triggers import RisingEdge, ReadOnly @@ -61,7 +62,8 @@ class AvalonSTPkts(BusMonitor): """ Packetised AvalonST bus """ - _signals = AvalonST._signals + ["startofpacket", "endofpacket", "ready", "empty", "error"] + _signals = ["valid", "data", "startofpacket", "endofpacket", "empty"] + _optional_signals = ["error", "channel", "ready"] @coroutine def _monitor_recv(self): From 34e9c9ca36ea8c13ce7a24e6308f25742b304a21 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 2 Aug 2013 16:44:48 -0700 Subject: [PATCH 0276/2656] Cleanup: Provide option to not fail tests immediately --- cocotb/scoreboard.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index 837d1239..6b69f10a 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -48,11 +48,12 @@ class Scoreboard(object): Statistics for end-of-test summary etc. """ - def __init__(self, dut, reorder_depth=0): + def __init__(self, dut, reorder_depth=0, fail_immediately=True): self.dut = dut self.log = SimLog("cocotb.scoreboard.%s" % self.dut.name) self.errors = 0 self.expected = {} + self._imm = fail_immediately @property def result(self): @@ -64,10 +65,17 @@ def result(self): callable function rather than a list" % str(monitor)) continue if len(expected_output): - self.log.warn("Still expecting %d transaction on %s" % (len(expected_output), str(monitor))) + self.log.warn("Still expecting %d transactions on %s" % (len(expected_output), str(monitor))) + for index, transaction in enumerate(expected_output): + self.log.info("Expecting %d:\n%s" % (index, hexdump(str(transaction)))) + if index > 5: + self.log.info("... and %d more to come" % len(expected_output) - index - 1) + break fail = True if fail: return TestFailure("Not all expected output was received") + if self.errors: + return TestFailure("Errors were recorded during the test") return TestSuccess() def add_interface(self, monitor, expected_output): @@ -92,7 +100,7 @@ def check_received_transaction(transaction): if not expected_output: self.errors += 1 self.log.error("%s" % (transaction)) # TODO hexdump - raise TestFailure("Recieved a transaction but wasn't expecting anything") + if self._imm: raise TestFailure("Recieved a transaction but wasn't expecting anything") if callable(expected_output): exp = expected_output() @@ -102,7 +110,7 @@ def check_received_transaction(transaction): self.errors += 1 self.log.error("Received transaction is a different type to expected transaction") self.log.info("Got: %s but expected %s" % (str(type(transaction)), str(type(exp)))) - raise TestFailure("Received transaction of wrong type") + if self._imm: raise TestFailure("Received transaction of wrong type") if transaction != exp: self.errors += 1 @@ -118,7 +126,7 @@ def check_received_transaction(transaction): for word in transaction: self.log.info(str(word)) except: pass self.log.warning(hexdiffs(exp, transaction)) - raise TestFailure("Received transaction differed from expected transaction") + if self._imm: raise TestFailure("Received transaction differed from expected transaction") else: self.log.debug("Received expected transaction %d bytes" % (len(transaction))) self.log.debug(repr(transaction)) From 3c472a4a5016a1de64d679bef484bb9f51875eca Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 2 Aug 2013 17:12:43 -0700 Subject: [PATCH 0277/2656] Cleanup: Fix bug where we could pop from an empty list --- cocotb/scoreboard.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index 6b69f10a..25298061 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -96,16 +96,15 @@ def add_interface(self, monitor, expected_output): def check_received_transaction(transaction): """Called back by the monitor when a new transaction has been received""" - - if not expected_output: + if callable(expected_output): + exp = expected_output() + elif len(expected_output): + exp = expected_output.pop(0) + else: self.errors += 1 self.log.error("%s" % (transaction)) # TODO hexdump if self._imm: raise TestFailure("Recieved a transaction but wasn't expecting anything") - if callable(expected_output): - exp = expected_output() - else: exp = expected_output.pop(0) - if type(transaction) != type(exp): self.errors += 1 self.log.error("Received transaction is a different type to expected transaction") From 8171201250123ba771ea377fbd5244b2e66481be Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 2 Aug 2013 17:14:34 -0700 Subject: [PATCH 0278/2656] Cleanup: Fix for boneheaded fixes --- cocotb/scoreboard.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index 25298061..7d801c58 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -104,12 +104,14 @@ def check_received_transaction(transaction): self.errors += 1 self.log.error("%s" % (transaction)) # TODO hexdump if self._imm: raise TestFailure("Recieved a transaction but wasn't expecting anything") + return if type(transaction) != type(exp): self.errors += 1 self.log.error("Received transaction is a different type to expected transaction") self.log.info("Got: %s but expected %s" % (str(type(transaction)), str(type(exp)))) if self._imm: raise TestFailure("Received transaction of wrong type") + return if transaction != exp: self.errors += 1 From 659c047abcb3c758859d684abaffbe6687b3c4b2 Mon Sep 17 00:00:00 2001 From: STuart Date: Mon, 5 Aug 2013 20:39:50 +0100 Subject: [PATCH 0279/2656] Cleanup: Add results.xml to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index deedff4e..55b50d24 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ __pycache__ # Waveforms *.vcd + +# Results +results.xml From a8f42c1f17d1fb16903ce57dfe4d27e75f924a48 Mon Sep 17 00:00:00 2001 From: STuart Date: Mon, 5 Aug 2013 22:31:18 +0100 Subject: [PATCH 0280/2656] Add limited(1) vpi object types --- include/vpi_user.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/vpi_user.h b/include/vpi_user.h index 0ae1d6e9..2aebc24b 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -175,6 +175,9 @@ typedef struct t_cb_data #define cbInteractiveScopeChange 23 #define cbUnresolvedSystf 24 +/* Object Types */ +#define vpiPort 44 + /* error severity levels */ #define vpiNotice 1 #define vpiWarning 2 From 41cb09b9d58c25c0facf5b90cb07ae98fec84414 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 6 Aug 2013 00:42:51 +0100 Subject: [PATCH 0281/2656] Regression cleanup: Allow an internal test to be marked as expect_error --- cocotb/decorators.py | 7 ++++++- cocotb/regression.py | 8 ++++++-- examples/functionality/tests/test_cocotb.py | 6 +++--- examples/functionality/tests/test_discovery.py | 4 ++-- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 7ce9846f..5099518e 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -157,6 +157,7 @@ def __init__(self, inst, parent): self.started = False self.start_time = 0 self.expect_fail = parent.expect_fail + self.expect_error = parent.expect_error self.skip = parent.skip self.handler = RunningTest.ErrorLogHandler(self._handle_error_message) @@ -259,12 +260,16 @@ class test(coroutine): value representing simulation timeout (not implemented) expect_fail: (bool): Don't mark the result as a failure if the test fails + expect_error: (bool): + Don't make the result as an error if an error is raised + This is for cocotb internal regression use skip: (bool): Don't execute this test as part of the regression """ - def __init__(self, timeout=None, expect_fail=False, skip=False): + def __init__(self, timeout=None, expect_fail=False, expect_error=False, skip=False): self.timeout = timeout self.expect_fail = expect_fail + self.expect_error = expect_error self.skip = skip def __call__(self, f): diff --git a/cocotb/regression.py b/cocotb/regression.py index d7d679d0..db988863 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -138,13 +138,17 @@ def handle_result(self, result): elif isinstance(result, TestFailure) and self._running_test.expect_fail: self.log.info("Test failed as expected: %s (result was %s)" % ( - self._running_test.funcname, result.__class__.__name__)) + self._running_test.funcname, result.__class__.__name__)) elif isinstance(result, TestSuccess): self.log.error("Test passed but we expected a failure: %s (result was %s)" % ( - self._running_test.funcname, result.__class__.__name__)) + self._running_test.funcname, result.__class__.__name__)) self.xunit.add_failure(stdout=str(result), stderr="\n".join(self._running_test.error_messages)) + elif isinstance(result, TestError) and self._running_test.expect_error: + self.log.info("Test errored as expected: %s (result was %s)" % ( + self._running_test.funcname, result.__class__.__name__)) + else: self.log.error("Test Failed: %s (result was %s)" % ( self._running_test.funcname, result.__class__.__name__)) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index ef5005fe..eaa7d200 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -50,13 +50,13 @@ def function_not_a_coroutine(): """If we don't yield, this isn't a coroutine""" return "This should fail" -@cocotb.test(expect_fail=True) +@cocotb.test(expect_error=True) def test_function_not_a_coroutine(dut): """Example of trying to yield a coroutine that isn't a coroutine""" yield Timer(500) yield function_not_a_coroutine() -@cocotb.test(expect_fail=True) +@cocotb.test(expect_error=True) def test_function_not_a_coroutine_fork(dut): """Example of trying to fork a coroutine that isn't a coroutine""" yield Timer(500) @@ -121,7 +121,7 @@ def test_coroutine_kill(dut): if test_flag is not True: raise cocotb.TestFailed -@cocotb.test(expect_fail=True) +@cocotb.test(expect_error=True) def test_adding_a_coroutine_without_starting(dut): """Catch (and provide useful error) for attempts to fork coroutines incorrectly""" yield Timer(100) diff --git a/examples/functionality/tests/test_discovery.py b/examples/functionality/tests/test_discovery.py index 7aae6771..9772a8ed 100644 --- a/examples/functionality/tests/test_discovery.py +++ b/examples/functionality/tests/test_discovery.py @@ -9,7 +9,7 @@ def discover_module_values(dut): for thing in dut: thing.log.info("Found something: %s" % thing.fullname) -@cocotb.test(expect_fail=True) +@cocotb.test(expect_error=True) def discover_value_not_in_dut(dut): """Try and get a value from the DUT that is not there""" yield Timer(0) @@ -33,7 +33,7 @@ def access_single_bit(dut): -@cocotb.test(expect_fail=True) +@cocotb.test(expect_error=True) def access_single_bit_erroneous(dut): """Access a non-existent single bit""" yield Timer(10) From 8b88aed66a8d09c5d038cc29e09bf0a0e6bfa739 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 6 Aug 2013 01:54:30 +0100 Subject: [PATCH 0282/2656] Issue 66: Bring cocotbenv back up to data and add missing prototype to vpi_user.h --- bin/cocotbenv.py | 6 +++--- include/vpi_user.h | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/bin/cocotbenv.py b/bin/cocotbenv.py index b551e802..697ad32e 100755 --- a/bin/cocotbenv.py +++ b/bin/cocotbenv.py @@ -29,7 +29,7 @@ Determines how to execute the cocotb world """ -import os, sys, inspect +import os, sys, inspect, platform from subprocess import call class SimType(): @@ -40,7 +40,7 @@ def __init__(self, name=None, sdebug=False): def execute(self, cmd): print("Running cocotb against %s simulator" % self._name) print(cmd) - try: + try: call(cmd, shell=True) except KeyboardInterrupt: print("Closing Test Bench") @@ -136,7 +136,7 @@ def main(): # and the arch that it is running on exec_path = inspect.getfile(inspect.currentframe()) base_path = exec_path.split('/bin', 1)[0] - lib_path = base_path + '/build/libs/i686' + lib_path = base_path + '/build/libs/' + platform.machine() py_path = base_path py_path = py_path + ':' + lib_path py_path = py_path + ':' + os.getcwd() diff --git a/include/vpi_user.h b/include/vpi_user.h index 2aebc24b..ba560ad6 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -256,6 +256,8 @@ extern vpiHandle vpi_handle_by_multi_index(vpiHandle obj, extern int32_t vpi_chk_error(p_vpi_error_info); +extern int32_t vpi_get_vlog_info(p_vpi_vlog_info info_p); + extern void (*vlog_startup_routines[])(void); #ifdef __cplusplus From bd72d8060f274d47c849be37fe0d5fab4507f332 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 6 Aug 2013 01:55:33 +0100 Subject: [PATCH 0283/2656] Cleanup: add .tab files to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 55b50d24..5022cdee 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ __pycache__ # Results results.xml + +# VCS files +*.tab From ebf6674b1a71053e04a6a8c81f71779d44ece8a6 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 6 Aug 2013 01:58:16 +0100 Subject: [PATCH 0284/2656] Cleanup: Remove unused function putting requirement on vpi implementations --- lib/vpi_shim/gpi_vpi.c | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index b89963c4..efa07bce 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -501,25 +501,6 @@ char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl) // Callback related functions -// Ask the attached simulator to return the user pointer -// that was given when the callback was registered -// Useful for operating on the data before the callback -// has fired since we only have the handle in hand -static p_vpi_cb_user_data gpi_get_user_data(gpi_sim_hdl hdl) -{ - p_vpi_cb_user_data user_data; - s_cb_data cbdata; - FENTER - - vpi_get_cb_info((vpiHandle)hdl, &cbdata); - check_vpi_error(); - user_data = (p_vpi_cb_user_data)cbdata.user_data; - - FEXIT - return user_data; -} - - static int32_t handle_vpi_callback(p_cb_data cb_data) { FENTER @@ -596,7 +577,7 @@ int gpi_deregister_callback(gpi_sim_hdl gpi_hdl) p_vpi_cb_user_data user_data; int rc = 1; FENTER - // We should be able to user gpi_get_user_data + // We should be able to user vpi_get_cb_info // but this is not implemented in ICARUS // and gets upset on VCS. So instead we // do some pointer magic. From bc77c4d0d3d5ccb3b6c083f70fd1be261d75848d Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 6 Aug 2013 02:16:35 +0100 Subject: [PATCH 0285/2656] Cleanup: Add in some more missing function prototypes --- include/vpi_user.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/vpi_user.h b/include/vpi_user.h index ba560ad6..379f21e2 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -258,6 +258,8 @@ extern int32_t vpi_chk_error(p_vpi_error_info); extern int32_t vpi_get_vlog_info(p_vpi_vlog_info info_p); +extern int32_t vpi_register_systf(p_vpi_systf_data data_p); + extern void (*vlog_startup_routines[])(void); #ifdef __cplusplus From 514043de4e48c36c283f0e3cfe4173cd8003eb34 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 7 Aug 2013 00:58:45 +0100 Subject: [PATCH 0286/2656] Issue #58: Complete the @function decorator and allow it to return a value --- cocotb/decorators.py | 28 ++--- cocotb/log.py | 2 +- cocotb/result.py | 4 + cocotb/scheduler.py | 17 ++- cocotb/triggers.py | 2 + examples/functionality/tests/test_calling.py | 110 +++++++++++-------- 6 files changed, 95 insertions(+), 68 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 5099518e..c0bf1e84 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -32,7 +32,7 @@ import cocotb from cocotb.log import SimLog from cocotb.triggers import Join, PythonTrigger, Timer -from cocotb.result import TestComplete, TestError, TestFailure, TestSuccess +from cocotb.result import TestComplete, TestError, TestFailure, TestSuccess, ReturnValue def public(f): @@ -86,6 +86,7 @@ def __init__(self, inst, parent): self.__doc__ = parent._func.__doc__ self.module = parent._func.__module__ self.funcname = parent._func.__name__ + self.retval = None if not hasattr(self._coro, "send"): self.log.error("%s isn't a value coroutine! Did you use the yield keyword?" @@ -104,6 +105,9 @@ def send(self, value): except TestComplete as e: self.log.info(str(e)) raise + except ReturnValue as e: + self.retval = e.retval + raise CoroutineComplete(callback=self._finished_cb) except StopIteration: raise CoroutineComplete(callback=self._finished_cb) @@ -200,6 +204,7 @@ def __call__(self, *args, **kwargs): try: return RunningCoroutine(self._func(*args, **kwargs), self) except Exception as e: + print "bing" traceback.print_exc() result = TestError(str(e)) traceback.print_exc(file=result.stderr) @@ -215,17 +220,6 @@ def __iter__(self): return self def __str__(self): return str(self._func.__name__) -#@coroutine -def block_handler(block_function, event, *args, **kwargs): - """To get back from the threading context just triggers the event - for the moment - """ - # Bit of a hack here -# yield Timer(0) - event.result = block_function(*args, **kwargs) - event.set() - - @public class function(object): """Decorator class that allows a a function to block @@ -238,10 +232,16 @@ def __init__(self, func): self._func = func self.log = SimLog("cocotb.function.%s" % self._func.__name__, id(self)) + def __call__(self, *args, **kwargs): + + @coroutine + def execute_func(self, event): + event.result = yield cocotb.coroutine(self._func)(*args, **kwargs) + event.set() + self._event = threading.Event() - #cocotb.scheduler.queue(block_handler(self._func, self._event, *args, **kwargs)) - block_handler(self._func, self._event, *args, **kwargs) + coro = cocotb.scheduler.queue(execute_func(self, self._event)) self._event.wait() return self._event.result diff --git a/cocotb/log.py b/cocotb/log.py index b6abcd9a..8c9fea3d 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -147,7 +147,7 @@ def rjust(string, chars): return string.rjust(chars) def _format(self, timeh, timel, level, record, msg): - simtime = "% 6d.%02dns" % ((timel/1000), (timel%1000)/10) + simtime = "% 6d.%02dns" % ((timel/1000), (timel%1000)/10) prefix = simtime + ' ' + level + ' ' + \ self.ljust(record.name, _RECORD_CHARS) + \ self.rjust(os.path.split(record.filename)[1], _FILENAME_CHARS) + \ diff --git a/cocotb/result.py b/cocotb/result.py index 555606a0..2dc3ba36 100644 --- a/cocotb/result.py +++ b/cocotb/result.py @@ -26,6 +26,10 @@ # TODO: Coule use cStringIO? from StringIO import StringIO +class ReturnValue(StopIteration): + def __init__(self, retval): + self.retval = retval + class TestComplete(StopIteration): """ Exceptions are used to pass test results around. diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index f04dc2a8..93841652 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -128,6 +128,7 @@ def _add_trigger(self, trigger, coroutine): def queue(self, coroutine): """Queue a coroutine for execution""" + self.log.info("Adding coroutine %s to queue" % coroutine.__name__) self._pending_adds.append(coroutine) def add(self, coroutine): @@ -198,12 +199,16 @@ def schedule(self, coroutine, trigger=None): trigger (cocotb.triggers.Trigger): The trigger that caused this coroutine to be scheduled """ - - coroutine.log.debug("Scheduling (%s)" % str(trigger)) + if hasattr(trigger, "pass_retval"): + self.log.debug("Coroutine returned a retval") + sendval = trigger.retval + else: + coroutine.log.debug("Scheduling (%s)" % str(trigger)) + sendval = trigger try: try: - result = coroutine.send(trigger) + result = coroutine.send(sendval) # Normal co-routine completion except cocotb.decorators.CoroutineComplete as exc: @@ -227,7 +232,9 @@ def schedule(self, coroutine, trigger=None): # Queue current routine to schedule when the nested routine exits self.queue(result) - self._add_trigger(result.join(), coroutine) + new_trigger = result.join() + new_trigger.pass_retval = True + self._add_trigger(new_trigger, coroutine) elif isinstance(result, list): for trigger in result: @@ -238,7 +245,7 @@ def schedule(self, coroutine, trigger=None): # TestComplete indication is game over, tidy up except TestComplete as test_result: - + if hasattr(test_result, "stderr"): print str(test_result.stderr.getvalue()) # Tag that close down is needed, save the test_result # for later use in cleanup handler # If we're already tearing down we ignore any further test results diff --git a/cocotb/triggers.py b/cocotb/triggers.py index a9a20ea0..fedc4b12 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -284,10 +284,12 @@ class Join(PythonTrigger): def __init__(self, coroutine): Trigger.__init__(self) self._coroutine = coroutine + self.retval = None def prime(self, callback): """Register our calback for when the coroutine exits""" def _cb(): + self.retval = self._coroutine.retval callback(self) self._coroutine._callbacks.append(_cb) diff --git a/examples/functionality/tests/test_calling.py b/examples/functionality/tests/test_calling.py index bb537fe9..e3f8e422 100644 --- a/examples/functionality/tests/test_calling.py +++ b/examples/functionality/tests/test_calling.py @@ -34,74 +34,88 @@ import threading import time import cocotb -from cocotb.triggers import Timer, Join, RisingEdge, ReadOnly +from cocotb.result import ReturnValue, TestFailure +from cocotb.triggers import Timer, Join, RisingEdge, ReadOnly, Edge -signal = None +test_count = 0 +g_dut = None # Tests relating to calling convention and operation -def create_thread(): - new_thread = threading.Thread(group=None, target=blocking_function, name="Test_thread", args=(), kwargs={}) - new_thread.start() - @cocotb.function -def blocking_function(): - global signal - stime = 2 - print("Blocking for %d seconds then asserting clock" % stime) - time.sleep(stime) - signal <= 1 - print("Block finished") - -clock_count = 0 +def decorated_test_read(dut, signal): + global test_count + dut.log.info("Inside decorated_test_read") + test_count = 0 + while test_count is not 5: + yield RisingEdge(dut.clk) + test_count += 1 + + raise ReturnValue(test_count) + +def test_read(dut, signal): + global test_count + dut.log.info("Inside test_read") + test_count = 0 + while test_count is not 5: + yield RisingEdge(dut.clk) + test_count += 1 + +def hal_read(function): + global g_dut + global test_count + function(g_dut, g_dut.stream_out_ready) + g_dut.log.info("Cycles seen is %d" % test_count) + +def create_thread(function): + """ Create a thread to simulate an external calling entity """ + new_thread = threading.Thread(group=None, target=hal_read, name="Test_thread", args=([function]), kwargs={}) + new_thread.start() @cocotb.coroutine def clock_gen(clock): """Drive the clock signal""" - global clock_count for i in range(10000): clock <= 0 - yield Timer(1000) + yield Timer(100) clock <= 1 - yield Timer(1000) - clock_count += 1 + yield Timer(100) clock.log.warning("Clock generator finished!") -signal_count = 0 - -@cocotb.coroutine -def signal_monitor(signal): - """Check that the clock is moving and increment - a counter - """ - global signal_count - - yield RisingEdge(signal) - signal_count += 1 - - print("Clock mon exiting") - - @cocotb.test(expect_fail=False) def test_callable(dut): - """Test ability to call a blocking function that will block but allow other coroutines to continue + """Test ability to call a function that will block but allow other coroutines to continue + + Test creates a thread to simulate another context. This thread will then "block" for + 5 clock cycles. 5 cycles should be seen by the thread + """ + global g_dut + global test_count + g_dut = dut + create_thread(decorated_test_read) + dut.log.info("Test thread created") + clk_gen = cocotb.scheduler.add(clock_gen(dut.clk)) + yield Timer(10000) + clk_gen.kill() + if test_count is not 5: + raise TestFailure - The test creates another thread that will block for a period of time. This would normally - mean that the simulator could not progress since control would not pass back to the simulator +@cocotb.test(expect_fail=True) +def test_callable_fail(dut): + """Test ability to call a function that will block but allow other coroutines to continue - In this test the clock driver should continue to be able to toggle pins - we monitor this as well and count that the number of observed transitions matches the number of sets + Test creates a thread to simulate another context. This thread will then "block" for + 5 clock cycles but not using the function decorator. No cycls should be seen. """ - global clock_count - global signal - signal = dut.stream_in_valid - signal_mon = cocotb.scheduler.add(signal_monitor(signal)) + global g_dut + global test_count + g_dut = dut + create_thread(test_read) + dut.log.info("Test thread created") clk_gen = cocotb.scheduler.add(clock_gen(dut.clk)) - create_thread() - #blocking_function() - yield Timer(1000) - yield Join(signal_mon) + yield Timer(10000) clk_gen.kill() - print("Have had %d transitions" % clock_count) + if test_count is not 5: + raise TestFailure From 592e895ea5e1ea31ca7d756e03997121931512d4 Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 7 Aug 2013 10:04:30 -0700 Subject: [PATCH 0287/2656] Issue #67 - define a COCOTB_SIM for VCS --- makefiles/simulators/Makefile.vcs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index 396df719..a18b5754 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -49,11 +49,11 @@ pli.tab : .PHONY: sim sim: $(OBJ_DIR) $(VERILOG_SOURCES) pli.tab libs_native PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - vcs -R +acc+1 +vpi -P pli.tab -sverilog $(EXTRA_ARGS) -debug -load libgpi.so $(VERILOG_SOURCES) + vcs -R +acc+1 +vpi -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -debug -load libgpi.so $(VERILOG_SOURCES) dve: $(OBJ_DIR) $(VERILOG_SOURCES) pli.tab libs_native PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - vcs -R +acc+1 +vpi+1+assertion -P pli.tab -sverilog $(EXTRA_ARGS) -gui -load libgpi.so $(VERILOG_SOURCES) + vcs -R +acc+1 +vpi+1+assertion -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -gui -load libgpi.so $(VERILOG_SOURCES) clean: -@rm -rf $(OBJ_DIR) -@rm -f pli.tab From 3da74e8c781fd415aea01f130885b5aaf6698b5d Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 8 Aug 2013 17:26:40 +0100 Subject: [PATCH 0288/2656] Cleanup: Remove debug accidentally committed --- cocotb/scheduler.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 93841652..2adb0640 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -128,7 +128,6 @@ def _add_trigger(self, trigger, coroutine): def queue(self, coroutine): """Queue a coroutine for execution""" - self.log.info("Adding coroutine %s to queue" % coroutine.__name__) self._pending_adds.append(coroutine) def add(self, coroutine): From e81e7a56ff7daef596fd5815d06e37224bd498e9 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 8 Aug 2013 17:28:17 +0100 Subject: [PATCH 0289/2656] Issue #59: Enable calling of external functions --- cocotb/__init__.py | 2 +- cocotb/decorators.py | 27 +++++++++++++++++++- examples/functionality/tests/test_calling.py | 25 ++++++++++++++++++ 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index c6d53ce7..2cca4ea0 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -43,7 +43,7 @@ # Things we want in the cocotb namespace -from cocotb.decorators import test, coroutine, function +from cocotb.decorators import test, coroutine, function, external # Singleton scheduler instance # NB this cheekily ensures a singleton since we're replacing the reference diff --git a/cocotb/decorators.py b/cocotb/decorators.py index c0bf1e84..0d56eea1 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -31,7 +31,7 @@ import cocotb from cocotb.log import SimLog -from cocotb.triggers import Join, PythonTrigger, Timer +from cocotb.triggers import Join, PythonTrigger, Timer, Event from cocotb.result import TestComplete, TestError, TestFailure, TestSuccess, ReturnValue @@ -245,7 +245,32 @@ def execute_func(self, event): self._event.wait() return self._event.result +def external(func): + """Decorator to apply to an external function to enable calling from cocotb + This currently creates a new execution context for each function that is + call. Scope for this to be streamlined to a queue in future + """ + + @coroutine + def wrapped(*args, **kwargs): + + # Call the function that was decorated in the context of the thread + def execute_func(func, event): + event.result = func(*args, **kwargs) + event.set() + + # Start up the thread, this is done in coroutine context + event = Event() + thread = threading.Thread(group=None, target=execute_func, + name="Test_thread", args=([func, event]), kwargs={}) + thread.start() + yield event.wait() + if event.result is not None: + raise ReturnValue(event.result) + + + return wrapped @public class test(coroutine): diff --git a/examples/functionality/tests/test_calling.py b/examples/functionality/tests/test_calling.py index e3f8e422..6605588a 100644 --- a/examples/functionality/tests/test_calling.py +++ b/examples/functionality/tests/test_calling.py @@ -37,6 +37,8 @@ from cocotb.result import ReturnValue, TestFailure from cocotb.triggers import Timer, Join, RisingEdge, ReadOnly, Edge +from cocotb.decorators import external + test_count = 0 g_dut = None @@ -119,3 +121,26 @@ def test_callable_fail(dut): clk_gen.kill() if test_count is not 5: raise TestFailure + +def test_ext_function(dut): + dut.log.info("Sleeping") + time.sleep(0.01) + +def test_ext_function_return(dut): + value = 2 + dut.log.info("Sleepoing and returning %s" % value) + time.sleep(0.01) + return value + +@cocotb.test(expect_fail=False) +def test_ext_call_return(dut): + """Test ability to yeild on an external non cocotb coroutine decorated function""" + clk_gen = cocotb.scheduler.add(clock_gen(dut.clk)) + value = yield external(test_ext_function_return)(dut) + dut.log.info("Value back was %d" % value) + +@cocotb.test(expect_fail=False) +def test_ext_call_nreturn(dut): + """Test ability to yeild on an external non cocotb coroutine decorated function""" + clk_gen = cocotb.scheduler.add(clock_gen(dut.clk)) + yield external(test_ext_function)(dut) From 1cc6a22cb0d42b9d4bcdf178b8e42588ce7e4397 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 8 Aug 2013 18:47:06 +0100 Subject: [PATCH 0290/2656] Fixes #68: Only increment test count when test is appended to queue --- cocotb/regression.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index db988863..39175ea5 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -96,13 +96,13 @@ def initialise(self): self.xunit.add_skipped() continue - self.ntests += 1 if test.skip: self.log.info("Skipping test %s" % thing.name) self.xunit.add_testcase(name=thing.name, classname=module_name, time="0.0") self.xunit.add_skipped() else: self._queue.append(test) + self.ntests += 1 for valid_tests in self._queue: self.log.info("Found test %s.%s" % From 04ce21bf87b9f8062383505a86012c5967a10a4e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 9 Aug 2013 00:42:21 +0100 Subject: [PATCH 0291/2656] Issue #59: Ensure that unblocking of yield is done from coroutine context --- cocotb/decorators.py | 21 ++++++++++++++------- cocotb/triggers.py | 15 +++++++++++++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 0d56eea1..91a3174f 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -31,7 +31,7 @@ import cocotb from cocotb.log import SimLog -from cocotb.triggers import Join, PythonTrigger, Timer, Event +from cocotb.triggers import Join, PythonTrigger, Timer, Event, NullTrigger from cocotb.result import TestComplete, TestError, TestFailure, TestSuccess, ReturnValue @@ -245,6 +245,12 @@ def execute_func(self, event): self._event.wait() return self._event.result +@function +def unblock_external(event): + event._release = NullTrigger() + event.set() + yield event._release + def external(func): """Decorator to apply to an external function to enable calling from cocotb @@ -255,20 +261,21 @@ def external(func): @coroutine def wrapped(*args, **kwargs): - # Call the function that was decorated in the context of the thread - def execute_func(func, event): - event.result = func(*args, **kwargs) - event.set() + # Call the function in thread context + def execute_func(func, _event): + _event.result = func(*args, **kwargs) + # Queue a co-routine to + unblock_external(_event) # Start up the thread, this is done in coroutine context event = Event() thread = threading.Thread(group=None, target=execute_func, - name="Test_thread", args=([func, event]), kwargs={}) + name=str(func) + "thread", args=([func, event]), kwargs={}) thread.start() yield event.wait() if event.result is not None: raise ReturnValue(event.result) - + event._release.set() return wrapped diff --git a/cocotb/triggers.py b/cocotb/triggers.py index fedc4b12..da629d67 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -277,6 +277,21 @@ def wait(self): def __str__(self): return self.__class__.__name__ + "(%s)" % self.name +class NullTrigger(Trigger): + """ + Trigger for internal interfacing use" + """ + def __init__(self, name=""): + Trigger.__init__(self) + self._callback = None + self.name = name + + def prime(self, callback): + pass + + def set(self): + pass + class Join(PythonTrigger): """ Join a coroutine, firing when it exits From d8b4e441bf26cf9da07ef35d934fa323403f5783 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 11 Aug 2013 20:04:44 -0700 Subject: [PATCH 0292/2656] Cleanup: Fix up __name__ attribute (TODO: use functools.wraps) --- cocotb/decorators.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index c0bf1e84..5b0e376d 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -286,4 +286,5 @@ def _wrapped_test(*args, **kwargs): _wrapped_test.im_test = True # For auto-regressions _wrapped_test.name = self._func.__name__ + _wrapped_test.__name__ = self._func.__name__ return _wrapped_test From 4c1615b2b5af44dc03dc99fbc4fccb307173d9f6 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 11 Aug 2013 20:26:34 -0700 Subject: [PATCH 0293/2656] Cleanup: Sort tests by name before running in regression --- cocotb/regression.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cocotb/regression.py b/cocotb/regression.py index 39175ea5..377f450b 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -104,6 +104,8 @@ def initialise(self): self._queue.append(test) self.ntests += 1 + self._queue.sort(key=lambda test: "%s.%s" % (test.module, test.funcname)) + for valid_tests in self._queue: self.log.info("Found test %s.%s" % (valid_tests.module, From 30d35009d03a21d0d8d8c8924a9949da4be576bb Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 11 Aug 2013 20:55:59 -0700 Subject: [PATCH 0294/2656] Permit COCOTB_ANSI_OUTPUT environment variable to override default behaviour --- cocotb/log.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cocotb/log.py b/cocotb/log.py index 8c9fea3d..4bb02e93 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -46,7 +46,12 @@ class SimBaseLog(logging.getLoggerClass()): def __init__(self, name): hdlr = logging.StreamHandler(sys.stdout) - if sys.stdout.isatty(): + want_ansi = os.getenv("COCOTB_ANSI_OUTPUT") + if want_ansi is None: + want_ansi = sys.stdout.isatty() # default to ANSI for TTYs + else: + want_ansi = want_ansi == '1' + if want_ansi: hdlr.setFormatter(SimColourLogFormatter()) else: hdlr.setFormatter(SimLogFormatter()) From a3dcc53cdb983fe5c1df8fd5208cf20d6f96b1f1 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 12 Aug 2013 06:05:50 +0100 Subject: [PATCH 0295/2656] Issue #58: test_callable_fail does not work and was only passing due to test order, mark as skipped for now --- examples/functionality/tests/test_calling.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/functionality/tests/test_calling.py b/examples/functionality/tests/test_calling.py index 6605588a..e2f9428f 100644 --- a/examples/functionality/tests/test_calling.py +++ b/examples/functionality/tests/test_calling.py @@ -104,7 +104,7 @@ def test_callable(dut): if test_count is not 5: raise TestFailure -@cocotb.test(expect_fail=True) +@cocotb.test(expect_fail=True, skip=True) def test_callable_fail(dut): """Test ability to call a function that will block but allow other coroutines to continue From bdf538374d6ccd351a3d67f47d392422e4a7cecb Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 12 Aug 2013 21:55:24 +0100 Subject: [PATCH 0296/2656] Issue #69: Initial implementation of read/write --- cocotb/drivers/__init__.py | 36 ++++++++++++++++- cocotb/drivers/avalon.py | 81 ++++++++++++++++++++++++++++++++------ 2 files changed, 105 insertions(+), 12 deletions(-) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index 79296f76..c37fdcf2 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -34,9 +34,10 @@ import cocotb from cocotb.decorators import coroutine -from cocotb.triggers import Event, RisingEdge +from cocotb.triggers import Event, RisingEdge, ReadOnly from cocotb.bus import Bus from cocotb.log import SimLog +from cocotb.result import ReturnValue class BitDriver(object): @@ -201,6 +202,39 @@ def _driver_send(self, transaction): yield RisingEdge(self.clock) self.bus <= transaction + @coroutine + def __wait_for_value_on_signal(self, signal, level): + loops = 0 + yield ReadOnly() + while not signal.value is not level: + yield RisingEdge(self.clock) + yield ReadOnly() + loops += 1 + + raise ReturnValue(loops) + + @coroutine + def _wait_for_signal(self, signal): + """This method will return with the specified signal + has hit logic 1. The state will be in the ReadOnly phase + so sim will need to move to NextTimeStep before + registering more callbacks can occour + """ + res = yield self.__wait_for_value_on_signal(signal, 1) + + raise ReturnValue(res) + + @coroutine + def _wait_for_nsignal(self, signal): + """This method will return with the specified signal + has hit logic 0. The state will be in the ReadOnly phase + so sim will need to move to NextTimeStep before + registering more callbacks can occour + """ + res = yield self.__wait_for_value_on_signal(signal, 0) + + raise ReturnValue(res) + class ValidatedBusDriver(BusDriver): """ diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 9e274f7b..1883a401 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -30,10 +30,11 @@ NB Currently we only support a very small subset of functionality """ from cocotb.decorators import coroutine -from cocotb.triggers import RisingEdge, ReadOnly +from cocotb.triggers import RisingEdge, ReadOnly, NextTimeStep from cocotb.drivers import BusDriver, ValidatedBusDriver from cocotb.utils import hexdump from cocotb.binary import BinaryValue +from cocotb.result import ReturnValue class AvalonMM(BusDriver): """Avalon-MM Driver @@ -41,8 +42,8 @@ class AvalonMM(BusDriver): Currently we only support the mode required to communicate with SF avalon_mapper which is a limited subset of all the signals - - This needs some thought... do we do a transaction based mechanism or 'blocking' read/write calls? + Blocking operation is all that is supported at the moment, and for the near future as well + Posted responses from a slave are not supported. """ _signals = ["readdata", "read", "write", "waitrequest", "writedata", "address"] @@ -52,17 +53,78 @@ def __init__(self, entity, name, clock): # Drive some sensible defaults self.bus.read <= 0 self.bus.write <= 0 + self.bus.address <= 0 + + def read(self, address): + pass + def write(self, address, value): + pass + +class AvalonMaster(AvalonMM): + """Avalon-MM master + """ + def __init__(self, entity, name, clock): + AvalonMM.__init__(self, entity, name, clock) + self.log.warning("AvalonMaster created") + + @coroutine def read(self, address): """ + Issue a request to the bus and block until this + comes back. Simulation time still progresses + but syntactically it blocks. + See http://www.altera.com/literature/manual/mnl_avalon_spec_1_3.pdf """ - pass + # Apply values for next clock edge + yield RisingEdge(self.clock) + self.bus.address <= address + self.bus.read <= 1 + + # Wait for waitrequest to be low + yield self._wait_for_nsignal(self.bus.waitrequest) + + # Get the data + data = self.bus.readdata.value + # Deassert read + yield NextTimeStep() + self.bus.read <= 0 + # Ensure that !read has been applied to bus - def write(self, address): + raise ReturnValue(data) + + @coroutine + def write(self, address, value): """ + Issue a write to the given address with the specified + value. + See http://www.altera.com/literature/manual/mnl_avalon_spec_1_3.pdf """ - pass + # Apply valuse to bus + yield RisingEdge(self.clock) + self.bus.address <= address + self.bus.writedata <= value + self.bus.write <= 1 + + # Wait for waitrequest to be low + count = yield self._wait_for_nsignal(self.bus.waitrequest) + if count is not 0: + csr.log.warning("Waiting for %d loops for waitrequest to go low" % count) + + # Deassert write + yield NextTimeStep() + self.bus.write <= 0 + + + +class AvalonSlave(AvalonMM): + """Avalon-MM Slave + + This is not supported at the moment + """ + def __init__(self, entity, name, clock): + AvalonMM.__init__(self, entity, name, clock) class AvalonST(ValidatedBusDriver): @@ -81,10 +143,7 @@ def _wait_ready(self): FIXME assumes readyLatency of 0 """ - yield ReadOnly() - while not self.bus.ready.value: - yield RisingEdge(self.clock) - yield ReadOnly() + yield self._wait_for_signal(self.bus.ready) @coroutine def _send_string(self, string): @@ -135,7 +194,7 @@ def _send_string(self, string): nbytes = min(len(string), bus_width) data = string[:nbytes] word.buff = data[::-1] # Big Endian FIXME - + if len(string) <= bus_width: self.bus.endofpacket <= 1 From d6f4ce20768ad052c397544dc98686ff76f621a7 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 12 Aug 2013 23:42:58 +0100 Subject: [PATCH 0297/2656] Issue #58: Fix test case for test_callable_fail --- examples/functionality/tests/test_calling.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/functionality/tests/test_calling.py b/examples/functionality/tests/test_calling.py index e2f9428f..a49d4cd3 100644 --- a/examples/functionality/tests/test_calling.py +++ b/examples/functionality/tests/test_calling.py @@ -58,7 +58,6 @@ def decorated_test_read(dut, signal): def test_read(dut, signal): global test_count dut.log.info("Inside test_read") - test_count = 0 while test_count is not 5: yield RisingEdge(dut.clk) test_count += 1 @@ -66,6 +65,7 @@ def test_read(dut, signal): def hal_read(function): global g_dut global test_count + test_count = 0 function(g_dut, g_dut.stream_out_ready) g_dut.log.info("Cycles seen is %d" % test_count) From f687f13831825097f2b43218008145cee1689f58 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 12 Aug 2013 16:14:57 -0700 Subject: [PATCH 0298/2656] Cleanup: Tweaks to AvalonMM drivers --- cocotb/drivers/avalon.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 1883a401..f7b2ad64 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -30,7 +30,7 @@ NB Currently we only support a very small subset of functionality """ from cocotb.decorators import coroutine -from cocotb.triggers import RisingEdge, ReadOnly, NextTimeStep +from cocotb.triggers import RisingEdge, ReadOnly from cocotb.drivers import BusDriver, ValidatedBusDriver from cocotb.utils import hexdump from cocotb.binary import BinaryValue @@ -85,12 +85,16 @@ def read(self, address): # Wait for waitrequest to be low yield self._wait_for_nsignal(self.bus.waitrequest) - # Get the data - data = self.bus.readdata.value + # Assume readLatency = 1 + # FIXME need to configure this, should take a dictionary of Avalon properties. + yield RisingEdge(self.clock) + # Deassert read - yield NextTimeStep() self.bus.read <= 0 - # Ensure that !read has been applied to bus + + # Get the data + yield ReadOnly() + data = self.bus.readdata.value raise ReturnValue(data) @@ -113,7 +117,7 @@ def write(self, address, value): csr.log.warning("Waiting for %d loops for waitrequest to go low" % count) # Deassert write - yield NextTimeStep() + yield RisingEdge(self.clock) self.bus.write <= 0 From 30347a5a7661387645f7131ce5f06dfcd3d84e2f Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 12 Aug 2013 16:42:57 -0700 Subject: [PATCH 0299/2656] Cleanup: Allow @function decorator to decorate methods --- cocotb/decorators.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 61266ede..28d1d60d 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -245,6 +245,11 @@ def execute_func(self, event): self._event.wait() return self._event.result + def __get__(self, obj, type=None): + """Permit the decorator to be used on class methods + and standalone functions""" + return self.__class__(self._func.__get__(obj, type)) + @function def unblock_external(event): event._release = NullTrigger() From e0e8748dc14c46b10e1c5267f2c07c80e4df1e4d Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 13 Aug 2013 02:07:05 +0100 Subject: [PATCH 0300/2656] Issue #59: Cleanup test cases --- cocotb/decorators.py | 6 +++--- examples/functionality/tests/test_calling.py | 13 ++++++++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 28d1d60d..cdee6781 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -252,9 +252,8 @@ def __get__(self, obj, type=None): @function def unblock_external(event): - event._release = NullTrigger() event.set() - yield event._release + yield NullTrigger() def external(func): """Decorator to apply to an external function to enable calling from cocotb @@ -274,13 +273,14 @@ def execute_func(func, _event): # Start up the thread, this is done in coroutine context event = Event() + event._result = None thread = threading.Thread(group=None, target=execute_func, name=str(func) + "thread", args=([func, event]), kwargs={}) thread.start() yield event.wait() + if event.result is not None: raise ReturnValue(event.result) - event._release.set() return wrapped diff --git a/examples/functionality/tests/test_calling.py b/examples/functionality/tests/test_calling.py index a49d4cd3..b68a3a83 100644 --- a/examples/functionality/tests/test_calling.py +++ b/examples/functionality/tests/test_calling.py @@ -129,9 +129,10 @@ def test_ext_function(dut): def test_ext_function_return(dut): value = 2 dut.log.info("Sleepoing and returning %s" % value) - time.sleep(0.01) + time.sleep(1) return value + @cocotb.test(expect_fail=False) def test_ext_call_return(dut): """Test ability to yeild on an external non cocotb coroutine decorated function""" @@ -144,3 +145,13 @@ def test_ext_call_nreturn(dut): """Test ability to yeild on an external non cocotb coroutine decorated function""" clk_gen = cocotb.scheduler.add(clock_gen(dut.clk)) yield external(test_ext_function)(dut) + +@cocotb.test(expect_fail=False) +def test_ext_exit_error(dut): + """Test that a premature exit of the sim at it's request still results in the + clean close down of the sim world""" + value = yield external(test_ext_function_return)(dut) + if value is not None: + dut.log.info("Value back was %d" % value) + else: + dut.log.info("Bit odd that it was None") From 4ad48610d27893d90dec9128a075efbf843925d8 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 12 Aug 2013 19:09:30 -0700 Subject: [PATCH 0301/2656] Cleanup: Fix bug where BinaryValue contructor ignored longs --- cocotb/binary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 4dc2c720..7faa2ed2 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -68,7 +68,7 @@ def assign(self, value): if the string contains any characters that aren't 0, 1, X or Z then we interpret the string as a binary buffer... """ - if isinstance(value, int): + if isinstance(value, (int, long)): self.value = value elif isinstance(value, str): try: From 2cf23d9c0cbe7421c803a77f968dc37d3c1ce1ad Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 13 Aug 2013 03:28:31 +0100 Subject: [PATCH 0302/2656] Cleanup: Bring the internal clock back to like --- cocotb/clock.py | 14 +++++-- cocotb/decorators.py | 4 +- examples/functionality/tests/test_calling.py | 41 +++++++++++++------- examples/functionality/tests/test_cocotb.py | 12 ++++-- lib/vpi_shim/gpi_vpi.c | 2 +- 5 files changed, 50 insertions(+), 23 deletions(-) diff --git a/cocotb/clock.py b/cocotb/clock.py index cdd42091..79af1c0f 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -44,14 +44,22 @@ def __init__(self, signal, period): BaseClock.__init__(self, signal) self.period = period self.frequency = 1.0 / period * 1000000 + self.hdl = None def start(self, cycles=0): - self.hdl = simulator.create_clock(self.signal._handle, self.period, cycles) - self.log.debug("Clock %s Started with period %d" % (str(self.signal), self.period)) + """ + cycles = 0 will not auto stop the clock + """ + if self.hdl is None: + self.hdl = simulator.create_clock(self.signal._handle, self.period, cycles) + self.log.debug("Clock %s Started with period %d" % (str(self.signal), self.period)) + else: + self.log.warning("Clock %s already started" % (str(self.signal))) def stop(self): - simulator.stop_clock(self.hdl) + if self.hdl is not None: + simulator.stop_clock(self.hdl) def __str__(self): return self.__class__.__name__ + "(%3.1fMHz)" % self.frequency diff --git a/cocotb/decorators.py b/cocotb/decorators.py index cdee6781..f6d4e5c2 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -253,7 +253,7 @@ def __get__(self, obj, type=None): @function def unblock_external(event): event.set() - yield NullTrigger() + yield NullTrigger def external(func): """Decorator to apply to an external function to enable calling from cocotb @@ -273,7 +273,7 @@ def execute_func(func, _event): # Start up the thread, this is done in coroutine context event = Event() - event._result = None + event.result = None thread = threading.Thread(group=None, target=execute_func, name=str(func) + "thread", args=([func, event]), kwargs={}) thread.start() diff --git a/examples/functionality/tests/test_calling.py b/examples/functionality/tests/test_calling.py index b68a3a83..a8a66229 100644 --- a/examples/functionality/tests/test_calling.py +++ b/examples/functionality/tests/test_calling.py @@ -36,7 +36,7 @@ import cocotb from cocotb.result import ReturnValue, TestFailure from cocotb.triggers import Timer, Join, RisingEdge, ReadOnly, Edge - +from cocotb.clock import Clock from cocotb.decorators import external test_count = 0 @@ -98,9 +98,10 @@ def test_callable(dut): g_dut = dut create_thread(decorated_test_read) dut.log.info("Test thread created") - clk_gen = cocotb.scheduler.add(clock_gen(dut.clk)) + clk_gen = Clock(dut.clk, 100) + clk_gen.start() yield Timer(10000) - clk_gen.kill() + clk_gen.stop() if test_count is not 5: raise TestFailure @@ -116,37 +117,49 @@ def test_callable_fail(dut): g_dut = dut create_thread(test_read) dut.log.info("Test thread created") - clk_gen = cocotb.scheduler.add(clock_gen(dut.clk)) + clk_gen = Clock(dut.clk, 100) + clk_gen.start() yield Timer(10000) - clk_gen.kill() + clk_gen.stop() if test_count is not 5: raise TestFailure def test_ext_function(dut): dut.log.info("Sleeping") - time.sleep(0.01) + return 2 def test_ext_function_return(dut): - value = 2 - dut.log.info("Sleepoing and returning %s" % value) - time.sleep(1) + value = dut.clk.value.value + dut.log.info("Sleeping and returning %s" % value) return value +@cocotb.coroutine +def clock_monitor(dut): + count = 0 + while True: + yield RisingEdge(dut.clk) + count += 1 @cocotb.test(expect_fail=False) def test_ext_call_return(dut): """Test ability to yeild on an external non cocotb coroutine decorated function""" - clk_gen = cocotb.scheduler.add(clock_gen(dut.clk)) - value = yield external(test_ext_function_return)(dut) - dut.log.info("Value back was %d" % value) + mon = cocotb.scheduler.queue(clock_monitor(dut)) + clk_gen = Clock(dut.clk, 100) + clk_gen.start() + value = yield external(test_ext_function)(dut) + clk_gen.stop() + dut.log.info("Value was %d" % value) @cocotb.test(expect_fail=False) def test_ext_call_nreturn(dut): """Test ability to yeild on an external non cocotb coroutine decorated function""" - clk_gen = cocotb.scheduler.add(clock_gen(dut.clk)) + mon = cocotb.scheduler.queue(clock_monitor(dut)) + clk_gen = Clock(dut.clk, 100) + clk_gen.start() yield external(test_ext_function)(dut) + clk_gen.stop() -@cocotb.test(expect_fail=False) +@cocotb.test(expect_fail=True) def test_ext_exit_error(dut): """Test that a premature exit of the sim at it's request still results in the clean close down of the sim world""" diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index eaa7d200..18f7ba6d 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -32,8 +32,8 @@ """ import cocotb -from cocotb.triggers import Timer, Join - +from cocotb.triggers import Timer, Join, RisingEdge +from cocotb.clock import Clock @@ -135,4 +135,10 @@ def test_failure_from_system_task(dut): """Allow the dut to call $fail_test() from verilog""" yield Timer(10000000) - +@cocotb.test(expect_fail=False) +def test_internal_clock(dut): + """Test ability to yeild on an external non cocotb coroutine decorated function""" + clk_gen = Clock(dut.clk, 100) + clk_gen.start() + yield RisingEdge(dut.clk) + clk_gen.stop() diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index efa07bce..0205fe4e 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -885,7 +885,7 @@ int gpi_clock_handler(void *clock) gpi_clock_hdl hdl = (gpi_clock_hdl)clock; gpi_sim_hdl cb_hdl; - if (hdl->exit || (hdl->max_cycles == hdl->curr_cycle)) + if (hdl->exit || ((hdl->max_cycles != 0) && (hdl->max_cycles == hdl->curr_cycle))) return; /* Unregister/free the last callback that just fired */ From 06438d50ef065a5fd6e85478ef9801046a4bf653 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 13 Aug 2013 10:16:05 -0700 Subject: [PATCH 0303/2656] Cleanup: Fix bug where unblock_external always raised an exception --- cocotb/decorators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index f6d4e5c2..407ac8d9 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -253,7 +253,7 @@ def __get__(self, obj, type=None): @function def unblock_external(event): event.set() - yield NullTrigger + yield NullTrigger() def external(func): """Decorator to apply to an external function to enable calling from cocotb From 9dabb8146cce9efc6811575445423d2e43e3c114 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 13 Aug 2013 18:42:05 +0100 Subject: [PATCH 0304/2656] Cleanup: Sample more than one clock edge for test_internal_clock --- examples/functionality/tests/test_cocotb.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 18f7ba6d..cdb087fe 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -140,5 +140,8 @@ def test_internal_clock(dut): """Test ability to yeild on an external non cocotb coroutine decorated function""" clk_gen = Clock(dut.clk, 100) clk_gen.start() - yield RisingEdge(dut.clk) + count = 0 + while count is not 100: + yield RisingEdge(dut.clk) + count += 1 clk_gen.stop() From 6d49e4dd296e96d8de8ded2d452b66c659b440a1 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 14 Aug 2013 17:43:02 +0100 Subject: [PATCH 0305/2656] temporary work store --- cocotb/decorators.py | 43 ++++++++++++-------- cocotb/log.py | 2 + cocotb/result.py | 12 ++++++ cocotb/scheduler.py | 8 ++-- cocotb/triggers.py | 7 ++-- examples/functionality/tests/test_calling.py | 5 ++- examples/functionality/tests/test_cocotb.py | 2 +- 7 files changed, 52 insertions(+), 27 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 407ac8d9..317ac55b 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -32,7 +32,7 @@ import cocotb from cocotb.log import SimLog from cocotb.triggers import Join, PythonTrigger, Timer, Event, NullTrigger -from cocotb.result import TestComplete, TestError, TestFailure, TestSuccess, ReturnValue +from cocotb.result import TestComplete, TestError, TestFailure, TestSuccess, ReturnValue, raise_error def public(f): @@ -251,9 +251,23 @@ def __get__(self, obj, type=None): return self.__class__(self._func.__get__(obj, type)) @function -def unblock_external(event): - event.set() +def unblock_external(bridge): yield NullTrigger() + print("Back from trigger") + bridge.set_out() + +@public +class test_locker(object): + def __init__(self): + self.in_event = None + self.out_event = Event() + self.result = None + + def set_in(self): + self.in_event.set() + + def set_out(self): + self.out_event.set() def external(func): """Decorator to apply to an external function to enable calling from cocotb @@ -266,21 +280,21 @@ def external(func): def wrapped(*args, **kwargs): # Call the function in thread context - def execute_func(func, _event): - _event.result = func(*args, **kwargs) + def execute_func(func, obj): + print("before call") + obj.result = func(*args, **kwargs) # Queue a co-routine to - unblock_external(_event) + unblock_external(obj) # Start up the thread, this is done in coroutine context - event = Event() - event.result = None + bridge = test_locker() thread = threading.Thread(group=None, target=execute_func, - name=str(func) + "thread", args=([func, event]), kwargs={}) + name=str(func) + "thread", args=([func, bridge]), kwargs={}) thread.start() - yield event.wait() + yield bridge.out_event.wait() - if event.result is not None: - raise ReturnValue(event.result) + if bridge.result is not None: + raise ReturnValue(bridge.result) return wrapped @@ -316,10 +330,7 @@ def _wrapped_test(*args, **kwargs): try: return RunningTest(self._func(*args, **kwargs), self) except Exception as e: - traceback.print_exc() - result = TestError(str(e)) - traceback.print_exc(file=result.stderr) - raise result + raise_error(self, str(e)) _wrapped_test.im_test = True # For auto-regressions _wrapped_test.name = self._func.__name__ diff --git a/cocotb/log.py b/cocotb/log.py index 4bb02e93..a945d620 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -78,6 +78,8 @@ def __init__(self, name, ident=None): self._log_name = name def _makeRecord(self, msg, level): + return + if self.logger.isEnabledFor(level): frame = inspect.stack()[2] info = inspect.getframeinfo(frame[0]) diff --git a/cocotb/result.py b/cocotb/result.py index 2dc3ba36..cec88768 100644 --- a/cocotb/result.py +++ b/cocotb/result.py @@ -24,8 +24,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' # TODO: Coule use cStringIO? +import traceback +import sys from StringIO import StringIO +def raise_error(obj, msg): + buff = StringIO() + lastframe = sys._getframe(2) + traceback.print_stack(lastframe, file=buff) + obj.log.error("%s\n%s" % (msg, buff.getvalue())) + exception = TestError(msg) + exception.stderr.write(buff.getvalue()) + buff.close() + raise exception + class ReturnValue(StopIteration): def __init__(self, retval): self.retval = retval diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 2adb0640..e418834f 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -38,7 +38,7 @@ import cocotb.decorators from cocotb.triggers import Trigger, Timer, ReadOnly, NextTimeStep, ReadWrite from cocotb.log import SimLog -from cocotb.result import TestComplete, TestError +from cocotb.result import TestComplete, TestError, ReturnValue class Scheduler(object): @@ -88,8 +88,8 @@ def react(self, trigger): self.log.debug("Completed scheduling loop, still waiting on:") - for trig, routines in self.waiting.items(): - self.log.debug("\t%s: [%s]" % (str(trig).ljust(30), " ".join([routine.__name__ for routine in routines]))) + #for trig, routines in self.waiting.items(): + # self.log.debug("\t%s: [%s]" % (str(trig).ljust(30), " ".join([routine.__name__ for routine in routines]))) # If we've performed any writes that are cached then schedule # another callback for the read-write part of the sim cycle, but @@ -121,8 +121,8 @@ def save_write(self, handle, args): def _add_trigger(self, trigger, coroutine): """Adds a new trigger which will cause the coroutine to continue when fired""" try: - trigger.prime(self.react) self.waiting[trigger].append(coroutine) + trigger.prime(self.react) except Exception as e: raise TestError("Unable to prime a trigger: %s" % str(e)) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index da629d67..6c1b26c1 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -279,7 +279,8 @@ def __str__(self): class NullTrigger(Trigger): """ - Trigger for internal interfacing use" + Trigger for internal interfacing use call the callback as soon + as it is primed and then remove it's self from the scheduler """ def __init__(self, name=""): Trigger.__init__(self) @@ -287,10 +288,8 @@ def __init__(self, name=""): self.name = name def prime(self, callback): - pass + callback(self) - def set(self): - pass class Join(PythonTrigger): """ diff --git a/examples/functionality/tests/test_calling.py b/examples/functionality/tests/test_calling.py index a8a66229..db74d4cd 100644 --- a/examples/functionality/tests/test_calling.py +++ b/examples/functionality/tests/test_calling.py @@ -138,14 +138,15 @@ def clock_monitor(dut): count = 0 while True: yield RisingEdge(dut.clk) + yield Timer(1000) count += 1 @cocotb.test(expect_fail=False) def test_ext_call_return(dut): """Test ability to yeild on an external non cocotb coroutine decorated function""" mon = cocotb.scheduler.queue(clock_monitor(dut)) - clk_gen = Clock(dut.clk, 100) - clk_gen.start() + clk_gen = Clock(dut.clk, 1000) + clk_gen.start(cycles=20) value = yield external(test_ext_function)(dut) clk_gen.stop() dut.log.info("Value was %d" % value) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index cdb087fe..01a66085 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -136,7 +136,7 @@ def test_failure_from_system_task(dut): yield Timer(10000000) @cocotb.test(expect_fail=False) -def test_internal_clock(dut): +def test_anternal_clock(dut): """Test ability to yeild on an external non cocotb coroutine decorated function""" clk_gen = Clock(dut.clk, 100) clk_gen.start() From 761ead050745328d317250a6c20898aaa78db8cd Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 14 Aug 2013 10:56:16 -0700 Subject: [PATCH 0306/2656] Add endianness control to BinaryValue and wire up Avalon Masters (fixes #70) --- cocotb/binary.py | 35 +++++++++++++++++++++++++++-------- cocotb/drivers/avalon.py | 26 +++++++++++++++++++++----- cocotb/monitors/avalon.py | 20 +++++++++++++++++++- 3 files changed, 67 insertions(+), 14 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 7faa2ed2..815d7413 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -25,8 +25,9 @@ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + def resolve(string): - for char in "xXzZ": + for char in BinaryValue._resolve_to_0: string = string.replace(char, "0") return string @@ -50,12 +51,23 @@ class BinaryValue(object): '*' """ - _permitted_chars = "01xXzZ" + _resolve_to_0 = "xXzZuU" + _permitted_chars = "xXzZuU" + "01" + + + def __init__(self, value=None, bits=None, bigEndian=True): + """ + Kwagrs: + Value (string or int or long): value to assign to the bus + bits (int): Number of bits to use for the underlying binary representation - def __init__(self, value=None, bits=None): + bigEndian (bool): Interpret the binary as big-endian when converting + to/from a string buffer. + """ self._str = "" self._bits = bits + self.big_endian = bigEndian if value is not None: self.assign(value) @@ -91,21 +103,28 @@ def get_buff(self): e.g. vector "0000000100011111".buff == "\x01\x1F" TODO: Doctest this! """ - bits = self._str - if len(bits) % 8: bits = "0" * (8 - len(bits) % 8) + bits - bits = resolve(bits) + bits = resolve(self._str) + if len(bits) % 8: + bits = "0" * (8 - len(bits) % 8) + bits + buff = "" while bits: byte = bits[:8] bits = bits[8:] val = int(byte, 2) - buff += chr(val) + if self.big_endian: + buff += chr(val) + else: + buff = chr(val) + buff return buff def set_buff(self, buff): self._str = "" for char in buff: - self._str = "{0:08b}".format(ord(char)) + self._str + if self.big_endian: + self._str += "{0:08b}".format(ord(char)) + else: + self._str = "{0:08b}".format(ord(char)) + self._str self._adjust() buff = property(get_buff, set_buff, None, "Access to the value as a buffer") diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index f7b2ad64..6cf00bc8 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -36,6 +36,7 @@ from cocotb.binary import BinaryValue from cocotb.result import ReturnValue + class AvalonMM(BusDriver): """Avalon-MM Driver @@ -53,7 +54,7 @@ def __init__(self, entity, name, clock): # Drive some sensible defaults self.bus.read <= 0 self.bus.write <= 0 - self.bus.address <= 0 + self.bus.address <= 0 def read(self, address): pass @@ -139,6 +140,23 @@ class AvalonSTPkts(ValidatedBusDriver): _signals = ["valid", "data", "startofpacket", "endofpacket", "empty"] _optional_signals = ["error", "channel", "ready"] + _default_config = { + "dataBitsPerSymbol" : 8, + "firstSymbolInHighOrderBits" : True, + "maxChannel" : 0, + "readyLatency" : 0 + } + + def __init__(self, *args, **kwargs): + ValidatedBusDriver.__init__(self, *args, **kwargs) + + self.config = AvalonSTPkts._default_config + + config = kwargs.pop('config', {}) + + for configoption, value in config.iteritems(): + self.config[configoption] = value + @coroutine def _wait_ready(self): """Wait for a ready cycle on the bus before continuing @@ -161,8 +179,8 @@ def _send_string(self, string): # FIXME busses that aren't integer numbers of bytes bus_width = len(self.bus.data) / 8 - word = BinaryValue(bits=len(self.bus.data)) + word = BinaryValue(bits=len(self.bus.data), bigEndian=self.config['firstSymbolInHighOrderBits']) # Drive some defaults since we don't know what state we're in self.bus.empty <= 0 @@ -171,7 +189,6 @@ def _send_string(self, string): self.bus.error <= 0 self.bus.valid <= 0 - while string: yield clkedge @@ -197,8 +214,7 @@ def _send_string(self, string): nbytes = min(len(string), bus_width) data = string[:nbytes] - word.buff = data[::-1] # Big Endian FIXME - + word.buff = data if len(string) <= bus_width: self.bus.endofpacket <= 1 diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index b4777509..c84d5265 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -65,13 +65,30 @@ class AvalonSTPkts(BusMonitor): _signals = ["valid", "data", "startofpacket", "endofpacket", "empty"] _optional_signals = ["error", "channel", "ready"] + _default_config = { + "dataBitsPerSymbol" : 8, + "firstSymbolInHighOrderBits" : True, + "maxChannel" : 0, + "readyLatency" : 0 + } + + def __init__(self, *args, **kwargs): + BusMonitor.__init__(self, *args, **kwargs) + + self.config = AvalonSTPkts._default_config + + config = kwargs.pop('config', {}) + + for configoption, value in config.iteritems(): + self.config[configoption] = value + @coroutine def _monitor_recv(self): """Watch the pins and reconstruct transactions""" # Avoid spurious object creation by recycling clkedge = RisingEdge(self.clock) - rdonly = ReadOnly() + rdonly = ReadOnly() pkt = "" while True: @@ -79,6 +96,7 @@ def _monitor_recv(self): yield rdonly if self.bus.valid.value and self.bus.startofpacket.value: vec = self.bus.data.value + vec.big_endian = self.config['firstSymbolInHighOrderBits'] pkt += vec.buff while True: yield clkedge From f4f55ba11f9327464b7520ef3808f5f8b5265636 Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 14 Aug 2013 14:19:15 -0700 Subject: [PATCH 0307/2656] Cleanup: Fix bug in BinaryValue padding logic --- cocotb/binary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 815d7413..39a4941c 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -148,7 +148,7 @@ def _adjust(self): return l = len(self._str) if l < self._bits: - self._str = "0" * (l-self._bits) + self._str + self._str = "0" * (self._bits-l) + self._str elif l > self._bits: print "WARNING truncating value to match requested number of bits (%d)" % l self._str = self._str[l - self._bits:] From 653b603101c57b2d6a8d738f269b0b191bf3c0a6 Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 14 Aug 2013 14:19:48 -0700 Subject: [PATCH 0308/2656] Cleanup: Correct syntax error in scoreboard --- cocotb/scoreboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index 7d801c58..070ec6aa 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -69,7 +69,7 @@ def result(self): for index, transaction in enumerate(expected_output): self.log.info("Expecting %d:\n%s" % (index, hexdump(str(transaction)))) if index > 5: - self.log.info("... and %d more to come" % len(expected_output) - index - 1) + self.log.info("... and %d more to come" % (len(expected_output) - index - 1)) break fail = True if fail: From 43d489a18c395c161eb11781b8fd7b3c1b25e5e1 Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 14 Aug 2013 15:23:32 -0700 Subject: [PATCH 0309/2656] Cleanup: Adjust Clock message severities --- cocotb/clock.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/clock.py b/cocotb/clock.py index 79af1c0f..b497e017 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -53,9 +53,9 @@ def start(self, cycles=0): """ if self.hdl is None: self.hdl = simulator.create_clock(self.signal._handle, self.period, cycles) - self.log.debug("Clock %s Started with period %d" % (str(self.signal), self.period)) + self.log.info("Clock %s Started with period %d" % (str(self.signal), self.period)) else: - self.log.warning("Clock %s already started" % (str(self.signal))) + self.log.debug("Clock %s already started" % (str(self.signal))) def stop(self): if self.hdl is not None: From 80201f30fc3b2bb7e1b2274fe44005941cb4382c Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 15 Aug 2013 01:37:20 +0100 Subject: [PATCH 0310/2656] Issue #24: Update VCS makefile to have a more defined interface --- makefiles/simulators/Makefile.vcs | 59 ++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index a18b5754..b8082d7b 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -25,40 +25,59 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -OBJ_DIR := obj +ifeq ($(SIM_BUILD_ROOT),) +SIM_BUILD := sim_build +else +SIM_BUILD := $(SIM_BUILD_ROOT) +endif + VPI_LIB := vpi ifeq ($(ARCH),x86_64) EXTRA_ARGS += -full64 endif -all: $(SIM_OBJ_DIR) sim - -$(OBJ_DIR): - mkdir -p $(OBJ_DIR) - +$(SIM_BUILD): + mkdir -p $(SIM_BUILD) # TODO: # investigate +vpi+1 option which reduces memory requirements # Can't do this using an argument, we have to create a PLI table file # enabling write access to the design -pli.tab : +$(SIM_BUILD)/pli.tab : echo "acc+=rw,wn:*" > $@ -.PHONY: sim -sim: $(OBJ_DIR) $(VERILOG_SOURCES) pli.tab libs_native - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - vcs -R +acc+1 +vpi -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -debug -load libgpi.so $(VERILOG_SOURCES) +.PHONY: regression sim + +sim: + -@rm -rf results.xml + $(MAKE) results.xml + +regression: build_simv results.xml -dve: $(OBJ_DIR) $(VERILOG_SOURCES) pli.tab libs_native +PYTHON_FILES:=$(shell find $(SIM_ROOT)/cocotb/ -name "*.py" | xargs) $(MODULE).py + +results.xml: $(SIM_BUILD)/simv $(PYTHON_FILES) + -PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + $(SIM_BUILD)/simv +define+COCOTB_SIM=1 $(EXTRA_ARGS) + +dve: $(SIM_BUILD)/simv_dve PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - vcs -R +acc+1 +vpi+1+assertion -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -gui -load libgpi.so $(VERILOG_SOURCES) -clean: - -@rm -rf $(OBJ_DIR) - -@rm -f pli.tab - -@rm -rf csrc - -@rm -rf simv.daidir - -@rm -f simv - -@rm -f ucli.key + vcs +acc+1 +vpi+1+assertion -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -gui -load libgpi.so $(VERILOG_SOURCES) +.PHONY: build_simv +build_simv: $(SIM_BUILD)/simv + +$(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab libs_native + cd $(SIM_BUILD) && \ + LD_LIBRARY_PATH=$(LIB_DIR) TOPLEVEL=$(TOPLEVEL) \ + vcs +acc+1 +vpi -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -debug -load libgpi.so $(VERILOG_SOURCES) + +.PHONY: clean +clean:: + -@rm -rf $(SIM_BUILD) + -@rm -rf simv.daidir + -@rm -rf cm.log + -@rm -rf results.xml + -@rm -rf ucli.key From 01b61b6d253af0401d42b26f322ce24a17dfa624 Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 14 Aug 2013 18:58:13 -0700 Subject: [PATCH 0311/2656] Issue #70: Correct padding of binary string when in bigEndian --- cocotb/binary.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 39a4941c..ca8f1ecc 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -148,7 +148,10 @@ def _adjust(self): return l = len(self._str) if l < self._bits: - self._str = "0" * (self._bits-l) + self._str + if self.big_endian: + self._str = "0" * (self._bits-l) + self._str + else: + self._str = self._str + "0" * (self._bits-l) elif l > self._bits: print "WARNING truncating value to match requested number of bits (%d)" % l self._str = self._str[l - self._bits:] From e23c56e4014db117039c720f21d0c3db5132f80e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 15 Aug 2013 03:04:14 +0100 Subject: [PATCH 0312/2656] Issue #24: Fix up some makefiles to handle changes, some of this should be removed later --- examples/comb_seq/Makefile | 4 +++- examples/demo/Makefile | 4 +++- examples/functionality/tests/Makefile | 4 +++- examples/plusargs/Makefile | 4 +++- makefiles/simulators/Makefile.vcs | 13 +++++++------ 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/examples/comb_seq/Makefile b/examples/comb_seq/Makefile index 5fc9c5ec..88128217 100644 --- a/examples/comb_seq/Makefile +++ b/examples/comb_seq/Makefile @@ -28,7 +28,9 @@ TOPLEVEL = issue12 -VERILOG_SOURCES = issue12.v +PWD=$(shell pwd) + +VERILOG_SOURCES = $(PWD)/issue12.v MODULE=issue12 include ../../makefiles/Makefile.inc diff --git a/examples/demo/Makefile b/examples/demo/Makefile index af9305a5..3d0fcfca 100644 --- a/examples/demo/Makefile +++ b/examples/demo/Makefile @@ -29,7 +29,9 @@ # FIXME these should come from some cunning script, possibly from the FDK :) TOPLEVEL = first_counter -VERILOG_SOURCES = counter.v +PWD=$(shell pwd) + +VERILOG_SOURCES = $(PWD)/counter.v MODULE=demo include ../../makefiles/Makefile.inc diff --git a/examples/functionality/tests/Makefile b/examples/functionality/tests/Makefile index 0517f5ec..0e5d2098 100644 --- a/examples/functionality/tests/Makefile +++ b/examples/functionality/tests/Makefile @@ -28,7 +28,9 @@ TOPLEVEL = sample_module -VERILOG_SOURCES = ../hdl/sample_module.v +PWD=$(shell pwd) + +VERILOG_SOURCES = $(PWD)/../hdl/sample_module.v MODULE=test_cocotb,test_multiple_modules,test_discovery,test_calling include ../../../makefiles/Makefile.inc diff --git a/examples/plusargs/Makefile b/examples/plusargs/Makefile index 0f017c47..1ae382b8 100644 --- a/examples/plusargs/Makefile +++ b/examples/plusargs/Makefile @@ -28,7 +28,9 @@ TOPLEVEL = tb_top -VERILOG_SOURCES = tb_top.v +PWD=$(shell pwd) + +VERILOG_SOURCES = $(PWD)/tb_top.v MODULE=plusargs PLUSARGS=+foo=bar +test1 +test2 +options=fubar diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index b8082d7b..c7eb179c 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -37,6 +37,10 @@ ifeq ($(ARCH),x86_64) EXTRA_ARGS += -full64 endif +sim: + -@rm -rf results.xml + $(MAKE) results.xml + $(SIM_BUILD): mkdir -p $(SIM_BUILD) @@ -50,16 +54,13 @@ $(SIM_BUILD)/pli.tab : .PHONY: regression sim -sim: - -@rm -rf results.xml - $(MAKE) results.xml - regression: build_simv results.xml -PYTHON_FILES:=$(shell find $(SIM_ROOT)/cocotb/ -name "*.py" | xargs) $(MODULE).py +MODULE_FILES:=$(shell for X in $(echo "test1,test2" | sed 's/,/ /g'); do echo $X.py; done) +PYTHON_FILES:=$(shell find $(SIM_ROOT)/cocotb/ -name "*.py" | xargs) $(MODULE_FILES) results.xml: $(SIM_BUILD)/simv $(PYTHON_FILES) - -PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + -PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ $(SIM_BUILD)/simv +define+COCOTB_SIM=1 $(EXTRA_ARGS) dve: $(SIM_BUILD)/simv_dve From 17e69ce84f6d8ff73875198b9389efdf52ea7fd9 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 15 Aug 2013 03:06:19 +0100 Subject: [PATCH 0313/2656] Cleanup: update .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 5022cdee..26088659 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,5 @@ results.xml # VCS files *.tab +sim_build +ucli.key From efb084c901ad96707105934524b1665b102261d1 Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 14 Aug 2013 19:31:21 -0700 Subject: [PATCH 0314/2656] Cleanup: Temporarily disable the $fail_test() since it interferes with other tests --- examples/functionality/hdl/sample_module.v | 3 ++- examples/functionality/tests/test_cocotb.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/functionality/hdl/sample_module.v b/examples/functionality/hdl/sample_module.v index 0f586f81..6a98eb41 100644 --- a/examples/functionality/hdl/sample_module.v +++ b/examples/functionality/hdl/sample_module.v @@ -54,7 +54,8 @@ initial begin $dumpfile("waveform.vcd"); $dumpvars(0,sample_module); - #500000 $fail_test("Test timed out, failing..."); +// TODO: Move into a separate test +// #500000 $fail_test("Test timed out, failing..."); end endmodule diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index cdb087fe..1058dcbd 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -130,7 +130,7 @@ def test_adding_a_coroutine_without_starting(dut): yield Join(forked) yield Timer(100) -@cocotb.test(expect_fail=True) +@cocotb.test(expect_fail=True, skip=True) def test_failure_from_system_task(dut): """Allow the dut to call $fail_test() from verilog""" yield Timer(10000000) From 6093684cd72f3332e8b38f1bc8020124b3be7066 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 15 Aug 2013 03:53:11 +0100 Subject: [PATCH 0315/2656] Cleanup: Remove test_calling from regresssion as still some problems --- examples/functionality/tests/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/functionality/tests/Makefile b/examples/functionality/tests/Makefile index 0e5d2098..4031b422 100644 --- a/examples/functionality/tests/Makefile +++ b/examples/functionality/tests/Makefile @@ -31,7 +31,7 @@ TOPLEVEL = sample_module PWD=$(shell pwd) VERILOG_SOURCES = $(PWD)/../hdl/sample_module.v -MODULE=test_cocotb,test_multiple_modules,test_discovery,test_calling +MODULE=test_cocotb,test_multiple_modules,test_discovery include ../../../makefiles/Makefile.inc include ../../../makefiles/Makefile.sim From 6bbe3b6f444d31535f7277f6d0c5b1581e064b1c Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 15 Aug 2013 15:20:27 -0700 Subject: [PATCH 0316/2656] Cleanup: Revert change to wait_ready in Avalon driver (broke world) --- cocotb/drivers/avalon.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 6cf00bc8..fcecb828 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -165,7 +165,10 @@ def _wait_ready(self): FIXME assumes readyLatency of 0 """ - yield self._wait_for_signal(self.bus.ready) + yield ReadOnly() + while not self.bus.ready.value: + yield RisingEdge(self.clock) + yield ReadOnly() @coroutine def _send_string(self, string): From 2fd8a9c1a588c55ffc8a18e59072d66882931728 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 15 Aug 2013 15:22:39 -0700 Subject: [PATCH 0317/2656] Tidy up of Avalon monitor, fix for missing optional signals --- cocotb/monitors/__init__.py | 2 +- cocotb/monitors/avalon.py | 35 +++++++++++++++++++---------------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index 1d6ec1f9..5fbcff35 100644 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -128,7 +128,7 @@ def __init__(self, entity, name, clock, callback=None, event=None): self.entity = entity self.name = name self.clock = clock - self.bus = Bus(self.entity, self.name, self._signals) + self.bus = Bus(self.entity, self.name, self._signals, optional_signals=self._optional_signals) Monitor.__init__(self, callback=callback, event=event) def __str__(self): diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index c84d5265..7e1705f5 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -91,25 +91,28 @@ def _monitor_recv(self): rdonly = ReadOnly() pkt = "" + def valid(): + if hasattr(self.bus, 'ready'): + return self.bus.valid.value and self.bus.ready.value + return self.bus.valid.value + while True: yield clkedge yield rdonly - if self.bus.valid.value and self.bus.startofpacket.value: + + if valid(): + if self.bus.startofpacket.value: + pkt = "" + vec = self.bus.data.value vec.big_endian = self.config['firstSymbolInHighOrderBits'] pkt += vec.buff - while True: - yield clkedge - yield rdonly - if self.bus.valid.value: - vec = self.bus.data.value - pkt += vec.buff - if self.bus.endofpacket.value: - # Truncate the empty bits - if self.bus.empty.value.value: - pkt = pkt[:-self.bus.empty.value.value] - self.log.info("Recieved a packet of %d bytes" % len(pkt)) - self.log.debug(hexdump(str((pkt)))) - self._recv(pkt) - pkt = "" - break + + if self.bus.endofpacket.value: + # Truncate the empty bits + if self.bus.empty.value.value: + pkt = pkt[:-self.bus.empty.value.value] + self.log.info("Recieved a packet of %d bytes" % len(pkt)) + self.log.debug(hexdump(str((pkt)))) + self._recv(pkt) + pkt = "" From 36cd3ec674f024fcebd8dd38ea421883b426aed2 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 15 Aug 2013 15:25:35 -0700 Subject: [PATCH 0318/2656] Issue #70 - correct 0 padding of values --- cocotb/binary.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index ca8f1ecc..3a6b609d 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -149,9 +149,9 @@ def _adjust(self): l = len(self._str) if l < self._bits: if self.big_endian: - self._str = "0" * (self._bits-l) + self._str - else: self._str = self._str + "0" * (self._bits-l) + else: + self._str = "0" * (self._bits-l) + self._str elif l > self._bits: print "WARNING truncating value to match requested number of bits (%d)" % l self._str = self._str[l - self._bits:] From 04866bea29442c3e9e148dcbe8252fe103d374e8 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 15 Aug 2013 15:38:29 -0700 Subject: [PATCH 0319/2656] Cleanup: Fix for missing default members --- cocotb/monitors/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index 5fbcff35..ca4fa5c2 100644 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -123,6 +123,9 @@ class BusMonitor(Monitor): """ Wrapper providing common functionality for monitoring busses """ + _signals = [] + _optional_signals = [] + def __init__(self, entity, name, clock, callback=None, event=None): self.log = SimLog("cocotb.%s.%s" % (entity.name, name)) self.entity = entity From 8e36665bc5bdfbeb0eab58dec7585038ed17bc94 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 16 Aug 2013 12:48:18 -0700 Subject: [PATCH 0320/2656] Cleanup: Support for busses with no name --- cocotb/bus.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cocotb/bus.py b/cocotb/bus.py index 7c188a9a..25e9d7bf 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -62,13 +62,19 @@ def __init__(self, entity, name, signals, optional_signals=[]): self._signals = {} for signal in signals: - signame = name + "_" + signal + if name: + signame = name + "_" + signal + else: + signame = signal setattr(self, signal, getattr(entity, signame)) self._signals[signal] = getattr(self, signal) # Also support a set of optional signals that don't have to be present for signal in optional_signals: - signame = name + "_" + signal + if name: + signame = name + "_" + signal + else: + signame = signal # Attempts to access a signal that doesn't exist will print a # backtrace so we 'peek' first, slightly un-pythonic if entity.__hasattr__(signame): From 85ceaee6ff0a260c565f54c7dbd68c4ea94babfb Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 16 Aug 2013 20:49:55 +0100 Subject: [PATCH 0321/2656] Issue#59: rename test_calling to force to the end --- .../functionality/tests/{test_calling.py => test_external.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/functionality/tests/{test_calling.py => test_external.py} (100%) diff --git a/examples/functionality/tests/test_calling.py b/examples/functionality/tests/test_external.py similarity index 100% rename from examples/functionality/tests/test_calling.py rename to examples/functionality/tests/test_external.py From d73b43860ebc6e72f0a9058df179b682b9a14730 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 16 Aug 2013 20:58:33 +0100 Subject: [PATCH 0322/2656] Cleanup: test_multiple_modules is not needed since we have more than one real test case file now --- examples/functionality/tests/test_multiple_modules.py | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 examples/functionality/tests/test_multiple_modules.py diff --git a/examples/functionality/tests/test_multiple_modules.py b/examples/functionality/tests/test_multiple_modules.py deleted file mode 100644 index f3c2286d..00000000 --- a/examples/functionality/tests/test_multiple_modules.py +++ /dev/null @@ -1,9 +0,0 @@ -import cocotb -from cocotb.triggers import Timer, Join - -@cocotb.test(expect_fail=False) -def simple_test(dut): - """Simple test in another module""" - yield Timer(10000) - - From 6220530d3ba03c95fcf9e9381fb63c1918318913 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 16 Aug 2013 21:01:44 +0100 Subject: [PATCH 0323/2656] Cleanup: Re-enable notification that the simulator has asked for a close down --- cocotb/__init__.py | 2 +- cocotb/scheduler.py | 15 ++++++++++++--- lib/embed/gpi_embed.c | 4 ++-- lib/vpi_shim/gpi_vpi.c | 4 ++++ 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 2cca4ea0..880759c4 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -121,7 +121,7 @@ def _fail_test(message): """Function that can be called externally to fail a test""" from cocotb.result import TestFailure scheduler.log.error("Failing test at simulator request") - scheduler.finish_test(TestFailure("Failure from external source: %s" % message)) + scheduler.finish_scheduler(TestFailure("Failure from external source: %s" % message)) def process_plusargs(): diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 2adb0640..e8dba755 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -256,6 +256,12 @@ def schedule(self, coroutine, trigger=None): coroutine.log.debug("Finished sheduling coroutine (%s)" % str(trigger)) + def finish_scheduler(self, test_result): + # if the sim it's self has issued a close down then the + # normal shutdown will not work + self.cleanup() + self.issue_result(test_result) + def finish_test(self, test_result): if not self._terminate: self._terminate = True @@ -272,15 +278,18 @@ def cleanup(self): self.log.debug("Killing %s" % str(coro)) coro.kill() + def issue_result(self, test_result): + # Tell the handler what the result was + self.log.debug("Issue test result to regresssion object") + cocotb.regression.handle_result(test_result) + @cocotb.decorators.coroutine def move_to_cleanup(self): yield Timer(1) self.prune_routines() self._readonly = None - # Tell the handler what the result was - self.log.debug("Issue test result to regresssion object") - cocotb.regression.handle_result(self._test_result) + self.issue_result(self._test_result) self._test_result = None # If another test was added to queue kick it off diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 063717dc..158a1be6 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -231,7 +231,7 @@ void embed_sim_init(gpi_sim_info_t *info) void embed_sim_end(void) { FENTER - LOG_WARN("Closing down cocotb at simulator request!"); + LOG_WARN("Simulator requested orderly shutdown"); FEXIT } @@ -253,4 +253,4 @@ void fail_test(const char *message) PyGILState_Release(gstate); FEXIT -} \ No newline at end of file +} diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 0205fe4e..da663cf0 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -965,6 +965,10 @@ int handle_sim_end(void *gpi_cb_data) FENTER if (sim_finish_cb) { sim_finish_cb = NULL; + /* This means that we have been asked to close */ + fail_test("Simulator has asked for forceable close"); + } else { + embed_sim_end(); } __gpi_free_callback(sim_init_cb); FEXIT From aca54c77a3501eed04adc0ece9ffc4c5674a9bfc Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 16 Aug 2013 21:02:23 +0100 Subject: [PATCH 0324/2656] Issue #59: Update test name to force the closing of the simulator to occour at the end of the test --- examples/functionality/tests/Makefile | 2 +- examples/functionality/tests/test_external.py | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/examples/functionality/tests/Makefile b/examples/functionality/tests/Makefile index 4031b422..e91c13f6 100644 --- a/examples/functionality/tests/Makefile +++ b/examples/functionality/tests/Makefile @@ -31,7 +31,7 @@ TOPLEVEL = sample_module PWD=$(shell pwd) VERILOG_SOURCES = $(PWD)/../hdl/sample_module.v -MODULE=test_cocotb,test_multiple_modules,test_discovery +MODULE=test_cocotb,test_discovery,test_external include ../../../makefiles/Makefile.inc include ../../../makefiles/Makefile.sim diff --git a/examples/functionality/tests/test_external.py b/examples/functionality/tests/test_external.py index a8a66229..7934edfe 100644 --- a/examples/functionality/tests/test_external.py +++ b/examples/functionality/tests/test_external.py @@ -160,11 +160,8 @@ def test_ext_call_nreturn(dut): clk_gen.stop() @cocotb.test(expect_fail=True) -def test_ext_exit_error(dut): +def ztest_ext_exit_error(dut): """Test that a premature exit of the sim at it's request still results in the clean close down of the sim world""" - value = yield external(test_ext_function_return)(dut) - if value is not None: - dut.log.info("Value back was %d" % value) - else: - dut.log.info("Bit odd that it was None") + yield external(test_ext_function_return)(dut) + yield Timer(100) From baaee4f6d63be55d4a4aa9e9b764ddcd81796ed9 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 19 Aug 2013 08:32:48 +0100 Subject: [PATCH 0325/2656] Issue#59: Test updates to try and track down the locking issue --- cocotb/decorators.py | 8 +++++++- cocotb/log.py | 2 +- cocotb/scheduler.py | 6 +++--- cocotb/triggers.py | 1 + examples/functionality/tests/test_external.py | 14 ++++++++++++-- lib/embed/gpi_embed.c | 1 + lib/vpi_shim/gpi_vpi.c | 5 ++--- 7 files changed, 27 insertions(+), 10 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 407ac8d9..b0c10b50 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -252,8 +252,8 @@ def __get__(self, obj, type=None): @function def unblock_external(event): - event.set() yield NullTrigger() + event.set() def external(func): """Decorator to apply to an external function to enable calling from cocotb @@ -267,17 +267,23 @@ def wrapped(*args, **kwargs): # Call the function in thread context def execute_func(func, _event): + print("Before") _event.result = func(*args, **kwargs) + print("After") # Queue a co-routine to unblock_external(_event) + print("Back from unblock") # Start up the thread, this is done in coroutine context event = Event() event.result = None + print("Before thread start") thread = threading.Thread(group=None, target=execute_func, name=str(func) + "thread", args=([func, event]), kwargs={}) thread.start() + print("Thread started") yield event.wait() + print("Back from wait") if event.result is not None: raise ReturnValue(event.result) diff --git a/cocotb/log.py b/cocotb/log.py index 4bb02e93..288173aa 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -62,7 +62,7 @@ def __init__(self, name): self.propagate = False logging.__init__(name) self.addHandler(hdlr) - self.setLevel(logging.INFO) + self.setLevel(logging.DEBUG) """ Need to play with this to get the path of the called back, construct our own makeRecord for this """ diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index e8dba755..a8ac1447 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -88,8 +88,8 @@ def react(self, trigger): self.log.debug("Completed scheduling loop, still waiting on:") - for trig, routines in self.waiting.items(): - self.log.debug("\t%s: [%s]" % (str(trig).ljust(30), " ".join([routine.__name__ for routine in routines]))) + #for trig, routines in self.waiting.items(): + # self.log.debug("\t%s: [%s]" % (str(trig).ljust(30), " ".join([routine.__name__ for routine in routines]))) # If we've performed any writes that are cached then schedule # another callback for the read-write part of the sim cycle, but @@ -121,8 +121,8 @@ def save_write(self, handle, args): def _add_trigger(self, trigger, coroutine): """Adds a new trigger which will cause the coroutine to continue when fired""" try: - trigger.prime(self.react) self.waiting[trigger].append(coroutine) + trigger.prime(self.react) except Exception as e: raise TestError("Unable to prime a trigger: %s" % str(e)) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index da629d67..a53ad5cb 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -287,6 +287,7 @@ def __init__(self, name=""): self.name = name def prime(self, callback): + callback(self) pass def set(self): diff --git a/examples/functionality/tests/test_external.py b/examples/functionality/tests/test_external.py index 7934edfe..1b2fa20d 100644 --- a/examples/functionality/tests/test_external.py +++ b/examples/functionality/tests/test_external.py @@ -125,12 +125,13 @@ def test_callable_fail(dut): raise TestFailure def test_ext_function(dut): - dut.log.info("Sleeping") + #dut.log.info("Sleeping") return 2 def test_ext_function_return(dut): value = dut.clk.value.value - dut.log.info("Sleeping and returning %s" % value) + #dut.log.info("Sleeping and returning %s" % value) + #time.sleep(0.2) return value @cocotb.coroutine @@ -159,6 +160,15 @@ def test_ext_call_nreturn(dut): yield external(test_ext_function)(dut) clk_gen.stop() +@cocotb.test(expect_fail=False) +def test_multiple_externals(dut): + clk_gen = Clock(dut.clk, 100) + clk_gen.start() + yield external(test_ext_function)(dut) + dut.log.info("First one completed") + yield external(test_ext_function)(dut) + dut.log.info("Second one completed") + @cocotb.test(expect_fail=True) def ztest_ext_exit_error(dut): """Test that a premature exit of the sim at it's request still results in the diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 158a1be6..66d99c75 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -231,6 +231,7 @@ void embed_sim_init(gpi_sim_info_t *info) void embed_sim_end(void) { FENTER + /* Indicate to the upper layer that this needs to close down */ LOG_WARN("Simulator requested orderly shutdown"); FEXIT } diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index da663cf0..15227a14 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -966,10 +966,9 @@ int handle_sim_end(void *gpi_cb_data) if (sim_finish_cb) { sim_finish_cb = NULL; /* This means that we have been asked to close */ - fail_test("Simulator has asked for forceable close"); - } else { embed_sim_end(); - } + } /* Other sise we have already been here from the top down so do not need + to inform the upper layers that anything has occoured */ __gpi_free_callback(sim_init_cb); FEXIT } From 37059d62312374c07f8ccbe8ea2c3cb7dc18a1fc Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 20 Aug 2013 17:48:14 +0100 Subject: [PATCH 0326/2656] Issue #59: Synchronise the exchange from external to main thread --- cocotb/decorators.py | 20 ++--- cocotb/scheduler.py | 87 ++++++++++++++++++- cocotb/triggers.py | 1 + examples/functionality/tests/test_external.py | 16 ++-- 4 files changed, 103 insertions(+), 21 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 48a5a3a0..048eb499 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -28,6 +28,7 @@ import logging import traceback import threading +import pdb import cocotb from cocotb.log import SimLog @@ -236,13 +237,15 @@ def __init__(self, func): def __call__(self, *args, **kwargs): @coroutine - def execute_func(self, event): + def execute_function(self, event): event.result = yield cocotb.coroutine(self._func)(*args, **kwargs) event.set() self._event = threading.Event() - coro = cocotb.scheduler.queue(execute_func(self, self._event)) + self._event.result = None + coro = cocotb.scheduler.queue_external(execute_function(self, self._event)) self._event.wait() + return self._event.result def __get__(self, obj, type=None): @@ -253,7 +256,6 @@ def __get__(self, obj, type=None): @function def unblock_external(bridge): yield NullTrigger() - print("Back from trigger") bridge.set_out() @public @@ -278,24 +280,20 @@ def external(func): @coroutine def wrapped(*args, **kwargs): - + cocotb.scheduler.set_external() # Start up the thread, this is done in coroutine context bridge = test_locker() - def execute_func(func, _event): - print("Before") + def execute_external(func, _event): _event.result = func(*args, **kwargs) - print("After") # Queue a co-routine to unblock_external(_event) - print("Back from unblock") - print("Before thread start") - thread = threading.Thread(group=None, target=execute_func, + thread = threading.Thread(group=None, target=execute_external, name=str(func) + "thread", args=([func, bridge]), kwargs={}) thread.start() + yield bridge.out_event.wait() - print("Back from wait") if bridge.result is not None: raise ReturnValue(bridge.result) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index e12ca17b..52a07c81 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -32,13 +32,17 @@ import threading import collections import os +import threading +import time +import pdb import simulator import cocotb import cocotb.decorators -from cocotb.triggers import Trigger, Timer, ReadOnly, NextTimeStep, ReadWrite +from cocotb.triggers import Trigger, Timer, ReadOnly, NextTimeStep, ReadWrite, NullTrigger from cocotb.log import SimLog from cocotb.result import TestComplete, TestError, ReturnValue +from threading import RLock class Scheduler(object): @@ -53,6 +57,11 @@ def __init__(self): self._terminate = False self._test_result = None self._readonly = None + self._entry_lock = RLock() + self._external_trigger = threading.Semaphore(1) + self._external_trigger._value = 0 + self._react_timer = None + self._external = False # Keep this last self._readwrite = self.add(self.move_to_rw()) @@ -95,19 +104,38 @@ def react(self, trigger): # another callback for the read-write part of the sim cycle, but # if we are terminating then do not allow another callback to be # scheduled - if len(self.writes) and self._readwrite is None and self._terminate is False: - self._readwrite = self.add(self.move_to_rw()) + if self._terminate is False and len(self.writes) and self._readwrite is None: + self._readwrite = self.add(self.move_to_rw()) # If the python has caused any subsequent events to fire we might # need to schedule more coroutines before we drop back into the # simulator while self._pending_adds: + self._entry_lock.acquire() coroutine = self._pending_adds.pop(0) + self._entry_lock.release() self.add(coroutine) + # if we get the lock then there was nothing in progress + # if we do not then there was so schedule a loop + # to come back in until we managed to add to the pending + # list and so can progress past this time step + result = self._external_trigger.acquire(blocking=False) + if self._external_trigger._value: + self.enable_react_delay() + else: + self._external_trigger.release() + return + def set_external(self): + """ Take the semaphore to indicate to the react later that there an external + is being added to the list + """ + self._external_trigger.acquire() + self._external_trigger._value = 1 + def playout_writes(self): if self.writes: while self.writes: @@ -121,14 +149,28 @@ def save_write(self, handle, args): def _add_trigger(self, trigger, coroutine): """Adds a new trigger which will cause the coroutine to continue when fired""" try: + self._entry_lock.acquire() self.waiting[trigger].append(coroutine) + self._entry_lock.release() + # We drop the lock before calling out to the simulator (most likely consequence of prime) trigger.prime(self.react) except Exception as e: raise TestError("Unable to prime a trigger: %s" % str(e)) def queue(self, coroutine): """Queue a coroutine for execution""" + self._entry_lock.acquire() + self._pending_adds.append(coroutine) + self._entry_lock.release() + + def queue_external(self, coroutine): + """Add a coroutine that is part of an external thread""" + # We do not need to take the lock here as are protected by the external trigger + self._entry_lock.acquire() self._pending_adds.append(coroutine) + self._entry_lock.release() + self._external_trigger._value = 0 + self._external_trigger.release() def add(self, coroutine): """Add a new coroutine. Required because we cant send to a just started generator (FIXME)""" @@ -151,22 +193,44 @@ def add(self, coroutine): self.log.debug("Queuing new coroutine %s" % coroutine.__name__) + self.log.debug("Adding %s" % coroutine.__name__) self.schedule(coroutine) return coroutine + @cocotb.decorators.coroutine + def go_to_null(self): + yield NullTrigger() + + def add_and_react(self, coroutine): + """Add a coroutine and also schedule a callback from the sim""" + self._entry_lock.acquire() + self.queue(coroutine) + + #if self._readwrite is None: + # test_coro = self.move_to_rw() + # pdb.set_trace() + #self._readwrite = self.add(test_coro) + self.add(self.go_to_null()) + + self._entry_lock.release() + def new_test(self, coroutine): self._startpoint = coroutine def remove(self, trigger): """Remove a trigger from the list of pending coroutines""" + self._entry_lock.acquire() self.waiting.pop(trigger) + self._entry_lock.release() trigger.unprime() def schedule_remove(self, coroutine, callback): """Adds the specified coroutine to the list of routines That will be removed at the end of the current loop """ + self._entry_lock.acquire() self._remove.append((coroutine, callback)) + self._entry_lock.release() def prune_routines(self): """ @@ -174,19 +238,27 @@ def prune_routines(self): execution of a parent routine """ while self._remove: + self._entry_lock.acquire() delroutine, cb = self._remove.pop(0) for trigger, waiting in self.waiting.items(): for coro in waiting: if coro is delroutine: self.log.debug("Closing %s" % str(coro)) + self._entry_lock.release() cb() + self._entry_lock.acquire() self.waiting[trigger].remove(coro) + self._entry_lock.release() coro.close() + self._entry_lock.acquire() # Clean up any triggers that no longer have pending coroutines for trigger, waiting in self.waiting.items(): if not waiting: + self._entry_lock.release() trigger.unprime() + self._entry_lock.acquire() del self.waiting[trigger] + self._entry_lock.release() def schedule(self, coroutine, trigger=None): """ @@ -307,3 +379,12 @@ def move_to_rw(self): yield ReadWrite() self._readwrite = None self.playout_writes() + + def enable_react_delay(self): + if self._react_timer is None: + self._react_timer = self.add(self.react_delay()) + + @cocotb.decorators.coroutine + def react_delay(self): + yield Timer(0) + self._react_timer = None diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 6c1b26c1..3ef7e7fb 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -28,6 +28,7 @@ """ import simulator import cocotb +import pdb from cocotb.log import SimLog diff --git a/examples/functionality/tests/test_external.py b/examples/functionality/tests/test_external.py index de86dce7..e9ac3b27 100644 --- a/examples/functionality/tests/test_external.py +++ b/examples/functionality/tests/test_external.py @@ -34,6 +34,7 @@ import threading import time import cocotb +import pdb from cocotb.result import ReturnValue, TestFailure from cocotb.triggers import Timer, Join, RisingEdge, ReadOnly, Edge from cocotb.clock import Clock @@ -100,7 +101,7 @@ def test_callable(dut): dut.log.info("Test thread created") clk_gen = Clock(dut.clk, 100) clk_gen.start() - yield Timer(10000) + yield Timer(100000) clk_gen.stop() if test_count is not 5: raise TestFailure @@ -119,7 +120,7 @@ def test_callable_fail(dut): dut.log.info("Test thread created") clk_gen = Clock(dut.clk, 100) clk_gen.start() - yield Timer(10000) + yield Timer(100000) clk_gen.stop() if test_count is not 5: raise TestFailure @@ -146,7 +147,7 @@ def clock_monitor(dut): def test_ext_call_return(dut): """Test ability to yeild on an external non cocotb coroutine decorated function""" mon = cocotb.scheduler.queue(clock_monitor(dut)) - clk_gen = Clock(dut.clk, 1000) + clk_gen = Clock(dut.clk, 100) clk_gen.start(cycles=20) value = yield external(test_ext_function)(dut) clk_gen.stop() @@ -165,14 +166,15 @@ def test_ext_call_nreturn(dut): def test_multiple_externals(dut): clk_gen = Clock(dut.clk, 100) clk_gen.start() - yield external(test_ext_function)(dut) + value = yield external(test_ext_function)(dut) dut.log.info("First one completed") - yield external(test_ext_function)(dut) + value = yield external(test_ext_function)(dut) dut.log.info("Second one completed") + clk_gen.stop() -@cocotb.test(expect_fail=True) +@cocotb.test(expect_fail=True, skip=True) def ztest_ext_exit_error(dut): """Test that a premature exit of the sim at it's request still results in the clean close down of the sim world""" yield external(test_ext_function_return)(dut) - yield Timer(100) + yield Timer(1000) From 416d64c5bebe4badcfbe7931620497a3b03aa1c5 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 20 Aug 2013 17:48:31 +0100 Subject: [PATCH 0327/2656] Issue #59: Review and fix up GIL operations in simulator --- lib/simulator/simulatormodule.c | 205 ++++++++++++++++++++++++++------ 1 file changed, 166 insertions(+), 39 deletions(-) diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 7c4a88f6..b8e0f778 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -32,8 +32,24 @@ * Uses GPI calls to interface to the simulator. */ +static int takes = 0; +static int releases = 0; + #include "simulatormodule.h" +PyGILState_STATE TAKE_GIL(void) +{ + PyGILState_STATE state = PyGILState_Ensure(); + takes ++; + return state; +} + +void DROP_GIL(PyGILState_STATE state) +{ + PyGILState_Release(state); + releases++; +} + /** * @name Callback Handling * @brief Handle a callback coming from GPI @@ -43,7 +59,7 @@ * * GILState after calling: Unknown * - * Makes one call to PyGILState_Ensure and one call to PyGILState_Release + * Makes one call to TAKE_GIL and one call to DROP_GIL * * Returns 0 on success or 1 on a failure. * @@ -71,13 +87,13 @@ int handle_gpi_callback(void *user_data) PyGILState_STATE gstate; - gstate = PyGILState_Ensure(); + gstate = TAKE_GIL(); // Python allowed if (!PyCallable_Check(callback_data_p->function)) { fprintf(stderr, "Callback fired but function isn't callable?!\n"); - PyGILState_Release(gstate); + DROP_GIL(gstate); return 1; } @@ -90,7 +106,7 @@ int handle_gpi_callback(void *user_data) fprintf(stderr, "ERROR: called callback function returned NULL\n"); PyErr_Print(); fprintf(stderr, "Failed to execute callback\n"); - PyGILState_Release(gstate); + DROP_GIL(gstate); gpi_sim_end(); return 1; } @@ -103,7 +119,7 @@ int handle_gpi_callback(void *user_data) // Free the callback data free(callback_data_p); - PyGILState_Release(gstate); + DROP_GIL(gstate); return 0; } @@ -140,7 +156,7 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) char *result; PyGILState_STATE gstate; - gstate = PyGILState_Ensure(); + gstate = TAKE_GIL(); p_callback_data callback_data_p; @@ -183,7 +199,7 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) gpi_register_readonly_callback(hdl, handle_gpi_callback, (void *)callback_data_p); PyObject *rv = Py_BuildValue("s", "OK!"); - PyGILState_Release(gstate); + DROP_GIL(gstate); FEXIT return rv; @@ -201,7 +217,7 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) char *result; PyGILState_STATE gstate; - gstate = PyGILState_Ensure(); + gstate = TAKE_GIL(); p_callback_data callback_data_p; @@ -244,7 +260,7 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) gpi_register_readwrite_callback(hdl, handle_gpi_callback, (void *)callback_data_p); PyObject *rv = Py_BuildValue("s", "OK!"); - PyGILState_Release(gstate); + DROP_GIL(gstate); FEXIT return rv; @@ -264,7 +280,7 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) gpi_sim_hdl hdl; PyGILState_STATE gstate; - gstate = PyGILState_Ensure(); + gstate = TAKE_GIL(); p_callback_data callback_data_p; @@ -307,7 +323,7 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) gpi_register_nexttime_callback(hdl, handle_gpi_callback, (void *)callback_data_p); PyObject *rv = Py_BuildValue("s", "OK!"); - PyGILState_Release(gstate); + DROP_GIL(gstate); FEXIT return rv; @@ -331,7 +347,7 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) p_callback_data callback_data_p; PyGILState_STATE gstate; - gstate = PyGILState_Ensure(); + gstate = TAKE_GIL(); Py_ssize_t numargs = PyTuple_Size(args); @@ -379,7 +395,7 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) // Check success PyObject *rv = Py_BuildValue("s", "OK!"); - PyGILState_Release(gstate); + DROP_GIL(gstate); FEXIT return rv; @@ -406,7 +422,7 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) PyGILState_STATE gstate; - gstate = PyGILState_Ensure(); + gstate = TAKE_GIL(); p_callback_data callback_data_p; @@ -456,7 +472,7 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) // Check success PyObject *rv = Py_BuildValue("s", "OK!"); - PyGILState_Release(gstate); + DROP_GIL(gstate); FEXIT return rv; @@ -468,13 +484,23 @@ static PyObject *iterate(PyObject *self, PyObject *args) gpi_sim_hdl hdl; uint32_t type; gpi_iterator_hdl result; + PyObject *res; - if (!PyArg_ParseTuple(args, "il", &type, &hdl)) + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + if (!PyArg_ParseTuple(args, "il", &type, &hdl)) { + DROP_GIL(gstate); return NULL; + } result = gpi_iterate(type, hdl); - return Py_BuildValue("l", result); + res = Py_BuildValue("l", result); + + DROP_GIL(gstate); + + return res; } @@ -482,14 +508,21 @@ static PyObject *next(PyObject *self, PyObject *args) { gpi_iterator_hdl hdl; gpi_sim_hdl result; + PyObject *res; - if (!PyArg_ParseTuple(args, "l", &hdl)) + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + if (!PyArg_ParseTuple(args, "l", &hdl)) { + DROP_GIL(gstate); return NULL; + } // It's valid for iterate to return a NULL handle, to make the Python // intuitive we simply raise StopIteration on the first iteration if (!hdl) { PyErr_SetNone(PyExc_StopIteration); + DROP_GIL(gstate); return NULL; } @@ -498,10 +531,15 @@ static PyObject *next(PyObject *self, PyObject *args) // Raise stopiteration when we're done if (!result) { PyErr_SetNone(PyExc_StopIteration); + DROP_GIL(gstate); return NULL; } - return Py_BuildValue("l", result); + res = Py_BuildValue("l", result); + + DROP_GIL(gstate); + + return res; } @@ -511,13 +549,20 @@ static PyObject *get_signal_val(PyObject *self, PyObject *args) char *result; PyObject *retstr; - if (!PyArg_ParseTuple(args, "l", &hdl)) + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + if (!PyArg_ParseTuple(args, "l", &hdl)) { + DROP_GIL(gstate); return NULL; + } result = gpi_get_signal_value_binstr((gpi_sim_hdl)hdl); retstr = Py_BuildValue("s", result); free(result); + DROP_GIL(gstate); + return retstr; } @@ -526,12 +571,22 @@ static PyObject *set_signal_val(PyObject *self, PyObject *args) { gpi_sim_hdl hdl; long value; + PyObject *res; + + PyGILState_STATE gstate; + gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "ll", &hdl, &value)) + if (!PyArg_ParseTuple(args, "ll", &hdl, &value)) { + DROP_GIL(gstate); return NULL; + } gpi_set_signal_value_int(hdl,value); - return Py_BuildValue("s", "OK!"); + res = Py_BuildValue("s", "OK!"); + + DROP_GIL(gstate); + + return res; } @@ -539,12 +594,22 @@ static PyObject *set_signal_val_str(PyObject *self, PyObject *args) { gpi_sim_hdl hdl; const char *binstr; + PyObject *res; - if (!PyArg_ParseTuple(args, "ls", &hdl, &binstr)) + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + if (!PyArg_ParseTuple(args, "ls", &hdl, &binstr)) { + DROP_GIL(gstate); return NULL; + } gpi_set_signal_value_str(hdl,binstr); - return Py_BuildValue("s", "OK!"); + res = Py_BuildValue("s", "OK!"); + + DROP_GIL(gstate); + + return res; } @@ -553,13 +618,23 @@ static PyObject *get_handle_by_name(PyObject *self, PyObject *args) const char *name; gpi_sim_hdl hdl; gpi_sim_hdl result; + PyObject *res; + + PyGILState_STATE gstate; + gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "ls", &hdl, &name)) + if (!PyArg_ParseTuple(args, "ls", &hdl, &name)) { + DROP_GIL(gstate); return NULL; + } result = gpi_get_handle_by_name(name, (gpi_sim_hdl)hdl); - return Py_BuildValue("l", result); + res = Py_BuildValue("l", result); + + DROP_GIL(gstate); + + return res; } static PyObject *get_handle_by_index(PyObject *self, PyObject *args) @@ -567,13 +642,23 @@ static PyObject *get_handle_by_index(PyObject *self, PyObject *args) uint32_t index; gpi_sim_hdl hdl; gpi_sim_hdl result; + PyObject *value; - if (!PyArg_ParseTuple(args, "li", &hdl, &index)) + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + if (!PyArg_ParseTuple(args, "li", &hdl, &index)) { + DROP_GIL(gstate); return NULL; + } result = gpi_get_handle_by_index((gpi_sim_hdl)hdl, index); - return Py_BuildValue("l", result); + value = Py_BuildValue("l", result); + + DROP_GIL(gstate); + + return value; } @@ -584,13 +669,20 @@ static PyObject *get_name_string(PyObject *self, PyObject *args) gpi_sim_hdl hdl; PyObject *retstr; - if (!PyArg_ParseTuple(args, "l", &hdl)) + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + if (!PyArg_ParseTuple(args, "l", &hdl)) { + DROP_GIL(gstate); return NULL; + } result = gpi_get_signal_name_str((gpi_sim_hdl)hdl); retstr = Py_BuildValue("s", result); free(result); + DROP_GIL(gstate); + return retstr; } @@ -601,13 +693,20 @@ static PyObject *get_type_string(PyObject *self, PyObject *args) gpi_sim_hdl hdl; PyObject *retstr; - if (!PyArg_ParseTuple(args, "l", &hdl)) + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + if (!PyArg_ParseTuple(args, "l", &hdl)) { + DROP_GIL(gstate); return NULL; + } result = gpi_get_signal_type_str((gpi_sim_hdl)hdl); retstr = Py_BuildValue("s", result); free(result); + DROP_GIL(gstate); + return retstr; } @@ -620,12 +719,17 @@ static PyObject *get_sim_time(PyObject *self, PyObject *args) uint32_t high, low; + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + gpi_get_sim_time(&high, &low); PyObject *pTuple = PyTuple_New(2); PyTuple_SetItem(pTuple, 0, PyLong_FromUnsignedLong(high)); // Note: This function “steals†a reference to o. PyTuple_SetItem(pTuple, 1, PyLong_FromUnsignedLong(low)); // Note: This function “steals†a reference to o. + DROP_GIL(gstate); + return pTuple; } @@ -653,45 +757,67 @@ static PyObject *deregister_callback(PyObject *self, PyObject *args) { gpi_sim_hdl hdl; PyObject *pSihHdl; + PyObject *value; FENTER + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + pSihHdl = PyTuple_GetItem(args, 0); hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(pSihHdl); gpi_deregister_callback(hdl); + value = Py_BuildValue("s", "OK!"); + + DROP_GIL(gstate); + FEXIT - return Py_BuildValue("s", "OK!"); + return value; } static PyObject *remove_callback(PyObject *self, PyObject *args) { gpi_sim_hdl hdl; PyObject *pSihHdl; + PyObject *value; FENTER + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + pSihHdl = PyTuple_GetItem(args, 0); hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(pSihHdl); gpi_destroy_cb_handle(hdl); + value = Py_BuildValue("s", "OK!"); + + DROP_GIL(gstate); + FEXIT - return Py_BuildValue("s", "OK!"); + return value; } static PyObject *create_callback(PyObject *self, PyObject *args) { FENTER - PyGILState_STATE gstate; - gstate = PyGILState_Ensure(); + + PyObject *value; + gpi_sim_hdl cb_hdl = gpi_create_cb_handle(); - PyGILState_Release(gstate); + PyGILState_STATE gstate; + gstate = TAKE_GIL(); - FENTER - return Py_BuildValue("l", cb_hdl); + value = Py_BuildValue("l", cb_hdl); + + DROP_GIL(gstate); + + FEXIT + return value; } static PyObject *create_clock(PyObject *self, PyObject *args) @@ -701,12 +827,13 @@ static PyObject *create_clock(PyObject *self, PyObject *args) unsigned int mcycles; PyGILState_STATE gstate; - gstate = PyGILState_Ensure(); + gstate = TAKE_GIL(); Py_ssize_t numargs = PyTuple_Size(args); if (numargs < 3) { fprintf(stderr, "Attempt to create a clock with without enough arguments!\n"); + DROP_GIL(gstate); return NULL; } @@ -722,7 +849,7 @@ static PyObject *create_clock(PyObject *self, PyObject *args) gpi_sim_hdl clk_hdl = gpi_clock_register(hdl, period, mcycles); PyObject *rv = Py_BuildValue("l", clk_hdl); - PyGILState_Release(gstate); + DROP_GIL(gstate); return rv; } From 2b8c714abc647572fb0d2bdb7da7d657daa2f4a0 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 20 Aug 2013 17:49:13 +0100 Subject: [PATCH 0328/2656] Issue #59: Fix up log level back to INFO --- cocotb/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/log.py b/cocotb/log.py index dbad657a..7f13aeed 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -62,7 +62,7 @@ def __init__(self, name): self.propagate = False logging.__init__(name) self.addHandler(hdlr) - self.setLevel(logging.DEBUG) + self.setLevel(logging.INFO) """ Need to play with this to get the path of the called back, construct our own makeRecord for this """ From bd4cc40f43fb72c26f7bea070f1010f1138b7828 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 20 Aug 2013 18:23:24 +0100 Subject: [PATCH 0329/2656] Fixes #73: new test case --- examples/Makefile | 3 +- examples/sim_exit/hdl/close_module.v | 60 ++++++ examples/sim_exit/tests/Makefile | 37 ++++ examples/sim_exit/tests/before.out | 225 ++++++++++++++++++++++ examples/sim_exit/tests/core.19928 | Bin 0 -> 2101248 bytes examples/sim_exit/tests/mem.out | 40 ++++ examples/sim_exit/tests/test_closedown.py | 42 ++++ 7 files changed, 406 insertions(+), 1 deletion(-) create mode 100644 examples/sim_exit/hdl/close_module.v create mode 100644 examples/sim_exit/tests/Makefile create mode 100644 examples/sim_exit/tests/before.out create mode 100644 examples/sim_exit/tests/core.19928 create mode 100644 examples/sim_exit/tests/mem.out create mode 100644 examples/sim_exit/tests/test_closedown.py diff --git a/examples/Makefile b/examples/Makefile index 97aab620..bf8ccf49 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -30,6 +30,7 @@ SMOKE_TESTS := demo \ comb_seq \ plusargs \ + sim_exit \ functionality/tests .PHONY: $(SMOKE_TESTS) @@ -37,7 +38,7 @@ SMOKE_TESTS := demo \ all: $(SMOKE_TESTS) $(SMOKE_TESTS): - @cd $@ && $(MAKE) + -@cd $@ && $(MAKE) clean: $(foreach TEST, $(SMOKE_TESTS), $(MAKE) -C $(TEST) clean;) diff --git a/examples/sim_exit/hdl/close_module.v b/examples/sim_exit/hdl/close_module.v new file mode 100644 index 00000000..104b88b6 --- /dev/null +++ b/examples/sim_exit/hdl/close_module.v @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2013 Potential Ventures Ltd +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Potential Ventures Ltd nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + + +`timescale 1 ps / 1 ps + +module close_module ( + input clk, + + output reg stream_in_ready, + input stream_in_valid, + input [7:0] stream_in_data, + + input stream_out_ready, + output reg [7:0] stream_out_data_comb, + output reg [7:0] stream_out_data_registered +); + +always @(posedge clk) + stream_out_data_registered <= stream_in_data; + +always @(stream_in_data) + stream_out_data_comb = stream_in_data; + +always @(stream_out_ready) + stream_in_ready = stream_out_ready; + + +initial begin + $dumpfile("waveform.vcd"); + $dumpvars(0,close_module); + + #500000 $fail_test("Test timed out, failing..."); +end + +endmodule diff --git a/examples/sim_exit/tests/Makefile b/examples/sim_exit/tests/Makefile new file mode 100644 index 00000000..5018ffb3 --- /dev/null +++ b/examples/sim_exit/tests/Makefile @@ -0,0 +1,37 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +TOPLEVEL = close_module + +PWD=$(shell pwd) + +VERILOG_SOURCES = $(PWD)/../hdl/close_module.v +MODULE=test_closedown + +include ../../../makefiles/Makefile.inc +include ../../../makefiles/Makefile.sim diff --git a/examples/sim_exit/tests/before.out b/examples/sim_exit/tests/before.out new file mode 100644 index 00000000..12a986b0 --- /dev/null +++ b/examples/sim_exit/tests/before.out @@ -0,0 +1,225 @@ + +Running test 1/8:test_yield_list +Partition of a set of 33098 objects. Total size = 2449120 bytes. + Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) + 0 12235 37 624288 25 624288 25 types.CodeType + 1 1870 6 192136 8 816424 33 dict of module + 2 3027 9 176468 7 992892 41 dict of type + 3 897 3 154844 6 1147736 47 type + 4 2313 7 153360 6 1301096 53 function + 5 507 2 114676 5 1415772 58 function, tuple + 6 2547 8 96624 4 1512396 62 tuple + 7 55 0 89656 4 1602052 65 function, module + 8 1781 5 73380 3 1675432 68 dict (no owner) + 9 176 1 68876 3 1744308 71 class +<628 more rows. Type e.g. '_.more' to view.> +End of test +Partition of a set of 33326 objects. Total size = 2478564 bytes. + Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) + 0 12235 37 624288 25 624288 25 types.CodeType + 1 1870 6 192136 8 816424 33 dict of module + 2 3027 9 176468 7 992892 40 dict of type + 3 897 3 154844 6 1147736 46 type + 4 2311 7 153216 6 1300952 52 function + 5 506 2 114604 5 1415556 57 function, tuple + 6 2541 8 96492 4 1512048 61 tuple + 7 55 0 89656 4 1601704 65 function, module + 8 1822 5 74664 3 1676368 68 dict (no owner) + 9 178 1 69044 3 1745412 70 class +<649 more rows. Type e.g. '_.more' to view.> +Running test 2/8:test_function_not_a_coroutine +Partition of a set of 33329 objects. Total size = 2479008 bytes. + Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) + 0 12235 37 624288 25 624288 25 types.CodeType + 1 1870 6 192136 8 816424 33 dict of module + 2 3027 9 176468 7 992892 40 dict of type + 3 897 3 154844 6 1147736 46 type + 4 2310 7 153144 6 1300880 52 function + 5 506 2 114604 5 1415484 57 function, tuple + 6 2541 8 96492 4 1511976 61 tuple + 7 55 0 89656 4 1601632 65 function, module + 8 1822 5 74664 3 1676296 68 dict (no owner) + 9 178 1 69044 3 1745340 70 class +<649 more rows. Type e.g. '_.more' to view.> +End of test +Partition of a set of 33353 objects. Total size = 2480976 bytes. + Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) + 0 12235 37 624288 25 624288 25 types.CodeType + 1 1870 6 192136 8 816424 33 dict of module + 2 3027 9 176468 7 992892 40 dict of type + 3 897 3 154844 6 1147736 46 type + 4 2311 7 153216 6 1300952 52 function + 5 506 2 114604 5 1415556 57 function, tuple + 6 2541 8 96492 4 1512048 61 tuple + 7 55 0 89656 4 1601704 65 function, module + 8 1825 5 74748 3 1676452 68 dict (no owner) + 9 178 1 69044 3 1745496 70 class +<652 more rows. Type e.g. '_.more' to view.> +Running test 3/8:test_coroutine_kill +Partition of a set of 33354 objects. Total size = 2481344 bytes. + Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) + 0 12235 37 624288 25 624288 25 types.CodeType + 1 1870 6 192136 8 816424 33 dict of module + 2 3027 9 176468 7 992892 40 dict of type + 3 897 3 154844 6 1147736 46 type + 4 2310 7 153144 6 1300880 52 function + 5 506 2 114604 5 1415484 57 function, tuple + 6 2541 8 96492 4 1511976 61 tuple + 7 55 0 89656 4 1601632 65 function, module + 8 1825 5 74748 3 1676380 68 dict (no owner) + 9 178 1 69044 3 1745424 70 class +<652 more rows. Type e.g. '_.more' to view.> +End of test +Partition of a set of 33422 objects. Total size = 2494636 bytes. + Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) + 0 12235 37 624288 25 624288 25 types.CodeType + 1 1870 6 192136 8 816424 33 dict of module + 2 3027 9 176468 7 992892 40 dict of type + 3 897 3 154844 6 1147736 46 type + 4 2311 7 153216 6 1300952 52 function + 5 506 2 114604 5 1415556 57 function, tuple + 6 2541 8 96492 4 1512048 61 tuple + 7 55 0 89656 4 1601704 64 function, module + 8 1843 6 75280 3 1676984 67 dict (no owner) + 9 178 1 69044 3 1746028 70 class +<651 more rows. Type e.g. '_.more' to view.> +Running test 4/8:test_adding_a_coroutine_without_starting +Partition of a set of 33438 objects. Total size = 2496172 bytes. + Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) + 0 12235 37 624288 25 624288 25 types.CodeType + 1 1869 6 192140 8 816428 33 dict of module + 2 3026 9 176412 7 992840 40 dict of type + 3 897 3 154844 6 1147684 46 type + 4 2310 7 153104 6 1300788 52 function + 5 506 2 114624 5 1415412 57 function, tuple + 6 2541 8 96436 4 1511848 61 tuple + 7 55 0 89656 4 1601504 64 function, module + 8 1841 6 75224 3 1676728 67 dict (no owner) + 9 178 1 69044 3 1745772 70 class +<662 more rows. Type e.g. '_.more' to view.> +End of test +Partition of a set of 33490 objects. Total size = 2502996 bytes. + Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) + 0 12235 37 624288 25 624288 25 types.CodeType + 1 1869 6 192140 8 816428 33 dict of module + 2 3026 9 176412 7 992840 40 dict of type + 3 897 3 154844 6 1147684 46 type + 4 2311 7 153176 6 1300860 52 function + 5 506 2 114624 5 1415484 57 function, tuple + 6 2539 8 96336 4 1511820 60 tuple + 7 55 0 89656 4 1601476 64 function, module + 8 1846 6 75364 3 1676840 67 dict (no owner) + 9 178 1 69044 3 1745884 70 class +<662 more rows. Type e.g. '_.more' to view.> +Running test 5/8:test_function_not_a_coroutine_fork +Partition of a set of 33491 objects. Total size = 2503364 bytes. + Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) + 0 12235 37 624288 25 624288 25 types.CodeType + 1 1869 6 192140 8 816428 33 dict of module + 2 3026 9 176412 7 992840 40 dict of type + 3 897 3 154844 6 1147684 46 type + 4 2310 7 153104 6 1300788 52 function + 5 506 2 114624 5 1415412 57 function, tuple + 6 2539 8 96336 4 1511748 60 tuple + 7 55 0 89656 4 1601404 64 function, module + 8 1846 6 75364 3 1676768 67 dict (no owner) + 9 178 1 69044 3 1745812 70 class +<662 more rows. Type e.g. '_.more' to view.> +End of test +Partition of a set of 33510 objects. Total size = 2505008 bytes. + Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) + 0 12235 37 624288 25 624288 25 types.CodeType + 1 1869 6 192140 8 816428 33 dict of module + 2 3026 9 176412 7 992840 40 dict of type + 3 897 3 154844 6 1147684 46 type + 4 2311 7 153176 6 1300860 52 function + 5 506 2 114624 5 1415484 57 function, tuple + 6 2539 8 96336 4 1511820 60 tuple + 7 55 0 89656 4 1601476 64 function, module + 8 1849 6 75448 3 1676924 67 dict (no owner) + 9 178 1 69044 3 1745968 70 class +<663 more rows. Type e.g. '_.more' to view.> +Running test 6/8:test_function_reentrant_clock +Partition of a set of 33511 objects. Total size = 2505372 bytes. + Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) + 0 12235 37 624288 25 624288 25 types.CodeType + 1 1869 6 192140 8 816428 33 dict of module + 2 3026 9 176412 7 992840 40 dict of type + 3 897 3 154844 6 1147684 46 type + 4 2310 7 153104 6 1300788 52 function + 5 506 2 114624 5 1415412 56 function, tuple + 6 2539 8 96336 4 1511748 60 tuple + 7 55 0 89656 4 1601404 64 function, module + 8 1849 6 75448 3 1676852 67 dict (no owner) + 9 178 1 69044 3 1745896 70 class +<663 more rows. Type e.g. '_.more' to view.> +End of test +Partition of a set of 33694 objects. Total size = 2534072 bytes. + Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) + 0 12235 36 624288 25 624288 25 types.CodeType + 1 1869 6 192140 8 816428 32 dict of module + 2 3026 9 176412 7 992840 39 dict of type + 3 897 3 154844 6 1147684 45 type + 4 2311 7 153176 6 1300860 51 function + 5 506 2 114624 5 1415484 56 function, tuple + 6 2539 8 96336 4 1511820 60 tuple + 7 55 0 89656 4 1601476 63 function, module + 8 1888 6 76540 3 1678016 66 dict (no owner) + 9 178 1 69044 3 1747060 69 class +<665 more rows. Type e.g. '_.more' to view.> +Running test 7/8:simple_test +Partition of a set of 33695 objects. Total size = 2534440 bytes. + Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) + 0 12235 36 624288 25 624288 25 types.CodeType + 1 1869 6 192140 8 816428 32 dict of module + 2 3026 9 176412 7 992840 39 dict of type + 3 897 3 154844 6 1147684 45 type + 4 2310 7 153104 6 1300788 51 function + 5 506 2 114624 5 1415412 56 function, tuple + 6 2539 8 96336 4 1511748 60 tuple + 7 55 0 89656 4 1601404 63 function, module + 8 1888 6 76540 3 1677944 66 dict (no owner) + 9 178 1 69044 3 1746988 69 class +<665 more rows. Type e.g. '_.more' to view.> +End of test +Partition of a set of 33707 objects. Total size = 2535580 bytes. + Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) + 0 12235 36 624288 25 624288 25 types.CodeType + 1 1869 6 192140 8 816428 32 dict of module + 2 3026 9 176412 7 992840 39 dict of type + 3 897 3 154844 6 1147684 45 type + 4 2311 7 153176 6 1300860 51 function + 5 506 2 114624 5 1415484 56 function, tuple + 6 2539 8 96336 4 1511820 60 tuple + 7 56 0 90176 4 1601996 63 function, module + 8 1891 6 76624 3 1678620 66 dict (no owner) + 9 178 1 69044 3 1747664 69 class +<665 more rows. Type e.g. '_.more' to view.> +Running test 8/8:discover_module_values +Partition of a set of 33708 objects. Total size = 2535944 bytes. + Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) + 0 12235 36 624288 25 624288 25 types.CodeType + 1 1869 6 192140 8 816428 32 dict of module + 2 3026 9 176412 7 992840 39 dict of type + 3 897 3 154844 6 1147684 45 type + 4 2310 7 153104 6 1300788 51 function + 5 506 2 114624 5 1415412 56 function, tuple + 6 2539 8 96336 4 1511748 60 tuple + 7 56 0 90176 4 1601924 63 function, module + 8 1891 6 76624 3 1678548 66 dict (no owner) + 9 178 1 69044 3 1747592 69 class +<664 more rows. Type e.g. '_.more' to view.> +End of test +Partition of a set of 33792 objects. Total size = 2547164 bytes. + Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) + 0 12235 36 624288 25 624288 25 types.CodeType + 1 1869 6 192140 8 816428 32 dict of module + 2 3026 9 176412 7 992840 39 dict of type + 3 897 3 154844 6 1147684 45 type + 4 2311 7 153176 6 1300860 51 function + 5 506 1 114624 5 1415484 56 function, tuple + 6 2539 8 96336 4 1511820 59 tuple + 7 57 0 90696 4 1602516 63 function, module + 8 1901 6 76904 3 1679420 66 dict (no owner) + 9 133 0 69160 3 1748580 69 logging.Logger +<665 more rows. Type e.g. '_.more' to view.> \ No newline at end of file diff --git a/examples/sim_exit/tests/core.19928 b/examples/sim_exit/tests/core.19928 new file mode 100644 index 0000000000000000000000000000000000000000..7f5255ebd2b73a31396e377b0c0e1dc6e688e960 GIT binary patch literal 2101248 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
    eojFPV_4gmAzx*ZG1W}j=P1`!lId16Auz@Q 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 QYEsUfXbwelzD@K01H7eW8vp Date: Tue, 20 Aug 2013 18:43:59 +0100 Subject: [PATCH 0330/2656] Issue #73: Make test more complex --- examples/sim_exit/tests/test_closedown.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/examples/sim_exit/tests/test_closedown.py b/examples/sim_exit/tests/test_closedown.py index de456cab..0700c5dd 100644 --- a/examples/sim_exit/tests/test_closedown.py +++ b/examples/sim_exit/tests/test_closedown.py @@ -35,8 +35,26 @@ from cocotb.triggers import Timer, Join, RisingEdge from cocotb.clock import Clock +def test_read(dut): + global test_count + dut.log.info("Inside test_read") + while test_count is not 5: + yield RisingEdge(dut.clk) + test_count += 1 + +@cocotb.coroutine +def run_external(dut): + yield cocotb.external(test_read)(dut) + +@cocotb.coroutine +def clock_mon(dut): + yield RisingEdge(dut.clk) @cocotb.test(expect_fail=True) def test_failure_from_system_task(dut): """Allow the dut to call $fail_test() from verilog""" + clock = Clock(dut.clk, 100) + clock.start() + coro = cocotb.fork(clock_mon(dut)) + extern = cocotb.fork(run_external(dut)) yield Timer(10000000) From efe458decae39385682eefefe23a0356cabf869f Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 20 Aug 2013 19:09:32 +0100 Subject: [PATCH 0331/2656] Fixes #74: Add extra check on expect_error flag --- cocotb/regression.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 377f450b..65605ce8 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -135,13 +135,18 @@ def handle_result(self, result): classname=self._running_test.module, time=repr(time.time() - self._running_test.start_time) ) - if isinstance(result, TestSuccess) and not self._running_test.expect_fail: + if isinstance(result, TestSuccess) and not self._running_test.expect_fail and not self._running_test.expect_error: self.log.info("Test Passed: %s" % self._running_test.funcname) elif isinstance(result, TestFailure) and self._running_test.expect_fail: self.log.info("Test failed as expected: %s (result was %s)" % ( self._running_test.funcname, result.__class__.__name__)) + elif isinstance(result, TestSuccess) and self._running_test.expect_error: + self.log.error("Test passed but we expected an error: %s (result was %s)" % ( + self._running_test.funcname, result.__class__.__name__)) + self.xunit.add_failure(stdout=str(result), stderr="\n".join(self._running_test.error_messages)) + elif isinstance(result, TestSuccess): self.log.error("Test passed but we expected a failure: %s (result was %s)" % ( self._running_test.funcname, result.__class__.__name__)) From 232a57974e7bf8c241f8427236eca303a153a01a Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 21 Aug 2013 11:15:56 +0100 Subject: [PATCH 0332/2656] Fixes #75: Propogate simulator failues and raise exceptions on trigger creation --- cocotb/triggers.py | 26 ++++++++++++------ lib/simulator/simulatormodule.c | 25 +++++++++++------- lib/vpi_shim/gpi_vpi.c | 47 ++++++++++++++++++++++++--------- 3 files changed, 67 insertions(+), 31 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 3ef7e7fb..8c421acd 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -111,7 +111,8 @@ def __init__(self, time_ps): def prime(self, callback): """Register for a timed callback""" - simulator.register_timed_callback(self.cbhdl, self.time_ps, callback, self) + if simulator.register_timed_callback(self.cbhdl, self.time_ps, callback, self): + raise TestError("Unable set up Timer Trigger") def __str__(self): return self.__class__.__name__ + "(%dps)" % self.time_ps @@ -126,7 +127,8 @@ def __init__(self, signal): def prime(self, callback): """Register notification of a value change via a callback""" - simulator.register_value_change_callback(self.chbdl, self.signal._handle, callback, self) + if simulator.register_value_change_callback(self.chbdl, self.signal._handle, callback, self): + raise TestError("Unable set up Edge Trigger") def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal.name @@ -140,7 +142,8 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): - simulator.register_readonly_callback(self.cbhdl, callback, self) + if simulator.register_readonly_callback(self.cbhdl, callback, self): + raise TestError("Unable set up ReadOnly Trigger") def __str__(self): return self.__class__.__name__ + "(readonly)" @@ -154,7 +157,8 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): - simulator.register_rwsynch_callback(self.cbhdl, callback, self) + if simulator.register_rwsynch_callback(self.cbhdl, callback, self): + raise TestError("Unable set up ReadWrite Trigger") def __str__(self): return self.__class__.__name__ + "(readwritesync)" @@ -186,9 +190,12 @@ def _check(obj): if self.signal.value: self._callback(self) else: - simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self) + if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self): + raise TestError("Unable set up RisingEdge Trigger") + + if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self): + raise TestError("Unable set up RisingEdge Trigger") - simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self) def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal.name @@ -212,9 +219,12 @@ def _check(obj): self._callback(self) return - simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self) + if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self): + raise TestError("Unable set up ClockCycles Trigger") + - simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self) + if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self): + raise TestError("Unable set up ClockCycles Trigger") def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal.name diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index b8e0f778..08183308 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -154,6 +154,7 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) PyObject *handle; gpi_sim_hdl hdl; char *result; + int ret; PyGILState_STATE gstate; gstate = TAKE_GIL(); @@ -196,9 +197,9 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; callback_data_p->cb_hdl = hdl; - gpi_register_readonly_callback(hdl, handle_gpi_callback, (void *)callback_data_p); + ret = gpi_register_readonly_callback(hdl, handle_gpi_callback, (void *)callback_data_p); - PyObject *rv = Py_BuildValue("s", "OK!"); + PyObject *rv = Py_BuildValue("i", ret); DROP_GIL(gstate); FEXIT @@ -215,6 +216,7 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) PyObject *handle; gpi_sim_hdl hdl; char *result; + int ret; PyGILState_STATE gstate; gstate = TAKE_GIL(); @@ -257,9 +259,9 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; callback_data_p->cb_hdl = hdl; - gpi_register_readwrite_callback(hdl, handle_gpi_callback, (void *)callback_data_p); + ret = gpi_register_readwrite_callback(hdl, handle_gpi_callback, (void *)callback_data_p); - PyObject *rv = Py_BuildValue("s", "OK!"); + PyObject *rv = Py_BuildValue("i", ret); DROP_GIL(gstate); FEXIT @@ -278,6 +280,7 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) PyObject *retstr; PyObject *handle; gpi_sim_hdl hdl; + int ret; PyGILState_STATE gstate; gstate = TAKE_GIL(); @@ -320,9 +323,9 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; callback_data_p->cb_hdl = hdl; - gpi_register_nexttime_callback(hdl, handle_gpi_callback, (void *)callback_data_p); + ret = gpi_register_nexttime_callback(hdl, handle_gpi_callback, (void *)callback_data_p); - PyObject *rv = Py_BuildValue("s", "OK!"); + PyObject *rv = Py_BuildValue("i", ret); DROP_GIL(gstate); FEXIT @@ -343,6 +346,7 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) PyObject *handle; gpi_sim_hdl hdl; uint64_t time_ps; + int ret; p_callback_data callback_data_p; @@ -391,10 +395,10 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; callback_data_p->cb_hdl = hdl; - gpi_register_timed_callback(hdl, handle_gpi_callback, (void *)callback_data_p, time_ps); + ret = gpi_register_timed_callback(hdl, handle_gpi_callback, (void *)callback_data_p, time_ps); // Check success - PyObject *rv = Py_BuildValue("s", "OK!"); + PyObject *rv = Py_BuildValue("i", ret); DROP_GIL(gstate); FEXIT @@ -418,6 +422,7 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) PyObject *retstr; PyObject *handle; gpi_sim_hdl hdl; + int ret; @@ -467,10 +472,10 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; callback_data_p->cb_hdl = hdl; - gpi_register_value_change_callback(hdl, handle_gpi_callback, (void *)callback_data_p, sig_hdl); + ret = gpi_register_value_change_callback(hdl, handle_gpi_callback, (void *)callback_data_p, sig_hdl); // Check success - PyObject *rv = Py_BuildValue("s", "OK!"); + PyObject *rv = Py_BuildValue("i", ret); DROP_GIL(gstate); FEXIT diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 15227a14..57d5b5e8 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -152,11 +152,13 @@ static inline int __gpi_register_cb(p_vpi_cb_user_data user, p_cb_data cb_data) * before getting the new one */ vpiHandle new_hdl = vpi_register_cb(cb_data); + int ret = 0; if (!new_hdl) { LOG_CRITICAL("VPI: Unable to register callback a handle for VPI type %s(%d)\n", vpi_reason_to_string(cb_data->reason), cb_data->reason); check_vpi_error(); + ret = -1; } if (user->cb_hdl != NULL) @@ -164,7 +166,7 @@ static inline int __gpi_register_cb(p_vpi_cb_user_data user, p_cb_data cb_data) user->cb_hdl = new_hdl; - return 0; + return ret; } static inline p_vpi_cb_user_data __gpi_alloc_user(void) @@ -538,11 +540,17 @@ static int32_t handle_vpi_callback(p_cb_data cb_data) */ gpi_sim_hdl gpi_create_cb_handle(void) { + gpi_sim_hdl ret = NULL; FENTER + p_vpi_cb_user_data user_data = __gpi_alloc_user(); - user_data->state = VPI_FREE; + if (user_data) { + user_data->state = VPI_FREE; + ret = &user_data->gpi_hdl; + } + FEXIT - return &user_data->gpi_hdl; + return ret; } /* Destroys the memory associated with the sim handle @@ -659,6 +667,7 @@ int gpi_register_value_change_callback(gpi_sim_hdl cb, s_vpi_time vpi_time_s; s_vpi_value vpi_value_s; p_vpi_cb_user_data user_data; + int ret; user_data = gpi_container_of(cb, s_vpi_cb_user_data, gpi_hdl); @@ -677,12 +686,12 @@ int gpi_register_value_change_callback(gpi_sim_hdl cb, cb_data_s.value = &user_data->cb_value; cb_data_s.user_data = (char *)user_data; - __gpi_register_cb(user_data, &cb_data_s); + ret = __gpi_register_cb(user_data, &cb_data_s); user_data->state = VPI_PRIMED; FEXIT - return 0; + return ret; } @@ -694,6 +703,7 @@ int gpi_register_readonly_callback(gpi_sim_hdl cb, s_cb_data cb_data_s; s_vpi_time vpi_time_s; p_vpi_cb_user_data user_data; + int ret; user_data = gpi_container_of(cb, s_vpi_cb_user_data, gpi_hdl); @@ -712,11 +722,11 @@ int gpi_register_readonly_callback(gpi_sim_hdl cb, cb_data_s.value = NULL; cb_data_s.user_data = (char *)user_data; - __gpi_register_cb(user_data, &cb_data_s); + ret = __gpi_register_cb(user_data, &cb_data_s); user_data->state = VPI_PRIMED; FEXIT - return 0; + return ret; } int gpi_register_readwrite_callback(gpi_sim_hdl cb, @@ -727,6 +737,7 @@ int gpi_register_readwrite_callback(gpi_sim_hdl cb, s_cb_data cb_data_s; s_vpi_time vpi_time_s; p_vpi_cb_user_data user_data; + int ret; user_data = gpi_container_of(cb, s_vpi_cb_user_data, gpi_hdl); @@ -745,11 +756,11 @@ int gpi_register_readwrite_callback(gpi_sim_hdl cb, cb_data_s.value = NULL; cb_data_s.user_data = (char *)user_data; - __gpi_register_cb(user_data, &cb_data_s); + ret = __gpi_register_cb(user_data, &cb_data_s); user_data->state = VPI_PRIMED; FEXIT - return 0; + return ret; } int gpi_register_nexttime_callback(gpi_sim_hdl cb, @@ -760,6 +771,7 @@ int gpi_register_nexttime_callback(gpi_sim_hdl cb, s_cb_data cb_data_s; s_vpi_time vpi_time_s; p_vpi_cb_user_data user_data; + int ret; user_data = gpi_container_of(cb, s_vpi_cb_user_data, gpi_hdl); @@ -778,11 +790,11 @@ int gpi_register_nexttime_callback(gpi_sim_hdl cb, cb_data_s.value = NULL; cb_data_s.user_data = (char *)user_data; - __gpi_register_cb(user_data, &cb_data_s); + ret = __gpi_register_cb(user_data, &cb_data_s); user_data->state = VPI_PRIMED; FEXIT - return 0; + return ret; } int gpi_register_timed_callback(gpi_sim_hdl cb, @@ -794,6 +806,7 @@ int gpi_register_timed_callback(gpi_sim_hdl cb, s_cb_data cb_data_s; s_vpi_time vpi_time_s; p_vpi_cb_user_data user_data; + int ret; user_data = gpi_container_of(cb, s_vpi_cb_user_data, gpi_hdl); @@ -812,12 +825,12 @@ int gpi_register_timed_callback(gpi_sim_hdl cb, cb_data_s.value = NULL; cb_data_s.user_data = (char *)user_data; - __gpi_register_cb(user_data, &cb_data_s); + ret = __gpi_register_cb(user_data, &cb_data_s); user_data->state = VPI_PRIMED; FEXIT - return 0; + return ret; } int gpi_register_sim_start_callback(gpi_sim_hdl cb, @@ -842,6 +855,10 @@ int gpi_register_sim_start_callback(gpi_sim_hdl cb, cb_data_s.value = NULL; cb_data_s.user_data = (char *)user_data; + /* We ignore the return value here as VCS does some silly + * things on comilation that means it tries to run through + * the vlog_startup_routines and so call this routine + */ __gpi_register_cb(user_data, &cb_data_s); user_data->state = VPI_PRIMED; @@ -872,6 +889,10 @@ int gpi_register_sim_end_callback(gpi_sim_hdl cb, cb_data_s.value = NULL; cb_data_s.user_data = (char *)user_data; + /* We ignore the return value here as VCS does some silly + * things on comilation that means it tries to run through + * the vlog_startup_routines and so call this routine + */ __gpi_register_cb(user_data, &cb_data_s); user_data->state = VPI_PRIMED; From 78bbfc21d7ffcdd55f2aabebc9be03e0a90db1ca Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 21 Aug 2013 11:33:37 +0100 Subject: [PATCH 0333/2656] Cleanup: Fixup messages to LOG_xxx to not have a new line since this is added by the python logging routines --- lib/vpi_shim/gpi_vpi.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 57d5b5e8..b880da64 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -136,7 +136,7 @@ static int __check_vpi_error(const char *func, long line) } gpi_log("cocotb.gpi", loglevel, __FILE__, func, line, - "VPI Error level %d\nPROD %s\nCODE %s\nFILE %s\n", + "VPI Error level %d\nPROD %s\nCODE %s\nFILE %s", info.message, info.product, info.code, info.file); #endif @@ -155,7 +155,7 @@ static inline int __gpi_register_cb(p_vpi_cb_user_data user, p_cb_data cb_data) int ret = 0; if (!new_hdl) { - LOG_CRITICAL("VPI: Unable to register callback a handle for VPI type %s(%d)\n", + LOG_CRITICAL("VPI: Unable to register callback a handle for VPI type %s(%d)", vpi_reason_to_string(cb_data->reason), cb_data->reason); check_vpi_error(); ret = -1; @@ -198,7 +198,7 @@ static gpi_sim_hdl gpi_alloc_handle(void) { gpi_sim_hdl new_hdl = calloc(1, sizeof(*new_hdl)); if (!new_hdl) { - LOG_CRITICAL("VPI: Could not allocate handle\n"); + LOG_CRITICAL("VPI: Could not allocate handle"); exit(1); } @@ -255,13 +255,13 @@ gpi_sim_hdl gpi_get_root_handle(const char* name) error: - LOG_CRITICAL("VPI: Couldn't find root handle %s\n", name); + LOG_CRITICAL("VPI: Couldn't find root handle %s", name); iterator = vpi_iterate(vpiModule, NULL); for (root = vpi_scan(iterator); root != NULL; root = vpi_scan(iterator)) { - LOG_CRITICAL("VPI: Toplevel instances: %s != %s...\n", name, vpi_get_str(vpiFullName, root)); + LOG_CRITICAL("VPI: Toplevel instances: %s != %s...", name, vpi_get_str(vpiFullName, root)); if (name == NULL || !strcmp(name, vpi_get_str(vpiFullName, root))) break; @@ -448,7 +448,7 @@ static char *gpi_copy_name(const char *name) if (name) len = strlen(name) + 1; else { - LOG_CRITICAL("VPI: NULL came back from VPI\n"); + LOG_CRITICAL("VPI: NULL came back from VPI"); len = strlen(null); name = null; } @@ -610,7 +610,7 @@ static int gpi_free_one_time(p_vpi_cb_user_data user_data) int rc; vpiHandle cb_hdl = user_data->cb_hdl; if (!cb_hdl) { - LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING\n"); + LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); exit(1); } From bacff8bd188c178c13fb382de6d3ec8242c3a09b Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 21 Aug 2013 12:05:00 +0100 Subject: [PATCH 0334/2656] Cleanup: Modify raise_error to have more of the stack frame that is of interest --- cocotb/result.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/result.py b/cocotb/result.py index cec88768..915fb624 100644 --- a/cocotb/result.py +++ b/cocotb/result.py @@ -30,7 +30,7 @@ def raise_error(obj, msg): buff = StringIO() - lastframe = sys._getframe(2) + lastframe = sys._getframe(1) traceback.print_stack(lastframe, file=buff) obj.log.error("%s\n%s" % (msg, buff.getvalue())) exception = TestError(msg) From 738fbbd4c15e3b89ff6b270c26e521b6550fc708 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 21 Aug 2013 13:27:21 +0100 Subject: [PATCH 0335/2656] Issue #77: Remove old examples --- examples/comb_seq/Makefile | 37 ----------- examples/comb_seq/issue12.py | 124 ----------------------------------- examples/comb_seq/issue12.v | 63 ------------------ examples/demo/Makefile | 39 ----------- examples/demo/counter.v | 87 ------------------------ examples/demo/demo.py | 111 ------------------------------- 6 files changed, 461 deletions(-) delete mode 100644 examples/comb_seq/Makefile delete mode 100644 examples/comb_seq/issue12.py delete mode 100644 examples/comb_seq/issue12.v delete mode 100644 examples/demo/Makefile delete mode 100644 examples/demo/counter.v delete mode 100644 examples/demo/demo.py diff --git a/examples/comb_seq/Makefile b/examples/comb_seq/Makefile deleted file mode 100644 index 88128217..00000000 --- a/examples/comb_seq/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - - -TOPLEVEL = issue12 - -PWD=$(shell pwd) - -VERILOG_SOURCES = $(PWD)/issue12.v -MODULE=issue12 - -include ../../makefiles/Makefile.inc -include ../../makefiles/Makefile.sim diff --git a/examples/comb_seq/issue12.py b/examples/comb_seq/issue12.py deleted file mode 100644 index 848f80ef..00000000 --- a/examples/comb_seq/issue12.py +++ /dev/null @@ -1,124 +0,0 @@ -#!/bin/python - -''' Copyright (c) 2013 Potential Ventures Ltd -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - - -""" - Test bench for issue 12 - - - Expected behaviour over 6 clock cycles - - start of day - ready <= 0 - - - clock tick 1 - drive ready to 1 - drive 1 onto data in - data_out_comb changes to 1 - - clock tick 2 - drive ready to 0 - data_out_registered changes to 1 (driven by process inside the sim) - - etc. - -""" - -import cocotb -from cocotb.decorators import coroutine -from cocotb.triggers import Timer, Edge, Event, RisingEdge, ReadOnly -from cocotb.clock import Clock - -@coroutine -def ready_fiddler(clock, ready): - """Assert ready every other clock cycle""" - v = 0 - ready <= v - while True: - yield RisingEdge(clock) - v = not v - ready <= v - -@coroutine -def driver(clock, ready, data): - """Drives incrementing values onto a bus each time ready is high""" - data <= 0 - while True: - yield RisingEdge(ready) - data <= data.value.value + 1 - -@cocotb.test() -def issue12(dut): - """Test highlighting the ReadWriteSync issue (issue12 in testsauce repo)""" - dut.log.info("Test got DUT:" + str(dut)) - - # convenience - clock = dut.clk - ready = dut.stream_in_ready - dout_comb = dut.stream_out_data_comb - dout_regd = dut.stream_out_data_registered - - clock <= 0 - - dut.stream_out_ready <= 0 - - real_clock = Clock(clock, 1000) - yield Timer(1000) - - # kick off the coroutines - rdys = cocotb.scheduler.add(ready_fiddler(dut.clk, dut.stream_out_ready)) - drvr = cocotb.scheduler.add(driver(dut.clk, dut.stream_in_ready, dut.stream_in_data)) - - yield Timer(1000) - real_clock.start(12) - - expected_comb = 0 - expected_regd = 0 - failed = 0 - - for clock_tick in range(6): - yield RisingEdge(dut.clk) - yield ReadOnly() - if ready.value.value: expected_comb += 1 - dut.log.info("ready: %s\tdout_comb: %d\tdout_regd: %d" % (ready.value.value, dout_comb.value.value, dout_regd.value.value)) - if dout_comb.value.value != expected_comb: - dut.log.error("Expected dout_comb to be %d but got %d" % (expected_comb, dout_comb.value.value)) - failed += 1 - if dout_regd.value.value != expected_regd: - dut.log.error("Expected dout_regd to be %d but got %d" % (expected_regd, dout_regd.value.value)) - failed += 1 - expected_regd = expected_comb - - # FIXME the sim exits when the cgen finishes but we still want this code to run - # If caching writes then this code is run, if not then we exit prematurely - dut.log.info("test complete!") - if failed: - dut.log.critical("%d failures!" % (failed)) - else: - dut.log.warn("Test Passed") - diff --git a/examples/comb_seq/issue12.v b/examples/comb_seq/issue12.v deleted file mode 100644 index ea176e13..00000000 --- a/examples/comb_seq/issue12.v +++ /dev/null @@ -1,63 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2013 Potential Ventures Ltd -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of Potential Ventures Ltd nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -//----------------------------------------------------------------------------- - - -`timescale 1 ps / 1 ps -/* - - Looking at combinatorial and registered outputs. - -*/ - -module issue12 ( - input clk, - - output reg stream_in_ready, - input stream_in_valid, - input [7:0] stream_in_data, - - input stream_out_ready, - output reg [7:0] stream_out_data_comb, - output reg [7:0] stream_out_data_registered -); - -always @(posedge clk) - stream_out_data_registered <= stream_in_data; - -always @(stream_in_data) - stream_out_data_comb = stream_in_data; - -always @(stream_out_ready) - stream_in_ready = stream_out_ready; - - -initial begin - $dumpfile("waveform.vcd"); - $dumpvars(0,issue12); -end - -endmodule diff --git a/examples/demo/Makefile b/examples/demo/Makefile deleted file mode 100644 index 3d0fcfca..00000000 --- a/examples/demo/Makefile +++ /dev/null @@ -1,39 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - - -# FIXME these should come from some cunning script, possibly from the FDK :) -TOPLEVEL = first_counter - -PWD=$(shell pwd) - -VERILOG_SOURCES = $(PWD)/counter.v -MODULE=demo - -include ../../makefiles/Makefile.inc -include ../../makefiles/Makefile.sim - diff --git a/examples/demo/counter.v b/examples/demo/counter.v deleted file mode 100644 index 7e95350a..00000000 --- a/examples/demo/counter.v +++ /dev/null @@ -1,87 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2013 Potential Ventures Ltd -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of Potential Ventures Ltd nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -//----------------------------------------------------------------------------- - -//----------------------------------------------------- -// This is my second Verilog Design -// Design Name : first_counter -// File Name : first_counter.v -// Function : This is a 4 bit up-counter with -// Synchronous active high reset and -// with active high enable signal -//----------------------------------------------------- -module first_counter ( -clock , // Clock input of the design -reset , // active high, synchronous Reset input -enable , // Active high enable signal for counter -counter_out, // 4 bit vector output of the counter -test_in, // test input -test_out // test output -); // End of port list -//-------------Input Ports----------------------------- -input clock ; -input reset ; -input enable ; -input test_in ; -//-------------Output Ports---------------------------- -output [3:0] counter_out ; -output test_out ; -//-------------Input ports Data Type------------------- -// By rule all the input ports should be wires -wire clock ; -wire reset ; -wire enable ; -wire test_in ; -//-------------Output Ports Data Type------------------ -// Output port can be a storage element (reg) or a wire -reg [3:0] counter_out ; -reg test_out ; - -initial begin - $dumpfile("dump.vcd"); - $dumpvars(0,first_counter); -end - -//------------Code Starts Here------------------------- -// Since this counter is a positive edge trigged one, -// We trigger the below block with respect to positive -// edge of the clock. -always @ (posedge clock) -begin : COUNTER // Block Name - // At every rising edge of clock we check if reset is active - // If active, we load the counter output with 4'b0000 - if (reset == 1'b1) begin - counter_out <= #1 4'b0000; - test_out <= #1 0; - end - // If enable is active, then we increment the counter - else if (enable == 1'b1) begin - counter_out <= #1 counter_out + 1; - test_out <= test_in; - end -end // End of Block COUNTER - -endmodule // End of Module counter diff --git a/examples/demo/demo.py b/examples/demo/demo.py deleted file mode 100644 index 7d82c261..00000000 --- a/examples/demo/demo.py +++ /dev/null @@ -1,111 +0,0 @@ -#!/bin/python - -''' Copyright (c) 2013 Potential Ventures Ltd -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - -""" - Example usage of the testbench framework -""" - -import cocotb -from cocotb.decorators import coroutine -from cocotb.triggers import Timer, Edge, Event - -@coroutine -def clock_generator(signal): - for i in range(10): - signal <= 0 - yield Timer(1000) - signal <= 1 - yield Timer(1000) - signal.log.warning("Clock generator finished!") - -@coroutine -def clock_monitor(signal, output): - while True: - yield Edge(signal) - signal.log.info("Edge triggered: %s : %s = %s" % (signal.getvalue().value, str(output), output.getvalue().value)) - - -@coroutine -def reset_dut(clock, reset, enable): - clock_ticks = 0 - reset <= 1 - enable <= 0 - while True: - yield Edge(clock) - clock_ticks += 1 - if clock_ticks >= 4: - reset <= 0 - enable <= 1 - break - reset.log.info("Released reset: %s" % reset.getvalue()) - - -@coroutine -def waiting_coroutine(some_event): - some_event.log.info("Putting waiting coroutine to sleep until this event fires") - yield some_event.wait() - some_event.log.info("Coroutine woke up again! Awesome") - -@cocotb.test() -def example_test(dut): - """This is an example test""" - dut.log.info("Example test got DUT:" + str(dut)) - - yield Timer(10000) - - clk = dut.clock - enable = dut.enable - reset = dut.reset - count = dut.counter_out - - dut.log.info(str(clk)) - - cgen = cocotb.scheduler.add(clock_generator(clk)) - yield reset_dut(clk, reset, enable) - dut.log.info("Reset DUT complete, continuing test...") - cmon = cocotb.scheduler.add(clock_monitor(clk, count)) - - dut.log.info("Blocking test until the clock generator finishes...") - yield cgen.join() - - sync = Event() - cocotb.scheduler.add(waiting_coroutine(sync)) - yield Timer(10000) - dut.log.info("Waking up the waiting coroutine with an event...") - sync.set() - yield Timer(10000) - - - result = yield Timer(1000000) - dut.log.warning("test complete!") - - -@cocotb.test() -def example_test2(dut): - """This is another example test""" - result = yield Timer(1000000) - dut.log.warning("test complete!") From 9db6dfcdfd06fec7fa3956898055c4093f4aae9e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 21 Aug 2013 15:45:42 +0100 Subject: [PATCH 0336/2656] Fixes #78: Implement single call from vpi upto __init__.py --- cocotb/__init__.py | 19 ++++++++++++++----- include/embed.h | 2 +- include/gpi.h | 6 ++++++ lib/embed/gpi_embed.c | 29 +++++++++++------------------ lib/vpi_shim/gpi_vpi.c | 4 ++-- 5 files changed, 34 insertions(+), 26 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 880759c4..6671809c 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -116,12 +116,21 @@ def _initialise_testbench(root_handle): _rlock.release() return True - -def _fail_test(message): - """Function that can be called externally to fail a test""" +def _sim_event(level, message): + """Function that can be called externally to signal an event""" + SIM_INFO = 0 + SIM_TEST_FAIL = 1 + SIM_FAIL = 2 from cocotb.result import TestFailure - scheduler.log.error("Failing test at simulator request") - scheduler.finish_scheduler(TestFailure("Failure from external source: %s" % message)) + + if level is SIM_TEST_FAIL: + scheduler.log.error("Failing test at simulator request") + scheduler.finish_test(TestFailure("Failure from external source: %s" % message)) + elif level is SIM_FAIL: + scheduler.log.error("Failing test at simulator request before test run completion: %s" % message) + scheduler.finish_scheduler(TestFailure("Failing test at simulator request before test run completion")) + else: + scheduler.log.error("Unsupported sim event") def process_plusargs(): diff --git a/include/embed.h b/include/embed.h index 0e0fc96e..c43acd46 100644 --- a/include/embed.h +++ b/include/embed.h @@ -34,6 +34,6 @@ extern void embed_init_python(void); extern void embed_sim_init(gpi_sim_info_t *info); -extern void embed_sim_end(void); +extern void embed_sim_event(gpi_event_t level, const char *msg); #endif /* COCOTB_EMBED_H_ */ diff --git a/include/gpi.h b/include/gpi.h index b6d6f955..ec873961 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -76,6 +76,12 @@ we have to create a process with the signal on the sensitivity list to imitate a EXTERN_C_START +typedef enum gpi_event_e { + SIM_INFO = 0, + SIM_TEST_FAIL = 1, + SIM_FAIL = 2, +} gpi_event_t; + typedef struct gpi_sim_info_s { int32_t argc; diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 66d99c75..14f81bdf 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -34,7 +34,7 @@ static PyThreadState *gtstate; static char progname[] = "cocotb"; static PyObject *thread_dict; -static PyObject *pFailTestFn; +static PyObject *pEventFn; /** @@ -185,14 +185,14 @@ void embed_sim_init(gpi_sim_info_t *info) // Now that logging has been set up ok we initialise the testbench // Hold onto a reference to our _fail_test function - pFailTestFn = PyObject_GetAttrString(cocotb_module, "_fail_test"); + pEventFn = PyObject_GetAttrString(cocotb_module, "_sim_event"); - if (!PyCallable_Check(pFailTestFn)) { + if (!PyCallable_Check(pEventFn)) { PyErr_Print(); - fprintf(stderr, "cocotb._fail_test is not callable"); + fprintf(stderr, "cocotb._sim_event is not callable"); goto cleanup; } - Py_INCREF(pFailTestFn); + Py_INCREF(pEventFn); cocotb_init = PyObject_GetAttrString(cocotb_module, "_initialise_testbench"); // New reference @@ -228,27 +228,20 @@ void embed_sim_init(gpi_sim_info_t *info) PyGILState_Release(gstate); } -void embed_sim_end(void) -{ - FENTER - /* Indicate to the upper layer that this needs to close down */ - LOG_WARN("Simulator requested orderly shutdown"); - FEXIT -} - - -void fail_test(const char *message) +void embed_sim_event(gpi_event_t level, const char *msg) { FENTER + /* Indicate to the upper layer a sim event occoured */ PyGILState_STATE gstate; gstate = PyGILState_Ensure(); LOG_WARN("Failing the test at simulator request!"); - PyObject *fArgs = PyTuple_New(1); - PyTuple_SetItem(fArgs, 0, PyString_FromString(message)); - PyObject *pValue = PyObject_Call(pFailTestFn, fArgs, NULL); + PyObject *fArgs = PyTuple_New(2); + PyTuple_SetItem(fArgs, 0, PyInt_FromLong(level)); + PyTuple_SetItem(fArgs, 1, PyString_FromString(msg)); + PyObject *pValue = PyObject_Call(pEventFn, fArgs, NULL); Py_DECREF(fArgs); PyGILState_Release(gstate); diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index b880da64..2de9d64a 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -987,7 +987,7 @@ int handle_sim_end(void *gpi_cb_data) if (sim_finish_cb) { sim_finish_cb = NULL; /* This means that we have been asked to close */ - embed_sim_end(); + embed_sim_event(SIM_FAIL, "Simulator shutdown prematurely"); } /* Other sise we have already been here from the top down so do not need to inform the upper layers that anything has occoured */ __gpi_free_callback(sim_init_cb); @@ -1020,7 +1020,7 @@ static int system_function_fail_test(char *userdata) argval.format = vpiStringVal; vpi_get_value(argh, &argval); - fail_test(argval.value.str); + embed_sim_event(SIM_TEST_FAIL, argval.value.str); // Cleanup and return vpi_free_object(args_iter); From 087d3082219a8816508875bce7d568a99ccbbe93 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 21 Aug 2013 15:46:59 +0100 Subject: [PATCH 0337/2656] Cleanup: Add messages to clock module for stop and enable restarting of the same clock --- cocotb/clock.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cocotb/clock.py b/cocotb/clock.py index b497e017..f1ad3b03 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -60,6 +60,10 @@ def start(self, cycles=0): def stop(self): if self.hdl is not None: simulator.stop_clock(self.hdl) + self.log.info("Clock %s Stopped" % (str(self.signal))) + self.hdl = None + else: + self.log.debug("Clock %s already stopped" % (str(self.signal))) def __str__(self): return self.__class__.__name__ + "(%3.1fMHz)" % self.frequency From 469e82ff45bcd439f24b036f013adafc301bc8c1 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 21 Aug 2013 15:48:49 +0100 Subject: [PATCH 0338/2656] Cleanup: Add a testcase for close down of two routines on the same trigger and executing ReadWrite after ReadOnly --- examples/functionality/tests/test_cocotb.py | 54 ++++++++++++++++++--- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 30dcac9b..00ea54dd 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -32,7 +32,7 @@ """ import cocotb -from cocotb.triggers import Timer, Join, RisingEdge +from cocotb.triggers import Timer, Join, RisingEdge, ReadOnly, ReadWrite from cocotb.clock import Clock @@ -130,11 +130,6 @@ def test_adding_a_coroutine_without_starting(dut): yield Join(forked) yield Timer(100) -@cocotb.test(expect_fail=True, skip=True) -def test_failure_from_system_task(dut): - """Allow the dut to call $fail_test() from verilog""" - yield Timer(10000000) - @cocotb.test(expect_fail=False) def test_anternal_clock(dut): """Test ability to yeild on an external non cocotb coroutine decorated function""" @@ -145,3 +140,50 @@ def test_anternal_clock(dut): yield RisingEdge(dut.clk) count += 1 clk_gen.stop() + +@cocotb.coroutine +def do_test_readwrite_in_readonly(dut): + yield RisingEdge(dut.clk) + yield ReadOnly() + dut.clk <= 0 + yield ReadWrite() + +@cocotb.test(expect_error=True) +def test_readwrite_in_readonly(dut): + """Test doing invalid sim operation""" + clk_gen = Clock(dut.clk, 100) + clk_gen.start() + coro = cocotb.fork(do_test_readwrite_in_readonly(dut)) + yield Timer(10000) + clk.stop() + +@cocotb.coroutine +def clock_one(dut): + count = 0 + while count is not 50: + yield RisingEdge(dut.clk) + yield Timer(10000) + count += 1 + +@cocotb.coroutine +def clock_two(dut): + count = 0 + while count is not 50: + yield RisingEdge(dut.clk) + yield Timer(100000) + count += 1 + +@cocotb.test(expect_fail=False) +def test_coroutine_close_down(dut): + clk_gen = Clock(dut.clk, 100) + clk_gen.start() + + coro_one = cocotb.fork(clock_one(dut)) + coro_two = cocotb.fork(clock_two(dut)) + + yield Join(coro_one) + yield Join(coro_two) + + dut.log.info("Back from joins") + + clk_gen.stop() From c06341e9e71a4d04541b98f8e37ce575a36c521c Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 21 Aug 2013 15:51:18 +0100 Subject: [PATCH 0339/2656] Fixes #71: move to using raise_error --- cocotb/scheduler.py | 14 +++++++------- cocotb/triggers.py | 18 +++++++++--------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 52a07c81..d9370a18 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -41,7 +41,7 @@ import cocotb.decorators from cocotb.triggers import Trigger, Timer, ReadOnly, NextTimeStep, ReadWrite, NullTrigger from cocotb.log import SimLog -from cocotb.result import TestComplete, TestError, ReturnValue +from cocotb.result import TestComplete, TestError, ReturnValue, raise_error from threading import RLock class Scheduler(object): @@ -95,11 +95,6 @@ def react(self, trigger): self.schedule(coroutine, trigger=trigger) self.log.debug("Scheduled coroutine %s" % (coroutine.__name__)) - self.log.debug("Completed scheduling loop, still waiting on:") - - #for trig, routines in self.waiting.items(): - # self.log.debug("\t%s: [%s]" % (str(trig).ljust(30), " ".join([routine.__name__ for routine in routines]))) - # If we've performed any writes that are cached then schedule # another callback for the read-write part of the sim cycle, but # if we are terminating then do not allow another callback to be @@ -154,8 +149,13 @@ def _add_trigger(self, trigger, coroutine): self._entry_lock.release() # We drop the lock before calling out to the simulator (most likely consequence of prime) trigger.prime(self.react) + except TestError as e: + self.waiting[trigger].remove(coroutine) + # Do not re-call raise_error since the error will already be logged at point of interest + raise e except Exception as e: - raise TestError("Unable to prime a trigger: %s" % str(e)) + self.waiting[trigger].remove(coroutine) + raise_error(self, "Unable to prime a trigger: %s" % str(e)) def queue(self, coroutine): """Queue a coroutine for execution""" diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 8c421acd..5a076127 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -30,7 +30,7 @@ import cocotb import pdb from cocotb.log import SimLog - +from cocotb.result import raise_error class TriggerException(Exception): @@ -112,7 +112,7 @@ def __init__(self, time_ps): def prime(self, callback): """Register for a timed callback""" if simulator.register_timed_callback(self.cbhdl, self.time_ps, callback, self): - raise TestError("Unable set up Timer Trigger") + raise_error(self, "Unable set up %s Trigger" % (str(self))) def __str__(self): return self.__class__.__name__ + "(%dps)" % self.time_ps @@ -128,7 +128,7 @@ def __init__(self, signal): def prime(self, callback): """Register notification of a value change via a callback""" if simulator.register_value_change_callback(self.chbdl, self.signal._handle, callback, self): - raise TestError("Unable set up Edge Trigger") + raise_error(self, "Unable set up %s Trigger" % (str(self))) def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal.name @@ -143,7 +143,7 @@ def __init__(self): def prime(self, callback): if simulator.register_readonly_callback(self.cbhdl, callback, self): - raise TestError("Unable set up ReadOnly Trigger") + raise_error(self, "Unable set up %s Trigger" % (str(self))) def __str__(self): return self.__class__.__name__ + "(readonly)" @@ -158,7 +158,7 @@ def __init__(self): def prime(self, callback): if simulator.register_rwsynch_callback(self.cbhdl, callback, self): - raise TestError("Unable set up ReadWrite Trigger") + raise_error(self, "Unable set up %s Trigger" % (str(self))) def __str__(self): return self.__class__.__name__ + "(readwritesync)" @@ -191,10 +191,10 @@ def _check(obj): self._callback(self) else: if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self): - raise TestError("Unable set up RisingEdge Trigger") + raise_error(self, "Unable set up %s Trigger" % (str(self))) if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self): - raise TestError("Unable set up RisingEdge Trigger") + raise_error(self, "Unable set up %s Trigger" % (str(self))) def __str__(self): @@ -220,11 +220,11 @@ def _check(obj): return if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self): - raise TestError("Unable set up ClockCycles Trigger") + raise_error(self, "Unable set up %s Trigger" % (str(self))) if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self): - raise TestError("Unable set up ClockCycles Trigger") + raise_error(self, "Unable set up %s Trigger" % (str(self))) def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal.name From 566136fe7a389deb21af0323b672c658cdbcf211 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 21 Aug 2013 15:53:55 +0100 Subject: [PATCH 0340/2656] Cleanup: Remove dirs that are no longer present in structure from makefile --- examples/Makefile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index bf8ccf49..5f7c16b4 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -27,9 +27,7 @@ -SMOKE_TESTS := demo \ - comb_seq \ - plusargs \ +SMOKE_TESTS := plusargs \ sim_exit \ functionality/tests From 5992af9fe863596747a20f2dc9b63b141df049dc Mon Sep 17 00:00:00 2001 From: Finn Grimwood Date: Thu, 22 Aug 2013 00:28:18 +0100 Subject: [PATCH 0341/2656] Fixes #79: replaced printf with LOG_CRITICAL --- lib/vpi_shim/gpi_vpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 2de9d64a..05bc44f4 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -642,7 +642,7 @@ static int gpi_free_recurring(p_vpi_cb_user_data user_data) int rc; vpiHandle cb_hdl = user_data->cb_hdl; if (!cb_hdl) { - printf("VPI: %s passed a NULL pointer : ABORTING"); + LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); exit(1); } From 1e37a812f22a3c73085b9bdfc6410ba0a8969d65 Mon Sep 17 00:00:00 2001 From: Finn Grimwood Date: Thu, 22 Aug 2013 01:08:43 +0100 Subject: [PATCH 0342/2656] Fixes #81: Fix segfault in embed_sim_init by returning early in error case --- lib/embed/gpi_embed.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 14f81bdf..2c8f5a75 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -109,6 +109,7 @@ void embed_sim_init(gpi_sim_info_t *info) if (dut == NULL) { fprintf(stderr, "Unable to find root instance!\n"); gpi_sim_end(); + return; } PyObject *cocotb_module, *cocotb_init, *cocotb_args, *cocotb_retval; From 5700a005a2b8558ce416a8801b795e5380e0c8cf Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 22 Aug 2013 15:09:03 +0100 Subject: [PATCH 0343/2656] Issue #69: Fix issue of being in ReadOnly state if the signal to wait on is not what we want and scheduling a ReadWrite after --- cocotb/drivers/__init__.py | 31 ++++++++++++++----------------- cocotb/drivers/avalon.py | 3 ++- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index c37fdcf2..39e0724e 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -34,7 +34,7 @@ import cocotb from cocotb.decorators import coroutine -from cocotb.triggers import Event, RisingEdge, ReadOnly +from cocotb.triggers import Event, RisingEdge, ReadOnly, Timer, NextTimeStep from cocotb.bus import Bus from cocotb.log import SimLog from cocotb.result import ReturnValue @@ -202,17 +202,6 @@ def _driver_send(self, transaction): yield RisingEdge(self.clock) self.bus <= transaction - @coroutine - def __wait_for_value_on_signal(self, signal, level): - loops = 0 - yield ReadOnly() - while not signal.value is not level: - yield RisingEdge(self.clock) - yield ReadOnly() - loops += 1 - - raise ReturnValue(loops) - @coroutine def _wait_for_signal(self, signal): """This method will return with the specified signal @@ -220,9 +209,11 @@ def _wait_for_signal(self, signal): so sim will need to move to NextTimeStep before registering more callbacks can occour """ - res = yield self.__wait_for_value_on_signal(signal, 1) - - raise ReturnValue(res) + yield RisingEdge(self.clock) + yield ReadOnly() + if signal.value.value is not 1: + yield RisingEdge(signal) + yield NextTimeStep() @coroutine def _wait_for_nsignal(self, signal): @@ -231,9 +222,15 @@ def _wait_for_nsignal(self, signal): so sim will need to move to NextTimeStep before registering more callbacks can occour """ - res = yield self.__wait_for_value_on_signal(signal, 0) + yield RisingEdge(self.clock) + yield ReadOnly() + if signal.value.value is not 0: + yield Edge(signal) + yield NextTimeStep() - raise ReturnValue(res) + def __str__(self): + """Provide the name of the bus""" + return str(self.name) class ValidatedBusDriver(BusDriver): diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index fcecb828..7a04d7b4 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -30,7 +30,7 @@ NB Currently we only support a very small subset of functionality """ from cocotb.decorators import coroutine -from cocotb.triggers import RisingEdge, ReadOnly +from cocotb.triggers import RisingEdge, ReadOnly, NextTimeStep from cocotb.drivers import BusDriver, ValidatedBusDriver from cocotb.utils import hexdump from cocotb.binary import BinaryValue @@ -96,6 +96,7 @@ def read(self, address): # Get the data yield ReadOnly() data = self.bus.readdata.value + yield NextTimeStep() raise ReturnValue(data) From bdacf09d783b74f24bf3bf4a15b7f4f272f751eb Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 22 Aug 2013 19:06:48 +0100 Subject: [PATCH 0344/2656] Cleanup: fix silly bug --- cocotb/drivers/avalon.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 7a04d7b4..4118886a 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -115,8 +115,6 @@ def write(self, address, value): # Wait for waitrequest to be low count = yield self._wait_for_nsignal(self.bus.waitrequest) - if count is not 0: - csr.log.warning("Waiting for %d loops for waitrequest to go low" % count) # Deassert write yield RisingEdge(self.clock) From 22f30e1fd3fff1b5a13b8d12febaa31980d58254 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Fri, 23 Aug 2013 13:53:20 +0100 Subject: [PATCH 0345/2656] Issue #71: Catch exceptions so that subsequent tests still attempt to execute --- cocotb/decorators.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 048eb499..9801cdd5 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -111,6 +111,8 @@ def send(self, value): raise CoroutineComplete(callback=self._finished_cb) except StopIteration: raise CoroutineComplete(callback=self._finished_cb) + except Exception as e: + raise_error(self, "send raised %s" % (str(e))) def throw(self, exc): return self._coro.throw(exc) @@ -184,6 +186,8 @@ def send(self, value): raise except StopIteration: raise TestSuccess() + except Exception as e: + raise_error(self, "send raised %s" % (str(e))) def _handle_error_message(self, msg): self.error_messages.append(msg) From 055ba7eb04e800e9033672c2c6ab50d64300a546 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Fri, 23 Aug 2013 16:29:38 +0100 Subject: [PATCH 0346/2656] Cleanup: Propogate setLevel method down to underlying logger object --- cocotb/log.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cocotb/log.py b/cocotb/log.py index 7f13aeed..4face241 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -136,6 +136,8 @@ def info(self, msg): def addHandler(self, handler): self.logger.addHandler(handler) + def setLevel(self, level): + self.logger.setLevel(level) class SimLogFormatter(logging.Formatter): From 5b5547bec4d7dc87413dd15b804ef770f0ad5351 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 25 Aug 2013 19:37:39 +0100 Subject: [PATCH 0347/2656] Issue #9: Add documentation about environment variables --- documentation/source/building.rst | 55 +++++++++++++++++++++++++++++++ documentation/source/index.rst | 1 + 2 files changed, 56 insertions(+) create mode 100644 documentation/source/building.rst diff --git a/documentation/source/building.rst b/documentation/source/building.rst new file mode 100644 index 00000000..df0d5f71 --- /dev/null +++ b/documentation/source/building.rst @@ -0,0 +1,55 @@ +####################################### +Build options and Environment Variables +####################################### + +Make System +=========== + + +Environment Variables +===================== + + + +RANDOM_SEED +----------- + +Seed the Python random module to recreate a previous test stimulus. At the beginning of every test a message is displayed with the seed used for that execution: + +.. code-block:: bash + + INFO cocotb.gpi __init__.py:89 in _initialise_testbench Seeding Python random module with 1377424946 + + +To recreate the same stimulis use the following: + +.. code-block:: bash + + make RANDOM_SEED=1377424946 + + + +COCOTB_ANSI_OUTPUT +------------------ + +Use this to override the default behaviour of annotating cocotb output with +ANSI colour codes if the output is a terminal (isatty()). + +COCOTB_ANSI_OUTPUT=1 forces output to be ANSI regardless of the type stdout + +COCOTB_ANDI_OUTPUT=0 supresses the ANSI output in the log messages + + +MODULE +------ + +The name of the module(s) to search for test functions. Multiple modules can be specified using a comma-separated list. + + +TESTCASE +-------- + +The name of the test function(s) to run. If this variable is not defined cocotb discovers and executes all functions decorated with @cocotb.test() decorator in the supplied modules. + +Multiple functions can be specified in a comma-separated list. + diff --git a/documentation/source/index.rst b/documentation/source/index.rst index da4b1dce..35ba61ea 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -14,6 +14,7 @@ Contents: introduction overview quickstart + building coroutines triggers roadmap From da7a5dca9d93157c4430c969490acc424b6370ee Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Mon, 26 Aug 2013 08:15:04 +0100 Subject: [PATCH 0348/2656] Issue #67: Define COCOTB_SIM=1 in Icarus --- makefiles/simulators/Makefile.icarus | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index ba7d7b7c..44b9f26c 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -36,13 +36,13 @@ $(OBJ_DIR): .PHONY: sim sim: $(OBJ_DIR) $(VERILOG_SOURCES) libs_native - iverilog -o $(OBJ_DIR)/sim.vvp $(VERILOG_SOURCES) + iverilog -o $(OBJ_DIR)/sim.vvp -D COCOTB_SIM=1 $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp $(PLUSARGS) .PHONY: gdb gdb: $(OBJ_DIR) $(VERILOG_SOURCES) libs_native - iverilog -o $(OBJ_DIR)/sim.vvp $(VERILOG_SOURCES) + iverilog -o $(OBJ_DIR)/sim.vvp -D COCOTB_SIM=1 $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ gdb --args vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp $(PLUSARGS) From bd579dc4f62810441bf2c6e9470df4e5549d4feb Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Mon, 26 Aug 2013 08:32:29 +0100 Subject: [PATCH 0349/2656] Issue #83: Harmonise makefile variables and tidy up makefiles --- documentation/source/building.rst | 40 ++++++++++++++++++++++++++++ makefiles/simulators/Makefile.icarus | 11 ++++---- makefiles/simulators/Makefile.ius | 3 --- makefiles/simulators/Makefile.questa | 4 --- makefiles/simulators/Makefile.vcs | 6 ++--- 5 files changed, 49 insertions(+), 15 deletions(-) diff --git a/documentation/source/building.rst b/documentation/source/building.rst index df0d5f71..23008997 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -5,12 +5,52 @@ Build options and Environment Variables Make System =========== +Makefiles are provided for a variety of simulators in cocotb/makefiles/simulators. The common Makefile cocotb/makefiles/Makefile.sim includes the appropriate simulator makefile based on the contents of the SIM variable. + +Variables +--------- + +SIM +~~~ + +Selects which simulator Makefile to use. Attempts to include cocotb/makefiles/makefile.$(SIM) + + +VERILOG_SOURCES +~~~~~~~~~~~~~~~ + +A list of the verilog source files to include. + + +COMPILE_ARGS +~~~~~~~~~~~~ + +Any arguments or flags to pass to the compile stage of the simulation. Only applies to simulators with a separate compilation stage (currently Icarus and VCS). + + +SIM_ARGS +~~~~~~~~ + +Any arguments or flags to pass to the execution of the compiled simulation. Only applies to simulators with a separate compilation stage (currently Icarus and VCS). + +EXTRA_ARGS +~~~~~~~~~~ + +Passed to both the compile and execute phases of simulators with two rules, or passed to the single compile and run commad for simulators which don't have a distinct compilation stage. + + Environment Variables ===================== +TOPLEVEL +-------- + +Used to indicate the instance in the hierarchy to use as the DUT. If this isn't defined then the first root instance is used. + + RANDOM_SEED ----------- diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index 44b9f26c..78d6c804 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -36,16 +36,17 @@ $(OBJ_DIR): .PHONY: sim sim: $(OBJ_DIR) $(VERILOG_SOURCES) libs_native - iverilog -o $(OBJ_DIR)/sim.vvp -D COCOTB_SIM=1 $(VERILOG_SOURCES) + iverilog -o $(OBJ_DIR)/sim.vvp -D COCOTB_SIM=1 $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp $(PLUSARGS) + vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) + .PHONY: gdb gdb: $(OBJ_DIR) $(VERILOG_SOURCES) libs_native - iverilog -o $(OBJ_DIR)/sim.vvp -D COCOTB_SIM=1 $(VERILOG_SOURCES) + iverilog -o $(OBJ_DIR)/sim.vvp -D COCOTB_SIM=1 $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - gdb --args vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp $(PLUSARGS) + gdb --args vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) -clean: +clean:: rm -rf $(OBJ_DIR) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index f64b8e2c..bc204e9a 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -39,9 +39,6 @@ sim: $(OBJ_DIR) $(VERILOG_SOURCES) libs_native PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ irun -sv $(EXTRA_ARGS) -64bit -loadvpi $(BUILD_DIR)/libgpi:vlog_startup_routines_bootstrap +access+rwc $(VERILOG_SOURCES) -dve: $(OBJ_DIR) $(VERILOG_SOURCES) libs_native - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - irun -sv $(EXTRA_ARGS) -64bit -loadvpi $(BUILD_DIR)/libgpi:vlog_startup_routines_bootstrap +access+rwc $(VER clean: -@rm -rf $(OBJ_DIR) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index d33b7cdc..ad5bf24e 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -15,10 +15,6 @@ sim: $(OBJ_DIR) $(VERILOG_SOURCES) libs_native PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(PYTHON_LIBDIR):$(PYTHON_DYNLIBDIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ qverilog -64 -R -pli $(VPI_FILE) - +acc $(EXTRA_ARGS) $(VERILOG_SOURCES) -dve: $(OBJ_DIR) $(VERILOG_SOURCES) libs_native - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - qverilog -64 -R -pli $(VPI_FILE) - +acc $(EXTRA_ARGS) $(VERILOG_SOURCES) - clean: -@rm -rf $(OBJ_DIR) -@rm -rf INCA_libs diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index c7eb179c..c8aa27c7 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -61,11 +61,11 @@ PYTHON_FILES:=$(shell find $(SIM_ROOT)/cocotb/ -name "*.py" | xargs) $(MODULE_FI results.xml: $(SIM_BUILD)/simv $(PYTHON_FILES) -PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - $(SIM_BUILD)/simv +define+COCOTB_SIM=1 $(EXTRA_ARGS) + $(SIM_BUILD)/simv +define+COCOTB_SIM=1 $(SIM_ARGS) $(EXTRA_ARGS) dve: $(SIM_BUILD)/simv_dve PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - vcs +acc+1 +vpi+1+assertion -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -gui -load libgpi.so $(VERILOG_SOURCES) + vcs +acc+1 +vpi+1+assertion -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -gui -load libgpi.so $(COMPILE_ARGS) $(VERILOG_SOURCES) .PHONY: build_simv build_simv: $(SIM_BUILD)/simv @@ -73,7 +73,7 @@ build_simv: $(SIM_BUILD)/simv $(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab libs_native cd $(SIM_BUILD) && \ LD_LIBRARY_PATH=$(LIB_DIR) TOPLEVEL=$(TOPLEVEL) \ - vcs +acc+1 +vpi -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -debug -load libgpi.so $(VERILOG_SOURCES) + vcs +acc+1 +vpi -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -debug -load libgpi.so $(COMPILE_ARGS) $(VERILOG_SOURCES) .PHONY: clean clean:: From 2b025b716e4943ec86c053ba58f0150154da3bc8 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 27 Aug 2013 15:49:04 +0100 Subject: [PATCH 0350/2656] Cleanup: Don't use id(self) as log ident for handles or singletons --- cocotb/handle.py | 2 +- cocotb/regression.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 70bb4325..26061cc2 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -52,7 +52,7 @@ def __init__(self, handle): self.name = simulator.get_name_string(self._handle) self.fullname = self.name + '(%s)' % simulator.get_type_string(self._handle) - self.log = SimLog('cocotb.' + self.name, id(self)) + self.log = SimLog('cocotb.' + self.name) self.log.debug("Created!") def __str__(self): diff --git a/cocotb/regression.py b/cocotb/regression.py index 65605ce8..6de69e26 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -61,7 +61,7 @@ def __init__(self, dut, modules, tests=None): self._modules = modules self._functions = tests self._running_test = None - self.log = SimLog("cocotb.regression", id(self)) + self.log = SimLog("cocotb.regression") def initialise(self): From 0ce6f4ab86943a1731b2e9af7afac874dd19a197 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 28 Aug 2013 17:08:48 +0100 Subject: [PATCH 0351/2656] Issue #24: Add CUSTOM_DEPS environment var --- makefiles/simulators/Makefile.vcs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index c8aa27c7..65a26741 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -59,10 +59,14 @@ regression: build_simv results.xml MODULE_FILES:=$(shell for X in $(echo "test1,test2" | sed 's/,/ /g'); do echo $X.py; done) PYTHON_FILES:=$(shell find $(SIM_ROOT)/cocotb/ -name "*.py" | xargs) $(MODULE_FILES) -results.xml: $(SIM_BUILD)/simv $(PYTHON_FILES) +results.xml: $(SIM_BUILD)/simv $(PYTHON_FILES) $(CUSTOM_DEPS) -PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ $(SIM_BUILD)/simv +define+COCOTB_SIM=1 $(SIM_ARGS) $(EXTRA_ARGS) +gdb: $(SIM_BUILD)/simv $(PYTHON_FILES) + -PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + gdb --args $(SIM_BUILD)/simv +define+COCOTB_SIM=1 $(EXTRA_ARGS) + dve: $(SIM_BUILD)/simv_dve PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ vcs +acc+1 +vpi+1+assertion -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -gui -load libgpi.so $(COMPILE_ARGS) $(VERILOG_SOURCES) From 1fc97a010a36bb91d6423df7fd3cfb2635690d06 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 28 Aug 2013 17:15:00 +0100 Subject: [PATCH 0352/2656] Issue #71: Correct traceback printing for exceptions --- examples/functionality/tests/test_cocotb.py | 26 +++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 00ea54dd..123680f1 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -187,3 +187,29 @@ def test_coroutine_close_down(dut): dut.log.info("Back from joins") clk_gen.stop() + +@cocotb.coroutine +def syntax_error(): + yield Timer(100) + fail + +@cocotb.test(expect_error=True) +def test_syntax_error(dut): + """Syntax error in the test""" + yield Timer(100) + + fail + +@cocotb.test(expect_error=True) +def test_coroutine_syntax_error(dut): + """Syntax error in a coroutine that we yield""" + yield Timer(100) + yield syntax_error() + +@cocotb.test(expect_error=True) +def test_fork_syntax_error(dut): + """Syntax error in a coroutine that we fork""" + yield Timer(100) + cocotb.fork(syntax_error()) + yield Timer(100) + From 2d99a5af60f13e2cd37938641a40e40d8a3328fc Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 28 Aug 2013 17:22:49 +0100 Subject: [PATCH 0353/2656] Issue #71: Correct traceback printing for exceptions --- cocotb/decorators.py | 5 ++--- cocotb/result.py | 11 ++++++++--- cocotb/scheduler.py | 2 -- examples/functionality/tests/test_cocotb.py | 9 ++++----- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 9801cdd5..4c24bbbf 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -112,7 +112,7 @@ def send(self, value): except StopIteration: raise CoroutineComplete(callback=self._finished_cb) except Exception as e: - raise_error(self, "send raised %s" % (str(e))) + raise_error(self, "Send raised exception: %s" % (str(e))) def throw(self, exc): return self._coro.throw(exc) @@ -187,7 +187,7 @@ def send(self, value): except StopIteration: raise TestSuccess() except Exception as e: - raise_error(self, "send raised %s" % (str(e))) + raise_error(self, "Send raised exception: %s" % (str(e))) def _handle_error_message(self, msg): self.error_messages.append(msg) @@ -209,7 +209,6 @@ def __call__(self, *args, **kwargs): try: return RunningCoroutine(self._func(*args, **kwargs), self) except Exception as e: - print "bing" traceback.print_exc() result = TestError(str(e)) traceback.print_exc(file=result.stderr) diff --git a/cocotb/result.py b/cocotb/result.py index 915fb624..17eb7b67 100644 --- a/cocotb/result.py +++ b/cocotb/result.py @@ -29,15 +29,20 @@ from StringIO import StringIO def raise_error(obj, msg): + """Creates a TestError exception and raises it after printing a traceback + + obj has a log method + msg is a string + """ + exc_type, exc_value, exc_traceback = sys.exc_info() buff = StringIO() - lastframe = sys._getframe(1) - traceback.print_stack(lastframe, file=buff) + traceback.print_tb(exc_traceback, file=buff) obj.log.error("%s\n%s" % (msg, buff.getvalue())) exception = TestError(msg) exception.stderr.write(buff.getvalue()) - buff.close() raise exception + class ReturnValue(StopIteration): def __init__(self, retval): self.retval = retval diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index d9370a18..eae1076b 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -316,14 +316,12 @@ def schedule(self, coroutine, trigger=None): # TestComplete indication is game over, tidy up except TestComplete as test_result: - if hasattr(test_result, "stderr"): print str(test_result.stderr.getvalue()) # Tag that close down is needed, save the test_result # for later use in cleanup handler # If we're already tearing down we ignore any further test results # that may be raised. Required because currently Python triggers don't unprime if not self._terminate: self.finish_test(test_result) - self.log.warning("Coroutine completed execution with %s: %s" % (test_result.__class__.__name__, str(coroutine))) return coroutine.log.debug("Finished sheduling coroutine (%s)" % str(trigger)) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 123680f1..7e57c433 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -196,20 +196,19 @@ def syntax_error(): @cocotb.test(expect_error=True) def test_syntax_error(dut): """Syntax error in the test""" - yield Timer(100) - + yield clock_gen(dut.clk) fail @cocotb.test(expect_error=True) def test_coroutine_syntax_error(dut): """Syntax error in a coroutine that we yield""" - yield Timer(100) + yield clock_gen(dut.clk) yield syntax_error() @cocotb.test(expect_error=True) def test_fork_syntax_error(dut): """Syntax error in a coroutine that we fork""" - yield Timer(100) + yield clock_gen(dut.clk) cocotb.fork(syntax_error()) - yield Timer(100) + yield clock_gen(dut.clk) From 7380e94202e33f3ee7a3369da0096b13560fce1d Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 29 Aug 2013 16:24:30 +0100 Subject: [PATCH 0354/2656] Cleanup: deprecate BinaryValue.value in favour of BinaryValue.integer --- cocotb/binary.py | 11 +++++++---- cocotb/drivers/__init__.py | 4 ++-- cocotb/monitors/avalon.py | 4 ++-- examples/functionality/tests/test_discovery.py | 4 ++-- examples/functionality/tests/test_external.py | 2 +- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 3a6b609d..866ca78e 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -37,14 +37,16 @@ class BinaryValue(object): The underlying value can be set or accessed using three aliasing attributes, - - BinaryValue.value is an integer + - BinaryValue.integer is an integer - BinaryValue.binstr is a string of "01xXzZ" - BinaryValue.buff is a binary buffer of bytes + - BinaryValue.value is an integer *** deprecated *** + For example: >>> vec = BinaryValue() - >>> vec.value = 42 + >>> vec.integer = 42 >>> print vec.binstr 101010 >>> print repr(vec.buff) @@ -96,7 +98,8 @@ def set_value(self, integer): self._str = bin(integer)[2:] self._adjust() - value = property(get_value, set_value, None, "Integer access to the value") + value = property(get_value, set_value, None, "Integer access to the value *** deprecated ***") + integer = property(get_value, set_value, None, "Integer access to the value") def get_buff(self): """Attribute self.buff represents the value as a binary string buffer @@ -169,7 +172,7 @@ def __nonzero__(self): >>> if val: print "True" ... else: print "False" False - >>> val.value = 42 + >>> val.integer = 42 >>> if val: print "True" ... else: print "False" True diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index 39e0724e..5334c4bd 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -211,7 +211,7 @@ def _wait_for_signal(self, signal): """ yield RisingEdge(self.clock) yield ReadOnly() - if signal.value.value is not 1: + if signal.value.integer != 1: yield RisingEdge(signal) yield NextTimeStep() @@ -224,7 +224,7 @@ def _wait_for_nsignal(self, signal): """ yield RisingEdge(self.clock) yield ReadOnly() - if signal.value.value is not 0: + if signal.value.integer != 0: yield Edge(signal) yield NextTimeStep() diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index 7e1705f5..8a30b36a 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -110,8 +110,8 @@ def valid(): if self.bus.endofpacket.value: # Truncate the empty bits - if self.bus.empty.value.value: - pkt = pkt[:-self.bus.empty.value.value] + if self.bus.empty.value.integer: + pkt = pkt[:-self.bus.empty.value.integer] self.log.info("Recieved a packet of %d bytes" % len(pkt)) self.log.debug(hexdump(str((pkt)))) self._recv(pkt) diff --git a/examples/functionality/tests/test_discovery.py b/examples/functionality/tests/test_discovery.py index 9772a8ed..370ffb8c 100644 --- a/examples/functionality/tests/test_discovery.py +++ b/examples/functionality/tests/test_discovery.py @@ -26,10 +26,10 @@ def access_single_bit(dut): dut.log.info("%s = %d bits" % (str(dut.stream_in_data), len(dut.stream_in_data))) dut.stream_in_data[2] <= 1 yield Timer(10) - if dut.stream_out_data_comb.value.value != (1<<2): + if dut.stream_out_data_comb.value.integer != (1<<2): raise TestError("%s.%s != %d" % (str(dut.stream_out_data_comb), - dut.stream_out_data_comb.value.value, (1<<2))) + dut.stream_out_data_comb.value.integer, (1<<2))) diff --git a/examples/functionality/tests/test_external.py b/examples/functionality/tests/test_external.py index e9ac3b27..6ff798f1 100644 --- a/examples/functionality/tests/test_external.py +++ b/examples/functionality/tests/test_external.py @@ -130,7 +130,7 @@ def test_ext_function(dut): return 2 def test_ext_function_return(dut): - value = dut.clk.value.value + value = dut.clk.value.integer #dut.log.info("Sleeping and returning %s" % value) #time.sleep(0.2) return value From f6cb514e8926c3c17d4e603ec8e27954d3a4e7b4 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Fri, 30 Aug 2013 09:16:18 +0100 Subject: [PATCH 0355/2656] Cleanup: Updates to ValidatedBusDriver to cope with valid generators that return zeros --- cocotb/drivers/__init__.py | 29 +++++++++++++++++++++++------ cocotb/drivers/avalon.py | 13 ++++++++----- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index 5334c4bd..aaee7158 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -255,18 +255,35 @@ def __init__(self, entity, name, clock, valid_generator=None): self.set_valid_generator(valid_generator=valid_generator) - def set_valid_generator(self, valid_generator=None): + def _next_valids(self): """ - Set a new valid generator for this bus + Optionally insert invalid cycles every N cycles + Generator should return a tuple with the number of cycles to be + on followed by the number of cycles to be off. + The 'on' cycles should be non-zero, we skip invalid generator entries """ + self.on = False - self.valid_generator = valid_generator - - # Optionally insert invalid cycles every N if self.valid_generator is not None: - self.on, self.off = valid_generator.next() + while not self.on: + try: + self.on, self.off = self.valid_generator.next() + except StopIteration: + self.on = True # If the generator runs out stop inserting non-valid cycles + self.log.info("Valid generator exhausted, not inserting non-valid cycles anymore") + return + self.log.debug("Will be on for %d cycles, off for %s" % (self.on, self.off)) else: # Valid every clock cycle self.on, self.off = True, False self.log.debug("Not using valid generator") + + + def set_valid_generator(self, valid_generator=None): + """ + Set a new valid generator for this bus + """ + + self.valid_generator = valid_generator + self._next_valids() diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 4118886a..e78eb194 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -199,10 +199,12 @@ def _send_string(self, string): self.bus.valid <= 0 for i in range(self.off): yield clkedge - self.on, self.off = self.valid_generator.next() + + # Grab the next set of on/off values + self._next_valids() # Consume a valid cycle - if self.on is not True: + if self.on is not True and self.on: self.on -= 1 self.bus.valid <= 1 @@ -251,14 +253,15 @@ def _send_iterable(self, pkt): # Insert a gap where valid is low if not self.on: - self.log.debug("Inserting %d non-valid cycles" % (self.off)) self.bus.valid <= 0 for i in range(self.off): yield clkedge - self.on, self.off = self.valid_generator.next() + + # Grab the next set of on/off values + self._next_valids() # Consume a valid cycle - if self.on is not True: + if self.on is not True and self.on: self.on -= 1 self.bus <= word From aadca47f5d0ead9760dc29d6a2879fa81e685661 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Fri, 30 Aug 2013 10:50:13 +0100 Subject: [PATCH 0356/2656] Cleanup: Reduce verbosity of missing handle message since this is caught in Python --- lib/vpi_shim/gpi_vpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 05bc44f4..e262c5d5 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -290,7 +290,7 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) strncpy(buff, name, len); obj = vpi_handle_by_name(buff, (vpiHandle)(parent->sim_hdl)); if (!obj) { - LOG_ERROR("VPI: Handle '%s' not found!", name); + LOG_DEBUG("VPI: Handle '%s' not found!", name); check_vpi_error(); return NULL; } From ac399781f9e306a39182b585c99a7dc646cd53d4 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 30 Aug 2013 10:53:27 +0100 Subject: [PATCH 0357/2656] Issue #59: change to use NextTimeStep() and not Timer(0) as can be in ReadOnly() --- cocotb/scheduler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index d9370a18..ec8ec82c 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -386,5 +386,5 @@ def enable_react_delay(self): @cocotb.decorators.coroutine def react_delay(self): - yield Timer(0) + yield NextTimeStep() self._react_timer = None From 8480853041b05b646f6bf11924e3c3833ec02203 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 30 Aug 2013 11:37:40 +0100 Subject: [PATCH 0358/2656] Issue #59: No need to check @function explicitly since this is used internally to @external --- examples/functionality/tests/test_external.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/functionality/tests/test_external.py b/examples/functionality/tests/test_external.py index 6ff798f1..67e73656 100644 --- a/examples/functionality/tests/test_external.py +++ b/examples/functionality/tests/test_external.py @@ -104,7 +104,10 @@ def test_callable(dut): yield Timer(100000) clk_gen.stop() if test_count is not 5: + print("Count was %d" % test_count) + clk_gen.stop() raise TestFailure + clk_gen.stop() @cocotb.test(expect_fail=True, skip=True) def test_callable_fail(dut): From 23cf4c1ab43c30c3845851505096ec5787bcadca Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 2 Sep 2013 11:15:08 +0100 Subject: [PATCH 0359/2656] Cleanup: enable sim_exit tests --- examples/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Makefile b/examples/Makefile index 5f7c16b4..751ec53e 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -28,7 +28,7 @@ SMOKE_TESTS := plusargs \ - sim_exit \ + sim_exit/tests \ functionality/tests .PHONY: $(SMOKE_TESTS) From 37040c0e9ebd6556485856924594a5324a139b0f Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Mon, 2 Sep 2013 12:14:23 +0100 Subject: [PATCH 0360/2656] Cleanup: Tidy up some formatting niggles in logging output --- cocotb/log.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/cocotb/log.py b/cocotb/log.py index 4face241..c97b4d62 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -133,11 +133,10 @@ def critical(self, msg): def info(self, msg): self._makeRecord(msg, logging.INFO) - def addHandler(self, handler): - self.logger.addHandler(handler) + def __getattr__(self, attribute): + """Forward any other attribute accesses on to our logger object""" + return getattr(self.logger, attribute) - def setLevel(self, level): - self.logger.setLevel(level) class SimLogFormatter(logging.Formatter): @@ -146,12 +145,12 @@ class SimLogFormatter(logging.Formatter): # Justify and truncate @staticmethod def ljust(string, chars): - if len(string) > chars: return string[:chars-2] + ".." + if len(string) > chars: return ".." + string[(chars-2)*-1:] return string.ljust(chars) @staticmethod def rjust(string, chars): - if len(string) > chars: return string[:chars-2] + ".." + if len(string) > chars: return ".." + string[(chars-2)*-1:] return string.rjust(chars) def _format(self, timeh, timel, level, record, msg): @@ -162,7 +161,7 @@ def _format(self, timeh, timel, level, record, msg): ':' + self.ljust(str(record.lineno), _LINENO_CHARS) + \ ' in ' + self.ljust(str(record.funcName), _FUNCNAME_CHARS) + ' ' - pad = "\n" + " " * (len(prefix) - 10) + pad = "\n" + " " * (len(prefix)) return prefix + pad.join(msg.split('\n')) From 3eee4d07d85e18fcca03e321fbbf6b678036453c Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Mon, 2 Sep 2013 12:15:23 +0100 Subject: [PATCH 0361/2656] Fixes #85: Make the scoreboard more flexible, improve logging output --- cocotb/scoreboard.py | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index 070ec6aa..e51703f2 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -28,6 +28,7 @@ """ Common scoreboarding capability. """ +import logging import cocotb from cocotb.utils import hexdump, hexdiffs @@ -78,7 +79,7 @@ def result(self): return TestFailure("Errors were recorded during the test") return TestSuccess() - def add_interface(self, monitor, expected_output): + def add_interface(self, monitor, expected_output, compare_fn=None): """Add an interface to be scoreboarded. Provides a function which the monitor will callback with received transactions @@ -94,42 +95,52 @@ def add_interface(self, monitor, expected_output): if not isinstance(monitor, Monitor): raise TypeError("Expected monitor on the interface but got %s" % (monitor.__class__.__name__)) + if compare_fn is not None: + if callable(compare_fn): + monitor.add_callback(compare_fn) + return + raise TypeError("Expected a callable compare function but got %s" % str(type(compare_fn))) + def check_received_transaction(transaction): """Called back by the monitor when a new transaction has been received""" + + log = logging.getLogger(self.log.name + '.' + monitor.name) + if callable(expected_output): - exp = expected_output() + exp = expected_output(transaction) elif len(expected_output): exp = expected_output.pop(0) else: self.errors += 1 - self.log.error("%s" % (transaction)) # TODO hexdump + log.error("Received a transaction but wasn't expecting anything") + log.info("Got: %s" % (hexdump(str(transaction)))) if self._imm: raise TestFailure("Recieved a transaction but wasn't expecting anything") return if type(transaction) != type(exp): self.errors += 1 - self.log.error("Received transaction is a different type to expected transaction") - self.log.info("Got: %s but expected %s" % (str(type(transaction)), str(type(exp)))) + log.error("Received transaction is a different type to expected transaction") + log.info("Got: %s but expected %s" % (str(type(transaction)), str(type(exp)))) if self._imm: raise TestFailure("Received transaction of wrong type") return if transaction != exp: self.errors += 1 - self.log.error("Received transaction differed from expected output") - self.log.info("Expected:\n" + hexdump(exp)) + log.error("Received transaction differed from expected output") + log.info("Expected:\n" + hexdump(exp)) if not isinstance(exp, str): try: for word in exp: self.log.info(str(word)) except: pass - self.log.info("Received:\n" + hexdump(transaction)) + log.info("Received:\n" + hexdump(transaction)) if not isinstance(transaction, str): try: for word in transaction: self.log.info(str(word)) except: pass - self.log.warning(hexdiffs(exp, transaction)) + log.warning(hexdiffs(exp, transaction)) if self._imm: raise TestFailure("Received transaction differed from expected transaction") else: - self.log.debug("Received expected transaction %d bytes" % (len(transaction))) - self.log.debug(repr(transaction)) + log.debug("Received expected transaction %d bytes" % (len(transaction))) + log.debug(repr(transaction)) monitor.add_callback(check_received_transaction) From 01613681b13b6b5245f7cd4be4996d7a0e41e816 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 3 Sep 2013 09:37:12 +0100 Subject: [PATCH 0362/2656] Cleanup: Fix up test_readwrite_in_readonly to notice the test failure that was expected --- examples/functionality/tests/test_cocotb.py | 31 ++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 7e57c433..c2cb1dee 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -141,21 +141,50 @@ def test_anternal_clock(dut): count += 1 clk_gen.stop() +exited = False + @cocotb.coroutine def do_test_readwrite_in_readonly(dut): yield RisingEdge(dut.clk) yield ReadOnly() dut.clk <= 0 yield ReadWrite() + exited = True + +@cocotb.coroutine +def do_test_afterdelay_in_readonly(dut): + yield RisingEdge(dut.clk) + yield ReadOnly() + yield Timer(0) + exited = True @cocotb.test(expect_error=True) def test_readwrite_in_readonly(dut): """Test doing invalid sim operation""" + global exited + exited = False clk_gen = Clock(dut.clk, 100) clk_gen.start() coro = cocotb.fork(do_test_readwrite_in_readonly(dut)) yield Timer(10000) - clk.stop() + yield [Join(coro), Timer(1000)] + clk_gen.stop() + if exited is not True: + raise cocotb.TestFailed + +@cocotb.test(expect_error=True) +def test_afterdelay_in_readonly(dut): + """Test doing invalid sim operation""" + global exited + exited = False + clk_gen = Clock(dut.clk, 100) + clk_gen.start() + coro = cocotb.fork(do_test_afterdelay_in_readonly(dut)) + yield Timer(10000) + yield [Join(coro), Timer(1000)] + clk_gen.stop() + if exited is not True: + raise cocotb.TestFailed @cocotb.coroutine def clock_one(dut): From 88eb41cabce1f9306e6253231d8f198ad9f627e5 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 3 Sep 2013 19:21:26 +0100 Subject: [PATCH 0363/2656] Introduce a monitor on the clock object to enable us to move out of non deterministic states --- cocotb/clock.py | 3 +- cocotb/decorators.py | 3 +- cocotb/scheduler.py | 113 ++++++++++++++++++++++--------------------- 3 files changed, 61 insertions(+), 58 deletions(-) diff --git a/cocotb/clock.py b/cocotb/clock.py index f1ad3b03..3c8b3920 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -27,7 +27,7 @@ A clock class """ import simulator - +import cocotb from cocotb.log import SimLog class BaseClock(object): @@ -45,6 +45,7 @@ def __init__(self, signal, period): self.period = period self.frequency = 1.0 / period * 1000000 self.hdl = None + self.clock_coro = cocotb.scheduler.add(cocotb.scheduler.internal_clock(signal)) def start(self, cycles=0): diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 4c24bbbf..b02c1492 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -246,7 +246,7 @@ def execute_function(self, event): self._event = threading.Event() self._event.result = None - coro = cocotb.scheduler.queue_external(execute_function(self, self._event)) + coro = cocotb.scheduler.queue(execute_function(self, self._event)) self._event.wait() return self._event.result @@ -283,7 +283,6 @@ def external(func): @coroutine def wrapped(*args, **kwargs): - cocotb.scheduler.set_external() # Start up the thread, this is done in coroutine context bridge = test_locker() diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index c55c3d50..dce49c36 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -39,7 +39,7 @@ import simulator import cocotb import cocotb.decorators -from cocotb.triggers import Trigger, Timer, ReadOnly, NextTimeStep, ReadWrite, NullTrigger +from cocotb.triggers import Trigger, Timer, ReadOnly, NextTimeStep, ReadWrite, RisingEdge from cocotb.log import SimLog from cocotb.result import TestComplete, TestError, ReturnValue, raise_error from threading import RLock @@ -48,6 +48,7 @@ class Scheduler(object): def __init__(self): self.waiting = collections.defaultdict(list) + self.delay_waiting = collections.defaultdict(list) self.log = SimLog("cocotb.scheduler") self.writes = {} self.writes_lock = threading.RLock() @@ -56,12 +57,13 @@ def __init__(self): self._startpoint = None self._terminate = False self._test_result = None - self._readonly = None + self._do_cleanup = None self._entry_lock = RLock() self._external_trigger = threading.Semaphore(1) - self._external_trigger._value = 0 - self._react_timer = None + self._external_trigger._value = False self._external = False + self._readonly = False + self._react_timer = None # Keep this last self._readwrite = self.add(self.move_to_rw()) @@ -72,6 +74,10 @@ def react(self, trigger): """ trigger.log.debug("Fired!") + if isinstance(trigger, ReadOnly): + self.enable_react_delay() + self._readonly = True + if trigger not in self.waiting: # This isn't actually an error - often might do event.set() without knowing # whether any coroutines are actually waiting on this event @@ -95,32 +101,37 @@ def react(self, trigger): self.schedule(coroutine, trigger=trigger) self.log.debug("Scheduled coroutine %s" % (coroutine.__name__)) + if self._readonly is False: + # We may also have possible routines that need to be added since + # the exit from ReadOnly + for ptrigger, pwaiting in self.delay_waiting.items(): + for pcoro in pwaiting: + self.delay_waiting[ptrigger].remove(pcoro) + self._add_trigger(ptrigger, pcoro) + del self.delay_waiting[ptrigger] + # If we've performed any writes that are cached then schedule # another callback for the read-write part of the sim cycle, but # if we are terminating then do not allow another callback to be - # scheduled + # scheduled, only do this if this trigger was not ReadOnly as + # Scheduling ReadWrite is a violation, it will be picked up + # on next react - if self._terminate is False and len(self.writes) and self._readwrite is None: - self._readwrite = self.add(self.move_to_rw()) + if self._readonly is False: + if self._terminate is False and len(self.writes) and self._readwrite is None: + self._readwrite = self.add(self.move_to_rw()) # If the python has caused any subsequent events to fire we might # need to schedule more coroutines before we drop back into the # simulator - while self._pending_adds: + if self._readonly is False: self._entry_lock.acquire() - coroutine = self._pending_adds.pop(0) + while self._pending_adds: + coroutine = self._pending_adds.pop(0) + self._entry_lock.release() + self.add(coroutine) + self._entry_lock.acquire() self._entry_lock.release() - self.add(coroutine) - - # if we get the lock then there was nothing in progress - # if we do not then there was so schedule a loop - # to come back in until we managed to add to the pending - # list and so can progress past this time step - result = self._external_trigger.acquire(blocking=False) - if self._external_trigger._value: - self.enable_react_delay() - else: - self._external_trigger.release() return @@ -128,8 +139,8 @@ def set_external(self): """ Take the semaphore to indicate to the react later that there an external is being added to the list """ - self._external_trigger.acquire() - self._external_trigger._value = 1 +# self._external_trigger.acquire() + self._external_trigger._value = True def playout_writes(self): if self.writes: @@ -144,15 +155,26 @@ def save_write(self, handle, args): def _add_trigger(self, trigger, coroutine): """Adds a new trigger which will cause the coroutine to continue when fired""" try: - self._entry_lock.acquire() - self.waiting[trigger].append(coroutine) - self._entry_lock.release() - # We drop the lock before calling out to the simulator (most likely consequence of prime) - trigger.prime(self.react) + # If we are in readonly for the currently firing trigger then new coroutines + # are not added to the waiting list and primed, they are instead + # added to a secondary list of events that will then be handled on the next + # entry to react when we exit ReadOnly into NextTimeStep + if self._readonly is True: + self._entry_lock.acquire() + self.delay_waiting[trigger].append(coroutine) + self._entry_lock.release() + else: + self._entry_lock.acquire() + self.waiting[trigger].append(coroutine) + self._entry_lock.release() + # We drop the lock before calling out to the simulator (most likely consequence of prime) + trigger.prime(self.react) + except TestError as e: self.waiting[trigger].remove(coroutine) # Do not re-call raise_error since the error will already be logged at point of interest raise e + except Exception as e: self.waiting[trigger].remove(coroutine) raise_error(self, "Unable to prime a trigger: %s" % str(e)) @@ -163,15 +185,6 @@ def queue(self, coroutine): self._pending_adds.append(coroutine) self._entry_lock.release() - def queue_external(self, coroutine): - """Add a coroutine that is part of an external thread""" - # We do not need to take the lock here as are protected by the external trigger - self._entry_lock.acquire() - self._pending_adds.append(coroutine) - self._entry_lock.release() - self._external_trigger._value = 0 - self._external_trigger.release() - def add(self, coroutine): """Add a new coroutine. Required because we cant send to a just started generator (FIXME)""" @@ -197,23 +210,6 @@ def add(self, coroutine): self.schedule(coroutine) return coroutine - @cocotb.decorators.coroutine - def go_to_null(self): - yield NullTrigger() - - def add_and_react(self, coroutine): - """Add a coroutine and also schedule a callback from the sim""" - self._entry_lock.acquire() - self.queue(coroutine) - - #if self._readwrite is None: - # test_coro = self.move_to_rw() - # pdb.set_trace() - #self._readwrite = self.add(test_coro) - self.add(self.go_to_null()) - - self._entry_lock.release() - def new_test(self, coroutine): self._startpoint = coroutine @@ -337,7 +333,7 @@ def finish_test(self, test_result): self._terminate = True self._test_result = test_result self.cleanup() - self._readonly = self.add(self.move_to_cleanup()) + self._do_cleanup = self.add(self.move_to_cleanup()) def cleanup(self): """ Clear up all our state @@ -357,7 +353,7 @@ def issue_result(self, test_result): def move_to_cleanup(self): yield Timer(1) self.prune_routines() - self._readonly = None + self._do_cleanup = None self.issue_result(self._test_result) self._test_result = None @@ -378,6 +374,11 @@ def move_to_rw(self): self._readwrite = None self.playout_writes() + @cocotb.decorators.coroutine + def internal_clock(self, clock): + while True: + yield RisingEdge(clock) + def enable_react_delay(self): if self._react_timer is None: self._react_timer = self.add(self.react_delay()) @@ -386,3 +387,5 @@ def enable_react_delay(self): def react_delay(self): yield NextTimeStep() self._react_timer = None + self._readonly = False + From 16caba4e2e36b8a80f7dc386fb0c5337a82b9e48 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 3 Sep 2013 19:21:43 +0100 Subject: [PATCH 0364/2656] Issue #87: Update tests --- examples/functionality/tests/test_cocotb.py | 17 ++++++----- examples/functionality/tests/test_external.py | 30 +++++++++++++++---- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index c2cb1dee..40e132b5 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -34,6 +34,7 @@ import cocotb from cocotb.triggers import Timer, Join, RisingEdge, ReadOnly, ReadWrite from cocotb.clock import Clock +from cocotb.result import ReturnValue @@ -145,6 +146,7 @@ def test_anternal_clock(dut): @cocotb.coroutine def do_test_readwrite_in_readonly(dut): + global exited yield RisingEdge(dut.clk) yield ReadOnly() dut.clk <= 0 @@ -153,12 +155,13 @@ def do_test_readwrite_in_readonly(dut): @cocotb.coroutine def do_test_afterdelay_in_readonly(dut): + global exited yield RisingEdge(dut.clk) yield ReadOnly() yield Timer(0) exited = True -@cocotb.test(expect_error=True) +@cocotb.test() def test_readwrite_in_readonly(dut): """Test doing invalid sim operation""" global exited @@ -166,13 +169,12 @@ def test_readwrite_in_readonly(dut): clk_gen = Clock(dut.clk, 100) clk_gen.start() coro = cocotb.fork(do_test_readwrite_in_readonly(dut)) - yield Timer(10000) - yield [Join(coro), Timer(1000)] + yield [Join(coro), Timer(10000)] clk_gen.stop() if exited is not True: raise cocotb.TestFailed -@cocotb.test(expect_error=True) +@cocotb.test() def test_afterdelay_in_readonly(dut): """Test doing invalid sim operation""" global exited @@ -180,8 +182,7 @@ def test_afterdelay_in_readonly(dut): clk_gen = Clock(dut.clk, 100) clk_gen.start() coro = cocotb.fork(do_test_afterdelay_in_readonly(dut)) - yield Timer(10000) - yield [Join(coro), Timer(1000)] + yield [Join(coro), Timer(100000)] clk_gen.stop() if exited is not True: raise cocotb.TestFailed @@ -191,7 +192,7 @@ def clock_one(dut): count = 0 while count is not 50: yield RisingEdge(dut.clk) - yield Timer(10000) + yield Timer(1000) count += 1 @cocotb.coroutine @@ -199,7 +200,7 @@ def clock_two(dut): count = 0 while count is not 50: yield RisingEdge(dut.clk) - yield Timer(100000) + yield Timer(10000) count += 1 @cocotb.test(expect_fail=False) diff --git a/examples/functionality/tests/test_external.py b/examples/functionality/tests/test_external.py index 67e73656..2493daac 100644 --- a/examples/functionality/tests/test_external.py +++ b/examples/functionality/tests/test_external.py @@ -36,7 +36,7 @@ import cocotb import pdb from cocotb.result import ReturnValue, TestFailure -from cocotb.triggers import Timer, Join, RisingEdge, ReadOnly, Edge +from cocotb.triggers import Timer, Join, RisingEdge, ReadOnly, Edge, ReadWrite from cocotb.clock import Clock from cocotb.decorators import external @@ -87,7 +87,7 @@ def clock_gen(clock): clock.log.warning("Clock generator finished!") -@cocotb.test(expect_fail=False) +@cocotb.test(expect_fail=False, skip=True) def test_callable(dut): """Test ability to call a function that will block but allow other coroutines to continue @@ -132,6 +132,15 @@ def test_ext_function(dut): #dut.log.info("Sleeping") return 2 +@cocotb.function +def yield_to_readwrite(dut): + yield RisingEdge(dut.clk) + dut.log.info("Returning from yield_to_readwrite") + raise ReturnValue(2) + +def test_ext_function_access(dut): + return yield_to_readwrite(dut) + def test_ext_function_return(dut): value = dut.clk.value.integer #dut.log.info("Sleeping and returning %s" % value) @@ -151,10 +160,10 @@ def test_ext_call_return(dut): """Test ability to yeild on an external non cocotb coroutine decorated function""" mon = cocotb.scheduler.queue(clock_monitor(dut)) clk_gen = Clock(dut.clk, 100) - clk_gen.start(cycles=20) + clk_gen.start() value = yield external(test_ext_function)(dut) - clk_gen.stop() dut.log.info("Value was %d" % value) + clk_gen.stop() @cocotb.test(expect_fail=False) def test_ext_call_nreturn(dut): @@ -175,9 +184,20 @@ def test_multiple_externals(dut): dut.log.info("Second one completed") clk_gen.stop() +@cocotb.test(expect_fail=False) +def test_external_from_readonly(dut): + clk_gen = Clock(dut.clk, 100) + clk_gen.start() + + yield ReadOnly() + dut.log.info("In readonly") + value = yield external(test_ext_function_access)(dut) + + clk_gen.stop() + @cocotb.test(expect_fail=True, skip=True) def ztest_ext_exit_error(dut): """Test that a premature exit of the sim at it's request still results in the clean close down of the sim world""" - yield external(test_ext_function_return)(dut) + yield external(test_ext_function)(dut) yield Timer(1000) From db1ab98e241da403a1455fd03377857d8f1c6012 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 4 Sep 2013 13:33:31 +0100 Subject: [PATCH 0365/2656] Cleanup: From Stan Bell, Fix some typos --- cocotb/monitors/avalon.py | 2 +- cocotb/scoreboard.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index 8a30b36a..dfffcd8a 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -112,7 +112,7 @@ def valid(): # Truncate the empty bits if self.bus.empty.value.integer: pkt = pkt[:-self.bus.empty.value.integer] - self.log.info("Recieved a packet of %d bytes" % len(pkt)) + self.log.info("Received a packet of %d bytes" % len(pkt)) self.log.debug(hexdump(str((pkt)))) self._recv(pkt) pkt = "" diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index e51703f2..351777e0 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -114,7 +114,7 @@ def check_received_transaction(transaction): self.errors += 1 log.error("Received a transaction but wasn't expecting anything") log.info("Got: %s" % (hexdump(str(transaction)))) - if self._imm: raise TestFailure("Recieved a transaction but wasn't expecting anything") + if self._imm: raise TestFailure("Received a transaction but wasn't expecting anything") return if type(transaction) != type(exp): From 5b27d4897a3d5cfc0c20b59f2b4a099b29a84d75 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 5 Sep 2013 15:12:29 +0100 Subject: [PATCH 0366/2656] Issue #88: Move clock implementation from C to python --- cocotb/clock.py | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/cocotb/clock.py b/cocotb/clock.py index 3c8b3920..11519fb6 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -29,6 +29,7 @@ import simulator import cocotb from cocotb.log import SimLog +from cocotb.triggers import Timer, RisingEdge class BaseClock(object): """Base class to derive from""" @@ -43,28 +44,20 @@ class Clock(BaseClock): def __init__(self, signal, period): BaseClock.__init__(self, signal) self.period = period + self.half_period = period / 2 self.frequency = 1.0 / period * 1000000 self.hdl = None - self.clock_coro = cocotb.scheduler.add(cocotb.scheduler.internal_clock(signal)) - + self.signal = signal + self.coro = None + self.mcoro = None + @cocotb.coroutine def start(self, cycles=0): - """ - cycles = 0 will not auto stop the clock - """ - if self.hdl is None: - self.hdl = simulator.create_clock(self.signal._handle, self.period, cycles) - self.log.info("Clock %s Started with period %d" % (str(self.signal), self.period)) - else: - self.log.debug("Clock %s already started" % (str(self.signal))) - - def stop(self): - if self.hdl is not None: - simulator.stop_clock(self.hdl) - self.log.info("Clock %s Stopped" % (str(self.signal))) - self.hdl = None - else: - self.log.debug("Clock %s already stopped" % (str(self.signal))) + while True: + self.signal <= 1 + yield Timer(self.half_period) + self.signal <= 0 + yield Timer(self.half_period) def __str__(self): return self.__class__.__name__ + "(%3.1fMHz)" % self.frequency From 509a88c062c5dcb291c78be1f5cbd6454db5849e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 5 Sep 2013 15:36:09 +0100 Subject: [PATCH 0367/2656] Issue #88: Move examples to generic clock --- examples/functionality/tests/test_cocotb.py | 20 +++++-------- examples/functionality/tests/test_external.py | 29 +++++-------------- 2 files changed, 15 insertions(+), 34 deletions(-) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 40e132b5..1d488a9f 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -134,13 +134,12 @@ def test_adding_a_coroutine_without_starting(dut): @cocotb.test(expect_fail=False) def test_anternal_clock(dut): """Test ability to yeild on an external non cocotb coroutine decorated function""" - clk_gen = Clock(dut.clk, 100) - clk_gen.start() + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) count = 0 while count is not 100: yield RisingEdge(dut.clk) count += 1 - clk_gen.stop() + clk_gen.kill() exited = False @@ -166,11 +165,10 @@ def test_readwrite_in_readonly(dut): """Test doing invalid sim operation""" global exited exited = False - clk_gen = Clock(dut.clk, 100) - clk_gen.start() + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) coro = cocotb.fork(do_test_readwrite_in_readonly(dut)) yield [Join(coro), Timer(10000)] - clk_gen.stop() + clk_gen.kill() if exited is not True: raise cocotb.TestFailed @@ -179,11 +177,10 @@ def test_afterdelay_in_readonly(dut): """Test doing invalid sim operation""" global exited exited = False - clk_gen = Clock(dut.clk, 100) - clk_gen.start() + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) coro = cocotb.fork(do_test_afterdelay_in_readonly(dut)) yield [Join(coro), Timer(100000)] - clk_gen.stop() + clk_gen.kill() if exited is not True: raise cocotb.TestFailed @@ -205,8 +202,7 @@ def clock_two(dut): @cocotb.test(expect_fail=False) def test_coroutine_close_down(dut): - clk_gen = Clock(dut.clk, 100) - clk_gen.start() + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) coro_one = cocotb.fork(clock_one(dut)) coro_two = cocotb.fork(clock_two(dut)) @@ -216,8 +212,6 @@ def test_coroutine_close_down(dut): dut.log.info("Back from joins") - clk_gen.stop() - @cocotb.coroutine def syntax_error(): yield Timer(100) diff --git a/examples/functionality/tests/test_external.py b/examples/functionality/tests/test_external.py index 2493daac..1fc806b0 100644 --- a/examples/functionality/tests/test_external.py +++ b/examples/functionality/tests/test_external.py @@ -99,15 +99,12 @@ def test_callable(dut): g_dut = dut create_thread(decorated_test_read) dut.log.info("Test thread created") - clk_gen = Clock(dut.clk, 100) - clk_gen.start() + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) yield Timer(100000) - clk_gen.stop() + clk_gen.kill() if test_count is not 5: print("Count was %d" % test_count) - clk_gen.stop() raise TestFailure - clk_gen.stop() @cocotb.test(expect_fail=True, skip=True) def test_callable_fail(dut): @@ -121,10 +118,9 @@ def test_callable_fail(dut): g_dut = dut create_thread(test_read) dut.log.info("Test thread created") - clk_gen = Clock(dut.clk, 100) - clk_gen.start() + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) yield Timer(100000) - clk_gen.stop() + clk_gen.kill() if test_count is not 5: raise TestFailure @@ -159,42 +155,33 @@ def clock_monitor(dut): def test_ext_call_return(dut): """Test ability to yeild on an external non cocotb coroutine decorated function""" mon = cocotb.scheduler.queue(clock_monitor(dut)) - clk_gen = Clock(dut.clk, 100) - clk_gen.start() + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) value = yield external(test_ext_function)(dut) dut.log.info("Value was %d" % value) - clk_gen.stop() @cocotb.test(expect_fail=False) def test_ext_call_nreturn(dut): """Test ability to yeild on an external non cocotb coroutine decorated function""" mon = cocotb.scheduler.queue(clock_monitor(dut)) - clk_gen = Clock(dut.clk, 100) - clk_gen.start() + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) yield external(test_ext_function)(dut) - clk_gen.stop() @cocotb.test(expect_fail=False) def test_multiple_externals(dut): - clk_gen = Clock(dut.clk, 100) - clk_gen.start() + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) value = yield external(test_ext_function)(dut) dut.log.info("First one completed") value = yield external(test_ext_function)(dut) dut.log.info("Second one completed") - clk_gen.stop() @cocotb.test(expect_fail=False) def test_external_from_readonly(dut): - clk_gen = Clock(dut.clk, 100) - clk_gen.start() + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) yield ReadOnly() dut.log.info("In readonly") value = yield external(test_ext_function_access)(dut) - clk_gen.stop() - @cocotb.test(expect_fail=True, skip=True) def ztest_ext_exit_error(dut): """Test that a premature exit of the sim at it's request still results in the From 90999786258c842a9e24b8eda308b3345cdbe322 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 6 Sep 2013 12:40:00 +0100 Subject: [PATCH 0368/2656] Fixes #90: Add new api to Monitor which takes a timeout and returns none or the packet --- cocotb/monitors/__init__.py | 24 +++++++++++++++++++++++- cocotb/triggers.py | 11 ++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index ca4fa5c2..b3129e50 100644 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -37,10 +37,11 @@ import cocotb from cocotb.decorators import coroutine -from cocotb.triggers import Edge, Event, RisingEdge, ReadOnly +from cocotb.triggers import Edge, Event, RisingEdge, ReadOnly, Timer from cocotb.binary import BinaryValue from cocotb.bus import Bus from cocotb.log import SimLog +from cocotb.result import ReturnValue class MonitorStatistics(object): @@ -62,6 +63,7 @@ def __init__(self, callback=None, event=None): event used to notify any consumers. """ self._event = event + self._wait_event = None self._recvQ = [] self._callbacks = [] self.stats = MonitorStatistics() @@ -91,6 +93,22 @@ def add_callback(self, callback): self.log.debug("Adding callback of function %s to monitor" % (callback.__name__)) self._callbacks.append(callback) + @coroutine + def wait_for_recv(self, timeout=None): + self._wait_event = Event() + if timeout: + yield [self._wait_event.wait(), Timer(timeout)] + else + yield self._wait_event.wait() + + res, pkt = self._wait_event.has_fired() + if res: + # Event was set and not the Timer, send the packet back + raise ReturnValue(pkt) + else: + # Timer fired instead + raise ReturnValue(None) + @coroutine def _monitor_recv(self): """ @@ -118,6 +136,10 @@ def _recv(self, transaction): if self._event is not None: self._event.set() + # If anyone was waiting then let them know + if self._wait_event is not None: + self._wait_event.set(data=transaction) + class BusMonitor(Monitor): """ diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 5a076127..a6b9e090 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -271,12 +271,16 @@ def __init__(self, name=""): Trigger.__init__(self) self._callback = None self.name = name + self.fired = False + self.data = None def prime(self, callback): self._callback = callback - def set(self): + def set(self, data=None): """Wake up any coroutines blocked on this event""" + self.fired = True + self.data = data if not self._callback: pass # nobody waiting self._callback(self) @@ -285,6 +289,11 @@ def wait(self): """This can be yielded to block this coroutine until another wakes it""" return self + def has_fired(self): + """Query If the Event has already been triggered. Useful when the Trigger + is used in a list""" + return self.fired, self.data + def __str__(self): return self.__class__.__name__ + "(%s)" % self.name From a3f726a6e2a7a072d3aecf80fe5999528db98233 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 6 Sep 2013 12:59:59 +0100 Subject: [PATCH 0369/2656] Fixes #90: Fixed syntax error --- cocotb/monitors/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index b3129e50..75c8905d 100644 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -98,7 +98,7 @@ def wait_for_recv(self, timeout=None): self._wait_event = Event() if timeout: yield [self._wait_event.wait(), Timer(timeout)] - else + else: yield self._wait_event.wait() res, pkt = self._wait_event.has_fired() From 8e917a850cbce4e2a827bc0f51556327a038706e Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 8 Sep 2013 10:59:34 +0100 Subject: [PATCH 0370/2656] Fixes #91: Add sync=True to driver.send method to permit back-to-back packets --- cocotb/drivers/__init__.py | 33 ++++++++++++++++++++------------- cocotb/drivers/avalon.py | 24 +++++++++++++++--------- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index aaee7158..11242279 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -119,17 +119,22 @@ def append(self, transaction, callback=None, event=None): self._pending.set() @coroutine - def send(self, transaction): + def send(self, transaction, sync=True): """ Blocking send call (hence must be "yielded" rather than called) Sends the transaction over the bus + + Args: + transaction (any): the transaction to send + + Kwargs: + sync (boolean): synchronise the transfer by waiting for risingedge """ - #yield self.busy.acquire() - yield self._send(transaction, None, None) + yield self._send(transaction, None, None, sync=sync) - def _driver_send(self, transaction): + def _driver_send(self, transaction, sync=True): """ actual impementation of the send. @@ -139,13 +144,13 @@ def _driver_send(self, transaction): @coroutine - def _send(self, transaction, callback, event): + def _send(self, transaction, callback, event, sync=True): """ assumes the caller has already acquired the busy lock releases busy lock once sending is complete """ - yield self._driver_send(transaction) + yield self._driver_send(transaction, sync=sync) # Notify the world that this transaction is complete if event: event.set() @@ -162,12 +167,13 @@ def _send_thread(self): while not self._sendQ: yield self._pending.wait() - transaction, callback, event = self._sendQ.pop(0) - # Send the pending transaction - self.log.info("Sending packet...") - yield self._send(transaction, callback, event) - self.log.info("Done, shouldn't be waiting on _send.join() anymore..") + synchronised = False + while self._senqQ: + transaction, callback, event = self._sendQ.pop(0) + self.log.debug("Sending queued packet...") + yield self._send(transaction, callback, event, sync=not synchronised) + synchronised = True class BusDriver(Driver): """ @@ -198,8 +204,9 @@ def __init__(self, entity, name, clock): @coroutine - def _driver_send(self, transaction): - yield RisingEdge(self.clock) + def _driver_send(self, transaction, sync=True): + if sync: + yield RisingEdge(self.clock) self.bus <= transaction @coroutine diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index e78eb194..7eb06207 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -170,7 +170,7 @@ def _wait_ready(self): yield ReadOnly() @coroutine - def _send_string(self, string): + def _send_string(self, string, sync=True): """ Args: string (str): A string of bytes to send over the bus @@ -192,7 +192,8 @@ def _send_string(self, string): self.bus.valid <= 0 while string: - yield clkedge + if not firstword or (firstword and sync): + yield clkedge # Insert a gap where valid is low if not self.on: @@ -240,16 +241,20 @@ def _send_string(self, string): @coroutine - def _send_iterable(self, pkt): + def _send_iterable(self, pkt, sync=True): """ Args: pkt (iterable): Will yield objects with attributes matching the signal names for each individual bus cycle """ clkedge = RisingEdge(self.clock) + firstword = True for word in pkt: - yield clkedge + if not firstword or (firstword and sync): + yield clkedge + + firstword = False # Insert a gap where valid is low if not self.on: @@ -276,7 +281,7 @@ def _send_iterable(self, pkt): self.bus.valid <= 0 @coroutine - def _driver_send(self, pkt): + def _driver_send(self, pkt, sync=True): """Send a packet over the bus Args: @@ -290,12 +295,13 @@ def _driver_send(self, pkt): # Avoid spurious object creation by recycling - self.log.info("Sending packet of length %d bytes" % len(pkt)) + self.log.debug("Sending packet of length %d bytes" % len(pkt)) self.log.debug(hexdump(pkt)) if isinstance(pkt, str): - yield self._send_string(pkt) + yield self._send_string(pkt, sync=sync) else: - yield self._send_iterable(pkt) + yield self._send_iterable(pkt, sync=sync) + + self.log.info("Sucessfully sent packet of length %d bytes" % (len(pkt))) - self.log.info("Packet sent successfully") From 399aba3ca5b64b0ce481dc4900e345823d07966e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 9 Sep 2013 16:51:48 +0100 Subject: [PATCH 0371/2656] Bug #91: Fix typo, senqQ -> sendQ --- cocotb/drivers/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index 11242279..d3bbd928 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -161,6 +161,7 @@ def _send(self, transaction, callback, event, sync=True): @coroutine def _send_thread(self): + import pdb while True: # Sleep until we have something to send @@ -169,7 +170,9 @@ def _send_thread(self): synchronised = False - while self._senqQ: + pdb.set_trace() + + while self._sendQ: transaction, callback, event = self._sendQ.pop(0) self.log.debug("Sending queued packet...") yield self._send(transaction, callback, event, sync=not synchronised) From 0d9daf340b212473ec9a43294502ae9b180b03f3 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 9 Sep 2013 16:54:21 +0100 Subject: [PATCH 0372/2656] Bug #91: Remove debug commit --- cocotb/drivers/__init__.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index d3bbd928..1dddfe5b 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -161,7 +161,6 @@ def _send(self, transaction, callback, event, sync=True): @coroutine def _send_thread(self): - import pdb while True: # Sleep until we have something to send @@ -170,8 +169,6 @@ def _send_thread(self): synchronised = False - pdb.set_trace() - while self._sendQ: transaction, callback, event = self._sendQ.pop(0) self.log.debug("Sending queued packet...") From d8249c5aaa57c27157d9b8b8d00e53b966d76456 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 9 Sep 2013 17:19:41 +0100 Subject: [PATCH 0373/2656] Fixes #94: use raise_error to fail test and get stack trace and print a more helpful message. Plus add regression test --- cocotb/scheduler.py | 2 +- examples/functionality/tests/test_cocotb.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index dce49c36..8bd30adb 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -308,7 +308,7 @@ def schedule(self, coroutine, trigger=None): trigger.addpeers(result) self._add_trigger(trigger, coroutine) else: - raise TestError(("Unable to schedule coroutine since it's returning stuff %s" % repr(result))) + raise_error(self, "Coroutine %s yield something that was not a trigger or a coroutine, did you forget to decorate?" % (str(coroutine))) # TestComplete indication is game over, tidy up except TestComplete as test_result: diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 1d488a9f..ce6ff3c2 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -64,6 +64,13 @@ def test_function_not_a_coroutine_fork(dut): cocotb.fork(function_not_a_coroutine()) yield Timer(500) +def normal_function(dut): + return True + +@cocotb.test(expect_error=True) +def test_function_not_decorated(dut): + yield normal_function(dut) + @cocotb.test(expect_fail=False) def test_function_reentrant_clock(dut): """Test yielding a reentrant clock""" From 40f900e46119e60c38d1f98e5f4ad88a66d2e8f3 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 10 Sep 2013 12:28:14 +0100 Subject: [PATCH 0374/2656] Issue #95: Add clear() method to event and fix up usage in drivers/monitors --- cocotb/drivers/__init__.py | 3 +++ cocotb/monitors/__init__.py | 17 ++++++++--------- cocotb/triggers.py | 9 +++++---- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index 1dddfe5b..f8fdec32 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -165,16 +165,19 @@ def _send_thread(self): # Sleep until we have something to send while not self._sendQ: + self._pending.clear() yield self._pending.wait() synchronised = False + # Send in all the queued packets, only synchronise on the first send while self._sendQ: transaction, callback, event = self._sendQ.pop(0) self.log.debug("Sending queued packet...") yield self._send(transaction, callback, event, sync=not synchronised) synchronised = True + class BusDriver(Driver): """ Wrapper around common functionality for busses which have: diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index 75c8905d..1215b57b 100644 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -67,6 +67,7 @@ def __init__(self, callback=None, event=None): self._recvQ = [] self._callbacks = [] self.stats = MonitorStatistics() + self._wait_event = Event() # Subclasses may already set up logging if not hasattr(self, "log"): @@ -95,19 +96,16 @@ def add_callback(self, callback): @coroutine def wait_for_recv(self, timeout=None): - self._wait_event = Event() if timeout: - yield [self._wait_event.wait(), Timer(timeout)] + t = Timer(timeout) + fired = yield [self._wait_event.wait(), Timer(timeout)] + if fired is t: + raise ReturnValue(None) else: yield self._wait_event.wait() - res, pkt = self._wait_event.has_fired() - if res: - # Event was set and not the Timer, send the packet back - raise ReturnValue(pkt) - else: - # Timer fired instead - raise ReturnValue(None) + pkt = self._wait_event.data + raise ReturnValue(pkt) @coroutine def _monitor_recv(self): @@ -139,6 +137,7 @@ def _recv(self, transaction): # If anyone was waiting then let them know if self._wait_event is not None: self._wait_event.set(data=transaction) + self._wait_event.clear() class BusMonitor(Monitor): diff --git a/cocotb/triggers.py b/cocotb/triggers.py index a6b9e090..3783bec8 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -289,10 +289,11 @@ def wait(self): """This can be yielded to block this coroutine until another wakes it""" return self - def has_fired(self): - """Query If the Event has already been triggered. Useful when the Trigger - is used in a list""" - return self.fired, self.data + def clear(self): + """Clear this event that's fired. + + Subsequent calls to wait will block until set() is called again""" + self.fired = False def __str__(self): return self.__class__.__name__ + "(%s)" % self.name From b4c3981d58d6761530e7838d4b644d4e0346c666 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 10 Sep 2013 12:48:11 +0100 Subject: [PATCH 0375/2656] Cleanup: correct error in previous commit --- cocotb/monitors/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index 1215b57b..80008587 100644 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -98,7 +98,7 @@ def add_callback(self, callback): def wait_for_recv(self, timeout=None): if timeout: t = Timer(timeout) - fired = yield [self._wait_event.wait(), Timer(timeout)] + fired = yield [self._wait_event.wait(), t] if fired is t: raise ReturnValue(None) else: From ba473b2eb9251a1a30fcfe68578217d8fe208c69 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 10 Sep 2013 13:36:04 +0100 Subject: [PATCH 0376/2656] Cleanup: Don't attempt to call event._callback() if not set --- cocotb/triggers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 3783bec8..ce904906 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -282,7 +282,7 @@ def set(self, data=None): self.fired = True self.data = data if not self._callback: - pass # nobody waiting + return self._callback(self) def wait(self): From e9ae87c2bb5177aff27ee32687f8258d05804ca1 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 10 Sep 2013 14:08:11 +0100 Subject: [PATCH 0377/2656] Cleanup: Fix up custom dependencies for Makefiles --- makefiles/simulators/Makefile.vcs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index 65a26741..f1a27996 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -59,22 +59,22 @@ regression: build_simv results.xml MODULE_FILES:=$(shell for X in $(echo "test1,test2" | sed 's/,/ /g'); do echo $X.py; done) PYTHON_FILES:=$(shell find $(SIM_ROOT)/cocotb/ -name "*.py" | xargs) $(MODULE_FILES) -results.xml: $(SIM_BUILD)/simv $(PYTHON_FILES) $(CUSTOM_DEPS) +results.xml: $(SIM_BUILD)/simv $(PYTHON_FILES) $(CUSTOM_SIM_DEPS) -PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ $(SIM_BUILD)/simv +define+COCOTB_SIM=1 $(SIM_ARGS) $(EXTRA_ARGS) -gdb: $(SIM_BUILD)/simv $(PYTHON_FILES) +gdb: $(SIM_BUILD)/simv $(PYTHON_FILES) $(CUSTOM_SIM_DEPS) -PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - gdb --args $(SIM_BUILD)/simv +define+COCOTB_SIM=1 $(EXTRA_ARGS) + gdb --args $(SIM_BUILD)/simv +define+COCOTB_SIM=1 $(SIM_ARGS) $(EXTRA_ARGS) -dve: $(SIM_BUILD)/simv_dve +dve: $(SIM_BUILD)/simv_dve $(CUSTOM_COMPILE_DEPS) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ vcs +acc+1 +vpi+1+assertion -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -gui -load libgpi.so $(COMPILE_ARGS) $(VERILOG_SOURCES) .PHONY: build_simv build_simv: $(SIM_BUILD)/simv -$(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab libs_native +$(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab libs_native $(CUSTOM_COMPILE_DEPS) cd $(SIM_BUILD) && \ LD_LIBRARY_PATH=$(LIB_DIR) TOPLEVEL=$(TOPLEVEL) \ vcs +acc+1 +vpi -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -debug -load libgpi.so $(COMPILE_ARGS) $(VERILOG_SOURCES) From 05b37f6b80ae784778de53019e845a801a179f06 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Mon, 16 Sep 2013 19:33:35 +0100 Subject: [PATCH 0378/2656] Cleanup: Ensure we don't erroneously fail the test --- cocotb/scoreboard.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index 351777e0..6c29d201 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -140,7 +140,10 @@ def check_received_transaction(transaction): log.warning(hexdiffs(exp, transaction)) if self._imm: raise TestFailure("Received transaction differed from expected transaction") else: - log.debug("Received expected transaction %d bytes" % (len(transaction))) - log.debug(repr(transaction)) + # Don't want to fail the test if we're passed something without __len__ + try: + log.debug("Received expected transaction %d bytes" % (len(transaction))) + log.debug(repr(transaction)) + except: pass monitor.add_callback(check_received_transaction) From 8a52de7abd7ab773c0bbb3933fb0bcf0a4e88267 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 17 Sep 2013 18:14:47 +0100 Subject: [PATCH 0379/2656] Cleanup: Allow long assignments --- cocotb/handle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 26061cc2..2cd00517 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -121,7 +121,7 @@ def setimeadiatevalue(self, value): value = BinaryValue(value=cocotb.utils.pack(value), bits=len(self)) if isinstance(value, BinaryValue): simulator.set_signal_val_str(self._handle, value.binstr) - elif isinstance(value, int): + elif isinstance(value, (int, long)): simulator.set_signal_val(self._handle, value) else: self.log.critical("Unsupported type for value assignment: %s (%s)" % (type(value), repr(value))) From ffbbf00b63a8cf3af8b433a8a17ed73b5be24807 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 22 Sep 2013 11:24:25 +0100 Subject: [PATCH 0380/2656] Add TestFactory class to regression module --- cocotb/regression.py | 131 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/cocotb/regression.py b/cocotb/regression.py index 6de69e26..321fe47b 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -29,6 +29,8 @@ import time import logging +import inspect +from itertools import product import simulator @@ -180,3 +182,132 @@ def execute(self): else: self.tear_down() + +def _create_test(function, name, documentation, mod, *args, **kwargs): + """Factory function to create tests, avoids late binding + + Creates a test dynamically. The test will call the supplied + function with the supplied arguments. + + Args: + function: (function) the test function to run + + name: (string) the name of the test + + documentation: (string) the docstring for the test + + mod: (module) the module this function belongs to + + *args: remaining args to pass to test function + + Kwaygs: + **kwargs: passed to the test function + + Returns: + decorated test function + """ + def _my_test(dut): + yield function(dut, *args, **kwargs) + + _my_test.__name__ = name + _my_test.__doc__ = documentation + _my_test.__module__ = mod.__name__ + return cocotb.test()(_my_test) + + +class TestFactory(object): + + """ + Used to automatically generate tests. + + Assuming we have a common test function that will run a test. This test + function will take keyword arguments (for example generators for each of + the input interfaces) and generate tests that call the supplied function. + + This Factory allows us to generate sets of tests based on the different + permutations of the possible arguments to the test function. + + For example if we have a module that takes backpressure and idles and + have some packet generations routines gen_a and gen_b. + + tf = TestFactory(run_test) + + tf.add_option('data_in', [gen_a, gen_b]) + tf.add_option('backpressure', [None, random_backpressure]) + tf.add_option('idles', [None, random_idles]) + tf.generate_tests() + + We would get the following tests: + gen_a with no backpressure and no idles + gen_a with no backpressure and random_idles + gen_a with random_backpressure and no idles + gen_a with random_backpressure and random_idles + gen_b with no backpressure and no idles + gen_b with no backpressure and random_idles + gen_b with random_backpressure and no idles + gen_b with random_backpressure and random_idles + + The tests are appended to the calling module for aut-discovery. + + Tests are simply named test_function_N. The docstring for the test (hence + the test description) includes the name and description of each generator. + """ + + def __init__(self, test_function, *args): + """ + Args: + test_function (function): the function that executes a test. + Must take 'dut' as the first argument. + + *args: Remaining args are passed directly to the test function. + Note that these arguments are not varied. An argument that + varies with each test must be a keyword argument to the + test function. + """ + if not isinstance(test_function, cocotb.coroutine): + raise TypeError("TestFactory requires a cocotb coroutine") + self.test_function = test_function + self.name = self.test_function._func.__name__ + + self.args = args + self.kwargs = {} + + def add_option(self, name, optionlist): + """Add a named option to the test. + + Args: + name (string): name of the option. passed to test as a keyword + argument + + optionlist (list): A list of possible options for this test knob + """ + self.kwargs[name] = optionlist + + def generate_tests(self): + """ + Generates exhasutive set of tests using the cartesian product of the + possible keyword arguments. + + The generated tests are appended to the namespace of the calling + module. + """ + + frm = inspect.stack()[1] + mod = inspect.getmodule(frm[0]) + + d = self.kwargs + + for index, testoptions in enumerate( (dict(zip(d, v)) for v in product(*d.values())) ): + + name = "%s_%03d" % (self.name, index + 1) + doc = "Automatically generated test\n\n" + + for optname, optvalue in testoptions.iteritems(): + if callable(optvalue): + doc += "\t%s: %s (%s)\n" % (optname, optvalue.__name__, optvalue.__doc__.split('\n')[0]) + else: + doc += "\t%s: %s\n" % (optname, repr(optvalue)) + + cocotb.log.debug("Adding generated test \"%s\" to module \"%s\"" % (name, mod.__name__)) + setattr(mod, name, _create_test(self.test_function, name, doc, mod, *self.args, **testoptions)) + From 0f71123596bc68a9202af74dc7de542c614c370d Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 22 Sep 2013 16:17:15 +0100 Subject: [PATCH 0381/2656] Correct spelling of method name --- cocotb/handle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 2cd00517..392e8232 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -101,7 +101,7 @@ def getvalue(self): result.binstr = self._get_value_str() return result - def setimeadiatevalue(self, value): + def setimmediatevalue(self, value): """ Set the value of the underlying simulation object to value. From 57a95447f4fa1c72699fda3a5348f4ca070f6230 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 22 Sep 2013 16:22:16 +0100 Subject: [PATCH 0382/2656] Cleanup: Don't report a warning if a test ended successfully --- cocotb/decorators.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index b02c1492..cb7fa53e 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -104,7 +104,10 @@ def send(self, value): try: return self._coro.send(value) except TestComplete as e: - self.log.info(str(e)) + if isinstance(e, TestFailure): + self.log.warning(str(e)) + else: + self.log.info(str(e)) raise except ReturnValue as e: self.retval = e.retval @@ -181,7 +184,10 @@ def send(self, value): self.log.debug("Sending trigger %s" % (str(value))) return self._coro.send(value) except TestComplete as e: - self.log.info(str(e)) + if isinstance(e, TestFailure): + self.log.warning(str(e)) + else: + self.log.info(str(e)) e.stderr.write("\n".join(self.error_messages)) raise except StopIteration: From 615a8d86306159e4c845a46065c8ec697c1cc405 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 22 Sep 2013 16:23:03 +0100 Subject: [PATCH 0383/2656] Correct imports --- cocotb/drivers/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index f8fdec32..4cea981c 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -34,7 +34,7 @@ import cocotb from cocotb.decorators import coroutine -from cocotb.triggers import Event, RisingEdge, ReadOnly, Timer, NextTimeStep +from cocotb.triggers import Event, RisingEdge, ReadOnly, Timer, NextTimeStep, Edge from cocotb.bus import Bus from cocotb.log import SimLog from cocotb.result import ReturnValue From 313d7eabd3a195f78966c406a4a5285270005a98 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 22 Sep 2013 16:23:32 +0100 Subject: [PATCH 0384/2656] Correct typo of cbhdl in Edge --- cocotb/triggers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index ce904906..7124bb32 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -127,7 +127,7 @@ def __init__(self, signal): def prime(self, callback): """Register notification of a value change via a callback""" - if simulator.register_value_change_callback(self.chbdl, self.signal._handle, callback, self): + if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, callback, self): raise_error(self, "Unable set up %s Trigger" % (str(self))) def __str__(self): From 1b4fc9e034b21a8c8cf2d17fbf654c9cd31e622e Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 22 Sep 2013 16:24:15 +0100 Subject: [PATCH 0385/2656] Fix type of setimmediatevalue --- cocotb/scheduler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 8bd30adb..ca60459d 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -146,7 +146,7 @@ def playout_writes(self): if self.writes: while self.writes: handle, args = self.writes.popitem() - handle.setimeadiatevalue(args) + handle.setimmediatevalue(args) def save_write(self, handle, args): From e5fd22f67769a1a06c9b4831b25073e016f2b950 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 22 Sep 2013 20:07:24 +0100 Subject: [PATCH 0386/2656] Reduce the verbosity of optional signals in Bus --- cocotb/bus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/bus.py b/cocotb/bus.py index 25e9d7bf..11f6ab5c 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -82,7 +82,7 @@ def __init__(self, entity, name, signals, optional_signals=[]): setattr(self, signal, hdl) self._signals[signal] = getattr(self, signal) else: - self._entity.log.info("Ignoring optional missing signal %s on bus %s" + self._entity.log.debug("Ignoring optional missing signal %s on bus %s" % (signal, name)) From 916258ff7ba9c0645328c7d28975771281c5ee8f Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 22 Sep 2013 20:07:48 +0100 Subject: [PATCH 0387/2656] Ensure that coroutines have a __name__ attribute --- cocotb/decorators.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index cb7fa53e..ef1b9e1c 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -210,6 +210,7 @@ class coroutine(object): def __init__(self, func): self._func = func self.log = SimLog("cocotb.function.%s" % self._func.__name__, id(self)) + self.__name__ = self._func.__name__ def __call__(self, *args, **kwargs): try: From e1fafe493de63b5386c978d516b7d2c062957e36 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 22 Sep 2013 20:09:38 +0100 Subject: [PATCH 0388/2656] Fix for Avalon config dictionary --- cocotb/drivers/avalon.py | 11 ++++++----- cocotb/monitors/avalon.py | 6 +++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 7eb06207..45075633 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -68,7 +68,7 @@ class AvalonMaster(AvalonMM): """ def __init__(self, entity, name, clock): AvalonMM.__init__(self, entity, name, clock) - self.log.warning("AvalonMaster created") + self.log.debug("AvalonMaster created") @coroutine def read(self, address): @@ -147,14 +147,14 @@ class AvalonSTPkts(ValidatedBusDriver): } def __init__(self, *args, **kwargs): + config = kwargs.pop('config', {}) ValidatedBusDriver.__init__(self, *args, **kwargs) - self.config = AvalonSTPkts._default_config - - config = kwargs.pop('config', {}) + self.config = AvalonSTPkts._default_config.copy() for configoption, value in config.iteritems(): self.config[configoption] = value + self.log.debug("Setting config option %s to %s" % (configoption, str(value))) @coroutine def _wait_ready(self): @@ -188,8 +188,9 @@ def _send_string(self, string, sync=True): self.bus.empty <= 0 self.bus.startofpacket <= 0 self.bus.endofpacket <= 0 - self.bus.error <= 0 self.bus.valid <= 0 + if hasattr(self.bus, 'error'): + self.bus.error <= 0 while string: if not firstword or (firstword and sync): diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index dfffcd8a..bfe9db46 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -73,14 +73,14 @@ class AvalonSTPkts(BusMonitor): } def __init__(self, *args, **kwargs): + config = kwargs.pop('config', {}) BusMonitor.__init__(self, *args, **kwargs) - self.config = AvalonSTPkts._default_config - - config = kwargs.pop('config', {}) + self.config = AvalonSTPkts._default_config.copy() for configoption, value in config.iteritems(): self.config[configoption] = value + self.log.debug("Setting config option %s to %s" % (configoption, str(value))) @coroutine def _monitor_recv(self): From ede62d312eb1e3d5b746bfdb73999a43d81c9ad0 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 22 Sep 2013 20:10:05 +0100 Subject: [PATCH 0389/2656] Issue #9: Expanded information about make system slightly --- documentation/source/building.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/documentation/source/building.rst b/documentation/source/building.rst index 23008997..ece1c2af 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -7,6 +7,14 @@ Make System Makefiles are provided for a variety of simulators in cocotb/makefiles/simulators. The common Makefile cocotb/makefiles/Makefile.sim includes the appropriate simulator makefile based on the contents of the SIM variable. +Targets +------- + +Makefiles define two targets, 'regression' and 'sim', the default target is sim. + +Both rules create a results file in the calling directory called 'results.xml'. This file is a JUnit-compatible output file suitable for use with `Jenkins `_. The 'sim' targets unconditionally re-runs the simulator whereas the regression target only re-builds if any dependencies have changed. + + Variables --------- From e41b8d5178a674f7d14676748b143dd2675dceb3 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 22 Sep 2013 20:11:06 +0100 Subject: [PATCH 0390/2656] Update version numbers of repo and documentation to current --- documentation/source/conf.py | 4 ++-- version | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/source/conf.py b/documentation/source/conf.py index f7a0038d..7f41fbde 100644 --- a/documentation/source/conf.py +++ b/documentation/source/conf.py @@ -48,9 +48,9 @@ # built documents. # # The short X.Y version. -version = '0.1' +version = '0.3' # The full version, including alpha/beta/rc tags. -release = '0.1' +release = '0.3' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/version b/version index 2e94ead3..61acab09 100644 --- a/version +++ b/version @@ -1 +1 @@ -VERSION=0.1 +VERSION=0.3 From ec0afaf9c12a36eafd8007878449c1e36d5369d8 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 22 Sep 2013 20:15:14 +0100 Subject: [PATCH 0391/2656] Issue #77: Add real-world example with streaming interfaces and CSR access --- examples/Makefile | 3 +- examples/endian_swapper/hdl/endian_swapper.sv | 137 +++++++++++++++++ examples/endian_swapper/tests/Makefile | 37 +++++ .../tests/test_endian_swapper.py | 144 ++++++++++++++++++ 4 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 examples/endian_swapper/hdl/endian_swapper.sv create mode 100644 examples/endian_swapper/tests/Makefile create mode 100644 examples/endian_swapper/tests/test_endian_swapper.py diff --git a/examples/Makefile b/examples/Makefile index 751ec53e..ff04a555 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -29,7 +29,8 @@ SMOKE_TESTS := plusargs \ sim_exit/tests \ - functionality/tests + functionality/tests \ + endian_swapper/tests .PHONY: $(SMOKE_TESTS) diff --git a/examples/endian_swapper/hdl/endian_swapper.sv b/examples/endian_swapper/hdl/endian_swapper.sv new file mode 100644 index 00000000..176f8011 --- /dev/null +++ b/examples/endian_swapper/hdl/endian_swapper.sv @@ -0,0 +1,137 @@ +/* + +Endian swapping module. + +Simple example with Avalon streaming interfaces and a CSR bus + +Exposes 2 32-bit registers via the Avalon-MM interface + + Address 0: bit 0 [R/W] byteswap enable + bits 31-1: [N/A] reserved + Adress 1: bits 31-0: [RO] packet count + +*/ + +module endian_swapper #( + parameter DATA_BYTES = 8 +) ( + input clk, + input reset_n, + + input [DATA_BYTES*8-1:0] stream_in_data, + input [$clog2(DATA_BYTES)-1:0] stream_in_empty, + input stream_in_valid, + input stream_in_startofpacket, + input stream_in_endofpacket, + output reg stream_in_ready, + + output reg [DATA_BYTES*8-1:0] stream_out_data, + output reg [$clog2(DATA_BYTES)-1:0] stream_out_empty, + output reg stream_out_valid, + output reg stream_out_startofpacket, + output reg stream_out_endofpacket, + input stream_out_ready, + + input [1:0] csr_address, + output reg [31:0] csr_readdata, + output reg csr_readdatavalid, + input csr_read, + input csr_write, + output reg csr_waitrequest, + input [31:0] csr_writedata +); + +reg in_packet; +reg byteswapping; +reg [31:0] packet_count; + +function [DATA_BYTES*8-1:0] byteswap(input [DATA_BYTES*8-1:0] data); + +/* + // FIXME Icarus doesn't seem to like this.... + reg [$clog2(DATA_BYTES)-1:0] i; + + for (i=0; i Date: Sun, 22 Sep 2013 21:22:21 +0100 Subject: [PATCH 0392/2656] Cleanup: Remove setup.py --- setup.py | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 setup.py diff --git a/setup.py b/setup.py deleted file mode 100644 index b534ebca..00000000 --- a/setup.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python - -from setuptools import setup, find_packages -from distutils.command.install import install as DistutilsInstall - -class CocotbInstall(DistutilsInstall): - def run(self): - print("Build support libs\n") - DistutilsInstall.run(self) - -setup( - name='cocotb', - version='0.0.1', - description='Coroutine Cosimulation Test Bench', - author='Potential Ventures', - author_email='coctob@potentialventures.com', - packages=find_packages('.'), - cmdclass={'install': CocotbInstall}, - include_package_data = True, - long_description="""\ - Cocotb is a coroutines-based cosimulation test bench framework for rapid - development of FPGA components - """, - classifiers=[ - "License :: BSD", - "Programming Language :: Python", - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "Topic :: Internet", - ], - keywords='simulation', - license='BSD', - ) From 622d7b625f4bba8ddd6c39f99acbe657162dcd99 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 22 Sep 2013 23:05:07 +0100 Subject: [PATCH 0393/2656] Ensure that only one coroutine can drive the Avalon-MM interface at any time --- cocotb/drivers/avalon.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 45075633..ab58b169 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -30,7 +30,7 @@ NB Currently we only support a very small subset of functionality """ from cocotb.decorators import coroutine -from cocotb.triggers import RisingEdge, ReadOnly, NextTimeStep +from cocotb.triggers import RisingEdge, ReadOnly, NextTimeStep, Event from cocotb.drivers import BusDriver, ValidatedBusDriver from cocotb.utils import hexdump from cocotb.binary import BinaryValue @@ -69,6 +69,19 @@ class AvalonMaster(AvalonMM): def __init__(self, entity, name, clock): AvalonMM.__init__(self, entity, name, clock) self.log.debug("AvalonMaster created") + self.busy_event = Event("%s_busy" % name) + self.busy = False + + @coroutine + def _acquire_lock(self): + if self.busy: + yield self.busy_event.wait() + self.busy_event.clear() + self.busy = True + + def _release_lock(self): + self.busy = False + self.busy_event.set() @coroutine def read(self, address): @@ -78,6 +91,8 @@ def read(self, address): but syntactically it blocks. See http://www.altera.com/literature/manual/mnl_avalon_spec_1_3.pdf """ + yield self._acquire_lock() + # Apply values for next clock edge yield RisingEdge(self.clock) self.bus.address <= address @@ -97,7 +112,7 @@ def read(self, address): yield ReadOnly() data = self.bus.readdata.value yield NextTimeStep() - + self._release_lock() raise ReturnValue(data) @coroutine @@ -107,6 +122,8 @@ def write(self, address, value): value. See http://www.altera.com/literature/manual/mnl_avalon_spec_1_3.pdf """ + yield self._acquire_lock() + # Apply valuse to bus yield RisingEdge(self.clock) self.bus.address <= address @@ -119,7 +136,7 @@ def write(self, address, value): # Deassert write yield RisingEdge(self.clock) self.bus.write <= 0 - + self._release_lock() class AvalonSlave(AvalonMM): From 98dd0a3ce00f5753d76930c7e84655d1a36eecbd Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 22 Sep 2013 23:05:59 +0100 Subject: [PATCH 0394/2656] Issue #77: Update example so that it passes tests --- examples/endian_swapper/hdl/endian_swapper.sv | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/examples/endian_swapper/hdl/endian_swapper.sv b/examples/endian_swapper/hdl/endian_swapper.sv index 176f8011..fe9c4eec 100644 --- a/examples/endian_swapper/hdl/endian_swapper.sv +++ b/examples/endian_swapper/hdl/endian_swapper.sv @@ -41,9 +41,12 @@ module endian_swapper #( input [31:0] csr_writedata ); + +reg flush_pipe; reg in_packet; reg byteswapping; reg [31:0] packet_count; +reg stream_in_endofpacket_d; function [DATA_BYTES*8-1:0] byteswap(input [DATA_BYTES*8-1:0] data); @@ -65,17 +68,22 @@ function [DATA_BYTES*8-1:0] byteswap(input [DATA_BYTES*8-1:0] data); endfunction +always @(*) + stream_out_valid = (stream_in_valid & ~stream_out_endofpacket) | flush_pipe; always @(posedge clk or negedge reset_n) begin if (!reset_n) begin - stream_out_valid <= 1'b0; + flush_pipe <= 1'b0; in_packet <= 1'b0; packet_count <= 32'd0; end else begin - stream_out_valid <= stream_in_valid; + if (flush_pipe & stream_out_ready) + flush_pipe <= 1'b0; + else if (!flush_pipe) + flush_pipe <= stream_in_endofpacket & stream_in_valid & stream_out_ready; - if (stream_out_ready) begin + if (stream_out_ready & stream_in_valid) begin stream_out_empty <= stream_in_empty; stream_out_startofpacket <= stream_in_startofpacket; stream_out_endofpacket <= stream_in_endofpacket; @@ -99,8 +107,8 @@ end -always @(reset_n, stream_in_startofpacket, stream_in_valid) - csr_waitrequest = !reset_n || in_packet || (stream_in_startofpacket & stream_in_valid); +always @(*) + csr_waitrequest = !reset_n || in_packet || (stream_in_startofpacket & stream_in_valid) || flush_pipe; // Workaround Icarus bug where a simple assign doesn't work @@ -110,7 +118,8 @@ always @(stream_out_ready) always @(posedge clk or negedge reset_n) begin if (!reset_n) begin - byteswapping <= 1'b0; + byteswapping <= 1'b0; + csr_readdatavalid <= 1'b0; end else begin if (csr_read) begin csr_readdatavalid <= !csr_waitrequest; From b36f34eb3c3e563bdeb7b1d20014599f5738ad1e Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Mon, 23 Sep 2013 07:27:35 +0100 Subject: [PATCH 0395/2656] Issue #77: Clean-up endian_swapper.sv --- examples/endian_swapper/hdl/endian_swapper.sv | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/examples/endian_swapper/hdl/endian_swapper.sv b/examples/endian_swapper/hdl/endian_swapper.sv index fe9c4eec..ecf40d1e 100644 --- a/examples/endian_swapper/hdl/endian_swapper.sv +++ b/examples/endian_swapper/hdl/endian_swapper.sv @@ -4,6 +4,9 @@ Endian swapping module. Simple example with Avalon streaming interfaces and a CSR bus +Avalon-ST has readyLatency of 0 +Avalon-MM has fixed readLatency of 1 + Exposes 2 32-bit registers via the Avalon-MM interface Address 0: bit 0 [R/W] byteswap enable @@ -42,14 +45,8 @@ module endian_swapper #( ); -reg flush_pipe; -reg in_packet; -reg byteswapping; -reg [31:0] packet_count; -reg stream_in_endofpacket_d; function [DATA_BYTES*8-1:0] byteswap(input [DATA_BYTES*8-1:0] data); - /* // FIXME Icarus doesn't seem to like this.... reg [$clog2(DATA_BYTES)-1:0] i; @@ -57,6 +54,8 @@ function [DATA_BYTES*8-1:0] byteswap(input [DATA_BYTES*8-1:0] data); for (i=0; i Date: Mon, 23 Sep 2013 22:16:21 +0100 Subject: [PATCH 0396/2656] Issue #86: Mods to makefiles --- lib/Makefile | 37 ++++++++++++++++------------ lib/embed/Makefile | 20 ++++++--------- lib/gpi/Makefile | 17 ++++++------- lib/simulator/Makefile | 21 ++++++---------- lib/vpi_shim/Makefile | 20 ++++++--------- makefiles/Makefile.inc | 3 +-- makefiles/Makefile.rules | 6 ++--- makefiles/simulators/Makefile.icarus | 4 +-- makefiles/simulators/Makefile.vcs | 2 +- 9 files changed, 59 insertions(+), 71 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index a80f86e6..3c1fefe1 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -27,27 +27,32 @@ include $(SIM_ROOT)/makefiles/Makefile.inc -CLIBS:= $(SIM_ROOT)/lib/gpi \ - $(SIM_ROOT)/lib/embed \ - $(SIM_ROOT)/lib/vpi_shim \ - $(SIM_ROOT)/lib/simulator +$(LIB_OBJ_DIR): + mkdir -p $@ -libs_native: $(CLIBS) +$(LIB_DIR): + mkdir -p $@ -force_32: - ARCH=i686 make +CLIBS := $(LIB_DIR) $(LIB_OBJ_DIR) -$(SIM_ROOT)/lib/vpi_shim: $(SIM_ROOT)/lib/gpi $(SIM_ROOT)/lib/embed -$(SIM_ROOT)/lib/simulator: $(SIM_ROOT)/lib/vpi_shim +include $(SIM_ROOT)/lib/gpi/Makefile +#include $(SIM_ROOT)/lib/embed/Makefile +#include $(SIM_ROOT)/lib/vpi_shim/Makefile +#include $(SIM_ROOT)/lib/simulator/Makefile +#include $(SIM_ROOT)/makefiles/Makefile.rules -$(LIB_OBJ_DIR): - mkdir -p $@ +libs: $(CLIBS) -$(LIB_DIR): $(LIB_OBJ_DIR) - mkdir -p $@ +#libs_native: $(LIB_DIR) all + +force_32: + ARCH=i686 make + +#vpi_shim: gpi embed +#simulator: vpi_shim -$(CLIBS): $(LIB_DIR) - $(MAKE) -C $@ +#$(CLIBS): +# $(MAKE) -C $(SIM_ROOT)/lib/$@ -clean_lib: +clean:: -@rm -rf $(BUILD_DIR) diff --git a/lib/embed/Makefile b/lib/embed/Makefile index 5752dc8c..e9bfc7b6 100644 --- a/lib/embed/Makefile +++ b/lib/embed/Makefile @@ -25,20 +25,16 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -include $(SIM_ROOT)/makefiles/Makefile.inc +LIB_BASE := libcocotb +LIB_NAME := $(LIB_BASE).so -INCLUDES += -GCC_ARGS += -LIBS := $(PYLIBS) -LD_PATH := -L$(LIB_DIR) +INCLUDES += +GCC_ARGS += +LIBS := $(PYLIBS) +LD_PATH := -L$(LIB_DIR) -SRCS := gpi_embed.c +SRCS := gpi_embed.c -LIB_NAME := libcocotb.so - -all: $(LIB_DIR)/$(LIB_NAME) - -clean: - rm -rf $(OBJ_DIR) +CLIBS += $(LIB_DIR)/$(LIB_NAME) include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index 5565bb12..cd055845 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -25,19 +25,16 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +LIB_BASE := libgpilog +LIB_NAME := $(LIB_BASE).so include $(SIM_ROOT)/makefiles/Makefile.inc -INCLUDES += -GCC_ARGS += -DFILTER -LIBS := $(PYLIBS) -SRCS := gpi_logging.c +INCLUDES += +GCC_ARGS += -DFILTER +LIBS := $(PYLIBS) +SRCS := gpi_logging.c -LIB_NAME := libgpilog.so - -all: $(LIB_DIR)/$(LIB_NAME) - -clean: - rm -rf $(OBJ_DIR) +CLIBS += $(LIB_DIR)/$(LIB_NAME) include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/lib/simulator/Makefile b/lib/simulator/Makefile index 80b37ef3..7e3d183b 100644 --- a/lib/simulator/Makefile +++ b/lib/simulator/Makefile @@ -25,24 +25,19 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -include $(SIM_ROOT)/makefiles/Makefile.inc +LIB_BASE := libsim +LIB_NAME := $(LIB_BASE).so -INCLUDES += -GCC_ARGS += -LIBS := -lgpi $(PYLIBS) -LD_PATH := -L$(LIB_DIR) +INCLUDES += +GCC_ARGS += +LIBS := -lgpi $(PYLIBS) +LD_PATH := -L$(LIB_DIR) -SRCS := simulatormodule.c -OBJS := $(SRCS:%.c=$(OBJ_DIR)/%.o) +SRCS := simulatormodule.c -LIB_NAME := libsim.so - -all: $(LIB_DIR)/simulator.so +CLIBS += $(LIB_DIR)/$(LIB_NAME) $(LIB_DIR)/simulator.so: $(LIB_DIR)/$(LIB_NAME) ln -sf $(LIB_NAME) $@ -clean : - @rm -rf $(OBJ_DIR) - include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/lib/vpi_shim/Makefile b/lib/vpi_shim/Makefile index b38e7f4c..d2c1b653 100644 --- a/lib/vpi_shim/Makefile +++ b/lib/vpi_shim/Makefile @@ -25,18 +25,17 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -include $(SIM_ROOT)/makefiles/Makefile.inc +LIB_BASE := libgpi +LIB_NAME := $(LIB_BASE).so -INCLUDES += -GCC_ARGS += -DVPI_CHECKING -LIBS := -lgpilog -lcocotb -LD_PATH := -L$(LIB_DIR) +INCLUDES += +GCC_ARGS += -DVPI_CHECKING +LIBS := -lgpilog -lcocotb +LD_PATH := -L$(LIB_DIR) -SRCS := gpi_vpi.c +SRCS := gpi_vpi.c -LIB_NAME := libgpi.so - -all: $(LIB_DIR)/gpivpi.vpl +CLIBS += $(LIB_DIR)/gpivpi.vpl # More rules such that this may be needed depending on the requirements of # different simulators, icarus for instance loads a .vpi libraray to use the vpi @@ -44,7 +43,4 @@ all: $(LIB_DIR)/gpivpi.vpl $(LIB_DIR)/gpivpi.vpl: $(LIB_DIR)/$(LIB_NAME) ln -sf $(LIB_NAME) $@ -clean: - rm -rf $(OBJ_DIR) - include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index be047994..adb3c5b4 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -48,11 +48,10 @@ include $(SIM_ROOT)/makefiles/Makefile.paths include $(SIM_ROOT)/makefiles/Makefile.pylib export LIB_DIR=$(BUILD_DIR)/libs/$(ARCH) -export LIB_OBJ_DIR:= $(BUILD_DIR)/libs/$(ARCH)/obj +export LIB_OBJ_DIR:= $(BUILD_DIR)/obj/$(ARCH) export INCLUDES := -I$(SIM_ROOT)/include $(PYTHON_INCLUDEDIR) - # Base GCC flags ifeq ($(OS),Darwin) GCC_ARGS := -g -DDEBUG -fpic diff --git a/makefiles/Makefile.rules b/makefiles/Makefile.rules index 24849b7f..1e983bbe 100644 --- a/makefiles/Makefile.rules +++ b/makefiles/Makefile.rules @@ -31,10 +31,10 @@ %: %.o # Define some of our own -OBJS:= $(SRCS:%.c=$(LIB_OBJ_DIR)/%.o) +$(LIB_NAME)_OBJS:= $(SRCS:%.c=$(LIB_OBJ_DIR)/%.o) $(LIB_OBJ_DIR)/%.o: %.c gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $@ $< -$(LIB_DIR)/$(LIB_NAME): $(LIB_OBJ_DIR) $(OBJS) - gcc $(GCC_ARGS) $(LINKER_ARGS) -o $@ $(OBJS) $(LIBS) $(LD_PATH) +$(LIB_DIR)/$(LIB_NAME): $($(LIB_NAME)_OBJS) + gcc $(GCC_ARGS) $(LINKER_ARGS) -o $@ $($(LIB_NAME)_OBJS) $(LIBS) $(LD_PATH) diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index 78d6c804..7a2b52f7 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -35,14 +35,14 @@ $(OBJ_DIR): mkdir -p $(OBJ_DIR) .PHONY: sim -sim: $(OBJ_DIR) $(VERILOG_SOURCES) libs_native +sim: $(OBJ_DIR) $(VERILOG_SOURCES) libs iverilog -o $(OBJ_DIR)/sim.vvp -D COCOTB_SIM=1 $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) .PHONY: gdb -gdb: $(OBJ_DIR) $(VERILOG_SOURCES) libs_native +gdb: $(OBJ_DIR) $(VERILOG_SOURCES) libs iverilog -o $(OBJ_DIR)/sim.vvp -D COCOTB_SIM=1 $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index f1a27996..d95e61f5 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -74,7 +74,7 @@ dve: $(SIM_BUILD)/simv_dve $(CUSTOM_COMPILE_DEPS) .PHONY: build_simv build_simv: $(SIM_BUILD)/simv -$(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab libs_native $(CUSTOM_COMPILE_DEPS) +$(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab libs $(CUSTOM_COMPILE_DEPS) cd $(SIM_BUILD) && \ LD_LIBRARY_PATH=$(LIB_DIR) TOPLEVEL=$(TOPLEVEL) \ vcs +acc+1 +vpi -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -debug -load libgpi.so $(COMPILE_ARGS) $(VERILOG_SOURCES) From 84bea1ab32f9e10d80174cabfd29e08731e1f3c1 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 24 Sep 2013 17:41:50 +0100 Subject: [PATCH 0397/2656] Issue #97: Add Dowser support --- cocotb/__init__.py | 7 +++++++ cocotb/memdebug.py | 10 ++++++++++ 2 files changed, 17 insertions(+) create mode 100644 cocotb/memdebug.py diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 6671809c..6682363d 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -71,6 +71,9 @@ class TestFailed(Exception): # FIXME is this really required? _rlock = threading.RLock() +def mem_debug(port): + import cocotb.memdebug + memdebug.start(port) def _initialise_testbench(root_handle): """ @@ -83,6 +86,10 @@ def _initialise_testbench(root_handle): """ _rlock.acquire() + memcheck_port = os.getenv('MEMCHECK') + if memcheck_port is not None: + mem_debug(int(memcheck_port)) + # Seed the Python random number generator to make this repeatable seed = os.getenv('RANDOM_SEED') if seed is None: diff --git a/cocotb/memdebug.py b/cocotb/memdebug.py new file mode 100644 index 00000000..41a06568 --- /dev/null +++ b/cocotb/memdebug.py @@ -0,0 +1,10 @@ +import cherrypy +import dowser + +def start(port): + cherrypy.tree.mount(dowser.Root()) + cherrypy.config.update({ + 'environment': 'embedded', + 'server.socket_port': port + }) + cherrypy.engine.start() From 3ce86a353d1321e1db8c9426e93838e645b2aff1 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 25 Sep 2013 23:35:20 +0100 Subject: [PATCH 0398/2656] Issue #87: Libs no longer always rebuilt --- lib/Makefile | 31 +++++++++++++--------------- lib/embed/Makefile | 19 ++++++++++------- lib/gpi/Makefile | 10 +++++---- lib/simulator/Makefile | 8 +++++-- lib/vpi_shim/Makefile | 8 +++++-- makefiles/simulators/Makefile.icarus | 2 +- 6 files changed, 44 insertions(+), 34 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index 3c1fefe1..f821f3d6 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -30,29 +30,26 @@ include $(SIM_ROOT)/makefiles/Makefile.inc $(LIB_OBJ_DIR): mkdir -p $@ -$(LIB_DIR): +$(LIB_DIR): $(LIB_OBJ_DIR) mkdir -p $@ -CLIBS := $(LIB_DIR) $(LIB_OBJ_DIR) +$(LIB_DIR)/libgpilog.so: + make -C $(SIM_ROOT)/lib/gpi -include $(SIM_ROOT)/lib/gpi/Makefile -#include $(SIM_ROOT)/lib/embed/Makefile -#include $(SIM_ROOT)/lib/vpi_shim/Makefile -#include $(SIM_ROOT)/lib/simulator/Makefile -#include $(SIM_ROOT)/makefiles/Makefile.rules +$(LIB_DIR)/libcocotb.so: + make -C $(SIM_ROOT)/lib/embed -libs: $(CLIBS) +$(LIB_DIR)/libgpi.so: + make -C $(SIM_ROOT)/lib/vpi_shim -#libs_native: $(LIB_DIR) all +$(LIB_DIR)/libsim.so: + make -C $(SIM_ROOT)/lib/simulator -force_32: - ARCH=i686 make - -#vpi_shim: gpi embed -#simulator: vpi_shim - -#$(CLIBS): -# $(MAKE) -C $(SIM_ROOT)/lib/$@ +libs: $(LIB_DIR) \ + $(LIB_DIR)/libgpilog.so \ + $(LIB_DIR)/libcocotb.so \ + $(LIB_DIR)/libgpi.so \ + $(LIB_DIR)/libsim.so clean:: -@rm -rf $(BUILD_DIR) diff --git a/lib/embed/Makefile b/lib/embed/Makefile index e9bfc7b6..d993757c 100644 --- a/lib/embed/Makefile +++ b/lib/embed/Makefile @@ -25,16 +25,19 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -LIB_BASE := libcocotb -LIB_NAME := $(LIB_BASE).so +include $(SIM_ROOT)/makefiles/Makefile.inc -INCLUDES += -GCC_ARGS += -LIBS := $(PYLIBS) -LD_PATH := -L$(LIB_DIR) +INCLUDES += +GCC_ARGS += +LIBS := $(PYLIBS) +LD_PATH := -L$(LIB_DIR) +SRCS := gpi_embed.c -SRCS := gpi_embed.c +LIB_NAME := libcocotb.so -CLIBS += $(LIB_DIR)/$(LIB_NAME) +all: $(LIB_DIR)/$(LIB_NAME) + +clean: + -@rm -rf $(OBJ_DIR) include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index cd055845..6bfa7f59 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -25,9 +25,6 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -LIB_BASE := libgpilog -LIB_NAME := $(LIB_BASE).so - include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += @@ -35,6 +32,11 @@ GCC_ARGS += -DFILTER LIBS := $(PYLIBS) SRCS := gpi_logging.c -CLIBS += $(LIB_DIR)/$(LIB_NAME) +LIB_NAME := libgpilog.so + +all: $(LIB_DIR)/$(LIB_NAME) + +clean: + -@rm -rf $(LIB_DIR)/$(LIB_NAME) include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/lib/simulator/Makefile b/lib/simulator/Makefile index 7e3d183b..e84aaa84 100644 --- a/lib/simulator/Makefile +++ b/lib/simulator/Makefile @@ -25,13 +25,13 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -LIB_BASE := libsim -LIB_NAME := $(LIB_BASE).so +include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += GCC_ARGS += LIBS := -lgpi $(PYLIBS) LD_PATH := -L$(LIB_DIR) +LIB_NAME := libsim.so SRCS := simulatormodule.c @@ -40,4 +40,8 @@ CLIBS += $(LIB_DIR)/$(LIB_NAME) $(LIB_DIR)/simulator.so: $(LIB_DIR)/$(LIB_NAME) ln -sf $(LIB_NAME) $@ +clean: + -@rm -rf $(LIB_DIR)/simulator.so + -@rm -rf $(LIB_DIR)/$(LIB_NAME) + include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/lib/vpi_shim/Makefile b/lib/vpi_shim/Makefile index d2c1b653..353eaebb 100644 --- a/lib/vpi_shim/Makefile +++ b/lib/vpi_shim/Makefile @@ -25,13 +25,13 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -LIB_BASE := libgpi -LIB_NAME := $(LIB_BASE).so +include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += GCC_ARGS += -DVPI_CHECKING LIBS := -lgpilog -lcocotb LD_PATH := -L$(LIB_DIR) +LIB_NAME := libgpi.so SRCS := gpi_vpi.c @@ -43,4 +43,8 @@ CLIBS += $(LIB_DIR)/gpivpi.vpl $(LIB_DIR)/gpivpi.vpl: $(LIB_DIR)/$(LIB_NAME) ln -sf $(LIB_NAME) $@ +clean: + -@rm -rf $(LIB_DIR)/gpivpi.vpl + -@rm -rf $(LIB_DIR)/$(LIB_NAME) + include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index 7a2b52f7..a1b2db85 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -49,4 +49,4 @@ gdb: $(OBJ_DIR) $(VERILOG_SOURCES) libs gdb --args vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) clean:: - rm -rf $(OBJ_DIR) + -@rm -rf $(OBJ_DIR) From eb37c7a14b4e7947e6363e75281343fb216fff61 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 26 Sep 2013 00:03:34 +0100 Subject: [PATCH 0399/2656] Cleanup: Delete core file --- examples/sim_exit/tests/core.19928 | Bin 2101248 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 examples/sim_exit/tests/core.19928 diff --git a/examples/sim_exit/tests/core.19928 b/examples/sim_exit/tests/core.19928 deleted file mode 100644 index 7f5255ebd2b73a31396e377b0c0e1dc6e688e960..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2101248 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
      eojFPV_4gmAzx*ZG1W}j=P1`!lId16Auz@Q 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 QYEsUfXbwelzD@K01H7eW8vp Date: Thu, 26 Sep 2013 16:13:21 +0100 Subject: [PATCH 0400/2656] Use setimmediatevalue for start-of-day defaults on Avalon bus --- cocotb/drivers/avalon.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index ab58b169..da364a46 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -51,10 +51,10 @@ class AvalonMM(BusDriver): def __init__(self, entity, name, clock): BusDriver.__init__(self, entity, name, clock) - # Drive some sensible defaults - self.bus.read <= 0 - self.bus.write <= 0 - self.bus.address <= 0 + # Drive some sensible defaults (setimmediatevalue to avoid x asserts) + self.bus.read.setimmediatevalue(0) + self.bus.write.setimmediatevalue(0) + self.bus.address.setimmediatevalue(0) def read(self, address): pass From 92b75fad83c895a58fee72e657711e9508112d4d Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 27 Sep 2013 00:23:01 +0100 Subject: [PATCH 0401/2656] Fixes #98: Update license --- LICENSE | 4 ++- Makefile | 4 ++- bin/cocotbenv.py | 4 ++- bin/create_files.py | 4 ++- bin/create_project.py | 4 ++- cocotb/ANSI.py | 4 ++- cocotb/__init__.py | 4 ++- cocotb/binary.py | 4 ++- cocotb/bus.py | 4 ++- cocotb/clock.py | 4 ++- cocotb/decorators.py | 4 ++- cocotb/drivers/__init__.py | 4 ++- cocotb/drivers/avalon.py | 4 ++- cocotb/generators/__init__.py | 4 ++- cocotb/generators/bit.py | 4 ++- cocotb/generators/byte.py | 4 ++- cocotb/generators/feeds/__init__.py | 4 ++- cocotb/generators/feeds/itch_40.py | 4 ++- cocotb/generators/feeds/itch_feed.py | 4 ++- cocotb/generators/feeds/moldudp64.py | 4 ++- cocotb/generators/feeds/opra_binary.py | 4 ++- cocotb/generators/feeds/packet_util.py | 4 ++- cocotb/generators/packet.py | 4 ++- cocotb/handle.py | 4 ++- cocotb/log.py | 4 ++- cocotb/memdebug.py | 27 +++++++++++++++++ cocotb/monitors/__init__.py | 4 ++- cocotb/monitors/avalon.py | 4 ++- cocotb/regression.py | 4 ++- cocotb/result.py | 4 ++- cocotb/scheduler.py | 4 ++- cocotb/scoreboard.py | 4 ++- cocotb/triggers.py | 4 ++- cocotb/utils.py | 4 ++- cocotb/xunit_reporter.py | 27 +++++++++++++++++ examples/Makefile | 4 ++- examples/endian_swapper/hdl/endian_swapper.sv | 29 +++++++++++++++++++ examples/endian_swapper/tests/Makefile | 4 ++- .../tests/test_endian_swapper.py | 27 +++++++++++++++++ examples/functionality/hdl/sample_module.v | 5 ++-- examples/functionality/tests/Makefile | 4 ++- examples/functionality/tests/test_cocotb.py | 4 ++- .../functionality/tests/test_discovery.py | 26 +++++++++++++++++ examples/functionality/tests/test_external.py | 4 ++- examples/plusargs/Makefile | 4 ++- examples/plusargs/plusargs.py | 27 ++++++++++++++++- examples/plusargs/tb_top.v | 29 +++++++++++++++++++ examples/sim_exit/hdl/close_module.v | 4 ++- examples/sim_exit/tests/Makefile | 4 ++- examples/sim_exit/tests/test_closedown.py | 4 ++- include/embed.h | 4 ++- include/gpi.h | 4 ++- include/gpi_logging.h | 4 ++- include/vpi_user.h | 4 ++- lib/Makefile | 4 ++- lib/embed/Makefile | 4 ++- lib/embed/gpi_embed.c | 4 ++- lib/gpi/Makefile | 4 ++- lib/gpi/gpi_logging.c | 4 ++- lib/simulator/Makefile | 4 ++- lib/simulator/simulatormodule.c | 4 ++- lib/simulator/simulatormodule.h | 4 ++- lib/vpi_shim/Makefile | 4 ++- lib/vpi_shim/gpi_vpi.c | 4 ++- makefiles/Makefile.doc | 4 ++- makefiles/Makefile.inc | 4 ++- makefiles/Makefile.paths | 4 ++- makefiles/Makefile.pylib | 4 ++- makefiles/Makefile.rules | 4 ++- makefiles/Makefile.sim | 4 ++- makefiles/simulators/Makefile.aldec | 4 ++- makefiles/simulators/Makefile.icarus | 4 ++- makefiles/simulators/Makefile.ius | 4 ++- makefiles/simulators/Makefile.questa | 28 ++++++++++++++++++ makefiles/simulators/Makefile.vcs | 4 ++- 75 files changed, 420 insertions(+), 69 deletions(-) diff --git a/LICENSE b/LICENSE index cd61e247..3d7655a9 100644 --- a/LICENSE +++ b/LICENSE @@ -2,6 +2,7 @@ Cocotb is licensed under the Revised BSD License. Full license text below. ############################################################################### # Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -11,7 +12,8 @@ Cocotb is licensed under the Revised BSD License. Full license text below. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # diff --git a/Makefile b/Makefile index 6a95dee0..abf716ea 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ ############################################################################### # Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # diff --git a/bin/cocotbenv.py b/bin/cocotbenv.py index 697ad32e..2a9925de 100755 --- a/bin/cocotbenv.py +++ b/bin/cocotbenv.py @@ -1,6 +1,7 @@ #!/usr/bin/env python ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -10,7 +11,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/bin/create_files.py b/bin/create_files.py index 0544422e..49ff7b2a 100755 --- a/bin/create_files.py +++ b/bin/create_files.py @@ -1,6 +1,7 @@ #!/usr/bin/env python ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -10,7 +11,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/bin/create_project.py b/bin/create_project.py index b3319884..1d0982f4 100755 --- a/bin/create_project.py +++ b/bin/create_project.py @@ -1,6 +1,7 @@ #!/usr/bin/env python ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -10,7 +11,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/ANSI.py b/cocotb/ANSI.py index 3c70f1d4..17b6e2c3 100644 --- a/cocotb/ANSI.py +++ b/cocotb/ANSI.py @@ -1,4 +1,5 @@ ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -8,7 +9,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 6682363d..aacdd538 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -1,4 +1,5 @@ ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -8,7 +9,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/binary.py b/cocotb/binary.py index 866ca78e..0ef404ee 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -1,6 +1,7 @@ #!/usr/bin/env python ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -10,7 +11,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/bus.py b/cocotb/bus.py index 11f6ab5c..70e1b2f2 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -1,6 +1,7 @@ #!/usr/bin/env python ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -10,7 +11,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/clock.py b/cocotb/clock.py index 11519fb6..1fd9bf35 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -1,4 +1,5 @@ ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -8,7 +9,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/decorators.py b/cocotb/decorators.py index ef1b9e1c..3fbda849 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -1,4 +1,5 @@ ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -8,7 +9,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index 4cea981c..00ba78a2 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -1,6 +1,7 @@ #!/bin/env python ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -10,7 +11,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index ab58b169..0c564714 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -1,4 +1,5 @@ ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -8,7 +9,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/generators/__init__.py b/cocotb/generators/__init__.py index 238e7c5f..c5570c1f 100644 --- a/cocotb/generators/__init__.py +++ b/cocotb/generators/__init__.py @@ -1,4 +1,5 @@ ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -8,7 +9,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/generators/bit.py b/cocotb/generators/bit.py index f84dc958..671e04a1 100644 --- a/cocotb/generators/bit.py +++ b/cocotb/generators/bit.py @@ -1,4 +1,5 @@ ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -8,7 +9,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/generators/byte.py b/cocotb/generators/byte.py index c197514c..00c1ee5e 100644 --- a/cocotb/generators/byte.py +++ b/cocotb/generators/byte.py @@ -1,6 +1,7 @@ #!/usr/bin/env python ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -10,7 +11,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/generators/feeds/__init__.py b/cocotb/generators/feeds/__init__.py index 58cf8850..f132b5c6 100644 --- a/cocotb/generators/feeds/__init__.py +++ b/cocotb/generators/feeds/__init__.py @@ -1,5 +1,6 @@ #!/usr/bin/env python ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/generators/feeds/itch_40.py b/cocotb/generators/feeds/itch_40.py index 90ec00ad..890eea42 100644 --- a/cocotb/generators/feeds/itch_40.py +++ b/cocotb/generators/feeds/itch_40.py @@ -1,5 +1,6 @@ #! /usr/bin/env python ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/generators/feeds/itch_feed.py b/cocotb/generators/feeds/itch_feed.py index 7c1969cb..968b99fe 100644 --- a/cocotb/generators/feeds/itch_feed.py +++ b/cocotb/generators/feeds/itch_feed.py @@ -1,5 +1,6 @@ #! /usr/bin/env python ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/generators/feeds/moldudp64.py b/cocotb/generators/feeds/moldudp64.py index 7ee39ed6..9aa051a9 100644 --- a/cocotb/generators/feeds/moldudp64.py +++ b/cocotb/generators/feeds/moldudp64.py @@ -1,5 +1,6 @@ #! /usr/bin/env python ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/generators/feeds/opra_binary.py b/cocotb/generators/feeds/opra_binary.py index c59c3ac5..c21d981a 100644 --- a/cocotb/generators/feeds/opra_binary.py +++ b/cocotb/generators/feeds/opra_binary.py @@ -1,5 +1,6 @@ #! /usr/bin/env python ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/generators/feeds/packet_util.py b/cocotb/generators/feeds/packet_util.py index b32a526d..9852bc6d 100644 --- a/cocotb/generators/feeds/packet_util.py +++ b/cocotb/generators/feeds/packet_util.py @@ -1,5 +1,6 @@ #! /usr/bin/env python ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/generators/packet.py b/cocotb/generators/packet.py index 7689d7e7..7845170e 100644 --- a/cocotb/generators/packet.py +++ b/cocotb/generators/packet.py @@ -1,6 +1,7 @@ #!/usr/bin/env python ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -10,7 +11,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/handle.py b/cocotb/handle.py index 392e8232..39c7dffc 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -1,6 +1,7 @@ #!/usr/bin/env python ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -10,7 +11,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/log.py b/cocotb/log.py index c97b4d62..567d05de 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -1,4 +1,5 @@ ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -8,7 +9,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/memdebug.py b/cocotb/memdebug.py index 41a06568..35b0ab11 100644 --- a/cocotb/memdebug.py +++ b/cocotb/memdebug.py @@ -1,3 +1,30 @@ +''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + import cherrypy import dowser diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index 80008587..c3c0025c 100644 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -1,6 +1,7 @@ #!/bin/env python ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -10,7 +11,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index bfe9db46..ff60a000 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -1,4 +1,5 @@ ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -8,7 +9,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/regression.py b/cocotb/regression.py index 321fe47b..1419171a 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -1,4 +1,5 @@ ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -8,7 +9,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/result.py b/cocotb/result.py index 17eb7b67..3fb96d84 100644 --- a/cocotb/result.py +++ b/cocotb/result.py @@ -1,4 +1,5 @@ ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -8,7 +9,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index ca60459d..5ef104d4 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -1,6 +1,7 @@ #!/usr/bin/env python ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -10,7 +11,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index 6c29d201..5e21bd5f 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -1,6 +1,7 @@ #!/usr/bin/env python ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -10,7 +11,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 7124bb32..f01bba21 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -1,4 +1,5 @@ ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -8,7 +9,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/utils.py b/cocotb/utils.py index f124e141..ce9cb9bd 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -1,4 +1,5 @@ ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -8,7 +9,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/cocotb/xunit_reporter.py b/cocotb/xunit_reporter.py index e1db2dc5..dc2275c2 100644 --- a/cocotb/xunit_reporter.py +++ b/cocotb/xunit_reporter.py @@ -1,3 +1,30 @@ +''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + from xml.etree.ElementTree import Element, SubElement import xml.etree.ElementTree as ET diff --git a/examples/Makefile b/examples/Makefile index ff04a555..16134c4d 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,5 +1,6 @@ ############################################################################### # Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # diff --git a/examples/endian_swapper/hdl/endian_swapper.sv b/examples/endian_swapper/hdl/endian_swapper.sv index ecf40d1e..faa2bf35 100644 --- a/examples/endian_swapper/hdl/endian_swapper.sv +++ b/examples/endian_swapper/hdl/endian_swapper.sv @@ -1,3 +1,32 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2013 Potential Ventures Ltd +// Copyright (c) 2013 SolarFlare Communications Inc +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Potential Ventures Ltd, +// Copyright (c) 2013 SolarFlare Communications Inc nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + /* Endian swapping module. diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index 23a9c2dd..78a5a2d9 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -1,5 +1,6 @@ ############################################################################### # Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # diff --git a/examples/endian_swapper/tests/test_endian_swapper.py b/examples/endian_swapper/tests/test_endian_swapper.py index a79c38e6..69d459fe 100644 --- a/examples/endian_swapper/tests/test_endian_swapper.py +++ b/examples/endian_swapper/tests/test_endian_swapper.py @@ -1,3 +1,30 @@ +''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + import random import logging diff --git a/examples/functionality/hdl/sample_module.v b/examples/functionality/hdl/sample_module.v index 6a98eb41..3919714c 100644 --- a/examples/functionality/hdl/sample_module.v +++ b/examples/functionality/hdl/sample_module.v @@ -1,5 +1,6 @@ //----------------------------------------------------------------------------- // Copyright (c) 2013 Potential Ventures Ltd +// Copyright (c) 2013 SolarFlare Communications Inc // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. -// * Neither the name of Potential Ventures Ltd nor the +// * Neither the name of Potential Ventures Ltd, +// Copyright (c) 2013 SolarFlare Communications Inc nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // @@ -25,7 +27,6 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //----------------------------------------------------------------------------- - `timescale 1 ps / 1 ps module sample_module ( diff --git a/examples/functionality/tests/Makefile b/examples/functionality/tests/Makefile index e91c13f6..200f76e3 100644 --- a/examples/functionality/tests/Makefile +++ b/examples/functionality/tests/Makefile @@ -1,5 +1,6 @@ ############################################################################### # Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index ce6ff3c2..36315f6d 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -1,6 +1,7 @@ #!/usr/bin/env python ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -10,7 +11,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/examples/functionality/tests/test_discovery.py b/examples/functionality/tests/test_discovery.py index 370ffb8c..2addb9f1 100644 --- a/examples/functionality/tests/test_discovery.py +++ b/examples/functionality/tests/test_discovery.py @@ -1,3 +1,29 @@ +''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' import cocotb from cocotb.triggers import Timer diff --git a/examples/functionality/tests/test_external.py b/examples/functionality/tests/test_external.py index 1fc806b0..1d94dcc0 100644 --- a/examples/functionality/tests/test_external.py +++ b/examples/functionality/tests/test_external.py @@ -1,6 +1,7 @@ #!/usr/bin/env python ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -10,7 +11,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/examples/plusargs/Makefile b/examples/plusargs/Makefile index 1ae382b8..1cf7cbfe 100644 --- a/examples/plusargs/Makefile +++ b/examples/plusargs/Makefile @@ -1,5 +1,6 @@ ############################################################################### # Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # diff --git a/examples/plusargs/plusargs.py b/examples/plusargs/plusargs.py index 0076c820..63a6212f 100644 --- a/examples/plusargs/plusargs.py +++ b/examples/plusargs/plusargs.py @@ -1,4 +1,29 @@ -#!/bin/python +''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' """ plusarg testing diff --git a/examples/plusargs/tb_top.v b/examples/plusargs/tb_top.v index f1c40cc9..4b9e4cae 100644 --- a/examples/plusargs/tb_top.v +++ b/examples/plusargs/tb_top.v @@ -1,3 +1,32 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2013 Potential Ventures Ltd +// Copyright (c) 2013 SolarFlare Communications Inc +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Potential Ventures Ltd, +// Copyright (c) 2013 SolarFlare Communications Inc nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + module tb_top ; reg [1000:0] foo_string; diff --git a/examples/sim_exit/hdl/close_module.v b/examples/sim_exit/hdl/close_module.v index 104b88b6..481501f8 100644 --- a/examples/sim_exit/hdl/close_module.v +++ b/examples/sim_exit/hdl/close_module.v @@ -1,5 +1,6 @@ //----------------------------------------------------------------------------- // Copyright (c) 2013 Potential Ventures Ltd +// Copyright (c) 2013 SolarFlare Communications Inc // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. -// * Neither the name of Potential Ventures Ltd nor the +// * Neither the name of Potential Ventures Ltd, +// Copyright (c) 2013 SolarFlare Communications Inc nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // diff --git a/examples/sim_exit/tests/Makefile b/examples/sim_exit/tests/Makefile index 5018ffb3..3c00d50b 100644 --- a/examples/sim_exit/tests/Makefile +++ b/examples/sim_exit/tests/Makefile @@ -1,5 +1,6 @@ ############################################################################### # Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # diff --git a/examples/sim_exit/tests/test_closedown.py b/examples/sim_exit/tests/test_closedown.py index 0700c5dd..d74b0120 100644 --- a/examples/sim_exit/tests/test_closedown.py +++ b/examples/sim_exit/tests/test_closedown.py @@ -1,6 +1,7 @@ #!/usr/bin/env python ''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. Redistribution and use in source and binary forms, with or without @@ -10,7 +11,8 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/include/embed.h b/include/embed.h index c43acd46..655be032 100644 --- a/include/embed.h +++ b/include/embed.h @@ -1,5 +1,6 @@ /****************************************************************************** * Copyright (c) 2013 Potential Ventures Ltd +* Copyright (c) 2013 SolarFlare Communications Inc * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd nor the +* * Neither the name of Potential Ventures Ltd, +* SolarFlare Communications Inc nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * diff --git a/include/gpi.h b/include/gpi.h index ec873961..15735fa9 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -1,5 +1,6 @@ /****************************************************************************** * Copyright (c) 2013 Potential Ventures Ltd +* Copyright (c) 2013 SolarFlare Communications Inc * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd nor the +* * Neither the name of Potential Ventures Ltd, +* SolarFlare Communications Inc nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * diff --git a/include/gpi_logging.h b/include/gpi_logging.h index 44ac3521..36dd13ba 100644 --- a/include/gpi_logging.h +++ b/include/gpi_logging.h @@ -1,5 +1,6 @@ /****************************************************************************** * Copyright (c) 2013 Potential Ventures Ltd +* Copyright (c) 2013 SolarFlare Communications Inc * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd nor the +* * Neither the name of Potential Ventures Ltd, +* SolarFlare Communications Inc nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * diff --git a/include/vpi_user.h b/include/vpi_user.h index 379f21e2..9212da00 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -1,5 +1,6 @@ /****************************************************************************** * Copyright (c) 2013 Potential Ventures Ltd +* Copyright (c) 2013 SolarFlare Communications Inc * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd nor the +* * Neither the name of Potential Ventures Ltd, +* SolarFlare Communications Inc nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * diff --git a/lib/Makefile b/lib/Makefile index a80f86e6..4648825d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,5 +1,6 @@ ############################################################################### # Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # diff --git a/lib/embed/Makefile b/lib/embed/Makefile index 5752dc8c..44209187 100644 --- a/lib/embed/Makefile +++ b/lib/embed/Makefile @@ -1,5 +1,6 @@ ############################################################################### # Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 2c8f5a75..021bce98 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -1,5 +1,6 @@ /****************************************************************************** * Copyright (c) 2013 Potential Ventures Ltd +* Copyright (c) 2013 SolarFlare Communications Inc * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd nor the +* * Neither the name of Potential Ventures Ltd, +* SolarFlare Communications Inc nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index 5565bb12..9c8c0088 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -1,5 +1,6 @@ ############################################################################### # Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # diff --git a/lib/gpi/gpi_logging.c b/lib/gpi/gpi_logging.c index 5a177286..24bdfc04 100644 --- a/lib/gpi/gpi_logging.c +++ b/lib/gpi/gpi_logging.c @@ -1,5 +1,6 @@ /****************************************************************************** * Copyright (c) 2013 Potential Ventures Ltd +* Copyright (c) 2013 SolarFlare Communications Inc * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd nor the +* * Neither the name of Potential Ventures Ltd, +* SolarFlare Communications Inc nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * diff --git a/lib/simulator/Makefile b/lib/simulator/Makefile index 80b37ef3..fdee8295 100644 --- a/lib/simulator/Makefile +++ b/lib/simulator/Makefile @@ -1,5 +1,6 @@ ############################################################################### # Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 08183308..54a3f9e1 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -1,5 +1,6 @@ /****************************************************************************** * Copyright (c) 2013 Potential Ventures Ltd +* Copyright (c) 2013 SolarFlare Communications Inc * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd nor the +* * Neither the name of Potential Ventures Ltd, +* SolarFlare Communications Inc nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 67e1f2c8..db1a7a43 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -1,5 +1,6 @@ /****************************************************************************** * Copyright (c) 2013 Potential Ventures Ltd +* Copyright (c) 2013 SolarFlare Communications Inc * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd nor the +* * Neither the name of Potential Ventures Ltd, +* SolarFlare Communications Inc nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * diff --git a/lib/vpi_shim/Makefile b/lib/vpi_shim/Makefile index b38e7f4c..90d627d1 100644 --- a/lib/vpi_shim/Makefile +++ b/lib/vpi_shim/Makefile @@ -1,5 +1,6 @@ ############################################################################### # Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index e262c5d5..25421a37 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -1,5 +1,6 @@ /****************************************************************************** * Copyright (c) 2013 Potential Ventures Ltd +* Copyright (c) 2013 SolarFlare Communications Inc * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd nor the +* * Neither the name of Potential Ventures Ltd, +* SolarFlare Communications Inc nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * diff --git a/makefiles/Makefile.doc b/makefiles/Makefile.doc index 3b6ef03b..49d8f9e2 100644 --- a/makefiles/Makefile.doc +++ b/makefiles/Makefile.doc @@ -1,5 +1,6 @@ ############################################################################### # Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index be047994..0ca4b618 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -1,5 +1,6 @@ ############################################################################### # Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # diff --git a/makefiles/Makefile.paths b/makefiles/Makefile.paths index 8b52a6fd..75975a37 100644 --- a/makefiles/Makefile.paths +++ b/makefiles/Makefile.paths @@ -1,5 +1,6 @@ ############################################################################### # Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib index 8bbb7a24..23876252 100644 --- a/makefiles/Makefile.pylib +++ b/makefiles/Makefile.pylib @@ -1,5 +1,6 @@ ############################################################################### # Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # diff --git a/makefiles/Makefile.rules b/makefiles/Makefile.rules index 24849b7f..a7b74d1b 100644 --- a/makefiles/Makefile.rules +++ b/makefiles/Makefile.rules @@ -1,5 +1,6 @@ ############################################################################### # Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # diff --git a/makefiles/Makefile.sim b/makefiles/Makefile.sim index f97c3b2e..2f06c334 100644 --- a/makefiles/Makefile.sim +++ b/makefiles/Makefile.sim @@ -1,5 +1,6 @@ ############################################################################### # Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 1cc8cecb..fab3dac6 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -1,5 +1,6 @@ ############################################################################### # Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index 78d6c804..efb6ecba 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -1,5 +1,6 @@ ############################################################################### # Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index bc204e9a..fadd5e98 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -1,5 +1,6 @@ ############################################################################### # Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index ad5bf24e..751a1ec6 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -1,3 +1,31 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### OBJ_DIR := obj VPI_LIB := vpi diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index f1a27996..af44380e 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -1,5 +1,6 @@ ############################################################################### # Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -9,7 +10,8 @@ # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # From df34f0dce65061e75a39a828c0c76410c8255166 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 7 Oct 2013 11:51:05 +0100 Subject: [PATCH 0402/2656] Fixes #99: Report version number from installed version file and path cocotb executed from at start of a test --- cocotb/__init__.py | 10 ++++++++++ makefiles/Makefile.sim | 2 ++ 2 files changed, 12 insertions(+) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index aacdd538..e285762d 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -102,6 +102,16 @@ def _initialise_testbench(root_handle): log.info("Seeding Python random module with supplied seed %d" % (seed)) random.seed(seed) + exec_path = os.getenv('SIM_ROOT') + if exec_path is None: + exec_path = 'Unknown' + + version = os.getenv('VERSION') + if version is None: + log.info("Unable to determine version") + else: + log.info("Running tests with CoCoTB-%s from %s" % (version, exec_path)) + # Create the base handle type dut = cocotb.handle.SimHandle(root_handle) diff --git a/makefiles/Makefile.sim b/makefiles/Makefile.sim index 2f06c334..310906f5 100644 --- a/makefiles/Makefile.sim +++ b/makefiles/Makefile.sim @@ -46,3 +46,5 @@ endif include $(SIM_ROOT)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) include $(SIM_ROOT)/lib/Makefile +include $(SIM_ROOT)/version +export VERSION From 24267bc61d406b584d09ade515ceaf4018606263 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 8 Oct 2013 21:39:01 +0100 Subject: [PATCH 0403/2656] Issue #92: Allow examples to run against an installed location --- Makefile | 1 + examples/Makefile | 3 ++- examples/endian_swapper/tests/Makefile | 4 ++-- examples/functionality/tests/Makefile | 4 ++-- examples/plusargs/Makefile | 4 ++-- examples/sim_exit/tests/Makefile | 4 ++-- 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index abf716ea..40662ff6 100644 --- a/Makefile +++ b/Makefile @@ -59,6 +59,7 @@ common_install: @cp -R bin/cocotbenv.py $(FULL_INSTALL_DIR)/bin/ @cp -R bin/create_project.py $(FULL_INSTALL_DIR)/bin/ @cp -R makefiles $(FULL_INSTALL_DIR)/ + @cp version $(FULL_INSTALL_DIR)/ create_files: bin/create_files.py $(FULL_INSTALL_DIR) diff --git a/examples/Makefile b/examples/Makefile index 16134c4d..92f9062d 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -27,7 +27,8 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### - +COCOTB?=$(shell pwd)/../ +export COCOTB SMOKE_TESTS := plusargs \ sim_exit/tests \ diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index 78a5a2d9..28a2c338 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -35,5 +35,5 @@ PWD=$(shell pwd) VERILOG_SOURCES = $(PWD)/../hdl/endian_swapper.sv MODULE=test_endian_swapper -include ../../../makefiles/Makefile.inc -include ../../../makefiles/Makefile.sim +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim diff --git a/examples/functionality/tests/Makefile b/examples/functionality/tests/Makefile index 200f76e3..b9686289 100644 --- a/examples/functionality/tests/Makefile +++ b/examples/functionality/tests/Makefile @@ -35,5 +35,5 @@ PWD=$(shell pwd) VERILOG_SOURCES = $(PWD)/../hdl/sample_module.v MODULE=test_cocotb,test_discovery,test_external -include ../../../makefiles/Makefile.inc -include ../../../makefiles/Makefile.sim +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim diff --git a/examples/plusargs/Makefile b/examples/plusargs/Makefile index 1cf7cbfe..7f0bddd8 100644 --- a/examples/plusargs/Makefile +++ b/examples/plusargs/Makefile @@ -37,6 +37,6 @@ MODULE=plusargs PLUSARGS=+foo=bar +test1 +test2 +options=fubar -include ../../makefiles/Makefile.inc -include ../../makefiles/Makefile.sim +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim diff --git a/examples/sim_exit/tests/Makefile b/examples/sim_exit/tests/Makefile index 3c00d50b..21de9877 100644 --- a/examples/sim_exit/tests/Makefile +++ b/examples/sim_exit/tests/Makefile @@ -35,5 +35,5 @@ PWD=$(shell pwd) VERILOG_SOURCES = $(PWD)/../hdl/close_module.v MODULE=test_closedown -include ../../../makefiles/Makefile.inc -include ../../../makefiles/Makefile.sim +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim From 4b8abe2338a7fc5697e765f0c135e7d93e03f8d0 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 8 Oct 2013 22:08:51 +0100 Subject: [PATCH 0404/2656] Issue #92: Harmonise to SIM_BUILD to specify build products area for simulators --- lib/vpi_shim/Makefile | 6 ++++++ makefiles/Makefile.sim | 9 +++++++- makefiles/simulators/Makefile.aldec | 31 +++++++--------------------- makefiles/simulators/Makefile.icarus | 21 +++++++------------ makefiles/simulators/Makefile.ius | 11 +++------- makefiles/simulators/Makefile.questa | 14 ++++--------- makefiles/simulators/Makefile.vcs | 9 -------- 7 files changed, 36 insertions(+), 65 deletions(-) diff --git a/lib/vpi_shim/Makefile b/lib/vpi_shim/Makefile index 3ec3555e..3395e9b9 100644 --- a/lib/vpi_shim/Makefile +++ b/lib/vpi_shim/Makefile @@ -42,9 +42,15 @@ CLIBS += $(LIB_DIR)/gpivpi.vpl # More rules such that this may be needed depending on the requirements of # different simulators, icarus for instance loads a .vpi libraray to use the vpi # inerface at the start of the simulation + +all: $(LIB_DIR)/gpivpi.vpl $(LIB_DIR)/cocotb.vpi + $(LIB_DIR)/gpivpi.vpl: $(LIB_DIR)/$(LIB_NAME) ln -sf $(LIB_NAME) $@ +$(LIB_DIR)/cocotb.vpi: $(LIB_DIR)/$(LIB_NAME) + ln -sf $(LIB_NAME) $@ + clean: -@rm -rf $(LIB_DIR)/gpivpi.vpl -@rm -rf $(LIB_DIR)/$(LIB_NAME) diff --git a/makefiles/Makefile.sim b/makefiles/Makefile.sim index 310906f5..6cb801af 100644 --- a/makefiles/Makefile.sim +++ b/makefiles/Makefile.sim @@ -28,7 +28,11 @@ ############################################################################### # This is a simple wrapper Makefile to pull in the appropriate Makefile for -# the desired simulator +# the desired simulator and set up common paths for the sims to use to build in + + +SIM_BUILD ?= sim_build +export SIM_BUILD # Default to Icarus if no simulator is defined SIM ?= icarus @@ -48,3 +52,6 @@ include $(SIM_ROOT)/lib/Makefile include $(SIM_ROOT)/version export VERSION + +$(SIM_BUILD): + mkdir -p $@ diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index fab3dac6..4e4e2c56 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -28,23 +28,12 @@ ############################################################################### -COCOTB = $(SIM_ROOT)/sim_core/cocotb -VPI_FILE = $(SIM_ROOT)/build/cocotb.vpi -OBJ_DIR := obj +VPI_FILE = $(LIB_DIR)/cocotb.vpi VPI_LIB := aldecpli VPI_LD_PATH := $(RIVERA_BASE)/lib all : sim -$(OBJ_DIR) : - mkdir -p $(OBJ_DIR) - -core: - make -C $(SIM_ROOT)/sim_core - -# By default create output goes in a build directory -SIM_DIR ?= sim_output - # Common Makefile for Aldec Riviera-PRO simulator ifeq ($(GUI),1) @@ -53,16 +42,11 @@ else CMD := vsimsa endif -$(SIM_DIR): - mkdir -p $(SIM_DIR) - - - # Aldec requires a process in the verilog - if the only # timesteps are PLI initiated then the sim finishes immediately # # FIXME this doesn't work, still need this code in the toplevel GRRRR - $(SIM_DIR)/initial.v : $(SIM_DIR) +$(SIM_BUILD)/initial.v: echo -e "module fake_initial;\n\ initial begin\n\ $display (\"Initial statement began\");\n\ @@ -71,10 +55,10 @@ end\n\ endmodule\n" > $@ # Create a TCL script based on the list of $(VERILOG_SOURCES) -$(SIM_DIR)/runsim.tcl : $(VERILOG_SOURCES) $(SIM_DIR) libs_native +$(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(SIM_BUILD) libs_native echo "alib work" > $@ echo "set worklib work" >> $@ - cd $(SIM_DIR) && create_project.py $(VERILOG_SOURCES) >> runsim.tcl + cd $(SIM_BUILD) && create_project.py $(VERILOG_SOURCES) >> runsim.tcl echo "asim +access +w -O2 -dbg -pli libcocotb $(TOPLEVEL)" >> $@ ifeq ($(GUI),1) echo "wave -rec *" >> $@ @@ -87,11 +71,10 @@ endif # that turns on batch mode (i.e. exit on completion/error) .PHONY: sim -sim: $(SIM_DIR)/runsim.tcl core - -cd $(SIM_DIR) && ln -sf $(VPI_FILE) libcocotb.so - cd $(SIM_DIR) && PYTHONPATH=$(LIB_DIR):$(PWD)/testbench:$(SIM_ROOT)/sim_core:$(PWD):$(PYTHONPATH) \ +sim: $(SIM_BUILD)/runsim.tcl core + cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(PWD)/testbench:$(SIM_ROOT)/sim_core:$(PWD):$(PYTHONPATH) \ LD_LIBRARY_PATH=$(LD_LIBRARY_PATH):$(VSIMSABIN)/Linux64 MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do runsim.tcl | tee sim.log clean:: - rm -rf $(SIM_DIR) + rm -rf $(SIM_BUILD) diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index 25d01215..ceb5e304 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -27,28 +27,23 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -OBJ_DIR := obj VPI_LIB := vpi -VPI_LD_PATH := $(ICARUS_BASE)/lib -all: $(OBJ_DIR) sim - -$(OBJ_DIR): - mkdir -p $(OBJ_DIR) +all: $(SIM_BUILD) sim .PHONY: sim -sim: $(OBJ_DIR) $(VERILOG_SOURCES) libs - iverilog -o $(OBJ_DIR)/sim.vvp -D COCOTB_SIM=1 $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) +sim: $(SIM_BUILD) $(VERILOG_SOURCES) libs + iverilog -o $(SIM_BUILD)/sim.vvp -D COCOTB_SIM=1 $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) + vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) .PHONY: gdb -gdb: $(OBJ_DIR) $(VERILOG_SOURCES) libs - iverilog -o $(OBJ_DIR)/sim.vvp -D COCOTB_SIM=1 $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) +gdb: $(SIM_BUILD) $(VERILOG_SOURCES) libs + iverilog -o $(SIM_BUILD)/sim.vvp -D COCOTB_SIM=1 $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - gdb --args vvp -M $(LIB_DIR) -m gpivpi $(OBJ_DIR)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) + gdb --args vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) clean:: - -@rm -rf $(OBJ_DIR) + -@rm -rf $(SIM_BUILD) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index fadd5e98..df766ae7 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -27,20 +27,15 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -OBJ_DIR := obj VPI_LIB := vpi -all: $(OBJ_DIR) sim - -$(OBJ_DIR): - mkdir -p $(OBJ_DIR) - +all: $(SIM_BUILD) sim .PHONY: sim -sim: $(OBJ_DIR) $(VERILOG_SOURCES) libs_native +sim: $(SIM_BUILD) $(VERILOG_SOURCES) libs_native PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ irun -sv $(EXTRA_ARGS) -64bit -loadvpi $(BUILD_DIR)/libgpi:vlog_startup_routines_bootstrap +access+rwc $(VERILOG_SOURCES) clean: - -@rm -rf $(OBJ_DIR) + -@rm -rf $(SIM_BUILD) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 751a1ec6..2584f892 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -27,24 +27,18 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -OBJ_DIR := obj VPI_LIB := vpi -VPI_FILE := $(SIM_ROOT)/build/libs/x86_64/libgpi.so - -all: $(OBJ_DIR) sim - -$(OBJ_DIR): - mkdir -p $(OBJ_DIR) - +VPI_FILE := $(LIB_DIR)/libgpi.so +all: $(SIM_BUILD) sim .PHONY: sim -sim: $(OBJ_DIR) $(VERILOG_SOURCES) libs_native +sim: $(SIM_BUILD) $(VERILOG_SOURCES) libs_native PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(PYTHON_LIBDIR):$(PYTHON_DYNLIBDIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ qverilog -64 -R -pli $(VPI_FILE) - +acc $(EXTRA_ARGS) $(VERILOG_SOURCES) clean: - -@rm -rf $(OBJ_DIR) + -@rm -rf $(SIM_BUILD) -@rm -rf INCA_libs -@rm -rf qverilog.log diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index 688a035d..df21458d 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -27,12 +27,6 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -ifeq ($(SIM_BUILD_ROOT),) -SIM_BUILD := sim_build -else -SIM_BUILD := $(SIM_BUILD_ROOT) -endif - VPI_LIB := vpi ifeq ($(ARCH),x86_64) @@ -43,9 +37,6 @@ sim: -@rm -rf results.xml $(MAKE) results.xml -$(SIM_BUILD): - mkdir -p $(SIM_BUILD) - # TODO: # investigate +vpi+1 option which reduces memory requirements From 1981ec1e8cfb8c1685a92bc14dbcd342818a21bc Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 15 Oct 2013 16:59:16 +0100 Subject: [PATCH 0405/2656] Cleanup: Improve reporting so that the tail of the log displays number of failures --- cocotb/regression.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 1419171a..3181628b 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -71,6 +71,8 @@ def initialise(self): self.ntests = 0 self.count = 1 + self.skipped = 0 + self.failures = 0 self.xunit = XUnitReporter() self.xunit.add_testsuite(name="all", tests=repr(self.ntests), package="all") @@ -94,16 +96,16 @@ def initialise(self): if hasattr(thing, "im_test"): try: test = thing(self._dut) + skip = test.skip except TestError: - self.log.warning("Skipping test %s" % thing.name) - self.xunit.add_testcase(name=thing.name, classname=module_name, time="0.0") - self.xunit.add_skipped() - continue + skip = True + self.log.warning("Failed to initialise test%s" % thing.name) - if test.skip: + if skip: self.log.info("Skipping test %s" % thing.name) self.xunit.add_testcase(name=thing.name, classname=module_name, time="0.0") self.xunit.add_skipped() + self.skipped += 1 else: self._queue.append(test) self.ntests += 1 @@ -117,6 +119,12 @@ def initialise(self): def tear_down(self): """It's the end of the world as we know it""" + if self.failures: + self.log.error("Failed %d out of %d tests (%d skipped)" % + (self.failures, self.count, self.skipped)) + else: + self.log.info("Passed %d tests (%d skipped)" % + (self.count, self.skipped)) self.log.info("Shutting down...") self.xunit.write() simulator.stop_simulator() @@ -150,11 +158,13 @@ def handle_result(self, result): self.log.error("Test passed but we expected an error: %s (result was %s)" % ( self._running_test.funcname, result.__class__.__name__)) self.xunit.add_failure(stdout=str(result), stderr="\n".join(self._running_test.error_messages)) + self.failures += 1 elif isinstance(result, TestSuccess): self.log.error("Test passed but we expected a failure: %s (result was %s)" % ( self._running_test.funcname, result.__class__.__name__)) self.xunit.add_failure(stdout=str(result), stderr="\n".join(self._running_test.error_messages)) + self.failures += 1 elif isinstance(result, TestError) and self._running_test.expect_error: self.log.info("Test errored as expected: %s (result was %s)" % ( @@ -164,6 +174,7 @@ def handle_result(self, result): self.log.error("Test Failed: %s (result was %s)" % ( self._running_test.funcname, result.__class__.__name__)) self.xunit.add_failure(stdout=str(result), stderr="\n".join(self._running_test.error_messages)) + self.failures += 1 self.execute() From a058b6dc66cd08b3747a09f0ae2f49fc9f64f75b Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Fri, 18 Oct 2013 16:46:50 +0100 Subject: [PATCH 0406/2656] Cleanup: Correct out-by-1 error in number of tests --- cocotb/regression.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 3181628b..21d891a6 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -124,7 +124,7 @@ def tear_down(self): (self.failures, self.count, self.skipped)) else: self.log.info("Passed %d tests (%d skipped)" % - (self.count, self.skipped)) + (self.count-1, self.skipped)) self.log.info("Shutting down...") self.xunit.write() simulator.stop_simulator() From 7799a8d2661e85e6b7a700f4bec347416d9bcf2f Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Wed, 23 Oct 2013 14:55:58 +0100 Subject: [PATCH 0407/2656] Cleanup: Correct out-by-1 error in number of tests (failed case) --- cocotb/regression.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 21d891a6..1f8bacb5 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -121,7 +121,7 @@ def tear_down(self): """It's the end of the world as we know it""" if self.failures: self.log.error("Failed %d out of %d tests (%d skipped)" % - (self.failures, self.count, self.skipped)) + (self.failures, self.count-1, self.skipped)) else: self.log.info("Passed %d tests (%d skipped)" % (self.count-1, self.skipped)) From 1652f2fd454dcdfcf96c870fba7d36c603196eed Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 24 Oct 2013 10:18:47 +0100 Subject: [PATCH 0408/2656] Added clear method to driver to clear the queue of pending transactions --- cocotb/drivers/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index 00ba78a2..d2b57416 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -120,6 +120,12 @@ def append(self, transaction, callback=None, event=None): self._sendQ.append((transaction, callback, event)) self._pending.set() + def clear(self): + """ + Clear any queued transactions without sending them onto the bus + """ + self._sendQ = [] + @coroutine def send(self, transaction, sync=True): """ From b746eb91bdaa45047e5cb66bbf8c91d15e8b6e2d Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 29 Oct 2013 15:53:41 +0000 Subject: [PATCH 0409/2656] Fixes #101: Implement Lock trigger that multiple coroutines can block on --- cocotb/triggers.py | 75 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index f01bba21..5b216a98 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -239,7 +239,7 @@ class Combine(PythonTrigger): """ def __init__(self, *args): - Trigger.__init__(self) + PythonTrigger.__init__(self) self._triggers = args # TODO: check that trigger is an iterable containing only Trigger objects try: @@ -270,7 +270,7 @@ class Event(PythonTrigger): Event to permit synchronisation between two coroutines """ def __init__(self, name=""): - Trigger.__init__(self) + PythonTrigger.__init__(self) self._callback = None self.name = name self.fired = False @@ -300,6 +300,75 @@ def clear(self): def __str__(self): return self.__class__.__name__ + "(%s)" % self.name + +class _Lock(PythonTrigger): + """ + Unique instance used by the Lock object. + + One created for each attempt to acquire the Lock so that the scheduler + can maintain a dictionary of indexing each individual coroutine + """ + def __init__(self, parent): + PythonTrigger.__init__(self) + self.parent = parent + + def prime(self, callback): + self._callback = callback + self.parent.prime(callback, self) + + def __call__(self): + self._callback(self) + + +class Lock(PythonTrigger): + """ + Lock primitive (not re-entrant) + """ + + def __init__(self, name=""): + PythonTrigger.__init__(self) + self._pending_unprimed = [] + self._pending_primed = [] + self.name = name + self.locked = False + + def prime(self, callback, trigger): + + self._pending_unprimed.remove(trigger) + + if not self.locked: + self.locked = True + callback(trigger) + else: + self._pending_primed.append(trigger) + + def acquire(self): + """This can be yielded to block until the lock is acquired""" + trig = _Lock(self) + self._pending_unprimed.append(trig) + return trig + + + def release(self): + + if not self.locked: + raise_error(self, "Attempt to release an unacquired Lock %s" % (str(self))) + + self.locked = False + + # nobody waiting for this lock + if not self._pending_primed: + return + + trigger = self._pending_primed.pop(0) + self.locked = True + trigger() + + def __str__(self): + return self.__class__.__name__ + "(%s) [%s waiting]" % ( + self.name, len(self._pending_primed)) + + class NullTrigger(Trigger): """ Trigger for internal interfacing use call the callback as soon @@ -319,7 +388,7 @@ class Join(PythonTrigger): Join a coroutine, firing when it exits """ def __init__(self, coroutine): - Trigger.__init__(self) + PythonTrigger.__init__(self) self._coroutine = coroutine self.retval = None From ee9194cda3c588920e84e87f2f139363f884b9a8 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 29 Oct 2013 18:42:09 +0000 Subject: [PATCH 0410/2656] Issue #102: Provide a distinct trigger for each coro that yields on Event.wait() --- cocotb/triggers.py | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 5b216a98..e1c0dc25 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -265,31 +265,57 @@ def unprime(self): trigger.unprime() +class _Event(PythonTrigger): + """ + Unique instance used by the Event object. + + One created for each attempt to wait on the event so that the scheduler + can maintain a dictionary of indexing each individual coroutine + + FIXME: This will leak - need to use peers to ensure everything is removed + """ + def __init__(self, parent): + PythonTrigger.__init__(self) + self.parent = parent + + def prime(self, callback): + self._callback = callback + self.parent.prime(callback, self) + + def __call__(self): + self._callback(self) + + + class Event(PythonTrigger): """ Event to permit synchronisation between two coroutines """ def __init__(self, name=""): PythonTrigger.__init__(self) - self._callback = None + self._pending = [] self.name = name self.fired = False self.data = None - def prime(self, callback): - self._callback = callback + def prime(self, callback, trigger): + self._pending.append(trigger) def set(self, data=None): """Wake up any coroutines blocked on this event""" self.fired = True self.data = data - if not self._callback: - return - self._callback(self) + + p = self._pending[:] + + self._pending = [] + + for trigger in p: + trigger() def wait(self): """This can be yielded to block this coroutine until another wakes it""" - return self + return _Event(self) def clear(self): """Clear this event that's fired. @@ -307,6 +333,8 @@ class _Lock(PythonTrigger): One created for each attempt to acquire the Lock so that the scheduler can maintain a dictionary of indexing each individual coroutine + + FIXME: This will leak - need to use peers to ensure everything is removed """ def __init__(self, parent): PythonTrigger.__init__(self) From f138639bd921813653c01c192f977aa3199e0f8f Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 3 Nov 2013 22:57:50 +0000 Subject: [PATCH 0411/2656] Issue #103: Fix "clean" target to consistently use double colon --- makefiles/simulators/Makefile.ius | 2 +- makefiles/simulators/Makefile.questa | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index df766ae7..3a9a59da 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -36,6 +36,6 @@ sim: $(SIM_BUILD) $(VERILOG_SOURCES) libs_native PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ irun -sv $(EXTRA_ARGS) -64bit -loadvpi $(BUILD_DIR)/libgpi:vlog_startup_routines_bootstrap +access+rwc $(VERILOG_SOURCES) -clean: +clean:: -@rm -rf $(SIM_BUILD) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 2584f892..5e54d88b 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -37,7 +37,7 @@ sim: $(SIM_BUILD) $(VERILOG_SOURCES) libs_native PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(PYTHON_LIBDIR):$(PYTHON_DYNLIBDIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ qverilog -64 -R -pli $(VPI_FILE) - +acc $(EXTRA_ARGS) $(VERILOG_SOURCES) -clean: +clean:: -@rm -rf $(SIM_BUILD) -@rm -rf INCA_libs -@rm -rf qverilog.log From dd65cf40ea9423768ae715b73f95486a5e61c60c Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Mon, 4 Nov 2013 08:14:44 +0000 Subject: [PATCH 0412/2656] Issue #103: Fix dependencies in makefiles --- makefiles/simulators/Makefile.ius | 2 +- makefiles/simulators/Makefile.questa | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 3a9a59da..ac6441e5 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -32,7 +32,7 @@ VPI_LIB := vpi all: $(SIM_BUILD) sim .PHONY: sim -sim: $(SIM_BUILD) $(VERILOG_SOURCES) libs_native +sim: $(SIM_BUILD) $(VERILOG_SOURCES) libs $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ irun -sv $(EXTRA_ARGS) -64bit -loadvpi $(BUILD_DIR)/libgpi:vlog_startup_routines_bootstrap +access+rwc $(VERILOG_SOURCES) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 5e54d88b..da0c91d3 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -33,7 +33,7 @@ VPI_FILE := $(LIB_DIR)/libgpi.so all: $(SIM_BUILD) sim .PHONY: sim -sim: $(SIM_BUILD) $(VERILOG_SOURCES) libs_native +sim: $(SIM_BUILD) $(VERILOG_SOURCES) libs $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(PYTHON_LIBDIR):$(PYTHON_DYNLIBDIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ qverilog -64 -R -pli $(VPI_FILE) - +acc $(EXTRA_ARGS) $(VERILOG_SOURCES) From 7a280d504eb8e476703fd4a5a252093cecbde97c Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 2 Dec 2013 14:39:29 +0000 Subject: [PATCH 0413/2656] Update Makefile for Aldec simulator --- makefiles/simulators/Makefile.aldec | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 4e4e2c56..703b1443 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -55,10 +55,10 @@ end\n\ endmodule\n" > $@ # Create a TCL script based on the list of $(VERILOG_SOURCES) -$(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(SIM_BUILD) libs_native +$(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(SIM_BUILD) libs echo "alib work" > $@ echo "set worklib work" >> $@ - cd $(SIM_BUILD) && create_project.py $(VERILOG_SOURCES) >> runsim.tcl + cd $(SIM_BUILD) && $(COCOTB)/bin/create_project.py $(VERILOG_SOURCES) >> runsim.tcl echo "asim +access +w -O2 -dbg -pli libcocotb $(TOPLEVEL)" >> $@ ifeq ($(GUI),1) echo "wave -rec *" >> $@ @@ -71,7 +71,7 @@ endif # that turns on batch mode (i.e. exit on completion/error) .PHONY: sim -sim: $(SIM_BUILD)/runsim.tcl core +sim: $(SIM_BUILD)/runsim.tcl cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(PWD)/testbench:$(SIM_ROOT)/sim_core:$(PWD):$(PYTHONPATH) \ LD_LIBRARY_PATH=$(LD_LIBRARY_PATH):$(VSIMSABIN)/Linux64 MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do runsim.tcl | tee sim.log From 96663f4e08e3531c4ffd71797e1406d36b2a8901 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 5 Dec 2013 09:49:22 +0000 Subject: [PATCH 0414/2656] Cleanup: Correct typo in teardown --- cocotb/handle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) mode change 100644 => 100755 cocotb/handle.py diff --git a/cocotb/handle.py b/cocotb/handle.py old mode 100644 new mode 100755 index 39c7dffc..f0f806f0 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -192,5 +192,5 @@ def __iter__(self): yield hdl def __del__(self): """Free handle from gpi that was allocated on construction""" - if self.handle is not None: - simulator.free_handle(self.handle) + if self._handle is not None: + simulator.free_handle(self._handle) From f5113a270e991cf0ed1c334cfa2355c430649821 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 5 Dec 2013 10:05:08 +0000 Subject: [PATCH 0415/2656] Fixes #105: Riviera support --- bin/create_project.py | 2 +- cocotb/triggers.py | 20 +++++++++++--- examples/endian_swapper/hdl/endian_swapper.sv | 1 + lib/vpi_shim/gpi_vpi.c | 2 +- makefiles/simulators/Makefile.aldec | 27 ++++--------------- 5 files changed, 24 insertions(+), 28 deletions(-) diff --git a/bin/create_project.py b/bin/create_project.py index 1d0982f4..44cbcaad 100755 --- a/bin/create_project.py +++ b/bin/create_project.py @@ -42,7 +42,7 @@ def riviera(source_files): for filename in source_files: - print "alog %s %s" % (_debug_str, filename) + print "alog +define+COCOTB_SIM %s %s" % (_debug_str, filename) if __name__ == "__main__": diff --git a/cocotb/triggers.py b/cocotb/triggers.py index e1c0dc25..4a06f3cb 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -29,8 +29,6 @@ A collections of triggers which a testbench can 'yield' """ import simulator -import cocotb -import pdb from cocotb.log import SimLog from cocotb.result import raise_error @@ -85,13 +83,14 @@ class GPITrigger(Trigger): """ def __init__(self): Trigger.__init__(self) - self.cbhdl = simulator.create_callback(self) + self.cbhdl = None def unprime(self): """Unregister a prior registered timed callback""" Trigger.unprime(self) if self.cbhdl is not None: simulator.deregister_callback(self.cbhdl) + self.chhdl = None def __del__(self): """Remove knowledge of the trigger""" @@ -113,6 +112,7 @@ def __init__(self, time_ps): def prime(self, callback): """Register for a timed callback""" + self.cbhdl = simulator.create_callback(self) if simulator.register_timed_callback(self.cbhdl, self.time_ps, callback, self): raise_error(self, "Unable set up %s Trigger" % (str(self))) @@ -129,6 +129,7 @@ def __init__(self, signal): def prime(self, callback): """Register notification of a value change via a callback""" + self.cbhdl = simulator.create_callback(self) if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, callback, self): raise_error(self, "Unable set up %s Trigger" % (str(self))) @@ -144,6 +145,7 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): + self.cbhdl = simulator.create_callback(self) if simulator.register_readonly_callback(self.cbhdl, callback, self): raise_error(self, "Unable set up %s Trigger" % (str(self))) @@ -159,6 +161,7 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): + self.cbhdl = simulator.create_callback(self) if simulator.register_rwsynch_callback(self.cbhdl, callback, self): raise_error(self, "Unable set up %s Trigger" % (str(self))) @@ -173,6 +176,7 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): + self.cbhdl = simulator.create_callback(self) simulator.register_nextstep_callback(self.cbhdl, callback, self) def __str__(self): @@ -181,6 +185,9 @@ def __str__(self): class RisingEdge(Edge): """ Execution will resume when a rising edge occurs on the provided signal + + NB Riviera doesn't seem to like re-using a callback handle? + """ def __init__(self, signal): Edge.__init__(self, signal) @@ -192,9 +199,12 @@ def _check(obj): if self.signal.value: self._callback(self) else: + simulator.deregister_callback(self.cbhdl) + self.cbhdl = simulator.create_callback(self) if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self): raise_error(self, "Unable set up %s Trigger" % (str(self))) + self.cbhdl = simulator.create_callback(self) if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self): raise_error(self, "Unable set up %s Trigger" % (str(self))) @@ -221,10 +231,12 @@ def _check(obj): self._callback(self) return + simulator.deregister_callback(self.cbhdl) + self.cbhdl = simulator.create_callback(self) if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self): raise_error(self, "Unable set up %s Trigger" % (str(self))) - + self.cbhdl = simulator.create_callback(self) if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self): raise_error(self, "Unable set up %s Trigger" % (str(self))) diff --git a/examples/endian_swapper/hdl/endian_swapper.sv b/examples/endian_swapper/hdl/endian_swapper.sv index faa2bf35..2ec3e304 100644 --- a/examples/endian_swapper/hdl/endian_swapper.sv +++ b/examples/endian_swapper/hdl/endian_swapper.sv @@ -174,6 +174,7 @@ end initial begin $dumpfile ("waveform.vcd"); $dumpvars (0,endian_swapper); + #1; end `endif diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 25421a37..831108a8 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -293,7 +293,7 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) obj = vpi_handle_by_name(buff, (vpiHandle)(parent->sim_hdl)); if (!obj) { LOG_DEBUG("VPI: Handle '%s' not found!", name); - check_vpi_error(); +// check_vpi_error(); return NULL; } diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 703b1443..f94309fd 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -27,11 +27,6 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### - -VPI_FILE = $(LIB_DIR)/cocotb.vpi -VPI_LIB := aldecpli -VPI_LD_PATH := $(RIVERA_BASE)/lib - all : sim # Common Makefile for Aldec Riviera-PRO simulator @@ -42,24 +37,12 @@ else CMD := vsimsa endif -# Aldec requires a process in the verilog - if the only -# timesteps are PLI initiated then the sim finishes immediately -# -# FIXME this doesn't work, still need this code in the toplevel GRRRR -$(SIM_BUILD)/initial.v: - echo -e "module fake_initial;\n\ -initial begin\n\ - $display (\"Initial statement began\");\n\ - #99999999999 $finish;\n\ -end\n\ -endmodule\n" > $@ - # Create a TCL script based on the list of $(VERILOG_SOURCES) -$(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(SIM_BUILD) libs +$(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(SIM_BUILD) echo "alib work" > $@ echo "set worklib work" >> $@ cd $(SIM_BUILD) && $(COCOTB)/bin/create_project.py $(VERILOG_SOURCES) >> runsim.tcl - echo "asim +access +w -O2 -dbg -pli libcocotb $(TOPLEVEL)" >> $@ + echo "asim +access +w -O2 -dbg -pli libgpi $(TOPLEVEL)" >> $@ ifeq ($(GUI),1) echo "wave -rec *" >> $@ else @@ -71,9 +54,9 @@ endif # that turns on batch mode (i.e. exit on completion/error) .PHONY: sim -sim: $(SIM_BUILD)/runsim.tcl - cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(PWD)/testbench:$(SIM_ROOT)/sim_core:$(PWD):$(PYTHONPATH) \ - LD_LIBRARY_PATH=$(LD_LIBRARY_PATH):$(VSIMSABIN)/Linux64 MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do runsim.tcl | tee sim.log +sim: $(SIM_BUILD)/runsim.tcl libs + cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ + LD_LIBRARY_PATH=$(LIB_DIR):$(VSIMSABIN)/Linux64 MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do runsim.tcl | tee sim.log clean:: rm -rf $(SIM_BUILD) From 71550332dfa4e5bf6a3dd3ec64a2241cdb0dadd1 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 5 Dec 2013 16:02:45 +0000 Subject: [PATCH 0416/2656] Enable optional coverage for Aldec Riviera --- bin/create_project.py | 2 +- makefiles/simulators/Makefile.aldec | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/bin/create_project.py b/bin/create_project.py index 44cbcaad..2fd2f593 100755 --- a/bin/create_project.py +++ b/bin/create_project.py @@ -42,7 +42,7 @@ def riviera(source_files): for filename in source_files: - print "alog +define+COCOTB_SIM %s %s" % (_debug_str, filename) + print "alog -coverage sbe +define+COCOTB_SIM %s %s" % (_debug_str, filename) if __name__ == "__main__": diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index f94309fd..7fadc7c5 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -37,18 +37,26 @@ else CMD := vsimsa endif +ifeq ($(COVERAGE),1) + ASIM_ARGS += "-acdb" +endif + + # Create a TCL script based on the list of $(VERILOG_SOURCES) $(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(SIM_BUILD) echo "alib work" > $@ echo "set worklib work" >> $@ cd $(SIM_BUILD) && $(COCOTB)/bin/create_project.py $(VERILOG_SOURCES) >> runsim.tcl - echo "asim +access +w -O2 -dbg -pli libgpi $(TOPLEVEL)" >> $@ + echo "asim $(ASIM_ARGS) +access +w -O2 -dbg -pli libgpi $(TOPLEVEL)" >> $@ ifeq ($(GUI),1) echo "wave -rec *" >> $@ else echo "run -all" >> $@ echo "endsim" >> $@ endif +ifeq ($(COVERAGE),1) + echo "acdb report -db work.acdb -html" >> $@ +endif # Note it's the redirection of the output rather than the 'do' command # that turns on batch mode (i.e. exit on completion/error) From 85d20a146717b51681504eeec826102fcbc7881f Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 5 Dec 2013 16:12:39 +0000 Subject: [PATCH 0417/2656] Put coverage report in a separate directory for Jenkins --- makefiles/simulators/Makefile.aldec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 7fadc7c5..faa5e245 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -55,7 +55,7 @@ else echo "endsim" >> $@ endif ifeq ($(COVERAGE),1) - echo "acdb report -db work.acdb -html" >> $@ + echo "acdb report -db work.acdb -html -o coverage/acdb_report.html" >> $@ endif # Note it's the redirection of the output rather than the 'do' command From f5d0886a0aabac5309f518e6d9007d36678f8b03 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 10 Dec 2013 10:46:31 +0000 Subject: [PATCH 0418/2656] Issue #106: Example of responding to ping in RTL using a TUN interface --- examples/ping_tun_tap/hdl/icmp_reply.sv | 159 ++++++++++++++++++ examples/ping_tun_tap/tests/Makefile | 38 +++++ .../ping_tun_tap/tests/test_icmp_reply.py | 108 ++++++++++++ 3 files changed, 305 insertions(+) create mode 100644 examples/ping_tun_tap/hdl/icmp_reply.sv create mode 100644 examples/ping_tun_tap/tests/Makefile create mode 100644 examples/ping_tun_tap/tests/test_icmp_reply.py diff --git a/examples/ping_tun_tap/hdl/icmp_reply.sv b/examples/ping_tun_tap/hdl/icmp_reply.sv new file mode 100644 index 00000000..1cc69b80 --- /dev/null +++ b/examples/ping_tun_tap/hdl/icmp_reply.sv @@ -0,0 +1,159 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2013 Potential Ventures Ltd +// Copyright (c) 2013 SolarFlare Communications Inc +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Potential Ventures Ltd, +// Copyright (c) 2013 SolarFlare Communications Inc nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- +// +// Simple ICMP echo server to repond to pings. +// +// Does store-and-forward using an array then modifies the checksum in place +// +// Doesn't perform any validation of the packet etc. +// +// Note this is not an example of how to write RTL ;) + +module icmp_reply ( + input clk, + input reset_n, + + input [31:0] stream_in_data, + input [1:0] stream_in_empty, + input stream_in_valid, + input stream_in_startofpacket, + input stream_in_endofpacket, + output reg stream_in_ready, + + output reg [31:0] stream_out_data, + output reg [1:0] stream_out_empty, + output reg stream_out_valid, + output reg stream_out_startofpacket, + output reg stream_out_endofpacket, + input stream_out_ready +); + +parameter S_IDLE = 3'b000; +parameter S_RECV_PACKET = 3'b001; +parameter S_MODIFY_PACKET = 3'b010; +parameter S_SEND_PACKET = 3'b011; + +reg [2:0] state; +reg [31:0] packet_buffer [63:0]; + +reg [4:0] rx_word_ptr, tx_word_ptr; +reg [1:0] empty_saved; + + + +always @(posedge clk or negedge reset_n) begin + if (!reset_n) begin + state <= S_IDLE; + rx_word_ptr <= 0; + tx_word_ptr <= 0; + stream_out_valid <= 1'b0; + stream_in_ready <= 1'b0; + end else begin + + case (state) + S_IDLE: begin + + stream_in_ready <= 1'b1; + + if (stream_in_startofpacket && stream_in_valid && stream_in_ready) begin + state <= S_RECV_PACKET; + rx_word_ptr <= 1; + packet_buffer[0] <= stream_in_data; + end + end + + S_RECV_PACKET: begin + if (stream_in_valid) begin + packet_buffer[rx_word_ptr] <= stream_in_data; + rx_word_ptr <= rx_word_ptr + 1; + + if (stream_in_endofpacket) begin + state <= S_MODIFY_PACKET; + stream_in_ready <= 1'b0; + empty_saved <= stream_in_empty; + end + end + end + + // NB since we do all modification in one cycle this won't + // synthesise as a RAM - code not intended for actual use + S_MODIFY_PACKET: begin + + // Swap src/destination addresses + packet_buffer[3] <= packet_buffer[4]; + packet_buffer[4] <= packet_buffer[3]; + + // Change the ICMP type to Echo Reply + packet_buffer[5][7:0] <= 8'b0; + + // Modify checksum in-place + packet_buffer[5][31:16] <= packet_buffer[5][31:16] - 16'h0800; + + state <= S_SEND_PACKET; + stream_out_startofpacket<= 1'b1; + stream_out_empty <= 0; + end + + S_SEND_PACKET: begin + stream_out_valid <= 1'b1; + stream_out_data <= packet_buffer[tx_word_ptr]; + + if (stream_out_ready) begin + tx_word_ptr <= tx_word_ptr + 1; + stream_out_startofpacket <= 1'b0; + + if (tx_word_ptr == rx_word_ptr - 1) begin + stream_out_empty <= empty_saved; + stream_out_endofpacket <= 1'b1; + end + + if (tx_word_ptr == rx_word_ptr) begin + state <= S_IDLE; + rx_word_ptr <= 0; + tx_word_ptr <= 0; + stream_out_valid <= 1'b0; + stream_out_endofpacket <= 1'b0; + end + + end + end + + endcase + end +end + +`ifdef COCOTB_SIM +initial begin + $dumpfile ("waveform.vcd"); + $dumpvars (0,icmp_reply); + #1 $display("Sim running..."); +end +`endif + +endmodule diff --git a/examples/ping_tun_tap/tests/Makefile b/examples/ping_tun_tap/tests/Makefile new file mode 100644 index 00000000..b14f9307 --- /dev/null +++ b/examples/ping_tun_tap/tests/Makefile @@ -0,0 +1,38 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +TOPLEVEL = icmp_reply + +PWD=$(shell pwd) + +VERILOG_SOURCES = $(PWD)/../hdl/icmp_reply.sv +MODULE=test_icmp_reply + +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim diff --git a/examples/ping_tun_tap/tests/test_icmp_reply.py b/examples/ping_tun_tap/tests/test_icmp_reply.py new file mode 100644 index 00000000..307318ae --- /dev/null +++ b/examples/ping_tun_tap/tests/test_icmp_reply.py @@ -0,0 +1,108 @@ +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +import random +import logging + +import fcntl +import os +import struct +import subprocess + +import cocotb + +from cocotb.clock import Clock +from cocotb.triggers import Timer, RisingEdge +from cocotb.drivers.avalon import AvalonSTPkts as AvalonSTDriver +from cocotb.monitors.avalon import AvalonSTPkts as AvalonSTMonitor + + +def create_tun(name="tun0", ip="192.168.255.1"): + cocotb.log.info("Attempting to create interface %s (%s)" % (name, ip)) + TUNSETIFF = 0x400454ca + TUNSETOWNER = TUNSETIFF + 2 + IFF_TUN = 0x0001 + IFF_NO_PI = 0x1000 + tun = open('/dev/net/tun', 'r+b') + ifr = struct.pack('16sH', name, IFF_TUN | IFF_NO_PI) + fcntl.ioctl(tun, TUNSETIFF, ifr) + fcntl.ioctl(tun, TUNSETOWNER, 1000) + subprocess.check_call('ifconfig tun0 %s up pointopoint 192.168.255.2 up' % ip, shell=True) + return tun + + +@cocotb.test() +def tun_tap_example_test(dut): + """Example of a test using TUN/TAP. + + Creates an interface (192.168.255.1) and any packets received are sent + into the DUT. The response output by the DUT is then sent back out on + this interface. + + Note to create the TUN interface this test must be run as root or the user + must have CAP_NET_ADMIN capability. + """ + + cocotb.fork(Clock(dut.clk, 5000).start()) + + stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) + stream_out = AvalonSTMonitor(dut, "stream_out", dut.clk) + + # Enable verbose logging so we can see what's going on + stream_in.log.setLevel(logging.DEBUG) + stream_out.log.setLevel(logging.DEBUG) + + + # Reset the DUT + dut.log.debug("Resetting DUT") + dut.reset_n <= 0 + stream_in.bus.valid <= 0 + yield Timer(10000) + yield RisingEdge(dut.clk) + dut.reset_n <= 1 + dut.stream_out_ready <= 1 + dut.log.debug("Out of reset") + + + # Create our interface (destroyed at the end of the test) + tun = create_tun() + fd = tun.fileno() + + # Kick off a ping... + subprocess.check_call('ping -c 5 192.168.255.2 &', shell=True) + + # Respond to 5 pings, then quit + for i in xrange(5): + + cocotb.log.info("Waiting for packets on tun interface") + packet = os.read(fd, 2048) + cocotb.log.info("Received a packet!") + + stream_in.append(packet) + result = yield stream_out.wait_for_recv() + + cocotb.log.info("Rtl replied!") + os.write(fd, str(result)) From e682bb4bf43f1b8f45085bbbe540e1cdc7495bd9 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 10 Dec 2013 10:48:33 +0000 Subject: [PATCH 0419/2656] Fix copyright on new file --- examples/ping_tun_tap/hdl/icmp_reply.sv | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/ping_tun_tap/hdl/icmp_reply.sv b/examples/ping_tun_tap/hdl/icmp_reply.sv index 1cc69b80..a1b8eb51 100644 --- a/examples/ping_tun_tap/hdl/icmp_reply.sv +++ b/examples/ping_tun_tap/hdl/icmp_reply.sv @@ -1,6 +1,5 @@ //----------------------------------------------------------------------------- // Copyright (c) 2013 Potential Ventures Ltd -// Copyright (c) 2013 SolarFlare Communications Inc // All rights reserved. // // Redistribution and use in source and binary forms, with or without From b7fa2da13c4ae4607d5bff7bce713a8191ad7e01 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 12 Dec 2013 09:19:25 +0000 Subject: [PATCH 0420/2656] Support for ALOG_ARGS --- makefiles/simulators/Makefile.aldec | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index faa5e245..ce55b432 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -37,8 +37,11 @@ else CMD := vsimsa endif +ALOG_ARGS += "+define+COCOTB_SIM -dbg" + ifeq ($(COVERAGE),1) ASIM_ARGS += "-acdb" + ALOG_ARGS += "-coverage sbe" endif @@ -46,7 +49,8 @@ endif $(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(SIM_BUILD) echo "alib work" > $@ echo "set worklib work" >> $@ - cd $(SIM_BUILD) && $(COCOTB)/bin/create_project.py $(VERILOG_SOURCES) >> runsim.tcl + $(foreach src,$(VERILOG_SOURCES),\ + echo "alog $(ALOG_ARGS) $(src)" >> $@) echo "asim $(ASIM_ARGS) +access +w -O2 -dbg -pli libgpi $(TOPLEVEL)" >> $@ ifeq ($(GUI),1) echo "wave -rec *" >> $@ From 31913a417a13b15e81bcdffffb9e4f271d3760aa Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 12 Dec 2013 09:27:00 +0000 Subject: [PATCH 0421/2656] Workaround for Aldec to ensure simulation runs --- examples/plusargs/tb_top.v | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/plusargs/tb_top.v b/examples/plusargs/tb_top.v index 4b9e4cae..358e8442 100644 --- a/examples/plusargs/tb_top.v +++ b/examples/plusargs/tb_top.v @@ -36,6 +36,7 @@ initial begin $display("Plusargs test"); result = $value$plusargs("foo=%s", foo_string); $display("Plusarg foo has value %0s", foo_string); - + #1 $diplay("Test running"); end + endmodule //: tb_top From 9d4e7ca5d4be0c756ff738486a00131c78fe6bb6 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 12 Dec 2013 10:55:36 +0000 Subject: [PATCH 0422/2656] Fixes #100: Correct RTL bug in endian swapper and test issue in _wait_for_nsignal --- cocotb/drivers/__init__.py | 3 ++- examples/endian_swapper/hdl/endian_swapper.sv | 5 +++-- examples/endian_swapper/tests/test_endian_swapper.py | 6 +++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index d2b57416..fb5520e0 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -242,8 +242,9 @@ def _wait_for_nsignal(self, signal): """ yield RisingEdge(self.clock) yield ReadOnly() - if signal.value.integer != 0: + while signal.value.integer != 0: yield Edge(signal) + yield ReadOnly() yield NextTimeStep() def __str__(self): diff --git a/examples/endian_swapper/hdl/endian_swapper.sv b/examples/endian_swapper/hdl/endian_swapper.sv index faa2bf35..9ef4bd14 100644 --- a/examples/endian_swapper/hdl/endian_swapper.sv +++ b/examples/endian_swapper/hdl/endian_swapper.sv @@ -110,7 +110,7 @@ always @(posedge clk or negedge reset_n) begin end else begin if (flush_pipe & stream_out_ready) - flush_pipe <= 1'b0; + flush_pipe <= stream_in_endofpacket & stream_in_valid & stream_out_ready; else if (!flush_pipe) flush_pipe <= stream_in_endofpacket & stream_in_valid & stream_out_ready; @@ -171,9 +171,10 @@ always @(posedge clk or negedge reset_n) begin end `ifdef COCOTB_SIM -initial begin +initial begin $dumpfile ("waveform.vcd"); $dumpvars (0,endian_swapper); + #1; end `endif diff --git a/examples/endian_swapper/tests/test_endian_swapper.py b/examples/endian_swapper/tests/test_endian_swapper.py index 69d459fe..d98a3eba 100644 --- a/examples/endian_swapper/tests/test_endian_swapper.py +++ b/examples/endian_swapper/tests/test_endian_swapper.py @@ -117,7 +117,7 @@ def run_test(dut, data_in=None, config_coroutine=None, idle_inserter=None, backp cocotb.fork(clock_gen(dut.clk)) tb = EndianSwapperTB(dut) - + yield tb.reset() dut.stream_out_ready <= 1 @@ -132,13 +132,13 @@ def run_test(dut, data_in=None, config_coroutine=None, idle_inserter=None, backp # Send in the packets for transaction in data_in(): yield tb.stream_in.send(transaction) - + # Wait at least 2 cycles where output ready is low before ending the test for i in xrange(2): yield RisingEdge(dut.clk) while not dut.stream_out_ready.value: yield RisingEdge(dut.clk) - + pkt_count = yield tb.csr.read(1) if pkt_count.integer != tb.pkts_sent: From be033dbcd03556c08381cea7a381610f3745f8de Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 12 Dec 2013 12:02:40 +0000 Subject: [PATCH 0423/2656] Update Aldec Makefile --- makefiles/simulators/Makefile.aldec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index ce55b432..c8d1a8dc 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -49,8 +49,8 @@ endif $(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(SIM_BUILD) echo "alib work" > $@ echo "set worklib work" >> $@ - $(foreach src,$(VERILOG_SOURCES),\ - echo "alog $(ALOG_ARGS) $(src)" >> $@) + echo -e $(foreach src,$(VERILOG_SOURCES),\ + "alog $(ALOG_ARGS) $(src)\n") >> $@ echo "asim $(ASIM_ARGS) +access +w -O2 -dbg -pli libgpi $(TOPLEVEL)" >> $@ ifeq ($(GUI),1) echo "wave -rec *" >> $@ From 5b5f71cbe57e2adc7ad29c33b352f6291499fb16 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 12 Dec 2013 12:48:37 +0000 Subject: [PATCH 0424/2656] Cleanup: Removed unused files cocotbenv.py and create_project.py --- Makefile | 2 - bin/cocotbenv.py | 157 ------------------------------------------ bin/create_project.py | 50 -------------- 3 files changed, 209 deletions(-) delete mode 100755 bin/cocotbenv.py delete mode 100755 bin/create_project.py diff --git a/Makefile b/Makefile index 40662ff6..f1b96a9a 100644 --- a/Makefile +++ b/Makefile @@ -56,8 +56,6 @@ src_install: @cp -R include/* $(FULL_INSTALL_DIR)/include/ common_install: - @cp -R bin/cocotbenv.py $(FULL_INSTALL_DIR)/bin/ - @cp -R bin/create_project.py $(FULL_INSTALL_DIR)/bin/ @cp -R makefiles $(FULL_INSTALL_DIR)/ @cp version $(FULL_INSTALL_DIR)/ diff --git a/bin/cocotbenv.py b/bin/cocotbenv.py deleted file mode 100755 index 2a9925de..00000000 --- a/bin/cocotbenv.py +++ /dev/null @@ -1,157 +0,0 @@ -#!/usr/bin/env python - -''' Copyright (c) 2013 Potential Ventures Ltd -Copyright (c) 2013 SolarFlare Communications Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - -""" - Determines how to execute the cocotb world -""" - -import os, sys, inspect, platform -from subprocess import call - -class SimType(): - def __init__(self, name=None, sdebug=False): - self._name = name; - self._sdebug = sdebug - - def execute(self, cmd): - print("Running cocotb against %s simulator" % self._name) - print(cmd) - try: - call(cmd, shell=True) - except KeyboardInterrupt: - print("Closing Test Bench") - -class SimIcarus(SimType): - def __init__(self, name=None, sdebug=False): - SimType.__init__(self, "Icarus", sdebug) - self._base_cmd = ' vvp -m gpivpi' - - def execute(self, py_path, lib_path, module, function, finput): - cmd = 'PYTHONPATH=' + py_path - cmd = cmd + ' LD_LIBRARY_PATH=' + lib_path - cmd = cmd + ' MODULE=' + module - cmd = cmd + ' TESTCASE=' + function - if self._sdebug is True: - cmd = cmd + ' gdb --args' - cmd = cmd + self._base_cmd - cmd = cmd + ' -M ' + lib_path - cmd = cmd + ' ' + finput - - SimType.execute(self, cmd) - -class SimSfsim(SimType): - def __init__(self, name=None, sdebug=False): - SimType.__init__(self, "SolarFlare Model", sdebug) - - def execute(self, py_path, lib_path, module, function, finput): - cmd = 'PYTHONPATH=' + py_path - cmd = cmd + ' LD_LIBRARY_PATH=' + lib_path - cmd = cmd + ' MODULE=' + module - cmd = cmd + ' TESTCASE=' + function - if self._sdebug is True: - cmd = cmd + ' gdb --args' - cmd = cmd + ' ' + finput - cmd = cmd + ' -l ' + lib_path + '/libgpi.so' - - SimType.execute(self, cmd) - -def main(): - - """ Start the desired simulator with cocotb embedded within. - -Options: - -h, --help Show this message and exit - -f, --function Function within Module to execute - -m, --module Module to load with test case - -s, --simtype The Simulator to use for execution - -i, --input Input file, type depends on simtype - -d, --debug Start the environment in gdb -""" - - from optparse import OptionParser - parser = OptionParser() - parser.add_option("-m", "--module", dest="module_str", - help="Module to load with test case") - parser.add_option("-f", "--function", dest="function_str", - help="Function within Module to execute") - parser.add_option("-s", "--simtype", dest="sim_str", - help="The Simulator to use for execution") - parser.add_option("-i", "--input", dest="input_str", - help="Input file, type depends on simtype") - parser.add_option("-d", action="store_true", dest="debug", - help="Debug option") - - (options, args) = parser.parse_args() - - if not options.module_str: - print main.__doc__ - sys.exit(1) - - if not options.function_str: - print main.__doc__ - sys.exit(1) - - if not options.sim_str: - print main.__doc__ - sys.exit(1) - - if not options.input_str: - print main.__doc__ - sys.exit(1) - - class_name_l = options.sim_str.lower() - class_name = 'Sim' + class_name_l[0].upper() + class_name_l[1:] - - try: - ctype = globals()[class_name] - except KeyError as e: - print ("Specified name is not valid (%s)" % class_name) - sys.exit(1) - - # Get the library paths from the current location of the cocotbenv binary - # and the arch that it is running on - exec_path = inspect.getfile(inspect.currentframe()) - base_path = exec_path.split('/bin', 1)[0] - lib_path = base_path + '/build/libs/' + platform.machine() - py_path = base_path - py_path = py_path + ':' + lib_path - py_path = py_path + ':' + os.getcwd() - - # Add the module and function as command line options that are passed - # as env vars to the underlying code - - sim_world = ctype(sdebug=options.debug) - sim_world.execute(py_path, - lib_path, - options.module_str, - options.function_str, - options.input_str) - -if __name__ == "__main__": - main() diff --git a/bin/create_project.py b/bin/create_project.py deleted file mode 100755 index 2fd2f593..00000000 --- a/bin/create_project.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python - -''' Copyright (c) 2013 Potential Ventures Ltd -Copyright (c) 2013 SolarFlare Communications Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - -""" -Script takes a list of source files and outputs a TCL script suitable -for compiling using Aldec Riviera - -Note we only handle the file-list here, any PLI / coverage is done by the Makefile - -""" -import sys - -_DEBUG=__debug__ - -_debug_str = "-dbg" if _DEBUG else "" - -def riviera(source_files): - for filename in source_files: - print "alog -coverage sbe +define+COCOTB_SIM %s %s" % (_debug_str, filename) - - -if __name__ == "__main__": - riviera(sys.argv[1:]) - From 963f41a3b3d05a823be3694ed090a653b2c88c25 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 12 Dec 2013 12:55:23 +0000 Subject: [PATCH 0425/2656] Closes #109: Add a noddy script to combine regression results into single XML file --- Makefile | 1 + bin/combine_results.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100755 bin/combine_results.py diff --git a/Makefile b/Makefile index f1b96a9a..aede5dcb 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,7 @@ clean: test: $(MAKE) -k -C examples + ./bin/combine_results.py pycode: @cp -R $(SIM_ROOT)/cocotb $(FULL_INSTALL_DIR)/ diff --git a/bin/combine_results.py b/bin/combine_results.py new file mode 100755 index 00000000..1b56995b --- /dev/null +++ b/bin/combine_results.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +""" +Simple script to combine JUnit test results into a single XML file. + +Useful for Jenkins. + +TODO: Pretty indentation +""" + +import os +from xml.etree import cElementTree as ET + +def find_all(name, path): + result = [] + for root, dirs, files in os.walk(path): + if name in files: + yield os.path.join(root, name) + +def main(path, output): + + testsuite = ET.Element("testsuite", name="all", package="all", tests="0") + + for fname in find_all("results.xml", path): + tree = ET.parse(fname) + for element in tree.iter("testcase"): + testsuite.append(element) + + result = ET.Element("testsuites", name="results") + result.append(testsuite) + + ET.ElementTree(result).write(output, encoding="UTF-8") + +if __name__ == "__main__": + main(".", "results.xml") + From 8aabbacd06e0b634b40a77270e6bf20257289f56 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 12 Dec 2013 13:18:51 +0000 Subject: [PATCH 0426/2656] Change output filename for combined results to avoid recursive accumulation --- bin/combine_results.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/combine_results.py b/bin/combine_results.py index 1b56995b..f61223c2 100755 --- a/bin/combine_results.py +++ b/bin/combine_results.py @@ -31,5 +31,5 @@ def main(path, output): ET.ElementTree(result).write(output, encoding="UTF-8") if __name__ == "__main__": - main(".", "results.xml") + main(".", "combined_results.xml") From 4dff52eda5d84544d208eacff35072b5b97e4d17 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 15 Dec 2013 08:24:18 +0000 Subject: [PATCH 0427/2656] Uprev repository version to 0.4 --- documentation/source/conf.py | 6 +++--- version | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/documentation/source/conf.py b/documentation/source/conf.py index 7f41fbde..4b9196c3 100644 --- a/documentation/source/conf.py +++ b/documentation/source/conf.py @@ -41,16 +41,16 @@ # General information about the project. project = u'cocotb' -copyright = u'2013, PotentialVentures' +copyright = u'2014, PotentialVentures' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '0.3' +version = '0.4' # The full version, including alpha/beta/rc tags. -release = '0.3' +release = '0.4' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/version b/version index 61acab09..eadcafcd 100644 --- a/version +++ b/version @@ -1 +1 @@ -VERSION=0.3 +VERSION=0.4 From 9f79179eacd257410359fbdc059bcb845125cd3c Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 15 Dec 2013 09:35:12 +0000 Subject: [PATCH 0428/2656] Issue #9: Beginnings of tutorial for the endian_swapper example --- .../diagrams/svg/endian_swapper_design.svg | 1 + .../diagrams/svg/endian_swapper_testbench.svg | 1 + .../diagrams/xml/endian_swapper_design.xml | 1 + .../diagrams/xml/endian_swapper_testbench.xml | 1 + documentation/source/index.rst | 1 + documentation/source/quickstart.rst | 2 +- documentation/source/tutorials.rst | 54 +++++++++++++++++++ 7 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 documentation/source/diagrams/svg/endian_swapper_design.svg create mode 100644 documentation/source/diagrams/svg/endian_swapper_testbench.svg create mode 100644 documentation/source/diagrams/xml/endian_swapper_design.xml create mode 100644 documentation/source/diagrams/xml/endian_swapper_testbench.xml create mode 100644 documentation/source/tutorials.rst diff --git a/documentation/source/diagrams/svg/endian_swapper_design.svg b/documentation/source/diagrams/svg/endian_swapper_design.svg new file mode 100644 index 00000000..533862bd --- /dev/null +++ b/documentation/source/diagrams/svg/endian_swapper_design.svg @@ -0,0 +1 @@ +
      endian_swapper
      [Object]
      stream_in(Avalon-ST)stream_out(Avalon-ST)csr(Avalon-MM)
      \ No newline at end of file diff --git a/documentation/source/diagrams/svg/endian_swapper_testbench.svg b/documentation/source/diagrams/svg/endian_swapper_testbench.svg new file mode 100644 index 00000000..cac38f15 --- /dev/null +++ b/documentation/source/diagrams/svg/endian_swapper_testbench.svg @@ -0,0 +1 @@ +
      endian_swapper
      [Object]
      stream_in(Avalon-ST)stream_out(Avalon-ST)csr(Avalon-MM)
      Driver
      [Object]
      Monitor
      [Object]
      Driver
      [Object]
      Scoreboard
      [Object]
      Monitor
      [Object]
      tb.model()
      [Object]
      \ No newline at end of file diff --git a/documentation/source/diagrams/xml/endian_swapper_design.xml b/documentation/source/diagrams/xml/endian_swapper_design.xml new file mode 100644 index 00000000..aed3db19 --- /dev/null +++ b/documentation/source/diagrams/xml/endian_swapper_design.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/documentation/source/diagrams/xml/endian_swapper_testbench.xml b/documentation/source/diagrams/xml/endian_swapper_testbench.xml new file mode 100644 index 00000000..077e88d6 --- /dev/null +++ b/documentation/source/diagrams/xml/endian_swapper_testbench.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/documentation/source/index.rst b/documentation/source/index.rst index 35ba61ea..f35749e9 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -17,6 +17,7 @@ Contents: building coroutines triggers + tutorials roadmap simulator_support diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index 60578794..8679ace2 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -24,7 +24,7 @@ Running an example $> git clone https://github.com/potentialventures/cocotb $> cd cocotb $> make - $> cd examples/demo + $> cd examples/endian_swapper $> make To run a test using a different simulator: diff --git a/documentation/source/tutorials.rst b/documentation/source/tutorials.rst new file mode 100644 index 00000000..2f1d2dad --- /dev/null +++ b/documentation/source/tutorials.rst @@ -0,0 +1,54 @@ +######### +Tutorials +######### + + +Endian Swapper +============== + +In this tutorial we'll use some of the built-in features of Cocotb to create a complex testbench. + +.. note:: All the code and sample output from this example are available on `EDA Playground `_ + + +Design +------ + +We have a relatively simplistic RTL block called the endian_swapper. The DUT has three interfaces, all conforming to the Avalon standard: + +.. image:: diagrams/svg/endian_swapper_design.svg + +The dut will swap the endianness of packets on the Avalon-ST bus if a configuration bit is set. For every packet arriving on the "stream_in" interface the entire packet will be endian swapped if the configuration bit is set, otherwise the entire packet will pass through unmodified. + +Testbench +--------- + +To begin with we create a class to encapsulate all the common code for the testbench. It is possible to write directed tests without using a testbench class however to encourage code re-use it is good practice to create a distinct class. + + +.. code-block:: python + + class EndianSwapperTB(object): + + def __init__(self, dut): + self.dut = dut + self.stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) + self.stream_out = AvalonSTMonitor(dut, "stream_out", dut.clk) + + self.csr = AvalonMaster(dut, "csr", dut.clk) + + # Create a scoreboard on the stream_out bus + self.expected_output = [] + self.scoreboard = Scoreboard(dut) + self.scoreboard.add_interface(self.stream_out, self.expected_output) + + # Reconstruct the input transactions from the pins and send them to our 'model' + self.stream_in_recovered = AvalonSTMonitor(dut, "stream_in", dut.clk, callback=self.model) + +With the above code we have created a testbench with the following structure: + +..image:: diagrams/svg/endian_swapper_testbench.svg + + + + From 51f0c4f3f071cd27b2b4bc2a64bdc9f384faac57 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 15 Dec 2013 09:52:55 +0000 Subject: [PATCH 0429/2656] Issue #9: Continue endian_swapper tutorial --- documentation/source/tutorials.rst | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/documentation/source/tutorials.rst b/documentation/source/tutorials.rst index 2f1d2dad..da173b58 100644 --- a/documentation/source/tutorials.rst +++ b/documentation/source/tutorials.rst @@ -6,7 +6,7 @@ Tutorials Endian Swapper ============== -In this tutorial we'll use some of the built-in features of Cocotb to create a complex testbench. +In this tutorial we'll use some of the built-in features of Cocotb to quickly create a complex testbench. .. note:: All the code and sample output from this example are available on `EDA Playground `_ @@ -47,8 +47,27 @@ To begin with we create a class to encapsulate all the common code for the testb With the above code we have created a testbench with the following structure: -..image:: diagrams/svg/endian_swapper_testbench.svg +.. image:: diagrams/svg/endian_swapper_testbench.svg +If we inspect this line-by-line: +.. code-block:: python + + self.stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) + +Here we're creating an AvalonSTDriver instance. The constructor requires 3 arguments - a handle to the entity containing the interface (**dut**), the name of the interface (**stream_in**) and the associated clock with which to drive the interface (**dut.clk**). The driver will auto-discover the signals for the interface, assuming that they follow the following naming convention interface_name_signal. + +In this case we have the following signals defined for the stream_in interface: +===== ====== ======= +Name Type Description (from Avalon Specification) +===== ====== ======= +stream_in_data data The data signal from the source to the sink +stream_in_empty empty Indicates the number of symbols that are empty during cycles that contain the end of a packet +stream_in_valid valid Asserted by the source to qualify all other source to sink signals +stream_in_startofpacket startofpacket Asserted by the source to mark the beginning of a packet +stream_in_endofpacket endofpacket Asserted by the source to mark the end of a packet +stream_in_ready ready Asserted high to indicate that the sink can accept data +===== ====== ======= +By following the signal naming convention the driver can find the signals associated with this interface automatically. From e48bb937e906cef19655e67ba4a552865de7dbee Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 16 Dec 2013 23:09:34 +0000 Subject: [PATCH 0430/2656] Update tutorials.rst --- documentation/source/tutorials.rst | 42 +++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/documentation/source/tutorials.rst b/documentation/source/tutorials.rst index da173b58..dbed9bee 100644 --- a/documentation/source/tutorials.rst +++ b/documentation/source/tutorials.rst @@ -34,10 +34,8 @@ To begin with we create a class to encapsulate all the common code for the testb self.dut = dut self.stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) self.stream_out = AvalonSTMonitor(dut, "stream_out", dut.clk) - self.csr = AvalonMaster(dut, "csr", dut.clk) - # Create a scoreboard on the stream_out bus self.expected_output = [] self.scoreboard = Scoreboard(dut) self.scoreboard.add_interface(self.stream_out, self.expected_output) @@ -57,17 +55,49 @@ If we inspect this line-by-line: Here we're creating an AvalonSTDriver instance. The constructor requires 3 arguments - a handle to the entity containing the interface (**dut**), the name of the interface (**stream_in**) and the associated clock with which to drive the interface (**dut.clk**). The driver will auto-discover the signals for the interface, assuming that they follow the following naming convention interface_name_signal. -In this case we have the following signals defined for the stream_in interface: +In this case we have the following signals defined for the **stream_in** interface: -===== ====== ======= +===== ====== ======================================= Name Type Description (from Avalon Specification) -===== ====== ======= +===== ====== ======================================= stream_in_data data The data signal from the source to the sink stream_in_empty empty Indicates the number of symbols that are empty during cycles that contain the end of a packet stream_in_valid valid Asserted by the source to qualify all other source to sink signals stream_in_startofpacket startofpacket Asserted by the source to mark the beginning of a packet stream_in_endofpacket endofpacket Asserted by the source to mark the end of a packet stream_in_ready ready Asserted high to indicate that the sink can accept data -===== ====== ======= +===== ====== ======================================= By following the signal naming convention the driver can find the signals associated with this interface automatically. + + +.. code-block:: python + + self.stream_out = AvalonSTMonitor(dut, "stream_out", dut.clk) + self.csr = AvalonMaster(dut, "csr", dut.clk) + +We do the same to create the monitor on **stream_out** and the CSR interface. + + +.. code-block:: python + + self.expected_output = [] + self.scoreboard = Scoreboard(dut) + self.scoreboard.add_interface(self.stream_out, self.expected_output) + +The above lines create a Scoreboard instance and attach it to the **stream_out** monitor instance. The scoreboard is used to check that the DUT behaviour is correct. The call to **add_interface** takes a Monitor instance as the first argument and the second argument is a mechanism for describing the expected output for that interface. This could be a callable function but in this example a simple list of expected transactions is sufficient. + +.. code-block:: python + + # Reconstruct the input transactions from the pins and send them to our 'model' + self.stream_in_recovered = AvalonSTMonitor(dut, "stream_in", dut.clk, callback=self.model) + +Finally we create another Monitor instance, this time connected to the **stream_in** interface. This is to reconstruct the transactions being driven into the DUT. It's good practice to use a monitor to reconstruct the transactions from the pin interactions rather than snooping them from a higher abstraction layer as we can gain confidence that our drivers and monitors are functioning correctly. We also pass the keyword argument **callback** to the monitor constructor which will result in the supplied function being called for each transaction seen on the bus with the transaction as the first argument. Our model function is quite straightforward in this case - we simply append the transaction to the expected output list and increment a counter: + +.. code-block:: python + + def model(self, transaction): + """Model the DUT based on the input transaction""" + self.expected_output.append(transaction) + self.pkts_sent += 1 + From 9316464091b2d6b0156085f88963f429930e2b00 Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 18 Dec 2013 11:28:20 +0000 Subject: [PATCH 0431/2656] Fixes #110: Default to "No docstring supplied" in the test description --- cocotb/regression.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 1f8bacb5..a6a4b07b 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -317,7 +317,9 @@ def generate_tests(self): for optname, optvalue in testoptions.iteritems(): if callable(optvalue): - doc += "\t%s: %s (%s)\n" % (optname, optvalue.__name__, optvalue.__doc__.split('\n')[0]) + if not optvalue.__doc__: desc = "No docstring supplied" + else: desc = optvalue.__doc__.split('\n')[0] + doc += "\t%s: %s (%s)\n" % (optname, optvalue.__name__, desc) else: doc += "\t%s: %s\n" % (optname, repr(optvalue)) From 8a2a85c9ed79677ba9de2ecfcd5b89de4c261fd2 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 19 Dec 2013 10:10:59 +0000 Subject: [PATCH 0432/2656] Tweak to coverage collection to workaround Aldec bug --- makefiles/simulators/Makefile.aldec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index c8d1a8dc..588ed044 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -41,7 +41,7 @@ ALOG_ARGS += "+define+COCOTB_SIM -dbg" ifeq ($(COVERAGE),1) ASIM_ARGS += "-acdb" - ALOG_ARGS += "-coverage sbe" + ALOG_ARGS += "-coverage sb" endif From a9bea36a357ae99677e1b06ff1dc949fc3460d7f Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 19 Dec 2013 17:32:48 +0000 Subject: [PATCH 0433/2656] Update coverage for Aldec makefile --- makefiles/simulators/Makefile.aldec | 1 + 1 file changed, 1 insertion(+) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 588ed044..a319e612 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -60,6 +60,7 @@ else endif ifeq ($(COVERAGE),1) echo "acdb report -db work.acdb -html -o coverage/acdb_report.html" >> $@ + echo "acdb report -db work.acdb -txt -o coverage/acdb_report.txt" >> $@ endif # Note it's the redirection of the output rather than the 'do' command From 4a76d52b3f662ed11b209856ecd9f1ef759100f9 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 24 Dec 2013 09:27:26 +0000 Subject: [PATCH 0434/2656] Closes #111: Implementation of XGMII driver and monitor --- cocotb/drivers/xgmii.py | 227 +++++++++++++++++++++++++++++++++++++++ cocotb/monitors/xgmii.py | 179 ++++++++++++++++++++++++++++++ 2 files changed, 406 insertions(+) create mode 100644 cocotb/drivers/xgmii.py create mode 100644 cocotb/monitors/xgmii.py diff --git a/cocotb/drivers/xgmii.py b/cocotb/drivers/xgmii.py new file mode 100644 index 00000000..ab783d3f --- /dev/null +++ b/cocotb/drivers/xgmii.py @@ -0,0 +1,227 @@ +''' Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +""" +Drivers for XGMII +""" + +import struct +import zlib + +import cocotb +from cocotb.triggers import RisingEdge +from cocotb.drivers import Driver +from cocotb.utils import hexdump +from cocotb.binary import BinaryValue + +_XGMII_IDLE = "\x07" +_XGMII_START = "\xFB" +_XGMII_TERMINATE= "\xFD" + +# Preamble is technically supposed to be 7 bytes of 0x55 but it seems that it's +# permissible for the start byte to replace one of the preamble bytes +_PREAMBLE_SFD = "\x55\x55\x55\x55\x55\x55\xD5" + +class _XGMIIBus(object): + """ + Helper object for abstracting the underlying bus format + + Index bytes directly on this object, pass a tuple of (value, ctrl) to + set a byte. + + For example: + + >>> xgmii = _XGMIIBus(4) + >>> xgmii[0] = (_XGMII_IDLE, True) # Control byte + >>> xgmii[1] = ("\x55", False) # Data byte + """ + + def __init__(self, nbytes, interleaved=True): + """ + Args: + nbytes (int): The number of bytes transferred per clock cycle + (usually 8 for SDR, 4 for DDR) + + Kwargs: + interleaved (bool): The arrangement of control bits on the bus. + + If interleaved we have a bus with 9-bits per + byte, the control bit being the 9th bit of each + byte. + + If not interleaved then we have a byte per data + byte plus a control bit per byte in the MSBs. + """ + + self._value = BinaryValue(bits=nbytes*9, bigEndian=False) + self._integer = long(0) + self._interleaved = interleaved + self._nbytes = nbytes + + # Default to idle + for i in range(nbytes): + self[i] = (0x07, True) + + def __setitem__(self, index, value): + byte, ctrl = value + + if isinstance(byte, str): + byte = ord(byte) + + if index >= self._nbytes: + raise IndexError("Attempt to access byte %d of a %d byte bus" % ( + index, self._nbytes)) + + if self._interleaved: + self._integer |= (byte << (index * 9)) + self._integer |= (int(ctrl) << (9*index + 8)) + else: + self._integer |= (byte << (index * 8)) + self._integer |= (int(ctrl) << (self._nbytes*8 + index)) + + self._value.integer = self._integer + + + @property + def value(self): + """ + Get the integer representation of this data word suitable for driving + onto the bus. + + NB clears the value + """ + self._value.integer = self._integer + self._integer = long(0) + print "self._integer is 0" + return self._value + + def __len__(self): + return self._nbytes + + +class XGMII(Driver): + """ + XGMII driver + """ + + def __init__(self, signal, clock, interleaved=True): + """ + Args: + signal (SimHandle): The xgmii data bus + + clock (SimHandle): The associated clock (assumed to be + driven by another coroutine) + + Kwargs: + interleaved (bool: Whether control bits are interleaved + with the data bytes or not. + + If interleaved the bus is + byte0, byte0_control, byte1, byte1_control .... + + Otherwise expect: + + byte0, byte1, ..., byte0_control, byte1_control... + """ + self.log = signal.log + self.signal = signal + self.clock = clock + self.bus = _XGMIIBus(len(signal)/9, interleaved=interleaved) + Driver.__init__(self) + + @staticmethod + def layer1(packet): + """Take an Ethernet packet (as a string) and format as a layer 1 packet + + Pads to 64-bytes, prepends preamble and appends 4-byte CRC on the end + """ + if len(packet) < 60: + padding = "\x00" * (60 - len(packet)) + packet += padding + return _PREAMBLE_SFD + packet + struct.pack("> (i*byte_shift)) & 0xff)) + ctrls.append(bool(value & (1<<(ctrl_base)))) + ctrl_base += ctrl_inc + + return ctrls, bytes + + + def _add_payload(self, ctrl, bytes): + """Take the payload and return true if more to come""" + for index, byte in enumerate(bytes): + if ctrl[index]: + if byte != _XGMII_TERMINATE: + self.log.error("Got control character in XGMII payload") + self.log.info("data = :" + " ".join(["%02X" % ord(b) for b in bytes])) + self.log.info("ctrl = :" + " ".join(["%s" % str(c) for c in ctrl])) + self._pkt = "" + return False + + self._pkt += byte + return True + + + @cocotb.coroutine + def _monitor_recv(self): + clk = RisingEdge(self.clock) + self._pkt = "" + + while True: + yield clk + ctrl, bytes = self._get_bytes() + + if ctrl[0] and bytes[0] == _XGMII_START: + + ctrl, bytes = ctrl[1:], bytes[1:] + + while self._add_payload(ctrl, bytes): + yield clk + ctrl, bytes = self._get_bytes() + + if self._pkt: + + self.log.debug("Received:\n%s" % (hexdump(self._pkt))) + + if len(self._pkt) < 64 + 7: + self.log.error("Received a runt frame!") + if len(self._pkt) < 12: + self.log.error("No data to extract") + self._pkt = "" + continue + + preamble_sfd = self._pkt[0:7] + crc32 = self._pkt[-4:] + payload = self._pkt[7:-4] + + if preamble_sfd != _PREAMBLE_SFD: + self.log.error("Got a frame with unknown preamble/SFD") + self.log.error(hexdump(preamble_sfd)) + self._pkt = "" + continue + + expected_crc = struct.pack(" Date: Tue, 24 Dec 2013 09:45:40 +0000 Subject: [PATCH 0435/2656] Ensure COCOTB is set so endian_swapper example can run in isolation --- examples/endian_swapper/tests/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index 28a2c338..af744a16 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -31,7 +31,7 @@ TOPLEVEL = endian_swapper PWD=$(shell pwd) - +COCOTB=$(PWD)/../../.. VERILOG_SOURCES = $(PWD)/../hdl/endian_swapper.sv MODULE=test_endian_swapper From a9f1dd9e5d01d5e4eaf71171203e82998659c784 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 24 Dec 2013 09:53:29 +0000 Subject: [PATCH 0436/2656] Initial work on issue #112 * Fix Python libs makefile for cross compile * Create Makefile.modelsim so now can run example using make SIM=modelsim --- makefiles/Makefile.pylib | 12 +++++-- makefiles/simulators/Makefile.modelsim | 46 ++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 makefiles/simulators/Makefile.modelsim diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib index 23876252..23fafe79 100644 --- a/makefiles/Makefile.pylib +++ b/makefiles/Makefile.pylib @@ -31,11 +31,19 @@ PYTHON_PREFIX = $(shell python-config --prefix) +NATIVE_ARCH=$(shell uname -m) + +ifeq ($(NATIVE_ARCH),$(ARCH)) + STD_LIB=True +else + STD_LIB=False +endif + # We might work with other Python versions PYTHON_VERSION?=$(shell python -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_version()') -PYTHON_LIBDIR:=$(shell python -c 'import os.path,distutils.sysconfig;print os.path.sep+os.path.join(*list(distutils.sysconfig.get_python_lib(standard_lib=True).split(os.path.sep)[:-1]))') +PYTHON_LIBDIR:=$(shell python -c 'import os.path,distutils.sysconfig;print os.path.sep+os.path.join(*list(distutils.sysconfig.get_python_lib(standard_lib=$(STD_LIB)).split(os.path.sep)[:-1]))') -PYTHON_DYNLIBDIR:=$(PYTHON_LIBDIR)/python$(PYTHON_VERSION)/lib-dynload +PYTHON_DYNLIBDIR:=$(PYTHON_LIBDIR)/lib-dynload # Since we don't know which modules we might need or whether the simulator # we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim new file mode 100644 index 00000000..649a4bdd --- /dev/null +++ b/makefiles/simulators/Makefile.modelsim @@ -0,0 +1,46 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +VPI_LIB := vpi + +# Modelsim is 32-bit only +ARCH:=i686 + + +sim: + -@rm -rf results.xml + $(MAKE) results.xml + +results.xml: $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) libs + vlib work + vlog -writetoplevels modelsim.tops '-timescale' '1ns/1ns' '-mfcu' '+acc=rmb' '-sv' $(VERILOG_SOURCES) + LD_LIBRARY_PATH=$(LIB_DIR): MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ + vsim -c -do "onElabError resume; run -all; exit" -f modelsim.tops -pli libgpi.so 2>&1 | tee sim.log + From b713edae765f43583fa881a3b2cbb09eb5b9e2e9 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Mon, 30 Dec 2013 08:35:05 +0000 Subject: [PATCH 0437/2656] Fixes #84 and closes #24: Tidy up of makefiles * fix rules so "make regression" works * Harmonise use of CUSTOM_SIM_DEPS and CUSTOM_COMPILE_DEPS * Remove 'gdb' rules --- lib/Makefile | 20 ++++++++-------- makefiles/Makefile.sim | 17 +++++++++++++- makefiles/simulators/Makefile.aldec | 10 +++----- makefiles/simulators/Makefile.icarus | 18 ++++----------- makefiles/simulators/Makefile.ius | 7 +----- makefiles/simulators/Makefile.questa | 3 +-- makefiles/simulators/Makefile.vcs | 34 ++++++---------------------- 7 files changed, 44 insertions(+), 65 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index c76657d1..0bbe8ba8 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -29,29 +29,31 @@ include $(SIM_ROOT)/makefiles/Makefile.inc +# FIXME it's a bit nasty to have the source files listed here as dependencies +# but better than re-building the libraries every time. + $(LIB_OBJ_DIR): mkdir -p $@ $(LIB_DIR): $(LIB_OBJ_DIR) mkdir -p $@ -$(LIB_DIR)/libgpilog.so: +$(LIB_DIR)/libgpilog.so: $(SIM_ROOT)/lib/gpi/gpi_logging.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/gpi -$(LIB_DIR)/libcocotb.so: +$(LIB_DIR)/libcocotb.so: $(SIM_ROOT)/lib/embed/gpi_embed.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/embed -$(LIB_DIR)/libgpi.so: +$(LIB_DIR)/libgpi.so: $(SIM_ROOT)/lib/vpi_shim/gpi_vpi.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/vpi_shim -$(LIB_DIR)/libsim.so: +$(LIB_DIR)/libsim.so: $(SIM_ROOT)/lib/simulator/simulatormodule.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/simulator -libs: $(LIB_DIR) \ - $(LIB_DIR)/libgpilog.so \ - $(LIB_DIR)/libcocotb.so \ - $(LIB_DIR)/libgpi.so \ - $(LIB_DIR)/libsim.so +COCOTB_LIBS = $(LIB_DIR)/libgpilog.so \ + $(LIB_DIR)/libcocotb.so \ + $(LIB_DIR)/libgpi.so \ + $(LIB_DIR)/libsim.so clean:: -@rm -rf $(BUILD_DIR) diff --git a/makefiles/Makefile.sim b/makefiles/Makefile.sim index 6cb801af..7d50127a 100644 --- a/makefiles/Makefile.sim +++ b/makefiles/Makefile.sim @@ -28,8 +28,9 @@ ############################################################################### # This is a simple wrapper Makefile to pull in the appropriate Makefile for -# the desired simulator and set up common paths for the sims to use to build in +# the desired simulator and set up common paths for the sims to use to build with +all: sim SIM_BUILD ?= sim_build export SIM_BUILD @@ -47,6 +48,9 @@ ifeq ($(HAVE_SIMULATOR),0) $(error "Couldn't find makefile for simulator: "$(SIM_LOWERCASE)"! Available simualtors: $(AVAILABLE_SIMULATORS)") endif +# We want to include the Python files from Cocotb in the dependencies +CUSTOM_SIM_DEPS += $(shell find $(SIM_ROOT)/cocotb/ -name "*.py") + include $(SIM_ROOT)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) include $(SIM_ROOT)/lib/Makefile @@ -55,3 +59,14 @@ export VERSION $(SIM_BUILD): mkdir -p $@ + +# Regression rule uses Make dependencies to determine whether to run the simulation +.PHONY: regression +regression: results.xml + +# Default sim rule will force a re-run of the simulation (though the cocotb library +# and RTL compilation phases are still evaluated by makefile dependencies) +.PHONY: sim +sim: + -@rm results.xml + $(MAKE) results.xml diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index a319e612..bc09b78d 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -27,8 +27,6 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -all : sim - # Common Makefile for Aldec Riviera-PRO simulator ifeq ($(GUI),1) @@ -65,11 +63,9 @@ endif # Note it's the redirection of the output rather than the 'do' command # that turns on batch mode (i.e. exit on completion/error) -.PHONY: sim - -sim: $(SIM_BUILD)/runsim.tcl libs - cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ - LD_LIBRARY_PATH=$(LIB_DIR):$(VSIMSABIN)/Linux64 MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do runsim.tcl | tee sim.log +results.xml: $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(CUSTOM_SIM_DEPS) + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ + LD_LIBRARY_PATH=$(LIB_DIR):$(VSIMSABIN)/Linux64 MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do $(SIM_BUILS)/runsim.tcl | tee $(SIM_BUILD)/sim.log clean:: rm -rf $(SIM_BUILD) diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index ceb5e304..85e187f3 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -27,23 +27,15 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -VPI_LIB := vpi - -all: $(SIM_BUILD) sim - -.PHONY: sim -sim: $(SIM_BUILD) $(VERILOG_SOURCES) libs +# Compilation phase +$(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) iverilog -o $(SIM_BUILD)/sim.vvp -D COCOTB_SIM=1 $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) -.PHONY: gdb -gdb: $(SIM_BUILD) $(VERILOG_SOURCES) libs - iverilog -o $(SIM_BUILD)/sim.vvp -D COCOTB_SIM=1 $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) +# Execution phase +results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - gdb --args vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) + vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) clean:: -@rm -rf $(SIM_BUILD) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index ac6441e5..6fa28585 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -27,12 +27,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -VPI_LIB := vpi - -all: $(SIM_BUILD) sim - -.PHONY: sim -sim: $(SIM_BUILD) $(VERILOG_SOURCES) libs $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) +results.xml: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ irun -sv $(EXTRA_ARGS) -64bit -loadvpi $(BUILD_DIR)/libgpi:vlog_startup_routines_bootstrap +access+rwc $(VERILOG_SOURCES) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index da0c91d3..a6dbc754 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -27,13 +27,12 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -VPI_LIB := vpi VPI_FILE := $(LIB_DIR)/libgpi.so all: $(SIM_BUILD) sim .PHONY: sim -sim: $(SIM_BUILD) $(VERILOG_SOURCES) libs $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) +sim: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(PYTHON_LIBDIR):$(PYTHON_DYNLIBDIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ qverilog -64 -R -pli $(VPI_FILE) - +acc $(EXTRA_ARGS) $(VERILOG_SOURCES) diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index df21458d..2d1edf41 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -27,16 +27,10 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -VPI_LIB := vpi - ifeq ($(ARCH),x86_64) EXTRA_ARGS += -full64 endif -sim: - -@rm -rf results.xml - $(MAKE) results.xml - # TODO: # investigate +vpi+1 option which reduces memory requirements @@ -45,32 +39,17 @@ sim: $(SIM_BUILD)/pli.tab : echo "acc+=rw,wn:*" > $@ -.PHONY: regression sim - -regression: build_simv results.xml - -MODULE_FILES:=$(shell for X in $(echo "test1,test2" | sed 's/,/ /g'); do echo $X.py; done) -PYTHON_FILES:=$(shell find $(SIM_ROOT)/cocotb/ -name "*.py" | xargs) $(MODULE_FILES) +# Compilation phase +$(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(COCOTB_LIBS) $(CUSTOM_COMPILE_DEPS) + cd $(SIM_BUILD) && \ + LD_LIBRARY_PATH=$(LIB_DIR) TOPLEVEL=$(TOPLEVEL) \ + vcs +acc+1 +vpi -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -debug -load libgpi.so $(COMPILE_ARGS) $(VERILOG_SOURCES) +# Execution phase results.xml: $(SIM_BUILD)/simv $(PYTHON_FILES) $(CUSTOM_SIM_DEPS) -PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ $(SIM_BUILD)/simv +define+COCOTB_SIM=1 $(SIM_ARGS) $(EXTRA_ARGS) -gdb: $(SIM_BUILD)/simv $(PYTHON_FILES) $(CUSTOM_SIM_DEPS) - -PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - gdb --args $(SIM_BUILD)/simv +define+COCOTB_SIM=1 $(SIM_ARGS) $(EXTRA_ARGS) - -dve: $(SIM_BUILD)/simv_dve $(CUSTOM_COMPILE_DEPS) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - vcs +acc+1 +vpi+1+assertion -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -gui -load libgpi.so $(COMPILE_ARGS) $(VERILOG_SOURCES) - -.PHONY: build_simv -build_simv: $(SIM_BUILD)/simv - -$(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab libs $(CUSTOM_COMPILE_DEPS) - cd $(SIM_BUILD) && \ - LD_LIBRARY_PATH=$(LIB_DIR) TOPLEVEL=$(TOPLEVEL) \ - vcs +acc+1 +vpi -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -debug -load libgpi.so $(COMPILE_ARGS) $(VERILOG_SOURCES) .PHONY: clean clean:: @@ -79,3 +58,4 @@ clean:: -@rm -rf cm.log -@rm -rf results.xml -@rm -rf ucli.key + From 98d6971bd85ac3463fa1f2076f6e790790cdd7d2 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Mon, 30 Dec 2013 08:39:26 +0000 Subject: [PATCH 0438/2656] Issue #9: Tweaks to documentation --- documentation/source/building.rst | 27 +++++++++-- documentation/source/installation.rst | 4 +- documentation/source/introduction.rst | 67 +++++++++++++++------------ 3 files changed, 62 insertions(+), 36 deletions(-) diff --git a/documentation/source/building.rst b/documentation/source/building.rst index ece1c2af..aacd215c 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -7,21 +7,28 @@ Make System Makefiles are provided for a variety of simulators in cocotb/makefiles/simulators. The common Makefile cocotb/makefiles/Makefile.sim includes the appropriate simulator makefile based on the contents of the SIM variable. -Targets -------- +Make Targets +------------ Makefiles define two targets, 'regression' and 'sim', the default target is sim. Both rules create a results file in the calling directory called 'results.xml'. This file is a JUnit-compatible output file suitable for use with `Jenkins `_. The 'sim' targets unconditionally re-runs the simulator whereas the regression target only re-builds if any dependencies have changed. -Variables ---------- +Make phases +----------- + +Typically the makefiles provided with Cocotb for various simulators use a separate *compile* and *run* target. This allows for a rapid re-running of a simulator if none of the RTL source files have changed and therefore the simulator does not need to recompile the RTL. + + + +Make Variables +-------------- SIM ~~~ -Selects which simulator Makefile to use. Attempts to include cocotb/makefiles/makefile.$(SIM) +Selects which simulator Makefile to use. Attempts to include a simulator specific makefile from cocotb/makefiles/makefile.$(SIM) VERILOG_SOURCES @@ -46,6 +53,15 @@ EXTRA_ARGS Passed to both the compile and execute phases of simulators with two rules, or passed to the single compile and run commad for simulators which don't have a distinct compilation stage. +CUSTOM_COMPILE_DEPS +~~~~~~~~~~~~~~~~~~~ + +Use to add additional dependencies to the compilation target; useful for defining additional rules to run pre-compilation or if the compilation phase depends on files other than the RTL sources listed in **VERILOG_SOURCES**. + +CUSTOM_SIM_DEPS +~~~~~~~~~~~~~~~ + +Use to add additional dependencies to the simulation target. Environment Variables @@ -101,3 +117,4 @@ The name of the test function(s) to run. If this variable is not defined cocotb Multiple functions can be specified in a comma-separated list. + diff --git a/documentation/source/installation.rst b/documentation/source/installation.rst index 10a6ebef..94e874ac 100644 --- a/documentation/source/installation.rst +++ b/documentation/source/installation.rst @@ -5,7 +5,7 @@ Installation Get the Source ============== -Source can be obtained as a tar ball for the current `release `_. +Source can be obtained as a tar ball for the current `release `_. Or by cloning the repository `git@github.com:potentialventures/cocotb.git` @@ -21,4 +21,4 @@ The list of supported simulators for the version you have can be found by *make Centralised Usage ================= -A build can be installed in a centralised location with *make install INSTALL_PATH=
      ". This will also generate and uninstall script. +A build can be installed in a centralised location with *make install INSTALL_PATH=*. This will also generate an uninstall script. diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index 2e682746..6881a0d0 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -9,7 +9,7 @@ What is cocotb? **Cocotb** is completely free, open source (under the `BSD License `_) and hosted on `GitHub `_. -**Cocotb** still requires a simulator to simulate the RTL. Simulators that have been tested and known to work with cocotb: +**Cocotb** requires a simulator to simulate the RTL. Simulators that have been tested and known to work with cocotb: * Icarus Verilog * Synopsys VCS @@ -17,7 +17,8 @@ What is cocotb? * Mentor Questa * Cadance Incisive -**Cocotb** has recently been developed with the support of `SolarFlare Communications Ltd `_ for use on thier `AOE product line `_. +**Cocotb** was developed by Potential Ventures with the support of `Solarflare Communications Ltd `_ and contributions from Gordon McGreggor and Finn Grimwood. + How is cocotb different? @@ -25,20 +26,13 @@ How is cocotb different? Cocotb encourages the same philosophy of design re-use and randomised testing as UVM, however is implemented in Python rather than SystemVerilog. +In cocotb VHDL/Verilog/SystemVerilog are only used for the synthesisable design. All verification is done using Python which is ideal for rapid development of complex systems and integrating multiple languages. Using Python has various advantages over using SystemVerilog or VHDL for verification: -The right tool for the job -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -In cocotb VHDL/Verilog/SystemVerilog are only used for the synthesisable design. All verification is done using Python which is ideal for rapid development of complex systems and integrating multiple languages. - +* Writing Python is **fast** - it's a very productive language * It's **easy** to interface to other languages from Python -* Python has a huge library of existing code to **re-use** like `constraint solvers `_ and `packet parsing/generation `_ libraries. -* Python is **interpreted**. Tests can be edited and re-run them without having to recompile the design or even exit the simulator GUI. -* Writing Python is **fast**, it's *easy to understand* and *everybody* knows it. - -.. note:: - - That last statement me be a slight exaggeration, however a brief peruse of `Google Trends `_ suggests that compared to UVM, everybody knows Python. +* Python has a huge library of existing code to **re-use** like `packet parsing/generation `_ libraries. +* Python is **interpreted**. Tests can be edited and re-run them without having to recompile the design or exit the simulator GUI. +* Python is **popular** - far more engineers know Python than SystemVerilog or VHDL Lower the overhead of creating a test @@ -53,38 +47,53 @@ Open verification to a wider audience ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ASIC/FPGA designs always involve some software development. -Often the hardware team is responsible for creating and verifying the RTL with little interaction -with the software team although as the boundaries blur the software teams are tending to get more involved (often with exclamations of `WTF?! `_). -Cocotb encourages more people to get more involved in RTL design by lowering the barrier to entry. Python is a common language accessible to a larger audience. +Often the hardware team is responsible for creating and verifying the RTL with little interaction between them and the software team. As the boundaries blur the software teams becoming more involved in the RTL and Cocotb encourages this by lowering the barrier to entry. Python is a common language accessible to a larger audience providing a common ground for RTL, verification and software developers. How should I use cocotb? -======================== +------------------------ + +Verifying using Cocotb is similar to any other verification environment and the usual best-practice advice applies: -Utilise the simulator features ------------------------------- -If you are paying for a simulator, make the most of all the fucntionlity it provides. Use metrics to asses how verification is progressing. It's surprisingly easy to write a test that doesn't actually test anything. -* Code coverage -* Functional coverage -* Assersions +Utilise the simulator features +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you are paying for a simulator, make the most of all the fucntionlity it provides. Use metrics to asses how verification is progressing. It's surprisingly easy to write a test that doesn't actually test anything. Use *code coverage* and *functional coverage* to track verification progress, embed assersions to validate behaviour. Structure your testbench ------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^ + +Cocotb provides base classes for `Drivers`_, `Monitors`_ and `Scoreboards`_. Any moderately complex testbench should build on these abstractions. + +Drivers provide an mechanism for driving a transaction onto a bus in the simulator by waggling pins typically over one or more clock cycles. + +Monitors perform the inverse function of drivers, reconstructing transactions by observing bus behaviour. + +A scoreboard is a way to validate that the DUT is behaving correctly (in addition to assertions and protocol checkers in any bus models). Typically this is by comparing the transactions seen by the monitors with expected output based on a software model. -Drivers, Monitors, Scoreboards. +Write directed and randomised tests +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Make use of both directed and randomised tests ----------------------------------------------- +Directed tests are very useful in the initial phases of development and when attempting to recreate particular bugs or behaviour. Cocotb encourages the use of short directed tests forming a regression since all tests are automatically included in a regression using test auto-discovery. + + +Test Driven Development +^^^^^^^^^^^^^^^^^^^^^^^ Use a regression system ------------------------ +^^^^^^^^^^^^^^^^^^^^^^^ `Jenkins `_ +Getting Started +=============== + +**Cocotb** can be used live in a web-browser on the excellent `EDA Playground `_. + From 3e7e7cf6ad8ced18be9161e7b1b02d915e2b78d1 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 30 Dec 2013 09:41:04 +0000 Subject: [PATCH 0439/2656] Correct makefile rules from previous commit --- makefiles/Makefile.sim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/Makefile.sim b/makefiles/Makefile.sim index 7d50127a..9d7c195a 100644 --- a/makefiles/Makefile.sim +++ b/makefiles/Makefile.sim @@ -51,8 +51,8 @@ endif # We want to include the Python files from Cocotb in the dependencies CUSTOM_SIM_DEPS += $(shell find $(SIM_ROOT)/cocotb/ -name "*.py") -include $(SIM_ROOT)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) include $(SIM_ROOT)/lib/Makefile +include $(SIM_ROOT)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) include $(SIM_ROOT)/version export VERSION From 3869d246db44c9c68ff9f91b1424842861348aa4 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 30 Dec 2013 09:47:43 +0000 Subject: [PATCH 0440/2656] Correct typo in Aldec simulator Makefile --- makefiles/simulators/Makefile.aldec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index bc09b78d..4bb9dad4 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -65,7 +65,7 @@ endif # that turns on batch mode (i.e. exit on completion/error) results.xml: $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(CUSTOM_SIM_DEPS) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ - LD_LIBRARY_PATH=$(LIB_DIR):$(VSIMSABIN)/Linux64 MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do $(SIM_BUILS)/runsim.tcl | tee $(SIM_BUILD)/sim.log + LD_LIBRARY_PATH=$(LIB_DIR):$(VSIMSABIN)/Linux64 MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do $(SIM_BUILD)/runsim.tcl | tee $(SIM_BUILD)/sim.log clean:: rm -rf $(SIM_BUILD) From 9f2e41be5fbfb902d7c766369adef8323931d457 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 30 Dec 2013 10:56:21 +0000 Subject: [PATCH 0441/2656] Added comment about preamble length on XGMII --- cocotb/drivers/xgmii.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cocotb/drivers/xgmii.py b/cocotb/drivers/xgmii.py index ab783d3f..e7b8a1d9 100644 --- a/cocotb/drivers/xgmii.py +++ b/cocotb/drivers/xgmii.py @@ -42,6 +42,7 @@ # Preamble is technically supposed to be 7 bytes of 0x55 but it seems that it's # permissible for the start byte to replace one of the preamble bytes +# see http://grouper.ieee.org/groups/802/3/10G_study/email/msg04647.html _PREAMBLE_SFD = "\x55\x55\x55\x55\x55\x55\xD5" class _XGMIIBus(object): From 5012948938b919a5e959a12247f74b4c60b8afb2 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 31 Dec 2013 16:14:57 +0000 Subject: [PATCH 0442/2656] Cleanup: Remove leftover print statement --- cocotb/drivers/xgmii.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cocotb/drivers/xgmii.py b/cocotb/drivers/xgmii.py index e7b8a1d9..0c545fc8 100644 --- a/cocotb/drivers/xgmii.py +++ b/cocotb/drivers/xgmii.py @@ -115,7 +115,6 @@ def value(self): """ self._value.integer = self._integer self._integer = long(0) - print "self._integer is 0" return self._value def __len__(self): From f2795633b72be41714664098569f3277607fac9c Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 2 Jan 2014 10:39:07 +0000 Subject: [PATCH 0443/2656] Preserve LD_LIBRARY_PATH in Aldec simulator --- makefiles/simulators/Makefile.aldec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 4bb9dad4..bc655769 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -65,7 +65,7 @@ endif # that turns on batch mode (i.e. exit on completion/error) results.xml: $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(CUSTOM_SIM_DEPS) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ - LD_LIBRARY_PATH=$(LIB_DIR):$(VSIMSABIN)/Linux64 MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do $(SIM_BUILD)/runsim.tcl | tee $(SIM_BUILD)/sim.log + LD_LIBRARY_PATH=$(LIB_DIR):$(VSIMSABIN)/Linux64:$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do $(SIM_BUILD)/runsim.tcl | tee $(SIM_BUILD)/sim.log clean:: rm -rf $(SIM_BUILD) From 1bd4c9c46488147a6d22e859437f3553922e4045 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sat, 4 Jan 2014 16:35:32 +0000 Subject: [PATCH 0444/2656] Update introduction.rst --- documentation/source/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index 6881a0d0..15a0e455 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -17,7 +17,7 @@ What is cocotb? * Mentor Questa * Cadance Incisive -**Cocotb** was developed by Potential Ventures with the support of `Solarflare Communications Ltd `_ and contributions from Gordon McGreggor and Finn Grimwood. +**Cocotb** was developed by Potential Ventures with the support of `Solarflare Communications Ltd `_ and contributions from Gordon McGregor and Finn Grimwood. From 2ad1c71df805632042becaec21c132244e5f7627 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 5 Jan 2014 09:35:40 +0000 Subject: [PATCH 0445/2656] Cleanup: Set COCOTB variable in ping Makefile --- examples/ping_tun_tap/tests/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/ping_tun_tap/tests/Makefile b/examples/ping_tun_tap/tests/Makefile index b14f9307..ae619b67 100644 --- a/examples/ping_tun_tap/tests/Makefile +++ b/examples/ping_tun_tap/tests/Makefile @@ -30,6 +30,7 @@ TOPLEVEL = icmp_reply PWD=$(shell pwd) +COCOTB=$(PWD)/../../.. VERILOG_SOURCES = $(PWD)/../hdl/icmp_reply.sv MODULE=test_icmp_reply From ade12daed6c440943b9bf26310e48a0225b6410b Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 5 Jan 2014 09:47:18 +0000 Subject: [PATCH 0446/2656] Issue #9 and issue #77: Add documentation for ping_tun_tap example --- .../source/diagrams/svg/ping_tun_tap.svg | 1 + .../source/diagrams/xml/ping_tun_tap.xml | 1 + .../{tutorials.rst => endian_swapper.rst} | 8 +- documentation/source/index.rst | 3 +- documentation/source/ping_tun_tap.rst | 154 ++++++++++++++++++ 5 files changed, 160 insertions(+), 7 deletions(-) create mode 100644 documentation/source/diagrams/svg/ping_tun_tap.svg create mode 100644 documentation/source/diagrams/xml/ping_tun_tap.xml rename documentation/source/{tutorials.rst => endian_swapper.rst} (98%) create mode 100644 documentation/source/ping_tun_tap.rst diff --git a/documentation/source/diagrams/svg/ping_tun_tap.svg b/documentation/source/diagrams/svg/ping_tun_tap.svg new file mode 100644 index 00000000..a602a447 --- /dev/null +++ b/documentation/source/diagrams/svg/ping_tun_tap.svg @@ -0,0 +1 @@ +
      ping
      [Object]
      TUN virtual interface
      [Object]
      File descriptor
      [Object]
      read()write()
      Driver
      [Object]
      Monitor
      [Object]
      DUT
      [Object]
      send()wait_for_recv()SimulatorLinuxTestbench
      \ No newline at end of file diff --git a/documentation/source/diagrams/xml/ping_tun_tap.xml b/documentation/source/diagrams/xml/ping_tun_tap.xml new file mode 100644 index 00000000..f6633699 --- /dev/null +++ b/documentation/source/diagrams/xml/ping_tun_tap.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/documentation/source/tutorials.rst b/documentation/source/endian_swapper.rst similarity index 98% rename from documentation/source/tutorials.rst rename to documentation/source/endian_swapper.rst index dbed9bee..5652f8ee 100644 --- a/documentation/source/tutorials.rst +++ b/documentation/source/endian_swapper.rst @@ -1,10 +1,6 @@ -######### -Tutorials -######### - -Endian Swapper -============== +Tutorial: Endian Swapper +======================== In this tutorial we'll use some of the built-in features of Cocotb to quickly create a complex testbench. diff --git a/documentation/source/index.rst b/documentation/source/index.rst index f35749e9..65732474 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -17,7 +17,8 @@ Contents: building coroutines triggers - tutorials + endian_swapper + ping_tun_tap roadmap simulator_support diff --git a/documentation/source/ping_tun_tap.rst b/documentation/source/ping_tun_tap.rst new file mode 100644 index 00000000..5c0ce58a --- /dev/null +++ b/documentation/source/ping_tun_tap.rst @@ -0,0 +1,154 @@ +Tutorial: Ping +============== + +One of the benefits of Python is the ease with which interfacing is possible. +In this tutorial we'll look at interfacing the standard GNU **ping** command +to the simulator. Using Python we can **ping** out DUT in fewer than 100 lines +of code. + +For the impatient this an example is provided with Cocotb and you can run this +example thusly: + +.. code-block:: + + cd examples/ping_tun_tap/tests + sudo make + + +.. note:: To create a virtual interface the test either needs root permissions or have CAP_NET_ADMIN capability. + + +Architecture +------------ + +We have a simple RTL block that takes ICMP echo requests and generates an ICMP +echo response. To verify this behaviour we want to run `ping`_ utility +against our RTL running in the simulator. + +In order to achieve this we need to capture the packets that are created by +ping, drive them onto the pins of our DUT in simulation, monitor the output of +the DUT and send any responses back to the ping process. + +Linux has a `TUN/TAP`_ virtual network device which we can use for this +purpose, allowing `ping`_ to run unmodified and unaware that it is +communicating with our simulation rather than a remote network endpoint. + +.. image:: diagrams/svg/ping_tun_tap.svg + + +Implementation +-------------- + +First of all we need to work out how to create a virtual interface. Python has +a huge developer base and a quick search of the web reveals a `TUN example`_ +that looks like an ideal starting point for our testbench. Using this example +we write a function that will create our virtual interface: + +.. code-block:: python + + import subprocess, fcntl, struct + + def create_tun(name="tun0", ip="192.168.255.1"): + TUNSETIFF = 0x400454ca + TUNSETOWNER = TUNSETIFF + 2 + IFF_TUN = 0x0001 + IFF_NO_PI = 0x1000 + tun = open('/dev/net/tun', 'r+b') + ifr = struct.pack('16sH', name, IFF_TUN | IFF_NO_PI) + fcntl.ioctl(tun, TUNSETIFF, ifr) + fcntl.ioctl(tun, TUNSETOWNER, 1000) + subprocess.check_call('ifconfig tun0 %s up pointopoint 192.168.255.2 up' % ip, shell=True) + return tun + +Now we can get started on the actual test. First of all we'll create a clock +signal and connect up the Avalon driver and monitor to the DUT. To help debug +the testbench we'll enable verbose debug on the drivers and monitors by setting +the log level to **logging.DEBUG**. + +.. code-block:: python + + import cocotb + from cocotb.clock import Clock + from cocotb.drivers.avalon import AvalonSTPkts as AvalonSTDriver + from cocotb.monitors.avalon import AvalonSTPkts as AvalonSTMonitor + + @cocotb.test() + def tun_tap_example_test(dut): + cocotb.fork(Clock(dut.clk, 5000).start()) + + stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) + stream_out = AvalonSTMonitor(dut, "stream_out", dut.clk) + + # Enable verbose logging on the streaming interfaces + stream_in.log.setLevel(logging.DEBUG) + stream_out.log.setLevel(logging.DEBUG) + + +We also need to reset the DUT and drive some default values onto some of the +bus signals. Note that we'll need to import the **Timer** and **RisingEdge** +triggers. + +.. code-block:: python + + # Reset the DUT + dut.log.debug("Resetting DUT") + dut.reset_n <= 0 + stream_in.bus.valid <= 0 + yield Timer(10000) + yield RisingEdge(dut.clk) + dut.reset_n <= 1 + dut.stream_out_ready <= 1 + + +The rest of the test becomes fairly straightforward. We create our TUN +interface using our function defined previously. We'll also use the +**subprocess** module to actually start the ping command. + +We then wait for a packet by calling a blocking read call on the TUN file +descriptor and simply append that to the queue on the driver. We wait for +a packet to arrive on the monitor by yielding on wait_for_recv() and then +write the received packet back to the TUN file descriptor. + + +.. code-block:: python + + # Create our interface (destroyed at the end of the test) + tun = create_tun() + fd = tun.fileno() + + # Kick off a ping... + subprocess.check_call('ping -c 5 192.168.255.2 &', shell=True) + + # Respond to 5 pings, then quit + for i in xrange(5): + + cocotb.log.info("Waiting for packets on tun interface") + packet = os.read(fd, 2048) + cocotb.log.info("Received a packet!") + + stream_in.append(packet) + result = yield stream_out.wait_for_recv() + + os.write(fd, str(result)) + + +That's it - simple! + + +Further work +------------ + +This example is deliberately simplistic to focus on the fundamentals of +interfacing to the simulator using TUN/TAP. As an exercise for the reader a +useful addition would be to make the file descriptor non-blocking and spawn +out separate coroutines for the monitor / driver, thus decoupling the sending +and receiving of packets. + + +.. _TUN example: https://gist.github.com/glacjay/585369 + +.. _Ping: http://www.gnu.org/software/inetutils/manual/html_node/ping-invocation.html + +.. _TUN/TAP: http://en.wikipedia.org/wiki/TUN/TAP + + From 7046e019a194befc7adace3d57e5b71d62e85742 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 5 Jan 2014 12:13:03 +0000 Subject: [PATCH 0447/2656] Cleanup: Fix blockquote formatting --- documentation/source/ping_tun_tap.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/documentation/source/ping_tun_tap.rst b/documentation/source/ping_tun_tap.rst index 5c0ce58a..0ed2667c 100644 --- a/documentation/source/ping_tun_tap.rst +++ b/documentation/source/ping_tun_tap.rst @@ -7,9 +7,7 @@ to the simulator. Using Python we can **ping** out DUT in fewer than 100 lines of code. For the impatient this an example is provided with Cocotb and you can run this -example thusly: - -.. code-block:: +example thusly:: cd examples/ping_tun_tap/tests sudo make From 8f7f4975df38eecba1b08fc0ca21bfc5a730df38 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 5 Jan 2014 13:04:56 +0000 Subject: [PATCH 0448/2656] Issue #9: Tweak documentation --- documentation/source/ping_tun_tap.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/documentation/source/ping_tun_tap.rst b/documentation/source/ping_tun_tap.rst index 0ed2667c..f961a0ef 100644 --- a/documentation/source/ping_tun_tap.rst +++ b/documentation/source/ping_tun_tap.rst @@ -2,12 +2,12 @@ Tutorial: Ping ============== One of the benefits of Python is the ease with which interfacing is possible. -In this tutorial we'll look at interfacing the standard GNU **ping** command -to the simulator. Using Python we can **ping** out DUT in fewer than 100 lines -of code. +In this tutorial we'll look at interfacing the standard GNU `ping`_ command +to the simulator. Using Python we can ping our DUT with fewer than 50 lines of +code. -For the impatient this an example is provided with Cocotb and you can run this -example thusly:: +For the impatient this tutorial is provided as an example with Cocotb. You can +run this example from a fresh checkout:: cd examples/ping_tun_tap/tests sudo make From 0fe8d107dffcf71c00efb5f4480533a19118962e Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 8 Jan 2014 13:58:28 +0000 Subject: [PATCH 0449/2656] Cleanup: Aldec - ensure all generated files go into the build directory --- makefiles/simulators/Makefile.aldec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index bc655769..d8f17bfa 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -64,8 +64,8 @@ endif # Note it's the redirection of the output rather than the 'do' command # that turns on batch mode (i.e. exit on completion/error) results.xml: $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(CUSTOM_SIM_DEPS) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ - LD_LIBRARY_PATH=$(LIB_DIR):$(VSIMSABIN)/Linux64:$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do $(SIM_BUILD)/runsim.tcl | tee $(SIM_BUILD)/sim.log + cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ + LD_LIBRARY_PATH=$(LIB_DIR):$(VSIMSABIN)/Linux64:$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do runsim.tcl | tee sim.log clean:: rm -rf $(SIM_BUILD) From 776aaa3cabb1fc51fbbb660e31dbe4c1638fe878 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sat, 11 Jan 2014 09:43:28 +0000 Subject: [PATCH 0450/2656] Cleanup: Tidy up initial test output --- cocotb/__init__.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index e285762d..acf27c8d 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -108,9 +108,9 @@ def _initialise_testbench(root_handle): version = os.getenv('VERSION') if version is None: - log.info("Unable to determine version") + log.info("Unable to determine Cocotb version from %s" % exec_path) else: - log.info("Running tests with CoCoTB-%s from %s" % (version, exec_path)) + log.info("Running tests with Cocotb v%s from %s" % (version, exec_path)) # Create the base handle type dut = cocotb.handle.SimHandle(root_handle) @@ -166,5 +166,3 @@ def process_plusargs(): else: plusargs[option[1:]] = True - print plusargs - From 3b97df1508bd22ab5c55369770da2bb04ae96bfd Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 16 Jan 2014 12:16:14 +0000 Subject: [PATCH 0451/2656] Add very basic protocol check to packetised Avalon-ST monitor --- cocotb/monitors/avalon.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index ff60a000..fdbfb7cf 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -36,6 +36,9 @@ from cocotb.monitors import BusMonitor from cocotb.triggers import RisingEdge, ReadOnly + +class AvalonProtocolError(Exception): pass + class AvalonST(BusMonitor): """ AvalonST bus. @@ -104,6 +107,10 @@ def valid(): if valid(): if self.bus.startofpacket.value: + if pkt: + raise AvalonProtocolError( + "Duplicate start-of-packet received on %s" % ( + str(self.bus.startofpacket))) pkt = "" vec = self.bus.data.value From a8b73f0e20d0c78e1ff0706e0a39873abfffc91d Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 16 Jan 2014 12:17:29 +0000 Subject: [PATCH 0452/2656] More helpful debug message when yielding wrong objects to scheduler --- cocotb/scheduler.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 5ef104d4..268e8e54 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -310,7 +310,10 @@ def schedule(self, coroutine, trigger=None): trigger.addpeers(result) self._add_trigger(trigger, coroutine) else: - raise_error(self, "Coroutine %s yield something that was not a trigger or a coroutine, did you forget to decorate?" % (str(coroutine))) + msg = "Coroutine %s yielded something that was not a trigger or a coroutine!" % str(coroutine) + msg += "\nGot type: %s repr: %s str: %s" % (type(result), repr(result), str(result)) + msg += "\nDid you forget to decorate with @cocotb.cocorutine?" + raise_error(self, msg) # TestComplete indication is game over, tidy up except TestComplete as test_result: From febdfd4236222c95472579618f0d63b85d0a0e8a Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 17 Jan 2014 12:12:53 +0000 Subject: [PATCH 0453/2656] Cleanup: Add WAVES=1 option to Aldec Makefile to create a full database --- makefiles/simulators/Makefile.aldec | 3 +++ 1 file changed, 3 insertions(+) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index d8f17bfa..1b88b1ff 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -50,6 +50,9 @@ $(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(SIM_BUILD) echo -e $(foreach src,$(VERILOG_SOURCES),\ "alog $(ALOG_ARGS) $(src)\n") >> $@ echo "asim $(ASIM_ARGS) +access +w -O2 -dbg -pli libgpi $(TOPLEVEL)" >> $@ +ifeq ($(WAVES),1) + echo "log -recursive *" >> $@ +endif ifeq ($(GUI),1) echo "wave -rec *" >> $@ else From b540a2065cc325def5531073b38487b11718a2cd Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sat, 18 Jan 2014 00:53:19 +0000 Subject: [PATCH 0454/2656] Fix table in endian_swapper documentation, add information about TestFactory --- documentation/source/endian_swapper.rst | 85 +++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 5 deletions(-) diff --git a/documentation/source/endian_swapper.rst b/documentation/source/endian_swapper.rst index 5652f8ee..6fcc8410 100644 --- a/documentation/source/endian_swapper.rst +++ b/documentation/source/endian_swapper.rst @@ -6,6 +6,11 @@ In this tutorial we'll use some of the built-in features of Cocotb to quickly cr .. note:: All the code and sample output from this example are available on `EDA Playground `_ +For the impatient this tutorial is provided as an example with Cocotb. You can run this example from a fresh checkout:: + + cd examples/endian_swapper/tests + make + Design ------ @@ -49,20 +54,20 @@ If we inspect this line-by-line: self.stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) -Here we're creating an AvalonSTDriver instance. The constructor requires 3 arguments - a handle to the entity containing the interface (**dut**), the name of the interface (**stream_in**) and the associated clock with which to drive the interface (**dut.clk**). The driver will auto-discover the signals for the interface, assuming that they follow the following naming convention interface_name_signal. +Here we're creating an AvalonSTDriver instance. The constructor requires 3 arguments - a handle to the entity containing the interface (**dut**), the name of the interface (**stream_in**) and the associated clock with which to drive the interface (**dut.clk**). The driver will auto-discover the signals for the interface, assuming that they follow the following naming convention **interface_name** _ *signal*. In this case we have the following signals defined for the **stream_in** interface: -===== ====== ======================================= +======================= =============== ============================================================================================== Name Type Description (from Avalon Specification) -===== ====== ======================================= +======================= =============== ============================================================================================== stream_in_data data The data signal from the source to the sink stream_in_empty empty Indicates the number of symbols that are empty during cycles that contain the end of a packet stream_in_valid valid Asserted by the source to qualify all other source to sink signals stream_in_startofpacket startofpacket Asserted by the source to mark the beginning of a packet stream_in_endofpacket endofpacket Asserted by the source to mark the end of a packet stream_in_ready ready Asserted high to indicate that the sink can accept data -===== ====== ======================================= +======================= =============== ============================================================================================== By following the signal naming convention the driver can find the signals associated with this interface automatically. @@ -96,4 +101,74 @@ Finally we create another Monitor instance, this time connected to the **stream_ """Model the DUT based on the input transaction""" self.expected_output.append(transaction) self.pkts_sent += 1 - + + +Test Function +~~~~~~~~~~~~~ + +There are various 'knobs' we can tweak on this tesbench to vary the behaviour: + +* Packet size +* Backpressure on the **stream_out** interface +* Idle cycles on the **stream_in** interface +* Configuration switching of the endian swap register during the test. + +We want to run different variations of tests but they will all have a very similar structure so we create a common ``run_test`` function. To generate backpressure on the **stream_out** interface we use the ``BitDriver`` class from ``cocotb.drivers``. + +.. code-block:: python + + @cocotb.coroutine + def run_test(dut, data_in=None, config_coroutine=None, idle_inserter=None, backpressure_inserter=None): + + cocotb.fork(clock_gen(dut.clk)) + tb = EndianSwapperTB(dut) + + yield tb.reset() + dut.stream_out_ready <= 1 + + # Start off any optional coroutines + if config_coroutine is not None: + cocotb.fork(config_coroutine(tb.csr)) + if idle_inserter is not None: + tb.stream_in.set_valid_generator(idle_inserter()) + if backpressure_inserter is not None: + tb.backpressure.start(backpressure_inserter()) + + # Send in the packets + for transaction in data_in(): + yield tb.stream_in.send(transaction) + + # Wait at least 2 cycles where output ready is low before ending the test + for i in xrange(2): + yield RisingEdge(dut.clk) + while not dut.stream_out_ready.value: + yield RisingEdge(dut.clk) + + pkt_count = yield tb.csr.read(1) + + if pkt_count.integer != tb.pkts_sent: + raise TestFailure("DUT recorded %d packets but tb counted %d" % ( + pkt_count.integer, tb.pkts_sent)) + else: + dut.log.info("DUT correctly counted %d packets" % pkt_count.integer) + + raise tb.scoreboard.result + +We can see that this test function creates an instance of the tesbench, resets the DUT by running the coroutine ``tb.reset()`` and then starts off any optional coroutines passed in using the keyword arguments. We then send in all the packets from ``data_in``, ensure that all the packets have been received by waiting 2 cycles at the end. We read the packet count and compare this with the number of packets. Finally we use the ``tb.scoreboard.result`` to determine the status of the test. If any transactions didn't match the expected output then this member would be and instance of the ``TestFailure`` result. + + +Test permutations +~~~~~~~~~~~~~~~~~ + +Having defined a test function we can now auto-generate different permutations of tests using the ``TestFactory`` class: + +.. code-block:: python + + factory = TestFactory(run_test) + factory.add_option("data_in", [random_packet_sizes]) + factory.add_option("config_coroutine", [None, randomly_switch_config]) + factory.add_option("idle_inserter", [None, wave, intermittent_single_cycles, random_50_percent]) + factory.add_option("backpressure_inserter", [None, wave, intermittent_single_cycles, random_50_percent]) + factory.generate_tests() + +This will generate 32 tests (named run_test_001 to run_test_032) with all possible permutations of options provided for each argument. Note that we utilise some of the built-in generators to toggle backpressure and insert idle cycles. From 1176c66917eee08fc159ba28dbbbf6f1db961db9 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 21 Jan 2014 15:39:52 +0000 Subject: [PATCH 0455/2656] Prevent writing non-UTF-8 characters to XML --- cocotb/regression.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index a6a4b07b..93078bcc 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -157,13 +157,13 @@ def handle_result(self, result): elif isinstance(result, TestSuccess) and self._running_test.expect_error: self.log.error("Test passed but we expected an error: %s (result was %s)" % ( self._running_test.funcname, result.__class__.__name__)) - self.xunit.add_failure(stdout=str(result), stderr="\n".join(self._running_test.error_messages)) + self.xunit.add_failure(stdout=repr(str(result)), stderr="\n".join(self._running_test.error_messages)) self.failures += 1 elif isinstance(result, TestSuccess): self.log.error("Test passed but we expected a failure: %s (result was %s)" % ( self._running_test.funcname, result.__class__.__name__)) - self.xunit.add_failure(stdout=str(result), stderr="\n".join(self._running_test.error_messages)) + self.xunit.add_failure(stdout=repr(str(result)), stderr="\n".join(self._running_test.error_messages)) self.failures += 1 elif isinstance(result, TestError) and self._running_test.expect_error: @@ -173,7 +173,7 @@ def handle_result(self, result): else: self.log.error("Test Failed: %s (result was %s)" % ( self._running_test.funcname, result.__class__.__name__)) - self.xunit.add_failure(stdout=str(result), stderr="\n".join(self._running_test.error_messages)) + self.xunit.add_failure(stdout=repr(str(result)), stderr="\n".join(self._running_test.error_messages)) self.failures += 1 self.execute() From 71c5c2182ff5b5d611a3a754357987840113166f Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 26 Jan 2014 09:36:51 +0000 Subject: [PATCH 0456/2656] Issue #9: Start adding auto-generated documentation from docstrings --- cocotb/__init__.py | 8 +++-- cocotb/handle.py | 9 ++++- cocotb/log.py | 6 +++- cocotb/regression.py | 38 +++++++++++---------- cocotb/scheduler.py | 7 +++- cocotb/triggers.py | 8 ++++- documentation/source/conf.py | 3 +- documentation/source/index.rst | 1 + documentation/source/library_reference.rst | 39 ++++++++++++++++++++++ 9 files changed, 94 insertions(+), 25 deletions(-) create mode 100644 documentation/source/library_reference.rst diff --git a/cocotb/__init__.py b/cocotb/__init__.py index acf27c8d..3f5d96bc 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -53,9 +53,11 @@ # scheduler package # GPI logging instance -logging.basicConfig() -logging.setLoggerClass(SimBaseLog) -log = SimLog('cocotb.gpi') +# For autodocumentation don't need the extension modules +if "SPHINX_BUILD" not in os.environ: + logging.basicConfig() + logging.setLoggerClass(SimBaseLog) + log = SimLog('cocotb.gpi') scheduler = Scheduler() regression = None diff --git a/cocotb/handle.py b/cocotb/handle.py index f0f806f0..cee7f9ca 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -35,7 +35,14 @@ import sys from StringIO import StringIO -import simulator +import os + +# For autodocumentation don't need the extension modules +if "SPHINX_BUILD" in os.environ: + simulator = None +else: + import simulator + import cocotb from cocotb.binary import BinaryValue from cocotb.log import SimLog diff --git a/cocotb/log.py b/cocotb/log.py index 567d05de..8cd08fac 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -33,7 +33,11 @@ import sys import logging import inspect -import simulator +# For autodocumentation don't need the extension modules +if "SPHINX_BUILD" in os.environ: + simulator = None +else: + import simulator import cocotb.ANSI as ANSI from pdb import set_trace diff --git a/cocotb/regression.py b/cocotb/regression.py index 93078bcc..eacdd101 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -34,7 +34,12 @@ import inspect from itertools import product -import simulator +import os +# For autodocumentation don't need the extension modules +if "SPHINX_BUILD" in os.environ: + simulator = None +else: + import simulator import cocotb import cocotb.ANSI as ANSI @@ -243,24 +248,23 @@ class TestFactory(object): For example if we have a module that takes backpressure and idles and have some packet generations routines gen_a and gen_b. - tf = TestFactory(run_test) - - tf.add_option('data_in', [gen_a, gen_b]) - tf.add_option('backpressure', [None, random_backpressure]) - tf.add_option('idles', [None, random_idles]) - tf.generate_tests() + >>> tf = TestFactory(run_test) + >>> tf.add_option('data_in', [gen_a, gen_b]) + >>> tf.add_option('backpressure', [None, random_backpressure]) + >>> tf.add_option('idles', [None, random_idles]) + >>> tf.generate_tests() We would get the following tests: - gen_a with no backpressure and no idles - gen_a with no backpressure and random_idles - gen_a with random_backpressure and no idles - gen_a with random_backpressure and random_idles - gen_b with no backpressure and no idles - gen_b with no backpressure and random_idles - gen_b with random_backpressure and no idles - gen_b with random_backpressure and random_idles - - The tests are appended to the calling module for aut-discovery. + * gen_a with no backpressure and no idles + * gen_a with no backpressure and random_idles + * gen_a with random_backpressure and no idles + * gen_a with random_backpressure and random_idles + * gen_b with no backpressure and no idles + * gen_b with no backpressure and random_idles + * gen_b with random_backpressure and no idles + * gen_b with random_backpressure and random_idles + + The tests are appended to the calling module for auto-discovery. Tests are simply named test_function_N. The docstring for the test (hence the test description) includes the name and description of each generator. diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 268e8e54..75f444f8 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -38,7 +38,12 @@ import time import pdb -import simulator +# For autodocumentation don't need the extension modules +if "SPHINX_BUILD" in os.environ: + simulator = None +else: + import simulator + import cocotb import cocotb.decorators from cocotb.triggers import Trigger, Timer, ReadOnly, NextTimeStep, ReadWrite, RisingEdge diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 4a06f3cb..ff835629 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -28,7 +28,13 @@ """ A collections of triggers which a testbench can 'yield' """ -import simulator +import os + +# For autodocumentation don't need the extension modules +if "SPHINX_BUILD" in os.environ: + simulator = None +else: + import simulator from cocotb.log import SimLog from cocotb.result import raise_error diff --git a/documentation/source/conf.py b/documentation/source/conf.py index 4b9196c3..24dbc050 100644 --- a/documentation/source/conf.py +++ b/documentation/source/conf.py @@ -16,7 +16,8 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) +sys.path.insert(0, os.path.abspath('../..')) +os.environ["SPHINX_BUILD"] = "1" # -- General configuration ----------------------------------------------------- diff --git a/documentation/source/index.rst b/documentation/source/index.rst index 65732474..85e509bd 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -16,6 +16,7 @@ Contents: quickstart building coroutines + library_reference triggers endian_swapper ping_tun_tap diff --git a/documentation/source/library_reference.rst b/documentation/source/library_reference.rst new file mode 100644 index 00000000..def91053 --- /dev/null +++ b/documentation/source/library_reference.rst @@ -0,0 +1,39 @@ +################# +Library Reference +################# + +Test Results +============ + +The following exceptions can be raised at any point by any code and will terminate the test: + +.. autoclass:: cocotb.result.TestComplete + +.. autoclass:: cocotb.result.TestError + +.. autoclass:: cocotb.result.TestFailure + +.. autoclass:: cocotb.result.TestSuccess + + +Writing and Generating tests +============================ + +.. autoclass:: cocotb.test + +.. autoclass:: cocotb.coroutine + +.. autoclass:: cocotb.regression.TestFactory + :members: + + + +Interacting with the Simulator +============================== + + +.. autoclass:: cocotb.binary.BinaryValue + :members: + +.. autoclass:: cocotb.bus.Bus + :members: From 35dc871fce050046ce8de8cf15d481e302be6b02 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 26 Jan 2014 10:14:40 +0000 Subject: [PATCH 0457/2656] Issue #9: Move trigger documentation into library reference and autodoc --- documentation/source/index.rst | 1 - documentation/source/library_reference.rst | 35 ++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/documentation/source/index.rst b/documentation/source/index.rst index 85e509bd..8adab1d9 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -17,7 +17,6 @@ Contents: building coroutines library_reference - triggers endian_swapper ping_tun_tap roadmap diff --git a/documentation/source/library_reference.rst b/documentation/source/library_reference.rst index def91053..27d89eec 100644 --- a/documentation/source/library_reference.rst +++ b/documentation/source/library_reference.rst @@ -37,3 +37,38 @@ Interacting with the Simulator .. autoclass:: cocotb.bus.Bus :members: + + +Triggers +-------- + +Triggers are used to indicate when the scheduler should resume coroutine execution. Typically a coroutine will **yield** a trigger or a list of triggers. + +Simulation Timing +~~~~~~~~~~~~~~~~~ + +.. autoclass:: cocotb.triggers.Timer + +.. autoclass:: cocotb.triggers.ReadOnly + + +Signal related +~~~~~~~~~~~~~~ + +.. autoclass:: cocotb.triggers.Edge + +.. autoclass:: cocotb.triggers.RisingEdge + + +Python Triggers +~~~~~~~~~~~~~~~ + + +.. autoclass:: cocotb.triggers.Event + :members: + +.. autoclass:: cocotb.triggers.Lock + :members: + +.. autoclass:: cocotb.triggers.Join + :members: From b71fe2a0731f0d0c3836fc88a115827f5665b743 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 2 Feb 2014 16:26:11 +0000 Subject: [PATCH 0458/2656] Issue #9 - add Driver, Monitor and Scoreboard to library reference --- documentation/source/library_reference.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/documentation/source/library_reference.rst b/documentation/source/library_reference.rst index 27d89eec..9fd8baf3 100644 --- a/documentation/source/library_reference.rst +++ b/documentation/source/library_reference.rst @@ -72,3 +72,19 @@ Python Triggers .. autoclass:: cocotb.triggers.Join :members: + + +Testbench Structure +=================== + +.. autoclass:: cocotb.drivers.Driver + :members: + +.. autoclass:: cocotb.monitors.Monitor + :members: + +.. autoclass:: cocotb.scoreboard.Scoreboard + :members: + + + From b8bd554fcdd6536d80c4947f02cbac78c077a3b4 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 4 Feb 2014 09:11:04 +0000 Subject: [PATCH 0459/2656] Don't fail silently if drivers don't implement _driver_send method --- cocotb/drivers/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index fb5520e0..b6c0c918 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -148,7 +148,7 @@ def _driver_send(self, transaction, sync=True): subclasses should override this method to implement the actual send routine """ - pass + raise NotImplemented("Subclasses of Driver should define a _driver_send coroutine") @coroutine From 4e1981dff95c4e380aab757f6fd52246be9df872 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 4 Feb 2014 14:29:32 +0000 Subject: [PATCH 0460/2656] chmod -x on handle.py --- cocotb/handle.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 cocotb/handle.py diff --git a/cocotb/handle.py b/cocotb/handle.py old mode 100755 new mode 100644 From 29fd4e110685fa0d9af7699ef76589d4bd0396e3 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 4 Feb 2014 18:13:30 +0000 Subject: [PATCH 0461/2656] Issue #113 add operators to BinaryValue --- cocotb/binary.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/cocotb/binary.py b/cocotb/binary.py index 0ef404ee..fbe29fe5 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -190,6 +190,63 @@ def __cmp__(self, other): other = other.value return self.value.__cmp__(other) + def __int__(self): + return self.integer + + def __long__(self): + return self.integer + + def __add__(self, other): + return self.integer + int(other) + + def __iadd__(self, other): + self.integer = self.integer + int(other) + return self + + def __sub__(self, other): + return self.integer - int(other) + + def __isub__(self, other): + self.integer = self.integer - int(other) + return self + + def __mul__(self, other): + return self.integer * int(other) + + def __imul__(self, other): + self.integer = self.integer * int(other) + return self + + def __divmod__(self, other): + return self.integer // int(other) + + def __idivmod__(self, other): + self.integer = self.integer // int(other) + return self + + def __mod__(self, other): + return self.integer % int(other) + + def __imod__(self, other): + self.integer = self.integer % int(other) + return self + + def __lshift__(self, other): + return int(self) << int(other) + + def __ilshift__(self, other): + """Preserves X values""" + self.binstr = self.binstr[other:] + self.binstr[:other] + return self + + def __rshift__(self, other): + return int(self) >> int(other) + + def __irshift__(self, other): + """Preserves X values""" + self.binstr = self.binstr[-other:] + self.binstr[:-other] + return self + if __name__ == "__main__": import doctest From f5ef70f816a977287fb56cecb26ddcef5de279d0 Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 19 Feb 2014 14:09:04 +0000 Subject: [PATCH 0462/2656] Issue #114: Fixes for IUS simulator makefile from @nicklfitz --- makefiles/Makefile.inc | 2 +- makefiles/simulators/Makefile.ius | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 8c5660ac..7c2a9b19 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -70,5 +70,5 @@ endif ifeq ($(OS),Darwin) LINKER_ARGS := -shared -undefined suppress -flat_namespace -L$(PYTHON_LIBDIR) else -LINKER_ARGS := -shared -Xlinker -export-dynamic +LINKER_ARGS := -shared -Xlinker -export-dynamic -L$(PYTHON_LIBDIR) endif diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 6fa28585..cdf7f5c4 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -28,8 +28,8 @@ ############################################################################### results.xml: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - irun -sv $(EXTRA_ARGS) -64bit -loadvpi $(BUILD_DIR)/libgpi:vlog_startup_routines_bootstrap +access+rwc $(VERILOG_SOURCES) + LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + irun -sv $(EXTRA_ARGS) -64bit -loadvpi $(LIB_DIR)/libgpi:vlog_startup_routines_bootstrap +access+rwc $(VERILOG_SOURCES) clean:: -@rm -rf $(SIM_BUILD) From 84649c20c9d8a0097854e7baa063f8237469c44b Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 21 Feb 2014 20:48:08 +0000 Subject: [PATCH 0463/2656] Closes #107: Implement system tasks for reporting and fail the test on $fatal --- examples/sim_exit/hdl/close_module.v | 9 +++- examples/sim_exit/tests/Makefile | 2 +- include/vpi_user.h | 6 ++- lib/vpi_shim/gpi_vpi.c | 81 ++++++++++++++++++++++++---- 4 files changed, 83 insertions(+), 15 deletions(-) diff --git a/examples/sim_exit/hdl/close_module.v b/examples/sim_exit/hdl/close_module.v index 481501f8..e56e9410 100644 --- a/examples/sim_exit/hdl/close_module.v +++ b/examples/sim_exit/hdl/close_module.v @@ -55,8 +55,13 @@ always @(stream_out_ready) initial begin $dumpfile("waveform.vcd"); $dumpvars(0,close_module); - - #500000 $fail_test("Test timed out, failing..."); + #10 $info; + #10 $info("This is an info message"); + #10 $warning; + #10 $warning("This is a warning message"); + #10 $error; + #10 $error("This is an error message"); + #1000 $fatal("This is a a fatal message that fails the test"); end endmodule diff --git a/examples/sim_exit/tests/Makefile b/examples/sim_exit/tests/Makefile index 21de9877..fa9f769d 100644 --- a/examples/sim_exit/tests/Makefile +++ b/examples/sim_exit/tests/Makefile @@ -31,7 +31,7 @@ TOPLEVEL = close_module PWD=$(shell pwd) - +COCOTB=$(PWD)/../../.. VERILOG_SOURCES = $(PWD)/../hdl/close_module.v MODULE=test_closedown diff --git a/include/vpi_user.h b/include/vpi_user.h index 9212da00..56066f3d 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -137,6 +137,10 @@ typedef struct t_vpi_value #define vpiL 5 #define vpiDontCare 6 +/* properties */ +#define vpiFile 5 +#define vpiLineNo 6 + /* normal callback structure */ typedef struct t_cb_data { @@ -260,7 +264,7 @@ extern int32_t vpi_chk_error(p_vpi_error_info); extern int32_t vpi_get_vlog_info(p_vpi_vlog_info info_p); -extern int32_t vpi_register_systf(p_vpi_systf_data data_p); +extern vpiHandle vpi_register_systf(p_vpi_systf_data data_p); extern void (*vlog_startup_routines[])(void); diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 831108a8..99aa47ad 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -1004,36 +1004,95 @@ void register_final_callback(void) FEXIT } +// Called at compile time to validate the arguments to the system functions +// we redefine (info, warning, error, fatal). +// +// Expect either no arguments or a single string +static int system_function_compiletf(char *userdata) +{ + vpiHandle systf_handle, arg_iterator, arg_handle; + int tfarg_type; + + systf_handle = vpi_handle(vpiSysTfCall, NULL); + arg_iterator = vpi_iterate(vpiArgument, systf_handle); + + if (arg_iterator == NULL) + return 0; + + arg_handle = vpi_scan(arg_iterator); + tfarg_type = vpi_get(vpiType, arg_handle); + + // FIXME: HACK for some reason Icarus returns a vpiRealVal type for strings? + if (vpiStringVal != tfarg_type && vpiRealVal != tfarg_type) { + vpi_printf("ERROR: $[info|warning|error|fata] argument wrong type: %d\n", + tfarg_type); + vpi_free_object(arg_iterator); + vpi_control(vpiFinish, 1); + } +} + +static int systf_info_level = GPIInfo; +static int systf_warning_level = GPIWarning; +static int systf_error_level = GPIError; +static int systf_fatal_level = GPICritical; // System function to permit code in the simulator to fail a test // TODO: Pass in an error string -static int system_function_fail_test(char *userdata) +static int system_function_overload(char *userdata) { - vpiHandle systfref, args_iter, argh; struct t_vpi_value argval; + const char *msg = "*** NO MESSAGE PROVIDED ***"; // Obtain a handle to the argument list systfref = vpi_handle(vpiSysTfCall, NULL); args_iter = vpi_iterate(vpiArgument, systfref); - // Grab the value of the first argument - argh = vpi_scan(args_iter); - argval.format = vpiStringVal; - vpi_get_value(argh, &argval); + if (args_iter) { + // Grab the value of the first argument + argh = vpi_scan(args_iter); + argval.format = vpiStringVal; + vpi_get_value(argh, &argval); + vpi_free_object(args_iter); + msg = argval.value.str; + } - embed_sim_event(SIM_TEST_FAIL, argval.value.str); + gpi_log("simulator", *userdata, vpi_get_str(vpiFile, systfref), "", (long)vpi_get(vpiLineNo, systfref), msg ); + + // Fail the test for critical errors + if (GPICritical == *userdata) + embed_sim_event(SIM_TEST_FAIL, argval.value.str); - // Cleanup and return - vpi_free_object(args_iter); return 0; } + + void register_system_functions(void) { FENTER - s_vpi_systf_data data = {vpiSysTask, vpiIntFunc, "$fail_test", system_function_fail_test, NULL, NULL, NULL}; - vpi_register_systf(&data); + s_vpi_systf_data tfData = { vpiSysTask, vpiSysTask }; + + tfData.sizetf = NULL; + tfData.compiletf = system_function_compiletf; + tfData.calltf = system_function_overload; + + tfData.user_data = (void *)&systf_info_level; + tfData.tfname = "$info"; + vpi_register_systf( &tfData ); + + tfData.user_data = (void *)&systf_warning_level; + tfData.tfname = "$warning"; + vpi_register_systf( &tfData ); + + tfData.user_data = (void *)&systf_error_level; + tfData.tfname = "$error"; + vpi_register_systf( &tfData ); + + tfData.user_data = (void *)&systf_fatal_level; + tfData.tfname = "$fatal"; + vpi_register_systf( &tfData ); + FEXIT } From f483024a396300e37c29304cf1eec206016cbfcc Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 21 Feb 2014 21:21:36 +0000 Subject: [PATCH 0464/2656] Issue #117: Initial AXI4-Lite master implementation --- cocotb/drivers/amba.py | 173 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 cocotb/drivers/amba.py diff --git a/cocotb/drivers/amba.py b/cocotb/drivers/amba.py new file mode 100644 index 00000000..e91ad751 --- /dev/null +++ b/cocotb/drivers/amba.py @@ -0,0 +1,173 @@ +''' Copyright (c) 2014 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' +""" +Drivers for Advanced Microcontroller Bus Architecture +""" +import cocotb +from cocotb.triggers import RisingEdge, ReadOnly, Lock +from cocotb.drivers import BusDriver +from cocotb.result import ReturnValue + + +class AXIReadError(Exception): pass + + +class AXI4LiteMaster(BusDriver): + """ + AXI4-Lite Master + + TODO: Kill all pending transactions if reset is asserted... + """ + _signals = ["ARESETn", + "AWVALID", "AWADDR", "AWREADY", # Write address channel + "WVALID", "WREADY", "WDATA", "WSTRB", # Write data channel + "BVALID", "BREADY", "BRESP", # Write response channel + "ARVALID", "ARADDR", "ARREADY", # Read address channel + "RVALID", "RREADY", "RRESP", "RDATA"] # Read data channel + + def __init__(self, entity, name, clock): + BusDriver.__init__(self, entity, name, clock) + + # Drive some sensible defaults (setimmediatevalue to avoid x asserts) + self.bus.AWVALID.setimmediatevalue(0) + self.bus.WVALID.setimmediatevalue(0) + self.bus.ARVALID.setimmediatevalue(0) + self.bus.BREADY.setimmediatevalue(1) + self.bus.RREADY.setimmediatevalue(1) + + # Mutex for each channel that we master to prevent contention + self.write_address_busy = Lock("%s_wabusy" % name) + self.read_address_busy = Lock("%s_rabusy" % name) + self.write_data_busy = Lock("%s_wbusy" % name) + + + @cocotb.coroutine + def _send_write_address(self, address, delay=0): + """ + Send the write address, with optional delay + """ + yield self.write_address_busy.acquire() + for cycle in xrange(delay): + yield RisingEdge(self.clock) + + self.bus.AWADDR <= address + self.bus.AWVALID <= 1 + + while True: + yield ReadOnly() + if self.bus.AWREADY.value: + break + yield RisingEdge(self.clock) + yield RisingEdge(self.clock) + self.bus.AWVALID <= 0 + self.write_address_busy.release() + + @cocotb.coroutine + def _send_write_data(self, data, delay=0, byte_enable=0xF): + """ + Send the write address, with optional delay + """ + yield self.write_data_busy.acquire() + for cycle in xrange(delay): + yield RisingEdge(self.clock) + + self.bus.WDATA <= data + self.bus.WVALID <= 1 + self.bus.WSTRB <= byte_enable + + while True: + yield ReadOnly() + if self.bus.WREADY.value: + break + yield RisingEdge(self.clock) + yield RisingEdge(self.clock) + self.bus.WVALID <= 0 + self.write_data_busy.release() + + + @cocotb.coroutine + def write(self, address, value, byte_enable=0xf, address_latency=0, data_latency=0): + """ + Write a value to an address. + + The *_latency KWargs allow control over the delta + """ + c_addr = cocotb.fork(self._send_write_address(address, delay=address_latency)) + c_data = cocotb.fork(self._send_write_data(value, byte_enable=byte_enable, delay=data_latency)) + + if c_addr: + yield c_addr.join() + if c_data: + yield c_data.join() + + # Wait for the response + while True: + yield ReadOnly() + if self.bus.BVALID.value and self.bus.BREADY.value: + result = self.bus.BRESP.value + break + yield RisingEdge(self.clock) + + if int(result): + raise AXIReadError("Write to address 0x%08x failed with BRESP: %d" %( + address, int(result))) + + raise ReturnValue(result) + + + @cocotb.coroutine + def read(self, address, sync=True): + """ + Read from an address. + """ + if sync: + yield RisingEdge(self.clock) + + self.bus.ARADDR <= address + self.bus.ARVALID <= 1 + + while True: + yield ReadOnly() + if self.bus.WREADY.value: + break + yield RisingEdge(self.clock) + + yield RisingEdge(self.clock) + self.bus.ARVALID <= 0 + + while True: + yield ReadOnly() + if self.bus.RVALID.value and self.bus.RREADY.value: + data = self.bus.RDATA.value + result = self.bus.RRESP.value + break + yield RisingEdge(self.clock) + + if int(result): + raise AXIReadError("Read address 0x%08x failed with RRESP: %d" %( + address, int(result))) + + raise ReturnValue(data) From cb6e6fa2d3353117bc3d70aa0f07c904744a9dea Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 21 Feb 2014 22:04:22 +0000 Subject: [PATCH 0465/2656] Issue #116: Initial stab at VHPI layer (uncompiled!) --- lib/vhpi_shim/Makefile | 48 ++ lib/vhpi_shim/gpi_vhpi.c | 977 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 1025 insertions(+) create mode 100644 lib/vhpi_shim/Makefile create mode 100644 lib/vhpi_shim/gpi_vhpi.c diff --git a/lib/vhpi_shim/Makefile b/lib/vhpi_shim/Makefile new file mode 100644 index 00000000..b915c578 --- /dev/null +++ b/lib/vhpi_shim/Makefile @@ -0,0 +1,48 @@ +############################################################################### +# Copyright (c) 2014 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +include $(SIM_ROOT)/makefiles/Makefile.inc + +INCLUDES += +GCC_ARGS += -DVPI_CHECKING +LIBS := -lgpilog -lcocotb +LD_PATH := -L$(LIB_DIR) +LIB_NAME := libgpi.so + +SRCS := gpi_vhpi.c + +all: $(LIB_DIR)/cocotb.so + +$(LIB_DIR)/cocotb.so: $(LIB_DIR)/$(LIB_NAME) + ln -sf $(LIB_NAME) $@ + +clean: + -@rm -rf $(LIB_DIR)/gpivpi.vpl + -@rm -rf $(LIB_DIR)/$(LIB_NAME) + +include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/lib/vhpi_shim/gpi_vhpi.c b/lib/vhpi_shim/gpi_vhpi.c new file mode 100644 index 00000000..50c06a28 --- /dev/null +++ b/lib/vhpi_shim/gpi_vhpi.c @@ -0,0 +1,977 @@ +/****************************************************************************** +* Copyright (c) 2014 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd, +* SolarFlare Communications Inc nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + + +#include +#include +#include + +#include +#include +#include +#include + +#define gpi_container_of(_address, _type, _member) \ + ((_type *)((uintptr_t)(_address) - \ + (uintptr_t)(&((_type *)0)->_member))) + +#define VPI_CHECKING 1 + +static gpi_sim_hdl sim_init_cb; +static gpi_sim_hdl sim_finish_cb; + +static int alloc_count = 0; +static int dealloc_count = 0; +static int clear_count = 0; +static int total_count = 0; + +typedef enum vhpi_cb_state_e { + VHPI_FREE = 0, + VHPI_PRIMED = 1, + VHPI_PRE_CALL = 2, + VHPI_POST_CALL = 3, + VHPI_DELETE = 4, +} vhpi_cb_state_t; + +// callback user data used for VPI callbacks +// (mostly just a thin wrapper around the gpi_callback) +typedef struct t_vhpi_cb_user_data { + void *gpi_cb_data; + int (*gpi_function)(void *); + int (*gpi_cleanup)(struct t_vhpi_cb_user_data *); + vhpiHandleT cb_hdl; + vhpiValueT cb_value; + gpi_sim_hdl_t gpi_hdl; + vhpi_cb_state_t state; +} s_vhpi_cb_user_data, *p_vhpi_cb_user_data; + +// Define a type of a clock object +typedef struct gpi_clock_s { + int period; + int value; + unsigned int max_cycles; + unsigned int curr_cycle; + bool exit; + gpi_sim_hdl_t gpi_hdl; /* Handle to pass back to called */ + gpi_sim_hdl clk_hdl; /* Handle for signal to operate on */ + gpi_sim_hdl cb_hdl; /* Handle for the current pending callback */ +} gpi_clock_t; + +typedef gpi_clock_t *gpi_clock_hdl; + +// Add to this over time +static const char * vhpi_reason_to_string(int reason) +{ + switch (reason) { + case vhpiCbValueChange: + return "vhpiCbValueChange"; + case vhpiCbStartOfNextCycle: + return "vhpiCbStartOfNextCycle"; + case vhpiCbStartOfPostponed: + return "vhpiCbStartOfPostponed"; + case vhpiCbEndOfTimeStep: + return "vhpiCbEndOfTimeStep"; + case vhpiCbNextTimeStep: + return "vhpiCbNextTimeStep"; + case vhpiCbAfterDelay: + return "vhpiCbAfterDelay"; + case vhpiCbStartOfSimulation: + return "vhpiCbStartOfSimulation"; + case vhpiCbEndOfSimulation: + return "vhpiCbEndOfSimulation"; + default: + return "unknown"; + } +} + +// Should be run after every VPI call to check error status +static int __check_vhpi_error(const char *func, long line) +{ + int level=0; +#if VPI_CHECKING + vhpiErrorInfoT info; + int loglevel; + level = vhpi_check_error(&info); + if (level == 0) + return; + + switch (level) { + case vpiNotice: + loglevel = GPIInfo; + break; + case vpiWarning: + loglevel = GPIWarning; + break; + case vpiError: + loglevel = GPIError; + break; + case vpiSystem: + case vpiInternal: + loglevel = GPICritical; + break; + } + + gpi_log("cocotb.gpi", loglevel, __FILE__, func, line, + "VPI Error level %d: %s\nFILE %s:%d", + info.severity, info.message, info.file, info.line); + +#endif + return level; +} + +#define check_vhpi_error() \ + __check_vhpi_error(__func__, __LINE__) + +static inline int __gpi_register_cb(p_vhpi_cb_user_data user, vhpiCbDataT *cb_data) +{ + /* If the user data already has a callback handle then deregister + * before getting the new one + */ + vhpiHandleT new_hdl = vhpi_register_cb(cb_data, vhpiReturnCb); + int ret = 0; + + if (!new_hdl) { + LOG_CRITICAL("VPI: Unable to register callback a handle for VHPI type %s(%d)", + vhpi_reason_to_string(cb_data->reason), cb_data->reason); + check_vhpi_error(); + ret = -1; + } + + if (user->cb_hdl != NULL) + gpi_deregister_callback(&user->gpi_hdl); + + user->cb_hdl = new_hdl; + + return ret; +} + +static inline p_vhpi_cb_user_data __gpi_alloc_user(void) +{ + p_vhpi_cb_user_data new_data = calloc(1, sizeof(*new_data)); + if (new_data == NULL) { + LOG_CRITICAL("VPI: Attempting allocate user_data failed!"); + } + + return new_data; +} + +static inline void __gpi_free_callback(gpi_sim_hdl gpi_hdl) +{ + FENTER + p_vhpi_cb_user_data user_data; + user_data = gpi_container_of(gpi_hdl, s_vhpi_cb_user_data, gpi_hdl); + + free(user_data); + FEXIT +} + +void gpi_free_handle(gpi_sim_hdl gpi_hdl) +{ + free(gpi_hdl); +} + +static gpi_sim_hdl gpi_alloc_handle(void) +{ + gpi_sim_hdl new_hdl = calloc(1, sizeof(*new_hdl)); + if (!new_hdl) { + LOG_CRITICAL("VPI: Could not allocate handle"); + exit(1); + } + + return new_hdl; +} + +// Handle related functions +/** + * @name Find the root handle + * @brief Find the root handle using a optional name + * + * Get a handle to the root simulator object. This is usually the toplevel. + * + * FIXME: In VHPI we always return the first root instance + * + * TODO: Investigate possibility of iterating and checking names as per VHPI + * If no name is defined, we return the first root instance. + * + * If name is provided, we check the name against the available objects until + * we find a match. If no match is found we return NULL + */ +gpi_sim_hdl gpi_get_root_handle(const char* name) +{ + FENTER + vhpiHandleT root; + gpi_sim_hdl rv; + + root = vhpi_handle(vhpiRootInst, NULL); + check_vhpi_error(); + + if (!root) { + LOG_ERROR("VHPI: Attempting to get the root handle failed"); + FEXIT + return NULL; + } + + const char *found = vhpi_get_str(vhpiFullNameP, root); + check_vhpi_error(); + + if (name != NULL && strcmp(name, found)) { + LOG_WARN("VHPI: Root %s doesn't match requested toplevel %s", name, found); + FEXIT + return NULL; + + rv = gpi_alloc_handle(); + rv->sim_hdl = root; + + FEXIT + return rv; +} + + +gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) +{ + FENTER + gpi_sim_hdl rv; + vhpiHandleT obj; + int len; + char *buff; + if (name) + len = strlen(name) + 1; + + buff = (char *)malloc(len); + if (buff == NULL) { + LOG_CRITICAL("VHPI: Attempting allocate string buffer failed!"); + return NULL; + } + + strncpy(buff, name, len); + obj = vpi_handle_by_name(buff, (vhpiHandleT)(parent->sim_hdl)); + if (!obj) { + LOG_DEBUG("VHPI: Handle '%s' not found!", name); +// check_vhpi_error(); + return NULL; + } + + free(buff); + + rv = gpi_alloc_handle(); + rv->sim_hdl = obj; + + FEXIT + return rv; +} + +/** + * @brief Get a handle for an object based on its index within a parent + * + * @param parent handle to the parent + * @param indext Index to retrieve + * + * Can be used on bit-vectors to access a specific bit or + * memories to access an address + */ +gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) +{ + FENTER + gpi_sim_hdl rv; + vhpiHandleT obj; + + obj = vhpi_handle_by_index((vhpiParamDecls, (vhpiHandleT)(parent->sim_hdl), index); + if (!obj) { + LOG_ERROR("VHPI: Handle idx '%d' not found!", index); + return NULL; + } + + rv = gpi_alloc_handle(); + rv->sim_hdl = obj; + + FEXIT + return rv; +} + + +// Functions for iterating over entries of a handle +// Returns an iterator handle which can then be used in gpi_next calls +// NB May return NULL if no objects of the request type exist +gpi_iterator_hdl gpi_iterate(uint32_t type, gpi_sim_hdl base) { + FENTER + + vhpiHandleT iterator; + + iterator = vhpi_iterate(type, (vhpiHandleT)(base->sim_hdl)); + check_vhpi_error(); + + FEXIT + return (gpi_iterator_hdl)iterator; +} + +// Returns NULL when there are no more objects +gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) +{ + FENTER + vhpiHandleT result; + gpi_sim_hdl rv = gpi_alloc_handle(); + + rv->sim_hdl = vhpi_scan((vhpiHandleT) iterator); + check_vhpi_error(); + if (!rv->sim_hdl) { + gpi_free_handle(rv); + rv = NULL; + } + FEXIT + return rv; +} + +void gpi_get_sim_time(uint32_t *high, uint32_t *low) +{ + vhpiTimeT vhpi_time_s; + vhpi_get_time(&vhpi_time_s, NULL); + check_vhpi_error(); + *high = vhpi_time_s.high; + *low = vhpi_time_s.low; +} + +// Value related functions +void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) +{ + FENTER + vhpiValueT value_s; + value_s.format = vhpiIntVal; + value_s.value.intg = value; + + vhpi_put_value((vhpiHandleT)(gpi_hdl->sim_hdl), &value_s, vhpiForcePropagate); + check_vhpi_error(); + + FEXIT +} + +void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) +{ + FENTER + vhpiValueT value_s; + vhpiValueT *value_p = &value_s; + + int len; + char *buff; + if (str) + len = strlen(str) + 1; + + buff = (char *)malloc(len); + if (buff== NULL) { + LOG_CRITICAL("VPI: Attempting allocate string buffer failed!"); + return; + } + + strncpy(buff, str, len); + + value_p->bufSize = len; + value_p->numElems = len; + value_p->value.str = buff; + value_p->format = vhpiBinStrVal; + + vhpi_put_value((vhpiHandleT)(gpi_hdl->sim_hdl), &value_s, vhpiForcePropagate); + check_vhpi_error(); + free(buff); + FEXIT +} + + +static char *gpi_copy_name(const char *name) +{ + int len; + char *result; + const char null[] = "NULL"; + + if (name) + len = strlen(name) + 1; + else { + LOG_CRITICAL("VPI: NULL came back from VPI"); + len = strlen(null); + name = null; + } + + result = (char *)malloc(len); + if (result == NULL) { + LOG_CRITICAL("VPI: Attempting allocate string buffer failed!"); + len = strlen(null); + name = null; + } + + snprintf(result, len, "%s\0", name); + + return result; +} + + +char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) +{ + FENTER + vhpiValueT value_s = {vhpiBinStrVal}; + vhpiValueT *value_p = &value_s; + + vpi_get_value((vhpiHandleT)(gpi_hdl->sim_hdl), value_p); + check_vhpi_error(); + + char *result = gpi_copy_name(value_p->value.str); + FEXIT + return result; +} + +char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl) +{ + FENTER + const char *name = vhpi_get_str(vhpiFullNameP, (vpiHandle)(gpi_hdl->sim_hdl)); + check_vhpi_error(); + char *result = gpi_copy_name(name); + FEXIT + return result; +} + +char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl) +{ + FENTER + const char *name = vhpi_get_str(vhpiKindStrP, (vpiHandle)(gpi_hdl->sim_hdl)); + check_vhpi_error(); + char *result = gpi_copy_name(name); + FEXIT + return result; +} + + +// Callback related functions +static int32_t handle_vhpi_callback(p_cb_data cb_data) +{ + FENTER + int rv = 0; + vpiHandle old_cb; + + p_vhpi_cb_user_data user_data; + user_data = (p_vhpi_cb_user_data)cb_data->user_data; + + if (!user_data) + LOG_CRITICAL("VPI: Callback data corrupted"); + + user_data->state = VPI_PRE_CALL; + old_cb = user_data->cb_hdl; + rv = user_data->gpi_function(user_data->gpi_cb_data); + + if (old_cb == user_data->cb_hdl) + gpi_deregister_callback(&user_data->gpi_hdl); + + /* A request to delete could have been done + * inside gpi_function + */ + if (user_data->state == VPI_DELETE) + gpi_destroy_cb_handle(&user_data->gpi_hdl); + else + user_data->state = VPI_POST_CALL; + + FEXIT + return rv; +}; + +/* Allocates memory that will persist for the lifetime of the + * handle, this may be short or long. A call to create + * must have a matching call to destroy at some point + */ +gpi_sim_hdl gpi_create_cb_handle(void) +{ + gpi_sim_hdl ret = NULL; + FENTER + + p_vhpi_cb_user_data user_data = __gpi_alloc_user(); + if (user_data) { + user_data->state = VPI_FREE; + ret = &user_data->gpi_hdl; + } + + FEXIT + return ret; +} + +/* Destroys the memory associated with the sim handle + * this can only be called on a handle that has been + * returned by a call to gpi_create_cb_handle + */ +void gpi_destroy_cb_handle(gpi_sim_hdl gpi_hdl) +{ + /* Check that is has been called, if this has not + * happend then also close down the sim data as well + */ + FENTER + p_vhpi_cb_user_data user_data; + user_data = gpi_container_of(gpi_hdl, s_vhpi_cb_user_data, gpi_hdl); + + if (user_data->state == VPI_PRE_CALL) { + user_data->state = VPI_DELETE; + } else { + gpi_deregister_callback(gpi_hdl); + __gpi_free_callback(gpi_hdl); + } + FEXIT +} + +/* Deregister a prior set up callback with the simulator + * The handle must have been allocated with gpi_create_cb_handle + * This can be called at any point between + * gpi_create_cb_handle and gpi_destroy_cb_handle + */ +int gpi_deregister_callback(gpi_sim_hdl gpi_hdl) +{ + p_vhpi_cb_user_data user_data; + int rc = 1; + FENTER + // We should be able to user vpi_get_cb_info + // but this is not implemented in ICARUS + // and gets upset on VCS. So instead we + // do some pointer magic. + + user_data = gpi_container_of(gpi_hdl, s_vhpi_cb_user_data, gpi_hdl); + + if (user_data->cb_hdl) { + rc = user_data->gpi_cleanup(user_data); + user_data->cb_hdl = NULL; + } + + FEXIT + GPI_RET(rc); +} + +// Call when the handle relates to a one time callback +// No need to call vpi_deregister_cb as the sim will +// do this but do need to destroy the handle +static int gpi_free_one_time(p_vhpi_cb_user_data user_data) +{ + FENTER + int rc; + vhpiHandleT cb_hdl = user_data->cb_hdl; + if (!cb_hdl) { + LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); + exit(1); + } + + // If the callback has not been called we also need to call + // remove as well + if (!user_data->state == VPI_PRIMED) { + rc = vhpi_remove_cb(cb_hdl); + if (!rc) { + check_vhpi_error(); + return rc; + } + + rc = vhpi_release_handle(cb_hdl); + if (!rc) { + check_vhpi_error(); + return rc; + } + } + FEXIT + return rc; +} + +// Call when the handle relates to recurring callback +// Unregister must be called when not needed and this +// will clean all memory allocated by the sim +static int gpi_free_recurring(p_vhpi_cb_user_data user_data) +{ + FENTER + int rc; + vhpiHandleT cb_hdl = user_data->cb_hdl; + if (!cb_hdl) { + LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); + exit(1); + } + + rc = vhpi_remove_cb(cb_hdl); + check_vhpi_error(); + FEXIT + return rc; +} + +/* These functions request a callback to be active with the current + * handle and associated data. A callback handle needs to have been + * allocated with gpi_create_cb_handle first + */ + +int gpi_register_value_change_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data, + gpi_sim_hdl gpi_hdl) +{ + FENTER + vhpiCbDataT cb_data_s; + vhpiTimeT time; + + p_vhpi_cb_user_data user_data; + int ret; + + user_data = gpi_container_of(cb, s_vhpi_cb_user_data, gpi_hdl); + + user_data->gpi_cb_data = gpi_cb_data; + user_data->gpi_function = gpi_function; + user_data->gpi_cleanup = gpi_free_recurring; + user_data->cb_value.format = vhpiIntVal; + + cb_data_s.reason = vhpiCbValueChange; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = (vhpiHandleT)(gpi_hdl->sim_hdl); + cb_data_s.time = &time; + cb_data_s.value = &user_data->cb_value; + cb_data_s.user_data = (char *)user_data; + + ret = __gpi_register_cb(user_data, &cb_data_s); + user_data->state = VPI_PRIMED; + + FEXIT + + return ret; +} + + +int gpi_register_readonly_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) +{ + FENTER + vhpiCbDataT cb_data_s; + vhpiTimeT time; + + p_vhpi_cb_user_data user_data; + int ret; + + user_data = gpi_container_of(cb, s_vhpi_cb_user_data, gpi_hdl); + + user_data->gpi_cb_data = gpi_cb_data; + user_data->gpi_function = gpi_function; + user_data->gpi_cleanup = gpi_free_one_time; + + cb_data_s.reason = vhpiCbEndOfTimeStep; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &time; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)user_data; + + ret = __gpi_register_cb(user_data, &cb_data_s); + user_data->state = VPI_PRIMED; + + FEXIT + + return ret; +} + +int gpi_register_readwrite_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) +{ + FENTER + vhpiCbDataT cb_data_s; + vhpiTimeT time; + + p_vhpi_cb_user_data user_data; + int ret; + + user_data = gpi_container_of(cb, s_vhpi_cb_user_data, gpi_hdl); + + user_data->gpi_cb_data = gpi_cb_data; + user_data->gpi_function = gpi_function; + user_data->gpi_cleanup = gpi_free_one_time; + + cb_data_s.reason = vhpiCbEndOfProcesses; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &time; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)user_data; + + ret = __gpi_register_cb(user_data, &cb_data_s); + user_data->state = VPI_PRIMED; + + FEXIT + + return ret; +} + +int gpi_register_nexttime_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) +{ + FENTER + vhpiCbDataT cb_data_s; + vhpiTimeT time; + + p_vhpi_cb_user_data user_data; + int ret; + + user_data = gpi_container_of(cb, s_vhpi_cb_user_data, gpi_hdl); + + user_data->gpi_cb_data = gpi_cb_data; + user_data->gpi_function = gpi_function; + user_data->gpi_cleanup = gpi_free_one_time; + + cb_data_s.reason = vhpiCbNextTimeStep; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &time; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)user_data; + + ret = __gpi_register_cb(user_data, &cb_data_s); + user_data->state = VPI_PRIMED; + + FEXIT + + return ret; +} +int gpi_register_timed_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data, + uint64_t time_ps) +{ + FENTER + vhpiCbDataT cb_data_s; + vhpiTimeT time; + + p_vhpi_cb_user_data user_data; + int ret; + + user_data = gpi_container_of(cb, s_vhpi_cb_user_data, gpi_hdl); + + user_data->gpi_cb_data = gpi_cb_data; + user_data->gpi_function = gpi_function; + user_data->gpi_cleanup = gpi_free_one_time; + + vpi_time_s.high = (uint32_t)(time_ps>>32); + vpi_time_s.low = (uint32_t)(time_ps); + + cb_data_s.reason = vhpiCbAfterDelay; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &vpi_time_s; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)user_data; + + ret = __gpi_register_cb(user_data, &cb_data_s); + user_data->state = VPI_PRIMED; + + FEXIT + + return ret; +} + +int gpi_register_sim_start_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) +{ + FENTER + + vhpiCbDataT cb_data_s; + p_vhpi_cb_user_data user_data; + + user_data = gpi_container_of(cb, s_vhpi_cb_user_data, gpi_hdl); + + user_data->gpi_cb_data = gpi_cb_data; + user_data->gpi_function = gpi_function; + user_data->gpi_cleanup = gpi_free_one_time; + + cb_data_s.reason = vhpiCbStartOfSimulation; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = NULL; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)user_data; + + /* We ignore the return value here as VCS does some silly + * things on comilation that means it tries to run through + * the vlog_startup_routines and so call this routine + */ + __gpi_register_cb(user_data, &cb_data_s); + user_data->state = VPI_PRIMED; + + FEXIT + return 0; + +} + +int gpi_register_sim_end_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) +{ + FENTER + + vhpiCbDataT cb_data_s; + p_vhpi_cb_user_data user_data; + + user_data = gpi_container_of(cb, s_vhpi_cb_user_data, gpi_hdl); + + user_data->gpi_cb_data = gpi_cb_data; + user_data->gpi_function = gpi_function; + user_data->gpi_cleanup = gpi_free_one_time; + + cb_data_s.reason = vhpiCbEndOfSimulation; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = NULL; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)user_data; + + /* We ignore the return value here as VCS does some silly + * things on comilation that means it tries to run through + * the vlog_startup_routines and so call this routine + */ + __gpi_register_cb(user_data, &cb_data_s); + user_data->state = VPI_PRIMED; + + FEXIT + return 0; + +} + +int gpi_clock_handler(void *clock) +{ + gpi_clock_hdl hdl = (gpi_clock_hdl)clock; + gpi_sim_hdl cb_hdl; + + if (hdl->exit || ((hdl->max_cycles != 0) && (hdl->max_cycles == hdl->curr_cycle))) + return; + + /* Unregister/free the last callback that just fired */ + cb_hdl = hdl->cb_hdl; + + hdl->value = !hdl->value; + gpi_set_signal_value_int(hdl->clk_hdl, hdl->value); + gpi_register_timed_callback(cb_hdl, gpi_clock_handler, hdl, hdl->period); + hdl->curr_cycle++; +} + +gpi_sim_hdl gpi_clock_register(gpi_sim_hdl sim_hdl, int period, unsigned int cycles) +{ + FENTER + + gpi_clock_hdl hdl = malloc(sizeof(gpi_clock_t)); + if (!hdl) + LOG_CRITICAL("VPI: Unable to allocate memory"); + + hdl->period = period; + hdl->value = 0; + hdl->clk_hdl = sim_hdl; + hdl->exit = false; + hdl->max_cycles = cycles; + hdl->curr_cycle = 0; + + gpi_set_signal_value_int(hdl->clk_hdl, hdl->value); + hdl->cb_hdl = gpi_create_cb_handle(); + + gpi_register_timed_callback(hdl->cb_hdl, gpi_clock_handler, hdl, hdl->period); + + FEXIT + return &hdl->gpi_hdl; +} + +void gpi_clock_unregister(gpi_sim_hdl clock) +{ + gpi_clock_hdl hdl = gpi_container_of(clock, gpi_clock_t, gpi_hdl); + hdl->exit = true; +} + +void register_embed(void) +{ + FENTER + embed_init_python(); + FEXIT +} + + +int handle_sim_init(void *gpi_cb_data) +{ + FENTER + s_vpi_vlog_info info; + gpi_sim_info_t sim_info; + + vpi_get_vlog_info(&info); + + sim_info.argc = info.argc; + sim_info.argv = info.argv; + sim_info.product = info.product; + sim_info.version = info.version; + + embed_sim_init(&sim_info); + FEXIT +} + +void register_initial_callback(void) +{ + FENTER + sim_init_cb = gpi_create_cb_handle(); + gpi_register_sim_start_callback(sim_init_cb, handle_sim_init, (void *)NULL); + FEXIT +} + +int handle_sim_end(void *gpi_cb_data) +{ + FENTER + if (sim_finish_cb) { + sim_finish_cb = NULL; + /* This means that we have been asked to close */ + embed_sim_event(SIM_FAIL, "Simulator shutdown prematurely"); + } /* Other sise we have already been here from the top down so do not need + to inform the upper layers that anything has occoured */ + __gpi_free_callback(sim_init_cb); + FEXIT +} + +void register_final_callback(void) +{ + FENTER + sim_finish_cb = gpi_create_cb_handle(); + gpi_register_sim_end_callback(sim_finish_cb, handle_sim_end, (void *)NULL); + FEXIT +} + + +// If the Pything world wants things to shut down then unregister +// the callback for end of sim +void gpi_sim_end(void) +{ + FENTER + + sim_finish_cb = NULL; + vhpi_control(vhpiFinish); + check_vhpi_error(); + FEXIT +} + + +// pre-defined VHPI registration table +PLI_VOID(*vhpi_startup_routines[])() = +{ + register_embed, + register_initial_callback, + register_final_callback, + 0 +}; + From 49bf4cca4a29c4ffc11058381c1d111db963e29c Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 22 Feb 2014 10:13:04 +0000 Subject: [PATCH 0466/2656] Cleanup: Improve initialisation reporting and remove duplicate warning message --- lib/embed/gpi_embed.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 021bce98..086eeaa6 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -183,6 +183,7 @@ void embed_sim_init(gpi_sim_info_t *info) goto cleanup; } + LOG_INFO("Running on %s version %s", info->product, info->version); LOG_INFO("Python interpreter initialised and cocotb loaded!"); // Now that logging has been set up ok we initialise the testbench @@ -212,7 +213,7 @@ void embed_sim_init(gpi_sim_info_t *info) cocotb_retval = PyObject_CallObject(cocotb_init, cocotb_args); if (cocotb_retval != NULL) { - LOG_INFO("_initialise_testbench successful"); + LOG_DEBUG("_initialise_testbench successful"); Py_DECREF(cocotb_retval); } else { PyErr_Print(); @@ -239,8 +240,6 @@ void embed_sim_event(gpi_event_t level, const char *msg) PyGILState_STATE gstate; gstate = PyGILState_Ensure(); - LOG_WARN("Failing the test at simulator request!"); - PyObject *fArgs = PyTuple_New(2); PyTuple_SetItem(fArgs, 0, PyInt_FromLong(level)); PyTuple_SetItem(fArgs, 1, PyString_FromString(msg)); From ccfa798d7433f1787c2dae546903546f89354be6 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 22 Feb 2014 10:26:23 +0000 Subject: [PATCH 0467/2656] Issue #116: Fix up VHPI errors using GHDL, add vhpi_user.h --- lib/Makefile | 4 ++ lib/vhpi_shim/Makefile | 7 +--- lib/vhpi_shim/gpi_vhpi.c | 90 +++++++++++++++++++--------------------- 3 files changed, 48 insertions(+), 53 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index 0bbe8ba8..40d895b0 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -47,12 +47,16 @@ $(LIB_DIR)/libcocotb.so: $(SIM_ROOT)/lib/embed/gpi_embed.c | $(LIB_DIR) $(LIB_DIR)/libgpi.so: $(SIM_ROOT)/lib/vpi_shim/gpi_vpi.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/vpi_shim +$(LIB_DIR)/libgpivhpi.so: $(SIM_ROOT)/lib/vhpi_shim/gpi_vhpi.c | $(LIB_DIR) + make -C $(SIM_ROOT)/lib/vhpi_shim + $(LIB_DIR)/libsim.so: $(SIM_ROOT)/lib/simulator/simulatormodule.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/simulator COCOTB_LIBS = $(LIB_DIR)/libgpilog.so \ $(LIB_DIR)/libcocotb.so \ $(LIB_DIR)/libgpi.so \ + $(LIB_DIR)/libgpivhpi.so \ $(LIB_DIR)/libsim.so clean:: diff --git a/lib/vhpi_shim/Makefile b/lib/vhpi_shim/Makefile index b915c578..a5ab0cc4 100644 --- a/lib/vhpi_shim/Makefile +++ b/lib/vhpi_shim/Makefile @@ -32,14 +32,11 @@ INCLUDES += GCC_ARGS += -DVPI_CHECKING LIBS := -lgpilog -lcocotb LD_PATH := -L$(LIB_DIR) -LIB_NAME := libgpi.so +LIB_NAME := libgpivhpi.so SRCS := gpi_vhpi.c -all: $(LIB_DIR)/cocotb.so - -$(LIB_DIR)/cocotb.so: $(LIB_DIR)/$(LIB_NAME) - ln -sf $(LIB_NAME) $@ +all: $(LIB_DIR)/$(LIB_NAME) clean: -@rm -rf $(LIB_DIR)/gpivpi.vpl diff --git a/lib/vhpi_shim/gpi_vhpi.c b/lib/vhpi_shim/gpi_vhpi.c index 50c06a28..4e7e3ab6 100644 --- a/lib/vhpi_shim/gpi_vhpi.c +++ b/lib/vhpi_shim/gpi_vhpi.c @@ -243,6 +243,7 @@ gpi_sim_hdl gpi_get_root_handle(const char* name) LOG_WARN("VHPI: Root %s doesn't match requested toplevel %s", name, found); FEXIT return NULL; + } rv = gpi_alloc_handle(); rv->sim_hdl = root; @@ -269,7 +270,7 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) } strncpy(buff, name, len); - obj = vpi_handle_by_name(buff, (vhpiHandleT)(parent->sim_hdl)); + obj = vhpi_handle_by_name(buff, (vhpiHandleT)(parent->sim_hdl)); if (!obj) { LOG_DEBUG("VHPI: Handle '%s' not found!", name); // check_vhpi_error(); @@ -300,7 +301,7 @@ gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) gpi_sim_hdl rv; vhpiHandleT obj; - obj = vhpi_handle_by_index((vhpiParamDecls, (vhpiHandleT)(parent->sim_hdl), index); + obj = vhpi_handle_by_index(vhpiParamDecls, (vhpiHandleT)(parent->sim_hdl), index); if (!obj) { LOG_ERROR("VHPI: Handle idx '%d' not found!", index); return NULL; @@ -322,7 +323,7 @@ gpi_iterator_hdl gpi_iterate(uint32_t type, gpi_sim_hdl base) { vhpiHandleT iterator; - iterator = vhpi_iterate(type, (vhpiHandleT)(base->sim_hdl)); + iterator = vhpi_iterator(type, (vhpiHandleT)(base->sim_hdl)); check_vhpi_error(); FEXIT @@ -433,7 +434,7 @@ char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) vhpiValueT value_s = {vhpiBinStrVal}; vhpiValueT *value_p = &value_s; - vpi_get_value((vhpiHandleT)(gpi_hdl->sim_hdl), value_p); + vhpi_get_value((vhpiHandleT)(gpi_hdl->sim_hdl), value_p); check_vhpi_error(); char *result = gpi_copy_name(value_p->value.str); @@ -463,7 +464,7 @@ char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl) // Callback related functions -static int32_t handle_vhpi_callback(p_cb_data cb_data) +static void handle_vhpi_callback(const vhpiCbDataT *cb_data) { FENTER int rv = 0; @@ -475,7 +476,7 @@ static int32_t handle_vhpi_callback(p_cb_data cb_data) if (!user_data) LOG_CRITICAL("VPI: Callback data corrupted"); - user_data->state = VPI_PRE_CALL; + user_data->state = VHPI_PRE_CALL; old_cb = user_data->cb_hdl; rv = user_data->gpi_function(user_data->gpi_cb_data); @@ -485,13 +486,13 @@ static int32_t handle_vhpi_callback(p_cb_data cb_data) /* A request to delete could have been done * inside gpi_function */ - if (user_data->state == VPI_DELETE) + if (user_data->state == VHPI_DELETE) gpi_destroy_cb_handle(&user_data->gpi_hdl); else - user_data->state = VPI_POST_CALL; + user_data->state = VHPI_POST_CALL; FEXIT - return rv; + return; }; /* Allocates memory that will persist for the lifetime of the @@ -505,7 +506,7 @@ gpi_sim_hdl gpi_create_cb_handle(void) p_vhpi_cb_user_data user_data = __gpi_alloc_user(); if (user_data) { - user_data->state = VPI_FREE; + user_data->state = VHPI_FREE; ret = &user_data->gpi_hdl; } @@ -526,8 +527,8 @@ void gpi_destroy_cb_handle(gpi_sim_hdl gpi_hdl) p_vhpi_cb_user_data user_data; user_data = gpi_container_of(gpi_hdl, s_vhpi_cb_user_data, gpi_hdl); - if (user_data->state == VPI_PRE_CALL) { - user_data->state = VPI_DELETE; + if (user_data->state == VHPI_PRE_CALL) { + user_data->state = VHPI_DELETE; } else { gpi_deregister_callback(gpi_hdl); __gpi_free_callback(gpi_hdl); @@ -545,11 +546,7 @@ int gpi_deregister_callback(gpi_sim_hdl gpi_hdl) p_vhpi_cb_user_data user_data; int rc = 1; FENTER - // We should be able to user vpi_get_cb_info - // but this is not implemented in ICARUS - // and gets upset on VCS. So instead we - // do some pointer magic. - + user_data = gpi_container_of(gpi_hdl, s_vhpi_cb_user_data, gpi_hdl); if (user_data->cb_hdl) { @@ -562,7 +559,7 @@ int gpi_deregister_callback(gpi_sim_hdl gpi_hdl) } // Call when the handle relates to a one time callback -// No need to call vpi_deregister_cb as the sim will +// No need to call vhpi_deregister_cb as the sim will // do this but do need to destroy the handle static int gpi_free_one_time(p_vhpi_cb_user_data user_data) { @@ -576,7 +573,7 @@ static int gpi_free_one_time(p_vhpi_cb_user_data user_data) // If the callback has not been called we also need to call // remove as well - if (!user_data->state == VPI_PRIMED) { + if (!user_data->state == VHPI_PRIMED) { rc = vhpi_remove_cb(cb_hdl); if (!rc) { check_vhpi_error(); @@ -637,14 +634,14 @@ int gpi_register_value_change_callback(gpi_sim_hdl cb, user_data->cb_value.format = vhpiIntVal; cb_data_s.reason = vhpiCbValueChange; - cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.cb_rtn = handle_vhpi_callback; cb_data_s.obj = (vhpiHandleT)(gpi_hdl->sim_hdl); cb_data_s.time = &time; cb_data_s.value = &user_data->cb_value; cb_data_s.user_data = (char *)user_data; ret = __gpi_register_cb(user_data, &cb_data_s); - user_data->state = VPI_PRIMED; + user_data->state = VHPI_PRIMED; FEXIT @@ -670,14 +667,14 @@ int gpi_register_readonly_callback(gpi_sim_hdl cb, user_data->gpi_cleanup = gpi_free_one_time; cb_data_s.reason = vhpiCbEndOfTimeStep; - cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.cb_rtn = handle_vhpi_callback; cb_data_s.obj = NULL; cb_data_s.time = &time; cb_data_s.value = NULL; cb_data_s.user_data = (char *)user_data; ret = __gpi_register_cb(user_data, &cb_data_s); - user_data->state = VPI_PRIMED; + user_data->state = VHPI_PRIMED; FEXIT @@ -702,14 +699,14 @@ int gpi_register_readwrite_callback(gpi_sim_hdl cb, user_data->gpi_cleanup = gpi_free_one_time; cb_data_s.reason = vhpiCbEndOfProcesses; - cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.cb_rtn = handle_vhpi_callback; cb_data_s.obj = NULL; cb_data_s.time = &time; cb_data_s.value = NULL; cb_data_s.user_data = (char *)user_data; ret = __gpi_register_cb(user_data, &cb_data_s); - user_data->state = VPI_PRIMED; + user_data->state = VHPI_PRIMED; FEXIT @@ -734,14 +731,14 @@ int gpi_register_nexttime_callback(gpi_sim_hdl cb, user_data->gpi_cleanup = gpi_free_one_time; cb_data_s.reason = vhpiCbNextTimeStep; - cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.cb_rtn = handle_vhpi_callback; cb_data_s.obj = NULL; cb_data_s.time = &time; cb_data_s.value = NULL; cb_data_s.user_data = (char *)user_data; ret = __gpi_register_cb(user_data, &cb_data_s); - user_data->state = VPI_PRIMED; + user_data->state = VHPI_PRIMED; FEXIT @@ -754,7 +751,7 @@ int gpi_register_timed_callback(gpi_sim_hdl cb, { FENTER vhpiCbDataT cb_data_s; - vhpiTimeT time; + vhpiTimeT time_s; p_vhpi_cb_user_data user_data; int ret; @@ -765,18 +762,18 @@ int gpi_register_timed_callback(gpi_sim_hdl cb, user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_one_time; - vpi_time_s.high = (uint32_t)(time_ps>>32); - vpi_time_s.low = (uint32_t)(time_ps); + time_s.high = (uint32_t)(time_ps>>32); + time_s.low = (uint32_t)(time_ps); cb_data_s.reason = vhpiCbAfterDelay; - cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.cb_rtn = handle_vhpi_callback; cb_data_s.obj = NULL; - cb_data_s.time = &vpi_time_s; + cb_data_s.time = &time_s; cb_data_s.value = NULL; cb_data_s.user_data = (char *)user_data; ret = __gpi_register_cb(user_data, &cb_data_s); - user_data->state = VPI_PRIMED; + user_data->state = VHPI_PRIMED; FEXIT @@ -799,7 +796,7 @@ int gpi_register_sim_start_callback(gpi_sim_hdl cb, user_data->gpi_cleanup = gpi_free_one_time; cb_data_s.reason = vhpiCbStartOfSimulation; - cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.cb_rtn = handle_vhpi_callback; cb_data_s.obj = NULL; cb_data_s.time = NULL; cb_data_s.value = NULL; @@ -810,7 +807,7 @@ int gpi_register_sim_start_callback(gpi_sim_hdl cb, * the vlog_startup_routines and so call this routine */ __gpi_register_cb(user_data, &cb_data_s); - user_data->state = VPI_PRIMED; + user_data->state = VHPI_PRIMED; FEXIT return 0; @@ -833,7 +830,7 @@ int gpi_register_sim_end_callback(gpi_sim_hdl cb, user_data->gpi_cleanup = gpi_free_one_time; cb_data_s.reason = vhpiCbEndOfSimulation; - cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.cb_rtn = handle_vhpi_callback; cb_data_s.obj = NULL; cb_data_s.time = NULL; cb_data_s.value = NULL; @@ -844,7 +841,7 @@ int gpi_register_sim_end_callback(gpi_sim_hdl cb, * the vlog_startup_routines and so call this routine */ __gpi_register_cb(user_data, &cb_data_s); - user_data->state = VPI_PRIMED; + user_data->state = VHPI_PRIMED; FEXIT return 0; @@ -909,17 +906,16 @@ void register_embed(void) int handle_sim_init(void *gpi_cb_data) { FENTER - s_vpi_vlog_info info; gpi_sim_info_t sim_info; + sim_info.argc = 0; + sim_info.argv = NULL; + sim_info.product = gpi_copy_name(vhpi_get_str(vhpiNameP, NULL)); + sim_info.version = gpi_copy_name(vhpi_get_str(vhpiToolVersionP, NULL)); + embed_sim_init(&sim_info); - vpi_get_vlog_info(&info); - - sim_info.argc = info.argc; - sim_info.argv = info.argv; - sim_info.product = info.product; - sim_info.version = info.version; + free(sim_info.product); + free(sim_info.version); - embed_sim_init(&sim_info); FEXIT } @@ -967,11 +963,9 @@ void gpi_sim_end(void) // pre-defined VHPI registration table -PLI_VOID(*vhpi_startup_routines[])() = -{ +void (*vhpi_startup_routines[])(void) = { register_embed, register_initial_callback, register_final_callback, 0 }; - From c7edecffe537c0c446a017283b2bbef5e7eaffe6 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 22 Feb 2014 10:28:05 +0000 Subject: [PATCH 0468/2656] Issue #116: Really add vhpi_user.h --- include/vhpi_user.h | 1260 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1260 insertions(+) create mode 100644 include/vhpi_user.h diff --git a/include/vhpi_user.h b/include/vhpi_user.h new file mode 100644 index 00000000..ae37abcc --- /dev/null +++ b/include/vhpi_user.h @@ -0,0 +1,1260 @@ +/* + * |---------------------------------------------------------------| + * | | + * | This is the VHPI header file | + * | | + * | | + * | | + * | FOR CONFORMANCE WITH THE VHPI STANDARD, A VHPI APPLICATION | + * | OR PROGRAM MUST REFERENCE THIS HEADER FILE | + * | Its contents can be modified to include vendor extensions. | + * | | + * |---------------------------------------------------------------| + */ + +/*** File vhpi_user.h ***/ +/*** This file describes the procedural interface to access VHDL + compiled, instantiated and run-time data. It is derived from + the UML model. ***/ + +#ifndef VHPI_USER_H +#define VHPI_USER_H +#include +#include +/* Ensure that size-critical types are defined on all OS platforms. */ +#if defined(_MSC_VER) + typedef unsigned __int64 uint64_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int8 uint8_t; + typedef signed __int64 int64_t; + typedef signed __int32 int32_t; + typedef signed __int8 int8_t; +#elif defined(__MINGW32__) + #include +#elif defined(__linux) + #include +#else + #include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#pragma pack(8) + +/*---------------------------------------------------------------------------*/ +/*--------------------------- Portability Help ------------------------------*/ +/*---------------------------------------------------------------------------*/ +/* Use to import/export a symbol */ +#if defined(WIN32) || defined(WIN64) + #ifndef PLI_DLLISPEC + #define PLI_DLLISPEC __declspec(dllimport) + #define VHPI_USER_DEFINED_DLLISPEC 1 + #endif + #ifndef PLI_DLLESPEC + #define PLI_DLLESPEC __declspec(dllexport) + #define VHPI_USER_DEFINED_DLLESPEC 1 + #endif +#else + #ifndef PLI_DLLISPEC + #define PLI_DLLISPEC + #endif + #ifndef PLI_DLLESPEC + #define PLI_DLLESPEC + #endif +#endif + +/* Use to mark a function as external */ +#ifndef PLI_EXTERN + #define PLI_EXTERN +#endif + +/* Use to mark a variable as external */ +#ifndef PLI_VEXTERN + #define PLI_VEXTERN extern +#endif + +#ifndef PLI_PROTOTYPES + #define PLI_PROTOTYPES + /* object is defined imported by the application */ + #define XXTERN PLI_EXTERN PLI_DLLISPEC + /* object is exported by the application */ + #define EETERN PLI_EXTERN PLI_DLLESPEC +#endif + +/* basic typedefs */ +#ifndef VHPI_TYPES + #define VHPI_TYPES + typedef uint32_t *vhpiHandleT; + typedef uint32_t vhpiEnumT; + typedef uint8_t vhpiSmallEnumT; + typedef uint32_t vhpiIntT; + typedef uint64_t vhpiLongIntT; + typedef char vhpiCharT; + typedef double vhpiRealT; + typedef uint32_t vhpiSmallPhysT; + + typedef struct vhpiPhysS + { + int32_t high; + uint32_t low; + } vhpiPhysT; + + /* Sized variables */ + #ifndef PLI_TYPES + #define PLI_TYPES + typedef int PLI_INT32; + typedef unsigned int PLI_UINT32; + typedef short PLI_INT16; + typedef unsigned short PLI_UINT16; + typedef char PLI_BYTE8; + typedef unsigned char PLI_UBYTE8; + typedef void PLI_VOID; + #endif + + /********************** time structure ****************************/ + typedef struct vhpiTimeS + { + uint32_t high; + uint32_t low; + } vhpiTimeT; + + /********************** value structure **************************/ + + /* value formats */ + typedef enum + { + vhpiBinStrVal = 1, /* do not move */ + vhpiOctStrVal = 2, /* do not move */ + vhpiDecStrVal = 3, /* do not move */ + vhpiHexStrVal = 4, /* do not move */ + vhpiEnumVal = 5, + vhpiIntVal = 6, + vhpiLogicVal = 7, + vhpiRealVal = 8, + vhpiStrVal = 9, + vhpiCharVal = 10, + vhpiTimeVal = 11, + vhpiPhysVal = 12, + vhpiObjTypeVal = 13, + vhpiPtrVal = 14, + vhpiEnumVecVal = 15, + vhpiIntVecVal = 16, + vhpiLogicVecVal = 17, + vhpiRealVecVal = 18, + vhpiTimeVecVal = 19, + vhpiPhysVecVal = 20, + vhpiPtrVecVal = 21, + vhpiRawDataVal = 22, + vhpiSmallEnumVal = 23, + vhpiSmallEnumVecVal = 24, + vhpiLongIntVal = 25, + vhpiLongIntVecVal = 26, + vhpiSmallPhysVal = 27, + vhpiSmallPhysVecVal = 28 + + #ifdef VHPIEXTEND_VAL_FORMATS + VHPIEXTEND_VAL_FORMATS + #endif + } vhpiFormatT; + + /* value structure */ + typedef struct vhpiValueS + { + vhpiFormatT format; /* vhpi[Char,[Bin,Oct,Dec,Hex]Str, + [Small]Enum,Logic,Int,Real,[Small]Phys,Time,Ptr, + [Small]EnumVec,LogicVec,IntVect,RealVec,[Small]PhysVec,TimeVec, + PtrVec,ObjType,RawData]Val */ + size_t bufSize; /* the size in bytes of the value buffer; this is set by the user */ + int32_t numElems; + /* different meanings depending on the format: + vhpiStrVal, vhpi{Bin...}StrVal: size of string + array type values: number of array elements + scalar type values: undefined + */ + + vhpiPhysT unit; + union + { + vhpiEnumT enumv, *enumvs; + vhpiSmallEnumT smallenumv, *smallenumvs; + vhpiIntT intg, *intgs; + vhpiLongIntT longintg, *longintgs; + vhpiRealT real, *reals; + vhpiSmallPhysT smallphys, *smallphyss; + vhpiPhysT phys, *physs; + vhpiTimeT time, *times; + vhpiCharT ch, *str; + void *ptr, **ptrs; + } value; + } vhpiValueT; +#endif + +/* Following are the constant definitions. They are divided into + three major areas: + + 1) object types + + 2) access methods + + 3) properties + +*/ + +#define vhpiUndefined -1 + +/*************** OBJECT KINDS *******************/ +typedef enum +{ + vhpiAccessTypeDeclK = 1001, + vhpiAggregateK = 1002, + vhpiAliasDeclK = 1003, + vhpiAllK = 1004, + vhpiAllocatorK = 1005, + vhpiAnyCollectionK = 1006, + vhpiArchBodyK = 1007, + vhpiArgvK = 1008, + vhpiArrayTypeDeclK = 1009, + vhpiAssertStmtK = 1010, + vhpiAssocElemK = 1011, + vhpiAttrDeclK = 1012, + vhpiAttrSpecK = 1013, + vhpiBinaryExprK = 1014, /* DEPRECATED */ + vhpiBitStringLiteralK = 1015, + vhpiBlockConfigK = 1016, + vhpiBlockStmtK = 1017, + vhpiBranchK = 1018, + vhpiCallbackK = 1019, + vhpiCaseStmtK = 1020, + vhpiCharLiteralK = 1021, + vhpiCompConfigK = 1022, + vhpiCompDeclK = 1023, + vhpiCompInstStmtK = 1024, + vhpiCondSigAssignStmtK = 1025, + vhpiCondWaveformK = 1026, + vhpiConfigDeclK = 1027, + vhpiConstDeclK = 1028, + vhpiConstParamDeclK = 1029, + vhpiConvFuncK = 1030, + vhpiDerefObjK = 1031, + vhpiDisconnectSpecK = 1032, + vhpiDriverK = 1033, + vhpiDriverCollectionK = 1034, + vhpiElemAssocK = 1035, + vhpiElemDeclK = 1036, + vhpiEntityClassEntryK = 1037, + vhpiEntityDeclK = 1038, + vhpiEnumLiteralK = 1039, + vhpiEnumRangeK = 1040, + vhpiEnumTypeDeclK = 1041, + vhpiExitStmtK = 1042, + vhpiFileDeclK = 1043, + vhpiFileParamDeclK = 1044, + vhpiFileTypeDeclK = 1045, + vhpiFloatRangeK = 1046, + vhpiFloatTypeDeclK = 1047, + vhpiForGenerateK = 1048, + vhpiForLoopK = 1049, + vhpiForeignfK = 1050, + vhpiFuncCallK = 1051, + vhpiFuncDeclK = 1052, + vhpiGenericDeclK = 1053, + vhpiGroupDeclK = 1054, + vhpiGroupTempDeclK = 1055, + vhpiIfGenerateK = 1056, + vhpiIfStmtK = 1057, + vhpiInPortK = 1058, + vhpiIndexedNameK = 1059, + vhpiIntLiteralK = 1060, + vhpiIntRangeK = 1061, + vhpiIntTypeDeclK = 1062, + vhpiIteratorK = 1063, + vhpiLibraryDeclK = 1064, + vhpiLoopStmtK = 1065, + vhpiNextStmtK = 1066, + vhpiNullLiteralK = 1067, + vhpiNullStmtK = 1068, + vhpiOperatorK = 1069, + vhpiOthersK = 1070, + vhpiOutPortK = 1071, + vhpiPackBodyK = 1072, + vhpiPackDeclK = 1073, + vhpiPackInstK = 1074, + vhpiParamAttrNameK = 1075, + vhpiPhysLiteralK = 1076, + vhpiPhysRangeK = 1077, + vhpiPhysTypeDeclK = 1078, + vhpiPortDeclK = 1079, + vhpiProcCallStmtK = 1080, + vhpiProcDeclK = 1081, + vhpiProcessStmtK = 1082, + vhpiProtectedTypeK = 1083, + vhpiProtectedTypeBodyK = 1084, + vhpiProtectedTypeDeclK = 1085, + vhpiRealLiteralK = 1086, + vhpiRecordTypeDeclK = 1087, + vhpiReportStmtK = 1088, + vhpiReturnStmtK = 1089, + vhpiRootInstK = 1090, + vhpiSelectSigAssignStmtK = 1091, + vhpiSelectWaveformK = 1092, + vhpiSelectedNameK = 1093, + vhpiSigDeclK = 1094, + vhpiSigParamDeclK = 1095, + vhpiSimpAttrNameK = 1096, + vhpiSimpleSigAssignStmtK = 1097, + vhpiSliceNameK = 1098, + vhpiStringLiteralK = 1099, + vhpiSubpBodyK = 1100, + vhpiSubtypeDeclK = 1101, + vhpiSubtypeIndicK = 1102, /* DEPRECATED */ + vhpiToolK = 1103, + vhpiTransactionK = 1104, + vhpiTypeConvK = 1105, + vhpiUnaryExprK = 1106, /* DEPRECATED */ + vhpiUnitDeclK = 1107, + vhpiUserAttrNameK = 1108, + vhpiVarAssignStmtK = 1109, + vhpiVarDeclK = 1110, + vhpiVarParamDeclK = 1111, + vhpiWaitStmtK = 1112, + vhpiWaveformElemK = 1113, + vhpiWhileLoopK = 1114, + vhpiQualifiedExprK = 1115, + vhpiUseClauseK = 1116, + +#ifdef VHPIEXTEND_CLASSES + VHPIEXTEND_CLASSES +#endif + /* private vendor extensions */ + vhpiVerilog = 1117, + vhpiEdifUnit = 1118, + vhpiCollectionK = 1119, + vhpiVHDL = 1120, + vhpiSystemC = 1121 +} vhpiClassKindT; + +/************** methods used to traverse 1 to 1 relationships *******************/ +typedef enum +{ + vhpiAbstractLiteral = 1301, + vhpiActual = 1302, + vhpiAll = 1303, + vhpiAttrDecl = 1304, + vhpiAttrSpec = 1305, + vhpiBaseType = 1306, + vhpiBaseUnit = 1307, + vhpiBasicSignal = 1308, + vhpiBlockConfig = 1309, + vhpiCaseExpr = 1310, + vhpiCondExpr = 1311, + vhpiConfigDecl = 1312, + vhpiConfigSpec = 1313, + vhpiConstraint = 1314, + vhpiContributor = 1315, + vhpiCurCallback = 1316, + vhpiCurEqProcess = 1317, + vhpiCurStackFrame = 1318, + vhpiDerefObj = 1319, + vhpiDecl = 1320, + vhpiDesignUnit = 1321, + vhpiDownStack = 1322, + vhpiElemSubtype = 1323, /* DEPRECATED */ + vhpiEntityAspect = 1324, + vhpiEntityDecl = 1325, + vhpiEqProcessStmt = 1326, + vhpiExpr = 1327, + vhpiFormal = 1328, + vhpiFuncDecl = 1329, + vhpiGroupTempDecl = 1330, + vhpiGuardExpr = 1331, + vhpiGuardSig = 1332, + vhpiImmRegion = 1333, + vhpiInPort = 1334, + vhpiInitExpr = 1335, + vhpiIterScheme = 1336, + vhpiLeftExpr = 1337, + vhpiLexicalScope = 1338, + vhpiLhsExpr = 1339, + vhpiLocal = 1340, + vhpiLogicalExpr = 1341, + vhpiName = 1342, + vhpiOperator = 1343, + vhpiOthers = 1344, + vhpiOutPort = 1345, + vhpiParamDecl = 1346, + vhpiParamExpr = 1347, + vhpiParent = 1348, + vhpiPhysLiteral = 1349, + vhpiPrefix = 1350, + vhpiPrimaryUnit = 1351, + vhpiProtectedTypeBody = 1352, + vhpiProtectedTypeDecl = 1353, + vhpiRejectTime = 1354, + vhpiReportExpr = 1355, + vhpiResolFunc = 1356, + vhpiReturnExpr = 1357, + vhpiReturnTypeMark = 1358, /* DEPRECATED */ + vhpiRhsExpr = 1359, + vhpiRightExpr = 1360, + vhpiRootInst = 1361, + vhpiSelectExpr = 1362, + vhpiSeverityExpr = 1363, + vhpiSimpleName = 1364, + vhpiSubpBody = 1365, + vhpiSubpDecl = 1366, + vhpiSubtype = 1367, /* DEPRECATED */ + vhpiSuffix = 1368, + vhpiTimeExpr = 1369, + vhpiTimeOutExpr = 1370, + vhpiTool = 1371, + vhpiType = 1372, + vhpiTypeMark = 1373, /* DEPRECATED */ + vhpiTypespec , + vhpiUnitDecl = 1374, + vhpiUpStack = 1375, + vhpiUpperRegion = 1376, + vhpiUse = 1377, + vhpiValExpr = 1378, + vhpiValSubtype = 1379, /* DEPRECATED */ + vhpiElemType = 1380, + vhpiFirstNamedType = 1381, + vhpiReturnType = 1382, + vhpiValType = 1383, + vhpiCurRegion = 1384 + +#ifdef VHPIEXTEND_ONE_METHODS + VHPIEXTEND_ONE_METHODS +#endif +} vhpiOneToOneT; + +/************** methods used to traverse 1 to many relationships *******************/ +typedef enum +{ + vhpiAliasDecls = 1501, + vhpiArgvs = 1502, + vhpiAttrDecls = 1503, + vhpiAttrSpecs = 1504, + vhpiBasicSignals = 1505, + vhpiBlockStmts = 1506, + vhpiBranchs = 1507, + /* 1508 */ + vhpiChoices = 1509, + vhpiCompInstStmts = 1510, + vhpiCondExprs = 1511, + vhpiCondWaveforms = 1512, + vhpiConfigItems = 1513, + vhpiConfigSpecs = 1514, + vhpiConstDecls = 1515, + vhpiConstraints = 1516, + vhpiContributors = 1517, + /* 1518 */ + vhpiDecls = 1519, + vhpiDepUnits = 1520, + vhpiDesignUnits = 1521, + vhpiDrivenSigs = 1522, + vhpiDrivers = 1523, + vhpiElemAssocs = 1524, + vhpiEntityClassEntrys = 1525, + vhpiEntityDesignators = 1526, + vhpiEnumLiterals = 1527, + vhpiForeignfs = 1528, + vhpiGenericAssocs = 1529, + vhpiGenericDecls = 1530, + vhpiIndexExprs = 1531, + vhpiIndexedNames = 1532, + vhpiInternalRegions = 1533, + vhpiMembers = 1534, + vhpiPackInsts = 1535, + vhpiParamAssocs = 1536, + vhpiParamDecls = 1537, + vhpiPortAssocs = 1538, + vhpiPortDecls = 1539, + vhpiRecordElems = 1540, + vhpiSelectWaveforms = 1541, + vhpiSelectedNames = 1542, + vhpiSensitivitys = 1543, + vhpiSeqStmts = 1544, + vhpiSigAttrs = 1545, + vhpiSigDecls = 1546, + vhpiSigNames = 1547, + vhpiSignals = 1548, + vhpiSpecNames = 1549, + vhpiSpecs = 1550, + vhpiStmts = 1551, + vhpiTransactions = 1552, + vhpiTypeMarks = 1553, /* DEPRECATED */ + vhpiUnitDecls = 1554, + vhpiUses = 1555, + vhpiVarDecls = 1556, + vhpiWaveformElems = 1557, + vhpiLibraryDecls = 1558, + vhpiLocalLoads = 1559, + vhpiOptimizedLoads = 1560, + vhpiTypes = 1561, + vhpiUseClauses = 1562, + +#ifdef VHPIEXTEND_MANY_METHODS + VHPIEXTEND_MANY_METHODS +#endif + vhpiCallbacks = 1563, + vhpiCurRegions = 1564 +} vhpiOneToManyT; + +/****************** PROPERTIES *******************/ +/******* INTEGER or BOOLEAN PROPERTIES **********/ +typedef enum +{ + vhpiAccessP = 1001, + vhpiArgcP = 1002, + vhpiAttrKindP = 1003, + vhpiBaseIndexP = 1004, + vhpiBeginLineNoP = 1005, + vhpiEndLineNoP = 1006, + vhpiEntityClassP = 1007, + vhpiForeignKindP = 1008, + vhpiFrameLevelP = 1009, + vhpiGenerateIndexP = 1010, + vhpiIntValP = 1011, + vhpiIsAnonymousP = 1012, + vhpiIsBasicP = 1013, + vhpiIsCompositeP = 1014, + vhpiIsDefaultP = 1015, + vhpiIsDeferredP = 1016, + vhpiIsDiscreteP = 1017, + vhpiIsForcedP = 1018, + vhpiIsForeignP = 1019, + vhpiIsGuardedP = 1020, + vhpiIsImplicitDeclP = 1021, + vhpiIsInvalidP = 1022, /* DEPRECATED */ + vhpiIsLocalP = 1023, + vhpiIsNamedP = 1024, + vhpiIsNullP = 1025, + vhpiIsOpenP = 1026, + vhpiIsPLIP = 1027, + vhpiIsPassiveP = 1028, + vhpiIsPostponedP = 1029, + vhpiIsProtectedTypeP = 1030, + vhpiIsPureP = 1031, + vhpiIsResolvedP = 1032, + vhpiIsScalarP = 1033, + vhpiIsSeqStmtP = 1034, + vhpiIsSharedP = 1035, + vhpiIsTransportP = 1036, + vhpiIsUnaffectedP = 1037, + vhpiIsUnconstrainedP = 1038, + vhpiIsUninstantiatedP = 1039, + vhpiIsUpP = 1040, + vhpiIsVitalP = 1041, + vhpiIteratorTypeP = 1042, + vhpiKindP = 1043, + vhpiLeftBoundP = 1044, + vhpiLevelP = 1045, /* DEPRECATED */ + vhpiLineNoP = 1046, + vhpiLineOffsetP = 1047, + vhpiLoopIndexP = 1048, + vhpiModeP = 1049, + vhpiNumDimensionsP = 1050, + vhpiNumFieldsP = 1051, /* DEPRECATED */ + vhpiNumGensP = 1052, + vhpiNumLiteralsP = 1053, + vhpiNumMembersP = 1054, + vhpiNumParamsP = 1055, + vhpiNumPortsP = 1056, + vhpiOpenModeP = 1057, + vhpiPhaseP = 1058, + vhpiPositionP = 1059, + vhpiPredefAttrP = 1060, + /* 1061 */ + vhpiReasonP = 1062, + vhpiRightBoundP = 1063, + vhpiSigKindP = 1064, + vhpiSizeP = 1065, + vhpiStartLineNoP = 1066, + vhpiStateP = 1067, + vhpiStaticnessP = 1068, + vhpiVHDLversionP = 1069, + vhpiIdP = 1070, + vhpiCapabilitiesP = 1071, + +#ifdef VHPIEXTEND_INT_PROPERTIES + VHPIEXTEND_INT_PROPERTIES +#endif + vhpiIsStdLogicP = 1072, + vhpiIsStdULogicP = 1073, + vhpiIsStdLogicVectorP = 1074, + vhpiIsStdULogicVectorP = 1075, +# + vhpiLanguageP = 1200 +} vhpiIntPropertyT; + +/******* STRING PROPERTIES **********/ +typedef enum +{ + vhpiCaseNameP = 1301, + vhpiCompNameP = 1302, + vhpiDefNameP = 1303, + vhpiFileNameP = 1304, + vhpiFullCaseNameP = 1305, + vhpiFullNameP = 1306, + vhpiKindStrP = 1307, + vhpiLabelNameP = 1308, + vhpiLibLogicalNameP = 1309, + vhpiLibPhysicalNameP = 1310, + vhpiLogicalNameP = 1311, + vhpiLoopLabelNameP = 1312, + vhpiNameP = 1313, + vhpiOpNameP = 1314, + vhpiStrValP = 1315, + vhpiToolVersionP = 1316, + vhpiUnitNameP = 1317, + vhpiSaveRestartLocationP = 1318 + +#ifdef VHPIEXTEND_STR_PROPERTIES + VHPIEXTEND_STR_PROPERTIES +#endif +} vhpiStrPropertyT; + +/******* REAL PROPERTIES **********/ +typedef enum +{ + vhpiFloatLeftBoundP = 1601, + vhpiFloatRightBoundP = 1602, + vhpiRealValP = 1603 + +#ifdef VHPIEXTEND_REAL_PROPERTIES + VHPIEXTEND_REAL_PROPERTIES +#endif +} vhpiRealPropertyT; + +/******* PHYSICAL PROPERTIES **********/ +typedef enum +{ + vhpiPhysLeftBoundP = 1651, + vhpiPhysPositionP = 1652, + vhpiPhysRightBoundP = 1653, + vhpiPhysValP = 1654, + vhpiPrecisionP = 1655, /* DEPRECATED */ + vhpiSimTimeUnitP = 1656, /* DEPRECATED */ + vhpiResolutionLimitP = 1657 + +#ifdef VHPIEXTEND_PHYS_PROPERTIES + VHPIEXTEND_PHYS_PROPERTIES +#endif +} vhpiPhysPropertyT; + +/******************* PROPERTY VALUES ************************/ + +/* vhpiCapabilitiesP */ +typedef enum +{ + vhpiProvidesHierarchy = 1, + vhpiProvidesStaticAccess = 2, + vhpiProvidesConnectivity = 4, + vhpiProvidesPostAnalysis = 8, + vhpiProvidesForeignModel = 16, + vhpiProvidesAdvancedForeignModel = 32, + vhpiProvidesSaveRestart = 64, + vhpiProvidesReset = 128, + vhpiProvidesDebugRuntime = 256, + vhpiProvidesAdvancedDebugRuntime = 512, + vhpiProvidesDynamicElab = 1024 +} vhpiCapabibilityT; + + +/* vhpiOpenModeP */ +typedef enum +{ + vhpiInOpen = 1001, + vhpiOutOpen = 1002, + vhpiReadOpen = 1003, + vhpiWriteOpen = 1004, + vhpiAppendOpen = 1005 +} vhpiOpenModeT; + +/* vhpiModeP */ +typedef enum +{ + vhpiInMode = 1001, + vhpiOutMode = 1002, + vhpiInoutMode = 1003, + vhpiBufferMode = 1004, + vhpiLinkageMode = 1005 +} vhpiModeT; + +/* vhpiSigKindP */ +typedef enum +{ + vhpiRegister = 1001, + vhpiBus = 1002, + vhpiNormal = 1003 +} vhpiSigKindT; + +/* vhpiStaticnessP */ +typedef enum +{ + vhpiLocallyStatic = 1001, + vhpiGloballyStatic = 1002, + vhpiDynamic = 1003 +} vhpiStaticnessT; + +/* vhpiPredefAttrP */ +typedef enum +{ + vhpiActivePA = 1001, + vhpiAscendingPA = 1002, + vhpiBasePA = 1003, + vhpiDelayedPA = 1004, + vhpiDrivingPA = 1005, + vhpiDriving_valuePA = 1006, + vhpiEventPA = 1007, + vhpiHighPA = 1008, + vhpiImagePA = 1009, + vhpiInstance_namePA = 1010, + vhpiLast_activePA = 1011, + vhpiLast_eventPA = 1012, + vhpiLast_valuePA = 1013, + vhpiLeftPA = 1014, + vhpiLeftofPA = 1015, + vhpiLengthPA = 1016, + vhpiLowPA = 1017, + vhpiPath_namePA = 1018, + vhpiPosPA = 1019, + vhpiPredPA = 1020, + vhpiQuietPA = 1021, + vhpiRangePA = 1022, + vhpiReverse_rangePA = 1023, + vhpiRightPA = 1024, + vhpiRightofPA = 1025, + vhpiSimple_namePA = 1026, + vhpiStablePA = 1027, + vhpiSuccPA = 1028, + vhpiTransactionPA = 1029, + vhpiValPA = 1030, + vhpiValuePA = 1031 +} vhpiPredefAttrT; + +/* vhpiAttrKindP */ +typedef enum +{ + vhpiFunctionAK = 1, + vhpiRangeAK = 2, + vhpiSignalAK = 3, + vhpiTypeAK = 4, + vhpiValueAK = 5 + +#ifdef VHPIEXTEND_INT_ATTR + VHPI_EXTEND_INT_ATTR +#endif +} vhpiAttrKindT; + +/* vhpiEntityClassP */ +typedef enum +{ + vhpiEntityEC = 1001, + vhpiArchitectureEC = 1002, + vhpiConfigurationEC = 1003, + vhpiProcedureEC = 1004, + vhpiFunctionEC = 1005, + vhpiPackageEC = 1006, + vhpiTypeEC = 1007, + vhpiSubtypeEC = 1008, + vhpiConstantEC = 1009, + vhpiSignalEC = 1010, + vhpiVariableEC = 1011, + vhpiComponentEC = 1012, + vhpiLabelEC = 1013, + vhpiLiteralEC = 1014, + vhpiUnitsEC = 1015, + vhpiFileEC = 1016, + vhpiGroupEC = 1017 +} vhpiEntityClassT; + +/* vhpiAccessP */ +typedef enum +{ + vhpiRead = 1, + vhpiWrite = 2, + vhpiConnectivity = 4, + vhpiNoAccess = 8 +} vhpiAccessT; + +/* value for vhpiStateP property for callbacks */ +typedef enum +{ + vhpiEnable, + vhpiDisable, + vhpiMature /* callback has occurred */ +} vhpiStateT; + +/* enumeration type for vhpiCompInstKindP property */ +typedef enum +{ + vhpiDirect, + vhpiComp, + vhpiConfig +} vhpiCompInstKindT; + + +/* the following values are used only for the + vhpiResolutionLimitP property and for setting the unit field of the value + structure; they represent the physical position of a given + VHDL time unit */ +/* time unit physical position values {high, low} */ +PLI_VEXTERN PLI_DLLISPEC const vhpiPhysT vhpiFS; +PLI_VEXTERN PLI_DLLISPEC const vhpiPhysT vhpiPS; +PLI_VEXTERN PLI_DLLISPEC const vhpiPhysT vhpiNS; +PLI_VEXTERN PLI_DLLISPEC const vhpiPhysT vhpiUS; +PLI_VEXTERN PLI_DLLISPEC const vhpiPhysT vhpiMS; +PLI_VEXTERN PLI_DLLISPEC const vhpiPhysT vhpiS; +PLI_VEXTERN PLI_DLLISPEC const vhpiPhysT vhpiMN; +PLI_VEXTERN PLI_DLLISPEC const vhpiPhysT vhpiHR; + +/* IEEE std_logic values */ +#define vhpiU 0 /* uninitialized */ +#define vhpiX 1 /* unknown */ +#define vhpi0 2 /* forcing 0 */ +#define vhpi1 3 /* forcing 1 */ +#define vhpiZ 4 /* high impedance */ +#define vhpiW 5 /* weak unknown */ +#define vhpiL 6 /* weak 0 */ +#define vhpiH 7 /* weak 1 */ +#define vhpiDontCare 8 /* don't care */ + +/* IEEE std bit values */ +#define vhpibit0 0 /* bit 0 */ +#define vhpibit1 1 /* bit 1 */ + +/* IEEE std boolean values */ +#define vhpiFalse 0 /* false */ +#define vhpiTrue 1 /* true */ + +/************** vhpiPhaseP property values *************/ +typedef enum +{ + vhpiRegistrationPhase = 1, + vhpiAnalysisPhase = 2, + vhpiElaborationPhase = 3, + vhpiInitializationPhase = 4, + vhpiSimulationPhase = 5, + vhpiTerminationPhase = 6, + vhpiSavePhase = 7, + vhpiRestartPhase = 8, + vhpiResetPhase = 9 +} vhpiPhaseT ; + +/**************** PLI error information structure ****************/ + +typedef enum +{ + vhpiNote = 1, + vhpiWarning = 2, + vhpiError = 3, + vhpiFailure = 6, + vhpiSystem = 4, + vhpiInternal = 5 +} vhpiSeverityT; + +typedef struct vhpiErrorInfoS +{ + vhpiSeverityT severity; + char *message; + char *str; + char *file; /* Name of the VHDL file where the VHPI error originated */ + int32_t line; /* Line number in the VHDL file */ +} vhpiErrorInfoT; + +/********************* callback structures ************************/ +/* callback user data structure */ + +typedef struct vhpiCbDataS +{ + int32_t reason; /* callback reason */ + void (*cb_rtn) (const struct vhpiCbDataS *); /* call routine */ + vhpiHandleT obj; /* trigger object */ + vhpiTimeT *time; /* callback time */ + vhpiValueT *value; /* trigger object value */ + void *user_data; /* pointer to user data to be passed to the callback function */ +} vhpiCbDataT; + +/**************************** CALLBACK REASONS ****************************/ +/*************************** Simulation object related ***********************/ +/* These are repetitive callbacks */ +#define vhpiCbValueChange 1001 +#define vhpiCbForce 1002 +#define vhpiCbRelease 1003 +#define vhpiCbTransaction 1004 /* optional callback reason */ + +/****************************** Statement related ****************************/ +/* These are repetitive callbacks */ +#define vhpiCbStmt 1005 +#define vhpiCbResume 1006 +#define vhpiCbSuspend 1007 +#define vhpiCbStartOfSubpCall 1008 +#define vhpiCbEndOfSubpCall 1009 + +/****************************** Time related ******************************/ +/* the Rep callback reasons are the repeated versions of the callbacks */ + +#define vhpiCbAfterDelay 1010 +#define vhpiCbRepAfterDelay 1011 + +/*************************** Simulation cycle phase related *****************/ +#define vhpiCbNextTimeStep 1012 +#define vhpiCbRepNextTimeStep 1013 +#define vhpiCbStartOfNextCycle 1014 +#define vhpiCbRepStartOfNextCycle 1015 +#define vhpiCbStartOfProcesses 1016 +#define vhpiCbRepStartOfProcesses 1017 +#define vhpiCbEndOfProcesses 1018 +#define vhpiCbRepEndOfProcesses 1019 +#define vhpiCbLastKnownDeltaCycle 1020 +#define vhpiCbRepLastKnownDeltaCycle 1021 +#define vhpiCbStartOfPostponed 1022 +#define vhpiCbRepStartOfPostponed 1023 +#define vhpiCbEndOfTimeStep 1024 +#define vhpiCbRepEndOfTimeStep 1025 + +/***************************** Action related *****************************/ +/* these are one time callback unless otherwise noted */ +#define vhpiCbStartOfTool 1026 +#define vhpiCbEndOfTool 1027 +#define vhpiCbStartOfAnalysis 1028 +#define vhpiCbEndOfAnalysis 1029 +#define vhpiCbStartOfElaboration 1030 +#define vhpiCbEndOfElaboration 1031 +#define vhpiCbStartOfInitialization 1032 +#define vhpiCbEndOfInitialization 1033 +#define vhpiCbStartOfSimulation 1034 +#define vhpiCbEndOfSimulation 1035 +#define vhpiCbQuiescense 1036 /* repetitive */ +#define vhpiCbPLIError 1037 /* repetitive */ +#define vhpiCbStartOfSave 1038 +#define vhpiCbEndOfSave 1039 +#define vhpiCbStartOfRestart 1040 +#define vhpiCbEndOfRestart 1041 +#define vhpiCbStartOfReset 1042 +#define vhpiCbEndOfReset 1043 +#define vhpiCbEnterInteractive 1044 /* repetitive */ +#define vhpiCbExitInteractive 1045 /* repetitive */ +#define vhpiCbSigInterrupt 1046 /* repetitive */ + +/* Foreign model callbacks */ +#define vhpiCbTimeOut 1047 /* non repetitive */ +#define vhpiCbRepTimeOut 1048 /* repetitive */ +#define vhpiCbSensitivity 1049 /* repetitive */ + +/**************************** CALLBACK FLAGS ******************************/ +#define vhpiReturnCb 0x00000001 +#define vhpiDisableCb 0x00000010 + +/************** vhpiAutomaticRestoreP property values *************/ +typedef enum +{ + vhpiRestoreAll = 1, + vhpiRestoreUserData = 2, + vhpiRestoreHandles = 4, + vhpiRestoreCallbacks = 8 +} vhpiAutomaticRestoreT; + + +/******************** FUNCTION DECLARATIONS *********************/ + +XXTERN int vhpi_assert (vhpiSeverityT severity, + const char *formatmsg, + ...); + +/* callback related */ + +XXTERN vhpiHandleT vhpi_register_cb (vhpiCbDataT *cb_data_p, + int32_t flags); + +XXTERN int vhpi_remove_cb (vhpiHandleT cb_obj); + +XXTERN int vhpi_disable_cb (vhpiHandleT cb_obj); + +XXTERN int vhpi_enable_cb (vhpiHandleT cb_obj); + +XXTERN int vhpi_get_cb_info (vhpiHandleT object, + vhpiCbDataT *cb_data_p); + +/* utilities for sensitivity-set bitmaps */ +/* The replacement text for these macros is implementation defined */ +/* The behavior is specified in G.1 */ +XXTERN int vhpi_sens_first (vhpiValueT *sens); + +XXTERN int vhpi_sens_zero (vhpiValueT *sens); + +XXTERN int vhpi_sens_clr (int obj, + vhpiValueT *sens); + +XXTERN int vhpi_sens_set (int obj, + vhpiValueT *sens); + +XXTERN int vhpi_sens_isset (int obj, + vhpiValueT *sens); + +#define VHPI_SENS_ZERO(sens) vhpi_sens_zero(sens) +#define VHPI_SENS_SET(obj, sens) vhpi_sens_set(obj, sens) +#define VHPI_SENS_CLR(obj, sens) vhpi_sens_clr(obj, sens) +#define VHPI_SENS_ISSET(obj, sens) vhpi_sens_isset(obj, sens) +#define VHPI_SENS_FIRST(sens) vhpi_sens_first(sens) + +/* for obtaining handles */ + +XXTERN vhpiHandleT vhpi_handle_by_name (const char *name, + vhpiHandleT scope); + +XXTERN vhpiHandleT vhpi_handle_by_index (vhpiOneToManyT itRel, + vhpiHandleT parent, + int32_t indx); + +/* for traversing relationships */ + +XXTERN vhpiHandleT vhpi_handle (vhpiOneToOneT type, + vhpiHandleT referenceHandle); + +XXTERN vhpiHandleT vhpi_iterator (vhpiOneToManyT type, + vhpiHandleT referenceHandle); + +XXTERN vhpiHandleT vhpi_scan (vhpiHandleT iterator); + +/* for processsing properties */ + +XXTERN vhpiIntT vhpi_get (vhpiIntPropertyT property, + vhpiHandleT object); + +XXTERN const vhpiCharT * vhpi_get_str (vhpiStrPropertyT property, + vhpiHandleT object); + +XXTERN vhpiRealT vhpi_get_real (vhpiRealPropertyT property, + vhpiHandleT object); + +XXTERN vhpiPhysT vhpi_get_phys (vhpiPhysPropertyT property, + vhpiHandleT object); + +/* for access to protected types */ + +typedef int (*vhpiUserFctT)(void); + +XXTERN int vhpi_protected_call (vhpiHandleT varHdl, + vhpiUserFctT userFct, + void *userData); + +/* value processing */ + +/* vhpi_put_value flags */ +typedef enum +{ + vhpiDeposit, + vhpiDepositPropagate, + vhpiForce, + vhpiForcePropagate, + vhpiRelease, + vhpiSizeConstraint +} vhpiPutValueModeT; + +typedef enum +{ + vhpiInertial, + vhpiTransport +} vhpiDelayModeT; + +XXTERN int vhpi_get_value (vhpiHandleT expr, + vhpiValueT *value_p); + +XXTERN int vhpi_put_value (vhpiHandleT object, + vhpiValueT *value_p, + vhpiPutValueModeT flags); + +XXTERN int vhpi_schedule_transaction (vhpiHandleT drivHdl, + vhpiValueT *value_p, + uint32_t numValues, + vhpiTimeT *delayp, + vhpiDelayModeT delayMode, + vhpiTimeT *pulseRejp); + +XXTERN int vhpi_format_value (const vhpiValueT *in_value_p, + vhpiValueT *out_value_p); + +/* time processing */ + +XXTERN void vhpi_get_time (vhpiTimeT *time_p, + long *cycles); + +#define vhpiNoActivity -1 + +XXTERN int vhpi_get_next_time (vhpiTimeT *time_p); + +/* simulation control */ + +typedef enum +{ + vhpiStop = 0, + vhpiFinish = 1, + vhpiReset = 2 + +#ifdef VHPIEXTEND_CONTROL + VHPIEXTEND_CONTROL +#endif +} vhpiSimControlT; + +XXTERN int vhpi_control (vhpiSimControlT command, + ...); + +XXTERN int vhpi_sim_control (vhpiSimControlT command); /* for compatibility reasons */ + +/* I/O routine */ + +XXTERN int vhpi_printf (const char *format, + ...); + +XXTERN int vhpi_vprintf (const char *format, + va_list args); + +/* utilities to print VHDL strings */ + +XXTERN int vhpi_is_printable(char ch); + + +/* utility routines */ + +XXTERN int vhpi_compare_handles (vhpiHandleT handle1, + vhpiHandleT handle2); + +XXTERN int vhpi_check_error (vhpiErrorInfoT *error_info_p); + +XXTERN int vhpi_release_handle (vhpiHandleT object); + +/* creation functions */ + +XXTERN vhpiHandleT vhpi_create (vhpiClassKindT kind, + vhpiHandleT handle1, + vhpiHandleT handle2); + +/* Foreign model data structures and functions */ + +typedef enum +{ + vhpiArchF = 1, + vhpiArchFK = 1, /* for compatibility reasons */ + vhpiFuncF = 2, + vhpiFuncFK = 2, /* for compatibility reasons */ + vhpiProcF = 3, + vhpiProcFK = 3, /* for compatibility reasons */ + vhpiLibF = 4, + vhpiAppF = 5 +} vhpiForeignT; + +typedef struct vhpiForeignDataS +{ + vhpiForeignT kind; + char * libraryName; + char * modelName; + void (*elabf)(const struct vhpiCbDataS *cb_data_p); + void (*execf)(const struct vhpiCbDataS *cb_data_p); +} vhpiForeignDataT; + +XXTERN vhpiHandleT vhpi_register_foreignf (vhpiForeignDataT *foreignDatap); + +XXTERN int vhpi_get_foreignf_info (vhpiHandleT hdl, + vhpiForeignDataT *foreignDatap); + +/* vhpi_get_foreign_info is DEPRECATED */ +XXTERN int vhpi_get_foreign_info (vhpiHandleT hdl, + vhpiForeignDataT *foreignDatap); + +/* for saving and restoring foreign models data */ + +XXTERN size_t vhpi_get_data (int32_t id, + void *dataLoc, + size_t numBytes); + +XXTERN size_t vhpi_put_data (int32_t id, + void *dataLoc, + size_t numBytes); + +#ifdef VHPIEXTEND_FUNCTIONS + VHPIEXTEND_FUNCTIONS +#endif + +/* Visual Elite integration - Cause & Effect support + Function returns instance handle for specified signal + Warning!!! Function each time allocates new handle for returned instance, so you should free it !!! */ +XXTERN vhpiHandleT vhpi_get_cause_instance (vhpiHandleT sigHandle); + +/* Function returns source point number to code which made event on specified signal */ +XXTERN int vhpi_get_cause (vhpiHandleT sigHandle, + unsigned int** p2MagicNumbersBuffer); + +/* Function returns info about line nr and instance hierarchy path for specified source point nr + and instance handle + returns: + For invalid source point: *pnLineNr == -1 + For insufficient buffer size, returns required buffer size + For invalid args function returns -1 + If all is OK, function returns 0 */ +XXTERN int vhpi_get_cause_info (const unsigned int** pn2MagicNumbers, + int nBufLen, + char* pszHierScopeBuf, + int nFilePathBufLen, + char* pszSourceFilePathBuf, + int* pnLineNr); + +/* +* vhpi_value_size() +* +* Description: +* The vhpi_value_size() function lets you query the size in bytes +* required for a buffer to store the value of the specified object handle +* in the specified format. Use this function to allocate memory for the +* appropriate field of the value structure before using +* vphi_get_value() or vhpi_put_value(). +* +* Returns the size in bytes required for a buffer to store the value of +* the specified object handle in the specified format. +* +* Syntax: vhpi_value_size(vhpiHandleT objHdl, vhpiFormatT format) +* Returns: vhpiIntT Size in bytes +* +* Arguments: +* VhpiHandleT objHdl Object handle whose value is needed. +* VhpiFormatT format Format in which the value needs to be represented +* +*******************************************************************************/ + +XXTERN vhpiIntT vhpi_value_size (vhpiHandleT objHdl, + vhpiFormatT format); + +/**************************** Typedef for VHPI bootstrap functions + ****************************/ + +typedef void (*vhpiBootstrapFctT)(void); + + +#undef PLI_EXTERN +#undef PLI_VEXTERN + +#ifdef VHPI_USER_DEFINED_DLLISPEC + #undef VHPI_USER_DEFINED_DLLISPEC + #undef PLI_DLLISPEC +#endif +#ifdef VHPI_USER_DEFINED_DLLESPEC + #undef VHPI_USER_DEFINED_DLLESPEC + #undef PLI_DLLESPEC +#endif + +#ifdef PLI_PROTOTYPES + #undef PLI_PROTOTYPES + #undef XXTERN + #undef EETERN +#endif + +#ifdef __cplusplus +} +#endif + +#pragma pack() + +#endif /* VHPI_USER_H */ From d3698dcfe7c887e041d96ac90604bf1a5edabe46 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 22 Feb 2014 10:29:38 +0000 Subject: [PATCH 0469/2656] Issue #116: Makefile for GHDL (not working) --- makefiles/simulators/Makefile.ghdl | 42 ++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 makefiles/simulators/Makefile.ghdl diff --git a/makefiles/simulators/Makefile.ghdl b/makefiles/simulators/Makefile.ghdl new file mode 100644 index 00000000..8b411f46 --- /dev/null +++ b/makefiles/simulators/Makefile.ghdl @@ -0,0 +1,42 @@ +############################################################################### +# Copyright (c) 2014 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +.PHONY: analyse + +# Compilation phase +analyse: $(VHDL_SOURCES) $(SIM_BUILD) + cd $(SIM_BUILD) && ghdl -a $(VHDL_SOURCES) + +results.xml: analyse $(COCOTB_LIBS) + cd $(SIM_BUILD) && LD_LIBRARY_PATH=$(LIB_DIR) ghdl -e -Wl,-L$(LIB_DIR) -Wl,-lgpivhpi $(TOPLEVEL) + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + ./$(SIM_BUILD)/$(TOPLEVEL) + +clean:: + -@rm -rf $(SIM_BUILD) From b705b111d5bafc313b554b67bfe167a73e5b2117 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sat, 22 Feb 2014 16:40:42 +0000 Subject: [PATCH 0470/2656] Issue #116: Fixed up some leftover VPI errors, added startup routines --- lib/vhpi_shim/gpi_vhpi.c | 73 ++++++++++++++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 10 deletions(-) diff --git a/lib/vhpi_shim/gpi_vhpi.c b/lib/vhpi_shim/gpi_vhpi.c index 4e7e3ab6..a3448a0e 100644 --- a/lib/vhpi_shim/gpi_vhpi.c +++ b/lib/vhpi_shim/gpi_vhpi.c @@ -121,17 +121,18 @@ static int __check_vhpi_error(const char *func, long line) return; switch (level) { - case vpiNotice: + case vhpiNote: loglevel = GPIInfo; break; - case vpiWarning: + case vhpiWarning: loglevel = GPIWarning; break; - case vpiError: + case vhpiError: loglevel = GPIError; break; - case vpiSystem: - case vpiInternal: + case vhpiFailure: + case vhpiSystem: + case vhpiInternal: loglevel = GPICritical; break; } @@ -225,6 +226,7 @@ gpi_sim_hdl gpi_get_root_handle(const char* name) { FENTER vhpiHandleT root; + vhpiHandleT dut; gpi_sim_hdl rv; root = vhpi_handle(vhpiRootInst, NULL); @@ -236,17 +238,29 @@ gpi_sim_hdl gpi_get_root_handle(const char* name) return NULL; } - const char *found = vhpi_get_str(vhpiFullNameP, root); + if (name) + dut = vhpi_handle_by_name(name, NULL); + else + dut = vhpi_handle(vhpiDesignUnit, root); + check_vhpi_error(); + + if (!dut) { + LOG_ERROR("VHPI: Attempting to get the DUT handle failed"); + FEXIT + return NULL; + } + + const char *found = vhpi_get_str(vhpiNameP, dut); check_vhpi_error(); if (name != NULL && strcmp(name, found)) { - LOG_WARN("VHPI: Root %s doesn't match requested toplevel %s", name, found); + LOG_WARN("VHPI: Root '%s' doesn't match requested toplevel %s", found, name); FEXIT return NULL; } rv = gpi_alloc_handle(); - rv->sim_hdl = root; + rv->sim_hdl = dut; FEXIT return rv; @@ -363,6 +377,11 @@ void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) vhpiValueT value_s; value_s.format = vhpiIntVal; value_s.value.intg = value; + value_s.bufSize = sizeof(vhpiIntT); + +// printf("Setting %s to %d\n", +// vhpi_get_str(vhpiFullNameP, (vhpiHandleT)(gpi_hdl->sim_hdl)), +// value); vhpi_put_value((vhpiHandleT)(gpi_hdl->sim_hdl), &value_s, vhpiForcePropagate); check_vhpi_error(); @@ -428,16 +447,38 @@ static char *gpi_copy_name(const char *name) } +// FIXME Seem to have a problem here +// According to VHPI spec we should call vhpi_get_value once to determine +// how much memory to allocate for the result... it appears that we just +// get bogus values back so we'll use a fixed size buffer for now char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) { FENTER - vhpiValueT value_s = {vhpiBinStrVal}; + vhpiValueT value_s; vhpiValueT *value_p = &value_s; + char *result; + size_t size; + value_p->format = vhpiBinStrVal; + +// // Call once to find out how long the string is +// vhpi_get_value((vhpiHandleT)(gpi_hdl->sim_hdl), value_p); +// check_vhpi_error(); + +// size = value_p->bufSize; +// LOG_ERROR("After initial call to get value: bufSize=%u", size); + + result = (char *)malloc(512); + if (result == NULL) { + LOG_CRITICAL("VHPI: Attempting allocate string buffer failed!"); + } + // Call again to get the value + value_p->bufSize = 512; + value_p->value.str = result; vhpi_get_value((vhpiHandleT)(gpi_hdl->sim_hdl), value_p); check_vhpi_error(); - char *result = gpi_copy_name(value_p->value.str); +// char *result = gpi_copy_name(value_p->value.str); FEXIT return result; } @@ -969,3 +1010,15 @@ void (*vhpi_startup_routines[])(void) = { register_final_callback, 0 }; + +// For non-VPI compliant applications that cannot find vlog_startup_routines symbol +void vhpi_startup_routines_bootstrap(void) { + void (*routine)(void); + int i; + routine = vhpi_startup_routines[0]; + for (i = 0, routine = vhpi_startup_routines[i]; + routine; + routine = vhpi_startup_routines[++i]) { + routine(); + } +} From b465e426de079cc7e04977f41d8664815992129c Mon Sep 17 00:00:00 2001 From: Chiggs Date: Mon, 24 Feb 2014 18:47:51 +0000 Subject: [PATCH 0471/2656] Cleanup: Provide boolean testing of Lock objects --- cocotb/triggers.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index ff835629..8275c075 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -414,6 +414,10 @@ def __str__(self): return self.__class__.__name__ + "(%s) [%s waiting]" % ( self.name, len(self._pending_primed)) + def __nonzero__(self): + """Provide boolean of a Lock""" + return self.locked + class NullTrigger(Trigger): """ From 1e5a140176ec1d7afbf1812079f629c374fe7318 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Mon, 24 Feb 2014 18:49:22 +0000 Subject: [PATCH 0472/2656] Cleanup: Correct grammar in comment --- lib/gpi/gpi_logging.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gpi/gpi_logging.c b/lib/gpi/gpi_logging.c index 24bdfc04..9b278c86 100644 --- a/lib/gpi/gpi_logging.c +++ b/lib/gpi/gpi_logging.c @@ -76,7 +76,7 @@ const char *log_level(long level) // We keep this module global to avoid reallocation // we do not need to worry about locking here as -// are single threaded and can not have to calls +// are single threaded and can not have multiple calls // into gpi_log at once. #define LOG_SIZE 512 static char log_buff[LOG_SIZE]; From 7ab634bc3921f5609f5eb1ea00007076a95135d4 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Mon, 24 Feb 2014 19:32:39 +0000 Subject: [PATCH 0473/2656] Cleanup: Ensure Makefile works stand-alone --- examples/functionality/tests/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/functionality/tests/Makefile b/examples/functionality/tests/Makefile index b9686289..5162a5d2 100644 --- a/examples/functionality/tests/Makefile +++ b/examples/functionality/tests/Makefile @@ -31,6 +31,7 @@ TOPLEVEL = sample_module PWD=$(shell pwd) +COCOTB=$(PWD)/../../.. VERILOG_SOURCES = $(PWD)/../hdl/sample_module.v MODULE=test_cocotb,test_discovery,test_external From 5dbd0d70b6d8a7667518ea85ad3255e6880a7049 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Mon, 24 Feb 2014 19:35:20 +0000 Subject: [PATCH 0474/2656] Issue #116: Update Makefiles to support VHPI, bugfixes to VHPI shim --- lib/Makefile | 16 ++- lib/vhpi_shim/Makefile | 8 +- lib/vhpi_shim/gpi_vhpi.c | 231 ++++++++++++++++++++++++++++++--------- lib/vpi_shim/Makefile | 4 +- 4 files changed, 197 insertions(+), 62 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index 40d895b0..9b24cbc4 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -29,6 +29,14 @@ include $(SIM_ROOT)/makefiles/Makefile.inc +# Default to VPI layer for now, override this for VHPI +GPI_IMPL ?= vpi + +ifndef GPI_IMPL + $(error "Unable to determine appropriate GPI layer to use") +endif + + # FIXME it's a bit nasty to have the source files listed here as dependencies # but better than re-building the libraries every time. @@ -44,19 +52,15 @@ $(LIB_DIR)/libgpilog.so: $(SIM_ROOT)/lib/gpi/gpi_logging.c | $(LIB_DIR) $(LIB_DIR)/libcocotb.so: $(SIM_ROOT)/lib/embed/gpi_embed.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/embed -$(LIB_DIR)/libgpi.so: $(SIM_ROOT)/lib/vpi_shim/gpi_vpi.c | $(LIB_DIR) - make -C $(SIM_ROOT)/lib/vpi_shim +$(LIB_DIR)/libgpi.so: $(SIM_ROOT)/lib/$(GPI_IMPL)_shim/gpi_$(GPI_IMPL).c | $(LIB_DIR) + make -C $(SIM_ROOT)/lib/$(GPI_IMPL)_shim -$(LIB_DIR)/libgpivhpi.so: $(SIM_ROOT)/lib/vhpi_shim/gpi_vhpi.c | $(LIB_DIR) - make -C $(SIM_ROOT)/lib/vhpi_shim - $(LIB_DIR)/libsim.so: $(SIM_ROOT)/lib/simulator/simulatormodule.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/simulator COCOTB_LIBS = $(LIB_DIR)/libgpilog.so \ $(LIB_DIR)/libcocotb.so \ $(LIB_DIR)/libgpi.so \ - $(LIB_DIR)/libgpivhpi.so \ $(LIB_DIR)/libsim.so clean:: diff --git a/lib/vhpi_shim/Makefile b/lib/vhpi_shim/Makefile index a5ab0cc4..8ceefb22 100644 --- a/lib/vhpi_shim/Makefile +++ b/lib/vhpi_shim/Makefile @@ -29,10 +29,10 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += -GCC_ARGS += -DVPI_CHECKING -LIBS := -lgpilog -lcocotb -LD_PATH := -L$(LIB_DIR) -LIB_NAME := libgpivhpi.so +GCC_ARGS += -DVHPI_CHECKING +LIBS := -lgpilog -lcocotb $(EXTRA_LIBS) +LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) +LIB_NAME := libgpi.so SRCS := gpi_vhpi.c diff --git a/lib/vhpi_shim/gpi_vhpi.c b/lib/vhpi_shim/gpi_vhpi.c index a3448a0e..0b49ae9a 100644 --- a/lib/vhpi_shim/gpi_vhpi.c +++ b/lib/vhpi_shim/gpi_vhpi.c @@ -9,8 +9,7 @@ * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd, -* SolarFlare Communications Inc nor the +* * Neither the name of Potential Ventures Ltd not the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * @@ -26,6 +25,18 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ +// TODO: +// Now that we have VPI and VHPI we could refactor some of the common code into +// another file (e.g. gpi_copy_name). +// +// This file could be neater, for example by using a mapping of callback type to +// free_one_time vs. free_recurring. +// +// Some functions are completely untested (gpi_get_handle_by_index) and others +// need optimisation. +// +// VHPI seems to run significantly slower the VPI, need to investigate. + #include #include @@ -40,16 +51,11 @@ ((_type *)((uintptr_t)(_address) - \ (uintptr_t)(&((_type *)0)->_member))) -#define VPI_CHECKING 1 +#define VHPI_CHECKING 1 static gpi_sim_hdl sim_init_cb; static gpi_sim_hdl sim_finish_cb; -static int alloc_count = 0; -static int dealloc_count = 0; -static int clear_count = 0; -static int total_count = 0; - typedef enum vhpi_cb_state_e { VHPI_FREE = 0, VHPI_PRIMED = 1, @@ -84,7 +90,6 @@ typedef struct gpi_clock_s { typedef gpi_clock_t *gpi_clock_hdl; -// Add to this over time static const char * vhpi_reason_to_string(int reason) { switch (reason) { @@ -104,16 +109,61 @@ static const char * vhpi_reason_to_string(int reason) return "vhpiCbStartOfSimulation"; case vhpiCbEndOfSimulation: return "vhpiCbEndOfSimulation"; + case vhpiCbEndOfProcesses: + return "vhpiCbEndOfProcesses"; + case vhpiCbLastKnownDeltaCycle: + return "vhpiCbLastKnownDeltaCycle"; default: return "unknown"; } } +static const char * vhpi_format_to_string(int reason) +{ + switch (reason) { + case vhpiBinStrVal: + return "vhpiBinStrVal"; + case vhpiOctStrVal: + return "vhpiOctStrVal"; + case vhpiDecStrVal: + return "vhpiDecStrVal"; + case vhpiHexStrVal: + return "vhpiHexStrVal"; + case vhpiEnumVal: + return "vhpiEnumVal"; + case vhpiIntVal: + return "vhpiIntVal"; + case vhpiLogicVal: + return "vhpiLogicVal"; + case vhpiRealVal: + return "vhpiRealVal"; + case vhpiStrVal: + return "vhpiStrVal"; + case vhpiCharVal: + return "vhpiCharVal"; + case vhpiTimeVal: + return "vhpiTimeVal"; + case vhpiPhysVal: + return "vhpiPhysVal"; + case vhpiObjTypeVal: + return "vhpiObjTypeVal"; + case vhpiPtrVal: + return "vhpiPtrVal"; + case vhpiEnumVecVal: + return "vhpiEnumVecVal"; + + default: + return "unknown"; + } +} + + + // Should be run after every VPI call to check error status static int __check_vhpi_error(const char *func, long line) { int level=0; -#if VPI_CHECKING +#if VHPI_CHECKING vhpiErrorInfoT info; int loglevel; level = vhpi_check_error(&info); @@ -157,14 +207,16 @@ static inline int __gpi_register_cb(p_vhpi_cb_user_data user, vhpiCbDataT *cb_da int ret = 0; if (!new_hdl) { - LOG_CRITICAL("VPI: Unable to register callback a handle for VHPI type %s(%d)", + LOG_CRITICAL("VHPI: Unable to register callback a handle for VHPI type %s(%d)", vhpi_reason_to_string(cb_data->reason), cb_data->reason); check_vhpi_error(); ret = -1; } - if (user->cb_hdl != NULL) + if (user->cb_hdl != NULL) { + printf("VHPI: Attempt to register a callback that's already registered...\n"); gpi_deregister_callback(&user->gpi_hdl); + } user->cb_hdl = new_hdl; @@ -371,51 +423,126 @@ void gpi_get_sim_time(uint32_t *high, uint32_t *low) } // Value related functions +static vhpiEnumT chr2vhpi(const char value) { + switch (value) { + case '0': + return vhpi0; + case '1': + return vhpi1; + case 'U': + case 'u': + return vhpiU; + case 'Z': + case 'z': + return vhpiZ; + case 'X': + case 'x': + return vhpiX; + default: + return vhpiDontCare; + } +} + +// Unfortunately it seems that format conversion is not well supported +// We have to set values using vhpiEnum* void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) { FENTER - vhpiValueT value_s; - value_s.format = vhpiIntVal; - value_s.value.intg = value; - value_s.bufSize = sizeof(vhpiIntT); + vhpiValueT value_s; + vhpiValueT *value_p = &value_s; + int size, i; + + // Determine the type of object, either scalar or vector + value_s.format = vhpiObjTypeVal; + value_s.bufSize = 0; + value_s.value.str = NULL; + + vhpi_get_value((vhpiHandleT)(gpi_hdl->sim_hdl), &value_s); + check_vhpi_error(); + + switch (value_s.format) { + case vhpiEnumVal: { + value_s.value.enumv = value ? vhpi1 : vhpi0; + break; + } + + case vhpiEnumVecVal: { + size = vhpi_get(vhpiSizeP, (vhpiHandleT)(gpi_hdl->sim_hdl)); + value_s.bufSize = size*sizeof(vhpiEnumT); + value_s.value.enumvs = (vhpiEnumT *)malloc(size*sizeof(vhpiEnumT)); -// printf("Setting %s to %d\n", -// vhpi_get_str(vhpiFullNameP, (vhpiHandleT)(gpi_hdl->sim_hdl)), -// value); + for (i=0; isim_hdl), &value_s, vhpiForcePropagate); check_vhpi_error(); + if (vhpiEnumVecVal == value_s.format) + free(value_s.value.enumvs); FEXIT } + + +// Unfortunately it seems that format conversion is not well supported +// We have to set values using vhpiEnum* void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) { FENTER vhpiValueT value_s; vhpiValueT *value_p = &value_s; + int len, size, i; + const char *ptr; - int len; - char *buff; - if (str) - len = strlen(str) + 1; + // Determine the type of object, either scalar or vector + value_s.format = vhpiObjTypeVal; + value_s.bufSize = 0; + value_s.value.str = NULL; - buff = (char *)malloc(len); - if (buff== NULL) { - LOG_CRITICAL("VPI: Attempting allocate string buffer failed!"); - return; - } + vhpi_get_value((vhpiHandleT)(gpi_hdl->sim_hdl), &value_s); + check_vhpi_error(); + + switch (value_s.format) { + case vhpiEnumVal: { + value_s.value.enumv = chr2vhpi(*str); + break; + } - strncpy(buff, str, len); + case vhpiEnumVecVal: { + len = strlen(str); + size = vhpi_get(vhpiSizeP, (vhpiHandleT)(gpi_hdl->sim_hdl)); + value_s.bufSize = size*sizeof(vhpiEnumT); + value_s.value.enumvs = (vhpiEnumT *)malloc(size*sizeof(vhpiEnumT)); - value_p->bufSize = len; - value_p->numElems = len; - value_p->value.str = buff; - value_p->format = vhpiBinStrVal; + // Initialise to 0s + for (i=0; isim_hdl), &value_s, vhpiForcePropagate); check_vhpi_error(); - free(buff); + if(value_s.format == vhpiEnumVecVal) + free(value_s.value.enumvs); FEXIT } @@ -447,10 +574,6 @@ static char *gpi_copy_name(const char *name) } -// FIXME Seem to have a problem here -// According to VHPI spec we should call vhpi_get_value once to determine -// how much memory to allocate for the result... it appears that we just -// get bogus values back so we'll use a fixed size buffer for now char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) { FENTER @@ -460,25 +583,33 @@ char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) size_t size; value_p->format = vhpiBinStrVal; -// // Call once to find out how long the string is -// vhpi_get_value((vhpiHandleT)(gpi_hdl->sim_hdl), value_p); -// check_vhpi_error(); -// size = value_p->bufSize; -// LOG_ERROR("After initial call to get value: bufSize=%u", size); +// FIXME Seem to have a problem here +// According to VHPI spec we should call vhpi_get_value once to determine +// how much memory to allocate for the result... it appears that we just +// get bogus values back so we'll use a fixed size buffer for now +#if 0 + // Call once to find out how long the string is + vhpi_get_value((vhpiHandleT)(gpi_hdl->sim_hdl), value_p); + check_vhpi_error(); + + size = value_p->bufSize; + LOG_ERROR("After initial call to get value: bufSize=%u", size); +#else + size = 512; +#endif - result = (char *)malloc(512); + result = (char *)malloc(size); if (result == NULL) { LOG_CRITICAL("VHPI: Attempting allocate string buffer failed!"); } // Call again to get the value - value_p->bufSize = 512; + value_p->bufSize = size; value_p->value.str = result; vhpi_get_value((vhpiHandleT)(gpi_hdl->sim_hdl), value_p); check_vhpi_error(); -// char *result = gpi_copy_name(value_p->value.str); FEXIT return result; } @@ -590,7 +721,7 @@ int gpi_deregister_callback(gpi_sim_hdl gpi_hdl) user_data = gpi_container_of(gpi_hdl, s_vhpi_cb_user_data, gpi_hdl); - if (user_data->cb_hdl) { + if (user_data->cb_hdl != NULL) { rc = user_data->gpi_cleanup(user_data); user_data->cb_hdl = NULL; } @@ -605,7 +736,7 @@ int gpi_deregister_callback(gpi_sim_hdl gpi_hdl) static int gpi_free_one_time(p_vhpi_cb_user_data user_data) { FENTER - int rc; + int rc = 0; vhpiHandleT cb_hdl = user_data->cb_hdl; if (!cb_hdl) { LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); @@ -707,7 +838,7 @@ int gpi_register_readonly_callback(gpi_sim_hdl cb, user_data->gpi_function = gpi_function; user_data->gpi_cleanup = gpi_free_one_time; - cb_data_s.reason = vhpiCbEndOfTimeStep; + cb_data_s.reason = vhpiCbLastKnownDeltaCycle; cb_data_s.cb_rtn = handle_vhpi_callback; cb_data_s.obj = NULL; cb_data_s.time = &time; @@ -1011,7 +1142,7 @@ void (*vhpi_startup_routines[])(void) = { 0 }; -// For non-VPI compliant applications that cannot find vlog_startup_routines symbol +// For non-VPI compliant applications that cannot find vlog_startup_routines void vhpi_startup_routines_bootstrap(void) { void (*routine)(void); int i; diff --git a/lib/vpi_shim/Makefile b/lib/vpi_shim/Makefile index 3395e9b9..6da808f8 100644 --- a/lib/vpi_shim/Makefile +++ b/lib/vpi_shim/Makefile @@ -31,8 +31,8 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += GCC_ARGS += -DVPI_CHECKING -LIBS := -lgpilog -lcocotb -LD_PATH := -L$(LIB_DIR) +LIBS := -lgpilog -lcocotb $(EXTRA_LIBS) +LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) LIB_NAME := libgpi.so SRCS := gpi_vpi.c From 922dc7ce643e30707090820bb436100299f9c221 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Mon, 24 Feb 2014 22:32:31 +0000 Subject: [PATCH 0475/2656] Issue #119: Provide new assignment mechanism: dut.signal = value --- cocotb/handle.py | 10 +++++++ .../functionality/tests/test_discovery.py | 26 ++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index cee7f9ca..48f75061 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -89,6 +89,13 @@ def _raise_testerror(self, msg): buff.close() raise exception + def __setattr__(self, name, value): + """Provide transparent access to signals""" + if not name.startswith('_') and self.__hasattr__(name): + getattr(self, name).setcachedvalue(value) + return + object.__setattr__(self, name, value) + def __hasattr__(self, name): """Since calling hasattr(handle, "something") will print out a backtrace to the log since usually attempting to access a @@ -104,6 +111,9 @@ def __getitem__(self, index): self._sub_handles[index] = SimHandle(new_handle) return self._sub_handles[index] + def __setitem__(self, index, value): + """Provide transparent assignment to bit index""" + self.__getitem__(index).setcachedvalue(value) def getvalue(self): result = BinaryValue() diff --git a/examples/functionality/tests/test_discovery.py b/examples/functionality/tests/test_discovery.py index 2addb9f1..8e976269 100644 --- a/examples/functionality/tests/test_discovery.py +++ b/examples/functionality/tests/test_discovery.py @@ -43,6 +43,18 @@ def discover_value_not_in_dut(dut): yield Timer(0) +@cocotb.test() +def access_signal(dut): + """Access a signal using the assignment mechanism""" + dut.stream_in_data = 1 + yield Timer(10) + if dut.stream_in_data.value.integer != 1: + raise TestError("%s.%s != %d" % ( + str(dut.stream_in_data.value.integer), + dut.stream_in_data.value.integer)) + + + @cocotb.test() def access_single_bit(dut): """Access a single bit in a vector of the dut""" @@ -57,7 +69,19 @@ def access_single_bit(dut): (str(dut.stream_out_data_comb), dut.stream_out_data_comb.value.integer, (1<<2))) - +@cocotb.test() +def access_single_bit_assignment(dut): + """Access a single bit in a vector of the dut using the assignment mechanism""" + # FIXME this test fails on Icarus but works on VCS + dut.stream_in_data = 0 + yield Timer(10) + dut.log.info("%s = %d bits" % (str(dut.stream_in_data), len(dut.stream_in_data))) + dut.stream_in_data[2] = 1 + yield Timer(10) + if dut.stream_out_data_comb.value.integer != (1<<2): + raise TestError("%s.%s != %d" % + (str(dut.stream_out_data_comb), + dut.stream_out_data_comb.value.integer, (1<<2))) @cocotb.test(expect_error=True) def access_single_bit_erroneous(dut): From 5d7a740dbf9e1c87da3130403703737a7eeb1e9c Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 25 Feb 2014 17:36:23 +0000 Subject: [PATCH 0476/2656] Fixes #116: Example of using VHPI interface to simulator --- .../endian_swapper/hdl/endian_swapper.vhdl | 184 ++++++++++++++++++ examples/endian_swapper/tests/Makefile | 16 +- makefiles/simulators/Makefile.aldec | 22 ++- 3 files changed, 220 insertions(+), 2 deletions(-) create mode 100644 examples/endian_swapper/hdl/endian_swapper.vhdl diff --git a/examples/endian_swapper/hdl/endian_swapper.vhdl b/examples/endian_swapper/hdl/endian_swapper.vhdl new file mode 100644 index 00000000..2416e826 --- /dev/null +++ b/examples/endian_swapper/hdl/endian_swapper.vhdl @@ -0,0 +1,184 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2014 Potential Ventures Ltd +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- * Redistributions of source code must retain the above copyright +-- notice, this list of conditions and the following disclaimer. +-- * Redistributions in binary form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- * Neither the name of Potential Ventures Ltd nor +-- names of its contributors may be used to endorse or promote products +-- derived from this software without specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +-- DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +-- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +-- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +------------------------------------------------------------------------------- +-- +-- +-- Endian swapping module. +-- +-- Simple example with Avalon streaming interfaces and a CSR bus +-- +-- Avalon-ST has readyLatency of 0 +-- Avalon-MM has fixed readLatency of 1 +-- +-- Exposes 2 32-bit registers via the Avalon-MM interface +-- +-- Address 0: bit 0 [R/W] byteswap enable +-- bits 31-1: [N/A] reserved +-- Adress 1: bits 31-0: [RO] packet count +-- +library ieee; + +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity endian_swapper is + generic ( + DATA_BYTES : integer := 8); + port ( + clk : in std_ulogic; + reset_n : in std_ulogic; + + stream_in_data : in std_ulogic_vector(DATA_BYTES*8-1 downto 0); + stream_in_empty : in std_ulogic_vector(2 downto 0); + stream_in_valid : in std_ulogic; + stream_in_startofpacket : in std_ulogic; + stream_in_endofpacket : in std_ulogic; + stream_in_ready : out std_ulogic; + + stream_out_data : out std_ulogic_vector(DATA_BYTES*8-1 downto 0); + stream_out_empty : out std_ulogic_vector(2 downto 0); + stream_out_valid : out std_ulogic; + stream_out_startofpacket: out std_ulogic; + stream_out_endofpacket : out std_ulogic; + stream_out_ready : in std_ulogic; + + csr_address : in std_ulogic_vector(1 downto 0); + csr_readdata : out std_ulogic_vector(31 downto 0); + csr_readdatavalid : out std_ulogic; + csr_read : in std_ulogic; + csr_write : in std_ulogic; + csr_waitrequest : out std_ulogic; + csr_writedata : in std_ulogic_vector(31 downto 0) + ); +end; + +architecture impl of endian_swapper is + + +function byteswap(data : in std_ulogic_vector(63 downto 0)) return std_ulogic_vector is begin + return data(7 downto 0) & + data(15 downto 8) & + data(23 downto 16) & + data(31 downto 24) & + data(39 downto 32) & + data(47 downto 40) & + data(55 downto 48) & + data(63 downto 56); +end; + +signal csr_waitrequest_int : std_ulogic; +signal stream_out_endofpacket_int: std_ulogic; +signal flush_pipe : std_ulogic; +signal in_packet : std_ulogic; +signal byteswapping : std_ulogic; +signal packet_count : unsigned(31 downto 0); + +begin + + +process (clk, reset_n) begin + if (reset_n = '0') then + flush_pipe <= '0'; + in_packet <= '0'; + packet_count <= to_unsigned(0, 32); + elsif rising_edge(clk) then + + + if (flush_pipe = '1' and stream_out_ready = '1') then + + flush_pipe <= stream_in_endofpacket and stream_in_valid and stream_out_ready; + + elsif (flush_pipe = '0') then + flush_pipe <= stream_in_endofpacket and stream_in_valid and stream_out_ready; + end if; + + if (stream_out_ready = '1' and stream_in_valid = '1') then + stream_out_empty <= stream_in_empty; + stream_out_startofpacket <= stream_in_startofpacket; + stream_out_endofpacket_int <= stream_in_endofpacket; + + if (byteswapping = '0') then + stream_out_data <= stream_in_data; + else + stream_out_data <= byteswap(stream_in_data); + end if; + + if (stream_in_startofpacket = '1' and stream_in_valid = '1') then + packet_count <= packet_count + 1; + in_packet <= '1'; + end if; + + if (stream_in_endofpacket = '1' and stream_in_valid = '1') then + in_packet <= '0'; + end if; + end if; + end if; +end process; + + +stream_in_ready <= stream_out_ready; +stream_out_endofpacket <= stream_out_endofpacket_int; + +stream_out_valid <= '1' when (stream_in_valid = '1' and stream_out_endofpacket_int = '0') or flush_pipe = '1' else '0'; + +-- Hold off CSR accesses during packet transfers to prevent changing of endian configuration mid-packet +csr_waitrequest_int <= '1' when reset_n = '0' or in_packet = '1' or (stream_in_startofpacket = '1' and stream_in_valid = '1') or flush_pipe = '1' else '0'; +csr_waitrequest <= csr_waitrequest_int; + +process (clk, reset_n) begin + if (reset_n = '0') then + byteswapping <= '0'; + csr_readdatavalid <= '0'; + elsif rising_edge(clk) then + + if (csr_read = '1') then + csr_readdatavalid <= not csr_waitrequest_int; + + case csr_address is + when "00" => csr_readdata <= (31 downto 1 => '0') & byteswapping; + when "01" => csr_readdata <= std_ulogic_vector(packet_count); + when others => csr_readdata <= (31 downto 0 => 'X'); + end case; + + elsif (csr_write = '1' and csr_waitrequest_int = '0') then + case csr_address is + when "00" => byteswapping <= csr_writedata(0); + when others => null; + end case; + end if; + end if; +end process; + + +-- Unfortunately this workaround is required for Aldec: Need to schedule an event +fake_process :process +begin + wait for 50 ns; +end process; + + +end architecture; + diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index af744a16..488e9fff 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -28,11 +28,25 @@ ############################################################################### +# Override this variable to use VHPI instead of VPI +GPI_IMPL ?= vpi + + TOPLEVEL = endian_swapper PWD=$(shell pwd) COCOTB=$(PWD)/../../.. -VERILOG_SOURCES = $(PWD)/../hdl/endian_swapper.sv + + + +ifeq ($(GPI_IMPL),vpi) + VERILOG_SOURCES = $(PWD)/../hdl/endian_swapper.sv +endif + +ifeq ($(GPI_IMPL),vhpi) + VHDL_SOURCES = $(PWD)/../hdl/endian_swapper.vhdl +endif + MODULE=test_endian_swapper include $(COCOTB)/makefiles/Makefile.inc diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 1b88b1ff..057d761f 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -35,21 +35,41 @@ else CMD := vsimsa endif +EXTRA_LIBS = -laldecpli -lstdc++ -lsupc++ +EXTRA_LIBDIRS = -L$(ALDEC_PATH)/bin/Linux64 + ALOG_ARGS += "+define+COCOTB_SIM -dbg" +ACOM_ARGS += "-dbg" ifeq ($(COVERAGE),1) ASIM_ARGS += "-acdb" ALOG_ARGS += "-coverage sb" endif +ifeq ($(GPI_IMPL),vpi) + GPI_ARGS = -pli libgpi +endif +ifeq ($(GPI_IMPL),vhpi) + GPI_ARGS = -loadvhpi $(LIB_DIR)/libgpi:vhpi_startup_routines_bootstrap +endif + +ifndef GPI_ARGS + $(error "Unable to determine appropriate GPI layer to use") +endif + # Create a TCL script based on the list of $(VERILOG_SOURCES) $(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(SIM_BUILD) echo "alib work" > $@ echo "set worklib work" >> $@ + echo -e $(foreach src,$(VHDL_SOURCES),\ + "acom $(ACOM_ARGS) $(src)\n") >> $@ echo -e $(foreach src,$(VERILOG_SOURCES),\ "alog $(ALOG_ARGS) $(src)\n") >> $@ - echo "asim $(ASIM_ARGS) +access +w -O2 -dbg -pli libgpi $(TOPLEVEL)" >> $@ +ifdef SCRIPT_FILE + echo "do $(SCRIPT_FILE)" >> $@ +endif + echo "asim $(ASIM_ARGS) +access +w -O2 -dbg $(GPI_ARGS) $(TOPLEVEL)" >> $@ ifeq ($(WAVES),1) echo "log -recursive *" >> $@ endif From b99219ea0e7fe54626432b42b6e54930ee4c5617 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Tue, 25 Feb 2014 20:15:20 +0000 Subject: [PATCH 0477/2656] Issue #9: Updates to documentation for 0.4 release --- documentation/source/index.rst | 5 +- documentation/source/introduction.rst | 75 ++++++--------------------- documentation/source/overview.rst | 30 ----------- documentation/source/quickstart.rst | 23 ++++---- 4 files changed, 31 insertions(+), 102 deletions(-) delete mode 100644 documentation/source/overview.rst diff --git a/documentation/source/index.rst b/documentation/source/index.rst index 8adab1d9..ff8c22c1 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -12,13 +12,12 @@ Contents: :maxdepth: 2 introduction - overview quickstart - building coroutines - library_reference endian_swapper ping_tun_tap + library_reference + building roadmap simulator_support diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index 15a0e455..91b178f2 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -5,7 +5,7 @@ Introduction What is cocotb? =============== -**Cocotb** is a *coroutine* based *cosimulation* *testbench* environment for testing VHDL/Verilog RTL using Python. +**Cocotb** is a *coroutine* based *cosimulation* *testbench* environment for testing VHDL/Verilog RTL using `Python `_. **Cocotb** is completely free, open source (under the `BSD License `_) and hosted on `GitHub `_. @@ -14,86 +14,43 @@ What is cocotb? * Icarus Verilog * Synopsys VCS * Aldec Riviera-PRO -* Mentor Questa * Cadance Incisive -**Cocotb** was developed by Potential Ventures with the support of `Solarflare Communications Ltd `_ and contributions from Gordon McGregor and Finn Grimwood. +**Cocotb** was developed by `Potential Ventures _` with the support of `Solarflare Communications Ltd `_ and contributions from Gordon McGregor and Finn Grimwood. +**Cocotb** can be used live in a web-browser on the excellent `EDA Playground `_. How is cocotb different? ------------------------ -Cocotb encourages the same philosophy of design re-use and randomised testing as UVM, however is implemented in Python rather than SystemVerilog. +**Cocotb** encourages the same philosophy of design re-use and randomised testing as UVM, however is implemented in Python rather than SystemVerilog. -In cocotb VHDL/Verilog/SystemVerilog are only used for the synthesisable design. All verification is done using Python which is ideal for rapid development of complex systems and integrating multiple languages. Using Python has various advantages over using SystemVerilog or VHDL for verification: +In cocotb VHDL/Verilog/SystemVerilog are only used for the synthesisable design. All verification is done using Python which has various advantages over using SystemVerilog or VHDL for verification: * Writing Python is **fast** - it's a very productive language * It's **easy** to interface to other languages from Python -* Python has a huge library of existing code to **re-use** like `packet parsing/generation `_ libraries. +* Python has a huge library of existing code to **re-use** like `packet generation `_ libraries. * Python is **interpreted**. Tests can be edited and re-run them without having to recompile the design or exit the simulator GUI. * Python is **popular** - far more engineers know Python than SystemVerilog or VHDL +**Cocotb** was specifically designed to lower the overhead of creating a test. -Lower the overhead of creating a test -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Using cocotb the DUT hangs in "free space" in the simulator, you don't even have to create an instance and wire it up. -Tests themselves are very terse and easy to create. This lower overhead encourages creation of regression tests even for -sub-blocks where usually the overhead of writing a testbench is too onerous. - - -Open verification to a wider audience -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -ASIC/FPGA designs always involve some software development. -Often the hardware team is responsible for creating and verifying the RTL with little interaction between them and the software team. As the boundaries blur the software teams becoming more involved in the RTL and Cocotb encourages this by lowering the barrier to entry. Python is a common language accessible to a larger audience providing a common ground for RTL, verification and software developers. - - - -How should I use cocotb? ------------------------- - -Verifying using Cocotb is similar to any other verification environment and the usual best-practice advice applies: - - - -Utilise the simulator features -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +**Cocotb** has built-in support for integrating with the `Jenkins `_ continuous integration system. -If you are paying for a simulator, make the most of all the fucntionlity it provides. Use metrics to asses how verification is progressing. It's surprisingly easy to write a test that doesn't actually test anything. Use *code coverage* and *functional coverage* to track verification progress, embed assersions to validate behaviour. +**Cocotb** automatically discovers tests to that no additional step is required to add a test to a regression. -Structure your testbench -^^^^^^^^^^^^^^^^^^^^^^^^ +How does Cocotb work +==================== -Cocotb provides base classes for `Drivers`_, `Monitors`_ and `Scoreboards`_. Any moderately complex testbench should build on these abstractions. +Overview +-------- -Drivers provide an mechanism for driving a transaction onto a bus in the simulator by waggling pins typically over one or more clock cycles. +A typical cocotb testbench requires no additional RTL code. The Design Under Test (DUT) is instantiated as the toplevel in the simulator without any wrapper code. Cocotb drives stimulus onto the inputs to the DUT (or further down the hierarchy) and monitors the outputs directly from Python. -Monitors perform the inverse function of drivers, reconstructing transactions by observing bus behaviour. -A scoreboard is a way to validate that the DUT is behaving correctly (in addition to assertions and protocol checkers in any bus models). Typically this is by comparing the transactions seen by the monitors with expected output based on a software model. +.. image:: diagrams/svg/cocotb_overview.svg - -Write directed and randomised tests -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Directed tests are very useful in the initial phases of development and when attempting to recreate particular bugs or behaviour. Cocotb encourages the use of short directed tests forming a regression since all tests are automatically included in a regression using test auto-discovery. - - -Test Driven Development -^^^^^^^^^^^^^^^^^^^^^^^ - - -Use a regression system -^^^^^^^^^^^^^^^^^^^^^^^ - -`Jenkins `_ - - -Getting Started -=============== - -**Cocotb** can be used live in a web-browser on the excellent `EDA Playground `_. +A test is simply a Python function. At any given time either the simulator is advancing time or the Python code is executing. The **yield** keyword is used to indicate when to pass control of execution back to the simulator. A test can spawn multiple coroutines, allowing for independent flows of execution. diff --git a/documentation/source/overview.rst b/documentation/source/overview.rst deleted file mode 100644 index c91d6464..00000000 --- a/documentation/source/overview.rst +++ /dev/null @@ -1,30 +0,0 @@ -################## -Overview of cocotb -################## - -.. image:: diagrams/svg/cocotb_overview.svg - - -Overview -======== - - -A typical cocotb testbench requires no additional RTL code. The Design Under Test (DUT) is instantiated as the toplevel in the simulator without any wrapper code. Cocotb drives stimulus onto the inputs to the DUT (or further down the hierarchy) and monitors the outputs directly from Python. - -Cocotb comprises 3 layers: - -GPI (Generic Procedural Interface) ----------------------------------- - -This layer abstracts the simulator language interface to provide a common set of functionality. Supports VHDL via VHPI and Verilog/SystemVerilog via VPI. Modelsim FLI may be supported in the future. - -simulatormodule ---------------- - -A CPython extension which utilises GPI to expose the simulator to Python. - -cocotb Python package ---------------------- - -Python infrastructure including coroutine scheduler, base classes etc. The cocotb package provides a mechanism to traverse the hierarchy, query signal values, force signals to particular values and control simulator execution. - diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index 8679ace2..1c8ae86d 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -34,13 +34,12 @@ To run a test using a different simulator: $> make SIM=vcs -Supported simulators --------------------- +Compatible Simulators +--------------------- * Icarus Verilog * Synopsys VCS * Aldec Riviera-PRO -* Mentor Questa * Cadance Incisive @@ -72,15 +71,19 @@ The same mechanism can be used to access signals inside the design. Assigning values to signals --------------------------- -Values can be assigned to signals using either the .value property of a handle object or using the overloaded less than or equal to operator +Values can be assigned to signals using either the .value property of a handle object or using direct assignment while traversing the hierarchy .. code-block:: python - # Assign the value 12 to signal "input_signal" on DUT - dut.input_signal.value = 12 + # Get a reference to the "clk" signal and assign a value + clk = dut.clk + clk.value = 1 - # Use the overloaded less than or equal to operator - dut.input_signal <= 12 + # Direct assignment through the hierarchy + dut.input_signal = 12 + + # Assign a value to a memory deep in the hierarchy + dut.sub_block.memory.array[4] = 2 Reading values from signals @@ -95,8 +98,8 @@ Accessing the .value property of a handle object will return a :class:`BinaryVal >>> >>> print count.binstr 1X1010 - >>> # Resolve the value (X or Z treated as 0) - >>> print count.value + >>> # Resolve the value to an integer (X or Z treated as 0) + >>> print count.integer 42 From 4685565350ba3f56ab932fc7425b55e10e012cc2 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Tue, 25 Feb 2014 20:31:00 +0000 Subject: [PATCH 0478/2656] Uprev repository version to 0.5 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index eadcafcd..3407a865 100644 --- a/version +++ b/version @@ -1 +1 @@ -VERSION=0.4 +VERSION=0.5 From 33952d47fcbd8d36db2e97c44667df95611618d3 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 28 Feb 2014 00:26:39 +0000 Subject: [PATCH 0479/2656] Simplify README.md --- README.md | 100 ++++++++++-------------------------------------------- 1 file changed, 18 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index 77177547..f089e611 100644 --- a/README.md +++ b/README.md @@ -1,87 +1,23 @@ - **cocotb** is a coroutine based cosimulation library for writing VHDL and Verilog testbenches in Python. + +* Skim the introductory presentation: http://potential.ventures * Read the [documentation](http://cocotb.readthedocs.org) * Get involved: [Raise a bug / request an enhancement](https://github.com/potentialventures/cocotb/issues/new) (Requires a GitHub account) * Get in contact: [E-mail us](mailto:cocotb@potentialventures.com) - - -Overview -======== - -VHDL and Verilog are both unsuitable for writing complex testbenches. eRM, SystemVerilog and the various SV based methodologies have emerged to address this deficiency. These verification methodologies are large and cumbersome, requiring specialist knowledge, significant time investment and expensive toolchains to achieve satisfactory verification. The overhead of setting up testbenches is onerous so often designers write small throwaway testbenches at a block level and defer the majority of verification to a large system level testbench. - -Cocotb intends to bridge this gap in order to make block-level testing useful, reusable, fast, accessible to a much wider skill-set. The net effect is that bugs will be discovered earlier in the design cycle which equates to significant time and cost saving. - -Why verify in Python? ---------------------- - -* It's **easy** to interface to other languages from Python -* Python has a huge library of existing code to **re-use** like [constraint solvers](https://code.google.com/p/or-tools/) [packet parsing/generation](http://www.secdev.org/projects/scapy/) libraries. -* Python is **interpreted**. Tests can be edited and re-run them without having to recompile the design or even exit the simulator GUI. -* Writing Python is **fast**, it's *easy to understand* and *everybody* knows it. - - -Example -------- - -A simplistic example of generating a clock with 10ns period: -```python -@cocotb.coroutine -def clock(signal): - while True: - signal <= 1 - yield Timer(5) - signal <= 0 - yield Timer(5) -``` - -The python "yield" keyword is used to suspend the coroutine and return control back to the simulator until a condition is met. In this case, the condition is a simple timer that fires after 5ns of simulation time. - -This may seem pretty pointless, in fact a Verilog process to perform the same operation requires fewer lines of code. Let's examine a more useful example: - -```python -@cocotb.coroutine -def pcap_replay(bus, filename): - prevtime = 0 - for timestamp, packet in dpkt.pcap.Reader(open(filename, 'r')): - yield Timer((timestamp - prevtime) * 1000) - yield bus.send(packet) -``` - -Here we utilise a thirdparty python library called dpkt to iterate over packets in a PCAP packet capture. This becomes trivially easy in Python when compared to a VHDL/Verilog/SystemVerilog implementation. The argument "bus" is an instance of a Driver class which provides a send method to serialise the packet transaction over multiple cycles on the bus. Although the majority of the complexity is hidden in this (reusable) Driver class the actual implementation is once again straightforward: - -```python -def send(self, packet): - - words = len(packet) / (len(self.data) / 8) - - yield RisingEdge(self.clk) # Synchronise to bus clock - self.sop <= 1 # First word is start-of-packet - self.valid <= 1 - - for index, word in enumerate(packet): - self.data <= word - yield rising_edge(self.clk) - self.sop <= 0 - if index == words - 1: - self.eop <= 1 - self.len <= len(word) - - yield rising_edge(self.clk) - self.eop <= 0 - self.valid <= 0 -``` - -Similar projects ----------------- - -[MyHDL](http://www.myhdl.org/) seeks to displace VHDL and Verilog with a Python framework that can generate synthesiable RTL. - -Several examples exist of projects providing a VPI interface into a language other than C: - -* [PyHVL](http://pyhvl.sourceforge.net/) -* [Ruby-vpi](http://snk.tuxfamily.org/lib/ruby-vpi/) - -Cocotb tries to build on the advances made by these projects while learning from the direction of UVM and other verification standards. - +* Follow us on twitter: [@PVCocotb](https://twitter.com/PVCocotb) + +## Quickstart + + # Install pre-requisites (waveform viewer optional) + sudo yum install -y iverilog python-devel gtkwave + + # Checkout git repositories + git clone https://github.com/potentialventures/cocotb.git + + # Run the tests... + cd cocotb/examples/endian_swapper/tests + make + + # View the waveform + gtkwave sim_build/waveform.vcd From 10214e49b5b59aa68b26df19229d6cb13143291a Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 28 Feb 2014 00:33:23 +0000 Subject: [PATCH 0480/2656] Add tutorial / example links to README.md --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index f089e611..e336cbb6 100644 --- a/README.md +++ b/README.md @@ -21,3 +21,10 @@ # View the waveform gtkwave sim_build/waveform.vcd + + +## Tutorials and examples + +* [Endian Swapper tutorial](https://cocotb.readthedocs.org/en/latest/endian_swapper.html) +* [Ping using TUN/TAP tutorial](https://cocotb.readthedocs.org/en/latest/ping_tun_tap.html) +* [OpenCores JPEG Encoder example](https://github.com/chiggs/oc_jpegencode/) From f8d3f368bf0305a8a7aee93e7cf2d140c17a8bb2 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 14 Mar 2014 16:58:04 +0000 Subject: [PATCH 0481/2656] Issue #120: Regression testcase to demonstrate failure --- examples/functionality/tests/Makefile | 2 +- .../functionality/tests/test_regression.py | 47 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 examples/functionality/tests/test_regression.py diff --git a/examples/functionality/tests/Makefile b/examples/functionality/tests/Makefile index 5162a5d2..b474492e 100644 --- a/examples/functionality/tests/Makefile +++ b/examples/functionality/tests/Makefile @@ -34,7 +34,7 @@ PWD=$(shell pwd) COCOTB=$(PWD)/../../.. VERILOG_SOURCES = $(PWD)/../hdl/sample_module.v -MODULE=test_cocotb,test_discovery,test_external +MODULE=test_cocotb,test_discovery,test_external,test_regression include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim diff --git a/examples/functionality/tests/test_regression.py b/examples/functionality/tests/test_regression.py new file mode 100644 index 00000000..f7739661 --- /dev/null +++ b/examples/functionality/tests/test_regression.py @@ -0,0 +1,47 @@ +# A set of regression tests for open issues + +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import RisingEdge, Timer, ReadOnly +from cocotb.result import TestFailure + +@cocotb.coroutine +def send_data(dut): + dut.stream_in_valid = 1 + yield RisingEdge(dut.clk) + dut.stream_in_valid = 0 + + +@cocotb.coroutine +def monitor(dut): + for i in range(4): + yield RisingEdge(dut.clk) + yield ReadOnly() + if not dut.stream_in_valid.value.integer: + raise TestFailure("stream_in_valid should be high on the 5th cycle") + + +@cocotb.test() +def issue_120_scheduling(dut): + + cocotb.fork(Clock(dut.clk, 2500).start()) + cocotb.fork(monitor(dut)) + yield RisingEdge(dut.clk) + + # First attempt, not from coroutine - works as expected + for i in range(2): + dut.stream_in_valid = 1 + yield RisingEdge(dut.clk) + dut.stream_in_valid = 0 + + yield RisingEdge(dut.clk) + + # Failure - we don't drive valid on the rising edge even though + # behaviour should be identical to the above + yield send_data(dut) + dut.stream_in_valid = 1 + yield RisingEdge(dut.clk) + dut.stream_in_valid = 0 + + yield RisingEdge(dut.clk) + From 89d892c22ade7c7f6be0cb370f5822dea76fb461 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 14 Mar 2014 17:03:45 +0000 Subject: [PATCH 0482/2656] Fixes #120: Change scheduler to ensure nested coroutines are executed this timestep --- cocotb/scheduler.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 75f444f8..8e39012a 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -108,7 +108,10 @@ def react(self, trigger): self.schedule(coroutine, trigger=trigger) self.log.debug("Scheduled coroutine %s" % (coroutine.__name__)) + # Various interactions with the simulator cannot occur during + # ReadOnly event periods if self._readonly is False: + # We may also have possible routines that need to be added since # the exit from ReadOnly for ptrigger, pwaiting in self.delay_waiting.items(): @@ -117,21 +120,9 @@ def react(self, trigger): self._add_trigger(ptrigger, pcoro) del self.delay_waiting[ptrigger] - # If we've performed any writes that are cached then schedule - # another callback for the read-write part of the sim cycle, but - # if we are terminating then do not allow another callback to be - # scheduled, only do this if this trigger was not ReadOnly as - # Scheduling ReadWrite is a violation, it will be picked up - # on next react - - if self._readonly is False: - if self._terminate is False and len(self.writes) and self._readwrite is None: - self._readwrite = self.add(self.move_to_rw()) - - # If the python has caused any subsequent events to fire we might - # need to schedule more coroutines before we drop back into the - # simulator - if self._readonly is False: + # If the python has caused any subsequent events to fire we might + # need to schedule more coroutines before we drop back into the + # simulator self._entry_lock.acquire() while self._pending_adds: coroutine = self._pending_adds.pop(0) @@ -140,6 +131,15 @@ def react(self, trigger): self._entry_lock.acquire() self._entry_lock.release() + # If we've performed any writes that are cached then schedule + # another callback for the read-write part of the sim cycle, but + # if we are terminating then do not allow another callback to be + # scheduled, only do this if this trigger was not ReadOnly as + # Scheduling ReadWrite is a violation, it will be picked up + # on next react + if self._terminate is False and len(self.writes) and self._readwrite is None: + self._readwrite = self.add(self.move_to_rw()) + return def set_external(self): From a222b8b8e4a53fe0a667959fce86b7c8855adfe4 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 23 Mar 2014 17:19:42 +0000 Subject: [PATCH 0483/2656] Major overhaul of Scheduler as per issue #121 --- cocotb/clock.py | 5 +- cocotb/decorators.py | 16 +- cocotb/handle.py | 5 +- cocotb/scheduler.py | 672 +++++++++++++++---------- cocotb/triggers.py | 93 ++-- examples/endian_swapper/tests/Makefile | 21 + lib/vhpi_shim/gpi_vhpi.c | 2 +- lib/vpi_shim/gpi_vpi.c | 2 +- 8 files changed, 482 insertions(+), 334 deletions(-) diff --git a/cocotb/clock.py b/cocotb/clock.py index 1fd9bf35..2cc57115 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -55,11 +55,12 @@ def __init__(self, signal, period): @cocotb.coroutine def start(self, cycles=0): + t = Timer(self.half_period) while True: self.signal <= 1 - yield Timer(self.half_period) + yield t self.signal <= 0 - yield Timer(self.half_period) + yield t def __str__(self): return self.__class__.__name__ + "(%3.1fMHz)" % self.frequency diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 3fbda849..a26afab8 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -34,7 +34,7 @@ import cocotb from cocotb.log import SimLog -from cocotb.triggers import Join, PythonTrigger, Timer, Event, NullTrigger +from cocotb.triggers import _Join, PythonTrigger, Timer, Event, NullTrigger from cocotb.result import TestComplete, TestError, TestFailure, TestSuccess, ReturnValue, raise_error @@ -65,9 +65,6 @@ def __init__(self, text="", callback=None): StopIteration.__init__(self, text) self.callback = callback - def __call__(self): - if self.callback is not None: self.callback() - class RunningCoroutine(object): """Per instance wrapper around an function to turn it into a coroutine @@ -85,6 +82,7 @@ def __init__(self, inst, parent): self._coro = inst self._finished = False self._callbacks = [] + self._join = _Join(self) self._parent = parent self.__doc__ = parent._func.__doc__ self.module = parent._func.__module__ @@ -108,8 +106,6 @@ def send(self, value): except TestComplete as e: if isinstance(e, TestFailure): self.log.warning(str(e)) - else: - self.log.info(str(e)) raise except ReturnValue as e: self.retval = e.retval @@ -128,21 +124,17 @@ def close(self): def kill(self): """Kill a coroutine""" self.log.debug("kill() called on coroutine") - cocotb.scheduler.schedule_remove(self, self._finished_cb) + cocotb.scheduler.unschedule(self) def _finished_cb(self): """Called when the coroutine completes. Allows us to mark the coroutine as finished so that boolean testing works. Also call any callbacks, usually the result of coroutine.join()""" self._finished = True - self.log.debug("Coroutine finished calling pending callbacks (%d pending)" % len(self._callbacks)) - for cb in self._callbacks: - cb() - self._callbacks = [] def join(self): """Return a trigger that will fire when the wrapped coroutine exits""" - return Join(self) + return self._join def __nonzero__(self): """Provide boolean testing diff --git a/cocotb/handle.py b/cocotb/handle.py index 48f75061..8c49302f 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -47,6 +47,7 @@ from cocotb.binary import BinaryValue from cocotb.log import SimLog from cocotb.result import TestError +from cocotb.triggers import _RisingEdge class SimHandle(object): @@ -63,6 +64,7 @@ def __init__(self, handle): self.fullname = self.name + '(%s)' % simulator.get_type_string(self._handle) self.log = SimLog('cocotb.' + self.name) self.log.debug("Created!") + self._edge = _RisingEdge(self) def __str__(self): return "%s @0x%x" % (self.name, self._handle) @@ -91,7 +93,8 @@ def _raise_testerror(self, msg): def __setattr__(self, name, value): """Provide transparent access to signals""" - if not name.startswith('_') and self.__hasattr__(name): + if not name.startswith('_') and not name in ["name", "fullname", "log"] \ + and self.__hasattr__(name): getattr(self, name).setcachedvalue(value) return object.__setattr__(self, name, value) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 8e39012a..89cc4f9e 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -30,13 +30,10 @@ """ Coroutine scheduler. """ -import types -import threading import collections import os -import threading -import time -import pdb +import logging + # For autodocumentation don't need the extension modules if "SPHINX_BUILD" in os.environ: @@ -44,224 +41,383 @@ else: import simulator +# Debug mode controlled by environment variables +if "COCOTB_ENABLE_PROFILING" in os.environ: + import cProfile, StringIO, pstats + _profile = cProfile.Profile() + _profiling = True +else: + _profiling = False + +# Sadly the python standard logging module is very slow so it's better not to +# make any calls by testing a boolean flag first +if "COCOTB_SCHEDULER_DEBUG" in os.environ: + _debug = True +else: + _debug = False + + import cocotb import cocotb.decorators -from cocotb.triggers import Trigger, Timer, ReadOnly, NextTimeStep, ReadWrite, RisingEdge +from cocotb.triggers import Trigger, GPITrigger, Timer, ReadOnly, _NextTimeStep, ReadWrite from cocotb.log import SimLog from cocotb.result import TestComplete, TestError, ReturnValue, raise_error -from threading import RLock + + + class Scheduler(object): + """ + The main scheduler. + + Here we accept callbacks from the simulator and schedule the appropriate + coroutines. + + A callback fires, causing the `react`_ method to be called, with the + trigger that caused the callback as the first argument. + + We look up a list of coroutines to schedule (indexed by the trigger) and + schedule them in turn. NB implementors should not depend on the scheduling + order! + + Some additional management is required since coroutines can return a list + of triggers, to be scheduled when any one of the triggers fires. To + ensure we don't receive spurious callbacks, we have to un-prime all the + other triggers when any one fires. + + Due to the simulator nuances and fun with delta delays we have the + following modes: + + Normal mode + - Callbacks cause coroutines to be scheduled + - Any pending writes are cached and do not happen immediately + + ReadOnly mode + - Corresponds to cbReadOnlySynch (VPI) or vhpiCbLastKnownDeltaCycle + (VHPI). In this state we are not allowed to perform writes. + + Write mode + - Corresponds to cbReadWriteSynch (VPI) or vhpiCbEndOfProcesses (VHPI) + In this mode we play back all the cached write updates. + + We can legally transition from normal->write by registering a ReadWrite + callback, however usually once a simulator has entered the ReadOnly phase + of a given timestep then we must move to a new timestep before performing + any writes. The mechanism for moving to a new timestep may not be + consistent across simulators and therefore we provide an abstraction to + assist with compatibility. + + + Unless a coroutine has explicitly requested to be scheduled in ReadOnly + mode (for example wanting to sample the finally settled value after all + delta delays) then it can reasonably be expected to be scheduled during + "normal mode" i.e. where writes are permitted. + """ + + _MODE_NORMAL = 1 + _MODE_READONLY = 2 + _MODE_WRITE = 3 + _MODE_TERM = 4 + + # Singleton events, recycled to avoid spurious object creation + _readonly = ReadOnly() + _next_timestep = _NextTimeStep() + _readwrite = ReadWrite() + _timer1 = Timer(1) + _timer0 = Timer(0) def __init__(self): - self.waiting = collections.defaultdict(list) - self.delay_waiting = collections.defaultdict(list) + self.log = SimLog("cocotb.scheduler") - self.writes = {} - self.writes_lock = threading.RLock() - self._remove = [] - self._pending_adds = [] - self._startpoint = None + if _debug: + self.log.setLevel(logging.DEBUG) + + # A dictionary of pending coroutines for each trigger, indexed by trigger + self._trigger2coros = collections.defaultdict(list) + + # A dictionary of pending triggers for each coroutine, indexed by coro + self._coro2triggers = collections.defaultdict(list) + + # Our main state + self._mode = Scheduler._MODE_NORMAL + + # A dictionary of pending writes + self._writes = {} + + self._pending_coros = [] + self._pending_callbacks = [] + self._pending_triggers = [] + self._terminate = False self._test_result = None - self._do_cleanup = None - self._entry_lock = RLock() - self._external_trigger = threading.Semaphore(1) - self._external_trigger._value = False - self._external = False - self._readonly = False - self._react_timer = None - # Keep this last - self._readwrite = self.add(self.move_to_rw()) + self._entrypoint = None + + # Select the appropriate scheduling algorithm for this simulator + self.advance = self.default_scheduling_algorithm + + + def default_scheduling_algorithm(self): + """ + Decide whether we need to schedule our own triggers (if at all) in order + to progress to the next mode. + + This algorithm has been tested against the following simulators: + Icarus Verilog + """ + if not self._terminate and self._writes: + + if self._mode == Scheduler._MODE_NORMAL: + self._readwrite.prime(self.react) + elif not self._next_timestep.primed: + self._next_timestep.prime(self.react) + + elif self._terminate: + if _debug: self.log.debug("Test terminating, scheduling Timer") + + for t in self._trigger2coros: + t.unprime() + + if self._timer1.primed: + self.log.debug("Un-priming already primed Timer trigger") + self._timer1.unprime() + + self._timer1.prime(self.begin_test) + self._trigger2coros = collections.defaultdict(list) + self._coro2triggers = collections.defaultdict(list) + self._terminate = False + self._mode = Scheduler._MODE_TERM + + + def begin_test(self, trigger=None): + """ + Called to initiate a test. + + Could be called on start-up or from a callback + """ + if _debug: + self.log.debug("begin_test called with trigger: %s" % (str(trigger))) + if _profiling: + ps = pstats.Stats(_profile).sort_stats('cumulative') + ps.dump_stats("test_profile.pstat") + _profile.enable() + + self._mode = Scheduler._MODE_NORMAL + if trigger is not None: trigger.unprime() + + # Issue previous test result, if there is one + if self._test_result is not None: + if _debug: + self.log.debug("Issue test result to regresssion object") + cocotb.regression.handle_result(self._test_result) + self._test_result = None + if self._entrypoint is not None: + test = self._entrypoint + self._entrypoint = None + self.schedule(test) + + if _profiling: + _profile.disable() + def react(self, trigger): """ - React called when a trigger fires. We find any coroutines that are waiting on the particular - trigger and schedule them. + React called when a trigger fires. + + We find any coroutines that are waiting on the particular trigger and + schedule them. """ - trigger.log.debug("Fired!") + if _debug: + trigger.log.debug("Fired!") + if _profiling: + _profile.enable() - if isinstance(trigger, ReadOnly): - self.enable_react_delay() - self._readonly = True + # We can always unprime the trigger that actually fired - if it's + # recycled then prime will be called again. + trigger.unprime() - if trigger not in self.waiting: - # This isn't actually an error - often might do event.set() without knowing - # whether any coroutines are actually waiting on this event - # NB should catch a GPI trigger cause that would be catestrophic - self.log.debug("Not waiting on triggger that fired! (%s)" % (str(trigger))) + if self._mode == Scheduler._MODE_TERM: + if _debug: + self.log.debug("Ignoring trigger %s since we're terminating" % + str(trigger)) return - # Scheduled coroutines may append to our waiting list - # so the first thing to do is pop all entries waiting - # on this trigger. - self._scheduling = self.waiting.pop(trigger) - to_run = len(self._scheduling) - - self.log.debug("%d pending coroutines for event %s" % (to_run, trigger)) - - while self._scheduling: - coroutine = self._scheduling.pop(0) - del_list = trigger.clearpeers() - while del_list: - self.remove(del_list.pop(0)) - self.schedule(coroutine, trigger=trigger) - self.log.debug("Scheduled coroutine %s" % (coroutine.__name__)) - - # Various interactions with the simulator cannot occur during - # ReadOnly event periods - if self._readonly is False: - - # We may also have possible routines that need to be added since - # the exit from ReadOnly - for ptrigger, pwaiting in self.delay_waiting.items(): - for pcoro in pwaiting: - self.delay_waiting[ptrigger].remove(pcoro) - self._add_trigger(ptrigger, pcoro) - del self.delay_waiting[ptrigger] - - # If the python has caused any subsequent events to fire we might - # need to schedule more coroutines before we drop back into the - # simulator - self._entry_lock.acquire() - while self._pending_adds: - coroutine = self._pending_adds.pop(0) - self._entry_lock.release() - self.add(coroutine) - self._entry_lock.acquire() - self._entry_lock.release() - - # If we've performed any writes that are cached then schedule - # another callback for the read-write part of the sim cycle, but - # if we are terminating then do not allow another callback to be - # scheduled, only do this if this trigger was not ReadOnly as - # Scheduling ReadWrite is a violation, it will be picked up - # on next react - if self._terminate is False and len(self.writes) and self._readwrite is None: - self._readwrite = self.add(self.move_to_rw()) + if trigger is self._readonly: + self._mode = Scheduler._MODE_READONLY + else: + self._mode = Scheduler._MODE_NORMAL - return + # We're the only source of ReadWrite triggers which are only used for + # playing back any cached signal updates + if trigger is self._readwrite: - def set_external(self): - """ Take the semaphore to indicate to the react later that there an external - is being added to the list - """ -# self._external_trigger.acquire() - self._external_trigger._value = True + if _debug: self.log.debug("Writing cached signal updates") - def playout_writes(self): - if self.writes: - while self.writes: - handle, args = self.writes.popitem() - handle.setimmediatevalue(args) + while self._writes: + handle, value = self._writes.popitem() + handle.setimmediatevalue(value) + if _profiling: + _profile.disable() + return - def save_write(self, handle, args): - self.writes[handle]=args + # Similarly if we've scheduled our next_timestep on way to readwrite + if trigger is self._next_timestep: - def _add_trigger(self, trigger, coroutine): - """Adds a new trigger which will cause the coroutine to continue when fired""" - try: - # If we are in readonly for the currently firing trigger then new coroutines - # are not added to the waiting list and primed, they are instead - # added to a secondary list of events that will then be handled on the next - # entry to react when we exit ReadOnly into NextTimeStep - if self._readonly is True: - self._entry_lock.acquire() - self.delay_waiting[trigger].append(coroutine) - self._entry_lock.release() + if not self._writes: + self.log.error( + "Moved to next timestep without any pending writes!") else: - self._entry_lock.acquire() - self.waiting[trigger].append(coroutine) - self._entry_lock.release() - # We drop the lock before calling out to the simulator (most likely consequence of prime) - trigger.prime(self.react) + self._readwrite.prime(self.react) + + if _profiling: + _profile.disable() + return + + + + if trigger not in self._trigger2coros: - except TestError as e: - self.waiting[trigger].remove(coroutine) - # Do not re-call raise_error since the error will already be logged at point of interest - raise e + # GPI triggers should only be ever pending if there is an + # associated coroutine waiting on that trigger, otherwise it would + # have been unprimed already + if isinstance(trigger, GPITrigger): + self.log.critical( + "No coroutines waiting on trigger that fired: %s" % + str(trigger)) + trigger.log.info("I'm the culprit") + # For Python triggers this isn't actually an error - we might do + # event.set() without knowing whether any coroutines are actually + # waiting on this event, for example + elif _debug: + self.log.debug( + "No coroutines waiting on trigger that fired: %s" % + str(trigger)) - except Exception as e: - self.waiting[trigger].remove(coroutine) - raise_error(self, "Unable to prime a trigger: %s" % str(e)) + if _profiling: + _profile.disable() + return + + # Scheduled coroutines may append to our waiting list so the first + # thing to do is pop all entries waiting on this trigger. + scheduling = self._trigger2coros.pop(trigger) + + if _debug: + self.log.debug("%d pending coroutines for event %s\n%s" % ( + len(scheduling), str(trigger), + "\n\t".join([coro.__name__ for coro in scheduling]))) + for coro in scheduling: + coro.log.debug("I was waiting") + + # If the coroutine was waiting on multiple triggers we may be able + # to unprime the other triggers that didn't fire + for coro in scheduling: + for pending in self._coro2triggers[coro]: + for others in self._trigger2coros[pending]: + if others not in scheduling: break + else: + if pending.primed: pending.unprime() + del self._trigger2coros[pending] + + + for coro in scheduling: + self.schedule(coro, trigger=trigger) + if _debug: + self.log.debug("Scheduled coroutine %s" % (coro.__name__)) + + while self._pending_triggers: + if _debug: + self.log.debug("Scheduling pending trigger %s" % ( + str(self._pending_triggers[0]))) + self.react(self._pending_triggers.pop(0)) + + self.advance() + + if _debug: + self.log.debug( + "All coroutines scheduled, handing control back to simulator") + + if _profiling: + _profile.disable() + return + + + def unschedule(self, coro): + """Unschedule a coroutine. Unprime any pending triggers""" + + for trigger in self._coro2triggers[coro]: + if coro in self._trigger2coros[trigger]: + self._trigger2coros[trigger].remove(coro) + if not self._trigger2coros[trigger]: + trigger.unprime() + del self._coro2triggers[coro] + + + if coro._join in self._trigger2coros: + self._pending_triggers.append(coro._join) + + + def save_write(self, handle, value): + self._writes[handle] = value + + + def _coroutine_yielded(self, coro, triggers): + """ + Prime the triggers and update our internal mappings + """ + self._coro2triggers[coro] = triggers + + for trigger in triggers: + + self._trigger2coros[trigger].append(coro) + if not trigger.primed: + try: + trigger.prime(self.react) + except Exception as e: + raise_error(self, "Unable to prime trigger %s: %s" % ( + str(trigger), str(e))) def queue(self, coroutine): """Queue a coroutine for execution""" - self._entry_lock.acquire() - self._pending_adds.append(coroutine) - self._entry_lock.release() + self._pending_coros.append(coroutine) def add(self, coroutine): - """Add a new coroutine. Required because we cant send to a just started generator (FIXME)""" + """ + Add a new coroutine. + Just a wrapper around self.schedule which provides some debug and + useful error mesages in the event of common gotchas + """ if isinstance(coroutine, cocotb.decorators.coroutine): - self.log.critical("Attempt to schedule a coroutine that hasn't started") + self.log.critical( + "Attempt to schedule a coroutine that hasn't started") coroutine.log.error("This is the failing coroutine") - self.log.warning("Did you forget to add paranthesis to the @test decorator?") - self._result = TestError("Attempt to schedule a coroutine that hasn't started") - self.cleanup() + self.log.warning( + "Did you forget to add paranthesis to the @test decorator?") + self._test_result = TestError( + "Attempt to schedule a coroutine that hasn't started") + self._terminate = True return elif not isinstance(coroutine, cocotb.decorators.RunningCoroutine): - self.log.critical("Attempt to add something to the scheduler which isn't a coroutine") - self.log.warning("Got: %s (%s)" % (str(type(coroutine)), repr(coroutine))) + self.log.critical( + "Attempt to add something to the scheduler which isn't a coroutine") + self.log.warning( + "Got: %s (%s)" % (str(type(coroutine)), repr(coroutine))) self.log.warning("Did you use the @coroutine decorator?") - self._result = TestError("Attempt to schedule a coroutine that hasn't started") - self.cleanup() + self._test_result = TestError( + "Attempt to schedule a coroutine that hasn't started") + self._terminate = True return + if _debug: + self.log.debug("Adding new coroutine %s" % coroutine.__name__) - self.log.debug("Queuing new coroutine %s" % coroutine.__name__) - self.log.debug("Adding %s" % coroutine.__name__) self.schedule(coroutine) return coroutine def new_test(self, coroutine): - self._startpoint = coroutine + self._entrypoint = coroutine - def remove(self, trigger): - """Remove a trigger from the list of pending coroutines""" - self._entry_lock.acquire() - self.waiting.pop(trigger) - self._entry_lock.release() - trigger.unprime() - - def schedule_remove(self, coroutine, callback): - """Adds the specified coroutine to the list of routines - That will be removed at the end of the current loop - """ - self._entry_lock.acquire() - self._remove.append((coroutine, callback)) - self._entry_lock.release() - - def prune_routines(self): - """ - Process the remove list that can have accumulatad during the - execution of a parent routine - """ - while self._remove: - self._entry_lock.acquire() - delroutine, cb = self._remove.pop(0) - for trigger, waiting in self.waiting.items(): - for coro in waiting: - if coro is delroutine: - self.log.debug("Closing %s" % str(coro)) - self._entry_lock.release() - cb() - self._entry_lock.acquire() - self.waiting[trigger].remove(coro) - self._entry_lock.release() - coro.close() - self._entry_lock.acquire() - # Clean up any triggers that no longer have pending coroutines - for trigger, waiting in self.waiting.items(): - if not waiting: - self._entry_lock.release() - trigger.unprime() - self._entry_lock.acquire() - del self.waiting[trigger] - self._entry_lock.release() def schedule(self, coroutine, trigger=None): """ @@ -274,128 +430,92 @@ def schedule(self, coroutine, trigger=None): coroutine to be scheduled """ if hasattr(trigger, "pass_retval"): - self.log.debug("Coroutine returned a retval") sendval = trigger.retval + if _debug: + coroutine.log.debug("Scheduling with ReturnValue(%s)" % ( + repr(sendval))) else: - coroutine.log.debug("Scheduling (%s)" % str(trigger)) sendval = trigger - try: + if _debug: + coroutine.log.debug("Scheduling with %s" % str(trigger)) - try: - result = coroutine.send(sendval) - - # Normal co-routine completion - except cocotb.decorators.CoroutineComplete as exc: - self.log.debug("Coroutine completed execution with CoroutineComplete: %s" % str(coroutine)) - - # Call any pending callbacks that were waiting for this coroutine to exit - exc() - return - - # Entries may have been added to the remove list while the - # coroutine was running, clear these down and deschedule - # before resuming - if self._terminate is False: - self.prune_routines() - - if isinstance(result, Trigger): - self._add_trigger(result, coroutine) - elif isinstance(result, cocotb.decorators.RunningCoroutine): - if self._terminate is False: - self.log.debug("Scheduling nested co-routine: %s" % result.__name__) - - # Queue current routine to schedule when the nested routine exits - self.queue(result) - new_trigger = result.join() - new_trigger.pass_retval = True - self._add_trigger(new_trigger, coroutine) - - elif isinstance(result, list): - for trigger in result: - trigger.addpeers(result) - self._add_trigger(trigger, coroutine) - else: - msg = "Coroutine %s yielded something that was not a trigger or a coroutine!" % str(coroutine) - msg += "\nGot type: %s repr: %s str: %s" % (type(result), repr(result), str(result)) - msg += "\nDid you forget to decorate with @cocotb.cocorutine?" - raise_error(self, msg) + try: + result = coroutine.send(sendval) + if _debug: + self.log.debug("Coroutine %s yielded %s (mode %d)" % ( + coroutine.__name__, str(result), self._mode)) # TestComplete indication is game over, tidy up except TestComplete as test_result: # Tag that close down is needed, save the test_result # for later use in cleanup handler - # If we're already tearing down we ignore any further test results - # that may be raised. Required because currently Python triggers don't unprime - if not self._terminate: - self.finish_test(test_result) - return + self.finish_test(test_result) + return - coroutine.log.debug("Finished sheduling coroutine (%s)" % str(trigger)) - def finish_scheduler(self, test_result): - # if the sim it's self has issued a close down then the - # normal shutdown will not work - self.cleanup() - self.issue_result(test_result) + # Normal co-routine completion + except cocotb.decorators.CoroutineComplete as exc: + if _debug: + self.log.debug("Coroutine completed: %s" % str(coroutine)) + self.unschedule(coroutine) + return - def finish_test(self, test_result): - if not self._terminate: - self._terminate = True - self._test_result = test_result - self.cleanup() - self._do_cleanup = self.add(self.move_to_cleanup()) + # Don't handle the result if we're shutting down + if self._terminate: + return - def cleanup(self): - """ Clear up all our state + # Queue current routine to schedule when the nested routine exits + if isinstance(result, cocotb.decorators.RunningCoroutine): + if _debug: + self.log.debug("Scheduling nested co-routine: %s" % + result.__name__) - Unprime all pending triggers and kill off any coroutines""" - for trigger, waiting in self.waiting.items(): - for coro in waiting: - self.log.debug("Killing %s" % str(coro)) - coro.kill() + self.queue(result) + new_trigger = result.join() + new_trigger.pass_retval = True + self._coroutine_yielded(coroutine, [new_trigger]) - def issue_result(self, test_result): - # Tell the handler what the result was - self.log.debug("Issue test result to regresssion object") - cocotb.regression.handle_result(test_result) + elif isinstance(result, Trigger): + self._coroutine_yielded(coroutine, [result]) - @cocotb.decorators.coroutine - def move_to_cleanup(self): - yield Timer(1) - self.prune_routines() - self._do_cleanup = None + elif isinstance(result, list) and \ + not [t for t in result if not isinstance(t, Trigger)]: + self._coroutine_yielded(coroutine, result) - self.issue_result(self._test_result) - self._test_result = None - - # If another test was added to queue kick it off - self._terminate = False - if self._startpoint is not None: - newstart = self._startpoint - self._startpoint = None - self.add(newstart) + else: + msg = "Coroutine %s yielded something the scheduler can't handle" \ + % str(coroutine) + msg += "\nGot type: %s repr: %s str: %s" % \ + (type(result), repr(result), str(result)) + msg += "\nDid you forget to decorate with @cocotb.cocorutine?" + try: + raise_error(self, msg) + except Exception as e: + self.finish_test(e) - self.log.debug("Cleanup done") + # Handle any newly queued coroutines that need to be scheduled + while self._pending_coros: + self.add(self._pending_coros.pop(0)) + while self._pending_callbacks: + self._pending_callbacks.pop(0)() - @cocotb.decorators.coroutine - def move_to_rw(self): - yield ReadWrite() - self._readwrite = None - self.playout_writes() - @cocotb.decorators.coroutine - def internal_clock(self, clock): - while True: - yield RisingEdge(clock) + def finish_test(self, test_result): + """Cache the test result and set the terminate flag""" + if not self._terminate: + self._terminate = True + self._test_result = test_result + self.cleanup() - def enable_react_delay(self): - if self._react_timer is None: - self._react_timer = self.add(self.react_delay()) - @cocotb.decorators.coroutine - def react_delay(self): - yield NextTimeStep() - self._react_timer = None - self._readonly = False + def cleanup(self): + """ + Clear up all our state + Unprime all pending triggers and kill off any coroutines + """ + for trigger, waiting in self._trigger2coros.items(): + for coro in waiting: + if _debug: self.log.debug("Killing %s" % str(coro)) + coro.kill() diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 8275c075..8babbf1e 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -46,22 +46,16 @@ class Trigger(object): """Base class to derive from""" def __init__(self): self.log = SimLog("cocotb.%s" % (self.__class__.__name__), id(self)) - self.peers = [] self.signal = None + self.primed = False + + def prime(self, *args): + self.primed = True def unprime(self): """Remove any pending callbacks if necessary""" - self.peers = [] - - def addpeers(self, peers): - """Store any relate triggers""" - for elem in peers: - if elem is not self: - self.peers.append(elem) - - def clearpeers(self): - """Call _clearpeers on each trigger that is not me""" - return self.peers + self.primed = False + self.log.debug("Unprimed") def __del__(self): """Ensure if a trigger drops out of scope we remove any pending callbacks""" @@ -89,14 +83,13 @@ class GPITrigger(Trigger): """ def __init__(self): Trigger.__init__(self) - self.cbhdl = None + self.cbhdl = simulator.create_callback(self) def unprime(self): """Unregister a prior registered timed callback""" - Trigger.unprime(self) - if self.cbhdl is not None: + if self.primed: simulator.deregister_callback(self.cbhdl) - self.chhdl = None + Trigger.unprime(self) def __del__(self): """Remove knowledge of the trigger""" @@ -118,7 +111,7 @@ def __init__(self, time_ps): def prime(self, callback): """Register for a timed callback""" - self.cbhdl = simulator.create_callback(self) + Trigger.prime(self) if simulator.register_timed_callback(self.cbhdl, self.time_ps, callback, self): raise_error(self, "Unable set up %s Trigger" % (str(self))) @@ -135,14 +128,14 @@ def __init__(self, signal): def prime(self, callback): """Register notification of a value change via a callback""" - self.cbhdl = simulator.create_callback(self) + Trigger.prime(self) if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, callback, self): raise_error(self, "Unable set up %s Trigger" % (str(self))) def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal.name -class ReadOnly(GPITrigger): +class _ReadOnly(GPITrigger): """ Execution will resume when the readonly portion of the sim cycles is readched @@ -151,14 +144,18 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): - self.cbhdl = simulator.create_callback(self) + Trigger.prime(self) if simulator.register_readonly_callback(self.cbhdl, callback, self): raise_error(self, "Unable set up %s Trigger" % (str(self))) def __str__(self): return self.__class__.__name__ + "(readonly)" -class ReadWrite(GPITrigger): +_ro = _ReadOnly() +def ReadOnly(): + return _ro + +class _ReadWrite(GPITrigger): """ Execution will resume when the readwrite porttion of the sim cycles is reached @@ -167,14 +164,18 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): - self.cbhdl = simulator.create_callback(self) + Trigger.prime(self) if simulator.register_rwsynch_callback(self.cbhdl, callback, self): raise_error(self, "Unable set up %s Trigger" % (str(self))) def __str__(self): return self.__class__.__name__ + "(readwritesync)" -class NextTimeStep(GPITrigger): +_rw = _ReadWrite() +def ReadWrite(): + return _rw + +class _NextTimeStep(GPITrigger): """ Execution will resume when the next time step is started """ @@ -182,23 +183,28 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): - self.cbhdl = simulator.create_callback(self) + Trigger.prime(self) simulator.register_nextstep_callback(self.cbhdl, callback, self) def __str__(self): return self.__class__.__name__ + "(nexttimestep)" -class RisingEdge(Edge): +_nxts = _NextTimeStep() +def NextTimeStep(): + return _nxts + +class _RisingEdge(Edge): """ Execution will resume when a rising edge occurs on the provided signal - + NB Riviera doesn't seem to like re-using a callback handle? - + """ def __init__(self, signal): Edge.__init__(self, signal) def prime(self, callback): + Trigger.prime(self) self._callback = callback def _check(obj): @@ -206,11 +212,9 @@ def _check(obj): self._callback(self) else: simulator.deregister_callback(self.cbhdl) - self.cbhdl = simulator.create_callback(self) if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self): raise_error(self, "Unable set up %s Trigger" % (str(self))) - self.cbhdl = simulator.create_callback(self) if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self): raise_error(self, "Unable set up %s Trigger" % (str(self))) @@ -218,6 +222,10 @@ def _check(obj): def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal.name + +def RisingEdge(signal): + return signal._edge + class ClockCycles(Edge): """ Execution will resume after N rising edges @@ -227,6 +235,7 @@ def __init__(self, signal, num_cycles): self.num_cycles = num_cycles def prime(self, callback): + Trigger.prime(self) self._callback = callback def _check(obj): @@ -237,12 +246,9 @@ def _check(obj): self._callback(self) return - simulator.deregister_callback(self.cbhdl) - self.cbhdl = simulator.create_callback(self) if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self): raise_error(self, "Unable set up %s Trigger" % (str(self))) - self.cbhdl = simulator.create_callback(self) if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self): raise_error(self, "Unable set up %s Trigger" % (str(self))) @@ -268,6 +274,7 @@ def __init__(self, *args): raise TriggerException("%s requires a list of Trigger objects" % self.__class__.__name__) def prime(self, callback): + Trigger.prime(self) self._callback = callback self._fired = [] for trigger in self._triggers: @@ -297,6 +304,7 @@ def __init__(self, parent): self.parent = parent def prime(self, callback): + Trigger.prime(self) self._callback = callback self.parent.prime(callback, self) @@ -317,6 +325,7 @@ def __init__(self, name=""): self.data = None def prime(self, callback, trigger): + Trigger.prime(self) self._pending.append(trigger) def set(self, data=None): @@ -359,6 +368,7 @@ def __init__(self, parent): self.parent = parent def prime(self, callback): + Trigger.prime(self) self._callback = callback self.parent.prime(callback, self) @@ -379,6 +389,7 @@ def __init__(self, name=""): self.locked = False def prime(self, callback, trigger): + Trigger.prime(self) self._pending_unprimed.remove(trigger) @@ -433,25 +444,25 @@ def prime(self, callback): callback(self) -class Join(PythonTrigger): +class _Join(PythonTrigger): """ Join a coroutine, firing when it exits """ def __init__(self, coroutine): PythonTrigger.__init__(self) self._coroutine = coroutine - self.retval = None - def prime(self, callback): - """Register our calback for when the coroutine exits""" - def _cb(): - self.retval = self._coroutine.retval - callback(self) + @property + def retval(self): + return self._coroutine.retval - self._coroutine._callbacks.append(_cb) - self.log.debug("Primed on %s" % self._coroutine.__name__) + #def prime(self, callback): + #"""Register our calback for when the coroutine exits""" + #Trigger.prime(self) def __str__(self): return self.__class__.__name__ + "(%s)" % self._coroutine.__name__ +def Join(coro): + return coro._join diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index 488e9fff..7850a5f3 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -49,5 +49,26 @@ endif MODULE=test_endian_swapper + + include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim + + +# Stuff below is useful for profiling +# Need grof2dot from https://code.google.com/p/jrfonseca.gprof2dot/ +test_profile.pstat: sim + +callgraph.svg: test_profile.pstat + gprof2dot.py -f pstats $< | dot -Tsvg -o $@ + +.PHONY: profile +profile: + COCOTB_ENABLE_PROFILING=1 $(MAKE) callgraph.svg + + +clean:: + -rm -rf test_profile.pstat + -rm -rf callgraph.svg + + diff --git a/lib/vhpi_shim/gpi_vhpi.c b/lib/vhpi_shim/gpi_vhpi.c index 0b49ae9a..995c3870 100644 --- a/lib/vhpi_shim/gpi_vhpi.c +++ b/lib/vhpi_shim/gpi_vhpi.c @@ -745,7 +745,7 @@ static int gpi_free_one_time(p_vhpi_cb_user_data user_data) // If the callback has not been called we also need to call // remove as well - if (!user_data->state == VHPI_PRIMED) { + if (user_data->state == VHPI_PRIMED) { rc = vhpi_remove_cb(cb_hdl); if (!rc) { check_vhpi_error(); diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 99aa47ad..350b9232 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -618,7 +618,7 @@ static int gpi_free_one_time(p_vpi_cb_user_data user_data) // If the callback has not been called we also need to call // remove as well - if (!user_data->state == VPI_PRIMED) { + if (user_data->state == VPI_PRIMED) { rc = vpi_remove_cb(cb_hdl); if (!rc) { check_vpi_error(); From a0777593cefb57dfbfb7def1fb5e15c841026f02 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 23 Mar 2014 22:29:20 +0000 Subject: [PATCH 0484/2656] Get modelsim working #112 on top of scheduler re-write #121 --- cocotb/scheduler.py | 22 +++++++++++++--------- cocotb/triggers.py | 1 - lib/vpi_shim/gpi_vpi.c | 19 +++++++++++++++++-- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 89cc4f9e..c16c0e03 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -219,7 +219,7 @@ def begin_test(self, trigger=None): _profile.disable() - def react(self, trigger): + def react(self, trigger, depth=0): """ React called when a trigger fires. @@ -228,11 +228,12 @@ def react(self, trigger): """ if _debug: trigger.log.debug("Fired!") - if _profiling: + if _profiling and not depth: _profile.enable() # We can always unprime the trigger that actually fired - if it's # recycled then prime will be called again. + self.log.debug("Unprimed %s" % str(trigger)) trigger.unprime() if self._mode == Scheduler._MODE_TERM: @@ -267,6 +268,7 @@ def react(self, trigger): self.log.error( "Moved to next timestep without any pending writes!") else: + self.log.debug("Priming ReadWrite trigger so we can playback writes") self._readwrite.prime(self.react) if _profiling: @@ -328,16 +330,17 @@ def react(self, trigger): if _debug: self.log.debug("Scheduling pending trigger %s" % ( str(self._pending_triggers[0]))) - self.react(self._pending_triggers.pop(0)) + self.react(self._pending_triggers.pop(0), depth=depth+1) - self.advance() + if not depth: + self.advance() - if _debug: - self.log.debug( - "All coroutines scheduled, handing control back to simulator") + if _debug: + self.log.debug( + "All coroutines scheduled, handing control back to simulator") - if _profiling: - _profile.disable() + if _profiling: + _profile.disable() return @@ -503,6 +506,7 @@ def schedule(self, coroutine, trigger=None): def finish_test(self, test_result): """Cache the test result and set the terminate flag""" + self.log.debug("finish_test called with %s" % (repr(test_result))) if not self._terminate: self._terminate = True self._test_result = test_result diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 8babbf1e..4bcb6c2e 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -95,7 +95,6 @@ def __del__(self): """Remove knowledge of the trigger""" if self.cbhdl is not None: simulator.remove_callback(self.cbhdl) - self.chhdl = None Trigger.__del__(self) diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 350b9232..9371743c 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -153,6 +153,11 @@ static inline int __gpi_register_cb(p_vpi_cb_user_data user, p_cb_data cb_data) /* If the user data already has a callback handle then deregister * before getting the new one */ + if (user->state == VPI_PRIMED) { + fprintf(stderr, "Attempt to prime an already primed trigger for %s!\n", + vpi_reason_to_string(cb_data->reason)); + } + vpiHandle new_hdl = vpi_register_cb(cb_data); int ret = 0; @@ -163,8 +168,11 @@ static inline int __gpi_register_cb(p_vpi_cb_user_data user, p_cb_data cb_data) ret = -1; } - if (user->cb_hdl != NULL) + if (user->cb_hdl != NULL) { + fprintf(stderr, "user->cb_hdl is not null, deregistering %s!\n", + vpi_reason_to_string(cb_data->reason)); gpi_deregister_callback(&user->gpi_hdl); + } user->cb_hdl = new_hdl; @@ -521,8 +529,11 @@ static int32_t handle_vpi_callback(p_cb_data cb_data) old_cb = user_data->cb_hdl; rv = user_data->gpi_function(user_data->gpi_cb_data); +// HACK: Investigate further - this breaks modelsim +#if 0 if (old_cb == user_data->cb_hdl) gpi_deregister_callback(&user_data->gpi_hdl); +#endif /* A request to delete could have been done * inside gpi_function @@ -619,18 +630,23 @@ static int gpi_free_one_time(p_vpi_cb_user_data user_data) // If the callback has not been called we also need to call // remove as well if (user_data->state == VPI_PRIMED) { + rc = vpi_remove_cb(cb_hdl); if (!rc) { check_vpi_error(); return rc; } +// HACK: Calling vpi_free_object after vpi_remove_cb causes Modelsim to VPIEndOfSimulationCallback +#if 0 rc = vpi_free_object(cb_hdl); if (!rc) { check_vpi_error(); return rc; } +#endif } + user_data->state = VPI_FREE; FEXIT return rc; } @@ -679,7 +695,6 @@ int gpi_register_value_change_callback(gpi_sim_hdl cb, user_data->cb_value.format = vpiIntVal; vpi_time_s.type = vpiSuppressTime; - vpi_value_s.format = vpiIntVal; cb_data_s.reason = cbValueChange; cb_data_s.cb_rtn = handle_vpi_callback; From 8b22f611ae5eec5570184a48a8b6f5671152dc4b Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 23 Mar 2014 22:36:21 +0000 Subject: [PATCH 0485/2656] Issue #112: Correct Modelsim makefile to depend on libraries --- makefiles/simulators/Makefile.modelsim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index 649a4bdd..d20446a0 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -37,7 +37,7 @@ sim: -@rm -rf results.xml $(MAKE) results.xml -results.xml: $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) libs +results.xml: $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) vlib work vlog -writetoplevels modelsim.tops '-timescale' '1ns/1ns' '-mfcu' '+acc=rmb' '-sv' $(VERILOG_SOURCES) LD_LIBRARY_PATH=$(LIB_DIR): MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ From 5cb0e5268991d27cb4aaec8acca16b30ddcc9750 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 25 Mar 2014 10:43:22 +0000 Subject: [PATCH 0486/2656] Fix Python Makefile: 'site-packages' isn't always present in distutils.sysconfig.get_python_lib() --- makefiles/Makefile.pylib | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib index 23fafe79..2dd1bf4e 100644 --- a/makefiles/Makefile.pylib +++ b/makefiles/Makefile.pylib @@ -32,6 +32,7 @@ PYTHON_PREFIX = $(shell python-config --prefix) NATIVE_ARCH=$(shell uname -m) +ARCH?=$(NATIVE_ARCH) ifeq ($(NATIVE_ARCH),$(ARCH)) STD_LIB=True @@ -41,7 +42,7 @@ endif # We might work with other Python versions PYTHON_VERSION?=$(shell python -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_version()') -PYTHON_LIBDIR:=$(shell python -c 'import os.path,distutils.sysconfig;print os.path.sep+os.path.join(*list(distutils.sysconfig.get_python_lib(standard_lib=$(STD_LIB)).split(os.path.sep)[:-1]))') +PYTHON_LIBDIR:=$(shell python -c 'import os.path,distutils.sysconfig;print distutils.sysconfig.get_python_lib(standard_lib=$(STD_LIB)).rstrip("site-packages").rstrip("/")') PYTHON_DYNLIBDIR:=$(PYTHON_LIBDIR)/lib-dynload From 6e8e9c1d949a08e3e74955b7b7c5fb547540d9e4 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 25 Mar 2014 11:25:31 +0000 Subject: [PATCH 0487/2656] Account for incosistent arguments for $fatal vs. $info/$warning/$error --- examples/sim_exit/hdl/close_module.v | 2 +- lib/vpi_shim/gpi_vpi.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/sim_exit/hdl/close_module.v b/examples/sim_exit/hdl/close_module.v index e56e9410..eb5d5d21 100644 --- a/examples/sim_exit/hdl/close_module.v +++ b/examples/sim_exit/hdl/close_module.v @@ -61,7 +61,7 @@ initial begin #10 $warning("This is a warning message"); #10 $error; #10 $error("This is an error message"); - #1000 $fatal("This is a a fatal message that fails the test"); + #1000 $fatal(1, "This is a a fatal message that fails the test"); end endmodule diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 9371743c..211805e9 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -1063,6 +1063,11 @@ static int system_function_overload(char *userdata) systfref = vpi_handle(vpiSysTfCall, NULL); args_iter = vpi_iterate(vpiArgument, systfref); + // The first argument to fatal is the FinishNum which we discard + if (args_iter && *userdata == systf_fatal_level) { + argh = vpi_scan(args_iter); + } + if (args_iter) { // Grab the value of the first argument argh = vpi_scan(args_iter); From c2cdf21df50b43623ca3c3c2fce1fd4735a37f4a Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 25 Mar 2014 17:34:38 +0000 Subject: [PATCH 0488/2656] Updates to simulator makefiles for Aldec and Modelsim --- lib/Makefile | 2 +- makefiles/simulators/Makefile.aldec | 18 ++++++++------- makefiles/simulators/Makefile.modelsim | 32 ++++++++++++++++++++------ 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index 9b24cbc4..d21c49b4 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -53,7 +53,7 @@ $(LIB_DIR)/libcocotb.so: $(SIM_ROOT)/lib/embed/gpi_embed.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/embed $(LIB_DIR)/libgpi.so: $(SIM_ROOT)/lib/$(GPI_IMPL)_shim/gpi_$(GPI_IMPL).c | $(LIB_DIR) - make -C $(SIM_ROOT)/lib/$(GPI_IMPL)_shim + make -C $(SIM_ROOT)/lib/$(GPI_IMPL)_shim EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) $(LIB_DIR)/libsim.so: $(SIM_ROOT)/lib/simulator/simulatormodule.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/simulator diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 057d761f..4fa5b201 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -38,12 +38,12 @@ endif EXTRA_LIBS = -laldecpli -lstdc++ -lsupc++ EXTRA_LIBDIRS = -L$(ALDEC_PATH)/bin/Linux64 -ALOG_ARGS += "+define+COCOTB_SIM -dbg" -ACOM_ARGS += "-dbg" +ALOG_ARGS += +define+COCOTB_SIM -dbg -pli libgpi +ACOM_ARGS += -dbg ifeq ($(COVERAGE),1) - ASIM_ARGS += "-acdb" - ALOG_ARGS += "-coverage sb" + ASIM_ARGS += -acdb + ALOG_ARGS += -coverage sb endif ifeq ($(GPI_IMPL),vpi) @@ -62,10 +62,12 @@ endif $(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(SIM_BUILD) echo "alib work" > $@ echo "set worklib work" >> $@ - echo -e $(foreach src,$(VHDL_SOURCES),\ - "acom $(ACOM_ARGS) $(src)\n") >> $@ - echo -e $(foreach src,$(VERILOG_SOURCES),\ - "alog $(ALOG_ARGS) $(src)\n") >> $@ +ifdef VHDL_SOURCES + echo "acom $(ACOM_ARGS) $(VHDL_SOURCES)" >> $@ +endif +ifdef VERILOG_SOURCES + echo "alog $(ALOG_ARGS) $(VERILOG_SOURCES)" >> $@ +endif ifdef SCRIPT_FILE echo "do $(SCRIPT_FILE)" >> $@ endif diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index d20446a0..54de8a26 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -32,15 +32,33 @@ VPI_LIB := vpi # Modelsim is 32-bit only ARCH:=i686 +ifdef VERILOG_INCLUDE_DIRS +VLOG_ARGS += +incdir+$(VERILOG_INCLUDE_DIRS) +endif -sim: - -@rm -rf results.xml - $(MAKE) results.xml +ifeq ($(GUI),1) +SIM_CMD = vsim -gui +else +SIM_CMD = vsim -c +endif -results.xml: $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) - vlib work - vlog -writetoplevels modelsim.tops '-timescale' '1ns/1ns' '-mfcu' '+acc=rmb' '-sv' $(VERILOG_SOURCES) + +runsim.do : $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) + echo "vlib work" > $@ + echo "vlog -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(VERILOG_SOURCES)" >> $@ + echo "vsim -pli libgpi.so $(VSIM_ARGS) work.$(TOPLEVEL) -onfinish stop" >> $@ +ifneq ($(GUI),1) + echo "run -all" >> $@ + echo "exit" >> $@ +endif + + +results.xml: runsim.do $(COCOTB_LIBS) LD_LIBRARY_PATH=$(LIB_DIR): MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ - vsim -c -do "onElabError resume; run -all; exit" -f modelsim.tops -pli libgpi.so 2>&1 | tee sim.log + $(SIM_CMD) -do runsim.do 2>&1 | tee sim.log + +clean:: + -rm -rf runsim.do + -rm -rf sim.log From 3d012f5ec4874d89ca92fb70f975ea2128dc098b Mon Sep 17 00:00:00 2001 From: Chiggs Date: Tue, 25 Mar 2014 20:39:31 +0000 Subject: [PATCH 0489/2656] Ensure we unprime all triggers when tearing down after a test --- cocotb/scheduler.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index c16c0e03..cf249c98 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -59,7 +59,7 @@ import cocotb import cocotb.decorators -from cocotb.triggers import Trigger, GPITrigger, Timer, ReadOnly, _NextTimeStep, ReadWrite +from cocotb.triggers import Trigger, GPITrigger, Timer, ReadOnly, _NextTimeStep, _ReadWrite from cocotb.log import SimLog from cocotb.result import TestComplete, TestError, ReturnValue, raise_error @@ -122,7 +122,7 @@ class Scheduler(object): # Singleton events, recycled to avoid spurious object creation _readonly = ReadOnly() _next_timestep = _NextTimeStep() - _readwrite = ReadWrite() + _readwrite = _ReadWrite() _timer1 = Timer(1) _timer0 = Timer(0) @@ -167,7 +167,8 @@ def default_scheduling_algorithm(self): if not self._terminate and self._writes: if self._mode == Scheduler._MODE_NORMAL: - self._readwrite.prime(self.react) + if not self._readwrite.primed: + self._readwrite.prime(self.react) elif not self._next_timestep.primed: self._next_timestep.prime(self.react) @@ -177,9 +178,10 @@ def default_scheduling_algorithm(self): for t in self._trigger2coros: t.unprime() - if self._timer1.primed: - self.log.debug("Un-priming already primed Timer trigger") - self._timer1.unprime() + for t in [self._readwrite, self._readonly, self._next_timestep, \ + self._timer1, self._timer0]: + if t.primed: + t.unprime() self._timer1.prime(self.begin_test) self._trigger2coros = collections.defaultdict(list) From ddd140d5070b64cd9b7b3b22aee23b12c112cd0e Mon Sep 17 00:00:00 2001 From: Chiggs Date: Tue, 25 Mar 2014 21:05:30 +0000 Subject: [PATCH 0490/2656] Ensure we advance after every schedule, otherwise we don't start the next test --- cocotb/scheduler.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index cf249c98..9d3bd69e 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -216,6 +216,7 @@ def begin_test(self, trigger=None): test = self._entrypoint self._entrypoint = None self.schedule(test) + self.advance() if _profiling: _profile.disable() @@ -418,6 +419,7 @@ def add(self, coroutine): self.log.debug("Adding new coroutine %s" % coroutine.__name__) self.schedule(coroutine) + self.advance() return coroutine def new_test(self, coroutine): From d26c499b70a12f492548884ea25a272d1751f2af Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sat, 29 Mar 2014 17:40:06 +0000 Subject: [PATCH 0491/2656] Correct erroneous address --- cocotb/drivers/amba.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/drivers/amba.py b/cocotb/drivers/amba.py index e91ad751..a35278c3 100644 --- a/cocotb/drivers/amba.py +++ b/cocotb/drivers/amba.py @@ -41,7 +41,7 @@ class AXI4LiteMaster(BusDriver): TODO: Kill all pending transactions if reset is asserted... """ - _signals = ["ARESETn", + _signals = ["ARESETN", "AWVALID", "AWADDR", "AWREADY", # Write address channel "WVALID", "WREADY", "WDATA", "WSTRB", # Write data channel "BVALID", "BREADY", "BRESP", # Write response channel @@ -151,7 +151,7 @@ def read(self, address, sync=True): while True: yield ReadOnly() - if self.bus.WREADY.value: + if self.bus.ARREADY.value: break yield RisingEdge(self.clock) From c725d11dc6f484a7e1baf5158ec5a2f3a2d08d33 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 30 Mar 2014 10:56:53 +0100 Subject: [PATCH 0492/2656] Tweak to scheduler and coroutines * Fix for coroutines not marking themselves as complete * Scheduler only advances on calls from the simulator * Added note about possible problem with ReadOnly triggers --- cocotb/decorators.py | 3 +++ cocotb/scheduler.py | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index a26afab8..aab25bfb 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -109,10 +109,13 @@ def send(self, value): raise except ReturnValue as e: self.retval = e.retval + self._finished = True raise CoroutineComplete(callback=self._finished_cb) except StopIteration: + self._finished = True raise CoroutineComplete(callback=self._finished_cb) except Exception as e: + self._finished = True raise_error(self, "Send raised exception: %s" % (str(e))) def throw(self, exc): diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 9d3bd69e..09b3117d 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -29,6 +29,11 @@ """ Coroutine scheduler. + + +FIXME: We have a problem here. If a coroutine schedules a read-only but we +also have pending writes we have to schedule the ReadWrite callback before +the ReadOnly (and this is invalid, at least in Modelsim). """ import collections import os @@ -335,7 +340,8 @@ def react(self, trigger, depth=0): str(self._pending_triggers[0]))) self.react(self._pending_triggers.pop(0), depth=depth+1) - if not depth: + # We only advance for GPI triggers + if not depth and isinstance(trigger, GPITrigger): self.advance() if _debug: From 58b602006f7847b0475340bacd7b4854240fef5d Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sun, 6 Apr 2014 08:38:51 +0100 Subject: [PATCH 0493/2656] Issue #122 expand work-around to include "value" attribute --- cocotb/handle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 8c49302f..1611c1a5 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -93,7 +93,7 @@ def _raise_testerror(self, msg): def __setattr__(self, name, value): """Provide transparent access to signals""" - if not name.startswith('_') and not name in ["name", "fullname", "log"] \ + if not name.startswith('_') and not name in ["name", "fullname", "log", "value"] \ and self.__hasattr__(name): getattr(self, name).setcachedvalue(value) return From ae879593c07b5e235ac6501f91f40fc618e7f991 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Thu, 10 Apr 2014 19:53:08 +0100 Subject: [PATCH 0494/2656] Fix typo in plusargs example --- examples/plusargs/tb_top.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/plusargs/tb_top.v b/examples/plusargs/tb_top.v index 358e8442..a9fc5b12 100644 --- a/examples/plusargs/tb_top.v +++ b/examples/plusargs/tb_top.v @@ -36,7 +36,7 @@ initial begin $display("Plusargs test"); result = $value$plusargs("foo=%s", foo_string); $display("Plusarg foo has value %0s", foo_string); - #1 $diplay("Test running"); + #1 $display("Test running"); end endmodule //: tb_top From c6eef03267896b0f0b62902b2454d21a0d8b68d0 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 15 Apr 2014 16:00:56 +0100 Subject: [PATCH 0495/2656] Add an AvalonMemory object --- cocotb/drivers/avalon.py | 128 +++++++++++++++++++++++++++++++++++---- 1 file changed, 116 insertions(+), 12 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 849d7c7e..42455e5a 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -31,12 +31,15 @@ NB Currently we only support a very small subset of functionality """ +import random + +import cocotb from cocotb.decorators import coroutine from cocotb.triggers import RisingEdge, ReadOnly, NextTimeStep, Event from cocotb.drivers import BusDriver, ValidatedBusDriver from cocotb.utils import hexdump from cocotb.binary import BinaryValue -from cocotb.result import ReturnValue +from cocotb.result import ReturnValue, TestError class AvalonMM(BusDriver): @@ -48,14 +51,23 @@ class AvalonMM(BusDriver): Blocking operation is all that is supported at the moment, and for the near future as well Posted responses from a slave are not supported. """ - _signals = ["readdata", "read", "write", "waitrequest", "writedata", "address"] + _signals = ["address"] + _optional_signals = ["readdata", "read", "write", "waitrequest", "writedata", "readdatavalid"] def __init__(self, entity, name, clock): BusDriver.__init__(self, entity, name, clock) + self._can_read = False + self._can_write = False # Drive some sensible defaults (setimmediatevalue to avoid x asserts) - self.bus.read.setimmediatevalue(0) - self.bus.write.setimmediatevalue(0) + if hasattr(self.bus, "read"): + self.bus.read.setimmediatevalue(0) + self._can_read = True + + if hasattr(self.bus, "write"): + self.bus.write.setimmediatevalue(0) + self._can_write = True + self.bus.address.setimmediatevalue(0) def read(self, address): @@ -74,6 +86,7 @@ def __init__(self, entity, name, clock): self.busy_event = Event("%s_busy" % name) self.busy = False + @coroutine def _acquire_lock(self): if self.busy: @@ -93,6 +106,10 @@ def read(self, address): but syntactically it blocks. See http://www.altera.com/literature/manual/mnl_avalon_spec_1_3.pdf """ + if not self._can_read: + self.log.error("Cannot read - have no read signal") + raise TestError("Attempt to read on a write-only AvalonMaster") + yield self._acquire_lock() # Apply values for next clock edge @@ -101,7 +118,8 @@ def read(self, address): self.bus.read <= 1 # Wait for waitrequest to be low - yield self._wait_for_nsignal(self.bus.waitrequest) + if hasattr(self.bus, "waitrequest"): + yield self._wait_for_nsignal(self.bus.waitrequest) # Assume readLatency = 1 # FIXME need to configure this, should take a dictionary of Avalon properties. @@ -124,6 +142,10 @@ def write(self, address, value): value. See http://www.altera.com/literature/manual/mnl_avalon_spec_1_3.pdf """ + if not self._can_write: + self.log.error("Cannot write - have no write signal") + raise TestError("Attempt to write on a read-only AvalonMaster") + yield self._acquire_lock() # Apply valuse to bus @@ -133,7 +155,8 @@ def write(self, address, value): self.bus.write <= 1 # Wait for waitrequest to be low - count = yield self._wait_for_nsignal(self.bus.waitrequest) + if hasattr(self.bus, "waitrequest"): + count = yield self._wait_for_nsignal(self.bus.waitrequest) # Deassert write yield RisingEdge(self.clock) @@ -141,13 +164,94 @@ def write(self, address, value): self._release_lock() -class AvalonSlave(AvalonMM): - """Avalon-MM Slave - - This is not supported at the moment +class AvalonMemory(BusDriver): """ - def __init__(self, entity, name, clock): - AvalonMM.__init__(self, entity, name, clock) + Emulate a memory, with back-door access + """ + _signals = ["address"] + _optional_signals = ["write", "read", "writedata", "readdatavalid", "readdata"] + + def __init__(self, entity, name, clock, readlatency_min=1, readlatency_max=1, memory=None): + BusDriver.__init__(self, entity, name, clock) + + self._readable = False + self._writeable = False + + if hasattr(self.bus, "readdata"): + self._width = len(self.bus.readdata) + self._readable = True + + if hasattr(self.bus, "writedata"): + self._width = len(self.bus.writedata) + self._writeable = True + + if not self._readable and not self._writeable: + raise TestError("Attempt to instantiate useless memory") + + # Allow dual port RAMs by referencing the same dictionary + if memory is None: + self._mem = {} + else: + self._mem = memory + + self._val = BinaryValue(bits=self._width, bigEndian=False) + self._readlatency_min = readlatency_min + self._readlatency_max = readlatency_max + self._responses = [] + self._coro = cocotb.fork(self._respond()) + + if hasattr(self.bus, "readdatavalid"): + self.bus.readdatavalid.setimmediatevalue(0) + + def _pad(self): + """Pad response queue up to read latency""" + l = random.randint(self._readlatency_min, self._readlatency_max) + while len(self._responses) < l: + self._responses.append(None) + + @coroutine + def _respond(self): + """ + Coroutine to response to the actual requests + """ + edge = RisingEdge(self.clock) + while True: + yield edge + + if self._responses: + resp = self._responses.pop(0) + else: + resp = None + + if resp is not None: + if resp is True: + self._val.binstr = "x"*self._width + else: + self._val.integer = resp + self.log.debug("sending 0x%x (%s)" % (self._val.integer, self._val.binstr)) + self.bus.readdata <= self._val + self.bus.readdatavalid <= 1 + else: + self.bus.readdatavalid <= 0 + + yield ReadOnly() + + if self._readable and self.bus.read.value: + self._pad() + addr = self.bus.address.value.integer + if addr not in self._mem: + self.log.warning("Attempt to read from uninitialised address 0x%x" % addr) + self._responses.append(True) + else: + self.log.debug("Read from address 0x%x returning 0x%x" % (addr, self._mem[addr])) + self._responses.append(self._mem[addr]) + + if self._writeable and self.bus.write.value: + addr = self.bus.address.value.integer + data = self.bus.writedata.value.integer + self.log.debug("Write to address 0x%x -> 0x%x" % (addr, data)) + self._mem[addr] = data + class AvalonST(ValidatedBusDriver): From df7ae2e3a6fb510bdce2906cb1664cfe0e50602b Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 26 Apr 2014 13:47:46 +0100 Subject: [PATCH 0496/2656] Fixed #123: Remove embedded "\0" in format string --- lib/vhpi_shim/gpi_vhpi.c | 2 +- lib/vpi_shim/gpi_vpi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/vhpi_shim/gpi_vhpi.c b/lib/vhpi_shim/gpi_vhpi.c index 995c3870..992499bd 100644 --- a/lib/vhpi_shim/gpi_vhpi.c +++ b/lib/vhpi_shim/gpi_vhpi.c @@ -568,7 +568,7 @@ static char *gpi_copy_name(const char *name) name = null; } - snprintf(result, len, "%s\0", name); + snprintf(result, len, "%s", name); return result; } diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 211805e9..05be59a4 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -470,7 +470,7 @@ static char *gpi_copy_name(const char *name) name = null; } - snprintf(result, len, "%s\0", name); + snprintf(result, len, "%s", name); return result; } From 04b607684dbca9849a97eb61783fc6be9ed9221f Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 26 Apr 2014 13:52:15 +0100 Subject: [PATCH 0497/2656] Issue #124: Incorporate @nicklfitz updates to Questa Makefile --- makefiles/simulators/Makefile.questa | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index a6dbc754..ea7ff178 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -29,10 +29,7 @@ VPI_FILE := $(LIB_DIR)/libgpi.so -all: $(SIM_BUILD) sim - -.PHONY: sim -sim: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) +results.xml: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(PYTHON_LIBDIR):$(PYTHON_DYNLIBDIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ qverilog -64 -R -pli $(VPI_FILE) - +acc $(EXTRA_ARGS) $(VERILOG_SOURCES) From 13807634563b1f153618314bcef270326dc32c48 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Wed, 30 Apr 2014 18:42:31 +0100 Subject: [PATCH 0498/2656] Preserve LD_LIBRARY_PATH variable when invoking simulators --- makefiles/simulators/Makefile.ghdl | 4 ++-- makefiles/simulators/Makefile.icarus | 2 +- makefiles/simulators/Makefile.ius | 2 +- makefiles/simulators/Makefile.modelsim | 2 +- makefiles/simulators/Makefile.questa | 2 +- makefiles/simulators/Makefile.vcs | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/makefiles/simulators/Makefile.ghdl b/makefiles/simulators/Makefile.ghdl index 8b411f46..e19feff7 100644 --- a/makefiles/simulators/Makefile.ghdl +++ b/makefiles/simulators/Makefile.ghdl @@ -33,8 +33,8 @@ analyse: $(VHDL_SOURCES) $(SIM_BUILD) cd $(SIM_BUILD) && ghdl -a $(VHDL_SOURCES) results.xml: analyse $(COCOTB_LIBS) - cd $(SIM_BUILD) && LD_LIBRARY_PATH=$(LIB_DIR) ghdl -e -Wl,-L$(LIB_DIR) -Wl,-lgpivhpi $(TOPLEVEL) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) \ + cd $(SIM_BUILD) && LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) ghdl -e -Wl,-L$(LIB_DIR) -Wl,-lgpivhpi $(TOPLEVEL) + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ ./$(SIM_BUILD)/$(TOPLEVEL) diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index 85e187f3..abd26e11 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -33,7 +33,7 @@ $(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) # Execution phase results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index cdf7f5c4..d861c3b6 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -28,7 +28,7 @@ ############################################################################### results.xml: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) - LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ irun -sv $(EXTRA_ARGS) -64bit -loadvpi $(LIB_DIR)/libgpi:vlog_startup_routines_bootstrap +access+rwc $(VERILOG_SOURCES) clean:: diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index 54de8a26..377ca474 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -54,7 +54,7 @@ endif results.xml: runsim.do $(COCOTB_LIBS) - LD_LIBRARY_PATH=$(LIB_DIR): MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + LD_LIBRARY_PATH=$(LIB_DIR)::$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ $(SIM_CMD) -do runsim.do 2>&1 | tee sim.log diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index ea7ff178..c30e6548 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -30,7 +30,7 @@ VPI_FILE := $(LIB_DIR)/libgpi.so results.xml: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(PYTHON_LIBDIR):$(PYTHON_DYNLIBDIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(PYTHON_LIBDIR):$(PYTHON_DYNLIBDIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ qverilog -64 -R -pli $(VPI_FILE) - +acc $(EXTRA_ARGS) $(VERILOG_SOURCES) clean:: diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index 2d1edf41..87f76308 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -42,12 +42,12 @@ $(SIM_BUILD)/pli.tab : # Compilation phase $(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(COCOTB_LIBS) $(CUSTOM_COMPILE_DEPS) cd $(SIM_BUILD) && \ - LD_LIBRARY_PATH=$(LIB_DIR) TOPLEVEL=$(TOPLEVEL) \ + LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) TOPLEVEL=$(TOPLEVEL) \ vcs +acc+1 +vpi -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -debug -load libgpi.so $(COMPILE_ARGS) $(VERILOG_SOURCES) # Execution phase results.xml: $(SIM_BUILD)/simv $(PYTHON_FILES) $(CUSTOM_SIM_DEPS) - -PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + -PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ $(SIM_BUILD)/simv +define+COCOTB_SIM=1 $(SIM_ARGS) $(EXTRA_ARGS) From 728a9e427ec78585f6ba0d9f386a3d8c93f9c7e4 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sat, 10 May 2014 21:35:07 +0100 Subject: [PATCH 0499/2656] Add Modelsim to list of compatible simulators --- documentation/source/introduction.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index 91b178f2..b5afe841 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -15,6 +15,7 @@ What is cocotb? * Synopsys VCS * Aldec Riviera-PRO * Cadance Incisive +* Mentor Modelsim **Cocotb** was developed by `Potential Ventures _` with the support of `Solarflare Communications Ltd `_ and contributions from Gordon McGregor and Finn Grimwood. From fa1fd6e8fa7a3501d6c4b5c5516252ba16dd6805 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Sat, 10 May 2014 21:36:58 +0100 Subject: [PATCH 0500/2656] Added note about running a VHDL example --- documentation/source/quickstart.rst | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index 1c8ae86d..61ea52d1 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -34,13 +34,14 @@ To run a test using a different simulator: $> make SIM=vcs -Compatible Simulators ---------------------- +Running a VHDL example +---------------------- -* Icarus Verilog -* Synopsys VCS -* Aldec Riviera-PRO -* Cadance Incisive +The endian swapper example includes both a VHDL and Verlog RTL implementation. The Cocotb testbench can execute against either implementation using VPI for Verilog and VHPI for VHDL. To run the test suite against the VHDL implementation use the following command (a VHPI capable simulator must be used): + +.. code-block:: bash + + $> make SIM=aldec GPI_IMPL=vhpi From 209c10a3c1d4652ecda50e4403439fd51941672d Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 11 May 2014 09:14:00 +0100 Subject: [PATCH 0501/2656] Background work for issue #125: C HAL, SWIG wrapper, IO module --- examples/endian_swapper/cosim/Makefile | 21 +++ examples/endian_swapper/cosim/io.c | 120 ++++++++++++++++++ examples/endian_swapper/cosim/io.h | 36 ++++++ examples/endian_swapper/cosim/io_module.h | 42 ++++++ .../endian_swapper/hal/endian_swapper_hal.c | 66 ++++++++++ .../endian_swapper/hal/endian_swapper_hal.h | 70 ++++++++++ .../endian_swapper/hal/endian_swapper_regs.h | 57 +++++++++ 7 files changed, 412 insertions(+) create mode 100644 examples/endian_swapper/cosim/Makefile create mode 100644 examples/endian_swapper/cosim/io.c create mode 100644 examples/endian_swapper/cosim/io.h create mode 100644 examples/endian_swapper/cosim/io_module.h create mode 100644 examples/endian_swapper/hal/endian_swapper_hal.c create mode 100644 examples/endian_swapper/hal/endian_swapper_hal.h create mode 100644 examples/endian_swapper/hal/endian_swapper_regs.h diff --git a/examples/endian_swapper/cosim/Makefile b/examples/endian_swapper/cosim/Makefile new file mode 100644 index 00000000..1e94c53f --- /dev/null +++ b/examples/endian_swapper/cosim/Makefile @@ -0,0 +1,21 @@ + +all: io_module.so _hal.so + +io_module.o: io.c + gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -DNDEBUG -I/usr/include/python2.7 -c $< -o $@ + +io_module.so: io_module.o + gcc -pthread -shared -Wl,-z,relro $< -L/usr/lib64 -lpython2.7 -o $@ + +_hal.so : ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so + gcc -g -ldl -shared -fPIC -I$(shell pwd) -I/usr/include/python2.7 -I../hal ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so -o $@ + +endian_swapper_hal_wrap.c: ../hal/endian_swapper_hal.h + swig -python -outcurrentdir ../hal/endian_swapper_hal.h + +.PHONY: clean +clean: + -rm -rf hal.py* + -rm -rf *.so + -rm -rf *.o + -rm -rf endian_swapper_hal_wrap.c diff --git a/examples/endian_swapper/cosim/io.c b/examples/endian_swapper/cosim/io.c new file mode 100644 index 00000000..9bce133e --- /dev/null +++ b/examples/endian_swapper/cosim/io.c @@ -0,0 +1,120 @@ +/****************************************************************************** +* Copyright (c) 2014 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +// This module bridges between the testbench world and the HAL + +// We export a READ/WRITE function with the same name as normal hardware +// access + + +#include "io.h" + +#include +#include "io_module.h" + +// Python read function just takes an offset in bytes and a buffer +static PyObject *pWrFunction; +// Python read function just takes an offset in bytes and returns a value +static PyObject *pRdFunction; + +// Functions called by Python +static PyObject *set_write_function(PyObject *self, PyObject *args) { + + pWrFunction = PyTuple_GetItem(args, 0); + Py_INCREF(pWrFunction); + + PyObject *retstr = Py_BuildValue("s", "OK!"); + return retstr; +} + +static PyObject *set_read_function(PyObject *self, PyObject *args) { + + pRdFunction = PyTuple_GetItem(args, 0); + Py_INCREF(pRdFunction); + + PyObject *retstr = Py_BuildValue("s", "OK!"); + return retstr; +} + + +// Functions called by C (exported in a shared library) +unsigned int IORD(unsigned int base, unsigned int address) { + + printf("In IORD\n"); + +// *buffer = 4; +// return 1; + + if (!PyCallable_Check(pRdFunction)) { + printf("Read function not callable...\n"); + return 0; + } + +// PyObject *call_args = PyTuple_New(1); +// PyObject *rv; +// +// PyTuple_SetItem(call_args, 0, PyInt_FromLong(address)); +// +// printf("Attempting to call our read function...\n"); +// rv = PyObject_CallObject(pRdFunction, call_args); +// printf("Called!\n"); +// *buffer = PyInt_AsLong(rv); +// +// if (PyErr_Occurred()) +// PyErr_Print(); +// +// Py_DECREF(rv); +// Py_DECREF(call_args); + + return 1; +} + +int IOWR(unsigned int base, unsigned int address, unsigned int data) +{ + printf("In IOWR\n"); + + if (!PyCallable_Check(pWrFunction)) { + printf("Write function isn't callable...\n"); + return 0; + } + +// PyObject *call_args = PyTuple_New(2); +// PyObject *rv; +// +// PyTuple_SetItem(call_args, 0, PyInt_FromLong(address)); +// PyTuple_SetItem(call_args, 1, PyInt_FromLong(value)); +// +// rv = PyObject_CallObject(pWrFunction, call_args); +// +// if (PyErr_Occurred()) +// PyErr_Print(); +// +// Py_DECREF(rv); +// Py_DECREF(call_args); + + return 1; +} diff --git a/examples/endian_swapper/cosim/io.h b/examples/endian_swapper/cosim/io.h new file mode 100644 index 00000000..5a998342 --- /dev/null +++ b/examples/endian_swapper/cosim/io.h @@ -0,0 +1,36 @@ +/****************************************************************************** +* Copyright (c) 2014 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +// Simulation-only implementation of IO functions for cosimulation + +#ifndef __IO_H__ +#define __IO_H__ + +extern unsigned int IORD(unsigned int base, unsigned int address); +extern int IOWR(unsigned int base, unsigned int address, unsigned int data); + +#endif // __IO_H__ \ No newline at end of file diff --git a/examples/endian_swapper/cosim/io_module.h b/examples/endian_swapper/cosim/io_module.h new file mode 100644 index 00000000..0f22109e --- /dev/null +++ b/examples/endian_swapper/cosim/io_module.h @@ -0,0 +1,42 @@ +/****************************************************************************** +* Copyright (c) 2014 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +#ifndef _IO_MODULE_H +#define _IO_MODULE_H + +#include + +static PyObject *set_write_function(PyObject *self, PyObject *args); +static PyObject *set_read_function(PyObject *self, PyObject *args); + +static PyMethodDef IOModuleMethods[] = { + {"set_write_function", set_write_function, METH_VARARGS, "Set the write function"}, + {"set_read_function", set_read_function, METH_VARARGS, "Set the read function"}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +#endif \ No newline at end of file diff --git a/examples/endian_swapper/hal/endian_swapper_hal.c b/examples/endian_swapper/hal/endian_swapper_hal.c new file mode 100644 index 00000000..c9678b3e --- /dev/null +++ b/examples/endian_swapper/hal/endian_swapper_hal.c @@ -0,0 +1,66 @@ +/****************************************************************************** +* Copyright (c) 2014 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +// Implementation of the HAL +#include "io.h" +#include "endian_swapper_regs.h" +#include "endian_swapper_hal.h" + + +endian_swapper_state_t endian_swapper_init(unsigned int base) +{ + endian_swapper_state_t state; + state.base = base; + return state; +} + + +int endian_swapper_enable(endian_swapper_state_t *state) +{ + unsigned int control; + control = IORD_ENDIAN_SWAPPER_CONTROL_REG(state->base); + control |= ENDIAN_SWAPPER_ENABLE_MASK; + IOWR_ENDIAN_SWAPPER_CONTROL_REG(state->base, control); + return 0; +} + + +int endian_swapper_disable(endian_swapper_state_t *state) +{ + unsigned int control; + control = IORD_ENDIAN_SWAPPER_CONTROL_REG(state->base); + control ^= ENDIAN_SWAPPER_ENABLE_MASK; + IOWR_ENDIAN_SWAPPER_CONTROL_REG(state->base, control); + return 0; +} + + +int endian_swapper_get_count(endian_swapper_state_t *state) +{ + return IORD_ENDIAN_SWAPPER_PACKET_COUNT_REG(state->base); +} + diff --git a/examples/endian_swapper/hal/endian_swapper_hal.h b/examples/endian_swapper/hal/endian_swapper_hal.h new file mode 100644 index 00000000..850f2c68 --- /dev/null +++ b/examples/endian_swapper/hal/endian_swapper_hal.h @@ -0,0 +1,70 @@ +/****************************************************************************** +* Copyright (c) 2014 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +#ifndef __ENDIAN_SWAPPER_HAL_H__ +#define __ENDIAN_SWAPPER_HAL_H__ + + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +typedef struct endian_swapper_state_s +{ + unsigned int base; +} endian_swapper_state_t; + +endian_swapper_state_t endian_swapper_init(unsigned int base); + +int endian_swapper_enable(endian_swapper_state_t *state); +int endian_swapper_disable(endian_swapper_state_t *state); +int endian_swapper_get_count(endian_swapper_state_t *state); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +// Swig template for this file +#ifdef SWIG +%module hal +%{ +/* Includes the header in the wrapper code */ +#include "endian_swapper_hal.h" +%} + +/* Parse the header file to generate wrappers */ +%include "endian_swapper_hal.h" +#endif + + + + +#endif /* __ENDIAN_SWAPPER_HAL_H__ */ \ No newline at end of file diff --git a/examples/endian_swapper/hal/endian_swapper_regs.h b/examples/endian_swapper/hal/endian_swapper_regs.h new file mode 100644 index 00000000..f5f5dbf4 --- /dev/null +++ b/examples/endian_swapper/hal/endian_swapper_regs.h @@ -0,0 +1,57 @@ +/****************************************************************************** +* Copyright (c) 2014 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +// This file defines the register map for the endian swapper example design. +// +// byte offset 0: bit 0 [R/W] byteswap enable +// bits 31-1: [N/A] reserved +// byte offset 4: bits 31-0: [RO] packet count + + +#ifndef __ENDIAN_SWAPPER_REGS_H__ +#define __ENDIAN_SWAPPER_REGS_H__ + + +#define ENDIAN_SWAPPER_CONTROL_REG 0 +#define IORD_ENDIAN_SWAPPER_CONTROL_REG(base) \ + IORD(base, ENDIAN_SWAPPER_CONTROL_REG) + +#define IOWR_ENDIAN_SWAPPER_CONTROL_REG(base, data) \ + IOWR(base, ENDIAN_SWAPPER_CONTROL_REG, data) + +#define ENDIAN_SWAPPER_ENABLE_MASK (0x01) +#define ENDIAN_SWAPPER_ENABLE_OFFSET (0) + +#define ENDIAN_SWAPPER_PACKET_COUNT_REG 1 +#define IORD_ENDIAN_SWAPPER_PACKET_COUNT_REG(base) \ + IORD(base, ENDIAN_SWAPPER_PACKET_COUNT_REG) + +#define ENDIAN_SWAPPER_PACKET_COUNT_MASK (0xFFFFFFFF) +#define ENDIAN_SWAPPER_PACKET_COUNT_OFFSET (0) + + +#endif // __ENDIAN_SWAPPER_REGS_H__ \ No newline at end of file From 80ab37c1604fba6ef40fdfce48ecd0568c204569 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 11 May 2014 10:36:55 +0100 Subject: [PATCH 0502/2656] Issue #125: Simple test showing cocotb.function and unmodified HAL --- examples/endian_swapper/cosim/Makefile | 2 +- examples/endian_swapper/cosim/io.c | 80 +++++++++-------- examples/endian_swapper/cosim/io_module.h | 2 +- examples/endian_swapper/tests/Makefile | 10 ++- .../tests/test_endian_swapper_hal.py | 90 +++++++++++++++++++ 5 files changed, 142 insertions(+), 42 deletions(-) create mode 100644 examples/endian_swapper/tests/test_endian_swapper_hal.py diff --git a/examples/endian_swapper/cosim/Makefile b/examples/endian_swapper/cosim/Makefile index 1e94c53f..b8031f86 100644 --- a/examples/endian_swapper/cosim/Makefile +++ b/examples/endian_swapper/cosim/Makefile @@ -1,7 +1,7 @@ all: io_module.so _hal.so -io_module.o: io.c +io_module.o: io.c io_module.h io.h gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -DNDEBUG -I/usr/include/python2.7 -c $< -o $@ io_module.so: io_module.o diff --git a/examples/endian_swapper/cosim/io.c b/examples/endian_swapper/cosim/io.c index 9bce133e..fedb57d7 100644 --- a/examples/endian_swapper/cosim/io.c +++ b/examples/endian_swapper/cosim/io.c @@ -64,57 +64,59 @@ static PyObject *set_read_function(PyObject *self, PyObject *args) { // Functions called by C (exported in a shared library) unsigned int IORD(unsigned int base, unsigned int address) { - printf("In IORD\n"); - -// *buffer = 4; -// return 1; + unsigned int value; if (!PyCallable_Check(pRdFunction)) { printf("Read function not callable...\n"); return 0; } -// PyObject *call_args = PyTuple_New(1); -// PyObject *rv; -// -// PyTuple_SetItem(call_args, 0, PyInt_FromLong(address)); -// -// printf("Attempting to call our read function...\n"); -// rv = PyObject_CallObject(pRdFunction, call_args); -// printf("Called!\n"); -// *buffer = PyInt_AsLong(rv); -// -// if (PyErr_Occurred()) -// PyErr_Print(); -// -// Py_DECREF(rv); -// Py_DECREF(call_args); - - return 1; + PyObject *call_args = PyTuple_New(1); + PyObject *rv; + + PyTuple_SetItem(call_args, 0, PyInt_FromLong(base + address)); + + rv = PyObject_CallObject(pRdFunction, call_args); + value = PyInt_AsLong(rv); + + if (PyErr_Occurred()) + PyErr_Print(); + + Py_DECREF(rv); + Py_DECREF(call_args); + + return value; } -int IOWR(unsigned int base, unsigned int address, unsigned int data) +int IOWR(unsigned int base, unsigned int address, unsigned int value) { - printf("In IOWR\n"); if (!PyCallable_Check(pWrFunction)) { printf("Write function isn't callable...\n"); - return 0; + return -1; } -// PyObject *call_args = PyTuple_New(2); -// PyObject *rv; -// -// PyTuple_SetItem(call_args, 0, PyInt_FromLong(address)); -// PyTuple_SetItem(call_args, 1, PyInt_FromLong(value)); -// -// rv = PyObject_CallObject(pWrFunction, call_args); -// -// if (PyErr_Occurred()) -// PyErr_Print(); -// -// Py_DECREF(rv); -// Py_DECREF(call_args); - - return 1; + PyObject *call_args = PyTuple_New(2); + PyObject *rv; + + PyTuple_SetItem(call_args, 0, PyInt_FromLong(base + address)); + PyTuple_SetItem(call_args, 1, PyInt_FromLong(value)); + + rv = PyObject_CallObject(pWrFunction, call_args); + + if (PyErr_Occurred()) + PyErr_Print(); + + Py_DECREF(rv); + Py_DECREF(call_args); + + return 0; } + +PyMODINIT_FUNC +initio_module(void) +{ + PyObject* io_module; + io_module = Py_InitModule("io_module", io_module_methods); +} + diff --git a/examples/endian_swapper/cosim/io_module.h b/examples/endian_swapper/cosim/io_module.h index 0f22109e..f0ecc6df 100644 --- a/examples/endian_swapper/cosim/io_module.h +++ b/examples/endian_swapper/cosim/io_module.h @@ -33,7 +33,7 @@ static PyObject *set_write_function(PyObject *self, PyObject *args); static PyObject *set_read_function(PyObject *self, PyObject *args); -static PyMethodDef IOModuleMethods[] = { +static PyMethodDef io_module_methods[] = { {"set_write_function", set_write_function, METH_VARARGS, "Set the write function"}, {"set_read_function", set_read_function, METH_VARARGS, "Set the read function"}, {NULL, NULL, 0, NULL} /* Sentinel */ diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index 7850a5f3..b53dd21b 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -38,7 +38,6 @@ PWD=$(shell pwd) COCOTB=$(PWD)/../../.. - ifeq ($(GPI_IMPL),vpi) VERILOG_SOURCES = $(PWD)/../hdl/endian_swapper.sv endif @@ -49,11 +48,20 @@ endif MODULE=test_endian_swapper +CUSTOM_COMPILE_DEPS = hal +PYTHONPATH := $(PWD)/../cosim:$(PYTHONPATH) +export PYTHONPATH + +LD_LIBRARY_PATH := $(PWD)/../cosim:$(LD_LIBRARY_PATH) +export LD_LIBRARY_PATH include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim +.PHONY: hal +hal: + -cd ../cosim && make # Stuff below is useful for profiling # Need grof2dot from https://code.google.com/p/jrfonseca.gprof2dot/ diff --git a/examples/endian_swapper/tests/test_endian_swapper_hal.py b/examples/endian_swapper/tests/test_endian_swapper_hal.py new file mode 100644 index 00000000..de4cf843 --- /dev/null +++ b/examples/endian_swapper/tests/test_endian_swapper_hal.py @@ -0,0 +1,90 @@ +''' +Copyright (c) 2013 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +import logging + +import cocotb +from cocotb.triggers import RisingEdge, Timer +from cocotb.clock import Clock +from cocotb.drivers.avalon import AvalonMaster +from cocotb.result import ReturnValue, TestFailure + +import hal +import io_module + +@cocotb.coroutine +def reset(dut, duration=10000): + dut.log.debug("Resetting DUT") + dut.reset_n = 0 + dut.stream_in_valid = 0 + yield Timer(duration) + yield RisingEdge(dut.clk) + dut.reset_n = 1 + dut.log.debug("Out of reset") + + +@cocotb.test() +def initial_hal_test(dut, debug=True): + """Example of using the software HAL against cosim testbench""" + + cocotb.fork(Clock(dut.clk, 5000).start()) + yield reset(dut) + + # Create the avalon master and direct our HAL calls to that + master = AvalonMaster(dut, "csr", dut.clk) + if debug: + master.log.setLevel(logging.DEBUG) + + @cocotb.function + def read(address): + master.log.debug("External source: reading address 0x%08X" % address) + value = yield master.read(address) + master.log.debug("Reading complete: got value 0x%08x" % value) + raise ReturnValue(value) + + @cocotb.function + def write(address, value): + master.log.debug("Write called for 0x%08X -> %d" % (address, value)) + yield master.write(address, value) + master.log.debug("Write complete") + + io_module.set_write_function(write) + io_module.set_read_function(read) + + dut.log.info("READ/WRITE functions set up, initialising HAL") + + state = hal.endian_swapper_init(0) + + # Check the actual value + if dut.byteswapping.value: + raise TestFailure("Byteswapping is enabled but havne't configured DUT") + + yield cocotb.external(hal.endian_swapper_enable)(state) + + if not dut.byteswapping.value: + raise TestFailure("Byteswapping wasn't enabled after calling endian_swapper_enable") + + dut.log.info("HAL call endian_swapper_enable successfully enabled the DUT") From c18a5b20aa4e952c182dec37fb449544c024a25a Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 11 May 2014 19:48:31 +0100 Subject: [PATCH 0503/2656] Issue #9: Fix broken auto-generated documentation --- cocotb/triggers.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 4bcb6c2e..4fdb48a8 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -83,7 +83,12 @@ class GPITrigger(Trigger): """ def __init__(self): Trigger.__init__(self) - self.cbhdl = simulator.create_callback(self) + + # Required to ensure documentation can build + if simulator is not None: + self.cbhdl = simulator.create_callback(self) + else: + self.cbhdl = None def unprime(self): """Unregister a prior registered timed callback""" From da2b25ccfcb630408bd550c6f2ff2af74f1f90dd Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 11 May 2014 21:55:02 +0100 Subject: [PATCH 0504/2656] Issue #125: Document the new HAL example --- .../source/diagrams/svg/hal_cosimulation.svg | 1 + .../source/diagrams/xml/hal_cosimulation.xml | 1 + documentation/source/hal_cosimulation.rst | 171 ++++++++++++++++++ documentation/source/index.rst | 1 + 4 files changed, 174 insertions(+) create mode 100644 documentation/source/diagrams/svg/hal_cosimulation.svg create mode 100644 documentation/source/diagrams/xml/hal_cosimulation.xml create mode 100644 documentation/source/hal_cosimulation.rst diff --git a/documentation/source/diagrams/svg/hal_cosimulation.svg b/documentation/source/diagrams/svg/hal_cosimulation.svg new file mode 100644 index 00000000..94440f00 --- /dev/null +++ b/documentation/source/diagrams/svg/hal_cosimulation.svg @@ -0,0 +1 @@ +
      SWIG
      [Not supported by viewer]
      Test
      [Not supported by viewer]
      HAL
      [Not supported by viewer]
      Shim library
      [Not supported by viewer]
      DUT
      [Not supported by viewer]
      Avalon-MM
      Driver
      [Not supported by viewer]
      external()read()/write()function()CPythonPythonRTL
      \ No newline at end of file diff --git a/documentation/source/diagrams/xml/hal_cosimulation.xml b/documentation/source/diagrams/xml/hal_cosimulation.xml new file mode 100644 index 00000000..8499db5d --- /dev/null +++ b/documentation/source/diagrams/xml/hal_cosimulation.xml @@ -0,0 +1 @@ +1Zhfb+I4EMA/DdLeQ1eQhLR9BArtSlepOrrq3qMvNsSSiZExBb59x8k4/8wG9gKU8gKe4InnN+OZsTv+aLF9VGQZP0vKRMfr0m3Hf+h4ntfvduHLSHaZ5Db0M8FccZqJeihYc8pWFZGWUmi+rAojmSQs0hXZTIqqsiWZM0cwjYhwpW+c6jiT3nlhIX9ifB7b1/TC++zJSu+sDspmZC30TSqCZ+bxglhdqd3+GNgoKUGN+bXYjpgwfKzpGZzJb57mi1QswYU0TwgQ9jsRa1yks+pNzDWbLklkxhtwWscfUrKKmdGBb3xnSjN04Z5FpCJcwSOTC6bVDv6CE/p3uAr0eM+GwKYA3QtRFpcg58sn6PB5rruwGX6g2fsR+OinT0UQWJObEFhMp0dwewUIbjBUGxHcng1B/woQ9PqHEUCCPBMCz42C6duPRweDkuuEos3+cB+UGRdiJIVU6QR/MhmNxiHIVzGhcoMT54pQDmAqfzSfU4C8r4G0fA7Fkp3XiqMbSq9spdtzHIeG5EU5HoHRxmgZY56qWnHE0lri+DT4+xThOA4vjLEWjt6xqc0/BcY7d1vHfAESwf9TBJV98Q0OXeKRRDHJtSLqdgwPP1/bYwSIfaBzQYxhre3wQsxdZYy2Lpcx2m6sVcl1e88B/JbJzfNzx2x+f0vA/gfFwcavmD0Dmy5zusH3S/K1LUlDS8MSOlAqpREJslrxCJiAeWr3C0RdO/jXDGDtZkgnwLVoeBh1zk01PvBCuVapZ0pe10TNmW1f0g3lclRMEA3eryhrhQQJlJCwrWYqIeLbXy4cMG2Kw0Qm8DVkW64NGRM65ncGBip+E7X/xcl2ERVOafJyOR0sx1Z4NFB8x4vksMQi61rnYUAH9TKfGYCzysfOmqI8ClCRX1eUWe0oSv2bG36cy/H2oORyxQg17p5sFGSNfY5v3hW4EayHT7ErbK9T8XZauC+xKwIH0WydRJrL5M/hnCFl2A6mDCcLoEvAcbtQmFNjAjUC1jUkgs8TwwWsgZLlD0354HCHNMAHC06pmTM8QXXJj6wNh8V9NwYnOS27PeXLTscyuQ4yh0/R5wITuDX3isAER9wvnI2MW3r/eXVPc5+BJbxcwMCwuNzNqllxC+6PPwA= \ No newline at end of file diff --git a/documentation/source/hal_cosimulation.rst b/documentation/source/hal_cosimulation.rst new file mode 100644 index 00000000..9c6824cd --- /dev/null +++ b/documentation/source/hal_cosimulation.rst @@ -0,0 +1,171 @@ +Tutorial: Driver Cosimulation +============================= + +Cocotb was designed to provide a common platform for hardware and software +developers to interact. By integrating systems early, ideally at the +block level, it's possible to find bugs earlier in the design process. + +For any given component that has a software interface there is typically a +software abstraction layer or driver which communicates with the hardware. In +this tutorial we will call unmodified production software from our testbench +and re-use the code written to configure the entity. + + + +Difficulties with Driver Co-simulation +-------------------------------------- + +Co-simulating *un-modified* production software against a block-level +testbench is not trivial - there are a couple of significant obstacles to +overcome: + + +Calling the HAL from a test +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Typically the software component (often referred to as a Hardware Abstraction +Layer or HAL) is written in C. We need to call this software from our test +written in Python. There are multiple ways to call C code from Python, in +this tutorial we'll use `SWIG`_ to generate Python bindings for our HAL. + + +Blocking in the driver +~~~~~~~~~~~~~~~~~~~~~~ + +Another difficulty to overcome is the fact that the HAL is expecting to call +a low-level function to access the hardware, often something like ``ioread32``. +We need this call to block while simulation time advances and a value is +either read or written on the bus. To achieve this we link the HAL against +a C library that provides the low level read/write functions. These functions +in turn call into Cocotb and perform an the relevant access on the DUT. + + +Cocotb infrastructure +--------------------- + +There are two decorators provided to enable this flow, which are typically used +together to achieve the required functionality. The ``cocotb.external`` +decorator turns a normal function that isn't a coroutine into a blocking +coroutine (by running the function in a separate thread). The +``cocotb.function`` decorator allows a coroutine that consumes simulation time +to be called by a normal thread. The call sequence looks like this: + + +.. image:: diagrams/svg/hal_cosimulation.svg + + +Implementation +-------------- + + +Register Map +~~~~~~~~~~~~ + +The endian swapper has a very simple register map: + ++-------------+-------------+------+--------+------------+ +| Byte Offset | Register | Bits | Access | Description| ++=============+=============+======+========+============+ +|0 | CONTROL | 0 | R/W | Enable | +| | +------+--------+------------+ +| | | 31:1 | N/A | Reserved | ++-------------+-------------+------+--------+------------+ +|4 |PACKET_COUNT | 31:0 | RO | Num Packets| ++-------------+-------------+------+--------+------------+ + + +HAL +~~~ + +To keep things simple we use the same RTL from the :doc:`endian_swapper`. We +write a simplistic HAL which provides the following functions: + + +.. code-block:: c + + endian_swapper_enable(endian_swapper_state_t *state); + endian_swapper_disable(endian_swapper_state_t *state); + endian_swapper_get_count(endian_swapper_state_t *state); + + +These functions call ``IORD`` and ``IOWR`` - usually provided by the Altera +NIOS framework. + + +IO Module +~~~~~~~~~ + +This module acts as the bridge between the C HAL and the Python testbench. It +exposes the ``IORD`` and ``IOWR`` calls to link the HAL against, but also +provides a Python interface to allow the read/write bindings to be dynamically +set (through ``set_write_function`` and ``set_read_function`` module functions. + +In a more complicated scenario, this could act as an interconnect, dispatching +the access to the appropriate driver depending on address decoding, for +instance. + + +Testbench +~~~~~~~~~ + +First of all we set up a clock, create an Avalon Master interface and reset +the DUT. Then we create two functions that are wrapped with the +``cocotb.function`` decorator to be called when the HAL attempts to perform +a read or write. These are then passed to the `IO Module`_: + + +.. code-block:: python + + + @cocotb.function + def read(address): + master.log.debug("External source: reading address 0x%08X" % address) + value = yield master.read(address) + master.log.debug("Reading complete: got value 0x%08x" % value) + raise ReturnValue(value) + + @cocotb.function + def write(address, value): + master.log.debug("Write called for 0x%08X -> %d" % (address, value)) + yield master.write(address, value) + master.log.debug("Write complete") + + io_module.set_write_function(write) + io_module.set_read_function(read) + + +We can then intialise the HAL and call functions, using the ``cocotb.external`` +decorator to turn the normal function into a blocking coroutine that we can +``yield``: + + +.. code-block:: python + + state = hal.endian_swapper_init(0) + yield cocotb.external(hal.endian_swapper_enable)(state) + + +The HAL will perform whatever calls it needs, accessing the DUT through the +Avalon-MM driver, and control will return to the testbench when the function +returns. + +.. note:: The decorator is applied to the function before it is called + + + +Further Work +------------ + +In future tutorials we'll consider co-simulating unmodified drivers written +using ``mmap`` (for example built upon the `UIO framework`_) and consider +interfacing with emulators like `QEMU`_ to allow us to co-simulate when the +software needs to execute on a different processor architecture. + + +.. _SWIG: http://www.swig.org/ + +.. _UIO framework: https://www.kernel.org/doc/htmldocs/uio-howto/about.html + +.. _QEMU: http://wiki.qemu.org/Main_Page + + diff --git a/documentation/source/index.rst b/documentation/source/index.rst index ff8c22c1..3ebce1e8 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -16,6 +16,7 @@ Contents: coroutines endian_swapper ping_tun_tap + hal_cosimulation library_reference building roadmap From 3250a92eec5d0584c4e833145543cda929d01dab Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 11 May 2014 21:57:25 +0100 Subject: [PATCH 0505/2656] Issue #125: Add instruction on running the example! --- documentation/source/hal_cosimulation.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/documentation/source/hal_cosimulation.rst b/documentation/source/hal_cosimulation.rst index 9c6824cd..b1fd4aef 100644 --- a/documentation/source/hal_cosimulation.rst +++ b/documentation/source/hal_cosimulation.rst @@ -10,6 +10,15 @@ software abstraction layer or driver which communicates with the hardware. In this tutorial we will call unmodified production software from our testbench and re-use the code written to configure the entity. +For the impatient this tutorial is provided as an example with Cocotb. You can +run this example from a fresh checkout:: + + cd examples/endian_swapper/tests + make MODULE=test_endian_swapper_hal + +.. note:: `SWIG`_ is required to compile the example + + Difficulties with Driver Co-simulation From c8489ed315b8880e16070aa41b0c820dba50269e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 30 Apr 2014 17:02:42 +0100 Subject: [PATCH 0506/2656] Issue #127: Refactor the gpi/vpi layer, functionality same as before change --- include/gpi.h | 30 +- lib/Makefile | 2 +- lib/simulator/simulatormodule.c | 51 +--- lib/simulator/simulatormodule.h | 2 - lib/vpi_shim/Makefile | 2 +- lib/vpi_shim/gpi_vpi.c | 527 +++++++++++++------------------- 6 files changed, 232 insertions(+), 382 deletions(-) diff --git a/include/gpi.h b/include/gpi.h index 15735fa9..becddcad 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -34,7 +34,7 @@ Generic Language Interface This header file defines a Generic Language Interface into any simulator. -Implementations should include this header file and MUST +Implementations need to implement the underlying functions in gpi_priv.h The functions are essentially a limited subset of VPI/VHPI/FLI. @@ -54,8 +54,11 @@ we have to create a process with the signal on the sensitivity list to imitate a #include #include #include +#include #include +#include + #if defined(__MINGW32__) || defined (__CYGWIN32__) # define DLLEXPORT __declspec(dllexport) #else @@ -95,9 +98,16 @@ typedef struct gpi_sim_info_s // Define a type for our simulation handle. typedef struct gpi_sim_hdl_s { - void *sim_hdl; + void *sim_hdl; // Opaque handle for for a simulator object } gpi_sim_hdl_t, *gpi_sim_hdl; +// Define a type for a simulator callback handle +typedef struct gpi_cb_hdl_s { + gpi_sim_hdl_t hdl; + int (*gpi_function)(void *); // GPI function to callback + void *gpi_cb_data; // GPI data supplied to "gpi_functin" +} gpi_cb_hdl_t, *gpi_cb_hdl; + // Define a handle type for iterators typedef struct __gpi_iterator_hdl *gpi_iterator_hdl; @@ -156,9 +166,7 @@ char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl); void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value); void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str); // String of binary char(s) [1, 0, x, z] -// The callback registering functions all return a gpi_sim_hdl; -int gpi_register_sim_start_callback (gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); -int gpi_register_sim_end_callback (gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); +// The callback registering functions int gpi_register_timed_callback (gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps); int gpi_register_value_change_callback (gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl); int gpi_register_readonly_callback (gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); @@ -167,11 +175,15 @@ int gpi_register_readwrite_callback (gpi_sim_hdl, int (*gpi_functio // Calling convention is that 0 = success and negative numbers a failure // For implementers of GPI the provided macro GPI_RET(x) is provided +//gpi_sim_hdl gpi_create_cb_handle(void); +//void gpi_destroy_cb_handle(gpi_sim_hdl gpi_hdl); gpi_sim_hdl gpi_create_cb_handle(void); -void gpi_destroy_cb_handle(gpi_sim_hdl gpi_hdl); -int gpi_deregister_callback(gpi_sim_hdl gpi_hdl); -gpi_sim_hdl gpi_clock_register(gpi_sim_hdl sim_hdl, int period, unsigned int cycles); -void gpi_clock_unregister(gpi_sim_hdl clock); +void gpi_free_cb_handle(gpi_sim_hdl gpi_hdl); + +gpi_sim_hdl gpi_create_handle(void); +void gpi_free_handle(gpi_sim_hdl gpi_hdl); + +void gpi_deregister_callback(gpi_sim_hdl gpi_hdl); #define GPI_RET(_code) \ if (_code == 1) \ diff --git a/lib/Makefile b/lib/Makefile index d21c49b4..68615204 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -52,7 +52,7 @@ $(LIB_DIR)/libgpilog.so: $(SIM_ROOT)/lib/gpi/gpi_logging.c | $(LIB_DIR) $(LIB_DIR)/libcocotb.so: $(SIM_ROOT)/lib/embed/gpi_embed.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/embed -$(LIB_DIR)/libgpi.so: $(SIM_ROOT)/lib/$(GPI_IMPL)_shim/gpi_$(GPI_IMPL).c | $(LIB_DIR) +$(LIB_DIR)/libgpi.so: $(SIM_ROOT)/lib/vpi_shim/gpi_common.c $(SIM_ROOT)/lib/$(GPI_IMPL)_shim/gpi_$(GPI_IMPL).c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/$(GPI_IMPL)_shim EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) $(LIB_DIR)/libsim.so: $(SIM_ROOT)/lib/simulator/simulatormodule.c | $(LIB_DIR) diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 54a3f9e1..122b44fb 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -798,7 +798,7 @@ static PyObject *remove_callback(PyObject *self, PyObject *args) pSihHdl = PyTuple_GetItem(args, 0); hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(pSihHdl); - gpi_destroy_cb_handle(hdl); + gpi_free_cb_handle(hdl); value = Py_BuildValue("s", "OK!"); @@ -827,55 +827,6 @@ static PyObject *create_callback(PyObject *self, PyObject *args) return value; } -static PyObject *create_clock(PyObject *self, PyObject *args) -{ - gpi_sim_hdl hdl; - int period; - unsigned int mcycles; - - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - - Py_ssize_t numargs = PyTuple_Size(args); - - if (numargs < 3) { - fprintf(stderr, "Attempt to create a clock with without enough arguments!\n"); - DROP_GIL(gstate); - return NULL; - } - - PyObject *pSihHdl = PyTuple_GetItem(args, 0); - hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(pSihHdl); - - PyObject *pPeriod = PyTuple_GetItem(args, 1); - period = (int)PyInt_AsLong(pPeriod); - - PyObject *pCycles = PyTuple_GetItem(args, 2); - mcycles = (unsigned int)PyLong_AsUnsignedLong(pCycles); - - gpi_sim_hdl clk_hdl = gpi_clock_register(hdl, period, mcycles); - PyObject *rv = Py_BuildValue("l", clk_hdl); - - DROP_GIL(gstate); - return rv; -} - - -static PyObject *stop_clock(PyObject *self, PyObject *args) -{ - gpi_sim_hdl clk_hdl; - - if (!PyArg_ParseTuple(args, "l", &clk_hdl)) - return NULL; - - gpi_clock_unregister(clk_hdl); - PyObject *rv = Py_BuildValue("l", NULL); - return rv; -} - - - - PyMODINIT_FUNC initsimulator(void) { diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index db1a7a43..0e42427e 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -93,8 +93,6 @@ static PyMethodDef SimulatorMethods[] = { {"register_readonly_callback", register_readonly_callback, METH_VARARGS, "Register a callback for readonly section"}, {"register_nextstep_callback", register_nextstep_callback, METH_VARARGS, "Register a cllback for the nextsimtime callback"}, {"register_rwsynch_callback", register_rwsynch_callback, METH_VARARGS, "Register a callback for the readwrite section"}, - {"create_clock", create_clock, METH_VARARGS, "Register a clock object"}, - {"stop_clock", stop_clock, METH_VARARGS, "Terminate a clock"}, {"stop_simulator", stop_simulator, METH_VARARGS, "Instruct the attached simulator to stop"}, {"iterate", iterate, METH_VARARGS, "Get an iterator handle to loop over all members in an object"}, {"next", next, METH_VARARGS, "Get the next object from the iterator"}, diff --git a/lib/vpi_shim/Makefile b/lib/vpi_shim/Makefile index 6da808f8..2f3f06f0 100644 --- a/lib/vpi_shim/Makefile +++ b/lib/vpi_shim/Makefile @@ -35,7 +35,7 @@ LIBS := -lgpilog -lcocotb $(EXTRA_LIBS) LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) LIB_NAME := libgpi.so -SRCS := gpi_vpi.c +SRCS := gpi_common.c gpi_vpi.c CLIBS += $(LIB_DIR)/gpivpi.vpl diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 05be59a4..90f872be 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -27,30 +27,14 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ - -#include -#include -#include - -#include -#include -#include +#include "gpi_priv.h" #include -#define gpi_container_of(_address, _type, _member) \ - ((_type *)((uintptr_t)(_address) - \ - (uintptr_t)(&((_type *)0)->_member))) - #define VPI_CHECKING 1 static gpi_sim_hdl sim_init_cb; static gpi_sim_hdl sim_finish_cb; -static int alloc_count = 0; -static int dealloc_count = 0; -static int clear_count = 0; -static int total_count = 0; - typedef enum vpi_cb_state_e { VPI_FREE = 0, VPI_PRIMED = 1, @@ -60,30 +44,15 @@ typedef enum vpi_cb_state_e { } vpi_cb_state_t; // callback user data used for VPI callbacks -// (mostly just a thin wrapper around the gpi_callback) -typedef struct t_vpi_cb_user_data { - void *gpi_cb_data; - int (*gpi_function)(void *); - int (*gpi_cleanup)(struct t_vpi_cb_user_data *); + +typedef struct t_vpi_cb { vpiHandle cb_hdl; s_vpi_value cb_value; - gpi_sim_hdl_t gpi_hdl; vpi_cb_state_t state; -} s_vpi_cb_user_data, *p_vpi_cb_user_data; - -// Define a type of a clock object -typedef struct gpi_clock_s { - int period; - int value; - unsigned int max_cycles; - unsigned int curr_cycle; - bool exit; - gpi_sim_hdl_t gpi_hdl; /* Handle to pass back to called */ - gpi_sim_hdl clk_hdl; /* Handle for signal to operate on */ - gpi_sim_hdl cb_hdl; /* Handle for the current pending callback */ -} gpi_clock_t; - -typedef gpi_clock_t *gpi_clock_hdl; + gpi_cb_hdl_t gpi_cb_data; + int (*vpi_cleanup)(struct t_vpi_cb *cb_data); +} s_vpi_cb, *p_vpi_cb; + // Add to this over time static const char * vpi_reason_to_string(int reason) @@ -110,6 +79,7 @@ static const char * vpi_reason_to_string(int reason) } } + // Should be run after every VPI call to check error status static int __check_vpi_error(const char *func, long line) { @@ -148,7 +118,7 @@ static int __check_vpi_error(const char *func, long line) #define check_vpi_error() \ __check_vpi_error(__func__, __LINE__) -static inline int __gpi_register_cb(p_vpi_cb_user_data user, p_cb_data cb_data) +static inline int __vpi_register_cb(p_vpi_cb user, p_cb_data cb_data) { /* If the user data already has a callback handle then deregister * before getting the new one @@ -169,51 +139,17 @@ static inline int __gpi_register_cb(p_vpi_cb_user_data user, p_cb_data cb_data) } if (user->cb_hdl != NULL) { - fprintf(stderr, "user->cb_hdl is not null, deregistering %s!\n", + fprintf(stderr, "user->gpi_cb_hdl is not null, deregistering %s!\n", vpi_reason_to_string(cb_data->reason)); - gpi_deregister_callback(&user->gpi_hdl); + vpi_deregister_callback(user); } user->cb_hdl = new_hdl; + user->state = VPI_PRIMED; return ret; } -static inline p_vpi_cb_user_data __gpi_alloc_user(void) -{ - p_vpi_cb_user_data new_data = calloc(1, sizeof(*new_data)); - if (new_data == NULL) { - LOG_CRITICAL("VPI: Attempting allocate user_data failed!"); - } - - return new_data; -} - -static inline void __gpi_free_callback(gpi_sim_hdl gpi_hdl) -{ - FENTER - p_vpi_cb_user_data user_data; - user_data = gpi_container_of(gpi_hdl, s_vpi_cb_user_data, gpi_hdl); - - free(user_data); - FEXIT -} - -void gpi_free_handle(gpi_sim_hdl gpi_hdl) -{ - free(gpi_hdl); -} - -static gpi_sim_hdl gpi_alloc_handle(void) -{ - gpi_sim_hdl new_hdl = calloc(1, sizeof(*new_hdl)); - if (!new_hdl) { - LOG_CRITICAL("VPI: Could not allocate handle"); - exit(1); - } - - return new_hdl; -} // Handle related functions /** @@ -227,7 +163,7 @@ static gpi_sim_hdl gpi_alloc_handle(void) * If name is provided, we check the name against the available objects until * we find a match. If no match is found we return NULL */ -gpi_sim_hdl gpi_get_root_handle(const char* name) +static gpi_sim_hdl vpi_get_root_handle(const char* name) { FENTER vpiHandle root; @@ -257,7 +193,7 @@ gpi_sim_hdl gpi_get_root_handle(const char* name) check_vpi_error(); } - rv = gpi_alloc_handle(); + rv = gpi_create_handle(); rv->sim_hdl = root; FEXIT @@ -281,7 +217,8 @@ gpi_sim_hdl gpi_get_root_handle(const char* name) return NULL; } -gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) + +static gpi_sim_hdl vpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) { FENTER gpi_sim_hdl rv; @@ -301,13 +238,13 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) obj = vpi_handle_by_name(buff, (vpiHandle)(parent->sim_hdl)); if (!obj) { LOG_DEBUG("VPI: Handle '%s' not found!", name); -// check_vpi_error(); + check_vpi_error(); return NULL; } free(buff); - rv = gpi_alloc_handle(); + rv = gpi_create_handle(); rv->sim_hdl = obj; FEXIT @@ -323,7 +260,7 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) * Can be used on bit-vectors to access a specific bit or * memories to access an address */ -gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) +static gpi_sim_hdl vpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) { FENTER gpi_sim_hdl rv; @@ -335,7 +272,7 @@ gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) return NULL; } - rv = gpi_alloc_handle(); + rv = gpi_create_handle(); rv->sim_hdl = obj; FEXIT @@ -346,7 +283,7 @@ gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) // Functions for iterating over entries of a handle // Returns an iterator handle which can then be used in gpi_next calls // NB May return NULL if no objects of the request type exist -gpi_iterator_hdl gpi_iterate(uint32_t type, gpi_sim_hdl base) { +static gpi_iterator_hdl vpi_iterate_hdl(uint32_t type, gpi_sim_hdl base) { FENTER vpiHandle iterator; @@ -359,11 +296,11 @@ gpi_iterator_hdl gpi_iterate(uint32_t type, gpi_sim_hdl base) { } // Returns NULL when there are no more objects -gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) +static gpi_sim_hdl vpi_next_hdl(gpi_iterator_hdl iterator) { FENTER vpiHandle result; - gpi_sim_hdl rv = gpi_alloc_handle(); + gpi_sim_hdl rv = gpi_create_handle(); rv->sim_hdl = vpi_scan((vpiHandle) iterator); check_vpi_error(); @@ -382,7 +319,7 @@ gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) } // double gpi_get_sim_time() -void gpi_get_sim_time(uint32_t *high, uint32_t *low) +static void vpi_get_sim_time(uint32_t *high, uint32_t *low) { s_vpi_time vpi_time_s; vpi_time_s.type = vpiSimTime;//vpiScaledRealTime; //vpiSimTime; @@ -393,7 +330,7 @@ void gpi_get_sim_time(uint32_t *high, uint32_t *low) } // Value related functions -void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) +static void vpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) { FENTER s_vpi_value value_s; @@ -416,7 +353,7 @@ void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) FEXIT } -void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) +static void vpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) { FENTER s_vpi_value value_s; @@ -448,35 +385,7 @@ void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) FEXIT } - -static char *gpi_copy_name(const char *name) -{ - int len; - char *result; - const char null[] = "NULL"; - - if (name) - len = strlen(name) + 1; - else { - LOG_CRITICAL("VPI: NULL came back from VPI"); - len = strlen(null); - name = null; - } - - result = (char *)malloc(len); - if (result == NULL) { - LOG_CRITICAL("VPI: Attempting allocate string buffer failed!"); - len = strlen(null); - name = null; - } - - snprintf(result, len, "%s", name); - - return result; -} - - -char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) +static char *vpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) { FENTER s_vpi_value value_s = {vpiBinStrVal}; @@ -490,7 +399,7 @@ char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) return result; } -char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl) +static char *vpi_get_signal_name_str(gpi_sim_hdl gpi_hdl) { FENTER const char *name = vpi_get_str(vpiFullName, (vpiHandle)(gpi_hdl->sim_hdl)); @@ -500,7 +409,7 @@ char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl) return result; } -char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl) +static char *vpi_get_signal_type_str(gpi_sim_hdl gpi_hdl) { FENTER const char *name = vpi_get_str(vpiType, (vpiHandle)(gpi_hdl->sim_hdl)); @@ -512,23 +421,22 @@ char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl) // Callback related functions - static int32_t handle_vpi_callback(p_cb_data cb_data) { FENTER int rv = 0; vpiHandle old_cb; - p_vpi_cb_user_data user_data; - user_data = (p_vpi_cb_user_data)cb_data->user_data; + p_vpi_cb user_data; + user_data = (p_vpi_cb)cb_data->user_data; if (!user_data) LOG_CRITICAL("VPI: Callback data corrupted"); user_data->state = VPI_PRE_CALL; old_cb = user_data->cb_hdl; - rv = user_data->gpi_function(user_data->gpi_cb_data); - + gpi_handle_callback(&user_data->gpi_cb_data.hdl); + // HACK: Investigate further - this breaks modelsim #if 0 if (old_cb == user_data->cb_hdl) @@ -539,17 +447,19 @@ static int32_t handle_vpi_callback(p_cb_data cb_data) * inside gpi_function */ if (user_data->state == VPI_DELETE) - gpi_destroy_cb_handle(&user_data->gpi_hdl); + gpi_destroy_cb_handle(&user_data->gpi_cb_data); else user_data->state = VPI_POST_CALL; FEXIT return rv; }; +#if 0 /* Allocates memory that will persist for the lifetime of the - * handle, this may be short or long. A call to create - * must have a matching call to destroy at some point + * callback handle. This is not the same as the handle to the object + * that the callback is registered for, this may be short or long. + * A call to create must have a matching call to destroy at some point */ gpi_sim_hdl gpi_create_cb_handle(void) { @@ -588,36 +498,40 @@ void gpi_destroy_cb_handle(gpi_sim_hdl gpi_hdl) FEXIT } +#endif /* Deregister a prior set up callback with the simulator * The handle must have been allocated with gpi_create_cb_handle * This can be called at any point between * gpi_create_cb_handle and gpi_destroy_cb_handle */ -int gpi_deregister_callback(gpi_sim_hdl gpi_hdl) -{ - p_vpi_cb_user_data user_data; - int rc = 1; +int vpi_deregister_callback(gpi_sim_hdl gpi_hdl) +{ FENTER + p_vpi_cb vpi_user_data; + gpi_cb_hdl gpi_user_data; + int rc = 1; // We should be able to user vpi_get_cb_info // but this is not implemented in ICARUS // and gets upset on VCS. So instead we // do some pointer magic. - user_data = gpi_container_of(gpi_hdl, s_vpi_cb_user_data, gpi_hdl); + gpi_user_data = gpi_container_of(gpi_hdl, gpi_cb_hdl_t, hdl); + vpi_user_data = gpi_container_of(gpi_user_data, s_vpi_cb, gpi_cb_data); - if (user_data->cb_hdl) { - rc = user_data->gpi_cleanup(user_data); - user_data->cb_hdl = NULL; + if (vpi_user_data->cb_hdl) { + rc = vpi_user_data->vpi_cleanup(vpi_user_data); + vpi_user_data->cb_hdl = NULL; } FEXIT GPI_RET(rc); } + // Call when the handle relates to a one time callback // No need to call vpi_deregister_cb as the sim will // do this but do need to destroy the handle -static int gpi_free_one_time(p_vpi_cb_user_data user_data) +static int vpi_free_one_time(p_vpi_cb user_data) { FENTER int rc; @@ -636,6 +550,7 @@ static int gpi_free_one_time(p_vpi_cb_user_data user_data) check_vpi_error(); return rc; } + user_data->cb_hdl = NULL; // HACK: Calling vpi_free_object after vpi_remove_cb causes Modelsim to VPIEndOfSimulationCallback #if 0 @@ -651,10 +566,11 @@ static int gpi_free_one_time(p_vpi_cb_user_data user_data) return rc; } + // Call when the handle relates to recurring callback // Unregister must be called when not needed and this // will clean all memory allocated by the sim -static int gpi_free_recurring(p_vpi_cb_user_data user_data) +static int vpi_free_recurring(p_vpi_cb user_data) { FENTER int rc; @@ -675,24 +591,25 @@ static int gpi_free_recurring(p_vpi_cb_user_data user_data) * allocated with gpi_create_cb_handle first */ -int gpi_register_value_change_callback(gpi_sim_hdl cb, + +int vpi_register_value_change_callback(gpi_sim_hdl cb, int (*gpi_function)(void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl) { FENTER + + int ret; s_cb_data cb_data_s; s_vpi_time vpi_time_s; - s_vpi_value vpi_value_s; - p_vpi_cb_user_data user_data; - int ret; + p_vpi_cb vpi_user_data; + gpi_cb_hdl gpi_user_data; - user_data = gpi_container_of(cb, s_vpi_cb_user_data, gpi_hdl); + gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); + vpi_user_data = gpi_container_of(gpi_user_data, s_vpi_cb, gpi_cb_data); - user_data->gpi_cb_data = gpi_cb_data; - user_data->gpi_function = gpi_function; - user_data->gpi_cleanup = gpi_free_recurring; - user_data->cb_value.format = vpiIntVal; + vpi_user_data->vpi_cleanup = vpi_free_recurring; + vpi_user_data->cb_value.format = vpiIntVal; vpi_time_s.type = vpiSuppressTime; @@ -700,11 +617,10 @@ int gpi_register_value_change_callback(gpi_sim_hdl cb, cb_data_s.cb_rtn = handle_vpi_callback; cb_data_s.obj = (vpiHandle)(gpi_hdl->sim_hdl); cb_data_s.time = &vpi_time_s; - cb_data_s.value = &user_data->cb_value; - cb_data_s.user_data = (char *)user_data; + cb_data_s.value = &vpi_user_data->cb_value; + cb_data_s.user_data = (char *)vpi_user_data; - ret = __gpi_register_cb(user_data, &cb_data_s); - user_data->state = VPI_PRIMED; + ret = __vpi_register_cb(vpi_user_data, &cb_data_s); FEXIT @@ -712,21 +628,22 @@ int gpi_register_value_change_callback(gpi_sim_hdl cb, } -int gpi_register_readonly_callback(gpi_sim_hdl cb, +int vpi_register_readonly_callback(gpi_sim_hdl cb, int (*gpi_function)(void *), void *gpi_cb_data) { FENTER + + int ret; s_cb_data cb_data_s; s_vpi_time vpi_time_s; - p_vpi_cb_user_data user_data; - int ret; + p_vpi_cb vpi_user_data; + gpi_cb_hdl gpi_user_data; - user_data = gpi_container_of(cb, s_vpi_cb_user_data, gpi_hdl); + gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); + vpi_user_data = gpi_container_of(gpi_user_data, s_vpi_cb, gpi_cb_data); - user_data->gpi_cb_data = gpi_cb_data; - user_data->gpi_function = gpi_function; - user_data->gpi_cleanup = gpi_free_one_time; + vpi_user_data->vpi_cleanup = vpi_free_one_time; vpi_time_s.type = vpiSimTime; vpi_time_s.high = 0; @@ -737,30 +654,30 @@ int gpi_register_readonly_callback(gpi_sim_hdl cb, cb_data_s.obj = NULL; cb_data_s.time = &vpi_time_s; cb_data_s.value = NULL; - cb_data_s.user_data = (char *)user_data; + cb_data_s.user_data = (char *)vpi_user_data; - ret = __gpi_register_cb(user_data, &cb_data_s); - user_data->state = VPI_PRIMED; + ret = __vpi_register_cb(vpi_user_data, &cb_data_s); FEXIT return ret; } -int gpi_register_readwrite_callback(gpi_sim_hdl cb, +int vpi_register_readwrite_callback(gpi_sim_hdl cb, int (*gpi_function)(void *), void *gpi_cb_data) { FENTER + + int ret; s_cb_data cb_data_s; s_vpi_time vpi_time_s; - p_vpi_cb_user_data user_data; - int ret; + p_vpi_cb vpi_user_data; + gpi_cb_hdl gpi_user_data; - user_data = gpi_container_of(cb, s_vpi_cb_user_data, gpi_hdl); + gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); + vpi_user_data = gpi_container_of(gpi_user_data, s_vpi_cb, gpi_cb_data); - user_data->gpi_cb_data = gpi_cb_data; - user_data->gpi_function = gpi_function; - user_data->gpi_cleanup = gpi_free_one_time; + vpi_user_data->vpi_cleanup = vpi_free_one_time; vpi_time_s.type = vpiSimTime; vpi_time_s.high = 0; @@ -771,30 +688,31 @@ int gpi_register_readwrite_callback(gpi_sim_hdl cb, cb_data_s.obj = NULL; cb_data_s.time = &vpi_time_s; cb_data_s.value = NULL; - cb_data_s.user_data = (char *)user_data; + cb_data_s.user_data = (char *)vpi_user_data; - ret = __gpi_register_cb(user_data, &cb_data_s); - user_data->state = VPI_PRIMED; + ret = __vpi_register_cb(vpi_user_data, &cb_data_s); FEXIT return ret; } -int gpi_register_nexttime_callback(gpi_sim_hdl cb, + +int vpi_register_nexttime_callback(gpi_sim_hdl cb, int (*gpi_function)(void *), void *gpi_cb_data) { FENTER + + int ret; s_cb_data cb_data_s; s_vpi_time vpi_time_s; - p_vpi_cb_user_data user_data; - int ret; + p_vpi_cb vpi_user_data; + gpi_cb_hdl gpi_user_data; - user_data = gpi_container_of(cb, s_vpi_cb_user_data, gpi_hdl); + gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); + vpi_user_data = gpi_container_of(gpi_user_data, s_vpi_cb, gpi_cb_data); - user_data->gpi_cb_data = gpi_cb_data; - user_data->gpi_function = gpi_function; - user_data->gpi_cleanup = gpi_free_one_time; + vpi_user_data->vpi_cleanup = vpi_free_one_time; vpi_time_s.type = vpiSimTime; vpi_time_s.high = 0; @@ -805,31 +723,31 @@ int gpi_register_nexttime_callback(gpi_sim_hdl cb, cb_data_s.obj = NULL; cb_data_s.time = &vpi_time_s; cb_data_s.value = NULL; - cb_data_s.user_data = (char *)user_data; + cb_data_s.user_data = (char *)vpi_user_data; - ret = __gpi_register_cb(user_data, &cb_data_s); - user_data->state = VPI_PRIMED; + ret = __vpi_register_cb(vpi_user_data, &cb_data_s); FEXIT return ret; } - -int gpi_register_timed_callback(gpi_sim_hdl cb, + +int vpi_register_timed_callback(gpi_sim_hdl cb, int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps) { FENTER + + int ret; s_cb_data cb_data_s; s_vpi_time vpi_time_s; - p_vpi_cb_user_data user_data; - int ret; + p_vpi_cb vpi_user_data; + gpi_cb_hdl gpi_user_data; - user_data = gpi_container_of(cb, s_vpi_cb_user_data, gpi_hdl); + gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); + vpi_user_data = gpi_container_of(gpi_user_data, s_vpi_cb, gpi_cb_data); - user_data->gpi_cb_data = gpi_cb_data; - user_data->gpi_function = gpi_function; - user_data->gpi_cleanup = gpi_free_one_time; + vpi_user_data->vpi_cleanup = vpi_free_one_time; vpi_time_s.type = vpiSimTime; vpi_time_s.high = (uint32_t)(time_ps>>32); @@ -840,136 +758,68 @@ int gpi_register_timed_callback(gpi_sim_hdl cb, cb_data_s.obj = NULL; cb_data_s.time = &vpi_time_s; cb_data_s.value = NULL; - cb_data_s.user_data = (char *)user_data; + cb_data_s.user_data = (char *)vpi_user_data; - ret = __gpi_register_cb(user_data, &cb_data_s); - user_data->state = VPI_PRIMED; + ret = __vpi_register_cb(vpi_user_data, &cb_data_s); FEXIT return ret; } -int gpi_register_sim_start_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) -{ - FENTER - - p_vpi_cb_user_data user_data; - s_cb_data cb_data_s; - - user_data = gpi_container_of(cb, s_vpi_cb_user_data, gpi_hdl); - - user_data->gpi_cb_data = gpi_cb_data; - user_data->gpi_function = gpi_function; - user_data->gpi_cleanup = gpi_free_one_time; - - cb_data_s.reason = cbStartOfSimulation; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = NULL; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)user_data; - - /* We ignore the return value here as VCS does some silly - * things on comilation that means it tries to run through - * the vlog_startup_routines and so call this routine - */ - __gpi_register_cb(user_data, &cb_data_s); - user_data->state = VPI_PRIMED; - - FEXIT - return 0; -} - -int gpi_register_sim_end_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) +/* Checking of validity is done in the common code */ +gpi_cb_hdl vpi_create_cb_handle(void) { - FENTER - - p_vpi_cb_user_data user_data; - s_cb_data cb_data_s; - - user_data = gpi_container_of(cb, s_vpi_cb_user_data, gpi_hdl); - - user_data->gpi_cb_data = gpi_cb_data; - user_data->gpi_function = gpi_function; - user_data->gpi_cleanup = gpi_free_one_time; - - cb_data_s.reason = cbEndOfSimulation; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = NULL; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)user_data; - - /* We ignore the return value here as VCS does some silly - * things on comilation that means it tries to run through - * the vlog_startup_routines and so call this routine - */ - __gpi_register_cb(user_data, &cb_data_s); - user_data->state = VPI_PRIMED; - - FEXIT - return 0; + p_vpi_cb new_cb_hdl = calloc(1, sizeof(*new_cb_hdl)); + if (new_cb_hdl) + return &new_cb_hdl->gpi_cb_data; + return NULL; } -int gpi_clock_handler(void *clock) +void vpi_destroy_cb_handle(gpi_cb_hdl hdl) { - gpi_clock_hdl hdl = (gpi_clock_hdl)clock; - gpi_sim_hdl cb_hdl; - - if (hdl->exit || ((hdl->max_cycles != 0) && (hdl->max_cycles == hdl->curr_cycle))) - return; - - /* Unregister/free the last callback that just fired */ - cb_hdl = hdl->cb_hdl; - - hdl->value = !hdl->value; - gpi_set_signal_value_int(hdl->clk_hdl, hdl->value); - gpi_register_timed_callback(cb_hdl, gpi_clock_handler, hdl, hdl->period); - hdl->curr_cycle++; + p_vpi_cb vpi_hdl = gpi_container_of(hdl, s_vpi_cb, gpi_cb_data); + free(vpi_hdl); } -gpi_sim_hdl gpi_clock_register(gpi_sim_hdl sim_hdl, int period, unsigned int cycles) +// If the Pything world wants things to shut down then unregister +// the callback for end of sim +void vpi_sim_end(void) { - FENTER - - gpi_clock_hdl hdl = malloc(sizeof(gpi_clock_t)); - if (!hdl) - LOG_CRITICAL("VPI: Unable to allocate memory"); - - hdl->period = period; - hdl->value = 0; - hdl->clk_hdl = sim_hdl; - hdl->exit = false; - hdl->max_cycles = cycles; - hdl->curr_cycle = 0; - - gpi_set_signal_value_int(hdl->clk_hdl, hdl->value); - hdl->cb_hdl = gpi_create_cb_handle(); - - gpi_register_timed_callback(hdl->cb_hdl, gpi_clock_handler, hdl, hdl->period); - - FEXIT - return &hdl->gpi_hdl; + sim_finish_cb = NULL; + vpi_control(vpiFinish); + check_vpi_error(); } -void gpi_clock_unregister(gpi_sim_hdl clock) -{ - gpi_clock_hdl hdl = gpi_container_of(clock, gpi_clock_t, gpi_hdl); - hdl->exit = true; -} +static s_gpi_impl_tbl vpi_table = { + .sim_end = vpi_sim_end, + .iterate_handle = vpi_iterate_hdl, + .next_handle = vpi_next_hdl, + .create_cb_handle = vpi_create_cb_handle, + .destroy_cb_handle = vpi_destroy_cb_handle, + .deregister_callback = vpi_deregister_callback, + .get_root_handle = vpi_get_root_handle, + .get_sim_time = vpi_get_sim_time, + .get_handle_by_name = vpi_get_handle_by_name, + .get_handle_by_index = vpi_get_handle_by_index, + .get_signal_name_str = vpi_get_signal_name_str, + .get_signal_type_str = vpi_get_signal_type_str, + .get_signal_value_binstr = vpi_get_signal_value_binstr, + .set_signal_value_int = vpi_set_signal_value_int, + .set_signal_value_str = vpi_set_signal_value_str, + .register_timed_callback = vpi_register_timed_callback, + .register_readwrite_callback = vpi_register_readwrite_callback, + .register_nexttime_callback = vpi_register_nexttime_callback, + .register_value_change_callback = vpi_register_value_change_callback, + .register_readonly_callback = vpi_register_readonly_callback, +}; void register_embed(void) { - FENTER - embed_init_python(); - FEXIT + gpi_register_impl(&vpi_table, 0xfeed); + gpi_embed_init_python(); } @@ -986,15 +836,41 @@ int handle_sim_init(void *gpi_cb_data) sim_info.product = info.product; sim_info.version = info.version; - embed_sim_init(&sim_info); + gpi_embed_init(&sim_info); FEXIT } void register_initial_callback(void) { FENTER + + s_cb_data cb_data_s; + p_vpi_cb vpi_user_data; + gpi_cb_hdl gpi_user_data; + sim_init_cb = gpi_create_cb_handle(); - gpi_register_sim_start_callback(sim_init_cb, handle_sim_init, (void *)NULL); + + gpi_user_data = gpi_container_of(sim_init_cb, gpi_cb_hdl_t, hdl); + vpi_user_data = gpi_container_of(gpi_user_data, s_vpi_cb, gpi_cb_data); + + gpi_user_data->gpi_cb_data = NULL; + gpi_user_data->gpi_function = handle_sim_init; + + vpi_user_data->vpi_cleanup = vpi_free_one_time; + + cb_data_s.reason = cbStartOfSimulation; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = NULL; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)vpi_user_data; + + /* We ignore the return value here as VCS does some silly + * things on comilation that means it tries to run through + * the vlog_startup_routines and so call this routine + */ + __vpi_register_cb(vpi_user_data, &cb_data_s); + FEXIT } @@ -1004,21 +880,48 @@ int handle_sim_end(void *gpi_cb_data) if (sim_finish_cb) { sim_finish_cb = NULL; /* This means that we have been asked to close */ - embed_sim_event(SIM_FAIL, "Simulator shutdown prematurely"); + gpi_embed_end(); } /* Other sise we have already been here from the top down so do not need to inform the upper layers that anything has occoured */ - __gpi_free_callback(sim_init_cb); + gpi_free_cb_handle(sim_init_cb); FEXIT } void register_final_callback(void) { FENTER - sim_finish_cb = gpi_create_cb_handle(); - gpi_register_sim_end_callback(sim_finish_cb, handle_sim_end, (void *)NULL); + + s_cb_data cb_data_s; + p_vpi_cb vpi_user_data; + gpi_cb_hdl gpi_user_data; + + sim_init_cb = gpi_create_cb_handle(); + + gpi_user_data = gpi_container_of(sim_init_cb, gpi_cb_hdl_t, hdl); + vpi_user_data = gpi_container_of(gpi_user_data, s_vpi_cb, gpi_cb_data); + + gpi_user_data->gpi_cb_data = NULL; + gpi_user_data->gpi_function = handle_sim_end; + + vpi_user_data->vpi_cleanup = vpi_free_one_time; + + cb_data_s.reason = cbEndOfSimulation; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = NULL; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)vpi_user_data; + + /* We ignore the return value here as VCS does some silly + * things on comilation that means it tries to run through + * the vlog_startup_routines and so call this routine + */ + __vpi_register_cb(vpi_user_data, &cb_data_s); + FEXIT } + // Called at compile time to validate the arguments to the system functions // we redefine (info, warning, error, fatal). // @@ -1086,8 +989,6 @@ static int system_function_overload(char *userdata) return 0; } - - void register_system_functions(void) { FENTER @@ -1116,18 +1017,6 @@ void register_system_functions(void) FEXIT } -// If the Pything world wants things to shut down then unregister -// the callback for end of sim -void gpi_sim_end(void) -{ - FENTER - - sim_finish_cb = NULL; - vpi_control(vpiFinish); - check_vpi_error(); - FEXIT -} - void (*vlog_startup_routines[])(void) = { register_embed, register_system_functions, @@ -1147,4 +1036,4 @@ void vlog_startup_routines_bootstrap(void) { routine = vlog_startup_routines[++i]) { routine(); } -} +} \ No newline at end of file From ae6c288a191990bdfdddf0c3ca9dbb99a09cb5ed Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 30 Apr 2014 17:15:26 +0100 Subject: [PATCH 0507/2656] Issue #127: Removed unused code --- lib/vpi_shim/gpi_vpi.c | 43 ------------------------------------------ 1 file changed, 43 deletions(-) diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 90f872be..13fd80db 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -454,51 +454,8 @@ static int32_t handle_vpi_callback(p_cb_data cb_data) FEXIT return rv; }; -#if 0 - -/* Allocates memory that will persist for the lifetime of the - * callback handle. This is not the same as the handle to the object - * that the callback is registered for, this may be short or long. - * A call to create must have a matching call to destroy at some point - */ -gpi_sim_hdl gpi_create_cb_handle(void) -{ - gpi_sim_hdl ret = NULL; - FENTER - p_vpi_cb_user_data user_data = __gpi_alloc_user(); - if (user_data) { - user_data->state = VPI_FREE; - ret = &user_data->gpi_hdl; - } - - FEXIT - return ret; -} -/* Destroys the memory associated with the sim handle - * this can only be called on a handle that has been - * returned by a call to gpi_create_cb_handle - */ -void gpi_destroy_cb_handle(gpi_sim_hdl gpi_hdl) -{ - /* Check that is has been called, if this has not - * happend then also close down the sim data as well - */ - FENTER - p_vpi_cb_user_data user_data; - user_data = gpi_container_of(gpi_hdl, s_vpi_cb_user_data, gpi_hdl); - - if (user_data->state == VPI_PRE_CALL) { - user_data->state = VPI_DELETE; - } else { - gpi_deregister_callback(gpi_hdl); - __gpi_free_callback(gpi_hdl); - } - FEXIT -} - -#endif /* Deregister a prior set up callback with the simulator * The handle must have been allocated with gpi_create_cb_handle * This can be called at any point between From cf4eb16577b643565590ad6b80ffe6260257f67b Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 11 May 2014 16:45:12 +0100 Subject: [PATCH 0508/2656] Issue #127: Add back some tracing for memory allocation --- lib/vpi_shim/gpi_vpi.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index 13fd80db..e554b672 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -728,17 +728,23 @@ int vpi_register_timed_callback(gpi_sim_hdl cb, /* Checking of validity is done in the common code */ gpi_cb_hdl vpi_create_cb_handle(void) { + gpi_sim_hdl ret = NULL; + FENTER + p_vpi_cb new_cb_hdl = calloc(1, sizeof(*new_cb_hdl)); if (new_cb_hdl) - return &new_cb_hdl->gpi_cb_data; + ret = &new_cb_hdl->gpi_cb_data; - return NULL; + FEXIT + return ret; } void vpi_destroy_cb_handle(gpi_cb_hdl hdl) { + FENTER p_vpi_cb vpi_hdl = gpi_container_of(hdl, s_vpi_cb, gpi_cb_data); free(vpi_hdl); + FEXIT } // If the Pything world wants things to shut down then unregister From 24e42cf2a5de0eb347c2c6687e0a5272bc1e6357 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 11 May 2014 16:51:43 +0100 Subject: [PATCH 0509/2656] Issue #127: Add new common base file and header --- lib/vpi_shim/gpi_common.c | 262 ++++++++++++++++++++++++++++++++++++++ lib/vpi_shim/gpi_priv.h | 68 ++++++++++ 2 files changed, 330 insertions(+) create mode 100644 lib/vpi_shim/gpi_common.c create mode 100644 lib/vpi_shim/gpi_priv.h diff --git a/lib/vpi_shim/gpi_common.c b/lib/vpi_shim/gpi_common.c new file mode 100644 index 00000000..2a7b0d48 --- /dev/null +++ b/lib/vpi_shim/gpi_common.c @@ -0,0 +1,262 @@ +/****************************************************************************** +* Copyright (c) 2013 Potential Ventures Ltd +* Copyright (c) 2013 SolarFlare Communications Inc +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd, +* SolarFlare Communications Inc nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +#include "gpi_priv.h" + +#define MAX_IMPLS 5 + +typedef struct impls { + gpi_impl_tbl tbl; + int type; +} r_impl; + +static r_impl registed_impls[MAX_IMPLS] = {{NULL,0},}; + +#define IMPL_ROOT registed_impls[0].tbl + +static inline void set_user_data(gpi_sim_hdl hdl, int (*gpi_function)(void*), void *data) +{ + gpi_cb_hdl gpi_user_data = gpi_container_of(hdl, gpi_cb_hdl_t, hdl); + + gpi_user_data->gpi_cb_data = data; + gpi_user_data->gpi_function = gpi_function; +} + +void gpi_embed_init(gpi_sim_info_t *info) +{ + embed_sim_init(info); +} + +void gpi_embed_end(void) +{ + embed_sim_event(SIM_FAIL, "Simulator shutdown prematurely"); +} + +void gpi_sim_end(void) +{ + IMPL_ROOT->sim_end(); +} + +void gpi_embed_init_python(void) +{ + embed_init_python(); +} + +void gpi_get_sim_time(uint32_t *high, uint32_t *low) +{ + IMPL_ROOT->get_sim_time(high, low); +} + +gpi_sim_hdl gpi_get_root_handle(const char *name) +{ + return IMPL_ROOT->get_root_handle(name); +} + +gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) +{ + return IMPL_ROOT->get_handle_by_name(name, parent); +} + +gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) +{ + return IMPL_ROOT->get_handle_by_index(parent, index); +} + +gpi_iterator_hdl gpi_iterate(uint32_t type, gpi_sim_hdl base) +{ + return IMPL_ROOT->iterate_handle(type, base);; +} + +gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) +{ + return IMPL_ROOT->next_handle(iterator); +} + +char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) +{ + return IMPL_ROOT->get_signal_value_binstr(gpi_hdl); +} + +char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl) +{ + return IMPL_ROOT->get_signal_name_str(gpi_hdl); +} + +char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl) +{ + return IMPL_ROOT->get_signal_type_str(gpi_hdl); +} + +void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) +{ + IMPL_ROOT->set_signal_value_int(gpi_hdl, value); +} + +void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) +{ + IMPL_ROOT->set_signal_value_str(gpi_hdl, str); +} + + +int gpi_register_timed_callback(gpi_sim_hdl hdl, + int (*gpi_function)(void *), + void *gpi_cb_data, uint64_t time_ps) +{ + set_user_data(hdl, gpi_function, gpi_cb_data); + IMPL_ROOT->register_timed_callback(hdl, gpi_function, gpi_cb_data, time_ps); +} + +int gpi_register_value_change_callback(gpi_sim_hdl hdl, + int (*gpi_function)(void *), + void *gpi_cb_data, gpi_sim_hdl gpi_hdl) +{ + set_user_data(hdl, gpi_function, gpi_cb_data); + IMPL_ROOT->register_value_change_callback(hdl, gpi_function, gpi_cb_data, gpi_hdl); +} + +int gpi_register_readonly_callback(gpi_sim_hdl hdl, + int (*gpi_function)(void *), + void *gpi_cb_data) +{ + set_user_data(hdl, gpi_function, gpi_cb_data); + IMPL_ROOT->register_readonly_callback(hdl, gpi_function, gpi_cb_data); +} + +int gpi_register_nexttime_callback(gpi_sim_hdl hdl, + int (*gpi_function)(void *), + void *gpi_cb_data) +{ + set_user_data(hdl, gpi_function, gpi_cb_data); + IMPL_ROOT->register_nexttime_callback(hdl, gpi_function, gpi_cb_data); +} + +int gpi_register_readwrite_callback(gpi_sim_hdl hdl, + int (*gpi_function)(void *), + void *gpi_cb_data) +{ + set_user_data(hdl, gpi_function, gpi_cb_data); + IMPL_ROOT->register_readwrite_callback(hdl, gpi_function, gpi_cb_data); +} + +void gpi_deregister_callback(gpi_sim_hdl hdl) +{ + IMPL_ROOT->deregister_callback(hdl); +} + +void gpi_handle_callback(gpi_sim_hdl hdl) +{ + gpi_cb_hdl cb_hdl = gpi_container_of(hdl, gpi_cb_hdl_t, hdl); + cb_hdl->gpi_function(cb_hdl->gpi_cb_data); +} + +/* Callback handles are abstracted to the implementation layer since + they may need to have some state stored on a per handle basis. +*/ +gpi_sim_hdl gpi_create_cb_handle(void) +{ + gpi_cb_hdl ret = NULL; + + ret = IMPL_ROOT->create_cb_handle(); + if (!ret) { + LOG_CRITICAL("GPI: Attempting allocate user_data failed!"); + } + + return &ret->hdl; +} + +void gpi_free_cb_handle(gpi_sim_hdl hdl) +{ + gpi_cb_hdl cb_hdl = gpi_container_of(hdl, gpi_cb_hdl_t, hdl); + IMPL_ROOT->destroy_cb_handle(cb_hdl); +} + + +/* This needs to be filled with the pointer to the table */ +gpi_sim_hdl gpi_create_handle(void) +{ + gpi_sim_hdl new_hdl = calloc(1, sizeof(*new_hdl)); + if (!new_hdl) { + LOG_CRITICAL("GPI: Could not allocate handle"); + exit(1); + } + + return new_hdl; +} + +void gpi_free_handle(gpi_sim_hdl hdl) +{ + free(hdl); +} + +int gpi_register_impl(gpi_impl_tbl func_tbl, int type) +{ + int idx; + for (idx = 0; idx < MAX_IMPLS; idx++) { + if (!registed_impls[idx].tbl) { + /* TODO + * check that the pointers are set and error if not + */ + registed_impls[idx].tbl = func_tbl; + registed_impls[idx].type = type; + } + } + return 0; +} + +char *gpi_copy_name(const char *name) +{ + int len; + char *result; + const char null[] = "NULL"; + + if (name) + len = strlen(name) + 1; + else { + LOG_CRITICAL("GPI: attempt to use NULL from impl"); + len = strlen(null); + name = null; + } + + result = (char *)malloc(len); + if (result == NULL) { + LOG_CRITICAL("GPI: Attempting allocate string buffer failed!"); + len = strlen(null); + name = null; + } + + snprintf(result, len, "%s", name); + + return result; +} + +/* TODO + Each of the top level calls then needs to call into a function + table pointer to do the actual implementation. This can be on a per + handle basis. +*/ \ No newline at end of file diff --git a/lib/vpi_shim/gpi_priv.h b/lib/vpi_shim/gpi_priv.h new file mode 100644 index 00000000..a08fe494 --- /dev/null +++ b/lib/vpi_shim/gpi_priv.h @@ -0,0 +1,68 @@ +/****************************************************************************** +* Copyright (c) 2013 Potential Ventures Ltd +* Copyright (c) 2013 SolarFlare Communications Inc +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd, +* SolarFlare Communications Inc nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +#include + +#define gpi_container_of(_address, _type, _member) \ + ((_type *)((uintptr_t)(_address) - \ + (uintptr_t)(&((_type *)0)->_member))) + +typedef struct t_gpi_impl_tbl { + void (*sim_end)(void); + void (*get_sim_time)(uint32_t *high, uint32_t *low); + gpi_sim_hdl (*get_root_handle)(const char *name); + gpi_sim_hdl (*get_handle_by_name)(const char *name, gpi_sim_hdl parent); + gpi_sim_hdl (*get_handle_by_index)(gpi_sim_hdl parent, uint32_t index); + void (*free_handle)(gpi_sim_hdl); + gpi_iterator_hdl (*iterate_handle)(uint32_t type, gpi_sim_hdl base); + gpi_sim_hdl (*next_handle)(gpi_iterator_hdl iterator); + char* (*get_signal_value_binstr)(gpi_sim_hdl gpi_hdl); + char* (*get_signal_name_str)(gpi_sim_hdl gpi_hdl); + char* (*get_signal_type_str)(gpi_sim_hdl gpi_hdl); + void (*set_signal_value_int)(gpi_sim_hdl gpi_hdl, int value); + void (*set_signal_value_str)(gpi_sim_hdl gpi_hdl, const char *str); // String of binary char(s) [1, 0, x, z] + int (*register_timed_callback)(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps); + int (*register_value_change_callback)(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl); + int (*register_readonly_callback)(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); + int (*register_nexttime_callback)(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); + int (*register_readwrite_callback)(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); + gpi_cb_hdl (*create_cb_handle)(void); + void (*destroy_cb_handle)(gpi_cb_hdl gpi_hdl); + int (*deregister_callback)(gpi_sim_hdl gpi_hdl); +} s_gpi_impl_tbl, *gpi_impl_tbl; + +int gpi_register_impl(gpi_impl_tbl func_tbl, int type); + +void gpi_embed_init(gpi_sim_info_t *info); +void gpi_embed_end(void); +void gpi_embed_init_python(void); + +char *gpi_copy_name(const char *name); + +void gpi_handle_callback(gpi_sim_hdl cb_data); \ No newline at end of file From 32c74af70a838cf64646c42cd43862b27c35cfa9 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 11 May 2014 17:05:01 +0100 Subject: [PATCH 0510/2656] Issue #127: Fix some errors --- lib/vpi_shim/gpi_vpi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/vpi_shim/gpi_vpi.c index e554b672..17baa682 100644 --- a/lib/vpi_shim/gpi_vpi.c +++ b/lib/vpi_shim/gpi_vpi.c @@ -584,7 +584,6 @@ int vpi_register_value_change_callback(gpi_sim_hdl cb, return ret; } - int vpi_register_readonly_callback(gpi_sim_hdl cb, int (*gpi_function)(void *), void *gpi_cb_data) @@ -728,7 +727,7 @@ int vpi_register_timed_callback(gpi_sim_hdl cb, /* Checking of validity is done in the common code */ gpi_cb_hdl vpi_create_cb_handle(void) { - gpi_sim_hdl ret = NULL; + gpi_cb_hdl ret = NULL; FENTER p_vpi_cb new_cb_hdl = calloc(1, sizeof(*new_cb_hdl)); From cba4e323037d1ba5c0a6fbd673d3114f72e7b70c Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 11 May 2014 17:06:51 +0100 Subject: [PATCH 0511/2656] Issue #127: Move gpi to gpi_log, since it is log lib --- lib/Makefile | 4 ++-- lib/{gpi => gpi_log}/Makefile | 0 lib/{gpi => gpi_log}/gpi_logging.c | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename lib/{gpi => gpi_log}/Makefile (100%) rename lib/{gpi => gpi_log}/gpi_logging.c (100%) diff --git a/lib/Makefile b/lib/Makefile index 68615204..37948d7e 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -46,8 +46,8 @@ $(LIB_OBJ_DIR): $(LIB_DIR): $(LIB_OBJ_DIR) mkdir -p $@ -$(LIB_DIR)/libgpilog.so: $(SIM_ROOT)/lib/gpi/gpi_logging.c | $(LIB_DIR) - make -C $(SIM_ROOT)/lib/gpi +$(LIB_DIR)/libgpilog.so: $(SIM_ROOT)/lib/gpi_log/gpi_logging.c | $(LIB_DIR) + make -C $(SIM_ROOT)/lib/gpi_log $(LIB_DIR)/libcocotb.so: $(SIM_ROOT)/lib/embed/gpi_embed.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/embed diff --git a/lib/gpi/Makefile b/lib/gpi_log/Makefile similarity index 100% rename from lib/gpi/Makefile rename to lib/gpi_log/Makefile diff --git a/lib/gpi/gpi_logging.c b/lib/gpi_log/gpi_logging.c similarity index 100% rename from lib/gpi/gpi_logging.c rename to lib/gpi_log/gpi_logging.c From 2b7288cc550a77da4433c733ec4b9789cde64e3d Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 11 May 2014 17:13:04 +0100 Subject: [PATCH 0512/2656] Issue #127: Rename vpi_shim dir since this now has the implementations --- lib/{vpi_shim => gpi_impl}/Makefile | 0 lib/{vpi_shim => gpi_impl}/gpi_common.c | 0 lib/{vpi_shim => gpi_impl}/gpi_priv.h | 0 lib/{vpi_shim => gpi_impl}/gpi_vpi.c | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename lib/{vpi_shim => gpi_impl}/Makefile (100%) rename lib/{vpi_shim => gpi_impl}/gpi_common.c (100%) rename lib/{vpi_shim => gpi_impl}/gpi_priv.h (100%) rename lib/{vpi_shim => gpi_impl}/gpi_vpi.c (100%) diff --git a/lib/vpi_shim/Makefile b/lib/gpi_impl/Makefile similarity index 100% rename from lib/vpi_shim/Makefile rename to lib/gpi_impl/Makefile diff --git a/lib/vpi_shim/gpi_common.c b/lib/gpi_impl/gpi_common.c similarity index 100% rename from lib/vpi_shim/gpi_common.c rename to lib/gpi_impl/gpi_common.c diff --git a/lib/vpi_shim/gpi_priv.h b/lib/gpi_impl/gpi_priv.h similarity index 100% rename from lib/vpi_shim/gpi_priv.h rename to lib/gpi_impl/gpi_priv.h diff --git a/lib/vpi_shim/gpi_vpi.c b/lib/gpi_impl/gpi_vpi.c similarity index 100% rename from lib/vpi_shim/gpi_vpi.c rename to lib/gpi_impl/gpi_vpi.c From a23f12609f009be586bb060825ec0fa4e68fc887 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 11 May 2014 17:16:59 +0100 Subject: [PATCH 0513/2656] Issue #127: Move gpi_vhpi to gpi_impl dir (does not compile) --- lib/Makefile | 6 ++++-- lib/gpi_impl/Makefile | 2 +- lib/{vhpi_shim => gpi_impl}/gpi_vhpi.c | 0 3 files changed, 5 insertions(+), 3 deletions(-) rename lib/{vhpi_shim => gpi_impl}/gpi_vhpi.c (100%) diff --git a/lib/Makefile b/lib/Makefile index 37948d7e..b91c6542 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -52,8 +52,10 @@ $(LIB_DIR)/libgpilog.so: $(SIM_ROOT)/lib/gpi_log/gpi_logging.c | $(LIB_DIR) $(LIB_DIR)/libcocotb.so: $(SIM_ROOT)/lib/embed/gpi_embed.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/embed -$(LIB_DIR)/libgpi.so: $(SIM_ROOT)/lib/vpi_shim/gpi_common.c $(SIM_ROOT)/lib/$(GPI_IMPL)_shim/gpi_$(GPI_IMPL).c | $(LIB_DIR) - make -C $(SIM_ROOT)/lib/$(GPI_IMPL)_shim EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) +$(LIB_DIR)/libgpi.so: $(SIM_ROOT)/lib/gpi_impl/gpi_common.c \ + $(SIM_ROOT)/lib/gpi_impl/gpi_vpi.c \ + $(SIM_ROOT)/lib/gpi_impl/gpi_vhpi.c | $(LIB_DIR) + make -C $(SIM_ROOT)/lib/gpi_impl EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) $(LIB_DIR)/libsim.so: $(SIM_ROOT)/lib/simulator/simulatormodule.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/simulator diff --git a/lib/gpi_impl/Makefile b/lib/gpi_impl/Makefile index 2f3f06f0..37b3b9c6 100644 --- a/lib/gpi_impl/Makefile +++ b/lib/gpi_impl/Makefile @@ -35,7 +35,7 @@ LIBS := -lgpilog -lcocotb $(EXTRA_LIBS) LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) LIB_NAME := libgpi.so -SRCS := gpi_common.c gpi_vpi.c +SRCS := gpi_common.c gpi_vpi.c gpi_vhpi.c CLIBS += $(LIB_DIR)/gpivpi.vpl diff --git a/lib/vhpi_shim/gpi_vhpi.c b/lib/gpi_impl/gpi_vhpi.c similarity index 100% rename from lib/vhpi_shim/gpi_vhpi.c rename to lib/gpi_impl/gpi_vhpi.c From 924edbc0c3e37a320b8394094c7ca8e4f5205eb8 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 11 May 2014 18:41:04 +0100 Subject: [PATCH 0514/2656] Cleanup: do not show error if file is not there on clean --- makefiles/Makefile.sim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/Makefile.sim b/makefiles/Makefile.sim index 9d7c195a..bf046bd2 100644 --- a/makefiles/Makefile.sim +++ b/makefiles/Makefile.sim @@ -68,5 +68,5 @@ regression: results.xml # and RTL compilation phases are still evaluated by makefile dependencies) .PHONY: sim sim: - -@rm results.xml + -@rm -f results.xml $(MAKE) results.xml From 045d033a29c337177c57c9319d9ded454e16db23 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 11 May 2014 18:41:48 +0100 Subject: [PATCH 0515/2656] Issue #127: Tie into new structure --- lib/gpi_impl/gpi_vhpi.c | 577 +++++++++++++++------------------------- 1 file changed, 217 insertions(+), 360 deletions(-) diff --git a/lib/gpi_impl/gpi_vhpi.c b/lib/gpi_impl/gpi_vhpi.c index 992499bd..884e3e1d 100644 --- a/lib/gpi_impl/gpi_vhpi.c +++ b/lib/gpi_impl/gpi_vhpi.c @@ -26,31 +26,15 @@ ******************************************************************************/ // TODO: -// Now that we have VPI and VHPI we could refactor some of the common code into -// another file (e.g. gpi_copy_name). -// -// This file could be neater, for example by using a mapping of callback type to -// free_one_time vs. free_recurring. -// -// Some functions are completely untested (gpi_get_handle_by_index) and others +// Some functions are completely untested (vhpi_get_handle_by_index) and others // need optimisation. // // VHPI seems to run significantly slower the VPI, need to investigate. -#include -#include -#include - -#include -#include -#include +#include "gpi_priv.h" #include -#define gpi_container_of(_address, _type, _member) \ - ((_type *)((uintptr_t)(_address) - \ - (uintptr_t)(&((_type *)0)->_member))) - #define VHPI_CHECKING 1 static gpi_sim_hdl sim_init_cb; @@ -66,29 +50,16 @@ typedef enum vhpi_cb_state_e { // callback user data used for VPI callbacks // (mostly just a thin wrapper around the gpi_callback) -typedef struct t_vhpi_cb_user_data { - void *gpi_cb_data; - int (*gpi_function)(void *); - int (*gpi_cleanup)(struct t_vhpi_cb_user_data *); - vhpiHandleT cb_hdl; - vhpiValueT cb_value; - gpi_sim_hdl_t gpi_hdl; - vhpi_cb_state_t state; -} s_vhpi_cb_user_data, *p_vhpi_cb_user_data; - -// Define a type of a clock object -typedef struct gpi_clock_s { - int period; - int value; - unsigned int max_cycles; - unsigned int curr_cycle; - bool exit; - gpi_sim_hdl_t gpi_hdl; /* Handle to pass back to called */ - gpi_sim_hdl clk_hdl; /* Handle for signal to operate on */ - gpi_sim_hdl cb_hdl; /* Handle for the current pending callback */ -} gpi_clock_t; - -typedef gpi_clock_t *gpi_clock_hdl; +typedef struct t_vhpi_cb { + vhpiHandleT cb_hdl; + vhpiValueT cb_value; + vhpi_cb_state_t state; + gpi_cb_hdl_t gpi_cb_data; + int (*vhpi_cleanup)(struct t_vhpi_cb *); +} s_vhpi_cb, *p_vhpi_cb; + +// Forward declarations +static int vhpi_deregister_callback(gpi_sim_hdl gpi_hdl); static const char * vhpi_reason_to_string(int reason) { @@ -188,7 +159,7 @@ static int __check_vhpi_error(const char *func, long line) } gpi_log("cocotb.gpi", loglevel, __FILE__, func, line, - "VPI Error level %d: %s\nFILE %s:%d", + "VHPI Error level %d: %s\nFILE %s:%d", info.severity, info.message, info.file, info.line); #endif @@ -198,7 +169,7 @@ static int __check_vhpi_error(const char *func, long line) #define check_vhpi_error() \ __check_vhpi_error(__func__, __LINE__) -static inline int __gpi_register_cb(p_vhpi_cb_user_data user, vhpiCbDataT *cb_data) +static inline int __vhpi_register_cb(p_vhpi_cb user, vhpiCbDataT *cb_data) { /* If the user data already has a callback handle then deregister * before getting the new one @@ -215,50 +186,15 @@ static inline int __gpi_register_cb(p_vhpi_cb_user_data user, vhpiCbDataT *cb_da if (user->cb_hdl != NULL) { printf("VHPI: Attempt to register a callback that's already registered...\n"); - gpi_deregister_callback(&user->gpi_hdl); + vhpi_deregister_callback(&user->gpi_cb_data.hdl); } user->cb_hdl = new_hdl; + user->state = VHPI_PRIMED; return ret; } -static inline p_vhpi_cb_user_data __gpi_alloc_user(void) -{ - p_vhpi_cb_user_data new_data = calloc(1, sizeof(*new_data)); - if (new_data == NULL) { - LOG_CRITICAL("VPI: Attempting allocate user_data failed!"); - } - - return new_data; -} - -static inline void __gpi_free_callback(gpi_sim_hdl gpi_hdl) -{ - FENTER - p_vhpi_cb_user_data user_data; - user_data = gpi_container_of(gpi_hdl, s_vhpi_cb_user_data, gpi_hdl); - - free(user_data); - FEXIT -} - -void gpi_free_handle(gpi_sim_hdl gpi_hdl) -{ - free(gpi_hdl); -} - -static gpi_sim_hdl gpi_alloc_handle(void) -{ - gpi_sim_hdl new_hdl = calloc(1, sizeof(*new_hdl)); - if (!new_hdl) { - LOG_CRITICAL("VPI: Could not allocate handle"); - exit(1); - } - - return new_hdl; -} - // Handle related functions /** * @name Find the root handle @@ -274,7 +210,7 @@ static gpi_sim_hdl gpi_alloc_handle(void) * If name is provided, we check the name against the available objects until * we find a match. If no match is found we return NULL */ -gpi_sim_hdl gpi_get_root_handle(const char* name) +static gpi_sim_hdl vhpi_get_root_handle(const char* name) { FENTER vhpiHandleT root; @@ -311,7 +247,7 @@ gpi_sim_hdl gpi_get_root_handle(const char* name) return NULL; } - rv = gpi_alloc_handle(); + rv = gpi_create_handle(); rv->sim_hdl = dut; FEXIT @@ -319,7 +255,7 @@ gpi_sim_hdl gpi_get_root_handle(const char* name) } -gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) +static gpi_sim_hdl vhpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) { FENTER gpi_sim_hdl rv; @@ -345,7 +281,7 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) free(buff); - rv = gpi_alloc_handle(); + rv = gpi_create_handle(); rv->sim_hdl = obj; FEXIT @@ -361,7 +297,7 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) * Can be used on bit-vectors to access a specific bit or * memories to access an address */ -gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) +static gpi_sim_hdl vhpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) { FENTER gpi_sim_hdl rv; @@ -373,7 +309,7 @@ gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) return NULL; } - rv = gpi_alloc_handle(); + rv = gpi_create_handle(); rv->sim_hdl = obj; FEXIT @@ -384,7 +320,7 @@ gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) // Functions for iterating over entries of a handle // Returns an iterator handle which can then be used in gpi_next calls // NB May return NULL if no objects of the request type exist -gpi_iterator_hdl gpi_iterate(uint32_t type, gpi_sim_hdl base) { +static gpi_iterator_hdl vhpi_iterate_hdl(uint32_t type, gpi_sim_hdl base) { FENTER vhpiHandleT iterator; @@ -397,11 +333,11 @@ gpi_iterator_hdl gpi_iterate(uint32_t type, gpi_sim_hdl base) { } // Returns NULL when there are no more objects -gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) +static gpi_sim_hdl vhpi_next_hdl(gpi_iterator_hdl iterator) { FENTER vhpiHandleT result; - gpi_sim_hdl rv = gpi_alloc_handle(); + gpi_sim_hdl rv = gpi_create_handle(); rv->sim_hdl = vhpi_scan((vhpiHandleT) iterator); check_vhpi_error(); @@ -413,7 +349,7 @@ gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) return rv; } -void gpi_get_sim_time(uint32_t *high, uint32_t *low) +static void vhpi_get_sim_time(uint32_t *high, uint32_t *low) { vhpiTimeT vhpi_time_s; vhpi_get_time(&vhpi_time_s, NULL); @@ -445,7 +381,7 @@ static vhpiEnumT chr2vhpi(const char value) { // Unfortunately it seems that format conversion is not well supported // We have to set values using vhpiEnum* -void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) +static void vhpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) { FENTER vhpiValueT value_s; @@ -495,7 +431,7 @@ void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) // Unfortunately it seems that format conversion is not well supported // We have to set values using vhpiEnum* -void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) +static void vhpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) { FENTER vhpiValueT value_s; @@ -546,35 +482,7 @@ void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) FEXIT } - -static char *gpi_copy_name(const char *name) -{ - int len; - char *result; - const char null[] = "NULL"; - - if (name) - len = strlen(name) + 1; - else { - LOG_CRITICAL("VPI: NULL came back from VPI"); - len = strlen(null); - name = null; - } - - result = (char *)malloc(len); - if (result == NULL) { - LOG_CRITICAL("VPI: Attempting allocate string buffer failed!"); - len = strlen(null); - name = null; - } - - snprintf(result, len, "%s", name); - - return result; -} - - -char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) +static char *vhpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) { FENTER vhpiValueT value_s; @@ -614,20 +522,20 @@ char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) return result; } -char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl) +static char *vhpi_get_signal_name_str(gpi_sim_hdl gpi_hdl) { FENTER - const char *name = vhpi_get_str(vhpiFullNameP, (vpiHandle)(gpi_hdl->sim_hdl)); + const char *name = vhpi_get_str(vhpiFullNameP, (vhpiHandleT)(gpi_hdl->sim_hdl)); check_vhpi_error(); char *result = gpi_copy_name(name); FEXIT return result; } -char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl) +static char *vhpi_get_signal_type_str(gpi_sim_hdl gpi_hdl) { FENTER - const char *name = vhpi_get_str(vhpiKindStrP, (vpiHandle)(gpi_hdl->sim_hdl)); + const char *name = vhpi_get_str(vhpiKindStrP, (vhpiHandleT)(gpi_hdl->sim_hdl)); check_vhpi_error(); char *result = gpi_copy_name(name); FEXIT @@ -640,26 +548,26 @@ static void handle_vhpi_callback(const vhpiCbDataT *cb_data) { FENTER int rv = 0; - vpiHandle old_cb; + vhpiHandleT old_cb; - p_vhpi_cb_user_data user_data; - user_data = (p_vhpi_cb_user_data)cb_data->user_data; + p_vhpi_cb user_data; + user_data = (p_vhpi_cb)cb_data->user_data; if (!user_data) - LOG_CRITICAL("VPI: Callback data corrupted"); + LOG_CRITICAL("VHPI: Callback data corrupted"); user_data->state = VHPI_PRE_CALL; old_cb = user_data->cb_hdl; - rv = user_data->gpi_function(user_data->gpi_cb_data); + gpi_handle_callback(&user_data->gpi_cb_data.hdl); if (old_cb == user_data->cb_hdl) - gpi_deregister_callback(&user_data->gpi_hdl); + gpi_deregister_callback(&user_data->gpi_cb_data.hdl); /* A request to delete could have been done * inside gpi_function */ if (user_data->state == VHPI_DELETE) - gpi_destroy_cb_handle(&user_data->gpi_hdl); + gpi_destroy_cb_handle(&user_data->gpi_cb_data); else user_data->state = VHPI_POST_CALL; @@ -671,16 +579,14 @@ static void handle_vhpi_callback(const vhpiCbDataT *cb_data) * handle, this may be short or long. A call to create * must have a matching call to destroy at some point */ -gpi_sim_hdl gpi_create_cb_handle(void) +static gpi_cb_hdl vhpi_create_cb_handle(void) { - gpi_sim_hdl ret = NULL; + gpi_cb_hdl ret = NULL; FENTER - p_vhpi_cb_user_data user_data = __gpi_alloc_user(); - if (user_data) { - user_data->state = VHPI_FREE; - ret = &user_data->gpi_hdl; - } + p_vhpi_cb user_data = calloc(1, sizeof(*user_data)); + if (user_data) + ret = &user_data->gpi_cb_data; FEXIT return ret; @@ -690,21 +596,15 @@ gpi_sim_hdl gpi_create_cb_handle(void) * this can only be called on a handle that has been * returned by a call to gpi_create_cb_handle */ -void gpi_destroy_cb_handle(gpi_sim_hdl gpi_hdl) +static void vhpi_destroy_cb_handle(gpi_cb_hdl hdl) { /* Check that is has been called, if this has not * happend then also close down the sim data as well */ FENTER - p_vhpi_cb_user_data user_data; - user_data = gpi_container_of(gpi_hdl, s_vhpi_cb_user_data, gpi_hdl); - - if (user_data->state == VHPI_PRE_CALL) { - user_data->state = VHPI_DELETE; - } else { - gpi_deregister_callback(gpi_hdl); - __gpi_free_callback(gpi_hdl); - } + p_vhpi_cb user_data = gpi_container_of(hdl, s_vhpi_cb, gpi_cb_data); + + free(user_data); FEXIT } @@ -713,17 +613,19 @@ void gpi_destroy_cb_handle(gpi_sim_hdl gpi_hdl) * This can be called at any point between * gpi_create_cb_handle and gpi_destroy_cb_handle */ -int gpi_deregister_callback(gpi_sim_hdl gpi_hdl) +static int vhpi_deregister_callback(gpi_sim_hdl gpi_hdl) { - p_vhpi_cb_user_data user_data; - int rc = 1; FENTER + p_vhpi_cb vhpi_user_data; + gpi_cb_hdl gpi_user_data; + int rc = 1; - user_data = gpi_container_of(gpi_hdl, s_vhpi_cb_user_data, gpi_hdl); + gpi_user_data = gpi_container_of(gpi_hdl, gpi_cb_hdl_t, hdl); + vhpi_user_data = gpi_container_of(gpi_hdl, s_vhpi_cb, gpi_cb_data); - if (user_data->cb_hdl != NULL) { - rc = user_data->gpi_cleanup(user_data); - user_data->cb_hdl = NULL; + if (vhpi_user_data->cb_hdl != NULL) { + rc = vhpi_user_data->vhpi_cleanup(vhpi_user_data); + vhpi_user_data->cb_hdl = NULL; } FEXIT @@ -733,13 +635,13 @@ int gpi_deregister_callback(gpi_sim_hdl gpi_hdl) // Call when the handle relates to a one time callback // No need to call vhpi_deregister_cb as the sim will // do this but do need to destroy the handle -static int gpi_free_one_time(p_vhpi_cb_user_data user_data) +static int vhpi_free_one_time(p_vhpi_cb user_data) { FENTER int rc = 0; vhpiHandleT cb_hdl = user_data->cb_hdl; if (!cb_hdl) { - LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); + LOG_CRITICAL("VHPI: passed a NULL pointer : ABORTING"); exit(1); } @@ -765,13 +667,13 @@ static int gpi_free_one_time(p_vhpi_cb_user_data user_data) // Call when the handle relates to recurring callback // Unregister must be called when not needed and this // will clean all memory allocated by the sim -static int gpi_free_recurring(p_vhpi_cb_user_data user_data) +static int vhpi_free_recurring(p_vhpi_cb user_data) { FENTER int rc; vhpiHandleT cb_hdl = user_data->cb_hdl; if (!cb_hdl) { - LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); + LOG_CRITICAL("VHPI: passed a NULL pointer : ABORTING"); exit(1); } @@ -786,153 +688,151 @@ static int gpi_free_recurring(p_vhpi_cb_user_data user_data) * allocated with gpi_create_cb_handle first */ -int gpi_register_value_change_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data, - gpi_sim_hdl gpi_hdl) +static int vhpi_register_value_change_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data, + gpi_sim_hdl gpi_hdl) { FENTER + + int ret; vhpiCbDataT cb_data_s; vhpiTimeT time; + p_vhpi_cb vhpi_user_data; + gpi_cb_hdl gpi_user_data; - p_vhpi_cb_user_data user_data; - int ret; - - user_data = gpi_container_of(cb, s_vhpi_cb_user_data, gpi_hdl); + gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); + vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); - user_data->gpi_cb_data = gpi_cb_data; - user_data->gpi_function = gpi_function; - user_data->gpi_cleanup = gpi_free_recurring; - user_data->cb_value.format = vhpiIntVal; + vhpi_user_data->vhpi_cleanup = vhpi_free_recurring; + vhpi_user_data->cb_value.format = vhpiIntVal; cb_data_s.reason = vhpiCbValueChange; cb_data_s.cb_rtn = handle_vhpi_callback; cb_data_s.obj = (vhpiHandleT)(gpi_hdl->sim_hdl); cb_data_s.time = &time; - cb_data_s.value = &user_data->cb_value; - cb_data_s.user_data = (char *)user_data; - - ret = __gpi_register_cb(user_data, &cb_data_s); - user_data->state = VHPI_PRIMED; + cb_data_s.value = &vhpi_user_data->cb_value; + cb_data_s.user_data = (char *)vhpi_user_data; + ret = __vhpi_register_cb(vhpi_user_data, &cb_data_s); + FEXIT return ret; } -int gpi_register_readonly_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) +static int vhpi_register_readonly_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) { FENTER - vhpiCbDataT cb_data_s; - vhpiTimeT time; - p_vhpi_cb_user_data user_data; int ret; + vhpiCbDataT cb_data_s; + vhpiTimeT time; + p_vhpi_cb vhpi_user_data; + gpi_cb_hdl gpi_user_data; - user_data = gpi_container_of(cb, s_vhpi_cb_user_data, gpi_hdl); + gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); + vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); - user_data->gpi_cb_data = gpi_cb_data; - user_data->gpi_function = gpi_function; - user_data->gpi_cleanup = gpi_free_one_time; + vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; cb_data_s.reason = vhpiCbLastKnownDeltaCycle; cb_data_s.cb_rtn = handle_vhpi_callback; cb_data_s.obj = NULL; cb_data_s.time = &time; cb_data_s.value = NULL; - cb_data_s.user_data = (char *)user_data; - - ret = __gpi_register_cb(user_data, &cb_data_s); - user_data->state = VHPI_PRIMED; + cb_data_s.user_data = (char *)vhpi_user_data; + ret = __vhpi_register_cb(vhpi_user_data, &cb_data_s); + FEXIT return ret; } -int gpi_register_readwrite_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) +static int vhpi_register_readwrite_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) { FENTER + + + int ret; vhpiCbDataT cb_data_s; vhpiTimeT time; + p_vhpi_cb vhpi_user_data; + gpi_cb_hdl gpi_user_data; - p_vhpi_cb_user_data user_data; - int ret; - - user_data = gpi_container_of(cb, s_vhpi_cb_user_data, gpi_hdl); + gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); + vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); - user_data->gpi_cb_data = gpi_cb_data; - user_data->gpi_function = gpi_function; - user_data->gpi_cleanup = gpi_free_one_time; + vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; cb_data_s.reason = vhpiCbEndOfProcesses; cb_data_s.cb_rtn = handle_vhpi_callback; cb_data_s.obj = NULL; cb_data_s.time = &time; cb_data_s.value = NULL; - cb_data_s.user_data = (char *)user_data; - - ret = __gpi_register_cb(user_data, &cb_data_s); - user_data->state = VHPI_PRIMED; + cb_data_s.user_data = (char *)vhpi_user_data; + ret = __vhpi_register_cb(vhpi_user_data, &cb_data_s); + FEXIT return ret; } -int gpi_register_nexttime_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) +static int vhpi_register_nexttime_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) { FENTER - vhpiCbDataT cb_data_s; - vhpiTimeT time; - p_vhpi_cb_user_data user_data; int ret; + vhpiCbDataT cb_data_s; + vhpiTimeT time; + p_vhpi_cb vhpi_user_data; + gpi_cb_hdl gpi_user_data; - user_data = gpi_container_of(cb, s_vhpi_cb_user_data, gpi_hdl); + gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); + vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); - user_data->gpi_cb_data = gpi_cb_data; - user_data->gpi_function = gpi_function; - user_data->gpi_cleanup = gpi_free_one_time; + vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; cb_data_s.reason = vhpiCbNextTimeStep; cb_data_s.cb_rtn = handle_vhpi_callback; cb_data_s.obj = NULL; cb_data_s.time = &time; cb_data_s.value = NULL; - cb_data_s.user_data = (char *)user_data; + cb_data_s.user_data = (char *)vhpi_user_data; - ret = __gpi_register_cb(user_data, &cb_data_s); - user_data->state = VHPI_PRIMED; + ret = __vhpi_register_cb(vhpi_user_data, &cb_data_s); FEXIT return ret; } -int gpi_register_timed_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data, - uint64_t time_ps) + +static int vhpi_register_timed_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data, + uint64_t time_ps) { FENTER + + int ret; vhpiCbDataT cb_data_s; vhpiTimeT time_s; + p_vhpi_cb vhpi_user_data; + gpi_cb_hdl gpi_user_data; - p_vhpi_cb_user_data user_data; - int ret; - - user_data = gpi_container_of(cb, s_vhpi_cb_user_data, gpi_hdl); + gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); + vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); - user_data->gpi_cb_data = gpi_cb_data; - user_data->gpi_function = gpi_function; - user_data->gpi_cleanup = gpi_free_one_time; + vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; time_s.high = (uint32_t)(time_ps>>32); time_s.low = (uint32_t)(time_ps); @@ -942,140 +842,60 @@ int gpi_register_timed_callback(gpi_sim_hdl cb, cb_data_s.obj = NULL; cb_data_s.time = &time_s; cb_data_s.value = NULL; - cb_data_s.user_data = (char *)user_data; + cb_data_s.user_data = (char *)vhpi_user_data; - ret = __gpi_register_cb(user_data, &cb_data_s); - user_data->state = VHPI_PRIMED; + ret = __vhpi_register_cb(vhpi_user_data, &cb_data_s); FEXIT return ret; } -int gpi_register_sim_start_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) -{ - FENTER - - vhpiCbDataT cb_data_s; - p_vhpi_cb_user_data user_data; - - user_data = gpi_container_of(cb, s_vhpi_cb_user_data, gpi_hdl); - - user_data->gpi_cb_data = gpi_cb_data; - user_data->gpi_function = gpi_function; - user_data->gpi_cleanup = gpi_free_one_time; - - cb_data_s.reason = vhpiCbStartOfSimulation; - cb_data_s.cb_rtn = handle_vhpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = NULL; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)user_data; - - /* We ignore the return value here as VCS does some silly - * things on comilation that means it tries to run through - * the vlog_startup_routines and so call this routine - */ - __gpi_register_cb(user_data, &cb_data_s); - user_data->state = VHPI_PRIMED; - - FEXIT - return 0; - -} - -int gpi_register_sim_end_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) -{ - FENTER - - vhpiCbDataT cb_data_s; - p_vhpi_cb_user_data user_data; - - user_data = gpi_container_of(cb, s_vhpi_cb_user_data, gpi_hdl); - - user_data->gpi_cb_data = gpi_cb_data; - user_data->gpi_function = gpi_function; - user_data->gpi_cleanup = gpi_free_one_time; - - cb_data_s.reason = vhpiCbEndOfSimulation; - cb_data_s.cb_rtn = handle_vhpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = NULL; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)user_data; - - /* We ignore the return value here as VCS does some silly - * things on comilation that means it tries to run through - * the vlog_startup_routines and so call this routine - */ - __gpi_register_cb(user_data, &cb_data_s); - user_data->state = VHPI_PRIMED; - - FEXIT - return 0; - -} - -int gpi_clock_handler(void *clock) -{ - gpi_clock_hdl hdl = (gpi_clock_hdl)clock; - gpi_sim_hdl cb_hdl; - - if (hdl->exit || ((hdl->max_cycles != 0) && (hdl->max_cycles == hdl->curr_cycle))) - return; - - /* Unregister/free the last callback that just fired */ - cb_hdl = hdl->cb_hdl; - - hdl->value = !hdl->value; - gpi_set_signal_value_int(hdl->clk_hdl, hdl->value); - gpi_register_timed_callback(cb_hdl, gpi_clock_handler, hdl, hdl->period); - hdl->curr_cycle++; -} -gpi_sim_hdl gpi_clock_register(gpi_sim_hdl sim_hdl, int period, unsigned int cycles) +// If the Pything world wants things to shut down then unregister +// the callback for end of sim +static void vhpi_sim_end(void) { FENTER - gpi_clock_hdl hdl = malloc(sizeof(gpi_clock_t)); - if (!hdl) - LOG_CRITICAL("VPI: Unable to allocate memory"); - - hdl->period = period; - hdl->value = 0; - hdl->clk_hdl = sim_hdl; - hdl->exit = false; - hdl->max_cycles = cycles; - hdl->curr_cycle = 0; - - gpi_set_signal_value_int(hdl->clk_hdl, hdl->value); - hdl->cb_hdl = gpi_create_cb_handle(); - - gpi_register_timed_callback(hdl->cb_hdl, gpi_clock_handler, hdl, hdl->period); - + sim_finish_cb = NULL; + vhpi_control(vhpiFinish); + check_vhpi_error(); FEXIT - return &hdl->gpi_hdl; } -void gpi_clock_unregister(gpi_sim_hdl clock) -{ - gpi_clock_hdl hdl = gpi_container_of(clock, gpi_clock_t, gpi_hdl); - hdl->exit = true; -} +static s_gpi_impl_tbl vhpi_table = { + .sim_end = vhpi_sim_end, + .iterate_handle = vhpi_iterate_hdl, + .next_handle = vhpi_next_hdl, + .create_cb_handle = vhpi_create_cb_handle, + .destroy_cb_handle = vhpi_destroy_cb_handle, + .deregister_callback = vhpi_deregister_callback, + .get_root_handle = vhpi_get_root_handle, + .get_sim_time = vhpi_get_sim_time, + .get_handle_by_name = vhpi_get_handle_by_name, + .get_handle_by_index = vhpi_get_handle_by_index, + .get_signal_name_str = vhpi_get_signal_name_str, + .get_signal_type_str = vhpi_get_signal_type_str, + .get_signal_value_binstr = vhpi_get_signal_value_binstr, + .set_signal_value_int = vhpi_set_signal_value_int, + .set_signal_value_str = vhpi_set_signal_value_str, + .register_timed_callback = vhpi_register_timed_callback, + .register_readwrite_callback = vhpi_register_readwrite_callback, + .register_nexttime_callback = vhpi_register_nexttime_callback, + .register_value_change_callback = vhpi_register_value_change_callback, + .register_readonly_callback = vhpi_register_readonly_callback, +}; -void register_embed(void) +static void register_embed(void) { FENTER - embed_init_python(); + gpi_embed_init_python(); FEXIT } -int handle_sim_init(void *gpi_cb_data) +static int handle_sim_init(void *gpi_cb_data) { FENTER gpi_sim_info_t sim_info; @@ -1083,7 +903,7 @@ int handle_sim_init(void *gpi_cb_data) sim_info.argv = NULL; sim_info.product = gpi_copy_name(vhpi_get_str(vhpiNameP, NULL)); sim_info.version = gpi_copy_name(vhpi_get_str(vhpiToolVersionP, NULL)); - embed_sim_init(&sim_info); + gpi_embed_init(&sim_info); free(sim_info.product); free(sim_info.version); @@ -1091,15 +911,41 @@ int handle_sim_init(void *gpi_cb_data) FEXIT } -void register_initial_callback(void) +static void register_initial_callback(void) { FENTER + + vhpiCbDataT cb_data_s; + p_vhpi_cb vhpi_user_data; + gpi_cb_hdl gpi_user_data; + sim_init_cb = gpi_create_cb_handle(); - gpi_register_sim_start_callback(sim_init_cb, handle_sim_init, (void *)NULL); + + gpi_user_data = gpi_container_of(sim_init_cb, gpi_cb_hdl_t, hdl); + vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); + + gpi_user_data->gpi_cb_data = NULL; + gpi_user_data->gpi_function = handle_sim_init; + + vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; + + cb_data_s.reason = vhpiCbStartOfSimulation; + cb_data_s.cb_rtn = handle_vhpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = NULL; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)vhpi_user_data; + + /* We ignore the return value here as VCS does some silly + * things on comilation that means it tries to run through + * the vlog_startup_routines and so call this routine + */ + __vhpi_register_cb(vhpi_user_data, &cb_data_s); + FEXIT } -int handle_sim_end(void *gpi_cb_data) +static int handle_sim_end(void *gpi_cb_data) { FENTER if (sim_finish_cb) { @@ -1108,32 +954,43 @@ int handle_sim_end(void *gpi_cb_data) embed_sim_event(SIM_FAIL, "Simulator shutdown prematurely"); } /* Other sise we have already been here from the top down so do not need to inform the upper layers that anything has occoured */ - __gpi_free_callback(sim_init_cb); + gpi_free_cb_handle(sim_init_cb); FEXIT } -void register_final_callback(void) +static void register_final_callback(void) { FENTER + + vhpiCbDataT cb_data_s; + p_vhpi_cb vhpi_user_data; + gpi_cb_hdl gpi_user_data; + sim_finish_cb = gpi_create_cb_handle(); - gpi_register_sim_end_callback(sim_finish_cb, handle_sim_end, (void *)NULL); - FEXIT -} + gpi_user_data = gpi_container_of(sim_init_cb, gpi_cb_hdl_t, hdl); + vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); -// If the Pything world wants things to shut down then unregister -// the callback for end of sim -void gpi_sim_end(void) -{ - FENTER + gpi_user_data->gpi_cb_data = NULL; + gpi_user_data->gpi_function = handle_sim_end; + vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; + + cb_data_s.reason = vhpiCbEndOfSimulation; + cb_data_s.cb_rtn = handle_vhpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = NULL; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)vhpi_user_data; + + /* We ignore the return value here as VCS does some silly + * things on comilation that means it tries to run through + * the vlog_startup_routines and so call this routine + */ + __vhpi_register_cb(vhpi_user_data, &cb_data_s); - sim_finish_cb = NULL; - vhpi_control(vhpiFinish); - check_vhpi_error(); FEXIT } - // pre-defined VHPI registration table void (*vhpi_startup_routines[])(void) = { register_embed, From 13496d15ac11965cd0dc3d57d11c6f669bfdd47b Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 11 May 2014 18:42:09 +0100 Subject: [PATCH 0516/2656] Issue #127: Make functions static and fix errors --- lib/gpi_impl/gpi_vpi.c | 60 ++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/lib/gpi_impl/gpi_vpi.c b/lib/gpi_impl/gpi_vpi.c index 17baa682..cd11193e 100644 --- a/lib/gpi_impl/gpi_vpi.c +++ b/lib/gpi_impl/gpi_vpi.c @@ -53,6 +53,8 @@ typedef struct t_vpi_cb { int (*vpi_cleanup)(struct t_vpi_cb *cb_data); } s_vpi_cb, *p_vpi_cb; +// Forward declarations +static int vpi_deregister_callback(gpi_sim_hdl gpi_hdl); // Add to this over time static const char * vpi_reason_to_string(int reason) @@ -141,7 +143,7 @@ static inline int __vpi_register_cb(p_vpi_cb user, p_cb_data cb_data) if (user->cb_hdl != NULL) { fprintf(stderr, "user->gpi_cb_hdl is not null, deregistering %s!\n", vpi_reason_to_string(cb_data->reason)); - vpi_deregister_callback(user); + vpi_deregister_callback(&user->gpi_cb_data.hdl); } user->cb_hdl = new_hdl; @@ -461,7 +463,7 @@ static int32_t handle_vpi_callback(p_cb_data cb_data) * This can be called at any point between * gpi_create_cb_handle and gpi_destroy_cb_handle */ -int vpi_deregister_callback(gpi_sim_hdl gpi_hdl) +static int vpi_deregister_callback(gpi_sim_hdl gpi_hdl) { FENTER p_vpi_cb vpi_user_data; @@ -549,10 +551,10 @@ static int vpi_free_recurring(p_vpi_cb user_data) */ -int vpi_register_value_change_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data, - gpi_sim_hdl gpi_hdl) +static int vpi_register_value_change_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data, + gpi_sim_hdl gpi_hdl) { FENTER @@ -584,9 +586,9 @@ int vpi_register_value_change_callback(gpi_sim_hdl cb, return ret; } -int vpi_register_readonly_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) +static int vpi_register_readonly_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) { FENTER @@ -618,9 +620,9 @@ int vpi_register_readonly_callback(gpi_sim_hdl cb, return ret; } -int vpi_register_readwrite_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) +static int vpi_register_readwrite_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) { FENTER @@ -653,9 +655,9 @@ int vpi_register_readwrite_callback(gpi_sim_hdl cb, } -int vpi_register_nexttime_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) +static int vpi_register_nexttime_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) { FENTER @@ -687,10 +689,10 @@ int vpi_register_nexttime_callback(gpi_sim_hdl cb, return ret; } -int vpi_register_timed_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data, - uint64_t time_ps) +static int vpi_register_timed_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data, + uint64_t time_ps) { FENTER @@ -725,9 +727,10 @@ int vpi_register_timed_callback(gpi_sim_hdl cb, /* Checking of validity is done in the common code */ -gpi_cb_hdl vpi_create_cb_handle(void) +static gpi_cb_hdl vpi_create_cb_handle(void) { gpi_cb_hdl ret = NULL; + FENTER p_vpi_cb new_cb_hdl = calloc(1, sizeof(*new_cb_hdl)); @@ -738,7 +741,7 @@ gpi_cb_hdl vpi_create_cb_handle(void) return ret; } -void vpi_destroy_cb_handle(gpi_cb_hdl hdl) +static void vpi_destroy_cb_handle(gpi_cb_hdl hdl) { FENTER p_vpi_cb vpi_hdl = gpi_container_of(hdl, s_vpi_cb, gpi_cb_data); @@ -748,7 +751,7 @@ void vpi_destroy_cb_handle(gpi_cb_hdl hdl) // If the Pything world wants things to shut down then unregister // the callback for end of sim -void vpi_sim_end(void) +static void vpi_sim_end(void) { sim_finish_cb = NULL; vpi_control(vpiFinish); @@ -778,14 +781,14 @@ static s_gpi_impl_tbl vpi_table = { .register_readonly_callback = vpi_register_readonly_callback, }; -void register_embed(void) +static void register_embed(void) { gpi_register_impl(&vpi_table, 0xfeed); gpi_embed_init_python(); } -int handle_sim_init(void *gpi_cb_data) +static int handle_sim_init(void *gpi_cb_data) { FENTER s_vpi_vlog_info info; @@ -799,10 +802,11 @@ int handle_sim_init(void *gpi_cb_data) sim_info.version = info.version; gpi_embed_init(&sim_info); + FEXIT } -void register_initial_callback(void) +static void register_initial_callback(void) { FENTER @@ -836,7 +840,7 @@ void register_initial_callback(void) FEXIT } -int handle_sim_end(void *gpi_cb_data) +static int handle_sim_end(void *gpi_cb_data) { FENTER if (sim_finish_cb) { @@ -849,7 +853,7 @@ int handle_sim_end(void *gpi_cb_data) FEXIT } -void register_final_callback(void) +static void register_final_callback(void) { FENTER @@ -951,7 +955,7 @@ static int system_function_overload(char *userdata) return 0; } -void register_system_functions(void) +static void register_system_functions(void) { FENTER s_vpi_systf_data tfData = { vpiSysTask, vpiSysTask }; From a16292639aeb4ef84dce201a0288776222c21351 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 15 May 2014 18:05:50 +0100 Subject: [PATCH 0517/2656] Issue #127: Fix up VHPI implementation --- cocotb/triggers.py | 5 +--- include/gpi.h | 9 +++++-- lib/gpi_impl/gpi_common.c | 8 ++++-- lib/gpi_impl/gpi_priv.h | 4 ++- lib/gpi_impl/gpi_vhpi.c | 39 ++++++++++++++++++++++------- lib/gpi_impl/gpi_vpi.c | 16 +++++++++++- lib/simulator/simulatormodule.c | 44 +++++++++++++++++++++++++++++---- lib/simulator/simulatormodule.h | 2 ++ 8 files changed, 103 insertions(+), 24 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 4fdb48a8..13ae8cf6 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -215,14 +215,11 @@ def _check(obj): if self.signal.value: self._callback(self) else: - simulator.deregister_callback(self.cbhdl) - if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self): - raise_error(self, "Unable set up %s Trigger" % (str(self))) + simulator.reenable_callback(self.cbhdl) if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self): raise_error(self, "Unable set up %s Trigger" % (str(self))) - def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal.name diff --git a/include/gpi.h b/include/gpi.h index becddcad..bd9415d4 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -88,7 +88,7 @@ typedef enum gpi_event_e { } gpi_event_t; typedef struct gpi_sim_info_s -{ +{ int32_t argc; char **argv; char *product; @@ -105,7 +105,7 @@ typedef struct gpi_sim_hdl_s { typedef struct gpi_cb_hdl_s { gpi_sim_hdl_t hdl; int (*gpi_function)(void *); // GPI function to callback - void *gpi_cb_data; // GPI data supplied to "gpi_functin" + void *gpi_cb_data; // GPI data supplied to "gpi_function" } gpi_cb_hdl_t, *gpi_cb_hdl; // Define a handle type for iterators @@ -185,6 +185,11 @@ void gpi_free_handle(gpi_sim_hdl gpi_hdl); void gpi_deregister_callback(gpi_sim_hdl gpi_hdl); +// Because the internal structures may be different for different implementations +// of GPI we provide a convenience function to extract the callback data +void *gpi_get_callback_data(gpi_sim_hdl gpi_hdl); + + #define GPI_RET(_code) \ if (_code == 1) \ return 0; \ diff --git a/lib/gpi_impl/gpi_common.c b/lib/gpi_impl/gpi_common.c index 2a7b0d48..f4975903 100644 --- a/lib/gpi_impl/gpi_common.c +++ b/lib/gpi_impl/gpi_common.c @@ -43,7 +43,7 @@ static r_impl registed_impls[MAX_IMPLS] = {{NULL,0},}; static inline void set_user_data(gpi_sim_hdl hdl, int (*gpi_function)(void*), void *data) { gpi_cb_hdl gpi_user_data = gpi_container_of(hdl, gpi_cb_hdl_t, hdl); - + gpi_user_data->gpi_cb_data = data; gpi_user_data->gpi_function = gpi_function; } @@ -123,6 +123,10 @@ void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) IMPL_ROOT->set_signal_value_str(gpi_hdl, str); } +void *gpi_get_callback_data(gpi_sim_hdl gpi_hdl) +{ + return IMPL_ROOT->get_callback_data(gpi_hdl); +} int gpi_register_timed_callback(gpi_sim_hdl hdl, int (*gpi_function)(void *), @@ -259,4 +263,4 @@ char *gpi_copy_name(const char *name) Each of the top level calls then needs to call into a function table pointer to do the actual implementation. This can be on a per handle basis. -*/ \ No newline at end of file +*/ diff --git a/lib/gpi_impl/gpi_priv.h b/lib/gpi_impl/gpi_priv.h index a08fe494..f8235e10 100644 --- a/lib/gpi_impl/gpi_priv.h +++ b/lib/gpi_impl/gpi_priv.h @@ -55,6 +55,7 @@ typedef struct t_gpi_impl_tbl { gpi_cb_hdl (*create_cb_handle)(void); void (*destroy_cb_handle)(gpi_cb_hdl gpi_hdl); int (*deregister_callback)(gpi_sim_hdl gpi_hdl); + void* (*get_callback_data)(gpi_sim_hdl gpi_hdl); } s_gpi_impl_tbl, *gpi_impl_tbl; int gpi_register_impl(gpi_impl_tbl func_tbl, int type); @@ -65,4 +66,5 @@ void gpi_embed_init_python(void); char *gpi_copy_name(const char *name); -void gpi_handle_callback(gpi_sim_hdl cb_data); \ No newline at end of file +void gpi_handle_callback(gpi_sim_hdl cb_data); + diff --git a/lib/gpi_impl/gpi_vhpi.c b/lib/gpi_impl/gpi_vhpi.c index 884e3e1d..8827e607 100644 --- a/lib/gpi_impl/gpi_vhpi.c +++ b/lib/gpi_impl/gpi_vhpi.c @@ -184,8 +184,13 @@ static inline int __vhpi_register_cb(p_vhpi_cb user, vhpiCbDataT *cb_data) ret = -1; } + vhpiStateT cbState = vhpi_get(vhpiStateP, new_hdl); + if (cbState != vhpiEnable) { + LOG_CRITICAL("VHPI ERROR: Registered callback isn't enabled! Got %d\n", cbState); + } + if (user->cb_hdl != NULL) { - printf("VHPI: Attempt to register a callback that's already registered...\n"); + LOG_ERROR("VHPI: Attempt to register a callback that's already registered...\n"); vhpi_deregister_callback(&user->gpi_cb_data.hdl); } @@ -560,8 +565,15 @@ static void handle_vhpi_callback(const vhpiCbDataT *cb_data) old_cb = user_data->cb_hdl; gpi_handle_callback(&user_data->gpi_cb_data.hdl); - if (old_cb == user_data->cb_hdl) - gpi_deregister_callback(&user_data->gpi_cb_data.hdl); + if (old_cb == user_data->cb_hdl) { + + // Don't de-register recurring callbacks - VHPI only seems to allow + // a single registration per recurring callback. For edge events on + // signals etc. we never want to remove. + vhpiStateT cbState = vhpi_get(vhpiStateP, user_data->cb_hdl); + if (vhpiMature == cbState) + gpi_deregister_callback(&user_data->gpi_cb_data.hdl); + } /* A request to delete could have been done * inside gpi_function @@ -583,8 +595,8 @@ static gpi_cb_hdl vhpi_create_cb_handle(void) { gpi_cb_hdl ret = NULL; FENTER - p_vhpi_cb user_data = calloc(1, sizeof(*user_data)); + if (user_data) ret = &user_data->gpi_cb_data; @@ -608,6 +620,16 @@ static void vhpi_destroy_cb_handle(gpi_cb_hdl hdl) FEXIT } + +static void *vhpi_get_callback_data(gpi_sim_hdl gpi_hdl) +{ + FENTER + gpi_cb_hdl gpi_user_data; + gpi_user_data = gpi_container_of(gpi_hdl, gpi_cb_hdl_t, hdl); + return gpi_user_data->gpi_cb_data; +} + + /* Deregister a prior set up callback with the simulator * The handle must have been allocated with gpi_create_cb_handle * This can be called at any point between @@ -885,11 +907,13 @@ static s_gpi_impl_tbl vhpi_table = { .register_nexttime_callback = vhpi_register_nexttime_callback, .register_value_change_callback = vhpi_register_value_change_callback, .register_readonly_callback = vhpi_register_readonly_callback, + .get_callback_data = vhpi_get_callback_data, }; static void register_embed(void) { FENTER + gpi_register_impl(&vhpi_table, 0xfeed); gpi_embed_init_python(); FEXIT } @@ -919,6 +943,7 @@ static void register_initial_callback(void) p_vhpi_cb vhpi_user_data; gpi_cb_hdl gpi_user_data; + sim_init_cb = gpi_create_cb_handle(); gpi_user_data = gpi_container_of(sim_init_cb, gpi_cb_hdl_t, hdl); @@ -936,10 +961,6 @@ static void register_initial_callback(void) cb_data_s.value = NULL; cb_data_s.user_data = (char *)vhpi_user_data; - /* We ignore the return value here as VCS does some silly - * things on comilation that means it tries to run through - * the vlog_startup_routines and so call this routine - */ __vhpi_register_cb(vhpi_user_data, &cb_data_s); FEXIT @@ -968,7 +989,7 @@ static void register_final_callback(void) sim_finish_cb = gpi_create_cb_handle(); - gpi_user_data = gpi_container_of(sim_init_cb, gpi_cb_hdl_t, hdl); + gpi_user_data = gpi_container_of(sim_finish_cb, gpi_cb_hdl_t, hdl); vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); gpi_user_data->gpi_cb_data = NULL; diff --git a/lib/gpi_impl/gpi_vpi.c b/lib/gpi_impl/gpi_vpi.c index cd11193e..e712f67f 100644 --- a/lib/gpi_impl/gpi_vpi.c +++ b/lib/gpi_impl/gpi_vpi.c @@ -240,7 +240,11 @@ static gpi_sim_hdl vpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) obj = vpi_handle_by_name(buff, (vpiHandle)(parent->sim_hdl)); if (!obj) { LOG_DEBUG("VPI: Handle '%s' not found!", name); - check_vpi_error(); + + // NB we deliberately don't dump an error message here because it's + // a valid use case to attempt to grab a signal by name - for example + // optional signals on a bus. + // check_vpi_error(); return NULL; } @@ -749,6 +753,15 @@ static void vpi_destroy_cb_handle(gpi_cb_hdl hdl) FEXIT } +static void *vpi_get_callback_data(gpi_sim_hdl gpi_hdl) +{ + FENTER + gpi_cb_hdl gpi_user_data; + gpi_user_data = gpi_container_of(gpi_hdl, gpi_cb_hdl_t, hdl); + return gpi_user_data->gpi_cb_data; +} + + // If the Pything world wants things to shut down then unregister // the callback for end of sim static void vpi_sim_end(void) @@ -779,6 +792,7 @@ static s_gpi_impl_tbl vpi_table = { .register_nexttime_callback = vpi_register_nexttime_callback, .register_value_change_callback = vpi_register_value_change_callback, .register_readonly_callback = vpi_register_readonly_callback, + .get_callback_data = vpi_get_callback_data, }; static void register_embed(void) diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 122b44fb..b21940b0 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -115,11 +115,16 @@ int handle_gpi_callback(void *user_data) // Free up our mess Py_DECREF(pValue); - Py_DECREF(callback_data_p->function); - Py_DECREF(callback_data_p->args); - // Free the callback data - free(callback_data_p); + // Callbacks may have been re-enabled + if (callback_data_p->id_value == COCOTB_INACTIVE_ID) { + Py_DECREF(callback_data_p->function); + Py_DECREF(callback_data_p->args); + + // Free the callback data + free(callback_data_p); + } + DROP_GIL(gstate); @@ -756,7 +761,36 @@ static PyObject *free_handle(PyObject *self, PyObject *args) static PyObject *stop_simulator(PyObject *self, PyObject *args) { gpi_sim_end(); - return Py_BuildValue("s", "OK!"); + return Py_BuildValue("s", "OK!"); +} + +static PyObject *reenable_callback(PyObject *self, PyObject *args) +{ + gpi_sim_hdl hdl; + p_callback_data callback_data_p; + PyObject *pSihHdl; + PyObject *value; + + FENTER + + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + pSihHdl = PyTuple_GetItem(args, 0); + hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(pSihHdl); + callback_data_p = (p_callback_data)gpi_get_callback_data(hdl); + +// printf("Reenable callback, previous id_value: %08x\n", callback_data_p->id_value); + + callback_data_p->id_value = COCOTB_ACTIVE_ID; +// printf("Now id_value: %08x\n", callback_data_p->id_value); + + value = Py_BuildValue("s", "OK!"); + + DROP_GIL(gstate); + + FEXIT + return value; } diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 0e42427e..8ac1ec97 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -66,6 +66,7 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args); static PyObject *register_readonly_callback(PyObject *self, PyObject *args); static PyObject *register_nextstep_callback(PyObject *self, PyObject *args); static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args); +static PyObject *reenable_callback(PyObject *self, PyObject *args); static PyObject *create_clock(PyObject *self, PyObject *args); static PyObject *stop_clock(PyObject *self, PyObject *args); static PyObject *stop_simulator(PyObject *self, PyObject *args); @@ -93,6 +94,7 @@ static PyMethodDef SimulatorMethods[] = { {"register_readonly_callback", register_readonly_callback, METH_VARARGS, "Register a callback for readonly section"}, {"register_nextstep_callback", register_nextstep_callback, METH_VARARGS, "Register a cllback for the nextsimtime callback"}, {"register_rwsynch_callback", register_rwsynch_callback, METH_VARARGS, "Register a callback for the readwrite section"}, + {"reenable_callback", reenable_callback, METH_VARARGS, "Re-enable a recurring callback"}, {"stop_simulator", stop_simulator, METH_VARARGS, "Instruct the attached simulator to stop"}, {"iterate", iterate, METH_VARARGS, "Get an iterator handle to loop over all members in an object"}, {"next", next, METH_VARARGS, "Get the next object from the iterator"}, From 2f384d38c4de2bbda0faad82f3d764e4c7f40dfa Mon Sep 17 00:00:00 2001 From: Chiggs Date: Thu, 15 May 2014 22:03:08 +0100 Subject: [PATCH 0518/2656] Update GHDL makefile (still broken) --- makefiles/simulators/Makefile.ghdl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.ghdl b/makefiles/simulators/Makefile.ghdl index e19feff7..c1fb5fe6 100644 --- a/makefiles/simulators/Makefile.ghdl +++ b/makefiles/simulators/Makefile.ghdl @@ -33,7 +33,7 @@ analyse: $(VHDL_SOURCES) $(SIM_BUILD) cd $(SIM_BUILD) && ghdl -a $(VHDL_SOURCES) results.xml: analyse $(COCOTB_LIBS) - cd $(SIM_BUILD) && LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) ghdl -e -Wl,-L$(LIB_DIR) -Wl,-lgpivhpi $(TOPLEVEL) + cd $(SIM_BUILD) && LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) ghdl -e -Wl,-L$(LIB_DIR) -Wl,-lgpi $(TOPLEVEL) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ ./$(SIM_BUILD)/$(TOPLEVEL) From 21a070f4bbd518a6b72fb043fe51aa60bd1c0c37 Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 4 Jun 2014 17:46:29 +0100 Subject: [PATCH 0519/2656] Fixes #132 - Break reference tree so GC can cleanup coros --- cocotb/scheduler.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 09b3117d..cdd403d5 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -367,6 +367,8 @@ def unschedule(self, coro): if coro._join in self._trigger2coros: self._pending_triggers.append(coro._join) + # Remove references to allow GC to clean up + del coro._join def save_write(self, handle, value): self._writes[handle] = value From a44c48f43114de3f2cc229a229d761d860627d8c Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 7 Jun 2014 14:45:05 +0100 Subject: [PATCH 0520/2656] Issue #133: Correct call to non-existant function gpi_destroy_cb_handle --- lib/gpi_impl/gpi_vhpi.c | 5 +++-- lib/gpi_impl/gpi_vpi.c | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/gpi_impl/gpi_vhpi.c b/lib/gpi_impl/gpi_vhpi.c index 8827e607..ebc56807 100644 --- a/lib/gpi_impl/gpi_vhpi.c +++ b/lib/gpi_impl/gpi_vhpi.c @@ -60,6 +60,7 @@ typedef struct t_vhpi_cb { // Forward declarations static int vhpi_deregister_callback(gpi_sim_hdl gpi_hdl); +static void vhpi_destroy_cb_handle(gpi_cb_hdl hdl); static const char * vhpi_reason_to_string(int reason) { @@ -579,7 +580,7 @@ static void handle_vhpi_callback(const vhpiCbDataT *cb_data) * inside gpi_function */ if (user_data->state == VHPI_DELETE) - gpi_destroy_cb_handle(&user_data->gpi_cb_data); + vhpi_destroy_cb_handle(&user_data->gpi_cb_data); else user_data->state = VHPI_POST_CALL; @@ -633,7 +634,7 @@ static void *vhpi_get_callback_data(gpi_sim_hdl gpi_hdl) /* Deregister a prior set up callback with the simulator * The handle must have been allocated with gpi_create_cb_handle * This can be called at any point between - * gpi_create_cb_handle and gpi_destroy_cb_handle + * gpi_create_cb_handle and gpi_free_cb_handle */ static int vhpi_deregister_callback(gpi_sim_hdl gpi_hdl) { diff --git a/lib/gpi_impl/gpi_vpi.c b/lib/gpi_impl/gpi_vpi.c index e712f67f..c6cfa54a 100644 --- a/lib/gpi_impl/gpi_vpi.c +++ b/lib/gpi_impl/gpi_vpi.c @@ -55,6 +55,7 @@ typedef struct t_vpi_cb { // Forward declarations static int vpi_deregister_callback(gpi_sim_hdl gpi_hdl); +static void vpi_destroy_cb_handle(gpi_cb_hdl hdl); // Add to this over time static const char * vpi_reason_to_string(int reason) @@ -453,7 +454,7 @@ static int32_t handle_vpi_callback(p_cb_data cb_data) * inside gpi_function */ if (user_data->state == VPI_DELETE) - gpi_destroy_cb_handle(&user_data->gpi_cb_data); + vpi_destroy_cb_handle(&user_data->gpi_cb_data); else user_data->state = VPI_POST_CALL; @@ -465,7 +466,7 @@ static int32_t handle_vpi_callback(p_cb_data cb_data) /* Deregister a prior set up callback with the simulator * The handle must have been allocated with gpi_create_cb_handle * This can be called at any point between - * gpi_create_cb_handle and gpi_destroy_cb_handle + * gpi_create_cb_handle and gpi_free_cb_handle */ static int vpi_deregister_callback(gpi_sim_hdl gpi_hdl) { @@ -1016,4 +1017,4 @@ void vlog_startup_routines_bootstrap(void) { routine = vlog_startup_routines[++i]) { routine(); } -} \ No newline at end of file +} From 1fc5cc679d3539b009a7b0dc736d299d4b3063d6 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sat, 7 Jun 2014 18:33:08 +0100 Subject: [PATCH 0521/2656] Issues#133: Call into the upper layer first --- include/gpi.h | 2 -- lib/gpi_impl/gpi_vhpi.c | 2 +- lib/gpi_impl/gpi_vpi.c | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/include/gpi.h b/include/gpi.h index bd9415d4..5691d1e8 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -175,8 +175,6 @@ int gpi_register_readwrite_callback (gpi_sim_hdl, int (*gpi_functio // Calling convention is that 0 = success and negative numbers a failure // For implementers of GPI the provided macro GPI_RET(x) is provided -//gpi_sim_hdl gpi_create_cb_handle(void); -//void gpi_destroy_cb_handle(gpi_sim_hdl gpi_hdl); gpi_sim_hdl gpi_create_cb_handle(void); void gpi_free_cb_handle(gpi_sim_hdl gpi_hdl); diff --git a/lib/gpi_impl/gpi_vhpi.c b/lib/gpi_impl/gpi_vhpi.c index ebc56807..40d9dbdd 100644 --- a/lib/gpi_impl/gpi_vhpi.c +++ b/lib/gpi_impl/gpi_vhpi.c @@ -580,7 +580,7 @@ static void handle_vhpi_callback(const vhpiCbDataT *cb_data) * inside gpi_function */ if (user_data->state == VHPI_DELETE) - vhpi_destroy_cb_handle(&user_data->gpi_cb_data); + gpi_free_cb_handle(&user_data->gpi_cb_data.hdl); else user_data->state = VHPI_POST_CALL; diff --git a/lib/gpi_impl/gpi_vpi.c b/lib/gpi_impl/gpi_vpi.c index c6cfa54a..06600e23 100644 --- a/lib/gpi_impl/gpi_vpi.c +++ b/lib/gpi_impl/gpi_vpi.c @@ -454,7 +454,7 @@ static int32_t handle_vpi_callback(p_cb_data cb_data) * inside gpi_function */ if (user_data->state == VPI_DELETE) - vpi_destroy_cb_handle(&user_data->gpi_cb_data); + gpi_free_cb_handle(&user_data->gpi_cb_data.hdl); else user_data->state = VPI_POST_CALL; From 735915562d6e1afce059c8d89bf6a1c7f9a4f725 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 9 Jun 2014 08:57:13 +0100 Subject: [PATCH 0522/2656] Fix for issue #128 thanks to @nicklfitz --- makefiles/Makefile.pylib | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib index 2dd1bf4e..5503f9c1 100644 --- a/makefiles/Makefile.pylib +++ b/makefiles/Makefile.pylib @@ -42,9 +42,8 @@ endif # We might work with other Python versions PYTHON_VERSION?=$(shell python -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_version()') -PYTHON_LIBDIR:=$(shell python -c 'import os.path,distutils.sysconfig;print distutils.sysconfig.get_python_lib(standard_lib=$(STD_LIB)).rstrip("site-packages").rstrip("/")') - -PYTHON_DYNLIBDIR:=$(PYTHON_LIBDIR)/lib-dynload +PYTHON_LIBDIR:=$(shell python -c 'from distutils import sysconfig; print sysconfig.get_config_var("LIBDIR")') +PYTHON_DYNLIBDIR:=$(shell python -c 'from distutils import sysconfig; print sysconfig.get_config_var("DESTSHARED")') # Since we don't know which modules we might need or whether the simulator # we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports From 89294e77c0e7649c991eb9b2ea4ece533b512a48 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 9 Jun 2014 16:11:25 +0100 Subject: [PATCH 0523/2656] Add optional support for resets to BusMonitor --- cocotb/monitors/__init__.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index c3c0025c..00691526 100644 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -149,13 +149,26 @@ class BusMonitor(Monitor): _signals = [] _optional_signals = [] - def __init__(self, entity, name, clock, callback=None, event=None): + def __init__(self, entity, name, clock, reset=None, reset_n=None, callback=None, event=None): self.log = SimLog("cocotb.%s.%s" % (entity.name, name)) self.entity = entity self.name = name self.clock = clock self.bus = Bus(self.entity, self.name, self._signals, optional_signals=self._optional_signals) + self._reset = reset + self._reset_n = reset_n Monitor.__init__(self, callback=callback, event=event) + + + + @property + def in_reset(self): + if self._reset_n is not None: + return not bool(self._reset_n.value.integer) + if self._reset is not None: + return bool(self._reset_n.value.integer) + return False + def __str__(self): return "%s(%s)" % (self.__class__.__name__, self.name) From 22034472160a38d5610aa79411f4b0c774c4bc20 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 9 Jun 2014 16:11:40 +0100 Subject: [PATCH 0524/2656] Add error for AvalonST transfers outside of packets, support for reset --- cocotb/monitors/avalon.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index fdbfb7cf..dbd02977 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -95,6 +95,7 @@ def _monitor_recv(self): clkedge = RisingEdge(self.clock) rdonly = ReadOnly() pkt = "" + in_pkt = False def valid(): if hasattr(self.bus, 'ready'): @@ -105,6 +106,9 @@ def valid(): yield clkedge yield rdonly + if self.in_reset: + continue + if valid(): if self.bus.startofpacket.value: if pkt: @@ -112,6 +116,10 @@ def valid(): "Duplicate start-of-packet received on %s" % ( str(self.bus.startofpacket))) pkt = "" + in_pkt = True + + if not in_pkt: + raise AvalonProtocolError("Data transfer outside of packet") vec = self.bus.data.value vec.big_endian = self.config['firstSymbolInHighOrderBits'] @@ -125,3 +133,4 @@ def valid(): self.log.debug(hexdump(str((pkt)))) self._recv(pkt) pkt = "" + in_pkt = False From c439cf0a86d0b192f5d369c65e3390ac15ce41bb Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 25 Jun 2014 09:35:04 +0100 Subject: [PATCH 0525/2656] Universal and VPI compliant access to structure members --- include/vpi_user.h | 3 +++ lib/gpi_impl/gpi_vpi.c | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/include/vpi_user.h b/include/vpi_user.h index 56066f3d..a89f32b3 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -44,12 +44,14 @@ typedef uint32_t *vpiHandle; #define vpiNet 36 /* scalar or vector net */ #define vpiModule 32 /* module instance */ +#define vpiStructVar 618 #define vpiStop 66 /* execute simulator's $stop */ #define vpiFinish 67 /* execute simulator's $finish */ #define vpiReset 68 /* execute simulator's $reset */ #define vpiType 1 /* type of object */ +#define vpiName 2 /* local name of object */ #define vpiFullName 3 /* full hierarchical name */ #define vpiNoDelay 1 @@ -183,6 +185,7 @@ typedef struct t_cb_data /* Object Types */ #define vpiPort 44 +#define vpiMember 742 /* error severity levels */ #define vpiNotice 1 diff --git a/lib/gpi_impl/gpi_vpi.c b/lib/gpi_impl/gpi_vpi.c index 06600e23..28b58211 100644 --- a/lib/gpi_impl/gpi_vpi.c +++ b/lib/gpi_impl/gpi_vpi.c @@ -195,7 +195,7 @@ static gpi_sim_hdl vpi_get_root_handle(const char* name) LOG_WARN("VPI: Attempting to free root iterator failed!"); check_vpi_error(); } - + rv = gpi_create_handle(); rv->sim_hdl = root; @@ -221,13 +221,47 @@ static gpi_sim_hdl vpi_get_root_handle(const char* name) } +/** + * @brief Get a handle to an object under the scope of parent + * + * @param name of the object to find + * @param parent handle to parent object defining the scope to search + * + * @return gpi_sim_hdl for the new object or NULL if object not found + */ static gpi_sim_hdl vpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) { FENTER gpi_sim_hdl rv; vpiHandle obj; + vpiHandle iterator; int len; char *buff; + + // Structures aren't technically a scope, according to the LRM. If parent + // is a structure then we have to iterate over the members comparing names + if (vpiStructVar == vpi_get(vpiType, (vpiHandle)(parent->sim_hdl))) { + + iterator = vpi_iterate(vpiMember, (vpiHandle)(parent->sim_hdl)); + + for (obj = vpi_scan(iterator); obj != NULL; obj = vpi_scan(iterator)) { + + if (!strcmp(name, strrchr(vpi_get_str(vpiName, obj), 46) + 1)) + break; + } + + if (!obj) + return NULL; + + // Need to free the iterator if it didn't return NULL + if (!vpi_free_object(iterator)) { + LOG_WARN("VPI: Attempting to free root iterator failed!"); + check_vpi_error(); + } + + goto success; + } + if (name) len = strlen(name) + 1; @@ -246,11 +280,13 @@ static gpi_sim_hdl vpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) // a valid use case to attempt to grab a signal by name - for example // optional signals on a bus. // check_vpi_error(); + free(buff); return NULL; } free(buff); +success: rv = gpi_create_handle(); rv->sim_hdl = obj; From 53f78b9c21d68894071d6481c3475e470a390b44 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 6 Jul 2014 20:16:44 +0100 Subject: [PATCH 0526/2656] Add detail about GUI variable --- documentation/source/building.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/documentation/source/building.rst b/documentation/source/building.rst index aacd215c..5121a1be 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -25,6 +25,13 @@ Typically the makefiles provided with Cocotb for various simulators use a separa Make Variables -------------- +GUI +~~~ + +Set this to 1 to enable the GUI mode in the simulator (if supported). + + + SIM ~~~ From e9ff18f5d0fb7efd6e1f0eb0d435028651e758dc Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 8 Jul 2014 09:15:22 +0100 Subject: [PATCH 0527/2656] Updates for issue #112 and issue #114: 32-bit Python support Workaround for building against 32-bit Python on 64-bit systems. Unfortunately on Linux it's only possible with a hack? --- makefiles/Makefile.pylib | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib index 5503f9c1..8dc6a1c3 100644 --- a/makefiles/Makefile.pylib +++ b/makefiles/Makefile.pylib @@ -34,17 +34,18 @@ PYTHON_PREFIX = $(shell python-config --prefix) NATIVE_ARCH=$(shell uname -m) ARCH?=$(NATIVE_ARCH) -ifeq ($(NATIVE_ARCH),$(ARCH)) - STD_LIB=True -else - STD_LIB=False -endif - # We might work with other Python versions PYTHON_VERSION?=$(shell python -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_version()') PYTHON_LIBDIR:=$(shell python -c 'from distutils import sysconfig; print sysconfig.get_config_var("LIBDIR")') PYTHON_DYNLIBDIR:=$(shell python -c 'from distutils import sysconfig; print sysconfig.get_config_var("DESTSHARED")') +# Nasty hack but there's no way in Python properly get the directories for 32-bit Python on a 64-bit system +# TODO: Under OSX we can use "export VERSIONER_PYTHON_PREFER_32_BIT=yes", no Linux equivalent though +ifneq ($(NATIVE_ARCH),$(ARCH)) + PYTHON_LIBDIR:=$(subst 64,,$(PYTHON_LIBDIR)) + PYTHON_DYNLIBDIR:=$(subst 64,,$(PYTHON_DYNLIBDIR)) +endif + # Since we don't know which modules we might need or whether the simulator # we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports # is we simply link against all dynamic libraries in the Python installation From a2fc970f814284ebb551f39edd0573c83f307943 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 8 Jul 2014 09:17:35 +0100 Subject: [PATCH 0528/2656] Cleanup: Tidy up error printing mechanism in scoreboard --- cocotb/scoreboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index 5e21bd5f..a6d1e8c5 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -139,7 +139,7 @@ def check_received_transaction(transaction): try: for word in transaction: self.log.info(str(word)) except: pass - log.warning(hexdiffs(exp, transaction)) + log.warning("Difference:\n%s" % hexdiffs(exp, transaction)) if self._imm: raise TestFailure("Received transaction differed from expected transaction") else: # Don't want to fail the test if we're passed something without __len__ From ab37846f1fcdfe6a8c9c54e8460149a2a6395b02 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Tue, 8 Jul 2014 18:30:36 +0100 Subject: [PATCH 0529/2656] Correct issue with Scheduler - could get mode wrong if Python events fire --- cocotb/scheduler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index cdd403d5..31ba20da 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -252,7 +252,8 @@ def react(self, trigger, depth=0): if trigger is self._readonly: self._mode = Scheduler._MODE_READONLY - else: + # Only GPI triggers affect the simulator scheduling mode + elif isinstance(trigger, GPITrigger): self._mode = Scheduler._MODE_NORMAL # We're the only source of ReadWrite triggers which are only used for From 642c28f84c801979043d005824b6623980dad4cc Mon Sep 17 00:00:00 2001 From: Chiggs Date: Tue, 8 Jul 2014 18:31:29 +0100 Subject: [PATCH 0530/2656] Correct bug in example causing malformed Avalon transactions --- examples/ping_tun_tap/hdl/icmp_reply.sv | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/ping_tun_tap/hdl/icmp_reply.sv b/examples/ping_tun_tap/hdl/icmp_reply.sv index a1b8eb51..124fc3b9 100644 --- a/examples/ping_tun_tap/hdl/icmp_reply.sv +++ b/examples/ping_tun_tap/hdl/icmp_reply.sv @@ -125,7 +125,9 @@ always @(posedge clk or negedge reset_n) begin if (stream_out_ready) begin tx_word_ptr <= tx_word_ptr + 1; - stream_out_startofpacket <= 1'b0; + + if (tx_word_ptr) + stream_out_startofpacket<= 1'b0; if (tx_word_ptr == rx_word_ptr - 1) begin stream_out_empty <= empty_saved; From 3a2d2d195d3a8dd5cbbdaae9f6a91ee55c228ea0 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Thu, 10 Jul 2014 09:03:07 +0100 Subject: [PATCH 0531/2656] Issue #134: Provide int() access to handles --- cocotb/handle.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cocotb/handle.py b/cocotb/handle.py index 1611c1a5..1d5fdb3f 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -214,3 +214,8 @@ def __del__(self): """Free handle from gpi that was allocated on construction""" if self._handle is not None: simulator.free_handle(self._handle) + + def __int__(self): + return int(self.value) + + From e9dd0c2810d658ec05fed0afac838184dea3786e Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Sun, 13 Jul 2014 18:35:20 +0100 Subject: [PATCH 0532/2656] Allow vhpiLogicVal and vhpiLogicVecVal as aliases of vhpiEnumVal and vhpiEnumVecVal According to the VHPI spec, signals with type std_logic, std_ulogic, or bit, and arrays of those types should use vhpiLogicVal or vhpiLogicVecVal as the native format. --- lib/gpi_impl/gpi_vhpi.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/gpi_impl/gpi_vhpi.c b/lib/gpi_impl/gpi_vhpi.c index 40d9dbdd..43f18b86 100644 --- a/lib/gpi_impl/gpi_vhpi.c +++ b/lib/gpi_impl/gpi_vhpi.c @@ -403,12 +403,14 @@ static void vhpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) check_vhpi_error(); switch (value_s.format) { - case vhpiEnumVal: { + case vhpiEnumVal: + case vhpiLogicVal: { value_s.value.enumv = value ? vhpi1 : vhpi0; break; } - case vhpiEnumVecVal: { + case vhpiEnumVecVal: + case vhpiLogicVecVal: { size = vhpi_get(vhpiSizeP, (vhpiHandleT)(gpi_hdl->sim_hdl)); value_s.bufSize = size*sizeof(vhpiEnumT); value_s.value.enumvs = (vhpiEnumT *)malloc(size*sizeof(vhpiEnumT)); @@ -454,12 +456,14 @@ static void vhpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) check_vhpi_error(); switch (value_s.format) { - case vhpiEnumVal: { + case vhpiEnumVal: + case vhpiLogicVal: { value_s.value.enumv = chr2vhpi(*str); break; } - case vhpiEnumVecVal: { + case vhpiEnumVecVal: + case vhpiLogicVecVal: { len = strlen(str); size = vhpi_get(vhpiSizeP, (vhpiHandleT)(gpi_hdl->sim_hdl)); value_s.bufSize = size*sizeof(vhpiEnumT); From ecb6284e955b8348aa782fbda8ae514957803de5 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Tue, 15 Jul 2014 07:28:31 +0100 Subject: [PATCH 0533/2656] Fix read of uninitialised variables in embed_sim_init If get_module_ref fails (e.g. because it can't find the cocotb module) then cocotb_module and arg_dict may contain garbage values after the jump to cleanup which potentially calls Py_DECREF on them. --- lib/embed/gpi_embed.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 086eeaa6..15f12af2 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -118,6 +118,8 @@ void embed_sim_init(gpi_sim_info_t *info) PyObject *simlog_class, *simlog_obj, *simlog_args, *simlog_func; PyObject *argv_list, *argc, *arg_dict, *arg_value; + cocotb_module = NULL; + arg_dict = NULL; //Ensure that the current thread is ready to callthe Python C API PyGILState_STATE gstate = PyGILState_Ensure(); From 369bb8b91fb8d6cebcdf2d28f607c76894ae649c Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Tue, 15 Jul 2014 07:33:44 +0100 Subject: [PATCH 0534/2656] Fix crash in embed_sim_event if embed_sim_init fails If embed_sim_init fails then the pEventFn global will be NULL. However, when an "end of simulation" event occurs embed_sim_event will still dereference this when it calls PyObject_Call. Added a NULL check around this function. --- lib/embed/gpi_embed.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 15f12af2..74850cce 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -239,16 +239,18 @@ void embed_sim_event(gpi_event_t level, const char *msg) FENTER /* Indicate to the upper layer a sim event occoured */ - PyGILState_STATE gstate; - gstate = PyGILState_Ensure(); + if (pEventFn) { + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); - PyObject *fArgs = PyTuple_New(2); - PyTuple_SetItem(fArgs, 0, PyInt_FromLong(level)); - PyTuple_SetItem(fArgs, 1, PyString_FromString(msg)); - PyObject *pValue = PyObject_Call(pEventFn, fArgs, NULL); + PyObject *fArgs = PyTuple_New(2); + PyTuple_SetItem(fArgs, 0, PyInt_FromLong(level)); + PyTuple_SetItem(fArgs, 1, PyString_FromString(msg)); + PyObject *pValue = PyObject_Call(pEventFn, fArgs, NULL); - Py_DECREF(fArgs); - PyGILState_Release(gstate); + Py_DECREF(fArgs); + PyGILState_Release(gstate); + } FEXIT } From 736c1057b0b1b8c08d191f3347d3d8689ede973d Mon Sep 17 00:00:00 2001 From: Chiggs Date: Thu, 17 Jul 2014 19:14:39 +0100 Subject: [PATCH 0535/2656] Cleanup: Remove unused Makefile and vhpi_shim directory --- lib/vhpi_shim/Makefile | 45 ------------------------------------------ 1 file changed, 45 deletions(-) delete mode 100644 lib/vhpi_shim/Makefile diff --git a/lib/vhpi_shim/Makefile b/lib/vhpi_shim/Makefile deleted file mode 100644 index 8ceefb22..00000000 --- a/lib/vhpi_shim/Makefile +++ /dev/null @@ -1,45 +0,0 @@ -############################################################################### -# Copyright (c) 2014 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -include $(SIM_ROOT)/makefiles/Makefile.inc - -INCLUDES += -GCC_ARGS += -DVHPI_CHECKING -LIBS := -lgpilog -lcocotb $(EXTRA_LIBS) -LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) -LIB_NAME := libgpi.so - -SRCS := gpi_vhpi.c - -all: $(LIB_DIR)/$(LIB_NAME) - -clean: - -@rm -rf $(LIB_DIR)/gpivpi.vpl - -@rm -rf $(LIB_DIR)/$(LIB_NAME) - -include $(SIM_ROOT)/makefiles/Makefile.rules From 6cf24c3bbafc06128686affc0389f2f497b10021 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Sat, 19 Jul 2014 09:18:47 +0100 Subject: [PATCH 0536/2656] Add a makefile for nvc --- makefiles/simulators/Makefile.nvc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 makefiles/simulators/Makefile.nvc diff --git a/makefiles/simulators/Makefile.nvc b/makefiles/simulators/Makefile.nvc new file mode 100644 index 00000000..7c5f3808 --- /dev/null +++ b/makefiles/simulators/Makefile.nvc @@ -0,0 +1,17 @@ +# -*- mode: makefile -*- + +.PHONY: analyse + +# Compilation phase +analyse: $(VHDL_SOURCES) $(SIM_BUILD) + cd $(SIM_BUILD) && nvc -a $(VHDL_SOURCES) + +results.xml: analyse $(COCOTB_LIBS) + cd $(SIM_BUILD) && LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) nvc -e $(TOPLEVEL) + cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + LD_LIBRARY_PATH=$(LIB_DIR) \ + nvc -r --load $(LIB_DIR)/libgpi.so $(TOPLEVEL) + +clean:: + -@rm -rf $(SIM_BUILD) From 70032b77e333b6b344813b05fb76b2a25699e731 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 20 Jul 2014 08:41:33 +0100 Subject: [PATCH 0537/2656] Issue #141: Initial work on FLI layer (unfinished) --- lib/gpi_impl/gpi_fli.c | 424 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 424 insertions(+) create mode 100644 lib/gpi_impl/gpi_fli.c diff --git a/lib/gpi_impl/gpi_fli.c b/lib/gpi_impl/gpi_fli.c new file mode 100644 index 00000000..8b2abeba --- /dev/null +++ b/lib/gpi_impl/gpi_fli.c @@ -0,0 +1,424 @@ +/****************************************************************************** +* Copyright (c) 2013 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +#include "gpi_priv.h" +#include + +#define FLII_CHECKING 1 + +static gpi_sim_hdl sim_init_cb; +static gpi_sim_hdl sim_finish_cb; + + +// Handle related functions +/** + * @name Find the root handle + * @brief Find the root handle using a optional name + * + * Get a handle to the root simulator object. This is usually the toplevel. + * + * If no name is defined, we return the first root instance. + * + * If name is provided, we check the name against the available objects until + * we find a match. If no match is found we return NULL + */ +static gpi_sim_hdl fli_get_root_handle(const char* name) +{ + FENTER + mtiRegionIdT root; + + for (root = mti_GetTopRegion(); root != NULL; root = mti_NextRegion(root)) { + if (name == NULL || !strcmp(name, mti_GetRegionName(root))) + break; + } + + if (!root) { + goto error; + } + + rv = gpi_create_handle(); + rv->sim_hdl = (void *)root; + + FEXIT + return rv; + + error: + + LOG_CRITICAL("FPI: Couldn't find root handle %s", name); + + for (root = mti_GetTopRegion(); root != NULL; root = mti_NextRegion(root)) { + + LOG_CRITICAL("FLI: Toplevel instances: %s != %s...", name, mti_GetRegionName(root)); + + if (name == NULL) + break; + } + + FEXIT + return NULL; +} + + +/** + * @brief Get a handle to an object under the scope of parent + * + * @param name of the object to find + * @param parent handle to parent object defining the scope to search + * + * @return gpi_sim_hdl for the new object or NULL if object not found + */ +static gpi_sim_hdl fli_get_handle_by_name(const char *name, gpi_sim_hdl parent) +{ + FENTER + mtiRegionIdT hdl = (mtiRegionIdT)parent->sim_hdl; + mtiRegionIdT result; + char *fullname; + + fullname = (char *)malloc(strlen(mti_GetRegionFullName(root) + strlen(name) + 2); + + if (fullname == NULL) { + LOG_CRITICAL("FLI: Attempting allocate string buffer failed!"); + return NULL; + } + + result = mti_FindRegion(fullname); + if (result) goto success; + result = mti_FindPort(fullname); + if (result) goto success; + result = mti_FindSignal(fullname); + if (result) goto success; + result = mti_FindVar(fullname); + if (result) goto success; + +error: + LOG_DEBUG("FLII: Handle '%s' not found!", name); + + // NB we deliberately don't dump an error message here because it's + // a valid use case to attempt to grab a signal by name - for example + // optional signals on a bus. + free(buff); + return NULL; + +success: + free(buff); + rv = gpi_create_handle(); + rv->sim_hdl = obj; + + FEXIT + return rv; +} + + +/** + * @brief Get a handle for an object based on its index within a parent + * + * @param parent handle to the parent + * @param indext Index to retrieve + * + * Can be used on bit-vectors to access a specific bit or + * memories to access an address + */ +static gpi_sim_hdl fli_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) +{ + FENTER + LOG_ERROR("FLI: Obtaining a handle by index not supported by FLI?"); + FEXIT + return NULL; +} + + +// Functions for iterating over entries of a handle +// Returns an iterator handle which can then be used in gpi_next calls +// NB May return NULL if no objects of the request type exist +static gpi_iterator_hdl fli_iterate_hdl(uint32_t type, gpi_sim_hdl base) { + FENTER + LOG_ERROR("FLI: Iterating over a handle not implemented yet"); + FEXIT + return NULL; +} + +// Returns NULL when there are no more objects +static gpi_sim_hdl fli_next_hdl(gpi_iterator_hdl iterator) +{ + FENTER + LOG_ERROR("FLI: Iterating over a handle not implemented yet"); + FEXIT + return NULL; +} + +static void fli_get_sim_time(uint32_t *high, uint32_t *low) +{ + *high = mti_NowUpper(); + *low = mti_Now(); +} + +// Value related functions +static void fli_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) +{ + FENTER + LOG_ERROR("Attempt to force signal %s failed (not implemented)", + mti_GetSignalName((mtiSignalIdT)gpi_hdl->sim_hdl)); + FEXIT +} + +static void fli_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) +{ + FENTER + int rc = mti_ForceSignal((mtiSignalIdT)(gpi_hdl->sim_hdl), + str, + (mtiDelayT)-1, // If the delay parameter is negative, then the force is applied immediately. + MTI_FORCE_DEPOSIT, + -1, // cancel_period + -1); // repeat_period + + if (!rc) + LOG_ERROR("Attempt to force signal %s failed", + mti_GetSignalName((mtiSignalIdT)gpi_hdl->sim_hdl)); + FEXIT +} + +static char *fli_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) +{ + FENTER + + switch (mti_GetTypeKind(mti_GetSignalType((mtiSignalIdT)gpi_hdl->sim_hdl))){ + case MTI_TYPE_SCALAR: + case MTI_TYPE_ENUM: + case MTI_TYPE_PHYSICAL: + mti_PrintFormatted( "%d\n", mti_GetSignalValue( sigid ) ); + break; + default: + mti_PrintFormatted( "(Type not supported)\n" ); + break; + } + char *result = gpi_copy_name(value_p->value.str); + FEXIT + return result; +} + +static char *fli_get_signal_name_str(gpi_sim_hdl gpi_hdl) +{ + FENTER + const char *name = mti_GetSignalName((mtiSignalIdT)gpi_hdl->sim_hdl)); + char *result = gpi_copy_name(name); + FEXIT + return result; +} + +static char *fli_get_signal_type_str(gpi_sim_hdl gpi_hdl) +{ + switch (mti_GetTypeKind(mti_GetSignalType((mtiSignalIdT)gpi_hdl->sim_hdl))){ + case MTI_TYPE_SCALAR : return "Scalar"; + case MTI_TYPE_ARRAY : return "Array"; + case MTI_TYPE_RECORD : return "Record"; + case MTI_TYPE_ENUM : return "Enum"; + case MTI_TYPE_INTEGER : return "Integer"; + case MTI_TYPE_PHYSICAL : return "Physical"; + case MTI_TYPE_REAL : return "Real"; + case MTI_TYPE_ACCESS : return "Access"; + case MTI_TYPE_FILE : return "File"; + case MTI_TYPE_TIME : return "Time"; + case MTI_TYPE_C_REAL : return "C Real"; + case MTI_TYPE_C_ENUM : return "C Enum"; + default : return "Unknown!"; + } +} + + +// Callback related functions +static int32_t handle_fli_callback(p_cb_data cb_data) +{ + FENTER + LOG_CRITICAL("FLI: Callbacks not implemented yet"); + FEXIT + return 0; +}; + + +static int fli_deregister_callback(gpi_sim_hdl gpi_hdl) +{ + FENTER + LOG_CRITICAL("FLI: Callbacks not implemented yet"); + FEXIT + return 0; +} + + +/* These functions request a callback to be active with the current + * handle and associated data. A callback handle needs to have been + * allocated with gpi_create_cb_handle first + */ +static int fli_register_value_change_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data, + gpi_sim_hdl gpi_hdl) +{ + FENTER + FEXIT + return 0; +} + +static int fli_register_readonly_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) +{ + FENTER + FEXIT + return 0; +} + +static int fli_register_readwrite_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) +{ + FENTER + FEXIT + return 0; +} + + + +static int fli_register_nexttime_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) +{ + FENTER + FEXIT + return 0; +} + +static int fli_register_timed_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data, + uint64_t time_ps) +{ + FENTER + FEXIT + return 0; +} + + +static s_gpi_impl_tbl fli_table = { + .sim_end = fli_sim_end, + .iterate_handle = fli_iterate_hdl, + .next_handle = fli_next_hdl, + .create_cb_handle = fli_create_cb_handle, + .destroy_cb_handle = fli_destroy_cb_handle, + .deregister_callback = fli_deregister_callback, + .get_root_handle = fli_get_root_handle, + .get_sim_time = fli_get_sim_time, + .get_handle_by_name = fli_get_handle_by_name, + .get_handle_by_index = fli_get_handle_by_index, + .get_signal_name_str = fli_get_signal_name_str, + .get_signal_type_str = fli_get_signal_type_str, + .get_signal_value_binstr = fli_get_signal_value_binstr, + .set_signal_value_int = fli_set_signal_value_int, + .set_signal_value_str = fli_set_signal_value_str, + .register_timed_callback = fli_register_timed_callback, + .register_readwrite_callback = fli_register_readwrite_callback, + .register_nexttime_callback = fli_register_nexttime_callback, + .register_value_change_callback = fli_register_value_change_callback, + .register_readonly_callback = fli_register_readonly_callback, + .get_callback_data = fli_get_callback_data, +}; + +static void register_embed(void) +{ + gpi_register_impl(&fli_table, 0xfe70); + gpi_embed_init_python(); +} + + +// No access to plusargs via FLI? +static int handle_sim_init(void *gpi_cb_data) +{ + FENTER + gpi_sim_info_t sim_info; + sim_info.argc = NULL; + sim_info.argv = NULL; + sim_info.product = mti_GetProductVersion(); + sim_info.version = NULL; + gpi_embed_init(&sim_info); + FEXIT +} + +static void register_initial_callback(void) +{ + FENTER + gpi_cb_hdl gpi_user_data; + sim_init_cb = gpi_create_cb_handle(); + gpi_user_data = gpi_container_of(sim_init_cb, gpi_cb_hdl_t, hdl); + + gpi_user_data->gpi_cb_data = NULL; + gpi_user_data->gpi_function = handle_sim_init; + + mti_AddLoadDoneCB(handle_fli_callback, &gpi_user_data); + FEXIT +} + +static int handle_sim_end(void *gpi_cb_data) +{ + FENTER + if (sim_finish_cb) { + sim_finish_cb = NULL; + /* This means that we have been asked to close */ + gpi_embed_end(); + } /* Other sise we have already been here from the top down so do not need + to inform the upper layers that anything has occoured */ + gpi_free_cb_handle(sim_init_cb); + FEXIT +} + +static void register_final_callback(void) +{ + FENTER + gpi_cb_hdl gpi_user_data; + sim_init_cb = gpi_create_cb_handle(); + gpi_user_data = gpi_container_of(sim_init_cb, gpi_cb_hdl_t, hdl); + + gpi_user_data->gpi_cb_data = NULL; + gpi_user_data->gpi_function = handle_sim_end; + + mti_AddQuitCB(handle_fli_callback, &gpi_user_data); + FEXIT +} + + +// Initialisation needs to be triggered from a foreign architecture in the RTL +// +// ATTRIBUTE foreign OF blah : ARCHITECTURE IS "cocotb_init libgpi.so; parameter"; +void cocotb_init(mtiRegionIdT region, + char *param, + mtiInterfaceListT *generics, + mtiInterfaceListT *ports) +{ + register_embed(); + register_initial_callback(); + register_final_callback(); +} + + From 920848f0a9ca4e3b079a3db3112bc65e7bc99045 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 20 Jul 2014 09:12:39 +0100 Subject: [PATCH 0538/2656] Clean up debug output in scheduler --- cocotb/scheduler.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 31ba20da..7fc5a194 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -234,14 +234,13 @@ def react(self, trigger, depth=0): We find any coroutines that are waiting on the particular trigger and schedule them. """ - if _debug: - trigger.log.debug("Fired!") if _profiling and not depth: _profile.enable() # We can always unprime the trigger that actually fired - if it's # recycled then prime will be called again. - self.log.debug("Unprimed %s" % str(trigger)) + if _debug: + self.log.debug("Trigger fired: %s... unpriming" % str(trigger)) trigger.unprime() if self._mode == Scheduler._MODE_TERM: @@ -313,11 +312,10 @@ def react(self, trigger, depth=0): scheduling = self._trigger2coros.pop(trigger) if _debug: - self.log.debug("%d pending coroutines for event %s\n%s" % ( - len(scheduling), str(trigger), - "\n\t".join([coro.__name__ for coro in scheduling]))) - for coro in scheduling: - coro.log.debug("I was waiting") + debugstr = "\n\t".join([coro.__name__ for coro in scheduling]) + if len(scheduling): debugstr = "\n\t" + debugstr + self.log.debug("%d pending coroutines for event %s%s" % ( + len(scheduling), str(trigger), debugstr)) # If the coroutine was waiting on multiple triggers we may be able # to unprime the other triggers that didn't fire From 44f7ba3c88f63bbfd0e319b49c416c3bcb856c3e Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 23 Jul 2014 18:01:16 +0100 Subject: [PATCH 0539/2656] Issue #142: Convert integers to BinaryValues if too wide for GPI --- cocotb/handle.py | 14 +++++++--- examples/functionality/hdl/sample_module.v | 1 + .../functionality/tests/test_regression.py | 27 ++++++++++++++++++- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 1d5fdb3f..7217126a 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -138,17 +138,23 @@ def setimmediatevalue(self, value): object eg net, signal or variable. We determine the library call to make based on the type of the value + + Assigning integers less than 32-bits is faster """ + if isinstance(value, (int, long)) and value < 0x7fffffff: + simulator.set_signal_val(self._handle, value) + return + if isinstance(value, ctypes.Structure): value = BinaryValue(value=cocotb.utils.pack(value), bits=len(self)) - if isinstance(value, BinaryValue): - simulator.set_signal_val_str(self._handle, value.binstr) elif isinstance(value, (int, long)): - simulator.set_signal_val(self._handle, value) - else: + value = BinaryValue(value=value, bits=len(self), bigEndian=False) + elif not isinstance(value, BinaryValue): self.log.critical("Unsupported type for value assignment: %s (%s)" % (type(value), repr(value))) raise TypeError("Unable to set simulator value with type %s" % (type(value))) + simulator.set_signal_val_str(self._handle, value.binstr) + def setcachedvalue(self, value): """Intercept the store of a value and hold in cache. diff --git a/examples/functionality/hdl/sample_module.v b/examples/functionality/hdl/sample_module.v index 3919714c..4aefad1f 100644 --- a/examples/functionality/hdl/sample_module.v +++ b/examples/functionality/hdl/sample_module.v @@ -35,6 +35,7 @@ module sample_module ( output reg stream_in_ready, input stream_in_valid, input [7:0] stream_in_data, + input [63:0] stream_in_data_wide, input stream_out_ready, output reg [7:0] stream_out_data_comb, diff --git a/examples/functionality/tests/test_regression.py b/examples/functionality/tests/test_regression.py index f7739661..fe701a18 100644 --- a/examples/functionality/tests/test_regression.py +++ b/examples/functionality/tests/test_regression.py @@ -4,6 +4,7 @@ from cocotb.clock import Clock from cocotb.triggers import RisingEdge, Timer, ReadOnly from cocotb.result import TestFailure +from cocotb.binary import BinaryValue @cocotb.coroutine def send_data(dut): @@ -44,4 +45,28 @@ def issue_120_scheduling(dut): dut.stream_in_valid = 0 yield RisingEdge(dut.clk) - + + + +@cocotb.test() +def issue_142_overflow_error(dut): + """Tranparently convert ints too long to pass + through the GPI interface natively into BinaryValues""" + cocotb.fork(Clock(dut.clk, 2500).start()) + + def _compare(value): + if int(dut.stream_in_data_wide.value) != int(value): + raise TestFailure("Expecting 0x%x but got 0x%x on %s" % ( + int(value), int(dut.stream_in_data_wide.value), + str(dut.stream_in_data_wide))) + + # Wider values are transparently converted to BinaryValues + for value in [0, 0x7FFFFFFF, 0x7FFFFFFFFFFF, BinaryValue(0x7FFFFFFFFFFFFF)]: + + dut.stream_in_data_wide <= value + yield RisingEdge(dut.clk) + _compare(value) + dut.stream_in_data_wide = value + yield RisingEdge(dut.clk) + _compare(value) + From 1384285ff0799cde72a264ce22d98803befce03b Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 25 Jul 2014 18:36:56 +0100 Subject: [PATCH 0540/2656] Issue #145: Populate sys.argv to ensure that embedding IPython works --- lib/embed/gpi_embed.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 74850cce..9ac49f9c 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -35,6 +35,7 @@ static PyThreadState *gtstate; static char progname[] = "cocotb"; +static char *argv[] = { progname }; static PyObject *thread_dict; static PyObject *pEventFn; @@ -60,6 +61,7 @@ void embed_init_python(void) Py_SetProgramName(progname); Py_Initialize(); /* Initialize the interpreter */ + PySys_SetArgvEx(1, argv, 0); PyEval_InitThreads(); /* Create (and acquire) the interpreter lock */ /* Swap out and return current thread state and release the GIL */ From 6194aa67ce54ce5c2fb0de58bfb2d75607d7b882 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 3 Aug 2014 13:07:48 +0100 Subject: [PATCH 0541/2656] Cleanup: Enable regression run to work on python 2.6 --- bin/combine_results.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/combine_results.py b/bin/combine_results.py index f61223c2..5c8d511d 100755 --- a/bin/combine_results.py +++ b/bin/combine_results.py @@ -22,7 +22,7 @@ def main(path, output): for fname in find_all("results.xml", path): tree = ET.parse(fname) - for element in tree.iter("testcase"): + for element in tree.getiterator("testcase"): testsuite.append(element) result = ET.Element("testsuites", name="results") From fc51a7588e8a9954a93df02aaf8462820af12cf8 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 14 Aug 2014 09:23:57 +0100 Subject: [PATCH 0542/2656] Issue#148: Cleanup warnings --- include/vpi_user.h | 5 +++++ lib/embed/gpi_embed.c | 6 ++++-- lib/gpi_impl/gpi_common.c | 10 +++++----- lib/gpi_impl/gpi_priv.h | 1 + lib/gpi_impl/gpi_vhpi.c | 12 ++++++------ lib/gpi_impl/gpi_vpi.c | 15 +++++++++------ lib/gpi_log/gpi_logging.c | 5 ++++- lib/simulator/simulatormodule.c | 8 -------- lib/simulator/simulatormodule.h | 2 -- makefiles/Makefile.inc | 2 +- 10 files changed, 35 insertions(+), 31 deletions(-) diff --git a/include/vpi_user.h b/include/vpi_user.h index a89f32b3..5865d201 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -255,6 +255,9 @@ extern vpiHandle vpi_put_value(vpiHandle object, extern void vpi_get_time(vpiHandle object, p_vpi_time time_p); +extern int32_t vpi_get(int property, + vpiHandle ref); + extern int32_t vpi_free_object(vpiHandle object); extern int32_t vpi_control(int32_t operation, ...); @@ -269,6 +272,8 @@ extern int32_t vpi_get_vlog_info(p_vpi_vlog_info info_p); extern vpiHandle vpi_register_systf(p_vpi_systf_data data_p); +extern int32_t vpi_printf(const char *fmt, ...) __attribute__((format (printf,1,2))); + extern void (*vlog_startup_routines[])(void); #ifdef __cplusplus diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 9ac49f9c..e26bbeeb 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -36,7 +36,6 @@ static PyThreadState *gtstate; static char progname[] = "cocotb"; static char *argv[] = { progname }; -static PyObject *thread_dict; static PyObject *pEventFn; @@ -117,7 +116,7 @@ void embed_sim_init(gpi_sim_info_t *info) } PyObject *cocotb_module, *cocotb_init, *cocotb_args, *cocotb_retval; - PyObject *simlog_class, *simlog_obj, *simlog_args, *simlog_func; + PyObject *simlog_obj, *simlog_func; PyObject *argv_list, *argc, *arg_dict, *arg_value; cocotb_module = NULL; @@ -249,6 +248,9 @@ void embed_sim_event(gpi_event_t level, const char *msg) PyTuple_SetItem(fArgs, 0, PyInt_FromLong(level)); PyTuple_SetItem(fArgs, 1, PyString_FromString(msg)); PyObject *pValue = PyObject_Call(pEventFn, fArgs, NULL); + if (!pValue) { + LOG_ERROR("Passing event to upper layer failed"); + } Py_DECREF(fArgs); PyGILState_Release(gstate); diff --git a/lib/gpi_impl/gpi_common.c b/lib/gpi_impl/gpi_common.c index f4975903..63a75e91 100644 --- a/lib/gpi_impl/gpi_common.c +++ b/lib/gpi_impl/gpi_common.c @@ -133,7 +133,7 @@ int gpi_register_timed_callback(gpi_sim_hdl hdl, void *gpi_cb_data, uint64_t time_ps) { set_user_data(hdl, gpi_function, gpi_cb_data); - IMPL_ROOT->register_timed_callback(hdl, gpi_function, gpi_cb_data, time_ps); + return IMPL_ROOT->register_timed_callback(hdl, gpi_function, gpi_cb_data, time_ps); } int gpi_register_value_change_callback(gpi_sim_hdl hdl, @@ -141,7 +141,7 @@ int gpi_register_value_change_callback(gpi_sim_hdl hdl, void *gpi_cb_data, gpi_sim_hdl gpi_hdl) { set_user_data(hdl, gpi_function, gpi_cb_data); - IMPL_ROOT->register_value_change_callback(hdl, gpi_function, gpi_cb_data, gpi_hdl); + return IMPL_ROOT->register_value_change_callback(hdl, gpi_function, gpi_cb_data, gpi_hdl); } int gpi_register_readonly_callback(gpi_sim_hdl hdl, @@ -149,7 +149,7 @@ int gpi_register_readonly_callback(gpi_sim_hdl hdl, void *gpi_cb_data) { set_user_data(hdl, gpi_function, gpi_cb_data); - IMPL_ROOT->register_readonly_callback(hdl, gpi_function, gpi_cb_data); + return IMPL_ROOT->register_readonly_callback(hdl, gpi_function, gpi_cb_data); } int gpi_register_nexttime_callback(gpi_sim_hdl hdl, @@ -157,7 +157,7 @@ int gpi_register_nexttime_callback(gpi_sim_hdl hdl, void *gpi_cb_data) { set_user_data(hdl, gpi_function, gpi_cb_data); - IMPL_ROOT->register_nexttime_callback(hdl, gpi_function, gpi_cb_data); + return IMPL_ROOT->register_nexttime_callback(hdl, gpi_function, gpi_cb_data); } int gpi_register_readwrite_callback(gpi_sim_hdl hdl, @@ -165,7 +165,7 @@ int gpi_register_readwrite_callback(gpi_sim_hdl hdl, void *gpi_cb_data) { set_user_data(hdl, gpi_function, gpi_cb_data); - IMPL_ROOT->register_readwrite_callback(hdl, gpi_function, gpi_cb_data); + return IMPL_ROOT->register_readwrite_callback(hdl, gpi_function, gpi_cb_data); } void gpi_deregister_callback(gpi_sim_hdl hdl) diff --git a/lib/gpi_impl/gpi_priv.h b/lib/gpi_impl/gpi_priv.h index f8235e10..2ce2f2a7 100644 --- a/lib/gpi_impl/gpi_priv.h +++ b/lib/gpi_impl/gpi_priv.h @@ -28,6 +28,7 @@ ******************************************************************************/ #include +#include #define gpi_container_of(_address, _type, _member) \ ((_type *)((uintptr_t)(_address) - \ diff --git a/lib/gpi_impl/gpi_vhpi.c b/lib/gpi_impl/gpi_vhpi.c index 43f18b86..5495393e 100644 --- a/lib/gpi_impl/gpi_vhpi.c +++ b/lib/gpi_impl/gpi_vhpi.c @@ -140,7 +140,7 @@ static int __check_vhpi_error(const char *func, long line) int loglevel; level = vhpi_check_error(&info); if (level == 0) - return; + return 0; switch (level) { case vhpiNote: @@ -342,7 +342,6 @@ static gpi_iterator_hdl vhpi_iterate_hdl(uint32_t type, gpi_sim_hdl base) { static gpi_sim_hdl vhpi_next_hdl(gpi_iterator_hdl iterator) { FENTER - vhpiHandleT result; gpi_sim_hdl rv = gpi_create_handle(); rv->sim_hdl = vhpi_scan((vhpiHandleT) iterator); @@ -391,7 +390,6 @@ static void vhpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) { FENTER vhpiValueT value_s; - vhpiValueT *value_p = &value_s; int size, i; // Determine the type of object, either scalar or vector @@ -443,7 +441,6 @@ static void vhpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) { FENTER vhpiValueT value_s; - vhpiValueT *value_p = &value_s; int len, size, i; const char *ptr; @@ -557,7 +554,6 @@ static char *vhpi_get_signal_type_str(gpi_sim_hdl gpi_hdl) static void handle_vhpi_callback(const vhpiCbDataT *cb_data) { FENTER - int rv = 0; vhpiHandleT old_cb; p_vhpi_cb user_data; @@ -648,7 +644,7 @@ static int vhpi_deregister_callback(gpi_sim_hdl gpi_hdl) int rc = 1; gpi_user_data = gpi_container_of(gpi_hdl, gpi_cb_hdl_t, hdl); - vhpi_user_data = gpi_container_of(gpi_hdl, s_vhpi_cb, gpi_cb_data); + vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); if (vhpi_user_data->cb_hdl != NULL) { rc = vhpi_user_data->vhpi_cleanup(vhpi_user_data); @@ -938,6 +934,8 @@ static int handle_sim_init(void *gpi_cb_data) free(sim_info.version); FEXIT + + return 0; } static void register_initial_callback(void) @@ -982,6 +980,8 @@ static int handle_sim_end(void *gpi_cb_data) to inform the upper layers that anything has occoured */ gpi_free_cb_handle(sim_init_cb); FEXIT + + return 0; } static void register_final_callback(void) diff --git a/lib/gpi_impl/gpi_vpi.c b/lib/gpi_impl/gpi_vpi.c index 28b58211..dc9b9456 100644 --- a/lib/gpi_impl/gpi_vpi.c +++ b/lib/gpi_impl/gpi_vpi.c @@ -92,7 +92,7 @@ static int __check_vpi_error(const char *func, long line) int loglevel; level = vpi_chk_error(&info); if (level == 0) - return; + return 0; switch (level) { case vpiNotice: @@ -173,8 +173,6 @@ static gpi_sim_hdl vpi_get_root_handle(const char* name) vpiHandle iterator; gpi_sim_hdl rv; - const char* found; - // vpi_iterate with a ref of NULL returns the top level module iterator = vpi_iterate(vpiModule, NULL); check_vpi_error(); @@ -342,7 +340,6 @@ static gpi_iterator_hdl vpi_iterate_hdl(uint32_t type, gpi_sim_hdl base) { static gpi_sim_hdl vpi_next_hdl(gpi_iterator_hdl iterator) { FENTER - vpiHandle result; gpi_sim_hdl rv = gpi_create_handle(); rv->sim_hdl = vpi_scan((vpiHandle) iterator); @@ -468,7 +465,7 @@ static int32_t handle_vpi_callback(p_cb_data cb_data) { FENTER int rv = 0; - vpiHandle old_cb; + //vpiHandle old_cb; p_vpi_cb user_data; user_data = (p_vpi_cb)cb_data->user_data; @@ -477,7 +474,7 @@ static int32_t handle_vpi_callback(p_cb_data cb_data) LOG_CRITICAL("VPI: Callback data corrupted"); user_data->state = VPI_PRE_CALL; - old_cb = user_data->cb_hdl; + //old_cb = user_data->cb_hdl; gpi_handle_callback(&user_data->gpi_cb_data.hdl); // HACK: Investigate further - this breaks modelsim @@ -855,6 +852,8 @@ static int handle_sim_init(void *gpi_cb_data) gpi_embed_init(&sim_info); FEXIT + + return 0; } static void register_initial_callback(void) @@ -902,6 +901,8 @@ static int handle_sim_end(void *gpi_cb_data) to inform the upper layers that anything has occoured */ gpi_free_cb_handle(sim_init_cb); FEXIT + + return 0; } static void register_final_callback(void) @@ -963,7 +964,9 @@ static int system_function_compiletf(char *userdata) tfarg_type); vpi_free_object(arg_iterator); vpi_control(vpiFinish, 1); + return -1; } + return 0; } static int systf_info_level = GPIInfo; diff --git a/lib/gpi_log/gpi_logging.c b/lib/gpi_log/gpi_logging.c index 9b278c86..09da81b0 100644 --- a/lib/gpi_log/gpi_logging.c +++ b/lib/gpi_log/gpi_logging.c @@ -103,13 +103,16 @@ void gpi_log(const char *name, long level, const char *pathname, const char *fun */ va_list ap; int n; - int curr_level; if (!pLogHandler) { if (level >= 20) { va_start(ap, msg); n = vsnprintf(log_buff, LOG_SIZE, msg, ap); va_end(ap); + + if (0 > n) { + fprintf(stderr, "Log message construction failed\n"); + } fprintf(stdout, " -.--ns "); fprintf(stdout, "%-8s", log_level(level)); diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index b21940b0..2e5873ee 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -160,7 +160,6 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) PyObject *function; PyObject *handle; gpi_sim_hdl hdl; - char *result; int ret; PyGILState_STATE gstate; @@ -222,7 +221,6 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) PyObject *function; PyObject *handle; gpi_sim_hdl hdl; - char *result; int ret; PyGILState_STATE gstate; @@ -282,9 +280,6 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; - uint64_t time_ps; - char *result; - PyObject *retstr; PyObject *handle; gpi_sim_hdl hdl; int ret; @@ -423,10 +418,7 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; - uint64_t time_ps; gpi_sim_hdl sig_hdl; - char *result; - PyObject *retstr; PyObject *handle; gpi_sim_hdl hdl; int ret; diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 8ac1ec97..5b4c3f61 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -67,8 +67,6 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args); static PyObject *register_nextstep_callback(PyObject *self, PyObject *args); static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args); static PyObject *reenable_callback(PyObject *self, PyObject *args); -static PyObject *create_clock(PyObject *self, PyObject *args); -static PyObject *stop_clock(PyObject *self, PyObject *args); static PyObject *stop_simulator(PyObject *self, PyObject *args); static PyObject *iterate(PyObject *self, PyObject *args); diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 7c2a9b19..c984a3d4 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -59,7 +59,7 @@ ifeq ($(OS),Darwin) GCC_ARGS := -g -DDEBUG -fpic else GCC_ARGS := -Werror -Wcast-qual -Wcast-align -Wwrite-strings \ - -Waggregate-return -Wstrict-prototypes \ + -Waggregate-return -Wstrict-prototypes -Wall \ -Wno-unused-parameter -fno-common -g -DDEBUG -fpic ifeq ($(ARCH),i686) GCC_ARGS += -m32 From 21708523375c72c8bdaea9bc8f343fda6c2f8862 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 14 Aug 2014 14:39:38 +0100 Subject: [PATCH 0543/2656] Issue #128: Correct warnings from other gcc versions --- lib/embed/gpi_embed.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index e26bbeeb..fd9f4dcf 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -228,10 +228,12 @@ void embed_sim_init(gpi_sim_info_t *info) FEXIT cleanup: - if (cocotb_module) + if (cocotb_module) { Py_DECREF(cocotb_module); - if (arg_dict) + } + if (arg_dict) { Py_DECREF(arg_dict); + } PyGILState_Release(gstate); } From ee6e54c881bd033d34fed2e1812489a77ef28cd1 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 26 Aug 2014 17:50:43 +0100 Subject: [PATCH 0544/2656] Issue#146: Only link against libpython not the dyn-libs too --- makefiles/Makefile.pylib | 4 ---- 1 file changed, 4 deletions(-) diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib index 8dc6a1c3..651f90e2 100644 --- a/makefiles/Makefile.pylib +++ b/makefiles/Makefile.pylib @@ -49,11 +49,7 @@ endif # Since we don't know which modules we might need or whether the simulator # we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports # is we simply link against all dynamic libraries in the Python installation -ifeq ($(OS),Linux) -PYLIBS = $(wildcard $(PYTHON_DYNLIBDIR)/*.so) $(shell python-config --libs) -else PYLIBS = $(shell python-config --libs) -endif PYTHON_INCLUDEDIR := -I$(shell python -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_inc()') From a19ece8d231d58dde111b8e6fae7647405afa1da Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 27 Aug 2014 11:19:07 +0100 Subject: [PATCH 0545/2656] Cleanup: Initialise gState to NULL --- lib/embed/gpi_embed.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index fd9f4dcf..d7301403 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -32,7 +32,7 @@ #include #include "embed.h" -static PyThreadState *gtstate; +static PyThreadState *gtstate = NULL; static char progname[] = "cocotb"; static char *argv[] = { progname }; From 3048ba1969c9c99e2ad4d69b60ee2bd3f05a9671 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 27 Aug 2014 11:19:56 +0100 Subject: [PATCH 0546/2656] Issue#146: Tell modelesim to do global resolution of symbols from libgpi.so --- makefiles/simulators/Makefile.modelsim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index 377ca474..3dd56fb8 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -46,7 +46,7 @@ endif runsim.do : $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) echo "vlib work" > $@ echo "vlog -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(VERILOG_SOURCES)" >> $@ - echo "vsim -pli libgpi.so $(VSIM_ARGS) work.$(TOPLEVEL) -onfinish stop" >> $@ + echo "vsim -pli libgpi.so -gblso libgpi.so $(VSIM_ARGS) work.$(TOPLEVEL) -onfinish stop" >> $@ ifneq ($(GUI),1) echo "run -all" >> $@ echo "exit" >> $@ From 304132c6e67136c93e1f99438ee5ce2d9420489e Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 29 Aug 2014 08:20:38 +0100 Subject: [PATCH 0547/2656] Make synchronisation optional on Avalon-MM reads --- cocotb/drivers/avalon.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 42455e5a..a5307fb4 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -99,7 +99,7 @@ def _release_lock(self): self.busy_event.set() @coroutine - def read(self, address): + def read(self, address, sync=True): """ Issue a request to the bus and block until this comes back. Simulation time still progresses @@ -113,7 +113,8 @@ def read(self, address): yield self._acquire_lock() # Apply values for next clock edge - yield RisingEdge(self.clock) + if sync: + yield RisingEdge(self.clock) self.bus.address <= address self.bus.read <= 1 From e8d56b226ca4bcdc36785140c9655f11d097f0df Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 29 Aug 2014 08:32:24 +0100 Subject: [PATCH 0548/2656] Issue #141: Some updates to FLI implementation (still unfinished) --- lib/Makefile | 1 + lib/gpi_impl/Makefile | 2 +- lib/gpi_impl/gpi_fli.c | 105 ++++++++++++++++++++++++++++------------- 3 files changed, 75 insertions(+), 33 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index b91c6542..04542629 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -54,6 +54,7 @@ $(LIB_DIR)/libcocotb.so: $(SIM_ROOT)/lib/embed/gpi_embed.c | $(LIB_DIR) $(LIB_DIR)/libgpi.so: $(SIM_ROOT)/lib/gpi_impl/gpi_common.c \ $(SIM_ROOT)/lib/gpi_impl/gpi_vpi.c \ + $(SIM_ROOT)/lib/gpi_impl/gpi_fli.c \ $(SIM_ROOT)/lib/gpi_impl/gpi_vhpi.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/gpi_impl EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) diff --git a/lib/gpi_impl/Makefile b/lib/gpi_impl/Makefile index 37b3b9c6..646d4e91 100644 --- a/lib/gpi_impl/Makefile +++ b/lib/gpi_impl/Makefile @@ -35,7 +35,7 @@ LIBS := -lgpilog -lcocotb $(EXTRA_LIBS) LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) LIB_NAME := libgpi.so -SRCS := gpi_common.c gpi_vpi.c gpi_vhpi.c +SRCS := gpi_common.c gpi_vpi.c gpi_vhpi.c gpi_fli.c CLIBS += $(LIB_DIR)/gpivpi.vpl diff --git a/lib/gpi_impl/gpi_fli.c b/lib/gpi_impl/gpi_fli.c index 8b2abeba..94f2339e 100644 --- a/lib/gpi_impl/gpi_fli.c +++ b/lib/gpi_impl/gpi_fli.c @@ -50,6 +50,7 @@ static gpi_sim_hdl fli_get_root_handle(const char* name) { FENTER mtiRegionIdT root; + gpi_sim_hdl rv; for (root = mti_GetTopRegion(); root != NULL; root = mti_NextRegion(root)) { if (name == NULL || !strcmp(name, mti_GetRegionName(root))) @@ -95,23 +96,37 @@ static gpi_sim_hdl fli_get_handle_by_name(const char *name, gpi_sim_hdl parent) { FENTER mtiRegionIdT hdl = (mtiRegionIdT)parent->sim_hdl; - mtiRegionIdT result; + gpi_sim_hdl rv; + void *result; + mtiRegionIdT result_reg; + mtiSignalIdT result_sig; + mtiVariableIdT result_var; + size_t baselen = strlen(mti_GetRegionFullName(hdl)); char *fullname; + char *ptr; - fullname = (char *)malloc(strlen(mti_GetRegionFullName(root) + strlen(name) + 2); + fullname = (char *)malloc(baselen + strlen(name) + 2); if (fullname == NULL) { LOG_CRITICAL("FLI: Attempting allocate string buffer failed!"); return NULL; } - result = mti_FindRegion(fullname); - if (result) goto success; - result = mti_FindPort(fullname); - if (result) goto success; - result = mti_FindSignal(fullname); - if (result) goto success; - result = mti_FindVar(fullname); + strncpy(fullname, mti_GetRegionFullName(hdl), baselen); + ptr = fullname + baselen; + *ptr++ = '/'; + strncpy(ptr, name, strlen(name)); + + result_reg = mti_FindRegion(fullname); + result = (void *)result_reg; + if (result_reg) goto success; +// result = mti_FindPort(fullname); +// if (result) goto success; + result_sig = mti_FindSignal(fullname); + result = (void *)result_sig; + if (result_sig) goto success; + result_var = mti_FindVar(fullname); + result = (void *)result_var; if (result) goto success; error: @@ -120,13 +135,13 @@ static gpi_sim_hdl fli_get_handle_by_name(const char *name, gpi_sim_hdl parent) // NB we deliberately don't dump an error message here because it's // a valid use case to attempt to grab a signal by name - for example // optional signals on a bus. - free(buff); + free(fullname); return NULL; success: - free(buff); + free(fullname); rv = gpi_create_handle(); - rv->sim_hdl = obj; + rv->sim_hdl = result; FEXIT return rv; @@ -188,9 +203,11 @@ static void fli_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) static void fli_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) { FENTER + char * str_copy = strdup(str); // Mentor discard const qualifier int rc = mti_ForceSignal((mtiSignalIdT)(gpi_hdl->sim_hdl), - str, - (mtiDelayT)-1, // If the delay parameter is negative, then the force is applied immediately. + str_copy, + -1, // If the delay parameter is negative, + // then the force is applied immediately. MTI_FORCE_DEPOSIT, -1, // cancel_period -1); // repeat_period @@ -198,24 +215,25 @@ static void fli_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) if (!rc) LOG_ERROR("Attempt to force signal %s failed", mti_GetSignalName((mtiSignalIdT)gpi_hdl->sim_hdl)); + free(str_copy); FEXIT } static char *fli_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) { FENTER - + mtiInt32T value; switch (mti_GetTypeKind(mti_GetSignalType((mtiSignalIdT)gpi_hdl->sim_hdl))){ case MTI_TYPE_SCALAR: case MTI_TYPE_ENUM: case MTI_TYPE_PHYSICAL: - mti_PrintFormatted( "%d\n", mti_GetSignalValue( sigid ) ); + value = mti_GetSignalValue((mtiSignalIdT)gpi_hdl->sim_hdl); break; default: mti_PrintFormatted( "(Type not supported)\n" ); break; } - char *result = gpi_copy_name(value_p->value.str); + char *result;// = gpi_copy_name(value_p->value.str); FEXIT return result; } @@ -223,7 +241,7 @@ static char *fli_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) static char *fli_get_signal_name_str(gpi_sim_hdl gpi_hdl) { FENTER - const char *name = mti_GetSignalName((mtiSignalIdT)gpi_hdl->sim_hdl)); + const char *name = mti_GetSignalName((mtiSignalIdT)gpi_hdl->sim_hdl); char *result = gpi_copy_name(name); FEXIT return result; @@ -232,25 +250,25 @@ static char *fli_get_signal_name_str(gpi_sim_hdl gpi_hdl) static char *fli_get_signal_type_str(gpi_sim_hdl gpi_hdl) { switch (mti_GetTypeKind(mti_GetSignalType((mtiSignalIdT)gpi_hdl->sim_hdl))){ - case MTI_TYPE_SCALAR : return "Scalar"; - case MTI_TYPE_ARRAY : return "Array"; - case MTI_TYPE_RECORD : return "Record"; - case MTI_TYPE_ENUM : return "Enum"; - case MTI_TYPE_INTEGER : return "Integer"; - case MTI_TYPE_PHYSICAL : return "Physical"; - case MTI_TYPE_REAL : return "Real"; - case MTI_TYPE_ACCESS : return "Access"; - case MTI_TYPE_FILE : return "File"; - case MTI_TYPE_TIME : return "Time"; - case MTI_TYPE_C_REAL : return "C Real"; - case MTI_TYPE_C_ENUM : return "C Enum"; - default : return "Unknown!"; + case MTI_TYPE_SCALAR : return strdup("Scalar"); + case MTI_TYPE_ARRAY : return strdup("Array"); + case MTI_TYPE_RECORD : return strdup("Record"); + case MTI_TYPE_ENUM : return strdup("Enum"); + case MTI_TYPE_INTEGER : return strdup("Integer"); + case MTI_TYPE_PHYSICAL : return strdup("Physical"); + case MTI_TYPE_REAL : return strdup("Real"); + case MTI_TYPE_ACCESS : return strdup("Access"); + case MTI_TYPE_FILE : return strdup("File"); + case MTI_TYPE_TIME : return strdup("Time"); + case MTI_TYPE_C_REAL : return strdup("C Real"); + case MTI_TYPE_C_ENUM : return strdup("C Enum"); + default : return strdup("Unknown!"); } } // Callback related functions -static int32_t handle_fli_callback(p_cb_data cb_data) +static int32_t handle_fli_callback(gpi_cb_hdl cb_data) { FENTER LOG_CRITICAL("FLI: Callbacks not implemented yet"); @@ -321,6 +339,29 @@ static int fli_register_timed_callback(gpi_sim_hdl cb, return 0; } +static void fli_sim_end(void) +{ + sim_finish_cb = NULL; + mti_Quit(); +} + + +/* Checking of validity is done in the common code */ +static gpi_cb_hdl fli_create_cb_handle(void) +{ + gpi_cb_hdl ret = NULL; + + FENTER + + void * new_cb_hdl = calloc(1, sizeof(*new_cb_hdl)); + if (new_cb_hdl) + ret = &new_cb_hdl->gpi_cb_data; + + FEXIT + return ret; +} + + static s_gpi_impl_tbl fli_table = { .sim_end = fli_sim_end, From ceb5d8114c9b9c5c46013cd745c3dca5931c6757 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 29 Aug 2014 15:09:17 +0200 Subject: [PATCH 0549/2656] Update io.h Fix message "io.h:36:19: warning: no newline at end of file" --- examples/endian_swapper/cosim/io.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/endian_swapper/cosim/io.h b/examples/endian_swapper/cosim/io.h index 5a998342..daa93a39 100644 --- a/examples/endian_swapper/cosim/io.h +++ b/examples/endian_swapper/cosim/io.h @@ -33,4 +33,4 @@ extern unsigned int IORD(unsigned int base, unsigned int address); extern int IOWR(unsigned int base, unsigned int address, unsigned int data); -#endif // __IO_H__ \ No newline at end of file +#endif // __IO_H_ From 0e4a138aa85974375ea446ded145d4b39fac0814 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 29 Aug 2014 15:09:54 +0200 Subject: [PATCH 0550/2656] Update io_module.h Fix message "io_module.h:42:7: warning: no newline at end of file" --- examples/endian_swapper/cosim/io_module.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/endian_swapper/cosim/io_module.h b/examples/endian_swapper/cosim/io_module.h index f0ecc6df..f6ffc69d 100644 --- a/examples/endian_swapper/cosim/io_module.h +++ b/examples/endian_swapper/cosim/io_module.h @@ -39,4 +39,4 @@ static PyMethodDef io_module_methods[] = { {NULL, NULL, 0, NULL} /* Sentinel */ }; -#endif \ No newline at end of file +#endif From ebd10f4355dcf1ee770d72920b3d947535064029 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 29 Aug 2014 15:51:11 +0200 Subject: [PATCH 0551/2656] Update introduction.rst Typofix: "Cadence" --- documentation/source/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index b5afe841..0f5da120 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -14,7 +14,7 @@ What is cocotb? * Icarus Verilog * Synopsys VCS * Aldec Riviera-PRO -* Cadance Incisive +* Cadence Incisive * Mentor Modelsim **Cocotb** was developed by `Potential Ventures _` with the support of `Solarflare Communications Ltd `_ and contributions from Gordon McGregor and Finn Grimwood. From 91c6f59bba5ebc6d1cc208850c063995cc3d8abd Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 29 Aug 2014 15:51:32 +0200 Subject: [PATCH 0552/2656] Update simulator_support.rst Typofix: "Cadence" --- documentation/source/simulator_support.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index a4491324..479776d1 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -25,6 +25,6 @@ Aldec Riviera-PRO Mentor Questa ------------- -Cadance Incisive +Cadence Incisive ---------------- From 0d972038badf343ff51872818eb1459ece57e2b1 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 1 Sep 2014 10:57:49 +0200 Subject: [PATCH 0553/2656] Update Makefile Small rewording to prevent throwing off simplistic syntax highlighters. --- documentation/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Makefile b/documentation/Makefile index 2ec084c3..e410134c 100644 --- a/documentation/Makefile +++ b/documentation/Makefile @@ -9,7 +9,7 @@ BUILDDIR = build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you do not have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. From 91e86978f100214ed9804ac7ab4aba4efc6fdaa6 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 1 Sep 2014 14:58:17 +0200 Subject: [PATCH 0554/2656] Update io.h Fix missing underscore in comment. --- examples/endian_swapper/cosim/io.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/endian_swapper/cosim/io.h b/examples/endian_swapper/cosim/io.h index daa93a39..98750fe7 100644 --- a/examples/endian_swapper/cosim/io.h +++ b/examples/endian_swapper/cosim/io.h @@ -33,4 +33,4 @@ extern unsigned int IORD(unsigned int base, unsigned int address); extern int IOWR(unsigned int base, unsigned int address, unsigned int data); -#endif // __IO_H_ +#endif // __IO_H__ From e26ecb428e07137d64fbda982bec54441639350e Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 1 Sep 2014 17:00:56 +0200 Subject: [PATCH 0555/2656] Update Makefile.ius First step toward supporting GPI_IMPL=vhpi. --- makefiles/simulators/Makefile.ius | 36 +++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index d861c3b6..654f1da1 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -27,10 +27,38 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -results.xml: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) +# Common Makefile for Cadence Incisive + +EXTRA_ARGS += -64bit + +ifeq ($(GUI),1) + EXTRA_ARGS += -gui +else + EXTRA_ARGS += +endif + +ifeq ($(GPI_IMPL),vpi) + GPI_ARGS = -loadvpi $(LIB_DIR)/libgpi:vlog_startup_routines_bootstrap + EXTRA_ARGS += -sv + HDL_SOURCES = $(VERILOG_SOURCES) +endif + +ifeq ($(GPI_IMPL),vhpi) + GPI_ARGS = -loadvhpi $(LIB_DIR)/libgpi:vhpi_startup_routines_bootstrap + EXTRA_ARGS += -work $(SIM_BUILD) + EXTRA_ARGS += -cdslib cds.lib + EXTRA_ARGS += -v93 + EXTRA_ARGS += -top work.$(TOPLEVEL) + HDL_SOURCES = $(VHDL_SOURCES) +endif + +ifndef GPI_ARGS + $(error "Unable to determine appropriate GPI layer to use") +endif + +results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - irun -sv $(EXTRA_ARGS) -64bit -loadvpi $(LIB_DIR)/libgpi:vlog_startup_routines_bootstrap +access+rwc $(VERILOG_SOURCES) + irun $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(HDL_SOURCES) clean:: - -@rm -rf $(SIM_BUILD) - + -rm -rf $(SIM_BUILD) From 68e6e949152056419f686b1d688cb339107b6ea6 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 1 Sep 2014 17:22:21 +0200 Subject: [PATCH 0556/2656] Update Makefile.ius Use -nclibdirname. Comment out the cds.lib usage, it is not generally needed. --- makefiles/simulators/Makefile.ius | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 654f1da1..721f91e2 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -30,6 +30,7 @@ # Common Makefile for Cadence Incisive EXTRA_ARGS += -64bit +EXTRA_ARGS += -nclibdirname $(SIM_BUILD) ifeq ($(GUI),1) EXTRA_ARGS += -gui @@ -45,10 +46,9 @@ endif ifeq ($(GPI_IMPL),vhpi) GPI_ARGS = -loadvhpi $(LIB_DIR)/libgpi:vhpi_startup_routines_bootstrap - EXTRA_ARGS += -work $(SIM_BUILD) - EXTRA_ARGS += -cdslib cds.lib + #EXTRA_ARGS += -cdslib cds.lib EXTRA_ARGS += -v93 - EXTRA_ARGS += -top work.$(TOPLEVEL) + EXTRA_ARGS += -top worklib.$(TOPLEVEL) HDL_SOURCES = $(VHDL_SOURCES) endif From 61864640ccf69fc7a7b060aebe917bb6f3d96b59 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 2 Sep 2014 14:12:39 +0200 Subject: [PATCH 0557/2656] Update Makefile Define and use PYTHON_INCDIR, PYTHON_INCDIR and SWIG as variables so that they can be overwritten externally. --- examples/endian_swapper/cosim/Makefile | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/examples/endian_swapper/cosim/Makefile b/examples/endian_swapper/cosim/Makefile index b8031f86..866f656b 100644 --- a/examples/endian_swapper/cosim/Makefile +++ b/examples/endian_swapper/cosim/Makefile @@ -1,17 +1,21 @@ +PYTHON_INCDIR ?= /usr/include/python2.7 +PYTHON_LIBDIR ?= /usr/lib64 +SWIG ?= swig +.PHONY: all all: io_module.so _hal.so io_module.o: io.c io_module.h io.h - gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -DNDEBUG -I/usr/include/python2.7 -c $< -o $@ + gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -DNDEBUG -I$(PYTHON_INCDIR) -c $< -o $@ io_module.so: io_module.o - gcc -pthread -shared -Wl,-z,relro $< -L/usr/lib64 -lpython2.7 -o $@ + gcc -pthread -shared -Wl,-z,relro $< -L$(PYTHON_LIBDIR) -lpython2.7 -o $@ -_hal.so : ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so - gcc -g -ldl -shared -fPIC -I$(shell pwd) -I/usr/include/python2.7 -I../hal ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so -o $@ +_hal.so: ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so + gcc -g -ldl -shared -fPIC -I$(shell pwd) -I$(PYTHON_INCDIR) -I../hal ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so -o $@ endian_swapper_hal_wrap.c: ../hal/endian_swapper_hal.h - swig -python -outcurrentdir ../hal/endian_swapper_hal.h + $(SWIG) -python -outcurrentdir ../hal/endian_swapper_hal.h .PHONY: clean clean: From 3af51c2da193e072c5e96546f8a1ea63192a2997 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 2 Sep 2014 15:42:54 +0200 Subject: [PATCH 0558/2656] Update scheduler.py Typofix. --- cocotb/scheduler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 7fc5a194..3e7e840f 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -405,7 +405,7 @@ def add(self, coroutine): "Attempt to schedule a coroutine that hasn't started") coroutine.log.error("This is the failing coroutine") self.log.warning( - "Did you forget to add paranthesis to the @test decorator?") + "Did you forget to add parentheses to the @test decorator?") self._test_result = TestError( "Attempt to schedule a coroutine that hasn't started") self._terminate = True From 2b1ec6d231158159e04aca537fb32e06ec5d1fc6 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 2 Sep 2014 15:43:54 +0200 Subject: [PATCH 0559/2656] Update gpi_vhpi.c Typofix. --- lib/gpi_impl/gpi_vhpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gpi_impl/gpi_vhpi.c b/lib/gpi_impl/gpi_vhpi.c index 5495393e..6ac6f28d 100644 --- a/lib/gpi_impl/gpi_vhpi.c +++ b/lib/gpi_impl/gpi_vhpi.c @@ -29,7 +29,7 @@ // Some functions are completely untested (vhpi_get_handle_by_index) and others // need optimisation. // -// VHPI seems to run significantly slower the VPI, need to investigate. +// VHPI seems to run significantly slower than VPI, need to investigate. #include "gpi_priv.h" From c5466a0728f682942d0ef6febe0cc709b67eef2a Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 3 Sep 2014 10:18:17 +0200 Subject: [PATCH 0560/2656] Update test_endian_swapper_hal.py Typofix --- examples/endian_swapper/tests/test_endian_swapper_hal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/endian_swapper/tests/test_endian_swapper_hal.py b/examples/endian_swapper/tests/test_endian_swapper_hal.py index de4cf843..6d91cc0a 100644 --- a/examples/endian_swapper/tests/test_endian_swapper_hal.py +++ b/examples/endian_swapper/tests/test_endian_swapper_hal.py @@ -80,7 +80,7 @@ def write(address, value): # Check the actual value if dut.byteswapping.value: - raise TestFailure("Byteswapping is enabled but havne't configured DUT") + raise TestFailure("Byteswapping is enabled but haven't configured DUT") yield cocotb.external(hal.endian_swapper_enable)(state) From 7691af7c05cb526ad663a54a23d3b31ab4327729 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 3 Sep 2014 10:21:06 +0200 Subject: [PATCH 0561/2656] Update introduction.rst Fix link. --- documentation/source/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index 0f5da120..5fa7cede 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -17,7 +17,7 @@ What is cocotb? * Cadence Incisive * Mentor Modelsim -**Cocotb** was developed by `Potential Ventures _` with the support of `Solarflare Communications Ltd `_ and contributions from Gordon McGregor and Finn Grimwood. +**Cocotb** was developed by `Potential Ventures `_ with the support of `Solarflare Communications Ltd `_ and contributions from Gordon McGregor and Finn Grimwood. **Cocotb** can be used live in a web-browser on the excellent `EDA Playground `_. From 9fdb2131ad96bfee482e8668c1af1dc64832b07d Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 3 Sep 2014 10:23:35 +0200 Subject: [PATCH 0562/2656] Update introduction.rst Typofix. --- documentation/source/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index 5fa7cede..ed178cb9 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -39,7 +39,7 @@ In cocotb VHDL/Verilog/SystemVerilog are only used for the synthesisable design. **Cocotb** has built-in support for integrating with the `Jenkins `_ continuous integration system. -**Cocotb** automatically discovers tests to that no additional step is required to add a test to a regression. +**Cocotb** automatically discovers tests so that no additional step is required to add a test to a regression. How does Cocotb work From 771eb09211cbaf4a73e08a5587ed07e237803cf2 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 3 Sep 2014 10:39:48 +0200 Subject: [PATCH 0563/2656] Update quickstart.rst Typofixes. --- documentation/source/quickstart.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index 61ea52d1..ef2c8672 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -13,7 +13,7 @@ Cocotb has the following requirements: * Python 2.6+ * Python-dev packages -* A verilog simulator +* A Verilog simulator Running an example @@ -37,7 +37,7 @@ To run a test using a different simulator: Running a VHDL example ---------------------- -The endian swapper example includes both a VHDL and Verlog RTL implementation. The Cocotb testbench can execute against either implementation using VPI for Verilog and VHPI for VHDL. To run the test suite against the VHDL implementation use the following command (a VHPI capable simulator must be used): +The endian swapper example includes both a VHDL and Verilog RTL implementation. The Cocotb testbench can execute against either implementation using VPI for Verilog and VHPI for VHDL. To run the test suite against the VHDL implementation use the following command (a VHPI capable simulator must be used): .. code-block:: bash @@ -72,7 +72,7 @@ The same mechanism can be used to access signals inside the design. Assigning values to signals --------------------------- -Values can be assigned to signals using either the .value property of a handle object or using direct assignment while traversing the hierarchy +Values can be assigned to signals using either the .value property of a handle object or using direct assignment while traversing the hierarchy. .. code-block:: python From 0ecd1915e6067aab852772c75854107c65438fde Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 3 Sep 2014 10:41:49 +0200 Subject: [PATCH 0564/2656] Update coroutines.rst Typofix. --- documentation/source/coroutines.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/coroutines.rst b/documentation/source/coroutines.rst index fe987d4d..41fb47e5 100644 --- a/documentation/source/coroutines.rst +++ b/documentation/source/coroutines.rst @@ -2,7 +2,7 @@ Coroutines ========== Testbenches built using Cocotb use coroutines. While the coroutine is executing -the simulation is paused. The coroutine use the :keyword:`yield` keyword to +the simulation is paused. The coroutine uses the :keyword:`yield` keyword to pass control of execution back to the simulator and simulation time can advance again. From 75f1ad2985d7b25bd0fce7e5727971c150bb06fd Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 3 Sep 2014 10:51:47 +0200 Subject: [PATCH 0565/2656] Update endian_swapper.rst Typofixes. --- documentation/source/endian_swapper.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/documentation/source/endian_swapper.rst b/documentation/source/endian_swapper.rst index 6fcc8410..a53668d5 100644 --- a/documentation/source/endian_swapper.rst +++ b/documentation/source/endian_swapper.rst @@ -19,7 +19,7 @@ We have a relatively simplistic RTL block called the endian_swapper. The DUT ha .. image:: diagrams/svg/endian_swapper_design.svg -The dut will swap the endianness of packets on the Avalon-ST bus if a configuration bit is set. For every packet arriving on the "stream_in" interface the entire packet will be endian swapped if the configuration bit is set, otherwise the entire packet will pass through unmodified. +The DUT will swap the endianness of packets on the Avalon-ST bus if a configuration bit is set. For every packet arriving on the "stream_in" interface the entire packet will be endian swapped if the configuration bit is set, otherwise the entire packet will pass through unmodified. Testbench --------- @@ -54,7 +54,7 @@ If we inspect this line-by-line: self.stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) -Here we're creating an AvalonSTDriver instance. The constructor requires 3 arguments - a handle to the entity containing the interface (**dut**), the name of the interface (**stream_in**) and the associated clock with which to drive the interface (**dut.clk**). The driver will auto-discover the signals for the interface, assuming that they follow the following naming convention **interface_name** _ *signal*. +Here we're creating an AvalonSTDriver instance. The constructor requires 3 arguments - a handle to the entity containing the interface (**dut**), the name of the interface (**stream_in**) and the associated clock with which to drive the interface (**dut.clk**). The driver will auto-discover the signals for the interface, assuming that they follow the naming convention **interface_name** _ *signal*. In this case we have the following signals defined for the **stream_in** interface: @@ -106,7 +106,7 @@ Finally we create another Monitor instance, this time connected to the **stream_ Test Function ~~~~~~~~~~~~~ -There are various 'knobs' we can tweak on this tesbench to vary the behaviour: +There are various 'knobs' we can tweak on this testbench to vary the behaviour: * Packet size * Backpressure on the **stream_out** interface @@ -154,7 +154,7 @@ We want to run different variations of tests but they will all have a very simil raise tb.scoreboard.result -We can see that this test function creates an instance of the tesbench, resets the DUT by running the coroutine ``tb.reset()`` and then starts off any optional coroutines passed in using the keyword arguments. We then send in all the packets from ``data_in``, ensure that all the packets have been received by waiting 2 cycles at the end. We read the packet count and compare this with the number of packets. Finally we use the ``tb.scoreboard.result`` to determine the status of the test. If any transactions didn't match the expected output then this member would be and instance of the ``TestFailure`` result. +We can see that this test function creates an instance of the testbench, resets the DUT by running the coroutine ``tb.reset()`` and then starts off any optional coroutines passed in using the keyword arguments. We then send in all the packets from ``data_in``, ensure that all the packets have been received by waiting 2 cycles at the end. We read the packet count and compare this with the number of packets. Finally we use the ``tb.scoreboard.result`` to determine the status of the test. If any transactions didn't match the expected output then this member would be an instance of the ``TestFailure`` result. Test permutations From de22fe747c7c6eb1070307da970cf992ca603c39 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 3 Sep 2014 10:56:31 +0200 Subject: [PATCH 0566/2656] Update ping_tun_tap.rst Typofix. --- documentation/source/ping_tun_tap.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/ping_tun_tap.rst b/documentation/source/ping_tun_tap.rst index f961a0ef..ffb721b8 100644 --- a/documentation/source/ping_tun_tap.rst +++ b/documentation/source/ping_tun_tap.rst @@ -20,7 +20,7 @@ Architecture ------------ We have a simple RTL block that takes ICMP echo requests and generates an ICMP -echo response. To verify this behaviour we want to run `ping`_ utility +echo response. To verify this behaviour we want to run the `ping`_ utility against our RTL running in the simulator. In order to achieve this we need to capture the packets that are created by From a903acd96d13693c64d1d1953ad58e4254843c18 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 3 Sep 2014 11:03:38 +0200 Subject: [PATCH 0567/2656] Update hal_cosimulation.rst Typofix. --- documentation/source/hal_cosimulation.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/source/hal_cosimulation.rst b/documentation/source/hal_cosimulation.rst index b1fd4aef..53e6f804 100644 --- a/documentation/source/hal_cosimulation.rst +++ b/documentation/source/hal_cosimulation.rst @@ -46,7 +46,7 @@ a low-level function to access the hardware, often something like ``ioread32``. We need this call to block while simulation time advances and a value is either read or written on the bus. To achieve this we link the HAL against a C library that provides the low level read/write functions. These functions -in turn call into Cocotb and perform an the relevant access on the DUT. +in turn call into Cocotb and perform the relevant access on the DUT. Cocotb infrastructure @@ -107,7 +107,7 @@ IO Module This module acts as the bridge between the C HAL and the Python testbench. It exposes the ``IORD`` and ``IOWR`` calls to link the HAL against, but also provides a Python interface to allow the read/write bindings to be dynamically -set (through ``set_write_function`` and ``set_read_function`` module functions. +set (through ``set_write_function`` and ``set_read_function`` module functions). In a more complicated scenario, this could act as an interconnect, dispatching the access to the appropriate driver depending on address decoding, for From 560e5204c638fb37be8ceecd32e30aa1ea793d75 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 3 Sep 2014 11:06:51 +0200 Subject: [PATCH 0568/2656] Update decorators.py Typofix in docstring. --- cocotb/decorators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index aab25bfb..93e99f8f 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -308,7 +308,7 @@ def execute_external(func, _event): @public class test(coroutine): - """Decorator to mark a fucntion as a test + """Decorator to mark a function as a test All tests are coroutines. The test decorator provides some common reporting etc, a test timeout and allows From 532d37544fb0253ddbbd79241fb793453ac625c0 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 3 Sep 2014 11:09:49 +0200 Subject: [PATCH 0569/2656] Update scoreboard.py Typofixes in docstrings. --- cocotb/scoreboard.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index a6d1e8c5..5b3547b5 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -40,7 +40,7 @@ class Scoreboard(object): - """Generic scorboarding class + """Generic scoreboarding class We can add interfaces by providing a monitor and an expected output queue @@ -60,7 +60,7 @@ def __init__(self, dut, reorder_depth=0, fail_immediately=True): @property def result(self): - """determine the test result - do we have any pending data remaining?""" + """Determine the test result - do we have any pending data remaining?""" fail = False for monitor, expected_output in self.expected.iteritems(): if callable(expected_output): From 74e3499962b3902774518a5b9007ba434189314f Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 3 Sep 2014 11:17:46 +0200 Subject: [PATCH 0570/2656] Update building.rst Typofixes. Add VHDL_SOURCES doc. --- documentation/source/building.rst | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/documentation/source/building.rst b/documentation/source/building.rst index 5121a1be..f3ff2b71 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -41,7 +41,13 @@ Selects which simulator Makefile to use. Attempts to include a simulator specif VERILOG_SOURCES ~~~~~~~~~~~~~~~ -A list of the verilog source files to include. +A list of the Verilog source files to include. + + +VHDL_SOURCES +~~~~~~~~~~~~~~~ + +A list of the VHDL source files to include. COMPILE_ARGS @@ -58,12 +64,12 @@ Any arguments or flags to pass to the execution of the compiled simulation. Onl EXTRA_ARGS ~~~~~~~~~~ -Passed to both the compile and execute phases of simulators with two rules, or passed to the single compile and run commad for simulators which don't have a distinct compilation stage. +Passed to both the compile and execute phases of simulators with two rules, or passed to the single compile and run command for simulators which don't have a distinct compilation stage. CUSTOM_COMPILE_DEPS ~~~~~~~~~~~~~~~~~~~ -Use to add additional dependencies to the compilation target; useful for defining additional rules to run pre-compilation or if the compilation phase depends on files other than the RTL sources listed in **VERILOG_SOURCES**. +Use to add additional dependencies to the compilation target; useful for defining additional rules to run pre-compilation or if the compilation phase depends on files other than the RTL sources listed in **VERILOG_SOURCES** or **VHDL_SOURCES**. CUSTOM_SIM_DEPS ~~~~~~~~~~~~~~~ @@ -108,7 +114,7 @@ ANSI colour codes if the output is a terminal (isatty()). COCOTB_ANSI_OUTPUT=1 forces output to be ANSI regardless of the type stdout -COCOTB_ANDI_OUTPUT=0 supresses the ANSI output in the log messages +COCOTB_ANSI_OUTPUT=0 supresses the ANSI output in the log messages MODULE From 031dc0fc37018e9ff047633855cbea273f28b87f Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 3 Sep 2014 12:06:08 +0100 Subject: [PATCH 0571/2656] Fixed segfault if no message extracted from malformed call to $fatal() --- lib/embed/gpi_embed.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index d7301403..d6ccf595 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -248,7 +248,10 @@ void embed_sim_event(gpi_event_t level, const char *msg) PyObject *fArgs = PyTuple_New(2); PyTuple_SetItem(fArgs, 0, PyInt_FromLong(level)); - PyTuple_SetItem(fArgs, 1, PyString_FromString(msg)); + if (msg != NULL) + PyTuple_SetItem(fArgs, 1, PyString_FromString(msg)); + else + PyTuple_SetItem(fArgs, 1, PyString_FromString("No message provided")); PyObject *pValue = PyObject_Call(pEventFn, fArgs, NULL); if (!pValue) { LOG_ERROR("Passing event to upper layer failed"); From 6426e782a784191eede0e309c3705a76d60d9488 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Wed, 3 Sep 2014 19:48:32 +0100 Subject: [PATCH 0572/2656] Fixes #76: Use simulation name retrieved in cocotb.SIM_NAME so mark expected failures --- examples/functionality/tests/test_discovery.py | 18 ++++++++++++------ lib/embed/gpi_embed.c | 5 +++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/examples/functionality/tests/test_discovery.py b/examples/functionality/tests/test_discovery.py index 8e976269..4dd9f464 100644 --- a/examples/functionality/tests/test_discovery.py +++ b/examples/functionality/tests/test_discovery.py @@ -55,10 +55,13 @@ def access_signal(dut): -@cocotb.test() +@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) def access_single_bit(dut): - """Access a single bit in a vector of the dut""" - # FIXME this test fails on Icarus but works on VCS + """ + Access a single bit in a vector of the dut + + Icarus v0.96 doesn't support single bit access to vectors + """ dut.stream_in_data <= 0 yield Timer(10) dut.log.info("%s = %d bits" % (str(dut.stream_in_data), len(dut.stream_in_data))) @@ -69,10 +72,13 @@ def access_single_bit(dut): (str(dut.stream_out_data_comb), dut.stream_out_data_comb.value.integer, (1<<2))) -@cocotb.test() +@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) def access_single_bit_assignment(dut): - """Access a single bit in a vector of the dut using the assignment mechanism""" - # FIXME this test fails on Icarus but works on VCS + """ + Access a single bit in a vector of the dut using the assignment mechanism + + Icarus v0.96 doesn't support single bit access to vectors + """ dut.stream_in_data = 0 yield Timer(10) dut.log.info("%s = %d bits" % (str(dut.stream_in_data), len(dut.stream_in_data))) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index d6ccf595..6951517f 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -190,6 +190,11 @@ void embed_sim_init(gpi_sim_info_t *info) LOG_INFO("Python interpreter initialised and cocotb loaded!"); // Now that logging has been set up ok we initialise the testbench + if (-1 == PyObject_SetAttrString(cocotb_module, "SIM_NAME", PyString_FromString(info->product))) { + PyErr_Print(); + fprintf(stderr, "Unable to set SIM_NAME"); + goto cleanup; + } // Hold onto a reference to our _fail_test function pEventFn = PyObject_GetAttrString(cocotb_module, "_sim_event"); From 34dfdb74ecf815d7d12b10e2e7c37d1cfa9a430b Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 3 Sep 2014 20:40:21 +0100 Subject: [PATCH 0573/2656] Cleanup: Test should be marked as expecting error --- examples/functionality/tests/test_cocotb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 36315f6d..469175a1 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -169,7 +169,7 @@ def do_test_afterdelay_in_readonly(dut): yield Timer(0) exited = True -@cocotb.test() +@cocotb.test(expect_error=True) def test_readwrite_in_readonly(dut): """Test doing invalid sim operation""" global exited From 811d75c96e19d0f6639df25cbc8588ae1a7c7fcf Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 3 Sep 2014 20:40:54 +0100 Subject: [PATCH 0574/2656] Issue#146: Reinstate dlopen to catch sims that do not --- lib/embed/gpi_embed.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index d7301403..1ad04e8c 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -30,6 +30,7 @@ // Embed Python into the simulator using GPI #include +#include #include "embed.h" static PyThreadState *gtstate = NULL; @@ -54,6 +55,11 @@ void embed_init_python(void) { FENTER; + void *ret = dlopen("/usr/lib/x86_64-linux-gnu/libpython2.7.so", RTLD_LAZY | RTLD_GLOBAL); + if (!ret) { + fprintf(stderr, "Failed to find python lib (%s)\n", dlerror()); + } + // Don't initialise python if already running if (gtstate) return; From cb801b8f3a4d013c8bdeba9ff6f7bfe47651530e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 3 Sep 2014 20:55:08 +0100 Subject: [PATCH 0575/2656] Cleaup: Mark expected fail and new dual of test in readonly phase --- examples/functionality/tests/test_cocotb.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 469175a1..380c612b 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -162,11 +162,11 @@ def do_test_readwrite_in_readonly(dut): exited = True @cocotb.coroutine -def do_test_afterdelay_in_readonly(dut): +def do_test_afterdelay_in_readonly(dut, delay): global exited yield RisingEdge(dut.clk) yield ReadOnly() - yield Timer(0) + yield Timer(delay) exited = True @cocotb.test(expect_error=True) @@ -181,13 +181,25 @@ def test_readwrite_in_readonly(dut): if exited is not True: raise cocotb.TestFailed -@cocotb.test() +@cocotb.test(expect_error=True) def test_afterdelay_in_readonly(dut): """Test doing invalid sim operation""" global exited exited = False clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) - coro = cocotb.fork(do_test_afterdelay_in_readonly(dut)) + coro = cocotb.fork(do_test_afterdelay_in_readonly(dut, 0)) + yield [Join(coro), Timer(100000)] + clk_gen.kill() + if exited is not True: + raise cocotb.TestFailed + +@cocotb.test() +def test_afterdelay_in_readonly_valid(dut): + """Same as test_afterdelay_in_readonly but with valid delay > 0""" + global exited + exited = False + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + coro = cocotb.fork(do_test_afterdelay_in_readonly(dut, 1)) yield [Join(coro), Timer(100000)] clk_gen.kill() if exited is not True: From 9a47a17714f0398aef95cff7e279dd3abb37c247 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 3 Sep 2014 21:01:03 +0100 Subject: [PATCH 0576/2656] Cleanup: Aldec and icarus behave differently, shame but they do --- examples/functionality/tests/test_cocotb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 380c612b..abcc8491 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -181,7 +181,7 @@ def test_readwrite_in_readonly(dut): if exited is not True: raise cocotb.TestFailed -@cocotb.test(expect_error=True) +@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) def test_afterdelay_in_readonly(dut): """Test doing invalid sim operation""" global exited From 17806b23f5adbf1a7daf9fb752c945fe81f7606f Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 3 Sep 2014 22:04:43 +0100 Subject: [PATCH 0577/2656] Issue#146: Pass the name of the lib to load at runtime This is done not as environment vars but as a literal that can be stringified at compilation time. This ensures that the version of the lib that we load to work around some simulators not doing global symbol resolution matches that which was used as build time. If we used name=get_env("") this would be more prone to problems. --- lib/embed/Makefile | 2 +- lib/embed/gpi_embed.c | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/embed/Makefile b/lib/embed/Makefile index bee321d8..8380e649 100644 --- a/lib/embed/Makefile +++ b/lib/embed/Makefile @@ -30,7 +30,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += -GCC_ARGS += +GCC_ARGS += -DPYTHON_SO_LIB=libpython$(PYTHON_VERSION).so LIBS := $(PYLIBS) LD_PATH := -L$(LIB_DIR) SRCS := gpi_embed.c diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index e0fed158..259323b0 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -51,13 +51,23 @@ static PyObject *pEventFn; * * Stores the thread state for cocotb in static variable gtstate */ + +#define xstr(a) str(a) +#define str(a) #a + void embed_init_python(void) { FENTER; - void *ret = dlopen("/usr/lib/x86_64-linux-gnu/libpython2.7.so", RTLD_LAZY | RTLD_GLOBAL); +#ifndef PYTHON_SO_LIB +#error "Python version needs passing in with -DPYTHON_SO_VERSION=libpython.so" +#else +#define PY_SO_LIB xstr(PYTHON_SO_LIB) +#endif + + void *ret = dlopen(PY_SO_LIB, RTLD_LAZY | RTLD_GLOBAL); if (!ret) { - fprintf(stderr, "Failed to find python lib (%s)\n", dlerror()); + fprintf(stderr, "Failed to find python lib %s (%s)\n", PY_SO_LIB, dlerror()); } // Don't initialise python if already running From 7052bc91d204daed57780abd1d8a74996aff677a Mon Sep 17 00:00:00 2001 From: Chiggs Date: Thu, 4 Sep 2014 07:50:11 +0100 Subject: [PATCH 0578/2656] Cleanup: Ensure that exceptions raised during trigger prime are recorded as a TestError --- cocotb/result.py | 18 +++++++++++++++--- cocotb/scheduler.py | 8 +++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/cocotb/result.py b/cocotb/result.py index 3fb96d84..ba3c1b88 100644 --- a/cocotb/result.py +++ b/cocotb/result.py @@ -31,10 +31,11 @@ from StringIO import StringIO def raise_error(obj, msg): - """Creates a TestError exception and raises it after printing a traceback + """ + Creates a TestError exception and raises it after printing a traceback - obj has a log method - msg is a string + obj has a log method + msg is a string """ exc_type, exc_value, exc_traceback = sys.exc_info() buff = StringIO() @@ -44,6 +45,17 @@ def raise_error(obj, msg): exception.stderr.write(buff.getvalue()) raise exception +def create_error(obj, msg): + """ + As above, but return the exception rather than raise it, simply to avoid + too many levels of nested try/except blocks + """ + try: + raise_error(obj, msg) + except TestError as error: + return error + return TestError("Creating error traceback failed") + class ReturnValue(StopIteration): def __init__(self, retval): diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 3e7e840f..f75b5656 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -66,7 +66,7 @@ import cocotb.decorators from cocotb.triggers import Trigger, GPITrigger, Timer, ReadOnly, _NextTimeStep, _ReadWrite from cocotb.log import SimLog -from cocotb.result import TestComplete, TestError, ReturnValue, raise_error +from cocotb.result import TestComplete, TestError, ReturnValue, raise_error, create_error @@ -386,8 +386,10 @@ def _coroutine_yielded(self, coro, triggers): try: trigger.prime(self.react) except Exception as e: - raise_error(self, "Unable to prime trigger %s: %s" % ( - str(trigger), str(e))) + # Convert any exceptions into a test result + self.finish_test( + create_error(self, "Unable to prime trigger %s: %s" % ( + str(trigger), str(e)))) def queue(self, coroutine): """Queue a coroutine for execution""" From 030a25bdeae4d02db12becf461f33c5d4217c67f Mon Sep 17 00:00:00 2001 From: mike Date: Wed, 20 Aug 2014 11:50:30 -0400 Subject: [PATCH 0579/2656] Updated binary.py for support of signed value types. Added invert and python slice to BinaryValue Added AD9361 interface driver Added AXI4 Slave model. --- cocotb/binary.py | 261 ++++++++++++++++++++++++++++++++++++--- cocotb/drivers/ad9361.py | 185 +++++++++++++++++++++++++++ cocotb/drivers/amba.py | 241 +++++++++++++++++++++++++++++++++++- lib/gpi_impl/gpi_vpi.c | 3 + 4 files changed, 668 insertions(+), 22 deletions(-) create mode 100644 cocotb/drivers/ad9361.py diff --git a/cocotb/binary.py b/cocotb/binary.py index fbe29fe5..9603c11a 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -27,12 +27,18 @@ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' +from math import log,ceil def resolve(string): for char in BinaryValue._resolve_to_0: string = string.replace(char, "0") return string +class BinaryRepresentation(): + UNSIGNED = 0 + SIGNED_MAGNITUDE = 1 + TWOS_COMPLEMENT = 2 + class BinaryValue(object): """Represenatation of values in binary format. @@ -59,7 +65,7 @@ class BinaryValue(object): _permitted_chars = "xXzZuU" + "01" - def __init__(self, value=None, bits=None, bigEndian=True): + def __init__(self, value=None, bits=None, bigEndian=True, binaryRepresentation=BinaryRepresentation.UNSIGNED): """ Kwagrs: Value (string or int or long): value to assign to the bus @@ -72,6 +78,18 @@ def __init__(self, value=None, bits=None, bigEndian=True): self._str = "" self._bits = bits self.big_endian = bigEndian + self.binaryRepresentation = binaryRepresentation + self._convert_to = { + BinaryRepresentation.UNSIGNED : self._convert_to_unsigned , + BinaryRepresentation.SIGNED_MAGNITUDE : self._convert_to_signed_mag , + BinaryRepresentation.TWOS_COMPLEMENT : self._convert_to_twos_comp , + } + + self._convert_from = { + BinaryRepresentation.UNSIGNED : self._convert_from_unsigned , + BinaryRepresentation.SIGNED_MAGNITUDE : self._convert_from_signed_mag , + BinaryRepresentation.TWOS_COMPLEMENT : self._convert_from_twos_comp , + } if value is not None: self.assign(value) @@ -92,13 +110,129 @@ def assign(self, value): except ValueError: self.buff = value + def _convert_to_unsigned(self,x): + x = bin(x) + if x[0]=='-': + raise ValueError('Attempt to assigned negative number to unsigned BinaryValue') + return self._adjust_unsigned(x[2:]) + + def _convert_to_signed_mag(self,x): + x = bin(x) + if x[0]=='-': + binstr = self._adjust_signed_mag('1' + x[3:]) + else: + binstr = self._adjust_signed_mag('0' + x[2:]) + if self.big_endian: + binstr = binstr[::-1] + return binstr + + def _convert_to_twos_comp(self,x): + if x < 0: + ceil_log2 = int(ceil(log(abs(x),2))) + binstr = bin(2**(ceil_log2+1)+x)[2:] + binstr = self._adjust_twos_comp(binstr) + pass + else: + binstr = self._adjust_twos_comp('0' + bin(x)[2:]) + if self.big_endian: + binstr = binstr[::-1] + return binstr + + def _convert_from_unsigned(self,x): + return int(resolve(x), 2) + + def _convert_from_signed_mag(self,x): + rv = int(resolve(self._str[1:]), 2) + if self._str[0] == '1': + rv = rv * -1 + return rv + + def _convert_from_twos_comp(self,x): + if x[0]=='1': + binstr = x[1:] + binstr = self._invert(binstr) + rv = int(binstr,2) + 1 + if x[0] == '1': + rv = rv * -1 + else: + rv = int(resolve(x), 2) + return rv + + def _invert(self,x): + inverted = '' + for bit in x: + if bit == '0': + inverted = inverted + '1' + elif bit == '1': + inverted = inverted + '0' + else: + inverted = inverted + bit + return inverted + + def _adjust_unsigned (self,x): + if self._bits is None: + return + l = len(x) + if l < self._bits: + if self.big_endian: + rv = x + '0' * (self._bits-l) + else: + rv = '0' * (self._bits-l) + x + elif l > self._bits: + print "WARNING truncating value to match requested number of bits (%d -> %d)" % (l,self._bits) + if self.big_endian: + rv = x[l - self._bits:] + else: + rv = x[:l - self._bits] + return rv + + def _adjust_signed_mag (self,x): + """Pad/truncate the bit string to the correct length""" + if self._bits is None: + return + l = len(x) + if l < self._bits: + if self.big_endian: + rv = x[:-1] + '0' * (self._bits-1-l) + rv = rv + x[-1] + else: + rv = '0' * (self._bits-1-l) + x[1:] + rv = x[0] + rv + elif l > self._bits: + print "WARNING truncating value to match requested number of bits (%d -> %d)" % (l,self._bits) + if self.big_endian: + rv = x[l - self._bits:] + else: + rv = x[:-(l - self._bits)] + else: + rv = x + return rv + + def _adjust_twos_comp (self,x): + if self._bits is None: + return + l = len(x) + if l < self._bits: + if self.big_endian: + rv = x + x[-1] * (self._bits-l) + else: + rv = x[0] * (self._bits-l) + x + elif l > self._bits: + print "WARNING truncating value to match requested number of bits (%d -> %d)" % (l,self._bits) + if self.big_endian: + rv = x[l - self._bits:] + else: + rv = x[:-(l-self._bits)] + else: + rv = x + return rv + def get_value(self): """value is an integer representaion of the underlying vector""" - return int(resolve(self._str), 2) + return self._convert_from[self.binaryRepresentation](self._str) def set_value(self, integer): - self._str = bin(integer)[2:] - self._adjust() + self._str = self._convert_to[self.binaryRepresentation](integer) value = property(get_value, set_value, None, "Integer access to the value *** deprecated ***") integer = property(get_value, set_value, None, "Integer access to the value") @@ -123,6 +257,11 @@ def get_buff(self): buff = chr(val) + buff return buff + def get_hex_buff(self): + bstr = self.get_buff() + hstr = '%0*X' % ((len(bstr) + 3) // 4, int(bstr, 2)) + return hstr + def set_buff(self, buff): self._str = "" for char in buff: @@ -132,6 +271,20 @@ def set_buff(self, buff): self._str = "{0:08b}".format(ord(char)) + self._str self._adjust() + def _adjust(self): + """Pad/truncate the bit string to the correct length""" + if self._bits is None: + return + l = len(self._str) + if l < self._bits: + if self.big_endian: + self._str = self._str + "0" * (self._bits-l) + else: + self._str = "0" * (self._bits-l) + self._str + elif l > self._bits: + print "WARNING truncating value to match requested number of bits (%d)" % l + self._str = self._str[l - self._bits:] + buff = property(get_buff, set_buff, None, "Access to the value as a buffer") def get_binstr(self): @@ -147,19 +300,11 @@ def set_binstr(self, string): binstr = property(get_binstr, set_binstr, None, "Access to the binary string") - def _adjust(self): - """Pad/truncate the bit string to the correct length""" - if self._bits is None: - return - l = len(self._str) - if l < self._bits: - if self.big_endian: - self._str = self._str + "0" * (self._bits-l) - else: - self._str = "0" * (self._bits-l) + self._str - elif l > self._bits: - print "WARNING truncating value to match requested number of bits (%d)" % l - self._str = self._str[l - self._bits:] + def hex(self): + try: + return hex(self.get_value()) + except: + return hex(int(self.binstr,2)) def __le__(self, other): self.assign(other) @@ -232,7 +377,7 @@ def __imod__(self, other): return self def __lshift__(self, other): - return int(self) << int(other) + return int(self) << int(other) def __ilshift__(self, other): """Preserves X values""" @@ -246,7 +391,87 @@ def __irshift__(self, other): """Preserves X values""" self.binstr = self.binstr[-other:] + self.binstr[:-other] return self + + def __invert__(self): + """Preserves X values""" + self.binstr = self._invert(self.binstr) + return self + def __len__(self): + return len(self.binstr) + + def __getitem__(self, key): + ''' BinaryValue uses verilog/vhdl style slices as opposed to python style''' + if isinstance(key, slice): + first, second = key.start, key.stop + if self.big_endian: + if first < 0 or second < 0: + raise IndexError('BinaryValue does not support negative indices') + if second > self._bits-1: + raise IndexError('High index greater than number of bits.') + if first > second: + raise IndexError('Big Endian indices must be specified low to high') + _binstr = self.binstr[first:(second+1)] + pass + else: + if first < 0 or second < 0: + raise IndexError('BinaryValue does not support negative indices') + if first > self._bits-1: + raise IndexError('High index greater than number of bits.') + if second > first: + raise IndexError('Litte Endian indices must be specified high to low') + high = self._bits - second + low = self._bits - 1 - first + _binstr = self.binstr[low:high] + pass + else: + index = key + if index > self._bits-1: + raise IndexError('Index greater than number of bits.') + _binstr = self.binstr[index] + rv = BinaryValue(bits=len(_binstr),bigEndian=self.big_endian,binaryRepresentation=self.binaryRepresentation) + rv.set_binstr(_binstr) + return rv + + def __setitem__(self, key, val): + ''' BinaryValue uses verilog/vhdl style slices as opposed to python style''' + if not isinstance(val,str): + raise TypeError('BinaryValue slices only accept string values') + if isinstance(key, slice): + first, second = key.start, key.stop + if self.big_endian: + if first < 0 or second < 0: + raise IndexError('BinaryValue does not support negative indices') + if second > self._bits-1: + raise IndexError('High index greater than number of bits.') + if first > second: + raise IndexError('Big Endian indices must be specified low to high') + if len(val) > (second+1-first): + raise ValueError("String length must be equal to slice length") + slice_1 = self.binstr[:first] + slice_2 = self.binstr[second+1:] + self.binstr = slice_1 + val + slice_2 + pass + else: + if first < 0 or second < 0: + raise IndexError('BinaryValue does not support negative indices') + if first > self._bits-1: + raise IndexError('High index greater than number of bits.') + if second > first: + raise IndexError('Litte Endian indices must be specified high to low') + high = self._bits - second + low = self._bits - 1 - first + if len(val) > (high - low): + raise ValueError("String length must be equal to slice length") + slice_1 = self.binstr[:low] + slice_2 = self.binstr[high:] + self.binstr = slice_1 + val + slice_2 + pass + else: + index = key + if index > self._bits-1: + raise IndexError('Index greater than number of bits.') + self.binstr = self.binstr[:index] + val + self.binstr[index+1:] if __name__ == "__main__": import doctest diff --git a/cocotb/drivers/ad9361.py b/cocotb/drivers/ad9361.py new file mode 100644 index 00000000..5bf01c91 --- /dev/null +++ b/cocotb/drivers/ad9361.py @@ -0,0 +1,185 @@ +''' +Created on Aug 24, 2014 + +@author: msnook +''' + +import cocotb +from cocotb.triggers import Timer, RisingEdge, ReadOnly, Lock, Event +from cocotb.bus import Bus +from cocotb.result import ReturnValue +from cocotb.drivers import BusDriver +from cocotb.binary import BinaryValue,BinaryRepresentation + +from collections import deque + +class AD9361(BusDriver): + ''' + classdocs + ''' + + def __init__(self,dut,rx_channels=1, tx_channels=1, tx_clock_half_period=16276, rx_clock_half_period=16276,loopback_queue_maxlen=16): + ''' + Constructor + ''' + self.dut = dut + self.tx_clock_half_period = tx_clock_half_period + self.rx_clock_half_period = rx_clock_half_period + self.rx_frame_asserted = False + self.tx_frame_asserted = False + self.lbqi = deque() + self.lbqq = deque() + cocotb.fork(self._rx_clock()) + self.got_tx = Event("Got tx event") + + @cocotb.coroutine + def _rx_clock(self): + t = Timer(self.rx_clock_half_period) + while True: + self.dut.rx_clk_in_p <= 1 + self.dut.rx_clk_in_n <= 0 + yield t + self.dut.rx_clk_in_p <= 0 + self.dut.rx_clk_in_n <= 1 + yield t + + def send_data(self,i_data,q_data,i_data2=None,q_data2=None,binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT): + print binaryRepresentation + cocotb.fork(self.rx_data_to_ad9361(i_data,q_data,i_data2,q_data2,binaryRepresentation)) + + @cocotb.coroutine + def rx_data_to_ad9361(self,i_data,q_data,i_data2=None,q_data2=None,binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT): + i_bin_val = BinaryValue(bits=12, bigEndian=False,binaryRepresentation=binaryRepresentation) + q_bin_val = BinaryValue(bits=12, bigEndian=False,binaryRepresentation=binaryRepresentation) + index = 0 + if i_data2==None and q_data2==None: + while True: + yield RisingEdge(self.dut.rx_clk_in_p) + if self.rx_frame_asserted: + self.dut.rx_data_in_p <= i_bin_val[5:0] + self.dut.rx_data_in_n <= ~i_bin_val[5:0] + self.rx_frame_asserted = False + self.dut.rx_frame_in_p <= 0 + self.dut.rx_frame_in_n <= 1 + else: + if index < len(i_data): + i_bin_val.set_value(i_data[index]) + q_bin_val.set_value(q_data[index]) + index += 1 + else: + return + self.dut.rx_data_in_p <= i_bin_val[11:6] + self.dut.rx_data_in_n <= ~i_bin_val[11:6] + self.rx_frame_asserted = True + self.dut.rx_frame_in_p <= 1 + self.dut.rx_frame_in_n <= 0 + yield RisingEdge(self.dut.rx_clk_in_n) + if self.rx_frame_asserted: + self.dut.rx_data_in_p <= q_bin_val[11:6] + self.dut.rx_data_in_n <= ~q_bin_val[11:6] + else: + self.dut.rx_data_in_p <= q_bin_val[5:0] + self.dut.rx_data_in_n <= ~q_bin_val[5:0] + else: + I_SEND_HIGH = True + Q_SEND_HIGH = True + channel = 1 + while True: + yield RisingEdge(self.dut.rx_clk_in_p) + if I_SEND_HIGH: + self.dut.rx_data_in_p <= i_bin_val[11:6] + self.dut.rx_data_in_n <= ~i_bin_val[11:6] + I_SEND_HIGH = False + if channel == 1: + self.dut.rx_frame_in_p <= 1 + self.dut.rx_frame_in_n <= 0 + elif channel == 2: + self.dut.rx_frame_in_p <= 0 + self.dut.rx_frame_in_n <= 1 + else: + self.dut.rx_data_in_p <= i_bin_val[5:0] + self.dut.rx_data_in_n <= ~i_bin_val[5:0] + I_SEND_HIGH = True + yield RisingEdge(self.dut.rx_clk_in_n) + if Q_SEND_HIGH: + self.dut.rx_data_in_p <= q_bin_val[5:0] + self.dut.rx_data_in_n <= ~q_bin_val[5:0] + Q_SEND_HIGH = False + else: + self.dut.rx_data_in_p <= q_bin_val[11:6] + self.dut.rx_data_in_n <= ~q_bin_val[11:6] + Q_SEND_HIGH = True + if index < len(i_data): + if channel == 1: + i_bin_val.set_value(i_data[index]) + q_bin_val.set_value(q_data[index]) + channel = 2 + elif channel == 2: + i_bin_val.set_value(i_data2[index]) + q_bin_val.set_value(q_data2[index]) + channel = 1 + index += 1 + else: + return + + @cocotb.coroutine + def _tx_data_from_ad9361(self): + i_bin_val = BinaryValue(bits=12, bigEndian=False) + q_bin_val = BinaryValue(bits=12, bigEndian=False) + while True: + yield RisingEdge(self.dut.tx_clk_out_p) + if self.dut.tx_frame_out_p.value.integer==1: + q_bin_val[11:6] = self.dut.tx_data_out_p.value.get_binstr() + else: + q_bin_val[5:0] = self.dut.tx_data_out_p.value.get_binstr() + yield RisingEdge(self.dut.tx_clk_out_n) + if self.dut.tx_frame_out_p.value.integer==1: + i_bin_val[11:6] = self.dut.tx_data_out_p.value.get_binstr() + else: + i_bin_val[5:0] = self.dut.tx_data_out_p.value.get_binstr() + #print "i_data",i_bin_val.get_value() + #print "q_data",q_bin_val.get_value() + self.lbqi.append(i_bin_val) + self.lbqq.append(q_bin_val) + self.got_tx.set([i_bin_val,q_bin_val]) + + @cocotb.coroutine + def _ad9361_tx_to_rx_loopback(self): + cocotb.fork(self._tx_data_from_ad9361()) + i_bin_val = BinaryValue(bits=12, bigEndian=False) + q_bin_val = BinaryValue(bits=12, bigEndian=False) + while True: + yield RisingEdge(self.dut.rx_clk_in_p) + if self.rx_frame_asserted: + self.dut.rx_data_in_p <= i_bin_val[5:0] + self.dut.rx_data_in_n <= ~i_bin_val[5:0] + self.rx_frame_asserted = False + self.dut.rx_frame_in_p <= 0 + self.dut.rx_frame_in_n <= 1 + else: + if len(self.lbqi) > 0: + i_bin_val = self.lbqi.popleft() + else: + i_bin_val.set_value(0) + if len(self.lbqq) > 0: + q_bin_val = self.lbqq.popleft() + else: + q_bin_val.set_value(0) + self.dut.rx_data_in_p <= i_bin_val[11:6] + self.dut.rx_data_in_n <= ~i_bin_val[11:6] + self.rx_frame_asserted = True + self.dut.rx_frame_in_p <= 1 + self.dut.rx_frame_in_n <= 0 + yield RisingEdge(self.dut.rx_clk_in_n) + if self.rx_frame_asserted: + self.dut.rx_data_in_p <= q_bin_val[11:6] + self.dut.rx_data_in_n <= ~q_bin_val[11:6] + else: + self.dut.rx_data_in_p <= q_bin_val[5:0] + self.dut.rx_data_in_n <= ~q_bin_val[5:0] + + def ad9361_tx_to_rx_loopback(self): + cocotb.fork(self._ad9361_tx_to_rx_loopback()) + + def tx_data_from_ad9361(self): + cocotb.fork(self._tx_data_from_ad9361()) diff --git a/cocotb/drivers/amba.py b/cocotb/drivers/amba.py index a35278c3..a763b9c3 100644 --- a/cocotb/drivers/amba.py +++ b/cocotb/drivers/amba.py @@ -30,19 +30,22 @@ from cocotb.triggers import RisingEdge, ReadOnly, Lock from cocotb.drivers import BusDriver from cocotb.result import ReturnValue +from cocotb.binary import BinaryValue +import binascii +import array +import struct +import pydevd class AXIReadError(Exception): pass - class AXI4LiteMaster(BusDriver): """ AXI4-Lite Master TODO: Kill all pending transactions if reset is asserted... """ - _signals = ["ARESETN", - "AWVALID", "AWADDR", "AWREADY", # Write address channel + _signals = ["AWVALID", "AWADDR", "AWREADY", # Write address channel "WVALID", "WREADY", "WDATA", "WSTRB", # Write data channel "BVALID", "BREADY", "BRESP", # Write response channel "ARVALID", "ARADDR", "ARREADY", # Read address channel @@ -115,9 +118,13 @@ def write(self, address, value, byte_enable=0xf, address_latency=0, data_latency The *_latency KWargs allow control over the delta """ + + c_addr = cocotb.fork(self._send_write_address(address, delay=address_latency)) c_data = cocotb.fork(self._send_write_data(value, byte_enable=byte_enable, delay=data_latency)) + + if c_addr: yield c_addr.join() if c_data: @@ -130,7 +137,9 @@ def write(self, address, value, byte_enable=0xf, address_latency=0, data_latency result = self.bus.BRESP.value break yield RisingEdge(self.clock) - + + yield RisingEdge(self.clock) + if int(result): raise AXIReadError("Write to address 0x%08x failed with BRESP: %d" %( address, int(result))) @@ -171,3 +180,227 @@ def read(self, address, sync=True): address, int(result))) raise ReturnValue(data) + + + +class AXI4Slave(BusDriver): + ''' + AXI4 Slave + + Monitors an internal memory and handles read and write requests. + ''' + _signals = [ + "ACLK" , # input + + "ARREADY" , # input + "ARVALID" , # output + "ARADDR" , # output [31 : 0] + + "ARLEN" , # output [3 : 0] + "ARSIZE" , # output [2 : 0] + "ARBURST" , # output [1 : 0] + "ARPROT" , # output [2 : 0] + + "RREADY" , # output + "RVALID" , # input + "RDATA" , # input [63 : 0] + "RLAST" , # input + + "AWREADY" , # input + "AWADDR" , # output [31 : 0] + "AWVALID" , # output + "AWLEN" , # output [3 : 0] + "AWPROT" , # output [2 : 0] + "AWSIZE" , # output [2 : 0] + "AWBURST" , # output [1 : 0] + + "WREADY" , # input + "WLAST" , # output + "WVALID" , # output + "WDATA" , # output [63 : 0] + "WSTRB" , # output [7 : 0] + + "BVALID" , # input + "BREADY" , # output + "BRESP" , # input [1 : 0] + + "RRESP" , # input [1 : 0] + + "RCOUNT" , # input [7 : 0] + "WCOUNT" , # input [7 : 0] + "RACOUNT" , # input [2 : 0] + "WACOUNT" , # input [5 : 0] + + "RDISSUECAP1_EN" , # output + "WRISSUECAP1_EN" , # output + "ARLOCK" , # output [1 : 0] + "AWLOCK" , # output [1 : 0] + "ARCACHE" , # output [3 : 0] + "AWCACHE" , # output [3 : 0] + "ARQOS" , # output [3 : 0] + "AWQOS" , # output [3 : 0] + "ARID" , # output [5 : 0] + "AWID" , # output [5 : 0] + "BID" , # input [5 : 0] + "RID" , # input [5 : 0] + "WID" , # output [5 : 0] + ] + + def __init__(self, entity, name, clock, memory, reset=None, reset_n=None, callback=None, event=None, big_endian=False, print_debug=False): + ''' + Constructor + ''' + BusDriver.__init__(self,entity,name,clock) + self.clock = clock + + self.big_endain = big_endian + self.bus.ARREADY.setimmediatevalue(1) + self.bus.RVALID.setimmediatevalue(0) + self.bus.RLAST.setimmediatevalue(0) + self.bus.AWREADY.setimmediatevalue(1) + + self._memory = memory + self.print_debug = print_debug + +# self._memory = array.array('B',((i % 256) for i in range(MEMORY_SIZE_IN_BYTES))) + + self.write_address_busy = Lock("%s_wabusy" % name) + self.read_address_busy = Lock("%s_rabusy" % name) + self.write_data_busy = Lock("%s_wbusy" % name) + + cocotb.fork(self._read_data()) + cocotb.fork(self._write_data()) + + def _size_to_bytes_in_beat(self,AxSIZE): + bytes_in_transfer = 0 + if AxSIZE == 0: + bytes_in_transfer = 1 + elif AxSIZE == 1: + bytes_in_transfer = 2 + elif AxSIZE == 2: + bytes_in_transfer = 4 + elif AxSIZE == 3: + bytes_in_transfer = 8 + elif AxSIZE == 4: + bytes_in_transfer = 16 + elif AxSIZE == 5: + bytes_in_transfer = 32 + elif AxSIZE == 6: + bytes_in_transfer = 64 + elif AxSIZE == 7: + bytes_in_transfer = 128 + else: + bytes_in_transfer = None + + return bytes_in_transfer + + @cocotb.coroutine + def _write_data(self): + clock_re = RisingEdge(self.clock) + + while True: + while True: + self.bus.WREADY <= 0 + yield ReadOnly() + if self.bus.AWVALID.value: + self.bus.WREADY <= 1 + break + yield clock_re + + + yield ReadOnly() + _awaddr = self.bus.AWADDR.value + _awlen = self.bus.AWLEN.value + _awsize = self.bus.AWSIZE.value + _awburst = self.bus.AWBURST.value + _awprot = self.bus.AWPROT.value + + burst_length = _awlen + 1 + bytes_in_beat = self._size_to_bytes_in_beat(_awsize) + + word = BinaryValue(bits=bytes_in_beat*8, bigEndian=self.big_endain) + + if self.print_debug: + print "AWADDR %d" % _awaddr + print "AWLEN %d" % _awlen + print "AWSIZE %d" % _awsize + print "AWBURST %d" % _awburst + print "BURST_LENGTH %d" % burst_length + print "Bytes in beat %d" % bytes_in_beat + + burst_count = burst_length + + + yield clock_re + + if bytes_in_beat != 8: + print "Hmm bytes_in_beat not 8?" + + while True: + if self.bus.WVALID.value: + word = self.bus.WDATA.value + word.big_endian = self.big_endain + if self.print_debug: + print binascii.hexlify(word.get_buff()) + " to " + str(_awaddr+((burst_length-burst_count)*bytes_in_beat)) + " : " + str(_awaddr+(((burst_length-burst_count)+1)*bytes_in_beat)) + self._memory[_awaddr+((burst_length-burst_count)*bytes_in_beat):_awaddr+(((burst_length-burst_count)+1)*bytes_in_beat)] = array.array('B',word.get_buff()) + burst_count -= 1 + if burst_count == 0: + break + yield clock_re + + + @cocotb.coroutine + def _read_data(self): + clock_re = RisingEdge(self.clock) + + while True: + while True: + yield ReadOnly() + if self.bus.ARVALID.value: + break + yield clock_re + + yield ReadOnly() + _araddr = self.bus.ARADDR.value + _arlen = self.bus.ARLEN.value + _arsize = self.bus.ARSIZE.value + _arburst = self.bus.ARBURST.value + _arprot = self.bus.ARPROT.value + + burst_length = _arlen + 1 + bytes_in_beat = self._size_to_bytes_in_beat(_arsize) + + word = BinaryValue(bits=bytes_in_beat*8, bigEndian=self.big_endain) + + if self.print_debug: + print "ARADDR %d" % _araddr + print "ARLEN %d" % _arlen + print "ARSIZE %d" % _arsize + print "ARBURST %d" % _arburst + print "BURST_LENGTH %d" % burst_length + print "Bytes in beat %d" % bytes_in_beat + + burst_count = burst_length + + + yield clock_re + + if bytes_in_beat != 8: + print "Hmm bytes_in_beat not 8?" + + while True: + self.bus.RVALID <= 1 + yield ReadOnly() + if self.bus.RREADY.value: + word.buff = self._memory[_araddr+((burst_length-burst_count)*bytes_in_beat):_araddr+(((burst_length-burst_count)+1)*bytes_in_beat)].tostring() + if self.print_debug: + print binascii.hexlify(self._memory[_araddr+((burst_length-burst_count)*bytes_in_beat):_araddr+(((burst_length-burst_count)+1)*bytes_in_beat)]) + self.bus.RDATA <= word + if burst_count == 1: + self.bus.RLAST <= 1 + yield clock_re + burst_count -= 1 + self.bus.RLAST <= 0 + if burst_count == 0: + break + diff --git a/lib/gpi_impl/gpi_vpi.c b/lib/gpi_impl/gpi_vpi.c index dc9b9456..f59f0e27 100644 --- a/lib/gpi_impl/gpi_vpi.c +++ b/lib/gpi_impl/gpi_vpi.c @@ -32,6 +32,9 @@ #define VPI_CHECKING 1 +//#define FENTER LOG_INFO("Entering %s",__FUNCTION__); +//#define FEXIT LOG_INFO("Exiting %s",__FUNCTION__); + static gpi_sim_hdl sim_init_cb; static gpi_sim_hdl sim_finish_cb; From 403172c90cf0c73b752017f07702a5114633358e Mon Sep 17 00:00:00 2001 From: Chiggs Date: Thu, 4 Sep 2014 20:06:13 +0100 Subject: [PATCH 0580/2656] Cleanup: Ensure Modelsim exits when not running in GUI mode --- makefiles/simulators/Makefile.modelsim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index 3dd56fb8..ae7d7ff5 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -38,18 +38,20 @@ endif ifeq ($(GUI),1) SIM_CMD = vsim -gui +VSIM_ARGS += -onfinish stop else SIM_CMD = vsim -c +VSIM_ARGS += -onfinish exit endif runsim.do : $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) echo "vlib work" > $@ echo "vlog -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(VERILOG_SOURCES)" >> $@ - echo "vsim -pli libgpi.so -gblso libgpi.so $(VSIM_ARGS) work.$(TOPLEVEL) -onfinish stop" >> $@ + echo "vsim -pli libgpi.so $(VSIM_ARGS) work.$(TOPLEVEL)" >> $@ ifneq ($(GUI),1) echo "run -all" >> $@ - echo "exit" >> $@ + echo "quit" >> $@ endif From f3008e5478d6b806552b6c664f65e28ca97086ac Mon Sep 17 00:00:00 2001 From: Darrell Harmon Date: Thu, 11 Sep 2014 09:31:10 -0600 Subject: [PATCH 0581/2656] add BinaryValue.signed_integer to allow easier use of signed values --- cocotb/binary.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cocotb/binary.py b/cocotb/binary.py index fbe29fe5..e83be445 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -40,6 +40,7 @@ class BinaryValue(object): The underlying value can be set or accessed using three aliasing attributes, - BinaryValue.integer is an integer + - BinaryValue.signed_integer is a signed integer - BinaryValue.binstr is a string of "01xXzZ" - BinaryValue.buff is a binary buffer of bytes @@ -96,12 +97,23 @@ def get_value(self): """value is an integer representaion of the underlying vector""" return int(resolve(self._str), 2) + def get_value_signed(self): + """value is an signed integer representaion of the underlying vector""" + ival = int(resolve(self._str), 2) + bits = len(self._str) + signbit = (1 << (bits - 1)) + if (ival & signbit) == 0: + return ival + else: + return -1 * (int(~ival+1) & (signbit - 1)) + def set_value(self, integer): self._str = bin(integer)[2:] self._adjust() value = property(get_value, set_value, None, "Integer access to the value *** deprecated ***") integer = property(get_value, set_value, None, "Integer access to the value") + signed_integer = property(get_value_signed, set_value, None, "Signed integer access to the value") def get_buff(self): """Attribute self.buff represents the value as a binary string buffer From 495706c5545ab33608bd8e44e1514460ecc59912 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Thu, 11 Sep 2014 21:04:37 +0100 Subject: [PATCH 0582/2656] Cleanup: Tidy-up of new AXI4Slave driver (untested...) --- cocotb/drivers/amba.py | 211 ++++++++++++++--------------------------- 1 file changed, 70 insertions(+), 141 deletions(-) diff --git a/cocotb/drivers/amba.py b/cocotb/drivers/amba.py index a763b9c3..8f7c7f27 100644 --- a/cocotb/drivers/amba.py +++ b/cocotb/drivers/amba.py @@ -34,8 +34,7 @@ import binascii import array -import struct -import pydevd + class AXIReadError(Exception): pass @@ -186,113 +185,55 @@ def read(self, address, sync=True): class AXI4Slave(BusDriver): ''' AXI4 Slave - + Monitors an internal memory and handles read and write requests. ''' _signals = [ - "ACLK" , # input - - "ARREADY" , # input - "ARVALID" , # output - "ARADDR" , # output [31 : 0] - - "ARLEN" , # output [3 : 0] - "ARSIZE" , # output [2 : 0] - "ARBURST" , # output [1 : 0] - "ARPROT" , # output [2 : 0] - - "RREADY" , # output - "RVALID" , # input - "RDATA" , # input [63 : 0] - "RLAST" , # input - - "AWREADY" , # input - "AWADDR" , # output [31 : 0] - "AWVALID" , # output - "AWLEN" , # output [3 : 0] - "AWPROT" , # output [2 : 0] - "AWSIZE" , # output [2 : 0] - "AWBURST" , # output [1 : 0] - - "WREADY" , # input - "WLAST" , # output - "WVALID" , # output - "WDATA" , # output [63 : 0] - "WSTRB" , # output [7 : 0] - - "BVALID" , # input - "BREADY" , # output - "BRESP" , # input [1 : 0] - - "RRESP" , # input [1 : 0] - - "RCOUNT" , # input [7 : 0] - "WCOUNT" , # input [7 : 0] - "RACOUNT" , # input [2 : 0] - "WACOUNT" , # input [5 : 0] - - "RDISSUECAP1_EN" , # output - "WRISSUECAP1_EN" , # output - "ARLOCK" , # output [1 : 0] - "AWLOCK" , # output [1 : 0] - "ARCACHE" , # output [3 : 0] - "AWCACHE" , # output [3 : 0] - "ARQOS" , # output [3 : 0] - "AWQOS" , # output [3 : 0] - "ARID" , # output [5 : 0] - "AWID" , # output [5 : 0] - "BID" , # input [5 : 0] - "RID" , # input [5 : 0] - "WID" , # output [5 : 0] + "ARREADY", "ARVALID", "ARADDR", # Read address channel + "ARLEN", "ARSIZE", "ARBURST", "ARPROT", + + "RREADY", "RVALID", "RDATA", "RLAST",# Read response channel + + "AWREADY", "AWADDR", "AWVALID", # Write address channel + "AWPROT", "AWSIZE", "AWBURST", "AWLEN", + + "WREADY", "WVALID", "WDATA", + + ] + + # Not currently supported by this driver + _optional_signals = [ + "WLAST", "WSTRB", + "BVALID", "BREADY", "BRESP", "RRESP", + "RCOUNT", "WCOUNT", "RACOUNT", "WACOUNT", + "ARLOCK", "AWLOCK", "ARCACHE", "AWCACHE", + "ARQOS", "AWQOS", "ARID", "AWID", + "BID", "RID", "WID" ] - def __init__(self, entity, name, clock, memory, reset=None, reset_n=None, callback=None, event=None, big_endian=False, print_debug=False): - ''' - Constructor - ''' + def __init__(self, entity, name, clock, memory, callback=None, event=None, big_endian=False): + BusDriver.__init__(self,entity,name,clock) self.clock = clock - + self.big_endain = big_endian self.bus.ARREADY.setimmediatevalue(1) self.bus.RVALID.setimmediatevalue(0) self.bus.RLAST.setimmediatevalue(0) self.bus.AWREADY.setimmediatevalue(1) - self._memory = memory - self.print_debug = print_debug - -# self._memory = array.array('B',((i % 256) for i in range(MEMORY_SIZE_IN_BYTES))) - + self.write_address_busy = Lock("%s_wabusy" % name) self.read_address_busy = Lock("%s_rabusy" % name) self.write_data_busy = Lock("%s_wbusy" % name) - + cocotb.fork(self._read_data()) cocotb.fork(self._write_data()) - + def _size_to_bytes_in_beat(self,AxSIZE): - bytes_in_transfer = 0 - if AxSIZE == 0: - bytes_in_transfer = 1 - elif AxSIZE == 1: - bytes_in_transfer = 2 - elif AxSIZE == 2: - bytes_in_transfer = 4 - elif AxSIZE == 3: - bytes_in_transfer = 8 - elif AxSIZE == 4: - bytes_in_transfer = 16 - elif AxSIZE == 5: - bytes_in_transfer = 32 - elif AxSIZE == 6: - bytes_in_transfer = 64 - elif AxSIZE == 7: - bytes_in_transfer = 128 - else: - bytes_in_transfer = None - - return bytes_in_transfer + if AxSIZE < 7: + return 2**AxSIZE + return None @cocotb.coroutine def _write_data(self): @@ -306,48 +247,42 @@ def _write_data(self): self.bus.WREADY <= 1 break yield clock_re - - + yield ReadOnly() - _awaddr = self.bus.AWADDR.value - _awlen = self.bus.AWLEN.value - _awsize = self.bus.AWSIZE.value - _awburst = self.bus.AWBURST.value - _awprot = self.bus.AWPROT.value - + _awaddr = int(self.bus.AWADDR) + _awlen = int(self.bus.AWLEN) + _awsize = int(self.bus.AWSIZE) + _awburst = int(self.bus.AWBURST) + _awprot = int(self.bus.AWPROT) + burst_length = _awlen + 1 bytes_in_beat = self._size_to_bytes_in_beat(_awsize) - + word = BinaryValue(bits=bytes_in_beat*8, bigEndian=self.big_endain) - - if self.print_debug: - print "AWADDR %d" % _awaddr - print "AWLEN %d" % _awlen - print "AWSIZE %d" % _awsize - print "AWBURST %d" % _awburst - print "BURST_LENGTH %d" % burst_length - print "Bytes in beat %d" % bytes_in_beat - - burst_count = burst_length + if __debug__: + self.log.debug( + "AWADDR %d\n" % _awaddr + + "AWLEN %d\n" % _awlen + + "AWSIZE %d\n" % _awsize + + "AWBURST %d\n" % _awburst + + "BURST_LENGTH %d\n" % burst_length + + "Bytes in beat %d\n" % bytes_in_beat) + + burst_count = burst_length yield clock_re - - if bytes_in_beat != 8: - print "Hmm bytes_in_beat not 8?" - + while True: if self.bus.WVALID.value: word = self.bus.WDATA.value word.big_endian = self.big_endain - if self.print_debug: - print binascii.hexlify(word.get_buff()) + " to " + str(_awaddr+((burst_length-burst_count)*bytes_in_beat)) + " : " + str(_awaddr+(((burst_length-burst_count)+1)*bytes_in_beat)) self._memory[_awaddr+((burst_length-burst_count)*bytes_in_beat):_awaddr+(((burst_length-burst_count)+1)*bytes_in_beat)] = array.array('B',word.get_buff()) burst_count -= 1 if burst_count == 0: break yield clock_re - + @cocotb.coroutine def _read_data(self): @@ -359,42 +294,37 @@ def _read_data(self): if self.bus.ARVALID.value: break yield clock_re - + yield ReadOnly() - _araddr = self.bus.ARADDR.value - _arlen = self.bus.ARLEN.value - _arsize = self.bus.ARSIZE.value - _arburst = self.bus.ARBURST.value - _arprot = self.bus.ARPROT.value - + _araddr = int(self.bus.ARADDR) + _arlen = int(self.bus.ARLEN) + _arsize = int(self.bus.ARSIZE) + _arburst = int(self.bus.ARBURST) + _arprot = int(self.bus.ARPROT) + burst_length = _arlen + 1 bytes_in_beat = self._size_to_bytes_in_beat(_arsize) - + word = BinaryValue(bits=bytes_in_beat*8, bigEndian=self.big_endain) - - if self.print_debug: - print "ARADDR %d" % _araddr - print "ARLEN %d" % _arlen - print "ARSIZE %d" % _arsize - print "ARBURST %d" % _arburst - print "BURST_LENGTH %d" % burst_length - print "Bytes in beat %d" % bytes_in_beat - + + if __debug__: + self.log.debug( + "AWADDR %d\n" % _awaddr + + "AWLEN %d\n" % _awlen + + "AWSIZE %d\n" % _awsize + + "AWBURST %d\n" % _awburst + + "BURST_LENGTH %d\n" % burst_length + + "Bytes in beat %d\n" % bytes_in_beat) + burst_count = burst_length - yield clock_re - - if bytes_in_beat != 8: - print "Hmm bytes_in_beat not 8?" - + while True: self.bus.RVALID <= 1 yield ReadOnly() if self.bus.RREADY.value: word.buff = self._memory[_araddr+((burst_length-burst_count)*bytes_in_beat):_araddr+(((burst_length-burst_count)+1)*bytes_in_beat)].tostring() - if self.print_debug: - print binascii.hexlify(self._memory[_araddr+((burst_length-burst_count)*bytes_in_beat):_araddr+(((burst_length-burst_count)+1)*bytes_in_beat)]) self.bus.RDATA <= word if burst_count == 1: self.bus.RLAST <= 1 @@ -403,4 +333,3 @@ def _read_data(self): self.bus.RLAST <= 0 if burst_count == 0: break - From 11f9ac0de6c8c98d14af2f510d71ebbb9ef36cfc Mon Sep 17 00:00:00 2001 From: Chiggs Date: Thu, 11 Sep 2014 21:06:58 +0100 Subject: [PATCH 0583/2656] Cleanup: Revert unneccessary change from pull request --- lib/gpi_impl/gpi_vpi.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/gpi_impl/gpi_vpi.c b/lib/gpi_impl/gpi_vpi.c index f59f0e27..dc9b9456 100644 --- a/lib/gpi_impl/gpi_vpi.c +++ b/lib/gpi_impl/gpi_vpi.c @@ -32,9 +32,6 @@ #define VPI_CHECKING 1 -//#define FENTER LOG_INFO("Entering %s",__FUNCTION__); -//#define FEXIT LOG_INFO("Exiting %s",__FUNCTION__); - static gpi_sim_hdl sim_init_cb; static gpi_sim_hdl sim_finish_cb; From 06d82393ad7d2da73f2a72d5c8756a7a3ace1c3b Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Fri, 26 Sep 2014 14:43:53 +1000 Subject: [PATCH 0584/2656] IUS : plusargs makefile variable wasn't getting passed to irun commandline - running examples/plusargs with SIM=ius didn't show the plusargs defined in examples/plusargs/Makefile --- makefiles/simulators/Makefile.ius | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 721f91e2..2635ce8a 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -58,7 +58,7 @@ endif results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - irun $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(HDL_SOURCES) + irun $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(HDL_SOURCES) $(PLUSARGS) clean:: -rm -rf $(SIM_BUILD) From 31f3fc39bedde1c293e614fc2055684b057b9f97 Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Fri, 26 Sep 2014 14:44:39 +1000 Subject: [PATCH 0585/2656] examples : making the source of the print/display statements a little clearer - on my first run through the quickstart guide it wasn't clear to me where the statements were coming from in the plusargs test. Making it a little more explicit as plusargs is the first test that runs when running "make all" from a fresh clone. --- examples/plusargs/plusargs.py | 2 +- examples/plusargs/tb_top.v | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/plusargs/plusargs.py b/examples/plusargs/plusargs.py index 63a6212f..d80e7f53 100644 --- a/examples/plusargs/plusargs.py +++ b/examples/plusargs/plusargs.py @@ -42,7 +42,7 @@ def plusargs_test(dut): yield Timer(10000) for name in cocotb.plusargs: - print name, cocotb.plusargs[name] + print "COCOTB:", name, cocotb.plusargs[name] yield Timer(10000) diff --git a/examples/plusargs/tb_top.v b/examples/plusargs/tb_top.v index a9fc5b12..613c0252 100644 --- a/examples/plusargs/tb_top.v +++ b/examples/plusargs/tb_top.v @@ -33,10 +33,10 @@ module tb_top ; integer result; initial begin - $display("Plusargs test"); - result = $value$plusargs("foo=%s", foo_string); - $display("Plusarg foo has value %0s", foo_string); - #1 $display("Test running"); + $display("SIM: Plusargs test"); + result = $value$plusargs("foo=%s", foo_string); + $display("SIM: Plusarg foo has value %0s", foo_string); + #1 $display("SIM: Test running"); end endmodule //: tb_top From 8a59c9b81aa27d5733134cb7e664b122f6308149 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 28 Sep 2014 16:02:20 +0100 Subject: [PATCH 0586/2656] Issue #161: Add a mixed language example to assist in C work --- examples/endian_swapper/hdl/endian_swapper.sv | 6 +- .../endian_swapper/hdl/endian_swapper.vhdl | 4 +- examples/endian_swapper/tests/Makefile | 3 +- examples/mixed_language/hdl/toplevel.sv | 104 ++++++++++++++++++ examples/mixed_language/tests/Makefile | 29 +++++ .../tests/test_mixed_language.py | 27 +++++ 6 files changed, 167 insertions(+), 6 deletions(-) create mode 100644 examples/mixed_language/hdl/toplevel.sv create mode 100644 examples/mixed_language/tests/Makefile create mode 100644 examples/mixed_language/tests/test_mixed_language.py diff --git a/examples/endian_swapper/hdl/endian_swapper.sv b/examples/endian_swapper/hdl/endian_swapper.sv index 9ef4bd14..4a307a65 100644 --- a/examples/endian_swapper/hdl/endian_swapper.sv +++ b/examples/endian_swapper/hdl/endian_swapper.sv @@ -44,9 +44,9 @@ Exposes 2 32-bit registers via the Avalon-MM interface */ -module endian_swapper #( +module endian_swapper_sv #( parameter DATA_BYTES = 8 -) ( +) ( input clk, input reset_n, @@ -173,7 +173,7 @@ end `ifdef COCOTB_SIM initial begin $dumpfile ("waveform.vcd"); - $dumpvars (0,endian_swapper); + $dumpvars (0,endian_swapper_sv); #1; end `endif diff --git a/examples/endian_swapper/hdl/endian_swapper.vhdl b/examples/endian_swapper/hdl/endian_swapper.vhdl index 2416e826..096239de 100644 --- a/examples/endian_swapper/hdl/endian_swapper.vhdl +++ b/examples/endian_swapper/hdl/endian_swapper.vhdl @@ -44,7 +44,7 @@ library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; -entity endian_swapper is +entity endian_swapper_vhdl is generic ( DATA_BYTES : integer := 8); port ( @@ -75,7 +75,7 @@ entity endian_swapper is ); end; -architecture impl of endian_swapper is +architecture impl of endian_swapper_vhdl is function byteswap(data : in std_ulogic_vector(63 downto 0)) return std_ulogic_vector is begin diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index b53dd21b..6cfc00d2 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -32,7 +32,6 @@ GPI_IMPL ?= vpi -TOPLEVEL = endian_swapper PWD=$(shell pwd) COCOTB=$(PWD)/../../.. @@ -40,10 +39,12 @@ COCOTB=$(PWD)/../../.. ifeq ($(GPI_IMPL),vpi) VERILOG_SOURCES = $(PWD)/../hdl/endian_swapper.sv + TOPLEVEL = endian_swapper_sv endif ifeq ($(GPI_IMPL),vhpi) VHDL_SOURCES = $(PWD)/../hdl/endian_swapper.vhdl + TOPLEVEL = endian_swapper_vhdl endif MODULE=test_endian_swapper diff --git a/examples/mixed_language/hdl/toplevel.sv b/examples/mixed_language/hdl/toplevel.sv new file mode 100644 index 00000000..f8f6e87f --- /dev/null +++ b/examples/mixed_language/hdl/toplevel.sv @@ -0,0 +1,104 @@ +// Example using mixed-language simulation +// +// Here we have a SystemVerilog toplevel that instantiates both SV and VHDL +// sub entities +module endian_swapper_mixed #( + parameter DATA_BYTES = 8 +) ( + input clk, + input reset_n, + + input [DATA_BYTES*8-1:0] stream_in_data, + input [$clog2(DATA_BYTES)-1:0] stream_in_empty, + input stream_in_valid, + input stream_in_startofpacket, + input stream_in_endofpacket, + output reg stream_in_ready, + + output reg [DATA_BYTES*8-1:0] stream_out_data, + output reg [$clog2(DATA_BYTES)-1:0] stream_out_empty, + output reg stream_out_valid, + output reg stream_out_startofpacket, + output reg stream_out_endofpacket, + input stream_out_ready, + + input [1:0] csr_address, + output reg [31:0] csr_readdata, + output reg csr_readdatavalid, + input csr_read, + input csr_write, + output reg csr_waitrequest, + input [31:0] csr_writedata +); + +logic [DATA_BYTES*8-1:0] sv_to_vhdl_data; +logic [$clog2(DATA_BYTES)-1:0] sv_to_vhdl_empty; +logic sv_to_vhdl_valid; +logic sv_to_vhdl_startofpacket; +logic sv_to_vhdl_endofpacket; +logic sv_to_vhdl_ready; + + +endian_swapper_sv #( + .DATA_BYTES (DATA_BYTES) +) i_swapper_sv ( + .clk (clk), + .reset_n (reset_n), + + .stream_in_empty (stream_in_empty), + .stream_in_data (stream_in_data), + .stream_in_endofpacket (stream_in_endofpacket), + .stream_in_startofpacket (stream_in_startofpacket), + .stream_in_valid (stream_in_valid), + .stream_in_ready (stream_in_ready), + + .stream_out_empty (sv_to_vhdl_empty), + .stream_out_data (sv_to_vhdl_data), + .stream_out_endofpacket (sv_to_vhdl_endofpacket), + .stream_out_startofpacket (sv_to_vhdl_startofpacket), + .stream_out_valid (sv_to_vhdl_valid), + .stream_out_ready (sv_to_vhdl_ready), + + .csr_address (csr_address), + .csr_readdata (csr_readdata), + .csr_readdatavalid (csr_readdatavalid), + .csr_read (csr_read), + .csr_write (csr_write), + .csr_waitrequest (csr_waitrequest_sv), + .csr_writedata (csr_writedata) +); + + +endian_swapper_vhdl #( + .DATA_BYTES (DATA_BYTES) +) i_swapper_vhdl ( + .clk (clk), + .reset_n (reset_n), + + .stream_in_empty (sv_to_vhdl_empty), + .stream_in_data (sv_to_vhdl_data), + .stream_in_endofpacket (sv_to_vhdl_endofpacket), + .stream_in_startofpacket (sv_to_vhdl_startofpacket), + .stream_in_valid (sv_to_vhdl_valid), + .stream_in_ready (sv_to_vhdl_ready), + + .stream_out_empty (stream_out_empty), + .stream_out_data (stream_out_data), + .stream_out_endofpacket (stream_out_endofpacket), + .stream_out_startofpacket (stream_out_startofpacket), + .stream_out_valid (stream_out_valid), + .stream_out_ready (stream_out_ready), + + .csr_address (csr_address), + .csr_readdata (), + .csr_readdatavalid (), + .csr_read (csr_read), + .csr_write (csr_write), + .csr_waitrequest (csr_waitrequest_vhdl), + .csr_writedata (~csr_writedata) + +); + +assign csr_waitrequest = csr_waitrequest_sv | csr_waitrequest_vhdl; + +endmodule diff --git a/examples/mixed_language/tests/Makefile b/examples/mixed_language/tests/Makefile new file mode 100644 index 00000000..61d8d46a --- /dev/null +++ b/examples/mixed_language/tests/Makefile @@ -0,0 +1,29 @@ +# Override this variable to use a VHDL toplevel instead of SystemVerilog +TOPLEVEL_LANG ?= sv + +# At the moment the only simulator supporting mixed language VHPI/VPI is Aldec +SIM?=aldec + +TOPLEVEL = endian_swapper_mixed + +PWD=$(shell pwd) +COCOTB=$(PWD)/../../.. + +VERILOG_SOURCES = $(PWD)/../../endian_swapper/hdl/endian_swapper.sv +VHDL_SOURCES = $(PWD)/../../endian_swapper/hdl/endian_swapper.vhdl + +ifeq ($(TOPLEVEL_LANG),sv) + VERILOG_SOURCES += $(PWD)/../hdl/toplevel.sv +endif + +ifeq ($(TOPLEVEL_LANG),vhdl) + $(error "VHDL toplevel not yet implemented") + VHDL_SOURCES += $(PWD)/../hdl/toplevel.vhdl +endif + + +MODULE=test_mixed_language + +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim + diff --git a/examples/mixed_language/tests/test_mixed_language.py b/examples/mixed_language/tests/test_mixed_language.py new file mode 100644 index 00000000..fe07b58f --- /dev/null +++ b/examples/mixed_language/tests/test_mixed_language.py @@ -0,0 +1,27 @@ +import cocotb +from cocotb.triggers import Timer +from cocotb.result import TestFailure + +@cocotb.test() +def mixed_language_test(dut): + """ + Try accessing handles and setting values in a mixed language environment + """ + yield Timer(100) + + verilog = dut.i_swapper_sv + dut.log.info("Got: %s" % repr(verilog.name)) + + vhdl = dut.i_swapper_vhdl + dut.log.info("Got: %s" % repr(vhdl.name)) + + verilog.reset_n = 1 + yield Timer(100) + + vhdl.reset_n = 1 + yield Timer(100) + + if int(verilog.reset_n) == int(vhdl.reset_n): + dut.log.info("Both signals read as %d" % int(vhdl.reset_n)) + else: + raise TestFailure("reset_n signals were different") \ No newline at end of file From e92d7d2dc40508595817a5848431878d6f4cdd0d Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 28 Sep 2014 16:07:37 +0100 Subject: [PATCH 0587/2656] Issue #161: Failing example when trying to cross language boundary --- examples/mixed_language/tests/test_mixed_language.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/mixed_language/tests/test_mixed_language.py b/examples/mixed_language/tests/test_mixed_language.py index fe07b58f..bf516a94 100644 --- a/examples/mixed_language/tests/test_mixed_language.py +++ b/examples/mixed_language/tests/test_mixed_language.py @@ -24,4 +24,8 @@ def mixed_language_test(dut): if int(verilog.reset_n) == int(vhdl.reset_n): dut.log.info("Both signals read as %d" % int(vhdl.reset_n)) else: - raise TestFailure("reset_n signals were different") \ No newline at end of file + raise TestFailure("reset_n signals were different") + + # Try accessing an object other than a port... + verilog_flush = int(verilog.flush_pipe) + vhdl_flush = int(vhdl.flush_pipe) From 9e183f5cd0cfa6407d58bc8369bb9063e444a91c Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 24 Sep 2014 00:26:40 +0100 Subject: [PATCH 0588/2656] Multilang: gpi_vpi partially converted and compiling --- include/gpi.h | 8 +- lib/Makefile | 18 +- lib/{gpi_impl => fli}/Makefile | 2 +- lib/gpi/Makefile | 58 ++ .../gpi_common.c => gpi/gpi_common.cpp} | 7 +- lib/gpi/gpi_priv.h | 93 +++ lib/gpi_impl/gpi_priv.h | 71 --- lib/vhpi/Makefile | 45 ++ lib/{gpi_impl => vhpi}/gpi_vhpi.c | 2 +- lib/vpi/Makefile | 45 ++ lib/{gpi_impl/gpi_vpi.c => vpi/gpi_vpi.cpp} | 557 ++++++++++-------- makefiles/Makefile.inc | 12 +- makefiles/Makefile.rules | 7 +- 13 files changed, 568 insertions(+), 357 deletions(-) rename lib/{gpi_impl => fli}/Makefile (98%) create mode 100644 lib/gpi/Makefile rename lib/{gpi_impl/gpi_common.c => gpi/gpi_common.cpp} (97%) create mode 100644 lib/gpi/gpi_priv.h delete mode 100644 lib/gpi_impl/gpi_priv.h create mode 100644 lib/vhpi/Makefile rename lib/{gpi_impl => vhpi}/gpi_vhpi.c (99%) create mode 100644 lib/vpi/Makefile rename lib/{gpi_impl/gpi_vpi.c => vpi/gpi_vpi.cpp} (72%) diff --git a/include/gpi.h b/include/gpi.h index 5691d1e8..682b889c 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -79,6 +79,7 @@ we have to create a process with the signal on the sensitivity list to imitate a #endif + EXTERN_C_START typedef enum gpi_event_e { @@ -101,13 +102,6 @@ typedef struct gpi_sim_hdl_s { void *sim_hdl; // Opaque handle for for a simulator object } gpi_sim_hdl_t, *gpi_sim_hdl; -// Define a type for a simulator callback handle -typedef struct gpi_cb_hdl_s { - gpi_sim_hdl_t hdl; - int (*gpi_function)(void *); // GPI function to callback - void *gpi_cb_data; // GPI data supplied to "gpi_function" -} gpi_cb_hdl_t, *gpi_cb_hdl; - // Define a handle type for iterators typedef struct __gpi_iterator_hdl *gpi_iterator_hdl; diff --git a/lib/Makefile b/lib/Makefile index b91c6542..a6b5cd9b 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -52,17 +52,25 @@ $(LIB_DIR)/libgpilog.so: $(SIM_ROOT)/lib/gpi_log/gpi_logging.c | $(LIB_DIR) $(LIB_DIR)/libcocotb.so: $(SIM_ROOT)/lib/embed/gpi_embed.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/embed -$(LIB_DIR)/libgpi.so: $(SIM_ROOT)/lib/gpi_impl/gpi_common.c \ - $(SIM_ROOT)/lib/gpi_impl/gpi_vpi.c \ - $(SIM_ROOT)/lib/gpi_impl/gpi_vhpi.c | $(LIB_DIR) - make -C $(SIM_ROOT)/lib/gpi_impl EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) +$(LIB_DIR)/libvpi.so: $(SIM_ROOT)/lib/vpi/gpi_vpi.cpp | $(LIB_DIR) + make -C $(SIM_ROOT)/lib/vpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) + +$(LIB_DIR)/libvhpi.so: $(SIM_ROOT)/lib/vhpi/gpi_vhpi.c | $(LIB_DIR) + make -C $(SIM_ROOT)/lib/vhpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) + +$(LIB_DIR)/libgpi.so: $(SIM_ROOT)/lib/gpi/gpi_common.cpp | $(LIB_DIR) + make -C $(SIM_ROOT)/lib/gpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) $(LIB_DIR)/libsim.so: $(SIM_ROOT)/lib/simulator/simulatormodule.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/simulator -COCOTB_LIBS = $(LIB_DIR)/libgpilog.so \ +COCOTB_LIBS = $(LIB_DIR)/libvpi.so + +#COCOTB_LIBS = $(LIB_DIR)/libgpilog.so \ $(LIB_DIR)/libcocotb.so \ $(LIB_DIR)/libgpi.so \ + $(LIB_DIR)/libvpi.so \ + $(LIB_DIR)/libvhpi.so \ $(LIB_DIR)/libsim.so clean:: diff --git a/lib/gpi_impl/Makefile b/lib/fli/Makefile similarity index 98% rename from lib/gpi_impl/Makefile rename to lib/fli/Makefile index 37b3b9c6..18c01b90 100644 --- a/lib/gpi_impl/Makefile +++ b/lib/fli/Makefile @@ -35,7 +35,7 @@ LIBS := -lgpilog -lcocotb $(EXTRA_LIBS) LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) LIB_NAME := libgpi.so -SRCS := gpi_common.c gpi_vpi.c gpi_vhpi.c +SRCS := gpi_common.c CLIBS += $(LIB_DIR)/gpivpi.vpl diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile new file mode 100644 index 00000000..23edbdac --- /dev/null +++ b/lib/gpi/Makefile @@ -0,0 +1,58 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +include $(SIM_ROOT)/makefiles/Makefile.inc + +INCLUDES += +GCC_ARGS += -DVPI_CHECKING +LIBS := -lgpilog -lcocotb $(EXTRA_LIBS) +LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) +LIB_NAME := libgpi.so + +SRCS := gpi_common.cpp + +CLIBS += $(LIB_DIR)/gpivpi.vpl + +# More rules such that this may be needed depending on the requirements of +# different simulators, icarus for instance loads a .vpi libraray to use the vpi +# inerface at the start of the simulation + +all: $(LIB_DIR)/gpivpi.vpl $(LIB_DIR)/cocotb.vpi $(IMPS) + +$(LIB_DIR)/gpivpi.vpl: $(LIB_DIR)/$(LIB_NAME) + ln -sf $(LIB_NAME) $@ + +$(LIB_DIR)/cocotb.vpi: $(LIB_DIR)/$(LIB_NAME) + ln -sf $(LIB_NAME) $@ + +clean: + -@rm -rf $(LIB_DIR)/gpivpi.vpl + -@rm -rf $(LIB_DIR)/$(LIB_NAME) + +include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/lib/gpi_impl/gpi_common.c b/lib/gpi/gpi_common.cpp similarity index 97% rename from lib/gpi_impl/gpi_common.c rename to lib/gpi/gpi_common.cpp index 63a75e91..a1bdd1ec 100644 --- a/lib/gpi_impl/gpi_common.c +++ b/lib/gpi/gpi_common.cpp @@ -32,7 +32,7 @@ #define MAX_IMPLS 5 typedef struct impls { - gpi_impl_tbl tbl; + gpi_impl_tbl *tbl; int type; } r_impl; @@ -218,14 +218,11 @@ void gpi_free_handle(gpi_sim_hdl hdl) free(hdl); } -int gpi_register_impl(gpi_impl_tbl func_tbl, int type) +int gpi_register_impl(gpi_impl_tbl *func_tbl, int type) { int idx; for (idx = 0; idx < MAX_IMPLS; idx++) { if (!registed_impls[idx].tbl) { - /* TODO - * check that the pointers are set and error if not - */ registed_impls[idx].tbl = func_tbl; registed_impls[idx].type = type; } diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h new file mode 100644 index 00000000..fa0b62b6 --- /dev/null +++ b/lib/gpi/gpi_priv.h @@ -0,0 +1,93 @@ +/****************************************************************************** +* Copyright (c) 2013 Potential Ventures Ltd +* Copyright (c) 2013 SolarFlare Communications Inc +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd, +* SolarFlare Communications Inc nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +#include +#include + +#define gpi_container_of(_address, _type, _member) \ + ((_type *)((uintptr_t)(_address) - \ + (uintptr_t)(&((_type *)0)->_member))) + +// Define a type for a simulator callback handle +//typedef struct gpi_cb_hdl_s { +// gpi_sim_hdl_t hdl; +// int (*gpi_function)(void *); // GPI function to callback +// void *gpi_cb_data; // GPI data supplied to "gpi_function" +//} gpi_cb_hdl_t, *gpi_cb_hdl; + +class gpi_cb_hdl { +public: + gpi_cb_hdl(); + virtual ~gpi_cb_hdl() { + + } +private: + int (*gpi_function)(void *); // GPI function to callback + void *gpi_cb_data; // GPI data supplied to "gpi_function" +}; + +class gpi_impl_interface { +public: + gpi_impl_interface(); + virtual ~gpi_impl_interface() = 0; + + virtual void sim_end(void) = 0; + virtual void get_sim_time(uint32_t *high, uint32_t *low); + virtual gpi_sim_hdl get_root_handle(const char *name) = 0; + virtual gpi_sim_hdl get_handle_by_name(const char *name, gpi_sim_hdl parent) = 0; + virtual gpi_sim_hdl get_handle_by_index(gpi_sim_hdl parent, uint32_t index) = 0; + virtual void free_handle(gpi_sim_hdl) = 0; + virtual gpi_iterator_hdl iterate_handle(uint32_t type, gpi_sim_hdl base) = 0; + virtual gpi_sim_hdl next_handle(gpi_iterator_hdl iterator) = 0; + virtual char* get_signal_value_binstr(gpi_sim_hdl gpi_hdl) = 0; + virtual char* get_signal_name_str(gpi_sim_hdl gpi_hdl) = 0; + virtual char* get_signal_type_str(gpi_sim_hdl gpi_hdl) = 0; + virtual void set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) = 0; + virtual void set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) = 0; // String of binary char(s) [1, 0, x, z] + virtual int register_timed_callback(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps) = 0; + virtual int register_value_change_callback(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl) = 0; + virtual int register_readonly_callback(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data) = 0; + virtual int register_nexttime_callback(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data) = 0; + virtual int register_readwrite_callback(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data) = 0; + virtual gpi_cb_hdl *create_cb_handle(void) = 0; + virtual void destroy_cb_handle(gpi_cb_hdl *gpi_hdl) = 0; + virtual int deregister_callback(gpi_sim_hdl gpi_hdl) = 0; + //virtual void* get_callback_data(gpi_sim_hdl gpi_hdl) = 0; +}; + +int gpi_register_impl(const gpi_impl_interface &func_tbl, int type); + +void gpi_embed_init(gpi_sim_info_t *info); +void gpi_embed_end(void); +void gpi_embed_init_python(void); + +char *gpi_copy_name(const char *name); + +void gpi_handle_callback(gpi_sim_hdl cb_data); + diff --git a/lib/gpi_impl/gpi_priv.h b/lib/gpi_impl/gpi_priv.h deleted file mode 100644 index 2ce2f2a7..00000000 --- a/lib/gpi_impl/gpi_priv.h +++ /dev/null @@ -1,71 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2013 Potential Ventures Ltd -* Copyright (c) 2013 SolarFlare Communications Inc -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd, -* SolarFlare Communications Inc nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#include -#include - -#define gpi_container_of(_address, _type, _member) \ - ((_type *)((uintptr_t)(_address) - \ - (uintptr_t)(&((_type *)0)->_member))) - -typedef struct t_gpi_impl_tbl { - void (*sim_end)(void); - void (*get_sim_time)(uint32_t *high, uint32_t *low); - gpi_sim_hdl (*get_root_handle)(const char *name); - gpi_sim_hdl (*get_handle_by_name)(const char *name, gpi_sim_hdl parent); - gpi_sim_hdl (*get_handle_by_index)(gpi_sim_hdl parent, uint32_t index); - void (*free_handle)(gpi_sim_hdl); - gpi_iterator_hdl (*iterate_handle)(uint32_t type, gpi_sim_hdl base); - gpi_sim_hdl (*next_handle)(gpi_iterator_hdl iterator); - char* (*get_signal_value_binstr)(gpi_sim_hdl gpi_hdl); - char* (*get_signal_name_str)(gpi_sim_hdl gpi_hdl); - char* (*get_signal_type_str)(gpi_sim_hdl gpi_hdl); - void (*set_signal_value_int)(gpi_sim_hdl gpi_hdl, int value); - void (*set_signal_value_str)(gpi_sim_hdl gpi_hdl, const char *str); // String of binary char(s) [1, 0, x, z] - int (*register_timed_callback)(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps); - int (*register_value_change_callback)(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl); - int (*register_readonly_callback)(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); - int (*register_nexttime_callback)(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); - int (*register_readwrite_callback)(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); - gpi_cb_hdl (*create_cb_handle)(void); - void (*destroy_cb_handle)(gpi_cb_hdl gpi_hdl); - int (*deregister_callback)(gpi_sim_hdl gpi_hdl); - void* (*get_callback_data)(gpi_sim_hdl gpi_hdl); -} s_gpi_impl_tbl, *gpi_impl_tbl; - -int gpi_register_impl(gpi_impl_tbl func_tbl, int type); - -void gpi_embed_init(gpi_sim_info_t *info); -void gpi_embed_end(void); -void gpi_embed_init_python(void); - -char *gpi_copy_name(const char *name); - -void gpi_handle_callback(gpi_sim_hdl cb_data); - diff --git a/lib/vhpi/Makefile b/lib/vhpi/Makefile new file mode 100644 index 00000000..850a8e42 --- /dev/null +++ b/lib/vhpi/Makefile @@ -0,0 +1,45 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +include $(SIM_ROOT)/makefiles/Makefile.inc + +INCLUDES += +GCC_ARGS += -DVPI_CHECKING +LIBS := -lgpilog +LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) +LIB_NAME := libvhpi.so + +SRCS := gpi_vhpi.c + +all: $(LIB_DIR)/$(LIB_NAME) + +clean: + -@rm -rf $(LIB_DIR)/$(LIB_NAME) + +include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/lib/gpi_impl/gpi_vhpi.c b/lib/vhpi/gpi_vhpi.c similarity index 99% rename from lib/gpi_impl/gpi_vhpi.c rename to lib/vhpi/gpi_vhpi.c index 6ac6f28d..74e811f3 100644 --- a/lib/gpi_impl/gpi_vhpi.c +++ b/lib/vhpi/gpi_vhpi.c @@ -32,7 +32,7 @@ // VHPI seems to run significantly slower than VPI, need to investigate. -#include "gpi_priv.h" +#include "../gpi/gpi_priv.h" #include #define VHPI_CHECKING 1 diff --git a/lib/vpi/Makefile b/lib/vpi/Makefile new file mode 100644 index 00000000..85745bf1 --- /dev/null +++ b/lib/vpi/Makefile @@ -0,0 +1,45 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +include $(SIM_ROOT)/makefiles/Makefile.inc + +INCLUDES += +GCC_ARGS += -DVPI_CHECKING +LIBS := -lgpilog +LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) +LIB_NAME := libvpi.so + +SRCS := gpi_vpi.cpp + +all: $(LIB_DIR)/$(LIB_NAME) + +clean: + -@rm -rf $(LIB_DIR)/$(LIB_NAME) + +include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/lib/gpi_impl/gpi_vpi.c b/lib/vpi/gpi_vpi.cpp similarity index 72% rename from lib/gpi_impl/gpi_vpi.c rename to lib/vpi/gpi_vpi.cpp index dc9b9456..22f5758e 100644 --- a/lib/gpi_impl/gpi_vpi.c +++ b/lib/vpi/gpi_vpi.cpp @@ -27,13 +27,12 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ -#include "gpi_priv.h" +#include "../gpi/gpi_priv.h" #include #define VPI_CHECKING 1 -static gpi_sim_hdl sim_init_cb; -static gpi_sim_hdl sim_finish_cb; +extern "C" int32_t handle_vpi_callback(p_cb_data cb_data); typedef enum vpi_cb_state_e { VPI_FREE = 0, @@ -43,46 +42,6 @@ typedef enum vpi_cb_state_e { VPI_DELETE = 4, } vpi_cb_state_t; -// callback user data used for VPI callbacks - -typedef struct t_vpi_cb { - vpiHandle cb_hdl; - s_vpi_value cb_value; - vpi_cb_state_t state; - gpi_cb_hdl_t gpi_cb_data; - int (*vpi_cleanup)(struct t_vpi_cb *cb_data); -} s_vpi_cb, *p_vpi_cb; - -// Forward declarations -static int vpi_deregister_callback(gpi_sim_hdl gpi_hdl); -static void vpi_destroy_cb_handle(gpi_cb_hdl hdl); - -// Add to this over time -static const char * vpi_reason_to_string(int reason) -{ - switch (reason) { - case cbValueChange: - return "cbValueChange"; - case cbAtStartOfSimTime: - return "cbAtStartOfSimTime"; - case cbReadWriteSynch: - return "cbReadWriteSynch"; - case cbReadOnlySynch: - return "cbReadOnlySynch"; - case cbNextSimTime: - return "cbNextSimTime"; - case cbAfterDelay: - return "cbAfterDelay"; - case cbStartOfSimulation: - return "cbStartOfSimulation"; - case cbEndOfSimulation: - return "cbEndOfSimulation"; - default: - return "unknown"; - } -} - - // Should be run after every VPI call to check error status static int __check_vpi_error(const char *func, long line) { @@ -118,17 +77,215 @@ static int __check_vpi_error(const char *func, long line) return level; } -#define check_vpi_error() \ - __check_vpi_error(__func__, __LINE__) +#define check_vpi_error() do { \ + __check_vpi_error(__func__, __LINE__); \ +} while (0) + +class vpi_cb_hdl : public gpi_cb_hdl { +protected: + s_vpi_value cb_value; + vpi_cb_state_t state; + gpi_cb_hdl gpi_cb_data; + vpiHandle cb_hdl; + +public: + vpi_cb_hdl() { + + } + virtual ~vpi_cb_hdl() { + + } + + const char *vpi_reason_to_string(int reason); + void set_state(vpi_cb_state_t new_state); + virtual int arm_callback(void); + virtual int cleanup_callback(void); + virtual int run_callback(void); + +protected: + int __vpi_register_cb(p_cb_data cb_data); +}; + +class vpi_one_time_callback : public vpi_cb_hdl { +protected: + int cleanup_callback(void) { + FENTER + int rc; + if (!cb_hdl) { + LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); + exit(1); + } + + // If the callback has not been called we also need to call + // remove as well + if (state == VPI_PRIMED) { + + rc = vpi_remove_cb(cb_hdl); + if (!rc) { + check_vpi_error(); + return rc; + } + cb_hdl = NULL; + + // HACK: Calling vpi_free_object after vpi_remove_cb causes Modelsim to VPIEndOfSimulationCallback + #if 0 + rc = vpi_free_object(cb_hdl); + if (!rc) { + check_vpi_error(); + return rc; + } + #endif + } + state = VPI_FREE; + FEXIT + return rc; + } +}; -static inline int __vpi_register_cb(p_vpi_cb user, p_cb_data cb_data) +class vpi_reurring_callback : public vpi_cb_hdl { +public: + int cleanup_callback(void) { + FENTER + int rc; + + if (!cb_hdl) { + LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); + exit(1); + } + + rc = vpi_remove_cb(cb_hdl); + check_vpi_error(); + FEXIT + return rc; + } +}; + +class vpi_initial_cb_hdl : public vpi_one_time_callback { + +public: + int arm_callback(void) { + s_cb_data cb_data_s; + + cb_data_s.reason = cbStartOfSimulation; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = NULL; + cb_data_s.value = NULL; + cb_data_s.user_data = (char*)this; + + return __vpi_register_cb(&cb_data_s); + } + + int run_callback(void) { + s_vpi_vlog_info info; + gpi_sim_info_t sim_info; + + vpi_get_vlog_info(&info); + + sim_info.argc = info.argc; + sim_info.argv = info.argv; + sim_info.product = info.product; + sim_info.version = info.version; + + gpi_embed_init(&sim_info); + + return 0; + } +}; + +class vpi_final_cb_hdl : public vpi_one_time_callback { +public: + int arm_callback(void) { + s_cb_data cb_data_s; + + cb_data_s.reason = cbEndOfSimulation; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = NULL; + cb_data_s.value = NULL; + cb_data_s.user_data = (char*)this; + + return __vpi_register_cb(&cb_data_s); + } + + int run_callback(void) { + gpi_embed_end(); + return 0; + } +}; + +class vpi_impl : public gpi_impl_interface { +public: + + void sim_end(void); + void get_sim_time(uint32_t *high, uint32_t *low); + gpi_sim_hdl get_root_handle(const char *name); + gpi_sim_hdl get_handle_by_name(const char *name, gpi_sim_hdl parent); + gpi_sim_hdl get_handle_by_index(gpi_sim_hdl parent, uint32_t index); + void free_handle(gpi_sim_hdl); + gpi_iterator_hdl iterate_handle(uint32_t type, gpi_sim_hdl base); + gpi_sim_hdl next_handle(gpi_iterator_hdl iterator); + char* get_signal_value_binstr(gpi_sim_hdl gpi_hdl); + char* get_signal_name_str(gpi_sim_hdl gpi_hdl); + char* get_signal_type_str(gpi_sim_hdl gpi_hdl); + void set_signal_value_int(gpi_sim_hdl gpi_hdl, int value); + void set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str); // String of binary char(s) [1, 0, x, z] + int register_timed_callback(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps); + int register_value_change_callback(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl); + int register_readonly_callback(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); + int register_nexttime_callback(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); + int register_readwrite_callback(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); + gpi_cb_hdl *create_cb_handle(void); + void destroy_cb_handle(gpi_cb_hdl *gpi_hdl); + int deregister_callback(gpi_sim_hdl gpi_hdl); +}; + +static vpi_cb_hdl *sim_init_cb; +static vpi_cb_hdl *sim_finish_cb; + +// Add to this over time +const char * vpi_cb_hdl::vpi_reason_to_string(int reason) +{ + switch (reason) { + case cbValueChange: + return "cbValueChange"; + case cbAtStartOfSimTime: + return "cbAtStartOfSimTime"; + case cbReadWriteSynch: + return "cbReadWriteSynch"; + case cbReadOnlySynch: + return "cbReadOnlySynch"; + case cbNextSimTime: + return "cbNextSimTime"; + case cbAfterDelay: + return "cbAfterDelay"; + case cbStartOfSimulation: + return "cbStartOfSimulation"; + case cbEndOfSimulation: + return "cbEndOfSimulation"; + default: + return "unknown"; + } +} + +int vpi_cb_hdl::__vpi_register_cb(p_cb_data cb_data) { /* If the user data already has a callback handle then deregister * before getting the new one */ - if (user->state == VPI_PRIMED) { - fprintf(stderr, "Attempt to prime an already primed trigger for %s!\n", - vpi_reason_to_string(cb_data->reason)); + if (state == VPI_PRIMED) { + fprintf(stderr, + "Attempt to prime an already primed trigger for %s!\n", + vpi_reason_to_string(cb_data->reason)); + } + + if (cb_hdl != NULL) { + fprintf(stderr, + "We seem to already be registered, deregistering %s!\n", + vpi_reason_to_string(cb_data->reason)); +#ifdef FIX_THIS + vpi_deregister_callback(&cb_hdl); +#endif } vpiHandle new_hdl = vpi_register_cb(cb_data); @@ -141,14 +298,8 @@ static inline int __vpi_register_cb(p_vpi_cb user, p_cb_data cb_data) ret = -1; } - if (user->cb_hdl != NULL) { - fprintf(stderr, "user->gpi_cb_hdl is not null, deregistering %s!\n", - vpi_reason_to_string(cb_data->reason)); - vpi_deregister_callback(&user->gpi_cb_data.hdl); - } - - user->cb_hdl = new_hdl; - user->state = VPI_PRIMED; + cb_hdl = new_hdl; + state = VPI_PRIMED; return ret; } @@ -166,7 +317,7 @@ static inline int __vpi_register_cb(p_vpi_cb user, p_cb_data cb_data) * If name is provided, we check the name against the available objects until * we find a match. If no match is found we return NULL */ -static gpi_sim_hdl vpi_get_root_handle(const char* name) +gpi_sim_hdl vpi_impl::get_root_handle(const char* name) { FENTER vpiHandle root; @@ -227,7 +378,7 @@ static gpi_sim_hdl vpi_get_root_handle(const char* name) * * @return gpi_sim_hdl for the new object or NULL if object not found */ -static gpi_sim_hdl vpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) +gpi_sim_hdl vpi_impl::get_handle_by_name(const char *name, gpi_sim_hdl parent) { FENTER gpi_sim_hdl rv; @@ -301,7 +452,7 @@ static gpi_sim_hdl vpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) * Can be used on bit-vectors to access a specific bit or * memories to access an address */ -static gpi_sim_hdl vpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) +gpi_sim_hdl vpi_impl::get_handle_by_index(gpi_sim_hdl parent, uint32_t index) { FENTER gpi_sim_hdl rv; @@ -324,7 +475,7 @@ static gpi_sim_hdl vpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) // Functions for iterating over entries of a handle // Returns an iterator handle which can then be used in gpi_next calls // NB May return NULL if no objects of the request type exist -static gpi_iterator_hdl vpi_iterate_hdl(uint32_t type, gpi_sim_hdl base) { +gpi_iterator_hdl vpi_impl::iterate_handle(uint32_t type, gpi_sim_hdl base) { FENTER vpiHandle iterator; @@ -337,7 +488,7 @@ static gpi_iterator_hdl vpi_iterate_hdl(uint32_t type, gpi_sim_hdl base) { } // Returns NULL when there are no more objects -static gpi_sim_hdl vpi_next_hdl(gpi_iterator_hdl iterator) +gpi_sim_hdl vpi_impl::next_handle(gpi_iterator_hdl iterator) { FENTER gpi_sim_hdl rv = gpi_create_handle(); @@ -359,7 +510,7 @@ static gpi_sim_hdl vpi_next_hdl(gpi_iterator_hdl iterator) } // double gpi_get_sim_time() -static void vpi_get_sim_time(uint32_t *high, uint32_t *low) +void vpi_impl::get_sim_time(uint32_t *high, uint32_t *low) { s_vpi_time vpi_time_s; vpi_time_s.type = vpiSimTime;//vpiScaledRealTime; //vpiSimTime; @@ -370,7 +521,7 @@ static void vpi_get_sim_time(uint32_t *high, uint32_t *low) } // Value related functions -static void vpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) +void vpi_impl::set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) { FENTER s_vpi_value value_s; @@ -393,7 +544,7 @@ static void vpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) FEXIT } -static void vpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) +void vpi_impl::set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) { FENTER s_vpi_value value_s; @@ -425,7 +576,7 @@ static void vpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) FEXIT } -static char *vpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) +char *vpi_impl::get_signal_value_binstr(gpi_sim_hdl gpi_hdl) { FENTER s_vpi_value value_s = {vpiBinStrVal}; @@ -439,7 +590,7 @@ static char *vpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) return result; } -static char *vpi_get_signal_name_str(gpi_sim_hdl gpi_hdl) +char *vpi_impl::get_signal_name_str(gpi_sim_hdl gpi_hdl) { FENTER const char *name = vpi_get_str(vpiFullName, (vpiHandle)(gpi_hdl->sim_hdl)); @@ -449,7 +600,7 @@ static char *vpi_get_signal_name_str(gpi_sim_hdl gpi_hdl) return result; } -static char *vpi_get_signal_type_str(gpi_sim_hdl gpi_hdl) +char *vpi_impl::get_signal_type_str(gpi_sim_hdl gpi_hdl) { FENTER const char *name = vpi_get_str(vpiType, (vpiHandle)(gpi_hdl->sim_hdl)); @@ -459,24 +610,23 @@ static char *vpi_get_signal_type_str(gpi_sim_hdl gpi_hdl) return result; } - -// Callback related functions -static int32_t handle_vpi_callback(p_cb_data cb_data) +// Main re-entry point for callbacks from simulator +int32_t handle_vpi_callback(p_cb_data cb_data) { FENTER int rv = 0; //vpiHandle old_cb; - p_vpi_cb user_data; - user_data = (p_vpi_cb)cb_data->user_data; + vpi_cb_hdl *cb_hdl = (vpi_cb_hdl*)cb_data->user_data; - if (!user_data) + if (!cb_hdl) LOG_CRITICAL("VPI: Callback data corrupted"); - user_data->state = VPI_PRE_CALL; + cb_hdl->set_state(VPI_PRE_CALL); //old_cb = user_data->cb_hdl; - gpi_handle_callback(&user_data->gpi_cb_data.hdl); + cb_hdl->run_callback(); +#if 0 // HACK: Investigate further - this breaks modelsim #if 0 if (old_cb == user_data->cb_hdl) @@ -490,6 +640,8 @@ static int32_t handle_vpi_callback(p_cb_data cb_data) gpi_free_cb_handle(&user_data->gpi_cb_data.hdl); else user_data->state = VPI_POST_CALL; +#endif + cb_hdl->set_state(VPI_POST_CALL); FEXIT return rv; @@ -501,87 +653,24 @@ static int32_t handle_vpi_callback(p_cb_data cb_data) * This can be called at any point between * gpi_create_cb_handle and gpi_free_cb_handle */ -static int vpi_deregister_callback(gpi_sim_hdl gpi_hdl) +int vpi_impl::deregister_callback(gpi_sim_hdl gpi_hdl) { FENTER - p_vpi_cb vpi_user_data; - gpi_cb_hdl gpi_user_data; int rc = 1; // We should be able to user vpi_get_cb_info // but this is not implemented in ICARUS // and gets upset on VCS. So instead we // do some pointer magic. - - gpi_user_data = gpi_container_of(gpi_hdl, gpi_cb_hdl_t, hdl); - vpi_user_data = gpi_container_of(gpi_user_data, s_vpi_cb, gpi_cb_data); - - if (vpi_user_data->cb_hdl) { - rc = vpi_user_data->vpi_cleanup(vpi_user_data); - vpi_user_data->cb_hdl = NULL; - } - - FEXIT - GPI_RET(rc); -} - - -// Call when the handle relates to a one time callback -// No need to call vpi_deregister_cb as the sim will -// do this but do need to destroy the handle -static int vpi_free_one_time(p_vpi_cb user_data) -{ - FENTER - int rc; - vpiHandle cb_hdl = user_data->cb_hdl; - if (!cb_hdl) { - LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); - exit(1); - } - // If the callback has not been called we also need to call - // remove as well - if (user_data->state == VPI_PRIMED) { + vpi_cb_hdl *hdl = reinterpret_cast(gpi_hdl->sim_hdl); - rc = vpi_remove_cb(cb_hdl); - if (!rc) { - check_vpi_error(); - return rc; - } - user_data->cb_hdl = NULL; + hdl->cleanup_callback(); -// HACK: Calling vpi_free_object after vpi_remove_cb causes Modelsim to VPIEndOfSimulationCallback -#if 0 - rc = vpi_free_object(cb_hdl); - if (!rc) { - check_vpi_error(); - return rc; - } -#endif - } - user_data->state = VPI_FREE; FEXIT - return rc; + GPI_RET(rc); } -// Call when the handle relates to recurring callback -// Unregister must be called when not needed and this -// will clean all memory allocated by the sim -static int vpi_free_recurring(p_vpi_cb user_data) -{ - FENTER - int rc; - vpiHandle cb_hdl = user_data->cb_hdl; - if (!cb_hdl) { - LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); - exit(1); - } - - rc = vpi_remove_cb(cb_hdl); - check_vpi_error(); - FEXIT - return rc; -} /* These functions request a callback to be active with the current * handle and associated data. A callback handle needs to have been @@ -589,11 +678,12 @@ static int vpi_free_recurring(p_vpi_cb user_data) */ -static int vpi_register_value_change_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data, - gpi_sim_hdl gpi_hdl) +int vpi_impl::register_value_change_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data, + gpi_sim_hdl gpi_hdl) { +#if 0 FENTER int ret; @@ -617,17 +707,21 @@ static int vpi_register_value_change_callback(gpi_sim_hdl cb, cb_data_s.value = &vpi_user_data->cb_value; cb_data_s.user_data = (char *)vpi_user_data; - ret = __vpi_register_cb(vpi_user_data, &cb_data_s); + ret = vpi_user_data.__vpi_register_cb(&cb_data_s); FEXIT return ret; +#else + return 0; +#endif } -static int vpi_register_readonly_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) +int vpi_impl::register_readonly_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) { +#if 0 FENTER int ret; @@ -656,12 +750,16 @@ static int vpi_register_readonly_callback(gpi_sim_hdl cb, FEXIT return ret; +#else + return 0; +#endif } -static int vpi_register_readwrite_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) +int vpi_impl::register_readwrite_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) { +#if 0 FENTER int ret; @@ -690,13 +788,17 @@ static int vpi_register_readwrite_callback(gpi_sim_hdl cb, FEXIT return ret; +#else + return 0; +#endif } -static int vpi_register_nexttime_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) +int vpi_impl::register_nexttime_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) { +#if 0 FENTER int ret; @@ -725,13 +827,17 @@ static int vpi_register_nexttime_callback(gpi_sim_hdl cb, FEXIT return ret; +#else + return 0; +#endif } -static int vpi_register_timed_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data, - uint64_t time_ps) +int vpi_impl::register_timed_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data, + uint64_t time_ps) { +#if 0 FENTER int ret; @@ -761,50 +867,49 @@ static int vpi_register_timed_callback(gpi_sim_hdl cb, FEXIT return ret; +#else + return 0; +#endif } /* Checking of validity is done in the common code */ -static gpi_cb_hdl vpi_create_cb_handle(void) +gpi_cb_hdl *vpi_impl::create_cb_handle(void) { - gpi_cb_hdl ret = NULL; - FENTER - p_vpi_cb new_cb_hdl = calloc(1, sizeof(*new_cb_hdl)); - if (new_cb_hdl) - ret = &new_cb_hdl->gpi_cb_data; + vpi_cb_hdl *new_hdl = new vpi_cb_hdl(); FEXIT - return ret; + return new_hdl; } -static void vpi_destroy_cb_handle(gpi_cb_hdl hdl) +void vpi_impl::destroy_cb_handle(gpi_cb_hdl *hdl) { FENTER - p_vpi_cb vpi_hdl = gpi_container_of(hdl, s_vpi_cb, gpi_cb_data); - free(vpi_hdl); + delete(hdl); FEXIT } -static void *vpi_get_callback_data(gpi_sim_hdl gpi_hdl) -{ - FENTER - gpi_cb_hdl gpi_user_data; - gpi_user_data = gpi_container_of(gpi_hdl, gpi_cb_hdl_t, hdl); - return gpi_user_data->gpi_cb_data; -} +//void *vpi_impl::get_callback_data(gpi_sim_hdl gpi_hdl) +//{ +// FENTER +// gpi_cb_hdl gpi_user_data; +// gpi_user_data = gpi_container_of(gpi_hdl, gpi_cb_hdl_t, hdl); +// return gpi_user_data->gpi_cb_data; +//} // If the Pything world wants things to shut down then unregister // the callback for end of sim -static void vpi_sim_end(void) +void vpi_impl::sim_end(void) { sim_finish_cb = NULL; vpi_control(vpiFinish); check_vpi_error(); } +#if 0 static s_gpi_impl_tbl vpi_table = { .sim_end = vpi_sim_end, .iterate_handle = vpi_iterate_hdl, @@ -828,113 +933,43 @@ static s_gpi_impl_tbl vpi_table = { .register_readonly_callback = vpi_register_readonly_callback, .get_callback_data = vpi_get_callback_data, }; +#endif static void register_embed(void) { - gpi_register_impl(&vpi_table, 0xfeed); + vpi_impl vpi_table; + gpi_register_impl(vpi_table, 0xfeed); gpi_embed_init_python(); } -static int handle_sim_init(void *gpi_cb_data) -{ - FENTER - s_vpi_vlog_info info; - gpi_sim_info_t sim_info; - - vpi_get_vlog_info(&info); - - sim_info.argc = info.argc; - sim_info.argv = info.argv; - sim_info.product = info.product; - sim_info.version = info.version; - - gpi_embed_init(&sim_info); - - FEXIT - - return 0; -} - static void register_initial_callback(void) { FENTER - s_cb_data cb_data_s; - p_vpi_cb vpi_user_data; - gpi_cb_hdl gpi_user_data; - - sim_init_cb = gpi_create_cb_handle(); - - gpi_user_data = gpi_container_of(sim_init_cb, gpi_cb_hdl_t, hdl); - vpi_user_data = gpi_container_of(gpi_user_data, s_vpi_cb, gpi_cb_data); - - gpi_user_data->gpi_cb_data = NULL; - gpi_user_data->gpi_function = handle_sim_init; - - vpi_user_data->vpi_cleanup = vpi_free_one_time; - - cb_data_s.reason = cbStartOfSimulation; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = NULL; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)vpi_user_data; + sim_init_cb = new vpi_initial_cb_hdl(); /* We ignore the return value here as VCS does some silly * things on comilation that means it tries to run through * the vlog_startup_routines and so call this routine */ - __vpi_register_cb(vpi_user_data, &cb_data_s); - - FEXIT -} + sim_init_cb->arm_callback(); -static int handle_sim_end(void *gpi_cb_data) -{ - FENTER - if (sim_finish_cb) { - sim_finish_cb = NULL; - /* This means that we have been asked to close */ - gpi_embed_end(); - } /* Other sise we have already been here from the top down so do not need - to inform the upper layers that anything has occoured */ - gpi_free_cb_handle(sim_init_cb); FEXIT - - return 0; } static void register_final_callback(void) { FENTER - s_cb_data cb_data_s; - p_vpi_cb vpi_user_data; - gpi_cb_hdl gpi_user_data; - - sim_init_cb = gpi_create_cb_handle(); - - gpi_user_data = gpi_container_of(sim_init_cb, gpi_cb_hdl_t, hdl); - vpi_user_data = gpi_container_of(gpi_user_data, s_vpi_cb, gpi_cb_data); - - gpi_user_data->gpi_cb_data = NULL; - gpi_user_data->gpi_function = handle_sim_end; - - vpi_user_data->vpi_cleanup = vpi_free_one_time; - - cb_data_s.reason = cbEndOfSimulation; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = NULL; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)vpi_user_data; + sim_finish_cb = new vpi_initial_cb_hdl(); /* We ignore the return value here as VCS does some silly * things on comilation that means it tries to run through * the vlog_startup_routines and so call this routine */ - __vpi_register_cb(vpi_user_data, &cb_data_s); + + sim_finish_cb->arm_callback(); FEXIT } @@ -1018,19 +1053,19 @@ static void register_system_functions(void) tfData.compiletf = system_function_compiletf; tfData.calltf = system_function_overload; - tfData.user_data = (void *)&systf_info_level; + tfData.user_data = (char *)&systf_info_level; tfData.tfname = "$info"; vpi_register_systf( &tfData ); - tfData.user_data = (void *)&systf_warning_level; + tfData.user_data = (char *)&systf_warning_level; tfData.tfname = "$warning"; vpi_register_systf( &tfData ); - tfData.user_data = (void *)&systf_error_level; + tfData.user_data = (char *)&systf_error_level; tfData.tfname = "$error"; vpi_register_systf( &tfData ); - tfData.user_data = (void *)&systf_fatal_level; + tfData.user_data = (char *)&systf_fatal_level; tfData.tfname = "$fatal"; vpi_register_systf( &tfData ); diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index c984a3d4..8a5277ab 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -56,14 +56,16 @@ export INCLUDES := -I$(SIM_ROOT)/include $(PYTHON_INCLUDEDIR) # Base GCC flags ifeq ($(OS),Darwin) -GCC_ARGS := -g -DDEBUG -fpic +GXX_ARGS := -g -DDEBUG -fpic +GCC_ARGS := $(GXX_ARGS) else -GCC_ARGS := -Werror -Wcast-qual -Wcast-align -Wwrite-strings \ - -Waggregate-return -Wstrict-prototypes -Wall \ - -Wno-unused-parameter -fno-common -g -DDEBUG -fpic ifeq ($(ARCH),i686) -GCC_ARGS += -m32 +GXX_ARGS := -m32 endif +GXX_ARGS += -Werror -Wcast-qual -Wcast-align -Wwrite-strings \ + -Waggregate-return -Wall \ + -Wno-unused-parameter -fno-common -g -DDEBUG -fpic +GCC_ARGS += $(GXX_ARGS) -Wstrict-prototypes endif diff --git a/makefiles/Makefile.rules b/makefiles/Makefile.rules index 2b3b06c2..5db8a874 100644 --- a/makefiles/Makefile.rules +++ b/makefiles/Makefile.rules @@ -31,12 +31,17 @@ # Disable some inbuild rules %: %.c %: %.o +%: %.cpp # Define some of our own -$(LIB_NAME)_OBJS:= $(SRCS:%.c=$(LIB_OBJ_DIR)/%.o) +$(LIB_NAME)_OBJS:= $(patsubst %.c,$(LIB_OBJ_DIR)/%.o,$(filter %.c,$(SRCS))) +$(LIB_NAME)_OBJS+= $(patsubst %.cpp,$(LIB_OBJ_DIR)/%.o,$(filter %.cpp,$(SRCS))) $(LIB_OBJ_DIR)/%.o: %.c gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $@ $< +$(LIB_OBJ_DIR)/%.o: %.cpp + g++ $(GXX_ARGS) -c -fpic $(INCLUDES) -o $@ $< + $(LIB_DIR)/$(LIB_NAME): $($(LIB_NAME)_OBJS) gcc $(GCC_ARGS) $(LINKER_ARGS) -o $@ $($(LIB_NAME)_OBJS) $(LIBS) $(LD_PATH) From 9cb9ba12e63c71c3895feea90ec5c6ecb6e136c9 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 28 Sep 2014 17:24:26 +0100 Subject: [PATCH 0589/2656] MixedLang: Push more up to the GPI layer --- include/embed.h | 8 + include/gpi.h | 6 +- lib/Makefile | 6 +- lib/gpi/Makefile | 18 +-- lib/gpi/gpi_common.cpp | 262 ++++++++++++++++++-------------- lib/gpi/gpi_priv.h | 154 +++++++++++++------ lib/simulator/simulatormodule.c | 2 + lib/vpi/Makefile | 18 ++- lib/vpi/gpi_vpi.cpp | 229 ++++++++++++++-------------- makefiles/Makefile.inc | 6 +- 10 files changed, 412 insertions(+), 297 deletions(-) diff --git a/include/embed.h b/include/embed.h index 655be032..4f1203d8 100644 --- a/include/embed.h +++ b/include/embed.h @@ -34,8 +34,16 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + extern void embed_init_python(void); extern void embed_sim_init(gpi_sim_info_t *info); extern void embed_sim_event(gpi_event_t level, const char *msg); +#ifdef __cplusplus +} +#endif + #endif /* COCOTB_EMBED_H_ */ diff --git a/include/gpi.h b/include/gpi.h index 682b889c..481b9ee8 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -98,12 +98,10 @@ typedef struct gpi_sim_info_s } gpi_sim_info_t; // Define a type for our simulation handle. -typedef struct gpi_sim_hdl_s { - void *sim_hdl; // Opaque handle for for a simulator object -} gpi_sim_hdl_t, *gpi_sim_hdl; +typedef void * gpi_sim_hdl; // Define a handle type for iterators -typedef struct __gpi_iterator_hdl *gpi_iterator_hdl; +typedef void * gpi_iterator_hdl; // Functions for controlling/querying the simulation state diff --git a/lib/Makefile b/lib/Makefile index a6b5cd9b..a712d3e0 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -58,15 +58,13 @@ $(LIB_DIR)/libvpi.so: $(SIM_ROOT)/lib/vpi/gpi_vpi.cpp | $(LIB_DIR) $(LIB_DIR)/libvhpi.so: $(SIM_ROOT)/lib/vhpi/gpi_vhpi.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/vhpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) -$(LIB_DIR)/libgpi.so: $(SIM_ROOT)/lib/gpi/gpi_common.cpp | $(LIB_DIR) +$(LIB_DIR)/libgpi.so: $(SIM_ROOT)/lib/gpi/gpi_common.cpp $(SIM_ROOT)/lib/gpi/gpi_cb_hdl.cpp | $(LIB_DIR) make -C $(SIM_ROOT)/lib/gpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) $(LIB_DIR)/libsim.so: $(SIM_ROOT)/lib/simulator/simulatormodule.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/simulator -COCOTB_LIBS = $(LIB_DIR)/libvpi.so - -#COCOTB_LIBS = $(LIB_DIR)/libgpilog.so \ +COCOTB_LIBS = $(LIB_DIR)/libgpilog.so \ $(LIB_DIR)/libcocotb.so \ $(LIB_DIR)/libgpi.so \ $(LIB_DIR)/libvpi.so \ diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index 23edbdac..25535308 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -31,28 +31,16 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += GCC_ARGS += -DVPI_CHECKING -LIBS := -lgpilog -lcocotb $(EXTRA_LIBS) +LIBS := -lgpilog -lcocotb -lstdc++ $(EXTRA_LIBS) LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) LIB_NAME := libgpi.so -SRCS := gpi_common.cpp +SRCS := gpi_cb_hdl.cpp gpi_common.cpp -CLIBS += $(LIB_DIR)/gpivpi.vpl - -# More rules such that this may be needed depending on the requirements of -# different simulators, icarus for instance loads a .vpi libraray to use the vpi -# inerface at the start of the simulation - -all: $(LIB_DIR)/gpivpi.vpl $(LIB_DIR)/cocotb.vpi $(IMPS) - -$(LIB_DIR)/gpivpi.vpl: $(LIB_DIR)/$(LIB_NAME) - ln -sf $(LIB_NAME) $@ - -$(LIB_DIR)/cocotb.vpi: $(LIB_DIR)/$(LIB_NAME) +all: $(LIB_DIR)/$(LIB_NAME) ln -sf $(LIB_NAME) $@ clean: - -@rm -rf $(LIB_DIR)/gpivpi.vpl -@rm -rf $(LIB_DIR)/$(LIB_NAME) include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/lib/gpi/gpi_common.cpp b/lib/gpi/gpi_common.cpp index a1bdd1ec..e81c0457 100644 --- a/lib/gpi/gpi_common.cpp +++ b/lib/gpi/gpi_common.cpp @@ -28,24 +28,49 @@ ******************************************************************************/ #include "gpi_priv.h" +#include -#define MAX_IMPLS 5 +using namespace std; -typedef struct impls { - gpi_impl_tbl *tbl; - int type; -} r_impl; - -static r_impl registed_impls[MAX_IMPLS] = {{NULL,0},}; +static inline gpi_cb_hdl * sim_to_cbhdl(gpi_sim_hdl hdl, bool fatal) +{ + gpi_cb_hdl *cb_hdl = reinterpret_cast(hdl); + if (!cb_hdl) { + LOG_CRITICAL("GPI: Handle passed down is not valid gpi_sim_hdl"); + if (fatal) + exit(1); + } + return cb_hdl; +} -#define IMPL_ROOT registed_impls[0].tbl +static inline gpi_obj_hdl * sim_to_objhdl(gpi_sim_hdl hdl, bool fatal) +{ + gpi_obj_hdl *obj_hdl = reinterpret_cast(hdl); + if (!obj_hdl) { + LOG_CRITICAL("GPI: Handle passed down is not valid gpi_onj_hdl"); + if (fatal) + exit(1); + } + return obj_hdl; +} -static inline void set_user_data(gpi_sim_hdl hdl, int (*gpi_function)(void*), void *data) +static inline gpi_iterator * sim_to_iterhdl(gpi_sim_hdl hdl, bool fatal) { - gpi_cb_hdl gpi_user_data = gpi_container_of(hdl, gpi_cb_hdl_t, hdl); + gpi_iterator *iter_hdl = reinterpret_cast(hdl); + if (!iter_hdl) { + LOG_CRITICAL("GPI: Handle passed down is not valid gpi_iterator"); + if (fatal) + exit(1); + } + return iter_hdl; +} + +static vector registed_impls; - gpi_user_data->gpi_cb_data = data; - gpi_user_data->gpi_function = gpi_function; +int gpi_register_impl(gpi_impl_interface *func_tbl) +{ + registed_impls.push_back(func_tbl); + return 0; } void gpi_embed_init(gpi_sim_info_t *info) @@ -60,7 +85,7 @@ void gpi_embed_end(void) void gpi_sim_end(void) { - IMPL_ROOT->sim_end(); + registed_impls[0]->sim_end(); } void gpi_embed_init_python(void) @@ -70,113 +95,168 @@ void gpi_embed_init_python(void) void gpi_get_sim_time(uint32_t *high, uint32_t *low) { - IMPL_ROOT->get_sim_time(high, low); + registed_impls[0]->get_sim_time(high, low); } gpi_sim_hdl gpi_get_root_handle(const char *name) { - return IMPL_ROOT->get_root_handle(name); + /* May need to look over all the implementations that are registered + to find this handle */ + vector::iterator iter; + + gpi_obj_hdl *hdl; + + for (iter = registed_impls.begin(); + iter != registed_impls.end(); + iter++) { + if ((hdl = (*iter)->get_root_handle(name))) { + return (void*)hdl; + } + } + return NULL; } gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) { - return IMPL_ROOT->get_handle_by_name(name, parent); + vector::iterator iter; + + gpi_obj_hdl *hdl; + gpi_obj_hdl *base = sim_to_objhdl(parent, true); + + /* Either want this or use the parent */ + for (iter = registed_impls.begin(); + iter != registed_impls.end(); + iter++) { + if ((hdl = (*iter)->get_handle_by_name(name, base))) { + return (void*)hdl; + } + } +#if 0 + hdl = base->m_impl->get_handle_by_name(name, base); + return (void*)hdl; +#endif + return NULL; } gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) { - return IMPL_ROOT->get_handle_by_index(parent, index); + gpi_obj_hdl *obj_hdl = sim_to_objhdl(parent, false); + return (void*)obj_hdl->m_impl->get_handle_by_index(obj_hdl, index); } gpi_iterator_hdl gpi_iterate(uint32_t type, gpi_sim_hdl base) { - return IMPL_ROOT->iterate_handle(type, base);; + gpi_obj_hdl *obj_hdl = sim_to_objhdl(base, false); + gpi_iterator *iter = obj_hdl->m_impl->iterate_handle(type, obj_hdl); + if (iter) { + return NULL; + } + iter->parent = obj_hdl; + return (void*)iter; } gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) { - return IMPL_ROOT->next_handle(iterator); + gpi_iterator *iter = sim_to_iterhdl(iterator, false); + return (void*)iter->parent->m_impl->next_handle(iter); } -char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) +char *gpi_get_signal_value_binstr(gpi_sim_hdl sig_hdl) { - return IMPL_ROOT->get_signal_value_binstr(gpi_hdl); + gpi_obj_hdl *obj_hdl = sim_to_objhdl(sig_hdl, false); + return obj_hdl->m_impl->get_signal_value_binstr(obj_hdl); } -char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl) +char *gpi_get_signal_name_str(gpi_sim_hdl sig_hdl) { - return IMPL_ROOT->get_signal_name_str(gpi_hdl); + gpi_obj_hdl *obj_hdl = sim_to_objhdl(sig_hdl, false); + return obj_hdl->m_impl->get_signal_name_str(obj_hdl); } -char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl) +char *gpi_get_signal_type_str(gpi_sim_hdl sig_hdl) { - return IMPL_ROOT->get_signal_type_str(gpi_hdl); + gpi_obj_hdl *obj_hdl = sim_to_objhdl(sig_hdl, false); + return obj_hdl->m_impl->get_signal_type_str(obj_hdl); } -void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) +void gpi_set_signal_value_int(gpi_sim_hdl sig_hdl, int value) { - IMPL_ROOT->set_signal_value_int(gpi_hdl, value); + gpi_obj_hdl *obj_hdl = sim_to_objhdl(sig_hdl, false); + obj_hdl->m_impl->set_signal_value_int(obj_hdl, value); } -void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) +void gpi_set_signal_value_str(gpi_sim_hdl sig_hdl, const char *str) { - IMPL_ROOT->set_signal_value_str(gpi_hdl, str); + gpi_obj_hdl *obj_hdl = sim_to_objhdl(sig_hdl, false); + obj_hdl->m_impl->set_signal_value_str(obj_hdl, str); } -void *gpi_get_callback_data(gpi_sim_hdl gpi_hdl) +void *gpi_get_callback_data(gpi_sim_hdl sim_hdl) { - return IMPL_ROOT->get_callback_data(gpi_hdl); + gpi_cb_hdl *cb_hdl = sim_to_cbhdl(sim_hdl, false); + return cb_hdl->get_user_data(); } -int gpi_register_timed_callback(gpi_sim_hdl hdl, - int (*gpi_function)(void *), - void *gpi_cb_data, uint64_t time_ps) +gpi_sim_hdl gpi_register_value_change_callback(gpi_sim_hdl cb_hdl, + int (*gpi_function)(void *), + void *gpi_cb_data, gpi_sim_hdl sig_hdl) { - set_user_data(hdl, gpi_function, gpi_cb_data); - return IMPL_ROOT->register_timed_callback(hdl, gpi_function, gpi_cb_data, time_ps); + gpi_cb_hdl *gpi_hdl = sim_to_cbhdl(cb_hdl, false); + gpi_obj_hdl *obj_hdl = sim_to_objhdl(sig_hdl, false); + gpi_hdl->set_user_data(gpi_function, gpi_cb_data); + return gpi_hdl->m_impl->register_value_change_callback(gpi_hdl, obj_hdl); } -int gpi_register_value_change_callback(gpi_sim_hdl hdl, - int (*gpi_function)(void *), - void *gpi_cb_data, gpi_sim_hdl gpi_hdl) +/* It should not matter which implementation we use for this so just pick the first + one +*/ +gpi_sim_hdl gpi_register_timed_callback(gpi_sim_hdl cb_hdl, + int (*gpi_function)(void *), + void *gpi_cb_data, uint64_t time_ps) { - set_user_data(hdl, gpi_function, gpi_cb_data); - return IMPL_ROOT->register_value_change_callback(hdl, gpi_function, gpi_cb_data, gpi_hdl); + gpi_cb_hdl *gpi_hdl = sim_to_cbhdl(cb_hdl, false); + gpi_hdl->set_user_data(gpi_function, gpi_cb_data); + return gpi_hdl->m_impl->register_timed_callback(gpi_hdl, time_ps); } -int gpi_register_readonly_callback(gpi_sim_hdl hdl, +/* It should not matter which implementation we use for this so just pick the first + one +*/ +gpi_sim_hdl gpi_register_readonly_callback(gpi_sim_hdl cb_hdl, int (*gpi_function)(void *), void *gpi_cb_data) { - set_user_data(hdl, gpi_function, gpi_cb_data); - return IMPL_ROOT->register_readonly_callback(hdl, gpi_function, gpi_cb_data); + gpi_cb_hdl *gpi_hdl = sim_to_cbhdl(cb_hdl, false); + gpi_hdl->set_user_data(gpi_function, gpi_cb_data); + return gpi_hdl->m_impl->register_readonly_callback(gpi_hdl); } -int gpi_register_nexttime_callback(gpi_sim_hdl hdl, +gpi_sim_hdl gpi_register_nexttime_callback(gpi_sim_hdl cb_hdl, int (*gpi_function)(void *), void *gpi_cb_data) { - set_user_data(hdl, gpi_function, gpi_cb_data); - return IMPL_ROOT->register_nexttime_callback(hdl, gpi_function, gpi_cb_data); + gpi_cb_hdl *gpi_hdl = sim_to_cbhdl(cb_hdl, false); + gpi_hdl->set_user_data(gpi_function, gpi_cb_data); + return gpi_hdl->m_impl->register_nexttime_callback(gpi_hdl); } -int gpi_register_readwrite_callback(gpi_sim_hdl hdl, +/* It should not matter which implementation we use for this so just pick the first + one +*/ +gpi_sim_hdl gpi_register_readwrite_callback(gpi_sim_hdl cb_hdl, int (*gpi_function)(void *), void *gpi_cb_data) { - set_user_data(hdl, gpi_function, gpi_cb_data); - return IMPL_ROOT->register_readwrite_callback(hdl, gpi_function, gpi_cb_data); + gpi_cb_hdl *gpi_hdl = sim_to_cbhdl(cb_hdl, false); + gpi_hdl->set_user_data(gpi_function, gpi_cb_data); + return gpi_hdl->m_impl->register_readwrite_callback(gpi_hdl); } -void gpi_deregister_callback(gpi_sim_hdl hdl) -{ - IMPL_ROOT->deregister_callback(hdl); -} -void gpi_handle_callback(gpi_sim_hdl hdl) +void gpi_deregister_callback(gpi_sim_hdl hdl) { - gpi_cb_hdl cb_hdl = gpi_container_of(hdl, gpi_cb_hdl_t, hdl); - cb_hdl->gpi_function(cb_hdl->gpi_cb_data); + gpi_cb_hdl *cb_hdl = sim_to_cbhdl(hdl, false); + cb_hdl->m_impl->deregister_callback(cb_hdl); } /* Callback handles are abstracted to the implementation layer since @@ -184,80 +264,38 @@ void gpi_handle_callback(gpi_sim_hdl hdl) */ gpi_sim_hdl gpi_create_cb_handle(void) { - gpi_cb_hdl ret = NULL; - - ret = IMPL_ROOT->create_cb_handle(); - if (!ret) { - LOG_CRITICAL("GPI: Attempting allocate user_data failed!"); + gpi_cb_hdl *cb_hdl = new gpi_cb_hdl(); + if (!cb_hdl) { + LOG_CRITICAL("GPI: Attempting allocate callback handle failed!"); + exit(1); } - - return &ret->hdl; + return (void*)cb_hdl; } void gpi_free_cb_handle(gpi_sim_hdl hdl) { - gpi_cb_hdl cb_hdl = gpi_container_of(hdl, gpi_cb_hdl_t, hdl); - IMPL_ROOT->destroy_cb_handle(cb_hdl); + gpi_cb_hdl *cb_hdl = sim_to_cbhdl(hdl, true); + delete(cb_hdl); } /* This needs to be filled with the pointer to the table */ gpi_sim_hdl gpi_create_handle(void) { - gpi_sim_hdl new_hdl = calloc(1, sizeof(*new_hdl)); + gpi_obj_hdl *new_hdl = new gpi_obj_hdl(); if (!new_hdl) { LOG_CRITICAL("GPI: Could not allocate handle"); exit(1); } - return new_hdl; + return (void*)new_hdl; } void gpi_free_handle(gpi_sim_hdl hdl) { - free(hdl); -} - -int gpi_register_impl(gpi_impl_tbl *func_tbl, int type) -{ - int idx; - for (idx = 0; idx < MAX_IMPLS; idx++) { - if (!registed_impls[idx].tbl) { - registed_impls[idx].tbl = func_tbl; - registed_impls[idx].type = type; - } - } - return 0; + gpi_obj_hdl *obj = sim_to_objhdl(hdl, true); + delete(obj); } -char *gpi_copy_name(const char *name) -{ - int len; - char *result; - const char null[] = "NULL"; - - if (name) - len = strlen(name) + 1; - else { - LOG_CRITICAL("GPI: attempt to use NULL from impl"); - len = strlen(null); - name = null; - } - - result = (char *)malloc(len); - if (result == NULL) { - LOG_CRITICAL("GPI: Attempting allocate string buffer failed!"); - len = strlen(null); - name = null; - } - - snprintf(result, len, "%s", name); - - return result; -} - -/* TODO - Each of the top level calls then needs to call into a function - table pointer to do the actual implementation. This can be on a per - handle basis. -*/ +gpi_impl_interface::~gpi_impl_interface() { } +gpi_impl_interface::gpi_impl_interface(const string& name) : m_name(name) { } \ No newline at end of file diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index fa0b62b6..089ed2d4 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -29,65 +29,125 @@ #include #include +#include -#define gpi_container_of(_address, _type, _member) \ - ((_type *)((uintptr_t)(_address) - \ - (uintptr_t)(&((_type *)0)->_member))) +using namespace std; -// Define a type for a simulator callback handle -//typedef struct gpi_cb_hdl_s { -// gpi_sim_hdl_t hdl; -// int (*gpi_function)(void *); // GPI function to callback -// void *gpi_cb_data; // GPI data supplied to "gpi_function" -//} gpi_cb_hdl_t, *gpi_cb_hdl; +class gpi_impl_interface; -class gpi_cb_hdl { +class gpi_hdl { public: - gpi_cb_hdl(); - virtual ~gpi_cb_hdl() { + gpi_hdl(); + virtual ~gpi_hdl() { } - } -private: + void set_gpi_impl(gpi_impl_interface *impl); + +public: + gpi_impl_interface *m_impl; // Implementation routines +}; + +class gpi_obj_hdl : public gpi_hdl { +public: + char *gpi_copy_name(const char *name); +}; + +class gpi_cb_hdl : public gpi_hdl { +public: + /* Override to change behaviour as needed */ + int handle_callback(void); + virtual int arm_callback(void); + virtual int run_callback(void); + virtual int cleanup_callback(void) = 0; + + int set_user_data(int (*gpi_function)(void*), void *data); + void *get_user_data(void); + +protected: int (*gpi_function)(void *); // GPI function to callback - void *gpi_cb_data; // GPI data supplied to "gpi_function" + void *m_cb_data; // GPI data supplied to "gpi_function" }; -class gpi_impl_interface { +class gpi_recurring_cb : public gpi_cb_hdl { public: - gpi_impl_interface(); - virtual ~gpi_impl_interface() = 0; - - virtual void sim_end(void) = 0; - virtual void get_sim_time(uint32_t *high, uint32_t *low); - virtual gpi_sim_hdl get_root_handle(const char *name) = 0; - virtual gpi_sim_hdl get_handle_by_name(const char *name, gpi_sim_hdl parent) = 0; - virtual gpi_sim_hdl get_handle_by_index(gpi_sim_hdl parent, uint32_t index) = 0; - virtual void free_handle(gpi_sim_hdl) = 0; - virtual gpi_iterator_hdl iterate_handle(uint32_t type, gpi_sim_hdl base) = 0; - virtual gpi_sim_hdl next_handle(gpi_iterator_hdl iterator) = 0; - virtual char* get_signal_value_binstr(gpi_sim_hdl gpi_hdl) = 0; - virtual char* get_signal_name_str(gpi_sim_hdl gpi_hdl) = 0; - virtual char* get_signal_type_str(gpi_sim_hdl gpi_hdl) = 0; - virtual void set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) = 0; - virtual void set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) = 0; // String of binary char(s) [1, 0, x, z] - virtual int register_timed_callback(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps) = 0; - virtual int register_value_change_callback(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl) = 0; - virtual int register_readonly_callback(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data) = 0; - virtual int register_nexttime_callback(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data) = 0; - virtual int register_readwrite_callback(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data) = 0; - virtual gpi_cb_hdl *create_cb_handle(void) = 0; - virtual void destroy_cb_handle(gpi_cb_hdl *gpi_hdl) = 0; - virtual int deregister_callback(gpi_sim_hdl gpi_hdl) = 0; - //virtual void* get_callback_data(gpi_sim_hdl gpi_hdl) = 0; + int cleanup_callback(void); }; -int gpi_register_impl(const gpi_impl_interface &func_tbl, int type); +class gpi_onetime_cb : public gpi_cb_hdl { +public: + int cleanup_callback(void); +}; -void gpi_embed_init(gpi_sim_info_t *info); -void gpi_embed_end(void); -void gpi_embed_init_python(void); +class gpi_cb_timed : public gpi_onetime_cb { +public: + int run_callback(void); +}; + +class gpi_cb_value_change : public gpi_recurring_cb { +public: + int run_callback(void); +}; + +class gpi_cb_readonly_phase : public gpi_onetime_cb { +public: + int run_callback(void); +}; -char *gpi_copy_name(const char *name); +class gpi_cb_nexttime_phase : public gpi_onetime_cb { +public: + int run_callback(void); +}; -void gpi_handle_callback(gpi_sim_hdl cb_data); +class gpi_cb_readwrite_phase : public gpi_onetime_cb { +public: + int run_callback(void); +}; +class gpi_iterator { +public: + gpi_obj_hdl *parent; +}; + +class gpi_impl_interface { +public: + string m_name; + +public: + gpi_impl_interface(const string& name); + virtual ~gpi_impl_interface() = 0; + + /* Sim related */ + virtual void sim_end(void) = 0; + virtual void get_sim_time(uint32_t *high, uint32_t *low) = 0; + + /* Signal related */ + virtual gpi_obj_hdl *get_root_handle(const char *name) = 0; + virtual gpi_obj_hdl *get_handle_by_name(const char *name, gpi_obj_hdl *parent) = 0; + virtual gpi_obj_hdl *get_handle_by_index(gpi_obj_hdl *parent, uint32_t index) = 0; + virtual void free_handle(gpi_obj_hdl*) = 0; + virtual gpi_iterator *iterate_handle(uint32_t type, gpi_obj_hdl *base) = 0; + virtual gpi_obj_hdl *next_handle(gpi_iterator *iterator) = 0; + virtual char* get_signal_value_binstr(gpi_obj_hdl *gpi_hdl) = 0; + virtual char* get_signal_name_str(gpi_obj_hdl *gpi_hdl) = 0; + virtual char* get_signal_type_str(gpi_obj_hdl *gpi_hdl) = 0; + virtual void set_signal_value_int(gpi_obj_hdl *gpi_hdl, int value) = 0; + virtual void set_signal_value_str(gpi_obj_hdl *gpi_hdl, const char *str) = 0; // String of binary char(s) [1, 0, x, z] + + /* Callback related */ + virtual gpi_cb_timed *register_timed_callback(gpi_cb_hdl*, uint64_t time_ps) = 0; + virtual gpi_cb_value_change *register_value_change_callback(gpi_cb_hdl *gpi_hdl, gpi_obj_hdl *obj_hdl) = 0; + virtual gpi_cb_readonly_phase *register_readonly_callback(gpi_cb_hdl *gpi_hdl) = 0; + virtual gpi_cb_nexttime_phase *register_nexttime_callback(gpi_cb_hdl *gpi_hdl) = 0; + virtual gpi_cb_readwrite_phase *register_readwrite_callback(gpi_cb_hdl *gpi_hdl) = 0; + virtual int deregister_callback(gpi_cb_hdl *gpi_hdl) = 0; + + virtual gpi_cb_hdl *create_cb_handle(void) = 0; + virtual void destroy_cb_handle(gpi_cb_hdl *gpi_hdl) = 0; + //virtual void* get_callback_data(gpi_sim_hdl gpi_hdl) = 0; +}; + +/* Called from implementaton layers back up the stack */ +int gpi_register_impl(gpi_impl_interface *func_tbl); + +void gpi_embed_init(gpi_sim_info_t *info); +void gpi_embed_end(void); +void gpi_embed_init_python(void); \ No newline at end of file diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 2e5873ee..8138c38e 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -93,6 +93,8 @@ int handle_gpi_callback(void *user_data) // Python allowed + LOG_ERROR("Callback now"); + if (!PyCallable_Check(callback_data_p->function)) { fprintf(stderr, "Callback fired but function isn't callable?!\n"); DROP_GIL(gstate); diff --git a/lib/vpi/Makefile b/lib/vpi/Makefile index 85745bf1..8ef088d6 100644 --- a/lib/vpi/Makefile +++ b/lib/vpi/Makefile @@ -31,15 +31,29 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += GCC_ARGS += -DVPI_CHECKING -LIBS := -lgpilog +LIBS := -lgpilog -lgpi -lstdc++ LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) LIB_NAME := libvpi.so SRCS := gpi_vpi.cpp -all: $(LIB_DIR)/$(LIB_NAME) +CLIBS += $(LIB_DIR)/gpivpi.vpl + +# More rules such that this may be needed depending on the requirements of +# different simulators, icarus for instance loads a .vpi libraray to use the vpi +# inerface at the start of the simulation + +all: $(LIB_DIR)/gpivpi.vpl $(LIB_DIR)/cocotb.vpi $(IMPS) + +$(LIB_DIR)/gpivpi.vpl: $(LIB_DIR)/$(LIB_NAME) + ln -sf $(LIB_NAME) $@ + +$(LIB_DIR)/cocotb.vpi: $(LIB_DIR)/$(LIB_NAME) + ln -sf $(LIB_NAME) $@ clean: -@rm -rf $(LIB_DIR)/$(LIB_NAME) + -@rm -rf $(LIB_DIR)/gpivpi.vpl + -@rm -rf $(LIB_DIR)/cocotb.vpi include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/lib/vpi/gpi_vpi.cpp b/lib/vpi/gpi_vpi.cpp index 22f5758e..e12c255b 100644 --- a/lib/vpi/gpi_vpi.cpp +++ b/lib/vpi/gpi_vpi.cpp @@ -81,15 +81,22 @@ static int __check_vpi_error(const char *func, long line) __check_vpi_error(__func__, __LINE__); \ } while (0) +class vpi_callback_handler; +class vpi_onetime_handler; +class vpi_recurring_handler; + class vpi_cb_hdl : public gpi_cb_hdl { -protected: +friend class vpi_onetime_handler; +friend class vpi_recurring_handler; +private: s_vpi_value cb_value; vpi_cb_state_t state; - gpi_cb_hdl gpi_cb_data; - vpiHandle cb_hdl; + //gpi_cb_hdl gpi_cb_data; + vpiHandle vpi_hdl; + vpi_callback_handler *handler; public: - vpi_cb_hdl() { + vpi_cb_hdl() : vpi_hdl(NULL) { } virtual ~vpi_cb_hdl() { @@ -97,35 +104,49 @@ class vpi_cb_hdl : public gpi_cb_hdl { } const char *vpi_reason_to_string(int reason); - void set_state(vpi_cb_state_t new_state); - virtual int arm_callback(void); - virtual int cleanup_callback(void); - virtual int run_callback(void); -protected: - int __vpi_register_cb(p_cb_data cb_data); + void set_state(vpi_cb_state_t new_state) { + state = new_state; + } + + int arm_callback(p_cb_data cb_data, vpi_callback_handler *hdlr); + int run_callback(void); + int cancel_callback(void); }; -class vpi_one_time_callback : public vpi_cb_hdl { -protected: - int cleanup_callback(void) { +class vpi_callback_handler { +public: + virtual ~vpi_callback_handler() { + + } + virtual int cleanup_handler(vpi_cb_hdl *cb) { + return 0; + } + virtual int run_handler(vpi_cb_hdl *cb) { + return 0; + } +}; + +class vpi_onetime_handler : public vpi_callback_handler { +public: + int cleanup_handler(vpi_cb_hdl *cb) { FENTER - int rc; - if (!cb_hdl) { + int rc = 0; + if (!cb->vpi_hdl) { LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); exit(1); } // If the callback has not been called we also need to call // remove as well - if (state == VPI_PRIMED) { + if (cb->state == VPI_PRIMED) { - rc = vpi_remove_cb(cb_hdl); + rc = vpi_remove_cb(cb->vpi_hdl); if (!rc) { check_vpi_error(); return rc; } - cb_hdl = NULL; + cb->vpi_hdl = NULL; // HACK: Calling vpi_free_object after vpi_remove_cb causes Modelsim to VPIEndOfSimulationCallback #if 0 @@ -136,47 +157,35 @@ class vpi_one_time_callback : public vpi_cb_hdl { } #endif } - state = VPI_FREE; + cb->state = VPI_FREE; FEXIT return rc; } }; -class vpi_reurring_callback : public vpi_cb_hdl { +class vpi_recurring_handler : public vpi_callback_handler { public: - int cleanup_callback(void) { + int cleanup_handler(vpi_cb_hdl *cb) { FENTER int rc; - if (!cb_hdl) { + LOG_ERROR("In cleanup"); + + if (!cb->vpi_hdl) { LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); exit(1); } - rc = vpi_remove_cb(cb_hdl); + rc = vpi_remove_cb(cb->vpi_hdl); check_vpi_error(); FEXIT return rc; } }; -class vpi_initial_cb_hdl : public vpi_one_time_callback { - +class vpi_startup_handler : public vpi_onetime_handler { public: - int arm_callback(void) { - s_cb_data cb_data_s; - - cb_data_s.reason = cbStartOfSimulation; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = NULL; - cb_data_s.value = NULL; - cb_data_s.user_data = (char*)this; - - return __vpi_register_cb(&cb_data_s); - } - - int run_callback(void) { + int run_handler(vpi_cb_hdl *cb) { s_vpi_vlog_info info; gpi_sim_info_t sim_info; @@ -193,29 +202,27 @@ class vpi_initial_cb_hdl : public vpi_one_time_callback { } }; -class vpi_final_cb_hdl : public vpi_one_time_callback { -public: - int arm_callback(void) { - s_cb_data cb_data_s; - - cb_data_s.reason = cbEndOfSimulation; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = NULL; - cb_data_s.value = NULL; - cb_data_s.user_data = (char*)this; - return __vpi_register_cb(&cb_data_s); +class vpi_shutdown_handler : public vpi_onetime_handler { +public: + int run_handler(vpi_cb_hdl *cb) { + //gpi_embed_end(); + return 0; } +}; - int run_callback(void) { - gpi_embed_end(); +class vpi_timed_handler : public vpi_onetime_handler { +public: + int run_handler(vpi_cb_hdl *cb) { + LOG_ERROR("In timed handler") + gpi_handle_callback(cb); return 0; } }; class vpi_impl : public gpi_impl_interface { public: + vpi_impl(const string& name) : gpi_impl_interface(name) { } void sim_end(void); void get_sim_time(uint32_t *high, uint32_t *low); @@ -268,7 +275,7 @@ const char * vpi_cb_hdl::vpi_reason_to_string(int reason) } } -int vpi_cb_hdl::__vpi_register_cb(p_cb_data cb_data) +int vpi_cb_hdl::arm_callback(p_cb_data cb_data, vpi_callback_handler *hdlr) { /* If the user data already has a callback handle then deregister * before getting the new one @@ -279,7 +286,7 @@ int vpi_cb_hdl::__vpi_register_cb(p_cb_data cb_data) vpi_reason_to_string(cb_data->reason)); } - if (cb_hdl != NULL) { + if (vpi_hdl != NULL) { fprintf(stderr, "We seem to already be registered, deregistering %s!\n", vpi_reason_to_string(cb_data->reason)); @@ -298,12 +305,28 @@ int vpi_cb_hdl::__vpi_register_cb(p_cb_data cb_data) ret = -1; } - cb_hdl = new_hdl; + vpi_hdl = new_hdl; + handler = hdlr; state = VPI_PRIMED; return ret; } +int vpi_cb_hdl::run_callback(void) +{ + vpi_hdl = NULL; + handler->run_handler(this); + delete(handler); + handler = NULL; + /* Can now be re-used */ + return 0; +} + +int vpi_cb_hdl::cancel_callback(void) +{ + return handler->cleanup_handler(this); +} + // Handle related functions /** @@ -475,7 +498,8 @@ gpi_sim_hdl vpi_impl::get_handle_by_index(gpi_sim_hdl parent, uint32_t index) // Functions for iterating over entries of a handle // Returns an iterator handle which can then be used in gpi_next calls // NB May return NULL if no objects of the request type exist -gpi_iterator_hdl vpi_impl::iterate_handle(uint32_t type, gpi_sim_hdl base) { +gpi_iterator_hdl vpi_impl::iterate_handle(uint32_t type, gpi_sim_hdl base) +{ FENTER vpiHandle iterator; @@ -487,6 +511,11 @@ gpi_iterator_hdl vpi_impl::iterate_handle(uint32_t type, gpi_sim_hdl base) { return (gpi_iterator_hdl)iterator; } +void vpi_impl::free_handle(gpi_sim_hdl) +{ + +} + // Returns NULL when there are no more objects gpi_sim_hdl vpi_impl::next_handle(gpi_iterator_hdl iterator) { @@ -663,8 +692,8 @@ int vpi_impl::deregister_callback(gpi_sim_hdl gpi_hdl) // do some pointer magic. vpi_cb_hdl *hdl = reinterpret_cast(gpi_hdl->sim_hdl); - - hdl->cleanup_callback(); + (void)hdl; + hdl->cancel_callback(); FEXIT GPI_RET(rc); @@ -837,19 +866,12 @@ int vpi_impl::register_timed_callback(gpi_sim_hdl cb, void *gpi_cb_data, uint64_t time_ps) { -#if 0 FENTER int ret; s_cb_data cb_data_s; s_vpi_time vpi_time_s; - p_vpi_cb vpi_user_data; - gpi_cb_hdl gpi_user_data; - - gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); - vpi_user_data = gpi_container_of(gpi_user_data, s_vpi_cb, gpi_cb_data); - - vpi_user_data->vpi_cleanup = vpi_free_one_time; + vpi_cb_hdl *hdl = reinterpret_cast(cb->sim_hdl); vpi_time_s.type = vpiSimTime; vpi_time_s.high = (uint32_t)(time_ps>>32); @@ -860,16 +882,13 @@ int vpi_impl::register_timed_callback(gpi_sim_hdl cb, cb_data_s.obj = NULL; cb_data_s.time = &vpi_time_s; cb_data_s.value = NULL; - cb_data_s.user_data = (char *)vpi_user_data; + cb_data_s.user_data = (char *)hdl; - ret = __vpi_register_cb(vpi_user_data, &cb_data_s); + vpi_timed_handler *handler = new vpi_timed_handler(); + ret = hdl->arm_callback(&cb_data_s, handler); FEXIT - return ret; -#else - return 0; -#endif } @@ -909,35 +928,11 @@ void vpi_impl::sim_end(void) check_vpi_error(); } -#if 0 -static s_gpi_impl_tbl vpi_table = { - .sim_end = vpi_sim_end, - .iterate_handle = vpi_iterate_hdl, - .next_handle = vpi_next_hdl, - .create_cb_handle = vpi_create_cb_handle, - .destroy_cb_handle = vpi_destroy_cb_handle, - .deregister_callback = vpi_deregister_callback, - .get_root_handle = vpi_get_root_handle, - .get_sim_time = vpi_get_sim_time, - .get_handle_by_name = vpi_get_handle_by_name, - .get_handle_by_index = vpi_get_handle_by_index, - .get_signal_name_str = vpi_get_signal_name_str, - .get_signal_type_str = vpi_get_signal_type_str, - .get_signal_value_binstr = vpi_get_signal_value_binstr, - .set_signal_value_int = vpi_set_signal_value_int, - .set_signal_value_str = vpi_set_signal_value_str, - .register_timed_callback = vpi_register_timed_callback, - .register_readwrite_callback = vpi_register_readwrite_callback, - .register_nexttime_callback = vpi_register_nexttime_callback, - .register_value_change_callback = vpi_register_value_change_callback, - .register_readonly_callback = vpi_register_readonly_callback, - .get_callback_data = vpi_get_callback_data, -}; -#endif +extern "C" { static void register_embed(void) { - vpi_impl vpi_table; + vpi_impl *vpi_table = new vpi_impl("VPI"); gpi_register_impl(vpi_table, 0xfeed); gpi_embed_init_python(); } @@ -945,33 +940,45 @@ static void register_embed(void) static void register_initial_callback(void) { - FENTER - - sim_init_cb = new vpi_initial_cb_hdl(); + sim_init_cb = new vpi_cb_hdl(); + vpi_startup_handler *handler = new vpi_startup_handler(); /* We ignore the return value here as VCS does some silly * things on comilation that means it tries to run through * the vlog_startup_routines and so call this routine */ - sim_init_cb->arm_callback(); + s_cb_data cb_data_s; - FEXIT + cb_data_s.reason = cbStartOfSimulation; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = NULL; + cb_data_s.value = NULL; + cb_data_s.user_data = (char*)sim_init_cb; + + sim_init_cb->arm_callback(&cb_data_s, handler); } static void register_final_callback(void) { - FENTER - - sim_finish_cb = new vpi_initial_cb_hdl(); + sim_finish_cb = new vpi_cb_hdl(); + vpi_shutdown_handler *handler = new vpi_shutdown_handler(); /* We ignore the return value here as VCS does some silly * things on comilation that means it tries to run through * the vlog_startup_routines and so call this routine */ - sim_finish_cb->arm_callback(); - - FEXIT + s_cb_data cb_data_s; + + cb_data_s.reason = cbEndOfSimulation; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = NULL; + cb_data_s.value = NULL; + cb_data_s.user_data = (char*)sim_finish_cb; + + sim_finish_cb->arm_callback(&cb_data_s, handler); } @@ -1092,3 +1099,5 @@ void vlog_startup_routines_bootstrap(void) { routine(); } } + +} \ No newline at end of file diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 8a5277ab..b3b5ea2c 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -63,9 +63,9 @@ ifeq ($(ARCH),i686) GXX_ARGS := -m32 endif GXX_ARGS += -Werror -Wcast-qual -Wcast-align -Wwrite-strings \ - -Waggregate-return -Wall \ - -Wno-unused-parameter -fno-common -g -DDEBUG -fpic -GCC_ARGS += $(GXX_ARGS) -Wstrict-prototypes + -Wall -Wno-unused-parameter \ + -fno-common -g -DDEBUG -fpic +GCC_ARGS += $(GXX_ARGS) -Wstrict-prototypes -Waggregate-return endif From 219d80a04ab72c3c3a00340d56530f2764d8c23d Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 28 Sep 2014 19:10:39 +0100 Subject: [PATCH 0590/2656] Changed interface to pass handles all the way up --- include/gpi.h | 10 +- lib/gpi/gpi_common.cpp | 60 ++- lib/gpi/gpi_priv.h | 12 +- lib/simulator/simulatormodule.c | 90 ++-- lib/vpi/gpi_vpi.cpp | 918 ++++++-------------------------- 5 files changed, 263 insertions(+), 827 deletions(-) diff --git a/include/gpi.h b/include/gpi.h index 481b9ee8..485b480c 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -159,11 +159,11 @@ void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value); void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str); // String of binary char(s) [1, 0, x, z] // The callback registering functions -int gpi_register_timed_callback (gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps); -int gpi_register_value_change_callback (gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl); -int gpi_register_readonly_callback (gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); -int gpi_register_nexttime_callback (gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); -int gpi_register_readwrite_callback (gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); +gpi_sim_hdl gpi_register_timed_callback (int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps); +gpi_sim_hdl gpi_register_value_change_callback (int (*gpi_function)(void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl); +gpi_sim_hdl gpi_register_readonly_callback (int (*gpi_function)(void *), void *gpi_cb_data); +gpi_sim_hdl gpi_register_nexttime_callback (int (*gpi_function)(void *), void *gpi_cb_data); +gpi_sim_hdl gpi_register_readwrite_callback (int (*gpi_function)(void *), void *gpi_cb_data); // Calling convention is that 0 = success and negative numbers a failure // For implementers of GPI the provided macro GPI_RET(x) is provided diff --git a/lib/gpi/gpi_common.cpp b/lib/gpi/gpi_common.cpp index e81c0457..88f6e521 100644 --- a/lib/gpi/gpi_common.cpp +++ b/lib/gpi/gpi_common.cpp @@ -197,59 +197,71 @@ void *gpi_get_callback_data(gpi_sim_hdl sim_hdl) return cb_hdl->get_user_data(); } -gpi_sim_hdl gpi_register_value_change_callback(gpi_sim_hdl cb_hdl, - int (*gpi_function)(void *), - void *gpi_cb_data, gpi_sim_hdl sig_hdl) +gpi_sim_hdl gpi_register_value_change_callback(int (*gpi_function)(void *), + void *gpi_cb_data, + gpi_sim_hdl sig_hdl) { - gpi_cb_hdl *gpi_hdl = sim_to_cbhdl(cb_hdl, false); gpi_obj_hdl *obj_hdl = sim_to_objhdl(sig_hdl, false); + gpi_cb_hdl *gpi_hdl = obj_hdl->m_impl->register_value_change_callback(obj_hdl); + if (!gpi_hdl) + LOG_ERROR("Failed to register a value change callback"); + gpi_hdl->set_user_data(gpi_function, gpi_cb_data); - return gpi_hdl->m_impl->register_value_change_callback(gpi_hdl, obj_hdl); + return (gpi_sim_hdl)gpi_hdl; } /* It should not matter which implementation we use for this so just pick the first one */ -gpi_sim_hdl gpi_register_timed_callback(gpi_sim_hdl cb_hdl, +gpi_sim_hdl gpi_register_timed_callback( int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps) { - gpi_cb_hdl *gpi_hdl = sim_to_cbhdl(cb_hdl, false); + gpi_cb_hdl *gpi_hdl = registed_impls[0]->register_timed_callback(time_ps); + if (!gpi_hdl) + LOG_ERROR("Failed to register a timed callback"); + gpi_hdl->set_user_data(gpi_function, gpi_cb_data); - return gpi_hdl->m_impl->register_timed_callback(gpi_hdl, time_ps); + return (gpi_sim_hdl)gpi_hdl; } /* It should not matter which implementation we use for this so just pick the first one */ -gpi_sim_hdl gpi_register_readonly_callback(gpi_sim_hdl cb_hdl, - int (*gpi_function)(void *), - void *gpi_cb_data) +gpi_sim_hdl gpi_register_readonly_callback(int (*gpi_function)(void *), + void *gpi_cb_data) { - gpi_cb_hdl *gpi_hdl = sim_to_cbhdl(cb_hdl, false); + gpi_cb_hdl *gpi_hdl = registed_impls[0]->register_readonly_callback(); + if (!gpi_hdl) + LOG_ERROR("Failed to register a readonly callback"); + gpi_hdl->set_user_data(gpi_function, gpi_cb_data); - return gpi_hdl->m_impl->register_readonly_callback(gpi_hdl); + return (gpi_sim_hdl)gpi_hdl; } -gpi_sim_hdl gpi_register_nexttime_callback(gpi_sim_hdl cb_hdl, - int (*gpi_function)(void *), - void *gpi_cb_data) +gpi_sim_hdl gpi_register_nexttime_callback(int (*gpi_function)(void *), + void *gpi_cb_data) { - gpi_cb_hdl *gpi_hdl = sim_to_cbhdl(cb_hdl, false); + gpi_cb_hdl *gpi_hdl = registed_impls[0]->register_nexttime_callback(); + if (!gpi_hdl) + LOG_ERROR("Failed to register a nexttime callback"); + gpi_hdl->set_user_data(gpi_function, gpi_cb_data); - return gpi_hdl->m_impl->register_nexttime_callback(gpi_hdl); + return (gpi_sim_hdl)gpi_hdl; } /* It should not matter which implementation we use for this so just pick the first one */ -gpi_sim_hdl gpi_register_readwrite_callback(gpi_sim_hdl cb_hdl, - int (*gpi_function)(void *), - void *gpi_cb_data) +gpi_sim_hdl gpi_register_readwrite_callback(int (*gpi_function)(void *), + void *gpi_cb_data) { - gpi_cb_hdl *gpi_hdl = sim_to_cbhdl(cb_hdl, false); + gpi_cb_hdl *gpi_hdl = registed_impls[0] ->register_nexttime_callback(); + if (!gpi_hdl) + LOG_ERROR("Failed to register a readwrite callback"); + gpi_hdl->set_user_data(gpi_function, gpi_cb_data); - return gpi_hdl->m_impl->register_readwrite_callback(gpi_hdl); + return (gpi_sim_hdl)gpi_hdl; } @@ -262,6 +274,7 @@ void gpi_deregister_callback(gpi_sim_hdl hdl) /* Callback handles are abstracted to the implementation layer since they may need to have some state stored on a per handle basis. */ +#if 0 gpi_sim_hdl gpi_create_cb_handle(void) { gpi_cb_hdl *cb_hdl = new gpi_cb_hdl(); @@ -277,6 +290,7 @@ void gpi_free_cb_handle(gpi_sim_hdl hdl) gpi_cb_hdl *cb_hdl = sim_to_cbhdl(hdl, true); delete(cb_hdl); } +#endif /* This needs to be filled with the pointer to the table */ diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 089ed2d4..3e12fc6e 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -37,7 +37,7 @@ class gpi_impl_interface; class gpi_hdl { public: - gpi_hdl(); + gpi_hdl() {} virtual ~gpi_hdl() { } void set_gpi_impl(gpi_impl_interface *impl); @@ -133,11 +133,11 @@ class gpi_impl_interface { virtual void set_signal_value_str(gpi_obj_hdl *gpi_hdl, const char *str) = 0; // String of binary char(s) [1, 0, x, z] /* Callback related */ - virtual gpi_cb_timed *register_timed_callback(gpi_cb_hdl*, uint64_t time_ps) = 0; - virtual gpi_cb_value_change *register_value_change_callback(gpi_cb_hdl *gpi_hdl, gpi_obj_hdl *obj_hdl) = 0; - virtual gpi_cb_readonly_phase *register_readonly_callback(gpi_cb_hdl *gpi_hdl) = 0; - virtual gpi_cb_nexttime_phase *register_nexttime_callback(gpi_cb_hdl *gpi_hdl) = 0; - virtual gpi_cb_readwrite_phase *register_readwrite_callback(gpi_cb_hdl *gpi_hdl) = 0; + virtual gpi_cb_hdl *register_timed_callback(uint64_t time_ps) = 0; + virtual gpi_cb_hdl *register_value_change_callback(gpi_obj_hdl *obj_hdl) = 0; + virtual gpi_cb_hdl *register_readonly_callback(void) = 0; + virtual gpi_cb_hdl *register_nexttime_callback(void) = 0; + virtual gpi_cb_hdl *register_readwrite_callback(void) = 0; virtual int deregister_callback(gpi_cb_hdl *gpi_hdl) = 0; virtual gpi_cb_hdl *create_cb_handle(void) = 0; diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 8138c38e..2489e0c3 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -160,9 +160,9 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; - PyObject *handle; + //PyObject *handle; gpi_sim_hdl hdl; - int ret; + //int ret; PyGILState_STATE gstate; gstate = TAKE_GIL(); @@ -176,11 +176,11 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) return NULL; } - handle = PyTuple_GetItem(args, 0); - hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(handle); + //handle = PyTuple_GetItem(args, 0); + //hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(handle); // Extract the callback function - function = PyTuple_GetItem(args, 1); + function = PyTuple_GetItem(args, 0); if (!PyCallable_Check(function)) { fprintf(stderr, "Attempt to register ReadOnly without supplying a callback!\n"); return NULL; @@ -189,7 +189,7 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) // Remaining args for function if (numargs > 2) - fArgs = PyTuple_GetSlice(args, 2, numargs); // New reference + fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference else fArgs = PyTuple_New(0); // args must not be NULL, use an empty tuple if no arguments are needed. @@ -204,10 +204,10 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) callback_data_p->function = function; callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; - callback_data_p->cb_hdl = hdl; - ret = gpi_register_readonly_callback(hdl, handle_gpi_callback, (void *)callback_data_p); + //callback_data_p->cb_hdl = hdl; + hdl = gpi_register_readonly_callback(handle_gpi_callback, (void *)callback_data_p); - PyObject *rv = Py_BuildValue("i", ret); + PyObject *rv = Py_BuildValue("l", hdl); DROP_GIL(gstate); FEXIT @@ -221,9 +221,9 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; - PyObject *handle; + //PyObject *handle; gpi_sim_hdl hdl; - int ret; + //int ret; PyGILState_STATE gstate; gstate = TAKE_GIL(); @@ -237,11 +237,11 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) return NULL; } - handle = PyTuple_GetItem(args, 0); - hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(handle); + //handle = PyTuple_GetItem(args, 0); + //hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(handle); // Extract the callback function - function = PyTuple_GetItem(args, 1); + function = PyTuple_GetItem(args, 0); if (!PyCallable_Check(function)) { fprintf(stderr, "Attempt to register ReadOnly without supplying a callback!\n"); return NULL; @@ -265,10 +265,10 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) callback_data_p->function = function; callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; - callback_data_p->cb_hdl = hdl; - ret = gpi_register_readwrite_callback(hdl, handle_gpi_callback, (void *)callback_data_p); + //callback_data_p->cb_hdl = hdl; + hdl = gpi_register_readwrite_callback(handle_gpi_callback, (void *)callback_data_p); - PyObject *rv = Py_BuildValue("i", ret); + PyObject *rv = Py_BuildValue("l", hdl); DROP_GIL(gstate); FEXIT @@ -282,9 +282,9 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; - PyObject *handle; + //PyObject *handle; gpi_sim_hdl hdl; - int ret; + //int ret; PyGILState_STATE gstate; gstate = TAKE_GIL(); @@ -298,11 +298,11 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) return NULL; } - handle = PyTuple_GetItem(args, 0); - hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(handle); + //handle = PyTuple_GetItem(args, 0); + //hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(handle); // Extract the callback function - function = PyTuple_GetItem(args, 1); + function = PyTuple_GetItem(args, 0); if (!PyCallable_Check(function)) { fprintf(stderr, "Attempt to register ReadOnly without supplying a callback!\n"); return NULL; @@ -326,10 +326,10 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) callback_data_p->function = function; callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; - callback_data_p->cb_hdl = hdl; - ret = gpi_register_nexttime_callback(hdl, handle_gpi_callback, (void *)callback_data_p); + //callback_data_p->cb_hdl = hdl; + hdl = gpi_register_nexttime_callback(handle_gpi_callback, (void *)callback_data_p); - PyObject *rv = Py_BuildValue("i", ret); + PyObject *rv = Py_BuildValue("l", hdl); DROP_GIL(gstate); FEXIT @@ -347,10 +347,10 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; - PyObject *handle; + //PyObject *handle; gpi_sim_hdl hdl; uint64_t time_ps; - int ret; + //int ret; p_callback_data callback_data_p; @@ -365,15 +365,15 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) } // Get the handle - handle = PyTuple_GetItem(args, 0); - hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(handle); + //handle = PyTuple_GetItem(args, 0); + //hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(handle); // Extract the time - PyObject *pTime = PyTuple_GetItem(args, 1); + PyObject *pTime = PyTuple_GetItem(args, 0); time_ps = PyLong_AsLongLong(pTime); // Extract the callback function - function = PyTuple_GetItem(args, 2); + function = PyTuple_GetItem(args, 1); if (!PyCallable_Check(function)) { fprintf(stderr, "Attempt to register timed callback without passing a callable callback!\n"); return NULL; @@ -381,8 +381,8 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) Py_INCREF(function); // Remaining args for function - if (numargs > 3) - fArgs = PyTuple_GetSlice(args, 3, numargs); // New reference + if (numargs > 2) + fArgs = PyTuple_GetSlice(args, 2, numargs); // New reference else fArgs = PyTuple_New(0); // args must not be NULL, use an empty tuple if no arguments are needed. @@ -398,11 +398,11 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) callback_data_p->function = function; callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; - callback_data_p->cb_hdl = hdl; - ret = gpi_register_timed_callback(hdl, handle_gpi_callback, (void *)callback_data_p, time_ps); + //callback_data_p->cb_hdl = hdl; + hdl = gpi_register_timed_callback(handle_gpi_callback, (void *)callback_data_p, time_ps); // Check success - PyObject *rv = Py_BuildValue("i", ret); + PyObject *rv = Py_BuildValue("l", hdl); DROP_GIL(gstate); FEXIT @@ -421,9 +421,9 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; gpi_sim_hdl sig_hdl; - PyObject *handle; + //PyObject *handle; gpi_sim_hdl hdl; - int ret; + //int ret; @@ -439,14 +439,14 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) return NULL; } - handle = PyTuple_GetItem(args, 0); - hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(handle); + //handle = PyTuple_GetItem(args, 0); + //hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(handle); - PyObject *pSihHdl = PyTuple_GetItem(args, 1); + PyObject *pSihHdl = PyTuple_GetItem(args, 0); sig_hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(pSihHdl); // Extract the callback function - function = PyTuple_GetItem(args, 2); + function = PyTuple_GetItem(args, 1); if (!PyCallable_Check(function)) { fprintf(stderr, "Attempt to register value change callback without passing a callable callback!\n"); return NULL; @@ -472,11 +472,11 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) callback_data_p->function = function; callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; - callback_data_p->cb_hdl = hdl; - ret = gpi_register_value_change_callback(hdl, handle_gpi_callback, (void *)callback_data_p, sig_hdl); + //callback_data_p->cb_hdl = hdl; + hdl = gpi_register_value_change_callback(handle_gpi_callback, (void *)callback_data_p, sig_hdl); // Check success - PyObject *rv = Py_BuildValue("i", ret); + PyObject *rv = Py_BuildValue("l", hdl); DROP_GIL(gstate); FEXIT diff --git a/lib/vpi/gpi_vpi.cpp b/lib/vpi/gpi_vpi.cpp index e12c255b..dcf29524 100644 --- a/lib/vpi/gpi_vpi.cpp +++ b/lib/vpi/gpi_vpi.cpp @@ -32,18 +32,14 @@ #define VPI_CHECKING 1 -extern "C" int32_t handle_vpi_callback(p_cb_data cb_data); +extern "C" { +int32_t handle_vpi_callback(p_cb_data cb_data); +int __check_vpi_error(const char *func, long line); +} -typedef enum vpi_cb_state_e { - VPI_FREE = 0, - VPI_PRIMED = 1, - VPI_PRE_CALL = 2, - VPI_POST_CALL = 3, - VPI_DELETE = 4, -} vpi_cb_state_t; // Should be run after every VPI call to check error status -static int __check_vpi_error(const char *func, long line) +int __check_vpi_error(const char *func, long line) { int level=0; #if VPI_CHECKING @@ -81,19 +77,18 @@ static int __check_vpi_error(const char *func, long line) __check_vpi_error(__func__, __LINE__); \ } while (0) -class vpi_callback_handler; -class vpi_onetime_handler; -class vpi_recurring_handler; +typedef enum vpi_cb_state_e { + VPI_FREE = 0, + VPI_PRIMED = 1, + VPI_PRE_CALL = 2, + VPI_POST_CALL = 3, + VPI_DELETE = 4, +} vpi_cb_state_t; class vpi_cb_hdl : public gpi_cb_hdl { -friend class vpi_onetime_handler; -friend class vpi_recurring_handler; -private: - s_vpi_value cb_value; - vpi_cb_state_t state; - //gpi_cb_hdl gpi_cb_data; +protected: vpiHandle vpi_hdl; - vpi_callback_handler *handler; + vpi_cb_state_t state; public: vpi_cb_hdl() : vpi_hdl(NULL) { @@ -103,89 +98,104 @@ friend class vpi_recurring_handler; } - const char *vpi_reason_to_string(int reason); +protected: + /* If the user data already has a callback handle then deregister + * before getting the new one + */ + int register_cb(p_cb_data cb_data) { + if (state == VPI_PRIMED) { + fprintf(stderr, + "Attempt to prime an already primed trigger for %s!\n", + vpi_reason_to_string(cb_data->reason)); + } - void set_state(vpi_cb_state_t new_state) { - state = new_state; - } + if (vpi_hdl != NULL) { + fprintf(stderr, + "We seem to already be registered, deregistering %s!\n", + vpi_reason_to_string(cb_data->reason)); - int arm_callback(p_cb_data cb_data, vpi_callback_handler *hdlr); - int run_callback(void); - int cancel_callback(void); -}; + cleanup_callback(); + } -class vpi_callback_handler { -public: - virtual ~vpi_callback_handler() { + vpiHandle new_hdl = vpi_register_cb(cb_data); + int ret = 0; + if (!new_hdl) { + LOG_CRITICAL("VPI: Unable to register callback a handle for VPI type %s(%d)", + vpi_reason_to_string(cb_data->reason), cb_data->reason); + check_vpi_error(); + ret = -1; + } + + vpi_hdl = new_hdl; + state = VPI_PRIMED; + + return ret; } - virtual int cleanup_handler(vpi_cb_hdl *cb) { - return 0; - } - virtual int run_handler(vpi_cb_hdl *cb) { - return 0; + + const char *vpi_reason_to_string(int reason) + { + switch (reason) { + case cbValueChange: + return "cbValueChange"; + case cbAtStartOfSimTime: + return "cbAtStartOfSimTime"; + case cbReadWriteSynch: + return "cbReadWriteSynch"; + case cbReadOnlySynch: + return "cbReadOnlySynch"; + case cbNextSimTime: + return "cbNextSimTime"; + case cbAfterDelay: + return "cbAfterDelay"; + case cbStartOfSimulation: + return "cbStartOfSimulation"; + case cbEndOfSimulation: + return "cbEndOfSimulation"; + default: + return "unknown"; + } } + }; -class vpi_onetime_handler : public vpi_callback_handler { +class vpi_onetime_cb : public vpi_cb_hdl { public: - int cleanup_handler(vpi_cb_hdl *cb) { + int cleanup_callback(void) { FENTER int rc = 0; - if (!cb->vpi_hdl) { + if (!vpi_hdl) { LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); exit(1); } - - // If the callback has not been called we also need to call - // remove as well - if (cb->state == VPI_PRIMED) { - - rc = vpi_remove_cb(cb->vpi_hdl); - if (!rc) { - check_vpi_error(); - return rc; - } - cb->vpi_hdl = NULL; - - // HACK: Calling vpi_free_object after vpi_remove_cb causes Modelsim to VPIEndOfSimulationCallback - #if 0 - rc = vpi_free_object(cb_hdl); - if (!rc) { - check_vpi_error(); - return rc; - } - #endif - } - cb->state = VPI_FREE; FEXIT return rc; } }; -class vpi_recurring_handler : public vpi_callback_handler { +class vpi_recurring_cb : public vpi_cb_hdl { public: - int cleanup_handler(vpi_cb_hdl *cb) { + int cleanup_handler(void) { FENTER int rc; - LOG_ERROR("In cleanup"); - - if (!cb->vpi_hdl) { + if (!vpi_hdl) { LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); exit(1); } - - rc = vpi_remove_cb(cb->vpi_hdl); - check_vpi_error(); FEXIT return rc; } }; -class vpi_startup_handler : public vpi_onetime_handler { +class vpi_cb_value_change : public vpi_recurring_cb { +public: + +}; + +class vpi_cb_startup : public vpi_onetime_cb { public: - int run_handler(vpi_cb_hdl *cb) { + int run_callback(void) { s_vpi_vlog_info info; gpi_sim_info_t sim_info; @@ -200,22 +210,46 @@ class vpi_startup_handler : public vpi_onetime_handler { return 0; } + int arm_callback(void) { + s_cb_data cb_data_s; + + cb_data_s.reason = cbStartOfSimulation; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = NULL; + cb_data_s.value = NULL; + cb_data_s.user_data = (char*)this; + + return register_cb(&cb_data_s); + } }; -class vpi_shutdown_handler : public vpi_onetime_handler { +class vpi_cb_shutdown : public vpi_onetime_cb { public: - int run_handler(vpi_cb_hdl *cb) { - //gpi_embed_end(); + int run_callback(void) { + gpi_embed_end(); return 0; } + + int arm_callback(void) { + s_cb_data cb_data_s; + + cb_data_s.reason = cbEndOfSimulation; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = NULL; + cb_data_s.value = NULL; + cb_data_s.user_data = (char*)this; + + return register_cb(&cb_data_s); + } }; -class vpi_timed_handler : public vpi_onetime_handler { +class vpi_cb_timed : public vpi_onetime_cb { public: - int run_handler(vpi_cb_hdl *cb) { + int run_callback(vpi_cb_hdl *cb) { LOG_ERROR("In timed handler") - gpi_handle_callback(cb); return 0; } }; @@ -224,420 +258,80 @@ class vpi_impl : public gpi_impl_interface { public: vpi_impl(const string& name) : gpi_impl_interface(name) { } + /* Sim related */ void sim_end(void); - void get_sim_time(uint32_t *high, uint32_t *low); - gpi_sim_hdl get_root_handle(const char *name); - gpi_sim_hdl get_handle_by_name(const char *name, gpi_sim_hdl parent); - gpi_sim_hdl get_handle_by_index(gpi_sim_hdl parent, uint32_t index); - void free_handle(gpi_sim_hdl); - gpi_iterator_hdl iterate_handle(uint32_t type, gpi_sim_hdl base); - gpi_sim_hdl next_handle(gpi_iterator_hdl iterator); - char* get_signal_value_binstr(gpi_sim_hdl gpi_hdl); - char* get_signal_name_str(gpi_sim_hdl gpi_hdl); - char* get_signal_type_str(gpi_sim_hdl gpi_hdl); - void set_signal_value_int(gpi_sim_hdl gpi_hdl, int value); - void set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str); // String of binary char(s) [1, 0, x, z] - int register_timed_callback(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps); - int register_value_change_callback(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl); - int register_readonly_callback(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); - int register_nexttime_callback(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); - int register_readwrite_callback(gpi_sim_hdl, int (*gpi_function)(void *), void *gpi_cb_data); - gpi_cb_hdl *create_cb_handle(void); - void destroy_cb_handle(gpi_cb_hdl *gpi_hdl); - int deregister_callback(gpi_sim_hdl gpi_hdl); + void get_sim_time(uint32_t *high, uint32_t *low) { } + + /* Signal related */ + gpi_obj_hdl *get_root_handle(const char *name) { return NULL;} + gpi_obj_hdl *get_handle_by_name(const char *name, gpi_obj_hdl *parent) { return NULL; } + gpi_obj_hdl *get_handle_by_index(gpi_obj_hdl *parent, uint32_t index) { return NULL; } + void free_handle(gpi_obj_hdl*) { } + gpi_iterator *iterate_handle(uint32_t type, gpi_obj_hdl *base) { return NULL; } + gpi_obj_hdl *next_handle(gpi_iterator *iterator) { return NULL; } + char* get_signal_value_binstr(gpi_obj_hdl *gpi_hdl) { return NULL; } + char* get_signal_name_str(gpi_obj_hdl *gpi_hdl) { return NULL; } + char* get_signal_type_str(gpi_obj_hdl *gpi_hdl) { return NULL; } + void set_signal_value_int(gpi_obj_hdl *gpi_hdl, int value) { } + void set_signal_value_str(gpi_obj_hdl *gpi_hdl, const char *str) { } // String of binary char(s) [1, 0, x, z] + + /* Callback related */ + gpi_cb_hdl *register_timed_callback(uint64_t time_ps) { return NULL; } + gpi_cb_hdl *register_value_change_callback(gpi_obj_hdl *obj_hdl) { return NULL; } + gpi_cb_hdl *register_readonly_callback(void) { return NULL; } + gpi_cb_hdl *register_nexttime_callback(void) { return NULL; } + gpi_cb_hdl *register_readwrite_callback(void) { return NULL; } + int deregister_callback(gpi_cb_hdl *gpi_hdl) { return 0; } + + gpi_cb_hdl *create_cb_handle(void) { return NULL; } + void destroy_cb_handle(gpi_cb_hdl *gpi_hdl) { } }; -static vpi_cb_hdl *sim_init_cb; -static vpi_cb_hdl *sim_finish_cb; - -// Add to this over time -const char * vpi_cb_hdl::vpi_reason_to_string(int reason) -{ - switch (reason) { - case cbValueChange: - return "cbValueChange"; - case cbAtStartOfSimTime: - return "cbAtStartOfSimTime"; - case cbReadWriteSynch: - return "cbReadWriteSynch"; - case cbReadOnlySynch: - return "cbReadOnlySynch"; - case cbNextSimTime: - return "cbNextSimTime"; - case cbAfterDelay: - return "cbAfterDelay"; - case cbStartOfSimulation: - return "cbStartOfSimulation"; - case cbEndOfSimulation: - return "cbEndOfSimulation"; - default: - return "unknown"; - } -} - -int vpi_cb_hdl::arm_callback(p_cb_data cb_data, vpi_callback_handler *hdlr) -{ - /* If the user data already has a callback handle then deregister - * before getting the new one - */ - if (state == VPI_PRIMED) { - fprintf(stderr, - "Attempt to prime an already primed trigger for %s!\n", - vpi_reason_to_string(cb_data->reason)); - } - - if (vpi_hdl != NULL) { - fprintf(stderr, - "We seem to already be registered, deregistering %s!\n", - vpi_reason_to_string(cb_data->reason)); -#ifdef FIX_THIS - vpi_deregister_callback(&cb_hdl); -#endif - } - - vpiHandle new_hdl = vpi_register_cb(cb_data); - int ret = 0; - - if (!new_hdl) { - LOG_CRITICAL("VPI: Unable to register callback a handle for VPI type %s(%d)", - vpi_reason_to_string(cb_data->reason), cb_data->reason); - check_vpi_error(); - ret = -1; - } - - vpi_hdl = new_hdl; - handler = hdlr; - state = VPI_PRIMED; - - return ret; -} - -int vpi_cb_hdl::run_callback(void) -{ - vpi_hdl = NULL; - handler->run_handler(this); - delete(handler); - handler = NULL; - /* Can now be re-used */ - return 0; -} - -int vpi_cb_hdl::cancel_callback(void) -{ - return handler->cleanup_handler(this); -} - - -// Handle related functions -/** - * @name Find the root handle - * @brief Find the root handle using a optional name - * - * Get a handle to the root simulator object. This is usually the toplevel. - * - * If no name is defined, we return the first root instance. - * - * If name is provided, we check the name against the available objects until - * we find a match. If no match is found we return NULL - */ -gpi_sim_hdl vpi_impl::get_root_handle(const char* name) -{ - FENTER - vpiHandle root; - vpiHandle iterator; - gpi_sim_hdl rv; - - // vpi_iterate with a ref of NULL returns the top level module - iterator = vpi_iterate(vpiModule, NULL); - check_vpi_error(); - - for (root = vpi_scan(iterator); root != NULL; root = vpi_scan(iterator)) { - - if (name == NULL || !strcmp(name, vpi_get_str(vpiFullName, root))) - break; - } - - if (!root) { - check_vpi_error(); - goto error; - } - - // Need to free the iterator if it didn't return NULL - if (!vpi_free_object(iterator)) { - LOG_WARN("VPI: Attempting to free root iterator failed!"); - check_vpi_error(); - } - - rv = gpi_create_handle(); - rv->sim_hdl = root; - - FEXIT - return rv; - - error: - - LOG_CRITICAL("VPI: Couldn't find root handle %s", name); - - iterator = vpi_iterate(vpiModule, NULL); - - for (root = vpi_scan(iterator); root != NULL; root = vpi_scan(iterator)) { - - LOG_CRITICAL("VPI: Toplevel instances: %s != %s...", name, vpi_get_str(vpiFullName, root)); - - if (name == NULL || !strcmp(name, vpi_get_str(vpiFullName, root))) - break; - } - - FEXIT - return NULL; -} - - -/** - * @brief Get a handle to an object under the scope of parent - * - * @param name of the object to find - * @param parent handle to parent object defining the scope to search - * - * @return gpi_sim_hdl for the new object or NULL if object not found - */ -gpi_sim_hdl vpi_impl::get_handle_by_name(const char *name, gpi_sim_hdl parent) -{ - FENTER - gpi_sim_hdl rv; - vpiHandle obj; - vpiHandle iterator; - int len; - char *buff; - - // Structures aren't technically a scope, according to the LRM. If parent - // is a structure then we have to iterate over the members comparing names - if (vpiStructVar == vpi_get(vpiType, (vpiHandle)(parent->sim_hdl))) { - - iterator = vpi_iterate(vpiMember, (vpiHandle)(parent->sim_hdl)); - - for (obj = vpi_scan(iterator); obj != NULL; obj = vpi_scan(iterator)) { - - if (!strcmp(name, strrchr(vpi_get_str(vpiName, obj), 46) + 1)) - break; - } - - if (!obj) - return NULL; - - // Need to free the iterator if it didn't return NULL - if (!vpi_free_object(iterator)) { - LOG_WARN("VPI: Attempting to free root iterator failed!"); - check_vpi_error(); - } - - goto success; - } - - if (name) - len = strlen(name) + 1; - - buff = (char *)malloc(len); - if (buff == NULL) { - LOG_CRITICAL("VPI: Attempting allocate string buffer failed!"); - return NULL; - } - - strncpy(buff, name, len); - obj = vpi_handle_by_name(buff, (vpiHandle)(parent->sim_hdl)); - if (!obj) { - LOG_DEBUG("VPI: Handle '%s' not found!", name); - - // NB we deliberately don't dump an error message here because it's - // a valid use case to attempt to grab a signal by name - for example - // optional signals on a bus. - // check_vpi_error(); - free(buff); - return NULL; - } - - free(buff); - -success: - rv = gpi_create_handle(); - rv->sim_hdl = obj; - - FEXIT - return rv; -} - -/** - * @brief Get a handle for an object based on its index within a parent - * - * @param parent handle to the parent - * @param indext Index to retrieve - * - * Can be used on bit-vectors to access a specific bit or - * memories to access an address - */ -gpi_sim_hdl vpi_impl::get_handle_by_index(gpi_sim_hdl parent, uint32_t index) -{ - FENTER - gpi_sim_hdl rv; - vpiHandle obj; - - obj = vpi_handle_by_index((vpiHandle)(parent->sim_hdl), index); - if (!obj) { - LOG_ERROR("VPI: Handle idx '%d' not found!", index); - return NULL; - } - - rv = gpi_create_handle(); - rv->sim_hdl = obj; - - FEXIT - return rv; -} - - -// Functions for iterating over entries of a handle -// Returns an iterator handle which can then be used in gpi_next calls -// NB May return NULL if no objects of the request type exist -gpi_iterator_hdl vpi_impl::iterate_handle(uint32_t type, gpi_sim_hdl base) -{ - FENTER - - vpiHandle iterator; - - iterator = vpi_iterate(type, (vpiHandle)(base->sim_hdl)); - check_vpi_error(); - - FEXIT - return (gpi_iterator_hdl)iterator; -} - -void vpi_impl::free_handle(gpi_sim_hdl) -{ - -} - -// Returns NULL when there are no more objects -gpi_sim_hdl vpi_impl::next_handle(gpi_iterator_hdl iterator) -{ - FENTER - gpi_sim_hdl rv = gpi_create_handle(); - - rv->sim_hdl = vpi_scan((vpiHandle) iterator); - check_vpi_error(); - if (!rv->sim_hdl) { - gpi_free_handle(rv); - rv = NULL; - } - - // Don't need to call vpi_free_object on the iterator handle - // From VPI spec: - // After returning NULL, memory associated with the iteratod handle is - // freed, making the handle invalid. - - FEXIT - return rv; -} - -// double gpi_get_sim_time() -void vpi_impl::get_sim_time(uint32_t *high, uint32_t *low) -{ - s_vpi_time vpi_time_s; - vpi_time_s.type = vpiSimTime;//vpiScaledRealTime; //vpiSimTime; - vpi_get_time(NULL, &vpi_time_s); - check_vpi_error(); - *high = vpi_time_s.high; - *low = vpi_time_s.low; -} - -// Value related functions -void vpi_impl::set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) +#if 0 +gpi_cb_hdl *vpi_impl::register_timed_callback(uint64_t time_ps) { FENTER - s_vpi_value value_s; - p_vpi_value value_p = &value_s; - - value_p->value.integer = value; - value_p->format = vpiIntVal; + int ret; + s_cb_data cb_data_s; s_vpi_time vpi_time_s; - p_vpi_time vpi_time_p = &vpi_time_s; - vpi_time_p->type = vpiSimTime; - vpi_time_p->high = 0; - vpi_time_p->low = 0; - - // Use Inertial delay to schedule an event, thus behaving like a verilog testbench - vpi_put_value((vpiHandle)(gpi_hdl->sim_hdl), value_p, vpi_time_p, vpiInertialDelay); - check_vpi_error(); - - FEXIT -} - -void vpi_impl::set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) -{ - FENTER - s_vpi_value value_s; - p_vpi_value value_p = &value_s; - - int len; - char *buff; - if (str) - len = strlen(str) + 1; - - buff = (char *)malloc(len); - if (buff== NULL) { - LOG_CRITICAL("VPI: Attempting allocate string buffer failed!"); - return; - } + vpi_time_s.type = vpiSimTime; + vpi_time_s.high = (uint32_t)(time_ps>>32); + vpi_time_s.low = (uint32_t)(time_ps); - strncpy(buff, str, len); + cb_data_s.reason = cbAfterDelay; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &vpi_time_s; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)hdl; - value_p->value.str = buff; - value_p->format = vpiBinStrVal; + ret = hdl->arm_callback(&cb_data_s); - /* vpiNoDelay -- Set the value immediately. The p_vpi_time parameter - * may be NULL, in this case. This is like a blocking assignment - * in behavioral code. - */ - vpi_put_value((vpiHandle)(gpi_hdl->sim_hdl), value_p, NULL, vpiNoDelay); - check_vpi_error(); - free(buff); FEXIT + return ret; } +#endif -char *vpi_impl::get_signal_value_binstr(gpi_sim_hdl gpi_hdl) -{ - FENTER - s_vpi_value value_s = {vpiBinStrVal}; - p_vpi_value value_p = &value_s; +extern "C" { - vpi_get_value((vpiHandle)(gpi_hdl->sim_hdl), value_p); - check_vpi_error(); +static vpi_cb_hdl *sim_init_cb; +static vpi_cb_hdl *sim_finish_cb; +static vpi_impl *vpi_table; - char *result = gpi_copy_name(value_p->value.str); - FEXIT - return result; } -char *vpi_impl::get_signal_name_str(gpi_sim_hdl gpi_hdl) +// If the Pything world wants things to shut down then unregister +// the callback for end of sim +void vpi_impl::sim_end(void) { - FENTER - const char *name = vpi_get_str(vpiFullName, (vpiHandle)(gpi_hdl->sim_hdl)); + sim_finish_cb = NULL; + vpi_control(vpiFinish); check_vpi_error(); - char *result = gpi_copy_name(name); - FEXIT - return result; } -char *vpi_impl::get_signal_type_str(gpi_sim_hdl gpi_hdl) -{ - FENTER - const char *name = vpi_get_str(vpiType, (vpiHandle)(gpi_hdl->sim_hdl)); - check_vpi_error(); - char *result = gpi_copy_name(name); - FEXIT - return result; -} +extern "C" { // Main re-entry point for callbacks from simulator int32_t handle_vpi_callback(p_cb_data cb_data) @@ -651,7 +345,7 @@ int32_t handle_vpi_callback(p_cb_data cb_data) if (!cb_hdl) LOG_CRITICAL("VPI: Callback data corrupted"); - cb_hdl->set_state(VPI_PRE_CALL); + //cb_hdl->set_state(VPI_PRE_CALL); //old_cb = user_data->cb_hdl; cb_hdl->run_callback(); @@ -670,315 +364,43 @@ int32_t handle_vpi_callback(p_cb_data cb_data) else user_data->state = VPI_POST_CALL; #endif - cb_hdl->set_state(VPI_POST_CALL); + //cb_hdl->set_state(VPI_POST_CALL); FEXIT return rv; }; -/* Deregister a prior set up callback with the simulator - * The handle must have been allocated with gpi_create_cb_handle - * This can be called at any point between - * gpi_create_cb_handle and gpi_free_cb_handle - */ -int vpi_impl::deregister_callback(gpi_sim_hdl gpi_hdl) -{ - FENTER - int rc = 1; - // We should be able to user vpi_get_cb_info - // but this is not implemented in ICARUS - // and gets upset on VCS. So instead we - // do some pointer magic. - - vpi_cb_hdl *hdl = reinterpret_cast(gpi_hdl->sim_hdl); - (void)hdl; - hdl->cancel_callback(); - - FEXIT - GPI_RET(rc); -} - - - -/* These functions request a callback to be active with the current - * handle and associated data. A callback handle needs to have been - * allocated with gpi_create_cb_handle first - */ - - -int vpi_impl::register_value_change_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data, - gpi_sim_hdl gpi_hdl) -{ -#if 0 - FENTER - - int ret; - s_cb_data cb_data_s; - s_vpi_time vpi_time_s; - p_vpi_cb vpi_user_data; - gpi_cb_hdl gpi_user_data; - - gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); - vpi_user_data = gpi_container_of(gpi_user_data, s_vpi_cb, gpi_cb_data); - - vpi_user_data->vpi_cleanup = vpi_free_recurring; - vpi_user_data->cb_value.format = vpiIntVal; - - vpi_time_s.type = vpiSuppressTime; - - cb_data_s.reason = cbValueChange; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = (vpiHandle)(gpi_hdl->sim_hdl); - cb_data_s.time = &vpi_time_s; - cb_data_s.value = &vpi_user_data->cb_value; - cb_data_s.user_data = (char *)vpi_user_data; - - ret = vpi_user_data.__vpi_register_cb(&cb_data_s); - - FEXIT - - return ret; -#else - return 0; -#endif -} - -int vpi_impl::register_readonly_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) -{ -#if 0 - FENTER - - int ret; - s_cb_data cb_data_s; - s_vpi_time vpi_time_s; - p_vpi_cb vpi_user_data; - gpi_cb_hdl gpi_user_data; - - gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); - vpi_user_data = gpi_container_of(gpi_user_data, s_vpi_cb, gpi_cb_data); - - vpi_user_data->vpi_cleanup = vpi_free_one_time; - - vpi_time_s.type = vpiSimTime; - vpi_time_s.high = 0; - vpi_time_s.low = 0; - - cb_data_s.reason = cbReadOnlySynch; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = &vpi_time_s; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)vpi_user_data; - - ret = __vpi_register_cb(vpi_user_data, &cb_data_s); - - FEXIT - return ret; -#else - return 0; -#endif -} - -int vpi_impl::register_readwrite_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) -{ -#if 0 - FENTER - - int ret; - s_cb_data cb_data_s; - s_vpi_time vpi_time_s; - p_vpi_cb vpi_user_data; - gpi_cb_hdl gpi_user_data; - - gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); - vpi_user_data = gpi_container_of(gpi_user_data, s_vpi_cb, gpi_cb_data); - - vpi_user_data->vpi_cleanup = vpi_free_one_time; - - vpi_time_s.type = vpiSimTime; - vpi_time_s.high = 0; - vpi_time_s.low = 0; - - cb_data_s.reason = cbReadWriteSynch; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = &vpi_time_s; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)vpi_user_data; - - ret = __vpi_register_cb(vpi_user_data, &cb_data_s); - - FEXIT - return ret; -#else - return 0; -#endif -} - - -int vpi_impl::register_nexttime_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) -{ -#if 0 - FENTER - - int ret; - s_cb_data cb_data_s; - s_vpi_time vpi_time_s; - p_vpi_cb vpi_user_data; - gpi_cb_hdl gpi_user_data; - - gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); - vpi_user_data = gpi_container_of(gpi_user_data, s_vpi_cb, gpi_cb_data); - - vpi_user_data->vpi_cleanup = vpi_free_one_time; - - vpi_time_s.type = vpiSimTime; - vpi_time_s.high = 0; - vpi_time_s.low = 0; - - cb_data_s.reason = cbNextSimTime; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = &vpi_time_s; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)vpi_user_data; - - ret = __vpi_register_cb(vpi_user_data, &cb_data_s); - - FEXIT - return ret; -#else - return 0; -#endif -} - -int vpi_impl::register_timed_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data, - uint64_t time_ps) -{ - FENTER - - int ret; - s_cb_data cb_data_s; - s_vpi_time vpi_time_s; - vpi_cb_hdl *hdl = reinterpret_cast(cb->sim_hdl); - - vpi_time_s.type = vpiSimTime; - vpi_time_s.high = (uint32_t)(time_ps>>32); - vpi_time_s.low = (uint32_t)(time_ps); - - cb_data_s.reason = cbAfterDelay; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = &vpi_time_s; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)hdl; - - vpi_timed_handler *handler = new vpi_timed_handler(); - ret = hdl->arm_callback(&cb_data_s, handler); - - FEXIT - return ret; -} - - -/* Checking of validity is done in the common code */ -gpi_cb_hdl *vpi_impl::create_cb_handle(void) -{ - FENTER - - vpi_cb_hdl *new_hdl = new vpi_cb_hdl(); - - FEXIT - return new_hdl; -} - -void vpi_impl::destroy_cb_handle(gpi_cb_hdl *hdl) -{ - FENTER - delete(hdl); - FEXIT -} - -//void *vpi_impl::get_callback_data(gpi_sim_hdl gpi_hdl) -//{ -// FENTER -// gpi_cb_hdl gpi_user_data; -// gpi_user_data = gpi_container_of(gpi_hdl, gpi_cb_hdl_t, hdl); -// return gpi_user_data->gpi_cb_data; -//} - - -// If the Pything world wants things to shut down then unregister -// the callback for end of sim -void vpi_impl::sim_end(void) -{ - sim_finish_cb = NULL; - vpi_control(vpiFinish); - check_vpi_error(); -} - -extern "C" { - static void register_embed(void) { - vpi_impl *vpi_table = new vpi_impl("VPI"); - gpi_register_impl(vpi_table, 0xfeed); + vpi_table = new vpi_impl("VPI"); + gpi_register_impl(vpi_table); gpi_embed_init_python(); } static void register_initial_callback(void) { - sim_init_cb = new vpi_cb_hdl(); - vpi_startup_handler *handler = new vpi_startup_handler(); + sim_init_cb = new vpi_cb_startup(); /* We ignore the return value here as VCS does some silly * things on comilation that means it tries to run through * the vlog_startup_routines and so call this routine */ - s_cb_data cb_data_s; - - cb_data_s.reason = cbStartOfSimulation; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = NULL; - cb_data_s.value = NULL; - cb_data_s.user_data = (char*)sim_init_cb; - sim_init_cb->arm_callback(&cb_data_s, handler); + sim_init_cb->arm_callback(); } static void register_final_callback(void) { - sim_finish_cb = new vpi_cb_hdl(); - vpi_shutdown_handler *handler = new vpi_shutdown_handler(); + sim_finish_cb = new vpi_cb_shutdown(); /* We ignore the return value here as VCS does some silly * things on comilation that means it tries to run through * the vlog_startup_routines and so call this routine */ - s_cb_data cb_data_s; - - cb_data_s.reason = cbEndOfSimulation; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = NULL; - cb_data_s.value = NULL; - cb_data_s.user_data = (char*)sim_finish_cb; - - sim_finish_cb->arm_callback(&cb_data_s, handler); + sim_finish_cb->arm_callback(); } From 0195a21726dac0a62f5e38612a6de069ed609670 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 28 Sep 2014 20:22:41 +0100 Subject: [PATCH 0591/2656] MixedLang: Updated the API and everything else --- cocotb/triggers.py | 37 +++++++++------ lib/gpi/gpi_priv.h | 9 ++-- lib/gpi_log/gpi_logging.c | 8 +++- lib/simulator/simulatormodule.c | 43 ------------------ lib/simulator/simulatormodule.h | 4 -- lib/vpi/gpi_vpi.cpp | 79 +++++++++++++++++++++++++++++---- 6 files changed, 105 insertions(+), 75 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 13ae8cf6..5e9c0a37 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -85,10 +85,10 @@ def __init__(self): Trigger.__init__(self) # Required to ensure documentation can build - if simulator is not None: - self.cbhdl = simulator.create_callback(self) - else: - self.cbhdl = None + #if simulator is not None: + # self.cbhdl = simulator.create_callback(self) + #else: + self.cbhdl = None def unprime(self): """Unregister a prior registered timed callback""" @@ -98,8 +98,8 @@ def unprime(self): def __del__(self): """Remove knowledge of the trigger""" - if self.cbhdl is not None: - simulator.remove_callback(self.cbhdl) + if self.cbhdl is None: + self.cbhdl = simulator.remove_callback() Trigger.__del__(self) @@ -116,7 +116,8 @@ def __init__(self, time_ps): def prime(self, callback): """Register for a timed callback""" Trigger.prime(self) - if simulator.register_timed_callback(self.cbhdl, self.time_ps, callback, self): + self.cbhdl = simulator.register_timed_callback(self.time_ps, callback, self) + if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) def __str__(self): @@ -133,7 +134,8 @@ def __init__(self, signal): def prime(self, callback): """Register notification of a value change via a callback""" Trigger.prime(self) - if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, callback, self): + self.cbhdl = simulator.register_value_change_callback(self.signal._handle, callback, self) + if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) def __str__(self): @@ -149,7 +151,8 @@ def __init__(self): def prime(self, callback): Trigger.prime(self) - if simulator.register_readonly_callback(self.cbhdl, callback, self): + self.chhdl = simulator.register_readonly_callback(callback, self) + if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) def __str__(self): @@ -169,7 +172,8 @@ def __init__(self): def prime(self, callback): Trigger.prime(self) - if simulator.register_rwsynch_callback(self.cbhdl, callback, self): + self.cbhdl = simulator.register_rwsynch_callback(callback, self) + if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) def __str__(self): @@ -188,7 +192,9 @@ def __init__(self): def prime(self, callback): Trigger.prime(self) - simulator.register_nextstep_callback(self.cbhdl, callback, self) + self.cbhdl = simulator.register_nextstep_callback(callback, self) + if self.cbhdl is None: + raise_error(self, "Unable set up %s Trigger" % (str(self))) def __str__(self): return self.__class__.__name__ + "(nexttimestep)" @@ -217,7 +223,8 @@ def _check(obj): else: simulator.reenable_callback(self.cbhdl) - if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self): + self.cbhdl = simulator.register_value_change_callback(self.signal._handle, _check, self) + if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) def __str__(self): @@ -247,10 +254,12 @@ def _check(obj): self._callback(self) return - if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self): + self.cbhdl = simulator.register_value_change_callback(self.signal._handle, _check, self) + if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) - if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self): + self.cbhdl = simulator.register_value_change_callback(self.signal._handle, _check, self) + if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) def __str__(self): diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 3e12fc6e..301e4c07 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -37,23 +37,23 @@ class gpi_impl_interface; class gpi_hdl { public: - gpi_hdl() {} + gpi_hdl(gpi_impl_interface *impl) : m_impl(impl) { } virtual ~gpi_hdl() { } - void set_gpi_impl(gpi_impl_interface *impl); - public: gpi_impl_interface *m_impl; // Implementation routines }; class gpi_obj_hdl : public gpi_hdl { public: + gpi_obj_hdl(gpi_impl_interface *impl) : gpi_hdl(impl) { } char *gpi_copy_name(const char *name); }; class gpi_cb_hdl : public gpi_hdl { public: /* Override to change behaviour as needed */ + gpi_cb_hdl(gpi_impl_interface *impl) : gpi_hdl(impl) { } int handle_callback(void); virtual int arm_callback(void); virtual int run_callback(void); @@ -62,6 +62,9 @@ class gpi_cb_hdl : public gpi_hdl { int set_user_data(int (*gpi_function)(void*), void *data); void *get_user_data(void); +private: + gpi_cb_hdl(); + protected: int (*gpi_function)(void *); // GPI function to callback void *m_cb_data; // GPI data supplied to "gpi_function" diff --git a/lib/gpi_log/gpi_logging.c b/lib/gpi_log/gpi_logging.c index 09da81b0..541ae75d 100644 --- a/lib/gpi_log/gpi_logging.c +++ b/lib/gpi_log/gpi_logging.c @@ -136,12 +136,12 @@ void gpi_log(const char *name, long level, const char *pathname, const char *fun PyObject *retuple = PyObject_CallObject(pLogFilter, check_args); if (retuple != Py_True) { - Py_DECREF(retuple); Py_DECREF(check_args); return; } Py_DECREF(retuple); + Py_DECREF(check_args); va_start(ap, msg); n = vsnprintf(log_buff, LOG_SIZE, msg, ap); @@ -155,9 +155,13 @@ void gpi_log(const char *name, long level, const char *pathname, const char *fun PyTuple_SetItem(call_args, 4, PyString_FromString(funcname)); retuple = PyObject_CallObject(pLogHandler, call_args); + + if (retuple != Py_True) { + return; + } + Py_DECREF(call_args); Py_DECREF(retuple); - Py_DECREF(check_args); PyGILState_Release(gstate); } diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 2489e0c3..6bd2ff80 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -812,49 +812,6 @@ static PyObject *deregister_callback(PyObject *self, PyObject *args) return value; } -static PyObject *remove_callback(PyObject *self, PyObject *args) -{ - gpi_sim_hdl hdl; - PyObject *pSihHdl; - PyObject *value; - - FENTER - - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - - pSihHdl = PyTuple_GetItem(args, 0); - hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(pSihHdl); - - gpi_free_cb_handle(hdl); - - value = Py_BuildValue("s", "OK!"); - - DROP_GIL(gstate); - - FEXIT - return value; -} - -static PyObject *create_callback(PyObject *self, PyObject *args) -{ - FENTER - - PyObject *value; - - gpi_sim_hdl cb_hdl = gpi_create_cb_handle(); - - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - - value = Py_BuildValue("l", cb_hdl); - - DROP_GIL(gstate); - - FEXIT - return value; -} - PyMODINIT_FUNC initsimulator(void) { diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 5b4c3f61..6d0ad670 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -74,8 +74,6 @@ static PyObject *next(PyObject *self, PyObject *args); static PyObject *get_sim_time(PyObject *self, PyObject *args); static PyObject *deregister_callback(PyObject *self, PyObject *args); -static PyObject *remove_callback(PyObject *self, PyObject *args); -static PyObject *create_callback(PyObject *self, PyObject *args); static PyObject *free_handle(PyObject *self, PyObject *args); static PyMethodDef SimulatorMethods[] = { @@ -101,8 +99,6 @@ static PyMethodDef SimulatorMethods[] = { // FIXME METH_NOARGS => initialization from incompatible pointer type {"get_sim_time", get_sim_time, METH_VARARGS, "Get the current simulation time as a float"}, {"deregister_callback", deregister_callback, METH_VARARGS, "Deregister a callback"}, - {"remove_callback", remove_callback, METH_VARARGS, "Remove a callback"}, - {"create_callback", create_callback, METH_VARARGS, "Creates a callback"}, {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/lib/vpi/gpi_vpi.cpp b/lib/vpi/gpi_vpi.cpp index dcf29524..8bf5651c 100644 --- a/lib/vpi/gpi_vpi.cpp +++ b/lib/vpi/gpi_vpi.cpp @@ -85,18 +85,23 @@ typedef enum vpi_cb_state_e { VPI_DELETE = 4, } vpi_cb_state_t; +class vpi_obj_hdl : public gpi_obj_hdl { +public: + vpi_obj_hdl(vpiHandle hdl, gpi_impl_interface *impl) : gpi_obj_hdl(impl), + vpi_hdl(hdl) { } +private: + vpiHandle vpi_hdl; + +}; + class vpi_cb_hdl : public gpi_cb_hdl { protected: vpiHandle vpi_hdl; vpi_cb_state_t state; public: - vpi_cb_hdl() : vpi_hdl(NULL) { - - } - virtual ~vpi_cb_hdl() { - - } + vpi_cb_hdl(gpi_impl_interface *impl) : gpi_cb_hdl(impl), + vpi_hdl(NULL) { } protected: /* If the user data already has a callback handle then deregister @@ -161,6 +166,7 @@ class vpi_cb_hdl : public gpi_cb_hdl { class vpi_onetime_cb : public vpi_cb_hdl { public: + vpi_onetime_cb(gpi_impl_interface *impl) : vpi_cb_hdl(impl) { } int cleanup_callback(void) { FENTER int rc = 0; @@ -175,6 +181,7 @@ class vpi_onetime_cb : public vpi_cb_hdl { class vpi_recurring_cb : public vpi_cb_hdl { public: + vpi_recurring_cb(gpi_impl_interface *impl) : vpi_cb_hdl(impl) { } int cleanup_handler(void) { FENTER int rc; @@ -195,6 +202,7 @@ class vpi_cb_value_change : public vpi_recurring_cb { class vpi_cb_startup : public vpi_onetime_cb { public: + vpi_cb_startup(gpi_impl_interface *impl) : vpi_onetime_cb(impl) { } int run_callback(void) { s_vpi_vlog_info info; gpi_sim_info_t sim_info; @@ -227,6 +235,7 @@ class vpi_cb_startup : public vpi_onetime_cb { class vpi_cb_shutdown : public vpi_onetime_cb { public: + vpi_cb_shutdown(gpi_impl_interface *impl) : vpi_onetime_cb(impl) { } int run_callback(void) { gpi_embed_end(); return 0; @@ -248,6 +257,7 @@ class vpi_cb_shutdown : public vpi_onetime_cb { class vpi_cb_timed : public vpi_onetime_cb { public: + vpi_cb_timed(gpi_impl_interface *impl) : vpi_onetime_cb(impl) { } int run_callback(vpi_cb_hdl *cb) { LOG_ERROR("In timed handler") return 0; @@ -263,7 +273,7 @@ class vpi_impl : public gpi_impl_interface { void get_sim_time(uint32_t *high, uint32_t *low) { } /* Signal related */ - gpi_obj_hdl *get_root_handle(const char *name) { return NULL;} + gpi_obj_hdl *get_root_handle(const char *name); gpi_obj_hdl *get_handle_by_name(const char *name, gpi_obj_hdl *parent) { return NULL; } gpi_obj_hdl *get_handle_by_index(gpi_obj_hdl *parent, uint32_t index) { return NULL; } void free_handle(gpi_obj_hdl*) { } @@ -287,6 +297,57 @@ class vpi_impl : public gpi_impl_interface { void destroy_cb_handle(gpi_cb_hdl *gpi_hdl) { } }; +gpi_obj_hdl *vpi_impl::get_root_handle(const char* name) +{ + FENTER + vpiHandle root; + vpiHandle iterator; + vpi_obj_hdl *rv; + + // vpi_iterate with a ref of NULL returns the top level module + iterator = vpi_iterate(vpiModule, NULL); + check_vpi_error(); + + for (root = vpi_scan(iterator); root != NULL; root = vpi_scan(iterator)) { + + if (name == NULL || !strcmp(name, vpi_get_str(vpiFullName, root))) + break; + } + + if (!root) { + check_vpi_error(); + goto error; + } + + // Need to free the iterator if it didn't return NULL + if (!vpi_free_object(iterator)) { + LOG_WARN("VPI: Attempting to free root iterator failed!"); + check_vpi_error(); + } + + rv = new vpi_obj_hdl(root, this); + + FEXIT + return rv; + + error: + + LOG_CRITICAL("VPI: Couldn't find root handle %s", name); + + iterator = vpi_iterate(vpiModule, NULL); + + for (root = vpi_scan(iterator); root != NULL; root = vpi_scan(iterator)) { + + LOG_CRITICAL("VPI: Toplevel instances: %s != %s...", name, vpi_get_str(vpiFullName, root)); + + if (name == NULL || !strcmp(name, vpi_get_str(vpiFullName, root))) + break; + } + + FEXIT + return NULL; +} + #if 0 gpi_cb_hdl *vpi_impl::register_timed_callback(uint64_t time_ps) { @@ -381,7 +442,7 @@ static void register_embed(void) static void register_initial_callback(void) { - sim_init_cb = new vpi_cb_startup(); + sim_init_cb = new vpi_cb_startup(vpi_table); /* We ignore the return value here as VCS does some silly * things on comilation that means it tries to run through @@ -393,7 +454,7 @@ static void register_initial_callback(void) static void register_final_callback(void) { - sim_finish_cb = new vpi_cb_shutdown(); + sim_finish_cb = new vpi_cb_shutdown(vpi_table); /* We ignore the return value here as VCS does some silly * things on comilation that means it tries to run through From 7ea09811b2d6a2c0f45efa9ef5119b6188cb4a8c Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 29 Sep 2014 07:50:00 +0100 Subject: [PATCH 0592/2656] Mixedlang: Add gpi_get_handle_by_index --- lib/embed/gpi_embed.c | 3 +- lib/gpi/gpi_common.cpp | 82 +++----- lib/vpi/gpi_vpi.cpp | 437 ++++++++++++++++++++++++++++++++++++----- 3 files changed, 423 insertions(+), 99 deletions(-) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 259323b0..79eb25a4 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -269,11 +269,12 @@ void embed_sim_event(gpi_event_t level, const char *msg) PyObject *fArgs = PyTuple_New(2); PyTuple_SetItem(fArgs, 0, PyInt_FromLong(level)); + if (msg != NULL) PyTuple_SetItem(fArgs, 1, PyString_FromString(msg)); else PyTuple_SetItem(fArgs, 1, PyString_FromString("No message provided")); - PyObject *pValue = PyObject_Call(pEventFn, fArgs, NULL); + PyObject *pValue = PyObject_CallObject(pEventFn, fArgs); if (!pValue) { LOG_ERROR("Passing event to upper layer failed"); } diff --git a/lib/gpi/gpi_common.cpp b/lib/gpi/gpi_common.cpp index 88f6e521..f0efb7ec 100644 --- a/lib/gpi/gpi_common.cpp +++ b/lib/gpi/gpi_common.cpp @@ -65,11 +65,11 @@ static inline gpi_iterator * sim_to_iterhdl(gpi_sim_hdl hdl, bool fatal) return iter_hdl; } -static vector registed_impls; +static vector registered_impls; int gpi_register_impl(gpi_impl_interface *func_tbl) { - registed_impls.push_back(func_tbl); + registered_impls.push_back(func_tbl); return 0; } @@ -85,7 +85,7 @@ void gpi_embed_end(void) void gpi_sim_end(void) { - registed_impls[0]->sim_end(); + registered_impls[0]->sim_end(); } void gpi_embed_init_python(void) @@ -95,7 +95,7 @@ void gpi_embed_init_python(void) void gpi_get_sim_time(uint32_t *high, uint32_t *low) { - registed_impls[0]->get_sim_time(high, low); + registered_impls[0]->get_sim_time(high, low); } gpi_sim_hdl gpi_get_root_handle(const char *name) @@ -106,8 +106,8 @@ gpi_sim_hdl gpi_get_root_handle(const char *name) gpi_obj_hdl *hdl; - for (iter = registed_impls.begin(); - iter != registed_impls.end(); + for (iter = registered_impls.begin(); + iter != registered_impls.end(); iter++) { if ((hdl = (*iter)->get_root_handle(name))) { return (void*)hdl; @@ -124,8 +124,8 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) gpi_obj_hdl *base = sim_to_objhdl(parent, true); /* Either want this or use the parent */ - for (iter = registed_impls.begin(); - iter != registed_impls.end(); + for (iter = registered_impls.begin(); + iter != registered_impls.end(); iter++) { if ((hdl = (*iter)->get_handle_by_name(name, base))) { return (void*)hdl; @@ -140,6 +140,7 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) { + /* Either want this or use the parent */ gpi_obj_hdl *obj_hdl = sim_to_objhdl(parent, false); return (void*)obj_hdl->m_impl->get_handle_by_index(obj_hdl, index); } @@ -148,7 +149,7 @@ gpi_iterator_hdl gpi_iterate(uint32_t type, gpi_sim_hdl base) { gpi_obj_hdl *obj_hdl = sim_to_objhdl(base, false); gpi_iterator *iter = obj_hdl->m_impl->iterate_handle(type, obj_hdl); - if (iter) { + if (!iter) { return NULL; } iter->parent = obj_hdl; @@ -203,8 +204,10 @@ gpi_sim_hdl gpi_register_value_change_callback(int (*gpi_function)(void *), { gpi_obj_hdl *obj_hdl = sim_to_objhdl(sig_hdl, false); gpi_cb_hdl *gpi_hdl = obj_hdl->m_impl->register_value_change_callback(obj_hdl); - if (!gpi_hdl) + if (!gpi_hdl) { LOG_ERROR("Failed to register a value change callback"); + return NULL; + } gpi_hdl->set_user_data(gpi_function, gpi_cb_data); return (gpi_sim_hdl)gpi_hdl; @@ -213,13 +216,14 @@ gpi_sim_hdl gpi_register_value_change_callback(int (*gpi_function)(void *), /* It should not matter which implementation we use for this so just pick the first one */ -gpi_sim_hdl gpi_register_timed_callback( - int (*gpi_function)(void *), - void *gpi_cb_data, uint64_t time_ps) +gpi_sim_hdl gpi_register_timed_callback(int (*gpi_function)(void *), + void *gpi_cb_data, uint64_t time_ps) { - gpi_cb_hdl *gpi_hdl = registed_impls[0]->register_timed_callback(time_ps); - if (!gpi_hdl) + gpi_cb_hdl *gpi_hdl = registered_impls[0]->register_timed_callback(time_ps); + if (!gpi_hdl) { LOG_ERROR("Failed to register a timed callback"); + return NULL; + } gpi_hdl->set_user_data(gpi_function, gpi_cb_data); return (gpi_sim_hdl)gpi_hdl; @@ -231,9 +235,11 @@ gpi_sim_hdl gpi_register_timed_callback( gpi_sim_hdl gpi_register_readonly_callback(int (*gpi_function)(void *), void *gpi_cb_data) { - gpi_cb_hdl *gpi_hdl = registed_impls[0]->register_readonly_callback(); - if (!gpi_hdl) + gpi_cb_hdl *gpi_hdl = registered_impls[0]->register_readonly_callback(); + if (!gpi_hdl) { LOG_ERROR("Failed to register a readonly callback"); + return NULL; + } gpi_hdl->set_user_data(gpi_function, gpi_cb_data); return (gpi_sim_hdl)gpi_hdl; @@ -242,9 +248,11 @@ gpi_sim_hdl gpi_register_readonly_callback(int (*gpi_function)(void *), gpi_sim_hdl gpi_register_nexttime_callback(int (*gpi_function)(void *), void *gpi_cb_data) { - gpi_cb_hdl *gpi_hdl = registed_impls[0]->register_nexttime_callback(); - if (!gpi_hdl) + gpi_cb_hdl *gpi_hdl = registered_impls[0]->register_nexttime_callback(); + if (!gpi_hdl) { LOG_ERROR("Failed to register a nexttime callback"); + return NULL; + } gpi_hdl->set_user_data(gpi_function, gpi_cb_data); return (gpi_sim_hdl)gpi_hdl; @@ -256,7 +264,7 @@ gpi_sim_hdl gpi_register_nexttime_callback(int (*gpi_function)(void *), gpi_sim_hdl gpi_register_readwrite_callback(int (*gpi_function)(void *), void *gpi_cb_data) { - gpi_cb_hdl *gpi_hdl = registed_impls[0] ->register_nexttime_callback(); + gpi_cb_hdl *gpi_hdl = registered_impls[0] ->register_readwrite_callback(); if (!gpi_hdl) LOG_ERROR("Failed to register a readwrite callback"); @@ -271,40 +279,6 @@ void gpi_deregister_callback(gpi_sim_hdl hdl) cb_hdl->m_impl->deregister_callback(cb_hdl); } -/* Callback handles are abstracted to the implementation layer since - they may need to have some state stored on a per handle basis. -*/ -#if 0 -gpi_sim_hdl gpi_create_cb_handle(void) -{ - gpi_cb_hdl *cb_hdl = new gpi_cb_hdl(); - if (!cb_hdl) { - LOG_CRITICAL("GPI: Attempting allocate callback handle failed!"); - exit(1); - } - return (void*)cb_hdl; -} - -void gpi_free_cb_handle(gpi_sim_hdl hdl) -{ - gpi_cb_hdl *cb_hdl = sim_to_cbhdl(hdl, true); - delete(cb_hdl); -} -#endif - - -/* This needs to be filled with the pointer to the table */ -gpi_sim_hdl gpi_create_handle(void) -{ - gpi_obj_hdl *new_hdl = new gpi_obj_hdl(); - if (!new_hdl) { - LOG_CRITICAL("GPI: Could not allocate handle"); - exit(1); - } - - return (void*)new_hdl; -} - void gpi_free_handle(gpi_sim_hdl hdl) { gpi_obj_hdl *obj = sim_to_objhdl(hdl, true); diff --git a/lib/vpi/gpi_vpi.cpp b/lib/vpi/gpi_vpi.cpp index 8bf5651c..7aae5d01 100644 --- a/lib/vpi/gpi_vpi.cpp +++ b/lib/vpi/gpi_vpi.cpp @@ -89,13 +89,13 @@ class vpi_obj_hdl : public gpi_obj_hdl { public: vpi_obj_hdl(vpiHandle hdl, gpi_impl_interface *impl) : gpi_obj_hdl(impl), vpi_hdl(hdl) { } -private: +public: vpiHandle vpi_hdl; }; class vpi_cb_hdl : public gpi_cb_hdl { -protected: +public: vpiHandle vpi_hdl; vpi_cb_state_t state; @@ -169,12 +169,33 @@ class vpi_onetime_cb : public vpi_cb_hdl { vpi_onetime_cb(gpi_impl_interface *impl) : vpi_cb_hdl(impl) { } int cleanup_callback(void) { FENTER - int rc = 0; + int rc; if (!vpi_hdl) { LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); exit(1); } - FEXIT + + // If the callback has not been called we also need to call + // remove as well + if (state == VPI_PRIMED) { + + rc = vpi_remove_cb(vpi_hdl); + if (!rc) { + check_vpi_error(); + return rc; + } + vpi_hdl = NULL; + + // HACK: Calling vpi_free_object after vpi_remove_cb causes Modelsim to VPIEndOfSimulationCallback + #if 0 + rc = vpi_free_object(cb_hdl); + if (!rc) { + check_vpi_error(); + return rc; + } + #endif + } + state = VPI_FREE; return rc; } }; @@ -182,14 +203,16 @@ class vpi_onetime_cb : public vpi_cb_hdl { class vpi_recurring_cb : public vpi_cb_hdl { public: vpi_recurring_cb(gpi_impl_interface *impl) : vpi_cb_hdl(impl) { } - int cleanup_handler(void) { + int cleanup_callback(void) { FENTER int rc; - if (!vpi_hdl) { LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); exit(1); } + + rc = vpi_remove_cb(vpi_hdl); + check_vpi_error(); FEXIT return rc; } @@ -197,7 +220,21 @@ class vpi_recurring_cb : public vpi_cb_hdl { class vpi_cb_value_change : public vpi_recurring_cb { public: + vpi_cb_value_change(gpi_impl_interface *impl) : vpi_recurring_cb(impl) { } + int arm_callback(vpi_obj_hdl *vpi_hdl) { + s_cb_data cb_data_s; + s_vpi_time vpi_time_s = {.type = vpiSuppressTime }; + s_vpi_value cb_value = {.format = vpiIntVal }; + + cb_data_s.reason = cbValueChange; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = vpi_hdl->vpi_hdl; + cb_data_s.time = &vpi_time_s; + cb_data_s.value = &cb_value; + cb_data_s.user_data = (char *)this; + return register_cb(&cb_data_s); + } }; class vpi_cb_startup : public vpi_onetime_cb { @@ -218,6 +255,7 @@ class vpi_cb_startup : public vpi_onetime_cb { return 0; } + int arm_callback(void) { s_cb_data cb_data_s; @@ -232,11 +270,34 @@ class vpi_cb_startup : public vpi_onetime_cb { } }; +class vpi_cb_readonly : public vpi_onetime_cb { +public: + vpi_cb_readonly(gpi_impl_interface *impl) : vpi_onetime_cb(impl) { } + int arm_callback(void) { + s_cb_data cb_data_s; + s_vpi_time vpi_time_s; + + vpi_time_s.type = vpiSimTime, + vpi_time_s.low = 0, + vpi_time_s.high = 0, + + cb_data_s.reason = cbReadOnlySynch; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &vpi_time_s; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)this; + + return register_cb(&cb_data_s); + } +}; + class vpi_cb_shutdown : public vpi_onetime_cb { public: vpi_cb_shutdown(gpi_impl_interface *impl) : vpi_onetime_cb(impl) { } int run_callback(void) { + LOG_WARN("Shutdown called"); gpi_embed_end(); return 0; } @@ -258,9 +319,46 @@ class vpi_cb_shutdown : public vpi_onetime_cb { class vpi_cb_timed : public vpi_onetime_cb { public: vpi_cb_timed(gpi_impl_interface *impl) : vpi_onetime_cb(impl) { } - int run_callback(vpi_cb_hdl *cb) { - LOG_ERROR("In timed handler") - return 0; + + int arm_callback(uint64_t time_ps) { + s_cb_data cb_data_s; + s_vpi_time vpi_time_s; + + vpi_time_s.type = vpiSimTime; + vpi_time_s.high = (uint32_t)(time_ps>>32); + vpi_time_s.low = (uint32_t)(time_ps); + + cb_data_s.reason = cbAfterDelay; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &vpi_time_s; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)this; + + return register_cb(&cb_data_s); + } +}; + +class vpi_cb_readwrite : public vpi_onetime_cb { +public: + vpi_cb_readwrite(gpi_impl_interface *impl) : vpi_onetime_cb(impl) { } + + int arm_callback(void) { + s_cb_data cb_data_s; + s_vpi_time vpi_time_s; + + vpi_time_s.type = vpiSimTime; + vpi_time_s.high = 0; + vpi_time_s.low = 0; + + cb_data_s.reason = cbReadWriteSynch; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &vpi_time_s; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)this; + + return register_cb(&cb_data_s); } }; @@ -270,33 +368,51 @@ class vpi_impl : public gpi_impl_interface { /* Sim related */ void sim_end(void); - void get_sim_time(uint32_t *high, uint32_t *low) { } + void get_sim_time(uint32_t *high, uint32_t *low); /* Signal related */ gpi_obj_hdl *get_root_handle(const char *name); - gpi_obj_hdl *get_handle_by_name(const char *name, gpi_obj_hdl *parent) { return NULL; } - gpi_obj_hdl *get_handle_by_index(gpi_obj_hdl *parent, uint32_t index) { return NULL; } + gpi_obj_hdl *get_handle_by_name(const char *name, gpi_obj_hdl *parent); + gpi_obj_hdl *get_handle_by_index(gpi_obj_hdl *parent, uint32_t index); void free_handle(gpi_obj_hdl*) { } gpi_iterator *iterate_handle(uint32_t type, gpi_obj_hdl *base) { return NULL; } gpi_obj_hdl *next_handle(gpi_iterator *iterator) { return NULL; } - char* get_signal_value_binstr(gpi_obj_hdl *gpi_hdl) { return NULL; } - char* get_signal_name_str(gpi_obj_hdl *gpi_hdl) { return NULL; } - char* get_signal_type_str(gpi_obj_hdl *gpi_hdl) { return NULL; } - void set_signal_value_int(gpi_obj_hdl *gpi_hdl, int value) { } - void set_signal_value_str(gpi_obj_hdl *gpi_hdl, const char *str) { } // String of binary char(s) [1, 0, x, z] + char* get_signal_value_binstr(gpi_obj_hdl *gpi_hdl); + char* get_signal_name_str(gpi_obj_hdl *gpi_hdl); + char* get_signal_type_str(gpi_obj_hdl *gpi_hdl); + void set_signal_value_int(gpi_obj_hdl *gpi_hdl, int value); + void set_signal_value_str(gpi_obj_hdl *gpi_hdl, const char *str); // String of binary char(s) [1, 0, x, z] /* Callback related */ - gpi_cb_hdl *register_timed_callback(uint64_t time_ps) { return NULL; } - gpi_cb_hdl *register_value_change_callback(gpi_obj_hdl *obj_hdl) { return NULL; } - gpi_cb_hdl *register_readonly_callback(void) { return NULL; } + gpi_cb_hdl *register_timed_callback(uint64_t time_ps); + gpi_cb_hdl *register_value_change_callback(gpi_obj_hdl *obj_hdl); + gpi_cb_hdl *register_readonly_callback(void); gpi_cb_hdl *register_nexttime_callback(void) { return NULL; } - gpi_cb_hdl *register_readwrite_callback(void) { return NULL; } - int deregister_callback(gpi_cb_hdl *gpi_hdl) { return 0; } + gpi_cb_hdl *register_readwrite_callback(void); + int deregister_callback(gpi_cb_hdl *gpi_hdl); gpi_cb_hdl *create_cb_handle(void) { return NULL; } void destroy_cb_handle(gpi_cb_hdl *gpi_hdl) { } }; +extern "C" { + +static vpi_cb_hdl *sim_init_cb; +static vpi_cb_hdl *sim_finish_cb; +static vpi_impl *vpi_table; + +} + +void vpi_impl::get_sim_time(uint32_t *high, uint32_t *low) +{ + s_vpi_time vpi_time_s; + vpi_time_s.type = vpiSimTime;//vpiScaledRealTime; //vpiSimTime; + vpi_get_time(NULL, &vpi_time_s); + check_vpi_error(); + *high = vpi_time_s.high; + *low = vpi_time_s.low; +} + gpi_obj_hdl *vpi_impl::get_root_handle(const char* name) { FENTER @@ -348,46 +464,277 @@ gpi_obj_hdl *vpi_impl::get_root_handle(const char* name) return NULL; } -#if 0 -gpi_cb_hdl *vpi_impl::register_timed_callback(uint64_t time_ps) +/** + * @brief Get a handle to an object under the scope of parent + * + * @param name of the object to find + * @param parent handle to parent object defining the scope to search + * + * @return gpi_sim_hdl for the new object or NULL if object not found + */ +gpi_obj_hdl *vpi_impl::get_handle_by_name(const char *name, gpi_obj_hdl *parent) +{ + FENTER + gpi_obj_hdl *rv; + vpiHandle obj; + vpiHandle iterator; + int len; + char *buff; + vpi_obj_hdl *vpi_obj = reinterpret_cast(parent); + + // Structures aren't technically a scope, according to the LRM. If parent + // is a structure then we have to iterate over the members comparing names + if (vpiStructVar == vpi_get(vpiType, vpi_obj->vpi_hdl)) { + + iterator = vpi_iterate(vpiMember, vpi_obj->vpi_hdl); + + for (obj = vpi_scan(iterator); obj != NULL; obj = vpi_scan(iterator)) { + + if (!strcmp(name, strrchr(vpi_get_str(vpiName, obj), 46) + 1)) + break; + } + + if (!obj) + return NULL; + + // Need to free the iterator if it didn't return NULL + if (!vpi_free_object(iterator)) { + LOG_WARN("VPI: Attempting to free root iterator failed!"); + check_vpi_error(); + } + + goto success; + } + + if (name) + len = strlen(name) + 1; + + buff = (char *)malloc(len); + if (buff == NULL) { + LOG_CRITICAL("VPI: Attempting allocate string buffer failed!"); + return NULL; + } + + strncpy(buff, name, len); + obj = vpi_handle_by_name(buff, vpi_obj->vpi_hdl); + if (!obj) { + LOG_DEBUG("VPI: Handle '%s' not found!", name); + + // NB we deliberately don't dump an error message here because it's + // a valid use case to attempt to grab a signal by name - for example + // optional signals on a bus. + // check_vpi_error(); + free(buff); + return NULL; + } + + free(buff); + +success: + rv = new vpi_obj_hdl(obj, this); + + FEXIT + return rv; +} + +/** + * @brief Get a handle for an object based on its index within a parent + * + * @param parent handle to the parent + * @param indext Index to retrieve + * + * Can be used on bit-vectors to access a specific bit or + * memories to access an address + */ +gpi_obj_hdl *vpi_impl::get_handle_by_index(gpi_obj_hdl *parent, uint32_t index) { FENTER + vpiHandle obj; + gpi_obj_hdl *rv; + vpi_obj_hdl *vpi_obj = reinterpret_cast(parent); + + obj = vpi_handle_by_index(vpi_obj->vpi_hdl, index); + if (!obj) { + LOG_ERROR("VPI: Handle idx '%d' not found!", index); + return NULL; + } + + rv = new vpi_obj_hdl(obj, this); + + FEXIT + return rv; +} + +// Value related functions +void vpi_impl::set_signal_value_int(gpi_obj_hdl *gpi_hdl, int value) +{ + FENTER + s_vpi_value value_s; + p_vpi_value value_p = &value_s; + + value_p->value.integer = value; + value_p->format = vpiIntVal; - int ret; - s_cb_data cb_data_s; s_vpi_time vpi_time_s; + p_vpi_time vpi_time_p = &vpi_time_s; - vpi_time_s.type = vpiSimTime; - vpi_time_s.high = (uint32_t)(time_ps>>32); - vpi_time_s.low = (uint32_t)(time_ps); + vpi_time_p->type = vpiSimTime; + vpi_time_p->high = 0; + vpi_time_p->low = 0; - cb_data_s.reason = cbAfterDelay; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = &vpi_time_s; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)hdl; + // Use Inertial delay to schedule an event, thus behaving like a verilog testbench + vpi_obj_hdl *vpi_obj = reinterpret_cast(gpi_hdl); + vpi_put_value(vpi_obj->vpi_hdl, value_p, vpi_time_p, vpiInertialDelay); + check_vpi_error(); - ret = hdl->arm_callback(&cb_data_s); + FEXIT +} +void vpi_impl::set_signal_value_str(gpi_obj_hdl *gpi_hdl, const char *str) +{ + FENTER + s_vpi_value value_s; + p_vpi_value value_p = &value_s; + + int len; + char *buff; + if (str) + len = strlen(str) + 1; + + buff = (char *)malloc(len); + if (buff== NULL) { + LOG_CRITICAL("VPI: Attempting allocate string buffer failed!"); + return; + } + + strncpy(buff, str, len); + + value_p->value.str = buff; + value_p->format = vpiBinStrVal; + + /* vpiNoDelay -- Set the value immediately. The p_vpi_time parameter + * may be NULL, in this case. This is like a blocking assignment + * in behavioral code. + */ + vpi_obj_hdl *vpi_obj = reinterpret_cast(gpi_hdl); + vpi_put_value(vpi_obj->vpi_hdl, value_p, NULL, vpiNoDelay); + check_vpi_error(); + free(buff); FEXIT - return ret; } -#endif -extern "C" { +char *vpi_impl::get_signal_type_str(gpi_obj_hdl *gpi_hdl) +{ + FENTER + vpi_obj_hdl *vpi_obj = reinterpret_cast(gpi_hdl); + const char *name = vpi_get_str(vpiType, vpi_obj->vpi_hdl); + check_vpi_error(); + char *result = gpi_hdl->gpi_copy_name(name); + FEXIT + return result; +} -static vpi_cb_hdl *sim_init_cb; -static vpi_cb_hdl *sim_finish_cb; -static vpi_impl *vpi_table; +char *vpi_impl::get_signal_name_str(gpi_obj_hdl *gpi_hdl) +{ + FENTER + vpi_obj_hdl *vpi_obj = reinterpret_cast(gpi_hdl); + const char *name = vpi_get_str(vpiFullName, vpi_obj->vpi_hdl); + check_vpi_error(); + char *result = gpi_hdl->gpi_copy_name(name); + FEXIT + return result; +} + +char *vpi_impl::get_signal_value_binstr(gpi_obj_hdl *gpi_hdl) +{ + FENTER + vpi_obj_hdl *vpi_obj = reinterpret_cast(gpi_hdl); + s_vpi_value value_s = {vpiBinStrVal}; + p_vpi_value value_p = &value_s; + + vpi_get_value(vpi_obj->vpi_hdl, value_p); + check_vpi_error(); + + char *result = gpi_hdl->gpi_copy_name(value_p->value.str); + FEXIT + return result; +} +gpi_cb_hdl *vpi_impl::register_timed_callback(uint64_t time_ps) +{ + FENTER + + vpi_cb_timed *hdl = new vpi_cb_timed(vpi_table); + + if (hdl->arm_callback(time_ps)) { + delete(hdl); + hdl = NULL; + } + + FEXIT + return hdl; +} + +gpi_cb_hdl *vpi_impl::register_readwrite_callback(void) +{ + FENTER + + vpi_cb_readwrite *hdl = new vpi_cb_readwrite(vpi_table); + + if (hdl->arm_callback()) { + delete(hdl); + hdl = NULL; + } + + FEXIT + return hdl; +} + +gpi_cb_hdl *vpi_impl::register_value_change_callback(gpi_obj_hdl *gpi_hdl) +{ + FENTER + + vpi_obj_hdl *vpi_obj = reinterpret_cast(gpi_hdl); + vpi_cb_value_change *hdl = new vpi_cb_value_change(vpi_table); + + if (hdl->arm_callback(vpi_obj)) { + delete(hdl); + hdl = NULL; + } + + FEXIT + return hdl; +} + +gpi_cb_hdl *vpi_impl::register_readonly_callback(void) +{ + FENTER + + vpi_cb_readonly *hdl = new vpi_cb_readonly(vpi_table); + + if (hdl->arm_callback()) { + delete(hdl); + hdl = NULL; + } + + FEXIT + return hdl; +} + +int vpi_impl::deregister_callback(gpi_cb_hdl *gpi_hdl) +{ + vpi_cb_hdl *vpi_obj = reinterpret_cast(gpi_hdl); + return vpi_obj->cleanup_callback(); } // If the Pything world wants things to shut down then unregister // the callback for end of sim void vpi_impl::sim_end(void) { - sim_finish_cb = NULL; + /* Some sims do not seem to be able to deregister the end of sim callback + * so we need to make sure we have tracked this and not call the handler + */ + sim_finish_cb->state = VPI_DELETE; vpi_control(vpiFinish); check_vpi_error(); } @@ -408,7 +755,9 @@ int32_t handle_vpi_callback(p_cb_data cb_data) //cb_hdl->set_state(VPI_PRE_CALL); //old_cb = user_data->cb_hdl; - cb_hdl->run_callback(); + if (cb_hdl->state == VPI_PRIMED) + cb_hdl->run_callback(); + //gpi_deregister_callback(cb_hdl); #if 0 // HACK: Investigate further - this breaks modelsim From 1e778cab0c920e9b1eb1c545b547fe69bbc97570 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 29 Sep 2014 19:43:57 +0100 Subject: [PATCH 0593/2656] Mixedlang: Functionality tests pass --- cocotb/__init__.py | 2 + cocotb/triggers.py | 20 ++++--- lib/Makefile | 1 - lib/simulator/simulatormodule.c | 20 ++++--- lib/vpi/gpi_vpi.cpp | 78 +++++++++++++++++++++++----- makefiles/simulators/Makefile.icarus | 5 ++ 6 files changed, 92 insertions(+), 34 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 3f5d96bc..f58c3c31 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -153,6 +153,8 @@ def _sim_event(level, message): else: scheduler.log.error("Unsupported sim event") + return True + def process_plusargs(): diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 5e9c0a37..e1dc1f09 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -92,14 +92,16 @@ def __init__(self): def unprime(self): """Unregister a prior registered timed callback""" - if self.primed: + if self.cbhdl: + #self.log.warn("Unpriming %s" % (str(self))) simulator.deregister_callback(self.cbhdl) + self.cbhdl = None Trigger.unprime(self) def __del__(self): """Remove knowledge of the trigger""" - if self.cbhdl is None: - self.cbhdl = simulator.remove_callback() + if self.cbhdl is not None: + self.unprime() Trigger.__del__(self) @@ -151,7 +153,7 @@ def __init__(self): def prime(self, callback): Trigger.prime(self) - self.chhdl = simulator.register_readonly_callback(callback, self) + self.cbhdl = simulator.register_readonly_callback(callback, self) if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) @@ -218,10 +220,12 @@ def prime(self, callback): self._callback = callback def _check(obj): - if self.signal.value: - self._callback(self) - else: - simulator.reenable_callback(self.cbhdl) + if 0: + if self.signal.value: + self._callback(self) + else: + simulator.reenable_callback(self.cbhdl) + self._callback(self) self.cbhdl = simulator.register_value_change_callback(self.signal._handle, _check, self) if self.cbhdl is None: diff --git a/lib/Makefile b/lib/Makefile index a712d3e0..31f9ac42 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -68,7 +68,6 @@ COCOTB_LIBS = $(LIB_DIR)/libgpilog.so \ $(LIB_DIR)/libcocotb.so \ $(LIB_DIR)/libgpi.so \ $(LIB_DIR)/libvpi.so \ - $(LIB_DIR)/libvhpi.so \ $(LIB_DIR)/libsim.so clean:: diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 6bd2ff80..9431149c 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -93,8 +93,6 @@ int handle_gpi_callback(void *user_data) // Python allowed - LOG_ERROR("Callback now"); - if (!PyCallable_Check(callback_data_p->function)) { fprintf(stderr, "Callback fired but function isn't callable?!\n"); DROP_GIL(gstate); @@ -107,12 +105,13 @@ int handle_gpi_callback(void *user_data) // If the return value is NULL a Python exception has occurred if (pValue == NULL) { + return 0; fprintf(stderr, "ERROR: called callback function returned NULL\n"); PyErr_Print(); fprintf(stderr, "Failed to execute callback\n"); DROP_GIL(gstate); gpi_sim_end(); - return 1; + return 0; } // Free up our mess @@ -127,7 +126,6 @@ int handle_gpi_callback(void *user_data) free(callback_data_p); } - DROP_GIL(gstate); return 0; @@ -188,7 +186,7 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) Py_INCREF(function); // Remaining args for function - if (numargs > 2) + if (numargs > 1) fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference else fArgs = PyTuple_New(0); // args must not be NULL, use an empty tuple if no arguments are needed. @@ -249,8 +247,8 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) Py_INCREF(function); // Remaining args for function - if (numargs > 2) - fArgs = PyTuple_GetSlice(args, 2, numargs); // New reference + if (numargs > 1) + fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference else fArgs = PyTuple_New(0); // args must not be NULL, use an empty tuple if no arguments are needed. @@ -310,8 +308,8 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) Py_INCREF(function); // Remaining args for function - if (numargs > 2) - fArgs = PyTuple_GetSlice(args, 2, numargs); // New reference + if (numargs > 1) + fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference else fArgs = PyTuple_New(0); // args must not be NULL, use an empty tuple if no arguments are needed. @@ -454,8 +452,8 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) Py_INCREF(function); // Remaining args for function - if (numargs > 3) - fArgs = PyTuple_GetSlice(args, 3, numargs); // New reference + if (numargs > 2) + fArgs = PyTuple_GetSlice(args, 2, numargs); // New reference else fArgs = PyTuple_New(0); // args must not be NULL, use an empty tuple if no arguments are needed. diff --git a/lib/vpi/gpi_vpi.cpp b/lib/vpi/gpi_vpi.cpp index 7aae5d01..797b8acd 100644 --- a/lib/vpi/gpi_vpi.cpp +++ b/lib/vpi/gpi_vpi.cpp @@ -169,6 +169,7 @@ class vpi_onetime_cb : public vpi_cb_hdl { vpi_onetime_cb(gpi_impl_interface *impl) : vpi_cb_hdl(impl) { } int cleanup_callback(void) { FENTER + //LOG_WARN("Cleanup %p state is %d", this, state); int rc; if (!vpi_hdl) { LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); @@ -184,17 +185,17 @@ class vpi_onetime_cb : public vpi_cb_hdl { check_vpi_error(); return rc; } - vpi_hdl = NULL; + } - // HACK: Calling vpi_free_object after vpi_remove_cb causes Modelsim to VPIEndOfSimulationCallback - #if 0 - rc = vpi_free_object(cb_hdl); - if (!rc) { - check_vpi_error(); - return rc; - } - #endif + // HACK: Calling vpi_free_object after vpi_remove_cb causes Modelsim to VPIEndOfSimulationCallback + + rc = vpi_free_object(vpi_hdl); + if (!rc) { + check_vpi_error(); + return rc; } + + vpi_hdl = NULL; state = VPI_FREE; return rc; } @@ -205,6 +206,7 @@ class vpi_recurring_cb : public vpi_cb_hdl { vpi_recurring_cb(gpi_impl_interface *impl) : vpi_cb_hdl(impl) { } int cleanup_callback(void) { FENTER + //LOG_WARN("Cleanup %p", this); int rc; if (!vpi_hdl) { LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); @@ -219,12 +221,15 @@ class vpi_recurring_cb : public vpi_cb_hdl { }; class vpi_cb_value_change : public vpi_recurring_cb { +private: + s_vpi_value cb_value; public: - vpi_cb_value_change(gpi_impl_interface *impl) : vpi_recurring_cb(impl) { } + vpi_cb_value_change(gpi_impl_interface *impl) : vpi_recurring_cb(impl) { + cb_value.format = vpiIntVal; + } int arm_callback(vpi_obj_hdl *vpi_hdl) { s_cb_data cb_data_s; s_vpi_time vpi_time_s = {.type = vpiSuppressTime }; - s_vpi_value cb_value = {.format = vpiIntVal }; cb_data_s.reason = cbValueChange; cb_data_s.cb_rtn = handle_vpi_callback; @@ -362,6 +367,29 @@ class vpi_cb_readwrite : public vpi_onetime_cb { } }; +class vpi_cb_nexttime : public vpi_onetime_cb { +public: + vpi_cb_nexttime(gpi_impl_interface *impl) : vpi_onetime_cb(impl) { } + + int arm_callback(void) { + s_cb_data cb_data_s; + s_vpi_time vpi_time_s; + + vpi_time_s.type = vpiSimTime; + vpi_time_s.high = 0; + vpi_time_s.low = 0; + + cb_data_s.reason = cbNextSimTime; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &vpi_time_s; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)this; + + return register_cb(&cb_data_s); + } +}; + class vpi_impl : public gpi_impl_interface { public: vpi_impl(const string& name) : gpi_impl_interface(name) { } @@ -387,7 +415,7 @@ class vpi_impl : public gpi_impl_interface { gpi_cb_hdl *register_timed_callback(uint64_t time_ps); gpi_cb_hdl *register_value_change_callback(gpi_obj_hdl *obj_hdl); gpi_cb_hdl *register_readonly_callback(void); - gpi_cb_hdl *register_nexttime_callback(void) { return NULL; } + gpi_cb_hdl *register_nexttime_callback(void); gpi_cb_hdl *register_readwrite_callback(void); int deregister_callback(gpi_cb_hdl *gpi_hdl); @@ -655,6 +683,8 @@ char *vpi_impl::get_signal_value_binstr(gpi_obj_hdl *gpi_hdl) vpi_get_value(vpi_obj->vpi_hdl, value_p); check_vpi_error(); + LOG_WARN("Value is %s", value_p->value.str) + char *result = gpi_hdl->gpi_copy_name(value_p->value.str); FEXIT return result; @@ -721,10 +751,27 @@ gpi_cb_hdl *vpi_impl::register_readonly_callback(void) return hdl; } +gpi_cb_hdl *vpi_impl::register_nexttime_callback(void) +{ + FENTER + + vpi_cb_nexttime *hdl = new vpi_cb_nexttime(vpi_table); + + if (hdl->arm_callback()) { + delete(hdl); + hdl = NULL; + } + + FEXIT + return hdl; +} + int vpi_impl::deregister_callback(gpi_cb_hdl *gpi_hdl) { vpi_cb_hdl *vpi_obj = reinterpret_cast(gpi_hdl); - return vpi_obj->cleanup_callback(); + int rc = vpi_obj->cleanup_callback(); + //delete(vpi_obj); + return rc; } // If the Pything world wants things to shut down then unregister @@ -755,8 +802,11 @@ int32_t handle_vpi_callback(p_cb_data cb_data) //cb_hdl->set_state(VPI_PRE_CALL); //old_cb = user_data->cb_hdl; - if (cb_hdl->state == VPI_PRIMED) + if (cb_hdl->state == VPI_PRIMED) { + cb_hdl->state = VPI_PRE_CALL; cb_hdl->run_callback(); + cb_hdl->state = VPI_POST_CALL; + } //gpi_deregister_callback(cb_hdl); #if 0 diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index abd26e11..715d19e0 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -39,3 +39,8 @@ results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) clean:: -@rm -rf $(SIM_BUILD) + +debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + gdb --args vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) From 799fb44fe3348da2ab86c2e50e6d1732fb004443 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 29 Sep 2014 23:47:03 +0100 Subject: [PATCH 0594/2656] Mixedlang: Add virtual destructores to fix mem leak --- cocotb/triggers.py | 35 +++++++++++------------- lib/gpi/gpi_common.cpp | 4 ++- lib/gpi/gpi_priv.h | 4 +++ lib/vpi/gpi_vpi.cpp | 60 ++++++++++++++++++++++++++++++++---------- 4 files changed, 69 insertions(+), 34 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index e1dc1f09..27fcd9ab 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -93,9 +93,8 @@ def __init__(self): def unprime(self): """Unregister a prior registered timed callback""" if self.cbhdl: - #self.log.warn("Unpriming %s" % (str(self))) simulator.deregister_callback(self.cbhdl) - self.cbhdl = None + self.cbhdl = None Trigger.unprime(self) def __del__(self): @@ -117,10 +116,10 @@ def __init__(self, time_ps): def prime(self, callback): """Register for a timed callback""" - Trigger.prime(self) self.cbhdl = simulator.register_timed_callback(self.time_ps, callback, self) if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) + Trigger.prime(self) def __str__(self): return self.__class__.__name__ + "(%dps)" % self.time_ps @@ -135,10 +134,10 @@ def __init__(self, signal): def prime(self, callback): """Register notification of a value change via a callback""" - Trigger.prime(self) self.cbhdl = simulator.register_value_change_callback(self.signal._handle, callback, self) if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) + Trigger.prime(self) def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal.name @@ -152,10 +151,10 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): - Trigger.prime(self) self.cbhdl = simulator.register_readonly_callback(callback, self) if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) + Trigger.prime(self) def __str__(self): return self.__class__.__name__ + "(readonly)" @@ -173,10 +172,10 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): - Trigger.prime(self) self.cbhdl = simulator.register_rwsynch_callback(callback, self) if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) + Trigger.prime(self) def __str__(self): return self.__class__.__name__ + "(readwritesync)" @@ -193,10 +192,10 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): - Trigger.prime(self) self.cbhdl = simulator.register_nextstep_callback(callback, self) if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) + Trigger.prime(self) def __str__(self): return self.__class__.__name__ + "(nexttimestep)" @@ -216,20 +215,18 @@ def __init__(self, signal): Edge.__init__(self, signal) def prime(self, callback): - Trigger.prime(self) self._callback = callback def _check(obj): - if 0: - if self.signal.value: - self._callback(self) - else: - simulator.reenable_callback(self.cbhdl) - self._callback(self) + if self.signal.value: + self._callback(self) + else: + self.cbhdl = simulator.register_value_change_callback(self.signal._handle, _check, self) self.cbhdl = simulator.register_value_change_callback(self.signal._handle, _check, self) if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) + Trigger.prime(self) def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal.name @@ -247,7 +244,6 @@ def __init__(self, signal, num_cycles): self.num_cycles = num_cycles def prime(self, callback): - Trigger.prime(self) self._callback = callback def _check(obj): @@ -265,6 +261,7 @@ def _check(obj): self.cbhdl = simulator.register_value_change_callback(self.signal._handle, _check, self) if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) + Trigger.prime(self) def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal.name @@ -288,11 +285,11 @@ def __init__(self, *args): raise TriggerException("%s requires a list of Trigger objects" % self.__class__.__name__) def prime(self, callback): - Trigger.prime(self) self._callback = callback self._fired = [] for trigger in self._triggers: trigger.prime(self._check_all_fired) + Trigger.prime(self) def _check_all_fired(self, trigger): self._fired.append(trigger) @@ -318,9 +315,9 @@ def __init__(self, parent): self.parent = parent def prime(self, callback): - Trigger.prime(self) self._callback = callback self.parent.prime(callback, self) + Trigger.prime(self) def __call__(self): self._callback(self) @@ -339,8 +336,8 @@ def __init__(self, name=""): self.data = None def prime(self, callback, trigger): - Trigger.prime(self) self._pending.append(trigger) + Trigger.prime(self) def set(self, data=None): """Wake up any coroutines blocked on this event""" @@ -382,9 +379,9 @@ def __init__(self, parent): self.parent = parent def prime(self, callback): - Trigger.prime(self) self._callback = callback self.parent.prime(callback, self) + Trigger.prime(self) def __call__(self): self._callback(self) diff --git a/lib/gpi/gpi_common.cpp b/lib/gpi/gpi_common.cpp index f0efb7ec..7b27ce73 100644 --- a/lib/gpi/gpi_common.cpp +++ b/lib/gpi/gpi_common.cpp @@ -265,8 +265,10 @@ gpi_sim_hdl gpi_register_readwrite_callback(int (*gpi_function)(void *), void *gpi_cb_data) { gpi_cb_hdl *gpi_hdl = registered_impls[0] ->register_readwrite_callback(); - if (!gpi_hdl) + if (!gpi_hdl) { LOG_ERROR("Failed to register a readwrite callback"); + return NULL; + } gpi_hdl->set_user_data(gpi_function, gpi_cb_data); return (gpi_sim_hdl)gpi_hdl; diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 301e4c07..d65f16e0 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -48,6 +48,8 @@ class gpi_obj_hdl : public gpi_hdl { public: gpi_obj_hdl(gpi_impl_interface *impl) : gpi_hdl(impl) { } char *gpi_copy_name(const char *name); + + virtual ~gpi_obj_hdl() { } }; class gpi_cb_hdl : public gpi_hdl { @@ -62,6 +64,8 @@ class gpi_cb_hdl : public gpi_hdl { int set_user_data(int (*gpi_function)(void*), void *data); void *get_user_data(void); + virtual ~gpi_cb_hdl() { } + private: gpi_cb_hdl(); diff --git a/lib/vpi/gpi_vpi.cpp b/lib/vpi/gpi_vpi.cpp index 797b8acd..b0a78683 100644 --- a/lib/vpi/gpi_vpi.cpp +++ b/lib/vpi/gpi_vpi.cpp @@ -89,6 +89,7 @@ class vpi_obj_hdl : public gpi_obj_hdl { public: vpi_obj_hdl(vpiHandle hdl, gpi_impl_interface *impl) : gpi_obj_hdl(impl), vpi_hdl(hdl) { } + virtual ~vpi_obj_hdl() { } public: vpiHandle vpi_hdl; @@ -101,7 +102,10 @@ class vpi_cb_hdl : public gpi_cb_hdl { public: vpi_cb_hdl(gpi_impl_interface *impl) : gpi_cb_hdl(impl), - vpi_hdl(NULL) { } + vpi_hdl(NULL), + state(VPI_FREE) { } + virtual int cleanup_callback(void) { return 0; } + virtual ~vpi_cb_hdl() { } protected: /* If the user data already has a callback handle then deregister @@ -176,6 +180,16 @@ class vpi_onetime_cb : public vpi_cb_hdl { exit(1); } + // HACK: Calling vpi_free_object after vpi_remove_cb causes Modelsim + // to VPIEndOfSimulationCallback +#if 0 + rc = vpi_free_object(vpi_hdl); + if (!rc) { + check_vpi_error(); + return rc; + } +#endif + // If the callback has not been called we also need to call // remove as well if (state == VPI_PRIMED) { @@ -187,18 +201,13 @@ class vpi_onetime_cb : public vpi_cb_hdl { } } - // HACK: Calling vpi_free_object after vpi_remove_cb causes Modelsim to VPIEndOfSimulationCallback - rc = vpi_free_object(vpi_hdl); - if (!rc) { - check_vpi_error(); - return rc; - } vpi_hdl = NULL; state = VPI_FREE; return rc; } + virtual ~vpi_onetime_cb() { } }; class vpi_recurring_cb : public vpi_cb_hdl { @@ -215,9 +224,14 @@ class vpi_recurring_cb : public vpi_cb_hdl { rc = vpi_remove_cb(vpi_hdl); check_vpi_error(); + + vpi_hdl = NULL; + state = VPI_FREE; + FEXIT return rc; } + virtual ~vpi_recurring_cb() { } }; class vpi_cb_value_change : public vpi_recurring_cb { @@ -240,6 +254,7 @@ class vpi_cb_value_change : public vpi_recurring_cb { return register_cb(&cb_data_s); } + virtual ~vpi_cb_value_change() { } }; class vpi_cb_startup : public vpi_onetime_cb { @@ -273,6 +288,8 @@ class vpi_cb_startup : public vpi_onetime_cb { return register_cb(&cb_data_s); } + + virtual ~vpi_cb_startup() { } }; class vpi_cb_readonly : public vpi_onetime_cb { @@ -295,6 +312,8 @@ class vpi_cb_readonly : public vpi_onetime_cb { return register_cb(&cb_data_s); } + + virtual ~vpi_cb_readonly() { } }; @@ -319,6 +338,8 @@ class vpi_cb_shutdown : public vpi_onetime_cb { return register_cb(&cb_data_s); } + + virtual ~vpi_cb_shutdown() { } }; class vpi_cb_timed : public vpi_onetime_cb { @@ -342,6 +363,8 @@ class vpi_cb_timed : public vpi_onetime_cb { return register_cb(&cb_data_s); } + + virtual ~vpi_cb_timed() { } }; class vpi_cb_readwrite : public vpi_onetime_cb { @@ -365,6 +388,8 @@ class vpi_cb_readwrite : public vpi_onetime_cb { return register_cb(&cb_data_s); } + + virtual ~vpi_cb_readwrite() { } }; class vpi_cb_nexttime : public vpi_onetime_cb { @@ -388,6 +413,8 @@ class vpi_cb_nexttime : public vpi_onetime_cb { return register_cb(&cb_data_s); } + + virtual ~vpi_cb_nexttime() { } }; class vpi_impl : public gpi_impl_interface { @@ -683,8 +710,6 @@ char *vpi_impl::get_signal_value_binstr(gpi_obj_hdl *gpi_hdl) vpi_get_value(vpi_obj->vpi_hdl, value_p); check_vpi_error(); - LOG_WARN("Value is %s", value_p->value.str) - char *result = gpi_hdl->gpi_copy_name(value_p->value.str); FEXIT return result; @@ -769,8 +794,14 @@ gpi_cb_hdl *vpi_impl::register_nexttime_callback(void) int vpi_impl::deregister_callback(gpi_cb_hdl *gpi_hdl) { vpi_cb_hdl *vpi_obj = reinterpret_cast(gpi_hdl); + if (vpi_obj->state == VPI_PRE_CALL) { + //LOG_INFO("Not deleting yet %p", vpi_obj); + return 0; + } + int rc = vpi_obj->cleanup_callback(); - //delete(vpi_obj); + // LOG_INFO("DELETING %p", vpi_obj); + delete(vpi_obj); return rc; } @@ -800,15 +831,16 @@ int32_t handle_vpi_callback(p_cb_data cb_data) if (!cb_hdl) LOG_CRITICAL("VPI: Callback data corrupted"); - //cb_hdl->set_state(VPI_PRE_CALL); - //old_cb = user_data->cb_hdl; + LOG_DEBUG("Running %p", cb_hdl); + if (cb_hdl->state == VPI_PRIMED) { cb_hdl->state = VPI_PRE_CALL; cb_hdl->run_callback(); cb_hdl->state = VPI_POST_CALL; } - //gpi_deregister_callback(cb_hdl); - + + gpi_deregister_callback(cb_hdl); + #if 0 // HACK: Investigate further - this breaks modelsim #if 0 From 35541923bf9e25f943924b6c664c015098608fc0 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 30 Sep 2014 00:03:21 +0100 Subject: [PATCH 0595/2656] Mixedlang: Fixup sim Makefiles to use new lib names --- makefiles/simulators/Makefile.aldec | 6 +++--- makefiles/simulators/Makefile.ghdl | 2 +- makefiles/simulators/Makefile.ius | 4 ++-- makefiles/simulators/Makefile.modelsim | 2 +- makefiles/simulators/Makefile.nvc | 2 +- makefiles/simulators/Makefile.questa | 2 +- makefiles/simulators/Makefile.vcs | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 4fa5b201..51360785 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -38,7 +38,7 @@ endif EXTRA_LIBS = -laldecpli -lstdc++ -lsupc++ EXTRA_LIBDIRS = -L$(ALDEC_PATH)/bin/Linux64 -ALOG_ARGS += +define+COCOTB_SIM -dbg -pli libgpi +ALOG_ARGS += +define+COCOTB_SIM -dbg -pli libvpi ACOM_ARGS += -dbg ifeq ($(COVERAGE),1) @@ -47,10 +47,10 @@ ifeq ($(COVERAGE),1) endif ifeq ($(GPI_IMPL),vpi) - GPI_ARGS = -pli libgpi + GPI_ARGS = -pli libvpi endif ifeq ($(GPI_IMPL),vhpi) - GPI_ARGS = -loadvhpi $(LIB_DIR)/libgpi:vhpi_startup_routines_bootstrap + GPI_ARGS = -loadvhpi $(LIB_DIR)/libvhpi:vhpi_startup_routines_bootstrap endif ifndef GPI_ARGS diff --git a/makefiles/simulators/Makefile.ghdl b/makefiles/simulators/Makefile.ghdl index c1fb5fe6..ccb45531 100644 --- a/makefiles/simulators/Makefile.ghdl +++ b/makefiles/simulators/Makefile.ghdl @@ -33,7 +33,7 @@ analyse: $(VHDL_SOURCES) $(SIM_BUILD) cd $(SIM_BUILD) && ghdl -a $(VHDL_SOURCES) results.xml: analyse $(COCOTB_LIBS) - cd $(SIM_BUILD) && LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) ghdl -e -Wl,-L$(LIB_DIR) -Wl,-lgpi $(TOPLEVEL) + cd $(SIM_BUILD) && LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) ghdl -e -Wl,-L$(LIB_DIR) -Wl,-lvpi $(TOPLEVEL) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ ./$(SIM_BUILD)/$(TOPLEVEL) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 2635ce8a..8da04098 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -39,13 +39,13 @@ else endif ifeq ($(GPI_IMPL),vpi) - GPI_ARGS = -loadvpi $(LIB_DIR)/libgpi:vlog_startup_routines_bootstrap + GPI_ARGS = -loadvpi $(LIB_DIR)/libvpi:vlog_startup_routines_bootstrap EXTRA_ARGS += -sv HDL_SOURCES = $(VERILOG_SOURCES) endif ifeq ($(GPI_IMPL),vhpi) - GPI_ARGS = -loadvhpi $(LIB_DIR)/libgpi:vhpi_startup_routines_bootstrap + GPI_ARGS = -loadvhpi $(LIB_DIR)/libvhpi:vhpi_startup_routines_bootstrap #EXTRA_ARGS += -cdslib cds.lib EXTRA_ARGS += -v93 EXTRA_ARGS += -top worklib.$(TOPLEVEL) diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index ae7d7ff5..cc2424fe 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -48,7 +48,7 @@ endif runsim.do : $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) echo "vlib work" > $@ echo "vlog -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(VERILOG_SOURCES)" >> $@ - echo "vsim -pli libgpi.so $(VSIM_ARGS) work.$(TOPLEVEL)" >> $@ + echo "vsim -pli libvpi.so $(VSIM_ARGS) work.$(TOPLEVEL)" >> $@ ifneq ($(GUI),1) echo "run -all" >> $@ echo "quit" >> $@ diff --git a/makefiles/simulators/Makefile.nvc b/makefiles/simulators/Makefile.nvc index 7c5f3808..b613a69c 100644 --- a/makefiles/simulators/Makefile.nvc +++ b/makefiles/simulators/Makefile.nvc @@ -11,7 +11,7 @@ results.xml: analyse $(COCOTB_LIBS) cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ LD_LIBRARY_PATH=$(LIB_DIR) \ - nvc -r --load $(LIB_DIR)/libgpi.so $(TOPLEVEL) + nvc -r --load $(LIB_DIR)/libvpi.so $(TOPLEVEL) clean:: -@rm -rf $(SIM_BUILD) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index c30e6548..f152ec54 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -27,7 +27,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -VPI_FILE := $(LIB_DIR)/libgpi.so +VPI_FILE := $(LIB_DIR)/libvpi.so results.xml: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(PYTHON_LIBDIR):$(PYTHON_DYNLIBDIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index 87f76308..aa8cf098 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -43,7 +43,7 @@ $(SIM_BUILD)/pli.tab : $(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(COCOTB_LIBS) $(CUSTOM_COMPILE_DEPS) cd $(SIM_BUILD) && \ LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) TOPLEVEL=$(TOPLEVEL) \ - vcs +acc+1 +vpi -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -debug -load libgpi.so $(COMPILE_ARGS) $(VERILOG_SOURCES) + vcs +acc+1 +vpi -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -debug -load libvpi.so $(COMPILE_ARGS) $(VERILOG_SOURCES) # Execution phase results.xml: $(SIM_BUILD)/simv $(PYTHON_FILES) $(CUSTOM_SIM_DEPS) From 9426d60619c43d9593dd8d4428c9c348747ac4e3 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 30 Sep 2014 11:32:35 +0100 Subject: [PATCH 0596/2656] Issue#161: Add new callback handle implemention file --- lib/gpi/gpi_cb_hdl.cpp | 134 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 lib/gpi/gpi_cb_hdl.cpp diff --git a/lib/gpi/gpi_cb_hdl.cpp b/lib/gpi/gpi_cb_hdl.cpp new file mode 100644 index 00000000..c750158b --- /dev/null +++ b/lib/gpi/gpi_cb_hdl.cpp @@ -0,0 +1,134 @@ +/****************************************************************************** +* Copyright (c) 2013 Potential Ventures Ltd +* Copyright (c) 2013 SolarFlare Communications Inc +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd, +* SolarFlare Communications Inc nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +#include "gpi_priv.h" +#include + +using namespace std; + +/* Genertic base clss implementations */ +char *gpi_obj_hdl::gpi_copy_name(const char *name) +{ + int len; + char *result; + const char null[] = "NULL"; + + if (name) + len = strlen(name) + 1; + else { + LOG_CRITICAL("GPI: attempt to use NULL from impl"); + len = strlen(null); + name = null; + } + + result = (char *)malloc(len); + if (result == NULL) { + LOG_CRITICAL("GPI: Attempting allocate string buffer failed!"); + len = strlen(null); + name = null; + } + + snprintf(result, len, "%s", name); + + return result; +} + +int gpi_cb_hdl::handle_callback(void) +{ + return this->gpi_function(m_cb_data); +} + +int gpi_cb_hdl::arm_callback(void) +{ + return 0; +} + +int gpi_cb_hdl::run_callback(void) +{ + return this->gpi_function(m_cb_data); +} + +int gpi_cb_hdl::set_user_data(int (*gpi_function)(void*), void *data) +{ + if (!gpi_function) { + LOG_ERROR("gpi_function to set_user_data is NULL"); + } + this->gpi_function = gpi_function; + this->m_cb_data = data; + return 0; +} + +void * gpi_cb_hdl::get_user_data(void) +{ + return m_cb_data; +} + +int gpi_cb_hdl::cleanup_callback(void) +{ + LOG_WARN("Generic cleanup handler"); + return 0; +} + +/* Specific callback types */ + +int gpi_recurring_cb::cleanup_callback(void) +{ + LOG_ERROR("Need to override"); + return 0; +} + +int gpi_onetime_cb::cleanup_callback(void) +{ + LOG_ERROR("Need to override"); + return 0; +} + +int gpi_cb_value_change::run_callback(void) +{ + LOG_ERROR("Need to override"); + return 0; +} + +int gpi_cb_readonly_phase::run_callback(void) +{ + LOG_ERROR("Need to override"); + return 0; +} + +int gpi_cb_nexttime_phase::run_callback(void) +{ + LOG_ERROR("Need to override"); + return 0; +} + +int gpi_cb_readwrite_phase::run_callback(void) +{ + LOG_ERROR("Need to override"); + return 0; +} \ No newline at end of file From b1c1b7a3d018d8424d8fed844c74df8298b828a8 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 30 Sep 2014 17:48:09 +0100 Subject: [PATCH 0597/2656] Issue#161: Fix over zelous rename of libs --- lib/vhpi/gpi_vhpi.cpp | 1038 +++++++++++++++++++++++++++ makefiles/simulators/Makefile.aldec | 2 +- 2 files changed, 1039 insertions(+), 1 deletion(-) create mode 100644 lib/vhpi/gpi_vhpi.cpp diff --git a/lib/vhpi/gpi_vhpi.cpp b/lib/vhpi/gpi_vhpi.cpp new file mode 100644 index 00000000..74e811f3 --- /dev/null +++ b/lib/vhpi/gpi_vhpi.cpp @@ -0,0 +1,1038 @@ +/****************************************************************************** +* Copyright (c) 2014 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd not the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +// TODO: +// Some functions are completely untested (vhpi_get_handle_by_index) and others +// need optimisation. +// +// VHPI seems to run significantly slower than VPI, need to investigate. + + +#include "../gpi/gpi_priv.h" +#include + +#define VHPI_CHECKING 1 + +static gpi_sim_hdl sim_init_cb; +static gpi_sim_hdl sim_finish_cb; + +typedef enum vhpi_cb_state_e { + VHPI_FREE = 0, + VHPI_PRIMED = 1, + VHPI_PRE_CALL = 2, + VHPI_POST_CALL = 3, + VHPI_DELETE = 4, +} vhpi_cb_state_t; + +// callback user data used for VPI callbacks +// (mostly just a thin wrapper around the gpi_callback) +typedef struct t_vhpi_cb { + vhpiHandleT cb_hdl; + vhpiValueT cb_value; + vhpi_cb_state_t state; + gpi_cb_hdl_t gpi_cb_data; + int (*vhpi_cleanup)(struct t_vhpi_cb *); +} s_vhpi_cb, *p_vhpi_cb; + +// Forward declarations +static int vhpi_deregister_callback(gpi_sim_hdl gpi_hdl); +static void vhpi_destroy_cb_handle(gpi_cb_hdl hdl); + +static const char * vhpi_reason_to_string(int reason) +{ + switch (reason) { + case vhpiCbValueChange: + return "vhpiCbValueChange"; + case vhpiCbStartOfNextCycle: + return "vhpiCbStartOfNextCycle"; + case vhpiCbStartOfPostponed: + return "vhpiCbStartOfPostponed"; + case vhpiCbEndOfTimeStep: + return "vhpiCbEndOfTimeStep"; + case vhpiCbNextTimeStep: + return "vhpiCbNextTimeStep"; + case vhpiCbAfterDelay: + return "vhpiCbAfterDelay"; + case vhpiCbStartOfSimulation: + return "vhpiCbStartOfSimulation"; + case vhpiCbEndOfSimulation: + return "vhpiCbEndOfSimulation"; + case vhpiCbEndOfProcesses: + return "vhpiCbEndOfProcesses"; + case vhpiCbLastKnownDeltaCycle: + return "vhpiCbLastKnownDeltaCycle"; + default: + return "unknown"; + } +} + +static const char * vhpi_format_to_string(int reason) +{ + switch (reason) { + case vhpiBinStrVal: + return "vhpiBinStrVal"; + case vhpiOctStrVal: + return "vhpiOctStrVal"; + case vhpiDecStrVal: + return "vhpiDecStrVal"; + case vhpiHexStrVal: + return "vhpiHexStrVal"; + case vhpiEnumVal: + return "vhpiEnumVal"; + case vhpiIntVal: + return "vhpiIntVal"; + case vhpiLogicVal: + return "vhpiLogicVal"; + case vhpiRealVal: + return "vhpiRealVal"; + case vhpiStrVal: + return "vhpiStrVal"; + case vhpiCharVal: + return "vhpiCharVal"; + case vhpiTimeVal: + return "vhpiTimeVal"; + case vhpiPhysVal: + return "vhpiPhysVal"; + case vhpiObjTypeVal: + return "vhpiObjTypeVal"; + case vhpiPtrVal: + return "vhpiPtrVal"; + case vhpiEnumVecVal: + return "vhpiEnumVecVal"; + + default: + return "unknown"; + } +} + + + +// Should be run after every VPI call to check error status +static int __check_vhpi_error(const char *func, long line) +{ + int level=0; +#if VHPI_CHECKING + vhpiErrorInfoT info; + int loglevel; + level = vhpi_check_error(&info); + if (level == 0) + return 0; + + switch (level) { + case vhpiNote: + loglevel = GPIInfo; + break; + case vhpiWarning: + loglevel = GPIWarning; + break; + case vhpiError: + loglevel = GPIError; + break; + case vhpiFailure: + case vhpiSystem: + case vhpiInternal: + loglevel = GPICritical; + break; + } + + gpi_log("cocotb.gpi", loglevel, __FILE__, func, line, + "VHPI Error level %d: %s\nFILE %s:%d", + info.severity, info.message, info.file, info.line); + +#endif + return level; +} + +#define check_vhpi_error() \ + __check_vhpi_error(__func__, __LINE__) + +static inline int __vhpi_register_cb(p_vhpi_cb user, vhpiCbDataT *cb_data) +{ + /* If the user data already has a callback handle then deregister + * before getting the new one + */ + vhpiHandleT new_hdl = vhpi_register_cb(cb_data, vhpiReturnCb); + int ret = 0; + + if (!new_hdl) { + LOG_CRITICAL("VHPI: Unable to register callback a handle for VHPI type %s(%d)", + vhpi_reason_to_string(cb_data->reason), cb_data->reason); + check_vhpi_error(); + ret = -1; + } + + vhpiStateT cbState = vhpi_get(vhpiStateP, new_hdl); + if (cbState != vhpiEnable) { + LOG_CRITICAL("VHPI ERROR: Registered callback isn't enabled! Got %d\n", cbState); + } + + if (user->cb_hdl != NULL) { + LOG_ERROR("VHPI: Attempt to register a callback that's already registered...\n"); + vhpi_deregister_callback(&user->gpi_cb_data.hdl); + } + + user->cb_hdl = new_hdl; + user->state = VHPI_PRIMED; + + return ret; +} + +// Handle related functions +/** + * @name Find the root handle + * @brief Find the root handle using a optional name + * + * Get a handle to the root simulator object. This is usually the toplevel. + * + * FIXME: In VHPI we always return the first root instance + * + * TODO: Investigate possibility of iterating and checking names as per VHPI + * If no name is defined, we return the first root instance. + * + * If name is provided, we check the name against the available objects until + * we find a match. If no match is found we return NULL + */ +static gpi_sim_hdl vhpi_get_root_handle(const char* name) +{ + FENTER + vhpiHandleT root; + vhpiHandleT dut; + gpi_sim_hdl rv; + + root = vhpi_handle(vhpiRootInst, NULL); + check_vhpi_error(); + + if (!root) { + LOG_ERROR("VHPI: Attempting to get the root handle failed"); + FEXIT + return NULL; + } + + if (name) + dut = vhpi_handle_by_name(name, NULL); + else + dut = vhpi_handle(vhpiDesignUnit, root); + check_vhpi_error(); + + if (!dut) { + LOG_ERROR("VHPI: Attempting to get the DUT handle failed"); + FEXIT + return NULL; + } + + const char *found = vhpi_get_str(vhpiNameP, dut); + check_vhpi_error(); + + if (name != NULL && strcmp(name, found)) { + LOG_WARN("VHPI: Root '%s' doesn't match requested toplevel %s", found, name); + FEXIT + return NULL; + } + + rv = gpi_create_handle(); + rv->sim_hdl = dut; + + FEXIT + return rv; +} + + +static gpi_sim_hdl vhpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) +{ + FENTER + gpi_sim_hdl rv; + vhpiHandleT obj; + int len; + char *buff; + if (name) + len = strlen(name) + 1; + + buff = (char *)malloc(len); + if (buff == NULL) { + LOG_CRITICAL("VHPI: Attempting allocate string buffer failed!"); + return NULL; + } + + strncpy(buff, name, len); + obj = vhpi_handle_by_name(buff, (vhpiHandleT)(parent->sim_hdl)); + if (!obj) { + LOG_DEBUG("VHPI: Handle '%s' not found!", name); +// check_vhpi_error(); + return NULL; + } + + free(buff); + + rv = gpi_create_handle(); + rv->sim_hdl = obj; + + FEXIT + return rv; +} + +/** + * @brief Get a handle for an object based on its index within a parent + * + * @param parent handle to the parent + * @param indext Index to retrieve + * + * Can be used on bit-vectors to access a specific bit or + * memories to access an address + */ +static gpi_sim_hdl vhpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) +{ + FENTER + gpi_sim_hdl rv; + vhpiHandleT obj; + + obj = vhpi_handle_by_index(vhpiParamDecls, (vhpiHandleT)(parent->sim_hdl), index); + if (!obj) { + LOG_ERROR("VHPI: Handle idx '%d' not found!", index); + return NULL; + } + + rv = gpi_create_handle(); + rv->sim_hdl = obj; + + FEXIT + return rv; +} + + +// Functions for iterating over entries of a handle +// Returns an iterator handle which can then be used in gpi_next calls +// NB May return NULL if no objects of the request type exist +static gpi_iterator_hdl vhpi_iterate_hdl(uint32_t type, gpi_sim_hdl base) { + FENTER + + vhpiHandleT iterator; + + iterator = vhpi_iterator(type, (vhpiHandleT)(base->sim_hdl)); + check_vhpi_error(); + + FEXIT + return (gpi_iterator_hdl)iterator; +} + +// Returns NULL when there are no more objects +static gpi_sim_hdl vhpi_next_hdl(gpi_iterator_hdl iterator) +{ + FENTER + gpi_sim_hdl rv = gpi_create_handle(); + + rv->sim_hdl = vhpi_scan((vhpiHandleT) iterator); + check_vhpi_error(); + if (!rv->sim_hdl) { + gpi_free_handle(rv); + rv = NULL; + } + FEXIT + return rv; +} + +static void vhpi_get_sim_time(uint32_t *high, uint32_t *low) +{ + vhpiTimeT vhpi_time_s; + vhpi_get_time(&vhpi_time_s, NULL); + check_vhpi_error(); + *high = vhpi_time_s.high; + *low = vhpi_time_s.low; +} + +// Value related functions +static vhpiEnumT chr2vhpi(const char value) { + switch (value) { + case '0': + return vhpi0; + case '1': + return vhpi1; + case 'U': + case 'u': + return vhpiU; + case 'Z': + case 'z': + return vhpiZ; + case 'X': + case 'x': + return vhpiX; + default: + return vhpiDontCare; + } +} + +// Unfortunately it seems that format conversion is not well supported +// We have to set values using vhpiEnum* +static void vhpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) +{ + FENTER + vhpiValueT value_s; + int size, i; + + // Determine the type of object, either scalar or vector + value_s.format = vhpiObjTypeVal; + value_s.bufSize = 0; + value_s.value.str = NULL; + + vhpi_get_value((vhpiHandleT)(gpi_hdl->sim_hdl), &value_s); + check_vhpi_error(); + + switch (value_s.format) { + case vhpiEnumVal: + case vhpiLogicVal: { + value_s.value.enumv = value ? vhpi1 : vhpi0; + break; + } + + case vhpiEnumVecVal: + case vhpiLogicVecVal: { + size = vhpi_get(vhpiSizeP, (vhpiHandleT)(gpi_hdl->sim_hdl)); + value_s.bufSize = size*sizeof(vhpiEnumT); + value_s.value.enumvs = (vhpiEnumT *)malloc(size*sizeof(vhpiEnumT)); + + for (i=0; isim_hdl), &value_s, vhpiForcePropagate); + check_vhpi_error(); + + if (vhpiEnumVecVal == value_s.format) + free(value_s.value.enumvs); + FEXIT +} + + + +// Unfortunately it seems that format conversion is not well supported +// We have to set values using vhpiEnum* +static void vhpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) +{ + FENTER + vhpiValueT value_s; + int len, size, i; + const char *ptr; + + // Determine the type of object, either scalar or vector + value_s.format = vhpiObjTypeVal; + value_s.bufSize = 0; + value_s.value.str = NULL; + + vhpi_get_value((vhpiHandleT)(gpi_hdl->sim_hdl), &value_s); + check_vhpi_error(); + + switch (value_s.format) { + case vhpiEnumVal: + case vhpiLogicVal: { + value_s.value.enumv = chr2vhpi(*str); + break; + } + + case vhpiEnumVecVal: + case vhpiLogicVecVal: { + len = strlen(str); + size = vhpi_get(vhpiSizeP, (vhpiHandleT)(gpi_hdl->sim_hdl)); + value_s.bufSize = size*sizeof(vhpiEnumT); + value_s.value.enumvs = (vhpiEnumT *)malloc(size*sizeof(vhpiEnumT)); + + // Initialise to 0s + for (i=0; isim_hdl), &value_s, vhpiForcePropagate); + check_vhpi_error(); + if(value_s.format == vhpiEnumVecVal) + free(value_s.value.enumvs); + FEXIT +} + +static char *vhpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) +{ + FENTER + vhpiValueT value_s; + vhpiValueT *value_p = &value_s; + char *result; + size_t size; + value_p->format = vhpiBinStrVal; + + +// FIXME Seem to have a problem here +// According to VHPI spec we should call vhpi_get_value once to determine +// how much memory to allocate for the result... it appears that we just +// get bogus values back so we'll use a fixed size buffer for now +#if 0 + // Call once to find out how long the string is + vhpi_get_value((vhpiHandleT)(gpi_hdl->sim_hdl), value_p); + check_vhpi_error(); + + size = value_p->bufSize; + LOG_ERROR("After initial call to get value: bufSize=%u", size); +#else + size = 512; +#endif + + result = (char *)malloc(size); + if (result == NULL) { + LOG_CRITICAL("VHPI: Attempting allocate string buffer failed!"); + } + + // Call again to get the value + value_p->bufSize = size; + value_p->value.str = result; + vhpi_get_value((vhpiHandleT)(gpi_hdl->sim_hdl), value_p); + check_vhpi_error(); + + FEXIT + return result; +} + +static char *vhpi_get_signal_name_str(gpi_sim_hdl gpi_hdl) +{ + FENTER + const char *name = vhpi_get_str(vhpiFullNameP, (vhpiHandleT)(gpi_hdl->sim_hdl)); + check_vhpi_error(); + char *result = gpi_copy_name(name); + FEXIT + return result; +} + +static char *vhpi_get_signal_type_str(gpi_sim_hdl gpi_hdl) +{ + FENTER + const char *name = vhpi_get_str(vhpiKindStrP, (vhpiHandleT)(gpi_hdl->sim_hdl)); + check_vhpi_error(); + char *result = gpi_copy_name(name); + FEXIT + return result; +} + + +// Callback related functions +static void handle_vhpi_callback(const vhpiCbDataT *cb_data) +{ + FENTER + vhpiHandleT old_cb; + + p_vhpi_cb user_data; + user_data = (p_vhpi_cb)cb_data->user_data; + + if (!user_data) + LOG_CRITICAL("VHPI: Callback data corrupted"); + + user_data->state = VHPI_PRE_CALL; + old_cb = user_data->cb_hdl; + gpi_handle_callback(&user_data->gpi_cb_data.hdl); + + if (old_cb == user_data->cb_hdl) { + + // Don't de-register recurring callbacks - VHPI only seems to allow + // a single registration per recurring callback. For edge events on + // signals etc. we never want to remove. + vhpiStateT cbState = vhpi_get(vhpiStateP, user_data->cb_hdl); + if (vhpiMature == cbState) + gpi_deregister_callback(&user_data->gpi_cb_data.hdl); + } + + /* A request to delete could have been done + * inside gpi_function + */ + if (user_data->state == VHPI_DELETE) + gpi_free_cb_handle(&user_data->gpi_cb_data.hdl); + else + user_data->state = VHPI_POST_CALL; + + FEXIT + return; +}; + +/* Allocates memory that will persist for the lifetime of the + * handle, this may be short or long. A call to create + * must have a matching call to destroy at some point + */ +static gpi_cb_hdl vhpi_create_cb_handle(void) +{ + gpi_cb_hdl ret = NULL; + FENTER + p_vhpi_cb user_data = calloc(1, sizeof(*user_data)); + + if (user_data) + ret = &user_data->gpi_cb_data; + + FEXIT + return ret; +} + +/* Destroys the memory associated with the sim handle + * this can only be called on a handle that has been + * returned by a call to gpi_create_cb_handle + */ +static void vhpi_destroy_cb_handle(gpi_cb_hdl hdl) +{ + /* Check that is has been called, if this has not + * happend then also close down the sim data as well + */ + FENTER + p_vhpi_cb user_data = gpi_container_of(hdl, s_vhpi_cb, gpi_cb_data); + + free(user_data); + FEXIT +} + + +static void *vhpi_get_callback_data(gpi_sim_hdl gpi_hdl) +{ + FENTER + gpi_cb_hdl gpi_user_data; + gpi_user_data = gpi_container_of(gpi_hdl, gpi_cb_hdl_t, hdl); + return gpi_user_data->gpi_cb_data; +} + + +/* Deregister a prior set up callback with the simulator + * The handle must have been allocated with gpi_create_cb_handle + * This can be called at any point between + * gpi_create_cb_handle and gpi_free_cb_handle + */ +static int vhpi_deregister_callback(gpi_sim_hdl gpi_hdl) +{ + FENTER + p_vhpi_cb vhpi_user_data; + gpi_cb_hdl gpi_user_data; + int rc = 1; + + gpi_user_data = gpi_container_of(gpi_hdl, gpi_cb_hdl_t, hdl); + vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); + + if (vhpi_user_data->cb_hdl != NULL) { + rc = vhpi_user_data->vhpi_cleanup(vhpi_user_data); + vhpi_user_data->cb_hdl = NULL; + } + + FEXIT + GPI_RET(rc); +} + +// Call when the handle relates to a one time callback +// No need to call vhpi_deregister_cb as the sim will +// do this but do need to destroy the handle +static int vhpi_free_one_time(p_vhpi_cb user_data) +{ + FENTER + int rc = 0; + vhpiHandleT cb_hdl = user_data->cb_hdl; + if (!cb_hdl) { + LOG_CRITICAL("VHPI: passed a NULL pointer : ABORTING"); + exit(1); + } + + // If the callback has not been called we also need to call + // remove as well + if (user_data->state == VHPI_PRIMED) { + rc = vhpi_remove_cb(cb_hdl); + if (!rc) { + check_vhpi_error(); + return rc; + } + + rc = vhpi_release_handle(cb_hdl); + if (!rc) { + check_vhpi_error(); + return rc; + } + } + FEXIT + return rc; +} + +// Call when the handle relates to recurring callback +// Unregister must be called when not needed and this +// will clean all memory allocated by the sim +static int vhpi_free_recurring(p_vhpi_cb user_data) +{ + FENTER + int rc; + vhpiHandleT cb_hdl = user_data->cb_hdl; + if (!cb_hdl) { + LOG_CRITICAL("VHPI: passed a NULL pointer : ABORTING"); + exit(1); + } + + rc = vhpi_remove_cb(cb_hdl); + check_vhpi_error(); + FEXIT + return rc; +} + +/* These functions request a callback to be active with the current + * handle and associated data. A callback handle needs to have been + * allocated with gpi_create_cb_handle first + */ + +static int vhpi_register_value_change_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data, + gpi_sim_hdl gpi_hdl) +{ + FENTER + + int ret; + vhpiCbDataT cb_data_s; + vhpiTimeT time; + p_vhpi_cb vhpi_user_data; + gpi_cb_hdl gpi_user_data; + + gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); + vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); + + vhpi_user_data->vhpi_cleanup = vhpi_free_recurring; + vhpi_user_data->cb_value.format = vhpiIntVal; + + cb_data_s.reason = vhpiCbValueChange; + cb_data_s.cb_rtn = handle_vhpi_callback; + cb_data_s.obj = (vhpiHandleT)(gpi_hdl->sim_hdl); + cb_data_s.time = &time; + cb_data_s.value = &vhpi_user_data->cb_value; + cb_data_s.user_data = (char *)vhpi_user_data; + + ret = __vhpi_register_cb(vhpi_user_data, &cb_data_s); + + FEXIT + + return ret; +} + + +static int vhpi_register_readonly_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) +{ + FENTER + + int ret; + vhpiCbDataT cb_data_s; + vhpiTimeT time; + p_vhpi_cb vhpi_user_data; + gpi_cb_hdl gpi_user_data; + + gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); + vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); + + vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; + + cb_data_s.reason = vhpiCbLastKnownDeltaCycle; + cb_data_s.cb_rtn = handle_vhpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &time; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)vhpi_user_data; + + ret = __vhpi_register_cb(vhpi_user_data, &cb_data_s); + + FEXIT + + return ret; +} + +static int vhpi_register_readwrite_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) +{ + FENTER + + + int ret; + vhpiCbDataT cb_data_s; + vhpiTimeT time; + p_vhpi_cb vhpi_user_data; + gpi_cb_hdl gpi_user_data; + + gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); + vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); + + vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; + + cb_data_s.reason = vhpiCbEndOfProcesses; + cb_data_s.cb_rtn = handle_vhpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &time; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)vhpi_user_data; + + ret = __vhpi_register_cb(vhpi_user_data, &cb_data_s); + + FEXIT + + return ret; +} + +static int vhpi_register_nexttime_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data) +{ + FENTER + + int ret; + vhpiCbDataT cb_data_s; + vhpiTimeT time; + p_vhpi_cb vhpi_user_data; + gpi_cb_hdl gpi_user_data; + + gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); + vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); + + vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; + + cb_data_s.reason = vhpiCbNextTimeStep; + cb_data_s.cb_rtn = handle_vhpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &time; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)vhpi_user_data; + + ret = __vhpi_register_cb(vhpi_user_data, &cb_data_s); + + FEXIT + + return ret; +} + +static int vhpi_register_timed_callback(gpi_sim_hdl cb, + int (*gpi_function)(void *), + void *gpi_cb_data, + uint64_t time_ps) +{ + FENTER + + int ret; + vhpiCbDataT cb_data_s; + vhpiTimeT time_s; + p_vhpi_cb vhpi_user_data; + gpi_cb_hdl gpi_user_data; + + gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); + vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); + + vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; + + time_s.high = (uint32_t)(time_ps>>32); + time_s.low = (uint32_t)(time_ps); + + cb_data_s.reason = vhpiCbAfterDelay; + cb_data_s.cb_rtn = handle_vhpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &time_s; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)vhpi_user_data; + + ret = __vhpi_register_cb(vhpi_user_data, &cb_data_s); + + FEXIT + + return ret; +} + + +// If the Pything world wants things to shut down then unregister +// the callback for end of sim +static void vhpi_sim_end(void) +{ + FENTER + + sim_finish_cb = NULL; + vhpi_control(vhpiFinish); + check_vhpi_error(); + FEXIT +} + +static s_gpi_impl_tbl vhpi_table = { + .sim_end = vhpi_sim_end, + .iterate_handle = vhpi_iterate_hdl, + .next_handle = vhpi_next_hdl, + .create_cb_handle = vhpi_create_cb_handle, + .destroy_cb_handle = vhpi_destroy_cb_handle, + .deregister_callback = vhpi_deregister_callback, + .get_root_handle = vhpi_get_root_handle, + .get_sim_time = vhpi_get_sim_time, + .get_handle_by_name = vhpi_get_handle_by_name, + .get_handle_by_index = vhpi_get_handle_by_index, + .get_signal_name_str = vhpi_get_signal_name_str, + .get_signal_type_str = vhpi_get_signal_type_str, + .get_signal_value_binstr = vhpi_get_signal_value_binstr, + .set_signal_value_int = vhpi_set_signal_value_int, + .set_signal_value_str = vhpi_set_signal_value_str, + .register_timed_callback = vhpi_register_timed_callback, + .register_readwrite_callback = vhpi_register_readwrite_callback, + .register_nexttime_callback = vhpi_register_nexttime_callback, + .register_value_change_callback = vhpi_register_value_change_callback, + .register_readonly_callback = vhpi_register_readonly_callback, + .get_callback_data = vhpi_get_callback_data, +}; + +static void register_embed(void) +{ + FENTER + gpi_register_impl(&vhpi_table, 0xfeed); + gpi_embed_init_python(); + FEXIT +} + + +static int handle_sim_init(void *gpi_cb_data) +{ + FENTER + gpi_sim_info_t sim_info; + sim_info.argc = 0; + sim_info.argv = NULL; + sim_info.product = gpi_copy_name(vhpi_get_str(vhpiNameP, NULL)); + sim_info.version = gpi_copy_name(vhpi_get_str(vhpiToolVersionP, NULL)); + gpi_embed_init(&sim_info); + + free(sim_info.product); + free(sim_info.version); + + FEXIT + + return 0; +} + +static void register_initial_callback(void) +{ + FENTER + + vhpiCbDataT cb_data_s; + p_vhpi_cb vhpi_user_data; + gpi_cb_hdl gpi_user_data; + + + sim_init_cb = gpi_create_cb_handle(); + + gpi_user_data = gpi_container_of(sim_init_cb, gpi_cb_hdl_t, hdl); + vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); + + gpi_user_data->gpi_cb_data = NULL; + gpi_user_data->gpi_function = handle_sim_init; + + vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; + + cb_data_s.reason = vhpiCbStartOfSimulation; + cb_data_s.cb_rtn = handle_vhpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = NULL; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)vhpi_user_data; + + __vhpi_register_cb(vhpi_user_data, &cb_data_s); + + FEXIT +} + +static int handle_sim_end(void *gpi_cb_data) +{ + FENTER + if (sim_finish_cb) { + sim_finish_cb = NULL; + /* This means that we have been asked to close */ + embed_sim_event(SIM_FAIL, "Simulator shutdown prematurely"); + } /* Other sise we have already been here from the top down so do not need + to inform the upper layers that anything has occoured */ + gpi_free_cb_handle(sim_init_cb); + FEXIT + + return 0; +} + +static void register_final_callback(void) +{ + FENTER + + vhpiCbDataT cb_data_s; + p_vhpi_cb vhpi_user_data; + gpi_cb_hdl gpi_user_data; + + sim_finish_cb = gpi_create_cb_handle(); + + gpi_user_data = gpi_container_of(sim_finish_cb, gpi_cb_hdl_t, hdl); + vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); + + gpi_user_data->gpi_cb_data = NULL; + gpi_user_data->gpi_function = handle_sim_end; + vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; + + cb_data_s.reason = vhpiCbEndOfSimulation; + cb_data_s.cb_rtn = handle_vhpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = NULL; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)vhpi_user_data; + + /* We ignore the return value here as VCS does some silly + * things on comilation that means it tries to run through + * the vlog_startup_routines and so call this routine + */ + __vhpi_register_cb(vhpi_user_data, &cb_data_s); + + FEXIT +} + +// pre-defined VHPI registration table +void (*vhpi_startup_routines[])(void) = { + register_embed, + register_initial_callback, + register_final_callback, + 0 +}; + +// For non-VPI compliant applications that cannot find vlog_startup_routines +void vhpi_startup_routines_bootstrap(void) { + void (*routine)(void); + int i; + routine = vhpi_startup_routines[0]; + for (i = 0, routine = vhpi_startup_routines[i]; + routine; + routine = vhpi_startup_routines[++i]) { + routine(); + } +} diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 51360785..8ac245be 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -38,7 +38,7 @@ endif EXTRA_LIBS = -laldecpli -lstdc++ -lsupc++ EXTRA_LIBDIRS = -L$(ALDEC_PATH)/bin/Linux64 -ALOG_ARGS += +define+COCOTB_SIM -dbg -pli libvpi +ALOG_ARGS += +define+COCOTB_SIM -dbg -pli libgpi ACOM_ARGS += -dbg ifeq ($(COVERAGE),1) From c8418eab5f080e67ab285ab08e75073625328ef7 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 30 Sep 2014 17:48:51 +0100 Subject: [PATCH 0598/2656] Issue#161: Move VPI_ to GPI level --- lib/gpi/gpi_priv.h | 14 ++++++++++- lib/vpi/gpi_vpi.cpp | 61 ++++++++------------------------------------- 2 files changed, 24 insertions(+), 51 deletions(-) diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index d65f16e0..4ea5ca38 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -35,6 +35,14 @@ using namespace std; class gpi_impl_interface; +typedef enum gpi_cb_state_e { + GPI_FREE = 0, + GPI_PRIMED = 1, + GPI_PRE_CALL = 2, + GPI_POST_CALL = 3, + GPI_DELETE = 4, +} gpi_cb_state_t; + class gpi_hdl { public: gpi_hdl(gpi_impl_interface *impl) : m_impl(impl) { } @@ -55,7 +63,8 @@ class gpi_obj_hdl : public gpi_hdl { class gpi_cb_hdl : public gpi_hdl { public: /* Override to change behaviour as needed */ - gpi_cb_hdl(gpi_impl_interface *impl) : gpi_hdl(impl) { } + gpi_cb_hdl(gpi_impl_interface *impl) : gpi_hdl(impl), + m_state(GPI_FREE) { } int handle_callback(void); virtual int arm_callback(void); virtual int run_callback(void); @@ -72,6 +81,9 @@ class gpi_cb_hdl : public gpi_hdl { protected: int (*gpi_function)(void *); // GPI function to callback void *m_cb_data; // GPI data supplied to "gpi_function" + +public: + gpi_cb_state_t m_state; }; class gpi_recurring_cb : public gpi_cb_hdl { diff --git a/lib/vpi/gpi_vpi.cpp b/lib/vpi/gpi_vpi.cpp index b0a78683..f4010b8c 100644 --- a/lib/vpi/gpi_vpi.cpp +++ b/lib/vpi/gpi_vpi.cpp @@ -77,14 +77,6 @@ int __check_vpi_error(const char *func, long line) __check_vpi_error(__func__, __LINE__); \ } while (0) -typedef enum vpi_cb_state_e { - VPI_FREE = 0, - VPI_PRIMED = 1, - VPI_PRE_CALL = 2, - VPI_POST_CALL = 3, - VPI_DELETE = 4, -} vpi_cb_state_t; - class vpi_obj_hdl : public gpi_obj_hdl { public: vpi_obj_hdl(vpiHandle hdl, gpi_impl_interface *impl) : gpi_obj_hdl(impl), @@ -98,12 +90,10 @@ class vpi_obj_hdl : public gpi_obj_hdl { class vpi_cb_hdl : public gpi_cb_hdl { public: vpiHandle vpi_hdl; - vpi_cb_state_t state; public: vpi_cb_hdl(gpi_impl_interface *impl) : gpi_cb_hdl(impl), - vpi_hdl(NULL), - state(VPI_FREE) { } + vpi_hdl(NULL) { } virtual int cleanup_callback(void) { return 0; } virtual ~vpi_cb_hdl() { } @@ -112,7 +102,7 @@ class vpi_cb_hdl : public gpi_cb_hdl { * before getting the new one */ int register_cb(p_cb_data cb_data) { - if (state == VPI_PRIMED) { + if (m_state == GPI_PRIMED) { fprintf(stderr, "Attempt to prime an already primed trigger for %s!\n", vpi_reason_to_string(cb_data->reason)); @@ -137,7 +127,7 @@ class vpi_cb_hdl : public gpi_cb_hdl { } vpi_hdl = new_hdl; - state = VPI_PRIMED; + m_state = GPI_PRIMED; return ret; } @@ -180,19 +170,9 @@ class vpi_onetime_cb : public vpi_cb_hdl { exit(1); } - // HACK: Calling vpi_free_object after vpi_remove_cb causes Modelsim - // to VPIEndOfSimulationCallback -#if 0 - rc = vpi_free_object(vpi_hdl); - if (!rc) { - check_vpi_error(); - return rc; - } -#endif - // If the callback has not been called we also need to call // remove as well - if (state == VPI_PRIMED) { + if (m_state == GPI_PRIMED) { rc = vpi_remove_cb(vpi_hdl); if (!rc) { @@ -201,10 +181,8 @@ class vpi_onetime_cb : public vpi_cb_hdl { } } - - vpi_hdl = NULL; - state = VPI_FREE; + m_state = GPI_FREE; return rc; } virtual ~vpi_onetime_cb() { } @@ -226,7 +204,7 @@ class vpi_recurring_cb : public vpi_cb_hdl { check_vpi_error(); vpi_hdl = NULL; - state = VPI_FREE; + m_state = GPI_FREE; FEXIT return rc; @@ -794,7 +772,7 @@ gpi_cb_hdl *vpi_impl::register_nexttime_callback(void) int vpi_impl::deregister_callback(gpi_cb_hdl *gpi_hdl) { vpi_cb_hdl *vpi_obj = reinterpret_cast(gpi_hdl); - if (vpi_obj->state == VPI_PRE_CALL) { + if (vpi_obj->m_state == GPI_PRE_CALL) { //LOG_INFO("Not deleting yet %p", vpi_obj); return 0; } @@ -812,7 +790,7 @@ void vpi_impl::sim_end(void) /* Some sims do not seem to be able to deregister the end of sim callback * so we need to make sure we have tracked this and not call the handler */ - sim_finish_cb->state = VPI_DELETE; + sim_finish_cb->m_state = GPI_DELETE; vpi_control(vpiFinish); check_vpi_error(); } @@ -833,31 +811,14 @@ int32_t handle_vpi_callback(p_cb_data cb_data) LOG_DEBUG("Running %p", cb_hdl); - if (cb_hdl->state == VPI_PRIMED) { - cb_hdl->state = VPI_PRE_CALL; + if (cb_hdl->m_state == GPI_PRIMED) { + cb_hdl->m_state = GPI_PRE_CALL; cb_hdl->run_callback(); - cb_hdl->state = VPI_POST_CALL; + cb_hdl->m_state = GPI_POST_CALL; } gpi_deregister_callback(cb_hdl); -#if 0 -// HACK: Investigate further - this breaks modelsim -#if 0 - if (old_cb == user_data->cb_hdl) - gpi_deregister_callback(&user_data->gpi_hdl); -#endif - - /* A request to delete could have been done - * inside gpi_function - */ - if (user_data->state == VPI_DELETE) - gpi_free_cb_handle(&user_data->gpi_cb_data.hdl); - else - user_data->state = VPI_POST_CALL; -#endif - //cb_hdl->set_state(VPI_POST_CALL); - FEXIT return rv; }; From 103c6e495719edd479e4723bf15e36c4d0089eea Mon Sep 17 00:00:00 2001 From: Chiggs Date: Thu, 2 Oct 2014 20:05:41 +0100 Subject: [PATCH 0599/2656] Issue #161: Add VHDL toplevel for mixed language sims --- examples/mixed_language/hdl/toplevel.vhdl | 74 +++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 examples/mixed_language/hdl/toplevel.vhdl diff --git a/examples/mixed_language/hdl/toplevel.vhdl b/examples/mixed_language/hdl/toplevel.vhdl new file mode 100644 index 00000000..56d324b4 --- /dev/null +++ b/examples/mixed_language/hdl/toplevel.vhdl @@ -0,0 +1,74 @@ +-- Example using mixed-language simulation +-- +-- Here we have a VHDL toplevel that instantiates both SV and VHDL +-- sub entities +library ieee; + +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity endian_swapper_mixed is + generic ( + DATA_BYTES : integer := 8); + port ( + clk : in std_ulogic; + reset_n : in std_ulogic; + + stream_in_data : in std_ulogic_vector(DATA_BYTES*8-1 downto 0); + stream_in_empty : in std_ulogic_vector(2 downto 0); + stream_in_valid : in std_ulogic; + stream_in_startofpacket : in std_ulogic; + stream_in_endofpacket : in std_ulogic; + stream_in_ready : out std_ulogic; + + stream_out_data : out std_ulogic_vector(DATA_BYTES*8-1 downto 0); + stream_out_empty : out std_ulogic_vector(2 downto 0); + stream_out_valid : out std_ulogic; + stream_out_startofpacket: out std_ulogic; + stream_out_endofpacket : out std_ulogic; + stream_out_ready : in std_ulogic; + + csr_address : in std_ulogic_vector(1 downto 0); + csr_readdata : out std_ulogic_vector(31 downto 0); + csr_readdatavalid : out std_ulogic; + csr_read : in std_ulogic; + csr_write : in std_ulogic; + csr_waitrequest : out std_ulogic; + csr_writedata : in std_ulogic_vector(31 downto 0) + ); +end; + +architecture impl of endian_swapper_mixed is begin + +i_swapper_sv : entity work.endian_swapper_sv + generic map ( + DATA_BYTES => DATA_BYTES + ) port map ( + clk => clk, + reset_n => reset_n, + + stream_in_empty => stream_in_empty, + stream_in_data => stream_in_data, + stream_in_endofpacket => stream_in_endofpacket, + stream_in_startofpacket => stream_in_startofpacket, + stream_in_valid => stream_in_valid, + stream_in_ready => stream_in_ready, + + stream_out_empty => stream_out_empty, + stream_out_data => stream_out_data, + stream_out_endofpacket => stream_out_endofpacket, + stream_out_startofpacket=> stream_out_startofpacket, + stream_out_valid => stream_out_valid, + stream_out_ready => stream_out_ready, + + csr_address => csr_address, + csr_readdata => csr_readdata, + csr_readdatavalid => csr_readdatavalid, + csr_read => csr_read, + csr_write => csr_write, + csr_waitrequest => csr_waitrequest, + csr_writedata => csr_writedata + ); + +end architecture; + From a63be4510d397b9350aa9e9665a91b763dd7c3cb Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 5 Oct 2014 20:54:03 +0100 Subject: [PATCH 0600/2656] Issue#161: Update GPI headers and common code - not working state --- include/gpi.h | 13 ++- lib/gpi/gpi_common.cpp | 152 +++++++++++++++--------------- lib/gpi/gpi_priv.h | 204 ++++++++++++++++++++++++----------------- 3 files changed, 206 insertions(+), 163 deletions(-) diff --git a/include/gpi.h b/include/gpi.h index 485b480c..414d7926 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -148,9 +148,9 @@ gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator); // Caller responsible for freeing the returned string. // This is all slightly verbose but it saves having to enumerate various value types // We only care about a limited subset of values. -char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl); -char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl); -char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl); +const char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl); +const char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl); +const char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl); @@ -158,9 +158,14 @@ char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl); void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value); void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str); // String of binary char(s) [1, 0, x, z] +typedef enum gpi_edge { + GPI_RISING = 1, + GPI_FALLING = 2, +} gpi_edge_e; + // The callback registering functions gpi_sim_hdl gpi_register_timed_callback (int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps); -gpi_sim_hdl gpi_register_value_change_callback (int (*gpi_function)(void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl); +gpi_sim_hdl gpi_register_value_change_callback (int (*gpi_function)(void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl, int edge); gpi_sim_hdl gpi_register_readonly_callback (int (*gpi_function)(void *), void *gpi_cb_data); gpi_sim_hdl gpi_register_nexttime_callback (int (*gpi_function)(void *), void *gpi_cb_data); gpi_sim_hdl gpi_register_readwrite_callback (int (*gpi_function)(void *), void *gpi_cb_data); diff --git a/lib/gpi/gpi_common.cpp b/lib/gpi/gpi_common.cpp index 7b27ce73..906147e8 100644 --- a/lib/gpi/gpi_common.cpp +++ b/lib/gpi/gpi_common.cpp @@ -32,42 +32,21 @@ using namespace std; -static inline gpi_cb_hdl * sim_to_cbhdl(gpi_sim_hdl hdl, bool fatal) +template +inline To sim_to_hdl(Ti input) { - gpi_cb_hdl *cb_hdl = reinterpret_cast(hdl); - if (!cb_hdl) { + To result = reinterpret_cast(input); + if (!result) { LOG_CRITICAL("GPI: Handle passed down is not valid gpi_sim_hdl"); - if (fatal) - exit(1); + exit(1); } - return cb_hdl; -} - -static inline gpi_obj_hdl * sim_to_objhdl(gpi_sim_hdl hdl, bool fatal) -{ - gpi_obj_hdl *obj_hdl = reinterpret_cast(hdl); - if (!obj_hdl) { - LOG_CRITICAL("GPI: Handle passed down is not valid gpi_onj_hdl"); - if (fatal) - exit(1); - } - return obj_hdl; -} -static inline gpi_iterator * sim_to_iterhdl(gpi_sim_hdl hdl, bool fatal) -{ - gpi_iterator *iter_hdl = reinterpret_cast(hdl); - if (!iter_hdl) { - LOG_CRITICAL("GPI: Handle passed down is not valid gpi_iterator"); - if (fatal) - exit(1); - } - return iter_hdl; + return result; } -static vector registered_impls; +static vector registered_impls; -int gpi_register_impl(gpi_impl_interface *func_tbl) +int gpi_register_impl(GpiImplInterface *func_tbl) { registered_impls.push_back(func_tbl); return 0; @@ -102,9 +81,9 @@ gpi_sim_hdl gpi_get_root_handle(const char *name) { /* May need to look over all the implementations that are registered to find this handle */ - vector::iterator iter; + vector::iterator iter; - gpi_obj_hdl *hdl; + GpiObjHdl *hdl; for (iter = registered_impls.begin(); iter != registered_impls.end(); @@ -118,20 +97,22 @@ gpi_sim_hdl gpi_get_root_handle(const char *name) gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) { - vector::iterator iter; +#if 0 + vector::iterator iter; + + GpiObjHdl *hdl; + GpiObjHdl *base = sim_to_hdl(parent); - gpi_obj_hdl *hdl; - gpi_obj_hdl *base = sim_to_objhdl(parent, true); /* Either want this or use the parent */ for (iter = registered_impls.begin(); iter != registered_impls.end(); iter++) { + LOG_WARN("Quering impl %s", (*iter)->get_name()); if ((hdl = (*iter)->get_handle_by_name(name, base))) { return (void*)hdl; } } -#if 0 hdl = base->m_impl->get_handle_by_name(name, base); return (void*)hdl; #endif @@ -140,70 +121,74 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) { +#if 0 /* Either want this or use the parent */ - gpi_obj_hdl *obj_hdl = sim_to_objhdl(parent, false); + GpiObjHdl *obj_hdl = sim_to_hdl(parent); return (void*)obj_hdl->m_impl->get_handle_by_index(obj_hdl, index); +#endif + return NULL; } gpi_iterator_hdl gpi_iterate(uint32_t type, gpi_sim_hdl base) { - gpi_obj_hdl *obj_hdl = sim_to_objhdl(base, false); - gpi_iterator *iter = obj_hdl->m_impl->iterate_handle(type, obj_hdl); +#if 0 + GpiObjHdl *obj_hdl = sim_to_hdl(base); + GpiIterator *iter = obj_hdl->m_impl->iterate_handle(type, obj_hdl); if (!iter) { return NULL; } iter->parent = obj_hdl; - return (void*)iter; + return (gpi_iterator_hdl)iter; +#endif + return NULL; } gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) { - gpi_iterator *iter = sim_to_iterhdl(iterator, false); - return (void*)iter->parent->m_impl->next_handle(iter); + GpiIterator *iter = sim_to_hdl(iterator); + return (gpi_sim_hdl)iter->parent->next_handle(iter); } -char *gpi_get_signal_value_binstr(gpi_sim_hdl sig_hdl) +const char *gpi_get_signal_value_binstr(gpi_sim_hdl sig_hdl) { - gpi_obj_hdl *obj_hdl = sim_to_objhdl(sig_hdl, false); - return obj_hdl->m_impl->get_signal_value_binstr(obj_hdl); + GpiSignalObjHdl *obj_hdl = sim_to_hdl(sig_hdl); + return obj_hdl->get_signal_value_binstr(); } -char *gpi_get_signal_name_str(gpi_sim_hdl sig_hdl) +const char *gpi_get_signal_name_str(gpi_sim_hdl sig_hdl) { - gpi_obj_hdl *obj_hdl = sim_to_objhdl(sig_hdl, false); - return obj_hdl->m_impl->get_signal_name_str(obj_hdl); + GpiSignalObjHdl *obj_hdl = sim_to_hdl(sig_hdl); + return obj_hdl->get_name_str(); } -char *gpi_get_signal_type_str(gpi_sim_hdl sig_hdl) +const char *gpi_get_signal_type_str(gpi_sim_hdl sig_hdl) { - gpi_obj_hdl *obj_hdl = sim_to_objhdl(sig_hdl, false); - return obj_hdl->m_impl->get_signal_type_str(obj_hdl); + GpiObjHdl *obj_hdl = sim_to_hdl(sig_hdl); + return obj_hdl->get_type_str(); } void gpi_set_signal_value_int(gpi_sim_hdl sig_hdl, int value) { - gpi_obj_hdl *obj_hdl = sim_to_objhdl(sig_hdl, false); - obj_hdl->m_impl->set_signal_value_int(obj_hdl, value); + GpiSignalObjHdl *obj_hdl = sim_to_hdl(sig_hdl); + obj_hdl->set_signal_value(value); } void gpi_set_signal_value_str(gpi_sim_hdl sig_hdl, const char *str) { - gpi_obj_hdl *obj_hdl = sim_to_objhdl(sig_hdl, false); - obj_hdl->m_impl->set_signal_value_str(obj_hdl, str); -} - -void *gpi_get_callback_data(gpi_sim_hdl sim_hdl) -{ - gpi_cb_hdl *cb_hdl = sim_to_cbhdl(sim_hdl, false); - return cb_hdl->get_user_data(); + GpiSignalObjHdl *obj_hdl = sim_to_hdl(sig_hdl); + obj_hdl->set_signal_value(str); } gpi_sim_hdl gpi_register_value_change_callback(int (*gpi_function)(void *), void *gpi_cb_data, - gpi_sim_hdl sig_hdl) + gpi_sim_hdl sig_hdl, + int rising) { - gpi_obj_hdl *obj_hdl = sim_to_objhdl(sig_hdl, false); - gpi_cb_hdl *gpi_hdl = obj_hdl->m_impl->register_value_change_callback(obj_hdl); + #if 0 + GpiObjHdl *obj_hdl = sim_to_hdl(sig_hdl); + + /* Do something based on int & GPI_RISING | GPI_FALLING */ + GpiCbHdl *gpi_hdl = obj_hdl->value_change_cb(); if (!gpi_hdl) { LOG_ERROR("Failed to register a value change callback"); return NULL; @@ -211,15 +196,16 @@ gpi_sim_hdl gpi_register_value_change_callback(int (*gpi_function)(void *), gpi_hdl->set_user_data(gpi_function, gpi_cb_data); return (gpi_sim_hdl)gpi_hdl; + #endif + return NULL; } /* It should not matter which implementation we use for this so just pick the first - one -*/ + one */ gpi_sim_hdl gpi_register_timed_callback(int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps) { - gpi_cb_hdl *gpi_hdl = registered_impls[0]->register_timed_callback(time_ps); + GpiCbHdl *gpi_hdl = registered_impls[0]->register_timed_callback(time_ps); if (!gpi_hdl) { LOG_ERROR("Failed to register a timed callback"); return NULL; @@ -235,7 +221,7 @@ gpi_sim_hdl gpi_register_timed_callback(int (*gpi_function)(void *), gpi_sim_hdl gpi_register_readonly_callback(int (*gpi_function)(void *), void *gpi_cb_data) { - gpi_cb_hdl *gpi_hdl = registered_impls[0]->register_readonly_callback(); + GpiCbHdl *gpi_hdl = registered_impls[0]->register_readonly_callback(); if (!gpi_hdl) { LOG_ERROR("Failed to register a readonly callback"); return NULL; @@ -248,7 +234,7 @@ gpi_sim_hdl gpi_register_readonly_callback(int (*gpi_function)(void *), gpi_sim_hdl gpi_register_nexttime_callback(int (*gpi_function)(void *), void *gpi_cb_data) { - gpi_cb_hdl *gpi_hdl = registered_impls[0]->register_nexttime_callback(); + GpiCbHdl *gpi_hdl = registered_impls[0]->register_nexttime_callback(); if (!gpi_hdl) { LOG_ERROR("Failed to register a nexttime callback"); return NULL; @@ -264,7 +250,7 @@ gpi_sim_hdl gpi_register_nexttime_callback(int (*gpi_function)(void *), gpi_sim_hdl gpi_register_readwrite_callback(int (*gpi_function)(void *), void *gpi_cb_data) { - gpi_cb_hdl *gpi_hdl = registered_impls[0] ->register_readwrite_callback(); + GpiCbHdl *gpi_hdl = registered_impls[0] ->register_readwrite_callback(); if (!gpi_hdl) { LOG_ERROR("Failed to register a readwrite callback"); return NULL; @@ -274,18 +260,38 @@ gpi_sim_hdl gpi_register_readwrite_callback(int (*gpi_function)(void *), return (gpi_sim_hdl)gpi_hdl; } +gpi_sim_hdl gpi_create_clock(gpi_sim_hdl *clk_signal, const int period) +{ + GpiObjHdl *clk_hdl = sim_to_hdl(clk_signal); + GpiClockHdl *clock = new GpiClockHdl(clk_hdl); + clock->start_clock(period); + return (gpi_sim_hdl)clock; +} + +void gpi_stop_clock(gpi_sim_hdl *clk_object) +{ + GpiClockHdl *clock = sim_to_hdl(clk_object); + clock->stop_clock(); + delete(clock); +} void gpi_deregister_callback(gpi_sim_hdl hdl) { - gpi_cb_hdl *cb_hdl = sim_to_cbhdl(hdl, false); + GpiCbHdl *cb_hdl = sim_to_hdl(hdl); cb_hdl->m_impl->deregister_callback(cb_hdl); } void gpi_free_handle(gpi_sim_hdl hdl) { - gpi_obj_hdl *obj = sim_to_objhdl(hdl, true); + GpiObjHdl *obj = sim_to_hdl(hdl); delete(obj); } -gpi_impl_interface::~gpi_impl_interface() { } -gpi_impl_interface::gpi_impl_interface(const string& name) : m_name(name) { } \ No newline at end of file +GpiImplInterface::~GpiImplInterface() { } +GpiImplInterface::GpiImplInterface(const std::string& name) : m_name(name) { } +const char* GpiImplInterface::get_name_c(void) { + return m_name.c_str(); +} +const string& GpiImplInterface::get_name_s(void) { + return m_name; +} \ No newline at end of file diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 4ea5ca38..3faabb65 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -1,6 +1,5 @@ /****************************************************************************** * Copyright (c) 2013 Potential Ventures Ltd -* Copyright (c) 2013 SolarFlare Communications Inc * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,141 +30,174 @@ #include #include -using namespace std; - -class gpi_impl_interface; - -typedef enum gpi_cb_state_e { +typedef enum gpi_cb_state { GPI_FREE = 0, GPI_PRIMED = 1, GPI_PRE_CALL = 2, GPI_POST_CALL = 3, GPI_DELETE = 4, -} gpi_cb_state_t; +} gpi_cb_state_e; + +class GpiCbHdl; +class GpiImplInterface; +class GpiIterator; +class GpiCbHdl; -class gpi_hdl { +/* Base GPI class others are derived from */ +class GpiHdl { public: - gpi_hdl(gpi_impl_interface *impl) : m_impl(impl) { } - virtual ~gpi_hdl() { } + GpiHdl(GpiImplInterface *impl) : m_impl(impl) { } + virtual ~GpiHdl() { } + virtual int Initialise(void); // Post constructor init + +private: + GpiHdl() { } // Disable default constructor public: - gpi_impl_interface *m_impl; // Implementation routines + GpiImplInterface *m_impl; // VPI/VHPI/FLI routines + const char *gpi_copy_name(const char *name); // Might not be needed }; -class gpi_obj_hdl : public gpi_hdl { +/* GPI object handle, maps to a simulation object */ +// An object is any item in the hierarchy +// Provides methods for iterating through children or finding by name +// Initial object is returned by call to GpiImplInterface::get_root_handle() +// Susequent operations to get children go through this handle. +// GpiObjHdl::get_handle_by_name/get_handle_by_index are really factories +// that construct an object derived from GpiSignalObjHdl or GpiObjHdl +class GpiObjHdl : public GpiHdl { public: - gpi_obj_hdl(gpi_impl_interface *impl) : gpi_hdl(impl) { } - char *gpi_copy_name(const char *name); + GpiObjHdl(GpiImplInterface *impl) : GpiHdl(impl) { } + virtual ~GpiObjHdl() { } - virtual ~gpi_obj_hdl() { } + // The following methods permit children below this level of the hierarchy + // to be discovered and instantiated + virtual GpiObjHdl *get_handle_by_name(const char *name) = 0; + virtual GpiObjHdl *get_handle_by_index(uint32_t index) = 0; + virtual GpiIterator *iterate_handle(uint32_t type) = 0; + virtual GpiObjHdl *next_handle(GpiIterator *iterator) = 0; + + virtual const char* get_name_str(void) = 0; + virtual const char* get_type_str(void) = 0; + + std::string m_name; + std::string m_type; }; -class gpi_cb_hdl : public gpi_hdl { + +/* GPI Signal object handle, maps to a simulation object */ +// +// Identical to an object but adds additional methods for getting/setting the +// value of the signal (which doesn't apply to non signal items in the hierarchy +class GpiSignalObjHdl : public GpiObjHdl { public: - /* Override to change behaviour as needed */ - gpi_cb_hdl(gpi_impl_interface *impl) : gpi_hdl(impl), - m_state(GPI_FREE) { } - int handle_callback(void); - virtual int arm_callback(void); - virtual int run_callback(void); - virtual int cleanup_callback(void) = 0; + GpiSignalObjHdl(GpiImplInterface *impl) : GpiObjHdl(impl) { } + virtual ~GpiSignalObjHdl() { } + // Provide public access to the implementation (composition vs inheritance) + virtual const char* get_signal_value_binstr(void) = 0; + + int m_length; - int set_user_data(int (*gpi_function)(void*), void *data); - void *get_user_data(void); + virtual int set_signal_value(const int value) = 0; + virtual int set_signal_value(const char *str) = 0; + //virtual GpiCbHdl monitor_value(bool rising_edge) = 0; this was for the triggers + // but the explicit ones are probably better - virtual ~gpi_cb_hdl() { } + // Also think we want the triggers here? + virtual GpiCbHdl *rising_edge_cb(void) = 0; + virtual GpiCbHdl *falling_edge_cb(void) = 0; + virtual GpiCbHdl *value_change_cb(void) = 0; private: - gpi_cb_hdl(); + //GpiCbHdl value_change_cb; +}; -protected: - int (*gpi_function)(void *); // GPI function to callback - void *m_cb_data; // GPI data supplied to "gpi_function" +/* GPI Callback handle */ +// To set a callback it needs the signal to do this on, +// vpiHandle/vhpiHandleT for instance. The +class GpiCbHdl : public GpiHdl { public: - gpi_cb_state_t m_state; -}; + GpiCbHdl(GpiImplInterface *impl) : GpiHdl(impl), + m_state(GPI_FREE) { } + // Pure virtual functions for derived classes + virtual int arm_callback(void) = 0; // Register with siumlator + virtual int run_callback(void) = 0; // Entry point from simulator + virtual int cleanup_callback(void) = 0; // Cleanup the callback, arm can be called after -class gpi_recurring_cb : public gpi_cb_hdl { -public: - int cleanup_callback(void); -}; + // Set the data to be used for run callback, seperate to arm_callback so data can be re-used + int set_user_data(int (*gpi_function)(void*), const void *data); + const void *get_user_data(void); -class gpi_onetime_cb : public gpi_cb_hdl { -public: - int cleanup_callback(void); -}; + void set_call_state(gpi_cb_state_e new_state); + gpi_cb_state_e get_call_state(void); -class gpi_cb_timed : public gpi_onetime_cb { -public: - int run_callback(void); + virtual ~GpiCbHdl() { } + +protected: + int (*gpi_function)(void *); // GPI function to callback + void *m_cb_data; // GPI data supplied to "gpi_function" + gpi_cb_state_e m_state; // GPI state of the callback through its cycle }; -class gpi_cb_value_change : public gpi_recurring_cb { +/* We would then have */ +class GpiClockHdl { public: - int run_callback(void); + GpiClockHdl(GpiObjHdl *clk) { } + GpiClockHdl(const char *clk) { } + ~GpiClockHdl() { } + int start_clock(const int period_ps); /* Do things with the GpiSignalObjHdl */ + int stop_clock(void); }; -class gpi_cb_readonly_phase : public gpi_onetime_cb { -public: - int run_callback(void); +class GpiNextTime : public GpiCbHdl { + }; -class gpi_cb_nexttime_phase : public gpi_onetime_cb { -public: - int run_callback(void); +class GpiTimer : public GpiCbHdl { + }; -class gpi_cb_readwrite_phase : public gpi_onetime_cb { -public: - int run_callback(void); +class GpiReadOnly : public GpiCbHdl { + }; -class gpi_iterator { -public: - gpi_obj_hdl *parent; +class GpiReadWrite : public GpiCbHdl { + }; -class gpi_impl_interface { +class GpiIterator { public: - string m_name; + GpiObjHdl *parent; +}; +class GpiImplInterface { public: - gpi_impl_interface(const string& name); - virtual ~gpi_impl_interface() = 0; + GpiImplInterface(const std::string& name); + const char *get_name_c(void); + const std::string& get_name_s(void); + virtual ~GpiImplInterface() = 0; /* Sim related */ virtual void sim_end(void) = 0; virtual void get_sim_time(uint32_t *high, uint32_t *low) = 0; - /* Signal related */ - virtual gpi_obj_hdl *get_root_handle(const char *name) = 0; - virtual gpi_obj_hdl *get_handle_by_name(const char *name, gpi_obj_hdl *parent) = 0; - virtual gpi_obj_hdl *get_handle_by_index(gpi_obj_hdl *parent, uint32_t index) = 0; - virtual void free_handle(gpi_obj_hdl*) = 0; - virtual gpi_iterator *iterate_handle(uint32_t type, gpi_obj_hdl *base) = 0; - virtual gpi_obj_hdl *next_handle(gpi_iterator *iterator) = 0; - virtual char* get_signal_value_binstr(gpi_obj_hdl *gpi_hdl) = 0; - virtual char* get_signal_name_str(gpi_obj_hdl *gpi_hdl) = 0; - virtual char* get_signal_type_str(gpi_obj_hdl *gpi_hdl) = 0; - virtual void set_signal_value_int(gpi_obj_hdl *gpi_hdl, int value) = 0; - virtual void set_signal_value_str(gpi_obj_hdl *gpi_hdl, const char *str) = 0; // String of binary char(s) [1, 0, x, z] - - /* Callback related */ - virtual gpi_cb_hdl *register_timed_callback(uint64_t time_ps) = 0; - virtual gpi_cb_hdl *register_value_change_callback(gpi_obj_hdl *obj_hdl) = 0; - virtual gpi_cb_hdl *register_readonly_callback(void) = 0; - virtual gpi_cb_hdl *register_nexttime_callback(void) = 0; - virtual gpi_cb_hdl *register_readwrite_callback(void) = 0; - virtual int deregister_callback(gpi_cb_hdl *gpi_hdl) = 0; - - virtual gpi_cb_hdl *create_cb_handle(void) = 0; - virtual void destroy_cb_handle(gpi_cb_hdl *gpi_hdl) = 0; - //virtual void* get_callback_data(gpi_sim_hdl gpi_hdl) = 0; + /* Hierachy related */ + virtual GpiObjHdl *get_root_handle(const char *name) = 0; + + /* Callback related, these may (will) return the same handle*/ + virtual GpiCbHdl *register_timed_callback(uint64_t time_ps) = 0; + virtual GpiCbHdl *register_readonly_callback(void) = 0; + virtual GpiCbHdl *register_nexttime_callback(void) = 0; + virtual GpiCbHdl *register_readwrite_callback(void) = 0; + virtual int deregister_callback(GpiCbHdl *obj_hdl) = 0; + +private: + std::string m_name; }; /* Called from implementaton layers back up the stack */ -int gpi_register_impl(gpi_impl_interface *func_tbl); +int gpi_register_impl(GpiImplInterface *func_tbl); void gpi_embed_init(gpi_sim_info_t *info); void gpi_embed_end(void); From 11091111c3744d0e5e9d24df26500b9dc33f753b Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 5 Oct 2014 21:53:17 +0100 Subject: [PATCH 0601/2656] Issue#161: Update some pointers to const for functions and data that is callback related --- include/gpi.h | 10 ++-- lib/gpi/{gpi_cb_hdl.cpp => GpiCbHdl.cpp} | 69 ++++++++--------------- lib/gpi/{gpi_common.cpp => GpiCommon.cpp} | 10 ++-- lib/gpi/Makefile | 2 +- lib/gpi/gpi_priv.h | 10 ++-- 5 files changed, 39 insertions(+), 62 deletions(-) rename lib/gpi/{gpi_cb_hdl.cpp => GpiCbHdl.cpp} (71%) rename lib/gpi/{gpi_common.cpp => GpiCommon.cpp} (95%) diff --git a/include/gpi.h b/include/gpi.h index 414d7926..e96d1331 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -164,11 +164,11 @@ typedef enum gpi_edge { } gpi_edge_e; // The callback registering functions -gpi_sim_hdl gpi_register_timed_callback (int (*gpi_function)(void *), void *gpi_cb_data, uint64_t time_ps); -gpi_sim_hdl gpi_register_value_change_callback (int (*gpi_function)(void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl, int edge); -gpi_sim_hdl gpi_register_readonly_callback (int (*gpi_function)(void *), void *gpi_cb_data); -gpi_sim_hdl gpi_register_nexttime_callback (int (*gpi_function)(void *), void *gpi_cb_data); -gpi_sim_hdl gpi_register_readwrite_callback (int (*gpi_function)(void *), void *gpi_cb_data); +gpi_sim_hdl gpi_register_timed_callback (const int (*gpi_function)(const void *), void *gpi_cb_data, uint64_t time_ps); +gpi_sim_hdl gpi_register_value_change_callback (const int (*gpi_function)(const void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl, int edge); +gpi_sim_hdl gpi_register_readonly_callback (const int (*gpi_function)(const void *), void *gpi_cb_data); +gpi_sim_hdl gpi_register_nexttime_callback (const int (*gpi_function)(const void *), void *gpi_cb_data); +gpi_sim_hdl gpi_register_readwrite_callback (const int (*gpi_function)(const void *), void *gpi_cb_data); // Calling convention is that 0 = success and negative numbers a failure // For implementers of GPI the provided macro GPI_RET(x) is provided diff --git a/lib/gpi/gpi_cb_hdl.cpp b/lib/gpi/GpiCbHdl.cpp similarity index 71% rename from lib/gpi/gpi_cb_hdl.cpp rename to lib/gpi/GpiCbHdl.cpp index c750158b..aeaedd0c 100644 --- a/lib/gpi/gpi_cb_hdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -1,6 +1,5 @@ /****************************************************************************** * Copyright (c) 2013 Potential Ventures Ltd -* Copyright (c) 2013 SolarFlare Communications Inc * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,12 +27,9 @@ ******************************************************************************/ #include "gpi_priv.h" -#include - -using namespace std; /* Genertic base clss implementations */ -char *gpi_obj_hdl::gpi_copy_name(const char *name) +const char *GpiHdl::gpi_copy_name(const char *name) { int len; char *result; @@ -59,22 +55,32 @@ char *gpi_obj_hdl::gpi_copy_name(const char *name) return result; } -int gpi_cb_hdl::handle_callback(void) +#if 0 +int GpiCbHdl::handle_callback(void) { return this->gpi_function(m_cb_data); } +#endif -int gpi_cb_hdl::arm_callback(void) +int GpiCbHdl::run_callback(void) { + LOG_WARN("Generic run_callback"); + return this->gpi_function(m_cb_data); +} + +int GpiCbHdl::cleanup_callback(void) +{ + LOG_WARN("Generic cleanu_handler"); return 0; } -int gpi_cb_hdl::run_callback(void) +int GpiCbHdl::arm_callback(void) { - return this->gpi_function(m_cb_data); + LOG_WARN("Generic arm_callback"); + return 0; } -int gpi_cb_hdl::set_user_data(int (*gpi_function)(void*), void *data) +int GpiCbHdl::set_user_data(const int (*gpi_function)(const void*), const void *data) { if (!gpi_function) { LOG_ERROR("gpi_function to set_user_data is NULL"); @@ -84,51 +90,22 @@ int gpi_cb_hdl::set_user_data(int (*gpi_function)(void*), void *data) return 0; } -void * gpi_cb_hdl::get_user_data(void) +const void * GpiCbHdl::get_user_data(void) { return m_cb_data; } -int gpi_cb_hdl::cleanup_callback(void) +void GpiCbHdl::set_call_state(gpi_cb_state_e new_state) { - LOG_WARN("Generic cleanup handler"); - return 0; + m_state = new_state; } -/* Specific callback types */ - -int gpi_recurring_cb::cleanup_callback(void) +gpi_cb_state_e GpiCbHdl::get_call_state(void) { - LOG_ERROR("Need to override"); - return 0; -} - -int gpi_onetime_cb::cleanup_callback(void) -{ - LOG_ERROR("Need to override"); - return 0; + return m_state; } -int gpi_cb_value_change::run_callback(void) +GpiCbHdl::~GpiCbHdl(void) { - LOG_ERROR("Need to override"); - return 0; -} - -int gpi_cb_readonly_phase::run_callback(void) -{ - LOG_ERROR("Need to override"); - return 0; -} - -int gpi_cb_nexttime_phase::run_callback(void) -{ - LOG_ERROR("Need to override"); - return 0; -} - -int gpi_cb_readwrite_phase::run_callback(void) -{ - LOG_ERROR("Need to override"); - return 0; + LOG_WARN("In GpiCbHdl Destructor"); } \ No newline at end of file diff --git a/lib/gpi/gpi_common.cpp b/lib/gpi/GpiCommon.cpp similarity index 95% rename from lib/gpi/gpi_common.cpp rename to lib/gpi/GpiCommon.cpp index 906147e8..25c31588 100644 --- a/lib/gpi/gpi_common.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -179,7 +179,7 @@ void gpi_set_signal_value_str(gpi_sim_hdl sig_hdl, const char *str) obj_hdl->set_signal_value(str); } -gpi_sim_hdl gpi_register_value_change_callback(int (*gpi_function)(void *), +gpi_sim_hdl gpi_register_value_change_callback(const int (*gpi_function)(const void *), void *gpi_cb_data, gpi_sim_hdl sig_hdl, int rising) @@ -202,7 +202,7 @@ gpi_sim_hdl gpi_register_value_change_callback(int (*gpi_function)(void *), /* It should not matter which implementation we use for this so just pick the first one */ -gpi_sim_hdl gpi_register_timed_callback(int (*gpi_function)(void *), +gpi_sim_hdl gpi_register_timed_callback(const int (*gpi_function)(const void *), void *gpi_cb_data, uint64_t time_ps) { GpiCbHdl *gpi_hdl = registered_impls[0]->register_timed_callback(time_ps); @@ -218,7 +218,7 @@ gpi_sim_hdl gpi_register_timed_callback(int (*gpi_function)(void *), /* It should not matter which implementation we use for this so just pick the first one */ -gpi_sim_hdl gpi_register_readonly_callback(int (*gpi_function)(void *), +gpi_sim_hdl gpi_register_readonly_callback(const int (*gpi_function)(const void *), void *gpi_cb_data) { GpiCbHdl *gpi_hdl = registered_impls[0]->register_readonly_callback(); @@ -231,7 +231,7 @@ gpi_sim_hdl gpi_register_readonly_callback(int (*gpi_function)(void *), return (gpi_sim_hdl)gpi_hdl; } -gpi_sim_hdl gpi_register_nexttime_callback(int (*gpi_function)(void *), +gpi_sim_hdl gpi_register_nexttime_callback(const int (*gpi_function)(const void *), void *gpi_cb_data) { GpiCbHdl *gpi_hdl = registered_impls[0]->register_nexttime_callback(); @@ -247,7 +247,7 @@ gpi_sim_hdl gpi_register_nexttime_callback(int (*gpi_function)(void *), /* It should not matter which implementation we use for this so just pick the first one */ -gpi_sim_hdl gpi_register_readwrite_callback(int (*gpi_function)(void *), +gpi_sim_hdl gpi_register_readwrite_callback(const int (*gpi_function)(const void *), void *gpi_cb_data) { GpiCbHdl *gpi_hdl = registered_impls[0] ->register_readwrite_callback(); diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index 25535308..c92d79cd 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -35,7 +35,7 @@ LIBS := -lgpilog -lcocotb -lstdc++ $(EXTRA_LIBS) LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) LIB_NAME := libgpi.so -SRCS := gpi_cb_hdl.cpp gpi_common.cpp +SRCS := GpiCbHdl.cpp GpiCommon.cpp all: $(LIB_DIR)/$(LIB_NAME) ln -sf $(LIB_NAME) $@ diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 3faabb65..ca2dca6e 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -19,7 +19,7 @@ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE dGOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS @@ -126,17 +126,17 @@ class GpiCbHdl : public GpiHdl { virtual int cleanup_callback(void) = 0; // Cleanup the callback, arm can be called after // Set the data to be used for run callback, seperate to arm_callback so data can be re-used - int set_user_data(int (*gpi_function)(void*), const void *data); + int set_user_data(const int (*gpi_function)(const void*), const void *data); const void *get_user_data(void); void set_call_state(gpi_cb_state_e new_state); gpi_cb_state_e get_call_state(void); - virtual ~GpiCbHdl() { } + virtual ~GpiCbHdl(); protected: - int (*gpi_function)(void *); // GPI function to callback - void *m_cb_data; // GPI data supplied to "gpi_function" + const int (*gpi_function)(const void *); // GPI function to callback + const void *m_cb_data; // GPI data supplied to "gpi_function" gpi_cb_state_e m_state; // GPI state of the callback through its cycle }; From ffc9c91f767ae9cf592dc03153f4ffc1f3a1ba13 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 7 Oct 2014 21:20:37 +0100 Subject: [PATCH 0602/2656] Issue#161: Rest of const additions --- lib/simulator/simulatormodule.c | 34 ++++++++++++++++----------- lib/vpi/{gpi_vpi.cpp => VpiCbHdl.cpp} | 0 2 files changed, 20 insertions(+), 14 deletions(-) rename lib/vpi/{gpi_vpi.cpp => VpiCbHdl.cpp} (100%) diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 9431149c..44cb1318 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -39,6 +39,8 @@ static int releases = 0; #include "simulatormodule.h" +typedef const int (*gpi_function_t)(const void *); + PyGILState_STATE TAKE_GIL(void) { PyGILState_STATE state = PyGILState_Ensure(); @@ -131,7 +133,6 @@ int handle_gpi_callback(void *user_data) return 0; } - static PyObject *log_msg(PyObject *self, PyObject *args) { const char *name; @@ -203,7 +204,7 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; //callback_data_p->cb_hdl = hdl; - hdl = gpi_register_readonly_callback(handle_gpi_callback, (void *)callback_data_p); + hdl = gpi_register_readonly_callback((gpi_function_t)handle_gpi_callback, callback_data_p); PyObject *rv = Py_BuildValue("l", hdl); DROP_GIL(gstate); @@ -264,7 +265,7 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; //callback_data_p->cb_hdl = hdl; - hdl = gpi_register_readwrite_callback(handle_gpi_callback, (void *)callback_data_p); + hdl = gpi_register_readwrite_callback((gpi_function_t)handle_gpi_callback, callback_data_p); PyObject *rv = Py_BuildValue("l", hdl); DROP_GIL(gstate); @@ -325,7 +326,7 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; //callback_data_p->cb_hdl = hdl; - hdl = gpi_register_nexttime_callback(handle_gpi_callback, (void *)callback_data_p); + hdl = gpi_register_nexttime_callback((gpi_function_t)handle_gpi_callback, callback_data_p); PyObject *rv = Py_BuildValue("l", hdl); DROP_GIL(gstate); @@ -397,7 +398,7 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; //callback_data_p->cb_hdl = hdl; - hdl = gpi_register_timed_callback(handle_gpi_callback, (void *)callback_data_p, time_ps); + hdl = gpi_register_timed_callback((gpi_function_t)handle_gpi_callback, callback_data_p, time_ps); // Check success PyObject *rv = Py_BuildValue("l", hdl); @@ -470,8 +471,9 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) callback_data_p->function = function; callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; + int edge = 3; //callback_data_p->cb_hdl = hdl; - hdl = gpi_register_value_change_callback(handle_gpi_callback, (void *)callback_data_p, sig_hdl); + hdl = gpi_register_value_change_callback((gpi_function_t)handle_gpi_callback, callback_data_p, sig_hdl, edge); // Check success PyObject *rv = Py_BuildValue("l", hdl); @@ -550,7 +552,7 @@ static PyObject *next(PyObject *self, PyObject *args) static PyObject *get_signal_val(PyObject *self, PyObject *args) { gpi_sim_hdl hdl; - char *result; + const char *result; PyObject *retstr; PyGILState_STATE gstate; @@ -561,9 +563,9 @@ static PyObject *get_signal_val(PyObject *self, PyObject *args) return NULL; } - result = gpi_get_signal_value_binstr((gpi_sim_hdl)hdl); + result = gpi_get_signal_value_binstr(hdl); retstr = Py_BuildValue("s", result); - free(result); + //free(result); DROP_GIL(gstate); @@ -669,7 +671,7 @@ static PyObject *get_handle_by_index(PyObject *self, PyObject *args) static PyObject *get_name_string(PyObject *self, PyObject *args) { - char *result; + const char *result; gpi_sim_hdl hdl; PyObject *retstr; @@ -683,7 +685,7 @@ static PyObject *get_name_string(PyObject *self, PyObject *args) result = gpi_get_signal_name_str((gpi_sim_hdl)hdl); retstr = Py_BuildValue("s", result); - free(result); + //free(result); DROP_GIL(gstate); @@ -693,7 +695,7 @@ static PyObject *get_name_string(PyObject *self, PyObject *args) static PyObject *get_type_string(PyObject *self, PyObject *args) { - char *result; + const char *result; gpi_sim_hdl hdl; PyObject *retstr; @@ -707,7 +709,7 @@ static PyObject *get_type_string(PyObject *self, PyObject *args) result = gpi_get_signal_type_str((gpi_sim_hdl)hdl); retstr = Py_BuildValue("s", result); - free(result); + //free(result); DROP_GIL(gstate); @@ -756,8 +758,10 @@ static PyObject *stop_simulator(PyObject *self, PyObject *args) return Py_BuildValue("s", "OK!"); } + static PyObject *reenable_callback(PyObject *self, PyObject *args) { + #if 0 gpi_sim_hdl hdl; p_callback_data callback_data_p; PyObject *pSihHdl; @@ -770,7 +774,7 @@ static PyObject *reenable_callback(PyObject *self, PyObject *args) pSihHdl = PyTuple_GetItem(args, 0); hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(pSihHdl); - callback_data_p = (p_callback_data)gpi_get_callback_data(hdl); +// callback_data_p = (p_callback_data)gpi_get_callback_data(hdl); // printf("Reenable callback, previous id_value: %08x\n", callback_data_p->id_value); @@ -783,6 +787,8 @@ static PyObject *reenable_callback(PyObject *self, PyObject *args) FEXIT return value; + #endif + return NULL; } diff --git a/lib/vpi/gpi_vpi.cpp b/lib/vpi/VpiCbHdl.cpp similarity index 100% rename from lib/vpi/gpi_vpi.cpp rename to lib/vpi/VpiCbHdl.cpp From 8c435b0d3b7e855897a7c72e9b0aa8d005ac89e6 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 7 Oct 2014 21:23:32 +0100 Subject: [PATCH 0603/2656] Issue#161: Rename CPP files, split into headers and implementations, rename classes --- lib/gpi/GpiCbHdl.cpp | 27 + lib/gpi/GpiCommon.cpp | 3 + lib/gpi/gpi_priv.h | 25 +- lib/vhpi/gpi_vhpi.c | 1038 -------------------------------------- lib/vhpi/gpi_vhpi.cpp | 1116 +++++++++++++---------------------------- lib/vpi/VpiCbHdl.cpp | 964 ++++++++--------------------------- lib/vpi/VpiImpl.cpp | 403 +++++++++++++++ lib/vpi/VpiImpl.h | 185 +++++++ 8 files changed, 1197 insertions(+), 2564 deletions(-) delete mode 100644 lib/vhpi/gpi_vhpi.c create mode 100644 lib/vpi/VpiImpl.cpp create mode 100644 lib/vpi/VpiImpl.h diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index aeaedd0c..fd6d9b21 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -28,6 +28,21 @@ #include "gpi_priv.h" +bool GpiObjHdl::is_native_impl(GpiImplInterface *impl) +{ + return impl->native_check(m_name); +} + +const char * GpiObjHdl::get_name_str(void) +{ + return m_name.c_str(); +} + +const char * GpiObjHdl::get_type_str(void) +{ + return m_type.c_str(); +} + /* Genertic base clss implementations */ const char *GpiHdl::gpi_copy_name(const char *name) { @@ -55,6 +70,18 @@ const char *GpiHdl::gpi_copy_name(const char *name) return result; } +int GpiHdl::initialise(std::string name) +{ + LOG_WARN("Generic initialise, doubt you should have called this"); + return 0; +} + +int GpiObjHdl::initialise(std::string name) +{ + m_name = name; + return 0; +} + #if 0 int GpiCbHdl::handle_callback(void) { diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 25c31588..3202df22 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -89,6 +89,9 @@ gpi_sim_hdl gpi_get_root_handle(const char *name) iter != registered_impls.end(); iter++) { if ((hdl = (*iter)->get_root_handle(name))) { + LOG_WARN("Got a handle (%s) back from %s", + hdl->get_name_str(), + (*iter)->get_name_c()); return (void*)hdl; } } diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index ca2dca6e..59ea9df2 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -26,6 +26,9 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ +#ifndef COCOTB_GPI_PRIV_H_ +#define COCOTB_GPI_PRIV_H_ + #include #include #include @@ -46,12 +49,13 @@ class GpiCbHdl; /* Base GPI class others are derived from */ class GpiHdl { public: + GpiHdl() : m_impl(NULL) { } GpiHdl(GpiImplInterface *impl) : m_impl(impl) { } virtual ~GpiHdl() { } - virtual int Initialise(void); // Post constructor init + virtual int initialise(std::string name); // Post constructor init private: - GpiHdl() { } // Disable default constructor + //GpiHdl() { } // Disable default constructor public: GpiImplInterface *m_impl; // VPI/VHPI/FLI routines @@ -67,6 +71,7 @@ class GpiHdl { // that construct an object derived from GpiSignalObjHdl or GpiObjHdl class GpiObjHdl : public GpiHdl { public: + GpiObjHdl(std::string name) : GpiHdl(NULL), m_name(name) { } GpiObjHdl(GpiImplInterface *impl) : GpiHdl(impl) { } virtual ~GpiObjHdl() { } @@ -77,9 +82,13 @@ class GpiObjHdl : public GpiHdl { virtual GpiIterator *iterate_handle(uint32_t type) = 0; virtual GpiObjHdl *next_handle(GpiIterator *iterator) = 0; - virtual const char* get_name_str(void) = 0; - virtual const char* get_type_str(void) = 0; + const char* get_name_str(void); + const char* get_type_str(void); + + bool is_native_impl(GpiImplInterface *impl); + virtual int initialise(std::string name); +protected: std::string m_name; std::string m_type; }; @@ -122,7 +131,7 @@ class GpiCbHdl : public GpiHdl { m_state(GPI_FREE) { } // Pure virtual functions for derived classes virtual int arm_callback(void) = 0; // Register with siumlator - virtual int run_callback(void) = 0; // Entry point from simulator + virtual int run_callback(void); // Entry point from simulator virtual int cleanup_callback(void) = 0; // Cleanup the callback, arm can be called after // Set the data to be used for run callback, seperate to arm_callback so data can be re-used @@ -183,6 +192,8 @@ class GpiImplInterface { virtual void get_sim_time(uint32_t *high, uint32_t *low) = 0; /* Hierachy related */ + virtual bool is_native(GpiObjHdl *hdl) = 0; + virtual bool native_check(std::string &name) = 0; virtual GpiObjHdl *get_root_handle(const char *name) = 0; /* Callback related, these may (will) return the same handle*/ @@ -201,4 +212,6 @@ int gpi_register_impl(GpiImplInterface *func_tbl); void gpi_embed_init(gpi_sim_info_t *info); void gpi_embed_end(void); -void gpi_embed_init_python(void); \ No newline at end of file +void gpi_embed_init_python(void); + +#endif /* COCOTB_GPI_PRIV_H_ */ \ No newline at end of file diff --git a/lib/vhpi/gpi_vhpi.c b/lib/vhpi/gpi_vhpi.c deleted file mode 100644 index 74e811f3..00000000 --- a/lib/vhpi/gpi_vhpi.c +++ /dev/null @@ -1,1038 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2014 Potential Ventures Ltd -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd not the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -// TODO: -// Some functions are completely untested (vhpi_get_handle_by_index) and others -// need optimisation. -// -// VHPI seems to run significantly slower than VPI, need to investigate. - - -#include "../gpi/gpi_priv.h" -#include - -#define VHPI_CHECKING 1 - -static gpi_sim_hdl sim_init_cb; -static gpi_sim_hdl sim_finish_cb; - -typedef enum vhpi_cb_state_e { - VHPI_FREE = 0, - VHPI_PRIMED = 1, - VHPI_PRE_CALL = 2, - VHPI_POST_CALL = 3, - VHPI_DELETE = 4, -} vhpi_cb_state_t; - -// callback user data used for VPI callbacks -// (mostly just a thin wrapper around the gpi_callback) -typedef struct t_vhpi_cb { - vhpiHandleT cb_hdl; - vhpiValueT cb_value; - vhpi_cb_state_t state; - gpi_cb_hdl_t gpi_cb_data; - int (*vhpi_cleanup)(struct t_vhpi_cb *); -} s_vhpi_cb, *p_vhpi_cb; - -// Forward declarations -static int vhpi_deregister_callback(gpi_sim_hdl gpi_hdl); -static void vhpi_destroy_cb_handle(gpi_cb_hdl hdl); - -static const char * vhpi_reason_to_string(int reason) -{ - switch (reason) { - case vhpiCbValueChange: - return "vhpiCbValueChange"; - case vhpiCbStartOfNextCycle: - return "vhpiCbStartOfNextCycle"; - case vhpiCbStartOfPostponed: - return "vhpiCbStartOfPostponed"; - case vhpiCbEndOfTimeStep: - return "vhpiCbEndOfTimeStep"; - case vhpiCbNextTimeStep: - return "vhpiCbNextTimeStep"; - case vhpiCbAfterDelay: - return "vhpiCbAfterDelay"; - case vhpiCbStartOfSimulation: - return "vhpiCbStartOfSimulation"; - case vhpiCbEndOfSimulation: - return "vhpiCbEndOfSimulation"; - case vhpiCbEndOfProcesses: - return "vhpiCbEndOfProcesses"; - case vhpiCbLastKnownDeltaCycle: - return "vhpiCbLastKnownDeltaCycle"; - default: - return "unknown"; - } -} - -static const char * vhpi_format_to_string(int reason) -{ - switch (reason) { - case vhpiBinStrVal: - return "vhpiBinStrVal"; - case vhpiOctStrVal: - return "vhpiOctStrVal"; - case vhpiDecStrVal: - return "vhpiDecStrVal"; - case vhpiHexStrVal: - return "vhpiHexStrVal"; - case vhpiEnumVal: - return "vhpiEnumVal"; - case vhpiIntVal: - return "vhpiIntVal"; - case vhpiLogicVal: - return "vhpiLogicVal"; - case vhpiRealVal: - return "vhpiRealVal"; - case vhpiStrVal: - return "vhpiStrVal"; - case vhpiCharVal: - return "vhpiCharVal"; - case vhpiTimeVal: - return "vhpiTimeVal"; - case vhpiPhysVal: - return "vhpiPhysVal"; - case vhpiObjTypeVal: - return "vhpiObjTypeVal"; - case vhpiPtrVal: - return "vhpiPtrVal"; - case vhpiEnumVecVal: - return "vhpiEnumVecVal"; - - default: - return "unknown"; - } -} - - - -// Should be run after every VPI call to check error status -static int __check_vhpi_error(const char *func, long line) -{ - int level=0; -#if VHPI_CHECKING - vhpiErrorInfoT info; - int loglevel; - level = vhpi_check_error(&info); - if (level == 0) - return 0; - - switch (level) { - case vhpiNote: - loglevel = GPIInfo; - break; - case vhpiWarning: - loglevel = GPIWarning; - break; - case vhpiError: - loglevel = GPIError; - break; - case vhpiFailure: - case vhpiSystem: - case vhpiInternal: - loglevel = GPICritical; - break; - } - - gpi_log("cocotb.gpi", loglevel, __FILE__, func, line, - "VHPI Error level %d: %s\nFILE %s:%d", - info.severity, info.message, info.file, info.line); - -#endif - return level; -} - -#define check_vhpi_error() \ - __check_vhpi_error(__func__, __LINE__) - -static inline int __vhpi_register_cb(p_vhpi_cb user, vhpiCbDataT *cb_data) -{ - /* If the user data already has a callback handle then deregister - * before getting the new one - */ - vhpiHandleT new_hdl = vhpi_register_cb(cb_data, vhpiReturnCb); - int ret = 0; - - if (!new_hdl) { - LOG_CRITICAL("VHPI: Unable to register callback a handle for VHPI type %s(%d)", - vhpi_reason_to_string(cb_data->reason), cb_data->reason); - check_vhpi_error(); - ret = -1; - } - - vhpiStateT cbState = vhpi_get(vhpiStateP, new_hdl); - if (cbState != vhpiEnable) { - LOG_CRITICAL("VHPI ERROR: Registered callback isn't enabled! Got %d\n", cbState); - } - - if (user->cb_hdl != NULL) { - LOG_ERROR("VHPI: Attempt to register a callback that's already registered...\n"); - vhpi_deregister_callback(&user->gpi_cb_data.hdl); - } - - user->cb_hdl = new_hdl; - user->state = VHPI_PRIMED; - - return ret; -} - -// Handle related functions -/** - * @name Find the root handle - * @brief Find the root handle using a optional name - * - * Get a handle to the root simulator object. This is usually the toplevel. - * - * FIXME: In VHPI we always return the first root instance - * - * TODO: Investigate possibility of iterating and checking names as per VHPI - * If no name is defined, we return the first root instance. - * - * If name is provided, we check the name against the available objects until - * we find a match. If no match is found we return NULL - */ -static gpi_sim_hdl vhpi_get_root_handle(const char* name) -{ - FENTER - vhpiHandleT root; - vhpiHandleT dut; - gpi_sim_hdl rv; - - root = vhpi_handle(vhpiRootInst, NULL); - check_vhpi_error(); - - if (!root) { - LOG_ERROR("VHPI: Attempting to get the root handle failed"); - FEXIT - return NULL; - } - - if (name) - dut = vhpi_handle_by_name(name, NULL); - else - dut = vhpi_handle(vhpiDesignUnit, root); - check_vhpi_error(); - - if (!dut) { - LOG_ERROR("VHPI: Attempting to get the DUT handle failed"); - FEXIT - return NULL; - } - - const char *found = vhpi_get_str(vhpiNameP, dut); - check_vhpi_error(); - - if (name != NULL && strcmp(name, found)) { - LOG_WARN("VHPI: Root '%s' doesn't match requested toplevel %s", found, name); - FEXIT - return NULL; - } - - rv = gpi_create_handle(); - rv->sim_hdl = dut; - - FEXIT - return rv; -} - - -static gpi_sim_hdl vhpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) -{ - FENTER - gpi_sim_hdl rv; - vhpiHandleT obj; - int len; - char *buff; - if (name) - len = strlen(name) + 1; - - buff = (char *)malloc(len); - if (buff == NULL) { - LOG_CRITICAL("VHPI: Attempting allocate string buffer failed!"); - return NULL; - } - - strncpy(buff, name, len); - obj = vhpi_handle_by_name(buff, (vhpiHandleT)(parent->sim_hdl)); - if (!obj) { - LOG_DEBUG("VHPI: Handle '%s' not found!", name); -// check_vhpi_error(); - return NULL; - } - - free(buff); - - rv = gpi_create_handle(); - rv->sim_hdl = obj; - - FEXIT - return rv; -} - -/** - * @brief Get a handle for an object based on its index within a parent - * - * @param parent handle to the parent - * @param indext Index to retrieve - * - * Can be used on bit-vectors to access a specific bit or - * memories to access an address - */ -static gpi_sim_hdl vhpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) -{ - FENTER - gpi_sim_hdl rv; - vhpiHandleT obj; - - obj = vhpi_handle_by_index(vhpiParamDecls, (vhpiHandleT)(parent->sim_hdl), index); - if (!obj) { - LOG_ERROR("VHPI: Handle idx '%d' not found!", index); - return NULL; - } - - rv = gpi_create_handle(); - rv->sim_hdl = obj; - - FEXIT - return rv; -} - - -// Functions for iterating over entries of a handle -// Returns an iterator handle which can then be used in gpi_next calls -// NB May return NULL if no objects of the request type exist -static gpi_iterator_hdl vhpi_iterate_hdl(uint32_t type, gpi_sim_hdl base) { - FENTER - - vhpiHandleT iterator; - - iterator = vhpi_iterator(type, (vhpiHandleT)(base->sim_hdl)); - check_vhpi_error(); - - FEXIT - return (gpi_iterator_hdl)iterator; -} - -// Returns NULL when there are no more objects -static gpi_sim_hdl vhpi_next_hdl(gpi_iterator_hdl iterator) -{ - FENTER - gpi_sim_hdl rv = gpi_create_handle(); - - rv->sim_hdl = vhpi_scan((vhpiHandleT) iterator); - check_vhpi_error(); - if (!rv->sim_hdl) { - gpi_free_handle(rv); - rv = NULL; - } - FEXIT - return rv; -} - -static void vhpi_get_sim_time(uint32_t *high, uint32_t *low) -{ - vhpiTimeT vhpi_time_s; - vhpi_get_time(&vhpi_time_s, NULL); - check_vhpi_error(); - *high = vhpi_time_s.high; - *low = vhpi_time_s.low; -} - -// Value related functions -static vhpiEnumT chr2vhpi(const char value) { - switch (value) { - case '0': - return vhpi0; - case '1': - return vhpi1; - case 'U': - case 'u': - return vhpiU; - case 'Z': - case 'z': - return vhpiZ; - case 'X': - case 'x': - return vhpiX; - default: - return vhpiDontCare; - } -} - -// Unfortunately it seems that format conversion is not well supported -// We have to set values using vhpiEnum* -static void vhpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) -{ - FENTER - vhpiValueT value_s; - int size, i; - - // Determine the type of object, either scalar or vector - value_s.format = vhpiObjTypeVal; - value_s.bufSize = 0; - value_s.value.str = NULL; - - vhpi_get_value((vhpiHandleT)(gpi_hdl->sim_hdl), &value_s); - check_vhpi_error(); - - switch (value_s.format) { - case vhpiEnumVal: - case vhpiLogicVal: { - value_s.value.enumv = value ? vhpi1 : vhpi0; - break; - } - - case vhpiEnumVecVal: - case vhpiLogicVecVal: { - size = vhpi_get(vhpiSizeP, (vhpiHandleT)(gpi_hdl->sim_hdl)); - value_s.bufSize = size*sizeof(vhpiEnumT); - value_s.value.enumvs = (vhpiEnumT *)malloc(size*sizeof(vhpiEnumT)); - - for (i=0; isim_hdl), &value_s, vhpiForcePropagate); - check_vhpi_error(); - - if (vhpiEnumVecVal == value_s.format) - free(value_s.value.enumvs); - FEXIT -} - - - -// Unfortunately it seems that format conversion is not well supported -// We have to set values using vhpiEnum* -static void vhpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) -{ - FENTER - vhpiValueT value_s; - int len, size, i; - const char *ptr; - - // Determine the type of object, either scalar or vector - value_s.format = vhpiObjTypeVal; - value_s.bufSize = 0; - value_s.value.str = NULL; - - vhpi_get_value((vhpiHandleT)(gpi_hdl->sim_hdl), &value_s); - check_vhpi_error(); - - switch (value_s.format) { - case vhpiEnumVal: - case vhpiLogicVal: { - value_s.value.enumv = chr2vhpi(*str); - break; - } - - case vhpiEnumVecVal: - case vhpiLogicVecVal: { - len = strlen(str); - size = vhpi_get(vhpiSizeP, (vhpiHandleT)(gpi_hdl->sim_hdl)); - value_s.bufSize = size*sizeof(vhpiEnumT); - value_s.value.enumvs = (vhpiEnumT *)malloc(size*sizeof(vhpiEnumT)); - - // Initialise to 0s - for (i=0; isim_hdl), &value_s, vhpiForcePropagate); - check_vhpi_error(); - if(value_s.format == vhpiEnumVecVal) - free(value_s.value.enumvs); - FEXIT -} - -static char *vhpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) -{ - FENTER - vhpiValueT value_s; - vhpiValueT *value_p = &value_s; - char *result; - size_t size; - value_p->format = vhpiBinStrVal; - - -// FIXME Seem to have a problem here -// According to VHPI spec we should call vhpi_get_value once to determine -// how much memory to allocate for the result... it appears that we just -// get bogus values back so we'll use a fixed size buffer for now -#if 0 - // Call once to find out how long the string is - vhpi_get_value((vhpiHandleT)(gpi_hdl->sim_hdl), value_p); - check_vhpi_error(); - - size = value_p->bufSize; - LOG_ERROR("After initial call to get value: bufSize=%u", size); -#else - size = 512; -#endif - - result = (char *)malloc(size); - if (result == NULL) { - LOG_CRITICAL("VHPI: Attempting allocate string buffer failed!"); - } - - // Call again to get the value - value_p->bufSize = size; - value_p->value.str = result; - vhpi_get_value((vhpiHandleT)(gpi_hdl->sim_hdl), value_p); - check_vhpi_error(); - - FEXIT - return result; -} - -static char *vhpi_get_signal_name_str(gpi_sim_hdl gpi_hdl) -{ - FENTER - const char *name = vhpi_get_str(vhpiFullNameP, (vhpiHandleT)(gpi_hdl->sim_hdl)); - check_vhpi_error(); - char *result = gpi_copy_name(name); - FEXIT - return result; -} - -static char *vhpi_get_signal_type_str(gpi_sim_hdl gpi_hdl) -{ - FENTER - const char *name = vhpi_get_str(vhpiKindStrP, (vhpiHandleT)(gpi_hdl->sim_hdl)); - check_vhpi_error(); - char *result = gpi_copy_name(name); - FEXIT - return result; -} - - -// Callback related functions -static void handle_vhpi_callback(const vhpiCbDataT *cb_data) -{ - FENTER - vhpiHandleT old_cb; - - p_vhpi_cb user_data; - user_data = (p_vhpi_cb)cb_data->user_data; - - if (!user_data) - LOG_CRITICAL("VHPI: Callback data corrupted"); - - user_data->state = VHPI_PRE_CALL; - old_cb = user_data->cb_hdl; - gpi_handle_callback(&user_data->gpi_cb_data.hdl); - - if (old_cb == user_data->cb_hdl) { - - // Don't de-register recurring callbacks - VHPI only seems to allow - // a single registration per recurring callback. For edge events on - // signals etc. we never want to remove. - vhpiStateT cbState = vhpi_get(vhpiStateP, user_data->cb_hdl); - if (vhpiMature == cbState) - gpi_deregister_callback(&user_data->gpi_cb_data.hdl); - } - - /* A request to delete could have been done - * inside gpi_function - */ - if (user_data->state == VHPI_DELETE) - gpi_free_cb_handle(&user_data->gpi_cb_data.hdl); - else - user_data->state = VHPI_POST_CALL; - - FEXIT - return; -}; - -/* Allocates memory that will persist for the lifetime of the - * handle, this may be short or long. A call to create - * must have a matching call to destroy at some point - */ -static gpi_cb_hdl vhpi_create_cb_handle(void) -{ - gpi_cb_hdl ret = NULL; - FENTER - p_vhpi_cb user_data = calloc(1, sizeof(*user_data)); - - if (user_data) - ret = &user_data->gpi_cb_data; - - FEXIT - return ret; -} - -/* Destroys the memory associated with the sim handle - * this can only be called on a handle that has been - * returned by a call to gpi_create_cb_handle - */ -static void vhpi_destroy_cb_handle(gpi_cb_hdl hdl) -{ - /* Check that is has been called, if this has not - * happend then also close down the sim data as well - */ - FENTER - p_vhpi_cb user_data = gpi_container_of(hdl, s_vhpi_cb, gpi_cb_data); - - free(user_data); - FEXIT -} - - -static void *vhpi_get_callback_data(gpi_sim_hdl gpi_hdl) -{ - FENTER - gpi_cb_hdl gpi_user_data; - gpi_user_data = gpi_container_of(gpi_hdl, gpi_cb_hdl_t, hdl); - return gpi_user_data->gpi_cb_data; -} - - -/* Deregister a prior set up callback with the simulator - * The handle must have been allocated with gpi_create_cb_handle - * This can be called at any point between - * gpi_create_cb_handle and gpi_free_cb_handle - */ -static int vhpi_deregister_callback(gpi_sim_hdl gpi_hdl) -{ - FENTER - p_vhpi_cb vhpi_user_data; - gpi_cb_hdl gpi_user_data; - int rc = 1; - - gpi_user_data = gpi_container_of(gpi_hdl, gpi_cb_hdl_t, hdl); - vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); - - if (vhpi_user_data->cb_hdl != NULL) { - rc = vhpi_user_data->vhpi_cleanup(vhpi_user_data); - vhpi_user_data->cb_hdl = NULL; - } - - FEXIT - GPI_RET(rc); -} - -// Call when the handle relates to a one time callback -// No need to call vhpi_deregister_cb as the sim will -// do this but do need to destroy the handle -static int vhpi_free_one_time(p_vhpi_cb user_data) -{ - FENTER - int rc = 0; - vhpiHandleT cb_hdl = user_data->cb_hdl; - if (!cb_hdl) { - LOG_CRITICAL("VHPI: passed a NULL pointer : ABORTING"); - exit(1); - } - - // If the callback has not been called we also need to call - // remove as well - if (user_data->state == VHPI_PRIMED) { - rc = vhpi_remove_cb(cb_hdl); - if (!rc) { - check_vhpi_error(); - return rc; - } - - rc = vhpi_release_handle(cb_hdl); - if (!rc) { - check_vhpi_error(); - return rc; - } - } - FEXIT - return rc; -} - -// Call when the handle relates to recurring callback -// Unregister must be called when not needed and this -// will clean all memory allocated by the sim -static int vhpi_free_recurring(p_vhpi_cb user_data) -{ - FENTER - int rc; - vhpiHandleT cb_hdl = user_data->cb_hdl; - if (!cb_hdl) { - LOG_CRITICAL("VHPI: passed a NULL pointer : ABORTING"); - exit(1); - } - - rc = vhpi_remove_cb(cb_hdl); - check_vhpi_error(); - FEXIT - return rc; -} - -/* These functions request a callback to be active with the current - * handle and associated data. A callback handle needs to have been - * allocated with gpi_create_cb_handle first - */ - -static int vhpi_register_value_change_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data, - gpi_sim_hdl gpi_hdl) -{ - FENTER - - int ret; - vhpiCbDataT cb_data_s; - vhpiTimeT time; - p_vhpi_cb vhpi_user_data; - gpi_cb_hdl gpi_user_data; - - gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); - vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); - - vhpi_user_data->vhpi_cleanup = vhpi_free_recurring; - vhpi_user_data->cb_value.format = vhpiIntVal; - - cb_data_s.reason = vhpiCbValueChange; - cb_data_s.cb_rtn = handle_vhpi_callback; - cb_data_s.obj = (vhpiHandleT)(gpi_hdl->sim_hdl); - cb_data_s.time = &time; - cb_data_s.value = &vhpi_user_data->cb_value; - cb_data_s.user_data = (char *)vhpi_user_data; - - ret = __vhpi_register_cb(vhpi_user_data, &cb_data_s); - - FEXIT - - return ret; -} - - -static int vhpi_register_readonly_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) -{ - FENTER - - int ret; - vhpiCbDataT cb_data_s; - vhpiTimeT time; - p_vhpi_cb vhpi_user_data; - gpi_cb_hdl gpi_user_data; - - gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); - vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); - - vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; - - cb_data_s.reason = vhpiCbLastKnownDeltaCycle; - cb_data_s.cb_rtn = handle_vhpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = &time; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)vhpi_user_data; - - ret = __vhpi_register_cb(vhpi_user_data, &cb_data_s); - - FEXIT - - return ret; -} - -static int vhpi_register_readwrite_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) -{ - FENTER - - - int ret; - vhpiCbDataT cb_data_s; - vhpiTimeT time; - p_vhpi_cb vhpi_user_data; - gpi_cb_hdl gpi_user_data; - - gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); - vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); - - vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; - - cb_data_s.reason = vhpiCbEndOfProcesses; - cb_data_s.cb_rtn = handle_vhpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = &time; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)vhpi_user_data; - - ret = __vhpi_register_cb(vhpi_user_data, &cb_data_s); - - FEXIT - - return ret; -} - -static int vhpi_register_nexttime_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) -{ - FENTER - - int ret; - vhpiCbDataT cb_data_s; - vhpiTimeT time; - p_vhpi_cb vhpi_user_data; - gpi_cb_hdl gpi_user_data; - - gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); - vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); - - vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; - - cb_data_s.reason = vhpiCbNextTimeStep; - cb_data_s.cb_rtn = handle_vhpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = &time; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)vhpi_user_data; - - ret = __vhpi_register_cb(vhpi_user_data, &cb_data_s); - - FEXIT - - return ret; -} - -static int vhpi_register_timed_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data, - uint64_t time_ps) -{ - FENTER - - int ret; - vhpiCbDataT cb_data_s; - vhpiTimeT time_s; - p_vhpi_cb vhpi_user_data; - gpi_cb_hdl gpi_user_data; - - gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); - vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); - - vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; - - time_s.high = (uint32_t)(time_ps>>32); - time_s.low = (uint32_t)(time_ps); - - cb_data_s.reason = vhpiCbAfterDelay; - cb_data_s.cb_rtn = handle_vhpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = &time_s; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)vhpi_user_data; - - ret = __vhpi_register_cb(vhpi_user_data, &cb_data_s); - - FEXIT - - return ret; -} - - -// If the Pything world wants things to shut down then unregister -// the callback for end of sim -static void vhpi_sim_end(void) -{ - FENTER - - sim_finish_cb = NULL; - vhpi_control(vhpiFinish); - check_vhpi_error(); - FEXIT -} - -static s_gpi_impl_tbl vhpi_table = { - .sim_end = vhpi_sim_end, - .iterate_handle = vhpi_iterate_hdl, - .next_handle = vhpi_next_hdl, - .create_cb_handle = vhpi_create_cb_handle, - .destroy_cb_handle = vhpi_destroy_cb_handle, - .deregister_callback = vhpi_deregister_callback, - .get_root_handle = vhpi_get_root_handle, - .get_sim_time = vhpi_get_sim_time, - .get_handle_by_name = vhpi_get_handle_by_name, - .get_handle_by_index = vhpi_get_handle_by_index, - .get_signal_name_str = vhpi_get_signal_name_str, - .get_signal_type_str = vhpi_get_signal_type_str, - .get_signal_value_binstr = vhpi_get_signal_value_binstr, - .set_signal_value_int = vhpi_set_signal_value_int, - .set_signal_value_str = vhpi_set_signal_value_str, - .register_timed_callback = vhpi_register_timed_callback, - .register_readwrite_callback = vhpi_register_readwrite_callback, - .register_nexttime_callback = vhpi_register_nexttime_callback, - .register_value_change_callback = vhpi_register_value_change_callback, - .register_readonly_callback = vhpi_register_readonly_callback, - .get_callback_data = vhpi_get_callback_data, -}; - -static void register_embed(void) -{ - FENTER - gpi_register_impl(&vhpi_table, 0xfeed); - gpi_embed_init_python(); - FEXIT -} - - -static int handle_sim_init(void *gpi_cb_data) -{ - FENTER - gpi_sim_info_t sim_info; - sim_info.argc = 0; - sim_info.argv = NULL; - sim_info.product = gpi_copy_name(vhpi_get_str(vhpiNameP, NULL)); - sim_info.version = gpi_copy_name(vhpi_get_str(vhpiToolVersionP, NULL)); - gpi_embed_init(&sim_info); - - free(sim_info.product); - free(sim_info.version); - - FEXIT - - return 0; -} - -static void register_initial_callback(void) -{ - FENTER - - vhpiCbDataT cb_data_s; - p_vhpi_cb vhpi_user_data; - gpi_cb_hdl gpi_user_data; - - - sim_init_cb = gpi_create_cb_handle(); - - gpi_user_data = gpi_container_of(sim_init_cb, gpi_cb_hdl_t, hdl); - vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); - - gpi_user_data->gpi_cb_data = NULL; - gpi_user_data->gpi_function = handle_sim_init; - - vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; - - cb_data_s.reason = vhpiCbStartOfSimulation; - cb_data_s.cb_rtn = handle_vhpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = NULL; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)vhpi_user_data; - - __vhpi_register_cb(vhpi_user_data, &cb_data_s); - - FEXIT -} - -static int handle_sim_end(void *gpi_cb_data) -{ - FENTER - if (sim_finish_cb) { - sim_finish_cb = NULL; - /* This means that we have been asked to close */ - embed_sim_event(SIM_FAIL, "Simulator shutdown prematurely"); - } /* Other sise we have already been here from the top down so do not need - to inform the upper layers that anything has occoured */ - gpi_free_cb_handle(sim_init_cb); - FEXIT - - return 0; -} - -static void register_final_callback(void) -{ - FENTER - - vhpiCbDataT cb_data_s; - p_vhpi_cb vhpi_user_data; - gpi_cb_hdl gpi_user_data; - - sim_finish_cb = gpi_create_cb_handle(); - - gpi_user_data = gpi_container_of(sim_finish_cb, gpi_cb_hdl_t, hdl); - vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); - - gpi_user_data->gpi_cb_data = NULL; - gpi_user_data->gpi_function = handle_sim_end; - vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; - - cb_data_s.reason = vhpiCbEndOfSimulation; - cb_data_s.cb_rtn = handle_vhpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = NULL; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)vhpi_user_data; - - /* We ignore the return value here as VCS does some silly - * things on comilation that means it tries to run through - * the vlog_startup_routines and so call this routine - */ - __vhpi_register_cb(vhpi_user_data, &cb_data_s); - - FEXIT -} - -// pre-defined VHPI registration table -void (*vhpi_startup_routines[])(void) = { - register_embed, - register_initial_callback, - register_final_callback, - 0 -}; - -// For non-VPI compliant applications that cannot find vlog_startup_routines -void vhpi_startup_routines_bootstrap(void) { - void (*routine)(void); - int i; - routine = vhpi_startup_routines[0]; - for (i = 0, routine = vhpi_startup_routines[i]; - routine; - routine = vhpi_startup_routines[++i]) { - routine(); - } -} diff --git a/lib/vhpi/gpi_vhpi.cpp b/lib/vhpi/gpi_vhpi.cpp index 74e811f3..122a8b2b 100644 --- a/lib/vhpi/gpi_vhpi.cpp +++ b/lib/vhpi/gpi_vhpi.cpp @@ -37,60 +37,14 @@ #define VHPI_CHECKING 1 -static gpi_sim_hdl sim_init_cb; -static gpi_sim_hdl sim_finish_cb; - -typedef enum vhpi_cb_state_e { - VHPI_FREE = 0, - VHPI_PRIMED = 1, - VHPI_PRE_CALL = 2, - VHPI_POST_CALL = 3, - VHPI_DELETE = 4, -} vhpi_cb_state_t; - -// callback user data used for VPI callbacks -// (mostly just a thin wrapper around the gpi_callback) -typedef struct t_vhpi_cb { - vhpiHandleT cb_hdl; - vhpiValueT cb_value; - vhpi_cb_state_t state; - gpi_cb_hdl_t gpi_cb_data; - int (*vhpi_cleanup)(struct t_vhpi_cb *); -} s_vhpi_cb, *p_vhpi_cb; - -// Forward declarations -static int vhpi_deregister_callback(gpi_sim_hdl gpi_hdl); -static void vhpi_destroy_cb_handle(gpi_cb_hdl hdl); - -static const char * vhpi_reason_to_string(int reason) -{ - switch (reason) { - case vhpiCbValueChange: - return "vhpiCbValueChange"; - case vhpiCbStartOfNextCycle: - return "vhpiCbStartOfNextCycle"; - case vhpiCbStartOfPostponed: - return "vhpiCbStartOfPostponed"; - case vhpiCbEndOfTimeStep: - return "vhpiCbEndOfTimeStep"; - case vhpiCbNextTimeStep: - return "vhpiCbNextTimeStep"; - case vhpiCbAfterDelay: - return "vhpiCbAfterDelay"; - case vhpiCbStartOfSimulation: - return "vhpiCbStartOfSimulation"; - case vhpiCbEndOfSimulation: - return "vhpiCbEndOfSimulation"; - case vhpiCbEndOfProcesses: - return "vhpiCbEndOfProcesses"; - case vhpiCbLastKnownDeltaCycle: - return "vhpiCbLastKnownDeltaCycle"; - default: - return "unknown"; - } +extern "C" { +void handle_vhpi_callback(const vhpiCbDataT *cb_data); +int __check_vhpi_error(const char *func, long line); +const char * vhpi_format_to_string(int reason); +const vhpiEnumT chr2vhpi(const char value); } -static const char * vhpi_format_to_string(int reason) +const char * vhpi_format_to_string(int reason) { switch (reason) { case vhpiBinStrVal: @@ -129,10 +83,30 @@ static const char * vhpi_format_to_string(int reason) } } - +// Value related functions +const vhpiEnumT chr2vhpi(const char value) +{ + switch (value) { + case '0': + return vhpi0; + case '1': + return vhpi1; + case 'U': + case 'u': + return vhpiU; + case 'Z': + case 'z': + return vhpiZ; + case 'X': + case 'x': + return vhpiX; + default: + return vhpiDontCare; + } +} // Should be run after every VPI call to check error status -static int __check_vhpi_error(const char *func, long line) +int __check_vhpi_error(const char *func, long line) { int level=0; #if VHPI_CHECKING @@ -170,36 +144,278 @@ static int __check_vhpi_error(const char *func, long line) #define check_vhpi_error() \ __check_vhpi_error(__func__, __LINE__) -static inline int __vhpi_register_cb(p_vhpi_cb user, vhpiCbDataT *cb_data) -{ - /* If the user data already has a callback handle then deregister - * before getting the new one - */ - vhpiHandleT new_hdl = vhpi_register_cb(cb_data, vhpiReturnCb); - int ret = 0; - - if (!new_hdl) { - LOG_CRITICAL("VHPI: Unable to register callback a handle for VHPI type %s(%d)", - vhpi_reason_to_string(cb_data->reason), cb_data->reason); +class vhpi_obj_hdl : public gpi_obj_hdl { +private: + int m_size; + vhpiValueT m_value; +public: + vhpi_obj_hdl(vhpiHandleT hdl, gpi_impl_interface *impl) : gpi_obj_hdl(impl), + m_size(0), + vhpi_hdl(hdl) { } + virtual ~vhpi_obj_hdl() { + if (m_value.format == vhpiEnumVecVal || + m_value.format == vhpiLogicVecVal) { + free(m_value.value.enumvs); + } + } +public: + vhpiHandleT vhpi_hdl; + + int initialise(void) { + // Determine the type of object, either scalar or vector + m_value.format = vhpiObjTypeVal; + m_value.bufSize = 0; + m_value.value.str = NULL; + + vhpi_get_value(vhpi_hdl, &m_value); check_vhpi_error(); - ret = -1; + + switch (m_value.format) { + case vhpiEnumVal: + case vhpiLogicVal: { + m_value.value.enumv = vhpi0; + break; + } + + case vhpiEnumVecVal: + case vhpiLogicVecVal: { + m_size = vhpi_get(vhpiSizeP, vhpi_hdl); + m_value.bufSize = m_size*sizeof(vhpiEnumT); + m_value.value.enumvs = (vhpiEnumT *)malloc(m_size*sizeof(vhpiEnumT)); + + memset(&m_value.value.enumvs, m_size, vhpi0); + //for (i=0; icb_hdl != NULL) { - LOG_ERROR("VHPI: Attempt to register a callback that's already registered...\n"); - vhpi_deregister_callback(&user->gpi_cb_data.hdl); + int write_new_value(const char *str) { + switch (m_value.format) { + case vhpiEnumVal: + case vhpiLogicVal: { + m_value.value.enumv = chr2vhpi(*str); + break; + } + + case vhpiEnumVecVal: + case vhpiLogicVecVal: { + + const char *ptr; + int len = strlen(str); + if (len > m_size) { + LOG_ERROR("VHPI: Attempt to write string longer than signal %d > %d", + len, m_size); + return -1; + } + int i; + for (i=0, ptr=str; icb_hdl = new_hdl; - user->state = VHPI_PRIMED; +class vhpi_cb_hdl : public gpi_cb_hdl { +protected: + vhpiCbDataT cb_data; + vhpiHandleT vhpi_hdl; +public: + vhpi_cb_hdl(gpi_impl_interface *impl) : gpi_cb_hdl(impl) { + cb_data.reason = 0; + cb_data.cb_rtn = handle_vhpi_callback; + cb_data.obj = NULL; + cb_data.time = NULL; + cb_data.value = NULL; + cb_data.user_data = (char *)this; + } - return ret; -} + int arm_callback(void) { + vhpiHandleT new_hdl = vhpi_register_cb(&cb_data, vhpiReturnCb); + int ret = 0; + + if (!new_hdl) { + LOG_CRITICAL("VHPI: Unable to register callback a handle for VHPI type %s(%d)", + reason_to_string(cb_data.reason), cb_data.reason); + check_vhpi_error(); + ret = -1; + } + + vhpiStateT cbState = (vhpiStateT)vhpi_get(vhpiStateP, new_hdl); + if (cbState != vhpiEnable) { + LOG_CRITICAL("VHPI ERROR: Registered callback isn't enabled! Got %d\n", cbState); + } + + vhpi_hdl = new_hdl; + m_state = GPI_PRIMED; + + return ret; + }; + + int cleanup_callback(void) { + vhpiStateT cbState = (vhpiStateT)vhpi_get(vhpiStateP, vhpi_hdl); + if (vhpiMature == cbState) + return vhpi_remove_cb(vhpi_hdl); + return 0; + } + + const char *reason_to_string(int reason) { + switch (reason) { + case vhpiCbValueChange: + return "vhpiCbValueChange"; + case vhpiCbStartOfNextCycle: + return "vhpiCbStartOfNextCycle"; + case vhpiCbStartOfPostponed: + return "vhpiCbStartOfPostponed"; + case vhpiCbEndOfTimeStep: + return "vhpiCbEndOfTimeStep"; + case vhpiCbNextTimeStep: + return "vhpiCbNextTimeStep"; + case vhpiCbAfterDelay: + return "vhpiCbAfterDelay"; + case vhpiCbStartOfSimulation: + return "vhpiCbStartOfSimulation"; + case vhpiCbEndOfSimulation: + return "vhpiCbEndOfSimulation"; + case vhpiCbEndOfProcesses: + return "vhpiCbEndOfProcesses"; + case vhpiCbLastKnownDeltaCycle: + return "vhpiCbLastKnownDeltaCycle"; + default: + return "unknown"; + } + } + +}; + +class vhpi_cb_startup : public vhpi_cb_hdl { +public: + vhpi_cb_startup(gpi_impl_interface *impl) : vhpi_cb_hdl(impl) { + cb_data.reason = vhpiCbStartOfSimulation; + } + + int run_callback(void) { + FENTER + gpi_sim_info_t sim_info; + sim_info.argc = 0; + sim_info.argv = NULL; + sim_info.product = gpi_copy_name(vhpi_get_str(vhpiNameP, NULL)); + sim_info.version = gpi_copy_name(vhpi_get_str(vhpiToolVersionP, NULL)); + gpi_embed_init(&sim_info); + + free(sim_info.product); + free(sim_info.version); + + FEXIT + + return 0; + } +}; + +class vhpi_cb_shutdown : public vhpi_cb_hdl { +public: + vhpi_cb_shutdown(gpi_impl_interface *impl) : vhpi_cb_hdl(impl) { + cb_data.reason = vhpiCbEndOfSimulation; + } + int run_callback(void) { + gpi_embed_end(); + return 0; + } +}; + +class vhpi_cb_timed : public vhpi_cb_hdl { +private: +vhpiTimeT time; +public: + vhpi_cb_timed(gpi_impl_interface *impl, uint64_t time_ps) : vhpi_cb_hdl(impl) { + time.high = (uint32_t)(time_ps>>32); + time.low = (uint32_t)(time_ps); + + cb_data.reason = vhpiCbAfterDelay; + cb_data.time = &time; + } +}; + + +class vhpi_impl : public gpi_impl_interface { +public: + vhpi_impl(const string& name) : gpi_impl_interface(name) { } + + /* Sim related */ + void sim_end(void) { } + void get_sim_time(uint32_t *high, uint32_t *low) { } + + /* Signal related */ + gpi_obj_hdl *get_root_handle(const char *name); + gpi_obj_hdl *get_handle_by_name(const char *name, gpi_obj_hdl *parent) { return NULL; } + gpi_obj_hdl *get_handle_by_index(gpi_obj_hdl *parent, uint32_t index) { return NULL; } + void free_handle(gpi_obj_hdl*) { } + gpi_iterator *iterate_handle(uint32_t type, gpi_obj_hdl *base) { return NULL; } + gpi_obj_hdl *next_handle(gpi_iterator *iterator) { return NULL; } + char* get_signal_value_binstr(gpi_obj_hdl *gpi_hdl) { return NULL; } + char* get_signal_name_str(gpi_obj_hdl *gpi_hdl); + char* get_signal_type_str(gpi_obj_hdl *gpi_hdl); + void set_signal_value_int(gpi_obj_hdl *gpi_hdl, int value); + void set_signal_value_str(gpi_obj_hdl *gpi_hdl, const char *str); // String of binary char(s) [1, 0, x, z] + + /* Callback related */ + gpi_cb_hdl *register_timed_callback(uint64_t time_ps); + gpi_cb_hdl *register_value_change_callback(gpi_obj_hdl *obj_hdl) { return NULL; } + gpi_cb_hdl *register_readonly_callback(void) { return NULL; } + gpi_cb_hdl *register_nexttime_callback(void) { return NULL; } + gpi_cb_hdl *register_readwrite_callback(void) { return NULL; } + int deregister_callback(gpi_cb_hdl *gpi_hdl) { return 0; } + + gpi_cb_hdl *create_cb_handle(void) { return NULL; } + void destroy_cb_handle(gpi_cb_hdl *gpi_hdl) { } +}; // Handle related functions /** @@ -216,12 +432,12 @@ static inline int __vhpi_register_cb(p_vhpi_cb user, vhpiCbDataT *cb_data) * If name is provided, we check the name against the available objects until * we find a match. If no match is found we return NULL */ -static gpi_sim_hdl vhpi_get_root_handle(const char* name) +gpi_obj_hdl *vhpi_impl::get_root_handle(const char* name) { FENTER vhpiHandleT root; vhpiHandleT dut; - gpi_sim_hdl rv; + gpi_obj_hdl *rv; root = vhpi_handle(vhpiRootInst, NULL); check_vhpi_error(); @@ -253,768 +469,122 @@ static gpi_sim_hdl vhpi_get_root_handle(const char* name) return NULL; } - rv = gpi_create_handle(); - rv->sim_hdl = dut; - - FEXIT - return rv; -} - - -static gpi_sim_hdl vhpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) -{ - FENTER - gpi_sim_hdl rv; - vhpiHandleT obj; - int len; - char *buff; - if (name) - len = strlen(name) + 1; - - buff = (char *)malloc(len); - if (buff == NULL) { - LOG_CRITICAL("VHPI: Attempting allocate string buffer failed!"); - return NULL; - } - - strncpy(buff, name, len); - obj = vhpi_handle_by_name(buff, (vhpiHandleT)(parent->sim_hdl)); - if (!obj) { - LOG_DEBUG("VHPI: Handle '%s' not found!", name); -// check_vhpi_error(); - return NULL; - } - - free(buff); - - rv = gpi_create_handle(); - rv->sim_hdl = obj; + rv = new vhpi_obj_hdl(root, this); FEXIT return rv; } -/** - * @brief Get a handle for an object based on its index within a parent - * - * @param parent handle to the parent - * @param indext Index to retrieve - * - * Can be used on bit-vectors to access a specific bit or - * memories to access an address - */ -static gpi_sim_hdl vhpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) +char *vhpi_impl::get_signal_name_str(gpi_obj_hdl *gpi_hdl) { FENTER - gpi_sim_hdl rv; - vhpiHandleT obj; - - obj = vhpi_handle_by_index(vhpiParamDecls, (vhpiHandleT)(parent->sim_hdl), index); - if (!obj) { - LOG_ERROR("VHPI: Handle idx '%d' not found!", index); - return NULL; - } - - rv = gpi_create_handle(); - rv->sim_hdl = obj; - - FEXIT - return rv; -} - - -// Functions for iterating over entries of a handle -// Returns an iterator handle which can then be used in gpi_next calls -// NB May return NULL if no objects of the request type exist -static gpi_iterator_hdl vhpi_iterate_hdl(uint32_t type, gpi_sim_hdl base) { - FENTER - - vhpiHandleT iterator; - - iterator = vhpi_iterator(type, (vhpiHandleT)(base->sim_hdl)); + vhpi_obj_hdl *vhpi_obj = reinterpret_cast(gpi_hdl); + const char *name = vhpi_get_str(vhpiFullNameP, vhpi_obj->vhpi_hdl); check_vhpi_error(); - + char *result = vhpi_obj->gpi_copy_name(name); + LOG_WARN("Signal name was %s", name); FEXIT - return (gpi_iterator_hdl)iterator; + return result; } -// Returns NULL when there are no more objects -static gpi_sim_hdl vhpi_next_hdl(gpi_iterator_hdl iterator) +char *vhpi_impl::get_signal_type_str(gpi_obj_hdl *gpi_hdl) { FENTER - gpi_sim_hdl rv = gpi_create_handle(); - - rv->sim_hdl = vhpi_scan((vhpiHandleT) iterator); + vhpi_obj_hdl *vhpi_obj = reinterpret_cast(gpi_hdl); + const char *name = vhpi_get_str(vhpiKindStrP, vhpi_obj->vhpi_hdl); check_vhpi_error(); - if (!rv->sim_hdl) { - gpi_free_handle(rv); - rv = NULL; - } + char *result = vhpi_obj->gpi_copy_name(name); + LOG_WARN("Signal type was %s", name); FEXIT - return rv; -} - -static void vhpi_get_sim_time(uint32_t *high, uint32_t *low) -{ - vhpiTimeT vhpi_time_s; - vhpi_get_time(&vhpi_time_s, NULL); - check_vhpi_error(); - *high = vhpi_time_s.high; - *low = vhpi_time_s.low; -} - -// Value related functions -static vhpiEnumT chr2vhpi(const char value) { - switch (value) { - case '0': - return vhpi0; - case '1': - return vhpi1; - case 'U': - case 'u': - return vhpiU; - case 'Z': - case 'z': - return vhpiZ; - case 'X': - case 'x': - return vhpiX; - default: - return vhpiDontCare; - } + return result; } -// Unfortunately it seems that format conversion is not well supported -// We have to set values using vhpiEnum* -static void vhpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) +gpi_cb_hdl *vhpi_impl::register_timed_callback(uint64_t time_ps) { - FENTER - vhpiValueT value_s; - int size, i; - - // Determine the type of object, either scalar or vector - value_s.format = vhpiObjTypeVal; - value_s.bufSize = 0; - value_s.value.str = NULL; - - vhpi_get_value((vhpiHandleT)(gpi_hdl->sim_hdl), &value_s); - check_vhpi_error(); + vhpi_cb_timed *hdl = new vhpi_cb_timed(this, time_ps); - switch (value_s.format) { - case vhpiEnumVal: - case vhpiLogicVal: { - value_s.value.enumv = value ? vhpi1 : vhpi0; - break; - } - - case vhpiEnumVecVal: - case vhpiLogicVecVal: { - size = vhpi_get(vhpiSizeP, (vhpiHandleT)(gpi_hdl->sim_hdl)); - value_s.bufSize = size*sizeof(vhpiEnumT); - value_s.value.enumvs = (vhpiEnumT *)malloc(size*sizeof(vhpiEnumT)); - - for (i=0; iarm_callback()) { + delete(hdl); + hdl = NULL; } - vhpi_put_value((vhpiHandleT)(gpi_hdl->sim_hdl), &value_s, vhpiForcePropagate); - check_vhpi_error(); - - if (vhpiEnumVecVal == value_s.format) - free(value_s.value.enumvs); - FEXIT + return hdl; } - - // Unfortunately it seems that format conversion is not well supported // We have to set values using vhpiEnum* -static void vhpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) +void vhpi_impl::set_signal_value_int(gpi_obj_hdl *gpi_hdl, int value) { FENTER - vhpiValueT value_s; - int len, size, i; - const char *ptr; - - // Determine the type of object, either scalar or vector - value_s.format = vhpiObjTypeVal; - value_s.bufSize = 0; - value_s.value.str = NULL; - - vhpi_get_value((vhpiHandleT)(gpi_hdl->sim_hdl), &value_s); - check_vhpi_error(); - - switch (value_s.format) { - case vhpiEnumVal: - case vhpiLogicVal: { - value_s.value.enumv = chr2vhpi(*str); - break; - } - - case vhpiEnumVecVal: - case vhpiLogicVecVal: { - len = strlen(str); - size = vhpi_get(vhpiSizeP, (vhpiHandleT)(gpi_hdl->sim_hdl)); - value_s.bufSize = size*sizeof(vhpiEnumT); - value_s.value.enumvs = (vhpiEnumT *)malloc(size*sizeof(vhpiEnumT)); - - // Initialise to 0s - for (i=0; i(gpi_hdl); + vhpi_obj->write_new_value(value); - break; - } - - default: { - LOG_CRITICAL("Unable to assign value to %s (%d) format object", - vhpi_format_to_string(value_s.format), value_s.format); - } - } - - vhpi_put_value((vhpiHandleT)(gpi_hdl->sim_hdl), &value_s, vhpiForcePropagate); - check_vhpi_error(); - if(value_s.format == vhpiEnumVecVal) - free(value_s.value.enumvs); FEXIT } -static char *vhpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) +void vhpi_impl::set_signal_value_str(gpi_obj_hdl *gpi_hdl, const char *str) { FENTER - vhpiValueT value_s; - vhpiValueT *value_p = &value_s; - char *result; - size_t size; - value_p->format = vhpiBinStrVal; - - -// FIXME Seem to have a problem here -// According to VHPI spec we should call vhpi_get_value once to determine -// how much memory to allocate for the result... it appears that we just -// get bogus values back so we'll use a fixed size buffer for now -#if 0 - // Call once to find out how long the string is - vhpi_get_value((vhpiHandleT)(gpi_hdl->sim_hdl), value_p); - check_vhpi_error(); - size = value_p->bufSize; - LOG_ERROR("After initial call to get value: bufSize=%u", size); -#else - size = 512; -#endif - - result = (char *)malloc(size); - if (result == NULL) { - LOG_CRITICAL("VHPI: Attempting allocate string buffer failed!"); - } - - // Call again to get the value - value_p->bufSize = size; - value_p->value.str = result; - vhpi_get_value((vhpiHandleT)(gpi_hdl->sim_hdl), value_p); - check_vhpi_error(); + vhpi_obj_hdl *vhpi_obj = reinterpret_cast(gpi_hdl); + vhpi_obj->write_new_value(str); FEXIT - return result; } -static char *vhpi_get_signal_name_str(gpi_sim_hdl gpi_hdl) -{ - FENTER - const char *name = vhpi_get_str(vhpiFullNameP, (vhpiHandleT)(gpi_hdl->sim_hdl)); - check_vhpi_error(); - char *result = gpi_copy_name(name); - FEXIT - return result; -} +extern "C" { -static char *vhpi_get_signal_type_str(gpi_sim_hdl gpi_hdl) -{ - FENTER - const char *name = vhpi_get_str(vhpiKindStrP, (vhpiHandleT)(gpi_hdl->sim_hdl)); - check_vhpi_error(); - char *result = gpi_copy_name(name); - FEXIT - return result; -} +static vhpi_cb_hdl *sim_init_cb; +static vhpi_cb_hdl *sim_finish_cb; +static vhpi_impl *vhpi_table; -// Callback related functions -static void handle_vhpi_callback(const vhpiCbDataT *cb_data) +// Main entry point for callbacks from simulator +void handle_vhpi_callback(const vhpiCbDataT *cb_data) { FENTER - vhpiHandleT old_cb; - p_vhpi_cb user_data; - user_data = (p_vhpi_cb)cb_data->user_data; + vhpi_cb_hdl *cb_hdl = (vhpi_cb_hdl*)cb_data->user_data; - if (!user_data) - LOG_CRITICAL("VHPI: Callback data corrupted"); + if (!cb_hdl) + LOG_CRITICAL("VPI: Callback data corrupted"); - user_data->state = VHPI_PRE_CALL; - old_cb = user_data->cb_hdl; - gpi_handle_callback(&user_data->gpi_cb_data.hdl); + LOG_DEBUG("Running %p", cb_hdl); - if (old_cb == user_data->cb_hdl) { - - // Don't de-register recurring callbacks - VHPI only seems to allow - // a single registration per recurring callback. For edge events on - // signals etc. we never want to remove. - vhpiStateT cbState = vhpi_get(vhpiStateP, user_data->cb_hdl); - if (vhpiMature == cbState) - gpi_deregister_callback(&user_data->gpi_cb_data.hdl); + if (cb_hdl->get_call_state() == GPI_PRIMED) { + cb_hdl->set_call_state(GPI_PRE_CALL); + cb_hdl->run_callback(); + cb_hdl->set_call_state(GPI_POST_CALL); } - /* A request to delete could have been done - * inside gpi_function - */ - if (user_data->state == VHPI_DELETE) - gpi_free_cb_handle(&user_data->gpi_cb_data.hdl); - else - user_data->state = VHPI_POST_CALL; + gpi_deregister_callback(cb_hdl); FEXIT return; }; -/* Allocates memory that will persist for the lifetime of the - * handle, this may be short or long. A call to create - * must have a matching call to destroy at some point - */ -static gpi_cb_hdl vhpi_create_cb_handle(void) -{ - gpi_cb_hdl ret = NULL; - FENTER - p_vhpi_cb user_data = calloc(1, sizeof(*user_data)); - - if (user_data) - ret = &user_data->gpi_cb_data; - - FEXIT - return ret; -} - -/* Destroys the memory associated with the sim handle - * this can only be called on a handle that has been - * returned by a call to gpi_create_cb_handle - */ -static void vhpi_destroy_cb_handle(gpi_cb_hdl hdl) -{ - /* Check that is has been called, if this has not - * happend then also close down the sim data as well - */ - FENTER - p_vhpi_cb user_data = gpi_container_of(hdl, s_vhpi_cb, gpi_cb_data); - - free(user_data); - FEXIT -} - - -static void *vhpi_get_callback_data(gpi_sim_hdl gpi_hdl) -{ - FENTER - gpi_cb_hdl gpi_user_data; - gpi_user_data = gpi_container_of(gpi_hdl, gpi_cb_hdl_t, hdl); - return gpi_user_data->gpi_cb_data; -} - - -/* Deregister a prior set up callback with the simulator - * The handle must have been allocated with gpi_create_cb_handle - * This can be called at any point between - * gpi_create_cb_handle and gpi_free_cb_handle - */ -static int vhpi_deregister_callback(gpi_sim_hdl gpi_hdl) -{ - FENTER - p_vhpi_cb vhpi_user_data; - gpi_cb_hdl gpi_user_data; - int rc = 1; - - gpi_user_data = gpi_container_of(gpi_hdl, gpi_cb_hdl_t, hdl); - vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); - - if (vhpi_user_data->cb_hdl != NULL) { - rc = vhpi_user_data->vhpi_cleanup(vhpi_user_data); - vhpi_user_data->cb_hdl = NULL; - } - - FEXIT - GPI_RET(rc); -} - -// Call when the handle relates to a one time callback -// No need to call vhpi_deregister_cb as the sim will -// do this but do need to destroy the handle -static int vhpi_free_one_time(p_vhpi_cb user_data) -{ - FENTER - int rc = 0; - vhpiHandleT cb_hdl = user_data->cb_hdl; - if (!cb_hdl) { - LOG_CRITICAL("VHPI: passed a NULL pointer : ABORTING"); - exit(1); - } - - // If the callback has not been called we also need to call - // remove as well - if (user_data->state == VHPI_PRIMED) { - rc = vhpi_remove_cb(cb_hdl); - if (!rc) { - check_vhpi_error(); - return rc; - } - - rc = vhpi_release_handle(cb_hdl); - if (!rc) { - check_vhpi_error(); - return rc; - } - } - FEXIT - return rc; -} - -// Call when the handle relates to recurring callback -// Unregister must be called when not needed and this -// will clean all memory allocated by the sim -static int vhpi_free_recurring(p_vhpi_cb user_data) -{ - FENTER - int rc; - vhpiHandleT cb_hdl = user_data->cb_hdl; - if (!cb_hdl) { - LOG_CRITICAL("VHPI: passed a NULL pointer : ABORTING"); - exit(1); - } - - rc = vhpi_remove_cb(cb_hdl); - check_vhpi_error(); - FEXIT - return rc; -} - -/* These functions request a callback to be active with the current - * handle and associated data. A callback handle needs to have been - * allocated with gpi_create_cb_handle first - */ - -static int vhpi_register_value_change_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data, - gpi_sim_hdl gpi_hdl) -{ - FENTER - - int ret; - vhpiCbDataT cb_data_s; - vhpiTimeT time; - p_vhpi_cb vhpi_user_data; - gpi_cb_hdl gpi_user_data; - - gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); - vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); - - vhpi_user_data->vhpi_cleanup = vhpi_free_recurring; - vhpi_user_data->cb_value.format = vhpiIntVal; - - cb_data_s.reason = vhpiCbValueChange; - cb_data_s.cb_rtn = handle_vhpi_callback; - cb_data_s.obj = (vhpiHandleT)(gpi_hdl->sim_hdl); - cb_data_s.time = &time; - cb_data_s.value = &vhpi_user_data->cb_value; - cb_data_s.user_data = (char *)vhpi_user_data; - - ret = __vhpi_register_cb(vhpi_user_data, &cb_data_s); - - FEXIT - - return ret; -} - - -static int vhpi_register_readonly_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) -{ - FENTER - - int ret; - vhpiCbDataT cb_data_s; - vhpiTimeT time; - p_vhpi_cb vhpi_user_data; - gpi_cb_hdl gpi_user_data; - - gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); - vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); - - vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; - - cb_data_s.reason = vhpiCbLastKnownDeltaCycle; - cb_data_s.cb_rtn = handle_vhpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = &time; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)vhpi_user_data; - - ret = __vhpi_register_cb(vhpi_user_data, &cb_data_s); - - FEXIT - - return ret; -} - -static int vhpi_register_readwrite_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) -{ - FENTER - - - int ret; - vhpiCbDataT cb_data_s; - vhpiTimeT time; - p_vhpi_cb vhpi_user_data; - gpi_cb_hdl gpi_user_data; - - gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); - vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); - - vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; - - cb_data_s.reason = vhpiCbEndOfProcesses; - cb_data_s.cb_rtn = handle_vhpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = &time; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)vhpi_user_data; - - ret = __vhpi_register_cb(vhpi_user_data, &cb_data_s); - - FEXIT - - return ret; -} - -static int vhpi_register_nexttime_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) -{ - FENTER - - int ret; - vhpiCbDataT cb_data_s; - vhpiTimeT time; - p_vhpi_cb vhpi_user_data; - gpi_cb_hdl gpi_user_data; - - gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); - vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); - - vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; - - cb_data_s.reason = vhpiCbNextTimeStep; - cb_data_s.cb_rtn = handle_vhpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = &time; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)vhpi_user_data; - - ret = __vhpi_register_cb(vhpi_user_data, &cb_data_s); - - FEXIT - - return ret; -} - -static int vhpi_register_timed_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data, - uint64_t time_ps) +static void register_initial_callback(void) { FENTER - - int ret; - vhpiCbDataT cb_data_s; - vhpiTimeT time_s; - p_vhpi_cb vhpi_user_data; - gpi_cb_hdl gpi_user_data; - - gpi_user_data = gpi_container_of(cb, gpi_cb_hdl_t, hdl); - vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); - - vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; - - time_s.high = (uint32_t)(time_ps>>32); - time_s.low = (uint32_t)(time_ps); - - cb_data_s.reason = vhpiCbAfterDelay; - cb_data_s.cb_rtn = handle_vhpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = &time_s; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)vhpi_user_data; - - ret = __vhpi_register_cb(vhpi_user_data, &cb_data_s); - + sim_init_cb = new vhpi_cb_startup(vhpi_table); + sim_init_cb->arm_callback(); FEXIT - - return ret; } - -// If the Pything world wants things to shut down then unregister -// the callback for end of sim -static void vhpi_sim_end(void) +static void register_final_callback(void) { FENTER - - sim_finish_cb = NULL; - vhpi_control(vhpiFinish); - check_vhpi_error(); + sim_finish_cb = new vhpi_cb_shutdown(vhpi_table); + sim_finish_cb->arm_callback(); FEXIT } -static s_gpi_impl_tbl vhpi_table = { - .sim_end = vhpi_sim_end, - .iterate_handle = vhpi_iterate_hdl, - .next_handle = vhpi_next_hdl, - .create_cb_handle = vhpi_create_cb_handle, - .destroy_cb_handle = vhpi_destroy_cb_handle, - .deregister_callback = vhpi_deregister_callback, - .get_root_handle = vhpi_get_root_handle, - .get_sim_time = vhpi_get_sim_time, - .get_handle_by_name = vhpi_get_handle_by_name, - .get_handle_by_index = vhpi_get_handle_by_index, - .get_signal_name_str = vhpi_get_signal_name_str, - .get_signal_type_str = vhpi_get_signal_type_str, - .get_signal_value_binstr = vhpi_get_signal_value_binstr, - .set_signal_value_int = vhpi_set_signal_value_int, - .set_signal_value_str = vhpi_set_signal_value_str, - .register_timed_callback = vhpi_register_timed_callback, - .register_readwrite_callback = vhpi_register_readwrite_callback, - .register_nexttime_callback = vhpi_register_nexttime_callback, - .register_value_change_callback = vhpi_register_value_change_callback, - .register_readonly_callback = vhpi_register_readonly_callback, - .get_callback_data = vhpi_get_callback_data, -}; - static void register_embed(void) { - FENTER - gpi_register_impl(&vhpi_table, 0xfeed); + vhpi_table = new vhpi_impl("VHPI"); + gpi_register_impl(vhpi_table); gpi_embed_init_python(); - FEXIT -} - - -static int handle_sim_init(void *gpi_cb_data) -{ - FENTER - gpi_sim_info_t sim_info; - sim_info.argc = 0; - sim_info.argv = NULL; - sim_info.product = gpi_copy_name(vhpi_get_str(vhpiNameP, NULL)); - sim_info.version = gpi_copy_name(vhpi_get_str(vhpiToolVersionP, NULL)); - gpi_embed_init(&sim_info); - - free(sim_info.product); - free(sim_info.version); - - FEXIT - - return 0; -} - -static void register_initial_callback(void) -{ - FENTER - - vhpiCbDataT cb_data_s; - p_vhpi_cb vhpi_user_data; - gpi_cb_hdl gpi_user_data; - - - sim_init_cb = gpi_create_cb_handle(); - - gpi_user_data = gpi_container_of(sim_init_cb, gpi_cb_hdl_t, hdl); - vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); - - gpi_user_data->gpi_cb_data = NULL; - gpi_user_data->gpi_function = handle_sim_init; - - vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; - - cb_data_s.reason = vhpiCbStartOfSimulation; - cb_data_s.cb_rtn = handle_vhpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = NULL; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)vhpi_user_data; - - __vhpi_register_cb(vhpi_user_data, &cb_data_s); - - FEXIT -} - -static int handle_sim_end(void *gpi_cb_data) -{ - FENTER - if (sim_finish_cb) { - sim_finish_cb = NULL; - /* This means that we have been asked to close */ - embed_sim_event(SIM_FAIL, "Simulator shutdown prematurely"); - } /* Other sise we have already been here from the top down so do not need - to inform the upper layers that anything has occoured */ - gpi_free_cb_handle(sim_init_cb); - FEXIT - - return 0; -} - -static void register_final_callback(void) -{ - FENTER - - vhpiCbDataT cb_data_s; - p_vhpi_cb vhpi_user_data; - gpi_cb_hdl gpi_user_data; - - sim_finish_cb = gpi_create_cb_handle(); - - gpi_user_data = gpi_container_of(sim_finish_cb, gpi_cb_hdl_t, hdl); - vhpi_user_data = gpi_container_of(gpi_user_data, s_vhpi_cb, gpi_cb_data); - - gpi_user_data->gpi_cb_data = NULL; - gpi_user_data->gpi_function = handle_sim_end; - vhpi_user_data->vhpi_cleanup = vhpi_free_one_time; - - cb_data_s.reason = vhpiCbEndOfSimulation; - cb_data_s.cb_rtn = handle_vhpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = NULL; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)vhpi_user_data; - - /* We ignore the return value here as VCS does some silly - * things on comilation that means it tries to run through - * the vlog_startup_routines and so call this routine - */ - __vhpi_register_cb(vhpi_user_data, &cb_data_s); - - FEXIT } // pre-defined VHPI registration table @@ -1036,3 +606,5 @@ void vhpi_startup_routines_bootstrap(void) { routine(); } } + +} \ No newline at end of file diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index f4010b8c..cc23995e 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -27,137 +27,245 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ -#include "../gpi/gpi_priv.h" -#include +#include "VpiImpl.h" -#define VPI_CHECKING 1 +extern "C" int32_t handle_vpi_callback(p_cb_data cb_data); -extern "C" { -int32_t handle_vpi_callback(p_cb_data cb_data); -int __check_vpi_error(const char *func, long line); +const char *VpiObjHdl::reason_to_string(int reason) +{ + switch (reason) { + case cbValueChange: + return "cbValueChange"; + case cbAtStartOfSimTime: + return "cbAtStartOfSimTime"; + case cbReadWriteSynch: + return "cbReadWriteSynch"; + case cbReadOnlySynch: + return "cbReadOnlySynch"; + case cbNextSimTime: + return "cbNextSimTime"; + case cbAfterDelay: + return "cbAfterDelay"; + case cbStartOfSimulation: + return "cbStartOfSimulation"; + case cbEndOfSimulation: + return "cbEndOfSimulation"; + default: + return "unknown"; + } } - -// Should be run after every VPI call to check error status -int __check_vpi_error(const char *func, long line) +/** + * @brief Get a handle to an object under the scope of parent + * + * @param name of the object to find, below this object in the hierachy + * + * @return gpi_sim_hdl for the new object or NULL if object not found + */ +GpiObjHdl *VpiObjHdl::get_handle_by_name(const char *name) { - int level=0; -#if VPI_CHECKING - s_vpi_error_info info; - int loglevel; - level = vpi_chk_error(&info); - if (level == 0) - return 0; - - switch (level) { - case vpiNotice: - loglevel = GPIInfo; - break; - case vpiWarning: - loglevel = GPIWarning; - break; - case vpiError: - loglevel = GPIError; - break; - case vpiSystem: - case vpiInternal: - loglevel = GPICritical; - break; + FENTER + GpiObjHdl *rv = NULL; + vpiHandle obj; + vpiHandle iterator; + int len; + char *buff; + + // Structures aren't technically a scope, according to the LRM. If parent + // is a structure then we have to iterate over the members comparing names + if (vpiStructVar == vpi_get(vpiType, vpi_hdl)) { + + iterator = vpi_iterate(vpiMember, vpi_hdl); + + for (obj = vpi_scan(iterator); obj != NULL; obj = vpi_scan(iterator)) { + + if (!strcmp(name, strrchr(vpi_get_str(vpiName, obj), 46) + 1)) + break; + } + + if (!obj) + return NULL; + + // Need to free the iterator if it didn't return NULL + if (!vpi_free_object(iterator)) { + LOG_WARN("VPI: Attempting to free root iterator failed!"); + check_vpi_error(); + } + + goto success; } - gpi_log("cocotb.gpi", loglevel, __FILE__, func, line, - "VPI Error level %d\nPROD %s\nCODE %s\nFILE %s", - info.message, info.product, info.code, info.file); + if (name) + len = strlen(name) + 1; -#endif - return level; -} + buff = (char *)malloc(len); + if (buff == NULL) { + LOG_CRITICAL("VPI: Attempting allocate string buffer failed!"); + return NULL; + } -#define check_vpi_error() do { \ - __check_vpi_error(__func__, __LINE__); \ -} while (0) + strncpy(buff, name, len); + obj = vpi_handle_by_name(buff, vpi_hdl); + if (!obj) { + LOG_DEBUG("VPI: Handle '%s' not found!", name); -class vpi_obj_hdl : public gpi_obj_hdl { -public: - vpi_obj_hdl(vpiHandle hdl, gpi_impl_interface *impl) : gpi_obj_hdl(impl), - vpi_hdl(hdl) { } - virtual ~vpi_obj_hdl() { } -public: - vpiHandle vpi_hdl; + // NB we deliberately don't dump an error message here because it's + // a valid use case to attempt to grab a signal by name - for example + // optional signals on a bus. + // check_vpi_error(); + free(buff); + return NULL; + } -}; + free(buff); -class vpi_cb_hdl : public gpi_cb_hdl { -public: - vpiHandle vpi_hdl; +success: + // rv = new VpiObjHdl(obj); -public: - vpi_cb_hdl(gpi_impl_interface *impl) : gpi_cb_hdl(impl), - vpi_hdl(NULL) { } - virtual int cleanup_callback(void) { return 0; } - virtual ~vpi_cb_hdl() { } - -protected: - /* If the user data already has a callback handle then deregister - * before getting the new one - */ - int register_cb(p_cb_data cb_data) { - if (m_state == GPI_PRIMED) { - fprintf(stderr, - "Attempt to prime an already primed trigger for %s!\n", - vpi_reason_to_string(cb_data->reason)); - } + FEXIT + return rv; +} - if (vpi_hdl != NULL) { - fprintf(stderr, - "We seem to already be registered, deregistering %s!\n", - vpi_reason_to_string(cb_data->reason)); +/** + * @brief Get a handle for an object based on its index within a parent + * + * @param parent handle to the parent + * @param indext Index to retrieve + * + * Can be used on bit-vectors to access a specific bit or + * memories to access an address + */ +GpiObjHdl *VpiObjHdl::get_handle_by_index(uint32_t index) +{ + FENTER + vpiHandle obj; + GpiObjHdl *rv = NULL; - cleanup_callback(); - } + obj = vpi_handle_by_index(vpi_hdl, index); + if (!obj) { + LOG_ERROR("VPI: Handle idx '%d' not found!", index); + return NULL; + } - vpiHandle new_hdl = vpi_register_cb(cb_data); - int ret = 0; + //rv = new VpiObjHdl(obj, this); - if (!new_hdl) { - LOG_CRITICAL("VPI: Unable to register callback a handle for VPI type %s(%d)", - vpi_reason_to_string(cb_data->reason), cb_data->reason); - check_vpi_error(); - ret = -1; - } + FEXIT + return rv; +} + +/* If the user data already has a callback handle then deregister + * before getting the new one + */ +int VpiCbHdl::register_cb(p_cb_data cb_data) { + if (m_state == GPI_PRIMED) { + fprintf(stderr, + "Attempt to prime an already primed trigger for %s!\n", + reason_to_string(cb_data->reason)); + } - vpi_hdl = new_hdl; - m_state = GPI_PRIMED; + if (vpi_hdl != NULL) { + fprintf(stderr, + "We seem to already be registered, deregistering %s!\n", + reason_to_string(cb_data->reason)); - return ret; + cleanup_callback(); } - const char *vpi_reason_to_string(int reason) - { - switch (reason) { - case cbValueChange: - return "cbValueChange"; - case cbAtStartOfSimTime: - return "cbAtStartOfSimTime"; - case cbReadWriteSynch: - return "cbReadWriteSynch"; - case cbReadOnlySynch: - return "cbReadOnlySynch"; - case cbNextSimTime: - return "cbNextSimTime"; - case cbAfterDelay: - return "cbAfterDelay"; - case cbStartOfSimulation: - return "cbStartOfSimulation"; - case cbEndOfSimulation: - return "cbEndOfSimulation"; - default: - return "unknown"; - } + vpiHandle new_hdl = vpi_register_cb(cb_data); + int ret = 0; + + if (!new_hdl) { + LOG_CRITICAL("VPI: Unable to register callback a handle for VPI type %s(%d)", + reason_to_string(cb_data->reason), cb_data->reason); + check_vpi_error(); + ret = -1; } -}; + vpi_hdl = new_hdl; + m_state = GPI_PRIMED; + + return ret; +} + +int VpiCbHdl::cleanup_callback(void) +{ + return 0; +} + +int VpiCbHdl::arm_callback(void) +{ + return 0; +} + +int VpiStartupCbHdl::run_callback(void) { + s_vpi_vlog_info info; + gpi_sim_info_t sim_info; + + vpi_get_vlog_info(&info); + + sim_info.argc = info.argc; + sim_info.argv = info.argv; + sim_info.product = info.product; + sim_info.version = info.version; + + gpi_embed_init(&sim_info); + + return 0; +} + +int VpiStartupCbHdl::arm_callback(void) { + s_cb_data cb_data_s; + + cb_data_s.reason = cbStartOfSimulation; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = NULL; + cb_data_s.value = NULL; + cb_data_s.user_data = (char*)this; + + return register_cb(&cb_data_s); +} + +int VpiShutdownCbHdl::run_callback(void) { + LOG_WARN("Shutdown called"); + gpi_embed_end(); + return 0; +} + +int VpiShutdownCbHdl::arm_callback(void) { + s_cb_data cb_data_s; + + cb_data_s.reason = cbEndOfSimulation; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = NULL; + cb_data_s.value = NULL; + cb_data_s.user_data = (char*)this; + + return register_cb(&cb_data_s); +} + + +int VpiTimedCbHdl::arm_callback(uint64_t time_ps) { + s_cb_data cb_data_s; + s_vpi_time vpi_time_s; + + vpi_time_s.type = vpiSimTime; + vpi_time_s.high = (uint32_t)(time_ps>>32); + vpi_time_s.low = (uint32_t)(time_ps); + + cb_data_s.reason = cbAfterDelay; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &vpi_time_s; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)this; + + return register_cb(&cb_data_s); +} + +#if 0 class vpi_onetime_cb : public vpi_cb_hdl { public: vpi_onetime_cb(gpi_impl_interface *impl) : vpi_cb_hdl(impl) { } @@ -235,41 +343,6 @@ class vpi_cb_value_change : public vpi_recurring_cb { virtual ~vpi_cb_value_change() { } }; -class vpi_cb_startup : public vpi_onetime_cb { -public: - vpi_cb_startup(gpi_impl_interface *impl) : vpi_onetime_cb(impl) { } - int run_callback(void) { - s_vpi_vlog_info info; - gpi_sim_info_t sim_info; - - vpi_get_vlog_info(&info); - - sim_info.argc = info.argc; - sim_info.argv = info.argv; - sim_info.product = info.product; - sim_info.version = info.version; - - gpi_embed_init(&sim_info); - - return 0; - } - - int arm_callback(void) { - s_cb_data cb_data_s; - - cb_data_s.reason = cbStartOfSimulation; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = NULL; - cb_data_s.value = NULL; - cb_data_s.user_data = (char*)this; - - return register_cb(&cb_data_s); - } - - virtual ~vpi_cb_startup() { } -}; - class vpi_cb_readonly : public vpi_onetime_cb { public: vpi_cb_readonly(gpi_impl_interface *impl) : vpi_onetime_cb(impl) { } @@ -295,31 +368,6 @@ class vpi_cb_readonly : public vpi_onetime_cb { }; -class vpi_cb_shutdown : public vpi_onetime_cb { -public: - vpi_cb_shutdown(gpi_impl_interface *impl) : vpi_onetime_cb(impl) { } - int run_callback(void) { - LOG_WARN("Shutdown called"); - gpi_embed_end(); - return 0; - } - - int arm_callback(void) { - s_cb_data cb_data_s; - - cb_data_s.reason = cbEndOfSimulation; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = NULL; - cb_data_s.value = NULL; - cb_data_s.user_data = (char*)this; - - return register_cb(&cb_data_s); - } - - virtual ~vpi_cb_shutdown() { } -}; - class vpi_cb_timed : public vpi_onetime_cb { public: vpi_cb_timed(gpi_impl_interface *impl) : vpi_onetime_cb(impl) { } @@ -395,584 +443,4 @@ class vpi_cb_nexttime : public vpi_onetime_cb { virtual ~vpi_cb_nexttime() { } }; -class vpi_impl : public gpi_impl_interface { -public: - vpi_impl(const string& name) : gpi_impl_interface(name) { } - - /* Sim related */ - void sim_end(void); - void get_sim_time(uint32_t *high, uint32_t *low); - - /* Signal related */ - gpi_obj_hdl *get_root_handle(const char *name); - gpi_obj_hdl *get_handle_by_name(const char *name, gpi_obj_hdl *parent); - gpi_obj_hdl *get_handle_by_index(gpi_obj_hdl *parent, uint32_t index); - void free_handle(gpi_obj_hdl*) { } - gpi_iterator *iterate_handle(uint32_t type, gpi_obj_hdl *base) { return NULL; } - gpi_obj_hdl *next_handle(gpi_iterator *iterator) { return NULL; } - char* get_signal_value_binstr(gpi_obj_hdl *gpi_hdl); - char* get_signal_name_str(gpi_obj_hdl *gpi_hdl); - char* get_signal_type_str(gpi_obj_hdl *gpi_hdl); - void set_signal_value_int(gpi_obj_hdl *gpi_hdl, int value); - void set_signal_value_str(gpi_obj_hdl *gpi_hdl, const char *str); // String of binary char(s) [1, 0, x, z] - - /* Callback related */ - gpi_cb_hdl *register_timed_callback(uint64_t time_ps); - gpi_cb_hdl *register_value_change_callback(gpi_obj_hdl *obj_hdl); - gpi_cb_hdl *register_readonly_callback(void); - gpi_cb_hdl *register_nexttime_callback(void); - gpi_cb_hdl *register_readwrite_callback(void); - int deregister_callback(gpi_cb_hdl *gpi_hdl); - - gpi_cb_hdl *create_cb_handle(void) { return NULL; } - void destroy_cb_handle(gpi_cb_hdl *gpi_hdl) { } -}; - -extern "C" { - -static vpi_cb_hdl *sim_init_cb; -static vpi_cb_hdl *sim_finish_cb; -static vpi_impl *vpi_table; - -} - -void vpi_impl::get_sim_time(uint32_t *high, uint32_t *low) -{ - s_vpi_time vpi_time_s; - vpi_time_s.type = vpiSimTime;//vpiScaledRealTime; //vpiSimTime; - vpi_get_time(NULL, &vpi_time_s); - check_vpi_error(); - *high = vpi_time_s.high; - *low = vpi_time_s.low; -} - -gpi_obj_hdl *vpi_impl::get_root_handle(const char* name) -{ - FENTER - vpiHandle root; - vpiHandle iterator; - vpi_obj_hdl *rv; - - // vpi_iterate with a ref of NULL returns the top level module - iterator = vpi_iterate(vpiModule, NULL); - check_vpi_error(); - - for (root = vpi_scan(iterator); root != NULL; root = vpi_scan(iterator)) { - - if (name == NULL || !strcmp(name, vpi_get_str(vpiFullName, root))) - break; - } - - if (!root) { - check_vpi_error(); - goto error; - } - - // Need to free the iterator if it didn't return NULL - if (!vpi_free_object(iterator)) { - LOG_WARN("VPI: Attempting to free root iterator failed!"); - check_vpi_error(); - } - - rv = new vpi_obj_hdl(root, this); - - FEXIT - return rv; - - error: - - LOG_CRITICAL("VPI: Couldn't find root handle %s", name); - - iterator = vpi_iterate(vpiModule, NULL); - - for (root = vpi_scan(iterator); root != NULL; root = vpi_scan(iterator)) { - - LOG_CRITICAL("VPI: Toplevel instances: %s != %s...", name, vpi_get_str(vpiFullName, root)); - - if (name == NULL || !strcmp(name, vpi_get_str(vpiFullName, root))) - break; - } - - FEXIT - return NULL; -} - -/** - * @brief Get a handle to an object under the scope of parent - * - * @param name of the object to find - * @param parent handle to parent object defining the scope to search - * - * @return gpi_sim_hdl for the new object or NULL if object not found - */ -gpi_obj_hdl *vpi_impl::get_handle_by_name(const char *name, gpi_obj_hdl *parent) -{ - FENTER - gpi_obj_hdl *rv; - vpiHandle obj; - vpiHandle iterator; - int len; - char *buff; - vpi_obj_hdl *vpi_obj = reinterpret_cast(parent); - - // Structures aren't technically a scope, according to the LRM. If parent - // is a structure then we have to iterate over the members comparing names - if (vpiStructVar == vpi_get(vpiType, vpi_obj->vpi_hdl)) { - - iterator = vpi_iterate(vpiMember, vpi_obj->vpi_hdl); - - for (obj = vpi_scan(iterator); obj != NULL; obj = vpi_scan(iterator)) { - - if (!strcmp(name, strrchr(vpi_get_str(vpiName, obj), 46) + 1)) - break; - } - - if (!obj) - return NULL; - - // Need to free the iterator if it didn't return NULL - if (!vpi_free_object(iterator)) { - LOG_WARN("VPI: Attempting to free root iterator failed!"); - check_vpi_error(); - } - - goto success; - } - - if (name) - len = strlen(name) + 1; - - buff = (char *)malloc(len); - if (buff == NULL) { - LOG_CRITICAL("VPI: Attempting allocate string buffer failed!"); - return NULL; - } - - strncpy(buff, name, len); - obj = vpi_handle_by_name(buff, vpi_obj->vpi_hdl); - if (!obj) { - LOG_DEBUG("VPI: Handle '%s' not found!", name); - - // NB we deliberately don't dump an error message here because it's - // a valid use case to attempt to grab a signal by name - for example - // optional signals on a bus. - // check_vpi_error(); - free(buff); - return NULL; - } - - free(buff); - -success: - rv = new vpi_obj_hdl(obj, this); - - FEXIT - return rv; -} - -/** - * @brief Get a handle for an object based on its index within a parent - * - * @param parent handle to the parent - * @param indext Index to retrieve - * - * Can be used on bit-vectors to access a specific bit or - * memories to access an address - */ -gpi_obj_hdl *vpi_impl::get_handle_by_index(gpi_obj_hdl *parent, uint32_t index) -{ - FENTER - vpiHandle obj; - gpi_obj_hdl *rv; - vpi_obj_hdl *vpi_obj = reinterpret_cast(parent); - - obj = vpi_handle_by_index(vpi_obj->vpi_hdl, index); - if (!obj) { - LOG_ERROR("VPI: Handle idx '%d' not found!", index); - return NULL; - } - - rv = new vpi_obj_hdl(obj, this); - - FEXIT - return rv; -} - -// Value related functions -void vpi_impl::set_signal_value_int(gpi_obj_hdl *gpi_hdl, int value) -{ - FENTER - s_vpi_value value_s; - p_vpi_value value_p = &value_s; - - value_p->value.integer = value; - value_p->format = vpiIntVal; - - s_vpi_time vpi_time_s; - p_vpi_time vpi_time_p = &vpi_time_s; - - vpi_time_p->type = vpiSimTime; - vpi_time_p->high = 0; - vpi_time_p->low = 0; - - // Use Inertial delay to schedule an event, thus behaving like a verilog testbench - vpi_obj_hdl *vpi_obj = reinterpret_cast(gpi_hdl); - vpi_put_value(vpi_obj->vpi_hdl, value_p, vpi_time_p, vpiInertialDelay); - check_vpi_error(); - - FEXIT -} - -void vpi_impl::set_signal_value_str(gpi_obj_hdl *gpi_hdl, const char *str) -{ - FENTER - s_vpi_value value_s; - p_vpi_value value_p = &value_s; - - int len; - char *buff; - if (str) - len = strlen(str) + 1; - - buff = (char *)malloc(len); - if (buff== NULL) { - LOG_CRITICAL("VPI: Attempting allocate string buffer failed!"); - return; - } - - strncpy(buff, str, len); - - value_p->value.str = buff; - value_p->format = vpiBinStrVal; - - /* vpiNoDelay -- Set the value immediately. The p_vpi_time parameter - * may be NULL, in this case. This is like a blocking assignment - * in behavioral code. - */ - vpi_obj_hdl *vpi_obj = reinterpret_cast(gpi_hdl); - vpi_put_value(vpi_obj->vpi_hdl, value_p, NULL, vpiNoDelay); - check_vpi_error(); - free(buff); - FEXIT -} - -char *vpi_impl::get_signal_type_str(gpi_obj_hdl *gpi_hdl) -{ - FENTER - vpi_obj_hdl *vpi_obj = reinterpret_cast(gpi_hdl); - const char *name = vpi_get_str(vpiType, vpi_obj->vpi_hdl); - check_vpi_error(); - char *result = gpi_hdl->gpi_copy_name(name); - FEXIT - return result; -} - -char *vpi_impl::get_signal_name_str(gpi_obj_hdl *gpi_hdl) -{ - FENTER - vpi_obj_hdl *vpi_obj = reinterpret_cast(gpi_hdl); - const char *name = vpi_get_str(vpiFullName, vpi_obj->vpi_hdl); - check_vpi_error(); - char *result = gpi_hdl->gpi_copy_name(name); - FEXIT - return result; -} - -char *vpi_impl::get_signal_value_binstr(gpi_obj_hdl *gpi_hdl) -{ - FENTER - vpi_obj_hdl *vpi_obj = reinterpret_cast(gpi_hdl); - s_vpi_value value_s = {vpiBinStrVal}; - p_vpi_value value_p = &value_s; - - vpi_get_value(vpi_obj->vpi_hdl, value_p); - check_vpi_error(); - - char *result = gpi_hdl->gpi_copy_name(value_p->value.str); - FEXIT - return result; -} - -gpi_cb_hdl *vpi_impl::register_timed_callback(uint64_t time_ps) -{ - FENTER - - vpi_cb_timed *hdl = new vpi_cb_timed(vpi_table); - - if (hdl->arm_callback(time_ps)) { - delete(hdl); - hdl = NULL; - } - - FEXIT - return hdl; -} - -gpi_cb_hdl *vpi_impl::register_readwrite_callback(void) -{ - FENTER - - vpi_cb_readwrite *hdl = new vpi_cb_readwrite(vpi_table); - - if (hdl->arm_callback()) { - delete(hdl); - hdl = NULL; - } - - FEXIT - return hdl; -} - -gpi_cb_hdl *vpi_impl::register_value_change_callback(gpi_obj_hdl *gpi_hdl) -{ - FENTER - - vpi_obj_hdl *vpi_obj = reinterpret_cast(gpi_hdl); - vpi_cb_value_change *hdl = new vpi_cb_value_change(vpi_table); - - if (hdl->arm_callback(vpi_obj)) { - delete(hdl); - hdl = NULL; - } - - FEXIT - return hdl; -} - -gpi_cb_hdl *vpi_impl::register_readonly_callback(void) -{ - FENTER - - vpi_cb_readonly *hdl = new vpi_cb_readonly(vpi_table); - - if (hdl->arm_callback()) { - delete(hdl); - hdl = NULL; - } - - FEXIT - return hdl; -} - -gpi_cb_hdl *vpi_impl::register_nexttime_callback(void) -{ - FENTER - - vpi_cb_nexttime *hdl = new vpi_cb_nexttime(vpi_table); - - if (hdl->arm_callback()) { - delete(hdl); - hdl = NULL; - } - - FEXIT - return hdl; -} - -int vpi_impl::deregister_callback(gpi_cb_hdl *gpi_hdl) -{ - vpi_cb_hdl *vpi_obj = reinterpret_cast(gpi_hdl); - if (vpi_obj->m_state == GPI_PRE_CALL) { - //LOG_INFO("Not deleting yet %p", vpi_obj); - return 0; - } - - int rc = vpi_obj->cleanup_callback(); - // LOG_INFO("DELETING %p", vpi_obj); - delete(vpi_obj); - return rc; -} - -// If the Pything world wants things to shut down then unregister -// the callback for end of sim -void vpi_impl::sim_end(void) -{ - /* Some sims do not seem to be able to deregister the end of sim callback - * so we need to make sure we have tracked this and not call the handler - */ - sim_finish_cb->m_state = GPI_DELETE; - vpi_control(vpiFinish); - check_vpi_error(); -} - -extern "C" { - -// Main re-entry point for callbacks from simulator -int32_t handle_vpi_callback(p_cb_data cb_data) -{ - FENTER - int rv = 0; - //vpiHandle old_cb; - - vpi_cb_hdl *cb_hdl = (vpi_cb_hdl*)cb_data->user_data; - - if (!cb_hdl) - LOG_CRITICAL("VPI: Callback data corrupted"); - - LOG_DEBUG("Running %p", cb_hdl); - - if (cb_hdl->m_state == GPI_PRIMED) { - cb_hdl->m_state = GPI_PRE_CALL; - cb_hdl->run_callback(); - cb_hdl->m_state = GPI_POST_CALL; - } - - gpi_deregister_callback(cb_hdl); - - FEXIT - return rv; -}; - - -static void register_embed(void) -{ - vpi_table = new vpi_impl("VPI"); - gpi_register_impl(vpi_table); - gpi_embed_init_python(); -} - - -static void register_initial_callback(void) -{ - sim_init_cb = new vpi_cb_startup(vpi_table); - - /* We ignore the return value here as VCS does some silly - * things on comilation that means it tries to run through - * the vlog_startup_routines and so call this routine - */ - - sim_init_cb->arm_callback(); -} - -static void register_final_callback(void) -{ - sim_finish_cb = new vpi_cb_shutdown(vpi_table); - - /* We ignore the return value here as VCS does some silly - * things on comilation that means it tries to run through - * the vlog_startup_routines and so call this routine - */ - - sim_finish_cb->arm_callback(); -} - - -// Called at compile time to validate the arguments to the system functions -// we redefine (info, warning, error, fatal). -// -// Expect either no arguments or a single string -static int system_function_compiletf(char *userdata) -{ - vpiHandle systf_handle, arg_iterator, arg_handle; - int tfarg_type; - - systf_handle = vpi_handle(vpiSysTfCall, NULL); - arg_iterator = vpi_iterate(vpiArgument, systf_handle); - - if (arg_iterator == NULL) - return 0; - - arg_handle = vpi_scan(arg_iterator); - tfarg_type = vpi_get(vpiType, arg_handle); - - // FIXME: HACK for some reason Icarus returns a vpiRealVal type for strings? - if (vpiStringVal != tfarg_type && vpiRealVal != tfarg_type) { - vpi_printf("ERROR: $[info|warning|error|fata] argument wrong type: %d\n", - tfarg_type); - vpi_free_object(arg_iterator); - vpi_control(vpiFinish, 1); - return -1; - } - return 0; -} - -static int systf_info_level = GPIInfo; -static int systf_warning_level = GPIWarning; -static int systf_error_level = GPIError; -static int systf_fatal_level = GPICritical; - -// System function to permit code in the simulator to fail a test -// TODO: Pass in an error string -static int system_function_overload(char *userdata) -{ - vpiHandle systfref, args_iter, argh; - struct t_vpi_value argval; - const char *msg = "*** NO MESSAGE PROVIDED ***"; - - // Obtain a handle to the argument list - systfref = vpi_handle(vpiSysTfCall, NULL); - args_iter = vpi_iterate(vpiArgument, systfref); - - // The first argument to fatal is the FinishNum which we discard - if (args_iter && *userdata == systf_fatal_level) { - argh = vpi_scan(args_iter); - } - - if (args_iter) { - // Grab the value of the first argument - argh = vpi_scan(args_iter); - argval.format = vpiStringVal; - vpi_get_value(argh, &argval); - vpi_free_object(args_iter); - msg = argval.value.str; - } - - gpi_log("simulator", *userdata, vpi_get_str(vpiFile, systfref), "", (long)vpi_get(vpiLineNo, systfref), msg ); - - // Fail the test for critical errors - if (GPICritical == *userdata) - embed_sim_event(SIM_TEST_FAIL, argval.value.str); - - return 0; -} - -static void register_system_functions(void) -{ - FENTER - s_vpi_systf_data tfData = { vpiSysTask, vpiSysTask }; - - tfData.sizetf = NULL; - tfData.compiletf = system_function_compiletf; - tfData.calltf = system_function_overload; - - tfData.user_data = (char *)&systf_info_level; - tfData.tfname = "$info"; - vpi_register_systf( &tfData ); - - tfData.user_data = (char *)&systf_warning_level; - tfData.tfname = "$warning"; - vpi_register_systf( &tfData ); - - tfData.user_data = (char *)&systf_error_level; - tfData.tfname = "$error"; - vpi_register_systf( &tfData ); - - tfData.user_data = (char *)&systf_fatal_level; - tfData.tfname = "$fatal"; - vpi_register_systf( &tfData ); - - FEXIT -} - -void (*vlog_startup_routines[])(void) = { - register_embed, - register_system_functions, - register_initial_callback, - register_final_callback, - 0 -}; - - -// For non-VPI compliant applications that cannot find vlog_startup_routines symbol -void vlog_startup_routines_bootstrap(void) { - void (*routine)(void); - int i; - routine = vlog_startup_routines[0]; - for (i = 0, routine = vlog_startup_routines[i]; - routine; - routine = vlog_startup_routines[++i]) { - routine(); - } -} - -} \ No newline at end of file +#endif \ No newline at end of file diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp new file mode 100644 index 00000000..b5cacfea --- /dev/null +++ b/lib/vpi/VpiImpl.cpp @@ -0,0 +1,403 @@ +/****************************************************************************** +* Copyright (c) 2013 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +#include "VpiImpl.h" +#include + +extern "C" { + +static VpiCbHdl *sim_init_cb; +static VpiCbHdl *sim_finish_cb; +static VpiImpl *vpi_table; + +} + +void VpiImpl::get_sim_time(uint32_t *high, uint32_t *low) +{ + s_vpi_time vpi_time_s; + vpi_time_s.type = vpiSimTime; //vpiSimTime; + vpi_get_time(NULL, &vpi_time_s); + check_vpi_error(); + *high = vpi_time_s.high; + *low = vpi_time_s.low; +} + +bool VpiImpl::is_native(GpiObjHdl *hdl) +{ + return hdl->is_native_impl(this); +} + +bool VpiImpl::native_check(std::string &name) +{ + bool ret = true; + vpiHandle vpi_hdl = NULL; + std::vector writable(name.begin(), name.end()); + writable.push_back('\0'); + + vpi_handle_by_name(&writable[0], vpi_hdl); + + if (vpiUnknown == vpi_get(vpiType, vpi_hdl)) { + ret = false; + } + + vpi_free_object(vpi_hdl); + + return ret; +} + +GpiObjHdl *VpiImpl::get_root_handle(const char* name) +{ + FENTER + vpiHandle root; + vpiHandle iterator; + VpiObjHdl *rv; + + // vpi_iterate with a ref of NULL returns the top level module + iterator = vpi_iterate(vpiModule, NULL); + check_vpi_error(); + + for (root = vpi_scan(iterator); root != NULL; root = vpi_scan(iterator)) { + + if (name == NULL || !strcmp(name, vpi_get_str(vpiFullName, root))) + break; + } + + if (!root) { + check_vpi_error(); + goto error; + } + + // Need to free the iterator if it didn't return NULL + if (!vpi_free_object(iterator)) { + LOG_WARN("VPI: Attempting to free root iterator failed!"); + check_vpi_error(); + } + + rv = new VpiObjHdl(this, root); + rv->initialise(name); + + FEXIT + return rv; + + error: + + LOG_CRITICAL("VPI: Couldn't find root handle %s", name); + + iterator = vpi_iterate(vpiModule, NULL); + + for (root = vpi_scan(iterator); root != NULL; root = vpi_scan(iterator)) { + + LOG_CRITICAL("VPI: Toplevel instances: %s != %s...", name, vpi_get_str(vpiFullName, root)); + + if (name == NULL || !strcmp(name, vpi_get_str(vpiFullName, root))) + break; + } + + FEXIT + return NULL; +} + + +GpiCbHdl *VpiImpl::register_timed_callback(uint64_t time_ps) +{ + FENTER + + VpiTimedCbHdl *hdl = new VpiTimedCbHdl(this); + + if (hdl->arm_callback(time_ps)) { + delete(hdl); + hdl = NULL; + } + + FEXIT + return hdl; +} + +GpiCbHdl *VpiImpl::register_readwrite_callback(void) +{ + #if 0 + FENTER + + vpi_cb_readwrite *hdl = new VpiReadWriteCbHdl(this); + + if (hdl->arm_callback()) { + delete(hdl); + hdl = NULL; + } + + FEXIT + return hdl; + #endif + return NULL; +} + +GpiCbHdl *VpiImpl::register_readonly_callback(void) +{ + #if 0 + FENTER + + vpi_cb_readonly *hdl = new VpiReadOnlyCbHdl(this); + + if (hdl->arm_callback()) { + delete(hdl); + hdl = NULL; + } + + FEXIT + return hdl; + #endif + return NULL; +} + +GpiCbHdl *VpiImpl::register_nexttime_callback(void) +{ + #if 0 + FENTER + + vpi_cb_nexttime *hdl = new VpiNextPhaseCbHdl(this); + + if (hdl->arm_callback()) { + delete(hdl); + hdl = NULL; + } + + FEXIT + return hdl; + #endif + return NULL; +} + +int VpiImpl::deregister_callback(GpiCbHdl *gpi_hdl) +{ + #if 0 + VpiCbHdl *vpi_obj = reinterpret_cast(gpi_hdl); + if (vpi_obj->get_call_state() == GPI_PRE_CALL) { + //LOG_INFO("Not deleting yet %p", vpi_obj); + return 0; + } + + int rc = vpi_obj->cleanup_callback(); + // LOG_INFO("DELETING %p", vpi_obj); + delete(vpi_obj); + return rc; + #endif + return 0; +} + +// If the Pything world wants things to shut down then unregister +// the callback for end of sim +void VpiImpl::sim_end(void) +{ + /* Some sims do not seem to be able to deregister the end of sim callback + * so we need to make sure we have tracked this and not call the handler + */ + sim_finish_cb->set_call_state(GPI_DELETE); + vpi_control(vpiFinish); + check_vpi_error(); +} + +extern "C" { + +// Main re-entry point for callbacks from simulator +int32_t handle_vpi_callback(p_cb_data cb_data) +{ + FENTER + int rv = 0; + //vpiHandle old_cb; + + VpiCbHdl *cb_hdl = (VpiCbHdl*)cb_data->user_data; + + if (!cb_hdl) + LOG_CRITICAL("VPI: Callback data corrupted"); + + LOG_DEBUG("Running %p", cb_hdl); + + if (cb_hdl->get_call_state() == GPI_PRIMED) { + cb_hdl->set_call_state(GPI_PRE_CALL); + cb_hdl->run_callback(); + cb_hdl->set_call_state(GPI_POST_CALL); + } + + gpi_deregister_callback(cb_hdl); + + FEXIT + return rv; +}; + + +static void register_embed(void) +{ + vpi_table = new VpiImpl("VPI"); + gpi_register_impl(vpi_table); + gpi_embed_init_python(); +} + + +static void register_initial_callback(void) +{ + sim_init_cb = new VpiStartupCbHdl(vpi_table); + + /* We ignore the return value here as VCS does some silly + * things on comilation that means it tries to run through + * the vlog_startup_routines and so call this routine + */ + + sim_init_cb->arm_callback(); +} + +static void register_final_callback(void) +{ + sim_finish_cb = new VpiShutdownCbHdl(vpi_table); + + /* We ignore the return value here as VCS does some silly + * things on comilation that means it tries to run through + * the vlog_startup_routines and so call this routine + */ + + sim_finish_cb->arm_callback(); +} + + +// Called at compile time to validate the arguments to the system functions +// we redefine (info, warning, error, fatal). +// +// Expect either no arguments or a single string +static int system_function_compiletf(char *userdata) +{ + vpiHandle systf_handle, arg_iterator, arg_handle; + int tfarg_type; + + systf_handle = vpi_handle(vpiSysTfCall, NULL); + arg_iterator = vpi_iterate(vpiArgument, systf_handle); + + if (arg_iterator == NULL) + return 0; + + arg_handle = vpi_scan(arg_iterator); + tfarg_type = vpi_get(vpiType, arg_handle); + + // FIXME: HACK for some reason Icarus returns a vpiRealVal type for strings? + if (vpiStringVal != tfarg_type && vpiRealVal != tfarg_type) { + vpi_printf("ERROR: $[info|warning|error|fata] argument wrong type: %d\n", + tfarg_type); + vpi_free_object(arg_iterator); + vpi_control(vpiFinish, 1); + return -1; + } + return 0; +} + +static int systf_info_level = GPIInfo; +static int systf_warning_level = GPIWarning; +static int systf_error_level = GPIError; +static int systf_fatal_level = GPICritical; + +// System function to permit code in the simulator to fail a test +// TODO: Pass in an error string +static int system_function_overload(char *userdata) +{ + vpiHandle systfref, args_iter, argh; + struct t_vpi_value argval; + const char *msg = "*** NO MESSAGE PROVIDED ***"; + + // Obtain a handle to the argument list + systfref = vpi_handle(vpiSysTfCall, NULL); + args_iter = vpi_iterate(vpiArgument, systfref); + + // The first argument to fatal is the FinishNum which we discard + if (args_iter && *userdata == systf_fatal_level) { + argh = vpi_scan(args_iter); + } + + if (args_iter) { + // Grab the value of the first argument + argh = vpi_scan(args_iter); + argval.format = vpiStringVal; + vpi_get_value(argh, &argval); + vpi_free_object(args_iter); + msg = argval.value.str; + } + + gpi_log("simulator", *userdata, vpi_get_str(vpiFile, systfref), "", (long)vpi_get(vpiLineNo, systfref), msg ); + + // Fail the test for critical errors + if (GPICritical == *userdata) + embed_sim_event(SIM_TEST_FAIL, argval.value.str); + + return 0; +} + +static void register_system_functions(void) +{ + FENTER + s_vpi_systf_data tfData = { vpiSysTask, vpiSysTask }; + + tfData.sizetf = NULL; + tfData.compiletf = system_function_compiletf; + tfData.calltf = system_function_overload; + + tfData.user_data = (char *)&systf_info_level; + tfData.tfname = "$info"; + vpi_register_systf( &tfData ); + + tfData.user_data = (char *)&systf_warning_level; + tfData.tfname = "$warning"; + vpi_register_systf( &tfData ); + + tfData.user_data = (char *)&systf_error_level; + tfData.tfname = "$error"; + vpi_register_systf( &tfData ); + + tfData.user_data = (char *)&systf_fatal_level; + tfData.tfname = "$fatal"; + vpi_register_systf( &tfData ); + + FEXIT +} + +void (*vlog_startup_routines[])(void) = { + register_embed, + register_system_functions, + register_initial_callback, + register_final_callback, + 0 +}; + + +// For non-VPI compliant applications that cannot find vlog_startup_routines symbol +void vlog_startup_routines_bootstrap(void) { + void (*routine)(void); + int i; + routine = vlog_startup_routines[0]; + for (i = 0, routine = vlog_startup_routines[i]; + routine; + routine = vlog_startup_routines[++i]) { + routine(); + } +} + +} \ No newline at end of file diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h new file mode 100644 index 00000000..b262ccef --- /dev/null +++ b/lib/vpi/VpiImpl.h @@ -0,0 +1,185 @@ +/****************************************************************************** +* Copyright (c) 2013 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +#ifndef COCOTB_VPI_IMPL_H_ +#define COCOTB_VPI_IMPL_H_ + +#include "../gpi/gpi_priv.h" +#include + +#define VPI_CHECKING 1 + +// Should be run after every VPI call to check error status +static inline int __check_vpi_error(const char *func, long line) +{ + int level=0; +#if VPI_CHECKING + s_vpi_error_info info; + int loglevel; + level = vpi_chk_error(&info); + if (level == 0) + return 0; + + switch (level) { + case vpiNotice: + loglevel = GPIInfo; + break; + case vpiWarning: + loglevel = GPIWarning; + break; + case vpiError: + loglevel = GPIError; + break; + case vpiSystem: + case vpiInternal: + loglevel = GPICritical; + break; + } + + gpi_log("cocotb.gpi", loglevel, __FILE__, func, line, + "VPI Error level %d\nPROD %s\nCODE %s\nFILE %s", + info.message, info.product, info.code, info.file); + +#endif + return level; +} + +#define check_vpi_error() do { \ + __check_vpi_error(__func__, __LINE__); \ +} while (0) + +class VpiImpl : public GpiImplInterface { +public: + VpiImpl(const std::string& name) : GpiImplInterface(name) { } + + /* Sim related */ + void sim_end(void); + void get_sim_time(uint32_t *high, uint32_t *low); + + /* Hierachy related */ + bool is_native(GpiObjHdl *hdl); + GpiObjHdl *get_root_handle(const char *name); + + /* Callback related, these may (will) return the same handle*/ + GpiCbHdl *register_timed_callback(uint64_t time_ps); + GpiCbHdl *register_readonly_callback(void); + GpiCbHdl *register_nexttime_callback(void); + GpiCbHdl *register_readwrite_callback(void); + int deregister_callback(GpiCbHdl *obj_hdl); + bool native_check(std::string &name); +}; + +class VpiObjHdl : public GpiObjHdl { +public: + VpiObjHdl(GpiImplInterface *impl, vpiHandle hdl) : GpiObjHdl(impl), + vpi_hdl(hdl) { } + virtual ~VpiObjHdl() { } + + virtual GpiObjHdl *get_handle_by_name(const char *name); + virtual GpiObjHdl *get_handle_by_index(uint32_t index); + virtual GpiIterator *iterate_handle(uint32_t type) { return NULL ;} + virtual GpiObjHdl *next_handle(GpiIterator *iterator) { return NULL; } + bool is_native_impl(GpiImplInterface *impl); + + const char* get_name_str(void); + const char* get_type_str(void); + +protected: + const char * reason_to_string(int reason); + +protected: + vpiHandle vpi_hdl; + +}; + +class VpiCbHdl : public GpiCbHdl, public VpiObjHdl { +public: + VpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + VpiObjHdl(impl, NULL) { } + virtual ~VpiCbHdl() { } + + virtual int arm_callback(void); + virtual int cleanup_callback(void); + +protected: + int register_cb(p_cb_data cb_data); +}; + +class VpiSignalObjHdl : public GpiSignalObjHdl { +public: + VpiSignalObjHdl(GpiImplInterface *impl) : GpiSignalObjHdl(impl) { } + virtual ~VpiSignalObjHdl() { } + + virtual int set_signal_value(const int value); + virtual int set_signal_value(const char *str); + //virtual GpiCbHdl monitor_value(bool rising_edge) = 0; this was for the triggers + // but the explicit ones are probably better + + // Also think we want the triggers here? + virtual GpiCbHdl *rising_edge_cb(void); + virtual GpiCbHdl *falling_edge_cb(void); + virtual GpiCbHdl *value_change_cb(void); +}; + +class VpiTimedCbHdl : public VpiCbHdl { +public: + VpiTimedCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) { } + int arm_callback(uint64_t time_ps); + virtual ~VpiTimedCbHdl() { } +}; + +#if 0 +class VpiReadOnlyCbHdl : public VpiCbHdl { + +}; + +class VpiReadWriteCbHdl : public VpiCbHdl { + +}; + +class VpiNextPhaseCbHdl : public VpiCbHdl { + +}; +#endif + +class VpiStartupCbHdl : public VpiCbHdl { +public: + VpiStartupCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) { } + int run_callback(void); + int arm_callback(void); + virtual ~VpiStartupCbHdl() { } +}; + +class VpiShutdownCbHdl : public VpiCbHdl { +public: + VpiShutdownCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) { } + int run_callback(void); + int arm_callback(void); + virtual ~VpiShutdownCbHdl() { } +}; + +#endif /*COCOTB_VPI_IMPL_H_ */ \ No newline at end of file From c67b1299f75f4a694c9589269fb5b6510342abb5 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 7 Oct 2014 21:24:52 +0100 Subject: [PATCH 0604/2656] Issue#161: To not initialise python twice --- lib/embed/gpi_embed.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 79eb25a4..fa9d0272 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -37,7 +37,7 @@ static PyThreadState *gtstate = NULL; static char progname[] = "cocotb"; static char *argv[] = { progname }; -static PyObject *pEventFn; +static PyObject *pEventFn = NULL; /** @@ -65,15 +65,15 @@ void embed_init_python(void) #define PY_SO_LIB xstr(PYTHON_SO_LIB) #endif + // Don't initialise python if already running + if (gtstate) + return; + void *ret = dlopen(PY_SO_LIB, RTLD_LAZY | RTLD_GLOBAL); if (!ret) { fprintf(stderr, "Failed to find python lib %s (%s)\n", PY_SO_LIB, dlerror()); } - // Don't initialise python if already running - if (gtstate) - return; - Py_SetProgramName(progname); Py_Initialize(); /* Initialize the interpreter */ PySys_SetArgvEx(1, argv, 0); @@ -122,6 +122,10 @@ void embed_sim_init(gpi_sim_info_t *info) int i; + /* Check that we are not already initialised */ + if (pEventFn) + return; + // Find the simulation root gpi_sim_hdl dut = gpi_get_root_handle(getenv("TOPLEVEL")); From 85fcfe3c879b7fce2d320c3d9f0f51a4e0faf32c Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 7 Oct 2014 21:26:02 +0100 Subject: [PATCH 0605/2656] Issue#161: Update makefile file deps --- lib/Makefile | 6 +++--- lib/vpi/Makefile | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index 31f9ac42..0779b5ba 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -52,13 +52,13 @@ $(LIB_DIR)/libgpilog.so: $(SIM_ROOT)/lib/gpi_log/gpi_logging.c | $(LIB_DIR) $(LIB_DIR)/libcocotb.so: $(SIM_ROOT)/lib/embed/gpi_embed.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/embed -$(LIB_DIR)/libvpi.so: $(SIM_ROOT)/lib/vpi/gpi_vpi.cpp | $(LIB_DIR) +$(LIB_DIR)/libvpi.so: $(SIM_ROOT)/lib/vpi/VpiImpl.cpp $(SIM_ROOT)/lib/vpi/VpiCbHdl.cpp | $(LIB_DIR) make -C $(SIM_ROOT)/lib/vpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) -$(LIB_DIR)/libvhpi.so: $(SIM_ROOT)/lib/vhpi/gpi_vhpi.c | $(LIB_DIR) +$(LIB_DIR)/libvhpi.so: $(SIM_ROOT)/lib/vhpi/gpi_vhpi.cpp | $(LIB_DIR) make -C $(SIM_ROOT)/lib/vhpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) -$(LIB_DIR)/libgpi.so: $(SIM_ROOT)/lib/gpi/gpi_common.cpp $(SIM_ROOT)/lib/gpi/gpi_cb_hdl.cpp | $(LIB_DIR) +$(LIB_DIR)/libgpi.so: $(SIM_ROOT)/lib/gpi/GpiCommon.cpp $(SIM_ROOT)/lib/gpi/GpiCbHdl.cpp | $(LIB_DIR) make -C $(SIM_ROOT)/lib/gpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) $(LIB_DIR)/libsim.so: $(SIM_ROOT)/lib/simulator/simulatormodule.c | $(LIB_DIR) diff --git a/lib/vpi/Makefile b/lib/vpi/Makefile index 8ef088d6..1e26768f 100644 --- a/lib/vpi/Makefile +++ b/lib/vpi/Makefile @@ -35,7 +35,7 @@ LIBS := -lgpilog -lgpi -lstdc++ LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) LIB_NAME := libvpi.so -SRCS := gpi_vpi.cpp +SRCS := VpiImpl.cpp VpiCbHdl.cpp CLIBS += $(LIB_DIR)/gpivpi.vpl From 9274e477465533ec9b6d28956f508362066c0abe Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 7 Oct 2014 21:26:21 +0100 Subject: [PATCH 0606/2656] Issue#161: Add vpiUnknown --- include/vpi_user.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/vpi_user.h b/include/vpi_user.h index 5865d201..bf2135f0 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -143,6 +143,8 @@ typedef struct t_vpi_value #define vpiFile 5 #define vpiLineNo 6 +#define vpiUnknown 3 + /* normal callback structure */ typedef struct t_cb_data { From ab2bfb91790d426de605e472ceec246363040cc7 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 8 Oct 2014 09:48:54 +0100 Subject: [PATCH 0607/2656] Issue#161: Basis for checking if a requested handle is native to an implementation --- lib/gpi/GpiCbHdl.cpp | 5 ----- lib/gpi/GpiCommon.cpp | 36 ++++++++++++------------------------ lib/gpi/gpi_priv.h | 21 ++++++++++++++++----- lib/vpi/VpiCbHdl.cpp | 29 +++++++++++++++++++++-------- lib/vpi/VpiImpl.cpp | 12 +++++------- lib/vpi/VpiImpl.h | 8 ++++---- 6 files changed, 58 insertions(+), 53 deletions(-) diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index fd6d9b21..e6ff0519 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -28,11 +28,6 @@ #include "gpi_priv.h" -bool GpiObjHdl::is_native_impl(GpiImplInterface *impl) -{ - return impl->native_check(m_name); -} - const char * GpiObjHdl::get_name_str(void) { return m_name.c_str(); diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 3202df22..f4e07c22 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -32,18 +32,6 @@ using namespace std; -template -inline To sim_to_hdl(Ti input) -{ - To result = reinterpret_cast(input); - if (!result) { - LOG_CRITICAL("GPI: Handle passed down is not valid gpi_sim_hdl"); - exit(1); - } - - return result; -} - static vector registered_impls; int gpi_register_impl(GpiImplInterface *func_tbl) @@ -89,10 +77,10 @@ gpi_sim_hdl gpi_get_root_handle(const char *name) iter != registered_impls.end(); iter++) { if ((hdl = (*iter)->get_root_handle(name))) { - LOG_WARN("Got a handle (%s) back from %s", + LOG_WARN("Got a Root handle (%s) back from %s", hdl->get_name_str(), (*iter)->get_name_c()); - return (void*)hdl; + return (gpi_sim_hdl)hdl; } } return NULL; @@ -100,30 +88,30 @@ gpi_sim_hdl gpi_get_root_handle(const char *name) gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) { -#if 0 + vector::iterator iter; GpiObjHdl *hdl; - GpiObjHdl *base = sim_to_hdl(parent); + GpiObjHdl *base = sim_to_hdl(parent); + std::string s_name = name; - /* Either want this or use the parent */ for (iter = registered_impls.begin(); iter != registered_impls.end(); iter++) { - LOG_WARN("Quering impl %s", (*iter)->get_name()); - if ((hdl = (*iter)->get_handle_by_name(name, base))) { - return (void*)hdl; + LOG_WARN("Checking if %s native though impl %s ", name, (*iter)->get_name_c()); + if ((*iter)->native_check(s_name, base)) { + LOG_WARN("Found %s via %s", name, (*iter)->get_name_c()); + hdl = base->get_handle_by_name(s_name); } } - hdl = base->m_impl->get_handle_by_name(name, base); - return (void*)hdl; -#endif - return NULL; + + return (gpi_sim_hdl)hdl; } gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) { + LOG_WARN("Trying index"); #if 0 /* Either want this or use the parent */ GpiObjHdl *obj_hdl = sim_to_hdl(parent); diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 59ea9df2..3e6abac8 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -46,16 +46,28 @@ class GpiImplInterface; class GpiIterator; class GpiCbHdl; +template +inline To sim_to_hdl(Ti input) +{ + To result = reinterpret_cast(input); + if (!result) { + LOG_CRITICAL("GPI: Handle passed down is not valid gpi_sim_hdl"); + exit(1); + } + + return result; +} + /* Base GPI class others are derived from */ class GpiHdl { public: - GpiHdl() : m_impl(NULL) { } + //GpiHdl() : m_impl(NULL) { } GpiHdl(GpiImplInterface *impl) : m_impl(impl) { } virtual ~GpiHdl() { } virtual int initialise(std::string name); // Post constructor init private: - //GpiHdl() { } // Disable default constructor + GpiHdl() { } // Disable default constructor public: GpiImplInterface *m_impl; // VPI/VHPI/FLI routines @@ -77,7 +89,7 @@ class GpiObjHdl : public GpiHdl { // The following methods permit children below this level of the hierarchy // to be discovered and instantiated - virtual GpiObjHdl *get_handle_by_name(const char *name) = 0; + virtual GpiObjHdl *get_handle_by_name(std::string &name) = 0; virtual GpiObjHdl *get_handle_by_index(uint32_t index) = 0; virtual GpiIterator *iterate_handle(uint32_t type) = 0; virtual GpiObjHdl *next_handle(GpiIterator *iterator) = 0; @@ -192,8 +204,7 @@ class GpiImplInterface { virtual void get_sim_time(uint32_t *high, uint32_t *low) = 0; /* Hierachy related */ - virtual bool is_native(GpiObjHdl *hdl) = 0; - virtual bool native_check(std::string &name) = 0; + virtual bool native_check(std::string &name, GpiObjHdl *parent) = 0; virtual GpiObjHdl *get_root_handle(const char *name) = 0; /* Callback related, these may (will) return the same handle*/ diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index cc23995e..e6e7dc90 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -28,6 +28,7 @@ ******************************************************************************/ #include "VpiImpl.h" +#include extern "C" int32_t handle_vpi_callback(p_cb_data cb_data); @@ -55,6 +56,11 @@ const char *VpiObjHdl::reason_to_string(int reason) } } +vpiHandle VpiObjHdl::get_handle(void) +{ + return vpi_hdl; +} + /** * @brief Get a handle to an object under the scope of parent * @@ -62,14 +68,16 @@ const char *VpiObjHdl::reason_to_string(int reason) * * @return gpi_sim_hdl for the new object or NULL if object not found */ -GpiObjHdl *VpiObjHdl::get_handle_by_name(const char *name) +GpiObjHdl *VpiObjHdl::get_handle_by_name(std::string &name) { FENTER GpiObjHdl *rv = NULL; vpiHandle obj; vpiHandle iterator; - int len; - char *buff; + std::vector writable(name.begin(), name.end()); + writable.push_back('\0'); + //int len; + //char *buff; // Structures aren't technically a scope, according to the LRM. If parent // is a structure then we have to iterate over the members comparing names @@ -79,7 +87,7 @@ GpiObjHdl *VpiObjHdl::get_handle_by_name(const char *name) for (obj = vpi_scan(iterator); obj != NULL; obj = vpi_scan(iterator)) { - if (!strcmp(name, strrchr(vpi_get_str(vpiName, obj), 46) + 1)) + if (!strcmp(name.c_str(), strrchr(vpi_get_str(vpiName, obj), 46) + 1)) break; } @@ -95,6 +103,8 @@ GpiObjHdl *VpiObjHdl::get_handle_by_name(const char *name) goto success; } + #if 0 + Do we need this if (name) len = strlen(name) + 1; @@ -105,19 +115,22 @@ GpiObjHdl *VpiObjHdl::get_handle_by_name(const char *name) } strncpy(buff, name, len); - obj = vpi_handle_by_name(buff, vpi_hdl); + #endif + + + obj = vpi_handle_by_name(&writable[0], vpi_hdl); if (!obj) { - LOG_DEBUG("VPI: Handle '%s' not found!", name); + LOG_DEBUG("VPI: Handle '%s' not found!", name.c_str()); // NB we deliberately don't dump an error message here because it's // a valid use case to attempt to grab a signal by name - for example // optional signals on a bus. // check_vpi_error(); - free(buff); + //free(buff); return NULL; } - free(buff); + //free(buff); success: // rv = new VpiObjHdl(obj); diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index b5cacfea..e6bac218 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -46,15 +46,13 @@ void VpiImpl::get_sim_time(uint32_t *high, uint32_t *low) *low = vpi_time_s.low; } -bool VpiImpl::is_native(GpiObjHdl *hdl) -{ - return hdl->is_native_impl(this); -} - -bool VpiImpl::native_check(std::string &name) +/* Not interested in the hierachy for checking that a handle +in in this implementation */ +bool VpiImpl::native_check(std::string &name, GpiObjHdl *parent) { bool ret = true; - vpiHandle vpi_hdl = NULL; + VpiObjHdl *parent_hdl = sim_to_hdl(parent); + vpiHandle vpi_hdl = parent_hdl->get_handle(); std::vector writable(name.begin(), name.end()); writable.push_back('\0'); diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index b262ccef..7fb5c463 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -81,7 +81,6 @@ class VpiImpl : public GpiImplInterface { void get_sim_time(uint32_t *high, uint32_t *low); /* Hierachy related */ - bool is_native(GpiObjHdl *hdl); GpiObjHdl *get_root_handle(const char *name); /* Callback related, these may (will) return the same handle*/ @@ -90,7 +89,7 @@ class VpiImpl : public GpiImplInterface { GpiCbHdl *register_nexttime_callback(void); GpiCbHdl *register_readwrite_callback(void); int deregister_callback(GpiCbHdl *obj_hdl); - bool native_check(std::string &name); + bool native_check(std::string &name, GpiObjHdl *parent); }; class VpiObjHdl : public GpiObjHdl { @@ -99,15 +98,16 @@ class VpiObjHdl : public GpiObjHdl { vpi_hdl(hdl) { } virtual ~VpiObjHdl() { } - virtual GpiObjHdl *get_handle_by_name(const char *name); + virtual GpiObjHdl *get_handle_by_name(std::string &name); virtual GpiObjHdl *get_handle_by_index(uint32_t index); virtual GpiIterator *iterate_handle(uint32_t type) { return NULL ;} virtual GpiObjHdl *next_handle(GpiIterator *iterator) { return NULL; } - bool is_native_impl(GpiImplInterface *impl); const char* get_name_str(void); const char* get_type_str(void); + vpiHandle get_handle(void); + protected: const char * reason_to_string(int reason); From 4d4a449a046e09299b83834bed7c8a72d1a875b5 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 8 Oct 2014 12:50:56 +0100 Subject: [PATCH 0608/2656] test post commit hook over https --- hook_test | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 hook_test diff --git a/hook_test b/hook_test new file mode 100644 index 00000000..e69de29b From 889949f79ffab59932b8115894323b20bc642d4e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 9 Oct 2014 07:34:49 +0100 Subject: [PATCH 0609/2656] Issue#161: Do not derive VpiCbHdl from VpiObjHdl --- lib/gpi/gpi_priv.h | 3 +++ lib/vpi/VpiCbHdl.cpp | 44 ++++++++++---------------------------------- lib/vpi/VpiImpl.cpp | 24 ++++++++++++++++++++++++ lib/vpi/VpiImpl.h | 11 +++++------ 4 files changed, 42 insertions(+), 40 deletions(-) diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 3e6abac8..3fabcac9 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -214,6 +214,9 @@ class GpiImplInterface { virtual GpiCbHdl *register_readwrite_callback(void) = 0; virtual int deregister_callback(GpiCbHdl *obj_hdl) = 0; + /* Method to provide strings from operation types */ + virtual const char * reason_to_string(int reason) = 0; + private: std::string m_name; }; diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index e6e7dc90..91fc97de 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -32,30 +32,6 @@ extern "C" int32_t handle_vpi_callback(p_cb_data cb_data); -const char *VpiObjHdl::reason_to_string(int reason) -{ - switch (reason) { - case cbValueChange: - return "cbValueChange"; - case cbAtStartOfSimTime: - return "cbAtStartOfSimTime"; - case cbReadWriteSynch: - return "cbReadWriteSynch"; - case cbReadOnlySynch: - return "cbReadOnlySynch"; - case cbNextSimTime: - return "cbNextSimTime"; - case cbAfterDelay: - return "cbAfterDelay"; - case cbStartOfSimulation: - return "cbStartOfSimulation"; - case cbEndOfSimulation: - return "cbEndOfSimulation"; - default: - return "unknown"; - } -} - vpiHandle VpiObjHdl::get_handle(void) { return vpi_hdl; @@ -173,13 +149,13 @@ int VpiCbHdl::register_cb(p_cb_data cb_data) { if (m_state == GPI_PRIMED) { fprintf(stderr, "Attempt to prime an already primed trigger for %s!\n", - reason_to_string(cb_data->reason)); + m_impl->reason_to_string(cb_data->reason)); } if (vpi_hdl != NULL) { fprintf(stderr, "We seem to already be registered, deregistering %s!\n", - reason_to_string(cb_data->reason)); + m_impl->reason_to_string(cb_data->reason)); cleanup_callback(); } @@ -189,7 +165,7 @@ int VpiCbHdl::register_cb(p_cb_data cb_data) { if (!new_hdl) { LOG_CRITICAL("VPI: Unable to register callback a handle for VPI type %s(%d)", - reason_to_string(cb_data->reason), cb_data->reason); + m_impl->reason_to_string(cb_data->reason), cb_data->reason); check_vpi_error(); ret = -1; } @@ -281,7 +257,7 @@ int VpiTimedCbHdl::arm_callback(uint64_t time_ps) { #if 0 class vpi_onetime_cb : public vpi_cb_hdl { public: - vpi_onetime_cb(gpi_impl_interface *impl) : vpi_cb_hdl(impl) { } + vpi_onetime_cb(gpi_m_impl_interface *m_impl) : vpi_cb_hdl(m_impl) { } int cleanup_callback(void) { FENTER //LOG_WARN("Cleanup %p state is %d", this, state); @@ -311,7 +287,7 @@ class vpi_onetime_cb : public vpi_cb_hdl { class vpi_recurring_cb : public vpi_cb_hdl { public: - vpi_recurring_cb(gpi_impl_interface *impl) : vpi_cb_hdl(impl) { } + vpi_recurring_cb(gpi_m_impl_interface *m_impl) : vpi_cb_hdl(m_impl) { } int cleanup_callback(void) { FENTER //LOG_WARN("Cleanup %p", this); @@ -337,7 +313,7 @@ class vpi_cb_value_change : public vpi_recurring_cb { private: s_vpi_value cb_value; public: - vpi_cb_value_change(gpi_impl_interface *impl) : vpi_recurring_cb(impl) { + vpi_cb_value_change(gpi_m_impl_interface *m_impl) : vpi_recurring_cb(m_impl) { cb_value.format = vpiIntVal; } int arm_callback(vpi_obj_hdl *vpi_hdl) { @@ -358,7 +334,7 @@ class vpi_cb_value_change : public vpi_recurring_cb { class vpi_cb_readonly : public vpi_onetime_cb { public: - vpi_cb_readonly(gpi_impl_interface *impl) : vpi_onetime_cb(impl) { } + vpi_cb_readonly(gpi_m_impl_interface *m_impl) : vpi_onetime_cb(m_impl) { } int arm_callback(void) { s_cb_data cb_data_s; s_vpi_time vpi_time_s; @@ -383,7 +359,7 @@ class vpi_cb_readonly : public vpi_onetime_cb { class vpi_cb_timed : public vpi_onetime_cb { public: - vpi_cb_timed(gpi_impl_interface *impl) : vpi_onetime_cb(impl) { } + vpi_cb_timed(gpi_m_impl_interface *m_impl) : vpi_onetime_cb(m_impl) { } int arm_callback(uint64_t time_ps) { s_cb_data cb_data_s; @@ -408,7 +384,7 @@ class vpi_cb_timed : public vpi_onetime_cb { class vpi_cb_readwrite : public vpi_onetime_cb { public: - vpi_cb_readwrite(gpi_impl_interface *impl) : vpi_onetime_cb(impl) { } + vpi_cb_readwrite(gpi_m_impl_interface *m_impl) : vpi_onetime_cb(m_impl) { } int arm_callback(void) { s_cb_data cb_data_s; @@ -433,7 +409,7 @@ class vpi_cb_readwrite : public vpi_onetime_cb { class vpi_cb_nexttime : public vpi_onetime_cb { public: - vpi_cb_nexttime(gpi_impl_interface *impl) : vpi_onetime_cb(impl) { } + vpi_cb_nexttime(gpi_m_impl_interface *m_impl) : vpi_onetime_cb(m_impl) { } int arm_callback(void) { s_cb_data cb_data_s; diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index e6bac218..96b41e07 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -36,6 +36,30 @@ static VpiImpl *vpi_table; } +const char *VpiImpl::reason_to_string(int reason) +{ + switch (reason) { + case cbValueChange: + return "cbValueChange"; + case cbAtStartOfSimTime: + return "cbAtStartOfSimTime"; + case cbReadWriteSynch: + return "cbReadWriteSynch"; + case cbReadOnlySynch: + return "cbReadOnlySynch"; + case cbNextSimTime: + return "cbNextSimTime"; + case cbAfterDelay: + return "cbAfterDelay"; + case cbStartOfSimulation: + return "cbStartOfSimulation"; + case cbEndOfSimulation: + return "cbEndOfSimulation"; + default: + return "unknown"; + } +} + void VpiImpl::get_sim_time(uint32_t *high, uint32_t *low) { s_vpi_time vpi_time_s; diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 7fb5c463..68b2ab70 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -90,6 +90,8 @@ class VpiImpl : public GpiImplInterface { GpiCbHdl *register_readwrite_callback(void); int deregister_callback(GpiCbHdl *obj_hdl); bool native_check(std::string &name, GpiObjHdl *parent); + + const char * reason_to_string(int reason); }; class VpiObjHdl : public GpiObjHdl { @@ -108,18 +110,14 @@ class VpiObjHdl : public GpiObjHdl { vpiHandle get_handle(void); -protected: - const char * reason_to_string(int reason); - protected: vpiHandle vpi_hdl; }; -class VpiCbHdl : public GpiCbHdl, public VpiObjHdl { +class VpiCbHdl : public GpiCbHdl { public: - VpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - VpiObjHdl(impl, NULL) { } + VpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl) { } virtual ~VpiCbHdl() { } virtual int arm_callback(void); @@ -127,6 +125,7 @@ class VpiCbHdl : public GpiCbHdl, public VpiObjHdl { protected: int register_cb(p_cb_data cb_data); + vpiHandle vpi_hdl; }; class VpiSignalObjHdl : public GpiSignalObjHdl { From 6856daf2dd64db6a92578712596754515853a256 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 10 Oct 2014 19:26:48 +0100 Subject: [PATCH 0610/2656] Cleanup: If a test here does fail then we do not have the exception to handle it --- examples/functionality/tests/test_discovery.py | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/functionality/tests/test_discovery.py b/examples/functionality/tests/test_discovery.py index 4dd9f464..8a407be2 100644 --- a/examples/functionality/tests/test_discovery.py +++ b/examples/functionality/tests/test_discovery.py @@ -27,6 +27,7 @@ import cocotb from cocotb.triggers import Timer +from cocotb.result import TestError @cocotb.test() def discover_module_values(dut): From e9104a8a1cb58774acfbd5c185f46a7ea969350e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 10 Oct 2014 19:27:55 +0100 Subject: [PATCH 0611/2656] Issue#161: test_discovery passes, most callbacks are not implemented yet, vhpi test_discovery next --- lib/gpi/GpiCommon.cpp | 8 ++-- lib/gpi/gpi_priv.h | 7 ++-- lib/vpi/VpiCbHdl.cpp | 97 ++++++++++++++++++++++++++++++++----------- lib/vpi/VpiImpl.cpp | 53 ++++++++++++++++++++--- lib/vpi/VpiImpl.h | 51 ++++++++++++++++++----- 5 files changed, 169 insertions(+), 47 deletions(-) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index f4e07c22..e83db0ba 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -95,14 +95,15 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) GpiObjHdl *base = sim_to_hdl(parent); std::string s_name = name; + LOG_WARN("Searchingn for %s", name); for (iter = registered_impls.begin(); iter != registered_impls.end(); iter++) { LOG_WARN("Checking if %s native though impl %s ", name, (*iter)->get_name_c()); - if ((*iter)->native_check(s_name, base)) { + if ((hdl = (*iter)->native_check_create(s_name, base))) { LOG_WARN("Found %s via %s", name, (*iter)->get_name_c()); - hdl = base->get_handle_by_name(s_name); + //hdl = base->get_handle_by_name(s_name); } } @@ -166,8 +167,9 @@ void gpi_set_signal_value_int(gpi_sim_hdl sig_hdl, int value) void gpi_set_signal_value_str(gpi_sim_hdl sig_hdl, const char *str) { + std::string value = str; GpiSignalObjHdl *obj_hdl = sim_to_hdl(sig_hdl); - obj_hdl->set_signal_value(str); + obj_hdl->set_signal_value(value); } gpi_sim_hdl gpi_register_value_change_callback(const int (*gpi_function)(const void *), diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 3fabcac9..bf2b0eae 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -94,8 +94,8 @@ class GpiObjHdl : public GpiHdl { virtual GpiIterator *iterate_handle(uint32_t type) = 0; virtual GpiObjHdl *next_handle(GpiIterator *iterator) = 0; - const char* get_name_str(void); - const char* get_type_str(void); + virtual const char* get_name_str(void); + virtual const char* get_type_str(void); bool is_native_impl(GpiImplInterface *impl); virtual int initialise(std::string name); @@ -120,7 +120,7 @@ class GpiSignalObjHdl : public GpiObjHdl { int m_length; virtual int set_signal_value(const int value) = 0; - virtual int set_signal_value(const char *str) = 0; + virtual int set_signal_value(std::string &value) = 0; //virtual GpiCbHdl monitor_value(bool rising_edge) = 0; this was for the triggers // but the explicit ones are probably better @@ -205,6 +205,7 @@ class GpiImplInterface { /* Hierachy related */ virtual bool native_check(std::string &name, GpiObjHdl *parent) = 0; + virtual GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent) = 0; virtual GpiObjHdl *get_root_handle(const char *name) = 0; /* Callback related, these may (will) return the same handle*/ diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 91fc97de..320ccc73 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -186,6 +186,61 @@ int VpiCbHdl::arm_callback(void) return 0; } +const char* VpiSignalObjHdl::get_signal_value_binstr(void) +{ + FENTER + s_vpi_value value_s = {vpiBinStrVal}; + p_vpi_value value_p = &value_s; + + vpi_get_value(vpi_hdl, value_p); + check_vpi_error(); + + LOG_WARN("Value back was %s", value_p->value.str); + + return value_p->value.str; +} + +// Value related functions +int VpiSignalObjHdl::set_signal_value(int value) +{ + FENTER + s_vpi_value value_s; + + value_s.value.integer = value; + value_s.format = vpiIntVal; + + s_vpi_time vpi_time_s; + + vpi_time_s.type = vpiSimTime; + vpi_time_s.high = 0; + vpi_time_s.low = 0; + + // Use Inertial delay to schedule an event, thus behaving like a verilog testbench + vpi_put_value(vpi_hdl, &value_s, &vpi_time_s, vpiInertialDelay); + check_vpi_error(); + + FEXIT + return 0; +} + +int VpiSignalObjHdl::set_signal_value(std::string &value) +{ + FENTER + s_vpi_value value_s; + + std::vector writable(value.begin(), value.end()); + writable.push_back('\0'); + + value_s.value.str = &writable[0]; + value_s.format = vpiBinStrVal; + + vpi_put_value(vpi_hdl, &value_s, NULL, vpiNoDelay); + check_vpi_error(); + + FEXIT + return 0; +} + int VpiStartupCbHdl::run_callback(void) { s_vpi_vlog_info info; gpi_sim_info_t sim_info; @@ -253,6 +308,23 @@ int VpiTimedCbHdl::arm_callback(uint64_t time_ps) { return register_cb(&cb_data_s); } +int VpiReadwriteCbHdl::arm_callback(void) { + s_cb_data cb_data_s; + s_vpi_time vpi_time_s; + + vpi_time_s.type = vpiSimTime; + vpi_time_s.high = 0; + vpi_time_s.low = 0; + + cb_data_s.reason = cbReadWriteSynch; + cb_data_s.cb_rtn = handle_vpi_callback; + cb_data_s.obj = NULL; + cb_data_s.time = &vpi_time_s; + cb_data_s.value = NULL; + cb_data_s.user_data = (char *)this; + + return register_cb(&cb_data_s); +} #if 0 class vpi_onetime_cb : public vpi_cb_hdl { @@ -382,31 +454,6 @@ class vpi_cb_timed : public vpi_onetime_cb { virtual ~vpi_cb_timed() { } }; -class vpi_cb_readwrite : public vpi_onetime_cb { -public: - vpi_cb_readwrite(gpi_m_impl_interface *m_impl) : vpi_onetime_cb(m_impl) { } - - int arm_callback(void) { - s_cb_data cb_data_s; - s_vpi_time vpi_time_s; - - vpi_time_s.type = vpiSimTime; - vpi_time_s.high = 0; - vpi_time_s.low = 0; - - cb_data_s.reason = cbReadWriteSynch; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = &vpi_time_s; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)this; - - return register_cb(&cb_data_s); - } - - virtual ~vpi_cb_readwrite() { } -}; - class vpi_cb_nexttime : public vpi_onetime_cb { public: vpi_cb_nexttime(gpi_m_impl_interface *m_impl) : vpi_onetime_cb(m_impl) { } diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 96b41e07..60a5b343 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -74,15 +74,17 @@ void VpiImpl::get_sim_time(uint32_t *high, uint32_t *low) in in this implementation */ bool VpiImpl::native_check(std::string &name, GpiObjHdl *parent) { + int32_t type; bool ret = true; VpiObjHdl *parent_hdl = sim_to_hdl(parent); vpiHandle vpi_hdl = parent_hdl->get_handle(); + vpiHandle new_hdl; std::vector writable(name.begin(), name.end()); writable.push_back('\0'); - vpi_handle_by_name(&writable[0], vpi_hdl); + new_hdl = vpi_handle_by_name(&writable[0], vpi_hdl); - if (vpiUnknown == vpi_get(vpiType, vpi_hdl)) { + if (vpiUnknown == (type = vpi_get(vpiType, new_hdl))) { ret = false; } @@ -91,6 +93,48 @@ bool VpiImpl::native_check(std::string &name, GpiObjHdl *parent) return ret; } +GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) +{ + int32_t type; + VpiObjHdl *parent_hdl = sim_to_hdl(parent); + vpiHandle vpi_hdl = parent_hdl->get_handle(); + vpiHandle new_hdl; + VpiObjHdl *new_obj = NULL; + std::vector writable(name.begin(), name.end()); + writable.push_back('\0'); + + new_hdl = vpi_handle_by_name(&writable[0], vpi_hdl); + + if (!new_hdl) + return NULL; + + if (vpiUnknown == (type = vpi_get(vpiType, new_hdl))) { + vpi_free_object(vpi_hdl); + return new_obj; + } + + /* What sort of isntance is this ?*/ + switch (type) { + case vpiNet: + new_obj = new VpiSignalObjHdl(this, new_hdl); + LOG_WARN("Created VpiSignalObjHdl"); + break; + case vpiModule: + new_obj = new VpiObjHdl(this, new_hdl); + LOG_WARN("Created VpiObjHdl"); + break; + default: + LOG_CRITICAL("Not sure what to do with type %d for entity (%s)", type, name.c_str()); + return false; + } + + LOG_WARN("Type was %d", type); + /* Might move the object creation inside here */ + new_obj->initialise(name); + + return new_obj; +} + GpiObjHdl *VpiImpl::get_root_handle(const char* name) { FENTER @@ -161,10 +205,9 @@ GpiCbHdl *VpiImpl::register_timed_callback(uint64_t time_ps) GpiCbHdl *VpiImpl::register_readwrite_callback(void) { - #if 0 FENTER - vpi_cb_readwrite *hdl = new VpiReadWriteCbHdl(this); + VpiReadwriteCbHdl *hdl = new VpiReadwriteCbHdl(this); if (hdl->arm_callback()) { delete(hdl); @@ -173,8 +216,6 @@ GpiCbHdl *VpiImpl::register_readwrite_callback(void) FEXIT return hdl; - #endif - return NULL; } GpiCbHdl *VpiImpl::register_readonly_callback(void) diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 68b2ab70..3f54b7d0 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -90,6 +90,7 @@ class VpiImpl : public GpiImplInterface { GpiCbHdl *register_readwrite_callback(void); int deregister_callback(GpiCbHdl *obj_hdl); bool native_check(std::string &name, GpiObjHdl *parent); + GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); const char * reason_to_string(int reason); }; @@ -105,8 +106,8 @@ class VpiObjHdl : public GpiObjHdl { virtual GpiIterator *iterate_handle(uint32_t type) { return NULL ;} virtual GpiObjHdl *next_handle(GpiIterator *iterator) { return NULL; } - const char* get_name_str(void); - const char* get_type_str(void); + //const char* get_name_str(void); + //const char* get_type_str(void); vpiHandle get_handle(void); @@ -117,7 +118,8 @@ class VpiObjHdl : public GpiObjHdl { class VpiCbHdl : public GpiCbHdl { public: - VpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl) { } + VpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + vpi_hdl(NULL) { } virtual ~VpiCbHdl() { } virtual int arm_callback(void); @@ -128,20 +130,42 @@ class VpiCbHdl : public GpiCbHdl { vpiHandle vpi_hdl; }; -class VpiSignalObjHdl : public GpiSignalObjHdl { +class VpiSignalObjHdl : public VpiObjHdl, public GpiSignalObjHdl { public: - VpiSignalObjHdl(GpiImplInterface *impl) : GpiSignalObjHdl(impl) { } + VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl) : VpiObjHdl(impl, hdl), + GpiSignalObjHdl(impl) { } virtual ~VpiSignalObjHdl() { } - virtual int set_signal_value(const int value); - virtual int set_signal_value(const char *str); + const char* get_signal_value_binstr(void); + + int set_signal_value(const int value); + int set_signal_value(std::string &value); //virtual GpiCbHdl monitor_value(bool rising_edge) = 0; this was for the triggers // but the explicit ones are probably better // Also think we want the triggers here? - virtual GpiCbHdl *rising_edge_cb(void); - virtual GpiCbHdl *falling_edge_cb(void); - virtual GpiCbHdl *value_change_cb(void); + virtual GpiCbHdl *rising_edge_cb(void) { return NULL; } + virtual GpiCbHdl *falling_edge_cb(void) { return NULL; } + virtual GpiCbHdl *value_change_cb(void) { return NULL; } + + /* Functions that I would like to inherit but do not ?*/ + virtual GpiObjHdl *get_handle_by_name(std::string &name) { + return VpiObjHdl::get_handle_by_name(name); + } + virtual GpiObjHdl *get_handle_by_index(uint32_t index) { + return VpiObjHdl::get_handle_by_index(index); + } + virtual GpiIterator *iterate_handle(uint32_t type) + { + return VpiObjHdl::iterate_handle(type); + } + virtual GpiObjHdl *next_handle(GpiIterator *iterator) + { + return VpiObjHdl::next_handle(iterator); + } + virtual int initialise(std::string name) { + return VpiObjHdl::initialise(name); + } }; class VpiTimedCbHdl : public VpiCbHdl { @@ -181,4 +205,11 @@ class VpiShutdownCbHdl : public VpiCbHdl { virtual ~VpiShutdownCbHdl() { } }; +class VpiReadwriteCbHdl : public VpiCbHdl { +public: + VpiReadwriteCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) { } + int arm_callback(void); + virtual ~VpiReadwriteCbHdl() { } +}; + #endif /*COCOTB_VPI_IMPL_H_ */ \ No newline at end of file From c735f5f8819ba10fcab1500b9e44bc67ee32fa62 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 11 Oct 2014 09:28:53 +0100 Subject: [PATCH 0612/2656] Work on #161 and #141 - FLI and C++ --- lib/fli/FliImpl.cpp | 92 ++++++++++++++++++++++++++ lib/fli/FliImpl.h | 153 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 245 insertions(+) create mode 100644 lib/fli/FliImpl.cpp create mode 100644 lib/fli/FliImpl.h diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp new file mode 100644 index 00000000..5837ec41 --- /dev/null +++ b/lib/fli/FliImpl.cpp @@ -0,0 +1,92 @@ +/****************************************************************************** +* Copyright (c) 2014 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +#include "FliImpl.h" + + +void FliImpl::sim_end(void) +{ + mti_Quit(); +} + + + +/** + * @name Get current simulation time + * @brief Get current simulation time + * + * NB units depend on the simulation configuration + */ +void FliImpl::get_sim_time(uint32_t *high, uint32_t *low) +{ + *high = mti_NowUpper(); + *low = mti_Now(); +} + +/** + * @name Find the root handle + * @brief Find the root handle using an optional name + * + * Get a handle to the root simulator object. This is usually the toplevel. + * + * If no name is provided, we return the first root instance. + * + * If name is provided, we check the name against the available objects until + * we find a match. If no match is found we return NULL + */ +GpiObjHdl *FliImpl::get_root_handle(const char *name) +{ + mtiRegionIdT root; + VpiObjHdl rv; + + for (root = mti_GetTopRegion(); root != NULL; root = mti_NextRegion(root)) { + if (name == NULL || !strcmp(name, mti_GetRegionName(root))) + break; + } + + if (!root) { + goto error; + } + + rv = new FliObjHdl(this, root); + rv->initialise(name); + return rv; + + error: + + LOG_CRITICAL("FLI: Couldn't find root handle %s", name); + + for (root = mti_GetTopRegion(); root != NULL; root = mti_NextRegion(root)) { + + LOG_CRITICAL("FLI: Toplevel instances: %s != %s...", name, mti_GetRegionName(root)); + + if (name == NULL) + break; + } + return NULL; +} + diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h new file mode 100644 index 00000000..7319dcda --- /dev/null +++ b/lib/fli/FliImpl.h @@ -0,0 +1,153 @@ +/****************************************************************************** +* Copyright (c) 2014 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +#ifndef COCOTB_FLI_IMPL_H_ +#define COCOTB_FLI_IMPL_H_ + +#include "../gpi/gpi_priv.h" +#include + + + +class FliImpl : public GpiImplInterface { +public: + FliImpl(const std::string& name) : GpiImplInterface(name) { } + + /* Sim related */ + void sim_end(void); + void get_sim_time(uint32_t *high, uint32_t *low); + + /* Hierachy related */ + GpiObjHdl *get_root_handle(const char *name); + + GpiCbHdl *register_timed_callback(uint64_t time_ps); + + /* Callback related, these may (will) return the same handle*/ + GpiCbHdl *register_readonly_callback(void); + GpiCbHdl *register_nexttime_callback(void); + GpiCbHdl *register_readwrite_callback(void); + int deregister_callback(GpiCbHdl *obj_hdl); + bool native_check(std::string &name, GpiObjHdl *parent); + +private: + GpiCbHdl m_readonly_cbhdl; + GpiCbHdl m_nexttime_cbhdl; + GpiCbHdl m_readwrite_cbhdl; + +}; + +class FliObjHdl : public GpiObjHdl { +public: + FliObjHdl(GpiImplInterface *impl, mtiRegionIdT hdl) : GpiObjHdl(impl), + fli_hdl(hdl) { } + virtual ~FliObjHdl() { } + + virtual GpiObjHdl *get_handle_by_name(std::string &name); + virtual GpiObjHdl *get_handle_by_index(uint32_t index); + virtual GpiIterator *iterate_handle(uint32_t type) { return NULL ;} + virtual GpiObjHdl *next_handle(GpiIterator *iterator) { return NULL; } + + const char* get_name_str(void); + const char* get_type_str(void); + +protected: + mtiRegionIdT fli_hdl; + +}; + +class FliCbHdl : public GpiCbHdl { +public: + FliCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl) { } + virtual ~FliCbHdl() { } + + virtual int arm_callback(void); + virtual int cleanup_callback(void); + +protected: + int register_cb(p_cb_data cb_data); +}; + + +// In FLI some callbacks require us to register a process +// We use a subclass to track the process state related to the callback +class FliProcessCbHdl : public FliCbHdl { + +public: + FliCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + m_proc_hdl(NULL) { } + virtual ~FliCbHdl() { } + + virtual int arm_callback(void); + virtual int cleanup_callback(void); + +private: + mtiProcessIdt m_proc_hdl; + bool m_sensitised; +} + + +class FliSignalObjHdl : public GpiSignalObjHdl { +public: + FliSignalObjHdl(GpiImplInterface *impl) : GpiSignalObjHdl(impl) { } + virtual ~FliSignalObjHdl() { } + + virtual int set_signal_value(const int value); + virtual int set_signal_value(const char *str); + + virtual GpiCbHdl *rising_edge_cb(void); + virtual GpiCbHdl *falling_edge_cb(void); + virtual GpiCbHdl *value_change_cb(void); + +private: + mtiSignalIdt fli_hdl; + +}; + +class FliTimedCbHdl : public FliCbHdl { +public: + FliTimedCbHdl(GpiImplInterface *impl) : FliCbHdl(impl) { } + int arm_callback(uint64_t time_ps); + virtual ~FliTimedCbHdl() { } +}; + +class FliStartupCbHdl : public FliCbHdl { +public: + FliStartupCbHdl(GpiImplInterface *impl) : FliCbHdl(impl) { } + int run_callback(void); + int arm_callback(void); + virtual ~FliStartupCbHdl() { } +}; + +class FliShutdownCbHdl : public FliCbHdl { +public: + FliShutdownCbHdl(GpiImplInterface *impl) : FliCbHdl(impl) { } + int run_callback(void); + int arm_callback(void); + virtual ~FliShutdownCbHdl() { } +}; + +#endif /*COCOTB_FLI_IMPL_H_ */ From 63fd61d001d5af4a95819c7004a270558b97348e Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 11 Oct 2014 09:29:55 +0100 Subject: [PATCH 0613/2656] Issue #161: Ensure we don't leak handles from Python --- cocotb/handle.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 7217126a..562e698b 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -100,10 +100,18 @@ def __setattr__(self, name, value): object.__setattr__(self, name, value) def __hasattr__(self, name): - """Since calling hasattr(handle, "something") will print out a - backtrace to the log since usually attempting to access a - non-existent member is an error we provide a 'peek function""" - return bool(simulator.get_handle_by_name(self._handle, name)) + """ + Since calling hasattr(handle, "something") will print out a + backtrace to the log since usually attempting to access a + non-existent member is an error we provide a 'peek function + + We still add the found handle to our dictionary to prevent leaking + handles. + """ + new_handle = simulator.get_handle_by_name(self._handle, name) + if new_handle: + self._sub_handles[name] = SimHandle(new_handle) + return new_handle def __getitem__(self, index): if index in self._sub_handles: From e66134f2deb96f0bf8e6a74e19d8bad630e52580 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sat, 11 Oct 2014 15:58:59 +0100 Subject: [PATCH 0614/2656] Issue#161: Implement get_handle_by_index to pass test_discovery on aldec --- include/vpi_user.h | 5 ++++- lib/gpi/GpiCommon.cpp | 23 +++++++++++++++++------ lib/gpi/gpi_priv.h | 1 + lib/vpi/VpiImpl.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ lib/vpi/VpiImpl.h | 1 + 5 files changed, 66 insertions(+), 7 deletions(-) diff --git a/include/vpi_user.h b/include/vpi_user.h index bf2135f0..5cd9cd68 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -42,8 +42,11 @@ extern "C" { typedef uint32_t *vpiHandle; -#define vpiNet 36 /* scalar or vector net */ #define vpiModule 32 /* module instance */ +#define vpiNet 36 /* scalar or vector net */ +#define vpiNetBit 37 /* bit of a vector net */ +#define vpiReg 48 /* scalar or vector reg */ +#define vpiRegBit 49 /* bit of vector reg */ #define vpiStructVar 618 #define vpiStop 66 /* execute simulator's $stop */ diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index e83db0ba..a8b821e2 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -112,13 +112,24 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) { + vector::iterator iter; + + GpiObjHdl *hdl; + GpiObjHdl *base = sim_to_hdl(parent); + LOG_WARN("Trying index"); -#if 0 - /* Either want this or use the parent */ - GpiObjHdl *obj_hdl = sim_to_hdl(parent); - return (void*)obj_hdl->m_impl->get_handle_by_index(obj_hdl, index); -#endif - return NULL; + + for (iter = registered_impls.begin(); + iter != registered_impls.end(); + iter++) { + LOG_WARN("Checking if %d native though impl %s ", index, (*iter)->get_name_c()); + if ((hdl = (*iter)->native_check_create(index, base))) { + LOG_WARN("Found %d via %s", index, (*iter)->get_name_c()); + //hdl = base->get_handle_by_name(s_name); + } + } + + return (gpi_sim_hdl)hdl; } gpi_iterator_hdl gpi_iterate(uint32_t type, gpi_sim_hdl base) diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index bf2b0eae..bac3d3d3 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -206,6 +206,7 @@ class GpiImplInterface { /* Hierachy related */ virtual bool native_check(std::string &name, GpiObjHdl *parent) = 0; virtual GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent) = 0; + virtual GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent) = 0; virtual GpiObjHdl *get_root_handle(const char *name) = 0; /* Callback related, these may (will) return the same handle*/ diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 60a5b343..7ec8d050 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -116,6 +116,7 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) /* What sort of isntance is this ?*/ switch (type) { case vpiNet: + case vpiReg: new_obj = new VpiSignalObjHdl(this, new_hdl); LOG_WARN("Created VpiSignalObjHdl"); break; @@ -135,6 +136,48 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) return new_obj; } +GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) +{ + int32_t type; + VpiObjHdl *parent_hdl = sim_to_hdl(parent); + vpiHandle vpi_hdl = parent_hdl->get_handle(); + vpiHandle new_hdl; + VpiObjHdl *new_obj = NULL; + + new_hdl = vpi_handle_by_index(vpi_hdl, index); + + if (!new_hdl) + return NULL; + + if (vpiUnknown == (type = vpi_get(vpiType, new_hdl))) { + vpi_free_object(vpi_hdl); + return new_obj; + } + + /* What sort of isntance is this ?*/ + switch (type) { + case vpiNet: + case vpiNetBit: + new_obj = new VpiSignalObjHdl(this, new_hdl); + LOG_WARN("Created VpiSignalObjHdl"); + break; + case vpiModule: + new_obj = new VpiObjHdl(this, new_hdl); + LOG_WARN("Created VpiObjHdl"); + break; + default: + LOG_CRITICAL("Not sure what to do with type %d below entity (%s) at index (%d)", + type, parent->get_name_str(), index); + return false; + } + + LOG_WARN("Type was %d", type); + /* Might move the object creation inside here */ + //new_obj->initialise(name); + + return new_obj; +} + GpiObjHdl *VpiImpl::get_root_handle(const char* name) { FENTER diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 3f54b7d0..66376472 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -91,6 +91,7 @@ class VpiImpl : public GpiImplInterface { int deregister_callback(GpiCbHdl *obj_hdl); bool native_check(std::string &name, GpiObjHdl *parent); GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); + GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); const char * reason_to_string(int reason); }; From 716d5b706c6dfa7ddc896fbd488914b475da0867 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sat, 11 Oct 2014 17:58:38 +0100 Subject: [PATCH 0615/2656] Issue#161: Push common code into Derived callback constructors --- lib/gpi/GpiCbHdl.cpp | 7 - lib/vhpi/{gpi_vhpi.cpp => VpiImpl.cpp} | 0 lib/vpi/VpiCbHdl.cpp | 202 +++++-------------------- lib/vpi/VpiImpl.cpp | 4 +- lib/vpi/VpiImpl.h | 24 +-- 5 files changed, 47 insertions(+), 190 deletions(-) rename lib/vhpi/{gpi_vhpi.cpp => VpiImpl.cpp} (100%) diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index e6ff0519..7a2c8c83 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -77,13 +77,6 @@ int GpiObjHdl::initialise(std::string name) return 0; } -#if 0 -int GpiCbHdl::handle_callback(void) -{ - return this->gpi_function(m_cb_data); -} -#endif - int GpiCbHdl::run_callback(void) { LOG_WARN("Generic run_callback"); diff --git a/lib/vhpi/gpi_vhpi.cpp b/lib/vhpi/VpiImpl.cpp similarity index 100% rename from lib/vhpi/gpi_vhpi.cpp rename to lib/vhpi/VpiImpl.cpp diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 320ccc73..1dfa0bc2 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -37,135 +37,41 @@ vpiHandle VpiObjHdl::get_handle(void) return vpi_hdl; } -/** - * @brief Get a handle to an object under the scope of parent - * - * @param name of the object to find, below this object in the hierachy - * - * @return gpi_sim_hdl for the new object or NULL if object not found - */ -GpiObjHdl *VpiObjHdl::get_handle_by_name(std::string &name) +VpiCbHdl::VpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + vpi_hdl(NULL) { - FENTER - GpiObjHdl *rv = NULL; - vpiHandle obj; - vpiHandle iterator; - std::vector writable(name.begin(), name.end()); - writable.push_back('\0'); - //int len; - //char *buff; - - // Structures aren't technically a scope, according to the LRM. If parent - // is a structure then we have to iterate over the members comparing names - if (vpiStructVar == vpi_get(vpiType, vpi_hdl)) { - - iterator = vpi_iterate(vpiMember, vpi_hdl); - - for (obj = vpi_scan(iterator); obj != NULL; obj = vpi_scan(iterator)) { - - if (!strcmp(name.c_str(), strrchr(vpi_get_str(vpiName, obj), 46) + 1)) - break; - } - - if (!obj) - return NULL; - - // Need to free the iterator if it didn't return NULL - if (!vpi_free_object(iterator)) { - LOG_WARN("VPI: Attempting to free root iterator failed!"); - check_vpi_error(); - } - - goto success; - } - - #if 0 - Do we need this - if (name) - len = strlen(name) + 1; - - buff = (char *)malloc(len); - if (buff == NULL) { - LOG_CRITICAL("VPI: Attempting allocate string buffer failed!"); - return NULL; - } - - strncpy(buff, name, len); - #endif - - - obj = vpi_handle_by_name(&writable[0], vpi_hdl); - if (!obj) { - LOG_DEBUG("VPI: Handle '%s' not found!", name.c_str()); - - // NB we deliberately don't dump an error message here because it's - // a valid use case to attempt to grab a signal by name - for example - // optional signals on a bus. - // check_vpi_error(); - //free(buff); - return NULL; - } - - //free(buff); - -success: - // rv = new VpiObjHdl(obj); - - FEXIT - return rv; -} - -/** - * @brief Get a handle for an object based on its index within a parent - * - * @param parent handle to the parent - * @param indext Index to retrieve - * - * Can be used on bit-vectors to access a specific bit or - * memories to access an address - */ -GpiObjHdl *VpiObjHdl::get_handle_by_index(uint32_t index) -{ - FENTER - vpiHandle obj; - GpiObjHdl *rv = NULL; - - obj = vpi_handle_by_index(vpi_hdl, index); - if (!obj) { - LOG_ERROR("VPI: Handle idx '%d' not found!", index); - return NULL; - } - - //rv = new VpiObjHdl(obj, this); - - FEXIT - return rv; + cb_data.reason = 0; + cb_data.cb_rtn = handle_vpi_callback; + cb_data.obj = NULL; + cb_data.time = NULL; + cb_data.value = NULL; + cb_data.user_data = (char*)this; } /* If the user data already has a callback handle then deregister * before getting the new one */ -int VpiCbHdl::register_cb(p_cb_data cb_data) { +int VpiCbHdl::arm_callback(void) { if (m_state == GPI_PRIMED) { fprintf(stderr, "Attempt to prime an already primed trigger for %s!\n", - m_impl->reason_to_string(cb_data->reason)); + m_impl->reason_to_string(cb_data.reason)); } if (vpi_hdl != NULL) { fprintf(stderr, "We seem to already be registered, deregistering %s!\n", - m_impl->reason_to_string(cb_data->reason)); + m_impl->reason_to_string(cb_data.reason)); cleanup_callback(); } - vpiHandle new_hdl = vpi_register_cb(cb_data); + vpiHandle new_hdl = vpi_register_cb(&cb_data); int ret = 0; if (!new_hdl) { LOG_CRITICAL("VPI: Unable to register callback a handle for VPI type %s(%d)", - m_impl->reason_to_string(cb_data->reason), cb_data->reason); + m_impl->reason_to_string(cb_data.reason), cb_data.reason); check_vpi_error(); ret = -1; } @@ -181,11 +87,6 @@ int VpiCbHdl::cleanup_callback(void) return 0; } -int VpiCbHdl::arm_callback(void) -{ - return 0; -} - const char* VpiSignalObjHdl::get_signal_value_binstr(void) { FENTER @@ -241,6 +142,11 @@ int VpiSignalObjHdl::set_signal_value(std::string &value) return 0; } +VpiStartupCbHdl::VpiStartupCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) +{ + cb_data.reason = cbStartOfSimulation; +} + int VpiStartupCbHdl::run_callback(void) { s_vpi_vlog_info info; gpi_sim_info_t sim_info; @@ -257,17 +163,9 @@ int VpiStartupCbHdl::run_callback(void) { return 0; } -int VpiStartupCbHdl::arm_callback(void) { - s_cb_data cb_data_s; - - cb_data_s.reason = cbStartOfSimulation; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = NULL; - cb_data_s.value = NULL; - cb_data_s.user_data = (char*)this; - - return register_cb(&cb_data_s); +VpiShutdownCbHdl::VpiShutdownCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) +{ + cb_data.reason = cbEndOfSimulation; } int VpiShutdownCbHdl::run_callback(void) { @@ -276,54 +174,28 @@ int VpiShutdownCbHdl::run_callback(void) { return 0; } -int VpiShutdownCbHdl::arm_callback(void) { - s_cb_data cb_data_s; - - cb_data_s.reason = cbEndOfSimulation; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = NULL; - cb_data_s.value = NULL; - cb_data_s.user_data = (char*)this; - - return register_cb(&cb_data_s); -} - - -int VpiTimedCbHdl::arm_callback(uint64_t time_ps) { - s_cb_data cb_data_s; - s_vpi_time vpi_time_s; - - vpi_time_s.type = vpiSimTime; - vpi_time_s.high = (uint32_t)(time_ps>>32); - vpi_time_s.low = (uint32_t)(time_ps); +VpiTimedCbHdl::VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : VpiCbHdl(impl) +{ + s_vpi_time vpi_time; - cb_data_s.reason = cbAfterDelay; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = &vpi_time_s; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)this; + vpi_time.type = vpiSimTime; + vpi_time.high = (uint32_t)(time_ps>>32); + vpi_time.low = (uint32_t)(time_ps); - return register_cb(&cb_data_s); + cb_data.reason = cbAfterDelay; + cb_data.time = &vpi_time; } -int VpiReadwriteCbHdl::arm_callback(void) { - s_cb_data cb_data_s; - s_vpi_time vpi_time_s; - - vpi_time_s.type = vpiSimTime; - vpi_time_s.high = 0; - vpi_time_s.low = 0; +VpiReadwriteCbHdl::VpiReadwriteCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) +{ + s_vpi_time vpi_time; - cb_data_s.reason = cbReadWriteSynch; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = &vpi_time_s; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)this; + vpi_time.type = vpiSimTime; + vpi_time.high = 0; + vpi_time.low = 0; - return register_cb(&cb_data_s); + cb_data.reason = cbReadWriteSynch; + cb_data.time = &vpi_time; } #if 0 diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 7ec8d050..d381f20c 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -235,9 +235,9 @@ GpiCbHdl *VpiImpl::register_timed_callback(uint64_t time_ps) { FENTER - VpiTimedCbHdl *hdl = new VpiTimedCbHdl(this); + VpiTimedCbHdl *hdl = new VpiTimedCbHdl(this, time_ps); - if (hdl->arm_callback(time_ps)) { + if (hdl->arm_callback()) { delete(hdl); hdl = NULL; } diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 66376472..6fded060 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -102,14 +102,11 @@ class VpiObjHdl : public GpiObjHdl { vpi_hdl(hdl) { } virtual ~VpiObjHdl() { } - virtual GpiObjHdl *get_handle_by_name(std::string &name); - virtual GpiObjHdl *get_handle_by_index(uint32_t index); + virtual GpiObjHdl *get_handle_by_name(std::string &name) { return NULL; } + virtual GpiObjHdl *get_handle_by_index(uint32_t index) { return NULL; } virtual GpiIterator *iterate_handle(uint32_t type) { return NULL ;} virtual GpiObjHdl *next_handle(GpiIterator *iterator) { return NULL; } - //const char* get_name_str(void); - //const char* get_type_str(void); - vpiHandle get_handle(void); protected: @@ -119,16 +116,15 @@ class VpiObjHdl : public GpiObjHdl { class VpiCbHdl : public GpiCbHdl { public: - VpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - vpi_hdl(NULL) { } + VpiCbHdl(GpiImplInterface *impl); virtual ~VpiCbHdl() { } virtual int arm_callback(void); virtual int cleanup_callback(void); protected: - int register_cb(p_cb_data cb_data); vpiHandle vpi_hdl; + s_cb_data cb_data; }; class VpiSignalObjHdl : public VpiObjHdl, public GpiSignalObjHdl { @@ -171,8 +167,7 @@ class VpiSignalObjHdl : public VpiObjHdl, public GpiSignalObjHdl { class VpiTimedCbHdl : public VpiCbHdl { public: - VpiTimedCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) { } - int arm_callback(uint64_t time_ps); + VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps); virtual ~VpiTimedCbHdl() { } }; @@ -192,24 +187,21 @@ class VpiNextPhaseCbHdl : public VpiCbHdl { class VpiStartupCbHdl : public VpiCbHdl { public: - VpiStartupCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) { } + VpiStartupCbHdl(GpiImplInterface *impl); int run_callback(void); - int arm_callback(void); virtual ~VpiStartupCbHdl() { } }; class VpiShutdownCbHdl : public VpiCbHdl { public: - VpiShutdownCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) { } + VpiShutdownCbHdl(GpiImplInterface *impl); int run_callback(void); - int arm_callback(void); virtual ~VpiShutdownCbHdl() { } }; class VpiReadwriteCbHdl : public VpiCbHdl { public: - VpiReadwriteCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) { } - int arm_callback(void); + VpiReadwriteCbHdl(GpiImplInterface *impl); virtual ~VpiReadwriteCbHdl() { } }; From deaaf2a451a057601811aa678cec7974938fa275 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sat, 11 Oct 2014 18:35:30 +0100 Subject: [PATCH 0616/2656] Issue#161: make gpi_copy_name return a non const char* --- lib/gpi/GpiCbHdl.cpp | 2 +- lib/gpi/gpi_priv.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index 7a2c8c83..11c72f15 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -39,7 +39,7 @@ const char * GpiObjHdl::get_type_str(void) } /* Genertic base clss implementations */ -const char *GpiHdl::gpi_copy_name(const char *name) +char *GpiHdl::gpi_copy_name(const char *name) { int len; char *result; diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index bac3d3d3..e587d1e2 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -71,7 +71,7 @@ class GpiHdl { public: GpiImplInterface *m_impl; // VPI/VHPI/FLI routines - const char *gpi_copy_name(const char *name); // Might not be needed + char *gpi_copy_name(const char *name); // Might not be needed }; /* GPI object handle, maps to a simulation object */ From 2dede61eecd46d6445da0b0d554a4435cfe2934c Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sat, 11 Oct 2014 18:36:12 +0100 Subject: [PATCH 0617/2656] Issue#161: Put time structure in class scope so address is valid when arm_callback called --- lib/vpi/VpiCbHdl.cpp | 4 ---- lib/vpi/VpiImpl.h | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 1dfa0bc2..b36e66c0 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -176,8 +176,6 @@ int VpiShutdownCbHdl::run_callback(void) { VpiTimedCbHdl::VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : VpiCbHdl(impl) { - s_vpi_time vpi_time; - vpi_time.type = vpiSimTime; vpi_time.high = (uint32_t)(time_ps>>32); vpi_time.low = (uint32_t)(time_ps); @@ -188,8 +186,6 @@ VpiTimedCbHdl::VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : VpiCbHd VpiReadwriteCbHdl::VpiReadwriteCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) { - s_vpi_time vpi_time; - vpi_time.type = vpiSimTime; vpi_time.high = 0; vpi_time.low = 0; diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 6fded060..6c6a1e9e 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -169,6 +169,8 @@ class VpiTimedCbHdl : public VpiCbHdl { public: VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps); virtual ~VpiTimedCbHdl() { } +private: + s_vpi_time vpi_time; }; #if 0 @@ -203,6 +205,8 @@ class VpiReadwriteCbHdl : public VpiCbHdl { public: VpiReadwriteCbHdl(GpiImplInterface *impl); virtual ~VpiReadwriteCbHdl() { } +private: + s_vpi_time vpi_time; }; #endif /*COCOTB_VPI_IMPL_H_ */ \ No newline at end of file From 577c0fdc6b4b49a658c2adc7de4e8f42becaaa14 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sat, 11 Oct 2014 18:38:25 +0100 Subject: [PATCH 0618/2656] Issue#161: Bring back vhpi, compiles but not working --- lib/Makefile | 11 +- lib/vhpi/Makefile | 6 +- lib/vhpi/VhpiCbHdl.cpp | 142 +++++++++++ lib/vhpi/{VpiImpl.cpp => VhpiImpl.cpp} | 331 +++++-------------------- lib/vhpi/VhpiImpl.h | 210 ++++++++++++++++ 5 files changed, 420 insertions(+), 280 deletions(-) create mode 100644 lib/vhpi/VhpiCbHdl.cpp rename lib/vhpi/{VpiImpl.cpp => VhpiImpl.cpp} (51%) create mode 100644 lib/vhpi/VhpiImpl.h diff --git a/lib/Makefile b/lib/Makefile index 0779b5ba..a0dbd026 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -55,7 +55,7 @@ $(LIB_DIR)/libcocotb.so: $(SIM_ROOT)/lib/embed/gpi_embed.c | $(LIB_DIR) $(LIB_DIR)/libvpi.so: $(SIM_ROOT)/lib/vpi/VpiImpl.cpp $(SIM_ROOT)/lib/vpi/VpiCbHdl.cpp | $(LIB_DIR) make -C $(SIM_ROOT)/lib/vpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) -$(LIB_DIR)/libvhpi.so: $(SIM_ROOT)/lib/vhpi/gpi_vhpi.cpp | $(LIB_DIR) +$(LIB_DIR)/libvhpi.so: $(SIM_ROOT)/lib/vhpi/VhpiImpl.cpp $(SIM_ROOT)/lib/vhpi/VhpiCbHdl.cpp | $(LIB_DIR) make -C $(SIM_ROOT)/lib/vhpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) $(LIB_DIR)/libgpi.so: $(SIM_ROOT)/lib/gpi/GpiCommon.cpp $(SIM_ROOT)/lib/gpi/GpiCbHdl.cpp | $(LIB_DIR) @@ -65,10 +65,11 @@ $(LIB_DIR)/libsim.so: $(SIM_ROOT)/lib/simulator/simulatormodule.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/simulator COCOTB_LIBS = $(LIB_DIR)/libgpilog.so \ - $(LIB_DIR)/libcocotb.so \ - $(LIB_DIR)/libgpi.so \ - $(LIB_DIR)/libvpi.so \ - $(LIB_DIR)/libsim.so + $(LIB_DIR)/libcocotb.so \ + $(LIB_DIR)/libgpi.so \ + $(LIB_DIR)/libvpi.so \ + $(LIB_DIR)/libsim.so \ + $(LIB_DIR)/libvhpi.so clean:: -@rm -rf $(BUILD_DIR) diff --git a/lib/vhpi/Makefile b/lib/vhpi/Makefile index 850a8e42..abe72a63 100644 --- a/lib/vhpi/Makefile +++ b/lib/vhpi/Makefile @@ -31,15 +31,15 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += GCC_ARGS += -DVPI_CHECKING -LIBS := -lgpilog +LIBS := -lgpilog -lgpi -lstdc++ LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) LIB_NAME := libvhpi.so -SRCS := gpi_vhpi.c +SRCS := VhpiImpl.cpp VhpiCbHdl.cpp all: $(LIB_DIR)/$(LIB_NAME) clean: -@rm -rf $(LIB_DIR)/$(LIB_NAME) -include $(SIM_ROOT)/makefiles/Makefile.rules +include $(SIM_ROOT)/makefiles/Makefile.rules \ No newline at end of file diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp new file mode 100644 index 00000000..293ef338 --- /dev/null +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -0,0 +1,142 @@ +/****************************************************************************** +* Copyright (c) 2013 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd, +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +#include "VhpiImpl.h" +#include + +extern "C" void handle_vhpi_callback(const vhpiCbDataT *cb_data); + +vhpiHandleT VhpiObjHdl::get_handle(void) +{ + return vhpi_hdl; +} + +VhpiCbHdl::VhpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + vhpi_hdl(NULL) +{ + cb_data.reason = 0; + cb_data.cb_rtn = handle_vhpi_callback; + cb_data.obj = NULL; + cb_data.time = NULL; + cb_data.value = NULL; + cb_data.user_data = (char *)this; +} + +int VhpiCbHdl::cleanup_callback(void) +{ + vhpiStateT cbState = (vhpiStateT)vhpi_get(vhpiStateP, vhpi_hdl); + if (vhpiMature == cbState) + return vhpi_remove_cb(vhpi_hdl); + return 0; +} + +int VhpiCbHdl::arm_callback(void) +{ + vhpiHandleT new_hdl = vhpi_register_cb(&cb_data, vhpiReturnCb); + int ret = 0; + + if (!new_hdl) { + LOG_CRITICAL("VHPI: Unable to register callback a handle for VHPI type %s(%d)", + m_impl->reason_to_string(cb_data.reason), cb_data.reason); + check_vhpi_error(); + ret = -1; + } + + vhpiStateT cbState = (vhpiStateT)vhpi_get(vhpiStateP, new_hdl); + if (cbState != vhpiEnable) { + LOG_CRITICAL("VHPI ERROR: Registered callback isn't enabled! Got %d\n", cbState); + } + + vhpi_hdl = new_hdl; + m_state = GPI_PRIMED; + + return ret; +} + +const char* VhpiSignalObjHdl::get_signal_value_binstr(void) +{ + s_vpi_value value_s = {vpiBinStrVal}; + + vpi_get_value(vhpi_hdl, &value_s); + check_vhpi_error(); + + LOG_WARN("Value back was %s", value_s.value.str); + + return value_s.value.str; +} + +// Value related functions +int VhpiSignalObjHdl::set_signal_value(int value) +{ + + return 0; +} + +int VhpiSignalObjHdl::set_signal_value(std::string &value) +{ + + return 0; +} + +VhpiStartupCbHdl::VhpiStartupCbHdl(GpiImplInterface *impl) : VhpiCbHdl(impl) +{ + cb_data.reason = vhpiCbStartOfSimulation; +} + +int VhpiStartupCbHdl::run_callback(void) { + gpi_sim_info_t sim_info; + sim_info.argc = 0; + sim_info.argv = NULL; + sim_info.product = gpi_copy_name(vhpi_get_str(vhpiNameP, NULL)); + sim_info.version = gpi_copy_name(vhpi_get_str(vhpiToolVersionP, NULL)); + gpi_embed_init(&sim_info); + + free(sim_info.product); + free(sim_info.version); + + return 0; +} + +VhpiShutdownCbHdl::VhpiShutdownCbHdl(GpiImplInterface *impl) : VhpiCbHdl(impl) +{ + cb_data.reason = vhpiCbEndOfSimulation; +} + +int VhpiShutdownCbHdl::run_callback(void) { + LOG_WARN("Shutdown called"); + gpi_embed_end(); + return 0; +} + +VhpiTimedCbHdl::VhpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : VhpiCbHdl(impl) +{ + vhpi_time.high = (uint32_t)(time_ps>>32); + vhpi_time.low = (uint32_t)(time_ps); + + cb_data.reason = vhpiCbAfterDelay; + cb_data.time = &vhpi_time; +} \ No newline at end of file diff --git a/lib/vhpi/VpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp similarity index 51% rename from lib/vhpi/VpiImpl.cpp rename to lib/vhpi/VhpiImpl.cpp index 122a8b2b..3f773167 100644 --- a/lib/vhpi/VpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -32,16 +32,15 @@ // VHPI seems to run significantly slower than VPI, need to investigate. -#include "../gpi/gpi_priv.h" -#include - -#define VHPI_CHECKING 1 +#include "VhpiImpl.h" +#include extern "C" { -void handle_vhpi_callback(const vhpiCbDataT *cb_data); -int __check_vhpi_error(const char *func, long line); -const char * vhpi_format_to_string(int reason); -const vhpiEnumT chr2vhpi(const char value); + +static VhpiCbHdl *sim_init_cb; +static VhpiCbHdl *sim_finish_cb; +static VhpiImpl *vhpi_table; + } const char * vhpi_format_to_string(int reason) @@ -105,54 +104,16 @@ const vhpiEnumT chr2vhpi(const char value) } } -// Should be run after every VPI call to check error status -int __check_vhpi_error(const char *func, long line) -{ - int level=0; -#if VHPI_CHECKING - vhpiErrorInfoT info; - int loglevel; - level = vhpi_check_error(&info); - if (level == 0) - return 0; - - switch (level) { - case vhpiNote: - loglevel = GPIInfo; - break; - case vhpiWarning: - loglevel = GPIWarning; - break; - case vhpiError: - loglevel = GPIError; - break; - case vhpiFailure: - case vhpiSystem: - case vhpiInternal: - loglevel = GPICritical; - break; - } - - gpi_log("cocotb.gpi", loglevel, __FILE__, func, line, - "VHPI Error level %d: %s\nFILE %s:%d", - info.severity, info.message, info.file, info.line); - -#endif - return level; -} - -#define check_vhpi_error() \ - __check_vhpi_error(__func__, __LINE__) - -class vhpi_obj_hdl : public gpi_obj_hdl { +#if 0 +class VhpiObjHdl : public GpiObjHdl { private: int m_size; vhpiValueT m_value; public: - vhpi_obj_hdl(vhpiHandleT hdl, gpi_impl_interface *impl) : gpi_obj_hdl(impl), + VhpiObjHdl(vhpiHandleT hdl, gpi_impl_interface *impl) : GpiObjHdl(impl), m_size(0), vhpi_hdl(hdl) { } - virtual ~vhpi_obj_hdl() { + virtual ~VhpiObjHdl() { if (m_value.format == vhpiEnumVecVal || m_value.format == vhpiLogicVecVal) { free(m_value.value.enumvs); @@ -262,182 +223,51 @@ class vhpi_obj_hdl : public gpi_obj_hdl { return check_vhpi_error(); } }; +#endif -class vhpi_cb_hdl : public gpi_cb_hdl { -protected: - vhpiCbDataT cb_data; - vhpiHandleT vhpi_hdl; -public: - vhpi_cb_hdl(gpi_impl_interface *impl) : gpi_cb_hdl(impl) { - cb_data.reason = 0; - cb_data.cb_rtn = handle_vhpi_callback; - cb_data.obj = NULL; - cb_data.time = NULL; - cb_data.value = NULL; - cb_data.user_data = (char *)this; - } - - int arm_callback(void) { - vhpiHandleT new_hdl = vhpi_register_cb(&cb_data, vhpiReturnCb); - int ret = 0; - - if (!new_hdl) { - LOG_CRITICAL("VHPI: Unable to register callback a handle for VHPI type %s(%d)", - reason_to_string(cb_data.reason), cb_data.reason); - check_vhpi_error(); - ret = -1; - } - - vhpiStateT cbState = (vhpiStateT)vhpi_get(vhpiStateP, new_hdl); - if (cbState != vhpiEnable) { - LOG_CRITICAL("VHPI ERROR: Registered callback isn't enabled! Got %d\n", cbState); - } - - vhpi_hdl = new_hdl; - m_state = GPI_PRIMED; - - return ret; - }; - - int cleanup_callback(void) { - vhpiStateT cbState = (vhpiStateT)vhpi_get(vhpiStateP, vhpi_hdl); - if (vhpiMature == cbState) - return vhpi_remove_cb(vhpi_hdl); - return 0; - } - - const char *reason_to_string(int reason) { - switch (reason) { - case vhpiCbValueChange: - return "vhpiCbValueChange"; - case vhpiCbStartOfNextCycle: - return "vhpiCbStartOfNextCycle"; - case vhpiCbStartOfPostponed: - return "vhpiCbStartOfPostponed"; - case vhpiCbEndOfTimeStep: - return "vhpiCbEndOfTimeStep"; - case vhpiCbNextTimeStep: - return "vhpiCbNextTimeStep"; - case vhpiCbAfterDelay: - return "vhpiCbAfterDelay"; - case vhpiCbStartOfSimulation: - return "vhpiCbStartOfSimulation"; - case vhpiCbEndOfSimulation: - return "vhpiCbEndOfSimulation"; - case vhpiCbEndOfProcesses: - return "vhpiCbEndOfProcesses"; - case vhpiCbLastKnownDeltaCycle: - return "vhpiCbLastKnownDeltaCycle"; - default: - return "unknown"; - } - } - -}; - -class vhpi_cb_startup : public vhpi_cb_hdl { -public: - vhpi_cb_startup(gpi_impl_interface *impl) : vhpi_cb_hdl(impl) { - cb_data.reason = vhpiCbStartOfSimulation; - } - - int run_callback(void) { - FENTER - gpi_sim_info_t sim_info; - sim_info.argc = 0; - sim_info.argv = NULL; - sim_info.product = gpi_copy_name(vhpi_get_str(vhpiNameP, NULL)); - sim_info.version = gpi_copy_name(vhpi_get_str(vhpiToolVersionP, NULL)); - gpi_embed_init(&sim_info); - - free(sim_info.product); - free(sim_info.version); - - FEXIT - - return 0; - } -}; - -class vhpi_cb_shutdown : public vhpi_cb_hdl { -public: - vhpi_cb_shutdown(gpi_impl_interface *impl) : vhpi_cb_hdl(impl) { - cb_data.reason = vhpiCbEndOfSimulation; - } - int run_callback(void) { - gpi_embed_end(); - return 0; - } -}; - -class vhpi_cb_timed : public vhpi_cb_hdl { -private: -vhpiTimeT time; -public: - vhpi_cb_timed(gpi_impl_interface *impl, uint64_t time_ps) : vhpi_cb_hdl(impl) { - time.high = (uint32_t)(time_ps>>32); - time.low = (uint32_t)(time_ps); - - cb_data.reason = vhpiCbAfterDelay; - cb_data.time = &time; +const char *VhpiImpl::reason_to_string(int reason) +{ + switch (reason) { + case vhpiCbValueChange: + return "vhpiCbValueChange"; + case vhpiCbStartOfNextCycle: + return "vhpiCbStartOfNextCycle"; + case vhpiCbStartOfPostponed: + return "vhpiCbStartOfPostponed"; + case vhpiCbEndOfTimeStep: + return "vhpiCbEndOfTimeStep"; + case vhpiCbNextTimeStep: + return "vhpiCbNextTimeStep"; + case vhpiCbAfterDelay: + return "vhpiCbAfterDelay"; + case vhpiCbStartOfSimulation: + return "vhpiCbStartOfSimulation"; + case vhpiCbEndOfSimulation: + return "vhpiCbEndOfSimulation"; + case vhpiCbEndOfProcesses: + return "vhpiCbEndOfProcesses"; + case vhpiCbLastKnownDeltaCycle: + return "vhpiCbLastKnownDeltaCycle"; + default: + return "unknown"; } -}; - +} -class vhpi_impl : public gpi_impl_interface { -public: - vhpi_impl(const string& name) : gpi_impl_interface(name) { } - - /* Sim related */ - void sim_end(void) { } - void get_sim_time(uint32_t *high, uint32_t *low) { } - - /* Signal related */ - gpi_obj_hdl *get_root_handle(const char *name); - gpi_obj_hdl *get_handle_by_name(const char *name, gpi_obj_hdl *parent) { return NULL; } - gpi_obj_hdl *get_handle_by_index(gpi_obj_hdl *parent, uint32_t index) { return NULL; } - void free_handle(gpi_obj_hdl*) { } - gpi_iterator *iterate_handle(uint32_t type, gpi_obj_hdl *base) { return NULL; } - gpi_obj_hdl *next_handle(gpi_iterator *iterator) { return NULL; } - char* get_signal_value_binstr(gpi_obj_hdl *gpi_hdl) { return NULL; } - char* get_signal_name_str(gpi_obj_hdl *gpi_hdl); - char* get_signal_type_str(gpi_obj_hdl *gpi_hdl); - void set_signal_value_int(gpi_obj_hdl *gpi_hdl, int value); - void set_signal_value_str(gpi_obj_hdl *gpi_hdl, const char *str); // String of binary char(s) [1, 0, x, z] - - /* Callback related */ - gpi_cb_hdl *register_timed_callback(uint64_t time_ps); - gpi_cb_hdl *register_value_change_callback(gpi_obj_hdl *obj_hdl) { return NULL; } - gpi_cb_hdl *register_readonly_callback(void) { return NULL; } - gpi_cb_hdl *register_nexttime_callback(void) { return NULL; } - gpi_cb_hdl *register_readwrite_callback(void) { return NULL; } - int deregister_callback(gpi_cb_hdl *gpi_hdl) { return 0; } - - gpi_cb_hdl *create_cb_handle(void) { return NULL; } - void destroy_cb_handle(gpi_cb_hdl *gpi_hdl) { } -}; +void VhpiImpl::get_sim_time(uint32_t *high, uint32_t *low) +{ + vhpiTimeT vhpi_time_s; + vhpi_get_time(&vhpi_time_s, NULL); + check_vhpi_error(); + *high = vhpi_time_s.high; + *low = vhpi_time_s.low; +} -// Handle related functions -/** - * @name Find the root handle - * @brief Find the root handle using a optional name - * - * Get a handle to the root simulator object. This is usually the toplevel. - * - * FIXME: In VHPI we always return the first root instance - * - * TODO: Investigate possibility of iterating and checking names as per VHPI - * If no name is defined, we return the first root instance. - * - * If name is provided, we check the name against the available objects until - * we find a match. If no match is found we return NULL - */ -gpi_obj_hdl *vhpi_impl::get_root_handle(const char* name) +GpiObjHdl *VhpiImpl::get_root_handle(const char* name) { FENTER vhpiHandleT root; vhpiHandleT dut; - gpi_obj_hdl *rv; + GpiObjHdl *rv; root = vhpi_handle(vhpiRootInst, NULL); check_vhpi_error(); @@ -469,39 +299,16 @@ gpi_obj_hdl *vhpi_impl::get_root_handle(const char* name) return NULL; } - rv = new vhpi_obj_hdl(root, this); + rv = new VhpiObjHdl(this, root); FEXIT return rv; } -char *vhpi_impl::get_signal_name_str(gpi_obj_hdl *gpi_hdl) -{ - FENTER - vhpi_obj_hdl *vhpi_obj = reinterpret_cast(gpi_hdl); - const char *name = vhpi_get_str(vhpiFullNameP, vhpi_obj->vhpi_hdl); - check_vhpi_error(); - char *result = vhpi_obj->gpi_copy_name(name); - LOG_WARN("Signal name was %s", name); - FEXIT - return result; -} - -char *vhpi_impl::get_signal_type_str(gpi_obj_hdl *gpi_hdl) -{ - FENTER - vhpi_obj_hdl *vhpi_obj = reinterpret_cast(gpi_hdl); - const char *name = vhpi_get_str(vhpiKindStrP, vhpi_obj->vhpi_hdl); - check_vhpi_error(); - char *result = vhpi_obj->gpi_copy_name(name); - LOG_WARN("Signal type was %s", name); - FEXIT - return result; -} -gpi_cb_hdl *vhpi_impl::register_timed_callback(uint64_t time_ps) +GpiCbHdl *VhpiImpl::register_timed_callback(uint64_t time_ps) { - vhpi_cb_timed *hdl = new vhpi_cb_timed(this, time_ps); + VhpiTimedCbHdl *hdl = new VhpiTimedCbHdl(this, time_ps); if (hdl->arm_callback()) { delete(hdl); @@ -511,41 +318,21 @@ gpi_cb_hdl *vhpi_impl::register_timed_callback(uint64_t time_ps) return hdl; } -// Unfortunately it seems that format conversion is not well supported -// We have to set values using vhpiEnum* -void vhpi_impl::set_signal_value_int(gpi_obj_hdl *gpi_hdl, int value) -{ - FENTER - - vhpi_obj_hdl *vhpi_obj = reinterpret_cast(gpi_hdl); - vhpi_obj->write_new_value(value); - - FEXIT -} - -void vhpi_impl::set_signal_value_str(gpi_obj_hdl *gpi_hdl, const char *str) +void VhpiImpl::sim_end(void) { - FENTER - - vhpi_obj_hdl *vhpi_obj = reinterpret_cast(gpi_hdl); - vhpi_obj->write_new_value(str); - - FEXIT + sim_finish_cb = NULL; + vhpi_control(vhpiFinish); + check_vhpi_error(); } extern "C" { -static vhpi_cb_hdl *sim_init_cb; -static vhpi_cb_hdl *sim_finish_cb; -static vhpi_impl *vhpi_table; - - // Main entry point for callbacks from simulator void handle_vhpi_callback(const vhpiCbDataT *cb_data) { FENTER - vhpi_cb_hdl *cb_hdl = (vhpi_cb_hdl*)cb_data->user_data; + VhpiCbHdl *cb_hdl = (VhpiCbHdl*)cb_data->user_data; if (!cb_hdl) LOG_CRITICAL("VPI: Callback data corrupted"); @@ -567,7 +354,7 @@ void handle_vhpi_callback(const vhpiCbDataT *cb_data) static void register_initial_callback(void) { FENTER - sim_init_cb = new vhpi_cb_startup(vhpi_table); + sim_init_cb = new VhpiStartupCbHdl(vhpi_table); sim_init_cb->arm_callback(); FEXIT } @@ -575,14 +362,14 @@ static void register_initial_callback(void) static void register_final_callback(void) { FENTER - sim_finish_cb = new vhpi_cb_shutdown(vhpi_table); + sim_finish_cb = new VhpiShutdownCbHdl(vhpi_table); sim_finish_cb->arm_callback(); FEXIT } static void register_embed(void) { - vhpi_table = new vhpi_impl("VHPI"); + vhpi_table = new VhpiImpl("VHPI"); gpi_register_impl(vhpi_table); gpi_embed_init_python(); } diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h new file mode 100644 index 00000000..0e7a408d --- /dev/null +++ b/lib/vhpi/VhpiImpl.h @@ -0,0 +1,210 @@ +/****************************************************************************** +* Copyright (c) 2013 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +#ifndef COCOTB_VHPI_IMPL_H_ +#define COCOTB_VHPI_IMPL_H_ + +#include "../gpi/gpi_priv.h" +#include + +#define VHPI_CHECKING 1 + +// Should be run after every VHPI call to check error status +static inline int __check_vhpi_error(const char *func, long line) +{ + int level=0; +#if VHPI_CHECKING + vhpiErrorInfoT info; + int loglevel; + level = vhpi_check_error(&info); + if (level == 0) + return 0; + + switch (level) { + case vhpiNote: + loglevel = GPIInfo; + break; + case vhpiWarning: + loglevel = GPIWarning; + break; + case vhpiError: + loglevel = GPIError; + break; + case vhpiFailure: + case vhpiSystem: + case vhpiInternal: + loglevel = GPICritical; + break; + } + + gpi_log("cocotb.gpi", loglevel, __FILE__, func, line, + "VHPI Error level %d: %s\nFILE %s:%d", + info.severity, info.message, info.file, info.line); + +#endif + return level; +} + +#define check_vhpi_error() do { \ + __check_vhpi_error(__func__, __LINE__); \ +} while (0) + +class VhpiImpl : public GpiImplInterface { +public: + VhpiImpl(const std::string& name) : GpiImplInterface(name) { } + + /* Sim related */ + void sim_end(void); + void get_sim_time(uint32_t *high, uint32_t *low); + + /* Hierachy related */ + GpiObjHdl *get_root_handle(const char *name); + + /* Callback related, these may (will) return the same handle*/ + GpiCbHdl *register_timed_callback(uint64_t time_ps); + GpiCbHdl *register_readonly_callback(void) { return NULL; } + GpiCbHdl *register_nexttime_callback(void) { return NULL; } + GpiCbHdl *register_readwrite_callback(void) { return NULL; } + int deregister_callback(GpiCbHdl *obj_hdl) { return 0; } + bool native_check(std::string &name, GpiObjHdl *parent) { return false; } + GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent) { return NULL; } + GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent) { return NULL; } + + const char * reason_to_string(int reason); +}; + +class VhpiObjHdl : public GpiObjHdl { +public: + VhpiObjHdl(GpiImplInterface *impl, vhpiHandleT hdl) : GpiObjHdl(impl), + vhpi_hdl(hdl) { } + virtual ~VhpiObjHdl() { } + + virtual GpiObjHdl *get_handle_by_name(std::string &name) { return NULL; } + virtual GpiObjHdl *get_handle_by_index(uint32_t index) { return NULL; } + virtual GpiIterator *iterate_handle(uint32_t type) { return NULL ;} + virtual GpiObjHdl *next_handle(GpiIterator *iterator) { return NULL; } + + vhpiHandleT get_handle(void); + +protected: + vhpiHandleT vhpi_hdl; +}; + +class VhpiCbHdl : public GpiCbHdl { +public: + VhpiCbHdl(GpiImplInterface *impl); + virtual ~VhpiCbHdl() { } + + virtual int arm_callback(void); + virtual int cleanup_callback(void); + +protected: + vhpiHandleT vhpi_hdl; + vhpiCbDataT cb_data; +}; + +class VhpiSignalObjHdl : public VhpiObjHdl, public GpiSignalObjHdl { +public: + VhpiSignalObjHdl(GpiImplInterface *impl, vhpiHandleT hdl) : VhpiObjHdl(impl, hdl), + GpiSignalObjHdl(impl) { } + virtual ~VhpiSignalObjHdl() { } + + const char* get_signal_value_binstr(void); + + int set_signal_value(const int value); + int set_signal_value(std::string &value); + //virtual GpiCbHdl monitor_value(bool rising_edge) = 0; this was for the triggers + // but the explicit ones are probably better + + // Also think we want the triggers here? + virtual GpiCbHdl *rising_edge_cb(void) { return NULL; } + virtual GpiCbHdl *falling_edge_cb(void) { return NULL; } + virtual GpiCbHdl *value_change_cb(void) { return NULL; } + + /* Functions that I would like to inherit but do not ?*/ + virtual GpiObjHdl *get_handle_by_name(std::string &name) { + return VhpiObjHdl::get_handle_by_name(name); + } + virtual GpiObjHdl *get_handle_by_index(uint32_t index) { + return VhpiObjHdl::get_handle_by_index(index); + } + virtual GpiIterator *iterate_handle(uint32_t type) + { + return VhpiObjHdl::iterate_handle(type); + } + virtual GpiObjHdl *next_handle(GpiIterator *iterator) + { + return VhpiObjHdl::next_handle(iterator); + } + virtual int initialise(std::string name) { + return VhpiObjHdl::initialise(name); + } +}; + +class VhpiTimedCbHdl : public VhpiCbHdl { +public: + VhpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps); + virtual ~VhpiTimedCbHdl() { } +private: + vhpiTimeT vhpi_time; +}; + +#if 0 +class VhpiReadOnlyCbHdl : public VhpiCbHdl { + +}; + +class VhpiReadWriteCbHdl : public VhpiCbHdl { + +}; + +class VhpiNextPhaseCbHdl : public VhpiCbHdl { + +}; +#endif + +class VhpiStartupCbHdl : public VhpiCbHdl { +public: + VhpiStartupCbHdl(GpiImplInterface *impl); + int run_callback(void); + virtual ~VhpiStartupCbHdl() { } +}; + +class VhpiShutdownCbHdl : public VhpiCbHdl { +public: + VhpiShutdownCbHdl(GpiImplInterface *impl); + int run_callback(void); + virtual ~VhpiShutdownCbHdl() { } +}; + +class VhpiReadwriteCbHdl : public VhpiCbHdl { +public: + VhpiReadwriteCbHdl(GpiImplInterface *impl); + virtual ~VhpiReadwriteCbHdl() { } +}; + +#endif /*COCOTB_VHPI_IMPL_H_ */ \ No newline at end of file From 1961be0f22074290f50344bcdf8a4ab5963682d0 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 12 Oct 2014 17:51:41 +0100 Subject: [PATCH 0619/2656] Issue #141: VHDL entrypoint for FLI --- lib/fli/entrypoint.vhdl | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 lib/fli/entrypoint.vhdl diff --git a/lib/fli/entrypoint.vhdl b/lib/fli/entrypoint.vhdl new file mode 100644 index 00000000..4aa774dc --- /dev/null +++ b/lib/fli/entrypoint.vhdl @@ -0,0 +1,14 @@ +-- The FLI doesn't appear to support an entrypoint defined from the +-- command line parameters or using a pre-defined symbol in a library +-- (unlike VPI or VHPI). Therefore an entrypoint has to come from +-- the VHDL side. Because we require access to the FLI at elaboration +-- to register the start of sim callback, we need to use a foreign +-- architecture rather than a foreign procedure. + +entity cocotb_entrypoint is +end cocotb_entrypoint; + +architecture cocotb_arch of cocotb_entrypoint is + attribute foreign of cocotb_arch : architecture is "cocotb_fli_init fli.so"; +begin +end cocotb_arch; From e276c34c2e65ef9545430b14f1d86573a1853aea Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 15 Oct 2014 11:21:03 +0100 Subject: [PATCH 0620/2656] Issue#161: Mixed language test now passes - big change is that lower level implementations now use the fully qualified name to find an object --- lib/gpi/GpiCbHdl.cpp | 18 ++- lib/gpi/GpiCommon.cpp | 18 ++- lib/gpi/gpi_priv.h | 10 +- lib/vhpi/VhpiCbHdl.cpp | 166 +++++++++++++++++++++-- lib/vhpi/VhpiImpl.cpp | 201 ++++++++-------------------- lib/vhpi/VhpiImpl.h | 22 ++- lib/vpi/VpiImpl.cpp | 22 ++- lib/vpi/VpiImpl.h | 2 +- makefiles/simulators/Makefile.aldec | 3 + 9 files changed, 278 insertions(+), 184 deletions(-) diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index 11c72f15..9c614b52 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -38,6 +38,11 @@ const char * GpiObjHdl::get_type_str(void) return m_type.c_str(); } +const std::string & GpiObjHdl::get_name(void) +{ + return m_name; +} + /* Genertic base clss implementations */ char *GpiHdl::gpi_copy_name(const char *name) { @@ -65,13 +70,18 @@ char *GpiHdl::gpi_copy_name(const char *name) return result; } -int GpiHdl::initialise(std::string name) +bool GpiHdl::is_this_impl(GpiImplInterface *impl) +{ + return impl == this->m_impl; +} + +int GpiHdl::initialise(std::string &name) { LOG_WARN("Generic initialise, doubt you should have called this"); return 0; } -int GpiObjHdl::initialise(std::string name) +int GpiObjHdl::initialise(std::string &name) { m_name = name; return 0; @@ -80,7 +90,9 @@ int GpiObjHdl::initialise(std::string name) int GpiCbHdl::run_callback(void) { LOG_WARN("Generic run_callback"); - return this->gpi_function(m_cb_data); + this->gpi_function(m_cb_data); + LOG_WARN("Generic run_callback done"); + return 0; } int GpiCbHdl::cleanup_callback(void) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index a8b821e2..5e863ebb 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -52,6 +52,7 @@ void gpi_embed_end(void) void gpi_sim_end(void) { + LOG_WARN("Sim end called"); registered_impls[0]->sim_end(); } @@ -94,20 +95,29 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) GpiObjHdl *hdl; GpiObjHdl *base = sim_to_hdl(parent); std::string s_name = name; + std::string fq_name = base->get_name() + "." + s_name; - LOG_WARN("Searchingn for %s", name); + LOG_WARN("Searching for %s", name); for (iter = registered_impls.begin(); iter != registered_impls.end(); iter++) { LOG_WARN("Checking if %s native though impl %s ", name, (*iter)->get_name_c()); - if ((hdl = (*iter)->native_check_create(s_name, base))) { + + /* If the current interface is not the same as the one that we + are going to query then append the name we are looking for to + the parent, such as .name. This is so that it entity can + be seen discovered even if the parents implementation is not the same + as the one that we are querying through */ + + //std::string &to_query = base->is_this_impl(*iter) ? s_name : fq_name; + if ((hdl = (*iter)->native_check_create(fq_name, base))) { LOG_WARN("Found %s via %s", name, (*iter)->get_name_c()); - //hdl = base->get_handle_by_name(s_name); + return (gpi_sim_hdl)hdl; } } - return (gpi_sim_hdl)hdl; + return NULL; } gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index e587d1e2..a5405ba9 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -64,14 +64,15 @@ class GpiHdl { //GpiHdl() : m_impl(NULL) { } GpiHdl(GpiImplInterface *impl) : m_impl(impl) { } virtual ~GpiHdl() { } - virtual int initialise(std::string name); // Post constructor init + virtual int initialise(std::string &name); // Post constructor init private: GpiHdl() { } // Disable default constructor public: - GpiImplInterface *m_impl; // VPI/VHPI/FLI routines - char *gpi_copy_name(const char *name); // Might not be needed + GpiImplInterface *m_impl; // VPI/VHPI/FLI routines + char *gpi_copy_name(const char *name); // Might not be needed + bool is_this_impl(GpiImplInterface *impl); // Is the passed interface the one this object uses }; /* GPI object handle, maps to a simulation object */ @@ -96,9 +97,10 @@ class GpiObjHdl : public GpiHdl { virtual const char* get_name_str(void); virtual const char* get_type_str(void); + const std::string & get_name(void); bool is_native_impl(GpiImplInterface *impl); - virtual int initialise(std::string name); + virtual int initialise(std::string &name); protected: std::string m_name; diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 293ef338..041f36a5 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -35,6 +35,69 @@ vhpiHandleT VhpiObjHdl::get_handle(void) return vhpi_hdl; } +VhpiSignalObjHdl::~VhpiSignalObjHdl() +{ + if (m_value.format == vhpiEnumVecVal || + m_value.format == vhpiLogicVecVal) { + free(m_value.value.enumvs); + } + + free(m_binvalue.value.str); +} + +int VhpiSignalObjHdl::initialise(std::string &name) { + // Determine the type of object, either scalar or vector + m_value.format = vhpiObjTypeVal; + m_value.bufSize = 0; + m_value.value.str = NULL; + + vhpi_get_value(vhpi_hdl, &m_value); + check_vhpi_error(); + + switch (m_value.format) { + case vhpiEnumVal: + case vhpiLogicVal: { + m_value.value.enumv = vhpi0; + break; + } + + case vhpiEnumVecVal: + case vhpiLogicVecVal: { + m_size = vhpi_get(vhpiSizeP, vhpi_hdl); + m_value.bufSize = m_size*sizeof(vhpiEnumT); + m_value.value.enumvs = (vhpiEnumT *)malloc(m_value.bufSize); + if (!m_value.value.enumvs) { + LOG_CRITICAL("Unable to alloc mem for write buffer"); + } + + memset(&m_value.value.enumvs, m_size, vhpi0); + + break; + } + + default: { + LOG_CRITICAL("Unable to determine property for %s (%d) format object", + ((VhpiImpl*)VhpiObjHdl::m_impl)->format_to_string(m_value.format), m_value.format); + } + } + + /* We also alloc a second value member for use with read string operations */ + m_binvalue.format = vhpiBinStrVal; + m_binvalue.bufSize = 0;//m_size*sizeof(vhpiCharT); + m_binvalue.value.str = NULL;//(vhpiCharT *)calloc(m_binvalue.bufSize, m_binvalue.bufSize); + + int new_size = vhpi_get_value(vhpi_hdl, &m_binvalue); + + m_binvalue.bufSize = new_size*sizeof(vhpiCharT); + m_binvalue.value.str = (vhpiCharT *)calloc(m_binvalue.bufSize, m_binvalue.bufSize); + + if (!m_value.value.str) { + LOG_CRITICAL("Unable to alloc mem for read buffer"); + } + + return 0; +} + VhpiCbHdl::VhpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), vhpi_hdl(NULL) { @@ -77,31 +140,110 @@ int VhpiCbHdl::arm_callback(void) return ret; } -const char* VhpiSignalObjHdl::get_signal_value_binstr(void) +// Value related functions +const vhpiEnumT VhpiSignalObjHdl::chr2vhpi(const char value) { - s_vpi_value value_s = {vpiBinStrVal}; - - vpi_get_value(vhpi_hdl, &value_s); - check_vhpi_error(); - - LOG_WARN("Value back was %s", value_s.value.str); - - return value_s.value.str; + switch (value) { + case '0': + return vhpi0; + case '1': + return vhpi1; + case 'U': + case 'u': + return vhpiU; + case 'Z': + case 'z': + return vhpiZ; + case 'X': + case 'x': + return vhpiX; + default: + return vhpiDontCare; + } } // Value related functions int VhpiSignalObjHdl::set_signal_value(int value) { - + switch (m_value.format) { + case vhpiEnumVal: + case vhpiLogicVal: { + m_value.value.enumv = value ? vhpi1 : vhpi0; + break; + } + + case vhpiEnumVecVal: + case vhpiLogicVecVal: { + unsigned int i; + for (i=0; i m_size) { + LOG_ERROR("VHPI: Attempt to write string longer than signal %d > %d", + len, m_size); + return -1; + } + + std::string::iterator iter; + + unsigned int i = 0; + for (iter = value.begin(); + iter != value.end(); + iter++, i++) { + m_value.value.enumvs[i] = chr2vhpi(*iter); + } + + // Fill bits at the end of the value to 0's + for (i = len; i < m_size; i++) + m_value.value.enumvs[i] = vhpi0; + + break; + } + + default: { + LOG_CRITICAL("VHPI type of object has changed at runtime, big fail"); + } + } + + vhpi_put_value(vhpi_hdl, &m_value, vhpiForcePropagate); + check_vhpi_error(); return 0; } +const char* VhpiSignalObjHdl::get_signal_value_binstr(void) +{ + vhpi_get_value(vhpi_hdl, &m_binvalue); + check_vhpi_error(); + + return m_binvalue.value.str; +} + VhpiStartupCbHdl::VhpiStartupCbHdl(GpiImplInterface *impl) : VhpiCbHdl(impl) { cb_data.reason = vhpiCbStartOfSimulation; @@ -127,7 +269,7 @@ VhpiShutdownCbHdl::VhpiShutdownCbHdl(GpiImplInterface *impl) : VhpiCbHdl(impl) } int VhpiShutdownCbHdl::run_callback(void) { - LOG_WARN("Shutdown called"); + //LOG_WARN("Shutdown called"); gpi_embed_end(); return 0; } diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 3f773167..949e20b4 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -43,9 +43,9 @@ static VhpiImpl *vhpi_table; } -const char * vhpi_format_to_string(int reason) +const char * VhpiImpl::format_to_string(int format) { - switch (reason) { + switch (format) { case vhpiBinStrVal: return "vhpiBinStrVal"; case vhpiOctStrVal: @@ -82,149 +82,6 @@ const char * vhpi_format_to_string(int reason) } } -// Value related functions -const vhpiEnumT chr2vhpi(const char value) -{ - switch (value) { - case '0': - return vhpi0; - case '1': - return vhpi1; - case 'U': - case 'u': - return vhpiU; - case 'Z': - case 'z': - return vhpiZ; - case 'X': - case 'x': - return vhpiX; - default: - return vhpiDontCare; - } -} - -#if 0 -class VhpiObjHdl : public GpiObjHdl { -private: - int m_size; - vhpiValueT m_value; -public: - VhpiObjHdl(vhpiHandleT hdl, gpi_impl_interface *impl) : GpiObjHdl(impl), - m_size(0), - vhpi_hdl(hdl) { } - virtual ~VhpiObjHdl() { - if (m_value.format == vhpiEnumVecVal || - m_value.format == vhpiLogicVecVal) { - free(m_value.value.enumvs); - } - } -public: - vhpiHandleT vhpi_hdl; - - int initialise(void) { - // Determine the type of object, either scalar or vector - m_value.format = vhpiObjTypeVal; - m_value.bufSize = 0; - m_value.value.str = NULL; - - vhpi_get_value(vhpi_hdl, &m_value); - check_vhpi_error(); - - switch (m_value.format) { - case vhpiEnumVal: - case vhpiLogicVal: { - m_value.value.enumv = vhpi0; - break; - } - - case vhpiEnumVecVal: - case vhpiLogicVecVal: { - m_size = vhpi_get(vhpiSizeP, vhpi_hdl); - m_value.bufSize = m_size*sizeof(vhpiEnumT); - m_value.value.enumvs = (vhpiEnumT *)malloc(m_size*sizeof(vhpiEnumT)); - - memset(&m_value.value.enumvs, m_size, vhpi0); - //for (i=0; i m_size) { - LOG_ERROR("VHPI: Attempt to write string longer than signal %d > %d", - len, m_size); - return -1; - } - int i; - for (i=0, ptr=str; i(parent); + vhpiHandleT vpi_hdl = parent_hdl->get_handle(); + vhpiHandleT new_hdl; + VhpiObjHdl *new_obj = NULL; + unsigned int name_start = 0; + std::vector writable(name.begin(), name.end()); + writable.push_back('\0'); + + new_hdl = vhpi_handle_by_name(&writable[name_start], NULL); + + if (!new_hdl) + return NULL; + + if (vhpiVerilog == (type = vhpi_get(vhpiKindP, new_hdl))) { + vpi_free_object(vpi_hdl); + LOG_DEBUG("Not a VHPI object"); + return NULL; + } + + /* What sort of isntance is this ?*/ + switch (type) { + case vhpiPortDeclK: + case vhpiSigDeclK: + new_obj = new VhpiSignalObjHdl(this, new_hdl); + LOG_DEBUG("Created VhpiSignalObjHdl"); + break; + case vhpiCompInstStmtK: + new_obj = new VhpiObjHdl(this, new_hdl); + LOG_DEBUG("Created VhpiObjHdl"); + break; + default: + LOG_CRITICAL("Not sure what to do with type %d for entity (%s)", type, name.c_str()); + return NULL; + } + + LOG_DEBUG("Type was %d", type); + /* Might move the object creation inside here */ + new_obj->initialise(name); + + return new_obj; +} + +GpiObjHdl *VhpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) +{ + return NULL; +} + GpiObjHdl *VhpiImpl::get_root_handle(const char* name) { FENTER vhpiHandleT root; vhpiHandleT dut; GpiObjHdl *rv; + std::string root_name = name; root = vhpi_handle(vhpiRootInst, NULL); check_vhpi_error(); @@ -300,6 +208,7 @@ GpiObjHdl *VhpiImpl::get_root_handle(const char* name) } rv = new VhpiObjHdl(this, root); + rv->initialise(root_name); FEXIT return rv; @@ -337,8 +246,6 @@ void handle_vhpi_callback(const vhpiCbDataT *cb_data) if (!cb_hdl) LOG_CRITICAL("VPI: Callback data corrupted"); - LOG_DEBUG("Running %p", cb_hdl); - if (cb_hdl->get_call_state() == GPI_PRIMED) { cb_hdl->set_call_state(GPI_PRE_CALL); cb_hdl->run_callback(); diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 0e7a408d..79df0296 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -91,10 +91,11 @@ class VhpiImpl : public GpiImplInterface { GpiCbHdl *register_readwrite_callback(void) { return NULL; } int deregister_callback(GpiCbHdl *obj_hdl) { return 0; } bool native_check(std::string &name, GpiObjHdl *parent) { return false; } - GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent) { return NULL; } - GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent) { return NULL; } + GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); + GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); const char * reason_to_string(int reason); + const char * format_to_string(int format); }; class VhpiObjHdl : public GpiObjHdl { @@ -107,9 +108,11 @@ class VhpiObjHdl : public GpiObjHdl { virtual GpiObjHdl *get_handle_by_index(uint32_t index) { return NULL; } virtual GpiIterator *iterate_handle(uint32_t type) { return NULL ;} virtual GpiObjHdl *next_handle(GpiIterator *iterator) { return NULL; } + //int initialise(std::string &name); vhpiHandleT get_handle(void); + protected: vhpiHandleT vhpi_hdl; }; @@ -130,8 +133,9 @@ class VhpiCbHdl : public GpiCbHdl { class VhpiSignalObjHdl : public VhpiObjHdl, public GpiSignalObjHdl { public: VhpiSignalObjHdl(GpiImplInterface *impl, vhpiHandleT hdl) : VhpiObjHdl(impl, hdl), - GpiSignalObjHdl(impl) { } - virtual ~VhpiSignalObjHdl() { } + GpiSignalObjHdl(impl), + m_size(0) { } + virtual ~VhpiSignalObjHdl(); const char* get_signal_value_binstr(void); @@ -160,9 +164,13 @@ class VhpiSignalObjHdl : public VhpiObjHdl, public GpiSignalObjHdl { { return VhpiObjHdl::next_handle(iterator); } - virtual int initialise(std::string name) { - return VhpiObjHdl::initialise(name); - } + virtual int initialise(std::string &name); + +private: + const vhpiEnumT chr2vhpi(const char value); + unsigned int m_size; + vhpiValueT m_value; + vhpiValueT m_binvalue; }; class VhpiTimedCbHdl : public VhpiCbHdl { diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index d381f20c..f727d869 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -102,14 +102,17 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) VpiObjHdl *new_obj = NULL; std::vector writable(name.begin(), name.end()); writable.push_back('\0'); - - new_hdl = vpi_handle_by_name(&writable[0], vpi_hdl); + + LOG_WARN("Name passed in is %s", name.c_str()); + + new_hdl = vpi_handle_by_name(&writable[0], NULL); if (!new_hdl) return NULL; if (vpiUnknown == (type = vpi_get(vpiType, new_hdl))) { vpi_free_object(vpi_hdl); + LOG_WARN("Not a VPI object") return new_obj; } @@ -126,7 +129,7 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) break; default: LOG_CRITICAL("Not sure what to do with type %d for entity (%s)", type, name.c_str()); - return false; + return NULL; } LOG_WARN("Type was %d", type); @@ -168,7 +171,7 @@ GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) default: LOG_CRITICAL("Not sure what to do with type %d below entity (%s) at index (%d)", type, parent->get_name_str(), index); - return false; + return NULL; } LOG_WARN("Type was %d", type); @@ -184,6 +187,7 @@ GpiObjHdl *VpiImpl::get_root_handle(const char* name) vpiHandle root; vpiHandle iterator; VpiObjHdl *rv; + std::string root_name = name; // vpi_iterate with a ref of NULL returns the top level module iterator = vpi_iterate(vpiModule, NULL); @@ -207,7 +211,7 @@ GpiObjHdl *VpiImpl::get_root_handle(const char* name) } rv = new VpiObjHdl(this, root); - rv->initialise(name); + rv->initialise(root_name); FEXIT return rv; @@ -323,7 +327,9 @@ void VpiImpl::sim_end(void) */ sim_finish_cb->set_call_state(GPI_DELETE); vpi_control(vpiFinish); + LOG_WARN("Returned from vpi_control"); check_vpi_error(); + LOG_WARN("Returned from check_error"); } extern "C" { @@ -340,7 +346,7 @@ int32_t handle_vpi_callback(p_cb_data cb_data) if (!cb_hdl) LOG_CRITICAL("VPI: Callback data corrupted"); - LOG_DEBUG("Running %p", cb_hdl); + LOG_WARN("Running %p", cb_hdl); if (cb_hdl->get_call_state() == GPI_PRIMED) { cb_hdl->set_call_state(GPI_PRE_CALL); @@ -348,8 +354,12 @@ int32_t handle_vpi_callback(p_cb_data cb_data) cb_hdl->set_call_state(GPI_POST_CALL); } + LOG_WARN("Running %p done", cb_hdl); + gpi_deregister_callback(cb_hdl); + LOG_WARN("Deregister %p done", cb_hdl); + FEXIT return rv; }; diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 6c6a1e9e..b380fe1f 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -160,7 +160,7 @@ class VpiSignalObjHdl : public VpiObjHdl, public GpiSignalObjHdl { { return VpiObjHdl::next_handle(iterator); } - virtual int initialise(std::string name) { + virtual int initialise(std::string &name) { return VpiObjHdl::initialise(name); } }; diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 8ac245be..c6fc9832 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -52,6 +52,9 @@ endif ifeq ($(GPI_IMPL),vhpi) GPI_ARGS = -loadvhpi $(LIB_DIR)/libvhpi:vhpi_startup_routines_bootstrap endif +ifeq ($(GPI_IMPL),mixed) + GPI_ARGS = -pli libvpi -loadvhpi $(LIB_DIR)/libvhpi:vhpi_startup_routines_bootstrap +endif ifndef GPI_ARGS $(error "Unable to determine appropriate GPI layer to use") From e2c3bd04a3208cd6f90329a26596f64b03c26d2c Mon Sep 17 00:00:00 2001 From: Chiggs Date: Wed, 15 Oct 2014 20:45:16 +0100 Subject: [PATCH 0621/2656] Issue #161: Add VHDL sample module to allow mixed language tests --- examples/functionality/hdl/sample_module.vhdl | 62 +++++++++++++++++++ examples/functionality/tests/Makefile | 13 +++- 2 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 examples/functionality/hdl/sample_module.vhdl diff --git a/examples/functionality/hdl/sample_module.vhdl b/examples/functionality/hdl/sample_module.vhdl new file mode 100644 index 00000000..4efd9b90 --- /dev/null +++ b/examples/functionality/hdl/sample_module.vhdl @@ -0,0 +1,62 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2014 Potential Ventures Ltd +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- * Redistributions of source code must retain the above copyright +-- notice, this list of conditions and the following disclaimer. +-- * Redistributions in binary form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- * Neither the name of Potential Ventures Ltd, +-- Copyright (c) 2013 SolarFlare Communications Inc nor the +-- names of its contributors may be used to endorse or promote products +-- derived from this software without specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +-- DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +-- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +-- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +------------------------------------------------------------------------------- + +library ieee; + +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity sample_module is + port ( + clk : in std_ulogic; + + stream_in_data : in std_ulogic_vector(7 downto 0); + stream_in_data_wide : in std_ulogic_vector(63 downto 0); + stream_in_valid : in std_ulogic; + stream_in_ready : out std_ulogic; + + stream_out_data_comb : out std_ulogic_vector(7 downto 0); + stream_out_data_registered : out std_ulogic_vector(7 downto 0); + stream_out_ready : in std_ulogic + ); +end; + +architecture impl of sample_module is + +begin + +process (clk) begin + if rising_edge(clk) then + stream_out_data_registered <= stream_in_data; + end if; +end process; + +stream_out_data_comb <= stream_in_data; +stream_in_ready <= stream_out_ready; + +end architecture; \ No newline at end of file diff --git a/examples/functionality/tests/Makefile b/examples/functionality/tests/Makefile index b474492e..cd58946c 100644 --- a/examples/functionality/tests/Makefile +++ b/examples/functionality/tests/Makefile @@ -28,13 +28,20 @@ ############################################################################### -TOPLEVEL = sample_module +TOPLEVEL := sample_module +LANGUAGE ?= verilog PWD=$(shell pwd) COCOTB=$(PWD)/../../.. -VERILOG_SOURCES = $(PWD)/../hdl/sample_module.v -MODULE=test_cocotb,test_discovery,test_external,test_regression +ifeq ($(LANGUAGE),vhdl) + VHDL_SOURCES = $(PWD)/../hdl/sample_module.vhdl + TOPLEVEL := $(TOPLEVEL) +else + VERILOG_SOURCES = $(PWD)/../hdl/sample_module.v +endif + +MODULE ?= test_cocotb,test_discovery,test_external,test_regression include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim From e76ee4b4983757554df9a53c446e0aeb169e86ec Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 16 Oct 2014 08:33:42 +0100 Subject: [PATCH 0622/2656] Issue#161: Add some debug for registering of implementation layers --- lib/gpi/GpiCommon.cpp | 2 ++ lib/vhpi/VhpiImpl.cpp | 2 ++ lib/vpi/VpiImpl.cpp | 2 ++ 3 files changed, 6 insertions(+) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 5e863ebb..e1e5d7db 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -74,6 +74,8 @@ gpi_sim_hdl gpi_get_root_handle(const char *name) GpiObjHdl *hdl; + LOG_WARN("Looking for root handle over %d impls", registered_impls.size()); + for (iter = registered_impls.begin(); iter != registered_impls.end(); iter++) { diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 949e20b4..614df0fd 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -261,6 +261,7 @@ void handle_vhpi_callback(const vhpiCbDataT *cb_data) static void register_initial_callback(void) { FENTER + LOG_WARN("Initial callback registering"); sim_init_cb = new VhpiStartupCbHdl(vhpi_table); sim_init_cb->arm_callback(); FEXIT @@ -276,6 +277,7 @@ static void register_final_callback(void) static void register_embed(void) { + printf("%s %d Registered VHPI \n", __func__, __LINE__); vhpi_table = new VhpiImpl("VHPI"); gpi_register_impl(vhpi_table); gpi_embed_init_python(); diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index f727d869..58ed782c 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -367,6 +367,7 @@ int32_t handle_vpi_callback(p_cb_data cb_data) static void register_embed(void) { + printf("%s %d Registered VPI \n", __func__, __LINE__); vpi_table = new VpiImpl("VPI"); gpi_register_impl(vpi_table); gpi_embed_init_python(); @@ -375,6 +376,7 @@ static void register_embed(void) static void register_initial_callback(void) { + LOG_WARN("Initial callback registering"); sim_init_cb = new VpiStartupCbHdl(vpi_table); /* We ignore the return value here as VCS does some silly From 2f7a9bc7f2d125d24cfdea6be684e38642100b81 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 16 Oct 2014 23:00:37 +0100 Subject: [PATCH 0623/2656] Issue#161: Re-enable NextPhase and ReadOnly callbacks for VPI --- lib/vpi/VpiCbHdl.cpp | 20 ++++++++++++++++++++ lib/vpi/VpiImpl.cpp | 10 ++-------- lib/vpi/VpiImpl.h | 19 +++++++++++-------- 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index b36e66c0..1943eb5b 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -194,6 +194,26 @@ VpiReadwriteCbHdl::VpiReadwriteCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) cb_data.time = &vpi_time; } +VpiReadOnlyCbHdl::VpiReadOnlyCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) +{ + vpi_time.type = vpiSimTime; + vpi_time.high = 0; + vpi_time.low = 0; + + cb_data.reason = cbReadOnlySynch; + cb_data.time = &vpi_time; +} + +VpiNextPhaseCbHdl::VpiNextPhaseCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) +{ + vpi_time.type = vpiSimTime; + vpi_time.high = 0; + vpi_time.low = 0; + + cb_data.reason = cbNextSimTime; + cb_data.time = &vpi_time; +} + #if 0 class vpi_onetime_cb : public vpi_cb_hdl { public: diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 58ed782c..1e664fad 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -267,10 +267,9 @@ GpiCbHdl *VpiImpl::register_readwrite_callback(void) GpiCbHdl *VpiImpl::register_readonly_callback(void) { - #if 0 FENTER - vpi_cb_readonly *hdl = new VpiReadOnlyCbHdl(this); + VpiReadOnlyCbHdl *hdl = new VpiReadOnlyCbHdl(this); if (hdl->arm_callback()) { delete(hdl); @@ -279,16 +278,13 @@ GpiCbHdl *VpiImpl::register_readonly_callback(void) FEXIT return hdl; - #endif - return NULL; } GpiCbHdl *VpiImpl::register_nexttime_callback(void) { - #if 0 FENTER - vpi_cb_nexttime *hdl = new VpiNextPhaseCbHdl(this); + VpiNextPhaseCbHdl *hdl = new VpiNextPhaseCbHdl(this); if (hdl->arm_callback()) { delete(hdl); @@ -297,8 +293,6 @@ GpiCbHdl *VpiImpl::register_nexttime_callback(void) FEXIT return hdl; - #endif - return NULL; } int VpiImpl::deregister_callback(GpiCbHdl *gpi_hdl) diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index b380fe1f..ab0d266f 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -173,19 +173,22 @@ class VpiTimedCbHdl : public VpiCbHdl { s_vpi_time vpi_time; }; -#if 0 -class VpiReadOnlyCbHdl : public VpiCbHdl { - -}; - -class VpiReadWriteCbHdl : public VpiCbHdl { +class VpiReadOnlyCbHdl : public VpiCbHdl { +public: + VpiReadOnlyCbHdl(GpiImplInterface *impl); + virtual ~VpiReadOnlyCbHdl() { } +private: + s_vpi_time vpi_time; }; class VpiNextPhaseCbHdl : public VpiCbHdl { - +public: + VpiNextPhaseCbHdl(GpiImplInterface *impl); + virtual ~VpiNextPhaseCbHdl() { } +private: + s_vpi_time vpi_time; }; -#endif class VpiStartupCbHdl : public VpiCbHdl { public: From ef963f33c976514701abfca060dfc4c475b3c4a9 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 17 Oct 2014 00:12:46 +0100 Subject: [PATCH 0624/2656] Issue#161: Turned on remaining VPI functionality, endian_swapper and functionality tests for VPI pass --- lib/gpi/GpiCbHdl.cpp | 6 +- lib/gpi/GpiCommon.cpp | 14 ++- lib/vpi/VpiCbHdl.cpp | 219 ++++++++++++------------------------------ lib/vpi/VpiImpl.cpp | 34 +++---- lib/vpi/VpiImpl.h | 84 +++++++++------- 5 files changed, 127 insertions(+), 230 deletions(-) diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index 9c614b52..79349f31 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -89,9 +89,9 @@ int GpiObjHdl::initialise(std::string &name) int GpiCbHdl::run_callback(void) { - LOG_WARN("Generic run_callback"); + LOG_DEBUG("Generic run_callback"); this->gpi_function(m_cb_data); - LOG_WARN("Generic run_callback done"); + LOG_DEBUG("Generic run_callback done"); return 0; } @@ -134,5 +134,5 @@ gpi_cb_state_e GpiCbHdl::get_call_state(void) GpiCbHdl::~GpiCbHdl(void) { - LOG_WARN("In GpiCbHdl Destructor"); + } \ No newline at end of file diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index e1e5d7db..88211954 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -99,12 +99,12 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) std::string s_name = name; std::string fq_name = base->get_name() + "." + s_name; - LOG_WARN("Searching for %s", name); + LOG_DEBUG("Searching for %s", name); for (iter = registered_impls.begin(); iter != registered_impls.end(); iter++) { - LOG_WARN("Checking if %s native though impl %s ", name, (*iter)->get_name_c()); + LOG_DEBUG("Checking if %s native though impl %s ", name, (*iter)->get_name_c()); /* If the current interface is not the same as the one that we are going to query then append the name we are looking for to @@ -114,7 +114,7 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) //std::string &to_query = base->is_this_impl(*iter) ? s_name : fq_name; if ((hdl = (*iter)->native_check_create(fq_name, base))) { - LOG_WARN("Found %s via %s", name, (*iter)->get_name_c()); + LOG_DEBUG("Found %s via %s", name, (*iter)->get_name_c()); return (gpi_sim_hdl)hdl; } } @@ -200,11 +200,11 @@ gpi_sim_hdl gpi_register_value_change_callback(const int (*gpi_function)(const v gpi_sim_hdl sig_hdl, int rising) { - #if 0 - GpiObjHdl *obj_hdl = sim_to_hdl(sig_hdl); + + GpiSignalObjHdl *signal_hdl = sim_to_hdl(sig_hdl); /* Do something based on int & GPI_RISING | GPI_FALLING */ - GpiCbHdl *gpi_hdl = obj_hdl->value_change_cb(); + GpiCbHdl *gpi_hdl = signal_hdl->value_change_cb(); if (!gpi_hdl) { LOG_ERROR("Failed to register a value change callback"); return NULL; @@ -212,8 +212,6 @@ gpi_sim_hdl gpi_register_value_change_callback(const int (*gpi_function)(const v gpi_hdl->set_user_data(gpi_function, gpi_cb_data); return (gpi_sim_hdl)gpi_hdl; - #endif - return NULL; } /* It should not matter which implementation we use for this so just pick the first diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 1943eb5b..d94bfc44 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -70,7 +70,7 @@ int VpiCbHdl::arm_callback(void) { int ret = 0; if (!new_hdl) { - LOG_CRITICAL("VPI: Unable to register callback a handle for VPI type %s(%d)", + LOG_CRITICAL("VPI: Unable to register a callback handle for VPI type %s(%d)", m_impl->reason_to_string(cb_data.reason), cb_data.reason); check_vpi_error(); ret = -1; @@ -84,7 +84,26 @@ int VpiCbHdl::arm_callback(void) { int VpiCbHdl::cleanup_callback(void) { - return 0; + int rc; + if (!vpi_hdl) { + LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); + exit(1); + } + + // If the callback has not been called we also need to call + // remove as well + if (m_state == GPI_PRIMED) { + + rc = vpi_remove_cb(vpi_hdl); + if (!rc) { + check_vpi_error(); + return rc; + } + } + + vpi_hdl = NULL; + m_state = GPI_FREE; + return rc; } const char* VpiSignalObjHdl::get_signal_value_binstr(void) @@ -96,8 +115,6 @@ const char* VpiSignalObjHdl::get_signal_value_binstr(void) vpi_get_value(vpi_hdl, value_p); check_vpi_error(); - LOG_WARN("Value back was %s", value_p->value.str); - return value_p->value.str; } @@ -142,6 +159,43 @@ int VpiSignalObjHdl::set_signal_value(std::string &value) return 0; } +GpiCbHdl * VpiSignalObjHdl::value_change_cb(void) +{ + value_cb = new VpiValueCbHdl(VpiObjHdl::m_impl, this); + + if (value_cb->arm_callback()) + return NULL; + + return value_cb; +} + +VpiValueCbHdl::VpiValueCbHdl(GpiImplInterface *impl, VpiSignalObjHdl *sig) : VpiCbHdl(impl) +{ + vpi_time.type = vpiSuppressTime; + + cb_data.reason = cbValueChange; + cb_data.time = &vpi_time; + cb_data.obj = sig->get_handle(); +} + +int VpiValueCbHdl::cleanup_callback(void) +{ + //LOG_WARN("Cleanup %p", this); + int rc; + if (!vpi_hdl) { + LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); + exit(1); + } + + rc = vpi_remove_cb(vpi_hdl); + check_vpi_error(); + + vpi_hdl = NULL; + m_state = GPI_FREE; + + return rc; +} + VpiStartupCbHdl::VpiStartupCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) { cb_data.reason = cbStartOfSimulation; @@ -212,159 +266,4 @@ VpiNextPhaseCbHdl::VpiNextPhaseCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) cb_data.reason = cbNextSimTime; cb_data.time = &vpi_time; -} - -#if 0 -class vpi_onetime_cb : public vpi_cb_hdl { -public: - vpi_onetime_cb(gpi_m_impl_interface *m_impl) : vpi_cb_hdl(m_impl) { } - int cleanup_callback(void) { - FENTER - //LOG_WARN("Cleanup %p state is %d", this, state); - int rc; - if (!vpi_hdl) { - LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); - exit(1); - } - - // If the callback has not been called we also need to call - // remove as well - if (m_state == GPI_PRIMED) { - - rc = vpi_remove_cb(vpi_hdl); - if (!rc) { - check_vpi_error(); - return rc; - } - } - - vpi_hdl = NULL; - m_state = GPI_FREE; - return rc; - } - virtual ~vpi_onetime_cb() { } -}; - -class vpi_recurring_cb : public vpi_cb_hdl { -public: - vpi_recurring_cb(gpi_m_impl_interface *m_impl) : vpi_cb_hdl(m_impl) { } - int cleanup_callback(void) { - FENTER - //LOG_WARN("Cleanup %p", this); - int rc; - if (!vpi_hdl) { - LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); - exit(1); - } - - rc = vpi_remove_cb(vpi_hdl); - check_vpi_error(); - - vpi_hdl = NULL; - m_state = GPI_FREE; - - FEXIT - return rc; - } - virtual ~vpi_recurring_cb() { } -}; - -class vpi_cb_value_change : public vpi_recurring_cb { -private: - s_vpi_value cb_value; -public: - vpi_cb_value_change(gpi_m_impl_interface *m_impl) : vpi_recurring_cb(m_impl) { - cb_value.format = vpiIntVal; - } - int arm_callback(vpi_obj_hdl *vpi_hdl) { - s_cb_data cb_data_s; - s_vpi_time vpi_time_s = {.type = vpiSuppressTime }; - - cb_data_s.reason = cbValueChange; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = vpi_hdl->vpi_hdl; - cb_data_s.time = &vpi_time_s; - cb_data_s.value = &cb_value; - cb_data_s.user_data = (char *)this; - - return register_cb(&cb_data_s); - } - virtual ~vpi_cb_value_change() { } -}; - -class vpi_cb_readonly : public vpi_onetime_cb { -public: - vpi_cb_readonly(gpi_m_impl_interface *m_impl) : vpi_onetime_cb(m_impl) { } - int arm_callback(void) { - s_cb_data cb_data_s; - s_vpi_time vpi_time_s; - - vpi_time_s.type = vpiSimTime, - vpi_time_s.low = 0, - vpi_time_s.high = 0, - - cb_data_s.reason = cbReadOnlySynch; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = &vpi_time_s; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)this; - - return register_cb(&cb_data_s); - } - - virtual ~vpi_cb_readonly() { } -}; - - -class vpi_cb_timed : public vpi_onetime_cb { -public: - vpi_cb_timed(gpi_m_impl_interface *m_impl) : vpi_onetime_cb(m_impl) { } - - int arm_callback(uint64_t time_ps) { - s_cb_data cb_data_s; - s_vpi_time vpi_time_s; - - vpi_time_s.type = vpiSimTime; - vpi_time_s.high = (uint32_t)(time_ps>>32); - vpi_time_s.low = (uint32_t)(time_ps); - - cb_data_s.reason = cbAfterDelay; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = &vpi_time_s; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)this; - - return register_cb(&cb_data_s); - } - - virtual ~vpi_cb_timed() { } -}; - -class vpi_cb_nexttime : public vpi_onetime_cb { -public: - vpi_cb_nexttime(gpi_m_impl_interface *m_impl) : vpi_onetime_cb(m_impl) { } - - int arm_callback(void) { - s_cb_data cb_data_s; - s_vpi_time vpi_time_s; - - vpi_time_s.type = vpiSimTime; - vpi_time_s.high = 0; - vpi_time_s.low = 0; - - cb_data_s.reason = cbNextSimTime; - cb_data_s.cb_rtn = handle_vpi_callback; - cb_data_s.obj = NULL; - cb_data_s.time = &vpi_time_s; - cb_data_s.value = NULL; - cb_data_s.user_data = (char *)this; - - return register_cb(&cb_data_s); - } - - virtual ~vpi_cb_nexttime() { } -}; - -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 1e664fad..ea96b599 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -103,8 +103,6 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) std::vector writable(name.begin(), name.end()); writable.push_back('\0'); - LOG_WARN("Name passed in is %s", name.c_str()); - new_hdl = vpi_handle_by_name(&writable[0], NULL); if (!new_hdl) @@ -112,7 +110,7 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) if (vpiUnknown == (type = vpi_get(vpiType, new_hdl))) { vpi_free_object(vpi_hdl); - LOG_WARN("Not a VPI object") + LOG_WARN("Not a VPI object"); return new_obj; } @@ -121,19 +119,17 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) case vpiNet: case vpiReg: new_obj = new VpiSignalObjHdl(this, new_hdl); - LOG_WARN("Created VpiSignalObjHdl"); + LOG_DEBUG("Created VpiSignalObjHdl"); break; case vpiModule: new_obj = new VpiObjHdl(this, new_hdl); - LOG_WARN("Created VpiObjHdl"); + LOG_DEBUG("Created VpiObjHdl"); break; default: LOG_CRITICAL("Not sure what to do with type %d for entity (%s)", type, name.c_str()); return NULL; } - LOG_WARN("Type was %d", type); - /* Might move the object creation inside here */ new_obj->initialise(name); return new_obj; @@ -162,11 +158,11 @@ GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) case vpiNet: case vpiNetBit: new_obj = new VpiSignalObjHdl(this, new_hdl); - LOG_WARN("Created VpiSignalObjHdl"); + LOG_DEBUG("Created VpiSignalObjHdl"); break; case vpiModule: new_obj = new VpiObjHdl(this, new_hdl); - LOG_WARN("Created VpiObjHdl"); + LOG_DEBUG("Created VpiObjHdl"); break; default: LOG_CRITICAL("Not sure what to do with type %d below entity (%s) at index (%d)", @@ -174,10 +170,6 @@ GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) return NULL; } - LOG_WARN("Type was %d", type); - /* Might move the object creation inside here */ - //new_obj->initialise(name); - return new_obj; } @@ -297,19 +289,15 @@ GpiCbHdl *VpiImpl::register_nexttime_callback(void) int VpiImpl::deregister_callback(GpiCbHdl *gpi_hdl) { - #if 0 - VpiCbHdl *vpi_obj = reinterpret_cast(gpi_hdl); - if (vpi_obj->get_call_state() == GPI_PRE_CALL) { + if (gpi_hdl->get_call_state() == GPI_PRE_CALL) { //LOG_INFO("Not deleting yet %p", vpi_obj); return 0; } - int rc = vpi_obj->cleanup_callback(); + int rc = gpi_hdl->cleanup_callback(); // LOG_INFO("DELETING %p", vpi_obj); - delete(vpi_obj); + delete(gpi_hdl); return rc; - #endif - return 0; } // If the Pything world wants things to shut down then unregister @@ -340,7 +328,7 @@ int32_t handle_vpi_callback(p_cb_data cb_data) if (!cb_hdl) LOG_CRITICAL("VPI: Callback data corrupted"); - LOG_WARN("Running %p", cb_hdl); + LOG_DEBUG("Running %p", cb_hdl); if (cb_hdl->get_call_state() == GPI_PRIMED) { cb_hdl->set_call_state(GPI_PRE_CALL); @@ -348,11 +336,11 @@ int32_t handle_vpi_callback(p_cb_data cb_data) cb_hdl->set_call_state(GPI_POST_CALL); } - LOG_WARN("Running %p done", cb_hdl); + LOG_DEBUG("Running %p done", cb_hdl); gpi_deregister_callback(cb_hdl); - LOG_WARN("Deregister %p done", cb_hdl); + LOG_DEBUG("Deregister %p done", cb_hdl); FEXIT return rv; diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index ab0d266f..b7c1923a 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -127,42 +127,15 @@ class VpiCbHdl : public GpiCbHdl { s_cb_data cb_data; }; -class VpiSignalObjHdl : public VpiObjHdl, public GpiSignalObjHdl { -public: - VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl) : VpiObjHdl(impl, hdl), - GpiSignalObjHdl(impl) { } - virtual ~VpiSignalObjHdl() { } +class VpiSignalObjHdl; - const char* get_signal_value_binstr(void); - - int set_signal_value(const int value); - int set_signal_value(std::string &value); - //virtual GpiCbHdl monitor_value(bool rising_edge) = 0; this was for the triggers - // but the explicit ones are probably better - - // Also think we want the triggers here? - virtual GpiCbHdl *rising_edge_cb(void) { return NULL; } - virtual GpiCbHdl *falling_edge_cb(void) { return NULL; } - virtual GpiCbHdl *value_change_cb(void) { return NULL; } - - /* Functions that I would like to inherit but do not ?*/ - virtual GpiObjHdl *get_handle_by_name(std::string &name) { - return VpiObjHdl::get_handle_by_name(name); - } - virtual GpiObjHdl *get_handle_by_index(uint32_t index) { - return VpiObjHdl::get_handle_by_index(index); - } - virtual GpiIterator *iterate_handle(uint32_t type) - { - return VpiObjHdl::iterate_handle(type); - } - virtual GpiObjHdl *next_handle(GpiIterator *iterator) - { - return VpiObjHdl::next_handle(iterator); - } - virtual int initialise(std::string &name) { - return VpiObjHdl::initialise(name); - } +class VpiValueCbHdl : public VpiCbHdl { +public: + VpiValueCbHdl(GpiImplInterface *impl, VpiSignalObjHdl *sig); + virtual ~VpiValueCbHdl() { } + int cleanup_callback(void); +private: + s_vpi_time vpi_time; }; class VpiTimedCbHdl : public VpiCbHdl { @@ -173,7 +146,6 @@ class VpiTimedCbHdl : public VpiCbHdl { s_vpi_time vpi_time; }; - class VpiReadOnlyCbHdl : public VpiCbHdl { public: VpiReadOnlyCbHdl(GpiImplInterface *impl); @@ -212,4 +184,44 @@ class VpiReadwriteCbHdl : public VpiCbHdl { s_vpi_time vpi_time; }; +class VpiSignalObjHdl : public VpiObjHdl, public GpiSignalObjHdl { +public: + VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl) : VpiObjHdl(impl, hdl), + GpiSignalObjHdl(impl) { } + virtual ~VpiSignalObjHdl() { } + + const char* get_signal_value_binstr(void); + + int set_signal_value(const int value); + int set_signal_value(std::string &value); + //virtual GpiCbHdl monitor_value(bool rising_edge) = 0; this was for the triggers + // but the explicit ones are probably better + + // Also think we want the triggers here? + virtual GpiCbHdl *rising_edge_cb(void) { return NULL; } + virtual GpiCbHdl *falling_edge_cb(void) { return NULL; } + virtual GpiCbHdl *value_change_cb(void); + + /* Functions that I would like to inherit but do not ?*/ + virtual GpiObjHdl *get_handle_by_name(std::string &name) { + return VpiObjHdl::get_handle_by_name(name); + } + virtual GpiObjHdl *get_handle_by_index(uint32_t index) { + return VpiObjHdl::get_handle_by_index(index); + } + virtual GpiIterator *iterate_handle(uint32_t type) + { + return VpiObjHdl::iterate_handle(type); + } + virtual GpiObjHdl *next_handle(GpiIterator *iterator) + { + return VpiObjHdl::next_handle(iterator); + } + virtual int initialise(std::string &name) { + return VpiObjHdl::initialise(name); + } +private: + VpiValueCbHdl *value_cb; +}; + #endif /*COCOTB_VPI_IMPL_H_ */ \ No newline at end of file From 04bfa473d172382e8ddc4031fbeb8c83f2bb2f27 Mon Sep 17 00:00:00 2001 From: Finn Grimwood Date: Mon, 20 Oct 2014 21:12:40 +0100 Subject: [PATCH 0625/2656] Issue #143: Add a falling edge trigger --- cocotb/triggers.py | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 13ae8cf6..ee843985 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -197,24 +197,21 @@ def __str__(self): def NextTimeStep(): return _nxts -class _RisingEdge(Edge): - """ - Execution will resume when a rising edge occurs on the provided signal - - NB Riviera doesn't seem to like re-using a callback handle? - - """ - def __init__(self, signal): +class _RisingOrFallingEdge(Edge): + def __init__(self, signal, rising): Edge.__init__(self, signal) + self._rising = rising def prime(self, callback): Trigger.prime(self) self._callback = callback def _check(obj): - if self.signal.value: + condition = self.signal.value == self._rising + if condition: self._callback(self) else: + #Riviera doesn't seem to like re-using a callback handle? simulator.reenable_callback(self.cbhdl) if simulator.register_value_change_callback(self.cbhdl, self.signal._handle, _check, self): @@ -223,10 +220,23 @@ def _check(obj): def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal.name +class _RisingEdge(_RisingOrFallingEdge): + """ + Execution will resume when a rising edge occurs on the provided signal + """ + def __init__(self, signal): + _RisingOrFallingEdge.__init__(self, signal, rising=True) def RisingEdge(signal): return signal._edge +class FallingEdge(_RisingOrFallingEdge): + """ + Execution will resume when a falling edge occurs on the provided signal + """ + def __init__(self, signal): + _RisingOrFallingEdge.__init__(self, signal, rising=False) + class ClockCycles(Edge): """ Execution will resume after N rising edges From c08f3c3593093f9991aeae15207f15fc79fca700 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 20 Oct 2014 22:03:43 +0100 Subject: [PATCH 0626/2656] Issue#161: Remove debug now working --- lib/gpi/GpiCommon.cpp | 1 - lib/vpi/VpiCbHdl.cpp | 1 - lib/vpi/VpiImpl.cpp | 13 ++----------- lib/vpi/VpiImpl.h | 2 +- 4 files changed, 3 insertions(+), 14 deletions(-) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 88211954..fcfefa9e 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -52,7 +52,6 @@ void gpi_embed_end(void) void gpi_sim_end(void) { - LOG_WARN("Sim end called"); registered_impls[0]->sim_end(); } diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index d94bfc44..a156afd1 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -223,7 +223,6 @@ VpiShutdownCbHdl::VpiShutdownCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) } int VpiShutdownCbHdl::run_callback(void) { - LOG_WARN("Shutdown called"); gpi_embed_end(); return 0; } diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index ea96b599..b8a6903b 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -26,6 +26,8 @@ ******************************************************************************/ #include "VpiImpl.h" +#include +#include #include extern "C" { @@ -309,9 +311,7 @@ void VpiImpl::sim_end(void) */ sim_finish_cb->set_call_state(GPI_DELETE); vpi_control(vpiFinish); - LOG_WARN("Returned from vpi_control"); check_vpi_error(); - LOG_WARN("Returned from check_error"); } extern "C" { @@ -321,27 +321,20 @@ int32_t handle_vpi_callback(p_cb_data cb_data) { FENTER int rv = 0; - //vpiHandle old_cb; VpiCbHdl *cb_hdl = (VpiCbHdl*)cb_data->user_data; if (!cb_hdl) LOG_CRITICAL("VPI: Callback data corrupted"); - LOG_DEBUG("Running %p", cb_hdl); - if (cb_hdl->get_call_state() == GPI_PRIMED) { cb_hdl->set_call_state(GPI_PRE_CALL); cb_hdl->run_callback(); cb_hdl->set_call_state(GPI_POST_CALL); } - LOG_DEBUG("Running %p done", cb_hdl); - gpi_deregister_callback(cb_hdl); - LOG_DEBUG("Deregister %p done", cb_hdl); - FEXIT return rv; }; @@ -349,7 +342,6 @@ int32_t handle_vpi_callback(p_cb_data cb_data) static void register_embed(void) { - printf("%s %d Registered VPI \n", __func__, __LINE__); vpi_table = new VpiImpl("VPI"); gpi_register_impl(vpi_table); gpi_embed_init_python(); @@ -358,7 +350,6 @@ static void register_embed(void) static void register_initial_callback(void) { - LOG_WARN("Initial callback registering"); sim_init_cb = new VpiStartupCbHdl(vpi_table); /* We ignore the return value here as VCS does some silly diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index b7c1923a..af6b0eb6 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -122,7 +122,7 @@ class VpiCbHdl : public GpiCbHdl { virtual int arm_callback(void); virtual int cleanup_callback(void); -protected: +public: vpiHandle vpi_hdl; s_cb_data cb_data; }; From 2add93093bb0342cabc140437d5f3178abdd050d Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 20 Oct 2014 22:04:31 +0100 Subject: [PATCH 0627/2656] Cleanup: Release lock on log error, not sure why the error occours but we at least finish in aldec now --- lib/gpi_log/gpi_logging.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/gpi_log/gpi_logging.c b/lib/gpi_log/gpi_logging.c index 541ae75d..ed89b6b8 100644 --- a/lib/gpi_log/gpi_logging.c +++ b/lib/gpi_log/gpi_logging.c @@ -137,6 +137,7 @@ void gpi_log(const char *name, long level, const char *pathname, const char *fun if (retuple != Py_True) { Py_DECREF(check_args); + PyGILState_Release(gstate); return; } @@ -157,6 +158,7 @@ void gpi_log(const char *name, long level, const char *pathname, const char *fun retuple = PyObject_CallObject(pLogHandler, call_args); if (retuple != Py_True) { + PyGILState_Release(gstate); return; } From 2398e7e24513caefcd07cc64faa9eb30463c8891 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 20 Oct 2014 22:04:57 +0100 Subject: [PATCH 0628/2656] Cleanup: Disable this test as it causes a python error --- examples/functionality/tests/test_regression.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/functionality/tests/test_regression.py b/examples/functionality/tests/test_regression.py index fe701a18..2a5c320f 100644 --- a/examples/functionality/tests/test_regression.py +++ b/examples/functionality/tests/test_regression.py @@ -48,7 +48,7 @@ def issue_120_scheduling(dut): -@cocotb.test() +@cocotb.test(skip=True) def issue_142_overflow_error(dut): """Tranparently convert ints too long to pass through the GPI interface natively into BinaryValues""" From 39d71a6f9a541cd6df81b233614ed58b84905ab3 Mon Sep 17 00:00:00 2001 From: Finn Grimwood Date: Wed, 22 Oct 2014 22:23:41 +0100 Subject: [PATCH 0629/2656] fix #143: Update documentation with falling edge trigger --- documentation/source/triggers.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/documentation/source/triggers.rst b/documentation/source/triggers.rst index edcae0b6..849e4460 100644 --- a/documentation/source/triggers.rst +++ b/documentation/source/triggers.rst @@ -34,13 +34,18 @@ Registers a callback that will continue execution of the coroutine on any value Behaviour for vectors - RisingEdge(signal) ^^^^^^^^^^^^^^^^^^ Registers a callback that will continue execution of the coroutine on a transition from 0 to 1 of signal. +FallingEdge(signal) +^^^^^^^^^^^^^^^^^^ + +Registers a callback that will continue execution of the coroutine on a transition from 1 to 0 of signal. + + ClockCycles(signal, num_cycles) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 551c0c2840abd2c7a9e2fa2bb056ccfd34db377f Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 23 Oct 2014 12:33:52 +0100 Subject: [PATCH 0630/2656] Issue #168: Add a debug rule to Makefile.pylib to assist in debug --- Makefile | 5 +++-- makefiles/Makefile.pylib | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index aede5dcb..8567d926 100644 --- a/Makefile +++ b/Makefile @@ -68,9 +68,10 @@ install: src_install common_install pycode create_files @echo -e "To uninstall run $(FULL_INSTALL_DIR)/bin/cocotb_uninstall\n" help: - @echo -e "\nCoCoTB make help\n\nall\t- Build libaries for native" + @echo -e "\nCocotb make help\n\nall\t- Build libaries for native" @echo -e "install\t- Build and install libaries to FULL_INSTALL_DIR (default=$(FULL_INSTALL_DIR))" - @echo -e "clean\t- Clean the build dir\n\n" + @echo -e "clean\t- Clean the build dir" + @echo -e "debug\t- Dump out some useful debug info\n\n" @echo -e "To build natively just run make.\nTo build for 32bit on a 64 bit system set ARCH=i686\n" @echo -e "Default simulator is Icarus. To use another set environment variable SIM as below\n" @for X in $(shell ls makefiles/simulators/); do \ diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib index 651f90e2..ac5d4ed2 100644 --- a/makefiles/Makefile.pylib +++ b/makefiles/Makefile.pylib @@ -53,4 +53,13 @@ PYLIBS = $(shell python-config --libs) PYTHON_INCLUDEDIR := -I$(shell python -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_inc()') +debug:: + @echo "Debug from Makefile.pylib" + @echo -e "\tUsing PYTHON_VERSION=$(PYTHON_VERSION)" + @echo -e "\tUsing PYTHON_LIBDIR=$(PYTHON_LIBDIR)" + @echo -e "\tUsing PYTHON_SO_LIB=libpython$(PYTHON_VERSION).so" + @echo -e "\tUsing PYTHON_DYNLIBDIR=$(PYTHON_DYNLIBDIR)" + @echo -e "\tUsing PYTHON_INCLUDEDIR=$(PYTHON_INCLUDEDIR)" + file $(PYTHON_LIBDIR)/libpython$(PYTHON_VERSION).so + file -L $(PYTHON_LIBDIR)/libpython$(PYTHON_VERSION).so From 7066290447e346aa4f1166a85134f6ca6c8888f5 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 25 Oct 2014 19:05:40 +0100 Subject: [PATCH 0631/2656] Cleanup: Remove empty file --- hook_test | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 hook_test diff --git a/hook_test b/hook_test deleted file mode 100644 index e69de29b..00000000 From 678bf30da131f606628226ed4f0d35957ad80ce5 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 25 Oct 2014 19:08:43 +0100 Subject: [PATCH 0632/2656] Issue #169: Correct logic conditions for padding values --- cocotb/binary.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index ce8a9ac6..83c39219 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -174,7 +174,7 @@ def _adjust_unsigned (self,x): if self._bits is None: return l = len(x) - if l < self._bits: + if l <= self._bits: if self.big_endian: rv = x + '0' * (self._bits-l) else: @@ -192,7 +192,7 @@ def _adjust_signed_mag (self,x): if self._bits is None: return l = len(x) - if l < self._bits: + if l <= self._bits: if self.big_endian: rv = x[:-1] + '0' * (self._bits-1-l) rv = rv + x[-1] @@ -213,7 +213,7 @@ def _adjust_twos_comp (self,x): if self._bits is None: return l = len(x) - if l < self._bits: + if l <= self._bits: if self.big_endian: rv = x + x[-1] * (self._bits-l) else: From 1535c869ae411e7966dcfc854e1cd67ff2d20595 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 26 Oct 2014 08:25:21 +0000 Subject: [PATCH 0633/2656] Add test for monitoring a running task --- examples/functionality/tests/test_cocotb.py | 42 ++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index abcc8491..c0d077bc 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -36,7 +36,7 @@ import cocotb from cocotb.triggers import Timer, Join, RisingEdge, ReadOnly, ReadWrite from cocotb.clock import Clock -from cocotb.result import ReturnValue +from cocotb.result import ReturnValue, TestFailure @@ -257,3 +257,43 @@ def test_fork_syntax_error(dut): cocotb.fork(syntax_error()) yield clock_gen(dut.clk) + + +@cocotb.coroutine +def count_edges_cycles(signal, edges): + edge = RisingEdge(signal) + for i in xrange(edges): + yield edge + signal.log.info("Rising edge %d detected" % i) + signal.log.info("Finished, returning %d" % edges) + raise ReturnValue(edges) + +@cocotb.test() +def test_fork_and_monitor(dut, period=1000, clocks=6): + cocotb.fork(Clock(dut.clk, period).start()) + + # Ensure the clock has started + yield RisingEdge(dut.clk) + + timer = Timer(period + 10) + task = cocotb.fork(count_edges_cycles(dut.clk, clocks)) + count = 0 + expect = clocks-1 + + + while True: + result = yield [timer, task.join()] + if count > expect: + raise TestFailure("Task didn't complete in expected time") + if result is timer: + dut.log.info("Count %d: Task still running" % count) + count += 1 + else: + break + if count != expect: + raise TestFailure("Expected to monitor the task %d times but got %d" % ( + expect, count)) + if result != clocks: + raise TestFailure("Expected task to return %d but got %s" % (clocks, repr(result))) + + From 1ab2fd21bdfbf66ef3926108d76dd6b18d3ba2ca Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 26 Oct 2014 08:36:01 +0000 Subject: [PATCH 0634/2656] Fixes #170: Ensure that Join() triggers always return the result from coroutine finish --- cocotb/scheduler.py | 1 - cocotb/triggers.py | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index f75b5656..95c8cba8 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -488,7 +488,6 @@ def schedule(self, coroutine, trigger=None): self.queue(result) new_trigger = result.join() - new_trigger.pass_retval = True self._coroutine_yielded(coroutine, [new_trigger]) elif isinstance(result, Trigger): diff --git a/cocotb/triggers.py b/cocotb/triggers.py index ee843985..fe71cd60 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -462,6 +462,7 @@ class _Join(PythonTrigger): def __init__(self, coroutine): PythonTrigger.__init__(self) self._coroutine = coroutine + self.pass_retval = True @property def retval(self): From 112342a73019fe7db160c966a1394787a5a8ba2e Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 26 Oct 2014 08:37:32 +0000 Subject: [PATCH 0635/2656] Issue #168: Comment out debug - not meant to be default rule --- makefiles/Makefile.pylib | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib index ac5d4ed2..de92375b 100644 --- a/makefiles/Makefile.pylib +++ b/makefiles/Makefile.pylib @@ -53,13 +53,13 @@ PYLIBS = $(shell python-config --libs) PYTHON_INCLUDEDIR := -I$(shell python -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_inc()') -debug:: - @echo "Debug from Makefile.pylib" - @echo -e "\tUsing PYTHON_VERSION=$(PYTHON_VERSION)" - @echo -e "\tUsing PYTHON_LIBDIR=$(PYTHON_LIBDIR)" - @echo -e "\tUsing PYTHON_SO_LIB=libpython$(PYTHON_VERSION).so" - @echo -e "\tUsing PYTHON_DYNLIBDIR=$(PYTHON_DYNLIBDIR)" - @echo -e "\tUsing PYTHON_INCLUDEDIR=$(PYTHON_INCLUDEDIR)" - file $(PYTHON_LIBDIR)/libpython$(PYTHON_VERSION).so - file -L $(PYTHON_LIBDIR)/libpython$(PYTHON_VERSION).so +#debug:: +# @echo "Debug from Makefile.pylib" +# @echo -e "\tUsing PYTHON_VERSION=$(PYTHON_VERSION)" +# @echo -e "\tUsing PYTHON_LIBDIR=$(PYTHON_LIBDIR)" +# @echo -e "\tUsing PYTHON_SO_LIB=libpython$(PYTHON_VERSION).so" +# @echo -e "\tUsing PYTHON_DYNLIBDIR=$(PYTHON_DYNLIBDIR)" +# @echo -e "\tUsing PYTHON_INCLUDEDIR=$(PYTHON_INCLUDEDIR)" +# file $(PYTHON_LIBDIR)/libpython$(PYTHON_VERSION).so +# file -L $(PYTHON_LIBDIR)/libpython$(PYTHON_VERSION).so From 6686fd356abb34466e4ea86a1dfa72fdb9264b6b Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 26 Oct 2014 08:45:03 +0000 Subject: [PATCH 0636/2656] Issue #142: Return original value if width of signal is unknown --- cocotb/binary.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 83c39219..616235e3 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -170,9 +170,9 @@ def _invert(self,x): inverted = inverted + bit return inverted - def _adjust_unsigned (self,x): + def _adjust_unsigned(self,x): if self._bits is None: - return + return x l = len(x) if l <= self._bits: if self.big_endian: @@ -187,10 +187,10 @@ def _adjust_unsigned (self,x): rv = x[:l - self._bits] return rv - def _adjust_signed_mag (self,x): + def _adjust_signed_mag(self,x): """Pad/truncate the bit string to the correct length""" if self._bits is None: - return + return x l = len(x) if l <= self._bits: if self.big_endian: @@ -209,9 +209,9 @@ def _adjust_signed_mag (self,x): rv = x return rv - def _adjust_twos_comp (self,x): + def _adjust_twos_comp(self,x): if self._bits is None: - return + return x l = len(x) if l <= self._bits: if self.big_endian: From 7e631d1d807c701678f0bedaa7e0fb301a67b71b Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 26 Oct 2014 10:30:35 +0000 Subject: [PATCH 0637/2656] Issue #141: Some work on the FLI layer --- lib/fli/FliImpl.cpp | 12 ++++++-- lib/fli/FliImpl.h | 71 ++++++++++++++++++++++++--------------------- lib/fli/Makefile | 28 +++++------------- 3 files changed, 55 insertions(+), 56 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 5837ec41..fb91a518 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -61,7 +61,8 @@ void FliImpl::get_sim_time(uint32_t *high, uint32_t *low) GpiObjHdl *FliImpl::get_root_handle(const char *name) { mtiRegionIdT root; - VpiObjHdl rv; + FliObjHdl *rv; + std::string root_name = name; for (root = mti_GetTopRegion(); root != NULL; root = mti_NextRegion(root)) { if (name == NULL || !strcmp(name, mti_GetRegionName(root))) @@ -73,7 +74,7 @@ GpiObjHdl *FliImpl::get_root_handle(const char *name) } rv = new FliObjHdl(this, root); - rv->initialise(name); + rv->initialise(root_name); return rv; error: @@ -90,3 +91,10 @@ GpiObjHdl *FliImpl::get_root_handle(const char *name) return NULL; } +extern "C" { + +void cocotb_init(void) { + printf("cocotb_init called\n"); +} + +} // extern "C" \ No newline at end of file diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index 7319dcda..09ad2968 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -29,36 +29,10 @@ #define COCOTB_FLI_IMPL_H_ #include "../gpi/gpi_priv.h" -#include +#include "mti.h" -class FliImpl : public GpiImplInterface { -public: - FliImpl(const std::string& name) : GpiImplInterface(name) { } - - /* Sim related */ - void sim_end(void); - void get_sim_time(uint32_t *high, uint32_t *low); - - /* Hierachy related */ - GpiObjHdl *get_root_handle(const char *name); - - GpiCbHdl *register_timed_callback(uint64_t time_ps); - - /* Callback related, these may (will) return the same handle*/ - GpiCbHdl *register_readonly_callback(void); - GpiCbHdl *register_nexttime_callback(void); - GpiCbHdl *register_readwrite_callback(void); - int deregister_callback(GpiCbHdl *obj_hdl); - bool native_check(std::string &name, GpiObjHdl *parent); - -private: - GpiCbHdl m_readonly_cbhdl; - GpiCbHdl m_nexttime_cbhdl; - GpiCbHdl m_readwrite_cbhdl; - -}; class FliObjHdl : public GpiObjHdl { public: @@ -95,19 +69,18 @@ class FliCbHdl : public GpiCbHdl { // In FLI some callbacks require us to register a process // We use a subclass to track the process state related to the callback class FliProcessCbHdl : public FliCbHdl { - public: - FliCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + FliProcessCbHdl(GpiImplInterface *impl) : FliCbHdl(impl), m_proc_hdl(NULL) { } - virtual ~FliCbHdl() { } + virtual ~FliProcessCbHdl() { } virtual int arm_callback(void); virtual int cleanup_callback(void); private: - mtiProcessIdt m_proc_hdl; + mtiProcessIdT m_proc_hdl; bool m_sensitised; -} +}; class FliSignalObjHdl : public GpiSignalObjHdl { @@ -123,7 +96,7 @@ class FliSignalObjHdl : public GpiSignalObjHdl { virtual GpiCbHdl *value_change_cb(void); private: - mtiSignalIdt fli_hdl; + mtiSignalIdT fli_hdl; }; @@ -150,4 +123,36 @@ class FliShutdownCbHdl : public FliCbHdl { virtual ~FliShutdownCbHdl() { } }; + +class FliImpl : public GpiImplInterface { +public: + FliImpl(const std::string& name) : GpiImplInterface(name), + m_readonly_cbhdl(NULL), + m_nexttime_cbhdl(NULL), + m_readwrite_cbhdl(NULL) { } + + /* Sim related */ + void sim_end(void); + void get_sim_time(uint32_t *high, uint32_t *low); + + /* Hierachy related */ + GpiObjHdl *get_root_handle(const char *name); + + GpiCbHdl *register_timed_callback(uint64_t time_ps); + + /* Callback related, these may (will) return the same handle*/ + GpiCbHdl *register_readonly_callback(void); + GpiCbHdl *register_nexttime_callback(void); + GpiCbHdl *register_readwrite_callback(void); + int deregister_callback(GpiCbHdl *obj_hdl); + bool native_check(std::string &name, GpiObjHdl *parent); + +private: + FliTimedCbHdl m_readonly_cbhdl; + FliTimedCbHdl m_nexttime_cbhdl; + FliTimedCbHdl m_readwrite_cbhdl; + +}; + + #endif /*COCOTB_FLI_IMPL_H_ */ diff --git a/lib/fli/Makefile b/lib/fli/Makefile index 18c01b90..9a80cbd3 100644 --- a/lib/fli/Makefile +++ b/lib/fli/Makefile @@ -1,6 +1,5 @@ ############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc +# Copyright (c) 2014 Potential Ventures Ltd # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -29,30 +28,17 @@ include $(SIM_ROOT)/makefiles/Makefile.inc -INCLUDES += -GCC_ARGS += -DVPI_CHECKING -LIBS := -lgpilog -lcocotb $(EXTRA_LIBS) +INCLUDES += -I/local/tools/modelsim/10.3c/modelsim_dlx/include +GCC_ARGS += -DFLI_CHECKING +LIBS := -lgpilog -lgpi -lstdc++ LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) -LIB_NAME := libgpi.so +LIB_NAME := libfli.so -SRCS := gpi_common.c +SRCS := FliImpl.cpp -CLIBS += $(LIB_DIR)/gpivpi.vpl - -# More rules such that this may be needed depending on the requirements of -# different simulators, icarus for instance loads a .vpi libraray to use the vpi -# inerface at the start of the simulation - -all: $(LIB_DIR)/gpivpi.vpl $(LIB_DIR)/cocotb.vpi - -$(LIB_DIR)/gpivpi.vpl: $(LIB_DIR)/$(LIB_NAME) - ln -sf $(LIB_NAME) $@ - -$(LIB_DIR)/cocotb.vpi: $(LIB_DIR)/$(LIB_NAME) - ln -sf $(LIB_NAME) $@ +all: $(LIB_DIR)/$(LIB_NAME) clean: - -@rm -rf $(LIB_DIR)/gpivpi.vpl -@rm -rf $(LIB_DIR)/$(LIB_NAME) include $(SIM_ROOT)/makefiles/Makefile.rules From 737ac19f0ca039721f43aa5442d60f3ac133558e Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 26 Oct 2014 15:23:50 +0000 Subject: [PATCH 0638/2656] Issue #141: Further work on FLI - can find signal handles --- lib/fli/FliImpl.cpp | 253 +++++++++++++++++++++++++++++++++++++++++++- lib/fli/FliImpl.h | 106 +++++++++++-------- 2 files changed, 312 insertions(+), 47 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index fb91a518..a98856b5 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -25,14 +25,138 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ +#include + #include "FliImpl.h" +#include "acc_vhdl.h" // Messy :( +#include "acc_user.h" + + +extern "C" { + +static FliImpl *fli_table; + +void cocotb_init(void) { + LOG_INFO("cocotb_init called\n"); + + fli_table = new FliImpl("FLI"); + gpi_register_impl(fli_table); + gpi_embed_init_python(); + + // Elaboration has already happened so jump straight in! + gpi_sim_info_t sim_info; + + char *version = mti_GetProductVersion(); // Returned pointer must not be freed + + // copy in sim_info.product + // FIXME split product and version from returned string? + sim_info.argc = 0; + sim_info.argv = NULL; + sim_info.product = version; + sim_info.version = version; + + gpi_embed_init(&sim_info); +} + +// Main re-entry point for callbacks from simulator +void handle_fli_callback(void *data) +{ + FliCbHdl *cb_hdl = (FliCbHdl*)data; + if (!cb_hdl) + LOG_CRITICAL("FLI: Callback data corrupted"); + + if (cb_hdl->get_call_state() == GPI_PRIMED) { + cb_hdl->set_call_state(GPI_PRE_CALL); + cb_hdl->run_callback(); + cb_hdl->set_call_state(GPI_POST_CALL); + } + + gpi_deregister_callback(cb_hdl); +}; + +} // extern "C" void FliImpl::sim_end(void) { mti_Quit(); } +/** + * @name Native Check + * @brief Determine whether a simulation object is native to FLI + */ +bool FliImpl::native_check(std::string &name, GpiObjHdl *parent) +{ + bool ret = true; + int kind = 0;// = mti_GetRegionKind(); + + // FIXME: Not clear where all these types are defined and how to ascertain + // a definitive mapping here... + switch (kind) { + case accPackage: + case accArchitecture: + ret = true; + break; + case accFunction: + case accModule: + case accForeign: + case accTask: + ret = false; + break; + default: +// LOG_ERROR("Unrecognised kind for %s: %d", name, kind); + ret = false; + break; + } + + return ret; +} + +/** + * @name Native Check Create + * @brief Determine whether a simulation object is native to FLI and create + * a handle if it is + */ +GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) +{ + LOG_INFO("Looking for child %s from %s", name.c_str(), parent->get_name_str()); + + + FliObjHdl *new_obj = NULL; + std::vector writable(name.begin(), name.end()); + writable.push_back('\0'); + + mtiSignalIdT sig_hdl; + sig_hdl = mti_FindSignal(&writable[0]); + if (sig_hdl) { + LOG_INFO("Found a signal %s -> %p", &writable[0], sig_hdl); + new_obj = new FliSignalObjHdl(this, sig_hdl); + } + + if (NULL == new_obj) { + LOG_WARN("Didn't find anything named %s", &writable[0]); + return NULL; + } + + new_obj->initialise(name); + return new_obj; +} + +/** + * @name Native Check Create + * @brief Determine whether a simulation object is native to FLI and create + * a handle if it is + */ +GpiObjHdl* FliImpl::native_check_create(uint32_t index, GpiObjHdl *parent) +{ + return NULL; +} + +const char *FliImpl::reason_to_string(int reason) +{ + return "Who can explain it, who can tell you why?"; +} /** @@ -47,6 +171,25 @@ void FliImpl::get_sim_time(uint32_t *high, uint32_t *low) *low = mti_Now(); } +/** + * @name Find the root handle + * @brief Find the root handle using an optional name + * + * Get a handle to the root simulator object. This is usually the toplevel. + * + * If no name is provided, we return the first root instance. + * + * If name is provided, we check the name against the available objects until + * we find a match. If no match is found we return NULL + */ +int FliObjHdl::initialise(std::string &name) { + m_name = name; + m_type = "unknown"; + + return 0; +} + + /** * @name Find the root handle * @brief Find the root handle using an optional name @@ -65,6 +208,7 @@ GpiObjHdl *FliImpl::get_root_handle(const char *name) std::string root_name = name; for (root = mti_GetTopRegion(); root != NULL; root = mti_NextRegion(root)) { + LOG_INFO("Iterating over: %s", mti_GetRegionName(root)); if (name == NULL || !strcmp(name, mti_GetRegionName(root))) break; } @@ -73,8 +217,12 @@ GpiObjHdl *FliImpl::get_root_handle(const char *name) goto error; } - rv = new FliObjHdl(this, root); + LOG_INFO("Found toplevel: %s, creating handle....", name); + + rv = new FliRegionObjHdl(this, root); rv->initialise(root_name); + + LOG_INFO("Returning root handle %p", rv); return rv; error: @@ -91,10 +239,105 @@ GpiObjHdl *FliImpl::get_root_handle(const char *name) return NULL; } -extern "C" { -void cocotb_init(void) { - printf("cocotb_init called\n"); +GpiCbHdl *FliImpl::register_timed_callback(uint64_t time_ps) +{ + FliTimedCbHdl *hdl = new FliTimedCbHdl(this, time_ps); + + if (hdl->arm_callback()) { + delete(hdl); + hdl = NULL; + } + return hdl; +} + + +GpiCbHdl *FliImpl::register_readonly_callback(void) +{ + if (m_readonly_cbhdl.arm_callback()) { + return NULL; + } + return &m_readonly_cbhdl; +} + +GpiCbHdl *FliImpl::register_readwrite_callback(void) +{ + if (m_readwrite_cbhdl.arm_callback()) { + return NULL; + } + return &m_readwrite_cbhdl; +} + +GpiCbHdl *FliImpl::register_nexttime_callback(void) +{ + if (m_nexttime_cbhdl.arm_callback()) { + return NULL; + } + return &m_nexttime_cbhdl; +} + + +int FliImpl::deregister_callback(GpiCbHdl *gpi_hdl) +{ + if (gpi_hdl->get_call_state() == GPI_PRE_CALL) { + return 0; + } + + int rc = gpi_hdl->cleanup_callback(); + // TOOD: Don't delete if it's a re-usable doobery +// delete(gpi_hdl); + return rc; +} + + + + + +/** + * @name cleanup callback + * @brief Called while unwinding after a GPI callback + * + * We keep the process but de-sensitise it + * + * NB need a way to determine if should leave it sensitised, hmmm... + * + */ +int FliProcessCbHdl::cleanup_callback(void) { + + if (m_sensitised) + mti_Desensitize(m_proc_hdl); + m_sensitised = false; + return 0; +} + +int FliTimedCbHdl::arm_callback(void) { + m_proc_hdl = mti_CreateProcessWithPriority(NULL, handle_fli_callback, (void *)this, MTI_PROC_IMMEDIATE); + mti_ScheduleWakeup(m_proc_hdl, m_time_ps); + m_sensitised = true; + return 0; +} + +int FliSignalCbHdl::arm_callback(void) { + + if (NULL == m_proc_hdl) { + LOG_DEBUG("Creating a new process to sensitise to signal %s", mti_GetSignalName(m_sig_hdl)); + m_proc_hdl = mti_CreateProcess(NULL, handle_fli_callback, (void *)this); + } + + mti_Sensitize(m_proc_hdl, m_sig_hdl, MTI_EVENT); + m_sensitised = true; + return 0; +} + +int FliSimPhaseCbHdl::arm_callback(void) { + + if (NULL == m_proc_hdl) { + LOG_DEBUG("Creating a new process to sensitise with priority %d", m_priority); + m_proc_hdl = mti_CreateProcessWithPriority(NULL, handle_fli_callback, (void *)this, m_priority); + } + + mti_ScheduleWakeup(m_proc_hdl, 0); + m_sensitised = true; + return 0; } -} // extern "C" \ No newline at end of file diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index 09ad2968..ff098482 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -32,34 +32,48 @@ #include "mti.h" - - class FliObjHdl : public GpiObjHdl { public: - FliObjHdl(GpiImplInterface *impl, mtiRegionIdT hdl) : GpiObjHdl(impl), - fli_hdl(hdl) { } + FliObjHdl(GpiImplInterface *impl) : GpiObjHdl(impl) { } virtual ~FliObjHdl() { } - virtual GpiObjHdl *get_handle_by_name(std::string &name); - virtual GpiObjHdl *get_handle_by_index(uint32_t index); - virtual GpiIterator *iterate_handle(uint32_t type) { return NULL ;} - virtual GpiObjHdl *next_handle(GpiIterator *iterator) { return NULL; } + GpiObjHdl *get_handle_by_name(std::string &name) {return NULL; }; + GpiObjHdl *get_handle_by_index(uint32_t index) {return NULL; } ; + GpiIterator *iterate_handle(uint32_t type) { return NULL; } + GpiObjHdl *next_handle(GpiIterator *iterator) { return NULL; } + + int initialise(std::string &name); +}; + +class FliRegionObjHdl : public FliObjHdl { +public: + FliRegionObjHdl(GpiImplInterface *impl, mtiRegionIdT hdl) : FliObjHdl(impl), + m_fli_hdl(hdl) { } + virtual ~FliRegionObjHdl() { } + +protected: + mtiRegionIdT m_fli_hdl; +}; - const char* get_name_str(void); - const char* get_type_str(void); +class FliSignalObjHdl : public FliObjHdl { +public: + FliSignalObjHdl(GpiImplInterface *impl, mtiSignalIdT hdl) : FliObjHdl(impl), + m_fli_hdl(hdl) { } + virtual ~FliSignalObjHdl() { } protected: - mtiRegionIdT fli_hdl; + mtiSignalIdT m_fli_hdl; }; + class FliCbHdl : public GpiCbHdl { public: FliCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl) { } virtual ~FliCbHdl() { } - virtual int arm_callback(void); - virtual int cleanup_callback(void); + virtual int arm_callback(void) = 0; + virtual int cleanup_callback(void) = 0; protected: int register_cb(p_cb_data cb_data); @@ -71,50 +85,54 @@ class FliCbHdl : public GpiCbHdl { class FliProcessCbHdl : public FliCbHdl { public: FliProcessCbHdl(GpiImplInterface *impl) : FliCbHdl(impl), - m_proc_hdl(NULL) { } + m_proc_hdl(NULL) { } virtual ~FliProcessCbHdl() { } - virtual int arm_callback(void); - virtual int cleanup_callback(void); + virtual int arm_callback(void) = 0; + int cleanup_callback(void); -private: +protected: mtiProcessIdT m_proc_hdl; bool m_sensitised; }; +// One class of callbacks uses mti_Sensitize to react to a signal +class FliSignalCbHdl : public FliProcessCbHdl { -class FliSignalObjHdl : public GpiSignalObjHdl { public: - FliSignalObjHdl(GpiImplInterface *impl) : GpiSignalObjHdl(impl) { } - virtual ~FliSignalObjHdl() { } - - virtual int set_signal_value(const int value); - virtual int set_signal_value(const char *str); + FliSignalCbHdl(GpiImplInterface *impl, mtiSignalIdT sig_hdl) : FliProcessCbHdl(impl), m_sig_hdl(sig_hdl) { } + virtual ~FliSignalCbHdl() { } - virtual GpiCbHdl *rising_edge_cb(void); - virtual GpiCbHdl *falling_edge_cb(void); - virtual GpiCbHdl *value_change_cb(void); + int arm_callback(void); private: - mtiSignalIdT fli_hdl; - + mtiSignalIdT m_sig_hdl; }; -class FliTimedCbHdl : public FliCbHdl { + +// All other callbacks are related to the simulation phasing +class FliSimPhaseCbHdl : public FliProcessCbHdl { + public: - FliTimedCbHdl(GpiImplInterface *impl) : FliCbHdl(impl) { } - int arm_callback(uint64_t time_ps); - virtual ~FliTimedCbHdl() { } + FliSimPhaseCbHdl(GpiImplInterface *impl) : FliProcessCbHdl(impl) { } + virtual ~FliSimPhaseCbHdl() { } + + int arm_callback(void); + +protected: + mtiProcessPriorityT m_priority; }; -class FliStartupCbHdl : public FliCbHdl { +class FliTimedCbHdl : public FliProcessCbHdl { public: - FliStartupCbHdl(GpiImplInterface *impl) : FliCbHdl(impl) { } - int run_callback(void); + FliTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : FliProcessCbHdl(impl), m_time_ps(time_ps) {}; + virtual ~FliTimedCbHdl() { } int arm_callback(void); - virtual ~FliStartupCbHdl() { } +private: + uint64_t m_time_ps; }; + class FliShutdownCbHdl : public FliCbHdl { public: FliShutdownCbHdl(GpiImplInterface *impl) : FliCbHdl(impl) { } @@ -136,21 +154,25 @@ class FliImpl : public GpiImplInterface { void get_sim_time(uint32_t *high, uint32_t *low); /* Hierachy related */ + bool native_check(std::string &name, GpiObjHdl *parent); + GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); + GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); GpiObjHdl *get_root_handle(const char *name); - GpiCbHdl *register_timed_callback(uint64_t time_ps); - /* Callback related, these may (will) return the same handle*/ + GpiCbHdl *register_timed_callback(uint64_t time_ps); GpiCbHdl *register_readonly_callback(void); GpiCbHdl *register_nexttime_callback(void); GpiCbHdl *register_readwrite_callback(void); int deregister_callback(GpiCbHdl *obj_hdl); - bool native_check(std::string &name, GpiObjHdl *parent); + + /* Method to provide strings from operation types */ + const char *reason_to_string(int reason); private: - FliTimedCbHdl m_readonly_cbhdl; - FliTimedCbHdl m_nexttime_cbhdl; - FliTimedCbHdl m_readwrite_cbhdl; + FliSimPhaseCbHdl m_readonly_cbhdl; + FliSimPhaseCbHdl m_nexttime_cbhdl; + FliSimPhaseCbHdl m_readwrite_cbhdl; }; From d8d63ea597fe6790e0a14a597d5ffdb2c342b3a9 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 20 Oct 2014 22:58:16 +0100 Subject: [PATCH 0639/2656] Issue#161: Revert debug change to member vars of VpiCbHdl --- lib/vhpi/VhpiImpl.h | 94 ++++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 40 deletions(-) diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 79df0296..14c4e707 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -88,7 +88,7 @@ class VhpiImpl : public GpiImplInterface { GpiCbHdl *register_timed_callback(uint64_t time_ps); GpiCbHdl *register_readonly_callback(void) { return NULL; } GpiCbHdl *register_nexttime_callback(void) { return NULL; } - GpiCbHdl *register_readwrite_callback(void) { return NULL; } + GpiCbHdl *register_readwrite_callback(void); int deregister_callback(GpiCbHdl *obj_hdl) { return 0; } bool native_check(std::string &name, GpiObjHdl *parent) { return false; } GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); @@ -130,47 +130,15 @@ class VhpiCbHdl : public GpiCbHdl { vhpiCbDataT cb_data; }; -class VhpiSignalObjHdl : public VhpiObjHdl, public GpiSignalObjHdl { -public: - VhpiSignalObjHdl(GpiImplInterface *impl, vhpiHandleT hdl) : VhpiObjHdl(impl, hdl), - GpiSignalObjHdl(impl), - m_size(0) { } - virtual ~VhpiSignalObjHdl(); - - const char* get_signal_value_binstr(void); - - int set_signal_value(const int value); - int set_signal_value(std::string &value); - //virtual GpiCbHdl monitor_value(bool rising_edge) = 0; this was for the triggers - // but the explicit ones are probably better - - // Also think we want the triggers here? - virtual GpiCbHdl *rising_edge_cb(void) { return NULL; } - virtual GpiCbHdl *falling_edge_cb(void) { return NULL; } - virtual GpiCbHdl *value_change_cb(void) { return NULL; } - - /* Functions that I would like to inherit but do not ?*/ - virtual GpiObjHdl *get_handle_by_name(std::string &name) { - return VhpiObjHdl::get_handle_by_name(name); - } - virtual GpiObjHdl *get_handle_by_index(uint32_t index) { - return VhpiObjHdl::get_handle_by_index(index); - } - virtual GpiIterator *iterate_handle(uint32_t type) - { - return VhpiObjHdl::iterate_handle(type); - } - virtual GpiObjHdl *next_handle(GpiIterator *iterator) - { - return VhpiObjHdl::next_handle(iterator); - } - virtual int initialise(std::string &name); +class VhpiSignalObjHdl; +class VhpiValueCbHdl : public VhpiCbHdl { +public: + VhpiValueCbHdl(GpiImplInterface *impl, VhpiSignalObjHdl *sig); + virtual ~VhpiValueCbHdl() { } + int cleanup_callback(void); private: - const vhpiEnumT chr2vhpi(const char value); - unsigned int m_size; - vhpiValueT m_value; - vhpiValueT m_binvalue; + vhpiTimeT vhpi_time; }; class VhpiTimedCbHdl : public VhpiCbHdl { @@ -213,6 +181,52 @@ class VhpiReadwriteCbHdl : public VhpiCbHdl { public: VhpiReadwriteCbHdl(GpiImplInterface *impl); virtual ~VhpiReadwriteCbHdl() { } +private: + vhpiTimeT vhpi_time; +}; + +class VhpiSignalObjHdl : public VhpiObjHdl, public GpiSignalObjHdl { +public: + VhpiSignalObjHdl(GpiImplInterface *impl, vhpiHandleT hdl) : VhpiObjHdl(impl, hdl), + GpiSignalObjHdl(impl), + m_size(0) { } + virtual ~VhpiSignalObjHdl(); + + const char* get_signal_value_binstr(void); + + int set_signal_value(const int value); + int set_signal_value(std::string &value); + //virtual GpiCbHdl monitor_value(bool rising_edge) = 0; this was for the triggers + // but the explicit ones are probably better + + // Also think we want the triggers here? + virtual GpiCbHdl *rising_edge_cb(void) { return NULL; } + virtual GpiCbHdl *falling_edge_cb(void) { return NULL; } + virtual GpiCbHdl *value_change_cb(void); + + /* Functions that I would like to inherit but do not ?*/ + virtual GpiObjHdl *get_handle_by_name(std::string &name) { + return VhpiObjHdl::get_handle_by_name(name); + } + virtual GpiObjHdl *get_handle_by_index(uint32_t index) { + return VhpiObjHdl::get_handle_by_index(index); + } + virtual GpiIterator *iterate_handle(uint32_t type) + { + return VhpiObjHdl::iterate_handle(type); + } + virtual GpiObjHdl *next_handle(GpiIterator *iterator) + { + return VhpiObjHdl::next_handle(iterator); + } + virtual int initialise(std::string &name); + +private: + const vhpiEnumT chr2vhpi(const char value); + unsigned int m_size; + vhpiValueT m_value; + vhpiValueT m_binvalue; + VhpiValueCbHdl *value_cb; }; #endif /*COCOTB_VHPI_IMPL_H_ */ \ No newline at end of file From 1fbb98218fa422c3c1ae31430078d9c3e14551b8 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sat, 25 Oct 2014 18:26:24 +0100 Subject: [PATCH 0640/2656] Issue#161: Remove dead code from simulatormodule --- lib/simulator/simulatormodule.c | 78 +++++---------------------------- lib/simulator/simulatormodule.h | 2 - 2 files changed, 10 insertions(+), 70 deletions(-) diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 44cb1318..bc160615 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -159,9 +159,7 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; - //PyObject *handle; gpi_sim_hdl hdl; - //int ret; PyGILState_STATE gstate; gstate = TAKE_GIL(); @@ -175,9 +173,6 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) return NULL; } - //handle = PyTuple_GetItem(args, 0); - //hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(handle); - // Extract the callback function function = PyTuple_GetItem(args, 0); if (!PyCallable_Check(function)) { @@ -203,7 +198,7 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) callback_data_p->function = function; callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; - //callback_data_p->cb_hdl = hdl; + hdl = gpi_register_readonly_callback((gpi_function_t)handle_gpi_callback, callback_data_p); PyObject *rv = Py_BuildValue("l", hdl); @@ -220,9 +215,7 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; - //PyObject *handle; gpi_sim_hdl hdl; - //int ret; PyGILState_STATE gstate; gstate = TAKE_GIL(); @@ -236,9 +229,6 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) return NULL; } - //handle = PyTuple_GetItem(args, 0); - //hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(handle); - // Extract the callback function function = PyTuple_GetItem(args, 0); if (!PyCallable_Check(function)) { @@ -264,7 +254,7 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) callback_data_p->function = function; callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; - //callback_data_p->cb_hdl = hdl; + hdl = gpi_register_readwrite_callback((gpi_function_t)handle_gpi_callback, callback_data_p); PyObject *rv = Py_BuildValue("l", hdl); @@ -281,9 +271,7 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; - //PyObject *handle; gpi_sim_hdl hdl; - //int ret; PyGILState_STATE gstate; gstate = TAKE_GIL(); @@ -297,9 +285,6 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) return NULL; } - //handle = PyTuple_GetItem(args, 0); - //hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(handle); - // Extract the callback function function = PyTuple_GetItem(args, 0); if (!PyCallable_Check(function)) { @@ -325,7 +310,7 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) callback_data_p->function = function; callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; - //callback_data_p->cb_hdl = hdl; + hdl = gpi_register_nexttime_callback((gpi_function_t)handle_gpi_callback, callback_data_p); PyObject *rv = Py_BuildValue("l", hdl); @@ -346,10 +331,8 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; - //PyObject *handle; gpi_sim_hdl hdl; uint64_t time_ps; - //int ret; p_callback_data callback_data_p; @@ -363,10 +346,6 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) return NULL; } - // Get the handle - //handle = PyTuple_GetItem(args, 0); - //hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(handle); - // Extract the time PyObject *pTime = PyTuple_GetItem(args, 0); time_ps = PyLong_AsLongLong(pTime); @@ -397,7 +376,7 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) callback_data_p->function = function; callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; - //callback_data_p->cb_hdl = hdl; + hdl = gpi_register_timed_callback((gpi_function_t)handle_gpi_callback, callback_data_p, time_ps); // Check success @@ -420,11 +399,8 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) PyObject *fArgs; PyObject *function; gpi_sim_hdl sig_hdl; - //PyObject *handle; gpi_sim_hdl hdl; - //int ret; - - + int edge; PyGILState_STATE gstate; gstate = TAKE_GIL(); @@ -438,9 +414,6 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) return NULL; } - //handle = PyTuple_GetItem(args, 0); - //hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(handle); - PyObject *pSihHdl = PyTuple_GetItem(args, 0); sig_hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(pSihHdl); @@ -471,9 +444,11 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) callback_data_p->function = function; callback_data_p->args = fArgs; callback_data_p->kwargs = NULL; - int edge = 3; - //callback_data_p->cb_hdl = hdl; - hdl = gpi_register_value_change_callback((gpi_function_t)handle_gpi_callback, callback_data_p, sig_hdl, edge); + + hdl = gpi_register_value_change_callback((gpi_function_t)handle_gpi_callback, + callback_data_p, + sig_hdl, + edge); // Check success PyObject *rv = Py_BuildValue("l", hdl); @@ -759,39 +734,6 @@ static PyObject *stop_simulator(PyObject *self, PyObject *args) } -static PyObject *reenable_callback(PyObject *self, PyObject *args) -{ - #if 0 - gpi_sim_hdl hdl; - p_callback_data callback_data_p; - PyObject *pSihHdl; - PyObject *value; - - FENTER - - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - - pSihHdl = PyTuple_GetItem(args, 0); - hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(pSihHdl); -// callback_data_p = (p_callback_data)gpi_get_callback_data(hdl); - -// printf("Reenable callback, previous id_value: %08x\n", callback_data_p->id_value); - - callback_data_p->id_value = COCOTB_ACTIVE_ID; -// printf("Now id_value: %08x\n", callback_data_p->id_value); - - value = Py_BuildValue("s", "OK!"); - - DROP_GIL(gstate); - - FEXIT - return value; - #endif - return NULL; -} - - static PyObject *deregister_callback(PyObject *self, PyObject *args) { gpi_sim_hdl hdl; diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 6d0ad670..4eadd2b6 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -66,7 +66,6 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args); static PyObject *register_readonly_callback(PyObject *self, PyObject *args); static PyObject *register_nextstep_callback(PyObject *self, PyObject *args); static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args); -static PyObject *reenable_callback(PyObject *self, PyObject *args); static PyObject *stop_simulator(PyObject *self, PyObject *args); static PyObject *iterate(PyObject *self, PyObject *args); @@ -90,7 +89,6 @@ static PyMethodDef SimulatorMethods[] = { {"register_readonly_callback", register_readonly_callback, METH_VARARGS, "Register a callback for readonly section"}, {"register_nextstep_callback", register_nextstep_callback, METH_VARARGS, "Register a cllback for the nextsimtime callback"}, {"register_rwsynch_callback", register_rwsynch_callback, METH_VARARGS, "Register a callback for the readwrite section"}, - {"reenable_callback", reenable_callback, METH_VARARGS, "Re-enable a recurring callback"}, {"stop_simulator", stop_simulator, METH_VARARGS, "Instruct the attached simulator to stop"}, {"iterate", iterate, METH_VARARGS, "Get an iterator handle to loop over all members in an object"}, {"next", next, METH_VARARGS, "Get the next object from the iterator"}, From 70dc1a6a39ad9d3991e5d20d9e7105bf5f59b4db Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 27 Oct 2014 09:12:03 +0000 Subject: [PATCH 0641/2656] Issue#161: Push edge detection down into C++ and add test casees - Falling edge disabled for now --- cocotb/scheduler.py | 9 +- cocotb/triggers.py | 16 +-- examples/functionality/tests/test_cocotb.py | 37 ++++++- include/gpi.h | 2 +- lib/gpi/GpiCommon.cpp | 6 +- lib/gpi/gpi_priv.h | 18 +--- lib/simulator/simulatormodule.c | 19 ++-- lib/vpi/VpiCbHdl.cpp | 93 +++++++++++++---- lib/vpi/VpiImpl.cpp | 107 +++++++++----------- lib/vpi/VpiImpl.h | 25 ++++- 10 files changed, 203 insertions(+), 129 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index f75b5656..bab0bfb2 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -237,11 +237,10 @@ def react(self, trigger, depth=0): if _profiling and not depth: _profile.enable() - # We can always unprime the trigger that actually fired - if it's - # recycled then prime will be called again. + # When a trigger fires it is unprimed internally if _debug: - self.log.debug("Trigger fired: %s... unpriming" % str(trigger)) - trigger.unprime() + self.log.debug("Trigger fired: %s" % str(trigger)) + #trigger.unprime() if self._mode == Scheduler._MODE_TERM: if _debug: @@ -265,6 +264,8 @@ def react(self, trigger, depth=0): handle, value = self._writes.popitem() handle.setimmediatevalue(value) + self._readwrite.unprime() + if _profiling: _profile.disable() return diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 27fcd9ab..20824338 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -48,6 +48,7 @@ def __init__(self): self.log = SimLog("cocotb.%s" % (self.__class__.__name__), id(self)) self.signal = None self.primed = False + self.registered = False def prime(self, *args): self.primed = True @@ -91,7 +92,7 @@ def __init__(self): self.cbhdl = None def unprime(self): - """Unregister a prior registered timed callback""" + """Disable a primed trigger, can be reprimed""" if self.cbhdl: simulator.deregister_callback(self.cbhdl) self.cbhdl = None @@ -215,17 +216,10 @@ def __init__(self, signal): Edge.__init__(self, signal) def prime(self, callback): - self._callback = callback - - def _check(obj): - if self.signal.value: - self._callback(self) - else: - self.cbhdl = simulator.register_value_change_callback(self.signal._handle, _check, self) - - self.cbhdl = simulator.register_value_change_callback(self.signal._handle, _check, self) if self.cbhdl is None: - raise_error(self, "Unable set up %s Trigger" % (str(self))) + self.cbhdl = simulator.register_value_change_callback(self.signal._handle, callback, 1, self) + if self.cbhdl is None: + raise_error(self, "Unable set up %s Trigger" % (str(self))) Trigger.prime(self) def __str__(self): diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index abcc8491..db65664a 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -36,7 +36,7 @@ import cocotb from cocotb.triggers import Timer, Join, RisingEdge, ReadOnly, ReadWrite from cocotb.clock import Clock -from cocotb.result import ReturnValue +from cocotb.result import ReturnValue, TestError @@ -257,3 +257,38 @@ def test_fork_syntax_error(dut): cocotb.fork(syntax_error()) yield clock_gen(dut.clk) + +@cocotb.coroutine +def do_edge_check(dut, level): + """Do test for rising edge""" + old_value = dut.clk.value.integer + if old_value is level: + raise TestError("%s not to start with" % (dut.clk, not level)) + + if level: + yield RisingEdge(dut.clk) + else: + yield FallingEdge(dut.clk) + + new_value = dut.clk.value.integer + dut.log.info("Value was %d" % old_value) + if new_value is not level: + raise TestError("%s not 1 at end" % (dut.clk, level)) + +@cocotb.test() +def test_rising_edge(dut): + """Test that a rising edge can be yielded on""" + test = cocotb.fork(do_edge_check(dut, 1)) + yield Timer(10) + dut.clk <= 1 + yield [Timer(1000), Join(test)] + +@cocotb.test(expect_error=True) +def test_falling_edge(dut): + """Test that a falling edge can be yielded on""" + dut.clk <= 1 + yield Timer(10) + test = cocotb.fork(do_edge_check(dut, 0)) + yield Timer(10) + dut.clk <= 0 + yield [Timer(1000), Join(test)] diff --git a/include/gpi.h b/include/gpi.h index e96d1331..377efb12 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -165,7 +165,7 @@ typedef enum gpi_edge { // The callback registering functions gpi_sim_hdl gpi_register_timed_callback (const int (*gpi_function)(const void *), void *gpi_cb_data, uint64_t time_ps); -gpi_sim_hdl gpi_register_value_change_callback (const int (*gpi_function)(const void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl, int edge); +gpi_sim_hdl gpi_register_value_change_callback (const int (*gpi_function)(const void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl, unsigned int edge); gpi_sim_hdl gpi_register_readonly_callback (const int (*gpi_function)(const void *), void *gpi_cb_data); gpi_sim_hdl gpi_register_nexttime_callback (const int (*gpi_function)(const void *), void *gpi_cb_data); gpi_sim_hdl gpi_register_readwrite_callback (const int (*gpi_function)(const void *), void *gpi_cb_data); diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index fcfefa9e..90a9dda0 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -28,6 +28,8 @@ ******************************************************************************/ #include "gpi_priv.h" +#include +#include #include using namespace std; @@ -197,13 +199,13 @@ void gpi_set_signal_value_str(gpi_sim_hdl sig_hdl, const char *str) gpi_sim_hdl gpi_register_value_change_callback(const int (*gpi_function)(const void *), void *gpi_cb_data, gpi_sim_hdl sig_hdl, - int rising) + unsigned int edge) { GpiSignalObjHdl *signal_hdl = sim_to_hdl(sig_hdl); /* Do something based on int & GPI_RISING | GPI_FALLING */ - GpiCbHdl *gpi_hdl = signal_hdl->value_change_cb(); + GpiCbHdl *gpi_hdl = signal_hdl->value_change_cb(edge); if (!gpi_hdl) { LOG_ERROR("Failed to register a value change callback"); return NULL; diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index a5405ba9..2a902ef7 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -129,7 +129,7 @@ class GpiSignalObjHdl : public GpiObjHdl { // Also think we want the triggers here? virtual GpiCbHdl *rising_edge_cb(void) = 0; virtual GpiCbHdl *falling_edge_cb(void) = 0; - virtual GpiCbHdl *value_change_cb(void) = 0; + virtual GpiCbHdl *value_change_cb(unsigned int edge) = 0; private: //GpiCbHdl value_change_cb; @@ -173,22 +173,6 @@ class GpiClockHdl { int stop_clock(void); }; -class GpiNextTime : public GpiCbHdl { - -}; - -class GpiTimer : public GpiCbHdl { - -}; - -class GpiReadOnly : public GpiCbHdl { - -}; - -class GpiReadWrite : public GpiCbHdl { - -}; - class GpiIterator { public: GpiObjHdl *parent; diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index bc160615..9fb2d33c 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -83,11 +83,11 @@ int handle_gpi_callback(void *user_data) { p_callback_data callback_data_p = (p_callback_data)user_data; - if (callback_data_p->id_value != COCOTB_ACTIVE_ID) { - fprintf(stderr, "Userdata corrupted!\n"); - return 1; - } - callback_data_p->id_value = COCOTB_INACTIVE_ID; + //if (callback_data_p->id_value != COCOTB_ACTIVE_ID) { + // fprintf(stderr, "Userdata corrupted!\n"); + // return 1; + //} + //callback_data_p->id_value = COCOTB_INACTIVE_ID; PyGILState_STATE gstate; @@ -400,7 +400,7 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) PyObject *function; gpi_sim_hdl sig_hdl; gpi_sim_hdl hdl; - int edge; + unsigned int edge; PyGILState_STATE gstate; gstate = TAKE_GIL(); @@ -425,9 +425,12 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) } Py_INCREF(function); + PyObject *pedge = PyTuple_GetItem(args, 2); + edge = (unsigned int)PyLong_AsUnsignedLong(pedge); + // Remaining args for function - if (numargs > 2) - fArgs = PyTuple_GetSlice(args, 2, numargs); // New reference + if (numargs > 3) + fArgs = PyTuple_GetSlice(args, 3, numargs); // New reference else fArgs = PyTuple_New(0); // args must not be NULL, use an empty tuple if no arguments are needed. diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index a156afd1..886a5593 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -58,11 +58,12 @@ int VpiCbHdl::arm_callback(void) { m_impl->reason_to_string(cb_data.reason)); } - if (vpi_hdl != NULL) { + // Only a problem if we have not been asked to deregister and register + // in the same simultion callback + if ((vpi_hdl != NULL) && (m_state != GPI_DELETE)) { fprintf(stderr, "We seem to already be registered, deregistering %s!\n", m_impl->reason_to_string(cb_data.reason)); - cleanup_callback(); } @@ -85,24 +86,27 @@ int VpiCbHdl::arm_callback(void) { int VpiCbHdl::cleanup_callback(void) { int rc; - if (!vpi_hdl) { - LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); - exit(1); - } // If the callback has not been called we also need to call // remove as well - if (m_state == GPI_PRIMED) { + if (m_state == GPI_FREE) + return 0; - rc = vpi_remove_cb(vpi_hdl); - if (!rc) { - check_vpi_error(); - return rc; - } + if (!vpi_hdl) { + LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); + exit(1); } + rc = vpi_remove_cb(vpi_hdl); + check_vpi_error(); + vpi_hdl = NULL; + + if (m_state == GPI_DELETE) + return 0; + m_state = GPI_FREE; + return rc; } @@ -159,28 +163,46 @@ int VpiSignalObjHdl::set_signal_value(std::string &value) return 0; } -GpiCbHdl * VpiSignalObjHdl::value_change_cb(void) +GpiCbHdl * VpiSignalObjHdl::value_change_cb(unsigned int edge) { - value_cb = new VpiValueCbHdl(VpiObjHdl::m_impl, this); + if (!value_cb) + value_cb = new VpiValueCbHdl(VpiObjHdl::m_impl, this, edge); - if (value_cb->arm_callback()) - return NULL; + if (value_cb->arm_callback()) { + delete value_cb; + value_cb = NULL; + } return value_cb; } -VpiValueCbHdl::VpiValueCbHdl(GpiImplInterface *impl, VpiSignalObjHdl *sig) : VpiCbHdl(impl) +VpiValueCbHdl::VpiValueCbHdl(GpiImplInterface *impl, + VpiSignalObjHdl *sig, + unsigned int edge) : VpiCbHdl(impl), + rising(false), + falling(false), + signal(sig) { vpi_time.type = vpiSuppressTime; cb_data.reason = cbValueChange; cb_data.time = &vpi_time; - cb_data.obj = sig->get_handle(); + cb_data.obj = signal->get_handle(); + + + if (edge & 1) + rising = true; + + if (edge & 2) + falling = true; + } int VpiValueCbHdl::cleanup_callback(void) { - //LOG_WARN("Cleanup %p", this); + if (m_state == GPI_FREE) + return 0; + int rc; if (!vpi_hdl) { LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); @@ -191,11 +213,44 @@ int VpiValueCbHdl::cleanup_callback(void) check_vpi_error(); vpi_hdl = NULL; + + if (m_state == GPI_DELETE) + return 0; + m_state = GPI_FREE; return rc; } +int VpiValueCbHdl::run_callback(void) +{ + std::string current_value; + std::string required; + bool pass = false; + if (rising && falling) { + pass = true; + goto check; + } + + current_value = signal->get_signal_value_binstr(); + if (rising && (current_value == "1")) { + pass = true; + goto check; + } + + if (falling && (current_value == "0")) { + pass = true; + goto check; + } + +check: + if (pass) { + this->gpi_function(m_cb_data); + } + + return 1; +} + VpiStartupCbHdl::VpiStartupCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) { cb_data.reason = cbStartOfSimulation; diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index b8a6903b..bc3e8e8c 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -26,8 +26,6 @@ ******************************************************************************/ #include "VpiImpl.h" -#include -#include #include extern "C" { @@ -177,7 +175,6 @@ GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) GpiObjHdl *VpiImpl::get_root_handle(const char* name) { - FENTER vpiHandle root; vpiHandle iterator; VpiObjHdl *rv; @@ -207,7 +204,6 @@ GpiObjHdl *VpiImpl::get_root_handle(const char* name) rv = new VpiObjHdl(this, root); rv->initialise(root_name); - FEXIT return rv; error: @@ -224,15 +220,12 @@ GpiObjHdl *VpiImpl::get_root_handle(const char* name) break; } - FEXIT return NULL; } GpiCbHdl *VpiImpl::register_timed_callback(uint64_t time_ps) { - FENTER - VpiTimedCbHdl *hdl = new VpiTimedCbHdl(this, time_ps); if (hdl->arm_callback()) { @@ -240,66 +233,68 @@ GpiCbHdl *VpiImpl::register_timed_callback(uint64_t time_ps) hdl = NULL; } - FEXIT return hdl; } GpiCbHdl *VpiImpl::register_readwrite_callback(void) { - FENTER - - VpiReadwriteCbHdl *hdl = new VpiReadwriteCbHdl(this); + VpiReadwriteCbHdl *hdl; - if (hdl->arm_callback()) { - delete(hdl); - hdl = NULL; + if (m_read_write) + hdl = reinterpret_cast(m_read_write); + else { + hdl = new VpiReadwriteCbHdl(this); + m_read_write = hdl; } - FEXIT - return hdl; + if (hdl->arm_callback()) + return NULL; + + return m_read_write; } GpiCbHdl *VpiImpl::register_readonly_callback(void) { - FENTER + VpiReadOnlyCbHdl *hdl; - VpiReadOnlyCbHdl *hdl = new VpiReadOnlyCbHdl(this); + if (m_read_only) + hdl = reinterpret_cast(m_read_only); + else { + hdl = new VpiReadOnlyCbHdl(this); + m_read_only = hdl; + } - if (hdl->arm_callback()) { - delete(hdl); + + if (hdl->arm_callback()) hdl = NULL; - } - FEXIT return hdl; } GpiCbHdl *VpiImpl::register_nexttime_callback(void) { - FENTER - - VpiNextPhaseCbHdl *hdl = new VpiNextPhaseCbHdl(this); + VpiNextPhaseCbHdl *hdl; - if (hdl->arm_callback()) { - delete(hdl); - hdl = NULL; + if (m_next_phase) + hdl = reinterpret_cast(m_next_phase); + else { + hdl = new VpiNextPhaseCbHdl(this); + m_next_phase = hdl; } - FEXIT + if (hdl->arm_callback()) + hdl = NULL; + return hdl; } int VpiImpl::deregister_callback(GpiCbHdl *gpi_hdl) { - if (gpi_hdl->get_call_state() == GPI_PRE_CALL) { - //LOG_INFO("Not deleting yet %p", vpi_obj); - return 0; - } - - int rc = gpi_hdl->cleanup_callback(); - // LOG_INFO("DELETING %p", vpi_obj); - delete(gpi_hdl); - return rc; + /* This can only be called from callback context, + we handle the deletion later as we unwind + */ + gpi_hdl->set_call_state(GPI_DELETE); + return 0; } // If the Pything world wants things to shut down then unregister @@ -309,9 +304,11 @@ void VpiImpl::sim_end(void) /* Some sims do not seem to be able to deregister the end of sim callback * so we need to make sure we have tracked this and not call the handler */ - sim_finish_cb->set_call_state(GPI_DELETE); - vpi_control(vpiFinish); - check_vpi_error(); + if (GPI_DELETE != sim_finish_cb->get_call_state()) { + sim_finish_cb->set_call_state(GPI_DELETE); + vpi_control(vpiFinish); + check_vpi_error(); + } } extern "C" { @@ -319,7 +316,6 @@ extern "C" { // Main re-entry point for callbacks from simulator int32_t handle_vpi_callback(p_cb_data cb_data) { - FENTER int rv = 0; VpiCbHdl *cb_hdl = (VpiCbHdl*)cb_data->user_data; @@ -328,14 +324,17 @@ int32_t handle_vpi_callback(p_cb_data cb_data) LOG_CRITICAL("VPI: Callback data corrupted"); if (cb_hdl->get_call_state() == GPI_PRIMED) { + int rearm = 0; cb_hdl->set_call_state(GPI_PRE_CALL); - cb_hdl->run_callback(); - cb_hdl->set_call_state(GPI_POST_CALL); - } + rearm = cb_hdl->run_callback(); + cb_hdl->cleanup_callback(); - gpi_deregister_callback(cb_hdl); + if (cb_hdl->get_call_state() != GPI_DELETE && + rearm) { + cb_hdl->arm_callback(); + } + } - FEXIT return rv; }; @@ -351,24 +350,12 @@ static void register_embed(void) static void register_initial_callback(void) { sim_init_cb = new VpiStartupCbHdl(vpi_table); - - /* We ignore the return value here as VCS does some silly - * things on comilation that means it tries to run through - * the vlog_startup_routines and so call this routine - */ - sim_init_cb->arm_callback(); } static void register_final_callback(void) { sim_finish_cb = new VpiShutdownCbHdl(vpi_table); - - /* We ignore the return value here as VCS does some silly - * things on comilation that means it tries to run through - * the vlog_startup_routines and so call this routine - */ - sim_finish_cb->arm_callback(); } @@ -444,7 +431,6 @@ static int system_function_overload(char *userdata) static void register_system_functions(void) { - FENTER s_vpi_systf_data tfData = { vpiSysTask, vpiSysTask }; tfData.sizetf = NULL; @@ -467,7 +453,6 @@ static void register_system_functions(void) tfData.tfname = "$fatal"; vpi_register_systf( &tfData ); - FEXIT } void (*vlog_startup_routines[])(void) = { diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index af6b0eb6..06a9a4d0 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -74,7 +74,10 @@ static inline int __check_vpi_error(const char *func, long line) class VpiImpl : public GpiImplInterface { public: - VpiImpl(const std::string& name) : GpiImplInterface(name) { } + VpiImpl(const std::string& name) : GpiImplInterface(name), + m_read_write(NULL), + m_next_phase(NULL), + m_read_only(NULL) { } /* Sim related */ void sim_end(void); @@ -94,6 +97,12 @@ class VpiImpl : public GpiImplInterface { GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); const char * reason_to_string(int reason); + +private: + /* Singleton callbacks */ + GpiCbHdl *m_read_write; + GpiCbHdl *m_next_phase; + GpiCbHdl *m_read_only; }; class VpiObjHdl : public GpiObjHdl { @@ -122,7 +131,7 @@ class VpiCbHdl : public GpiCbHdl { virtual int arm_callback(void); virtual int cleanup_callback(void); -public: +protected: vpiHandle vpi_hdl; s_cb_data cb_data; }; @@ -131,11 +140,16 @@ class VpiSignalObjHdl; class VpiValueCbHdl : public VpiCbHdl { public: - VpiValueCbHdl(GpiImplInterface *impl, VpiSignalObjHdl *sig); + VpiValueCbHdl(GpiImplInterface *impl, VpiSignalObjHdl *sig, unsigned int edge); virtual ~VpiValueCbHdl() { } int cleanup_callback(void); + int run_callback(void); private: s_vpi_time vpi_time; + std::string initial_value; + bool rising; + bool falling; + VpiSignalObjHdl *signal; }; class VpiTimedCbHdl : public VpiCbHdl { @@ -187,7 +201,8 @@ class VpiReadwriteCbHdl : public VpiCbHdl { class VpiSignalObjHdl : public VpiObjHdl, public GpiSignalObjHdl { public: VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl) : VpiObjHdl(impl, hdl), - GpiSignalObjHdl(impl) { } + GpiSignalObjHdl(impl), + value_cb(NULL) { } virtual ~VpiSignalObjHdl() { } const char* get_signal_value_binstr(void); @@ -200,7 +215,7 @@ class VpiSignalObjHdl : public VpiObjHdl, public GpiSignalObjHdl { // Also think we want the triggers here? virtual GpiCbHdl *rising_edge_cb(void) { return NULL; } virtual GpiCbHdl *falling_edge_cb(void) { return NULL; } - virtual GpiCbHdl *value_change_cb(void); + virtual GpiCbHdl *value_change_cb(unsigned int edge); /* Functions that I would like to inherit but do not ?*/ virtual GpiObjHdl *get_handle_by_name(std::string &name) { From 2e839e62870df4a8904dafff112a7d375e75c915 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 27 Oct 2014 10:06:25 +0000 Subject: [PATCH 0642/2656] Issue#161: Add tests for Edge Trigger and call function correctly --- cocotb/triggers.py | 2 +- examples/functionality/tests/test_cocotb.py | 42 +++++++++++++++++---- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 20824338..9e4e5a20 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -135,7 +135,7 @@ def __init__(self, signal): def prime(self, callback): """Register notification of a value change via a callback""" - self.cbhdl = simulator.register_value_change_callback(self.signal._handle, callback, self) + self.cbhdl = simulator.register_value_change_callback(self.signal._handle, callback, 3, self) if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) Trigger.prime(self) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index db65664a..bc144598 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -34,7 +34,7 @@ """ import cocotb -from cocotb.triggers import Timer, Join, RisingEdge, ReadOnly, ReadWrite +from cocotb.triggers import Timer, Join, RisingEdge, Edge, ReadOnly, ReadWrite from cocotb.clock import Clock from cocotb.result import ReturnValue, TestError @@ -259,26 +259,25 @@ def test_fork_syntax_error(dut): @cocotb.coroutine -def do_edge_check(dut, level): +def do_single_edge_check(dut, level): """Do test for rising edge""" old_value = dut.clk.value.integer + dut.log.info("Value of %s is %d" % (dut.clk, old_value)) if old_value is level: raise TestError("%s not to start with" % (dut.clk, not level)) - if level: yield RisingEdge(dut.clk) else: yield FallingEdge(dut.clk) - new_value = dut.clk.value.integer - dut.log.info("Value was %d" % old_value) + dut.log.info("Value of %s is %d" % (dut.clk, new_value)) if new_value is not level: raise TestError("%s not 1 at end" % (dut.clk, level)) @cocotb.test() def test_rising_edge(dut): """Test that a rising edge can be yielded on""" - test = cocotb.fork(do_edge_check(dut, 1)) + test = cocotb.fork(do_single_edge_check(dut, 1)) yield Timer(10) dut.clk <= 1 yield [Timer(1000), Join(test)] @@ -288,7 +287,36 @@ def test_falling_edge(dut): """Test that a falling edge can be yielded on""" dut.clk <= 1 yield Timer(10) - test = cocotb.fork(do_edge_check(dut, 0)) + test = cocotb.fork(do_single_edge_check(dut, 0)) yield Timer(10) dut.clk <= 0 yield [Timer(1000), Join(test)] + +@cocotb.coroutine +def do_either_edge_test(dut): + """Run do either edge test""" + yield Edge(dut.clk) + dut.log.info("Value of %s is %d" % (dut.clk, dut.clk.value.integer)) + if dut.clk.value.integer is not 1: + raise TestError("Value should be 0") + yield Edge(dut.clk) + dut.log.info("Value of %s is %d" % (dut.clk, dut.clk.value.integer)) + if dut.clk.value.integer is not 0: + raise TestError("Value should be 1") + yield Timer(100) + +@cocotb.test() +def test_either_edge(dut): + """Test that either edge can be triggered on""" + dut.clk <= 0 + yield Timer(1) + test = cocotb.fork(do_either_edge_test(dut)) + yield Timer(11) + dut.clk <= 1 + yield Timer(11) + dut.clk <= 0 + fail_timer = Timer(1000) + result = yield [fail_timer, test.join()] + if result is fail_timer: + raise TestError("Test timed out") + From e8e2aad3e4eb8eb4a4612ffa08a276cc0ac0a71b Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 28 Oct 2014 09:22:48 +0000 Subject: [PATCH 0643/2656] Issue#161: Fix up some long running failures in edge trigger from C++ --- cocotb/scheduler.py | 1 + cocotb/triggers.py | 29 ++++++++------- examples/functionality/tests/test_cocotb.py | 2 +- lib/gpi/gpi_priv.h | 4 +-- lib/vpi/VpiCbHdl.cpp | 40 ++++++--------------- lib/vpi/VpiImpl.cpp | 27 +++++++------- lib/vpi/VpiImpl.h | 2 +- 7 files changed, 46 insertions(+), 59 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index bab0bfb2..08f311c2 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -325,6 +325,7 @@ def react(self, trigger, depth=0): for others in self._trigger2coros[pending]: if others not in scheduling: break else: + #if pending is not trigger and pending.primed: pending.unprime() if pending.primed: pending.unprime() del self._trigger2coros[pending] diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 9e4e5a20..a4911039 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -48,7 +48,6 @@ def __init__(self): self.log = SimLog("cocotb.%s" % (self.__class__.__name__), id(self)) self.signal = None self.primed = False - self.registered = False def prime(self, *args): self.primed = True @@ -56,7 +55,6 @@ def prime(self, *args): def unprime(self): """Remove any pending callbacks if necessary""" self.primed = False - self.log.debug("Unprimed") def __del__(self): """Ensure if a trigger drops out of scope we remove any pending callbacks""" @@ -117,9 +115,10 @@ def __init__(self, time_ps): def prime(self, callback): """Register for a timed callback""" - self.cbhdl = simulator.register_timed_callback(self.time_ps, callback, self) if self.cbhdl is None: - raise_error(self, "Unable set up %s Trigger" % (str(self))) + self.cbhdl = simulator.register_timed_callback(self.time_ps, callback, self) + if self.cbhdl is None: + raise_error(self, "Unable set up %s Trigger" % (str(self))) Trigger.prime(self) def __str__(self): @@ -135,9 +134,10 @@ def __init__(self, signal): def prime(self, callback): """Register notification of a value change via a callback""" - self.cbhdl = simulator.register_value_change_callback(self.signal._handle, callback, 3, self) if self.cbhdl is None: - raise_error(self, "Unable set up %s Trigger" % (str(self))) + self.cbhdl = simulator.register_value_change_callback(self.signal._handle, callback, 3, self) + if self.cbhdl is None: + raise_error(self, "Unable set up %s Trigger" % (str(self))) Trigger.prime(self) def __str__(self): @@ -152,9 +152,10 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): - self.cbhdl = simulator.register_readonly_callback(callback, self) if self.cbhdl is None: - raise_error(self, "Unable set up %s Trigger" % (str(self))) + self.cbhdl = simulator.register_readonly_callback(callback, self) + if self.cbhdl is None: + raise_error(self, "Unable set up %s Trigger" % (str(self))) Trigger.prime(self) def __str__(self): @@ -173,9 +174,12 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): - self.cbhdl = simulator.register_rwsynch_callback(callback, self) if self.cbhdl is None: - raise_error(self, "Unable set up %s Trigger" % (str(self))) + #import pdb + #pdb.set_trace() + self.cbhdl = simulator.register_rwsynch_callback(callback, self) + if self.cbhdl is None: + raise_error(self, "Unable set up %s Trigger" % (str(self))) Trigger.prime(self) def __str__(self): @@ -193,9 +197,10 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): - self.cbhdl = simulator.register_nextstep_callback(callback, self) if self.cbhdl is None: - raise_error(self, "Unable set up %s Trigger" % (str(self))) + self.cbhdl = simulator.register_nextstep_callback(callback, self) + if self.cbhdl is None: + raise_error(self, "Unable set up %s Trigger" % (str(self))) Trigger.prime(self) def __str__(self): diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index bc144598..4d4ed798 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -188,7 +188,7 @@ def test_afterdelay_in_readonly(dut): exited = False clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) coro = cocotb.fork(do_test_afterdelay_in_readonly(dut, 0)) - yield [Join(coro), Timer(100000)] + yield [Join(coro), Timer(1000)] clk_gen.kill() if exited is not True: raise cocotb.TestFailed diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 2a902ef7..5914538e 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -36,8 +36,8 @@ typedef enum gpi_cb_state { GPI_FREE = 0, GPI_PRIMED = 1, - GPI_PRE_CALL = 2, - GPI_POST_CALL = 3, + GPI_CALL = 2, + GPI_REPRIME = 3, GPI_DELETE = 4, } gpi_cb_state_e; diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 886a5593..961b0f0f 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -52,6 +52,7 @@ VpiCbHdl::VpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), * before getting the new one */ int VpiCbHdl::arm_callback(void) { + if (m_state == GPI_PRIMED) { fprintf(stderr, "Attempt to prime an already primed trigger for %s!\n", @@ -60,7 +61,7 @@ int VpiCbHdl::arm_callback(void) { // Only a problem if we have not been asked to deregister and register // in the same simultion callback - if ((vpi_hdl != NULL) && (m_state != GPI_DELETE)) { + if (vpi_hdl != NULL && m_state != GPI_DELETE) { fprintf(stderr, "We seem to already be registered, deregistering %s!\n", m_impl->reason_to_string(cb_data.reason)); @@ -89,6 +90,7 @@ int VpiCbHdl::cleanup_callback(void) // If the callback has not been called we also need to call // remove as well + if (m_state == GPI_FREE) return 0; @@ -101,10 +103,6 @@ int VpiCbHdl::cleanup_callback(void) check_vpi_error(); vpi_hdl = NULL; - - if (m_state == GPI_DELETE) - return 0; - m_state = GPI_FREE; return rc; @@ -198,30 +196,6 @@ VpiValueCbHdl::VpiValueCbHdl(GpiImplInterface *impl, } -int VpiValueCbHdl::cleanup_callback(void) -{ - if (m_state == GPI_FREE) - return 0; - - int rc; - if (!vpi_hdl) { - LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); - exit(1); - } - - rc = vpi_remove_cb(vpi_hdl); - check_vpi_error(); - - vpi_hdl = NULL; - - if (m_state == GPI_DELETE) - return 0; - - m_state = GPI_FREE; - - return rc; -} - int VpiValueCbHdl::run_callback(void) { std::string current_value; @@ -245,10 +219,16 @@ int VpiValueCbHdl::run_callback(void) check: if (pass) { + //LOG_WARN("Running Value change passup"); this->gpi_function(m_cb_data); + } else { + //LOG_WARN("Running Value change NO passup"); + //set_call_state(GPI_REPRIME); + cleanup_callback(); + arm_callback(); } - return 1; + return 0; } VpiStartupCbHdl::VpiStartupCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index bc3e8e8c..e6009bbc 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -290,10 +290,8 @@ GpiCbHdl *VpiImpl::register_nexttime_callback(void) int VpiImpl::deregister_callback(GpiCbHdl *gpi_hdl) { - /* This can only be called from callback context, - we handle the deletion later as we unwind - */ - gpi_hdl->set_call_state(GPI_DELETE); + gpi_hdl->cleanup_callback(); + //gpi_hdl->set_call_state(GPI_DELETE); return 0; } @@ -323,16 +321,19 @@ int32_t handle_vpi_callback(p_cb_data cb_data) if (!cb_hdl) LOG_CRITICAL("VPI: Callback data corrupted"); - if (cb_hdl->get_call_state() == GPI_PRIMED) { - int rearm = 0; - cb_hdl->set_call_state(GPI_PRE_CALL); - rearm = cb_hdl->run_callback(); - cb_hdl->cleanup_callback(); + gpi_cb_state_e old_state = cb_hdl->get_call_state(); + + if (old_state == GPI_PRIMED) { + + cb_hdl->set_call_state(GPI_CALL); + cb_hdl->run_callback(); + + gpi_cb_state_e new_state = cb_hdl->get_call_state(); + + /* We have re-primed in the handler */ + if (new_state != GPI_PRIMED) + cb_hdl->cleanup_callback(); - if (cb_hdl->get_call_state() != GPI_DELETE && - rearm) { - cb_hdl->arm_callback(); - } } return rv; diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 06a9a4d0..6389e698 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -142,7 +142,7 @@ class VpiValueCbHdl : public VpiCbHdl { public: VpiValueCbHdl(GpiImplInterface *impl, VpiSignalObjHdl *sig, unsigned int edge); virtual ~VpiValueCbHdl() { } - int cleanup_callback(void); + //int cleanup_callback(void); int run_callback(void); private: s_vpi_time vpi_time; From 5b6b17a3a2258bfb9fead649c78cc7cb37341484 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 28 Oct 2014 09:52:31 +0000 Subject: [PATCH 0644/2656] Issue#161: Change implementation callbacks to be allocated inside the implementation --- lib/vpi/VpiCbHdl.cpp | 19 ++++++----- lib/vpi/VpiImpl.cpp | 45 +++++--------------------- lib/vpi/VpiImpl.h | 75 +++++++++++++++++++++++--------------------- 3 files changed, 57 insertions(+), 82 deletions(-) diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 961b0f0f..e8620da2 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -163,20 +163,18 @@ int VpiSignalObjHdl::set_signal_value(std::string &value) GpiCbHdl * VpiSignalObjHdl::value_change_cb(unsigned int edge) { - if (!value_cb) - value_cb = new VpiValueCbHdl(VpiObjHdl::m_impl, this, edge); + m_value_cb.set_edge(edge); - if (value_cb->arm_callback()) { - delete value_cb; - value_cb = NULL; + if (m_value_cb.arm_callback()) { + return NULL; } - return value_cb; + return &m_value_cb; } VpiValueCbHdl::VpiValueCbHdl(GpiImplInterface *impl, - VpiSignalObjHdl *sig, - unsigned int edge) : VpiCbHdl(impl), + VpiSignalObjHdl *sig) : + VpiCbHdl(impl), rising(false), falling(false), signal(sig) @@ -186,14 +184,15 @@ VpiValueCbHdl::VpiValueCbHdl(GpiImplInterface *impl, cb_data.reason = cbValueChange; cb_data.time = &vpi_time; cb_data.obj = signal->get_handle(); +} - +void VpiValueCbHdl::set_edge(unsigned int edge) +{ if (edge & 1) rising = true; if (edge & 2) falling = true; - } int VpiValueCbHdl::run_callback(void) diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index e6009bbc..3e335a13 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -238,60 +238,31 @@ GpiCbHdl *VpiImpl::register_timed_callback(uint64_t time_ps) GpiCbHdl *VpiImpl::register_readwrite_callback(void) { - VpiReadwriteCbHdl *hdl; - - if (m_read_write) - hdl = reinterpret_cast(m_read_write); - else { - hdl = new VpiReadwriteCbHdl(this); - m_read_write = hdl; - } - - if (hdl->arm_callback()) + if (m_read_write.arm_callback()) return NULL; - return m_read_write; + return &m_read_write; } GpiCbHdl *VpiImpl::register_readonly_callback(void) { - VpiReadOnlyCbHdl *hdl; - - if (m_read_only) - hdl = reinterpret_cast(m_read_only); - else { - hdl = new VpiReadOnlyCbHdl(this); - m_read_only = hdl; - } - - - if (hdl->arm_callback()) - hdl = NULL; + if (m_read_only.arm_callback()) + return NULL; - return hdl; + return &m_read_only; } GpiCbHdl *VpiImpl::register_nexttime_callback(void) { - VpiNextPhaseCbHdl *hdl; - - if (m_next_phase) - hdl = reinterpret_cast(m_next_phase); - else { - hdl = new VpiNextPhaseCbHdl(this); - m_next_phase = hdl; - } - - if (hdl->arm_callback()) - hdl = NULL; + if (m_next_phase.arm_callback()) + return NULL; - return hdl; + return &m_next_phase; } int VpiImpl::deregister_callback(GpiCbHdl *gpi_hdl) { gpi_hdl->cleanup_callback(); - //gpi_hdl->set_call_state(GPI_DELETE); return 0; } diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 6389e698..bd4712dc 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -72,38 +72,9 @@ static inline int __check_vpi_error(const char *func, long line) __check_vpi_error(__func__, __LINE__); \ } while (0) -class VpiImpl : public GpiImplInterface { -public: - VpiImpl(const std::string& name) : GpiImplInterface(name), - m_read_write(NULL), - m_next_phase(NULL), - m_read_only(NULL) { } - - /* Sim related */ - void sim_end(void); - void get_sim_time(uint32_t *high, uint32_t *low); - - /* Hierachy related */ - GpiObjHdl *get_root_handle(const char *name); - - /* Callback related, these may (will) return the same handle*/ - GpiCbHdl *register_timed_callback(uint64_t time_ps); - GpiCbHdl *register_readonly_callback(void); - GpiCbHdl *register_nexttime_callback(void); - GpiCbHdl *register_readwrite_callback(void); - int deregister_callback(GpiCbHdl *obj_hdl); - bool native_check(std::string &name, GpiObjHdl *parent); - GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); - GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); - - const char * reason_to_string(int reason); - -private: - /* Singleton callbacks */ - GpiCbHdl *m_read_write; - GpiCbHdl *m_next_phase; - GpiCbHdl *m_read_only; -}; +class VpiReadwriteCbHdl; +class VpiNextPhaseCbHdl; +class VpiReadOnlyCbHdl; class VpiObjHdl : public GpiObjHdl { public: @@ -140,10 +111,11 @@ class VpiSignalObjHdl; class VpiValueCbHdl : public VpiCbHdl { public: - VpiValueCbHdl(GpiImplInterface *impl, VpiSignalObjHdl *sig, unsigned int edge); + VpiValueCbHdl(GpiImplInterface *impl, VpiSignalObjHdl *sig); virtual ~VpiValueCbHdl() { } //int cleanup_callback(void); int run_callback(void); + void set_edge(unsigned int edge); private: s_vpi_time vpi_time; std::string initial_value; @@ -202,7 +174,7 @@ class VpiSignalObjHdl : public VpiObjHdl, public GpiSignalObjHdl { public: VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl) : VpiObjHdl(impl, hdl), GpiSignalObjHdl(impl), - value_cb(NULL) { } + m_value_cb(impl, this) { } virtual ~VpiSignalObjHdl() { } const char* get_signal_value_binstr(void); @@ -236,7 +208,40 @@ class VpiSignalObjHdl : public VpiObjHdl, public GpiSignalObjHdl { return VpiObjHdl::initialise(name); } private: - VpiValueCbHdl *value_cb; + VpiValueCbHdl m_value_cb; +}; + +class VpiImpl : public GpiImplInterface { +public: + VpiImpl(const std::string& name) : GpiImplInterface(name), + m_read_write(this), + m_next_phase(this), + m_read_only(this) { } + + /* Sim related */ + void sim_end(void); + void get_sim_time(uint32_t *high, uint32_t *low); + + /* Hierachy related */ + GpiObjHdl *get_root_handle(const char *name); + + /* Callback related, these may (will) return the same handle*/ + GpiCbHdl *register_timed_callback(uint64_t time_ps); + GpiCbHdl *register_readonly_callback(void); + GpiCbHdl *register_nexttime_callback(void); + GpiCbHdl *register_readwrite_callback(void); + int deregister_callback(GpiCbHdl *obj_hdl); + bool native_check(std::string &name, GpiObjHdl *parent); + GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); + GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); + + const char * reason_to_string(int reason); + +private: + /* Singleton callbacks */ + VpiReadwriteCbHdl m_read_write; + VpiNextPhaseCbHdl m_next_phase; + VpiReadOnlyCbHdl m_read_only; }; #endif /*COCOTB_VPI_IMPL_H_ */ \ No newline at end of file From dc17ba01080faaec6fb57acd9660d1755b60549c Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 28 Oct 2014 10:08:04 +0000 Subject: [PATCH 0645/2656] Issue#161: Cleanup timers --- lib/vpi/VpiImpl.cpp | 3 ++- lib/vpi/VpiImpl.h | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 3e335a13..0f311532 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -303,7 +303,8 @@ int32_t handle_vpi_callback(p_cb_data cb_data) /* We have re-primed in the handler */ if (new_state != GPI_PRIMED) - cb_hdl->cleanup_callback(); + if (cb_hdl->cleanup_callback()) + delete cb_hdl; } diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index bd4712dc..df027056 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -128,6 +128,10 @@ class VpiTimedCbHdl : public VpiCbHdl { public: VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps); virtual ~VpiTimedCbHdl() { } + int cleanup_callback() { + VpiCbHdl::cleanup_callback(); + return 1; + } private: s_vpi_time vpi_time; }; From 29c2929dbbb37b5bd008051842468ea157e2d797 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 28 Oct 2014 17:14:34 +0000 Subject: [PATCH 0646/2656] Issue#161: Fix hanging reference to args on callbacks that lead to handle leakage in python, and couple of cleanups for turning on -O2 --- lib/gpi/GpiCommon.cpp | 2 +- lib/simulator/simulatormodule.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 90a9dda0..49ad3959 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -127,7 +127,7 @@ gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) { vector::iterator iter; - GpiObjHdl *hdl; + GpiObjHdl *hdl = NULL; GpiObjHdl *base = sim_to_hdl(parent); LOG_WARN("Trying index"); diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 9fb2d33c..6041809b 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -83,11 +83,11 @@ int handle_gpi_callback(void *user_data) { p_callback_data callback_data_p = (p_callback_data)user_data; - //if (callback_data_p->id_value != COCOTB_ACTIVE_ID) { - // fprintf(stderr, "Userdata corrupted!\n"); - // return 1; - //} - //callback_data_p->id_value = COCOTB_INACTIVE_ID; + if (callback_data_p->id_value != COCOTB_ACTIVE_ID) { + fprintf(stderr, "Userdata corrupted!\n"); + return 1; + } + callback_data_p->id_value = COCOTB_INACTIVE_ID; PyGILState_STATE gstate; From 72ef109b8917ab9b7aa0eec705002c4d728f4fb7 Mon Sep 17 00:00:00 2001 From: Chris Higgs Date: Tue, 28 Oct 2014 17:22:28 +0000 Subject: [PATCH 0647/2656] Issue #161: Add support for getting parameter values via VPI --- include/vpi_user.h | 1 + lib/vpi/VpiImpl.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/vpi_user.h b/include/vpi_user.h index 5cd9cd68..d7daa02d 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -47,6 +47,7 @@ typedef uint32_t *vpiHandle; #define vpiNetBit 37 /* bit of a vector net */ #define vpiReg 48 /* scalar or vector reg */ #define vpiRegBit 49 /* bit of vector reg */ +#define vpiParameter 41 /* module parameter */ #define vpiStructVar 618 #define vpiStop 66 /* execute simulator's $stop */ diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 0f311532..eaf7ad37 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -118,6 +118,7 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) switch (type) { case vpiNet: case vpiReg: + case vpiParameter: new_obj = new VpiSignalObjHdl(this, new_hdl); LOG_DEBUG("Created VpiSignalObjHdl"); break; @@ -449,4 +450,4 @@ void vlog_startup_routines_bootstrap(void) { } } -} \ No newline at end of file +} From 9baffe032962672ecc84d14d429a5afa16163334 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 29 Oct 2014 09:22:31 +0000 Subject: [PATCH 0648/2656] Issue#161: Some small tidyups --- lib/gpi/gpi_priv.h | 4 ++-- lib/vpi/VpiCbHdl.cpp | 18 +++++++++--------- lib/vpi/VpiImpl.h | 10 ++++++++++ 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 5914538e..d5c35499 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -169,8 +169,8 @@ class GpiClockHdl { GpiClockHdl(GpiObjHdl *clk) { } GpiClockHdl(const char *clk) { } ~GpiClockHdl() { } - int start_clock(const int period_ps); /* Do things with the GpiSignalObjHdl */ - int stop_clock(void); + int start_clock(const int period_ps) { return 0; } ; /* Do things with the GpiSignalObjHdl */ + int stop_clock(void) { return 0; } }; class GpiIterator { diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index e8620da2..1946bb5e 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -76,21 +76,17 @@ int VpiCbHdl::arm_callback(void) { m_impl->reason_to_string(cb_data.reason), cb_data.reason); check_vpi_error(); ret = -1; + } else { + m_state = GPI_PRIMED; } - + vpi_hdl = new_hdl; - m_state = GPI_PRIMED; return ret; } int VpiCbHdl::cleanup_callback(void) { - int rc; - - // If the callback has not been called we also need to call - // remove as well - if (m_state == GPI_FREE) return 0; @@ -99,13 +95,17 @@ int VpiCbHdl::cleanup_callback(void) exit(1); } - rc = vpi_remove_cb(vpi_hdl); + if (!(vpi_remove_cb(vpi_hdl))) { + LOG_CRITICAL("VPI: unbale to remove callback : ABORTING"); + exit(1); + } + check_vpi_error(); vpi_hdl = NULL; m_state = GPI_FREE; - return rc; + return 0; } const char* VpiSignalObjHdl::get_signal_value_binstr(void) diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index df027056..3f0f3af0 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -58,6 +58,8 @@ static inline int __check_vpi_error(const char *func, long line) case vpiInternal: loglevel = GPICritical; break; + default: + return level; } gpi_log("cocotb.gpi", loglevel, __FILE__, func, line, @@ -156,6 +158,10 @@ class VpiStartupCbHdl : public VpiCbHdl { public: VpiStartupCbHdl(GpiImplInterface *impl); int run_callback(void); + int cleanup_callback() { + /* Too many sims get upset with this so we override to do nothing */ + return 0; + } virtual ~VpiStartupCbHdl() { } }; @@ -163,6 +169,10 @@ class VpiShutdownCbHdl : public VpiCbHdl { public: VpiShutdownCbHdl(GpiImplInterface *impl); int run_callback(void); + int cleanup_callback() { + /* Too many sims get upset with this so we override to do nothing */ + return 0; + } virtual ~VpiShutdownCbHdl() { } }; From 760c4a7520bf4b1cbc9ab54956554d664421aa4b Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 30 Oct 2014 11:37:09 +0000 Subject: [PATCH 0649/2656] Cleanup: Fix format error when printing a test failure --- cocotb/regression.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index eacdd101..71568709 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -104,7 +104,7 @@ def initialise(self): skip = test.skip except TestError: skip = True - self.log.warning("Failed to initialise test%s" % thing.name) + self.log.warning("Failed to initialise test %s" % thing.name) if skip: self.log.info("Skipping test %s" % thing.name) From d924bf60d1aa25b0c2d68df112cd57cf1f288747 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 30 Oct 2014 11:48:57 +0000 Subject: [PATCH 0650/2656] Issue#161: Bring VHPI closer to VPI. Edge change not pushed down for VHPI yet --- .../functionality/tests/test_discovery.py | 8 +- lib/embed/gpi_embed.c | 11 +++ lib/vhpi/VhpiCbHdl.cpp | 58 ++++++++++- lib/vhpi/VhpiImpl.cpp | 98 ++++++++++++++++--- lib/vhpi/VhpiImpl.h | 90 ++++++++++------- lib/vpi/VpiImpl.h | 1 - 6 files changed, 209 insertions(+), 57 deletions(-) diff --git a/examples/functionality/tests/test_discovery.py b/examples/functionality/tests/test_discovery.py index 8a407be2..8fe8b54c 100644 --- a/examples/functionality/tests/test_discovery.py +++ b/examples/functionality/tests/test_discovery.py @@ -47,7 +47,7 @@ def discover_value_not_in_dut(dut): @cocotb.test() def access_signal(dut): """Access a signal using the assignment mechanism""" - dut.stream_in_data = 1 + dut.stream_in_data.setimmediatevalue(1) yield Timer(10) if dut.stream_in_data.value.integer != 1: raise TestError("%s.%s != %d" % ( @@ -56,7 +56,8 @@ def access_signal(dut): -@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) +@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"], + skip=cocotb.LANGUAGE in ["vhdl"]) def access_single_bit(dut): """ Access a single bit in a vector of the dut @@ -73,7 +74,8 @@ def access_single_bit(dut): (str(dut.stream_out_data_comb), dut.stream_out_data_comb.value.integer, (1<<2))) -@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) +@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"], + skip=cocotb.LANGUAGE in ["vhdl"]) def access_single_bit_assignment(dut): """ Access a single bit in a vector of the dut using the assignment mechanism diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index fa9d0272..63fa4847 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -216,6 +216,17 @@ void embed_sim_init(gpi_sim_info_t *info) goto cleanup; } + // Set languare in use + const char *lang = getenv("LANGUAGE"); + if (!lang) + fprintf(stderr, "You should really set LANGUAGE to \"verilog/vhdl\""); + else { + if (-1 == PyObject_SetAttrString(cocotb_module, "LANGUAGE", PyString_FromString(lang))) { + fprintf(stderr, "Unable to set LANGUAGE"); + goto cleanup; + } + } + // Hold onto a reference to our _fail_test function pEventFn = PyObject_GetAttrString(cocotb_module, "_sim_event"); diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 041f36a5..fd95c36b 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -95,6 +95,8 @@ int VhpiSignalObjHdl::initialise(std::string &name) { LOG_CRITICAL("Unable to alloc mem for read buffer"); } + VhpiObjHdl::initialise(name); + return 0; } @@ -111,9 +113,15 @@ VhpiCbHdl::VhpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), int VhpiCbHdl::cleanup_callback(void) { + if (m_state == GPI_FREE) + return 0; + vhpiStateT cbState = (vhpiStateT)vhpi_get(vhpiStateP, vhpi_hdl); - if (vhpiMature == cbState) - return vhpi_remove_cb(vhpi_hdl); + if (vhpiMature != cbState) + vhpi_remove_cb(vhpi_hdl); + + vhpi_hdl = NULL; + m_state = GPI_FREE; return 0; } @@ -244,6 +252,23 @@ const char* VhpiSignalObjHdl::get_signal_value_binstr(void) return m_binvalue.value.str; } +GpiCbHdl * VhpiSignalObjHdl::value_change_cb(unsigned int edge) +{ + value_cb = new VhpiValueCbHdl(VhpiObjHdl::m_impl, this); + + if (value_cb->arm_callback()) + return NULL; + + return value_cb; +} + +VhpiValueCbHdl::VhpiValueCbHdl(GpiImplInterface *impl, VhpiSignalObjHdl *sig) : VhpiCbHdl(impl) +{ + cb_data.reason = vhpiCbValueChange; + cb_data.time = &vhpi_time; + cb_data.obj = sig->get_handle(); +} + VhpiStartupCbHdl::VhpiStartupCbHdl(GpiImplInterface *impl) : VhpiCbHdl(impl) { cb_data.reason = vhpiCbStartOfSimulation; @@ -269,7 +294,7 @@ VhpiShutdownCbHdl::VhpiShutdownCbHdl(GpiImplInterface *impl) : VhpiCbHdl(impl) } int VhpiShutdownCbHdl::run_callback(void) { - //LOG_WARN("Shutdown called"); + set_call_state(GPI_DELETE); gpi_embed_end(); return 0; } @@ -281,4 +306,31 @@ VhpiTimedCbHdl::VhpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : VhpiC cb_data.reason = vhpiCbAfterDelay; cb_data.time = &vhpi_time; +} + +VhpiReadwriteCbHdl::VhpiReadwriteCbHdl(GpiImplInterface *impl) : VhpiCbHdl(impl) +{ + vhpi_time.high = 0; + vhpi_time.low = 0; + + cb_data.reason = vhpiCbEndOfProcesses; + cb_data.time = &vhpi_time; +} + +VhpiReadOnlyCbHdl::VhpiReadOnlyCbHdl(GpiImplInterface *impl) : VhpiCbHdl(impl) +{ + vhpi_time.high = 0; + vhpi_time.low = 0; + + cb_data.reason = vhpiCbLastKnownDeltaCycle; + cb_data.time = &vhpi_time; +} + +VhpiNextPhaseCbHdl::VhpiNextPhaseCbHdl(GpiImplInterface *impl) : VhpiCbHdl(impl) +{ + vhpi_time.high = 0; + vhpi_time.low = 0; + + cb_data.reason = vhpiCbNextTimeStep; + cb_data.time = &vhpi_time; } \ No newline at end of file diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 614df0fd..f6386f59 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -158,7 +158,6 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) } LOG_DEBUG("Type was %d", type); - /* Might move the object creation inside here */ new_obj->initialise(name); return new_obj; @@ -166,7 +165,45 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) GpiObjHdl *VhpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) { - return NULL; + vhpiIntT type; + VhpiObjHdl *parent_hdl = sim_to_hdl(parent); + vhpiHandleT vpi_hdl = parent_hdl->get_handle(); + vhpiHandleT new_hdl; + VhpiObjHdl *new_obj = NULL; + + new_hdl = vhpi_handle_by_index(vhpiIndexedNames, vpi_hdl, index); + check_vhpi_error(); + + if (!new_hdl) + return NULL; + + if (vhpiVerilog == (type = vhpi_get(vhpiKindP, new_hdl))) { + vpi_free_object(vpi_hdl); + LOG_DEBUG("Not a VHPI object"); + return NULL; + } + + /* What sort of isntance is this ?*/ + switch (type) { + case vhpiIndexedNameK: + new_obj = new VhpiSignalObjHdl(this, new_hdl); + LOG_DEBUG("Created VhpiSignalObjHdl"); + break; + case vhpiCompInstStmtK: + new_obj = new VhpiObjHdl(this, new_hdl); + LOG_DEBUG("Created VhpiObjHdl"); + break; + default: + LOG_CRITICAL("Not sure what to do with type %d below entity (%s) at index (%d)", + type, parent->get_name_str(), index); + return NULL; + } + + LOG_DEBUG("Type was %d", type); + std::string name = vhpi_get_str(vhpiNameP, new_hdl); + new_obj->initialise(name); + + return new_obj; } GpiObjHdl *VhpiImpl::get_root_handle(const char* name) @@ -227,9 +264,39 @@ GpiCbHdl *VhpiImpl::register_timed_callback(uint64_t time_ps) return hdl; } +GpiCbHdl *VhpiImpl::register_readwrite_callback(void) +{ + if (m_read_write.arm_callback()) + return NULL; + + return &m_read_write; +} + +GpiCbHdl *VhpiImpl::register_readonly_callback(void) +{ + if (m_read_only.arm_callback()) + return NULL; + + return &m_read_only; +} + +GpiCbHdl *VhpiImpl::register_nexttime_callback(void) +{ + if (m_next_phase.arm_callback()) + return NULL; + + return &m_next_phase; +} + +int VhpiImpl::deregister_callback(GpiCbHdl *gpi_hdl) +{ + gpi_hdl->cleanup_callback(); + return 0; +} + void VhpiImpl::sim_end(void) { - sim_finish_cb = NULL; + sim_finish_cb->set_call_state(GPI_DELETE); vhpi_control(vhpiFinish); check_vhpi_error(); } @@ -239,29 +306,33 @@ extern "C" { // Main entry point for callbacks from simulator void handle_vhpi_callback(const vhpiCbDataT *cb_data) { - FENTER - VhpiCbHdl *cb_hdl = (VhpiCbHdl*)cb_data->user_data; if (!cb_hdl) - LOG_CRITICAL("VPI: Callback data corrupted"); + LOG_CRITICAL("VHPI: Callback data corrupted"); + + gpi_cb_state_e old_state = cb_hdl->get_call_state(); - if (cb_hdl->get_call_state() == GPI_PRIMED) { - cb_hdl->set_call_state(GPI_PRE_CALL); + if (old_state == GPI_PRIMED) { + + cb_hdl->set_call_state(GPI_CALL); cb_hdl->run_callback(); - cb_hdl->set_call_state(GPI_POST_CALL); - } - gpi_deregister_callback(cb_hdl); + gpi_cb_state_e new_state = cb_hdl->get_call_state(); + + /* We have re-primed in the handler */ + if (new_state != GPI_PRIMED) + if (cb_hdl->cleanup_callback()) + delete cb_hdl; + + } - FEXIT return; }; static void register_initial_callback(void) { FENTER - LOG_WARN("Initial callback registering"); sim_init_cb = new VhpiStartupCbHdl(vhpi_table); sim_init_cb->arm_callback(); FEXIT @@ -277,7 +348,6 @@ static void register_final_callback(void) static void register_embed(void) { - printf("%s %d Registered VHPI \n", __func__, __LINE__); vhpi_table = new VhpiImpl("VHPI"); gpi_register_impl(vhpi_table); gpi_embed_init_python(); diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 14c4e707..2e5f494a 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -73,31 +73,6 @@ static inline int __check_vhpi_error(const char *func, long line) __check_vhpi_error(__func__, __LINE__); \ } while (0) -class VhpiImpl : public GpiImplInterface { -public: - VhpiImpl(const std::string& name) : GpiImplInterface(name) { } - - /* Sim related */ - void sim_end(void); - void get_sim_time(uint32_t *high, uint32_t *low); - - /* Hierachy related */ - GpiObjHdl *get_root_handle(const char *name); - - /* Callback related, these may (will) return the same handle*/ - GpiCbHdl *register_timed_callback(uint64_t time_ps); - GpiCbHdl *register_readonly_callback(void) { return NULL; } - GpiCbHdl *register_nexttime_callback(void) { return NULL; } - GpiCbHdl *register_readwrite_callback(void); - int deregister_callback(GpiCbHdl *obj_hdl) { return 0; } - bool native_check(std::string &name, GpiObjHdl *parent) { return false; } - GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); - GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); - - const char * reason_to_string(int reason); - const char * format_to_string(int format); -}; - class VhpiObjHdl : public GpiObjHdl { public: VhpiObjHdl(GpiImplInterface *impl, vhpiHandleT hdl) : GpiObjHdl(impl), @@ -136,7 +111,6 @@ class VhpiValueCbHdl : public VhpiCbHdl { public: VhpiValueCbHdl(GpiImplInterface *impl, VhpiSignalObjHdl *sig); virtual ~VhpiValueCbHdl() { } - int cleanup_callback(void); private: vhpiTimeT vhpi_time; }; @@ -149,24 +123,30 @@ class VhpiTimedCbHdl : public VhpiCbHdl { vhpiTimeT vhpi_time; }; -#if 0 class VhpiReadOnlyCbHdl : public VhpiCbHdl { - -}; - -class VhpiReadWriteCbHdl : public VhpiCbHdl { - +public: + VhpiReadOnlyCbHdl(GpiImplInterface *impl); + virtual ~VhpiReadOnlyCbHdl() { } +private: + vhpiTimeT vhpi_time; }; class VhpiNextPhaseCbHdl : public VhpiCbHdl { - +public: + VhpiNextPhaseCbHdl(GpiImplInterface *impl); + virtual ~VhpiNextPhaseCbHdl() { } +private: + vhpiTimeT vhpi_time; }; -#endif class VhpiStartupCbHdl : public VhpiCbHdl { public: VhpiStartupCbHdl(GpiImplInterface *impl); int run_callback(void); + int cleanup_callback() { + /* Too many sims get upset with this so we override to do nothing */ + return 0; + } virtual ~VhpiStartupCbHdl() { } }; @@ -174,6 +154,10 @@ class VhpiShutdownCbHdl : public VhpiCbHdl { public: VhpiShutdownCbHdl(GpiImplInterface *impl); int run_callback(void); + int cleanup_callback() { + /* Too many sims get upset with this so we override to do nothing */ + return 0; + } virtual ~VhpiShutdownCbHdl() { } }; @@ -189,7 +173,8 @@ class VhpiSignalObjHdl : public VhpiObjHdl, public GpiSignalObjHdl { public: VhpiSignalObjHdl(GpiImplInterface *impl, vhpiHandleT hdl) : VhpiObjHdl(impl, hdl), GpiSignalObjHdl(impl), - m_size(0) { } + m_size(0), + value_cb(NULL) { } virtual ~VhpiSignalObjHdl(); const char* get_signal_value_binstr(void); @@ -202,7 +187,7 @@ class VhpiSignalObjHdl : public VhpiObjHdl, public GpiSignalObjHdl { // Also think we want the triggers here? virtual GpiCbHdl *rising_edge_cb(void) { return NULL; } virtual GpiCbHdl *falling_edge_cb(void) { return NULL; } - virtual GpiCbHdl *value_change_cb(void); + virtual GpiCbHdl *value_change_cb(unsigned int edge); /* Functions that I would like to inherit but do not ?*/ virtual GpiObjHdl *get_handle_by_name(std::string &name) { @@ -229,4 +214,37 @@ class VhpiSignalObjHdl : public VhpiObjHdl, public GpiSignalObjHdl { VhpiValueCbHdl *value_cb; }; +class VhpiImpl : public GpiImplInterface { +public: + VhpiImpl(const std::string& name) : GpiImplInterface(name), + m_read_write(this), + m_next_phase(this), + m_read_only(this) { } + + /* Sim related */ + void sim_end(void); + void get_sim_time(uint32_t *high, uint32_t *low); + + /* Hierachy related */ + GpiObjHdl *get_root_handle(const char *name); + + /* Callback related, these may (will) return the same handle*/ + GpiCbHdl *register_timed_callback(uint64_t time_ps); + GpiCbHdl *register_readonly_callback(void); + GpiCbHdl *register_nexttime_callback(void); + GpiCbHdl *register_readwrite_callback(void); + int deregister_callback(GpiCbHdl *obj_hdl); + bool native_check(std::string &name, GpiObjHdl *parent) { return false; } + GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); + GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); + + const char * reason_to_string(int reason); + const char * format_to_string(int format); + +private: + VhpiReadwriteCbHdl m_read_write; + VhpiNextPhaseCbHdl m_next_phase; + VhpiReadOnlyCbHdl m_read_only; +}; + #endif /*COCOTB_VHPI_IMPL_H_ */ \ No newline at end of file diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 3f0f3af0..259a0bc5 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -115,7 +115,6 @@ class VpiValueCbHdl : public VpiCbHdl { public: VpiValueCbHdl(GpiImplInterface *impl, VpiSignalObjHdl *sig); virtual ~VpiValueCbHdl() { } - //int cleanup_callback(void); int run_callback(void); void set_edge(unsigned int edge); private: From 6cddd9e8cebd5073841415fc4325d139d3eaaaf5 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 31 Oct 2014 09:44:52 +0000 Subject: [PATCH 0651/2656] Issue#161: Make edge tests more robust --- examples/functionality/tests/test_cocotb.py | 30 ++++++++++++--------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 4d4ed798..88c5b01c 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -276,21 +276,27 @@ def do_single_edge_check(dut, level): @cocotb.test() def test_rising_edge(dut): - """Test that a rising edge can be yielded on""" - test = cocotb.fork(do_single_edge_check(dut, 1)) - yield Timer(10) - dut.clk <= 1 - yield [Timer(1000), Join(test)] + """Test that a rising edge can be yielded on""" + test = cocotb.fork(do_single_edge_check(dut, 1)) + yield Timer(10) + dut.clk <= 1 + fail_timer = Timer(1000) + result = yield [fail_timer, test.join()] + if result is fail_timer: + raise TestError("Test timed out") @cocotb.test(expect_error=True) def test_falling_edge(dut): - """Test that a falling edge can be yielded on""" - dut.clk <= 1 - yield Timer(10) - test = cocotb.fork(do_single_edge_check(dut, 0)) - yield Timer(10) - dut.clk <= 0 - yield [Timer(1000), Join(test)] + """Test that a falling edge can be yielded on""" + dut.clk <= 1 + yield Timer(10) + test = cocotb.fork(do_single_edge_check(dut, 0)) + yield Timer(10) + dut.clk <= 0 + fail_time = Timer(1000) + result = yield [fail_timer, test.join()] + if result is fail_timer: + raise TestError("Test timed out") @cocotb.coroutine def do_either_edge_test(dut): From 1a0ca9b8ec9f2e65040b9295795be2ab5ef2175a Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 31 Oct 2014 10:13:31 +0000 Subject: [PATCH 0652/2656] Issue#161: VHPI value change pused down into vhpi layer --- lib/vhpi/VhpiCbHdl.cpp | 59 ++++++++++++++++++++++++++++++++++++++---- lib/vhpi/VhpiImpl.h | 10 +++++-- lib/vpi/VpiCbHdl.cpp | 1 - 3 files changed, 62 insertions(+), 8 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index fd95c36b..df373599 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -254,19 +254,68 @@ const char* VhpiSignalObjHdl::get_signal_value_binstr(void) GpiCbHdl * VhpiSignalObjHdl::value_change_cb(unsigned int edge) { - value_cb = new VhpiValueCbHdl(VhpiObjHdl::m_impl, this); + m_value_cb.set_edge(edge); - if (value_cb->arm_callback()) + if (m_value_cb.arm_callback()) return NULL; - return value_cb; + return &m_value_cb; } -VhpiValueCbHdl::VhpiValueCbHdl(GpiImplInterface *impl, VhpiSignalObjHdl *sig) : VhpiCbHdl(impl) +VhpiValueCbHdl::VhpiValueCbHdl(GpiImplInterface *impl, + VhpiSignalObjHdl *sig) : + VhpiCbHdl(impl), + rising(false), + falling(false), + signal(sig) { cb_data.reason = vhpiCbValueChange; cb_data.time = &vhpi_time; - cb_data.obj = sig->get_handle(); + cb_data.obj = signal->get_handle(); +} + +void VhpiValueCbHdl::set_edge(unsigned int edge) +{ + if (edge & 1) + rising = true; + + if (edge & 2) + falling = true; +} + +int VhpiValueCbHdl::run_callback(void) +{ + std::string current_value; + std::string required; + bool pass = false; + if (rising && falling) { + pass = true; + goto check; + } + + current_value = signal->get_signal_value_binstr(); + if (rising && (current_value == "1")) { + pass = true; + goto check; + } + + if (falling && (current_value == "0")) { + pass = true; + goto check; + } + +check: + if (pass) { + LOG_WARN("Running Value change passup"); + this->gpi_function(m_cb_data); + } else { + LOG_WARN("Running Value change NO passup"); + //set_call_state(GPI_REPRIME); + cleanup_callback(); + arm_callback(); + } + + return 0; } VhpiStartupCbHdl::VhpiStartupCbHdl(GpiImplInterface *impl) : VhpiCbHdl(impl) diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 2e5f494a..459058ce 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -111,8 +111,14 @@ class VhpiValueCbHdl : public VhpiCbHdl { public: VhpiValueCbHdl(GpiImplInterface *impl, VhpiSignalObjHdl *sig); virtual ~VhpiValueCbHdl() { } + int run_callback(void); + void set_edge(unsigned int edge); private: vhpiTimeT vhpi_time; + std::string initial_value; + bool rising; + bool falling; + VhpiSignalObjHdl *signal; }; class VhpiTimedCbHdl : public VhpiCbHdl { @@ -174,7 +180,7 @@ class VhpiSignalObjHdl : public VhpiObjHdl, public GpiSignalObjHdl { VhpiSignalObjHdl(GpiImplInterface *impl, vhpiHandleT hdl) : VhpiObjHdl(impl, hdl), GpiSignalObjHdl(impl), m_size(0), - value_cb(NULL) { } + m_value_cb(impl, this) { } virtual ~VhpiSignalObjHdl(); const char* get_signal_value_binstr(void); @@ -211,7 +217,7 @@ class VhpiSignalObjHdl : public VhpiObjHdl, public GpiSignalObjHdl { unsigned int m_size; vhpiValueT m_value; vhpiValueT m_binvalue; - VhpiValueCbHdl *value_cb; + VhpiValueCbHdl m_value_cb; }; class VhpiImpl : public GpiImplInterface { diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 1946bb5e..001a0448 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -222,7 +222,6 @@ int VpiValueCbHdl::run_callback(void) this->gpi_function(m_cb_data); } else { //LOG_WARN("Running Value change NO passup"); - //set_call_state(GPI_REPRIME); cleanup_callback(); arm_callback(); } From 9be73662524516bc633cea175bc9f273044dbe9a Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 2 Nov 2014 17:41:17 +0000 Subject: [PATCH 0653/2656] Issue#161: Load extra libs, GPI_EXTRA, at runtime to enable mixed language support --- examples/mixed_language/tests/Makefile | 4 ++ lib/fli/FliImpl.cpp | 1 + lib/gpi/GpiCommon.cpp | 63 +++++++++++++++++++++++++- lib/gpi/gpi_priv.h | 12 +++++ lib/vhpi/VhpiCbHdl.cpp | 3 -- lib/vhpi/VhpiImpl.cpp | 14 ++---- lib/vpi/VpiCbHdl.cpp | 2 - lib/vpi/VpiImpl.cpp | 11 +++-- makefiles/Makefile.inc | 1 + makefiles/simulators/Makefile.aldec | 5 +- 10 files changed, 92 insertions(+), 24 deletions(-) diff --git a/examples/mixed_language/tests/Makefile b/examples/mixed_language/tests/Makefile index 61d8d46a..73d732b7 100644 --- a/examples/mixed_language/tests/Makefile +++ b/examples/mixed_language/tests/Makefile @@ -14,11 +14,15 @@ VHDL_SOURCES = $(PWD)/../../endian_swapper/hdl/endian_swapper.vhdl ifeq ($(TOPLEVEL_LANG),sv) VERILOG_SOURCES += $(PWD)/../hdl/toplevel.sv + GPI_IMPL=vpi + GPI_EXTRA=vhpi endif ifeq ($(TOPLEVEL_LANG),vhdl) $(error "VHDL toplevel not yet implemented") VHDL_SOURCES += $(PWD)/../hdl/toplevel.vhdl + GPI_IMPL=vhpi + GPI_EXTRA=vpi endif diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index a98856b5..8542a182 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -341,3 +341,4 @@ int FliSimPhaseCbHdl::arm_callback(void) { return 0; } +GPI_ENTRY_POINT(fli, cocotb_init); \ No newline at end of file diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 49ad3959..f692a2bb 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -30,6 +30,7 @@ #include "gpi_priv.h" #include #include +#include #include using namespace std; @@ -57,8 +58,66 @@ void gpi_sim_end(void) registered_impls[0]->sim_end(); } +static void gpi_load_libs(std::vector to_load) +{ + std::vector::iterator iter; + + for (iter = to_load.begin(); + iter != to_load.end(); + iter++) + { + void *lib_handle = NULL; + std::string full_name = "lib" + *iter + ".so"; + const char *now_loading = (full_name).c_str(); + + lib_handle = dlopen(now_loading, RTLD_GLOBAL | RTLD_NOW); + if (!lib_handle) { + printf("Error loading lib %s (%s)\n", now_loading, dlerror()); + exit(1); + } + std::string sym = (*iter) + "_entry_point"; + void *entry_point = dlsym(lib_handle, sym.c_str()); + if (!entry_point) { + printf("Unable to find entry point for %s (%s)\n", now_loading, dlerror()); + exit(1); + } + layer_entry_func new_lib_entry = (layer_entry_func)entry_point; + new_lib_entry(); + + dlerror(); + } +} + void gpi_embed_init_python(void) { + static bool loading = false; + + if (loading) + return; + + /* Lets look at what other libs we where asked to load too */ + char *lib_env = getenv("GPI_EXTRA"); + + if (lib_env) { + std::string lib_list = lib_env; + std::string delim = ":"; + std::vector to_load; + + size_t e_pos = 0; + while (std::string::npos != (e_pos = lib_list.find(delim))) { + std::string lib = lib_list.substr(0, e_pos); + lib_list.erase(0, e_pos + delim.length()); + + to_load.push_back(lib); + } + if (lib_list.length()) { + to_load.push_back(lib_list); + } + + loading = true; + gpi_load_libs(to_load); + } + embed_init_python(); } @@ -75,13 +134,13 @@ gpi_sim_hdl gpi_get_root_handle(const char *name) GpiObjHdl *hdl; - LOG_WARN("Looking for root handle over %d impls", registered_impls.size()); + LOG_DEBUG("Looking for root handle over %d impls", registered_impls.size()); for (iter = registered_impls.begin(); iter != registered_impls.end(); iter++) { if ((hdl = (*iter)->get_root_handle(name))) { - LOG_WARN("Got a Root handle (%s) back from %s", + LOG_DEBUG("Got a Root handle (%s) back from %s", hdl->get_name_str(), (*iter)->get_name_c()); return (gpi_sim_hdl)hdl; diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index d5c35499..5bada2cf 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -216,4 +216,16 @@ void gpi_embed_init(gpi_sim_info_t *info); void gpi_embed_end(void); void gpi_embed_init_python(void); +typedef const void (*layer_entry_func)(void); + +/* Use this macro in an implementation layer to define an enty point */ +#define GPI_ENTRY_POINT(NAME, func) \ + extern "C" { \ + const void NAME##_entry_point(void) \ + { \ + printf("In entry point for " #NAME "\n"); \ + func(); \ + } \ + } + #endif /* COCOTB_GPI_PRIV_H_ */ \ No newline at end of file diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index df373599..5c831776 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -306,11 +306,8 @@ int VhpiValueCbHdl::run_callback(void) check: if (pass) { - LOG_WARN("Running Value change passup"); this->gpi_function(m_cb_data); } else { - LOG_WARN("Running Value change NO passup"); - //set_call_state(GPI_REPRIME); cleanup_callback(); arm_callback(); } diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index f6386f59..7f33088f 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -36,11 +36,9 @@ #include extern "C" { - static VhpiCbHdl *sim_init_cb; static VhpiCbHdl *sim_finish_cb; static VhpiImpl *vhpi_table; - } const char * VhpiImpl::format_to_string(int format) @@ -146,18 +144,15 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) case vhpiPortDeclK: case vhpiSigDeclK: new_obj = new VhpiSignalObjHdl(this, new_hdl); - LOG_DEBUG("Created VhpiSignalObjHdl"); break; case vhpiCompInstStmtK: new_obj = new VhpiObjHdl(this, new_hdl); - LOG_DEBUG("Created VhpiObjHdl"); break; default: - LOG_CRITICAL("Not sure what to do with type %d for entity (%s)", type, name.c_str()); + LOG_DEBUG("Not sure what to do with type %d for entity (%s)", type, name.c_str()); return NULL; } - LOG_DEBUG("Type was %d", type); new_obj->initialise(name); return new_obj; @@ -194,12 +189,11 @@ GpiObjHdl *VhpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) LOG_DEBUG("Created VhpiObjHdl"); break; default: - LOG_CRITICAL("Not sure what to do with type %d below entity (%s) at index (%d)", + LOG_DEBUG("Not sure what to do with type %d below entity (%s) at index (%d)", type, parent->get_name_str(), index); return NULL; } - LOG_DEBUG("Type was %d", type); std::string name = vhpi_get_str(vhpiNameP, new_hdl); new_obj->initialise(name); @@ -373,4 +367,6 @@ void vhpi_startup_routines_bootstrap(void) { } } -} \ No newline at end of file +} + +GPI_ENTRY_POINT(vhpi, register_embed) diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 001a0448..b8fcb9d3 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -218,10 +218,8 @@ int VpiValueCbHdl::run_callback(void) check: if (pass) { - //LOG_WARN("Running Value change passup"); this->gpi_function(m_cb_data); } else { - //LOG_WARN("Running Value change NO passup"); cleanup_callback(); arm_callback(); } diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index eaf7ad37..1989abc0 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -127,7 +127,7 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) LOG_DEBUG("Created VpiObjHdl"); break; default: - LOG_CRITICAL("Not sure what to do with type %d for entity (%s)", type, name.c_str()); + LOG_DEBUG("Not sure what to do with type %d for entity (%s)", type, name.c_str()); return NULL; } @@ -159,18 +159,19 @@ GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) case vpiNet: case vpiNetBit: new_obj = new VpiSignalObjHdl(this, new_hdl); - LOG_DEBUG("Created VpiSignalObjHdl"); break; case vpiModule: new_obj = new VpiObjHdl(this, new_hdl); - LOG_DEBUG("Created VpiObjHdl"); break; default: - LOG_CRITICAL("Not sure what to do with type %d below entity (%s) at index (%d)", + LOG_DEBUG("Not sure what to do with type %d below entity (%s) at index (%d)", type, parent->get_name_str(), index); return NULL; } + std::string name = vpi_get_str(vpiFullName, new_hdl); + new_obj->initialise(name); + return new_obj; } @@ -451,3 +452,5 @@ void vlog_startup_routines_bootstrap(void) { } } + +GPI_ENTRY_POINT(vpi, register_embed) \ No newline at end of file diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index b3b5ea2c..a0703cbb 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -52,6 +52,7 @@ include $(SIM_ROOT)/makefiles/Makefile.pylib export LIB_DIR=$(BUILD_DIR)/libs/$(ARCH) export LIB_OBJ_DIR:= $(BUILD_DIR)/obj/$(ARCH) export INCLUDES := -I$(SIM_ROOT)/include $(PYTHON_INCLUDEDIR) +export GPI_EXTRA # Base GCC flags diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index c6fc9832..e5cfdf09 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -52,12 +52,9 @@ endif ifeq ($(GPI_IMPL),vhpi) GPI_ARGS = -loadvhpi $(LIB_DIR)/libvhpi:vhpi_startup_routines_bootstrap endif -ifeq ($(GPI_IMPL),mixed) - GPI_ARGS = -pli libvpi -loadvhpi $(LIB_DIR)/libvhpi:vhpi_startup_routines_bootstrap -endif ifndef GPI_ARGS - $(error "Unable to determine appropriate GPI layer to use") + $(error "Unable to determine appropriate GPI layer to use as main entry point") endif From 5ff75609145f3c5d8971ca3e43534bf0d1c37ff8 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 2 Nov 2014 17:55:46 +0000 Subject: [PATCH 0654/2656] Issue#161: Remove some debug --- lib/vhpi/VhpiImpl.cpp | 2 -- lib/vpi/VpiImpl.cpp | 2 -- 2 files changed, 4 deletions(-) diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 7f33088f..e2d184c4 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -182,11 +182,9 @@ GpiObjHdl *VhpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) switch (type) { case vhpiIndexedNameK: new_obj = new VhpiSignalObjHdl(this, new_hdl); - LOG_DEBUG("Created VhpiSignalObjHdl"); break; case vhpiCompInstStmtK: new_obj = new VhpiObjHdl(this, new_hdl); - LOG_DEBUG("Created VhpiObjHdl"); break; default: LOG_DEBUG("Not sure what to do with type %d below entity (%s) at index (%d)", diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 1989abc0..ab685f4a 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -120,11 +120,9 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) case vpiReg: case vpiParameter: new_obj = new VpiSignalObjHdl(this, new_hdl); - LOG_DEBUG("Created VpiSignalObjHdl"); break; case vpiModule: new_obj = new VpiObjHdl(this, new_hdl); - LOG_DEBUG("Created VpiObjHdl"); break; default: LOG_DEBUG("Not sure what to do with type %d for entity (%s)", type, name.c_str()); From 65c5c0ffbf716a20211a7f9774b0349fc27cd667 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 5 Nov 2014 11:26:13 +0000 Subject: [PATCH 0655/2656] Issue#161: Add edge count test, for looking in to vhpi issues --- examples/functionality/tests/Makefile | 6 +- examples/functionality/tests/test_cocotb.py | 64 ++++++++++++++++++--- 2 files changed, 61 insertions(+), 9 deletions(-) diff --git a/examples/functionality/tests/Makefile b/examples/functionality/tests/Makefile index cd58946c..a23e0f1c 100644 --- a/examples/functionality/tests/Makefile +++ b/examples/functionality/tests/Makefile @@ -29,16 +29,18 @@ TOPLEVEL := sample_module -LANGUAGE ?= verilog +TOPLEVEL_LANG ?= verilog PWD=$(shell pwd) COCOTB=$(PWD)/../../.. -ifeq ($(LANGUAGE),vhdl) +ifeq ($(TOPLEVEL_LANG),vhdl) VHDL_SOURCES = $(PWD)/../hdl/sample_module.vhdl TOPLEVEL := $(TOPLEVEL) + GPI_IMPL := vhpi else VERILOG_SOURCES = $(PWD)/../hdl/sample_module.v + GPI_IMPL := vpi endif MODULE ?= test_cocotb,test_discovery,test_external,test_regression diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 88c5b01c..5e1a30c3 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -303,26 +303,76 @@ def do_either_edge_test(dut): """Run do either edge test""" yield Edge(dut.clk) dut.log.info("Value of %s is %d" % (dut.clk, dut.clk.value.integer)) - if dut.clk.value.integer is not 1: - raise TestError("Value should be 0") + #if dut.clk.value.integer is not 1: + # raise TestError("Value should be 0") yield Edge(dut.clk) dut.log.info("Value of %s is %d" % (dut.clk, dut.clk.value.integer)) - if dut.clk.value.integer is not 0: - raise TestError("Value should be 1") + #if dut.clk.value.integer is not 0: + # raise TestError("Value should be 1") yield Timer(100) @cocotb.test() def test_either_edge(dut): """Test that either edge can be triggered on""" dut.clk <= 0 - yield Timer(1) + yield Timer(10) + dut.log.info("First timer") + yield Timer(10) test = cocotb.fork(do_either_edge_test(dut)) - yield Timer(11) dut.clk <= 1 - yield Timer(11) + yield Timer(10) + dut.log.info("Second timer") + dut.clk <= 0 + yield Timer(10) + dut.log.info("Third timer") + dut.clk <= 1 + yield Timer(10) + dut.log.info("fourth timer") dut.clk <= 0 + yield Timer(10) + dut.log.info("Fifth timer") + dut.clk <= 1 + yield Timer(10) + dut.log.info("Sixth timer") + fail_timer = Timer(1000) result = yield [fail_timer, test.join()] if result is fail_timer: raise TestError("Test timed out") +@cocotb.coroutine +def do_clock(dut, limit, period): + """Simple clock with a limit""" + wait_period = period / 2 + while limit: + yield Timer(wait_period) + dut.clk <= 0 + yield Timer(wait_period) + dut.clk <= 1 + limit -= 1 + +@cocotb.coroutine +def do_edge_count(dut, signal): + """Count the edges""" + global edges_seen + count = 0; + while True: + yield RisingEdge(signal) + edges_seen += 1 + + +@cocotb.test() +def test_edge_count(dut): + """Count the number of edges is as expected""" + global edges_seen + edges_seen = 0 + clk_period = 100; + edge_count = 10; + clock = cocotb.fork(do_clock(dut, edge_count, clk_period)) + test = cocotb.fork(do_edge_count(dut, dut.clk)) + + yield Timer(clk_period * (edge_count + 1)) + + if edge_count is not edges_seen: + raise cocotb.TestFailed + From 3624ca5fc6ae673dbcf67d3fdc6783aa7525012e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 5 Nov 2014 11:26:45 +0000 Subject: [PATCH 0656/2656] Issue#161: Some VPI cleanup --- lib/vpi/VpiCbHdl.cpp | 17 ++++------------- lib/vpi/VpiImpl.h | 14 +++----------- 2 files changed, 7 insertions(+), 24 deletions(-) diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index b8fcb9d3..485f0275 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -46,6 +46,10 @@ VpiCbHdl::VpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), cb_data.time = NULL; cb_data.value = NULL; cb_data.user_data = (char*)this; + + vpi_time.high = 0; + vpi_time.low = 0; + vpi_time.type = vpiSimTime; } /* If the user data already has a callback handle then deregister @@ -260,7 +264,6 @@ int VpiShutdownCbHdl::run_callback(void) { VpiTimedCbHdl::VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : VpiCbHdl(impl) { - vpi_time.type = vpiSimTime; vpi_time.high = (uint32_t)(time_ps>>32); vpi_time.low = (uint32_t)(time_ps); @@ -270,30 +273,18 @@ VpiTimedCbHdl::VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : VpiCbHd VpiReadwriteCbHdl::VpiReadwriteCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) { - vpi_time.type = vpiSimTime; - vpi_time.high = 0; - vpi_time.low = 0; - cb_data.reason = cbReadWriteSynch; cb_data.time = &vpi_time; } VpiReadOnlyCbHdl::VpiReadOnlyCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) { - vpi_time.type = vpiSimTime; - vpi_time.high = 0; - vpi_time.low = 0; - cb_data.reason = cbReadOnlySynch; cb_data.time = &vpi_time; } VpiNextPhaseCbHdl::VpiNextPhaseCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) { - vpi_time.type = vpiSimTime; - vpi_time.high = 0; - vpi_time.low = 0; - cb_data.reason = cbNextSimTime; cb_data.time = &vpi_time; } \ No newline at end of file diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 259a0bc5..f92f5ab2 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -107,6 +107,7 @@ class VpiCbHdl : public GpiCbHdl { protected: vpiHandle vpi_hdl; s_cb_data cb_data; + s_vpi_time vpi_time; }; class VpiSignalObjHdl; @@ -118,7 +119,6 @@ class VpiValueCbHdl : public VpiCbHdl { int run_callback(void); void set_edge(unsigned int edge); private: - s_vpi_time vpi_time; std::string initial_value; bool rising; bool falling; @@ -133,31 +133,25 @@ class VpiTimedCbHdl : public VpiCbHdl { VpiCbHdl::cleanup_callback(); return 1; } -private: - s_vpi_time vpi_time; }; class VpiReadOnlyCbHdl : public VpiCbHdl { public: VpiReadOnlyCbHdl(GpiImplInterface *impl); virtual ~VpiReadOnlyCbHdl() { } -private: - s_vpi_time vpi_time; }; class VpiNextPhaseCbHdl : public VpiCbHdl { public: VpiNextPhaseCbHdl(GpiImplInterface *impl); virtual ~VpiNextPhaseCbHdl() { } -private: - s_vpi_time vpi_time; }; class VpiStartupCbHdl : public VpiCbHdl { public: VpiStartupCbHdl(GpiImplInterface *impl); int run_callback(void); - int cleanup_callback() { + int cleanup_callback(void) { /* Too many sims get upset with this so we override to do nothing */ return 0; } @@ -168,7 +162,7 @@ class VpiShutdownCbHdl : public VpiCbHdl { public: VpiShutdownCbHdl(GpiImplInterface *impl); int run_callback(void); - int cleanup_callback() { + int cleanup_callback(void) { /* Too many sims get upset with this so we override to do nothing */ return 0; } @@ -179,8 +173,6 @@ class VpiReadwriteCbHdl : public VpiCbHdl { public: VpiReadwriteCbHdl(GpiImplInterface *impl); virtual ~VpiReadwriteCbHdl() { } -private: - s_vpi_time vpi_time; }; class VpiSignalObjHdl : public VpiObjHdl, public GpiSignalObjHdl { From 57fcbcf76a4ab97b405ccd422c168bec88f705f4 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 6 Nov 2014 20:33:14 +0000 Subject: [PATCH 0657/2656] Cleanup: Remove gcc switches that are not fully supported --- examples/endian_swapper/cosim/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/endian_swapper/cosim/Makefile b/examples/endian_swapper/cosim/Makefile index 866f656b..73405567 100644 --- a/examples/endian_swapper/cosim/Makefile +++ b/examples/endian_swapper/cosim/Makefile @@ -6,7 +6,7 @@ SWIG ?= swig all: io_module.so _hal.so io_module.o: io.c io_module.h io.h - gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -DNDEBUG -I$(PYTHON_INCDIR) -c $< -o $@ + gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions --param=ssp-buffer-size=4 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -DNDEBUG -I$(PYTHON_INCDIR) -c $< -o $@ io_module.so: io_module.o gcc -pthread -shared -Wl,-z,relro $< -L$(PYTHON_LIBDIR) -lpython2.7 -o $@ From f181a7e3ff8c75139358c3ada9e7b0ef61ab3d99 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 7 Nov 2014 09:49:37 +0000 Subject: [PATCH 0658/2656] Issue#161: Fix memory corruption bug on initialisaton of VhpiSignalCbhdl --- lib/vhpi/VhpiCbHdl.cpp | 105 ++++++++++++++++++++++++++--------------- lib/vhpi/VhpiImpl.cpp | 3 +- lib/vhpi/VhpiImpl.h | 15 ++---- 3 files changed, 72 insertions(+), 51 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 5c831776..68ae34bf 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -70,8 +70,6 @@ int VhpiSignalObjHdl::initialise(std::string &name) { LOG_CRITICAL("Unable to alloc mem for write buffer"); } - memset(&m_value.value.enumvs, m_size, vhpi0); - break; } @@ -83,12 +81,12 @@ int VhpiSignalObjHdl::initialise(std::string &name) { /* We also alloc a second value member for use with read string operations */ m_binvalue.format = vhpiBinStrVal; - m_binvalue.bufSize = 0;//m_size*sizeof(vhpiCharT); - m_binvalue.value.str = NULL;//(vhpiCharT *)calloc(m_binvalue.bufSize, m_binvalue.bufSize); + m_binvalue.bufSize = 0; + m_binvalue.value.str = NULL; int new_size = vhpi_get_value(vhpi_hdl, &m_binvalue); - m_binvalue.bufSize = new_size*sizeof(vhpiCharT); + m_binvalue.bufSize = new_size*sizeof(vhpiCharT) + 1; m_binvalue.value.str = (vhpiCharT *)calloc(m_binvalue.bufSize, m_binvalue.bufSize); if (!m_value.value.str) { @@ -109,40 +107,67 @@ VhpiCbHdl::VhpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), cb_data.time = NULL; cb_data.value = NULL; cb_data.user_data = (char *)this; + + vhpi_time.high = 0; + vhpi_time.low = 0; } int VhpiCbHdl::cleanup_callback(void) { + /* For non timer callbacks we disable rather than remove */ + int ret = 0; if (m_state == GPI_FREE) return 0; vhpiStateT cbState = (vhpiStateT)vhpi_get(vhpiStateP, vhpi_hdl); - if (vhpiMature != cbState) - vhpi_remove_cb(vhpi_hdl); + if (vhpiEnable == cbState) { + ret = vhpi_disable_cb(vhpi_hdl); + m_state = GPI_FREE; + } + + if (ret) + check_vhpi_error(); - vhpi_hdl = NULL; - m_state = GPI_FREE; return 0; } int VhpiCbHdl::arm_callback(void) { - vhpiHandleT new_hdl = vhpi_register_cb(&cb_data, vhpiReturnCb); int ret = 0; + vhpiStateT cbState; - if (!new_hdl) { - LOG_CRITICAL("VHPI: Unable to register callback a handle for VHPI type %s(%d)", - m_impl->reason_to_string(cb_data.reason), cb_data.reason); - check_vhpi_error(); - ret = -1; - } + if (m_state == GPI_PRIMED) + return 0; - vhpiStateT cbState = (vhpiStateT)vhpi_get(vhpiStateP, new_hdl); - if (cbState != vhpiEnable) { - LOG_CRITICAL("VHPI ERROR: Registered callback isn't enabled! Got %d\n", cbState); - } + /* Do we already have a handle, if so and it is disabled then + just re-enable it */ + + if (vhpi_hdl) { + cbState = (vhpiStateT)vhpi_get(vhpiStateP, vhpi_hdl); + if (vhpiDisable == cbState) { + if (vhpi_enable_cb(vhpi_hdl)) { + check_vhpi_error(); + ret = -1; + } + } + } else { + + vhpiHandleT new_hdl = vhpi_register_cb(&cb_data, vhpiReturnCb); + + if (!new_hdl) { + LOG_CRITICAL("VHPI: Unable to register callback a handle for VHPI type %s(%d)", + m_impl->reason_to_string(cb_data.reason), cb_data.reason); + check_vhpi_error(); + ret = -1; + } + + cbState = (vhpiStateT)vhpi_get(vhpiStateP, new_hdl); + if (vhpiEnable != cbState) { + LOG_CRITICAL("VHPI ERROR: Registered callback isn't enabled! Got %d\n", cbState); + } - vhpi_hdl = new_hdl; + vhpi_hdl = new_hdl; + } m_state = GPI_PRIMED; return ret; @@ -228,8 +253,9 @@ int VhpiSignalObjHdl::set_signal_value(std::string &value) } // Fill bits at the end of the value to 0's - for (i = len; i < m_size; i++) + for (i = len; i < m_size; i++) { m_value.value.enumvs[i] = vhpi0; + } break; } @@ -305,12 +331,10 @@ int VhpiValueCbHdl::run_callback(void) } check: - if (pass) { + if (pass) this->gpi_function(m_cb_data); - } else { - cleanup_callback(); - arm_callback(); - } + else + m_state = GPI_PRIMED; return 0; } @@ -354,29 +378,32 @@ VhpiTimedCbHdl::VhpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : VhpiC cb_data.time = &vhpi_time; } -VhpiReadwriteCbHdl::VhpiReadwriteCbHdl(GpiImplInterface *impl) : VhpiCbHdl(impl) +int VhpiTimedCbHdl::cleanup_callback(void) { - vhpi_time.high = 0; - vhpi_time.low = 0; + if (m_state == GPI_FREE) + return 1; + + vhpi_remove_cb(vhpi_hdl); - cb_data.reason = vhpiCbEndOfProcesses; + vhpi_hdl = NULL; + m_state = GPI_FREE; + return 1; +} + +VhpiReadwriteCbHdl::VhpiReadwriteCbHdl(GpiImplInterface *impl) : VhpiCbHdl(impl) +{ + cb_data.reason = vhpiCbRepEndOfProcesses; cb_data.time = &vhpi_time; } VhpiReadOnlyCbHdl::VhpiReadOnlyCbHdl(GpiImplInterface *impl) : VhpiCbHdl(impl) { - vhpi_time.high = 0; - vhpi_time.low = 0; - - cb_data.reason = vhpiCbLastKnownDeltaCycle; + cb_data.reason = vhpiCbRepLastKnownDeltaCycle; cb_data.time = &vhpi_time; } VhpiNextPhaseCbHdl::VhpiNextPhaseCbHdl(GpiImplInterface *impl) : VhpiCbHdl(impl) { - vhpi_time.high = 0; - vhpi_time.low = 0; - - cb_data.reason = vhpiCbNextTimeStep; + cb_data.reason = vhpiCbRepNextTimeStep; cb_data.time = &vhpi_time; } \ No newline at end of file diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index e2d184c4..80610fc0 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -314,8 +314,9 @@ void handle_vhpi_callback(const vhpiCbDataT *cb_data) /* We have re-primed in the handler */ if (new_state != GPI_PRIMED) - if (cb_hdl->cleanup_callback()) + if (cb_hdl->cleanup_callback()) { delete cb_hdl; + } } diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 459058ce..41b456c2 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -103,6 +103,7 @@ class VhpiCbHdl : public GpiCbHdl { protected: vhpiHandleT vhpi_hdl; vhpiCbDataT cb_data; + vhpiTimeT vhpi_time; }; class VhpiSignalObjHdl; @@ -114,7 +115,6 @@ class VhpiValueCbHdl : public VhpiCbHdl { int run_callback(void); void set_edge(unsigned int edge); private: - vhpiTimeT vhpi_time; std::string initial_value; bool rising; bool falling; @@ -125,31 +125,26 @@ class VhpiTimedCbHdl : public VhpiCbHdl { public: VhpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps); virtual ~VhpiTimedCbHdl() { } -private: - vhpiTimeT vhpi_time; + int cleanup_callback(); }; class VhpiReadOnlyCbHdl : public VhpiCbHdl { public: VhpiReadOnlyCbHdl(GpiImplInterface *impl); virtual ~VhpiReadOnlyCbHdl() { } -private: - vhpiTimeT vhpi_time; }; class VhpiNextPhaseCbHdl : public VhpiCbHdl { public: VhpiNextPhaseCbHdl(GpiImplInterface *impl); virtual ~VhpiNextPhaseCbHdl() { } -private: - vhpiTimeT vhpi_time; }; class VhpiStartupCbHdl : public VhpiCbHdl { public: VhpiStartupCbHdl(GpiImplInterface *impl); int run_callback(void); - int cleanup_callback() { + int cleanup_callback(void) { /* Too many sims get upset with this so we override to do nothing */ return 0; } @@ -160,7 +155,7 @@ class VhpiShutdownCbHdl : public VhpiCbHdl { public: VhpiShutdownCbHdl(GpiImplInterface *impl); int run_callback(void); - int cleanup_callback() { + int cleanup_callback(void) { /* Too many sims get upset with this so we override to do nothing */ return 0; } @@ -171,8 +166,6 @@ class VhpiReadwriteCbHdl : public VhpiCbHdl { public: VhpiReadwriteCbHdl(GpiImplInterface *impl); virtual ~VhpiReadwriteCbHdl() { } -private: - vhpiTimeT vhpi_time; }; class VhpiSignalObjHdl : public VhpiObjHdl, public GpiSignalObjHdl { From 2aa752b2bb89120f9f1024838b0c9f0d5f2090ed Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 7 Nov 2014 09:57:47 +0000 Subject: [PATCH 0659/2656] Cleanup: Pick up python version correctly --- examples/endian_swapper/cosim/Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/endian_swapper/cosim/Makefile b/examples/endian_swapper/cosim/Makefile index 73405567..c0216b32 100644 --- a/examples/endian_swapper/cosim/Makefile +++ b/examples/endian_swapper/cosim/Makefile @@ -1,4 +1,3 @@ -PYTHON_INCDIR ?= /usr/include/python2.7 PYTHON_LIBDIR ?= /usr/lib64 SWIG ?= swig @@ -6,10 +5,12 @@ SWIG ?= swig all: io_module.so _hal.so io_module.o: io.c io_module.h io.h - gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions --param=ssp-buffer-size=4 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -DNDEBUG -I$(PYTHON_INCDIR) -c $< -o $@ + gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 \ + -fexceptions --param=ssp-buffer-size=4 -mtune=generic -D_GNU_SOURCE -fPIC \ + -fwrapv -DNDEBUG -I$(PYTHON_INCLUDEDIR) -c $< -o $@ io_module.so: io_module.o - gcc -pthread -shared -Wl,-z,relro $< -L$(PYTHON_LIBDIR) -lpython2.7 -o $@ + gcc -pthread -shared -Wl,-z,relro $< -L$(PYTHON_LIBDIR) -lpython$(PYTHON_VERSION) -o $@ _hal.so: ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so gcc -g -ldl -shared -fPIC -I$(shell pwd) -I$(PYTHON_INCDIR) -I../hal ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so -o $@ From 5f5dbfc79f37bb3d66255f2894182fa04170127f Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 7 Nov 2014 09:58:14 +0000 Subject: [PATCH 0660/2656] Issue#161: Use TOPLEVEL_LANG as selection method --- examples/endian_swapper/tests/Makefile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index 6cfc00d2..e806187f 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -29,7 +29,7 @@ # Override this variable to use VHPI instead of VPI -GPI_IMPL ?= vpi +TOPLEVEL_LANG ?= verilog @@ -37,14 +37,16 @@ PWD=$(shell pwd) COCOTB=$(PWD)/../../.. -ifeq ($(GPI_IMPL),vpi) +ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES = $(PWD)/../hdl/endian_swapper.sv TOPLEVEL = endian_swapper_sv + GPI_IMPL=vpi endif -ifeq ($(GPI_IMPL),vhpi) +ifeq ($(TOPLEVEL_LANG),vhdl) VHDL_SOURCES = $(PWD)/../hdl/endian_swapper.vhdl TOPLEVEL = endian_swapper_vhdl + GPI_IMPL=vhpi endif MODULE=test_endian_swapper From bca69efa5b6ba35ec36c81ee2da775ead98c5093 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 12 Nov 2014 11:09:39 +0000 Subject: [PATCH 0661/2656] Cleanup: Fixup python include path and use in cosim for endian_swapper --- examples/endian_swapper/cosim/Makefile | 4 +++- examples/endian_swapper/cosim/io.c | 4 ++++ makefiles/Makefile.inc | 3 ++- makefiles/Makefile.pylib | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/examples/endian_swapper/cosim/Makefile b/examples/endian_swapper/cosim/Makefile index c0216b32..616d0d86 100644 --- a/examples/endian_swapper/cosim/Makefile +++ b/examples/endian_swapper/cosim/Makefile @@ -1,3 +1,5 @@ +include $(SIM_ROOT)/makefiles/Makefile.pylib + PYTHON_LIBDIR ?= /usr/lib64 SWIG ?= swig @@ -13,7 +15,7 @@ io_module.so: io_module.o gcc -pthread -shared -Wl,-z,relro $< -L$(PYTHON_LIBDIR) -lpython$(PYTHON_VERSION) -o $@ _hal.so: ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so - gcc -g -ldl -shared -fPIC -I$(shell pwd) -I$(PYTHON_INCDIR) -I../hal ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so -o $@ + gcc -g -ldl -shared -fPIC -I$(shell pwd) -I$(PYTHON_INCLUDEDIR) -I../hal ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so -o $@ endian_swapper_hal_wrap.c: ../hal/endian_swapper_hal.h $(SWIG) -python -outcurrentdir ../hal/endian_swapper_hal.h diff --git a/examples/endian_swapper/cosim/io.c b/examples/endian_swapper/cosim/io.c index fedb57d7..917f0d18 100644 --- a/examples/endian_swapper/cosim/io.c +++ b/examples/endian_swapper/cosim/io.c @@ -118,5 +118,9 @@ initio_module(void) { PyObject* io_module; io_module = Py_InitModule("io_module", io_module_methods); + if (!io_module) { + printf("Failed to load io_module\n"); + exit(1); + } } diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index a0703cbb..9c5d5b44 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -51,8 +51,9 @@ include $(SIM_ROOT)/makefiles/Makefile.pylib export LIB_DIR=$(BUILD_DIR)/libs/$(ARCH) export LIB_OBJ_DIR:= $(BUILD_DIR)/obj/$(ARCH) -export INCLUDES := -I$(SIM_ROOT)/include $(PYTHON_INCLUDEDIR) +export INCLUDES := -I$(SIM_ROOT)/include -I$(PYTHON_INCLUDEDIR) export GPI_EXTRA +export GPI_LANGUAGE # Base GCC flags diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib index 651f90e2..4c44330e 100644 --- a/makefiles/Makefile.pylib +++ b/makefiles/Makefile.pylib @@ -51,6 +51,6 @@ endif # is we simply link against all dynamic libraries in the Python installation PYLIBS = $(shell python-config --libs) -PYTHON_INCLUDEDIR := -I$(shell python -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_inc()') +PYTHON_INCLUDEDIR := $(shell python -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_inc()') From 4401622185bb53ea2e933009cb953aada9079eab Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 12 Nov 2014 11:18:09 +0000 Subject: [PATCH 0662/2656] Cleanup: Forgot to add makefile to previoue commit --- examples/endian_swapper/tests/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index e806187f..899793cd 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -81,5 +81,5 @@ profile: clean:: -rm -rf test_profile.pstat -rm -rf callgraph.svg - + -cd ../cosim && make clean From 25207e5302fce4a4bbf36ffb755268e6afb74d84 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 12 Nov 2014 11:50:55 +0000 Subject: [PATCH 0663/2656] Cleanup: Do not use LANGUAGE internally anymore since this can be set by login scripts --- lib/embed/gpi_embed.c | 4 ++-- makefiles/Makefile.inc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 63fa4847..26868865 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -217,9 +217,9 @@ void embed_sim_init(gpi_sim_info_t *info) } // Set languare in use - const char *lang = getenv("LANGUAGE"); + const char *lang = getenv("TOPLEVEL_LANG"); if (!lang) - fprintf(stderr, "You should really set LANGUAGE to \"verilog/vhdl\""); + fprintf(stderr, "You should really set TOPLEVEL_LANG to \"verilog/vhdl\""); else { if (-1 == PyObject_SetAttrString(cocotb_module, "LANGUAGE", PyString_FromString(lang))) { fprintf(stderr, "Unable to set LANGUAGE"); diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 9c5d5b44..1131c645 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -53,7 +53,7 @@ export LIB_DIR=$(BUILD_DIR)/libs/$(ARCH) export LIB_OBJ_DIR:= $(BUILD_DIR)/obj/$(ARCH) export INCLUDES := -I$(SIM_ROOT)/include -I$(PYTHON_INCLUDEDIR) export GPI_EXTRA -export GPI_LANGUAGE +export TOPLEVEL_LANG # Base GCC flags From 374c33218f6b32faea7cb1474db5ab2cdfde946e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 12 Nov 2014 12:19:46 +0000 Subject: [PATCH 0664/2656] Cleanup: Make cosim buildable as 32bit --- examples/endian_swapper/cosim/Makefile | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/endian_swapper/cosim/Makefile b/examples/endian_swapper/cosim/Makefile index 616d0d86..f6c3fd99 100644 --- a/examples/endian_swapper/cosim/Makefile +++ b/examples/endian_swapper/cosim/Makefile @@ -2,20 +2,24 @@ include $(SIM_ROOT)/makefiles/Makefile.pylib PYTHON_LIBDIR ?= /usr/lib64 SWIG ?= swig +CC=gcc +ifeq ($(ARCH),i686) + CC += -m32 +endif .PHONY: all all: io_module.so _hal.so io_module.o: io.c io_module.h io.h - gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 \ + $(CC) -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 \ -fexceptions --param=ssp-buffer-size=4 -mtune=generic -D_GNU_SOURCE -fPIC \ -fwrapv -DNDEBUG -I$(PYTHON_INCLUDEDIR) -c $< -o $@ io_module.so: io_module.o - gcc -pthread -shared -Wl,-z,relro $< -L$(PYTHON_LIBDIR) -lpython$(PYTHON_VERSION) -o $@ + $(CC) -pthread -shared -Wl,-z,relro $< -L$(PYTHON_LIBDIR) -lpython$(PYTHON_VERSION) -o $@ _hal.so: ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so - gcc -g -ldl -shared -fPIC -I$(shell pwd) -I$(PYTHON_INCLUDEDIR) -I../hal ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so -o $@ + $(CC) -g -ldl -shared -fPIC -I$(shell pwd) -I$(PYTHON_INCLUDEDIR) -I../hal ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so -o $@ endian_swapper_hal_wrap.c: ../hal/endian_swapper_hal.h $(SWIG) -python -outcurrentdir ../hal/endian_swapper_hal.h From ea73c27373c78c13a2cc957ed04d2d3762038ab8 Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 12 Nov 2014 23:21:04 +0000 Subject: [PATCH 0665/2656] Test webhooks --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 3407a865..bf60c6e9 100644 --- a/version +++ b/version @@ -1 +1 @@ -VERSION=0.5 +VERSION=0.5a From dc69124dbb8f0b92c223487828b712adc828f8a5 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Tue, 18 Nov 2014 20:10:10 +0000 Subject: [PATCH 0666/2656] Issue #141: Some further work on FLI (still broken) --- .../endian_swapper/hdl/endian_swapper.vhdl | 2 + .../tests/test_endian_swapper.py | 1 + lib/Makefile | 6 +- lib/fli/FliImpl.cpp | 80 ++++++++++--- lib/fli/FliImpl.h | 106 +++++++++++++----- makefiles/simulators/Makefile.modelsim | 12 +- 6 files changed, 163 insertions(+), 44 deletions(-) diff --git a/examples/endian_swapper/hdl/endian_swapper.vhdl b/examples/endian_swapper/hdl/endian_swapper.vhdl index 096239de..a4c7f7b2 100644 --- a/examples/endian_swapper/hdl/endian_swapper.vhdl +++ b/examples/endian_swapper/hdl/endian_swapper.vhdl @@ -176,7 +176,9 @@ end process; -- Unfortunately this workaround is required for Aldec: Need to schedule an event fake_process :process begin + report "Time 0...."; wait for 50 ns; + report "50 ns has elapsed"; end process; diff --git a/examples/endian_swapper/tests/test_endian_swapper.py b/examples/endian_swapper/tests/test_endian_swapper.py index d98a3eba..30549ab5 100644 --- a/examples/endian_swapper/tests/test_endian_swapper.py +++ b/examples/endian_swapper/tests/test_endian_swapper.py @@ -116,6 +116,7 @@ def clock_gen(signal): def run_test(dut, data_in=None, config_coroutine=None, idle_inserter=None, backpressure_inserter=None): cocotb.fork(clock_gen(dut.clk)) + yield RisingEdge(dut.clk) tb = EndianSwapperTB(dut) yield tb.reset() diff --git a/lib/Makefile b/lib/Makefile index a0dbd026..4907c412 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -61,6 +61,9 @@ $(LIB_DIR)/libvhpi.so: $(SIM_ROOT)/lib/vhpi/VhpiImpl.cpp $(SIM_ROOT)/lib/vhpi/Vh $(LIB_DIR)/libgpi.so: $(SIM_ROOT)/lib/gpi/GpiCommon.cpp $(SIM_ROOT)/lib/gpi/GpiCbHdl.cpp | $(LIB_DIR) make -C $(SIM_ROOT)/lib/gpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) +$(LIB_DIR)/libfli.so: $(SIM_ROOT)/lib/fli/FliImpl.cpp | $(LIB_DIR) + make -C $(SIM_ROOT)/lib/fli EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) + $(LIB_DIR)/libsim.so: $(SIM_ROOT)/lib/simulator/simulatormodule.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/simulator @@ -69,7 +72,8 @@ COCOTB_LIBS = $(LIB_DIR)/libgpilog.so \ $(LIB_DIR)/libgpi.so \ $(LIB_DIR)/libvpi.so \ $(LIB_DIR)/libsim.so \ - $(LIB_DIR)/libvhpi.so + $(LIB_DIR)/libvhpi.so \ + $(LIB_DIR)/libfli.so clean:: -@rm -rf $(BUILD_DIR) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 8542a182..0e0dc745 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -36,8 +36,8 @@ extern "C" { static FliImpl *fli_table; -void cocotb_init(void) { - LOG_INFO("cocotb_init called\n"); +void fli_elab_cb(void *nothing) { + LOG_INFO("fli_elab_cb called\n"); fli_table = new FliImpl("FLI"); gpi_register_impl(fli_table); @@ -58,6 +58,13 @@ void cocotb_init(void) { gpi_embed_init(&sim_info); } +void cocotb_init(void) { + LOG_INFO("cocotb_init called\n"); + mti_AddLoadDoneCB(fli_elab_cb, NULL); +} + + + // Main re-entry point for callbacks from simulator void handle_fli_callback(void *data) { @@ -66,13 +73,20 @@ void handle_fli_callback(void *data) if (!cb_hdl) LOG_CRITICAL("FLI: Callback data corrupted"); - if (cb_hdl->get_call_state() == GPI_PRIMED) { - cb_hdl->set_call_state(GPI_PRE_CALL); + gpi_cb_state_e old_state = cb_hdl->get_call_state(); + + if (old_state == GPI_PRIMED) { + + cb_hdl->set_call_state(GPI_CALL); cb_hdl->run_callback(); - cb_hdl->set_call_state(GPI_POST_CALL); - } - gpi_deregister_callback(cb_hdl); + gpi_cb_state_e new_state = cb_hdl->get_call_state(); + + /* We have re-primed in the handler */ + if (new_state != GPI_PRIMED) + if (cb_hdl->cleanup_callback()) + delete cb_hdl; + } }; } // extern "C" @@ -279,10 +293,6 @@ GpiCbHdl *FliImpl::register_nexttime_callback(void) int FliImpl::deregister_callback(GpiCbHdl *gpi_hdl) { - if (gpi_hdl->get_call_state() == GPI_PRE_CALL) { - return 0; - } - int rc = gpi_hdl->cleanup_callback(); // TOOD: Don't delete if it's a re-usable doobery // delete(gpi_hdl); @@ -311,8 +321,10 @@ int FliProcessCbHdl::cleanup_callback(void) { } int FliTimedCbHdl::arm_callback(void) { + LOG_INFO("Creating a new process to sensitise with timer"); m_proc_hdl = mti_CreateProcessWithPriority(NULL, handle_fli_callback, (void *)this, MTI_PROC_IMMEDIATE); mti_ScheduleWakeup(m_proc_hdl, m_time_ps); + LOG_INFO("Wakeup scheduled on %p for %llu", m_proc_hdl, m_time_ps); m_sensitised = true; return 0; } @@ -320,7 +332,7 @@ int FliTimedCbHdl::arm_callback(void) { int FliSignalCbHdl::arm_callback(void) { if (NULL == m_proc_hdl) { - LOG_DEBUG("Creating a new process to sensitise to signal %s", mti_GetSignalName(m_sig_hdl)); + LOG_INFO("Creating a new process to sensitise to signal %s", mti_GetSignalName(m_sig_hdl)); m_proc_hdl = mti_CreateProcess(NULL, handle_fli_callback, (void *)this); } @@ -332,7 +344,7 @@ int FliSignalCbHdl::arm_callback(void) { int FliSimPhaseCbHdl::arm_callback(void) { if (NULL == m_proc_hdl) { - LOG_DEBUG("Creating a new process to sensitise with priority %d", m_priority); + LOG_INFO("Creating a new process to sensitise with priority %d", m_priority); m_proc_hdl = mti_CreateProcessWithPriority(NULL, handle_fli_callback, (void *)this, m_priority); } @@ -341,4 +353,44 @@ int FliSimPhaseCbHdl::arm_callback(void) { return 0; } -GPI_ENTRY_POINT(fli, cocotb_init); \ No newline at end of file +GPI_ENTRY_POINT(fli, cocotb_init); + + + +FliSignalCbHdl *FliSignalObjHdl::value_change_cb(void) { + + LOG_INFO("Creating value change callback for %s", m_name.c_str()); + + if (NULL == m_cb_hdl) { + m_cb_hdl = new FliSignalCbHdl(m_impl, m_fli_hdl); + } + m_cb_hdl->arm_callback(); + + return m_cb_hdl; +} + +GpiCbHdl *FliSignalObjHdl::rising_edge_cb(void) { + LOG_INFO("Creating rising edge callback for %s", m_name.c_str()); + return m_cb_hdl; +} + +GpiCbHdl *FliSignalObjHdl::falling_edge_cb(void) { + + LOG_INFO("Creating falling edge callback for %s", m_name.c_str()); + return m_cb_hdl; +} + + + +const char* FliSignalObjHdl::get_signal_value_binstr(void) { + return "010101"; +} + +int FliSignalObjHdl::set_signal_value(const int value) { + return 0; +} + +int FliSignalObjHdl::set_signal_value(std::string &value) { + return 0; +} + diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index ff098482..c9767a9b 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -31,7 +31,7 @@ #include "../gpi/gpi_priv.h" #include "mti.h" - +// FLI versions of base types class FliObjHdl : public GpiObjHdl { public: FliObjHdl(GpiImplInterface *impl) : GpiObjHdl(impl) { } @@ -45,28 +45,6 @@ class FliObjHdl : public GpiObjHdl { int initialise(std::string &name); }; -class FliRegionObjHdl : public FliObjHdl { -public: - FliRegionObjHdl(GpiImplInterface *impl, mtiRegionIdT hdl) : FliObjHdl(impl), - m_fli_hdl(hdl) { } - virtual ~FliRegionObjHdl() { } - -protected: - mtiRegionIdT m_fli_hdl; -}; - - -class FliSignalObjHdl : public FliObjHdl { -public: - FliSignalObjHdl(GpiImplInterface *impl, mtiSignalIdT hdl) : FliObjHdl(impl), - m_fli_hdl(hdl) { } - virtual ~FliSignalObjHdl() { } -protected: - mtiSignalIdT m_fli_hdl; - -}; - - class FliCbHdl : public GpiCbHdl { public: FliCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl) { } @@ -80,6 +58,10 @@ class FliCbHdl : public GpiCbHdl { }; + + +// Callback handles + // In FLI some callbacks require us to register a process // We use a subclass to track the process state related to the callback class FliProcessCbHdl : public FliCbHdl { @@ -114,7 +96,8 @@ class FliSignalCbHdl : public FliProcessCbHdl { class FliSimPhaseCbHdl : public FliProcessCbHdl { public: - FliSimPhaseCbHdl(GpiImplInterface *impl) : FliProcessCbHdl(impl) { } + FliSimPhaseCbHdl(GpiImplInterface *impl, mtiProcessPriorityT priority) : FliProcessCbHdl(impl), + m_priority(priority) { } virtual ~FliSimPhaseCbHdl() { } int arm_callback(void); @@ -123,6 +106,26 @@ class FliSimPhaseCbHdl : public FliProcessCbHdl { mtiProcessPriorityT m_priority; }; +// FIXME templates? +class FliReadWriteCbHdl : public FliSimPhaseCbHdl { +public: + FliReadWriteCbHdl(GpiImplInterface *impl) : FliSimPhaseCbHdl(impl, MTI_PROC_SYNCH) { } + virtual ~FliReadWriteCbHdl() { } +}; + +class FliNextPhaseCbHdl : public FliSimPhaseCbHdl { +public: + FliNextPhaseCbHdl(GpiImplInterface *impl) : FliSimPhaseCbHdl(impl, MTI_PROC_IMMEDIATE) { } + virtual ~FliNextPhaseCbHdl() { } +}; +class FliReadOnlyCbHdl : public FliSimPhaseCbHdl { +public: + FliReadOnlyCbHdl(GpiImplInterface *impl) : FliSimPhaseCbHdl(impl, MTI_PROC_POSTPONED) { } + virtual ~FliReadOnlyCbHdl() { } +}; + + + class FliTimedCbHdl : public FliProcessCbHdl { public: FliTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : FliProcessCbHdl(impl), m_time_ps(time_ps) {}; @@ -142,6 +145,55 @@ class FliShutdownCbHdl : public FliCbHdl { }; + + + + + + + + + + + + + + +class FliRegionObjHdl : public FliObjHdl { +public: + FliRegionObjHdl(GpiImplInterface *impl, mtiRegionIdT hdl) : FliObjHdl(impl), + m_fli_hdl(hdl) { } + virtual ~FliRegionObjHdl() { } + +protected: + mtiRegionIdT m_fli_hdl; +}; + + +class FliSignalObjHdl : public FliObjHdl, public GpiSignalObjHdl { +public: + FliSignalObjHdl(GpiImplInterface *impl, mtiSignalIdT hdl) : FliObjHdl(impl), + GpiSignalObjHdl(impl), + m_fli_hdl(hdl), + m_cb_hdl(NULL) { } + virtual ~FliSignalObjHdl() { } + + const char* get_signal_value_binstr(void); + int set_signal_value(const int value); + int set_signal_value(std::string &value); + GpiCbHdl *rising_edge_cb(void);// { return NULL; } + GpiCbHdl *falling_edge_cb(void);// { return NULL; } + FliSignalCbHdl *value_change_cb(void); +protected: + mtiSignalIdT m_fli_hdl; + FliSignalCbHdl *m_cb_hdl; +}; + + + + + + class FliImpl : public GpiImplInterface { public: FliImpl(const std::string& name) : GpiImplInterface(name), @@ -170,9 +222,9 @@ class FliImpl : public GpiImplInterface { const char *reason_to_string(int reason); private: - FliSimPhaseCbHdl m_readonly_cbhdl; - FliSimPhaseCbHdl m_nexttime_cbhdl; - FliSimPhaseCbHdl m_readwrite_cbhdl; + FliReadOnlyCbHdl m_readonly_cbhdl; + FliNextPhaseCbHdl m_nexttime_cbhdl; + FliReadWriteCbHdl m_readwrite_cbhdl; }; diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index cc2424fe..cfcc8533 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -44,11 +44,19 @@ SIM_CMD = vsim -c VSIM_ARGS += -onfinish exit endif +ifeq ($(GPI_IMPL),vhpi) +VSIM_ARGS += -foreign \"cocotb_init libfli.so\" -trace_foreign 3 +else +VSIM_ARGS += -pli libvpi.so +endif -runsim.do : $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) +runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) echo "vlib work" > $@ +ifdef $(VERILOG_SOURCES) echo "vlog -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(VERILOG_SOURCES)" >> $@ - echo "vsim -pli libvpi.so $(VSIM_ARGS) work.$(TOPLEVEL)" >> $@ +endif + echo "vcom $(VHDL_ARGS) $(VHDL_SOURCES)" >> $@ + echo "vsim $(VSIM_ARGS) $(TOPLEVEL)" >> $@ ifneq ($(GUI),1) echo "run -all" >> $@ echo "quit" >> $@ From a72d9f454ba116d9020eb165a6ff7d6067588156 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Tue, 18 Nov 2014 20:22:38 +0000 Subject: [PATCH 0667/2656] Issue #141: Disable FLI builds until it compiles --- lib/Makefile | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index 4907c412..40942c59 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -67,13 +67,16 @@ $(LIB_DIR)/libfli.so: $(SIM_ROOT)/lib/fli/FliImpl.cpp | $(LIB_DIR) $(LIB_DIR)/libsim.so: $(SIM_ROOT)/lib/simulator/simulatormodule.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/simulator -COCOTB_LIBS = $(LIB_DIR)/libgpilog.so \ +COCOTB_LIBS := $(LIB_DIR)/libgpilog.so \ $(LIB_DIR)/libcocotb.so \ $(LIB_DIR)/libgpi.so \ $(LIB_DIR)/libvpi.so \ $(LIB_DIR)/libsim.so \ - $(LIB_DIR)/libvhpi.so \ - $(LIB_DIR)/libfli.so + $(LIB_DIR)/libvhpi.so + +ifdef BUILD_FLI +COCOTB_LIBS += $(LIB_DIR)/libfli.so +endif clean:: -@rm -rf $(BUILD_DIR) From 25461b18e5a7ee97216cc71c619190a8fd0499ad Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 18 Nov 2014 19:53:11 +0000 Subject: [PATCH 0668/2656] Issue#161: Fix issue with modelsim double freeing single use callbacks. Some other small changes as a results of messages returned fro the sims --- lib/gpi/GpiCommon.cpp | 4 +-- lib/gpi/gpi_priv.h | 2 ++ lib/vpi/VpiCbHdl.cpp | 69 ++++++++++++++++++++++++++++++++----------- lib/vpi/VpiImpl.cpp | 11 +++---- lib/vpi/VpiImpl.h | 23 +++++++++------ 5 files changed, 73 insertions(+), 36 deletions(-) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index f692a2bb..310a6b50 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -91,10 +91,10 @@ static void gpi_load_libs(std::vector to_load) void gpi_embed_init_python(void) { static bool loading = false; - + if (loading) return; - + /* Lets look at what other libs we where asked to load too */ char *lib_env = getenv("GPI_EXTRA"); diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 5bada2cf..70eedeff 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -161,6 +161,8 @@ class GpiCbHdl : public GpiHdl { const int (*gpi_function)(const void *); // GPI function to callback const void *m_cb_data; // GPI data supplied to "gpi_function" gpi_cb_state_e m_state; // GPI state of the callback through its cycle +public: + std::string m_type; }; /* We would then have */ diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 485f0275..8bb228f0 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -40,16 +40,17 @@ vpiHandle VpiObjHdl::get_handle(void) VpiCbHdl::VpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), vpi_hdl(NULL) { + + vpi_time.high = 0; + vpi_time.low = 0; + vpi_time.type = vpiSimTime; + cb_data.reason = 0; cb_data.cb_rtn = handle_vpi_callback; cb_data.obj = NULL; - cb_data.time = NULL; + cb_data.time = &vpi_time; cb_data.value = NULL; cb_data.user_data = (char*)this; - - vpi_time.high = 0; - vpi_time.low = 0; - vpi_time.type = vpiSimTime; } /* If the user data already has a callback handle then deregister @@ -73,12 +74,14 @@ int VpiCbHdl::arm_callback(void) { } vpiHandle new_hdl = vpi_register_cb(&cb_data); + check_vpi_error(); + int ret = 0; if (!new_hdl) { LOG_CRITICAL("VPI: Unable to register a callback handle for VPI type %s(%d)", m_impl->reason_to_string(cb_data.reason), cb_data.reason); - check_vpi_error(); + ret = -1; } else { m_state = GPI_PRIMED; @@ -94,17 +97,31 @@ int VpiCbHdl::cleanup_callback(void) if (m_state == GPI_FREE) return 0; - if (!vpi_hdl) { - LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); - exit(1); - } + /* If the one-time callback has not come back then + * remove it, it is has then free it. The remove is done + * internally */ + if (m_state == GPI_PRIMED) { + if (!vpi_hdl) { + LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); + exit(1); + } - if (!(vpi_remove_cb(vpi_hdl))) { - LOG_CRITICAL("VPI: unbale to remove callback : ABORTING"); - exit(1); + if (!(vpi_remove_cb(vpi_hdl))) { + LOG_CRITICAL("VPI: unbale to remove callback : ABORTING"); + exit(1); + } + + check_vpi_error(); + } else { +#ifndef MODELSIM + /* This is disabled for now, causes a small leak going to put back in */ + if (!(vpi_free_object(vpi_hdl))) { + LOG_CRITICAL("VPI: unbale to free handle : ABORTING"); + exit(1); + } +#endif } - check_vpi_error(); vpi_hdl = NULL; m_state = GPI_FREE; @@ -184,9 +201,11 @@ VpiValueCbHdl::VpiValueCbHdl(GpiImplInterface *impl, signal(sig) { vpi_time.type = vpiSuppressTime; + m_vpi_value.format = vpiIntVal; cb_data.reason = cbValueChange; cb_data.time = &vpi_time; + cb_data.value = &m_vpi_value; cb_data.obj = signal->get_handle(); } @@ -231,6 +250,23 @@ int VpiValueCbHdl::run_callback(void) return 0; } +int VpiValueCbHdl::cleanup_callback(void) +{ + if (m_state == GPI_FREE) + return 0; + + /* This is a recurring callback so just remove when + * not wanted */ + if (!(vpi_remove_cb(vpi_hdl))) { + LOG_CRITICAL("VPI: unbale to remove callback : ABORTING"); + exit(1); + } + + vpi_hdl = NULL; + m_state = GPI_FREE; + return 0; +} + VpiStartupCbHdl::VpiStartupCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) { cb_data.reason = cbStartOfSimulation; @@ -266,25 +302,22 @@ VpiTimedCbHdl::VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : VpiCbHd { vpi_time.high = (uint32_t)(time_ps>>32); vpi_time.low = (uint32_t)(time_ps); + vpi_time.type = vpiSimTime; cb_data.reason = cbAfterDelay; - cb_data.time = &vpi_time; } VpiReadwriteCbHdl::VpiReadwriteCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) { cb_data.reason = cbReadWriteSynch; - cb_data.time = &vpi_time; } VpiReadOnlyCbHdl::VpiReadOnlyCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) { cb_data.reason = cbReadOnlySynch; - cb_data.time = &vpi_time; } VpiNextPhaseCbHdl::VpiNextPhaseCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) { cb_data.reason = cbNextSimTime; - cb_data.time = &vpi_time; } \ No newline at end of file diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index ab685f4a..2b556a93 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -88,7 +88,7 @@ bool VpiImpl::native_check(std::string &name, GpiObjHdl *parent) ret = false; } - vpi_free_object(vpi_hdl); + //vpi_free_object(vpi_hdl); return ret; } @@ -96,8 +96,6 @@ bool VpiImpl::native_check(std::string &name, GpiObjHdl *parent) GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) { int32_t type; - VpiObjHdl *parent_hdl = sim_to_hdl(parent); - vpiHandle vpi_hdl = parent_hdl->get_handle(); vpiHandle new_hdl; VpiObjHdl *new_obj = NULL; std::vector writable(name.begin(), name.end()); @@ -109,8 +107,7 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) return NULL; if (vpiUnknown == (type = vpi_get(vpiType, new_hdl))) { - vpi_free_object(vpi_hdl); - LOG_WARN("Not a VPI object"); + vpi_free_object(new_hdl); return new_obj; } @@ -195,8 +192,8 @@ GpiObjHdl *VpiImpl::get_root_handle(const char* name) goto error; } - // Need to free the iterator if it didn't return NULL - if (!vpi_free_object(iterator)) { + //Need to free the iterator if it didn't return NULL + if (iterator && !vpi_free_object(iterator)) { LOG_WARN("VPI: Attempting to free root iterator failed!"); check_vpi_error(); } diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index f92f5ab2..1d761aec 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -40,8 +40,10 @@ static inline int __check_vpi_error(const char *func, long line) #if VPI_CHECKING s_vpi_error_info info; int loglevel; + + memset(&info, 0, sizeof(info)); level = vpi_chk_error(&info); - if (level == 0) + if (info.code == 0 && level == 0) return 0; switch (level) { @@ -59,11 +61,11 @@ static inline int __check_vpi_error(const char *func, long line) loglevel = GPICritical; break; default: - return level; + loglevel = GPIWarning; } gpi_log("cocotb.gpi", loglevel, __FILE__, func, line, - "VPI Error level %d\nPROD %s\nCODE %s\nFILE %s", + "VPI Error %s\nPROD %s\nCODE %s\nFILE %s", info.message, info.product, info.code, info.file); #endif @@ -118,11 +120,13 @@ class VpiValueCbHdl : public VpiCbHdl { virtual ~VpiValueCbHdl() { } int run_callback(void); void set_edge(unsigned int edge); + int cleanup_callback(void); private: std::string initial_value; bool rising; bool falling; VpiSignalObjHdl *signal; + s_vpi_value m_vpi_value; }; class VpiTimedCbHdl : public VpiCbHdl { @@ -131,6 +135,7 @@ class VpiTimedCbHdl : public VpiCbHdl { virtual ~VpiTimedCbHdl() { } int cleanup_callback() { VpiCbHdl::cleanup_callback(); + /* Return one so we delete this object */ return 1; } }; @@ -147,6 +152,12 @@ class VpiNextPhaseCbHdl : public VpiCbHdl { virtual ~VpiNextPhaseCbHdl() { } }; +class VpiReadwriteCbHdl : public VpiCbHdl { +public: + VpiReadwriteCbHdl(GpiImplInterface *impl); + virtual ~VpiReadwriteCbHdl() { } +}; + class VpiStartupCbHdl : public VpiCbHdl { public: VpiStartupCbHdl(GpiImplInterface *impl); @@ -169,12 +180,6 @@ class VpiShutdownCbHdl : public VpiCbHdl { virtual ~VpiShutdownCbHdl() { } }; -class VpiReadwriteCbHdl : public VpiCbHdl { -public: - VpiReadwriteCbHdl(GpiImplInterface *impl); - virtual ~VpiReadwriteCbHdl() { } -}; - class VpiSignalObjHdl : public VpiObjHdl, public GpiSignalObjHdl { public: VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl) : VpiObjHdl(impl, hdl), From 79e9411381ac057f48cbef67464be4577be839af Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 19 Nov 2014 15:19:57 +0000 Subject: [PATCH 0669/2656] Cleanup: Move some exports to make smoke tests run from checkout root --- examples/endian_swapper/tests/Makefile | 5 ++--- examples/functionality/tests/Makefile | 2 ++ makefiles/Makefile.inc | 2 -- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index 899793cd..78288c5d 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -31,8 +31,6 @@ # Override this variable to use VHPI instead of VPI TOPLEVEL_LANG ?= verilog - - PWD=$(shell pwd) COCOTB=$(PWD)/../../.. @@ -49,6 +47,8 @@ ifeq ($(TOPLEVEL_LANG),vhdl) GPI_IMPL=vhpi endif +export TOPLEVEL_LANG + MODULE=test_endian_swapper CUSTOM_COMPILE_DEPS = hal @@ -58,7 +58,6 @@ export PYTHONPATH LD_LIBRARY_PATH := $(PWD)/../cosim:$(LD_LIBRARY_PATH) export LD_LIBRARY_PATH - include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim diff --git a/examples/functionality/tests/Makefile b/examples/functionality/tests/Makefile index a23e0f1c..c6f2cfdd 100644 --- a/examples/functionality/tests/Makefile +++ b/examples/functionality/tests/Makefile @@ -43,6 +43,8 @@ else GPI_IMPL := vpi endif +export TOPLEVEL_LANG + MODULE ?= test_cocotb,test_discovery,test_external,test_regression include $(COCOTB)/makefiles/Makefile.inc diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 1131c645..ae40fc14 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -53,8 +53,6 @@ export LIB_DIR=$(BUILD_DIR)/libs/$(ARCH) export LIB_OBJ_DIR:= $(BUILD_DIR)/obj/$(ARCH) export INCLUDES := -I$(SIM_ROOT)/include -I$(PYTHON_INCLUDEDIR) export GPI_EXTRA -export TOPLEVEL_LANG - # Base GCC flags ifeq ($(OS),Darwin) From 1023b2821ca4c99c5efbf31c8c5635da5d413df0 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 19 Nov 2014 17:19:08 +0000 Subject: [PATCH 0670/2656] Cleanup: Fixup modlesim makefile --- makefiles/simulators/Makefile.modelsim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index cfcc8533..08980748 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -52,10 +52,11 @@ endif runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) echo "vlib work" > $@ -ifdef $(VERILOG_SOURCES) +ifneq ($(VERILOG_SOURCES),) echo "vlog -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(VERILOG_SOURCES)" >> $@ -endif +else echo "vcom $(VHDL_ARGS) $(VHDL_SOURCES)" >> $@ +endif echo "vsim $(VSIM_ARGS) $(TOPLEVEL)" >> $@ ifneq ($(GUI),1) echo "run -all" >> $@ From ab237e26c853f80ecc62dd0f4e9b8e70fc20b9bd Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 19 Nov 2014 17:29:40 +0000 Subject: [PATCH 0671/2656] Issue#161: Do not remove a readwrite callback that has not been primed, delay until it fires - Work around for VCS --- lib/vpi/VpiCbHdl.cpp | 4 +++- lib/vpi/VpiImpl.h | 20 +++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 8bb228f0..43d1fa64 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -100,6 +100,7 @@ int VpiCbHdl::cleanup_callback(void) /* If the one-time callback has not come back then * remove it, it is has then free it. The remove is done * internally */ + if (m_state == GPI_PRIMED) { if (!vpi_hdl) { LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); @@ -310,6 +311,7 @@ VpiTimedCbHdl::VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : VpiCbHd VpiReadwriteCbHdl::VpiReadwriteCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) { cb_data.reason = cbReadWriteSynch; + delay_kill = false; } VpiReadOnlyCbHdl::VpiReadOnlyCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) @@ -320,4 +322,4 @@ VpiReadOnlyCbHdl::VpiReadOnlyCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) VpiNextPhaseCbHdl::VpiNextPhaseCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) { cb_data.reason = cbNextSimTime; -} \ No newline at end of file +} diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 1d761aec..e614d598 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -156,6 +156,24 @@ class VpiReadwriteCbHdl : public VpiCbHdl { public: VpiReadwriteCbHdl(GpiImplInterface *impl); virtual ~VpiReadwriteCbHdl() { } + int run_callback(void) { + if (delay_kill) { + delay_kill = false; + return 0; + } else { + return VpiCbHdl::run_callback(); + } + } + int cleanup_callback(void) { + if (m_state == GPI_PRIMED) { + delay_kill = true; + return 0; + } else { + return VpiCbHdl::cleanup_callback(); + } + } +private: + bool delay_kill; }; class VpiStartupCbHdl : public VpiCbHdl { @@ -254,4 +272,4 @@ class VpiImpl : public GpiImplInterface { VpiReadOnlyCbHdl m_read_only; }; -#endif /*COCOTB_VPI_IMPL_H_ */ \ No newline at end of file +#endif /*COCOTB_VPI_IMPL_H_ */ From a45448a5ed3eac9aaad1365fdcdc5826ae456ee5 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 19 Nov 2014 21:59:59 +0000 Subject: [PATCH 0672/2656] Issue#161: VCS exposed error in test, test also needed to have its criteria set up before hand --- examples/functionality/tests/test_cocotb.py | 8 +++++--- lib/vpi/VpiImpl.cpp | 2 +- lib/vpi/VpiImpl.h | 4 ++++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 5e1a30c3..875b7c7a 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -181,7 +181,7 @@ def test_readwrite_in_readonly(dut): if exited is not True: raise cocotb.TestFailed -@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) +@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog", "Chronologic Simulation VCS Release "]) def test_afterdelay_in_readonly(dut): """Test doing invalid sim operation""" global exited @@ -264,7 +264,7 @@ def do_single_edge_check(dut, level): old_value = dut.clk.value.integer dut.log.info("Value of %s is %d" % (dut.clk, old_value)) if old_value is level: - raise TestError("%s not to start with" % (dut.clk, not level)) + raise TestError("%s not to %d start with" % (dut.clk, not level)) if level: yield RisingEdge(dut.clk) else: @@ -272,11 +272,13 @@ def do_single_edge_check(dut, level): new_value = dut.clk.value.integer dut.log.info("Value of %s is %d" % (dut.clk, new_value)) if new_value is not level: - raise TestError("%s not 1 at end" % (dut.clk, level)) + raise TestError("%s not %d at end" % (dut.clk, level)) @cocotb.test() def test_rising_edge(dut): """Test that a rising edge can be yielded on""" + dut.clk <= 0 + yield Timer(1) test = cocotb.fork(do_single_edge_check(dut, 1)) yield Timer(10) dut.clk <= 1 diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 2b556a93..0b61151c 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -448,4 +448,4 @@ void vlog_startup_routines_bootstrap(void) { } -GPI_ENTRY_POINT(vpi, register_embed) \ No newline at end of file +GPI_ENTRY_POINT(vpi, register_embed) diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index e614d598..f7e83c70 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -172,6 +172,10 @@ class VpiReadwriteCbHdl : public VpiCbHdl { return VpiCbHdl::cleanup_callback(); } } + int arm_callback(void) { + delay_kill = false; + return VpiCbHdl::arm_callback(); + } private: bool delay_kill; }; From aef749b924721fb711d9ec950be4457320933ece Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 19 Nov 2014 22:08:32 +0000 Subject: [PATCH 0673/2656] Issue#161: Remove some old debug code --- lib/gpi/gpi_priv.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 70eedeff..f9b4489f 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -130,9 +130,6 @@ class GpiSignalObjHdl : public GpiObjHdl { virtual GpiCbHdl *rising_edge_cb(void) = 0; virtual GpiCbHdl *falling_edge_cb(void) = 0; virtual GpiCbHdl *value_change_cb(unsigned int edge) = 0; - -private: - //GpiCbHdl value_change_cb; }; @@ -161,8 +158,6 @@ class GpiCbHdl : public GpiHdl { const int (*gpi_function)(const void *); // GPI function to callback const void *m_cb_data; // GPI data supplied to "gpi_function" gpi_cb_state_e m_state; // GPI state of the callback through its cycle -public: - std::string m_type; }; /* We would then have */ From 566c59dad130e698ad6fbbb2b02cf49be107ff70 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 19 Nov 2014 22:22:07 +0000 Subject: [PATCH 0674/2656] Issue#161: Print out the registered GPI implementations --- include/gpi.h | 3 +++ lib/embed/gpi_embed.c | 1 + lib/gpi/GpiCommon.cpp | 12 ++++++++++++ 3 files changed, 16 insertions(+) diff --git a/include/gpi.h b/include/gpi.h index 377efb12..6f6b1f14 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -184,6 +184,9 @@ void gpi_deregister_callback(gpi_sim_hdl gpi_hdl); // of GPI we provide a convenience function to extract the callback data void *gpi_get_callback_data(gpi_sim_hdl gpi_hdl); +// Print out what implementations are registered. Python needs to be loaded for this, +// Returns the number of libs +int gpi_print_registered_impl(void); #define GPI_RET(_code) \ if (_code == 1) \ diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 26868865..ff872f9e 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -206,6 +206,7 @@ void embed_sim_init(gpi_sim_info_t *info) goto cleanup; } + gpi_print_registered_impl(); LOG_INFO("Running on %s version %s", info->product, info->version); LOG_INFO("Python interpreter initialised and cocotb loaded!"); diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 310a6b50..3f8929a1 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -37,6 +37,18 @@ using namespace std; static vector registered_impls; +int gpi_print_registered_impl(void) +{ + vector::iterator iter; + for (iter = registered_impls.begin(); + iter != registered_impls.end(); + iter++) + { + LOG_INFO("%s registered", (*iter)->get_name_c()); + } + return registered_impls.size(); +} + int gpi_register_impl(GpiImplInterface *func_tbl) { registered_impls.push_back(func_tbl); From 17b537c1018c0fcc36f321cdb41e29f475382dcb Mon Sep 17 00:00:00 2001 From: Chiggs Date: Wed, 19 Nov 2014 23:02:58 +0000 Subject: [PATCH 0675/2656] Cleanup: Don't print odd messages to log --- examples/endian_swapper/hdl/endian_swapper.vhdl | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/endian_swapper/hdl/endian_swapper.vhdl b/examples/endian_swapper/hdl/endian_swapper.vhdl index a4c7f7b2..096239de 100644 --- a/examples/endian_swapper/hdl/endian_swapper.vhdl +++ b/examples/endian_swapper/hdl/endian_swapper.vhdl @@ -176,9 +176,7 @@ end process; -- Unfortunately this workaround is required for Aldec: Need to schedule an event fake_process :process begin - report "Time 0...."; wait for 50 ns; - report "50 ns has elapsed"; end process; From 3cd4824d2d21db5eb302d034233e8a07e5efcea0 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 21 Nov 2014 09:01:14 +0000 Subject: [PATCH 0676/2656] Cleaup: Use CXX_FLAGS not CPP_FLAGS in lib Makefiles --- lib/fli/Makefile | 2 +- lib/gpi/Makefile | 2 +- lib/vhpi/Makefile | 4 ++-- lib/vpi/Makefile | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/fli/Makefile b/lib/fli/Makefile index 9a80cbd3..b0754205 100644 --- a/lib/fli/Makefile +++ b/lib/fli/Makefile @@ -29,7 +29,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += -I/local/tools/modelsim/10.3c/modelsim_dlx/include -GCC_ARGS += -DFLI_CHECKING +GXX_ARGS += -DFLI_CHECKING LIBS := -lgpilog -lgpi -lstdc++ LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) LIB_NAME := libfli.so diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index c92d79cd..dbd794a3 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -30,7 +30,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += -GCC_ARGS += -DVPI_CHECKING +GXX_ARGS += -DVPI_CHECKING LIBS := -lgpilog -lcocotb -lstdc++ $(EXTRA_LIBS) LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) LIB_NAME := libgpi.so diff --git a/lib/vhpi/Makefile b/lib/vhpi/Makefile index abe72a63..e9f5d52e 100644 --- a/lib/vhpi/Makefile +++ b/lib/vhpi/Makefile @@ -30,7 +30,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += -GCC_ARGS += -DVPI_CHECKING +GXX_ARGS += -DVHPI_CHECKING LIBS := -lgpilog -lgpi -lstdc++ LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) LIB_NAME := libvhpi.so @@ -42,4 +42,4 @@ all: $(LIB_DIR)/$(LIB_NAME) clean: -@rm -rf $(LIB_DIR)/$(LIB_NAME) -include $(SIM_ROOT)/makefiles/Makefile.rules \ No newline at end of file +include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/lib/vpi/Makefile b/lib/vpi/Makefile index 1e26768f..fbc2f422 100644 --- a/lib/vpi/Makefile +++ b/lib/vpi/Makefile @@ -30,7 +30,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += -GCC_ARGS += -DVPI_CHECKING +GXX_ARGS += -DVPI_CHECKING LIBS := -lgpilog -lgpi -lstdc++ LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) LIB_NAME := libvpi.so From 77d2aef81026ce5373174d832a2c2d083d0bd8a3 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 21 Nov 2014 09:02:21 +0000 Subject: [PATCH 0677/2656] Issue#161: Rename gpi_embed_python to gpi_load_extra_libs since this is what it now does --- lib/fli/FliImpl.cpp | 2 +- lib/gpi/GpiCommon.cpp | 3 ++- lib/gpi/gpi_priv.h | 2 +- lib/vhpi/VhpiImpl.cpp | 2 +- lib/vpi/VpiImpl.cpp | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 0e0dc745..f447324b 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -41,7 +41,7 @@ void fli_elab_cb(void *nothing) { fli_table = new FliImpl("FLI"); gpi_register_impl(fli_table); - gpi_embed_init_python(); + gpi_load_extra_libs(); // Elaboration has already happened so jump straight in! gpi_sim_info_t sim_info; diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 3f8929a1..0a3f68ca 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -100,7 +100,7 @@ static void gpi_load_libs(std::vector to_load) } } -void gpi_embed_init_python(void) +void gpi_load_extra_libs(void) { static bool loading = false; @@ -130,6 +130,7 @@ void gpi_embed_init_python(void) gpi_load_libs(to_load); } + /* Finally embed python */ embed_init_python(); } diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index f9b4489f..bcef2e33 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -211,7 +211,7 @@ int gpi_register_impl(GpiImplInterface *func_tbl); void gpi_embed_init(gpi_sim_info_t *info); void gpi_embed_end(void); -void gpi_embed_init_python(void); +void gpi_load_extra_libs(void); typedef const void (*layer_entry_func)(void); diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 80610fc0..ee94c563 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -343,7 +343,7 @@ static void register_embed(void) { vhpi_table = new VhpiImpl("VHPI"); gpi_register_impl(vhpi_table); - gpi_embed_init_python(); + gpi_load_extra_libs(); } // pre-defined VHPI registration table diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 0b61151c..2180139b 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -313,7 +313,7 @@ static void register_embed(void) { vpi_table = new VpiImpl("VPI"); gpi_register_impl(vpi_table); - gpi_embed_init_python(); + gpi_load_extra_libs(); } From fd7630f7c3112b3fa0a606dbdb1465c0117db02f Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 21 Nov 2014 09:02:54 +0000 Subject: [PATCH 0678/2656] Cleanup: Remove duplication of flags from header files --- lib/vhpi/VhpiImpl.h | 2 -- lib/vpi/VpiImpl.h | 2 -- 2 files changed, 4 deletions(-) diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 41b456c2..a56bb8c0 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -31,8 +31,6 @@ #include "../gpi/gpi_priv.h" #include -#define VHPI_CHECKING 1 - // Should be run after every VHPI call to check error status static inline int __check_vhpi_error(const char *func, long line) { diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index f7e83c70..eacfa6b1 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -31,8 +31,6 @@ #include "../gpi/gpi_priv.h" #include -#define VPI_CHECKING 1 - // Should be run after every VPI call to check error status static inline int __check_vpi_error(const char *func, long line) { From 38831d1fb04fc0cb2d1d5ab311fe87d8c7fbdda9 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 21 Nov 2014 09:27:49 +0000 Subject: [PATCH 0679/2656] Cleanup: Remove some old functions and commented out code --- include/gpi.h | 11 +---------- lib/simulator/simulatormodule.c | 3 --- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/include/gpi.h b/include/gpi.h index 6f6b1f14..28f29197 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -119,7 +119,7 @@ void gpi_get_sim_time(uint32_t *high, uint32_t *low); gpi_sim_hdl gpi_get_root_handle(const char *name); gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent); gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index); -void gpi_free_handle(gpi_sim_hdl); +void gpi_free_handle(gpi_sim_hdl gpi_hdl); // Types that can be passed to the iterator. // @@ -141,10 +141,7 @@ gpi_iterator_hdl gpi_iterate(uint32_t type, gpi_sim_hdl base); // Returns NULL when there are no more objects gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator); - - // Functions for querying the properties of a handle - // Caller responsible for freeing the returned string. // This is all slightly verbose but it saves having to enumerate various value types // We only care about a limited subset of values. @@ -172,12 +169,6 @@ gpi_sim_hdl gpi_register_readwrite_callback (const int (*gpi_functi // Calling convention is that 0 = success and negative numbers a failure // For implementers of GPI the provided macro GPI_RET(x) is provided -gpi_sim_hdl gpi_create_cb_handle(void); -void gpi_free_cb_handle(gpi_sim_hdl gpi_hdl); - -gpi_sim_hdl gpi_create_handle(void); -void gpi_free_handle(gpi_sim_hdl gpi_hdl); - void gpi_deregister_callback(gpi_sim_hdl gpi_hdl); // Because the internal structures may be different for different implementations diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 6041809b..6b6b1ecb 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -543,7 +543,6 @@ static PyObject *get_signal_val(PyObject *self, PyObject *args) result = gpi_get_signal_value_binstr(hdl); retstr = Py_BuildValue("s", result); - //free(result); DROP_GIL(gstate); @@ -663,7 +662,6 @@ static PyObject *get_name_string(PyObject *self, PyObject *args) result = gpi_get_signal_name_str((gpi_sim_hdl)hdl); retstr = Py_BuildValue("s", result); - //free(result); DROP_GIL(gstate); @@ -687,7 +685,6 @@ static PyObject *get_type_string(PyObject *self, PyObject *args) result = gpi_get_signal_type_str((gpi_sim_hdl)hdl); retstr = Py_BuildValue("s", result); - //free(result); DROP_GIL(gstate); From 5696ec9d12d4b5222361011ce0ab0cab322368d1 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 21 Nov 2014 09:59:26 +0000 Subject: [PATCH 0680/2656] Issue#161: Remove native_check, been replaced with native_check_create methods --- lib/fli/FliImpl.cpp | 31 ------------------------------- lib/fli/FliImpl.h | 1 - lib/gpi/gpi_priv.h | 1 - lib/vhpi/VhpiImpl.h | 1 - lib/vpi/VpiImpl.cpp | 23 ----------------------- lib/vpi/VpiImpl.h | 1 - 6 files changed, 58 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index f447324b..16cd60cf 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -96,37 +96,6 @@ void FliImpl::sim_end(void) mti_Quit(); } -/** - * @name Native Check - * @brief Determine whether a simulation object is native to FLI - */ -bool FliImpl::native_check(std::string &name, GpiObjHdl *parent) -{ - bool ret = true; - int kind = 0;// = mti_GetRegionKind(); - - // FIXME: Not clear where all these types are defined and how to ascertain - // a definitive mapping here... - switch (kind) { - case accPackage: - case accArchitecture: - ret = true; - break; - case accFunction: - case accModule: - case accForeign: - case accTask: - ret = false; - break; - default: -// LOG_ERROR("Unrecognised kind for %s: %d", name, kind); - ret = false; - break; - } - - return ret; -} - /** * @name Native Check Create * @brief Determine whether a simulation object is native to FLI and create diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index c9767a9b..b18eb426 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -206,7 +206,6 @@ class FliImpl : public GpiImplInterface { void get_sim_time(uint32_t *high, uint32_t *low); /* Hierachy related */ - bool native_check(std::string &name, GpiObjHdl *parent); GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); GpiObjHdl *get_root_handle(const char *name); diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index bcef2e33..7e77f40e 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -187,7 +187,6 @@ class GpiImplInterface { virtual void get_sim_time(uint32_t *high, uint32_t *low) = 0; /* Hierachy related */ - virtual bool native_check(std::string &name, GpiObjHdl *parent) = 0; virtual GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent) = 0; virtual GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent) = 0; virtual GpiObjHdl *get_root_handle(const char *name) = 0; diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index a56bb8c0..377cf4b4 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -231,7 +231,6 @@ class VhpiImpl : public GpiImplInterface { GpiCbHdl *register_nexttime_callback(void); GpiCbHdl *register_readwrite_callback(void); int deregister_callback(GpiCbHdl *obj_hdl); - bool native_check(std::string &name, GpiObjHdl *parent) { return false; } GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 2180139b..6fa88743 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -70,29 +70,6 @@ void VpiImpl::get_sim_time(uint32_t *high, uint32_t *low) *low = vpi_time_s.low; } -/* Not interested in the hierachy for checking that a handle -in in this implementation */ -bool VpiImpl::native_check(std::string &name, GpiObjHdl *parent) -{ - int32_t type; - bool ret = true; - VpiObjHdl *parent_hdl = sim_to_hdl(parent); - vpiHandle vpi_hdl = parent_hdl->get_handle(); - vpiHandle new_hdl; - std::vector writable(name.begin(), name.end()); - writable.push_back('\0'); - - new_hdl = vpi_handle_by_name(&writable[0], vpi_hdl); - - if (vpiUnknown == (type = vpi_get(vpiType, new_hdl))) { - ret = false; - } - - //vpi_free_object(vpi_hdl); - - return ret; -} - GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) { int32_t type; diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index eacfa6b1..3f165ee5 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -261,7 +261,6 @@ class VpiImpl : public GpiImplInterface { GpiCbHdl *register_nexttime_callback(void); GpiCbHdl *register_readwrite_callback(void); int deregister_callback(GpiCbHdl *obj_hdl); - bool native_check(std::string &name, GpiObjHdl *parent); GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); From d81a67d1b052f449a57f4148cdbc4d3d8900d0a0 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 21 Nov 2014 09:59:57 +0000 Subject: [PATCH 0681/2656] Issue#161: No need for free_handle any more, a reference is always held in python for signals and callbacks are static or automatically freed in the GPI layer --- cocotb/handle.py | 4 ---- lib/gpi/GpiCommon.cpp | 6 ------ lib/simulator/simulatormodule.c | 13 ------------- lib/simulator/simulatormodule.h | 2 -- 4 files changed, 25 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 562e698b..a2afeb0f 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -224,10 +224,6 @@ def __iter__(self): hdl = SimHandle(thing) self._sub_handles[hdl.name] = hdl yield hdl - def __del__(self): - """Free handle from gpi that was allocated on construction""" - if self._handle is not None: - simulator.free_handle(self._handle) def __int__(self): return int(self.value) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 0a3f68ca..53f1760a 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -368,12 +368,6 @@ void gpi_deregister_callback(gpi_sim_hdl hdl) cb_hdl->m_impl->deregister_callback(cb_hdl); } -void gpi_free_handle(gpi_sim_hdl hdl) -{ - GpiObjHdl *obj = sim_to_hdl(hdl); - delete(obj); -} - GpiImplInterface::~GpiImplInterface() { } GpiImplInterface::GpiImplInterface(const std::string& name) : m_name(name) { } const char* GpiImplInterface::get_name_c(void) { diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 6b6b1ecb..ac892809 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -714,19 +714,6 @@ static PyObject *get_sim_time(PyObject *self, PyObject *args) return pTuple; } -static PyObject *free_handle(PyObject *self, PyObject *args) -{ - gpi_sim_hdl hdl; - - if (!PyArg_ParseTuple(args, "l", &hdl)) - return NULL; - - gpi_free_handle(hdl); - - return Py_BuildValue("s", "OK!"); -} - - static PyObject *stop_simulator(PyObject *self, PyObject *args) { gpi_sim_end(); diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 4eadd2b6..fcf0ffd5 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -73,7 +73,6 @@ static PyObject *next(PyObject *self, PyObject *args); static PyObject *get_sim_time(PyObject *self, PyObject *args); static PyObject *deregister_callback(PyObject *self, PyObject *args); -static PyObject *free_handle(PyObject *self, PyObject *args); static PyMethodDef SimulatorMethods[] = { {"log_msg", log_msg, METH_VARARGS, "Log a message"}, @@ -92,7 +91,6 @@ static PyMethodDef SimulatorMethods[] = { {"stop_simulator", stop_simulator, METH_VARARGS, "Instruct the attached simulator to stop"}, {"iterate", iterate, METH_VARARGS, "Get an iterator handle to loop over all members in an object"}, {"next", next, METH_VARARGS, "Get the next object from the iterator"}, - {"free_handle", free_handle, METH_VARARGS, "Free a handle"}, // FIXME METH_NOARGS => initialization from incompatible pointer type {"get_sim_time", get_sim_time, METH_VARARGS, "Get the current simulation time as a float"}, From f750c9df878a6ec3090135aa43463bc7c0b5bc1a Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 21 Nov 2014 10:00:29 +0000 Subject: [PATCH 0682/2656] Issue#161: Do not register the same GPI implementation layer twice --- lib/gpi/GpiCommon.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 53f1760a..64683a3d 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -51,6 +51,16 @@ int gpi_print_registered_impl(void) int gpi_register_impl(GpiImplInterface *func_tbl) { + vector::iterator iter; + for (iter = registered_impls.begin(); + iter != registered_impls.end(); + iter++) + { + if ((*iter)->get_name_s() == func_tbl->get_name_s()) { + LOG_WARN("%s already registered, Check GPI_EXTRA", func_tbl->get_name_c()); + return -1; + } + } registered_impls.push_back(func_tbl); return 0; } From 86880527957c9bda24d10621b80b63788a55aa98 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 21 Nov 2014 11:25:57 +0000 Subject: [PATCH 0683/2656] Issue#161: Post merge cleanup of falling and rising edge triggers --- cocotb/handle.py | 5 +- cocotb/triggers.py | 15 ++-- examples/functionality/tests/test_cocotb.py | 79 ++++++++++----------- 3 files changed, 46 insertions(+), 53 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index a2afeb0f..768c905a 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -47,7 +47,7 @@ from cocotb.binary import BinaryValue from cocotb.log import SimLog from cocotb.result import TestError -from cocotb.triggers import _RisingEdge +from cocotb.triggers import _RisingEdge, _FallingEdge class SimHandle(object): @@ -64,7 +64,8 @@ def __init__(self, handle): self.fullname = self.name + '(%s)' % simulator.get_type_string(self._handle) self.log = SimLog('cocotb.' + self.name) self.log.debug("Created!") - self._edge = _RisingEdge(self) + self._r_edge = _RisingEdge(self) + self._f_edge = _FallingEdge(self) def __str__(self): return "%s @0x%x" % (self.name, self._handle) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 503d9b2e..df6bb503 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -213,15 +213,14 @@ def NextTimeStep(): class _RisingOrFallingEdge(Edge): def __init__(self, signal, rising): Edge.__init__(self, signal) - self._rising = rising + if rising == True: + self._rising = 1 + else: + self._rising = 2 def prime(self, callback): if self.cbhdl is None: - if self._rising: - rising = 1; - else: - rising - 0; - self.cbhdl = simulator.register_value_change_callback(self.signal._handle, callback, rising, self) + self.cbhdl = simulator.register_value_change_callback(self.signal._handle, callback, self._rising, self) if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) Trigger.prime(self) @@ -237,7 +236,7 @@ def __init__(self, signal): _RisingOrFallingEdge.__init__(self, signal, rising=True) def RisingEdge(signal): - return signal._edge + return signal._r_edge class _FallingEdge(_RisingOrFallingEdge): """ @@ -247,7 +246,7 @@ def __init__(self, signal): _RisingOrFallingEdge.__init__(self, signal, rising=False) def FallingEdge(signal): - return signal._edge + return signal._f_edge class ClockCycles(Edge): """ diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 397b5f7b..15f25ba1 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -34,7 +34,7 @@ """ import cocotb -from cocotb.triggers import Timer, Join, RisingEdge, Edge, ReadOnly, ReadWrite +from cocotb.triggers import Timer, Join, RisingEdge, FallingEdge, Edge, ReadOnly, ReadWrite from cocotb.clock import Clock from cocotb.result import ReturnValue, TestFailure, TestError @@ -296,19 +296,19 @@ def count_edges_cycles(signal, edges): @cocotb.coroutine def do_single_edge_check(dut, level): - """Do test for rising edge""" - old_value = dut.clk.value.integer - dut.log.info("Value of %s is %d" % (dut.clk, old_value)) - if old_value is level: - raise TestError("%s not to %d start with" % (dut.clk, not level)) - if level: - yield RisingEdge(dut.clk) - else: + """Do test for rising edge""" + old_value = dut.clk.value.integer + dut.log.info("Value of %s is %d" % (dut.clk, old_value)) + if old_value is level: + raise TestError("%s not to %d start with" % (dut.clk, not level)) + if level == 1: + yield RisingEdge(dut.clk) + else: yield FallingEdge(dut.clk) - new_value = dut.clk.value.integer - dut.log.info("Value of %s is %d" % (dut.clk, new_value)) - if new_value is not level: - raise TestError("%s not %d at end" % (dut.clk, level)) + new_value = dut.clk.value.integer + dut.log.info("Value of %s is %d" % (dut.clk, new_value)) + if new_value is not level: + raise TestError("%s not %d at end" % (dut.clk, level)) @cocotb.test() def test_rising_edge(dut): @@ -323,60 +323,53 @@ def test_rising_edge(dut): if result is fail_timer: raise TestError("Test timed out") -@cocotb.test(expect_error=True) +@cocotb.test() def test_falling_edge(dut): """Test that a falling edge can be yielded on""" dut.clk <= 1 - yield Timer(10) + yield Timer(1) test = cocotb.fork(do_single_edge_check(dut, 0)) yield Timer(10) dut.clk <= 0 - fail_time = Timer(1000) + fail_timer = Timer(1000) result = yield [fail_timer, test.join()] if result is fail_timer: raise TestError("Test timed out") -@cocotb.coroutine -def do_either_edge_test(dut): - """Run do either edge test""" - yield Edge(dut.clk) - dut.log.info("Value of %s is %d" % (dut.clk, dut.clk.value.integer)) - #if dut.clk.value.integer is not 1: - # raise TestError("Value should be 0") - yield Edge(dut.clk) - dut.log.info("Value of %s is %d" % (dut.clk, dut.clk.value.integer)) - #if dut.clk.value.integer is not 0: - # raise TestError("Value should be 1") - yield Timer(100) - @cocotb.test() def test_either_edge(dut): """Test that either edge can be triggered on""" dut.clk <= 0 - yield Timer(10) - dut.log.info("First timer") - yield Timer(10) - test = cocotb.fork(do_either_edge_test(dut)) + yield Timer(1) dut.clk <= 1 + yield Edge(dut.clk) + if dut.clk.value.integer is not 1: + raise TestError("Value should be 0") yield Timer(10) - dut.log.info("Second timer") dut.clk <= 0 + yield Edge(dut.clk) + if dut.clk.value.integer is not 0: + raise TestError("Value should be 0") yield Timer(10) - dut.log.info("Third timer") dut.clk <= 1 + yield Edge(dut.clk) + if dut.clk.value.integer is not 1: + raise TestError("Value should be 0") yield Timer(10) - dut.log.info("fourth timer") dut.clk <= 0 + yield Edge(dut.clk) + if dut.clk.value.integer is not 0: + raise TestError("Value should be 0") yield Timer(10) - dut.log.info("Fifth timer") dut.clk <= 1 + yield Edge(dut.clk) + if dut.clk.value.integer is not 1: + raise TestError("Value should be 0") yield Timer(10) - dut.log.info("Sixth timer") - - fail_timer = Timer(1000) - result = yield [fail_timer, test.join()] - if result is fail_timer: - raise TestError("Test timed out") + dut.clk <= 0 + yield Edge(dut.clk) + if dut.clk.value.integer is not 0: + raise TestError("Value should be 0") @cocotb.coroutine def do_clock(dut, limit, period): From bcdfb0843acd1aeeb6ac0b7de548dab5902a1b95 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sat, 22 Nov 2014 12:28:35 +0000 Subject: [PATCH 0684/2656] Fixes Issue#173: Add new exception type and show different error but still go through regression manager so output makes more sense to user. Add another callback type of cbError while at it --- cocotb/__init__.py | 10 +++------- cocotb/regression.py | 18 ++++++++++++++---- cocotb/result.py | 2 ++ cocotb/scheduler.py | 5 +++++ lib/vpi/VpiCbHdl.cpp | 11 +++++++++++ lib/vpi/VpiImpl.cpp | 8 ++++++++ lib/vpi/VpiImpl.h | 11 +++++++++++ 7 files changed, 54 insertions(+), 11 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index f58c3c31..27a17b5d 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -67,11 +67,6 @@ # To save typing provide an alias to scheduler.add fork = scheduler.add -class TestFailed(Exception): - pass - - - # FIXME is this really required? _rlock = threading.RLock() @@ -142,14 +137,15 @@ def _sim_event(level, message): SIM_INFO = 0 SIM_TEST_FAIL = 1 SIM_FAIL = 2 - from cocotb.result import TestFailure + from cocotb.result import TestFailure, SimFailure if level is SIM_TEST_FAIL: scheduler.log.error("Failing test at simulator request") scheduler.finish_test(TestFailure("Failure from external source: %s" % message)) elif level is SIM_FAIL: + # We simply return here as the simulator will exit so no cleanup is needed scheduler.log.error("Failing test at simulator request before test run completion: %s" % message) - scheduler.finish_scheduler(TestFailure("Failing test at simulator request before test run completion")) + scheduler.finish_scheduler(SimFailure("Failing test at simulator request before test run completion %s" % message)) else: scheduler.log.error("Unsupported sim event") diff --git a/cocotb/regression.py b/cocotb/regression.py index 71568709..c32af4a1 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -44,7 +44,7 @@ import cocotb import cocotb.ANSI as ANSI from cocotb.log import SimLog -from cocotb.result import TestError, TestFailure, TestSuccess +from cocotb.result import TestError, TestFailure, TestSuccess, SimFailure from cocotb.xunit_reporter import XUnitReporter def _my_import(name): @@ -126,7 +126,7 @@ def tear_down(self): """It's the end of the world as we know it""" if self.failures: self.log.error("Failed %d out of %d tests (%d skipped)" % - (self.failures, self.count-1, self.skipped)) + (self.failures, self.count -1, self.skipped)) else: self.log.info("Passed %d tests (%d skipped)" % (self.count-1, self.skipped)) @@ -169,17 +169,27 @@ def handle_result(self, result): self.log.error("Test passed but we expected a failure: %s (result was %s)" % ( self._running_test.funcname, result.__class__.__name__)) self.xunit.add_failure(stdout=repr(str(result)), stderr="\n".join(self._running_test.error_messages)) - self.failures += 1 + self.failures += 1 elif isinstance(result, TestError) and self._running_test.expect_error: self.log.info("Test errored as expected: %s (result was %s)" % ( self._running_test.funcname, result.__class__.__name__)) + elif isinstance(result, SimFailure): + if self._running_test.expect_error: + self.log.info("Test errored as expected: %s (result was %s)" % ( + self._running_test.funcname, result.__class__.__name__)) + else: + self.log.error("Test error has lead to simulator shuttting us down") + self.failures += 1 + self.tear_down() + return + else: self.log.error("Test Failed: %s (result was %s)" % ( self._running_test.funcname, result.__class__.__name__)) self.xunit.add_failure(stdout=repr(str(result)), stderr="\n".join(self._running_test.error_messages)) - self.failures += 1 + self.failures += 1 self.execute() diff --git a/cocotb/result.py b/cocotb/result.py index ba3c1b88..ff0ffe04 100644 --- a/cocotb/result.py +++ b/cocotb/result.py @@ -75,3 +75,5 @@ class TestError(TestComplete): pass class TestFailure(TestComplete): pass class TestSuccess(TestComplete): pass + +class SimFailure(TestComplete): pass \ No newline at end of file diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index e912b1b7..9f9abe78 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -526,6 +526,11 @@ def finish_test(self, test_result): self._test_result = test_result self.cleanup() + def finish_scheduler(self, test_result): + """Directly call into the regression manager and end test + once we return the sim will close us so no cleanup is needed""" + self.log.debug("Issue sim closedown result to regresssion object") + cocotb.regression.handle_result(test_result) def cleanup(self): """ diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 43d1fa64..acbfb287 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -299,6 +299,17 @@ int VpiShutdownCbHdl::run_callback(void) { return 0; } +VpiErrorCbHdl::VpiErrorCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) +{ + cb_data.reason = cbError; +} + +int VpiErrorCbHdl::run_callback(void) { + LOG_CRITICAL("Critical error"); + gpi_embed_end(); + return 0; +} + VpiTimedCbHdl::VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : VpiCbHdl(impl) { vpi_time.high = (uint32_t)(time_ps>>32); diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 6fa88743..a6a7b526 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -32,6 +32,7 @@ extern "C" { static VpiCbHdl *sim_init_cb; static VpiCbHdl *sim_finish_cb; +static VpiCbHdl *sim_error_cb; static VpiImpl *vpi_table; } @@ -306,6 +307,12 @@ static void register_final_callback(void) sim_finish_cb->arm_callback(); } +static void register_error_callback(void) +{ + sim_error_cb = new VpiErrorCbHdl(vpi_table); + sim_error_cb->arm_callback(); +} + // Called at compile time to validate the arguments to the system functions // we redefine (info, warning, error, fatal). @@ -406,6 +413,7 @@ void (*vlog_startup_routines[])(void) = { register_embed, register_system_functions, register_initial_callback, + register_error_callback, register_final_callback, 0 }; diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 3f165ee5..11315ee3 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -200,6 +200,17 @@ class VpiShutdownCbHdl : public VpiCbHdl { virtual ~VpiShutdownCbHdl() { } }; +class VpiErrorCbHdl : public VpiCbHdl { +public: + VpiErrorCbHdl(GpiImplInterface *impl); + int run_callback(void); + int cleanup_callback(void) { + /* Too many sims get upset with this so we override to do nothing */ + return 0; + } + virtual ~VpiErrorCbHdl() { } +}; + class VpiSignalObjHdl : public VpiObjHdl, public GpiSignalObjHdl { public: VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl) : VpiObjHdl(impl, hdl), From e73dcba2cff6c9bf4262e5ed3465e82a0a3ac3e7 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 24 Nov 2014 11:05:25 +0000 Subject: [PATCH 0685/2656] Issue#172: Handle edges with a seperate value calback object --- lib/vpi/VpiCbHdl.cpp | 60 +++++++++++++++++++++++--------------------- lib/vpi/VpiImpl.h | 15 +++++------ 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index acbfb287..a01b2114 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -185,20 +185,33 @@ int VpiSignalObjHdl::set_signal_value(std::string &value) GpiCbHdl * VpiSignalObjHdl::value_change_cb(unsigned int edge) { - m_value_cb.set_edge(edge); + VpiValueCbHdl *cb = NULL; + + switch (edge) { + case 1: + cb = &m_rising_cb; + break; + case 2: + cb = &m_falling_cb; + break; + case 3: + cb = &m_either_cb; + break; + default: + return NULL; + } - if (m_value_cb.arm_callback()) { + if (cb->arm_callback()) { return NULL; } - return &m_value_cb; + return cb; } VpiValueCbHdl::VpiValueCbHdl(GpiImplInterface *impl, - VpiSignalObjHdl *sig) : + VpiSignalObjHdl *sig, + int edge) : VpiCbHdl(impl), - rising(false), - falling(false), signal(sig) { vpi_time.type = vpiSuppressTime; @@ -208,39 +221,28 @@ VpiValueCbHdl::VpiValueCbHdl(GpiImplInterface *impl, cb_data.time = &vpi_time; cb_data.value = &m_vpi_value; cb_data.obj = signal->get_handle(); -} - -void VpiValueCbHdl::set_edge(unsigned int edge) -{ - if (edge & 1) - rising = true; - if (edge & 2) - falling = true; + if (edge == (GPI_RISING | GPI_FALLING)) + required_value = "X"; + else if (edge & GPI_RISING) + required_value = "1"; + else if (edge & GPI_FALLING) + required_value = "0"; } int VpiValueCbHdl::run_callback(void) { std::string current_value; - std::string required; - bool pass = false; - if (rising && falling) { - pass = true; - goto check; - } - - current_value = signal->get_signal_value_binstr(); - if (rising && (current_value == "1")) { - pass = true; - goto check; - } + bool pass; - if (falling && (current_value == "0")) { + if (required_value == "X") pass = true; - goto check; + else { + current_value = signal->get_signal_value_binstr(); + if (current_value == required_value) + pass = true; } -check: if (pass) { this->gpi_function(m_cb_data); } else { diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 11315ee3..72fd9603 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -114,15 +114,12 @@ class VpiSignalObjHdl; class VpiValueCbHdl : public VpiCbHdl { public: - VpiValueCbHdl(GpiImplInterface *impl, VpiSignalObjHdl *sig); + VpiValueCbHdl(GpiImplInterface *impl, VpiSignalObjHdl *sig, int edge); virtual ~VpiValueCbHdl() { } int run_callback(void); - void set_edge(unsigned int edge); int cleanup_callback(void); private: - std::string initial_value; - bool rising; - bool falling; + std::string required_value; VpiSignalObjHdl *signal; s_vpi_value m_vpi_value; }; @@ -215,7 +212,9 @@ class VpiSignalObjHdl : public VpiObjHdl, public GpiSignalObjHdl { public: VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl) : VpiObjHdl(impl, hdl), GpiSignalObjHdl(impl), - m_value_cb(impl, this) { } + m_rising_cb(impl, this, GPI_RISING), + m_falling_cb(impl, this, GPI_FALLING), + m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } virtual ~VpiSignalObjHdl() { } const char* get_signal_value_binstr(void); @@ -249,7 +248,9 @@ class VpiSignalObjHdl : public VpiObjHdl, public GpiSignalObjHdl { return VpiObjHdl::initialise(name); } private: - VpiValueCbHdl m_value_cb; + VpiValueCbHdl m_rising_cb; + VpiValueCbHdl m_falling_cb; + VpiValueCbHdl m_either_cb; }; class VpiImpl : public GpiImplInterface { From f9f5b3b0f3aa81073180112f713d0af6beeaa129 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 25 Nov 2014 08:40:03 +0000 Subject: [PATCH 0686/2656] Issue#172: Cleanup some formatting --- lib/gpi/GpiCbHdl.cpp | 2 +- lib/gpi/gpi_priv.h | 3 --- lib/vhpi/VhpiImpl.h | 8 ++------ lib/vpi/VpiCbHdl.cpp | 4 ++-- lib/vpi/VpiImpl.h | 6 +----- 5 files changed, 6 insertions(+), 17 deletions(-) diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index 79349f31..60998a56 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -97,7 +97,7 @@ int GpiCbHdl::run_callback(void) int GpiCbHdl::cleanup_callback(void) { - LOG_WARN("Generic cleanu_handler"); + LOG_WARN("Generic cleanup_handler"); return 0; } diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 7e77f40e..a3662df9 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -126,9 +126,6 @@ class GpiSignalObjHdl : public GpiObjHdl { //virtual GpiCbHdl monitor_value(bool rising_edge) = 0; this was for the triggers // but the explicit ones are probably better - // Also think we want the triggers here? - virtual GpiCbHdl *rising_edge_cb(void) = 0; - virtual GpiCbHdl *falling_edge_cb(void) = 0; virtual GpiCbHdl *value_change_cb(unsigned int edge) = 0; }; diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 377cf4b4..cf8f1f6e 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -178,12 +178,8 @@ class VhpiSignalObjHdl : public VhpiObjHdl, public GpiSignalObjHdl { int set_signal_value(const int value); int set_signal_value(std::string &value); - //virtual GpiCbHdl monitor_value(bool rising_edge) = 0; this was for the triggers - // but the explicit ones are probably better - - // Also think we want the triggers here? - virtual GpiCbHdl *rising_edge_cb(void) { return NULL; } - virtual GpiCbHdl *falling_edge_cb(void) { return NULL; } + + /* Value change callback accessor */ virtual GpiCbHdl *value_change_cb(unsigned int edge); /* Functions that I would like to inherit but do not ?*/ diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index a01b2114..25bce292 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -211,8 +211,8 @@ GpiCbHdl * VpiSignalObjHdl::value_change_cb(unsigned int edge) VpiValueCbHdl::VpiValueCbHdl(GpiImplInterface *impl, VpiSignalObjHdl *sig, int edge) : - VpiCbHdl(impl), - signal(sig) + VpiCbHdl(impl), + signal(sig) { vpi_time.type = vpiSuppressTime; m_vpi_value.format = vpiIntVal; diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 72fd9603..8d7c2cb9 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -221,12 +221,8 @@ class VpiSignalObjHdl : public VpiObjHdl, public GpiSignalObjHdl { int set_signal_value(const int value); int set_signal_value(std::string &value); - //virtual GpiCbHdl monitor_value(bool rising_edge) = 0; this was for the triggers - // but the explicit ones are probably better - // Also think we want the triggers here? - virtual GpiCbHdl *rising_edge_cb(void) { return NULL; } - virtual GpiCbHdl *falling_edge_cb(void) { return NULL; } + /* Value change callback accessor */ virtual GpiCbHdl *value_change_cb(unsigned int edge); /* Functions that I would like to inherit but do not ?*/ From 6579b7d17a9140b05fefae850f51e98961a6faa8 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 25 Nov 2014 11:36:25 +0000 Subject: [PATCH 0687/2656] Issue#172: Push object handle into GpiHdl and push as much code as possible up th GPI --- lib/gpi/GpiCbHdl.cpp | 36 ++++++++++++++++++ lib/gpi/gpi_priv.h | 31 ++++++++++++--- lib/vpi/VpiCbHdl.cpp | 90 ++++++++++++++++---------------------------- lib/vpi/VpiImpl.cpp | 2 +- lib/vpi/VpiImpl.h | 20 +++------- 5 files changed, 101 insertions(+), 78 deletions(-) diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index 60998a56..7cc1cf3d 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -135,4 +135,40 @@ gpi_cb_state_e GpiCbHdl::get_call_state(void) GpiCbHdl::~GpiCbHdl(void) { +} + +GpiValueCbHdl::GpiValueCbHdl(GpiImplInterface *impl, + GpiSignalObjHdl *signal, + int edge) : GpiCbHdl(impl), + m_signal(signal) +{ + if (edge == (GPI_RISING | GPI_FALLING)) + required_value = "X"; + else if (edge & GPI_RISING) + required_value = "1"; + else if (edge & GPI_FALLING) + required_value = "0"; +} + +int GpiValueCbHdl::run_callback(void) +{ + std::string current_value; + bool pass; + + if (required_value == "X") + pass = true; + else { + current_value = m_signal->get_signal_value_binstr(); + if (current_value == required_value) + pass = true; + } + + if (pass) { + this->gpi_function(m_cb_data); + } else { + cleanup_callback(); + arm_callback(); + } + + return 0; } \ No newline at end of file diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index a3662df9..2e0af3bf 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -62,10 +62,15 @@ inline To sim_to_hdl(Ti input) class GpiHdl { public: //GpiHdl() : m_impl(NULL) { } - GpiHdl(GpiImplInterface *impl) : m_impl(impl) { } + GpiHdl(GpiImplInterface *impl, void *hdl) : m_impl(impl), m_obj_hdl(hdl) { } virtual ~GpiHdl() { } virtual int initialise(std::string &name); // Post constructor init + + template T get_handle(void) { + return static_cast(m_obj_hdl); + } + private: GpiHdl() { } // Disable default constructor @@ -73,6 +78,9 @@ class GpiHdl { GpiImplInterface *m_impl; // VPI/VHPI/FLI routines char *gpi_copy_name(const char *name); // Might not be needed bool is_this_impl(GpiImplInterface *impl); // Is the passed interface the one this object uses + +protected: + void *m_obj_hdl; }; /* GPI object handle, maps to a simulation object */ @@ -84,8 +92,9 @@ class GpiHdl { // that construct an object derived from GpiSignalObjHdl or GpiObjHdl class GpiObjHdl : public GpiHdl { public: - GpiObjHdl(std::string name) : GpiHdl(NULL), m_name(name) { } - GpiObjHdl(GpiImplInterface *impl) : GpiHdl(impl) { } + GpiObjHdl(std::string name) : GpiHdl(NULL, NULL), m_name(name) { } + GpiObjHdl(GpiImplInterface *impl) : GpiHdl(impl, NULL) { } + GpiObjHdl(GpiImplInterface *impl, void *hdl) : GpiHdl(impl, hdl) { } virtual ~GpiObjHdl() { } // The following methods permit children below this level of the hierarchy @@ -114,7 +123,7 @@ class GpiObjHdl : public GpiHdl { // value of the signal (which doesn't apply to non signal items in the hierarchy class GpiSignalObjHdl : public GpiObjHdl { public: - GpiSignalObjHdl(GpiImplInterface *impl) : GpiObjHdl(impl) { } + GpiSignalObjHdl(GpiImplInterface *impl, void *hdl) : GpiObjHdl(impl, hdl) { } virtual ~GpiSignalObjHdl() { } // Provide public access to the implementation (composition vs inheritance) virtual const char* get_signal_value_binstr(void) = 0; @@ -135,7 +144,7 @@ class GpiSignalObjHdl : public GpiObjHdl { // vpiHandle/vhpiHandleT for instance. The class GpiCbHdl : public GpiHdl { public: - GpiCbHdl(GpiImplInterface *impl) : GpiHdl(impl), + GpiCbHdl(GpiImplInterface *impl) : GpiHdl(impl, NULL), m_state(GPI_FREE) { } // Pure virtual functions for derived classes virtual int arm_callback(void) = 0; // Register with siumlator @@ -157,6 +166,18 @@ class GpiCbHdl : public GpiHdl { gpi_cb_state_e m_state; // GPI state of the callback through its cycle }; +class GpiValueCbHdl : public virtual GpiCbHdl { +public: + GpiValueCbHdl(GpiImplInterface *impl, GpiSignalObjHdl *signal, int edge); + virtual ~GpiValueCbHdl() { } + virtual int run_callback(void); + virtual int cleanup_callback(void) = 0; + //virtual int arm_callback(void); +protected: + std::string required_value; + GpiSignalObjHdl *m_signal; +}; + /* We would then have */ class GpiClockHdl { public: diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 25bce292..fb57e179 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -32,13 +32,12 @@ extern "C" int32_t handle_vpi_callback(p_cb_data cb_data); -vpiHandle VpiObjHdl::get_handle(void) -{ - return vpi_hdl; -} +//vpiHandle VpiObjHdl::get_handle(void) +//{ +// return m_obj_hdl; +//} -VpiCbHdl::VpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - vpi_hdl(NULL) +VpiCbHdl::VpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl) { vpi_time.high = 0; @@ -66,7 +65,7 @@ int VpiCbHdl::arm_callback(void) { // Only a problem if we have not been asked to deregister and register // in the same simultion callback - if (vpi_hdl != NULL && m_state != GPI_DELETE) { + if (m_obj_hdl != NULL && m_state != GPI_DELETE) { fprintf(stderr, "We seem to already be registered, deregistering %s!\n", m_impl->reason_to_string(cb_data.reason)); @@ -87,7 +86,7 @@ int VpiCbHdl::arm_callback(void) { m_state = GPI_PRIMED; } - vpi_hdl = new_hdl; + m_obj_hdl = new_hdl; return ret; } @@ -102,12 +101,12 @@ int VpiCbHdl::cleanup_callback(void) * internally */ if (m_state == GPI_PRIMED) { - if (!vpi_hdl) { + if (!m_obj_hdl) { LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); exit(1); } - if (!(vpi_remove_cb(vpi_hdl))) { + if (!(vpi_remove_cb(get_handle()))) { LOG_CRITICAL("VPI: unbale to remove callback : ABORTING"); exit(1); } @@ -116,7 +115,7 @@ int VpiCbHdl::cleanup_callback(void) } else { #ifndef MODELSIM /* This is disabled for now, causes a small leak going to put back in */ - if (!(vpi_free_object(vpi_hdl))) { + if (!(vpi_free_object(get_handle()))) { LOG_CRITICAL("VPI: unbale to free handle : ABORTING"); exit(1); } @@ -124,7 +123,7 @@ int VpiCbHdl::cleanup_callback(void) } - vpi_hdl = NULL; + m_obj_hdl = NULL; m_state = GPI_FREE; return 0; @@ -136,7 +135,7 @@ const char* VpiSignalObjHdl::get_signal_value_binstr(void) s_vpi_value value_s = {vpiBinStrVal}; p_vpi_value value_p = &value_s; - vpi_get_value(vpi_hdl, value_p); + vpi_get_value(VpiObjHdl::get_handle(), value_p); check_vpi_error(); return value_p->value.str; @@ -158,7 +157,7 @@ int VpiSignalObjHdl::set_signal_value(int value) vpi_time_s.low = 0; // Use Inertial delay to schedule an event, thus behaving like a verilog testbench - vpi_put_value(vpi_hdl, &value_s, &vpi_time_s, vpiInertialDelay); + vpi_put_value(VpiObjHdl::get_handle(), &value_s, &vpi_time_s, vpiInertialDelay); check_vpi_error(); FEXIT @@ -176,7 +175,7 @@ int VpiSignalObjHdl::set_signal_value(std::string &value) value_s.value.str = &writable[0]; value_s.format = vpiBinStrVal; - vpi_put_value(vpi_hdl, &value_s, NULL, vpiNoDelay); + vpi_put_value(VpiObjHdl::get_handle(), &value_s, NULL, vpiNoDelay); check_vpi_error(); FEXIT @@ -210,9 +209,9 @@ GpiCbHdl * VpiSignalObjHdl::value_change_cb(unsigned int edge) VpiValueCbHdl::VpiValueCbHdl(GpiImplInterface *impl, VpiSignalObjHdl *sig, - int edge) : + int edge) :GpiCbHdl(impl), VpiCbHdl(impl), - signal(sig) + GpiValueCbHdl(impl,sig,edge) { vpi_time.type = vpiSuppressTime; m_vpi_value.format = vpiIntVal; @@ -220,37 +219,7 @@ VpiValueCbHdl::VpiValueCbHdl(GpiImplInterface *impl, cb_data.reason = cbValueChange; cb_data.time = &vpi_time; cb_data.value = &m_vpi_value; - cb_data.obj = signal->get_handle(); - - if (edge == (GPI_RISING | GPI_FALLING)) - required_value = "X"; - else if (edge & GPI_RISING) - required_value = "1"; - else if (edge & GPI_FALLING) - required_value = "0"; -} - -int VpiValueCbHdl::run_callback(void) -{ - std::string current_value; - bool pass; - - if (required_value == "X") - pass = true; - else { - current_value = signal->get_signal_value_binstr(); - if (current_value == required_value) - pass = true; - } - - if (pass) { - this->gpi_function(m_cb_data); - } else { - cleanup_callback(); - arm_callback(); - } - - return 0; + cb_data.obj = m_signal->get_handle(); } int VpiValueCbHdl::cleanup_callback(void) @@ -260,17 +229,18 @@ int VpiValueCbHdl::cleanup_callback(void) /* This is a recurring callback so just remove when * not wanted */ - if (!(vpi_remove_cb(vpi_hdl))) { + if (!(vpi_remove_cb(get_handle()))) { LOG_CRITICAL("VPI: unbale to remove callback : ABORTING"); exit(1); } - vpi_hdl = NULL; + m_obj_hdl = NULL; m_state = GPI_FREE; return 0; } -VpiStartupCbHdl::VpiStartupCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) +VpiStartupCbHdl::VpiStartupCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + VpiCbHdl(impl) { cb_data.reason = cbStartOfSimulation; } @@ -291,7 +261,8 @@ int VpiStartupCbHdl::run_callback(void) { return 0; } -VpiShutdownCbHdl::VpiShutdownCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) +VpiShutdownCbHdl::VpiShutdownCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + VpiCbHdl(impl) { cb_data.reason = cbEndOfSimulation; } @@ -301,7 +272,8 @@ int VpiShutdownCbHdl::run_callback(void) { return 0; } -VpiErrorCbHdl::VpiErrorCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) +VpiErrorCbHdl::VpiErrorCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + VpiCbHdl(impl) { cb_data.reason = cbError; } @@ -312,7 +284,8 @@ int VpiErrorCbHdl::run_callback(void) { return 0; } -VpiTimedCbHdl::VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : VpiCbHdl(impl) +VpiTimedCbHdl::VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : GpiCbHdl(impl), + VpiCbHdl(impl) { vpi_time.high = (uint32_t)(time_ps>>32); vpi_time.low = (uint32_t)(time_ps); @@ -321,18 +294,21 @@ VpiTimedCbHdl::VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : VpiCbHd cb_data.reason = cbAfterDelay; } -VpiReadwriteCbHdl::VpiReadwriteCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) +VpiReadwriteCbHdl::VpiReadwriteCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + VpiCbHdl(impl) { cb_data.reason = cbReadWriteSynch; delay_kill = false; } -VpiReadOnlyCbHdl::VpiReadOnlyCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) +VpiReadOnlyCbHdl::VpiReadOnlyCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + VpiCbHdl(impl) { cb_data.reason = cbReadOnlySynch; } -VpiNextPhaseCbHdl::VpiNextPhaseCbHdl(GpiImplInterface *impl) : VpiCbHdl(impl) +VpiNextPhaseCbHdl::VpiNextPhaseCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + VpiCbHdl(impl) { cb_data.reason = cbNextSimTime; } diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index a6a7b526..f3340955 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -113,7 +113,7 @@ GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) { int32_t type; VpiObjHdl *parent_hdl = sim_to_hdl(parent); - vpiHandle vpi_hdl = parent_hdl->get_handle(); + vpiHandle vpi_hdl = parent_hdl->get_handle(); vpiHandle new_hdl; VpiObjHdl *new_obj = NULL; diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 8d7c2cb9..ddd50ec3 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -80,23 +80,17 @@ class VpiReadOnlyCbHdl; class VpiObjHdl : public GpiObjHdl { public: - VpiObjHdl(GpiImplInterface *impl, vpiHandle hdl) : GpiObjHdl(impl), - vpi_hdl(hdl) { } + VpiObjHdl(GpiImplInterface *impl, vpiHandle hdl) : GpiObjHdl(impl, hdl) + { } virtual ~VpiObjHdl() { } virtual GpiObjHdl *get_handle_by_name(std::string &name) { return NULL; } virtual GpiObjHdl *get_handle_by_index(uint32_t index) { return NULL; } virtual GpiIterator *iterate_handle(uint32_t type) { return NULL ;} virtual GpiObjHdl *next_handle(GpiIterator *iterator) { return NULL; } - - vpiHandle get_handle(void); - -protected: - vpiHandle vpi_hdl; - }; -class VpiCbHdl : public GpiCbHdl { +class VpiCbHdl : public virtual GpiCbHdl { public: VpiCbHdl(GpiImplInterface *impl); virtual ~VpiCbHdl() { } @@ -105,22 +99,18 @@ class VpiCbHdl : public GpiCbHdl { virtual int cleanup_callback(void); protected: - vpiHandle vpi_hdl; s_cb_data cb_data; s_vpi_time vpi_time; }; class VpiSignalObjHdl; -class VpiValueCbHdl : public VpiCbHdl { +class VpiValueCbHdl : public VpiCbHdl, public GpiValueCbHdl { public: VpiValueCbHdl(GpiImplInterface *impl, VpiSignalObjHdl *sig, int edge); virtual ~VpiValueCbHdl() { } - int run_callback(void); int cleanup_callback(void); private: - std::string required_value; - VpiSignalObjHdl *signal; s_vpi_value m_vpi_value; }; @@ -211,7 +201,7 @@ class VpiErrorCbHdl : public VpiCbHdl { class VpiSignalObjHdl : public VpiObjHdl, public GpiSignalObjHdl { public: VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl) : VpiObjHdl(impl, hdl), - GpiSignalObjHdl(impl), + GpiSignalObjHdl(impl, hdl), m_rising_cb(impl, this, GPI_RISING), m_falling_cb(impl, this, GPI_FALLING), m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } From b6f036889f31dffe67eee36939e04012f5848b64 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 25 Nov 2014 18:44:41 +0000 Subject: [PATCH 0688/2656] Issue#172: Apply same fixes for VHPI implementation --- lib/gpi/gpi_priv.h | 2 +- lib/vhpi/VhpiCbHdl.cpp | 125 +++++++++++++++++------------------------ lib/vhpi/VhpiImpl.cpp | 4 +- lib/vhpi/VhpiImpl.h | 32 +++++------ lib/vpi/VpiCbHdl.cpp | 5 -- 5 files changed, 68 insertions(+), 100 deletions(-) diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 2e0af3bf..31294055 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -172,7 +172,7 @@ class GpiValueCbHdl : public virtual GpiCbHdl { virtual ~GpiValueCbHdl() { } virtual int run_callback(void); virtual int cleanup_callback(void) = 0; - //virtual int arm_callback(void); + protected: std::string required_value; GpiSignalObjHdl *m_signal; diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 68ae34bf..f3095c4f 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -30,11 +30,6 @@ extern "C" void handle_vhpi_callback(const vhpiCbDataT *cb_data); -vhpiHandleT VhpiObjHdl::get_handle(void) -{ - return vhpi_hdl; -} - VhpiSignalObjHdl::~VhpiSignalObjHdl() { if (m_value.format == vhpiEnumVecVal || @@ -51,7 +46,7 @@ int VhpiSignalObjHdl::initialise(std::string &name) { m_value.bufSize = 0; m_value.value.str = NULL; - vhpi_get_value(vhpi_hdl, &m_value); + vhpi_get_value(VhpiObjHdl::get_handle(), &m_value); check_vhpi_error(); switch (m_value.format) { @@ -63,7 +58,7 @@ int VhpiSignalObjHdl::initialise(std::string &name) { case vhpiEnumVecVal: case vhpiLogicVecVal: { - m_size = vhpi_get(vhpiSizeP, vhpi_hdl); + m_size = vhpi_get(vhpiSizeP, VhpiObjHdl::get_handle()); m_value.bufSize = m_size*sizeof(vhpiEnumT); m_value.value.enumvs = (vhpiEnumT *)malloc(m_value.bufSize); if (!m_value.value.enumvs) { @@ -84,7 +79,7 @@ int VhpiSignalObjHdl::initialise(std::string &name) { m_binvalue.bufSize = 0; m_binvalue.value.str = NULL; - int new_size = vhpi_get_value(vhpi_hdl, &m_binvalue); + int new_size = vhpi_get_value(VhpiObjHdl::get_handle(), &m_binvalue); m_binvalue.bufSize = new_size*sizeof(vhpiCharT) + 1; m_binvalue.value.str = (vhpiCharT *)calloc(m_binvalue.bufSize, m_binvalue.bufSize); @@ -98,8 +93,7 @@ int VhpiSignalObjHdl::initialise(std::string &name) { return 0; } -VhpiCbHdl::VhpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - vhpi_hdl(NULL) +VhpiCbHdl::VhpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl) { cb_data.reason = 0; cb_data.cb_rtn = handle_vhpi_callback; @@ -119,9 +113,9 @@ int VhpiCbHdl::cleanup_callback(void) if (m_state == GPI_FREE) return 0; - vhpiStateT cbState = (vhpiStateT)vhpi_get(vhpiStateP, vhpi_hdl); + vhpiStateT cbState = (vhpiStateT)vhpi_get(vhpiStateP, get_handle()); if (vhpiEnable == cbState) { - ret = vhpi_disable_cb(vhpi_hdl); + ret = vhpi_disable_cb(get_handle()); m_state = GPI_FREE; } @@ -142,10 +136,10 @@ int VhpiCbHdl::arm_callback(void) /* Do we already have a handle, if so and it is disabled then just re-enable it */ - if (vhpi_hdl) { - cbState = (vhpiStateT)vhpi_get(vhpiStateP, vhpi_hdl); + if (get_handle()) { + cbState = (vhpiStateT)vhpi_get(vhpiStateP, get_handle()); if (vhpiDisable == cbState) { - if (vhpi_enable_cb(vhpi_hdl)) { + if (vhpi_enable_cb(get_handle())) { check_vhpi_error(); ret = -1; } @@ -166,7 +160,7 @@ int VhpiCbHdl::arm_callback(void) LOG_CRITICAL("VHPI ERROR: Registered callback isn't enabled! Got %d\n", cbState); } - vhpi_hdl = new_hdl; + m_obj_hdl = new_hdl; } m_state = GPI_PRIMED; @@ -218,7 +212,7 @@ int VhpiSignalObjHdl::set_signal_value(int value) LOG_CRITICAL("VHPI type of object has changed at runtime, big fail"); } } - vhpi_put_value(vhpi_hdl, &m_value, vhpiForcePropagate); + vhpi_put_value(VhpiObjHdl::get_handle(), &m_value, vhpiForcePropagate); check_vhpi_error(); return 0; } @@ -265,14 +259,14 @@ int VhpiSignalObjHdl::set_signal_value(std::string &value) } } - vhpi_put_value(vhpi_hdl, &m_value, vhpiForcePropagate); + vhpi_put_value(VhpiObjHdl::get_handle(), &m_value, vhpiForcePropagate); check_vhpi_error(); return 0; } const char* VhpiSignalObjHdl::get_signal_value_binstr(void) { - vhpi_get_value(vhpi_hdl, &m_binvalue); + vhpi_get_value(VhpiObjHdl::get_handle(), &m_binvalue); check_vhpi_error(); return m_binvalue.value.str; @@ -280,66 +274,42 @@ const char* VhpiSignalObjHdl::get_signal_value_binstr(void) GpiCbHdl * VhpiSignalObjHdl::value_change_cb(unsigned int edge) { - m_value_cb.set_edge(edge); + VhpiValueCbHdl *cb = NULL; + + switch (edge) { + case 1: + cb = &m_rising_cb; + break; + case 2: + cb = &m_falling_cb; + break; + case 3: + cb = &m_either_cb; + break; + default: + return NULL; + } - if (m_value_cb.arm_callback()) + if (cb->arm_callback()) { return NULL; + } - return &m_value_cb; + return cb; } VhpiValueCbHdl::VhpiValueCbHdl(GpiImplInterface *impl, - VhpiSignalObjHdl *sig) : - VhpiCbHdl(impl), - rising(false), - falling(false), - signal(sig) + VhpiSignalObjHdl *sig, + int edge) : GpiCbHdl(impl), + VhpiCbHdl(impl), + GpiValueCbHdl(impl,sig,edge) { cb_data.reason = vhpiCbValueChange; cb_data.time = &vhpi_time; - cb_data.obj = signal->get_handle(); -} - -void VhpiValueCbHdl::set_edge(unsigned int edge) -{ - if (edge & 1) - rising = true; - - if (edge & 2) - falling = true; -} - -int VhpiValueCbHdl::run_callback(void) -{ - std::string current_value; - std::string required; - bool pass = false; - if (rising && falling) { - pass = true; - goto check; - } - - current_value = signal->get_signal_value_binstr(); - if (rising && (current_value == "1")) { - pass = true; - goto check; - } - - if (falling && (current_value == "0")) { - pass = true; - goto check; - } - -check: - if (pass) - this->gpi_function(m_cb_data); - else - m_state = GPI_PRIMED; - - return 0; + cb_data.obj = m_signal->get_handle(); } -VhpiStartupCbHdl::VhpiStartupCbHdl(GpiImplInterface *impl) : VhpiCbHdl(impl) +VhpiStartupCbHdl::VhpiStartupCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + VhpiCbHdl(impl) { cb_data.reason = vhpiCbStartOfSimulation; } @@ -358,7 +328,8 @@ int VhpiStartupCbHdl::run_callback(void) { return 0; } -VhpiShutdownCbHdl::VhpiShutdownCbHdl(GpiImplInterface *impl) : VhpiCbHdl(impl) +VhpiShutdownCbHdl::VhpiShutdownCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + VhpiCbHdl(impl) { cb_data.reason = vhpiCbEndOfSimulation; } @@ -369,7 +340,8 @@ int VhpiShutdownCbHdl::run_callback(void) { return 0; } -VhpiTimedCbHdl::VhpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : VhpiCbHdl(impl) +VhpiTimedCbHdl::VhpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : GpiCbHdl(impl), + VhpiCbHdl(impl) { vhpi_time.high = (uint32_t)(time_ps>>32); vhpi_time.low = (uint32_t)(time_ps); @@ -383,26 +355,29 @@ int VhpiTimedCbHdl::cleanup_callback(void) if (m_state == GPI_FREE) return 1; - vhpi_remove_cb(vhpi_hdl); + vhpi_remove_cb(get_handle()); - vhpi_hdl = NULL; + m_obj_hdl = NULL; m_state = GPI_FREE; return 1; } -VhpiReadwriteCbHdl::VhpiReadwriteCbHdl(GpiImplInterface *impl) : VhpiCbHdl(impl) +VhpiReadwriteCbHdl::VhpiReadwriteCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + VhpiCbHdl(impl) { cb_data.reason = vhpiCbRepEndOfProcesses; cb_data.time = &vhpi_time; } -VhpiReadOnlyCbHdl::VhpiReadOnlyCbHdl(GpiImplInterface *impl) : VhpiCbHdl(impl) +VhpiReadOnlyCbHdl::VhpiReadOnlyCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + VhpiCbHdl(impl) { cb_data.reason = vhpiCbRepLastKnownDeltaCycle; cb_data.time = &vhpi_time; } -VhpiNextPhaseCbHdl::VhpiNextPhaseCbHdl(GpiImplInterface *impl) : VhpiCbHdl(impl) +VhpiNextPhaseCbHdl::VhpiNextPhaseCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + VhpiCbHdl(impl) { cb_data.reason = vhpiCbRepNextTimeStep; cb_data.time = &vhpi_time; diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index ee94c563..0f5f258f 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -121,7 +121,7 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) { vhpiIntT type; VhpiObjHdl *parent_hdl = sim_to_hdl(parent); - vhpiHandleT vpi_hdl = parent_hdl->get_handle(); + vhpiHandleT vpi_hdl = parent_hdl->get_handle(); vhpiHandleT new_hdl; VhpiObjHdl *new_obj = NULL; unsigned int name_start = 0; @@ -162,7 +162,7 @@ GpiObjHdl *VhpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) { vhpiIntT type; VhpiObjHdl *parent_hdl = sim_to_hdl(parent); - vhpiHandleT vpi_hdl = parent_hdl->get_handle(); + vhpiHandleT vpi_hdl = parent_hdl->get_handle(); vhpiHandleT new_hdl; VhpiObjHdl *new_obj = NULL; diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index cf8f1f6e..ed8c43f4 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -73,24 +73,18 @@ static inline int __check_vhpi_error(const char *func, long line) class VhpiObjHdl : public GpiObjHdl { public: - VhpiObjHdl(GpiImplInterface *impl, vhpiHandleT hdl) : GpiObjHdl(impl), - vhpi_hdl(hdl) { } + VhpiObjHdl(GpiImplInterface *impl, vhpiHandleT hdl) : GpiObjHdl(impl, hdl) + { } virtual ~VhpiObjHdl() { } virtual GpiObjHdl *get_handle_by_name(std::string &name) { return NULL; } virtual GpiObjHdl *get_handle_by_index(uint32_t index) { return NULL; } virtual GpiIterator *iterate_handle(uint32_t type) { return NULL ;} virtual GpiObjHdl *next_handle(GpiIterator *iterator) { return NULL; } - //int initialise(std::string &name); - vhpiHandleT get_handle(void); - - -protected: - vhpiHandleT vhpi_hdl; }; -class VhpiCbHdl : public GpiCbHdl { +class VhpiCbHdl : public virtual GpiCbHdl { public: VhpiCbHdl(GpiImplInterface *impl); virtual ~VhpiCbHdl() { } @@ -99,19 +93,19 @@ class VhpiCbHdl : public GpiCbHdl { virtual int cleanup_callback(void); protected: - vhpiHandleT vhpi_hdl; vhpiCbDataT cb_data; vhpiTimeT vhpi_time; }; class VhpiSignalObjHdl; -class VhpiValueCbHdl : public VhpiCbHdl { +class VhpiValueCbHdl : public VhpiCbHdl, public GpiValueCbHdl { public: - VhpiValueCbHdl(GpiImplInterface *impl, VhpiSignalObjHdl *sig); + VhpiValueCbHdl(GpiImplInterface *impl, VhpiSignalObjHdl *sig, int edge); virtual ~VhpiValueCbHdl() { } - int run_callback(void); - void set_edge(unsigned int edge); + int cleanup_callback(void) { + return VhpiCbHdl::cleanup_callback(); + } private: std::string initial_value; bool rising; @@ -169,9 +163,11 @@ class VhpiReadwriteCbHdl : public VhpiCbHdl { class VhpiSignalObjHdl : public VhpiObjHdl, public GpiSignalObjHdl { public: VhpiSignalObjHdl(GpiImplInterface *impl, vhpiHandleT hdl) : VhpiObjHdl(impl, hdl), - GpiSignalObjHdl(impl), + GpiSignalObjHdl(impl, hdl), m_size(0), - m_value_cb(impl, this) { } + m_rising_cb(impl, this, GPI_RISING), + m_falling_cb(impl, this, GPI_FALLING), + m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } virtual ~VhpiSignalObjHdl(); const char* get_signal_value_binstr(void); @@ -204,7 +200,9 @@ class VhpiSignalObjHdl : public VhpiObjHdl, public GpiSignalObjHdl { unsigned int m_size; vhpiValueT m_value; vhpiValueT m_binvalue; - VhpiValueCbHdl m_value_cb; + VhpiValueCbHdl m_rising_cb; + VhpiValueCbHdl m_falling_cb; + VhpiValueCbHdl m_either_cb; }; class VhpiImpl : public GpiImplInterface { diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index fb57e179..55bd6864 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -32,11 +32,6 @@ extern "C" int32_t handle_vpi_callback(p_cb_data cb_data); -//vpiHandle VpiObjHdl::get_handle(void) -//{ -// return m_obj_hdl; -//} - VpiCbHdl::VpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl) { From 22a3a93bbc8034b6a8266ae2ca96d8051cbf7223 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 25 Nov 2014 20:24:43 +0000 Subject: [PATCH 0689/2656] Cleanup: Fix failure mode of test --- examples/functionality/tests/test_cocotb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 15f25ba1..ec26d420 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -405,4 +405,4 @@ def test_edge_count(dut): yield Timer(clk_period * (edge_count + 1)) if edge_count is not edges_seen: - raise cocotb.TestFailed + raise TestFailure("Correct edge count failed saw %d wanted %d" % (edges_seen, edge_count)) From d688e253d912c901ac9d17ad956abedd81b92153 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 25 Nov 2014 20:30:59 +0000 Subject: [PATCH 0690/2656] Issue#172: Fluke it worked on x86_64, unitialised boolean value --- lib/gpi/GpiCbHdl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index 7cc1cf3d..80318e98 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -153,7 +153,7 @@ GpiValueCbHdl::GpiValueCbHdl(GpiImplInterface *impl, int GpiValueCbHdl::run_callback(void) { std::string current_value; - bool pass; + bool pass = false; if (required_value == "X") pass = true; From bb40d15d6e0cad07853aa65f438bffebf3a9df3f Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 25 Nov 2014 20:54:45 +0000 Subject: [PATCH 0691/2656] Fixes#173: Revert of bcdfb0843a --- lib/vpi/VpiCbHdl.cpp | 12 ------------ lib/vpi/VpiImpl.cpp | 9 --------- lib/vpi/VpiImpl.h | 11 ----------- 3 files changed, 32 deletions(-) diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 55bd6864..121384c1 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -267,18 +267,6 @@ int VpiShutdownCbHdl::run_callback(void) { return 0; } -VpiErrorCbHdl::VpiErrorCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - VpiCbHdl(impl) -{ - cb_data.reason = cbError; -} - -int VpiErrorCbHdl::run_callback(void) { - LOG_CRITICAL("Critical error"); - gpi_embed_end(); - return 0; -} - VpiTimedCbHdl::VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : GpiCbHdl(impl), VpiCbHdl(impl) { diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index f3340955..26ff8cc8 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -32,7 +32,6 @@ extern "C" { static VpiCbHdl *sim_init_cb; static VpiCbHdl *sim_finish_cb; -static VpiCbHdl *sim_error_cb; static VpiImpl *vpi_table; } @@ -307,13 +306,6 @@ static void register_final_callback(void) sim_finish_cb->arm_callback(); } -static void register_error_callback(void) -{ - sim_error_cb = new VpiErrorCbHdl(vpi_table); - sim_error_cb->arm_callback(); -} - - // Called at compile time to validate the arguments to the system functions // we redefine (info, warning, error, fatal). // @@ -413,7 +405,6 @@ void (*vlog_startup_routines[])(void) = { register_embed, register_system_functions, register_initial_callback, - register_error_callback, register_final_callback, 0 }; diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index ddd50ec3..0ea34590 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -187,17 +187,6 @@ class VpiShutdownCbHdl : public VpiCbHdl { virtual ~VpiShutdownCbHdl() { } }; -class VpiErrorCbHdl : public VpiCbHdl { -public: - VpiErrorCbHdl(GpiImplInterface *impl); - int run_callback(void); - int cleanup_callback(void) { - /* Too many sims get upset with this so we override to do nothing */ - return 0; - } - virtual ~VpiErrorCbHdl() { } -}; - class VpiSignalObjHdl : public VpiObjHdl, public GpiSignalObjHdl { public: VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl) : VpiObjHdl(impl, hdl), From fe4be41c5ea65c6bf08e08f2b58b133baac5945a Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 26 Nov 2014 15:09:18 +0000 Subject: [PATCH 0692/2656] Add reorder_depth support to the scoreboard --- cocotb/scoreboard.py | 92 +++++++++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 30 deletions(-) diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index 5b3547b5..c09a2845 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -81,7 +81,58 @@ def result(self): return TestFailure("Errors were recorded during the test") return TestSuccess() - def add_interface(self, monitor, expected_output, compare_fn=None): + + def compare(self, got, exp, log, strict_type=True): + """ + Common function for comparing two transactions. + + Can be re-implemented by a subclass. + """ + + # Compare the types + if strict_type and type(got) != type(exp): + self.errors += 1 + log.error("Received transaction is a different type to expected transaction") + log.info("Got: %s but expected %s" % (str(type(got)), str(type(exp)))) + if self._imm: raise TestFailure("Received transaction of wrong type") + return + # Or convert to a string before comparison + elif not strict_type: + got, exp = str(got), str(exp) + + # Compare directly + if got != exp: + self.errors += 1 + + # Try our best to print out something useful + strgot, strexp = str(got), str(exp) + + log.error("Received transaction differed from expected output") + log.info("Expected:\n" + hexdump(strexp)) + if not isinstance(exp, str): + try: + for word in exp: log.info(str(word)) + except: + pass + log.info("Received:\n" + hexdump(strgot)) + if not isinstance(got, str): + try: + for word in got: log.info(str(word)) + except: + pass + log.warning("Difference:\n%s" % hexdiffs(strexp, strgot)) + if self._imm: raise TestFailure("Received transaction differed from expected transaction") + else: + # Don't want to fail the test if we're passed something without __len__ + try: + log.debug("Received expected transaction %d bytes" % (len(got))) + log.debug(repr(got)) + except: pass + + + + + def add_interface(self, monitor, expected_output, compare_fn=None, reorder_depth=0, strict_type=True): """Add an interface to be scoreboarded. Provides a function which the monitor will callback with received transactions @@ -103,6 +154,8 @@ def add_interface(self, monitor, expected_output, compare_fn=None): return raise TypeError("Expected a callable compare function but got %s" % str(type(compare_fn))) + self.log.info("Created with reorder_depth %d" % reorder_depth) + def check_received_transaction(transaction): """Called back by the monitor when a new transaction has been received""" @@ -110,8 +163,14 @@ def check_received_transaction(transaction): if callable(expected_output): exp = expected_output(transaction) + elif len(expected_output): - exp = expected_output.pop(0) + for i in xrange(min((reorder_depth+1), len(expected_output))): + if expected_output[i] == transaction: + break + else: + i = 0 + exp = expected_output.pop(i) else: self.errors += 1 log.error("Received a transaction but wasn't expecting anything") @@ -119,33 +178,6 @@ def check_received_transaction(transaction): if self._imm: raise TestFailure("Received a transaction but wasn't expecting anything") return - if type(transaction) != type(exp): - self.errors += 1 - log.error("Received transaction is a different type to expected transaction") - log.info("Got: %s but expected %s" % (str(type(transaction)), str(type(exp)))) - if self._imm: raise TestFailure("Received transaction of wrong type") - return - - if transaction != exp: - self.errors += 1 - log.error("Received transaction differed from expected output") - log.info("Expected:\n" + hexdump(exp)) - if not isinstance(exp, str): - try: - for word in exp: self.log.info(str(word)) - except: pass - log.info("Received:\n" + hexdump(transaction)) - if not isinstance(transaction, str): - try: - for word in transaction: self.log.info(str(word)) - except: pass - log.warning("Difference:\n%s" % hexdiffs(exp, transaction)) - if self._imm: raise TestFailure("Received transaction differed from expected transaction") - else: - # Don't want to fail the test if we're passed something without __len__ - try: - log.debug("Received expected transaction %d bytes" % (len(transaction))) - log.debug(repr(transaction)) - except: pass + self.compare(transaction, exp, log, strict_type=strict_type) monitor.add_callback(check_received_transaction) From 9775357a817f798432ceca6e09fc5a3347dd7ef4 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sat, 29 Nov 2014 14:19:18 +0000 Subject: [PATCH 0693/2656] Update Modelsim makefile to use a separate build directory --- makefiles/simulators/Makefile.modelsim | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index 08980748..383e68c1 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -50,7 +50,7 @@ else VSIM_ARGS += -pli libvpi.so endif -runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) +$(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) | $(SIM_BUILD) echo "vlib work" > $@ ifneq ($(VERILOG_SOURCES),) echo "vlog -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(VERILOG_SOURCES)" >> $@ @@ -64,12 +64,11 @@ ifneq ($(GUI),1) endif -results.xml: runsim.do $(COCOTB_LIBS) - LD_LIBRARY_PATH=$(LIB_DIR)::$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ +results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) + cd $(SIM_BUILD) && LD_LIBRARY_PATH=$(LIB_DIR)::$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ $(SIM_CMD) -do runsim.do 2>&1 | tee sim.log clean:: - -rm -rf runsim.do - -rm -rf sim.log + -rm -rf $(SIM_BUILD) From b705bd446c1364c8de22e2e2dc9630b45a83f3fa Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 3 Dec 2014 14:10:07 +0000 Subject: [PATCH 0694/2656] Cleanup: Correct Avalon master double-access due to waitrequest --- cocotb/drivers/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index b6c0c918..431d5c3c 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -227,10 +227,10 @@ def _wait_for_signal(self, signal): so sim will need to move to NextTimeStep before registering more callbacks can occour """ - yield RisingEdge(self.clock) yield ReadOnly() - if signal.value.integer != 1: + while signal.value.integer != 1: yield RisingEdge(signal) + yield ReadOnly() yield NextTimeStep() @coroutine @@ -240,7 +240,6 @@ def _wait_for_nsignal(self, signal): so sim will need to move to NextTimeStep before registering more callbacks can occour """ - yield RisingEdge(self.clock) yield ReadOnly() while signal.value.integer != 0: yield Edge(signal) From 91e2219fa6da6ba65a023a68b574f4aa9c4ac1a8 Mon Sep 17 00:00:00 2001 From: Sigve Tjora Date: Mon, 8 Dec 2014 22:26:29 +0100 Subject: [PATCH 0695/2656] Added missing newlines at end of files This fixes g++ compiler warnings. --- lib/gpi/GpiCbHdl.cpp | 2 +- lib/gpi/GpiCommon.cpp | 2 +- lib/gpi/gpi_priv.h | 2 +- lib/vhpi/VhpiCbHdl.cpp | 2 +- lib/vhpi/VhpiImpl.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index 80318e98..bf9dea9e 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -171,4 +171,4 @@ int GpiValueCbHdl::run_callback(void) } return 0; -} \ No newline at end of file +} diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 64683a3d..48b14857 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -385,4 +385,4 @@ const char* GpiImplInterface::get_name_c(void) { } const string& GpiImplInterface::get_name_s(void) { return m_name; -} \ No newline at end of file +} diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 31294055..f880c7f2 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -242,4 +242,4 @@ typedef const void (*layer_entry_func)(void); } \ } -#endif /* COCOTB_GPI_PRIV_H_ */ \ No newline at end of file +#endif /* COCOTB_GPI_PRIV_H_ */ diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index f3095c4f..1cecb37d 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -381,4 +381,4 @@ VhpiNextPhaseCbHdl::VhpiNextPhaseCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), { cb_data.reason = vhpiCbRepNextTimeStep; cb_data.time = &vhpi_time; -} \ No newline at end of file +} diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index ed8c43f4..0253a54a 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -237,4 +237,4 @@ class VhpiImpl : public GpiImplInterface { VhpiReadOnlyCbHdl m_read_only; }; -#endif /*COCOTB_VHPI_IMPL_H_ */ \ No newline at end of file +#endif /*COCOTB_VHPI_IMPL_H_ */ From a2c1b0694817e188743caf3672c5094e57eed73d Mon Sep 17 00:00:00 2001 From: Sigve Tjora Date: Mon, 8 Dec 2014 22:30:31 +0100 Subject: [PATCH 0696/2656] Remove un-needed const qualifiers on function return types For g++ (GCC) 4.1.2, this fixes warnings like cocotb/include/gpi.h:164: warning: type qualifiers ignored on function return type --- include/gpi.h | 10 +++++----- lib/gpi/GpiCbHdl.cpp | 2 +- lib/gpi/GpiCommon.cpp | 10 +++++----- lib/gpi/gpi_priv.h | 4 ++-- lib/simulator/simulatormodule.c | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/include/gpi.h b/include/gpi.h index 28f29197..912a3e5f 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -161,11 +161,11 @@ typedef enum gpi_edge { } gpi_edge_e; // The callback registering functions -gpi_sim_hdl gpi_register_timed_callback (const int (*gpi_function)(const void *), void *gpi_cb_data, uint64_t time_ps); -gpi_sim_hdl gpi_register_value_change_callback (const int (*gpi_function)(const void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl, unsigned int edge); -gpi_sim_hdl gpi_register_readonly_callback (const int (*gpi_function)(const void *), void *gpi_cb_data); -gpi_sim_hdl gpi_register_nexttime_callback (const int (*gpi_function)(const void *), void *gpi_cb_data); -gpi_sim_hdl gpi_register_readwrite_callback (const int (*gpi_function)(const void *), void *gpi_cb_data); +gpi_sim_hdl gpi_register_timed_callback (int (*gpi_function)(const void *), void *gpi_cb_data, uint64_t time_ps); +gpi_sim_hdl gpi_register_value_change_callback (int (*gpi_function)(const void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl, unsigned int edge); +gpi_sim_hdl gpi_register_readonly_callback (int (*gpi_function)(const void *), void *gpi_cb_data); +gpi_sim_hdl gpi_register_nexttime_callback (int (*gpi_function)(const void *), void *gpi_cb_data); +gpi_sim_hdl gpi_register_readwrite_callback (int (*gpi_function)(const void *), void *gpi_cb_data); // Calling convention is that 0 = success and negative numbers a failure // For implementers of GPI the provided macro GPI_RET(x) is provided diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index bf9dea9e..d843f235 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -107,7 +107,7 @@ int GpiCbHdl::arm_callback(void) return 0; } -int GpiCbHdl::set_user_data(const int (*gpi_function)(const void*), const void *data) +int GpiCbHdl::set_user_data(int (*gpi_function)(const void*), const void *data) { if (!gpi_function) { LOG_ERROR("gpi_function to set_user_data is NULL"); diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 48b14857..88c11a7c 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -278,7 +278,7 @@ void gpi_set_signal_value_str(gpi_sim_hdl sig_hdl, const char *str) obj_hdl->set_signal_value(value); } -gpi_sim_hdl gpi_register_value_change_callback(const int (*gpi_function)(const void *), +gpi_sim_hdl gpi_register_value_change_callback(int (*gpi_function)(const void *), void *gpi_cb_data, gpi_sim_hdl sig_hdl, unsigned int edge) @@ -299,7 +299,7 @@ gpi_sim_hdl gpi_register_value_change_callback(const int (*gpi_function)(const v /* It should not matter which implementation we use for this so just pick the first one */ -gpi_sim_hdl gpi_register_timed_callback(const int (*gpi_function)(const void *), +gpi_sim_hdl gpi_register_timed_callback(int (*gpi_function)(const void *), void *gpi_cb_data, uint64_t time_ps) { GpiCbHdl *gpi_hdl = registered_impls[0]->register_timed_callback(time_ps); @@ -315,7 +315,7 @@ gpi_sim_hdl gpi_register_timed_callback(const int (*gpi_function)(const void *), /* It should not matter which implementation we use for this so just pick the first one */ -gpi_sim_hdl gpi_register_readonly_callback(const int (*gpi_function)(const void *), +gpi_sim_hdl gpi_register_readonly_callback(int (*gpi_function)(const void *), void *gpi_cb_data) { GpiCbHdl *gpi_hdl = registered_impls[0]->register_readonly_callback(); @@ -328,7 +328,7 @@ gpi_sim_hdl gpi_register_readonly_callback(const int (*gpi_function)(const void return (gpi_sim_hdl)gpi_hdl; } -gpi_sim_hdl gpi_register_nexttime_callback(const int (*gpi_function)(const void *), +gpi_sim_hdl gpi_register_nexttime_callback(int (*gpi_function)(const void *), void *gpi_cb_data) { GpiCbHdl *gpi_hdl = registered_impls[0]->register_nexttime_callback(); @@ -344,7 +344,7 @@ gpi_sim_hdl gpi_register_nexttime_callback(const int (*gpi_function)(const void /* It should not matter which implementation we use for this so just pick the first one */ -gpi_sim_hdl gpi_register_readwrite_callback(const int (*gpi_function)(const void *), +gpi_sim_hdl gpi_register_readwrite_callback(int (*gpi_function)(const void *), void *gpi_cb_data) { GpiCbHdl *gpi_hdl = registered_impls[0] ->register_readwrite_callback(); diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index f880c7f2..654ca0cd 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -152,7 +152,7 @@ class GpiCbHdl : public GpiHdl { virtual int cleanup_callback(void) = 0; // Cleanup the callback, arm can be called after // Set the data to be used for run callback, seperate to arm_callback so data can be re-used - int set_user_data(const int (*gpi_function)(const void*), const void *data); + int set_user_data(int (*gpi_function)(const void*), const void *data); const void *get_user_data(void); void set_call_state(gpi_cb_state_e new_state); @@ -161,7 +161,7 @@ class GpiCbHdl : public GpiHdl { virtual ~GpiCbHdl(); protected: - const int (*gpi_function)(const void *); // GPI function to callback + int (*gpi_function)(const void *); // GPI function to callback const void *m_cb_data; // GPI data supplied to "gpi_function" gpi_cb_state_e m_state; // GPI state of the callback through its cycle }; diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index ac892809..e69d0b78 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -39,7 +39,7 @@ static int releases = 0; #include "simulatormodule.h" -typedef const int (*gpi_function_t)(const void *); +typedef int (*gpi_function_t)(const void *); PyGILState_STATE TAKE_GIL(void) { From 4b0bb87c47d7fb67758020c27b0ef5e521c39fc2 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Tue, 9 Dec 2014 21:54:55 +0000 Subject: [PATCH 0697/2656] Cleanup: Improve error reporting for invalid coroutines --- cocotb/decorators.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 93e99f8f..c09795cc 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -77,8 +77,11 @@ class RunningCoroutine(object): coro.kill() will destroy a coroutine instance (and cause any Join triggers to fire """ def __init__(self, inst, parent): - self.__name__ = "%s" % inst.__name__ - self.log = SimLog("cocotb.coroutine.%s" % self.__name__, id(self)) + if hasattr(inst, "__name__"): + self.__name__ = "%s" % inst.__name__ + self.log = SimLog("cocotb.coroutine.%s" % self.__name__, id(self)) + else: + self.log = SimLog("cocotb.coroutine.fail") self._coro = inst self._finished = False self._callbacks = [] @@ -91,7 +94,7 @@ def __init__(self, inst, parent): if not hasattr(self._coro, "send"): self.log.error("%s isn't a value coroutine! Did you use the yield keyword?" - % self.__name__) + % self.funcname) raise CoroutineComplete(callback=self._finished_cb) def __iter__(self): From ccf96e4e1e154267d84726d4cfdeb13e1adf2d38 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 12 Dec 2014 18:29:35 +0000 Subject: [PATCH 0698/2656] Issue #161: Cleanup some rather naff inheritance --- lib/gpi/GpiCbHdl.cpp | 1 + lib/gpi/GpiCommon.cpp | 7 +++++-- lib/gpi/gpi_priv.h | 16 +++++----------- lib/vhpi/VhpiCbHdl.cpp | 16 ++++++++-------- lib/vhpi/VhpiImpl.cpp | 21 +++++++-------------- lib/vhpi/VhpiImpl.h | 38 ++++---------------------------------- lib/vpi/VpiCbHdl.cpp | 6 +++--- lib/vpi/VpiImpl.cpp | 14 +++++++------- lib/vpi/VpiImpl.h | 37 +++---------------------------------- 9 files changed, 43 insertions(+), 113 deletions(-) diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index d843f235..8f424d75 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -84,6 +84,7 @@ int GpiHdl::initialise(std::string &name) int GpiObjHdl::initialise(std::string &name) { m_name = name; + m_type = "unknown"; return 0; } diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 88c11a7c..2c1e7845 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -243,8 +243,11 @@ gpi_iterator_hdl gpi_iterate(uint32_t type, gpi_sim_hdl base) gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) { +#if 0 GpiIterator *iter = sim_to_hdl(iterator); return (gpi_sim_hdl)iter->parent->next_handle(iter); +#endif + return NULL; } const char *gpi_get_signal_value_binstr(gpi_sim_hdl sig_hdl) @@ -357,7 +360,7 @@ gpi_sim_hdl gpi_register_readwrite_callback(int (*gpi_function)(const void *), return (gpi_sim_hdl)gpi_hdl; } -gpi_sim_hdl gpi_create_clock(gpi_sim_hdl *clk_signal, const int period) +gpi_sim_hdl gpi_create_clock(gpi_sim_hdl clk_signal, const int period) { GpiObjHdl *clk_hdl = sim_to_hdl(clk_signal); GpiClockHdl *clock = new GpiClockHdl(clk_hdl); @@ -365,7 +368,7 @@ gpi_sim_hdl gpi_create_clock(gpi_sim_hdl *clk_signal, const int period) return (gpi_sim_hdl)clock; } -void gpi_stop_clock(gpi_sim_hdl *clk_object) +void gpi_stop_clock(gpi_sim_hdl clk_object) { GpiClockHdl *clock = sim_to_hdl(clk_object); clock->stop_clock(); diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 654ca0cd..0e03400a 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -46,10 +46,10 @@ class GpiImplInterface; class GpiIterator; class GpiCbHdl; -template -inline To sim_to_hdl(Ti input) +template +inline To sim_to_hdl(gpi_sim_hdl input) { - To result = reinterpret_cast(input); + To result = static_cast(input); if (!result) { LOG_CRITICAL("GPI: Handle passed down is not valid gpi_sim_hdl"); exit(1); @@ -97,13 +97,6 @@ class GpiObjHdl : public GpiHdl { GpiObjHdl(GpiImplInterface *impl, void *hdl) : GpiHdl(impl, hdl) { } virtual ~GpiObjHdl() { } - // The following methods permit children below this level of the hierarchy - // to be discovered and instantiated - virtual GpiObjHdl *get_handle_by_name(std::string &name) = 0; - virtual GpiObjHdl *get_handle_by_index(uint32_t index) = 0; - virtual GpiIterator *iterate_handle(uint32_t type) = 0; - virtual GpiObjHdl *next_handle(GpiIterator *iterator) = 0; - virtual const char* get_name_str(void); virtual const char* get_type_str(void); const std::string & get_name(void); @@ -123,7 +116,8 @@ class GpiObjHdl : public GpiHdl { // value of the signal (which doesn't apply to non signal items in the hierarchy class GpiSignalObjHdl : public GpiObjHdl { public: - GpiSignalObjHdl(GpiImplInterface *impl, void *hdl) : GpiObjHdl(impl, hdl) { } + GpiSignalObjHdl(GpiImplInterface *impl, void *hdl) : GpiObjHdl(impl, hdl), + m_length(0) { } virtual ~GpiSignalObjHdl() { } // Provide public access to the implementation (composition vs inheritance) virtual const char* get_signal_value_binstr(void) = 0; diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 1cecb37d..6eef65f0 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -46,7 +46,7 @@ int VhpiSignalObjHdl::initialise(std::string &name) { m_value.bufSize = 0; m_value.value.str = NULL; - vhpi_get_value(VhpiObjHdl::get_handle(), &m_value); + vhpi_get_value(GpiObjHdl::get_handle(), &m_value); check_vhpi_error(); switch (m_value.format) { @@ -58,7 +58,7 @@ int VhpiSignalObjHdl::initialise(std::string &name) { case vhpiEnumVecVal: case vhpiLogicVecVal: { - m_size = vhpi_get(vhpiSizeP, VhpiObjHdl::get_handle()); + m_size = vhpi_get(vhpiSizeP, GpiObjHdl::get_handle()); m_value.bufSize = m_size*sizeof(vhpiEnumT); m_value.value.enumvs = (vhpiEnumT *)malloc(m_value.bufSize); if (!m_value.value.enumvs) { @@ -70,7 +70,7 @@ int VhpiSignalObjHdl::initialise(std::string &name) { default: { LOG_CRITICAL("Unable to determine property for %s (%d) format object", - ((VhpiImpl*)VhpiObjHdl::m_impl)->format_to_string(m_value.format), m_value.format); + ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format), m_value.format); } } @@ -79,7 +79,7 @@ int VhpiSignalObjHdl::initialise(std::string &name) { m_binvalue.bufSize = 0; m_binvalue.value.str = NULL; - int new_size = vhpi_get_value(VhpiObjHdl::get_handle(), &m_binvalue); + int new_size = vhpi_get_value(GpiObjHdl::get_handle(), &m_binvalue); m_binvalue.bufSize = new_size*sizeof(vhpiCharT) + 1; m_binvalue.value.str = (vhpiCharT *)calloc(m_binvalue.bufSize, m_binvalue.bufSize); @@ -88,7 +88,7 @@ int VhpiSignalObjHdl::initialise(std::string &name) { LOG_CRITICAL("Unable to alloc mem for read buffer"); } - VhpiObjHdl::initialise(name); + GpiObjHdl::initialise(name); return 0; } @@ -212,7 +212,7 @@ int VhpiSignalObjHdl::set_signal_value(int value) LOG_CRITICAL("VHPI type of object has changed at runtime, big fail"); } } - vhpi_put_value(VhpiObjHdl::get_handle(), &m_value, vhpiForcePropagate); + vhpi_put_value(GpiObjHdl::get_handle(), &m_value, vhpiForcePropagate); check_vhpi_error(); return 0; } @@ -259,14 +259,14 @@ int VhpiSignalObjHdl::set_signal_value(std::string &value) } } - vhpi_put_value(VhpiObjHdl::get_handle(), &m_value, vhpiForcePropagate); + vhpi_put_value(GpiObjHdl::get_handle(), &m_value, vhpiForcePropagate); check_vhpi_error(); return 0; } const char* VhpiSignalObjHdl::get_signal_value_binstr(void) { - vhpi_get_value(VhpiObjHdl::get_handle(), &m_binvalue); + vhpi_get_value(GpiObjHdl::get_handle(), &m_binvalue); check_vhpi_error(); return m_binvalue.value.str; diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 0f5f258f..2a56e6a0 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -25,13 +25,6 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ -// TODO: -// Some functions are completely untested (vhpi_get_handle_by_index) and others -// need optimisation. -// -// VHPI seems to run significantly slower than VPI, need to investigate. - - #include "VhpiImpl.h" #include @@ -120,10 +113,10 @@ void VhpiImpl::get_sim_time(uint32_t *high, uint32_t *low) GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) { vhpiIntT type; - VhpiObjHdl *parent_hdl = sim_to_hdl(parent); + GpiObjHdl *parent_hdl = sim_to_hdl(parent); vhpiHandleT vpi_hdl = parent_hdl->get_handle(); vhpiHandleT new_hdl; - VhpiObjHdl *new_obj = NULL; + GpiObjHdl *new_obj = NULL; unsigned int name_start = 0; std::vector writable(name.begin(), name.end()); writable.push_back('\0'); @@ -146,7 +139,7 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) new_obj = new VhpiSignalObjHdl(this, new_hdl); break; case vhpiCompInstStmtK: - new_obj = new VhpiObjHdl(this, new_hdl); + new_obj = new GpiObjHdl(this, new_hdl); break; default: LOG_DEBUG("Not sure what to do with type %d for entity (%s)", type, name.c_str()); @@ -161,10 +154,10 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) GpiObjHdl *VhpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) { vhpiIntT type; - VhpiObjHdl *parent_hdl = sim_to_hdl(parent); + GpiObjHdl *parent_hdl = sim_to_hdl(parent); vhpiHandleT vpi_hdl = parent_hdl->get_handle(); vhpiHandleT new_hdl; - VhpiObjHdl *new_obj = NULL; + GpiObjHdl *new_obj = NULL; new_hdl = vhpi_handle_by_index(vhpiIndexedNames, vpi_hdl, index); check_vhpi_error(); @@ -184,7 +177,7 @@ GpiObjHdl *VhpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) new_obj = new VhpiSignalObjHdl(this, new_hdl); break; case vhpiCompInstStmtK: - new_obj = new VhpiObjHdl(this, new_hdl); + new_obj = new GpiObjHdl(this, new_hdl); break; default: LOG_DEBUG("Not sure what to do with type %d below entity (%s) at index (%d)", @@ -236,7 +229,7 @@ GpiObjHdl *VhpiImpl::get_root_handle(const char* name) return NULL; } - rv = new VhpiObjHdl(this, root); + rv = new GpiObjHdl(this, root); rv->initialise(root_name); FEXIT diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 0253a54a..51a1a4f7 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -71,19 +71,6 @@ static inline int __check_vhpi_error(const char *func, long line) __check_vhpi_error(__func__, __LINE__); \ } while (0) -class VhpiObjHdl : public GpiObjHdl { -public: - VhpiObjHdl(GpiImplInterface *impl, vhpiHandleT hdl) : GpiObjHdl(impl, hdl) - { } - virtual ~VhpiObjHdl() { } - - virtual GpiObjHdl *get_handle_by_name(std::string &name) { return NULL; } - virtual GpiObjHdl *get_handle_by_index(uint32_t index) { return NULL; } - virtual GpiIterator *iterate_handle(uint32_t type) { return NULL ;} - virtual GpiObjHdl *next_handle(GpiIterator *iterator) { return NULL; } - -}; - class VhpiCbHdl : public virtual GpiCbHdl { public: VhpiCbHdl(GpiImplInterface *impl); @@ -160,10 +147,9 @@ class VhpiReadwriteCbHdl : public VhpiCbHdl { virtual ~VhpiReadwriteCbHdl() { } }; -class VhpiSignalObjHdl : public VhpiObjHdl, public GpiSignalObjHdl { +class VhpiSignalObjHdl : public GpiSignalObjHdl { public: - VhpiSignalObjHdl(GpiImplInterface *impl, vhpiHandleT hdl) : VhpiObjHdl(impl, hdl), - GpiSignalObjHdl(impl, hdl), + VhpiSignalObjHdl(GpiImplInterface *impl, vhpiHandleT hdl) : GpiSignalObjHdl(impl, hdl), m_size(0), m_rising_cb(impl, this, GPI_RISING), m_falling_cb(impl, this, GPI_FALLING), @@ -176,24 +162,8 @@ class VhpiSignalObjHdl : public VhpiObjHdl, public GpiSignalObjHdl { int set_signal_value(std::string &value); /* Value change callback accessor */ - virtual GpiCbHdl *value_change_cb(unsigned int edge); - - /* Functions that I would like to inherit but do not ?*/ - virtual GpiObjHdl *get_handle_by_name(std::string &name) { - return VhpiObjHdl::get_handle_by_name(name); - } - virtual GpiObjHdl *get_handle_by_index(uint32_t index) { - return VhpiObjHdl::get_handle_by_index(index); - } - virtual GpiIterator *iterate_handle(uint32_t type) - { - return VhpiObjHdl::iterate_handle(type); - } - virtual GpiObjHdl *next_handle(GpiIterator *iterator) - { - return VhpiObjHdl::next_handle(iterator); - } - virtual int initialise(std::string &name); + GpiCbHdl *value_change_cb(unsigned int edge); + int initialise(std::string &name); private: const vhpiEnumT chr2vhpi(const char value); diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 121384c1..1386d216 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -130,7 +130,7 @@ const char* VpiSignalObjHdl::get_signal_value_binstr(void) s_vpi_value value_s = {vpiBinStrVal}; p_vpi_value value_p = &value_s; - vpi_get_value(VpiObjHdl::get_handle(), value_p); + vpi_get_value(GpiObjHdl::get_handle(), value_p); check_vpi_error(); return value_p->value.str; @@ -152,7 +152,7 @@ int VpiSignalObjHdl::set_signal_value(int value) vpi_time_s.low = 0; // Use Inertial delay to schedule an event, thus behaving like a verilog testbench - vpi_put_value(VpiObjHdl::get_handle(), &value_s, &vpi_time_s, vpiInertialDelay); + vpi_put_value(GpiObjHdl::get_handle(), &value_s, &vpi_time_s, vpiInertialDelay); check_vpi_error(); FEXIT @@ -170,7 +170,7 @@ int VpiSignalObjHdl::set_signal_value(std::string &value) value_s.value.str = &writable[0]; value_s.format = vpiBinStrVal; - vpi_put_value(VpiObjHdl::get_handle(), &value_s, NULL, vpiNoDelay); + vpi_put_value(GpiObjHdl::get_handle(), &value_s, NULL, vpiNoDelay); check_vpi_error(); FEXIT diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 26ff8cc8..003af269 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -74,7 +74,7 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) { int32_t type; vpiHandle new_hdl; - VpiObjHdl *new_obj = NULL; + GpiObjHdl *new_obj = NULL; std::vector writable(name.begin(), name.end()); writable.push_back('\0'); @@ -96,7 +96,7 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) new_obj = new VpiSignalObjHdl(this, new_hdl); break; case vpiModule: - new_obj = new VpiObjHdl(this, new_hdl); + new_obj = new GpiObjHdl(this, new_hdl); break; default: LOG_DEBUG("Not sure what to do with type %d for entity (%s)", type, name.c_str()); @@ -111,10 +111,10 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) { int32_t type; - VpiObjHdl *parent_hdl = sim_to_hdl(parent); + GpiObjHdl *parent_hdl = sim_to_hdl(parent); vpiHandle vpi_hdl = parent_hdl->get_handle(); vpiHandle new_hdl; - VpiObjHdl *new_obj = NULL; + GpiObjHdl *new_obj = NULL; new_hdl = vpi_handle_by_index(vpi_hdl, index); @@ -133,7 +133,7 @@ GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) new_obj = new VpiSignalObjHdl(this, new_hdl); break; case vpiModule: - new_obj = new VpiObjHdl(this, new_hdl); + new_obj = new GpiObjHdl(this, new_hdl); break; default: LOG_DEBUG("Not sure what to do with type %d below entity (%s) at index (%d)", @@ -151,7 +151,7 @@ GpiObjHdl *VpiImpl::get_root_handle(const char* name) { vpiHandle root; vpiHandle iterator; - VpiObjHdl *rv; + GpiObjHdl *rv; std::string root_name = name; // vpi_iterate with a ref of NULL returns the top level module @@ -175,7 +175,7 @@ GpiObjHdl *VpiImpl::get_root_handle(const char* name) check_vpi_error(); } - rv = new VpiObjHdl(this, root); + rv = new GpiObjHdl(this, root); rv->initialise(root_name); return rv; diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 0ea34590..9c41a4b3 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -78,18 +78,6 @@ class VpiReadwriteCbHdl; class VpiNextPhaseCbHdl; class VpiReadOnlyCbHdl; -class VpiObjHdl : public GpiObjHdl { -public: - VpiObjHdl(GpiImplInterface *impl, vpiHandle hdl) : GpiObjHdl(impl, hdl) - { } - virtual ~VpiObjHdl() { } - - virtual GpiObjHdl *get_handle_by_name(std::string &name) { return NULL; } - virtual GpiObjHdl *get_handle_by_index(uint32_t index) { return NULL; } - virtual GpiIterator *iterate_handle(uint32_t type) { return NULL ;} - virtual GpiObjHdl *next_handle(GpiIterator *iterator) { return NULL; } -}; - class VpiCbHdl : public virtual GpiCbHdl { public: VpiCbHdl(GpiImplInterface *impl); @@ -187,10 +175,9 @@ class VpiShutdownCbHdl : public VpiCbHdl { virtual ~VpiShutdownCbHdl() { } }; -class VpiSignalObjHdl : public VpiObjHdl, public GpiSignalObjHdl { +class VpiSignalObjHdl : public GpiSignalObjHdl { public: - VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl) : VpiObjHdl(impl, hdl), - GpiSignalObjHdl(impl, hdl), + VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl) : GpiSignalObjHdl(impl, hdl), m_rising_cb(impl, this, GPI_RISING), m_falling_cb(impl, this, GPI_FALLING), m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } @@ -202,26 +189,8 @@ class VpiSignalObjHdl : public VpiObjHdl, public GpiSignalObjHdl { int set_signal_value(std::string &value); /* Value change callback accessor */ - virtual GpiCbHdl *value_change_cb(unsigned int edge); + GpiCbHdl *value_change_cb(unsigned int edge); - /* Functions that I would like to inherit but do not ?*/ - virtual GpiObjHdl *get_handle_by_name(std::string &name) { - return VpiObjHdl::get_handle_by_name(name); - } - virtual GpiObjHdl *get_handle_by_index(uint32_t index) { - return VpiObjHdl::get_handle_by_index(index); - } - virtual GpiIterator *iterate_handle(uint32_t type) - { - return VpiObjHdl::iterate_handle(type); - } - virtual GpiObjHdl *next_handle(GpiIterator *iterator) - { - return VpiObjHdl::next_handle(iterator); - } - virtual int initialise(std::string &name) { - return VpiObjHdl::initialise(name); - } private: VpiValueCbHdl m_rising_cb; VpiValueCbHdl m_falling_cb; From 7660df4453f5c29f560a302516cfe77f9c7f701d Mon Sep 17 00:00:00 2001 From: chiggs Date: Sat, 13 Dec 2014 18:22:42 +0000 Subject: [PATCH 0699/2656] Cleanup: Create hierarchical object for VPI structures --- lib/vpi/VpiImpl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 003af269..c7cabae2 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -95,6 +95,7 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) case vpiParameter: new_obj = new VpiSignalObjHdl(this, new_hdl); break; + case vpiStructVar: case vpiModule: new_obj = new GpiObjHdl(this, new_hdl); break; From 35987dbdb9fc15066bd9bcb60886850c8e95b756 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 14 Dec 2014 13:57:42 +0000 Subject: [PATCH 0700/2656] Issue #144: Dynamic loading in now done in our libs to the existing windows work needs enhancements to support dlls, this is the start of this --- makefiles/Makefile.inc | 20 +++++--- makefiles/Makefile.pylib | 25 ++-------- makefiles/Makefile.pylib.Linux | 65 +++++++++++++++++++++++++ makefiles/Makefile.pylib.MINGW32_NT-6.1 | 46 +++++++++++++++++ makefiles/Makefile.rules | 4 +- 5 files changed, 129 insertions(+), 31 deletions(-) create mode 100644 makefiles/Makefile.pylib.Linux create mode 100644 makefiles/Makefile.pylib.MINGW32_NT-6.1 diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index ae40fc14..a8d12a74 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -55,22 +55,26 @@ export INCLUDES := -I$(SIM_ROOT)/include -I$(PYTHON_INCLUDEDIR) export GPI_EXTRA # Base GCC flags +ifeq ($(ARCH),i686) +GXX_ARGS := -m32 +endif ifeq ($(OS),Darwin) -GXX_ARGS := -g -DDEBUG -fpic +GXX_ARGS += -g -DDEBUG -fpic GCC_ARGS := $(GXX_ARGS) -else -ifeq ($(ARCH),i686) -GXX_ARGS := -m32 endif +ifeq ($(OS),MINGW32_NT-6.1) +GXX_ARGS += -g -DDEBUG -shared +GCC_ARGS := $(GCC_ARGS) +endif +ifeq ($(OS),Linux) GXX_ARGS += -Werror -Wcast-qual -Wcast-align -Wwrite-strings \ -Wall -Wno-unused-parameter \ - -fno-common -g -DDEBUG -fpic -GCC_ARGS += $(GXX_ARGS) -Wstrict-prototypes -Waggregate-return + -fno-common -g -DDEBUG +GCC_ARGS = $(GXX_ARGS) -Wstrict-prototypes -Waggregate-return endif - ifeq ($(OS),Darwin) LINKER_ARGS := -shared -undefined suppress -flat_namespace -L$(PYTHON_LIBDIR) else -LINKER_ARGS := -shared -Xlinker -export-dynamic -L$(PYTHON_LIBDIR) +LINKER_ARGS := -shared -Xlinker -L$(PYTHON_LIBDIR) endif diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib index cf7a882d..6d821bb4 100644 --- a/makefiles/Makefile.pylib +++ b/makefiles/Makefile.pylib @@ -34,32 +34,15 @@ PYTHON_PREFIX = $(shell python-config --prefix) NATIVE_ARCH=$(shell uname -m) ARCH?=$(NATIVE_ARCH) -# We might work with other Python versions -PYTHON_VERSION?=$(shell python -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_version()') -PYTHON_LIBDIR:=$(shell python -c 'from distutils import sysconfig; print sysconfig.get_config_var("LIBDIR")') -PYTHON_DYNLIBDIR:=$(shell python -c 'from distutils import sysconfig; print sysconfig.get_config_var("DESTSHARED")') - -# Nasty hack but there's no way in Python properly get the directories for 32-bit Python on a 64-bit system -# TODO: Under OSX we can use "export VERSIONER_PYTHON_PREFER_32_BIT=yes", no Linux equivalent though -ifneq ($(NATIVE_ARCH),$(ARCH)) - PYTHON_LIBDIR:=$(subst 64,,$(PYTHON_LIBDIR)) - PYTHON_DYNLIBDIR:=$(subst 64,,$(PYTHON_DYNLIBDIR)) -endif - -# Since we don't know which modules we might need or whether the simulator -# we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports -# is we simply link against all dynamic libraries in the Python installation -PYLIBS = $(shell python-config --libs) - -PYTHON_INCLUDEDIR := $(shell python -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_inc()') +include $(SIM_ROOT)/makefiles/Makefile.pylib.$(OS) #debug:: # @echo "Debug from Makefile.pylib" # @echo -e "\tUsing PYTHON_VERSION=$(PYTHON_VERSION)" # @echo -e "\tUsing PYTHON_LIBDIR=$(PYTHON_LIBDIR)" -# @echo -e "\tUsing PYTHON_SO_LIB=libpython$(PYTHON_VERSION).so" +# @echo -e "\tUsing PYTHON_DYN_LIB=$(PYTHON_DYN_LIB)" # @echo -e "\tUsing PYTHON_DYNLIBDIR=$(PYTHON_DYNLIBDIR)" # @echo -e "\tUsing PYTHON_INCLUDEDIR=$(PYTHON_INCLUDEDIR)" -# file $(PYTHON_LIBDIR)/libpython$(PYTHON_VERSION).so -# file -L $(PYTHON_LIBDIR)/libpython$(PYTHON_VERSION).so +# file $(PYTHON_LIBDIR)/$(PYTHON_DYN_LIB) +# file -L $(PYTHON_LIBDIR)/$(PYTHON_DYN_LIB) diff --git a/makefiles/Makefile.pylib.Linux b/makefiles/Makefile.pylib.Linux new file mode 100644 index 00000000..cf7a882d --- /dev/null +++ b/makefiles/Makefile.pylib.Linux @@ -0,0 +1,65 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +# All common pyhon related rules + +PYTHON_PREFIX = $(shell python-config --prefix) + +NATIVE_ARCH=$(shell uname -m) +ARCH?=$(NATIVE_ARCH) + +# We might work with other Python versions +PYTHON_VERSION?=$(shell python -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_version()') +PYTHON_LIBDIR:=$(shell python -c 'from distutils import sysconfig; print sysconfig.get_config_var("LIBDIR")') +PYTHON_DYNLIBDIR:=$(shell python -c 'from distutils import sysconfig; print sysconfig.get_config_var("DESTSHARED")') + +# Nasty hack but there's no way in Python properly get the directories for 32-bit Python on a 64-bit system +# TODO: Under OSX we can use "export VERSIONER_PYTHON_PREFER_32_BIT=yes", no Linux equivalent though +ifneq ($(NATIVE_ARCH),$(ARCH)) + PYTHON_LIBDIR:=$(subst 64,,$(PYTHON_LIBDIR)) + PYTHON_DYNLIBDIR:=$(subst 64,,$(PYTHON_DYNLIBDIR)) +endif + +# Since we don't know which modules we might need or whether the simulator +# we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports +# is we simply link against all dynamic libraries in the Python installation +PYLIBS = $(shell python-config --libs) + +PYTHON_INCLUDEDIR := $(shell python -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_inc()') + +#debug:: +# @echo "Debug from Makefile.pylib" +# @echo -e "\tUsing PYTHON_VERSION=$(PYTHON_VERSION)" +# @echo -e "\tUsing PYTHON_LIBDIR=$(PYTHON_LIBDIR)" +# @echo -e "\tUsing PYTHON_SO_LIB=libpython$(PYTHON_VERSION).so" +# @echo -e "\tUsing PYTHON_DYNLIBDIR=$(PYTHON_DYNLIBDIR)" +# @echo -e "\tUsing PYTHON_INCLUDEDIR=$(PYTHON_INCLUDEDIR)" +# file $(PYTHON_LIBDIR)/libpython$(PYTHON_VERSION).so +# file -L $(PYTHON_LIBDIR)/libpython$(PYTHON_VERSION).so + diff --git a/makefiles/Makefile.pylib.MINGW32_NT-6.1 b/makefiles/Makefile.pylib.MINGW32_NT-6.1 new file mode 100644 index 00000000..bce4c61f --- /dev/null +++ b/makefiles/Makefile.pylib.MINGW32_NT-6.1 @@ -0,0 +1,46 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +# All common pyhon related rules + +PYTHON_DIR=/tools/Python27 +PYTHON_BIN=$(PYTHON_DIR)/python.exe + +# We might work with other Python versions +PYTHON_VERSION?=$(shell $(PYTHON_BIN) -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_version()') +PYTHON_DYNLIBDIR:=$(shell $(PYTHON_BIN) -c 'from distutils import sysconfig; print sysconfig.get_config_var("DESTSHARED")') +PYTHON_LIBDIR=$(PYTHON_DIR)/libs + +period := . +empty := +LOCAL_VERSION=$(subst $(period),$(empty),$(PYTHON_VERSION)) +PYTHON_INCLUDEDIR := $(shell $(PYTHON_BIN) -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_inc()') + +PYLIBS = -lPython$(LOCAL_VERSION) +PYTHON_DYN_LIB=Python$(LOCAL_VERSION).lib \ No newline at end of file diff --git a/makefiles/Makefile.rules b/makefiles/Makefile.rules index 5db8a874..9cf9df42 100644 --- a/makefiles/Makefile.rules +++ b/makefiles/Makefile.rules @@ -38,10 +38,10 @@ $(LIB_NAME)_OBJS:= $(patsubst %.c,$(LIB_OBJ_DIR)/%.o,$(filter %.c,$(SRCS))) $(LIB_NAME)_OBJS+= $(patsubst %.cpp,$(LIB_OBJ_DIR)/%.o,$(filter %.cpp,$(SRCS))) $(LIB_OBJ_DIR)/%.o: %.c - gcc $(GCC_ARGS) -c -fpic $(INCLUDES) -o $@ $< + gcc $(GCC_ARGS) -c $(INCLUDES) -o $@ $< $(LIB_OBJ_DIR)/%.o: %.cpp - g++ $(GXX_ARGS) -c -fpic $(INCLUDES) -o $@ $< + g++ $(GXX_ARGS) -c $(INCLUDES) -o $@ $< $(LIB_DIR)/$(LIB_NAME): $($(LIB_NAME)_OBJS) gcc $(GCC_ARGS) $(LINKER_ARGS) -o $@ $($(LIB_NAME)_OBJS) $(LIBS) $(LD_PATH) From c25dc3358310ed37b45692d2d363843315b60af8 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 14 Dec 2014 14:26:19 +0000 Subject: [PATCH 0701/2656] Issue #144: Add back -fpic for Linux builds --- makefiles/Makefile.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index a8d12a74..4a585280 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -69,7 +69,7 @@ endif ifeq ($(OS),Linux) GXX_ARGS += -Werror -Wcast-qual -Wcast-align -Wwrite-strings \ -Wall -Wno-unused-parameter \ - -fno-common -g -DDEBUG + -fno-common -g -DDEBUG -fpic GCC_ARGS = $(GXX_ARGS) -Wstrict-prototypes -Waggregate-return endif From ae7f3d7e11d5d770d59758a6969c2dac152f44fc Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 14 Dec 2014 18:04:09 +0000 Subject: [PATCH 0702/2656] Issue #144: split dlopen from gpi_embed into a util lib, first steps to do windows dynamic loading implementation --- include/cocotb_utils.h | 39 +++++++++++++++++++++++++++++++++++ lib/Makefile | 16 +++++++++------ lib/embed/Makefile | 5 +++-- lib/embed/gpi_embed.c | 11 ++++++---- lib/fli/Makefile | 4 ++-- lib/gpi/Makefile | 4 ++-- lib/gpi_log/Makefile | 4 ++-- lib/simulator/Makefile | 8 ++++---- lib/utils/Makefile | 42 ++++++++++++++++++++++++++++++++++++++ lib/utils/cocotb_utils.c | 44 ++++++++++++++++++++++++++++++++++++++++ lib/vhpi/Makefile | 4 ++-- lib/vpi/Makefile | 14 ++++++------- makefiles/Makefile.inc | 3 ++- makefiles/Makefile.rules | 7 +++++-- 14 files changed, 171 insertions(+), 34 deletions(-) create mode 100644 include/cocotb_utils.h create mode 100644 lib/utils/Makefile create mode 100644 lib/utils/cocotb_utils.c diff --git a/include/cocotb_utils.h b/include/cocotb_utils.h new file mode 100644 index 00000000..2ae589f1 --- /dev/null +++ b/include/cocotb_utils.h @@ -0,0 +1,39 @@ +/****************************************************************************** +* Copyright (c) 2013 Potential Ventures Ltd +* Copyright (c) 2013 SolarFlare Communications Inc +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd, +* SolarFlare Communications Inc nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +#ifndef COCOTB_UTILS_H_ +#define COCOTB_UTILS_H_ + +#ifdef __linux__ +#include +#endif + +void* utils_dyn_open(const char* lib_name); + +#endif /* COCOTB_UTILS_H_ */ \ No newline at end of file diff --git a/lib/Makefile b/lib/Makefile index 40942c59..aefca47d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -67,12 +67,16 @@ $(LIB_DIR)/libfli.so: $(SIM_ROOT)/lib/fli/FliImpl.cpp | $(LIB_DIR) $(LIB_DIR)/libsim.so: $(SIM_ROOT)/lib/simulator/simulatormodule.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/simulator -COCOTB_LIBS := $(LIB_DIR)/libgpilog.so \ - $(LIB_DIR)/libcocotb.so \ - $(LIB_DIR)/libgpi.so \ - $(LIB_DIR)/libvpi.so \ - $(LIB_DIR)/libsim.so \ - $(LIB_DIR)/libvhpi.so +$(LIB_DIR)/libcocotbutils.a: $(SIM_ROOT)/lib/utils/cocotb_utils.c | $(LIB_DIR) + make -C $(SIM_ROOT)/lib/utils + +COCOTB_LIBS := $(LIB_DIR)/libcocotbutils.a \ + $(LIB_DIR)/libgpilog.so \ + $(LIB_DIR)/libcocotb.so \ + $(LIB_DIR)/libgpi.so \ + $(LIB_DIR)/libvpi.so \ + $(LIB_DIR)/libsim.so \ + $(LIB_DIR)/libvhpi.so ifdef BUILD_FLI COCOTB_LIBS += $(LIB_DIR)/libfli.so diff --git a/lib/embed/Makefile b/lib/embed/Makefile index 8380e649..15d907d3 100644 --- a/lib/embed/Makefile +++ b/lib/embed/Makefile @@ -31,13 +31,14 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += GCC_ARGS += -DPYTHON_SO_LIB=libpython$(PYTHON_VERSION).so +ARS += $(LIB_DIR)/libcocotbutils.a LIBS := $(PYLIBS) LD_PATH := -L$(LIB_DIR) SRCS := gpi_embed.c -LIB_NAME := libcocotb.so +LIB_NAME := libcocotb -all: $(LIB_DIR)/$(LIB_NAME) +all: $(LIB_DIR)/$(LIB_NAME).so clean: -@rm -rf $(OBJ_DIR) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index ff872f9e..a6f6bfab 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -19,7 +19,10 @@ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* void * ret = DIRECT, INDIRECT, +if (!ret) { + fprintf(stderr, "Failed to find python lib %s (%s)\n", lib_name, dlerror()); + } INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT @@ -30,7 +33,7 @@ // Embed Python into the simulator using GPI #include -#include +#include #include "embed.h" static PyThreadState *gtstate = NULL; @@ -69,9 +72,9 @@ void embed_init_python(void) if (gtstate) return; - void *ret = dlopen(PY_SO_LIB, RTLD_LAZY | RTLD_GLOBAL); + void * ret = utils_dyn_open(PY_SO_LIB); if (!ret) { - fprintf(stderr, "Failed to find python lib %s (%s)\n", PY_SO_LIB, dlerror()); + fprintf(stderr, "Failed to find python lib\n"); } Py_SetProgramName(progname); diff --git a/lib/fli/Makefile b/lib/fli/Makefile index b0754205..b0dfd441 100644 --- a/lib/fli/Makefile +++ b/lib/fli/Makefile @@ -32,11 +32,11 @@ INCLUDES += -I/local/tools/modelsim/10.3c/modelsim_dlx/include GXX_ARGS += -DFLI_CHECKING LIBS := -lgpilog -lgpi -lstdc++ LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) -LIB_NAME := libfli.so +LIB_NAME := libfli SRCS := FliImpl.cpp -all: $(LIB_DIR)/$(LIB_NAME) +all: $(LIB_DIR)/$(LIB_NAME).so clean: -@rm -rf $(LIB_DIR)/$(LIB_NAME) diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index dbd794a3..53319371 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -33,11 +33,11 @@ INCLUDES += GXX_ARGS += -DVPI_CHECKING LIBS := -lgpilog -lcocotb -lstdc++ $(EXTRA_LIBS) LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) -LIB_NAME := libgpi.so +LIB_NAME := libgpi SRCS := GpiCbHdl.cpp GpiCommon.cpp -all: $(LIB_DIR)/$(LIB_NAME) +all: $(LIB_DIR)/$(LIB_NAME).so ln -sf $(LIB_NAME) $@ clean: diff --git a/lib/gpi_log/Makefile b/lib/gpi_log/Makefile index af11d3ac..dbb64abb 100644 --- a/lib/gpi_log/Makefile +++ b/lib/gpi_log/Makefile @@ -34,9 +34,9 @@ GCC_ARGS += -DFILTER LIBS := $(PYLIBS) SRCS := gpi_logging.c -LIB_NAME := libgpilog.so +LIB_NAME := libgpilog -all: $(LIB_DIR)/$(LIB_NAME) +all: $(LIB_DIR)/$(LIB_NAME).so clean: -@rm -rf $(LIB_DIR)/$(LIB_NAME) diff --git a/lib/simulator/Makefile b/lib/simulator/Makefile index a78606f8..64746ee2 100644 --- a/lib/simulator/Makefile +++ b/lib/simulator/Makefile @@ -33,17 +33,17 @@ INCLUDES += GCC_ARGS += LIBS := -lgpi $(PYLIBS) LD_PATH := -L$(LIB_DIR) -LIB_NAME := libsim.so +LIB_NAME := libsim SRCS := simulatormodule.c CLIBS += $(LIB_DIR)/$(LIB_NAME) -$(LIB_DIR)/simulator.so: $(LIB_DIR)/$(LIB_NAME) - ln -sf $(LIB_NAME) $@ +$(LIB_DIR)/simulator.so: $(LIB_DIR)/$(LIB_NAME).so + ln -sf $(LIB_NAME).so $@ clean: -@rm -rf $(LIB_DIR)/simulator.so - -@rm -rf $(LIB_DIR)/$(LIB_NAME) + -@rm -rf $(LIB_DIR)/$(LIB_NAME).so include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/lib/utils/Makefile b/lib/utils/Makefile new file mode 100644 index 00000000..cacca262 --- /dev/null +++ b/lib/utils/Makefile @@ -0,0 +1,42 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +include $(SIM_ROOT)/makefiles/Makefile.inc + +INCLUDES += +SRCS := cocotb_utils.c + +LIB_NAME := libcocotbutils + +all: $(LIB_DIR)/$(LIB_NAME).a + +clean: + -@rm -rf $(OBJ_DIR) + +include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/lib/utils/cocotb_utils.c b/lib/utils/cocotb_utils.c new file mode 100644 index 00000000..e0739be8 --- /dev/null +++ b/lib/utils/cocotb_utils.c @@ -0,0 +1,44 @@ +/****************************************************************************** +* Copyright (c) 2013 Potential Ventures Ltd +* Copyright (c) 2013 SolarFlare Communications Inc +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd, +* SolarFlare Communications Inc nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +#include +#include + +void* utils_dyn_open(const char* lib_name) { + void *ret = NULL; +#ifdef __linux__ + ret = dlopen(lib_name, RTLD_LAZY | RTLD_GLOBAL); + if (!ret) { + printf("Unable to open lib %s (%s)\n", lib_name, dlerror()); + } +#else + printf("Do something for windows here\n"); +#endif + return ret; +} \ No newline at end of file diff --git a/lib/vhpi/Makefile b/lib/vhpi/Makefile index e9f5d52e..d7a6483d 100644 --- a/lib/vhpi/Makefile +++ b/lib/vhpi/Makefile @@ -33,11 +33,11 @@ INCLUDES += GXX_ARGS += -DVHPI_CHECKING LIBS := -lgpilog -lgpi -lstdc++ LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) -LIB_NAME := libvhpi.so +LIB_NAME := libvhpi SRCS := VhpiImpl.cpp VhpiCbHdl.cpp -all: $(LIB_DIR)/$(LIB_NAME) +all: $(LIB_DIR)/$(LIB_NAME).so clean: -@rm -rf $(LIB_DIR)/$(LIB_NAME) diff --git a/lib/vpi/Makefile b/lib/vpi/Makefile index fbc2f422..f476e8f9 100644 --- a/lib/vpi/Makefile +++ b/lib/vpi/Makefile @@ -33,7 +33,7 @@ INCLUDES += GXX_ARGS += -DVPI_CHECKING LIBS := -lgpilog -lgpi -lstdc++ LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) -LIB_NAME := libvpi.so +LIB_NAME := libvpi SRCS := VpiImpl.cpp VpiCbHdl.cpp @@ -43,16 +43,16 @@ CLIBS += $(LIB_DIR)/gpivpi.vpl # different simulators, icarus for instance loads a .vpi libraray to use the vpi # inerface at the start of the simulation -all: $(LIB_DIR)/gpivpi.vpl $(LIB_DIR)/cocotb.vpi $(IMPS) +all: $(LIB_DIR)/gpivpi.vpl $(LIB_DIR)/cocotb.vpi -$(LIB_DIR)/gpivpi.vpl: $(LIB_DIR)/$(LIB_NAME) - ln -sf $(LIB_NAME) $@ +$(LIB_DIR)/gpivpi.vpl: $(LIB_DIR)/$(LIB_NAME).so + ln -sf $(LIB_NAME).so $@ -$(LIB_DIR)/cocotb.vpi: $(LIB_DIR)/$(LIB_NAME) - ln -sf $(LIB_NAME) $@ +$(LIB_DIR)/cocotb.vpi: $(LIB_DIR)/$(LIB_NAME).so + ln -sf $(LIB_NAME).so $@ clean: - -@rm -rf $(LIB_DIR)/$(LIB_NAME) + -@rm -rf $(LIB_DIR)/$(LIB_NAME).so -@rm -rf $(LIB_DIR)/gpivpi.vpl -@rm -rf $(LIB_DIR)/cocotb.vpi diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 4a585280..c86080bc 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -76,5 +76,6 @@ endif ifeq ($(OS),Darwin) LINKER_ARGS := -shared -undefined suppress -flat_namespace -L$(PYTHON_LIBDIR) else -LINKER_ARGS := -shared -Xlinker -L$(PYTHON_LIBDIR) +SO_LINKER_ARGS := -shared -Xlinker -L$(PYTHON_LIBDIR) +AR_LINKER_ARGS := cr endif diff --git a/makefiles/Makefile.rules b/makefiles/Makefile.rules index 9cf9df42..e02a4bc9 100644 --- a/makefiles/Makefile.rules +++ b/makefiles/Makefile.rules @@ -43,5 +43,8 @@ $(LIB_OBJ_DIR)/%.o: %.c $(LIB_OBJ_DIR)/%.o: %.cpp g++ $(GXX_ARGS) -c $(INCLUDES) -o $@ $< -$(LIB_DIR)/$(LIB_NAME): $($(LIB_NAME)_OBJS) - gcc $(GCC_ARGS) $(LINKER_ARGS) -o $@ $($(LIB_NAME)_OBJS) $(LIBS) $(LD_PATH) +$(LIB_DIR)/$(LIB_NAME).so: $($(LIB_NAME)_OBJS) + gcc $(GCC_ARGS) $(SO_LINKER_ARGS) -o $@ $($(LIB_NAME)_OBJS) $(ARS) $(LIBS) $(LD_PATH) + +$(LIB_DIR)/$(LIB_NAME).a: $($(LIB_NAME)_OBJS) + ar $(AR_LINKER_ARGS) -o $@ $($(LIB_NAME)_OBJS) $(LIBS) $(LD_PATH) \ No newline at end of file From 34a9a6c8af0c7290d183b5714e1ac2c2962019b3 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 14 Dec 2014 18:25:23 +0000 Subject: [PATCH 0703/2656] Issue #144: Taken some direction from "elgorwi" and allowed the extenstion for a shared lib to be based on the OS --- lib/Makefile | 30 +++++++++++++++--------------- lib/embed/Makefile | 6 +++--- lib/fli/Makefile | 2 +- lib/gpi/Makefile | 2 +- lib/gpi_log/Makefile | 4 ++-- lib/simulator/Makefile | 8 ++++---- lib/vhpi/Makefile | 2 +- lib/vpi/Makefile | 10 +++++----- makefiles/Makefile.inc | 3 +++ makefiles/Makefile.rules | 2 +- 10 files changed, 36 insertions(+), 33 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index aefca47d..88fa08ec 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -37,7 +37,7 @@ ifndef GPI_IMPL endif -# FIXME it's a bit nasty to have the source files listed here as dependencies +# FIXME it's a bit nasty to have the.$(LIB_EXT)urce files listed here as dependencies # but better than re-building the libraries every time. $(LIB_OBJ_DIR): @@ -46,40 +46,40 @@ $(LIB_OBJ_DIR): $(LIB_DIR): $(LIB_OBJ_DIR) mkdir -p $@ -$(LIB_DIR)/libgpilog.so: $(SIM_ROOT)/lib/gpi_log/gpi_logging.c | $(LIB_DIR) +$(LIB_DIR)/libgpilog.$(LIB_EXT): $(SIM_ROOT)/lib/gpi_log/gpi_logging.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/gpi_log -$(LIB_DIR)/libcocotb.so: $(SIM_ROOT)/lib/embed/gpi_embed.c | $(LIB_DIR) +$(LIB_DIR)/libcocotb.$(LIB_EXT): $(SIM_ROOT)/lib/embed/gpi_embed.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/embed -$(LIB_DIR)/libvpi.so: $(SIM_ROOT)/lib/vpi/VpiImpl.cpp $(SIM_ROOT)/lib/vpi/VpiCbHdl.cpp | $(LIB_DIR) +$(LIB_DIR)/libvpi.$(LIB_EXT): $(SIM_ROOT)/lib/vpi/VpiImpl.cpp $(SIM_ROOT)/lib/vpi/VpiCbHdl.cpp | $(LIB_DIR) make -C $(SIM_ROOT)/lib/vpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) -$(LIB_DIR)/libvhpi.so: $(SIM_ROOT)/lib/vhpi/VhpiImpl.cpp $(SIM_ROOT)/lib/vhpi/VhpiCbHdl.cpp | $(LIB_DIR) +$(LIB_DIR)/libvhpi.$(LIB_EXT): $(SIM_ROOT)/lib/vhpi/VhpiImpl.cpp $(SIM_ROOT)/lib/vhpi/VhpiCbHdl.cpp | $(LIB_DIR) make -C $(SIM_ROOT)/lib/vhpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) -$(LIB_DIR)/libgpi.so: $(SIM_ROOT)/lib/gpi/GpiCommon.cpp $(SIM_ROOT)/lib/gpi/GpiCbHdl.cpp | $(LIB_DIR) +$(LIB_DIR)/libgpi.$(LIB_EXT): $(SIM_ROOT)/lib/gpi/GpiCommon.cpp $(SIM_ROOT)/lib/gpi/GpiCbHdl.cpp | $(LIB_DIR) make -C $(SIM_ROOT)/lib/gpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) -$(LIB_DIR)/libfli.so: $(SIM_ROOT)/lib/fli/FliImpl.cpp | $(LIB_DIR) +$(LIB_DIR)/libfli.$(LIB_EXT): $(SIM_ROOT)/lib/fli/FliImpl.cpp | $(LIB_DIR) make -C $(SIM_ROOT)/lib/fli EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) -$(LIB_DIR)/libsim.so: $(SIM_ROOT)/lib/simulator/simulatormodule.c | $(LIB_DIR) +$(LIB_DIR)/libsim.$(LIB_EXT): $(SIM_ROOT)/lib/simulator/simulatormodule.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/simulator $(LIB_DIR)/libcocotbutils.a: $(SIM_ROOT)/lib/utils/cocotb_utils.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/utils COCOTB_LIBS := $(LIB_DIR)/libcocotbutils.a \ - $(LIB_DIR)/libgpilog.so \ - $(LIB_DIR)/libcocotb.so \ - $(LIB_DIR)/libgpi.so \ - $(LIB_DIR)/libvpi.so \ - $(LIB_DIR)/libsim.so \ - $(LIB_DIR)/libvhpi.so + $(LIB_DIR)/libgpilog.$(LIB_EXT) \ + $(LIB_DIR)/libcocotb.$(LIB_EXT) \ + $(LIB_DIR)/libgpi.$(LIB_EXT) \ + $(LIB_DIR)/libvpi.$(LIB_EXT) \ + $(LIB_DIR)/libsim.$(LIB_EXT) \ + $(LIB_DIR)/libvhpi.$(LIB_EXT) ifdef BUILD_FLI -COCOTB_LIBS += $(LIB_DIR)/libfli.so +COCOTB_LIBS += $(LIB_DIR)/libfli.$(LIB_EXT) endif clean:: diff --git a/lib/embed/Makefile b/lib/embed/Makefile index 15d907d3..cf8c4ff1 100644 --- a/lib/embed/Makefile +++ b/lib/embed/Makefile @@ -30,15 +30,15 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += -GCC_ARGS += -DPYTHON_SO_LIB=libpython$(PYTHON_VERSION).so +GCC_ARGS += -DPYTHON_SO_LIB=libpython$(PYTHON_VERSION).$(LIB_EXT) ARS += $(LIB_DIR)/libcocotbutils.a -LIBS := $(PYLIBS) +LIBS := $(PYLIBS) -lgpilog LD_PATH := -L$(LIB_DIR) SRCS := gpi_embed.c LIB_NAME := libcocotb -all: $(LIB_DIR)/$(LIB_NAME).so +all: $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) clean: -@rm -rf $(OBJ_DIR) diff --git a/lib/fli/Makefile b/lib/fli/Makefile index b0dfd441..37b4f37c 100644 --- a/lib/fli/Makefile +++ b/lib/fli/Makefile @@ -36,7 +36,7 @@ LIB_NAME := libfli SRCS := FliImpl.cpp -all: $(LIB_DIR)/$(LIB_NAME).so +all: $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) clean: -@rm -rf $(LIB_DIR)/$(LIB_NAME) diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index 53319371..25aa5206 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -37,7 +37,7 @@ LIB_NAME := libgpi SRCS := GpiCbHdl.cpp GpiCommon.cpp -all: $(LIB_DIR)/$(LIB_NAME).so +all: $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) ln -sf $(LIB_NAME) $@ clean: diff --git a/lib/gpi_log/Makefile b/lib/gpi_log/Makefile index dbb64abb..e8fe95f2 100644 --- a/lib/gpi_log/Makefile +++ b/lib/gpi_log/Makefile @@ -36,9 +36,9 @@ SRCS := gpi_logging.c LIB_NAME := libgpilog -all: $(LIB_DIR)/$(LIB_NAME).so +all: $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) clean: - -@rm -rf $(LIB_DIR)/$(LIB_NAME) + -@rm -rf $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/lib/simulator/Makefile b/lib/simulator/Makefile index 64746ee2..26251856 100644 --- a/lib/simulator/Makefile +++ b/lib/simulator/Makefile @@ -39,11 +39,11 @@ SRCS := simulatormodule.c CLIBS += $(LIB_DIR)/$(LIB_NAME) -$(LIB_DIR)/simulator.so: $(LIB_DIR)/$(LIB_NAME).so - ln -sf $(LIB_NAME).so $@ +$(LIB_DIR)/simulator.$(LIB_EXT): $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) + ln -sf $(LIB_NAME).$(LIB_EXT) $@ clean: - -@rm -rf $(LIB_DIR)/simulator.so - -@rm -rf $(LIB_DIR)/$(LIB_NAME).so + -@rm -rf $(LIB_DIR)/simulator.$(LIB_EXT) + -@rm -rf $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/lib/vhpi/Makefile b/lib/vhpi/Makefile index d7a6483d..8ccfbe40 100644 --- a/lib/vhpi/Makefile +++ b/lib/vhpi/Makefile @@ -37,7 +37,7 @@ LIB_NAME := libvhpi SRCS := VhpiImpl.cpp VhpiCbHdl.cpp -all: $(LIB_DIR)/$(LIB_NAME).so +all: $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) clean: -@rm -rf $(LIB_DIR)/$(LIB_NAME) diff --git a/lib/vpi/Makefile b/lib/vpi/Makefile index f476e8f9..413d3ff5 100644 --- a/lib/vpi/Makefile +++ b/lib/vpi/Makefile @@ -45,14 +45,14 @@ CLIBS += $(LIB_DIR)/gpivpi.vpl all: $(LIB_DIR)/gpivpi.vpl $(LIB_DIR)/cocotb.vpi -$(LIB_DIR)/gpivpi.vpl: $(LIB_DIR)/$(LIB_NAME).so - ln -sf $(LIB_NAME).so $@ +$(LIB_DIR)/gpivpi.vpl: $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) + ln -sf $(LIB_NAME).$(LIB_EXT) $@ -$(LIB_DIR)/cocotb.vpi: $(LIB_DIR)/$(LIB_NAME).so - ln -sf $(LIB_NAME).so $@ +$(LIB_DIR)/cocotb.vpi: $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) + ln -sf $(LIB_NAME).$(LIB_EXT) $@ clean: - -@rm -rf $(LIB_DIR)/$(LIB_NAME).so + -@rm -rf $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) -@rm -rf $(LIB_DIR)/gpivpi.vpl -@rm -rf $(LIB_DIR)/cocotb.vpi diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index c86080bc..e774fc1a 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -54,6 +54,8 @@ export LIB_OBJ_DIR:= $(BUILD_DIR)/obj/$(ARCH) export INCLUDES := -I$(SIM_ROOT)/include -I$(PYTHON_INCLUDEDIR) export GPI_EXTRA +LIB_EXT := so + # Base GCC flags ifeq ($(ARCH),i686) GXX_ARGS := -m32 @@ -65,6 +67,7 @@ endif ifeq ($(OS),MINGW32_NT-6.1) GXX_ARGS += -g -DDEBUG -shared GCC_ARGS := $(GCC_ARGS) +LIB_EXT := dll endif ifeq ($(OS),Linux) GXX_ARGS += -Werror -Wcast-qual -Wcast-align -Wwrite-strings \ diff --git a/makefiles/Makefile.rules b/makefiles/Makefile.rules index e02a4bc9..b5561561 100644 --- a/makefiles/Makefile.rules +++ b/makefiles/Makefile.rules @@ -43,7 +43,7 @@ $(LIB_OBJ_DIR)/%.o: %.c $(LIB_OBJ_DIR)/%.o: %.cpp g++ $(GXX_ARGS) -c $(INCLUDES) -o $@ $< -$(LIB_DIR)/$(LIB_NAME).so: $($(LIB_NAME)_OBJS) +$(LIB_DIR)/$(LIB_NAME).$(LIB_EXT): $($(LIB_NAME)_OBJS) gcc $(GCC_ARGS) $(SO_LINKER_ARGS) -o $@ $($(LIB_NAME)_OBJS) $(ARS) $(LIBS) $(LD_PATH) $(LIB_DIR)/$(LIB_NAME).a: $($(LIB_NAME)_OBJS) From a292d0a25112f84a33bd91695eab5d4c1339c122 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 14 Dec 2014 20:02:54 +0000 Subject: [PATCH 0704/2656] Issue #144: Do not call back down to Gpi on init failure, notify via return code. Fixes backwards linking with windows --- include/embed.h | 2 +- lib/embed/gpi_embed.c | 14 +++++++++----- lib/gpi/GpiCommon.cpp | 3 ++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/include/embed.h b/include/embed.h index 4f1203d8..0912ac1b 100644 --- a/include/embed.h +++ b/include/embed.h @@ -39,7 +39,7 @@ extern "C" { #endif extern void embed_init_python(void); -extern void embed_sim_init(gpi_sim_info_t *info); +extern int embed_sim_init(gpi_sim_info_t *info); extern void embed_sim_event(gpi_event_t level, const char *msg); #ifdef __cplusplus diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index a6f6bfab..6844f1a4 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -119,23 +119,23 @@ int get_module_ref(const char *modname, PyObject **mod) return 0; } -void embed_sim_init(gpi_sim_info_t *info) +int embed_sim_init(gpi_sim_info_t *info) { FENTER int i; + int ret = 0; /* Check that we are not already initialised */ if (pEventFn) - return; + return ret; // Find the simulation root gpi_sim_hdl dut = gpi_get_root_handle(getenv("TOPLEVEL")); if (dut == NULL) { fprintf(stderr, "Unable to find root instance!\n"); - gpi_sim_end(); - return; + return -1; } PyObject *cocotb_module, *cocotb_init, *cocotb_args, *cocotb_retval; @@ -261,13 +261,15 @@ void embed_sim_init(gpi_sim_info_t *info) } else { PyErr_Print(); fprintf(stderr,"Call failed\n"); - gpi_sim_end(); goto cleanup; } FEXIT + goto ok; cleanup: + ret = -1; +ok: if (cocotb_module) { Py_DECREF(cocotb_module); } @@ -275,6 +277,8 @@ void embed_sim_init(gpi_sim_info_t *info) Py_DECREF(arg_dict); } PyGILState_Release(gstate); + + return ret; } void embed_sim_event(gpi_event_t level, const char *msg) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 2c1e7845..11c19035 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -67,7 +67,8 @@ int gpi_register_impl(GpiImplInterface *func_tbl) void gpi_embed_init(gpi_sim_info_t *info) { - embed_sim_init(info); + if (embed_sim_init(info)) + gpi_sim_end(); } void gpi_embed_end(void) From b3f60b2a13120a8651df116022a16fd0f01e0efb Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 14 Dec 2014 21:49:45 +0000 Subject: [PATCH 0705/2656] Issue #144: Push the calling of gpi_get_root_handle up to Python so that it calls via the simulator module. This means that we not not have to link the embed lib against the gpi lib for this symbol --- cocotb/__init__.py | 5 ++--- cocotb/regression.py | 9 +++++++-- lib/embed/gpi_embed.c | 4 ++-- lib/simulator/simulatormodule.c | 22 ++++++++++++++++++++++ lib/simulator/simulatormodule.h | 2 ++ 5 files changed, 35 insertions(+), 7 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 27a17b5d..1f5a4cd3 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -74,7 +74,7 @@ def mem_debug(port): import cocotb.memdebug memdebug.start(port) -def _initialise_testbench(root_handle): +def _initialise_testbench(root_name): """ This function is called after the simulator has elaborated all entities and is ready to run the test. @@ -110,7 +110,6 @@ def _initialise_testbench(root_handle): log.info("Running tests with Cocotb v%s from %s" % (version, exec_path)) # Create the base handle type - dut = cocotb.handle.SimHandle(root_handle) process_plusargs() @@ -125,7 +124,7 @@ def _initialise_testbench(root_handle): global regression - regression = RegressionManager(dut, modules, tests=test_str) + regression = RegressionManager(root_name, modules, tests=test_str) regression.initialise() regression.execute() diff --git a/cocotb/regression.py b/cocotb/regression.py index c32af4a1..3c957ddb 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -58,7 +58,7 @@ def _my_import(name): class RegressionManager(object): """Encapsulates all regression capability into a single place""" - def __init__(self, dut, modules, tests=None): + def __init__(self, root_name, modules, tests=None): """ Args: modules (list): A list of python module names to run @@ -66,7 +66,8 @@ def __init__(self, dut, modules, tests=None): Kwargs """ self._queue = [] - self._dut = dut + self._root_name = root_name + self._dut = None self._modules = modules self._functions = tests self._running_test = None @@ -81,6 +82,10 @@ def initialise(self): self.xunit = XUnitReporter() self.xunit.add_testsuite(name="all", tests=repr(self.ntests), package="all") + self._dut = cocotb.handle.SimHandle(simulator.get_root_handle(self._root_name)) + if self._dut is None: + raise AttributeError("Can not find Root Handle (%s)" % root_name) + # Auto discovery for module_name in self._modules: module = _my_import(module_name) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 6844f1a4..3b3b83bf 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -131,7 +131,7 @@ int embed_sim_init(gpi_sim_info_t *info) return ret; // Find the simulation root - gpi_sim_hdl dut = gpi_get_root_handle(getenv("TOPLEVEL")); + const char *dut = getenv("TOPLEVEL"); if (dut == NULL) { fprintf(stderr, "Unable to find root instance!\n"); @@ -252,7 +252,7 @@ int embed_sim_init(gpi_sim_info_t *info) } cocotb_args = PyTuple_New(1); - PyTuple_SetItem(cocotb_args, 0, PyLong_FromLong((long)dut)); // Note: This function “steals†a reference to o. + PyTuple_SetItem(cocotb_args, 0, PyString_FromString(dut)); // Note: This function “steals†a reference to o. cocotb_retval = PyObject_CallObject(cocotb_init, cocotb_args); if (cocotb_retval != NULL) { diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index e69d0b78..6daccd5b 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -644,6 +644,28 @@ static PyObject *get_handle_by_index(PyObject *self, PyObject *args) return value; } +static PyObject *get_root_handle(PyObject *self, PyObject *args) +{ + const char *name; + gpi_sim_hdl result; + PyObject *value; + + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + if (!PyArg_ParseTuple(args, "s", &name)) { + DROP_GIL(gstate); + return NULL; + } + + result = gpi_get_root_handle(name); + + value = Py_BuildValue("l", result); + + DROP_GIL(gstate); + + return value; +} static PyObject *get_name_string(PyObject *self, PyObject *args) diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index fcf0ffd5..8a937cee 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -59,6 +59,7 @@ static PyObject *set_signal_val(PyObject *self, PyObject *args); static PyObject *set_signal_val_str(PyObject *self, PyObject *args); static PyObject *get_handle_by_name(PyObject *self, PyObject *args); static PyObject *get_handle_by_index(PyObject *self, PyObject *args); +static PyObject *get_root_handle(PyObject *self, PyObject *args); static PyObject *get_name_string(PyObject *self, PyObject *args); static PyObject *get_type_string(PyObject *self, PyObject *args); static PyObject *register_timed_callback(PyObject *self, PyObject *args); @@ -81,6 +82,7 @@ static PyMethodDef SimulatorMethods[] = { {"set_signal_val_str", set_signal_val_str, METH_VARARGS, "Set the value of a signal using a binary string"}, {"get_handle_by_name", get_handle_by_name, METH_VARARGS, "Get handle of a named object"}, {"get_handle_by_index", get_handle_by_index, METH_VARARGS, "Get handle of a object at an index in a parent"}, + {"get_root_handle", get_root_handle, METH_VARARGS, "Get the root handle"}, {"get_name_string", get_name_string, METH_VARARGS, "Get the name of an object"}, {"get_type_string", get_type_string, METH_VARARGS, "Get the type of an object"}, {"register_timed_callback", register_timed_callback, METH_VARARGS, "Register a timed callback"}, From 7dc9a19cfcf5fcbd3f9e13b5b0e135ac6d3078d9 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 16 Dec 2014 08:26:21 +0000 Subject: [PATCH 0706/2656] Issue #144: Moved looking for symbol into libcocotbutils and call gpi_embed_event rather than embed_sim_event --- include/cocotb_utils.h | 11 ++++++++++- lib/Makefile | 4 ++-- lib/embed/Makefile | 3 +-- lib/embed/gpi_embed.c | 7 ++----- lib/gpi/GpiCommon.cpp | 21 ++++++++++++--------- lib/gpi/Makefile | 5 ++--- lib/gpi/gpi_priv.h | 1 + lib/utils/Makefile | 2 +- lib/utils/cocotb_utils.c | 20 +++++++++++++++++++- lib/vpi/VpiImpl.cpp | 2 +- 10 files changed, 51 insertions(+), 25 deletions(-) diff --git a/include/cocotb_utils.h b/include/cocotb_utils.h index 2ae589f1..8e84dd83 100644 --- a/include/cocotb_utils.h +++ b/include/cocotb_utils.h @@ -30,10 +30,19 @@ #ifndef COCOTB_UTILS_H_ #define COCOTB_UTILS_H_ +#ifdef __cplusplus +extern "C" { +#endif + #ifdef __linux__ #include #endif -void* utils_dyn_open(const char* lib_name); +extern void* utils_dyn_open(const char* lib_name); +extern void* utils_dyn_sym(void *handle, const char* sym_name); + +#ifdef __cplusplus +} +#endif #endif /* COCOTB_UTILS_H_ */ \ No newline at end of file diff --git a/lib/Makefile b/lib/Makefile index 88fa08ec..13ceb988 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -67,10 +67,10 @@ $(LIB_DIR)/libfli.$(LIB_EXT): $(SIM_ROOT)/lib/fli/FliImpl.cpp | $(LIB_DIR) $(LIB_DIR)/libsim.$(LIB_EXT): $(SIM_ROOT)/lib/simulator/simulatormodule.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/simulator -$(LIB_DIR)/libcocotbutils.a: $(SIM_ROOT)/lib/utils/cocotb_utils.c | $(LIB_DIR) +$(LIB_DIR)/libcocotbutils.$(LIB_EXT): $(SIM_ROOT)/lib/utils/cocotb_utils.c | $(LIB_DIR) make -C $(SIM_ROOT)/lib/utils -COCOTB_LIBS := $(LIB_DIR)/libcocotbutils.a \ +COCOTB_LIBS := $(LIB_DIR)/libcocotbutils.$(LIB_EXT) \ $(LIB_DIR)/libgpilog.$(LIB_EXT) \ $(LIB_DIR)/libcocotb.$(LIB_EXT) \ $(LIB_DIR)/libgpi.$(LIB_EXT) \ diff --git a/lib/embed/Makefile b/lib/embed/Makefile index cf8c4ff1..3d475e64 100644 --- a/lib/embed/Makefile +++ b/lib/embed/Makefile @@ -31,8 +31,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += GCC_ARGS += -DPYTHON_SO_LIB=libpython$(PYTHON_VERSION).$(LIB_EXT) -ARS += $(LIB_DIR)/libcocotbutils.a -LIBS := $(PYLIBS) -lgpilog +LIBS := $(PYLIBS) -lgpilog -lcocotbutils LD_PATH := -L$(LIB_DIR) SRCS := gpi_embed.c diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 3b3b83bf..75c6c4ae 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -19,10 +19,7 @@ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* void * ret = DIRECT, INDIRECT, -if (!ret) { - fprintf(stderr, "Failed to find python lib %s (%s)\n", lib_name, dlerror()); - } INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT @@ -209,7 +206,7 @@ int embed_sim_init(gpi_sim_info_t *info) goto cleanup; } - gpi_print_registered_impl(); + //gpi_print_registered_impl(); LOG_INFO("Running on %s version %s", info->product, info->version); LOG_INFO("Python interpreter initialised and cocotb loaded!"); diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 11c19035..3573c243 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -28,9 +28,9 @@ ******************************************************************************/ #include "gpi_priv.h" +#include #include #include -#include #include using namespace std; @@ -72,6 +72,7 @@ void gpi_embed_init(gpi_sim_info_t *info) } void gpi_embed_end(void) + { embed_sim_event(SIM_FAIL, "Simulator shutdown prematurely"); } @@ -81,6 +82,11 @@ void gpi_sim_end(void) registered_impls[0]->sim_end(); } +void gpi_embed_event(gpi_event_t level, const char *msg) +{ + embed_sim_event(level, msg); +} + static void gpi_load_libs(std::vector to_load) { std::vector::iterator iter; @@ -93,21 +99,18 @@ static void gpi_load_libs(std::vector to_load) std::string full_name = "lib" + *iter + ".so"; const char *now_loading = (full_name).c_str(); - lib_handle = dlopen(now_loading, RTLD_GLOBAL | RTLD_NOW); + lib_handle = utils_dyn_open(now_loading); if (!lib_handle) { - printf("Error loading lib %s (%s)\n", now_loading, dlerror()); + printf("Error loading lib %s\n", now_loading); exit(1); } std::string sym = (*iter) + "_entry_point"; - void *entry_point = dlsym(lib_handle, sym.c_str()); + void *entry_point = utils_dyn_sym(lib_handle, sym.c_str()); if (!entry_point) { - printf("Unable to find entry point for %s (%s)\n", now_loading, dlerror()); + printf("Unable to find entry point for %s\n", now_loading); exit(1); } - layer_entry_func new_lib_entry = (layer_entry_func)entry_point; - new_lib_entry(); - - dlerror(); + } } diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index 25aa5206..5b0247dd 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -31,16 +31,15 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += GXX_ARGS += -DVPI_CHECKING -LIBS := -lgpilog -lcocotb -lstdc++ $(EXTRA_LIBS) +LIBS := -lcocotbutils -lgpilog -lcocotb -lstdc++ $(EXTRA_LIBS) LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) LIB_NAME := libgpi SRCS := GpiCbHdl.cpp GpiCommon.cpp all: $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) - ln -sf $(LIB_NAME) $@ clean: - -@rm -rf $(LIB_DIR)/$(LIB_NAME) + -@rm -rf $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) include $(SIM_ROOT)/makefiles/Makefile.rules diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 0e03400a..bdafb458 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -222,6 +222,7 @@ int gpi_register_impl(GpiImplInterface *func_tbl); void gpi_embed_init(gpi_sim_info_t *info); void gpi_embed_end(void); +void gpi_embed_event(gpi_event_t level, const char *msg); void gpi_load_extra_libs(void); typedef const void (*layer_entry_func)(void); diff --git a/lib/utils/Makefile b/lib/utils/Makefile index cacca262..8799c387 100644 --- a/lib/utils/Makefile +++ b/lib/utils/Makefile @@ -34,7 +34,7 @@ SRCS := cocotb_utils.c LIB_NAME := libcocotbutils -all: $(LIB_DIR)/$(LIB_NAME).a +all: $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) clean: -@rm -rf $(OBJ_DIR) diff --git a/lib/utils/cocotb_utils.c b/lib/utils/cocotb_utils.c index e0739be8..cb2db298 100644 --- a/lib/utils/cocotb_utils.c +++ b/lib/utils/cocotb_utils.c @@ -30,9 +30,13 @@ #include #include -void* utils_dyn_open(const char* lib_name) { +void* utils_dyn_open(const char* lib_name) +{ void *ret = NULL; #ifdef __linux__ + /* Clear status */ + dlerror(); + ret = dlopen(lib_name, RTLD_LAZY | RTLD_GLOBAL); if (!ret) { printf("Unable to open lib %s (%s)\n", lib_name, dlerror()); @@ -41,4 +45,18 @@ void* utils_dyn_open(const char* lib_name) { printf("Do something for windows here\n"); #endif return ret; +} + +void* utils_dyn_sym(void *handle, const char* sym_name) +{ + void *entry_point; +#ifdef __linux__ + entry_point = dlsym(handle, sym_name); + if (!entry_point) { + printf("Unable to find symbol %s (%s)\n", sym_name, dlerror()); + } +#else + printf("Do something for windows here\n"); +#endif + return entry_point; } \ No newline at end of file diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index c7cabae2..9ec59e79 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -371,7 +371,7 @@ static int system_function_overload(char *userdata) // Fail the test for critical errors if (GPICritical == *userdata) - embed_sim_event(SIM_TEST_FAIL, argval.value.str); + gpi_embed_event(SIM_TEST_FAIL, argval.value.str); return 0; } From 3957c326048fa378f9248067fd39ca77378ee7c0 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 16 Dec 2014 08:52:05 +0000 Subject: [PATCH 0707/2656] Issue #144: Include header macros to state that the functions are external on windows --- include/vpi_user.h | 58 ++++++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/include/vpi_user.h b/include/vpi_user.h index d7daa02d..b32bac56 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -36,10 +36,23 @@ #include + + #ifdef __cplusplus extern "C" { #endif +#if !defined(__linux__) +#ifndef VPI_DLLISPEC +#define VPI_DLLISPEC __declspec(dllimport) +#define VPI_DLL_LOCAL 1 +#endif +#else +#ifndef VPI_DLLISPEC +#define VPI_DLLISPEC +#endif +#endif + typedef uint32_t *vpiHandle; #define vpiModule 32 /* module instance */ @@ -229,58 +242,63 @@ typedef struct t_vpi_systf_data { #define vpiArgument 89 -extern vpiHandle vpi_register_cb(p_cb_data cb_data_p); +extern VPI_DLLISPEC vpiHandle vpi_register_cb(p_cb_data cb_data_p); -extern int32_t vpi_remove_cb(vpiHandle cb_obj); +extern VPI_DLLISPEC int32_t vpi_remove_cb(vpiHandle cb_obj); -extern vpiHandle vpi_handle_by_name(char *name, +extern VPI_DLLISPEC vpiHandle vpi_handle_by_name(char *name, vpiHandle scope); -extern vpiHandle vpi_handle_by_index(vpiHandle object, +extern VPI_DLLISPEC vpiHandle vpi_handle_by_index(vpiHandle object, int32_t indx); -extern vpiHandle vpi_handle(int32_t type, +extern VPI_DLLISPEC vpiHandle vpi_handle(int32_t type, vpiHandle refHandle); -extern vpiHandle vpi_iterate(int32_t type, +extern VPI_DLLISPEC vpiHandle vpi_iterate(int32_t type, vpiHandle refHandle); -extern vpiHandle vpi_scan(vpiHandle iterator); +extern VPI_DLLISPEC vpiHandle vpi_scan(vpiHandle iterator); -extern char *vpi_get_str(int32_t property, +extern VPI_DLLISPEC char *vpi_get_str(int32_t property, vpiHandle object); -extern void vpi_get_value(vpiHandle expr, +extern VPI_DLLISPEC void vpi_get_value(vpiHandle expr, p_vpi_value value_p); -extern vpiHandle vpi_put_value(vpiHandle object, +extern VPI_DLLISPEC vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p, p_vpi_time time_p, int32_t flags); -extern void vpi_get_time(vpiHandle object, +extern VPI_DLLISPEC void vpi_get_time(vpiHandle object, p_vpi_time time_p); -extern int32_t vpi_get(int property, +extern VPI_DLLISPEC int32_t vpi_get(int property, vpiHandle ref); -extern int32_t vpi_free_object(vpiHandle object); +extern VPI_DLLISPEC int32_t vpi_free_object(vpiHandle object); -extern int32_t vpi_control(int32_t operation, ...); -extern vpiHandle vpi_handle_by_multi_index(vpiHandle obj, +extern VPI_DLLISPEC int32_t vpi_control(int32_t operation, ...); +extern VPI_DLLISPEC vpiHandle vpi_handle_by_multi_index(vpiHandle obj, int32_t num_index, int32_t *index_array); -extern int32_t vpi_chk_error(p_vpi_error_info); +extern VPI_DLLISPEC int32_t vpi_chk_error(p_vpi_error_info); -extern int32_t vpi_get_vlog_info(p_vpi_vlog_info info_p); +extern VPI_DLLISPEC int32_t vpi_get_vlog_info(p_vpi_vlog_info info_p); -extern vpiHandle vpi_register_systf(p_vpi_systf_data data_p); +extern VPI_DLLISPEC vpiHandle vpi_register_systf(p_vpi_systf_data data_p); -extern int32_t vpi_printf(const char *fmt, ...) __attribute__((format (printf,1,2))); +extern VPI_DLLISPEC int32_t vpi_printf(const char *fmt, ...) __attribute__((format (printf,1,2))); -extern void (*vlog_startup_routines[])(void); +extern VPI_DLLISPEC void (*vlog_startup_routines[])(void); + +#ifdef VPI_DLL_LOCAL +#undef VPI_DLL_LOCAL +#undef VPI_DLLISPEC +#endif #ifdef __cplusplus } From 94a3997f302d02668ab8f2eacf92dae7b1ca486b Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 16 Dec 2014 19:42:44 +0000 Subject: [PATCH 0708/2656] Issue #144: Makefile changes to allow subset of interface libs to be built depending on what the sim provides. This is mainly for windows where unresolved symbols in a lib are not allowed to be resolved at run time. So for instance VHPI can not be built against icarus on that platform. --- lib/Makefile | 12 ++++++------ lib/gpi/Makefile | 4 ++-- lib/simulator/Makefile | 4 ++-- lib/vpi/Makefile | 4 ++-- makefiles/Makefile.inc | 23 ++++++++++++++-------- makefiles/Makefile.rules | 3 --- makefiles/simulators/Makefile.aldec | 4 ++-- makefiles/simulators/Makefile.ghdl | 2 +- makefiles/simulators/Makefile.icarus | 27 ++++++++++++++++++++------ makefiles/simulators/Makefile.ius | 2 +- makefiles/simulators/Makefile.modelsim | 20 ++++++++++++++++--- makefiles/simulators/Makefile.nvc | 2 +- makefiles/simulators/Makefile.questa | 2 +- makefiles/simulators/Makefile.vcs | 2 +- 14 files changed, 72 insertions(+), 39 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index 13ceb988..cc1c7002 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -74,13 +74,13 @@ COCOTB_LIBS := $(LIB_DIR)/libcocotbutils.$(LIB_EXT) \ $(LIB_DIR)/libgpilog.$(LIB_EXT) \ $(LIB_DIR)/libcocotb.$(LIB_EXT) \ $(LIB_DIR)/libgpi.$(LIB_EXT) \ - $(LIB_DIR)/libvpi.$(LIB_EXT) \ - $(LIB_DIR)/libsim.$(LIB_EXT) \ - $(LIB_DIR)/libvhpi.$(LIB_EXT) + $(LIB_DIR)/libsim.$(LIB_EXT) -ifdef BUILD_FLI -COCOTB_LIBS += $(LIB_DIR)/libfli.$(LIB_EXT) -endif +COCOTB_VPI_LIB := $(LIB_DIR)/libvpi.$(LIB_EXT) + +COCOTB_VHPI_LIB := $(LIB_DIR)/libvhpi.$(LIB_EXT) + +COCOTB_FLI_LIB := $(LIB_DIR)/libfli.$(LIB_EXT) clean:: -@rm -rf $(BUILD_DIR) diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index 5b0247dd..4e8842f0 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -31,8 +31,8 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += GXX_ARGS += -DVPI_CHECKING -LIBS := -lcocotbutils -lgpilog -lcocotb -lstdc++ $(EXTRA_LIBS) -LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) +LIBS := -lcocotbutils -lgpilog -lcocotb -lstdc++ +LD_PATH := -L$(LIB_DIR) LIB_NAME := libgpi SRCS := GpiCbHdl.cpp GpiCommon.cpp diff --git a/lib/simulator/Makefile b/lib/simulator/Makefile index 26251856..b5bb000a 100644 --- a/lib/simulator/Makefile +++ b/lib/simulator/Makefile @@ -31,7 +31,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += GCC_ARGS += -LIBS := -lgpi $(PYLIBS) +LIBS := -lgpi -lgpilog $(PYLIBS) LD_PATH := -L$(LIB_DIR) LIB_NAME := libsim @@ -39,7 +39,7 @@ SRCS := simulatormodule.c CLIBS += $(LIB_DIR)/$(LIB_NAME) -$(LIB_DIR)/simulator.$(LIB_EXT): $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) +$(LIB_DIR)/simulator.$(PY_EXT): $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) ln -sf $(LIB_NAME).$(LIB_EXT) $@ clean: diff --git a/lib/vpi/Makefile b/lib/vpi/Makefile index 413d3ff5..a042bf4a 100644 --- a/lib/vpi/Makefile +++ b/lib/vpi/Makefile @@ -31,8 +31,8 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += GXX_ARGS += -DVPI_CHECKING -LIBS := -lgpilog -lgpi -lstdc++ -LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) +LIBS := $(EXTRA_LIBS) -lgpilog -lgpi -lstdc++ +LD_PATH := $(EXTRA_LIBDIRS) -L$(LIB_DIR) LIB_NAME := libvpi SRCS := VpiImpl.cpp VpiCbHdl.cpp diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index e774fc1a..0a6df7fb 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -55,30 +55,37 @@ export INCLUDES := -I$(SIM_ROOT)/include -I$(PYTHON_INCLUDEDIR) export GPI_EXTRA LIB_EXT := so +PY_EXT := so # Base GCC flags ifeq ($(ARCH),i686) GXX_ARGS := -m32 endif + ifeq ($(OS),Darwin) GXX_ARGS += -g -DDEBUG -fpic GCC_ARGS := $(GXX_ARGS) -endif -ifeq ($(OS),MINGW32_NT-6.1) +else ifeq ($(OS),MINGW32_NT-6.1) GXX_ARGS += -g -DDEBUG -shared GCC_ARGS := $(GCC_ARGS) -LIB_EXT := dll -endif -ifeq ($(OS),Linux) +LIB_EXT := dll +PY_EXT := pyd +else ifeq ($(OS),Linux) GXX_ARGS += -Werror -Wcast-qual -Wcast-align -Wwrite-strings \ -Wall -Wno-unused-parameter \ -fno-common -g -DDEBUG -fpic GCC_ARGS = $(GXX_ARGS) -Wstrict-prototypes -Waggregate-return +else +$(error "Unsupported os " $(OS)) endif ifeq ($(OS),Darwin) -LINKER_ARGS := -shared -undefined suppress -flat_namespace -L$(PYTHON_LIBDIR) -else +SO_LINKER_ARGS := -shared -undefined suppress -flat_namespace -L$(PYTHON_LIBDIR) +else ifeq ($(OS),Linux) SO_LINKER_ARGS := -shared -Xlinker -L$(PYTHON_LIBDIR) -AR_LINKER_ARGS := cr +else ifeq ($(OS),MINGW32_NT-6.1) +SO_LINKER_ARGS := -shared -Wl,-no-undefined -Wl,-enable-runtime-pseudo-reloc-v2 \ + -Wl,--enable-auto-import -L$(PYTHON_LIBDIR) +else +$(error "Unsupported os" $(OS)) endif diff --git a/makefiles/Makefile.rules b/makefiles/Makefile.rules index b5561561..039aa590 100644 --- a/makefiles/Makefile.rules +++ b/makefiles/Makefile.rules @@ -45,6 +45,3 @@ $(LIB_OBJ_DIR)/%.o: %.cpp $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT): $($(LIB_NAME)_OBJS) gcc $(GCC_ARGS) $(SO_LINKER_ARGS) -o $@ $($(LIB_NAME)_OBJS) $(ARS) $(LIBS) $(LD_PATH) - -$(LIB_DIR)/$(LIB_NAME).a: $($(LIB_NAME)_OBJS) - ar $(AR_LINKER_ARGS) -o $@ $($(LIB_NAME)_OBJS) $(LIBS) $(LD_PATH) \ No newline at end of file diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index e5cfdf09..30d90907 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -35,7 +35,7 @@ else CMD := vsimsa endif -EXTRA_LIBS = -laldecpli -lstdc++ -lsupc++ +EXTRA_LIBS = -laldecpli -lsupc++ EXTRA_LIBDIRS = -L$(ALDEC_PATH)/bin/Linux64 ALOG_ARGS += +define+COCOTB_SIM -dbg -pli libgpi @@ -88,7 +88,7 @@ endif # Note it's the redirection of the output rather than the 'do' command # that turns on batch mode (i.e. exit on completion/error) -results.xml: $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(CUSTOM_SIM_DEPS) +results.xml: $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(COCOTB_VPI_LIB) $(CUSTOM_SIM_DEPS) cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ LD_LIBRARY_PATH=$(LIB_DIR):$(VSIMSABIN)/Linux64:$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do runsim.tcl | tee sim.log diff --git a/makefiles/simulators/Makefile.ghdl b/makefiles/simulators/Makefile.ghdl index ccb45531..e23f59b5 100644 --- a/makefiles/simulators/Makefile.ghdl +++ b/makefiles/simulators/Makefile.ghdl @@ -32,7 +32,7 @@ analyse: $(VHDL_SOURCES) $(SIM_BUILD) cd $(SIM_BUILD) && ghdl -a $(VHDL_SOURCES) -results.xml: analyse $(COCOTB_LIBS) +results.xml: analyse $(COCOTB_LIBS) $(COCOTB_LIB_VPI) cd $(SIM_BUILD) && LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) ghdl -e -Wl,-L$(LIB_DIR) -Wl,-lvpi $(TOPLEVEL) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index 715d19e0..a27525d1 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -27,20 +27,35 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +BUILD_VPI=1 + # Compilation phase $(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) iverilog -o $(SIM_BUILD)/sim.vvp -D COCOTB_SIM=1 $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) # Execution phase -results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ +ifeq ($(OS),MINGW32_NT-6.1) + +ifeq ($(ICARUS_PATH),) +$(error "Need to set ICARUS_PATH") +endif + +EXTRA_LIBS := -lvpi +EXTRA_LIBDIRS := -L$(ICARUS_PATH)/lib +LIB_LOAD := +else +LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) +endif + +results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) -clean:: - -@rm -rf $(SIM_BUILD) - debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ gdb --args vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) + +clean:: + -@rm -rf $(SIM_BUILD) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 8da04098..7e5c1196 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -56,7 +56,7 @@ ifndef GPI_ARGS $(error "Unable to determine appropriate GPI layer to use") endif -results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) +results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_LIB_VPI) LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ irun $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(HDL_SOURCES) $(PLUSARGS) diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index 383e68c1..52fa2af2 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -47,7 +47,7 @@ endif ifeq ($(GPI_IMPL),vhpi) VSIM_ARGS += -foreign \"cocotb_init libfli.so\" -trace_foreign 3 else -VSIM_ARGS += -pli libvpi.so +VSIM_ARGS += -pli libvpi.$(LIB_EXT) endif $(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) | $(SIM_BUILD) @@ -64,8 +64,22 @@ ifneq ($(GUI),1) endif -results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) - cd $(SIM_BUILD) && LD_LIBRARY_PATH=$(LIB_DIR)::$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ +ifeq ($(OS),MINGW32_NT-6.1) + +ifeq ($(MODELSIM_PATH),) +$(error "Need to set MODELSIM_PATH") +endif + +EXTRA_LIBS := -lmtipli +EXTRA_LIBDIRS := -L$(MODELSIM_PATH)/modelsim_ase/win32aloem + +LIB_LIAD := +else +LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) +endif + +results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(COCOTB_VPI_LIB) + cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ $(SIM_CMD) -do runsim.do 2>&1 | tee sim.log diff --git a/makefiles/simulators/Makefile.nvc b/makefiles/simulators/Makefile.nvc index b613a69c..6f6e916e 100644 --- a/makefiles/simulators/Makefile.nvc +++ b/makefiles/simulators/Makefile.nvc @@ -6,7 +6,7 @@ analyse: $(VHDL_SOURCES) $(SIM_BUILD) cd $(SIM_BUILD) && nvc -a $(VHDL_SOURCES) -results.xml: analyse $(COCOTB_LIBS) +results.xml: analyse $(COCOTB_LIBS) $(COCOTB_LIB_VPI) cd $(SIM_BUILD) && LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) nvc -e $(TOPLEVEL) cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index f152ec54..e31c9717 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -29,7 +29,7 @@ VPI_FILE := $(LIB_DIR)/libvpi.so -results.xml: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) +results.xml: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_LIB_VPI) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(PYTHON_LIBDIR):$(PYTHON_DYNLIBDIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ qverilog -64 -R -pli $(VPI_FILE) - +acc $(EXTRA_ARGS) $(VERILOG_SOURCES) diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index aa8cf098..23386d77 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -40,7 +40,7 @@ $(SIM_BUILD)/pli.tab : echo "acc+=rw,wn:*" > $@ # Compilation phase -$(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(COCOTB_LIBS) $(CUSTOM_COMPILE_DEPS) +$(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(COCOTB_LIBS) $(COCOTB_LIB_VPI) $(CUSTOM_COMPILE_DEPS) cd $(SIM_BUILD) && \ LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) TOPLEVEL=$(TOPLEVEL) \ vcs +acc+1 +vpi -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -debug -load libvpi.so $(COMPILE_ARGS) $(VERILOG_SOURCES) From dee93dbe6023a5a2807e3b8fc2366e379900d497 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 16 Dec 2014 21:50:09 +0000 Subject: [PATCH 0709/2656] Issue #144: Allow the path of a file to be passed to simulator in native format even if the shell uses a different format to do operations --- examples/endian_swapper/tests/Makefile | 10 ++++++++-- examples/functionality/tests/Makefile | 10 ++++++++-- examples/mixed_language/tests/Makefile | 14 ++++++++++---- examples/ping_tun_tap/tests/Makefile | 6 ++++++ examples/plusargs/Makefile | 4 ++++ examples/sim_exit/tests/Makefile | 8 +++++++- 6 files changed, 43 insertions(+), 9 deletions(-) diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index 78288c5d..688cedba 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -31,18 +31,24 @@ # Override this variable to use VHPI instead of VPI TOPLEVEL_LANG ?= verilog +WPWD=$(shell sh -c 'pwd -W') PWD=$(shell pwd) COCOTB=$(PWD)/../../.. +ifeq ($(OS),MINGW32_NT-6.1) +WPWD=$(shell sh -c 'pwd -W') +else +WPWD=$(shell pwd) +endif ifeq ($(TOPLEVEL_LANG),verilog) - VERILOG_SOURCES = $(PWD)/../hdl/endian_swapper.sv + VERILOG_SOURCES = $(WPWD)/../hdl/endian_swapper.sv TOPLEVEL = endian_swapper_sv GPI_IMPL=vpi endif ifeq ($(TOPLEVEL_LANG),vhdl) - VHDL_SOURCES = $(PWD)/../hdl/endian_swapper.vhdl + VHDL_SOURCES = $(WPWD)/../hdl/endian_swapper.vhdl TOPLEVEL = endian_swapper_vhdl GPI_IMPL=vhpi endif diff --git a/examples/functionality/tests/Makefile b/examples/functionality/tests/Makefile index c6f2cfdd..16c163a0 100644 --- a/examples/functionality/tests/Makefile +++ b/examples/functionality/tests/Makefile @@ -34,12 +34,18 @@ TOPLEVEL_LANG ?= verilog PWD=$(shell pwd) COCOTB=$(PWD)/../../.. +ifeq ($(OS),MINGW32_NT-6.1) +WPWD=$(shell sh -c 'pwd -W') +else +WPWD=$(shell pwd) +endif + ifeq ($(TOPLEVEL_LANG),vhdl) - VHDL_SOURCES = $(PWD)/../hdl/sample_module.vhdl + VHDL_SOURCES = $(WPWD)/../hdl/sample_module.vhdl TOPLEVEL := $(TOPLEVEL) GPI_IMPL := vhpi else - VERILOG_SOURCES = $(PWD)/../hdl/sample_module.v + VERILOG_SOURCES = $(WPWD)/../hdl/sample_module.v GPI_IMPL := vpi endif diff --git a/examples/mixed_language/tests/Makefile b/examples/mixed_language/tests/Makefile index 73d732b7..f8b04ccb 100644 --- a/examples/mixed_language/tests/Makefile +++ b/examples/mixed_language/tests/Makefile @@ -9,18 +9,24 @@ TOPLEVEL = endian_swapper_mixed PWD=$(shell pwd) COCOTB=$(PWD)/../../.. -VERILOG_SOURCES = $(PWD)/../../endian_swapper/hdl/endian_swapper.sv -VHDL_SOURCES = $(PWD)/../../endian_swapper/hdl/endian_swapper.vhdl +ifeq ($(OS),MINGW32_NT-6.1) +WPWD=$(shell sh -c 'pwd -W') +else +WPWD=$(shell pwd) +endif + +VERILOG_SOURCES = $(WPWD)/../../endian_swapper/hdl/endian_swapper.sv +VHDL_SOURCES = $(WPWD)/../../endian_swapper/hdl/endian_swapper.vhdl ifeq ($(TOPLEVEL_LANG),sv) - VERILOG_SOURCES += $(PWD)/../hdl/toplevel.sv + VERILOG_SOURCES += $(WPWD)/../hdl/toplevel.sv GPI_IMPL=vpi GPI_EXTRA=vhpi endif ifeq ($(TOPLEVEL_LANG),vhdl) $(error "VHDL toplevel not yet implemented") - VHDL_SOURCES += $(PWD)/../hdl/toplevel.vhdl + VHDL_SOURCES += $(WPWD)/../hdl/toplevel.vhdl GPI_IMPL=vhpi GPI_EXTRA=vpi endif diff --git a/examples/ping_tun_tap/tests/Makefile b/examples/ping_tun_tap/tests/Makefile index ae619b67..58379864 100644 --- a/examples/ping_tun_tap/tests/Makefile +++ b/examples/ping_tun_tap/tests/Makefile @@ -29,6 +29,12 @@ TOPLEVEL = icmp_reply +ifeq ($(OS),MINGW32_NT-6.1) +WPWD=$(shell sh -c 'pwd -W') +else +WPWD=$(shell pwd) +endif + PWD=$(shell pwd) COCOTB=$(PWD)/../../.. diff --git a/examples/plusargs/Makefile b/examples/plusargs/Makefile index 7f0bddd8..eb896cdf 100644 --- a/examples/plusargs/Makefile +++ b/examples/plusargs/Makefile @@ -30,7 +30,11 @@ TOPLEVEL = tb_top +ifeq ($(OS),MINGW32_NT-6.1) +PWD=$(shell sh -c 'pwd -W') +else PWD=$(shell pwd) +endif VERILOG_SOURCES = $(PWD)/tb_top.v MODULE=plusargs diff --git a/examples/sim_exit/tests/Makefile b/examples/sim_exit/tests/Makefile index fa9f769d..a20adea2 100644 --- a/examples/sim_exit/tests/Makefile +++ b/examples/sim_exit/tests/Makefile @@ -30,9 +30,15 @@ TOPLEVEL = close_module +ifeq ($(OS),MINGW32_NT-6.1) +WPWD=$(shell sh -c 'pwd -W') +else +WPWD=$(shell pwd) +endif + PWD=$(shell pwd) COCOTB=$(PWD)/../../.. -VERILOG_SOURCES = $(PWD)/../hdl/close_module.v +VERILOG_SOURCES = $(WPWD)/../hdl/close_module.v MODULE=test_closedown include $(COCOTB)/makefiles/Makefile.inc From 92b8bd65709e582dfbcf465e5ed14a1bbef582db Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 16 Dec 2014 22:56:26 +0000 Subject: [PATCH 0710/2656] Issue #144: Add back deleted loading of multilib --- lib/embed/gpi_embed.c | 1 - lib/gpi/GpiCommon.cpp | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 75c6c4ae..65d6e5e5 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -206,7 +206,6 @@ int embed_sim_init(gpi_sim_info_t *info) goto cleanup; } - //gpi_print_registered_impl(); LOG_INFO("Running on %s version %s", info->product, info->version); LOG_INFO("Python interpreter initialised and cocotb loaded!"); diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 3573c243..45ef6530 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -110,7 +110,9 @@ static void gpi_load_libs(std::vector to_load) printf("Unable to find entry point for %s\n", now_loading); exit(1); } - + + layer_entry_func new_lib_entry = (layer_entry_func)entry_point; + new_lib_entry(); } } @@ -146,6 +148,7 @@ void gpi_load_extra_libs(void) /* Finally embed python */ embed_init_python(); + gpi_print_registered_impl(); } void gpi_get_sim_time(uint32_t *high, uint32_t *low) From 8aefa7bd6a77bd69da8b3d00e1a402a2acc851cd Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 16 Dec 2014 23:00:46 +0000 Subject: [PATCH 0711/2656] Issue #144: Typo on VCS makefile --- makefiles/simulators/Makefile.vcs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index 23386d77..857337ce 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -40,7 +40,7 @@ $(SIM_BUILD)/pli.tab : echo "acc+=rw,wn:*" > $@ # Compilation phase -$(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(COCOTB_LIBS) $(COCOTB_LIB_VPI) $(CUSTOM_COMPILE_DEPS) +$(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(COCOTB_LIBS) $(COCOTB_VPI_LIB) $(CUSTOM_COMPILE_DEPS) cd $(SIM_BUILD) && \ LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) TOPLEVEL=$(TOPLEVEL) \ vcs +acc+1 +vpi -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -debug -load libvpi.so $(COMPILE_ARGS) $(VERILOG_SOURCES) From 93e54d8599f949aafddf454919cbc44f20a7084d Mon Sep 17 00:00:00 2001 From: Chiggs Date: Wed, 17 Dec 2014 20:37:40 +0000 Subject: [PATCH 0712/2656] Update Makefile for NVC (still not working) --- makefiles/simulators/Makefile.nvc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.nvc b/makefiles/simulators/Makefile.nvc index b613a69c..1ada2dc8 100644 --- a/makefiles/simulators/Makefile.nvc +++ b/makefiles/simulators/Makefile.nvc @@ -11,7 +11,7 @@ results.xml: analyse $(COCOTB_LIBS) cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ LD_LIBRARY_PATH=$(LIB_DIR) \ - nvc -r --load $(LIB_DIR)/libvpi.so $(TOPLEVEL) + nvc -r --load $(LIB_DIR)/libvhpi.so $(TOPLEVEL) clean:: -@rm -rf $(SIM_BUILD) From e87e01a0ae1883c9e389edd23abf82e349aea1b5 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Wed, 17 Dec 2014 21:31:43 +0000 Subject: [PATCH 0713/2656] Issue #179: Add capability to trace signals to a wavedrom diagram --- cocotb/wavedrom.py | 178 ++++++++++++++++++ .../tests/test_endian_swapper.py | 18 ++ 2 files changed, 196 insertions(+) create mode 100644 cocotb/wavedrom.py diff --git a/cocotb/wavedrom.py b/cocotb/wavedrom.py new file mode 100644 index 00000000..d111dd55 --- /dev/null +++ b/cocotb/wavedrom.py @@ -0,0 +1,178 @@ +''' +Copyright (c) 2014 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd, nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +import json +from collections import OrderedDict, defaultdict + +import cocotb +from cocotb.bus import Bus +from cocotb.triggers import RisingEdge, ReadOnly + +class Wavedrom(object): + """ + Base class for a wavedrom compatible tracer + """ + def __init__(self, obj): + + self._hdls = {} + if isinstance(obj, Bus): + for name, hdl in obj._signals.iteritems(): + self._hdls[name] = hdl + self._name = obj._name + else: + self._hdls[obj.name] = obj + + self.clear() + + def sample(self): + """ + Record a sample of the signal value at this point in time + """ + + def _lastval(samples): + for x in range(len(samples)-1, -1, -1): + if samples[x] not in "=.": return samples[x] + return None + + for name, hdl in self._hdls.iteritems(): + val = hdl.value + valstr = val.binstr.lower() + + # Decide what character to use to represent this signal + if len(valstr) == 1: char = valstr + elif "x" in valstr: char = "x" + elif "u" in valstr: char = "u" + elif "z" in valstr: char = "z" + else: + if len(self._data[name]) and self._data[name][-1] == int(val): + char = "." + else: + char = "=" + self._data[name].append(int(val)) + + # Detect if this is unchanged + if char == _lastval(self._samples[name]): + char = "." + self._samples[name].append(char) + + def clear(self): + """ + Delete all sampled data + """ + self._samples = defaultdict(list) + self._data = defaultdict(list) + + def get(self, add_clock=True): + """ + Return the samples as a list suitable for use with WaveDrom + """ + siglist = [] + traces = [] + for name, samples in self._samples.iteritems(): + traces.append({"name": name, "wave": "".join(samples)}) + if name in self._data: + traces[-1]["data"] = " ".join([repr(s) for s in self._data[name]]) + + if len(traces) > 1: + traces.insert(0, self._name) + siglist.append(traces) + else: + siglist.append(traces[0]) + + if add_clock: + tracelen = len(traces[-1]["wave"]) + siglist.insert(0, {"name": "clk", "wave" : "p" + "."*(tracelen-1)}) + + return siglist + + +class trace(object): + """ + Context manager to enable tracing of signals + + Arguments are an arbitrary number of signals or busses to trace. + + We also require a clock to sample on, passed in as a keyword argument + + Usage: + + with trace(sig1, sig2, a_bus, clk=clk) as waves: + # Stuff happens, we trace it + + # Dump to JSON format compatible with wavedrom + j = waves.dumpj() + """ + + def __init__(self, *args, **kwargs): + self._clock = kwargs.get("clk", None) + + self._signals = [] + for arg in args: + self._signals.append(Wavedrom(arg)) + self._coro = None + self._clocks = 0 + + if self._clock is None: + raise ValueError("Trace requires a clock to sample") + + @cocotb.coroutine + def _monitor(self): + self._clocks = 0 + while True: + yield RisingEdge(self._clock) + yield ReadOnly() + self._clocks += 1 + for sig in self._signals: + sig.sample() + + def __enter__(self): + for sig in self._signals: + sig.clear() + self._coro = cocotb.fork(self._monitor()) + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self._coro.kill() + for sig in self._signals: + sig.clear() + return None + + def write(self, filename, **kwargs): + with open(filename, "w") as f: + f.write(self.dumpj(**kwargs)) + + + def dumpj(self, header="", footer=""): + trace = {"signal" : []} + trace["signal"].append( + {"name": "clock", "wave" : "p" + "."*(self._clocks-1)}) + for sig in self._signals: + trace["signal"].extend(sig.get(add_clock=False)) + if header: + trace["head"] = {"text": header} + if footer: + trace["foot"] = {"text": footer} + return json.dumps(trace) diff --git a/examples/endian_swapper/tests/test_endian_swapper.py b/examples/endian_swapper/tests/test_endian_swapper.py index 30549ab5..515e4a46 100644 --- a/examples/endian_swapper/tests/test_endian_swapper.py +++ b/examples/endian_swapper/tests/test_endian_swapper.py @@ -170,3 +170,21 @@ def randomly_switch_config(csr): factory.add_option("backpressure_inserter", [None, wave, intermittent_single_cycles, random_50_percent]) factory.generate_tests() +import cocotb.wavedrom + +@cocotb.test() +def wavedrom_test(dut): + """ + Generate a JSON wavedrom diagram of a trace + """ + cocotb.fork(clock_gen(dut.clk)) + yield RisingEdge(dut.clk) + tb = EndianSwapperTB(dut) + yield tb.reset() + + with cocotb.wavedrom.trace(dut.reset_n, tb.csr.bus, clk=dut.clk) as waves: + yield RisingEdge(dut.clk) + yield tb.csr.read(0) + yield RisingEdge(dut.clk) + yield RisingEdge(dut.clk) + dut.log.info(waves.dumpj()) From 2347562b9ba02c8be596f4b2d44d7c74e2edf726 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 18 Dec 2014 20:57:05 +0000 Subject: [PATCH 0714/2656] Cleanup: Thank windows for finding this one, type but not an error on Linux --- lib/vhpi/VhpiImpl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 2a56e6a0..b5fafd7c 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -127,7 +127,7 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) return NULL; if (vhpiVerilog == (type = vhpi_get(vhpiKindP, new_hdl))) { - vpi_free_object(vpi_hdl); + vhpi_release_handle(vpi_hdl); LOG_DEBUG("Not a VHPI object"); return NULL; } @@ -166,7 +166,7 @@ GpiObjHdl *VhpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) return NULL; if (vhpiVerilog == (type = vhpi_get(vhpiKindP, new_hdl))) { - vpi_free_object(vpi_hdl); + vhpi_release_handle(vpi_hdl); LOG_DEBUG("Not a VHPI object"); return NULL; } From be798839ee0e702f245512839bbd9bf530e00ab7 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 20 Dec 2014 07:36:11 +0000 Subject: [PATCH 0715/2656] Issue #179: Maintain signal ordering if possible --- cocotb/wavedrom.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cocotb/wavedrom.py b/cocotb/wavedrom.py index d111dd55..d529632e 100644 --- a/cocotb/wavedrom.py +++ b/cocotb/wavedrom.py @@ -37,10 +37,10 @@ class Wavedrom(object): """ def __init__(self, obj): - self._hdls = {} + self._hdls = OrderedDict() if isinstance(obj, Bus): - for name, hdl in obj._signals.iteritems(): - self._hdls[name] = hdl + for name in sorted(obj._signals.keys()): + self._hdls[name] = obj._signals[name] self._name = obj._name else: self._hdls[obj.name] = obj @@ -91,7 +91,8 @@ def get(self, add_clock=True): """ siglist = [] traces = [] - for name, samples in self._samples.iteritems(): + for name in self._hdls.iterkeys(): + samples = self._samples[name] traces.append({"name": name, "wave": "".join(samples)}) if name in self._data: traces[-1]["data"] = " ".join([repr(s) for s in self._data[name]]) @@ -175,4 +176,4 @@ def dumpj(self, header="", footer=""): trace["head"] = {"text": header} if footer: trace["foot"] = {"text": footer} - return json.dumps(trace) + return json.dumps(trace, indent=4, sort_keys=False) From 50be96e0be4920c96c79c95a555fa115391de7e0 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 20 Dec 2014 07:36:41 +0000 Subject: [PATCH 0716/2656] Cleanup: Correct long-standing bug in endian swapper --- examples/endian_swapper/hdl/endian_swapper.sv | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/endian_swapper/hdl/endian_swapper.sv b/examples/endian_swapper/hdl/endian_swapper.sv index 4a307a65..810d3b74 100644 --- a/examples/endian_swapper/hdl/endian_swapper.sv +++ b/examples/endian_swapper/hdl/endian_swapper.sv @@ -156,6 +156,7 @@ always @(posedge clk or negedge reset_n) begin byteswapping <= 1'b0; csr_readdatavalid <= 1'b0; end else begin + csr_readdatavalid <= 1'b0; if (csr_read) begin csr_readdatavalid <= !csr_waitrequest; case (csr_address) From 4ec1454e62f707b82aeaf048f256b30f8ccd1f36 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 20 Dec 2014 08:16:05 +0000 Subject: [PATCH 0717/2656] Issue #179: Correct compression of identical values in wavedrom trace --- cocotb/wavedrom.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cocotb/wavedrom.py b/cocotb/wavedrom.py index d529632e..40c760dc 100644 --- a/cocotb/wavedrom.py +++ b/cocotb/wavedrom.py @@ -67,14 +67,15 @@ def _lastval(samples): elif "u" in valstr: char = "u" elif "z" in valstr: char = "z" else: - if len(self._data[name]) and self._data[name][-1] == int(val): + if len(self._data[name]) and self._data[name][-1] == int(val) \ + and self._samples[name][-1] in "=.": char = "." else: char = "=" self._data[name].append(int(val)) # Detect if this is unchanged - if char == _lastval(self._samples[name]): + if len(valstr) == 1 and char == _lastval(self._samples[name]): char = "." self._samples[name].append(char) From 993c256c008f2794056b90db0563155da91a3a44 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sat, 20 Dec 2014 18:38:42 +0000 Subject: [PATCH 0718/2656] Enable discovery of interfaces in VPI --- include/vpi_user.h | 2 ++ lib/vpi/VpiImpl.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/vpi_user.h b/include/vpi_user.h index d7daa02d..3b9160ae 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -49,6 +49,8 @@ typedef uint32_t *vpiHandle; #define vpiRegBit 49 /* bit of vector reg */ #define vpiParameter 41 /* module parameter */ #define vpiStructVar 618 +#define vpiInterface 601 +#define vpiModport 606 #define vpiStop 66 /* execute simulator's $stop */ #define vpiFinish 67 /* execute simulator's $finish */ diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index c7cabae2..fe0ed215 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -97,10 +97,12 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) break; case vpiStructVar: case vpiModule: - new_obj = new GpiObjHdl(this, new_hdl); + case vpiInterface: + case vpiModport: + new_obj = new VpiObjHdl(this, new_hdl); break; default: - LOG_DEBUG("Not sure what to do with type %d for entity (%s)", type, name.c_str()); + LOG_INFO("Not sure what to do with type %d for entity (%s)", type, name.c_str()); return NULL; } From db13be86fe85d328f138ba7bead9377a62960f84 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sat, 20 Dec 2014 19:39:54 +0000 Subject: [PATCH 0719/2656] Test after merging, d'oh --- lib/vpi/VpiImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index fe0ed215..e6070af2 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -99,7 +99,7 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) case vpiModule: case vpiInterface: case vpiModport: - new_obj = new VpiObjHdl(this, new_hdl); + new_obj = new GpiObjHdl(this, new_hdl); break; default: LOG_INFO("Not sure what to do with type %d for entity (%s)", type, name.c_str()); From a79c07a8f40d9fd03259970f06b82037f949cb8e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 21 Dec 2014 14:47:26 +0000 Subject: [PATCH 0720/2656] Cleanup: Incorrect object type --- lib/vpi/VpiImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index fe0ed215..e6070af2 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -99,7 +99,7 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) case vpiModule: case vpiInterface: case vpiModport: - new_obj = new VpiObjHdl(this, new_hdl); + new_obj = new GpiObjHdl(this, new_hdl); break; default: LOG_INFO("Not sure what to do with type %d for entity (%s)", type, name.c_str()); From 17a89af3db69b319ac760089b394322293daa973 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 21 Dec 2014 19:02:45 +0000 Subject: [PATCH 0721/2656] Issue #144: Implement dynamic loading on windows, a few cleanups to files and specify the full windows path of the vhpi lib for aldec --- include/cocotb_utils.h | 7 +++---- lib/embed/Makefile | 2 +- lib/embed/gpi_embed.c | 3 --- lib/gpi/GpiCommon.cpp | 3 ++- lib/gpi/Makefile | 2 +- lib/gpi/gpi_priv.h | 1 - lib/utils/cocotb_utils.c | 27 ++++++++++++++++++------- lib/vhpi/Makefile | 4 ++-- makefiles/Makefile.inc | 2 +- makefiles/Makefile.pylib.Linux | 12 +---------- makefiles/Makefile.pylib.MINGW32_NT-6.1 | 8 ++++---- makefiles/simulators/Makefile.aldec | 27 ++++++++++++++++++++----- makefiles/simulators/Makefile.modelsim | 3 +-- 13 files changed, 58 insertions(+), 43 deletions(-) diff --git a/include/cocotb_utils.h b/include/cocotb_utils.h index 8e84dd83..6494307e 100644 --- a/include/cocotb_utils.h +++ b/include/cocotb_utils.h @@ -34,9 +34,8 @@ extern "C" { #endif -#ifdef __linux__ -#include -#endif +#define xstr(a) str(a) +#define str(a) #a extern void* utils_dyn_open(const char* lib_name); extern void* utils_dyn_sym(void *handle, const char* sym_name); @@ -45,4 +44,4 @@ extern void* utils_dyn_sym(void *handle, const char* sym_name); } #endif -#endif /* COCOTB_UTILS_H_ */ \ No newline at end of file +#endif /* COCOTB_UTILS_H_ */ diff --git a/lib/embed/Makefile b/lib/embed/Makefile index 3d475e64..2ed725be 100644 --- a/lib/embed/Makefile +++ b/lib/embed/Makefile @@ -30,7 +30,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += -GCC_ARGS += -DPYTHON_SO_LIB=libpython$(PYTHON_VERSION).$(LIB_EXT) +GCC_ARGS += -DPYTHON_SO_LIB=$(PYTHON_DYN_LIB) LIBS := $(PYLIBS) -lgpilog -lcocotbutils LD_PATH := -L$(LIB_DIR) SRCS := gpi_embed.c diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 65d6e5e5..b3efbcc7 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -52,9 +52,6 @@ static PyObject *pEventFn = NULL; * Stores the thread state for cocotb in static variable gtstate */ -#define xstr(a) str(a) -#define str(a) #a - void embed_init_python(void) { FENTER; diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 45ef6530..d4dc20e7 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -89,6 +89,7 @@ void gpi_embed_event(gpi_event_t level, const char *msg) static void gpi_load_libs(std::vector to_load) { +#define DOT_LIB_EXT "."xstr(LIB_EXT) std::vector::iterator iter; for (iter = to_load.begin(); @@ -96,7 +97,7 @@ static void gpi_load_libs(std::vector to_load) iter++) { void *lib_handle = NULL; - std::string full_name = "lib" + *iter + ".so"; + std::string full_name = "lib" + *iter + DOT_LIB_EXT; const char *now_loading = (full_name).c_str(); lib_handle = utils_dyn_open(now_loading); diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index 4e8842f0..7f78f215 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -30,7 +30,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += -GXX_ARGS += -DVPI_CHECKING +GXX_ARGS += -DVPI_CHECKING -D$(LIB_EXT) LIBS := -lcocotbutils -lgpilog -lcocotb -lstdc++ LD_PATH := -L$(LIB_DIR) LIB_NAME := libgpi diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index bdafb458..5581c135 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -232,7 +232,6 @@ typedef const void (*layer_entry_func)(void); extern "C" { \ const void NAME##_entry_point(void) \ { \ - printf("In entry point for " #NAME "\n"); \ func(); \ } \ } diff --git a/lib/utils/cocotb_utils.c b/lib/utils/cocotb_utils.c index cb2db298..f7b55720 100644 --- a/lib/utils/cocotb_utils.c +++ b/lib/utils/cocotb_utils.c @@ -30,10 +30,22 @@ #include #include +#ifdef __linux__ +#include +#else +#include +#endif + void* utils_dyn_open(const char* lib_name) { void *ret = NULL; -#ifdef __linux__ +#ifndef __linux__ + SetErrorMode(0); + ret = LoadLibrary(lib_name); + if (!ret) { + printf("Unable to open lib %s\n", lib_name); + } +#else /* Clear status */ dlerror(); @@ -41,8 +53,6 @@ void* utils_dyn_open(const char* lib_name) if (!ret) { printf("Unable to open lib %s (%s)\n", lib_name, dlerror()); } -#else - printf("Do something for windows here\n"); #endif return ret; } @@ -50,13 +60,16 @@ void* utils_dyn_open(const char* lib_name) void* utils_dyn_sym(void *handle, const char* sym_name) { void *entry_point; -#ifdef __linux__ +#ifndef __linux__ + entry_point = GetProcAddress(handle, sym_name); + if (!entry_point) { + printf("Unable to find symbol %s\n", sym_name); + } +#else entry_point = dlsym(handle, sym_name); if (!entry_point) { printf("Unable to find symbol %s (%s)\n", sym_name, dlerror()); } -#else - printf("Do something for windows here\n"); #endif return entry_point; -} \ No newline at end of file +} diff --git a/lib/vhpi/Makefile b/lib/vhpi/Makefile index 8ccfbe40..a2fadb4b 100644 --- a/lib/vhpi/Makefile +++ b/lib/vhpi/Makefile @@ -31,8 +31,8 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += GXX_ARGS += -DVHPI_CHECKING -LIBS := -lgpilog -lgpi -lstdc++ -LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) +LIBS := $(EXTRA_LIBS) -lgpilog -lgpi -lstdc++ +LD_PATH := $(EXTRA_LIBDIRS) -L$(LIB_DIR) LIB_NAME := libvhpi SRCS := VhpiImpl.cpp VhpiCbHdl.cpp diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 0a6df7fb..d4f52226 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -67,7 +67,7 @@ GXX_ARGS += -g -DDEBUG -fpic GCC_ARGS := $(GXX_ARGS) else ifeq ($(OS),MINGW32_NT-6.1) GXX_ARGS += -g -DDEBUG -shared -GCC_ARGS := $(GCC_ARGS) +GCC_ARGS := $(GXX_ARGS) LIB_EXT := dll PY_EXT := pyd else ifeq ($(OS),Linux) diff --git a/makefiles/Makefile.pylib.Linux b/makefiles/Makefile.pylib.Linux index cf7a882d..a18601e2 100644 --- a/makefiles/Makefile.pylib.Linux +++ b/makefiles/Makefile.pylib.Linux @@ -52,14 +52,4 @@ endif PYLIBS = $(shell python-config --libs) PYTHON_INCLUDEDIR := $(shell python -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_inc()') - -#debug:: -# @echo "Debug from Makefile.pylib" -# @echo -e "\tUsing PYTHON_VERSION=$(PYTHON_VERSION)" -# @echo -e "\tUsing PYTHON_LIBDIR=$(PYTHON_LIBDIR)" -# @echo -e "\tUsing PYTHON_SO_LIB=libpython$(PYTHON_VERSION).so" -# @echo -e "\tUsing PYTHON_DYNLIBDIR=$(PYTHON_DYNLIBDIR)" -# @echo -e "\tUsing PYTHON_INCLUDEDIR=$(PYTHON_INCLUDEDIR)" -# file $(PYTHON_LIBDIR)/libpython$(PYTHON_VERSION).so -# file -L $(PYTHON_LIBDIR)/libpython$(PYTHON_VERSION).so - +PYTHON_DYN_LIB = libpython$(PYTHON_VERSION).so \ No newline at end of file diff --git a/makefiles/Makefile.pylib.MINGW32_NT-6.1 b/makefiles/Makefile.pylib.MINGW32_NT-6.1 index bce4c61f..19e7583b 100644 --- a/makefiles/Makefile.pylib.MINGW32_NT-6.1 +++ b/makefiles/Makefile.pylib.MINGW32_NT-6.1 @@ -33,14 +33,14 @@ PYTHON_DIR=/tools/Python27 PYTHON_BIN=$(PYTHON_DIR)/python.exe # We might work with other Python versions -PYTHON_VERSION?=$(shell $(PYTHON_BIN) -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_version()') +LOCAL_PYTHON_VERSION?=$(shell $(PYTHON_BIN) -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_version()') PYTHON_DYNLIBDIR:=$(shell $(PYTHON_BIN) -c 'from distutils import sysconfig; print sysconfig.get_config_var("DESTSHARED")') PYTHON_LIBDIR=$(PYTHON_DIR)/libs period := . empty := -LOCAL_VERSION=$(subst $(period),$(empty),$(PYTHON_VERSION)) +PYTHON_VERSION=$(subst $(period),$(empty),$(LOCAL_PYTHON_VERSION)) PYTHON_INCLUDEDIR := $(shell $(PYTHON_BIN) -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_inc()') -PYLIBS = -lPython$(LOCAL_VERSION) -PYTHON_DYN_LIB=Python$(LOCAL_VERSION).lib \ No newline at end of file +PYLIBS = -lPython$(PYTHON_VERSION) +PYTHON_DYN_LIB = Python$(PYTHON_VERSION).dll \ No newline at end of file diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 30d90907..1889bd30 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -35,9 +35,6 @@ else CMD := vsimsa endif -EXTRA_LIBS = -laldecpli -lsupc++ -EXTRA_LIBDIRS = -L$(ALDEC_PATH)/bin/Linux64 - ALOG_ARGS += +define+COCOTB_SIM -dbg -pli libgpi ACOM_ARGS += -dbg @@ -50,7 +47,12 @@ ifeq ($(GPI_IMPL),vpi) GPI_ARGS = -pli libvpi endif ifeq ($(GPI_IMPL),vhpi) - GPI_ARGS = -loadvhpi $(LIB_DIR)/libvhpi:vhpi_startup_routines_bootstrap +ifeq ($(OS),MINGW32_NT-6.1) + VHPI_LIB = $(shell sh -c 'cd $(LIB_DIR) && pwd -W')/libvhpi +else + VHPI_LIB = $(LIB_DIR)/libvhpi +endif + GPI_ARGS = -loadvhpi $(VHPI_LIB):vhpi_startup_routines_bootstrap endif ifndef GPI_ARGS @@ -86,11 +88,26 @@ ifeq ($(COVERAGE),1) echo "acdb report -db work.acdb -txt -o coverage/acdb_report.txt" >> $@ endif +ifeq ($(ALDEC_PATH),) +$(error "Need to set ALDEC_PATH") +endif + +ifeq ($(OS),MINGW32_NT-6.1) + +EXTRA_LIBS := -laldecpli +EXTRA_LIBDIRS := -L$(ALDEC_PATH)/bin/ +else + +EXTRA_LIBS = -laldecpli +EXTRA_LIBDIRS = -L$(ALDEC_PATH)/bin/Linux64 +LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) +endif + # Note it's the redirection of the output rather than the 'do' command # that turns on batch mode (i.e. exit on completion/error) results.xml: $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(COCOTB_VPI_LIB) $(CUSTOM_SIM_DEPS) cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ - LD_LIBRARY_PATH=$(LIB_DIR):$(VSIMSABIN)/Linux64:$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do runsim.tcl | tee sim.log + $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do runsim.tcl | tee sim.log clean:: rm -rf $(SIM_BUILD) diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index 52fa2af2..3e6918dd 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -71,9 +71,8 @@ $(error "Need to set MODELSIM_PATH") endif EXTRA_LIBS := -lmtipli -EXTRA_LIBDIRS := -L$(MODELSIM_PATH)/modelsim_ase/win32aloem +EXTRA_LIBDIRS := -L$(MODELSIM_PATH)/win32aloem -LIB_LIAD := else LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) endif From a71b0c443cdcda4fac65539f090b93987c7351de Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 21 Dec 2014 19:08:59 +0000 Subject: [PATCH 0722/2656] Cleanup: forgot to actually define the extension on the command line --- lib/gpi/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index 7f78f215..09183855 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -30,7 +30,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += -GXX_ARGS += -DVPI_CHECKING -D$(LIB_EXT) +GXX_ARGS += -DVPI_CHECKING -DLIB_EXT=$(LIB_EXT) LIBS := -lcocotbutils -lgpilog -lcocotb -lstdc++ LD_PATH := -L$(LIB_DIR) LIB_NAME := libgpi From a328671815514fb5f14954b933bf167f7eb1189c Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 21 Dec 2014 20:23:54 +0000 Subject: [PATCH 0723/2656] Cleanup: Fix some over zealous sedage --- lib/Makefile | 2 +- lib/embed/gpi_embed.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index cc1c7002..462d17a0 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -37,7 +37,7 @@ ifndef GPI_IMPL endif -# FIXME it's a bit nasty to have the.$(LIB_EXT)urce files listed here as dependencies +# FIXME it's a bit nasty to have the source files listed here as dependencies # but better than re-building the libraries every time. $(LIB_OBJ_DIR): diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index b3efbcc7..ac4165f7 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -19,7 +19,7 @@ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT From 47b2fa83e3178fa8a1067de35aa3714054eee1e4 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 23 Dec 2014 21:49:10 +0000 Subject: [PATCH 0724/2656] Issue #144: Specify aldec option to not steal stdout on windows --- makefiles/simulators/Makefile.aldec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 1889bd30..8199ce97 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -73,7 +73,7 @@ endif ifdef SCRIPT_FILE echo "do $(SCRIPT_FILE)" >> $@ endif - echo "asim $(ASIM_ARGS) +access +w -O2 -dbg $(GPI_ARGS) $(TOPLEVEL)" >> $@ + echo "asim $(ASIM_ARGS) +access +w -interceptcoutput -O2 -dbg $(GPI_ARGS) $(TOPLEVEL)" >> $@ ifeq ($(WAVES),1) echo "log -recursive *" >> $@ endif From 2bacbbbfb47cb4f9fa3db354bc341540a7180111 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sat, 27 Dec 2014 08:51:26 +0000 Subject: [PATCH 0725/2656] Add ability to enable/disable the trace --- cocotb/wavedrom.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/cocotb/wavedrom.py b/cocotb/wavedrom.py index 40c760dc..0e4d0129 100644 --- a/cocotb/wavedrom.py +++ b/cocotb/wavedrom.py @@ -43,7 +43,7 @@ def __init__(self, obj): self._hdls[name] = obj._signals[name] self._name = obj._name else: - self._hdls[obj.name] = obj + self._hdls[obj.name.split(".")[-1]] = obj self.clear() @@ -54,7 +54,7 @@ def sample(self): def _lastval(samples): for x in range(len(samples)-1, -1, -1): - if samples[x] not in "=.": return samples[x] + if samples[x] not in "=.|": return samples[x] return None for name, hdl in self._hdls.iteritems(): @@ -86,6 +86,10 @@ def clear(self): self._samples = defaultdict(list) self._data = defaultdict(list) + def gap(self): + for name, hdl in self._hdls.iteritems(): + self._samples[name].append("|") + def get(self, add_clock=True): """ Return the samples as a list suitable for use with WaveDrom @@ -136,6 +140,7 @@ def __init__(self, *args, **kwargs): self._signals.append(Wavedrom(arg)) self._coro = None self._clocks = 0 + self._enabled = False if self._clock is None: raise ValueError("Trace requires a clock to sample") @@ -146,13 +151,26 @@ def _monitor(self): while True: yield RisingEdge(self._clock) yield ReadOnly() + if not self._enabled: continue self._clocks += 1 for sig in self._signals: sig.sample() + def insert_gap(self): + self._clocks += 1 + for sig in self._signals: + sig.gap() + + def disable(self): + self._enabled = False + + def enable(self): + self._enabled = True + def __enter__(self): for sig in self._signals: sig.clear() + self.enable() self._coro = cocotb.fork(self._monitor()) return self @@ -160,6 +178,7 @@ def __exit__(self, exc_type, exc_val, exc_tb): self._coro.kill() for sig in self._signals: sig.clear() + self.disable() return None def write(self, filename, **kwargs): From 7c5476674e53dbbd159677287d2b53d7cfebd172 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 31 Dec 2014 23:02:39 +0000 Subject: [PATCH 0726/2656] Issue 144; Remove need lib build dir to PATH on windows, inspiration from jmoriaty --- makefiles/simulators/Makefile.aldec | 2 ++ makefiles/simulators/Makefile.icarus | 3 ++- makefiles/simulators/Makefile.modelsim | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 8199ce97..9ba227c4 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -96,6 +96,8 @@ ifeq ($(OS),MINGW32_NT-6.1) EXTRA_LIBS := -laldecpli EXTRA_LIBDIRS := -L$(ALDEC_PATH)/bin/ +OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') +LIB_LOAD := PATH=$(OLD_PATH):$(LIB_DIR) else EXTRA_LIBS = -laldecpli diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index a27525d1..565d8eb4 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -42,7 +42,8 @@ endif EXTRA_LIBS := -lvpi EXTRA_LIBDIRS := -L$(ICARUS_PATH)/lib -LIB_LOAD := +OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') +LIB_LOAD := PATH=$(OLD_PATH):$(LIB_DIR) else LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) endif diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index 3e6918dd..d7faf282 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -72,6 +72,8 @@ endif EXTRA_LIBS := -lmtipli EXTRA_LIBDIRS := -L$(MODELSIM_PATH)/win32aloem +OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') +LIB_LOAD := PATH=$(OLD_PATH):$(LIB_DIR) else LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) From 1a50effe37636f9ea2a6b0c85b4cdf4bf02d5342 Mon Sep 17 00:00:00 2001 From: Darrell Harmon Date: Sun, 4 Jan 2015 16:45:06 -0700 Subject: [PATCH 0727/2656] fix bug where minimum signed value was reported as 0 --- cocotb/binary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 616235e3..561eef18 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -240,7 +240,7 @@ def get_value_signed(self): if (ival & signbit) == 0: return ival else: - return -1 * (int(~ival+1) & (signbit - 1)) + return -1 * (1+(int(~ival) & (signbit - 1))) def set_value(self, integer): self._str = self._convert_to[self.binaryRepresentation](integer) From 499bbfe260cd891629887626f6347413bcd9935f Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 9 Jan 2015 19:57:51 +0000 Subject: [PATCH 0728/2656] Add support for coverage collection of python tests using coverage.py --- cocotb/regression.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 3c957ddb..0befc8e9 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -33,7 +33,7 @@ import logging import inspect from itertools import product - +import sys import os # For autodocumentation don't need the extension modules if "SPHINX_BUILD" in os.environ: @@ -41,6 +41,14 @@ else: import simulator +# Optional support for coverage collection of testbench files +coverage = None +if "COVERAGE" in os.environ: + try: + import coverage + except ImportError: + sys.stderr.write("Coverage collection requested but coverage module not availble") + import cocotb import cocotb.ANSI as ANSI from cocotb.log import SimLog @@ -71,6 +79,7 @@ def __init__(self, root_name, modules, tests=None): self._modules = modules self._functions = tests self._running_test = None + self._cov = None self.log = SimLog("cocotb.regression") def initialise(self): @@ -82,6 +91,11 @@ def initialise(self): self.xunit = XUnitReporter() self.xunit.add_testsuite(name="all", tests=repr(self.ntests), package="all") + if coverage is not None: + self.log.info("Enabling coverage collection of Python code") + self._cov = coverage.coverage(branch=True, omit=["*cocotb*"]) + self._cov.start() + self._dut = cocotb.handle.SimHandle(simulator.get_root_handle(self._root_name)) if self._dut is None: raise AttributeError("Can not find Root Handle (%s)" % root_name) @@ -135,6 +149,11 @@ def tear_down(self): else: self.log.info("Passed %d tests (%d skipped)" % (self.count-1, self.skipped)) + if self._cov: + self._cov.stop() + self.log.info("Writing coverage data") + self._cov.save() + self._cov.html_report() self.log.info("Shutting down...") self.xunit.write() simulator.stop_simulator() From 2459d0c1f4df03a8dd5473290341c5539fcc8c32 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 9 Jan 2015 20:23:05 +0000 Subject: [PATCH 0729/2656] Print a meaningfull error if coverage unavailable (travis issues) --- cocotb/regression.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 0befc8e9..bd3e49b3 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -46,8 +46,9 @@ if "COVERAGE" in os.environ: try: import coverage - except ImportError: - sys.stderr.write("Coverage collection requested but coverage module not availble") + except ImportError as e: + sys.stderr.write("Coverage collection requested but coverage module not availble\n") + sys.stderr.write("Import error was: %s\n" % repr(e)) import cocotb import cocotb.ANSI as ANSI From ebb80bb2a1963c76b9b33b9f923e93b0ae7ab6b0 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sat, 10 Jan 2015 13:11:13 +0000 Subject: [PATCH 0730/2656] Issue #9: Tweaks to documentation --- documentation/source/examples.rst | 48 +++++++++++++++++++++++++++ documentation/source/index.rst | 3 +- documentation/source/introduction.rst | 42 +++++++++++++++-------- 3 files changed, 79 insertions(+), 14 deletions(-) create mode 100644 documentation/source/examples.rst diff --git a/documentation/source/examples.rst b/documentation/source/examples.rst new file mode 100644 index 00000000..9b1ff38b --- /dev/null +++ b/documentation/source/examples.rst @@ -0,0 +1,48 @@ +######## +Examples +######## + +These code samples show some typical usage of Cocotb based on real-world problems. + + +Example testbench for snipped of code from `comp.lang.verilog `_: + +.. code-block:: python + + @cocotb.coroutine + def run_test(dut, data_generator=random_data, delay_cycles=2): + """ + Send data through the DUT and check it is sorted out output + """ + cocotb.fork(Clock(dut.clk, 100).start()) + + # Don't check until valid output + expected = [None] * delay_cycles + + for index, values in enumerate(data_generator(bits=len(dut.in1))): + expected.append(sorted(values)) + + yield RisingEdge(dut.clk) + dut.in1 = values[0] + dut.in2 = values[1] + dut.in3 = values[2] + dut.in4 = values[3] + dut.in5 = values[4] + + yield ReadOnly() + expect = expected.pop(0) + + if expect is None: continue + + + got = [int(dut.out5), int(dut.out4), int(dut.out3), + int(dut.out2), int(dut.out1)] + + if got != expect: + dut.log.error('Expected %s' % expect) + dut.log.error('Got %s' % got) + raise TestFailure("Output didn't match") + + dut.log.info('Sucessfully sent %d cycles of data' % (index + 1)) + + diff --git a/documentation/source/index.rst b/documentation/source/index.rst index 3ebce1e8..5c86268e 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -3,7 +3,7 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to cocotb's documentation! +Welcome to Cocotb's documentation! ================================== Contents: @@ -12,6 +12,7 @@ Contents: :maxdepth: 2 introduction + examples quickstart coroutines endian_swapper diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index ed178cb9..2d23193d 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -5,7 +5,7 @@ Introduction What is cocotb? =============== -**Cocotb** is a *coroutine* based *cosimulation* *testbench* environment for testing VHDL/Verilog RTL using `Python `_. +**Cocotb** is a *COroutine* based *COsimulation* *TestBench* environment for verifying VHDL/Verilog RTL using `Python `_. **Cocotb** is completely free, open source (under the `BSD License `_) and hosted on `GitHub `_. @@ -17,17 +17,33 @@ What is cocotb? * Cadence Incisive * Mentor Modelsim -**Cocotb** was developed by `Potential Ventures `_ with the support of `Solarflare Communications Ltd `_ and contributions from Gordon McGregor and Finn Grimwood. +**Cocotb** can be used live in a web-browser using `EDA Playground `_. -**Cocotb** can be used live in a web-browser on the excellent `EDA Playground `_. +What does Cocotb look like? +=========================== + +For some examples of Cocotb in action take a look at the `Examples`_ section. + +See the Jenkins pages for the Cocotb regression for a + + + +How is Cocotb different? +======================== -How is cocotb different? ------------------------- **Cocotb** encourages the same philosophy of design re-use and randomised testing as UVM, however is implemented in Python rather than SystemVerilog. -In cocotb VHDL/Verilog/SystemVerilog are only used for the synthesisable design. All verification is done using Python which has various advantages over using SystemVerilog or VHDL for verification: +In Cocotb VHDL/Verilog/SystemVerilog are only used for the synthesisable design. + +**Cocotb** has built-in support for integrating with the `Jenkins `_ continuous integration system. + +Cocotb was specifically designed to lower the overhead of creating a test. + +**Cocotb** automatically discovers tests so that no additional step is required to add a test to a regression. + +All verification is done using Python which has various advantages over using SystemVerilog or VHDL for verification: * Writing Python is **fast** - it's a very productive language * It's **easy** to interface to other languages from Python @@ -35,15 +51,10 @@ In cocotb VHDL/Verilog/SystemVerilog are only used for the synthesisable design. * Python is **interpreted**. Tests can be edited and re-run them without having to recompile the design or exit the simulator GUI. * Python is **popular** - far more engineers know Python than SystemVerilog or VHDL -**Cocotb** was specifically designed to lower the overhead of creating a test. - -**Cocotb** has built-in support for integrating with the `Jenkins `_ continuous integration system. - -**Cocotb** automatically discovers tests so that no additional step is required to add a test to a regression. -How does Cocotb work -==================== +How does Cocotb work? +===================== Overview -------- @@ -55,3 +66,8 @@ A typical cocotb testbench requires no additional RTL code. The Design Under Tes A test is simply a Python function. At any given time either the simulator is advancing time or the Python code is executing. The **yield** keyword is used to indicate when to pass control of execution back to the simulator. A test can spawn multiple coroutines, allowing for independent flows of execution. + +Contributors +============ + +**Cocotb** was developed by `Potential Ventures `_ with the support of `Solarflare Communications Ltd `_ and contributions from Gordon McGregor and Finn Grimwood (see `contributers `_ for full list of contributions). From bfc7c20ac18852b15982f186cbaf3f13e68d4b61 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Sun, 11 Jan 2015 09:54:18 +0100 Subject: [PATCH 0731/2656] Add travis-ci integration Add .travis.yml and bin/report_results.py --- .travis.yml | 21 +++++++++++++++++++++ Makefile | 1 + bin/report_results.py | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 .travis.yml create mode 100644 bin/report_results.py diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..7abe7907 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,21 @@ +language: python +python: + - "2.7" +before_install: + - sudo add-apt-repository -y ppa:team-electronics/ppa + - sudo apt-get update -qq +install: + - sudo apt-get install -qq iverilog-daily + - pip install coverage + - pip install xunitparser + +# - export COVERAGE=0 + - export PYTHONPATH=$PYTHONPATH:/home/travis/virtualenv/python2.7.9/lib/python2.7/site-packages + +# Build iverilog from source +# - sudo apt-get install gperf +# - wget -O iverilog-master.zip https://github.com/steveicarus/iverilog/archive/master.zip +# - unzip iverilog-master.zip +# - cd iverilog-master && autoconf && ./configure && make && sudo make install && cd .. +script: + - make test diff --git a/Makefile b/Makefile index 8567d926..8a20137d 100644 --- a/Makefile +++ b/Makefile @@ -45,6 +45,7 @@ clean: test: $(MAKE) -k -C examples ./bin/combine_results.py + ./bin/report_results.py combined_results.xml pycode: @cp -R $(SIM_ROOT)/cocotb $(FULL_INSTALL_DIR)/ diff --git a/bin/report_results.py b/bin/report_results.py new file mode 100644 index 00000000..e6e2e573 --- /dev/null +++ b/bin/report_results.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +""" +Simple script to report JUnit test results + +""" + +import sys + +def report_results(xml_filename): + xunitparser = None + try: + import xunitparser + except ImportError as e: + sys.stderr.write("xunitparser module not availble results report not avaliable\n") + sys.stderr.write("Import error was: %s\n" % repr(e)) + + + if xunitparser is not None: + ts, tr = xunitparser.parse(open(xml_filename)) + report = {'success': 0, 'skipped': 0, 'failed': 0, 'errored': 0} + for tc in ts: + report[tc.result] += 1 + + print 'Test Report:', report + if report['failed'] or report['errored'] or report['skipped']: + return 1 + + return 0 + +if __name__ == "__main__": + if len(sys.argv) is not 2: + print "Please specify a result file" + exit(1) + + exit(report_results(sys.argv[1])) + + From 0356907e9e028544354f37cdc0b9a985363d5852 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Sun, 11 Jan 2015 09:59:50 +0100 Subject: [PATCH 0732/2656] fix file permission --- bin/report_results.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 bin/report_results.py diff --git a/bin/report_results.py b/bin/report_results.py old mode 100644 new mode 100755 From 441fa39c9f02e7fed43aa0c0336b1fe540f70252 Mon Sep 17 00:00:00 2001 From: Jeremy Herbert Date: Sun, 11 Jan 2015 08:58:34 -0800 Subject: [PATCH 0733/2656] changed the makefile to work with either python2/python3 --- makefiles/Makefile.pylib.Linux | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/makefiles/Makefile.pylib.Linux b/makefiles/Makefile.pylib.Linux index a18601e2..33566caf 100644 --- a/makefiles/Makefile.pylib.Linux +++ b/makefiles/Makefile.pylib.Linux @@ -35,9 +35,9 @@ NATIVE_ARCH=$(shell uname -m) ARCH?=$(NATIVE_ARCH) # We might work with other Python versions -PYTHON_VERSION?=$(shell python -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_version()') -PYTHON_LIBDIR:=$(shell python -c 'from distutils import sysconfig; print sysconfig.get_config_var("LIBDIR")') -PYTHON_DYNLIBDIR:=$(shell python -c 'from distutils import sysconfig; print sysconfig.get_config_var("DESTSHARED")') +PYTHON_VERSION?=$(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') +PYTHON_LIBDIR:=$(shell python -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("LIBDIR"))') +PYTHON_DYNLIBDIR:=$(shell python -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("DESTSHARED"))') # Nasty hack but there's no way in Python properly get the directories for 32-bit Python on a 64-bit system # TODO: Under OSX we can use "export VERSIONER_PYTHON_PREFER_32_BIT=yes", no Linux equivalent though @@ -51,5 +51,5 @@ endif # is we simply link against all dynamic libraries in the Python installation PYLIBS = $(shell python-config --libs) -PYTHON_INCLUDEDIR := $(shell python -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_inc()') -PYTHON_DYN_LIB = libpython$(PYTHON_VERSION).so \ No newline at end of file +PYTHON_INCLUDEDIR := $(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())') +PYTHON_DYN_LIB = libpython$(PYTHON_VERSION).so From 02b579e497ba6da092c2e0f41d26e77905a4a1b1 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 11 Jan 2015 17:41:12 +0000 Subject: [PATCH 0734/2656] Ignore the combined results (test Travis webhook) --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 26088659..a0c34457 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ __pycache__ # Results results.xml +combined_results.xml # VCS files *.tab From c912f4c6aad66a92fe11871e774779c10aad8e1b Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 11 Jan 2015 17:45:50 +0000 Subject: [PATCH 0735/2656] Don't fail for skipped tests --- bin/report_results.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/report_results.py b/bin/report_results.py index e6e2e573..60f05c0d 100755 --- a/bin/report_results.py +++ b/bin/report_results.py @@ -22,7 +22,7 @@ def report_results(xml_filename): report[tc.result] += 1 print 'Test Report:', report - if report['failed'] or report['errored'] or report['skipped']: + if report['failed'] or report['errored']: return 1 return 0 From a4a208f3a12b4d51a285c71f421f3fe979459240 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 11 Jan 2015 17:46:36 +0000 Subject: [PATCH 0736/2656] Add Travis-ci badge to README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e336cbb6..e9ffedbb 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ **cocotb** is a coroutine based cosimulation library for writing VHDL and Verilog testbenches in Python. +[![Build Status](https://travis-ci.org/potentialventures/cocotb.svg?branch=master)](https://travis-ci.org/potentialventures/cocotb) * Skim the introductory presentation: http://potential.ventures * Read the [documentation](http://cocotb.readthedocs.org) From da0cb829fcb76a76e0c30e01abe03663c12c709b Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 12 Jan 2015 18:19:27 +0000 Subject: [PATCH 0737/2656] Issue #186: Do not raise exception of TestFailed as should be TestFailure, we also import already so do not do cocotb.TestFailure just TestFailure. Also for icarus expect the test to fail as it handles the errors by printing but nothing else --- examples/functionality/tests/test_cocotb.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index ec26d420..53f998ee 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -126,10 +126,10 @@ def test_coroutine_kill(dut): yield Timer(100) clk_gen.kill() if test_flag is not False: - raise cocotb.TestFailed + raise TestFailure yield Timer(1000) if test_flag is not True: - raise cocotb.TestFailed + raise TestFailure @cocotb.test(expect_error=True) def test_adding_a_coroutine_without_starting(dut): @@ -169,7 +169,7 @@ def do_test_afterdelay_in_readonly(dut, delay): yield Timer(delay) exited = True -@cocotb.test(expect_error=True) +@cocotb.test(expect_error=True, expect_fail=cocotb.SIM_NAME in ["Icarus Verilog"]) def test_readwrite_in_readonly(dut): """Test doing invalid sim operation""" global exited @@ -179,9 +179,9 @@ def test_readwrite_in_readonly(dut): yield [Join(coro), Timer(10000)] clk_gen.kill() if exited is not True: - raise cocotb.TestFailed + raise TestFailure -@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog", "Chronologic Simulation VCS Release "]) +@cocotb.test(expect_fail=cocotb.SIM_NAME in ["Icarus Verilog", "Chronologic Simulation VCS Release "]) def test_afterdelay_in_readonly(dut): """Test doing invalid sim operation""" global exited @@ -191,7 +191,7 @@ def test_afterdelay_in_readonly(dut): yield [Join(coro), Timer(1000)] clk_gen.kill() if exited is not True: - raise cocotb.TestFailed + raise TestFailure @cocotb.test() def test_afterdelay_in_readonly_valid(dut): @@ -203,7 +203,7 @@ def test_afterdelay_in_readonly_valid(dut): yield [Join(coro), Timer(100000)] clk_gen.kill() if exited is not True: - raise cocotb.TestFailed + raise TestFailure @cocotb.coroutine def clock_one(dut): From b21470e94e89a3799e768a85a4264b0fe7504bf5 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Tue, 13 Jan 2015 19:30:03 +0000 Subject: [PATCH 0738/2656] Issue #186: Fix some Python 3.x incompatibilities --- cocotb/binary.py | 3 +++ cocotb/drivers/amba.py | 4 ++-- cocotb/drivers/avalon.py | 2 +- cocotb/scoreboard.py | 2 +- cocotb/wavedrom.py | 2 +- cocotb/xunit_reporter.py | 2 +- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 561eef18..002bcd1a 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -324,6 +324,9 @@ def __le__(self, other): def __str__(self): return "%d" % (self.value) + def __bool__(self): + return self.__nonzero__() + def __nonzero__(self): """Provide boolean testing of a binstr. diff --git a/cocotb/drivers/amba.py b/cocotb/drivers/amba.py index 8f7c7f27..0b431994 100644 --- a/cocotb/drivers/amba.py +++ b/cocotb/drivers/amba.py @@ -72,7 +72,7 @@ def _send_write_address(self, address, delay=0): Send the write address, with optional delay """ yield self.write_address_busy.acquire() - for cycle in xrange(delay): + for cycle in range(delay): yield RisingEdge(self.clock) self.bus.AWADDR <= address @@ -93,7 +93,7 @@ def _send_write_data(self, data, delay=0, byte_enable=0xF): Send the write address, with optional delay """ yield self.write_data_busy.acquire() - for cycle in xrange(delay): + for cycle in range(delay): yield RisingEdge(self.clock) self.bus.WDATA <= data diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index a5307fb4..caef9939 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -304,7 +304,7 @@ def _send_string(self, string, sync=True): firstword = True # FIXME busses that aren't integer numbers of bytes - bus_width = len(self.bus.data) / 8 + bus_width = int(len(self.bus.data) / 8) word = BinaryValue(bits=len(self.bus.data), bigEndian=self.config['firstSymbolInHighOrderBits']) diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index c09a2845..15c780eb 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -165,7 +165,7 @@ def check_received_transaction(transaction): exp = expected_output(transaction) elif len(expected_output): - for i in xrange(min((reorder_depth+1), len(expected_output))): + for i in range(min((reorder_depth+1), len(expected_output))): if expected_output[i] == transaction: break else: diff --git a/cocotb/wavedrom.py b/cocotb/wavedrom.py index 0e4d0129..246869e7 100644 --- a/cocotb/wavedrom.py +++ b/cocotb/wavedrom.py @@ -96,7 +96,7 @@ def get(self, add_clock=True): """ siglist = [] traces = [] - for name in self._hdls.iterkeys(): + for name in self._hdls.keys(): samples = self._samples[name] traces.append({"name": name, "wave": "".join(samples)}) if name in self._data: diff --git a/cocotb/xunit_reporter.py b/cocotb/xunit_reporter.py index dc2275c2..e032002d 100644 --- a/cocotb/xunit_reporter.py +++ b/cocotb/xunit_reporter.py @@ -42,7 +42,7 @@ def countlines(self): def head(self, lines_2find=1): self.seek(0) #Rewind file - return [self.next() for x in xrange(lines_2find)] + return [self.next() for x in range(lines_2find)] def tail(self, lines_2find=1): self.seek(0, 2) #go to end of file From 28b4778951731b6bb2cc84d56f9ba09cd54c3f00 Mon Sep 17 00:00:00 2001 From: Jeremy Herbert Date: Sun, 11 Jan 2015 09:58:44 -0800 Subject: [PATCH 0739/2656] Python 3 port --- .gitignore | 3 + cocotb/binary.py | 12 ++-- cocotb/decorators.py | 11 +++- cocotb/drivers/__init__.py | 5 +- cocotb/drivers/avalon.py | 2 +- cocotb/generators/bit.py | 2 +- cocotb/generators/byte.py | 2 +- cocotb/handle.py | 18 ++++-- cocotb/monitors/avalon.py | 2 +- cocotb/regression.py | 2 +- cocotb/result.py | 14 +++-- cocotb/scoreboard.py | 2 +- cocotb/utils.py | 15 ++++- cocotb/wavedrom.py | 4 +- cocotb/xunit_reporter.py | 4 +- examples/Makefile | 3 +- examples/adder/hdl/adder.v | 15 +++++ examples/adder/tests/Makefile | 51 ++++++++++++++++ examples/adder/tests/test_adder.py | 43 +++++++++++++ examples/endian_swapper/cosim/io_module.h | 5 ++ .../tests/test_endian_swapper.py | 4 +- examples/functionality/tests/test_cocotb.py | 2 +- .../ping_tun_tap/tests/test_icmp_reply.py | 2 +- examples/plusargs/plusargs.py | 3 +- lib/compat/python3_compat.h | 22 +++++++ lib/embed/gpi_embed.c | 9 ++- lib/gpi_log/gpi_logging.c | 13 ++-- lib/simulator/Makefile | 2 +- lib/simulator/simulatormodule.c | 22 ++----- lib/simulator/simulatormodule.h | 6 ++ lib/simulator/simulatormodule_python2.c | 39 ++++++++++++ lib/simulator/simulatormodule_python3.c | 60 +++++++++++++++++++ makefiles/Makefile.pylib.Linux | 5 +- 33 files changed, 343 insertions(+), 61 deletions(-) create mode 100644 examples/adder/hdl/adder.v create mode 100644 examples/adder/tests/Makefile create mode 100644 examples/adder/tests/test_adder.py create mode 100644 lib/compat/python3_compat.h create mode 100644 lib/simulator/simulatormodule_python2.c create mode 100644 lib/simulator/simulatormodule_python3.c diff --git a/.gitignore b/.gitignore index a0c34457..4d01d206 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ # C extensions *.so +# C objects +*.o + # Packages *.egg *.egg-info diff --git a/cocotb/binary.py b/cocotb/binary.py index 002bcd1a..4c4f227a 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -27,7 +27,9 @@ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' +from __future__ import print_function from math import log,ceil +from cocotb.utils import get_python_integer_types def resolve(string): for char in BinaryValue._resolve_to_0: @@ -103,7 +105,7 @@ def assign(self, value): if the string contains any characters that aren't 0, 1, X or Z then we interpret the string as a binary buffer... """ - if isinstance(value, (int, long)): + if isinstance(value, utils.get_python_integer_types()): self.value = value elif isinstance(value, str): try: @@ -180,7 +182,7 @@ def _adjust_unsigned(self,x): else: rv = '0' * (self._bits-l) + x elif l > self._bits: - print "WARNING truncating value to match requested number of bits (%d -> %d)" % (l,self._bits) + print("WARNING truncating value to match requested number of bits (%d -> %d)" % (l,self._bits)) if self.big_endian: rv = x[l - self._bits:] else: @@ -200,7 +202,7 @@ def _adjust_signed_mag(self,x): rv = '0' * (self._bits-1-l) + x[1:] rv = x[0] + rv elif l > self._bits: - print "WARNING truncating value to match requested number of bits (%d -> %d)" % (l,self._bits) + print("WARNING truncating value to match requested number of bits (%d -> %d)" % (l,self._bits)) if self.big_endian: rv = x[l - self._bits:] else: @@ -219,7 +221,7 @@ def _adjust_twos_comp(self,x): else: rv = x[0] * (self._bits-l) + x elif l > self._bits: - print "WARNING truncating value to match requested number of bits (%d -> %d)" % (l,self._bits) + print("WARNING truncating value to match requested number of bits (%d -> %d)" % (l,self._bits)) if self.big_endian: rv = x[l - self._bits:] else: @@ -294,7 +296,7 @@ def _adjust(self): else: self._str = "0" * (self._bits-l) + self._str elif l > self._bits: - print "WARNING truncating value to match requested number of bits (%d)" % l + print("WARNING truncating value to match requested number of bits (%d)" % l) self._str = self._str[l - self._bits:] buff = property(get_buff, set_buff, None, "Access to the value as a buffer") diff --git a/cocotb/decorators.py b/cocotb/decorators.py index c09795cc..5f5b6def 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -32,6 +32,8 @@ import threading import pdb +from io import StringIO, BytesIO + import cocotb from cocotb.log import SimLog from cocotb.triggers import _Join, PythonTrigger, Timer, Event, NullTrigger @@ -218,7 +220,14 @@ def __call__(self, *args, **kwargs): except Exception as e: traceback.print_exc() result = TestError(str(e)) - traceback.print_exc(file=result.stderr) + if sys.version_info.major >= 3: + buff = StringIO() + traceback.print_exc(file=buff) + else: + buff_bytes = BytesIO() + traceback.print_exc(file=buff_bytes) + buff = StringIO(buff_bytes.getvalue().decode("UTF-8")) + result.stderr.write(buff.getvalue()) raise result def __get__(self, obj, type=None): diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index 431d5c3c..ef3d177a 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -31,7 +31,6 @@ Set of common driver base classes """ - import logging import cocotb @@ -70,7 +69,7 @@ def _cr_twiddler(self, generator=None): # Actual thread while True: - on,off = self._generator.next() + on,off = next(self._generator) self._signal <= 1 for i in range(on): yield edge @@ -285,7 +284,7 @@ def _next_valids(self): if self.valid_generator is not None: while not self.on: try: - self.on, self.off = self.valid_generator.next() + self.on, self.off = next(self.valid_generator) except StopIteration: self.on = True # If the generator runs out stop inserting non-valid cycles self.log.info("Valid generator exhausted, not inserting non-valid cycles anymore") diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index caef9939..7d279549 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -276,7 +276,7 @@ def __init__(self, *args, **kwargs): self.config = AvalonSTPkts._default_config.copy() - for configoption, value in config.iteritems(): + for configoption, value in config.items(): self.config[configoption] = value self.log.debug("Setting config option %s to %s" % (configoption, str(value))) diff --git a/cocotb/generators/bit.py b/cocotb/generators/bit.py index 671e04a1..27ffb43b 100644 --- a/cocotb/generators/bit.py +++ b/cocotb/generators/bit.py @@ -49,7 +49,7 @@ def bit_toggler(gen_on, gen_off): gen_off (generator): generator that yields number of cycles off """ while True: - yield int(abs(gen_on.next())), int(abs(gen_off.next())) + yield int(abs(next(gen_on))), int(abs(next(gen_off))) @public def intermittent_single_cycles(mean=10, sigma=None): diff --git a/cocotb/generators/byte.py b/cocotb/generators/byte.py index 00c1ee5e..402d7570 100644 --- a/cocotb/generators/byte.py +++ b/cocotb/generators/byte.py @@ -40,7 +40,7 @@ def get_bytes(nbytes, generator): """Get nbytes from generator""" result = "" for i in range(nbytes): - result += generator.next() + result += next(generator) return result @public diff --git a/cocotb/handle.py b/cocotb/handle.py index 768c905a..d489dace 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -33,7 +33,9 @@ import ctypes import traceback import sys -from StringIO import StringIO +#from StringIO import StringIO + +from io import StringIO, BytesIO import os @@ -48,6 +50,7 @@ from cocotb.log import SimLog from cocotb.result import TestError from cocotb.triggers import _RisingEdge, _FallingEdge +from cocotb.utils import get_python_integer_types class SimHandle(object): @@ -83,9 +86,14 @@ def __getattr__(self, name): return self._sub_handles[name] def _raise_testerror(self, msg): - buff = StringIO() lastframe = sys._getframe(2) - traceback.print_stack(lastframe, file=buff) + if sys.version_info.major >= 3: + buff = StringIO() + traceback.print_stack(lastframe, file=buff) + else: + buff_bytes = BytesIO() + traceback.print_stack(lastframe, file=buff_bytes) + buff = StringIO(buff_bytes.getvalue().decode("UTF8")) self.log.error("%s\n%s" % (msg, buff.getvalue())) exception = TestError(msg) exception.stderr.write(buff.getvalue()) @@ -150,13 +158,13 @@ def setimmediatevalue(self, value): Assigning integers less than 32-bits is faster """ - if isinstance(value, (int, long)) and value < 0x7fffffff: + if isinstance(value, get_python_integer_types()) and value < 0x7fffffff: simulator.set_signal_val(self._handle, value) return if isinstance(value, ctypes.Structure): value = BinaryValue(value=cocotb.utils.pack(value), bits=len(self)) - elif isinstance(value, (int, long)): + elif isinstance(value, get_python_integer_types()): value = BinaryValue(value=value, bits=len(self), bigEndian=False) elif not isinstance(value, BinaryValue): self.log.critical("Unsupported type for value assignment: %s (%s)" % (type(value), repr(value))) diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index dbd02977..4354882d 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -83,7 +83,7 @@ def __init__(self, *args, **kwargs): self.config = AvalonSTPkts._default_config.copy() - for configoption, value in config.iteritems(): + for configoption, value in config.items(): self.config[configoption] = value self.log.debug("Setting config option %s to %s" % (configoption, str(value))) diff --git a/cocotb/regression.py b/cocotb/regression.py index bd3e49b3..2bcb1fba 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -354,7 +354,7 @@ def generate_tests(self): name = "%s_%03d" % (self.name, index + 1) doc = "Automatically generated test\n\n" - for optname, optvalue in testoptions.iteritems(): + for optname, optvalue in testoptions.items(): if callable(optvalue): if not optvalue.__doc__: desc = "No docstring supplied" else: desc = optvalue.__doc__.split('\n')[0] diff --git a/cocotb/result.py b/cocotb/result.py index ff0ffe04..ba9e763d 100644 --- a/cocotb/result.py +++ b/cocotb/result.py @@ -28,7 +28,8 @@ # TODO: Coule use cStringIO? import traceback import sys -from StringIO import StringIO +#from StringIO import StringIO +from io import StringIO, BytesIO def raise_error(obj, msg): """ @@ -38,8 +39,13 @@ def raise_error(obj, msg): msg is a string """ exc_type, exc_value, exc_traceback = sys.exc_info() - buff = StringIO() - traceback.print_tb(exc_traceback, file=buff) + if sys.version_info.major >= 3: + buff = StringIO() + traceback.print_tb(exc_traceback, file=buff) + else: + buff_bytes = BytesIO() + traceback.print_tb(exc_traceback, file=buff_bytes) + buff = StringIO(buff_bytes.getvalue().decode("UTF-8")) obj.log.error("%s\n%s" % (msg, buff.getvalue())) exception = TestError(msg) exception.stderr.write(buff.getvalue()) @@ -76,4 +82,4 @@ class TestFailure(TestComplete): pass class TestSuccess(TestComplete): pass -class SimFailure(TestComplete): pass \ No newline at end of file +class SimFailure(TestComplete): pass diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index 15c780eb..b4151aed 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -62,7 +62,7 @@ def __init__(self, dut, reorder_depth=0, fail_immediately=True): def result(self): """Determine the test result - do we have any pending data remaining?""" fail = False - for monitor, expected_output in self.expected.iteritems(): + for monitor, expected_output in self.expected.items(): if callable(expected_output): self.log.debug("Can't check all data returned for %s since expected output is \ callable function rather than a list" % str(monitor)) diff --git a/cocotb/utils.py b/cocotb/utils.py index ce9cb9bd..89978580 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -1,3 +1,5 @@ +from __future__ import print_function + ''' Copyright (c) 2013 Potential Ventures Ltd Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. @@ -29,6 +31,15 @@ import ctypes +# python2 to python3 helper functions +def get_python_integer_types(): + try: + isinstance(1, long) + except NameError: + return (int,) # python 3 + else: + return (int, long) # python 2 + # Ctypes helper functions def pack(ctypes_obj): @@ -259,7 +270,7 @@ def highlight(string, colour=ANSI.YELLOW_FG): b = b[:offset] + chr(random.randint(0,255)) + b[offset+1:] diff = hexdiffs(a,b) - print diff + print(diff) space = '\n' + (" " * 20) - print space.join(diff.split('\n')) + print(space.join(diff.split('\n'))) diff --git a/cocotb/wavedrom.py b/cocotb/wavedrom.py index 246869e7..32d11a64 100644 --- a/cocotb/wavedrom.py +++ b/cocotb/wavedrom.py @@ -57,7 +57,7 @@ def _lastval(samples): if samples[x] not in "=.|": return samples[x] return None - for name, hdl in self._hdls.iteritems(): + for name, hdl in self._hdls.items(): val = hdl.value valstr = val.binstr.lower() @@ -87,7 +87,7 @@ def clear(self): self._data = defaultdict(list) def gap(self): - for name, hdl in self._hdls.iteritems(): + for name, hdl in self._hdls.items(): self._samples[name].append("|") def get(self, add_clock=True): diff --git a/cocotb/xunit_reporter.py b/cocotb/xunit_reporter.py index e032002d..e46f7cb7 100644 --- a/cocotb/xunit_reporter.py +++ b/cocotb/xunit_reporter.py @@ -28,10 +28,12 @@ from xml.etree.ElementTree import Element, SubElement import xml.etree.ElementTree as ET +from io import StringIO + TRUNCATE_LINES = 100 # file from http://stackoverflow.com/questions/136168/get-last-n-lines-of-a-file-with-python-similar-to-tail -class File(file): +class File(StringIO): def countlines(self): buf = mmap.mmap(self.fileno(), 0) diff --git a/examples/Makefile b/examples/Makefile index 92f9062d..6f777c8b 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -33,7 +33,8 @@ export COCOTB SMOKE_TESTS := plusargs \ sim_exit/tests \ functionality/tests \ - endian_swapper/tests + adder/tests \ + endian_swapper/tests \ .PHONY: $(SMOKE_TESTS) diff --git a/examples/adder/hdl/adder.v b/examples/adder/hdl/adder.v new file mode 100644 index 00000000..deb8eff1 --- /dev/null +++ b/examples/adder/hdl/adder.v @@ -0,0 +1,15 @@ +// Adder DUT +module adder (input [3:0] A, + input [3:0] B, + output reg [4:0] X); + always @(A or B) begin + X = A + B; + end + + // Dump waves + initial begin + $dumpfile("dump.vcd"); + $dumpvars(1, adder); + end + +endmodule diff --git a/examples/adder/tests/Makefile b/examples/adder/tests/Makefile new file mode 100644 index 00000000..20d68c3e --- /dev/null +++ b/examples/adder/tests/Makefile @@ -0,0 +1,51 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +TOPLEVEL := adder +TOPLEVEL_LANG ?= verilog + +PWD=$(shell pwd) +COCOTB=$(PWD)/../../.. + +ifeq ($(OS),MINGW32_NT-6.1) +WPWD=$(shell sh -c 'pwd -W') +else +WPWD=$(shell pwd) +endif + +VERILOG_SOURCES = $(WPWD)/../hdl/adder.v +GPI_IMPL := vpi + +export TOPLEVEL_LANG + +MODULE ?= test_adder + +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim diff --git a/examples/adder/tests/test_adder.py b/examples/adder/tests/test_adder.py new file mode 100644 index 00000000..68aa602b --- /dev/null +++ b/examples/adder/tests/test_adder.py @@ -0,0 +1,43 @@ +# Simple tests for an adder module +import cocotb +from cocotb.triggers import Timer +from cocotb.result import TestFailure +import random + +@cocotb.test() +def adder_basic_test(dut): + """Test for 5 + 10""" + yield Timer(2) + + dut.A = 5 + dut.B = 10 + + yield Timer(2) + + if int(dut.X) != 15: + raise TestFailure( + "Adder result is incorrect: %s != 15" % str(dut.X)) + else: # these last two lines are not strictly necessary + dut.log.info("Ok!") + +@cocotb.test() +def adder_randomised_test(dut): + """Test for adding 2 random numbers multiple times""" + yield Timer(2) + + for i in range(10): + A = random.randint(0,15) + B = random.randint(0,15) + + dut.A = A + dut.B = B + + yield Timer(2) + + if int(dut.X) != (A + B): + raise TestFailure( + "Randomised test failed with: %s + %s = %s" % + (dut.A, dut.B, dut.X)) + else: # these last two lines are not strictly necessary + dut.log.info("Ok!") + diff --git a/examples/endian_swapper/cosim/io_module.h b/examples/endian_swapper/cosim/io_module.h index f6ffc69d..e1e0436f 100644 --- a/examples/endian_swapper/cosim/io_module.h +++ b/examples/endian_swapper/cosim/io_module.h @@ -30,6 +30,11 @@ #include +#if PY_MAJOR_VERSION >= 3 +#define PyInt_FromLong PyLong_FromLong +#define PyInt_AsLong PyLong_AsLong +#endif + static PyObject *set_write_function(PyObject *self, PyObject *args); static PyObject *set_read_function(PyObject *self, PyObject *args); diff --git a/examples/endian_swapper/tests/test_endian_swapper.py b/examples/endian_swapper/tests/test_endian_swapper.py index 515e4a46..3f262cf6 100644 --- a/examples/endian_swapper/tests/test_endian_swapper.py +++ b/examples/endian_swapper/tests/test_endian_swapper.py @@ -135,7 +135,7 @@ def run_test(dut, data_in=None, config_coroutine=None, idle_inserter=None, backp yield tb.stream_in.send(transaction) # Wait at least 2 cycles where output ready is low before ending the test - for i in xrange(2): + for i in range(2): yield RisingEdge(dut.clk) while not dut.stream_out_ready.value: yield RisingEdge(dut.clk) @@ -153,7 +153,7 @@ def run_test(dut, data_in=None, config_coroutine=None, idle_inserter=None, backp def random_packet_sizes(min_size=1, max_size=150, npackets=10): """random string data of a random length""" - for i in xrange(npackets): + for i in range(npackets): yield get_bytes(random.randint(min_size, max_size), random_data()) @cocotb.coroutine diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 53f998ee..2468424b 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -288,7 +288,7 @@ def test_fork_and_monitor(dut, period=1000, clocks=6): @cocotb.coroutine def count_edges_cycles(signal, edges): edge = RisingEdge(signal) - for i in xrange(edges): + for i in range(edges): yield edge signal.log.info("Rising edge %d detected" % i) signal.log.info("Finished, returning %d" % edges) diff --git a/examples/ping_tun_tap/tests/test_icmp_reply.py b/examples/ping_tun_tap/tests/test_icmp_reply.py index 307318ae..8a0db866 100644 --- a/examples/ping_tun_tap/tests/test_icmp_reply.py +++ b/examples/ping_tun_tap/tests/test_icmp_reply.py @@ -95,7 +95,7 @@ def tun_tap_example_test(dut): subprocess.check_call('ping -c 5 192.168.255.2 &', shell=True) # Respond to 5 pings, then quit - for i in xrange(5): + for i in range(5): cocotb.log.info("Waiting for packets on tun interface") packet = os.read(fd, 2048) diff --git a/examples/plusargs/plusargs.py b/examples/plusargs/plusargs.py index d80e7f53..a05d332e 100644 --- a/examples/plusargs/plusargs.py +++ b/examples/plusargs/plusargs.py @@ -1,3 +1,4 @@ +from __future__ import print_function ''' Copyright (c) 2013 Potential Ventures Ltd Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. @@ -42,7 +43,7 @@ def plusargs_test(dut): yield Timer(10000) for name in cocotb.plusargs: - print "COCOTB:", name, cocotb.plusargs[name] + print("COCOTB:", name, cocotb.plusargs[name]) yield Timer(10000) diff --git a/lib/compat/python3_compat.h b/lib/compat/python3_compat.h new file mode 100644 index 00000000..6f6195e1 --- /dev/null +++ b/lib/compat/python3_compat.h @@ -0,0 +1,22 @@ +#ifndef _PYTHON3_COMPAT_H +#define _PYTHON3_COMPAT_H + +struct module_state { + PyObject *error; +}; + +#if PY_MAJOR_VERSION >= 3 +#define PyInt_FromLong PyLong_FromLong +#define PyString_FromString PyUnicode_FromString + +#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) +#define MODULE_ENTRY_POINT PyInit_simulator +#define INITERROR return NULL +#else + +#define GETSTATE(m) (&_state) +#define MODULE_ENTRY_POINT initsimulator +#define INITERROR return +#endif + +#endif diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index ac4165f7..c35febae 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -32,11 +32,18 @@ #include #include #include "embed.h" +#include "../compat/python3_compat.h" static PyThreadState *gtstate = NULL; +#if PY_MAJOR_VERSION >= 3 +static wchar_t progname[] = L"cocotb"; +static wchar_t *argv[] = { progname }; +#else static char progname[] = "cocotb"; static char *argv[] = { progname }; +#endif + static PyObject *pEventFn = NULL; @@ -101,7 +108,7 @@ void embed_init_python(void) int get_module_ref(const char *modname, PyObject **mod) { - PyObject *pModule = PyImport_Import(PyString_FromString(modname)); + PyObject *pModule = PyImport_ImportModule(modname); if (pModule == NULL) { PyErr_Print(); diff --git a/lib/gpi_log/gpi_logging.c b/lib/gpi_log/gpi_logging.c index ed89b6b8..e063cad3 100644 --- a/lib/gpi_log/gpi_logging.c +++ b/lib/gpi_log/gpi_logging.c @@ -28,6 +28,7 @@ ******************************************************************************/ #include +#include "../compat/python3_compat.h" // Used to log using the standard python mechanism static PyObject *pLogHandler; @@ -132,7 +133,7 @@ void gpi_log(const char *name, long level, const char *pathname, const char *fun PyGILState_STATE gstate = PyGILState_Ensure(); PyObject *check_args = PyTuple_New(1); - PyTuple_SetItem(check_args, 0, PyInt_FromLong(level)); + PyTuple_SetItem(check_args, 0, PyLong_FromLong(level)); PyObject *retuple = PyObject_CallObject(pLogFilter, check_args); if (retuple != Py_True) { @@ -149,11 +150,11 @@ void gpi_log(const char *name, long level, const char *pathname, const char *fun va_end(ap); PyObject *call_args = PyTuple_New(5); - PyTuple_SetItem(call_args, 0, PyInt_FromLong(level)); // Note: This function steals a reference. - PyTuple_SetItem(call_args, 1, PyString_FromString(pathname)); // Note: This function steals a reference. - PyTuple_SetItem(call_args, 2, PyInt_FromLong(lineno)); // Note: This function steals a reference. - PyTuple_SetItem(call_args, 3, PyString_FromString(log_buff)); // Note: This function steals a reference. - PyTuple_SetItem(call_args, 4, PyString_FromString(funcname)); + PyTuple_SetItem(call_args, 0, PyLong_FromLong(level)); // Note: This function steals a reference. + PyTuple_SetItem(call_args, 1, PyUnicode_FromString(pathname)); // Note: This function steals a reference. + PyTuple_SetItem(call_args, 2, PyLong_FromLong(lineno)); // Note: This function steals a reference. + PyTuple_SetItem(call_args, 3, PyUnicode_FromString(log_buff)); // Note: This function steals a reference. + PyTuple_SetItem(call_args, 4, PyUnicode_FromString(funcname)); retuple = PyObject_CallObject(pLogHandler, call_args); diff --git a/lib/simulator/Makefile b/lib/simulator/Makefile index b5bb000a..42176091 100644 --- a/lib/simulator/Makefile +++ b/lib/simulator/Makefile @@ -35,7 +35,7 @@ LIBS := -lgpi -lgpilog $(PYLIBS) LD_PATH := -L$(LIB_DIR) LIB_NAME := libsim -SRCS := simulatormodule.c +SRCS := simulatormodule.c CLIBS += $(LIB_DIR)/$(LIB_NAME) diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 6daccd5b..b0489749 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -767,20 +767,8 @@ static PyObject *deregister_callback(PyObject *self, PyObject *args) return value; } -PyMODINIT_FUNC -initsimulator(void) -{ - PyObject* simulator; - simulator = Py_InitModule("simulator", SimulatorMethods); - - // Make the GPI constants accessible from the C world - int rc = 0; - rc |= PyModule_AddIntConstant(simulator, "MEMORY", gpiMemory); - rc |= PyModule_AddIntConstant(simulator, "MODULE", gpiModule); - rc |= PyModule_AddIntConstant(simulator, "PARAMETER", gpiParameter); - rc |= PyModule_AddIntConstant(simulator, "REG", gpiReg); - rc |= PyModule_AddIntConstant(simulator, "NET", gpiNet); - rc |= PyModule_AddIntConstant(simulator, "NETARRAY", gpiNetArray); - if (rc != 0) - fprintf(stderr, "Failed to add module constants!\n"); -} +#if PY_MAJOR_VERSION >= 3 +#include "simulatormodule_python3.c" +#else +#include "simulatormodule_python2.c" +#endif diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 8a937cee..660598f1 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -31,6 +31,7 @@ #define _SIMULATOR_MODULE_H #include +#include "../compat/python3_compat.h" #include "gpi_logging.h" #include "gpi.h" @@ -39,6 +40,7 @@ #define COCOTB_ACTIVE_ID 0xC0C07B // User data flag to indicate callback is active #define COCOTB_INACTIVE_ID 0xDEADB175 // User data flag set when callback has been deregistered +#define MODULE_NAME "simulator" // callback user data typedef struct t_callback_data { @@ -50,6 +52,7 @@ typedef struct t_callback_data { gpi_sim_hdl cb_hdl; } s_callback_data, *p_callback_data; +static PyObject *error_out(PyObject *m); static PyObject *log_msg(PyObject *self, PyObject *args); // Raise an exception on failure @@ -97,6 +100,9 @@ static PyMethodDef SimulatorMethods[] = { // FIXME METH_NOARGS => initialization from incompatible pointer type {"get_sim_time", get_sim_time, METH_VARARGS, "Get the current simulation time as a float"}, {"deregister_callback", deregister_callback, METH_VARARGS, "Deregister a callback"}, + + {"error_out", (PyCFunction)error_out, METH_NOARGS, NULL}, + {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/lib/simulator/simulatormodule_python2.c b/lib/simulator/simulatormodule_python2.c new file mode 100644 index 00000000..aeed2271 --- /dev/null +++ b/lib/simulator/simulatormodule_python2.c @@ -0,0 +1,39 @@ +#include "simulatormodule.h" + +char error_module[] = MODULE_NAME ".Error"; +static struct module_state _state; + +static PyObject *error_out(PyObject *m) +{ + struct module_state *st = GETSTATE(m); + PyErr_SetString(st->error, "something bad happened"); + return NULL; +} + +PyMODINIT_FUNC +MODULE_ENTRY_POINT(void) +{ + PyObject* simulator; + + simulator = Py_InitModule(MODULE_NAME, SimulatorMethods); + + if (simulator == NULL) INITERROR; + struct module_state *st = GETSTATE(simulator); + + st->error = PyErr_NewException((char *) &error_module, NULL, NULL); + if (st->error == NULL) { + Py_DECREF(simulator); + INITERROR; + } + + // Make the GPI constants accessible from the C world + int rc = 0; + rc |= PyModule_AddIntConstant(simulator, "MEMORY", gpiMemory); + rc |= PyModule_AddIntConstant(simulator, "MODULE", gpiModule); + rc |= PyModule_AddIntConstant(simulator, "PARAMETER", gpiParameter); + rc |= PyModule_AddIntConstant(simulator, "REG", gpiReg); + rc |= PyModule_AddIntConstant(simulator, "NET", gpiNet); + rc |= PyModule_AddIntConstant(simulator, "NETARRAY", gpiNetArray); + if (rc != 0) + fprintf(stderr, "Failed to add module constants!\n"); +} diff --git a/lib/simulator/simulatormodule_python3.c b/lib/simulator/simulatormodule_python3.c new file mode 100644 index 00000000..2fbdac4e --- /dev/null +++ b/lib/simulator/simulatormodule_python3.c @@ -0,0 +1,60 @@ +#include "simulatormodule.h" + +static PyObject *error_out(PyObject *m) +{ + struct module_state *st = GETSTATE(m); + PyErr_SetString(st->error, "something bad happened"); + return NULL; +} + +static int simulator_traverse(PyObject *m, visitproc visit, void *arg) { + Py_VISIT(GETSTATE(m)->error); + return 0; +} + +static int simulator_clear(PyObject *m) { + Py_CLEAR(GETSTATE(m)->error); + return 0; +} + +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + MODULE_NAME, + NULL, + sizeof(struct module_state), + SimulatorMethods, + NULL, + simulator_traverse, + simulator_clear, + NULL +}; + +PyMODINIT_FUNC +MODULE_ENTRY_POINT(void) +{ + PyObject* simulator; + + simulator = PyModule_Create(&moduledef); + + if (simulator == NULL) INITERROR; + struct module_state *st = GETSTATE(simulator); + + st->error = PyErr_NewException(MODULE_NAME ".Error", NULL, NULL); + if (st->error == NULL) { + Py_DECREF(simulator); + INITERROR; + } + + // Make the GPI constants accessible from the C world + int rc = 0; + rc |= PyModule_AddIntConstant(simulator, "MEMORY", gpiMemory); + rc |= PyModule_AddIntConstant(simulator, "MODULE", gpiModule); + rc |= PyModule_AddIntConstant(simulator, "PARAMETER", gpiParameter); + rc |= PyModule_AddIntConstant(simulator, "REG", gpiReg); + rc |= PyModule_AddIntConstant(simulator, "NET", gpiNet); + rc |= PyModule_AddIntConstant(simulator, "NETARRAY", gpiNetArray); + if (rc != 0) + fprintf(stderr, "Failed to add module constants!\n"); + + return simulator; +} diff --git a/makefiles/Makefile.pylib.Linux b/makefiles/Makefile.pylib.Linux index 33566caf..23bb76bd 100644 --- a/makefiles/Makefile.pylib.Linux +++ b/makefiles/Makefile.pylib.Linux @@ -35,7 +35,10 @@ NATIVE_ARCH=$(shell uname -m) ARCH?=$(NATIVE_ARCH) # We might work with other Python versions -PYTHON_VERSION?=$(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') +PYTHON_MAJOR_VERSION?=$(shell python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") +PYTHON_MINOR_VERSION?=$(shell python -c "from __future__ import print_function; import sys; print(sys.version_info.minor)") +PYTHON_VERSION?=$(PYTHON_MAJOR_VERSION).$(PYTHON_MINOR_VERSION) + PYTHON_LIBDIR:=$(shell python -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("LIBDIR"))') PYTHON_DYNLIBDIR:=$(shell python -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("DESTSHARED"))') From 27afe492bfbf1573c79d54c2a25524c1407fea36 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Mon, 12 Jan 2015 21:50:44 +0100 Subject: [PATCH 0740/2656] update travis.yml to include build for python 3.4 --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7abe7907..cd268c98 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ language: python python: - "2.7" + - "3.4" before_install: - sudo add-apt-repository -y ppa:team-electronics/ppa - sudo apt-get update -qq @@ -10,7 +11,7 @@ install: - pip install xunitparser # - export COVERAGE=0 - - export PYTHONPATH=$PYTHONPATH:/home/travis/virtualenv/python2.7.9/lib/python2.7/site-packages + - export PYTHONPATH=$PYTHONPATH:$(python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())") # Build iverilog from source # - sudo apt-get install gperf From 5125037b16a34b56788c5e4a2c71a77d89734376 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Mon, 12 Jan 2015 21:56:28 +0100 Subject: [PATCH 0741/2656] report_results.py python3 fix --- bin/report_results.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/report_results.py b/bin/report_results.py index 60f05c0d..206aadda 100755 --- a/bin/report_results.py +++ b/bin/report_results.py @@ -21,7 +21,7 @@ def report_results(xml_filename): for tc in ts: report[tc.result] += 1 - print 'Test Report:', report + print('Test Report:', report) if report['failed'] or report['errored']: return 1 @@ -29,7 +29,7 @@ def report_results(xml_filename): if __name__ == "__main__": if len(sys.argv) is not 2: - print "Please specify a result file" + print("Please specify a result file") exit(1) exit(report_results(sys.argv[1])) From ca84bf9086b30fd19f6c1f7713df845adff9e00b Mon Sep 17 00:00:00 2001 From: Jeremy Herbert Date: Tue, 13 Jan 2015 01:47:53 -0800 Subject: [PATCH 0742/2656] travis-ci fixes for python3 --- .travis.yml | 7 ++++--- bin/report_results.py | 9 ++++----- cocotb/decorators.py | 11 +++++++---- examples/adder/tests/test_adder.py | 2 +- examples/endian_swapper/cosim/Makefile | 2 +- makefiles/Makefile.inc | 2 +- makefiles/Makefile.pylib.Linux | 6 +++--- 7 files changed, 21 insertions(+), 18 deletions(-) diff --git a/.travis.yml b/.travis.yml index cd268c98..5da2384f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,10 +9,11 @@ install: - sudo apt-get install -qq iverilog-daily - pip install coverage - pip install xunitparser - + - if [ $( python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") -eq 3 ]; then ln -s /opt/python/3.4.2/bin/python3.4-config /home/travis/virtualenv/python3.4/bin/python-config; fi + - python-config --ldflags # - export COVERAGE=0 - - export PYTHONPATH=$PYTHONPATH:$(python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())") - + - export PYTHONPATH=$PYTHONPATH:$(python -c "from __future__ import print_function; from distutils.sysconfig import get_python_lib; print(get_python_lib())") + - export LD_LIBRARY_PATH=/opt/python/3.4.2/lib:$LD_LIBRARY_PATH # Build iverilog from source # - sudo apt-get install gperf # - wget -O iverilog-master.zip https://github.com/steveicarus/iverilog/archive/master.zip diff --git a/bin/report_results.py b/bin/report_results.py index 206aadda..ff2de2b3 100755 --- a/bin/report_results.py +++ b/bin/report_results.py @@ -4,6 +4,7 @@ """ +from __future__ import print_function import sys def report_results(xml_filename): @@ -17,12 +18,10 @@ def report_results(xml_filename): if xunitparser is not None: ts, tr = xunitparser.parse(open(xml_filename)) - report = {'success': 0, 'skipped': 0, 'failed': 0, 'errored': 0} - for tc in ts: - report[tc.result] += 1 - + report = {'skipped': len(tr.skipped), 'failed': len(tr.failures), 'errored': len(tr.errors), 'all': tr.testsRun} + print('Test Report:', report) - if report['failed'] or report['errored']: + if report['failed'] or report['errored'] or not report['all']: return 1 return 0 diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 5f5b6def..63f3014d 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -24,7 +24,7 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - +from __future__ import print_function import sys import time import logging @@ -157,7 +157,7 @@ class RunningTest(RunningCoroutine): class ErrorLogHandler(logging.Handler): def __init__(self, fn): self.fn = fn - logging.Handler.__init__(self, level=logging.ERROR) + logging.Handler.__init__(self, level=logging.DEBUG) def handle(self, record): self.fn(self.format(record)) @@ -181,7 +181,6 @@ def send(self, value): self.log.info("Starting test: \"%s\"\nDescription: %s" % (self.funcname, self.__doc__)) self.start_time = time.time() self.started = True - try: self.log.debug("Sending trigger %s" % (str(value))) return self._coro.send(value) @@ -190,7 +189,11 @@ def send(self, value): self.log.warning(str(e)) else: self.log.info(str(e)) - e.stderr.write("\n".join(self.error_messages)) + + buff = StringIO(); + for message in self.error_messages: + print(message, file=buff) + e.stderr.write(buff.getvalue()) raise except StopIteration: raise TestSuccess() diff --git a/examples/adder/tests/test_adder.py b/examples/adder/tests/test_adder.py index 68aa602b..72d9b489 100644 --- a/examples/adder/tests/test_adder.py +++ b/examples/adder/tests/test_adder.py @@ -37,7 +37,7 @@ def adder_randomised_test(dut): if int(dut.X) != (A + B): raise TestFailure( "Randomised test failed with: %s + %s = %s" % - (dut.A, dut.B, dut.X)) + (int(dut.A), int(dut.B), int(dut.X))) else: # these last two lines are not strictly necessary dut.log.info("Ok!") diff --git a/examples/endian_swapper/cosim/Makefile b/examples/endian_swapper/cosim/Makefile index f6c3fd99..1f328b93 100644 --- a/examples/endian_swapper/cosim/Makefile +++ b/examples/endian_swapper/cosim/Makefile @@ -16,7 +16,7 @@ io_module.o: io.c io_module.h io.h -fwrapv -DNDEBUG -I$(PYTHON_INCLUDEDIR) -c $< -o $@ io_module.so: io_module.o - $(CC) -pthread -shared -Wl,-z,relro $< -L$(PYTHON_LIBDIR) -lpython$(PYTHON_VERSION) -o $@ + $(CC) -pthread -shared -Wl,-z,relro $< -L$(PYTHON_LIBDIR) $(PYTHON_LD_FLAGS) -o $@ _hal.so: ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so $(CC) -g -ldl -shared -fPIC -I$(shell pwd) -I$(PYTHON_INCLUDEDIR) -I../hal ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so -o $@ diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index d4f52226..3c61bb3e 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -85,7 +85,7 @@ else ifeq ($(OS),Linux) SO_LINKER_ARGS := -shared -Xlinker -L$(PYTHON_LIBDIR) else ifeq ($(OS),MINGW32_NT-6.1) SO_LINKER_ARGS := -shared -Wl,-no-undefined -Wl,-enable-runtime-pseudo-reloc-v2 \ - -Wl,--enable-auto-import -L$(PYTHON_LIBDIR) + -Wl,--enable-auto-import $(PYTHON_LD_FLAGS) else $(error "Unsupported os" $(OS)) endif diff --git a/makefiles/Makefile.pylib.Linux b/makefiles/Makefile.pylib.Linux index 23bb76bd..28339019 100644 --- a/makefiles/Makefile.pylib.Linux +++ b/makefiles/Makefile.pylib.Linux @@ -1,4 +1,4 @@ -############################################################################### +############################################################################## # Copyright (c) 2013 Potential Ventures Ltd # Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. @@ -37,7 +37,7 @@ ARCH?=$(NATIVE_ARCH) # We might work with other Python versions PYTHON_MAJOR_VERSION?=$(shell python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") PYTHON_MINOR_VERSION?=$(shell python -c "from __future__ import print_function; import sys; print(sys.version_info.minor)") -PYTHON_VERSION?=$(PYTHON_MAJOR_VERSION).$(PYTHON_MINOR_VERSION) +PYTHON_VERSION?=$(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') PYTHON_LIBDIR:=$(shell python -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("LIBDIR"))') PYTHON_DYNLIBDIR:=$(shell python -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("DESTSHARED"))') @@ -55,4 +55,4 @@ endif PYLIBS = $(shell python-config --libs) PYTHON_INCLUDEDIR := $(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())') -PYTHON_DYN_LIB = libpython$(PYTHON_VERSION).so +#PYTHON_DYN_LIB = libpython$(PYTHON_VERSION).so From 29b26ae0334e6a9d9c46e7bdf69a5f02aae500c0 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 15 Jan 2015 09:41:59 +0000 Subject: [PATCH 0743/2656] Issue 177: Set m_type to default value --- lib/gpi/gpi_priv.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 5581c135..f58ae35a 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -92,7 +92,9 @@ class GpiHdl { // that construct an object derived from GpiSignalObjHdl or GpiObjHdl class GpiObjHdl : public GpiHdl { public: - GpiObjHdl(std::string name) : GpiHdl(NULL, NULL), m_name(name) { } + GpiObjHdl(std::string name) : GpiHdl(NULL, NULL), + m_name(name), + m_type("unknown") { } GpiObjHdl(GpiImplInterface *impl) : GpiHdl(impl, NULL) { } GpiObjHdl(GpiImplInterface *impl, void *hdl) : GpiHdl(impl, hdl) { } virtual ~GpiObjHdl() { } From 24835b83eba7101fa00b6dbfd44a3a0744e875a9 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 15 Jan 2015 09:55:11 +0000 Subject: [PATCH 0744/2656] Issue #186: Revert commenting out of PYTHON_DYN_LIB, need this to load python dynamically at run time. --- makefiles/Makefile.pylib.Linux | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/Makefile.pylib.Linux b/makefiles/Makefile.pylib.Linux index 28339019..a4e7328e 100644 --- a/makefiles/Makefile.pylib.Linux +++ b/makefiles/Makefile.pylib.Linux @@ -55,4 +55,4 @@ endif PYLIBS = $(shell python-config --libs) PYTHON_INCLUDEDIR := $(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())') -#PYTHON_DYN_LIB = libpython$(PYTHON_VERSION).so +PYTHON_DYN_LIB = libpython$(PYTHON_VERSION).so From ce77d2490c82f8bde5d7382c019c82c37d6d5236 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 15 Jan 2015 10:02:42 +0000 Subject: [PATCH 0745/2656] Issue #188: Defer cleanup of deregistered Timer until it has fired --- lib/vpi/VpiCbHdl.cpp | 20 ++++++++++++++++++++ lib/vpi/VpiImpl.cpp | 4 ++++ lib/vpi/VpiImpl.h | 6 +----- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 1386d216..9b584033 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -277,6 +277,26 @@ VpiTimedCbHdl::VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : GpiCbHd cb_data.reason = cbAfterDelay; } +int VpiTimedCbHdl::cleanup_callback(void) +{ + switch (m_state) { + case GPI_PRIMED: + /* Issue #188: Work around for modelsim that is harmless to othes too, + we tag the time as delete, let it fire then do not pass up + */ + LOG_DEBUG("Not removing PRIMED timer %d\n",vpi_time.low); + m_state = GPI_DELETE; + return 0; + case GPI_DELETE: + LOG_DEBUG("Removing DELETE timer %d\n",vpi_time.low); + default: + break; + } + VpiCbHdl::cleanup_callback(); + /* Return one so we delete this object */ + return 1; +} + VpiReadwriteCbHdl::VpiReadwriteCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), VpiCbHdl(impl) { diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index ca26ec98..c004024d 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -283,6 +283,10 @@ int32_t handle_vpi_callback(p_cb_data cb_data) if (cb_hdl->cleanup_callback()) delete cb_hdl; + } else { + /* Issue #188: This is a work around for a modelsim */ + if (cb_hdl->cleanup_callback()) + delete cb_hdl; } return rv; diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 9c41a4b3..59d89308 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -106,11 +106,7 @@ class VpiTimedCbHdl : public VpiCbHdl { public: VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps); virtual ~VpiTimedCbHdl() { } - int cleanup_callback() { - VpiCbHdl::cleanup_callback(); - /* Return one so we delete this object */ - return 1; - } + int cleanup_callback(); }; class VpiReadOnlyCbHdl : public VpiCbHdl { From 17f2b6f1edfa2e24833504ed02e04b81e00d9081 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 15 Jan 2015 11:34:11 +0000 Subject: [PATCH 0746/2656] Issue #186: Revert change to windows build, could not find python libs --- makefiles/Makefile.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 3c61bb3e..d4f52226 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -85,7 +85,7 @@ else ifeq ($(OS),Linux) SO_LINKER_ARGS := -shared -Xlinker -L$(PYTHON_LIBDIR) else ifeq ($(OS),MINGW32_NT-6.1) SO_LINKER_ARGS := -shared -Wl,-no-undefined -Wl,-enable-runtime-pseudo-reloc-v2 \ - -Wl,--enable-auto-import $(PYTHON_LD_FLAGS) + -Wl,--enable-auto-import -L$(PYTHON_LIBDIR) else $(error "Unsupported os" $(OS)) endif From ef76904079070a7cdffec0bd0e071198fa3dea2b Mon Sep 17 00:00:00 2001 From: Sigve Tjora Date: Fri, 16 Jan 2015 00:07:34 +0100 Subject: [PATCH 0747/2656] Updated Makefile.ius with bugfixes Adding missing requirement for libvpi.so and libvhpi.so, causing them not to be built when using Cadence Incisive. Removed explicit call to vlog_startup_routines_bootstrap and vhpi_startup_routines_bootstrap. This fix was tested on ius v9.2, 11.1 and 14.1 for the vlog case and 14.1 for vhdl. --- makefiles/simulators/Makefile.ius | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 7e5c1196..9a205cd4 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -39,24 +39,26 @@ else endif ifeq ($(GPI_IMPL),vpi) - GPI_ARGS = -loadvpi $(LIB_DIR)/libvpi:vlog_startup_routines_bootstrap + GPI_ARGS = -loadvpi $(LIB_DIR)/libvpi EXTRA_ARGS += -sv HDL_SOURCES = $(VERILOG_SOURCES) + GPI_LIB = $(COCOTB_VPI_LIB) endif ifeq ($(GPI_IMPL),vhpi) - GPI_ARGS = -loadvhpi $(LIB_DIR)/libvhpi:vhpi_startup_routines_bootstrap + GPI_ARGS = -loadvhpi $(LIB_DIR)/libvhpi #EXTRA_ARGS += -cdslib cds.lib EXTRA_ARGS += -v93 EXTRA_ARGS += -top worklib.$(TOPLEVEL) HDL_SOURCES = $(VHDL_SOURCES) + GPI_LIB = $(COCOTB_VHPI_LIB) endif ifndef GPI_ARGS $(error "Unable to determine appropriate GPI layer to use") endif -results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_LIB_VPI) +results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(GPI_LIB) LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ irun $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(HDL_SOURCES) $(PLUSARGS) From 5b978b988f7f3d7cecff4d981c99609dd86d5291 Mon Sep 17 00:00:00 2001 From: Sigve Tjora Date: Fri, 16 Jan 2015 00:33:38 +0100 Subject: [PATCH 0748/2656] Added rudimentary support for EnumNets in SystemVerilog It is now possible to read and write enum-nets/ports from Python as integers, but not by using enum-names. This could be extended by adding introspection to get a int to enum name dictonary. --- include/vpi_user.h | 1 + lib/vpi/VpiImpl.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/include/vpi_user.h b/include/vpi_user.h index 18e0611f..b782dd80 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -64,6 +64,7 @@ typedef uint32_t *vpiHandle; #define vpiStructVar 618 #define vpiInterface 601 #define vpiModport 606 +#define vpiEnumNet 680 /* SystemVerilog */ #define vpiStop 66 /* execute simulator's $stop */ #define vpiFinish 67 /* execute simulator's $finish */ diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index c004024d..268cfc8f 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -93,6 +93,7 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) case vpiNet: case vpiReg: case vpiParameter: + case vpiEnumNet: new_obj = new VpiSignalObjHdl(this, new_hdl); break; case vpiStructVar: @@ -133,6 +134,7 @@ GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) switch (type) { case vpiNet: case vpiNetBit: + case vpiEnumNet: new_obj = new VpiSignalObjHdl(this, new_hdl); break; case vpiModule: From 9eac7fd0e5fe793fd2eff9b242ad19feed25effe Mon Sep 17 00:00:00 2001 From: Sigve Tjora Date: Fri, 16 Jan 2015 00:37:30 +0100 Subject: [PATCH 0749/2656] Added missing newline at end of two files Removes compiler warning. --- examples/endian_swapper/hal/endian_swapper_hal.h | 2 +- examples/endian_swapper/hal/endian_swapper_regs.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/endian_swapper/hal/endian_swapper_hal.h b/examples/endian_swapper/hal/endian_swapper_hal.h index 850f2c68..9b9fc03a 100644 --- a/examples/endian_swapper/hal/endian_swapper_hal.h +++ b/examples/endian_swapper/hal/endian_swapper_hal.h @@ -67,4 +67,4 @@ int endian_swapper_get_count(endian_swapper_state_t *state); -#endif /* __ENDIAN_SWAPPER_HAL_H__ */ \ No newline at end of file +#endif /* __ENDIAN_SWAPPER_HAL_H__ */ diff --git a/examples/endian_swapper/hal/endian_swapper_regs.h b/examples/endian_swapper/hal/endian_swapper_regs.h index f5f5dbf4..512d219e 100644 --- a/examples/endian_swapper/hal/endian_swapper_regs.h +++ b/examples/endian_swapper/hal/endian_swapper_regs.h @@ -54,4 +54,4 @@ #define ENDIAN_SWAPPER_PACKET_COUNT_OFFSET (0) -#endif // __ENDIAN_SWAPPER_REGS_H__ \ No newline at end of file +#endif // __ENDIAN_SWAPPER_REGS_H__ From c72c814dfd051acc608b1960d0745bb92a311e3a Mon Sep 17 00:00:00 2001 From: chiggs Date: Sat, 17 Jan 2015 19:00:37 +0000 Subject: [PATCH 0750/2656] Fixes #194: Use func.__name__ instead of str(func) * str(func) can block on the simulator before thread is started --- cocotb/decorators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 63f3014d..151a3989 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -311,7 +311,7 @@ def execute_external(func, _event): unblock_external(_event) thread = threading.Thread(group=None, target=execute_external, - name=str(func) + "thread", args=([func, bridge]), kwargs={}) + name=func.__name__ + "thread", args=([func, bridge]), kwargs={}) thread.start() yield bridge.out_event.wait() From a253e7a2b7834c68bc66b2144c9c3715e9da256f Mon Sep 17 00:00:00 2001 From: "J.Morarity" Date: Sat, 17 Jan 2015 16:39:45 -0800 Subject: [PATCH 0751/2656] branch to verify cocotb builds and runs on windows with modelsim (paid version) From ce588e24db045cc25af594633b8b74769d910514 Mon Sep 17 00:00:00 2001 From: "J.Morarity" Date: Sat, 17 Jan 2015 19:56:01 -0800 Subject: [PATCH 0752/2656] Added auto-detection of python and modelsim directories. Cocotb can now be built in mingw/msys environment by simply executing 'make SIM=modelsim'. This also solves problem of multiple versions of modelsim having different binary folders (win32aloem, win32pe, etc). Note that PYTHON_DIR can still optionally be passed to make on command line (as was previously required). --- lib/fli/Makefile | 2 +- makefiles/Makefile.pylib.MINGW32_NT-6.1 | 13 +++++++++++-- makefiles/simulators/Makefile.modelsim | 11 +++++++---- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/lib/fli/Makefile b/lib/fli/Makefile index 37b4f37c..0c23fe97 100644 --- a/lib/fli/Makefile +++ b/lib/fli/Makefile @@ -28,7 +28,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc -INCLUDES += -I/local/tools/modelsim/10.3c/modelsim_dlx/include +INCLUDES += -I$(MODELSIM_BIN_DIR)/../include GXX_ARGS += -DFLI_CHECKING LIBS := -lgpilog -lgpi -lstdc++ LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) diff --git a/makefiles/Makefile.pylib.MINGW32_NT-6.1 b/makefiles/Makefile.pylib.MINGW32_NT-6.1 index 19e7583b..69a981c8 100644 --- a/makefiles/Makefile.pylib.MINGW32_NT-6.1 +++ b/makefiles/Makefile.pylib.MINGW32_NT-6.1 @@ -27,9 +27,18 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -# All common pyhon related rules +# All common python related rules + +# if not explicitly set, auto-detect python dir from system path +ifeq ($(PYTHON_DIR),) +PYTHON_DIR = $(shell which python | sed 's|/python.exe||') +endif + +# make sure python dir was set properly +ifeq ($(PYTHON_DIR),) +$(error "Path to Python directory must be included in system path or defined in PYTHON_DIR environment variable") +endif -PYTHON_DIR=/tools/Python27 PYTHON_BIN=$(PYTHON_DIR)/python.exe # We might work with other Python versions diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index d7faf282..1265987f 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -63,15 +63,18 @@ ifneq ($(GUI),1) echo "quit" >> $@ endif - ifeq ($(OS),MINGW32_NT-6.1) -ifeq ($(MODELSIM_PATH),) -$(error "Need to set MODELSIM_PATH") +# auto-detect modelsim dir from system path +MODELSIM_BIN_DIR = $(shell which vsim | sed 's|/vsim.exe||') + +# make sure python dir was set properly +ifeq ($(MODELSIM_BIN_DIR),) +$(error "Directory containing ModelSim binaries must be included in system path") endif EXTRA_LIBS := -lmtipli -EXTRA_LIBDIRS := -L$(MODELSIM_PATH)/win32aloem +EXTRA_LIBDIRS := -L$(MODELSIM_BIN_DIR) OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') LIB_LOAD := PATH=$(OLD_PATH):$(LIB_DIR) From 8112ac0aac862b0796934fb2cb630f4f370086aa Mon Sep 17 00:00:00 2001 From: elgorwi Date: Sun, 18 Jan 2015 15:30:03 +1100 Subject: [PATCH 0753/2656] Support all msys/mingw platforms --- examples/adder/tests/Makefile | 2 +- examples/endian_swapper/tests/Makefile | 2 +- examples/functionality/tests/Makefile | 2 +- examples/mixed_language/tests/Makefile | 2 +- examples/ping_tun_tap/tests/Makefile | 2 +- examples/plusargs/Makefile | 2 +- examples/sim_exit/tests/Makefile | 2 +- makefiles/Makefile.inc | 9 +++++++-- ...Makefile.pylib.MINGW32_NT-6.1 => Makefile.pylib.Msys} | 0 makefiles/simulators/Makefile.aldec | 4 ++-- makefiles/simulators/Makefile.icarus | 2 +- makefiles/simulators/Makefile.modelsim | 2 +- 12 files changed, 18 insertions(+), 13 deletions(-) rename makefiles/{Makefile.pylib.MINGW32_NT-6.1 => Makefile.pylib.Msys} (100%) diff --git a/examples/adder/tests/Makefile b/examples/adder/tests/Makefile index 20d68c3e..fb1db7fd 100644 --- a/examples/adder/tests/Makefile +++ b/examples/adder/tests/Makefile @@ -34,7 +34,7 @@ TOPLEVEL_LANG ?= verilog PWD=$(shell pwd) COCOTB=$(PWD)/../../.. -ifeq ($(OS),MINGW32_NT-6.1) +ifeq ($(OS),Msys) WPWD=$(shell sh -c 'pwd -W') else WPWD=$(shell pwd) diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index 688cedba..d3560aea 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -35,7 +35,7 @@ WPWD=$(shell sh -c 'pwd -W') PWD=$(shell pwd) COCOTB=$(PWD)/../../.. -ifeq ($(OS),MINGW32_NT-6.1) +ifeq ($(OS),Msys) WPWD=$(shell sh -c 'pwd -W') else WPWD=$(shell pwd) diff --git a/examples/functionality/tests/Makefile b/examples/functionality/tests/Makefile index 16c163a0..e874e3ac 100644 --- a/examples/functionality/tests/Makefile +++ b/examples/functionality/tests/Makefile @@ -34,7 +34,7 @@ TOPLEVEL_LANG ?= verilog PWD=$(shell pwd) COCOTB=$(PWD)/../../.. -ifeq ($(OS),MINGW32_NT-6.1) +ifeq ($(OS),Msys) WPWD=$(shell sh -c 'pwd -W') else WPWD=$(shell pwd) diff --git a/examples/mixed_language/tests/Makefile b/examples/mixed_language/tests/Makefile index f8b04ccb..4f07e69f 100644 --- a/examples/mixed_language/tests/Makefile +++ b/examples/mixed_language/tests/Makefile @@ -9,7 +9,7 @@ TOPLEVEL = endian_swapper_mixed PWD=$(shell pwd) COCOTB=$(PWD)/../../.. -ifeq ($(OS),MINGW32_NT-6.1) +ifeq ($(OS),Msys) WPWD=$(shell sh -c 'pwd -W') else WPWD=$(shell pwd) diff --git a/examples/ping_tun_tap/tests/Makefile b/examples/ping_tun_tap/tests/Makefile index 58379864..f963eeb1 100644 --- a/examples/ping_tun_tap/tests/Makefile +++ b/examples/ping_tun_tap/tests/Makefile @@ -29,7 +29,7 @@ TOPLEVEL = icmp_reply -ifeq ($(OS),MINGW32_NT-6.1) +ifeq ($(OS),Msys) WPWD=$(shell sh -c 'pwd -W') else WPWD=$(shell pwd) diff --git a/examples/plusargs/Makefile b/examples/plusargs/Makefile index eb896cdf..c4c69e95 100644 --- a/examples/plusargs/Makefile +++ b/examples/plusargs/Makefile @@ -30,7 +30,7 @@ TOPLEVEL = tb_top -ifeq ($(OS),MINGW32_NT-6.1) +ifeq ($(OS),Msys) PWD=$(shell sh -c 'pwd -W') else PWD=$(shell pwd) diff --git a/examples/sim_exit/tests/Makefile b/examples/sim_exit/tests/Makefile index a20adea2..8272bd85 100644 --- a/examples/sim_exit/tests/Makefile +++ b/examples/sim_exit/tests/Makefile @@ -30,7 +30,7 @@ TOPLEVEL = close_module -ifeq ($(OS),MINGW32_NT-6.1) +ifeq ($(OS),Msys) WPWD=$(shell sh -c 'pwd -W') else WPWD=$(shell pwd) diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index d4f52226..62b7e942 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -44,6 +44,11 @@ ARCH?=$(shell uname -m) export ARCH OS=$(shell uname) +ifneq (, $(findstring MINGW, $(OS))) +OS=Msys +else ifneq (, $(findstring MSYS, $(OS))) +OS=Msys +endif export OS include $(SIM_ROOT)/makefiles/Makefile.paths @@ -65,7 +70,7 @@ endif ifeq ($(OS),Darwin) GXX_ARGS += -g -DDEBUG -fpic GCC_ARGS := $(GXX_ARGS) -else ifeq ($(OS),MINGW32_NT-6.1) +else ifeq ($(OS),Msys) GXX_ARGS += -g -DDEBUG -shared GCC_ARGS := $(GXX_ARGS) LIB_EXT := dll @@ -83,7 +88,7 @@ ifeq ($(OS),Darwin) SO_LINKER_ARGS := -shared -undefined suppress -flat_namespace -L$(PYTHON_LIBDIR) else ifeq ($(OS),Linux) SO_LINKER_ARGS := -shared -Xlinker -L$(PYTHON_LIBDIR) -else ifeq ($(OS),MINGW32_NT-6.1) +else ifeq ($(OS),Msys) SO_LINKER_ARGS := -shared -Wl,-no-undefined -Wl,-enable-runtime-pseudo-reloc-v2 \ -Wl,--enable-auto-import -L$(PYTHON_LIBDIR) else diff --git a/makefiles/Makefile.pylib.MINGW32_NT-6.1 b/makefiles/Makefile.pylib.Msys similarity index 100% rename from makefiles/Makefile.pylib.MINGW32_NT-6.1 rename to makefiles/Makefile.pylib.Msys diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 9ba227c4..8c23ce5a 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -47,7 +47,7 @@ ifeq ($(GPI_IMPL),vpi) GPI_ARGS = -pli libvpi endif ifeq ($(GPI_IMPL),vhpi) -ifeq ($(OS),MINGW32_NT-6.1) +ifeq ($(OS),Msys) VHPI_LIB = $(shell sh -c 'cd $(LIB_DIR) && pwd -W')/libvhpi else VHPI_LIB = $(LIB_DIR)/libvhpi @@ -92,7 +92,7 @@ ifeq ($(ALDEC_PATH),) $(error "Need to set ALDEC_PATH") endif -ifeq ($(OS),MINGW32_NT-6.1) +ifeq ($(OS),Msys) EXTRA_LIBS := -laldecpli EXTRA_LIBDIRS := -L$(ALDEC_PATH)/bin/ diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index 565d8eb4..e7044c39 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -34,7 +34,7 @@ $(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) iverilog -o $(SIM_BUILD)/sim.vvp -D COCOTB_SIM=1 $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) # Execution phase -ifeq ($(OS),MINGW32_NT-6.1) +ifeq ($(OS),Msys) ifeq ($(ICARUS_PATH),) $(error "Need to set ICARUS_PATH") diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index d7faf282..e25a8660 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -64,7 +64,7 @@ ifneq ($(GUI),1) endif -ifeq ($(OS),MINGW32_NT-6.1) +ifeq ($(OS),Msys) ifeq ($(MODELSIM_PATH),) $(error "Need to set MODELSIM_PATH") From 2ded021b9494399e0156f6a5096e4dcdb226eb25 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Sun, 18 Jan 2015 15:33:09 +1100 Subject: [PATCH 0754/2656] autodetect path to mtipli.dll --- makefiles/simulators/Makefile.modelsim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index e25a8660..debb3032 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -71,7 +71,7 @@ $(error "Need to set MODELSIM_PATH") endif EXTRA_LIBS := -lmtipli -EXTRA_LIBDIRS := -L$(MODELSIM_PATH)/win32aloem +EXTRA_LIBDIRS := -L$(shell find $(MODELSIM_PATH) -maxdepth 2 -type f -name 'mtipli.dll' -printf '%h') OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') LIB_LOAD := PATH=$(OLD_PATH):$(LIB_DIR) From 8b2f0867240b45ce4d0fb0c64f79eb3e7171c20b Mon Sep 17 00:00:00 2001 From: "J.Morarity" Date: Sat, 17 Jan 2015 23:07:45 -0800 Subject: [PATCH 0755/2656] fix improper package reference in BinaryValue class --- cocotb/binary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 4c4f227a..06124160 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -105,7 +105,7 @@ def assign(self, value): if the string contains any characters that aren't 0, 1, X or Z then we interpret the string as a binary buffer... """ - if isinstance(value, utils.get_python_integer_types()): + if isinstance(value, get_python_integer_types()): self.value = value elif isinstance(value, str): try: From 1a5b3675c0c5fd2cf6056c6d11dbb1f37f9d4239 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 18 Jan 2015 09:10:03 +0000 Subject: [PATCH 0756/2656] Add badge to documentation --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e9ffedbb..0ac55144 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ **cocotb** is a coroutine based cosimulation library for writing VHDL and Verilog testbenches in Python. +[![Documentation Status](https://readthedocs.org/projects/cocotb/badge/?version=latest)](http://cocotb.readthedocs.org/en/latest/) [![Build Status](https://travis-ci.org/potentialventures/cocotb.svg?branch=master)](https://travis-ci.org/potentialventures/cocotb) * Skim the introductory presentation: http://potential.ventures From 0cc31fd0e4ba57aa215c661f92632c719ff777d3 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 18 Jan 2015 09:10:42 +0000 Subject: [PATCH 0757/2656] Issue #9: Add information about simple Makefile to quickstart guide --- documentation/source/quickstart.rst | 63 ++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index ef2c8672..b182d279 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -22,9 +22,7 @@ Running an example .. code-block:: bash $> git clone https://github.com/potentialventures/cocotb - $> cd cocotb - $> make - $> cd examples/endian_swapper + $> cd cocotb/examples/endian_swapper/tests $> make To run a test using a different simulator: @@ -45,12 +43,58 @@ The endian swapper example includes both a VHDL and Verilog RTL implementation. -Using cocotb +Using Cocotb ============ -A typical cocotb testbench requires no additional RTL code. +A typical Cocotb testbench requires no additional RTL code. The Design Under Test (DUT) is instantiated as the toplevel in the simulator without any wrapper code. -Cocotb drives stimulus onto the inputs to the DUT (or further down the hierarchy) and monitors the outputs directly from Python. +Cocotb drives stimulus onto the inputs to the DUT and monitors the outputs directly from Python. + + +Creating a Makefile +------------------- + +To create a Cocotb test we typically have to create a Makefile. Cocotb provides +rules which make it easy to get started. We simply inform Cocotb of the +source files we need compiling, the toplevel entity to instantiate and the +python test script to load. + +.. code-block:: bash + + VERILOG_SOURCES = $(PWD)/submodule.sv $(PWD)/my_design.sv + TOPLEVEL=my_design + MODULE=test_my_design + include $(COCOTB)/makefiles/Makefile.inc + include $(COCOTB)/makefiles/Makefile.sim + +We would then create a file called ``test_my_design.py`` containing our tests. + + +Creating a test +--------------- + +The test is written in Python. Assuming we have a toplevel port called ``clk`` +we could create a test file containing the following: + +.. code-block:: python + + import cocotb + from cocotb.triggers import Timer + + @cocotb.test() + def my_first_test(dut): + """ + Try accessing the design + """ + dut.log.info("Running test!") + for cycle in range(10): + dut.clk = 0 + yield Timer(1000) + dut.clk = 1 + yield Timer(1000) + dut.log.info("Running test!") + +This will drive a square wave clock onto the ``clk`` port of the toplevel. Accessing the design @@ -103,6 +147,13 @@ Accessing the .value property of a handle object will return a :class:`BinaryVal >>> print count.integer 42 +We can also cast the signal handle directly to an integer: + +.. code-block:: python + + >>> print int(dut.counter) + 42 + Parallel and sequential execution of coroutines From a1431f6edbdece6098a5606118695f4d3450b305 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 19 Jan 2015 09:39:39 +0000 Subject: [PATCH 0758/2656] Issue #196: Add initial Makefile for CVC (doesn't work) --- makefiles/simulators/Makefile.cvc | 41 +++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 makefiles/simulators/Makefile.cvc diff --git a/makefiles/simulators/Makefile.cvc b/makefiles/simulators/Makefile.cvc new file mode 100644 index 00000000..c9269f78 --- /dev/null +++ b/makefiles/simulators/Makefile.cvc @@ -0,0 +1,41 @@ +############################################################################### +# Copyright (c) 2014 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +# Compilation phase +$(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(COCOTB_LIBS) + LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) \ + cvc64 +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=libvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) + +# Execution phase +results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) + +clean:: + -@rm -rf $(SIM_BUILD) + From b0c8130f05ddf17c4e882785d882cbf9c59349e5 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 19 Jan 2015 15:57:24 +0000 Subject: [PATCH 0759/2656] Cleanup: Force mingw/bin to front of path so that runtime c++ lib is the one we built with not any random one found --- makefiles/simulators/Makefile.aldec | 10 +++++++++- makefiles/simulators/Makefile.modelsim | 12 ++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 9ba227c4..4d7f8587 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -94,10 +94,18 @@ endif ifeq ($(OS),MINGW32_NT-6.1) +# Windows allows the situation where the libstc++ used at link time as +# specified by -L can be different to the one that is used at runtime which +# comes from the first libstdc++ that it finds in the path. As such +# we use the mingw lib used at build time and put this at the start of the path +# before running + +MINGW_BIN_DIR = $(shell which gcc | sed 's|/gcc.exe||') + EXTRA_LIBS := -laldecpli EXTRA_LIBDIRS := -L$(ALDEC_PATH)/bin/ OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') -LIB_LOAD := PATH=$(OLD_PATH):$(LIB_DIR) +LIB_LOAD := PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) else EXTRA_LIBS = -laldecpli diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index 1265987f..c83369c3 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -68,15 +68,23 @@ ifeq ($(OS),MINGW32_NT-6.1) # auto-detect modelsim dir from system path MODELSIM_BIN_DIR = $(shell which vsim | sed 's|/vsim.exe||') -# make sure python dir was set properly +# make sure modelsim dir was found ifeq ($(MODELSIM_BIN_DIR),) $(error "Directory containing ModelSim binaries must be included in system path") endif +# Windows allows the situation where the libstc++ used at link time as +# specified by -L can be different to the one that is used at runtime which +# comes from the first libstdc++ that it finds in the path. As such +# we use the mingw lib used at build time and put this at the start of the path +# before running + +MINGW_BIN_DIR = $(shell which gcc | sed 's|/gcc.exe||') + EXTRA_LIBS := -lmtipli EXTRA_LIBDIRS := -L$(MODELSIM_BIN_DIR) OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') -LIB_LOAD := PATH=$(OLD_PATH):$(LIB_DIR) +LIB_LOAD := PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) else LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) From 9cbf3345e84e312e47f9266c7488a8fab8b3178d Mon Sep 17 00:00:00 2001 From: Sigve Tjora Date: Tue, 20 Jan 2015 00:27:12 +0100 Subject: [PATCH 0760/2656] Fixed possible resource leak and removed duplicate case statement Fixed possible resource leak by freeing new_hdl object if creation of new_obj failed. Refactored duplicated case statement (and surrounding code) in methods GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) into method GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &name) --- lib/vpi/VpiImpl.cpp | 77 +++++++++++++++++++++------------------------ lib/vpi/VpiImpl.h | 2 +- 2 files changed, 36 insertions(+), 43 deletions(-) diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 268cfc8f..769c7c93 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -70,25 +70,16 @@ void VpiImpl::get_sim_time(uint32_t *high, uint32_t *low) *low = vpi_time_s.low; } -GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) +GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &name) { int32_t type; - vpiHandle new_hdl; - GpiObjHdl *new_obj = NULL; - std::vector writable(name.begin(), name.end()); - writable.push_back('\0'); - - new_hdl = vpi_handle_by_name(&writable[0], NULL); - - if (!new_hdl) - return NULL; - + GpiObjHdl *new_obj = NULL; if (vpiUnknown == (type = vpi_get(vpiType, new_hdl))) { - vpi_free_object(new_hdl); - return new_obj; + LOG_ERROR("vpiUnknown returned from vpi_get(vpiType, ...)") + return NULL; } - /* What sort of isntance is this ?*/ + /* What sort of instance is this ?*/ switch (type) { case vpiNet: case vpiReg: @@ -103,7 +94,7 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) new_obj = new GpiObjHdl(this, new_hdl); break; default: - LOG_INFO("Not sure what to do with type %d for entity (%s)", type, name.c_str()); + LOG_WARN("Not able to map type %d to object."); return NULL; } @@ -112,43 +103,45 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) return new_obj; } +GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) +{ + vpiHandle new_hdl; + std::vector writable(name.begin(), name.end()); + writable.push_back('\0'); + + new_hdl = vpi_handle_by_name(&writable[0], NULL); + if (new_hdl == NULL) { + LOG_WARN("Could not get vpi_get_handle_by_name %s", name.c_str()); + return NULL; + } + GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name); + if (new_obj == NULL) { + vpi_free_object(new_hdl); + LOG_WARN("Could not fetch object %s", name.c_str()); + return NULL; + } + return new_obj; +} + GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) { - int32_t type; GpiObjHdl *parent_hdl = sim_to_hdl(parent); vpiHandle vpi_hdl = parent_hdl->get_handle(); vpiHandle new_hdl; - GpiObjHdl *new_obj = NULL; new_hdl = vpi_handle_by_index(vpi_hdl, index); - - if (!new_hdl) + if (new_hdl == NULL) { + LOG_WARN("Error for vpi_get_handle_by_index %d. Is index out of bounds?", index); return NULL; - - if (vpiUnknown == (type = vpi_get(vpiType, new_hdl))) { - vpi_free_object(vpi_hdl); - return new_obj; - } - - /* What sort of isntance is this ?*/ - switch (type) { - case vpiNet: - case vpiNetBit: - case vpiEnumNet: - new_obj = new VpiSignalObjHdl(this, new_hdl); - break; - case vpiModule: - new_obj = new GpiObjHdl(this, new_hdl); - break; - default: - LOG_DEBUG("Not sure what to do with type %d below entity (%s) at index (%d)", - type, parent->get_name_str(), index); - return NULL; } - std::string name = vpi_get_str(vpiFullName, new_hdl); - new_obj->initialise(name); - + GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name); + if (new_obj == NULL) { + vpi_free_object(new_hdl); + LOG_WARN("Could not fetch object below entity (%s) at index (%d)", + parent->get_name_str(), index); + return NULL; + } return new_obj; } diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 59d89308..0763c843 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -215,10 +215,10 @@ class VpiImpl : public GpiImplInterface { int deregister_callback(GpiCbHdl *obj_hdl); GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); - const char * reason_to_string(int reason); private: + GpiObjHdl* create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &name); /* Singleton callbacks */ VpiReadwriteCbHdl m_read_write; VpiNextPhaseCbHdl m_next_phase; From 4ad29cf93ac9f4c9750f0033a9214abf86c663c4 Mon Sep 17 00:00:00 2001 From: Sigve Tjora Date: Tue, 20 Jan 2015 00:37:16 +0100 Subject: [PATCH 0761/2656] Added support for vpiInterfaceArray and vpiRefObj This allows indexing arrays of interfaces and allows use of modports. Supports making Bus and BusDriver instances that connects via interfaces or modports. --- cocotb/bus.py | 4 +++- cocotb/drivers/__init__.py | 5 +++-- include/vpi_user.h | 2 ++ lib/vpi/VpiImpl.cpp | 2 ++ 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/cocotb/bus.py b/cocotb/bus.py index 70e1b2f2..2b5abf57 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -53,7 +53,9 @@ def __init__(self, entity, name, signals, optional_signals=[]): """ Args: entity (SimHandle): SimHandle instance to the entity containing the bus - name (str): name of the bus + name (str): name of the bus. None for nameless bus, e.g. + bus-signals in an interface or a modport + (untested on struct/record, but could work here as well) signals (list): array of signal names Kwargs: diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index ef3d177a..832610c3 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -201,8 +201,9 @@ def __init__(self, entity, name, clock): Args: entity (SimHandle) : a handle to the simulator entity - name (str) : name of this bus - + name (str) : name of this bus. None for nameless bus, e.g. + bus-signals in an interface or a modport + (untested on struct/record, but could work here as well) clock (SimHandle) : A handle to the clock associated with this bus """ self.log = SimLog("cocotb.%s.%s" % (entity.name, name)) diff --git a/include/vpi_user.h b/include/vpi_user.h index b782dd80..5cbf9d77 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -63,7 +63,9 @@ typedef uint32_t *vpiHandle; #define vpiParameter 41 /* module parameter */ #define vpiStructVar 618 #define vpiInterface 601 +#define vpiInterfaceArray 603 #define vpiModport 606 +#define vpiRefObj 608 #define vpiEnumNet 680 /* SystemVerilog */ #define vpiStop 66 /* execute simulator's $stop */ diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 769c7c93..ec8d54e5 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -91,6 +91,8 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &n case vpiModule: case vpiInterface: case vpiModport: + case vpiInterfaceArray: + case vpiRefObj: new_obj = new GpiObjHdl(this, new_hdl); break; default: From f5fdee032c69c1c797b76ddad053f1d373ebac17 Mon Sep 17 00:00:00 2001 From: Sigve Tjora Date: Fri, 16 Jan 2015 00:47:01 +0100 Subject: [PATCH 0762/2656] Added kwargs to TestFactor.__init__ Added support for kwargs in TestFactor.__init__ for added readability. --- cocotb/regression.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 2bcb1fba..87a0f6d6 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -305,7 +305,7 @@ class TestFactory(object): the test description) includes the name and description of each generator. """ - def __init__(self, test_function, *args): + def __init__(self, test_function, *args, **kwargs): """ Args: test_function (function): the function that executes a test. @@ -315,6 +315,10 @@ def __init__(self, test_function, *args): Note that these arguments are not varied. An argument that varies with each test must be a keyword argument to the test function. + *kwargs: Remaining kwargs are passed directly to the test function. + Note that these arguments are not varied. An argument that + varies with each test must be a keyword argument to the + test function. """ if not isinstance(test_function, cocotb.coroutine): raise TypeError("TestFactory requires a cocotb coroutine") @@ -322,6 +326,7 @@ def __init__(self, test_function, *args): self.name = self.test_function._func.__name__ self.args = args + self.kwargs_constant = kwargs self.kwargs = {} def add_option(self, name, optionlist): @@ -363,5 +368,11 @@ def generate_tests(self): doc += "\t%s: %s\n" % (optname, repr(optvalue)) cocotb.log.debug("Adding generated test \"%s\" to module \"%s\"" % (name, mod.__name__)) - setattr(mod, name, _create_test(self.test_function, name, doc, mod, *self.args, **testoptions)) + kwargs = {} + kwargs.update(self.kwargs_constant) + kwargs.update(testoptions) + if hasattr(mod, name): + cocotb.log.error("Overwriting %s in module %s. This causes previously defined testcase " + "not to be run. Consider setting/changing name_postfix" % (name, mod)) + setattr(mod, name, _create_test(self.test_function, name, doc, mod, *self.args, **kwargs)) From 2a8edb90009fbea4bab7659de861816725036705 Mon Sep 17 00:00:00 2001 From: Sigve Tjora Date: Tue, 20 Jan 2015 08:45:31 +0100 Subject: [PATCH 0763/2656] Added prefix and postfix parameters to TestFactory.generate_tests This makes it possible to avoid nameclashes when using the same test_function with multiple TestFactories. Log errors when name-clashes are detected. --- cocotb/regression.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 87a0f6d6..3ce0a182 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -340,13 +340,23 @@ def add_option(self, name, optionlist): """ self.kwargs[name] = optionlist - def generate_tests(self): + def generate_tests(self, prefix="", postfix=""): """ Generates exhasutive set of tests using the cartesian product of the possible keyword arguments. The generated tests are appended to the namespace of the calling module. + + Args: + prefix: Text string to append to start of test_function name + when naming generated test cases. This allows reuse of + a single test_function with multiple TestFactories without + name clashes. + postfix: Text string to append to end of test_function name + when naming generated test cases. This allows reuse of + a single test_function with multiple TestFactories without + name clashes. """ frm = inspect.stack()[1] @@ -356,7 +366,7 @@ def generate_tests(self): for index, testoptions in enumerate( (dict(zip(d, v)) for v in product(*d.values())) ): - name = "%s_%03d" % (self.name, index + 1) + name = "%s%s%s_%03d" % (prefix, self.name, postfix, index + 1) doc = "Automatically generated test\n\n" for optname, optvalue in testoptions.items(): From 05c0bd8246768f4f035369e31de4b325f398ec90 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Tue, 20 Jan 2015 20:57:28 +1100 Subject: [PATCH 0764/2656] Ensure correct paths are set under msys. ('which gcc' returns path_to_gcc/gcc under msys rather than path_to_gcc/gcc.exe under mingw) --- makefiles/Makefile.pylib.Msys | 2 +- makefiles/simulators/Makefile.aldec | 2 +- makefiles/simulators/Makefile.modelsim | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/makefiles/Makefile.pylib.Msys b/makefiles/Makefile.pylib.Msys index 69a981c8..93eee3e9 100644 --- a/makefiles/Makefile.pylib.Msys +++ b/makefiles/Makefile.pylib.Msys @@ -31,7 +31,7 @@ # if not explicitly set, auto-detect python dir from system path ifeq ($(PYTHON_DIR),) -PYTHON_DIR = $(shell which python | sed 's|/python.exe||') +PYTHON_DIR = $(shell dirname $(shell which python)) endif # make sure python dir was set properly diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 1783ed1b..432d8e11 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -100,7 +100,7 @@ ifeq ($(OS),Msys) # we use the mingw lib used at build time and put this at the start of the path # before running -MINGW_BIN_DIR = $(shell which gcc | sed 's|/gcc.exe||') +MINGW_BIN_DIR = $(shell dirname $(shell which gcc)) EXTRA_LIBS := -laldecpli EXTRA_LIBDIRS := -L$(ALDEC_PATH)/bin/ diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index 6cd89419..6df751d2 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -66,7 +66,7 @@ endif ifeq ($(OS),Msys) # auto-detect modelsim dir from system path -MODELSIM_BIN_DIR = $(shell which vsim | sed 's|/vsim.exe||') +MODELSIM_BIN_DIR = $(shell dirname $(shell which vsim)) # make sure modelsim dir was found ifeq ($(MODELSIM_BIN_DIR),) @@ -79,7 +79,7 @@ endif # we use the mingw lib used at build time and put this at the start of the path # before running -MINGW_BIN_DIR = $(shell which gcc | sed 's|/gcc.exe||') +MINGW_BIN_DIR = $(shell dirname $(shell which gcc)) EXTRA_LIBS := -lmtipli EXTRA_LIBDIRS := -L$(MODELSIM_BIN_DIR) From 877d778615a31d32f76cd74a6c6a9d75083cb522 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Tue, 20 Jan 2015 21:23:53 +1100 Subject: [PATCH 0765/2656] Convert windows style PYTHONPATH to posix so that it's picked up correctly under msys. --- makefiles/simulators/Makefile.aldec | 1 + makefiles/simulators/Makefile.icarus | 1 + makefiles/simulators/Makefile.modelsim | 1 + 3 files changed, 3 insertions(+) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 432d8e11..c12e499c 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -106,6 +106,7 @@ EXTRA_LIBS := -laldecpli EXTRA_LIBDIRS := -L$(ALDEC_PATH)/bin/ OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') LIB_LOAD := PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) +PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' | sed 's/\([a-zA-Z]\)\(:\)\//\/\1\//g' | sed 's/;/:/g') else EXTRA_LIBS = -laldecpli diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index e7044c39..f3322630 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -44,6 +44,7 @@ EXTRA_LIBS := -lvpi EXTRA_LIBDIRS := -L$(ICARUS_PATH)/lib OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') LIB_LOAD := PATH=$(OLD_PATH):$(LIB_DIR) +PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' | sed 's/\([a-zA-Z]\)\(:\)\//\/\1\//g' | sed 's/;/:/g') else LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) endif diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index 6df751d2..d2bff64b 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -86,6 +86,7 @@ EXTRA_LIBDIRS := -L$(MODELSIM_BIN_DIR) OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') LIB_LOAD := PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) +PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' | sed 's/\([a-zA-Z]\)\(:\)\//\/\1\//g' | sed 's/;/:/g') else LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) From 87b90c19e9e2e53e77119556f46cbac909aed5b1 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 21 Jan 2015 14:50:45 +0000 Subject: [PATCH 0766/2656] Issue #197: Add back vpiNetBit, tests now pass again --- lib/vpi/VpiImpl.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index ec8d54e5..a64b5400 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -75,13 +75,14 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &n int32_t type; GpiObjHdl *new_obj = NULL; if (vpiUnknown == (type = vpi_get(vpiType, new_hdl))) { - LOG_ERROR("vpiUnknown returned from vpi_get(vpiType, ...)") + LOG_DEBUG("vpiUnknown returned from vpi_get(vpiType, ...)") return NULL; } /* What sort of instance is this ?*/ switch (type) { case vpiNet: + case vpiNetBit: case vpiReg: case vpiParameter: case vpiEnumNet: @@ -96,7 +97,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &n new_obj = new GpiObjHdl(this, new_hdl); break; default: - LOG_WARN("Not able to map type %d to object."); + LOG_WARN("Not able to map type %d to object.", type); return NULL; } @@ -113,13 +114,13 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) new_hdl = vpi_handle_by_name(&writable[0], NULL); if (new_hdl == NULL) { - LOG_WARN("Could not get vpi_get_handle_by_name %s", name.c_str()); + LOG_WARN("Failed to query vpi_get_handle_by_name %s", name.c_str()); return NULL; } GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name); if (new_obj == NULL) { vpi_free_object(new_hdl); - LOG_WARN("Could not fetch object %s", name.c_str()); + LOG_WARN("Failed to query fetch object %s", name.c_str()); return NULL; } return new_obj; From c138a7631f6f0f328a39e9859c4dbf8ddbfffa2a Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 21 Jan 2015 15:11:49 +0000 Subject: [PATCH 0767/2656] Issue #197: Lower not finding a signal, which could be optional to LOG_DEBUG. It is was needed then python will complain --- lib/vpi/VpiImpl.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index a64b5400..9808f0b5 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -114,13 +114,13 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) new_hdl = vpi_handle_by_name(&writable[0], NULL); if (new_hdl == NULL) { - LOG_WARN("Failed to query vpi_get_handle_by_name %s", name.c_str()); + LOG_DEBUG("Unable to query vpi_get_handle_by_name %s", name.c_str()); return NULL; } GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name); if (new_obj == NULL) { vpi_free_object(new_hdl); - LOG_WARN("Failed to query fetch object %s", name.c_str()); + LOG_ERROR("Unable to query fetch object %s", name.c_str()); return NULL; } return new_obj; @@ -134,14 +134,14 @@ GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) new_hdl = vpi_handle_by_index(vpi_hdl, index); if (new_hdl == NULL) { - LOG_WARN("Error for vpi_get_handle_by_index %d. Is index out of bounds?", index); + LOG_DEBUG("Unable to vpi_get_handle_by_index %d", index); return NULL; } std::string name = vpi_get_str(vpiFullName, new_hdl); GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name); if (new_obj == NULL) { vpi_free_object(new_hdl); - LOG_WARN("Could not fetch object below entity (%s) at index (%d)", + LOG_ERROR("Unable to fetch object below entity (%s) at index (%d)", parent->get_name_str(), index); return NULL; } From db20f1d781c32e2482b558830efff62e3745a886 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 21 Jan 2015 15:14:01 +0000 Subject: [PATCH 0768/2656] Issue #199: Move switch on type of GPI object to create to common code as per vpi, also harmonise message levels --- lib/vhpi/VhpiImpl.cpp | 76 ++++++++++++++++++++----------------------- lib/vhpi/VhpiImpl.h | 1 + 2 files changed, 37 insertions(+), 40 deletions(-) diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index b5fafd7c..d4cf9b56 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -110,39 +110,27 @@ void VhpiImpl::get_sim_time(uint32_t *high, uint32_t *low) *low = vhpi_time_s.low; } -GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) +GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string &name) { vhpiIntT type; - GpiObjHdl *parent_hdl = sim_to_hdl(parent); - vhpiHandleT vpi_hdl = parent_hdl->get_handle(); - vhpiHandleT new_hdl; GpiObjHdl *new_obj = NULL; - unsigned int name_start = 0; - std::vector writable(name.begin(), name.end()); - writable.push_back('\0'); - - new_hdl = vhpi_handle_by_name(&writable[name_start], NULL); - - if (!new_hdl) - return NULL; if (vhpiVerilog == (type = vhpi_get(vhpiKindP, new_hdl))) { - vhpi_release_handle(vpi_hdl); - LOG_DEBUG("Not a VHPI object"); + LOG_DEBUG("vhpiVerilog returned from vhpi_get(vhpiType, ...)") return NULL; } - /* What sort of isntance is this ?*/ switch (type) { case vhpiPortDeclK: case vhpiSigDeclK: + case vhpiIndexedNameK: new_obj = new VhpiSignalObjHdl(this, new_hdl); break; case vhpiCompInstStmtK: new_obj = new GpiObjHdl(this, new_hdl); break; default: - LOG_DEBUG("Not sure what to do with type %d for entity (%s)", type, name.c_str()); + LOG_WARN("Not able to map type %d to object."); return NULL; } @@ -151,42 +139,50 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) return new_obj; } -GpiObjHdl *VhpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) +GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) { - vhpiIntT type; - GpiObjHdl *parent_hdl = sim_to_hdl(parent); - vhpiHandleT vpi_hdl = parent_hdl->get_handle(); vhpiHandleT new_hdl; - GpiObjHdl *new_obj = NULL; + std::vector writable(name.begin(), name.end()); + writable.push_back('\0'); - new_hdl = vhpi_handle_by_index(vhpiIndexedNames, vpi_hdl, index); - check_vhpi_error(); + new_hdl = vhpi_handle_by_name(&writable[0], NULL); - if (!new_hdl) + if (new_hdl == NULL) { + LOG_DEBUG("Unable to query vhpi_handle_by_name %s", name.c_str()); return NULL; + } - if (vhpiVerilog == (type = vhpi_get(vhpiKindP, new_hdl))) { - vhpi_release_handle(vpi_hdl); - LOG_DEBUG("Not a VHPI object"); + GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name); + if (new_obj == NULL) { + vhpi_release_handle(new_hdl); + LOG_DEBUG("Unable to fetch object %s", name.c_str()); return NULL; } - /* What sort of isntance is this ?*/ - switch (type) { - case vhpiIndexedNameK: - new_obj = new VhpiSignalObjHdl(this, new_hdl); - break; - case vhpiCompInstStmtK: - new_obj = new GpiObjHdl(this, new_hdl); - break; - default: - LOG_DEBUG("Not sure what to do with type %d below entity (%s) at index (%d)", - type, parent->get_name_str(), index); - return NULL; + return new_obj; +} + +GpiObjHdl *VhpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) +{ + GpiObjHdl *parent_hdl = sim_to_hdl(parent); + vhpiHandleT vpi_hdl = parent_hdl->get_handle(); + vhpiHandleT new_hdl; + + new_hdl = vhpi_handle_by_index(vhpiIndexedNames, vpi_hdl, index); + + if (new_hdl == NULL) { + LOG_DEBUG("Unable to query vhpi_handle_by_index %s", index); + return NULL; } std::string name = vhpi_get_str(vhpiNameP, new_hdl); - new_obj->initialise(name); + GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name); + if (new_obj == NULL) { + vhpi_release_handle(new_hdl); + LOG_DEBUG("Could not fetch object below entity (%s) at index (%d)", + parent->get_name_str(), index); + return NULL; + } return new_obj; } diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 51a1a4f7..41e5aa9f 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -202,6 +202,7 @@ class VhpiImpl : public GpiImplInterface { const char * format_to_string(int format); private: + GpiObjHdl *create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string &name); VhpiReadwriteCbHdl m_read_write; VhpiNextPhaseCbHdl m_next_phase; VhpiReadOnlyCbHdl m_read_only; From 7d3f19310cefdb04d7823e956786e2e9e19846d8 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 21 Jan 2015 15:14:24 +0000 Subject: [PATCH 0769/2656] Cleanup: Expect fail for aldec as well --- examples/functionality/tests/test_cocotb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 2468424b..950328c5 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -169,7 +169,7 @@ def do_test_afterdelay_in_readonly(dut, delay): yield Timer(delay) exited = True -@cocotb.test(expect_error=True, expect_fail=cocotb.SIM_NAME in ["Icarus Verilog"]) +@cocotb.test(expect_error=True, expect_fail=cocotb.SIM_NAME in ["Icarus Verilog", "Riviera-PRO"]) def test_readwrite_in_readonly(dut): """Test doing invalid sim operation""" global exited From 1a2203c5c659942de632163ad711a2de9a6f4f01 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 21 Jan 2015 15:20:18 +0000 Subject: [PATCH 0770/2656] Cleanup: Also mark the modelsim versions that we test against as expect_fail as well --- examples/functionality/tests/test_cocotb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 950328c5..a6455c98 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -169,7 +169,7 @@ def do_test_afterdelay_in_readonly(dut, delay): yield Timer(delay) exited = True -@cocotb.test(expect_error=True, expect_fail=cocotb.SIM_NAME in ["Icarus Verilog", "Riviera-PRO"]) +@cocotb.test(expect_error=True, expect_fail=cocotb.SIM_NAME in ["Icarus Verilog", "Riviera-PRO", "ModelSim DE", "ModelSim ALTERA STARTER EDITION"]) def test_readwrite_in_readonly(dut): """Test doing invalid sim operation""" global exited From b61d9b03878dba620cc066a0f82d8fa87e4651ba Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 21 Jan 2015 15:23:08 +0000 Subject: [PATCH 0771/2656] Fixes #201: Ensure we don't create a new handle if one exists already --- cocotb/handle.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cocotb/handle.py b/cocotb/handle.py index d489dace..34f68a5c 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -70,6 +70,9 @@ def __init__(self, handle): self._r_edge = _RisingEdge(self) self._f_edge = _FallingEdge(self) + def __hash__(self): + return self._handle + def __str__(self): return "%s @0x%x" % (self.name, self._handle) @@ -117,6 +120,8 @@ def __hasattr__(self, name): We still add the found handle to our dictionary to prevent leaking handles. """ + if name in self._sub_handles: + return self._sub_handles[name] new_handle = simulator.get_handle_by_name(self._handle, name) if new_handle: self._sub_handles[name] = SimHandle(new_handle) From fe9c06cd3eb3cac87f4180026dc1d68842347f27 Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 21 Jan 2015 15:34:24 +0000 Subject: [PATCH 0772/2656] Cleanup: Get rid of annoying warning message about TOPLEVEL_LANG --- lib/embed/gpi_embed.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index c35febae..4c9cc9e2 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -220,15 +220,17 @@ int embed_sim_init(gpi_sim_info_t *info) goto cleanup; } - // Set languare in use + // Set language in use as an attribute to cocotb module, or None if not provided const char *lang = getenv("TOPLEVEL_LANG"); - if (!lang) - fprintf(stderr, "You should really set TOPLEVEL_LANG to \"verilog/vhdl\""); - else { - if (-1 == PyObject_SetAttrString(cocotb_module, "LANGUAGE", PyString_FromString(lang))) { - fprintf(stderr, "Unable to set LANGUAGE"); - goto cleanup; - } + PyObject* PyLang; + if (lang) + PyLang = PyString_FromString(lang); + else + PyLang = Py_None; + + if (-1 == PyObject_SetAttrString(cocotb_module, "LANGUAGE", PyLang)) { + fprintf(stderr, "Unable to set LANGUAGE"); + goto cleanup; } // Hold onto a reference to our _fail_test function From 9b51d596457f37850e69117162bc848edbfe6f27 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 21 Jan 2015 20:12:40 +0000 Subject: [PATCH 0773/2656] Issue #202: Allow make errors to propogate, make the sim exit if python failed on init. --- cocotb/result.py | 2 +- examples/Makefile | 2 +- lib/embed/gpi_embed.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cocotb/result.py b/cocotb/result.py index ba9e763d..8ff8d5f6 100644 --- a/cocotb/result.py +++ b/cocotb/result.py @@ -38,7 +38,7 @@ def raise_error(obj, msg): obj has a log method msg is a string """ - exc_type, exc_value, exc_traceback = sys.exc_info() + exc_type, exc_value, exc_traceback = sys.exc_info() if sys.version_info.major >= 3: buff = StringIO() traceback.print_tb(exc_traceback, file=buff) diff --git a/examples/Makefile b/examples/Makefile index 6f777c8b..0de8fbdc 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -41,7 +41,7 @@ SMOKE_TESTS := plusargs \ all: $(SMOKE_TESTS) $(SMOKE_TESTS): - -@cd $@ && $(MAKE) + @cd $@ && $(MAKE) clean: $(foreach TEST, $(SMOKE_TESTS), $(MAKE) -C $(TEST) clean;) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 4c9cc9e2..aeaf1af8 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -262,8 +262,8 @@ int embed_sim_init(gpi_sim_info_t *info) Py_DECREF(cocotb_retval); } else { PyErr_Print(); - fprintf(stderr,"Call failed\n"); - goto cleanup; + fprintf(stderr,"Cocotb initialisation failed - exiting\n"); + exit(1); } FEXIT From ca58c83ea4451e34af5daf63e234cb7173cf9fa8 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Thu, 22 Jan 2015 23:48:03 +0100 Subject: [PATCH 0774/2656] cvc support --- lib/vpi/Makefile | 5 +++++ makefiles/simulators/Makefile.cvc | 34 +++++++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/lib/vpi/Makefile b/lib/vpi/Makefile index a042bf4a..3acbf3ef 100644 --- a/lib/vpi/Makefile +++ b/lib/vpi/Makefile @@ -39,6 +39,11 @@ SRCS := VpiImpl.cpp VpiCbHdl.cpp CLIBS += $(LIB_DIR)/gpivpi.vpl +#temporary hack for cvc +ifeq ($(SIM),cvc) + GXX_ARGS += -DMODELSIM +endif + # More rules such that this may be needed depending on the requirements of # different simulators, icarus for instance loads a .vpi libraray to use the vpi # inerface at the start of the simulation diff --git a/makefiles/simulators/Makefile.cvc b/makefiles/simulators/Makefile.cvc index c9269f78..d185e731 100644 --- a/makefiles/simulators/Makefile.cvc +++ b/makefiles/simulators/Makefile.cvc @@ -25,16 +25,42 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +#only interpreted mode works for the moment +CVC_ITERP ?= 1 + +ifeq ($(CVC_ITERP),1) + CVC_ARGS += +interp +endif + # Compilation phase -$(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(COCOTB_LIBS) - LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) \ - cvc64 +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=libvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) +$(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + cvc64 $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=libvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) # Execution phase -results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) +ifeq ($(CVC_ITERP),1) + results.xml: $(SIM_BUILD)/sim.vvp +else + results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) +endif + +# Execution phase +ifeq ($(CVC_ITERP),1) + debug: $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + gdb --args cvc64 $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=libvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) +else + debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + gdb --args $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) +endif + clean:: -@rm -rf $(SIM_BUILD) From 107b67b9cc5de68ba3ed34fa62d3cc4dd2504371 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 3 Feb 2015 10:25:32 +0000 Subject: [PATCH 0775/2656] Fixes #202: No not use name to access version info --- cocotb/result.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cocotb/result.py b/cocotb/result.py index 8ff8d5f6..fe5b935e 100644 --- a/cocotb/result.py +++ b/cocotb/result.py @@ -39,7 +39,8 @@ def raise_error(obj, msg): msg is a string """ exc_type, exc_value, exc_traceback = sys.exc_info() - if sys.version_info.major >= 3: + # 2.6 cannot use named access + if sys.version_info[0] >= 3: buff = StringIO() traceback.print_tb(exc_traceback, file=buff) else: From 5d5e1673070c541d6b58f25c069551372a3112a9 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 9 Feb 2015 19:41:47 +0000 Subject: [PATCH 0776/2656] Fix some dependency issues --- lib/fli/FliImpl.cpp | 18 +++++------------- lib/fli/FliImpl.h | 21 ++++++++++----------- makefiles/simulators/Makefile.modelsim | 1 + 3 files changed, 16 insertions(+), 24 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 16cd60cf..fa457330 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -106,7 +106,7 @@ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) LOG_INFO("Looking for child %s from %s", name.c_str(), parent->get_name_str()); - FliObjHdl *new_obj = NULL; + GpiObjHdl *new_obj = NULL; std::vector writable(name.begin(), name.end()); writable.push_back('\0'); @@ -154,6 +154,7 @@ void FliImpl::get_sim_time(uint32_t *high, uint32_t *low) *low = mti_Now(); } +#if 0 /** * @name Find the root handle * @brief Find the root handle using an optional name @@ -171,6 +172,7 @@ int FliObjHdl::initialise(std::string &name) { return 0; } +#endif /** @@ -187,7 +189,7 @@ int FliObjHdl::initialise(std::string &name) { GpiObjHdl *FliImpl::get_root_handle(const char *name) { mtiRegionIdT root; - FliObjHdl *rv; + GpiObjHdl *rv; std::string root_name = name; for (root = mti_GetTopRegion(); root != NULL; root = mti_NextRegion(root)) { @@ -326,7 +328,7 @@ GPI_ENTRY_POINT(fli, cocotb_init); -FliSignalCbHdl *FliSignalObjHdl::value_change_cb(void) { +GpiCbHdl *FliSignalObjHdl::value_change_cb(unsigned int edge) { LOG_INFO("Creating value change callback for %s", m_name.c_str()); @@ -338,16 +340,6 @@ FliSignalCbHdl *FliSignalObjHdl::value_change_cb(void) { return m_cb_hdl; } -GpiCbHdl *FliSignalObjHdl::rising_edge_cb(void) { - LOG_INFO("Creating rising edge callback for %s", m_name.c_str()); - return m_cb_hdl; -} - -GpiCbHdl *FliSignalObjHdl::falling_edge_cb(void) { - - LOG_INFO("Creating falling edge callback for %s", m_name.c_str()); - return m_cb_hdl; -} diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index b18eb426..b276b171 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -32,10 +32,11 @@ #include "mti.h" // FLI versions of base types -class FliObjHdl : public GpiObjHdl { +#if 0 +class GpiObjHdl : public GpiObjHdl { public: - FliObjHdl(GpiImplInterface *impl) : GpiObjHdl(impl) { } - virtual ~FliObjHdl() { } + GpiObjHdl(GpiImplInterface *impl) : GpiObjHdl(impl) { } + virtual ~GpiObjHdl() { } GpiObjHdl *get_handle_by_name(std::string &name) {return NULL; }; GpiObjHdl *get_handle_by_index(uint32_t index) {return NULL; } ; @@ -44,6 +45,7 @@ class FliObjHdl : public GpiObjHdl { int initialise(std::string &name); }; +#endif class FliCbHdl : public GpiCbHdl { public: @@ -159,9 +161,9 @@ class FliShutdownCbHdl : public FliCbHdl { -class FliRegionObjHdl : public FliObjHdl { +class FliRegionObjHdl : public GpiObjHdl { public: - FliRegionObjHdl(GpiImplInterface *impl, mtiRegionIdT hdl) : FliObjHdl(impl), + FliRegionObjHdl(GpiImplInterface *impl, mtiRegionIdT hdl) : GpiObjHdl(impl), m_fli_hdl(hdl) { } virtual ~FliRegionObjHdl() { } @@ -170,10 +172,9 @@ class FliRegionObjHdl : public FliObjHdl { }; -class FliSignalObjHdl : public FliObjHdl, public GpiSignalObjHdl { +class FliSignalObjHdl : public GpiSignalObjHdl { public: - FliSignalObjHdl(GpiImplInterface *impl, mtiSignalIdT hdl) : FliObjHdl(impl), - GpiSignalObjHdl(impl), + FliSignalObjHdl(GpiImplInterface *impl, mtiSignalIdT hdl) : GpiSignalObjHdl(impl, hdl), m_fli_hdl(hdl), m_cb_hdl(NULL) { } virtual ~FliSignalObjHdl() { } @@ -181,9 +182,7 @@ class FliSignalObjHdl : public FliObjHdl, public GpiSignalObjHdl { const char* get_signal_value_binstr(void); int set_signal_value(const int value); int set_signal_value(std::string &value); - GpiCbHdl *rising_edge_cb(void);// { return NULL; } - GpiCbHdl *falling_edge_cb(void);// { return NULL; } - FliSignalCbHdl *value_change_cb(void); + GpiCbHdl *value_change_cb(unsigned int edge); protected: mtiSignalIdT m_fli_hdl; FliSignalCbHdl *m_cb_hdl; diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index c83369c3..66e0fe4a 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -87,6 +87,7 @@ OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ LIB_LOAD := PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) else +export MODELSIM_BIN_DIR = $(shell which vsim | sed 's|/vsim||') LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) endif From 5697665824439b70269e3e3b2c1167346c0e41dc Mon Sep 17 00:00:00 2001 From: Chiggs Date: Mon, 9 Feb 2015 19:44:16 +0000 Subject: [PATCH 0777/2656] Add dependency on libfli.so --- makefiles/simulators/Makefile.modelsim | 1 + 1 file changed, 1 insertion(+) diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index 66e0fe4a..fda6aa10 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -46,6 +46,7 @@ endif ifeq ($(GPI_IMPL),vhpi) VSIM_ARGS += -foreign \"cocotb_init libfli.so\" -trace_foreign 3 +CUSTOM_SIM_DEPS += $(LIB_DIR)/libfli.so else VSIM_ARGS += -pli libvpi.$(LIB_EXT) endif From 0d2791861f5806f9fe82c0142a8144a91abb75a5 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Mon, 9 Feb 2015 19:58:19 +0000 Subject: [PATCH 0778/2656] Default to modelsim with VHDL toplevel and tracing enabled --- examples/endian_swapper/tests/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index 688cedba..1403ca5a 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -29,7 +29,9 @@ # Override this variable to use VHPI instead of VPI -TOPLEVEL_LANG ?= verilog +TOPLEVEL_LANG ?= vhdl +SIM ?= modelsim +VSIM_ARGS += "-trace_foreign 1" WPWD=$(shell sh -c 'pwd -W') PWD=$(shell pwd) From e20bdba7f75d89bdf9217d1c9c59ad61dc6fb057 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Mon, 9 Feb 2015 20:15:33 +0000 Subject: [PATCH 0779/2656] Ensure we set m_state to GPI_PRIMED when arming a callback --- lib/fli/FliImpl.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index fa457330..63608bbe 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -68,6 +68,9 @@ void cocotb_init(void) { // Main re-entry point for callbacks from simulator void handle_fli_callback(void *data) { + fprintf(stderr, "Got a callback\n"); + fflush(stderr); + FliCbHdl *cb_hdl = (FliCbHdl*)data; if (!cb_hdl) @@ -75,6 +78,9 @@ void handle_fli_callback(void *data) gpi_cb_state_e old_state = cb_hdl->get_call_state(); + fprintf(stderr, "FLI: Old state was %d!\n", old_state); + fflush(stderr); + if (old_state == GPI_PRIMED) { cb_hdl->set_call_state(GPI_CALL); @@ -297,6 +303,7 @@ int FliTimedCbHdl::arm_callback(void) { mti_ScheduleWakeup(m_proc_hdl, m_time_ps); LOG_INFO("Wakeup scheduled on %p for %llu", m_proc_hdl, m_time_ps); m_sensitised = true; + m_state = GPI_PRIMED; return 0; } @@ -309,6 +316,7 @@ int FliSignalCbHdl::arm_callback(void) { mti_Sensitize(m_proc_hdl, m_sig_hdl, MTI_EVENT); m_sensitised = true; + m_state = GPI_PRIMED; return 0; } @@ -321,6 +329,7 @@ int FliSimPhaseCbHdl::arm_callback(void) { mti_ScheduleWakeup(m_proc_hdl, 0); m_sensitised = true; + m_state = GPI_PRIMED; return 0; } From c3bc8866070e378e833127381aea9206e46665df Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 9 Feb 2015 20:26:14 +0000 Subject: [PATCH 0780/2656] Fix fli dependency breaking build as it should be later in the list --- lib/Makefile | 2 +- makefiles/simulators/Makefile.modelsim | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index 462d17a0..7dc22aa5 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -74,7 +74,7 @@ COCOTB_LIBS := $(LIB_DIR)/libcocotbutils.$(LIB_EXT) \ $(LIB_DIR)/libgpilog.$(LIB_EXT) \ $(LIB_DIR)/libcocotb.$(LIB_EXT) \ $(LIB_DIR)/libgpi.$(LIB_EXT) \ - $(LIB_DIR)/libsim.$(LIB_EXT) + $(LIB_DIR)/libsim.$(LIB_EXT) \ COCOTB_VPI_LIB := $(LIB_DIR)/libvpi.$(LIB_EXT) diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index fda6aa10..855ce260 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -46,7 +46,6 @@ endif ifeq ($(GPI_IMPL),vhpi) VSIM_ARGS += -foreign \"cocotb_init libfli.so\" -trace_foreign 3 -CUSTOM_SIM_DEPS += $(LIB_DIR)/libfli.so else VSIM_ARGS += -pli libvpi.$(LIB_EXT) endif @@ -92,7 +91,7 @@ export MODELSIM_BIN_DIR = $(shell which vsim | sed 's|/vsim||') LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) endif -results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(COCOTB_VPI_LIB) +results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(COCOTB_VPI_LIB) $(COCOTB_FLI_LIB) cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ $(SIM_CMD) -do runsim.do 2>&1 | tee sim.log From ea449d6799064fdb565123e0d7e268d49148ea96 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Mon, 9 Feb 2015 20:41:41 +0000 Subject: [PATCH 0781/2656] Flesh out FliSignalObjHdl::set_signal_value --- lib/fli/FliImpl.cpp | 28 ++++++++++++++++++++++++++-- lib/fli/FliImpl.h | 6 ------ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 63608bbe..ab764767 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -84,8 +84,12 @@ void handle_fli_callback(void *data) if (old_state == GPI_PRIMED) { cb_hdl->set_call_state(GPI_CALL); - cb_hdl->run_callback(); + fprintf(stderr, "FLI: Calling run_callback\n"); + fflush(stderr); + cb_hdl->run_callback(); + fprintf(stderr, "FLI: Callback executed\n"); + fflush(stderr); gpi_cb_state_e new_state = cb_hdl->get_call_state(); /* We have re-primed in the handler */ @@ -357,10 +361,30 @@ const char* FliSignalObjHdl::get_signal_value_binstr(void) { } int FliSignalObjHdl::set_signal_value(const int value) { - return 0; + + fprintf(stderr, "Setting signal to %d\n", value); + fflush(stderr); + + int rc; + char buff[20]; + + snprintf(buff, 19, "16#%016x", value); + + rc = mti_ForceSignal(m_fli_hdl, buff, 0, MTI_FORCE_DEPOSIT, -1, -1); + + if (!rc) { + fprintf(stderr, "Setting signal value failed!\n"); + fflush(stderr); + } else { + fprintf(stderr, "Setting signal value worked!\n"); + fflush(stderr); + } + return rc-1; } int FliSignalObjHdl::set_signal_value(std::string &value) { + + fprintf(stderr, "Setting signal to %s\n", value.c_str()); return 0; } diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index b276b171..3c9f5b82 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -155,12 +155,6 @@ class FliShutdownCbHdl : public FliCbHdl { - - - - - - class FliRegionObjHdl : public GpiObjHdl { public: FliRegionObjHdl(GpiImplInterface *impl, mtiRegionIdT hdl) : GpiObjHdl(impl), From 10990fab0043b4ece5ca762dfd333687da9edd87 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Mon, 9 Feb 2015 20:50:40 +0000 Subject: [PATCH 0782/2656] Increase debug, implement FliSignalObjHdl::set_signal_value(std::string &value) --- cocotb/log.py | 2 +- lib/fli/FliImpl.cpp | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/cocotb/log.py b/cocotb/log.py index 8cd08fac..4128f3a2 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -68,7 +68,7 @@ def __init__(self, name): self.propagate = False logging.__init__(name) self.addHandler(hdlr) - self.setLevel(logging.INFO) + self.setLevel(logging.DEBUG) """ Need to play with this to get the path of the called back, construct our own makeRecord for this """ diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index ab764767..3b52df57 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -370,7 +370,7 @@ int FliSignalObjHdl::set_signal_value(const int value) { snprintf(buff, 19, "16#%016x", value); - rc = mti_ForceSignal(m_fli_hdl, buff, 0, MTI_FORCE_DEPOSIT, -1, -1); + rc = mti_ForceSignal(m_fli_hdl, &buff[0], 0, MTI_FORCE_DEPOSIT, -1, -1); if (!rc) { fprintf(stderr, "Setting signal value failed!\n"); @@ -384,7 +384,13 @@ int FliSignalObjHdl::set_signal_value(const int value) { int FliSignalObjHdl::set_signal_value(std::string &value) { + int rc; + char buff[128]; + int len = value.copy(buff, value.length()); + buff[len] = '\0'; + fprintf(stderr, "Setting signal to %s\n", value.c_str()); - return 0; + rc = mti_ForceSignal(m_fli_hdl, &buff[0], 0, MTI_FORCE_DEPOSIT, -1, -1); + return rc-1; } From bd7308562b26a0a5cc0155f809f1f59b5583d18b Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 9 Feb 2015 21:09:43 +0000 Subject: [PATCH 0783/2656] Initlalise callback handles correctly for fli implementation --- lib/fli/FliImpl.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index b276b171..838b3d8a 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -88,6 +88,10 @@ class FliSignalCbHdl : public FliProcessCbHdl { virtual ~FliSignalCbHdl() { } int arm_callback(void); + int run_callback(void) { + fprintf(stderr, "Hello\n"); fflush(stderr); + return 0; + } private: mtiSignalIdT m_sig_hdl; @@ -196,9 +200,9 @@ class FliSignalObjHdl : public GpiSignalObjHdl { class FliImpl : public GpiImplInterface { public: FliImpl(const std::string& name) : GpiImplInterface(name), - m_readonly_cbhdl(NULL), - m_nexttime_cbhdl(NULL), - m_readwrite_cbhdl(NULL) { } + m_readonly_cbhdl(this), + m_nexttime_cbhdl(this), + m_readwrite_cbhdl(this) { } /* Sim related */ void sim_end(void); From c5f1232877203aec626296ccca695b06a930d89e Mon Sep 17 00:00:00 2001 From: Chiggs Date: Mon, 9 Feb 2015 21:54:15 +0000 Subject: [PATCH 0784/2656] Add implementation of get_signal_value_binstr --- lib/fli/FliImpl.cpp | 69 +++++++++++++++++++++++++++++++++------------ 1 file changed, 51 insertions(+), 18 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 3b52df57..dadf7651 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -68,7 +68,7 @@ void cocotb_init(void) { // Main re-entry point for callbacks from simulator void handle_fli_callback(void *data) { - fprintf(stderr, "Got a callback\n"); +// fprintf(stderr, "Got a callback\n"); fflush(stderr); FliCbHdl *cb_hdl = (FliCbHdl*)data; @@ -78,18 +78,18 @@ void handle_fli_callback(void *data) gpi_cb_state_e old_state = cb_hdl->get_call_state(); - fprintf(stderr, "FLI: Old state was %d!\n", old_state); - fflush(stderr); +// fprintf(stderr, "FLI: Old state was %d!\n", old_state); +// fflush(stderr); if (old_state == GPI_PRIMED) { cb_hdl->set_call_state(GPI_CALL); - fprintf(stderr, "FLI: Calling run_callback\n"); - fflush(stderr); +// fprintf(stderr, "FLI: Calling run_callback\n"); +// fflush(stderr); cb_hdl->run_callback(); - fprintf(stderr, "FLI: Callback executed\n"); - fflush(stderr); +// fprintf(stderr, "FLI: Callback executed\n"); +// fflush(stderr); gpi_cb_state_e new_state = cb_hdl->get_call_state(); /* We have re-primed in the handler */ @@ -274,6 +274,8 @@ GpiCbHdl *FliImpl::register_nexttime_callback(void) int FliImpl::deregister_callback(GpiCbHdl *gpi_hdl) { +// fprintf(stderr, "Dereigster callback called....\n"); +// fflush(stderr); int rc = gpi_hdl->cleanup_callback(); // TOOD: Don't delete if it's a re-usable doobery // delete(gpi_hdl); @@ -355,15 +357,46 @@ GpiCbHdl *FliSignalObjHdl::value_change_cb(unsigned int edge) { +// TODO: Could cache various settings here which would save some of the calls +// into FLI +static char val_buff[1024]; const char* FliSignalObjHdl::get_signal_value_binstr(void) { - return "010101"; + + + fprintf(stderr, "Getting signal value\n"); + fflush(stderr); + + switch (mti_GetTypeKind(mti_GetSignalType(m_fli_hdl))) { + + case MTI_TYPE_ENUM: + case MTI_TYPE_SCALAR: + case MTI_TYPE_PHYSICAL: + mtiInt32T scalar_val; + scalar_val = mti_GetSignalValue(m_fli_hdl); + snprintf(val_buff, 1, "%d", scalar_val); + break; + case MTI_TYPE_ARRAY: { + mtiInt32T *array_val; + array_val = (mtiInt32T *)mti_GetArraySignalValue(m_fli_hdl, NULL); + int num_elems = mti_TickLength(mti_GetSignalType(m_fli_hdl)); + for (int i = 0; i < num_elems; i++ ) { + snprintf(&val_buff[i], 1, "%d", array_val[i]); + } + mti_VsimFree(array_val); + } break; + default: + LOG_CRITICAL("Signal type %d not currently supported", + mti_GetTypeKind(mti_GetSignalType(m_fli_hdl))); + break; + } + return &val_buff[0]; } int FliSignalObjHdl::set_signal_value(const int value) { - fprintf(stderr, "Setting signal to %d\n", value); - fflush(stderr); +// fprintf(stderr, "Setting signal to %d\n", value); +// fflush(stderr); int rc; char buff[20]; @@ -372,13 +405,13 @@ int FliSignalObjHdl::set_signal_value(const int value) { rc = mti_ForceSignal(m_fli_hdl, &buff[0], 0, MTI_FORCE_DEPOSIT, -1, -1); - if (!rc) { - fprintf(stderr, "Setting signal value failed!\n"); - fflush(stderr); - } else { - fprintf(stderr, "Setting signal value worked!\n"); - fflush(stderr); - } +// if (!rc) { +// fprintf(stderr, "Setting signal value failed!\n"); +// fflush(stderr); +// } else { +// fprintf(stderr, "Setting signal value worked!\n"); +// fflush(stderr); +// } return rc-1; } @@ -389,7 +422,7 @@ int FliSignalObjHdl::set_signal_value(std::string &value) { int len = value.copy(buff, value.length()); buff[len] = '\0'; - fprintf(stderr, "Setting signal to %s\n", value.c_str()); +// fprintf(stderr, "Setting signal to %s\n", value.c_str()); rc = mti_ForceSignal(m_fli_hdl, &buff[0], 0, MTI_FORCE_DEPOSIT, -1, -1); return rc-1; } From f39714b703cbf89e9c27d6b4e6534449543f7171 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Mon, 9 Feb 2015 22:14:59 +0000 Subject: [PATCH 0785/2656] Correct setting of signal values via integer --- lib/fli/FliImpl.cpp | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index dadf7651..ddef9259 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -395,23 +395,16 @@ const char* FliSignalObjHdl::get_signal_value_binstr(void) { int FliSignalObjHdl::set_signal_value(const int value) { -// fprintf(stderr, "Setting signal to %d\n", value); -// fflush(stderr); - int rc; char buff[20]; - snprintf(buff, 19, "16#%016x", value); + snprintf(buff, 20, "16#%016X", value); rc = mti_ForceSignal(m_fli_hdl, &buff[0], 0, MTI_FORCE_DEPOSIT, -1, -1); -// if (!rc) { -// fprintf(stderr, "Setting signal value failed!\n"); -// fflush(stderr); -// } else { -// fprintf(stderr, "Setting signal value worked!\n"); -// fflush(stderr); -// } + if (!rc) { + LOG_CRITICAL("Setting signal value failed!\n"); + } return rc-1; } @@ -422,8 +415,10 @@ int FliSignalObjHdl::set_signal_value(std::string &value) { int len = value.copy(buff, value.length()); buff[len] = '\0'; -// fprintf(stderr, "Setting signal to %s\n", value.c_str()); rc = mti_ForceSignal(m_fli_hdl, &buff[0], 0, MTI_FORCE_DEPOSIT, -1, -1); + if (!rc) { + LOG_CRITICAL("Setting signal value failed!\n"); + } return rc-1; } From da835cb3cd96fd3300f97a2babd5a2cda1f0cb0c Mon Sep 17 00:00:00 2001 From: Chiggs Date: Mon, 9 Feb 2015 22:55:52 +0000 Subject: [PATCH 0786/2656] Correct retrieval of values --- lib/fli/FliImpl.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index ddef9259..c9a3ebf8 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -360,13 +360,10 @@ GpiCbHdl *FliSignalObjHdl::value_change_cb(unsigned int edge) { // TODO: Could cache various settings here which would save some of the calls // into FLI static char val_buff[1024]; +static const char value_enum[10] = "UX01ZWLH-"; const char* FliSignalObjHdl::get_signal_value_binstr(void) { - - fprintf(stderr, "Getting signal value\n"); - fflush(stderr); - switch (mti_GetTypeKind(mti_GetSignalType(m_fli_hdl))) { case MTI_TYPE_ENUM: @@ -374,22 +371,27 @@ const char* FliSignalObjHdl::get_signal_value_binstr(void) { case MTI_TYPE_PHYSICAL: mtiInt32T scalar_val; scalar_val = mti_GetSignalValue(m_fli_hdl); - snprintf(val_buff, 1, "%d", scalar_val); + val_buff[0] = value_enum[scalar_val]; + val_buff[1] = '\0'; break; case MTI_TYPE_ARRAY: { mtiInt32T *array_val; array_val = (mtiInt32T *)mti_GetArraySignalValue(m_fli_hdl, NULL); int num_elems = mti_TickLength(mti_GetSignalType(m_fli_hdl)); for (int i = 0; i < num_elems; i++ ) { - snprintf(&val_buff[i], 1, "%d", array_val[i]); + val_buff[i] = value_enum[array_val[i]]; } + val_buff[num_elems] = '\0'; mti_VsimFree(array_val); } break; default: - LOG_CRITICAL("Signal type %d not currently supported", - mti_GetTypeKind(mti_GetSignalType(m_fli_hdl))); + LOG_CRITICAL("Signal %s type %d not currently supported", + m_name.c_str(), mti_GetTypeKind(mti_GetSignalType(m_fli_hdl))); break; } + + LOG_DEBUG("Retrieved \"%s\" for signal %s", &val_buff, m_name.c_str()); + return &val_buff[0]; } From b3acb14558ab37757a05f45890ea2741986ba579 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 9 Feb 2015 23:06:43 +0000 Subject: [PATCH 0787/2656] Get handle from parent object --- lib/fli/FliImpl.cpp | 52 ++++++++++++++++++++++++++++------------- lib/fli/FliImpl.h | 56 ++++++++++++++++++++++----------------------- 2 files changed, 64 insertions(+), 44 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index ddef9259..c64e372e 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -68,7 +68,6 @@ void cocotb_init(void) { // Main re-entry point for callbacks from simulator void handle_fli_callback(void *data) { -// fprintf(stderr, "Got a callback\n"); fflush(stderr); FliCbHdl *cb_hdl = (FliCbHdl*)data; @@ -78,18 +77,18 @@ void handle_fli_callback(void *data) gpi_cb_state_e old_state = cb_hdl->get_call_state(); -// fprintf(stderr, "FLI: Old state was %d!\n", old_state); -// fflush(stderr); +// fprintf(stderr, "FLI: Old state was %d!\n", old_state); +// fflush(stderr); if (old_state == GPI_PRIMED) { cb_hdl->set_call_state(GPI_CALL); -// fprintf(stderr, "FLI: Calling run_callback\n"); -// fflush(stderr); +// fprintf(stderr, "FLI: Calling run_callback\n"); +// fflush(stderr); cb_hdl->run_callback(); -// fprintf(stderr, "FLI: Callback executed\n"); -// fflush(stderr); +// fprintf(stderr, "FLI: Callback executed\n"); +// fflush(stderr); gpi_cb_state_e new_state = cb_hdl->get_call_state(); /* We have re-primed in the handler */ @@ -322,7 +321,7 @@ int FliSignalCbHdl::arm_callback(void) { mti_Sensitize(m_proc_hdl, m_sig_hdl, MTI_EVENT); m_sensitised = true; - m_state = GPI_PRIMED; + GpiValueCbHdl::m_state = GPI_PRIMED; return 0; } @@ -339,24 +338,43 @@ int FliSimPhaseCbHdl::arm_callback(void) { return 0; } -GPI_ENTRY_POINT(fli, cocotb_init); - +FliSignalCbHdl::FliSignalCbHdl(GpiImplInterface *impl, + FliSignalObjHdl *sig_hdl, + unsigned int edge) : GpiCbHdl(impl), + FliProcessCbHdl(impl), + GpiValueCbHdl(impl, sig_hdl, edge) +{ + m_sig_hdl = m_signal->get_handle(); +} GpiCbHdl *FliSignalObjHdl::value_change_cb(unsigned int edge) { LOG_INFO("Creating value change callback for %s", m_name.c_str()); - if (NULL == m_cb_hdl) { - m_cb_hdl = new FliSignalCbHdl(m_impl, m_fli_hdl); + FliSignalCbHdl *cb = NULL; + + switch (edge) { + case 1: + cb = &m_rising_cb; + break; + case 2: + cb = &m_falling_cb; + break; + case 3: + cb = &m_either_cb; + break; + default: + return NULL; + } + + if (cb->arm_callback()) { + return NULL; } - m_cb_hdl->arm_callback(); - return m_cb_hdl; + return (GpiValueCbHdl*)cb; } - - // TODO: Could cache various settings here which would save some of the calls // into FLI static char val_buff[1024]; @@ -422,3 +440,5 @@ int FliSignalObjHdl::set_signal_value(std::string &value) { return rc-1; } +GPI_ENTRY_POINT(fli, cocotb_init); + diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index fcc4c100..1d104c7f 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -80,23 +80,44 @@ class FliProcessCbHdl : public FliCbHdl { bool m_sensitised; }; +class FliSignalObjHdl; + // One class of callbacks uses mti_Sensitize to react to a signal -class FliSignalCbHdl : public FliProcessCbHdl { +class FliSignalCbHdl : public FliProcessCbHdl, public GpiValueCbHdl { public: - FliSignalCbHdl(GpiImplInterface *impl, mtiSignalIdT sig_hdl) : FliProcessCbHdl(impl), m_sig_hdl(sig_hdl) { } - virtual ~FliSignalCbHdl() { } + FliSignalCbHdl(GpiImplInterface *impl, + FliSignalObjHdl *sig_hdl, + unsigned int edge); + virtual ~FliSignalCbHdl() { } int arm_callback(void); - int run_callback(void) { - fprintf(stderr, "Hello\n"); fflush(stderr); - return 0; - } + int cleanup_callback(void) { fprintf(stderr, "Things\n"); fflush(stderr); return 0; } private: mtiSignalIdT m_sig_hdl; }; +class FliSignalObjHdl : public GpiSignalObjHdl { +public: + FliSignalObjHdl(GpiImplInterface *impl, mtiSignalIdT hdl) : GpiSignalObjHdl(impl, hdl), + m_fli_hdl(hdl), + m_rising_cb(impl, this, GPI_RISING), + m_falling_cb(impl, this, GPI_FALLING), + m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } + virtual ~FliSignalObjHdl() { } + + const char* get_signal_value_binstr(void); + int set_signal_value(const int value); + int set_signal_value(std::string &value); + GpiCbHdl *value_change_cb(unsigned int edge); +protected: + mtiSignalIdT m_fli_hdl; + FliSignalCbHdl m_rising_cb; + FliSignalCbHdl m_falling_cb; + FliSignalCbHdl m_either_cb; +}; + // All other callbacks are related to the simulation phasing class FliSimPhaseCbHdl : public FliProcessCbHdl { @@ -170,27 +191,6 @@ class FliRegionObjHdl : public GpiObjHdl { }; -class FliSignalObjHdl : public GpiSignalObjHdl { -public: - FliSignalObjHdl(GpiImplInterface *impl, mtiSignalIdT hdl) : GpiSignalObjHdl(impl, hdl), - m_fli_hdl(hdl), - m_cb_hdl(NULL) { } - virtual ~FliSignalObjHdl() { } - - const char* get_signal_value_binstr(void); - int set_signal_value(const int value); - int set_signal_value(std::string &value); - GpiCbHdl *value_change_cb(unsigned int edge); -protected: - mtiSignalIdT m_fli_hdl; - FliSignalCbHdl *m_cb_hdl; -}; - - - - - - class FliImpl : public GpiImplInterface { public: FliImpl(const std::string& name) : GpiImplInterface(name), From 01cce3704a1149501fb8ffd9235c335c3246835c Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 10 Feb 2015 08:57:29 +0000 Subject: [PATCH 0788/2656] Fix Issue of multiple inheritance for FliSignalCbHdl, set FliCbHdl as virtual so there is only one instance of GpiCbHdl in the hiarachy --- lib/fli/FliImpl.cpp | 18 +++++++++++------- lib/fli/FliImpl.h | 29 ++++++++++++++++++++--------- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 0d63a224..ebbae2f8 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -77,8 +77,8 @@ void handle_fli_callback(void *data) gpi_cb_state_e old_state = cb_hdl->get_call_state(); -// fprintf(stderr, "FLI: Old state was %d!\n", old_state); -// fflush(stderr); + fprintf(stderr, "FLI: Old state was %d at %p!\n", old_state, cb_hdl); + fflush(stderr); if (old_state == GPI_PRIMED) { @@ -319,9 +319,12 @@ int FliSignalCbHdl::arm_callback(void) { m_proc_hdl = mti_CreateProcess(NULL, handle_fli_callback, (void *)this); } + fprintf(stderr, "Just armed %p\n", this); + mti_Sensitize(m_proc_hdl, m_sig_hdl, MTI_EVENT); m_sensitised = true; - GpiValueCbHdl::m_state = GPI_PRIMED; + set_call_state(GPI_PRIMED); + //GpiValueCbHdl::m_state = GPI_PRIMED; return 0; } @@ -339,10 +342,11 @@ int FliSimPhaseCbHdl::arm_callback(void) { } FliSignalCbHdl::FliSignalCbHdl(GpiImplInterface *impl, - FliSignalObjHdl *sig_hdl, - unsigned int edge) : GpiCbHdl(impl), - FliProcessCbHdl(impl), - GpiValueCbHdl(impl, sig_hdl, edge) + FliSignalObjHdl *sig_hdl, + unsigned int edge) : GpiCbHdl(impl), + FliCbHdl(impl), + FliProcessCbHdl(impl), + GpiValueCbHdl(impl, sig_hdl, edge) { m_sig_hdl = m_signal->get_handle(); } diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index 1d104c7f..233a092d 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -47,7 +47,7 @@ class GpiObjHdl : public GpiObjHdl { }; #endif -class FliCbHdl : public GpiCbHdl { +class FliCbHdl : public virtual GpiCbHdl { public: FliCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl) { } virtual ~FliCbHdl() { } @@ -66,9 +66,10 @@ class FliCbHdl : public GpiCbHdl { // In FLI some callbacks require us to register a process // We use a subclass to track the process state related to the callback -class FliProcessCbHdl : public FliCbHdl { +class FliProcessCbHdl : public virtual FliCbHdl { public: - FliProcessCbHdl(GpiImplInterface *impl) : FliCbHdl(impl), + FliProcessCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + FliCbHdl(impl), m_proc_hdl(NULL) { } virtual ~FliProcessCbHdl() { } @@ -123,7 +124,9 @@ class FliSignalObjHdl : public GpiSignalObjHdl { class FliSimPhaseCbHdl : public FliProcessCbHdl { public: - FliSimPhaseCbHdl(GpiImplInterface *impl, mtiProcessPriorityT priority) : FliProcessCbHdl(impl), + FliSimPhaseCbHdl(GpiImplInterface *impl, mtiProcessPriorityT priority) : GpiCbHdl(impl), + FliCbHdl(impl), + FliProcessCbHdl(impl), m_priority(priority) { } virtual ~FliSimPhaseCbHdl() { } @@ -136,18 +139,24 @@ class FliSimPhaseCbHdl : public FliProcessCbHdl { // FIXME templates? class FliReadWriteCbHdl : public FliSimPhaseCbHdl { public: - FliReadWriteCbHdl(GpiImplInterface *impl) : FliSimPhaseCbHdl(impl, MTI_PROC_SYNCH) { } + FliReadWriteCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + FliCbHdl(impl), + FliSimPhaseCbHdl(impl, MTI_PROC_SYNCH) { } virtual ~FliReadWriteCbHdl() { } }; class FliNextPhaseCbHdl : public FliSimPhaseCbHdl { public: - FliNextPhaseCbHdl(GpiImplInterface *impl) : FliSimPhaseCbHdl(impl, MTI_PROC_IMMEDIATE) { } + FliNextPhaseCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + FliCbHdl(impl), + FliSimPhaseCbHdl(impl, MTI_PROC_IMMEDIATE) { } virtual ~FliNextPhaseCbHdl() { } }; class FliReadOnlyCbHdl : public FliSimPhaseCbHdl { public: - FliReadOnlyCbHdl(GpiImplInterface *impl) : FliSimPhaseCbHdl(impl, MTI_PROC_POSTPONED) { } + FliReadOnlyCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + FliCbHdl(impl), + FliSimPhaseCbHdl(impl, MTI_PROC_POSTPONED) { } virtual ~FliReadOnlyCbHdl() { } }; @@ -155,7 +164,9 @@ class FliReadOnlyCbHdl : public FliSimPhaseCbHdl { class FliTimedCbHdl : public FliProcessCbHdl { public: - FliTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : FliProcessCbHdl(impl), m_time_ps(time_ps) {}; + FliTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : GpiCbHdl(impl), + FliCbHdl(impl), + FliProcessCbHdl(impl), m_time_ps(time_ps) {}; virtual ~FliTimedCbHdl() { } int arm_callback(void); private: @@ -165,7 +176,7 @@ class FliTimedCbHdl : public FliProcessCbHdl { class FliShutdownCbHdl : public FliCbHdl { public: - FliShutdownCbHdl(GpiImplInterface *impl) : FliCbHdl(impl) { } + FliShutdownCbHdl(GpiImplInterface *impl) : GpiCbHdl*impl), FliCbHdl(impl) { } int run_callback(void); int arm_callback(void); virtual ~FliShutdownCbHdl() { } From cfc4ef49ea79a749c7e765f1fb7b92dcb2401a3e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 10 Feb 2015 09:29:44 +0000 Subject: [PATCH 0789/2656] Access an MTI_TYPE_ARRAY correctly if the reported length is less that 256, nice simple interface this --- cocotb/log.py | 2 +- lib/fli/FliImpl.cpp | 19 +++++++++++++------ lib/fli/FliImpl.h | 4 ++-- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/cocotb/log.py b/cocotb/log.py index 4128f3a2..8cd08fac 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -68,7 +68,7 @@ def __init__(self, name): self.propagate = False logging.__init__(name) self.addHandler(hdlr) - self.setLevel(logging.DEBUG) + self.setLevel(logging.INFO) """ Need to play with this to get the path of the called back, construct our own makeRecord for this """ diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index ebbae2f8..4cbc92b9 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -77,8 +77,8 @@ void handle_fli_callback(void *data) gpi_cb_state_e old_state = cb_hdl->get_call_state(); - fprintf(stderr, "FLI: Old state was %d at %p!\n", old_state, cb_hdl); - fflush(stderr); +// fprintf(stderr, "FLI: Old state was %d at %p!\n", old_state, cb_hdl); +// fflush(stderr); if (old_state == GPI_PRIMED) { @@ -319,7 +319,7 @@ int FliSignalCbHdl::arm_callback(void) { m_proc_hdl = mti_CreateProcess(NULL, handle_fli_callback, (void *)this); } - fprintf(stderr, "Just armed %p\n", this); + //fprintf(stderr, "Just armed %p\n", this); mti_Sensitize(m_proc_hdl, m_sig_hdl, MTI_EVENT); m_sensitised = true; @@ -400,8 +400,15 @@ const char* FliSignalObjHdl::get_signal_value_binstr(void) { mtiInt32T *array_val; array_val = (mtiInt32T *)mti_GetArraySignalValue(m_fli_hdl, NULL); int num_elems = mti_TickLength(mti_GetSignalType(m_fli_hdl)); - for (int i = 0; i < num_elems; i++ ) { - val_buff[i] = value_enum[array_val[i]]; + if (num_elems <= 256) { + char *iter = (char*)array_val; + for (int i = 0; i < num_elems; i++ ) { + val_buff[i] = value_enum[(int)iter[i]]; + } + } else { + for (int i = 0; i < num_elems; i++ ) { + val_buff[i] = value_enum[array_val[i]]; + } } val_buff[num_elems] = '\0'; mti_VsimFree(array_val); @@ -412,7 +419,7 @@ const char* FliSignalObjHdl::get_signal_value_binstr(void) { break; } - LOG_DEBUG("Retrieved \"%s\" for signal %s", &val_buff, m_name.c_str()); + LOG_INFO("Retrieved \"%s\" for signal %s", &val_buff, m_name.c_str()); return &val_buff[0]; } diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index 233a092d..a6a0dc19 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -93,7 +93,7 @@ class FliSignalCbHdl : public FliProcessCbHdl, public GpiValueCbHdl { virtual ~FliSignalCbHdl() { } int arm_callback(void); - int cleanup_callback(void) { fprintf(stderr, "Things\n"); fflush(stderr); return 0; } + int cleanup_callback(void) { return FliProcessCbHdl::cleanup_callback(); } private: mtiSignalIdT m_sig_hdl; @@ -176,7 +176,7 @@ class FliTimedCbHdl : public FliProcessCbHdl { class FliShutdownCbHdl : public FliCbHdl { public: - FliShutdownCbHdl(GpiImplInterface *impl) : GpiCbHdl*impl), FliCbHdl(impl) { } + FliShutdownCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), FliCbHdl(impl) { } int run_callback(void); int arm_callback(void); virtual ~FliShutdownCbHdl() { } From fa6f01c3aa3ebedf74de7277bc7494f5726c7911 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Tue, 10 Feb 2015 20:55:33 +1100 Subject: [PATCH 0790/2656] Fix PYTHONPATH in case it is a path list when running under msys --- makefiles/simulators/Makefile.aldec | 11 ++++++++--- makefiles/simulators/Makefile.icarus | 16 ++++++++++++++-- makefiles/simulators/Makefile.modelsim | 10 ++++++++-- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index c12e499c..8405e8b7 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -106,13 +106,16 @@ EXTRA_LIBS := -laldecpli EXTRA_LIBDIRS := -L$(ALDEC_PATH)/bin/ OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') LIB_LOAD := PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) -PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' | sed 's/\([a-zA-Z]\)\(:\)\//\/\1\//g' | sed 's/;/:/g') -else +OLD_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' | sed 's/\([a-zA-Z]\)\(:\)\//\/\1\//g' | sed 's/;/:/g') + +results.xml: $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(COCOTB_VPI_LIB) $(CUSTOM_SIM_DEPS) + cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(OLD_PYTHONPATH) \ + $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do runsim.tcl | tee sim.log +else EXTRA_LIBS = -laldecpli EXTRA_LIBDIRS = -L$(ALDEC_PATH)/bin/Linux64 LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) -endif # Note it's the redirection of the output rather than the 'do' command # that turns on batch mode (i.e. exit on completion/error) @@ -120,6 +123,8 @@ results.xml: $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(COCOTB_ cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do runsim.tcl | tee sim.log +endif + clean:: rm -rf $(SIM_BUILD) diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index f3322630..1f1c829a 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -44,10 +44,20 @@ EXTRA_LIBS := -lvpi EXTRA_LIBDIRS := -L$(ICARUS_PATH)/lib OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') LIB_LOAD := PATH=$(OLD_PATH):$(LIB_DIR) -PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' | sed 's/\([a-zA-Z]\)\(:\)\//\/\1\//g' | sed 's/;/:/g') +OLD_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' | sed 's/\([a-zA-Z]\)\(:\)\//\/\1\//g' | sed 's/;/:/g') + +results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(OLD_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) + +debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(OLD_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + gdb --args vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) + else LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) -endif results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ @@ -59,5 +69,7 @@ debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ gdb --args vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) +endif + clean:: -@rm -rf $(SIM_BUILD) diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index a09cf1e0..ccd12965 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -86,18 +86,24 @@ EXTRA_LIBDIRS := -L$(MODELSIM_BIN_DIR) OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') LIB_LOAD := PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) -PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' | sed 's/\([a-zA-Z]\)\(:\)\//\/\1\//g' | sed 's/;/:/g') +OLD_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' | sed 's/\([a-zA-Z]\)\(:\)\//\/\1\//g' | sed 's/;/:/g') + +results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(COCOTB_VPI_LIB) + cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(OLD_PYTHONPATH) \ + $(SIM_CMD) -do runsim.do 2>&1 | tee sim.log else export MODELSIM_BIN_DIR = $(shell which vsim | sed 's|/vsim||') LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) -endif results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(COCOTB_VPI_LIB) cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ $(SIM_CMD) -do runsim.do 2>&1 | tee sim.log +endif + clean:: -rm -rf $(SIM_BUILD) From 5a5ff4d1927359a8e37167f521f46449d54e51d1 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 10 Feb 2015 20:43:12 +0000 Subject: [PATCH 0791/2656] Issue #141: Just formatting changes --- lib/fli/FliImpl.cpp | 59 ++++++++++++++++----------------------------- lib/fli/FliImpl.h | 49 +++++++++---------------------------- 2 files changed, 32 insertions(+), 76 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 4cbc92b9..f32cf465 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -36,7 +36,8 @@ extern "C" { static FliImpl *fli_table; -void fli_elab_cb(void *nothing) { +void fli_elab_cb(void *nothing) +{ LOG_INFO("fli_elab_cb called\n"); fli_table = new FliImpl("FLI"); @@ -58,7 +59,8 @@ void fli_elab_cb(void *nothing) { gpi_embed_init(&sim_info); } -void cocotb_init(void) { +void cocotb_init(void) +{ LOG_INFO("cocotb_init called\n"); mti_AddLoadDoneCB(fli_elab_cb, NULL); } @@ -163,27 +165,6 @@ void FliImpl::get_sim_time(uint32_t *high, uint32_t *low) *low = mti_Now(); } -#if 0 -/** - * @name Find the root handle - * @brief Find the root handle using an optional name - * - * Get a handle to the root simulator object. This is usually the toplevel. - * - * If no name is provided, we return the first root instance. - * - * If name is provided, we check the name against the available objects until - * we find a match. If no match is found we return NULL - */ -int FliObjHdl::initialise(std::string &name) { - m_name = name; - m_type = "unknown"; - - return 0; -} -#endif - - /** * @name Find the root handle * @brief Find the root handle using an optional name @@ -219,7 +200,7 @@ GpiObjHdl *FliImpl::get_root_handle(const char *name) LOG_INFO("Returning root handle %p", rv); return rv; - error: +error: LOG_CRITICAL("FLI: Couldn't find root handle %s", name); @@ -282,9 +263,6 @@ int FliImpl::deregister_callback(GpiCbHdl *gpi_hdl) } - - - /** * @name cleanup callback * @brief Called while unwinding after a GPI callback @@ -294,7 +272,8 @@ int FliImpl::deregister_callback(GpiCbHdl *gpi_hdl) * NB need a way to determine if should leave it sensitised, hmmm... * */ -int FliProcessCbHdl::cleanup_callback(void) { +int FliProcessCbHdl::cleanup_callback(void) +{ if (m_sensitised) mti_Desensitize(m_proc_hdl); @@ -302,7 +281,8 @@ int FliProcessCbHdl::cleanup_callback(void) { return 0; } -int FliTimedCbHdl::arm_callback(void) { +int FliTimedCbHdl::arm_callback(void) +{ LOG_INFO("Creating a new process to sensitise with timer"); m_proc_hdl = mti_CreateProcessWithPriority(NULL, handle_fli_callback, (void *)this, MTI_PROC_IMMEDIATE); mti_ScheduleWakeup(m_proc_hdl, m_time_ps); @@ -312,7 +292,8 @@ int FliTimedCbHdl::arm_callback(void) { return 0; } -int FliSignalCbHdl::arm_callback(void) { +int FliSignalCbHdl::arm_callback(void) +{ if (NULL == m_proc_hdl) { LOG_INFO("Creating a new process to sensitise to signal %s", mti_GetSignalName(m_sig_hdl)); @@ -328,7 +309,8 @@ int FliSignalCbHdl::arm_callback(void) { return 0; } -int FliSimPhaseCbHdl::arm_callback(void) { +int FliSimPhaseCbHdl::arm_callback(void) +{ if (NULL == m_proc_hdl) { LOG_INFO("Creating a new process to sensitise with priority %d", m_priority); @@ -352,7 +334,8 @@ FliSignalCbHdl::FliSignalCbHdl(GpiImplInterface *impl, } -GpiCbHdl *FliSignalObjHdl::value_change_cb(unsigned int edge) { +GpiCbHdl *FliSignalObjHdl::value_change_cb(unsigned int edge) +{ LOG_INFO("Creating value change callback for %s", m_name.c_str()); @@ -384,8 +367,8 @@ GpiCbHdl *FliSignalObjHdl::value_change_cb(unsigned int edge) { static char val_buff[1024]; static const char value_enum[10] = "UX01ZWLH-"; -const char* FliSignalObjHdl::get_signal_value_binstr(void) { - +const char* FliSignalObjHdl::get_signal_value_binstr(void) +{ switch (mti_GetTypeKind(mti_GetSignalType(m_fli_hdl))) { case MTI_TYPE_ENUM: @@ -424,8 +407,8 @@ const char* FliSignalObjHdl::get_signal_value_binstr(void) { return &val_buff[0]; } -int FliSignalObjHdl::set_signal_value(const int value) { - +int FliSignalObjHdl::set_signal_value(const int value) +{ int rc; char buff[20]; @@ -439,8 +422,8 @@ int FliSignalObjHdl::set_signal_value(const int value) { return rc-1; } -int FliSignalObjHdl::set_signal_value(std::string &value) { - +int FliSignalObjHdl::set_signal_value(std::string &value) +{ int rc; char buff[128]; int len = value.copy(buff, value.length()); diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index a6a0dc19..7521b805 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -25,28 +25,12 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ -#ifndef COCOTB_FLI_IMPL_H_ -#define COCOTB_FLI_IMPL_H_ +#ifndef COCOTB_FLI_IMPL_H_ +#define COCOTB_FLI_IMPL_H_ #include "../gpi/gpi_priv.h" #include "mti.h" -// FLI versions of base types -#if 0 -class GpiObjHdl : public GpiObjHdl { -public: - GpiObjHdl(GpiImplInterface *impl) : GpiObjHdl(impl) { } - virtual ~GpiObjHdl() { } - - GpiObjHdl *get_handle_by_name(std::string &name) {return NULL; }; - GpiObjHdl *get_handle_by_index(uint32_t index) {return NULL; } ; - GpiIterator *iterate_handle(uint32_t type) { return NULL; } - GpiObjHdl *next_handle(GpiIterator *iterator) { return NULL; } - - int initialise(std::string &name); -}; -#endif - class FliCbHdl : public virtual GpiCbHdl { public: FliCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl) { } @@ -59,9 +43,6 @@ class FliCbHdl : public virtual GpiCbHdl { int register_cb(p_cb_data cb_data); }; - - - // Callback handles // In FLI some callbacks require us to register a process @@ -82,7 +63,6 @@ class FliProcessCbHdl : public virtual FliCbHdl { }; class FliSignalObjHdl; - // One class of callbacks uses mti_Sensitize to react to a signal class FliSignalCbHdl : public FliProcessCbHdl, public GpiValueCbHdl { @@ -93,7 +73,9 @@ class FliSignalCbHdl : public FliProcessCbHdl, public GpiValueCbHdl { virtual ~FliSignalCbHdl() { } int arm_callback(void); - int cleanup_callback(void) { return FliProcessCbHdl::cleanup_callback(); } + int cleanup_callback(void) { + return FliProcessCbHdl::cleanup_callback(); + } private: mtiSignalIdT m_sig_hdl; @@ -112,11 +94,12 @@ class FliSignalObjHdl : public GpiSignalObjHdl { int set_signal_value(const int value); int set_signal_value(std::string &value); GpiCbHdl *value_change_cb(unsigned int edge); + protected: - mtiSignalIdT m_fli_hdl; - FliSignalCbHdl m_rising_cb; - FliSignalCbHdl m_falling_cb; - FliSignalCbHdl m_either_cb; + mtiSignalIdT m_fli_hdl; + FliSignalCbHdl m_rising_cb; + FliSignalCbHdl m_falling_cb; + FliSignalCbHdl m_either_cb; }; @@ -160,8 +143,6 @@ class FliReadOnlyCbHdl : public FliSimPhaseCbHdl { virtual ~FliReadOnlyCbHdl() { } }; - - class FliTimedCbHdl : public FliProcessCbHdl { public: FliTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : GpiCbHdl(impl), @@ -183,14 +164,6 @@ class FliShutdownCbHdl : public FliCbHdl { }; - - - - - - - - class FliRegionObjHdl : public GpiObjHdl { public: FliRegionObjHdl(GpiImplInterface *impl, mtiRegionIdT hdl) : GpiObjHdl(impl), @@ -236,4 +209,4 @@ class FliImpl : public GpiImplInterface { }; -#endif /*COCOTB_FLI_IMPL_H_ */ +#endif /*COCOTB_FLI_IMPL_H_ */ From b0bd0c3ac56d3394ac7af2516f0d7b9a5a6d1a82 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 10 Feb 2015 21:03:48 +0000 Subject: [PATCH 0792/2656] Issue #141: Cleanup inheritance FliCbhdl not needed --- lib/fli/FliImpl.cpp | 7 +++---- lib/fli/FliImpl.h | 14 +++++--------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index f32cf465..7de6fa55 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -72,15 +72,15 @@ void handle_fli_callback(void *data) { fflush(stderr); - FliCbHdl *cb_hdl = (FliCbHdl*)data; + FliProcessCbHdl *cb_hdl = (FliProcessCbHdl*)data; if (!cb_hdl) LOG_CRITICAL("FLI: Callback data corrupted"); gpi_cb_state_e old_state = cb_hdl->get_call_state(); -// fprintf(stderr, "FLI: Old state was %d at %p!\n", old_state, cb_hdl); -// fflush(stderr); + //fprintf(stderr, "FLI: Old state was %d at %p!\n", old_state, cb_hdl); + //fflush(stderr); if (old_state == GPI_PRIMED) { @@ -326,7 +326,6 @@ int FliSimPhaseCbHdl::arm_callback(void) FliSignalCbHdl::FliSignalCbHdl(GpiImplInterface *impl, FliSignalObjHdl *sig_hdl, unsigned int edge) : GpiCbHdl(impl), - FliCbHdl(impl), FliProcessCbHdl(impl), GpiValueCbHdl(impl, sig_hdl, edge) { diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index 7521b805..2e254e96 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -31,6 +31,7 @@ #include "../gpi/gpi_priv.h" #include "mti.h" +#if 0 class FliCbHdl : public virtual GpiCbHdl { public: FliCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl) { } @@ -42,15 +43,15 @@ class FliCbHdl : public virtual GpiCbHdl { protected: int register_cb(p_cb_data cb_data); }; +#endif // Callback handles // In FLI some callbacks require us to register a process // We use a subclass to track the process state related to the callback -class FliProcessCbHdl : public virtual FliCbHdl { +class FliProcessCbHdl : public virtual GpiCbHdl { public: FliProcessCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - FliCbHdl(impl), m_proc_hdl(NULL) { } virtual ~FliProcessCbHdl() { } @@ -108,7 +109,6 @@ class FliSimPhaseCbHdl : public FliProcessCbHdl { public: FliSimPhaseCbHdl(GpiImplInterface *impl, mtiProcessPriorityT priority) : GpiCbHdl(impl), - FliCbHdl(impl), FliProcessCbHdl(impl), m_priority(priority) { } virtual ~FliSimPhaseCbHdl() { } @@ -123,7 +123,6 @@ class FliSimPhaseCbHdl : public FliProcessCbHdl { class FliReadWriteCbHdl : public FliSimPhaseCbHdl { public: FliReadWriteCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - FliCbHdl(impl), FliSimPhaseCbHdl(impl, MTI_PROC_SYNCH) { } virtual ~FliReadWriteCbHdl() { } }; @@ -131,14 +130,12 @@ class FliReadWriteCbHdl : public FliSimPhaseCbHdl { class FliNextPhaseCbHdl : public FliSimPhaseCbHdl { public: FliNextPhaseCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - FliCbHdl(impl), FliSimPhaseCbHdl(impl, MTI_PROC_IMMEDIATE) { } virtual ~FliNextPhaseCbHdl() { } }; class FliReadOnlyCbHdl : public FliSimPhaseCbHdl { public: FliReadOnlyCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - FliCbHdl(impl), FliSimPhaseCbHdl(impl, MTI_PROC_POSTPONED) { } virtual ~FliReadOnlyCbHdl() { } }; @@ -146,7 +143,6 @@ class FliReadOnlyCbHdl : public FliSimPhaseCbHdl { class FliTimedCbHdl : public FliProcessCbHdl { public: FliTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : GpiCbHdl(impl), - FliCbHdl(impl), FliProcessCbHdl(impl), m_time_ps(time_ps) {}; virtual ~FliTimedCbHdl() { } int arm_callback(void); @@ -155,9 +151,9 @@ class FliTimedCbHdl : public FliProcessCbHdl { }; -class FliShutdownCbHdl : public FliCbHdl { +class FliShutdownCbHdl : public GpiCbHdl { public: - FliShutdownCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), FliCbHdl(impl) { } + FliShutdownCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl) { } int run_callback(void); int arm_callback(void); virtual ~FliShutdownCbHdl() { } From d38f74567cd4287d915803099ae400410c385c86 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 10 Feb 2015 21:15:55 +0000 Subject: [PATCH 0793/2656] Issue #141: Fixup log levels to match other layers --- lib/fli/FliImpl.cpp | 41 +++++++++++++---------------------------- lib/fli/FliImpl.h | 14 -------------- 2 files changed, 13 insertions(+), 42 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 7de6fa55..5899617e 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -79,18 +79,11 @@ void handle_fli_callback(void *data) gpi_cb_state_e old_state = cb_hdl->get_call_state(); - //fprintf(stderr, "FLI: Old state was %d at %p!\n", old_state, cb_hdl); - //fflush(stderr); - if (old_state == GPI_PRIMED) { cb_hdl->set_call_state(GPI_CALL); -// fprintf(stderr, "FLI: Calling run_callback\n"); -// fflush(stderr); cb_hdl->run_callback(); -// fprintf(stderr, "FLI: Callback executed\n"); -// fflush(stderr); gpi_cb_state_e new_state = cb_hdl->get_call_state(); /* We have re-primed in the handler */ @@ -114,7 +107,7 @@ void FliImpl::sim_end(void) */ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) { - LOG_INFO("Looking for child %s from %s", name.c_str(), parent->get_name_str()); + LOG_DEBUG("Looking for child %s from %s", name.c_str(), parent->get_name_str()); GpiObjHdl *new_obj = NULL; @@ -124,12 +117,12 @@ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) mtiSignalIdT sig_hdl; sig_hdl = mti_FindSignal(&writable[0]); if (sig_hdl) { - LOG_INFO("Found a signal %s -> %p", &writable[0], sig_hdl); + LOG_DEBUG("Found a signal %s -> %p", &writable[0], sig_hdl); new_obj = new FliSignalObjHdl(this, sig_hdl); } if (NULL == new_obj) { - LOG_WARN("Didn't find anything named %s", &writable[0]); + LOG_DEBUG("Didn't find anything named %s", &writable[0]); return NULL; } @@ -183,7 +176,7 @@ GpiObjHdl *FliImpl::get_root_handle(const char *name) std::string root_name = name; for (root = mti_GetTopRegion(); root != NULL; root = mti_NextRegion(root)) { - LOG_INFO("Iterating over: %s", mti_GetRegionName(root)); + LOG_DEBUG("Iterating over: %s", mti_GetRegionName(root)); if (name == NULL || !strcmp(name, mti_GetRegionName(root))) break; } @@ -192,12 +185,12 @@ GpiObjHdl *FliImpl::get_root_handle(const char *name) goto error; } - LOG_INFO("Found toplevel: %s, creating handle....", name); + LOG_DEBUG("Found toplevel: %s, creating handle....", name); rv = new FliRegionObjHdl(this, root); rv->initialise(root_name); - LOG_INFO("Returning root handle %p", rv); + LOG_DEBUG("Returning root handle %p", rv); return rv; error: @@ -254,8 +247,6 @@ GpiCbHdl *FliImpl::register_nexttime_callback(void) int FliImpl::deregister_callback(GpiCbHdl *gpi_hdl) { -// fprintf(stderr, "Dereigster callback called....\n"); -// fflush(stderr); int rc = gpi_hdl->cleanup_callback(); // TOOD: Don't delete if it's a re-usable doobery // delete(gpi_hdl); @@ -283,12 +274,12 @@ int FliProcessCbHdl::cleanup_callback(void) int FliTimedCbHdl::arm_callback(void) { - LOG_INFO("Creating a new process to sensitise with timer"); + LOG_DEBUG("Creating a new process to sensitise with timer"); m_proc_hdl = mti_CreateProcessWithPriority(NULL, handle_fli_callback, (void *)this, MTI_PROC_IMMEDIATE); mti_ScheduleWakeup(m_proc_hdl, m_time_ps); - LOG_INFO("Wakeup scheduled on %p for %llu", m_proc_hdl, m_time_ps); + LOG_DEBUG("Wakeup scheduled on %p for %llu", m_proc_hdl, m_time_ps); m_sensitised = true; - m_state = GPI_PRIMED; + set_call_state(GPI_PRIMED); return 0; } @@ -296,16 +287,13 @@ int FliSignalCbHdl::arm_callback(void) { if (NULL == m_proc_hdl) { - LOG_INFO("Creating a new process to sensitise to signal %s", mti_GetSignalName(m_sig_hdl)); + LOG_DEBUG("Creating a new process to sensitise to signal %s", mti_GetSignalName(m_sig_hdl)); m_proc_hdl = mti_CreateProcess(NULL, handle_fli_callback, (void *)this); } - //fprintf(stderr, "Just armed %p\n", this); - mti_Sensitize(m_proc_hdl, m_sig_hdl, MTI_EVENT); m_sensitised = true; set_call_state(GPI_PRIMED); - //GpiValueCbHdl::m_state = GPI_PRIMED; return 0; } @@ -313,13 +301,13 @@ int FliSimPhaseCbHdl::arm_callback(void) { if (NULL == m_proc_hdl) { - LOG_INFO("Creating a new process to sensitise with priority %d", m_priority); + LOG_DEBUG("Creating a new process to sensitise with priority %d", m_priority); m_proc_hdl = mti_CreateProcessWithPriority(NULL, handle_fli_callback, (void *)this, m_priority); } mti_ScheduleWakeup(m_proc_hdl, 0); m_sensitised = true; - m_state = GPI_PRIMED; + set_call_state(GPI_PRIMED); return 0; } @@ -335,9 +323,6 @@ FliSignalCbHdl::FliSignalCbHdl(GpiImplInterface *impl, GpiCbHdl *FliSignalObjHdl::value_change_cb(unsigned int edge) { - - LOG_INFO("Creating value change callback for %s", m_name.c_str()); - FliSignalCbHdl *cb = NULL; switch (edge) { @@ -401,7 +386,7 @@ const char* FliSignalObjHdl::get_signal_value_binstr(void) break; } - LOG_INFO("Retrieved \"%s\" for signal %s", &val_buff, m_name.c_str()); + LOG_DEBUG("Retrieved \"%s\" for signal %s", &val_buff, m_name.c_str()); return &val_buff[0]; } diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index 2e254e96..c17b83f3 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -31,20 +31,6 @@ #include "../gpi/gpi_priv.h" #include "mti.h" -#if 0 -class FliCbHdl : public virtual GpiCbHdl { -public: - FliCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl) { } - virtual ~FliCbHdl() { } - - virtual int arm_callback(void) = 0; - virtual int cleanup_callback(void) = 0; - -protected: - int register_cb(p_cb_data cb_data); -}; -#endif - // Callback handles // In FLI some callbacks require us to register a process From ff68e85d3863c492a2189785731716f4bb257d6f Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 10 Feb 2015 22:51:23 +0000 Subject: [PATCH 0794/2656] Issue #141: Add cache for timer callback processes --- lib/fli/FliImpl.cpp | 42 +++++++++++++++++++++++++++++++++++---- lib/fli/FliImpl.h | 48 +++++++++++++++++++++++++++++++++------------ lib/fli/Makefile | 2 +- 3 files changed, 75 insertions(+), 17 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 5899617e..15383862 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -210,7 +210,7 @@ GpiObjHdl *FliImpl::get_root_handle(const char *name) GpiCbHdl *FliImpl::register_timed_callback(uint64_t time_ps) { - FliTimedCbHdl *hdl = new FliTimedCbHdl(this, time_ps); + FliTimedCbHdl *hdl = cache.get_timer(time_ps); if (hdl->arm_callback()) { delete(hdl); @@ -271,11 +271,17 @@ int FliProcessCbHdl::cleanup_callback(void) m_sensitised = false; return 0; } +FliTimedCbHdl::FliTimedCbHdl(GpiImplInterface *impl, + uint64_t time_ps) : GpiCbHdl(impl), + FliProcessCbHdl(impl), + m_time_ps(time_ps) +{ + m_proc_hdl = mti_CreateProcessWithPriority(NULL, handle_fli_callback, (void *)this, MTI_PROC_IMMEDIATE); +} int FliTimedCbHdl::arm_callback(void) { LOG_DEBUG("Creating a new process to sensitise with timer"); - m_proc_hdl = mti_CreateProcessWithPriority(NULL, handle_fli_callback, (void *)this, MTI_PROC_IMMEDIATE); mti_ScheduleWakeup(m_proc_hdl, m_time_ps); LOG_DEBUG("Wakeup scheduled on %p for %llu", m_proc_hdl, m_time_ps); m_sensitised = true; @@ -285,7 +291,6 @@ int FliTimedCbHdl::arm_callback(void) int FliSignalCbHdl::arm_callback(void) { - if (NULL == m_proc_hdl) { LOG_DEBUG("Creating a new process to sensitise to signal %s", mti_GetSignalName(m_sig_hdl)); m_proc_hdl = mti_CreateProcess(NULL, handle_fli_callback, (void *)this); @@ -299,7 +304,6 @@ int FliSignalCbHdl::arm_callback(void) int FliSimPhaseCbHdl::arm_callback(void) { - if (NULL == m_proc_hdl) { LOG_DEBUG("Creating a new process to sensitise with priority %d", m_priority); m_proc_hdl = mti_CreateProcessWithPriority(NULL, handle_fli_callback, (void *)this, m_priority); @@ -420,5 +424,35 @@ int FliSignalObjHdl::set_signal_value(std::string &value) return rc-1; } +FliTimedCbHdl* FliTimerCache::get_timer(uint64_t time_ps) +{ +#ifdef USE_CACHE + FliTimedCbHdl *hdl; + + if (free_list.size()) { + //LOG_DEBUG("Popping timer from cache list of %d\n", free_list.size() ); + std::vector::iterator first = free_list.begin(); + hdl = *first; + free_list.erase(first); + hdl->reset_time(time_ps); + } else { + //LOG_DEBUG("Created new timer\n"); + hdl = new FliTimedCbHdl(impl, time_ps); + } + + return hdl; +#else + return new FliTimedCbHdl(impl, time_ps); +#endif +} + +void FliTimerCache::put_timer(FliTimedCbHdl* hdl) +{ +#ifdef USE_CACHE + free_list.push_back(hdl); + //LOG_INFO("Adding timer to cache, now at %d\n", free_list.size()); +#endif +} + GPI_ENTRY_POINT(fli, cocotb_init); diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index c17b83f3..b997d722 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -126,17 +126,6 @@ class FliReadOnlyCbHdl : public FliSimPhaseCbHdl { virtual ~FliReadOnlyCbHdl() { } }; -class FliTimedCbHdl : public FliProcessCbHdl { -public: - FliTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : GpiCbHdl(impl), - FliProcessCbHdl(impl), m_time_ps(time_ps) {}; - virtual ~FliTimedCbHdl() { } - int arm_callback(void); -private: - uint64_t m_time_ps; -}; - - class FliShutdownCbHdl : public GpiCbHdl { public: FliShutdownCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl) { } @@ -156,13 +145,29 @@ class FliRegionObjHdl : public GpiObjHdl { mtiRegionIdT m_fli_hdl; }; +class FliImpl; +class FliTimedCbHdl; +class FliTimerCache { +public: + FliTimerCache(FliImpl* impl) : impl(impl) { } + ~FliTimerCache() { } + + FliTimedCbHdl* get_timer(uint64_t time_ps); + void put_timer(FliTimedCbHdl*); + +private: + std::vector free_list; + FliImpl *impl; +}; + class FliImpl : public GpiImplInterface { public: FliImpl(const std::string& name) : GpiImplInterface(name), m_readonly_cbhdl(this), m_nexttime_cbhdl(this), - m_readwrite_cbhdl(this) { } + m_readwrite_cbhdl(this), + cache(this) { } /* Sim related */ void sim_end(void); @@ -187,8 +192,27 @@ class FliImpl : public GpiImplInterface { FliReadOnlyCbHdl m_readonly_cbhdl; FliNextPhaseCbHdl m_nexttime_cbhdl; FliReadWriteCbHdl m_readwrite_cbhdl; +public: + FliTimerCache cache; }; +class FliTimedCbHdl : public FliProcessCbHdl { +public: + FliTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps); + virtual ~FliTimedCbHdl() { } + + int arm_callback(void); + void reset_time(uint64_t new_time) { + m_time_ps = new_time; + } + int cleanup_callback(void) { + FliImpl* impl = (FliImpl*)m_impl; + impl->cache.put_timer(this); + return 0; + } +private: + uint64_t m_time_ps; +}; #endif /*COCOTB_FLI_IMPL_H_ */ diff --git a/lib/fli/Makefile b/lib/fli/Makefile index 0c23fe97..5355298b 100644 --- a/lib/fli/Makefile +++ b/lib/fli/Makefile @@ -29,7 +29,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += -I$(MODELSIM_BIN_DIR)/../include -GXX_ARGS += -DFLI_CHECKING +GXX_ARGS += -DFLI_CHECKING -DUSE_CACHE LIBS := -lgpilog -lgpi -lstdc++ LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) LIB_NAME := libfli From 9605a85e925cc7b5ae384dc12b7e66db0a6a7af7 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 11 Feb 2015 14:33:40 +0000 Subject: [PATCH 0795/2656] Issue #141: Set up data for signals up front, no speed difference --- lib/fli/FliImpl.cpp | 104 +++++++++++++++++++++++++++----------------- lib/fli/FliImpl.h | 20 ++++++++- 2 files changed, 81 insertions(+), 43 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 15383862..083adf14 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -265,7 +265,6 @@ int FliImpl::deregister_callback(GpiCbHdl *gpi_hdl) */ int FliProcessCbHdl::cleanup_callback(void) { - if (m_sensitised) mti_Desensitize(m_proc_hdl); m_sensitised = false; @@ -296,8 +295,10 @@ int FliSignalCbHdl::arm_callback(void) m_proc_hdl = mti_CreateProcess(NULL, handle_fli_callback, (void *)this); } - mti_Sensitize(m_proc_hdl, m_sig_hdl, MTI_EVENT); - m_sensitised = true; + if (!m_sensitised) { + mti_Sensitize(m_proc_hdl, m_sig_hdl, MTI_EVENT); + m_sensitised = true; + } set_call_state(GPI_PRIMED); return 0; } @@ -309,8 +310,10 @@ int FliSimPhaseCbHdl::arm_callback(void) m_proc_hdl = mti_CreateProcessWithPriority(NULL, handle_fli_callback, (void *)this, m_priority); } - mti_ScheduleWakeup(m_proc_hdl, 0); - m_sensitised = true; + if (!m_sensitised) { + mti_ScheduleWakeup(m_proc_hdl, 0); + m_sensitised = true; + } set_call_state(GPI_PRIMED); return 0; } @@ -350,9 +353,6 @@ GpiCbHdl *FliSignalObjHdl::value_change_cb(unsigned int edge) return (GpiValueCbHdl*)cb; } -// TODO: Could cache various settings here which would save some of the calls -// into FLI -static char val_buff[1024]; static const char value_enum[10] = "UX01ZWLH-"; const char* FliSignalObjHdl::get_signal_value_binstr(void) @@ -362,37 +362,31 @@ const char* FliSignalObjHdl::get_signal_value_binstr(void) case MTI_TYPE_ENUM: case MTI_TYPE_SCALAR: case MTI_TYPE_PHYSICAL: - mtiInt32T scalar_val; - scalar_val = mti_GetSignalValue(m_fli_hdl); - val_buff[0] = value_enum[scalar_val]; - val_buff[1] = '\0'; + m_val_buff[0] = value_enum[mti_GetSignalValue(m_fli_hdl)]; break; case MTI_TYPE_ARRAY: { - mtiInt32T *array_val; - array_val = (mtiInt32T *)mti_GetArraySignalValue(m_fli_hdl, NULL); - int num_elems = mti_TickLength(mti_GetSignalType(m_fli_hdl)); - if (num_elems <= 256) { - char *iter = (char*)array_val; - for (int i = 0; i < num_elems; i++ ) { - val_buff[i] = value_enum[(int)iter[i]]; - } - } else { - for (int i = 0; i < num_elems; i++ ) { - val_buff[i] = value_enum[array_val[i]]; + mti_GetArraySignalValue(m_fli_hdl, m_mti_buff); + if (m_val_len <= 256) { + char *iter = (char*)m_mti_buff; + for (int i = 0; i < m_val_len; i++ ) { + m_val_buff[i] = value_enum[(int)iter[i]]; + } + } else { + for (int i = 0; i < m_val_len; i++ ) { + m_val_buff[i] = value_enum[m_mti_buff[i]]; + } } } - val_buff[num_elems] = '\0'; - mti_VsimFree(array_val); - } break; + break; default: LOG_CRITICAL("Signal %s type %d not currently supported", m_name.c_str(), mti_GetTypeKind(mti_GetSignalType(m_fli_hdl))); break; } - LOG_DEBUG("Retrieved \"%s\" for signal %s", &val_buff, m_name.c_str()); + LOG_DEBUG("Retrieved \"%s\" for signal %s", &m_val_buff, m_name.c_str()); - return &val_buff[0]; + return m_val_buff; } int FliSignalObjHdl::set_signal_value(const int value) @@ -413,45 +407,73 @@ int FliSignalObjHdl::set_signal_value(const int value) int FliSignalObjHdl::set_signal_value(std::string &value) { int rc; - char buff[128]; - int len = value.copy(buff, value.length()); - buff[len] = '\0'; + std::vector writable(value.begin(), value.end()); + writable.push_back('\0'); - rc = mti_ForceSignal(m_fli_hdl, &buff[0], 0, MTI_FORCE_DEPOSIT, -1, -1); + rc = mti_ForceSignal(m_fli_hdl, &writable[0], 0, MTI_FORCE_DEPOSIT, -1, -1); if (!rc) { LOG_CRITICAL("Setting signal value failed!\n"); } return rc-1; } +int FliSignalObjHdl::initialise(std::string &name) +{ + /* Pre allocte buffers on signal type basis */ + m_type = mti_GetTypeKind(mti_GetSignalType(m_fli_hdl)); + + switch (m_type) { + case MTI_TYPE_ENUM: + case MTI_TYPE_SCALAR: + case MTI_TYPE_PHYSICAL: + m_val_len = 2; + m_val_buff = (char*)malloc(m_val_len); + if (!m_val_buff) { + LOG_CRITICAL("Unable to alloc mem for signal read buffer"); + } + m_val_buff[1] = '\0'; + break; + case MTI_TYPE_ARRAY: + m_val_len = mti_TickLength(mti_GetSignalType(m_fli_hdl)); + m_val_buff = (char*)malloc(m_val_len); + if (!m_val_buff) { + LOG_CRITICAL("Unable to alloc mem for signal read buffer"); + } + m_val_buff[m_val_len] = '\0'; + m_mti_buff = (mtiInt32T*)malloc(sizeof(*m_mti_buff) * m_val_len); + if (!m_mti_buff) { + LOG_CRITICAL("Unable to alloc mem for signal mti read buffer"); + } + break; + default: + LOG_CRITICAL("Unable to handle onject type for %s (%d)", + name.c_str(), m_type); + } + + GpiObjHdl::initialise(name); + + return 0; +} + FliTimedCbHdl* FliTimerCache::get_timer(uint64_t time_ps) { -#ifdef USE_CACHE FliTimedCbHdl *hdl; if (free_list.size()) { - //LOG_DEBUG("Popping timer from cache list of %d\n", free_list.size() ); std::vector::iterator first = free_list.begin(); hdl = *first; free_list.erase(first); hdl->reset_time(time_ps); } else { - //LOG_DEBUG("Created new timer\n"); hdl = new FliTimedCbHdl(impl, time_ps); } return hdl; -#else - return new FliTimedCbHdl(impl, time_ps); -#endif } void FliTimerCache::put_timer(FliTimedCbHdl* hdl) { -#ifdef USE_CACHE free_list.push_back(hdl); - //LOG_INFO("Adding timer to cache, now at %d\n", free_list.size()); -#endif } GPI_ENTRY_POINT(fli, cocotb_init); diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index b997d722..ddba5c42 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -74,12 +74,22 @@ class FliSignalObjHdl : public GpiSignalObjHdl { m_fli_hdl(hdl), m_rising_cb(impl, this, GPI_RISING), m_falling_cb(impl, this, GPI_FALLING), - m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } - virtual ~FliSignalObjHdl() { } + m_either_cb(impl, this, GPI_FALLING | GPI_RISING), + m_type(MTI_TYPE_SCALAR), + m_mti_buff(NULL), + m_val_buff(NULL), + m_val_len(0) { } + virtual ~FliSignalObjHdl() { + if (m_val_len) + free(m_val_buff); + if (m_mti_buff) + free(m_mti_buff); + } const char* get_signal_value_binstr(void); int set_signal_value(const int value); int set_signal_value(std::string &value); + int initialise(std::string &name); GpiCbHdl *value_change_cb(unsigned int edge); protected: @@ -87,6 +97,12 @@ class FliSignalObjHdl : public GpiSignalObjHdl { FliSignalCbHdl m_rising_cb; FliSignalCbHdl m_falling_cb; FliSignalCbHdl m_either_cb; + +private: + mtiTypeKindT m_type; + mtiInt32T *m_mti_buff; + char *m_val_buff; + int m_val_len; }; From a67a000971a620a40515b6b77bf4597d4e641d5c Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 11 Feb 2015 16:37:40 +0000 Subject: [PATCH 0796/2656] Issue #141: Timer recylcing now working --- lib/fli/FliImpl.cpp | 51 ++++++++++++++++++++++++++++++++++----------- lib/fli/FliImpl.h | 15 +++++++------ 2 files changed, 46 insertions(+), 20 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 083adf14..7731f784 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -90,6 +90,9 @@ void handle_fli_callback(void *data) if (new_state != GPI_PRIMED) if (cb_hdl->cleanup_callback()) delete cb_hdl; + } else { + /* Issue #188 seems to appear via FLI as well */ + cb_hdl->cleanup_callback(); } }; @@ -247,10 +250,7 @@ GpiCbHdl *FliImpl::register_nexttime_callback(void) int FliImpl::deregister_callback(GpiCbHdl *gpi_hdl) { - int rc = gpi_hdl->cleanup_callback(); - // TOOD: Don't delete if it's a re-usable doobery -// delete(gpi_hdl); - return rc; + return gpi_hdl->cleanup_callback(); } @@ -265,11 +265,13 @@ int FliImpl::deregister_callback(GpiCbHdl *gpi_hdl) */ int FliProcessCbHdl::cleanup_callback(void) { - if (m_sensitised) + if (m_sensitised) { mti_Desensitize(m_proc_hdl); + } m_sensitised = false; return 0; } + FliTimedCbHdl::FliTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : GpiCbHdl(impl), FliProcessCbHdl(impl), @@ -280,14 +282,38 @@ FliTimedCbHdl::FliTimedCbHdl(GpiImplInterface *impl, int FliTimedCbHdl::arm_callback(void) { - LOG_DEBUG("Creating a new process to sensitise with timer"); mti_ScheduleWakeup(m_proc_hdl, m_time_ps); - LOG_DEBUG("Wakeup scheduled on %p for %llu", m_proc_hdl, m_time_ps); m_sensitised = true; set_call_state(GPI_PRIMED); return 0; } +int FliTimedCbHdl::cleanup_callback(void) +{ + switch (get_call_state()) { + case GPI_PRIMED: + /* Issue #188: Work around for modelsim that is harmless to othes too, + we tag the time as delete, let it fire then do not pass up + */ + LOG_DEBUG("Not removing PRIMED timer %p", m_time_ps); + set_call_state(GPI_DELETE); + return 0; + case GPI_CALL: + LOG_DEBUG("Not removing CALL timer yet %p", m_time_ps); + set_call_state(GPI_DELETE); + return 0; + case GPI_DELETE: + LOG_DEBUG("Removing Postponed DELETE timer %p", m_time_ps); + break; + default: + break; + } + FliProcessCbHdl::cleanup_callback(); + FliImpl* impl = (FliImpl*)m_impl; + impl->cache.put_timer(this); + return 0; +} + int FliSignalCbHdl::arm_callback(void) { if (NULL == m_proc_hdl) { @@ -455,14 +481,15 @@ int FliSignalObjHdl::initialise(std::string &name) return 0; } +#include + FliTimedCbHdl* FliTimerCache::get_timer(uint64_t time_ps) { FliTimedCbHdl *hdl; - if (free_list.size()) { - std::vector::iterator first = free_list.begin(); - hdl = *first; - free_list.erase(first); + if (!free_list.empty()) { + hdl = free_list.front(); + free_list.pop(); hdl->reset_time(time_ps); } else { hdl = new FliTimedCbHdl(impl, time_ps); @@ -473,7 +500,7 @@ FliTimedCbHdl* FliTimerCache::get_timer(uint64_t time_ps) void FliTimerCache::put_timer(FliTimedCbHdl* hdl) { - free_list.push_back(hdl); + free_list.push(hdl); } GPI_ENTRY_POINT(fli, cocotb_init); diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index ddba5c42..08e574ae 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -31,6 +31,8 @@ #include "../gpi/gpi_priv.h" #include "mti.h" +#include + // Callback handles // In FLI some callbacks require us to register a process @@ -38,11 +40,12 @@ class FliProcessCbHdl : public virtual GpiCbHdl { public: FliProcessCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - m_proc_hdl(NULL) { } + m_proc_hdl(NULL), + m_sensitised(false) { } virtual ~FliProcessCbHdl() { } virtual int arm_callback(void) = 0; - int cleanup_callback(void); + virtual int cleanup_callback(void); protected: mtiProcessIdT m_proc_hdl; @@ -172,7 +175,7 @@ class FliTimerCache { void put_timer(FliTimedCbHdl*); private: - std::vector free_list; + std::queue free_list; FliImpl *impl; }; @@ -222,11 +225,7 @@ class FliTimedCbHdl : public FliProcessCbHdl { void reset_time(uint64_t new_time) { m_time_ps = new_time; } - int cleanup_callback(void) { - FliImpl* impl = (FliImpl*)m_impl; - impl->cache.put_timer(this); - return 0; - } + int cleanup_callback(void); private: uint64_t m_time_ps; }; From a78d0f285cf6731f0ed3d70db88d1b7d177b0284 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Thu, 12 Feb 2015 21:12:30 +1100 Subject: [PATCH 0797/2656] Tidy up PYTHONPATH windows to posix conversion. --- makefiles/simulators/Makefile.aldec | 2 +- makefiles/simulators/Makefile.icarus | 2 +- makefiles/simulators/Makefile.modelsim | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 8405e8b7..87e2b9eb 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -106,7 +106,7 @@ EXTRA_LIBS := -laldecpli EXTRA_LIBDIRS := -L$(ALDEC_PATH)/bin/ OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') LIB_LOAD := PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) -OLD_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' | sed 's/\([a-zA-Z]\)\(:\)\//\/\1\//g' | sed 's/;/:/g') +OLD_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-zA-Z]\):\//\/\1\//g' -e 's/;/:/g') results.xml: $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(COCOTB_VPI_LIB) $(CUSTOM_SIM_DEPS) cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(OLD_PYTHONPATH) \ diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index 1f1c829a..c2f20dc4 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -44,7 +44,7 @@ EXTRA_LIBS := -lvpi EXTRA_LIBDIRS := -L$(ICARUS_PATH)/lib OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') LIB_LOAD := PATH=$(OLD_PATH):$(LIB_DIR) -OLD_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' | sed 's/\([a-zA-Z]\)\(:\)\//\/\1\//g' | sed 's/;/:/g') +OLD_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-zA-Z]\):\//\/\1\//g' -e 's/;/:/g') results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(OLD_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index ccd12965..420dea68 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -86,7 +86,7 @@ EXTRA_LIBDIRS := -L$(MODELSIM_BIN_DIR) OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') LIB_LOAD := PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) -OLD_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' | sed 's/\([a-zA-Z]\)\(:\)\//\/\1\//g' | sed 's/;/:/g') +OLD_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-zA-Z]\):\//\/\1\//g' -e 's/;/:/g') results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(COCOTB_VPI_LIB) cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ From 6e00695974a9fcb8dc3e6c567491ee3a9f79df08 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Thu, 12 Feb 2015 21:17:12 +1100 Subject: [PATCH 0798/2656] Modified adder example to use a python model of the adder function to test that PYTHONPATH is set correctly under Msys. Fixed PYTHONPATH issue in the endian swapper example. --- examples/adder/model/__init__.py | 0 examples/adder/model/adder_model.py | 3 +++ examples/adder/tests/Makefile | 3 +++ examples/adder/tests/test_adder.py | 13 ++++++++----- examples/endian_swapper/tests/Makefile | 5 +++-- 5 files changed, 17 insertions(+), 7 deletions(-) create mode 100644 examples/adder/model/__init__.py create mode 100644 examples/adder/model/adder_model.py diff --git a/examples/adder/model/__init__.py b/examples/adder/model/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/adder/model/adder_model.py b/examples/adder/model/adder_model.py new file mode 100644 index 00000000..4420f6b6 --- /dev/null +++ b/examples/adder/model/adder_model.py @@ -0,0 +1,3 @@ +def adder_model(a,b): + """ model of adder """ + return a+b diff --git a/examples/adder/tests/Makefile b/examples/adder/tests/Makefile index fb1db7fd..64abcb52 100644 --- a/examples/adder/tests/Makefile +++ b/examples/adder/tests/Makefile @@ -36,9 +36,12 @@ COCOTB=$(PWD)/../../.. ifeq ($(OS),Msys) WPWD=$(shell sh -c 'pwd -W') +PYTHONPATH := $(WPWD)/../model;$(PYTHONPATH) else WPWD=$(shell pwd) +PYTHONPATH := $(WPWD)/../model:$(PYTHONPATH) endif +export PYTHONPATH VERILOG_SOURCES = $(WPWD)/../hdl/adder.v GPI_IMPL := vpi diff --git a/examples/adder/tests/test_adder.py b/examples/adder/tests/test_adder.py index 72d9b489..aedc9a0c 100644 --- a/examples/adder/tests/test_adder.py +++ b/examples/adder/tests/test_adder.py @@ -2,19 +2,22 @@ import cocotb from cocotb.triggers import Timer from cocotb.result import TestFailure +from adder_model import adder_model import random @cocotb.test() def adder_basic_test(dut): """Test for 5 + 10""" yield Timer(2) - - dut.A = 5 - dut.B = 10 + A = 5 + B = 10 + + dut.A = A + dut.B = B yield Timer(2) - if int(dut.X) != 15: + if int(dut.X) != adder_model(A,B): raise TestFailure( "Adder result is incorrect: %s != 15" % str(dut.X)) else: # these last two lines are not strictly necessary @@ -34,7 +37,7 @@ def adder_randomised_test(dut): yield Timer(2) - if int(dut.X) != (A + B): + if int(dut.X) != adder_model(A,B): raise TestFailure( "Randomised test failed with: %s + %s = %s" % (int(dut.A), int(dut.B), int(dut.X))) diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index d3560aea..c9760ee1 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -37,9 +37,12 @@ COCOTB=$(PWD)/../../.. ifeq ($(OS),Msys) WPWD=$(shell sh -c 'pwd -W') +PYTHONPATH := $(PWD)/../cosim;$(PYTHONPATH) else WPWD=$(shell pwd) +PYTHONPATH := $(PWD)/../cosim:$(PYTHONPATH) endif +export PYTHONPATH ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES = $(WPWD)/../hdl/endian_swapper.sv @@ -58,8 +61,6 @@ export TOPLEVEL_LANG MODULE=test_endian_swapper CUSTOM_COMPILE_DEPS = hal -PYTHONPATH := $(PWD)/../cosim:$(PYTHONPATH) -export PYTHONPATH LD_LIBRARY_PATH := $(PWD)/../cosim:$(LD_LIBRARY_PATH) export LD_LIBRARY_PATH From 544bec996412ccbf843782768a43272325a22169 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 13 Feb 2015 13:22:18 +0000 Subject: [PATCH 0799/2656] Fix for Python < 3 support - index version tuple directly --- cocotb/handle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 34f68a5c..13e13c11 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -90,7 +90,7 @@ def __getattr__(self, name): def _raise_testerror(self, msg): lastframe = sys._getframe(2) - if sys.version_info.major >= 3: + if sys.version_info[0] >= 3: buff = StringIO() traceback.print_stack(lastframe, file=buff) else: From d288b3e352dff09044815af6d8b6da1c58829436 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 13 Feb 2015 13:22:54 +0000 Subject: [PATCH 0800/2656] Cleanup: Align GPI logging from C with Python output --- lib/gpi_log/gpi_logging.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gpi_log/gpi_logging.c b/lib/gpi_log/gpi_logging.c index e063cad3..bac74624 100644 --- a/lib/gpi_log/gpi_logging.c +++ b/lib/gpi_log/gpi_logging.c @@ -116,7 +116,7 @@ void gpi_log(const char *name, long level, const char *pathname, const char *fun } fprintf(stdout, " -.--ns "); - fprintf(stdout, "%-8s", log_level(level)); + fprintf(stdout, "%-9s", log_level(level)); fprintf(stdout, "%-35s", name); fprintf(stdout, "%20s:", pathname); fprintf(stdout, "%-4ld", lineno); From 8eb249057e42e437b64aaeffff4bb99af56bb50c Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 13 Feb 2015 13:23:14 +0000 Subject: [PATCH 0801/2656] Update dependencies in Aldec Makefile --- makefiles/simulators/Makefile.aldec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 4d7f8587..00d1b581 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -61,7 +61,7 @@ endif # Create a TCL script based on the list of $(VERILOG_SOURCES) -$(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(SIM_BUILD) +$(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(VHDL_SOURCES) $(SIM_BUILD) echo "alib work" > $@ echo "set worklib work" >> $@ ifdef VHDL_SOURCES From f67ea725981ffd268614c4dfa758573040179049 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 13 Feb 2015 13:35:07 +0000 Subject: [PATCH 0802/2656] Simplistic On-chip peripheral bus driver --- cocotb/drivers/opb.py | 130 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 cocotb/drivers/opb.py diff --git a/cocotb/drivers/opb.py b/cocotb/drivers/opb.py new file mode 100644 index 00000000..51e7c9a7 --- /dev/null +++ b/cocotb/drivers/opb.py @@ -0,0 +1,130 @@ +''' +Copyright (c) 2015 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' +""" +Drivers for On-chip Peripheral Bus + +NB Currently we only support a very small subset of functionality +""" +import random + +import cocotb +from cocotb.triggers import RisingEdge, ReadOnly, Event +from cocotb.drivers import BusDriver +from cocotb.result import ReturnValue + +class OPBException(Exception): pass + + +class OPBMaster(BusDriver): + """ + On-chip peripheral bus master + """ + _signals = ["xferAck", "errAck", "toutSup", "retry", "DBus_out", "select", "RNW", "BE", "ABus", "DBus_in"] + _optional_signals = ["seqAddr"] + _max_cycles = 16 + + def __init__(self, entity, name, clock): + BusDriver.__init__(self, entity, name, clock) + self.bus.select.setimmediatevalue(0) + self.log.debug("OPBMaster created") + self.busy_event = Event("%s_busy" % name) + self.busy = False + + @cocotb.coroutine + def _acquire_lock(self): + if self.busy: + yield self.busy_event.wait() + self.busy_event.clear() + self.busy = True + + def _release_lock(self): + self.busy = False + self.busy_event.set() + + @cocotb.coroutine + def read(self, address, sync=True): + """ + Issue a request to the bus and block until this + comes back. Simulation time still progresses + but syntactically it blocks. + """ + yield self._acquire_lock() + + # Apply values for next clock edge + if sync: + yield RisingEdge(self.clock) + self.bus.ABus <= address + self.bus.select <= 1 + self.bus.RNW <= 1 + self.bus.BE <= 0xF + + count = 0 + while not int(self.bus.xferAck.value): + yield RisingEdge(self.clock) + yield ReadOnly() + if int(self.bus.toutSup.value): + count = 0 + else: + count += 1 + if count >= self._max_cycles: + raise OPBException("Read took longer than 16 cycles") + data = int(self.bus.DBus_out.value) + + # Deassert read + self.bus.select <= 0 + self._release_lock() + self.log.info("Read of address 0x%x returned 0x%08x" % (address, data)) + raise ReturnValue(data) + + @cocotb.coroutine + def write(self, address, value, sync=True): + """ + """ + yield self._acquire_lock() + + if sync: + yield RisingEdge(self.clock) + self.bus.ABus <= address + self.bus.select <= 1 + self.bus.RNW <= 0 + self.bus.BE <= 0xF + self.bus.DBus_out <= value + + count = 0 + while not int(self.bus.xferAck.value): + yield RisingEdge(self.clock) + yield ReadOnly() + if int(self.bus.toutSup.value): + count = 0 + else: + count += 1 + if count >= self._max_cycles: + raise OPBException("Write took longer than 16 cycles") + + self.bus.select <= 0 + self._release_lock() + From add16badffde5143b64d2fb60fcf9abac9b00b37 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 13 Feb 2015 18:25:16 +0000 Subject: [PATCH 0803/2656] Issue #141: Revert forcing modelsim and vhld pre merge --- examples/endian_swapper/tests/Makefile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index 1403ca5a..688cedba 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -29,9 +29,7 @@ # Override this variable to use VHPI instead of VPI -TOPLEVEL_LANG ?= vhdl -SIM ?= modelsim -VSIM_ARGS += "-trace_foreign 1" +TOPLEVEL_LANG ?= verilog WPWD=$(shell sh -c 'pwd -W') PWD=$(shell pwd) From 0ac869b7d9f0e806ca0676827de32147ae0f6e15 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Sat, 14 Feb 2015 15:53:34 +1100 Subject: [PATCH 0804/2656] Add support for compilation of both VHDL and Verilog simultaneously. --- makefiles/simulators/Makefile.modelsim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index 2a471ee7..49ab275d 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -52,10 +52,11 @@ endif $(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) | $(SIM_BUILD) echo "vlib work" > $@ +ifneq ($(VHDL_SOURCES),) + echo "vcom $(VHDL_ARGS) $(VHDL_SOURCES)" >> $@ +endif ifneq ($(VERILOG_SOURCES),) echo "vlog -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(VERILOG_SOURCES)" >> $@ -else - echo "vcom $(VHDL_ARGS) $(VHDL_SOURCES)" >> $@ endif echo "vsim $(VSIM_ARGS) $(TOPLEVEL)" >> $@ ifneq ($(GUI),1) From dccda8d13f157a148c8f1784d8c3483eab320057 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Sat, 14 Feb 2015 15:55:48 +1100 Subject: [PATCH 0805/2656] fix modelsim warning: library already exists at "work" --- makefiles/simulators/Makefile.modelsim | 1 + 1 file changed, 1 insertion(+) diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index 49ab275d..79ad48e1 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -51,6 +51,7 @@ VSIM_ARGS += -pli libvpi.$(LIB_EXT) endif $(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) | $(SIM_BUILD) + echo "if [file exists work] {vdel -all}" > $@ echo "vlib work" > $@ ifneq ($(VHDL_SOURCES),) echo "vcom $(VHDL_ARGS) $(VHDL_SOURCES)" >> $@ From 257040dc9b3b08d02eba16b3a63fa2f4f666cfe1 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Sat, 14 Feb 2015 15:59:16 +1100 Subject: [PATCH 0806/2656] Rename VHDL_ARGS to VCOM_ARGS --- makefiles/simulators/Makefile.modelsim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index 79ad48e1..0d09113b 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -54,7 +54,7 @@ $(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) | echo "if [file exists work] {vdel -all}" > $@ echo "vlib work" > $@ ifneq ($(VHDL_SOURCES),) - echo "vcom $(VHDL_ARGS) $(VHDL_SOURCES)" >> $@ + echo "vcom $(VCOM_ARGS) $(VHDL_SOURCES)" >> $@ endif ifneq ($(VERILOG_SOURCES),) echo "vlog -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(VERILOG_SOURCES)" >> $@ From c2adfb013bda930f098c0ca08fb99ac6dd944634 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Sat, 14 Feb 2015 16:17:27 +1100 Subject: [PATCH 0807/2656] Add support for setting generics. --- makefiles/simulators/Makefile.modelsim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index 0d09113b..ae8a7a65 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -50,6 +50,10 @@ else VSIM_ARGS += -pli libvpi.$(LIB_EXT) endif +ifneq ($(GENERICS),) +VSIM_ARGS += $(foreach gen, $(GENERICS),"-G $(gen)") +endif + $(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) | $(SIM_BUILD) echo "if [file exists work] {vdel -all}" > $@ echo "vlib work" > $@ From 2ab63f22aa6663c4a191d786a50083945cd576e3 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Sat, 14 Feb 2015 16:31:58 +1100 Subject: [PATCH 0808/2656] Add support for vpiRegArray --- include/vpi_user.h | 1 + lib/vpi/VpiImpl.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/include/vpi_user.h b/include/vpi_user.h index 5cbf9d77..0593d886 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -61,6 +61,7 @@ typedef uint32_t *vpiHandle; #define vpiReg 48 /* scalar or vector reg */ #define vpiRegBit 49 /* bit of vector reg */ #define vpiParameter 41 /* module parameter */ +#define vpiRegArray 116 /* multidimensional reg */ #define vpiStructVar 618 #define vpiInterface 601 #define vpiInterfaceArray 603 diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 9808f0b5..7e52612d 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -85,6 +85,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &n case vpiNetBit: case vpiReg: case vpiParameter: + case vpiRegArray: case vpiEnumNet: new_obj = new VpiSignalObjHdl(this, new_hdl); break; From b6108840d12ffe8ec241fd0a6a73d855eb6288e7 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Sat, 14 Feb 2015 16:42:17 +1100 Subject: [PATCH 0809/2656] Add VHDL example instantiated in systemverilog wrapper. --- examples/mean/hdl/mean.vhd | 62 ++++++++++++++++ examples/mean/hdl/mean_pkg.vhd | 29 ++++++++ examples/mean/hdl/mean_sv.sv | 35 +++++++++ examples/mean/tests/Makefile | 31 ++++++++ examples/mean/tests/test_mean.py | 122 +++++++++++++++++++++++++++++++ 5 files changed, 279 insertions(+) create mode 100644 examples/mean/hdl/mean.vhd create mode 100644 examples/mean/hdl/mean_pkg.vhd create mode 100644 examples/mean/hdl/mean_sv.sv create mode 100644 examples/mean/tests/Makefile create mode 100644 examples/mean/tests/test_mean.py diff --git a/examples/mean/hdl/mean.vhd b/examples/mean/hdl/mean.vhd new file mode 100644 index 00000000..e1a00bbf --- /dev/null +++ b/examples/mean/hdl/mean.vhd @@ -0,0 +1,62 @@ +------------------------------------------------------------------------------- +-- Calculates mean of data input bus +------------------------------------------------------------------------------- +library ieee ; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.mean_pkg.all; + + +entity mean is +generic ( + BUS_WIDTH : natural := 2); +port ( + clk : in std_logic; + rst : in std_logic; + i_valid : in std_logic; + i_data : in t_data_array(0 to BUS_WIDTH-1); + o_valid : out std_logic; + o_data : out t_data + ); +end mean; + + +architecture RTL of mean is + +-- signal s_sum : unsigned(DATA_WIDTH + clog2(BUS_WIDTH-1)-1 downto 0) := (others => '0'); -- introduce bug + signal s_sum : unsigned(DATA_WIDTH + clog2(BUS_WIDTH)-1 downto 0) := (others => '0'); + signal s_valid : std_logic := '0'; + +begin + + assert BUS_WIDTH = 2**(clog2(BUS_WIDTH)) + report LF & " BUS_WIDTH = " & integer'image(BUS_WIDTH) & " , should be a power of 2!" + severity Failure; + + + process(clk) + variable v_sum : unsigned(s_sum'range); + begin + if rising_edge(clk) then + s_valid <= i_valid; + + if i_valid = '1' then + v_sum := (others => '0'); + for i in i_data'range loop + v_sum := v_sum + resize(i_data(i), v_sum'length); + end loop; + + s_sum <= v_sum; + end if; + + if rst = '1' then + s_sum <= (others => '0'); + s_valid <= '0'; + end if; + end if; + end process; + + o_valid <= s_valid; + o_data <= resize(shift_right(s_sum, clog2(BUS_WIDTH)), o_data'length); + +end rtl; \ No newline at end of file diff --git a/examples/mean/hdl/mean_pkg.vhd b/examples/mean/hdl/mean_pkg.vhd new file mode 100644 index 00000000..26163124 --- /dev/null +++ b/examples/mean/hdl/mean_pkg.vhd @@ -0,0 +1,29 @@ +------------------------------------------------------------------------------- +-- Package with constants, types & functions for mean.vhd +------------------------------------------------------------------------------- +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.math_real.all; + + +package mean_pkg is + + constant DATA_WIDTH : natural := 6; + + subtype t_data is unsigned(DATA_WIDTH-1 downto 0); + type t_data_array is array (natural range <>) of t_data; + + function clog2(n : positive) return natural; + +end package; + + +package body mean_pkg is + + function clog2(n : positive) return natural is + begin + return integer(ceil(log2(real(n)))); + end function; + +end package body; diff --git a/examples/mean/hdl/mean_sv.sv b/examples/mean/hdl/mean_sv.sv new file mode 100644 index 00000000..fa359bda --- /dev/null +++ b/examples/mean/hdl/mean_sv.sv @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------------- +// sv wrapper for mean.vhd +//----------------------------------------------------------------------------- +import mean_pkg::*; + + +module mean_sv #( + parameter BUS_WIDTH=2) + ( + input logic clk, + input logic rst, + input logic i_valid, + input t_data_array i_data [0:BUS_WIDTH-1], + output logic o_valid, + output t_data o_data + ); + + +// make constant from package visible +parameter DATA_WIDTH = data_width; + +// VHDL DUT + mean #( + .BUS_WIDTH (BUS_WIDTH)) + mean ( + .clk (clk), + .rst (rst), + .i_valid (i_valid), + .i_data (i_data), + .o_valid (o_valid), + .o_data (o_data) + ); + + +endmodule diff --git a/examples/mean/tests/Makefile b/examples/mean/tests/Makefile new file mode 100644 index 00000000..5bdebe60 --- /dev/null +++ b/examples/mean/tests/Makefile @@ -0,0 +1,31 @@ + +TOPLEVEL := mean_sv +TOPLEVEL_LANG ?= verilog + +PWD=$(shell pwd) +COCOTB=$(PWD)/../../.. + +ifeq ($(OS),Msys) +WPWD=$(shell sh -c 'pwd -W') +else +WPWD=$(shell pwd) +endif + +VHDL_SOURCES = $(WPWD)/../hdl/mean_pkg.vhd +VHDL_SOURCES += $(WPWD)/../hdl/mean.vhd + +VCOM_ARGS = -mixedsvvh +export VCOM_ARGS + +GENERICS = "BUS_WIDTH=4" +export GENERICS + +VERILOG_SOURCES = $(WPWD)/../hdl/mean_sv.sv +GPI_IMPL := vpi + +export TOPLEVEL_LANG + +MODULE ?= test_mean + +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim diff --git a/examples/mean/tests/test_mean.py b/examples/mean/tests/test_mean.py new file mode 100644 index 00000000..c5b346f7 --- /dev/null +++ b/examples/mean/tests/test_mean.py @@ -0,0 +1,122 @@ +from __future__ import print_function +from __future__ import division + +import cocotb +from cocotb.triggers import Timer, RisingEdge, ReadOnly +from cocotb.result import TestFailure +from cocotb.monitors import BusMonitor +from cocotb.scoreboard import Scoreboard + +import random + +clock_period = 100 + + +class StreamBusMonitor(BusMonitor): + """ + streaming bus monitor + """ + _signals = ["valid", "data"] + + @cocotb.coroutine + def _monitor_recv(self): + """Watch the pins and reconstruct transactions""" + + while True: + yield RisingEdge(self.clock) + yield ReadOnly() + if self.bus.valid.value: + self._recv(int(self.bus.data.value)) + + +@cocotb.coroutine +def clock_gen(signal, period=10000): + while True: + signal <= 0 + yield Timer(period/2) + signal <= 1 + yield Timer(period/2) + + +@cocotb.coroutine +def value_test(dut, num): + """ Test n*num/n = num """ + + data_width = int(dut.DATA_WIDTH.value) + bus_width = int(dut.BUS_WIDTH.value) + dut.log.info('Detected DATA_WIDTH = %d, BUS_WIDTH = %d' %(data_width, bus_width)) + + cocotb.fork(clock_gen(dut.clk, period=clock_period)) + + dut.rst <= 1 + for i in range(bus_width): + dut.i_data[i] <= 0 + dut.i_valid <= 0 + yield RisingEdge(dut.clk) + yield RisingEdge(dut.clk) + dut.rst <= 0 + + for i in range(bus_width): + dut.i_data[i] <= num + dut.i_valid <= 1 + yield RisingEdge(dut.clk) + dut.i_valid <= 0 + yield RisingEdge(dut.clk) + got = int(dut.o_data.value) + + if got != num: + raise TestFailure( + 'Mismatch detected: got %d, exp %d!' %(got, num)) + + +@cocotb.test() +def mean_basic_test(dut): + """ Test n*5/n = 5 """ + yield value_test(dut, 5) + + +@cocotb.test() +def mean_overflow_test(dut): + """ Test for overflow n*max_val/n = max_val """ + data_width = int(dut.DATA_WIDTH.value) + yield value_test(dut, 2**data_width-1) + + +@cocotb.test() +def mean_randomised_test(dut): + """ Test mean of random numbers multiple times """ + +# dut_in = StreamBusMonitor(dut, "i", dut.clk) # this doesn't work; VPI Error vpi_get_value(): ERROR - Cannot get a value for an object of type vpiArrayVar. + dut_out = StreamBusMonitor(dut, "o", dut.clk) + + exp_out = [] + scoreboard = Scoreboard(dut) + scoreboard.add_interface(dut_out, exp_out) + + data_width = int(dut.DATA_WIDTH.value) + bus_width = int(dut.BUS_WIDTH.value) + dut.log.info('Detected DATA_WIDTH = %d, BUS_WIDTH = %d' %(data_width, bus_width)) + + cocotb.fork(clock_gen(dut.clk, period=clock_period)) + + dut.rst <= 1 + for i in range(bus_width): + dut.i_data[i] = 0 + dut.i_valid <= 0 + yield RisingEdge(dut.clk) + yield RisingEdge(dut.clk) + dut.rst <= 0 + + for j in range(10): + nums = [] + for i in range(bus_width): + x = random.randint(0, 2**data_width-1) + dut.i_data[i] = x + nums.append(x) + dut.i_valid <= 1 + + nums_mean = sum(nums)//bus_width + exp_out.append(nums_mean) + yield RisingEdge(dut.clk) + dut.i_valid <= 0 + From 33dfaaf917ef4fb4debda6c1f6725712df68b342 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Sat, 14 Feb 2015 16:52:42 +1100 Subject: [PATCH 0810/2656] Introduce bug to raise TestFailure --- examples/mean/hdl/mean.vhd | 6 +++--- examples/mean/tests/Makefile | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/mean/hdl/mean.vhd b/examples/mean/hdl/mean.vhd index e1a00bbf..5ff90a6a 100644 --- a/examples/mean/hdl/mean.vhd +++ b/examples/mean/hdl/mean.vhd @@ -23,8 +23,8 @@ end mean; architecture RTL of mean is --- signal s_sum : unsigned(DATA_WIDTH + clog2(BUS_WIDTH-1)-1 downto 0) := (others => '0'); -- introduce bug - signal s_sum : unsigned(DATA_WIDTH + clog2(BUS_WIDTH)-1 downto 0) := (others => '0'); + signal s_sum : unsigned(DATA_WIDTH + clog2(BUS_WIDTH-1)-1 downto 0) := (others => '0'); -- introduce bug +-- signal s_sum : unsigned(DATA_WIDTH + clog2(BUS_WIDTH)-1 downto 0) := (others => '0'); signal s_valid : std_logic := '0'; begin @@ -59,4 +59,4 @@ begin o_valid <= s_valid; o_data <= resize(shift_right(s_sum, clog2(BUS_WIDTH)), o_data'length); -end rtl; \ No newline at end of file +end rtl; diff --git a/examples/mean/tests/Makefile b/examples/mean/tests/Makefile index 5bdebe60..4d0f1be7 100644 --- a/examples/mean/tests/Makefile +++ b/examples/mean/tests/Makefile @@ -17,7 +17,7 @@ VHDL_SOURCES += $(WPWD)/../hdl/mean.vhd VCOM_ARGS = -mixedsvvh export VCOM_ARGS -GENERICS = "BUS_WIDTH=4" +GENERICS = "BUS_WIDTH=2" export GENERICS VERILOG_SOURCES = $(WPWD)/../hdl/mean_sv.sv From 667ef746adad40b4a3a232ed0c8402feeff800b8 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Sat, 14 Feb 2015 17:40:34 +1100 Subject: [PATCH 0811/2656] Implement WAVES similar to aldec --- makefiles/simulators/Makefile.modelsim | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index ae8a7a65..e386dd3c 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -64,7 +64,12 @@ ifneq ($(VERILOG_SOURCES),) echo "vlog -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(VERILOG_SOURCES)" >> $@ endif echo "vsim $(VSIM_ARGS) $(TOPLEVEL)" >> $@ -ifneq ($(GUI),1) +ifeq ($(WAVES),1) + echo "log -recursive /*" >> $@ +endif +ifeq ($(GUI),1) + echo "add wave -recursive /*" >> $@ +else echo "run -all" >> $@ echo "quit" >> $@ endif From d8976859b07c9c34eff8628842bd180f7c8391f0 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sat, 14 Feb 2015 17:07:41 +0000 Subject: [PATCH 0812/2656] Cleanup: Fix up of sim selection for ones that need a lib dir plus remove some duplicate code --- makefiles/simulators/Makefile.aldec | 24 +++++++++---------- makefiles/simulators/Makefile.icarus | 33 +++++++++++--------------- makefiles/simulators/Makefile.modelsim | 25 ++++++++----------- 3 files changed, 35 insertions(+), 47 deletions(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index cc3854f1..a5748ebc 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -88,8 +88,11 @@ ifeq ($(COVERAGE),1) echo "acdb report -db work.acdb -txt -o coverage/acdb_report.txt" >> $@ endif -ifeq ($(ALDEC_PATH),) -$(error "Need to set ALDEC_PATH") +# auto-detect riviera dir from system path +export ALDEC_BIN_DIR = $(shell dirname $(shell which vsim)) + +ifeq ($(ALDEC_BIN_DIR),) +$(error "Directory containing ModelSim binaries must be included in system path") endif ifeq ($(OS),Msys) @@ -103,28 +106,23 @@ ifeq ($(OS),Msys) MINGW_BIN_DIR = $(shell dirname $(shell which gcc)) EXTRA_LIBS := -laldecpli -EXTRA_LIBDIRS := -L$(ALDEC_PATH)/bin/ +EXTRA_LIBDIRS := -L$(ALDEC_BIN_DIR) OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') LIB_LOAD := PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) -OLD_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-zA-Z]\):\//\/\1\//g' -e 's/;/:/g') - -results.xml: $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(COCOTB_VPI_LIB) $(CUSTOM_SIM_DEPS) - cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(OLD_PYTHONPATH) \ - $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do runsim.tcl | tee sim.log - +NEW_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-zA-Z]\):\//\/\1\//g' -e 's/;/:/g') else EXTRA_LIBS = -laldecpli -EXTRA_LIBDIRS = -L$(ALDEC_PATH)/bin/Linux64 +EXTRA_LIBDIRS = -L$(ALDEC_BIN_DIR)/Linux64 LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) +NEW_PYTHONPATH := $(PYTHON_PATH) +endif # Note it's the redirection of the output rather than the 'do' command # that turns on batch mode (i.e. exit on completion/error) results.xml: $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(COCOTB_VPI_LIB) $(CUSTOM_SIM_DEPS) - cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ + cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) \ $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do runsim.tcl | tee sim.log -endif - clean:: rm -rf $(SIM_BUILD) diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index c2f20dc4..6b81232f 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -34,42 +34,37 @@ $(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) iverilog -o $(SIM_BUILD)/sim.vvp -D COCOTB_SIM=1 $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) # Execution phase -ifeq ($(OS),Msys) -ifeq ($(ICARUS_PATH),) -$(error "Need to set ICARUS_PATH") +# auto-detect modelsim dir from system path +export ICARUS_BIN_DIR = $(shell dirname $(shell which iverilog)) + +# make sure modelsim dir was found +ifeq ($(ICARUS_BIN_DIR),) +$(error "Directory containing Icarus binaries must be included in system path") endif +ifeq ($(OS),Msys) + EXTRA_LIBS := -lvpi -EXTRA_LIBDIRS := -L$(ICARUS_PATH)/lib +EXTRA_LIBDIRS := -L$(ICARUS_BIN_DIR)/../lib OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') LIB_LOAD := PATH=$(OLD_PATH):$(LIB_DIR) -OLD_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-zA-Z]\):\//\/\1\//g' -e 's/;/:/g') - -results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(OLD_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) - -debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(OLD_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - gdb --args vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) +NEW_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-zA-Z]\):\//\/\1\//g' -e 's/;/:/g') else LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) +NEW_PYTHONPATH := $(PYTHONPAT) +endif results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ gdb --args vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) -endif - clean:: -@rm -rf $(SIM_BUILD) diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index e386dd3c..a3d2a979 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -74,16 +74,16 @@ else echo "quit" >> $@ endif -ifeq ($(OS),Msys) - # auto-detect modelsim dir from system path -MODELSIM_BIN_DIR = $(shell dirname $(shell which vsim)) +export MODELSIM_BIN_DIR = $(shell dirname $(shell which vsim)) # make sure modelsim dir was found ifeq ($(MODELSIM_BIN_DIR),) $(error "Directory containing ModelSim binaries must be included in system path") endif +ifeq ($(OS),Msys) + # Windows allows the situation where the libstc++ used at link time as # specified by -L can be different to the one that is used at runtime which # comes from the first libstdc++ that it finds in the path. As such @@ -97,24 +97,19 @@ EXTRA_LIBDIRS := -L$(MODELSIM_BIN_DIR) OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') LIB_LOAD := PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) -OLD_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-zA-Z]\):\//\/\1\//g' -e 's/;/:/g') - -results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(OLD_PYTHONPATH) \ - $(SIM_CMD) -do runsim.do 2>&1 | tee sim.log - +NEW_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-zA-Z]\):\//\/\1\//g' -e 's/;/:/g') +INT_LIBS := $(COCOTB_VPI_LIB) else -export MODELSIM_BIN_DIR = $(shell which vsim | sed 's|/vsim||') LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) +NEW_PYTHONPATH := $(PYTHONPATH) +INT_LIBS := $(COCOTB_VPI_LIB) $(COCOTB_FLI_LIB) +endif -results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(COCOTB_VPI_LIB) $(COCOTB_FLI_LIB) +results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) \ $(SIM_CMD) -do runsim.do 2>&1 | tee sim.log -endif - clean:: -rm -rf $(SIM_BUILD) From c572c9b346237bdf6e1631974023b98854a611c0 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sat, 14 Feb 2015 17:37:19 +0000 Subject: [PATCH 0813/2656] Cleanup: make less of a hash of the last commit --- makefiles/simulators/Makefile.aldec | 2 +- makefiles/simulators/Makefile.icarus | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index a5748ebc..8bfd6986 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -114,7 +114,7 @@ else EXTRA_LIBS = -laldecpli EXTRA_LIBDIRS = -L$(ALDEC_BIN_DIR)/Linux64 LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) -NEW_PYTHONPATH := $(PYTHON_PATH) +NEW_PYTHONPATH := $(PYTHONPATH) endif # Note it's the redirection of the output rather than the 'do' command diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index 6b81232f..afaea855 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -53,7 +53,7 @@ NEW_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-z else LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) -NEW_PYTHONPATH := $(PYTHONPAT) +NEW_PYTHONPATH := $(PYTHONPATH) endif results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) From 1538e9253223fcb189839833eac309be2abe688a Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sat, 14 Feb 2015 19:58:11 +0000 Subject: [PATCH 0814/2656] Cleanup: Print correct error for aldec --- makefiles/simulators/Makefile.aldec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 8bfd6986..3ff0a0cb 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -92,7 +92,7 @@ endif export ALDEC_BIN_DIR = $(shell dirname $(shell which vsim)) ifeq ($(ALDEC_BIN_DIR),) -$(error "Directory containing ModelSim binaries must be included in system path") +$(error "Directory containing Riviera-Pro binaries must be included in system path") endif ifeq ($(OS),Msys) From f334a240cbfc046feeea7cd4a18fcbd32c846ae7 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 15 Feb 2015 09:47:49 +0000 Subject: [PATCH 0815/2656] Issue #9: Cleanup of docs --- documentation/source/index.rst | 8 +------- documentation/source/introduction.rst | 20 ++++++-------------- 2 files changed, 7 insertions(+), 21 deletions(-) diff --git a/documentation/source/index.rst b/documentation/source/index.rst index 5c86268e..c2d02ca2 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -1,8 +1,3 @@ -.. cocotb documentation master file, created by - sphinx-quickstart on Wed Jun 19 14:44:09 2013. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - Welcome to Cocotb's documentation! ================================== @@ -12,14 +7,13 @@ Contents: :maxdepth: 2 introduction - examples quickstart coroutines + building endian_swapper ping_tun_tap hal_cosimulation library_reference - building roadmap simulator_support diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index 2d23193d..7668dac5 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -9,25 +9,17 @@ What is cocotb? **Cocotb** is completely free, open source (under the `BSD License `_) and hosted on `GitHub `_. -**Cocotb** requires a simulator to simulate the RTL. Simulators that have been tested and known to work with cocotb: +**Cocotb** requires a simulator to simulate the RTL. Simulators that have been tested and known to work with Cocotb: -* Icarus Verilog -* Synopsys VCS -* Aldec Riviera-PRO -* Cadence Incisive -* Mentor Modelsim +* `Icarus Verilog `_ +* `Aldec `_ Riviera-PRO +* `Synopsys `_ VCS +* `Cadence `_ Incisive +* `Mentor `_ Modelsim **Cocotb** can be used live in a web-browser using `EDA Playground `_. -What does Cocotb look like? -=========================== - -For some examples of Cocotb in action take a look at the `Examples`_ section. - -See the Jenkins pages for the Cocotb regression for a - - How is Cocotb different? ======================== From 9e2d5f44d8dc176c2987ba6afa5c7ce2a7853b73 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 15 Feb 2015 14:00:13 +0000 Subject: [PATCH 0816/2656] Cleanup: Enable cosim to build on windows, plus use fli for mixed_lang tests --- examples/endian_swapper/cosim/Makefile | 7 ++++--- examples/mixed_language/tests/Makefile | 10 ++++++++++ makefiles/simulators/Makefile.modelsim | 4 ++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/examples/endian_swapper/cosim/Makefile b/examples/endian_swapper/cosim/Makefile index 1f328b93..984e9915 100644 --- a/examples/endian_swapper/cosim/Makefile +++ b/examples/endian_swapper/cosim/Makefile @@ -1,4 +1,5 @@ include $(SIM_ROOT)/makefiles/Makefile.pylib +include $(SIM_ROOT)/makefiles/Makefile.inc PYTHON_LIBDIR ?= /usr/lib64 SWIG ?= swig @@ -11,12 +12,12 @@ endif all: io_module.so _hal.so io_module.o: io.c io_module.h io.h - $(CC) -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 \ - -fexceptions --param=ssp-buffer-size=4 -mtune=generic -D_GNU_SOURCE -fPIC \ + $(CC) $(GCC_FLAGS) -pthread -fno-strict-aliasing -O2 -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 \ + -fexceptions --param=ssp-buffer-size=4 -D_GNU_SOURCE \ -fwrapv -DNDEBUG -I$(PYTHON_INCLUDEDIR) -c $< -o $@ io_module.so: io_module.o - $(CC) -pthread -shared -Wl,-z,relro $< -L$(PYTHON_LIBDIR) $(PYTHON_LD_FLAGS) -o $@ + $(CC) -pthread -shared $< -L$(PYTHON_LIBDIR) $(PYTHON_LD_FLAGS) -o $@ _hal.so: ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so $(CC) -g -ldl -shared -fPIC -I$(shell pwd) -I$(PYTHON_INCLUDEDIR) -I../hal ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so -o $@ diff --git a/examples/mixed_language/tests/Makefile b/examples/mixed_language/tests/Makefile index 4f07e69f..fd7be0c5 100644 --- a/examples/mixed_language/tests/Makefile +++ b/examples/mixed_language/tests/Makefile @@ -21,13 +21,23 @@ VHDL_SOURCES = $(WPWD)/../../endian_swapper/hdl/endian_swapper.vhdl ifeq ($(TOPLEVEL_LANG),sv) VERILOG_SOURCES += $(WPWD)/../hdl/toplevel.sv GPI_IMPL=vpi +ifeq ($(SIM),aldec) GPI_EXTRA=vhpi endif +ifeq ($(SIM),modelsim) + GPI_EXTRA=fli +endif +endif ifeq ($(TOPLEVEL_LANG),vhdl) $(error "VHDL toplevel not yet implemented") VHDL_SOURCES += $(WPWD)/../hdl/toplevel.vhdl +ifeq ($(SIM),aldec) GPI_IMPL=vhpi +endif +ifeq ($(SIM),modelsim) + GPI_IMPL=fli +endif GPI_EXTRA=vpi endif diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index a3d2a979..970823fd 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -27,8 +27,6 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -VPI_LIB := vpi - # Modelsim is 32-bit only ARCH:=i686 @@ -105,6 +103,8 @@ NEW_PYTHONPATH := $(PYTHONPATH) INT_LIBS := $(COCOTB_VPI_LIB) $(COCOTB_FLI_LIB) endif +# Depending on the version of modelsim the interfaces that it supports can change +# Append or remove values to INT_LIBS depenending on your license results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) \ From cca9c3ad63160274a56cff3c8b9a205d5d6a953f Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 15 Feb 2015 15:46:13 +0000 Subject: [PATCH 0817/2656] Cleanup: Append Python library dir to LD_LIBRARY_PATH, enables 32bit builds to not have to specify this --- makefiles/Makefile.pylib | 2 ++ 1 file changed, 2 insertions(+) diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib index 6d821bb4..cac1a443 100644 --- a/makefiles/Makefile.pylib +++ b/makefiles/Makefile.pylib @@ -36,6 +36,8 @@ ARCH?=$(NATIVE_ARCH) include $(SIM_ROOT)/makefiles/Makefile.pylib.$(OS) +export LD_LIBRARY_PATH := $(LD_LIBRARY_PATH):$(PYTHON_LIBDIR) + #debug:: # @echo "Debug from Makefile.pylib" # @echo -e "\tUsing PYTHON_VERSION=$(PYTHON_VERSION)" From 8051ffc5d1c988fddd727e04e5018f98b6f2b84a Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 15 Feb 2015 17:08:43 +0000 Subject: [PATCH 0818/2656] Issue #9: Documentation for installation on supported platforms --- documentation/source/introduction.rst | 9 +++ documentation/source/quickstart.rst | 92 +++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index 2d23193d..737cbdc1 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -11,12 +11,21 @@ What is cocotb? **Cocotb** requires a simulator to simulate the RTL. Simulators that have been tested and known to work with cocotb: +Linux Platforms + * Icarus Verilog * Synopsys VCS * Aldec Riviera-PRO * Cadence Incisive * Mentor Modelsim +Windows Platform + +* Icarus Verilog +* Aldec Riviera-PRO +* Mentor Modelsim + + **Cocotb** can be used live in a web-browser using `EDA Playground `_. diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index b182d279..1e5ee087 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -13,8 +13,100 @@ Cocotb has the following requirements: * Python 2.6+ * Python-dev packages +* GCC and associated development packages +* GNU Make * A Verilog simulator +Internal development is performed on Linux Mint 17 (x64). We also use Redhat 6.5(x64). Other Redhat and Ubuntu based distributions (x32 and x64) should work too but due fragmented nature of Linux we can not test everything. Instructions are provided for the main distributions we use. + +Linux native arch installation +------------------------------ + +Ubuntu based installation + +.. code-block:: bash + + $> sudo apt-get install git make gcc g++ swig python-dev + +This will allow building of the Cocotb libs for use with a 64 bit native simulator. If a 32 bit simulator is being used then additional steps to install 32bit development libraries and python are needed. + +Redhat based installation + +.. code-block:: bash + + $> sudo yum install gcc gcc-c++ libstdc++-devel python-devel + +This will allow building of the Cocotb libs for use with a 64 bit native simulator. If a 32 bit simulator is being used then additional steps to install 32bit development libraries and python are needed. + + +32 bit Python +------------- + +Additional development libraries are needed for building 32bit python on 64 bit systems. + +Ubuntu based installation + +.. code-block:: bash + + $> sudo apt-get install libx32gcc1 gcc-4.8-multilib lib32stdc++-4.8-dev + +Replace 4.8 with the version of gcc that was installed on the system in the step above. Unlike on Redhat where 32 bit python can co-exist with native python ubuntu requires the source to be downloaded and built. + +Redhat based installation + +.. code-block:: bash + + $> sudo yum install glibc.i686 glibc-devel.i386 libgcc.i686 libstdc++-devel.i686 + + +Specific releases can be downloaded from https://www.python.org/downloads/ . + +.. code-block:: bash + + $> wget https://www.python.org/ftp/python/2.7.9/Python-2.7.9.tgz + $> tar xvf Python-2.7.9.tgz + $> cd Python-2.7.9 + $> export PY32_DIR=/opt/pym32 + $> ./configure CC="gcc -m32" LDFLAGS="-L/lib32 -L/usr/lib32 -Lpwd/lib32 -Wl,-rpath,/lib32 -Wl,-rpath,$PY32_DIR/lib" --prefix=$PY32_DIR --enable-shared + $> make + $> sudo make install + +Cocotb can now be built against 32bit python by setting the architecture and placing the 32bit python ahead of the native version in the path when running a test + +.. code-block:: bash + + $> export PATH=/opt/pym32/bin + $> cd + $> ARCH=i686 make + +Windows 7 installation +---------------------- + +Recent work has been done with the support of the Cocotb community to enable Windows support using the MinGW/Msys environment. Download the MinGQ installer from. + +http://sourceforge.net/projects/mingw/files/latest/download?source=files . + +Run the GUI installer and specify a directory you would like the environment installed in. The installer will retrieve a list of possible packages, when this is done press continue. The MinGW Installation Manager is then launched. + +The following packages need selecting by checking the tick box and selecting "Mark for installation" + +.. code-block:: bash + + Basic Installation + -- mingw-developer-tools + -- mingw32-base + -- mingw32-gcc-g++ + -- msys-base + +From the Installation menu then select "Apply Changes", in the next dialog select "Apply". + +When installed a shell can be opened using the "msys.bat" file located under the /msys/1.0/ + +Python can be downloaded from https://www.python.org/ftp/python/2.7.9/python-2.7.9.msi, other versions of python can be used as well. Run the installer and download to your chosen location. + +It is beneficial to add the path to Python to the windows system PATH variable so it can be used easily from inside Msys. + +Once inside the Msys shell commands as given here will work as expected. Running an example ------------------ From c1e259c1ba739d244ea2cdb4f857ea54e83d17a2 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 15 Feb 2015 18:18:12 +0000 Subject: [PATCH 0819/2656] Update version number --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index bf60c6e9..d53e27cf 100644 --- a/version +++ b/version @@ -1 +1 @@ -VERSION=0.5a +VERSION=1.0 From 97906760e1613250673e8515b1b4f571f0834245 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Tue, 17 Feb 2015 23:23:07 -0600 Subject: [PATCH 0820/2656] seed from +ntb_random_seed or +seed rather than falling back straightaway to current time --- cocotb/__init__.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 1f5a4cd3..7965d634 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -89,15 +89,6 @@ def _initialise_testbench(root_name): if memcheck_port is not None: mem_debug(int(memcheck_port)) - # Seed the Python random number generator to make this repeatable - seed = os.getenv('RANDOM_SEED') - if seed is None: - seed = int(time.time()) - log.info("Seeding Python random module with %d" % (seed)) - else: - seed = int(seed) - log.info("Seeding Python random module with supplied seed %d" % (seed)) - random.seed(seed) exec_path = os.getenv('SIM_ROOT') if exec_path is None: @@ -113,6 +104,22 @@ def _initialise_testbench(root_name): process_plusargs() + # Seed the Python random number generator to make this repeatable + seed = os.getenv('RANDOM_SEED') + + if seed is None: + if 'ntb_random_seed' in plusargs: + seed = eval(plusargs['ntb_random_seed']) + elif 'seed' in plusargs: + seed = eval(plusargs['seed']) + else: + seed = int(time.time()) + log.info("Seeding Python random module with %d" % (seed)) + else: + seed = int(seed) + log.info("Seeding Python random module with supplied seed %d" % (seed)) + random.seed(seed) + module_str = os.getenv('MODULE') test_str = os.getenv('TESTCASE') From 1c9e6d9e5128aa640f8535c0f3a04a76e8676cbc Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 20 Feb 2015 15:02:52 +0000 Subject: [PATCH 0821/2656] Cleanup: Remove dead feedhandling code --- cocotb/generators/feeds/__init__.py | 63 ----- cocotb/generators/feeds/itch_40.py | 328 ------------------------- cocotb/generators/feeds/itch_feed.py | 67 ----- cocotb/generators/feeds/moldudp64.py | 89 ------- cocotb/generators/feeds/opra_binary.py | 223 ----------------- cocotb/generators/feeds/packet_util.py | 96 -------- 6 files changed, 866 deletions(-) delete mode 100644 cocotb/generators/feeds/__init__.py delete mode 100644 cocotb/generators/feeds/itch_40.py delete mode 100644 cocotb/generators/feeds/itch_feed.py delete mode 100644 cocotb/generators/feeds/moldudp64.py delete mode 100644 cocotb/generators/feeds/opra_binary.py delete mode 100644 cocotb/generators/feeds/packet_util.py diff --git a/cocotb/generators/feeds/__init__.py b/cocotb/generators/feeds/__init__.py deleted file mode 100644 index f132b5c6..00000000 --- a/cocotb/generators/feeds/__init__.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python -''' Copyright (c) 2013 Potential Ventures Ltd -Copyright (c) 2013 SolarFlare Communications Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - -# Set log level to benefit from Scapy warnings -import logging -logging.getLogger("scapy").setLevel(1) - -import cocotb -from cocotb.log import SimLog -from scapy.all import * - -class Feed(object): - def __init__(self, name, filepath=None): - self.name = name - self._packets = {} - self._filepath = filepath - self.fullname = '\'' + self.name + '\'' - self.log = SimLog('cocotb.' + self.name) - if self._filepath: - self._source = open(self._filepath) - self.log.debug("loaded file %s" % self._filepath) - self.log.debug("Created feed!") - - def addmsg(self, tag, data): - """ Add a defined message to the internal feed store """ - self._packets[tag] = data - - def getmsg(self): - """ Get a string representation of the current list head - This packet will be ready to send - """ - if self._packets: - tag, packet = self._packets.popitem() - return str(packet) - else: - self.log.warn("No packets in feed %s" % self.fullname) - return None - diff --git a/cocotb/generators/feeds/itch_40.py b/cocotb/generators/feeds/itch_40.py deleted file mode 100644 index 890eea42..00000000 --- a/cocotb/generators/feeds/itch_40.py +++ /dev/null @@ -1,328 +0,0 @@ -#! /usr/bin/env python -''' Copyright (c) 2013 Potential Ventures Ltd -Copyright (c) 2013 SolarFlare Communications Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - -# Set log level to benefit from Scapy warnings -from scapy.all import * -from packet_util import * - -message_types = { - 'T' : "Timestamp", - 'S' : "System Event", - 'R' : "Stock Delivery", - 'H' : "Stock Trading Action", - 'Y' : "Reg SHO Short Sale Price Test", - 'L' : "Maktet Participant Position", - 'A' : "Add order without MPID", - 'F' : "Add order with MPID", - 'E' : "Order Executed", - 'C' : "Order Executed with Price", - 'X' : "Order Cancel", - 'D' : "Order Delete", - 'U' : "Order Replace", - 'P' : "Trade Message", - 'Q' : "Cross Trade Message", - 'B' : "Broken Trade", - 'I' : "Net Order Imbalance", - 'N' : "Retail Price Improvement Indicator" -} - -event_codes = { - 'O' : "Start of Message", - 'S' : "Start of System Hours", - 'Q' : "Start of Market Hours", - 'M' : "End of Market Hours", - 'E' : "End of System Hours", - 'C' : "End of Message", - 'A' : "Emergency Market Condition - HALT", - 'R' : "Emergency Market Condition - Quote Only Period", - 'B' : "Emergency Market Condition - Resumption", -} - -market_catagories = { - 'N' : "New York Stock Exchange (NYSE)", - 'A' : "NYSE Amex", - 'P' : "NYSE Arca", - 'Q' : "NASDAQ Global Select Market", - 'R' : "NASDAQ Global Market", - 'S' : "NASDAQ Capital Market", - 'Z' : "BATS BZX Exchange" -} - -status_indicators = { - 'D' : "Deficient", - 'E' : "Delinquent", - 'Q' : "Bankrupt", - 'S' : "Suspended", - 'G' : "Deficient and Bankrupt", - 'H' : "Deficient and Delinquent", - 'J' : "Delinquent and Bankrupt", - 'K' : "Deficient, Delinquent and Bankrupt", - ' ' : "Compliant" -} - -trading_state = { - 'H' : "Halted Across all U.S", - 'P' : "Paused Across all U.S", - 'Q' : "Quotation only period", - 'T' : "Trading on NASDAQ" -} - -market_mode = { - 'N' : "Normal", - 'P' : "Passive", - 'S' : "Syndicate", - 'R' : "Pre-Syndicate", - 'L' : "Penalty" -} - -market_state = { - 'A' : "Active", - 'E' : "Excused/Withdrawn", - 'W' : "Withdrawn", - 'S' : "Suspended", - 'D' : "Deleted" -} - -buy_sell = { - 'B' : "Buy", - 'S' : "Sell" -} - -cross_type = { - 'O' : "NASDAQ Opening Cross", - 'C' : "NASDAQ Closing Cross", - 'H' : "Cross for IPO halted/pause securities", - 'I' : "NASDAQ Cross Network: Intrdat/Postclose" -} - -imbalance_dir = { - 'B' : "Buy Imbalance", - 'S' : "Sell Imbalance", - 'N' : "No Imbalance", - 'O' : "Insufficent orders" -} - -price_var = { - 'L' : "Less than 1%", - '1' : "1 - 1.99%", - '2' : "2 - 2.99%", - '3' : "3 - 3.99%", - '4' : "4 - 4.99%", - '5' : "5 - 5.99%", - '6' : "6 - 6.99%", - '7' : "7 - 7.99%", - '8' : "8 - 8.99%", - '9' : "9 - 9.99%", - 'A' : "10 - 19.99%", - 'B' : "20 - 29.99%", - 'C' : "30% or greater", - ' ' : "Cannot calculate" -} - -interest_flag = { - 'B' : "RPI Orders available on the buy side", - 'S' : "RPT Orders available on the sell side", - 'A' : "RPI Orders available on both sides", - 'N' : "Not RPI orders available" -} - -class ItchMessage(Packet): - name = "ITCH 4.1" - -class ItchTimeStamp(ItchMessage): - name = "ITCH 4.1 Timestamp" - fields_desc = [ - CharEnumField("message", 'T', message_types), - IntField("seconds", 0)] - -class ItchEvent(ItchMessage): - name = "ITCH 4.1 System Event Message" - fields_desc = [ - CharEnumField("header", 'S', message_types), - IntField("nanoseconds", 0), - CharEnumField("eventcode", " ", event_codes)] - -class ItchStockDelivery(ItchMessage): - name = "ITCH 4.1 Stock Delivery" - fields_desc = [ - CharEnumField("header", 'R', message_types), - IntField("nanoseconds", 0), - StrFixedLenField("stock", " ", length=8), - CharEnumField("mktcat", ' ', market_catagories), - CharEnumField("status", ' ', status_indicators), - IntField("lotsize", 0), - CharEnumField("lotsonly", ' ', {'Y' : "Only round lots", 'N' : "Odd/Mixed allowed"})] - -class ItchStockAction(ItchMessage): - name = "ITCH 4.1 Stock Trading Action" - fields_desc = [ - CharEnumField("header", 'H', message_types), - IntField("nanoseconds", 0), - StrFixedLenField("stock", " ", length=8), - CharEnumField("state", ' ', trading_state), - ByteField("reserved", 0), - StrFixedLenField("reason", " ", length=4)] - -class ItchRegSHO(ItchMessage): - name = "ITCH 4.1 Reg SHO Short Sale Price Test Restricted Indicator" - fields_desc = [ - CharEnumField("header", 'Y', message_types), - IntField("nanoseconds", 0), - StrFixedLenField("stock", " ", length=8), - CharEnumField("reason", ' ', {'0' : "No test", '1' : "SHO on", '2' : "SHO remains"})] - -class ItchMarketPartPos(ItchMessage): - name = "ITCH 4.1 Matket Participant Position" - fields_desc = [ - CharEnumField("header", 'L', message_types), - IntField("nanoseconds", 0), - StrFixedLenField("mpid", " ", length=4), - StrFixedLenField("stock", " ", length=8), - CharEnumField("primary", ' ', {'Y' : "Primary Maker", 'N' : "Non-primary"}), - CharEnumField("mkmode", ' ' , market_mode), - CharEnumField("mkstate", ' ', market_state)] - -class ItchAddOrder(ItchMessage): - name = "ITCH 4.1 Add order without MPID" - fields_desc = [ - CharEnumField("header", 'A', message_types), - IntField("nanoseconds", 0), - LongDecimalField("order", 0, 8), - CharEnumField("type", ' ', buy_sell), - IntField("shares", 0), - StrFixedLenField("stock", " ", length=8), - IntField("price", 0)] - -class ItchAddOrderMPID(ItchMessage): - name = "ITCH 4.1 Add order with MPID" - fields_desc = [ - CharEnumField("header", 'F', message_types), - IntField("nanoseconds", 0), - LongDecimalField("order", 0, 8), - CharEnumField("type", ' ', buy_sell), - IntField("shares", 0), - StrFixedLenField("stock", " ", length=8), - IntField("price", 0), - StrFixedLenField("attrubution", " ", length=4)] - -class ItchOrderExec(ItchMessage): - name = "ITCH 4.1 Order Executed" - fields_desc = [ - CharEnumField("header", 'E', message_types), - IntField("nanoseconds", 0), - LongDecimalField("order", 0, 8), - IntField("shares", 0), - LongDecimalField("matchnum", 0, 8)] - -class ItchOrderExecPrice(ItchMessage): - name = "ITCH 4.1 Order Executed with price" - fields_desc = [ - CharEnumField("header", 'C', message_types), - IntField("nanoseconds", 0), - LongDecimalField("order", 0, 8), - IntField("shares", 0), - LongDecimalField("matchnum", 0, 8), - CharEnumField("printable", ' ', {'Y' : "Printable", 'N' : "Non-Printable"}), - IntField("price", 0)] - -class ItchOrderCancel(ItchMessage): - name = "ITCH 4.1 Order Cancle" - fields_desc = [ - CharEnumField("header", 'X', message_types), - IntField("nanoseconds", 0), - LongDecimalField("order", 0, 8), - IntField("cancelnum", 0)] - -class ItchOrderDelete(ItchMessage): - name = "ITCH 4.1 Order Delete" - filds_desc = [ - CharEnumField("header", 'D', message_types), - IntField("nanoseconds", 0), - LongDecimalField("order", 0, 8)] - -class ItchOrderReplace(ItchMessage): - name = "ITCH 4.1 Order Replace" - fields_desc = [ - CharEnumField("header", 'U', message_types), - IntField("nanoseconds", 0), - LongDecimalField("oldorder", 0, 8), - LongDecimalField("neworder", 0, 8), - IntField("shares", 0), - IntField("price", 0)] - -class ItchTrade(ItchMessage): - name = "ITCH 4.1 Trade Message Non-Cross" - fields_desc = [ - CharEnumField("header", 'P', message_types), - IntField("nanoseconds", 0), - LongDecimalField("order", 0, 8), - CharEnumField("type", ' ', buy_sell), - IntField("shares", 0), - StrFixedLenField("stock", " ", length=8), - IntField("price", 0), - LongDecimalField("matchnum", 0, 8)] - -class ItchTradeCross(ItchMessage): - name = "ITCH 4.1 Cross Trade Message" - fields_desc = [ - CharEnumField("header", 'P', message_types), - IntField("nanoseconds", 0), - LongDecimalField("shares", 0, 8), - StrFixedLenField("stock", " ", length=8), - IntField("crossprice", 0), - LongDecimalField("matchnum", 0, 8), - CharEnumField("ctype", ' ', cross_type)] - -class ItchBrokenTrade(ItchMessage): - name = "ITCH 4.1 Broken Trade" - fields_desc = [ - CharEnumField("header", 'B', message_types), - IntField("nanoseconds", 0), - LongDecimalField("matchnum", 0, 8)] - -class ItchNOII(ItchMessage): - name = "ITCH 4.1 NOII" - fields_desc = [ - CharEnumField("header", 'B', message_types), - IntField("nanoseconds", 0), - LongDecimalField("paired", 0, 8), - CharEnumField("Imdir", ' ', imbalance_dir), - StrFixedLenField("stock", " ", length=8), - IntField("farprice", 0), - IntField("nearprice", 0), - IntField("currref", 0), - CharEnumField("ctype", ' ', cross_type), - CharEnumField("var", ' ', price_var)] - -class ItchRPII(ItchMessage): - name = "ITCH 4.1 RPII" - fields_desc = [ - CharEnumField("header", 'B', message_types), - IntField("nanoseconds", 0), - StrFixedLenField("stock", " ", length=8), - CharEnumField("intflag", ' ', interest_flag)] diff --git a/cocotb/generators/feeds/itch_feed.py b/cocotb/generators/feeds/itch_feed.py deleted file mode 100644 index 968b99fe..00000000 --- a/cocotb/generators/feeds/itch_feed.py +++ /dev/null @@ -1,67 +0,0 @@ -#! /usr/bin/env python -''' Copyright (c) 2013 Potential Ventures Ltd -Copyright (c) 2013 SolarFlare Communications Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - -# Set log level to benefit from Scapy warnings -import cocotb -from scapy.all import * -from cocotb.generators.feeds import Feed -from cocotb.generators.feeds.moldudp64 import Moldudp64 - -""" Todo - Specify max number of blocks per underlying mold -""" - -class ItchFeed(Feed): - """ Class to represent a steam of Itch4.0 messages """ - - def __init__(self, name="default_itch", session=0, seqnum=0): - """ Session is the session for the current stream - Seqnum is the starting sequence number - """ - Feed.__init__(self, name, None) - self._session = session; - self._seqnum = seqnum - - def addmsg(self, msg): - """ Add a mold64 encapsulated msg to the queue """ - packet = Ether()/IP()/UDP()/Moldudp64(session=self._session, seqnum=self._seqnum, msgblock=[]) - mold = packet.getlayer(Moldudp64) - mold.insertblock(msg) - ret = self._seqnum - self._seqnum += 1 - super(self.__class__, self).addmsg(ret, packet) - return ret - - def appendmsg(self, seqnum, msg): - """ Append another msg to the specified sequence number """ - - def getmsg(self): - return Feed.getmsg(self) - -if __name__ == "__main__": - interact(mydict=globals(), mybanner="ITCH 4.0 packet generator") diff --git a/cocotb/generators/feeds/moldudp64.py b/cocotb/generators/feeds/moldudp64.py deleted file mode 100644 index 9aa051a9..00000000 --- a/cocotb/generators/feeds/moldudp64.py +++ /dev/null @@ -1,89 +0,0 @@ -#! /usr/bin/env python -''' Copyright (c) 2013 Potential Ventures Ltd -Copyright (c) 2013 SolarFlare Communications Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - -# Set log level to benefit from Scapy warnings -import logging -logging.getLogger("scapy").setLevel(1) - -from scapy.all import * -from packet_util import * - -class MsgDataLen(FieldLenField): - def __init__(self, name, default, fld): - FieldLenField.__init__(self, name, default) - self.fld = fld - - def i2len(self, pkt, x): - return len(self.i2len(pkt, x)) - - def i2m(self, pkt, x): - if x is None: - f = pkt.get_field(self.fld) - x = f.i2len(pkt, pkt.getfieldval(self.fld)) - return int_to_packed(x, 16, 8) - - def m2i(self, pkt, x): - if x is None: - return None, 0 - return x[0:2] - - def getfield(self, pkt, s): - i = len(s) - 1 - return s[i:], self.m2i(pkt, s[:i]) - - def addfield(self, pkt, s, val): - return s+self.i2m(pkt, val) - -class MoldCountField(FieldLenField): - def __init__(self, name, length, count): - FieldLenField.__init__(self, name, None, count_of=count) - self.length = length - - def i2m(self, pkt, x): - fld,fval = pkt.getfield_and_val(self.count_of) - f = fld.i2count(pkt, fval) - return int_to_packed(f, self.length * 8, 8) - - def addfield(self, pkt, s, val): - return s+self.i2m(pkt, val) - - -class MessageBlock(Packet): - fields_desc = [ MsgDataLen("length", None, "data"), - StrLenField("data", "", length_from="length") ] - - -class Moldudp64(Packet): - name = "Moldudp64" - fields_desc = [ LongDecimalField("session", 0, 10), - LongDecimalField("seqnum", 0, 8), - MoldCountField("msgcnt", 2, "msgblock"), - PacketListField("msgblock", [], MessageBlock) ] - - def insertblock(self, payload): - self.msgblock.append(MessageBlock(data=payload)) diff --git a/cocotb/generators/feeds/opra_binary.py b/cocotb/generators/feeds/opra_binary.py deleted file mode 100644 index c21d981a..00000000 --- a/cocotb/generators/feeds/opra_binary.py +++ /dev/null @@ -1,223 +0,0 @@ -#! /usr/bin/env python -''' Copyright (c) 2013 Potential Ventures Ltd -Copyright (c) 2013 SolarFlare Communications Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - -# Set log level to benefit from Scapy warnings -import logging -logging.getLogger("scapy").setLevel(1) - -from scapy.all import * - - -# TODO: Full implemenation - -class DenominatorField(): pass - -class OPRAValueField(): - """Representation of a value, adjusted for the appropriate denominator""" - def __init__(self, denominator): - pass - - - - -def hdr_to_msg_len(pkt): - if pkt.header.msg_category == "a": return 35 - if pkt.header.msg_category == "d": return 22 - if pkt.header.msg_category == "f": return 64 - if pkt.header.msg_category == "k": raise Exception("Don't support msg category k yet") - if pkt.header.msg_category == "q": raise Exception("Don't support msg category q yet") - if pkt.header.msg_category == "Y": return 19 - if pkt.header.msg_category == "C": raise Exception("Don't support msg category C yet") - if pkt.header.msg_category == "H": raise Exception("Don't support msg category H yet") - raise Exception("Uknown message category %c" % chr(msg.category)) - -#class OPRAEquityAndIndexLastSale(Packet): - #name = "EILS" - #fields_desc = [ - #OPRABinaryMessageHeader(Header, None), - #StrFixedLenField("symbol", " ", length=5), - #StrFixedLenField("padding", " ", length=26)] - #ByteField("reserved", 0), - #3ByteField("expiration", 0), - #DenominatorField("strike_denom", default), - #OPRAValueField("strike_price", "strike_denom"), - #IntField("volume", 0), - #DenominatorField("premium_denom", default), - #OPRAValueField("premium_price", "premium_denom"), - #IntField("trade_id", 0), - #IntField("reserved1", 0)] - - -class OPRABinaryMessageHeader(Packet): - name = "MSG" - fields_desc = [ - StrFixedLenField("participant_id", " ", length=1), - StrFixedLenField("msg_category", " ", length=1), - StrFixedLenField("msg_type", " ", length=1), - StrFixedLenField("msg_indicator", " ", length=1)] - - -class OPRAMessage(Packet): - fields_desc = [ - OPRABinaryMessageHeader("header", None), - StrFixedLenField("symbol", " ", length=5), - StrLenField("length", "", length_from=hdr_to_msg_len)] # Doesn't actually exist in protocol - - -class OPRABinaryBlock(Packet): - name = "OPRA" - fields_desc = [ - ByteField("version", 0), - ShortField("block_size", 0), - StrFixedLenField("feed_indicator", "O", length=1), - CharEnumField("retransmission_indicator", " ", {" " : "Original", "V": "Retransmitted"}), - ByteField("reserved", 0), - IntField("block_sequence_number", 0), - ByteField("nmsgs", 0), - LongField("timestamp", 0), - XShortField("checksum", 0), - PacketListField("msgs", [], OPRAMessage)] - #FieldLenField("length", None, count_of="msgs")] - - - - - -# The Participant ID field is a 1 Byte, ASCII character that identifies the Participant or Processor that initiated the message -participant_ids = { - 'A' : "NYSE AMEX", - 'B' : "Boston Options Exchange", - 'C' : "Chicago Board Options Exchange", - 'I' : "International Securities Exchange", - 'M' : "Miami International Securities Exchange", - 'N' : "NYSE ARCA", - 'O' : "Options Price Reporting Authority", - 'Q' : "NASDAQ Stock Market", - 'T' : "NASDAQ OMX BX Options*", - 'W' : "C2", - 'X' : "NASDAQ OMX PHLX", - 'Z' : "BATS"} - - - -# The Message Category field is a 1 Byte, ASCII character, either an upper or lower case letter. -message_category_ids = { - 'a' : "EQUITY AND INDEX LAST SALE", - 'd' : "OPEN INTEREST", - 'f' : "EQUITY AND INDEX END OF DAY SUMMARY", - 'k' : "LONG EQUITY AND INDEX QUOTE", - 'q' : "SHORT EQUITY AND INDEX QUOTE", - 'C' : "ADMINISTRATIVE", - 'H' : "CONTROL", - 'Y' : "UNDERLYING VALUE MESSAGE"} - -message_types_category_a = { - " " : "REGULAR" , - "A" : "CANC" , - "B" : "OSEQ" , - "C" : "CNCL" , - "D" : "LATE" , - "E" : "CNCO" , - "F" : "OPEN" , - "G" : "CNOL" , - "H" : "OPNL" , - "I" : "AUTO" , - "J" : "REOP" , - "K" : "AJST" , - "L" : "SPRD" , - "M" : "STDL" , - "N" : "STPD" , - "O" : "CSTP" , - "P" : "BWRT" , - "Q" : "CMBO" , - "R" : "SPIM" , - "S" : "ISOI" , - "T" : "BNMT" , - "X" : "XMPT" ,} - - - -type_2_description = { - "REGULAR" : "Indicates that the transaction was a regular sale and was made without stated conditions.", - "CANC" : "Transaction previously reported (other than as the last or opening report for the particular option contract) is now to be cancelled.", - "OSEQ" : "Transaction is being reported late and is out of sequence; i.e., later transactions have been reported for the particular option contract.", - "CNCL" : "Transaction is the last reported for the particular option contract and is now cancelled.", - "LATE" : "Transaction is being reported late, but is in the correct sequence; i.e., no later transactions have been reported for the particular option contract.", - "CNCO" : "Transaction was the first one (opening) reported this day for the particular option contract. Although later transactions have been reported, this transaction is now to be cancelled.", - "OPEN" : "Transaction is a late report of the opening trade and is out of sequence; i.e., other transactions have been reported for the particular option contract.", - "CNOL" : "Transaction was the only one reported this day for the particular option contract and is now to be cancelled.", - "OPNL" : "Transaction is a late report of the opening trade, but is in the correct sequence; i.e., no other transactions have been reported for the particular option contract.", - "AUTO" : "Transaction was executed electronically. Prefix appears solely for information; process as a regular transaction.", - "REOP" : "Transaction is a reopening of an option contract in which trading has been previously halted. Prefix appears solely for information; process as a regular transaction.", - "AJST" : "Transaction is an option contract for which the terms have been adjusted to reflect a stock dividend, stock split, or similar event. Prefix appears solely for information; process as a regular transaction.", - "SPRD" : "Transaction represents a trade in two options in the same class (a buy and a sell in the same class). Prefix appears solely for information; process as a regular transaction.", - "STDL" : "Transaction represents a trade in two options in the same class (a buy and a sell in a put and a call). Prefix appears solely for information; process as a regular transaction.", - "STPD" : "Transaction is the execution of a sale at a price agreed upon by the floor personnel involved, where a condition of the trade is that it reported following a non-stopped trade of the same series at the same price.", - "CSTP" : "Cancel stopped transaction.", - "BWRT" : "Transaction represents the option portion of an order involving a single option leg (buy or sell of a call or put) and stock. Prefix appears solely for information: process as a regular transaction.", - "CMBO" : "Transaction represents the buying of a call and the selling of a put for the same underlying stock or index. Prefix appears solely for information; process as a regular transaction.", - "SPIM" : "Transaction was the execution of an order which was \"stopped\" at a price that did not constitute a Trade-Through on another market at the time of the stop. Process like a normal transaction except don't update \"last\".", - "ISOI" : "Transaction was the execution of an order identified as an Intermarket Sweep Order. Process like normal transaction", - "BNMT" : "Transaction reflects the execution of a \"benchmark trade\". A \"Benchmark Trade\" is a trade resulting from the matching of \"Benchmark Orders\". A \"Benchmark Order\" is an order for which the price is not based, directly or indirectly, on the quote price of the option at the time of the order's execution and for which the material terms were not reasonably determinable at the time a commitment to trade the order was made. Process like a normal transaction except don't update \"last\".", - "XMPT" : "Transaction is Trade Through Exempt. The transaction should be treated like a regular sale."} - - -message_types_category_h = { - "A" : "Start of Test Cycle", - "B" : "End of Test Cycle", - "C" : "Start of Day", - "D" : "Good Morning", - "E" : "Start of Summary", - "F" : "End of Summary", - "G" : "Early Market Close", - "H" : "End of Transaction Reporting", - "I" : "Good Night", - "J" : "End of Day", - "K" : "Reset Block Sequence Number", - "L" : "Start of Open Interest", - "M" : "End of Open Interest", - "N" : "Line Integrity", - } - -message_types_category_kq = { - " " : "Regular Trading", - "F" : "Non-Firm Quote", - "R" : "Rotation", - "T" : "Trading Halted", - "A" : "Eligible for Automatic Execution", - "B" : "Bid contains Customer Trading Interest", - "O" : "Offer contains Customer Trading Interest", - "C" : "Both Bid and Offer contain Customer Trading Interest", - "X" : "Offer side of Quote Not Firm; Bid Side Firm", - "Y" : "Bid Side of Quote Not Firm; Offer Side Firm", - } - -message_types_category_Y = { - " " : "Index based on Last Sale", - "I" : "Index based on Bid and Offer", - } diff --git a/cocotb/generators/feeds/packet_util.py b/cocotb/generators/feeds/packet_util.py deleted file mode 100644 index 9852bc6d..00000000 --- a/cocotb/generators/feeds/packet_util.py +++ /dev/null @@ -1,96 +0,0 @@ -#! /usr/bin/env python -''' Copyright (c) 2013 Potential Ventures Ltd -Copyright (c) 2013 SolarFlare Communications Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - -from scapy.all import * - -#define all the fields as extensions of the current base classes that need it - -STRUCT_FMT = { - 8 : 'B', # unsigned char - 16 : 'H', # unsigned short - 32 : 'I', # unsigned int -} - -def int_to_words(int_val, num_words=4, word_size=32): - max_int = 2 ** (word_size*num_words) - 1 - max_word_size = 2 ** word_size - 1 - - if not 0 <= int_val <= max_int: - raise IndexError('integer %r is out of bounds!' % hex(int_val)) - - words = [] - for _ in range(num_words): - word = int_val & max_word_size - words.append(int(word)) - int_val >>= word_size - words.reverse() - - return words - -def int_to_packed(int_val, width=128, word_size=32): - num_words = width / word_size - words = int_to_words(int_val, num_words, word_size) - - try: - fmt = '>%d%s' % (num_words, STRUCT_FMT[word_size]) - #DEBUG: print 'format:', fmt - except KeyError: - raise ValueError('unsupported word size: %d!' % word_size) - - return struct.pack(fmt, *words) - -def packed_to_int(packed_int, width=128, word_size=32): - num_words = width / word_size - - try: - fmt = '>%d%s' % (num_words, STRUCT_FMT[word_size]) - #DEBUG: print 'format:', fmt - except KeyError: - raise ValueError('unsupported word size: %d!' % word_size) - - words = list(struct.unpack(fmt, packed_int)) - words.reverse() - - int_val = 0 - for i, num in enumerate(words): - word = num - word = word << word_size * i - int_val = int_val | word - - return int_val - -class LongDecimalField(StrFixedLenField): - def __init__(self, name, default, length): - StrFixedLenField.__init__(self, name, default, length, None) - self.length = length - - def addfield(self, pkt, s, val): - return s + int_to_packed(val, self.length * 8, 8) - - def extract_padding(self, s): - return '', s From a39e6fa6e1612102d2c07728b3877e61d02c2e8e Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 20 Feb 2015 15:10:58 +0000 Subject: [PATCH 0822/2656] Cleanup: Fix linting errors --- cocotb/__init__.py | 2 +- cocotb/drivers/__init__.py | 2 +- cocotb/drivers/amba.py | 9 +++++---- cocotb/generators/packet.py | 5 +++-- cocotb/regression.py | 2 +- cocotb/xunit_reporter.py | 1 + 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 1f5a4cd3..e4665585 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -72,7 +72,7 @@ def mem_debug(port): import cocotb.memdebug - memdebug.start(port) + cocotb.memdebug.start(port) def _initialise_testbench(root_name): """ diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index 832610c3..1f614884 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -147,7 +147,7 @@ def _driver_send(self, transaction, sync=True): subclasses should override this method to implement the actual send routine """ - raise NotImplemented("Subclasses of Driver should define a _driver_send coroutine") + raise NotImplementedError("Subclasses of Driver should define a _driver_send coroutine") @coroutine diff --git a/cocotb/drivers/amba.py b/cocotb/drivers/amba.py index 0b431994..7d25bcb3 100644 --- a/cocotb/drivers/amba.py +++ b/cocotb/drivers/amba.py @@ -309,10 +309,10 @@ def _read_data(self): if __debug__: self.log.debug( - "AWADDR %d\n" % _awaddr + - "AWLEN %d\n" % _awlen + - "AWSIZE %d\n" % _awsize + - "AWBURST %d\n" % _awburst + + "ARADDR %d\n" % _araddr + + "ARLEN %d\n" % _arlen + + "ARSIZE %d\n" % _arsize + + "ARBURST %d\n" % _arburst + "BURST_LENGTH %d\n" % burst_length + "Bytes in beat %d\n" % bytes_in_beat) @@ -333,3 +333,4 @@ def _read_data(self): self.bus.RLAST <= 0 if burst_count == 0: break + diff --git a/cocotb/generators/packet.py b/cocotb/generators/packet.py index 7845170e..29e46c1b 100644 --- a/cocotb/generators/packet.py +++ b/cocotb/generators/packet.py @@ -43,7 +43,7 @@ logging.getLogger("scapy").setLevel(logging.ERROR) from cocotb.decorators import public -from cocotb.generators.byte import * +from cocotb.generators.byte import get_bytes, random_data _default_payload = random_data @@ -54,7 +54,7 @@ def udp_all_sizes(max_size=1500, payload=_default_payload()): header = Ether() / IP() / UDP () for size in range(0, max_size-len(header)): - yield header / _get_payload(size, payload) + yield header / get_bytes(size, payload) @public def udp_random_sizes(npackets=100, payload=_default_payload()): @@ -71,3 +71,4 @@ def ipv4_small_packets(npackets=100, payload=_default_payload()): """Small (<100bytes payload) IPV4 packets""" for pkt in range(npackets): yield Ether() / IP() / get_bytes(random.randint(0, 100), payload) + diff --git a/cocotb/regression.py b/cocotb/regression.py index 3ce0a182..119d4e0a 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -99,7 +99,7 @@ def initialise(self): self._dut = cocotb.handle.SimHandle(simulator.get_root_handle(self._root_name)) if self._dut is None: - raise AttributeError("Can not find Root Handle (%s)" % root_name) + raise AttributeError("Can not find Root Handle (%s)" % self._root_name) # Auto discovery for module_name in self._modules: diff --git a/cocotb/xunit_reporter.py b/cocotb/xunit_reporter.py index e46f7cb7..d70977ea 100644 --- a/cocotb/xunit_reporter.py +++ b/cocotb/xunit_reporter.py @@ -28,6 +28,7 @@ from xml.etree.ElementTree import Element, SubElement import xml.etree.ElementTree as ET +import mmap from io import StringIO TRUNCATE_LINES = 100 From c4a455d2e568f6246d2b871d382cf5bf7ab3a334 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 20 Feb 2015 15:14:27 +0000 Subject: [PATCH 0823/2656] Issue #141: Remove debug trace from modelsim FLI: 300% improvement! --- makefiles/simulators/Makefile.modelsim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index 970823fd..8ee3eb40 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -43,7 +43,7 @@ VSIM_ARGS += -onfinish exit endif ifeq ($(GPI_IMPL),vhpi) -VSIM_ARGS += -foreign \"cocotb_init libfli.so\" -trace_foreign 3 +VSIM_ARGS += -foreign \"cocotb_init libfli.so\" else VSIM_ARGS += -pli libvpi.$(LIB_EXT) endif From 4c07164cc837eb373b22d956c1f338331eaaed72 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Mon, 23 Feb 2015 21:31:00 +0000 Subject: [PATCH 0824/2656] Cleanup (#9): Update documentation to 1.0 * in future should read version file dynamically... --- documentation/source/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/source/conf.py b/documentation/source/conf.py index 24dbc050..ee281624 100644 --- a/documentation/source/conf.py +++ b/documentation/source/conf.py @@ -49,9 +49,9 @@ # built documents. # # The short X.Y version. -version = '0.4' +version = '1.0' # The full version, including alpha/beta/rc tags. -release = '0.4' +release = '1.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From 9d3f44e7addd773072cbc2d3f187d5c122e4c570 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 26 Feb 2015 09:25:03 +0000 Subject: [PATCH 0825/2656] Fixes #217: Depend on the correct variable --- makefiles/simulators/Makefile.questa | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index e31c9717..5e1f9ed3 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -29,7 +29,7 @@ VPI_FILE := $(LIB_DIR)/libvpi.so -results.xml: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_LIB_VPI) +results.xml: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(PYTHON_LIBDIR):$(PYTHON_DYNLIBDIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ qverilog -64 -R -pli $(VPI_FILE) - +acc $(EXTRA_ARGS) $(VERILOG_SOURCES) From 5c5dd6f704097c3f128ad6b033c82f229ab3e819 Mon Sep 17 00:00:00 2001 From: Gordon McGregor Date: Tue, 3 Mar 2015 14:55:36 -0600 Subject: [PATCH 0826/2656] adding vpiNetArray support (same as vpiRegArray) --- include/vpi_user.h | 1 + lib/vpi/VpiImpl.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/include/vpi_user.h b/include/vpi_user.h index 0593d886..2b845290 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -61,6 +61,7 @@ typedef uint32_t *vpiHandle; #define vpiReg 48 /* scalar or vector reg */ #define vpiRegBit 49 /* bit of vector reg */ #define vpiParameter 41 /* module parameter */ +#define vpiNetArray 114 #define vpiRegArray 116 /* multidimensional reg */ #define vpiStructVar 618 #define vpiInterface 601 diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 7e52612d..8effcb6b 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -86,6 +86,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &n case vpiReg: case vpiParameter: case vpiRegArray: + case vpiNetArray: case vpiEnumNet: new_obj = new VpiSignalObjHdl(this, new_hdl); break; From 3e263106826b636d50c21895a1287ea0eacadb6c Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 16 Mar 2015 16:46:58 +0000 Subject: [PATCH 0827/2656] Fixes #216: Add some additional VPI types for unpacked arrays --- include/vpi_user.h | 1 + lib/vpi/VpiImpl.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/include/vpi_user.h b/include/vpi_user.h index 2b845290..6a7a4d9b 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -68,6 +68,7 @@ typedef uint32_t *vpiHandle; #define vpiInterfaceArray 603 #define vpiModport 606 #define vpiRefObj 608 +#define vpiPackedArrayVar 623 #define vpiEnumNet 680 /* SystemVerilog */ #define vpiStop 66 /* execute simulator's $stop */ diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 8effcb6b..c867f9b1 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -84,6 +84,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &n case vpiNet: case vpiNetBit: case vpiReg: + case vpiRegBit: case vpiParameter: case vpiRegArray: case vpiNetArray: @@ -96,6 +97,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &n case vpiModport: case vpiInterfaceArray: case vpiRefObj: + case vpiPackedArrayVar: new_obj = new GpiObjHdl(this, new_hdl); break; default: From 04b6f6fb499c8ce4f14da76043ceb67c8395be57 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 20 Mar 2015 18:06:53 +0000 Subject: [PATCH 0828/2656] Cleanup: Support passing in toplevel in form lib.module --- lib/embed/gpi_embed.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index aeaf1af8..8eb60d34 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -139,6 +139,13 @@ int embed_sim_init(gpi_sim_info_t *info) return -1; } + // Skip any library component of the toplevel + char *dot = strchr(dut, '.'); + if (dot != NULL) { + dut += (dot - dut + 1); + } + + PyObject *cocotb_module, *cocotb_init, *cocotb_args, *cocotb_retval; PyObject *simlog_obj, *simlog_func; PyObject *argv_list, *argc, *arg_dict, *arg_value; From f2c3c4a38ea40ed846f4be726dcfae048d50d575 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Sat, 21 Mar 2015 20:05:47 +0100 Subject: [PATCH 0829/2656] possibility to add args to vsim in questa and clean work --- makefiles/simulators/Makefile.questa | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 5e1f9ed3..5f4c8462 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -31,11 +31,11 @@ VPI_FILE := $(LIB_DIR)/libvpi.so results.xml: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(PYTHON_LIBDIR):$(PYTHON_DYNLIBDIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - qverilog -64 -R -pli $(VPI_FILE) - +acc $(EXTRA_ARGS) $(VERILOG_SOURCES) + qverilog -64 -R -pli $(VPI_FILE) $(VSIM_ARGS) - +acc $(EXTRA_ARGS) $(VERILOG_SOURCES) clean:: -@rm -rf $(SIM_BUILD) - -@rm -rf INCA_libs + -@rm -rf work -@rm -rf qverilog.log From 051a61cdf7f86bd5af09bba33b8918c1ddd438b5 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 22 Mar 2015 17:16:06 +0000 Subject: [PATCH 0830/2656] Cleanup: Add support for waitrequest to AvalonMemory --- cocotb/drivers/avalon.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 7d279549..41f1f2b3 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -170,7 +170,7 @@ class AvalonMemory(BusDriver): Emulate a memory, with back-door access """ _signals = ["address"] - _optional_signals = ["write", "read", "writedata", "readdatavalid", "readdata"] + _optional_signals = ["write", "read", "writedata", "readdatavalid", "readdata", "waitrequest"] def __init__(self, entity, name, clock, readlatency_min=1, readlatency_max=1, memory=None): BusDriver.__init__(self, entity, name, clock) @@ -204,6 +204,9 @@ def __init__(self, entity, name, clock, readlatency_min=1, readlatency_max=1, me if hasattr(self.bus, "readdatavalid"): self.bus.readdatavalid.setimmediatevalue(0) + if hasattr(self.bus, "waitrequest"): + self.bus.waitrequest.setimmediatevalue(0) + def _pad(self): """Pad response queue up to read latency""" l = random.randint(self._readlatency_min, self._readlatency_max) @@ -232,7 +235,7 @@ def _respond(self): self.log.debug("sending 0x%x (%s)" % (self._val.integer, self._val.binstr)) self.bus.readdata <= self._val self.bus.readdatavalid <= 1 - else: + elif hasattr(self.bus, "readdatavalid"): self.bus.readdatavalid <= 0 yield ReadOnly() From 927a5ccec7ebf6e426b5692e5e7bf8ab7d6d4e49 Mon Sep 17 00:00:00 2001 From: blorgon9000 Date: Wed, 1 Apr 2015 00:00:46 -0400 Subject: [PATCH 0831/2656] Enable cosim to build on Mac (Darwin) --- examples/endian_swapper/cosim/Makefile | 10 ++++- include/vpi_user.h | 2 +- lib/utils/cocotb_utils.c | 6 +-- makefiles/Makefile.pylib.Darwin | 58 ++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 makefiles/Makefile.pylib.Darwin diff --git a/examples/endian_swapper/cosim/Makefile b/examples/endian_swapper/cosim/Makefile index 984e9915..aa375c93 100644 --- a/examples/endian_swapper/cosim/Makefile +++ b/examples/endian_swapper/cosim/Makefile @@ -8,6 +8,10 @@ ifeq ($(ARCH),i686) CC += -m32 endif +ifeq ($(OS),Darwin) +PYTHON_LD_FLAGS += -undefined dynamic_lookup +endif + .PHONY: all all: io_module.so _hal.so @@ -20,7 +24,10 @@ io_module.so: io_module.o $(CC) -pthread -shared $< -L$(PYTHON_LIBDIR) $(PYTHON_LD_FLAGS) -o $@ _hal.so: ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so - $(CC) -g -ldl -shared -fPIC -I$(shell pwd) -I$(PYTHON_INCLUDEDIR) -I../hal ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so -o $@ + $(CC) -g -ldl -shared -fPIC -I$(shell pwd) $(PYTHON_LD_FLAGS) -I$(PYTHON_INCLUDEDIR) -I../hal ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so -o $@ + if [ $(OS) = Darwin ]; then \ + install_name_tool -change io_module.so $(PWD)/io_module.so $@; \ + fi endian_swapper_hal_wrap.c: ../hal/endian_swapper_hal.h $(SWIG) -python -outcurrentdir ../hal/endian_swapper_hal.h @@ -28,6 +35,7 @@ endian_swapper_hal_wrap.c: ../hal/endian_swapper_hal.h .PHONY: clean clean: -rm -rf hal.py* + -rm -rf _hal.so.* -rm -rf *.so -rm -rf *.o -rm -rf endian_swapper_hal_wrap.c diff --git a/include/vpi_user.h b/include/vpi_user.h index 6a7a4d9b..9ff6ece2 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -42,7 +42,7 @@ extern "C" { #endif -#if !defined(__linux__) +#if !defined(__linux__) && !defined(__APPLE__) #ifndef VPI_DLLISPEC #define VPI_DLLISPEC __declspec(dllimport) #define VPI_DLL_LOCAL 1 diff --git a/lib/utils/cocotb_utils.c b/lib/utils/cocotb_utils.c index f7b55720..58adcb1b 100644 --- a/lib/utils/cocotb_utils.c +++ b/lib/utils/cocotb_utils.c @@ -30,7 +30,7 @@ #include #include -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) #include #else #include @@ -39,7 +39,7 @@ void* utils_dyn_open(const char* lib_name) { void *ret = NULL; -#ifndef __linux__ +#if ! defined(__linux__) && ! defined(__APPLE__) SetErrorMode(0); ret = LoadLibrary(lib_name); if (!ret) { @@ -60,7 +60,7 @@ void* utils_dyn_open(const char* lib_name) void* utils_dyn_sym(void *handle, const char* sym_name) { void *entry_point; -#ifndef __linux__ +#if ! defined(__linux__) && ! defined(__APPLE__) entry_point = GetProcAddress(handle, sym_name); if (!entry_point) { printf("Unable to find symbol %s\n", sym_name); diff --git a/makefiles/Makefile.pylib.Darwin b/makefiles/Makefile.pylib.Darwin new file mode 100644 index 00000000..a0ba1d5b --- /dev/null +++ b/makefiles/Makefile.pylib.Darwin @@ -0,0 +1,58 @@ +############################################################################## +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +# All common pyhon related rules + +PYTHON_PREFIX = $(shell python-config --prefix) + +NATIVE_ARCH=$(shell uname -m) +ARCH?=$(NATIVE_ARCH) + +# We might work with other Python versions +PYTHON_MAJOR_VERSION?=$(shell python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") +PYTHON_MINOR_VERSION?=$(shell python -c "from __future__ import print_function; import sys; print(sys.version_info.minor)") +PYTHON_VERSION?=$(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') + +PYTHON_LIBDIR:=$(shell python -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("LIBDIR"))') +PYTHON_DYNLIBDIR:=$(shell python -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("DESTSHARED"))') + +# Nasty hack but there's no way in Python properly get the directories for 32-bit Python on a 64-bit system +# TODO: Under OSX we can use "export VERSIONER_PYTHON_PREFER_32_BIT=yes", no Linux equivalent though +ifneq ($(NATIVE_ARCH),$(ARCH)) + PYTHON_LIBDIR:=$(subst 64,,$(PYTHON_LIBDIR)) + PYTHON_DYNLIBDIR:=$(subst 64,,$(PYTHON_DYNLIBDIR)) +endif + +# Since we don't know which modules we might need or whether the simulator +# we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports +# is we simply link against all dynamic libraries in the Python installation +PYLIBS = $(shell python-config --libs) + +PYTHON_INCLUDEDIR := $(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())') +PYTHON_DYN_LIB = libpython$(PYTHON_VERSION).dylib From 598d1c84e7baafca7ffcadf4fd65c9fd5ecb70f7 Mon Sep 17 00:00:00 2001 From: blorgon9000 Date: Wed, 1 Apr 2015 02:18:06 -0400 Subject: [PATCH 0832/2656] Fix Linux hal build --- examples/endian_swapper/cosim/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/endian_swapper/cosim/Makefile b/examples/endian_swapper/cosim/Makefile index aa375c93..c1110354 100644 --- a/examples/endian_swapper/cosim/Makefile +++ b/examples/endian_swapper/cosim/Makefile @@ -16,7 +16,7 @@ endif all: io_module.so _hal.so io_module.o: io.c io_module.h io.h - $(CC) $(GCC_FLAGS) -pthread -fno-strict-aliasing -O2 -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 \ + $(CC) $(GCC_FLAGS) -fPIC -pthread -fno-strict-aliasing -O2 -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 \ -fexceptions --param=ssp-buffer-size=4 -D_GNU_SOURCE \ -fwrapv -DNDEBUG -I$(PYTHON_INCLUDEDIR) -c $< -o $@ @@ -26,7 +26,7 @@ io_module.so: io_module.o _hal.so: ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so $(CC) -g -ldl -shared -fPIC -I$(shell pwd) $(PYTHON_LD_FLAGS) -I$(PYTHON_INCLUDEDIR) -I../hal ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so -o $@ if [ $(OS) = Darwin ]; then \ - install_name_tool -change io_module.so $(PWD)/io_module.so $@; \ + install_name_tool -change io_module.so $(shell pwd)/io_module.so $@; \ fi endian_swapper_hal_wrap.c: ../hal/endian_swapper_hal.h From 94d899b4c75ba494d1fc63f85e961a4e342b454b Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 1 Apr 2015 13:14:28 +0100 Subject: [PATCH 0833/2656] Issue #9: Update documentation expanding on coroutines and add back triggers --- documentation/source/coroutines.rst | 19 +++++++++++++++++-- documentation/source/index.rst | 5 +++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/documentation/source/coroutines.rst b/documentation/source/coroutines.rst index 41fb47e5..c0ec8aa9 100644 --- a/documentation/source/coroutines.rst +++ b/documentation/source/coroutines.rst @@ -15,7 +15,7 @@ when it occurs. For example: @cocotb.coroutine def wait_10ns(): cocotb.log.info("About to wait for 10ns") - yield TimerNS(10) + yield Timer(10000) cocotb.log.info("Simulation time has advanced by 10 ns") Coroutines may also yield other coroutines: @@ -36,4 +36,19 @@ resume if *any* of them fires: @cocotb.coroutine def packet_with_timeout(monitor, timeout): """Wait for a packet but timeout if nothing arrives""" - yield [TimerNS(timeout), monitor.recv()] + yield [Timer(timeout), monitor.wait_for_recv()] + + +The trigger that caused execution to resume is passed back to the coroutine, +allowing them to distinguish which trigger fired: + +.. code-block:: python + + @cocotb.coroutine + def packet_with_timeout(monitor, timeout): + """Wait for a packet but timeout if nothing arrives""" + tout_trigger = Timer(timeout) + result = yield [tout_trigger, monitor.wait_for_recv()] + if result is tout_trigger: + raise TestFailure("Timed out waiting for packet") + diff --git a/documentation/source/index.rst b/documentation/source/index.rst index c2d02ca2..5070e624 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -8,12 +8,13 @@ Contents: introduction quickstart - coroutines building + coroutines + triggers + library_reference endian_swapper ping_tun_tap hal_cosimulation - library_reference roadmap simulator_support From 09141b3c20e19afb1501e8fbc3337f9eed913d67 Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Fri, 10 Apr 2015 16:46:22 +1000 Subject: [PATCH 0834/2656] IUS / VHPI : the cadence simulator needs the function in the shared object explicitly called out on the command line Without this change, a simulation with a vhdl top level + cocotb doesn't do anything. --- makefiles/simulators/Makefile.ius | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 9a205cd4..5ce64b4a 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -46,7 +46,7 @@ ifeq ($(GPI_IMPL),vpi) endif ifeq ($(GPI_IMPL),vhpi) - GPI_ARGS = -loadvhpi $(LIB_DIR)/libvhpi + GPI_ARGS = -loadvhpi $(LIB_DIR)/libvhpi:vhpi_startup_routines_bootstrap #EXTRA_ARGS += -cdslib cds.lib EXTRA_ARGS += -v93 EXTRA_ARGS += -top worklib.$(TOPLEVEL) From 313a120411dff8e2019ae3f0a08c64f327318fc7 Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Fri, 10 Apr 2015 16:48:11 +1000 Subject: [PATCH 0835/2656] endian_swapper : getting the vhdl top level invocation syntax to lineup with the docs The documentation for running the endian_swapper vhdl top level currently the invocation as: make SIM=aldec GPI_IMPL=vhpi I also needed to set TOPLEVEL_LANG=vhdl to make the vhdl example work. i.e: make SIM=ius GPI_IMPL=vhpi TOPLEVEL_LANG=vhdl Seem that we probably don't want to run the verilog top level with the VHPI, so I remove TOPLEVEL_LANG and now use GPI_IMPL as a proxy to select the verilog or vhdl source. --- examples/endian_swapper/tests/Makefile | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index c9760ee1..83cd2df6 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -27,9 +27,8 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### - -# Override this variable to use VHPI instead of VPI -TOPLEVEL_LANG ?= verilog +# Default to verilog +GPI_IMPL ?= vpi WPWD=$(shell sh -c 'pwd -W') PWD=$(shell pwd) @@ -44,20 +43,18 @@ PYTHONPATH := $(PWD)/../cosim:$(PYTHONPATH) endif export PYTHONPATH -ifeq ($(TOPLEVEL_LANG),verilog) +ifeq ($(GPI_IMPL),vpi) VERILOG_SOURCES = $(WPWD)/../hdl/endian_swapper.sv TOPLEVEL = endian_swapper_sv GPI_IMPL=vpi endif -ifeq ($(TOPLEVEL_LANG),vhdl) +ifeq ($(GPI_IMPL),vhpi) VHDL_SOURCES = $(WPWD)/../hdl/endian_swapper.vhdl TOPLEVEL = endian_swapper_vhdl GPI_IMPL=vhpi endif -export TOPLEVEL_LANG - MODULE=test_endian_swapper CUSTOM_COMPILE_DEPS = hal From c1d0d4edca047136397c5074774e9c1c2c414f61 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 10 Apr 2015 09:59:26 +0100 Subject: [PATCH 0836/2656] Workaround for #231: Don't attempt to compile sim_exit tests on VCS --- examples/sim_exit/tests/Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/sim_exit/tests/Makefile b/examples/sim_exit/tests/Makefile index 8272bd85..41a4c31f 100644 --- a/examples/sim_exit/tests/Makefile +++ b/examples/sim_exit/tests/Makefile @@ -41,5 +41,11 @@ COCOTB=$(PWD)/../../.. VERILOG_SOURCES = $(WPWD)/../hdl/close_module.v MODULE=test_closedown +ifneq ($(SIM),vcs) include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim +else +all: + @echo "Skipping test as system call overrides seqfault VCS" +endif + From 3c80349dcce9c53b05ae60579fa598ac55fd493c Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 10 Apr 2015 10:48:21 +0100 Subject: [PATCH 0837/2656] Issue #233: Cleanup exit code processing and remove dependency on xunitparser --- Makefile | 11 +++++++++-- bin/combine_results.py | 19 +++++++++++++++---- bin/report_results.py | 36 ------------------------------------ 3 files changed, 24 insertions(+), 42 deletions(-) delete mode 100755 bin/report_results.py diff --git a/Makefile b/Makefile index 8a20137d..7f13c61d 100644 --- a/Makefile +++ b/Makefile @@ -42,10 +42,17 @@ clean: -@find . -name "results.xml" | xargs rm -rf $(MAKE) -C examples clean -test: +do_tests: $(MAKE) -k -C examples + +# For jenkins we use the exit code to detect compile errors or catestrphic +# failures and the xml to track test results +jenkins: do_tests + ./bin/combine_results.py --squash_rc + +# By default want the exit code to indicate the test results +test: do_tests ./bin/combine_results.py - ./bin/report_results.py combined_results.xml pycode: @cp -R $(SIM_ROOT)/cocotb $(FULL_INSTALL_DIR)/ diff --git a/bin/combine_results.py b/bin/combine_results.py index 5c8d511d..aca20421 100755 --- a/bin/combine_results.py +++ b/bin/combine_results.py @@ -3,11 +3,10 @@ Simple script to combine JUnit test results into a single XML file. Useful for Jenkins. - -TODO: Pretty indentation """ import os +import sys from xml.etree import cElementTree as ET def find_all(name, path): @@ -17,7 +16,7 @@ def find_all(name, path): yield os.path.join(root, name) def main(path, output): - + rc = 0 testsuite = ET.Element("testsuite", name="all", package="all", tests="0") for fname in find_all("results.xml", path): @@ -25,11 +24,23 @@ def main(path, output): for element in tree.getiterator("testcase"): testsuite.append(element) + for child in element: + if child.tag in ["failure", "error"]: + sys.stderr.write("FAILURE: %s.%s\n" % ( + element.attrib["classname"], element.attrib["name"])) + rc = 1 + result = ET.Element("testsuites", name="results") result.append(testsuite) ET.ElementTree(result).write(output, encoding="UTF-8") + return rc if __name__ == "__main__": - main(".", "combined_results.xml") + rc = main(".", "combined_results.xml") + # Suppress exit code if run with any arguments + # Slightly hacky but argparse isnt' in 2.6 + if len(sys.argv) > 1: + sys.exit(0) + sys.exit(rc) diff --git a/bin/report_results.py b/bin/report_results.py deleted file mode 100755 index ff2de2b3..00000000 --- a/bin/report_results.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python -""" -Simple script to report JUnit test results - -""" - -from __future__ import print_function -import sys - -def report_results(xml_filename): - xunitparser = None - try: - import xunitparser - except ImportError as e: - sys.stderr.write("xunitparser module not availble results report not avaliable\n") - sys.stderr.write("Import error was: %s\n" % repr(e)) - - - if xunitparser is not None: - ts, tr = xunitparser.parse(open(xml_filename)) - report = {'skipped': len(tr.skipped), 'failed': len(tr.failures), 'errored': len(tr.errors), 'all': tr.testsRun} - - print('Test Report:', report) - if report['failed'] or report['errored'] or not report['all']: - return 1 - - return 0 - -if __name__ == "__main__": - if len(sys.argv) is not 2: - print("Please specify a result file") - exit(1) - - exit(report_results(sys.argv[1])) - - From d85a4dc2c0ed61367a3309f38b9a48bd0a84737e Mon Sep 17 00:00:00 2001 From: mfkaptan Date: Mon, 30 Mar 2015 00:40:47 +0300 Subject: [PATCH 0838/2656] Numerous style improvements for PEP8 - Issue: #6 Update bus.py for pep8 compliance Added "noqa" - skip this file for flake8 Update decorators.py for pep8 compliance Update binary.py for pep8 compliance Remove unnecessary `pass` statements Update handle.py for pep8 compliance Update __init__.py for pep8 compliance Update log.py for pep8 compliance Update memdebug.py for pep8 compliance Update regression.py for pep8 compliance More updates in regression.py for pep8 compliance Added a little helper function for logging results in regression.py Update result.py for pep8 compliance Update scheduler.py for pep8 compliance Update scoreboard.py for pep8 compliance Update triggers.py for pep8 compliance Update utils.py for pep8 compliance Update wavedrom.py for pep8 compliance Update xunit_reporter.py for pep8 compliance --- cocotb/ANSI.py | 2 +- cocotb/__init__.py | 20 ++-- cocotb/binary.py | 216 ++++++++++++++++++++++----------------- cocotb/bus.py | 32 +++--- cocotb/clock.py | 5 +- cocotb/decorators.py | 70 +++++++------ cocotb/handle.py | 44 ++++---- cocotb/log.py | 40 +++++--- cocotb/memdebug.py | 1 + cocotb/regression.py | 152 ++++++++++++++++----------- cocotb/result.py | 21 +++- cocotb/scheduler.py | 145 +++++++++++++------------- cocotb/scoreboard.py | 76 ++++++++------ cocotb/triggers.py | 87 +++++++++++----- cocotb/utils.py | 134 ++++++++++++------------ cocotb/wavedrom.py | 31 +++--- cocotb/xunit_reporter.py | 23 +++-- 17 files changed, 638 insertions(+), 461 deletions(-) diff --git a/cocotb/ANSI.py b/cocotb/ANSI.py index 17b6e2c3..dd8abc4e 100644 --- a/cocotb/ANSI.py +++ b/cocotb/ANSI.py @@ -28,7 +28,7 @@ ''' Some constants for doing ANSI stuff. ''' - +# flake8: noqa (skip this file for flake8: pypi.python.org/pypi/flake8) _ESCAPE = "\033[" diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 362614d2..efe13e32 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -70,10 +70,12 @@ # FIXME is this really required? _rlock = threading.RLock() + def mem_debug(port): import cocotb.memdebug cocotb.memdebug.start(port) + def _initialise_testbench(root_name): """ This function is called after the simulator has elaborated all @@ -89,7 +91,6 @@ def _initialise_testbench(root_name): if memcheck_port is not None: mem_debug(int(memcheck_port)) - exec_path = os.getenv('SIM_ROOT') if exec_path is None: exec_path = 'Unknown' @@ -98,7 +99,8 @@ def _initialise_testbench(root_name): if version is None: log.info("Unable to determine Cocotb version from %s" % exec_path) else: - log.info("Running tests with Cocotb v%s from %s" % (version, exec_path)) + log.info("Running tests with Cocotb v%s from %s" % + (version, exec_path)) # Create the base handle type @@ -138,6 +140,7 @@ def _initialise_testbench(root_name): _rlock.release() return True + def _sim_event(level, message): """Function that can be called externally to signal an event""" SIM_INFO = 0 @@ -147,11 +150,15 @@ def _sim_event(level, message): if level is SIM_TEST_FAIL: scheduler.log.error("Failing test at simulator request") - scheduler.finish_test(TestFailure("Failure from external source: %s" % message)) + scheduler.finish_test(TestFailure("Failure from external source: %s" % + message)) elif level is SIM_FAIL: - # We simply return here as the simulator will exit so no cleanup is needed - scheduler.log.error("Failing test at simulator request before test run completion: %s" % message) - scheduler.finish_scheduler(SimFailure("Failing test at simulator request before test run completion %s" % message)) + # We simply return here as the simulator will exit + # so no cleanup is needed + msg = ("Failing test at simulator request before test run completion: " + "%s" % message) + scheduler.log.error(msg) + scheduler.finish_scheduler(SimFailure(msg)) else: scheduler.log.error("Unsupported sim event") @@ -171,4 +178,3 @@ def process_plusargs(): plusargs[name] = value else: plusargs[option[1:]] = True - diff --git a/cocotb/binary.py b/cocotb/binary.py index 06124160..0ec47ffb 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -28,24 +28,26 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' from __future__ import print_function -from math import log,ceil +from math import log, ceil from cocotb.utils import get_python_integer_types + def resolve(string): for char in BinaryValue._resolve_to_0: string = string.replace(char, "0") return string + class BinaryRepresentation(): - UNSIGNED = 0 - SIGNED_MAGNITUDE = 1 - TWOS_COMPLEMENT = 2 + UNSIGNED = 0 # noqa + SIGNED_MAGNITUDE = 1 # noqa + TWOS_COMPLEMENT = 2 # noqa class BinaryValue(object): """Represenatation of values in binary format. - The underlying value can be set or accessed using three aliasing attributes, + The underlying value can be set or accessed using three aliasing attributes - BinaryValue.integer is an integer - BinaryValue.signed_integer is a signed integer @@ -64,19 +66,20 @@ class BinaryValue(object): '*' """ - _resolve_to_0 = "xXzZuU" - _permitted_chars = "xXzZuU" + "01" + _resolve_to_0 = "xXzZuU" # noqa + _permitted_chars = "xXzZuU" + "01" # noqa - - def __init__(self, value=None, bits=None, bigEndian=True, binaryRepresentation=BinaryRepresentation.UNSIGNED): + def __init__(self, value=None, bits=None, bigEndian=True, + binaryRepresentation=BinaryRepresentation.UNSIGNED): """ Kwagrs: Value (string or int or long): value to assign to the bus - bits (int): Number of bits to use for the underlying binary representation + bits (int): Number of bits to use for the underlying binary + representation - bigEndian (bool): Interpret the binary as big-endian when converting - to/from a string buffer. + bigEndian (bool): Interpret the binary as big-endian when + converting to/from a string buffer. """ self._str = "" self._bits = bits @@ -87,7 +90,7 @@ def __init__(self, value=None, bits=None, bigEndian=True, binaryRepresentation=B BinaryRepresentation.SIGNED_MAGNITUDE : self._convert_to_signed_mag , BinaryRepresentation.TWOS_COMPLEMENT : self._convert_to_twos_comp , } - + self._convert_from = { BinaryRepresentation.UNSIGNED : self._convert_from_unsigned , BinaryRepresentation.SIGNED_MAGNITUDE : self._convert_from_signed_mag , @@ -113,15 +116,16 @@ def assign(self, value): except ValueError: self.buff = value - def _convert_to_unsigned(self,x): + def _convert_to_unsigned(self, x): x = bin(x) - if x[0]=='-': - raise ValueError('Attempt to assigned negative number to unsigned BinaryValue') + if x[0] == '-': + raise ValueError('Attempt to assigned negative number to unsigned ' + 'BinaryValue') return self._adjust_unsigned(x[2:]) - - def _convert_to_signed_mag(self,x): + + def _convert_to_signed_mag(self, x): x = bin(x) - if x[0]=='-': + if x[0] == '-': binstr = self._adjust_signed_mag('1' + x[3:]) else: binstr = self._adjust_signed_mag('0' + x[2:]) @@ -129,39 +133,38 @@ def _convert_to_signed_mag(self,x): binstr = binstr[::-1] return binstr - def _convert_to_twos_comp(self,x): + def _convert_to_twos_comp(self, x): if x < 0: - ceil_log2 = int(ceil(log(abs(x),2))) - binstr = bin(2**(ceil_log2+1)+x)[2:] + ceil_log2 = int(ceil(log(abs(x), 2))) + binstr = bin(2 ** (ceil_log2 + 1) + x)[2:] binstr = self._adjust_twos_comp(binstr) - pass else: binstr = self._adjust_twos_comp('0' + bin(x)[2:]) if self.big_endian: binstr = binstr[::-1] return binstr - - def _convert_from_unsigned(self,x): + + def _convert_from_unsigned(self, x): return int(resolve(x), 2) - - def _convert_from_signed_mag(self,x): + + def _convert_from_signed_mag(self, x): rv = int(resolve(self._str[1:]), 2) if self._str[0] == '1': rv = rv * -1 return rv - - def _convert_from_twos_comp(self,x): - if x[0]=='1': + + def _convert_from_twos_comp(self, x): + if x[0] == '1': binstr = x[1:] binstr = self._invert(binstr) - rv = int(binstr,2) + 1 + rv = int(binstr, 2) + 1 if x[0] == '1': rv = rv * -1 else: rv = int(resolve(x), 2) return rv - - def _invert(self,x): + + def _invert(self, x): inverted = '' for bit in x: if bit == '0': @@ -171,38 +174,40 @@ def _invert(self,x): else: inverted = inverted + bit return inverted - - def _adjust_unsigned(self,x): + + def _adjust_unsigned(self, x): if self._bits is None: return x l = len(x) if l <= self._bits: if self.big_endian: - rv = x + '0' * (self._bits-l) + rv = x + '0' * (self._bits - l) else: - rv = '0' * (self._bits-l) + x + rv = '0' * (self._bits - l) + x elif l > self._bits: - print("WARNING truncating value to match requested number of bits (%d -> %d)" % (l,self._bits)) + print("WARNING truncating value to match requested number of bits " + "(%d -> %d)" % (l, self._bits)) if self.big_endian: rv = x[l - self._bits:] else: rv = x[:l - self._bits] return rv - def _adjust_signed_mag(self,x): + def _adjust_signed_mag(self, x): """Pad/truncate the bit string to the correct length""" if self._bits is None: return x l = len(x) if l <= self._bits: if self.big_endian: - rv = x[:-1] + '0' * (self._bits-1-l) + rv = x[:-1] + '0' * (self._bits - 1 - l) rv = rv + x[-1] else: - rv = '0' * (self._bits-1-l) + x[1:] + rv = '0' * (self._bits - 1 - l) + x[1:] rv = x[0] + rv elif l > self._bits: - print("WARNING truncating value to match requested number of bits (%d -> %d)" % (l,self._bits)) + print("WARNING truncating value to match requested number of bits " + "(%d -> %d)" % (l, self._bits)) if self.big_endian: rv = x[l - self._bits:] else: @@ -210,22 +215,23 @@ def _adjust_signed_mag(self,x): else: rv = x return rv - - def _adjust_twos_comp(self,x): + + def _adjust_twos_comp(self, x): if self._bits is None: return x l = len(x) if l <= self._bits: if self.big_endian: - rv = x + x[-1] * (self._bits-l) + rv = x + x[-1] * (self._bits - l) else: - rv = x[0] * (self._bits-l) + x + rv = x[0] * (self._bits - l) + x elif l > self._bits: - print("WARNING truncating value to match requested number of bits (%d -> %d)" % (l,self._bits)) + print("WARNING truncating value to match requested number of bits " + "(%d -> %d)" % (l, self._bits)) if self.big_endian: rv = x[l - self._bits:] else: - rv = x[:-(l-self._bits)] + rv = x[:-(l - self._bits)] else: rv = x return rv @@ -242,14 +248,17 @@ def get_value_signed(self): if (ival & signbit) == 0: return ival else: - return -1 * (1+(int(~ival) & (signbit - 1))) + return -1 * (1 + (int(~ival) & (signbit - 1))) def set_value(self, integer): self._str = self._convert_to[self.binaryRepresentation](integer) - value = property(get_value, set_value, None, "Integer access to the value *** deprecated ***") - integer = property(get_value, set_value, None, "Integer access to the value") - signed_integer = property(get_value_signed, set_value, None, "Signed integer access to the value") + value = property(get_value, set_value, None, + "Integer access to the value *** deprecated ***") + integer = property(get_value, set_value, None, + "Integer access to the value") + signed_integer = property(get_value_signed, set_value, None, + "Signed integer access to the value") def get_buff(self): """Attribute self.buff represents the value as a binary string buffer @@ -274,7 +283,7 @@ def get_buff(self): def get_hex_buff(self): bstr = self.get_buff() hstr = '%0*X' % ((len(bstr) + 3) // 4, int(bstr, 2)) - return hstr + return hstr def set_buff(self, buff): self._str = "" @@ -292,33 +301,38 @@ def _adjust(self): l = len(self._str) if l < self._bits: if self.big_endian: - self._str = self._str + "0" * (self._bits-l) + self._str = self._str + "0" * (self._bits - l) else: - self._str = "0" * (self._bits-l) + self._str + self._str = "0" * (self._bits - l) + self._str elif l > self._bits: - print("WARNING truncating value to match requested number of bits (%d)" % l) + print("WARNING truncating value to match requested number of bits " + " (%d)" % l) self._str = self._str[l - self._bits:] - buff = property(get_buff, set_buff, None, "Access to the value as a buffer") + buff = property(get_buff, set_buff, None, + "Access to the value as a buffer") def get_binstr(self): - """Attribute binstr is the binary representation stored as a string of 1s and 0s""" + """Attribute binstr is the binary representation stored as a string of + 1s and 0s""" return self._str def set_binstr(self, string): for char in string: if char not in BinaryValue._permitted_chars: - raise ValueError("Attempting to assign character %s to a %s" % (char, self.__class__.__name__)) + raise ValueError("Attempting to assign character %s to a %s" % + (char, self.__class__.__name__)) self._str = string self._adjust() - binstr = property(get_binstr, set_binstr, None, "Access to the binary string") + binstr = property(get_binstr, set_binstr, None, + "Access to the binary string") def hex(self): try: return hex(self.get_value()) except: - return hex(int(self.binstr,2)) + return hex(int(self.binstr, 2)) def __le__(self, other): self.assign(other) @@ -343,7 +357,8 @@ def __nonzero__(self): """ for char in self._str: - if char == "1": return True + if char == "1": + return True return False def __cmp__(self, other): @@ -408,7 +423,7 @@ def __irshift__(self, other): """Preserves X values""" self.binstr = self.binstr[-other:] + self.binstr[:-other] return self - + def __invert__(self): """Preserves X values""" self.binstr = self._invert(self.binstr) @@ -416,79 +431,88 @@ def __invert__(self): def __len__(self): return len(self.binstr) - + def __getitem__(self, key): - ''' BinaryValue uses verilog/vhdl style slices as opposed to python style''' + ''' BinaryValue uses verilog/vhdl style slices as opposed to python + style''' if isinstance(key, slice): first, second = key.start, key.stop if self.big_endian: if first < 0 or second < 0: - raise IndexError('BinaryValue does not support negative indices') - if second > self._bits-1: + raise IndexError('BinaryValue does not support negative ' + 'indices') + if second > self._bits - 1: raise IndexError('High index greater than number of bits.') if first > second: - raise IndexError('Big Endian indices must be specified low to high') - _binstr = self.binstr[first:(second+1)] - pass + raise IndexError('Big Endian indices must be specified ' + 'low to high') + _binstr = self.binstr[first:(second + 1)] else: if first < 0 or second < 0: - raise IndexError('BinaryValue does not support negative indices') - if first > self._bits-1: + raise IndexError('BinaryValue does not support negative ' + 'indices') + if first > self._bits - 1: raise IndexError('High index greater than number of bits.') if second > first: - raise IndexError('Litte Endian indices must be specified high to low') + raise IndexError('Litte Endian indices must be specified ' + 'high to low') high = self._bits - second - low = self._bits - 1 - first + low = self._bits - 1 - first _binstr = self.binstr[low:high] - pass else: index = key - if index > self._bits-1: + if index > self._bits - 1: raise IndexError('Index greater than number of bits.') _binstr = self.binstr[index] - rv = BinaryValue(bits=len(_binstr),bigEndian=self.big_endian,binaryRepresentation=self.binaryRepresentation) + rv = BinaryValue(bits=len(_binstr), bigEndian=self.big_endian, + binaryRepresentation=self.binaryRepresentation) rv.set_binstr(_binstr) return rv - + def __setitem__(self, key, val): - ''' BinaryValue uses verilog/vhdl style slices as opposed to python style''' - if not isinstance(val,str): + ''' BinaryValue uses verilog/vhdl style slices as opposed to python + style''' + if not isinstance(val, str): raise TypeError('BinaryValue slices only accept string values') if isinstance(key, slice): first, second = key.start, key.stop if self.big_endian: if first < 0 or second < 0: - raise IndexError('BinaryValue does not support negative indices') - if second > self._bits-1: + raise IndexError('BinaryValue does not support negative ' + 'indices') + if second > self._bits - 1: raise IndexError('High index greater than number of bits.') if first > second: - raise IndexError('Big Endian indices must be specified low to high') - if len(val) > (second+1-first): - raise ValueError("String length must be equal to slice length") + raise IndexError('Big Endian indices must be specified ' + 'low to high') + if len(val) > (second + 1 - first): + raise ValueError('String length must be equal to slice ' + 'length') slice_1 = self.binstr[:first] - slice_2 = self.binstr[second+1:] + slice_2 = self.binstr[second + 1:] self.binstr = slice_1 + val + slice_2 - pass else: if first < 0 or second < 0: - raise IndexError('BinaryValue does not support negative indices') - if first > self._bits-1: + raise IndexError('BinaryValue does not support negative ' + 'indices') + if first > self._bits - 1: raise IndexError('High index greater than number of bits.') if second > first: - raise IndexError('Litte Endian indices must be specified high to low') + raise IndexError('Litte Endian indices must be specified ' + 'high to low') high = self._bits - second - low = self._bits - 1 - first + low = self._bits - 1 - first if len(val) > (high - low): - raise ValueError("String length must be equal to slice length") - slice_1 = self.binstr[:low] + raise ValueError('String length must be equal to slice ' + 'length') + slice_1 = self.binstr[:low] slice_2 = self.binstr[high:] self.binstr = slice_1 + val + slice_2 - pass else: index = key - if index > self._bits-1: + if index > self._bits - 1: raise IndexError('Index greater than number of bits.') - self.binstr = self.binstr[:index] + val + self.binstr[index+1:] + self.binstr = self.binstr[:index] + val + self.binstr[index + 1:] if __name__ == "__main__": import doctest diff --git a/cocotb/bus.py b/cocotb/bus.py index 2b5abf57..6b7f5596 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -34,6 +34,7 @@ """ from cocotb.result import TestError + class Bus(object): """ Wraps up a collection of signals @@ -44,7 +45,7 @@ class Bus(object): for example a bus named "stream_in" with signals ["valid", "data"] dut.stream_in_valid - dut.stream_in_data + dut.stream_in_data TODO: Support for struct/record ports where signals are member names @@ -52,11 +53,13 @@ class Bus(object): def __init__(self, entity, name, signals, optional_signals=[]): """ Args: - entity (SimHandle): SimHandle instance to the entity containing the bus - name (str): name of the bus. None for nameless bus, e.g. - bus-signals in an interface or a modport - (untested on struct/record, but could work here as well) - signals (list): array of signal names + entity (SimHandle): SimHandle instance to the entity containing the + bus + name (str): name of the bus. None for nameless bus, e.g. + bus-signals in an interface or a modport + (untested on struct/record, but could work here + as well) + signals (list): array of signal names Kwargs: optiona_signals (list): array of optional signal names @@ -86,16 +89,16 @@ def __init__(self, entity, name, signals, optional_signals=[]): setattr(self, signal, hdl) self._signals[signal] = getattr(self, signal) else: - self._entity.log.debug("Ignoring optional missing signal %s on bus %s" - % (signal, name)) - + self._entity.log.debug("Ignoring optional missing signal " + "%s on bus %s" % (signal, name)) def drive(self, obj, strict=False): """ Drives values onto the bus. Args: - obj (any type) : object with attribute names that match the bus signals + obj (any type) : object with attribute names that match the bus + signals Kwargs: strict (bool) : Check that all signals are being assigned @@ -106,9 +109,12 @@ def drive(self, obj, strict=False): for name, hdl in self._signals.items(): if not hasattr(obj, name): if strict: - raise AttributeError("Unable to drive onto %s.%s because %s is missing attribute %s" % - (self._entity.name, self._name, obj.__class__.__name__, name)) - else: continue + msg = ("Unable to drive onto %s.%s because %s is missing " + "attribute %s" % self._entity.name, self._name, + obj.__class__.__name__, name) + raise AttributeError(msg) + else: + continue val = getattr(obj, name) hdl <= val diff --git a/cocotb/clock.py b/cocotb/clock.py index 2cc57115..27283b7e 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -33,11 +33,14 @@ from cocotb.log import SimLog from cocotb.triggers import Timer, RisingEdge + class BaseClock(object): """Base class to derive from""" def __init__(self, signal): self.signal = signal - self.log = SimLog("cocotb.%s.%s" % (self.__class__.__name__, self.signal.name)) + self.log = SimLog("cocotb.%s.%s" % + (self.__class__.__name__, self.signal.name)) + class Clock(BaseClock): """ diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 151a3989..4d462061 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -37,21 +37,22 @@ import cocotb from cocotb.log import SimLog from cocotb.triggers import _Join, PythonTrigger, Timer, Event, NullTrigger -from cocotb.result import TestComplete, TestError, TestFailure, TestSuccess, ReturnValue, raise_error +from cocotb.result import (TestComplete, TestError, TestFailure, TestSuccess, + ReturnValue, raise_error) def public(f): - """Use a decorator to avoid retyping function/class names. - - * Based on an idea by Duncan Booth: - http://groups.google.com/group/comp.lang.python/msg/11cbb03e09611b8a - * Improved via a suggestion by Dave Angel: - http://groups.google.com/group/comp.lang.python/msg/3d400fb22d8a42e1 - """ - all = sys.modules[f.__module__].__dict__.setdefault('__all__', []) - if f.__name__ not in all: # Prevent duplicates if run from an IDE. - all.append(f.__name__) - return f + """Use a decorator to avoid retyping function/class names. + + * Based on an idea by Duncan Booth: + http://groups.google.com/group/comp.lang.python/msg/11cbb03e09611b8a + * Improved via a suggestion by Dave Angel: + http://groups.google.com/group/comp.lang.python/msg/3d400fb22d8a42e1 + """ + all = sys.modules[f.__module__].__dict__.setdefault('__all__', []) + if f.__name__ not in all: # Prevent duplicates if run from an IDE. + all.append(f.__name__) + return f public(public) # Emulate decorating ourself @@ -59,9 +60,10 @@ def public(f): @public class CoroutineComplete(StopIteration): """ - To ensure that a coroutine has completed before we fire any triggers that - are blocked waiting for the coroutine to end, we create a subclass exception - that the Scheduler catches and the callbacks are attached here. + To ensure that a coroutine has completed before we fire any triggers + that are blocked waiting for the coroutine to end, we create a subclass + exception that the Scheduler catches and the callbacks are attached + here. """ def __init__(self, text="", callback=None): StopIteration.__init__(self, text) @@ -74,9 +76,11 @@ class RunningCoroutine(object): Provides the following: - coro.join() creates a Trigger that will fire when this coroutine completes + coro.join() creates a Trigger that will fire when this coroutine + completes - coro.kill() will destroy a coroutine instance (and cause any Join triggers to fire + coro.kill() will destroy a coroutine instance (and cause any Join + triggers to fire """ def __init__(self, inst, parent): if hasattr(inst, "__name__"): @@ -95,8 +99,8 @@ def __init__(self, inst, parent): self.retval = None if not hasattr(self._coro, "send"): - self.log.error("%s isn't a value coroutine! Did you use the yield keyword?" - % self.funcname) + self.log.error("%s isn't a value coroutine! Did you use the yield " + "keyword?" % self.funcname) raise CoroutineComplete(callback=self._finished_cb) def __iter__(self): @@ -136,7 +140,8 @@ def kill(self): def _finished_cb(self): """Called when the coroutine completes. - Allows us to mark the coroutine as finished so that boolean testing works. + Allows us to mark the coroutine as finished so that boolean testing + works. Also call any callbacks, usually the result of coroutine.join()""" self._finished = True @@ -162,7 +167,6 @@ def __init__(self, fn): def handle(self, record): self.fn(self.format(record)) - def __init__(self, inst, parent): self.error_messages = [] RunningCoroutine.__init__(self, inst, parent) @@ -178,7 +182,8 @@ def __init__(self, inst, parent): def send(self, value): if not self.started: self.error_messages = [] - self.log.info("Starting test: \"%s\"\nDescription: %s" % (self.funcname, self.__doc__)) + self.log.info("Starting test: \"%s\"\nDescription: %s" % + (self.funcname, self.__doc__)) self.start_time = time.time() self.started = True try: @@ -189,10 +194,10 @@ def send(self, value): self.log.warning(str(e)) else: self.log.info(str(e)) - - buff = StringIO(); + + buff = StringIO() for message in self.error_messages: - print(message, file=buff) + print(message, file=buff) e.stderr.write(buff.getvalue()) raise except StopIteration: @@ -238,11 +243,13 @@ def __get__(self, obj, type=None): and standalone functions""" return self.__class__(self._func.__get__(obj, type)) - def __iter__(self): return self + def __iter__(self): + return self def __str__(self): return str(self._func.__name__) + @public class function(object): """Decorator class that allows a a function to block @@ -255,7 +262,6 @@ def __init__(self, func): self._func = func self.log = SimLog("cocotb.function.%s" % self._func.__name__, id(self)) - def __call__(self, *args, **kwargs): @coroutine @@ -275,11 +281,13 @@ def __get__(self, obj, type=None): and standalone functions""" return self.__class__(self._func.__get__(obj, type)) + @function def unblock_external(bridge): yield NullTrigger() bridge.set_out() + @public class test_locker(object): def __init__(self): @@ -293,6 +301,7 @@ def set_in(self): def set_out(self): self.out_event.set() + def external(func): """Decorator to apply to an external function to enable calling from cocotb @@ -311,7 +320,8 @@ def execute_external(func, _event): unblock_external(_event) thread = threading.Thread(group=None, target=execute_external, - name=func.__name__ + "thread", args=([func, bridge]), kwargs={}) + name=func.__name__ + "thread", + args=([func, bridge]), kwargs={}) thread.start() yield bridge.out_event.wait() @@ -321,6 +331,7 @@ def execute_external(func, _event): return wrapped + @public class test(coroutine): """Decorator to mark a function as a test @@ -340,7 +351,8 @@ class test(coroutine): skip: (bool): Don't execute this test as part of the regression """ - def __init__(self, timeout=None, expect_fail=False, expect_error=False, skip=False): + def __init__(self, timeout=None, expect_fail=False, expect_error=False, + skip=False): self.timeout = timeout self.expect_fail = expect_fail self.expect_error = expect_error diff --git a/cocotb/handle.py b/cocotb/handle.py index 13e13c11..b0952cb4 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -33,7 +33,7 @@ import ctypes import traceback import sys -#from StringIO import StringIO +# from StringIO import StringIO from io import StringIO, BytesIO @@ -52,6 +52,7 @@ from cocotb.triggers import _RisingEdge, _FallingEdge from cocotb.utils import get_python_integer_types + class SimHandle(object): def __init__(self, handle): @@ -59,12 +60,13 @@ def __init__(self, handle): Args: _handle [integer] : vpi/vhpi handle to the simulator object """ - self._handle = handle # handle used for future simulator transactions - self._sub_handles = {} # Dictionary of SimHandle objects created by getattr + self._handle = handle # handle used for future simulator transactions + self._sub_handles = {} # Dict. of SimHandle objects created by getattr self._len = None self.name = simulator.get_name_string(self._handle) - self.fullname = self.name + '(%s)' % simulator.get_type_string(self._handle) + self.fullname = '%s(%s)' % (self.name, + simulator.get_type_string(self._handle)) self.log = SimLog('cocotb.' + self.name) self.log.debug("Created!") self._r_edge = _RisingEdge(self) @@ -84,7 +86,8 @@ def __getattr__(self, name): return self._sub_handles[name] new_handle = simulator.get_handle_by_name(self._handle, name) if not new_handle: - self._raise_testerror("%s contains no object named %s" % (self.name, name)) + self._raise_testerror("%s contains no object named %s" % + (self.name, name)) self._sub_handles[name] = SimHandle(new_handle) return self._sub_handles[name] @@ -105,8 +108,9 @@ def _raise_testerror(self, msg): def __setattr__(self, name, value): """Provide transparent access to signals""" - if not name.startswith('_') and not name in ["name", "fullname", "log", "value"] \ - and self.__hasattr__(name): + if (not name.startswith('_') and + not name in ["name", "fullname", "log", "value"] and + self.__hasattr__(name)): getattr(self, name).setcachedvalue(value) return object.__setattr__(self, name, value) @@ -132,7 +136,8 @@ def __getitem__(self, index): return self._sub_handles[index] new_handle = simulator.get_handle_by_index(self._handle, index) if not new_handle: - self._raise_testerror("%s contains no object at index %d" % (self.name, index)) + self._raise_testerror("%s contains no object at index %d" % + (self.name, index)) self._sub_handles[index] = SimHandle(new_handle) return self._sub_handles[index] @@ -163,7 +168,8 @@ def setimmediatevalue(self, value): Assigning integers less than 32-bits is faster """ - if isinstance(value, get_python_integer_types()) and value < 0x7fffffff: + if (isinstance(value, get_python_integer_types()) and + value < 0x7fffffff): simulator.set_signal_val(self._handle, value) return @@ -172,8 +178,10 @@ def setimmediatevalue(self, value): elif isinstance(value, get_python_integer_types()): value = BinaryValue(value=value, bits=len(self), bigEndian=False) elif not isinstance(value, BinaryValue): - self.log.critical("Unsupported type for value assignment: %s (%s)" % (type(value), repr(value))) - raise TypeError("Unable to set simulator value with type %s" % (type(value))) + self.log.critical("Unsupported type for value assignment: %s (%s)" + % (type(value), repr(value))) + raise TypeError("Unable to set simulator value with type %s" % + (type(value))) simulator.set_signal_val_str(self._handle, value.binstr) @@ -185,8 +193,10 @@ def setcachedvalue(self, value): sim time""" cocotb.scheduler.save_write(self, value) - # We want to maintain compatability with python 2.5 so we can't use @property with a setter - value = property(getvalue, setcachedvalue, None, "A reference to the value") + # We want to maintain compatability with python 2.5 so we can't use + # @property with a setter + value = property(getvalue, setcachedvalue, None, + "A reference to the value") def _get_value_str(self): return simulator.get_signal_val(self._handle) @@ -198,7 +208,6 @@ def __le__(self, value): """ self.value = value - def __len__(self): """Returns the 'length' of the underlying object. @@ -210,18 +219,17 @@ def __len__(self): self._len = len(self._get_value_str()) return self._len - def __cmp__(self, other): # Permits comparison of handles i.e. if clk == dut.clk if isinstance(other, SimHandle): - if self._handle == other._handle: return 0 + if self._handle == other._handle: + return 0 return 1 # Use the comparison method of the other object against our value return self.value.__cmp__(other) - def __iter__(self): """Iterates over all known types defined by simulator module""" for handle_type in [simulator.MODULE, @@ -241,5 +249,3 @@ def __iter__(self): def __int__(self): return int(self.value) - - diff --git a/cocotb/log.py b/cocotb/log.py index 8cd08fac..37eabaf7 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -43,18 +43,19 @@ from pdb import set_trace # Column alignment -_LEVEL_CHARS = len("CRITICAL") -_RECORD_CHARS = 35 -_FILENAME_CHARS = 20 -_LINENO_CHARS = 4 -_FUNCNAME_CHARS = 31 +_LEVEL_CHARS = len("CRITICAL") # noqa +_RECORD_CHARS = 35 # noqa +_FILENAME_CHARS = 20 # noqa +_LINENO_CHARS = 4 # noqa +_FUNCNAME_CHARS = 31 # noqa + class SimBaseLog(logging.getLoggerClass()): def __init__(self, name): hdlr = logging.StreamHandler(sys.stdout) want_ansi = os.getenv("COCOTB_ANSI_OUTPUT") if want_ansi is None: - want_ansi = sys.stdout.isatty() # default to ANSI for TTYs + want_ansi = sys.stdout.isatty() # default to ANSI for TTYs else: want_ansi = want_ansi == '1' if want_ansi: @@ -73,6 +74,7 @@ def __init__(self, name): """ Need to play with this to get the path of the called back, construct our own makeRecord for this """ + class SimLog(object): def __init__(self, name, ident=None): self._ident = ident @@ -151,16 +153,18 @@ class SimLogFormatter(logging.Formatter): # Justify and truncate @staticmethod def ljust(string, chars): - if len(string) > chars: return ".." + string[(chars-2)*-1:] + if len(string) > chars: + return ".." + string[(chars - 2) * -1:] return string.ljust(chars) @staticmethod def rjust(string, chars): - if len(string) > chars: return ".." + string[(chars-2)*-1:] + if len(string) > chars: + return ".." + string[(chars - 2) * -1:] return string.rjust(chars) def _format(self, timeh, timel, level, record, msg): - simtime = "% 6d.%02dns" % ((timel/1000), (timel%1000)/10) + simtime = "% 6d.%02dns" % ((timel / 1000), (timel % 1000) / 10) prefix = simtime + ' ' + level + ' ' + \ self.ljust(record.name, _RECORD_CHARS) + \ self.rjust(os.path.split(record.filename)[1], _FILENAME_CHARS) + \ @@ -170,11 +174,12 @@ def _format(self, timeh, timel, level, record, msg): pad = "\n" + " " * (len(prefix)) return prefix + pad.join(msg.split('\n')) - def format(self, record): """pretify the log output, annotate with simulation time""" - if record.args: msg = record.msg % record.args - else: msg = record.msg + if record.args: + msg = record.msg % record.args + else: + msg = record.msg msg = str(msg) level = record.levelname.ljust(_LEVEL_CHARS) @@ -183,7 +188,6 @@ def format(self, record): return self._format(timeh, timel, level, record, msg) - class SimColourLogFormatter(SimLogFormatter): """Log formatter to provide consistent log message handling.""" @@ -198,12 +202,14 @@ class SimColourLogFormatter(SimLogFormatter): def format(self, record): """pretify the log output, annotate with simulation time""" - if record.args: msg = record.msg % record.args - else: msg = record.msg + if record.args: + msg = record.msg % record.args + else: + msg = record.msg msg = SimColourLogFormatter.loglevel2colour[record.levelno] % msg - level = SimColourLogFormatter.loglevel2colour[record.levelno] % \ - record.levelname.ljust(_LEVEL_CHARS) + level = (SimColourLogFormatter.loglevel2colour[record.levelno] % + record.levelname.ljust(_LEVEL_CHARS)) timeh, timel = simulator.get_sim_time() return self._format(timeh, timel, level, record, msg) diff --git a/cocotb/memdebug.py b/cocotb/memdebug.py index 35b0ab11..bee3bf3e 100644 --- a/cocotb/memdebug.py +++ b/cocotb/memdebug.py @@ -28,6 +28,7 @@ import cherrypy import dowser + def start(port): cherrypy.tree.mount(dowser.Root()) cherrypy.config.update({ diff --git a/cocotb/regression.py b/cocotb/regression.py index 119d4e0a..3bbce0b3 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -47,8 +47,10 @@ try: import coverage except ImportError as e: - sys.stderr.write("Coverage collection requested but coverage module not availble\n") - sys.stderr.write("Import error was: %s\n" % repr(e)) + msg = ("Coverage collection requested but coverage module not availble" + "\n" + "Import error was: %s\n" % repr(e)) + sys.stderr.write(msg) import cocotb import cocotb.ANSI as ANSI @@ -56,6 +58,7 @@ from cocotb.result import TestError, TestFailure, TestSuccess, SimFailure from cocotb.xunit_reporter import XUnitReporter + def _my_import(name): mod = __import__(name) components = name.split('.') @@ -90,16 +93,19 @@ def initialise(self): self.skipped = 0 self.failures = 0 self.xunit = XUnitReporter() - self.xunit.add_testsuite(name="all", tests=repr(self.ntests), package="all") + self.xunit.add_testsuite(name="all", tests=repr(self.ntests), + package="all") if coverage is not None: self.log.info("Enabling coverage collection of Python code") self._cov = coverage.coverage(branch=True, omit=["*cocotb*"]) self._cov.start() - self._dut = cocotb.handle.SimHandle(simulator.get_root_handle(self._root_name)) + self._dut = cocotb.handle.SimHandle(simulator.get_root_handle( + self._root_name)) if self._dut is None: - raise AttributeError("Can not find Root Handle (%s)" % self._root_name) + raise AttributeError("Can not find Root Handle (%s)" % + self._root_name) # Auto discovery for module_name in self._modules: @@ -111,7 +117,7 @@ def initialise(self): for test in self._functions.rsplit(','): if not hasattr(module, test): raise AttributeError("Test %s doesn't exist in %s" % - (test, module_name)) + (test, module_name)) self._queue.append(getattr(module, test)(self._dut)) self.ntests += 1 @@ -124,32 +130,36 @@ def initialise(self): skip = test.skip except TestError: skip = True - self.log.warning("Failed to initialise test %s" % thing.name) + self.log.warning("Failed to initialise test %s" % + thing.name) if skip: self.log.info("Skipping test %s" % thing.name) - self.xunit.add_testcase(name=thing.name, classname=module_name, time="0.0") + self.xunit.add_testcase(name=thing.name, + classname=module_name, + time="0.0") self.xunit.add_skipped() - self.skipped += 1 + self.skipped += 1 else: self._queue.append(test) self.ntests += 1 - self._queue.sort(key=lambda test: "%s.%s" % (test.module, test.funcname)) + self._queue.sort(key=lambda test: "%s.%s" % + (test.module, test.funcname)) for valid_tests in self._queue: self.log.info("Found test %s.%s" % - (valid_tests.module, - valid_tests.funcname)) + (valid_tests.module, + valid_tests.funcname)) def tear_down(self): """It's the end of the world as we know it""" if self.failures: self.log.error("Failed %d out of %d tests (%d skipped)" % - (self.failures, self.count -1, self.skipped)) + (self.failures, self.count - 1, self.skipped)) else: - self.log.info("Passed %d tests (%d skipped)" % - (self.count-1, self.skipped)) + self.log.info("Passed %d tests (%d skipped)" % + (self.count - 1, self.skipped)) if self._cov: self._cov.stop() self.log.info("Writing coverage data") @@ -159,13 +169,12 @@ def tear_down(self): self.xunit.write() simulator.stop_simulator() - def next_test(self): """Get the next test to run""" - if not self._queue: return None + if not self._queue: + return None return self._queue.pop(0) - def handle_result(self, result): """Handle a test result @@ -173,47 +182,63 @@ def handle_result(self, result): Args: result (TestComplete exception) """ - self.xunit.add_testcase(name =self._running_test.funcname, + self.xunit.add_testcase(name=self._running_test.funcname, classname=self._running_test.module, - time=repr(time.time() - self._running_test.start_time) ) - - if isinstance(result, TestSuccess) and not self._running_test.expect_fail and not self._running_test.expect_error: - self.log.info("Test Passed: %s" % self._running_test.funcname) - - elif isinstance(result, TestFailure) and self._running_test.expect_fail: - self.log.info("Test failed as expected: %s (result was %s)" % ( - self._running_test.funcname, result.__class__.__name__)) - - elif isinstance(result, TestSuccess) and self._running_test.expect_error: - self.log.error("Test passed but we expected an error: %s (result was %s)" % ( - self._running_test.funcname, result.__class__.__name__)) - self.xunit.add_failure(stdout=repr(str(result)), stderr="\n".join(self._running_test.error_messages)) + time=repr(time.time() - + self._running_test.start_time)) + + running_test_funcname = self._running_test.funcname + + # Helper for logging result + def _result_was(): + result_was = ("%s (result was %s)" % + (running_test_funcname, result.__class__.__name__)) + return result_was + + if (isinstance(result, TestSuccess) and + not self._running_test.expect_fail and + not self._running_test.expect_error): + self.log.info("Test Passed: %s" % running_test_funcname) + + elif (isinstance(result, TestFailure) and + self._running_test.expect_fail): + self.log.info("Test failed as expected: " + _result_was()) + + elif (isinstance(result, TestSuccess) and + self._running_test.expect_error): + self.log.error("Test passed but we expected an error: " + + _result_was()) + self.xunit.add_failure(stdout=repr(str(result)), + stderr="\n".join( + self._running_test.error_messages)) self.failures += 1 elif isinstance(result, TestSuccess): - self.log.error("Test passed but we expected a failure: %s (result was %s)" % ( - self._running_test.funcname, result.__class__.__name__)) - self.xunit.add_failure(stdout=repr(str(result)), stderr="\n".join(self._running_test.error_messages)) + self.log.error("Test passed but we expected a failure: " + + _result_was()) + self.xunit.add_failure(stdout=repr(str(result)), + stderr="\n".join( + self._running_test.error_messages)) self.failures += 1 elif isinstance(result, TestError) and self._running_test.expect_error: - self.log.info("Test errored as expected: %s (result was %s)" % ( - self._running_test.funcname, result.__class__.__name__)) + self.log.info("Test errored as expected: " + _result_was()) elif isinstance(result, SimFailure): if self._running_test.expect_error: - self.log.info("Test errored as expected: %s (result was %s)" % ( - self._running_test.funcname, result.__class__.__name__)) + self.log.info("Test errored as expected: " + _result_was()) else: - self.log.error("Test error has lead to simulator shuttting us down") + self.log.error("Test error has lead to simulator shuttting us " + "down") self.failures += 1 self.tear_down() return else: - self.log.error("Test Failed: %s (result was %s)" % ( - self._running_test.funcname, result.__class__.__name__)) - self.xunit.add_failure(stdout=repr(str(result)), stderr="\n".join(self._running_test.error_messages)) + self.log.error("Test Failed: " + _result_was()) + self.xunit.add_failure(stdout=repr(str(result)), + stderr="\n".join( + self._running_test.error_messages)) self.failures += 1 self.execute() @@ -222,16 +247,16 @@ def execute(self): self._running_test = cocotb.regression.next_test() if self._running_test: # Want this to stand out a little bit - self.log.info("%sRunning test %d/%d:%s %s" % ( - ANSI.BLUE_BG +ANSI.BLACK_FG, - self.count, self.ntests, - ANSI.DEFAULT_FG + ANSI.DEFAULT_BG, - self._running_test.funcname)) + self.log.info("%sRunning test %d/%d:%s %s" % + (ANSI.BLUE_BG + ANSI.BLACK_FG, + self.count, self.ntests, + ANSI.DEFAULT_FG + ANSI.DEFAULT_BG, + self._running_test.funcname)) if self.count is 1: test = cocotb.scheduler.add(self._running_test) else: test = cocotb.scheduler.new_test(self._running_test) - self.count+=1 + self.count += 1 else: self.tear_down() @@ -345,7 +370,7 @@ def generate_tests(self, prefix="", postfix=""): Generates exhasutive set of tests using the cartesian product of the possible keyword arguments. - The generated tests are appended to the namespace of the calling + The generated tests are appended to the namespace of the calling module. Args: @@ -364,25 +389,34 @@ def generate_tests(self, prefix="", postfix=""): d = self.kwargs - for index, testoptions in enumerate( (dict(zip(d, v)) for v in product(*d.values())) ): + for index, testoptions in enumerate(( + dict(zip(d, v)) for v in + product(*d.values()) + )): name = "%s%s%s_%03d" % (prefix, self.name, postfix, index + 1) doc = "Automatically generated test\n\n" for optname, optvalue in testoptions.items(): if callable(optvalue): - if not optvalue.__doc__: desc = "No docstring supplied" - else: desc = optvalue.__doc__.split('\n')[0] - doc += "\t%s: %s (%s)\n" % (optname, optvalue.__name__, desc) + if not optvalue.__doc__: + desc = "No docstring supplied" + else: + desc = optvalue.__doc__.split('\n')[0] + doc += "\t%s: %s (%s)\n" % (optname, optvalue.__name__, + desc) else: doc += "\t%s: %s\n" % (optname, repr(optvalue)) - cocotb.log.debug("Adding generated test \"%s\" to module \"%s\"" % (name, mod.__name__)) + cocotb.log.debug("Adding generated test \"%s\" to module \"%s\"" % + (name, mod.__name__)) kwargs = {} kwargs.update(self.kwargs_constant) kwargs.update(testoptions) if hasattr(mod, name): - cocotb.log.error("Overwriting %s in module %s. This causes previously defined testcase " - "not to be run. Consider setting/changing name_postfix" % (name, mod)) - setattr(mod, name, _create_test(self.test_function, name, doc, mod, *self.args, **kwargs)) - + cocotb.log.error("Overwriting %s in module %s. " + "This causes previously defined testcase " + "not to be run. Consider setting/changing " + "name_postfix" % (name, mod)) + setattr(mod, name, _create_test(self.test_function, name, doc, mod, + *self.args, **kwargs)) diff --git a/cocotb/result.py b/cocotb/result.py index fe5b935e..4241146b 100644 --- a/cocotb/result.py +++ b/cocotb/result.py @@ -28,9 +28,10 @@ # TODO: Coule use cStringIO? import traceback import sys -#from StringIO import StringIO +# from StringIO import StringIO from io import StringIO, BytesIO + def raise_error(obj, msg): """ Creates a TestError exception and raises it after printing a traceback @@ -52,6 +53,7 @@ def raise_error(obj, msg): exception.stderr.write(buff.getvalue()) raise exception + def create_error(obj, msg): """ As above, but return the exception rather than raise it, simply to avoid @@ -68,6 +70,7 @@ class ReturnValue(StopIteration): def __init__(self, retval): self.retval = retval + class TestComplete(StopIteration): """ Exceptions are used to pass test results around. @@ -77,10 +80,18 @@ def __init__(self, *args, **kwargs): self.stdout = StringIO() self.stderr = StringIO() -class TestError(TestComplete): pass -class TestFailure(TestComplete): pass +class TestError(TestComplete): + pass + + +class TestFailure(TestComplete): + pass + + +class TestSuccess(TestComplete): + pass -class TestSuccess(TestComplete): pass -class SimFailure(TestComplete): pass +class SimFailure(TestComplete): + pass diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 9f9abe78..7bd41302 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -49,7 +49,7 @@ # Debug mode controlled by environment variables if "COCOTB_ENABLE_PROFILING" in os.environ: import cProfile, StringIO, pstats - _profile = cProfile.Profile() + _profile = cProfile.Profile() _profiling = True else: _profiling = False @@ -64,11 +64,11 @@ import cocotb import cocotb.decorators -from cocotb.triggers import Trigger, GPITrigger, Timer, ReadOnly, _NextTimeStep, _ReadWrite +from cocotb.triggers import (Trigger, GPITrigger, Timer, ReadOnly, + _NextTimeStep, _ReadWrite) from cocotb.log import SimLog -from cocotb.result import TestComplete, TestError, ReturnValue, raise_error, create_error - - +from cocotb.result import (TestComplete, TestError, ReturnValue, raise_error, + create_error) class Scheduler(object): @@ -86,7 +86,7 @@ class Scheduler(object): order! Some additional management is required since coroutines can return a list - of triggers, to be scheduled when any one of the triggers fires. To + of triggers, to be scheduled when any one of the triggers fires. To ensure we don't receive spurious callbacks, we have to un-prime all the other triggers when any one fires. @@ -119,10 +119,10 @@ class Scheduler(object): "normal mode" i.e. where writes are permitted. """ - _MODE_NORMAL = 1 - _MODE_READONLY = 2 - _MODE_WRITE = 3 - _MODE_TERM = 4 + _MODE_NORMAL = 1 # noqa + _MODE_READONLY = 2 # noqa + _MODE_WRITE = 3 # noqa + _MODE_TERM = 4 # noqa # Singleton events, recycled to avoid spurious object creation _readonly = ReadOnly() @@ -137,7 +137,8 @@ def __init__(self): if _debug: self.log.setLevel(logging.DEBUG) - # A dictionary of pending coroutines for each trigger, indexed by trigger + # A dictionary of pending coroutines for each trigger, + # indexed by trigger self._trigger2coros = collections.defaultdict(list) # A dictionary of pending triggers for each coroutine, indexed by coro @@ -149,7 +150,7 @@ def __init__(self): # A dictionary of pending writes self._writes = {} - self._pending_coros = [] + self._pending_coros = [] self._pending_callbacks = [] self._pending_triggers = [] @@ -160,11 +161,10 @@ def __init__(self): # Select the appropriate scheduling algorithm for this simulator self.advance = self.default_scheduling_algorithm - def default_scheduling_algorithm(self): """ - Decide whether we need to schedule our own triggers (if at all) in order - to progress to the next mode. + Decide whether we need to schedule our own triggers (if at all) in + order to progress to the next mode. This algorithm has been tested against the following simulators: Icarus Verilog @@ -178,13 +178,14 @@ def default_scheduling_algorithm(self): self._next_timestep.prime(self.react) elif self._terminate: - if _debug: self.log.debug("Test terminating, scheduling Timer") + if _debug: + self.log.debug("Test terminating, scheduling Timer") for t in self._trigger2coros: t.unprime() - for t in [self._readwrite, self._readonly, self._next_timestep, \ - self._timer1, self._timer0]: + for t in [self._readwrite, self._readonly, self._next_timestep, + self._timer1, self._timer0]: if t.primed: t.unprime() @@ -194,7 +195,6 @@ def default_scheduling_algorithm(self): self._terminate = False self._mode = Scheduler._MODE_TERM - def begin_test(self, trigger=None): """ Called to initiate a test. @@ -202,14 +202,16 @@ def begin_test(self, trigger=None): Could be called on start-up or from a callback """ if _debug: - self.log.debug("begin_test called with trigger: %s" % (str(trigger))) + self.log.debug("begin_test called with trigger: %s" % + (str(trigger))) if _profiling: ps = pstats.Stats(_profile).sort_stats('cumulative') ps.dump_stats("test_profile.pstat") _profile.enable() self._mode = Scheduler._MODE_NORMAL - if trigger is not None: trigger.unprime() + if trigger is not None: + trigger.unprime() # Issue previous test result, if there is one if self._test_result is not None: @@ -226,7 +228,6 @@ def begin_test(self, trigger=None): if _profiling: _profile.disable() - def react(self, trigger, depth=0): """ React called when a trigger fires. @@ -240,12 +241,12 @@ def react(self, trigger, depth=0): # When a trigger fires it is unprimed internally if _debug: self.log.debug("Trigger fired: %s" % str(trigger)) - #trigger.unprime() + # trigger.unprime() if self._mode == Scheduler._MODE_TERM: if _debug: self.log.debug("Ignoring trigger %s since we're terminating" % - str(trigger)) + str(trigger)) return if trigger is self._readonly: @@ -258,7 +259,8 @@ def react(self, trigger, depth=0): # playing back any cached signal updates if trigger is self._readwrite: - if _debug: self.log.debug("Writing cached signal updates") + if _debug: + self.log.debug("Writing cached signal updates") while self._writes: handle, value = self._writes.popitem() @@ -277,59 +279,62 @@ def react(self, trigger, depth=0): self.log.error( "Moved to next timestep without any pending writes!") else: - self.log.debug("Priming ReadWrite trigger so we can playback writes") + self.log.debug( + "Priming ReadWrite trigger so we can playback writes") self._readwrite.prime(self.react) if _profiling: _profile.disable() return - - if trigger not in self._trigger2coros: - # GPI triggers should only be ever pending if there is an + # GPI triggers should only be ever pending if there is an # associated coroutine waiting on that trigger, otherwise it would # have been unprimed already if isinstance(trigger, GPITrigger): self.log.critical( "No coroutines waiting on trigger that fired: %s" % - str(trigger)) + str(trigger)) + trigger.log.info("I'm the culprit") - # For Python triggers this isn't actually an error - we might do + # For Python triggers this isn't actually an error - we might do # event.set() without knowing whether any coroutines are actually # waiting on this event, for example elif _debug: self.log.debug( "No coroutines waiting on trigger that fired: %s" % - str(trigger)) + str(trigger)) if _profiling: _profile.disable() return - # Scheduled coroutines may append to our waiting list so the first + # Scheduled coroutines may append to our waiting list so the first # thing to do is pop all entries waiting on this trigger. scheduling = self._trigger2coros.pop(trigger) if _debug: debugstr = "\n\t".join([coro.__name__ for coro in scheduling]) - if len(scheduling): debugstr = "\n\t" + debugstr - self.log.debug("%d pending coroutines for event %s%s" % ( - len(scheduling), str(trigger), debugstr)) + if len(scheduling): + debugstr = "\n\t" + debugstr + self.log.debug("%d pending coroutines for event %s%s" % + (len(scheduling), str(trigger), debugstr)) # If the coroutine was waiting on multiple triggers we may be able # to unprime the other triggers that didn't fire for coro in scheduling: for pending in self._coro2triggers[coro]: for others in self._trigger2coros[pending]: - if others not in scheduling: break + if others not in scheduling: + break else: - #if pending is not trigger and pending.primed: pending.unprime() - if pending.primed: pending.unprime() + # if pending is not trigger and pending.primed: + # pending.unprime() + if pending.primed: + pending.unprime() del self._trigger2coros[pending] - for coro in scheduling: self.schedule(coro, trigger=trigger) if _debug: @@ -337,23 +342,22 @@ def react(self, trigger, depth=0): while self._pending_triggers: if _debug: - self.log.debug("Scheduling pending trigger %s" % ( - str(self._pending_triggers[0]))) - self.react(self._pending_triggers.pop(0), depth=depth+1) + self.log.debug("Scheduling pending trigger %s" % + (str(self._pending_triggers[0]))) + self.react(self._pending_triggers.pop(0), depth=depth + 1) # We only advance for GPI triggers if not depth and isinstance(trigger, GPITrigger): self.advance() if _debug: - self.log.debug( - "All coroutines scheduled, handing control back to simulator") + self.log.debug("All coroutines scheduled, handing control back" + " to simulator") if _profiling: _profile.disable() return - def unschedule(self, coro): """Unschedule a coroutine. Unprime any pending triggers""" @@ -364,7 +368,6 @@ def unschedule(self, coro): trigger.unprime() del self._coro2triggers[coro] - if coro._join in self._trigger2coros: self._pending_triggers.append(coro._join) @@ -374,7 +377,6 @@ def unschedule(self, coro): def save_write(self, handle, value): self._writes[handle] = value - def _coroutine_yielded(self, coro, triggers): """ Prime the triggers and update our internal mappings @@ -390,8 +392,8 @@ def _coroutine_yielded(self, coro, triggers): except Exception as e: # Convert any exceptions into a test result self.finish_test( - create_error(self, "Unable to prime trigger %s: %s" % ( - str(trigger), str(e)))) + create_error(self, "Unable to prime trigger %s: %s" % + (str(trigger), str(e)))) def queue(self, coroutine): """Queue a coroutine for execution""" @@ -406,23 +408,24 @@ def add(self, coroutine): """ if isinstance(coroutine, cocotb.decorators.coroutine): self.log.critical( - "Attempt to schedule a coroutine that hasn't started") + "Attempt to schedule a coroutine that hasn't started") coroutine.log.error("This is the failing coroutine") self.log.warning( - "Did you forget to add parentheses to the @test decorator?") + "Did you forget to add parentheses to the @test decorator?") self._test_result = TestError( - "Attempt to schedule a coroutine that hasn't started") + "Attempt to schedule a coroutine that hasn't started") self._terminate = True return elif not isinstance(coroutine, cocotb.decorators.RunningCoroutine): self.log.critical( - "Attempt to add something to the scheduler which isn't a coroutine") + "Attempt to add something to the scheduler which isn't a " + "coroutine") self.log.warning( - "Got: %s (%s)" % (str(type(coroutine)), repr(coroutine))) + "Got: %s (%s)" % (str(type(coroutine)), repr(coroutine))) self.log.warning("Did you use the @coroutine decorator?") self._test_result = TestError( - "Attempt to schedule a coroutine that hasn't started") + "Attempt to schedule a coroutine that hasn't started") self._terminate = True return @@ -436,7 +439,6 @@ def add(self, coroutine): def new_test(self, coroutine): self._entrypoint = coroutine - def schedule(self, coroutine, trigger=None): """ Schedule a coroutine by calling the send method @@ -450,8 +452,8 @@ def schedule(self, coroutine, trigger=None): if hasattr(trigger, "pass_retval"): sendval = trigger.retval if _debug: - coroutine.log.debug("Scheduling with ReturnValue(%s)" % ( - repr(sendval))) + coroutine.log.debug("Scheduling with ReturnValue(%s)" % + (repr(sendval))) else: sendval = trigger if _debug: @@ -460,8 +462,8 @@ def schedule(self, coroutine, trigger=None): try: result = coroutine.send(sendval) if _debug: - self.log.debug("Coroutine %s yielded %s (mode %d)" % ( - coroutine.__name__, str(result), self._mode)) + self.log.debug("Coroutine %s yielded %s (mode %d)" % + (coroutine.__name__, str(result), self._mode)) # TestComplete indication is game over, tidy up except TestComplete as test_result: @@ -470,7 +472,6 @@ def schedule(self, coroutine, trigger=None): self.finish_test(test_result) return - # Normal co-routine completion except cocotb.decorators.CoroutineComplete as exc: if _debug: @@ -485,8 +486,8 @@ def schedule(self, coroutine, trigger=None): # Queue current routine to schedule when the nested routine exits if isinstance(result, cocotb.decorators.RunningCoroutine): if _debug: - self.log.debug("Scheduling nested co-routine: %s" % - result.__name__) + self.log.debug("Scheduling nested co-routine: %s" % + result.__name__) self.queue(result) new_trigger = result.join() @@ -495,15 +496,15 @@ def schedule(self, coroutine, trigger=None): elif isinstance(result, Trigger): self._coroutine_yielded(coroutine, [result]) - elif isinstance(result, list) and \ - not [t for t in result if not isinstance(t, Trigger)]: + elif (isinstance(result, list) and + not [t for t in result if not isinstance(t, Trigger)]): self._coroutine_yielded(coroutine, result) else: - msg = "Coroutine %s yielded something the scheduler can't handle" \ - % str(coroutine) - msg += "\nGot type: %s repr: %s str: %s" % \ - (type(result), repr(result), str(result)) + msg = ("Coroutine %s yielded something the scheduler can't handle" + % str(coroutine)) + msg += ("\nGot type: %s repr: %s str: %s" % + (type(result), repr(result), str(result))) msg += "\nDid you forget to decorate with @cocotb.cocorutine?" try: raise_error(self, msg) @@ -517,7 +518,6 @@ def schedule(self, coroutine, trigger=None): while self._pending_callbacks: self._pending_callbacks.pop(0)() - def finish_test(self, test_result): """Cache the test result and set the terminate flag""" self.log.debug("finish_test called with %s" % (repr(test_result))) @@ -540,5 +540,6 @@ def cleanup(self): """ for trigger, waiting in self._trigger2coros.items(): for coro in waiting: - if _debug: self.log.debug("Killing %s" % str(coro)) + if _debug: + self.log.debug("Killing %s" % str(coro)) coro.kill() diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index b4151aed..bf5bc2fa 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -60,19 +60,23 @@ def __init__(self, dut, reorder_depth=0, fail_immediately=True): @property def result(self): - """Determine the test result - do we have any pending data remaining?""" + """Determine the test result, do we have any pending data remaining?""" fail = False for monitor, expected_output in self.expected.items(): if callable(expected_output): - self.log.debug("Can't check all data returned for %s since expected output is \ - callable function rather than a list" % str(monitor)) + self.log.debug("Can't check all data returned for %s since " + "expected output is callable function rather " + "than a list" % str(monitor)) continue if len(expected_output): - self.log.warn("Still expecting %d transactions on %s" % (len(expected_output), str(monitor))) + self.log.warn("Still expecting %d transactions on %s" % + (len(expected_output), str(monitor))) for index, transaction in enumerate(expected_output): - self.log.info("Expecting %d:\n%s" % (index, hexdump(str(transaction)))) + self.log.info("Expecting %d:\n%s" % + (index, hexdump(str(transaction)))) if index > 5: - self.log.info("... and %d more to come" % (len(expected_output) - index - 1)) + self.log.info("... and %d more to come" % + (len(expected_output) - index - 1)) break fail = True if fail: @@ -81,7 +85,6 @@ def result(self): return TestFailure("Errors were recorded during the test") return TestSuccess() - def compare(self, got, exp, log, strict_type=True): """ Common function for comparing two transactions. @@ -92,9 +95,12 @@ def compare(self, got, exp, log, strict_type=True): # Compare the types if strict_type and type(got) != type(exp): self.errors += 1 - log.error("Received transaction is a different type to expected transaction") - log.info("Got: %s but expected %s" % (str(type(got)), str(type(exp)))) - if self._imm: raise TestFailure("Received transaction of wrong type") + log.error("Received transaction is a different type to expected " + "transaction") + log.info("Got: %s but expected %s" % + (str(type(got)), str(type(exp)))) + if self._imm: + raise TestFailure("Received transaction of wrong type") return # Or convert to a string before comparison elif not strict_type: @@ -111,53 +117,62 @@ def compare(self, got, exp, log, strict_type=True): log.info("Expected:\n" + hexdump(strexp)) if not isinstance(exp, str): try: - for word in exp: log.info(str(word)) + for word in exp: + log.info(str(word)) except: pass log.info("Received:\n" + hexdump(strgot)) if not isinstance(got, str): try: - for word in got: log.info(str(word)) + for word in got: + log.info(str(word)) except: pass log.warning("Difference:\n%s" % hexdiffs(strexp, strgot)) - if self._imm: raise TestFailure("Received transaction differed from expected transaction") + if self._imm: + raise TestFailure("Received transaction differed from expected" + "transaction") else: - # Don't want to fail the test if we're passed something without __len__ + # Don't want to fail the test + # if we're passed something without __len__ try: - log.debug("Received expected transaction %d bytes" % (len(got))) + log.debug("Received expected transaction %d bytes" % + (len(got))) log.debug(repr(got)) - except: pass - - - + except: + pass - def add_interface(self, monitor, expected_output, compare_fn=None, reorder_depth=0, strict_type=True): + def add_interface(self, monitor, expected_output, compare_fn=None, + reorder_depth=0, strict_type=True): """Add an interface to be scoreboarded. - Provides a function which the monitor will callback with received transactions + Provides a function which the monitor will callback with received + transactions Simply check against the expected output. """ - # save a handle to the expected output so we can check if all expected data has - # been received at the end of a test. + # save a handle to the expected output so we can check if all expected + # data has been received at the end of a test. self.expected[monitor] = expected_output # Enforce some type checking as we only work with a real monitor if not isinstance(monitor, Monitor): - raise TypeError("Expected monitor on the interface but got %s" % (monitor.__class__.__name__)) + raise TypeError("Expected monitor on the interface but got %s" % + (monitor.__class__.__name__)) if compare_fn is not None: if callable(compare_fn): monitor.add_callback(compare_fn) return - raise TypeError("Expected a callable compare function but got %s" % str(type(compare_fn))) + raise TypeError("Expected a callable compare function but got %s" % + str(type(compare_fn))) self.log.info("Created with reorder_depth %d" % reorder_depth) def check_received_transaction(transaction): - """Called back by the monitor when a new transaction has been received""" + """Called back by the monitor when a new transaction has been + received""" log = logging.getLogger(self.log.name + '.' + monitor.name) @@ -165,7 +180,7 @@ def check_received_transaction(transaction): exp = expected_output(transaction) elif len(expected_output): - for i in range(min((reorder_depth+1), len(expected_output))): + for i in range(min((reorder_depth + 1), len(expected_output))): if expected_output[i] == transaction: break else: @@ -173,9 +188,12 @@ def check_received_transaction(transaction): exp = expected_output.pop(i) else: self.errors += 1 - log.error("Received a transaction but wasn't expecting anything") + log.error("Received a transaction but wasn't expecting " + "anything") log.info("Got: %s" % (hexdump(str(transaction)))) - if self._imm: raise TestFailure("Received a transaction but wasn't expecting anything") + if self._imm: + raise TestFailure("Received a transaction but wasn't " + "expecting anything") return self.compare(transaction, exp, log, strict_type=strict_type) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index df6bb503..5ed2c5f6 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -42,6 +42,7 @@ class TriggerException(Exception): pass + class Trigger(object): """Base class to derive from""" def __init__(self): @@ -57,12 +58,14 @@ def unprime(self): self.primed = False def __del__(self): - """Ensure if a trigger drops out of scope we remove any pending callbacks""" + """Ensure if a trigger drops out of scope we remove any pending + callbacks""" self.unprime() def __str__(self): return self.__class__.__name__ + class PythonTrigger(Trigger): """Python triggers don't use GPI at all @@ -84,9 +87,9 @@ def __init__(self): Trigger.__init__(self) # Required to ensure documentation can build - #if simulator is not None: + # if simulator is not None: # self.cbhdl = simulator.create_callback(self) - #else: + # else: self.cbhdl = None def unprime(self): @@ -116,7 +119,8 @@ def __init__(self, time_ps): def prime(self, callback): """Register for a timed callback""" if self.cbhdl is None: - self.cbhdl = simulator.register_timed_callback(self.time_ps, callback, self) + self.cbhdl = simulator.register_timed_callback(self.time_ps, + callback, self) if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) Trigger.prime(self) @@ -124,6 +128,7 @@ def prime(self, callback): def __str__(self): return self.__class__.__name__ + "(%dps)" % self.time_ps + class Edge(GPITrigger): """ Execution will resume when an edge occurs on the provided signal @@ -135,7 +140,11 @@ def __init__(self, signal): def prime(self, callback): """Register notification of a value change via a callback""" if self.cbhdl is None: - self.cbhdl = simulator.register_value_change_callback(self.signal._handle, callback, 3, self) + self.cbhdl = simulator.register_value_change_callback(self.signal. + _handle, + callback, + 3, + self) if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) Trigger.prime(self) @@ -143,6 +152,7 @@ def prime(self, callback): def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal.name + class _ReadOnly(GPITrigger): """ Execution will resume when the readonly portion of the sim cycles is @@ -162,9 +172,12 @@ def __str__(self): return self.__class__.__name__ + "(readonly)" _ro = _ReadOnly() + + def ReadOnly(): return _ro + class _ReadWrite(GPITrigger): """ Execution will resume when the readwrite porttion of the sim cycles is @@ -175,8 +188,8 @@ def __init__(self): def prime(self, callback): if self.cbhdl is None: - #import pdb - #pdb.set_trace() + # import pdb + # pdb.set_trace() self.cbhdl = simulator.register_rwsynch_callback(callback, self) if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) @@ -186,9 +199,12 @@ def __str__(self): return self.__class__.__name__ + "(readwritesync)" _rw = _ReadWrite() + + def ReadWrite(): return _rw + class _NextTimeStep(GPITrigger): """ Execution will resume when the next time step is started @@ -207,20 +223,27 @@ def __str__(self): return self.__class__.__name__ + "(nexttimestep)" _nxts = _NextTimeStep() + + def NextTimeStep(): return _nxts + class _RisingOrFallingEdge(Edge): def __init__(self, signal, rising): Edge.__init__(self, signal) - if rising == True: + if rising is True: self._rising = 1 else: self._rising = 2 def prime(self, callback): if self.cbhdl is None: - self.cbhdl = simulator.register_value_change_callback(self.signal._handle, callback, self._rising, self) + self.cbhdl = simulator.register_value_change_callback(self.signal. + _handle, + callback, + self._rising, + self) if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) Trigger.prime(self) @@ -228,6 +251,7 @@ def prime(self, callback): def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal.name + class _RisingEdge(_RisingOrFallingEdge): """ Execution will resume when a rising edge occurs on the provided signal @@ -235,9 +259,11 @@ class _RisingEdge(_RisingOrFallingEdge): def __init__(self, signal): _RisingOrFallingEdge.__init__(self, signal, rising=True) + def RisingEdge(signal): return signal._r_edge + class _FallingEdge(_RisingOrFallingEdge): """ Execution will resume when a falling edge occurs on the provided signal @@ -245,9 +271,11 @@ class _FallingEdge(_RisingOrFallingEdge): def __init__(self, signal): _RisingOrFallingEdge.__init__(self, signal, rising=False) + def FallingEdge(signal): return signal._f_edge + class ClockCycles(Edge): """ Execution will resume after N rising edges @@ -267,11 +295,17 @@ def _check(obj): self._callback(self) return - self.cbhdl = simulator.register_value_change_callback(self.signal._handle, _check, self) + self.cbhdl = simulator.register_value_change_callback(self.signal. + _handle, + _check, + self) if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) - self.cbhdl = simulator.register_value_change_callback(self.signal._handle, _check, self) + self.cbhdl = simulator.register_value_change_callback(self.signal. + _handle, + _check, + self) if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) Trigger.prime(self) @@ -289,13 +323,17 @@ class Combine(PythonTrigger): def __init__(self, *args): PythonTrigger.__init__(self) self._triggers = args - # TODO: check that trigger is an iterable containing only Trigger objects + # TODO: check that trigger is an iterable containing + # only Trigger objects try: for trigger in self._triggers: if not isinstance(trigger, Trigger): - raise TriggerException("All combined triggers must be instances of Trigger! Got: %s" % trigger.__class__.__name__) + raise TriggerException("All combined triggers must be " + "instances of Trigger! Got: %s" % + trigger.__class__.__name__) except Exception: - raise TriggerException("%s requires a list of Trigger objects" % self.__class__.__name__) + raise TriggerException("%s requires a list of Trigger objects" % + self.__class__.__name__) def prime(self, callback): self._callback = callback @@ -336,7 +374,6 @@ def __call__(self): self._callback(self) - class Event(PythonTrigger): """ Event to permit synchronisation between two coroutines @@ -365,7 +402,8 @@ def set(self, data=None): trigger() def wait(self): - """This can be yielded to block this coroutine until another wakes it""" + """This can be yielded to block this coroutine + until another wakes it""" return _Event(self) def clear(self): @@ -408,7 +446,7 @@ class Lock(PythonTrigger): def __init__(self, name=""): PythonTrigger.__init__(self) self._pending_unprimed = [] - self._pending_primed = [] + self._pending_primed = [] self.name = name self.locked = False @@ -429,11 +467,11 @@ def acquire(self): self._pending_unprimed.append(trig) return trig - def release(self): if not self.locked: - raise_error(self, "Attempt to release an unacquired Lock %s" % (str(self))) + raise_error(self, "Attempt to release an unacquired Lock %s" % + (str(self))) self.locked = False @@ -446,8 +484,9 @@ def release(self): trigger() def __str__(self): - return self.__class__.__name__ + "(%s) [%s waiting]" % ( - self.name, len(self._pending_primed)) + return "%s(%s) [%s waiting]" % (str(self.__class__.__name__), + self.name, + len(self._pending_primed)) def __nonzero__(self): """Provide boolean of a Lock""" @@ -481,9 +520,9 @@ def __init__(self, coroutine): def retval(self): return self._coroutine.retval - #def prime(self, callback): - #"""Register our calback for when the coroutine exits""" - #Trigger.prime(self) + # def prime(self, callback): + # """Register our calback for when the coroutine exits""" + # Trigger.prime(self) def __str__(self): return self.__class__.__name__ + "(%s)" % self._coroutine.__name__ diff --git a/cocotb/utils.py b/cocotb/utils.py index 89978580..88479666 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -31,17 +31,19 @@ import ctypes + # python2 to python3 helper functions def get_python_integer_types(): try: isinstance(1, long) except NameError: - return (int,) # python 3 + return (int,) # python 3 else: - return (int, long) # python 2 + return (int, long) # python 2 # Ctypes helper functions + def pack(ctypes_obj): """Convert a ctypes structure into a python string @@ -53,7 +55,8 @@ def pack(ctypes_obj): Returns: New python string containing the bytes from memory holding ctypes_obj """ - return ctypes.string_at(ctypes.addressof(ctypes_obj), ctypes.sizeof(ctypes_obj)) + return ctypes.string_at(ctypes.addressof(ctypes_obj), + ctypes.sizeof(ctypes_obj)) def unpack(ctypes_obj, string, bytes=None): @@ -70,8 +73,9 @@ def unpack(ctypes_obj, string, bytes=None): Raises: ValueError, MemoryError - If the length of the string is not the correct size for the memory footprint of the - ctypes structure then the bytes keyword argument must be used + If the length of the string is not the correct size for the memory + footprint of the ctypes structure then the bytes keyword argument must + be used """ if bytes is None: if len(string) != ctypes.sizeof(ctypes_obj): @@ -86,19 +90,17 @@ def unpack(ctypes_obj, string, bytes=None): ctypes.memmove(ctypes.addressof(ctypes_obj), string, bytes) - - import cocotb.ANSI as ANSI def _sane_color(x): - r="" + r = "" for i in x: j = ord(i) if (j < 32) or (j >= 127): - r+="." + r += "." else: - r+=i + r += i return r @@ -106,35 +108,36 @@ def hexdump(x): """Hexdump a buffer""" # adapted from scapy.utils.hexdump rs = "" - x=str(x) + x = str(x) l = len(x) i = 0 while i < l: rs += "%04x " % i for j in range(16): - if i+j < l: - rs += "%02X " % ord(x[i+j]) + if i + j < l: + rs += "%02X " % ord(x[i + j]) else: - rs += " " - if j%16 == 7: + rs += " " + if j % 16 == 7: rs += "" rs += " " - rs += _sane_color(x[i:i+16]) + "\n" + rs += _sane_color(x[i:i + 16]) + "\n" i += 16 return rs -def hexdiffs(x,y): + +def hexdiffs(x, y): """Return a diff string showing differences between 2 binary strings""" # adapted from scapy.utils.hexdiff def sane(x): - r="" + r = "" for i in x: j = ord(i) if (j < 32) or (j >= 127): - r=r+"." + r = r + "." else: - r=r+i + r = r + i return r def highlight(string, colour=ANSI.YELLOW_FG): @@ -142,46 +145,43 @@ def highlight(string, colour=ANSI.YELLOW_FG): rs = "" - x=str(x)[::-1] - y=str(y)[::-1] - SUBST=1 - INSERT=1 - d={} - d[-1,-1] = 0,(-1,-1) + x = str(x)[::-1] + y = str(y)[::-1] + SUBST = 1 + INSERT = 1 + d = {} + d[-1, -1] = 0, (-1, -1) for j in range(len(y)): - d[-1,j] = d[-1,j-1][0]+INSERT, (-1,j-1) + d[-1, j] = d[-1, j - 1][0] + INSERT, (-1, j - 1) for i in range(len(x)): - d[i,-1] = d[i-1,-1][0]+INSERT, (i-1,-1) + d[i, -1] = d[i - 1, -1][0] + INSERT, (i - 1, -1) for j in range(len(y)): for i in range(len(x)): - d[i,j] = min( ( d[i-1,j-1][0]+SUBST*(x[i] != y[j]), (i-1,j-1) ), - ( d[i-1,j][0]+INSERT, (i-1,j) ), - ( d[i,j-1][0]+INSERT, (i,j-1) ) ) - + d[i, j] = min((d[i-1, j-1][0] + SUBST*(x[i] != y[j]), (i-1, j-1)), + (d[i - 1, j][0] + INSERT, (i - 1, j)), + (d[i, j - 1][0] + INSERT, (i, j - 1))) backtrackx = [] backtracky = [] - i=len(x)-1 - j=len(y)-1 + i = len(x) - 1 + j = len(y) - 1 while not (i == j == -1): - i2,j2 = d[i,j][1] + i2, j2 = d[i, j][1] backtrackx.append(x[i2+1:i+1]) backtracky.append(y[j2+1:j+1]) - i,j = i2,j2 - - + i, j = i2, j2 x = y = i = 0 - colorize = { 0: lambda x:x, - -1: lambda x:x, - 1: lambda x:x } + colorize = { 0: lambda x: x, # noqa + -1: lambda x: x, # noqa + 1: lambda x: x} # noqa - dox=1 - doy=0 + dox = 1 + doy = 0 l = len(backtrackx) while i < l: - separate=0 + separate = 0 linex = backtrackx[i:i+16] liney = backtracky[i:i+16] xx = sum(len(k) for k in linex) @@ -190,7 +190,7 @@ def highlight(string, colour=ANSI.YELLOW_FG): dox = 0 doy = 1 if dox and linex == liney: - doy=1 + doy = 1 if dox: xd = y @@ -199,11 +199,11 @@ def highlight(string, colour=ANSI.YELLOW_FG): j += 1 xd -= 1 if dox != doy: - rs += highlight("%04x" % xd) + " " + rs += highlight("%04x" % xd) + " " else: - rs += highlight("%04x" % xd, colour=ANSI.CYAN_FG) + " " + rs += highlight("%04x" % xd, colour=ANSI.CYAN_FG) + " " x += xx - line=linex + line = linex else: rs += " " if doy: @@ -212,12 +212,12 @@ def highlight(string, colour=ANSI.YELLOW_FG): while not liney[j]: j += 1 yd -= 1 - if doy-dox != 0: + if doy - dox != 0: rs += " " + highlight("%04x" % yd) else: - rs += highlight("%04x" % yd, colour=ANSI.CYAN_FG) + rs += highlight("%04x" % yd, colour=ANSI.CYAN_FG) y += yy - line=liney + line = liney else: rs += " " @@ -225,16 +225,19 @@ def highlight(string, colour=ANSI.YELLOW_FG): cl = "" for j in range(16): - if i+j < l: + if i + j < l: if line[j]: if linex[j] != liney[j]: - rs += highlight("%02X" % ord(line[j]), colour=ANSI.RED_FG) + rs += highlight("%02X" % ord(line[j]), + colour=ANSI.RED_FG) else: rs += "%02X" % ord(line[j]) - if linex[j]==liney[j]: - cl += highlight(_sane_color(line[j]), colour=ANSI.MAGENTA_FG) + if linex[j] == liney[j]: + cl += highlight(_sane_color(line[j]), + colour=ANSI.MAGENTA_FG) else: - cl += highlight(sane(line[j]), colour=ANSI.CYAN_BG+ANSI.BLACK_FG) + cl += highlight(sane(line[j]), + colour=ANSI.CYAN_BG + ANSI.BLACK_FG) else: rs += " " cl += " " @@ -243,17 +246,16 @@ def highlight(string, colour=ANSI.YELLOW_FG): if j == 7: rs += " " - rs += " " + cl + '\n' if doy or not yy: - doy=0 - dox=1 + doy = 0 + dox = 1 i += 16 else: if yy: - dox=0 - doy=1 + dox = 0 + doy = 1 else: i += 16 return rs @@ -262,14 +264,14 @@ def highlight(string, colour=ANSI.YELLOW_FG): if __name__ == "__main__": import random a = "" - for char in range(random.randint(250,500)): - a += chr(random.randint(0,255)) + for char in range(random.randint(250, 500)): + a += chr(random.randint(0, 255)) b = a - for error in range(random.randint(2,9)): + for error in range(random.randint(2, 9)): offset = random.randint(0, len(a)) - b = b[:offset] + chr(random.randint(0,255)) + b[offset+1:] + b = b[:offset] + chr(random.randint(0, 255)) + b[offset+1:] - diff = hexdiffs(a,b) + diff = hexdiffs(a, b) print(diff) space = '\n' + (" " * 20) diff --git a/cocotb/wavedrom.py b/cocotb/wavedrom.py index 32d11a64..044d237c 100644 --- a/cocotb/wavedrom.py +++ b/cocotb/wavedrom.py @@ -31,6 +31,7 @@ from cocotb.bus import Bus from cocotb.triggers import RisingEdge, ReadOnly + class Wavedrom(object): """ Base class for a wavedrom compatible tracer @@ -54,7 +55,8 @@ def sample(self): def _lastval(samples): for x in range(len(samples)-1, -1, -1): - if samples[x] not in "=.|": return samples[x] + if samples[x] not in "=.|": + return samples[x] return None for name, hdl in self._hdls.items(): @@ -62,13 +64,18 @@ def _lastval(samples): valstr = val.binstr.lower() # Decide what character to use to represent this signal - if len(valstr) == 1: char = valstr - elif "x" in valstr: char = "x" - elif "u" in valstr: char = "u" - elif "z" in valstr: char = "z" + if len(valstr) == 1: + char = valstr + elif "x" in valstr: + char = "x" + elif "u" in valstr: + char = "u" + elif "z" in valstr: + char = "z" else: - if len(self._data[name]) and self._data[name][-1] == int(val) \ - and self._samples[name][-1] in "=.": + if (len(self._data[name]) and + self._data[name][-1] == int(val) and + self._samples[name][-1] in "=."): char = "." else: char = "=" @@ -110,7 +117,7 @@ def get(self, add_clock=True): if add_clock: tracelen = len(traces[-1]["wave"]) - siglist.insert(0, {"name": "clk", "wave" : "p" + "."*(tracelen-1)}) + siglist.insert(0, {"name": "clk", "wave": "p" + "."*(tracelen-1)}) return siglist @@ -151,7 +158,8 @@ def _monitor(self): while True: yield RisingEdge(self._clock) yield ReadOnly() - if not self._enabled: continue + if not self._enabled: + continue self._clocks += 1 for sig in self._signals: sig.sample() @@ -185,11 +193,10 @@ def write(self, filename, **kwargs): with open(filename, "w") as f: f.write(self.dumpj(**kwargs)) - def dumpj(self, header="", footer=""): - trace = {"signal" : []} + trace = {"signal": []} trace["signal"].append( - {"name": "clock", "wave" : "p" + "."*(self._clocks-1)}) + {"name": "clock", "wave": "p" + "."*(self._clocks-1)}) for sig in self._signals: trace["signal"].extend(sig.get(add_clock=False)) if header: diff --git a/cocotb/xunit_reporter.py b/cocotb/xunit_reporter.py index d70977ea..2a9c1cd0 100644 --- a/cocotb/xunit_reporter.py +++ b/cocotb/xunit_reporter.py @@ -33,6 +33,7 @@ TRUNCATE_LINES = 100 + # file from http://stackoverflow.com/questions/136168/get-last-n-lines-of-a-file-with-python-similar-to-tail class File(StringIO): @@ -44,11 +45,11 @@ def countlines(self): return lines def head(self, lines_2find=1): - self.seek(0) #Rewind file + self.seek(0) # Rewind file return [self.next() for x in range(lines_2find)] def tail(self, lines_2find=1): - self.seek(0, 2) #go to end of file + self.seek(0, 2) # go to end of file bytes_in_file = self.tell() lines_found, total_bytes_scanned = 0, 0 while (lines_2find+1 > lines_found and @@ -67,19 +68,19 @@ class XUnitReporter(object): def __init__(self, filename="results.xml"): self.results = Element("testsuites", name="results") self.filename = filename - + def add_testsuite(self, **kwargs): self.last_testsuite = SubElement(self.results, "testsuite", **kwargs) return self.last_testsuite def add_testcase(self, testsuite=None, **kwargs): - if testsuite == None: + if testsuite is None: testsuite = self.last_testsuite self.last_testcase = SubElement(testsuite, "testcase", **kwargs) return self.last_testcase def update_testsuite(self, testsuite=None, **kwargs): - if testsuite == None: + if testsuite is None: testsuite = self.last_testsuite for k in kwargs: testsuite.set(k, str(kwargs[k])) @@ -89,7 +90,7 @@ def update_testsuites(self, **kwargs): self.results.set(k, str(kwargs[k])) def add_log(self, logfile, testcase=None): - if testcase == None: + if testcase is None: testcase = self.last_testcase log = SubElement(testcase, "system-out") f = File(logfile, 'r+') @@ -97,20 +98,21 @@ def add_log(self, logfile, testcase=None): if lines > (TRUNCATE_LINES * 2): head = f.head(TRUNCATE_LINES) tail = f.tail(TRUNCATE_LINES) - log.text = "".join(head + list("[...truncated %d lines...]\n" % ( (lines - (TRUNCATE_LINES*2)) )) + tail) + log.text = "".join(head + list("[...truncated %d lines...]\n" % + ((lines - (TRUNCATE_LINES*2)))) + tail) else: log.text = "".join(f.readlines()) def add_failure(self, testcase=None, **kwargs): - if testcase == None: + if testcase is None: testcase = self.last_testcase log = SubElement(testcase, "failure", **kwargs) def add_skipped(self, testcase=None, **kwargs): - if testcase == None: + if testcase is None: testcase = self.last_testcase log = SubElement(testcase, "skipped", **kwargs) - + def indent(self, elem, level=0): i = "\n" + level*" " if len(elem): @@ -129,4 +131,3 @@ def indent(self, elem, level=0): def write(self): self.indent(self.results) ET.ElementTree(self.results).write(self.filename, encoding="UTF-8") - From b354b22bc46e6d0757baf0c9274881cfa5fa2e1c Mon Sep 17 00:00:00 2001 From: mfkaptan Date: Tue, 14 Apr 2015 22:15:40 +0200 Subject: [PATCH 0839/2656] Style improvements for PEP8 compliance, Issue #6 Updated files under: - bin - examples - cocotb/drivers - cocotb/generators - cocotb/monitors --- bin/combine_results.py | 8 +- bin/create_files.py | 4 + cocotb/drivers/__init__.py | 59 +++++---- cocotb/drivers/ad9361.py | 52 ++++---- cocotb/drivers/amba.py | 116 +++++++++--------- cocotb/drivers/avalon.py | 46 ++++--- cocotb/drivers/opb.py | 8 +- cocotb/drivers/xgmii.py | 67 +++++----- cocotb/generators/__init__.py | 5 +- cocotb/generators/bit.py | 10 +- cocotb/generators/byte.py | 8 +- cocotb/generators/packet.py | 12 +- cocotb/monitors/__init__.py | 29 +++-- cocotb/monitors/avalon.py | 15 ++- cocotb/monitors/xgmii.py | 41 ++++--- examples/adder/model/adder_model.py | 4 +- examples/adder/tests/test_adder.py | 33 ++--- .../tests/test_endian_swapper.py | 47 ++++--- .../tests/test_endian_swapper_hal.py | 4 +- examples/functionality/tests/test_cocotb.py | 74 ++++++++--- .../functionality/tests/test_discovery.py | 31 +++-- examples/functionality/tests/test_external.py | 53 +++++--- .../functionality/tests/test_regression.py | 5 +- examples/mean/tests/test_mean.py | 72 ++++++----- .../tests/test_mixed_language.py | 1 + .../ping_tun_tap/tests/test_icmp_reply.py | 7 +- examples/plusargs/plusargs.py | 2 +- examples/sim_exit/tests/test_closedown.py | 4 + 28 files changed, 485 insertions(+), 332 deletions(-) diff --git a/bin/combine_results.py b/bin/combine_results.py index aca20421..f8cf78ac 100755 --- a/bin/combine_results.py +++ b/bin/combine_results.py @@ -9,12 +9,14 @@ import sys from xml.etree import cElementTree as ET + def find_all(name, path): result = [] for root, dirs, files in os.walk(path): if name in files: yield os.path.join(root, name) + def main(path, output): rc = 0 testsuite = ET.Element("testsuite", name="all", package="all", tests="0") @@ -26,8 +28,9 @@ def main(path, output): for child in element: if child.tag in ["failure", "error"]: - sys.stderr.write("FAILURE: %s.%s\n" % ( - element.attrib["classname"], element.attrib["name"])) + sys.stderr.write("FAILURE: %s.%s\n" % + (element.attrib["classname"], + element.attrib["name"])) rc = 1 result = ET.Element("testsuites", name="results") @@ -43,4 +46,3 @@ def main(path, output): if len(sys.argv) > 1: sys.exit(0) sys.exit(rc) - diff --git a/bin/create_files.py b/bin/create_files.py index 49ff7b2a..a8fbe727 100755 --- a/bin/create_files.py +++ b/bin/create_files.py @@ -36,6 +36,7 @@ import platform from subprocess import call + def print_make_inc(path): makefile = open("/tmp/Makefile.inc", "w") makefile.write("export ARCH:=$(shell uname -m)\n") @@ -43,6 +44,7 @@ def print_make_inc(path): makefile.write("export LIB_DIR:=" + path + "/lib/$(ARCH)\n") makefile.close() + def print_uninstall(path): uninstall = open("/tmp/cocotb_uninstall", "w") file_contents = "#!/usr/bin/env python\n" @@ -55,12 +57,14 @@ def print_uninstall(path): uninstall.write(file_contents) + def print_files(path): print_uninstall(path) call("install -m 544 /tmp/cocotb_uninstall " + path + "/bin/cocotb_uninstall", shell=True) call("rm -rf /tmp/cocotb_uninstall", shell=True) + def check_args(args): if len(args) is not 1: print "Please specify a path" diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index 1f614884..b312ae9b 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -35,7 +35,8 @@ import cocotb from cocotb.decorators import coroutine -from cocotb.triggers import Event, RisingEdge, ReadOnly, Timer, NextTimeStep, Edge +from cocotb.triggers import (Event, RisingEdge, ReadOnly, Timer, NextTimeStep, + Edge) from cocotb.bus import Bus from cocotb.log import SimLog from cocotb.result import ReturnValue @@ -69,7 +70,7 @@ def _cr_twiddler(self, generator=None): # Actual thread while True: - on,off = next(self._generator) + on, off = next(self._generator) self._signal <= 1 for i in range(on): yield edge @@ -77,19 +78,20 @@ def _cr_twiddler(self, generator=None): for i in range(off): yield edge + class Driver(object): """ Class defining the standard interface for a driver within a testbench - The driver is responsible for serialising transactions onto the physical pins - of the interface. This may consume simulation time. + The driver is responsible for serialising transactions onto the physical + pins of the interface. This may consume simulation time. """ def __init__(self): """ Constructor for a driver instance """ - #self._busy = Lock() + # self._busy = Lock() self._pending = Event(name="Driver._pending") self._sendQ = [] @@ -100,7 +102,6 @@ def __init__(self): # Create an independent coroutine which can send stuff self._thread = cocotb.scheduler.add(self._send_thread()) - def kill(self): if self._thread: self._thread.kill() @@ -110,9 +111,11 @@ def append(self, transaction, callback=None, event=None): """ Queue up a transaction to be sent over the bus. - Mechanisms are provided to permit the caller to know when the transaction is processed + Mechanisms are provided to permit the caller to know when the + transaction is processed - callback: optional function to be called when the transaction has been sent + callback: optional function to be called when the transaction has been + sent event: event to be set when the tansaction has been sent """ @@ -140,15 +143,15 @@ def send(self, transaction, sync=True): """ yield self._send(transaction, None, None, sync=sync) - def _driver_send(self, transaction, sync=True): """ actual impementation of the send. - subclasses should override this method to implement the actual send routine + subclasses should override this method to implement the actual send + routine """ - raise NotImplementedError("Subclasses of Driver should define a _driver_send coroutine") - + raise NotImplementedError("Subclasses of Driver should define a " + "_driver_send coroutine") @coroutine def _send(self, transaction, callback, event, sync=True): @@ -160,11 +163,13 @@ def _send(self, transaction, callback, event, sync=True): yield self._driver_send(transaction, sync=sync) # Notify the world that this transaction is complete - if event: event.set() - if callback: callback(transaction) + if event: + event.set() + if callback: + callback(transaction) # No longer hogging the bus - #self.busy.release() + # self.busy.release() @coroutine def _send_thread(self): @@ -177,11 +182,13 @@ def _send_thread(self): synchronised = False - # Send in all the queued packets, only synchronise on the first send + # Send in all the queued packets, + # only synchronise on the first send while self._sendQ: transaction, callback, event = self._sendQ.pop(0) self.log.debug("Sending queued packet...") - yield self._send(transaction, callback, event, sync=not synchronised) + yield self._send(transaction, callback, event, + sync=not synchronised) synchronised = True @@ -203,7 +210,8 @@ def __init__(self, entity, name, clock): name (str) : name of this bus. None for nameless bus, e.g. bus-signals in an interface or a modport - (untested on struct/record, but could work here as well) + (untested on struct/record, + but could work here as well) clock (SimHandle) : A handle to the clock associated with this bus """ self.log = SimLog("cocotb.%s.%s" % (entity.name, name)) @@ -211,8 +219,8 @@ def __init__(self, entity, name, clock): self.entity = entity self.name = name self.clock = clock - self.bus = Bus(self.entity, self.name, self._signals, self._optional_signals) - + self.bus = Bus(self.entity, self.name, self._signals, + self._optional_signals) @coroutine def _driver_send(self, transaction, sync=True): @@ -272,7 +280,6 @@ def __init__(self, entity, name, clock, valid_generator=None): BusDriver.__init__(self, entity, name, clock) self.set_valid_generator(valid_generator=valid_generator) - def _next_valids(self): """ Optionally insert invalid cycles every N cycles @@ -287,17 +294,19 @@ def _next_valids(self): try: self.on, self.off = next(self.valid_generator) except StopIteration: - self.on = True # If the generator runs out stop inserting non-valid cycles - self.log.info("Valid generator exhausted, not inserting non-valid cycles anymore") + # If the generator runs out stop inserting non-valid cycles + self.on = True + self.log.info("Valid generator exhausted, not inserting " + "non-valid cycles anymore") return - self.log.debug("Will be on for %d cycles, off for %s" % (self.on, self.off)) + self.log.debug("Will be on for %d cycles, off for %s" % + (self.on, self.off)) else: # Valid every clock cycle self.on, self.off = True, False self.log.debug("Not using valid generator") - def set_valid_generator(self, valid_generator=None): """ Set a new valid generator for this bus diff --git a/cocotb/drivers/ad9361.py b/cocotb/drivers/ad9361.py index 5bf01c91..d04d402c 100644 --- a/cocotb/drivers/ad9361.py +++ b/cocotb/drivers/ad9361.py @@ -9,16 +9,19 @@ from cocotb.bus import Bus from cocotb.result import ReturnValue from cocotb.drivers import BusDriver -from cocotb.binary import BinaryValue,BinaryRepresentation +from cocotb.binary import BinaryValue, BinaryRepresentation from collections import deque + class AD9361(BusDriver): ''' classdocs ''' - def __init__(self,dut,rx_channels=1, tx_channels=1, tx_clock_half_period=16276, rx_clock_half_period=16276,loopback_queue_maxlen=16): + def __init__(self, dut, rx_channels=1, tx_channels=1, + tx_clock_half_period=16276, rx_clock_half_period=16276, + loopback_queue_maxlen=16): ''' Constructor ''' @@ -31,7 +34,7 @@ def __init__(self,dut,rx_channels=1, tx_channels=1, tx_clock_half_period=16276, self.lbqq = deque() cocotb.fork(self._rx_clock()) self.got_tx = Event("Got tx event") - + @cocotb.coroutine def _rx_clock(self): t = Timer(self.rx_clock_half_period) @@ -42,17 +45,22 @@ def _rx_clock(self): self.dut.rx_clk_in_p <= 0 self.dut.rx_clk_in_n <= 1 yield t - - def send_data(self,i_data,q_data,i_data2=None,q_data2=None,binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT): + + def send_data(self, i_data, q_data, i_data2=None, q_data2=None, + binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT): print binaryRepresentation - cocotb.fork(self.rx_data_to_ad9361(i_data,q_data,i_data2,q_data2,binaryRepresentation)) - + cocotb.fork(self.rx_data_to_ad9361(i_data, q_data, i_data2, q_data2, + binaryRepresentation)) + @cocotb.coroutine - def rx_data_to_ad9361(self,i_data,q_data,i_data2=None,q_data2=None,binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT): - i_bin_val = BinaryValue(bits=12, bigEndian=False,binaryRepresentation=binaryRepresentation) - q_bin_val = BinaryValue(bits=12, bigEndian=False,binaryRepresentation=binaryRepresentation) + def rx_data_to_ad9361(self, i_data, q_data, i_data2=None, q_data2=None, + binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT): + i_bin_val = BinaryValue(bits=12, bigEndian=False, + binaryRepresentation=binaryRepresentation) + q_bin_val = BinaryValue(bits=12, bigEndian=False, + binaryRepresentation=binaryRepresentation) index = 0 - if i_data2==None and q_data2==None: + if i_data2 is None and q_data2 is None: while True: yield RisingEdge(self.dut.rx_clk_in_p) if self.rx_frame_asserted: @@ -107,7 +115,7 @@ def rx_data_to_ad9361(self,i_data,q_data,i_data2=None,q_data2=None,binaryReprese Q_SEND_HIGH = False else: self.dut.rx_data_in_p <= q_bin_val[11:6] - self.dut.rx_data_in_n <= ~q_bin_val[11:6] + self.dut.rx_data_in_n <= ~q_bin_val[11:6] Q_SEND_HIGH = True if index < len(i_data): if channel == 1: @@ -120,29 +128,29 @@ def rx_data_to_ad9361(self,i_data,q_data,i_data2=None,q_data2=None,binaryReprese channel = 1 index += 1 else: - return - + return + @cocotb.coroutine def _tx_data_from_ad9361(self): i_bin_val = BinaryValue(bits=12, bigEndian=False) q_bin_val = BinaryValue(bits=12, bigEndian=False) while True: yield RisingEdge(self.dut.tx_clk_out_p) - if self.dut.tx_frame_out_p.value.integer==1: + if self.dut.tx_frame_out_p.value.integer == 1: q_bin_val[11:6] = self.dut.tx_data_out_p.value.get_binstr() else: q_bin_val[5:0] = self.dut.tx_data_out_p.value.get_binstr() yield RisingEdge(self.dut.tx_clk_out_n) - if self.dut.tx_frame_out_p.value.integer==1: + if self.dut.tx_frame_out_p.value.integer == 1: i_bin_val[11:6] = self.dut.tx_data_out_p.value.get_binstr() else: i_bin_val[5:0] = self.dut.tx_data_out_p.value.get_binstr() - #print "i_data",i_bin_val.get_value() - #print "q_data",q_bin_val.get_value() + # print "i_data",i_bin_val.get_value() + # print "q_data",q_bin_val.get_value() self.lbqi.append(i_bin_val) self.lbqq.append(q_bin_val) - self.got_tx.set([i_bin_val,q_bin_val]) - + self.got_tx.set([i_bin_val, q_bin_val]) + @cocotb.coroutine def _ad9361_tx_to_rx_loopback(self): cocotb.fork(self._tx_data_from_ad9361()) @@ -177,9 +185,9 @@ def _ad9361_tx_to_rx_loopback(self): else: self.dut.rx_data_in_p <= q_bin_val[5:0] self.dut.rx_data_in_n <= ~q_bin_val[5:0] - + def ad9361_tx_to_rx_loopback(self): cocotb.fork(self._ad9361_tx_to_rx_loopback()) - + def tx_data_from_ad9361(self): cocotb.fork(self._tx_data_from_ad9361()) diff --git a/cocotb/drivers/amba.py b/cocotb/drivers/amba.py index 7d25bcb3..30481a61 100644 --- a/cocotb/drivers/amba.py +++ b/cocotb/drivers/amba.py @@ -36,7 +36,9 @@ import array -class AXIReadError(Exception): pass +class AXIReadError(Exception): + pass + class AXI4LiteMaster(BusDriver): """ @@ -44,11 +46,11 @@ class AXI4LiteMaster(BusDriver): TODO: Kill all pending transactions if reset is asserted... """ - _signals = ["AWVALID", "AWADDR", "AWREADY", # Write address channel - "WVALID", "WREADY", "WDATA", "WSTRB", # Write data channel - "BVALID", "BREADY", "BRESP", # Write response channel - "ARVALID", "ARADDR", "ARREADY", # Read address channel - "RVALID", "RREADY", "RRESP", "RDATA"] # Read data channel + _signals = ["AWVALID", "AWADDR", "AWREADY", # Write address channel + "WVALID", "WREADY", "WDATA", "WSTRB", # Write data channel + "BVALID", "BREADY", "BRESP", # Write response channel + "ARVALID", "ARADDR", "ARREADY", # Read address channel + "RVALID", "RREADY", "RRESP", "RDATA"] # Read data channel def __init__(self, entity, name, clock): BusDriver.__init__(self, entity, name, clock) @@ -65,7 +67,6 @@ def __init__(self, entity, name, clock): self.read_address_busy = Lock("%s_rabusy" % name) self.write_data_busy = Lock("%s_wbusy" % name) - @cocotb.coroutine def _send_write_address(self, address, delay=0): """ @@ -75,8 +76,8 @@ def _send_write_address(self, address, delay=0): for cycle in range(delay): yield RisingEdge(self.clock) - self.bus.AWADDR <= address - self.bus.AWVALID <= 1 + self.bus.AWADDR <= address + self.bus.AWVALID <= 1 while True: yield ReadOnly() @@ -84,7 +85,7 @@ def _send_write_address(self, address, delay=0): break yield RisingEdge(self.clock) yield RisingEdge(self.clock) - self.bus.AWVALID <= 0 + self.bus.AWVALID <= 0 self.write_address_busy.release() @cocotb.coroutine @@ -96,9 +97,9 @@ def _send_write_data(self, data, delay=0, byte_enable=0xF): for cycle in range(delay): yield RisingEdge(self.clock) - self.bus.WDATA <= data - self.bus.WVALID <= 1 - self.bus.WSTRB <= byte_enable + self.bus.WDATA <= data + self.bus.WVALID <= 1 + self.bus.WSTRB <= byte_enable while True: yield ReadOnly() @@ -106,23 +107,23 @@ def _send_write_data(self, data, delay=0, byte_enable=0xF): break yield RisingEdge(self.clock) yield RisingEdge(self.clock) - self.bus.WVALID <= 0 + self.bus.WVALID <= 0 self.write_data_busy.release() - @cocotb.coroutine - def write(self, address, value, byte_enable=0xf, address_latency=0, data_latency=0): + def write(self, address, value, byte_enable=0xf, address_latency=0, + data_latency=0): """ Write a value to an address. - The *_latency KWargs allow control over the delta + The *_latency KWargs allow control over the delta """ - - - c_addr = cocotb.fork(self._send_write_address(address, delay=address_latency)) - c_data = cocotb.fork(self._send_write_data(value, byte_enable=byte_enable, delay=data_latency)) - + c_addr = cocotb.fork(self._send_write_address(address, + delay=address_latency)) + c_data = cocotb.fork(self._send_write_data(value, + byte_enable=byte_enable, + delay=data_latency)) if c_addr: yield c_addr.join() @@ -136,16 +137,15 @@ def write(self, address, value, byte_enable=0xf, address_latency=0, data_latency result = self.bus.BRESP.value break yield RisingEdge(self.clock) - - yield RisingEdge(self.clock) - + + yield RisingEdge(self.clock) + if int(result): - raise AXIReadError("Write to address 0x%08x failed with BRESP: %d" %( - address, int(result))) + raise AXIReadError("Write to address 0x%08x failed with BRESP: %d" + % (address, int(result))) raise ReturnValue(result) - @cocotb.coroutine def read(self, address, sync=True): """ @@ -154,8 +154,8 @@ def read(self, address, sync=True): if sync: yield RisingEdge(self.clock) - self.bus.ARADDR <= address - self.bus.ARVALID <= 1 + self.bus.ARADDR <= address + self.bus.ARVALID <= 1 while True: yield ReadOnly() @@ -164,7 +164,7 @@ def read(self, address, sync=True): yield RisingEdge(self.clock) yield RisingEdge(self.clock) - self.bus.ARVALID <= 0 + self.bus.ARVALID <= 0 while True: yield ReadOnly() @@ -175,13 +175,12 @@ def read(self, address, sync=True): yield RisingEdge(self.clock) if int(result): - raise AXIReadError("Read address 0x%08x failed with RRESP: %d" %( - address, int(result))) + raise AXIReadError("Read address 0x%08x failed with RRESP: %d" % + (address, int(result))) raise ReturnValue(data) - class AXI4Slave(BusDriver): ''' AXI4 Slave @@ -189,12 +188,12 @@ class AXI4Slave(BusDriver): Monitors an internal memory and handles read and write requests. ''' _signals = [ - "ARREADY", "ARVALID", "ARADDR", # Read address channel + "ARREADY", "ARVALID", "ARADDR", # Read address channel "ARLEN", "ARSIZE", "ARBURST", "ARPROT", - "RREADY", "RVALID", "RDATA", "RLAST",# Read response channel + "RREADY", "RVALID", "RDATA", "RLAST", # Read response channel - "AWREADY", "AWADDR", "AWVALID", # Write address channel + "AWREADY", "AWADDR", "AWVALID", # Write address channel "AWPROT", "AWSIZE", "AWBURST", "AWLEN", "WREADY", "WVALID", "WDATA", @@ -211,9 +210,10 @@ class AXI4Slave(BusDriver): "BID", "RID", "WID" ] - def __init__(self, entity, name, clock, memory, callback=None, event=None, big_endian=False): + def __init__(self, entity, name, clock, memory, callback=None, event=None, + big_endian=False): - BusDriver.__init__(self,entity,name,clock) + BusDriver.__init__(self, entity, name, clock) self.clock = clock self.big_endain = big_endian @@ -230,9 +230,9 @@ def __init__(self, entity, name, clock, memory, callback=None, event=None, big_e cocotb.fork(self._read_data()) cocotb.fork(self._write_data()) - def _size_to_bytes_in_beat(self,AxSIZE): + def _size_to_bytes_in_beat(self, AxSIZE): if AxSIZE < 7: - return 2**AxSIZE + return 2 ** AxSIZE return None @cocotb.coroutine @@ -249,11 +249,11 @@ def _write_data(self): yield clock_re yield ReadOnly() - _awaddr = int(self.bus.AWADDR) - _awlen = int(self.bus.AWLEN) - _awsize = int(self.bus.AWSIZE) + _awaddr = int(self.bus.AWADDR) + _awlen = int(self.bus.AWLEN) + _awsize = int(self.bus.AWSIZE) _awburst = int(self.bus.AWBURST) - _awprot = int(self.bus.AWPROT) + _awprot = int(self.bus.AWPROT) burst_length = _awlen + 1 bytes_in_beat = self._size_to_bytes_in_beat(_awsize) @@ -263,7 +263,7 @@ def _write_data(self): if __debug__: self.log.debug( "AWADDR %d\n" % _awaddr + - "AWLEN %d\n" % _awlen + + "AWLEN %d\n" % _awlen + "AWSIZE %d\n" % _awsize + "AWBURST %d\n" % _awburst + "BURST_LENGTH %d\n" % burst_length + @@ -277,13 +277,15 @@ def _write_data(self): if self.bus.WVALID.value: word = self.bus.WDATA.value word.big_endian = self.big_endain - self._memory[_awaddr+((burst_length-burst_count)*bytes_in_beat):_awaddr+(((burst_length-burst_count)+1)*bytes_in_beat)] = array.array('B',word.get_buff()) + _burst_diff = burst_length - burst_count + _st = _awaddr + (_burst_diff * bytes_in_beat) # start + _end = _awaddr + ((_burst_diff + 1) * bytes_in_beat) # end + self._memory[_st:_end] = array.array('B', word.get_buff()) burst_count -= 1 if burst_count == 0: break yield clock_re - @cocotb.coroutine def _read_data(self): clock_re = RisingEdge(self.clock) @@ -296,11 +298,11 @@ def _read_data(self): yield clock_re yield ReadOnly() - _araddr = int(self.bus.ARADDR) - _arlen = int(self.bus.ARLEN) - _arsize = int(self.bus.ARSIZE) + _araddr = int(self.bus.ARADDR) + _arlen = int(self.bus.ARLEN) + _arsize = int(self.bus.ARSIZE) _arburst = int(self.bus.ARBURST) - _arprot = int(self.bus.ARPROT) + _arprot = int(self.bus.ARPROT) burst_length = _arlen + 1 bytes_in_beat = self._size_to_bytes_in_beat(_arsize) @@ -310,7 +312,7 @@ def _read_data(self): if __debug__: self.log.debug( "ARADDR %d\n" % _araddr + - "ARLEN %d\n" % _arlen + + "ARLEN %d\n" % _arlen + "ARSIZE %d\n" % _arsize + "ARBURST %d\n" % _arburst + "BURST_LENGTH %d\n" % burst_length + @@ -324,8 +326,11 @@ def _read_data(self): self.bus.RVALID <= 1 yield ReadOnly() if self.bus.RREADY.value: - word.buff = self._memory[_araddr+((burst_length-burst_count)*bytes_in_beat):_araddr+(((burst_length-burst_count)+1)*bytes_in_beat)].tostring() - self.bus.RDATA <= word + _burst_diff = burst_length - burst_count + _st = _araddr + (_burst_diff * bytes_in_beat) + _end = _araddr + ((_burst_diff + 1) * bytes_in_beat) + word.buff = self._memory[_st:_end].tostring() + self.bus.RDATA <= word if burst_count == 1: self.bus.RLAST <= 1 yield clock_re @@ -333,4 +338,3 @@ def _read_data(self): self.bus.RLAST <= 0 if burst_count == 0: break - diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 41f1f2b3..405ee504 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -45,18 +45,20 @@ class AvalonMM(BusDriver): """Avalon-MM Driver - Currently we only support the mode required to communicate with SF avalon_mapper which - is a limited subset of all the signals + Currently we only support the mode required to communicate with SF + avalon_mapper which is a limited subset of all the signals - Blocking operation is all that is supported at the moment, and for the near future as well + Blocking operation is all that is supported at the moment, and for the near + future as well Posted responses from a slave are not supported. """ _signals = ["address"] - _optional_signals = ["readdata", "read", "write", "waitrequest", "writedata", "readdatavalid"] + _optional_signals = ["readdata", "read", "write", "waitrequest", + "writedata", "readdatavalid"] def __init__(self, entity, name, clock): BusDriver.__init__(self, entity, name, clock) - self._can_read = False + self._can_read = False self._can_write = False # Drive some sensible defaults (setimmediatevalue to avoid x asserts) @@ -73,10 +75,10 @@ def __init__(self, entity, name, clock): def read(self, address): pass - def write(self, address, value): pass + class AvalonMaster(AvalonMM): """Avalon-MM master """ @@ -86,7 +88,6 @@ def __init__(self, entity, name, clock): self.busy_event = Event("%s_busy" % name) self.busy = False - @coroutine def _acquire_lock(self): if self.busy: @@ -123,7 +124,8 @@ def read(self, address, sync=True): yield self._wait_for_nsignal(self.bus.waitrequest) # Assume readLatency = 1 - # FIXME need to configure this, should take a dictionary of Avalon properties. + # FIXME need to configure this, + # should take a dictionary of Avalon properties. yield RisingEdge(self.clock) # Deassert read @@ -170,9 +172,11 @@ class AvalonMemory(BusDriver): Emulate a memory, with back-door access """ _signals = ["address"] - _optional_signals = ["write", "read", "writedata", "readdatavalid", "readdata", "waitrequest"] + _optional_signals = ["write", "read", "writedata", "readdatavalid", + "readdata", "waitrequest"] - def __init__(self, entity, name, clock, readlatency_min=1, readlatency_max=1, memory=None): + def __init__(self, entity, name, clock, readlatency_min=1, + readlatency_max=1, memory=None): BusDriver.__init__(self, entity, name, clock) self._readable = False @@ -229,10 +233,11 @@ def _respond(self): if resp is not None: if resp is True: - self._val.binstr = "x"*self._width + self._val.binstr = "x" * self._width else: self._val.integer = resp - self.log.debug("sending 0x%x (%s)" % (self._val.integer, self._val.binstr)) + self.log.debug("sending 0x%x (%s)" % + (self._val.integer, self._val.binstr)) self.bus.readdata <= self._val self.bus.readdatavalid <= 1 elif hasattr(self.bus, "readdatavalid"): @@ -244,10 +249,12 @@ def _respond(self): self._pad() addr = self.bus.address.value.integer if addr not in self._mem: - self.log.warning("Attempt to read from uninitialised address 0x%x" % addr) + self.log.warning("Attempt to read from uninitialised " + "address 0x%x" % addr) self._responses.append(True) else: - self.log.debug("Read from address 0x%x returning 0x%x" % (addr, self._mem[addr])) + self.log.debug("Read from address 0x%x returning 0x%x" % + (addr, self._mem[addr])) self._responses.append(self._mem[addr]) if self._writeable and self.bus.write.value: @@ -257,7 +264,6 @@ def _respond(self): self._mem[addr] = data - class AvalonST(ValidatedBusDriver): _signals = ["valid", "data"] @@ -281,7 +287,8 @@ def __init__(self, *args, **kwargs): for configoption, value in config.items(): self.config[configoption] = value - self.log.debug("Setting config option %s to %s" % (configoption, str(value))) + self.log.debug("Setting config option %s to %s" % + (configoption, str(value))) @coroutine def _wait_ready(self): @@ -309,7 +316,8 @@ def _send_string(self, string, sync=True): # FIXME busses that aren't integer numbers of bytes bus_width = int(len(self.bus.data) / 8) - word = BinaryValue(bits=len(self.bus.data), bigEndian=self.config['firstSymbolInHighOrderBits']) + word = BinaryValue(bits=len(self.bus.data), + bigEndian=self.config['firstSymbolInHighOrderBits']) # Drive some defaults since we don't know what state we're in self.bus.empty <= 0 @@ -367,7 +375,6 @@ def _send_string(self, string, sync=True): self.bus.valid <= 0 self.bus.endofpacket <= 0 - @coroutine def _send_iterable(self, pkt, sync=True): """ @@ -431,5 +438,4 @@ def _driver_send(self, pkt, sync=True): else: yield self._send_iterable(pkt, sync=sync) - self.log.info("Sucessfully sent packet of length %d bytes" % (len(pkt))) - + self.log.info("Sucessfully sent packet of length %d bytes" % len(pkt)) diff --git a/cocotb/drivers/opb.py b/cocotb/drivers/opb.py index 51e7c9a7..9a01236d 100644 --- a/cocotb/drivers/opb.py +++ b/cocotb/drivers/opb.py @@ -36,14 +36,17 @@ from cocotb.drivers import BusDriver from cocotb.result import ReturnValue -class OPBException(Exception): pass + +class OPBException(Exception): + pass class OPBMaster(BusDriver): """ On-chip peripheral bus master """ - _signals = ["xferAck", "errAck", "toutSup", "retry", "DBus_out", "select", "RNW", "BE", "ABus", "DBus_in"] + _signals = ["xferAck", "errAck", "toutSup", "retry", "DBus_out", "select", + "RNW", "BE", "ABus", "DBus_in"] _optional_signals = ["seqAddr"] _max_cycles = 16 @@ -127,4 +130,3 @@ def write(self, address, value, sync=True): self.bus.select <= 0 self._release_lock() - diff --git a/cocotb/drivers/xgmii.py b/cocotb/drivers/xgmii.py index 0c545fc8..29a5d638 100644 --- a/cocotb/drivers/xgmii.py +++ b/cocotb/drivers/xgmii.py @@ -8,7 +8,7 @@ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the names of its + * Neither the name of Potential Ventures Ltd nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. @@ -36,44 +36,45 @@ from cocotb.utils import hexdump from cocotb.binary import BinaryValue -_XGMII_IDLE = "\x07" -_XGMII_START = "\xFB" -_XGMII_TERMINATE= "\xFD" +_XGMII_IDLE = "\x07" # noqa +_XGMII_START = "\xFB" # noqa +_XGMII_TERMINATE = "\xFD" # noqa # Preamble is technically supposed to be 7 bytes of 0x55 but it seems that it's # permissible for the start byte to replace one of the preamble bytes # see http://grouper.ieee.org/groups/802/3/10G_study/email/msg04647.html _PREAMBLE_SFD = "\x55\x55\x55\x55\x55\x55\xD5" + class _XGMIIBus(object): """ Helper object for abstracting the underlying bus format - + Index bytes directly on this object, pass a tuple of (value, ctrl) to set a byte. - + For example: - + >>> xgmii = _XGMIIBus(4) >>> xgmii[0] = (_XGMII_IDLE, True) # Control byte >>> xgmii[1] = ("\x55", False) # Data byte """ - + def __init__(self, nbytes, interleaved=True): """ Args: - nbytes (int): The number of bytes transferred per clock cycle - (usually 8 for SDR, 4 for DDR) + nbytes (int): The number of bytes transferred per clock cycle + (usually 8 for SDR, 4 for DDR) Kwargs: - interleaved (bool): The arrangement of control bits on the bus. - - If interleaved we have a bus with 9-bits per - byte, the control bit being the 9th bit of each - byte. - - If not interleaved then we have a byte per data - byte plus a control bit per byte in the MSBs. + interleaved (bool): The arrangement of control bits on the bus. + + If interleaved we have a bus with 9-bits per + byte, the control bit being the 9th bit of each + byte. + + If not interleaved then we have a byte per data + byte plus a control bit per byte in the MSBs. """ self._value = BinaryValue(bits=nbytes*9, bigEndian=False) @@ -104,14 +105,13 @@ def __setitem__(self, index, value): self._value.integer = self._integer - @property def value(self): """ Get the integer representation of this data word suitable for driving onto the bus. - - NB clears the value + + NB clears the value """ self._value.integer = self._integer self._integer = long(0) @@ -130,14 +130,14 @@ def __init__(self, signal, clock, interleaved=True): """ Args: signal (SimHandle): The xgmii data bus - - clock (SimHandle): The associated clock (assumed to be + + clock (SimHandle): The associated clock (assumed to be driven by another coroutine) - + Kwargs: interleaved (bool: Whether control bits are interleaved with the data bytes or not. - + If interleaved the bus is byte0, byte0_control, byte1, byte1_control .... @@ -148,19 +148,21 @@ def __init__(self, signal, clock, interleaved=True): self.log = signal.log self.signal = signal self.clock = clock - self.bus = _XGMIIBus(len(signal)/9, interleaved=interleaved) + self.bus = _XGMIIBus(len(signal)/9, interleaved=interleaved) Driver.__init__(self) @staticmethod def layer1(packet): """Take an Ethernet packet (as a string) and format as a layer 1 packet - Pads to 64-bytes, prepends preamble and appends 4-byte CRC on the end + Pads to 64-bytes, + prepends preamble and appends 4-byte CRC on the end """ if len(packet) < 60: padding = "\x00" * (60 - len(packet)) packet += padding - return _PREAMBLE_SFD + packet + struct.pack("> (i*byte_shift)) & 0xff)) - ctrls.append(bool(value & (1<<(ctrl_base)))) + bytes.append(chr((value >> (i * byte_shift)) & 0xff)) + ctrls.append(bool(value & (1 << ctrl_base))) ctrl_base += ctrl_inc return ctrls, bytes - def _add_payload(self, ctrl, bytes): """Take the payload and return true if more to come""" for index, byte in enumerate(bytes): if ctrl[index]: if byte != _XGMII_TERMINATE: self.log.error("Got control character in XGMII payload") - self.log.info("data = :" + " ".join(["%02X" % ord(b) for b in bytes])) - self.log.info("ctrl = :" + " ".join(["%s" % str(c) for c in ctrl])) + self.log.info("data = :" + + " ".join(["%02X" % ord(b) for b in bytes])) + self.log.info("ctrl = :" + + " ".join(["%s" % str(c) for c in ctrl])) self._pkt = "" return False self._pkt += byte return True - @cocotb.coroutine def _monitor_recv(self): clk = RisingEdge(self.clock) @@ -161,7 +163,8 @@ def _monitor_recv(self): self._pkt = "" continue - expected_crc = struct.pack(" 0""" @@ -205,6 +229,7 @@ def test_afterdelay_in_readonly_valid(dut): if exited is not True: raise TestFailure + @cocotb.coroutine def clock_one(dut): count = 0 @@ -213,6 +238,7 @@ def clock_one(dut): yield Timer(1000) count += 1 + @cocotb.coroutine def clock_two(dut): count = 0 @@ -221,6 +247,7 @@ def clock_two(dut): yield Timer(10000) count += 1 + @cocotb.test(expect_fail=False) def test_coroutine_close_down(dut): clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) @@ -233,23 +260,27 @@ def test_coroutine_close_down(dut): dut.log.info("Back from joins") + @cocotb.coroutine def syntax_error(): yield Timer(100) fail + @cocotb.test(expect_error=True) def test_syntax_error(dut): """Syntax error in the test""" yield clock_gen(dut.clk) fail + @cocotb.test(expect_error=True) def test_coroutine_syntax_error(dut): """Syntax error in a coroutine that we yield""" yield clock_gen(dut.clk) yield syntax_error() + @cocotb.test(expect_error=True) def test_fork_syntax_error(dut): """Syntax error in a coroutine that we fork""" @@ -257,6 +288,7 @@ def test_fork_syntax_error(dut): cocotb.fork(syntax_error()) yield clock_gen(dut.clk) + @cocotb.test() def test_fork_and_monitor(dut, period=1000, clocks=6): cocotb.fork(Clock(dut.clk, period).start()) @@ -267,8 +299,7 @@ def test_fork_and_monitor(dut, period=1000, clocks=6): timer = Timer(period + 10) task = cocotb.fork(count_edges_cycles(dut.clk, clocks)) count = 0 - expect = clocks-1 - + expect = clocks - 1 while True: result = yield [timer, task.join()] @@ -280,10 +311,12 @@ def test_fork_and_monitor(dut, period=1000, clocks=6): else: break if count != expect: - raise TestFailure("Expected to monitor the task %d times but got %d" % ( - expect, count)) + raise TestFailure("Expected to monitor the task %d times but got %d" % + (expect, count)) if result != clocks: - raise TestFailure("Expected task to return %d but got %s" % (clocks, repr(result))) + raise TestFailure("Expected task to return %d but got %s" % + (clocks, repr(result))) + @cocotb.coroutine def count_edges_cycles(signal, edges): @@ -294,6 +327,7 @@ def count_edges_cycles(signal, edges): signal.log.info("Finished, returning %d" % edges) raise ReturnValue(edges) + @cocotb.coroutine def do_single_edge_check(dut, level): """Do test for rising edge""" @@ -310,6 +344,7 @@ def do_single_edge_check(dut, level): if new_value is not level: raise TestError("%s not %d at end" % (dut.clk, level)) + @cocotb.test() def test_rising_edge(dut): """Test that a rising edge can be yielded on""" @@ -323,6 +358,7 @@ def test_rising_edge(dut): if result is fail_timer: raise TestError("Test timed out") + @cocotb.test() def test_falling_edge(dut): """Test that a falling edge can be yielded on""" @@ -336,6 +372,7 @@ def test_falling_edge(dut): if result is fail_timer: raise TestError("Test timed out") + @cocotb.test() def test_either_edge(dut): """Test that either edge can be triggered on""" @@ -371,6 +408,7 @@ def test_either_edge(dut): if dut.clk.value.integer is not 0: raise TestError("Value should be 0") + @cocotb.coroutine def do_clock(dut, limit, period): """Simple clock with a limit""" @@ -382,11 +420,12 @@ def do_clock(dut, limit, period): dut.clk <= 1 limit -= 1 + @cocotb.coroutine def do_edge_count(dut, signal): """Count the edges""" - global edges_seen - count = 0; + global edges_seen + count = 0 while True: yield RisingEdge(signal) edges_seen += 1 @@ -397,12 +436,13 @@ def test_edge_count(dut): """Count the number of edges is as expected""" global edges_seen edges_seen = 0 - clk_period = 100; - edge_count = 10; + clk_period = 100 + edge_count = 10 clock = cocotb.fork(do_clock(dut, edge_count, clk_period)) test = cocotb.fork(do_edge_count(dut, dut.clk)) yield Timer(clk_period * (edge_count + 1)) if edge_count is not edges_seen: - raise TestFailure("Correct edge count failed saw %d wanted %d" % (edges_seen, edge_count)) + raise TestFailure("Correct edge count failed saw %d wanted %d" % + (edges_seen, edge_count)) diff --git a/examples/functionality/tests/test_discovery.py b/examples/functionality/tests/test_discovery.py index 8fe8b54c..e0a04dd1 100644 --- a/examples/functionality/tests/test_discovery.py +++ b/examples/functionality/tests/test_discovery.py @@ -29,6 +29,7 @@ from cocotb.triggers import Timer from cocotb.result import TestError + @cocotb.test() def discover_module_values(dut): """Discover everything in the dut""" @@ -36,6 +37,7 @@ def discover_module_values(dut): for thing in dut: thing.log.info("Found something: %s" % thing.fullname) + @cocotb.test(expect_error=True) def discover_value_not_in_dut(dut): """Try and get a value from the DUT that is not there""" @@ -50,10 +52,9 @@ def access_signal(dut): dut.stream_in_data.setimmediatevalue(1) yield Timer(10) if dut.stream_in_data.value.integer != 1: - raise TestError("%s.%s != %d" % ( - str(dut.stream_in_data.value.integer), - dut.stream_in_data.value.integer)) - + raise TestError("%s.%s != %d" % + (str(dut.stream_in_data), + dut.stream_in_data.value.integer), 1) @cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"], @@ -66,13 +67,15 @@ def access_single_bit(dut): """ dut.stream_in_data <= 0 yield Timer(10) - dut.log.info("%s = %d bits" % (str(dut.stream_in_data), len(dut.stream_in_data))) + dut.log.info("%s = %d bits" % + (str(dut.stream_in_data), len(dut.stream_in_data))) dut.stream_in_data[2] <= 1 yield Timer(10) if dut.stream_out_data_comb.value.integer != (1<<2): raise TestError("%s.%s != %d" % - (str(dut.stream_out_data_comb), - dut.stream_out_data_comb.value.integer, (1<<2))) + (str(dut.stream_out_data_comb), + dut.stream_out_data_comb.value.integer, (1<<2))) + @cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"], skip=cocotb.LANGUAGE in ["vhdl"]) @@ -84,19 +87,22 @@ def access_single_bit_assignment(dut): """ dut.stream_in_data = 0 yield Timer(10) - dut.log.info("%s = %d bits" % (str(dut.stream_in_data), len(dut.stream_in_data))) + dut.log.info("%s = %d bits" % + (str(dut.stream_in_data), len(dut.stream_in_data))) dut.stream_in_data[2] = 1 yield Timer(10) if dut.stream_out_data_comb.value.integer != (1<<2): raise TestError("%s.%s != %d" % - (str(dut.stream_out_data_comb), - dut.stream_out_data_comb.value.integer, (1<<2))) + (str(dut.stream_out_data_comb), + dut.stream_out_data_comb.value.integer, (1<<2))) + @cocotb.test(expect_error=True) def access_single_bit_erroneous(dut): """Access a non-existent single bit""" yield Timer(10) - dut.log.info("%s = %d bits" % (str(dut.stream_in_data), len(dut.stream_in_data))) + dut.log.info("%s = %d bits" % + (str(dut.stream_in_data), len(dut.stream_in_data))) bit = len(dut.stream_in_data) + 4 dut.stream_in_data[bit] <= 1 yield Timer(10) @@ -106,7 +112,8 @@ def access_single_bit_erroneous(dut): def skip_a_test(dut): """This test shouldn't execute""" yield Timer(10) - dut.log.info("%s = %d bits" % (str(dut.stream_in_data), len(dut.stream_in_data))) + dut.log.info("%s = %d bits" % + (str(dut.stream_in_data), len(dut.stream_in_data))) bit = len(dut.stream_in_data) + 4 dut.stream_in_data[bit] <= 1 yield Timer(10) diff --git a/examples/functionality/tests/test_external.py b/examples/functionality/tests/test_external.py index 1d94dcc0..290824d4 100644 --- a/examples/functionality/tests/test_external.py +++ b/examples/functionality/tests/test_external.py @@ -45,6 +45,7 @@ test_count = 0 g_dut = None + # Tests relating to calling convention and operation @cocotb.function @@ -58,6 +59,7 @@ def decorated_test_read(dut, signal): raise ReturnValue(test_count) + def test_read(dut, signal): global test_count dut.log.info("Inside test_read") @@ -65,6 +67,7 @@ def test_read(dut, signal): yield RisingEdge(dut.clk) test_count += 1 + def hal_read(function): global g_dut global test_count @@ -72,11 +75,15 @@ def hal_read(function): function(g_dut, g_dut.stream_out_ready) g_dut.log.info("Cycles seen is %d" % test_count) + def create_thread(function): """ Create a thread to simulate an external calling entity """ - new_thread = threading.Thread(group=None, target=hal_read, name="Test_thread", args=([function]), kwargs={}) + new_thread = threading.Thread(group=None, target=hal_read, + name="Test_thread", args=([function]), + kwargs={}) new_thread.start() + @cocotb.coroutine def clock_gen(clock): """Drive the clock signal""" @@ -89,12 +96,15 @@ def clock_gen(clock): clock.log.warning("Clock generator finished!") + @cocotb.test(expect_fail=False, skip=True) def test_callable(dut): - """Test ability to call a function that will block but allow other coroutines to continue + """Test ability to call a function that will block but allow other + coroutines to continue - Test creates a thread to simulate another context. This thread will then "block" for - 5 clock cycles. 5 cycles should be seen by the thread + Test creates a thread to simulate another context. This thread will then + "block" for 5 clock cycles. + 5 cycles should be seen by the thread """ global g_dut global test_count @@ -108,12 +118,15 @@ def test_callable(dut): print("Count was %d" % test_count) raise TestFailure + @cocotb.test(expect_fail=True, skip=True) def test_callable_fail(dut): - """Test ability to call a function that will block but allow other coroutines to continue + """Test ability to call a function that will block but allow other + coroutines to continue - Test creates a thread to simulate another context. This thread will then "block" for - 5 clock cycles but not using the function decorator. No cycls should be seen. + Test creates a thread to simulate another context. This thread will then + "block" for 5 clock cycles but not using the function decorator. + No cycls should be seen. """ global g_dut global test_count @@ -126,25 +139,30 @@ def test_callable_fail(dut): if test_count is not 5: raise TestFailure + def test_ext_function(dut): - #dut.log.info("Sleeping") + # dut.log.info("Sleeping") return 2 + @cocotb.function def yield_to_readwrite(dut): yield RisingEdge(dut.clk) dut.log.info("Returning from yield_to_readwrite") raise ReturnValue(2) + def test_ext_function_access(dut): return yield_to_readwrite(dut) + def test_ext_function_return(dut): value = dut.clk.value.integer - #dut.log.info("Sleeping and returning %s" % value) - #time.sleep(0.2) + # dut.log.info("Sleeping and returning %s" % value) + # time.sleep(0.2) return value + @cocotb.coroutine def clock_monitor(dut): count = 0 @@ -153,21 +171,26 @@ def clock_monitor(dut): yield Timer(1000) count += 1 + @cocotb.test(expect_fail=False) def test_ext_call_return(dut): - """Test ability to yeild on an external non cocotb coroutine decorated function""" + """Test ability to yeild on an external non cocotb coroutine decorated + function""" mon = cocotb.scheduler.queue(clock_monitor(dut)) clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) value = yield external(test_ext_function)(dut) dut.log.info("Value was %d" % value) + @cocotb.test(expect_fail=False) def test_ext_call_nreturn(dut): - """Test ability to yeild on an external non cocotb coroutine decorated function""" + """Test ability to yeild on an external non cocotb coroutine decorated + function""" mon = cocotb.scheduler.queue(clock_monitor(dut)) clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) yield external(test_ext_function)(dut) + @cocotb.test(expect_fail=False) def test_multiple_externals(dut): clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) @@ -176,6 +199,7 @@ def test_multiple_externals(dut): value = yield external(test_ext_function)(dut) dut.log.info("Second one completed") + @cocotb.test(expect_fail=False) def test_external_from_readonly(dut): clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) @@ -184,9 +208,10 @@ def test_external_from_readonly(dut): dut.log.info("In readonly") value = yield external(test_ext_function_access)(dut) + @cocotb.test(expect_fail=True, skip=True) def ztest_ext_exit_error(dut): - """Test that a premature exit of the sim at it's request still results in the - clean close down of the sim world""" + """Test that a premature exit of the sim at it's request still results in + the clean close down of the sim world""" yield external(test_ext_function)(dut) yield Timer(1000) diff --git a/examples/functionality/tests/test_regression.py b/examples/functionality/tests/test_regression.py index 2a5c320f..381edf92 100644 --- a/examples/functionality/tests/test_regression.py +++ b/examples/functionality/tests/test_regression.py @@ -6,6 +6,7 @@ from cocotb.result import TestFailure from cocotb.binary import BinaryValue + @cocotb.coroutine def send_data(dut): dut.stream_in_valid = 1 @@ -45,7 +46,6 @@ def issue_120_scheduling(dut): dut.stream_in_valid = 0 yield RisingEdge(dut.clk) - @cocotb.test(skip=True) @@ -57,7 +57,7 @@ def issue_142_overflow_error(dut): def _compare(value): if int(dut.stream_in_data_wide.value) != int(value): raise TestFailure("Expecting 0x%x but got 0x%x on %s" % ( - int(value), int(dut.stream_in_data_wide.value), + int(value), int(dut.stream_in_data_wide.value), str(dut.stream_in_data_wide))) # Wider values are transparently converted to BinaryValues @@ -69,4 +69,3 @@ def _compare(value): dut.stream_in_data_wide = value yield RisingEdge(dut.clk) _compare(value) - diff --git a/examples/mean/tests/test_mean.py b/examples/mean/tests/test_mean.py index c5b346f7..b333b036 100644 --- a/examples/mean/tests/test_mean.py +++ b/examples/mean/tests/test_mean.py @@ -26,8 +26,8 @@ def _monitor_recv(self): yield RisingEdge(self.clock) yield ReadOnly() if self.bus.valid.value: - self._recv(int(self.bus.data.value)) - + self._recv(int(self.bus.data.value)) + @cocotb.coroutine def clock_gen(signal, period=10000): @@ -36,87 +36,91 @@ def clock_gen(signal, period=10000): yield Timer(period/2) signal <= 1 yield Timer(period/2) - - + + @cocotb.coroutine def value_test(dut, num): """ Test n*num/n = num """ - + data_width = int(dut.DATA_WIDTH.value) bus_width = int(dut.BUS_WIDTH.value) - dut.log.info('Detected DATA_WIDTH = %d, BUS_WIDTH = %d' %(data_width, bus_width)) - + dut.log.info('Detected DATA_WIDTH = %d, BUS_WIDTH = %d' % + (data_width, bus_width)) + cocotb.fork(clock_gen(dut.clk, period=clock_period)) - + dut.rst <= 1 for i in range(bus_width): dut.i_data[i] <= 0 - dut.i_valid <= 0 + dut.i_valid <= 0 + yield RisingEdge(dut.clk) yield RisingEdge(dut.clk) - yield RisingEdge(dut.clk) dut.rst <= 0 - + for i in range(bus_width): dut.i_data[i] <= num - dut.i_valid <= 1 + dut.i_valid <= 1 yield RisingEdge(dut.clk) - dut.i_valid <= 0 + dut.i_valid <= 0 yield RisingEdge(dut.clk) got = int(dut.o_data.value) - + if got != num: raise TestFailure( - 'Mismatch detected: got %d, exp %d!' %(got, num)) + 'Mismatch detected: got %d, exp %d!' % (got, num)) @cocotb.test() def mean_basic_test(dut): - """ Test n*5/n = 5 """ - yield value_test(dut, 5) + """ Test n*5/n = 5 """ + yield value_test(dut, 5) @cocotb.test() def mean_overflow_test(dut): - """ Test for overflow n*max_val/n = max_val """ + """ Test for overflow n*max_val/n = max_val """ data_width = int(dut.DATA_WIDTH.value) - yield value_test(dut, 2**data_width-1) + yield value_test(dut, 2**data_width - 1) @cocotb.test() def mean_randomised_test(dut): """ Test mean of random numbers multiple times """ - -# dut_in = StreamBusMonitor(dut, "i", dut.clk) # this doesn't work; VPI Error vpi_get_value(): ERROR - Cannot get a value for an object of type vpiArrayVar. + + # dut_in = StreamBusMonitor(dut, "i", dut.clk) # this doesn't work: + # VPI Error vpi_get_value(): + # ERROR - Cannot get a value for an object of type vpiArrayVar. + dut_out = StreamBusMonitor(dut, "o", dut.clk) exp_out = [] scoreboard = Scoreboard(dut) scoreboard.add_interface(dut_out, exp_out) - + data_width = int(dut.DATA_WIDTH.value) bus_width = int(dut.BUS_WIDTH.value) - dut.log.info('Detected DATA_WIDTH = %d, BUS_WIDTH = %d' %(data_width, bus_width)) + dut.log.info('Detected DATA_WIDTH = %d, BUS_WIDTH = %d' % + (data_width, bus_width)) cocotb.fork(clock_gen(dut.clk, period=clock_period)) - + dut.rst <= 1 for i in range(bus_width): dut.i_data[i] = 0 - dut.i_valid <= 0 - yield RisingEdge(dut.clk) - yield RisingEdge(dut.clk) + dut.i_valid <= 0 + yield RisingEdge(dut.clk) + yield RisingEdge(dut.clk) dut.rst <= 0 - + for j in range(10): nums = [] for i in range(bus_width): - x = random.randint(0, 2**data_width-1) - dut.i_data[i] = x + x = random.randint(0, 2**data_width - 1) + dut.i_data[i] = x nums.append(x) - dut.i_valid <= 1 - - nums_mean = sum(nums)//bus_width + dut.i_valid <= 1 + + nums_mean = sum(nums) // bus_width exp_out.append(nums_mean) yield RisingEdge(dut.clk) dut.i_valid <= 0 - diff --git a/examples/mixed_language/tests/test_mixed_language.py b/examples/mixed_language/tests/test_mixed_language.py index bf516a94..1d079c7c 100644 --- a/examples/mixed_language/tests/test_mixed_language.py +++ b/examples/mixed_language/tests/test_mixed_language.py @@ -2,6 +2,7 @@ from cocotb.triggers import Timer from cocotb.result import TestFailure + @cocotb.test() def mixed_language_test(dut): """ diff --git a/examples/ping_tun_tap/tests/test_icmp_reply.py b/examples/ping_tun_tap/tests/test_icmp_reply.py index 8a0db866..38c3c33d 100644 --- a/examples/ping_tun_tap/tests/test_icmp_reply.py +++ b/examples/ping_tun_tap/tests/test_icmp_reply.py @@ -50,7 +50,8 @@ def create_tun(name="tun0", ip="192.168.255.1"): ifr = struct.pack('16sH', name, IFF_TUN | IFF_NO_PI) fcntl.ioctl(tun, TUNSETIFF, ifr) fcntl.ioctl(tun, TUNSETOWNER, 1000) - subprocess.check_call('ifconfig tun0 %s up pointopoint 192.168.255.2 up' % ip, shell=True) + subprocess.check_call('ifconfig tun0 %s up pointopoint 192.168.255.2 up' % + ip, shell=True) return tun @@ -68,14 +69,13 @@ def tun_tap_example_test(dut): cocotb.fork(Clock(dut.clk, 5000).start()) - stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) + stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) stream_out = AvalonSTMonitor(dut, "stream_out", dut.clk) # Enable verbose logging so we can see what's going on stream_in.log.setLevel(logging.DEBUG) stream_out.log.setLevel(logging.DEBUG) - # Reset the DUT dut.log.debug("Resetting DUT") dut.reset_n <= 0 @@ -86,7 +86,6 @@ def tun_tap_example_test(dut): dut.stream_out_ready <= 1 dut.log.debug("Out of reset") - # Create our interface (destroyed at the end of the test) tun = create_tun() fd = tun.fileno() diff --git a/examples/plusargs/plusargs.py b/examples/plusargs/plusargs.py index a05d332e..a7592e11 100644 --- a/examples/plusargs/plusargs.py +++ b/examples/plusargs/plusargs.py @@ -36,6 +36,7 @@ import sys + @cocotb.test() def plusargs_test(dut): """Demonstrates plusarg access from Python test""" @@ -46,4 +47,3 @@ def plusargs_test(dut): print("COCOTB:", name, cocotb.plusargs[name]) yield Timer(10000) - diff --git a/examples/sim_exit/tests/test_closedown.py b/examples/sim_exit/tests/test_closedown.py index d74b0120..22b03bd2 100644 --- a/examples/sim_exit/tests/test_closedown.py +++ b/examples/sim_exit/tests/test_closedown.py @@ -37,6 +37,7 @@ from cocotb.triggers import Timer, Join, RisingEdge from cocotb.clock import Clock + def test_read(dut): global test_count dut.log.info("Inside test_read") @@ -44,14 +45,17 @@ def test_read(dut): yield RisingEdge(dut.clk) test_count += 1 + @cocotb.coroutine def run_external(dut): yield cocotb.external(test_read)(dut) + @cocotb.coroutine def clock_mon(dut): yield RisingEdge(dut.clk) + @cocotb.test(expect_fail=True) def test_failure_from_system_task(dut): """Allow the dut to call $fail_test() from verilog""" From 5d7ab3948bbc6b55ad47cba2be9a234f83de8485 Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Wed, 15 Apr 2015 21:40:07 +1000 Subject: [PATCH 0840/2656] Fixes #238: fix IPython tab complete by checking for other class introspection techniques --- cocotb/handle.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cocotb/handle.py b/cocotb/handle.py index 13e13c11..5b47cce0 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -80,6 +80,10 @@ def __getattr__(self, name): """ Query the simulator for a object with the specified name and cache the result to build a tree """ + # python's builtin dir and IPython's dir2 search for these, + # raise an AttributeError to avoid incorrect calls to _raise_testerror + if name in ["__methods__","__members__","trait_names","_getAttributeNames"]: + raise AttributeError, name if name in self._sub_handles: return self._sub_handles[name] new_handle = simulator.get_handle_by_name(self._handle, name) From c31c5873bff8731f61e5c55eeaa3a1265f43c53a Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Wed, 15 Apr 2015 21:59:53 +1000 Subject: [PATCH 0841/2656] Fixes #238: should use newer exception syntax --- cocotb/handle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 5b47cce0..79b8f937 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -83,7 +83,7 @@ def __getattr__(self, name): # python's builtin dir and IPython's dir2 search for these, # raise an AttributeError to avoid incorrect calls to _raise_testerror if name in ["__methods__","__members__","trait_names","_getAttributeNames"]: - raise AttributeError, name + raise AttributeError(name) if name in self._sub_handles: return self._sub_handles[name] new_handle = simulator.get_handle_by_name(self._handle, name) From 74de7a73ac15d5fd31f3e394c45bec01a33e13c0 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 19 Apr 2015 13:12:44 +0100 Subject: [PATCH 0842/2656] Issue #17: Re-implement iterator funtionality in VPI --- .../functionality/tests/test_discovery.py | 6 ++++- lib/gpi/GpiCbHdl.cpp | 5 ++++ lib/gpi/GpiCommon.cpp | 9 +------ lib/gpi/gpi_priv.h | 15 ++++++++++- lib/vpi/VpiImpl.cpp | 26 +++++++++++++++++++ lib/vpi/VpiImpl.h | 2 ++ 6 files changed, 53 insertions(+), 10 deletions(-) diff --git a/examples/functionality/tests/test_discovery.py b/examples/functionality/tests/test_discovery.py index 8fe8b54c..bc232505 100644 --- a/examples/functionality/tests/test_discovery.py +++ b/examples/functionality/tests/test_discovery.py @@ -27,14 +27,18 @@ import cocotb from cocotb.triggers import Timer -from cocotb.result import TestError +from cocotb.result import TestError, TestFailure @cocotb.test() def discover_module_values(dut): """Discover everything in the dut""" yield Timer(0) + count = 0 for thing in dut: thing.log.info("Found something: %s" % thing.fullname) + count += 1 + if count < 2: + raise TestFailure("Expected to discover things in the DUT") @cocotb.test(expect_error=True) def discover_value_not_in_dut(dut): diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index 8f424d75..715f5b93 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -27,6 +27,11 @@ ******************************************************************************/ #include "gpi_priv.h" +GpiObjHdl* GpiIterator::next_handle() +{ + return m_impl->next_handle(this); +} + const char * GpiObjHdl::get_name_str(void) { diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index d4dc20e7..8434b49d 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -237,25 +237,18 @@ gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) gpi_iterator_hdl gpi_iterate(uint32_t type, gpi_sim_hdl base) { -#if 0 GpiObjHdl *obj_hdl = sim_to_hdl(base); GpiIterator *iter = obj_hdl->m_impl->iterate_handle(type, obj_hdl); if (!iter) { return NULL; } - iter->parent = obj_hdl; return (gpi_iterator_hdl)iter; -#endif - return NULL; } gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) { -#if 0 GpiIterator *iter = sim_to_hdl(iterator); - return (gpi_sim_hdl)iter->parent->next_handle(iter); -#endif - return NULL; + return (gpi_sim_hdl)iter->next_handle(); } const char *gpi_get_signal_value_binstr(gpi_sim_hdl sig_hdl) diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index f58ae35a..5d2c59d1 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -186,9 +186,20 @@ class GpiClockHdl { class GpiIterator { public: - GpiObjHdl *parent; + GpiIterator(GpiImplInterface *impl, void *hdl) : m_impl(impl), m_iter_hdl(hdl) { } + virtual ~GpiIterator() { } + + GpiObjHdl* next_handle(); + +private: + GpiIterator() { } // Disable default constructor + +public: + GpiImplInterface *m_impl; // VPI/VHPI/FLI routines + void *m_iter_hdl; }; + class GpiImplInterface { public: GpiImplInterface(const std::string& name); @@ -204,6 +215,8 @@ class GpiImplInterface { virtual GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent) = 0; virtual GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent) = 0; virtual GpiObjHdl *get_root_handle(const char *name) = 0; + virtual GpiIterator *iterate_handle(uint32_t type, GpiObjHdl *obj_hdl) = 0; + virtual GpiObjHdl *next_handle(GpiIterator *iter) = 0; /* Callback related, these may (will) return the same handle*/ virtual GpiCbHdl *register_timed_callback(uint64_t time_ps) = 0; diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index c867f9b1..a52249f2 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -202,6 +202,32 @@ GpiObjHdl *VpiImpl::get_root_handle(const char* name) return NULL; } +GpiIterator *VpiImpl::iterate_handle(uint32_t type, GpiObjHdl *obj_hdl) +{ + vpiHandle vpi_hdl = obj_hdl->get_handle(); + + vpiHandle iterator; + iterator = vpi_iterate(vpiNet, vpi_hdl); + + GpiIterator *new_iter; + new_iter = new GpiIterator(this, iterator); + return new_iter; +} + +GpiObjHdl *VpiImpl::next_handle(GpiIterator *iter) +{ + vpiHandle obj; + GpiObjHdl *new_obj = NULL; + + obj = vpi_scan((vpiHandle)iter->m_iter_hdl); + + if (NULL==obj) + return new_obj; + + std::string name = vpi_get_str(vpiFullName, obj); + new_obj = create_gpi_obj_from_handle(obj, name); + return new_obj; +} GpiCbHdl *VpiImpl::register_timed_callback(uint64_t time_ps) { diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 0763c843..b561b16c 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -206,6 +206,8 @@ class VpiImpl : public GpiImplInterface { /* Hierachy related */ GpiObjHdl *get_root_handle(const char *name); + GpiIterator *iterate_handle(uint32_t type, GpiObjHdl *obj_hdl); + GpiObjHdl *next_handle(GpiIterator *iter); /* Callback related, these may (will) return the same handle*/ GpiCbHdl *register_timed_callback(uint64_t time_ps); From b11c7f255a596ed3b208cd39490879541a82bc17 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 26 Apr 2015 19:23:06 +0100 Subject: [PATCH 0843/2656] Implement __len__ on AvalonMaster so users can query address space size --- cocotb/drivers/avalon.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 405ee504..e0289b69 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -88,6 +88,9 @@ def __init__(self, entity, name, clock): self.busy_event = Event("%s_busy" % name) self.busy = False + def __len__(self): + return 2**len(self.bus.address) + @coroutine def _acquire_lock(self): if self.busy: From 70ac30334212d703fa7933980b6fb0725257c9fa Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 27 Apr 2015 11:59:25 +0100 Subject: [PATCH 0844/2656] Issue #237: Add support for VPI Int types --- include/vpi_user.h | 2 +- lib/vpi/VpiImpl.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/vpi_user.h b/include/vpi_user.h index 9ff6ece2..4a250a4d 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -70,7 +70,7 @@ typedef uint32_t *vpiHandle; #define vpiRefObj 608 #define vpiPackedArrayVar 623 #define vpiEnumNet 680 /* SystemVerilog */ - +#define vpiIntVar 612 #define vpiStop 66 /* execute simulator's $stop */ #define vpiFinish 67 /* execute simulator's $finish */ #define vpiReset 68 /* execute simulator's $reset */ diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index c867f9b1..9d35a5a7 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -89,6 +89,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &n case vpiRegArray: case vpiNetArray: case vpiEnumNet: + case vpiIntVar: new_obj = new VpiSignalObjHdl(this, new_hdl); break; case vpiStructVar: From 5705cfab89a53a49d6be3c112d38a308186de8b5 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 1 May 2015 18:42:35 +0100 Subject: [PATCH 0845/2656] Fixes #242: Add COCOTB_ATTACH environment variable to delay initialisation for debug --- lib/embed/gpi_embed.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 8eb60d34..a943d3fc 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -85,6 +85,15 @@ void embed_init_python(void) /* Swap out and return current thread state and release the GIL */ gtstate = PyEval_SaveThread(); + + /* Before returning we check if the user wants pause the simulator thread + such that they can attach */ + const char *pause = getenv("COCOTB_ATTACH"); + if (pause) { + int sleep_time = atoi(pause); + fprintf(stderr, "Waiting for %d seconds - Attach to %d\n", sleep_time, getpid()); + sleep(sleep_time); + } FEXIT; } From a48e45695b280b396772488949449dbd8362d6b3 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 8 May 2015 11:59:01 +0100 Subject: [PATCH 0846/2656] Make Avalon drivers more aggressive by driving 'X' when idle --- cocotb/drivers/avalon.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index e0289b69..cd1eb563 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -68,6 +68,9 @@ def __init__(self, entity, name, clock): if hasattr(self.bus, "write"): self.bus.write.setimmediatevalue(0) + v = self.bus.writedata.value + v.binstr = "x" * len(self.bus.writedata) + self.bus.writedata <= v self._can_write = True self.bus.address.setimmediatevalue(0) @@ -167,6 +170,9 @@ def write(self, address, value): # Deassert write yield RisingEdge(self.clock) self.bus.write <= 0 + v = self.bus.writedata.value + v.binstr = "x" * len(self.bus.writedata) + self.bus.writedata <= v self._release_lock() @@ -293,6 +299,15 @@ def __init__(self, *args, **kwargs): self.log.debug("Setting config option %s to %s" % (configoption, str(value))) + word = BinaryValue(bits=len(self.bus.data), + bigEndian=self.config['firstSymbolInHighOrderBits']) + word.binstr = ("x"*len(self.bus.data)) + self.bus.valid <= 0 + self.bus.data <= word + self.bus.empty <= word + self.bus.startofpacket <= word + self.bus.endofpacket <= word + @coroutine def _wait_ready(self): """Wait for a ready cycle on the bus before continuing @@ -323,7 +338,7 @@ def _send_string(self, string, sync=True): bigEndian=self.config['firstSymbolInHighOrderBits']) # Drive some defaults since we don't know what state we're in - self.bus.empty <= 0 + # self.bus.empty <= 0 self.bus.startofpacket <= 0 self.bus.endofpacket <= 0 self.bus.valid <= 0 @@ -350,7 +365,7 @@ def _send_string(self, string, sync=True): self.bus.valid <= 1 if firstword: - self.bus.empty <= 0 + #self.bus.empty <= 0 self.bus.startofpacket <= 1 firstword = False else: @@ -377,6 +392,11 @@ def _send_string(self, string, sync=True): yield clkedge self.bus.valid <= 0 self.bus.endofpacket <= 0 + word.binstr = ("x"*len(self.bus.data)) + self.bus.data <= word + self.bus.empty <= word + self.bus.startofpacket <= word + self.bus.endofpacket <= word @coroutine def _send_iterable(self, pkt, sync=True): From f8ca48587aeeacdbddb7ad6439239d7c0e775d20 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 10 May 2015 22:12:43 +0100 Subject: [PATCH 0847/2656] Add an example of a socket-based driver attachment --- cocotb/drivers/__init__.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index b312ae9b..7d454a1f 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -314,3 +314,29 @@ def set_valid_generator(self, valid_generator=None): self.valid_generator = valid_generator self._next_valids() + + +@cocotb.coroutine +def polled_socket_attachment(driver, sock): + """ + Non-blocking socket attachment that queues any payload received from the + socket to be queued for sending into the driver + """ + import socket, errno + sock.setblocking(False) + driver.log.info("Listening for data from %s" % repr(sock)) + while True: + yield RisingEdge(driver.clock) + try: + data = sock.recv(4096) + except socket.error as e: + if e.args[0] in [errno.EAGAIN, errno.EWOULDBLOCK]: + continue + else: + driver.log.error(repr(e)) + raise + if not len(data): + driver.log.info("Remote end closed the connection") + break + driver.append(data) + From bf39ec0f11188442f9c1258288e000dd13ce2b57 Mon Sep 17 00:00:00 2001 From: chiggs Date: Sun, 10 May 2015 22:18:07 +0100 Subject: [PATCH 0848/2656] Permit overriding of RTL_LIBRARY for coverage reporting on Aldec --- makefiles/simulators/Makefile.aldec | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 3ff0a0cb..cbcbaf96 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -35,6 +35,7 @@ else CMD := vsimsa endif +RTL_LIBRARY ?= work ALOG_ARGS += +define+COCOTB_SIM -dbg -pli libgpi ACOM_ARGS += -dbg @@ -82,10 +83,10 @@ ifeq ($(GUI),1) else echo "run -all" >> $@ echo "endsim" >> $@ -endif ifeq ($(COVERAGE),1) - echo "acdb report -db work.acdb -html -o coverage/acdb_report.html" >> $@ - echo "acdb report -db work.acdb -txt -o coverage/acdb_report.txt" >> $@ + echo "acdb report -db $(RTL_LIBRARY).acdb -html -o coverage/acdb_report.html" >> $@ + echo "acdb report -db $(RTL_LIBRARY).acdb -txt -o coverage/acdb_report.txt" >> $@ +endif endif # auto-detect riviera dir from system path From 14c3c56c2e79d41edbdfbae5387d00c303cd1b79 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 11 May 2015 22:27:00 +0100 Subject: [PATCH 0849/2656] Issue #243: Do not call exit on initialisation error, allow sim to close down of own free will --- lib/embed/gpi_embed.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index a943d3fc..ab326252 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -279,7 +279,6 @@ int embed_sim_init(gpi_sim_info_t *info) } else { PyErr_Print(); fprintf(stderr,"Cocotb initialisation failed - exiting\n"); - exit(1); } FEXIT From d26f30c7659130d3e28a7717980a98b653f249e3 Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Mon, 20 Apr 2015 21:45:49 +1000 Subject: [PATCH 0850/2656] ius : updates to progress mixed language example under ius Some makefile changes to get mixed lang ius sims running. And always compile both vpi and vhpi shared libraries. I'm still getting errors once the sim starts. But at least the vhpi gets loaded and the vhdl sources get compiled in. "make SIM=ius" in the mixed language example wasn't compiling libvhpi.so And I don't see any reason to not always compile both libraries. --- examples/mixed_language/tests/Makefile | 2 -- makefiles/simulators/Makefile.ius | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/mixed_language/tests/Makefile b/examples/mixed_language/tests/Makefile index fd7be0c5..e5747da3 100644 --- a/examples/mixed_language/tests/Makefile +++ b/examples/mixed_language/tests/Makefile @@ -21,9 +21,7 @@ VHDL_SOURCES = $(WPWD)/../../endian_swapper/hdl/endian_swapper.vhdl ifeq ($(TOPLEVEL_LANG),sv) VERILOG_SOURCES += $(WPWD)/../hdl/toplevel.sv GPI_IMPL=vpi -ifeq ($(SIM),aldec) GPI_EXTRA=vhpi -endif ifeq ($(SIM),modelsim) GPI_EXTRA=fli endif diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 5ce64b4a..43574bc2 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -40,9 +40,8 @@ endif ifeq ($(GPI_IMPL),vpi) GPI_ARGS = -loadvpi $(LIB_DIR)/libvpi - EXTRA_ARGS += -sv - HDL_SOURCES = $(VERILOG_SOURCES) - GPI_LIB = $(COCOTB_VPI_LIB) + EXTRA_ARGS += -sv -v93 + HDL_SOURCES = $(VERILOG_SOURCES) $(VHDL_SOURCES) endif ifeq ($(GPI_IMPL),vhpi) @@ -51,9 +50,10 @@ ifeq ($(GPI_IMPL),vhpi) EXTRA_ARGS += -v93 EXTRA_ARGS += -top worklib.$(TOPLEVEL) HDL_SOURCES = $(VHDL_SOURCES) - GPI_LIB = $(COCOTB_VHPI_LIB) endif +GPI_LIB = $(COCOTB_VPI_LIB) $(COCOTB_VHPI_LIB) + ifndef GPI_ARGS $(error "Unable to determine appropriate GPI layer to use") endif From 429a4ba28fa9aee52499286a480030e7a7b458d5 Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Tue, 26 May 2015 14:53:26 +1000 Subject: [PATCH 0851/2656] vhpi : populate for generate objects this doesn't really work due to #248 but at least we can populate vhpiForGenerateK objects with __getattr__ --- lib/vhpi/VhpiImpl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index d4cf9b56..5eb89e5c 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -126,6 +126,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string case vhpiIndexedNameK: new_obj = new VhpiSignalObjHdl(this, new_hdl); break; + case vhpiForGenerateK: case vhpiCompInstStmtK: new_obj = new GpiObjHdl(this, new_hdl); break; From b5b9d941231efddbc9fa232a54ba8f2e56744a64 Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Tue, 26 May 2015 14:03:58 +1000 Subject: [PATCH 0852/2656] vhpi : seems we can just drop vhpiIfGenerateK into this case and things just work Tried this with a VHDL if generate statement with ius, and it seem to do the correct thing. --- lib/vhpi/VhpiImpl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 5eb89e5c..9f29dd7f 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -127,6 +127,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string new_obj = new VhpiSignalObjHdl(this, new_hdl); break; case vhpiForGenerateK: + case vhpiIfGenerateK: case vhpiCompInstStmtK: new_obj = new GpiObjHdl(this, new_hdl); break; From 7608da28665a90250271b0fc886e43ac85697d5e Mon Sep 17 00:00:00 2001 From: chiggs Date: Sat, 6 Jun 2015 12:52:18 +0100 Subject: [PATCH 0853/2656] Issue #17: Further work on iterators (unknown state) --- cocotb/handle.py | 17 +- .../functionality/tests/test_discovery.py | 7 + lib/gpi/GpiCbHdl.cpp | 7 + lib/gpi/gpi_priv.h | 3 + lib/gpi_impl/gpi_fli.c | 465 ------------------ lib/vhpi/VhpiImpl.h | 2 + lib/vpi/VpiImpl.cpp | 2 +- 7 files changed, 35 insertions(+), 468 deletions(-) delete mode 100644 lib/gpi_impl/gpi_fli.c diff --git a/cocotb/handle.py b/cocotb/handle.py index 13e13c11..88581ed8 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -84,7 +84,8 @@ def __getattr__(self, name): return self._sub_handles[name] new_handle = simulator.get_handle_by_name(self._handle, name) if not new_handle: - self._raise_testerror("%s contains no object named %s" % (self.name, name)) + #self._raise_testerror("%s contains no object named %s" % (self.name, name)) + raise AttributeError("%s contains no object named %s" % (self.name, name)) self._sub_handles[name] = SimHandle(new_handle) return self._sub_handles[name] @@ -236,10 +237,22 @@ def __iter__(self): except StopIteration: break hdl = SimHandle(thing) - self._sub_handles[hdl.name] = hdl + self._sub_handles[hdl.name.split(".")[-1]] = hdl yield hdl def __int__(self): return int(self.value) + def _discover_all(self): + for thing in self: + pass + def __dir__(self): + return self._sub_handles.keys() + + def _getAttributeNames(self): + self._discover_all() + return dir(self) + + def __repr__(self): + return repr(int(self)) diff --git a/examples/functionality/tests/test_discovery.py b/examples/functionality/tests/test_discovery.py index bc232505..f3e37571 100644 --- a/examples/functionality/tests/test_discovery.py +++ b/examples/functionality/tests/test_discovery.py @@ -40,6 +40,13 @@ def discover_module_values(dut): if count < 2: raise TestFailure("Expected to discover things in the DUT") +@cocotb.test(skip=True) +def ipython_embed(dut): + yield Timer(0) + import IPython + IPython.embed() + + @cocotb.test(expect_error=True) def discover_value_not_in_dut(dut): """Try and get a value from the DUT that is not there""" diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index 715f5b93..0d8177e2 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -38,6 +38,11 @@ const char * GpiObjHdl::get_name_str(void) return m_name.c_str(); } +const char * GpiObjHdl::get_fullname_str(void) +{ + return m_fullname.c_str(); +} + const char * GpiObjHdl::get_type_str(void) { return m_type.c_str(); @@ -88,7 +93,9 @@ int GpiHdl::initialise(std::string &name) int GpiObjHdl::initialise(std::string &name) { + printf("Name is %s\n", name.c_str()); m_name = name; + m_fullname = "bleh2"; m_type = "unknown"; return 0; } diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 5d2c59d1..2382c0d0 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -94,12 +94,14 @@ class GpiObjHdl : public GpiHdl { public: GpiObjHdl(std::string name) : GpiHdl(NULL, NULL), m_name(name), + m_fullname("bleh"), m_type("unknown") { } GpiObjHdl(GpiImplInterface *impl) : GpiHdl(impl, NULL) { } GpiObjHdl(GpiImplInterface *impl, void *hdl) : GpiHdl(impl, hdl) { } virtual ~GpiObjHdl() { } virtual const char* get_name_str(void); + virtual const char* get_fullname_str(void); virtual const char* get_type_str(void); const std::string & get_name(void); @@ -108,6 +110,7 @@ class GpiObjHdl : public GpiHdl { protected: std::string m_name; + std::string m_fullname; std::string m_type; }; diff --git a/lib/gpi_impl/gpi_fli.c b/lib/gpi_impl/gpi_fli.c deleted file mode 100644 index 94f2339e..00000000 --- a/lib/gpi_impl/gpi_fli.c +++ /dev/null @@ -1,465 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2013 Potential Ventures Ltd -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#include "gpi_priv.h" -#include - -#define FLII_CHECKING 1 - -static gpi_sim_hdl sim_init_cb; -static gpi_sim_hdl sim_finish_cb; - - -// Handle related functions -/** - * @name Find the root handle - * @brief Find the root handle using a optional name - * - * Get a handle to the root simulator object. This is usually the toplevel. - * - * If no name is defined, we return the first root instance. - * - * If name is provided, we check the name against the available objects until - * we find a match. If no match is found we return NULL - */ -static gpi_sim_hdl fli_get_root_handle(const char* name) -{ - FENTER - mtiRegionIdT root; - gpi_sim_hdl rv; - - for (root = mti_GetTopRegion(); root != NULL; root = mti_NextRegion(root)) { - if (name == NULL || !strcmp(name, mti_GetRegionName(root))) - break; - } - - if (!root) { - goto error; - } - - rv = gpi_create_handle(); - rv->sim_hdl = (void *)root; - - FEXIT - return rv; - - error: - - LOG_CRITICAL("FPI: Couldn't find root handle %s", name); - - for (root = mti_GetTopRegion(); root != NULL; root = mti_NextRegion(root)) { - - LOG_CRITICAL("FLI: Toplevel instances: %s != %s...", name, mti_GetRegionName(root)); - - if (name == NULL) - break; - } - - FEXIT - return NULL; -} - - -/** - * @brief Get a handle to an object under the scope of parent - * - * @param name of the object to find - * @param parent handle to parent object defining the scope to search - * - * @return gpi_sim_hdl for the new object or NULL if object not found - */ -static gpi_sim_hdl fli_get_handle_by_name(const char *name, gpi_sim_hdl parent) -{ - FENTER - mtiRegionIdT hdl = (mtiRegionIdT)parent->sim_hdl; - gpi_sim_hdl rv; - void *result; - mtiRegionIdT result_reg; - mtiSignalIdT result_sig; - mtiVariableIdT result_var; - size_t baselen = strlen(mti_GetRegionFullName(hdl)); - char *fullname; - char *ptr; - - fullname = (char *)malloc(baselen + strlen(name) + 2); - - if (fullname == NULL) { - LOG_CRITICAL("FLI: Attempting allocate string buffer failed!"); - return NULL; - } - - strncpy(fullname, mti_GetRegionFullName(hdl), baselen); - ptr = fullname + baselen; - *ptr++ = '/'; - strncpy(ptr, name, strlen(name)); - - result_reg = mti_FindRegion(fullname); - result = (void *)result_reg; - if (result_reg) goto success; -// result = mti_FindPort(fullname); -// if (result) goto success; - result_sig = mti_FindSignal(fullname); - result = (void *)result_sig; - if (result_sig) goto success; - result_var = mti_FindVar(fullname); - result = (void *)result_var; - if (result) goto success; - -error: - LOG_DEBUG("FLII: Handle '%s' not found!", name); - - // NB we deliberately don't dump an error message here because it's - // a valid use case to attempt to grab a signal by name - for example - // optional signals on a bus. - free(fullname); - return NULL; - -success: - free(fullname); - rv = gpi_create_handle(); - rv->sim_hdl = result; - - FEXIT - return rv; -} - - -/** - * @brief Get a handle for an object based on its index within a parent - * - * @param parent handle to the parent - * @param indext Index to retrieve - * - * Can be used on bit-vectors to access a specific bit or - * memories to access an address - */ -static gpi_sim_hdl fli_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) -{ - FENTER - LOG_ERROR("FLI: Obtaining a handle by index not supported by FLI?"); - FEXIT - return NULL; -} - - -// Functions for iterating over entries of a handle -// Returns an iterator handle which can then be used in gpi_next calls -// NB May return NULL if no objects of the request type exist -static gpi_iterator_hdl fli_iterate_hdl(uint32_t type, gpi_sim_hdl base) { - FENTER - LOG_ERROR("FLI: Iterating over a handle not implemented yet"); - FEXIT - return NULL; -} - -// Returns NULL when there are no more objects -static gpi_sim_hdl fli_next_hdl(gpi_iterator_hdl iterator) -{ - FENTER - LOG_ERROR("FLI: Iterating over a handle not implemented yet"); - FEXIT - return NULL; -} - -static void fli_get_sim_time(uint32_t *high, uint32_t *low) -{ - *high = mti_NowUpper(); - *low = mti_Now(); -} - -// Value related functions -static void fli_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) -{ - FENTER - LOG_ERROR("Attempt to force signal %s failed (not implemented)", - mti_GetSignalName((mtiSignalIdT)gpi_hdl->sim_hdl)); - FEXIT -} - -static void fli_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) -{ - FENTER - char * str_copy = strdup(str); // Mentor discard const qualifier - int rc = mti_ForceSignal((mtiSignalIdT)(gpi_hdl->sim_hdl), - str_copy, - -1, // If the delay parameter is negative, - // then the force is applied immediately. - MTI_FORCE_DEPOSIT, - -1, // cancel_period - -1); // repeat_period - - if (!rc) - LOG_ERROR("Attempt to force signal %s failed", - mti_GetSignalName((mtiSignalIdT)gpi_hdl->sim_hdl)); - free(str_copy); - FEXIT -} - -static char *fli_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) -{ - FENTER - mtiInt32T value; - switch (mti_GetTypeKind(mti_GetSignalType((mtiSignalIdT)gpi_hdl->sim_hdl))){ - case MTI_TYPE_SCALAR: - case MTI_TYPE_ENUM: - case MTI_TYPE_PHYSICAL: - value = mti_GetSignalValue((mtiSignalIdT)gpi_hdl->sim_hdl); - break; - default: - mti_PrintFormatted( "(Type not supported)\n" ); - break; - } - char *result;// = gpi_copy_name(value_p->value.str); - FEXIT - return result; -} - -static char *fli_get_signal_name_str(gpi_sim_hdl gpi_hdl) -{ - FENTER - const char *name = mti_GetSignalName((mtiSignalIdT)gpi_hdl->sim_hdl); - char *result = gpi_copy_name(name); - FEXIT - return result; -} - -static char *fli_get_signal_type_str(gpi_sim_hdl gpi_hdl) -{ - switch (mti_GetTypeKind(mti_GetSignalType((mtiSignalIdT)gpi_hdl->sim_hdl))){ - case MTI_TYPE_SCALAR : return strdup("Scalar"); - case MTI_TYPE_ARRAY : return strdup("Array"); - case MTI_TYPE_RECORD : return strdup("Record"); - case MTI_TYPE_ENUM : return strdup("Enum"); - case MTI_TYPE_INTEGER : return strdup("Integer"); - case MTI_TYPE_PHYSICAL : return strdup("Physical"); - case MTI_TYPE_REAL : return strdup("Real"); - case MTI_TYPE_ACCESS : return strdup("Access"); - case MTI_TYPE_FILE : return strdup("File"); - case MTI_TYPE_TIME : return strdup("Time"); - case MTI_TYPE_C_REAL : return strdup("C Real"); - case MTI_TYPE_C_ENUM : return strdup("C Enum"); - default : return strdup("Unknown!"); - } -} - - -// Callback related functions -static int32_t handle_fli_callback(gpi_cb_hdl cb_data) -{ - FENTER - LOG_CRITICAL("FLI: Callbacks not implemented yet"); - FEXIT - return 0; -}; - - -static int fli_deregister_callback(gpi_sim_hdl gpi_hdl) -{ - FENTER - LOG_CRITICAL("FLI: Callbacks not implemented yet"); - FEXIT - return 0; -} - - -/* These functions request a callback to be active with the current - * handle and associated data. A callback handle needs to have been - * allocated with gpi_create_cb_handle first - */ -static int fli_register_value_change_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data, - gpi_sim_hdl gpi_hdl) -{ - FENTER - FEXIT - return 0; -} - -static int fli_register_readonly_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) -{ - FENTER - FEXIT - return 0; -} - -static int fli_register_readwrite_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) -{ - FENTER - FEXIT - return 0; -} - - - -static int fli_register_nexttime_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) -{ - FENTER - FEXIT - return 0; -} - -static int fli_register_timed_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data, - uint64_t time_ps) -{ - FENTER - FEXIT - return 0; -} - -static void fli_sim_end(void) -{ - sim_finish_cb = NULL; - mti_Quit(); -} - - -/* Checking of validity is done in the common code */ -static gpi_cb_hdl fli_create_cb_handle(void) -{ - gpi_cb_hdl ret = NULL; - - FENTER - - void * new_cb_hdl = calloc(1, sizeof(*new_cb_hdl)); - if (new_cb_hdl) - ret = &new_cb_hdl->gpi_cb_data; - - FEXIT - return ret; -} - - - -static s_gpi_impl_tbl fli_table = { - .sim_end = fli_sim_end, - .iterate_handle = fli_iterate_hdl, - .next_handle = fli_next_hdl, - .create_cb_handle = fli_create_cb_handle, - .destroy_cb_handle = fli_destroy_cb_handle, - .deregister_callback = fli_deregister_callback, - .get_root_handle = fli_get_root_handle, - .get_sim_time = fli_get_sim_time, - .get_handle_by_name = fli_get_handle_by_name, - .get_handle_by_index = fli_get_handle_by_index, - .get_signal_name_str = fli_get_signal_name_str, - .get_signal_type_str = fli_get_signal_type_str, - .get_signal_value_binstr = fli_get_signal_value_binstr, - .set_signal_value_int = fli_set_signal_value_int, - .set_signal_value_str = fli_set_signal_value_str, - .register_timed_callback = fli_register_timed_callback, - .register_readwrite_callback = fli_register_readwrite_callback, - .register_nexttime_callback = fli_register_nexttime_callback, - .register_value_change_callback = fli_register_value_change_callback, - .register_readonly_callback = fli_register_readonly_callback, - .get_callback_data = fli_get_callback_data, -}; - -static void register_embed(void) -{ - gpi_register_impl(&fli_table, 0xfe70); - gpi_embed_init_python(); -} - - -// No access to plusargs via FLI? -static int handle_sim_init(void *gpi_cb_data) -{ - FENTER - gpi_sim_info_t sim_info; - sim_info.argc = NULL; - sim_info.argv = NULL; - sim_info.product = mti_GetProductVersion(); - sim_info.version = NULL; - gpi_embed_init(&sim_info); - FEXIT -} - -static void register_initial_callback(void) -{ - FENTER - gpi_cb_hdl gpi_user_data; - sim_init_cb = gpi_create_cb_handle(); - gpi_user_data = gpi_container_of(sim_init_cb, gpi_cb_hdl_t, hdl); - - gpi_user_data->gpi_cb_data = NULL; - gpi_user_data->gpi_function = handle_sim_init; - - mti_AddLoadDoneCB(handle_fli_callback, &gpi_user_data); - FEXIT -} - -static int handle_sim_end(void *gpi_cb_data) -{ - FENTER - if (sim_finish_cb) { - sim_finish_cb = NULL; - /* This means that we have been asked to close */ - gpi_embed_end(); - } /* Other sise we have already been here from the top down so do not need - to inform the upper layers that anything has occoured */ - gpi_free_cb_handle(sim_init_cb); - FEXIT -} - -static void register_final_callback(void) -{ - FENTER - gpi_cb_hdl gpi_user_data; - sim_init_cb = gpi_create_cb_handle(); - gpi_user_data = gpi_container_of(sim_init_cb, gpi_cb_hdl_t, hdl); - - gpi_user_data->gpi_cb_data = NULL; - gpi_user_data->gpi_function = handle_sim_end; - - mti_AddQuitCB(handle_fli_callback, &gpi_user_data); - FEXIT -} - - -// Initialisation needs to be triggered from a foreign architecture in the RTL -// -// ATTRIBUTE foreign OF blah : ARCHITECTURE IS "cocotb_init libgpi.so; parameter"; -void cocotb_init(mtiRegionIdT region, - char *param, - mtiInterfaceListT *generics, - mtiInterfaceListT *ports) -{ - register_embed(); - register_initial_callback(); - register_final_callback(); -} - - diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 41e5aa9f..dc4b4224 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -188,6 +188,8 @@ class VhpiImpl : public GpiImplInterface { /* Hierachy related */ GpiObjHdl *get_root_handle(const char *name); + GpiIterator *iterate_handle(uint32_t type, GpiObjHdl *obj_hdl); + GpiObjHdl *next_handle(GpiIterator *iter); /* Callback related, these may (will) return the same handle*/ GpiCbHdl *register_timed_callback(uint64_t time_ps); diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index a52249f2..ed1d70f2 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -305,7 +305,7 @@ int32_t handle_vpi_callback(p_cb_data cb_data) cb_hdl->run_callback(); gpi_cb_state_e new_state = cb_hdl->get_call_state(); - + /* We have re-primed in the handler */ if (new_state != GPI_PRIMED) if (cb_hdl->cleanup_callback()) From 62448a593a54693ea0bc318ba68aa377b0d2df2b Mon Sep 17 00:00:00 2001 From: Sigve Tjora Date: Sun, 7 Jun 2015 20:30:08 +0200 Subject: [PATCH 0854/2656] Added *args and **kwargs method to the logging functions This is moddeled after the Python logging framework, to be able to postpone the string substitution to after it is decided that the message is going to be logged. Only extra-keyword is suppported for now. --- cocotb/log.py | 34 ++++++++++----------- examples/functionality/tests/test_cocotb.py | 25 ++++++++++++++- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/cocotb/log.py b/cocotb/log.py index 37eabaf7..c46ce6fc 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -85,8 +85,7 @@ def __init__(self, name, ident=None): else: self._log_name = name - def _makeRecord(self, msg, level): - + def _makeRecord(self, level, msg, args, extra=None): if self.logger.isEnabledFor(level): frame = inspect.stack()[2] info = inspect.getframeinfo(frame[0]) @@ -95,9 +94,10 @@ def _makeRecord(self, msg, level): info.filename, info.lineno, msg, + args, None, - None, - info.function) + info.function, + extra) self.logger.handle(record) def _willLog(self, level): @@ -123,23 +123,23 @@ def _printRecord(self, level, filename, lineno, msg, function): function) self.logger.handle(record) - def warn(self, msg): - self._makeRecord(msg, logging.WARNING) + def warn(self, msg, *args, **kwargs): + self._makeRecord(logging.WARNING, msg, args, **kwargs) - def warning(self, msg): - self._makeRecord(msg, logging.WARNING) + def warning(self, msg, *args, **kwargs): + self._makeRecord(logging.WARNING, msg, args, **kwargs) - def debug(self, msg): - self._makeRecord(msg, logging.DEBUG) + def debug(self, msg, *args, **kwargs): + self._makeRecord(logging.DEBUG, msg, args, **kwargs) - def error(self, msg): - self._makeRecord(msg, logging.ERROR) + def error(self, msg, *args, **kwargs): + self._makeRecord(logging.ERROR, msg, args, **kwargs) - def critical(self, msg): - self._makeRecord(msg, logging.CRITICAL) + def critical(self, msg, *args, **kwargs): + self._makeRecord(logging.CRITICAL, msg, args, **kwargs) - def info(self, msg): - self._makeRecord(msg, logging.INFO) + def info(self, msg, *args, **kwargs): + self._makeRecord(logging.INFO, msg, args, **kwargs) def __getattr__(self, attribute): """Forward any other attribute accesses on to our logger object""" @@ -164,7 +164,7 @@ def rjust(string, chars): return string.rjust(chars) def _format(self, timeh, timel, level, record, msg): - simtime = "% 6d.%02dns" % ((timel / 1000), (timel % 1000) / 10) + simtime = "% 6d.%03dns" % ((timel / 1000), (timel % 1000)) prefix = simtime + ' ' + level + ' ' + \ self.ljust(record.name, _RECORD_CHARS) + \ self.rjust(os.path.split(record.filename)[1], _FILENAME_CHARS) + \ diff --git a/examples/functionality/tests/test_cocotb.py b/examples/functionality/tests/test_cocotb.py index 1a353934..6a0b6233 100644 --- a/examples/functionality/tests/test_cocotb.py +++ b/examples/functionality/tests/test_cocotb.py @@ -26,6 +26,7 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' +import logging """ A set of tests that demonstrate cocotb functionality @@ -37,7 +38,7 @@ from cocotb.triggers import (Timer, Join, RisingEdge, FallingEdge, Edge, ReadOnly, ReadWrite) from cocotb.clock import Clock -from cocotb.result import ReturnValue, TestFailure, TestError +from cocotb.result import ReturnValue, TestFailure, TestError, TestSuccess # Tests relating to providing meaningful errors if we forget to use the @@ -446,3 +447,25 @@ def test_edge_count(dut): if edge_count is not edges_seen: raise TestFailure("Correct edge count failed saw %d wanted %d" % (edges_seen, edge_count)) + +class StrCallCounter(object): + def __init__(self): + self.str_counter = 0 + + def __str__(self): + self.str_counter += 1 + return "__str__ called %d time(s)" % self.str_counter + +@cocotb.test() +def test_logging_with_args(dut): + counter = StrCallCounter() + dut.log.logger.setLevel(logging.INFO) #To avoid logging debug message, to make next line run without error + dut.log.debug("%s", counter) + assert counter.str_counter == 0 + + dut.log.info("%s", counter) + assert counter.str_counter == 1 + + dut.log.info("No substitution") + + yield Timer(100) #Make it do something with time From 936b6cc943abe676c062476f598c6be052513242 Mon Sep 17 00:00:00 2001 From: Sigve Date: Sun, 7 Jun 2015 20:58:32 +0200 Subject: [PATCH 0855/2656] Reverted not related commit. --- cocotb/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/log.py b/cocotb/log.py index c46ce6fc..4900c489 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -164,7 +164,7 @@ def rjust(string, chars): return string.rjust(chars) def _format(self, timeh, timel, level, record, msg): - simtime = "% 6d.%03dns" % ((timel / 1000), (timel % 1000)) + simtime = "% 6d.%02dns" % ((timel / 1000), (timel % 1000) / 10) prefix = simtime + ' ' + level + ' ' + \ self.ljust(record.name, _RECORD_CHARS) + \ self.rjust(os.path.split(record.filename)[1], _FILENAME_CHARS) + \ From a60020543385fc5445c0c33abe3d7ab775701fb8 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 22 Jun 2015 12:02:22 +0100 Subject: [PATCH 0856/2656] Issue #134: Support for VPI access to enum variables --- include/vpi_user.h | 3 ++- lib/vpi/VpiImpl.cpp | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/vpi_user.h b/include/vpi_user.h index 4a250a4d..b7fdd56d 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -63,7 +63,6 @@ typedef uint32_t *vpiHandle; #define vpiParameter 41 /* module parameter */ #define vpiNetArray 114 #define vpiRegArray 116 /* multidimensional reg */ -#define vpiStructVar 618 #define vpiInterface 601 #define vpiInterfaceArray 603 #define vpiModport 606 @@ -71,6 +70,8 @@ typedef uint32_t *vpiHandle; #define vpiPackedArrayVar 623 #define vpiEnumNet 680 /* SystemVerilog */ #define vpiIntVar 612 +#define vpiEnumVar 617 +#define vpiStructVar 618 #define vpiStop 66 /* execute simulator's $stop */ #define vpiFinish 67 /* execute simulator's $finish */ #define vpiReset 68 /* execute simulator's $reset */ diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 9d35a5a7..af28fe4c 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -89,6 +89,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &n case vpiRegArray: case vpiNetArray: case vpiEnumNet: + case vpiEnumVar: case vpiIntVar: new_obj = new VpiSignalObjHdl(this, new_hdl); break; From 9c71dee5c8632f2d10d26faa336e683f285d7e5b Mon Sep 17 00:00:00 2001 From: Chiggs Date: Thu, 16 Jul 2015 15:11:58 +0100 Subject: [PATCH 0857/2656] Fixes #251: Harmonise Makefiles between Modelsim and Questa * Only difference is that Modelsim is 32-bit only --- makefiles/simulators/Makefile.modelsim | 84 +----------------------- makefiles/simulators/Makefile.questa | 90 +++++++++++++++++++++++--- 2 files changed, 84 insertions(+), 90 deletions(-) diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index 8ee3eb40..1425264d 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -30,86 +30,6 @@ # Modelsim is 32-bit only ARCH:=i686 -ifdef VERILOG_INCLUDE_DIRS -VLOG_ARGS += +incdir+$(VERILOG_INCLUDE_DIRS) -endif - -ifeq ($(GUI),1) -SIM_CMD = vsim -gui -VSIM_ARGS += -onfinish stop -else -SIM_CMD = vsim -c -VSIM_ARGS += -onfinish exit -endif - -ifeq ($(GPI_IMPL),vhpi) -VSIM_ARGS += -foreign \"cocotb_init libfli.so\" -else -VSIM_ARGS += -pli libvpi.$(LIB_EXT) -endif - -ifneq ($(GENERICS),) -VSIM_ARGS += $(foreach gen, $(GENERICS),"-G $(gen)") -endif - -$(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) | $(SIM_BUILD) - echo "if [file exists work] {vdel -all}" > $@ - echo "vlib work" > $@ -ifneq ($(VHDL_SOURCES),) - echo "vcom $(VCOM_ARGS) $(VHDL_SOURCES)" >> $@ -endif -ifneq ($(VERILOG_SOURCES),) - echo "vlog -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(VERILOG_SOURCES)" >> $@ -endif - echo "vsim $(VSIM_ARGS) $(TOPLEVEL)" >> $@ -ifeq ($(WAVES),1) - echo "log -recursive /*" >> $@ -endif -ifeq ($(GUI),1) - echo "add wave -recursive /*" >> $@ -else - echo "run -all" >> $@ - echo "quit" >> $@ -endif - -# auto-detect modelsim dir from system path -export MODELSIM_BIN_DIR = $(shell dirname $(shell which vsim)) - -# make sure modelsim dir was found -ifeq ($(MODELSIM_BIN_DIR),) -$(error "Directory containing ModelSim binaries must be included in system path") -endif - -ifeq ($(OS),Msys) - -# Windows allows the situation where the libstc++ used at link time as -# specified by -L can be different to the one that is used at runtime which -# comes from the first libstdc++ that it finds in the path. As such -# we use the mingw lib used at build time and put this at the start of the path -# before running - -MINGW_BIN_DIR = $(shell dirname $(shell which gcc)) - -EXTRA_LIBS := -lmtipli -EXTRA_LIBDIRS := -L$(MODELSIM_BIN_DIR) -OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') - -LIB_LOAD := PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) -NEW_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-zA-Z]\):\//\/\1\//g' -e 's/;/:/g') -INT_LIBS := $(COCOTB_VPI_LIB) -else -LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) -NEW_PYTHONPATH := $(PYTHONPATH) -INT_LIBS := $(COCOTB_VPI_LIB) $(COCOTB_FLI_LIB) -endif - -# Depending on the version of modelsim the interfaces that it supports can change -# Append or remove values to INT_LIBS depenending on your license -results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) - cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) \ - $(SIM_CMD) -do runsim.do 2>&1 | tee sim.log - -clean:: - -rm -rf $(SIM_BUILD) +# Everything else is identical to Quest +include Makefile.questa diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 5f4c8462..8ee3eb40 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -27,15 +27,89 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -VPI_FILE := $(LIB_DIR)/libvpi.so +# Modelsim is 32-bit only +ARCH:=i686 -results.xml: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(PYTHON_LIBDIR):$(PYTHON_DYNLIBDIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - qverilog -64 -R -pli $(VPI_FILE) $(VSIM_ARGS) - +acc $(EXTRA_ARGS) $(VERILOG_SOURCES) +ifdef VERILOG_INCLUDE_DIRS +VLOG_ARGS += +incdir+$(VERILOG_INCLUDE_DIRS) +endif -clean:: - -@rm -rf $(SIM_BUILD) - -@rm -rf work - -@rm -rf qverilog.log +ifeq ($(GUI),1) +SIM_CMD = vsim -gui +VSIM_ARGS += -onfinish stop +else +SIM_CMD = vsim -c +VSIM_ARGS += -onfinish exit +endif + +ifeq ($(GPI_IMPL),vhpi) +VSIM_ARGS += -foreign \"cocotb_init libfli.so\" +else +VSIM_ARGS += -pli libvpi.$(LIB_EXT) +endif + +ifneq ($(GENERICS),) +VSIM_ARGS += $(foreach gen, $(GENERICS),"-G $(gen)") +endif + +$(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) | $(SIM_BUILD) + echo "if [file exists work] {vdel -all}" > $@ + echo "vlib work" > $@ +ifneq ($(VHDL_SOURCES),) + echo "vcom $(VCOM_ARGS) $(VHDL_SOURCES)" >> $@ +endif +ifneq ($(VERILOG_SOURCES),) + echo "vlog -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(VERILOG_SOURCES)" >> $@ +endif + echo "vsim $(VSIM_ARGS) $(TOPLEVEL)" >> $@ +ifeq ($(WAVES),1) + echo "log -recursive /*" >> $@ +endif +ifeq ($(GUI),1) + echo "add wave -recursive /*" >> $@ +else + echo "run -all" >> $@ + echo "quit" >> $@ +endif + +# auto-detect modelsim dir from system path +export MODELSIM_BIN_DIR = $(shell dirname $(shell which vsim)) + +# make sure modelsim dir was found +ifeq ($(MODELSIM_BIN_DIR),) +$(error "Directory containing ModelSim binaries must be included in system path") +endif + +ifeq ($(OS),Msys) +# Windows allows the situation where the libstc++ used at link time as +# specified by -L can be different to the one that is used at runtime which +# comes from the first libstdc++ that it finds in the path. As such +# we use the mingw lib used at build time and put this at the start of the path +# before running + +MINGW_BIN_DIR = $(shell dirname $(shell which gcc)) + +EXTRA_LIBS := -lmtipli +EXTRA_LIBDIRS := -L$(MODELSIM_BIN_DIR) +OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') + +LIB_LOAD := PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) +NEW_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-zA-Z]\):\//\/\1\//g' -e 's/;/:/g') +INT_LIBS := $(COCOTB_VPI_LIB) +else +LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) +NEW_PYTHONPATH := $(PYTHONPATH) +INT_LIBS := $(COCOTB_VPI_LIB) $(COCOTB_FLI_LIB) +endif + +# Depending on the version of modelsim the interfaces that it supports can change +# Append or remove values to INT_LIBS depenending on your license +results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) + cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) \ + $(SIM_CMD) -do runsim.do 2>&1 | tee sim.log + +clean:: + -rm -rf $(SIM_BUILD) From 832a4ae8c3e1be2ba4faceb5897bb30d2cae6867 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Thu, 16 Jul 2015 15:42:10 +0100 Subject: [PATCH 0858/2656] Issue #251: Correct silly error - Questa isn't 32-bit only --- makefiles/simulators/Makefile.questa | 3 --- 1 file changed, 3 deletions(-) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 8ee3eb40..daef8bff 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -27,9 +27,6 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -# Modelsim is 32-bit only -ARCH:=i686 - ifdef VERILOG_INCLUDE_DIRS VLOG_ARGS += +incdir+$(VERILOG_INCLUDE_DIRS) endif From 37da618edb11056d17279341e6e2f904e9b1dc90 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Sun, 19 Jul 2015 20:46:12 -0500 Subject: [PATCH 0859/2656] Addresses multiple issues I raised with Potential Ventures repositories. --- lib/embed/gpi_embed.c | 22 ++-- lib/fli/FliImpl.cpp | 199 +++++++++++++++++++++++++++----- lib/fli/FliImpl.h | 34 +++++- lib/gpi/GpiCommon.cpp | 11 +- lib/simulator/simulatormodule.c | 4 +- lib/vhpi/VhpiImpl.cpp | 18 +-- lib/vpi/VpiImpl.cpp | 18 +-- 7 files changed, 238 insertions(+), 68 deletions(-) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index ab326252..7b65338b 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -90,7 +90,7 @@ void embed_init_python(void) such that they can attach */ const char *pause = getenv("COCOTB_ATTACH"); if (pause) { - int sleep_time = atoi(pause); + int sleep_time = atoi(pause); fprintf(stderr, "Waiting for %d seconds - Attach to %d\n", sleep_time, getpid()); sleep(sleep_time); } @@ -143,15 +143,12 @@ int embed_sim_init(gpi_sim_info_t *info) // Find the simulation root const char *dut = getenv("TOPLEVEL"); - if (dut == NULL) { - fprintf(stderr, "Unable to find root instance!\n"); - return -1; - } - - // Skip any library component of the toplevel - char *dot = strchr(dut, '.'); - if (dot != NULL) { - dut += (dot - dut + 1); + if (dut != NULL) { + // Skip any library component of the toplevel + char *dot = strchr(dut, '.'); + if (dot != NULL) { + dut += (dot - dut + 1); + } } @@ -270,7 +267,10 @@ int embed_sim_init(gpi_sim_info_t *info) } cocotb_args = PyTuple_New(1); - PyTuple_SetItem(cocotb_args, 0, PyString_FromString(dut)); // Note: This function “steals†a reference to o. + if (dut == NULL) + PyTuple_SetItem(cocotb_args, 0, Py_BuildValue("")); // Note: This function “steals†a reference to o. + else + PyTuple_SetItem(cocotb_args, 0, PyString_FromString(dut)); // Note: This function “steals†a reference to o. cocotb_retval = PyObject_CallObject(cocotb_init, cocotb_args); if (cocotb_retval != NULL) { diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 7731f784..1b3fe8c2 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -25,6 +25,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ +#include +#include #include #include "FliImpl.h" @@ -79,7 +81,7 @@ void handle_fli_callback(void *data) gpi_cb_state_e old_state = cb_hdl->get_call_state(); - if (old_state == GPI_PRIMED) { + if (old_state == GPI_PRIMED) { cb_hdl->set_call_state(GPI_CALL); @@ -100,7 +102,10 @@ void handle_fli_callback(void *data) void FliImpl::sim_end(void) { - mti_Quit(); + char buffer[5]; + + snprintf(buffer, 5, "stop"); + mti_Cmd(buffer); } /** @@ -110,18 +115,26 @@ void FliImpl::sim_end(void) */ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) { - LOG_DEBUG("Looking for child %s from %s", name.c_str(), parent->get_name_str()); + GpiObjHdl *new_obj = NULL; + std::string fq_name = parent->get_name() + "/" + name; + std::vector writable(fq_name.begin(), fq_name.end()); + writable.push_back('\0'); + mtiRegionIdT rgn_hdl; + mtiSignalIdT sig_hdl; + mtiVariableIdT var_hdl; - GpiObjHdl *new_obj = NULL; - std::vector writable(name.begin(), name.end()); - writable.push_back('\0'); + LOG_DEBUG("Looking for child %s from %s", name.c_str(), parent->get_name_str()); - mtiSignalIdT sig_hdl; - sig_hdl = mti_FindSignal(&writable[0]); - if (sig_hdl) { + if ((rgn_hdl = mti_FindRegion(&writable[0])) != NULL) { + LOG_DEBUG("Found a region %s -> %p", &writable[0], rgn_hdl); + new_obj = new FliRegionObjHdl(this, rgn_hdl); + } else if ((sig_hdl = mti_FindSignal(&writable[0])) != NULL) { LOG_DEBUG("Found a signal %s -> %p", &writable[0], sig_hdl); new_obj = new FliSignalObjHdl(this, sig_hdl); + } else if ((var_hdl = mti_FindVar(&writable[0])) != NULL) { + LOG_DEBUG("Found a variable %s -> %p", &writable[0], var_hdl); + new_obj = new FliVariableObjHdl(this, var_hdl); } if (NULL == new_obj) { @@ -129,7 +142,7 @@ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) return NULL; } - new_obj->initialise(name); + new_obj->initialise(fq_name); return new_obj; } @@ -176,7 +189,8 @@ GpiObjHdl *FliImpl::get_root_handle(const char *name) { mtiRegionIdT root; GpiObjHdl *rv; - std::string root_name = name; + char *rgn_name; + std::string root_name; for (root = mti_GetTopRegion(); root != NULL; root = mti_NextRegion(root)) { LOG_DEBUG("Iterating over: %s", mti_GetRegionName(root)); @@ -188,7 +202,12 @@ GpiObjHdl *FliImpl::get_root_handle(const char *name) goto error; } - LOG_DEBUG("Found toplevel: %s, creating handle....", name); + rgn_name = mti_GetRegionFullName(root); + + root_name = rgn_name; + mti_VsimFree(rgn_name); + + LOG_DEBUG("Found toplevel: %s, creating handle....", root_name.c_str()); rv = new FliRegionObjHdl(this, root); rv->initialise(root_name); @@ -201,11 +220,10 @@ GpiObjHdl *FliImpl::get_root_handle(const char *name) LOG_CRITICAL("FLI: Couldn't find root handle %s", name); for (root = mti_GetTopRegion(); root != NULL; root = mti_NextRegion(root)) { - - LOG_CRITICAL("FLI: Toplevel instances: %s != %s...", name, mti_GetRegionName(root)); - if (name == NULL) break; + + LOG_CRITICAL("FLI: Toplevel instances: %s != %s...", name, mti_GetRegionName(root)); } return NULL; } @@ -259,9 +277,9 @@ int FliImpl::deregister_callback(GpiCbHdl *gpi_hdl) * @brief Called while unwinding after a GPI callback * * We keep the process but de-sensitise it - * + * * NB need a way to determine if should leave it sensitised, hmmm... - * + * */ int FliProcessCbHdl::cleanup_callback(void) { @@ -345,10 +363,10 @@ int FliSimPhaseCbHdl::arm_callback(void) } FliSignalCbHdl::FliSignalCbHdl(GpiImplInterface *impl, - FliSignalObjHdl *sig_hdl, - unsigned int edge) : GpiCbHdl(impl), - FliProcessCbHdl(impl), - GpiValueCbHdl(impl, sig_hdl, edge) + FliSignalObjHdl *sig_hdl, + unsigned int edge) : GpiCbHdl(impl), + FliProcessCbHdl(impl), + GpiValueCbHdl(impl, sig_hdl, edge) { m_sig_hdl = m_signal->get_handle(); } @@ -386,10 +404,15 @@ const char* FliSignalObjHdl::get_signal_value_binstr(void) switch (mti_GetTypeKind(mti_GetSignalType(m_fli_hdl))) { case MTI_TYPE_ENUM: - case MTI_TYPE_SCALAR: - case MTI_TYPE_PHYSICAL: m_val_buff[0] = value_enum[mti_GetSignalValue(m_fli_hdl)]; break; + case MTI_TYPE_SCALAR: + case MTI_TYPE_PHYSICAL: { + std::bitset<32> value((unsigned long)mti_GetSignalValue(m_fli_hdl)); + std::string bin_str = value.to_string(); + snprintf(m_val_buff, m_val_len+1, "%s", bin_str.c_str()); + } + break; case MTI_TYPE_ARRAY: { mti_GetArraySignalValue(m_fli_hdl, m_mti_buff); if (m_val_len <= 256) { @@ -405,12 +428,12 @@ const char* FliSignalObjHdl::get_signal_value_binstr(void) } break; default: - LOG_CRITICAL("Signal %s type %d not currently supported", + LOG_CRITICAL("Signal %s type %d not currently supported", m_name.c_str(), mti_GetTypeKind(mti_GetSignalType(m_fli_hdl))); break; } - LOG_DEBUG("Retrieved \"%s\" for signal %s", &m_val_buff, m_name.c_str()); + LOG_DEBUG("Retrieved \"%s\" for signal %s", m_val_buff, m_name.c_str()); return m_val_buff; } @@ -445,23 +468,135 @@ int FliSignalObjHdl::set_signal_value(std::string &value) int FliSignalObjHdl::initialise(std::string &name) { + mtiTypeKindT type; + /* Pre allocte buffers on signal type basis */ - m_type = mti_GetTypeKind(mti_GetSignalType(m_fli_hdl)); + type = mti_GetTypeKind(mti_GetSignalType(m_fli_hdl)); - switch (m_type) { + switch (type) { case MTI_TYPE_ENUM: + m_val_len = 1; + m_val_buff = (char*)malloc(m_val_len+1); + if (!m_val_buff) { + LOG_CRITICAL("Unable to alloc mem for signal read buffer"); + } + m_val_buff[m_val_len] = '\0'; + break; case MTI_TYPE_SCALAR: case MTI_TYPE_PHYSICAL: - m_val_len = 2; - m_val_buff = (char*)malloc(m_val_len); + m_val_len = 32; + m_val_buff = (char*)malloc(m_val_len+1); if (!m_val_buff) { LOG_CRITICAL("Unable to alloc mem for signal read buffer"); } - m_val_buff[1] = '\0'; + m_val_buff[m_val_len] = '\0'; break; case MTI_TYPE_ARRAY: m_val_len = mti_TickLength(mti_GetSignalType(m_fli_hdl)); - m_val_buff = (char*)malloc(m_val_len); + m_val_buff = (char*)malloc(m_val_len+1); + if (!m_val_buff) { + LOG_CRITICAL("Unable to alloc mem for signal read buffer"); + } + m_val_buff[m_val_len] = '\0'; + m_mti_buff = (mtiInt32T*)malloc(sizeof(*m_mti_buff) * m_val_len); + if (!m_mti_buff) { + LOG_CRITICAL("Unable to alloc mem for signal mti read buffer"); + } + break; + default: + LOG_CRITICAL("Unable to handle onject type for %s (%d)", + name.c_str(), type); + } + + GpiObjHdl::initialise(name); + + return 0; +} + +GpiCbHdl *FliVariableObjHdl::value_change_cb(unsigned int edge) +{ + return NULL; +} + +const char* FliVariableObjHdl::get_signal_value_binstr(void) +{ + switch (mti_GetTypeKind(mti_GetVarType(m_fli_hdl))) { + + case MTI_TYPE_ENUM: + m_val_buff[0] = value_enum[mti_GetVarValue(m_fli_hdl)]; + break; + case MTI_TYPE_SCALAR: + case MTI_TYPE_PHYSICAL: { + std::bitset<32> value((unsigned long)mti_GetVarValue(m_fli_hdl)); + std::string bin_str = value.to_string(); + snprintf(m_val_buff, m_val_len+1, "%s", bin_str.c_str()); + } + break; + case MTI_TYPE_ARRAY: { + mti_GetArrayVarValue(m_fli_hdl, m_mti_buff); + if (m_val_len <= 256) { + char *iter = (char*)m_mti_buff; + for (int i = 0; i < m_val_len; i++ ) { + m_val_buff[i] = value_enum[(int)iter[i]]; + } + } else { + for (int i = 0; i < m_val_len; i++ ) { + m_val_buff[i] = value_enum[m_mti_buff[i]]; + } + } + } + break; + default: + LOG_CRITICAL("Variable %s type %d not currently supported", + m_name.c_str(), mti_GetTypeKind(mti_GetVarType(m_fli_hdl))); + break; + } + + LOG_DEBUG("Retrieved \"%s\" for variable %s", m_val_buff, m_name.c_str()); + + return m_val_buff; +} + +int FliVariableObjHdl::set_signal_value(const int value) +{ + LOG_CRITICAL("Setting variable value not currently supported!\n"); + return -1; +} + +int FliVariableObjHdl::set_signal_value(std::string &value) +{ + LOG_CRITICAL("Setting variable value not currently supported!\n"); + return -1; +} + +int FliVariableObjHdl::initialise(std::string &name) +{ + mtiTypeKindT type; + + /* Pre allocte buffers on signal type basis */ + type = mti_GetTypeKind(mti_GetVarType(m_fli_hdl)); + + switch (type) { + case MTI_TYPE_ENUM: + m_val_len = 1; + m_val_buff = (char*)malloc(m_val_len+1); + if (!m_val_buff) { + LOG_CRITICAL("Unable to alloc mem for signal read buffer"); + } + m_val_buff[m_val_len] = '\0'; + break; + case MTI_TYPE_SCALAR: + case MTI_TYPE_PHYSICAL: + m_val_len = 32; + m_val_buff = (char*)malloc(m_val_len+1); + if (!m_val_buff) { + LOG_CRITICAL("Unable to alloc mem for signal read buffer"); + } + m_val_buff[m_val_len] = '\0'; + break; + case MTI_TYPE_ARRAY: + m_val_len = mti_TickLength(mti_GetVarType(m_fli_hdl)); + m_val_buff = (char*)malloc(m_val_len+1); if (!m_val_buff) { LOG_CRITICAL("Unable to alloc mem for signal read buffer"); } @@ -473,7 +608,7 @@ int FliSignalObjHdl::initialise(std::string &name) break; default: LOG_CRITICAL("Unable to handle onject type for %s (%d)", - name.c_str(), m_type); + name.c_str(), type); } GpiObjHdl::initialise(name); diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index 08e574ae..17c075cf 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -63,7 +63,7 @@ class FliSignalCbHdl : public FliProcessCbHdl, public GpiValueCbHdl { virtual ~FliSignalCbHdl() { } int arm_callback(void); - int cleanup_callback(void) { + int cleanup_callback(void) { return FliProcessCbHdl::cleanup_callback(); } @@ -108,6 +108,38 @@ class FliSignalObjHdl : public GpiSignalObjHdl { int m_val_len; }; +class FliVariableObjHdl : public GpiSignalObjHdl { +public: + FliVariableObjHdl(GpiImplInterface *impl, mtiVariableIdT hdl) : GpiSignalObjHdl(impl, hdl), + m_fli_hdl(hdl), + m_type(MTI_TYPE_SCALAR), + m_mti_buff(NULL), + m_val_buff(NULL), + m_val_len(0) { } + virtual ~FliVariableObjHdl() { + if (m_val_len) + free(m_val_buff); + if (m_mti_buff) + free(m_mti_buff); + } + + const char* get_signal_value_binstr(void); + int set_signal_value(const int value); + int set_signal_value(std::string &value); + int initialise(std::string &name); + GpiCbHdl *value_change_cb(unsigned int edge); + +protected: + mtiVariableIdT m_fli_hdl; + +private: + mtiTypeKindT m_type; + mtiInt32T *m_mti_buff; + char *m_val_buff; + int m_val_len; +}; + + // All other callbacks are related to the simulation phasing class FliSimPhaseCbHdl : public FliProcessCbHdl { diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index d4dc20e7..ade441a5 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -188,7 +188,6 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) GpiObjHdl *hdl; GpiObjHdl *base = sim_to_hdl(parent); std::string s_name = name; - std::string fq_name = base->get_name() + "." + s_name; LOG_DEBUG("Searching for %s", name); @@ -204,7 +203,7 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) as the one that we are querying through */ //std::string &to_query = base->is_this_impl(*iter) ? s_name : fq_name; - if ((hdl = (*iter)->native_check_create(fq_name, base))) { + if ((hdl = (*iter)->native_check_create(s_name, base))) { LOG_DEBUG("Found %s via %s", name, (*iter)->get_name_c()); return (gpi_sim_hdl)hdl; } @@ -318,7 +317,7 @@ gpi_sim_hdl gpi_register_timed_callback(int (*gpi_function)(const void *), LOG_ERROR("Failed to register a timed callback"); return NULL; } - + gpi_hdl->set_user_data(gpi_function, gpi_cb_data); return (gpi_sim_hdl)gpi_hdl; } @@ -334,7 +333,7 @@ gpi_sim_hdl gpi_register_readonly_callback(int (*gpi_function)(const void *), LOG_ERROR("Failed to register a readonly callback"); return NULL; } - + gpi_hdl->set_user_data(gpi_function, gpi_cb_data); return (gpi_sim_hdl)gpi_hdl; } @@ -347,7 +346,7 @@ gpi_sim_hdl gpi_register_nexttime_callback(int (*gpi_function)(const void *), LOG_ERROR("Failed to register a nexttime callback"); return NULL; } - + gpi_hdl->set_user_data(gpi_function, gpi_cb_data); return (gpi_sim_hdl)gpi_hdl; } @@ -363,7 +362,7 @@ gpi_sim_hdl gpi_register_readwrite_callback(int (*gpi_function)(const void *), LOG_ERROR("Failed to register a readwrite callback"); return NULL; } - + gpi_hdl->set_user_data(gpi_function, gpi_cb_data); return (gpi_sim_hdl)gpi_hdl; } diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index b0489749..f87fcd67 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -439,7 +439,7 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) if (callback_data_p == NULL) { printf("Failed to allocate user data\n"); } - + // Set up the user data (no more python API calls after this! // Causes segfault? callback_data_p->_saved_thread_state = PyThreadState_Get();//PyThreadState_Get(); @@ -653,7 +653,7 @@ static PyObject *get_root_handle(PyObject *self, PyObject *args) PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "s", &name)) { + if (!PyArg_ParseTuple(args, "z", &name)) { DROP_GIL(gstate); return NULL; } diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 9f29dd7f..931c1757 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -73,7 +73,7 @@ const char * VhpiImpl::format_to_string(int format) } } -const char *VhpiImpl::reason_to_string(int reason) +const char *VhpiImpl::reason_to_string(int reason) { switch (reason) { case vhpiCbValueChange: @@ -144,20 +144,21 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) { vhpiHandleT new_hdl; - std::vector writable(name.begin(), name.end()); + std::string fq_name = parent->get_name() + "." + name; + std::vector writable(fq_name.begin(), fq_name.end()); writable.push_back('\0'); new_hdl = vhpi_handle_by_name(&writable[0], NULL); if (new_hdl == NULL) { - LOG_DEBUG("Unable to query vhpi_handle_by_name %s", name.c_str()); + LOG_DEBUG("Unable to query vhpi_handle_by_name %s", fq_name.c_str()); return NULL; } - GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name); + GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, fq_name); if (new_obj == NULL) { vhpi_release_handle(new_hdl); - LOG_DEBUG("Unable to fetch object %s", name.c_str()); + LOG_DEBUG("Unable to fetch object %s", fq_name.c_str()); return NULL; } @@ -195,7 +196,7 @@ GpiObjHdl *VhpiImpl::get_root_handle(const char* name) vhpiHandleT root; vhpiHandleT dut; GpiObjHdl *rv; - std::string root_name = name; + std::string root_name; root = vhpi_handle(vhpiRootInst, NULL); check_vhpi_error(); @@ -227,6 +228,7 @@ GpiObjHdl *VhpiImpl::get_root_handle(const char* name) return NULL; } + root_name = found; rv = new GpiObjHdl(this, root); rv->initialise(root_name); @@ -296,13 +298,13 @@ void handle_vhpi_callback(const vhpiCbDataT *cb_data) gpi_cb_state_e old_state = cb_hdl->get_call_state(); - if (old_state == GPI_PRIMED) { + if (old_state == GPI_PRIMED) { cb_hdl->set_call_state(GPI_CALL); cb_hdl->run_callback(); gpi_cb_state_e new_state = cb_hdl->get_call_state(); - + /* We have re-primed in the handler */ if (new_state != GPI_PRIMED) if (cb_hdl->cleanup_callback()) { diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index af28fe4c..28985281 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -115,18 +115,19 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &n GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) { vpiHandle new_hdl; - std::vector writable(name.begin(), name.end()); + std::string fq_name = parent->get_name() + "." + name; + std::vector writable(fq_name.begin(), fq_name.end()); writable.push_back('\0'); new_hdl = vpi_handle_by_name(&writable[0], NULL); if (new_hdl == NULL) { - LOG_DEBUG("Unable to query vpi_get_handle_by_name %s", name.c_str()); + LOG_DEBUG("Unable to query vpi_get_handle_by_name %s", fq_name.c_str()); return NULL; } - GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name); + GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, fq_name); if (new_obj == NULL) { vpi_free_object(new_hdl); - LOG_ERROR("Unable to query fetch object %s", name.c_str()); + LOG_ERROR("Unable to query fetch object %s", fq_name.c_str()); return NULL; } return new_obj; @@ -137,7 +138,7 @@ GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) GpiObjHdl *parent_hdl = sim_to_hdl(parent); vpiHandle vpi_hdl = parent_hdl->get_handle(); vpiHandle new_hdl; - + new_hdl = vpi_handle_by_index(vpi_hdl, index); if (new_hdl == NULL) { LOG_DEBUG("Unable to vpi_get_handle_by_index %d", index); @@ -159,7 +160,7 @@ GpiObjHdl *VpiImpl::get_root_handle(const char* name) vpiHandle root; vpiHandle iterator; GpiObjHdl *rv; - std::string root_name = name; + std::string root_name; // vpi_iterate with a ref of NULL returns the top level module iterator = vpi_iterate(vpiModule, NULL); @@ -182,6 +183,7 @@ GpiObjHdl *VpiImpl::get_root_handle(const char* name) check_vpi_error(); } + root_name = vpi_get_str(vpiFullName, root); rv = new GpiObjHdl(this, root); rv->initialise(root_name); @@ -275,13 +277,13 @@ int32_t handle_vpi_callback(p_cb_data cb_data) gpi_cb_state_e old_state = cb_hdl->get_call_state(); - if (old_state == GPI_PRIMED) { + if (old_state == GPI_PRIMED) { cb_hdl->set_call_state(GPI_CALL); cb_hdl->run_callback(); gpi_cb_state_e new_state = cb_hdl->get_call_state(); - + /* We have re-primed in the handler */ if (new_state != GPI_PRIMED) if (cb_hdl->cleanup_callback()) From c6363648bf724e347e33fd75ec842e59e910546b Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 20 Jul 2015 05:56:11 -0700 Subject: [PATCH 0860/2656] Address comments from pull request --- lib/embed/gpi_embed.c | 2 +- lib/fli/FliImpl.cpp | 30 ++++++++++++------------------ lib/fli/FliImpl.h | 8 ++++---- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 7b65338b..9e87df60 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -90,7 +90,7 @@ void embed_init_python(void) such that they can attach */ const char *pause = getenv("COCOTB_ATTACH"); if (pause) { - int sleep_time = atoi(pause); + int sleep_time = atoi(pause); fprintf(stderr, "Waiting for %d seconds - Attach to %d\n", sleep_time, getpid()); sleep(sleep_time); } diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 1b3fe8c2..939710a1 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -26,7 +26,6 @@ ******************************************************************************/ #include -#include #include #include "FliImpl.h" @@ -102,10 +101,9 @@ void handle_fli_callback(void *data) void FliImpl::sim_end(void) { - char buffer[5]; + const char *stop = "stop"; - snprintf(buffer, 5, "stop"); - mti_Cmd(buffer); + mti_Cmd(stop); } /** @@ -401,7 +399,7 @@ static const char value_enum[10] = "UX01ZWLH-"; const char* FliSignalObjHdl::get_signal_value_binstr(void) { - switch (mti_GetTypeKind(mti_GetSignalType(m_fli_hdl))) { + switch (m_fli_type) { case MTI_TYPE_ENUM: m_val_buff[0] = value_enum[mti_GetSignalValue(m_fli_hdl)]; @@ -429,7 +427,7 @@ const char* FliSignalObjHdl::get_signal_value_binstr(void) break; default: LOG_CRITICAL("Signal %s type %d not currently supported", - m_name.c_str(), mti_GetTypeKind(mti_GetSignalType(m_fli_hdl))); + m_name.c_str(), m_fli_type); break; } @@ -468,12 +466,10 @@ int FliSignalObjHdl::set_signal_value(std::string &value) int FliSignalObjHdl::initialise(std::string &name) { - mtiTypeKindT type; - /* Pre allocte buffers on signal type basis */ - type = mti_GetTypeKind(mti_GetSignalType(m_fli_hdl)); + m_fli_type = mti_GetTypeKind(mti_GetSignalType(m_fli_hdl)); - switch (type) { + switch (m_fli_type) { case MTI_TYPE_ENUM: m_val_len = 1; m_val_buff = (char*)malloc(m_val_len+1); @@ -505,7 +501,7 @@ int FliSignalObjHdl::initialise(std::string &name) break; default: LOG_CRITICAL("Unable to handle onject type for %s (%d)", - name.c_str(), type); + name.c_str(), m_fli_type); } GpiObjHdl::initialise(name); @@ -520,7 +516,7 @@ GpiCbHdl *FliVariableObjHdl::value_change_cb(unsigned int edge) const char* FliVariableObjHdl::get_signal_value_binstr(void) { - switch (mti_GetTypeKind(mti_GetVarType(m_fli_hdl))) { + switch (m_fli_type) { case MTI_TYPE_ENUM: m_val_buff[0] = value_enum[mti_GetVarValue(m_fli_hdl)]; @@ -548,7 +544,7 @@ const char* FliVariableObjHdl::get_signal_value_binstr(void) break; default: LOG_CRITICAL("Variable %s type %d not currently supported", - m_name.c_str(), mti_GetTypeKind(mti_GetVarType(m_fli_hdl))); + m_name.c_str(), m_fli_type); break; } @@ -571,12 +567,10 @@ int FliVariableObjHdl::set_signal_value(std::string &value) int FliVariableObjHdl::initialise(std::string &name) { - mtiTypeKindT type; - /* Pre allocte buffers on signal type basis */ - type = mti_GetTypeKind(mti_GetVarType(m_fli_hdl)); + m_fli_type = mti_GetTypeKind(mti_GetVarType(m_fli_hdl)); - switch (type) { + switch (m_fli_type) { case MTI_TYPE_ENUM: m_val_len = 1; m_val_buff = (char*)malloc(m_val_len+1); @@ -608,7 +602,7 @@ int FliVariableObjHdl::initialise(std::string &name) break; default: LOG_CRITICAL("Unable to handle onject type for %s (%d)", - name.c_str(), type); + name.c_str(), m_fli_type); } GpiObjHdl::initialise(name); diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index 17c075cf..e2593ffc 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -78,7 +78,7 @@ class FliSignalObjHdl : public GpiSignalObjHdl { m_rising_cb(impl, this, GPI_RISING), m_falling_cb(impl, this, GPI_FALLING), m_either_cb(impl, this, GPI_FALLING | GPI_RISING), - m_type(MTI_TYPE_SCALAR), + m_fli_type(MTI_TYPE_SCALAR), m_mti_buff(NULL), m_val_buff(NULL), m_val_len(0) { } @@ -102,7 +102,7 @@ class FliSignalObjHdl : public GpiSignalObjHdl { FliSignalCbHdl m_either_cb; private: - mtiTypeKindT m_type; + mtiTypeKindT m_fli_type; mtiInt32T *m_mti_buff; char *m_val_buff; int m_val_len; @@ -112,7 +112,7 @@ class FliVariableObjHdl : public GpiSignalObjHdl { public: FliVariableObjHdl(GpiImplInterface *impl, mtiVariableIdT hdl) : GpiSignalObjHdl(impl, hdl), m_fli_hdl(hdl), - m_type(MTI_TYPE_SCALAR), + m_fli_type(MTI_TYPE_SCALAR), m_mti_buff(NULL), m_val_buff(NULL), m_val_len(0) { } @@ -133,7 +133,7 @@ class FliVariableObjHdl : public GpiSignalObjHdl { mtiVariableIdT m_fli_hdl; private: - mtiTypeKindT m_type; + mtiTypeKindT m_fli_type; mtiInt32T *m_mti_buff; char *m_val_buff; int m_val_len; From 082e1316f6ad18a1f0c16fdb946cb88a589d3c47 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 20 Jul 2015 21:11:56 +0100 Subject: [PATCH 0861/2656] Fixes #251: Use full path to include Makefile.questa --- makefiles/simulators/Makefile.modelsim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index 1425264d..92a7bec7 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -31,5 +31,5 @@ ARCH:=i686 # Everything else is identical to Quest -include Makefile.questa +include $(SIM_ROOT)/makefiles/simulators/Makefile.questa From adbf0a22bdcdad50ba912d39169082035f39f6e0 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 24 Jul 2015 12:20:33 +0100 Subject: [PATCH 0862/2656] Issue #17: Add new methods for determining object type --- include/gpi.h | 2 ++ lib/simulator/simulatormodule.h | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/gpi.h b/include/gpi.h index 912a3e5f..ca6e93cd 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -149,6 +149,8 @@ const char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl); const char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl); const char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl); +// Returns on of the types defined above e.g. gpiMemory etc. +int get_object_type(gpi_sim_hdl gpi_hdl); // Functions for setting the properties of a handle diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 660598f1..81a3b986 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -64,6 +64,7 @@ static PyObject *get_handle_by_name(PyObject *self, PyObject *args); static PyObject *get_handle_by_index(PyObject *self, PyObject *args); static PyObject *get_root_handle(PyObject *self, PyObject *args); static PyObject *get_name_string(PyObject *self, PyObject *args); +static PyObject *get_type(PyObject *self, PyObject *args); static PyObject *get_type_string(PyObject *self, PyObject *args); static PyObject *register_timed_callback(PyObject *self, PyObject *args); static PyObject *register_value_change_callback(PyObject *self, PyObject *args); @@ -86,8 +87,9 @@ static PyMethodDef SimulatorMethods[] = { {"get_handle_by_name", get_handle_by_name, METH_VARARGS, "Get handle of a named object"}, {"get_handle_by_index", get_handle_by_index, METH_VARARGS, "Get handle of a object at an index in a parent"}, {"get_root_handle", get_root_handle, METH_VARARGS, "Get the root handle"}, - {"get_name_string", get_name_string, METH_VARARGS, "Get the name of an object"}, - {"get_type_string", get_type_string, METH_VARARGS, "Get the type of an object"}, + {"get_name_string", get_name_string, METH_VARARGS, "Get the name of an object as a string"}, + {"get_type_string", get_type_string, METH_VARARGS, "Get the type of an object as a string"}, + {"get_type", get_type_string, METH_VARARGS, "Get the type of an object, mapped to a GPI enumeration"}, {"register_timed_callback", register_timed_callback, METH_VARARGS, "Register a timed callback"}, {"register_value_change_callback", register_value_change_callback, METH_VARARGS, "Register a signal change callback"}, {"register_readonly_callback", register_readonly_callback, METH_VARARGS, "Register a callback for readonly section"}, From 5f4395ebd306fe506bd8ecb03e62ad74299168f7 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 24 Jul 2015 12:30:37 +0100 Subject: [PATCH 0863/2656] Issue #17 (in progress) re-factor handle to add hierarchy --- cocotb/handle.py | 267 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 199 insertions(+), 68 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 88581ed8..637c2741 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -52,8 +52,129 @@ from cocotb.triggers import _RisingEdge, _FallingEdge from cocotb.utils import get_python_integer_types -class SimHandle(object): + + + +class SimHandleBase(object): + """ + Base class for all simulation objects. + + We maintain a handle which we can use for GPI calls + """ + def __init__(self, handle): + """ + Args: + handle (integer) : the GPI handle to the simulator object + """ + self._handle = handle + self._len = None + self._sub_handles = {} # Dictionary of children + + self._name = simulator.get_name_string(self._handle) + self._fullname = self._name + "(%s)" % simulator.get_type_string(self._handle) + self._log = SimLog("cocotb.%s" % self._name) + self._log.debug("Created") + + def __hash__(self): + return self._handle + + def __len__(self): + """Returns the 'length' of the underlying object. + + For vectors this is the number of bits. + + TODO: Handle other types (loops, generate etc) + """ + if self._len is None: + self._len = len(self._get_value_str()) + return self._len + + def __getattr__(self, name): + """ Query the simulator for a object with the specified name + and cache the result to build a tree + """ + if name in self._sub_handles: + return self._sub_handles[name] + new_handle = simulator.get_handle_by_name(self._handle, name) + if not new_handle: + raise AttributeError("%s contains no object named %s" % (self.name, name)) + self._sub_handles[name] = SimHandle(new_handle) + return self._sub_handles[name] + + def __hasattr__(self, name): + """ + Since calling hasattr(handle, "something") will print out a + backtrace to the log since usually attempting to access a + non-existent member is an error we provide a 'peek function + + We still add the found handle to our dictionary to prevent leaking + handles. + """ + if name in self._sub_handles: + return self._sub_handles[name] + new_handle = simulator.get_handle_by_name(self._handle, name) + if new_handle: + self._sub_handles[name] = SimHandle(new_handle) + return new_handle + + + def __getitem__(self, index): + if index in self._sub_handles: + return self._sub_handles[index] + new_handle = simulator.get_handle_by_index(self._handle, index) + if not new_handle: + self._raise_testerror("%s contains no object at index %d" % (self.name, index)) + self._sub_handles[index] = SimHandle(new_handle) + return self._sub_handles[index] + + + def __cmp__(self, other): + + # Permits comparison of handles i.e. if clk == dut.clk + if isinstance(other, SimHandle): + if self._handle == other._handle: return 0 + return 1 + + def __dir__(self): + return self._sub_handles.keys() + + def _discover_all(self): + for thing in self: + pass + + def _getAttributeNames(self): + """Permits IPython tab completion to work""" + self._discover_all() + return dir(self) + + def __repr__(self): + return repr(int(self)) + +class HierarchyObjecct(SimHandleBase): + """ + Hierarchy objects don't have values, they are effectively scopes or namespaces + """ + pass + + +class ConstantObject(SimHandleBase): + """ + Constant objects have a value that can be read, but not set. + + We can also cache the value since it is elaboration time fixed and won't + change within a simulation + """ + def __init__(self, handle, *args, **kwargs): + SimHandleBase.__init__(self, handle) + self._value = None + + + def __int__(self): + return int(self._value) + + +class NonConstantObject(SimHandle): def __init__(self, handle): """ Args: @@ -104,13 +225,6 @@ def _raise_testerror(self, msg): buff.close() raise exception - def __setattr__(self, name, value): - """Provide transparent access to signals""" - if not name.startswith('_') and not name in ["name", "fullname", "log", "value"] \ - and self.__hasattr__(name): - getattr(self, name).setcachedvalue(value) - return - object.__setattr__(self, name, value) def __hasattr__(self, name): """ @@ -137,68 +251,19 @@ def __getitem__(self, index): self._sub_handles[index] = SimHandle(new_handle) return self._sub_handles[index] - def __setitem__(self, index, value): - """Provide transparent assignment to bit index""" - self.__getitem__(index).setcachedvalue(value) def getvalue(self): result = BinaryValue() result.binstr = self._get_value_str() return result - def setimmediatevalue(self, value): - """ - Set the value of the underlying simulation object to value. - - Args: - value (ctypes.Structure, cocotb.binary.BinaryValue, int) - The value to drive onto the simulator object - - Raises: - TypeError - - This operation will fail unless the handle refers to a modifiable - object eg net, signal or variable. - - We determine the library call to make based on the type of the value - - Assigning integers less than 32-bits is faster - """ - if isinstance(value, get_python_integer_types()) and value < 0x7fffffff: - simulator.set_signal_val(self._handle, value) - return - - if isinstance(value, ctypes.Structure): - value = BinaryValue(value=cocotb.utils.pack(value), bits=len(self)) - elif isinstance(value, get_python_integer_types()): - value = BinaryValue(value=value, bits=len(self), bigEndian=False) - elif not isinstance(value, BinaryValue): - self.log.critical("Unsupported type for value assignment: %s (%s)" % (type(value), repr(value))) - raise TypeError("Unable to set simulator value with type %s" % (type(value))) - - simulator.set_signal_val_str(self._handle, value.binstr) - - def setcachedvalue(self, value): - """Intercept the store of a value and hold in cache. - - This operation is to enable all of the scheduled callbacks to completed - with the same read data and for the writes to occour on the next - sim time""" - cocotb.scheduler.save_write(self, value) # We want to maintain compatability with python 2.5 so we can't use @property with a setter - value = property(getvalue, setcachedvalue, None, "A reference to the value") + value = property(getvalue, None, None, "A reference to the value") def _get_value_str(self): return simulator.get_signal_val(self._handle) - def __le__(self, value): - """Overload the less than or equal to operator to - provide an hdl-like shortcut - module.signal <= 2 - """ - self.value = value - def __len__(self): """Returns the 'length' of the underlying object. @@ -243,16 +308,82 @@ def __iter__(self): def __int__(self): return int(self.value) - def _discover_all(self): - for thing in self: - pass - def __dir__(self): - return self._sub_handles.keys() +class ModifiableObject(SimHandle): + """ + Base class for simulator objects whose values can be modified + """ - def _getAttributeNames(self): - self._discover_all() - return dir(self) + def __setattr__(self, name, value): + """Provide transparent access to signals""" + if not name.startswith('_') and not name in ["name", "fullname", "log", "value"] \ + and self.__hasattr__(name): + getattr(self, name).setcachedvalue(value) + return + object.__setattr__(self, name, value) - def __repr__(self): - return repr(int(self)) + def __setitem__(self, index, value): + """Provide transparent assignment to bit index""" + self.__getitem__(index).setcachedvalue(value) + + def setimmediatevalue(self, value): + """ + Set the value of the underlying simulation object to value. + + Args: + value (ctypes.Structure, cocotb.binary.BinaryValue, int) + The value to drive onto the simulator object + + Raises: + TypeError + + This operation will fail unless the handle refers to a modifiable + object eg net, signal or variable. + + We determine the library call to make based on the type of the value + + Assigning integers less than 32-bits is faster + """ + if isinstance(value, get_python_integer_types()) and value < 0x7fffffff: + simulator.set_signal_val(self._handle, value) + return + + if isinstance(value, ctypes.Structure): + value = BinaryValue(value=cocotb.utils.pack(value), bits=len(self)) + elif isinstance(value, get_python_integer_types()): + value = BinaryValue(value=value, bits=len(self), bigEndian=False) + elif not isinstance(value, BinaryValue): + self.log.critical("Unsupported type for value assignment: %s (%s)" % (type(value), repr(value))) + raise TypeError("Unable to set simulator value with type %s" % (type(value))) + + simulator.set_signal_val_str(self._handle, value.binstr) + + def setcachedvalue(self, value): + """ + Intercept the store of a value and hold in cache. + + This operation is to enable all of the scheduled callbacks to completed + with the same read data and for the writes to occour on the next + sim time + """ + cocotb.scheduler.save_write(self, value) + + # We want to maintain compatability with python 2.5 so we can't use @property with a setter + value = property(getvalue, setcachedvalue, None, "A reference to the value") + + + def __le__(self, value): + """Overload the less than or equal to operator to + provide an hdl-like shortcut + module.signal <= 2 + """ + self.value = value + + + +def get_simhandle(handle): + """ + Factory function to create the correct type of SimHandle object + """ + pass + From 3bddf65e7b71fbb2eb216ce9b6555148728e96c9 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 24 Jul 2015 13:22:34 +0100 Subject: [PATCH 0864/2656] Issue #17: Fix up some compilation errors --- cocotb/handle.py | 31 ++++++++++++++++--------------- include/gpi.h | 2 +- lib/gpi/GpiCbHdl.cpp | 5 +++++ lib/gpi/GpiCommon.cpp | 6 ++++++ lib/gpi/gpi_priv.h | 3 +++ lib/simulator/simulatormodule.c | 22 ++++++++++++++++++++++ lib/simulator/simulatormodule.h | 2 +- 7 files changed, 54 insertions(+), 17 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 637c2741..3571dfac 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -174,7 +174,7 @@ def __int__(self): return int(self._value) -class NonConstantObject(SimHandle): +class NonConstantObject(SimHandleBase): def __init__(self, handle): """ Args: @@ -251,20 +251,15 @@ def __getitem__(self, index): self._sub_handles[index] = SimHandle(new_handle) return self._sub_handles[index] - - def getvalue(self): + @property + def value(self): result = BinaryValue() result.binstr = self._get_value_str() return result - - # We want to maintain compatability with python 2.5 so we can't use @property with a setter - value = property(getvalue, None, None, "A reference to the value") - def _get_value_str(self): return simulator.get_signal_val(self._handle) - def __len__(self): """Returns the 'length' of the underlying object. @@ -309,7 +304,7 @@ def __int__(self): return int(self.value) -class ModifiableObject(SimHandle): +class ModifiableObject(SimHandleBase): """ Base class for simulator objects whose values can be modified """ @@ -358,6 +353,13 @@ def setimmediatevalue(self, value): simulator.set_signal_val_str(self._handle, value.binstr) + @property + def value(self): + result = BinaryValue() + result.binstr = self._get_value_str() + return result + + @value.setter def setcachedvalue(self, value): """ Intercept the store of a value and hold in cache. @@ -368,9 +370,6 @@ def setcachedvalue(self, value): """ cocotb.scheduler.save_write(self, value) - # We want to maintain compatability with python 2.5 so we can't use @property with a setter - value = property(getvalue, setcachedvalue, None, "A reference to the value") - def __le__(self, value): """Overload the less than or equal to operator to @@ -381,9 +380,11 @@ def __le__(self, value): -def get_simhandle(handle): +def SimHandle(handle): """ Factory function to create the correct type of SimHandle object """ - pass - + t = simulator.get_type(handle) + print t + return None + diff --git a/include/gpi.h b/include/gpi.h index ca6e93cd..b31e4cec 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -150,7 +150,7 @@ const char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl); const char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl); // Returns on of the types defined above e.g. gpiMemory etc. -int get_object_type(gpi_sim_hdl gpi_hdl); +int gpi_get_object_type(gpi_sim_hdl gpi_hdl); // Functions for setting the properties of a handle diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index 0d8177e2..10c9831a 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -48,6 +48,11 @@ const char * GpiObjHdl::get_type_str(void) return m_type.c_str(); } +int GpiObjHdl::get_type(void) +{ + return m_type_e; +} + const std::string & GpiObjHdl::get_name(void) { return m_name; diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 8434b49d..f2dccaf5 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -269,6 +269,12 @@ const char *gpi_get_signal_type_str(gpi_sim_hdl sig_hdl) return obj_hdl->get_type_str(); } +int gpi_get_object_type(gpi_sim_hdl sig_hdl) +{ + GpiObjHdl *obj_hdl = sim_to_hdl(sig_hdl); + return obj_hdl->get_type(); +} + void gpi_set_signal_value_int(gpi_sim_hdl sig_hdl, int value) { GpiSignalObjHdl *obj_hdl = sim_to_hdl(sig_hdl); diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 2382c0d0..285b964d 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -103,6 +103,8 @@ class GpiObjHdl : public GpiHdl { virtual const char* get_name_str(void); virtual const char* get_fullname_str(void); virtual const char* get_type_str(void); + virtual int get_type(void); + const std::string & get_name(void); bool is_native_impl(GpiImplInterface *impl); @@ -112,6 +114,7 @@ class GpiObjHdl : public GpiHdl { std::string m_name; std::string m_fullname; std::string m_type; + int m_type_e; }; diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index b0489749..5b063e20 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -690,6 +690,28 @@ static PyObject *get_name_string(PyObject *self, PyObject *args) return retstr; } +static PyObject *get_type(PyObject *self, PyObject *args) +{ + int result; + gpi_sim_hdl hdl; + PyObject *pyresult; + + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + if (!PyArg_ParseTuple(args, "l", &hdl)) { + DROP_GIL(gstate); + return NULL; + } + + result = gpi_get_object_type((gpi_sim_hdl)hdl); + pyresult = Py_BuildValue("i", result); + + DROP_GIL(gstate); + + return pyresult; +} + static PyObject *get_type_string(PyObject *self, PyObject *args) { diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 81a3b986..e5e41298 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -89,7 +89,7 @@ static PyMethodDef SimulatorMethods[] = { {"get_root_handle", get_root_handle, METH_VARARGS, "Get the root handle"}, {"get_name_string", get_name_string, METH_VARARGS, "Get the name of an object as a string"}, {"get_type_string", get_type_string, METH_VARARGS, "Get the type of an object as a string"}, - {"get_type", get_type_string, METH_VARARGS, "Get the type of an object, mapped to a GPI enumeration"}, + {"get_type", get_type, METH_VARARGS, "Get the type of an object, mapped to a GPI enumeration"}, {"register_timed_callback", register_timed_callback, METH_VARARGS, "Register a timed callback"}, {"register_value_change_callback", register_value_change_callback, METH_VARARGS, "Register a signal change callback"}, {"register_readonly_callback", register_readonly_callback, METH_VARARGS, "Register a callback for readonly section"}, From bc50018381babd5f2faadf0fd78849bfc8426359 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 24 Jul 2015 14:08:04 +0100 Subject: [PATCH 0865/2656] Issue #258: Move all regression tests, apart from mixed lang, out of /examples into /test --- Makefile | 5 +- examples/Makefile | 15 +- examples/sim_exit/tests/before.out | 225 ------------------ examples/sim_exit/tests/mem.out | 40 ---- tests/Makefile | 41 ++++ .../designs/close_module}/Makefile | 3 +- .../designs/close_module}/close_module.v | 0 .../designs/plusargs_module}/Makefile | 5 +- .../designs/plusargs_module}/tb_top.v | 0 .../designs/sample_module}/Makefile | 6 +- .../designs/sample_module}/sample_module.v | 0 .../designs/sample_module}/sample_module.vhdl | 0 tests/test_cases/issue_120/Makefile | 33 +++ .../test_cases/issue_120/issue_120.py | 23 -- tests/test_cases/issue_142/Makefile | 31 +++ tests/test_cases/issue_142/issue_142.py | 30 +++ tests/test_cases/test_closedown/Makefile | 30 +++ .../test_closedown}/test_closedown.py | 0 tests/test_cases/test_cocotb/Makefile | 33 +++ .../test_cases/test_cocotb}/test_cocotb.py | 0 tests/test_cases/test_discovery/Makefile | 31 +++ .../test_discovery}/test_discovery.py | 0 tests/test_cases/test_exit_error/Makefile | 31 +++ tests/test_cases/test_exit_error/test_exit.py | 14 ++ tests/test_cases/test_external/Makefile | 33 +++ .../test_external}/test_external.py | 0 tests/test_cases/test_plusargs/Makefile | 34 +++ .../test_cases/test_plusargs}/plusargs.py | 0 28 files changed, 354 insertions(+), 309 deletions(-) delete mode 100644 examples/sim_exit/tests/before.out delete mode 100644 examples/sim_exit/tests/mem.out create mode 100644 tests/Makefile rename {examples/sim_exit/tests => tests/designs/close_module}/Makefile (96%) rename {examples/sim_exit/hdl => tests/designs/close_module}/close_module.v (100%) rename {examples/plusargs => tests/designs/plusargs_module}/Makefile (95%) rename {examples/plusargs => tests/designs/plusargs_module}/tb_top.v (100%) rename {examples/functionality/tests => tests/designs/sample_module}/Makefile (92%) rename {examples/functionality/hdl => tests/designs/sample_module}/sample_module.v (100%) rename {examples/functionality/hdl => tests/designs/sample_module}/sample_module.vhdl (100%) create mode 100644 tests/test_cases/issue_120/Makefile rename examples/functionality/tests/test_regression.py => tests/test_cases/issue_120/issue_120.py (59%) create mode 100644 tests/test_cases/issue_142/Makefile create mode 100644 tests/test_cases/issue_142/issue_142.py create mode 100644 tests/test_cases/test_closedown/Makefile rename {examples/sim_exit/tests => tests/test_cases/test_closedown}/test_closedown.py (100%) create mode 100644 tests/test_cases/test_cocotb/Makefile rename {examples/functionality/tests => tests/test_cases/test_cocotb}/test_cocotb.py (100%) create mode 100644 tests/test_cases/test_discovery/Makefile rename {examples/functionality/tests => tests/test_cases/test_discovery}/test_discovery.py (100%) create mode 100644 tests/test_cases/test_exit_error/Makefile create mode 100644 tests/test_cases/test_exit_error/test_exit.py create mode 100644 tests/test_cases/test_external/Makefile rename {examples/functionality/tests => tests/test_cases/test_external}/test_external.py (100%) create mode 100644 tests/test_cases/test_plusargs/Makefile rename {examples/plusargs => tests/test_cases/test_plusargs}/plusargs.py (100%) diff --git a/Makefile b/Makefile index 7f13c61d..7c3338c3 100644 --- a/Makefile +++ b/Makefile @@ -39,11 +39,12 @@ clean: -@rm -rf $(BUILD_DIR) -@find . -name "obj" | xargs rm -rf -@find . -name "*.pyc" | xargs rm -rf - -@find . -name "results.xml" | xargs rm -rf + -@find . -name "*results.xml" | xargs rm -rf $(MAKE) -C examples clean + $(MAKE) -C tests clean do_tests: - $(MAKE) -k -C examples + $(MAKE) -k -C tests # For jenkins we use the exit code to detect compile errors or catestrphic # failures and the xml to track test results diff --git a/examples/Makefile b/examples/Makefile index 0de8fbdc..e48d9a88 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -30,18 +30,15 @@ COCOTB?=$(shell pwd)/../ export COCOTB -SMOKE_TESTS := plusargs \ - sim_exit/tests \ - functionality/tests \ - adder/tests \ - endian_swapper/tests \ +EXAMPLES := adder/tests \ + endian_swapper/tests \ -.PHONY: $(SMOKE_TESTS) +.PHONY: $(EXAMPLES) -all: $(SMOKE_TESTS) +all: $(EXAMPLES) -$(SMOKE_TESTS): +$(EXAMPLES): @cd $@ && $(MAKE) clean: - $(foreach TEST, $(SMOKE_TESTS), $(MAKE) -C $(TEST) clean;) + $(foreach TEST, $(EXAMPLES), $(MAKE) -C $(TEST) clean;) diff --git a/examples/sim_exit/tests/before.out b/examples/sim_exit/tests/before.out deleted file mode 100644 index 12a986b0..00000000 --- a/examples/sim_exit/tests/before.out +++ /dev/null @@ -1,225 +0,0 @@ - -Running test 1/8:test_yield_list -Partition of a set of 33098 objects. Total size = 2449120 bytes. - Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) - 0 12235 37 624288 25 624288 25 types.CodeType - 1 1870 6 192136 8 816424 33 dict of module - 2 3027 9 176468 7 992892 41 dict of type - 3 897 3 154844 6 1147736 47 type - 4 2313 7 153360 6 1301096 53 function - 5 507 2 114676 5 1415772 58 function, tuple - 6 2547 8 96624 4 1512396 62 tuple - 7 55 0 89656 4 1602052 65 function, module - 8 1781 5 73380 3 1675432 68 dict (no owner) - 9 176 1 68876 3 1744308 71 class -<628 more rows. Type e.g. '_.more' to view.> -End of test -Partition of a set of 33326 objects. Total size = 2478564 bytes. - Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) - 0 12235 37 624288 25 624288 25 types.CodeType - 1 1870 6 192136 8 816424 33 dict of module - 2 3027 9 176468 7 992892 40 dict of type - 3 897 3 154844 6 1147736 46 type - 4 2311 7 153216 6 1300952 52 function - 5 506 2 114604 5 1415556 57 function, tuple - 6 2541 8 96492 4 1512048 61 tuple - 7 55 0 89656 4 1601704 65 function, module - 8 1822 5 74664 3 1676368 68 dict (no owner) - 9 178 1 69044 3 1745412 70 class -<649 more rows. Type e.g. '_.more' to view.> -Running test 2/8:test_function_not_a_coroutine -Partition of a set of 33329 objects. Total size = 2479008 bytes. - Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) - 0 12235 37 624288 25 624288 25 types.CodeType - 1 1870 6 192136 8 816424 33 dict of module - 2 3027 9 176468 7 992892 40 dict of type - 3 897 3 154844 6 1147736 46 type - 4 2310 7 153144 6 1300880 52 function - 5 506 2 114604 5 1415484 57 function, tuple - 6 2541 8 96492 4 1511976 61 tuple - 7 55 0 89656 4 1601632 65 function, module - 8 1822 5 74664 3 1676296 68 dict (no owner) - 9 178 1 69044 3 1745340 70 class -<649 more rows. Type e.g. '_.more' to view.> -End of test -Partition of a set of 33353 objects. Total size = 2480976 bytes. - Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) - 0 12235 37 624288 25 624288 25 types.CodeType - 1 1870 6 192136 8 816424 33 dict of module - 2 3027 9 176468 7 992892 40 dict of type - 3 897 3 154844 6 1147736 46 type - 4 2311 7 153216 6 1300952 52 function - 5 506 2 114604 5 1415556 57 function, tuple - 6 2541 8 96492 4 1512048 61 tuple - 7 55 0 89656 4 1601704 65 function, module - 8 1825 5 74748 3 1676452 68 dict (no owner) - 9 178 1 69044 3 1745496 70 class -<652 more rows. Type e.g. '_.more' to view.> -Running test 3/8:test_coroutine_kill -Partition of a set of 33354 objects. Total size = 2481344 bytes. - Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) - 0 12235 37 624288 25 624288 25 types.CodeType - 1 1870 6 192136 8 816424 33 dict of module - 2 3027 9 176468 7 992892 40 dict of type - 3 897 3 154844 6 1147736 46 type - 4 2310 7 153144 6 1300880 52 function - 5 506 2 114604 5 1415484 57 function, tuple - 6 2541 8 96492 4 1511976 61 tuple - 7 55 0 89656 4 1601632 65 function, module - 8 1825 5 74748 3 1676380 68 dict (no owner) - 9 178 1 69044 3 1745424 70 class -<652 more rows. Type e.g. '_.more' to view.> -End of test -Partition of a set of 33422 objects. Total size = 2494636 bytes. - Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) - 0 12235 37 624288 25 624288 25 types.CodeType - 1 1870 6 192136 8 816424 33 dict of module - 2 3027 9 176468 7 992892 40 dict of type - 3 897 3 154844 6 1147736 46 type - 4 2311 7 153216 6 1300952 52 function - 5 506 2 114604 5 1415556 57 function, tuple - 6 2541 8 96492 4 1512048 61 tuple - 7 55 0 89656 4 1601704 64 function, module - 8 1843 6 75280 3 1676984 67 dict (no owner) - 9 178 1 69044 3 1746028 70 class -<651 more rows. Type e.g. '_.more' to view.> -Running test 4/8:test_adding_a_coroutine_without_starting -Partition of a set of 33438 objects. Total size = 2496172 bytes. - Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) - 0 12235 37 624288 25 624288 25 types.CodeType - 1 1869 6 192140 8 816428 33 dict of module - 2 3026 9 176412 7 992840 40 dict of type - 3 897 3 154844 6 1147684 46 type - 4 2310 7 153104 6 1300788 52 function - 5 506 2 114624 5 1415412 57 function, tuple - 6 2541 8 96436 4 1511848 61 tuple - 7 55 0 89656 4 1601504 64 function, module - 8 1841 6 75224 3 1676728 67 dict (no owner) - 9 178 1 69044 3 1745772 70 class -<662 more rows. Type e.g. '_.more' to view.> -End of test -Partition of a set of 33490 objects. Total size = 2502996 bytes. - Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) - 0 12235 37 624288 25 624288 25 types.CodeType - 1 1869 6 192140 8 816428 33 dict of module - 2 3026 9 176412 7 992840 40 dict of type - 3 897 3 154844 6 1147684 46 type - 4 2311 7 153176 6 1300860 52 function - 5 506 2 114624 5 1415484 57 function, tuple - 6 2539 8 96336 4 1511820 60 tuple - 7 55 0 89656 4 1601476 64 function, module - 8 1846 6 75364 3 1676840 67 dict (no owner) - 9 178 1 69044 3 1745884 70 class -<662 more rows. Type e.g. '_.more' to view.> -Running test 5/8:test_function_not_a_coroutine_fork -Partition of a set of 33491 objects. Total size = 2503364 bytes. - Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) - 0 12235 37 624288 25 624288 25 types.CodeType - 1 1869 6 192140 8 816428 33 dict of module - 2 3026 9 176412 7 992840 40 dict of type - 3 897 3 154844 6 1147684 46 type - 4 2310 7 153104 6 1300788 52 function - 5 506 2 114624 5 1415412 57 function, tuple - 6 2539 8 96336 4 1511748 60 tuple - 7 55 0 89656 4 1601404 64 function, module - 8 1846 6 75364 3 1676768 67 dict (no owner) - 9 178 1 69044 3 1745812 70 class -<662 more rows. Type e.g. '_.more' to view.> -End of test -Partition of a set of 33510 objects. Total size = 2505008 bytes. - Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) - 0 12235 37 624288 25 624288 25 types.CodeType - 1 1869 6 192140 8 816428 33 dict of module - 2 3026 9 176412 7 992840 40 dict of type - 3 897 3 154844 6 1147684 46 type - 4 2311 7 153176 6 1300860 52 function - 5 506 2 114624 5 1415484 57 function, tuple - 6 2539 8 96336 4 1511820 60 tuple - 7 55 0 89656 4 1601476 64 function, module - 8 1849 6 75448 3 1676924 67 dict (no owner) - 9 178 1 69044 3 1745968 70 class -<663 more rows. Type e.g. '_.more' to view.> -Running test 6/8:test_function_reentrant_clock -Partition of a set of 33511 objects. Total size = 2505372 bytes. - Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) - 0 12235 37 624288 25 624288 25 types.CodeType - 1 1869 6 192140 8 816428 33 dict of module - 2 3026 9 176412 7 992840 40 dict of type - 3 897 3 154844 6 1147684 46 type - 4 2310 7 153104 6 1300788 52 function - 5 506 2 114624 5 1415412 56 function, tuple - 6 2539 8 96336 4 1511748 60 tuple - 7 55 0 89656 4 1601404 64 function, module - 8 1849 6 75448 3 1676852 67 dict (no owner) - 9 178 1 69044 3 1745896 70 class -<663 more rows. Type e.g. '_.more' to view.> -End of test -Partition of a set of 33694 objects. Total size = 2534072 bytes. - Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) - 0 12235 36 624288 25 624288 25 types.CodeType - 1 1869 6 192140 8 816428 32 dict of module - 2 3026 9 176412 7 992840 39 dict of type - 3 897 3 154844 6 1147684 45 type - 4 2311 7 153176 6 1300860 51 function - 5 506 2 114624 5 1415484 56 function, tuple - 6 2539 8 96336 4 1511820 60 tuple - 7 55 0 89656 4 1601476 63 function, module - 8 1888 6 76540 3 1678016 66 dict (no owner) - 9 178 1 69044 3 1747060 69 class -<665 more rows. Type e.g. '_.more' to view.> -Running test 7/8:simple_test -Partition of a set of 33695 objects. Total size = 2534440 bytes. - Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) - 0 12235 36 624288 25 624288 25 types.CodeType - 1 1869 6 192140 8 816428 32 dict of module - 2 3026 9 176412 7 992840 39 dict of type - 3 897 3 154844 6 1147684 45 type - 4 2310 7 153104 6 1300788 51 function - 5 506 2 114624 5 1415412 56 function, tuple - 6 2539 8 96336 4 1511748 60 tuple - 7 55 0 89656 4 1601404 63 function, module - 8 1888 6 76540 3 1677944 66 dict (no owner) - 9 178 1 69044 3 1746988 69 class -<665 more rows. Type e.g. '_.more' to view.> -End of test -Partition of a set of 33707 objects. Total size = 2535580 bytes. - Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) - 0 12235 36 624288 25 624288 25 types.CodeType - 1 1869 6 192140 8 816428 32 dict of module - 2 3026 9 176412 7 992840 39 dict of type - 3 897 3 154844 6 1147684 45 type - 4 2311 7 153176 6 1300860 51 function - 5 506 2 114624 5 1415484 56 function, tuple - 6 2539 8 96336 4 1511820 60 tuple - 7 56 0 90176 4 1601996 63 function, module - 8 1891 6 76624 3 1678620 66 dict (no owner) - 9 178 1 69044 3 1747664 69 class -<665 more rows. Type e.g. '_.more' to view.> -Running test 8/8:discover_module_values -Partition of a set of 33708 objects. Total size = 2535944 bytes. - Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) - 0 12235 36 624288 25 624288 25 types.CodeType - 1 1869 6 192140 8 816428 32 dict of module - 2 3026 9 176412 7 992840 39 dict of type - 3 897 3 154844 6 1147684 45 type - 4 2310 7 153104 6 1300788 51 function - 5 506 2 114624 5 1415412 56 function, tuple - 6 2539 8 96336 4 1511748 60 tuple - 7 56 0 90176 4 1601924 63 function, module - 8 1891 6 76624 3 1678548 66 dict (no owner) - 9 178 1 69044 3 1747592 69 class -<664 more rows. Type e.g. '_.more' to view.> -End of test -Partition of a set of 33792 objects. Total size = 2547164 bytes. - Index Count % Size % Cumulative % Referrers by Kind (class / dict of class) - 0 12235 36 624288 25 624288 25 types.CodeType - 1 1869 6 192140 8 816428 32 dict of module - 2 3026 9 176412 7 992840 39 dict of type - 3 897 3 154844 6 1147684 45 type - 4 2311 7 153176 6 1300860 51 function - 5 506 1 114624 5 1415484 56 function, tuple - 6 2539 8 96336 4 1511820 59 tuple - 7 57 0 90696 4 1602516 63 function, module - 8 1901 6 76904 3 1679420 66 dict (no owner) - 9 133 0 69160 3 1748580 69 logging.Logger -<665 more rows. Type e.g. '_.more' to view.> \ No newline at end of file diff --git a/examples/sim_exit/tests/mem.out b/examples/sim_exit/tests/mem.out deleted file mode 100644 index 99da54a2..00000000 --- a/examples/sim_exit/tests/mem.out +++ /dev/null @@ -1,40 +0,0 @@ - - -Running test 1/8:test_yield_list - -End of test - - -Running test 2/8:test_function_not_a_coroutine - -End of test - - -Running test 3/8:test_coroutine_kill - -End of test - - -Running test 4/8:test_adding_a_coroutine_without_starting - -End of test - - -Running test 5/8:test_function_not_a_coroutine_fork - -End of test - - -Running test 6/8:test_function_reentrant_clock - -End of test - - -Running test 7/8:simple_test - -End of test - - -Running test 8/8:discover_module_values - -End of test diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 00000000..efd7abc3 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,41 @@ +############################################################################### +# Copyright (c) 2015 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +COCOTB?=$(shell pwd)/../ +export COCOTB + +REGRESSIONS := $(shell ls test_cases/) + +.PHONY: $(REGRESSIONS) + +all: $(REGRESSIONS) + +$(REGRESSIONS): + cd test_cases/$@ && $(MAKE) + +clean: + $(foreach TEST, $(REGRESSIONS), $(MAKE) -C test_cases/$(TEST) clean;) diff --git a/examples/sim_exit/tests/Makefile b/tests/designs/close_module/Makefile similarity index 96% rename from examples/sim_exit/tests/Makefile rename to tests/designs/close_module/Makefile index 41a4c31f..2f8d12ac 100644 --- a/examples/sim_exit/tests/Makefile +++ b/tests/designs/close_module/Makefile @@ -38,8 +38,7 @@ endif PWD=$(shell pwd) COCOTB=$(PWD)/../../.. -VERILOG_SOURCES = $(WPWD)/../hdl/close_module.v -MODULE=test_closedown +VERILOG_SOURCES = $(COCOTB)/tests/designs/close_module/close_module.v ifneq ($(SIM),vcs) include $(COCOTB)/makefiles/Makefile.inc diff --git a/examples/sim_exit/hdl/close_module.v b/tests/designs/close_module/close_module.v similarity index 100% rename from examples/sim_exit/hdl/close_module.v rename to tests/designs/close_module/close_module.v diff --git a/examples/plusargs/Makefile b/tests/designs/plusargs_module/Makefile similarity index 95% rename from examples/plusargs/Makefile rename to tests/designs/plusargs_module/Makefile index c4c69e95..4b709f7e 100644 --- a/examples/plusargs/Makefile +++ b/tests/designs/plusargs_module/Makefile @@ -36,10 +36,7 @@ else PWD=$(shell pwd) endif -VERILOG_SOURCES = $(PWD)/tb_top.v -MODULE=plusargs - -PLUSARGS=+foo=bar +test1 +test2 +options=fubar +VERILOG_SOURCES = $(COCOTB)/tests/designs/plusargs_module/tb_top.v include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim diff --git a/examples/plusargs/tb_top.v b/tests/designs/plusargs_module/tb_top.v similarity index 100% rename from examples/plusargs/tb_top.v rename to tests/designs/plusargs_module/tb_top.v diff --git a/examples/functionality/tests/Makefile b/tests/designs/sample_module/Makefile similarity index 92% rename from examples/functionality/tests/Makefile rename to tests/designs/sample_module/Makefile index e874e3ac..3365f401 100644 --- a/examples/functionality/tests/Makefile +++ b/tests/designs/sample_module/Makefile @@ -41,17 +41,15 @@ WPWD=$(shell pwd) endif ifeq ($(TOPLEVEL_LANG),vhdl) - VHDL_SOURCES = $(WPWD)/../hdl/sample_module.vhdl + VHDL_SOURCES = $(COCOTB)/tests/designs/sample_module/sample_module.vhdl TOPLEVEL := $(TOPLEVEL) GPI_IMPL := vhpi else - VERILOG_SOURCES = $(WPWD)/../hdl/sample_module.v + VERILOG_SOURCES = $(COCOTB)/tests/designs/sample_module/sample_module.v GPI_IMPL := vpi endif export TOPLEVEL_LANG -MODULE ?= test_cocotb,test_discovery,test_external,test_regression - include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim diff --git a/examples/functionality/hdl/sample_module.v b/tests/designs/sample_module/sample_module.v similarity index 100% rename from examples/functionality/hdl/sample_module.v rename to tests/designs/sample_module/sample_module.v diff --git a/examples/functionality/hdl/sample_module.vhdl b/tests/designs/sample_module/sample_module.vhdl similarity index 100% rename from examples/functionality/hdl/sample_module.vhdl rename to tests/designs/sample_module/sample_module.vhdl diff --git a/tests/test_cases/issue_120/Makefile b/tests/test_cases/issue_120/Makefile new file mode 100644 index 00000000..003e1637 --- /dev/null +++ b/tests/test_cases/issue_120/Makefile @@ -0,0 +1,33 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +include $(COCOTB)/tests/designs/sample_module/Makefile + +MODULE = issue_120 diff --git a/examples/functionality/tests/test_regression.py b/tests/test_cases/issue_120/issue_120.py similarity index 59% rename from examples/functionality/tests/test_regression.py rename to tests/test_cases/issue_120/issue_120.py index 381edf92..6441967f 100644 --- a/examples/functionality/tests/test_regression.py +++ b/tests/test_cases/issue_120/issue_120.py @@ -46,26 +46,3 @@ def issue_120_scheduling(dut): dut.stream_in_valid = 0 yield RisingEdge(dut.clk) - - -@cocotb.test(skip=True) -def issue_142_overflow_error(dut): - """Tranparently convert ints too long to pass - through the GPI interface natively into BinaryValues""" - cocotb.fork(Clock(dut.clk, 2500).start()) - - def _compare(value): - if int(dut.stream_in_data_wide.value) != int(value): - raise TestFailure("Expecting 0x%x but got 0x%x on %s" % ( - int(value), int(dut.stream_in_data_wide.value), - str(dut.stream_in_data_wide))) - - # Wider values are transparently converted to BinaryValues - for value in [0, 0x7FFFFFFF, 0x7FFFFFFFFFFF, BinaryValue(0x7FFFFFFFFFFFFF)]: - - dut.stream_in_data_wide <= value - yield RisingEdge(dut.clk) - _compare(value) - dut.stream_in_data_wide = value - yield RisingEdge(dut.clk) - _compare(value) diff --git a/tests/test_cases/issue_142/Makefile b/tests/test_cases/issue_142/Makefile new file mode 100644 index 00000000..25d08d19 --- /dev/null +++ b/tests/test_cases/issue_142/Makefile @@ -0,0 +1,31 @@ +############################################################################### +# Copyright (c) 2015 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +include $(COCOTB)/tests/designs/sample_module/Makefile + +MODULE = issue_142 diff --git a/tests/test_cases/issue_142/issue_142.py b/tests/test_cases/issue_142/issue_142.py new file mode 100644 index 00000000..43273ef1 --- /dev/null +++ b/tests/test_cases/issue_142/issue_142.py @@ -0,0 +1,30 @@ +# A set of regression tests for open issues + +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import RisingEdge, Timer, ReadOnly +from cocotb.result import TestFailure +from cocotb.binary import BinaryValue + + +@cocotb.test() +def issue_142_overflow_error(dut): + """Tranparently convert ints too long to pass + through the GPI interface natively into BinaryValues""" + cocotb.fork(Clock(dut.clk, 2500).start()) + + def _compare(value): + if int(dut.stream_in_data_wide.value) != int(value): + raise TestFailure("Expecting 0x%x but got 0x%x on %s" % ( + int(value), int(dut.stream_in_data_wide.value), + str(dut.stream_in_data_wide))) + + # Wider values are transparently converted to BinaryValues + for value in [0, 0x7FFFFFFF, 0x7FFFFFFFFFFF, BinaryValue(0x7FFFFFFFFFFFFF)]: + + dut.stream_in_data_wide <= value + yield RisingEdge(dut.clk) + _compare(value) + dut.stream_in_data_wide = value + yield RisingEdge(dut.clk) + _compare(value) diff --git a/tests/test_cases/test_closedown/Makefile b/tests/test_cases/test_closedown/Makefile new file mode 100644 index 00000000..e4b4d813 --- /dev/null +++ b/tests/test_cases/test_closedown/Makefile @@ -0,0 +1,30 @@ +############################################################################### +# Copyright (c) 2015 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +include $(COCOTB)/tests/designs/close_module/Makefile + +MODULE=test_closedown diff --git a/examples/sim_exit/tests/test_closedown.py b/tests/test_cases/test_closedown/test_closedown.py similarity index 100% rename from examples/sim_exit/tests/test_closedown.py rename to tests/test_cases/test_closedown/test_closedown.py diff --git a/tests/test_cases/test_cocotb/Makefile b/tests/test_cases/test_cocotb/Makefile new file mode 100644 index 00000000..d2dcdc3a --- /dev/null +++ b/tests/test_cases/test_cocotb/Makefile @@ -0,0 +1,33 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +include $(COCOTB)/tests/designs/sample_module/Makefile + +MODULE = test_cocotb diff --git a/examples/functionality/tests/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py similarity index 100% rename from examples/functionality/tests/test_cocotb.py rename to tests/test_cases/test_cocotb/test_cocotb.py diff --git a/tests/test_cases/test_discovery/Makefile b/tests/test_cases/test_discovery/Makefile new file mode 100644 index 00000000..f44fff6a --- /dev/null +++ b/tests/test_cases/test_discovery/Makefile @@ -0,0 +1,31 @@ +############################################################################### +# Copyright (c) 2015 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +include $(COCOTB)/tests/designs/sample_module/Makefile + +MODULE = test_discovery diff --git a/examples/functionality/tests/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py similarity index 100% rename from examples/functionality/tests/test_discovery.py rename to tests/test_cases/test_discovery/test_discovery.py diff --git a/tests/test_cases/test_exit_error/Makefile b/tests/test_cases/test_exit_error/Makefile new file mode 100644 index 00000000..364dac1f --- /dev/null +++ b/tests/test_cases/test_exit_error/Makefile @@ -0,0 +1,31 @@ +############################################################################### +# Copyright (c) 2015 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +include $(COCOTB)/tests/designs/sample_module/Makefile + +MODULE = test_exit diff --git a/tests/test_cases/test_exit_error/test_exit.py b/tests/test_cases/test_exit_error/test_exit.py new file mode 100644 index 00000000..9b65eacb --- /dev/null +++ b/tests/test_cases/test_exit_error/test_exit.py @@ -0,0 +1,14 @@ +# A set of regression tests for open issues + +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import RisingEdge, Timer, ReadOnly +from cocotb.result import TestFailure +from cocotb.binary import BinaryValue + +# This will cause the sim to exit but we want to to do this nicely +# If thi was in another module then the remaining tests would also fail + +@cocotb.test(expect_error=True) +def typosyntax_error(): + yield Timer(100)a diff --git a/tests/test_cases/test_external/Makefile b/tests/test_cases/test_external/Makefile new file mode 100644 index 00000000..b178eb83 --- /dev/null +++ b/tests/test_cases/test_external/Makefile @@ -0,0 +1,33 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +include $(COCOTB)/tests/designs/sample_module/Makefile + +MODULE = test_external diff --git a/examples/functionality/tests/test_external.py b/tests/test_cases/test_external/test_external.py similarity index 100% rename from examples/functionality/tests/test_external.py rename to tests/test_cases/test_external/test_external.py diff --git a/tests/test_cases/test_plusargs/Makefile b/tests/test_cases/test_plusargs/Makefile new file mode 100644 index 00000000..a1688744 --- /dev/null +++ b/tests/test_cases/test_plusargs/Makefile @@ -0,0 +1,34 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +include $(COCOTB)/tests/designs/plusargs_module/Makefile + +PLUSARGS=+foo=bar +test1 +test2 +options=fubar + +MODULE = plusargs diff --git a/examples/plusargs/plusargs.py b/tests/test_cases/test_plusargs/plusargs.py similarity index 100% rename from examples/plusargs/plusargs.py rename to tests/test_cases/test_plusargs/plusargs.py From fe706b05b76d6d84704e55a0cdd2deb8ede290ea Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 24 Jul 2015 15:26:38 +0100 Subject: [PATCH 0866/2656] Issue #257: Enable a global override to the logging level Before a test can be added to test the log levels so we catch an error like this in future we need a way to set the global level. Since the logging is inherited from parents we only need to set on the 'cocotb' logger. But there was not one. The highest level was cocotb.gpi and others where at the same level. So I have added a 'cocotb' logger that we can inherit from and also a loggpi=SimLog('cocotb.gpi') and changed gpi_embed to obtain a reference to this logger. --- cocotb/__init__.py | 12 +++++++++++- cocotb/log.py | 11 ++++++++++- lib/embed/gpi_embed.c | 4 ++-- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index efe13e32..df5d11b7 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -57,7 +57,17 @@ if "SPHINX_BUILD" not in os.environ: logging.basicConfig() logging.setLoggerClass(SimBaseLog) - log = SimLog('cocotb.gpi') + log = SimLog('cocotb') + _default_log = logging.INFO + if "COCOTB_LOG_LEVEL" in os.environ: + level = os.getenv("COCOTB_LOG_LEVEL") + try: + _default_log = getattr(logging, level) + except AttributeError as e: + pass + log.setLevel(_default_log) + loggpi = SimLog('cocotb.gpi') + scheduler = Scheduler() regression = None diff --git a/cocotb/log.py b/cocotb/log.py index 4900c489..ae737374 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -49,6 +49,15 @@ _LINENO_CHARS = 4 # noqa _FUNCNAME_CHARS = 31 # noqa +#if "COCOTB_LOG_LEVEL" in os.environ: +# _default_log = logging.INFO +# level = os.getenv("COCOTB_LOG_LEVEL") +# try: +# _default_log = getattr(logging, level) +# except AttributeError as e: +# pass +#else: +# _default_log = logging.INFO class SimBaseLog(logging.getLoggerClass()): def __init__(self, name): @@ -69,7 +78,7 @@ def __init__(self, name): self.propagate = False logging.__init__(name) self.addHandler(hdlr) - self.setLevel(logging.INFO) + self.setLevel(logging.NOTSET) """ Need to play with this to get the path of the called back, construct our own makeRecord for this """ diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 9e87df60..47f7d927 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -165,8 +165,8 @@ int embed_sim_init(gpi_sim_info_t *info) if (get_module_ref(COCOTB_MODULE, &cocotb_module)) goto cleanup; - // Create a logger object - simlog_obj = PyObject_GetAttrString(cocotb_module, "log"); + // Obtain the loggpi logger object + simlog_obj = PyObject_GetAttrString(cocotb_module, "loggpi"); if (simlog_obj == NULL) { PyErr_Print(); From 7ec488b682b55912a2a4bff1967275fb9e57f77d Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 24 Jul 2015 15:31:50 +0100 Subject: [PATCH 0867/2656] Issue #257: Remove some code I was playing with --- cocotb/log.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/cocotb/log.py b/cocotb/log.py index ae737374..9a19eba7 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -49,16 +49,6 @@ _LINENO_CHARS = 4 # noqa _FUNCNAME_CHARS = 31 # noqa -#if "COCOTB_LOG_LEVEL" in os.environ: -# _default_log = logging.INFO -# level = os.getenv("COCOTB_LOG_LEVEL") -# try: -# _default_log = getattr(logging, level) -# except AttributeError as e: -# pass -#else: -# _default_log = logging.INFO - class SimBaseLog(logging.getLoggerClass()): def __init__(self, name): hdlr = logging.StreamHandler(sys.stdout) From 886bfb300c13655b9020ac3187cbd7ef081cee3e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 24 Jul 2015 15:44:31 +0100 Subject: [PATCH 0868/2656] Issue #257: Use default value when getting environment var and pring message if level no found in logging class --- cocotb/__init__.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index df5d11b7..2e9c52bc 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -58,13 +58,12 @@ logging.basicConfig() logging.setLoggerClass(SimBaseLog) log = SimLog('cocotb') - _default_log = logging.INFO - if "COCOTB_LOG_LEVEL" in os.environ: - level = os.getenv("COCOTB_LOG_LEVEL") - try: - _default_log = getattr(logging, level) - except AttributeError as e: - pass + level = os.getenv("COCOTB_LOG_LEVEL", "INFO") + try: + _default_log = getattr(logging, level) + except AttributeError as e: + log.error("Unable to set loging level to %s" % level) + _default_log = logging.INFO log.setLevel(_default_log) loggpi = SimLog('cocotb.gpi') From 1d7cb0d33833fb38ca62d80ae6c3fd92f2fa2106 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 24 Jul 2015 17:34:20 +0100 Subject: [PATCH 0869/2656] Issue #17: Pull in a more complex design In order to work on iteration over handles we need a more complex design. Have chosen to pull one in from opencores that is under LGPL http://opencores.org/project,viterbi_decoder_axi4s Also a fix to the aldec makefile --- makefiles/simulators/Makefile.aldec | 4 +- tests/designs/viterbi_decoder_axi4s/Makefile | 68 +++ .../designs/viterbi_decoder_axi4s/gpl-2.0.txt | 339 +++++++++++++ .../packages/pkg_components.vhd | 194 +++++++ .../packages/pkg_helper.vhd | 58 +++ .../packages/pkg_param.vhd | 78 +++ .../packages/pkg_param_derived.vhd | 73 +++ .../packages/pkg_trellis.vhd | 185 +++++++ .../packages/pkg_types.vhd | 37 ++ .../designs/viterbi_decoder_axi4s/src/acs.vhd | 134 +++++ .../src/axi4s_buffer.vhd | 130 +++++ .../src/branch_distance.vhd | 111 ++++ .../viterbi_decoder_axi4s/src/dec_viterbi.vhd | 399 +++++++++++++++ .../src/generic_sp_ram.vhd | 91 ++++ .../viterbi_decoder_axi4s/src/ram_ctrl.vhd | 474 ++++++++++++++++++ .../viterbi_decoder_axi4s/src/recursion.vhd | 96 ++++ .../viterbi_decoder_axi4s/src/reorder.vhd | 129 +++++ .../viterbi_decoder_axi4s/src/traceback.vhd | 101 ++++ tests/test_cases/test_iteration/Makefile | 31 ++ .../test_iteration/test_iteration.py | 38 ++ 20 files changed, 2768 insertions(+), 2 deletions(-) create mode 100644 tests/designs/viterbi_decoder_axi4s/Makefile create mode 100644 tests/designs/viterbi_decoder_axi4s/gpl-2.0.txt create mode 100644 tests/designs/viterbi_decoder_axi4s/packages/pkg_components.vhd create mode 100644 tests/designs/viterbi_decoder_axi4s/packages/pkg_helper.vhd create mode 100644 tests/designs/viterbi_decoder_axi4s/packages/pkg_param.vhd create mode 100644 tests/designs/viterbi_decoder_axi4s/packages/pkg_param_derived.vhd create mode 100644 tests/designs/viterbi_decoder_axi4s/packages/pkg_trellis.vhd create mode 100644 tests/designs/viterbi_decoder_axi4s/packages/pkg_types.vhd create mode 100644 tests/designs/viterbi_decoder_axi4s/src/acs.vhd create mode 100644 tests/designs/viterbi_decoder_axi4s/src/axi4s_buffer.vhd create mode 100644 tests/designs/viterbi_decoder_axi4s/src/branch_distance.vhd create mode 100644 tests/designs/viterbi_decoder_axi4s/src/dec_viterbi.vhd create mode 100644 tests/designs/viterbi_decoder_axi4s/src/generic_sp_ram.vhd create mode 100644 tests/designs/viterbi_decoder_axi4s/src/ram_ctrl.vhd create mode 100644 tests/designs/viterbi_decoder_axi4s/src/recursion.vhd create mode 100644 tests/designs/viterbi_decoder_axi4s/src/reorder.vhd create mode 100644 tests/designs/viterbi_decoder_axi4s/src/traceback.vhd create mode 100644 tests/test_cases/test_iteration/Makefile create mode 100644 tests/test_cases/test_iteration/test_iteration.py diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index cbcbaf96..d79eced5 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -63,8 +63,8 @@ endif # Create a TCL script based on the list of $(VERILOG_SOURCES) $(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(VHDL_SOURCES) $(SIM_BUILD) - echo "alib work" > $@ - echo "set worklib work" >> $@ + echo "alib $(RTL_LIBRARY)" > $@ + echo "set worklib $(RTL_LIBRARY)" >> $@ ifdef VHDL_SOURCES echo "acom $(ACOM_ARGS) $(VHDL_SOURCES)" >> $@ endif diff --git a/tests/designs/viterbi_decoder_axi4s/Makefile b/tests/designs/viterbi_decoder_axi4s/Makefile new file mode 100644 index 00000000..83db69eb --- /dev/null +++ b/tests/designs/viterbi_decoder_axi4s/Makefile @@ -0,0 +1,68 @@ +############################################################################### +# Copyright (c) 2015 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +# Override this variable to use a VHDL toplevel instead of SystemVerilog +TOPLEVEL_LANG ?= sv + +# At the moment the only simulator supporting mixed language VHPI/VPI is Aldec +SIM?=aldec + +TOPLEVEL = dec_viterbi + +PWD=$(shell pwd) +COCOTB=$(PWD)/../../.. + +SRC_BASE = $(COCOTB)/tests/designs/viterbi_decoder_axi4s + +RTL_LIBRARY = dec_viterbi + +VHDL_SOURCES = $(SRC_BASE)/packages/pkg_helper.vhd \ + $(SRC_BASE)/packages/pkg_param.vhd \ + $(SRC_BASE)/packages/pkg_param_derived.vhd \ + $(SRC_BASE)/packages/pkg_types.vhd \ + $(SRC_BASE)/packages/pkg_components.vhd \ + $(SRC_BASE)/packages/pkg_trellis.vhd \ + $(SRC_BASE)/src/generic_sp_ram.vhd \ + $(SRC_BASE)/src/axi4s_buffer.vhd \ + $(SRC_BASE)/src/branch_distance.vhd \ + $(SRC_BASE)/src/traceback.vhd \ + $(SRC_BASE)/src/acs.vhd \ + $(SRC_BASE)/src/ram_ctrl.vhd \ + $(SRC_BASE)/src/reorder.vhd \ + $(SRC_BASE)/src/recursion.vhd \ + $(SRC_BASE)/src/dec_viterbi.vhd \ + +ifeq ($(SIM),aldec) + GPI_IMPL=vhpi +endif +ifeq ($(SIM),modelsim) + GPI_IMPL=fli +endif + +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim + diff --git a/tests/designs/viterbi_decoder_axi4s/gpl-2.0.txt b/tests/designs/viterbi_decoder_axi4s/gpl-2.0.txt new file mode 100644 index 00000000..d159169d --- /dev/null +++ b/tests/designs/viterbi_decoder_axi4s/gpl-2.0.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/tests/designs/viterbi_decoder_axi4s/packages/pkg_components.vhd b/tests/designs/viterbi_decoder_axi4s/packages/pkg_components.vhd new file mode 100644 index 00000000..1c0e2215 --- /dev/null +++ b/tests/designs/viterbi_decoder_axi4s/packages/pkg_components.vhd @@ -0,0 +1,194 @@ +--! +--! Copyright (C) 2011 - 2014 Creonic GmbH +--! +--! This file is part of the Creonic Viterbi Decoder, which is distributed +--! under the terms of the GNU General Public License version 2. +--! +--! @file +--! @brief Component declarations for Viterbi decoder +--! @author Markus Fehrenz +--! @date 2011/04/07 +--! +--! + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library dec_viterbi; +use dec_viterbi.pkg_param.all; +use dec_viterbi.pkg_param_derived.all; +use dec_viterbi.pkg_types.all; + +package pkg_components is + + component axi4s_buffer is + generic ( + DATA_WIDTH : natural := 1 + ); + port ( + clk : in std_logic; + rst : in std_logic; + + input : in std_logic_vector(DATA_WIDTH - 1 downto 0); + input_valid : in std_logic; + input_last : in std_logic; + input_accept : out std_logic; + + output : out std_logic_vector(DATA_WIDTH - 1 downto 0); + output_valid : out std_logic; + output_last : out std_logic; + output_accept : in std_logic + ); + end component axi4s_buffer; + + component branch_distance is + generic( + EDGE_WEIGHT : in std_logic_vector(NUMBER_PARITY_BITS - 1 downto 0) + ); + port( + clk : in std_logic; + rst : in std_logic; + + s_axis_input_tvalid : in std_logic; + s_axis_input_tdata : in t_input_block; + s_axis_input_tlast : in std_logic; + s_axis_input_tready : out std_logic; + + m_axis_output_tvalid : out std_logic; + m_axis_output_tdata : out std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); + m_axis_output_tlast : out std_logic; + m_axis_output_tready : in std_logic + + ); + end component branch_distance; + + component acs is + generic( + initialize_value : in signed(BW_MAX_PROBABILITY - 1 downto 0) + ); + port( + clk : in std_logic; + rst : in std_logic; + + s_axis_inbranch_tvalid : in std_logic; + s_axis_inbranch_tdata_low : in std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); + s_axis_inbranch_tdata_high : in std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); + s_axis_inbranch_tlast : in std_logic; + s_axis_inbranch_tready : out std_logic; + + s_axis_inprev_tvalid : in std_logic; + s_axis_inprev_tdata_low : in std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); + s_axis_inprev_tdata_high : in std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); + s_axis_inprev_tready : out std_logic; + + m_axis_outprob_tvalid : out std_logic; + m_axis_outprob_tdata : out std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); + m_axis_outprob_tready : in std_logic; + + m_axis_outdec_tvalid : out std_logic; + m_axis_outdec_tdata : out std_logic; + m_axis_outdec_tlast : out std_logic; + m_axis_outdec_tready : in std_logic + ); + end component acs; + + component ram_ctrl is + port( + clk : in std_logic; + rst : in std_logic; + + s_axis_input_tvalid : in std_logic; + s_axis_input_tdata : in std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); + s_axis_input_tlast : in std_logic; + s_axis_input_tready : out std_logic; + + m_axis_output_tvalid : out std_logic_vector(1 downto 0); + m_axis_output_tdata : out t_ram_rd_data; + m_axis_output_tlast : out std_logic_vector(1 downto 0); + m_axis_output_tready : in std_logic_vector(1 downto 0); + m_axis_output_window_tuser : out std_logic_vector(1 downto 0); + m_axis_output_last_tuser : out std_logic_vector(1 downto 0); + + s_axis_ctrl_tvalid : in std_logic; + s_axis_ctrl_tdata : in std_logic_vector(31 downto 0); + s_axis_ctrl_tready : out std_logic + ); + end component ram_ctrl; + + component generic_sp_ram is + generic( + DISTR_RAM : boolean; + WORDS : integer; + BITWIDTH : integer + ); + port( + clk : in std_logic; + rst : in std_logic; + + wen : in std_logic; + en : in std_logic; + + a : in std_logic_vector(BW_MAX_WINDOW_LENGTH - 1 downto 0); + d : in std_logic_vector(BITWIDTH - 1 downto 0 ); + q : out std_logic_vector(BITWIDTH - 1 downto 0) + ); + end component generic_sp_ram; + + component trellis_traceback is + port( + clk : in std_logic; + rst : in std_logic; + + s_axis_input_tvalid : in std_logic; + s_axis_input_tdata : in std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); + s_axis_input_tlast : in std_logic; + s_axis_input_tready : out std_logic; + s_axis_input_window_tuser : in std_logic; + s_axis_input_last_tuser : in std_logic; + + m_axis_output_tvalid : out std_logic; + m_axis_output_tdata : out std_logic; + m_axis_output_tlast : out std_logic; + m_axis_output_last_tuser : out std_logic; + m_axis_output_tready : in std_logic + ); + end component trellis_traceback; + + component reorder is + port( + clk : in std_logic; + rst : in std_logic; + + s_axis_input_tvalid : in std_logic; + s_axis_input_tdata : in std_logic; + s_axis_input_tlast : in std_logic; + s_axis_input_last_tuser : in std_logic; + s_axis_input_tready : out std_logic; + + m_axis_output_tvalid : out std_logic; + m_axis_output_tdata : out std_logic; + m_axis_output_tlast : out std_logic; + m_axis_output_last_tuser : out std_logic; + m_axis_output_tready : in std_logic + ); + end component reorder; + + component recursion is + port( + clk : in std_logic; + rst : in std_logic; + + s_axis_input_tvalid : in std_logic; + s_axis_input_tdata : in std_logic; + s_axis_input_tlast : in std_logic; + s_axis_input_tready : out std_logic; + + m_axis_output_tvalid : out std_logic; + m_axis_output_tdata : out std_logic; + m_axis_output_tlast : out std_logic; + m_axis_output_tready : in std_logic + ); + end component recursion; + +end package pkg_components; diff --git a/tests/designs/viterbi_decoder_axi4s/packages/pkg_helper.vhd b/tests/designs/viterbi_decoder_axi4s/packages/pkg_helper.vhd new file mode 100644 index 00000000..8fe59743 --- /dev/null +++ b/tests/designs/viterbi_decoder_axi4s/packages/pkg_helper.vhd @@ -0,0 +1,58 @@ +--! +--! Copyright (C) 2011 - 2014 Creonic GmbH +--! +--! This file is part of the Creonic Viterbi Decoder, which is distributed +--! under the terms of the GNU General Public License version 2. +--! +--! @file +--! @brief Helper package with useful functions +--! @author Markus Fehrenz +--! @date 2011/12/02 +--! + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +package pkg_helper is + + --! + --! Return the log_2 of an natural value, i.e. the number of bits required + --! to represent this unsigned value. + --! + function no_bits_natural(value_in : natural) return natural; + + --! Return maximum of two input values + function max(value_in_a, value_in_b : natural) return natural; + +end pkg_helper; + + +package body pkg_helper is + + function no_bits_natural(value_in: natural) return natural is + variable v_n_bit : unsigned(31 downto 0); + begin + if value_in = 0 then + return 0; + end if; + v_n_bit := to_unsigned(value_in, 32); + for i in 31 downto 0 loop + if v_n_bit(i) = '1' then + return i + 1; + end if; + end loop; + return 1; + end no_bits_natural; + + function max(value_in_a, value_in_b : natural) return natural is + begin + if value_in_a > value_in_b then + return value_in_a; + else + return value_in_b; + end if; + end function; + +end pkg_helper; diff --git a/tests/designs/viterbi_decoder_axi4s/packages/pkg_param.vhd b/tests/designs/viterbi_decoder_axi4s/packages/pkg_param.vhd new file mode 100644 index 00000000..1bc3f660 --- /dev/null +++ b/tests/designs/viterbi_decoder_axi4s/packages/pkg_param.vhd @@ -0,0 +1,78 @@ +--! +--! Copyright (C) 2011 - 2014 Creonic GmbH +--! +--! This file is part of the Creonic Viterbi Decoder, which is distributed +--! under the terms of the GNU General Public License version 2. +--! +--! @file +--! @brief Parameters +--! @author Markus Fehrenz +--! @date 2011/07/01 +--! +--! @details This is the configuration file of the Viterbi decoder. +--! Any changes for parameters should be done here. +--! Changing parameters somewhere else may result in a malicious +--! behavior. +--! + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +package pkg_param is + ----------------------------------- + -- Convolutional Code Parameters -- + ----------------------------------- + + + -- + -- Set the number of parity values + -- This has to correspond to PARITY_POLYNOMIALS + -- + constant NUMBER_PARITY_BITS : natural := 2; + type t_parity is array (NUMBER_PARITY_BITS - 1 downto 0) of natural; + + -- + -- Set parity polynoms in decimal notation + -- NUMBER_PARITY_BITS has to correspond to the number of elements + -- Examples: WiFi : [121,91] or [121,91,101] + -- CDMA : [491,369] or [367,435,369] or [501,441,331,315] + -- GSM : [27,19] or [27,21,31] + -- DAB : [91,121,101,91] + -- WiMAX: [91,121,117] + -- + constant PARITY_POLYNOMIALS : t_parity := (121,91); + + + -- + -- Set a recursive polynomial + -- Set to 0 if no recursion is used + -- Setting this arbitrary may result in a worse error correction ability + -- + constant FEEDBACK_POLYNOMIAL : natural := 0; + + + ----------------------------- + -- Architecture Parameters -- + ----------------------------- + + -- + -- Set bit width of LLR input + -- Recommended values: 3 or 4 + -- + constant BW_LLR_INPUT : natural := 4; + + -- + -- Set the maximum window length which shall be allowed at runtime. + -- Recommended: at least 6 * constraint length + -- + constant MAX_WINDOW_LENGTH : natural := 96; + + -- + -- Set to 'true' if distributed RAM shall be used + -- Set to 'false' if block RAM shall be used + -- + constant DISTRIBUTED_RAM : boolean := true; + +end package pkg_param; diff --git a/tests/designs/viterbi_decoder_axi4s/packages/pkg_param_derived.vhd b/tests/designs/viterbi_decoder_axi4s/packages/pkg_param_derived.vhd new file mode 100644 index 00000000..f6e77cad --- /dev/null +++ b/tests/designs/viterbi_decoder_axi4s/packages/pkg_param_derived.vhd @@ -0,0 +1,73 @@ +--! +--! Copyright (C) 2011 - 2014 Creonic GmbH +--! +--! This file is part of the Creonic Viterbi Decoder, which is distributed +--! under the terms of the GNU General Public License version 2. +--! +--! @file +--! @brief Derived parameters +--! @author Markus Fehrenz +--! @date 2011/07/04 +--! +--! @details This constants are derived from constants defined in pkg_param. +--! In order to prevent errors, there is no user choice for these parameters. +--! + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library dec_viterbi; +use dec_viterbi.pkg_param.all; +use dec_viterbi.pkg_helper.all; + + +package pkg_param_derived is + + -- Calculation of constraint length. + function calc_constraint_length return natural; + + -- Memory depth of the encoder shift register. + constant ENCODER_MEMORY_DEPTH : natural; + + -- Number of trellis states corresponds to the nubmer of ACS units. + constant NUMBER_TRELLIS_STATES : natural; + + -- Number of branch units for a single polynomial set + constant NUMBER_BRANCH_UNITS : natural; + + -- Bitwidth constants are needed for type conversions + constant BW_TRELLIS_STATES : natural; + constant BW_MAX_WINDOW_LENGTH : natural; + constant BW_BRANCH_RESULT : natural; + constant BW_MAX_PROBABILITY : natural; + +end package pkg_param_derived; + + +package body pkg_param_derived is + + function calc_constraint_length return natural is + variable v_maximum : natural := 0; + begin + + -- Find the position of the leftmost bit in the polynomials. + for i in NUMBER_PARITY_BITS - 1 downto 0 loop + v_maximum := max(v_maximum, no_bits_natural(PARITY_POLYNOMIALS(i))); + end loop; + v_maximum := max(v_maximum, no_bits_natural(FEEDBACK_POLYNOMIAL)); + return v_maximum; + end function calc_constraint_length; + + + constant ENCODER_MEMORY_DEPTH : natural := calc_constraint_length - 1; + + constant NUMBER_TRELLIS_STATES : natural := 2 ** ENCODER_MEMORY_DEPTH; + + constant NUMBER_BRANCH_UNITS : natural := 2 ** NUMBER_PARITY_BITS; + + constant BW_TRELLIS_STATES : natural := no_bits_natural(NUMBER_TRELLIS_STATES - 1); + constant BW_MAX_WINDOW_LENGTH : natural := no_bits_natural(MAX_WINDOW_LENGTH - 1); + constant BW_BRANCH_RESULT : natural := no_bits_natural((2 ** (BW_LLR_INPUT - 1)) * NUMBER_PARITY_BITS) + 1; + constant BW_MAX_PROBABILITY : natural := no_bits_natural(((2 ** (BW_LLR_INPUT - 1)) * NUMBER_PARITY_BITS) * 4 * ENCODER_MEMORY_DEPTH); +end package body pkg_param_derived; diff --git a/tests/designs/viterbi_decoder_axi4s/packages/pkg_trellis.vhd b/tests/designs/viterbi_decoder_axi4s/packages/pkg_trellis.vhd new file mode 100644 index 00000000..a797d3ac --- /dev/null +++ b/tests/designs/viterbi_decoder_axi4s/packages/pkg_trellis.vhd @@ -0,0 +1,185 @@ +--! +--! Copyright (C) 2011 - 2014 Creonic GmbH +--! +--! This file is part of the Creonic Viterbi Decoder, which is distributed +--! under the terms of the GNU General Public License version 2. +--! +--! @file +--! @brief Trellis parameter calculations (e.g., transitions, init values). +--! @author Markus Fehrenz +--! @date 2011/07/27 +--! +--! + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library dec_viterbi; +use dec_viterbi.pkg_param.all; +use dec_viterbi.pkg_param_derived.all; +use dec_viterbi.pkg_types.all; + + +package pkg_trellis is + + type t_prev_base is array (1 downto 0) of std_logic_vector(BW_TRELLIS_STATES - 1 downto 0); + type t_previous_states is array (NUMBER_TRELLIS_STATES - 1 downto 0) of t_prev_base; + + type t_trans_base is array (1 downto 0) of std_logic_vector(NUMBER_PARITY_BITS - 1 downto 0); + type t_transitions is array (NUMBER_TRELLIS_STATES - 1 downto 0) of t_trans_base; + + type t_trans_base_signed is array (1 downto 0) of std_logic_vector(NUMBER_PARITY_BITS downto 0); + type t_transitions_signed is array (NUMBER_TRELLIS_STATES - 1 downto 0) of t_trans_base_signed; + + + -- + -- This function calculates the previous states of each state. + -- The values are used to connect the ACS units. + -- + function calc_previous_states return t_previous_states; + + + -- + -- This function calculates corresponding transitions to a trellis sate. + -- The values are used to connect branch units to ACS units. + -- + function calc_transitions return t_transitions; + + + -- + -- This function calculates the initialization values for trellis metrics. + -- The values are used as a constant and written to the ACS unit, every time a new block arrives. + -- + function calc_initialize return t_node_s; + + constant PREVIOUS_STATES : t_previous_states; + constant TRANSITIONS : t_transitions; + constant INITIALIZE_TRELLIS : t_node_s; + +end package pkg_trellis; + + +package body pkg_trellis is + + + function calc_previous_states return t_previous_states is + variable v_prev_states : t_previous_states := (others=>(others=>(others => '0'))); + variable v_state0, v_state1 : std_logic_vector(BW_TRELLIS_STATES - 1 downto 0); + begin + for i in NUMBER_TRELLIS_STATES - 1 downto 0 loop + v_state0 := std_logic_vector(to_unsigned(i,BW_TRELLIS_STATES)); + v_state1 := v_state0(BW_TRELLIS_STATES - 2 downto 0) & '0'; + v_prev_states(i)(0) := v_state1; + v_state1 := v_state0(BW_TRELLIS_STATES - 2 downto 0) & '1'; + v_prev_states(i)(1) := v_state1; + end loop; + return v_prev_states; + end function calc_previous_states; + + + function calc_transitions return t_transitions is + variable v_transitions : t_transitions_signed := (others => (others => (others => '0'))); + variable v_transitions_out : t_transitions := (others => (others => (others => '0'))); + variable v_one_transition : std_logic_vector(NUMBER_PARITY_BITS - 1 downto 0); + variable v_next_state : unsigned(ENCODER_MEMORY_DEPTH - 1 downto 0) := (others => '0'); + variable v_state, v_states : unsigned(ENCODER_MEMORY_DEPTH downto 0); + variable v_bit : std_logic := '0'; + begin + + -- + -- It is possible to reduce code size at this stage, if feedback is handled differently, + -- but the complexity will increase. + -- + + for i in NUMBER_TRELLIS_STATES - 1 downto 0 loop + + -- + -- for input : 0 + -- determine correct input with feedback + -- + v_next_state := to_unsigned(i,ENCODER_MEMORY_DEPTH) and to_unsigned(FEEDBACK_POLYNOMIAL, ENCODER_MEMORY_DEPTH); + for k in ENCODER_MEMORY_DEPTH - 1 downto 0 loop + v_bit := v_bit xor v_next_state(k); + end loop; + v_state(ENCODER_MEMORY_DEPTH) := v_bit; + v_state(ENCODER_MEMORY_DEPTH - 1 downto 0) := to_unsigned(i,ENCODER_MEMORY_DEPTH); + v_next_state := v_state(ENCODER_MEMORY_DEPTH downto 1); + v_bit := '0'; + + -- determine paritybits + for j in NUMBER_PARITY_BITS - 1 downto 0 loop + v_states := v_state and to_unsigned(PARITY_POLYNOMIALS(j), ENCODER_MEMORY_DEPTH + 1); + for k in ENCODER_MEMORY_DEPTH downto 0 loop + v_bit := v_bit xor v_states(k); + end loop; + v_one_transition(j) := v_bit; + v_bit := '0'; + end loop; + + -- decide where to save the parity result + if v_transitions(to_integer(v_next_state))(1)(NUMBER_PARITY_BITS) = '0' then + v_transitions(to_integer(v_next_state))(1)(NUMBER_PARITY_BITS) := '1'; + v_transitions(to_integer(v_next_state))(1)(NUMBER_PARITY_BITS - 1 downto 0) := v_one_transition; + else + v_transitions(to_integer(v_next_state))(0)(NUMBER_PARITY_BITS - 1 downto 0) := v_one_transition; + end if; + + -- + -- for input: 1 + -- determine correct input with feedback + -- + v_next_state := to_unsigned(i,ENCODER_MEMORY_DEPTH) and to_unsigned(FEEDBACK_POLYNOMIAL, ENCODER_MEMORY_DEPTH); + for k in ENCODER_MEMORY_DEPTH - 1 downto 0 loop + v_bit := v_bit xor v_next_state(k); + end loop; + v_state(ENCODER_MEMORY_DEPTH) := '1' xor v_bit; + v_state(ENCODER_MEMORY_DEPTH - 1 downto 0) := to_unsigned(i,ENCODER_MEMORY_DEPTH); + v_next_state := v_state(ENCODER_MEMORY_DEPTH downto 1); + v_bit := '0'; + + -- determine paritybits + for j in NUMBER_PARITY_BITS - 1 downto 0 loop + v_states := v_state and to_unsigned(PARITY_POLYNOMIALS(j), ENCODER_MEMORY_DEPTH + 1); + for k in ENCODER_MEMORY_DEPTH downto 0 loop + v_bit := v_bit xor v_states(k); + end loop; + v_one_transition(j) := v_bit; + v_bit := '0'; + end loop; + + -- decide where to save parity result + if v_transitions(to_integer(v_next_state))(1)(NUMBER_PARITY_BITS) = '0' then + v_transitions(to_integer(v_next_state))(1)(NUMBER_PARITY_BITS) := '1'; + v_transitions(to_integer(v_next_state))(1)(NUMBER_PARITY_BITS - 1 downto 0) := v_one_transition; + else + v_transitions(to_integer(v_next_state))(0)(NUMBER_PARITY_BITS - 1 downto 0) := v_one_transition; + end if; + end loop; + + -- truncate, the bit, used to decide where to save parity result + for i in NUMBER_TRELLIS_STATES - 1 downto 0 loop + v_transitions_out(i)(1) := v_transitions(i)(1)(NUMBER_PARITY_BITS - 1 downto 0); + v_transitions_out(i)(0) := v_transitions(i)(0)(NUMBER_PARITY_BITS - 1 downto 0); + end loop; + + return v_transitions_out; + end function calc_transitions; + + + function calc_initialize return t_node_s is + variable v_initialize : t_node_s; + begin + v_initialize(0) := to_signed(0, BW_MAX_PROBABILITY); + for i in NUMBER_TRELLIS_STATES - 1 downto 1 loop + v_initialize(i) := to_signed(- 2 ** (BW_MAX_PROBABILITY - 2), BW_MAX_PROBABILITY); + end loop; + return v_initialize; + end function calc_initialize; + + + constant PREVIOUS_STATES : t_previous_states := calc_previous_states; + constant TRANSITIONS : t_transitions := calc_transitions; + constant INITIALIZE_TRELLIS : t_node_s := calc_initialize; + +end package body pkg_trellis; diff --git a/tests/designs/viterbi_decoder_axi4s/packages/pkg_types.vhd b/tests/designs/viterbi_decoder_axi4s/packages/pkg_types.vhd new file mode 100644 index 00000000..5acdd4eb --- /dev/null +++ b/tests/designs/viterbi_decoder_axi4s/packages/pkg_types.vhd @@ -0,0 +1,37 @@ +--! +--! Copyright (C) 2011 - 2014 Creonic GmbH +--! +--! This file is part of the Creonic Viterbi Decoder, which is distributed +--! under the terms of the GNU General Public License version 2. +--! +--! @file +--! @brief Global types for the Viterbi decoder +--! @author Markus Fehrenz +--! @date 2011/07/04 +--! +--! @details Most types are shared and used in different context. +--! Changing single types should be done with adding an additional type. +--! + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library dec_viterbi; +use dec_viterbi.pkg_param.all; +use dec_viterbi.pkg_param_derived.all; + +package pkg_types is + + -- Parity structure: p1_bit, p2_bit, ..., pN_bit + type t_input_block is array (NUMBER_PARITY_BITS - 1 downto 0) of signed(BW_LLR_INPUT - 1 downto 0); + + -- Types are used for bulk information to ACS and branch unit. + type t_node_s is array (NUMBER_TRELLIS_STATES - 1 downto 0) of signed(BW_MAX_PROBABILITY - 1 downto 0); + type t_node is array (NUMBER_TRELLIS_STATES - 1 downto 0) of std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); + type t_branch is array (NUMBER_BRANCH_UNITS - 1 downto 0) of std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); + + -- RAM Data + type t_ram_rd_data is array (1 downto 0) of std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); + +end package pkg_types; diff --git a/tests/designs/viterbi_decoder_axi4s/src/acs.vhd b/tests/designs/viterbi_decoder_axi4s/src/acs.vhd new file mode 100644 index 00000000..958050a3 --- /dev/null +++ b/tests/designs/viterbi_decoder_axi4s/src/acs.vhd @@ -0,0 +1,134 @@ +--! +--! Copyright (C) 2011 - 2014 Creonic GmbH +--! +--! This file is part of the Creonic Viterbi Decoder, which is distributed +--! under the terms of the GNU General Public License version 2. +--! +--! @file +--! @brief Add-compare-select unit for trellis processing. +--! @author Markus Fehrenz +--! @date 2011/07/04 +--! +--! @details The ACS decides which path is the the surviving trellis path. +--! In the design there are 2^{K-1} ACS instances. +--! + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library dec_viterbi; +use dec_viterbi.pkg_param.all; +use dec_viterbi.pkg_param_derived.all; +use dec_viterbi.pkg_types.all; +use dec_viterbi.pkg_helper.all; + + +entity acs is + generic( + + -- Reset value + INITIALIZE_VALUE : in signed(BW_MAX_PROBABILITY - 1 downto 0) + ); + port( + clk : in std_logic; + rst : in std_logic; + + -- + -- Values from branch distance, signed values in std_logic_vector + -- high is located in the upper half. + -- + s_axis_inbranch_tvalid : in std_logic; + s_axis_inbranch_tdata_low : in std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); + s_axis_inbranch_tdata_high : in std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); + s_axis_inbranch_tlast : in std_logic; + s_axis_inbranch_tready : out std_logic; + + -- + -- Probabilities from previous nodes, signed values in std_logic_vector + -- high is located in the upper half. + -- + s_axis_inprev_tvalid : in std_logic; + s_axis_inprev_tdata_low : in std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); + s_axis_inprev_tdata_high : in std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); + s_axis_inprev_tready : out std_logic; + + -- probability result of the add compare and select + m_axis_outprob_tvalid : out std_logic; + m_axis_outprob_tdata : out std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); + m_axis_outprob_tready : in std_logic; + + -- decision result of the add compare and select + m_axis_outdec_tvalid : out std_logic; + m_axis_outdec_tdata : out std_logic; + m_axis_outdec_tlast : out std_logic; + m_axis_outdec_tready : in std_logic + ); +end entity acs; + + +architecture rtl of acs is + + signal s_axis_inbranch_tlast_d : std_logic; + signal m_axis_outdec_tvalid_int : std_logic; + signal s_axis_inbranch_tready_int : std_logic; + +begin + + s_axis_inbranch_tready_int <= '1' when m_axis_outdec_tready = '1' or m_axis_outdec_tvalid_int = '0' else + '0'; + s_axis_inbranch_tready <= s_axis_inbranch_tready_int; + m_axis_outdec_tvalid <= m_axis_outdec_tvalid_int; + + -- Add branch to previous, compare both paths and select survivor. + pr_add_compare : process(clk) is + variable v_diff, v_high, v_low : signed(BW_MAX_PROBABILITY - 1 downto 0); + begin + if rising_edge(clk) then + if rst = '1' then + m_axis_outdec_tvalid_int <= '0'; + m_axis_outdec_tdata <= '0'; + m_axis_outdec_tlast <= '0'; + m_axis_outprob_tvalid <= '0'; + s_axis_inprev_tready <= '0'; + s_axis_inbranch_tlast_d <= '0'; + m_axis_outprob_tdata <= std_logic_vector(INITIALIZE_VALUE); + else + -- If this is the last value, prepare for processing of next incoming value. + if s_axis_inbranch_tlast_d = '1' then + m_axis_outprob_tdata <= std_logic_vector(INITIALIZE_VALUE); + s_axis_inbranch_tlast_d <= '0'; + m_axis_outdec_tvalid_int <= '0'; + end if; + if m_axis_outdec_tvalid_int = '1' and m_axis_outdec_tready = '1' then + m_axis_outdec_tvalid_int <= '0'; + end if; + + -- Process only if we receive valid data. + if s_axis_inbranch_tvalid = '1' and s_axis_inbranch_tready_int = '1' then + s_axis_inbranch_tlast_d <= s_axis_inbranch_tlast; + + -- Add. + v_low := signed(s_axis_inbranch_tdata_low) + signed(s_axis_inprev_tdata_low); + v_high := signed(s_axis_inbranch_tdata_high) + signed(s_axis_inprev_tdata_high); + + -- Use modulo normalization, do not extend the sign here! + v_diff := v_low - v_high; + + -- Compare, select the correct path. + if v_diff < 0 then + m_axis_outdec_tdata <= '1'; + m_axis_outprob_tdata <= std_logic_vector(v_high); + else + m_axis_outdec_tdata <= '0'; + m_axis_outprob_tdata <= std_logic_vector(v_low); + end if; + m_axis_outdec_tvalid_int <= '1'; + end if; + + m_axis_outdec_tlast <= s_axis_inbranch_tlast; + end if; + end if; + end process pr_add_compare; + +end architecture rtl; diff --git a/tests/designs/viterbi_decoder_axi4s/src/axi4s_buffer.vhd b/tests/designs/viterbi_decoder_axi4s/src/axi4s_buffer.vhd new file mode 100644 index 00000000..8e479162 --- /dev/null +++ b/tests/designs/viterbi_decoder_axi4s/src/axi4s_buffer.vhd @@ -0,0 +1,130 @@ +--! +--! Copyright (C) 2012 - 2014 Creonic GmbH +--! +--! This file is part of the Creonic Viterbi Decoder, which is distributed +--! under the terms of the GNU General Public License version 2. +--! +--! @file +--! @brief AXI4-Stream buffer that allows to buffer the accept-signal. +--! @author Matthias Alles +--! @date 2012/04/18 +--! +--! @details +--! One problem when concatenating multiple AXI4-Stream builind blocks is that +--! the accept signal has to pass from the very last component to the input +--! of the very first component. Only then it is possible to have an interruption +--! free data processing within the whole chain. The drawback of this approach is +--! that the accept signal has a long path and high fanouts. +--! This entity allows to use registers on the accept signals by introducing buffers +--! for storing the input values. It should improve timing of bigger building blocks. +--! + +library ieee; +use ieee.std_logic_1164.all; + + +entity axi4s_buffer is + generic ( + DATA_WIDTH : natural := 1 + ); + port ( + + clk : in std_logic; + rst : in std_logic; + + -- Input data handling + ---------------------- + + input : in std_logic_vector(DATA_WIDTH - 1 downto 0); + input_valid : in std_logic; + input_last : in std_logic; + input_accept : out std_logic; + + + -- Output data handling + ----------------------- + output : out std_logic_vector(DATA_WIDTH - 1 downto 0); + output_valid : out std_logic; + output_last : out std_logic; + output_accept : in std_logic +); +end entity axi4s_buffer; + + +architecture rtl of axi4s_buffer is + + + signal input_accept_int : std_logic; + + signal output_reg : std_logic_vector(DATA_WIDTH - 1 downto 0); + signal output_last_reg : std_logic; + signal output_valid_reg : std_logic; + + signal buffer_full : std_logic; + signal buffer_data : std_logic_vector(DATA_WIDTH - 1 downto 0); + signal buffer_last : std_logic; + +begin + + input_accept <= input_accept_int; + + output <= output_reg; + output_last <= output_last_reg; + output_valid <= output_valid_reg; + + -- + -- This process registers all signals. + -- No combinatorial logic is bypassed from input to output and vice versa. + -- + pr_reg: process(clk) is + begin + if rising_edge(clk) then + if rst = '1' then + output_reg <= (others => '0'); + output_last_reg <= '0'; + output_valid_reg <= '0'; + + input_accept_int <= '1'; + + buffer_full <= '0'; + buffer_data <= (others => '0'); + buffer_last <= '0'; + else + + -- + -- Data is coming, buf output data can't be sent => Store input data in buffer + -- and remove input_accept signal! + -- + if input_valid = '1' and input_accept_int = '1' and output_valid_reg = '1' and output_accept = '0' then + buffer_data <= input; + buffer_last <= input_last; + buffer_full <= '1'; + input_accept_int <= '0'; + end if; + + -- + -- Output data is being read but there is data in the buffer waiting for being sent + -- => Use the buffer data! + -- + if output_accept = '1' and output_valid_reg = '1' and buffer_full = '1' then + output_reg <= buffer_data; + output_last_reg <= buffer_last; + output_valid_reg <= '1'; + buffer_full <= '0'; + input_accept_int <= '1'; + + -- + -- Data is being read and buffer is empty => Use input data directly! + -- Output register is empty => Use input data directly! + -- + elsif (output_accept = '1' and output_valid_reg = '1') or output_valid_reg = '0' then + output_reg <= input; + output_last_reg <= input_last; + output_valid_reg <= input_valid; + end if; + + end if; + end if; + end process pr_reg; + +end architecture rtl; diff --git a/tests/designs/viterbi_decoder_axi4s/src/branch_distance.vhd b/tests/designs/viterbi_decoder_axi4s/src/branch_distance.vhd new file mode 100644 index 00000000..31aa9a4f --- /dev/null +++ b/tests/designs/viterbi_decoder_axi4s/src/branch_distance.vhd @@ -0,0 +1,111 @@ +--! +--! Copyright (C) 2011 - 2014 Creonic GmbH +--! +--! This file is part of the Creonic Viterbi Decoder, which is distributed +--! under the terms of the GNU General Public License version 2. +--! +--! @file +--! @brief Branch distance calculation unit. +--! @author Markus Fehrenz +--! @date 2011/08/04 +--! +--! @details Each branch has to be calculated only once. +--! The branch calculations are configured with a generic. +--! There is no limitation in branch calculations. +--! + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library dec_viterbi; +use dec_viterbi.pkg_param.all; +use dec_viterbi.pkg_param_derived.all; +use dec_viterbi.pkg_types.all; + + +entity branch_distance is + generic( + EDGE_WEIGHT : in std_logic_vector(0 to NUMBER_PARITY_BITS - 1) + ); + port( + clk : in std_logic; + rst : in std_logic; + + -- + -- Input LLR values + -- + s_axis_input_tvalid : in std_logic; + s_axis_input_tdata : in t_input_block; + s_axis_input_tlast : in std_logic; + s_axis_input_tready : out std_logic; + + + -- + -- Output branch metrics, going to ACS unit. + -- + m_axis_output_tvalid : out std_logic; + m_axis_output_tdata : out std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); + m_axis_output_tlast : out std_logic; + m_axis_output_tready : in std_logic + ); +end entity branch_distance; + + +architecture rtl of branch_distance is + + signal m_axis_output_tvalid_int : std_logic; + signal s_axis_input_tready_int : std_logic; + +begin + + -- We are ready, when we are allowed to write to the output, or the output is idle. + s_axis_input_tready_int <= '1' when m_axis_output_tready = '1' else + '0'; + + -- Connect internal versions of signal to output port. + s_axis_input_tready <= s_axis_input_tready_int; + m_axis_output_tvalid <= m_axis_output_tvalid_int; + + + -- Calculation of specific branch distance with a geometric distance function. + pr_branch : process(clk) is + variable v_branch_result : integer; + begin + if rising_edge(clk) then + if rst = '1' then + m_axis_output_tvalid_int <= '0'; + m_axis_output_tdata <= (others => '0'); + m_axis_output_tlast <= '0'; + else + + if m_axis_output_tvalid_int = '1' and m_axis_output_tready = '1' then + m_axis_output_tvalid_int <= '0'; + m_axis_output_tlast <= '0'; + end if; + + if s_axis_input_tready_int = '1' and s_axis_input_tvalid = '1' then + v_branch_result := 0; + + for i in NUMBER_PARITY_BITS - 1 downto 0 loop + + -- + -- Either the value is added or subtracted, depending on + -- the current branch metric we are computing. + -- + if EDGE_WEIGHT(i) = '0' then + v_branch_result := v_branch_result + to_integer(s_axis_input_tdata(i)); + else + v_branch_result := v_branch_result - to_integer(s_axis_input_tdata(i)); + end if; + end loop; + m_axis_output_tdata <= std_logic_vector(to_signed(v_branch_result, BW_BRANCH_RESULT)); + m_axis_output_tvalid_int <= '1'; + m_axis_output_tlast <= s_axis_input_tlast; + end if; + + end if; + end if; + end process pr_branch; + +end architecture rtl; diff --git a/tests/designs/viterbi_decoder_axi4s/src/dec_viterbi.vhd b/tests/designs/viterbi_decoder_axi4s/src/dec_viterbi.vhd new file mode 100644 index 00000000..e5ac98d8 --- /dev/null +++ b/tests/designs/viterbi_decoder_axi4s/src/dec_viterbi.vhd @@ -0,0 +1,399 @@ +--! +--! Copyright (C) 2011 - 2014 Creonic GmbH +--! +--! This file is part of the Creonic Viterbi Decoder, which is distributed +--! under the terms of the GNU General Public License version 2. +--! +--! @file +--! @brief Viterbi decoder top entity, connecting all decoder units. +--! @author Markus Fehrenz +--! @date 2011/12/05 +--! +--! @details +--! The AXI std_logic_vector input is mapped to an internal information type. +--! Further the correct output order is handled. +--! + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library dec_viterbi; +use dec_viterbi.pkg_param.all; +use dec_viterbi.pkg_param_derived.all; +use dec_viterbi.pkg_types.all; +use dec_viterbi.pkg_components.all; +use dec_viterbi.pkg_trellis.all; + + +entity dec_viterbi is + port( + + -- + -- The core only uses AXI4-Stream interfaces, + -- based on AMBA4 AXI4-Stream Protocol with restrictions according to + -- Xilinx Migration, described in Xilinx AXI Reference UG761 (v13.3). + -- + + aclk : in std_logic; + + -- Synchronous reset, active low. + aresetn : in std_logic; + + + -- + -- Slave (input) data signals + -- Delivers the parity LLR values, one byte per LLR value. + -- + s_axis_input_tvalid : in std_logic; + s_axis_input_tdata : in std_logic_vector(31 downto 0); + s_axis_input_tlast : in std_logic; + s_axis_input_tready : out std_logic; + + + -- + -- Master (output) data signals + -- Delivers the decoded systematic (payload) bits. + -- + m_axis_output_tvalid : out std_logic; + m_axis_output_tdata : out std_logic; + m_axis_output_tlast : out std_logic; + m_axis_output_tready : in std_logic; + + + -- + -- Slave (input) configuration signals + -- Configures window length and acquisition length. + -- + s_axis_ctrl_tvalid : in std_logic; + s_axis_ctrl_tdata : in std_logic_vector(31 downto 0); + s_axis_ctrl_tlast : in std_logic; + s_axis_ctrl_tready : out std_logic +); +end entity dec_viterbi; + + +architecture rtl of dec_viterbi is + + alias clk is aclk; + signal rst : std_logic; + + -- split tdata into input array + signal input : t_input_block; + + -- buffer signals + signal buffer_tdata : std_logic_vector(31 downto 0); + signal buffer_tvalid : std_logic; + signal buffer_tlast : std_logic; + + -- branch signals + signal branch_tvalid : std_logic_vector(NUMBER_BRANCH_UNITS - 1 downto 0); + signal branch_tdata : t_branch; + signal branch_tlast : std_logic_vector(NUMBER_BRANCH_UNITS - 1 downto 0); + signal branch_tready : std_logic_vector(NUMBER_BRANCH_UNITS - 1 downto 0); + + -- acs signals + signal acs_tvalid : std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); + signal acs_tlast : std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); + signal acs_tready : std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); + signal acs_dec_tdata : std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); + signal acs_prob_tdata : t_node; + + -- ram signals + signal ram_tready : std_logic; + signal ram_tvalid, ram_tlast, ram_window_tuser, ram_last_tuser : std_logic_vector(1 downto 0); + signal ram_tdata : t_ram_rd_data; + + -- traceback signals + signal traceback_tvalid, traceback_tdata : std_logic_vector(1 downto 0); + signal traceback_tready, traceback_tlast : std_logic_vector(1 downto 0); + signal traceback_last_tuser : std_logic_vector(1 downto 0); + + -- reorder signals + signal reorder_tready, reorder_tvalid : std_logic_vector(1 downto 0); + signal reorder_tdata, reorder_tlast : std_logic_vector(1 downto 0); + signal reorder_last_tuser : std_logic_vector(1 downto 0); + + -- output signals + signal output_tready : std_logic_vector(1 downto 0); + signal current_active : integer range 1 downto 0; + +begin + + -- + -- There is always one byte of data for each LLR value, even though each + -- LLR value is represented with BW_LLR_INPUT bits. Hence, only + -- BW_LLR_INPUT bits are extracted from the byte. + -- + gen_input_assignment: for i in NUMBER_PARITY_BITS - 1 downto 0 generate + begin + input(i) <= signed(buffer_tdata(8 * i + BW_LLR_INPUT - 1 downto 8 * i)); + end generate gen_input_assignment; + + rst <= not aresetn; + + ------------------------------ + --- Portmapping components --- + ------------------------------ + + ------------------------------------- + -- AXI4S input buffer + -------------------------------------- + + inst_axi4s_buffer: axi4s_buffer + generic map( + DATA_WIDTH => 32 + ) + port map( + clk => clk, + rst => rst, + + input => s_axis_input_tdata, + input_valid => s_axis_input_tvalid, + input_last => s_axis_input_tlast, + input_accept => s_axis_input_tready, + + output => buffer_tdata, + output_valid => buffer_tvalid, + output_last => buffer_tlast, + output_accept => branch_tready(0) + ); + + ------------------------------------- + -- Branch metric unit + -------------------------------------- + + gen_branch_distance : for i in NUMBER_BRANCH_UNITS - 1 downto 0 generate + begin + inst_branch_distance : branch_distance + generic map( + EDGE_WEIGHT => std_logic_vector(to_unsigned(i, NUMBER_PARITY_BITS)) + ) + port map( + clk => clk, + rst => rst, + + s_axis_input_tvalid => buffer_tvalid, + s_axis_input_tdata => input, + s_axis_input_tlast => buffer_tlast, + s_axis_input_tready => branch_tready(i), + + m_axis_output_tvalid => branch_tvalid(i), + m_axis_output_tdata => branch_tdata(i), + m_axis_output_tlast => branch_tlast(i), + m_axis_output_tready => acs_tready(0) + ); + end generate gen_branch_distance; + + + ------------------------------------- + -- ACS unit (path metric calculation) + -------------------------------------- + + gen_acs : for i in 0 to NUMBER_TRELLIS_STATES - 1 generate + signal inbranch_tdata_low : std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); + signal inbranch_tdata_high : std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); + signal inprev_tdata_low : std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); + signal inprev_tdata_high : std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); + begin + inbranch_tdata_low <= branch_tdata(to_integer(unsigned(TRANSITIONS(i)(0)))); + inbranch_tdata_high <= branch_tdata(to_integer(unsigned(TRANSITIONS(i)(1)))); + inprev_tdata_low <= acs_prob_tdata(to_integer(unsigned(PREVIOUS_STATES(i)(0)))); + inprev_tdata_high <= acs_prob_tdata(to_integer(unsigned(PREVIOUS_STATES(i)(1)))); + + inst_acs : acs + generic map( + initialize_value => INITIALIZE_TRELLIS(i) + ) + port map( + clk => clk, + rst => rst, + + s_axis_inbranch_tvalid => branch_tvalid(0), + s_axis_inbranch_tdata_low => inbranch_tdata_low, + s_axis_inbranch_tdata_high => inbranch_tdata_high, + s_axis_inbranch_tlast => branch_tlast(0), + s_axis_inbranch_tready => acs_tready(i), + + s_axis_inprev_tvalid => '1', + s_axis_inprev_tdata_low => inprev_tdata_low, + s_axis_inprev_tdata_high => inprev_tdata_high, + s_axis_inprev_tready => open, + + m_axis_outprob_tvalid => open, + m_axis_outprob_tdata => acs_prob_tdata(i), + m_axis_outprob_tready => '1', + + m_axis_outdec_tvalid => acs_tvalid(i), + m_axis_outdec_tdata => acs_dec_tdata(i), + m_axis_outdec_tlast => acs_tlast(i), + m_axis_outdec_tready => ram_tready + ); + end generate gen_acs; + + + ------------------------------- + -- Traceback + ------------------------------- + + inst_ram_ctrl : ram_ctrl + port map ( + clk => clk, + rst => rst, + + s_axis_input_tvalid => acs_tvalid(0), + s_axis_input_tdata => acs_dec_tdata, + s_axis_input_tlast => acs_tlast(0), + s_axis_input_tready => ram_tready, + + m_axis_output_tvalid => ram_tvalid, + m_axis_output_tdata => ram_tdata, + m_axis_output_tlast => ram_tlast, + m_axis_output_tready => traceback_tready, + m_axis_output_window_tuser => ram_window_tuser, + m_axis_output_last_tuser => ram_last_tuser, + + s_axis_ctrl_tvalid => s_axis_ctrl_tvalid, + s_axis_ctrl_tdata => s_axis_ctrl_tdata, + s_axis_ctrl_tready => s_axis_ctrl_tready + ); + + + gen_inst_trellis_traceback : for i in 1 downto 0 generate + begin + inst_trellis_traceback : trellis_traceback + port map( + clk => clk, + rst => rst, + + s_axis_input_tvalid => ram_tvalid(i), + s_axis_input_tdata => ram_tdata(i), + s_axis_input_tlast => ram_tlast(i), + s_axis_input_tready => traceback_tready(i), + s_axis_input_window_tuser => ram_window_tuser(i), + s_axis_input_last_tuser => ram_last_tuser(i), + + m_axis_output_tvalid => traceback_tvalid(i), + m_axis_output_tdata => traceback_tdata(i), + m_axis_output_tlast => traceback_tlast(i), + m_axis_output_last_tuser => traceback_last_tuser(i), + m_axis_output_tready => reorder_tready(i) + ); + end generate gen_inst_trellis_traceback; + + + ------------------------------- + -- Reverse output order + ------------------------------- + + gen_inst_reorder : for i in 1 downto 0 generate + begin + inst_reorder : reorder + port map( + clk => clk, + rst => rst, + + s_axis_input_tvalid => traceback_tvalid(i), + s_axis_input_tdata => traceback_tdata(i), + s_axis_input_tlast => traceback_tlast(i), + s_axis_input_last_tuser => traceback_last_tuser(i), + s_axis_input_tready => reorder_tready(i), + + m_axis_output_tvalid => reorder_tvalid(i), + m_axis_output_tdata => reorder_tdata(i), + m_axis_output_tlast => reorder_tlast(i), + m_axis_output_last_tuser => reorder_last_tuser(i), + m_axis_output_tready => output_tready(i) + ); + end generate gen_inst_reorder; + + + ------------------------------ + -- Recursive codes handling -- + ------------------------------ + + gen_inst_recursion : if FEEDBACK_POLYNOMIAL /= 0 generate + signal reorder_recursion_tvalid : std_logic; + signal reorder_recursion_tdata : std_logic; + signal reorder_recursion_tlast : std_logic; + signal recursion_tready : std_logic; + begin + inst_recursion : recursion + port map( + clk => clk, + rst => rst, + + s_axis_input_tvalid => reorder_recursion_tvalid, + s_axis_input_tdata => reorder_recursion_tdata, + s_axis_input_tlast => reorder_recursion_tlast, + s_axis_input_tready => recursion_tready, + + m_axis_output_tvalid => m_axis_output_tvalid, + m_axis_output_tdata => m_axis_output_tdata, + m_axis_output_tlast => m_axis_output_tlast, + m_axis_output_tready => m_axis_output_tready + ); + + ------------------------------- + -- Output interface handling + ------------------------------- + + reorder_recursion_tvalid <= '1' when reorder_tvalid(0) = '1' or reorder_tvalid(1) = '1' else + '0'; + + reorder_recursion_tdata <= reorder_tdata(0) when current_active = 0 else + reorder_tdata(1); + + reorder_recursion_tlast <= '1' when reorder_tlast(0) = '1' or reorder_tlast(1) = '1' else + '0'; + + output_tready(0) <= '1' when (current_active = 0) and m_axis_output_tready = '1' else + '0'; + output_tready(1) <= '1' when (current_active = 1) and m_axis_output_tready = '1' else + '0'; + end generate gen_inst_recursion; + + + + no_recursion: if FEEDBACK_POLYNOMIAL = 0 generate + + ------------------------------- + -- Output interface handling + ------------------------------- + + m_axis_output_tdata <= reorder_tdata(0) when current_active = 0 else + reorder_tdata(1); + + m_axis_output_tvalid <= '1' when reorder_tvalid(0) = '1' or reorder_tvalid(1) = '1' else + '0'; + + m_axis_output_tlast <= '1' when reorder_tlast(0) = '1' or reorder_tlast(1) = '1' else + '0'; + + output_tready(0) <= '1' when (current_active = 0) and m_axis_output_tready = '1' else + '0'; + output_tready(1) <= '1' when (current_active = 1) and m_axis_output_tready = '1' else + '0'; + end generate no_recursion; + + + recursion : if FEEDBACK_POLYNOMIAL /= 0 generate + begin + end generate recursion; + + + -- Check and merge reordering outputs and block if necessary. + pr_reorder_tready : process(clk) is + begin + if rising_edge(clk) then + if rst = '1' then + current_active <= 0; + else + if reorder_tvalid(current_active) = '1' and m_axis_output_tready = '1' and reorder_last_tuser(current_active) = '1' then + current_active <= 1 - current_active; + end if; + end if; + end if; + end process pr_reorder_tready; + +end architecture rtl; diff --git a/tests/designs/viterbi_decoder_axi4s/src/generic_sp_ram.vhd b/tests/designs/viterbi_decoder_axi4s/src/generic_sp_ram.vhd new file mode 100644 index 00000000..897225c6 --- /dev/null +++ b/tests/designs/viterbi_decoder_axi4s/src/generic_sp_ram.vhd @@ -0,0 +1,91 @@ +--! +--! Copyright (C) 2010 - 2012 Creonic GmbH +--! +--! This file is part of the Creonic Viterbi Decoder, which is distributed +--! under the terms of the GNU General Public License version 2. +--! +--! @file +--! @brief Generic single port RAM with a single read/write port +--! @author Matthias Alles +--! @date 2010/09/28 +--! + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.std_logic_unsigned.all; + +library dec_viterbi; +use dec_viterbi.pkg_helper.all; + + +entity generic_sp_ram is + generic( + DISTR_RAM : boolean := false; + WORDS : integer := 8; + BITWIDTH : integer := 8 + ); + port( + clk : in std_logic; + rst : in std_logic; + + wen : in std_logic; + en : in std_logic; + + a : in std_logic_vector(no_bits_natural(WORDS - 1) - 1 downto 0); + d : in std_logic_vector(BITWIDTH - 1 downto 0); + q : out std_logic_vector(BITWIDTH - 1 downto 0) + ); +end generic_sp_ram; + + +architecture rtl of generic_sp_ram is + + type t_ram is array(WORDS - 1 downto 0) of + std_logic_vector(BITWIDTH - 1 downto 0); + signal sp_ram : t_ram := (others => (others => '0')); + + function get_ram_style_xilinx(dist_ram : in boolean) return string is + begin + if dist_ram then + return "pipe_distributed"; + else + return "block"; + end if; + end function; + + function get_ram_style_altera(dist_ram : in boolean) return string is + begin + if dist_ram then + return "MLAB, no_rw_check"; + else + return "AUTO"; + end if; + end function; + + attribute RAM_STYLE : string; + attribute RAM_STYLE of sp_ram : signal is get_ram_style_xilinx(DISTR_RAM); + + attribute ramstyle : string; + attribute ramstyle of sp_ram : signal is get_ram_style_altera(DISTR_RAM); + +begin + + -- + -- Do not register the address for reading, since the synthesis doesn't + -- recognize then that this is a single-port RAM. + -- + pr_sp_ram_rw: process(clk) + begin + if rising_edge(clk) then + if en = '1' then + if wen = '1' then + sp_ram(conv_integer(a)) <= d; + else + q <= sp_ram(conv_integer(a)); + end if; + end if; + end if; + end process pr_sp_ram_rw; + +end rtl; diff --git a/tests/designs/viterbi_decoder_axi4s/src/ram_ctrl.vhd b/tests/designs/viterbi_decoder_axi4s/src/ram_ctrl.vhd new file mode 100644 index 00000000..71273881 --- /dev/null +++ b/tests/designs/viterbi_decoder_axi4s/src/ram_ctrl.vhd @@ -0,0 +1,474 @@ +--! +--! Copyright (C) 2011 - 2014 Creonic GmbH +--! +--! This file is part of the Creonic Viterbi Decoder, which is distributed +--! under the terms of the GNU General Public License version 2. +--! +--! @file +--! @brief Viterbi decoder RAM control +--! @author Markus Fehrenz +--! @date 2011/12/13 +--! +--! @details Manage RAM behavior. Write and read data. +--! The decisions are sent to the traceback units +--! It is signaled if the data belongs to acquisition or window phase. +--! + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library dec_viterbi; +use dec_viterbi.pkg_param.all; +use dec_viterbi.pkg_param_derived.all; +use dec_viterbi.pkg_types.all; +use dec_viterbi.pkg_components.all; + + +entity ram_ctrl is + port( + clk : in std_logic; + rst : in std_logic; + + + -- + -- Slave data signals, delivers the LLR parity values. + -- + s_axis_input_tvalid : in std_logic; + s_axis_input_tdata : in std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); + s_axis_input_tlast : in std_logic; + s_axis_input_tready : out std_logic; + + + -- + -- Master data signals for traceback units, delivers the decision vectors. + -- + m_axis_output_tvalid : out std_logic_vector(1 downto 0); + m_axis_output_tdata : out t_ram_rd_data; + m_axis_output_tlast : out std_logic_vector(1 downto 0); + m_axis_output_tready : in std_logic_vector(1 downto 0); + + -- Signals the traceback unit when the decision bits do not belong to an acquisition. + m_axis_output_window_tuser : out std_logic_vector(1 downto 0); + + -- Signals whether this is the last decision vector of the window. + m_axis_output_last_tuser : out std_logic_vector(1 downto 0); + + + -- + -- Slave configuration signals, delivering the configuration data. + -- + + s_axis_ctrl_tvalid : in std_logic; + s_axis_ctrl_tdata : in std_logic_vector(31 downto 0); + s_axis_ctrl_tready : out std_logic +); +end entity ram_ctrl; + + +architecture rtl of ram_ctrl is + + ------------------------ + -- Type definition + ------------------------ + + -- + -- Record contains runtime configuration. + -- The input configuration is stored in a register. + -- It is received from a AXI4-Stream interface from the top entity. + -- + type trec_runtime_param is record + window_length : unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); + acquisition_length : unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); + end record trec_runtime_param; + + -- Types for finite state machines + type t_write_ram_fsm is (CONFIGURE, START, RUN, WAIT_FOR_TRACEBACK, WAIT_FOR_LAST_TRACEBACK); + type t_read_ram_fsm is (WAIT_FOR_WINDOW, TRACEBACK, WAIT_FOR_RAM, FINISH); + type t_read_ram_fsm_array is array (0 to 1) of t_read_ram_fsm; + + -- RAM controling types + type t_ram_data is array (3 downto 0) of std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); + type t_ram_addr is array (3 downto 0) of unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); + type t_ram_rd_addr is array (1 downto 0) of unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); + type t_ram_ptr is array (1 downto 0) of unsigned(1 downto 0); + type t_ram_ptr_int is array (1 downto 0) of integer range 3 downto 0; + + type t_ram_data_cnt is array (1 downto 0) of integer range 2 * MAX_WINDOW_LENGTH downto 0; + + + ------------------------ + -- Signal declaration + ------------------------ + + signal ram_buffer : t_ram_rd_data; + signal ram_buffer_full : std_logic_vector(1 downto 0); + + signal config : trec_runtime_param; + signal write_ram_fsm : t_write_ram_fsm; + signal read_ram_fsm : t_read_ram_fsm_array; + signal wen_ram : std_logic_vector(3 downto 0); + signal addr : t_ram_addr; + signal q_reg : t_ram_data; + + -- ram addess, number and data pointer + signal write_ram_ptr : unsigned(1 downto 0); + signal read_ram_ptr : t_ram_ptr; + signal read_ram_ptr_d : t_ram_ptr; + signal write_addr_ptr : unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); + signal read_addr_ptr : t_ram_rd_addr; + + -- internal signals of outputs + signal m_axis_output_tvalid_int : std_logic_vector(1 downto 0); + signal m_axis_output_tlast_int : std_logic_vector(1 downto 0); + signal m_axis_output_window_tuser_int : std_logic_vector(1 downto 0); + signal m_axis_output_last_tuser_int : std_logic_vector(1 downto 0); + signal s_axis_input_tready_int : std_logic; + signal s_axis_ctrl_tready_int : std_logic; + + signal next_traceback : std_logic_vector(1 downto 0); + signal write_window_complete : std_logic; + signal write_last_window_complete : std_logic; + signal last_of_block : std_logic; + signal read_last_addr_ptr : unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); +begin + + m_axis_output_tvalid <= m_axis_output_tvalid_int; + m_axis_output_tlast <= m_axis_output_tlast_int; + m_axis_output_window_tuser <= m_axis_output_window_tuser_int; + m_axis_output_last_tuser <= m_axis_output_last_tuser_int; + m_axis_output_tdata(0) <= q_reg(to_integer(read_ram_ptr_d(0))) when ram_buffer_full(0) = '0' else ram_buffer(0); + m_axis_output_tdata(1) <= q_reg(to_integer(read_ram_ptr_d(1))) when ram_buffer_full(1) = '0' else ram_buffer(1); + + + -- + -- When the output port is not ready to read the output of the RAM immediately + -- we have to remember the output value of the RAM in an extra register. + -- When the output is ready to read, we first use the ouput of the register + -- and only then the output of the RAM again. + -- + pr_buf_ram_output: process(clk) is + begin + if rising_edge(clk) then + if rst = '1' then + ram_buffer <= (others => (others => '0')); + ram_buffer_full <= (others => '0'); + else + + for i in 0 to 1 loop + if m_axis_output_tvalid_int(i) = '1' and m_axis_output_tready(i) = '0' and ram_buffer_full(i) = '0' then + ram_buffer(i) <= q_reg(to_integer(read_ram_ptr_d(i))); + ram_buffer_full(i) <= '1'; + end if; + + if m_axis_output_tvalid_int(i) = '1' and m_axis_output_tready(i) = '1' and ram_buffer_full(i) = '1' then + ram_buffer_full(i) <= '0'; + end if; + end loop; + + end if; + end if; + end process pr_buf_ram_output; + + ----------------------------- + -- Manage writing from ACS -- + ----------------------------- + s_axis_input_tready_int <= '0' when (write_ram_fsm = CONFIGURE) or + (write_ram_ptr = read_ram_ptr(0) and read_ram_fsm(0) /= WAIT_FOR_WINDOW) or + (write_ram_ptr = read_ram_ptr(1) and read_ram_fsm(1) /= WAIT_FOR_WINDOW) or + write_ram_fsm = WAIT_FOR_TRACEBACK or write_ram_fsm = WAIT_FOR_LAST_TRACEBACK else + '1'; + s_axis_input_tready <= s_axis_input_tready_int; + + s_axis_ctrl_tready_int <= '1' when (read_ram_fsm(0) = WAIT_FOR_WINDOW and read_ram_fsm(1) = WAIT_FOR_WINDOW and write_ram_fsm = CONFIGURE) else + '0'; + s_axis_ctrl_tready <= s_axis_ctrl_tready_int; + + -- Process for writing to the RAM + pr_write_ram: process(clk) is + variable v_window_length : unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); + variable v_acquisition_length : unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); + begin + if rising_edge(clk) then + if rst = '1' then + write_ram_fsm <= CONFIGURE; + write_addr_ptr <= (others => '0'); + write_ram_ptr <= (others => '0'); + wen_ram <= (others => '0'); + write_window_complete <= '0'; + write_last_window_complete <= '0'; + read_last_addr_ptr <= (others => '0'); + else + + case write_ram_fsm is + + -- + -- It is necessary to configure the decoder before each block + -- + when CONFIGURE => + write_window_complete <= '0'; + write_last_window_complete <= '0'; + if s_axis_ctrl_tvalid = '1' and s_axis_ctrl_tready_int = '1' then + v_window_length := unsigned(s_axis_ctrl_tdata(BW_MAX_WINDOW_LENGTH - 1 + 16 downto 16)); + v_acquisition_length := unsigned(s_axis_ctrl_tdata(BW_MAX_WINDOW_LENGTH - 1 downto 0)); + write_addr_ptr <= v_window_length - v_acquisition_length; + config.window_length <= v_window_length; + config.acquisition_length <= v_acquisition_length; + write_ram_fsm <= START; + + wen_ram(to_integer(write_ram_ptr)) <= '1'; + end if; + + + -- + -- After the decoder is configured, the decoder is waiting for a new block. + -- When the AXIS handshake is there the packet transmission begins. + -- The first write is a special case, since writing data starts at the acquisition length. + -- There is no complete window available afterwards. + -- + when START => + if s_axis_input_tvalid = '1' and s_axis_input_tready_int = '1' then + + if write_addr_ptr = config.window_length - 1 then + + -- When we switch to the next RAM, we reset the write addr. + write_addr_ptr <= (others => '0'); + + -- Switch to the next RAM. + write_ram_ptr <= write_ram_ptr + 1; + wen_ram(to_integer(write_ram_ptr)) <= '0'; + wen_ram(to_integer(write_ram_ptr + 1)) <= '1'; + + write_ram_fsm <= RUN; + else + write_addr_ptr <= write_addr_ptr + 1; + end if; + end if; + + + -- + -- The decoder is receiving data from the ACS. + -- + when RUN => + write_window_complete <= '0'; + write_last_window_complete <= '0'; + + if s_axis_input_tvalid = '1' and s_axis_input_tready_int = '1' then + write_addr_ptr <= write_addr_ptr + 1; + + if write_addr_ptr = config.window_length - 1 then + + -- When we switch to the next RAM, we reset the write addr. + write_addr_ptr <= (others => '0'); + + -- Switch to the next RAM. + write_ram_ptr <= write_ram_ptr + 1; + wen_ram(to_integer(write_ram_ptr)) <= '0'; + wen_ram(to_integer(write_ram_ptr + 1)) <= '1'; + + -- Indicate, that a complete window is within the RAM and traceback may start. + write_window_complete <= '1'; + + if read_ram_fsm(0) /= WAIT_FOR_WINDOW and read_ram_fsm(1) /= WAIT_FOR_WINDOW then + write_ram_fsm <= WAIT_FOR_TRACEBACK; + end if; + + else + write_addr_ptr <= write_addr_ptr + 1; + end if; + + if s_axis_input_tlast = '1' then + write_ram_fsm <= CONFIGURE; + wen_ram <= (others => '0'); + + write_last_window_complete <= '1'; + if (read_ram_fsm(0) /= WAIT_FOR_WINDOW and read_ram_fsm(1) /= WAIT_FOR_WINDOW) or write_window_complete = '1' then + write_ram_fsm <= WAIT_FOR_LAST_TRACEBACK; + end if; + read_last_addr_ptr <= write_addr_ptr; + + write_addr_ptr <= (others => '0'); + write_ram_ptr <= write_ram_ptr + 1; + end if; + end if; + + when WAIT_FOR_TRACEBACK => + if read_ram_fsm(0) = WAIT_FOR_WINDOW or read_ram_fsm(1) = WAIT_FOR_WINDOW then + write_ram_fsm <= RUN; + write_window_complete <= '0'; + end if; + + when WAIT_FOR_LAST_TRACEBACK => + if read_ram_fsm(0) = WAIT_FOR_WINDOW or read_ram_fsm(1) = WAIT_FOR_WINDOW then + write_ram_fsm <= CONFIGURE; + write_last_window_complete <= '0'; + end if; + + end case; + end if; + end if; + end process pr_write_ram; + + + ------------------------------------------- + -- Manage reading from RAM for traceback -- + ------------------------------------------- + + gen_read_ram: for i in 0 to 1 generate + pr_read_ram: process(clk) is + begin + if rising_edge(clk) then + if rst = '1' then + read_addr_ptr(i) <= (others => '0'); + read_ram_fsm(i) <= WAIT_FOR_WINDOW; + m_axis_output_tvalid_int(i) <= '0'; + m_axis_output_tlast_int(i) <= '0'; + m_axis_output_window_tuser_int(i) <= '0'; + m_axis_output_last_tuser_int(i) <= '0'; + read_ram_ptr(i) <= (others => '0'); + read_ram_ptr_d(i) <= (others => '0'); + else + + read_ram_ptr_d(i) <= read_ram_ptr(i); + case read_ram_fsm(i) is + + -- Wait for the next window to be ready within the RAM. + when WAIT_FOR_WINDOW => + read_addr_ptr(i) <= config.window_length - 1; + m_axis_output_tlast_int(i) <= '0'; + m_axis_output_tvalid_int(i) <= '0'; + m_axis_output_last_tuser_int(i) <= '0'; + m_axis_output_window_tuser_int(i) <= '0'; + read_ram_ptr(i) <= write_ram_ptr; + + -- We always start from the RAM, which was written last. + if write_window_complete = '1' and next_traceback(i) = '1' then + read_ram_ptr(i) <= write_ram_ptr - 1; + read_addr_ptr(i) <= read_addr_ptr(i) - 1; + read_ram_fsm(i) <= TRACEBACK; + m_axis_output_tvalid_int(i) <= '1'; + end if; + if write_last_window_complete = '1' and next_traceback(i) = '1' then + read_ram_ptr(i) <= write_ram_ptr - 1; + read_addr_ptr(i) <= read_last_addr_ptr; + read_ram_fsm(i) <= TRACEBACK; + m_axis_output_window_tuser_int(i) <= '1'; + end if; + + -- Perform the Traceback on the RAM data of the first RAM we need for acquisition and traceback. + when TRACEBACK => + m_axis_output_tlast_int(i) <= '0'; + m_axis_output_last_tuser_int(i) <= '0'; + m_axis_output_tvalid_int(i) <= '1'; + + if m_axis_output_tready(i) = '1' then + if read_addr_ptr(i) = 0 then + if read_ram_fsm(1 - i) = TRACEBACK and read_ram_ptr(1 - i) = read_ram_ptr(i) - 1 then + read_ram_fsm(i) <= WAIT_FOR_RAM; + else + read_addr_ptr(i) <= config.window_length - 1; + read_ram_ptr(i) <= read_ram_ptr(i) - 1; + read_ram_fsm(i) <= FINISH; + end if; + else + read_addr_ptr(i) <= read_addr_ptr(i) - 1; + end if; + + -- Signal the traceback unit, acquisition is over. + if read_addr_ptr(i) = config.window_length - config.acquisition_length - 1 then + m_axis_output_window_tuser_int(i) <= '1'; + end if; + end if; + + when WAIT_FOR_RAM => + m_axis_output_tvalid_int(i) <= '0'; + if read_ram_fsm(1 - i) /= TRACEBACK or read_ram_ptr(1 - i) /= read_ram_ptr(i) - 1 then + read_addr_ptr(i) <= config.window_length - 1; + read_ram_ptr(i) <= read_ram_ptr(i) - 1; + read_ram_fsm(i) <= FINISH; + end if; + + -- Get the remaining values from the second RAM we need for traceback (no acquisition values in this RAM) + when FINISH => + if m_axis_output_tvalid_int(i) <= '0' then + m_axis_output_tvalid_int(i) <= '1'; + read_addr_ptr(i) <= read_addr_ptr(i) - 1; + end if; + if m_axis_output_tready(i) = '1' then + + if read_addr_ptr(i) = config.window_length - config.acquisition_length then + m_axis_output_last_tuser_int(i) <= '1'; + read_addr_ptr(i) <= config.window_length - 1; + read_ram_fsm(i) <= WAIT_FOR_WINDOW; + + -- Check if the other read process finished processing. + if read_ram_fsm((i+1) mod 2) = WAIT_FOR_WINDOW and last_of_block = '1' then + m_axis_output_tlast_int(i) <= '1'; + end if; + + else + read_addr_ptr(i) <= read_addr_ptr(i) - 1; + end if; + end if; + end case; + end if; + end if; + end process pr_read_ram; + end generate gen_read_ram; + + -- This process decides which traceback unit is the next one to use. + pr_next_traceback: process(clk) is + begin + if rising_edge(clk) then + if rst = '1' then + next_traceback <= "01"; + last_of_block <= '0'; + else + if write_window_complete = '1' then + if next_traceback(0) = '1' then + next_traceback(0) <= '0'; + next_traceback(1) <= '1'; + else + next_traceback(0) <= '1'; + next_traceback(1) <= '0'; + end if; + end if; + + if s_axis_input_tlast = '1' then + last_of_block <= '1'; + end if; + + end if; + end if; + end process pr_next_traceback; + + ------------------------------ + --- Portmapping components --- + ------------------------------ + + gen_generic_sp_ram : for i in 0 to 3 generate + begin + + addr(i) <= write_addr_ptr when (write_ram_fsm = RUN or write_ram_fsm = START) and to_integer(write_ram_ptr) = i else + read_addr_ptr(0) when (to_integer(read_ram_ptr(0)) = i and (read_ram_fsm(0) = TRACEBACK or read_ram_fsm(0) = WAIT_FOR_RAM or read_ram_fsm(0) = FINISH)) or + (next_traceback(0) = '1' and write_window_complete = '1' and to_integer(read_ram_ptr(0)) = i) else + read_addr_ptr(1); + + inst_generic_sp_ram : generic_sp_ram + generic map( + DISTR_RAM => DISTRIBUTED_RAM, + WORDS => MAX_WINDOW_LENGTH, + BITWIDTH => NUMBER_TRELLIS_STATES + ) + port map( + clk => clk, + rst => rst, + wen => wen_ram(i), + en => '1', + a => std_logic_vector(addr(i)), + d => s_axis_input_tdata, + q => q_reg(i) + ); + end generate gen_generic_sp_ram; + +end architecture rtl; diff --git a/tests/designs/viterbi_decoder_axi4s/src/recursion.vhd b/tests/designs/viterbi_decoder_axi4s/src/recursion.vhd new file mode 100644 index 00000000..ed64d89e --- /dev/null +++ b/tests/designs/viterbi_decoder_axi4s/src/recursion.vhd @@ -0,0 +1,96 @@ +--! +--! Copyright (C) 2011 - 2014 Creonic GmbH +--! +--! This file is part of the Creonic Viterbi Decoder, which is distributed +--! under the terms of the GNU General Public License version 2. +--! +--! @file +--! @brief Recursion unit for recursive code. +--! @author Markus Fehrenz +--! @date 2011/01/12 +--! +--! @details The recusion handling buffers the reorder ouput and +--! calculates the correct output depending on the feedback polynomial. +--! + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library dec_viterbi; +use dec_viterbi.pkg_param.all; +use dec_viterbi.pkg_param_derived.all; + +entity recursion is + port( + clk : in std_logic; + rst : in std_logic; + + -- + -- Decoded bits input from the reordering units in std_logic + -- + s_axis_input_tvalid : in std_logic; + s_axis_input_tdata : in std_logic; + s_axis_input_tlast : in std_logic; + s_axis_input_tready : out std_logic; + + -- + -- Output decoded bits convolved with the feedback polynomial + -- + m_axis_output_tvalid : out std_logic; + m_axis_output_tdata : out std_logic; + m_axis_output_tlast : out std_logic; + m_axis_output_tready : in std_logic + ); +end entity recursion; + +architecture rtl of recursion is + signal recursion_sreg : unsigned(ENCODER_MEMORY_DEPTH downto 0); + signal s_axis_input_tready_int : std_logic; + signal m_axis_output_tvalid_int : std_logic; + +begin + s_axis_input_tready_int <= '1' when m_axis_output_tready = '1' or m_axis_output_tvalid_int = '0' else + '0'; + + s_axis_input_tready <= s_axis_input_tready_int; + m_axis_output_tvalid <= m_axis_output_tvalid_int; + + -- Use the feedback polynomial to convolve the global path. + pr_recursion : process(clk) is + variable v_bit : std_logic := '0'; + variable v_recursion_state : unsigned(ENCODER_MEMORY_DEPTH downto 0); + begin + if rising_edge(clk) then + if rst = '1' then + recursion_sreg <= (others => '0'); + m_axis_output_tdata <= '0'; + m_axis_output_tlast <= '0'; + else + m_axis_output_tvalid_int <= s_axis_input_tvalid; + + if s_axis_input_tvalid = '1' and s_axis_input_tready_int = '1' then + + -- move current decoded output bits into shift register and reset if last flag is valid + if s_axis_input_tlast = '1' then + recursion_sreg <= (others => '0'); + else + recursion_sreg <= s_axis_input_tdata & recursion_sreg(ENCODER_MEMORY_DEPTH downto 1); + end if; + + -- convolve with feedback polynomial with the output register. + v_bit := '0'; + v_recursion_state := (s_axis_input_tdata & recursion_sreg(ENCODER_MEMORY_DEPTH downto 1)) and + ('1' & to_unsigned(FEEDBACK_POLYNOMIAL, ENCODER_MEMORY_DEPTH)); + for i in ENCODER_MEMORY_DEPTH downto 0 loop + v_bit := v_bit xor v_recursion_state(i); + end loop; + m_axis_output_tdata <= v_bit; + + m_axis_output_tlast <= s_axis_input_tlast; + end if; + end if; + end if; + end process pr_recursion; + +end architecture rtl; diff --git a/tests/designs/viterbi_decoder_axi4s/src/reorder.vhd b/tests/designs/viterbi_decoder_axi4s/src/reorder.vhd new file mode 100644 index 00000000..c2906909 --- /dev/null +++ b/tests/designs/viterbi_decoder_axi4s/src/reorder.vhd @@ -0,0 +1,129 @@ +--! +--! Copyright (C) 2011 - 2014 Creonic GmbH +--! +--! This file is part of the Creonic Viterbi Decoder, which is distributed +--! under the terms of the GNU General Public License version 2. +--! +--! @file +--! @brief Reorder twisted output due to windowing +--! @author Markus Fehrenz +--! @date 2011/05/12 +--! +--! @details The windowing output is twisted. +--! The correct order is simply rebuilt by reversing +--! the output of each traceback unit. +--! + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library dec_viterbi; +use dec_viterbi.pkg_param.all; +use dec_viterbi.pkg_param_derived.all; +use dec_viterbi.pkg_types.all; + + +entity reorder is + port( + clk : in std_logic; + rst : in std_logic; + + -- + -- Traceback unit output, twisted order + -- + s_axis_input_tvalid : in std_logic; + s_axis_input_tdata : in std_logic; + s_axis_input_tlast : in std_logic; + s_axis_input_last_tuser : in std_logic; + s_axis_input_tready : out std_logic; + + -- + -- Viterbi decoder output, original (input) order. + -- + m_axis_output_tvalid : out std_logic; + m_axis_output_tdata : out std_logic; + m_axis_output_tlast : out std_logic; + m_axis_output_last_tuser : out std_logic; -- Last bit of one traceback window + m_axis_output_tready : in std_logic + ); +end entity reorder; + + +architecture rtl of reorder is + + -- used to store one reversed output of a traceback unit + signal buffer_sreg : unsigned(MAX_WINDOW_LENGTH - 1 downto 0); + signal buffer_cnt : unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); + signal buffer_end : integer range ENCODER_MEMORY_DEPTH downto 0; + signal send_output, last_window : boolean; + + signal s_axis_input_tready_int : std_logic; + +begin + + s_axis_input_tready <= s_axis_input_tready_int; + s_axis_input_tready_int <= '1' when not(send_output) else + '0'; + +-- m_axis_output_tvalid <= '1' when send_output and m_axis_output_tready= '1' else + m_axis_output_tvalid <= '1' when send_output else + '0'; + m_axis_output_tdata <= buffer_sreg(0); + + m_axis_output_tlast <= '1' when buffer_cnt = ENCODER_MEMORY_DEPTH and last_window else + '0'; + + -- Reorder the global path given from an traceback unit with the help of a shift register. + pr_reorder : process(clk) is + begin + if rising_edge(clk) then + if rst = '1' then + buffer_sreg <= (others => '0'); + buffer_cnt <= (others => '0'); + send_output <= false; + last_window <= false; + buffer_end <= 0; + m_axis_output_last_tuser <= '0'; + else + + -- store output of traceback unit + if s_axis_input_tvalid = '1' and s_axis_input_tready_int = '1' then + if s_axis_input_tlast = '1' then + last_window <= true; + buffer_end <= ENCODER_MEMORY_DEPTH; + end if; + if s_axis_input_last_tuser = '1' then + send_output <= true; + buffer_sreg <= buffer_sreg(MAX_WINDOW_LENGTH - 2 downto 0) & s_axis_input_tdata; + else + buffer_sreg <= buffer_sreg(MAX_WINDOW_LENGTH - 2 downto 0) & s_axis_input_tdata; + buffer_cnt <= buffer_cnt + 1; + end if; + end if; + + -- send reordered data to the output + if m_axis_output_tready = '1' and send_output then + buffer_sreg <= '0' & buffer_sreg(MAX_WINDOW_LENGTH - 1 downto 1); + + -- Next transfer will be the last one of this window. + if buffer_cnt = 1 then + m_axis_output_last_tuser <= '1'; + end if; + + -- This was the last data transfer. Tailbits are cut off + if buffer_cnt = buffer_end then + send_output <= false; + last_window <= false; + buffer_end <= 0; + buffer_cnt <= (others => '0'); + m_axis_output_last_tuser <= '0'; + else + buffer_cnt <= buffer_cnt - 1; + end if; + end if; + end if; + end if; + end process pr_reorder; + +end architecture rtl; diff --git a/tests/designs/viterbi_decoder_axi4s/src/traceback.vhd b/tests/designs/viterbi_decoder_axi4s/src/traceback.vhd new file mode 100644 index 00000000..b3da8573 --- /dev/null +++ b/tests/designs/viterbi_decoder_axi4s/src/traceback.vhd @@ -0,0 +1,101 @@ +--! +--! Copyright (C) 2011 - 2014 Creonic GmbH +--! +--! This file is part of the Creonic Viterbi Decoder, which is distributed +--! under the terms of the GNU General Public License version 2. +--! +--! @file +--! @brief Traceback unit for a viterbi decoder +--! @author Markus Fehrenz +--! @date 2011/07/11 +--! +--! @details The traceback unit only processes a data stream. +--! There is no knowledge about the decoder configuration. +--! The information about acquisition and window lengths is received from ram control. +--! + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library dec_viterbi; +use dec_viterbi.pkg_param.all; +use dec_viterbi.pkg_param_derived.all; + + +entity trellis_traceback is + port( + -- general signals + clk : in std_logic; + rst : in std_logic; + + s_axis_input_tvalid : in std_logic; + s_axis_input_tdata : in std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); + s_axis_input_tlast : in std_logic; + s_axis_input_tready : out std_logic; + s_axis_input_window_tuser : in std_logic; + s_axis_input_last_tuser : in std_logic; + + m_axis_output_tvalid : out std_logic; + m_axis_output_tdata : out std_logic; + m_axis_output_tlast : out std_logic; + m_axis_output_last_tuser : out std_logic; + m_axis_output_tready : in std_logic + ); +end entity trellis_traceback; + + +architecture rtl of trellis_traceback is + + signal current_node : unsigned(BW_TRELLIS_STATES - 1 downto 0); + signal m_axis_output_tvalid_int : std_logic; + signal s_axis_input_tready_int : std_logic; + +begin + s_axis_input_tready_int <= '1' when m_axis_output_tready = '1' or m_axis_output_tvalid_int = '0' else + '0'; + s_axis_input_tready <= s_axis_input_tready_int; + + m_axis_output_tvalid <= m_axis_output_tvalid_int; + + + -- Traceback the ACS local path decisions and output the resulting global path. + pr_traceback : process(clk) is + begin + if rising_edge(clk) then + if rst = '1' then + m_axis_output_tvalid_int <= '0'; + m_axis_output_tdata <= '0'; + m_axis_output_tlast <= '0'; + m_axis_output_last_tuser <= '0'; + current_node <= (others => '0'); + else + + if m_axis_output_tready = '1' then + m_axis_output_tvalid_int <= '0'; + end if; + + -- calculate the decoded bit with an shift register + if s_axis_input_tvalid = '1' and s_axis_input_tready_int = '1' then + + m_axis_output_tlast <= s_axis_input_tlast; + m_axis_output_last_tuser <= s_axis_input_last_tuser; + + -- handle tvalid output signal + if s_axis_input_window_tuser = '1' then + m_axis_output_tvalid_int <= '1'; + m_axis_output_tdata <= current_node(BW_TRELLIS_STATES - 1); + end if; + + -- last value of current window? + if s_axis_input_last_tuser = '1' then + current_node <= to_unsigned(0, BW_TRELLIS_STATES); + else + current_node <= current_node(BW_TRELLIS_STATES - 2 downto 0) + & s_axis_input_tdata(to_integer(current_node(BW_TRELLIS_STATES - 1 downto 0))); + end if; + end if; + end if; + end if; + end process pr_traceback; +end architecture rtl; diff --git a/tests/test_cases/test_iteration/Makefile b/tests/test_cases/test_iteration/Makefile new file mode 100644 index 00000000..0c18b6da --- /dev/null +++ b/tests/test_cases/test_iteration/Makefile @@ -0,0 +1,31 @@ +############################################################################### +# Copyright (c) 2015 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +include $(COCOTB)/tests/designs/viterbi_decoder_axi4s/Makefile + +MODULE = test_iteration diff --git a/tests/test_cases/test_iteration/test_iteration.py b/tests/test_cases/test_iteration/test_iteration.py new file mode 100644 index 00000000..e18c2802 --- /dev/null +++ b/tests/test_cases/test_iteration/test_iteration.py @@ -0,0 +1,38 @@ +''' Copyright (c) 2015 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +import cocotb +from cocotb.triggers import Timer +from cocotb.result import TestError + + +@cocotb.test() +def discovery_all(dut): + dut.log.info("Trying to discover") + yield Timer(0) + for thing in dut: + thing.log.info("Found something: %s" % thing.fullname) + + clk = dut.aclk \ No newline at end of file From 05e7da89c5c73095a54418edcbda1f952351b146 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 24 Jul 2015 17:54:00 +0100 Subject: [PATCH 0870/2656] Issue #17: Some makefile fixes --- tests/designs/viterbi_decoder_axi4s/Makefile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/designs/viterbi_decoder_axi4s/Makefile b/tests/designs/viterbi_decoder_axi4s/Makefile index 83db69eb..18e1bed3 100644 --- a/tests/designs/viterbi_decoder_axi4s/Makefile +++ b/tests/designs/viterbi_decoder_axi4s/Makefile @@ -25,11 +25,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -# Override this variable to use a VHDL toplevel instead of SystemVerilog -TOPLEVEL_LANG ?= sv - -# At the moment the only simulator supporting mixed language VHPI/VPI is Aldec -SIM?=aldec +TOPLEVEL_LANG = vhdl TOPLEVEL = dec_viterbi @@ -63,6 +59,10 @@ ifeq ($(SIM),modelsim) GPI_IMPL=fli endif +ifneq ($(SIM),) include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim - +else +all: + @echo "Skipping test as icarus does not support VHDL" +endif \ No newline at end of file From f01aa8f182728ac8421a26b2e0bab15b64bf3cad Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 24 Jul 2015 18:39:16 +0100 Subject: [PATCH 0871/2656] Issue #17: Update VPI layer to record object type --- include/gpi.h | 12 ++++++++++++ lib/gpi/GpiCommon.cpp | 12 ++++++------ lib/gpi/gpi_priv.h | 5 +++++ lib/vpi/VpiImpl.cpp | 44 +++++++++++++++++++++++++++++++++++++++---- lib/vpi/VpiImpl.h | 6 ++++++ 5 files changed, 69 insertions(+), 10 deletions(-) diff --git a/include/gpi.h b/include/gpi.h index b31e4cec..0aa1dbaf 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -124,6 +124,18 @@ void gpi_free_handle(gpi_sim_hdl gpi_hdl); // Types that can be passed to the iterator. // // Note these are strikingly similar to the VPI types... +typedef enum gpi_objtype_e { + GPI_UNKNOWN = 0, + GPI_MEMORY = 1, + GPI_MODULE = 2, + GPI_NET = 3, + GPI_PARAMETER = 4, + GPI_REGISTER = 5, + GPI_ARRAY = 6, + GPI_ENUM = 7, + GPI_STRUCTURE = 8, +} gpi_objtype_t; + #define gpiMemory 29 #define gpiModule 32 #define gpiNet 36 diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index f2dccaf5..ed37a7e1 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -210,6 +210,7 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) } } + LOG_DEBUG("Failed to find a hdl named %s", name); return NULL; } @@ -220,19 +221,18 @@ gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) GpiObjHdl *hdl = NULL; GpiObjHdl *base = sim_to_hdl(parent); - LOG_WARN("Trying index"); - for (iter = registered_impls.begin(); iter != registered_impls.end(); iter++) { - LOG_WARN("Checking if %d native though impl %s ", index, (*iter)->get_name_c()); + LOG_DEBUG("Checking if index %d native though impl %s ", index, (*iter)->get_name_c()); if ((hdl = (*iter)->native_check_create(index, base))) { - LOG_WARN("Found %d via %s", index, (*iter)->get_name_c()); - //hdl = base->get_handle_by_name(s_name); + LOG_DEBUG("Found %d via %s", index, (*iter)->get_name_c()); + return (gpi_sim_hdl)hdl; } } - return (gpi_sim_hdl)hdl; + LOG_DEBUG("Failed to find a hdl at index %d", index); + return NULL; } gpi_iterator_hdl gpi_iterate(uint32_t type, gpi_sim_hdl base) diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 285b964d..3f31d4af 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -98,6 +98,8 @@ class GpiObjHdl : public GpiHdl { m_type("unknown") { } GpiObjHdl(GpiImplInterface *impl) : GpiHdl(impl, NULL) { } GpiObjHdl(GpiImplInterface *impl, void *hdl) : GpiHdl(impl, hdl) { } + GpiObjHdl(GpiImplInterface *impl, void *hdl, gpi_objtype_t objtype) : GpiHdl(impl, hdl), m_type_e(objtype) { } + virtual ~GpiObjHdl() { } virtual const char* get_name_str(void); @@ -126,6 +128,9 @@ class GpiSignalObjHdl : public GpiObjHdl { public: GpiSignalObjHdl(GpiImplInterface *impl, void *hdl) : GpiObjHdl(impl, hdl), m_length(0) { } + GpiSignalObjHdl(GpiImplInterface *impl, void *hdl, gpi_objtype_t objtype) : + GpiObjHdl(impl, hdl, objtype), + m_length(0) { } virtual ~GpiSignalObjHdl() { } // Provide public access to the implementation (composition vs inheritance) virtual const char* get_signal_value_binstr(void) = 0; diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index ed1d70f2..acc8efb1 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -70,6 +70,42 @@ void VpiImpl::get_sim_time(uint32_t *high, uint32_t *low) *low = vpi_time_s.low; } +gpi_objtype_t to_gpi_objtype(int32_t vpitype) +{ + switch (vpitype) { + case vpiNet: + case vpiNetBit: + case vpiReg: + case vpiRegBit: + return GPI_REGISTER; + + case vpiInterfaceArray: + case vpiPackedArrayVar: + case vpiRegArray: + case vpiNetArray: + return GPI_ARRAY; + + case vpiEnumNet: + return GPI_ENUM; + + case vpiParameter: + return GPI_PARAMETER; + + + case vpiStructVar: + return GPI_STRUCTURE; + + case vpiModport: + case vpiInterface: + case vpiModule: + return GPI_MODULE; + + default: + return GPI_UNKNOWN; + } +} + + GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &name) { int32_t type; @@ -89,7 +125,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &n case vpiRegArray: case vpiNetArray: case vpiEnumNet: - new_obj = new VpiSignalObjHdl(this, new_hdl); + new_obj = new VpiSignalObjHdl(this, new_hdl, to_gpi_objtype(type)); break; case vpiStructVar: case vpiModule: @@ -98,7 +134,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &n case vpiInterfaceArray: case vpiRefObj: case vpiPackedArrayVar: - new_obj = new GpiObjHdl(this, new_hdl); + new_obj = new GpiObjHdl(this, new_hdl, to_gpi_objtype(type)); break; default: LOG_WARN("Not able to map type %d to object.", type); @@ -135,7 +171,7 @@ GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) GpiObjHdl *parent_hdl = sim_to_hdl(parent); vpiHandle vpi_hdl = parent_hdl->get_handle(); vpiHandle new_hdl; - + new_hdl = vpi_handle_by_index(vpi_hdl, index); if (new_hdl == NULL) { LOG_DEBUG("Unable to vpi_get_handle_by_index %d", index); @@ -180,7 +216,7 @@ GpiObjHdl *VpiImpl::get_root_handle(const char* name) check_vpi_error(); } - rv = new GpiObjHdl(this, root); + rv = new GpiObjHdl(this, root, to_gpi_objtype(vpi_get(vpiType, root))); rv->initialise(root_name); return rv; diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index b561b16c..d27aa578 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -177,6 +177,12 @@ class VpiSignalObjHdl : public GpiSignalObjHdl { m_rising_cb(impl, this, GPI_RISING), m_falling_cb(impl, this, GPI_FALLING), m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } + + VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl, gpi_objtype_t objtype) : + GpiSignalObjHdl(impl, hdl, objtype), + m_rising_cb(impl, this, GPI_RISING), + m_falling_cb(impl, this, GPI_FALLING), + m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } virtual ~VpiSignalObjHdl() { } const char* get_signal_value_binstr(void); From f587656a673f24afbef6b9ca62ccb1bd790e96ae Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 24 Jul 2015 18:40:12 +0100 Subject: [PATCH 0872/2656] Issue #17: Various updates to handle.py - some tests passing again --- cocotb/handle.py | 184 ++++++++++++++++++++------------------------ cocotb/scheduler.py | 2 +- 2 files changed, 86 insertions(+), 100 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 3571dfac..85abd345 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -33,8 +33,7 @@ import ctypes import traceback import sys -#from StringIO import StringIO - +import warnings from io import StringIO, BytesIO import os @@ -62,6 +61,16 @@ class SimHandleBase(object): We maintain a handle which we can use for GPI calls """ + # For backwards compatibility we support a mapping of old member names + # which may alias with the simulator hierarchy. In these cases the + # simulator takes priority, only falling back + _compat_mapping = { + "log" : "_log", + "fullname" : "_fullname", + "name" : "_name", + "setimmediatevalue" : "_setimmediatevalue" + } + def __init__(self, handle): """ Args: @@ -79,26 +88,22 @@ def __init__(self, handle): def __hash__(self): return self._handle - def __len__(self): - """Returns the 'length' of the underlying object. - - For vectors this is the number of bits. - - TODO: Handle other types (loops, generate etc) - """ - if self._len is None: - self._len = len(self._get_value_str()) - return self._len - def __getattr__(self, name): - """ Query the simulator for a object with the specified name - and cache the result to build a tree """ + Query the simulator for a object with the specified name + and cache the result to build a tree of objects + """ + + if name == "value": + return self._getvalue() if name in self._sub_handles: return self._sub_handles[name] new_handle = simulator.get_handle_by_name(self._handle, name) if not new_handle: - raise AttributeError("%s contains no object named %s" % (self.name, name)) + if name in self._compat_mapping: + warnings.warn("Use of %s attribute is deprecated" % name) + return getattr(self, self._compat_mapping[name]) + raise AttributeError("%s contains no object named %s" % (self._name, name)) self._sub_handles[name] = SimHandle(new_handle) return self._sub_handles[name] @@ -124,7 +129,7 @@ def __getitem__(self, index): return self._sub_handles[index] new_handle = simulator.get_handle_by_index(self._handle, index) if not new_handle: - self._raise_testerror("%s contains no object at index %d" % (self.name, index)) + self._raise_testerror("%s contains no object at index %d" % (self._name, index)) self._sub_handles[index] = SimHandle(new_handle) return self._sub_handles[index] @@ -132,12 +137,12 @@ def __getitem__(self, index): def __cmp__(self, other): # Permits comparison of handles i.e. if clk == dut.clk - if isinstance(other, SimHandle): + if isinstance(other, SimHandleBase): if self._handle == other._handle: return 0 return 1 - def __dir__(self): - return self._sub_handles.keys() + #def __dir__(self): + #return self._sub_handles.keys() def _discover_all(self): for thing in self: @@ -149,13 +154,39 @@ def _getAttributeNames(self): return dir(self) def __repr__(self): - return repr(int(self)) + return self._fullname -class HierarchyObjecct(SimHandleBase): + def _raise_testerror(self, msg): + lastframe = sys._getframe(2) + if sys.version_info[0] >= 3: + buff = StringIO() + traceback.print_stack(lastframe, file=buff) + else: + buff_bytes = BytesIO() + traceback.print_stack(lastframe, file=buff_bytes) + buff = StringIO(buff_bytes.getvalue().decode("UTF8")) + self._log.error("%s\n%s" % (msg, buff.getvalue())) + exception = TestError(msg) + exception.stderr.write(buff.getvalue()) + buff.close() + raise exception + + +class HierarchyObject(SimHandleBase): """ Hierarchy objects don't have values, they are effectively scopes or namespaces """ - pass + def __setattr__(self, name, value): + """ + Provide transparent access to signals via the hierarchy + + Slightly hacky version of operator overloading in Python + """ + if not name.startswith('_') and name not in self._compat_mapping \ + and self.__hasattr__(name): + getattr(self, name)._setcachedvalue(value) + return + object.__setattr__(self, name, value) class ConstantObject(SimHandleBase): @@ -173,6 +204,9 @@ def __init__(self, handle, *args, **kwargs): def __int__(self): return int(self._value) + def __repr__(self): + return repr(int(self)) + class NonConstantObject(SimHandleBase): def __init__(self, handle): @@ -180,14 +214,7 @@ def __init__(self, handle): Args: _handle [integer] : vpi/vhpi handle to the simulator object """ - self._handle = handle # handle used for future simulator transactions - self._sub_handles = {} # Dictionary of SimHandle objects created by getattr - self._len = None - - self.name = simulator.get_name_string(self._handle) - self.fullname = self.name + '(%s)' % simulator.get_type_string(self._handle) - self.log = SimLog('cocotb.' + self.name) - self.log.debug("Created!") + SimHandleBase.__init__(self, handle) self._r_edge = _RisingEdge(self) self._f_edge = _FallingEdge(self) @@ -195,68 +222,25 @@ def __hash__(self): return self._handle def __str__(self): - return "%s @0x%x" % (self.name, self._handle) - - def __getattr__(self, name): - """ Query the simulator for a object with the specified name - and cache the result to build a tree - """ - if name in self._sub_handles: - return self._sub_handles[name] - new_handle = simulator.get_handle_by_name(self._handle, name) - if not new_handle: - #self._raise_testerror("%s contains no object named %s" % (self.name, name)) - raise AttributeError("%s contains no object named %s" % (self.name, name)) - self._sub_handles[name] = SimHandle(new_handle) - return self._sub_handles[name] - - def _raise_testerror(self, msg): - lastframe = sys._getframe(2) - if sys.version_info[0] >= 3: - buff = StringIO() - traceback.print_stack(lastframe, file=buff) - else: - buff_bytes = BytesIO() - traceback.print_stack(lastframe, file=buff_bytes) - buff = StringIO(buff_bytes.getvalue().decode("UTF8")) - self.log.error("%s\n%s" % (msg, buff.getvalue())) - exception = TestError(msg) - exception.stderr.write(buff.getvalue()) - buff.close() - raise exception - - - def __hasattr__(self, name): - """ - Since calling hasattr(handle, "something") will print out a - backtrace to the log since usually attempting to access a - non-existent member is an error we provide a 'peek function - - We still add the found handle to our dictionary to prevent leaking - handles. - """ - if name in self._sub_handles: - return self._sub_handles[name] - new_handle = simulator.get_handle_by_name(self._handle, name) - if new_handle: - self._sub_handles[name] = SimHandle(new_handle) - return new_handle + return "%s @0x%x" % (self._name, self._handle) def __getitem__(self, index): if index in self._sub_handles: return self._sub_handles[index] new_handle = simulator.get_handle_by_index(self._handle, index) if not new_handle: - self._raise_testerror("%s contains no object at index %d" % (self.name, index)) + self._raise_testerror("%s contains no object at index %d" % (self._name, index)) self._sub_handles[index] = SimHandle(new_handle) return self._sub_handles[index] - @property - def value(self): + def _getvalue(self): result = BinaryValue() result.binstr = self._get_value_str() return result + ## We want to maintain compatability with python 2.5 so we can't use @property with a setter + #value = property(_getvalue, None, None, "A reference to the value") + def _get_value_str(self): return simulator.get_signal_val(self._handle) @@ -303,25 +287,19 @@ def __iter__(self): def __int__(self): return int(self.value) + def __repr__(self): + return repr(int(self)) -class ModifiableObject(SimHandleBase): +class ModifiableObject(NonConstantObject): """ Base class for simulator objects whose values can be modified """ - def __setattr__(self, name, value): - """Provide transparent access to signals""" - if not name.startswith('_') and not name in ["name", "fullname", "log", "value"] \ - and self.__hasattr__(name): - getattr(self, name).setcachedvalue(value) - return - object.__setattr__(self, name, value) - def __setitem__(self, index, value): """Provide transparent assignment to bit index""" - self.__getitem__(index).setcachedvalue(value) + self.__getitem__(index)._setcachedvalue(value) - def setimmediatevalue(self, value): + def _setimmediatevalue(self, value): """ Set the value of the underlying simulation object to value. @@ -348,19 +326,17 @@ def setimmediatevalue(self, value): elif isinstance(value, get_python_integer_types()): value = BinaryValue(value=value, bits=len(self), bigEndian=False) elif not isinstance(value, BinaryValue): - self.log.critical("Unsupported type for value assignment: %s (%s)" % (type(value), repr(value))) + self._log.critical("Unsupported type for value assignment: %s (%s)" % (type(value), repr(value))) raise TypeError("Unable to set simulator value with type %s" % (type(value))) simulator.set_signal_val_str(self._handle, value.binstr) - @property - def value(self): + def _getvalue(self): result = BinaryValue() result.binstr = self._get_value_str() return result - @value.setter - def setcachedvalue(self, value): + def _setcachedvalue(self, value): """ Intercept the store of a value and hold in cache. @@ -371,6 +347,10 @@ def setcachedvalue(self, value): cocotb.scheduler.save_write(self, value) + # We want to maintain compatability with python 2.5 so we can't use @property with a setter + value = property(_getvalue, _setcachedvalue, None, "A reference to the value") + + def __le__(self, value): """Overload the less than or equal to operator to provide an hdl-like shortcut @@ -384,7 +364,13 @@ def SimHandle(handle): """ Factory function to create the correct type of SimHandle object """ - t = simulator.get_type(handle) - print t - return None + _type2cls = { + 2: HierarchyObject, + 5: ModifiableObject, + } + + t = simulator.get_type(handle) + if t not in _type2cls: + raise TestError("Couldn't find a matching object for GPI type %d" % t) + return _type2cls[t](handle) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 9f9abe78..c5011989 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -262,7 +262,7 @@ def react(self, trigger, depth=0): while self._writes: handle, value = self._writes.popitem() - handle.setimmediatevalue(value) + handle._setimmediatevalue(value) self._readwrite.unprime() From 4fd31584d365ed2ef6ffee0c503eda9e72cc9a43 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 25 Jul 2015 15:52:52 +0100 Subject: [PATCH 0873/2656] Issue #17: Add VHPI type mapping and iterator handles --- lib/vhpi/VhpiImpl.cpp | 72 +++++++++++++++++++++++++++++++++++++++++-- lib/vhpi/VhpiImpl.h | 9 +++++- 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 931c1757..0bfa9182 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -110,6 +110,44 @@ void VhpiImpl::get_sim_time(uint32_t *high, uint32_t *low) *low = vhpi_time_s.low; } + +gpi_objtype_t to_gpi_objtype(int32_t vhpitype) +{ + switch (vhpitype) { + case vhpiPortDeclK: + case vhpiSigDeclK: + case vhpiIndexedNameK: + case vhpiVarDeclK: + case vhpiVarParamDeclK: + return GPI_REGISTER; + + case vhpiArrayTypeDeclK: + return GPI_ARRAY; + + case vhpiEnumLiteralK: + return GPI_ENUM; + + case vhpiGenericDeclK: + return GPI_PARAMETER; + + case vhpiRecordTypeDeclK: + return GPI_STRUCTURE; + + case vhpiForGenerateK: + case vhpiIfGenerateK: + case vhpiCompInstStmtK: + case vhpiEntityDeclK: + case vhpiRootInstK: + return GPI_MODULE; + + default: + LOG_WARN("Unable to map VHPI type %d onto GPI type", vhpitype); + return GPI_UNKNOWN; + } +} + + + GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string &name) { vhpiIntT type; @@ -124,12 +162,12 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string case vhpiPortDeclK: case vhpiSigDeclK: case vhpiIndexedNameK: - new_obj = new VhpiSignalObjHdl(this, new_hdl); + new_obj = new VhpiSignalObjHdl(this, new_hdl, to_gpi_objtype(type)); break; case vhpiForGenerateK: case vhpiIfGenerateK: case vhpiCompInstStmtK: - new_obj = new GpiObjHdl(this, new_hdl); + new_obj = new GpiObjHdl(this, new_hdl, to_gpi_objtype(type)); break; default: LOG_WARN("Not able to map type %d to object."); @@ -229,13 +267,41 @@ GpiObjHdl *VhpiImpl::get_root_handle(const char* name) } root_name = found; - rv = new GpiObjHdl(this, root); + rv = new GpiObjHdl(this, root, to_gpi_objtype(vhpi_get(vhpiKindP, root))); rv->initialise(root_name); FEXIT return rv; } +GpiIterator *VhpiImpl::iterate_handle(uint32_t type, GpiObjHdl *obj_hdl) +{ + vhpiHandleT vhpi_hdl = obj_hdl->get_handle(); + + vhpiHandleT iterator; + iterator = vhpi_iterator(vhpiSignals, vhpi_hdl); + + GpiIterator *new_iter; + new_iter = new GpiIterator(this, iterator); + return new_iter; +} + +GpiObjHdl *VhpiImpl::next_handle(GpiIterator *iter) +{ + vhpiHandleT obj; + GpiObjHdl *new_obj = NULL; + + obj = vhpi_scan((vhpiHandleT)iter->m_iter_hdl); + + if (NULL==obj) + return new_obj; + + std::string name = vhpi_get_str(vhpiFullNameP, obj); + new_obj = create_gpi_obj_from_handle(obj, name); + return new_obj; +} + + GpiCbHdl *VhpiImpl::register_timed_callback(uint64_t time_ps) { diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index dc4b4224..4f6b3674 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -154,13 +154,20 @@ class VhpiSignalObjHdl : public GpiSignalObjHdl { m_rising_cb(impl, this, GPI_RISING), m_falling_cb(impl, this, GPI_FALLING), m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } + + VhpiSignalObjHdl(GpiImplInterface *impl, vhpiHandleT hdl, gpi_objtype_t objtype) : + GpiSignalObjHdl(impl, hdl, objtype), + m_size(0), + m_rising_cb(impl, this, GPI_RISING), + m_falling_cb(impl, this, GPI_FALLING), + m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } virtual ~VhpiSignalObjHdl(); const char* get_signal_value_binstr(void); int set_signal_value(const int value); int set_signal_value(std::string &value); - + /* Value change callback accessor */ GpiCbHdl *value_change_cb(unsigned int edge); int initialise(std::string &name); From 15c332f0a4732cf01b9a9062ea913a143cc7fc9a Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 25 Jul 2015 16:41:35 +0100 Subject: [PATCH 0874/2656] Issue #17: Some progress on VHPI iteration - seem to have one nested layer of hierarchy... --- cocotb/handle.py | 37 ++++++++++--------- lib/vhpi/VhpiImpl.cpp | 20 ++++++++-- .../test_iteration/test_iteration.py | 5 ++- 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 85abd345..6b513e83 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -188,6 +188,26 @@ def __setattr__(self, name, value): return object.__setattr__(self, name, value) + def __iter__(self): + """Iterates over all known types defined by simulator module""" + for handle_type in [simulator.MODULE]: + #simulator.PARAMETER, + #simulator.REG, + #simulator.NET, + #simulator.NETARRAY]: + iterator = simulator.iterate(handle_type, self._handle) + print "iterating..." + while True: + try: + thing = simulator.next(iterator) + except StopIteration: + print "Simulator raised stopiteration" + break + print "Got thing %d" % thing + hdl = SimHandle(thing) + self._sub_handles[hdl.name.split(".")[-1]] = hdl + yield hdl + class ConstantObject(SimHandleBase): """ @@ -267,23 +287,6 @@ def __cmp__(self, other): return self.value.__cmp__(other) - def __iter__(self): - """Iterates over all known types defined by simulator module""" - for handle_type in [simulator.MODULE, - simulator.PARAMETER, - simulator.REG, - simulator.NET, - simulator.NETARRAY]: - iterator = simulator.iterate(handle_type, self._handle) - while True: - try: - thing = simulator.next(iterator) - except StopIteration: - break - hdl = SimHandle(thing) - self._sub_handles[hdl.name.split(".")[-1]] = hdl - yield hdl - def __int__(self): return int(self.value) diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 0bfa9182..c0d73c21 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -279,7 +279,15 @@ GpiIterator *VhpiImpl::iterate_handle(uint32_t type, GpiObjHdl *obj_hdl) vhpiHandleT vhpi_hdl = obj_hdl->get_handle(); vhpiHandleT iterator; - iterator = vhpi_iterator(vhpiSignals, vhpi_hdl); + iterator = vhpi_iterator(vhpiInternalRegions, vhpi_hdl); + + if (NULL==iterator) { + LOG_WARN("Attemt to create vhpi_iterator returned NULL"); + } + + LOG_WARN("Created iterator working from scope %d (%s)", + vhpi_get(vhpiKindP, vhpi_hdl), + vhpi_get_str(vhpiKindStrP, vhpi_hdl)); GpiIterator *new_iter; new_iter = new GpiIterator(this, iterator); @@ -293,10 +301,16 @@ GpiObjHdl *VhpiImpl::next_handle(GpiIterator *iter) obj = vhpi_scan((vhpiHandleT)iter->m_iter_hdl); - if (NULL==obj) + if (NULL==obj) { + LOG_WARN("vhpi_scan returned NULL"); return new_obj; + } - std::string name = vhpi_get_str(vhpiFullNameP, obj); + std::string name = vhpi_get_str(vhpiNameP, obj); + LOG_WARN("vhpi_scan found %s (%d) kind:%s name:%s", name.c_str(), + vhpi_get(vhpiKindP, obj), + vhpi_get_str(vhpiKindStrP, obj), + vhpi_get_str(vhpiFullNameP, obj)); new_obj = create_gpi_obj_from_handle(obj, name); return new_obj; } diff --git a/tests/test_cases/test_iteration/test_iteration.py b/tests/test_cases/test_iteration/test_iteration.py index e18c2802..a6c1d02c 100644 --- a/tests/test_cases/test_iteration/test_iteration.py +++ b/tests/test_cases/test_iteration/test_iteration.py @@ -34,5 +34,6 @@ def discovery_all(dut): yield Timer(0) for thing in dut: thing.log.info("Found something: %s" % thing.fullname) - - clk = dut.aclk \ No newline at end of file + for subthing in thing: + thing.log.info("Found something: %s" % thing.fullname) + yield Timer(100) \ No newline at end of file From 01c053edf65366b0f8660e06dbff105b6c97a9f8 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 25 Jul 2015 17:24:32 +0100 Subject: [PATCH 0875/2656] Issue #17: Experiment with VHPI iteration and hierarchy --- cocotb/handle.py | 26 +++++++++++++++++-- lib/vhpi/VhpiImpl.cpp | 14 +++++++++- .../test_iteration/test_iteration.py | 5 +++- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 6b513e83..2461688f 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -203,9 +203,31 @@ def __iter__(self): except StopIteration: print "Simulator raised stopiteration" break - print "Got thing %d" % thing + name = simulator.get_name_string(thing) + print "Got thing %s hdl = %d" % (name, thing) hdl = SimHandle(thing) - self._sub_handles[hdl.name.split(".")[-1]] = hdl + + # This is slightly hacky, but we want generate loops to result in a list + # These are renamed in VHPI to __X where X is the index + import re + result = re.match("(?P.*)__(?P\d+)$", name) + if result: + print "Detected generate unrolling" + index = int(result.group("index")) + name = result.group("name") + print "index is %d" % int(index) + + if name not in self._sub_handles: + self._sub_handles[name] = [] + print "Created subhandle for %s" % name + if len(self._sub_handles[name]) < index + 1: + delta = index - len(self._sub_handles[name]) + 1 + print "extending list by %d" % delta + self._sub_handles[name].extend([None]*delta) + print "length is %d" % (len(self._sub_handles[name])) + self._sub_handles[name][index] = hdl + else: + self._sub_handles[hdl.name.split(".")[-1]] = hdl yield hdl diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index c0d73c21..88904268 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -138,6 +138,7 @@ gpi_objtype_t to_gpi_objtype(int32_t vhpitype) case vhpiCompInstStmtK: case vhpiEntityDeclK: case vhpiRootInstK: + case vhpiProcessStmtK: return GPI_MODULE; default: @@ -167,10 +168,11 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string case vhpiForGenerateK: case vhpiIfGenerateK: case vhpiCompInstStmtK: +// case vhpiProcessStmtK: new_obj = new GpiObjHdl(this, new_hdl, to_gpi_objtype(type)); break; default: - LOG_WARN("Not able to map type %d to object."); + LOG_WARN("Not able to map type (%s) %u to object", vhpi_get_str(vhpiKindStrP, new_hdl), type); return NULL; } @@ -280,6 +282,7 @@ GpiIterator *VhpiImpl::iterate_handle(uint32_t type, GpiObjHdl *obj_hdl) vhpiHandleT iterator; iterator = vhpi_iterator(vhpiInternalRegions, vhpi_hdl); + vhpiHandleT root_iterator; if (NULL==iterator) { LOG_WARN("Attemt to create vhpi_iterator returned NULL"); @@ -289,6 +292,15 @@ GpiIterator *VhpiImpl::iterate_handle(uint32_t type, GpiObjHdl *obj_hdl) vhpi_get(vhpiKindP, vhpi_hdl), vhpi_get_str(vhpiKindStrP, vhpi_hdl)); + // HACK: vhpiRootInstK seems to be a null level of hierarchy, need to skip + if (vhpiRootInstK == vhpi_get(vhpiKindP, vhpi_hdl)) { + vhpi_hdl = vhpi_scan(iterator); + root_iterator = vhpi_iterator(vhpiInternalRegions, vhpi_hdl); + iterator = root_iterator; // FIXME leaky + LOG_WARN("Skipped vhpiRootInstK to get to %s", vhpi_get_str(vhpiKindStrP, vhpi_hdl)); + } + + GpiIterator *new_iter; new_iter = new GpiIterator(this, iterator); return new_iter; diff --git a/tests/test_cases/test_iteration/test_iteration.py b/tests/test_cases/test_iteration/test_iteration.py index a6c1d02c..7ca52af4 100644 --- a/tests/test_cases/test_iteration/test_iteration.py +++ b/tests/test_cases/test_iteration/test_iteration.py @@ -36,4 +36,7 @@ def discovery_all(dut): thing.log.info("Found something: %s" % thing.fullname) for subthing in thing: thing.log.info("Found something: %s" % thing.fullname) - yield Timer(100) \ No newline at end of file + + thing._log.info("length of dut.inst_acs is %d" % len(dut.gen_acs)) + item = dut.gen_acs[3] + item._log.info("this is item") \ No newline at end of file From 69e1b103896c989684578d6e06a247d7bca8f5b2 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 26 Jul 2015 17:58:52 +0100 Subject: [PATCH 0876/2656] Issue#17: Remove old #defines and use gpi_objtype_e thoughtout GPI layer/simulator/python mappings. Implement empty FLI iteration --- cocotb/handle.py | 4 +-- include/gpi.h | 9 +----- lib/fli/FliImpl.cpp | 11 ++++++++ lib/fli/FliImpl.h | 6 ++-- lib/gpi/GpiCbHdl.cpp | 28 ++++++++++++++++--- lib/gpi/GpiCommon.cpp | 2 +- lib/gpi/gpi_priv.h | 19 ++++++------- lib/simulator/simulatormodule_python2.c | 15 ++++++---- lib/simulator/simulatormodule_python3.c | 16 +++++++---- lib/vhpi/VhpiImpl.cpp | 2 +- lib/vhpi/VhpiImpl.h | 6 ---- lib/vpi/VpiImpl.h | 5 ---- .../test_iteration/test_iteration.py | 8 +++--- 13 files changed, 76 insertions(+), 55 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 2461688f..2ef6c4de 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -391,8 +391,8 @@ def SimHandle(handle): """ _type2cls = { - 2: HierarchyObject, - 5: ModifiableObject, + simulator.MODULE: HierarchyObject, + simulator.REG: ModifiableObject, } t = simulator.get_type(handle) diff --git a/include/gpi.h b/include/gpi.h index 0aa1dbaf..8b8bf374 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -136,13 +136,6 @@ typedef enum gpi_objtype_e { GPI_STRUCTURE = 8, } gpi_objtype_t; -#define gpiMemory 29 -#define gpiModule 32 -#define gpiNet 36 -#define gpiParameter 41 -#define gpiReg 48 -#define gpiNetArray 114 - // Functions for iterating over entries of a handle // Returns an iterator handle which can then be used in gpi_next calls // @@ -162,7 +155,7 @@ const char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl); const char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl); // Returns on of the types defined above e.g. gpiMemory etc. -int gpi_get_object_type(gpi_sim_hdl gpi_hdl); +gpi_objtype_t gpi_get_object_type(gpi_sim_hdl gpi_hdl); // Functions for setting the properties of a handle diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 939710a1..82251e41 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -610,6 +610,17 @@ int FliVariableObjHdl::initialise(std::string &name) return 0; } +GpiIterator *FliImpl::iterate_handle(uint32_t type, GpiObjHdl *obj_hdl) +{ + return NULL; +} + +GpiObjHdl *FliImpl::next_handle(GpiIterator *iter) +{ + return NULL; +} + + #include FliTimedCbHdl* FliTimerCache::get_timer(uint64_t time_ps) diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index e2593ffc..2ab65b6f 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -73,7 +73,7 @@ class FliSignalCbHdl : public FliProcessCbHdl, public GpiValueCbHdl { class FliSignalObjHdl : public GpiSignalObjHdl { public: - FliSignalObjHdl(GpiImplInterface *impl, mtiSignalIdT hdl) : GpiSignalObjHdl(impl, hdl), + FliSignalObjHdl(GpiImplInterface *impl, mtiSignalIdT hdl) : GpiSignalObjHdl(impl, hdl, GPI_UNKNOWN), m_fli_hdl(hdl), m_rising_cb(impl, this, GPI_RISING), m_falling_cb(impl, this, GPI_FALLING), @@ -110,7 +110,7 @@ class FliSignalObjHdl : public GpiSignalObjHdl { class FliVariableObjHdl : public GpiSignalObjHdl { public: - FliVariableObjHdl(GpiImplInterface *impl, mtiVariableIdT hdl) : GpiSignalObjHdl(impl, hdl), + FliVariableObjHdl(GpiImplInterface *impl, mtiVariableIdT hdl) : GpiSignalObjHdl(impl, hdl, GPI_UNKNOWN), m_fli_hdl(hdl), m_fli_type(MTI_TYPE_SCALAR), m_mti_buff(NULL), @@ -228,6 +228,8 @@ class FliImpl : public GpiImplInterface { GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); GpiObjHdl *get_root_handle(const char *name); + GpiIterator *iterate_handle(uint32_t type, GpiObjHdl *obj_hdl); + GpiObjHdl *next_handle(GpiIterator *iter); /* Callback related, these may (will) return the same handle*/ GpiCbHdl *register_timed_callback(uint64_t time_ps); diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index 10c9831a..4054be47 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -45,12 +45,33 @@ const char * GpiObjHdl::get_fullname_str(void) const char * GpiObjHdl::get_type_str(void) { - return m_type.c_str(); +#define CASE_OPTION(_X) \ + case _X: \ + ret = #_X; \ + break + + const char *ret; + + switch (m_type) { + CASE_OPTION(GPI_UNKNOWN); + CASE_OPTION(GPI_MEMORY); + CASE_OPTION(GPI_MODULE); + CASE_OPTION(GPI_NET); + CASE_OPTION(GPI_PARAMETER); + CASE_OPTION(GPI_REGISTER); + CASE_OPTION(GPI_ARRAY); + CASE_OPTION(GPI_ENUM); + CASE_OPTION(GPI_STRUCTURE); + default: + ret = "unknown"; + } + + return ret; } -int GpiObjHdl::get_type(void) +gpi_objtype_e GpiObjHdl::get_type(void) { - return m_type_e; + return m_type; } const std::string & GpiObjHdl::get_name(void) @@ -101,7 +122,6 @@ int GpiObjHdl::initialise(std::string &name) printf("Name is %s\n", name.c_str()); m_name = name; m_fullname = "bleh2"; - m_type = "unknown"; return 0; } diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index a7dbc514..89790dcb 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -268,7 +268,7 @@ const char *gpi_get_signal_type_str(gpi_sim_hdl sig_hdl) return obj_hdl->get_type_str(); } -int gpi_get_object_type(gpi_sim_hdl sig_hdl) +gpi_objtype_t gpi_get_object_type(gpi_sim_hdl sig_hdl) { GpiObjHdl *obj_hdl = sim_to_hdl(sig_hdl); return obj_hdl->get_type(); diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 3f31d4af..35638ee6 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -94,18 +94,20 @@ class GpiObjHdl : public GpiHdl { public: GpiObjHdl(std::string name) : GpiHdl(NULL, NULL), m_name(name), - m_fullname("bleh"), - m_type("unknown") { } - GpiObjHdl(GpiImplInterface *impl) : GpiHdl(impl, NULL) { } - GpiObjHdl(GpiImplInterface *impl, void *hdl) : GpiHdl(impl, hdl) { } - GpiObjHdl(GpiImplInterface *impl, void *hdl, gpi_objtype_t objtype) : GpiHdl(impl, hdl), m_type_e(objtype) { } + m_fullname("unknown") { } + GpiObjHdl(GpiImplInterface *impl) : GpiHdl(impl, NULL), + m_fullname("unknown"), + m_type(GPI_UNKNOWN) { } + GpiObjHdl(GpiImplInterface *impl, void *hdl, gpi_objtype_t objtype) : GpiHdl(impl, hdl), + m_fullname("unknown"), + m_type(objtype) { } virtual ~GpiObjHdl() { } virtual const char* get_name_str(void); virtual const char* get_fullname_str(void); virtual const char* get_type_str(void); - virtual int get_type(void); + virtual gpi_objtype_t get_type(void); const std::string & get_name(void); @@ -115,8 +117,7 @@ class GpiObjHdl : public GpiHdl { protected: std::string m_name; std::string m_fullname; - std::string m_type; - int m_type_e; + gpi_objtype_t m_type; }; @@ -126,8 +127,6 @@ class GpiObjHdl : public GpiHdl { // value of the signal (which doesn't apply to non signal items in the hierarchy class GpiSignalObjHdl : public GpiObjHdl { public: - GpiSignalObjHdl(GpiImplInterface *impl, void *hdl) : GpiObjHdl(impl, hdl), - m_length(0) { } GpiSignalObjHdl(GpiImplInterface *impl, void *hdl, gpi_objtype_t objtype) : GpiObjHdl(impl, hdl, objtype), m_length(0) { } diff --git a/lib/simulator/simulatormodule_python2.c b/lib/simulator/simulatormodule_python2.c index aeed2271..64d7dffc 100644 --- a/lib/simulator/simulatormodule_python2.c +++ b/lib/simulator/simulatormodule_python2.c @@ -28,12 +28,15 @@ MODULE_ENTRY_POINT(void) // Make the GPI constants accessible from the C world int rc = 0; - rc |= PyModule_AddIntConstant(simulator, "MEMORY", gpiMemory); - rc |= PyModule_AddIntConstant(simulator, "MODULE", gpiModule); - rc |= PyModule_AddIntConstant(simulator, "PARAMETER", gpiParameter); - rc |= PyModule_AddIntConstant(simulator, "REG", gpiReg); - rc |= PyModule_AddIntConstant(simulator, "NET", gpiNet); - rc |= PyModule_AddIntConstant(simulator, "NETARRAY", gpiNetArray); + rc |= PyModule_AddIntConstant(simulator, "UNKNOWN", GPI_UNKNOWN); + rc |= PyModule_AddIntConstant(simulator, "MEMORY", GPI_MEMORY); + rc |= PyModule_AddIntConstant(simulator, "MODULE", GPI_MODULE); + rc |= PyModule_AddIntConstant(simulator, "NET", GPI_NET); + rc |= PyModule_AddIntConstant(simulator, "PARAMETER", GPI_PARAMETER); + rc |= PyModule_AddIntConstant(simulator, "REG", GPI_REGISTER); + rc |= PyModule_AddIntConstant(simulator, "NETARRAY", GPI_ARRAY); + rc |= PyModule_AddIntConstant(simulator, "ENUM", GPI_ENUM); + rc |= PyModule_AddIntConstant(simulator, "ENUM", GPI_STRUCTURE); if (rc != 0) fprintf(stderr, "Failed to add module constants!\n"); } diff --git a/lib/simulator/simulatormodule_python3.c b/lib/simulator/simulatormodule_python3.c index 2fbdac4e..4dcccf8d 100644 --- a/lib/simulator/simulatormodule_python3.c +++ b/lib/simulator/simulatormodule_python3.c @@ -47,14 +47,18 @@ MODULE_ENTRY_POINT(void) // Make the GPI constants accessible from the C world int rc = 0; - rc |= PyModule_AddIntConstant(simulator, "MEMORY", gpiMemory); - rc |= PyModule_AddIntConstant(simulator, "MODULE", gpiModule); - rc |= PyModule_AddIntConstant(simulator, "PARAMETER", gpiParameter); - rc |= PyModule_AddIntConstant(simulator, "REG", gpiReg); - rc |= PyModule_AddIntConstant(simulator, "NET", gpiNet); - rc |= PyModule_AddIntConstant(simulator, "NETARRAY", gpiNetArray); + rc |= PyModule_AddIntConstant(simulator, "UNKNOWN", GPI_UNKNOWN); + rc |= PyModule_AddIntConstant(simulator, "MEMORY", GPI_MEMORY); + rc |= PyModule_AddIntConstant(simulator, "MODULE", GPI_MODULE); + rc |= PyModule_AddIntConstant(simulator, "NET", GPI_NET); + rc |= PyModule_AddIntConstant(simulator, "PARAMETER", GPI_PARAMETER); + rc |= PyModule_AddIntConstant(simulator, "REG", GPI_REGISTER); + rc |= PyModule_AddIntConstant(simulator, "NETARRAY", GPI_ARRAY); + rc |= PyModule_AddIntConstant(simulator, "ENUM", GPI_ENUM); + rc |= PyModule_AddIntConstant(simulator, "ENUM", GPI_STRUCTURE); if (rc != 0) fprintf(stderr, "Failed to add module constants!\n"); return simulator; } + diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 88904268..c1ddea83 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -111,7 +111,7 @@ void VhpiImpl::get_sim_time(uint32_t *high, uint32_t *low) } -gpi_objtype_t to_gpi_objtype(int32_t vhpitype) +gpi_objtype_t to_gpi_objtype(vhpiIntT vhpitype) { switch (vhpitype) { case vhpiPortDeclK: diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 4f6b3674..f0f61978 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -149,12 +149,6 @@ class VhpiReadwriteCbHdl : public VhpiCbHdl { class VhpiSignalObjHdl : public GpiSignalObjHdl { public: - VhpiSignalObjHdl(GpiImplInterface *impl, vhpiHandleT hdl) : GpiSignalObjHdl(impl, hdl), - m_size(0), - m_rising_cb(impl, this, GPI_RISING), - m_falling_cb(impl, this, GPI_FALLING), - m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } - VhpiSignalObjHdl(GpiImplInterface *impl, vhpiHandleT hdl, gpi_objtype_t objtype) : GpiSignalObjHdl(impl, hdl, objtype), m_size(0), diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index d27aa578..980e15ca 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -173,11 +173,6 @@ class VpiShutdownCbHdl : public VpiCbHdl { class VpiSignalObjHdl : public GpiSignalObjHdl { public: - VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl) : GpiSignalObjHdl(impl, hdl), - m_rising_cb(impl, this, GPI_RISING), - m_falling_cb(impl, this, GPI_FALLING), - m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } - VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl, gpi_objtype_t objtype) : GpiSignalObjHdl(impl, hdl, objtype), m_rising_cb(impl, this, GPI_RISING), diff --git a/tests/test_cases/test_iteration/test_iteration.py b/tests/test_cases/test_iteration/test_iteration.py index 7ca52af4..ce69ea22 100644 --- a/tests/test_cases/test_iteration/test_iteration.py +++ b/tests/test_cases/test_iteration/test_iteration.py @@ -30,12 +30,12 @@ @cocotb.test() def discovery_all(dut): - dut.log.info("Trying to discover") + dut._log.info("Trying to discover") yield Timer(0) for thing in dut: - thing.log.info("Found something: %s" % thing.fullname) - for subthing in thing: - thing.log.info("Found something: %s" % thing.fullname) + thing._log.info("Found something: %s" % thing._fullname) + #for subthing in thing: + # thing._log.info("Found something: %s" % thing._fullname) thing._log.info("length of dut.inst_acs is %d" % len(dut.gen_acs)) item = dut.gen_acs[3] From 2419255931d98a927656e1b484853154f670629e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 27 Jul 2015 22:17:39 +0100 Subject: [PATCH 0877/2656] Issue#17: Derive VhpiIterator from GpiIterator and move functionality from VhpiImpl to this. Then add ability to loop over types of relationship --- lib/gpi/gpi_priv.h | 10 ++-- lib/vhpi/VhpiCbHdl.cpp | 101 +++++++++++++++++++++++++++++++++++++++++ lib/vhpi/VhpiImpl.cpp | 45 ++---------------- lib/vhpi/VhpiImpl.h | 17 ++++++- 4 files changed, 124 insertions(+), 49 deletions(-) diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 35638ee6..46b5df04 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -194,18 +194,14 @@ class GpiClockHdl { int stop_clock(void) { return 0; } }; -class GpiIterator { +class GpiIterator : public GpiHdl { public: - GpiIterator(GpiImplInterface *impl, void *hdl) : m_impl(impl), m_iter_hdl(hdl) { } + GpiIterator(GpiImplInterface *impl, void *hdl) : GpiHdl(impl, hdl) { } virtual ~GpiIterator() { } - GpiObjHdl* next_handle(); - -private: - GpiIterator() { } // Disable default constructor + virtual GpiObjHdl* next_handle() { return NULL; } public: - GpiImplInterface *m_impl; // VPI/VHPI/FLI routines void *m_iter_hdl; }; diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 6eef65f0..5d59113d 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -382,3 +382,104 @@ VhpiNextPhaseCbHdl::VhpiNextPhaseCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), cb_data.reason = vhpiCbRepNextTimeStep; cb_data.time = &vhpi_time; } + +static vhpiOneToManyT options[] = { + vhpiInternalRegions, + vhpiSignals, + vhpiMembers +}; + +static int options_len = sizeof(options) / sizeof(options[0]); + +VhpiIterator::VhpiIterator(GpiImplInterface *impl, vhpiHandleT hdl) : GpiIterator(impl, hdl), + iterate_over(options, + options + options_len) +{ + vhpiHandleT iterator; + + /* Find the first mapping type that yields a valid iterator */ + + for (curr_type = iterate_over.begin(); + curr_type != iterate_over.end(); + curr_type++) { + iterator = vhpi_iterator(*curr_type, hdl); + + if (iterator) + break; + + LOG_WARN("vhpi_scan vhpiOneToManyT=%d returned NULL", *curr_type); + } + + if (NULL == iterator) { + LOG_WARN("vhpi_iterate returned NULL for all relationships"); + return; + } + + LOG_WARN("Created iterator working from scope %d (%s)", + vhpi_get(vhpiKindP, hdl), + vhpi_get_str(vhpiKindStrP, hdl)); + + // HACK: vhpiRootInstK seems to be a null level of hierarchy, need to skip + if (vhpiRootInstK == vhpi_get(vhpiKindP, hdl)) { + vhpiHandleT root_iterator; + hdl = vhpi_scan(iterator); + root_iterator = vhpi_iterator(*curr_type, hdl); + vhpi_release_handle(iterator); + iterator = root_iterator; + LOG_WARN("Skipped vhpiRootInstK to get to %s", vhpi_get_str(vhpiKindStrP, hdl)); + } + + m_iterator = iterator; +} + +VhpiIterator::~VhpiIterator() +{ + vhpi_release_handle(m_iterator); +} + +GpiObjHdl *VhpiIterator::next_handle(void) +{ + vhpiHandleT obj; + GpiObjHdl *new_obj = NULL; + + /* We want the next object in the current mapping. + * If the end of mapping is reached then we want to + * try then next one until a new object is found + */ + + do { + obj = NULL; + + if (m_iterator) { + obj = vhpi_scan(m_iterator); + + if (obj) + continue; + + LOG_WARN("End of vhpiOneToManyT=%d iteration", *curr_type); + } else { + LOG_WARN("No valid vhpiOneToManyT=%d iterator", *curr_type); + } + + curr_type++; + vhpi_release_handle(m_iterator); + m_iterator = vhpi_iterator(*curr_type, get_handle()); + + } while (!obj && (curr_type != iterate_over.end())); + + if (NULL == obj) { + LOG_WARN("No more children, all relationships tested"); + return new_obj; + } + + std::string name = vhpi_get_str(vhpiNameP, obj); + LOG_WARN("vhpi_scan found %s (%d) kind:%s name:%s", name.c_str(), + vhpi_get(vhpiKindP, obj), + vhpi_get_str(vhpiKindStrP, obj), + vhpi_get_str(vhpiFullNameP, obj)); + + VhpiImpl *vhpi_impl = reinterpret_cast(m_impl); + new_obj = vhpi_impl->create_gpi_obj_from_handle(obj, name); + return new_obj; +} + diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index c1ddea83..e00b8683 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -168,7 +168,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string case vhpiForGenerateK: case vhpiIfGenerateK: case vhpiCompInstStmtK: -// case vhpiProcessStmtK: + case vhpiProcessStmtK: new_obj = new GpiObjHdl(this, new_hdl, to_gpi_objtype(type)); break; default: @@ -278,53 +278,16 @@ GpiObjHdl *VhpiImpl::get_root_handle(const char* name) GpiIterator *VhpiImpl::iterate_handle(uint32_t type, GpiObjHdl *obj_hdl) { + VhpiIterator *new_iter; vhpiHandleT vhpi_hdl = obj_hdl->get_handle(); - vhpiHandleT iterator; - iterator = vhpi_iterator(vhpiInternalRegions, vhpi_hdl); - vhpiHandleT root_iterator; - - if (NULL==iterator) { - LOG_WARN("Attemt to create vhpi_iterator returned NULL"); - } - - LOG_WARN("Created iterator working from scope %d (%s)", - vhpi_get(vhpiKindP, vhpi_hdl), - vhpi_get_str(vhpiKindStrP, vhpi_hdl)); - - // HACK: vhpiRootInstK seems to be a null level of hierarchy, need to skip - if (vhpiRootInstK == vhpi_get(vhpiKindP, vhpi_hdl)) { - vhpi_hdl = vhpi_scan(iterator); - root_iterator = vhpi_iterator(vhpiInternalRegions, vhpi_hdl); - iterator = root_iterator; // FIXME leaky - LOG_WARN("Skipped vhpiRootInstK to get to %s", vhpi_get_str(vhpiKindStrP, vhpi_hdl)); - } - - - GpiIterator *new_iter; - new_iter = new GpiIterator(this, iterator); + new_iter = new VhpiIterator(this, vhpi_hdl); return new_iter; } GpiObjHdl *VhpiImpl::next_handle(GpiIterator *iter) { - vhpiHandleT obj; - GpiObjHdl *new_obj = NULL; - - obj = vhpi_scan((vhpiHandleT)iter->m_iter_hdl); - - if (NULL==obj) { - LOG_WARN("vhpi_scan returned NULL"); - return new_obj; - } - - std::string name = vhpi_get_str(vhpiNameP, obj); - LOG_WARN("vhpi_scan found %s (%d) kind:%s name:%s", name.c_str(), - vhpi_get(vhpiKindP, obj), - vhpi_get_str(vhpiKindStrP, obj), - vhpi_get_str(vhpiFullNameP, obj)); - new_obj = create_gpi_obj_from_handle(obj, name); - return new_obj; + return iter->next_handle(); } diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index f0f61978..f5095312 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -30,6 +30,7 @@ #include "../gpi/gpi_priv.h" #include +#include // Should be run after every VHPI call to check error status static inline int __check_vhpi_error(const char *func, long line) @@ -176,6 +177,20 @@ class VhpiSignalObjHdl : public GpiSignalObjHdl { VhpiValueCbHdl m_either_cb; }; +class VhpiIterator : public GpiIterator { +public: + VhpiIterator(GpiImplInterface *impl, vhpiHandleT hdl); + + virtual ~VhpiIterator(); + + GpiObjHdl *next_handle(void); + +private: + vhpiHandleT m_iterator; + std::vector iterate_over; + std::vector::iterator curr_type; +}; + class VhpiImpl : public GpiImplInterface { public: VhpiImpl(const std::string& name) : GpiImplInterface(name), @@ -204,8 +219,8 @@ class VhpiImpl : public GpiImplInterface { const char * reason_to_string(int reason); const char * format_to_string(int format); -private: GpiObjHdl *create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string &name); +private: VhpiReadwriteCbHdl m_read_write; VhpiNextPhaseCbHdl m_next_phase; VhpiReadOnlyCbHdl m_read_only; From bb23c5c747742700c160b7efa785a7a1fafb3dc0 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 27 Jul 2015 22:26:13 +0100 Subject: [PATCH 0878/2656] Issue#17: Odd merge problem --- lib/gpi/GpiCbHdl.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index 4054be47..5e543467 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -27,11 +27,6 @@ ******************************************************************************/ #include "gpi_priv.h" -GpiObjHdl* GpiIterator::next_handle() -{ - return m_impl->next_handle(this); -} - const char * GpiObjHdl::get_name_str(void) { From 4459c3f98f936e8d146e0e97f05885a6bd43f073 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 28 Jul 2015 20:11:57 +0100 Subject: [PATCH 0879/2656] Issue #17: Remove type from simulator.iterate and implementation libs --- cocotb/handle.py | 73 +++++++++++++++------------------ include/gpi.h | 2 +- lib/fli/FliImpl.cpp | 2 +- lib/fli/FliImpl.h | 2 +- lib/gpi/GpiCommon.cpp | 4 +- lib/gpi/gpi_priv.h | 2 +- lib/simulator/simulatormodule.c | 5 +-- lib/vhpi/VhpiImpl.cpp | 2 +- lib/vhpi/VhpiImpl.h | 2 +- lib/vpi/VpiImpl.cpp | 2 +- lib/vpi/VpiImpl.h | 2 +- 11 files changed, 46 insertions(+), 52 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 2ef6c4de..2f6d89f1 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -190,45 +190,40 @@ def __setattr__(self, name, value): def __iter__(self): """Iterates over all known types defined by simulator module""" - for handle_type in [simulator.MODULE]: - #simulator.PARAMETER, - #simulator.REG, - #simulator.NET, - #simulator.NETARRAY]: - iterator = simulator.iterate(handle_type, self._handle) - print "iterating..." - while True: - try: - thing = simulator.next(iterator) - except StopIteration: - print "Simulator raised stopiteration" - break - name = simulator.get_name_string(thing) - print "Got thing %s hdl = %d" % (name, thing) - hdl = SimHandle(thing) - - # This is slightly hacky, but we want generate loops to result in a list - # These are renamed in VHPI to __X where X is the index - import re - result = re.match("(?P.*)__(?P\d+)$", name) - if result: - print "Detected generate unrolling" - index = int(result.group("index")) - name = result.group("name") - print "index is %d" % int(index) - - if name not in self._sub_handles: - self._sub_handles[name] = [] - print "Created subhandle for %s" % name - if len(self._sub_handles[name]) < index + 1: - delta = index - len(self._sub_handles[name]) + 1 - print "extending list by %d" % delta - self._sub_handles[name].extend([None]*delta) - print "length is %d" % (len(self._sub_handles[name])) - self._sub_handles[name][index] = hdl - else: - self._sub_handles[hdl.name.split(".")[-1]] = hdl - yield hdl + iterator = simulator.iterate(self._handle) + print "iterating..." + while True: + try: + thing = simulator.next(iterator) + except StopIteration: + print "Simulator raised stopiteration" + break + name = simulator.get_name_string(thing) + print "Got thing %s hdl = %d" % (name, thing) + hdl = SimHandle(thing) + + # This is slightly hacky, but we want generate loops to result in a list + # These are renamed in VHPI to __X where X is the index + import re + result = re.match("(?P.*)__(?P\d+)$", name) + if result: + print "Detected generate unrolling" + index = int(result.group("index")) + name = result.group("name") + print "index is %d" % int(index) + + if name not in self._sub_handles: + self._sub_handles[name] = [] + print "Created subhandle for %s" % name + if len(self._sub_handles[name]) < index + 1: + delta = index - len(self._sub_handles[name]) + 1 + print "extending list by %d" % delta + self._sub_handles[name].extend([None]*delta) + print "length is %d" % (len(self._sub_handles[name])) + self._sub_handles[name][index] = hdl + else: + self._sub_handles[hdl.name.split(".")[-1]] = hdl + yield hdl class ConstantObject(SimHandleBase): diff --git a/include/gpi.h b/include/gpi.h index 8b8bf374..6eda67ac 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -141,7 +141,7 @@ typedef enum gpi_objtype_e { // // NB the iterator handle may be NULL if no objects of the requested type are // found -gpi_iterator_hdl gpi_iterate(uint32_t type, gpi_sim_hdl base); +gpi_iterator_hdl gpi_iterate(gpi_sim_hdl base); // Returns NULL when there are no more objects gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator); diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 82251e41..e2c48039 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -610,7 +610,7 @@ int FliVariableObjHdl::initialise(std::string &name) return 0; } -GpiIterator *FliImpl::iterate_handle(uint32_t type, GpiObjHdl *obj_hdl) +GpiIterator *FliImpl::iterate_handle(GpiObjHdl *obj_hdl) { return NULL; } diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index 2ab65b6f..216dffd8 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -228,7 +228,7 @@ class FliImpl : public GpiImplInterface { GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); GpiObjHdl *get_root_handle(const char *name); - GpiIterator *iterate_handle(uint32_t type, GpiObjHdl *obj_hdl); + GpiIterator *iterate_handle(GpiObjHdl *obj_hdl); GpiObjHdl *next_handle(GpiIterator *iter); /* Callback related, these may (will) return the same handle*/ diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 89790dcb..ac6c9c03 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -234,10 +234,10 @@ gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) return NULL; } -gpi_iterator_hdl gpi_iterate(uint32_t type, gpi_sim_hdl base) +gpi_iterator_hdl gpi_iterate(gpi_sim_hdl base) { GpiObjHdl *obj_hdl = sim_to_hdl(base); - GpiIterator *iter = obj_hdl->m_impl->iterate_handle(type, obj_hdl); + GpiIterator *iter = obj_hdl->m_impl->iterate_handle(obj_hdl); if (!iter) { return NULL; } diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 46b5df04..6cbacd49 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -221,7 +221,7 @@ class GpiImplInterface { virtual GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent) = 0; virtual GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent) = 0; virtual GpiObjHdl *get_root_handle(const char *name) = 0; - virtual GpiIterator *iterate_handle(uint32_t type, GpiObjHdl *obj_hdl) = 0; + virtual GpiIterator *iterate_handle(GpiObjHdl *obj_hdl) = 0; virtual GpiObjHdl *next_handle(GpiIterator *iter) = 0; /* Callback related, these may (will) return the same handle*/ diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 023ff788..172eb965 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -466,19 +466,18 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) static PyObject *iterate(PyObject *self, PyObject *args) { gpi_sim_hdl hdl; - uint32_t type; gpi_iterator_hdl result; PyObject *res; PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "il", &type, &hdl)) { + if (!PyArg_ParseTuple(args, "l", &hdl)) { DROP_GIL(gstate); return NULL; } - result = gpi_iterate(type, hdl); + result = gpi_iterate(hdl); res = Py_BuildValue("l", result); diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index e00b8683..e1e99396 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -276,7 +276,7 @@ GpiObjHdl *VhpiImpl::get_root_handle(const char* name) return rv; } -GpiIterator *VhpiImpl::iterate_handle(uint32_t type, GpiObjHdl *obj_hdl) +GpiIterator *VhpiImpl::iterate_handle(GpiObjHdl *obj_hdl) { VhpiIterator *new_iter; vhpiHandleT vhpi_hdl = obj_hdl->get_handle(); diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index f5095312..0c0e2c77 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -204,7 +204,7 @@ class VhpiImpl : public GpiImplInterface { /* Hierachy related */ GpiObjHdl *get_root_handle(const char *name); - GpiIterator *iterate_handle(uint32_t type, GpiObjHdl *obj_hdl); + GpiIterator *iterate_handle(GpiObjHdl *obj_hdl); GpiObjHdl *next_handle(GpiIterator *iter); /* Callback related, these may (will) return the same handle*/ diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 8ab6630a..820ee27c 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -242,7 +242,7 @@ GpiObjHdl *VpiImpl::get_root_handle(const char* name) return NULL; } -GpiIterator *VpiImpl::iterate_handle(uint32_t type, GpiObjHdl *obj_hdl) +GpiIterator *VpiImpl::iterate_handle(GpiObjHdl *obj_hdl) { vpiHandle vpi_hdl = obj_hdl->get_handle(); diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 980e15ca..2e44b8f8 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -207,7 +207,7 @@ class VpiImpl : public GpiImplInterface { /* Hierachy related */ GpiObjHdl *get_root_handle(const char *name); - GpiIterator *iterate_handle(uint32_t type, GpiObjHdl *obj_hdl); + GpiIterator *iterate_handle(GpiObjHdl *obj_hdl); GpiObjHdl *next_handle(GpiIterator *iter); /* Callback related, these may (will) return the same handle*/ From 2173a3a029b9069f642db48d52b5976bf16dada6 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 28 Jul 2015 20:46:49 +0100 Subject: [PATCH 0880/2656] Issue #17: Move the possible types to iterate over to a static vector rather than one per iterator --- lib/vhpi/VhpiCbHdl.cpp | 8 ++++---- lib/vhpi/VhpiImpl.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 5d59113d..a1964949 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -389,11 +389,11 @@ static vhpiOneToManyT options[] = { vhpiMembers }; -static int options_len = sizeof(options) / sizeof(options[0]); +std::vector VhpiIterator::iterate_over(options, options + sizeof(options) / sizeof(options[0])); -VhpiIterator::VhpiIterator(GpiImplInterface *impl, vhpiHandleT hdl) : GpiIterator(impl, hdl), - iterate_over(options, - options + options_len) +VhpiIterator::VhpiIterator(GpiImplInterface *impl, vhpiHandleT hdl) : GpiIterator(impl, hdl) + //iterate_over(options, + // options + options_len) { vhpiHandleT iterator; diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 0c0e2c77..cee45361 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -187,7 +187,7 @@ class VhpiIterator : public GpiIterator { private: vhpiHandleT m_iterator; - std::vector iterate_over; + static std::vector iterate_over; std::vector::iterator curr_type; }; From a67e7a96f482ddff2572130ec0a4bc95316fddbc Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 28 Jul 2015 21:19:40 +0100 Subject: [PATCH 0881/2656] Issue #17: Test doing iteration from two co-routines --- tests/test_cases/test_iteration/test_iteration.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/test_cases/test_iteration/test_iteration.py b/tests/test_cases/test_iteration/test_iteration.py index ce69ea22..e7317fb6 100644 --- a/tests/test_cases/test_iteration/test_iteration.py +++ b/tests/test_cases/test_iteration/test_iteration.py @@ -39,4 +39,17 @@ def discovery_all(dut): thing._log.info("length of dut.inst_acs is %d" % len(dut.gen_acs)) item = dut.gen_acs[3] - item._log.info("this is item") \ No newline at end of file + item._log.info("this is item") + +@cocotb.coroutine +def iteration_loop(dut): + for thing in dut: + thing._log.info("Found something: %s" % thing._fullname) + yield Timer(1) + +@cocotb.test() +def dual_iteration(dut): + loop_one = cocotb.fork(iteration_loop(dut)) + loop_two = cocotb.fork(iteration_loop(dut)) + + yield [loop_one.join(), loop_two.join()] From fd1ad8cb7538df51db4e76b02135995e07d06566 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 28 Jul 2015 20:46:49 -0700 Subject: [PATCH 0882/2656] Fixes #265: Typo in Bus Monitor --- cocotb/monitors/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index 0d3efc24..7a27f767 100644 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -166,7 +166,7 @@ def in_reset(self): if self._reset_n is not None: return not bool(self._reset_n.value.integer) if self._reset is not None: - return bool(self._reset_n.value.integer) + return bool(self._reset.value.integer) return False def __str__(self): From 1fdbe5dc50ca1de8beb17628e56c93afe62ab8e0 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 28 Jul 2015 20:49:00 -0700 Subject: [PATCH 0883/2656] Fixes #266: Add protection to Join() for coroutines that have already finished --- cocotb/decorators.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 4d462061..d981b8da 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -147,7 +147,10 @@ def _finished_cb(self): def join(self): """Return a trigger that will fire when the wrapped coroutine exits""" - return self._join + if self._finished: + return NullTrigger() + else: + return self._join def __nonzero__(self): """Provide boolean testing From b7468a16c88663d315d581d7acbd5f73738f0b3b Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 28 Jul 2015 20:51:42 -0700 Subject: [PATCH 0884/2656] Fixes #267: Removes dependency on simulator radix setting when sending binary string --- lib/fli/FliImpl.cpp | 24 +++++++++++++++++++++--- lib/fli/FliImpl.h | 8 +++++++- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 939710a1..0cdc3db2 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -454,10 +454,10 @@ int FliSignalObjHdl::set_signal_value(const int value) int FliSignalObjHdl::set_signal_value(std::string &value) { int rc; - std::vector writable(value.begin(), value.end()); - writable.push_back('\0'); - rc = mti_ForceSignal(m_fli_hdl, &writable[0], 0, MTI_FORCE_DEPOSIT, -1, -1); + snprintf(m_val_str_buff, m_val_str_len+1, "%d'b%s", m_val_len, value.c_str()); + + rc = mti_ForceSignal(m_fli_hdl, &m_val_str_buff[0], 0, MTI_FORCE_DEPOSIT, -1, -1); if (!rc) { LOG_CRITICAL("Setting signal value failed!\n"); } @@ -477,6 +477,12 @@ int FliSignalObjHdl::initialise(std::string &name) LOG_CRITICAL("Unable to alloc mem for signal read buffer"); } m_val_buff[m_val_len] = '\0'; + m_val_str_len = 3+m_val_len; + m_val_str_buff = (char*)malloc(m_val_str_len+1); + if (!m_val_str_buff) { + LOG_CRITICAL("Unable to alloc mem for signal write buffer"); + } + m_val_str_buff[m_val_str_len] = '\0'; break; case MTI_TYPE_SCALAR: case MTI_TYPE_PHYSICAL: @@ -486,6 +492,12 @@ int FliSignalObjHdl::initialise(std::string &name) LOG_CRITICAL("Unable to alloc mem for signal read buffer"); } m_val_buff[m_val_len] = '\0'; + m_val_str_len = 4+m_val_len; + m_val_str_buff = (char*)malloc(m_val_str_len+1); + if (!m_val_str_buff) { + LOG_CRITICAL("Unable to alloc mem for signal write buffer"); + } + m_val_str_buff[m_val_str_len] = '\0'; break; case MTI_TYPE_ARRAY: m_val_len = mti_TickLength(mti_GetSignalType(m_fli_hdl)); @@ -498,6 +510,12 @@ int FliSignalObjHdl::initialise(std::string &name) if (!m_mti_buff) { LOG_CRITICAL("Unable to alloc mem for signal mti read buffer"); } + m_val_str_len = snprintf(NULL, 0, "%d'b", m_val_len)+m_val_len; + m_val_str_buff = (char*)malloc(m_val_str_len+1); + if (!m_val_str_buff) { + LOG_CRITICAL("Unable to alloc mem for signal write buffer"); + } + m_val_str_buff[m_val_str_len] = '\0'; break; default: LOG_CRITICAL("Unable to handle onject type for %s (%d)", diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index e2593ffc..6947a35f 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -81,12 +81,16 @@ class FliSignalObjHdl : public GpiSignalObjHdl { m_fli_type(MTI_TYPE_SCALAR), m_mti_buff(NULL), m_val_buff(NULL), - m_val_len(0) { } + m_val_str_buff(NULL), + m_val_len(0), + m_val_str_len(0) { } virtual ~FliSignalObjHdl() { if (m_val_len) free(m_val_buff); if (m_mti_buff) free(m_mti_buff); + if (m_val_str_len) + free(m_val_str_buff); } const char* get_signal_value_binstr(void); @@ -105,7 +109,9 @@ class FliSignalObjHdl : public GpiSignalObjHdl { mtiTypeKindT m_fli_type; mtiInt32T *m_mti_buff; char *m_val_buff; + char *m_val_str_buff; int m_val_len; + int m_val_str_len; }; class FliVariableObjHdl : public GpiSignalObjHdl { From f3d1f7d5d34d11edbf223efda9c2aa7f37485e97 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 28 Jul 2015 20:53:08 -0700 Subject: [PATCH 0885/2656] Fixes #269: Sets the number of bits when returning a BinaryValue --- cocotb/handle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 0836f94c..3e7d9186 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -82,7 +82,7 @@ def __getattr__(self, name): """ Query the simulator for a object with the specified name and cache the result to build a tree """ - # python's builtin dir and IPython's dir2 search for these, + # python's builtin dir and IPython's dir2 search for these, # raise an AttributeError to avoid incorrect calls to _raise_testerror if name in ["__methods__","__members__","trait_names","_getAttributeNames"]: raise AttributeError(name) @@ -150,7 +150,7 @@ def __setitem__(self, index, value): self.__getitem__(index).setcachedvalue(value) def getvalue(self): - result = BinaryValue() + result = BinaryValue(bits=len(self)) result.binstr = self._get_value_str() return result From 9c598d4719467a6d0db6ed35cf600be04c15ee9e Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 29 Jul 2015 13:26:25 +0100 Subject: [PATCH 0886/2656] Issue #17: Fix some problems with iteration * Don't create duplicate handles during iteration * Permit IPython tab-completion to work * Only discover handles once --- cocotb/handle.py | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 2f6d89f1..5967976f 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -79,6 +79,7 @@ def __init__(self, handle): self._handle = handle self._len = None self._sub_handles = {} # Dictionary of children + self._discovered = False self._name = simulator.get_name_string(self._handle) self._fullname = self._name + "(%s)" % simulator.get_type_string(self._handle) @@ -141,17 +142,6 @@ def __cmp__(self, other): if self._handle == other._handle: return 0 return 1 - #def __dir__(self): - #return self._sub_handles.keys() - - def _discover_all(self): - for thing in self: - pass - - def _getAttributeNames(self): - """Permits IPython tab completion to work""" - self._discover_all() - return dir(self) def __repr__(self): return self._fullname @@ -189,17 +179,30 @@ def __setattr__(self, name, value): object.__setattr__(self, name, value) def __iter__(self): - """Iterates over all known types defined by simulator module""" + """ + Iterate over all known objects in this layer of hierarchy + """ + if not self._discovered: + self._discover_all() + for handle in self._sub_handles.items(): + yield handle + + def _discover_all(self): + """ + When iterating or performing tab completion, we run through ahead of + time and discover all possible children, populating the _sub_handle + mapping. Hierarchy can't change after elaboration so we only have to + do this once. + """ + if self._discovered: return + iterator = simulator.iterate(self._handle) - print "iterating..." while True: try: thing = simulator.next(iterator) except StopIteration: - print "Simulator raised stopiteration" break name = simulator.get_name_string(thing) - print "Got thing %s hdl = %d" % (name, thing) hdl = SimHandle(thing) # This is slightly hacky, but we want generate loops to result in a list @@ -207,24 +210,24 @@ def __iter__(self): import re result = re.match("(?P.*)__(?P\d+)$", name) if result: - print "Detected generate unrolling" index = int(result.group("index")) name = result.group("name") - print "index is %d" % int(index) if name not in self._sub_handles: self._sub_handles[name] = [] - print "Created subhandle for %s" % name if len(self._sub_handles[name]) < index + 1: delta = index - len(self._sub_handles[name]) + 1 - print "extending list by %d" % delta self._sub_handles[name].extend([None]*delta) - print "length is %d" % (len(self._sub_handles[name])) self._sub_handles[name][index] = hdl else: self._sub_handles[hdl.name.split(".")[-1]] = hdl - yield hdl + self._discovered = True + + def _getAttributeNames(self): + """Permits IPython tab completion to work""" + self._discover_all() + return dir(self) class ConstantObject(SimHandleBase): """ From 3b1657fa1c0d88de461d29adaccf1c902c293a38 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Wed, 29 Jul 2015 13:51:11 +0100 Subject: [PATCH 0887/2656] Issue #17: some more fixes for iteration - don't include process statements in iteration --- cocotb/handle.py | 11 +++++++++-- lib/vhpi/VhpiCbHdl.cpp | 6 ++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 5967976f..81800844 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -184,8 +184,14 @@ def __iter__(self): """ if not self._discovered: self._discover_all() - for handle in self._sub_handles.items(): - yield handle + + for name, handle in self._sub_handles.items(): + if isinstance(handle, list): + for subhdl in handle: + yield subhdl + + else: + yield handle def _discover_all(self): """ @@ -397,3 +403,4 @@ def SimHandle(handle): if t not in _type2cls: raise TestError("Couldn't find a matching object for GPI type %d" % t) return _type2cls[t](handle) + diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index a1964949..d48bce58 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -453,6 +453,12 @@ GpiObjHdl *VhpiIterator::next_handle(void) if (m_iterator) { obj = vhpi_scan(m_iterator); + if (vhpiProcessStmtK == vhpi_get(vhpiKindP, obj)) { + LOG_WARN("Skipping %s (%s)", vhpi_get_str(vhpiFullNameP, obj), + vhpi_get_str(vhpiKindStrP, obj)); + obj=NULL; + } + if (obj) continue; From 8eb7191310703175737e24bcf0034f3a532f5d45 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Wed, 29 Jul 2015 05:57:45 -0700 Subject: [PATCH 0888/2656] Issue #267: Address pull request comments --- lib/fli/FliImpl.cpp | 85 +++++++++++++++++---------------------------- lib/fli/FliImpl.h | 6 ++-- 2 files changed, 35 insertions(+), 56 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 0cdc3db2..2e37ac46 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -471,57 +471,42 @@ int FliSignalObjHdl::initialise(std::string &name) switch (m_fli_type) { case MTI_TYPE_ENUM: - m_val_len = 1; - m_val_buff = (char*)malloc(m_val_len+1); - if (!m_val_buff) { - LOG_CRITICAL("Unable to alloc mem for signal read buffer"); - } - m_val_buff[m_val_len] = '\0'; - m_val_str_len = 3+m_val_len; - m_val_str_buff = (char*)malloc(m_val_str_len+1); - if (!m_val_str_buff) { - LOG_CRITICAL("Unable to alloc mem for signal write buffer"); - } - m_val_str_buff[m_val_str_len] = '\0'; + m_val_len = 1; + m_val_str_len = 3+m_val_len; break; case MTI_TYPE_SCALAR: case MTI_TYPE_PHYSICAL: - m_val_len = 32; - m_val_buff = (char*)malloc(m_val_len+1); - if (!m_val_buff) { - LOG_CRITICAL("Unable to alloc mem for signal read buffer"); - } - m_val_buff[m_val_len] = '\0'; - m_val_str_len = 4+m_val_len; - m_val_str_buff = (char*)malloc(m_val_str_len+1); - if (!m_val_str_buff) { - LOG_CRITICAL("Unable to alloc mem for signal write buffer"); - } - m_val_str_buff[m_val_str_len] = '\0'; + m_val_len = 32; + m_val_str_len = 4+m_val_len; break; case MTI_TYPE_ARRAY: - m_val_len = mti_TickLength(mti_GetSignalType(m_fli_hdl)); - m_val_buff = (char*)malloc(m_val_len+1); - if (!m_val_buff) { - LOG_CRITICAL("Unable to alloc mem for signal read buffer"); - } - m_val_buff[m_val_len] = '\0'; - m_mti_buff = (mtiInt32T*)malloc(sizeof(*m_mti_buff) * m_val_len); + m_val_len = mti_TickLength(mti_GetSignalType(m_fli_hdl)); + m_val_str_len = snprintf(NULL, 0, "%d'b", m_val_len)+m_val_len; + m_mti_buff = (mtiInt32T*)malloc(sizeof(*m_mti_buff) * m_val_len); if (!m_mti_buff) { LOG_CRITICAL("Unable to alloc mem for signal mti read buffer"); + exit(1); } - m_val_str_len = snprintf(NULL, 0, "%d'b", m_val_len)+m_val_len; - m_val_str_buff = (char*)malloc(m_val_str_len+1); - if (!m_val_str_buff) { - LOG_CRITICAL("Unable to alloc mem for signal write buffer"); - } - m_val_str_buff[m_val_str_len] = '\0'; break; default: LOG_CRITICAL("Unable to handle onject type for %s (%d)", name.c_str(), m_fli_type); + exit(1); } + m_val_buff = (char*)malloc(m_val_len+1); + if (!m_val_buff) { + LOG_CRITICAL("Unable to alloc mem for signal read buffer"); + exit(1); + } + m_val_buff[m_val_len] = '\0'; + m_val_str_buff = (char*)malloc(m_val_str_len+1); + if (!m_val_str_buff) { + LOG_CRITICAL("Unable to alloc mem for signal write buffer"); + exit(1); + } + m_val_str_buff[m_val_str_len] = '\0'; + GpiObjHdl::initialise(name); return 0; @@ -591,37 +576,31 @@ int FliVariableObjHdl::initialise(std::string &name) switch (m_fli_type) { case MTI_TYPE_ENUM: m_val_len = 1; - m_val_buff = (char*)malloc(m_val_len+1); - if (!m_val_buff) { - LOG_CRITICAL("Unable to alloc mem for signal read buffer"); - } - m_val_buff[m_val_len] = '\0'; break; case MTI_TYPE_SCALAR: case MTI_TYPE_PHYSICAL: m_val_len = 32; - m_val_buff = (char*)malloc(m_val_len+1); - if (!m_val_buff) { - LOG_CRITICAL("Unable to alloc mem for signal read buffer"); - } - m_val_buff[m_val_len] = '\0'; break; case MTI_TYPE_ARRAY: - m_val_len = mti_TickLength(mti_GetVarType(m_fli_hdl)); - m_val_buff = (char*)malloc(m_val_len+1); - if (!m_val_buff) { - LOG_CRITICAL("Unable to alloc mem for signal read buffer"); - } - m_val_buff[m_val_len] = '\0'; + m_val_len = mti_TickLength(mti_GetVarType(m_fli_hdl)); m_mti_buff = (mtiInt32T*)malloc(sizeof(*m_mti_buff) * m_val_len); if (!m_mti_buff) { LOG_CRITICAL("Unable to alloc mem for signal mti read buffer"); + exit(1); } break; default: LOG_CRITICAL("Unable to handle onject type for %s (%d)", name.c_str(), m_fli_type); + exit(1); + } + + m_val_buff = (char*)malloc(m_val_len+1); + if (!m_val_buff) { + LOG_CRITICAL("Unable to alloc mem for signal read buffer"); + exit(1); } + m_val_buff[m_val_len] = '\0'; GpiObjHdl::initialise(name); diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index 6947a35f..266c2397 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -85,11 +85,11 @@ class FliSignalObjHdl : public GpiSignalObjHdl { m_val_len(0), m_val_str_len(0) { } virtual ~FliSignalObjHdl() { - if (m_val_len) + if (m_val_buff) free(m_val_buff); if (m_mti_buff) free(m_mti_buff); - if (m_val_str_len) + if (m_val_str_buff) free(m_val_str_buff); } @@ -123,7 +123,7 @@ class FliVariableObjHdl : public GpiSignalObjHdl { m_val_buff(NULL), m_val_len(0) { } virtual ~FliVariableObjHdl() { - if (m_val_len) + if (m_val_buff) free(m_val_buff); if (m_mti_buff) free(m_mti_buff); From dd10dc68f75b450fd89abb1a25cb10bf35bc8c4b Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 29 Jul 2015 17:49:47 +0100 Subject: [PATCH 0889/2656] Issue #17: Cleanup the GpiIterator when it reaches the end --- cocotb/handle.py | 1 + lib/gpi/GpiCommon.cpp | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 81800844..bf2a487a 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -207,6 +207,7 @@ def _discover_all(self): try: thing = simulator.next(iterator) except StopIteration: + # Iterator is cleaned up internally in GPI break name = simulator.get_name_string(thing) hdl = SimHandle(thing) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index ac6c9c03..2da6cd26 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -246,8 +246,16 @@ gpi_iterator_hdl gpi_iterate(gpi_sim_hdl base) gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) { + gpi_sim_hdl next; GpiIterator *iter = sim_to_hdl(iterator); - return (gpi_sim_hdl)iter->next_handle(); + next = (gpi_sim_hdl)iter->next_handle(); + if (!next) { + /* If the iterator returns NULL then it has reached the + end, we clear up here before returning + */ + delete iter; + } + return next; } const char *gpi_get_signal_value_binstr(gpi_sim_hdl sig_hdl) From 3e803d264ee4eb93fe67ead0203779385e887f03 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 30 Jul 2015 08:45:52 +0100 Subject: [PATCH 0890/2656] Issue #17: Enable viterbi design files for ius --- makefiles/simulators/Makefile.ius | 5 +++-- tests/designs/viterbi_decoder_axi4s/Makefile | 10 +++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 43574bc2..5bf19e04 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -59,8 +59,9 @@ ifndef GPI_ARGS endif results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(GPI_LIB) - LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - irun $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(HDL_SOURCES) $(PLUSARGS) + LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ + LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + irun $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(HDL_SOURCES) $(PLUSARGS) clean:: -rm -rf $(SIM_BUILD) diff --git a/tests/designs/viterbi_decoder_axi4s/Makefile b/tests/designs/viterbi_decoder_axi4s/Makefile index 18e1bed3..640f9e9f 100644 --- a/tests/designs/viterbi_decoder_axi4s/Makefile +++ b/tests/designs/viterbi_decoder_axi4s/Makefile @@ -52,17 +52,21 @@ VHDL_SOURCES = $(SRC_BASE)/packages/pkg_helper.vhd \ $(SRC_BASE)/src/recursion.vhd \ $(SRC_BASE)/src/dec_viterbi.vhd \ +GPI_IMPL:= ifeq ($(SIM),aldec) GPI_IMPL=vhpi endif ifeq ($(SIM),modelsim) GPI_IMPL=fli endif +ifeq ($(SIM),ius) + GPI_IMPL=vhpi +endif -ifneq ($(SIM),) +ifneq ($(GPI_IMPL),) include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim else all: - @echo "Skipping test as icarus does not support VHDL" -endif \ No newline at end of file + @echo "Skipping test as VHDL not supported on simulator=$(SIM)" +endif From 6a4b0a8810e54bcd5d60da5a8ee57f78b6335840 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 30 Jul 2015 09:07:26 +0100 Subject: [PATCH 0891/2656] Issue #253: Add testcase for issue #253 with TOPLEVEL=[,] and not set at all --- lib/embed/gpi_embed.c | 15 ++++-- tests/test_cases/issue_253/Makefile | 66 +++++++++++++++++++++++++ tests/test_cases/issue_253/issue_253.py | 30 +++++++++++ 3 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 tests/test_cases/issue_253/Makefile create mode 100644 tests/test_cases/issue_253/issue_253.py diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 47f7d927..fd3644da 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -144,14 +144,21 @@ int embed_sim_init(gpi_sim_info_t *info) const char *dut = getenv("TOPLEVEL"); if (dut != NULL) { - // Skip any library component of the toplevel - char *dot = strchr(dut, '.'); - if (dot != NULL) { - dut += (dot - dut + 1); + if (!strcmp("", dut)) { + /* Empty string passed in, treat as NULL */ + dut = NULL; + } else { + // Skip any library component of the toplevel + char *dot = strchr(dut, '.'); + if (dot != NULL) { + dut += (dot - dut + 1); + } } } + + PyObject *cocotb_module, *cocotb_init, *cocotb_args, *cocotb_retval; PyObject *simlog_obj, *simlog_func; PyObject *argv_list, *argc, *arg_dict, *arg_value; diff --git a/tests/test_cases/issue_253/Makefile b/tests/test_cases/issue_253/Makefile new file mode 100644 index 00000000..ad4b4094 --- /dev/null +++ b/tests/test_cases/issue_253/Makefile @@ -0,0 +1,66 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +ifneq ($(SIM),) +all: + @echo "Skipping issue_253 only runs on icarus" +else + +include $(COCOTB)/tests/designs/sample_module/Makefile + +MODULE = issue_253 + +# Redefine the Icarus rule for results.xml to not specify TOPLEVEL, test should still pass + +all: empty_top_level no_top_level notset_top_level + +results.xml: + @echo "Skipping" + +notset_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + TESTCASE=issue_253_notset TOPLEVEL= \ + vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) + mkdir -p $@_result && mv results.xml $@_result/ + +empty_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + TESTCASE=issue_253_empty TOPLEVEL="" \ + vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) + mkdir -p $@_result && mv results.xml $@_result/ + +no_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) + PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + TESTCASE=issue_253_none \ + vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) + mkdir -p $@_result && mv results.xml $@_result/ + +clean:: + @rm -rf empty_top_level_result no_top_level_result notset_top_level_result +endif diff --git a/tests/test_cases/issue_253/issue_253.py b/tests/test_cases/issue_253/issue_253.py new file mode 100644 index 00000000..d3826ce5 --- /dev/null +++ b/tests/test_cases/issue_253/issue_253.py @@ -0,0 +1,30 @@ +# A set of regression tests for open issues + +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import RisingEdge, Timer, ReadOnly +from cocotb.result import TestFailure +from cocotb.binary import BinaryValue + +@cocotb.coroutine +def toggle_clock(dut): + dut.clk = 0 + yield Timer(10) + if dut.clk.value.integer is not 0: + raise TestFailure("Clock not set to 0 as expected") + dut.clk = 1 + yield Timer(10) + if dut.clk.value.integer is not 1: + raise TestFailure("Clock not set to 1 as expected") + +@cocotb.test() +def issue_253_empty(dut): + yield toggle_clock(dut) + +@cocotb.test() +def issue_253_none(dut): + yield toggle_clock(dut) + +@cocotb.test() +def issue_253_notset(dut): + yield toggle_clock(dut) From 67a2409dd524bee80d26087cc312c57dfe6fe58e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 30 Jul 2015 17:25:58 +0100 Subject: [PATCH 0892/2656] Cleanup: Remove old fli file --- lib/gpi_impl/gpi_fli.c | 465 ----------------------------------------- 1 file changed, 465 deletions(-) delete mode 100644 lib/gpi_impl/gpi_fli.c diff --git a/lib/gpi_impl/gpi_fli.c b/lib/gpi_impl/gpi_fli.c deleted file mode 100644 index 94f2339e..00000000 --- a/lib/gpi_impl/gpi_fli.c +++ /dev/null @@ -1,465 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2013 Potential Ventures Ltd -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#include "gpi_priv.h" -#include - -#define FLII_CHECKING 1 - -static gpi_sim_hdl sim_init_cb; -static gpi_sim_hdl sim_finish_cb; - - -// Handle related functions -/** - * @name Find the root handle - * @brief Find the root handle using a optional name - * - * Get a handle to the root simulator object. This is usually the toplevel. - * - * If no name is defined, we return the first root instance. - * - * If name is provided, we check the name against the available objects until - * we find a match. If no match is found we return NULL - */ -static gpi_sim_hdl fli_get_root_handle(const char* name) -{ - FENTER - mtiRegionIdT root; - gpi_sim_hdl rv; - - for (root = mti_GetTopRegion(); root != NULL; root = mti_NextRegion(root)) { - if (name == NULL || !strcmp(name, mti_GetRegionName(root))) - break; - } - - if (!root) { - goto error; - } - - rv = gpi_create_handle(); - rv->sim_hdl = (void *)root; - - FEXIT - return rv; - - error: - - LOG_CRITICAL("FPI: Couldn't find root handle %s", name); - - for (root = mti_GetTopRegion(); root != NULL; root = mti_NextRegion(root)) { - - LOG_CRITICAL("FLI: Toplevel instances: %s != %s...", name, mti_GetRegionName(root)); - - if (name == NULL) - break; - } - - FEXIT - return NULL; -} - - -/** - * @brief Get a handle to an object under the scope of parent - * - * @param name of the object to find - * @param parent handle to parent object defining the scope to search - * - * @return gpi_sim_hdl for the new object or NULL if object not found - */ -static gpi_sim_hdl fli_get_handle_by_name(const char *name, gpi_sim_hdl parent) -{ - FENTER - mtiRegionIdT hdl = (mtiRegionIdT)parent->sim_hdl; - gpi_sim_hdl rv; - void *result; - mtiRegionIdT result_reg; - mtiSignalIdT result_sig; - mtiVariableIdT result_var; - size_t baselen = strlen(mti_GetRegionFullName(hdl)); - char *fullname; - char *ptr; - - fullname = (char *)malloc(baselen + strlen(name) + 2); - - if (fullname == NULL) { - LOG_CRITICAL("FLI: Attempting allocate string buffer failed!"); - return NULL; - } - - strncpy(fullname, mti_GetRegionFullName(hdl), baselen); - ptr = fullname + baselen; - *ptr++ = '/'; - strncpy(ptr, name, strlen(name)); - - result_reg = mti_FindRegion(fullname); - result = (void *)result_reg; - if (result_reg) goto success; -// result = mti_FindPort(fullname); -// if (result) goto success; - result_sig = mti_FindSignal(fullname); - result = (void *)result_sig; - if (result_sig) goto success; - result_var = mti_FindVar(fullname); - result = (void *)result_var; - if (result) goto success; - -error: - LOG_DEBUG("FLII: Handle '%s' not found!", name); - - // NB we deliberately don't dump an error message here because it's - // a valid use case to attempt to grab a signal by name - for example - // optional signals on a bus. - free(fullname); - return NULL; - -success: - free(fullname); - rv = gpi_create_handle(); - rv->sim_hdl = result; - - FEXIT - return rv; -} - - -/** - * @brief Get a handle for an object based on its index within a parent - * - * @param parent handle to the parent - * @param indext Index to retrieve - * - * Can be used on bit-vectors to access a specific bit or - * memories to access an address - */ -static gpi_sim_hdl fli_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) -{ - FENTER - LOG_ERROR("FLI: Obtaining a handle by index not supported by FLI?"); - FEXIT - return NULL; -} - - -// Functions for iterating over entries of a handle -// Returns an iterator handle which can then be used in gpi_next calls -// NB May return NULL if no objects of the request type exist -static gpi_iterator_hdl fli_iterate_hdl(uint32_t type, gpi_sim_hdl base) { - FENTER - LOG_ERROR("FLI: Iterating over a handle not implemented yet"); - FEXIT - return NULL; -} - -// Returns NULL when there are no more objects -static gpi_sim_hdl fli_next_hdl(gpi_iterator_hdl iterator) -{ - FENTER - LOG_ERROR("FLI: Iterating over a handle not implemented yet"); - FEXIT - return NULL; -} - -static void fli_get_sim_time(uint32_t *high, uint32_t *low) -{ - *high = mti_NowUpper(); - *low = mti_Now(); -} - -// Value related functions -static void fli_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value) -{ - FENTER - LOG_ERROR("Attempt to force signal %s failed (not implemented)", - mti_GetSignalName((mtiSignalIdT)gpi_hdl->sim_hdl)); - FEXIT -} - -static void fli_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str) -{ - FENTER - char * str_copy = strdup(str); // Mentor discard const qualifier - int rc = mti_ForceSignal((mtiSignalIdT)(gpi_hdl->sim_hdl), - str_copy, - -1, // If the delay parameter is negative, - // then the force is applied immediately. - MTI_FORCE_DEPOSIT, - -1, // cancel_period - -1); // repeat_period - - if (!rc) - LOG_ERROR("Attempt to force signal %s failed", - mti_GetSignalName((mtiSignalIdT)gpi_hdl->sim_hdl)); - free(str_copy); - FEXIT -} - -static char *fli_get_signal_value_binstr(gpi_sim_hdl gpi_hdl) -{ - FENTER - mtiInt32T value; - switch (mti_GetTypeKind(mti_GetSignalType((mtiSignalIdT)gpi_hdl->sim_hdl))){ - case MTI_TYPE_SCALAR: - case MTI_TYPE_ENUM: - case MTI_TYPE_PHYSICAL: - value = mti_GetSignalValue((mtiSignalIdT)gpi_hdl->sim_hdl); - break; - default: - mti_PrintFormatted( "(Type not supported)\n" ); - break; - } - char *result;// = gpi_copy_name(value_p->value.str); - FEXIT - return result; -} - -static char *fli_get_signal_name_str(gpi_sim_hdl gpi_hdl) -{ - FENTER - const char *name = mti_GetSignalName((mtiSignalIdT)gpi_hdl->sim_hdl); - char *result = gpi_copy_name(name); - FEXIT - return result; -} - -static char *fli_get_signal_type_str(gpi_sim_hdl gpi_hdl) -{ - switch (mti_GetTypeKind(mti_GetSignalType((mtiSignalIdT)gpi_hdl->sim_hdl))){ - case MTI_TYPE_SCALAR : return strdup("Scalar"); - case MTI_TYPE_ARRAY : return strdup("Array"); - case MTI_TYPE_RECORD : return strdup("Record"); - case MTI_TYPE_ENUM : return strdup("Enum"); - case MTI_TYPE_INTEGER : return strdup("Integer"); - case MTI_TYPE_PHYSICAL : return strdup("Physical"); - case MTI_TYPE_REAL : return strdup("Real"); - case MTI_TYPE_ACCESS : return strdup("Access"); - case MTI_TYPE_FILE : return strdup("File"); - case MTI_TYPE_TIME : return strdup("Time"); - case MTI_TYPE_C_REAL : return strdup("C Real"); - case MTI_TYPE_C_ENUM : return strdup("C Enum"); - default : return strdup("Unknown!"); - } -} - - -// Callback related functions -static int32_t handle_fli_callback(gpi_cb_hdl cb_data) -{ - FENTER - LOG_CRITICAL("FLI: Callbacks not implemented yet"); - FEXIT - return 0; -}; - - -static int fli_deregister_callback(gpi_sim_hdl gpi_hdl) -{ - FENTER - LOG_CRITICAL("FLI: Callbacks not implemented yet"); - FEXIT - return 0; -} - - -/* These functions request a callback to be active with the current - * handle and associated data. A callback handle needs to have been - * allocated with gpi_create_cb_handle first - */ -static int fli_register_value_change_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data, - gpi_sim_hdl gpi_hdl) -{ - FENTER - FEXIT - return 0; -} - -static int fli_register_readonly_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) -{ - FENTER - FEXIT - return 0; -} - -static int fli_register_readwrite_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) -{ - FENTER - FEXIT - return 0; -} - - - -static int fli_register_nexttime_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data) -{ - FENTER - FEXIT - return 0; -} - -static int fli_register_timed_callback(gpi_sim_hdl cb, - int (*gpi_function)(void *), - void *gpi_cb_data, - uint64_t time_ps) -{ - FENTER - FEXIT - return 0; -} - -static void fli_sim_end(void) -{ - sim_finish_cb = NULL; - mti_Quit(); -} - - -/* Checking of validity is done in the common code */ -static gpi_cb_hdl fli_create_cb_handle(void) -{ - gpi_cb_hdl ret = NULL; - - FENTER - - void * new_cb_hdl = calloc(1, sizeof(*new_cb_hdl)); - if (new_cb_hdl) - ret = &new_cb_hdl->gpi_cb_data; - - FEXIT - return ret; -} - - - -static s_gpi_impl_tbl fli_table = { - .sim_end = fli_sim_end, - .iterate_handle = fli_iterate_hdl, - .next_handle = fli_next_hdl, - .create_cb_handle = fli_create_cb_handle, - .destroy_cb_handle = fli_destroy_cb_handle, - .deregister_callback = fli_deregister_callback, - .get_root_handle = fli_get_root_handle, - .get_sim_time = fli_get_sim_time, - .get_handle_by_name = fli_get_handle_by_name, - .get_handle_by_index = fli_get_handle_by_index, - .get_signal_name_str = fli_get_signal_name_str, - .get_signal_type_str = fli_get_signal_type_str, - .get_signal_value_binstr = fli_get_signal_value_binstr, - .set_signal_value_int = fli_set_signal_value_int, - .set_signal_value_str = fli_set_signal_value_str, - .register_timed_callback = fli_register_timed_callback, - .register_readwrite_callback = fli_register_readwrite_callback, - .register_nexttime_callback = fli_register_nexttime_callback, - .register_value_change_callback = fli_register_value_change_callback, - .register_readonly_callback = fli_register_readonly_callback, - .get_callback_data = fli_get_callback_data, -}; - -static void register_embed(void) -{ - gpi_register_impl(&fli_table, 0xfe70); - gpi_embed_init_python(); -} - - -// No access to plusargs via FLI? -static int handle_sim_init(void *gpi_cb_data) -{ - FENTER - gpi_sim_info_t sim_info; - sim_info.argc = NULL; - sim_info.argv = NULL; - sim_info.product = mti_GetProductVersion(); - sim_info.version = NULL; - gpi_embed_init(&sim_info); - FEXIT -} - -static void register_initial_callback(void) -{ - FENTER - gpi_cb_hdl gpi_user_data; - sim_init_cb = gpi_create_cb_handle(); - gpi_user_data = gpi_container_of(sim_init_cb, gpi_cb_hdl_t, hdl); - - gpi_user_data->gpi_cb_data = NULL; - gpi_user_data->gpi_function = handle_sim_init; - - mti_AddLoadDoneCB(handle_fli_callback, &gpi_user_data); - FEXIT -} - -static int handle_sim_end(void *gpi_cb_data) -{ - FENTER - if (sim_finish_cb) { - sim_finish_cb = NULL; - /* This means that we have been asked to close */ - gpi_embed_end(); - } /* Other sise we have already been here from the top down so do not need - to inform the upper layers that anything has occoured */ - gpi_free_cb_handle(sim_init_cb); - FEXIT -} - -static void register_final_callback(void) -{ - FENTER - gpi_cb_hdl gpi_user_data; - sim_init_cb = gpi_create_cb_handle(); - gpi_user_data = gpi_container_of(sim_init_cb, gpi_cb_hdl_t, hdl); - - gpi_user_data->gpi_cb_data = NULL; - gpi_user_data->gpi_function = handle_sim_end; - - mti_AddQuitCB(handle_fli_callback, &gpi_user_data); - FEXIT -} - - -// Initialisation needs to be triggered from a foreign architecture in the RTL -// -// ATTRIBUTE foreign OF blah : ARCHITECTURE IS "cocotb_init libgpi.so; parameter"; -void cocotb_init(mtiRegionIdT region, - char *param, - mtiInterfaceListT *generics, - mtiInterfaceListT *ports) -{ - register_embed(); - register_initial_callback(); - register_final_callback(); -} - - From 258351e6a895ab523fdb24ab232e0a653cdf620a Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 30 Jul 2015 19:03:34 +0100 Subject: [PATCH 0893/2656] Issue#134: Implement assignment of Reals to signals in VHPI and VPI --- cocotb/handle.py | 6 +++++- include/gpi.h | 1 + lib/fli/FliImpl.h | 1 + lib/gpi/GpiCommon.cpp | 6 ++++++ lib/gpi/gpi_priv.h | 1 + lib/simulator/simulatormodule.c | 21 ++++++++++++++++++ lib/simulator/simulatormodule.h | 2 ++ lib/vhpi/VhpiCbHdl.cpp | 38 +++++++++++++++++++++++++++++++++ lib/vhpi/VhpiImpl.h | 1 + lib/vpi/VpiCbHdl.cpp | 21 ++++++++++++++++++ lib/vpi/VpiImpl.h | 1 + 11 files changed, 98 insertions(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 3e7d9186..49d0aa71 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -159,7 +159,7 @@ def setimmediatevalue(self, value): Set the value of the underlying simulation object to value. Args: - value (ctypes.Structure, cocotb.binary.BinaryValue, int) + value (ctypes.Structure, cocotb.binary.BinaryValue, int, double) The value to drive onto the simulator object Raises: @@ -177,6 +177,10 @@ def setimmediatevalue(self, value): simulator.set_signal_val(self._handle, value) return + if isinstance(value, float): + simulator.set_signal_val_real(self._handle, value) + return + if isinstance(value, ctypes.Structure): value = BinaryValue(value=cocotb.utils.pack(value), bits=len(self)) elif isinstance(value, get_python_integer_types()): diff --git a/include/gpi.h b/include/gpi.h index 912a3e5f..af615dfa 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -152,6 +152,7 @@ const char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl); // Functions for setting the properties of a handle +void gpi_set_signal_value_real(gpi_sim_hdl gpi_hdl, double value); void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value); void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str); // String of binary char(s) [1, 0, x, z] diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index e2593ffc..d0e5f893 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -91,6 +91,7 @@ class FliSignalObjHdl : public GpiSignalObjHdl { const char* get_signal_value_binstr(void); int set_signal_value(const int value); + int set_signal_value(const double value) = { return 0; } int set_signal_value(std::string &value); int initialise(std::string &name); GpiCbHdl *value_change_cb(unsigned int edge); diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index ade441a5..a8e6dd9c 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -288,6 +288,12 @@ void gpi_set_signal_value_str(gpi_sim_hdl sig_hdl, const char *str) obj_hdl->set_signal_value(value); } +void gpi_set_signal_value_real(gpi_sim_hdl sig_hdl, double value) +{ + GpiSignalObjHdl *obj_hdl = sim_to_hdl(sig_hdl); + obj_hdl->set_signal_value(value); +} + gpi_sim_hdl gpi_register_value_change_callback(int (*gpi_function)(const void *), void *gpi_cb_data, gpi_sim_hdl sig_hdl, diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index f58ae35a..3f7e66bf 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -127,6 +127,7 @@ class GpiSignalObjHdl : public GpiObjHdl { int m_length; virtual int set_signal_value(const int value) = 0; + virtual int set_signal_value(const double value) = 0; virtual int set_signal_value(std::string &value) = 0; //virtual GpiCbHdl monitor_value(bool rising_edge) = 0; this was for the triggers // but the explicit ones are probably better diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index f87fcd67..cfe177de 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -595,6 +595,27 @@ static PyObject *set_signal_val_str(PyObject *self, PyObject *args) return res; } +static PyObject *set_signal_val_real(PyObject *self, PyObject *args) +{ + gpi_sim_hdl hdl; + double value; + PyObject *res; + + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + if (!PyArg_ParseTuple(args, "ld", &hdl, &value)) { + DROP_GIL(gstate); + return NULL; + } + + gpi_set_signal_value_real(hdl, value); + res = Py_BuildValue("s", "OK!"); + + DROP_GIL(gstate); + + return res; +} static PyObject *get_handle_by_name(PyObject *self, PyObject *args) { diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 660598f1..a2290730 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -60,6 +60,7 @@ static PyObject *log_msg(PyObject *self, PyObject *args); static PyObject *get_signal_val(PyObject *self, PyObject *args); static PyObject *set_signal_val(PyObject *self, PyObject *args); static PyObject *set_signal_val_str(PyObject *self, PyObject *args); +static PyObject *set_signal_val_real(PyObject *self, PyObject *args); static PyObject *get_handle_by_name(PyObject *self, PyObject *args); static PyObject *get_handle_by_index(PyObject *self, PyObject *args); static PyObject *get_root_handle(PyObject *self, PyObject *args); @@ -83,6 +84,7 @@ static PyMethodDef SimulatorMethods[] = { {"get_signal_val", get_signal_val, METH_VARARGS, "Get the value of a signal as a binary string"}, {"set_signal_val", set_signal_val, METH_VARARGS, "Set the value of a signal"}, {"set_signal_val_str", set_signal_val_str, METH_VARARGS, "Set the value of a signal using a binary string"}, + {"set_signal_val_real", set_signal_val_real, METH_VARARGS, "Set the value of a signal using a double"}, {"get_handle_by_name", get_handle_by_name, METH_VARARGS, "Get handle of a named object"}, {"get_handle_by_index", get_handle_by_index, METH_VARARGS, "Get handle of a object at an index in a parent"}, {"get_root_handle", get_root_handle, METH_VARARGS, "Get the root handle"}, diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 6eef65f0..037eefe7 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -56,6 +56,9 @@ int VhpiSignalObjHdl::initialise(std::string &name) { break; } + case vhpiRealVal: + break; + case vhpiEnumVecVal: case vhpiLogicVecVal: { m_size = vhpi_get(vhpiSizeP, GpiObjHdl::get_handle()); @@ -208,8 +211,13 @@ int VhpiSignalObjHdl::set_signal_value(int value) break; } + case vhpiRealVal: + LOG_WARN("Attempt to vhpiRealVal signal with integer"); + return 0; + default: { LOG_CRITICAL("VHPI type of object has changed at runtime, big fail"); + return -1; } } vhpi_put_value(GpiObjHdl::get_handle(), &m_value, vhpiForcePropagate); @@ -217,6 +225,30 @@ int VhpiSignalObjHdl::set_signal_value(int value) return 0; } +int VhpiSignalObjHdl::set_signal_value(double value) +{ + switch (m_value.format) { + case vhpiRealVal: + m_value.value.real = value; + break; + + case vhpiEnumVal: + case vhpiLogicVal: + case vhpiEnumVecVal: + case vhpiLogicVecVal: + LOG_WARN("Attempt to set non vhpiRealVal signal with double"); + return 0; + + default: { + LOG_CRITICAL("VHPI type of object has changed at runtime, big fail"); + return -1; + } + } + + vhpi_put_value(GpiObjHdl::get_handle(), &m_value, vhpiForcePropagate); + return 0; +} + int VhpiSignalObjHdl::set_signal_value(std::string &value) { switch (m_value.format) { @@ -254,8 +286,14 @@ int VhpiSignalObjHdl::set_signal_value(std::string &value) break; } + case vhpiRealVal: + LOG_WARN("Attempt to vhpiRealVal signal with string"); + return 0; + + default: { LOG_CRITICAL("VHPI type of object has changed at runtime, big fail"); + return -1; } } diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 41e5aa9f..788b5871 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -159,6 +159,7 @@ class VhpiSignalObjHdl : public GpiSignalObjHdl { const char* get_signal_value_binstr(void); int set_signal_value(const int value); + int set_signal_value(const double value); int set_signal_value(std::string &value); /* Value change callback accessor */ diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 9b584033..16433909 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -159,6 +159,27 @@ int VpiSignalObjHdl::set_signal_value(int value) return 0; } +int VpiSignalObjHdl::set_signal_value(double value) +{ + FENTER + s_vpi_value value_s; + + value_s.value.real = value; + value_s.format = vpiRealVal; + + s_vpi_time vpi_time_s; + + vpi_time_s.type = vpiSimTime; + vpi_time_s.high = 0; + vpi_time_s.low = 0; + + vpi_put_value(GpiObjHdl::get_handle(), &value_s, &vpi_time_s, vpiInertialDelay); + check_vpi_error(); + + FEXIT + return 0; +} + int VpiSignalObjHdl::set_signal_value(std::string &value) { FENTER diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 0763c843..ab4cc20f 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -182,6 +182,7 @@ class VpiSignalObjHdl : public GpiSignalObjHdl { const char* get_signal_value_binstr(void); int set_signal_value(const int value); + int set_signal_value(const double value); int set_signal_value(std::string &value); /* Value change callback accessor */ From 0dd1e8a76b8fed47e299b84ad0871e03217d3024 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 31 Jul 2015 08:16:47 +0100 Subject: [PATCH 0894/2656] Issue #134: Add very simple test case for VHDL --- tests/test_cases/issue_134/Makefile | 31 +++++++++++++++++++++++++ tests/test_cases/issue_134/issue_134.py | 17 ++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 tests/test_cases/issue_134/Makefile create mode 100644 tests/test_cases/issue_134/issue_134.py diff --git a/tests/test_cases/issue_134/Makefile b/tests/test_cases/issue_134/Makefile new file mode 100644 index 00000000..a60ede6f --- /dev/null +++ b/tests/test_cases/issue_134/Makefile @@ -0,0 +1,31 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +include $(COCOTB)/tests/designs/viterbi_decoder_axi4s/Makefile + +MODULE = issue_134 diff --git a/tests/test_cases/issue_134/issue_134.py b/tests/test_cases/issue_134/issue_134.py new file mode 100644 index 00000000..31a0154e --- /dev/null +++ b/tests/test_cases/issue_134/issue_134.py @@ -0,0 +1,17 @@ +# A set of regression tests for open issues + +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import RisingEdge, Timer, ReadOnly +from cocotb.result import TestFailure +from cocotb.binary import BinaryValue + + +@cocotb.test(expect_error=True) +def false_double(dut): + yield Timer(0) + + dut.aclk = float(1.0) + + yield Timer(1) + From 60858bf54a835ec141ee02f0fcf314f8d182c035 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 3 Aug 2015 21:07:58 +0100 Subject: [PATCH 0895/2656] Cleanup: Pass in __FUNC__ from macro so correct --- lib/vpi/VpiImpl.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 2e44b8f8..051cbd2f 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -32,7 +32,7 @@ #include // Should be run after every VPI call to check error status -static inline int __check_vpi_error(const char *func, long line) +static inline int __check_vpi_error(const char *file, const char *func, long line) { int level=0; #if VPI_CHECKING @@ -62,7 +62,7 @@ static inline int __check_vpi_error(const char *func, long line) loglevel = GPIWarning; } - gpi_log("cocotb.gpi", loglevel, __FILE__, func, line, + gpi_log("cocotb.gpi", loglevel, file, func, line, "VPI Error %s\nPROD %s\nCODE %s\nFILE %s", info.message, info.product, info.code, info.file); @@ -71,7 +71,7 @@ static inline int __check_vpi_error(const char *func, long line) } #define check_vpi_error() do { \ - __check_vpi_error(__func__, __LINE__); \ + __check_vpi_error(__FILE__, __func__, __LINE__); \ } while (0) class VpiReadwriteCbHdl; From e0d92dc00039bd09ebe61ca31d24097fc301df5a Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 3 Aug 2015 21:08:34 +0100 Subject: [PATCH 0896/2656] Issue #230: IUS makefile changes to work with multi lib --- makefiles/simulators/Makefile.ius | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 5bf19e04..4f845487 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -38,6 +38,10 @@ else EXTRA_ARGS += endif +# IUS loads vpi regardless of what is asked for, then VHPI if asked to +# Which does not play nice with out multi language. So we supply vhpi +# GPI_EXTRA if needed. + ifeq ($(GPI_IMPL),vpi) GPI_ARGS = -loadvpi $(LIB_DIR)/libvpi EXTRA_ARGS += -sv -v93 @@ -45,23 +49,18 @@ ifeq ($(GPI_IMPL),vpi) endif ifeq ($(GPI_IMPL),vhpi) - GPI_ARGS = -loadvhpi $(LIB_DIR)/libvhpi:vhpi_startup_routines_bootstrap - #EXTRA_ARGS += -cdslib cds.lib + GPI_EXTRA = vhpi EXTRA_ARGS += -v93 - EXTRA_ARGS += -top worklib.$(TOPLEVEL) + EXTRA_ARGS += -top $(TOPLEVEL) + MAKE_LIB := -makelib $(TOPLEVEL) HDL_SOURCES = $(VHDL_SOURCES) endif GPI_LIB = $(COCOTB_VPI_LIB) $(COCOTB_VHPI_LIB) -ifndef GPI_ARGS - $(error "Unable to determine appropriate GPI layer to use") -endif - results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(GPI_LIB) - LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ - LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - irun $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(HDL_SOURCES) $(PLUSARGS) + LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + irun $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) clean:: -rm -rf $(SIM_BUILD) From e72b557171fe4aa6ed43f61e2909e80ce41ca166 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 3 Aug 2015 21:09:01 +0100 Subject: [PATCH 0897/2656] Cleanup: Change so not duplicate debug string --- lib/vhpi/VhpiImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index e1e99396..2cb68c13 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -242,7 +242,7 @@ GpiObjHdl *VhpiImpl::get_root_handle(const char* name) check_vhpi_error(); if (!root) { - LOG_ERROR("VHPI: Attempting to get the root handle failed"); + LOG_ERROR("VHPI: Attempting to get the vhpiRootInst failed"); FEXIT return NULL; } From 44a29d367f8a8d4bda7881e4f6ba7b3849938b3f Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 3 Aug 2015 21:09:35 +0100 Subject: [PATCH 0898/2656] Issue #230: Check if the initial iterator is valid from vpi_iterate --- lib/vpi/VpiImpl.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 820ee27c..04c4a46f 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -201,6 +201,10 @@ GpiObjHdl *VpiImpl::get_root_handle(const char* name) // vpi_iterate with a ref of NULL returns the top level module iterator = vpi_iterate(vpiModule, NULL); check_vpi_error(); + if (!iterator) { + LOG_INFO("Nothing visible via VPI"); + return NULL; + } for (root = vpi_scan(iterator); root != NULL; root = vpi_scan(iterator)) { From 64f7a9f3737e15c4aa157cbbe4554e5526890819 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 3 Aug 2015 21:10:07 +0100 Subject: [PATCH 0899/2656] Issue #17: Come changes needed to source for IUS to work --- makefiles/simulators/Makefile.ius | 7 +++++-- .../viterbi_decoder_axi4s/packages/pkg_components.vhd | 4 ++-- tests/designs/viterbi_decoder_axi4s/src/dec_viterbi.vhd | 2 +- tests/designs/viterbi_decoder_axi4s/src/recursion.vhd | 6 +++--- tests/test_cases/test_iteration/Makefile | 2 +- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 4f845487..ea2ccb40 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -46,20 +46,23 @@ ifeq ($(GPI_IMPL),vpi) GPI_ARGS = -loadvpi $(LIB_DIR)/libvpi EXTRA_ARGS += -sv -v93 HDL_SOURCES = $(VERILOG_SOURCES) $(VHDL_SOURCES) + ROOT_LEVEL = $(TOPLEVEL) endif ifeq ($(GPI_IMPL),vhpi) GPI_EXTRA = vhpi EXTRA_ARGS += -v93 EXTRA_ARGS += -top $(TOPLEVEL) - MAKE_LIB := -makelib $(TOPLEVEL) + MAKE_LIB = -makelib $(TOPLEVEL) + ROOT_LEVEL =: HDL_SOURCES = $(VHDL_SOURCES) endif GPI_LIB = $(COCOTB_VPI_LIB) $(COCOTB_VHPI_LIB) results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(GPI_LIB) - LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ + LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(ROOT_LEVEL) \ irun $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) clean:: diff --git a/tests/designs/viterbi_decoder_axi4s/packages/pkg_components.vhd b/tests/designs/viterbi_decoder_axi4s/packages/pkg_components.vhd index 1c0e2215..fc86159a 100644 --- a/tests/designs/viterbi_decoder_axi4s/packages/pkg_components.vhd +++ b/tests/designs/viterbi_decoder_axi4s/packages/pkg_components.vhd @@ -174,7 +174,7 @@ package pkg_components is ); end component reorder; - component recursion is + component recursionx is port( clk : in std_logic; rst : in std_logic; @@ -189,6 +189,6 @@ package pkg_components is m_axis_output_tlast : out std_logic; m_axis_output_tready : in std_logic ); - end component recursion; + end component recursionx; end package pkg_components; diff --git a/tests/designs/viterbi_decoder_axi4s/src/dec_viterbi.vhd b/tests/designs/viterbi_decoder_axi4s/src/dec_viterbi.vhd index e5ac98d8..a619d5fa 100644 --- a/tests/designs/viterbi_decoder_axi4s/src/dec_viterbi.vhd +++ b/tests/designs/viterbi_decoder_axi4s/src/dec_viterbi.vhd @@ -318,7 +318,7 @@ begin signal reorder_recursion_tlast : std_logic; signal recursion_tready : std_logic; begin - inst_recursion : recursion + inst_recursion : recursionx port map( clk => clk, rst => rst, diff --git a/tests/designs/viterbi_decoder_axi4s/src/recursion.vhd b/tests/designs/viterbi_decoder_axi4s/src/recursion.vhd index ed64d89e..93205865 100644 --- a/tests/designs/viterbi_decoder_axi4s/src/recursion.vhd +++ b/tests/designs/viterbi_decoder_axi4s/src/recursion.vhd @@ -21,7 +21,7 @@ library dec_viterbi; use dec_viterbi.pkg_param.all; use dec_viterbi.pkg_param_derived.all; -entity recursion is +entity recursionx is port( clk : in std_logic; rst : in std_logic; @@ -42,9 +42,9 @@ entity recursion is m_axis_output_tlast : out std_logic; m_axis_output_tready : in std_logic ); -end entity recursion; +end entity recursionx; -architecture rtl of recursion is +architecture rtl of recursionx is signal recursion_sreg : unsigned(ENCODER_MEMORY_DEPTH downto 0); signal s_axis_input_tready_int : std_logic; signal m_axis_output_tvalid_int : std_logic; diff --git a/tests/test_cases/test_iteration/Makefile b/tests/test_cases/test_iteration/Makefile index 0c18b6da..ff7d75f1 100644 --- a/tests/test_cases/test_iteration/Makefile +++ b/tests/test_cases/test_iteration/Makefile @@ -27,5 +27,5 @@ include $(COCOTB)/tests/designs/viterbi_decoder_axi4s/Makefile - +#GPI_EXTRA=vhpi MODULE = test_iteration From eb0921eabbc6b1bff0a44531262097b726e2a703 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 3 Aug 2015 21:10:46 +0100 Subject: [PATCH 0900/2656] Issue #17: Some specific fixes for IUS wrt to initial VHDL toplevel --- include/vhpi_user.h | 8 +++++++- lib/gpi/GpiCbHdl.cpp | 1 - lib/gpi/GpiCommon.cpp | 2 +- lib/vhpi/VhpiCbHdl.cpp | 23 ++++++++++++----------- lib/vhpi/VhpiImpl.cpp | 17 +++++++++++++++-- lib/vhpi/VhpiImpl.h | 6 +++--- 6 files changed, 38 insertions(+), 19 deletions(-) diff --git a/include/vhpi_user.h b/include/vhpi_user.h index ae37abcc..f66cea49 100644 --- a/include/vhpi_user.h +++ b/include/vhpi_user.h @@ -609,7 +609,13 @@ typedef enum vhpiStrValP = 1315, vhpiToolVersionP = 1316, vhpiUnitNameP = 1317, - vhpiSaveRestartLocationP = 1318 + vhpiSaveRestartLocationP = 1318, + + /* Cadence IUS */ + vhpiFullVlogNameP = 1500, /* Full path name (VHDL names are Verilogized) */ + vhpiFullVHDLNameP = 1501, /* Full path name (Verilog names are vhdlized) */ + vhpiFullLSNameP = 1502, /* Full path name (Upper case) using ':' and '.' separators */ + vhpiFullLSCaseNameP = 1503 /* Full path name (VHDL case sensitive) */ #ifdef VHPIEXTEND_STR_PROPERTIES VHPIEXTEND_STR_PROPERTIES diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index 5e543467..5ee35496 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -114,7 +114,6 @@ int GpiHdl::initialise(std::string &name) int GpiObjHdl::initialise(std::string &name) { - printf("Name is %s\n", name.c_str()); m_name = name; m_fullname = "bleh2"; return 0; diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 2da6cd26..ac66eadb 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -165,7 +165,7 @@ gpi_sim_hdl gpi_get_root_handle(const char *name) GpiObjHdl *hdl; - LOG_DEBUG("Looking for root handle over %d impls", registered_impls.size()); + LOG_DEBUG("Looking for root handle '%s' over %d impls", name, registered_impls.size()); for (iter = registered_impls.begin(); iter != registered_impls.end(); diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index d48bce58..bc23b895 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -420,21 +420,22 @@ VhpiIterator::VhpiIterator(GpiImplInterface *impl, vhpiHandleT hdl) : GpiIterato vhpi_get_str(vhpiKindStrP, hdl)); // HACK: vhpiRootInstK seems to be a null level of hierarchy, need to skip - if (vhpiRootInstK == vhpi_get(vhpiKindP, hdl)) { - vhpiHandleT root_iterator; - hdl = vhpi_scan(iterator); - root_iterator = vhpi_iterator(*curr_type, hdl); - vhpi_release_handle(iterator); - iterator = root_iterator; - LOG_WARN("Skipped vhpiRootInstK to get to %s", vhpi_get_str(vhpiKindStrP, hdl)); - } + //if (vhpiRootInstK == vhpi_get(vhpiKindP, hdl)) { + // vhpiHandleT root_iterator; + // hdl = vhpi_scan(iterator); + // root_iterator = vhpi_iterator(*curr_type, hdl); + // vhpi_release_handle(iterator); + // iterator = root_iterator; + // LOG_WARN("Skipped vhpiRootInstK to get to %s", vhpi_get_str(vhpiKindStrP, hdl)); + //} m_iterator = iterator; } VhpiIterator::~VhpiIterator() { - vhpi_release_handle(m_iterator); + if (m_iterator) + vhpi_release_handle(m_iterator); } GpiObjHdl *VhpiIterator::next_handle(void) @@ -463,12 +464,12 @@ GpiObjHdl *VhpiIterator::next_handle(void) continue; LOG_WARN("End of vhpiOneToManyT=%d iteration", *curr_type); + vhpi_release_handle(m_iterator); } else { LOG_WARN("No valid vhpiOneToManyT=%d iterator", *curr_type); } curr_type++; - vhpi_release_handle(m_iterator); m_iterator = vhpi_iterator(*curr_type, get_handle()); } while (!obj && (curr_type != iterate_over.end())); @@ -478,7 +479,7 @@ GpiObjHdl *VhpiIterator::next_handle(void) return new_obj; } - std::string name = vhpi_get_str(vhpiNameP, obj); + std::string name = vhpi_get_str(vhpiCaseNameP, obj); LOG_WARN("vhpi_scan found %s (%d) kind:%s name:%s", name.c_str(), vhpi_get(vhpiKindP, obj), vhpi_get_str(vhpiKindStrP, obj), diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 2cb68c13..e5fe871a 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -27,6 +27,7 @@ #include "VhpiImpl.h" #include +#include extern "C" { static VhpiCbHdl *sim_init_cb; @@ -139,6 +140,7 @@ gpi_objtype_t to_gpi_objtype(vhpiIntT vhpitype) case vhpiEntityDeclK: case vhpiRootInstK: case vhpiProcessStmtK: + case vhpiSimpleSigAssignStmtK: return GPI_MODULE; default: @@ -169,6 +171,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string case vhpiIfGenerateK: case vhpiCompInstStmtK: case vhpiProcessStmtK: + case vhpiSimpleSigAssignStmtK: new_obj = new GpiObjHdl(this, new_hdl, to_gpi_objtype(type)); break; default: @@ -184,7 +187,12 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) { vhpiHandleT new_hdl; - std::string fq_name = parent->get_name() + "." + name; + std::string fq_name = parent->get_name(); + if (fq_name == ":") { + fq_name += name; + } else { + fq_name = fq_name + ":" + name; + } std::vector writable(fq_name.begin(), fq_name.end()); writable.push_back('\0'); @@ -251,15 +259,20 @@ GpiObjHdl *VhpiImpl::get_root_handle(const char* name) dut = vhpi_handle_by_name(name, NULL); else dut = vhpi_handle(vhpiDesignUnit, root); + check_vhpi_error(); + if (root) { + LOG_DEBUG("VHPI: We have found root='%s'", vhpi_get_str(vhpiCaseNameP, root)); + } + if (!dut) { LOG_ERROR("VHPI: Attempting to get the DUT handle failed"); FEXIT return NULL; } - const char *found = vhpi_get_str(vhpiNameP, dut); + const char *found = vhpi_get_str(vhpiCaseNameP, dut); check_vhpi_error(); if (name != NULL && strcmp(name, found)) { diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index cee45361..7e6841b6 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -33,7 +33,7 @@ #include // Should be run after every VHPI call to check error status -static inline int __check_vhpi_error(const char *func, long line) +static inline int __check_vhpi_error(const char *file, const char *func, long line) { int level=0; #if VHPI_CHECKING @@ -60,7 +60,7 @@ static inline int __check_vhpi_error(const char *func, long line) break; } - gpi_log("cocotb.gpi", loglevel, __FILE__, func, line, + gpi_log("cocotb.gpi", loglevel, file, func, line, "VHPI Error level %d: %s\nFILE %s:%d", info.severity, info.message, info.file, info.line); @@ -69,7 +69,7 @@ static inline int __check_vhpi_error(const char *func, long line) } #define check_vhpi_error() do { \ - __check_vhpi_error(__func__, __LINE__); \ + __check_vhpi_error(__FILE__, __func__, __LINE__); \ } while (0) class VhpiCbHdl : public virtual GpiCbHdl { From 7e6bb93c8f6cda344f8560c84677caf60f983af9 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 3 Aug 2015 21:14:17 +0100 Subject: [PATCH 0901/2656] Issue #17: Various small changes to work with IUS VHPI --- cocotb/handle.py | 5 +- lib/vhpi/VhpiCbHdl.cpp | 58 +++++++++++++------ .../test_iteration/test_iteration.py | 11 +++- 3 files changed, 54 insertions(+), 20 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index bf2a487a..8a6bb38f 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -216,6 +216,9 @@ def _discover_all(self): # These are renamed in VHPI to __X where X is the index import re result = re.match("(?P.*)__(?P\d+)$", name) + if not result: + result = re.match("(?P.*)\((?P\d+)", name) + if result: index = int(result.group("index")) name = result.group("name") @@ -227,7 +230,7 @@ def _discover_all(self): self._sub_handles[name].extend([None]*delta) self._sub_handles[name][index] = hdl else: - self._sub_handles[hdl.name.split(".")[-1]] = hdl + self._sub_handles[hdl._name.split(".")[-1]] = hdl self._discovered = True diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index bc23b895..cad64585 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -67,6 +67,11 @@ int VhpiSignalObjHdl::initialise(std::string &name) { break; } + case vhpiRawDataVal: + case vhpiIntVal: + GpiObjHdl::initialise(name); + return 0; + break; default: { LOG_CRITICAL("Unable to determine property for %s (%d) format object", @@ -77,17 +82,28 @@ int VhpiSignalObjHdl::initialise(std::string &name) { /* We also alloc a second value member for use with read string operations */ m_binvalue.format = vhpiBinStrVal; m_binvalue.bufSize = 0; + m_binvalue.numElems = 0; m_binvalue.value.str = NULL; int new_size = vhpi_get_value(GpiObjHdl::get_handle(), &m_binvalue); + if (new_size < 0) { + LOG_CRITICAL("Failed to determine size of signal object %s", name.c_str()); + goto out; + } + + if (new_size) { + m_binvalue.bufSize = new_size*sizeof(vhpiCharT); - m_binvalue.bufSize = new_size*sizeof(vhpiCharT) + 1; - m_binvalue.value.str = (vhpiCharT *)calloc(m_binvalue.bufSize, m_binvalue.bufSize); + LOG_DEBUG("Going to alloc %d\n", m_binvalue.bufSize); + m_binvalue.value.str = (vhpiCharT *)calloc(m_binvalue.bufSize, sizeof(vhpiCharT)); - if (!m_value.value.str) { - LOG_CRITICAL("Unable to alloc mem for read buffer"); + if (!m_value.value.str) { + LOG_CRITICAL("Unable to alloc mem for read buffer of signal %s", name.c_str()); + exit(1); + } } +out: GpiObjHdl::initialise(name); return 0; @@ -266,8 +282,15 @@ int VhpiSignalObjHdl::set_signal_value(std::string &value) const char* VhpiSignalObjHdl::get_signal_value_binstr(void) { - vhpi_get_value(GpiObjHdl::get_handle(), &m_binvalue); - check_vhpi_error(); + int ret = vhpi_get_value(GpiObjHdl::get_handle(), &m_binvalue); + if (ret) { + check_vhpi_error(); + LOG_ERROR("Size of m_binvalue.value.str was not large enough req=%d have=%d", + ret, + m_binvalue.bufSize); + } + + LOG_WARN("Return value was %d passed in was %d:%d\n", ret, m_binvalue.bufSize, m_binvalue.numElems); return m_binvalue.value.str; } @@ -385,20 +408,17 @@ VhpiNextPhaseCbHdl::VhpiNextPhaseCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), static vhpiOneToManyT options[] = { vhpiInternalRegions, - vhpiSignals, + vhpiSigDecls, vhpiMembers }; std::vector VhpiIterator::iterate_over(options, options + sizeof(options) / sizeof(options[0])); VhpiIterator::VhpiIterator(GpiImplInterface *impl, vhpiHandleT hdl) : GpiIterator(impl, hdl) - //iterate_over(options, - // options + options_len) { vhpiHandleT iterator; /* Find the first mapping type that yields a valid iterator */ - for (curr_type = iterate_over.begin(); curr_type != iterate_over.end(); curr_type++) { @@ -454,8 +474,8 @@ GpiObjHdl *VhpiIterator::next_handle(void) if (m_iterator) { obj = vhpi_scan(m_iterator); - if (vhpiProcessStmtK == vhpi_get(vhpiKindP, obj)) { - LOG_WARN("Skipping %s (%s)", vhpi_get_str(vhpiFullNameP, obj), + if (obj && (vhpiProcessStmtK == vhpi_get(vhpiKindP, obj))) { + LOG_DEBUG("Skipping %s (%s)", vhpi_get_str(vhpiFullNameP, obj), vhpi_get_str(vhpiKindStrP, obj)); obj=NULL; } @@ -463,27 +483,29 @@ GpiObjHdl *VhpiIterator::next_handle(void) if (obj) continue; - LOG_WARN("End of vhpiOneToManyT=%d iteration", *curr_type); + LOG_DEBUG("End of vhpiOneToManyT=%d iteration", *curr_type); vhpi_release_handle(m_iterator); } else { - LOG_WARN("No valid vhpiOneToManyT=%d iterator", *curr_type); + LOG_DEBUG("No valid vhpiOneToManyT=%d iterator", *curr_type); } curr_type++; + if (curr_type++ == iterate_over.end()) + break; m_iterator = vhpi_iterator(*curr_type, get_handle()); - } while (!obj && (curr_type != iterate_over.end())); + } while (!obj); if (NULL == obj) { - LOG_WARN("No more children, all relationships tested"); + LOG_DEBUG("No more children, all relationships tested"); return new_obj; } std::string name = vhpi_get_str(vhpiCaseNameP, obj); - LOG_WARN("vhpi_scan found %s (%d) kind:%s name:%s", name.c_str(), + LOG_DEBUG("vhpi_scan found %s (%d) kind:%s name:%s", name.c_str(), vhpi_get(vhpiKindP, obj), vhpi_get_str(vhpiKindStrP, obj), - vhpi_get_str(vhpiFullNameP, obj)); + vhpi_get_str(vhpiCaseNameP, obj)); VhpiImpl *vhpi_impl = reinterpret_cast(m_impl); new_obj = vhpi_impl->create_gpi_obj_from_handle(obj, name); diff --git a/tests/test_cases/test_iteration/test_iteration.py b/tests/test_cases/test_iteration/test_iteration.py index e7317fb6..c54c6a3d 100644 --- a/tests/test_cases/test_iteration/test_iteration.py +++ b/tests/test_cases/test_iteration/test_iteration.py @@ -25,7 +25,7 @@ import cocotb from cocotb.triggers import Timer -from cocotb.result import TestError +from cocotb.result import TestError, TestFailure @cocotb.test() @@ -53,3 +53,12 @@ def dual_iteration(dut): loop_two = cocotb.fork(iteration_loop(dut)) yield [loop_one.join(), loop_two.join()] + +@cocotb.test() +def get_clock(dut): + dut.aclk <= 0 + yield Timer(1) + dut.aclk <= 1 + yield Timer(1) + if dut.aclk.value is not 1: + raise TestFailure("dut.aclk is not what we expected") From c72688b32992a7818a7c41150ce13f8e7008d326 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 7 Aug 2015 18:30:56 +0100 Subject: [PATCH 0902/2656] Issue #17: Iteration working with IUS and Riviera using VHPI --- cocotb/handle.py | 6 +- lib/gpi/gpi_priv.h | 3 - lib/vhpi/VhpiCbHdl.cpp | 54 +++++++---- lib/vhpi/VhpiImpl.cpp | 3 +- lib/vhpi/VhpiImpl.h | 1 + lib/vpi/VpiCbHdl.cpp | 97 ++++++++++++++++++- lib/vpi/VpiImpl.cpp | 23 ++--- lib/vpi/VpiImpl.h | 17 +++- .../test_iteration/test_iteration.py | 2 +- 9 files changed, 161 insertions(+), 45 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 8a6bb38f..d86f3fc4 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -210,7 +210,11 @@ def _discover_all(self): # Iterator is cleaned up internally in GPI break name = simulator.get_name_string(thing) - hdl = SimHandle(thing) + try: + hdl = SimHandle(thing) + except TestError as e: + self._log.debug("%s" % e) + continue # This is slightly hacky, but we want generate loops to result in a list # These are renamed in VHPI to __X where X is the index diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 6cbacd49..79e1107c 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -200,9 +200,6 @@ class GpiIterator : public GpiHdl { virtual ~GpiIterator() { } virtual GpiObjHdl* next_handle() { return NULL; } - -public: - void *m_iter_hdl; }; diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index cad64585..0d1afc0b 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -26,7 +26,6 @@ ******************************************************************************/ #include "VhpiImpl.h" -#include extern "C" void handle_vhpi_callback(const vhpiCbDataT *cb_data); @@ -290,8 +289,6 @@ const char* VhpiSignalObjHdl::get_signal_value_binstr(void) m_binvalue.bufSize); } - LOG_WARN("Return value was %d passed in was %d:%d\n", ret, m_binvalue.bufSize, m_binvalue.numElems); - return m_binvalue.value.str; } @@ -409,12 +406,17 @@ VhpiNextPhaseCbHdl::VhpiNextPhaseCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), static vhpiOneToManyT options[] = { vhpiInternalRegions, vhpiSigDecls, - vhpiMembers + vhpiVarDecls, + vhpiPortDecls, + vhpiGenericDecls, + vhpiCompInstStmts }; std::vector VhpiIterator::iterate_over(options, options + sizeof(options) / sizeof(options[0])); -VhpiIterator::VhpiIterator(GpiImplInterface *impl, vhpiHandleT hdl) : GpiIterator(impl, hdl) +VhpiIterator::VhpiIterator(GpiImplInterface *impl, vhpiHandleT hdl) : GpiIterator(impl, hdl), + m_iterator(NULL), + m_iter_obj(hdl) { vhpiHandleT iterator; @@ -427,7 +429,7 @@ VhpiIterator::VhpiIterator(GpiImplInterface *impl, vhpiHandleT hdl) : GpiIterato if (iterator) break; - LOG_WARN("vhpi_scan vhpiOneToManyT=%d returned NULL", *curr_type); + LOG_WARN("vhpi_iterate vhpiOneToManyT=%d returned NULL", *curr_type); } if (NULL == iterator) { @@ -435,19 +437,33 @@ VhpiIterator::VhpiIterator(GpiImplInterface *impl, vhpiHandleT hdl) : GpiIterato return; } - LOG_WARN("Created iterator working from scope %d (%s)", + LOG_DEBUG("Created iterator working from scope %d (%s)", vhpi_get(vhpiKindP, hdl), vhpi_get_str(vhpiKindStrP, hdl)); - // HACK: vhpiRootInstK seems to be a null level of hierarchy, need to skip - //if (vhpiRootInstK == vhpi_get(vhpiKindP, hdl)) { - // vhpiHandleT root_iterator; - // hdl = vhpi_scan(iterator); - // root_iterator = vhpi_iterator(*curr_type, hdl); - // vhpi_release_handle(iterator); - // iterator = root_iterator; - // LOG_WARN("Skipped vhpiRootInstK to get to %s", vhpi_get_str(vhpiKindStrP, hdl)); - //} + /* On some simulators , Aldec vhpiRootInstK is a null level of hierachy + * We check that something is going to come back if not we try the level + * down + */ + + if (vhpiRootInstK == vhpi_get(vhpiKindP, hdl)) { + uint32_t children = 0; + vhpiHandleT tmp_hdl; + for(tmp_hdl = vhpi_scan(iterator); tmp_hdl && (children <= 1); tmp_hdl = vhpi_scan(iterator), children++) { } + + vhpi_release_handle(iterator); + iterator = vhpi_iterator(*curr_type, hdl); + + if (children == 1) { + vhpiHandleT root_iterator; + tmp_hdl = vhpi_scan(iterator); + root_iterator = vhpi_iterator(*curr_type, tmp_hdl); + vhpi_release_handle(iterator); + iterator = root_iterator; + LOG_WARN("Skipped vhpiRootInstK to get to %s", vhpi_get_str(vhpiKindStrP, tmp_hdl)); + m_iter_obj = tmp_hdl; + } + } m_iterator = iterator; } @@ -485,14 +501,14 @@ GpiObjHdl *VhpiIterator::next_handle(void) LOG_DEBUG("End of vhpiOneToManyT=%d iteration", *curr_type); vhpi_release_handle(m_iterator); + m_iterator = NULL; } else { LOG_DEBUG("No valid vhpiOneToManyT=%d iterator", *curr_type); } - curr_type++; - if (curr_type++ == iterate_over.end()) + if (++curr_type == iterate_over.end()) break; - m_iterator = vhpi_iterator(*curr_type, get_handle()); + m_iterator = vhpi_iterator(*curr_type, m_iter_obj); } while (!obj); diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index e5fe871a..c46f94ec 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -26,7 +26,6 @@ ******************************************************************************/ #include "VhpiImpl.h" -#include #include extern "C" { @@ -191,7 +190,7 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) if (fq_name == ":") { fq_name += name; } else { - fq_name = fq_name + ":" + name; + fq_name = fq_name + "." + name; } std::vector writable(fq_name.begin(), fq_name.end()); writable.push_back('\0'); diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 7e6841b6..cb7372f0 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -187,6 +187,7 @@ class VhpiIterator : public GpiIterator { private: vhpiHandleT m_iterator; + vhpiHandleT m_iter_obj; static std::vector iterate_over; std::vector::iterator curr_type; }; diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 9b584033..8d852042 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -28,7 +28,6 @@ ******************************************************************************/ #include "VpiImpl.h" -#include extern "C" int32_t handle_vpi_callback(p_cb_data cb_data); @@ -315,3 +314,99 @@ VpiNextPhaseCbHdl::VpiNextPhaseCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), { cb_data.reason = cbNextSimTime; } + +static int32_t options[] = { + vpiNet, + vpiNetBit, + vpiReg, + vpiRegBit, + vpiParameter, + vpiRegArray, + vpiNetArray, + vpiEnumNet, + vpiEnumVar, + vpiIntVar, + vpiStructVar, + vpiModule, + vpiInterface, + vpiModport, + vpiInterfaceArray, + vpiRefObj, + vpiPackedArrayVar, +}; + +std::vector VpiIterator::iterate_over(options, options + (sizeof(options) / sizeof(options[0]))); + +VpiIterator::VpiIterator(GpiImplInterface *impl, vpiHandle hdl) : GpiIterator(impl, hdl), + m_iterator(NULL) +{ + vpiHandle iterator; + + for (curr_type = iterate_over.begin(); + curr_type != iterate_over.end(); + curr_type++) { + iterator = vpi_iterate(*curr_type, hdl); + + if (iterator) + break; + + LOG_WARN("vpi_iterate type=%d returned NULL", *curr_type); + } + + if (NULL == iterator) { + LOG_WARN("vpi_iterate returned NULL for all types"); + return; + } + + LOG_DEBUG("Created iterator working from type %d", + *curr_type, + vpi_get_str(vpiFullName, hdl)); + + m_iterator = iterator; +} + +VpiIterator::~VpiIterator() +{ + if (m_iterator) + vpi_free_object(m_iterator); +} + +GpiObjHdl *VpiIterator::next_handle(void) +{ + vpiHandle obj; + GpiObjHdl *new_obj = NULL; + + do { + obj = NULL; + + if (m_iterator) { + obj = vpi_scan(m_iterator); + + if (NULL == obj) { + /* m_iterator will already be free'd internally here */ + m_iterator = NULL; + } else { + continue; + } + + LOG_DEBUG("End of type=%d iteration", *curr_type); + } else { + LOG_DEBUG("No valid type=%d iterator", *curr_type); + } + + if (curr_type++ == iterate_over.end()) + break; + m_iterator = vpi_iterate(*curr_type, get_handle()); + + } while (!obj); + + if (NULL == obj) { + LOG_DEBUG("No more children, all relationships tested"); + return new_obj; + } + + std::string name = vpi_get_str(vpiFullName, obj); + VpiImpl *vpi_impl = reinterpret_cast(m_impl); + new_obj = vpi_impl->create_gpi_obj_from_handle(obj, name); + return new_obj; +} diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 04c4a46f..b658c2f7 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -26,7 +26,6 @@ ******************************************************************************/ #include "VpiImpl.h" -#include extern "C" { @@ -86,6 +85,8 @@ gpi_objtype_t to_gpi_objtype(int32_t vpitype) return GPI_ARRAY; case vpiEnumNet: + case vpiEnumVar: + case vpiIntVar: return GPI_ENUM; case vpiParameter: @@ -98,6 +99,7 @@ gpi_objtype_t to_gpi_objtype(int32_t vpitype) case vpiModport: case vpiInterface: case vpiModule: + case vpiRefObj: return GPI_MODULE; default: @@ -248,29 +250,16 @@ GpiObjHdl *VpiImpl::get_root_handle(const char* name) GpiIterator *VpiImpl::iterate_handle(GpiObjHdl *obj_hdl) { + VpiIterator *new_iter; vpiHandle vpi_hdl = obj_hdl->get_handle(); - vpiHandle iterator; - iterator = vpi_iterate(vpiNet, vpi_hdl); - - GpiIterator *new_iter; - new_iter = new GpiIterator(this, iterator); + new_iter = new VpiIterator(this, vpi_hdl); return new_iter; } GpiObjHdl *VpiImpl::next_handle(GpiIterator *iter) { - vpiHandle obj; - GpiObjHdl *new_obj = NULL; - - obj = vpi_scan((vpiHandle)iter->m_iter_hdl); - - if (NULL==obj) - return new_obj; - - std::string name = vpi_get_str(vpiFullName, obj); - new_obj = create_gpi_obj_from_handle(obj, name); - return new_obj; + return iter->next_handle(); } GpiCbHdl *VpiImpl::register_timed_callback(uint64_t time_ps) diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 051cbd2f..fff4ab7c 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -30,6 +30,7 @@ #include "../gpi/gpi_priv.h" #include +#include // Should be run after every VPI call to check error status static inline int __check_vpi_error(const char *file, const char *func, long line) @@ -194,6 +195,20 @@ class VpiSignalObjHdl : public GpiSignalObjHdl { VpiValueCbHdl m_either_cb; }; +class VpiIterator : public GpiIterator { +public: + VpiIterator(GpiImplInterface *impl, vpiHandle hdl); + + virtual ~VpiIterator(); + + GpiObjHdl *next_handle(void); + +private: + vpiHandle m_iterator; + static std::vector iterate_over; + std::vector::iterator curr_type; +}; + class VpiImpl : public GpiImplInterface { public: VpiImpl(const std::string& name) : GpiImplInterface(name), @@ -219,9 +234,9 @@ class VpiImpl : public GpiImplInterface { GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); const char * reason_to_string(int reason); + GpiObjHdl* create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &name); private: - GpiObjHdl* create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &name); /* Singleton callbacks */ VpiReadwriteCbHdl m_read_write; VpiNextPhaseCbHdl m_next_phase; diff --git a/tests/test_cases/test_iteration/test_iteration.py b/tests/test_cases/test_iteration/test_iteration.py index c54c6a3d..96c000f2 100644 --- a/tests/test_cases/test_iteration/test_iteration.py +++ b/tests/test_cases/test_iteration/test_iteration.py @@ -60,5 +60,5 @@ def get_clock(dut): yield Timer(1) dut.aclk <= 1 yield Timer(1) - if dut.aclk.value is not 1: + if int(dut.aclk) is not 1: raise TestFailure("dut.aclk is not what we expected") From 6e6cf111ed175ac398ccab83d99da3ab1682090a Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 9 Aug 2015 20:24:18 +0100 Subject: [PATCH 0903/2656] Issue #134: Add testcase for real assignment --- tests/test_cases/issue_134/test_reals.py | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 tests/test_cases/issue_134/test_reals.py diff --git a/tests/test_cases/issue_134/test_reals.py b/tests/test_cases/issue_134/test_reals.py new file mode 100644 index 00000000..54296a1c --- /dev/null +++ b/tests/test_cases/issue_134/test_reals.py @@ -0,0 +1,27 @@ +import logging +import random +import sys + +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import RisingEdge, Timer, ReadOnly +from cocotb.result import TestFailure +from cocotb.binary import BinaryValue + + +@cocotb.test() +def assign_double(dut): + """ + Assign a random floating point value, read it back from the DUT and check + it matches what we assigned + """ + val = random.uniform(sys.float_info.min, sys.float_info.max) + log = logging.getLogger("cocotb.test") + yield Timer(1) + log.info("Setting the value %g" % val) + dut.stream_in_real = val + yield Timer(1) + got = float(dut.stream_out_real) + log.info("Read back value %g" % got) + if got != val: + raise TestFailure("Values didn't match!") From dc2b3ae46514ee8ef795c35b13678b6a1e0742f2 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 9 Aug 2015 20:24:38 +0100 Subject: [PATCH 0904/2656] Issue #134: Add reals to simple sample design --- tests/designs/sample_module/sample_module.v | 4 ++++ tests/test_cases/issue_134/Makefile | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/designs/sample_module/sample_module.v b/tests/designs/sample_module/sample_module.v index 4aefad1f..3756713d 100644 --- a/tests/designs/sample_module/sample_module.v +++ b/tests/designs/sample_module/sample_module.v @@ -34,10 +34,12 @@ module sample_module ( output reg stream_in_ready, input stream_in_valid, + input real stream_in_real, input [7:0] stream_in_data, input [63:0] stream_in_data_wide, input stream_out_ready, + output real stream_out_real, output reg [7:0] stream_out_data_comb, output reg [7:0] stream_out_data_registered ); @@ -51,6 +53,8 @@ always @(stream_in_data) always @(stream_out_ready) stream_in_ready = stream_out_ready; +always @(stream_in_real) + stream_out_real = stream_in_real; initial begin $dumpfile("waveform.vcd"); diff --git a/tests/test_cases/issue_134/Makefile b/tests/test_cases/issue_134/Makefile index a60ede6f..d2954e6e 100644 --- a/tests/test_cases/issue_134/Makefile +++ b/tests/test_cases/issue_134/Makefile @@ -25,7 +25,9 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +PWD := $(shell pwd) +COCOTB := $(PWD)/../../.. -include $(COCOTB)/tests/designs/viterbi_decoder_axi4s/Makefile +include $(COCOTB)/tests/designs/sample_module/Makefile -MODULE = issue_134 +MODULE ?= test_reals From defb7a55f12c9fd0ab0647e176acb84f5b32a372 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 9 Aug 2015 20:28:24 +0100 Subject: [PATCH 0905/2656] Issue #134: Add implementation to retrieve reals, working on VPI --- cocotb/handle.py | 7 ++++--- include/gpi.h | 2 ++ include/vpi_user.h | 1 + lib/gpi/GpiCommon.cpp | 6 ++++++ lib/gpi/gpi_priv.h | 1 + lib/simulator/simulatormodule.c | 22 ++++++++++++++++++++++ lib/simulator/simulatormodule.h | 2 ++ lib/simulator/simulatormodule_python2.c | 3 ++- lib/simulator/simulatormodule_python3.c | 3 ++- lib/vpi/VpiCbHdl.cpp | 13 +++++++++++++ lib/vpi/VpiImpl.cpp | 4 ++++ lib/vpi/VpiImpl.h | 1 + 12 files changed, 60 insertions(+), 5 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 405839d0..be3575fd 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -419,12 +419,13 @@ def _setimmediatevalue(self, value): simulator.set_signal_val_real(self._handle, value) def _getvalue(self): - raise NotImplementedError("Can't retrieve real values... yet") + return simulator.get_signal_val_real(self._handle) # We want to maintain compatability with python 2.5 so we can't use @property with a setter - value = property(_getvalue, _setcachedvalue, None, "A reference to the value") - + value = property(_getvalue, ModifiableObject._setcachedvalue, None, "A reference to the value") + def __float__(self): + return self._getvalue() def SimHandle(handle): diff --git a/include/gpi.h b/include/gpi.h index 3b3a1a9d..cb420bd6 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -134,6 +134,7 @@ typedef enum gpi_objtype_e { GPI_ARRAY = 6, GPI_ENUM = 7, GPI_STRUCTURE = 8, + GPI_REAL = 9 } gpi_objtype_t; // Functions for iterating over entries of a handle @@ -151,6 +152,7 @@ gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator); // This is all slightly verbose but it saves having to enumerate various value types // We only care about a limited subset of values. const char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl); +double gpi_get_signal_value_real(gpi_sim_hdl gpi_hdl); const char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl); const char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl); diff --git a/include/vpi_user.h b/include/vpi_user.h index b7fdd56d..8f22f22e 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -58,6 +58,7 @@ typedef uint32_t *vpiHandle; #define vpiModule 32 /* module instance */ #define vpiNet 36 /* scalar or vector net */ #define vpiNetBit 37 /* bit of a vector net */ +#define vpiRealVar 47 /* real variable */ #define vpiReg 48 /* scalar or vector reg */ #define vpiRegBit 49 /* bit of vector reg */ #define vpiParameter 41 /* module parameter */ diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 93f07fea..a1a57efa 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -264,6 +264,12 @@ const char *gpi_get_signal_value_binstr(gpi_sim_hdl sig_hdl) return obj_hdl->get_signal_value_binstr(); } +double gpi_get_signal_value_real(gpi_sim_hdl sig_hdl) +{ + GpiSignalObjHdl *obj_hdl = sim_to_hdl(sig_hdl); + return obj_hdl->get_signal_value_real(); +} + const char *gpi_get_signal_name_str(gpi_sim_hdl sig_hdl) { GpiSignalObjHdl *obj_hdl = sim_to_hdl(sig_hdl); diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 7fccceb7..42bb53e2 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -133,6 +133,7 @@ class GpiSignalObjHdl : public GpiObjHdl { virtual ~GpiSignalObjHdl() { } // Provide public access to the implementation (composition vs inheritance) virtual const char* get_signal_value_binstr(void) = 0; + virtual double get_signal_value_real(void) = 0; int m_length; diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 379d3ef4..2a8c1883 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -548,6 +548,28 @@ static PyObject *get_signal_val(PyObject *self, PyObject *args) return retstr; } +static PyObject *get_signal_val_real(PyObject *self, PyObject *args) +{ + gpi_sim_hdl hdl; + double result; + PyObject *retval; + + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + if (!PyArg_ParseTuple(args, "l", &hdl)) { + DROP_GIL(gstate); + return NULL; + } + + result = gpi_get_signal_value_real(hdl); + retval = Py_BuildValue("d", result); + + DROP_GIL(gstate); + + return retval; +} + static PyObject *set_signal_val(PyObject *self, PyObject *args) { diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 594a68cd..fc520dd1 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -58,6 +58,7 @@ static PyObject *log_msg(PyObject *self, PyObject *args); // Raise an exception on failure // Return None if for example get bin_string on enum? static PyObject *get_signal_val(PyObject *self, PyObject *args); +static PyObject *get_signal_val_real(PyObject *self, PyObject *args); static PyObject *set_signal_val(PyObject *self, PyObject *args); static PyObject *set_signal_val_str(PyObject *self, PyObject *args); static PyObject *set_signal_val_real(PyObject *self, PyObject *args); @@ -83,6 +84,7 @@ static PyObject *deregister_callback(PyObject *self, PyObject *args); static PyMethodDef SimulatorMethods[] = { {"log_msg", log_msg, METH_VARARGS, "Log a message"}, {"get_signal_val", get_signal_val, METH_VARARGS, "Get the value of a signal as a binary string"}, + {"get_signal_val_real", get_signal_val_real, METH_VARARGS, "Get the value of a signal as a double precision float"}, {"set_signal_val", set_signal_val, METH_VARARGS, "Set the value of a signal"}, {"set_signal_val_str", set_signal_val_str, METH_VARARGS, "Set the value of a signal using a binary string"}, {"set_signal_val_real", set_signal_val_real, METH_VARARGS, "Set the value of a signal using a double"}, diff --git a/lib/simulator/simulatormodule_python2.c b/lib/simulator/simulatormodule_python2.c index 64d7dffc..d88eb30c 100644 --- a/lib/simulator/simulatormodule_python2.c +++ b/lib/simulator/simulatormodule_python2.c @@ -36,7 +36,8 @@ MODULE_ENTRY_POINT(void) rc |= PyModule_AddIntConstant(simulator, "REG", GPI_REGISTER); rc |= PyModule_AddIntConstant(simulator, "NETARRAY", GPI_ARRAY); rc |= PyModule_AddIntConstant(simulator, "ENUM", GPI_ENUM); - rc |= PyModule_AddIntConstant(simulator, "ENUM", GPI_STRUCTURE); + rc |= PyModule_AddIntConstant(simulator, "STRUCTURE", GPI_STRUCTURE); + rc |= PyModule_AddIntConstant(simulator, "REAL", GPI_REAL); if (rc != 0) fprintf(stderr, "Failed to add module constants!\n"); } diff --git a/lib/simulator/simulatormodule_python3.c b/lib/simulator/simulatormodule_python3.c index 4dcccf8d..38f0d87b 100644 --- a/lib/simulator/simulatormodule_python3.c +++ b/lib/simulator/simulatormodule_python3.c @@ -55,7 +55,8 @@ MODULE_ENTRY_POINT(void) rc |= PyModule_AddIntConstant(simulator, "REG", GPI_REGISTER); rc |= PyModule_AddIntConstant(simulator, "NETARRAY", GPI_ARRAY); rc |= PyModule_AddIntConstant(simulator, "ENUM", GPI_ENUM); - rc |= PyModule_AddIntConstant(simulator, "ENUM", GPI_STRUCTURE); + rc |= PyModule_AddIntConstant(simulator, "STRUCTURE", GPI_STRUCTURE); + rc |= PyModule_AddIntConstant(simulator, "REAL", GPI_REAL); if (rc != 0) fprintf(stderr, "Failed to add module constants!\n"); diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 16433909..992fb272 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -136,6 +136,19 @@ const char* VpiSignalObjHdl::get_signal_value_binstr(void) return value_p->value.str; } +double VpiSignalObjHdl::get_signal_value_real(void) +{ + FENTER + s_vpi_value value_s = {vpiRealVal}; + p_vpi_value value_p = &value_s; + + vpi_get_value(GpiObjHdl::get_handle(), value_p); + check_vpi_error(); + + return value_p->value.real; +} + + // Value related functions int VpiSignalObjHdl::set_signal_value(int value) { diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 04c4a46f..7306f503 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -79,6 +79,9 @@ gpi_objtype_t to_gpi_objtype(int32_t vpitype) case vpiRegBit: return GPI_REGISTER; + case vpiRealVar: + return GPI_REAL; + case vpiInterfaceArray: case vpiPackedArrayVar: case vpiRegArray: @@ -127,6 +130,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &n case vpiEnumNet: case vpiEnumVar: case vpiIntVar: + case vpiRealVar: new_obj = new VpiSignalObjHdl(this, new_hdl, to_gpi_objtype(type)); break; case vpiStructVar: diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 7992fe72..42106bdd 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -181,6 +181,7 @@ class VpiSignalObjHdl : public GpiSignalObjHdl { virtual ~VpiSignalObjHdl() { } const char* get_signal_value_binstr(void); + double get_signal_value_real(void); int set_signal_value(const int value); int set_signal_value(const double value); From 5a20cde7bef4761ba14339426725098b7fcda45e Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 9 Aug 2015 20:28:45 +0100 Subject: [PATCH 0906/2656] Issue #134: Cleanup - remove old test (replaced with more specific name) --- tests/test_cases/issue_134/issue_134.py | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 tests/test_cases/issue_134/issue_134.py diff --git a/tests/test_cases/issue_134/issue_134.py b/tests/test_cases/issue_134/issue_134.py deleted file mode 100644 index 31a0154e..00000000 --- a/tests/test_cases/issue_134/issue_134.py +++ /dev/null @@ -1,17 +0,0 @@ -# A set of regression tests for open issues - -import cocotb -from cocotb.clock import Clock -from cocotb.triggers import RisingEdge, Timer, ReadOnly -from cocotb.result import TestFailure -from cocotb.binary import BinaryValue - - -@cocotb.test(expect_error=True) -def false_double(dut): - yield Timer(0) - - dut.aclk = float(1.0) - - yield Timer(1) - From 904db845d3fa01ecb5deb76abb53e72792db5389 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Mon, 10 Aug 2015 14:26:21 +0200 Subject: [PATCH 0907/2656] the waveform.vcd generated is not under sim_build/ directory --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0ac55144..282075b4 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ make # View the waveform - gtkwave sim_build/waveform.vcd + gtkwave waveform.vcd ## Tutorials and examples From 6f36c05cd7c0aa7d46d10e827f647fcd6aeec548 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Mon, 10 Aug 2015 14:45:24 +0200 Subject: [PATCH 0908/2656] grof2dot repository url changed to github --- examples/endian_swapper/tests/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index 83cd2df6..732f10e2 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -70,7 +70,7 @@ hal: -cd ../cosim && make # Stuff below is useful for profiling -# Need grof2dot from https://code.google.com/p/jrfonseca.gprof2dot/ +# Need grof2dot from https://github.com/jrfonseca/gprof2dot test_profile.pstat: sim callgraph.svg: test_profile.pstat From 3bd5b03cb0512f6fd7ef198acfd9d90c966d5184 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 11 Aug 2015 21:14:29 +0100 Subject: [PATCH 0909/2656] Issue #134: Fixup compilation of VHPI and FLI with empty get_signal_value_real --- lib/fli/FliImpl.h | 7 ++++++- lib/vhpi/VhpiImpl.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index 6777a533..3ab2fe11 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -90,8 +90,10 @@ class FliSignalObjHdl : public GpiSignalObjHdl { } const char* get_signal_value_binstr(void); + double get_signal_value_real(void) { return 0.0; } + int set_signal_value(const int value); - int set_signal_value(const double value) = { return 0; } + int set_signal_value(const double value) { return 0; } int set_signal_value(std::string &value); int initialise(std::string &name); GpiCbHdl *value_change_cb(unsigned int edge); @@ -125,8 +127,11 @@ class FliVariableObjHdl : public GpiSignalObjHdl { } const char* get_signal_value_binstr(void); + double get_signal_value_real(void) { return 0.0; } + int set_signal_value(const int value); int set_signal_value(std::string &value); + int set_signal_value(const double value) { return 0; } int initialise(std::string &name); GpiCbHdl *value_change_cb(unsigned int edge); diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index dfe93b3f..0a4468e3 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -159,6 +159,7 @@ class VhpiSignalObjHdl : public GpiSignalObjHdl { virtual ~VhpiSignalObjHdl(); const char* get_signal_value_binstr(void); + double get_signal_value_real(void) { return 0.0; } int set_signal_value(const int value); int set_signal_value(const double value); From ae929daf907603e19c86b3915cf46bc245f12ac2 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 11 Aug 2015 21:32:16 +0100 Subject: [PATCH 0910/2656] Issue #258: No longer required envvar COCOTB to be set in a test directory, as an error to do this --- tests/Makefile | 3 +-- tests/designs/plusargs_module/Makefile | 3 +++ tests/test_cases/issue_120/Makefile | 2 +- tests/test_cases/issue_134/Makefile | 5 +---- tests/test_cases/issue_142/Makefile | 2 +- tests/test_cases/test_closedown/Makefile | 2 +- tests/test_cases/test_cocotb/Makefile | 3 +-- tests/test_cases/test_discovery/Makefile | 2 +- tests/test_cases/test_exit_error/Makefile | 2 +- tests/test_cases/test_external/Makefile | 2 +- tests/test_cases/test_iteration/Makefile | 2 +- tests/test_cases/test_plusargs/Makefile | 2 +- 12 files changed, 14 insertions(+), 16 deletions(-) diff --git a/tests/Makefile b/tests/Makefile index efd7abc3..fe874f3d 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -25,8 +25,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -COCOTB?=$(shell pwd)/../ -export COCOTB +include ../makefiles/Makefile.inc REGRESSIONS := $(shell ls test_cases/) diff --git a/tests/designs/plusargs_module/Makefile b/tests/designs/plusargs_module/Makefile index 4b709f7e..23142062 100644 --- a/tests/designs/plusargs_module/Makefile +++ b/tests/designs/plusargs_module/Makefile @@ -30,6 +30,9 @@ TOPLEVEL = tb_top +PWD=$(shell pwd) +COCOTB=$(PWD)/../../.. + ifeq ($(OS),Msys) PWD=$(shell sh -c 'pwd -W') else diff --git a/tests/test_cases/issue_120/Makefile b/tests/test_cases/issue_120/Makefile index 003e1637..9931fd09 100644 --- a/tests/test_cases/issue_120/Makefile +++ b/tests/test_cases/issue_120/Makefile @@ -28,6 +28,6 @@ ############################################################################### -include $(COCOTB)/tests/designs/sample_module/Makefile +include ../../designs/sample_module/Makefile MODULE = issue_120 diff --git a/tests/test_cases/issue_134/Makefile b/tests/test_cases/issue_134/Makefile index d2954e6e..a495781e 100644 --- a/tests/test_cases/issue_134/Makefile +++ b/tests/test_cases/issue_134/Makefile @@ -25,9 +25,6 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -PWD := $(shell pwd) -COCOTB := $(PWD)/../../.. - -include $(COCOTB)/tests/designs/sample_module/Makefile +include ../../designs/sample_module/Makefile MODULE ?= test_reals diff --git a/tests/test_cases/issue_142/Makefile b/tests/test_cases/issue_142/Makefile index 25d08d19..ceb5b7b8 100644 --- a/tests/test_cases/issue_142/Makefile +++ b/tests/test_cases/issue_142/Makefile @@ -26,6 +26,6 @@ ############################################################################### -include $(COCOTB)/tests/designs/sample_module/Makefile +include ../../designs/sample_module/Makefile MODULE = issue_142 diff --git a/tests/test_cases/test_closedown/Makefile b/tests/test_cases/test_closedown/Makefile index e4b4d813..4cf069a6 100644 --- a/tests/test_cases/test_closedown/Makefile +++ b/tests/test_cases/test_closedown/Makefile @@ -25,6 +25,6 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -include $(COCOTB)/tests/designs/close_module/Makefile +include ../../designs/close_module/Makefile MODULE=test_closedown diff --git a/tests/test_cases/test_cocotb/Makefile b/tests/test_cases/test_cocotb/Makefile index d2dcdc3a..a643a47a 100644 --- a/tests/test_cases/test_cocotb/Makefile +++ b/tests/test_cases/test_cocotb/Makefile @@ -27,7 +27,6 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### - -include $(COCOTB)/tests/designs/sample_module/Makefile +include ../../designs/sample_module/Makefile MODULE = test_cocotb diff --git a/tests/test_cases/test_discovery/Makefile b/tests/test_cases/test_discovery/Makefile index f44fff6a..fb054750 100644 --- a/tests/test_cases/test_discovery/Makefile +++ b/tests/test_cases/test_discovery/Makefile @@ -26,6 +26,6 @@ ############################################################################### -include $(COCOTB)/tests/designs/sample_module/Makefile +include ../../designs/sample_module/Makefile MODULE = test_discovery diff --git a/tests/test_cases/test_exit_error/Makefile b/tests/test_cases/test_exit_error/Makefile index 364dac1f..b3cebc8a 100644 --- a/tests/test_cases/test_exit_error/Makefile +++ b/tests/test_cases/test_exit_error/Makefile @@ -26,6 +26,6 @@ ############################################################################### -include $(COCOTB)/tests/designs/sample_module/Makefile +include ../../designs/sample_module/Makefile MODULE = test_exit diff --git a/tests/test_cases/test_external/Makefile b/tests/test_cases/test_external/Makefile index b178eb83..83403ffb 100644 --- a/tests/test_cases/test_external/Makefile +++ b/tests/test_cases/test_external/Makefile @@ -28,6 +28,6 @@ ############################################################################### -include $(COCOTB)/tests/designs/sample_module/Makefile +include ../../designs/sample_module/Makefile MODULE = test_external diff --git a/tests/test_cases/test_iteration/Makefile b/tests/test_cases/test_iteration/Makefile index ff7d75f1..9851a679 100644 --- a/tests/test_cases/test_iteration/Makefile +++ b/tests/test_cases/test_iteration/Makefile @@ -26,6 +26,6 @@ ############################################################################### -include $(COCOTB)/tests/designs/viterbi_decoder_axi4s/Makefile +include ../../designs/viterbi_decoder_axi4s/Makefile #GPI_EXTRA=vhpi MODULE = test_iteration diff --git a/tests/test_cases/test_plusargs/Makefile b/tests/test_cases/test_plusargs/Makefile index a1688744..a93b3c8c 100644 --- a/tests/test_cases/test_plusargs/Makefile +++ b/tests/test_cases/test_plusargs/Makefile @@ -27,7 +27,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -include $(COCOTB)/tests/designs/plusargs_module/Makefile +include ../../designs/plusargs_module/Makefile PLUSARGS=+foo=bar +test1 +test2 +options=fubar From a379ba0426229a948167729ebdc2cf0fa0a1e834 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Tue, 11 Aug 2015 21:33:33 +0100 Subject: [PATCH 0911/2656] Issue #134: Older versions of Icarus don't seem to support reals --- tests/designs/sample_module/sample_module.v | 4 ++++ tests/test_cases/issue_134/test_reals.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/designs/sample_module/sample_module.v b/tests/designs/sample_module/sample_module.v index 3756713d..8a111fad 100644 --- a/tests/designs/sample_module/sample_module.v +++ b/tests/designs/sample_module/sample_module.v @@ -34,12 +34,16 @@ module sample_module ( output reg stream_in_ready, input stream_in_valid, +`ifndef __ICARUS__ input real stream_in_real, +`endif input [7:0] stream_in_data, input [63:0] stream_in_data_wide, input stream_out_ready, +`ifndef __ICARUS__ output real stream_out_real, +`endif output reg [7:0] stream_out_data_comb, output reg [7:0] stream_out_data_registered ); diff --git a/tests/test_cases/issue_134/test_reals.py b/tests/test_cases/issue_134/test_reals.py index 54296a1c..97a33d5c 100644 --- a/tests/test_cases/issue_134/test_reals.py +++ b/tests/test_cases/issue_134/test_reals.py @@ -9,7 +9,7 @@ from cocotb.binary import BinaryValue -@cocotb.test() +@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) def assign_double(dut): """ Assign a random floating point value, read it back from the DUT and check From 93dfb35d1f9c3092b4ec3bbdbba85be76918fe17 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 11 Aug 2015 21:54:02 +0100 Subject: [PATCH 0912/2656] Issue #134: Fully exclude reals on icarus --- tests/designs/sample_module/sample_module.v | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/designs/sample_module/sample_module.v b/tests/designs/sample_module/sample_module.v index 8a111fad..a34ced0c 100644 --- a/tests/designs/sample_module/sample_module.v +++ b/tests/designs/sample_module/sample_module.v @@ -57,8 +57,10 @@ always @(stream_in_data) always @(stream_out_ready) stream_in_ready = stream_out_ready; +`ifndef __ICARUS__ always @(stream_in_real) stream_out_real = stream_in_real; +`endif initial begin $dumpfile("waveform.vcd"); From 4dca9e5869ff40e0ae0851b15d06b7a013f6954b Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 11 Aug 2015 22:21:02 +0100 Subject: [PATCH 0913/2656] Cleanup: Skip test that causes IUS to exit, and add to list of expected fail for test_afterdelay_in_readonly --- tests/test_cases/test_cocotb/test_cocotb.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 6a0b6233..4f5ebe4d 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -191,7 +191,8 @@ def do_test_afterdelay_in_readonly(dut, delay): expect_fail=cocotb.SIM_NAME in ["Icarus Verilog", "Riviera-PRO", "ModelSim ALTERA STARTER EDITION", - "ModelSim DE"]) + "ModelSim DE", + "ncsim(64)"]) def test_readwrite_in_readonly(dut): """Test doing invalid sim operation""" global exited @@ -205,7 +206,8 @@ def test_readwrite_in_readonly(dut): @cocotb.test(expect_fail=cocotb.SIM_NAME in ["Icarus Verilog", - "Chronologic Simulation VCS Release"]) + "Chronologic Simulation VCS Release"], + skip=cocotb.SIM_NAME in ["ncsim(64)"]) def test_afterdelay_in_readonly(dut): """Test doing invalid sim operation""" global exited From 8f605a0d1a42573c138743c74abd15a05f751846 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Wed, 12 Aug 2015 07:53:13 +0100 Subject: [PATCH 0914/2656] Issue #134: Update makefile to discover COCOTB for convenience --- tests/test_cases/test_iteration/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_cases/test_iteration/Makefile b/tests/test_cases/test_iteration/Makefile index ff7d75f1..ed4b328e 100644 --- a/tests/test_cases/test_iteration/Makefile +++ b/tests/test_cases/test_iteration/Makefile @@ -25,6 +25,8 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +PWD := $(shell pwd) +COCOTB := $(PWD)/../../.. include $(COCOTB)/tests/designs/viterbi_decoder_axi4s/Makefile #GPI_EXTRA=vhpi From 95c5825d4d158f54c9ee561da0883a7f5cb83c15 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Wed, 12 Aug 2015 07:54:36 +0100 Subject: [PATCH 0915/2656] Issue #134: Add VHPI support for reals --- lib/vhpi/VhpiCbHdl.cpp | 7 +++++++ lib/vhpi/VhpiImpl.h | 1 + tests/designs/sample_module/sample_module.vhdl | 7 +++++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 7ee77bf8..91c59d8f 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -333,6 +333,13 @@ const char* VhpiSignalObjHdl::get_signal_value_binstr(void) return m_binvalue.value.str; } + +double VhpiSignalObjHdl::get_signal_value_real(void) +{ + return vhpi_get_real(vhpiRealValP, GpiObjHdl::get_handle()); +} + + GpiCbHdl * VhpiSignalObjHdl::value_change_cb(unsigned int edge) { VhpiValueCbHdl *cb = NULL; diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 477858da..98b25b51 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -159,6 +159,7 @@ class VhpiSignalObjHdl : public GpiSignalObjHdl { virtual ~VhpiSignalObjHdl(); const char* get_signal_value_binstr(void); + double get_signal_value_real(void); int set_signal_value(const int value); int set_signal_value(const double value); diff --git a/tests/designs/sample_module/sample_module.vhdl b/tests/designs/sample_module/sample_module.vhdl index 4efd9b90..519147fd 100644 --- a/tests/designs/sample_module/sample_module.vhdl +++ b/tests/designs/sample_module/sample_module.vhdl @@ -39,10 +39,12 @@ entity sample_module is stream_in_data_wide : in std_ulogic_vector(63 downto 0); stream_in_valid : in std_ulogic; stream_in_ready : out std_ulogic; + stream_in_real : in real; stream_out_data_comb : out std_ulogic_vector(7 downto 0); stream_out_data_registered : out std_ulogic_vector(7 downto 0); - stream_out_ready : in std_ulogic + stream_out_ready : in std_ulogic; + stream_out_real : out real ); end; @@ -58,5 +60,6 @@ end process; stream_out_data_comb <= stream_in_data; stream_in_ready <= stream_out_ready; +stream_out_real <= stream_in_real; -end architecture; \ No newline at end of file +end architecture; From 65549f5bb0c9e50024cc83ab0493ae5b3d142610 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Wed, 12 Aug 2015 07:55:15 +0100 Subject: [PATCH 0916/2656] Issue #134: Raise an exception rather than creating new attributes to hierarchy --- cocotb/handle.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index be3575fd..eaf0a9f0 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -166,17 +166,22 @@ class HierarchyObject(SimHandleBase): """ Hierarchy objects don't have values, they are effectively scopes or namespaces """ + def __setattr__(self, name, value): """ Provide transparent access to signals via the hierarchy Slightly hacky version of operator overloading in Python + + Raise an AttributeError if users attempt to create new members which + don't exist in the design. """ - if not name.startswith('_') and name not in self._compat_mapping \ - and self.__hasattr__(name): - getattr(self, name)._setcachedvalue(value) - return - object.__setattr__(self, name, value) + if name.startswith("_") or name in self._compat_mapping: + return object.__setattr__(self, name, value) + if self.__hasattr__(name): + return getattr(self, name)._setcachedvalue(value) + raise AttributeError("Attempt to access %s which isn't present in %s" %( + name, self._name)) def __iter__(self): """ From 1fe71d0731e8dc209e8035fb2fd9ed1f41af26bd Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Wed, 12 Aug 2015 14:12:55 +0200 Subject: [PATCH 0917/2656] cocotb/drivers/avalon.py: do not set readdatavalid signal if not exists --- cocotb/drivers/avalon.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index cd1eb563..acf2e7c5 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -248,7 +248,8 @@ def _respond(self): self.log.debug("sending 0x%x (%s)" % (self._val.integer, self._val.binstr)) self.bus.readdata <= self._val - self.bus.readdatavalid <= 1 + if hasattr(self.bus, "readdatavalid"): + self.bus.readdatavalid <= 1 elif hasattr(self.bus, "readdatavalid"): self.bus.readdatavalid <= 0 From 9666ccae520eef1f48edfbf6b52d2043322848c6 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Wed, 12 Aug 2015 17:40:27 +0200 Subject: [PATCH 0918/2656] little mistake in error message cocorutine -> coroutine --- cocotb/scheduler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 7bd41302..6c7ea642 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -505,7 +505,7 @@ def schedule(self, coroutine, trigger=None): % str(coroutine)) msg += ("\nGot type: %s repr: %s str: %s" % (type(result), repr(result), str(result))) - msg += "\nDid you forget to decorate with @cocotb.cocorutine?" + msg += "\nDid you forget to decorate with @cocotb.coroutine?" try: raise_error(self, msg) except Exception as e: From b90aac39b145a9eb5ccb29d9d9d4e079e0fc4b79 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Thu, 13 Aug 2015 17:48:24 +0200 Subject: [PATCH 0919/2656] avalonMM: adding byteenable signal --- cocotb/drivers/avalon.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index acf2e7c5..a2d1e919 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -54,7 +54,7 @@ class AvalonMM(BusDriver): """ _signals = ["address"] _optional_signals = ["readdata", "read", "write", "waitrequest", - "writedata", "readdatavalid"] + "writedata", "readdatavalid", "byteenable"] def __init__(self, entity, name, clock): BusDriver.__init__(self, entity, name, clock) @@ -73,6 +73,9 @@ def __init__(self, entity, name, clock): self.bus.writedata <= v self._can_write = True + if hasattr(self.bus, "byteenable"): + self.bus.byteenable.setimmediatevalue(0) + self.bus.address.setimmediatevalue(0) def read(self, address): @@ -124,6 +127,8 @@ def read(self, address, sync=True): yield RisingEdge(self.clock) self.bus.address <= address self.bus.read <= 1 + if hasattr(self.bus, "byteenable"): + self.bus.byteenable <= int("1"*len(self.bus.byteenable), 2) # Wait for waitrequest to be low if hasattr(self.bus, "waitrequest"): @@ -136,6 +141,8 @@ def read(self, address, sync=True): # Deassert read self.bus.read <= 0 + if hasattr(self.bus, "byteenable"): + self.bus.byteenable <= 0 # Get the data yield ReadOnly() @@ -162,6 +169,8 @@ def write(self, address, value): self.bus.address <= address self.bus.writedata <= value self.bus.write <= 1 + if hasattr(self.bus, "byteenable"): + self.bus.byteenable <= int("1"*len(self.bus.byteenable), 2) # Wait for waitrequest to be low if hasattr(self.bus, "waitrequest"): @@ -170,6 +179,9 @@ def write(self, address, value): # Deassert write yield RisingEdge(self.clock) self.bus.write <= 0 + if hasattr(self.bus, "byteenable"): + self.bus.byteenable <= 0 + v = self.bus.writedata.value v.binstr = "x" * len(self.bus.writedata) self.bus.writedata <= v From 7853875a819f944a47c6f056f343c2ac3c77421a Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 14 Aug 2015 14:08:02 +0100 Subject: [PATCH 0920/2656] Issue #134: Add support for detecting Real types in VHPI --- lib/vhpi/VhpiCbHdl.cpp | 19 ++++++++++-------- lib/vhpi/VhpiImpl.cpp | 25 ++++++++++++++++++++++-- tests/test_cases/issue_134/test_reals.py | 1 + 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 48797172..b4b0a1ff 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -55,9 +55,10 @@ int VhpiSignalObjHdl::initialise(std::string &name) { break; } - case vhpiRealVal: - break; - + case vhpiRealVal: { + GpiObjHdl::initialise(name); + return 0; + } case vhpiEnumVecVal: case vhpiLogicVecVal: { m_size = vhpi_get(vhpiSizeP, GpiObjHdl::get_handle()); @@ -70,10 +71,10 @@ int VhpiSignalObjHdl::initialise(std::string &name) { break; } case vhpiRawDataVal: - case vhpiIntVal: - GpiObjHdl::initialise(name); - return 0; - break; + case vhpiIntVal: { + GpiObjHdl::initialise(name); + return 0; + } default: { LOG_CRITICAL("Unable to determine property for %s (%d) format object", @@ -227,7 +228,7 @@ int VhpiSignalObjHdl::set_signal_value(int value) } case vhpiRealVal: - LOG_WARN("Attempt to vhpiRealVal signal with integer"); + LOG_WARN("Attempt to set vhpiRealVal signal with integer"); return 0; default: { @@ -244,6 +245,8 @@ int VhpiSignalObjHdl::set_signal_value(double value) { switch (m_value.format) { case vhpiRealVal: + m_value.numElems = 1; + m_value.bufSize = sizeof(value); m_value.value.real = value; break; diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index c46f94ec..e196b0ec 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -153,25 +153,46 @@ gpi_objtype_t to_gpi_objtype(vhpiIntT vhpitype) GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string &name) { vhpiIntT type; + gpi_objtype_t gpi_type; GpiObjHdl *new_obj = NULL; if (vhpiVerilog == (type = vhpi_get(vhpiKindP, new_hdl))) { LOG_DEBUG("vhpiVerilog returned from vhpi_get(vhpiType, ...)") return NULL; } + + gpi_type = to_gpi_objtype(type); + + // Sadly VHPI doesn't have a "Real" type returned - we just get + // vhpiPortDeclK rather than the signal type. + // + // We workaround this by querying the format value and overriding the + // result of to_gpi_objtype + vhpiValueT value; + value.format = vhpiObjTypeVal; + value.bufSize = 0; + value.numElems = 0; + value.value.str = NULL; + + vhpi_get_value(new_hdl, &value); + if (vhpiRealVal == value.format) { + LOG_DEBUG("Detected a REAL type", name.c_str()); + gpi_type = GPI_REAL; + } + /* What sort of isntance is this ?*/ switch (type) { case vhpiPortDeclK: case vhpiSigDeclK: case vhpiIndexedNameK: - new_obj = new VhpiSignalObjHdl(this, new_hdl, to_gpi_objtype(type)); + new_obj = new VhpiSignalObjHdl(this, new_hdl, gpi_type); break; case vhpiForGenerateK: case vhpiIfGenerateK: case vhpiCompInstStmtK: case vhpiProcessStmtK: case vhpiSimpleSigAssignStmtK: - new_obj = new GpiObjHdl(this, new_hdl, to_gpi_objtype(type)); + new_obj = new GpiObjHdl(this, new_hdl, gpi_type); break; default: LOG_WARN("Not able to map type (%s) %u to object", vhpi_get_str(vhpiKindStrP, new_hdl), type); diff --git a/tests/test_cases/issue_134/test_reals.py b/tests/test_cases/issue_134/test_reals.py index 97a33d5c..79ba8feb 100644 --- a/tests/test_cases/issue_134/test_reals.py +++ b/tests/test_cases/issue_134/test_reals.py @@ -21,6 +21,7 @@ def assign_double(dut): log.info("Setting the value %g" % val) dut.stream_in_real = val yield Timer(1) + yield Timer(1) # Workaround for VHPI scheduling - needs investigation got = float(dut.stream_out_real) log.info("Read back value %g" % got) if got != val: From 8391fc933c5bc76da591c72b0c9c67782b974d1b Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Tue, 18 Aug 2015 08:42:46 +0200 Subject: [PATCH 0921/2656] Avalon.py: read access is 2 clock period when readLatency = 1 --- cocotb/drivers/avalon.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index a2d1e919..f4922edd 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -139,15 +139,17 @@ def read(self, address, sync=True): # should take a dictionary of Avalon properties. yield RisingEdge(self.clock) + # Get the data + yield ReadOnly() + data = self.bus.readdata.value + + yield RisingEdge(self.clock) + # Deassert read self.bus.read <= 0 if hasattr(self.bus, "byteenable"): self.bus.byteenable <= 0 - # Get the data - yield ReadOnly() - data = self.bus.readdata.value - yield NextTimeStep() self._release_lock() raise ReturnValue(data) From 143e968f24ccb9cb85545269d9718761c7330ee6 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 18 Aug 2015 20:47:57 +0100 Subject: [PATCH 0922/2656] Issue #17: Add support for integers, by product of handle refactoring and querying of types from GPI --- cocotb/handle.py | 41 ++++++++++++++-- examples/endian_swapper/tests/Makefile | 6 +-- include/gpi.h | 3 +- include/vpi_user.h | 4 +- lib/fli/FliImpl.cpp | 6 +-- lib/fli/FliImpl.h | 6 ++- lib/gpi/GpiCommon.cpp | 8 ++- lib/gpi/gpi_priv.h | 3 +- lib/simulator/simulatormodule.c | 38 +++++++++++--- lib/simulator/simulatormodule.h | 20 ++++---- lib/vhpi/VhpiCbHdl.cpp | 49 ++++++++++++++----- lib/vhpi/VhpiImpl.cpp | 41 +++++++++------- lib/vhpi/VhpiImpl.h | 3 +- lib/vpi/VpiCbHdl.cpp | 22 ++++++--- lib/vpi/VpiImpl.cpp | 4 ++ lib/vpi/VpiImpl.h | 3 +- tests/designs/sample_module/sample_module.v | 10 +++- .../designs/sample_module/sample_module.vhdl | 5 +- tests/test_cases/issue_134/Makefile | 2 +- tests/test_cases/issue_134/test_integers.py | 27 ++++++++++ 20 files changed, 228 insertions(+), 73 deletions(-) create mode 100644 tests/test_cases/issue_134/test_integers.py diff --git a/cocotb/handle.py b/cocotb/handle.py index 02e86a5a..8f1d50ad 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -301,7 +301,7 @@ def _getvalue(self): #value = property(_getvalue, None, None, "A reference to the value") def _get_value_str(self): - return simulator.get_signal_val(self._handle) + return simulator.get_signal_val_str(self._handle) def __len__(self): """Returns the 'length' of the underlying object. @@ -360,7 +360,7 @@ def _setimmediatevalue(self, value): Assigning integers less than 32-bits is faster """ if isinstance(value, get_python_integer_types()) and value < 0x7fffffff: - simulator.set_signal_val(self._handle, value) + simulator.set_signal_val_long(self._handle, value) return if isinstance(value, ctypes.Structure): @@ -436,6 +436,40 @@ def _getvalue(self): def __float__(self): return self._getvalue() +class IntegerObject(ModifiableObject): + """ + Specific object handle for Integer and Enum signals and variables + """ + + def _setimmeadiatevalue(self, value): + """ + Set the value of the underlying simulation object to value. + + Args: + value (int) + The value to drive onto the simulator object + + Raises: + TypeError + + This operation will fail unless the handle refers to a modifiable + object eg net, signal or variable. + """ + if not isinstance(value, int): + self._log.critical("Unsupported type for integer value assignment: %s (%s)" % (type(value), repr(value))) + raise TypeError("Unable to set simulator value with type %s" % (type(value))) + + simulator.set_signal_val_long(self._handle, value) + + def _getvalue(self): + return simulator.get_signal_val_long(self._handle) + + # We want to maintain compatability with python 2.5 so we can't use @property with a setter + value = property(_getvalue, ModifiableObject._setcachedvalue, None, "A reference to the value") + + def __int__(self): + return self._getvalue() + def SimHandle(handle): """ @@ -445,7 +479,8 @@ def SimHandle(handle): _type2cls = { simulator.MODULE: HierarchyObject, simulator.REG: ModifiableObject, - simulator.REAL: RealObject + simulator.REAL: RealObject, + simulator.ENUM: IntegerObject } t = simulator.get_type(handle) diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index 83cd2df6..4e4b29e9 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -28,7 +28,7 @@ ############################################################################### # Default to verilog -GPI_IMPL ?= vpi +TOPLEVEL_LANG ?= verilog WPWD=$(shell sh -c 'pwd -W') PWD=$(shell pwd) @@ -43,13 +43,13 @@ PYTHONPATH := $(PWD)/../cosim:$(PYTHONPATH) endif export PYTHONPATH -ifeq ($(GPI_IMPL),vpi) +ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES = $(WPWD)/../hdl/endian_swapper.sv TOPLEVEL = endian_swapper_sv GPI_IMPL=vpi endif -ifeq ($(GPI_IMPL),vhpi) +ifeq ($(TOPLEVEL_LANG),vhdl) VHDL_SOURCES = $(WPWD)/../hdl/endian_swapper.vhdl TOPLEVEL = endian_swapper_vhdl GPI_IMPL=vhpi diff --git a/include/gpi.h b/include/gpi.h index cb420bd6..5988c27e 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -153,6 +153,7 @@ gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator); // We only care about a limited subset of values. const char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl); double gpi_get_signal_value_real(gpi_sim_hdl gpi_hdl); +long gpi_get_signal_value_long(gpi_sim_hdl gpi_hdl); const char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl); const char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl); @@ -162,7 +163,7 @@ gpi_objtype_t gpi_get_object_type(gpi_sim_hdl gpi_hdl); // Functions for setting the properties of a handle void gpi_set_signal_value_real(gpi_sim_hdl gpi_hdl, double value); -void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int value); +void gpi_set_signal_value_long(gpi_sim_hdl gpi_hdl, long value); void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str); // String of binary char(s) [1, 0, x, z] typedef enum gpi_edge { diff --git a/include/vpi_user.h b/include/vpi_user.h index 8f22f22e..6038729d 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -55,6 +55,7 @@ extern "C" { typedef uint32_t *vpiHandle; +#define vpiIntegerVar 25 /* Integer */ #define vpiModule 32 /* module instance */ #define vpiNet 36 /* scalar or vector net */ #define vpiNetBit 37 /* bit of a vector net */ @@ -62,7 +63,7 @@ typedef uint32_t *vpiHandle; #define vpiReg 48 /* scalar or vector reg */ #define vpiRegBit 49 /* bit of vector reg */ #define vpiParameter 41 /* module parameter */ -#define vpiNetArray 114 +#define vpiNetArray 114 #define vpiRegArray 116 /* multidimensional reg */ #define vpiInterface 601 #define vpiInterfaceArray 603 @@ -70,6 +71,7 @@ typedef uint32_t *vpiHandle; #define vpiRefObj 608 #define vpiPackedArrayVar 623 #define vpiEnumNet 680 /* SystemVerilog */ +#define vpiIntegerNet 681 #define vpiIntVar 612 #define vpiEnumVar 617 #define vpiStructVar 618 diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index e2c48039..1adc0d7f 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -436,12 +436,12 @@ const char* FliSignalObjHdl::get_signal_value_binstr(void) return m_val_buff; } -int FliSignalObjHdl::set_signal_value(const int value) +int FliSignalObjHdl::set_signal_value(const long value) { int rc; char buff[20]; - snprintf(buff, 20, "16#%016X", value); + snprintf(buff, 20, "16#%016X", (int)value); rc = mti_ForceSignal(m_fli_hdl, &buff[0], 0, MTI_FORCE_DEPOSIT, -1, -1); @@ -553,7 +553,7 @@ const char* FliVariableObjHdl::get_signal_value_binstr(void) return m_val_buff; } -int FliVariableObjHdl::set_signal_value(const int value) +int FliVariableObjHdl::set_signal_value(const long value) { LOG_CRITICAL("Setting variable value not currently supported!\n"); return -1; diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index 3ab2fe11..c575fa12 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -91,8 +91,9 @@ class FliSignalObjHdl : public GpiSignalObjHdl { const char* get_signal_value_binstr(void); double get_signal_value_real(void) { return 0.0; } + long get_signal_value_long(void) { return 0; } - int set_signal_value(const int value); + int set_signal_value(const long value); int set_signal_value(const double value) { return 0; } int set_signal_value(std::string &value); int initialise(std::string &name); @@ -128,8 +129,9 @@ class FliVariableObjHdl : public GpiSignalObjHdl { const char* get_signal_value_binstr(void); double get_signal_value_real(void) { return 0.0; } + long get_signal_value_long(void) { return 0; } - int set_signal_value(const int value); + int set_signal_value(const long value); int set_signal_value(std::string &value); int set_signal_value(const double value) { return 0; } int initialise(std::string &name); diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index a1a57efa..a1b6f540 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -270,6 +270,12 @@ double gpi_get_signal_value_real(gpi_sim_hdl sig_hdl) return obj_hdl->get_signal_value_real(); } +long gpi_get_signal_value_long(gpi_sim_hdl sig_hdl) +{ + GpiSignalObjHdl *obj_hdl = sim_to_hdl(sig_hdl); + return obj_hdl->get_signal_value_long(); +} + const char *gpi_get_signal_name_str(gpi_sim_hdl sig_hdl) { GpiSignalObjHdl *obj_hdl = sim_to_hdl(sig_hdl); @@ -288,7 +294,7 @@ gpi_objtype_t gpi_get_object_type(gpi_sim_hdl sig_hdl) return obj_hdl->get_type(); } -void gpi_set_signal_value_int(gpi_sim_hdl sig_hdl, int value) +void gpi_set_signal_value_long(gpi_sim_hdl sig_hdl, long value) { GpiSignalObjHdl *obj_hdl = sim_to_hdl(sig_hdl); obj_hdl->set_signal_value(value); diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 5e577bd3..7489c0a6 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -134,10 +134,11 @@ class GpiSignalObjHdl : public GpiObjHdl { // Provide public access to the implementation (composition vs inheritance) virtual const char* get_signal_value_binstr(void) = 0; virtual double get_signal_value_real(void) = 0; + virtual long get_signal_value_long(void) = 0; int m_length; - virtual int set_signal_value(const int value) = 0; + virtual int set_signal_value(const long value) = 0; virtual int set_signal_value(const double value) = 0; virtual int set_signal_value(std::string &value) = 0; //virtual GpiCbHdl monitor_value(bool rising_edge) = 0; this was for the triggers diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 2a8c1883..f5ae128b 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -526,7 +526,7 @@ static PyObject *next(PyObject *self, PyObject *args) } -static PyObject *get_signal_val(PyObject *self, PyObject *args) +static PyObject *get_signal_val_str(PyObject *self, PyObject *args) { gpi_sim_hdl hdl; const char *result; @@ -571,26 +571,26 @@ static PyObject *get_signal_val_real(PyObject *self, PyObject *args) } -static PyObject *set_signal_val(PyObject *self, PyObject *args) +static PyObject *get_signal_val_long(PyObject *self, PyObject *args) { gpi_sim_hdl hdl; - long value; - PyObject *res; + long result; + PyObject *retval; PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "ll", &hdl, &value)) { + if (!PyArg_ParseTuple(args, "l", &hdl)) { DROP_GIL(gstate); return NULL; } - gpi_set_signal_value_int(hdl,value); - res = Py_BuildValue("s", "OK!"); + result = gpi_get_signal_value_long(hdl); + retval = Py_BuildValue("l", result); DROP_GIL(gstate); - return res; + return retval; } @@ -638,6 +638,28 @@ static PyObject *set_signal_val_real(PyObject *self, PyObject *args) return res; } +static PyObject *set_signal_val_long(PyObject *self, PyObject *args) +{ + gpi_sim_hdl hdl; + long value; + PyObject *res; + + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + if (!PyArg_ParseTuple(args, "ll", &hdl, &value)) { + DROP_GIL(gstate); + return NULL; + } + + gpi_set_signal_value_long(hdl, value); + res = Py_BuildValue("s", "OK!"); + + DROP_GIL(gstate); + + return res; +} + static PyObject *get_handle_by_name(PyObject *self, PyObject *args) { const char *name; diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index fc520dd1..37af9b50 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -57,11 +57,12 @@ static PyObject *log_msg(PyObject *self, PyObject *args); // Raise an exception on failure // Return None if for example get bin_string on enum? -static PyObject *get_signal_val(PyObject *self, PyObject *args); +static PyObject *get_signal_val_long(PyObject *self, PyObject *args); static PyObject *get_signal_val_real(PyObject *self, PyObject *args); -static PyObject *set_signal_val(PyObject *self, PyObject *args); -static PyObject *set_signal_val_str(PyObject *self, PyObject *args); +static PyObject *get_signal_val_str(PyObject *self, PyObject *args); +static PyObject *set_signal_val_long(PyObject *self, PyObject *args); static PyObject *set_signal_val_real(PyObject *self, PyObject *args); +static PyObject *set_signal_val_str(PyObject *self, PyObject *args); static PyObject *get_handle_by_name(PyObject *self, PyObject *args); static PyObject *get_handle_by_index(PyObject *self, PyObject *args); static PyObject *get_root_handle(PyObject *self, PyObject *args); @@ -83,12 +84,13 @@ static PyObject *deregister_callback(PyObject *self, PyObject *args); static PyMethodDef SimulatorMethods[] = { {"log_msg", log_msg, METH_VARARGS, "Log a message"}, - {"get_signal_val", get_signal_val, METH_VARARGS, "Get the value of a signal as a binary string"}, - {"get_signal_val_real", get_signal_val_real, METH_VARARGS, "Get the value of a signal as a double precision float"}, - {"set_signal_val", set_signal_val, METH_VARARGS, "Set the value of a signal"}, - {"set_signal_val_str", set_signal_val_str, METH_VARARGS, "Set the value of a signal using a binary string"}, - {"set_signal_val_real", set_signal_val_real, METH_VARARGS, "Set the value of a signal using a double"}, - {"get_handle_by_name", get_handle_by_name, METH_VARARGS, "Get handle of a named object"}, + {"get_signal_val_long", get_signal_val_long, METH_VARARGS, "Get the value of a signal as a long"}, + {"get_signal_val_str", get_signal_val_str, METH_VARARGS, "Get the value of a signal as a binary string"}, + {"get_signal_val_real", get_signal_val_real, METH_VARARGS, "Get the value of a signal as a double precision float"}, + {"set_signal_val_long", set_signal_val_long, METH_VARARGS, "Set the value of a signal using a long"}, + {"set_signal_val_str", set_signal_val_str, METH_VARARGS, "Set the value of a signal using a binary string"}, + {"set_signal_val_real", set_signal_val_real, METH_VARARGS, "Set the value of a signal using a double precision float"}, + {"get_handle_by_name", get_handle_by_name, METH_VARARGS, "Get handle of a named object"}, {"get_handle_by_index", get_handle_by_index, METH_VARARGS, "Get handle of a object at an index in a parent"}, {"get_root_handle", get_root_handle, METH_VARARGS, "Get the root handle"}, {"get_name_string", get_name_string, METH_VARARGS, "Get the name of an object as a string"}, diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index b4b0a1ff..79cee0e9 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -36,7 +36,8 @@ VhpiSignalObjHdl::~VhpiSignalObjHdl() free(m_value.value.enumvs); } - free(m_binvalue.value.str); + if (m_binvalue.value.str) + free(m_binvalue.value.str); } int VhpiSignalObjHdl::initialise(std::string &name) { @@ -44,6 +45,11 @@ int VhpiSignalObjHdl::initialise(std::string &name) { m_value.format = vhpiObjTypeVal; m_value.bufSize = 0; m_value.value.str = NULL; + /* We also alloc a second value member for use with read string operations */ + m_binvalue.format = vhpiBinStrVal; + m_binvalue.bufSize = 0; + m_binvalue.numElems = 0; + m_binvalue.value.str = NULL; vhpi_get_value(GpiObjHdl::get_handle(), &m_value); check_vhpi_error(); @@ -59,6 +65,8 @@ int VhpiSignalObjHdl::initialise(std::string &name) { GpiObjHdl::initialise(name); return 0; } + + case vhpiIntVal: case vhpiEnumVecVal: case vhpiLogicVecVal: { m_size = vhpi_get(vhpiSizeP, GpiObjHdl::get_handle()); @@ -70,8 +78,7 @@ int VhpiSignalObjHdl::initialise(std::string &name) { break; } - case vhpiRawDataVal: - case vhpiIntVal: { + case vhpiRawDataVal: { GpiObjHdl::initialise(name); return 0; } @@ -82,12 +89,6 @@ int VhpiSignalObjHdl::initialise(std::string &name) { } } - /* We also alloc a second value member for use with read string operations */ - m_binvalue.format = vhpiBinStrVal; - m_binvalue.bufSize = 0; - m_binvalue.numElems = 0; - m_binvalue.value.str = NULL; - int new_size = vhpi_get_value(GpiObjHdl::get_handle(), &m_binvalue); if (new_size < 0) { LOG_CRITICAL("Failed to determine size of signal object %s", name.c_str()); @@ -209,7 +210,7 @@ const vhpiEnumT VhpiSignalObjHdl::chr2vhpi(const char value) } // Value related functions -int VhpiSignalObjHdl::set_signal_value(int value) +int VhpiSignalObjHdl::set_signal_value(long value) { switch (m_value.format) { case vhpiEnumVal: @@ -227,6 +228,11 @@ int VhpiSignalObjHdl::set_signal_value(int value) break; } + case vhpiIntVal: { + m_value.value.intg = value; + break; + } + case vhpiRealVal: LOG_WARN("Attempt to set vhpiRealVal signal with integer"); return 0; @@ -336,7 +342,28 @@ const char* VhpiSignalObjHdl::get_signal_value_binstr(void) double VhpiSignalObjHdl::get_signal_value_real(void) { - return vhpi_get_real(vhpiRealValP, GpiObjHdl::get_handle()); + m_value.format = vhpiRealVal; + m_value.numElems = 1; + m_value.bufSize = sizeof(double); + + int ret = vhpi_get_value(GpiObjHdl::get_handle(), &m_value); + if (ret) { + check_vhpi_error(); + LOG_ERROR("failed to get real value"); + } + return m_value.value.real; +} + +long VhpiSignalObjHdl::get_signal_value_long(void) +{ + vhpiValueT value; + value.format = vhpiIntVal; + value.numElems = 0; + + if (vhpi_get_value(GpiObjHdl::get_handle(), &value)) + check_vhpi_error(); + + return value.value.intg; } diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index e196b0ec..83fd366f 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -163,30 +163,35 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string gpi_type = to_gpi_objtype(type); - // Sadly VHPI doesn't have a "Real" type returned - we just get - // vhpiPortDeclK rather than the signal type. - // - // We workaround this by querying the format value and overriding the - // result of to_gpi_objtype - vhpiValueT value; - value.format = vhpiObjTypeVal; - value.bufSize = 0; - value.numElems = 0; - value.value.str = NULL; - - vhpi_get_value(new_hdl, &value); - if (vhpiRealVal == value.format) { - LOG_DEBUG("Detected a REAL type", name.c_str()); - gpi_type = GPI_REAL; - } - /* What sort of isntance is this ?*/ switch (type) { case vhpiPortDeclK: case vhpiSigDeclK: - case vhpiIndexedNameK: + case vhpiIndexedNameK: { + // Sadly VHPI doesn't have a "Real" type returned - we just get + // vhpiPortDeclK rather than the signal type. + // + // We workaround this by querying the format value and overriding the + // result of to_gpi_objtype + vhpiValueT value; + value.format = vhpiObjTypeVal; + value.bufSize = 0; + value.numElems = 0; + value.value.str = NULL; + + vhpi_get_value(new_hdl, &value); + if (vhpiRealVal == value.format) { + LOG_DEBUG("Detected a REAL type", name.c_str()); + gpi_type = GPI_REAL; + } + + if (vhpiIntVal == value.format) { + LOG_DEBUG("Detected an INT type", name.c_str()); + gpi_type = GPI_ENUM; + } new_obj = new VhpiSignalObjHdl(this, new_hdl, gpi_type); break; + } case vhpiForGenerateK: case vhpiIfGenerateK: case vhpiCompInstStmtK: diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index f6a4c5f8..2ca975ee 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -160,9 +160,10 @@ class VhpiSignalObjHdl : public GpiSignalObjHdl { const char* get_signal_value_binstr(void); double get_signal_value_real(void); + long get_signal_value_long(void); - int set_signal_value(const int value); + int set_signal_value(const long value); int set_signal_value(const double value); int set_signal_value(std::string &value); diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 6f108245..6f879ca0 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -127,29 +127,37 @@ const char* VpiSignalObjHdl::get_signal_value_binstr(void) { FENTER s_vpi_value value_s = {vpiBinStrVal}; - p_vpi_value value_p = &value_s; - vpi_get_value(GpiObjHdl::get_handle(), value_p); + vpi_get_value(GpiObjHdl::get_handle(), &value_s); check_vpi_error(); - return value_p->value.str; + return value_s.value.str; } double VpiSignalObjHdl::get_signal_value_real(void) { FENTER s_vpi_value value_s = {vpiRealVal}; - p_vpi_value value_p = &value_s; - vpi_get_value(GpiObjHdl::get_handle(), value_p); + vpi_get_value(GpiObjHdl::get_handle(), &value_s); check_vpi_error(); - return value_p->value.real; + return value_s.value.real; } +long VpiSignalObjHdl::get_signal_value_long(void) +{ + FENTER + s_vpi_value value_s = {vpiIntVal}; + + vpi_get_value(GpiObjHdl::get_handle(), &value_s); + check_vpi_error(); + + return value_s.value.integer; +} // Value related functions -int VpiSignalObjHdl::set_signal_value(int value) +int VpiSignalObjHdl::set_signal_value(long value) { FENTER s_vpi_value value_s; diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index ec99c1bc..3ac043e0 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -90,6 +90,8 @@ gpi_objtype_t to_gpi_objtype(int32_t vpitype) case vpiEnumNet: case vpiEnumVar: case vpiIntVar: + case vpiIntegerVar: + case vpiIntegerNet: return GPI_ENUM; case vpiParameter: @@ -132,6 +134,8 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &n case vpiEnumNet: case vpiEnumVar: case vpiIntVar: + case vpiIntegerVar: + case vpiIntegerNet: case vpiRealVar: new_obj = new VpiSignalObjHdl(this, new_hdl, to_gpi_objtype(type)); break; diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index cf7bfefc..8f3a25d7 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -183,8 +183,9 @@ class VpiSignalObjHdl : public GpiSignalObjHdl { const char* get_signal_value_binstr(void); double get_signal_value_real(void); + long get_signal_value_long(void); - int set_signal_value(const int value); + int set_signal_value(const long value); int set_signal_value(const double value); int set_signal_value(std::string &value); diff --git a/tests/designs/sample_module/sample_module.v b/tests/designs/sample_module/sample_module.v index a34ced0c..2ca2ed64 100644 --- a/tests/designs/sample_module/sample_module.v +++ b/tests/designs/sample_module/sample_module.v @@ -39,21 +39,29 @@ module sample_module ( `endif input [7:0] stream_in_data, input [63:0] stream_in_data_wide, + input integer stream_in_int, input stream_out_ready, `ifndef __ICARUS__ output real stream_out_real, `endif output reg [7:0] stream_out_data_comb, - output reg [7:0] stream_out_data_registered + output reg [7:0] stream_out_data_registered, + output integer stream_out_int ); always @(posedge clk) stream_out_data_registered <= stream_in_data; +always @(stream_in_int) + stream_out_int <= stream_in_int; + always @(stream_in_data) stream_out_data_comb = stream_in_data; +always @(stream_in_data) + stream_out_int = stream_in_int; + always @(stream_out_ready) stream_in_ready = stream_out_ready; diff --git a/tests/designs/sample_module/sample_module.vhdl b/tests/designs/sample_module/sample_module.vhdl index 519147fd..5e48e4f4 100644 --- a/tests/designs/sample_module/sample_module.vhdl +++ b/tests/designs/sample_module/sample_module.vhdl @@ -40,11 +40,13 @@ entity sample_module is stream_in_valid : in std_ulogic; stream_in_ready : out std_ulogic; stream_in_real : in real; + stream_in_int : in integer; stream_out_data_comb : out std_ulogic_vector(7 downto 0); stream_out_data_registered : out std_ulogic_vector(7 downto 0); stream_out_ready : in std_ulogic; - stream_out_real : out real + stream_out_real : out real; + stream_out_int : out integer ); end; @@ -61,5 +63,6 @@ end process; stream_out_data_comb <= stream_in_data; stream_in_ready <= stream_out_ready; stream_out_real <= stream_in_real; +stream_out_int <= stream_in_int; end architecture; diff --git a/tests/test_cases/issue_134/Makefile b/tests/test_cases/issue_134/Makefile index a495781e..95b104e8 100644 --- a/tests/test_cases/issue_134/Makefile +++ b/tests/test_cases/issue_134/Makefile @@ -27,4 +27,4 @@ include ../../designs/sample_module/Makefile -MODULE ?= test_reals +MODULE ?= test_reals,test_integers diff --git a/tests/test_cases/issue_134/test_integers.py b/tests/test_cases/issue_134/test_integers.py new file mode 100644 index 00000000..b63610ae --- /dev/null +++ b/tests/test_cases/issue_134/test_integers.py @@ -0,0 +1,27 @@ +import logging +import random +import sys + +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import RisingEdge, Timer, ReadOnly +from cocotb.result import TestFailure +from cocotb.binary import BinaryValue + + +@cocotb.test() +def test_integer(dut): + """ + Test access to integers + """ + log = logging.getLogger("cocotb.test") + yield Timer(10) + dut.stream_in_int = 4 + yield Timer(10) + yield Timer(10) + got_in = int(dut.stream_out_int) + got_out = int(dut.stream_in_int) + log.info("dut.stream_out_int = %d" % got_out) + log.info("dut.stream_in_int = %d" % got_in) + if got_in != got_out: + raise TestFailure("stream_in_int and stream_out_int should not match") From 9a9a9ea7f67cad5d8268b4ef0b42f7c63b5ec51b Mon Sep 17 00:00:00 2001 From: Chiggs Date: Wed, 19 Aug 2015 21:54:23 +0100 Subject: [PATCH 0923/2656] Issue #17: Add recursive iteration test --- cocotb/handle.py | 6 ++++++ .../test_iteration/test_iteration.py | 20 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/cocotb/handle.py b/cocotb/handle.py index 8f1d50ad..65588b8a 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -292,6 +292,12 @@ def __getitem__(self, index): self._sub_handles[index] = SimHandle(new_handle) return self._sub_handles[index] + def __iter__(self): + if len(self) == 1: + raise StopIteration + for i in range(len(self)): + yield self[i] + def _getvalue(self): result = BinaryValue() result.binstr = self._get_value_str() diff --git a/tests/test_cases/test_iteration/test_iteration.py b/tests/test_cases/test_iteration/test_iteration.py index 96c000f2..ca0c66fa 100644 --- a/tests/test_cases/test_iteration/test_iteration.py +++ b/tests/test_cases/test_iteration/test_iteration.py @@ -23,10 +23,30 @@ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' +import logging + import cocotb from cocotb.triggers import Timer from cocotb.result import TestError, TestFailure +@cocotb.test() +def recursive_discovery(dut): + """ + Recursively discover every single object in the design + """ + tlog = logging.getLogger("cocotb.test") + yield Timer(100) + def dump_all_the_things(parent): + count = 0 + for thing in parent: + count += 1 + tlog.info("Found %s.%s (%s)", parent._name, thing._name, type(thing)) + count += dump_all_the_things(thing) + return count + total = dump_all_the_things(dut) + tlog.info("Found a total of %d things", count) + + @cocotb.test() def discovery_all(dut): From afc01f4644b6ab35b756cc3850335139dbd05c21 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Wed, 19 Aug 2015 22:04:12 +0100 Subject: [PATCH 0924/2656] Issue #17: Fix error that prevented comparison of handles to integer objects --- cocotb/handle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 65588b8a..6a0a0c0e 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -324,7 +324,7 @@ def __len__(self): def __cmp__(self, other): # Permits comparison of handles i.e. if clk == dut.clk - if isinstance(other, SimHandle): + if isinstance(other, SimHandleBase): if self._handle == other._handle: return 0 return 1 From 932084cb12e1fd4ca6345cec5c71cd7a740da93e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 20 Aug 2015 08:27:29 +0100 Subject: [PATCH 0925/2656] Issue #17: Fix type in test --- tests/test_cases/test_iteration/test_iteration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/test_iteration/test_iteration.py b/tests/test_cases/test_iteration/test_iteration.py index ca0c66fa..06768eab 100644 --- a/tests/test_cases/test_iteration/test_iteration.py +++ b/tests/test_cases/test_iteration/test_iteration.py @@ -44,7 +44,7 @@ def dump_all_the_things(parent): count += dump_all_the_things(thing) return count total = dump_all_the_things(dut) - tlog.info("Found a total of %d things", count) + tlog.info("Found a total of %d things", total) From 48dfb871e8e72079dbc3dbb10a61b9875169fc2e Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 21 Aug 2015 09:33:42 +0100 Subject: [PATCH 0926/2656] Issue #17: Add a more complex mixed language testcase --- tests/designs/uart2bus/Makefile | 28 + tests/designs/uart2bus/README | 7 + tests/designs/uart2bus/top/verilog_toplevel.v | 58 ++ tests/designs/uart2bus/top/vhdl_toplevel.vhdl | 0 tests/designs/uart2bus/verilog/baud_gen.v | 56 ++ tests/designs/uart2bus/verilog/uart2bus_top.v | 83 +++ tests/designs/uart2bus/verilog/uart_parser.v | 667 ++++++++++++++++++ tests/designs/uart2bus/verilog/uart_rx.v | 116 +++ tests/designs/uart2bus/verilog/uart_top.v | 66 ++ tests/designs/uart2bus/verilog/uart_tx.v | 94 +++ tests/designs/uart2bus/vhdl/baudGen.vhd | 48 ++ tests/designs/uart2bus/vhdl/uart2BusTop.vhd | 77 ++ .../designs/uart2bus/vhdl/uart2BusTop_pkg.vhd | 90 +++ tests/designs/uart2bus/vhdl/uartParser.vhd | 576 +++++++++++++++ tests/designs/uart2bus/vhdl/uartRx.vhd | 111 +++ tests/designs/uart2bus/vhdl/uartTop.vhd | 63 ++ tests/designs/uart2bus/vhdl/uartTx.vhd | 96 +++ 17 files changed, 2236 insertions(+) create mode 100644 tests/designs/uart2bus/Makefile create mode 100644 tests/designs/uart2bus/README create mode 100644 tests/designs/uart2bus/top/verilog_toplevel.v create mode 100644 tests/designs/uart2bus/top/vhdl_toplevel.vhdl create mode 100644 tests/designs/uart2bus/verilog/baud_gen.v create mode 100644 tests/designs/uart2bus/verilog/uart2bus_top.v create mode 100644 tests/designs/uart2bus/verilog/uart_parser.v create mode 100644 tests/designs/uart2bus/verilog/uart_rx.v create mode 100644 tests/designs/uart2bus/verilog/uart_top.v create mode 100644 tests/designs/uart2bus/verilog/uart_tx.v create mode 100644 tests/designs/uart2bus/vhdl/baudGen.vhd create mode 100644 tests/designs/uart2bus/vhdl/uart2BusTop.vhd create mode 100644 tests/designs/uart2bus/vhdl/uart2BusTop_pkg.vhd create mode 100644 tests/designs/uart2bus/vhdl/uartParser.vhd create mode 100644 tests/designs/uart2bus/vhdl/uartRx.vhd create mode 100644 tests/designs/uart2bus/vhdl/uartTop.vhd create mode 100644 tests/designs/uart2bus/vhdl/uartTx.vhd diff --git a/tests/designs/uart2bus/Makefile b/tests/designs/uart2bus/Makefile new file mode 100644 index 00000000..b9acbda7 --- /dev/null +++ b/tests/designs/uart2bus/Makefile @@ -0,0 +1,28 @@ +SRC_BASE = $(COCOTB)/tests/designs/uart2bus + +TOPLEVEL_LANG ?= verilog + +VHDL_SOURCES = $(SRC_BASE)/vhdl/uart2BusTop_pkg.vhd \ + $(SRC_BASE)/vhdl/baudGen.vhd \ + $(SRC_BASE)/vhdl/uartParser.vhd \ + $(SRC_BASE)/vhdl/uartRx.vhd \ + $(SRC_BASE)/vhdl/uartTx.vhd \ + $(SRC_BASE)/vhdl/uartTop.vhd \ + $(SRC_BASE)/vhdl/uart2BusTop.vhd + +VERILOG_SOURCES = $(SRC_BASE)/verilog/baud_gen.v \ + $(SRC_BASE)/verilog/uart_parser.v \ + $(SRC_BASE)/verilog/uart_rx.v \ + $(SRC_BASE)/verilog/uart_tx.v \ + $(SRC_BASE)/verilog/uart_top.v \ + $(SRC_BASE)/verilog/uart2bus_top.v + +ifeq ($(TOPLEVEL_LANG),verilog) + VERILOG_SOURCES += $(SRC_BASE)/top/verilog_toplevel.v + TOPLEVEL = verilog_toplevel +else +$(error "Only verilog toplevel supported at the moment") +endif + +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim diff --git a/tests/designs/uart2bus/README b/tests/designs/uart2bus/README new file mode 100644 index 00000000..6e813ea7 --- /dev/null +++ b/tests/designs/uart2bus/README @@ -0,0 +1,7 @@ +UART to bus design from [OpenCores](http://opencores.org/project,uart2bus) + +BSD Licensed. + +Contains a VHDL and Verilog implementation, suitable for testing mixed +language simulations. + diff --git a/tests/designs/uart2bus/top/verilog_toplevel.v b/tests/designs/uart2bus/top/verilog_toplevel.v new file mode 100644 index 00000000..e10a0860 --- /dev/null +++ b/tests/designs/uart2bus/top/verilog_toplevel.v @@ -0,0 +1,58 @@ +// We simply connect two UARTs together in different languages +// +// Also define a few different structs etc. to help the testcase + +module verilog_toplevel ( + input clk, + input reset +); + + +// Verilog design +logic serial_v2h, serial_h2v; +logic [7:0] verilog_rd_data, vhdl_rd_data; + +typedef struct packed { + logic [15:0] address; + logic [7:0] wr_data; + logic read; + logic write; +} bus_struct_t; + +bus_struct_t bus_verilog, bus_vhdl; + +uart2bus_top i_verilog ( + .clock (clk), + .reset (reset), + + .ser_in (serial_h2v), + .ser_out (serial_v2h), + + .int_address (bus_verilog.address), + .int_wr_data (bus_verilog.wr_data), + .int_write (bus_verilog.write), + .int_read (bus_verilog.read), + .int_rd_data (verilog_rd_data), + + .int_req (), + .int_gnt () +); + +uart2BusTop i_vhdl ( + .clk (clk), + .clr (reset), + + .serIn (serial_v2h), + .serOut (serial_h2v), + + .intAddress (bus_vhdl.address), + .intWrData (bus_vhdl.wr_data), + .intWrite (bus_vhdl.write), + .intRead (bus_vhdl.read), + .intRdData (vhdl_rd_data), + + .intAccessReq (), + .intAccessGnt () +); + +endmodule diff --git a/tests/designs/uart2bus/top/vhdl_toplevel.vhdl b/tests/designs/uart2bus/top/vhdl_toplevel.vhdl new file mode 100644 index 00000000..e69de29b diff --git a/tests/designs/uart2bus/verilog/baud_gen.v b/tests/designs/uart2bus/verilog/baud_gen.v new file mode 100644 index 00000000..aac8ce3e --- /dev/null +++ b/tests/designs/uart2bus/verilog/baud_gen.v @@ -0,0 +1,56 @@ +//--------------------------------------------------------------------------------------- +// baud rate generator for uart +// +// this module has been changed to receive the baud rate dividing counter from registers. +// the two registers should be calculated as follows: +// first register: +// baud_freq = 16*baud_rate / gcd(global_clock_freq, 16*baud_rate) +// second register: +// baud_limit = (global_clock_freq / gcd(global_clock_freq, 16*baud_rate)) - baud_freq +// +//--------------------------------------------------------------------------------------- + +module baud_gen +( + clock, reset, + ce_16, baud_freq, baud_limit +); +//--------------------------------------------------------------------------------------- +// modules inputs and outputs +input clock; // global clock input +input reset; // global reset input +output ce_16; // baud rate multiplyed by 16 +input [11:0] baud_freq; // baud rate setting registers - see header description +input [15:0] baud_limit; + +// internal registers +reg ce_16; +reg [15:0] counter; +//--------------------------------------------------------------------------------------- +// module implementation +// baud divider counter +always @ (posedge clock or posedge reset) +begin + if (reset) + counter <= 16'b0; + else if (counter >= baud_limit) + counter <= counter - baud_limit; + else + counter <= counter + baud_freq; +end + +// clock divider output +always @ (posedge clock or posedge reset) +begin + if (reset) + ce_16 <= 1'b0; + else if (counter >= baud_limit) + ce_16 <= 1'b1; + else + ce_16 <= 1'b0; +end + +endmodule +//--------------------------------------------------------------------------------------- +// Th.. Th.. Th.. Thats all folks !!! +//--------------------------------------------------------------------------------------- diff --git a/tests/designs/uart2bus/verilog/uart2bus_top.v b/tests/designs/uart2bus/verilog/uart2bus_top.v new file mode 100644 index 00000000..a883769e --- /dev/null +++ b/tests/designs/uart2bus/verilog/uart2bus_top.v @@ -0,0 +1,83 @@ +//--------------------------------------------------------------------------------------- +// uart to internal bus top module +// +//--------------------------------------------------------------------------------------- + +module uart2bus_top +( + // global signals + clock, reset, + // uart serial signals + ser_in, ser_out, + // internal bus to register file + int_address, int_wr_data, int_write, + int_rd_data, int_read, + int_req, int_gnt +); +//--------------------------------------------------------------------------------------- +// modules inputs and outputs +input clock; // global clock input +input reset; // global reset input +input ser_in; // serial data input +output ser_out; // serial data output +output [15:0] int_address; // address bus to register file +output [7:0] int_wr_data; // write data to register file +output int_write; // write control to register file +output int_read; // read control to register file +input [7:0] int_rd_data; // data read from register file +output int_req; // bus access request signal +input int_gnt; // bus access grant signal + +// baud rate configuration, see baud_gen.v for more details. +// baud rate generator parameters for 115200 baud on 40MHz clock +`define D_BAUD_FREQ 12'h90 +`define D_BAUD_LIMIT 16'h0ba5 +// baud rate generator parameters for 115200 baud on 44MHz clock +// `define D_BAUD_FREQ 12'd23 +// `define D_BAUD_LIMIT 16'd527 +// baud rate generator parameters for 9600 baud on 66MHz clock +//`define D_BAUD_FREQ 12'h10 +//`define D_BAUD_LIMIT 16'h1ACB + +// internal wires +wire [7:0] tx_data; // data byte to transmit +wire new_tx_data; // asserted to indicate that there is a new data byte for transmission +wire tx_busy; // signs that transmitter is busy +wire [7:0] rx_data; // data byte received +wire new_rx_data; // signs that a new byte was received +wire [11:0] baud_freq; +wire [15:0] baud_limit; +wire baud_clk; + +//--------------------------------------------------------------------------------------- +// module implementation +// uart top module instance +uart_top uart1 +( + .clock(clock), .reset(reset), + .ser_in(ser_in), .ser_out(ser_out), + .rx_data(rx_data), .new_rx_data(new_rx_data), + .tx_data(tx_data), .new_tx_data(new_tx_data), .tx_busy(tx_busy), + .baud_freq(baud_freq), .baud_limit(baud_limit), + .baud_clk(baud_clk) +); + +// assign baud rate default values +assign baud_freq = `D_BAUD_FREQ; +assign baud_limit = `D_BAUD_LIMIT; + +// uart parser instance +uart_parser #(16) uart_parser1 +( + .clock(clock), .reset(reset), + .rx_data(rx_data), .new_rx_data(new_rx_data), + .tx_data(tx_data), .new_tx_data(new_tx_data), .tx_busy(tx_busy), + .int_address(int_address), .int_wr_data(int_wr_data), .int_write(int_write), + .int_rd_data(int_rd_data), .int_read(int_read), + .int_req(int_req), .int_gnt(int_gnt) +); + +endmodule +//--------------------------------------------------------------------------------------- +// Th.. Th.. Th.. Thats all folks !!! +//--------------------------------------------------------------------------------------- diff --git a/tests/designs/uart2bus/verilog/uart_parser.v b/tests/designs/uart2bus/verilog/uart_parser.v new file mode 100644 index 00000000..5d3461bc --- /dev/null +++ b/tests/designs/uart2bus/verilog/uart_parser.v @@ -0,0 +1,667 @@ +//--------------------------------------------------------------------------------------- +// uart parser module +// +//--------------------------------------------------------------------------------------- + +module uart_parser +( + // global signals + clock, reset, + // transmit and receive internal interface signals from uart interface + rx_data, new_rx_data, + tx_data, new_tx_data, tx_busy, + // internal bus to register file + int_address, int_wr_data, int_write, + int_rd_data, int_read, + int_req, int_gnt +); +//--------------------------------------------------------------------------------------- +// parameters +parameter AW = 8; // address bus width parameter + +// modules inputs and outputs +input clock; // global clock input +input reset; // global reset input +output [7:0] tx_data; // data byte to transmit +output new_tx_data; // asserted to indicate that there is a new data byte for + // transmission +input tx_busy; // signs that transmitter is busy +input [7:0] rx_data; // data byte received +input new_rx_data; // signs that a new byte was received +output [AW-1:0] int_address; // address bus to register file +output [7:0] int_wr_data; // write data to register file +output int_write; // write control to register file +output int_read; // read control to register file +input [7:0] int_rd_data; // data read from register file +output int_req; // bus access request signal +input int_gnt; // bus access grant signal + +// registered outputs +reg [7:0] tx_data; +reg new_tx_data; +reg [AW-1:0] int_address; +reg [7:0] int_wr_data; +reg write_req, read_req, int_write, int_read; + +// internal constants +// define characters used by the parser +`define CHAR_CR 8'h0d +`define CHAR_LF 8'h0a +`define CHAR_SPACE 8'h20 +`define CHAR_TAB 8'h09 +`define CHAR_COMMA 8'h2C +`define CHAR_R_UP 8'h52 +`define CHAR_r_LO 8'h72 +`define CHAR_W_UP 8'h57 +`define CHAR_w_LO 8'h77 +`define CHAR_0 8'h30 +`define CHAR_1 8'h31 +`define CHAR_2 8'h32 +`define CHAR_3 8'h33 +`define CHAR_4 8'h34 +`define CHAR_5 8'h35 +`define CHAR_6 8'h36 +`define CHAR_7 8'h37 +`define CHAR_8 8'h38 +`define CHAR_9 8'h39 +`define CHAR_A_UP 8'h41 +`define CHAR_B_UP 8'h42 +`define CHAR_C_UP 8'h43 +`define CHAR_D_UP 8'h44 +`define CHAR_E_UP 8'h45 +`define CHAR_F_UP 8'h46 +`define CHAR_a_LO 8'h61 +`define CHAR_b_LO 8'h62 +`define CHAR_c_LO 8'h63 +`define CHAR_d_LO 8'h64 +`define CHAR_e_LO 8'h65 +`define CHAR_f_LO 8'h66 + +// main (receive) state machine states +`define MAIN_IDLE 4'b0000 +`define MAIN_WHITE1 4'b0001 +`define MAIN_DATA 4'b0010 +`define MAIN_WHITE2 4'b0011 +`define MAIN_ADDR 4'b0100 +`define MAIN_EOL 4'b0101 +// binary mode extension states +`define MAIN_BIN_CMD 4'b1000 +`define MAIN_BIN_ADRH 4'b1001 +`define MAIN_BIN_ADRL 4'b1010 +`define MAIN_BIN_LEN 4'b1011 +`define MAIN_BIN_DATA 4'b1100 + +// transmit state machine +`define TX_IDLE 3'b000 +`define TX_HI_NIB 3'b001 +`define TX_LO_NIB 3'b100 +`define TX_CHAR_CR 3'b101 +`define TX_CHAR_LF 3'b110 + +// binary extension mode commands - the command is indicated by bits 5:4 of the command byte +`define BIN_CMD_NOP 2'b00 +`define BIN_CMD_READ 2'b01 +`define BIN_CMD_WRITE 2'b10 + +// internal wires and registers +reg [3:0] main_sm; // main state machine +reg read_op; // read operation flag +reg write_op; // write operation flag +reg data_in_hex_range; // indicates that the received data is in the range of hex number +reg [7:0] data_param; // operation data parameter +reg [15:0] addr_param; // operation address parameter +reg [3:0] data_nibble; // data nibble from received character +reg read_done; // internally generated read done flag +reg read_done_s; // sampled read done +reg [7:0] read_data_s; // sampled read data +reg [3:0] tx_nibble; // nibble value for transmission +reg [7:0] tx_char; // transmit byte from nibble to character conversion +reg [2:0] tx_sm; // transmit state machine +reg s_tx_busy; // sampled tx_busy for falling edge detection +reg bin_read_op; // binary mode read operation flag +reg bin_write_op; // binary mode write operation flag +reg addr_auto_inc; // address auto increment mode +reg send_stat_flag; // send status flag +reg [7:0] bin_byte_count; // binary mode byte counter +wire bin_last_byte; // last byte flag indicates that the current byte in the command is the last +wire tx_end_p; // transmission end pulse + +//--------------------------------------------------------------------------------------- +// module implementation +// main state machine +always @ (posedge clock or posedge reset) +begin + if (reset) + main_sm <= `MAIN_IDLE; + else if (new_rx_data) + begin + case (main_sm) + // wait for a read ('r') or write ('w') command + // binary extension - an all zeros byte enabled binary commands + `MAIN_IDLE: + // check received character + if (rx_data == 8'h0) + // an all zeros received byte enters binary mode + main_sm <= `MAIN_BIN_CMD; + else if ((rx_data == `CHAR_r_LO) | (rx_data == `CHAR_R_UP)) + // on read wait to receive only address field + main_sm <= `MAIN_WHITE2; + else if ((rx_data == `CHAR_w_LO) | (rx_data == `CHAR_W_UP)) + // on write wait to receive data and address + main_sm <= `MAIN_WHITE1; + else if ((rx_data == `CHAR_CR) | (rx_data == `CHAR_LF)) + // on new line sta in idle + main_sm <= `MAIN_IDLE; + else + // any other character wait to end of line (EOL) + main_sm <= `MAIN_EOL; + + // wait for white spaces till first data nibble + `MAIN_WHITE1: + // wait in this case until any white space character is received. in any + // valid character for data value switch to data state. a new line or carriage + // return should reset the state machine to idle. + // any other character transitions the state machine to wait for EOL. + if ((rx_data == `CHAR_SPACE) | (rx_data == `CHAR_TAB)) + main_sm <= `MAIN_WHITE1; + else if (data_in_hex_range) + main_sm <= `MAIN_DATA; + else if ((rx_data == `CHAR_CR) | (rx_data == `CHAR_LF)) + main_sm <= `MAIN_IDLE; + else + main_sm <= `MAIN_EOL; + + // receive data field + `MAIN_DATA: + // wait while data in hex range. white space transition to wait white 2 state. + // CR and LF resets the state machine. any other value cause state machine to + // wait til end of line. + if (data_in_hex_range) + main_sm <= `MAIN_DATA; + else if ((rx_data == `CHAR_SPACE) | (rx_data == `CHAR_TAB)) + main_sm <= `MAIN_WHITE2; + else if ((rx_data == `CHAR_CR) | (rx_data == `CHAR_LF)) + main_sm <= `MAIN_IDLE; + else + main_sm <= `MAIN_EOL; + + // wait for white spaces till first address nibble + `MAIN_WHITE2: + // similar to MAIN_WHITE1 + if ((rx_data == `CHAR_SPACE) | (rx_data == `CHAR_TAB)) + main_sm <= `MAIN_WHITE2; + else if (data_in_hex_range) + main_sm <= `MAIN_ADDR; + else if ((rx_data == `CHAR_CR) | (rx_data == `CHAR_LF)) + main_sm <= `MAIN_IDLE; + else + main_sm <= `MAIN_EOL; + + // receive address field + `MAIN_ADDR: + // similar to MAIN_DATA + if (data_in_hex_range) + main_sm <= `MAIN_ADDR; + else if ((rx_data == `CHAR_CR) | (rx_data == `CHAR_LF)) + main_sm <= `MAIN_IDLE; + else + main_sm <= `MAIN_EOL; + + // wait to EOL + `MAIN_EOL: + // wait for CR or LF to move back to idle + if ((rx_data == `CHAR_CR) | (rx_data == `CHAR_LF)) + main_sm <= `MAIN_IDLE; + + // binary extension + // wait for command - one byte + `MAIN_BIN_CMD: + // check if command is a NOP command + if (rx_data[5:4] == `BIN_CMD_NOP) + // if NOP command then switch back to idle state + main_sm <= `MAIN_IDLE; + else + // not a NOP command, continue receiving parameters + main_sm <= `MAIN_BIN_ADRH; + + // wait for address parameter - two bytes + // high address byte + `MAIN_BIN_ADRH: + // switch to next state + main_sm <= `MAIN_BIN_ADRL; + + // low address byte + `MAIN_BIN_ADRL: + // switch to next state + main_sm <= `MAIN_BIN_LEN; + + // wait for length parameter - one byte + `MAIN_BIN_LEN: + // check if write command else command reception ended + if (bin_write_op) + // wait for write data + main_sm <= `MAIN_BIN_DATA; + else + // command reception has ended + main_sm <= `MAIN_IDLE; + + // on write commands wait for data till end of buffer as specified by length parameter + `MAIN_BIN_DATA: + // if this is the last data byte then return to idle + if (bin_last_byte) + main_sm <= `MAIN_IDLE; + + // go to idle + default: + main_sm <= `MAIN_IDLE; + endcase + end +end + +// indicates that the received data is in the range of hex number +always @ (rx_data) +begin + if (((rx_data >= `CHAR_0 ) && (rx_data <= `CHAR_9 )) || + ((rx_data >= `CHAR_A_UP) && (rx_data <= `CHAR_F_UP)) || + ((rx_data >= `CHAR_a_LO) && (rx_data <= `CHAR_f_LO))) + data_in_hex_range <= 1'b1; + else + data_in_hex_range <= 1'b0; +end + +// read operation flag +always @ (posedge clock or posedge reset) +begin + if (reset) + read_op <= 1'b0; + else if ((main_sm == `MAIN_IDLE) && new_rx_data) + begin + // the read operation flag is set when a read command is received in idle state and cleared + // if any other character is received during that state. + if ((rx_data == `CHAR_r_LO) | (rx_data == `CHAR_R_UP)) + read_op <= 1'b1; + else + read_op <= 1'b0; + end +end + +// write operation flag +always @ (posedge clock or posedge reset) +begin + if (reset) + write_op <= 1'b0; + else if ((main_sm == `MAIN_IDLE) & new_rx_data) + begin + // the write operation flag is set when a write command is received in idle state and cleared + // if any other character is received during that state. + if ((rx_data == `CHAR_w_LO) | (rx_data == `CHAR_W_UP)) + write_op <= 1'b1; + else + write_op <= 1'b0; + end +end + +// binary mode read operation flag +always @ (posedge clock or posedge reset) +begin + if (reset) + bin_read_op <= 1'b0; + else if ((main_sm == `MAIN_BIN_CMD) && new_rx_data && (rx_data[5:4] == `BIN_CMD_READ)) + // read command is started on reception of a read command + bin_read_op <= 1'b1; + else if (bin_read_op && tx_end_p && bin_last_byte) + // read command ends on transmission of the last byte read + bin_read_op <= 1'b0; +end + +// binary mode write operation flag +always @ (posedge clock or posedge reset) +begin + if (reset) + bin_write_op <= 1'b0; + else if ((main_sm == `MAIN_BIN_CMD) && new_rx_data && (rx_data[5:4] == `BIN_CMD_WRITE)) + // write command is started on reception of a write command + bin_write_op <= 1'b1; + else if ((main_sm == `MAIN_BIN_DATA) && new_rx_data && bin_last_byte) + bin_write_op <= 1'b0; +end + +// send status flag - used only in binary extension mode +always @ (posedge clock or posedge reset) +begin + if (reset) + send_stat_flag <= 1'b0; + else if ((main_sm == `MAIN_BIN_CMD) && new_rx_data) + begin + // check if a status byte should be sent at the end of the command + if (rx_data[0] == 1'b1) + send_stat_flag <= 1'b1; + else + send_stat_flag <= 1'b0; + end +end + +// address auto increment - used only in binary extension mode +always @ (posedge clock or posedge reset) +begin + if (reset) + addr_auto_inc <= 1'b0; + else if ((main_sm == `MAIN_BIN_CMD) && new_rx_data) + begin + // check if address should be automatically incremented or not. + // Note that when rx_data[1] is set, address auto increment is disabled. + if (rx_data[1] == 1'b0) + addr_auto_inc <= 1'b1; + else + addr_auto_inc <= 1'b0; + end +end + +// operation data parameter +always @ (posedge clock or posedge reset) +begin + if (reset) + data_param <= 8'h0; + else if ((main_sm == `MAIN_WHITE1) & new_rx_data & data_in_hex_range) + data_param <= {4'h0, data_nibble}; + else if ((main_sm == `MAIN_DATA) & new_rx_data & data_in_hex_range) + data_param <= {data_param[3:0], data_nibble}; +end + +// operation address parameter +always @ (posedge clock or posedge reset) +begin + if (reset) + addr_param <= 0; + else if ((main_sm == `MAIN_WHITE2) & new_rx_data & data_in_hex_range) + addr_param <= {12'b0, data_nibble}; + else if ((main_sm == `MAIN_ADDR) & new_rx_data & data_in_hex_range) + addr_param <= {addr_param[11:0], data_nibble}; + // binary extension + else if (main_sm == `MAIN_BIN_ADRH) + addr_param[15:8] <= rx_data; + else if (main_sm == `MAIN_BIN_ADRL) + addr_param[7:0] <= rx_data; +end + +// binary mode command byte counter is loaded with the length parameter and counts down to zero. +// NOTE: a value of zero for the length parameter indicates a command of 256 bytes. +always @ (posedge clock or posedge reset) +begin + if (reset) + bin_byte_count <= 8'b0; + else if ((main_sm == `MAIN_BIN_LEN) && new_rx_data) + bin_byte_count <= rx_data; + else if ((bin_write_op && (main_sm == `MAIN_BIN_DATA) && new_rx_data) || + (bin_read_op && tx_end_p)) + // byte counter is updated on every new data received in write operations and for every + // byte transmitted for read operations. + bin_byte_count <= bin_byte_count - 1; +end +// last byte in command flag +assign bin_last_byte = (bin_byte_count == 8'h01) ? 1'b1 : 1'b0; + +// internal write control and data +always @ (posedge clock or posedge reset) +begin + if (reset) + begin + write_req <= 1'b0; + int_write <= 1'b0; + int_wr_data <= 0; + end + else if (write_op && (main_sm == `MAIN_ADDR) && new_rx_data && !data_in_hex_range) + begin + write_req <= 1'b1; + int_wr_data <= data_param; + end + // binary extension mode + else if (bin_write_op && (main_sm == `MAIN_BIN_DATA) && new_rx_data) + begin + write_req <= 1'b1; + int_wr_data <= rx_data; + end + else if (int_gnt && write_req) + begin + // set internal bus write and clear the write request flag + int_write <= 1'b1; + write_req <= 1'b0; + end + else + int_write <= 1'b0; +end + +// internal read control +always @ (posedge clock or posedge reset) +begin + if (reset) + begin + int_read <= 1'b0; + read_req <= 1'b0; + end + else if (read_op && (main_sm == `MAIN_ADDR) && new_rx_data && !data_in_hex_range) + read_req <= 1'b1; + // binary extension + else if (bin_read_op && (main_sm == `MAIN_BIN_LEN) && new_rx_data) + // the first read request is issued on reception of the length byte + read_req <= 1'b1; + else if (bin_read_op && tx_end_p && !bin_last_byte) + // the next read requests are issued after the previous read value was transmitted and + // this is not the last byte to be read. + read_req <= 1'b1; + else if (int_gnt && read_req) + begin + // set internal bus read and clear the read request flag + int_read <= 1'b1; + read_req <= 1'b0; + end + else + int_read <= 1'b0; +end + +// external request signal is active on read or write request +assign int_req = write_req | read_req; + +// internal address +always @ (posedge clock or posedge reset) +begin + if (reset) + int_address <= 0; + else if ((main_sm == `MAIN_ADDR) && new_rx_data && !data_in_hex_range) + int_address <= addr_param[AW-1:0]; + // binary extension + else if ((main_sm == `MAIN_BIN_LEN) && new_rx_data) + // sample address parameter on reception of length byte + int_address <= addr_param[AW-1:0]; + else if (addr_auto_inc && + ((bin_read_op && tx_end_p && !bin_last_byte) || + (bin_write_op && int_write))) + // address is incremented on every read or write if enabled + int_address <= int_address + 1; +end + +// read done flag and sampled data read +always @ (posedge clock or posedge reset) +begin + if (reset) begin + read_done <= 1'b0; + read_done_s <= 1'b0; + read_data_s <= 8'h0; + end + else + begin + // read done flag + if (int_read) + read_done <= 1'b1; + else + read_done <= 1'b0; + + // sampled read done + read_done_s <= read_done; + + // sampled data read + if (read_done) + read_data_s <= int_rd_data; + end +end + +// transmit state machine and control +always @ (posedge clock or posedge reset) +begin + if (reset) begin + tx_sm <= `TX_IDLE; + tx_data <= 8'h0; + new_tx_data <= 1'b0; + end + else + case (tx_sm) + // wait for read done indication + `TX_IDLE: + // on end of every read operation check how the data read should be transmitted + // according to read type: ascii or binary. + if (read_done_s) + // on binary mode read transmit byte value + if (bin_read_op) + begin + // note that there is no need to change state + tx_data <= read_data_s; + new_tx_data <= 1'b1; + end + else + begin + tx_sm <= `TX_HI_NIB; + tx_data <= tx_char; + new_tx_data <= 1'b1; + end + // check if status byte should be transmitted + else if ((send_stat_flag && bin_read_op && tx_end_p && bin_last_byte) || // end of read command + (send_stat_flag && bin_write_op && new_rx_data && bin_last_byte) || // end of write command + ((main_sm == `MAIN_BIN_CMD) && new_rx_data && (rx_data[5:4] == `BIN_CMD_NOP))) // NOP + begin + // send status byte - currently a constant + tx_data <= 8'h5a; + new_tx_data <= 1'b1; + end + else + new_tx_data <= 1'b0; + + // wait for transmit to end + `TX_HI_NIB: + if (tx_end_p) + begin + tx_sm <= `TX_LO_NIB; + tx_data <= tx_char; + new_tx_data <= 1'b1; + end + else + new_tx_data <= 1'b0; + + // wait for transmit to end + `TX_LO_NIB: + if (tx_end_p) + begin + tx_sm <= `TX_CHAR_CR; + tx_data <= `CHAR_CR; + new_tx_data <= 1'b1; + end + else + new_tx_data <= 1'b0; + + // wait for transmit to end + `TX_CHAR_CR: + if (tx_end_p) + begin + tx_sm <= `TX_CHAR_LF; + tx_data <= `CHAR_LF; + new_tx_data <= 1'b1; + end + else + new_tx_data <= 1'b0; + + // wait for transmit to end + `TX_CHAR_LF: + begin + if (tx_end_p) + tx_sm <= `TX_IDLE; + // clear tx new data flag + new_tx_data <= 1'b0; + end + + // return to idle + default: + tx_sm <= `TX_IDLE; + endcase +end + +// select the nibble to the nibble to character conversion +always @ (tx_sm or read_data_s) +begin + case (tx_sm) + `TX_IDLE: tx_nibble = read_data_s[7:4]; + `TX_HI_NIB: tx_nibble = read_data_s[3:0]; + default: tx_nibble = read_data_s[7:4]; + endcase +end + +// sampled tx_busy +always @ (posedge clock or posedge reset) +begin + if (reset) + s_tx_busy <= 1'b0; + else + s_tx_busy <= tx_busy; +end +// tx end pulse +assign tx_end_p = ~tx_busy & s_tx_busy; + +// character to nibble conversion +always @ (rx_data) +begin + case (rx_data) + `CHAR_0: data_nibble = 4'h0; + `CHAR_1: data_nibble = 4'h1; + `CHAR_2: data_nibble = 4'h2; + `CHAR_3: data_nibble = 4'h3; + `CHAR_4: data_nibble = 4'h4; + `CHAR_5: data_nibble = 4'h5; + `CHAR_6: data_nibble = 4'h6; + `CHAR_7: data_nibble = 4'h7; + `CHAR_8: data_nibble = 4'h8; + `CHAR_9: data_nibble = 4'h9; + `CHAR_A_UP, `CHAR_a_LO: data_nibble = 4'ha; + `CHAR_B_UP, `CHAR_b_LO: data_nibble = 4'hb; + `CHAR_C_UP, `CHAR_c_LO: data_nibble = 4'hc; + `CHAR_D_UP, `CHAR_d_LO: data_nibble = 4'hd; + `CHAR_E_UP, `CHAR_e_LO: data_nibble = 4'he; + `CHAR_F_UP, `CHAR_f_LO: data_nibble = 4'hf; + default: data_nibble = 4'hf; + endcase +end + +// nibble to character conversion +always @ (tx_nibble) +begin + case (tx_nibble) + 4'h0: tx_char = `CHAR_0; + 4'h1: tx_char = `CHAR_1; + 4'h2: tx_char = `CHAR_2; + 4'h3: tx_char = `CHAR_3; + 4'h4: tx_char = `CHAR_4; + 4'h5: tx_char = `CHAR_5; + 4'h6: tx_char = `CHAR_6; + 4'h7: tx_char = `CHAR_7; + 4'h8: tx_char = `CHAR_8; + 4'h9: tx_char = `CHAR_9; + 4'ha: tx_char = `CHAR_A_UP; + 4'hb: tx_char = `CHAR_B_UP; + 4'hc: tx_char = `CHAR_C_UP; + 4'hd: tx_char = `CHAR_D_UP; + 4'he: tx_char = `CHAR_E_UP; + default: tx_char = `CHAR_F_UP; + endcase +end + +endmodule +//--------------------------------------------------------------------------------------- +// Th.. Th.. Th.. Thats all folks !!! +//--------------------------------------------------------------------------------------- diff --git a/tests/designs/uart2bus/verilog/uart_rx.v b/tests/designs/uart2bus/verilog/uart_rx.v new file mode 100644 index 00000000..2bf8706f --- /dev/null +++ b/tests/designs/uart2bus/verilog/uart_rx.v @@ -0,0 +1,116 @@ +//--------------------------------------------------------------------------------------- +// uart receive module +// +//--------------------------------------------------------------------------------------- + +module uart_rx +( + clock, reset, + ce_16, ser_in, + rx_data, new_rx_data +); +//--------------------------------------------------------------------------------------- +// modules inputs and outputs +input clock; // global clock input +input reset; // global reset input +input ce_16; // baud rate multiplyed by 16 - generated by baud module +input ser_in; // serial data input +output [7:0] rx_data; // data byte received +output new_rx_data; // signs that a new byte was received + +// internal wires +wire ce_1; // clock enable at bit rate +wire ce_1_mid; // clock enable at the middle of each bit - used to sample data + +// internal registers +reg [7:0] rx_data; +reg new_rx_data; +reg [1:0] in_sync; +reg rx_busy; +reg [3:0] count16; +reg [3:0] bit_count; +reg [7:0] data_buf; +//--------------------------------------------------------------------------------------- +// module implementation +// input async input is sampled twice +always @ (posedge clock or posedge reset) +begin + if (reset) + in_sync <= 2'b11; + else + in_sync <= {in_sync[0], ser_in}; +end + +// a counter to count 16 pulses of ce_16 to generate the ce_1 and ce_1_mid pulses. +// this counter is used to detect the start bit while the receiver is not receiving and +// signs the sampling cycle during reception. +always @ (posedge clock or posedge reset) +begin + if (reset) + count16 <= 4'b0; + else if (ce_16) + begin + if (rx_busy | (in_sync[1] == 1'b0)) + count16 <= count16 + 4'b1; + else + count16 <= 4'b0; + end +end + +// ce_1 pulse indicating expected end of current bit +assign ce_1 = (count16 == 4'b1111) & ce_16; +// ce_1_mid pulse indication the sampling clock cycle of the current data bit +assign ce_1_mid = (count16 == 4'b0111) & ce_16; + +// receiving busy flag +always @ (posedge clock or posedge reset) +begin + if (reset) + rx_busy <= 1'b0; + else if (~rx_busy & ce_1_mid) + rx_busy <= 1'b1; + else if (rx_busy & (bit_count == 4'h8) & ce_1_mid) + rx_busy <= 1'b0; +end + +// bit counter +always @ (posedge clock or posedge reset) +begin + if (reset) + bit_count <= 4'h0; + else if (~rx_busy) + bit_count <= 4'h0; + else if (rx_busy & ce_1_mid) + bit_count <= bit_count + 4'h1; +end + +// data buffer shift register +always @ (posedge clock or posedge reset) +begin + if (reset) + data_buf <= 8'h0; + else if (rx_busy & ce_1_mid) + data_buf <= {in_sync[1], data_buf[7:1]}; +end + +// data output and flag +always @ (posedge clock or posedge reset) +begin + if (reset) + begin + rx_data <= 8'h0; + new_rx_data <= 1'b0; + end + else if (rx_busy & (bit_count == 4'h8) & ce_1) + begin + rx_data <= data_buf; + new_rx_data <= 1'b1; + end + else + new_rx_data <= 1'b0; +end + +endmodule +//--------------------------------------------------------------------------------------- +// Th.. Th.. Th.. Thats all folks !!! +//--------------------------------------------------------------------------------------- diff --git a/tests/designs/uart2bus/verilog/uart_top.v b/tests/designs/uart2bus/verilog/uart_top.v new file mode 100644 index 00000000..446eaea6 --- /dev/null +++ b/tests/designs/uart2bus/verilog/uart_top.v @@ -0,0 +1,66 @@ +//--------------------------------------------------------------------------------------- +// uart top level module +// +//--------------------------------------------------------------------------------------- + +module uart_top +( + // global signals + clock, reset, + // uart serial signals + ser_in, ser_out, + // transmit and receive internal interface signals + rx_data, new_rx_data, + tx_data, new_tx_data, tx_busy, + // baud rate configuration register - see baud_gen.v for details + baud_freq, baud_limit, + baud_clk +); +//--------------------------------------------------------------------------------------- +// modules inputs and outputs +input clock; // global clock input +input reset; // global reset input +input ser_in; // serial data input +output ser_out; // serial data output +input [7:0] tx_data; // data byte to transmit +input new_tx_data; // asserted to indicate that there is a new data byte for transmission +output tx_busy; // signs that transmitter is busy +output [7:0] rx_data; // data byte received +output new_rx_data; // signs that a new byte was received +input [11:0] baud_freq; // baud rate setting registers - see header description +input [15:0] baud_limit; +output baud_clk; + +// internal wires +wire ce_16; // clock enable at bit rate + +assign baud_clk = ce_16; +//--------------------------------------------------------------------------------------- +// module implementation +// baud rate generator module +baud_gen baud_gen_1 +( + .clock(clock), .reset(reset), + .ce_16(ce_16), .baud_freq(baud_freq), .baud_limit(baud_limit) +); + +// uart receiver +uart_rx uart_rx_1 +( + .clock(clock), .reset(reset), + .ce_16(ce_16), .ser_in(ser_in), + .rx_data(rx_data), .new_rx_data(new_rx_data) +); + +// uart transmitter +uart_tx uart_tx_1 +( + .clock(clock), .reset(reset), + .ce_16(ce_16), .tx_data(tx_data), .new_tx_data(new_tx_data), + .ser_out(ser_out), .tx_busy(tx_busy) +); + +endmodule +//--------------------------------------------------------------------------------------- +// Th.. Th.. Th.. Thats all folks !!! +//--------------------------------------------------------------------------------------- diff --git a/tests/designs/uart2bus/verilog/uart_tx.v b/tests/designs/uart2bus/verilog/uart_tx.v new file mode 100644 index 00000000..1309b345 --- /dev/null +++ b/tests/designs/uart2bus/verilog/uart_tx.v @@ -0,0 +1,94 @@ +//--------------------------------------------------------------------------------------- +// uart transmit module +// +//--------------------------------------------------------------------------------------- + +module uart_tx +( + clock, reset, + ce_16, tx_data, new_tx_data, + ser_out, tx_busy +); +//--------------------------------------------------------------------------------------- +// modules inputs and outputs +input clock; // global clock input +input reset; // global reset input +input ce_16; // baud rate multiplyed by 16 - generated by baud module +input [7:0] tx_data; // data byte to transmit +input new_tx_data; // asserted to indicate that there is a new data byte for transmission +output ser_out; // serial data output +output tx_busy; // signs that transmitter is busy + +// internal wires +wire ce_1; // clock enable at bit rate + +// internal registers +reg ser_out; +reg tx_busy; +reg [3:0] count16; +reg [3:0] bit_count; +reg [8:0] data_buf; +//--------------------------------------------------------------------------------------- +// module implementation +// a counter to count 16 pulses of ce_16 to generate the ce_1 pulse +always @ (posedge clock or posedge reset) +begin + if (reset) + count16 <= 4'b0; + else if (tx_busy & ce_16) + count16 <= count16 + 4'b1; + else if (~tx_busy) + count16 <= 4'b0; +end + +// ce_1 pulse indicating output data bit should be updated +assign ce_1 = (count16 == 4'b1111) & ce_16; + +// tx_busy flag +always @ (posedge clock or posedge reset) +begin + if (reset) + tx_busy <= 1'b0; + else if (~tx_busy & new_tx_data) + tx_busy <= 1'b1; + else if (tx_busy & (bit_count == 4'h9) & ce_1) + tx_busy <= 1'b0; +end + +// output bit counter +always @ (posedge clock or posedge reset) +begin + if (reset) + bit_count <= 4'h0; + else if (tx_busy & ce_1) + bit_count <= bit_count + 4'h1; + else if (~tx_busy) + bit_count <= 4'h0; +end + +// data shift register +always @ (posedge clock or posedge reset) +begin + if (reset) + data_buf <= 9'b0; + else if (~tx_busy) + data_buf <= {tx_data, 1'b0}; + else if (tx_busy & ce_1) + data_buf <= {1'b1, data_buf[8:1]}; +end + +// output data bit +always @ (posedge clock or posedge reset) +begin + if (reset) + ser_out <= 1'b1; + else if (tx_busy) + ser_out <= data_buf[0]; + else + ser_out <= 1'b1; +end + +endmodule +//--------------------------------------------------------------------------------------- +// Th.. Th.. Th.. Thats all folks !!! +//--------------------------------------------------------------------------------------- diff --git a/tests/designs/uart2bus/vhdl/baudGen.vhd b/tests/designs/uart2bus/vhdl/baudGen.vhd new file mode 100644 index 00000000..2cf39739 --- /dev/null +++ b/tests/designs/uart2bus/vhdl/baudGen.vhd @@ -0,0 +1,48 @@ +----------------------------------------------------------------------------------------- +-- baud rate generator for uart +-- +-- this module has been changed to receive the baud rate dividing counter from registers. +-- the two registers should be calculated as follows: +-- first register: +-- baud_freq = 16*baud_rate / gcd(global_clock_freq, 16*baud_rate) +-- second register: +-- baud_limit = (global_clock_freq / gcd(global_clock_freq, 16*baud_rate)) - baud_freq +-- +----------------------------------------------------------------------------------------- +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_unsigned.all; + +entity baudGen is + port ( clr : in std_logic; -- global reset input + clk : in std_logic; -- global clock input + -- baudFreq = 16 * baudRate / gcd(clkFreq, 16 * baudRate) + baudFreq : in std_logic_vector(11 downto 0); -- baud rate setting registers - see header description + -- baudLimit = clkFreq / gcd(clkFreq, 16 * baudRate) - baudFreq + baudLimit : in std_logic_vector(15 downto 0); -- baud rate setting registers - see header description + ce16 : out std_logic); -- baud rate multiplyed by 16 +end baudGen; + +architecture Behavioral of baudGen is + + signal counter : std_logic_vector(15 downto 0); + + begin + -- baud divider counter + -- clock divider output + process (clr, clk) + begin + if (clr = '1') then + counter <= (others => '0'); + ce16 <= '0'; + elsif (rising_edge(clk)) then + if (counter >= baudLimit) then + counter <= counter - baudLimit; + ce16 <= '1'; + else + counter <= counter + baudFreq; + ce16 <= '0'; + end if; + end if; + end process; + end Behavioral; diff --git a/tests/designs/uart2bus/vhdl/uart2BusTop.vhd b/tests/designs/uart2bus/vhdl/uart2BusTop.vhd new file mode 100644 index 00000000..f5f14dfb --- /dev/null +++ b/tests/designs/uart2bus/vhdl/uart2BusTop.vhd @@ -0,0 +1,77 @@ +----------------------------------------------------------------------------------------- +-- uart to internal bus top module +-- +----------------------------------------------------------------------------------------- +library ieee; +use ieee.std_logic_1164.all; + +library work; +use work.uart2BusTop_pkg.all; + +entity uart2BusTop is + generic ( AW : integer := 16); + port ( -- global signals + clr : in STD_LOGIC; -- global reset input + clk : in STD_LOGIC; -- global clock input + -- uart serial signals + serIn : in STD_LOGIC; -- serial data input + serOut : out STD_LOGIC; -- serial data output + -- internal bus to register file + intAccessReq : out std_logic; -- + intAccessGnt : in std_logic; -- + intRdData : in STD_LOGIC_VECTOR (7 downto 0); -- data read from register file + intAddress : out STD_LOGIC_VECTOR (AW - 1 downto 0); -- address bus to register file + intWrData : out STD_LOGIC_VECTOR (7 downto 0); -- write data to register file + intWrite : out STD_LOGIC; -- write control to register file + intRead : out STD_LOGIC); -- read control to register file +end uart2BusTop; + +architecture Behavioral of uart2BusTop is + + -- baud rate configuration, see baudGen.vhd for more details. + -- baud rate generator parameters for 115200 baud on 25MHz clock + constant baudFreq : std_logic_vector(11 downto 0) := x"480"; + constant baudLimit : std_logic_vector(15 downto 0) := x"3889"; + signal txData : std_logic_vector(7 downto 0); -- data byte to transmit + signal newTxData : std_logic; -- asserted to indicate that there is a new data byte for transmission + signal txBusy : std_logic; -- signs that transmitter is busy + signal rxData : std_logic_vector(7 downto 0); -- data byte received + signal newRxData : std_logic; -- signs that a new byte was received + + begin + -- uart top module instance + ut : uartTop + port map ( + clr => clr, + clk => clk, + serIn => serIn, + txData => txData, + newTxData => newTxData, + baudFreq => baudFreq, + baudLimit => baudLimit, + serOut => serOut, + txBusy => txBusy, + rxData => rxData, + newRxData => newRxData, + baudClk => open); + -- uart parser instance + up : uartParser + generic map ( + AW => AW) + port map ( + clr => clr, + clk => clk, + txBusy => txBusy, + rxData => rxData, + newRxData => newRxData, + intRdData => intRdData, + txData => txData, + newTxData => newTxData, + intReq => intAccessReq, + intGnt => intAccessGnt, + intAddress => intAddress, + intWrData => intWrData, + intWrite => intWrite, + intRead => intRead); + + end Behavioral; diff --git a/tests/designs/uart2bus/vhdl/uart2BusTop_pkg.vhd b/tests/designs/uart2bus/vhdl/uart2BusTop_pkg.vhd new file mode 100644 index 00000000..3c334700 --- /dev/null +++ b/tests/designs/uart2bus/vhdl/uart2BusTop_pkg.vhd @@ -0,0 +1,90 @@ +library ieee; +use ieee.std_logic_1164.all; + +package uart2BusTop_pkg is + + component baudGen + port ( + clr : in std_logic; + clk : in std_logic; + baudFreq : in std_logic_vector(11 downto 0); + baudLimit : in std_logic_vector(15 downto 0); + ce16 : out std_logic); + end component; + + component uartTx + port ( + clr : in std_logic; + clk : in std_logic; + ce16 : in std_logic; + txData : in std_logic_vector(7 downto 0); + newTxData : in std_logic; + serOut : out std_logic; + txBusy : out std_logic); + end component; + + component uartRx + port ( + clr : in std_logic; + clk : in std_logic; + ce16 : in std_logic; + serIn : in std_logic; + rxData : out std_logic_vector(7 downto 0); + newRxData : out std_logic); + end component; + + component uartTop + port ( clr : in std_logic; + clk : in std_logic; + serIn : in std_logic; + txData : in std_logic_vector(7 downto 0); + newTxData : in std_logic; + baudFreq : in std_logic_vector(11 downto 0); + baudLimit : in std_logic_vector(15 downto 0); + serOut : out std_logic; + txBusy : out std_logic; + rxData : out std_logic_vector(7 downto 0); + newRxData : out std_logic; + baudClk : out std_logic); + end component; + + component uartParser + generic ( AW : integer := 8); + port ( clr : in std_logic; + clk : in std_logic; + txBusy : in std_logic; + rxData : in std_logic_vector(7 downto 0); + newRxData : in std_logic; + intRdData : in std_logic_vector(7 downto 0); + txData : out std_logic_vector(7 downto 0); + newTxData : out std_logic; + intReq : out std_logic; + intGnt : in std_logic; + intAddress : out std_logic_vector(AW - 1 downto 0); + intWrData : out std_logic_vector(7 downto 0); + intWrite : out std_logic; + intRead : out std_logic); + end component; + + component uart2BusTop + generic + ( + AW : integer := 8 + ); + port + ( + clr : in std_logic; + clk : in std_logic; + serIn : in std_logic; + serOut : out std_logic; + intAccessReq : out std_logic; + intAccessGnt : in std_logic; + intRdData : in std_logic_vector(7 downto 0); + intAddress : out std_logic_vector(AW - 1 downto 0); + intWrData : out std_logic_vector(7 downto 0); + intWrite : out std_logic; + intRead : out std_logic + ); + end component; + +end uart2BusTop_pkg; diff --git a/tests/designs/uart2bus/vhdl/uartParser.vhd b/tests/designs/uart2bus/vhdl/uartParser.vhd new file mode 100644 index 00000000..ccdb7cb2 --- /dev/null +++ b/tests/designs/uart2bus/vhdl/uartParser.vhd @@ -0,0 +1,576 @@ +----------------------------------------------------------------------------------------- +-- uart parser module +-- +----------------------------------------------------------------------------------------- +library ieee; +use ieee.std_logic_1164.ALL; +use ieee.std_logic_unsigned.ALL; + +entity uartParser is + generic ( -- parameters + AW : integer := 8); + port ( -- global signals + clr : in std_logic; -- global reset input + clk : in std_logic; -- global clock input + -- transmit and receive internal interface signals from uart interface + txBusy : in std_logic; -- signs that transmitter is busy + rxData : in std_logic_vector(7 downto 0); -- data byte received + newRxData : in std_logic; -- signs that a new byte was received + txData : out std_logic_vector(7 downto 0); -- data byte to transmit + newTxData : out std_logic; -- asserted to indicate that there is a new data byte for transmission + -- internal bus to register file + intReq : out std_logic; -- + intGnt : in std_logic; -- + intRdData : in std_logic_vector(7 downto 0); -- data read from register file + intAddress : out std_logic_vector(AW - 1 downto 0); -- address bus to register file + intWrData : out std_logic_vector(7 downto 0); -- write data to register file + intWrite : out std_logic; -- write control to register file + intRead : out std_logic); -- read control to register file +end uartParser; + +architecture Behavioral of uartParser is + + -- internal constants + -- main (receive) state machine states + signal mainSm : std_logic_vector(3 downto 0); -- main state machine + constant mainIdle : std_logic_vector(mainSm'range) := "0000"; + constant mainWhite1 : std_logic_vector(mainSm'range) := "0001"; + constant mainData : std_logic_vector(mainSm'range) := "0010"; + constant mainWhite2 : std_logic_vector(mainSm'range) := "0011"; + constant mainAddr : std_logic_vector(mainSm'range) := "0100"; + constant mainEol : std_logic_vector(mainSm'range) := "0101"; + -- binary mode extension states + constant mainBinCmd : std_logic_vector(mainSm'range) := "1000"; + constant mainBinAdrh : std_logic_vector(mainSm'range) := "1001"; + constant mainBinAdrl : std_logic_vector(mainSm'range) := "1010"; + constant mainBinLen : std_logic_vector(mainSm'range) := "1011"; + constant mainBinData : std_logic_vector(mainSm'range) := "1100"; + + -- transmit state machine + signal txSm : std_logic_vector(2 downto 0); -- transmit state machine + constant txIdle : std_logic_vector(txSm'range) := "000"; + constant txHiNib : std_logic_vector(txSm'range) := "001"; + constant txLoNib : std_logic_vector(txSm'range) := "100"; + constant txCharCR : std_logic_vector(txSm'range) := "101"; + constant txCharLF : std_logic_vector(txSm'range) := "110"; + + -- define characters used by the parser + constant charNul : std_logic_vector(7 downto 0) := x"00"; + constant charTab : std_logic_vector(7 downto 0) := x"09"; + constant charLF : std_logic_vector(7 downto 0) := x"0A"; + constant charCR : std_logic_vector(7 downto 0) := x"0D"; + constant charSpace : std_logic_vector(7 downto 0) := x"20"; + constant charZero : std_logic_vector(7 downto 0) := x"30"; + constant charOne : std_logic_vector(7 downto 0) := x"31"; + constant charTwo : std_logic_vector(7 downto 0) := x"32"; + constant charThree : std_logic_vector(7 downto 0) := x"33"; + constant charFour : std_logic_vector(7 downto 0) := x"34"; + constant charFive : std_logic_vector(7 downto 0) := x"35"; + constant charSix : std_logic_vector(7 downto 0) := x"36"; + constant charSeven : std_logic_vector(7 downto 0) := x"37"; + constant charEight : std_logic_vector(7 downto 0) := x"38"; + constant charNine : std_logic_vector(7 downto 0) := x"39"; + constant charAHigh : std_logic_vector(7 downto 0) := x"41"; + constant charBHigh : std_logic_vector(7 downto 0) := x"42"; + constant charCHigh : std_logic_vector(7 downto 0) := x"43"; + constant charDHigh : std_logic_vector(7 downto 0) := x"44"; + constant charEHigh : std_logic_vector(7 downto 0) := x"45"; + constant charFHigh : std_logic_vector(7 downto 0) := x"46"; + constant charRHigh : std_logic_vector(7 downto 0) := x"52"; + constant charWHigh : std_logic_vector(7 downto 0) := x"57"; + constant charALow : std_logic_vector(7 downto 0) := x"61"; + constant charBLow : std_logic_vector(7 downto 0) := x"62"; + constant charCLow : std_logic_vector(7 downto 0) := x"63"; + constant charDLow : std_logic_vector(7 downto 0) := x"64"; + constant charELow : std_logic_vector(7 downto 0) := x"65"; + constant charFLow : std_logic_vector(7 downto 0) := x"66"; + constant charRLow : std_logic_vector(7 downto 0) := x"72"; + constant charWLow : std_logic_vector(7 downto 0) := x"77"; + + -- binary extension mode commands - the command is indicated by bits 5:4 of the command byte + constant binCmdNop : std_logic_vector(1 downto 0) := "00"; + constant binCmdRead : std_logic_vector(1 downto 0) := "01"; + constant binCmdWrite : std_logic_vector(1 downto 0) := "10"; + + signal dataInHexRange : std_logic; -- indicates that the received data is in the range of hex number + signal binLastByte : std_logic; -- last byte flag indicates that the current byte in the command is the last + signal txEndP : std_logic; -- transmission end pulse + signal readOp : std_logic; -- read operation flag + signal writeOp : std_logic; -- write operation flag + signal binReadOp : std_logic; -- binary mode read operation flag + signal binWriteOp : std_logic; -- binary mode write operation flag + signal sendStatFlag : std_logic; -- send status flag + signal addrAutoInc : std_logic; -- address auto increment mode + signal dataParam : std_logic_vector(7 downto 0); -- operation data parameter + signal dataNibble : std_logic_vector(3 downto 0); -- data nibble from received character + signal addrParam : std_logic_vector(15 downto 0); -- operation address parameter + signal addrNibble : std_logic_vector(3 downto 0); -- data nibble from received character + signal binByteCount : std_logic_vector(7 downto 0); -- binary mode byte counter + signal iIntAddress : std_logic_vector(intAddress'range); -- + signal iWriteReq : std_logic; -- + signal iIntWrite : std_logic; -- + signal readDone : std_logic; -- internally generated read done flag + signal readDoneS : std_logic; -- sampled read done + signal readDataS : std_logic_vector(7 downto 0); -- sampled read data + signal iReadReq : std_logic; -- + signal iIntRead : std_logic; -- + signal txChar : std_logic_vector(7 downto 0); -- transmit byte from nibble to character conversion + signal sTxBusy : std_logic; -- sampled tx_busy for falling edge detection + signal txNibble : std_logic_vector(3 downto 0); -- nibble value for transmission + + -- module implementation + -- main state machine + begin + process (clr, clk) + begin + if (clr = '1') then + mainSm <= mainIdle; + elsif (rising_edge(clk)) then + if (newRxData = '1') then + case mainSm is + -- wait for a read ('r') or write ('w') command + -- binary extension - an all zeros byte enabled binary commands + when mainIdle => + -- check received character + if (rxData = charNul) then + -- an all zeros received byte enters binary mode + mainSm <= mainBinCmd; + elsif ((rxData = charRLow) or (rxData = charRHigh)) then + -- on read wait to receive only address field + mainSm <= mainWhite2; + elsif ((rxData = charWLow) or (rxData = charWHigh)) then + -- on write wait to receive data and address + mainSm <= mainWhite1; + elsif ((rxData = charCR) or (rxData = charLF)) then + -- on new line sta in idle + mainSm <= mainIdle; + else + -- any other character wait to end of line (EOL) + mainSm <= mainEol; + end if; + -- wait for white spaces till first data nibble + when mainWhite1 => + -- wait in this case until any white space character is received. in any + -- valid character for data value switch to data state. a new line or carriage + -- return should reset the state machine to idle. + -- any other character transitions the state machine to wait for EOL. + if ((rxData = charSpace) or (rxData = charTab)) then + mainSm <= mainWhite1; + elsif (dataInHexRange = '1') then + mainSm <= mainData; + elsif ((rxData = charCR) or (rxData = charLF)) then + mainSm <= mainIdle; + else + mainSm <= mainEol; + end if; + -- receive data field + when mainData => + -- wait while data in hex range. white space transition to wait white 2 state. + -- CR and LF resets the state machine. any other value cause state machine to + -- wait til end of line. + if (dataInHexRange = '1') then + mainSm <= mainData; + elsif ((rxData = charSpace) or (rxData = charTab)) then + mainSm <= mainWhite2; + elsif ((rxData = charCR) or (rxData = charLF)) then + mainSm <= mainIdle; + else + mainSm <= mainEol; + end if; + -- wait for white spaces till first address nibble + when mainWhite2 => + -- similar to MAIN_WHITE1 + if ((rxData = charSpace) or (rxData = charTab)) then + mainSm <= mainWhite2; + elsif (dataInHexRange = '1') then + mainSm <= mainAddr; + elsif ((rxData = charCR) or (rxData = charLF)) then + mainSm <= mainIdle; + else + mainSm <= mainEol; + end if; + -- receive address field + when mainAddr => + -- similar to MAIN_DATA + if (dataInHexRange = '1') then + mainSm <= mainAddr; + elsif ((rxData = charCR) or (rxData = charLF)) then + mainSm <= mainIdle; + else + mainSm <= mainEol; + end if; + -- wait to EOL + when mainEol => + -- wait for CR or LF to move back to idle + if ((rxData = charCR) or (rxData = charLF)) then + mainSm <= mainIdle; + end if; + -- binary extension + -- wait for command - one byte + when mainBinCmd => + -- check if command is a NOP command + if (rxData(5 downto 4) = binCmdNop) then + -- if NOP command then switch back to idle state + mainSm <= mainIdle; + else + -- not a NOP command, continue receiving parameters + mainSm <= mainBinAdrh; + end if; + -- wait for address parameter - two bytes + -- high address byte + when mainBinAdrh => + -- switch to next state + mainSm <= mainBinAdrl; + -- low address byte + when mainBinAdrl => + -- switch to next state + mainSm <= mainBinLen; + -- wait for length parameter - one byte + when mainBinLen => + -- check if write command else command reception ended + if (binWriteOp = '1') then + -- wait for write data + mainSm <= mainBinData; + else + -- command reception has ended + mainSm <= mainIdle; + end if; + -- on write commands wait for data till end of buffer as specified by length parameter + when mainBinData => + -- if this is the last data byte then return to idle + if (binLastByte = '1') then + mainSm <= mainIdle; + end if; + -- go to idle + when others => + mainSm <= mainIdle; + end case; + end if; + end if; + end process; + -- read operation flag + -- write operation flag + -- binary mode read operation flag + -- binary mode write operation flag + process (clr, clk) + begin + if (clr = '1') then + readOp <= '0'; + writeOp <= '0'; + binReadOp <= '0'; + binWriteOp <= '0'; + elsif (rising_edge(clk)) then + if ((mainSm = mainIdle) and (newRxData = '1')) then + -- the read operation flag is set when a read command is received in idle state and cleared + -- if any other character is received during that state. + if ((rxData = charRLow) or (rxData = charRHigh)) then + readOp <= '1'; + else + readOp <= '0'; + end if; + -- the write operation flag is set when a write command is received in idle state and cleared + -- if any other character is received during that state. + if ((rxData = charWLow) or (rxData = charWHigh)) then + writeOp <= '1'; + else + writeOp <= '0'; + end if; + end if; + if ((mainSm = mainBinCmd) and (newRxData = '1') and (rxData(5 downto 4) = binCmdRead)) then + -- read command is started on reception of a read command + binReadOp <= '1'; + elsif ((binReadOp = '1') and (txEndP = '1') and (binLastByte = '1')) then + -- read command ends on transmission of the last byte read + binReadOp <= '0'; + end if; + if ((mainSm = mainBinCmd) and (newRxData = '1') and (rxData(5 downto 4) = binCmdWrite)) then + -- write command is started on reception of a write command + binWriteOp <= '1'; + elsif ((mainSm = mainBinData) and (newRxData = '1') and (binLastByte = '1')) then + binWriteOp <= '0'; + end if; + end if; + end process; + -- send status flag - used only in binary extension mode + -- address auto increment - used only in binary extension mode + process (clr, clk) + begin + if (clr = '1') then + sendStatFlag <= '0'; + addrAutoInc <= '0'; + elsif (rising_edge(clk)) then + if ((mainSm = mainBinCmd) and (newRxData = '1')) then + -- check if a status byte should be sent at the end of the command + sendStatFlag <= rxData(0); + -- check if address should be automatically incremented or not. + -- Note that when rx_data[1] is set, address auto increment is disabled. + addrAutoInc <= not(rxData(1)); + end if; + end if; + end process; + -- operation data parameter + process (clr, clk) + begin + if (clr = '1') then + dataParam <= (others => '0'); + elsif (rising_edge(clk)) then + if ((mainSm = mainWhite1) and (newRxData = '1') and (dataInHexRange = '1')) then + dataParam <= "0000" & dataNibble; + elsif ((mainSm = mainData) and (newRxData = '1') and (dataInHexRange = '1')) then + dataParam <= dataParam(3 downto 0) & dataNibble; + end if; + end if; + end process; + -- operation address parameter + process (clr, clk) + begin + if (clr = '1') then + addrParam <= (others => '0'); + elsif (rising_edge(clk)) then + if ((mainSm = mainWhite2) and (newRxData = '1') and (dataInHexRange = '1')) then + addrParam <= x"000" & dataNibble; + elsif ((mainSm = mainAddr) and (newRxData = '1') and (dataInHexRange = '1')) then + addrParam <= addrParam(11 downto 0) & dataNibble; + -- binary extension + elsif (mainSm = mainBinAdrh) then + addrParam(15 downto 8) <= rxData; + elsif (mainSm = mainBinAdrl) then + addrParam(7 downto 0) <= rxData; + end if; + end if; + end process; + -- binary mode command byte counter is loaded with the length parameter and counts down to zero. + -- NOTE: a value of zero for the length parameter indicates a command of 256 bytes. + process (clr, clk) + begin + if (clr = '1') then + binByteCount <= (others => '0'); + elsif (rising_edge(clk)) then + if ((mainSm = mainBinLen) and (newRxData = '1')) then + binByteCount <= rxData; + elsif (((mainSm = mainBinData) and (binWriteOp = '1') and (newRxData = '1')) or ((binReadOp = '1') and (txEndP = '1'))) then + -- byte counter is updated on every new data received in write operations and for every + -- byte transmitted for read operations. + binByteCount <= binByteCount - 1; + end if; + end if; + end process; + -- internal write control and data + -- internal read control + process (clr, clk) + begin + if (clr = '1') then + iReadReq <= '0'; + iIntRead <= '0'; + iWriteReq <= '0'; + iIntWrite <= '0'; + intWrData <= (others => '0'); + elsif (rising_edge(clk)) then + if ((mainSm = mainAddr) and (writeOp = '1') and (newRxData = '1') and (dataInHexRange = '0')) then + iWriteReq <= '1'; + intWrData <= dataParam; + -- binary extension mode + elsif ((mainSm = mainBinData) and (binWriteOp = '1') and (newRxData = '1')) then + iWriteReq <= '1'; + intWrData <= rxData; + elsif ((intGnt = '1') and (iWriteReq = '1')) then + iWriteReq <= '0'; + iIntWrite <= '1'; + else + iIntWrite <= '0'; + end if; + if ((mainSm = mainAddr) and (readOp = '1') and (newRxData = '1') and (dataInHexRange = '0')) then + iReadReq <= '1'; + -- binary extension + elsif ((mainSm = mainBinLen) and (binReadOp = '1') and (newRxData = '1')) then + -- the first read request is issued on reception of the length byte + iReadReq <= '1'; + elsif ((binReadOp = '1') and (txEndP = '1') and (binLastByte = '0')) then + -- the next read requests are issued after the previous read value was transmitted and + -- this is not the last byte to be read. + iReadReq <= '1'; + elsif ((intGnt = '1') and (iReadReq = '1')) then + iReadReq <= '0'; + iIntRead <= '1'; + else + iIntRead <= '0'; + end if; + end if; + end process; + -- internal address + process (clr, clk) + begin + if (clr = '1') then + iIntAddress <= (others => '0'); + elsif (rising_edge(clk)) then + if ((mainSm = mainAddr) and (newRxData = '1') and (dataInHexRange = '0')) then + iIntAddress <= addrParam(AW - 1 downto 0); + -- binary extension + elsif ((mainSm = mainBinLen) and (newRxData = '1')) then + -- sample address parameter on reception of length byte + iIntAddress <= addrParam(AW - 1 downto 0); + elsif ((addrAutoInc = '1') and (((binReadOp = '1') and (txEndP = '1') and (binLastByte = '0')) or ((binWriteOp = '1') and (iIntWrite = '1')))) then + -- address is incremented on every read or write if enabled + iIntAddress <= iIntAddress + 1; + end if; + end if; + end process; + -- read done flag and sampled data read + process (clr, clk) + begin + if (clr = '1') then + readDone <= '0'; + readDoneS <= '0'; + readDataS <= (others => '0'); + elsif (rising_edge(clk)) then + -- read done flag + readDone <= iIntRead; + -- sampled read done + readDoneS <= readDone; + -- sampled data read + if (readDone = '1') then + readDataS <= intRdData; + end if; + end if; + end process; + -- transmit state machine and control + process (clr, clk) + begin + if (clr = '1') then + txSm <= txIdle; + txData <= (others => '0'); + newTxData <= '0'; + elsif (rising_edge(clk)) then + case txSm is + -- wait for read done indication + when txIdle => + -- on end of every read operation check how the data read should be transmitted + -- according to read type: ascii or binary. + if (readDoneS = '1') then + -- on binary mode read transmit byte value + if (binReadOp = '1') then + -- note that there is no need to change state + txData <= readDataS; + newTxData <= '1'; + else + txSm <= txHiNib; + txData <= txChar; + newTxData <= '1'; + end if; + -- check if status byte should be transmitted + elsif (((sendStatFlag = '1') and (binReadOp = '1') and (txEndP = '1') and (binLastByte = '1')) or ((sendStatFlag = '1') and (binWriteOp = '1') and (newRxData = '1') and (binLastByte = '1')) or ((mainSm = mainBinCmd) and (newRxData = '1') and (rxData(5 downto 4) = binCmdNop))) then + -- send status byte - currently a constant + txData <= x"5A"; + newTxData <= '1'; + else + newTxData <= '0'; + end if; + when txHiNib => + -- wait for transmit to end + if (txEndP = '1') then + txSm <= txLoNib; + txData <= txChar; + newTxData <= '1'; + else + newTxData <= '0'; + end if; + -- wait for transmit to end + when txLoNib => + if (txEndP = '1') then + txSm <= txCharCR; + txData <= charCR; + newTxData <= '1'; + else + newTxData <= '0'; + end if; + -- wait for transmit to end + when txCharCR => + if (txEndP = '1') then + txSm <= txCharLF; + txData <= charLF; + newTxData <= '1'; + else + newTxData <= '0'; + end if; + -- wait for transmit to end + when txCharLF => + if (txEndP = '1') then + txSm <= txIdle; + end if; + -- clear tx new data flag + newTxData <= '0'; + -- return to idle + when others => + txSm <= txIdle; + end case; + end if; + end process; + -- sampled tx_busy + process (clr, clk) + begin + if (clr = '1') then + sTxBusy <= '1'; + elsif (rising_edge(clk)) then + sTxBusy <= txBusy; + end if; + end process; + -- indicates that the received data is in the range of hex number + dataInHexRange <= '1' when (((rxData >= charZero) and (rxData <= charNine)) or + ((rxData >= charAHigh) and (rxData <= charFHigh)) or + ((rxData >= charALow) and (rxData <= charFLow))) else '0'; + -- last byte in command flag + binLastByte <= '1' when (binByteCount = x"01") else '0'; + -- select the nibble to the nibble to character conversion + txNibble <= readDataS(3 downto 0) when (txSm = txHiNib) else readDataS(7 downto 4); + -- tx end pulse + txEndP <= '1' when ((txBusy = '0') and (sTxBusy = '1')) else '0'; + -- character to nibble conversion + with rxData select + dataNibble <= x"0" when charZero, + x"1" when charOne, + x"2" when charTwo, + x"3" when charThree, + x"4" when charFour, + x"5" when charFive, + x"6" when charSix, + x"7" when charSeven, + x"8" when charEight, + x"9" when charNine, + x"A" when charALow, + x"A" when charAHigh, + x"B" when charBLow, + x"B" when charBHigh, + x"C" when charCLow, + x"C" when charCHigh, + x"D" when charDLow, + x"D" when charDHigh, + x"E" when charELow, + x"E" when charEHigh, + x"F" when charFLow, + x"F" when charFHigh, + x"F" when others; + -- nibble to character conversion + with txNibble select + txChar <= charZero when x"0", + charOne when x"1", + charTwo when x"2", + charThree when x"3", + charFour when x"4", + charFive when x"5", + charSix when x"6", + charSeven when x"7", + charEight when x"8", + charNine when x"9", + charAHigh when x"A", + charBHigh when x"B", + charCHigh when x"C", + charDHigh when x"D", + charEHigh when x"E", + charFHigh when x"F", + charFHigh when others; + intAddress <= iIntAddress; + intWrite <= iIntWrite; + intRead <= iIntRead; + intReq <= '1' when (iReadReq = '1') else + '1' when (iWriteReq = '1') else '0'; + end Behavioral; diff --git a/tests/designs/uart2bus/vhdl/uartRx.vhd b/tests/designs/uart2bus/vhdl/uartRx.vhd new file mode 100644 index 00000000..f4fe45c4 --- /dev/null +++ b/tests/designs/uart2bus/vhdl/uartRx.vhd @@ -0,0 +1,111 @@ +----------------------------------------------------------------------------------------- +-- uart receive module +-- +----------------------------------------------------------------------------------------- +library ieee; +use ieee.std_logic_1164.ALL; +use ieee.std_logic_unsigned.ALL; + +entity uartRx is + port ( clr : in std_logic; -- global reset input + clk : in std_logic; -- global clock input + ce16 : in std_logic; -- baud rate multiplyed by 16 - generated by baud module + serIn : in std_logic; -- serial data input + rxData : out std_logic_vector(7 downto 0); -- data byte received + newRxData : out std_logic); -- signs that a new byte was received +end uartRx; + +architecture Behavioral of uartRx is + + signal ce1 : std_logic; -- clock enable at bit rate + signal ce1Mid : std_logic; -- clock enable at the middle of each bit - used to sample data + signal inSync : std_logic_vector(1 downto 0); + signal count16 : std_logic_vector(3 downto 0); + signal rxBusy : std_logic; + signal bitCount : std_logic_vector(3 downto 0); + signal dataBuf : std_logic_vector(7 downto 0); + + begin + -- input async input is sampled twice + process (clr, clk) + begin + if (clr = '1') then + inSync <= (others => '1'); + elsif (rising_edge(clk)) then + inSync <= inSync(0) & serIn; + end if; + end process; + -- a counter to count 16 pulses of ce_16 to generate the ce_1 and ce_1_mid pulses. + -- this counter is used to detect the start bit while the receiver is not receiving and + -- signs the sampling cycle during reception. + process (clr, clk) + begin + if (clr = '1') then + count16 <= (others => '0'); + elsif (rising_edge(clk)) then + if (ce16 = '1') then + if ((rxBusy = '1') or (inSync(1) = '0')) then + count16 <= count16 + 1; + else + count16 <= (others => '0'); + end if; + end if; + end if; + end process; + -- receiving busy flag + process (clr, clk) + begin + if (clr = '1') then + rxBusy <= '0'; + elsif (rising_edge(clk)) then + if ((rxBusy = '0') and (ce1Mid = '1')) then + rxBusy <= '1'; + elsif ((rxBusy = '1') and (bitCount = "1000") and (ce1Mid = '1')) then + rxBusy <= '0'; + end if; + end if; + end process; + -- bit counter + process (clr, clk) + begin + if (clr = '1') then + bitCount <= (others => '0'); + elsif (rising_edge(clk)) then + if (rxBusy = '0') then + bitCount <= (others => '0'); + elsif ((rxBusy = '1') and (ce1Mid = '1')) then + bitCount <= bitCount + 1; + end if; + end if; + end process; + -- data buffer shift register + process (clr, clk) + begin + if (clr = '1') then + dataBuf <= (others => '0'); + elsif (rising_edge(clk)) then + if ((rxBusy = '1') and (ce1Mid = '1')) then + dataBuf <= inSync(1) & dataBuf(7 downto 1); + end if; + end if; + end process; + -- data output and flag + process (clr, clk) + begin + if (clr = '1') then + rxData <= (others => '0'); + newRxData <= '0'; + elsif (rising_edge(clk)) then + if ((rxBusy = '1') and (bitCount = "1000") and (ce1 = '1')) then + rxData <= dataBuf; + newRxData <= '1'; + else + newRxData <= '0'; + end if; + end if; + end process; + -- ce_1 pulse indicating expected end of current bit + ce1 <= '1' when ((count16 = "1111") and (ce16 = '1')) else '0'; + -- ce_1_mid pulse indication the sampling clock cycle of the current data bit + ce1Mid <= '1' when ((count16 = "0111") and (ce16 = '1')) else '0'; + end Behavioral; diff --git a/tests/designs/uart2bus/vhdl/uartTop.vhd b/tests/designs/uart2bus/vhdl/uartTop.vhd new file mode 100644 index 00000000..7411f834 --- /dev/null +++ b/tests/designs/uart2bus/vhdl/uartTop.vhd @@ -0,0 +1,63 @@ +----------------------------------------------------------------------------------------- +-- uart top level module +-- +----------------------------------------------------------------------------------------- +library ieee; +use ieee.std_logic_1164.all; + +library work; +use work.uart2BusTop_pkg.all; + +entity uartTop is + port ( -- global signals + clr : in std_logic; -- global reset input + clk : in std_logic; -- global clock input + -- uart serial signals + serIn : in std_logic; -- serial data input + serOut : out std_logic; -- serial data output + -- transmit and receive internal interface signals + txData : in std_logic_vector(7 downto 0); -- data byte to transmit + newTxData : in std_logic; -- asserted to indicate that there is a new data byte for transmission + txBusy : out std_logic; -- signs that transmitter is busy + rxData : out std_logic_vector(7 downto 0); -- data byte received + newRxData : out std_logic; -- signs that a new byte was received + -- baud rate configuration register - see baudGen.vhd for details + baudFreq : in std_logic_vector(11 downto 0); -- baud rate setting registers - see header description + baudLimit : in std_logic_vector(15 downto 0); -- baud rate setting registers - see header description + baudClk : out std_logic); -- +end uartTop; + +architecture Behavioral of uartTop is + + signal ce16 : std_logic; -- clock enable at bit rate + + begin + -- baud rate generator module + bg : baudGen + port map ( + clr => clr, + clk => clk, + baudFreq => baudFreq, + baudLimit => baudLimit, + ce16 => ce16); + -- uart receiver + ut : uartTx + port map ( + clr => clr, + clk => clk, + ce16 => ce16, + txData => txData, + newTxData => newTxData, + serOut => serOut, + txBusy => txBusy); + -- uart transmitter + ur : uartRx + port map ( + clr => clr, + clk => clk, + ce16 => ce16, + serIn => serIn, + rxData => rxData, + newRxData => newRxData); + baudClk <= ce16; + end Behavioral; diff --git a/tests/designs/uart2bus/vhdl/uartTx.vhd b/tests/designs/uart2bus/vhdl/uartTx.vhd new file mode 100644 index 00000000..b77ab81d --- /dev/null +++ b/tests/designs/uart2bus/vhdl/uartTx.vhd @@ -0,0 +1,96 @@ +----------------------------------------------------------------------------------------- +-- uart transmit module +-- +----------------------------------------------------------------------------------------- +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_unsigned.all; + +entity uartTx is + port ( clr : in std_logic; -- global reset input + clk : in std_logic; -- global clock input + ce16 : in std_logic; -- baud rate multiplyed by 16 - generated by baud module + txData : in std_logic_vector(7 downto 0); -- data byte to transmit + newTxData : in std_logic; -- asserted to indicate that there is a new data byte for transmission + serOut : out std_logic; -- serial data output + txBusy : out std_logic); -- signs that transmitter is busy +end uartTx; + +architecture Behavioral of uartTx is + + signal iTxBusy : std_logic; + signal ce1 : std_logic; -- clock enable at bit rate + signal count16 : std_logic_vector(3 downto 0); + signal bitCount : std_logic_vector(3 downto 0); + signal dataBuf : std_logic_vector(8 downto 0); + + begin + -- a counter to count 16 pulses of ce_16 to generate the ce_1 pulse + process (clr, clk) + begin + if (clr = '1') then + count16 <= (others => '0'); + elsif (rising_edge(clk)) then + if ((iTxBusy = '1') and (ce16 = '1')) then + count16 <= count16 + 1; + elsif (iTxBusy = '0') then + count16 <= (others => '0'); + end if; + end if; + end process; + -- tx_busy flag + process (clr, clk) + begin + if (clr = '1') then + iTxBusy <= '0'; + elsif (rising_edge(clk)) then + if ((iTxBusy = '0') and (newTxData = '1')) then + iTxBusy <= '1'; + elsif ((iTxBusy = '1') and (bitCount = "1001") and (ce1 = '1')) then + iTxBusy <= '0'; + end if; + end if; + end process; + -- output bit counter + process (clr, clk) + begin + if (clr = '1') then + bitCount <= (others => '0'); + elsif (rising_edge(clk)) then + if ((iTxBusy = '1') and (ce1 = '1')) then + bitCount <= bitCount + 1; + elsif (iTxBusy = '0') then + bitCount <= (others => '0'); + end if; + end if; + end process; + -- data shift register + process (clr, clk) + begin + if (clr = '1') then + dataBuf <= (others => '0'); + elsif (rising_edge(clk)) then + if (iTxBusy = '0') then + dataBuf <= txData & '0'; + elsif ((iTxBusy = '1') and (ce1 = '1')) then + dataBuf <= '1' & dataBuf(8 downto 1); + end if; + end if; + end process; + -- output data bit + process (clr, clk) + begin + if (clr = '1') then + serOut <= '1'; + elsif (rising_edge(clk)) then + if (iTxBusy = '1') then + serOut <= dataBuf(0); + else + serOut <= '1'; + end if; + end if; + end process; + -- ce_1 pulse indicating output data bit should be updated + ce1 <= '1' when ((count16 = "1111") and (ce16 = '1')) else '0'; + txBusy <= iTxBusy; + end Behavioral; From 6f57532239c2fdd24ed8984bc3fe3826c22d733d Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 21 Aug 2015 09:35:06 +0100 Subject: [PATCH 0927/2656] Issue #17: Test iteration with a mixed-language module --- .../test_iteration_mixedlang/Makefile | 32 +++++++++++++ .../test_iteration.py | 48 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 tests/test_cases/test_iteration_mixedlang/Makefile create mode 100644 tests/test_cases/test_iteration_mixedlang/test_iteration.py diff --git a/tests/test_cases/test_iteration_mixedlang/Makefile b/tests/test_cases/test_iteration_mixedlang/Makefile new file mode 100644 index 00000000..8e658638 --- /dev/null +++ b/tests/test_cases/test_iteration_mixedlang/Makefile @@ -0,0 +1,32 @@ +############################################################################### +# Copyright (c) 2015 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +PWD := $(shell pwd) +COCOTB := $(PWD)/../../.. + +include ../../designs/uart2bus/Makefile +MODULE = test_iteration diff --git a/tests/test_cases/test_iteration_mixedlang/test_iteration.py b/tests/test_cases/test_iteration_mixedlang/test_iteration.py new file mode 100644 index 00000000..f6dde18c --- /dev/null +++ b/tests/test_cases/test_iteration_mixedlang/test_iteration.py @@ -0,0 +1,48 @@ +''' Copyright (c) 2015 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +import logging + +import cocotb +from cocotb.triggers import Timer +from cocotb.result import TestError, TestFailure + +@cocotb.test() +def recursive_discovery(dut): + """ + Recursively discover every single object in the design + """ + tlog = logging.getLogger("cocotb.test") + yield Timer(100) + def dump_all_the_things(parent): + count = 0 + for thing in parent: + count += 1 + tlog.info("Found %s.%s (%s)", parent._name, thing._name, type(thing)) + count += dump_all_the_things(thing) + return count + total = dump_all_the_things(dut) + tlog.info("Found a total of %d things", count) + From d2104abe980204b544f743b2334f5bbb1c9129e2 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 21 Aug 2015 10:22:15 +0100 Subject: [PATCH 0928/2656] Issue #17: Enable VHPI and VPI for mixed language --- tests/designs/uart2bus/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/designs/uart2bus/Makefile b/tests/designs/uart2bus/Makefile index b9acbda7..6f7f6d27 100644 --- a/tests/designs/uart2bus/Makefile +++ b/tests/designs/uart2bus/Makefile @@ -20,6 +20,8 @@ VERILOG_SOURCES = $(SRC_BASE)/verilog/baud_gen.v \ ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES += $(SRC_BASE)/top/verilog_toplevel.v TOPLEVEL = verilog_toplevel + GPI_IMPL=vpi + GPI_EXTRA=vhpi else $(error "Only verilog toplevel supported at the moment") endif From f1f31cd8ffc330492c1ae0ce2a18e0d57a40a2f2 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 21 Aug 2015 10:22:40 +0100 Subject: [PATCH 0929/2656] Issue #17: Correct variable name in test --- tests/test_cases/test_iteration_mixedlang/test_iteration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/test_iteration_mixedlang/test_iteration.py b/tests/test_cases/test_iteration_mixedlang/test_iteration.py index f6dde18c..942dc3f4 100644 --- a/tests/test_cases/test_iteration_mixedlang/test_iteration.py +++ b/tests/test_cases/test_iteration_mixedlang/test_iteration.py @@ -44,5 +44,5 @@ def dump_all_the_things(parent): count += dump_all_the_things(thing) return count total = dump_all_the_things(dut) - tlog.info("Found a total of %d things", count) + tlog.info("Found a total of %d things", total) From 35758df48aff3ef97740d7b2eb21ae9a83164004 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 21 Aug 2015 14:11:13 +0100 Subject: [PATCH 0930/2656] Add Coverity to travis.yml --- .travis.yml | 15 +++++++++++++++ README.md | 1 + 2 files changed, 16 insertions(+) diff --git a/.travis.yml b/.travis.yml index 5da2384f..d47fd05f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,8 @@ +env: + global: + # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created + # via the "travis encrypt" command using the project repo's public key + - secure: "YTKZKkNH8t11bZCtR4nMjszifW+QlIAluDAthYq8I4EpuRSKQ1ntVi9MnDs6EQu8CD9LfFOeQ0wymRU71FOf22n54sCVQgbo8OcwHXa/3PUeWi9A/i+BqRZ7iCdeGFJH8rQaCWov6mCyVjjdie52kwWv3jy1ZaTeuUTX54EKFII=" language: python python: - "2.7" @@ -21,3 +26,13 @@ install: # - cd iverilog-master && autoconf && ./configure && make && sudo make install && cd .. script: - make test +addons: + coverity_scan: + project: + name: "potentialventures/cocotb" + description: "Python Co-simulation library for RTL verification" + notification_email: github@potentialventures.com +# build_command_prepend: "" + build_command: "make tests" + branch_pattern: coverity_scan + diff --git a/README.md b/README.md index 282075b4..7ddafe02 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Documentation Status](https://readthedocs.org/projects/cocotb/badge/?version=latest)](http://cocotb.readthedocs.org/en/latest/) [![Build Status](https://travis-ci.org/potentialventures/cocotb.svg?branch=master)](https://travis-ci.org/potentialventures/cocotb) +[![Coverity Status](https://scan.coverity.com/projects/6110/badge.svg)](https://scan.coverity.com/projects/cocotb) * Skim the introductory presentation: http://potential.ventures * Read the [documentation](http://cocotb.readthedocs.org) From ac57d478195c3bdf7efa966449179ef5ee10b80d Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 21 Aug 2015 14:23:37 +0100 Subject: [PATCH 0931/2656] Coverity doesn't like to make tests... --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index d47fd05f..bf2b0e82 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,7 +32,6 @@ addons: name: "potentialventures/cocotb" description: "Python Co-simulation library for RTL verification" notification_email: github@potentialventures.com -# build_command_prepend: "" - build_command: "make tests" + build_command: "make" branch_pattern: coverity_scan From 1e5486b5b9da324780c8f41b8a58567639a1effe Mon Sep 17 00:00:00 2001 From: Marek Cieplucha Date: Sat, 22 Aug 2015 16:54:21 +0200 Subject: [PATCH 0932/2656] fixed issue 291 --- makefiles/simulators/Makefile.questa | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index daef8bff..493d2e1d 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -31,6 +31,10 @@ ifdef VERILOG_INCLUDE_DIRS VLOG_ARGS += +incdir+$(VERILOG_INCLUDE_DIRS) endif +ifdef EXTRA_ARGS +VLOG_ARGS += $(EXTRA_ARGS) +endif + ifeq ($(GUI),1) SIM_CMD = vsim -gui VSIM_ARGS += -onfinish stop From 00110d0948a310eb73b3a7c743e80b5668aca798 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 23 Aug 2015 10:40:10 +0100 Subject: [PATCH 0933/2656] Issue #17: Various changes for vhpi iteration 1. Some VHPI implementations do not allow creation of and iterator of arbitary type on a handle. The architecture inheritance diagrams seem to specify what the allowed one-2-many relationships are for a specific handle type. We now have mapping of lists that can be used for each handle type 2. Rather than depond on the length of a BinaryValue to determine the number of children a handle has call a new function get_num_elems that can make an informed decision. --- cocotb/handle.py | 10 +- include/gpi.h | 3 + lib/gpi/GpiCbHdl.cpp | 6 + lib/gpi/GpiCommon.cpp | 6 + lib/gpi/gpi_priv.h | 1 + lib/simulator/simulatormodule.c | 21 ++++ lib/simulator/simulatormodule.h | 2 + lib/vhpi/VhpiCbHdl.cpp | 214 +++++++++++++++++++++++++------- lib/vhpi/VhpiImpl.cpp | 48 ++++++- lib/vhpi/VhpiImpl.h | 20 ++- lib/vpi/VpiCbHdl.cpp | 45 ++++--- lib/vpi/VpiImpl.cpp | 5 +- 12 files changed, 308 insertions(+), 73 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 6a0a0c0e..3581d226 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -286,9 +286,10 @@ def __str__(self): def __getitem__(self, index): if index in self._sub_handles: return self._sub_handles[index] + self._log.info("Calling get handle for %s" % self._name) new_handle = simulator.get_handle_by_index(self._handle, index) if not new_handle: - self._raise_testerror("%s contains no object at index %d" % (self._name, index)) + self._raise_testerror("%s %s contains no object at index %d" % (self._name, simulator.get_type(self._handle), index)) self._sub_handles[index] = SimHandle(new_handle) return self._sub_handles[index] @@ -296,7 +297,10 @@ def __iter__(self): if len(self) == 1: raise StopIteration for i in range(len(self)): - yield self[i] + try: + yield self[i] + except: + continue def _getvalue(self): result = BinaryValue() @@ -317,7 +321,7 @@ def __len__(self): TODO: Handle other types (loops, generate etc) """ if self._len is None: - self._len = len(self._get_value_str()) + self._len = simulator.get_num_elems(self._handle) return self._len diff --git a/include/gpi.h b/include/gpi.h index 5988c27e..0d5669d0 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -147,6 +147,9 @@ gpi_iterator_hdl gpi_iterate(gpi_sim_hdl base); // Returns NULL when there are no more objects gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator); +// Returns the number of objects in the collection of the handle +int gpi_get_num_elems(gpi_sim_hdl gpi_sim_hdl); + // Functions for querying the properties of a handle // Caller responsible for freeing the returned string. // This is all slightly verbose but it saves having to enumerate various value types diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index 5ee35496..d9b8d932 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -74,6 +74,12 @@ const std::string & GpiObjHdl::get_name(void) return m_name; } +int GpiObjHdl::get_num_elems(void) +{ + LOG_WARN("Generic get_num_elems, doubtful this is correct"); + return 0; +} + /* Genertic base clss implementations */ char *GpiHdl::gpi_copy_name(const char *name) { diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index a1b6f540..11e25415 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -313,6 +313,12 @@ void gpi_set_signal_value_real(gpi_sim_hdl sig_hdl, double value) obj_hdl->set_signal_value(value); } +int gpi_get_num_elems(gpi_sim_hdl sig_hdl) +{ + GpiObjHdl *obj_hdl = sim_to_hdl(sig_hdl); + return obj_hdl->get_num_elems(); +} + gpi_sim_hdl gpi_register_value_change_callback(int (*gpi_function)(const void *), void *gpi_cb_data, gpi_sim_hdl sig_hdl, diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 7489c0a6..c46b1d28 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -108,6 +108,7 @@ class GpiObjHdl : public GpiHdl { virtual const char* get_fullname_str(void); virtual const char* get_type_str(void); virtual gpi_objtype_t get_type(void); + virtual int get_num_elems(void); const std::string & get_name(void); diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index f5ae128b..6e19a084 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -822,6 +822,27 @@ static PyObject *get_sim_time(PyObject *self, PyObject *args) return pTuple; } +static PyObject *get_num_elems(PyObject *self, PyObject *args) +{ + gpi_sim_hdl hdl; + PyObject *retstr; + + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + if (!PyArg_ParseTuple(args, "l", &hdl)) { + DROP_GIL(gstate); + return NULL; + } + + int elems = gpi_get_num_elems((gpi_sim_hdl)hdl); + retstr = Py_BuildValue("i", elems); + + DROP_GIL(gstate); + + return retstr; +} + static PyObject *stop_simulator(PyObject *self, PyObject *args) { gpi_sim_end(); diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 37af9b50..93b278b1 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -69,6 +69,7 @@ static PyObject *get_root_handle(PyObject *self, PyObject *args); static PyObject *get_name_string(PyObject *self, PyObject *args); static PyObject *get_type(PyObject *self, PyObject *args); static PyObject *get_type_string(PyObject *self, PyObject *args); +static PyObject *get_num_elems(PyObject *self, PyObject *args); static PyObject *register_timed_callback(PyObject *self, PyObject *args); static PyObject *register_value_change_callback(PyObject *self, PyObject *args); static PyObject *register_readonly_callback(PyObject *self, PyObject *args); @@ -96,6 +97,7 @@ static PyMethodDef SimulatorMethods[] = { {"get_name_string", get_name_string, METH_VARARGS, "Get the name of an object as a string"}, {"get_type_string", get_type_string, METH_VARARGS, "Get the type of an object as a string"}, {"get_type", get_type, METH_VARARGS, "Get the type of an object, mapped to a GPI enumeration"}, + {"get_num_elems", get_num_elems, METH_VARARGS, "Get the number of elements contained in the handle"}, {"register_timed_callback", register_timed_callback, METH_VARARGS, "Register a timed callback"}, {"register_value_change_callback", register_value_change_callback, METH_VARARGS, "Register a signal change callback"}, {"register_readonly_callback", register_readonly_callback, METH_VARARGS, "Register a callback for readonly section"}, diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 79cee0e9..7f14bc33 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -45,21 +45,29 @@ int VhpiSignalObjHdl::initialise(std::string &name) { m_value.format = vhpiObjTypeVal; m_value.bufSize = 0; m_value.value.str = NULL; + m_value.numElems = 0; /* We also alloc a second value member for use with read string operations */ m_binvalue.format = vhpiBinStrVal; m_binvalue.bufSize = 0; m_binvalue.numElems = 0; m_binvalue.value.str = NULL; - vhpi_get_value(GpiObjHdl::get_handle(), &m_value); + vhpi_get_value(get_handle(), &m_value); check_vhpi_error(); + LOG_DEBUG("Found %s of format type %s (%d) format object with %d elems buffsize %d size %d", + name.c_str(), + ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format), + m_value.format, + m_value.numElems, + m_value.bufSize, + vhpi_get(vhpiSizeP, GpiObjHdl::get_handle())); + switch (m_value.format) { case vhpiEnumVal: - case vhpiLogicVal: { + case vhpiLogicVal: m_value.value.enumv = vhpi0; break; - } case vhpiRealVal: { GpiObjHdl::initialise(name); @@ -70,13 +78,14 @@ int VhpiSignalObjHdl::initialise(std::string &name) { case vhpiEnumVecVal: case vhpiLogicVecVal: { m_size = vhpi_get(vhpiSizeP, GpiObjHdl::get_handle()); - m_value.bufSize = m_size*sizeof(vhpiEnumT); + m_value.bufSize = m_size*sizeof(vhpiEnumT); m_value.value.enumvs = (vhpiEnumT *)malloc(m_value.bufSize); if (!m_value.value.enumvs) { LOG_CRITICAL("Unable to alloc mem for write buffer"); } + GpiObjHdl::initialise(name); - break; + return 0; } case vhpiRawDataVal: { GpiObjHdl::initialise(name); @@ -91,14 +100,14 @@ int VhpiSignalObjHdl::initialise(std::string &name) { int new_size = vhpi_get_value(GpiObjHdl::get_handle(), &m_binvalue); if (new_size < 0) { - LOG_CRITICAL("Failed to determine size of signal object %s", name.c_str()); + LOG_CRITICAL("Failed to determine size of signal object %s of type %s", + name.c_str(), + ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); goto out; } if (new_size) { - m_binvalue.bufSize = new_size*sizeof(vhpiCharT); - - LOG_DEBUG("Going to alloc %d\n", m_binvalue.bufSize); + m_binvalue.bufSize = new_size*sizeof(vhpiCharT) + 1; m_binvalue.value.str = (vhpiCharT *)calloc(m_binvalue.bufSize, sizeof(vhpiCharT)); if (!m_value.value.str) { @@ -328,15 +337,25 @@ int VhpiSignalObjHdl::set_signal_value(std::string &value) const char* VhpiSignalObjHdl::get_signal_value_binstr(void) { - int ret = vhpi_get_value(GpiObjHdl::get_handle(), &m_binvalue); - if (ret) { - check_vhpi_error(); - LOG_ERROR("Size of m_binvalue.value.str was not large enough req=%d have=%d", - ret, - m_binvalue.bufSize); - } + switch (m_value.format) { + case vhpiEnumVecVal: + case vhpiLogicVecVal: + LOG_DEBUG("get_signal_value_binstr not supported for %s", + ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); + return ""; + default: { + int ret = vhpi_get_value(GpiObjHdl::get_handle(), &m_binvalue); + if (ret) { + check_vhpi_error(); + LOG_ERROR("Size of m_binvalue.value.str was not large enough req=%d have=%d for type %s", + ret, + m_binvalue.bufSize, + ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); + } - return m_binvalue.value.str; + return m_binvalue.value.str; + } + } } @@ -366,6 +385,27 @@ long VhpiSignalObjHdl::get_signal_value_long(void) return value.value.intg; } +int VhpiSignalObjHdl::get_num_elems(void) +{ + switch (m_value.format) { + case vhpiEnumVal: + case vhpiLogicVal: + return m_value.bufSize; + case vhpiRealVal: + case vhpiIntVal: + case vhpiRawDataVal: + return 1; + case vhpiEnumVecVal: + case vhpiLogicVecVal: + return m_value.numElems; + break; + default: + LOG_ERROR("Not sure %s", + ((VhpiImpl*)m_impl)->format_to_string(m_value.format)); + return 0; + } +} + GpiCbHdl * VhpiSignalObjHdl::value_change_cb(unsigned int edge) { @@ -478,16 +518,96 @@ VhpiNextPhaseCbHdl::VhpiNextPhaseCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), cb_data.time = &vhpi_time; } -static vhpiOneToManyT options[] = { - vhpiInternalRegions, - vhpiSigDecls, - vhpiVarDecls, - vhpiPortDecls, - vhpiGenericDecls, - vhpiCompInstStmts -}; +KindMappings::KindMappings() +{ +// std::vector option(root_options, root_options + sizeof(root_options) / sizeof(root_options[0])); + + /* vhpiRootInstK */ + vhpiOneToManyT root_options[] = { + vhpiInternalRegions, + vhpiSigDecls, + vhpiVarDecls, + vhpiPortDecls, + vhpiGenericDecls, + // vhpiIndexedNames, + vhpiCompInstStmts, + vhpiBlockStmts, + (vhpiOneToManyT)0, + }; + add_to_options(vhpiRootInstK, &root_options[0]); + + /* vhpiSigDeclK */ + vhpiOneToManyT sig_options[] = { + vhpiIndexedNames, + vhpiSelectedNames, + (vhpiOneToManyT)0, + }; + add_to_options(vhpiGenericDeclK, &sig_options[0]); + add_to_options(vhpiSigDeclK, &sig_options[0]); + + /* vhpiIndexedNameK */ + add_to_options(vhpiSelectedNameK, &sig_options[0]); + add_to_options(vhpiIndexedNameK, &sig_options[0]); + + /* vhpiCompInstStmtK */ + add_to_options(vhpiCompInstStmtK, &root_options[0]); + + /* vhpiSimpleSigAssignStmtK */ + vhpiOneToManyT simplesig_options[] = { + vhpiDecls, + vhpiInternalRegions, + vhpiSensitivitys, + vhpiStmts, + (vhpiOneToManyT)0, + }; + add_to_options(vhpiCondSigAssignStmtK, &simplesig_options[0]); + add_to_options(vhpiSimpleSigAssignStmtK, &simplesig_options[0]); + + /* vhpiPortDeclK */ + add_to_options(vhpiPortDeclK, &sig_options[0]); + + /* vhpiForGenerateK */ + vhpiOneToManyT gen_options[] = { + vhpiDecls, + vhpiCompInstStmts, + (vhpiOneToManyT)0, + }; + add_to_options(vhpiForGenerateK, &gen_options[0]); + + /* vhpiIfGenerateK */ + vhpiOneToManyT ifgen_options[] = { + vhpiDecls, + vhpiInternalRegions, + vhpiCompInstStmts, + (vhpiOneToManyT)0, + }; + add_to_options(vhpiIfGenerateK, &ifgen_options[0]); +} -std::vector VhpiIterator::iterate_over(options, options + sizeof(options) / sizeof(options[0])); +void KindMappings::add_to_options(vhpiClassKindT type, vhpiOneToManyT *options) +{ + std::vector option_vec; + vhpiOneToManyT *ptr = options; + while (*ptr) { + option_vec.push_back(*ptr); + ptr++; + } + options_map[type] = option_vec; +} + +std::vector* KindMappings::get_options(vhpiClassKindT type) +{ + std::map >::iterator valid = options_map.find(type); + + if (options_map.end() == valid) { + LOG_ERROR("VHPI: Implementation does not know how to iterate over %d", type); + exit(1); + } else { + return &valid->second; + } +} + +KindMappings VhpiIterator::iterate_over; VhpiIterator::VhpiIterator(GpiImplInterface *impl, vhpiHandleT hdl) : GpiIterator(impl, hdl), m_iterator(NULL), @@ -495,20 +615,27 @@ VhpiIterator::VhpiIterator(GpiImplInterface *impl, vhpiHandleT hdl) : GpiIterato { vhpiHandleT iterator; + selected = iterate_over.get_options((vhpiClassKindT)vhpi_get(vhpiKindP, hdl)); + /* Find the first mapping type that yields a valid iterator */ - for (curr_type = iterate_over.begin(); - curr_type != iterate_over.end(); - curr_type++) { - iterator = vhpi_iterator(*curr_type, hdl); + for (one2many = selected->begin(); + one2many != selected->end(); + one2many++) { + iterator = vhpi_iterator(*one2many, hdl); if (iterator) break; - LOG_WARN("vhpi_iterate vhpiOneToManyT=%d returned NULL", *curr_type); + LOG_DEBUG("vhpi_iterate vhpiOneToManyT=%d returned NULL", *one2many); } if (NULL == iterator) { - LOG_WARN("vhpi_iterate returned NULL for all relationships"); + std::string name = vhpi_get_str(vhpiCaseNameP, hdl); + LOG_WARN("vhpi_iterate return NULL for all relationships on %s (%d) kind:%s name:%s", name.c_str(), + vhpi_get(vhpiKindP, hdl), + vhpi_get_str(vhpiKindStrP, hdl), + vhpi_get_str(vhpiCaseNameP, hdl)); + m_iterator = NULL; return; } @@ -527,12 +654,12 @@ VhpiIterator::VhpiIterator(GpiImplInterface *impl, vhpiHandleT hdl) : GpiIterato for(tmp_hdl = vhpi_scan(iterator); tmp_hdl && (children <= 1); tmp_hdl = vhpi_scan(iterator), children++) { } vhpi_release_handle(iterator); - iterator = vhpi_iterator(*curr_type, hdl); + iterator = vhpi_iterator(*one2many, hdl); if (children == 1) { vhpiHandleT root_iterator; tmp_hdl = vhpi_scan(iterator); - root_iterator = vhpi_iterator(*curr_type, tmp_hdl); + root_iterator = vhpi_iterator(*one2many, tmp_hdl); vhpi_release_handle(iterator); iterator = root_iterator; LOG_WARN("Skipped vhpiRootInstK to get to %s", vhpi_get_str(vhpiKindStrP, tmp_hdl)); @@ -558,7 +685,6 @@ GpiObjHdl *VhpiIterator::next_handle(void) * If the end of mapping is reached then we want to * try then next one until a new object is found */ - do { obj = NULL; @@ -574,16 +700,18 @@ GpiObjHdl *VhpiIterator::next_handle(void) if (obj) continue; - LOG_DEBUG("End of vhpiOneToManyT=%d iteration", *curr_type); + LOG_DEBUG("End of vhpiOneToManyT=%d iteration", *one2many); vhpi_release_handle(m_iterator); m_iterator = NULL; } else { - LOG_DEBUG("No valid vhpiOneToManyT=%d iterator", *curr_type); + LOG_DEBUG("No valid vhpiOneToManyT=%d iterator", *one2many); } - if (++curr_type == iterate_over.end()) + if (++one2many >= selected->end()) { + obj = NULL; break; - m_iterator = vhpi_iterator(*curr_type, m_iter_obj); + } + m_iterator = vhpi_iterator(*one2many, m_iter_obj); } while (!obj); @@ -593,13 +721,15 @@ GpiObjHdl *VhpiIterator::next_handle(void) } std::string name = vhpi_get_str(vhpiCaseNameP, obj); + LOG_DEBUG("vhpi_scan found %s (%d) kind:%s name:%s", name.c_str(), - vhpi_get(vhpiKindP, obj), - vhpi_get_str(vhpiKindStrP, obj), - vhpi_get_str(vhpiCaseNameP, obj)); + vhpi_get(vhpiKindP, obj), + vhpi_get_str(vhpiKindStrP, obj), + vhpi_get_str(vhpiCaseNameP, obj)); VhpiImpl *vhpi_impl = reinterpret_cast(m_impl); new_obj = vhpi_impl->create_gpi_obj_from_handle(obj, name); + return new_obj; } diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 83fd366f..b222925e 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -67,6 +67,8 @@ const char * VhpiImpl::format_to_string(int format) return "vhpiPtrVal"; case vhpiEnumVecVal: return "vhpiEnumVecVal"; + case vhpiRawDataVal: + return "vhpiRawDataVal"; default: return "unknown"; @@ -117,6 +119,7 @@ gpi_objtype_t to_gpi_objtype(vhpiIntT vhpitype) case vhpiPortDeclK: case vhpiSigDeclK: case vhpiIndexedNameK: + case vhpiSelectedNameK: case vhpiVarDeclK: case vhpiVarParamDeclK: return GPI_REGISTER; @@ -127,6 +130,7 @@ gpi_objtype_t to_gpi_objtype(vhpiIntT vhpitype) case vhpiEnumLiteralK: return GPI_ENUM; + case vhpiConstDeclK: case vhpiGenericDeclK: return GPI_PARAMETER; @@ -140,6 +144,7 @@ gpi_objtype_t to_gpi_objtype(vhpiIntT vhpitype) case vhpiRootInstK: case vhpiProcessStmtK: case vhpiSimpleSigAssignStmtK: + case vhpiCondSigAssignStmtK: return GPI_MODULE; default: @@ -162,11 +167,15 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string } gpi_type = to_gpi_objtype(type); + LOG_DEBUG("Creating %s of type %d (%s)", vhpi_get_str(vhpiFullNameP, new_hdl), gpi_type, vhpi_get_str(vhpiKindStrP, new_hdl)); /* What sort of isntance is this ?*/ switch (type) { case vhpiPortDeclK: case vhpiSigDeclK: + case vhpiConstDeclK: + case vhpiGenericDeclK: + case vhpiSelectedNameK: case vhpiIndexedNameK: { // Sadly VHPI doesn't have a "Real" type returned - we just get // vhpiPortDeclK rather than the signal type. @@ -181,14 +190,30 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string vhpi_get_value(new_hdl, &value); if (vhpiRealVal == value.format) { - LOG_DEBUG("Detected a REAL type", name.c_str()); + LOG_DEBUG("Detected a REAL type %s", name.c_str()); gpi_type = GPI_REAL; } if (vhpiIntVal == value.format) { - LOG_DEBUG("Detected an INT type", name.c_str()); + LOG_DEBUG("Detected an INT type %s", name.c_str()); gpi_type = GPI_ENUM; + + } + if (vhpiRawDataVal == value.format) { + LOG_DEBUG("Detected a custom array type %s", name.c_str()); + gpi_type = GPI_MODULE; + } + + if (vhpiIntVecVal == value.format || + vhpiRealVecVal == value.format || + vhpiEnumVecVal == value.format || + vhpiLogicVecVal == value.format || + vhpiPhysVecVal == value.format || + vhpiTimeVecVal == value.format) { + LOG_DEBUG("Detected a vector type", name.c_str()); + gpi_type = GPI_MODULE; } + new_obj = new VhpiSignalObjHdl(this, new_hdl, gpi_type); break; } @@ -197,6 +222,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string case vhpiCompInstStmtK: case vhpiProcessStmtK: case vhpiSimpleSigAssignStmtK: + case vhpiCondSigAssignStmtK: new_obj = new GpiObjHdl(this, new_hdl, gpi_type); break; default: @@ -241,13 +267,23 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) GpiObjHdl *VhpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) { GpiObjHdl *parent_hdl = sim_to_hdl(parent); - vhpiHandleT vpi_hdl = parent_hdl->get_handle(); + vhpiHandleT vhpi_hdl = parent_hdl->get_handle(); vhpiHandleT new_hdl; - new_hdl = vhpi_handle_by_index(vhpiIndexedNames, vpi_hdl, index); + LOG_DEBUG("Native check create for index %u of parent %s (%s)", + index, + vhpi_get_str(vhpiNameP, vhpi_hdl), + vhpi_get_str(vhpiKindStrP, vhpi_hdl)); + + if (vhpiIndexedNameK == vhpi_get(vhpiKindP, vhpi_hdl)) { + LOG_DEBUG("Can't index into vhpiIndexedNameK, have to use an iterator?"); + return NULL; + } + + new_hdl = vhpi_handle_by_index(vhpiIndexedNames, vhpi_hdl, index); if (new_hdl == NULL) { - LOG_DEBUG("Unable to query vhpi_handle_by_index %s", index); + LOG_DEBUG("Unable to query vhpi_handle_by_index %u", index); return NULL; } @@ -255,7 +291,7 @@ GpiObjHdl *VhpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name); if (new_obj == NULL) { vhpi_release_handle(new_hdl); - LOG_DEBUG("Could not fetch object below entity (%s) at index (%d)", + LOG_DEBUG("Could not fetch object below entity (%s) at index (%u)", parent->get_name_str(), index); return NULL; } diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 2ca975ee..a1b75036 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -31,6 +31,7 @@ #include "../gpi/gpi_priv.h" #include #include +#include // Should be run after every VHPI call to check error status static inline int __check_vhpi_error(const char *file, const char *func, long line) @@ -171,6 +172,8 @@ class VhpiSignalObjHdl : public GpiSignalObjHdl { GpiCbHdl *value_change_cb(unsigned int edge); int initialise(std::string &name); + int get_num_elems(void); + private: const vhpiEnumT chr2vhpi(const char value); unsigned int m_size; @@ -181,6 +184,18 @@ class VhpiSignalObjHdl : public GpiSignalObjHdl { VhpiValueCbHdl m_either_cb; }; +class KindMappings { +public: + KindMappings(); + +public: + std::map > options_map; + std::vector* get_options(vhpiClassKindT type); + +private: + void add_to_options(vhpiClassKindT type, vhpiOneToManyT *options); +}; + class VhpiIterator : public GpiIterator { public: VhpiIterator(GpiImplInterface *impl, vhpiHandleT hdl); @@ -192,8 +207,9 @@ class VhpiIterator : public GpiIterator { private: vhpiHandleT m_iterator; vhpiHandleT m_iter_obj; - static std::vector iterate_over; - std::vector::iterator curr_type; + static KindMappings iterate_over; /* Possible mappings */ + std::vector *selected; /* Mapping currently in use */ + std::vector::iterator one2many; }; class VhpiImpl : public GpiImplInterface { diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 6f879ca0..d74029a6 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -218,6 +218,11 @@ int VpiSignalObjHdl::set_signal_value(std::string &value) return 0; } +int VpiSignalObjHdl::get_num_elems(void) +{ + return 1; +} + GpiCbHdl * VpiSignalObjHdl::value_change_cb(unsigned int edge) { VpiValueCbHdl *cb = NULL; @@ -359,22 +364,17 @@ VpiNextPhaseCbHdl::VpiNextPhaseCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), static int32_t options[] = { vpiNet, - vpiNetBit, vpiReg, - vpiRegBit, vpiParameter, vpiRegArray, - vpiNetArray, - vpiEnumNet, - vpiEnumVar, - vpiIntVar, - vpiStructVar, - vpiModule, - vpiInterface, - vpiModport, - vpiInterfaceArray, - vpiRefObj, - vpiPackedArrayVar, + /* Calling any of there below will cause a SEGV, even on their own + when run under a mixed language verilog toplevel. On another verilog design + only this is not the case + */ + //vpiNetArray, + //vpiModule, + //vpiInterface, + //vpiInterfaceArray }; std::vector VpiIterator::iterate_over(options, options + (sizeof(options) / sizeof(options[0]))); @@ -389,10 +389,11 @@ VpiIterator::VpiIterator(GpiImplInterface *impl, vpiHandle hdl) : GpiIterator(im curr_type++) { iterator = vpi_iterate(*curr_type, hdl); - if (iterator) + if (iterator) { break; + } - LOG_WARN("vpi_iterate type=%d returned NULL", *curr_type); + LOG_DEBUG("vpi_iterate type=%d returned NULL", *curr_type); } if (NULL == iterator) { @@ -411,6 +412,8 @@ VpiIterator::~VpiIterator() { if (m_iterator) vpi_free_object(m_iterator); + + LOG_DEBUG("Deleted VpiIterator"); } GpiObjHdl *VpiIterator::next_handle(void) @@ -428,7 +431,7 @@ GpiObjHdl *VpiIterator::next_handle(void) /* m_iterator will already be free'd internally here */ m_iterator = NULL; } else { - continue; + break; } LOG_DEBUG("End of type=%d iteration", *curr_type); @@ -436,9 +439,12 @@ GpiObjHdl *VpiIterator::next_handle(void) LOG_DEBUG("No valid type=%d iterator", *curr_type); } - if (curr_type++ == iterate_over.end()) + if (++curr_type >= iterate_over.end()) { + obj = NULL; break; - m_iterator = vpi_iterate(*curr_type, get_handle()); + } + + m_iterator = vpi_iterate(*curr_type, hdl); } while (!obj); @@ -448,6 +454,9 @@ GpiObjHdl *VpiIterator::next_handle(void) } std::string name = vpi_get_str(vpiFullName, obj); + + LOG_DEBUG("vhpi_scan found name:%s", name.c_str()); + VpiImpl *vpi_impl = reinterpret_cast(m_impl); new_obj = vpi_impl->create_gpi_obj_from_handle(obj, name); return new_obj; diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 3ac043e0..ded7213a 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -187,14 +187,15 @@ GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) new_hdl = vpi_handle_by_index(vpi_hdl, index); if (new_hdl == NULL) { - LOG_DEBUG("Unable to vpi_get_handle_by_index %d", index); + LOG_DEBUG("Unable to vpi_get_handle_by_index %u", index); return NULL; } + std::string name = vpi_get_str(vpiFullName, new_hdl); GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name); if (new_obj == NULL) { vpi_free_object(new_hdl); - LOG_ERROR("Unable to fetch object below entity (%s) at index (%d)", + LOG_ERROR("Unable to fetch object below entity (%s) at index (%u)", parent->get_name_str(), index); return NULL; } From b0566e510e5fb31e78a2b30e5179f166eeb83c1f Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 23 Aug 2015 10:55:56 +0100 Subject: [PATCH 0934/2656] Issue #17: Fixup some merge problems --- lib/vpi/VpiCbHdl.cpp | 2 +- lib/vpi/VpiImpl.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index d74029a6..0f5a47db 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -444,7 +444,7 @@ GpiObjHdl *VpiIterator::next_handle(void) break; } - m_iterator = vpi_iterate(*curr_type, hdl); + m_iterator = vpi_iterate(*curr_type, obj); } while (!obj); diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 8f3a25d7..56671dad 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -188,6 +188,7 @@ class VpiSignalObjHdl : public GpiSignalObjHdl { int set_signal_value(const long value); int set_signal_value(const double value); int set_signal_value(std::string &value); + int get_num_elems(void); /* Value change callback accessor */ GpiCbHdl *value_change_cb(unsigned int edge); From a6f1e37df0b59d96ab96798b3ec336a318ff99e6 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 23 Aug 2015 11:42:45 +0100 Subject: [PATCH 0935/2656] Issue #17: Fix up Icarus compilation problems on integers --- tests/designs/sample_module/sample_module.v | 14 +++++++------- tests/test_cases/issue_134/test_integers.py | 1 + 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/designs/sample_module/sample_module.v b/tests/designs/sample_module/sample_module.v index 2ca2ed64..86de625b 100644 --- a/tests/designs/sample_module/sample_module.v +++ b/tests/designs/sample_module/sample_module.v @@ -36,31 +36,28 @@ module sample_module ( input stream_in_valid, `ifndef __ICARUS__ input real stream_in_real, + input integer stream_in_int, `endif input [7:0] stream_in_data, input [63:0] stream_in_data_wide, - input integer stream_in_int, input stream_out_ready, `ifndef __ICARUS__ output real stream_out_real, + output integer stream_out_int, `endif output reg [7:0] stream_out_data_comb, - output reg [7:0] stream_out_data_registered, - output integer stream_out_int + output reg [7:0] stream_out_data_registered ); always @(posedge clk) stream_out_data_registered <= stream_in_data; -always @(stream_in_int) - stream_out_int <= stream_in_int; - always @(stream_in_data) stream_out_data_comb = stream_in_data; always @(stream_in_data) - stream_out_int = stream_in_int; + stream_out_data_comb = stream_in_data; always @(stream_out_ready) stream_in_ready = stream_out_ready; @@ -68,6 +65,9 @@ always @(stream_out_ready) `ifndef __ICARUS__ always @(stream_in_real) stream_out_real = stream_in_real; + +always @(stream_in_int) + stream_out_int <= stream_in_int; `endif initial begin diff --git a/tests/test_cases/issue_134/test_integers.py b/tests/test_cases/issue_134/test_integers.py index b63610ae..a51e0f99 100644 --- a/tests/test_cases/issue_134/test_integers.py +++ b/tests/test_cases/issue_134/test_integers.py @@ -9,6 +9,7 @@ from cocotb.binary import BinaryValue +@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) @cocotb.test() def test_integer(dut): """ From 6807c9210a1d0b7a45fdcc1d3ab12ccc73499463 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 23 Aug 2015 20:20:35 +0100 Subject: [PATCH 0936/2656] Issue #17: Some fix-ups for VHPI discovery and sizing (in progress) --- cocotb/handle.py | 24 +++++++++++++---- include/vpi_user.h | 1 + lib/gpi/GpiCbHdl.cpp | 6 ----- lib/gpi/gpi_priv.h | 6 ++++- lib/vhpi/VhpiCbHdl.cpp | 61 +++++++++++++++++++----------------------- lib/vhpi/VhpiImpl.cpp | 2 +- lib/vhpi/VhpiImpl.h | 4 --- lib/vpi/VpiCbHdl.cpp | 11 ++++---- lib/vpi/VpiImpl.cpp | 1 - lib/vpi/VpiImpl.h | 2 +- 10 files changed, 60 insertions(+), 58 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 3581d226..37acee44 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -192,10 +192,15 @@ def __iter__(self): for name, handle in self._sub_handles.items(): if isinstance(handle, list): - for subhdl in handle: + self._log.debug("Found index list length %d" % len(handle)) + for subindex, subhdl in enumerate(handle): + if subhdl is None: + self._log.warning("Index %d doesn't exist in %s.%s", subindex, self._name, name) + continue + self._log.debug("Yielding index %d from %s (%s)" % (subindex, name, type(subhdl))) yield subhdl - else: + self._log.debug("Yielding %s (%s)" % (name, handle)) yield handle def _discover_all(self): @@ -206,7 +211,7 @@ def _discover_all(self): do this once. """ if self._discovered: return - + self._log.warning("Discovering all on %s", self._name) iterator = simulator.iterate(self._handle) while True: try: @@ -226,7 +231,7 @@ def _discover_all(self): import re result = re.match("(?P.*)__(?P\d+)$", name) if not result: - result = re.match("(?P.*)\((?P\d+)", name) + result = re.match("(?P.*)\((?P\d+)\)$", name) if result: index = int(result.group("index")) @@ -234,11 +239,16 @@ def _discover_all(self): if name not in self._sub_handles: self._sub_handles[name] = [] + self._log.debug("creating new group for %s", name) if len(self._sub_handles[name]) < index + 1: delta = index - len(self._sub_handles[name]) + 1 self._sub_handles[name].extend([None]*delta) self._sub_handles[name][index] = hdl + self._log.debug("%s.%s[%d] is now %s", self._name, name, index, hdl._name) + for something in self._sub_handles[name]: + self._log.debug("%s: %s" % (type(something), something)) else: + self._log.debug("%s didn't match an index pattern", name) self._sub_handles[hdl._name.split(".")[-1]] = hdl self._discovered = True @@ -296,9 +306,12 @@ def __getitem__(self, index): def __iter__(self): if len(self) == 1: raise StopIteration + self._log.debug("Iterating with length %d" % len(self)) for i in range(len(self)): try: - yield self[i] + result = self[i] + self._log.warning("Yielding %s" % result) + yield result except: continue @@ -489,6 +502,7 @@ def SimHandle(handle): _type2cls = { simulator.MODULE: HierarchyObject, simulator.REG: ModifiableObject, + simulator.NETARRAY: ModifiableObject, simulator.REAL: RealObject, simulator.ENUM: IntegerObject } diff --git a/include/vpi_user.h b/include/vpi_user.h index 6038729d..f964a554 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -82,6 +82,7 @@ typedef uint32_t *vpiHandle; #define vpiType 1 /* type of object */ #define vpiName 2 /* local name of object */ #define vpiFullName 3 /* full hierarchical name */ +#define vpiSize 4 /* size of gate, net, port, etc. */ #define vpiNoDelay 1 #define vpiInertialDelay 2 diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index d9b8d932..5ee35496 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -74,12 +74,6 @@ const std::string & GpiObjHdl::get_name(void) return m_name; } -int GpiObjHdl::get_num_elems(void) -{ - LOG_WARN("Generic get_num_elems, doubtful this is correct"); - return 0; -} - /* Genertic base clss implementations */ char *GpiHdl::gpi_copy_name(const char *name) { diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index c46b1d28..f40d2d93 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -93,6 +93,7 @@ class GpiHdl { class GpiObjHdl : public GpiHdl { public: GpiObjHdl(std::string name) : GpiHdl(NULL, NULL), + m_num_elems(0), m_name(name), m_fullname("unknown") { } GpiObjHdl(GpiImplInterface *impl) : GpiHdl(impl, NULL), @@ -108,7 +109,9 @@ class GpiObjHdl : public GpiHdl { virtual const char* get_fullname_str(void); virtual const char* get_type_str(void); virtual gpi_objtype_t get_type(void); - virtual int get_num_elems(void); + int get_num_elems(void) { + LOG_DEBUG("%s has %d elements", m_name.c_str(), m_num_elems); + return m_num_elems; } const std::string & get_name(void); @@ -116,6 +119,7 @@ class GpiObjHdl : public GpiHdl { virtual int initialise(std::string &name); protected: + int m_num_elems; std::string m_name; std::string m_fullname; gpi_objtype_t m_type; diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 7f14bc33..f6f73778 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -63,6 +63,9 @@ int VhpiSignalObjHdl::initialise(std::string &name) { m_value.bufSize, vhpi_get(vhpiSizeP, GpiObjHdl::get_handle())); + // Default - overridden below in certain special cases + m_num_elems = m_value.numElems; + switch (m_value.format) { case vhpiEnumVal: case vhpiLogicVal: @@ -77,17 +80,30 @@ int VhpiSignalObjHdl::initialise(std::string &name) { case vhpiIntVal: case vhpiEnumVecVal: case vhpiLogicVecVal: { - m_size = vhpi_get(vhpiSizeP, GpiObjHdl::get_handle()); - m_value.bufSize = m_size*sizeof(vhpiEnumT); + m_num_elems = vhpi_get(vhpiSizeP, GpiObjHdl::get_handle()); + m_value.bufSize = m_num_elems*sizeof(vhpiEnumT); m_value.value.enumvs = (vhpiEnumT *)malloc(m_value.bufSize); if (!m_value.value.enumvs) { LOG_CRITICAL("Unable to alloc mem for write buffer"); } + LOG_DEBUG("Overriding num_elems to %d", m_num_elems); GpiObjHdl::initialise(name); return 0; } case vhpiRawDataVal: { + // This is an internal representation - the only way to determine + // the size is to iterate over the members and count sub-elements + vhpiHandleT result = NULL; + vhpiHandleT iterator = vhpi_iterator(vhpiIndexedNames, + GpiObjHdl::get_handle()); + while (true) { + result = vhpi_scan(iterator); + if (NULL == result) + break; + m_num_elems++; + } + LOG_DEBUG("Found vhpiRawDataVal with %d elements", m_num_elems); GpiObjHdl::initialise(name); return 0; } @@ -117,9 +133,7 @@ int VhpiSignalObjHdl::initialise(std::string &name) { } out: - GpiObjHdl::initialise(name); - - return 0; + return GpiObjHdl::initialise(name); } VhpiCbHdl::VhpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl) @@ -230,9 +244,9 @@ int VhpiSignalObjHdl::set_signal_value(long value) case vhpiEnumVecVal: case vhpiLogicVecVal: { - unsigned int i; - for (i=0; i m_size) { + if (len > m_num_elems) { LOG_ERROR("VHPI: Attempt to write string longer than signal %d > %d", - len, m_size); + len, m_num_elems); return -1; } std::string::iterator iter; - unsigned int i = 0; + int i = 0; for (iter = value.begin(); iter != value.end(); iter++, i++) { @@ -312,7 +326,7 @@ int VhpiSignalObjHdl::set_signal_value(std::string &value) } // Fill bits at the end of the value to 0's - for (i = len; i < m_size; i++) { + for (i = len; i < m_num_elems; i++) { m_value.value.enumvs[i] = vhpi0; } @@ -385,27 +399,6 @@ long VhpiSignalObjHdl::get_signal_value_long(void) return value.value.intg; } -int VhpiSignalObjHdl::get_num_elems(void) -{ - switch (m_value.format) { - case vhpiEnumVal: - case vhpiLogicVal: - return m_value.bufSize; - case vhpiRealVal: - case vhpiIntVal: - case vhpiRawDataVal: - return 1; - case vhpiEnumVecVal: - case vhpiLogicVecVal: - return m_value.numElems; - break; - default: - LOG_ERROR("Not sure %s", - ((VhpiImpl*)m_impl)->format_to_string(m_value.format)); - return 0; - } -} - GpiCbHdl * VhpiSignalObjHdl::value_change_cb(unsigned int edge) { diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index b222925e..5d48cb43 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -211,7 +211,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string vhpiPhysVecVal == value.format || vhpiTimeVecVal == value.format) { LOG_DEBUG("Detected a vector type", name.c_str()); - gpi_type = GPI_MODULE; + gpi_type = GPI_ARRAY; } new_obj = new VhpiSignalObjHdl(this, new_hdl, gpi_type); diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index a1b75036..781ba7b8 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -153,7 +153,6 @@ class VhpiSignalObjHdl : public GpiSignalObjHdl { public: VhpiSignalObjHdl(GpiImplInterface *impl, vhpiHandleT hdl, gpi_objtype_t objtype) : GpiSignalObjHdl(impl, hdl, objtype), - m_size(0), m_rising_cb(impl, this, GPI_RISING), m_falling_cb(impl, this, GPI_FALLING), m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } @@ -172,11 +171,8 @@ class VhpiSignalObjHdl : public GpiSignalObjHdl { GpiCbHdl *value_change_cb(unsigned int edge); int initialise(std::string &name); - int get_num_elems(void); - private: const vhpiEnumT chr2vhpi(const char value); - unsigned int m_size; vhpiValueT m_value; vhpiValueT m_binvalue; VhpiValueCbHdl m_rising_cb; diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 0f5a47db..de9683be 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -123,6 +123,12 @@ int VpiCbHdl::cleanup_callback(void) return 0; } +int VpiSignalObjHdl::initialise(std::string &name) { + m_num_elems = vpi_get(vpiSize, GpiObjHdl::get_handle()); + LOG_DEBUG("VPI: %s initialised with %d elements", name.c_str(), m_num_elems); + return GpiObjHdl::initialise(name); +} + const char* VpiSignalObjHdl::get_signal_value_binstr(void) { FENTER @@ -218,11 +224,6 @@ int VpiSignalObjHdl::set_signal_value(std::string &value) return 0; } -int VpiSignalObjHdl::get_num_elems(void) -{ - return 1; -} - GpiCbHdl * VpiSignalObjHdl::value_change_cb(unsigned int edge) { VpiValueCbHdl *cb = NULL; diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index ded7213a..48063dc9 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -112,7 +112,6 @@ gpi_objtype_t to_gpi_objtype(int32_t vpitype) } } - GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &name) { int32_t type; diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 56671dad..4be9ac95 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -188,10 +188,10 @@ class VpiSignalObjHdl : public GpiSignalObjHdl { int set_signal_value(const long value); int set_signal_value(const double value); int set_signal_value(std::string &value); - int get_num_elems(void); /* Value change callback accessor */ GpiCbHdl *value_change_cb(unsigned int edge); + int initialise(std::string &name); private: VpiValueCbHdl m_rising_cb; From 11c504e6a1452723504d425d33f07b27c13182e5 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 24 Aug 2015 11:33:39 +0100 Subject: [PATCH 0937/2656] Issue #17: Allow cocotb to be built to debug python libraries if COCOTB_PYTHON_DEBUG is set as an env var --- makefiles/Makefile.pylib | 2 -- makefiles/Makefile.pylib.Darwin | 2 -- makefiles/Makefile.pylib.Linux | 26 ++++++++++++++++---------- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib index cac1a443..bd177a5a 100644 --- a/makefiles/Makefile.pylib +++ b/makefiles/Makefile.pylib @@ -29,8 +29,6 @@ # All common pyhon related rules -PYTHON_PREFIX = $(shell python-config --prefix) - NATIVE_ARCH=$(shell uname -m) ARCH?=$(NATIVE_ARCH) diff --git a/makefiles/Makefile.pylib.Darwin b/makefiles/Makefile.pylib.Darwin index a0ba1d5b..36182c1c 100644 --- a/makefiles/Makefile.pylib.Darwin +++ b/makefiles/Makefile.pylib.Darwin @@ -29,8 +29,6 @@ # All common pyhon related rules -PYTHON_PREFIX = $(shell python-config --prefix) - NATIVE_ARCH=$(shell uname -m) ARCH?=$(NATIVE_ARCH) diff --git a/makefiles/Makefile.pylib.Linux b/makefiles/Makefile.pylib.Linux index a4e7328e..5fe50b34 100644 --- a/makefiles/Makefile.pylib.Linux +++ b/makefiles/Makefile.pylib.Linux @@ -27,20 +27,26 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -# All common pyhon related rules +# All common python related rules -PYTHON_PREFIX = $(shell python-config --prefix) +ifneq ($(COCOTB_PYTHON_DEBUG),) +DBG_PYTHON_VERSION=$(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') +PYTHON_BIN=python$(DBG_PYTHON_VERSION)-dbg +DYN_SUFFIX="_d" +else +PYTHON_BIN=python +endif NATIVE_ARCH=$(shell uname -m) ARCH?=$(NATIVE_ARCH) # We might work with other Python versions -PYTHON_MAJOR_VERSION?=$(shell python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") -PYTHON_MINOR_VERSION?=$(shell python -c "from __future__ import print_function; import sys; print(sys.version_info.minor)") -PYTHON_VERSION?=$(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') +PYTHON_MAJOR_VERSION?=$(shell $(PYTHON_BIN) -c "from __future__ import print_function; import sys; print(sys.version_info.major)") +PYTHON_MINOR_VERSION?=$(shell $(PYTHON_BIN) -c "from __future__ import print_function; import sys; print(sys.version_info.minor)") +PYTHON_VERSION?=$(shell $(PYTHON_BIN) -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') -PYTHON_LIBDIR:=$(shell python -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("LIBDIR"))') -PYTHON_DYNLIBDIR:=$(shell python -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("DESTSHARED"))') +PYTHON_LIBDIR:=$(shell $(PYTHON_BIN) -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("LIBDIR"))') +PYTHON_DYNLIBDIR:=$(shell $(PYTHON_BIN) -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("DESTSHARED"))') # Nasty hack but there's no way in Python properly get the directories for 32-bit Python on a 64-bit system # TODO: Under OSX we can use "export VERSIONER_PYTHON_PREFER_32_BIT=yes", no Linux equivalent though @@ -52,7 +58,7 @@ endif # Since we don't know which modules we might need or whether the simulator # we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports # is we simply link against all dynamic libraries in the Python installation -PYLIBS = $(shell python-config --libs) +PYLIBS = $(shell $(PYTHON_BIN)-config --libs) -PYTHON_INCLUDEDIR := $(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())') -PYTHON_DYN_LIB = libpython$(PYTHON_VERSION).so +PYTHON_INCLUDEDIR := $(shell $(PYTHON_BIN) -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())') +PYTHON_DYN_LIB = libpython$(PYTHON_VERSION)$(DYN_SUFFIX).so From e00143b90d9eaea3a66a827722817b00a7aac9e0 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 25 Aug 2015 16:21:28 +0100 Subject: [PATCH 0938/2656] Issue #17: Allow different iteration relationships per object type for VPI - mappings not complete --- include/vpi_user.h | 135 ++++++++++++++++++++++++++++++++++++++----- lib/vpi/VpiCbHdl.cpp | 111 ++++++++++++++++++++++++++--------- lib/vpi/VpiImpl.cpp | 2 + lib/vpi/VpiImpl.h | 18 +++++- 4 files changed, 222 insertions(+), 44 deletions(-) diff --git a/include/vpi_user.h b/include/vpi_user.h index f964a554..16dd8e91 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -55,16 +55,98 @@ extern "C" { typedef uint32_t *vpiHandle; -#define vpiIntegerVar 25 /* Integer */ -#define vpiModule 32 /* module instance */ -#define vpiNet 36 /* scalar or vector net */ -#define vpiNetBit 37 /* bit of a vector net */ -#define vpiRealVar 47 /* real variable */ -#define vpiReg 48 /* scalar or vector reg */ -#define vpiRegBit 49 /* bit of vector reg */ -#define vpiParameter 41 /* module parameter */ -#define vpiNetArray 114 -#define vpiRegArray 116 /* multidimensional reg */ +/******************************** OBJECT TYPES ********************************/ + +#define vpiAlways 1 /* always procedure */ +#define vpiAssignStmt 2 /* quasi-continuous assignment */ +#define vpiAssignment 3 /* procedural assignment */ +#define vpiBegin 4 /* block statement */ +#define vpiCase 5 /* case statement */ +#define vpiCaseItem 6 /* case statement item */ +#define vpiConstant 7 /* numerical constant or string literal */ +#define vpiContAssign 8 /* continuous assignment */ +#define vpiDeassign 9 /* deassignment statement */ +#define vpiDefParam 10 /* defparam */ +#define vpiDelayControl 11 /* delay statement (e.g., #10) */ +#define vpiDisable 12 /* named block disable statement */ +#define vpiEventControl 13 /* wait on event, e.g., @e */ +#define vpiEventStmt 14 /* event trigger, e.g., ->e */ +#define vpiFor 15 /* for statement */ +#define vpiForce 16 /* force statement */ +#define vpiForever 17 /* forever statement */ +#define vpiFork 18 /* fork-join block */ +#define vpiFuncCall 19 /* function call */ +#define vpiFunction 20 /* function */ +#define vpiGate 21 /* primitive gate */ +#define vpiIf 22 /* if statement */ +#define vpiIfElse 23 /* if-else statement */ +#define vpiInitial 24 /* initial procedure */ +#define vpiIntegerVar 25 /* integer variable */ +#define vpiInterModPath 26 /* intermodule wire delay */ +#define vpiIterator 27 /* iterator */ +#define vpiIODecl 28 /* input/output declaration */ +#define vpiMemory 29 /* behavioral memory */ +#define vpiMemoryWord 30 /* single word of memory */ +#define vpiModPath 31 /* module path for path delays */ +#define vpiModule 32 /* module instance */ +#define vpiNamedBegin 33 /* named block statement */ +#define vpiNamedEvent 34 /* event variable */ +#define vpiNamedFork 35 /* named fork-join block */ +#define vpiNet 36 /* scalar or vector net */ +#define vpiNetBit 37 /* bit of vector net */ +#define vpiNullStmt 38 /* a semicolon. Ie. #10 ; */ +#define vpiOperation 39 /* behavioral operation */ +#define vpiParamAssign 40 /* module parameter assignment */ +#define vpiParameter 41 /* module parameter */ +#define vpiPartSelect 42 /* part-select */ +#define vpiPathTerm 43 /* terminal of module path */ +#define vpiPort 44 /* module port */ +#define vpiPortBit 45 /* bit of vector module port */ +#define vpiPrimTerm 46 /* primitive terminal */ +#define vpiRealVar 47 /* real variable */ +#define vpiReg 48 /* scalar or vector reg */ +#define vpiRegBit 49 /* bit of vector reg */ +#define vpiRelease 50 /* release statement */ +#define vpiRepeat 51 /* repeat statement */ +#define vpiRepeatControl 52 /* repeat control in an assign stmt */ +#define vpiSchedEvent 53 /* vpi_put_value() event */ +#define vpiSpecParam 54 /* specparam */ +#define vpiSwitch 55 /* transistor switch */ +#define vpiSysFuncCall 56 /* system function call */ +#define vpiSysTaskCall 57 /* system task call */ +#define vpiTableEntry 58 /* UDP state table entry */ +#define vpiTask 59 /* task */ +#define vpiTaskCall 60 /* task call */ +#define vpiTchk 61 /* timing check */ +#define vpiTchkTerm 62 /* terminal of timing check */ +#define vpiTimeVar 63 /* time variable */ +#define vpiTimeQueue 64 /* simulation event queue */ +#define vpiUdp 65 /* user-defined primitive */ +#define vpiUdpDefn 66 /* UDP definition */ +#define vpiUserSystf 67 /* user-defined system task/function */ +#define vpiVarSelect 68 /* variable array selection */ +#define vpiWait 69 /* wait statement */ +#define vpiWhile 70 /* while statement */ + +/********************** object types added with 1364-2001 *********************/ + +#define vpiAttribute 105 /* attribute of an object */ +#define vpiBitSelect 106 /* Bit-select of parameter, var select */ +#define vpiCallback 107 /* callback object */ +#define vpiDelayTerm 108 /* Delay term which is a load or driver */ +#define vpiDelayDevice 109 /* Delay object within a net */ +#define vpiFrame 110 /* reentrant task/func frame */ +#define vpiGateArray 111 /* gate instance array */ +#define vpiModuleArray 112 /* module instance array */ +#define vpiPrimitiveArray 113 /* vpiprimitiveArray type */ +#define vpiNetArray 114 /* multidimensional net */ +#define vpiRange 115 /* range declaration */ +#define vpiRegArray 116 /* multidimensional reg */ +#define vpiSwitchArray 117 /* switch instance array */ +#define vpiUdpArray 118 /* UDP instance array */ +#define vpiContAssignBit 128 /* Bit of a vector continuous assignment */ +#define vpiNamedEventArray 129 /* multidimensional named event */ + #define vpiInterface 601 #define vpiInterfaceArray 603 #define vpiModport 606 @@ -75,9 +157,14 @@ typedef uint32_t *vpiHandle; #define vpiIntVar 612 #define vpiEnumVar 617 #define vpiStructVar 618 -#define vpiStop 66 /* execute simulator's $stop */ -#define vpiFinish 67 /* execute simulator's $finish */ -#define vpiReset 68 /* execute simulator's $reset */ + + +/********************** object types added with 1364-2005 *********************/ + +#define vpiIndexedPartSelect 130 /* Indexed part-select object */ +#define vpiGenScopeArray 133 /* array of generated scopes */ +#define vpiGenScope 134 /* A generated scope */ +#define vpiGenVar 135 /* Object used to instantiate gen scopes */ #define vpiType 1 /* type of object */ #define vpiName 2 /* local name of object */ @@ -87,6 +174,27 @@ typedef uint32_t *vpiHandle; #define vpiNoDelay 1 #define vpiInertialDelay 2 +/* One 2 many relationships */ +#define vpiArgument 89 /* argument to (system) task/function */ +#define vpiBit 90 /* bit of vector net or port */ +#define vpiDriver 91 /* driver for a net */ +#define vpiInternalScope 92 /* internal scope in module */ +#define vpiLoad 93 /* load on net or reg */ +#define vpiModDataPathIn 94 /* data terminal of a module path */ +#define vpiModPathIn 95 /* Input terminal of a module path */ +#define vpiModPathOut 96 /* output terminal of a module path */ +#define vpiOperand 97 /* operand of expression */ +#define vpiPortInst 98 /* connected port instance */ +#define vpiProcess 99 /* process in module */ +#define vpiVariables 100 /* variables in module */ +#define vpiUse 101 /* usage */ + +#define vpiStop 66 /* execute simulator's $stop */ +#define vpiFinish 67 /* execute simulator's $finish */ +#define vpiReset 68 /* execute simulator's $reset */ + + + typedef struct t_vpi_time { int32_t type; /* [vpiScaledRealTime, @@ -216,7 +324,6 @@ typedef struct t_cb_data #define cbUnresolvedSystf 24 /* Object Types */ -#define vpiPort 44 #define vpiMember 742 /* error severity levels */ diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index de9683be..1f6b54dd 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -363,38 +363,89 @@ VpiNextPhaseCbHdl::VpiNextPhaseCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), cb_data.reason = cbNextSimTime; } -static int32_t options[] = { - vpiNet, - vpiReg, - vpiParameter, - vpiRegArray, - /* Calling any of there below will cause a SEGV, even on their own - when run under a mixed language verilog toplevel. On another verilog design - only this is not the case - */ - //vpiNetArray, - //vpiModule, - //vpiInterface, - //vpiInterfaceArray -}; - -std::vector VpiIterator::iterate_over(options, options + (sizeof(options) / sizeof(options[0]))); +KindMappings::KindMappings() +{ + /* vpiModule */ + int32_t module_options[] = { + //vpiModule, add this back + vpiContAssign, + vpiDefParam, + vpiMemory, + vpiModPath, + vpiNamedEvent, + vpiNet, + //vpiParamAssign,Aldec does not support this - SEGV + vpiParameter, + vpiPort, + vpiReg, + vpiSpecParam, + vpiTchk, + vpiAttribute, + //vpiModuleArray, Aldec does not support this - SEGV + vpiPrimitiveArray, + vpiNetArray, + vpiRegArray, + vpiNamedEventArray, + //vpiInterface, Aldec does not support this - SEGV + //vpiInterfaceArray, Aldec does not support this - SEGV + 0 + }; + add_to_options(vpiModule, &module_options[0]); + /* vpiNet */ + int32_t net_options[] = { + 0 + }; + add_to_options(vpiNet, &net_options[0]); + /* vpiPort */ + int32_t port_options[] = { + vpiAttribute, + 0 + }; + add_to_options(vpiPort, &port_options[0]); +} + +void KindMappings::add_to_options(int32_t type, int32_t *options) +{ + std::vector option_vec; + int32_t *ptr = options; + while (*ptr) { + option_vec.push_back(*ptr); + ptr++; + } + options_map[type] = option_vec; +} + +std::vector* KindMappings::get_options(int32_t type) +{ + std::map >::iterator valid = options_map.find(type); + + if (options_map.end() == valid) { + LOG_ERROR("VPI: Implementation does not know how to iterate over %d", type); + exit(1); + } else { + return &valid->second; + } +} + +KindMappings VpiIterator::iterate_over; VpiIterator::VpiIterator(GpiImplInterface *impl, vpiHandle hdl) : GpiIterator(impl, hdl), m_iterator(NULL) { vpiHandle iterator; - for (curr_type = iterate_over.begin(); - curr_type != iterate_over.end(); - curr_type++) { - iterator = vpi_iterate(*curr_type, hdl); + selected = iterate_over.get_options(vpi_get(vpiType, hdl)); + + for (one2many = selected->begin(); + one2many != selected->end(); + one2many++) { + iterator = vpi_iterate(*one2many, hdl); if (iterator) { break; } - LOG_DEBUG("vpi_iterate type=%d returned NULL", *curr_type); + LOG_DEBUG("vpi_iterate type=%d returned NULL", *one2many); } if (NULL == iterator) { @@ -403,7 +454,7 @@ VpiIterator::VpiIterator(GpiImplInterface *impl, vpiHandle hdl) : GpiIterator(im } LOG_DEBUG("Created iterator working from type %d", - *curr_type, + *one2many, vpi_get_str(vpiFullName, hdl)); m_iterator = iterator; @@ -435,17 +486,17 @@ GpiObjHdl *VpiIterator::next_handle(void) break; } - LOG_DEBUG("End of type=%d iteration", *curr_type); + LOG_DEBUG("End of type=%d iteration", *one2many); } else { - LOG_DEBUG("No valid type=%d iterator", *curr_type); + LOG_DEBUG("No valid type=%d iterator", *one2many); } - if (++curr_type >= iterate_over.end()) { + if (++one2many >= selected->end()) { obj = NULL; break; } - m_iterator = vpi_iterate(*curr_type, obj); + m_iterator = vpi_iterate(*one2many, get_handle()); } while (!obj); @@ -454,9 +505,13 @@ GpiObjHdl *VpiIterator::next_handle(void) return new_obj; } - std::string name = vpi_get_str(vpiFullName, obj); + std::string name; + if (vpiPort == vpi_get(vpiType, obj)) + name = vpi_get_str(vpiName, obj); + else + name = name = vpi_get_str(vpiName, obj); - LOG_DEBUG("vhpi_scan found name:%s", name.c_str()); + LOG_DEBUG("vpi_scan found name:%s", name.c_str()); VpiImpl *vpi_impl = reinterpret_cast(m_impl); new_obj = vpi_impl->create_gpi_obj_from_handle(obj, name); diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 48063dc9..3f1217e6 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -105,6 +105,7 @@ gpi_objtype_t to_gpi_objtype(int32_t vpitype) case vpiInterface: case vpiModule: case vpiRefObj: + case vpiPort: return GPI_MODULE; default: @@ -145,6 +146,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &n case vpiInterfaceArray: case vpiRefObj: case vpiPackedArrayVar: + case vpiPort: new_obj = new GpiObjHdl(this, new_hdl, to_gpi_objtype(type)); break; default: diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 4be9ac95..51e3de56 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -31,6 +31,7 @@ #include "../gpi/gpi_priv.h" #include #include +#include // Should be run after every VPI call to check error status static inline int __check_vpi_error(const char *file, const char *func, long line) @@ -172,6 +173,18 @@ class VpiShutdownCbHdl : public VpiCbHdl { virtual ~VpiShutdownCbHdl() { } }; +class KindMappings { +public: + KindMappings(); + +public: + std::map > options_map; + std::vector* get_options(int32_t type); + +private: + void add_to_options(int32_t type, int32_t *options); +}; + class VpiSignalObjHdl : public GpiSignalObjHdl { public: VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl, gpi_objtype_t objtype) : @@ -209,8 +222,9 @@ class VpiIterator : public GpiIterator { private: vpiHandle m_iterator; - static std::vector iterate_over; - std::vector::iterator curr_type; + static KindMappings iterate_over; /* Possible mappings */ + std::vector *selected; /* Mapping currently in use */ + std::vector::iterator one2many; }; class VpiImpl : public GpiImplInterface { From 4b84d69f2baa92476419c2f0d6de48e1a82978ce Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 25 Aug 2015 16:22:29 +0100 Subject: [PATCH 0939/2656] issue #17: Iterate over a handle if vhpi_handle_by_index does not work --- lib/vhpi/VhpiCbHdl.cpp | 2 -- lib/vhpi/VhpiImpl.cpp | 26 +++++++++++++++++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index f6f73778..e831f33e 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -513,8 +513,6 @@ VhpiNextPhaseCbHdl::VhpiNextPhaseCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), KindMappings::KindMappings() { -// std::vector option(root_options, root_options + sizeof(root_options) / sizeof(root_options[0])); - /* vhpiRootInstK */ vhpiOneToManyT root_options[] = { vhpiInternalRegions, diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 5d48cb43..11d6f44c 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -275,12 +275,28 @@ GpiObjHdl *VhpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) vhpi_get_str(vhpiNameP, vhpi_hdl), vhpi_get_str(vhpiKindStrP, vhpi_hdl)); - if (vhpiIndexedNameK == vhpi_get(vhpiKindP, vhpi_hdl)) { - LOG_DEBUG("Can't index into vhpiIndexedNameK, have to use an iterator?"); - return NULL; - } - new_hdl = vhpi_handle_by_index(vhpiIndexedNames, vhpi_hdl, index); + if (!new_hdl) { + /* Support for the above seems poor, so if it die not work + try an iteration instead */ + + vhpiHandleT iter = vhpi_iterator(vhpiIndexedNames, vhpi_hdl); + if (iter) { + uint32_t curr_index = 0; + while (true) { + new_hdl = vhpi_scan(iter); + if (!new_hdl) { + break; + } + if (index == curr_index) { + LOG_DEBUG("Index match %u == %u", curr_index, index); + break; + } + curr_index++; + } + vhpi_release_handle(iter); + } + } if (new_hdl == NULL) { LOG_DEBUG("Unable to query vhpi_handle_by_index %u", index); From e4cf188e4858c261f603c960e93a57294351afde Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 25 Aug 2015 17:00:46 +0100 Subject: [PATCH 0940/2656] Issue #17: Excluded test_iteration_mixed from icarus --- tests/designs/uart2bus/Makefile | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/tests/designs/uart2bus/Makefile b/tests/designs/uart2bus/Makefile index 6f7f6d27..7c327ad5 100644 --- a/tests/designs/uart2bus/Makefile +++ b/tests/designs/uart2bus/Makefile @@ -17,14 +17,31 @@ VERILOG_SOURCES = $(SRC_BASE)/verilog/baud_gen.v \ $(SRC_BASE)/verilog/uart_top.v \ $(SRC_BASE)/verilog/uart2bus_top.v +GPI_EXTRA:= ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES += $(SRC_BASE)/top/verilog_toplevel.v TOPLEVEL = verilog_toplevel GPI_IMPL=vpi + +ifeq ($(SIM),aldec) GPI_EXTRA=vhpi +endif +ifeq ($(SIM),modelsim) + GPI_EXTRA=fli +endif +ifeq ($(SIM),ius) + GPI_EXTRA=vhpi +endif + +else + $(error "Only verilog toplevel supported at the moment") +endif + +ifneq ($(GPI_EXTRA),) + include $(COCOTB)/makefiles/Makefile.inc + include $(COCOTB)/makefiles/Makefile.sim else -$(error "Only verilog toplevel supported at the moment") +all: + @echo "Skipping test as VHDL not supported on simulator=$(SIM)" endif -include $(COCOTB)/makefiles/Makefile.inc -include $(COCOTB)/makefiles/Makefile.sim From 0aa051c256ca18944f13d7cd7cd78b690860c2d4 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 25 Aug 2015 18:27:58 +0100 Subject: [PATCH 0941/2656] Issue #297: Call exit as part of LOG_CRITICAL, tidyups to where it is called as well. Plus other fixes from coverity --- include/gpi_logging.h | 5 ++++- lib/embed/gpi_embed.c | 1 - lib/fli/FliImpl.cpp | 14 ++++---------- lib/gpi/GpiCbHdl.cpp | 4 +--- lib/gpi/gpi_priv.h | 3 ++- lib/simulator/simulatormodule.c | 15 +++++++-------- lib/vhpi/VhpiCbHdl.cpp | 3 +-- lib/vpi/VpiCbHdl.cpp | 8 ++------ lib/vpi/VpiImpl.cpp | 5 +++-- 9 files changed, 24 insertions(+), 34 deletions(-) diff --git a/include/gpi_logging.h b/include/gpi_logging.h index 36dd13ba..eb5611f0 100644 --- a/include/gpi_logging.h +++ b/include/gpi_logging.h @@ -53,7 +53,10 @@ enum gpi_log_levels { #define LOG_INFO(...) gpi_log("cocotb.gpi", GPIInfo, __FILE__, __func__, __LINE__, __VA_ARGS__); #define LOG_WARN(...) gpi_log("cocotb.gpi", GPIWarning, __FILE__, __func__, __LINE__, __VA_ARGS__); #define LOG_ERROR(...) gpi_log("cocotb.gpi", GPIError, __FILE__, __func__, __LINE__, __VA_ARGS__); -#define LOG_CRITICAL(...) gpi_log("cocotb.gpi", GPICritical, __FILE__, __func__, __LINE__, __VA_ARGS__); +#define LOG_CRITICAL(...) do { \ + gpi_log("cocotb.gpi", GPICritical, __FILE__, __func__, __LINE__, __VA_ARGS__); \ + exit(1); \ +} while (0) // #ifdef DEBUG // #define FENTER LOG_DEBUG(__func__) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 47f7d927..617c52f6 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -262,7 +262,6 @@ int embed_sim_init(gpi_sim_info_t *info) if (PyErr_Occurred()) PyErr_Print(); fprintf(stderr, "Cannot find function \"%s\"\n", "_initialise_testbench"); - Py_DECREF(cocotb_init); goto cleanup; } diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 2e37ac46..868f9dc1 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -75,8 +75,9 @@ void handle_fli_callback(void *data) FliProcessCbHdl *cb_hdl = (FliProcessCbHdl*)data; - if (!cb_hdl) + if (!cb_hdl) { LOG_CRITICAL("FLI: Callback data corrupted"); + } gpi_cb_state_e old_state = cb_hdl->get_call_state(); @@ -485,25 +486,21 @@ int FliSignalObjHdl::initialise(std::string &name) m_mti_buff = (mtiInt32T*)malloc(sizeof(*m_mti_buff) * m_val_len); if (!m_mti_buff) { LOG_CRITICAL("Unable to alloc mem for signal mti read buffer"); - exit(1); } break; default: LOG_CRITICAL("Unable to handle onject type for %s (%d)", name.c_str(), m_fli_type); - exit(1); } m_val_buff = (char*)malloc(m_val_len+1); if (!m_val_buff) { LOG_CRITICAL("Unable to alloc mem for signal read buffer"); - exit(1); } m_val_buff[m_val_len] = '\0'; m_val_str_buff = (char*)malloc(m_val_str_len+1); if (!m_val_str_buff) { LOG_CRITICAL("Unable to alloc mem for signal write buffer"); - exit(1); } m_val_str_buff[m_val_str_len] = '\0'; @@ -558,13 +555,13 @@ const char* FliVariableObjHdl::get_signal_value_binstr(void) int FliVariableObjHdl::set_signal_value(const int value) { - LOG_CRITICAL("Setting variable value not currently supported!\n"); + LOG_ERROR("Setting variable value not currently supported!\n"); return -1; } int FliVariableObjHdl::set_signal_value(std::string &value) { - LOG_CRITICAL("Setting variable value not currently supported!\n"); + LOG_ERROR("Setting variable value not currently supported!\n"); return -1; } @@ -586,19 +583,16 @@ int FliVariableObjHdl::initialise(std::string &name) m_mti_buff = (mtiInt32T*)malloc(sizeof(*m_mti_buff) * m_val_len); if (!m_mti_buff) { LOG_CRITICAL("Unable to alloc mem for signal mti read buffer"); - exit(1); } break; default: LOG_CRITICAL("Unable to handle onject type for %s (%d)", name.c_str(), m_fli_type); - exit(1); } m_val_buff = (char*)malloc(m_val_len+1); if (!m_val_buff) { LOG_CRITICAL("Unable to alloc mem for signal read buffer"); - exit(1); } m_val_buff[m_val_len] = '\0'; diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index 8f424d75..7b763731 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -53,7 +53,7 @@ char *GpiHdl::gpi_copy_name(const char *name) if (name) len = strlen(name) + 1; else { - LOG_CRITICAL("GPI: attempt to use NULL from impl"); + LOG_WARN("GPI: attempt to use NULL from impl"); len = strlen(null); name = null; } @@ -61,8 +61,6 @@ char *GpiHdl::gpi_copy_name(const char *name) result = (char *)malloc(len); if (result == NULL) { LOG_CRITICAL("GPI: Attempting allocate string buffer failed!"); - len = strlen(null); - name = null; } snprintf(result, len, "%s", name); diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index f58ae35a..1badea48 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -52,7 +52,6 @@ inline To sim_to_hdl(gpi_sim_hdl input) To result = static_cast(input); if (!result) { LOG_CRITICAL("GPI: Handle passed down is not valid gpi_sim_hdl"); - exit(1); } return result; @@ -141,6 +140,8 @@ class GpiSignalObjHdl : public GpiObjHdl { class GpiCbHdl : public GpiHdl { public: GpiCbHdl(GpiImplInterface *impl) : GpiHdl(impl, NULL), + gpi_function(NULL), + m_cb_data(NULL), m_state(GPI_FREE) { } // Pure virtual functions for derived classes virtual int arm_callback(void) = 0; // Register with siumlator diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index f87fcd67..b2377916 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -105,13 +105,12 @@ int handle_gpi_callback(void *user_data) PyObject *pValue = PyObject_Call(callback_data_p->function, callback_data_p->args, callback_data_p->kwargs); // If the return value is NULL a Python exception has occurred + // The best thing to do here is shutdown as any subsequent + // calls will go back to python which is now in an unknown state if (pValue == NULL) { - return 0; fprintf(stderr, "ERROR: called callback function returned NULL\n"); - PyErr_Print(); fprintf(stderr, "Failed to execute callback\n"); - DROP_GIL(gstate); gpi_sim_end(); return 0; } @@ -189,7 +188,7 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); if (callback_data_p == NULL) { - printf("Failed to allocate user data\n"); + LOG_CRITICAL("Failed to allocate user data\n"); } // Set up the user data (no more python API calls after this! @@ -245,7 +244,7 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); if (callback_data_p == NULL) { - printf("Failed to allocate user data\n"); + LOG_CRITICAL("Failed to allocate user data\n"); } // Set up the user data (no more python API calls after this! @@ -301,7 +300,7 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); if (callback_data_p == NULL) { - printf("Failed to allocate user data\n"); + LOG_CRITICAL("Failed to allocate user data\n"); } // Set up the user data (no more python API calls after this! @@ -367,7 +366,7 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); if (callback_data_p == NULL) { - printf("Failed to allocate user data\n"); + LOG_CRITICAL("Failed to allocate user data\n"); } // Set up the user data (no more python API calls after this! @@ -437,7 +436,7 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); if (callback_data_p == NULL) { - printf("Failed to allocate user data\n"); + LOG_CRITICAL("Failed to allocate user data\n"); } // Set up the user data (no more python API calls after this! diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 6eef65f0..3f17784b 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -149,10 +149,9 @@ int VhpiCbHdl::arm_callback(void) vhpiHandleT new_hdl = vhpi_register_cb(&cb_data, vhpiReturnCb); if (!new_hdl) { + check_vhpi_error(); LOG_CRITICAL("VHPI: Unable to register callback a handle for VHPI type %s(%d)", m_impl->reason_to_string(cb_data.reason), cb_data.reason); - check_vhpi_error(); - ret = -1; } cbState = (vhpiStateT)vhpi_get(vhpiStateP, new_hdl); diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 9b584033..381b4435 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -44,6 +44,7 @@ VpiCbHdl::VpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl) cb_data.obj = NULL; cb_data.time = &vpi_time; cb_data.value = NULL; + cb_data.index = 0; cb_data.user_data = (char*)this; } @@ -75,8 +76,7 @@ int VpiCbHdl::arm_callback(void) { if (!new_hdl) { LOG_CRITICAL("VPI: Unable to register a callback handle for VPI type %s(%d)", m_impl->reason_to_string(cb_data.reason), cb_data.reason); - - ret = -1; + } else { m_state = GPI_PRIMED; } @@ -98,12 +98,10 @@ int VpiCbHdl::cleanup_callback(void) if (m_state == GPI_PRIMED) { if (!m_obj_hdl) { LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); - exit(1); } if (!(vpi_remove_cb(get_handle()))) { LOG_CRITICAL("VPI: unbale to remove callback : ABORTING"); - exit(1); } check_vpi_error(); @@ -112,7 +110,6 @@ int VpiCbHdl::cleanup_callback(void) /* This is disabled for now, causes a small leak going to put back in */ if (!(vpi_free_object(get_handle()))) { LOG_CRITICAL("VPI: unbale to free handle : ABORTING"); - exit(1); } #endif } @@ -226,7 +223,6 @@ int VpiValueCbHdl::cleanup_callback(void) * not wanted */ if (!(vpi_remove_cb(get_handle()))) { LOG_CRITICAL("VPI: unbale to remove callback : ABORTING"); - exit(1); } m_obj_hdl = NULL; diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 28985281..867e78ef 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -272,8 +272,9 @@ int32_t handle_vpi_callback(p_cb_data cb_data) VpiCbHdl *cb_hdl = (VpiCbHdl*)cb_data->user_data; - if (!cb_hdl) + if (!cb_hdl) { LOG_CRITICAL("VPI: Callback data corrupted"); + } gpi_cb_state_e old_state = cb_hdl->get_call_state(); @@ -367,7 +368,7 @@ static int system_function_overload(char *userdata) // The first argument to fatal is the FinishNum which we discard if (args_iter && *userdata == systf_fatal_level) { - argh = vpi_scan(args_iter); + vpi_scan(args_iter); } if (args_iter) { From 8551c997afd27386830bac865802deca0c24c98b Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 25 Aug 2015 19:05:04 +0100 Subject: [PATCH 0942/2656] Issue #297: Do not exit if a handle can not be set leave to python to deal with it --- lib/vhpi/VhpiCbHdl.cpp | 6 ++++-- lib/vpi/VpiCbHdl.cpp | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 3f17784b..41eed7b1 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -150,13 +150,15 @@ int VhpiCbHdl::arm_callback(void) if (!new_hdl) { check_vhpi_error(); - LOG_CRITICAL("VHPI: Unable to register callback a handle for VHPI type %s(%d)", + LOG_ERROR("VHPI: Unable to register callback a handle for VHPI type %s(%d)", m_impl->reason_to_string(cb_data.reason), cb_data.reason); + return -1; } cbState = (vhpiStateT)vhpi_get(vhpiStateP, new_hdl); if (vhpiEnable != cbState) { - LOG_CRITICAL("VHPI ERROR: Registered callback isn't enabled! Got %d\n", cbState); + LOG_ERROR("VHPI ERROR: Registered callback isn't enabled! Got %d\n", cbState); + return -1; } m_obj_hdl = new_hdl; diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 381b4435..01e6dcaf 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -74,8 +74,9 @@ int VpiCbHdl::arm_callback(void) { int ret = 0; if (!new_hdl) { - LOG_CRITICAL("VPI: Unable to register a callback handle for VPI type %s(%d)", + LOG_ERROR("VPI: Unable to register a callback handle for VPI type %s(%d)", m_impl->reason_to_string(cb_data.reason), cb_data.reason); + return -1; } else { m_state = GPI_PRIMED; From 4d8cf9b45ec3d0cb72c3a8613131c5d38cdac529 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 25 Aug 2015 19:05:22 +0100 Subject: [PATCH 0943/2656] Issue #297: Allocte enough space for string --- lib/gpi/GpiCbHdl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index 7b763731..d80e68d7 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -58,7 +58,7 @@ char *GpiHdl::gpi_copy_name(const char *name) name = null; } - result = (char *)malloc(len); + result = (char *)malloc(len + 1); if (result == NULL) { LOG_CRITICAL("GPI: Attempting allocate string buffer failed!"); } From 85d63a85bce82110c45a6af19dc45bf6e169adc5 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 25 Aug 2015 19:05:47 +0100 Subject: [PATCH 0944/2656] Issue #297: Use strtol rather than atoi and handle the result better --- lib/embed/gpi_embed.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 617c52f6..585c1de7 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -90,10 +90,21 @@ void embed_init_python(void) such that they can attach */ const char *pause = getenv("COCOTB_ATTACH"); if (pause) { - int sleep_time = atoi(pause); - fprintf(stderr, "Waiting for %d seconds - Attach to %d\n", sleep_time, getpid()); + long sleep_time = strtol(pause, NULL, 10); + if (errno == ERANGE && (sleep_time == LONG_MAX || sleep_time == LONG_MIN)) { + fprintf(stderr, "COCOTB_ATTACH only needs to be set to ~30 seconds"); + goto out; + } + if ((errno != 0 && sleep_time == 0) || + (sleep_time <= 0)) { + fprintf(stderr, "COCOTB_ATTACH must be set to an integer base 10 or omitted"); + goto out; + } + + fprintf(stderr, "Waiting for %lu seconds - Attach to %d\n", sleep_time, getpid()); sleep(sleep_time); } +out: FEXIT; } From 8c7bda4ba1da0cc9bdcbde20b4cfc98dee98e2b1 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 25 Aug 2015 19:34:08 +0100 Subject: [PATCH 0945/2656] Add coverity status to README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 282075b4..e25bc0c2 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Documentation Status](https://readthedocs.org/projects/cocotb/badge/?version=latest)](http://cocotb.readthedocs.org/en/latest/) [![Build Status](https://travis-ci.org/potentialventures/cocotb.svg?branch=master)](https://travis-ci.org/potentialventures/cocotb) +[![Coverity Scan Status] (https://scan.coverity.com/projects/6110/badge.svg)](https://scan.coverity.com/projects/cocotb) * Skim the introductory presentation: http://potential.ventures * Read the [documentation](http://cocotb.readthedocs.org) From 5a452bc58bf93bbce1b989e288fc9a688d83c327 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 25 Aug 2015 20:03:28 +0100 Subject: [PATCH 0946/2656] Issue #17: correct typo --- lib/vhpi/VhpiImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 11d6f44c..c979011d 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -277,7 +277,7 @@ GpiObjHdl *VhpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) new_hdl = vhpi_handle_by_index(vhpiIndexedNames, vhpi_hdl, index); if (!new_hdl) { - /* Support for the above seems poor, so if it die not work + /* Support for the above seems poor, so if it did not work try an iteration instead */ vhpiHandleT iter = vhpi_iterator(vhpiIndexedNames, vhpi_hdl); From 9656b43558c822084fd9a8d1fc714d3503f48e25 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Wed, 26 Aug 2015 18:03:42 +0200 Subject: [PATCH 0947/2656] delete Unused NextTimeStep import --- cocotb/drivers/avalon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index f4922edd..6af12259 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -35,7 +35,7 @@ import cocotb from cocotb.decorators import coroutine -from cocotb.triggers import RisingEdge, ReadOnly, NextTimeStep, Event +from cocotb.triggers import RisingEdge, ReadOnly, Event from cocotb.drivers import BusDriver, ValidatedBusDriver from cocotb.utils import hexdump from cocotb.binary import BinaryValue From 5babb1de8cad30d6c1cfd4a92ed934547cf563e8 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 28 Aug 2015 08:20:19 +0100 Subject: [PATCH 0948/2656] Issue #17: Initialise m_num_elems --- lib/gpi/gpi_priv.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index f40d2d93..5bbcaa1e 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -97,9 +97,11 @@ class GpiObjHdl : public GpiHdl { m_name(name), m_fullname("unknown") { } GpiObjHdl(GpiImplInterface *impl) : GpiHdl(impl, NULL), + m_num_elems(0), m_fullname("unknown"), m_type(GPI_UNKNOWN) { } GpiObjHdl(GpiImplInterface *impl, void *hdl, gpi_objtype_t objtype) : GpiHdl(impl, hdl), + m_num_elems(0), m_fullname("unknown"), m_type(objtype) { } From 0fcb393be0563fd9b82cbe5978a8257b21e07027 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 28 Aug 2015 09:24:20 +0100 Subject: [PATCH 0949/2656] Issue #17: Update VPI iteration type mappings --- include/vpi_user.h | 3 ++ lib/vpi/VpiCbHdl.cpp | 71 +++++++++++++++++++++++++++++++++----------- 2 files changed, 57 insertions(+), 17 deletions(-) diff --git a/include/vpi_user.h b/include/vpi_user.h index 16dd8e91..feba2778 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -128,6 +128,9 @@ typedef uint32_t *vpiHandle; #define vpiWait 69 /* wait statement */ #define vpiWhile 70 /* while statement */ + +#define vpiPrimitive 103 /* primitive (gate, switch, UDP) */ + /********************** object types added with 1364-2001 *********************/ #define vpiAttribute 105 /* attribute of an object */ diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 1f6b54dd..7cdffc17 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -367,38 +367,75 @@ KindMappings::KindMappings() { /* vpiModule */ int32_t module_options[] = { - //vpiModule, add this back - vpiContAssign, - vpiDefParam, + //vpiModule, // Aldec SEGV on mixed language + //vpiModuleArray, // Aldec SEGV on mixed language + vpiInternalScope, + vpiPort, + //vpiIODecl, // Don't care about these + vpiNet, + vpiNetArray, + vpiReg, + vpiRegArray, vpiMemory, - vpiModPath, + //vpiVariables // Aldec SEGV on plain Verilog vpiNamedEvent, - vpiNet, - //vpiParamAssign,Aldec does not support this - SEGV + vpiNamedEventArray, vpiParameter, - vpiPort, - vpiReg, - vpiSpecParam, + //vpiSpecParam, // Don't care + //vpiParamAssign, // Aldec SEGV on mixed language + //vpiDefParam, // Don't care + vpiPrimitive, + vpiPrimitiveArray, + //vpiContAssign, // Don't care + vpiProcess, // Don't care + vpiModPath, vpiTchk, vpiAttribute, - //vpiModuleArray, Aldec does not support this - SEGV - vpiPrimitiveArray, - vpiNetArray, - vpiRegArray, - vpiNamedEventArray, - //vpiInterface, Aldec does not support this - SEGV - //vpiInterfaceArray, Aldec does not support this - SEGV + //vpiInterface, // Aldec SEGV on mixed language + //vpiInterfaceArray, // Aldec SEGV on mixed language 0 }; add_to_options(vpiModule, &module_options[0]); + /* vpiNet */ int32_t net_options[] = { + //vpiContAssign, // Driver and load handled separately + //vpiPrimTerm, + //vpiPathTerm, + //vpiTchkTerm, + //vpiDriver, + //vpiLocalDriver, + //vpiLoad, + //vpiLocalLoad, 0 }; add_to_options(vpiNet, &net_options[0]); + + /* vpiNetArray */ + int32_t netarray_options[] = { + vpiNet, + 0 + }; + add_to_options(vpiNetArray, &netarray_options[0]); + + + /* vpiRegArray */ + int32_t regarray_options[] = { + vpiReg, + 0 + }; + add_to_options(vpiRegArray, ®array_options[0]); + + /* vpiMemory */ + int32_t memory_options[] = { + vpiMemoryWord, + 0 + }; + add_to_options(vpiMemory, &memory_options[0]); + /* vpiPort */ int32_t port_options[] = { - vpiAttribute, + vpiBit, 0 }; add_to_options(vpiPort, &port_options[0]); From ebd978aa440c8bbe1d189daad3ff5b53733c5232 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 28 Aug 2015 10:01:05 +0100 Subject: [PATCH 0950/2656] Issue #17: Add example of iteration after manually crossing language boundary --- .../test_iteration.py | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/tests/test_cases/test_iteration_mixedlang/test_iteration.py b/tests/test_cases/test_iteration_mixedlang/test_iteration.py index 942dc3f4..85a0e431 100644 --- a/tests/test_cases/test_iteration_mixedlang/test_iteration.py +++ b/tests/test_cases/test_iteration_mixedlang/test_iteration.py @@ -29,6 +29,21 @@ from cocotb.triggers import Timer from cocotb.result import TestError, TestFailure + +def recursive_dump(parent, log): + """ + Recursively iterate through every object and log a message + + Returns a count of the total number of objects found + """ + count = 0 + for thing in parent: + count += 1 + log.info("Found %s.%s (%s)", parent._name, thing._name, type(thing)) + count += recursive_dump(thing, log) + return count + + @cocotb.test() def recursive_discovery(dut): """ @@ -36,13 +51,20 @@ def recursive_discovery(dut): """ tlog = logging.getLogger("cocotb.test") yield Timer(100) - def dump_all_the_things(parent): - count = 0 - for thing in parent: - count += 1 - tlog.info("Found %s.%s (%s)", parent._name, thing._name, type(thing)) - count += dump_all_the_things(thing) - return count - total = dump_all_the_things(dut) + total = recursive_dump(dut, tlog) + tlog.info("Found a total of %d things", total) + + +@cocotb.test() +def recursive_discovery_boundary(dut): + """ + Currently we can't traverse a language boundary during iteration + + However if we manually delve through the language boundary we + should then be able to iterate to discover objects + """ + tlog = logging.getLogger("cocotb.test") + yield Timer(100) + total = recursive_dump(dut.i_vhdl, tlog) tlog.info("Found a total of %d things", total) From 3efa2724a3d0e8f003f6efb174ab160a808828cf Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 28 Aug 2015 10:39:06 +0100 Subject: [PATCH 0951/2656] Issue #17: Add additional testcases for VHDL access --- tests/test_cases/test_vhdl_access/Makefile | 32 +++++++++ .../test_vhdl_access/test_vhdl_access.py | 70 +++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 tests/test_cases/test_vhdl_access/Makefile create mode 100644 tests/test_cases/test_vhdl_access/test_vhdl_access.py diff --git a/tests/test_cases/test_vhdl_access/Makefile b/tests/test_cases/test_vhdl_access/Makefile new file mode 100644 index 00000000..f51d0b8c --- /dev/null +++ b/tests/test_cases/test_vhdl_access/Makefile @@ -0,0 +1,32 @@ +############################################################################### +# Copyright (c) 2015 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +PWD := $(shell pwd) +COCOTB := $(PWD)/../../.. + +include ../../designs/viterbi_decoder_axi4s/Makefile +MODULE = test_vhdl_access diff --git a/tests/test_cases/test_vhdl_access/test_vhdl_access.py b/tests/test_cases/test_vhdl_access/test_vhdl_access.py new file mode 100644 index 00000000..7ec4b551 --- /dev/null +++ b/tests/test_cases/test_vhdl_access/test_vhdl_access.py @@ -0,0 +1,70 @@ +''' Copyright (c) 2015 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +import logging + +import cocotb +from cocotb.handle import HierarchyObject, ModifiableObject, RealObject, IntegerObject, ConstantObject +from cocotb.triggers import Timer +from cocotb.result import TestError, TestFailure + + + + +@cocotb.test() +def check_objects(dut): + """ + Check the types of objects that are returned + """ + tlog = logging.getLogger("cocotb.test") + fails = 0 + yield Timer(100) + + def check_instance(obj, objtype): + if not isinstance(obj, objtype): + tlog.error("Expected %s to be of type %s but got %s" % ( + obj._fullname, objtype.__name__, obj.__class__.__name__)) + return 1 + tlog.info("%s is %s" % (obj._fullname, obj.__class__.__name__)) + return 0 + + # Hierarchy checks + fails += check_instance(dut.inst_axi4s_buffer, HierarchyObject) + #fails += check_instance(dut.gen_branch_distance[0], HierarchyObject) + #fails += check_instance(dut.gen_branch_distance[0].inst_branch_distance, HierarchyObject) + #fails += check_instance(dut.gen_acs[0].inbranch_tdata_low, ModifiableObject) + #fails += check_instance(dut.gen_acs[0].inbranch_tdata_low[0], ModifiableObject) + + fails += check_instance(dut.aclk, ModifiableObject) + fails += check_instance(dut.s_axis_input_tdata, ModifiableObject) + + fails += check_instance(dut.current_active, IntegerObject) + + fails += check_instance(dut.inst_axi4s_buffer.DATA_WIDTH, ConstantObject) + + if fails: + raise TestFailure("%d objects were not of the expected type" % fails) + + From 59ad81fee8d94366779520d73e108378e66016ae Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 28 Aug 2015 14:36:46 +0100 Subject: [PATCH 0952/2656] Issue #17: Fix up access to generated code blocks, fix iteration --- cocotb/handle.py | 6 +++++ lib/vhpi/VhpiCbHdl.cpp | 26 +++++++++++-------- .../test_vhdl_access/test_vhdl_access.py | 8 +++--- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 37acee44..dc506eda 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -100,10 +100,16 @@ def __getattr__(self, name): if name in self._sub_handles: return self._sub_handles[name] new_handle = simulator.get_handle_by_name(self._handle, name) + if not new_handle: if name in self._compat_mapping: warnings.warn("Use of %s attribute is deprecated" % name) return getattr(self, self._compat_mapping[name]) + + # To find generated indices we have to discover all + self._discover_all() + if name in self._sub_handles: + return self._sub_handles[name] raise AttributeError("%s contains no object named %s" % (self._name, name)) self._sub_handles[name] = SimHandle(new_handle) return self._sub_handles[name] diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index e831f33e..f241c5f6 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -634,26 +634,27 @@ VhpiIterator::VhpiIterator(GpiImplInterface *impl, vhpiHandleT hdl) : GpiIterato vhpi_get(vhpiKindP, hdl), vhpi_get_str(vhpiKindStrP, hdl)); - /* On some simulators , Aldec vhpiRootInstK is a null level of hierachy + /* On some simulators (Aldec) vhpiRootInstK is a null level of hierachy * We check that something is going to come back if not we try the level * down */ - if (vhpiRootInstK == vhpi_get(vhpiKindP, hdl)) { uint32_t children = 0; vhpiHandleT tmp_hdl; - for(tmp_hdl = vhpi_scan(iterator); tmp_hdl && (children <= 1); tmp_hdl = vhpi_scan(iterator), children++) { } - vhpi_release_handle(iterator); + for(tmp_hdl = vhpi_scan(iterator); tmp_hdl != NULL; tmp_hdl = vhpi_scan(iterator), children++) { } + iterator = vhpi_iterator(*one2many, hdl); + // Only a single child, skip a level if (children == 1) { - vhpiHandleT root_iterator; tmp_hdl = vhpi_scan(iterator); - root_iterator = vhpi_iterator(*one2many, tmp_hdl); vhpi_release_handle(iterator); - iterator = root_iterator; - LOG_WARN("Skipped vhpiRootInstK to get to %s", vhpi_get_str(vhpiKindStrP, tmp_hdl)); + + iterator = vhpi_iterator(*one2many, tmp_hdl); + LOG_WARN("Skipped vhpiRootInstK to get to %s (%s)", + vhpi_get_str(vhpiKindStrP, tmp_hdl), + vhpi_get_str(vhpiFullNameP, tmp_hdl)); m_iter_obj = tmp_hdl; } } @@ -688,11 +689,14 @@ GpiObjHdl *VhpiIterator::next_handle(void) obj=NULL; } - if (obj) - continue; + if (obj) { + LOG_DEBUG("Found an item %s", vhpi_get_str(vhpiFullNameP, obj)); + break; + } else { + LOG_DEBUG("vhpi_scan on %d returned NULL", m_iterator); + } LOG_DEBUG("End of vhpiOneToManyT=%d iteration", *one2many); - vhpi_release_handle(m_iterator); m_iterator = NULL; } else { LOG_DEBUG("No valid vhpiOneToManyT=%d iterator", *one2many); diff --git a/tests/test_cases/test_vhdl_access/test_vhdl_access.py b/tests/test_cases/test_vhdl_access/test_vhdl_access.py index 7ec4b551..f586432e 100644 --- a/tests/test_cases/test_vhdl_access/test_vhdl_access.py +++ b/tests/test_cases/test_vhdl_access/test_vhdl_access.py @@ -52,10 +52,10 @@ def check_instance(obj, objtype): # Hierarchy checks fails += check_instance(dut.inst_axi4s_buffer, HierarchyObject) - #fails += check_instance(dut.gen_branch_distance[0], HierarchyObject) - #fails += check_instance(dut.gen_branch_distance[0].inst_branch_distance, HierarchyObject) - #fails += check_instance(dut.gen_acs[0].inbranch_tdata_low, ModifiableObject) - #fails += check_instance(dut.gen_acs[0].inbranch_tdata_low[0], ModifiableObject) + fails += check_instance(dut.gen_branch_distance[0], HierarchyObject) + fails += check_instance(dut.gen_branch_distance[0].inst_branch_distance, HierarchyObject) + fails += check_instance(dut.gen_acs[0].inbranch_tdata_low, ModifiableObject) + fails += check_instance(dut.gen_acs[0].inbranch_tdata_low[0], ModifiableObject) fails += check_instance(dut.aclk, ModifiableObject) fails += check_instance(dut.s_axis_input_tdata, ModifiableObject) From 251077d842e53b07ba96951a1a9f8045de31ddb8 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 28 Aug 2015 15:59:47 +0100 Subject: [PATCH 0953/2656] Issue #17: Add some tests for assignments to objects that should be constant --- .../test_vhdl_access/test_vhdl_access.py | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/tests/test_cases/test_vhdl_access/test_vhdl_access.py b/tests/test_cases/test_vhdl_access/test_vhdl_access.py index f586432e..1191280b 100644 --- a/tests/test_cases/test_vhdl_access/test_vhdl_access.py +++ b/tests/test_cases/test_vhdl_access/test_vhdl_access.py @@ -31,8 +31,6 @@ from cocotb.result import TestError, TestFailure - - @cocotb.test() def check_objects(dut): """ @@ -56,15 +54,31 @@ def check_instance(obj, objtype): fails += check_instance(dut.gen_branch_distance[0].inst_branch_distance, HierarchyObject) fails += check_instance(dut.gen_acs[0].inbranch_tdata_low, ModifiableObject) fails += check_instance(dut.gen_acs[0].inbranch_tdata_low[0], ModifiableObject) - fails += check_instance(dut.aclk, ModifiableObject) fails += check_instance(dut.s_axis_input_tdata, ModifiableObject) - fails += check_instance(dut.current_active, IntegerObject) - fails += check_instance(dut.inst_axi4s_buffer.DATA_WIDTH, ConstantObject) + fails += check_instance(dut.inst_ram_ctrl, HierarchyObject) + fails += check_instance(dut.inst_ram_ctrl.write_ram_fsm, IntegerObject) + + if dut.inst_axi4s_buffer.DATA_WIDTH != 32: + tlog.error("Expected dut.inst_axi4s_buffer.DATA_WIDTH to be 32 but got %d", + dut.inst_axi4s_buffer.DATA_WIDTH) + fails += 1 + + try: + dut.inst_axi4s_buffer.DATA_WIDTH = 42 + tlog.error("Shouldn't be allowed to set a value on constant object") + fails += 1 + except ValueError as e: + pass + + try: + dut.inst_axi4s_buffer.DATA_WIDTH <= 42 + tlog.error("Shouldn't be allowed to set a value on constant object") + fails += 1 + except ValueError as e: + pass if fails: - raise TestFailure("%d objects were not of the expected type" % fails) - - + raise TestFailure("%d Failures during the test" % fails) From aeb4adb6179035006723a687b387907e30cbecd1 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 28 Aug 2015 16:00:36 +0100 Subject: [PATCH 0954/2656] Issue #17: Distinguish between integers and enumerations --- cocotb/handle.py | 3 ++- include/gpi.h | 3 ++- lib/simulator/simulatormodule.c | 19 +++++++++++++++++++ lib/simulator/simulatormodule_python2.c | 15 +-------------- lib/simulator/simulatormodule_python3.c | 16 +--------------- lib/vhpi/VhpiImpl.cpp | 7 ++++++- lib/vpi/VpiImpl.cpp | 4 +++- 7 files changed, 34 insertions(+), 33 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index dc506eda..feb13948 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -510,7 +510,8 @@ def SimHandle(handle): simulator.REG: ModifiableObject, simulator.NETARRAY: ModifiableObject, simulator.REAL: RealObject, - simulator.ENUM: IntegerObject + simulator.INTEGER: IntegerObject, + simulator.ENUM: IntegerObject, } t = simulator.get_type(handle) diff --git a/include/gpi.h b/include/gpi.h index 0d5669d0..222782cf 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -134,7 +134,8 @@ typedef enum gpi_objtype_e { GPI_ARRAY = 6, GPI_ENUM = 7, GPI_STRUCTURE = 8, - GPI_REAL = 9 + GPI_REAL = 9, + GPI_INTEGER = 10 } gpi_objtype_t; // Functions for iterating over entries of a handle diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 6e19a084..7f3ba97e 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -874,6 +874,25 @@ static PyObject *deregister_callback(PyObject *self, PyObject *args) return value; } +static void add_module_constants(PyObject* simulator) +{ + // Make the GPI constants accessible from the C world + int rc = 0; + rc |= PyModule_AddIntConstant(simulator, "UNKNOWN", GPI_UNKNOWN); + rc |= PyModule_AddIntConstant(simulator, "MEMORY", GPI_MEMORY); + rc |= PyModule_AddIntConstant(simulator, "MODULE", GPI_MODULE); + rc |= PyModule_AddIntConstant(simulator, "NET", GPI_NET); + rc |= PyModule_AddIntConstant(simulator, "PARAMETER", GPI_PARAMETER); + rc |= PyModule_AddIntConstant(simulator, "REG", GPI_REGISTER); + rc |= PyModule_AddIntConstant(simulator, "NETARRAY", GPI_ARRAY); + rc |= PyModule_AddIntConstant(simulator, "ENUM", GPI_ENUM); + rc |= PyModule_AddIntConstant(simulator, "STRUCTURE", GPI_STRUCTURE); + rc |= PyModule_AddIntConstant(simulator, "REAL", GPI_REAL); + rc |= PyModule_AddIntConstant(simulator, "INTEGER", GPI_INTEGER); + if (rc != 0) + fprintf(stderr, "Failed to add module constants!\n"); +} + #if PY_MAJOR_VERSION >= 3 #include "simulatormodule_python3.c" #else diff --git a/lib/simulator/simulatormodule_python2.c b/lib/simulator/simulatormodule_python2.c index d88eb30c..0471fa71 100644 --- a/lib/simulator/simulatormodule_python2.c +++ b/lib/simulator/simulatormodule_python2.c @@ -26,18 +26,5 @@ MODULE_ENTRY_POINT(void) INITERROR; } - // Make the GPI constants accessible from the C world - int rc = 0; - rc |= PyModule_AddIntConstant(simulator, "UNKNOWN", GPI_UNKNOWN); - rc |= PyModule_AddIntConstant(simulator, "MEMORY", GPI_MEMORY); - rc |= PyModule_AddIntConstant(simulator, "MODULE", GPI_MODULE); - rc |= PyModule_AddIntConstant(simulator, "NET", GPI_NET); - rc |= PyModule_AddIntConstant(simulator, "PARAMETER", GPI_PARAMETER); - rc |= PyModule_AddIntConstant(simulator, "REG", GPI_REGISTER); - rc |= PyModule_AddIntConstant(simulator, "NETARRAY", GPI_ARRAY); - rc |= PyModule_AddIntConstant(simulator, "ENUM", GPI_ENUM); - rc |= PyModule_AddIntConstant(simulator, "STRUCTURE", GPI_STRUCTURE); - rc |= PyModule_AddIntConstant(simulator, "REAL", GPI_REAL); - if (rc != 0) - fprintf(stderr, "Failed to add module constants!\n"); + add_module_constants(simulator); } diff --git a/lib/simulator/simulatormodule_python3.c b/lib/simulator/simulatormodule_python3.c index 38f0d87b..de5ed9b9 100644 --- a/lib/simulator/simulatormodule_python3.c +++ b/lib/simulator/simulatormodule_python3.c @@ -45,21 +45,7 @@ MODULE_ENTRY_POINT(void) INITERROR; } - // Make the GPI constants accessible from the C world - int rc = 0; - rc |= PyModule_AddIntConstant(simulator, "UNKNOWN", GPI_UNKNOWN); - rc |= PyModule_AddIntConstant(simulator, "MEMORY", GPI_MEMORY); - rc |= PyModule_AddIntConstant(simulator, "MODULE", GPI_MODULE); - rc |= PyModule_AddIntConstant(simulator, "NET", GPI_NET); - rc |= PyModule_AddIntConstant(simulator, "PARAMETER", GPI_PARAMETER); - rc |= PyModule_AddIntConstant(simulator, "REG", GPI_REGISTER); - rc |= PyModule_AddIntConstant(simulator, "NETARRAY", GPI_ARRAY); - rc |= PyModule_AddIntConstant(simulator, "ENUM", GPI_ENUM); - rc |= PyModule_AddIntConstant(simulator, "STRUCTURE", GPI_STRUCTURE); - rc |= PyModule_AddIntConstant(simulator, "REAL", GPI_REAL); - if (rc != 0) - fprintf(stderr, "Failed to add module constants!\n"); - + add_module_constants(simulator); return simulator; } diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index c979011d..22384982 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -196,9 +196,14 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string if (vhpiIntVal == value.format) { LOG_DEBUG("Detected an INT type %s", name.c_str()); - gpi_type = GPI_ENUM; + gpi_type = GPI_INTEGER; + } + if (vhpiEnumVal == value.format) { + LOG_DEBUG("Detected an ENUM type %s", name.c_str()); + gpi_type = GPI_ENUM; } + if (vhpiRawDataVal == value.format) { LOG_DEBUG("Detected a custom array type %s", name.c_str()); gpi_type = GPI_MODULE; diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 3f1217e6..b601468d 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -89,10 +89,12 @@ gpi_objtype_t to_gpi_objtype(int32_t vpitype) case vpiEnumNet: case vpiEnumVar: + return GPI_ENUM; + case vpiIntVar: case vpiIntegerVar: case vpiIntegerNet: - return GPI_ENUM; + return GPI_INTEGER; case vpiParameter: return GPI_PARAMETER; From 7ede134a3bd750f8d7857f9ff1773a88867e9db9 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 28 Aug 2015 17:39:48 +0100 Subject: [PATCH 0955/2656] Issue #17: Ensure that each handle returned from the implementations is a singleton using the now populated m_fullname field --- lib/fli/FliImpl.cpp | 5 +++ lib/fli/FliImpl.h | 2 ++ lib/gpi/GpiCbHdl.cpp | 9 +++-- lib/gpi/GpiCommon.cpp | 79 +++++++++++++++++++++++++++++++++++------- lib/gpi/gpi_priv.h | 20 +++++++---- lib/vhpi/VhpiCbHdl.cpp | 50 ++++++++++++++------------ lib/vhpi/VhpiImpl.cpp | 45 +++++++++++++++--------- lib/vhpi/VhpiImpl.h | 11 ++++-- lib/vpi/VpiCbHdl.cpp | 22 +++++++----- lib/vpi/VpiImpl.cpp | 23 +++++++----- lib/vpi/VpiImpl.h | 10 ++++-- 11 files changed, 195 insertions(+), 81 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 00cd74b5..e6dd0381 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -617,6 +617,11 @@ GpiObjHdl *FliImpl::next_handle(GpiIterator *iter) return NULL; } +bool FliImpl::equal(GpiObjHdl* lhs, GpiObjHdl* rhs) +{ + return false; +} + #include diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index 05b03057..c75ec018 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -255,6 +255,8 @@ class FliImpl : public GpiImplInterface { /* Method to provide strings from operation types */ const char *reason_to_string(int reason); + bool equal(GpiObjHdl* lhs, GpiObjHdl* rhs); + private: FliReadOnlyCbHdl m_readonly_cbhdl; FliNextPhaseCbHdl m_nexttime_cbhdl; diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index 5ee35496..ebebd3cc 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -38,6 +38,11 @@ const char * GpiObjHdl::get_fullname_str(void) return m_fullname.c_str(); } +const std::string & GpiObjHdl::get_fullname(void) +{ + return m_fullname; +} + const char * GpiObjHdl::get_type_str(void) { #define CASE_OPTION(_X) \ @@ -112,10 +117,10 @@ int GpiHdl::initialise(std::string &name) return 0; } -int GpiObjHdl::initialise(std::string &name) +int GpiObjHdl::initialise(std::string &name, std::string &fq_name) { m_name = name; - m_fullname = "bleh2"; + m_fullname = fq_name; return 0; } diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 11e25415..ab5cf076 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -32,11 +32,52 @@ #include #include #include +#include using namespace std; static vector registered_impls; +#ifdef SINGLETON_HANDLES + +class GpiHandleStore { +public: + gpi_sim_hdl check_and_store(GpiObjHdl *hdl) { + std::map::iterator it; + + const std::string &name = hdl->get_fullname(); + + it = handle_map.find(name); + if (it == handle_map.end()) { + handle_map[name] = hdl; + return hdl; + } else { + LOG_WARN("Found duplicate %s", name.c_str()); + delete hdl; + return it->second; + } + } + + uint64_t handle_count(void) { + return handle_map.size(); + } + +private: + std::map handle_map; + +}; + +static GpiHandleStore unique_handles; + +#define CHECK_AND_STORE(_x) unique_handles.check_and_store(_x) + +#else + +#define CHECK_AND_STORE(_x) _x + +#endif + + int gpi_print_registered_impl(void) { vector::iterator iter; @@ -163,7 +204,7 @@ gpi_sim_hdl gpi_get_root_handle(const char *name) to find this handle */ vector::iterator iter; - GpiObjHdl *hdl; + GpiObjHdl *hdl = NULL; LOG_DEBUG("Looking for root handle '%s' over %d impls", name, registered_impls.size()); @@ -174,10 +215,14 @@ gpi_sim_hdl gpi_get_root_handle(const char *name) LOG_DEBUG("Got a Root handle (%s) back from %s", hdl->get_name_str(), (*iter)->get_name_c()); - return (gpi_sim_hdl)hdl; + break; } } - return NULL; + + if (hdl) + return CHECK_AND_STORE(hdl); + else + return hdl; } gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) @@ -185,7 +230,7 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) vector::iterator iter; - GpiObjHdl *hdl; + GpiObjHdl *hdl = NULL; GpiObjHdl *base = sim_to_hdl(parent); std::string s_name = name; @@ -205,12 +250,16 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) //std::string &to_query = base->is_this_impl(*iter) ? s_name : fq_name; if ((hdl = (*iter)->native_check_create(s_name, base))) { LOG_DEBUG("Found %s via %s", name, (*iter)->get_name_c()); - return (gpi_sim_hdl)hdl; + break; } } LOG_DEBUG("Failed to find a hdl named %s", name); - return NULL; + + if (hdl) + return CHECK_AND_STORE(hdl); + else + return hdl; } gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) @@ -226,12 +275,16 @@ gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) LOG_DEBUG("Checking if index %d native though impl %s ", index, (*iter)->get_name_c()); if ((hdl = (*iter)->native_check_create(index, base))) { LOG_DEBUG("Found %d via %s", index, (*iter)->get_name_c()); - return (gpi_sim_hdl)hdl; + break; } } - LOG_DEBUG("Failed to find a hdl at index %d", index); - return NULL; + if (hdl) + return CHECK_AND_STORE(hdl); + else { + LOG_DEBUG("Failed to find a hdl at index %d", index); + return hdl; + } } gpi_iterator_hdl gpi_iterate(gpi_sim_hdl base) @@ -246,16 +299,18 @@ gpi_iterator_hdl gpi_iterate(gpi_sim_hdl base) gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) { - gpi_sim_hdl next; + GpiObjHdl *next; GpiIterator *iter = sim_to_hdl(iterator); - next = (gpi_sim_hdl)iter->next_handle(); + next = iter->next_handle(); if (!next) { /* If the iterator returns NULL then it has reached the end, we clear up here before returning */ delete iter; + return next; } - return next; + + return CHECK_AND_STORE(next); } const char *gpi_get_signal_value_binstr(gpi_sim_hdl sig_hdl) diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 5bbcaa1e..e5335e37 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -61,13 +61,13 @@ inline To sim_to_hdl(gpi_sim_hdl input) /* Base GPI class others are derived from */ class GpiHdl { public: - //GpiHdl() : m_impl(NULL) { } + GpiHdl(GpiImplInterface *impl) : m_impl(impl), m_obj_hdl(NULL) { } GpiHdl(GpiImplInterface *impl, void *hdl) : m_impl(impl), m_obj_hdl(hdl) { } virtual ~GpiHdl() { } virtual int initialise(std::string &name); // Post constructor init - template T get_handle(void) { + template T get_handle(void) const { return static_cast(m_obj_hdl); } @@ -111,14 +111,16 @@ class GpiObjHdl : public GpiHdl { virtual const char* get_fullname_str(void); virtual const char* get_type_str(void); virtual gpi_objtype_t get_type(void); - int get_num_elems(void) { + int get_num_elems(void) { LOG_DEBUG("%s has %d elements", m_name.c_str(), m_num_elems); - return m_num_elems; } + return m_num_elems; + } const std::string & get_name(void); + const std::string & get_fullname(void); bool is_native_impl(GpiImplInterface *impl); - virtual int initialise(std::string &name); + virtual int initialise(std::string &name, std::string &full_name); protected: int m_num_elems; @@ -206,10 +208,13 @@ class GpiClockHdl { class GpiIterator : public GpiHdl { public: - GpiIterator(GpiImplInterface *impl, void *hdl) : GpiHdl(impl, hdl) { } + GpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiHdl(impl), + m_parent(hdl) { } virtual ~GpiIterator() { } virtual GpiObjHdl* next_handle() { return NULL; } +protected: + GpiObjHdl *m_parent; }; @@ -241,6 +246,9 @@ class GpiImplInterface { /* Method to provide strings from operation types */ virtual const char * reason_to_string(int reason) = 0; + /* Test equality of two handles */ + virtual bool equal(const GpiObjHdl* lhs, const GpiObjHdl* rhs) = 0; + private: std::string m_name; }; diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index f241c5f6..18448d04 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -40,7 +40,7 @@ VhpiSignalObjHdl::~VhpiSignalObjHdl() free(m_binvalue.value.str); } -int VhpiSignalObjHdl::initialise(std::string &name) { +int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { // Determine the type of object, either scalar or vector m_value.format = vhpiObjTypeVal; m_value.bufSize = 0; @@ -73,7 +73,7 @@ int VhpiSignalObjHdl::initialise(std::string &name) { break; case vhpiRealVal: { - GpiObjHdl::initialise(name); + GpiObjHdl::initialise(name, fq_name); return 0; } @@ -87,7 +87,7 @@ int VhpiSignalObjHdl::initialise(std::string &name) { LOG_CRITICAL("Unable to alloc mem for write buffer"); } LOG_DEBUG("Overriding num_elems to %d", m_num_elems); - GpiObjHdl::initialise(name); + GpiObjHdl::initialise(name, fq_name); return 0; } @@ -104,7 +104,7 @@ int VhpiSignalObjHdl::initialise(std::string &name) { m_num_elems++; } LOG_DEBUG("Found vhpiRawDataVal with %d elements", m_num_elems); - GpiObjHdl::initialise(name); + GpiObjHdl::initialise(name, fq_name); return 0; } @@ -133,7 +133,7 @@ int VhpiSignalObjHdl::initialise(std::string &name) { } out: - return GpiObjHdl::initialise(name); + return GpiObjHdl::initialise(name, fq_name); } VhpiCbHdl::VhpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl) @@ -600,19 +600,20 @@ std::vector* KindMappings::get_options(vhpiClassKindT type) KindMappings VhpiIterator::iterate_over; -VhpiIterator::VhpiIterator(GpiImplInterface *impl, vhpiHandleT hdl) : GpiIterator(impl, hdl), - m_iterator(NULL), - m_iter_obj(hdl) +VhpiIterator::VhpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(impl, hdl), + m_iterator(NULL), + m_iter_obj(NULL) { vhpiHandleT iterator; + vhpiHandleT vhpi_hdl = m_parent->get_handle(); - selected = iterate_over.get_options((vhpiClassKindT)vhpi_get(vhpiKindP, hdl)); + selected = iterate_over.get_options((vhpiClassKindT)vhpi_get(vhpiKindP, vhpi_hdl)); /* Find the first mapping type that yields a valid iterator */ for (one2many = selected->begin(); one2many != selected->end(); one2many++) { - iterator = vhpi_iterator(*one2many, hdl); + iterator = vhpi_iterator(*one2many, vhpi_hdl); if (iterator) break; @@ -621,36 +622,40 @@ VhpiIterator::VhpiIterator(GpiImplInterface *impl, vhpiHandleT hdl) : GpiIterato } if (NULL == iterator) { - std::string name = vhpi_get_str(vhpiCaseNameP, hdl); - LOG_WARN("vhpi_iterate return NULL for all relationships on %s (%d) kind:%s name:%s", name.c_str(), - vhpi_get(vhpiKindP, hdl), - vhpi_get_str(vhpiKindStrP, hdl), - vhpi_get_str(vhpiCaseNameP, hdl)); + std::string name = vhpi_get_str(vhpiCaseNameP, vhpi_hdl); + LOG_WARN("vhpi_iterate return NULL for all relationships on %s (%d) kind:%s name:%s", + name.c_str(), + vhpi_get(vhpiKindP, vhpi_hdl), + vhpi_get_str(vhpiKindStrP, vhpi_hdl), + vhpi_get_str(vhpiCaseNameP, vhpi_hdl)); m_iterator = NULL; return; } LOG_DEBUG("Created iterator working from scope %d (%s)", - vhpi_get(vhpiKindP, hdl), - vhpi_get_str(vhpiKindStrP, hdl)); + vhpi_get(vhpiKindP, vhpi_hdl), + vhpi_get_str(vhpiKindStrP, vhpi_hdl)); /* On some simulators (Aldec) vhpiRootInstK is a null level of hierachy * We check that something is going to come back if not we try the level * down */ - if (vhpiRootInstK == vhpi_get(vhpiKindP, hdl)) { + m_iter_obj = vhpi_hdl; + + if (vhpiRootInstK == vhpi_get(vhpiKindP, vhpi_hdl)) { uint32_t children = 0; vhpiHandleT tmp_hdl; - for(tmp_hdl = vhpi_scan(iterator); tmp_hdl != NULL; tmp_hdl = vhpi_scan(iterator), children++) { } + for(tmp_hdl = vhpi_scan(iterator); + tmp_hdl != NULL; + tmp_hdl = vhpi_scan(iterator), children++) { } - iterator = vhpi_iterator(*one2many, hdl); + iterator = vhpi_iterator(*one2many, vhpi_hdl); // Only a single child, skip a level if (children == 1) { tmp_hdl = vhpi_scan(iterator); vhpi_release_handle(iterator); - iterator = vhpi_iterator(*one2many, tmp_hdl); LOG_WARN("Skipped vhpiRootInstK to get to %s (%s)", vhpi_get_str(vhpiKindStrP, tmp_hdl), @@ -716,6 +721,7 @@ GpiObjHdl *VhpiIterator::next_handle(void) } std::string name = vhpi_get_str(vhpiCaseNameP, obj); + std::string fq_name = m_parent->get_fullname() + "." + name; LOG_DEBUG("vhpi_scan found %s (%d) kind:%s name:%s", name.c_str(), vhpi_get(vhpiKindP, obj), @@ -723,7 +729,7 @@ GpiObjHdl *VhpiIterator::next_handle(void) vhpi_get_str(vhpiCaseNameP, obj)); VhpiImpl *vhpi_impl = reinterpret_cast(m_impl); - new_obj = vhpi_impl->create_gpi_obj_from_handle(obj, name); + new_obj = vhpi_impl->create_gpi_obj_from_handle(obj, name, fq_name); return new_obj; } diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 22384982..f48cef65 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -155,7 +155,9 @@ gpi_objtype_t to_gpi_objtype(vhpiIntT vhpitype) -GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string &name) +GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, + std::string &name, + std::string &fq_name) { vhpiIntT type; gpi_objtype_t gpi_type; @@ -167,7 +169,10 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string } gpi_type = to_gpi_objtype(type); - LOG_DEBUG("Creating %s of type %d (%s)", vhpi_get_str(vhpiFullNameP, new_hdl), gpi_type, vhpi_get_str(vhpiKindStrP, new_hdl)); + LOG_DEBUG("Creating %s of type %d (%s)", + vhpi_get_str(vhpiFullNameP, new_hdl), + gpi_type, + vhpi_get_str(vhpiKindStrP, new_hdl)); /* What sort of isntance is this ?*/ switch (type) { @@ -190,22 +195,22 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string vhpi_get_value(new_hdl, &value); if (vhpiRealVal == value.format) { - LOG_DEBUG("Detected a REAL type %s", name.c_str()); + LOG_DEBUG("Detected a REAL type %s", fq_name.c_str()); gpi_type = GPI_REAL; } if (vhpiIntVal == value.format) { - LOG_DEBUG("Detected an INT type %s", name.c_str()); + LOG_DEBUG("Detected an INT type %s", fq_name.c_str()); gpi_type = GPI_INTEGER; } if (vhpiEnumVal == value.format) { - LOG_DEBUG("Detected an ENUM type %s", name.c_str()); + LOG_DEBUG("Detected an ENUM type %s", fq_name.c_str()); gpi_type = GPI_ENUM; } if (vhpiRawDataVal == value.format) { - LOG_DEBUG("Detected a custom array type %s", name.c_str()); + LOG_DEBUG("Detected a custom array type %s", fq_name.c_str()); gpi_type = GPI_MODULE; } @@ -215,7 +220,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string vhpiLogicVecVal == value.format || vhpiPhysVecVal == value.format || vhpiTimeVecVal == value.format) { - LOG_DEBUG("Detected a vector type", name.c_str()); + LOG_DEBUG("Detected a vector type", fq_name.c_str()); gpi_type = GPI_ARRAY; } @@ -231,11 +236,12 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string new_obj = new GpiObjHdl(this, new_hdl, gpi_type); break; default: - LOG_WARN("Not able to map type (%s) %u to object", vhpi_get_str(vhpiKindStrP, new_hdl), type); + LOG_WARN("Not able to map type (%s) %u to object", + vhpi_get_str(vhpiKindStrP, new_hdl), type); return NULL; } - new_obj->initialise(name); + new_obj->initialise(name, fq_name); return new_obj; } @@ -243,7 +249,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) { vhpiHandleT new_hdl; - std::string fq_name = parent->get_name(); + std::string fq_name = parent->get_fullname(); if (fq_name == ":") { fq_name += name; } else { @@ -259,7 +265,7 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) return NULL; } - GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, fq_name); + GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { vhpi_release_handle(new_hdl); LOG_DEBUG("Unable to fetch object %s", fq_name.c_str()); @@ -309,11 +315,12 @@ GpiObjHdl *VhpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) } std::string name = vhpi_get_str(vhpiNameP, new_hdl); - GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name); + std::string fq_name = parent->get_fullname() + "." + name; + GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { vhpi_release_handle(new_hdl); LOG_DEBUG("Could not fetch object below entity (%s) at index (%u)", - parent->get_name_str(), index); + parent->get_name_str(), index); return NULL; } @@ -365,7 +372,7 @@ GpiObjHdl *VhpiImpl::get_root_handle(const char* name) root_name = found; rv = new GpiObjHdl(this, root, to_gpi_objtype(vhpi_get(vhpiKindP, root))); - rv->initialise(root_name); + rv->initialise(root_name, root_name); FEXIT return rv; @@ -374,9 +381,8 @@ GpiObjHdl *VhpiImpl::get_root_handle(const char* name) GpiIterator *VhpiImpl::iterate_handle(GpiObjHdl *obj_hdl) { VhpiIterator *new_iter; - vhpiHandleT vhpi_hdl = obj_hdl->get_handle(); - new_iter = new VhpiIterator(this, vhpi_hdl); + new_iter = new VhpiIterator(this, obj_hdl); return new_iter; } @@ -429,6 +435,13 @@ int VhpiImpl::deregister_callback(GpiCbHdl *gpi_hdl) return 0; } +bool VhpiImpl::equal(const GpiObjHdl *lhs, const GpiObjHdl *rhs) +{ + vhpiHandleT lhs_handle = lhs->get_handle(); + vhpiHandleT rhs_handle = rhs->get_handle(); + return vhpi_compare_handles(lhs_handle, rhs_handle); +} + void VhpiImpl::sim_end(void) { sim_finish_cb->set_call_state(GPI_DELETE); diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 781ba7b8..ef77e71a 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -169,7 +169,7 @@ class VhpiSignalObjHdl : public GpiSignalObjHdl { /* Value change callback accessor */ GpiCbHdl *value_change_cb(unsigned int edge); - int initialise(std::string &name); + int initialise(std::string &name, std::string &fq_name); private: const vhpiEnumT chr2vhpi(const char value); @@ -194,7 +194,7 @@ class KindMappings { class VhpiIterator : public GpiIterator { public: - VhpiIterator(GpiImplInterface *impl, vhpiHandleT hdl); + VhpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl); virtual ~VhpiIterator(); @@ -236,7 +236,12 @@ class VhpiImpl : public GpiImplInterface { const char * reason_to_string(int reason); const char * format_to_string(int format); - GpiObjHdl *create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string &name); + GpiObjHdl *create_gpi_obj_from_handle(vhpiHandleT new_hdl, + std::string &name, + std::string &fq_name); + + bool equal(const GpiObjHdl* lhs, const GpiObjHdl* rhs); + private: VhpiReadwriteCbHdl m_read_write; VhpiNextPhaseCbHdl m_next_phase; diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 7cdffc17..4d30cc9d 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -123,10 +123,10 @@ int VpiCbHdl::cleanup_callback(void) return 0; } -int VpiSignalObjHdl::initialise(std::string &name) { +int VpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { m_num_elems = vpi_get(vpiSize, GpiObjHdl::get_handle()); LOG_DEBUG("VPI: %s initialised with %d elements", name.c_str(), m_num_elems); - return GpiObjHdl::initialise(name); + return GpiObjHdl::initialise(name, fq_name); } const char* VpiSignalObjHdl::get_signal_value_binstr(void) @@ -466,17 +466,18 @@ std::vector* KindMappings::get_options(int32_t type) KindMappings VpiIterator::iterate_over; -VpiIterator::VpiIterator(GpiImplInterface *impl, vpiHandle hdl) : GpiIterator(impl, hdl), - m_iterator(NULL) +VpiIterator::VpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(impl, hdl), + m_iterator(NULL) { vpiHandle iterator; + vpiHandle vpi_hdl = m_parent->get_handle(); - selected = iterate_over.get_options(vpi_get(vpiType, hdl)); + selected = iterate_over.get_options(vpi_get(vpiType, vpi_hdl)); for (one2many = selected->begin(); one2many != selected->end(); one2many++) { - iterator = vpi_iterate(*one2many, hdl); + iterator = vpi_iterate(*one2many, vpi_hdl); if (iterator) { break; @@ -492,7 +493,7 @@ VpiIterator::VpiIterator(GpiImplInterface *impl, vpiHandle hdl) : GpiIterator(im LOG_DEBUG("Created iterator working from type %d", *one2many, - vpi_get_str(vpiFullName, hdl)); + vpi_get_str(vpiFullName, vpi_hdl)); m_iterator = iterator; } @@ -508,6 +509,7 @@ VpiIterator::~VpiIterator() GpiObjHdl *VpiIterator::next_handle(void) { vpiHandle obj; + vpiHandle iter_obj = m_parent->get_handle(); GpiObjHdl *new_obj = NULL; do { @@ -533,7 +535,7 @@ GpiObjHdl *VpiIterator::next_handle(void) break; } - m_iterator = vpi_iterate(*one2many, get_handle()); + m_iterator = vpi_iterate(*one2many, iter_obj); } while (!obj); @@ -548,9 +550,11 @@ GpiObjHdl *VpiIterator::next_handle(void) else name = name = vpi_get_str(vpiName, obj); + std::string fq_name = m_parent->get_fullname() + "." + name; + LOG_DEBUG("vpi_scan found name:%s", name.c_str()); VpiImpl *vpi_impl = reinterpret_cast(m_impl); - new_obj = vpi_impl->create_gpi_obj_from_handle(obj, name); + new_obj = vpi_impl->create_gpi_obj_from_handle(obj, name, fq_name); return new_obj; } diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index b601468d..dd066531 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -115,7 +115,9 @@ gpi_objtype_t to_gpi_objtype(int32_t vpitype) } } -GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &name) +GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, + std::string &name, + std::string &fq_name) { int32_t type; GpiObjHdl *new_obj = NULL; @@ -156,7 +158,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &n return NULL; } - new_obj->initialise(name); + new_obj->initialise(name, fq_name); return new_obj; } @@ -173,7 +175,7 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) LOG_DEBUG("Unable to query vpi_get_handle_by_name %s", fq_name.c_str()); return NULL; } - GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, fq_name); + GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { vpi_free_object(new_hdl); LOG_ERROR("Unable to query fetch object %s", fq_name.c_str()); @@ -195,11 +197,12 @@ GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) } std::string name = vpi_get_str(vpiFullName, new_hdl); - GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name); + std::string fq_name = parent->get_fullname() + "." + name; + GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { vpi_free_object(new_hdl); LOG_ERROR("Unable to fetch object below entity (%s) at index (%u)", - parent->get_name_str(), index); + parent->get_name_str(), index); return NULL; } return new_obj; @@ -239,7 +242,7 @@ GpiObjHdl *VpiImpl::get_root_handle(const char* name) root_name = vpi_get_str(vpiFullName, root); rv = new GpiObjHdl(this, root, to_gpi_objtype(vpi_get(vpiType, root))); - rv->initialise(root_name); + rv->initialise(root_name, root_name); return rv; @@ -263,9 +266,8 @@ GpiObjHdl *VpiImpl::get_root_handle(const char* name) GpiIterator *VpiImpl::iterate_handle(GpiObjHdl *obj_hdl) { VpiIterator *new_iter; - vpiHandle vpi_hdl = obj_hdl->get_handle(); - new_iter = new VpiIterator(this, vpi_hdl); + new_iter = new VpiIterator(this, obj_hdl); return new_iter; } @@ -316,6 +318,11 @@ int VpiImpl::deregister_callback(GpiCbHdl *gpi_hdl) return 0; } +bool VpiImpl::equal(const GpiObjHdl *lhs, const GpiObjHdl *rhs) +{ + return false; +} + // If the Pything world wants things to shut down then unregister // the callback for end of sim void VpiImpl::sim_end(void) diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 51e3de56..b50263a2 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -204,7 +204,7 @@ class VpiSignalObjHdl : public GpiSignalObjHdl { /* Value change callback accessor */ GpiCbHdl *value_change_cb(unsigned int edge); - int initialise(std::string &name); + int initialise(std::string &name, std::string &fq_name); private: VpiValueCbHdl m_rising_cb; @@ -214,7 +214,7 @@ class VpiSignalObjHdl : public GpiSignalObjHdl { class VpiIterator : public GpiIterator { public: - VpiIterator(GpiImplInterface *impl, vpiHandle hdl); + VpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl); virtual ~VpiIterator(); @@ -252,7 +252,11 @@ class VpiImpl : public GpiImplInterface { GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); const char * reason_to_string(int reason); - GpiObjHdl* create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &name); + GpiObjHdl* create_gpi_obj_from_handle(vpiHandle new_hdl, + std::string &name, + std::string &fq_name); + + bool equal(const GpiObjHdl* lhs, const GpiObjHdl* rhs); private: /* Singleton callbacks */ From 28806a03e834df843dbd545ee4f2943e441851d0 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sat, 29 Aug 2015 08:49:52 +0100 Subject: [PATCH 0956/2656] Issue #17: A couople of stray log messages put in their place, add a failing test for mixed iteration --- lib/gpi/GpiCommon.cpp | 9 +++++---- lib/vpi/VpiCbHdl.cpp | 14 ++++++++++---- .../test_iteration_mixedlang/test_iteration.py | 5 +++++ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index ab5cf076..cdf5dbfc 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -52,7 +52,8 @@ class GpiHandleStore { handle_map[name] = hdl; return hdl; } else { - LOG_WARN("Found duplicate %s", name.c_str()); + LOG_DEBUG("Found duplicate %s", name.c_str()); + delete hdl; return it->second; } @@ -254,12 +255,12 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) } } - LOG_DEBUG("Failed to find a hdl named %s", name); - if (hdl) return CHECK_AND_STORE(hdl); - else + else { + LOG_DEBUG("Failed to find a hdl named %s", name); return hdl; + } } gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 4d30cc9d..ff3854db 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -369,7 +369,6 @@ KindMappings::KindMappings() int32_t module_options[] = { //vpiModule, // Aldec SEGV on mixed language //vpiModuleArray, // Aldec SEGV on mixed language - vpiInternalScope, vpiPort, //vpiIODecl, // Don't care about these vpiNet, @@ -391,6 +390,7 @@ KindMappings::KindMappings() vpiModPath, vpiTchk, vpiAttribute, + vpiInternalScope, //vpiInterface, // Aldec SEGV on mixed language //vpiInterfaceArray, // Aldec SEGV on mixed language 0 @@ -407,6 +407,7 @@ KindMappings::KindMappings() //vpiLocalDriver, //vpiLoad, //vpiLocalLoad, + vpiNetBit, 0 }; add_to_options(vpiNet, &net_options[0]); @@ -435,7 +436,7 @@ KindMappings::KindMappings() /* vpiPort */ int32_t port_options[] = { - vpiBit, + vpiPortBit, 0 }; add_to_options(vpiPort, &port_options[0]); @@ -458,7 +459,7 @@ std::vector* KindMappings::get_options(int32_t type) if (options_map.end() == valid) { LOG_ERROR("VPI: Implementation does not know how to iterate over %d", type); - exit(1); + return NULL; } else { return &valid->second; } @@ -472,7 +473,9 @@ VpiIterator::VpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(i vpiHandle iterator; vpiHandle vpi_hdl = m_parent->get_handle(); - selected = iterate_over.get_options(vpi_get(vpiType, vpi_hdl)); + if (NULL == (selected = iterate_over.get_options(vpi_get(vpiType, vpi_hdl)))) + return; + for (one2many = selected->begin(); one2many != selected->end(); @@ -512,6 +515,9 @@ GpiObjHdl *VpiIterator::next_handle(void) vpiHandle iter_obj = m_parent->get_handle(); GpiObjHdl *new_obj = NULL; + if (!selected) + return NULL; + do { obj = NULL; diff --git a/tests/test_cases/test_iteration_mixedlang/test_iteration.py b/tests/test_cases/test_iteration_mixedlang/test_iteration.py index 85a0e431..6878e5c4 100644 --- a/tests/test_cases/test_iteration_mixedlang/test_iteration.py +++ b/tests/test_cases/test_iteration_mixedlang/test_iteration.py @@ -54,6 +54,11 @@ def recursive_discovery(dut): total = recursive_dump(dut, tlog) tlog.info("Found a total of %d things", total) + if not isinstance(dut.i_verilog.uart1.baud_gen_1.baud_freq, cocotb.handle.ModifiableObject): + tlog.error("Expected dut.i_verilog.uart1.baud_gen_1.baud_freq to be modifiable") + tlog.error("but it was %s" % dut.i_verilog.uart1.baud_gen_1.baud_freq.__class__.__name__) + raise TestError() + @cocotb.test() def recursive_discovery_boundary(dut): From c8115234ba2738d0db5171dbde967a43f484365c Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 30 Aug 2015 11:22:10 +0100 Subject: [PATCH 0957/2656] Issue #17: Try and find modifyable objects before hiearchy and enable singleton handles by default --- lib/gpi/Makefile | 2 +- lib/vpi/VpiCbHdl.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index 09183855..027fde1f 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -30,7 +30,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += -GXX_ARGS += -DVPI_CHECKING -DLIB_EXT=$(LIB_EXT) +GXX_ARGS += -DVPI_CHECKING -DLIB_EXT=$(LIB_EXT) -DSINGLETON_HANDLES LIBS := -lcocotbutils -lgpilog -lcocotb -lstdc++ LD_PATH := -L$(LIB_DIR) LIB_NAME := libgpi diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index ff3854db..b27a6b3f 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -369,7 +369,6 @@ KindMappings::KindMappings() int32_t module_options[] = { //vpiModule, // Aldec SEGV on mixed language //vpiModuleArray, // Aldec SEGV on mixed language - vpiPort, //vpiIODecl, // Don't care about these vpiNet, vpiNetArray, @@ -390,6 +389,7 @@ KindMappings::KindMappings() vpiModPath, vpiTchk, vpiAttribute, + vpiPort, vpiInternalScope, //vpiInterface, // Aldec SEGV on mixed language //vpiInterfaceArray, // Aldec SEGV on mixed language From 99dc07f351855a0976653d4151e9ab74fc8767c4 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 30 Aug 2015 15:23:59 +0100 Subject: [PATCH 0958/2656] Issue #17: Fix poor behaviour of handles in Python * Can't assign to constant objects * Can't traverse hierarhcy of non-hierarchy objects * Can't get/set values on hierarchy objects * Optimise constants access - cache the value once on retrieval --- cocotb/handle.py | 129 ++++++++++-------- include/gpi.h | 3 + lib/gpi/GpiCbHdl.cpp | 5 - lib/gpi/GpiCommon.cpp | 8 ++ lib/gpi/gpi_priv.h | 25 ++-- lib/simulator/simulatormodule.c | 21 +++ lib/simulator/simulatormodule.h | 2 + lib/vhpi/VhpiImpl.cpp | 13 +- lib/vhpi/VhpiImpl.h | 4 +- lib/vpi/VpiImpl.cpp | 6 +- lib/vpi/VpiImpl.h | 4 +- .../test_vhdl_access/test_vhdl_access.py | 2 +- 12 files changed, 143 insertions(+), 79 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index feb13948..c65fbb70 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -89,57 +89,6 @@ def __init__(self, handle): def __hash__(self): return self._handle - def __getattr__(self, name): - """ - Query the simulator for a object with the specified name - and cache the result to build a tree of objects - """ - - if name == "value": - return self._getvalue() - if name in self._sub_handles: - return self._sub_handles[name] - new_handle = simulator.get_handle_by_name(self._handle, name) - - if not new_handle: - if name in self._compat_mapping: - warnings.warn("Use of %s attribute is deprecated" % name) - return getattr(self, self._compat_mapping[name]) - - # To find generated indices we have to discover all - self._discover_all() - if name in self._sub_handles: - return self._sub_handles[name] - raise AttributeError("%s contains no object named %s" % (self._name, name)) - self._sub_handles[name] = SimHandle(new_handle) - return self._sub_handles[name] - - def __hasattr__(self, name): - """ - Since calling hasattr(handle, "something") will print out a - backtrace to the log since usually attempting to access a - non-existent member is an error we provide a 'peek function - - We still add the found handle to our dictionary to prevent leaking - handles. - """ - if name in self._sub_handles: - return self._sub_handles[name] - new_handle = simulator.get_handle_by_name(self._handle, name) - if new_handle: - self._sub_handles[name] = SimHandle(new_handle) - return new_handle - - - def __getitem__(self, index): - if index in self._sub_handles: - return self._sub_handles[index] - new_handle = simulator.get_handle_by_index(self._handle, index) - if not new_handle: - self._raise_testerror("%s contains no object at index %d" % (self._name, index)) - self._sub_handles[index] = SimHandle(new_handle) - return self._sub_handles[index] - def __cmp__(self, other): @@ -148,7 +97,6 @@ def __cmp__(self, other): if self._handle == other._handle: return 0 return 1 - def __repr__(self): return self._fullname @@ -189,6 +137,31 @@ def __setattr__(self, name, value): raise AttributeError("Attempt to access %s which isn't present in %s" %( name, self._name)) + def __getattr__(self, name): + """ + Query the simulator for a object with the specified name + and cache the result to build a tree of objects + """ + if name in self._sub_handles: + return self._sub_handles[name] + # Avoid collision with signals named "value" while maintaining compatability + if name == "value": + return self._getvalue() + new_handle = simulator.get_handle_by_name(self._handle, name) + + if not new_handle: + if name in self._compat_mapping: + warnings.warn("Use of %s attribute is deprecated" % name) + return getattr(self, self._compat_mapping[name]) + + # To find generated indices we have to discover all + self._discover_all() + if name in self._sub_handles: + return self._sub_handles[name] + raise AttributeError("%s contains no object named %s" % (self._name, name)) + self._sub_handles[name] = SimHandle(new_handle) + return self._sub_handles[name] + def __iter__(self): """ Iterate over all known objects in this layer of hierarchy @@ -264,6 +237,32 @@ def _getAttributeNames(self): self._discover_all() return dir(self) + def __hasattr__(self, name): + """ + Since calling hasattr(handle, "something") will print out a + backtrace to the log since usually attempting to access a + non-existent member is an error we provide a 'peek function + + We still add the found handle to our dictionary to prevent leaking + handles. + """ + if name in self._sub_handles: + return self._sub_handles[name] + new_handle = simulator.get_handle_by_name(self._handle, name) + if new_handle: + self._sub_handles[name] = SimHandle(new_handle) + return new_handle + + + def __getitem__(self, index): + if index in self._sub_handles: + return self._sub_handles[index] + new_handle = simulator.get_handle_by_index(self._handle, index) + if not new_handle: + self._raise_testerror("%s contains no object at index %d" % (self._name, index)) + self._sub_handles[index] = SimHandle(new_handle) + return self._sub_handles[index] + class ConstantObject(SimHandleBase): """ Constant objects have a value that can be read, but not set. @@ -271,10 +270,15 @@ class ConstantObject(SimHandleBase): We can also cache the value since it is elaboration time fixed and won't change within a simulation """ - def __init__(self, handle, *args, **kwargs): + def __init__(self, handle, handle_type): SimHandleBase.__init__(self, handle) - self._value = None - + if handle_type in [simulator.INTEGER, simulator.ENUM]: + self._value = simulator.get_signal_val_long(self._handle) + elif handle_type == simulator.REAL: + self._value = simulator.get_signal_val_real(self._handle) + else: + self._value = BinaryValue() + self._value.binstr = simulator.get_signal_val_str(self._handle) def __int__(self): return int(self._value) @@ -282,6 +286,15 @@ def __int__(self): def __repr__(self): return repr(int(self)) + def __cmp__(self, other): + # Use the comparison method of the other object against our value + return self._value.__cmp__(other) + + def _setcachedvalue(self, *args, **kwargs): + raise ValueError("Not permissible to set values on a constant object") + + def __le__(self, *args, **kwargs): + raise ValueError("Not permissible to set values on a constant object") class NonConstantObject(SimHandleBase): def __init__(self, handle): @@ -354,13 +367,13 @@ def __cmp__(self, other): # Use the comparison method of the other object against our value return self.value.__cmp__(other) - def __int__(self): return int(self.value) def __repr__(self): return repr(int(self)) + class ModifiableObject(NonConstantObject): """ Base class for simulator objects whose values can be modified @@ -514,6 +527,10 @@ def SimHandle(handle): simulator.ENUM: IntegerObject, } + # Special case for constants + if simulator.get_const(handle): + return ConstantObject(handle, simulator.get_type(handle)) + t = simulator.get_type(handle) if t not in _type2cls: raise TestError("Couldn't find a matching object for GPI type %d" % t) diff --git a/include/gpi.h b/include/gpi.h index 222782cf..06d9910f 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -164,6 +164,9 @@ const char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl); // Returns on of the types defined above e.g. gpiMemory etc. gpi_objtype_t gpi_get_object_type(gpi_sim_hdl gpi_hdl); +// Determine whether an object value is constant (parameters / generics etc) +int gpi_is_constant(gpi_sim_hdl gpi_hdl); + // Functions for setting the properties of a handle void gpi_set_signal_value_real(gpi_sim_hdl gpi_hdl, double value); diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index ebebd3cc..002f40ed 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -69,11 +69,6 @@ const char * GpiObjHdl::get_type_str(void) return ret; } -gpi_objtype_e GpiObjHdl::get_type(void) -{ - return m_type; -} - const std::string & GpiObjHdl::get_name(void) { return m_name; diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index cdf5dbfc..447f4bc1 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -350,6 +350,14 @@ gpi_objtype_t gpi_get_object_type(gpi_sim_hdl sig_hdl) return obj_hdl->get_type(); } +int gpi_is_constant(gpi_sim_hdl sig_hdl) +{ + GpiObjHdl *obj_hdl = sim_to_hdl(sig_hdl); + if (obj_hdl->get_const()) + return 1; + return 0; +} + void gpi_set_signal_value_long(gpi_sim_hdl sig_hdl, long value) { GpiSignalObjHdl *obj_hdl = sim_to_hdl(sig_hdl); diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index e5335e37..0590338b 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -92,25 +92,29 @@ class GpiHdl { // that construct an object derived from GpiSignalObjHdl or GpiObjHdl class GpiObjHdl : public GpiHdl { public: - GpiObjHdl(std::string name) : GpiHdl(NULL, NULL), - m_num_elems(0), - m_name(name), - m_fullname("unknown") { } GpiObjHdl(GpiImplInterface *impl) : GpiHdl(impl, NULL), m_num_elems(0), m_fullname("unknown"), - m_type(GPI_UNKNOWN) { } + m_type(GPI_UNKNOWN), + m_const(false) { } GpiObjHdl(GpiImplInterface *impl, void *hdl, gpi_objtype_t objtype) : GpiHdl(impl, hdl), m_num_elems(0), m_fullname("unknown"), - m_type(objtype) { } - + m_type(objtype), + m_const(false) { } + GpiObjHdl(GpiImplInterface *impl, void *hdl, gpi_objtype_t objtype, bool is_const) : + GpiHdl(impl, hdl), + m_num_elems(0), + m_fullname("unknown"), + m_type(objtype), + m_const(is_const) { } virtual ~GpiObjHdl() { } virtual const char* get_name_str(void); virtual const char* get_fullname_str(void); virtual const char* get_type_str(void); - virtual gpi_objtype_t get_type(void); + gpi_objtype_t get_type(void) { return m_type; }; + bool get_const(void) { return m_const; }; int get_num_elems(void) { LOG_DEBUG("%s has %d elements", m_name.c_str(), m_num_elems); return m_num_elems; @@ -127,6 +131,7 @@ class GpiObjHdl : public GpiHdl { std::string m_name; std::string m_fullname; gpi_objtype_t m_type; + bool m_const; }; @@ -136,8 +141,8 @@ class GpiObjHdl : public GpiHdl { // value of the signal (which doesn't apply to non signal items in the hierarchy class GpiSignalObjHdl : public GpiObjHdl { public: - GpiSignalObjHdl(GpiImplInterface *impl, void *hdl, gpi_objtype_t objtype) : - GpiObjHdl(impl, hdl, objtype), + GpiSignalObjHdl(GpiImplInterface *impl, void *hdl, gpi_objtype_t objtype, bool is_const) : + GpiObjHdl(impl, hdl, objtype, is_const), m_length(0) { } virtual ~GpiSignalObjHdl() { } // Provide public access to the implementation (composition vs inheritance) diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 7f3ba97e..82290edc 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -776,6 +776,27 @@ static PyObject *get_type(PyObject *self, PyObject *args) return pyresult; } +static PyObject *get_const(PyObject *self, PyObject *args) +{ + int result; + gpi_sim_hdl hdl; + PyObject *pyresult; + + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + if (!PyArg_ParseTuple(args, "l", &hdl)) { + DROP_GIL(gstate); + return NULL; + } + + result = gpi_is_constant((gpi_sim_hdl)hdl); + pyresult = Py_BuildValue("i", result); + + DROP_GIL(gstate); + + return pyresult; +} static PyObject *get_type_string(PyObject *self, PyObject *args) { diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 93b278b1..6668b903 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -68,6 +68,7 @@ static PyObject *get_handle_by_index(PyObject *self, PyObject *args); static PyObject *get_root_handle(PyObject *self, PyObject *args); static PyObject *get_name_string(PyObject *self, PyObject *args); static PyObject *get_type(PyObject *self, PyObject *args); +static PyObject *get_const(PyObject *self, PyObject *args); static PyObject *get_type_string(PyObject *self, PyObject *args); static PyObject *get_num_elems(PyObject *self, PyObject *args); static PyObject *register_timed_callback(PyObject *self, PyObject *args); @@ -97,6 +98,7 @@ static PyMethodDef SimulatorMethods[] = { {"get_name_string", get_name_string, METH_VARARGS, "Get the name of an object as a string"}, {"get_type_string", get_type_string, METH_VARARGS, "Get the type of an object as a string"}, {"get_type", get_type, METH_VARARGS, "Get the type of an object, mapped to a GPI enumeration"}, + {"get_const", get_const, METH_VARARGS, "Get a flag indicating whether the object is a constant"}, {"get_num_elems", get_num_elems, METH_VARARGS, "Get the number of elements contained in the handle"}, {"register_timed_callback", register_timed_callback, METH_VARARGS, "Register a timed callback"}, {"register_value_change_callback", register_value_change_callback, METH_VARARGS, "Register a signal change callback"}, diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index f48cef65..ccd3712c 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -112,6 +112,17 @@ void VhpiImpl::get_sim_time(uint32_t *high, uint32_t *low) *low = vhpi_time_s.low; } +// Determine whether a VHPI object type is a constant or not +bool is_const(vhpiIntT vhpitype) +{ + switch (vhpitype) { + case vhpiConstDeclK: + case vhpiGenericDeclK: + return true; + default: + return false; + } +} gpi_objtype_t to_gpi_objtype(vhpiIntT vhpitype) { @@ -224,7 +235,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, gpi_type = GPI_ARRAY; } - new_obj = new VhpiSignalObjHdl(this, new_hdl, gpi_type); + new_obj = new VhpiSignalObjHdl(this, new_hdl, gpi_type, is_const(type)); break; } case vhpiForGenerateK: diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index ef77e71a..9636b08b 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -151,8 +151,8 @@ class VhpiReadwriteCbHdl : public VhpiCbHdl { class VhpiSignalObjHdl : public GpiSignalObjHdl { public: - VhpiSignalObjHdl(GpiImplInterface *impl, vhpiHandleT hdl, gpi_objtype_t objtype) : - GpiSignalObjHdl(impl, hdl, objtype), + VhpiSignalObjHdl(GpiImplInterface *impl, vhpiHandleT hdl, gpi_objtype_t objtype, bool is_const) : + GpiSignalObjHdl(impl, hdl, objtype, is_const), m_rising_cb(impl, this, GPI_RISING), m_falling_cb(impl, this, GPI_FALLING), m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index dd066531..1fe1d416 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -132,7 +132,6 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, case vpiNetBit: case vpiReg: case vpiRegBit: - case vpiParameter: case vpiRegArray: case vpiNetArray: case vpiEnumNet: @@ -141,7 +140,10 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, case vpiIntegerVar: case vpiIntegerNet: case vpiRealVar: - new_obj = new VpiSignalObjHdl(this, new_hdl, to_gpi_objtype(type)); + new_obj = new VpiSignalObjHdl(this, new_hdl, to_gpi_objtype(type), false); + break; + case vpiParameter: + new_obj = new VpiSignalObjHdl(this, new_hdl, to_gpi_objtype(type), true); break; case vpiStructVar: case vpiModule: diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index b50263a2..a12156a0 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -187,8 +187,8 @@ class KindMappings { class VpiSignalObjHdl : public GpiSignalObjHdl { public: - VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl, gpi_objtype_t objtype) : - GpiSignalObjHdl(impl, hdl, objtype), + VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl, gpi_objtype_t objtype, bool is_const) : + GpiSignalObjHdl(impl, hdl, objtype, is_const), m_rising_cb(impl, this, GPI_RISING), m_falling_cb(impl, this, GPI_FALLING), m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } diff --git a/tests/test_cases/test_vhdl_access/test_vhdl_access.py b/tests/test_cases/test_vhdl_access/test_vhdl_access.py index 1191280b..0b285c16 100644 --- a/tests/test_cases/test_vhdl_access/test_vhdl_access.py +++ b/tests/test_cases/test_vhdl_access/test_vhdl_access.py @@ -75,7 +75,7 @@ def check_instance(obj, objtype): try: dut.inst_axi4s_buffer.DATA_WIDTH <= 42 - tlog.error("Shouldn't be allowed to set a value on constant object") + tlog.error("Shouldn't be allowed to set a value on constant object using __le__") fails += 1 except ValueError as e: pass From 49d0516d7977b4598f25555923b6a89dcbfa997f Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 30 Aug 2015 15:49:37 +0100 Subject: [PATCH 0959/2656] Issue #17: Tests now pass * Fix failing testcases * Revert backwards incompatible change _setimmediatevalue * Mark enumeration detection as incomplete in VHDL --- cocotb/handle.py | 30 ++++++++++++++----- cocotb/scheduler.py | 2 +- lib/vhpi/VhpiImpl.cpp | 5 ---- .../test_iteration/test_iteration.py | 3 +- .../test_vhdl_access/test_vhdl_access.py | 11 ++++++- 5 files changed, 36 insertions(+), 15 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index c65fbb70..b0838cf6 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -68,7 +68,6 @@ class SimHandleBase(object): "log" : "_log", "fullname" : "_fullname", "name" : "_name", - "setimmediatevalue" : "_setimmediatevalue" } def __init__(self, handle): @@ -263,7 +262,21 @@ def __getitem__(self, index): self._sub_handles[index] = SimHandle(new_handle) return self._sub_handles[index] -class ConstantObject(SimHandleBase): +class NonHierarchyObject(SimHandleBase): + + """ + Common base class for all non-hierarchy objects + """ + + def __init__(self, handle): + SimHandleBase.__init__(self, handle) + for name, attribute in SimHandleBase._compat_mapping.items(): + setattr(self, name, getattr(self, attribute)) + + def __iter__(self): + raise StopIteration + +class ConstantObject(NonHierarchyObject): """ Constant objects have a value that can be read, but not set. @@ -271,7 +284,7 @@ class ConstantObject(SimHandleBase): change within a simulation """ def __init__(self, handle, handle_type): - SimHandleBase.__init__(self, handle) + NonHierarchyObject.__init__(self, handle) if handle_type in [simulator.INTEGER, simulator.ENUM]: self._value = simulator.get_signal_val_long(self._handle) elif handle_type == simulator.REAL: @@ -296,13 +309,13 @@ def _setcachedvalue(self, *args, **kwargs): def __le__(self, *args, **kwargs): raise ValueError("Not permissible to set values on a constant object") -class NonConstantObject(SimHandleBase): +class NonConstantObject(NonHierarchyObject): def __init__(self, handle): """ Args: _handle [integer] : vpi/vhpi handle to the simulator object """ - SimHandleBase.__init__(self, handle) + NonHierarchyObject.__init__(self, handle) self._r_edge = _RisingEdge(self) self._f_edge = _FallingEdge(self) @@ -368,6 +381,9 @@ def __cmp__(self, other): return self.value.__cmp__(other) def __int__(self): + print "Getting value..." + val = self.value + print "Got %s (%s)" % (type(val), repr(val)) return int(self.value) def __repr__(self): @@ -383,7 +399,7 @@ def __setitem__(self, index, value): """Provide transparent assignment to bit index""" self.__getitem__(index)._setcachedvalue(value) - def _setimmediatevalue(self, value): + def setimmediatevalue(self, value): """ Set the value of the underlying simulation object to value. @@ -449,7 +465,7 @@ class RealObject(ModifiableObject): Specific object handle for Real signals and variables """ - def _setimmediatevalue(self, value): + def setimmediatevalue(self, value): """ Set the value of the underlying simulation object to value. diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 3e72ecb2..6c7ea642 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -264,7 +264,7 @@ def react(self, trigger, depth=0): while self._writes: handle, value = self._writes.popitem() - handle._setimmediatevalue(value) + handle.setimmediatevalue(value) self._readwrite.unprime() diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index ccd3712c..0707f78e 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -215,11 +215,6 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, gpi_type = GPI_INTEGER; } - if (vhpiEnumVal == value.format) { - LOG_DEBUG("Detected an ENUM type %s", fq_name.c_str()); - gpi_type = GPI_ENUM; - } - if (vhpiRawDataVal == value.format) { LOG_DEBUG("Detected a custom array type %s", fq_name.c_str()); gpi_type = GPI_MODULE; diff --git a/tests/test_cases/test_iteration/test_iteration.py b/tests/test_cases/test_iteration/test_iteration.py index 06768eab..d76ecac5 100644 --- a/tests/test_cases/test_iteration/test_iteration.py +++ b/tests/test_cases/test_iteration/test_iteration.py @@ -76,9 +76,10 @@ def dual_iteration(dut): @cocotb.test() def get_clock(dut): + dut.log.info("dut.aclk is %s" % dut.aclk.__class__.__name__) dut.aclk <= 0 yield Timer(1) dut.aclk <= 1 yield Timer(1) if int(dut.aclk) is not 1: - raise TestFailure("dut.aclk is not what we expected") + raise TestFailure("dut.aclk is not what we expected (got %d)" % int(dut.aclk)) diff --git a/tests/test_cases/test_vhdl_access/test_vhdl_access.py b/tests/test_cases/test_vhdl_access/test_vhdl_access.py index 0b285c16..f2a4bd0e 100644 --- a/tests/test_cases/test_vhdl_access/test_vhdl_access.py +++ b/tests/test_cases/test_vhdl_access/test_vhdl_access.py @@ -30,6 +30,16 @@ from cocotb.triggers import Timer from cocotb.result import TestError, TestFailure +@cocotb.test(expect_fail=True) +def check_enum_object(dut): + """ + Enumerations currently behave as normal signals + + TODO: Implement an EnumObject class and detect valid string mappings + """ + yield Timer(100) + if not isinstance(dut.inst_ram_ctrl.write_ram_fsm, IntegerObject): + raise TestFailure("Expected the FSM enum to be an InterObject") @cocotb.test() def check_objects(dut): @@ -59,7 +69,6 @@ def check_instance(obj, objtype): fails += check_instance(dut.current_active, IntegerObject) fails += check_instance(dut.inst_axi4s_buffer.DATA_WIDTH, ConstantObject) fails += check_instance(dut.inst_ram_ctrl, HierarchyObject) - fails += check_instance(dut.inst_ram_ctrl.write_ram_fsm, IntegerObject) if dut.inst_axi4s_buffer.DATA_WIDTH != 32: tlog.error("Expected dut.inst_axi4s_buffer.DATA_WIDTH to be 32 but got %d", From 58ac30e82a4e6d9aff89da9c408d7971d0c68251 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 30 Aug 2015 15:52:56 +0100 Subject: [PATCH 0960/2656] Issue #17: Cleanup remove debugging prints --- cocotb/handle.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index b0838cf6..9acb04a0 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -381,9 +381,7 @@ def __cmp__(self, other): return self.value.__cmp__(other) def __int__(self): - print "Getting value..." val = self.value - print "Got %s (%s)" % (type(val), repr(val)) return int(self.value) def __repr__(self): From 6c48c9e2c7529289c4ff6fb5438952b371df0653 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 30 Aug 2015 19:55:08 +0100 Subject: [PATCH 0961/2656] Issue #17: Add iterators for driver/load information in VPI --- cocotb/handle.py | 54 ++++++++++++++----- cocotb/scheduler.py | 1 + include/gpi.h | 10 +++- lib/gpi/GpiCommon.cpp | 4 +- lib/gpi/gpi_priv.h | 2 +- lib/simulator/simulatormodule.c | 9 +++- lib/vhpi/VhpiImpl.cpp | 13 +++-- lib/vhpi/VhpiImpl.h | 2 +- lib/vpi/VpiCbHdl.cpp | 29 +++++++++- lib/vpi/VpiImpl.cpp | 20 +++++-- lib/vpi/VpiImpl.h | 30 ++++++++++- .../test_iteration/test_iteration.py | 3 +- .../test_iteration.py | 20 ++++++- 13 files changed, 165 insertions(+), 32 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 9acb04a0..4f6bc0da 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -189,8 +189,8 @@ def _discover_all(self): do this once. """ if self._discovered: return - self._log.warning("Discovering all on %s", self._name) - iterator = simulator.iterate(self._handle) + self._log.debug("Discovering all on %s", self._name) + iterator = simulator.iterate(self._handle, simulator.OBJECTS) while True: try: thing = simulator.next(iterator) @@ -274,7 +274,7 @@ def __init__(self, handle): setattr(self, name, getattr(self, attribute)) def __iter__(self): - raise StopIteration + return iter(()) class ConstantObject(NonHierarchyObject): """ @@ -291,14 +291,15 @@ def __init__(self, handle, handle_type): self._value = simulator.get_signal_val_real(self._handle) else: self._value = BinaryValue() - self._value.binstr = simulator.get_signal_val_str(self._handle) + try: + self._value.binstr = simulator.get_signal_val_str(self._handle) + except: + print "*** GOT %s" % simulator.get_signal_val_str(self._handle) + self._value = simulator.get_signal_val_str(self._handle) def __int__(self): return int(self._value) - def __repr__(self): - return repr(int(self)) - def __cmp__(self, other): # Use the comparison method of the other object against our value return self._value.__cmp__(other) @@ -328,7 +329,6 @@ def __str__(self): def __getitem__(self, index): if index in self._sub_handles: return self._sub_handles[index] - self._log.info("Calling get handle for %s" % self._name) new_handle = simulator.get_handle_by_index(self._handle, index) if not new_handle: self._raise_testerror("%s %s contains no object at index %d" % (self._name, simulator.get_type(self._handle), index)) @@ -342,7 +342,6 @@ def __iter__(self): for i in range(len(self)): try: result = self[i] - self._log.warning("Yielding %s" % result) yield result except: continue @@ -388,6 +387,23 @@ def __repr__(self): return repr(int(self)) + def drivers(self): + """ + An iterator for gathering all drivers for a signal + """ + iterator = simulator.iterate(self._handle, simulator.DRIVERS) + while True: + yield SimHandle(simulator.next(iterator)) + + def loads(self): + """ + An iterator for gathering all drivers for a signal + """ + iterator = simulator.iterate(self._handle, simulator.LOADS) + while True: + yield SimHandle(simulator.next(iterator)) + + class ModifiableObject(NonConstantObject): """ Base class for simulator objects whose values can be modified @@ -527,11 +543,12 @@ def __int__(self): return self._getvalue() +_handle2obj = {} + def SimHandle(handle): """ Factory function to create the correct type of SimHandle object """ - _type2cls = { simulator.MODULE: HierarchyObject, simulator.REG: ModifiableObject, @@ -541,12 +558,23 @@ def SimHandle(handle): simulator.ENUM: IntegerObject, } + # Enforce singletons since it's possible to retrieve handles avoiding + # the hierarchy by getting driver/load information + global _handle2obj + try: + return _handle2obj[handle] + except KeyError: + pass + # Special case for constants if simulator.get_const(handle): - return ConstantObject(handle, simulator.get_type(handle)) + obj = ConstantObject(handle, simulator.get_type(handle)) + _handle2obj[handle] = obj + return obj t = simulator.get_type(handle) if t not in _type2cls: raise TestError("Couldn't find a matching object for GPI type %d" % t) - return _type2cls[t](handle) - + obj = _type2cls[t](handle) + _handle2obj[handle] = obj + return obj diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 6c7ea642..53fdbc61 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -469,6 +469,7 @@ def schedule(self, coroutine, trigger=None): except TestComplete as test_result: # Tag that close down is needed, save the test_result # for later use in cleanup handler + self.log.debug("TestComplete received: %s" % test_result.__class__.__name__) self.finish_test(test_result) return diff --git a/include/gpi.h b/include/gpi.h index 06d9910f..998dcbe3 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -138,12 +138,20 @@ typedef enum gpi_objtype_e { GPI_INTEGER = 10 } gpi_objtype_t; +// When iterating, we can chose to either get child objects, drivers or loads +typedef enum gpi_iterator_sel_e { + GPI_OBJECTS = 1, + GPI_DRIVERS = 2, + GPI_LOADS = 3, +} gpi_iterator_sel_t; + + // Functions for iterating over entries of a handle // Returns an iterator handle which can then be used in gpi_next calls // // NB the iterator handle may be NULL if no objects of the requested type are // found -gpi_iterator_hdl gpi_iterate(gpi_sim_hdl base); +gpi_iterator_hdl gpi_iterate(gpi_sim_hdl base, gpi_iterator_sel_t type); // Returns NULL when there are no more objects gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator); diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 447f4bc1..6f6cacbf 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -288,10 +288,10 @@ gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) } } -gpi_iterator_hdl gpi_iterate(gpi_sim_hdl base) +gpi_iterator_hdl gpi_iterate(gpi_sim_hdl base, gpi_iterator_sel_t type) { GpiObjHdl *obj_hdl = sim_to_hdl(base); - GpiIterator *iter = obj_hdl->m_impl->iterate_handle(obj_hdl); + GpiIterator *iter = obj_hdl->m_impl->iterate_handle(obj_hdl, type); if (!iter) { return NULL; } diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 0590338b..a2888fb7 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -238,7 +238,7 @@ class GpiImplInterface { virtual GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent) = 0; virtual GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent) = 0; virtual GpiObjHdl *get_root_handle(const char *name) = 0; - virtual GpiIterator *iterate_handle(GpiObjHdl *obj_hdl) = 0; + virtual GpiIterator *iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) = 0; virtual GpiObjHdl *next_handle(GpiIterator *iter) = 0; /* Callback related, these may (will) return the same handle*/ diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 82290edc..385fdac5 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -466,18 +466,19 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) static PyObject *iterate(PyObject *self, PyObject *args) { gpi_sim_hdl hdl; + int type; gpi_iterator_hdl result; PyObject *res; PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { + if (!PyArg_ParseTuple(args, "li", &hdl, &type)) { DROP_GIL(gstate); return NULL; } - result = gpi_iterate(hdl); + result = gpi_iterate(hdl, (gpi_iterator_sel_t)type); res = Py_BuildValue("l", result); @@ -910,6 +911,10 @@ static void add_module_constants(PyObject* simulator) rc |= PyModule_AddIntConstant(simulator, "STRUCTURE", GPI_STRUCTURE); rc |= PyModule_AddIntConstant(simulator, "REAL", GPI_REAL); rc |= PyModule_AddIntConstant(simulator, "INTEGER", GPI_INTEGER); + rc |= PyModule_AddIntConstant(simulator, "OBJECTS", GPI_OBJECTS); + rc |= PyModule_AddIntConstant(simulator, "DRIVERS", GPI_DRIVERS); + rc |= PyModule_AddIntConstant(simulator, "LOADS", GPI_LOADS); + if (rc != 0) fprintf(stderr, "Failed to add module constants!\n"); } diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 0707f78e..6939e783 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -384,11 +384,18 @@ GpiObjHdl *VhpiImpl::get_root_handle(const char* name) return rv; } -GpiIterator *VhpiImpl::iterate_handle(GpiObjHdl *obj_hdl) +GpiIterator *VhpiImpl::iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) { - VhpiIterator *new_iter; + GpiIterator *new_iter; - new_iter = new VhpiIterator(this, obj_hdl); + switch (type) { + case GPI_OBJECTS: + new_iter = new VhpiIterator(this, obj_hdl); + break; + default: + LOG_WARN("Other iterator types not implemented yet"); + break; + } return new_iter; } diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 9636b08b..6cc13ed5 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -221,7 +221,7 @@ class VhpiImpl : public GpiImplInterface { /* Hierachy related */ GpiObjHdl *get_root_handle(const char *name); - GpiIterator *iterate_handle(GpiObjHdl *obj_hdl); + GpiIterator *iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type); GpiObjHdl *next_handle(GpiIterator *iter); /* Callback related, these may (will) return the same handle*/ diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index b27a6b3f..c2f99246 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -465,6 +465,7 @@ std::vector* KindMappings::get_options(int32_t type) } } + KindMappings VpiIterator::iterate_over; VpiIterator::VpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(impl, hdl), @@ -485,7 +486,7 @@ VpiIterator::VpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(i if (iterator) { break; } - + LOG_DEBUG("vpi_iterate type=%d returned NULL", *one2many); } @@ -509,6 +510,32 @@ VpiIterator::~VpiIterator() LOG_DEBUG("Deleted VpiIterator"); } +GpiObjHdl *VpiSingleIterator::next_handle(void) +{ + vpiHandle obj; + GpiObjHdl *new_obj = NULL; + + if (NULL == m_iterator) + return new_obj; + + obj = vpi_scan(m_iterator); + if (NULL == obj) + return new_obj; + + std::string name; + if (vpiPort == vpi_get(vpiType, obj)) + name = vpi_get_str(vpiName, obj); + else + name = vpi_get_str(vpiName, obj); + std::string fq_name = m_parent->get_fullname() + "." + name; + + LOG_DEBUG("vpi_scan found name:%s", name.c_str()); + + VpiImpl *vpi_impl = reinterpret_cast(m_impl); + new_obj = vpi_impl->create_gpi_obj_from_handle(obj, name, fq_name); + return new_obj; +} + GpiObjHdl *VpiIterator::next_handle(void) { vpiHandle obj; diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 1fe1d416..115f897e 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -265,11 +265,23 @@ GpiObjHdl *VpiImpl::get_root_handle(const char* name) return NULL; } -GpiIterator *VpiImpl::iterate_handle(GpiObjHdl *obj_hdl) +GpiIterator *VpiImpl::iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) { - VpiIterator *new_iter; - - new_iter = new VpiIterator(this, obj_hdl); + GpiIterator *new_iter; + switch (type) { + case GPI_OBJECTS: + new_iter = new VpiIterator(this, obj_hdl); + break; + case GPI_DRIVERS: + new_iter = new VpiSingleIterator(this, obj_hdl, vpiDriver); + break; + case GPI_LOADS: + new_iter = new VpiSingleIterator(this, obj_hdl, vpiLoad); + break; + default: + LOG_WARN("Other iterator types not implemented yet"); + break; + } return new_iter; } diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index a12156a0..b49c98e0 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -212,10 +212,11 @@ class VpiSignalObjHdl : public GpiSignalObjHdl { VpiValueCbHdl m_either_cb; }; + class VpiIterator : public GpiIterator { public: VpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl); - + virtual ~VpiIterator(); GpiObjHdl *next_handle(void); @@ -227,6 +228,31 @@ class VpiIterator : public GpiIterator { std::vector::iterator one2many; }; +// Base class for simple iterator that only iterates over a single type +class VpiSingleIterator : public GpiIterator { +public: + VpiSingleIterator(GpiImplInterface *impl, + GpiObjHdl *hdl, + int32_t vpitype) : GpiIterator(impl, hdl), + m_iterator(NULL) + + { + vpiHandle vpi_hdl = m_parent->get_handle(); + m_iterator = vpi_iterate(vpitype, vpi_hdl); + if (NULL == m_iterator) { + LOG_WARN("vpi_iterate returned NULL for %d", vpitype); + return; + } + } + + virtual ~VpiSingleIterator() { } + GpiObjHdl *next_handle(void); + +protected: + vpiHandle m_iterator; +}; + + class VpiImpl : public GpiImplInterface { public: VpiImpl(const std::string& name) : GpiImplInterface(name), @@ -240,7 +266,7 @@ class VpiImpl : public GpiImplInterface { /* Hierachy related */ GpiObjHdl *get_root_handle(const char *name); - GpiIterator *iterate_handle(GpiObjHdl *obj_hdl); + GpiIterator *iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type); GpiObjHdl *next_handle(GpiIterator *iter); /* Callback related, these may (will) return the same handle*/ diff --git a/tests/test_cases/test_iteration/test_iteration.py b/tests/test_cases/test_iteration/test_iteration.py index d76ecac5..9ff0a416 100644 --- a/tests/test_cases/test_iteration/test_iteration.py +++ b/tests/test_cases/test_iteration/test_iteration.py @@ -45,7 +45,8 @@ def dump_all_the_things(parent): return count total = dump_all_the_things(dut) tlog.info("Found a total of %d things", total) - + if total != 32290: + raise TestFailure("Expected 32290 objects but found %d" % total) @cocotb.test() diff --git a/tests/test_cases/test_iteration_mixedlang/test_iteration.py b/tests/test_cases/test_iteration_mixedlang/test_iteration.py index 6878e5c4..3aea79ad 100644 --- a/tests/test_cases/test_iteration_mixedlang/test_iteration.py +++ b/tests/test_cases/test_iteration_mixedlang/test_iteration.py @@ -44,6 +44,22 @@ def recursive_dump(parent, log): return count +@cocotb.test(expect_fail=True) +def test_drivers(dut): + """ + Try iterating over drivers of a signal. + + Seems that few simulators implement vpiDriver + """ + tlog = logging.getLogger("cocotb.test") + yield Timer(100) + for driver in dut.i_verilog.uart1.uart_rx_1.rx_data.drivers(): + tlog.info("Found %s" % repr(driver)) + break + else: + raise TestFailure("No drivers found for dut.i_verilog.uart1.uart_rx_1.rx_data") + + @cocotb.test() def recursive_discovery(dut): """ @@ -57,7 +73,7 @@ def recursive_discovery(dut): if not isinstance(dut.i_verilog.uart1.baud_gen_1.baud_freq, cocotb.handle.ModifiableObject): tlog.error("Expected dut.i_verilog.uart1.baud_gen_1.baud_freq to be modifiable") tlog.error("but it was %s" % dut.i_verilog.uart1.baud_gen_1.baud_freq.__class__.__name__) - raise TestError() + raise TestFailure() @cocotb.test() @@ -72,4 +88,6 @@ def recursive_discovery_boundary(dut): yield Timer(100) total = recursive_dump(dut.i_vhdl, tlog) tlog.info("Found a total of %d things", total) + if total != 426: + raise TestFailure("Expected 426 objects but found %d" % total) From 6de02b80e320b695218f48bfe743e0073e432dc5 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Mon, 31 Aug 2015 17:45:22 +0100 Subject: [PATCH 0962/2656] Issue #17: Fix review comments from pull request --- cocotb/handle.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 4f6bc0da..3b6448cf 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -287,15 +287,17 @@ def __init__(self, handle, handle_type): NonHierarchyObject.__init__(self, handle) if handle_type in [simulator.INTEGER, simulator.ENUM]: self._value = simulator.get_signal_val_long(self._handle) + return elif handle_type == simulator.REAL: self._value = simulator.get_signal_val_real(self._handle) - else: - self._value = BinaryValue() - try: - self._value.binstr = simulator.get_signal_val_str(self._handle) - except: - print "*** GOT %s" % simulator.get_signal_val_str(self._handle) - self._value = simulator.get_signal_val_str(self._handle) + return + + val = simulator.get_signal_val_str(self._handle) + self._value = BinaryValue(bits=len(val)) + try: + self._value.binstr = val + except: + self._value = val def __int__(self): return int(self._value) @@ -397,7 +399,7 @@ def drivers(self): def loads(self): """ - An iterator for gathering all drivers for a signal + An iterator for gathering all loads on a signal """ iterator = simulator.iterate(self._handle, simulator.LOADS) while True: From b9a5aa5d2dc8ad7ce554a978d8d30315d82d5bbc Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 1 Sep 2015 21:20:41 +0100 Subject: [PATCH 0963/2656] Issue #17: Allow the next_handle to express more than PASS/FAIL The previous GpiIterator::next_handle culd only express PASS/FAIL but we really needed to know if an object had been found but creation failed. The main reason for this is that the object was visible but of a different language. We now allow the function to return if this is the case the GPI layer can take appropriate action. --- lib/gpi/GpiCommon.cpp | 63 +++++++++++++++++++++++++------------- lib/gpi/gpi_priv.h | 17 +++++++++-- lib/vhpi/VhpiCbHdl.cpp | 47 ++++++++++++++++++----------- lib/vhpi/VhpiImpl.cpp | 7 ----- lib/vhpi/VhpiImpl.h | 4 +-- lib/vpi/VpiCbHdl.cpp | 68 ++++++++++++++++++++++++++---------------- lib/vpi/VpiImpl.cpp | 9 ++---- lib/vpi/VpiImpl.h | 6 ++-- 8 files changed, 137 insertions(+), 84 deletions(-) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 6f6cacbf..1a275127 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -42,7 +42,7 @@ static vector registered_impls; class GpiHandleStore { public: - gpi_sim_hdl check_and_store(GpiObjHdl *hdl) { + GpiObjHdl * check_and_store(GpiObjHdl *hdl) { std::map::iterator it; const std::string &name = hdl->get_fullname(); @@ -226,21 +226,20 @@ gpi_sim_hdl gpi_get_root_handle(const char *name) return hdl; } -gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) +static GpiObjHdl* __gpi_get_handle_by_name(std::string name, GpiObjHdl *parent) { - vector::iterator iter; GpiObjHdl *hdl = NULL; - GpiObjHdl *base = sim_to_hdl(parent); - std::string s_name = name; - LOG_DEBUG("Searching for %s", name); + LOG_DEBUG("Searching for %s", name.c_str()); for (iter = registered_impls.begin(); iter != registered_impls.end(); iter++) { - LOG_DEBUG("Checking if %s native though impl %s ", name, (*iter)->get_name_c()); + LOG_DEBUG("Checking if %s native though impl %s", + name.c_str(), + (*iter)->get_name_c()); /* If the current interface is not the same as the one that we are going to query then append the name we are looking for to @@ -249,8 +248,8 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) as the one that we are querying through */ //std::string &to_query = base->is_this_impl(*iter) ? s_name : fq_name; - if ((hdl = (*iter)->native_check_create(s_name, base))) { - LOG_DEBUG("Found %s via %s", name, (*iter)->get_name_c()); + if ((hdl = (*iter)->native_check_create(name, parent))) { + LOG_DEBUG("Found %s via %s", name.c_str(), (*iter)->get_name_c()); break; } } @@ -258,11 +257,18 @@ gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) if (hdl) return CHECK_AND_STORE(hdl); else { - LOG_DEBUG("Failed to find a hdl named %s", name); + LOG_DEBUG("Failed to find a hdl named %s", name.c_str()); return hdl; } } +gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) +{ + std::string s_name = name; + GpiObjHdl *base = sim_to_hdl(parent); + return __gpi_get_handle_by_name(s_name, base); +} + gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) { vector::iterator iter; @@ -300,18 +306,35 @@ gpi_iterator_hdl gpi_iterate(gpi_sim_hdl base, gpi_iterator_sel_t type) gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) { - GpiObjHdl *next; + std::string name; GpiIterator *iter = sim_to_hdl(iterator); - next = iter->next_handle(); - if (!next) { - /* If the iterator returns NULL then it has reached the - end, we clear up here before returning - */ - delete iter; - return next; + GpiObjHdl *parent = iter->get_parent(); + + while (true) { + GpiObjHdl *next = NULL; + int ret = iter->next_handle(name, &next); + + switch (ret) { + case GpiIterator::VALID: + LOG_DEBUG("Create a valid handle"); + return CHECK_AND_STORE(next); + case GpiIterator::VALID_NO_NAME: + LOG_WARN("Unable to fully setup handle, skipping"); + continue; + case GpiIterator::INVALID: + LOG_WARN("Found a name but unable to create via native implementation, trying others"); + next = __gpi_get_handle_by_name(name, parent); + if (next) { + return next; + } + LOG_WARN("Still no joy so skipping"); + continue; + case GpiIterator::END: + LOG_DEBUG("Reached end of iterator"); + delete iter; + return NULL; + } } - - return CHECK_AND_STORE(next); } const char *gpi_get_signal_value_binstr(gpi_sim_hdl sig_hdl) diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index a2888fb7..78c9654a 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -213,11 +213,24 @@ class GpiClockHdl { class GpiIterator : public GpiHdl { public: + enum Status { + VALID, VALID_NO_NAME, INVALID, END + }; + GpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiHdl(impl), m_parent(hdl) { } virtual ~GpiIterator() { } - virtual GpiObjHdl* next_handle() { return NULL; } + virtual int next_handle(std::string &name, GpiObjHdl **hdl) { + name = ""; + *hdl = NULL; + return GpiIterator::END; + } + + GpiObjHdl *get_parent(void) { + return m_parent; + } + protected: GpiObjHdl *m_parent; }; @@ -239,7 +252,7 @@ class GpiImplInterface { virtual GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent) = 0; virtual GpiObjHdl *get_root_handle(const char *name) = 0; virtual GpiIterator *iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) = 0; - virtual GpiObjHdl *next_handle(GpiIterator *iter) = 0; +// virtual GpiObjHdl *next_handle(GpiIterator *iter) = 0; /* Callback related, these may (will) return the same handle*/ virtual GpiCbHdl *register_timed_callback(uint64_t time_ps) = 0; diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 18448d04..1428f32f 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -31,10 +31,8 @@ extern "C" void handle_vhpi_callback(const vhpiCbDataT *cb_data); VhpiSignalObjHdl::~VhpiSignalObjHdl() { - if (m_value.format == vhpiEnumVecVal || - m_value.format == vhpiLogicVecVal) { - free(m_value.value.enumvs); - } + if (m_value.value.str) + free(m_value.value.str); if (m_binvalue.value.str) free(m_binvalue.value.str); @@ -82,7 +80,7 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { case vhpiLogicVecVal: { m_num_elems = vhpi_get(vhpiSizeP, GpiObjHdl::get_handle()); m_value.bufSize = m_num_elems*sizeof(vhpiEnumT); - m_value.value.enumvs = (vhpiEnumT *)malloc(m_value.bufSize); + m_value.value.enumvs = (vhpiEnumT *)malloc(m_value.bufSize + 1); if (!m_value.value.enumvs) { LOG_CRITICAL("Unable to alloc mem for write buffer"); } @@ -123,8 +121,8 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { } if (new_size) { - m_binvalue.bufSize = new_size*sizeof(vhpiCharT) + 1; - m_binvalue.value.str = (vhpiCharT *)calloc(m_binvalue.bufSize, sizeof(vhpiCharT)); + m_binvalue.bufSize = new_size*sizeof(vhpiCharT); + m_binvalue.value.str = (vhpiCharT *)calloc(m_binvalue.bufSize + 1, sizeof(vhpiCharT)); if (!m_value.value.str) { LOG_CRITICAL("Unable to alloc mem for read buffer of signal %s", name.c_str()); @@ -592,7 +590,7 @@ std::vector* KindMappings::get_options(vhpiClassKindT type) if (options_map.end() == valid) { LOG_ERROR("VHPI: Implementation does not know how to iterate over %d", type); - exit(1); + return NULL; } else { return &valid->second; } @@ -607,7 +605,8 @@ VhpiIterator::VhpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator vhpiHandleT iterator; vhpiHandleT vhpi_hdl = m_parent->get_handle(); - selected = iterate_over.get_options((vhpiClassKindT)vhpi_get(vhpiKindP, vhpi_hdl)); + if (NULL == (selected = iterate_over.get_options((vhpiClassKindT)vhpi_get(vhpiKindP, vhpi_hdl)))) + return; /* Find the first mapping type that yields a valid iterator */ for (one2many = selected->begin(); @@ -673,10 +672,13 @@ VhpiIterator::~VhpiIterator() vhpi_release_handle(m_iterator); } -GpiObjHdl *VhpiIterator::next_handle(void) +int VhpiIterator::next_handle(std::string &name, GpiObjHdl **hdl) { vhpiHandleT obj; - GpiObjHdl *new_obj = NULL; + GpiObjHdl *new_obj; + + if (!selected) + return GpiIterator::END; /* We want the next object in the current mapping. * If the end of mapping is reached then we want to @@ -698,7 +700,7 @@ GpiObjHdl *VhpiIterator::next_handle(void) LOG_DEBUG("Found an item %s", vhpi_get_str(vhpiFullNameP, obj)); break; } else { - LOG_DEBUG("vhpi_scan on %d returned NULL", m_iterator); + LOG_DEBUG("vhpi_scan on %d returned NULL", *one2many); } LOG_DEBUG("End of vhpiOneToManyT=%d iteration", *one2many); @@ -717,20 +719,31 @@ GpiObjHdl *VhpiIterator::next_handle(void) if (NULL == obj) { LOG_DEBUG("No more children, all relationships tested"); - return new_obj; + return GpiIterator::END; } - std::string name = vhpi_get_str(vhpiCaseNameP, obj); - std::string fq_name = m_parent->get_fullname() + "." + name; + const char *c_name = vhpi_get_str(vhpiCaseNameP, obj); + if (!c_name) { + return GpiIterator::VALID_NO_NAME; + } + name = c_name; LOG_DEBUG("vhpi_scan found %s (%d) kind:%s name:%s", name.c_str(), vhpi_get(vhpiKindP, obj), vhpi_get_str(vhpiKindStrP, obj), vhpi_get_str(vhpiCaseNameP, obj)); + /* We try and create a handle internally, if this is not possible we + return and GPI will try other implementations with the name + */ + std::string fq_name = m_parent->get_fullname() + "." + name; VhpiImpl *vhpi_impl = reinterpret_cast(m_impl); new_obj = vhpi_impl->create_gpi_obj_from_handle(obj, name, fq_name); - - return new_obj; + if (new_obj) { + *hdl = new_obj; + return GpiIterator::VALID; + } + else + return GpiIterator::INVALID; } diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 6939e783..fb0cc58b 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -399,13 +399,6 @@ GpiIterator *VhpiImpl::iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t typ return new_iter; } -GpiObjHdl *VhpiImpl::next_handle(GpiIterator *iter) -{ - return iter->next_handle(); -} - - - GpiCbHdl *VhpiImpl::register_timed_callback(uint64_t time_ps) { VhpiTimedCbHdl *hdl = new VhpiTimedCbHdl(this, time_ps); diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 6cc13ed5..d9dde737 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -198,7 +198,7 @@ class VhpiIterator : public GpiIterator { virtual ~VhpiIterator(); - GpiObjHdl *next_handle(void); + int next_handle(std::string &name, GpiObjHdl **hdl); private: vhpiHandleT m_iterator; @@ -222,7 +222,7 @@ class VhpiImpl : public GpiImplInterface { /* Hierachy related */ GpiObjHdl *get_root_handle(const char *name); GpiIterator *iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type); - GpiObjHdl *next_handle(GpiIterator *iter); + //GpiObjHdl *next_handle(GpiIterator *iter); /* Callback related, these may (will) return the same handle*/ GpiCbHdl *register_timed_callback(uint64_t time_ps); diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index c2f99246..221cfebb 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -506,44 +506,52 @@ VpiIterator::~VpiIterator() { if (m_iterator) vpi_free_object(m_iterator); - - LOG_DEBUG("Deleted VpiIterator"); } -GpiObjHdl *VpiSingleIterator::next_handle(void) +int VpiSingleIterator::next_handle(std::string &name, GpiObjHdl **hdl) { + GpiObjHdl *new_obj; vpiHandle obj; - GpiObjHdl *new_obj = NULL; if (NULL == m_iterator) - return new_obj; + return GpiIterator::END; obj = vpi_scan(m_iterator); if (NULL == obj) - return new_obj; + return GpiIterator::END; - std::string name; - if (vpiPort == vpi_get(vpiType, obj)) - name = vpi_get_str(vpiName, obj); - else - name = vpi_get_str(vpiName, obj); - std::string fq_name = m_parent->get_fullname() + "." + name; + const char *c_name = vpi_get_str(vpiName, obj); + if (!c_name) { + return GpiIterator::VALID_NO_NAME; + } + name = c_name; - LOG_DEBUG("vpi_scan found name:%s", name.c_str()); + const char *c_fq_name = vpi_get_str(vpiFullName, obj); + if (!c_fq_name) { + return GpiIterator::VALID_NO_NAME; + } + std::string fq_name = c_fq_name; + + LOG_DEBUG("vpi_scan found '%s = '%s'", name.c_str(), fq_name.c_str()); VpiImpl *vpi_impl = reinterpret_cast(m_impl); new_obj = vpi_impl->create_gpi_obj_from_handle(obj, name, fq_name); - return new_obj; + if (new_obj) { + *hdl = new_obj; + return GpiIterator::VALID; + } + else + return GpiIterator::INVALID; } -GpiObjHdl *VpiIterator::next_handle(void) +int VpiIterator::next_handle(std::string &name, GpiObjHdl **hdl) { + GpiObjHdl *new_obj; vpiHandle obj; vpiHandle iter_obj = m_parent->get_handle(); - GpiObjHdl *new_obj = NULL; if (!selected) - return NULL; + return GpiIterator::END; do { obj = NULL; @@ -574,20 +582,28 @@ GpiObjHdl *VpiIterator::next_handle(void) if (NULL == obj) { LOG_DEBUG("No more children, all relationships tested"); - return new_obj; + return GpiIterator::END; } - std::string name; - if (vpiPort == vpi_get(vpiType, obj)) - name = vpi_get_str(vpiName, obj); - else - name = name = vpi_get_str(vpiName, obj); + const char *c_name = vpi_get_str(vpiName, obj); + if (!c_name) { + return GpiIterator::VALID_NO_NAME; + } + name = c_name; - std::string fq_name = m_parent->get_fullname() + "." + name; + LOG_DEBUG("vpi_scan found '%s'", name.c_str()); - LOG_DEBUG("vpi_scan found name:%s", name.c_str()); + /* We try and create a handle internally, if this is not possible we + return and GPI will try other implementations with the name + */ + std::string fq_name = m_parent->get_fullname() + "." + name; VpiImpl *vpi_impl = reinterpret_cast(m_impl); new_obj = vpi_impl->create_gpi_obj_from_handle(obj, name, fq_name); - return new_obj; + if (new_obj) { + *hdl = new_obj; + return GpiIterator::VALID; + } + else + return GpiIterator::INVALID; } diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 115f897e..0a92f4f9 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -168,7 +168,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) { vpiHandle new_hdl; - std::string fq_name = parent->get_name() + "." + name; + std::string fq_name = parent->get_fullname() + "." + name; std::vector writable(fq_name.begin(), fq_name.end()); writable.push_back('\0'); @@ -180,7 +180,7 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { vpi_free_object(new_hdl); - LOG_ERROR("Unable to query fetch object %s", fq_name.c_str()); + LOG_ERROR("Unable to fetch object %s", fq_name.c_str()); return NULL; } return new_obj; @@ -285,11 +285,6 @@ GpiIterator *VpiImpl::iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type return new_iter; } -GpiObjHdl *VpiImpl::next_handle(GpiIterator *iter) -{ - return iter->next_handle(); -} - GpiCbHdl *VpiImpl::register_timed_callback(uint64_t time_ps) { VpiTimedCbHdl *hdl = new VpiTimedCbHdl(this, time_ps); diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index b49c98e0..6b529326 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -187,7 +187,7 @@ class KindMappings { class VpiSignalObjHdl : public GpiSignalObjHdl { public: - VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl, gpi_objtype_t objtype, bool is_const) : + VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl, gpi_objtype_t objtype, bool is_const) : GpiSignalObjHdl(impl, hdl, objtype, is_const), m_rising_cb(impl, this, GPI_RISING), m_falling_cb(impl, this, GPI_FALLING), @@ -219,7 +219,7 @@ class VpiIterator : public GpiIterator { virtual ~VpiIterator(); - GpiObjHdl *next_handle(void); + int next_handle(std::string &name, GpiObjHdl **hdl); private: vpiHandle m_iterator; @@ -246,7 +246,7 @@ class VpiSingleIterator : public GpiIterator { } virtual ~VpiSingleIterator() { } - GpiObjHdl *next_handle(void); + int next_handle(std::string &name, GpiObjHdl **hdl); protected: vpiHandle m_iterator; From 19aa2bd753b246c3e9e230934810eb9251b8fcd9 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 1 Sep 2015 21:27:14 +0100 Subject: [PATCH 0964/2656] Issue #17: Haronise proptypes for gpi_get_handle_by_xxx, just annoyed me --- include/gpi.h | 2 +- lib/gpi/GpiCommon.cpp | 8 ++++---- lib/simulator/simulatormodule.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/gpi.h b/include/gpi.h index 998dcbe3..1c421948 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -117,7 +117,7 @@ void gpi_get_sim_time(uint32_t *high, uint32_t *low); // Returns a handle to the root simulation object, // Should be freed with gpi_free_handle gpi_sim_hdl gpi_get_root_handle(const char *name); -gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent); +gpi_sim_hdl gpi_get_handle_by_name(gpi_sim_hdl parent, const char *name); gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index); void gpi_free_handle(gpi_sim_hdl gpi_hdl); diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 1a275127..0a2d5b29 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -226,7 +226,7 @@ gpi_sim_hdl gpi_get_root_handle(const char *name) return hdl; } -static GpiObjHdl* __gpi_get_handle_by_name(std::string name, GpiObjHdl *parent) +static GpiObjHdl* __gpi_get_handle_by_name(GpiObjHdl *parent, std::string name) { vector::iterator iter; @@ -262,11 +262,11 @@ static GpiObjHdl* __gpi_get_handle_by_name(std::string name, GpiObjHdl *parent) } } -gpi_sim_hdl gpi_get_handle_by_name(const char *name, gpi_sim_hdl parent) +gpi_sim_hdl gpi_get_handle_by_name(gpi_sim_hdl parent, const char *name) { std::string s_name = name; GpiObjHdl *base = sim_to_hdl(parent); - return __gpi_get_handle_by_name(s_name, base); + return __gpi_get_handle_by_name(base, s_name); } gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) @@ -323,7 +323,7 @@ gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) continue; case GpiIterator::INVALID: LOG_WARN("Found a name but unable to create via native implementation, trying others"); - next = __gpi_get_handle_by_name(name, parent); + next = __gpi_get_handle_by_name(parent, name); if (next) { return next; } diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 385fdac5..0405e349 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -676,7 +676,7 @@ static PyObject *get_handle_by_name(PyObject *self, PyObject *args) return NULL; } - result = gpi_get_handle_by_name(name, (gpi_sim_hdl)hdl); + result = gpi_get_handle_by_name((gpi_sim_hdl)hdl, name); res = Py_BuildValue("l", result); From 57230d08c2064fbcf5bea201f2fa532e11ab78b1 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 2 Sep 2015 08:10:10 +0100 Subject: [PATCH 0965/2656] Issue #17: Review comments for pull #307 --- lib/gpi/gpi_priv.h | 1 - lib/vhpi/VhpiImpl.cpp | 13 +++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 78c9654a..419db800 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -252,7 +252,6 @@ class GpiImplInterface { virtual GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent) = 0; virtual GpiObjHdl *get_root_handle(const char *name) = 0; virtual GpiIterator *iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) = 0; -// virtual GpiObjHdl *next_handle(GpiIterator *iter) = 0; /* Callback related, these may (will) return the same handle*/ virtual GpiCbHdl *register_timed_callback(uint64_t time_ps) = 0; diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index fb0cc58b..2ff95440 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -255,22 +255,23 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) { vhpiHandleT new_hdl; - std::string fq_name = parent->get_fullname(); - if (fq_name == ":") { - fq_name += name; + std::string search_name = parent->get_name(); + if (search_name == ":") { + search_name += name; } else { - fq_name = fq_name + "." + name; + search_name = search_name + ":" + name; } - std::vector writable(fq_name.begin(), fq_name.end()); + std::vector writable(search_name.begin(), search_name.end()); writable.push_back('\0'); new_hdl = vhpi_handle_by_name(&writable[0], NULL); if (new_hdl == NULL) { - LOG_DEBUG("Unable to query vhpi_handle_by_name %s", fq_name.c_str()); + LOG_DEBUG("Unable to query vhpi_handle_by_name %s", search_name.c_str()); return NULL; } + std::string fq_name = parent->get_fullname() + "." + name; GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { vhpi_release_handle(new_hdl); From fe0a88bda7845f258ba64cb1c66887b539d105d0 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 2 Sep 2015 08:15:35 +0100 Subject: [PATCH 0966/2656] Issue #17: Remove GpiImplInterace::equal from a few commits ago --- lib/fli/FliImpl.cpp | 6 ------ lib/fli/FliImpl.h | 2 -- lib/gpi/gpi_priv.h | 3 --- lib/vhpi/VhpiImpl.cpp | 7 ------- lib/vhpi/VhpiImpl.h | 2 -- lib/vpi/VpiImpl.cpp | 5 ----- lib/vpi/VpiImpl.h | 2 -- 7 files changed, 27 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index e6dd0381..9df0fc2f 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -617,12 +617,6 @@ GpiObjHdl *FliImpl::next_handle(GpiIterator *iter) return NULL; } -bool FliImpl::equal(GpiObjHdl* lhs, GpiObjHdl* rhs) -{ - return false; -} - - #include FliTimedCbHdl* FliTimerCache::get_timer(uint64_t time_ps) diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index c75ec018..05b03057 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -255,8 +255,6 @@ class FliImpl : public GpiImplInterface { /* Method to provide strings from operation types */ const char *reason_to_string(int reason); - bool equal(GpiObjHdl* lhs, GpiObjHdl* rhs); - private: FliReadOnlyCbHdl m_readonly_cbhdl; FliNextPhaseCbHdl m_nexttime_cbhdl; diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 419db800..f64fc885 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -263,9 +263,6 @@ class GpiImplInterface { /* Method to provide strings from operation types */ virtual const char * reason_to_string(int reason) = 0; - /* Test equality of two handles */ - virtual bool equal(const GpiObjHdl* lhs, const GpiObjHdl* rhs) = 0; - private: std::string m_name; }; diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 2ff95440..67b03140 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -442,13 +442,6 @@ int VhpiImpl::deregister_callback(GpiCbHdl *gpi_hdl) return 0; } -bool VhpiImpl::equal(const GpiObjHdl *lhs, const GpiObjHdl *rhs) -{ - vhpiHandleT lhs_handle = lhs->get_handle(); - vhpiHandleT rhs_handle = rhs->get_handle(); - return vhpi_compare_handles(lhs_handle, rhs_handle); -} - void VhpiImpl::sim_end(void) { sim_finish_cb->set_call_state(GPI_DELETE); diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index d9dde737..9f5089c6 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -240,8 +240,6 @@ class VhpiImpl : public GpiImplInterface { std::string &name, std::string &fq_name); - bool equal(const GpiObjHdl* lhs, const GpiObjHdl* rhs); - private: VhpiReadwriteCbHdl m_read_write; VhpiNextPhaseCbHdl m_next_phase; diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 0a92f4f9..2a1f9c98 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -327,11 +327,6 @@ int VpiImpl::deregister_callback(GpiCbHdl *gpi_hdl) return 0; } -bool VpiImpl::equal(const GpiObjHdl *lhs, const GpiObjHdl *rhs) -{ - return false; -} - // If the Pything world wants things to shut down then unregister // the callback for end of sim void VpiImpl::sim_end(void) diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 6b529326..7a6879ea 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -282,8 +282,6 @@ class VpiImpl : public GpiImplInterface { std::string &name, std::string &fq_name); - bool equal(const GpiObjHdl* lhs, const GpiObjHdl* rhs); - private: /* Singleton callbacks */ VpiReadwriteCbHdl m_read_write; From 014109ae46219ec407dedb018e30205c5f7b6c50 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 2 Sep 2015 08:49:56 +0100 Subject: [PATCH 0967/2656] Issue #17: Missing review comment --- lib/vhpi/VhpiImpl.h | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 9f5089c6..529eac74 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -222,7 +222,6 @@ class VhpiImpl : public GpiImplInterface { /* Hierachy related */ GpiObjHdl *get_root_handle(const char *name); GpiIterator *iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type); - //GpiObjHdl *next_handle(GpiIterator *iter); /* Callback related, these may (will) return the same handle*/ GpiCbHdl *register_timed_callback(uint64_t time_ps); From 124db969464e901980be5fe80ad2b47f3873c6fe Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 2 Sep 2015 08:50:15 +0100 Subject: [PATCH 0968/2656] Issue #17: Fixup FLI to work with new interfaces --- lib/fli/FliImpl.cpp | 62 +++++++++++++++++++++++++++++++++++---------- lib/fli/FliImpl.h | 23 ++++++++--------- 2 files changed, 59 insertions(+), 26 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 9df0fc2f..7ac145fc 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -140,7 +140,7 @@ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) return NULL; } - new_obj->initialise(fq_name); + new_obj->initialise(name, fq_name); return new_obj; } @@ -208,7 +208,7 @@ GpiObjHdl *FliImpl::get_root_handle(const char *name) LOG_DEBUG("Found toplevel: %s, creating handle....", root_name.c_str()); rv = new FliRegionObjHdl(this, root); - rv->initialise(root_name); + rv->initialise(root_name, root_name); LOG_DEBUG("Returning root handle %p", rv); return rv; @@ -436,6 +436,18 @@ const char* FliSignalObjHdl::get_signal_value_binstr(void) return m_val_buff; } +double FliSignalObjHdl::get_signal_value_real(void) +{ + LOG_ERROR("Getting signal value as double not currently supported!"); + return -1; +} + +long FliSignalObjHdl::get_signal_value_long(void) +{ + LOG_ERROR("Getting signal value as long not currently supported!"); + return -1; +} + int FliSignalObjHdl::set_signal_value(const long value) { int rc; @@ -464,7 +476,13 @@ int FliSignalObjHdl::set_signal_value(std::string &value) return rc-1; } -int FliSignalObjHdl::initialise(std::string &name) +int FliSignalObjHdl::set_signal_value(const double value) +{ + LOG_ERROR("Setting Signal via double not supported!"); + return -1; +} + +int FliSignalObjHdl::initialise(std::string &name, std::string &fq_name) { /* Pre allocte buffers on signal type basis */ m_fli_type = mti_GetTypeKind(mti_GetSignalType(m_fli_hdl)); @@ -507,7 +525,7 @@ int FliSignalObjHdl::initialise(std::string &name) } m_val_str_buff[m_val_str_len] = '\0'; - GpiObjHdl::initialise(name); + GpiObjHdl::initialise(name, fq_name); return 0; } @@ -556,19 +574,37 @@ const char* FliVariableObjHdl::get_signal_value_binstr(void) return m_val_buff; } +double FliVariableObjHdl::get_signal_value_real(void) +{ + LOG_ERROR("Getting variable value as double not currently supported!"); + return -1; +} + +long FliVariableObjHdl::get_signal_value_long(void) +{ + LOG_ERROR("Getting variable value as long not currently supported!"); + return -1; +} + int FliVariableObjHdl::set_signal_value(const long value) { - LOG_CRITICAL("Setting variable value not currently supported!\n"); + LOG_ERROR("Setting variable value not currently supported!"); return -1; } int FliVariableObjHdl::set_signal_value(std::string &value) { - LOG_CRITICAL("Setting variable value not currently supported!\n"); + LOG_ERROR("Setting variable value not currently supported!"); + return -1; +} + +int FliVariableObjHdl::set_signal_value(const double value) +{ + LOG_ERROR("Setting variable value not currently supported"); return -1; } -int FliVariableObjHdl::initialise(std::string &name) +int FliVariableObjHdl::initialise(std::string &name, std::string &fq_name) { /* Pre allocte buffers on signal type basis */ m_fli_type = mti_GetTypeKind(mti_GetVarType(m_fli_hdl)); @@ -602,18 +638,16 @@ int FliVariableObjHdl::initialise(std::string &name) } m_val_buff[m_val_len] = '\0'; - GpiObjHdl::initialise(name); + GpiObjHdl::initialise(name, fq_name); return 0; } -GpiIterator *FliImpl::iterate_handle(GpiObjHdl *obj_hdl) -{ - return NULL; -} - -GpiObjHdl *FliImpl::next_handle(GpiIterator *iter) +GpiIterator *FliImpl::iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) { + /* This function should return a class derived from GpiIterator and follows it's + interface. Specifically it's new_handle(std::string, std::string) method and + return values. Using VpiIterator as an example */ return NULL; } diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index 05b03057..7b0e367b 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -73,7 +73,7 @@ class FliSignalCbHdl : public FliProcessCbHdl, public GpiValueCbHdl { class FliSignalObjHdl : public GpiSignalObjHdl { public: - FliSignalObjHdl(GpiImplInterface *impl, mtiSignalIdT hdl) : GpiSignalObjHdl(impl, hdl, GPI_UNKNOWN), + FliSignalObjHdl(GpiImplInterface *impl, mtiSignalIdT hdl) : GpiSignalObjHdl(impl, hdl, GPI_UNKNOWN, false), m_fli_hdl(hdl), m_rising_cb(impl, this, GPI_RISING), m_falling_cb(impl, this, GPI_FALLING), @@ -94,13 +94,13 @@ class FliSignalObjHdl : public GpiSignalObjHdl { } const char* get_signal_value_binstr(void); - double get_signal_value_real(void) { return 0.0; } - long get_signal_value_long(void) { return 0; } + double get_signal_value_real(void); + long get_signal_value_long(void); int set_signal_value(const long value); - int set_signal_value(const double value) { return 0; } + int set_signal_value(const double value); int set_signal_value(std::string &value); - int initialise(std::string &name); + int initialise(std::string &name, std::string &fq_name); GpiCbHdl *value_change_cb(unsigned int edge); protected: @@ -120,7 +120,7 @@ class FliSignalObjHdl : public GpiSignalObjHdl { class FliVariableObjHdl : public GpiSignalObjHdl { public: - FliVariableObjHdl(GpiImplInterface *impl, mtiVariableIdT hdl) : GpiSignalObjHdl(impl, hdl, GPI_UNKNOWN), + FliVariableObjHdl(GpiImplInterface *impl, mtiVariableIdT hdl) : GpiSignalObjHdl(impl, hdl, GPI_UNKNOWN, true), m_fli_hdl(hdl), m_fli_type(MTI_TYPE_SCALAR), m_mti_buff(NULL), @@ -134,13 +134,13 @@ class FliVariableObjHdl : public GpiSignalObjHdl { } const char* get_signal_value_binstr(void); - double get_signal_value_real(void) { return 0.0; } - long get_signal_value_long(void) { return 0; } + double get_signal_value_real(void); + long get_signal_value_long(void); int set_signal_value(const long value); int set_signal_value(std::string &value); - int set_signal_value(const double value) { return 0; } - int initialise(std::string &name); + int set_signal_value(const double value); + int initialise(std::string &name, std::string &fq_name); GpiCbHdl *value_change_cb(unsigned int edge); protected: @@ -242,8 +242,7 @@ class FliImpl : public GpiImplInterface { GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); GpiObjHdl *get_root_handle(const char *name); - GpiIterator *iterate_handle(GpiObjHdl *obj_hdl); - GpiObjHdl *next_handle(GpiIterator *iter); + GpiIterator *iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type); /* Callback related, these may (will) return the same handle*/ GpiCbHdl *register_timed_callback(uint64_t time_ps); From d9726ff70446d6adbeff713b6ea67efb734969a0 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 2 Sep 2015 09:32:03 +0100 Subject: [PATCH 0969/2656] Issue #17: Allow the implementation tied to an iterators parent to be skipped during fallback into GPI code --- lib/gpi/GpiCommon.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 0a2d5b29..9a2beeeb 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -226,7 +226,9 @@ gpi_sim_hdl gpi_get_root_handle(const char *name) return hdl; } -static GpiObjHdl* __gpi_get_handle_by_name(GpiObjHdl *parent, std::string name) +static GpiObjHdl* __gpi_get_handle_by_name(GpiObjHdl *parent, + std::string name, + GpiImplInterface *skip_impl) { vector::iterator iter; @@ -237,6 +239,12 @@ static GpiObjHdl* __gpi_get_handle_by_name(GpiObjHdl *parent, std::string name) for (iter = registered_impls.begin(); iter != registered_impls.end(); iter++) { + + if (skip_impl && (skip_impl == (*iter))) { + LOG_DEBUG("Skipping %s impl", (*iter)->get_name_c()); + continue; + } + LOG_DEBUG("Checking if %s native though impl %s", name.c_str(), (*iter)->get_name_c()); @@ -266,7 +274,7 @@ gpi_sim_hdl gpi_get_handle_by_name(gpi_sim_hdl parent, const char *name) { std::string s_name = name; GpiObjHdl *base = sim_to_hdl(parent); - return __gpi_get_handle_by_name(base, s_name); + return __gpi_get_handle_by_name(base, s_name, NULL); } gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) @@ -323,7 +331,7 @@ gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) continue; case GpiIterator::INVALID: LOG_WARN("Found a name but unable to create via native implementation, trying others"); - next = __gpi_get_handle_by_name(parent, name); + next = __gpi_get_handle_by_name(parent, name, iter->m_impl); if (next) { return next; } From 634d72660704f1c5aa4976a863c52d68f6953000 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 2 Sep 2015 09:45:55 +0100 Subject: [PATCH 0970/2656] Issue #17: Move test_iteration to test_iteration_vhdl and re-use endian_swapper verilog as test_iteration_verilog --- .../test_iteration_verilog/Makefile | 39 ++++++++++++ .../test_iteration_es.py | 63 +++++++++++++++++++ .../Makefile | 0 .../test_iteration.py | 0 4 files changed, 102 insertions(+) create mode 100644 tests/test_cases/test_iteration_verilog/Makefile create mode 100644 tests/test_cases/test_iteration_verilog/test_iteration_es.py rename tests/test_cases/{test_iteration => test_iteration_vhdl}/Makefile (100%) rename tests/test_cases/{test_iteration => test_iteration_vhdl}/test_iteration.py (100%) diff --git a/tests/test_cases/test_iteration_verilog/Makefile b/tests/test_cases/test_iteration_verilog/Makefile new file mode 100644 index 00000000..fd2f7834 --- /dev/null +++ b/tests/test_cases/test_iteration_verilog/Makefile @@ -0,0 +1,39 @@ +############################################################################### +# Copyright (c) 2015 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +PWD := $(shell pwd) +COCOTB := $(PWD)/../../.. + +TOPLEVEL_LANG = verilog + +VERILOG_SOURCES = $(COCOTB)/examples/endian_swapper/hdl/endian_swapper.sv +TOPLEVEL = endian_swapper_sv + +MODULE = test_iteration_es + +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim diff --git a/tests/test_cases/test_iteration_verilog/test_iteration_es.py b/tests/test_cases/test_iteration_verilog/test_iteration_es.py new file mode 100644 index 00000000..45241cff --- /dev/null +++ b/tests/test_cases/test_iteration_verilog/test_iteration_es.py @@ -0,0 +1,63 @@ +''' Copyright (c) 2015 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +import logging + +import cocotb +from cocotb.triggers import Timer +from cocotb.result import TestError, TestFailure + +@cocotb.test() +def recursive_discovery(dut): + """ + Recursively discover every single object in the design + """ + tlog = logging.getLogger("cocotb.test") + yield Timer(100) + def dump_all_the_things(parent): + count = 0 + for thing in parent: + count += 1 + tlog.info("Found %s.%s (%s)", parent._name, thing._name, type(thing)) + count += dump_all_the_things(thing) + return count + total = dump_all_the_things(dut) + tlog.info("Found a total of %d things", total) + if total != 258: + raise TestFailure("Expected 32290 objects but found %d" % total) + +@cocotb.coroutine +def iteration_loop(dut): + for thing in dut: + thing._log.info("Found something: %s" % thing._fullname) + yield Timer(1) + +@cocotb.test() +def dual_iteration(dut): + loop_one = cocotb.fork(iteration_loop(dut)) + loop_two = cocotb.fork(iteration_loop(dut)) + + yield [loop_one.join(), loop_two.join()] + diff --git a/tests/test_cases/test_iteration/Makefile b/tests/test_cases/test_iteration_vhdl/Makefile similarity index 100% rename from tests/test_cases/test_iteration/Makefile rename to tests/test_cases/test_iteration_vhdl/Makefile diff --git a/tests/test_cases/test_iteration/test_iteration.py b/tests/test_cases/test_iteration_vhdl/test_iteration.py similarity index 100% rename from tests/test_cases/test_iteration/test_iteration.py rename to tests/test_cases/test_iteration_vhdl/test_iteration.py From fcc6efff326ebb7ac91fef06ab915ec079a0cafd Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 2 Sep 2015 09:59:18 +0100 Subject: [PATCH 0971/2656] Issue #1: Revert test change of sperator in VHPI hierachy to `'` back from `:` --- lib/vhpi/VhpiImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 67b03140..870eebd1 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -259,7 +259,7 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) if (search_name == ":") { search_name += name; } else { - search_name = search_name + ":" + name; + search_name = search_name + "." + name; } std::vector writable(search_name.begin(), search_name.end()); writable.push_back('\0'); From f87dc6561cacc0a6918d2063dcb7683900b31f23 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 2 Sep 2015 17:34:04 +0100 Subject: [PATCH 0972/2656] Issue #17: Disabled test_iteration_verilog on ICARUS --- tests/test_cases/test_iteration_verilog/Makefile | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/test_cases/test_iteration_verilog/Makefile b/tests/test_cases/test_iteration_verilog/Makefile index fd2f7834..147784ac 100644 --- a/tests/test_cases/test_iteration_verilog/Makefile +++ b/tests/test_cases/test_iteration_verilog/Makefile @@ -35,5 +35,10 @@ TOPLEVEL = endian_swapper_sv MODULE = test_iteration_es -include $(COCOTB)/makefiles/Makefile.inc -include $(COCOTB)/makefiles/Makefile.sim +ifeq ($(SIM),) +all: + @echo "Skipping test as ICAURS does not support enough VPI calls" +else + include $(COCOTB)/makefiles/Makefile.inc + include $(COCOTB)/makefiles/Makefile.sim +endif From 660bf160ca1564dfa44e540a2c7f9c80c40bfc73 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 2 Sep 2015 17:53:28 +0100 Subject: [PATCH 0973/2656] Issue #17: Add test for n dimension array and fix message on error of test_iteration_es.py --- .../test_iteration_es.py | 2 +- .../test_iteration_vhdl/test_iteration.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/test_cases/test_iteration_verilog/test_iteration_es.py b/tests/test_cases/test_iteration_verilog/test_iteration_es.py index 45241cff..724c29e5 100644 --- a/tests/test_cases/test_iteration_verilog/test_iteration_es.py +++ b/tests/test_cases/test_iteration_verilog/test_iteration_es.py @@ -46,7 +46,7 @@ def dump_all_the_things(parent): total = dump_all_the_things(dut) tlog.info("Found a total of %d things", total) if total != 258: - raise TestFailure("Expected 32290 objects but found %d" % total) + raise TestFailure("Expected 258 objects but found %d" % total) @cocotb.coroutine def iteration_loop(dut): diff --git a/tests/test_cases/test_iteration_vhdl/test_iteration.py b/tests/test_cases/test_iteration_vhdl/test_iteration.py index 9ff0a416..ce824374 100644 --- a/tests/test_cases/test_iteration_vhdl/test_iteration.py +++ b/tests/test_cases/test_iteration_vhdl/test_iteration.py @@ -84,3 +84,21 @@ def get_clock(dut): yield Timer(1) if int(dut.aclk) is not 1: raise TestFailure("dut.aclk is not what we expected (got %d)" % int(dut.aclk)) + +@cocotb.test() +def test_n_dimension_array(dut): + tlog = logging.getLogger("cocotb.test") + inner_count = 0 + outer_count = 0 + yield Timer(0) + config = dut.inst_ram_ctrl.config + # This signal is a 2 x 7 vhpiEnumVecVal + for thing in config: + for sub_thing in thing: + tlog.info("Found %s", subthing,_name) + inner_count += 1 + outer_count += 1 + + if inner_count != 14 and outer_count != 2: + raise TestFailure("dut.inst_ram_ctrl.config should have a total of 14 elems over 2 loops") + From 1bd38e36ea0282f03e6faf63da41d2915ad26883 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 2 Sep 2015 19:21:20 +0100 Subject: [PATCH 0974/2656] Issue #17: Handle multi dimensional vectors in VHDL --- lib/vhpi/VhpiCbHdl.cpp | 4 +++- lib/vhpi/VhpiImpl.cpp | 15 +++++++++++++-- .../test_iteration_vhdl/test_iteration.py | 6 +++--- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 1428f32f..d82142b3 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -85,8 +85,10 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { LOG_CRITICAL("Unable to alloc mem for write buffer"); } LOG_DEBUG("Overriding num_elems to %d", m_num_elems); - GpiObjHdl::initialise(name, fq_name); + VhpiIterator test_iter(this->m_impl, this); + + GpiObjHdl::initialise(name, fq_name); return 0; } case vhpiRawDataVal: { diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 870eebd1..e9b429bf 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -226,8 +226,19 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, vhpiLogicVecVal == value.format || vhpiPhysVecVal == value.format || vhpiTimeVecVal == value.format) { - LOG_DEBUG("Detected a vector type", fq_name.c_str()); - gpi_type = GPI_ARRAY; + /* This may well be an n dimensional vector if it is + then we create a non signal object + */ + int num_elems = vhpi_get(vhpiSizeP, new_hdl); + if (value.numElems == num_elems) { + LOG_DEBUG("Detected single dimension vector type", fq_name.c_str()); + gpi_type = GPI_ARRAY; + } else { + LOG_DEBUG("Detected an n dimension vector type", fq_name.c_str()); + gpi_type = GPI_MODULE; + new_obj = new GpiObjHdl(this, new_hdl, gpi_type); + break; + } } new_obj = new VhpiSignalObjHdl(this, new_hdl, gpi_type, is_const(type)); diff --git a/tests/test_cases/test_iteration_vhdl/test_iteration.py b/tests/test_cases/test_iteration_vhdl/test_iteration.py index ce824374..b1cff869 100644 --- a/tests/test_cases/test_iteration_vhdl/test_iteration.py +++ b/tests/test_cases/test_iteration_vhdl/test_iteration.py @@ -45,8 +45,8 @@ def dump_all_the_things(parent): return count total = dump_all_the_things(dut) tlog.info("Found a total of %d things", total) - if total != 32290: - raise TestFailure("Expected 32290 objects but found %d" % total) + if total != 32306: + raise TestFailure("Expected 32306 objects but found %d" % total) @cocotb.test() @@ -95,7 +95,7 @@ def test_n_dimension_array(dut): # This signal is a 2 x 7 vhpiEnumVecVal for thing in config: for sub_thing in thing: - tlog.info("Found %s", subthing,_name) + tlog.info("Found %s", sub_thing._name) inner_count += 1 outer_count += 1 From f3d22b252daaf89ecc0da15feca3dd1662093ba0 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 2 Sep 2015 19:25:02 +0100 Subject: [PATCH 0975/2656] Issue #17: Change some log levels and make message more helpful --- lib/gpi/GpiCommon.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 9a2beeeb..187f7afd 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -327,15 +327,15 @@ gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) LOG_DEBUG("Create a valid handle"); return CHECK_AND_STORE(next); case GpiIterator::VALID_NO_NAME: - LOG_WARN("Unable to fully setup handle, skipping"); + LOG_DEBUG("Unable to fully setup handle, skipping"); continue; case GpiIterator::INVALID: - LOG_WARN("Found a name but unable to create via native implementation, trying others"); + LOG_DEBUG("Found a name but unable to create via native implementation, trying others"); next = __gpi_get_handle_by_name(parent, name, iter->m_impl); if (next) { return next; } - LOG_WARN("Still no joy so skipping"); + LOG_WARN("Unable to create %s via any registered implementation", name.c_str()); continue; case GpiIterator::END: LOG_DEBUG("Reached end of iterator"); From ce735fcfcf031c825f5fa72bf4ea43b0e7bb30b4 Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 9 Sep 2015 13:43:37 +0100 Subject: [PATCH 0976/2656] Issue #17: Map GPI_STRUCTURE onto hierarchy object --- cocotb/handle.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cocotb/handle.py b/cocotb/handle.py index 3b6448cf..7457a0e4 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -553,6 +553,7 @@ def SimHandle(handle): """ _type2cls = { simulator.MODULE: HierarchyObject, + simulator.STRUCTURE: HierarchyObject, simulator.REG: ModifiableObject, simulator.NETARRAY: ModifiableObject, simulator.REAL: RealObject, From 172ff7441ff0716ab93f78a20e776e03e0537faf Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 11 Sep 2015 09:02:00 +0100 Subject: [PATCH 0977/2656] Issue #17: Be a little less aggressive with the warnings --- cocotb/handle.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 7457a0e4..1a8f46f2 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -51,6 +51,8 @@ from cocotb.triggers import _RisingEdge, _FallingEdge from cocotb.utils import get_python_integer_types +# Only issue a warning for each deprecated attribute access +_deprecation_warned = {} @@ -150,7 +152,9 @@ def __getattr__(self, name): if not new_handle: if name in self._compat_mapping: - warnings.warn("Use of %s attribute is deprecated" % name) + if name not in _deprecation_warned: + warnings.warn("Use of %s attribute is deprecated" % name) + _deprecation_warned[name] = True return getattr(self, self._compat_mapping[name]) # To find generated indices we have to discover all From f38ffae6c75bcb5dea33b0ebaf4a6dff77b2e078 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 11 Sep 2015 09:09:25 +0100 Subject: [PATCH 0978/2656] Issue #17: HierarchyObjects don't have _getvalue() --- cocotb/handle.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 1a8f46f2..0d4a2d46 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -145,9 +145,7 @@ def __getattr__(self, name): """ if name in self._sub_handles: return self._sub_handles[name] - # Avoid collision with signals named "value" while maintaining compatability - if name == "value": - return self._getvalue() + new_handle = simulator.get_handle_by_name(self._handle, name) if not new_handle: From 0dfaacc59ab714e60734dc25f2460e9bffc37aaa Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 11 Sep 2015 10:19:04 +0100 Subject: [PATCH 0979/2656] Issue #17: Add testcase for objects changing types after iteration --- .../test_vhdl_access/test_vhdl_access.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/test_cases/test_vhdl_access/test_vhdl_access.py b/tests/test_cases/test_vhdl_access/test_vhdl_access.py index f2a4bd0e..66fba8f2 100644 --- a/tests/test_cases/test_vhdl_access/test_vhdl_access.py +++ b/tests/test_cases/test_vhdl_access/test_vhdl_access.py @@ -91,3 +91,22 @@ def check_instance(obj, objtype): if fails: raise TestFailure("%d Failures during the test" % fails) + +@cocotb.test() +def port_not_hierarchy(dut): + """ + Test for issue raised by Luke - iteration causes a toplevel port type to + change from from ModifiableObject to HierarchyObject + """ + tlog = logging.getLogger("cocotb.test") + yield Timer(100) + if not isinstance(dut.aclk, ModifiableObject): + tlog.error("dut.aclk should be ModifiableObject but got %s", dut.aclk.__class__.__name__) + else: + tlog.info("dut.aclk is ModifiableObject") + for _ in dut: + pass + if not isinstance(dut.aclk, ModifiableObject): + tlog.error("dut.aclk should be ModifiableObject but got %s", dut.aclk.__class__.__name__) + else: + tlog.info("dut.aclk is ModifiableObject") From 0515555201fa734fd89bd6943dcc582c0a0bc026 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 11 Sep 2015 12:23:06 +0100 Subject: [PATCH 0980/2656] Fixes #309: Only use LOG_CRITICAL for unrecoverable errors --- lib/fli/FliImpl.cpp | 28 ++++++++++++++-------------- lib/vhpi/VhpiCbHdl.cpp | 10 +++++----- lib/vpi/VpiImpl.cpp | 6 +++--- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 868f9dc1..08c01b8b 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -76,7 +76,7 @@ void handle_fli_callback(void *data) FliProcessCbHdl *cb_hdl = (FliProcessCbHdl*)data; if (!cb_hdl) { - LOG_CRITICAL("FLI: Callback data corrupted"); + LOG_CRITICAL("FLI: Callback data corrupted: ABORTING"); } gpi_cb_state_e old_state = cb_hdl->get_call_state(); @@ -216,13 +216,13 @@ GpiObjHdl *FliImpl::get_root_handle(const char *name) error: - LOG_CRITICAL("FLI: Couldn't find root handle %s", name); + LOG_ERROR("FLI: Couldn't find root handle %s", name); for (root = mti_GetTopRegion(); root != NULL; root = mti_NextRegion(root)) { if (name == NULL) break; - LOG_CRITICAL("FLI: Toplevel instances: %s != %s...", name, mti_GetRegionName(root)); + LOG_ERROR("FLI: Toplevel instances: %s != %s...", name, mti_GetRegionName(root)); } return NULL; } @@ -427,7 +427,7 @@ const char* FliSignalObjHdl::get_signal_value_binstr(void) } break; default: - LOG_CRITICAL("Signal %s type %d not currently supported", + LOG_ERROR("Signal %s type %d not currently supported", m_name.c_str(), m_fli_type); break; } @@ -447,7 +447,7 @@ int FliSignalObjHdl::set_signal_value(const int value) rc = mti_ForceSignal(m_fli_hdl, &buff[0], 0, MTI_FORCE_DEPOSIT, -1, -1); if (!rc) { - LOG_CRITICAL("Setting signal value failed!\n"); + LOG_ERROR("Setting signal value failed!\n"); } return rc-1; } @@ -460,7 +460,7 @@ int FliSignalObjHdl::set_signal_value(std::string &value) rc = mti_ForceSignal(m_fli_hdl, &m_val_str_buff[0], 0, MTI_FORCE_DEPOSIT, -1, -1); if (!rc) { - LOG_CRITICAL("Setting signal value failed!\n"); + LOG_ERROR("Setting signal value failed!\n"); } return rc-1; } @@ -485,22 +485,22 @@ int FliSignalObjHdl::initialise(std::string &name) m_val_str_len = snprintf(NULL, 0, "%d'b", m_val_len)+m_val_len; m_mti_buff = (mtiInt32T*)malloc(sizeof(*m_mti_buff) * m_val_len); if (!m_mti_buff) { - LOG_CRITICAL("Unable to alloc mem for signal mti read buffer"); + LOG_CRITICAL("Unable to alloc mem for signal mti read buffer: ABORTING"); } break; default: - LOG_CRITICAL("Unable to handle onject type for %s (%d)", + LOG_ERROR("Unable to handle onject type for %s (%d)", name.c_str(), m_fli_type); } m_val_buff = (char*)malloc(m_val_len+1); if (!m_val_buff) { - LOG_CRITICAL("Unable to alloc mem for signal read buffer"); + LOG_CRITICAL("Unable to alloc mem for signal read buffer: ABORTING"); } m_val_buff[m_val_len] = '\0'; m_val_str_buff = (char*)malloc(m_val_str_len+1); if (!m_val_str_buff) { - LOG_CRITICAL("Unable to alloc mem for signal write buffer"); + LOG_CRITICAL("Unable to alloc mem for signal write buffer: ABORTING"); } m_val_str_buff[m_val_str_len] = '\0'; @@ -543,7 +543,7 @@ const char* FliVariableObjHdl::get_signal_value_binstr(void) } break; default: - LOG_CRITICAL("Variable %s type %d not currently supported", + LOG_ERROR("Variable %s type %d not currently supported", m_name.c_str(), m_fli_type); break; } @@ -582,17 +582,17 @@ int FliVariableObjHdl::initialise(std::string &name) m_val_len = mti_TickLength(mti_GetVarType(m_fli_hdl)); m_mti_buff = (mtiInt32T*)malloc(sizeof(*m_mti_buff) * m_val_len); if (!m_mti_buff) { - LOG_CRITICAL("Unable to alloc mem for signal mti read buffer"); + LOG_CRITICAL("Unable to alloc mem for signal mti read buffer: ABORTING"); } break; default: - LOG_CRITICAL("Unable to handle onject type for %s (%d)", + LOG_ERROR("Unable to handle object type for %s (%d)", name.c_str(), m_fli_type); } m_val_buff = (char*)malloc(m_val_len+1); if (!m_val_buff) { - LOG_CRITICAL("Unable to alloc mem for signal read buffer"); + LOG_CRITICAL("Unable to alloc mem for signal read buffer: ABORTING"); } m_val_buff[m_val_len] = '\0'; diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 41eed7b1..64c41da3 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -62,14 +62,14 @@ int VhpiSignalObjHdl::initialise(std::string &name) { m_value.bufSize = m_size*sizeof(vhpiEnumT); m_value.value.enumvs = (vhpiEnumT *)malloc(m_value.bufSize); if (!m_value.value.enumvs) { - LOG_CRITICAL("Unable to alloc mem for write buffer"); + LOG_CRITICAL("Unable to alloc mem for write buffer: ABORTING"); } break; } default: { - LOG_CRITICAL("Unable to determine property for %s (%d) format object", + LOG_ERROR("Unable to determine property for %s (%d) format object", ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format), m_value.format); } } @@ -85,7 +85,7 @@ int VhpiSignalObjHdl::initialise(std::string &name) { m_binvalue.value.str = (vhpiCharT *)calloc(m_binvalue.bufSize, m_binvalue.bufSize); if (!m_value.value.str) { - LOG_CRITICAL("Unable to alloc mem for read buffer"); + LOG_CRITICAL("Unable to alloc mem for read buffer: ABORTING"); } GpiObjHdl::initialise(name); @@ -210,7 +210,7 @@ int VhpiSignalObjHdl::set_signal_value(int value) } default: { - LOG_CRITICAL("VHPI type of object has changed at runtime, big fail"); + LOG_CRITICAL("VHPI type of object has changed at runtime: ABORTING"); } } vhpi_put_value(GpiObjHdl::get_handle(), &m_value, vhpiForcePropagate); @@ -256,7 +256,7 @@ int VhpiSignalObjHdl::set_signal_value(std::string &value) } default: { - LOG_CRITICAL("VHPI type of object has changed at runtime, big fail"); + LOG_CRITICAL("VHPI type of object has changed at runtime: ABORTING"); } } diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 867e78ef..9cc3bf07 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -191,13 +191,13 @@ GpiObjHdl *VpiImpl::get_root_handle(const char* name) error: - LOG_CRITICAL("VPI: Couldn't find root handle %s", name); + LOG_ERROR("VPI: Couldn't find root handle %s", name); iterator = vpi_iterate(vpiModule, NULL); for (root = vpi_scan(iterator); root != NULL; root = vpi_scan(iterator)) { - LOG_CRITICAL("VPI: Toplevel instances: %s != %s...", name, vpi_get_str(vpiFullName, root)); + LOG_ERROR("VPI: Toplevel instances: %s != %s...", name, vpi_get_str(vpiFullName, root)); if (name == NULL || !strcmp(name, vpi_get_str(vpiFullName, root))) break; @@ -273,7 +273,7 @@ int32_t handle_vpi_callback(p_cb_data cb_data) VpiCbHdl *cb_hdl = (VpiCbHdl*)cb_data->user_data; if (!cb_hdl) { - LOG_CRITICAL("VPI: Callback data corrupted"); + LOG_CRITICAL("VPI: Callback data corrupted: ABORTING"); } gpi_cb_state_e old_state = cb_hdl->get_call_state(); From 3ed7c4d5bbe7a049f5e29691d7a39f0adf861099 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 11 Sep 2015 13:11:09 +0100 Subject: [PATCH 0981/2656] Issue #309: Convert NULL to None on return from simulator.get_root_handle() --- cocotb/regression.py | 6 ++++-- lib/gpi/GpiCommon.cpp | 1 + lib/simulator/simulatormodule.c | 5 +++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 3bbce0b3..a6ad6561 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -101,8 +101,10 @@ def initialise(self): self._cov = coverage.coverage(branch=True, omit=["*cocotb*"]) self._cov.start() - self._dut = cocotb.handle.SimHandle(simulator.get_root_handle( - self._root_name)) + handle = simulator.get_root_handle(self._root_name) + + self._dut = cocotb.handle.SimHandle(handle) if handle else None + if self._dut is None: raise AttributeError("Can not find Root Handle (%s)" % self._root_name) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index ade441a5..470d2078 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -177,6 +177,7 @@ gpi_sim_hdl gpi_get_root_handle(const char *name) return (gpi_sim_hdl)hdl; } } + LOG_DEBUG("No root handle found"); return NULL; } diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index b2377916..f5f87ba3 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -658,6 +658,11 @@ static PyObject *get_root_handle(PyObject *self, PyObject *args) } result = gpi_get_root_handle(name); + if (NULL == result) { + DROP_GIL(gstate); + Py_RETURN_NONE; + } + value = Py_BuildValue("l", result); From b6b939ed7f3c0b285f0635e11595bb7f1dc84b51 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Fri, 11 Sep 2015 13:23:00 +0100 Subject: [PATCH 0982/2656] Issue #310: Improve logging when unable to import MODULE --- cocotb/regression.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 3bbce0b3..b00dcf0e 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -109,7 +109,13 @@ def initialise(self): # Auto discovery for module_name in self._modules: - module = _my_import(module_name) + try: + module = _my_import(module_name) + except ImportError: + self.log.critical("Failed to import module %s", module_name) + self.log.info("MODULE variable was \"%s\"", + ",".join(self._modules)) + raise if self._functions: From fc60f77658750987df99fd71a715cd6be172e4a8 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Sun, 13 Sep 2015 12:47:34 +0100 Subject: [PATCH 0983/2656] Various fixes to nvc makefile * Respect `RTL_LIBRARY` * Use correct `COCOTB_VHPI_LIB` variable for VHPI .so --- makefiles/simulators/Makefile.nvc | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/makefiles/simulators/Makefile.nvc b/makefiles/simulators/Makefile.nvc index 93debbf6..8473c8c5 100644 --- a/makefiles/simulators/Makefile.nvc +++ b/makefiles/simulators/Makefile.nvc @@ -1,17 +1,21 @@ -# -*- mode: makefile -*- +# -*- mode: makefile-gmake -*- + +RTL_LIBRARY ?= work .PHONY: analyse # Compilation phase analyse: $(VHDL_SOURCES) $(SIM_BUILD) - cd $(SIM_BUILD) && nvc -a $(VHDL_SOURCES) + cd $(SIM_BUILD) && nvc --work=$(RTL_LIBRARY) -a $(VHDL_SOURCES) -results.xml: analyse $(COCOTB_LIBS) $(COCOTB_LIB_VPI) - cd $(SIM_BUILD) && LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) nvc -e $(TOPLEVEL) - cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - LD_LIBRARY_PATH=$(LIB_DIR) \ - nvc -r --load $(LIB_DIR)/libvhpi.so $(TOPLEVEL) +results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) + cd $(SIM_BUILD) && LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) \ + nvc --work=$(RTL_LIBRARY) -e $(TOPLEVEL) + cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ + LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + LD_LIBRARY_PATH=$(LIB_DIR) \ + nvc --work=$(RTL_LIBRARY) -r --load $(COCOTB_VHPI_LIB) $(TOPLEVEL) clean:: -@rm -rf $(SIM_BUILD) From ca0ef0c25dfb0edcdbf0eac0ddae311ce646ff3e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 14 Sep 2015 13:25:53 +0100 Subject: [PATCH 0984/2656] Issue #17: Fix possible segv when freeing memory in destructor, depends on type of object. Also an error in checking memory allocation failure --- lib/vhpi/VhpiCbHdl.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index b32ed364..abc427d5 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -31,8 +31,14 @@ extern "C" void handle_vhpi_callback(const vhpiCbDataT *cb_data); VhpiSignalObjHdl::~VhpiSignalObjHdl() { - if (m_value.value.str) - free(m_value.value.str); + switch (m_value.format) { + case vhpiIntVal: + case vhpiEnumVecVal: + case vhpiLogicVecVal: + free(m_value.value.enumvs); + default: + break; + } if (m_binvalue.value.str) free(m_binvalue.value.str); @@ -67,7 +73,7 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { switch (m_value.format) { case vhpiEnumVal: case vhpiLogicVal: - m_value.value.enumv = vhpi0; + //m_value.value.enumv = vhpi0; break; case vhpiRealVal: { @@ -126,9 +132,8 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { m_binvalue.bufSize = new_size*sizeof(vhpiCharT); m_binvalue.value.str = (vhpiCharT *)calloc(m_binvalue.bufSize + 1, sizeof(vhpiCharT)); - if (!m_value.value.str) { + if (!m_binvalue.value.str) { LOG_CRITICAL("Unable to alloc mem for read buffer of signal %s", name.c_str()); - exit(1); } } From 24db57b4591325a2af1019c963351c01db854191 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 14 Sep 2015 14:59:19 +0100 Subject: [PATCH 0985/2656] Issue #17: Disable use of 64bit IUS --- makefiles/simulators/Makefile.ius | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index ea2ccb40..85fc0aab 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -29,7 +29,12 @@ # Common Makefile for Cadence Incisive -EXTRA_ARGS += -64bit +# Disable 64 bit. There are too many issues with access to values on IUS. +# hope to enable again when these have been resolved. + +ARCH=i686 +#EXTRA_ARGS += -64bit + EXTRA_ARGS += -nclibdirname $(SIM_BUILD) ifeq ($(GUI),1) From 2c80cde24396b2c1a638d75a54d01b6928109165 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 14 Sep 2015 15:02:12 +0100 Subject: [PATCH 0986/2656] Issue #17: Disable use of 64bit IUS --- makefiles/simulators/Makefile.ius | 3 +++ 1 file changed, 3 insertions(+) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 85fc0aab..d438e3cc 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -34,6 +34,9 @@ ARCH=i686 #EXTRA_ARGS += -64bit +ifeq ($(shell ncbits),64) +$(error "The 64bit version of IUS is on your PATH, please use the 32bit version for now") +endif EXTRA_ARGS += -nclibdirname $(SIM_BUILD) From ea60fe761014d3da7925687d29d595e425f79e9d Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 14 Sep 2015 15:02:42 +0100 Subject: [PATCH 0987/2656] Cleanup: Enable designs to be used externally to cocotb for cut down testcases --- tests/designs/sample_module/Makefile | 2 +- tests/designs/viterbi_decoder_axi4s/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/designs/sample_module/Makefile b/tests/designs/sample_module/Makefile index 3365f401..18e67f95 100644 --- a/tests/designs/sample_module/Makefile +++ b/tests/designs/sample_module/Makefile @@ -32,7 +32,7 @@ TOPLEVEL := sample_module TOPLEVEL_LANG ?= verilog PWD=$(shell pwd) -COCOTB=$(PWD)/../../.. +COCOTB?=$(PWD)/../../.. ifeq ($(OS),Msys) WPWD=$(shell sh -c 'pwd -W') diff --git a/tests/designs/viterbi_decoder_axi4s/Makefile b/tests/designs/viterbi_decoder_axi4s/Makefile index 640f9e9f..dcaa739c 100644 --- a/tests/designs/viterbi_decoder_axi4s/Makefile +++ b/tests/designs/viterbi_decoder_axi4s/Makefile @@ -30,7 +30,7 @@ TOPLEVEL_LANG = vhdl TOPLEVEL = dec_viterbi PWD=$(shell pwd) -COCOTB=$(PWD)/../../.. +COCOTB?=$(PWD)/../../.. SRC_BASE = $(COCOTB)/tests/designs/viterbi_decoder_axi4s From e0d40d019add4914d106cc6984528c49eecc1519 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 14 Sep 2015 15:04:07 +0100 Subject: [PATCH 0988/2656] Issue #17: Add vhpiConstDeclK to iteration map, some more changes to handle memory allocation --- lib/vhpi/VhpiCbHdl.cpp | 33 +++++++++++++++++++++------------ lib/vhpi/VhpiImpl.cpp | 5 ++++- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index abc427d5..d417c284 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -32,7 +32,7 @@ extern "C" void handle_vhpi_callback(const vhpiCbDataT *cb_data); VhpiSignalObjHdl::~VhpiSignalObjHdl() { switch (m_value.format) { - case vhpiIntVal: + case vhpiIntVecVal: case vhpiEnumVecVal: case vhpiLogicVecVal: free(m_value.value.enumvs); @@ -71,17 +71,15 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { m_num_elems = m_value.numElems; switch (m_value.format) { + case vhpiIntVal: case vhpiEnumVal: case vhpiLogicVal: - //m_value.value.enumv = vhpi0; - break; - case vhpiRealVal: { GpiObjHdl::initialise(name, fq_name); return 0; } - case vhpiIntVal: + case vhpiIntVecVal: case vhpiEnumVecVal: case vhpiLogicVecVal: { m_num_elems = vhpi_get(vhpiSizeP, GpiObjHdl::get_handle()); @@ -95,7 +93,7 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { VhpiIterator test_iter(this->m_impl, this); GpiObjHdl::initialise(name, fq_name); - return 0; + break; } case vhpiRawDataVal: { // This is an internal representation - the only way to determine @@ -122,10 +120,10 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { int new_size = vhpi_get_value(GpiObjHdl::get_handle(), &m_binvalue); if (new_size < 0) { - LOG_CRITICAL("Failed to determine size of signal object %s of type %s", + LOG_WARN("Failed to determine size of signal object %s of type %s", name.c_str(), ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); - goto out; + return -1; } if (new_size) { @@ -137,7 +135,6 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { } } -out: return GpiObjHdl::initialise(name, fq_name); } @@ -286,9 +283,11 @@ int VhpiSignalObjHdl::set_signal_value(double value) break; case vhpiEnumVal: - case vhpiLogicVal: case vhpiEnumVecVal: + case vhpiLogicVal: case vhpiLogicVecVal: + case vhpiIntVal: + case vhpiIntVecVal: LOG_WARN("Attempt to set non vhpiRealVal signal with double"); return 0; @@ -317,8 +316,8 @@ int VhpiSignalObjHdl::set_signal_value(std::string &value) int len = value.length(); if (len > m_num_elems) { - LOG_ERROR("VHPI: Attempt to write string longer than signal %d > %d", - len, m_num_elems); + LOG_ERROR("VHPI: Attempt to write string longer than (%s) signal %d > %d", + m_name.c_str(), len, m_num_elems); return -1; } @@ -579,6 +578,16 @@ KindMappings::KindMappings() (vhpiOneToManyT)0, }; add_to_options(vhpiIfGenerateK, &ifgen_options[0]); + + /* vhpiConstDeclK */ + vhpiOneToManyT const_options[] = { + vhpiAttrSpecs, + vhpiIndexedNames, + vhpiSelectedNames, + (vhpiOneToManyT)0, + }; + add_to_options(vhpiConstDeclK, &const_options[0]); + } void KindMappings::add_to_options(vhpiClassKindT type, vhpiOneToManyT *options) diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index e9b429bf..a86bb948 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -258,7 +258,10 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, return NULL; } - new_obj->initialise(name, fq_name); + if (new_obj->initialise(name, fq_name)) { + delete new_obj; + new_obj = NULL; + } return new_obj; } From 404b6b72a85c2ffc80d82fba48a23f8ab77189b7 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 14 Sep 2015 16:59:35 +0100 Subject: [PATCH 0989/2656] Cleanup: Exclude tests on 32 bit version of IUS as well --- tests/test_cases/test_cocotb/test_cocotb.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 4f5ebe4d..b0555e3f 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -192,7 +192,8 @@ def do_test_afterdelay_in_readonly(dut, delay): "Riviera-PRO", "ModelSim ALTERA STARTER EDITION", "ModelSim DE", - "ncsim(64)"]) + "ncsim(64)", + "ncsim"]) def test_readwrite_in_readonly(dut): """Test doing invalid sim operation""" global exited @@ -207,7 +208,8 @@ def test_readwrite_in_readonly(dut): @cocotb.test(expect_fail=cocotb.SIM_NAME in ["Icarus Verilog", "Chronologic Simulation VCS Release"], - skip=cocotb.SIM_NAME in ["ncsim(64)"]) + skip=cocotb.SIM_NAME in ["ncsim(64)", + "ncsim"]) def test_afterdelay_in_readonly(dut): """Test doing invalid sim operation""" global exited From c1183ec3e99d21b79d0a51ebb0558ea45aa2328a Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 14 Sep 2015 17:27:15 +0100 Subject: [PATCH 0990/2656] Issue #17: A few fixes here. 1. Allow getting and setting via BinaryString on simulators that allow it. 2. Fixup name generation for finding objects, this was find on Aldec but needed changes for IUS due the requirement placed on the toplevel and it not being a real name Aldec = top_level.array[idx].signal IUS = :array[idx].signal 3. Detection of n dimentionsal arrays also fixed to work with both simulators, there is an assumption here that may not be true for everything. But the testcases pass for normal access and an n-d. --- lib/vhpi/VhpiCbHdl.cpp | 34 +++++++++++++++++++--------------- lib/vhpi/VhpiImpl.cpp | 28 +++++++++++++++++++--------- 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index d417c284..45a1089c 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -76,7 +76,7 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { case vhpiLogicVal: case vhpiRealVal: { GpiObjHdl::initialise(name, fq_name); - return 0; + break; } case vhpiIntVecVal: @@ -120,10 +120,9 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { int new_size = vhpi_get_value(GpiObjHdl::get_handle(), &m_binvalue); if (new_size < 0) { - LOG_WARN("Failed to determine size of signal object %s of type %s", - name.c_str(), - ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); - return -1; + LOG_WARN("Failed to determine size for vhpiBinStrVal of signal object %s of type %s, falling back", + name.c_str(), + ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); } if (new_size) { @@ -316,16 +315,15 @@ int VhpiSignalObjHdl::set_signal_value(std::string &value) int len = value.length(); if (len > m_num_elems) { - LOG_ERROR("VHPI: Attempt to write string longer than (%s) signal %d > %d", + LOG_DEBUG("VHPI: Attempt to write string longer than (%s) signal %d > %d", m_name.c_str(), len, m_num_elems); - return -1; } std::string::iterator iter; int i = 0; for (iter = value.begin(); - iter != value.end(); + (iter != value.end()) && (i < m_num_elems); iter++, i++) { m_value.value.enumvs[i] = chr2vhpi(*iter); } @@ -357,12 +355,12 @@ int VhpiSignalObjHdl::set_signal_value(std::string &value) const char* VhpiSignalObjHdl::get_signal_value_binstr(void) { switch (m_value.format) { - case vhpiEnumVecVal: - case vhpiLogicVecVal: - LOG_DEBUG("get_signal_value_binstr not supported for %s", + case vhpiRealVal: + LOG_INFO("get_signal_value_binstr not supported for %s", ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); return ""; default: { + /* Some simulators do not support BinaryValues so we fake up here for them */ int ret = vhpi_get_value(GpiObjHdl::get_handle(), &m_binvalue); if (ret) { check_vhpi_error(); @@ -384,8 +382,7 @@ double VhpiSignalObjHdl::get_signal_value_real(void) m_value.numElems = 1; m_value.bufSize = sizeof(double); - int ret = vhpi_get_value(GpiObjHdl::get_handle(), &m_value); - if (ret) { + if (vhpi_get_value(GpiObjHdl::get_handle(), &m_value)) { check_vhpi_error(); LOG_ERROR("failed to get real value"); } @@ -398,8 +395,10 @@ long VhpiSignalObjHdl::get_signal_value_long(void) value.format = vhpiIntVal; value.numElems = 0; - if (vhpi_get_value(GpiObjHdl::get_handle(), &value)) + if (vhpi_get_value(GpiObjHdl::get_handle(), &value)) { check_vhpi_error(); + LOG_ERROR("failed to get long value"); + } return value.value.intg; } @@ -753,7 +752,12 @@ int VhpiIterator::next_handle(std::string &name, GpiObjHdl **hdl) /* We try and create a handle internally, if this is not possible we return and GPI will try other implementations with the name */ - std::string fq_name = m_parent->get_fullname() + "." + name; + std::string fq_name = m_parent->get_fullname(); + if (fq_name == ":") { + fq_name += name; + } else { + fq_name += "." + name; + } VhpiImpl *vhpi_impl = reinterpret_cast(m_impl); new_obj = vhpi_impl->create_gpi_obj_from_handle(obj, name, fq_name); if (new_obj) { diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index a86bb948..89fcd496 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -230,7 +230,13 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, then we create a non signal object */ int num_elems = vhpi_get(vhpiSizeP, new_hdl); - if (value.numElems == num_elems) { + + /* More differences between simulators. + Aldec sets value.numElems regardless + IUS does not set for a single dimension. + */ + + if (!value.numElems || (value.numElems == num_elems)) { LOG_DEBUG("Detected single dimension vector type", fq_name.c_str()); gpi_type = GPI_ARRAY; } else { @@ -269,23 +275,22 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) { vhpiHandleT new_hdl; - std::string search_name = parent->get_name(); - if (search_name == ":") { - search_name += name; + std::string fq_name = parent->get_fullname(); + if (fq_name == ":") { + fq_name += name; } else { - search_name = search_name + "." + name; + fq_name += "." + name; } - std::vector writable(search_name.begin(), search_name.end()); + std::vector writable(fq_name.begin(), fq_name.end()); writable.push_back('\0'); new_hdl = vhpi_handle_by_name(&writable[0], NULL); if (new_hdl == NULL) { - LOG_DEBUG("Unable to query vhpi_handle_by_name %s", search_name.c_str()); + LOG_DEBUG("Unable to query vhpi_handle_by_name %s", fq_name.c_str()); return NULL; } - std::string fq_name = parent->get_fullname() + "." + name; GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { vhpi_release_handle(new_hdl); @@ -336,7 +341,12 @@ GpiObjHdl *VhpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) } std::string name = vhpi_get_str(vhpiNameP, new_hdl); - std::string fq_name = parent->get_fullname() + "." + name; + std::string fq_name = parent->get_fullname(); + if (fq_name == ":") { + fq_name += name; + } else { + fq_name += "." + name; + } GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { vhpi_release_handle(new_hdl); From 6dc5c4dbc9e99b50bacbc8e720721026c99ffa4e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 14 Sep 2015 21:06:10 +0100 Subject: [PATCH 0991/2656] Issue #17: Allow ARCH to be set externally --- makefiles/simulators/Makefile.ius | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index d438e3cc..27eced58 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -32,7 +32,7 @@ # Disable 64 bit. There are too many issues with access to values on IUS. # hope to enable again when these have been resolved. -ARCH=i686 +ARCH?=i686 #EXTRA_ARGS += -64bit ifeq ($(shell ncbits),64) $(error "The 64bit version of IUS is on your PATH, please use the 32bit version for now") From fe916b4f8dd8a173311b8ac19a818125c1f4d9dc Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 16 Sep 2015 17:28:44 +0100 Subject: [PATCH 0992/2656] Issue #17: Addition of some debug for VPI object creation --- lib/vpi/VpiCbHdl.cpp | 7 +++++-- lib/vpi/VpiImpl.cpp | 21 ++++++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index d81bd74c..910e9ca0 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -455,7 +455,6 @@ std::vector* KindMappings::get_options(int32_t type) std::map >::iterator valid = options_map.find(type); if (options_map.end() == valid) { - LOG_ERROR("VPI: Implementation does not know how to iterate over %d", type); return NULL; } else { return &valid->second; @@ -471,8 +470,12 @@ VpiIterator::VpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(i vpiHandle iterator; vpiHandle vpi_hdl = m_parent->get_handle(); - if (NULL == (selected = iterate_over.get_options(vpi_get(vpiType, vpi_hdl)))) + int type = vpi_get(vpiType, vpi_hdl); + if (NULL == (selected = iterate_over.get_options(type))) { + LOG_ERROR("VPI: Implementation does not know how to iterate over %s(%d)", + vpi_get_str(vpiType, vpi_hdl), type); return; + } for (one2many = selected->begin(); diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 4544d010..f1a2b0b3 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -108,6 +108,9 @@ gpi_objtype_t to_gpi_objtype(int32_t vpitype) case vpiModule: case vpiRefObj: case vpiPort: + case vpiAlways: + case vpiFunction: + case vpiInitial: return GPI_MODULE; default: @@ -153,15 +156,31 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, case vpiRefObj: case vpiPackedArrayVar: case vpiPort: + case vpiAlways: + case vpiFunction: + case vpiInitial: new_obj = new GpiObjHdl(this, new_hdl, to_gpi_objtype(type)); break; default: - LOG_WARN("Not able to map type %d to object.", type); + /* We should only print a warning here if the type is really verilog, + It could be vhdl as some simulators allow qurying of both languages + via the same handle + */ + const char *type_name = vpi_get_str(vpiType, new_hdl); + std::string unknown = "vpiUnknown"; + if (unknown != type_name) { + LOG_WARN("VPI: Not able to map type %s(%d) to object.", type_name, type); + } else { + LOG_DEBUG("VPI: Simulator does not know this type (%d) via VPI", type); + } return NULL; } new_obj->initialise(name, fq_name); + LOG_DEBUG("VPI: Created object with type was %s(%d)", + vpi_get_str(vpiType, new_hdl), type); + return new_obj; } From 58ea2efaf2a8ad93e904e360f2f5b06d63418fb0 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 16 Sep 2015 17:29:08 +0100 Subject: [PATCH 0993/2656] Issue #17: Fixup pass reults for different simulators --- .../test_iteration_verilog/test_iteration_es.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/test_cases/test_iteration_verilog/test_iteration_es.py b/tests/test_cases/test_iteration_verilog/test_iteration_es.py index 724c29e5..fdf9ab18 100644 --- a/tests/test_cases/test_iteration_verilog/test_iteration_es.py +++ b/tests/test_cases/test_iteration_verilog/test_iteration_es.py @@ -34,6 +34,15 @@ def recursive_discovery(dut): """ Recursively discover every single object in the design """ + if cocotb.SIM_NAME in ["ModelSim ALTERA STARTER EDITION", + "ModelSim DE" + "ncsim(64)", + "ncsim"]: + # vpiAlways does not show up in IUS + pass_total = 259 + else: + pass_total = 265 + tlog = logging.getLogger("cocotb.test") yield Timer(100) def dump_all_the_things(parent): @@ -45,8 +54,8 @@ def dump_all_the_things(parent): return count total = dump_all_the_things(dut) tlog.info("Found a total of %d things", total) - if total != 258: - raise TestFailure("Expected 258 objects but found %d" % total) + if total != pass_total: + raise TestFailure("Expected %d objects but found %d" % (pass_total, total)) @cocotb.coroutine def iteration_loop(dut): From ee90bb7a10fb9a68aee6b878675712185e827aa2 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 16 Sep 2015 17:32:21 +0100 Subject: [PATCH 0994/2656] Issue #17: Addition of testcase to check type of objects before and after iteration --- tests/test_cases/test_verilog_access/Makefile | 32 +++++++++ .../test_verilog_access.py | 68 +++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 tests/test_cases/test_verilog_access/Makefile create mode 100644 tests/test_cases/test_verilog_access/test_verilog_access.py diff --git a/tests/test_cases/test_verilog_access/Makefile b/tests/test_cases/test_verilog_access/Makefile new file mode 100644 index 00000000..a557fd5d --- /dev/null +++ b/tests/test_cases/test_verilog_access/Makefile @@ -0,0 +1,32 @@ +############################################################################## +# Copyright (c) 2015 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +PWD := $(shell pwd) +COCOTB := $(PWD)/../../.. + +include ../../designs/uart2bus/Makefile +MODULE = test_verilog_access diff --git a/tests/test_cases/test_verilog_access/test_verilog_access.py b/tests/test_cases/test_verilog_access/test_verilog_access.py new file mode 100644 index 00000000..00659b89 --- /dev/null +++ b/tests/test_cases/test_verilog_access/test_verilog_access.py @@ -0,0 +1,68 @@ +''' Copyright (c) 2015 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +import logging + +import cocotb +from cocotb.handle import HierarchyObject, ModifiableObject, RealObject, IntegerObject, ConstantObject +from cocotb.triggers import Timer +from cocotb.result import TestError, TestFailure + +@cocotb.test() +def port_not_hierarchy(dut): + """ + Test for issue raised by Luke - iteration causes a toplevel port type to + change from from ModifiableObject to HierarchyObject + """ + fails = 0 + tlog = logging.getLogger("cocotb.test") + yield Timer(100) + + def check_instance(obj, objtype): + if not isinstance(obj, objtype): + tlog.error("Expected %s to be of type %s but got %s" % ( + obj._fullname, objtype.__name__, obj.__class__.__name__)) + return 1 + tlog.info("%s is %s" % (obj._fullname, obj.__class__.__name__)) + return 0 + + fails += check_instance(dut.clk, ModifiableObject) + fails += check_instance(dut.i_verilog, HierarchyObject) + fails += check_instance(dut.i_verilog.clock, ModifiableObject) + fails += check_instance(dut.i_verilog.tx_data, ModifiableObject) + + for _ in dut: + pass + + for _ in dut.i_verilog: + pass + + fails += check_instance(dut.clk, ModifiableObject) + fails += check_instance(dut.i_verilog, HierarchyObject) + fails += check_instance(dut.i_verilog.clock, ModifiableObject) + fails += check_instance(dut.i_verilog.tx_data, ModifiableObject) + + if fails: + raise TestFailure("%d Failures during the test" % fails) From 45ba89b3bc8714726bd353d5ae1e45a0b509f839 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 16 Sep 2015 17:54:13 +0100 Subject: [PATCH 0995/2656] Issue #17: Fix Possible error if pointer from VPI is NULL --- lib/vpi/VpiImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index f1a2b0b3..14232a9b 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -168,7 +168,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, */ const char *type_name = vpi_get_str(vpiType, new_hdl); std::string unknown = "vpiUnknown"; - if (unknown != type_name) { + if (type_name && (unknown != type_name)) { LOG_WARN("VPI: Not able to map type %s(%d) to object.", type_name, type); } else { LOG_DEBUG("VPI: Simulator does not know this type (%d) via VPI", type); From 9c284ef8fb88a4a675a8df9b63f22a7088eec8ec Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 16 Sep 2015 18:47:26 +0100 Subject: [PATCH 0996/2656] Issue #17: Store object type string as self._type in SimHandleBase --- cocotb/handle.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 0d4a2d46..c2293f2a 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -83,7 +83,8 @@ def __init__(self, handle): self._discovered = False self._name = simulator.get_name_string(self._handle) - self._fullname = self._name + "(%s)" % simulator.get_type_string(self._handle) + self._type = simulator.get_type_string(self._handle) + self._fullname = self._name + "(%s)" % self._type self._log = SimLog("cocotb.%s" % self._name) self._log.debug("Created") From 837b5c6f2108bc6808702321e87b5a094ea48877 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Fri, 18 Sep 2015 11:53:37 +0200 Subject: [PATCH 0997/2656] adding properties for avalonMemory bus driver --- cocotb/drivers/avalon.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 6af12259..511c615e 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -56,6 +56,7 @@ class AvalonMM(BusDriver): _optional_signals = ["readdata", "read", "write", "waitrequest", "writedata", "readdatavalid", "byteenable"] + def __init__(self, entity, name, clock): BusDriver.__init__(self, entity, name, clock) self._can_read = False @@ -196,12 +197,18 @@ class AvalonMemory(BusDriver): """ _signals = ["address"] _optional_signals = ["write", "read", "writedata", "readdatavalid", - "readdata", "waitrequest"] + "readdata", "waitrequest", "burstcount", "byteenable"] + _avalon_properties = { + "burstCountUnits": "symbols", # symbols or words + "addressUnits": "symbols", # symbols or words + } def __init__(self, entity, name, clock, readlatency_min=1, - readlatency_max=1, memory=None): + readlatency_max=1, memory=None, avl_properties={}): BusDriver.__init__(self, entity, name, clock) + if avl_properties != {}: + self._avalon_properties = avl_properties self._readable = False self._writeable = False From ad22da140beab3179e22b2dc68722ae443480e77 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Sun, 20 Sep 2015 09:25:32 +0200 Subject: [PATCH 0998/2656] Adding testing framework for avalon --- tests/designs/avalon_module/Makefile | 49 ++++++++++++++++++ tests/designs/avalon_module/avalon_module.v | 34 ++++++++++++ tests/test_cases/test_avalon/Makefile | 33 ++++++++++++ tests/test_cases/test_avalon/test_avalon.py | 57 +++++++++++++++++++++ 4 files changed, 173 insertions(+) create mode 100644 tests/designs/avalon_module/Makefile create mode 100644 tests/designs/avalon_module/avalon_module.v create mode 100644 tests/test_cases/test_avalon/Makefile create mode 100644 tests/test_cases/test_avalon/test_avalon.py diff --git a/tests/designs/avalon_module/Makefile b/tests/designs/avalon_module/Makefile new file mode 100644 index 00000000..285bb8b5 --- /dev/null +++ b/tests/designs/avalon_module/Makefile @@ -0,0 +1,49 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +TOPLEVEL := avalon_module +TOPLEVEL_LANG ?= verilog + +PWD=$(shell pwd) +COCOTB=$(PWD)/../../.. + +ifeq ($(OS),Msys) +WPWD=$(shell sh -c 'pwd -W') +else +WPWD=$(shell pwd) +endif + +VERILOG_SOURCES = $(COCOTB)/tests/designs/avalon_module/avalon_module.v +GPI_IMPL := vpi + +export TOPLEVEL_LANG + +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim diff --git a/tests/designs/avalon_module/avalon_module.v b/tests/designs/avalon_module/avalon_module.v new file mode 100644 index 00000000..3556b1de --- /dev/null +++ b/tests/designs/avalon_module/avalon_module.v @@ -0,0 +1,34 @@ +`timescale 1 ps / 1 ps + +module avalon_module ( + input clk, + + output reg stream_in_ready, + input stream_in_valid, + input [7:0] stream_in_data, + input [63:0] stream_in_data_wide, + + input stream_out_ready, + output reg [7:0] stream_out_data_comb, + output reg [7:0] stream_out_data_registered +); + +always @(posedge clk) + stream_out_data_registered <= stream_in_data; + +always @(stream_in_data) + stream_out_data_comb = stream_in_data; + +always @(stream_out_ready) + stream_in_ready = stream_out_ready; + + +initial begin + $dumpfile("waveform.vcd"); + $dumpvars(0,avalon_module); + +// TODO: Move into a separate test +// #500000 $fail_test("Test timed out, failing..."); +end + +endmodule diff --git a/tests/test_cases/test_avalon/Makefile b/tests/test_cases/test_avalon/Makefile new file mode 100644 index 00000000..31096bb2 --- /dev/null +++ b/tests/test_cases/test_avalon/Makefile @@ -0,0 +1,33 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +include $(COCOTB)/tests/designs/avalon_module/Makefile + +MODULE = test_avalon diff --git a/tests/test_cases/test_avalon/test_avalon.py b/tests/test_cases/test_avalon/test_avalon.py new file mode 100644 index 00000000..ab5d072f --- /dev/null +++ b/tests/test_cases/test_avalon/test_avalon.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python + +''' Copyright (c) 2013 Potential Ventures Ltd +Copyright (c) 2013 SolarFlare Communications Inc +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd, + SolarFlare Communications Inc nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' +import logging + +""" +A set of tests that demonstrate cocotb functionality + +Also used a regression test of cocotb capabilities +""" + +import cocotb +from cocotb.triggers import (Timer, Join, RisingEdge, FallingEdge, Edge, + ReadOnly, ReadWrite) +from cocotb.clock import Clock +from cocotb.result import ReturnValue, TestFailure, TestError, TestSuccess + +# Tests relating to providing meaningful errors if we forget to use the +# yield keyword correctly to turn a function into a coroutine + +@cocotb.test(expect_fail=False) +def test_function_reentrant_clock(dut): + """Test yielding a reentrant clock""" + clock = dut.clk + timer = Timer(100) + for i in range(10): + clock <= 0 + yield timer + clock <= 1 + yield timer + + From c0c0deec0f563586565b49149e673e30c7b69a29 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Sun, 20 Sep 2015 16:24:15 +0200 Subject: [PATCH 0999/2656] import verilog bursting example from altera --- tests/designs/avalon_module/Makefile | 4 +- tests/designs/avalon_module/avalon_module.v | 34 --- .../designs/avalon_module/burst_read_master.v | 276 ++++++++++++++++++ 3 files changed, 278 insertions(+), 36 deletions(-) delete mode 100644 tests/designs/avalon_module/avalon_module.v create mode 100644 tests/designs/avalon_module/burst_read_master.v diff --git a/tests/designs/avalon_module/Makefile b/tests/designs/avalon_module/Makefile index 285bb8b5..f86bd4e1 100644 --- a/tests/designs/avalon_module/Makefile +++ b/tests/designs/avalon_module/Makefile @@ -28,7 +28,7 @@ ############################################################################### -TOPLEVEL := avalon_module +TOPLEVEL := burst_read_master TOPLEVEL_LANG ?= verilog PWD=$(shell pwd) @@ -40,7 +40,7 @@ else WPWD=$(shell pwd) endif -VERILOG_SOURCES = $(COCOTB)/tests/designs/avalon_module/avalon_module.v +VERILOG_SOURCES = $(COCOTB)/tests/designs/avalon_module/burst_read_master.v GPI_IMPL := vpi export TOPLEVEL_LANG diff --git a/tests/designs/avalon_module/avalon_module.v b/tests/designs/avalon_module/avalon_module.v deleted file mode 100644 index 3556b1de..00000000 --- a/tests/designs/avalon_module/avalon_module.v +++ /dev/null @@ -1,34 +0,0 @@ -`timescale 1 ps / 1 ps - -module avalon_module ( - input clk, - - output reg stream_in_ready, - input stream_in_valid, - input [7:0] stream_in_data, - input [63:0] stream_in_data_wide, - - input stream_out_ready, - output reg [7:0] stream_out_data_comb, - output reg [7:0] stream_out_data_registered -); - -always @(posedge clk) - stream_out_data_registered <= stream_in_data; - -always @(stream_in_data) - stream_out_data_comb = stream_in_data; - -always @(stream_out_ready) - stream_in_ready = stream_out_ready; - - -initial begin - $dumpfile("waveform.vcd"); - $dumpvars(0,avalon_module); - -// TODO: Move into a separate test -// #500000 $fail_test("Test timed out, failing..."); -end - -endmodule diff --git a/tests/designs/avalon_module/burst_read_master.v b/tests/designs/avalon_module/burst_read_master.v new file mode 100644 index 00000000..6d3763c0 --- /dev/null +++ b/tests/designs/avalon_module/burst_read_master.v @@ -0,0 +1,276 @@ +/* + Legal Notice: (C)2007 Altera Corporation. All rights reserved. Your + use of Altera Corporation's design tools, logic functions and other + software and tools, and its AMPP partner logic functions, and any + output files any of the foregoing (including device programming or + simulation files), and any associated documentation or information are + expressly subject to the terms and conditions of the Altera Program + License Subscription Agreement or other applicable license agreement, + including, without limitation, that your use is for the sole purpose + of programming logic devices manufactured by Altera and sold by Altera + or its authorized distributors. Please refer to the applicable + agreement for further details. +*/ + +/* + + Author: JCJB + Date: 11/04/2007 + + This bursting read master is passed a word aligned address, length in bytes, + and a 'go' bit. The master will continue to post full length bursts until + the length register reaches a value less than a full burst. A single final + burst is then posted and when all the reads return the done bit will be asserted. + + To use this master you must simply drive the control signals into this block, + and also read the data from the exposed read FIFO. To read from the exposed FIFO + use the 'user_read_buffer' signal to pop data from the FIFO 'user_buffer_data'. + The signal 'user_data_available' is asserted whenever data is available from the + exposed FIFO. + +*/ + +// altera message_off 10230 + +/* altera scfifo model rewritten */ +module scfifo ( + input aclr, + input clock, + input [31:0] data, + output empty, + output [31:0] q, + input rdreq, + input [4:0] usedw, + input wrreq); + + localparam lpm_width = 32; + localparam lpm_numwords = 32; + localparam lpm_showahead = "ON"; + localparam use_eab = "ON"; + localparam add_ram_output_register = "OFF"; + localparam underflow_checking = "OFF"; + localparam overflow_checking = "OFF"; + +endmodule + +module burst_read_master ( + clk, + reset, + + // control inputs and outputs + control_fixed_location, + control_read_base, + control_read_length, + control_go, + control_done, + control_early_done, + + // user logic inputs and outputs + user_read_buffer, + user_buffer_data, + user_data_available, + + // master inputs and outputs + master_address, + master_read, + master_byteenable, + master_readdata, + master_readdatavalid, + master_burstcount, + master_waitrequest +); + + parameter DATAWIDTH = 32; + parameter MAXBURSTCOUNT = 4; + parameter BURSTCOUNTWIDTH = 3; + parameter BYTEENABLEWIDTH = 4; + parameter ADDRESSWIDTH = 32; + parameter FIFODEPTH = 32; + parameter FIFODEPTH_LOG2 = 5; + parameter FIFOUSEMEMORY = 1; // set to 0 to use LEs instead + + input clk; + input reset; + + + // control inputs and outputs + input control_fixed_location; + input [ADDRESSWIDTH-1:0] control_read_base; + input [ADDRESSWIDTH-1:0] control_read_length; + input control_go; + output wire control_done; + output wire control_early_done; // don't use this unless you know what you are doing, it's going to fire when the last read is posted, not when the last data returns! + + // user logic inputs and outputs + input user_read_buffer; + output wire [DATAWIDTH-1:0] user_buffer_data; + output wire user_data_available; + + // master inputs and outputs + input master_waitrequest; + input master_readdatavalid; + input [DATAWIDTH-1:0] master_readdata; + output wire [ADDRESSWIDTH-1:0] master_address; + output wire master_read; + output wire [BYTEENABLEWIDTH-1:0] master_byteenable; + output wire [BURSTCOUNTWIDTH-1:0] master_burstcount; + + // internal control signals + reg control_fixed_location_d1; + wire fifo_empty; + reg [ADDRESSWIDTH-1:0] address; + reg [ADDRESSWIDTH-1:0] length; + reg [FIFODEPTH_LOG2-1:0] reads_pending; + wire increment_address; + wire [BURSTCOUNTWIDTH-1:0] burst_count; + wire [BURSTCOUNTWIDTH-1:0] first_short_burst_count; + wire first_short_burst_enable; + wire [BURSTCOUNTWIDTH-1:0] final_short_burst_count; + wire final_short_burst_enable; + wire [BURSTCOUNTWIDTH-1:0] burst_boundary_word_address; + reg burst_begin; + wire too_many_reads_pending; + wire [FIFODEPTH_LOG2-1:0] fifo_used; + + + + // registering the control_fixed_location bit + always @ (posedge clk or posedge reset) + begin + if (reset == 1) + begin + control_fixed_location_d1 <= 0; + end + else + begin + if (control_go == 1) + begin + control_fixed_location_d1 <= control_fixed_location; + end + end + end + + + + // master address logic + always @ (posedge clk or posedge reset) + begin + if (reset == 1) + begin + address <= 0; + end + else + begin + if(control_go == 1) + begin + address <= control_read_base; + end + else if((increment_address == 1) & (control_fixed_location_d1 == 0)) + begin + address <= address + (burst_count * BYTEENABLEWIDTH); // always performing word size accesses, increment by the burst count presented + end + end + end + + + + // master length logic + always @ (posedge clk or posedge reset) + begin + if (reset == 1) + begin + length <= 0; + end + else + begin + if(control_go == 1) + begin + length <= control_read_length; + end + else if(increment_address == 1) + begin + length <= length - (burst_count * BYTEENABLEWIDTH); // always performing word size accesses, decrement by the burst count presented + end + end + end + + + + // controlled signals going to the master/control ports + assign master_address = address; + assign master_byteenable = -1; // all ones, always performing word size accesses + assign master_burstcount = burst_count; + assign control_done = (length == 0) & (reads_pending == 0); // need to make sure that the reads have returned before firing the done bit + assign control_early_done = (length == 0); // advanced feature, you should use 'control_done' if you need all the reads to return first + assign master_read = (too_many_reads_pending == 0) & (length != 0); + assign burst_boundary_word_address = ((address / BYTEENABLEWIDTH) & (MAXBURSTCOUNT - 1)); + assign first_short_burst_enable = (burst_boundary_word_address != 0); + assign final_short_burst_enable = (length < (MAXBURSTCOUNT * BYTEENABLEWIDTH)); + + assign first_short_burst_count = ((burst_boundary_word_address & 1'b1) == 1'b1)? 1 : // if the burst boundary isn't a multiple of 2 then must post a burst of 1 to get to a multiple of 2 for the next burst + (((MAXBURSTCOUNT - burst_boundary_word_address) < (length / BYTEENABLEWIDTH))? + (MAXBURSTCOUNT - burst_boundary_word_address) : (length / BYTEENABLEWIDTH)); + assign final_short_burst_count = (length / BYTEENABLEWIDTH); + + assign burst_count = (first_short_burst_enable == 1)? first_short_burst_count : // this will get the transfer back on a burst boundary, + (final_short_burst_enable == 1)? final_short_burst_count : MAXBURSTCOUNT; +assign increment_address = (too_many_reads_pending == 0) & (master_waitrequest == 0) & (length != 0); +assign too_many_reads_pending = (reads_pending + fifo_used) >= (FIFODEPTH - MAXBURSTCOUNT - 4); // make sure there are fewer reads posted than room in the FIFO + + +// tracking FIFO +always @ (posedge clk or posedge reset) +begin + if (reset == 1) + begin + reads_pending <= 0; + end + else + begin + if(increment_address == 1) + begin + if(master_readdatavalid == 0) + begin + reads_pending <= reads_pending + burst_count; + end + else + begin + reads_pending <= reads_pending + burst_count - 1; // a burst read was posted, but a word returned + end + end + else + begin + if(master_readdatavalid == 0) + begin + reads_pending <= reads_pending; // burst read was not posted and no read returned + end + else + begin + reads_pending <= reads_pending - 1; // burst read was not posted but a word returned + end + end + end +end + + +// read data feeding user logic +assign user_data_available = !fifo_empty; +scfifo the_master_to_user_fifo ( + .aclr (reset), + .clock (clk), + .data (master_readdata), + .empty (fifo_empty), + .q (user_buffer_data), + .rdreq (user_read_buffer), + .usedw (fifo_used), + .wrreq (master_readdatavalid) + ); + defparam the_master_to_user_fifo.lpm_width = DATAWIDTH; + defparam the_master_to_user_fifo.lpm_numwords = FIFODEPTH; + defparam the_master_to_user_fifo.lpm_showahead = "ON"; + defparam the_master_to_user_fifo.use_eab = (FIFOUSEMEMORY == 1)? "ON" : "OFF"; + defparam the_master_to_user_fifo.add_ram_output_register = "OFF"; + defparam the_master_to_user_fifo.underflow_checking = "OFF"; + defparam the_master_to_user_fifo.overflow_checking = "OFF"; + +endmodule From b1763e4695bb4d23458bbefbceda5df814bdf3c3 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Sun, 20 Sep 2015 16:24:37 +0200 Subject: [PATCH 1000/2656] set properties if in args --- cocotb/drivers/avalon.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 511c615e..7ee0bcee 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -208,6 +208,9 @@ def __init__(self, entity, name, clock, readlatency_min=1, BusDriver.__init__(self, entity, name, clock) if avl_properties != {}: + for key, value in self._avalon_properties.items(): + self._avalon_properties[key] = avl_properties.get(key, value) + self._avalon_properties = avl_properties self._readable = False self._writeable = False From 3ae6ed1562994ba514ac82454b5af5f46aaf8d6b Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Sun, 20 Sep 2015 17:33:22 +0200 Subject: [PATCH 1001/2656] write a scfifo model for burst read module --- .../designs/avalon_module/burst_read_master.v | 369 +++++++++++------- 1 file changed, 234 insertions(+), 135 deletions(-) diff --git a/tests/designs/avalon_module/burst_read_master.v b/tests/designs/avalon_module/burst_read_master.v index 6d3763c0..5eba4fd8 100644 --- a/tests/designs/avalon_module/burst_read_master.v +++ b/tests/designs/avalon_module/burst_read_master.v @@ -32,24 +32,106 @@ // altera message_off 10230 +`define FIFODEPTH_LOG2_DEF 5 /* altera scfifo model rewritten */ module scfifo ( - input aclr, - input clock, - input [31:0] data, - output empty, - output [31:0] q, - input rdreq, - input [4:0] usedw, - input wrreq); - - localparam lpm_width = 32; - localparam lpm_numwords = 32; - localparam lpm_showahead = "ON"; - localparam use_eab = "ON"; - localparam add_ram_output_register = "OFF"; - localparam underflow_checking = "OFF"; - localparam overflow_checking = "OFF"; + input aclr, + input clock, + input [31:0] data, + output empty, + output [31:0] q, + input rdreq, + output [`FIFODEPTH_LOG2_DEF-1:0] usedw, + input wrreq); + +localparam lpm_width = 32; +localparam lpm_numwords = 32; // FIFODEPTH +localparam lpm_showahead = "ON"; +localparam use_eab = "ON"; +localparam add_ram_output_register = "OFF"; +localparam underflow_checking = "OFF"; +localparam overflow_checking = "OFF"; + +// BEGIN PASTE + +assign q = buf_out; +assign empty = buf_empty; +assign usedw = fifo_counter; + +reg[lpm_width-1:0] buf_out; +reg buf_empty, buf_full; +reg[lpm_numwords :0] fifo_counter; +// pointer to read and write addresses +reg[`FIFODEPTH_LOG2_DEF-1:0] rd_ptr, wr_ptr; +reg[lpm_width:0] buf_mem[lpm_numwords-1 : 0]; + +always @(fifo_counter) +begin + buf_empty = (fifo_counter==0); + buf_full = (fifo_counter==lpm_numwords); +end + +always @(posedge clock or posedge aclr) +begin + if(aclr) + fifo_counter <= 0; + + else if( (!buf_full && wrreq) && ( !buf_empty && rdreq ) ) + fifo_counter <= fifo_counter; + + else if( !buf_full && wrreq ) + fifo_counter <= fifo_counter + 1; + + else if( !buf_empty && rdreq ) + fifo_counter <= fifo_counter - 1; + + else + fifo_counter <= fifo_counter; +end + +always @( posedge clock or posedge aclr) +begin + if( aclr ) + buf_out <= 0; + else + begin + if( rdreq && !buf_empty ) + buf_out <= buf_mem[rd_ptr]; + + else + buf_out <= buf_out; + + end +end + +always @(posedge clock) +begin + + if( wrreq && !buf_full ) + buf_mem[ wr_ptr ] <= data; + + else + buf_mem[ wr_ptr ] <= buf_mem[ wr_ptr ]; +end + +always@(posedge clock or posedge aclr) +begin + if( aclr ) + begin + wr_ptr <= 0; + rd_ptr <= 0; + end + else + begin + if( !buf_full && wrreq ) wr_ptr <= wr_ptr + 1; + else wr_ptr <= wr_ptr; + + if( !buf_empty && rdreq ) rd_ptr <= rd_ptr + 1; + else rd_ptr <= rd_ptr; + end + +end +// END PASTE endmodule @@ -59,68 +141,71 @@ module burst_read_master ( // control inputs and outputs control_fixed_location, - control_read_base, - control_read_length, - control_go, - control_done, - control_early_done, - - // user logic inputs and outputs - user_read_buffer, - user_buffer_data, - user_data_available, - - // master inputs and outputs - master_address, - master_read, - master_byteenable, - master_readdata, - master_readdatavalid, - master_burstcount, - master_waitrequest -); - - parameter DATAWIDTH = 32; - parameter MAXBURSTCOUNT = 4; - parameter BURSTCOUNTWIDTH = 3; - parameter BYTEENABLEWIDTH = 4; - parameter ADDRESSWIDTH = 32; - parameter FIFODEPTH = 32; - parameter FIFODEPTH_LOG2 = 5; - parameter FIFOUSEMEMORY = 1; // set to 0 to use LEs instead - - input clk; - input reset; - - - // control inputs and outputs - input control_fixed_location; - input [ADDRESSWIDTH-1:0] control_read_base; - input [ADDRESSWIDTH-1:0] control_read_length; - input control_go; - output wire control_done; - output wire control_early_done; // don't use this unless you know what you are doing, it's going to fire when the last read is posted, not when the last data returns! - - // user logic inputs and outputs - input user_read_buffer; - output wire [DATAWIDTH-1:0] user_buffer_data; - output wire user_data_available; - - // master inputs and outputs - input master_waitrequest; - input master_readdatavalid; - input [DATAWIDTH-1:0] master_readdata; - output wire [ADDRESSWIDTH-1:0] master_address; - output wire master_read; - output wire [BYTEENABLEWIDTH-1:0] master_byteenable; - output wire [BURSTCOUNTWIDTH-1:0] master_burstcount; - - // internal control signals - reg control_fixed_location_d1; - wire fifo_empty; - reg [ADDRESSWIDTH-1:0] address; - reg [ADDRESSWIDTH-1:0] length; - reg [FIFODEPTH_LOG2-1:0] reads_pending; + control_read_base, + control_read_length, + control_go, + control_done, + control_early_done, + + // user logic inputs and outputs + user_read_buffer, + user_buffer_data, + user_data_available, + + // master inputs and outputs + master_address, + master_read, + master_byteenable, + master_readdata, + master_readdatavalid, + master_burstcount, + master_waitrequest + ); + + parameter DATAWIDTH = 32; + parameter MAXBURSTCOUNT = 4; + parameter BURSTCOUNTWIDTH = 3; + parameter BYTEENABLEWIDTH = 4; + parameter ADDRESSWIDTH = 32; + parameter FIFODEPTH = 32; + parameter FIFODEPTH_LOG2 = `FIFODEPTH_LOG2_DEF; + parameter FIFOUSEMEMORY = 1; // set to 0 to use LEs instead + + input clk; + input reset; + + + // control inputs and outputs + input control_fixed_location; + input [ADDRESSWIDTH-1:0] control_read_base; + input [ADDRESSWIDTH-1:0] control_read_length; + input control_go; + output wire control_done; + // don't use this unless you know what you are doing, + // it's going to fire when the last read is posted, + // not when the last data returns! + output wire control_early_done; + + // user logic inputs and outputs + input user_read_buffer; + output wire [DATAWIDTH-1:0] user_buffer_data; + output wire user_data_available; + + // master inputs and outputs + input master_waitrequest; + input master_readdatavalid; + input [DATAWIDTH-1:0] master_readdata; + output wire [ADDRESSWIDTH-1:0] master_address; + output wire master_read; + output wire [BYTEENABLEWIDTH-1:0] master_byteenable; + output wire [BURSTCOUNTWIDTH-1:0] master_burstcount; + + // internal control signals + reg control_fixed_location_d1; + wire fifo_empty; + reg [ADDRESSWIDTH-1:0] address; + reg [ADDRESSWIDTH-1:0] length; + reg [FIFODEPTH_LOG2-1:0] reads_pending; wire increment_address; wire [BURSTCOUNTWIDTH-1:0] burst_count; wire [BURSTCOUNTWIDTH-1:0] first_short_burst_count; @@ -167,7 +252,9 @@ module burst_read_master ( end else if((increment_address == 1) & (control_fixed_location_d1 == 0)) begin - address <= address + (burst_count * BYTEENABLEWIDTH); // always performing word size accesses, increment by the burst count presented + // always performing word size accesses, + // increment by the burst count presented + address <= address + (burst_count * BYTEENABLEWIDTH); end end end @@ -189,7 +276,9 @@ module burst_read_master ( end else if(increment_address == 1) begin - length <= length - (burst_count * BYTEENABLEWIDTH); // always performing word size accesses, decrement by the burst count presented + // always performing word size accesses, + // decrement by the burst count presented + length <= length - (burst_count * BYTEENABLEWIDTH); end end end @@ -198,71 +287,81 @@ module burst_read_master ( // controlled signals going to the master/control ports assign master_address = address; - assign master_byteenable = -1; // all ones, always performing word size accesses + // all ones, always performing word size accesses + assign master_byteenable = -1; assign master_burstcount = burst_count; - assign control_done = (length == 0) & (reads_pending == 0); // need to make sure that the reads have returned before firing the done bit - assign control_early_done = (length == 0); // advanced feature, you should use 'control_done' if you need all the reads to return first - assign master_read = (too_many_reads_pending == 0) & (length != 0); - assign burst_boundary_word_address = ((address / BYTEENABLEWIDTH) & (MAXBURSTCOUNT - 1)); - assign first_short_burst_enable = (burst_boundary_word_address != 0); - assign final_short_burst_enable = (length < (MAXBURSTCOUNT * BYTEENABLEWIDTH)); - - assign first_short_burst_count = ((burst_boundary_word_address & 1'b1) == 1'b1)? 1 : // if the burst boundary isn't a multiple of 2 then must post a burst of 1 to get to a multiple of 2 for the next burst - (((MAXBURSTCOUNT - burst_boundary_word_address) < (length / BYTEENABLEWIDTH))? - (MAXBURSTCOUNT - burst_boundary_word_address) : (length / BYTEENABLEWIDTH)); - assign final_short_burst_count = (length / BYTEENABLEWIDTH); - - assign burst_count = (first_short_burst_enable == 1)? first_short_burst_count : // this will get the transfer back on a burst boundary, - (final_short_burst_enable == 1)? final_short_burst_count : MAXBURSTCOUNT; -assign increment_address = (too_many_reads_pending == 0) & (master_waitrequest == 0) & (length != 0); -assign too_many_reads_pending = (reads_pending + fifo_used) >= (FIFODEPTH - MAXBURSTCOUNT - 4); // make sure there are fewer reads posted than room in the FIFO - - -// tracking FIFO -always @ (posedge clk or posedge reset) -begin - if (reset == 1) - begin - reads_pending <= 0; - end - else - begin - if(increment_address == 1) + // need to make sure that the reads have returned before firing the done bit + assign control_done = (length == 0) & (reads_pending == 0); + // advanced feature, you should use 'control_done' if you need all + // the reads to return first + assign control_early_done = (length == 0); + assign master_read = (too_many_reads_pending == 0) & (length != 0); + assign burst_boundary_word_address = ((address / BYTEENABLEWIDTH) & (MAXBURSTCOUNT - 1)); + assign first_short_burst_enable = (burst_boundary_word_address != 0); + assign final_short_burst_enable = (length < (MAXBURSTCOUNT * BYTEENABLEWIDTH)); + + // if the burst boundary isn't a multiple of 2 then must post a burst of + // 1 to get to a multiple of 2 for the next burst + assign first_short_burst_count = ((burst_boundary_word_address & 1'b1) == 1'b1)? 1 : + (((MAXBURSTCOUNT - burst_boundary_word_address) < (length / BYTEENABLEWIDTH))? + (MAXBURSTCOUNT - burst_boundary_word_address) : (length / BYTEENABLEWIDTH)); + assign final_short_burst_count = (length / BYTEENABLEWIDTH); + + // this will get the transfer back on a burst boundary, + assign burst_count = (first_short_burst_enable == 1)? first_short_burst_count : + (final_short_burst_enable == 1)? final_short_burst_count : MAXBURSTCOUNT; + assign increment_address = + (too_many_reads_pending == 0) & (master_waitrequest == 0) & (length != 0); + // make sure there are fewer reads posted than room in the FIFO + assign too_many_reads_pending = (reads_pending + fifo_used) >= (FIFODEPTH - MAXBURSTCOUNT - 4); + + + // tracking FIFO + always @ (posedge clk or posedge reset) begin - if(master_readdatavalid == 0) + if (reset == 1) begin - reads_pending <= reads_pending + burst_count; + reads_pending <= 0; end else begin - reads_pending <= reads_pending + burst_count - 1; // a burst read was posted, but a word returned + if(increment_address == 1) + begin + if(master_readdatavalid == 0) + begin + reads_pending <= reads_pending + burst_count; + end + else + begin + // a burst read was posted, but a word returned + reads_pending <= reads_pending + burst_count - 1; + end + end + else + begin + if(master_readdatavalid == 0) + begin + reads_pending <= reads_pending; // burst read was not posted and no read returned + end + else + begin + reads_pending <= reads_pending - 1; // burst read was not posted but a word returned + end + end end end - else - begin - if(master_readdatavalid == 0) - begin - reads_pending <= reads_pending; // burst read was not posted and no read returned - end - else - begin - reads_pending <= reads_pending - 1; // burst read was not posted but a word returned - end - end - end -end -// read data feeding user logic -assign user_data_available = !fifo_empty; -scfifo the_master_to_user_fifo ( - .aclr (reset), - .clock (clk), - .data (master_readdata), - .empty (fifo_empty), - .q (user_buffer_data), - .rdreq (user_read_buffer), - .usedw (fifo_used), + // read data feeding user logic + assign user_data_available = !fifo_empty; + scfifo the_master_to_user_fifo ( + .aclr (reset), + .clock (clk), + .data (master_readdata), + .empty (fifo_empty), + .q (user_buffer_data), + .rdreq (user_read_buffer), + .usedw (fifo_used), .wrreq (master_readdatavalid) ); defparam the_master_to_user_fifo.lpm_width = DATAWIDTH; From f25e66dc681e117793b57f0977c49f02148df94d Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Sun, 20 Sep 2015 18:42:15 +0200 Subject: [PATCH 1002/2656] adding waveform traces, fix some indentations --- .../designs/avalon_module/burst_read_master.v | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/tests/designs/avalon_module/burst_read_master.v b/tests/designs/avalon_module/burst_read_master.v index 5eba4fd8..ab053607 100644 --- a/tests/designs/avalon_module/burst_read_master.v +++ b/tests/designs/avalon_module/burst_read_master.v @@ -30,6 +30,8 @@ */ +`timescale 1 ps / 1 ps + // altera message_off 10230 `define FIFODEPTH_LOG2_DEF 5 @@ -141,26 +143,25 @@ module burst_read_master ( // control inputs and outputs control_fixed_location, - control_read_base, - control_read_length, - control_go, - control_done, - control_early_done, - - // user logic inputs and outputs - user_read_buffer, - user_buffer_data, - user_data_available, - - // master inputs and outputs - master_address, - master_read, - master_byteenable, - master_readdata, - master_readdatavalid, - master_burstcount, - master_waitrequest - ); + control_read_base, + control_read_length, + control_go, + control_done, + control_early_done, + + // user logic inputs and outputs + user_read_buffer, + user_buffer_data, + user_data_available, + + // master inputs and outputs + master_address, + master_read, + master_byteenable, + master_readdata, + master_readdatavalid, + master_burstcount, + master_waitrequest); parameter DATAWIDTH = 32; parameter MAXBURSTCOUNT = 4; @@ -372,4 +373,10 @@ module burst_read_master ( defparam the_master_to_user_fifo.underflow_checking = "OFF"; defparam the_master_to_user_fifo.overflow_checking = "OFF"; +initial begin + $dumpfile("waveform.vcd"); + $dumpvars(0, burst_read_master); +end + + endmodule From dc237628ec20f082379ba5204c3e5569c7e89514 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Sun, 20 Sep 2015 18:42:30 +0200 Subject: [PATCH 1003/2656] adding burst tests --- tests/test_cases/test_avalon/test_avalon.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/test_cases/test_avalon/test_avalon.py b/tests/test_cases/test_avalon/test_avalon.py index ab5d072f..1978d374 100644 --- a/tests/test_cases/test_avalon/test_avalon.py +++ b/tests/test_cases/test_avalon/test_avalon.py @@ -35,13 +35,12 @@ """ import cocotb +from cocotb.drivers.avalon import AvalonMemory from cocotb.triggers import (Timer, Join, RisingEdge, FallingEdge, Edge, ReadOnly, ReadWrite) from cocotb.clock import Clock from cocotb.result import ReturnValue, TestFailure, TestError, TestSuccess -# Tests relating to providing meaningful errors if we forget to use the -# yield keyword correctly to turn a function into a coroutine @cocotb.test(expect_fail=False) def test_function_reentrant_clock(dut): @@ -55,3 +54,13 @@ def test_function_reentrant_clock(dut): yield timer +@cocotb.test(expect_fail=False) +def test_burst_read(dut): + """ Testing burst read """ + # Launch clock + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + + memdict = {} + avl32 = AvalonMemory(dut, "master", dut.clk) + yield Timer(100) + From 40dade6ad784cf9dd72ef8ff5c7b5d43d0818bbb Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Sun, 20 Sep 2015 20:38:58 +0200 Subject: [PATCH 1004/2656] gtkwave configuration + indent --- .../designs/avalon_module/burst_read_master.v | 87 +++++++++---------- .../test_avalon/test_avalon_cfg.gtkw | 45 ++++++++++ 2 files changed, 88 insertions(+), 44 deletions(-) create mode 100644 tests/test_cases/test_avalon/test_avalon_cfg.gtkw diff --git a/tests/designs/avalon_module/burst_read_master.v b/tests/designs/avalon_module/burst_read_master.v index ab053607..281da2a8 100644 --- a/tests/designs/avalon_module/burst_read_master.v +++ b/tests/designs/avalon_module/burst_read_master.v @@ -163,50 +163,49 @@ module burst_read_master ( master_burstcount, master_waitrequest); - parameter DATAWIDTH = 32; - parameter MAXBURSTCOUNT = 4; - parameter BURSTCOUNTWIDTH = 3; - parameter BYTEENABLEWIDTH = 4; - parameter ADDRESSWIDTH = 32; - parameter FIFODEPTH = 32; - parameter FIFODEPTH_LOG2 = `FIFODEPTH_LOG2_DEF; - parameter FIFOUSEMEMORY = 1; // set to 0 to use LEs instead - - input clk; - input reset; - - - // control inputs and outputs - input control_fixed_location; - input [ADDRESSWIDTH-1:0] control_read_base; - input [ADDRESSWIDTH-1:0] control_read_length; - input control_go; - output wire control_done; - // don't use this unless you know what you are doing, - // it's going to fire when the last read is posted, - // not when the last data returns! - output wire control_early_done; - - // user logic inputs and outputs - input user_read_buffer; - output wire [DATAWIDTH-1:0] user_buffer_data; - output wire user_data_available; - - // master inputs and outputs - input master_waitrequest; - input master_readdatavalid; - input [DATAWIDTH-1:0] master_readdata; - output wire [ADDRESSWIDTH-1:0] master_address; - output wire master_read; - output wire [BYTEENABLEWIDTH-1:0] master_byteenable; - output wire [BURSTCOUNTWIDTH-1:0] master_burstcount; - - // internal control signals - reg control_fixed_location_d1; - wire fifo_empty; - reg [ADDRESSWIDTH-1:0] address; - reg [ADDRESSWIDTH-1:0] length; - reg [FIFODEPTH_LOG2-1:0] reads_pending; + parameter DATAWIDTH = 32; + parameter MAXBURSTCOUNT = 4; + parameter BURSTCOUNTWIDTH = 3; + parameter BYTEENABLEWIDTH = 4; + parameter ADDRESSWIDTH = 32; + parameter FIFODEPTH = 32; + parameter FIFODEPTH_LOG2 = `FIFODEPTH_LOG2_DEF; + parameter FIFOUSEMEMORY = 1; // set to 0 to use LEs instead + + input clk; + input reset; + + // control inputs and outputs + input control_fixed_location; + input [ADDRESSWIDTH-1:0] control_read_base; + input [ADDRESSWIDTH-1:0] control_read_length; + input control_go; + output wire control_done; + // don't use this unless you know what you are doing, + // it's going to fire when the last read is posted, + // not when the last data returns! + output wire control_early_done; + + // user logic inputs and outputs + input user_read_buffer; + output wire [DATAWIDTH-1:0] user_buffer_data; + output wire user_data_available; + + // master inputs and outputs + input master_waitrequest; + input master_readdatavalid; + input [DATAWIDTH-1:0] master_readdata; + output wire [ADDRESSWIDTH-1:0] master_address; + output wire master_read; + output wire [BYTEENABLEWIDTH-1:0] master_byteenable; + output wire [BURSTCOUNTWIDTH-1:0] master_burstcount; + + // internal control signals + reg control_fixed_location_d1; + wire fifo_empty; + reg [ADDRESSWIDTH-1:0] address; + reg [ADDRESSWIDTH-1:0] length; + reg [FIFODEPTH_LOG2-1:0] reads_pending; wire increment_address; wire [BURSTCOUNTWIDTH-1:0] burst_count; wire [BURSTCOUNTWIDTH-1:0] first_short_burst_count; diff --git a/tests/test_cases/test_avalon/test_avalon_cfg.gtkw b/tests/test_cases/test_avalon/test_avalon_cfg.gtkw new file mode 100644 index 00000000..ad92a54a --- /dev/null +++ b/tests/test_cases/test_avalon/test_avalon_cfg.gtkw @@ -0,0 +1,45 @@ +[*] +[*] GTKWave Analyzer v3.3.62 (w)1999-2014 BSI +[*] Sun Sep 20 18:17:02 2015 +[*] +[dumpfile] "/opt/cocotb/tests/test_cases/test_avalon/waveform.vcd" +[dumpfile_mtime] "Sun Sep 20 18:16:16 2015" +[dumpfile_size] 4340 +[savefile] "/opt/cocotb/tests/test_cases/test_avalon/test_avalon_cfg.gtkw" +[timestart] 0 +[size] 1366 702 +[pos] -1 -1 +*-9.602564 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[sst_width] 289 +[signals_width] 214 +[sst_expanded] 1 +[sst_vpaned_height] 179 +@28 +burst_read_master.clk +@22 +burst_read_master.address[31:0] +@28 +burst_read_master.burst_count[2:0] +@200 +-drive +@28 +burst_read_master.control_go +@22 +burst_read_master.control_read_base[31:0] +@200 +-AVALON +@22 +burst_read_master.master_address[31:0] +@28 +burst_read_master.master_burstcount[2:0] +@22 +burst_read_master.master_byteenable[3:0] +@29 +burst_read_master.master_read +@22 +burst_read_master.master_readdata[31:0] +@28 +burst_read_master.master_readdatavalid +burst_read_master.master_waitrequest +[pattern_trace] 1 +[pattern_trace] 0 From 05638e648cfb02bb27be3f0100d981ff0c9bdb80 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Sun, 20 Sep 2015 20:41:06 +0200 Subject: [PATCH 1005/2656] improve burst test --- tests/test_cases/test_avalon/test_avalon.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/test_cases/test_avalon/test_avalon.py b/tests/test_cases/test_avalon/test_avalon.py index 1978d374..5b1f2390 100644 --- a/tests/test_cases/test_avalon/test_avalon.py +++ b/tests/test_cases/test_avalon/test_avalon.py @@ -58,9 +58,19 @@ def test_function_reentrant_clock(dut): def test_burst_read(dut): """ Testing burst read """ # Launch clock - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + dut.reset = 1 + clk_gen = cocotb.fork(Clock(dut.clk, 10).start()) memdict = {} avl32 = AvalonMemory(dut, "master", dut.clk) + yield Timer(10) + dut.reset = 0 + dut.control_read_base = 0 + dut.control_read_length = 32 + dut.control_fixed_location = 0 + dut.control_go = 0 + dut.master_waitrequest = 0 yield Timer(100) + dut.control_go = 1 + yield Timer(1000) From 073744b496d40e3274c9ff09a9443139b6dddcf9 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Mon, 21 Sep 2015 12:40:23 +0200 Subject: [PATCH 1006/2656] test burst with address 0 --- cocotb/drivers/avalon.py | 125 +++++++++++++++----- tests/test_cases/test_avalon/test_avalon.py | 94 +++++++++++++-- 2 files changed, 178 insertions(+), 41 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 7ee0bcee..15539446 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -212,17 +212,35 @@ def __init__(self, entity, name, clock, readlatency_min=1, self._avalon_properties[key] = avl_properties.get(key, value) self._avalon_properties = avl_properties + + if self._avalon_properties["burstCountUnits"] != "symbols": + self.log.error("Only symbols burstCountUnits is supported") + if self._avalon_properties["addressUnits"] != "symbols": + self.log.error("Only symbols addressUnits is supported") + + self._burstread = False + self._burstwrite = False self._readable = False self._writeable = False + self._width = None if hasattr(self.bus, "readdata"): self._width = len(self.bus.readdata) self._readable = True if hasattr(self.bus, "writedata"): - self._width = len(self.bus.writedata) + width = len(self.bus.writedata) + if (self._width is not None) and self._width != width: + self.log.error("readdata and writedata bus" + + " are not the same size") + self._width = width self._writeable = True + if hasattr(self.bus, "burstcount"): + if hasattr(self.bus, "readdatavalid"): + self._burstread = True + self._burstwrite = True + if not self._readable and not self._writeable: raise TestError("Attempt to instantiate useless memory") @@ -244,12 +262,34 @@ def __init__(self, entity, name, clock, readlatency_min=1, if hasattr(self.bus, "waitrequest"): self.bus.waitrequest.setimmediatevalue(0) + if hasattr(self.bus, "readdatavalid"): + self.bus.readdatavalid.setimmediatevalue(0) + def _pad(self): """Pad response queue up to read latency""" l = random.randint(self._readlatency_min, self._readlatency_max) while len(self._responses) < l: self._responses.append(None) + def _do_response(self): + if self._responses: + resp = self._responses.pop(0) + else: + resp = None + + if resp is not None: + if resp is True: + self._val.binstr = "x" * self._width + else: + self._val.integer = resp + self.log.debug("sending 0x%x (%s)" % + (self._val.integer, self._val.binstr)) + self.bus.readdata <= self._val + if hasattr(self.bus, "readdatavalid"): + self.bus.readdatavalid <= 1 + elif hasattr(self.bus, "readdatavalid"): + self.bus.readdatavalid <= 0 + @coroutine def _respond(self): """ @@ -258,38 +298,65 @@ def _respond(self): edge = RisingEdge(self.clock) while True: yield edge - - if self._responses: - resp = self._responses.pop(0) - else: - resp = None - - if resp is not None: - if resp is True: - self._val.binstr = "x" * self._width - else: - self._val.integer = resp - self.log.debug("sending 0x%x (%s)" % - (self._val.integer, self._val.binstr)) - self.bus.readdata <= self._val - if hasattr(self.bus, "readdatavalid"): - self.bus.readdatavalid <= 1 - elif hasattr(self.bus, "readdatavalid"): - self.bus.readdatavalid <= 0 + self._do_response() yield ReadOnly() if self._readable and self.bus.read.value: - self._pad() - addr = self.bus.address.value.integer - if addr not in self._mem: - self.log.warning("Attempt to read from uninitialised " - "address 0x%x" % addr) - self._responses.append(True) + if not self._burstread: + self._pad() + addr = self.bus.address.value.integer + if addr not in self._mem: + self.log.warning("Attempt to read from uninitialised " + "address 0x%x" % addr) + self._responses.append(True) + else: + self.log.debug("Read from address 0x%x returning 0x%x" % + (addr, self._mem[addr])) + self._responses.append(self._mem[addr]) else: - self.log.debug("Read from address 0x%x returning 0x%x" % - (addr, self._mem[addr])) - self._responses.append(self._mem[addr]) + addr = self.bus.address.value.integer + if addr % (self._width/8) != 0: + self.log.error("Address must be aligned to data width") + addr = addr / (self._width/8) + burstcount = self.bus.burstcount.value.integer + byteenable = self.bus.byteenable.value + if byteenable != int("1"*len(self.bus.byteenable), 2): + self.log.error("Only full word access is supported " + + "for burst read (byteenable must be " + + "0b" + "1" * len(self.bus.byteenable) + + ")") + if burstcount == 0: + self.log.error("Burstcount must be 1 at least") + + # toggle waitrequest + # TODO: configure waitrequest time with avalon properties + self.bus.waitrequest <= 1 + yield edge + yield edge + self.bus.waitrequest <= 0 + + # wait for read data + # TODO: configure readdatawait with avalon properties + yield edge + yield edge + + for count in range(burstcount): + self.log.warning("read value " + str(count)) + if (addr + count) not in self._mem: + self.log.warning("Attempt to read from uninitialised " + "address 0x%x" % (addr + count) ) + self._responses.append(True) + else: + value = 0 + for i in range(self._width/8): + value +=\ + self._mem[(addr + count)*(self._width/8) + i] << i*8 + self.log.debug("Read from address 0x%x returning 0x%x" % + (addr*(self._width/8), value)) + self._responses.append(value) + yield edge + self._do_response() if self._writeable and self.bus.write.value: addr = self.bus.address.value.integer @@ -332,7 +399,7 @@ def __init__(self, *args, **kwargs): self.bus.empty <= word self.bus.startofpacket <= word self.bus.endofpacket <= word - + @coroutine def _wait_ready(self): """Wait for a ready cycle on the bus before continuing diff --git a/tests/test_cases/test_avalon/test_avalon.py b/tests/test_cases/test_avalon/test_avalon.py index 5b1f2390..03fa2e21 100644 --- a/tests/test_cases/test_avalon/test_avalon.py +++ b/tests/test_cases/test_avalon/test_avalon.py @@ -41,6 +41,49 @@ from cocotb.clock import Clock from cocotb.result import ReturnValue, TestFailure, TestError, TestSuccess +class BurstAvlReadTest(object): + """ class to test avalon burst """ + + def __init__(self, dut): + self.dut = dut + # Launch clock + dut.reset = 1 + clk_gen = cocotb.fork(Clock(dut.clk, 10).start()) + + # Bytes aligned memory + self.memdict = { + 0x00: 0x00, 0x10: 0x10, 0x20: 0x20, 0x30: 0x30, + 0x01: 0x01, 0x11: 0x11, 0x21: 0x21, 0x31: 0x31, + 0x02: 0x02, 0x12: 0x12, 0x22: 0x22, 0x32: 0x32, + 0x03: 0x03, 0x13: 0x13, 0x23: 0x23, 0x33: 0x33, + 0x04: 0x04, 0x14: 0x14, 0x24: 0x24, 0x34: 0x34, + 0x05: 0x05, 0x15: 0x15, 0x25: 0x25, 0x35: 0x35, + 0x06: 0x06, 0x16: 0x16, 0x26: 0x26, 0x36: 0x36, + 0x07: 0x07, 0x17: 0x17, 0x27: 0x27, 0x37: 0x37, + 0x08: 0x08, 0x18: 0x18, 0x28: 0x28, 0x38: 0x38, + 0x09: 0x09, 0x19: 0x19, 0x29: 0x29, 0x39: 0x39, + 0x0a: 0x0a, 0x1a: 0x1a, 0x2a: 0x2a, 0x3a: 0x3a, + 0x0b: 0x0b, 0x1b: 0x1b, 0x2b: 0x2b, 0x3b: 0x3b, + 0x0c: 0x0c, 0x1c: 0x1c, 0x2c: 0x2c, 0x3c: 0x3c, + 0x0d: 0x0d, 0x1d: 0x1d, 0x2d: 0x2d, 0x3d: 0x3d, + 0x0e: 0x0e, 0x1e: 0x1e, 0x2e: 0x2e, 0x3e: 0x3e, + 0x0f: 0x0f, 0x1f: 0x1f, 0x2f: 0x2f, 0x3f: 0x3f, + } + + self.avl32 = AvalonMemory(dut, "master", dut.clk, + memory=self.memdict, readlatency_min=0) + @cocotb.coroutine + def init_sig(self, burstcount_w, address): + """ initialize all signals""" + yield Timer(10) + self.dut.reset = 0 + self.dut.user_read_buffer = 0 + self.dut.control_read_base = address + self.dut.control_read_length = burstcount_w*8 + self.dut.control_fixed_location = 0 + self.dut.control_go = 0 + self.dut.master_waitrequest = 0 + @cocotb.test(expect_fail=False) def test_function_reentrant_clock(dut): @@ -57,20 +100,47 @@ def test_function_reentrant_clock(dut): @cocotb.test(expect_fail=False) def test_burst_read(dut): """ Testing burst read """ - # Launch clock - dut.reset = 1 - clk_gen = cocotb.fork(Clock(dut.clk, 10).start()) + wordburstcount = 4 + address = 4 - memdict = {} - avl32 = AvalonMemory(dut, "master", dut.clk) - yield Timer(10) - dut.reset = 0 - dut.control_read_base = 0 - dut.control_read_length = 32 - dut.control_fixed_location = 0 - dut.control_go = 0 - dut.master_waitrequest = 0 + bart = BurstAvlReadTest(dut) + yield bart.init_sig(wordburstcount, address) yield Timer(100) + # Begin master burst read dut.control_go = 1 + yield Timer(10) + dut.control_go = 0 + yield Timer(200) + + # read back values + dut.user_read_buffer = 1 + yield RisingEdge(dut.clk) + read_mem = {} + databuswidthB = len(dut.master_byteenable) + for burst in range(wordburstcount): + yield RisingEdge(dut.clk) + value = dut.user_buffer_data.value + for i in range(databuswidthB): + read_mem[(address + burst)*databuswidthB + i] =\ + (value >> i*8)& 0xFF + + print str(read_mem) + + # checking values read + for key, value in read_mem.items(): + memdictvalue = bart.memdict.get(key, None) + if memdictvalue != value: + if memdictvalue is None: + memdictvalue = "Error" + else: + memdictvalue = hex(memdictvalue) + raise TestFailure("Wrong value read in memory :" + + " read_mem[" + hex(key) + "] = " + + hex(value) + " must be " + + memdictvalue) + + + yield Timer(1000) + dut.user_read_buffer = 0 yield Timer(1000) From 7c59fb8a9890971ae64b678969b15fbb6e9b46ba Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 21 Sep 2015 14:23:50 +0100 Subject: [PATCH 1007/2656] Cleanup: Fix typo in test --- tests/test_cases/test_iteration_verilog/test_iteration_es.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/test_iteration_verilog/test_iteration_es.py b/tests/test_cases/test_iteration_verilog/test_iteration_es.py index fdf9ab18..9aaab8fd 100644 --- a/tests/test_cases/test_iteration_verilog/test_iteration_es.py +++ b/tests/test_cases/test_iteration_verilog/test_iteration_es.py @@ -35,7 +35,7 @@ def recursive_discovery(dut): Recursively discover every single object in the design """ if cocotb.SIM_NAME in ["ModelSim ALTERA STARTER EDITION", - "ModelSim DE" + "ModelSim DE", "ncsim(64)", "ncsim"]: # vpiAlways does not show up in IUS From 4ab6550d2d1b9776ecbd0ddd6531a023bed354b3 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 21 Sep 2015 14:36:42 +0100 Subject: [PATCH 1008/2656] Issue #17: make a better effor of cleanup the directory --- makefiles/simulators/Makefile.ius | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 27eced58..416ce099 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -33,7 +33,6 @@ # hope to enable again when these have been resolved. ARCH?=i686 -#EXTRA_ARGS += -64bit ifeq ($(shell ncbits),64) $(error "The 64bit version of IUS is on your PATH, please use the 32bit version for now") endif @@ -74,4 +73,7 @@ results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEP irun $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) clean:: - -rm -rf $(SIM_BUILD) + @rm -rf $(SIM_BUILD) + @rm -rf irun.* + @rm -rf ncsim.* + @rm -rf gdb_cmd_ncsim From 58fa332dd11c23d4f6e6359a3ebc816223856db4 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 21 Sep 2015 14:37:19 +0100 Subject: [PATCH 1009/2656] Issue #17: Fix issue with creating of name for objects found via index --- lib/vpi/VpiImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 14232a9b..e90383ff 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -217,7 +217,7 @@ GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) return NULL; } - std::string name = vpi_get_str(vpiFullName, new_hdl); + std::string name = vpi_get_str(vpiName, new_hdl); std::string fq_name = parent->get_fullname() + "." + name; GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { From ccc657b43bee12aa6de987d782dd9a42df4b95a8 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 21 Sep 2015 14:38:30 +0100 Subject: [PATCH 1010/2656] Issue #17: Add the fullnames to debug output and fix incorrect number of arguments on one LOG_DEBUG entry --- lib/gpi/GpiCommon.cpp | 2 ++ lib/vpi/VpiCbHdl.cpp | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 187f7afd..726caaf8 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -47,6 +47,8 @@ class GpiHandleStore { const std::string &name = hdl->get_fullname(); + LOG_DEBUG("Checking %s exits", name.c_str()); + it = handle_map.find(name); if (it == handle_map.end()) { handle_map[name] = hdl; diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 910e9ca0..25b90295 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -495,7 +495,7 @@ VpiIterator::VpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(i return; } - LOG_DEBUG("Created iterator working from type %d", + LOG_DEBUG("Created iterator working from type %d %s", *one2many, vpi_get_str(vpiFullName, vpi_hdl)); @@ -591,13 +591,13 @@ int VpiIterator::next_handle(std::string &name, GpiObjHdl **hdl) } name = c_name; - LOG_DEBUG("vpi_scan found '%s'", name.c_str()); - /* We try and create a handle internally, if this is not possible we return and GPI will try other implementations with the name */ std::string fq_name = m_parent->get_fullname() + "." + name; + + LOG_DEBUG("vpi_scan found '%s'", fq_name.c_str()); VpiImpl *vpi_impl = reinterpret_cast(m_impl); new_obj = vpi_impl->create_gpi_obj_from_handle(obj, name, fq_name); if (new_obj) { From cb2d028c165991d3a742f6aa9d1dff993e45c093 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Mon, 21 Sep 2015 17:56:14 +0200 Subject: [PATCH 1011/2656] tested value for burst of size 16 --- tests/test_cases/test_avalon/test_avalon.py | 63 +++++++------------ .../test_avalon/test_avalon_cfg.gtkw | 45 +++++++++---- 2 files changed, 53 insertions(+), 55 deletions(-) diff --git a/tests/test_cases/test_avalon/test_avalon.py b/tests/test_cases/test_avalon/test_avalon.py index 03fa2e21..44b41f10 100644 --- a/tests/test_cases/test_avalon/test_avalon.py +++ b/tests/test_cases/test_avalon/test_avalon.py @@ -41,37 +41,24 @@ from cocotb.clock import Clock from cocotb.result import ReturnValue, TestFailure, TestError, TestSuccess + + class BurstAvlReadTest(object): """ class to test avalon burst """ - def __init__(self, dut): + def __init__(self, dut, avlproperties={}): self.dut = dut # Launch clock dut.reset = 1 clk_gen = cocotb.fork(Clock(dut.clk, 10).start()) # Bytes aligned memory - self.memdict = { - 0x00: 0x00, 0x10: 0x10, 0x20: 0x20, 0x30: 0x30, - 0x01: 0x01, 0x11: 0x11, 0x21: 0x21, 0x31: 0x31, - 0x02: 0x02, 0x12: 0x12, 0x22: 0x22, 0x32: 0x32, - 0x03: 0x03, 0x13: 0x13, 0x23: 0x23, 0x33: 0x33, - 0x04: 0x04, 0x14: 0x14, 0x24: 0x24, 0x34: 0x34, - 0x05: 0x05, 0x15: 0x15, 0x25: 0x25, 0x35: 0x35, - 0x06: 0x06, 0x16: 0x16, 0x26: 0x26, 0x36: 0x36, - 0x07: 0x07, 0x17: 0x17, 0x27: 0x27, 0x37: 0x37, - 0x08: 0x08, 0x18: 0x18, 0x28: 0x28, 0x38: 0x38, - 0x09: 0x09, 0x19: 0x19, 0x29: 0x29, 0x39: 0x39, - 0x0a: 0x0a, 0x1a: 0x1a, 0x2a: 0x2a, 0x3a: 0x3a, - 0x0b: 0x0b, 0x1b: 0x1b, 0x2b: 0x2b, 0x3b: 0x3b, - 0x0c: 0x0c, 0x1c: 0x1c, 0x2c: 0x2c, 0x3c: 0x3c, - 0x0d: 0x0d, 0x1d: 0x1d, 0x2d: 0x2d, 0x3d: 0x3d, - 0x0e: 0x0e, 0x1e: 0x1e, 0x2e: 0x2e, 0x3e: 0x3e, - 0x0f: 0x0f, 0x1f: 0x1f, 0x2f: 0x2f, 0x3f: 0x3f, - } + self.memdict = {value: value for value in range(0x1000)} self.avl32 = AvalonMemory(dut, "master", dut.clk, - memory=self.memdict, readlatency_min=0) + memory=self.memdict, + readlatency_min=0, + avl_properties=avlproperties) @cocotb.coroutine def init_sig(self, burstcount_w, address): """ initialize all signals""" @@ -79,31 +66,18 @@ def init_sig(self, burstcount_w, address): self.dut.reset = 0 self.dut.user_read_buffer = 0 self.dut.control_read_base = address - self.dut.control_read_length = burstcount_w*8 + self.dut.control_read_length = burstcount_w*4 self.dut.control_fixed_location = 0 self.dut.control_go = 0 self.dut.master_waitrequest = 0 - -@cocotb.test(expect_fail=False) -def test_function_reentrant_clock(dut): - """Test yielding a reentrant clock""" - clock = dut.clk - timer = Timer(100) - for i in range(10): - clock <= 0 - yield timer - clock <= 1 - yield timer - - @cocotb.test(expect_fail=False) def test_burst_read(dut): """ Testing burst read """ - wordburstcount = 4 - address = 4 + wordburstcount = 16 + address = 10*wordburstcount - bart = BurstAvlReadTest(dut) + bart = BurstAvlReadTest(dut, {"readLatency": 10}) yield bart.init_sig(wordburstcount, address) yield Timer(100) # Begin master burst read @@ -117,14 +91,20 @@ def test_burst_read(dut): yield RisingEdge(dut.clk) read_mem = {} databuswidthB = len(dut.master_byteenable) - for burst in range(wordburstcount): + burst = 0 + while dut.user_data_available == 1: yield RisingEdge(dut.clk) value = dut.user_buffer_data.value for i in range(databuswidthB): - read_mem[(address + burst)*databuswidthB + i] =\ + read_mem[address + burst*databuswidthB + i] =\ (value >> i*8)& 0xFF + burst += 1 + + dut.user_read_buffer = 0 + yield RisingEdge(dut.clk) print str(read_mem) + print str(len(read_mem)) + " 8bits values read" # checking values read for key, value in read_mem.items(): @@ -138,9 +118,8 @@ def test_burst_read(dut): " read_mem[" + hex(key) + "] = " + hex(value) + " must be " + memdictvalue) - - yield Timer(1000) + yield Timer(10) dut.user_read_buffer = 0 - yield Timer(1000) + yield Timer(10) diff --git a/tests/test_cases/test_avalon/test_avalon_cfg.gtkw b/tests/test_cases/test_avalon/test_avalon_cfg.gtkw index ad92a54a..48ab53aa 100644 --- a/tests/test_cases/test_avalon/test_avalon_cfg.gtkw +++ b/tests/test_cases/test_avalon/test_avalon_cfg.gtkw @@ -1,25 +1,24 @@ [*] [*] GTKWave Analyzer v3.3.62 (w)1999-2014 BSI -[*] Sun Sep 20 18:17:02 2015 +[*] Mon Sep 21 15:45:06 2015 [*] -[dumpfile] "/opt/cocotb/tests/test_cases/test_avalon/waveform.vcd" -[dumpfile_mtime] "Sun Sep 20 18:16:16 2015" -[dumpfile_size] 4340 -[savefile] "/opt/cocotb/tests/test_cases/test_avalon/test_avalon_cfg.gtkw" +[dumpfile] "/usr/local/opt/cocotb/tests/test_cases/test_avalon/waveform.vcd" +[dumpfile_mtime] "Mon Sep 21 15:44:32 2015" +[dumpfile_size] 8364 +[savefile] "/usr/local/opt/cocotb/tests/test_cases/test_avalon/test_avalon_cfg.gtkw" [timestart] 0 -[size] 1366 702 +[size] 1920 980 [pos] -1 -1 -*-9.602564 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +*-6.689228 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[treeopen] burst_read_master. [sst_width] 289 -[signals_width] 214 +[signals_width] 322 [sst_expanded] 1 [sst_vpaned_height] 179 @28 burst_read_master.clk @22 burst_read_master.address[31:0] -@28 -burst_read_master.burst_count[2:0] @200 -drive @28 @@ -28,18 +27,38 @@ burst_read_master.control_go burst_read_master.control_read_base[31:0] @200 -AVALON +@28 +burst_read_master.control_fixed_location_d1 +burst_read_master.control_fixed_location +burst_read_master.increment_address +burst_read_master.control_early_done +burst_read_master.control_done @22 -burst_read_master.master_address[31:0] +burst_read_master.control_read_length[31:0] +@23 +burst_read_master.length[31:0] @28 -burst_read_master.master_burstcount[2:0] +burst_read_master.first_short_burst_enable @22 +burst_read_master.master_address[31:0] burst_read_master.master_byteenable[3:0] -@29 +@28 burst_read_master.master_read @22 burst_read_master.master_readdata[31:0] @28 burst_read_master.master_readdatavalid burst_read_master.master_waitrequest +@200 +-fifo +@22 +burst_read_master.the_master_to_user_fifo.usedw[4:0] +@28 +burst_read_master.the_master_to_user_fifo.rdreq +burst_read_master.the_master_to_user_fifo.empty +@22 +burst_read_master.the_master_to_user_fifo.rd_ptr[4:0] +burst_read_master.the_master_to_user_fifo.wr_ptr[4:0] +burst_read_master.user_buffer_data[31:0] [pattern_trace] 1 [pattern_trace] 0 From 7f9bcebbe80010b07d10445ee35352b297664d97 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Mon, 21 Sep 2015 17:56:30 +0200 Subject: [PATCH 1012/2656] burst of 16 and indentation --- .../designs/avalon_module/burst_read_master.v | 140 +++++++++--------- 1 file changed, 68 insertions(+), 72 deletions(-) diff --git a/tests/designs/avalon_module/burst_read_master.v b/tests/designs/avalon_module/burst_read_master.v index 281da2a8..c99ade92 100644 --- a/tests/designs/avalon_module/burst_read_master.v +++ b/tests/designs/avalon_module/burst_read_master.v @@ -28,6 +28,9 @@ The signal 'user_data_available' is asserted whenever data is available from the exposed FIFO. + Update by FabienM + - adding scfifo verilog model description + */ `timescale 1 ps / 1 ps @@ -54,8 +57,6 @@ localparam add_ram_output_register = "OFF"; localparam underflow_checking = "OFF"; localparam overflow_checking = "OFF"; -// BEGIN PASTE - assign q = buf_out; assign empty = buf_empty; assign usedw = fifo_counter; @@ -133,8 +134,6 @@ begin end end -// END PASTE - endmodule module burst_read_master ( @@ -142,9 +141,9 @@ module burst_read_master ( reset, // control inputs and outputs - control_fixed_location, - control_read_base, - control_read_length, + control_fixed_location, // When set the master address will not increment + control_read_base, // Word aligned byte address + control_read_length, // Number of bytes to transfer control_go, control_done, control_early_done, @@ -163,11 +162,14 @@ module burst_read_master ( master_burstcount, master_waitrequest); + + parameter MAXBURSTCOUNT = 16; // in word + parameter BURSTCOUNTWIDTH = 5; + parameter DATAWIDTH = 32; - parameter MAXBURSTCOUNT = 4; - parameter BURSTCOUNTWIDTH = 3; parameter BYTEENABLEWIDTH = 4; parameter ADDRESSWIDTH = 32; + parameter FIFODEPTH = 32; parameter FIFODEPTH_LOG2 = `FIFODEPTH_LOG2_DEF; parameter FIFOUSEMEMORY = 1; // set to 0 to use LEs instead @@ -217,8 +219,6 @@ module burst_read_master ( wire too_many_reads_pending; wire [FIFODEPTH_LOG2-1:0] fifo_used; - - // registering the control_fixed_location bit always @ (posedge clk or posedge reset) begin @@ -235,8 +235,6 @@ module burst_read_master ( end end - - // master address logic always @ (posedge clk or posedge reset) begin @@ -259,8 +257,6 @@ module burst_read_master ( end end - - // master length logic always @ (posedge clk or posedge reset) begin @@ -283,8 +279,6 @@ module burst_read_master ( end end - - // controlled signals going to the master/control ports assign master_address = address; // all ones, always performing word size accesses @@ -293,75 +287,77 @@ module burst_read_master ( // need to make sure that the reads have returned before firing the done bit assign control_done = (length == 0) & (reads_pending == 0); // advanced feature, you should use 'control_done' if you need all - // the reads to return first - assign control_early_done = (length == 0); - assign master_read = (too_many_reads_pending == 0) & (length != 0); - assign burst_boundary_word_address = ((address / BYTEENABLEWIDTH) & (MAXBURSTCOUNT - 1)); - assign first_short_burst_enable = (burst_boundary_word_address != 0); - assign final_short_burst_enable = (length < (MAXBURSTCOUNT * BYTEENABLEWIDTH)); - - // if the burst boundary isn't a multiple of 2 then must post a burst of - // 1 to get to a multiple of 2 for the next burst - assign first_short_burst_count = ((burst_boundary_word_address & 1'b1) == 1'b1)? 1 : - (((MAXBURSTCOUNT - burst_boundary_word_address) < (length / BYTEENABLEWIDTH))? - (MAXBURSTCOUNT - burst_boundary_word_address) : (length / BYTEENABLEWIDTH)); - assign final_short_burst_count = (length / BYTEENABLEWIDTH); - - // this will get the transfer back on a burst boundary, - assign burst_count = (first_short_burst_enable == 1)? first_short_burst_count : - (final_short_burst_enable == 1)? final_short_burst_count : MAXBURSTCOUNT; - assign increment_address = - (too_many_reads_pending == 0) & (master_waitrequest == 0) & (length != 0); - // make sure there are fewer reads posted than room in the FIFO - assign too_many_reads_pending = (reads_pending + fifo_used) >= (FIFODEPTH - MAXBURSTCOUNT - 4); - - - // tracking FIFO - always @ (posedge clk or posedge reset) + // the reads to return first + assign control_early_done = (length == 0); + + assign master_read = (too_many_reads_pending == 0) & (length != 0); + assign burst_boundary_word_address = ((address / BYTEENABLEWIDTH) & (MAXBURSTCOUNT - 1)); + assign first_short_burst_enable = (burst_boundary_word_address != 0); + assign final_short_burst_enable = (length < (MAXBURSTCOUNT * BYTEENABLEWIDTH)); + + // if the burst boundary isn't a multiple of 2 then must post a burst of + // 1 to get to a multiple of 2 for the next burst + assign first_short_burst_count = ((burst_boundary_word_address & 1'b1) == 1'b1)? 1 : + (((MAXBURSTCOUNT - burst_boundary_word_address) < (length / BYTEENABLEWIDTH))? + (MAXBURSTCOUNT - burst_boundary_word_address) : (length / BYTEENABLEWIDTH)); + assign final_short_burst_count = (length / BYTEENABLEWIDTH); + + // this will get the transfer back on a burst boundary, + assign burst_count = (first_short_burst_enable == 1)? first_short_burst_count : + (final_short_burst_enable == 1)? final_short_burst_count : MAXBURSTCOUNT; + assign increment_address = + (too_many_reads_pending == 0) & (master_waitrequest == 0) & (length != 0); + // make sure there are fewer reads posted than room in the FIFO + assign too_many_reads_pending = (reads_pending + fifo_used) >= (FIFODEPTH - MAXBURSTCOUNT - 4); + + // tracking FIFO + always @ (posedge clk or posedge reset) + begin + if (reset == 1) + begin + reads_pending <= 0; + end + else begin - if (reset == 1) + if(increment_address == 1) begin - reads_pending <= 0; + if(master_readdatavalid == 0) + begin + reads_pending <= reads_pending + burst_count; + end + else + begin + // a burst read was posted, but a word returned + reads_pending <= reads_pending + burst_count - 1; + end end else begin - if(increment_address == 1) + if(master_readdatavalid == 0) begin - if(master_readdatavalid == 0) - begin - reads_pending <= reads_pending + burst_count; - end - else - begin - // a burst read was posted, but a word returned - reads_pending <= reads_pending + burst_count - 1; - end + // burst read was not posted and no read returned + reads_pending <= reads_pending; end else begin - if(master_readdatavalid == 0) - begin - reads_pending <= reads_pending; // burst read was not posted and no read returned - end - else - begin - reads_pending <= reads_pending - 1; // burst read was not posted but a word returned - end + // burst read was not posted but a word returned + reads_pending <= reads_pending - 1; end end end + end - // read data feeding user logic - assign user_data_available = !fifo_empty; - scfifo the_master_to_user_fifo ( - .aclr (reset), - .clock (clk), - .data (master_readdata), - .empty (fifo_empty), - .q (user_buffer_data), - .rdreq (user_read_buffer), - .usedw (fifo_used), + // read data feeding user logic + assign user_data_available = !fifo_empty; + scfifo the_master_to_user_fifo ( + .aclr (reset), + .clock (clk), + .data (master_readdata), + .empty (fifo_empty), + .q (user_buffer_data), + .rdreq (user_read_buffer), + .usedw (fifo_used), .wrreq (master_readdatavalid) ); defparam the_master_to_user_fifo.lpm_width = DATAWIDTH; From 42db0b43b84298270d9a814bf826d19fdc69a3e2 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Mon, 21 Sep 2015 17:57:10 +0200 Subject: [PATCH 1013/2656] adding readLatency property for burst --- cocotb/drivers/avalon.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 15539446..b5bb01d3 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -201,6 +201,7 @@ class AvalonMemory(BusDriver): _avalon_properties = { "burstCountUnits": "symbols", # symbols or words "addressUnits": "symbols", # symbols or words + "readLatency": 1, # number of cycles } def __init__(self, entity, name, clock, readlatency_min=1, @@ -211,10 +212,9 @@ def __init__(self, entity, name, clock, readlatency_min=1, for key, value in self._avalon_properties.items(): self._avalon_properties[key] = avl_properties.get(key, value) - self._avalon_properties = avl_properties - if self._avalon_properties["burstCountUnits"] != "symbols": self.log.error("Only symbols burstCountUnits is supported") + if self._avalon_properties["addressUnits"] != "symbols": self.log.error("Only symbols addressUnits is supported") @@ -337,12 +337,10 @@ def _respond(self): self.bus.waitrequest <= 0 # wait for read data - # TODO: configure readdatawait with avalon properties - yield edge - yield edge + for i in range(self._avalon_properties["readLatency"]): + yield edge for count in range(burstcount): - self.log.warning("read value " + str(count)) if (addr + count) not in self._mem: self.log.warning("Attempt to read from uninitialised " "address 0x%x" % (addr + count) ) From 0c44844e8e363292c150d3a506771c99682c3d67 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 21 Sep 2015 17:00:38 +0100 Subject: [PATCH 1014/2656] Issue #17: Fix type in debug --- lib/gpi/GpiCommon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 726caaf8..e58f9d13 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -47,7 +47,7 @@ class GpiHandleStore { const std::string &name = hdl->get_fullname(); - LOG_DEBUG("Checking %s exits", name.c_str()); + LOG_DEBUG("Checking %s exists", name.c_str()); it = handle_map.find(name); if (it == handle_map.end()) { From 7e5129c16e54f501d73c26c0fd521e197c1714f1 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Wed, 23 Sep 2015 14:43:40 +0200 Subject: [PATCH 1015/2656] Use known utf8 characters for doctest to avoid problems in latexpdf --- cocotb/binary.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 0ec47ffb..5ca8edd7 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -262,8 +262,10 @@ def set_value(self, integer): def get_buff(self): """Attribute self.buff represents the value as a binary string buffer - e.g. vector "0000000100011111".buff == "\x01\x1F" - TODO: Doctest this! + + >>> "0100000100101111".buff == "\x41\x2F" + True + """ bits = resolve(self._str) if len(bits) % 8: From 9afc1304bdbaf673814fb7da87e7eb629a39d73a Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Wed, 23 Sep 2015 14:44:15 +0200 Subject: [PATCH 1016/2656] Fix latexpdf generation error --- documentation/source/endian_swapper.rst | 4 ++-- documentation/source/hal_cosimulation.rst | 2 +- documentation/source/introduction.rst | 2 +- documentation/source/ping_tun_tap.rst | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/documentation/source/endian_swapper.rst b/documentation/source/endian_swapper.rst index a53668d5..f2d864c6 100644 --- a/documentation/source/endian_swapper.rst +++ b/documentation/source/endian_swapper.rst @@ -17,7 +17,7 @@ Design We have a relatively simplistic RTL block called the endian_swapper. The DUT has three interfaces, all conforming to the Avalon standard: -.. image:: diagrams/svg/endian_swapper_design.svg +.. image:: diagrams/svg/endian_swapper_design.* The DUT will swap the endianness of packets on the Avalon-ST bus if a configuration bit is set. For every packet arriving on the "stream_in" interface the entire packet will be endian swapped if the configuration bit is set, otherwise the entire packet will pass through unmodified. @@ -46,7 +46,7 @@ To begin with we create a class to encapsulate all the common code for the testb With the above code we have created a testbench with the following structure: -.. image:: diagrams/svg/endian_swapper_testbench.svg +.. image:: diagrams/svg/endian_swapper_testbench.* If we inspect this line-by-line: diff --git a/documentation/source/hal_cosimulation.rst b/documentation/source/hal_cosimulation.rst index 53e6f804..16660528 100644 --- a/documentation/source/hal_cosimulation.rst +++ b/documentation/source/hal_cosimulation.rst @@ -60,7 +60,7 @@ coroutine (by running the function in a separate thread). The to be called by a normal thread. The call sequence looks like this: -.. image:: diagrams/svg/hal_cosimulation.svg +.. image:: diagrams/svg/hal_cosimulation.* Implementation diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index 93c8d3d3..31889985 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -62,7 +62,7 @@ Overview A typical cocotb testbench requires no additional RTL code. The Design Under Test (DUT) is instantiated as the toplevel in the simulator without any wrapper code. Cocotb drives stimulus onto the inputs to the DUT (or further down the hierarchy) and monitors the outputs directly from Python. -.. image:: diagrams/svg/cocotb_overview.svg +.. image:: diagrams/svg/cocotb_overview.* A test is simply a Python function. At any given time either the simulator is advancing time or the Python code is executing. The **yield** keyword is used to indicate when to pass control of execution back to the simulator. A test can spawn multiple coroutines, allowing for independent flows of execution. diff --git a/documentation/source/ping_tun_tap.rst b/documentation/source/ping_tun_tap.rst index ffb721b8..0858de69 100644 --- a/documentation/source/ping_tun_tap.rst +++ b/documentation/source/ping_tun_tap.rst @@ -31,7 +31,7 @@ Linux has a `TUN/TAP`_ virtual network device which we can use for this purpose, allowing `ping`_ to run unmodified and unaware that it is communicating with our simulation rather than a remote network endpoint. -.. image:: diagrams/svg/ping_tun_tap.svg +.. image:: diagrams/svg/ping_tun_tap.* Implementation From 4d9fc56ad6391df8689550b12563cdbcdaf0af71 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Wed, 23 Sep 2015 14:51:22 +0200 Subject: [PATCH 1017/2656] improve error message with address and size --- cocotb/drivers/avalon.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index b5bb01d3..98bb853e 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -317,7 +317,9 @@ def _respond(self): else: addr = self.bus.address.value.integer if addr % (self._width/8) != 0: - self.log.error("Address must be aligned to data width") + self.log.error("Address must be aligned to data width" + + "(addr = " + hex(addr) + + ", width = " + str(self._width)) addr = addr / (self._width/8) burstcount = self.bus.burstcount.value.integer byteenable = self.bus.byteenable.value From 9e13806919282a5c2e629ba1add7a5df04d749d7 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 23 Sep 2015 14:04:55 +0100 Subject: [PATCH 1018/2656] Issue #17: Supress warnings from IUS --- makefiles/simulators/Makefile.ius | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 416ce099..1e4dc79d 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -37,7 +37,7 @@ ifeq ($(shell ncbits),64) $(error "The 64bit version of IUS is on your PATH, please use the 32bit version for now") endif -EXTRA_ARGS += -nclibdirname $(SIM_BUILD) +EXTRA_ARGS += -nclibdirname $(SIM_BUILD) -plinowarn ifeq ($(GUI),1) EXTRA_ARGS += -gui From 750be953ec0b986dec52333919fad0c46469d3eb Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 23 Sep 2015 14:07:28 +0100 Subject: [PATCH 1019/2656] Issue #17: Fix syntax error --- tests/designs/uart2bus/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/designs/uart2bus/Makefile b/tests/designs/uart2bus/Makefile index 7c327ad5..452cd3a1 100644 --- a/tests/designs/uart2bus/Makefile +++ b/tests/designs/uart2bus/Makefile @@ -34,7 +34,7 @@ ifeq ($(SIM),ius) endif else - $(error "Only verilog toplevel supported at the moment") +$(error "Only verilog toplevel supported at the moment") endif ifneq ($(GPI_EXTRA),) From 501fa7eacd176f3b923b8d12b94f3b3c6e23f8a2 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 24 Sep 2015 13:56:53 +0100 Subject: [PATCH 1020/2656] Cleanup: Tidy up from reviewing code --- lib/vpi/VpiCbHdl.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 25b90295..3e25af41 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -68,13 +68,11 @@ int VpiCbHdl::arm_callback(void) { } vpiHandle new_hdl = vpi_register_cb(&cb_data); - check_vpi_error(); - - int ret = 0; if (!new_hdl) { LOG_ERROR("VPI: Unable to register a callback handle for VPI type %s(%d)", - m_impl->reason_to_string(cb_data.reason), cb_data.reason); + m_impl->reason_to_string(cb_data.reason), cb_data.reason); + check_vpi_error(); return -1; } else { @@ -83,7 +81,7 @@ int VpiCbHdl::arm_callback(void) { m_obj_hdl = new_hdl; - return ret; + return 0; } int VpiCbHdl::cleanup_callback(void) @@ -164,6 +162,11 @@ long VpiSignalObjHdl::get_signal_value_long(void) int VpiSignalObjHdl::set_signal_value(long value) { FENTER + + LOG_WARN("Writing %ld to %s", + value, + m_name.c_str()); + s_vpi_value value_s; value_s.value.integer = value; From b6e3eef1c26640e461480f834bd01490af5f0482 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 24 Sep 2015 13:57:23 +0100 Subject: [PATCH 1021/2656] Issue #17: Set the number of elements to when writing a value --- lib/vhpi/VhpiCbHdl.cpp | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 45a1089c..411ce677 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -185,7 +185,7 @@ int VhpiCbHdl::arm_callback(void) if (vhpiDisable == cbState) { if (vhpi_enable_cb(get_handle())) { check_vhpi_error(); - ret = -1; + goto error; } } } else { @@ -196,13 +196,13 @@ int VhpiCbHdl::arm_callback(void) check_vhpi_error(); LOG_ERROR("VHPI: Unable to register callback a handle for VHPI type %s(%d)", m_impl->reason_to_string(cb_data.reason), cb_data.reason); - return -1; + goto error; } cbState = (vhpiStateT)vhpi_get(vhpiStateP, new_hdl); if (vhpiEnable != cbState) { LOG_ERROR("VHPI ERROR: Registered callback isn't enabled! Got %d\n", cbState); - return -1; + goto error; } m_obj_hdl = new_hdl; @@ -210,6 +210,10 @@ int VhpiCbHdl::arm_callback(void) m_state = GPI_PRIMED; return ret; + +error: + m_state = GPI_FREE; + return -1; } // Value related functions @@ -250,6 +254,10 @@ int VhpiSignalObjHdl::set_signal_value(long value) for (i=0; i(), &m_value, vhpiForcePropagate); - check_vhpi_error(); + if (vhpi_put_value(GpiObjHdl::get_handle(), &m_value, vhpiDeposit)) { + check_vhpi_error(); + return -1; + } + return 0; } @@ -296,7 +307,11 @@ int VhpiSignalObjHdl::set_signal_value(double value) } } - vhpi_put_value(GpiObjHdl::get_handle(), &m_value, vhpiForcePropagate); + if (vhpi_put_value(GpiObjHdl::get_handle(), &m_value, vhpiDeposit)) { + check_vhpi_error(); + return -1; + } + return 0; } @@ -314,9 +329,15 @@ int VhpiSignalObjHdl::set_signal_value(std::string &value) int len = value.length(); - if (len > m_num_elems) { + // Since we may not get the numElems correctly from the sim and have to infer it + // we also need to set it here as well each time. + + m_value.numElems = len; + + if (len > m_num_elems) { LOG_DEBUG("VHPI: Attempt to write string longer than (%s) signal %d > %d", m_name.c_str(), len, m_num_elems); + m_value.numElems = m_num_elems; } std::string::iterator iter; @@ -347,8 +368,11 @@ int VhpiSignalObjHdl::set_signal_value(std::string &value) } } - vhpi_put_value(GpiObjHdl::get_handle(), &m_value, vhpiForcePropagate); - check_vhpi_error(); + if (vhpi_put_value(GpiObjHdl::get_handle(), &m_value, vhpiDeposit)) { + check_vhpi_error(); + return -1; + } + return 0; } From 3146c2b7372d7e0522ab81e47d2c0f4d6e25b2bc Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 24 Sep 2015 14:05:16 +0100 Subject: [PATCH 1022/2656] Issue #17: Downgrade message indicating that iteration over an unknown type is not possible to warning from error --- lib/vpi/VpiCbHdl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 3e25af41..ac9b66df 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -475,7 +475,7 @@ VpiIterator::VpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(i int type = vpi_get(vpiType, vpi_hdl); if (NULL == (selected = iterate_over.get_options(type))) { - LOG_ERROR("VPI: Implementation does not know how to iterate over %s(%d)", + LOG_WARN("VPI: Implementation does not know how to iterate over %s(%d)", vpi_get_str(vpiType, vpi_hdl), type); return; } From df219c0fdbd550ff6ae6ec0c333c994c1a4d39c8 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 24 Sep 2015 16:52:08 +0100 Subject: [PATCH 1023/2656] Issue #17: Allow a raw handle to be passed up from an iterator if it was able to determine it was not native --- lib/fli/FliImpl.cpp | 7 +++ lib/fli/FliImpl.h | 1 + lib/gpi/GpiCommon.cpp | 49 +++++++++++++++-- lib/gpi/gpi_priv.h | 9 +++- lib/vhpi/VhpiCbHdl.cpp | 20 +++++-- lib/vhpi/VhpiImpl.cpp | 31 +++++++++++ lib/vhpi/VhpiImpl.h | 3 +- lib/vpi/VpiCbHdl.cpp | 53 ++++++++++++------- lib/vpi/VpiImpl.cpp | 24 +++++++++ lib/vpi/VpiImpl.h | 5 +- .../test_iteration.py | 25 +++++++-- 11 files changed, 190 insertions(+), 37 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 3b70cf2b..02c2252f 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -107,6 +107,13 @@ void FliImpl::sim_end(void) mti_Cmd(stop); } +GpiObjHdl* FliImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) +{ + LOG_WARN("%s implementation can not create from raw handle", + m_name.c_str()); + return NULL; +} + /** * @name Native Check Create * @brief Determine whether a simulation object is native to FLI and create diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index 7b0e367b..e47baab6 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -241,6 +241,7 @@ class FliImpl : public GpiImplInterface { /* Hierachy related */ GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); + GpiObjHdl* native_check_create(void *raw_hdl, GpiObjHdl *paret); GpiObjHdl *get_root_handle(const char *name); GpiIterator *iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type); diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index e58f9d13..6719a358 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -272,6 +272,37 @@ static GpiObjHdl* __gpi_get_handle_by_name(GpiObjHdl *parent, } } +static GpiObjHdl* __gpi_get_handle_by_raw(GpiObjHdl *parent, + void *raw_hdl, + GpiImplInterface *skip_impl) +{ + vector::iterator iter; + + GpiObjHdl *hdl = NULL; + + for (iter = registered_impls.begin(); + iter != registered_impls.end(); + iter++) { + + if (skip_impl && (skip_impl == (*iter))) { + LOG_DEBUG("Skipping %s impl", (*iter)->get_name_c()); + continue; + } + + if ((hdl = (*iter)->native_check_create(raw_hdl, parent))) { + LOG_DEBUG("Found %s via %s", hdl->get_name_str(), (*iter)->get_name_c()); + break; + } + } + + if (hdl) + return CHECK_AND_STORE(hdl); + else { + LOG_DEBUG("Failed to convert a raw handle to valid object"); + return hdl; + } +} + gpi_sim_hdl gpi_get_handle_by_name(gpi_sim_hdl parent, const char *name) { std::string s_name = name; @@ -322,16 +353,17 @@ gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) while (true) { GpiObjHdl *next = NULL; - int ret = iter->next_handle(name, &next); + void *raw_hdl = NULL; + GpiIterator::Status ret = iter->next_handle(name, &next, &raw_hdl); switch (ret) { - case GpiIterator::VALID: - LOG_DEBUG("Create a valid handle"); + case GpiIterator::NATIVE: + LOG_DEBUG("Create a native handle"); return CHECK_AND_STORE(next); - case GpiIterator::VALID_NO_NAME: + case GpiIterator::NATIVE_NO_NAME: LOG_DEBUG("Unable to fully setup handle, skipping"); continue; - case GpiIterator::INVALID: + case GpiIterator::NOT_NATIVE: LOG_DEBUG("Found a name but unable to create via native implementation, trying others"); next = __gpi_get_handle_by_name(parent, name, iter->m_impl); if (next) { @@ -339,6 +371,13 @@ gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) } LOG_WARN("Unable to create %s via any registered implementation", name.c_str()); continue; + case GpiIterator::NOT_NATIVE_NO_NAME: + LOG_DEBUG("Found an object but not accesbile via %s, trying others", iter->m_impl->get_name_c()); + next = __gpi_get_handle_by_raw(parent, raw_hdl, iter->m_impl); + if (next) { + return next; + } + continue; case GpiIterator::END: LOG_DEBUG("Reached end of iterator"); delete iter; diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index a2d93197..3801be29 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -215,14 +215,18 @@ class GpiClockHdl { class GpiIterator : public GpiHdl { public: enum Status { - VALID, VALID_NO_NAME, INVALID, END + NATIVE, // Fully resolved object was created + NATIVE_NO_NAME, // Native object was found but unable to fully create + NOT_NATIVE, // Mon native object was found but we did get a name + NOT_NATIVE_NO_NAME, // Mon native object was found without a name + END }; GpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiHdl(impl), m_parent(hdl) { } virtual ~GpiIterator() { } - virtual int next_handle(std::string &name, GpiObjHdl **hdl) { + virtual Status next_handle(std::string &name, GpiObjHdl **hdl, void **raw_hdl) { name = ""; *hdl = NULL; return GpiIterator::END; @@ -251,6 +255,7 @@ class GpiImplInterface { /* Hierachy related */ virtual GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent) = 0; virtual GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent) = 0; + virtual GpiObjHdl* native_check_create(void *raw_hdl, GpiObjHdl *parent) = 0; virtual GpiObjHdl *get_root_handle(const char *name) = 0; virtual GpiIterator *iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) = 0; diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 411ce677..94b64e53 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -629,7 +629,7 @@ std::vector* KindMappings::get_options(vhpiClassKindT type) std::map >::iterator valid = options_map.find(type); if (options_map.end() == valid) { - LOG_ERROR("VHPI: Implementation does not know how to iterate over %d", type); + LOG_WARN("VHPI: Implementation does not know how to iterate over %d", type); return NULL; } else { return &valid->second; @@ -712,7 +712,9 @@ VhpiIterator::~VhpiIterator() vhpi_release_handle(m_iterator); } -int VhpiIterator::next_handle(std::string &name, GpiObjHdl **hdl) +GpiIterator::Status VhpiIterator::next_handle(std::string &name, + GpiObjHdl **hdl, + void **raw_hdl) { vhpiHandleT obj; GpiObjHdl *new_obj; @@ -764,7 +766,15 @@ int VhpiIterator::next_handle(std::string &name, GpiObjHdl **hdl) const char *c_name = vhpi_get_str(vhpiCaseNameP, obj); if (!c_name) { - return GpiIterator::VALID_NO_NAME; + int type = vhpi_get(vhpiKindP, obj); + LOG_WARN("Unable to get the name for this object of type %d", type); + + if (type < 1000) { + *raw_hdl = (void*)obj; + return GpiIterator::NOT_NATIVE_NO_NAME; + } + + return GpiIterator::NATIVE_NO_NAME; } name = c_name; @@ -786,9 +796,9 @@ int VhpiIterator::next_handle(std::string &name, GpiObjHdl **hdl) new_obj = vhpi_impl->create_gpi_obj_from_handle(obj, name, fq_name); if (new_obj) { *hdl = new_obj; - return GpiIterator::VALID; + return GpiIterator::NATIVE; } else - return GpiIterator::INVALID; + return GpiIterator::NOT_NATIVE; } diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 89fcd496..6152ec6e 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -272,6 +272,37 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, return new_obj; } +GpiObjHdl *VhpiImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) +{ + LOG_DEBUG("Trying to convert raw to VHPI handle"); + + vhpiHandleT new_hdl = (vhpiHandleT)raw_hdl; + + std::string fq_name = parent->get_fullname(); + const char *c_name = vhpi_get_str(vhpiNameP, new_hdl); + if (!c_name) { + LOG_DEBUG("Unable to query name of passed in handle"); + return NULL; + } + + std::string name = c_name; + + if (fq_name == ":") { + fq_name += name; + } else { + fq_name += "." + name; + } + + GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); + if (new_obj == NULL) { + vhpi_release_handle(new_hdl); + LOG_DEBUG("Unable to fetch object %s", fq_name.c_str()); + return NULL; + } + + return new_obj; +} + GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) { vhpiHandleT new_hdl; diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 529eac74..0b226ef8 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -198,7 +198,7 @@ class VhpiIterator : public GpiIterator { virtual ~VhpiIterator(); - int next_handle(std::string &name, GpiObjHdl **hdl); + Status next_handle(std::string &name, GpiObjHdl **hdl, void **raw_hdl); private: vhpiHandleT m_iterator; @@ -231,6 +231,7 @@ class VhpiImpl : public GpiImplInterface { int deregister_callback(GpiCbHdl *obj_hdl); GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); + GpiObjHdl* native_check_create(void *raw_hdl, GpiObjHdl *parent); const char * reason_to_string(int reason); const char * format_to_string(int format); diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index ac9b66df..4e3e0ba9 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -162,11 +162,6 @@ long VpiSignalObjHdl::get_signal_value_long(void) int VpiSignalObjHdl::set_signal_value(long value) { FENTER - - LOG_WARN("Writing %ld to %s", - value, - m_name.c_str()); - s_vpi_value value_s; value_s.value.integer = value; @@ -511,7 +506,9 @@ VpiIterator::~VpiIterator() vpi_free_object(m_iterator); } -int VpiSingleIterator::next_handle(std::string &name, GpiObjHdl **hdl) +GpiIterator::Status VpiSingleIterator::next_handle(std::string &name, + GpiObjHdl **hdl, + void **raw_hdl) { GpiObjHdl *new_obj; vpiHandle obj; @@ -525,15 +522,18 @@ int VpiSingleIterator::next_handle(std::string &name, GpiObjHdl **hdl) const char *c_name = vpi_get_str(vpiName, obj); if (!c_name) { - return GpiIterator::VALID_NO_NAME; - } - name = c_name; + int type = vpi_get(vpiType, obj); + LOG_WARN("Unable to get the name for this object of type %d", type); + + if (type >= 1000) { + *raw_hdl = (void*)obj; + return GpiIterator::NOT_NATIVE_NO_NAME; + } - const char *c_fq_name = vpi_get_str(vpiFullName, obj); - if (!c_fq_name) { - return GpiIterator::VALID_NO_NAME; + return GpiIterator::NATIVE_NO_NAME; } - std::string fq_name = c_fq_name; + + std::string fq_name = c_name; LOG_DEBUG("vpi_scan found '%s = '%s'", name.c_str(), fq_name.c_str()); @@ -541,13 +541,13 @@ int VpiSingleIterator::next_handle(std::string &name, GpiObjHdl **hdl) new_obj = vpi_impl->create_gpi_obj_from_handle(obj, name, fq_name); if (new_obj) { *hdl = new_obj; - return GpiIterator::VALID; + return GpiIterator::NATIVE; } else - return GpiIterator::INVALID; + return GpiIterator::NOT_NATIVE; } -int VpiIterator::next_handle(std::string &name, GpiObjHdl **hdl) +GpiIterator::Status VpiIterator::next_handle(std::string &name, GpiObjHdl **hdl, void **raw_hdl) { GpiObjHdl *new_obj; vpiHandle obj; @@ -588,9 +588,24 @@ int VpiIterator::next_handle(std::string &name, GpiObjHdl **hdl) return GpiIterator::END; } + /* Simulators vary here. Some will allow the name to be accessed + across boundary. We can simply return this up and allow + the object to be created. Others do not. In this case + we see if the object is in out type range and if not + return the raw_hdl up */ + const char *c_name = vpi_get_str(vpiName, obj); if (!c_name) { - return GpiIterator::VALID_NO_NAME; + /* This may be another type */ + int type = vpi_get(vpiType, obj); + LOG_WARN("Unable to get the name for this object of type %d", type); + + if (type >= 1000) { + *raw_hdl = (void*)obj; + return GpiIterator::NOT_NATIVE_NO_NAME; + } + + return GpiIterator::NATIVE_NO_NAME; } name = c_name; @@ -605,8 +620,8 @@ int VpiIterator::next_handle(std::string &name, GpiObjHdl **hdl) new_obj = vpi_impl->create_gpi_obj_from_handle(obj, name, fq_name); if (new_obj) { *hdl = new_obj; - return GpiIterator::VALID; + return GpiIterator::NATIVE; } else - return GpiIterator::INVALID; + return GpiIterator::NOT_NATIVE; } diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index e90383ff..3f48aa73 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -184,6 +184,30 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, return new_obj; } +GpiObjHdl* VpiImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) +{ + LOG_DEBUG("Trying to convert raw to VPI handle"); + + vpiHandle new_hdl = (vpiHandle)raw_hdl; + + const char *c_name = vpi_get_str(vpiName, new_hdl); + if (!c_name) { + LOG_DEBUG("Unable to query name of passed in handle"); + return NULL; + } + + std::string name = c_name; + std::string fq_name = parent->get_fullname() + "." + name; + + GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); + if (new_obj == NULL) { + vpi_free_object(new_hdl); + LOG_ERROR("Unable to fetch object %s", fq_name.c_str()); + return NULL; + } + return new_obj; +} + GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) { vpiHandle new_hdl; diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 7a6879ea..799f86fc 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -219,7 +219,7 @@ class VpiIterator : public GpiIterator { virtual ~VpiIterator(); - int next_handle(std::string &name, GpiObjHdl **hdl); + Status next_handle(std::string &name, GpiObjHdl **hdl, void **raw_hdl); private: vpiHandle m_iterator; @@ -246,7 +246,7 @@ class VpiSingleIterator : public GpiIterator { } virtual ~VpiSingleIterator() { } - int next_handle(std::string &name, GpiObjHdl **hdl); + Status next_handle(std::string &name, GpiObjHdl **hdl, void **raw_hdl); protected: vpiHandle m_iterator; @@ -277,6 +277,7 @@ class VpiImpl : public GpiImplInterface { int deregister_callback(GpiCbHdl *obj_hdl); GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); + GpiObjHdl* native_check_create(void *raw_hdl, GpiObjHdl *parent); const char * reason_to_string(int reason); GpiObjHdl* create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &name, diff --git a/tests/test_cases/test_iteration_mixedlang/test_iteration.py b/tests/test_cases/test_iteration_mixedlang/test_iteration.py index 3aea79ad..446b3dcc 100644 --- a/tests/test_cases/test_iteration_mixedlang/test_iteration.py +++ b/tests/test_cases/test_iteration_mixedlang/test_iteration.py @@ -65,10 +65,22 @@ def recursive_discovery(dut): """ Recursively discover every single object in the design """ + if cocotb.SIM_NAME in ["ncsim(64)", + "ncsim"]: + # vpiAlways = 31 and vpiStructVar = 2 do not show up in IUS + # But vhpiSimpleSigAssignStmtK objects do, and ther are 2. + pass_total = 883 + else: + pass_total = 916 + tlog = logging.getLogger("cocotb.test") yield Timer(100) total = recursive_dump(dut, tlog) - tlog.info("Found a total of %d things", total) + + if pass_total != total: + raise TestFailure("Expected %d but found %d" % (pass_total, total)) + else: + tlog.info("Found a total of %d things", total) if not isinstance(dut.i_verilog.uart1.baud_gen_1.baud_freq, cocotb.handle.ModifiableObject): tlog.error("Expected dut.i_verilog.uart1.baud_gen_1.baud_freq to be modifiable") @@ -84,10 +96,17 @@ def recursive_discovery_boundary(dut): However if we manually delve through the language boundary we should then be able to iterate to discover objects """ + if cocotb.SIM_NAME in ["ncsim(64)", + "ncsim"]: + # # But vhpiSimpleSigAssignStmtK objects only show up on IUS, and ther are 2 + pass_total = 428 + else: + pass_total = 426 + tlog = logging.getLogger("cocotb.test") yield Timer(100) total = recursive_dump(dut.i_vhdl, tlog) tlog.info("Found a total of %d things", total) - if total != 426: - raise TestFailure("Expected 426 objects but found %d" % total) + if total != pass_total: + raise TestFailure("Expected %d objects but found %d" % (pass_total, total)) From bc81491f28a39d7b9b021832daa4de90d042cff6 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 24 Sep 2015 20:21:34 +0100 Subject: [PATCH 1024/2656] Issue #17: Define some magix numbers. Still in both implementations to preserve layer boundaries --- lib/vhpi/VhpiCbHdl.cpp | 4 +++- lib/vpi/VpiCbHdl.cpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 94b64e53..981f8c1b 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -712,6 +712,8 @@ VhpiIterator::~VhpiIterator() vhpi_release_handle(m_iterator); } +#define VHPI_TYPE_MIN (1000) + GpiIterator::Status VhpiIterator::next_handle(std::string &name, GpiObjHdl **hdl, void **raw_hdl) @@ -769,7 +771,7 @@ GpiIterator::Status VhpiIterator::next_handle(std::string &name, int type = vhpi_get(vhpiKindP, obj); LOG_WARN("Unable to get the name for this object of type %d", type); - if (type < 1000) { + if (type < VHPI_TYPE_MIN) { *raw_hdl = (void*)obj; return GpiIterator::NOT_NATIVE_NO_NAME; } diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 4e3e0ba9..ef7a92f5 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -547,6 +547,8 @@ GpiIterator::Status VpiSingleIterator::next_handle(std::string &name, return GpiIterator::NOT_NATIVE; } +#define VPI_TYPE_MAX (1000) + GpiIterator::Status VpiIterator::next_handle(std::string &name, GpiObjHdl **hdl, void **raw_hdl) { GpiObjHdl *new_obj; @@ -600,7 +602,7 @@ GpiIterator::Status VpiIterator::next_handle(std::string &name, GpiObjHdl **hdl, int type = vpi_get(vpiType, obj); LOG_WARN("Unable to get the name for this object of type %d", type); - if (type >= 1000) { + if (type >= VPI_TYPE_MAX) { *raw_hdl = (void*)obj; return GpiIterator::NOT_NATIVE_NO_NAME; } From 0788a556f12323685d4ab811892c435bb860c79b Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Fri, 25 Sep 2015 10:41:42 +0200 Subject: [PATCH 1025/2656] memory dict is bytes sized --- cocotb/drivers/avalon.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 98bb853e..2663c6ac 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -296,6 +296,7 @@ def _respond(self): Coroutine to response to the actual requests """ edge = RisingEdge(self.clock) + dataByteSize = self._width/8 while True: yield edge self._do_response() @@ -316,11 +317,11 @@ def _respond(self): self._responses.append(self._mem[addr]) else: addr = self.bus.address.value.integer - if addr % (self._width/8) != 0: + if addr % dataByteSize != 0: self.log.error("Address must be aligned to data width" + "(addr = " + hex(addr) + ", width = " + str(self._width)) - addr = addr / (self._width/8) + addr = addr / dataByteSize burstcount = self.bus.burstcount.value.integer byteenable = self.bus.byteenable.value if byteenable != int("1"*len(self.bus.byteenable), 2): @@ -343,17 +344,19 @@ def _respond(self): yield edge for count in range(burstcount): - if (addr + count) not in self._mem: - self.log.warning("Attempt to read from uninitialised " - "address 0x%x" % (addr + count) ) + if (addr + count)*dataByteSize not in self._mem: + self.log.warning( + "Attempt to burst read from uninitialised " + + "address 0x%x (addr 0x%x count 0x%x)" % + ((addr + count)*dataByteSize, addr, count) ) self._responses.append(True) else: value = 0 - for i in range(self._width/8): + for i in range(dataByteSize): value +=\ - self._mem[(addr + count)*(self._width/8) + i] << i*8 + self._mem[(addr + count)*dataByteSize + i] << i*8 self.log.debug("Read from address 0x%x returning 0x%x" % - (addr*(self._width/8), value)) + (addr*dataByteSize, value)) self._responses.append(value) yield edge self._do_response() From 6db2652284b2d3171033be5560406c78859c51fa Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sun, 27 Sep 2015 08:57:50 +0100 Subject: [PATCH 1026/2656] Issue #17: Testcase for VHDL configurations --- tests/designs/vhdl_configurations/Makefile | 17 ++++++++ .../vhdl_configurations/configurations.vhd | 15 +++++++ tests/designs/vhdl_configurations/dut.vhd | 42 +++++++++++++++++++ .../designs/vhdl_configurations/testbench.sv | 15 +++++++ .../designs/vhdl_configurations/testbench.vhd | 28 +++++++++++++ tests/test_cases/test_configuration/Makefile | 33 +++++++++++++++ .../test_configuration/test_configurations.py | 35 ++++++++++++++++ 7 files changed, 185 insertions(+) create mode 100644 tests/designs/vhdl_configurations/Makefile create mode 100644 tests/designs/vhdl_configurations/configurations.vhd create mode 100644 tests/designs/vhdl_configurations/dut.vhd create mode 100644 tests/designs/vhdl_configurations/testbench.sv create mode 100644 tests/designs/vhdl_configurations/testbench.vhd create mode 100644 tests/test_cases/test_configuration/Makefile create mode 100644 tests/test_cases/test_configuration/test_configurations.py diff --git a/tests/designs/vhdl_configurations/Makefile b/tests/designs/vhdl_configurations/Makefile new file mode 100644 index 00000000..e1287fc0 --- /dev/null +++ b/tests/designs/vhdl_configurations/Makefile @@ -0,0 +1,17 @@ +TOPLEVEL = testbench + +VHDL_SOURCES = $(COCOTB)/tests/designs/vhdl_configurations/dut.vhd +ifeq ($(TOPLEVEL_LANG),vhdl) + VHDL_SOURCES += $(COCOTB)/tests/designs/vhdl_configurations/testbench.vhd + GPI_IMPL := vhpi +endif +VHDL_SOURCES += $(COCOTB)/tests/designs/vhdl_configurations/configurations.vhd + +ifeq ($(TOPLEVEL_LANG),verilog) + VERILOG_SOURCES = $(COCOTB)/tests/designs/vhdl_configurations/testbench.sv + GPI_IMPL := vpi +endif + +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim + diff --git a/tests/designs/vhdl_configurations/configurations.vhd b/tests/designs/vhdl_configurations/configurations.vhd new file mode 100644 index 00000000..e6a8b7d6 --- /dev/null +++ b/tests/designs/vhdl_configurations/configurations.vhd @@ -0,0 +1,15 @@ +configuration config_single of testbench is + for myconfig + for dut_inst : dut + use entity work.dut(single); + end for; + end for; +end configuration config_single; + +configuration config_double of testbench is + for myconfig + for dut_inst : dut + use entity work.dut(double); + end for; + end for; +end configuration config_double; diff --git a/tests/designs/vhdl_configurations/dut.vhd b/tests/designs/vhdl_configurations/dut.vhd new file mode 100644 index 00000000..0be64af0 --- /dev/null +++ b/tests/designs/vhdl_configurations/dut.vhd @@ -0,0 +1,42 @@ +library ieee; +use ieee.std_logic_1164.all; + +entity dut is + port( + clk : in std_ulogic; + data_in : in std_ulogic; + data_out : out std_ulogic + ); +end entity dut; + +architecture single of dut is begin + + report_p : process begin + report "this is dut(single)"; + wait; + end process; + + clocked_p : process(clk) is begin + if rising_edge(clk) then + data_out <= data_in; + end if; + end process; +end architecture single; + + +architecture double of dut is + signal data_in_r : std_ulogic; +begin + + report_p : process begin + report "this is dut(double)"; + wait; + end process; + + clocked_p : process(clk) is begin + if rising_edge(clk) then + data_in_r <= data_in; + data_out <= data_in_r; + end if; + end process; +end architecture double; diff --git a/tests/designs/vhdl_configurations/testbench.sv b/tests/designs/vhdl_configurations/testbench.sv new file mode 100644 index 00000000..c93443ab --- /dev/null +++ b/tests/designs/vhdl_configurations/testbench.sv @@ -0,0 +1,15 @@ +module testbench ( + input logic clk, + input logic data_in, + output logic data_out +); + +dut i_dut ( + .clk (clk), + .data_in (data_in), + .data_out (data_out) +); + +endmodule + + diff --git a/tests/designs/vhdl_configurations/testbench.vhd b/tests/designs/vhdl_configurations/testbench.vhd new file mode 100644 index 00000000..82785857 --- /dev/null +++ b/tests/designs/vhdl_configurations/testbench.vhd @@ -0,0 +1,28 @@ +library ieee; +use ieee.std_logic_1164.all; + +entity testbench is +end entity testbench; + +architecture myconfig of testbench is + component dut + port( + clk : in std_ulogic; + data_in : in std_ulogic; + data_out : out std_ulogic); + end component dut; + + signal clk : std_ulogic; + signal data_in : std_ulogic; + signal data_out : std_ulogic; + +begin + + dut_inst : component dut + port map( + clk => clk, + data_in => data_in, + data_out => data_out + ); +end architecture myconfig; + diff --git a/tests/test_cases/test_configuration/Makefile b/tests/test_cases/test_configuration/Makefile new file mode 100644 index 00000000..eb9ade8b --- /dev/null +++ b/tests/test_cases/test_configuration/Makefile @@ -0,0 +1,33 @@ +############################################################################### +# Copyright (c) 2015 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +PWD := $(shell pwd) +COCOTB := $(PWD)/../../.. +TOPLEVEL_LANG?=vhdl + +include ../../designs/vhdl_configurations/Makefile +MODULE = test_configurations diff --git a/tests/test_cases/test_configuration/test_configurations.py b/tests/test_cases/test_configuration/test_configurations.py new file mode 100644 index 00000000..550c93b4 --- /dev/null +++ b/tests/test_cases/test_configuration/test_configurations.py @@ -0,0 +1,35 @@ +''' Copyright (c) 2015 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +import cocotb +from cocotb.triggers import Timer + +@cocotb.test() +def iterate(dut): + yield Timer(100) + for thing in dut: + thing._log.info("Found %s" % type(thing)) + yield Timer(100) + From c703f9a55ea63f5035ed2e941a619ae03d162b2b Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sun, 27 Sep 2015 19:24:11 +0100 Subject: [PATCH 1027/2656] Issue #17: Exclude test on icarus --- tests/designs/vhdl_configurations/Makefile | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/designs/vhdl_configurations/Makefile b/tests/designs/vhdl_configurations/Makefile index e1287fc0..a4859236 100644 --- a/tests/designs/vhdl_configurations/Makefile +++ b/tests/designs/vhdl_configurations/Makefile @@ -2,7 +2,7 @@ TOPLEVEL = testbench VHDL_SOURCES = $(COCOTB)/tests/designs/vhdl_configurations/dut.vhd ifeq ($(TOPLEVEL_LANG),vhdl) - VHDL_SOURCES += $(COCOTB)/tests/designs/vhdl_configurations/testbench.vhd + VHDL_SOURCES += $(COCOTB)/tests/designs/vhdl_configurations/testbench.vhd GPI_IMPL := vhpi endif VHDL_SOURCES += $(COCOTB)/tests/designs/vhdl_configurations/configurations.vhd @@ -12,6 +12,11 @@ ifeq ($(TOPLEVEL_LANG),verilog) GPI_IMPL := vpi endif -include $(COCOTB)/makefiles/Makefile.inc -include $(COCOTB)/makefiles/Makefile.sim +ifneq ($(SIM),) + include $(COCOTB)/makefiles/Makefile.inc + include $(COCOTB)/makefiles/Makefile.sim +else +all: + @echo "Skipping test as VHDL not supported on Icarus" +endif From a208779938c2f0b953cf71026c7ebdb91eca848a Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Tue, 29 Sep 2015 14:46:35 +0200 Subject: [PATCH 1028/2656] adding chipselect 'cs' signal in AvalonMM --- cocotb/drivers/avalon.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 2663c6ac..ec79112e 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -54,7 +54,8 @@ class AvalonMM(BusDriver): """ _signals = ["address"] _optional_signals = ["readdata", "read", "write", "waitrequest", - "writedata", "readdatavalid", "byteenable"] + "writedata", "readdatavalid", "byteenable", + "cs"] def __init__(self, entity, name, clock): @@ -77,6 +78,9 @@ def __init__(self, entity, name, clock): if hasattr(self.bus, "byteenable"): self.bus.byteenable.setimmediatevalue(0) + if hasattr(self.bus, "cs"): + self.bus.cs.setimmediatevalue(0) + self.bus.address.setimmediatevalue(0) def read(self, address): @@ -130,6 +134,8 @@ def read(self, address, sync=True): self.bus.read <= 1 if hasattr(self.bus, "byteenable"): self.bus.byteenable <= int("1"*len(self.bus.byteenable), 2) + if hasattr(self.bus, "cs"): + self.bus.cs <= 1 # Wait for waitrequest to be low if hasattr(self.bus, "waitrequest"): @@ -150,6 +156,8 @@ def read(self, address, sync=True): self.bus.read <= 0 if hasattr(self.bus, "byteenable"): self.bus.byteenable <= 0 + if hasattr(self.bus, "cs"): + self.bus.cs <= 0 self._release_lock() raise ReturnValue(data) @@ -174,6 +182,8 @@ def write(self, address, value): self.bus.write <= 1 if hasattr(self.bus, "byteenable"): self.bus.byteenable <= int("1"*len(self.bus.byteenable), 2) + if hasattr(self.bus, "cs"): + self.bus.cs <= 1 # Wait for waitrequest to be low if hasattr(self.bus, "waitrequest"): @@ -184,6 +194,8 @@ def write(self, address, value): self.bus.write <= 0 if hasattr(self.bus, "byteenable"): self.bus.byteenable <= 0 + if hasattr(self.bus, "cs"): + self.bus.cs <= 0 v = self.bus.writedata.value v.binstr = "x" * len(self.bus.writedata) From 4770f7a259831d22f6564d5d8518866f2d5795b9 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 29 Sep 2015 14:14:33 +0100 Subject: [PATCH 1029/2656] Issue #17: A few isues found with root handle The method for getting DUT was wrong. In historic efforts to fix this a few plasters where applied that caused simulator specific changes during iteration where we had to skip the vhpiRootInstK on Riviera. We also had to change what was passed in as TOPLEVEL in the IUS makefile. This was all wrong. The new root code tries to get the dut by name. If this does not work then some hiearchy traversal is done to make sure we have the correct designUnit and then we return the root handle. --- lib/vhpi/VhpiCbHdl.cpp | 25 +------- lib/vhpi/VhpiImpl.cpp | 59 +++++++++++++------ makefiles/simulators/Makefile.ius | 3 +- .../test_configuration/test_configurations.py | 8 ++- 4 files changed, 48 insertions(+), 47 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 981f8c1b..09ebf2e5 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -679,30 +679,7 @@ VhpiIterator::VhpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator * We check that something is going to come back if not we try the level * down */ - m_iter_obj = vhpi_hdl; - - if (vhpiRootInstK == vhpi_get(vhpiKindP, vhpi_hdl)) { - uint32_t children = 0; - vhpiHandleT tmp_hdl; - - for(tmp_hdl = vhpi_scan(iterator); - tmp_hdl != NULL; - tmp_hdl = vhpi_scan(iterator), children++) { } - - iterator = vhpi_iterator(*one2many, vhpi_hdl); - - // Only a single child, skip a level - if (children == 1) { - tmp_hdl = vhpi_scan(iterator); - vhpi_release_handle(iterator); - iterator = vhpi_iterator(*one2many, tmp_hdl); - LOG_WARN("Skipped vhpiRootInstK to get to %s (%s)", - vhpi_get_str(vhpiKindStrP, tmp_hdl), - vhpi_get_str(vhpiFullNameP, tmp_hdl)); - m_iter_obj = tmp_hdl; - } - } - + m_iter_obj = vhpi_hdl; m_iterator = iterator; } diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 6152ec6e..bebad48b 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -391,52 +391,73 @@ GpiObjHdl *VhpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) GpiObjHdl *VhpiImpl::get_root_handle(const char* name) { - FENTER - vhpiHandleT root; - vhpiHandleT dut; - GpiObjHdl *rv; + vhpiHandleT root = NULL; + vhpiHandleT arch = NULL; + vhpiHandleT dut = NULL; + GpiObjHdl *rv = NULL; std::string root_name; + const char *found; root = vhpi_handle(vhpiRootInst, NULL); check_vhpi_error(); if (!root) { LOG_ERROR("VHPI: Attempting to get the vhpiRootInst failed"); - FEXIT return NULL; + } else { + LOG_DEBUG("VHPI: We have found root='%s'", vhpi_get_str(vhpiCaseNameP, root)); } - if (name) - dut = vhpi_handle_by_name(name, NULL); - else - dut = vhpi_handle(vhpiDesignUnit, root); + if (name) { + if (NULL == (dut = vhpi_handle_by_name(name, NULL))) { + LOG_DEBUG("VHPI: Unable to query by name"); + check_vhpi_error(); + } + } - check_vhpi_error(); + if (!dut) { + if (NULL == (arch = vhpi_handle(vhpiDesignUnit, root))) { + LOG_DEBUG("VHPI: Unable to get vhpiDesignUnit via root"); + check_vhpi_error(); + return NULL; + } - if (root) { - LOG_DEBUG("VHPI: We have found root='%s'", vhpi_get_str(vhpiCaseNameP, root)); + if (NULL == (dut = vhpi_handle(vhpiPrimaryUnit, arch))) { + LOG_DEBUG("VHPI: Unable to get vhpiPrimaryUnit via arch"); + check_vhpi_error(); + return NULL; + } + + /* if this matches the name then it is what we want, but we + use the handle two levels up as the dut as do not want an + object of type vhpiEntityDeclK as the dut */ + + found = vhpi_get_str(vhpiCaseNameP, dut); + dut = root; + + } else { + found = vhpi_get_str(vhpiCaseNameP, dut); } if (!dut) { LOG_ERROR("VHPI: Attempting to get the DUT handle failed"); - FEXIT return NULL; } - const char *found = vhpi_get_str(vhpiCaseNameP, dut); - check_vhpi_error(); + if (!found) { + LOG_ERROR("VHPI: Unable to query name for DUT handle"); + return NULL; + } if (name != NULL && strcmp(name, found)) { - LOG_WARN("VHPI: Root '%s' doesn't match requested toplevel %s", found, name); - FEXIT + LOG_WARN("VHPI: DUT '%s' doesn't match requested toplevel %s", found, name); return NULL; } root_name = found; - rv = new GpiObjHdl(this, root, to_gpi_objtype(vhpi_get(vhpiKindP, root))); + rv = new GpiObjHdl(this, dut, to_gpi_objtype(vhpi_get(vhpiKindP, dut))); rv->initialise(root_name, root_name); - FEXIT return rv; } diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 1e4dc79d..d9c82cb3 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -61,7 +61,6 @@ ifeq ($(GPI_IMPL),vhpi) EXTRA_ARGS += -v93 EXTRA_ARGS += -top $(TOPLEVEL) MAKE_LIB = -makelib $(TOPLEVEL) - ROOT_LEVEL =: HDL_SOURCES = $(VHDL_SOURCES) endif @@ -69,7 +68,7 @@ GPI_LIB = $(COCOTB_VPI_LIB) $(COCOTB_VHPI_LIB) results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(GPI_LIB) LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ - LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(ROOT_LEVEL) \ + LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ irun $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) clean:: diff --git a/tests/test_cases/test_configuration/test_configurations.py b/tests/test_cases/test_configuration/test_configurations.py index 550c93b4..db4cbcde 100644 --- a/tests/test_cases/test_configuration/test_configurations.py +++ b/tests/test_cases/test_configuration/test_configurations.py @@ -26,10 +26,14 @@ import cocotb from cocotb.triggers import Timer +def sub_iterate(unit): + for thing in unit: + thing._log.info("Found %s.%s %s" % (unit._name, thing._name, type(thing))) + sub_iterate(thing) + @cocotb.test() def iterate(dut): yield Timer(100) - for thing in dut: - thing._log.info("Found %s" % type(thing)) + sub_iterate(dut) yield Timer(100) From 5ceeba656462e1f7e99f20b8bdb1b9e47d12204c Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 30 Sep 2015 09:48:19 +0100 Subject: [PATCH 1030/2656] Issue #17: Remove duplicate class declaration and use template pushed up to GPI instead --- lib/gpi/GpiCommon.cpp | 3 +-- lib/gpi/gpi_priv.h | 40 ++++++++++++++++++++++++++++-- lib/vhpi/VhpiCbHdl.cpp | 55 +++++++++++++----------------------------- lib/vhpi/VhpiImpl.h | 14 +---------- lib/vpi/VpiCbHdl.cpp | 39 ++++++------------------------ lib/vpi/VpiImpl.h | 14 +---------- 6 files changed, 66 insertions(+), 99 deletions(-) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 6719a358..ef9e934f 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -555,11 +555,10 @@ void gpi_deregister_callback(gpi_sim_hdl hdl) cb_hdl->m_impl->deregister_callback(cb_hdl); } -GpiImplInterface::~GpiImplInterface() { } -GpiImplInterface::GpiImplInterface(const std::string& name) : m_name(name) { } const char* GpiImplInterface::get_name_c(void) { return m_name.c_str(); } + const string& GpiImplInterface::get_name_s(void) { return m_name; } diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 3801be29..1567b842 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -32,6 +32,8 @@ #include #include #include +#include +#include typedef enum gpi_cb_state { GPI_FREE = 0, @@ -240,13 +242,47 @@ class GpiIterator : public GpiHdl { GpiObjHdl *m_parent; }; +template class GpiIteratorMapping { +public: + GpiIteratorMapping(void(*populate)(GpiIteratorMapping&)) { + populate(*this); + } +public: + std::vector* get_options(Ti type); + void add_to_options(Ti type, Tm *options); +private: + std::map > options_map; +}; + +template void GpiIteratorMapping::add_to_options(Ti type, Tm *options) +{ + std::vector option_vec; + Tm *ptr = options; + while (*ptr) { + option_vec.push_back(*ptr); + ptr++; + } + options_map[type] = option_vec; +} + +template std::vector * GpiIteratorMapping::get_options(Ti type) +{ + typename std::map >::iterator valid = options_map.find(type); + + if (options_map.end() == valid) { + return NULL; + } else { + return &valid->second; + } +} + class GpiImplInterface { public: - GpiImplInterface(const std::string& name); + GpiImplInterface(const std::string& name) : m_name(name) { } const char *get_name_c(void); const std::string& get_name_s(void); - virtual ~GpiImplInterface() = 0; + virtual ~GpiImplInterface() { } /* Sim related */ virtual void sim_end(void) = 0; diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 09ebf2e5..f8590de0 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -539,7 +539,7 @@ VhpiNextPhaseCbHdl::VhpiNextPhaseCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), cb_data.time = &vhpi_time; } -KindMappings::KindMappings() +void vhpi_mappings(GpiIteratorMapping &map) { /* vhpiRootInstK */ vhpiOneToManyT root_options[] = { @@ -553,7 +553,7 @@ KindMappings::KindMappings() vhpiBlockStmts, (vhpiOneToManyT)0, }; - add_to_options(vhpiRootInstK, &root_options[0]); + map.add_to_options(vhpiRootInstK, &root_options[0]); /* vhpiSigDeclK */ vhpiOneToManyT sig_options[] = { @@ -561,15 +561,15 @@ KindMappings::KindMappings() vhpiSelectedNames, (vhpiOneToManyT)0, }; - add_to_options(vhpiGenericDeclK, &sig_options[0]); - add_to_options(vhpiSigDeclK, &sig_options[0]); + map.add_to_options(vhpiGenericDeclK, &sig_options[0]); + map.add_to_options(vhpiSigDeclK, &sig_options[0]); /* vhpiIndexedNameK */ - add_to_options(vhpiSelectedNameK, &sig_options[0]); - add_to_options(vhpiIndexedNameK, &sig_options[0]); + map.add_to_options(vhpiSelectedNameK, &sig_options[0]); + map.add_to_options(vhpiIndexedNameK, &sig_options[0]); /* vhpiCompInstStmtK */ - add_to_options(vhpiCompInstStmtK, &root_options[0]); + map.add_to_options(vhpiCompInstStmtK, &root_options[0]); /* vhpiSimpleSigAssignStmtK */ vhpiOneToManyT simplesig_options[] = { @@ -579,11 +579,11 @@ KindMappings::KindMappings() vhpiStmts, (vhpiOneToManyT)0, }; - add_to_options(vhpiCondSigAssignStmtK, &simplesig_options[0]); - add_to_options(vhpiSimpleSigAssignStmtK, &simplesig_options[0]); + map.add_to_options(vhpiCondSigAssignStmtK, &simplesig_options[0]); + map.add_to_options(vhpiSimpleSigAssignStmtK, &simplesig_options[0]); /* vhpiPortDeclK */ - add_to_options(vhpiPortDeclK, &sig_options[0]); + map.add_to_options(vhpiPortDeclK, &sig_options[0]); /* vhpiForGenerateK */ vhpiOneToManyT gen_options[] = { @@ -591,7 +591,7 @@ KindMappings::KindMappings() vhpiCompInstStmts, (vhpiOneToManyT)0, }; - add_to_options(vhpiForGenerateK, &gen_options[0]); + map.add_to_options(vhpiForGenerateK, &gen_options[0]); /* vhpiIfGenerateK */ vhpiOneToManyT ifgen_options[] = { @@ -600,7 +600,7 @@ KindMappings::KindMappings() vhpiCompInstStmts, (vhpiOneToManyT)0, }; - add_to_options(vhpiIfGenerateK, &ifgen_options[0]); + map.add_to_options(vhpiIfGenerateK, &ifgen_options[0]); /* vhpiConstDeclK */ vhpiOneToManyT const_options[] = { @@ -609,34 +609,11 @@ KindMappings::KindMappings() vhpiSelectedNames, (vhpiOneToManyT)0, }; - add_to_options(vhpiConstDeclK, &const_options[0]); + map.add_to_options(vhpiConstDeclK, &const_options[0]); } -void KindMappings::add_to_options(vhpiClassKindT type, vhpiOneToManyT *options) -{ - std::vector option_vec; - vhpiOneToManyT *ptr = options; - while (*ptr) { - option_vec.push_back(*ptr); - ptr++; - } - options_map[type] = option_vec; -} - -std::vector* KindMappings::get_options(vhpiClassKindT type) -{ - std::map >::iterator valid = options_map.find(type); - - if (options_map.end() == valid) { - LOG_WARN("VHPI: Implementation does not know how to iterate over %d", type); - return NULL; - } else { - return &valid->second; - } -} - -KindMappings VhpiIterator::iterate_over; +GpiIteratorMapping VhpiIterator::iterate_over(vhpi_mappings); VhpiIterator::VhpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(impl, hdl), m_iterator(NULL), @@ -645,8 +622,10 @@ VhpiIterator::VhpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator vhpiHandleT iterator; vhpiHandleT vhpi_hdl = m_parent->get_handle(); - if (NULL == (selected = iterate_over.get_options((vhpiClassKindT)vhpi_get(vhpiKindP, vhpi_hdl)))) + if (NULL == (selected = iterate_over.get_options((vhpiClassKindT)vhpi_get(vhpiKindP, vhpi_hdl)))) { + LOG_WARN("VHPI: Implementation does not know how to iterate over %d", type); return; + } /* Find the first mapping type that yields a valid iterator */ for (one2many = selected->begin(); diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 0b226ef8..e42b2a40 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -180,18 +180,6 @@ class VhpiSignalObjHdl : public GpiSignalObjHdl { VhpiValueCbHdl m_either_cb; }; -class KindMappings { -public: - KindMappings(); - -public: - std::map > options_map; - std::vector* get_options(vhpiClassKindT type); - -private: - void add_to_options(vhpiClassKindT type, vhpiOneToManyT *options); -}; - class VhpiIterator : public GpiIterator { public: VhpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl); @@ -203,7 +191,7 @@ class VhpiIterator : public GpiIterator { private: vhpiHandleT m_iterator; vhpiHandleT m_iter_obj; - static KindMappings iterate_over; /* Possible mappings */ + static GpiIteratorMapping iterate_over; /* Possible mappings */ std::vector *selected; /* Mapping currently in use */ std::vector::iterator one2many; }; diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index ef7a92f5..fb479f0d 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -358,7 +358,7 @@ VpiNextPhaseCbHdl::VpiNextPhaseCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), cb_data.reason = cbNextSimTime; } -KindMappings::KindMappings() +void vpi_mappings(GpiIteratorMapping &map) { /* vpiModule */ int32_t module_options[] = { @@ -390,7 +390,7 @@ KindMappings::KindMappings() //vpiInterfaceArray, // Aldec SEGV on mixed language 0 }; - add_to_options(vpiModule, &module_options[0]); + map.add_to_options(vpiModule, &module_options[0]); /* vpiNet */ int32_t net_options[] = { @@ -405,14 +405,14 @@ KindMappings::KindMappings() vpiNetBit, 0 }; - add_to_options(vpiNet, &net_options[0]); + map.add_to_options(vpiNet, &net_options[0]); /* vpiNetArray */ int32_t netarray_options[] = { vpiNet, 0 }; - add_to_options(vpiNetArray, &netarray_options[0]); + map.add_to_options(vpiNetArray, &netarray_options[0]); /* vpiRegArray */ @@ -420,47 +420,24 @@ KindMappings::KindMappings() vpiReg, 0 }; - add_to_options(vpiRegArray, ®array_options[0]); + map.add_to_options(vpiRegArray, ®array_options[0]); /* vpiMemory */ int32_t memory_options[] = { vpiMemoryWord, 0 }; - add_to_options(vpiMemory, &memory_options[0]); + map.add_to_options(vpiMemory, &memory_options[0]); /* vpiPort */ int32_t port_options[] = { vpiPortBit, 0 }; - add_to_options(vpiPort, &port_options[0]); + map.add_to_options(vpiPort, &port_options[0]); } -void KindMappings::add_to_options(int32_t type, int32_t *options) -{ - std::vector option_vec; - int32_t *ptr = options; - while (*ptr) { - option_vec.push_back(*ptr); - ptr++; - } - options_map[type] = option_vec; -} - -std::vector* KindMappings::get_options(int32_t type) -{ - std::map >::iterator valid = options_map.find(type); - - if (options_map.end() == valid) { - return NULL; - } else { - return &valid->second; - } -} - - -KindMappings VpiIterator::iterate_over; +GpiIteratorMapping VpiIterator::iterate_over(vpi_mappings); VpiIterator::VpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(impl, hdl), m_iterator(NULL) diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 799f86fc..636bdd20 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -173,18 +173,6 @@ class VpiShutdownCbHdl : public VpiCbHdl { virtual ~VpiShutdownCbHdl() { } }; -class KindMappings { -public: - KindMappings(); - -public: - std::map > options_map; - std::vector* get_options(int32_t type); - -private: - void add_to_options(int32_t type, int32_t *options); -}; - class VpiSignalObjHdl : public GpiSignalObjHdl { public: VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl, gpi_objtype_t objtype, bool is_const) : @@ -223,7 +211,7 @@ class VpiIterator : public GpiIterator { private: vpiHandle m_iterator; - static KindMappings iterate_over; /* Possible mappings */ + static GpiIteratorMapping iterate_over; /* Possible mappings */ std::vector *selected; /* Mapping currently in use */ std::vector::iterator one2many; }; From baa45dc7058f6c85f35931104f6206b7d48c2dc0 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 30 Sep 2015 09:48:34 +0100 Subject: [PATCH 1031/2656] Issue #17: Fix test error if noting was found --- tests/test_cases/test_iteration_vhdl/test_iteration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/test_iteration_vhdl/test_iteration.py b/tests/test_cases/test_iteration_vhdl/test_iteration.py index b1cff869..f6ffc10f 100644 --- a/tests/test_cases/test_iteration_vhdl/test_iteration.py +++ b/tests/test_cases/test_iteration_vhdl/test_iteration.py @@ -58,7 +58,7 @@ def discovery_all(dut): #for subthing in thing: # thing._log.info("Found something: %s" % thing._fullname) - thing._log.info("length of dut.inst_acs is %d" % len(dut.gen_acs)) + dut._log.info("length of dut.inst_acs is %d" % len(dut.gen_acs)) item = dut.gen_acs[3] item._log.info("this is item") From 26f90f341f807bc94fa34875a91f5005f1c55432 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 30 Sep 2015 12:10:51 +0100 Subject: [PATCH 1032/2656] Issue #17: Fix compilation error --- lib/vhpi/VhpiCbHdl.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index f8590de0..92439053 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -622,8 +622,10 @@ VhpiIterator::VhpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator vhpiHandleT iterator; vhpiHandleT vhpi_hdl = m_parent->get_handle(); - if (NULL == (selected = iterate_over.get_options((vhpiClassKindT)vhpi_get(vhpiKindP, vhpi_hdl)))) { - LOG_WARN("VHPI: Implementation does not know how to iterate over %d", type); + vhpiClassKindT type = (vhpiClassKindT)vhpi_get(vhpiKindP, vhpi_hdl); + if (NULL == (selected = iterate_over.get_options(type))) { + LOG_WARN("VHPI: Implementation does not know how to iterate over %s(%d)", + vhpi_get_str(vhpiKindStrP, vhpi_hdl), type); return; } From 91b2091e5324fee0b67b14be96c05d4e1c903b06 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 30 Sep 2015 13:50:25 +0100 Subject: [PATCH 1033/2656] Issue #17: Missing continue after setting obj=NULL breaks out of the iteration early --- lib/vhpi/VhpiCbHdl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 92439053..71c768a0 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -696,6 +696,7 @@ GpiIterator::Status VhpiIterator::next_handle(std::string &name, LOG_DEBUG("Skipping %s (%s)", vhpi_get_str(vhpiFullNameP, obj), vhpi_get_str(vhpiKindStrP, obj)); obj=NULL; + continue; } if (obj) { From 9c5067b5b3165554bc763ca1376ddd20fa27a603 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 30 Sep 2015 14:36:07 +0100 Subject: [PATCH 1034/2656] Issu #17: Remove duplicate calls and do not use unreliable method Duplicate calls to GpiObjHdl::initialise had crept in. Also teh vhpi_get_value with an 0 length vhpiBinStrVal is unreliable on some simulators. We have already by this point determined the size and so can simply use this to allocate up the memory. --- lib/vhpi/VhpiCbHdl.cpp | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 71c768a0..57fb1fd7 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -75,7 +75,6 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { case vhpiEnumVal: case vhpiLogicVal: case vhpiRealVal: { - GpiObjHdl::initialise(name, fq_name); break; } @@ -89,10 +88,6 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { LOG_CRITICAL("Unable to alloc mem for write buffer"); } LOG_DEBUG("Overriding num_elems to %d", m_num_elems); - - VhpiIterator test_iter(this->m_impl, this); - - GpiObjHdl::initialise(name, fq_name); break; } case vhpiRawDataVal: { @@ -108,8 +103,7 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { m_num_elems++; } LOG_DEBUG("Found vhpiRawDataVal with %d elements", m_num_elems); - GpiObjHdl::initialise(name, fq_name); - return 0; + goto gpi_init; } default: { @@ -118,22 +112,16 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { } } - int new_size = vhpi_get_value(GpiObjHdl::get_handle(), &m_binvalue); - if (new_size < 0) { - LOG_WARN("Failed to determine size for vhpiBinStrVal of signal object %s of type %s, falling back", - name.c_str(), - ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); - } - - if (new_size) { - m_binvalue.bufSize = new_size*sizeof(vhpiCharT); - m_binvalue.value.str = (vhpiCharT *)calloc(m_binvalue.bufSize + 1, sizeof(vhpiCharT)); + if (m_num_elems) { + m_binvalue.bufSize = m_num_elems*sizeof(vhpiCharT) + 1; + m_binvalue.value.str = (vhpiCharT *)calloc(m_binvalue.bufSize, sizeof(vhpiCharT)); if (!m_binvalue.value.str) { LOG_CRITICAL("Unable to alloc mem for read buffer of signal %s", name.c_str()); } } +gpi_init: return GpiObjHdl::initialise(name, fq_name); } @@ -187,7 +175,7 @@ int VhpiCbHdl::arm_callback(void) check_vhpi_error(); goto error; } - } + } } else { vhpiHandleT new_hdl = vhpi_register_cb(&cb_data, vhpiReturnCb); From 773b26a8a1c435a3ddb72b8ca3cede0a2db0be03 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 30 Sep 2015 17:41:28 +0100 Subject: [PATCH 1035/2656] Issue #17: Shuffle some log levels around so normal operation is at LOG_DEBUG and LOG_WARN is pushed to GPI as a last resort --- lib/gpi/GpiCommon.cpp | 15 +++++++++------ lib/vhpi/VhpiImpl.cpp | 4 ++-- lib/vpi/VpiCbHdl.cpp | 12 +++++++----- lib/vpi/VpiImpl.cpp | 9 +++++---- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index ef9e934f..fad67321 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -266,10 +266,8 @@ static GpiObjHdl* __gpi_get_handle_by_name(GpiObjHdl *parent, if (hdl) return CHECK_AND_STORE(hdl); - else { - LOG_DEBUG("Failed to find a hdl named %s", name.c_str()); + else return hdl; - } } static GpiObjHdl* __gpi_get_handle_by_raw(GpiObjHdl *parent, @@ -298,7 +296,7 @@ static GpiObjHdl* __gpi_get_handle_by_raw(GpiObjHdl *parent, if (hdl) return CHECK_AND_STORE(hdl); else { - LOG_DEBUG("Failed to convert a raw handle to valid object"); + LOG_WARN("Failed to convert a raw handle to valid object via any registered implementation"); return hdl; } } @@ -307,7 +305,12 @@ gpi_sim_hdl gpi_get_handle_by_name(gpi_sim_hdl parent, const char *name) { std::string s_name = name; GpiObjHdl *base = sim_to_hdl(parent); - return __gpi_get_handle_by_name(base, s_name, NULL); + GpiObjHdl *hdl = __gpi_get_handle_by_name(base, s_name, NULL); + if (!hdl) { + LOG_WARN("Failed to find a hdl named %s via any registered implementation", + name); + } + return hdl; } gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) @@ -330,7 +333,7 @@ gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) if (hdl) return CHECK_AND_STORE(hdl); else { - LOG_DEBUG("Failed to find a hdl at index %d", index); + LOG_WARN("Failed to find a hdl at index %d via any registered implementation", index); return hdl; } } diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index bebad48b..7c9556b4 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -159,7 +159,7 @@ gpi_objtype_t to_gpi_objtype(vhpiIntT vhpitype) return GPI_MODULE; default: - LOG_WARN("Unable to map VHPI type %d onto GPI type", vhpitype); + LOG_DEBUG("Unable to map VHPI type %d onto GPI type", vhpitype); return GPI_UNKNOWN; } } @@ -259,7 +259,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, new_obj = new GpiObjHdl(this, new_hdl, gpi_type); break; default: - LOG_WARN("Not able to map type (%s) %u to object", + LOG_DEBUG("Not able to map type (%s) %u to object", vhpi_get_str(vhpiKindStrP, new_hdl), type); return NULL; } diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index fb479f0d..902d79ed 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -483,6 +483,8 @@ VpiIterator::~VpiIterator() vpi_free_object(m_iterator); } +#define VPI_TYPE_MAX (1000) + GpiIterator::Status VpiSingleIterator::next_handle(std::string &name, GpiObjHdl **hdl, void **raw_hdl) @@ -500,13 +502,14 @@ GpiIterator::Status VpiSingleIterator::next_handle(std::string &name, const char *c_name = vpi_get_str(vpiName, obj); if (!c_name) { int type = vpi_get(vpiType, obj); - LOG_WARN("Unable to get the name for this object of type %d", type); - if (type >= 1000) { + if (type >= VPI_TYPE_MAX) { *raw_hdl = (void*)obj; return GpiIterator::NOT_NATIVE_NO_NAME; } + LOG_WARN("Unable to get the name for this object of type %d", type); + return GpiIterator::NATIVE_NO_NAME; } @@ -524,8 +527,6 @@ GpiIterator::Status VpiSingleIterator::next_handle(std::string &name, return GpiIterator::NOT_NATIVE; } -#define VPI_TYPE_MAX (1000) - GpiIterator::Status VpiIterator::next_handle(std::string &name, GpiObjHdl **hdl, void **raw_hdl) { GpiObjHdl *new_obj; @@ -577,13 +578,14 @@ GpiIterator::Status VpiIterator::next_handle(std::string &name, GpiObjHdl **hdl, if (!c_name) { /* This may be another type */ int type = vpi_get(vpiType, obj); - LOG_WARN("Unable to get the name for this object of type %d", type); if (type >= VPI_TYPE_MAX) { *raw_hdl = (void*)obj; return GpiIterator::NOT_NATIVE_NO_NAME; } + LOG_WARN("Unable to get the name for this object of type %d", type); + return GpiIterator::NATIVE_NO_NAME; } name = c_name; diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 3f48aa73..6aefe73b 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -114,6 +114,7 @@ gpi_objtype_t to_gpi_objtype(int32_t vpitype) return GPI_MODULE; default: + LOG_DEBUG("Unable to map VPI type %d onto GPI type", vpitype); return GPI_UNKNOWN; } } @@ -169,7 +170,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, const char *type_name = vpi_get_str(vpiType, new_hdl); std::string unknown = "vpiUnknown"; if (type_name && (unknown != type_name)) { - LOG_WARN("VPI: Not able to map type %s(%d) to object.", type_name, type); + LOG_DEBUG("VPI: Not able to map type %s(%d) to object.", type_name, type); } else { LOG_DEBUG("VPI: Simulator does not know this type (%d) via VPI", type); } @@ -202,7 +203,7 @@ GpiObjHdl* VpiImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { vpi_free_object(new_hdl); - LOG_ERROR("Unable to fetch object %s", fq_name.c_str()); + LOG_DEBUG("Unable to fetch object %s", fq_name.c_str()); return NULL; } return new_obj; @@ -223,7 +224,7 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { vpi_free_object(new_hdl); - LOG_ERROR("Unable to fetch object %s", fq_name.c_str()); + LOG_DEBUG("Unable to fetch object %s", fq_name.c_str()); return NULL; } return new_obj; @@ -246,7 +247,7 @@ GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { vpi_free_object(new_hdl); - LOG_ERROR("Unable to fetch object below entity (%s) at index (%u)", + LOG_DEBUG("Unable to fetch object below entity (%s) at index (%u)", parent->get_name_str(), index); return NULL; } From aec5272e1c7863090a6796fd7737d10b98e5793e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 30 Sep 2015 18:01:05 +0100 Subject: [PATCH 1036/2656] Issue #17: A couple more log changes --- lib/vpi/VpiCbHdl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 902d79ed..c3653a0d 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -508,7 +508,7 @@ GpiIterator::Status VpiSingleIterator::next_handle(std::string &name, return GpiIterator::NOT_NATIVE_NO_NAME; } - LOG_WARN("Unable to get the name for this object of type %d", type); + LOG_DEBUG("Unable to get the name for this object of type %d", type); return GpiIterator::NATIVE_NO_NAME; } @@ -584,7 +584,7 @@ GpiIterator::Status VpiIterator::next_handle(std::string &name, GpiObjHdl **hdl, return GpiIterator::NOT_NATIVE_NO_NAME; } - LOG_WARN("Unable to get the name for this object of type %d", type); + LOG_DEBUG("Unable to get the name for this object of type %d", type); return GpiIterator::NATIVE_NO_NAME; } From 1824fd7545b8aadfb1dfa237848b114f6e56a67b Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Thu, 1 Oct 2015 10:52:19 +1000 Subject: [PATCH 1037/2656] IUS's definition for vhpiValueT still uses the old type for int32_t. Apparently the type of this struct member was changed in VHDL-2008. [commit from :/mnt/mag_drive/projects/justy/work/luke/justy/sim/chipsim/cocotb] --- include/vhpi_user.h | 4 ++++ makefiles/Makefile.rules | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/include/vhpi_user.h b/include/vhpi_user.h index f66cea49..0eefeaf5 100644 --- a/include/vhpi_user.h +++ b/include/vhpi_user.h @@ -166,7 +166,11 @@ extern "C" { [Small]Enum,Logic,Int,Real,[Small]Phys,Time,Ptr, [Small]EnumVec,LogicVec,IntVect,RealVec,[Small]PhysVec,TimeVec, PtrVec,ObjType,RawData]Val */ +#ifndef IUS size_t bufSize; /* the size in bytes of the value buffer; this is set by the user */ +#else + int32_t bufSize; /* IUS defines this as 32-bits, even when running in 64-bit mode */ +#endif int32_t numElems; /* different meanings depending on the format: vhpiStrVal, vhpi{Bin...}StrVal: size of string diff --git a/makefiles/Makefile.rules b/makefiles/Makefile.rules index 039aa590..46ce6cd4 100644 --- a/makefiles/Makefile.rules +++ b/makefiles/Makefile.rules @@ -37,6 +37,11 @@ $(LIB_NAME)_OBJS:= $(patsubst %.c,$(LIB_OBJ_DIR)/%.o,$(filter %.c,$(SRCS))) $(LIB_NAME)_OBJS+= $(patsubst %.cpp,$(LIB_OBJ_DIR)/%.o,$(filter %.cpp,$(SRCS))) +ifeq ($(SIM),ius) +GCC_ARGS+= -DIUS +GXX_ARGS+= -DIUS +endif + $(LIB_OBJ_DIR)/%.o: %.c gcc $(GCC_ARGS) -c $(INCLUDES) -o $@ $< From 46d1db4cee1a798aa9dfb702b759e58a6392fd1b Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 1 Oct 2015 12:25:55 +0100 Subject: [PATCH 1038/2656] Issue #17: Add a specific testcase for iteration over function calls --- .../designs/sample_module/sample_module.vhdl | 15 +++++++- .../test_discovery/test_discovery.py | 35 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/tests/designs/sample_module/sample_module.vhdl b/tests/designs/sample_module/sample_module.vhdl index 5e48e4f4..bc0dd57b 100644 --- a/tests/designs/sample_module/sample_module.vhdl +++ b/tests/designs/sample_module/sample_module.vhdl @@ -38,6 +38,7 @@ entity sample_module is stream_in_data : in std_ulogic_vector(7 downto 0); stream_in_data_wide : in std_ulogic_vector(63 downto 0); stream_in_valid : in std_ulogic; + stream_in_func_en : in std_ulogic; stream_in_ready : out std_ulogic; stream_in_real : in real; stream_in_int : in integer; @@ -52,6 +53,18 @@ end; architecture impl of sample_module is +function afunc(value : std_ulogic_vector) return std_ulogic_vector is + variable i: integer; + variable rv: std_ulogic_vector(7 downto 0); +begin + i := 0; + while i <= 7 loop + rv(i) := value(7-i); + i := i + 1; + end loop; + return rv; +end afunc; + begin process (clk) begin @@ -60,7 +73,7 @@ process (clk) begin end if; end process; -stream_out_data_comb <= stream_in_data; +stream_out_data_comb <= afunc(stream_in_data) when stream_in_func_en = '0' else stream_in_data; stream_in_ready <= stream_out_ready; stream_out_real <= stream_in_real; stream_out_int <= stream_in_int; diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 7ba06fa6..94df3625 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -26,10 +26,22 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' import cocotb +import logging from cocotb.triggers import Timer from cocotb.result import TestError, TestFailure +from cocotb.handle import IntegerObject +@cocotb.test() +def recursive_discover(dut): + """Discover absolutely everything in the dut""" + yield Timer(0) + def _discover(obj): + for thing in obj: + dut._log.info("Found %s (%s)", thing._name, type(thing)) + _discover(thing) + _discover(dut) + @cocotb.test() def discover_module_values(dut): """Discover everything in the dut""" @@ -118,6 +130,29 @@ def access_single_bit_erroneous(dut): dut.stream_in_data[bit] <= 1 yield Timer(10) +@cocotb.test() +def access_integer(dut): + """Integer should show as an IntegerObject""" + bitfail = False + tlog = logging.getLogger("cocotb.test") + yield Timer(10) + test_int = dut.stream_in_int + if not isinstance(test_int, IntegerObject): + raise TestFailure("dut.stream_in_int is not an integer") + + try: + bit = test_int[3] + except TestError as e: + tlog.info("Access to bit is an error as expected") + bitFail = True + + if not bitFail: + raise TestFailure("Access into an integer should be invalid") + + length = len(test_int) + if length is not 1: + raise TestFailure("Length should be 1 not %d" % length) + @cocotb.test(skip=True) def skip_a_test(dut): From 6cea193f419e41b82af9c14aeed3af6f5ee8b3c0 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 1 Oct 2015 12:26:48 +0100 Subject: [PATCH 1039/2656] Issue #17: Override m_num_elems for integers, length was incorrect and showing errors on iteration due to this --- lib/vpi/VpiCbHdl.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index c3653a0d..2f5ff35a 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -120,7 +120,14 @@ int VpiCbHdl::cleanup_callback(void) } int VpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { - m_num_elems = vpi_get(vpiSize, GpiObjHdl::get_handle()); + int32_t type = vpi_get(vpiType, GpiObjHdl::get_handle()); + if ((vpiIntVar == type) || + (vpiIntegerVar == type) || + (vpiIntegerNet == type )) { + m_num_elems = 1; + } else { + m_num_elems = vpi_get(vpiSize, GpiObjHdl::get_handle()); + } LOG_DEBUG("VPI: %s initialised with %d elements", name.c_str(), m_num_elems); return GpiObjHdl::initialise(name, fq_name); } From abaeb0045e5dc74e4332b02edcb69704795e7a0f Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 1 Oct 2015 18:06:03 +0100 Subject: [PATCH 1040/2656] Issue #17: Fixup invalid pointer returned if .driver/load called on a vhpi handle --- lib/vhpi/VhpiImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 7c9556b4..d3c4ad7c 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -463,7 +463,7 @@ GpiObjHdl *VhpiImpl::get_root_handle(const char* name) GpiIterator *VhpiImpl::iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) { - GpiIterator *new_iter; + GpiIterator *new_iter = NULL; switch (type) { case GPI_OBJECTS: From 3a581621b23598a31145b9e7dce8201a1d11d288 Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Tue, 6 Oct 2015 15:45:24 +1100 Subject: [PATCH 1041/2656] testcase for vhpiIndexedNameK error. --- include/vhpi_user.h | 4 +++ makefiles/Makefile.rules | 5 ++++ makefiles/simulators/Makefile.ius | 2 +- tests/designs/sample_module/Makefile | 2 +- .../designs/sample_module/sample_module.vhdl | 27 ++++++++++++++++++ .../sample_module/sample_module_1.vhdl | 28 +++++++++++++++++++ 6 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 tests/designs/sample_module/sample_module_1.vhdl diff --git a/include/vhpi_user.h b/include/vhpi_user.h index f66cea49..0eefeaf5 100644 --- a/include/vhpi_user.h +++ b/include/vhpi_user.h @@ -166,7 +166,11 @@ extern "C" { [Small]Enum,Logic,Int,Real,[Small]Phys,Time,Ptr, [Small]EnumVec,LogicVec,IntVect,RealVec,[Small]PhysVec,TimeVec, PtrVec,ObjType,RawData]Val */ +#ifndef IUS size_t bufSize; /* the size in bytes of the value buffer; this is set by the user */ +#else + int32_t bufSize; /* IUS defines this as 32-bits, even when running in 64-bit mode */ +#endif int32_t numElems; /* different meanings depending on the format: vhpiStrVal, vhpi{Bin...}StrVal: size of string diff --git a/makefiles/Makefile.rules b/makefiles/Makefile.rules index 039aa590..46ce6cd4 100644 --- a/makefiles/Makefile.rules +++ b/makefiles/Makefile.rules @@ -37,6 +37,11 @@ $(LIB_NAME)_OBJS:= $(patsubst %.c,$(LIB_OBJ_DIR)/%.o,$(filter %.c,$(SRCS))) $(LIB_NAME)_OBJS+= $(patsubst %.cpp,$(LIB_OBJ_DIR)/%.o,$(filter %.cpp,$(SRCS))) +ifeq ($(SIM),ius) +GCC_ARGS+= -DIUS +GXX_ARGS+= -DIUS +endif + $(LIB_OBJ_DIR)/%.o: %.c gcc $(GCC_ARGS) -c $(INCLUDES) -o $@ $< diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index d9c82cb3..659e0513 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -69,7 +69,7 @@ GPI_LIB = $(COCOTB_VPI_LIB) $(COCOTB_VHPI_LIB) results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(GPI_LIB) LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - irun $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) + irun -64 $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) clean:: @rm -rf $(SIM_BUILD) diff --git a/tests/designs/sample_module/Makefile b/tests/designs/sample_module/Makefile index 18e67f95..c1eca3e9 100644 --- a/tests/designs/sample_module/Makefile +++ b/tests/designs/sample_module/Makefile @@ -41,7 +41,7 @@ WPWD=$(shell pwd) endif ifeq ($(TOPLEVEL_LANG),vhdl) - VHDL_SOURCES = $(COCOTB)/tests/designs/sample_module/sample_module.vhdl + VHDL_SOURCES = $(COCOTB)/tests/designs/sample_module/sample_module_1.vhdl $(COCOTB)/tests/designs/sample_module/sample_module.vhdl TOPLEVEL := $(TOPLEVEL) GPI_IMPL := vhpi else diff --git a/tests/designs/sample_module/sample_module.vhdl b/tests/designs/sample_module/sample_module.vhdl index bc0dd57b..7faec0ca 100644 --- a/tests/designs/sample_module/sample_module.vhdl +++ b/tests/designs/sample_module/sample_module.vhdl @@ -26,6 +26,9 @@ -- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------- + + + library ieee; use ieee.std_logic_1164.all; @@ -53,6 +56,18 @@ end; architecture impl of sample_module is + component sample_module_1 is + generic ( + EXAMPLE_WIDTH : integer + ); + port ( + clk : in std_ulogic; + stream_in_data : in std_ulogic_vector(EXAMPLE_WIDTH downto 0); + stream_out_data_registered : buffer std_ulogic_vector(EXAMPLE_WIDTH downto 0); + stream_out_data_valid : out std_ulogic + ); +end component sample_module_1; + function afunc(value : std_ulogic_vector) return std_ulogic_vector is variable i: integer; variable rv: std_ulogic_vector(7 downto 0); @@ -78,4 +93,16 @@ stream_in_ready <= stream_out_ready; stream_out_real <= stream_in_real; stream_out_int <= stream_in_int; +isample_module1 : component sample_module_1 + generic map ( + EXAMPLE_WIDTH => 7 + ) + port map ( + clk => clk, + stream_in_data => stream_in_data, + stream_out_data_registered => open, + stream_out_data_valid => open + ); + + end architecture; diff --git a/tests/designs/sample_module/sample_module_1.vhdl b/tests/designs/sample_module/sample_module_1.vhdl new file mode 100644 index 00000000..1bbece4f --- /dev/null +++ b/tests/designs/sample_module/sample_module_1.vhdl @@ -0,0 +1,28 @@ +library ieee; + +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity sample_module_1 is + generic ( + EXAMPLE_WIDTH : integer + ); + port ( + clk : in std_ulogic; + stream_in_data : in std_ulogic_vector(EXAMPLE_WIDTH downto 0); + stream_out_data_registered : buffer std_ulogic_vector(EXAMPLE_WIDTH downto 0); + stream_out_data_valid : out std_ulogic + ); +end; + +architecture impl of sample_module_1 is +begin + process (clk) begin + if rising_edge(clk) then + stream_out_data_registered <= stream_in_data; + end if; + end process; + + stream_out_data_valid <= '1' when (stream_out_data_registered(EXAMPLE_WIDTH) = '1') else '0'; + +end architecture; From bc88831c32eab2f58e1f453df43bd194b1503d75 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 6 Oct 2015 08:26:45 +0100 Subject: [PATCH 1042/2656] Issue #17: Do not query handles again that we already know are not valid --- cocotb/handle.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index c2293f2a..036fae60 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -80,6 +80,7 @@ def __init__(self, handle): self._handle = handle self._len = None self._sub_handles = {} # Dictionary of children + self._invalid_sub_handles = {} # Dictionary of invalid queries self._discovered = False self._name = simulator.get_name_string(self._handle) @@ -147,15 +148,15 @@ def __getattr__(self, name): if name in self._sub_handles: return self._sub_handles[name] + if name in self._compat_mapping: + if name not in _deprecation_warned: + warnings.warn("Use of %s attribute is deprecated" % name) + _deprecation_warned[name] = True + return getattr(self, self._compat_mapping[name]) + new_handle = simulator.get_handle_by_name(self._handle, name) if not new_handle: - if name in self._compat_mapping: - if name not in _deprecation_warned: - warnings.warn("Use of %s attribute is deprecated" % name) - _deprecation_warned[name] = True - return getattr(self, self._compat_mapping[name]) - # To find generated indices we have to discover all self._discover_all() if name in self._sub_handles: @@ -250,9 +251,15 @@ def __hasattr__(self, name): """ if name in self._sub_handles: return self._sub_handles[name] + + if name in self._invalid_sub_handles: + return None + new_handle = simulator.get_handle_by_name(self._handle, name) if new_handle: self._sub_handles[name] = SimHandle(new_handle) + else: + self._invalid_sub_handles[name] = None return new_handle From c130d91d28cfdcb089bbbf893f7407e93b9e4d1e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 6 Oct 2015 12:04:42 +0100 Subject: [PATCH 1043/2656] Issue #17: Handle failure of a simulator to provide the format of an object --- lib/vhpi/VhpiImpl.cpp | 99 ++++++++++++------- tests/test_cases/test_discovery/Makefile | 4 + .../test_discovery/test_vhdl_indexed_name.py | 50 ++++++++++ .../test_iteration.py | 10 +- .../test_iteration_vhdl/test_iteration.py | 13 ++- 5 files changed, 131 insertions(+), 45 deletions(-) create mode 100644 tests/test_cases/test_discovery/test_vhdl_indexed_name.py diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index d3c4ad7c..ca7b471c 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -173,6 +173,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, vhpiIntT type; gpi_objtype_t gpi_type; GpiObjHdl *new_obj = NULL; + bool modifiable = true; if (vhpiVerilog == (type = vhpi_get(vhpiKindP, new_hdl))) { LOG_DEBUG("vhpiVerilog returned from vhpi_get(vhpiType, ...)") @@ -204,59 +205,78 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, value.numElems = 0; value.value.str = NULL; + /* It is possible that the simulator will fail the next call. + IUS Cannot use method vhpi_get_value on non-locally-static + object of kind vhpiIndexedNameK for example. To detect this + we check that the format has changed from vhpiObjTypeVal. If + not then we create a simple Obj handle + */ + vhpi_get_value(new_hdl, &value); - if (vhpiRealVal == value.format) { - LOG_DEBUG("Detected a REAL type %s", fq_name.c_str()); - gpi_type = GPI_REAL; - } + switch (value.format) { + case vhpiObjTypeVal: { + LOG_DEBUG("Forcing %s to GpiObjHdl as format unavailable", fq_name.c_str()); + modifiable = false; + break; + } - if (vhpiIntVal == value.format) { - LOG_DEBUG("Detected an INT type %s", fq_name.c_str()); - gpi_type = GPI_INTEGER; - } + case vhpiRealVal: { + LOG_DEBUG("Detected a REAL type %s", fq_name.c_str()); + gpi_type = GPI_REAL; + break; + } - if (vhpiRawDataVal == value.format) { - LOG_DEBUG("Detected a custom array type %s", fq_name.c_str()); - gpi_type = GPI_MODULE; - } + case vhpiIntVal: { + LOG_DEBUG("Detected an INT type %s", fq_name.c_str()); + gpi_type = GPI_INTEGER; + break; + } - if (vhpiIntVecVal == value.format || - vhpiRealVecVal == value.format || - vhpiEnumVecVal == value.format || - vhpiLogicVecVal == value.format || - vhpiPhysVecVal == value.format || - vhpiTimeVecVal == value.format) { - /* This may well be an n dimensional vector if it is - then we create a non signal object - */ - int num_elems = vhpi_get(vhpiSizeP, new_hdl); - - /* More differences between simulators. - Aldec sets value.numElems regardless - IUS does not set for a single dimension. - */ - - if (!value.numElems || (value.numElems == num_elems)) { - LOG_DEBUG("Detected single dimension vector type", fq_name.c_str()); - gpi_type = GPI_ARRAY; - } else { - LOG_DEBUG("Detected an n dimension vector type", fq_name.c_str()); + case vhpiRawDataVal: { + LOG_DEBUG("Detected a custom array type %s", fq_name.c_str()); gpi_type = GPI_MODULE; - new_obj = new GpiObjHdl(this, new_hdl, gpi_type); break; } - } - new_obj = new VhpiSignalObjHdl(this, new_hdl, gpi_type, is_const(type)); + case vhpiIntVecVal: + case vhpiRealVecVal: + case vhpiEnumVecVal: + case vhpiLogicVecVal: + case vhpiPhysVecVal: + case vhpiTimeVecVal: { + /* This may well be an n dimensional vector if it is + then we create a non signal object + */ + int num_elems = vhpi_get(vhpiSizeP, new_hdl); + + /* More differences between simulators. + Aldec sets value.numElems regardless + IUS does not set for a single dimension. + */ + + if (!value.numElems || (value.numElems == num_elems)) { + LOG_DEBUG("Detected single dimension vector type", fq_name.c_str()); + gpi_type = GPI_ARRAY; + } else { + LOG_DEBUG("Detected an n dimension vector type", fq_name.c_str()); + gpi_type = GPI_MODULE; + modifiable = false; + break; + } + } + default: + break; + } break; } + case vhpiForGenerateK: case vhpiIfGenerateK: case vhpiCompInstStmtK: case vhpiProcessStmtK: case vhpiSimpleSigAssignStmtK: case vhpiCondSigAssignStmtK: - new_obj = new GpiObjHdl(this, new_hdl, gpi_type); + modifiable = false; break; default: LOG_DEBUG("Not able to map type (%s) %u to object", @@ -264,6 +284,11 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, return NULL; } + if (modifiable) + new_obj = new VhpiSignalObjHdl(this, new_hdl, gpi_type, is_const(type)); + else + new_obj = new GpiObjHdl(this, new_hdl, gpi_type); + if (new_obj->initialise(name, fq_name)) { delete new_obj; new_obj = NULL; diff --git a/tests/test_cases/test_discovery/Makefile b/tests/test_cases/test_discovery/Makefile index fb054750..e43e3ba7 100644 --- a/tests/test_cases/test_discovery/Makefile +++ b/tests/test_cases/test_discovery/Makefile @@ -28,4 +28,8 @@ include ../../designs/sample_module/Makefile +ifeq ($(TOPLEVEL_LANG), vhdl) +MODULE = test_discovery,test_vhdl_indexed_name +else MODULE = test_discovery +endif diff --git a/tests/test_cases/test_discovery/test_vhdl_indexed_name.py b/tests/test_cases/test_discovery/test_vhdl_indexed_name.py new file mode 100644 index 00000000..115ce3b1 --- /dev/null +++ b/tests/test_cases/test_discovery/test_vhdl_indexed_name.py @@ -0,0 +1,50 @@ +''' Copyright (c) 2015 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd, + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +import cocotb +import logging +from cocotb.triggers import Timer +from cocotb.result import TestError, TestFailure +from cocotb.handle import IntegerObject + + +@cocotb.test() +def index_name_iter(dut): + """Access a non local indexed name""" + yield Timer(0) + total_count = 0 + def _discover(obj): + count = 0 + for thing in obj: + count += 1 + dut._log.info("Found %s (%s)", thing._name, type(thing)) + count += _discover(thing) + return count + total_count = _discover(dut.isample_module1) + + dut._log.info("Number of objects in non local vhpiIndexedNameK is %d" % total_count) + + dut._log.info("Value of EXAMPLE_WIDTH is %d" % dut.isample_module1.EXAMPLE_WIDTH) + diff --git a/tests/test_cases/test_iteration_mixedlang/test_iteration.py b/tests/test_cases/test_iteration_mixedlang/test_iteration.py index 446b3dcc..7c42a35f 100644 --- a/tests/test_cases/test_iteration_mixedlang/test_iteration.py +++ b/tests/test_cases/test_iteration_mixedlang/test_iteration.py @@ -69,7 +69,8 @@ def recursive_discovery(dut): "ncsim"]: # vpiAlways = 31 and vpiStructVar = 2 do not show up in IUS # But vhpiSimpleSigAssignStmtK objects do, and ther are 2. - pass_total = 883 + # Process statements and all sub handles also show up. + pass_total = 959 else: pass_total = 916 @@ -91,15 +92,12 @@ def recursive_discovery(dut): @cocotb.test() def recursive_discovery_boundary(dut): """ - Currently we can't traverse a language boundary during iteration - - However if we manually delve through the language boundary we - should then be able to iterate to discover objects + Iteration though the boundary works but this just double checks """ if cocotb.SIM_NAME in ["ncsim(64)", "ncsim"]: # # But vhpiSimpleSigAssignStmtK objects only show up on IUS, and ther are 2 - pass_total = 428 + pass_total = 504 else: pass_total = 426 diff --git a/tests/test_cases/test_iteration_vhdl/test_iteration.py b/tests/test_cases/test_iteration_vhdl/test_iteration.py index f6ffc10f..83cbf6a0 100644 --- a/tests/test_cases/test_iteration_vhdl/test_iteration.py +++ b/tests/test_cases/test_iteration_vhdl/test_iteration.py @@ -34,6 +34,15 @@ def recursive_discovery(dut): """ Recursively discover every single object in the design """ + if cocotb.SIM_NAME in ["ncsim(64)", + "ncsim"]: + # vpiAlways = 31 and vpiStructVar = 2 do not show up in IUS + # But vhpiSimpleSigAssignStmtK objects do, and ther are 2. + # Process statements and all sub handles also show up. + pass_total = 35841 + else: + pass_total = 32306 + tlog = logging.getLogger("cocotb.test") yield Timer(100) def dump_all_the_things(parent): @@ -45,8 +54,8 @@ def dump_all_the_things(parent): return count total = dump_all_the_things(dut) tlog.info("Found a total of %d things", total) - if total != 32306: - raise TestFailure("Expected 32306 objects but found %d" % total) + if total != pass_total: + raise TestFailure("Expected %d objects but found %d" % (pass_total, total)) @cocotb.test() From c335814bd7d3c46e94b9884c54a391615a586698 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 6 Oct 2015 12:16:35 +0100 Subject: [PATCH 1044/2656] Issue #17: Re-enable 64 bit in Makefile --- makefiles/simulators/Makefile.ius | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 659e0513..897cf0ab 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -29,12 +29,8 @@ # Common Makefile for Cadence Incisive -# Disable 64 bit. There are too many issues with access to values on IUS. -# hope to enable again when these have been resolved. - -ARCH?=i686 -ifeq ($(shell ncbits),64) -$(error "The 64bit version of IUS is on your PATH, please use the 32bit version for now") +ifneq ($(ARCH),i686) +EXTRA_ARGS += -64 endif EXTRA_ARGS += -nclibdirname $(SIM_BUILD) -plinowarn @@ -69,7 +65,7 @@ GPI_LIB = $(COCOTB_VPI_LIB) $(COCOTB_VHPI_LIB) results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(GPI_LIB) LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - irun -64 $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) + irun $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) clean:: @rm -rf $(SIM_BUILD) From 8421ae4a1935c0f3c52fed0ae184a25035b925ba Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 6 Oct 2015 21:31:05 +0100 Subject: [PATCH 1045/2656] Issue #17: Expect error as int does not work on icarus --- tests/test_cases/test_discovery/test_discovery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 94df3625..531b8134 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -130,7 +130,7 @@ def access_single_bit_erroneous(dut): dut.stream_in_data[bit] <= 1 yield Timer(10) -@cocotb.test() +@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) def access_integer(dut): """Integer should show as an IntegerObject""" bitfail = False From 4b7b5a885ab6b5d28a12b185876824467de96696 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 1 Oct 2015 12:25:55 +0100 Subject: [PATCH 1046/2656] Issue #17: Add a specific testcase for iteration over function calls --- .../designs/sample_module/sample_module.vhdl | 15 +++++++- .../test_discovery/test_discovery.py | 35 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/tests/designs/sample_module/sample_module.vhdl b/tests/designs/sample_module/sample_module.vhdl index 5e48e4f4..bc0dd57b 100644 --- a/tests/designs/sample_module/sample_module.vhdl +++ b/tests/designs/sample_module/sample_module.vhdl @@ -38,6 +38,7 @@ entity sample_module is stream_in_data : in std_ulogic_vector(7 downto 0); stream_in_data_wide : in std_ulogic_vector(63 downto 0); stream_in_valid : in std_ulogic; + stream_in_func_en : in std_ulogic; stream_in_ready : out std_ulogic; stream_in_real : in real; stream_in_int : in integer; @@ -52,6 +53,18 @@ end; architecture impl of sample_module is +function afunc(value : std_ulogic_vector) return std_ulogic_vector is + variable i: integer; + variable rv: std_ulogic_vector(7 downto 0); +begin + i := 0; + while i <= 7 loop + rv(i) := value(7-i); + i := i + 1; + end loop; + return rv; +end afunc; + begin process (clk) begin @@ -60,7 +73,7 @@ process (clk) begin end if; end process; -stream_out_data_comb <= stream_in_data; +stream_out_data_comb <= afunc(stream_in_data) when stream_in_func_en = '0' else stream_in_data; stream_in_ready <= stream_out_ready; stream_out_real <= stream_in_real; stream_out_int <= stream_in_int; diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 7ba06fa6..94df3625 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -26,10 +26,22 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' import cocotb +import logging from cocotb.triggers import Timer from cocotb.result import TestError, TestFailure +from cocotb.handle import IntegerObject +@cocotb.test() +def recursive_discover(dut): + """Discover absolutely everything in the dut""" + yield Timer(0) + def _discover(obj): + for thing in obj: + dut._log.info("Found %s (%s)", thing._name, type(thing)) + _discover(thing) + _discover(dut) + @cocotb.test() def discover_module_values(dut): """Discover everything in the dut""" @@ -118,6 +130,29 @@ def access_single_bit_erroneous(dut): dut.stream_in_data[bit] <= 1 yield Timer(10) +@cocotb.test() +def access_integer(dut): + """Integer should show as an IntegerObject""" + bitfail = False + tlog = logging.getLogger("cocotb.test") + yield Timer(10) + test_int = dut.stream_in_int + if not isinstance(test_int, IntegerObject): + raise TestFailure("dut.stream_in_int is not an integer") + + try: + bit = test_int[3] + except TestError as e: + tlog.info("Access to bit is an error as expected") + bitFail = True + + if not bitFail: + raise TestFailure("Access into an integer should be invalid") + + length = len(test_int) + if length is not 1: + raise TestFailure("Length should be 1 not %d" % length) + @cocotb.test(skip=True) def skip_a_test(dut): From 02ba252546f3e40cba70e6027d1a462b842ed523 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 1 Oct 2015 12:26:48 +0100 Subject: [PATCH 1047/2656] Issue #17: Override m_num_elems for integers, length was incorrect and showing errors on iteration due to this --- lib/vpi/VpiCbHdl.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index c3653a0d..2f5ff35a 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -120,7 +120,14 @@ int VpiCbHdl::cleanup_callback(void) } int VpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { - m_num_elems = vpi_get(vpiSize, GpiObjHdl::get_handle()); + int32_t type = vpi_get(vpiType, GpiObjHdl::get_handle()); + if ((vpiIntVar == type) || + (vpiIntegerVar == type) || + (vpiIntegerNet == type )) { + m_num_elems = 1; + } else { + m_num_elems = vpi_get(vpiSize, GpiObjHdl::get_handle()); + } LOG_DEBUG("VPI: %s initialised with %d elements", name.c_str(), m_num_elems); return GpiObjHdl::initialise(name, fq_name); } From 887848fc9e10e3ad2c15e213adb1427553dcd144 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 1 Oct 2015 18:06:03 +0100 Subject: [PATCH 1048/2656] Issue #17: Fixup invalid pointer returned if .driver/load called on a vhpi handle --- lib/vhpi/VhpiImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 7c9556b4..d3c4ad7c 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -463,7 +463,7 @@ GpiObjHdl *VhpiImpl::get_root_handle(const char* name) GpiIterator *VhpiImpl::iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) { - GpiIterator *new_iter; + GpiIterator *new_iter = NULL; switch (type) { case GPI_OBJECTS: From 5a57282f3e4c372301ccf5aebb4bbf09ec7caf5d Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 6 Oct 2015 08:26:45 +0100 Subject: [PATCH 1049/2656] Issue #17: Do not query handles again that we already know are not valid --- cocotb/handle.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index c2293f2a..036fae60 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -80,6 +80,7 @@ def __init__(self, handle): self._handle = handle self._len = None self._sub_handles = {} # Dictionary of children + self._invalid_sub_handles = {} # Dictionary of invalid queries self._discovered = False self._name = simulator.get_name_string(self._handle) @@ -147,15 +148,15 @@ def __getattr__(self, name): if name in self._sub_handles: return self._sub_handles[name] + if name in self._compat_mapping: + if name not in _deprecation_warned: + warnings.warn("Use of %s attribute is deprecated" % name) + _deprecation_warned[name] = True + return getattr(self, self._compat_mapping[name]) + new_handle = simulator.get_handle_by_name(self._handle, name) if not new_handle: - if name in self._compat_mapping: - if name not in _deprecation_warned: - warnings.warn("Use of %s attribute is deprecated" % name) - _deprecation_warned[name] = True - return getattr(self, self._compat_mapping[name]) - # To find generated indices we have to discover all self._discover_all() if name in self._sub_handles: @@ -250,9 +251,15 @@ def __hasattr__(self, name): """ if name in self._sub_handles: return self._sub_handles[name] + + if name in self._invalid_sub_handles: + return None + new_handle = simulator.get_handle_by_name(self._handle, name) if new_handle: self._sub_handles[name] = SimHandle(new_handle) + else: + self._invalid_sub_handles[name] = None return new_handle From 810bee11f1595c1c0abb0fe641c6d2307a08298e Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Tue, 6 Oct 2015 15:45:24 +1100 Subject: [PATCH 1050/2656] testcase for vhpiIndexedNameK error. --- makefiles/simulators/Makefile.ius | 2 +- tests/designs/sample_module/Makefile | 2 +- .../designs/sample_module/sample_module.vhdl | 27 ++++++++++++++++++ .../sample_module/sample_module_1.vhdl | 28 +++++++++++++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 tests/designs/sample_module/sample_module_1.vhdl diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index d9c82cb3..659e0513 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -69,7 +69,7 @@ GPI_LIB = $(COCOTB_VPI_LIB) $(COCOTB_VHPI_LIB) results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(GPI_LIB) LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - irun $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) + irun -64 $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) clean:: @rm -rf $(SIM_BUILD) diff --git a/tests/designs/sample_module/Makefile b/tests/designs/sample_module/Makefile index 18e67f95..c1eca3e9 100644 --- a/tests/designs/sample_module/Makefile +++ b/tests/designs/sample_module/Makefile @@ -41,7 +41,7 @@ WPWD=$(shell pwd) endif ifeq ($(TOPLEVEL_LANG),vhdl) - VHDL_SOURCES = $(COCOTB)/tests/designs/sample_module/sample_module.vhdl + VHDL_SOURCES = $(COCOTB)/tests/designs/sample_module/sample_module_1.vhdl $(COCOTB)/tests/designs/sample_module/sample_module.vhdl TOPLEVEL := $(TOPLEVEL) GPI_IMPL := vhpi else diff --git a/tests/designs/sample_module/sample_module.vhdl b/tests/designs/sample_module/sample_module.vhdl index bc0dd57b..7faec0ca 100644 --- a/tests/designs/sample_module/sample_module.vhdl +++ b/tests/designs/sample_module/sample_module.vhdl @@ -26,6 +26,9 @@ -- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------- + + + library ieee; use ieee.std_logic_1164.all; @@ -53,6 +56,18 @@ end; architecture impl of sample_module is + component sample_module_1 is + generic ( + EXAMPLE_WIDTH : integer + ); + port ( + clk : in std_ulogic; + stream_in_data : in std_ulogic_vector(EXAMPLE_WIDTH downto 0); + stream_out_data_registered : buffer std_ulogic_vector(EXAMPLE_WIDTH downto 0); + stream_out_data_valid : out std_ulogic + ); +end component sample_module_1; + function afunc(value : std_ulogic_vector) return std_ulogic_vector is variable i: integer; variable rv: std_ulogic_vector(7 downto 0); @@ -78,4 +93,16 @@ stream_in_ready <= stream_out_ready; stream_out_real <= stream_in_real; stream_out_int <= stream_in_int; +isample_module1 : component sample_module_1 + generic map ( + EXAMPLE_WIDTH => 7 + ) + port map ( + clk => clk, + stream_in_data => stream_in_data, + stream_out_data_registered => open, + stream_out_data_valid => open + ); + + end architecture; diff --git a/tests/designs/sample_module/sample_module_1.vhdl b/tests/designs/sample_module/sample_module_1.vhdl new file mode 100644 index 00000000..1bbece4f --- /dev/null +++ b/tests/designs/sample_module/sample_module_1.vhdl @@ -0,0 +1,28 @@ +library ieee; + +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity sample_module_1 is + generic ( + EXAMPLE_WIDTH : integer + ); + port ( + clk : in std_ulogic; + stream_in_data : in std_ulogic_vector(EXAMPLE_WIDTH downto 0); + stream_out_data_registered : buffer std_ulogic_vector(EXAMPLE_WIDTH downto 0); + stream_out_data_valid : out std_ulogic + ); +end; + +architecture impl of sample_module_1 is +begin + process (clk) begin + if rising_edge(clk) then + stream_out_data_registered <= stream_in_data; + end if; + end process; + + stream_out_data_valid <= '1' when (stream_out_data_registered(EXAMPLE_WIDTH) = '1') else '0'; + +end architecture; From d48028e77f1a79fd66bc5c2c0f5b428d080ed90e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 6 Oct 2015 12:04:42 +0100 Subject: [PATCH 1051/2656] Issue #17: Handle failure of a simulator to provide the format of an object --- lib/vhpi/VhpiImpl.cpp | 99 ++++++++++++------- tests/test_cases/test_discovery/Makefile | 4 + .../test_discovery/test_vhdl_indexed_name.py | 50 ++++++++++ .../test_iteration.py | 10 +- .../test_iteration_vhdl/test_iteration.py | 13 ++- 5 files changed, 131 insertions(+), 45 deletions(-) create mode 100644 tests/test_cases/test_discovery/test_vhdl_indexed_name.py diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index d3c4ad7c..ca7b471c 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -173,6 +173,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, vhpiIntT type; gpi_objtype_t gpi_type; GpiObjHdl *new_obj = NULL; + bool modifiable = true; if (vhpiVerilog == (type = vhpi_get(vhpiKindP, new_hdl))) { LOG_DEBUG("vhpiVerilog returned from vhpi_get(vhpiType, ...)") @@ -204,59 +205,78 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, value.numElems = 0; value.value.str = NULL; + /* It is possible that the simulator will fail the next call. + IUS Cannot use method vhpi_get_value on non-locally-static + object of kind vhpiIndexedNameK for example. To detect this + we check that the format has changed from vhpiObjTypeVal. If + not then we create a simple Obj handle + */ + vhpi_get_value(new_hdl, &value); - if (vhpiRealVal == value.format) { - LOG_DEBUG("Detected a REAL type %s", fq_name.c_str()); - gpi_type = GPI_REAL; - } + switch (value.format) { + case vhpiObjTypeVal: { + LOG_DEBUG("Forcing %s to GpiObjHdl as format unavailable", fq_name.c_str()); + modifiable = false; + break; + } - if (vhpiIntVal == value.format) { - LOG_DEBUG("Detected an INT type %s", fq_name.c_str()); - gpi_type = GPI_INTEGER; - } + case vhpiRealVal: { + LOG_DEBUG("Detected a REAL type %s", fq_name.c_str()); + gpi_type = GPI_REAL; + break; + } - if (vhpiRawDataVal == value.format) { - LOG_DEBUG("Detected a custom array type %s", fq_name.c_str()); - gpi_type = GPI_MODULE; - } + case vhpiIntVal: { + LOG_DEBUG("Detected an INT type %s", fq_name.c_str()); + gpi_type = GPI_INTEGER; + break; + } - if (vhpiIntVecVal == value.format || - vhpiRealVecVal == value.format || - vhpiEnumVecVal == value.format || - vhpiLogicVecVal == value.format || - vhpiPhysVecVal == value.format || - vhpiTimeVecVal == value.format) { - /* This may well be an n dimensional vector if it is - then we create a non signal object - */ - int num_elems = vhpi_get(vhpiSizeP, new_hdl); - - /* More differences between simulators. - Aldec sets value.numElems regardless - IUS does not set for a single dimension. - */ - - if (!value.numElems || (value.numElems == num_elems)) { - LOG_DEBUG("Detected single dimension vector type", fq_name.c_str()); - gpi_type = GPI_ARRAY; - } else { - LOG_DEBUG("Detected an n dimension vector type", fq_name.c_str()); + case vhpiRawDataVal: { + LOG_DEBUG("Detected a custom array type %s", fq_name.c_str()); gpi_type = GPI_MODULE; - new_obj = new GpiObjHdl(this, new_hdl, gpi_type); break; } - } - new_obj = new VhpiSignalObjHdl(this, new_hdl, gpi_type, is_const(type)); + case vhpiIntVecVal: + case vhpiRealVecVal: + case vhpiEnumVecVal: + case vhpiLogicVecVal: + case vhpiPhysVecVal: + case vhpiTimeVecVal: { + /* This may well be an n dimensional vector if it is + then we create a non signal object + */ + int num_elems = vhpi_get(vhpiSizeP, new_hdl); + + /* More differences between simulators. + Aldec sets value.numElems regardless + IUS does not set for a single dimension. + */ + + if (!value.numElems || (value.numElems == num_elems)) { + LOG_DEBUG("Detected single dimension vector type", fq_name.c_str()); + gpi_type = GPI_ARRAY; + } else { + LOG_DEBUG("Detected an n dimension vector type", fq_name.c_str()); + gpi_type = GPI_MODULE; + modifiable = false; + break; + } + } + default: + break; + } break; } + case vhpiForGenerateK: case vhpiIfGenerateK: case vhpiCompInstStmtK: case vhpiProcessStmtK: case vhpiSimpleSigAssignStmtK: case vhpiCondSigAssignStmtK: - new_obj = new GpiObjHdl(this, new_hdl, gpi_type); + modifiable = false; break; default: LOG_DEBUG("Not able to map type (%s) %u to object", @@ -264,6 +284,11 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, return NULL; } + if (modifiable) + new_obj = new VhpiSignalObjHdl(this, new_hdl, gpi_type, is_const(type)); + else + new_obj = new GpiObjHdl(this, new_hdl, gpi_type); + if (new_obj->initialise(name, fq_name)) { delete new_obj; new_obj = NULL; diff --git a/tests/test_cases/test_discovery/Makefile b/tests/test_cases/test_discovery/Makefile index fb054750..e43e3ba7 100644 --- a/tests/test_cases/test_discovery/Makefile +++ b/tests/test_cases/test_discovery/Makefile @@ -28,4 +28,8 @@ include ../../designs/sample_module/Makefile +ifeq ($(TOPLEVEL_LANG), vhdl) +MODULE = test_discovery,test_vhdl_indexed_name +else MODULE = test_discovery +endif diff --git a/tests/test_cases/test_discovery/test_vhdl_indexed_name.py b/tests/test_cases/test_discovery/test_vhdl_indexed_name.py new file mode 100644 index 00000000..115ce3b1 --- /dev/null +++ b/tests/test_cases/test_discovery/test_vhdl_indexed_name.py @@ -0,0 +1,50 @@ +''' Copyright (c) 2015 Potential Ventures Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Potential Ventures Ltd, + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' + +import cocotb +import logging +from cocotb.triggers import Timer +from cocotb.result import TestError, TestFailure +from cocotb.handle import IntegerObject + + +@cocotb.test() +def index_name_iter(dut): + """Access a non local indexed name""" + yield Timer(0) + total_count = 0 + def _discover(obj): + count = 0 + for thing in obj: + count += 1 + dut._log.info("Found %s (%s)", thing._name, type(thing)) + count += _discover(thing) + return count + total_count = _discover(dut.isample_module1) + + dut._log.info("Number of objects in non local vhpiIndexedNameK is %d" % total_count) + + dut._log.info("Value of EXAMPLE_WIDTH is %d" % dut.isample_module1.EXAMPLE_WIDTH) + diff --git a/tests/test_cases/test_iteration_mixedlang/test_iteration.py b/tests/test_cases/test_iteration_mixedlang/test_iteration.py index 446b3dcc..7c42a35f 100644 --- a/tests/test_cases/test_iteration_mixedlang/test_iteration.py +++ b/tests/test_cases/test_iteration_mixedlang/test_iteration.py @@ -69,7 +69,8 @@ def recursive_discovery(dut): "ncsim"]: # vpiAlways = 31 and vpiStructVar = 2 do not show up in IUS # But vhpiSimpleSigAssignStmtK objects do, and ther are 2. - pass_total = 883 + # Process statements and all sub handles also show up. + pass_total = 959 else: pass_total = 916 @@ -91,15 +92,12 @@ def recursive_discovery(dut): @cocotb.test() def recursive_discovery_boundary(dut): """ - Currently we can't traverse a language boundary during iteration - - However if we manually delve through the language boundary we - should then be able to iterate to discover objects + Iteration though the boundary works but this just double checks """ if cocotb.SIM_NAME in ["ncsim(64)", "ncsim"]: # # But vhpiSimpleSigAssignStmtK objects only show up on IUS, and ther are 2 - pass_total = 428 + pass_total = 504 else: pass_total = 426 diff --git a/tests/test_cases/test_iteration_vhdl/test_iteration.py b/tests/test_cases/test_iteration_vhdl/test_iteration.py index f6ffc10f..83cbf6a0 100644 --- a/tests/test_cases/test_iteration_vhdl/test_iteration.py +++ b/tests/test_cases/test_iteration_vhdl/test_iteration.py @@ -34,6 +34,15 @@ def recursive_discovery(dut): """ Recursively discover every single object in the design """ + if cocotb.SIM_NAME in ["ncsim(64)", + "ncsim"]: + # vpiAlways = 31 and vpiStructVar = 2 do not show up in IUS + # But vhpiSimpleSigAssignStmtK objects do, and ther are 2. + # Process statements and all sub handles also show up. + pass_total = 35841 + else: + pass_total = 32306 + tlog = logging.getLogger("cocotb.test") yield Timer(100) def dump_all_the_things(parent): @@ -45,8 +54,8 @@ def dump_all_the_things(parent): return count total = dump_all_the_things(dut) tlog.info("Found a total of %d things", total) - if total != 32306: - raise TestFailure("Expected 32306 objects but found %d" % total) + if total != pass_total: + raise TestFailure("Expected %d objects but found %d" % (pass_total, total)) @cocotb.test() From ada2dd3336afda3285450e92b9982cd34d0bb14a Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 6 Oct 2015 12:16:35 +0100 Subject: [PATCH 1052/2656] Issue #17: Re-enable 64 bit in Makefile --- makefiles/simulators/Makefile.ius | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 659e0513..897cf0ab 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -29,12 +29,8 @@ # Common Makefile for Cadence Incisive -# Disable 64 bit. There are too many issues with access to values on IUS. -# hope to enable again when these have been resolved. - -ARCH?=i686 -ifeq ($(shell ncbits),64) -$(error "The 64bit version of IUS is on your PATH, please use the 32bit version for now") +ifneq ($(ARCH),i686) +EXTRA_ARGS += -64 endif EXTRA_ARGS += -nclibdirname $(SIM_BUILD) -plinowarn @@ -69,7 +65,7 @@ GPI_LIB = $(COCOTB_VPI_LIB) $(COCOTB_VHPI_LIB) results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(GPI_LIB) LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - irun -64 $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) + irun $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) clean:: @rm -rf $(SIM_BUILD) From 64a1fdde92e3fc13e77dd9660ed575bdd577c3e2 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 6 Oct 2015 21:31:05 +0100 Subject: [PATCH 1053/2656] Issue #17: Expect error as int does not work on icarus --- tests/test_cases/test_discovery/test_discovery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 94df3625..531b8134 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -130,7 +130,7 @@ def access_single_bit_erroneous(dut): dut.stream_in_data[bit] <= 1 yield Timer(10) -@cocotb.test() +@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) def access_integer(dut): """Integer should show as an IntegerObject""" bitfail = False From 87298635614f6c01d376f29c4439020b62a88e2b Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Wed, 7 Oct 2015 15:51:10 +1100 Subject: [PATCH 1054/2656] testcase for vhdl generic with string type. --- tests/designs/sample_module/sample_module.vhdl | 2 ++ tests/designs/sample_module/sample_module_1.vhdl | 1 + 2 files changed, 3 insertions(+) diff --git a/tests/designs/sample_module/sample_module.vhdl b/tests/designs/sample_module/sample_module.vhdl index 7faec0ca..6d646b2a 100644 --- a/tests/designs/sample_module/sample_module.vhdl +++ b/tests/designs/sample_module/sample_module.vhdl @@ -58,6 +58,7 @@ architecture impl of sample_module is component sample_module_1 is generic ( + EXAMPLE_STRING : string; EXAMPLE_WIDTH : integer ); port ( @@ -95,6 +96,7 @@ stream_out_int <= stream_in_int; isample_module1 : component sample_module_1 generic map ( + EXAMPLE_STRING => "TESTING", EXAMPLE_WIDTH => 7 ) port map ( diff --git a/tests/designs/sample_module/sample_module_1.vhdl b/tests/designs/sample_module/sample_module_1.vhdl index 1bbece4f..9562689f 100644 --- a/tests/designs/sample_module/sample_module_1.vhdl +++ b/tests/designs/sample_module/sample_module_1.vhdl @@ -5,6 +5,7 @@ use ieee.numeric_std.all; entity sample_module_1 is generic ( + EXAMPLE_STRING : string; EXAMPLE_WIDTH : integer ); port ( From 349e33d9674787f785aaf2c978a1b66495b59b60 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 7 Oct 2015 15:49:30 +0100 Subject: [PATCH 1055/2656] Issue #17: Add support for vhpiStringVal and elements of that as vhpiCharVal --- cocotb/handle.py | 91 ++++++++++++++----- include/gpi.h | 4 +- lib/fli/FliImpl.cpp | 14 ++- lib/fli/FliImpl.h | 2 + lib/gpi/GpiCommon.cpp | 6 ++ lib/gpi/gpi_priv.h | 1 + lib/simulator/simulatormodule.c | 25 ++++- lib/simulator/simulatormodule.h | 4 +- lib/vhpi/VhpiCbHdl.cpp | 65 +++++++++++-- lib/vhpi/VhpiImpl.cpp | 9 +- lib/vhpi/VhpiImpl.h | 1 + lib/vpi/VpiCbHdl.cpp | 10 ++ lib/vpi/VpiImpl.cpp | 3 + lib/vpi/VpiImpl.h | 1 + .../designs/sample_module/sample_module.vhdl | 5 +- .../test_discovery/test_discovery.py | 88 +++++++++++++++++- .../test_discovery/test_vhdl_indexed_name.py | 2 - 17 files changed, 291 insertions(+), 40 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 036fae60..5ab43b7f 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -93,13 +93,16 @@ def __hash__(self): return self._handle - def __cmp__(self, other): + def __eq__(self, other): # Permits comparison of handles i.e. if clk == dut.clk if isinstance(other, SimHandleBase): if self._handle == other._handle: return 0 return 1 + def __ne__(self, other): + return not self.__eq__(other) + def __repr__(self): return self._fullname @@ -283,6 +286,9 @@ def __init__(self, handle): for name, attribute in SimHandleBase._compat_mapping.items(): setattr(self, name, getattr(self, attribute)) + def __str__(self): + return "%s @0x%x" % (self._name, self._handle) + def __iter__(self): return iter(()) @@ -297,24 +303,32 @@ def __init__(self, handle, handle_type): NonHierarchyObject.__init__(self, handle) if handle_type in [simulator.INTEGER, simulator.ENUM]: self._value = simulator.get_signal_val_long(self._handle) - return elif handle_type == simulator.REAL: self._value = simulator.get_signal_val_real(self._handle) - return - - val = simulator.get_signal_val_str(self._handle) - self._value = BinaryValue(bits=len(val)) - try: - self._value.binstr = val - except: - self._value = val + elif handle_type == simulator.STRING: + self._value = simulator.get_signal_val_str(self._handle) + else: + val = simulator.get_signal_val_binstr(self._handle) + self._value = BinaryValue(bits=len(val)) + try: + self._value.binstr = val + except: + self._value = val def __int__(self): return int(self._value) - def __cmp__(self, other): - # Use the comparison method of the other object against our value - return self._value.__cmp__(other) + def __eq__(self, other): + return self._value.__eq__(other) + + def __ne__(self, other): + if isinstance(self._value, str): + return self._value.__ne__(other) + else: + return self._value != other + + def __repr__(self): + return str(self._value) def _setcachedvalue(self, *args, **kwargs): raise ValueError("Not permissible to set values on a constant object") @@ -335,9 +349,6 @@ def __init__(self, handle): def __hash__(self): return self._handle - def __str__(self): - return "%s @0x%x" % (self._name, self._handle) - def __getitem__(self, index): if index in self._sub_handles: return self._sub_handles[index] @@ -367,7 +378,7 @@ def _getvalue(self): #value = property(_getvalue, None, None, "A reference to the value") def _get_value_str(self): - return simulator.get_signal_val_str(self._handle) + return simulator.get_signal_val_binstr(self._handle) def __len__(self): """Returns the 'length' of the underlying object. @@ -380,16 +391,16 @@ def __len__(self): self._len = simulator.get_num_elems(self._handle) return self._len - - def __cmp__(self, other): - - # Permits comparison of handles i.e. if clk == dut.clk + def __eq__(self, other): if isinstance(other, SimHandleBase): if self._handle == other._handle: return 0 return 1 # Use the comparison method of the other object against our value - return self.value.__cmp__(other) + return self.value.__eq__(other) + + def __ne__(self, other): + return not self.__eq__(other) def __int__(self): val = self.value @@ -525,7 +536,7 @@ class IntegerObject(ModifiableObject): Specific object handle for Integer and Enum signals and variables """ - def _setimmeadiatevalue(self, value): + def setimmediatevalue(self, value): """ Set the value of the underlying simulation object to value. @@ -554,6 +565,39 @@ def _getvalue(self): def __int__(self): return self._getvalue() +class StringObject(ModifiableObject): + """ + Specific object handle for String variables + """ + + def setimmediatevalue(self, value): + """ + Set the value of the underlying simulation object to value. + + Args: + value (string) + The value to drive onto the simulator object + + Raises: + TypeError + + This operation will fail unless the handle refers to a modifiable + object eg net, signal or variable. + """ + if not isinstance(value, str): + self._log.critical("Unsupported type for string value assignment: %s (%s)" % (type(value), repr(value))) + raise TypeError("Unable to set simulator value with type %s" % (type(value))) + + simulator.set_signal_val_str(self._handle, value) + + def _getvalue(self): + return simulator.get_signal_val_str(self._handle) + + # We want to maintain compatability with python 2.5 so we can't use @property with a setter + value = property(_getvalue, ModifiableObject._setcachedvalue, None, "A reference to the value") + + def __repr__(self): + return repr(self._getvalue()) _handle2obj = {} @@ -569,6 +613,7 @@ def SimHandle(handle): simulator.REAL: RealObject, simulator.INTEGER: IntegerObject, simulator.ENUM: IntegerObject, + simulator.STRING: StringObject, } # Enforce singletons since it's possible to retrieve handles avoiding diff --git a/include/gpi.h b/include/gpi.h index 1c421948..58131f59 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -135,7 +135,8 @@ typedef enum gpi_objtype_e { GPI_ENUM = 7, GPI_STRUCTURE = 8, GPI_REAL = 9, - GPI_INTEGER = 10 + GPI_INTEGER = 10, + GPI_STRING = 11, } gpi_objtype_t; // When iterating, we can chose to either get child objects, drivers or loads @@ -164,6 +165,7 @@ int gpi_get_num_elems(gpi_sim_hdl gpi_sim_hdl); // This is all slightly verbose but it saves having to enumerate various value types // We only care about a limited subset of values. const char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl); +const char *gpi_get_signal_value_str(gpi_sim_hdl gpi_hdl); double gpi_get_signal_value_real(gpi_sim_hdl gpi_hdl); long gpi_get_signal_value_long(gpi_sim_hdl gpi_hdl); const char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl); diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 02c2252f..34b7ddf3 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -110,7 +110,7 @@ void FliImpl::sim_end(void) GpiObjHdl* FliImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) { LOG_WARN("%s implementation can not create from raw handle", - m_name.c_str()); + get_name_c()); return NULL; } @@ -444,6 +444,12 @@ const char* FliSignalObjHdl::get_signal_value_binstr(void) return m_val_buff; } +const char* FliSignalObjHdl::get_signal_value_str(void) +{ + LOG_ERROR("Getting signal value as str not currently supported"); + return NULL; +} + double FliSignalObjHdl::get_signal_value_real(void) { LOG_ERROR("Getting signal value as double not currently supported!"); @@ -578,6 +584,12 @@ const char* FliVariableObjHdl::get_signal_value_binstr(void) return m_val_buff; } +const char* FliVariableObjHdl::get_signal_value_str(void) +{ + LOG_ERROR("Getting signal value as str not currently supported"); + return ""; +} + double FliVariableObjHdl::get_signal_value_real(void) { LOG_ERROR("Getting variable value as double not currently supported!"); diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index e47baab6..e9d0970e 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -94,6 +94,7 @@ class FliSignalObjHdl : public GpiSignalObjHdl { } const char* get_signal_value_binstr(void); + const char* get_signal_value_str(void); double get_signal_value_real(void); long get_signal_value_long(void); @@ -134,6 +135,7 @@ class FliVariableObjHdl : public GpiSignalObjHdl { } const char* get_signal_value_binstr(void); + const char* get_signal_value_str(void); double get_signal_value_real(void); long get_signal_value_long(void); diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index fad67321..836f2ea0 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -395,6 +395,12 @@ const char *gpi_get_signal_value_binstr(gpi_sim_hdl sig_hdl) return obj_hdl->get_signal_value_binstr(); } +const char *gpi_get_signal_value_str(gpi_sim_hdl sig_hdl) +{ + GpiSignalObjHdl *obj_hdl = sim_to_hdl(sig_hdl); + return obj_hdl->get_signal_value_str(); +} + double gpi_get_signal_value_real(gpi_sim_hdl sig_hdl) { GpiSignalObjHdl *obj_hdl = sim_to_hdl(sig_hdl); diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 1567b842..192b3d81 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -148,6 +148,7 @@ class GpiSignalObjHdl : public GpiObjHdl { virtual ~GpiSignalObjHdl() { } // Provide public access to the implementation (composition vs inheritance) virtual const char* get_signal_value_binstr(void) = 0; + virtual const char* get_signal_value_str(void) = 0; virtual double get_signal_value_real(void) = 0; virtual long get_signal_value_long(void) = 0; diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 7d701481..f2d19b8a 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -526,7 +526,7 @@ static PyObject *next(PyObject *self, PyObject *args) } -static PyObject *get_signal_val_str(PyObject *self, PyObject *args) +static PyObject *get_signal_val_binstr(PyObject *self, PyObject *args) { gpi_sim_hdl hdl; const char *result; @@ -548,6 +548,28 @@ static PyObject *get_signal_val_str(PyObject *self, PyObject *args) return retstr; } +static PyObject *get_signal_val_str(PyObject *self, PyObject *args) +{ + gpi_sim_hdl hdl; + const char *result; + PyObject *retstr; + + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + if (!PyArg_ParseTuple(args, "l", &hdl)) { + DROP_GIL(gstate); + return NULL; + } + + result = gpi_get_signal_value_str(hdl); + retstr = Py_BuildValue("s", result); + + DROP_GIL(gstate); + + return retstr; +} + static PyObject *get_signal_val_real(PyObject *self, PyObject *args) { gpi_sim_hdl hdl; @@ -910,6 +932,7 @@ static void add_module_constants(PyObject* simulator) rc |= PyModule_AddIntConstant(simulator, "STRUCTURE", GPI_STRUCTURE); rc |= PyModule_AddIntConstant(simulator, "REAL", GPI_REAL); rc |= PyModule_AddIntConstant(simulator, "INTEGER", GPI_INTEGER); + rc |= PyModule_AddIntConstant(simulator, "STRING", GPI_STRING); rc |= PyModule_AddIntConstant(simulator, "OBJECTS", GPI_OBJECTS); rc |= PyModule_AddIntConstant(simulator, "DRIVERS", GPI_DRIVERS); rc |= PyModule_AddIntConstant(simulator, "LOADS", GPI_LOADS); diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 6668b903..44423cce 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -60,6 +60,7 @@ static PyObject *log_msg(PyObject *self, PyObject *args); static PyObject *get_signal_val_long(PyObject *self, PyObject *args); static PyObject *get_signal_val_real(PyObject *self, PyObject *args); static PyObject *get_signal_val_str(PyObject *self, PyObject *args); +static PyObject *get_signal_val_binstr(PyObject *self, PyObject *args); static PyObject *set_signal_val_long(PyObject *self, PyObject *args); static PyObject *set_signal_val_real(PyObject *self, PyObject *args); static PyObject *set_signal_val_str(PyObject *self, PyObject *args); @@ -87,7 +88,8 @@ static PyObject *deregister_callback(PyObject *self, PyObject *args); static PyMethodDef SimulatorMethods[] = { {"log_msg", log_msg, METH_VARARGS, "Log a message"}, {"get_signal_val_long", get_signal_val_long, METH_VARARGS, "Get the value of a signal as a long"}, - {"get_signal_val_str", get_signal_val_str, METH_VARARGS, "Get the value of a signal as a binary string"}, + {"get_signal_val_str", get_signal_val_str, METH_VARARGS, "Get the value of a signal as an ascii string"}, + {"get_signal_val_binstr", get_signal_val_binstr, METH_VARARGS, "Get the value of a signal as a binary string"}, {"get_signal_val_real", get_signal_val_real, METH_VARARGS, "Get the value of a signal as a double precision float"}, {"set_signal_val_long", set_signal_val_long, METH_VARARGS, "Set the value of a signal using a long"}, {"set_signal_val_str", set_signal_val_str, METH_VARARGS, "Set the value of a signal using a binary string"}, diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 57fb1fd7..cf41188f 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -56,6 +56,8 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { m_binvalue.numElems = 0; m_binvalue.value.str = NULL; + vhpiHandleT handle = GpiObjHdl::get_handle(); + vhpi_get_value(get_handle(), &m_value); check_vhpi_error(); @@ -65,7 +67,7 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { m_value.format, m_value.numElems, m_value.bufSize, - vhpi_get(vhpiSizeP, GpiObjHdl::get_handle())); + vhpi_get(vhpiSizeP, handle)); // Default - overridden below in certain special cases m_num_elems = m_value.numElems; @@ -74,14 +76,15 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { case vhpiIntVal: case vhpiEnumVal: case vhpiLogicVal: - case vhpiRealVal: { + case vhpiRealVal: + case vhpiCharVal: { break; } case vhpiIntVecVal: case vhpiEnumVecVal: case vhpiLogicVecVal: { - m_num_elems = vhpi_get(vhpiSizeP, GpiObjHdl::get_handle()); + m_num_elems = vhpi_get(vhpiSizeP, handle); m_value.bufSize = m_num_elems*sizeof(vhpiEnumT); m_value.value.enumvs = (vhpiEnumT *)malloc(m_value.bufSize + 1); if (!m_value.value.enumvs) { @@ -90,12 +93,24 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { LOG_DEBUG("Overriding num_elems to %d", m_num_elems); break; } + + case vhpiStrVal: { + m_num_elems = vhpi_get(vhpiSizeP, handle); + m_value.bufSize = (m_num_elems)*sizeof(vhpiCharT) + 1; + m_value.value.str = (vhpiCharT *)malloc(m_value.bufSize); + m_value.numElems = m_num_elems; + if (!m_value.value.str) { + LOG_CRITICAL("Unable to alloc mem for write buffer"); + } + LOG_DEBUG("Overriding num_elems to %d", m_num_elems); + break; + } case vhpiRawDataVal: { // This is an internal representation - the only way to determine // the size is to iterate over the members and count sub-elements vhpiHandleT result = NULL; vhpiHandleT iterator = vhpi_iterator(vhpiIndexedNames, - GpiObjHdl::get_handle()); + handle); while (true) { result = vhpi_scan(iterator); if (NULL == result) @@ -254,6 +269,11 @@ int VhpiSignalObjHdl::set_signal_value(long value) break; } + case vhpiCharVal: { + m_value.value.ch = value; + break; + } + case vhpiRealVal: LOG_WARN("Attempt to set vhpiRealVal signal with integer"); return 0; @@ -263,7 +283,7 @@ int VhpiSignalObjHdl::set_signal_value(long value) return -1; } } - if (vhpi_put_value(GpiObjHdl::get_handle(), &m_value, vhpiDeposit)) { + if (vhpi_put_value(GpiObjHdl::get_handle(), &m_value, vhpiDepositPropagate)) { check_vhpi_error(); return -1; } @@ -295,7 +315,7 @@ int VhpiSignalObjHdl::set_signal_value(double value) } } - if (vhpi_put_value(GpiObjHdl::get_handle(), &m_value, vhpiDeposit)) { + if (vhpi_put_value(GpiObjHdl::get_handle(), &m_value, vhpiDepositPropagate)) { check_vhpi_error(); return -1; } @@ -345,10 +365,18 @@ int VhpiSignalObjHdl::set_signal_value(std::string &value) break; } - case vhpiRealVal: + case vhpiRealVal: { LOG_WARN("Attempt to vhpiRealVal signal with string"); return 0; + } + case vhpiStrVal: { + std::vector writable(value.begin(), value.end()); + writable.push_back('\0'); + strncpy(m_value.value.str, &writable[0], m_value.numElems); + m_value.value.str[m_value.numElems] = '\0'; + break; + } default: { LOG_CRITICAL("VHPI type of object has changed at runtime, big fail"); @@ -356,7 +384,7 @@ int VhpiSignalObjHdl::set_signal_value(std::string &value) } } - if (vhpi_put_value(GpiObjHdl::get_handle(), &m_value, vhpiDeposit)) { + if (vhpi_put_value(GpiObjHdl::get_handle(), &m_value, vhpiDepositPropagate)) { check_vhpi_error(); return -1; } @@ -387,6 +415,27 @@ const char* VhpiSignalObjHdl::get_signal_value_binstr(void) } } +const char* VhpiSignalObjHdl::get_signal_value_str(void) +{ + switch (m_value.format) { + case vhpiStrVal: { + int ret = vhpi_get_value(GpiObjHdl::get_handle(), &m_value); + if (ret) { + check_vhpi_error(); + LOG_ERROR("Size of m_value.value.str was not large enough req=%d have=%d for type %s", + ret, + m_value.bufSize, + ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); + } + break; + } + default: { + LOG_ERROR("Reading strings not valid for this handle"); + return ""; + } + } + return m_value.value.str; +} double VhpiSignalObjHdl::get_signal_value_real(void) { diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index ca7b471c..7d311a6e 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -226,7 +226,8 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, break; } - case vhpiIntVal: { + case vhpiIntVal: + case vhpiCharVal: { LOG_DEBUG("Detected an INT type %s", fq_name.c_str()); gpi_type = GPI_INTEGER; break; @@ -238,6 +239,12 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, break; } + case vhpiStrVal: { + LOG_DEBUG("Detected a STRING type %s", fq_name.c_str()); + gpi_type = GPI_STRING; + break; + } + case vhpiIntVecVal: case vhpiRealVecVal: case vhpiEnumVecVal: diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index e42b2a40..ff14dd1f 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -159,6 +159,7 @@ class VhpiSignalObjHdl : public GpiSignalObjHdl { virtual ~VhpiSignalObjHdl(); const char* get_signal_value_binstr(void); + const char* get_signal_value_str(void); double get_signal_value_real(void); long get_signal_value_long(void); diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 2f5ff35a..99d83025 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -143,6 +143,16 @@ const char* VpiSignalObjHdl::get_signal_value_binstr(void) return value_s.value.str; } +const char* VpiSignalObjHdl::get_signal_value_str(void) +{ + s_vpi_value value_s = {vpiStringVal}; + + vpi_get_value(GpiObjHdl::get_handle(), &value_s); + check_vpi_error(); + + return value_s.value.str; +} + double VpiSignalObjHdl::get_signal_value_real(void) { FENTER diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 6aefe73b..7cfb1f85 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -113,6 +113,9 @@ gpi_objtype_t to_gpi_objtype(int32_t vpitype) case vpiInitial: return GPI_MODULE; + case vpiStringVal: + return GPI_STRING; + default: LOG_DEBUG("Unable to map VPI type %d onto GPI type", vpitype); return GPI_UNKNOWN; diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 636bdd20..8786fb6a 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -183,6 +183,7 @@ class VpiSignalObjHdl : public GpiSignalObjHdl { virtual ~VpiSignalObjHdl() { } const char* get_signal_value_binstr(void); + const char* get_signal_value_str(void); double get_signal_value_real(void); long get_signal_value_long(void); diff --git a/tests/designs/sample_module/sample_module.vhdl b/tests/designs/sample_module/sample_module.vhdl index 6d646b2a..179b3003 100644 --- a/tests/designs/sample_module/sample_module.vhdl +++ b/tests/designs/sample_module/sample_module.vhdl @@ -45,12 +45,14 @@ entity sample_module is stream_in_ready : out std_ulogic; stream_in_real : in real; stream_in_int : in integer; + stream_in_string : in string(1 to 8); stream_out_data_comb : out std_ulogic_vector(7 downto 0); stream_out_data_registered : out std_ulogic_vector(7 downto 0); stream_out_ready : in std_ulogic; stream_out_real : out real; - stream_out_int : out integer + stream_out_int : out integer; + stream_out_string : out string(1 to 8) ); end; @@ -93,6 +95,7 @@ stream_out_data_comb <= afunc(stream_in_data) when stream_in_func_en = '0' else stream_in_ready <= stream_out_ready; stream_out_real <= stream_in_real; stream_out_int <= stream_in_int; +stream_out_string <= stream_in_string; isample_module1 : component sample_module_1 generic map ( diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 531b8134..84d1a190 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -29,7 +29,7 @@ import logging from cocotb.triggers import Timer from cocotb.result import TestError, TestFailure -from cocotb.handle import IntegerObject +from cocotb.handle import IntegerObject, ConstantObject @cocotb.test() @@ -153,6 +153,92 @@ def access_integer(dut): if length is not 1: raise TestFailure("Length should be 1 not %d" % length) +@cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) +def access_constant_integer(dut): + """ + Access a constant integer + """ + tlog = logging.getLogger("cocotb.test") + yield Timer(10) + constant_integer = dut.isample_module1.EXAMPLE_WIDTH + tlog.info("Value of EXAMPLE_WIDTH is %d" % constant_integer) + if not isinstance(constant_integer, ConstantObject): + raise TestFailure("EXAMPLE_WIDTH was not constant") + if constant_integer != 7: + raise TestFailure("EXAMPLE_WIDTH was not 7") + +@cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) +def access_string(dut): + """ + Access to a string, both constant and signal + """ + tlog = logging.getLogger("cocotb.test") + yield Timer(10) + constant_string = dut.isample_module1.EXAMPLE_STRING; + tlog.info("%s is %s" % (constant_string, repr(constant_string))) + if not isinstance(constant_string, ConstantObject): + raise TestFailure("EXAMPLE_STRING was not constant") + if constant_string != "TESTING": + raise TestFailure("EXAMPLE_STRING was not == \'TESTING\'") + + tlog.info("Test writing under size") + + test_string = "cocotb" + dut.stream_in_string <= test_string + + varible_string = dut.stream_out_string + if varible_string != '': + raise TestFailure("%s not \'\'" % varible_string) + + yield Timer(10) + + if varible_string != test_string: + raise TestFailure("%s %s != '%s'" % (varible_string, repr(varible_string), test_string)) + + test_string = "longer_than_the_array" + tlog.info("Test writing over size with '%s'" % test_string) + + dut.stream_in_string <= test_string + varible_string = dut.stream_out_string + + yield Timer(10) + + test_string = test_string[:len(varible_string)] + + if varible_string != test_string: + raise TestFailure("%s %s != '%s'" % (varible_string, repr(varible_string), test_string)) + + tlog.info("Test read access to a string character") + + yield Timer(10) + + idx = 3 + + result_slice = varible_string[idx] + if chr(result_slice) != test_string[idx]: + raise TestFailure("Single character did not match '%c' != '%c'" % + (result_slice, test_string[idx])) + + tlog.info("Test write access to a string character") + + yield Timer(10) + + for i in varible_string: + lower = chr(i) + upper = lower.upper() + i <= ord(upper) + + yield Timer(10) + + test_string = test_string.upper() + + result = repr(varible_string); + tlog.info("After setting bytes of string value is %s" % result) + if varible_string != test_string: + raise TestFailure("%s %s != '%s'" % (varible_string, result, test_string)) + + + @cocotb.test(skip=True) def skip_a_test(dut): diff --git a/tests/test_cases/test_discovery/test_vhdl_indexed_name.py b/tests/test_cases/test_discovery/test_vhdl_indexed_name.py index 115ce3b1..be7b5db1 100644 --- a/tests/test_cases/test_discovery/test_vhdl_indexed_name.py +++ b/tests/test_cases/test_discovery/test_vhdl_indexed_name.py @@ -46,5 +46,3 @@ def _discover(obj): dut._log.info("Number of objects in non local vhpiIndexedNameK is %d" % total_count) - dut._log.info("Value of EXAMPLE_WIDTH is %d" % dut.isample_module1.EXAMPLE_WIDTH) - From eb7f3dac911e6ff44bc8ac012ff8a019f2c0c0d9 Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Thu, 8 Oct 2015 10:54:25 +1100 Subject: [PATCH 1056/2656] Ability to do == on simulator values is nice. Without this change one needs to use the .value when comparing a simulator value. e.g if clk.value == 1: ... where doing this looks nicer - if clk == 1: ... --- cocotb/binary.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cocotb/binary.py b/cocotb/binary.py index 0ec47ffb..4ce6ec45 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -361,6 +361,16 @@ def __nonzero__(self): return True return False + def __eq__(self, other): + if isinstance(other, BinaryValue): + other = other.value + return self.value == other + + def __ne__(self, other): + if isinstance(other, BinaryValue): + other = other.value + return self.value != other + def __cmp__(self, other): """Comparison against other values""" if isinstance(other, BinaryValue): From 6d7a3283d9e541b1187c0782ae4994aafdb08a99 Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Thu, 8 Oct 2015 11:05:23 +1100 Subject: [PATCH 1057/2656] testcase for boolean generic in vhdl. Currently throwing up errors when recursive over modules with this construct. --- tests/designs/sample_module/sample_module.vhdl | 4 +++- tests/designs/sample_module/sample_module_1.vhdl | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/designs/sample_module/sample_module.vhdl b/tests/designs/sample_module/sample_module.vhdl index 179b3003..9932eb4e 100644 --- a/tests/designs/sample_module/sample_module.vhdl +++ b/tests/designs/sample_module/sample_module.vhdl @@ -61,6 +61,7 @@ architecture impl of sample_module is component sample_module_1 is generic ( EXAMPLE_STRING : string; + EXAMPLE_BOOL : boolean; EXAMPLE_WIDTH : integer ); port ( @@ -99,7 +100,8 @@ stream_out_string <= stream_in_string; isample_module1 : component sample_module_1 generic map ( - EXAMPLE_STRING => "TESTING", + EXAMPLE_STRING => "TESTING", + EXAMPLE_BOOL => true, EXAMPLE_WIDTH => 7 ) port map ( diff --git a/tests/designs/sample_module/sample_module_1.vhdl b/tests/designs/sample_module/sample_module_1.vhdl index 9562689f..7ca86fa1 100644 --- a/tests/designs/sample_module/sample_module_1.vhdl +++ b/tests/designs/sample_module/sample_module_1.vhdl @@ -6,6 +6,7 @@ use ieee.numeric_std.all; entity sample_module_1 is generic ( EXAMPLE_STRING : string; + EXAMPLE_BOOL : boolean; EXAMPLE_WIDTH : integer ); port ( From ebd4e6ef06d714cb4ebbb79ec25321445a9395c1 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Thu, 8 Oct 2015 16:49:12 +0100 Subject: [PATCH 1058/2656] Issue #330: Access elements of a structure --- include/vpi_user.h | 9 ++++++--- lib/vpi/VpiImpl.cpp | 1 + tests/designs/sample_module/sample_module.v | 9 ++++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/include/vpi_user.h b/include/vpi_user.h index b7fdd56d..1f06e719 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -61,17 +61,20 @@ typedef uint32_t *vpiHandle; #define vpiReg 48 /* scalar or vector reg */ #define vpiRegBit 49 /* bit of vector reg */ #define vpiParameter 41 /* module parameter */ -#define vpiNetArray 114 +#define vpiNetArray 114 #define vpiRegArray 116 /* multidimensional reg */ #define vpiInterface 601 #define vpiInterfaceArray 603 #define vpiModport 606 #define vpiRefObj 608 -#define vpiPackedArrayVar 623 -#define vpiEnumNet 680 /* SystemVerilog */ #define vpiIntVar 612 #define vpiEnumVar 617 #define vpiStructVar 618 +#define vpiPackedArrayVar 623 +#define vpiEnumNet 680 /* SystemVerilog */ +#define vpiStructNet 683 + + #define vpiStop 66 /* execute simulator's $stop */ #define vpiFinish 67 /* execute simulator's $finish */ #define vpiReset 68 /* execute simulator's $reset */ diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 9cc3bf07..68e92769 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -94,6 +94,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &n new_obj = new VpiSignalObjHdl(this, new_hdl); break; case vpiStructVar: + case vpiStructNet: case vpiModule: case vpiInterface: case vpiModport: diff --git a/tests/designs/sample_module/sample_module.v b/tests/designs/sample_module/sample_module.v index 4aefad1f..aaa51b92 100644 --- a/tests/designs/sample_module/sample_module.v +++ b/tests/designs/sample_module/sample_module.v @@ -29,6 +29,12 @@ `timescale 1 ps / 1 ps +typedef struct +{ + logic a_in; + logic b_out; +} test_if; + module sample_module ( input clk, @@ -39,7 +45,8 @@ module sample_module ( input stream_out_ready, output reg [7:0] stream_out_data_comb, - output reg [7:0] stream_out_data_registered + output reg [7:0] stream_out_data_registered, + inout test_if inout_if ); always @(posedge clk) From 2cc7fa8e13eace6a83bb2ab431fea5b4c7d8392c Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 9 Oct 2015 10:03:43 +0100 Subject: [PATCH 1059/2656] Issue #17: Handle boolean values. This should have been easy but showed an interesting issue with vhpi. Both std_logic and boolean end up appearing as vhpiEnumTypeDeclK but the method used to assign data is different. std_logic needing to use vhpi[X,U,Z,0,1] and boolean needing vhpi[True,False]. If this is not done then there is a range violation on the boolean access. Delving into the BaseType and qurying the number of literals that a signal has gives a likley probablity that a signal is boolean or not. Adding a subclass of vhpiSignalObjHdl then allows a specific set_signal_value to be used for the two cases. This is probably also a pre-cursor to using the base type rather than the format to selet the type of object to create --- cocotb/handle.py | 4 +- lib/vhpi/VhpiCbHdl.cpp | 126 ++++++++++++++---- lib/vhpi/VhpiImpl.cpp | 33 ++++- lib/vhpi/VhpiImpl.h | 27 +++- .../designs/sample_module/sample_module.vhdl | 5 +- .../test_discovery/test_discovery.py | 68 +++++++++- 6 files changed, 228 insertions(+), 35 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 5ab43b7f..0ae991b1 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -550,7 +550,9 @@ def setimmediatevalue(self, value): This operation will fail unless the handle refers to a modifiable object eg net, signal or variable. """ - if not isinstance(value, int): + if isinstance(value, BinaryValue): + value = int(value) + elif not isinstance(value, int): self._log.critical("Unsupported type for integer value assignment: %s (%s)" % (type(value), repr(value))) raise TypeError("Unable to set simulator value with type %s" % (type(value))) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index cf41188f..e7cb15b8 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -242,12 +242,13 @@ const vhpiEnumT VhpiSignalObjHdl::chr2vhpi(const char value) } // Value related functions -int VhpiSignalObjHdl::set_signal_value(long value) +int VhpiLogicSignalObjHdl::set_signal_value(long value) { switch (m_value.format) { case vhpiEnumVal: case vhpiLogicVal: { m_value.value.enumv = value ? vhpi1 : vhpi0; + LOG_WARN("Trying to write to %s with Logic %ld as %d", m_name.c_str(), value, m_value.value.enumv); break; } @@ -257,10 +258,103 @@ int VhpiSignalObjHdl::set_signal_value(long value) for (i=0; i(), &m_value, vhpiDepositPropagate)) { + check_vhpi_error(); + return -1; + } + + return 0; +} + +int VhpiLogicSignalObjHdl::set_signal_value(std::string &value) +{ + switch (m_value.format) { + case vhpiEnumVal: + case vhpiLogicVal: { + m_value.value.enumv = chr2vhpi(value.c_str()[0]); + break; + } + + case vhpiEnumVecVal: + case vhpiLogicVecVal: { + int len = value.length(); + + // Since we may not get the numElems correctly from the sim and have to infer it + // we also need to set it here as well each time. + + m_value.numElems = len; + + if (len > m_num_elems) { + LOG_DEBUG("VHPI: Attempt to write string longer than (%s) signal %d > %d", + m_name.c_str(), len, m_num_elems); + m_value.numElems = m_num_elems; + } + + std::string::iterator iter; + + int i = 0; + for (iter = value.begin(); + (iter != value.end()) && (i < m_num_elems); + iter++, i++) { + m_value.value.enumvs[i] = chr2vhpi(*iter); + } + + // Fill bits at the end of the value to 0's + for (i = len; i < m_num_elems; i++) { + m_value.value.enumvs[i] = vhpi0; + } + + break; + } + + default: { + LOG_ERROR("VHPI: Unable to set a std_logic signal with a raw value"); + return -1; + } + } + + if (vhpi_put_value(GpiObjHdl::get_handle(), &m_value, vhpiDepositPropagate)) { + check_vhpi_error(); + return -1; + } + + return 0; +} + + +// Value related functions +int VhpiSignalObjHdl::set_signal_value(long value) +{ + switch (m_value.format) { + case vhpiEnumVecVal: + case vhpiLogicVecVal: { + int i; + for (i=0; iformat_to_string(m_value.format)); return -1; } } @@ -300,19 +391,12 @@ int VhpiSignalObjHdl::set_signal_value(double value) m_value.value.real = value; break; - case vhpiEnumVal: - case vhpiEnumVecVal: - case vhpiLogicVal: - case vhpiLogicVecVal: - case vhpiIntVal: - case vhpiIntVecVal: - LOG_WARN("Attempt to set non vhpiRealVal signal with double"); - return 0; - default: { - LOG_CRITICAL("VHPI type of object has changed at runtime, big fail"); + LOG_ERROR("VHPI: Unable to set a Real handle this format type %s", + ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); return -1; } + } if (vhpi_put_value(GpiObjHdl::get_handle(), &m_value, vhpiDepositPropagate)) { @@ -365,11 +449,6 @@ int VhpiSignalObjHdl::set_signal_value(std::string &value) break; } - case vhpiRealVal: { - LOG_WARN("Attempt to vhpiRealVal signal with string"); - return 0; - } - case vhpiStrVal: { std::vector writable(value.begin(), value.end()); writable.push_back('\0'); @@ -379,8 +458,9 @@ int VhpiSignalObjHdl::set_signal_value(std::string &value) } default: { - LOG_CRITICAL("VHPI type of object has changed at runtime, big fail"); - return -1; + LOG_ERROR("VHPI: Unable to handle this format type %s", + ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); + return -1; } } diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 7d311a6e..5eafe64c 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -174,6 +174,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, gpi_objtype_t gpi_type; GpiObjHdl *new_obj = NULL; bool modifiable = true; + bool logic = false; if (vhpiVerilog == (type = vhpi_get(vhpiKindP, new_hdl))) { LOG_DEBUG("vhpiVerilog returned from vhpi_get(vhpiType, ...)") @@ -213,6 +214,30 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, */ vhpi_get_value(new_hdl, &value); + + /* We need to delve further here to detemine how to later set + the values of an object */ + if (value.format == vhpiEnumVal || + value.format == vhpiEnumVecVal) { + vhpiHandleT base_hdl = vhpi_handle(vhpiBaseType, new_hdl); + vhpiIntT base_type = vhpi_get(vhpiKindP, base_hdl); + const char *base_name = vhpi_get_str(vhpiKindStrP, base_hdl); + int num_literals = vhpi_get(vhpiNumLiteralsP, base_hdl); + vhpi_release_handle(base_hdl); + /* If it is an Enum format then it's logic unless it's boolean */ + logic = true; + if (vhpiEnumTypeDeclK == base_type) { + LOG_WARN("Base handle of type %s options %d", base_name, num_literals); + if (num_literals > 2) { + LOG_DEBUG("This is probably a std_logic type"); + } else { + LOG_DEBUG("This is probably a boolean"); + gpi_type = GPI_INTEGER; + } + break; + } + } + switch (value.format) { case vhpiObjTypeVal: { LOG_DEBUG("Forcing %s to GpiObjHdl as format unavailable", fq_name.c_str()); @@ -291,8 +316,12 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, return NULL; } - if (modifiable) - new_obj = new VhpiSignalObjHdl(this, new_hdl, gpi_type, is_const(type)); + if (modifiable) { + if (logic) + new_obj = new VhpiLogicSignalObjHdl(this, new_hdl, gpi_type, is_const(type)); + else + new_obj = new VhpiSignalObjHdl(this, new_hdl, gpi_type, is_const(type)); + } else new_obj = new GpiObjHdl(this, new_hdl, gpi_type); diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index ff14dd1f..9c9562b4 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -151,11 +151,13 @@ class VhpiReadwriteCbHdl : public VhpiCbHdl { class VhpiSignalObjHdl : public GpiSignalObjHdl { public: - VhpiSignalObjHdl(GpiImplInterface *impl, vhpiHandleT hdl, gpi_objtype_t objtype, bool is_const) : - GpiSignalObjHdl(impl, hdl, objtype, is_const), - m_rising_cb(impl, this, GPI_RISING), - m_falling_cb(impl, this, GPI_FALLING), - m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } + VhpiSignalObjHdl(GpiImplInterface *impl, + vhpiHandleT hdl, + gpi_objtype_t objtype, + bool is_const) : GpiSignalObjHdl(impl, hdl, objtype, is_const), + m_rising_cb(impl, this, GPI_RISING), + m_falling_cb(impl, this, GPI_FALLING), + m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } virtual ~VhpiSignalObjHdl(); const char* get_signal_value_binstr(void); @@ -172,7 +174,7 @@ class VhpiSignalObjHdl : public GpiSignalObjHdl { GpiCbHdl *value_change_cb(unsigned int edge); int initialise(std::string &name, std::string &fq_name); -private: +protected: const vhpiEnumT chr2vhpi(const char value); vhpiValueT m_value; vhpiValueT m_binvalue; @@ -181,6 +183,19 @@ class VhpiSignalObjHdl : public GpiSignalObjHdl { VhpiValueCbHdl m_either_cb; }; +class VhpiLogicSignalObjHdl : public VhpiSignalObjHdl { +public: + VhpiLogicSignalObjHdl(GpiImplInterface *impl, + vhpiHandleT hdl, + gpi_objtype_t objtype, + bool is_const) : VhpiSignalObjHdl(impl, hdl, objtype, is_const) { } + + virtual ~VhpiLogicSignalObjHdl() { } + + int set_signal_value(const long value); + int set_signal_value(std::string &value); +}; + class VhpiIterator : public GpiIterator { public: VhpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl); diff --git a/tests/designs/sample_module/sample_module.vhdl b/tests/designs/sample_module/sample_module.vhdl index 9932eb4e..58612298 100644 --- a/tests/designs/sample_module/sample_module.vhdl +++ b/tests/designs/sample_module/sample_module.vhdl @@ -46,13 +46,15 @@ entity sample_module is stream_in_real : in real; stream_in_int : in integer; stream_in_string : in string(1 to 8); + stream_in_bool : in boolean; stream_out_data_comb : out std_ulogic_vector(7 downto 0); stream_out_data_registered : out std_ulogic_vector(7 downto 0); stream_out_ready : in std_ulogic; stream_out_real : out real; stream_out_int : out integer; - stream_out_string : out string(1 to 8) + stream_out_string : out string(1 to 8); + stream_out_bool : out boolean ); end; @@ -97,6 +99,7 @@ stream_in_ready <= stream_out_ready; stream_out_real <= stream_in_real; stream_out_int <= stream_in_int; stream_out_string <= stream_in_string; +stream_out_bool <= stream_in_bool; isample_module1 : component sample_module_1 generic map ( diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 84d1a190..bf8395cb 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -72,12 +72,16 @@ def discover_value_not_in_dut(dut): @cocotb.test() def access_signal(dut): """Access a signal using the assignment mechanism""" + tlog = logging.getLogger("cocotb.test") + signal = dut.stream_in_data + + tlog.info("Signal is %s" % type(signal)) dut.stream_in_data.setimmediatevalue(1) yield Timer(10) if dut.stream_in_data.value.integer != 1: raise TestError("%s.%s != %d" % (str(dut.stream_in_data), - dut.stream_in_data.value.integer), 1) + dut.stream_in_data.value.integer, 1)) @cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"], @@ -130,7 +134,8 @@ def access_single_bit_erroneous(dut): dut.stream_in_data[bit] <= 1 yield Timer(10) -@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) +@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"], + expect_fail=cocotb.SIM_NAME in ["Riviera-PRO"]) def access_integer(dut): """Integer should show as an IntegerObject""" bitfail = False @@ -153,6 +158,14 @@ def access_integer(dut): if length is not 1: raise TestFailure("Length should be 1 not %d" % length) +@cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) +def access_ulogic(dut): + """Access a std_ulogic as enum""" + tlog = logging.getLogger("cocotb.test") + yield Timer(10) + constant_integer = dut.stream_in_valid + + @cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) def access_constant_integer(dut): """ @@ -237,7 +250,58 @@ def access_string(dut): if varible_string != test_string: raise TestFailure("%s %s != '%s'" % (varible_string, result, test_string)) +@cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) +def access_constant_boolean(dut): + """Test access to a constant boolean""" + tlog = logging.getLogger("cocotb.test") + + yield Timer(10) + constant_boolean = dut.isample_module1.EXAMPLE_BOOL + if not isinstance(constant_boolean, ConstantObject): + raise TestFailure("dut.stream_in_int.EXAMPLE_BOOL is not a ConstantObject") + + tlog.info("Value of %s is %d" % (constant_boolean, constant_boolean)) + +@cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) +def access_boolean(dut): + """Test access to a boolean""" + tlog = logging.getLogger("cocotb.test") + + yield Timer(10) + boolean = dut.stream_in_bool + + return + + #if not isinstance(boolean, IntegerObject): + # raise TestFailure("dut.stream_in_boolean is not a IntegerObject is %s" % type(boolean)) + + try: + bit = boolean[3] + except TestError as e: + tlog.info("Access to bit is an error as expected") + bitFail = True + + if not bitFail: + raise TestFailure("Access into an integer should be invalid") + + length = len(boolean) + if length is not 1: + raise TestFailure("Length should be 1 not %d" % length) + + tlog.info("Value of %s is %d" % (boolean, boolean)) + + curr_val = int(boolean) + output_bool = dut.stream_out_bool + + tlog.info("Before %d After = %d" % (curr_val, (not curr_val))) + + boolean.setimmediatevalue(not curr_val) + + yield Timer(1) + tlog.info("Value of %s is now %d" % (output_bool, output_bool)) + if (int(curr_val) == int(output_bool)): + raise TestFailure("Value did not propogate") @cocotb.test(skip=True) From fa8e0fc5962ea8c8e45692402cd96550c0f283a0 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 9 Oct 2015 13:34:38 +0100 Subject: [PATCH 1060/2656] Issue #17: Use the base type as a first step to detemining the object type. Less guess work involved --- cocotb/handle.py | 2 +- lib/vhpi/VhpiCbHdl.cpp | 5 - lib/vhpi/VhpiImpl.cpp | 208 ++++++++++++++++++++--------------------- 3 files changed, 103 insertions(+), 112 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 0ae991b1..95264619 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -614,7 +614,7 @@ def SimHandle(handle): simulator.NETARRAY: ModifiableObject, simulator.REAL: RealObject, simulator.INTEGER: IntegerObject, - simulator.ENUM: IntegerObject, + simulator.ENUM: ModifiableObject, simulator.STRING: StringObject, } diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index e7cb15b8..ef449bf2 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -248,7 +248,6 @@ int VhpiLogicSignalObjHdl::set_signal_value(long value) case vhpiEnumVal: case vhpiLogicVal: { m_value.value.enumv = value ? vhpi1 : vhpi0; - LOG_WARN("Trying to write to %s with Logic %ld as %d", m_name.c_str(), value, m_value.value.enumv); break; } @@ -259,7 +258,6 @@ int VhpiLogicSignalObjHdl::set_signal_value(long value) m_value.value.enumvs[m_num_elems-i-1] = value&(1< 2) { - LOG_DEBUG("This is probably a std_logic type"); - } else { - LOG_DEBUG("This is probably a boolean"); - gpi_type = GPI_INTEGER; - } - break; - } + if (vhpiStrVal == value.format) { + LOG_DEBUG("Detected a STRING type %s", fq_name.c_str()); + gpi_type = GPI_STRING; + break; } - switch (value.format) { - case vhpiObjTypeVal: { - LOG_DEBUG("Forcing %s to GpiObjHdl as format unavailable", fq_name.c_str()); - modifiable = false; - break; - } + if (vhpiCharVal == value.format) { + LOG_DEBUG("Detected an CHAR type %s", fq_name.c_str()); + gpi_type = GPI_INTEGER; + break; + } - case vhpiRealVal: { - LOG_DEBUG("Detected a REAL type %s", fq_name.c_str()); - gpi_type = GPI_REAL; - break; - } + if (vhpiRawDataVal == value.format) { + LOG_DEBUG("Detected a RAW type %s", fq_name.c_str()); + gpi_type = GPI_MODULE; + break; + } - case vhpiIntVal: - case vhpiCharVal: { - LOG_DEBUG("Detected an INT type %s", fq_name.c_str()); - gpi_type = GPI_INTEGER; - break; - } + /* More differences between simulators. + Aldec sets value.numElems regardless + IUS does not set for a single dimension. + */ + + if (!value.numElems || (value.numElems == num_elems)) { + LOG_DEBUG("Detected single dimension vector type", fq_name.c_str()); + gpi_type = GPI_ARRAY; + /* Do not break as want to go into vhpiEnumTypeDeclK */ + } else { + LOG_DEBUG("Detected an n dimension vector type", fq_name.c_str()); + gpi_type = GPI_MODULE; + modifiable = false; + break; + } + } - case vhpiRawDataVal: { - LOG_DEBUG("Detected a custom array type %s", fq_name.c_str()); - gpi_type = GPI_MODULE; - break; - } + case vhpiEnumTypeDeclK: { + int num_literals = vhpi_get(vhpiNumLiteralsP, query_hdl); + if (num_literals == 2) { + logic = false; + gpi_type = GPI_INTEGER; + LOG_DEBUG("Detected boolean %s", fq_name.c_str()); + } else { + logic = true; + LOG_DEBUG("Detected std_logic %s", fq_name.c_str()); + } + break; + } - case vhpiStrVal: { - LOG_DEBUG("Detected a STRING type %s", fq_name.c_str()); - gpi_type = GPI_STRING; - break; - } + case vhpiIntTypeDeclK: { + LOG_DEBUG("Detected an INT type %s", fq_name.c_str()); + gpi_type = GPI_INTEGER; + break; + } - case vhpiIntVecVal: - case vhpiRealVecVal: - case vhpiEnumVecVal: - case vhpiLogicVecVal: - case vhpiPhysVecVal: - case vhpiTimeVecVal: { - /* This may well be an n dimensional vector if it is - then we create a non signal object - */ - int num_elems = vhpi_get(vhpiSizeP, new_hdl); - - /* More differences between simulators. - Aldec sets value.numElems regardless - IUS does not set for a single dimension. - */ - - if (!value.numElems || (value.numElems == num_elems)) { - LOG_DEBUG("Detected single dimension vector type", fq_name.c_str()); - gpi_type = GPI_ARRAY; - } else { - LOG_DEBUG("Detected an n dimension vector type", fq_name.c_str()); - gpi_type = GPI_MODULE; - modifiable = false; - break; - } - } - default: - break; - } + case vhpiFloatTypeDeclK: { + LOG_DEBUG("Detected a REAL type %s", fq_name.c_str()); + gpi_type = GPI_REAL; break; } @@ -308,14 +295,19 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, case vhpiProcessStmtK: case vhpiSimpleSigAssignStmtK: case vhpiCondSigAssignStmtK: + case vhpiRecordTypeDeclK: modifiable = false; break; - default: - LOG_DEBUG("Not able to map type (%s) %u to object", - vhpi_get_str(vhpiKindStrP, new_hdl), type); - return NULL; + + default: { + LOG_ERROR("Not able to map type (%s) %u to object", + vhpi_get_str(vhpiKindStrP, query_hdl), type); + new_obj = NULL; + goto out; + } } +create: if (modifiable) { if (logic) new_obj = new VhpiLogicSignalObjHdl(this, new_hdl, gpi_type, is_const(type)); @@ -330,6 +322,10 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, new_obj = NULL; } +out: + if (base_hdl) + vhpi_release_handle(base_hdl); + return new_obj; } From 460fac20e84ae7163361d26f59b0a071927f546c Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 9 Oct 2015 13:37:08 +0100 Subject: [PATCH 1061/2656] Issue #17: Add a shadow copy of the current log level so GPI does not need to go up to python on each log message ~18% improvement --- cocotb/__init__.py | 3 +++ include/gpi_logging.h | 3 ++- lib/gpi_log/gpi_logging.c | 12 +++++++++++- lib/simulator/simulatormodule.c | 20 ++++++++++++++++++++ lib/simulator/simulatormodule.h | 3 +++ 5 files changed, 39 insertions(+), 2 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 2e9c52bc..8be9def5 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -55,6 +55,7 @@ # GPI logging instance # For autodocumentation don't need the extension modules if "SPHINX_BUILD" not in os.environ: + import simulator logging.basicConfig() logging.setLoggerClass(SimBaseLog) log = SimLog('cocotb') @@ -66,6 +67,8 @@ _default_log = logging.INFO log.setLevel(_default_log) loggpi = SimLog('cocotb.gpi') + # Notify GPI of log level + simulator.log_level(_default_log) scheduler = Scheduler() diff --git a/include/gpi_logging.h b/include/gpi_logging.h index eb5611f0..f2f74ff2 100644 --- a/include/gpi_logging.h +++ b/include/gpi_logging.h @@ -41,7 +41,7 @@ EXTERN_C_START enum gpi_log_levels { - GPIDebug= 10, + GPIDebug = 10, GPIInfo = 20, GPIWarning = 30, GPIError = 40, @@ -69,6 +69,7 @@ enum gpi_log_levels { void set_log_handler(void *handler); void set_make_record(void *makerecord); void set_log_filter(void *filter); +void set_log_level(enum gpi_log_levels new_level); void gpi_log(const char *name, long level, const char *pathname, const char *funcname, long lineno, const char *msg, ...); diff --git a/lib/gpi_log/gpi_logging.c b/lib/gpi_log/gpi_logging.c index bac74624..751b6e0e 100644 --- a/lib/gpi_log/gpi_logging.c +++ b/lib/gpi_log/gpi_logging.c @@ -29,10 +29,12 @@ #include #include "../compat/python3_compat.h" +#include // Used to log using the standard python mechanism static PyObject *pLogHandler; static PyObject *pLogFilter; +static enum gpi_log_levels local_level = GPIInfo; void set_log_handler(void *handler) { @@ -46,6 +48,11 @@ void set_log_filter(void *filter) Py_INCREF(pLogFilter); } +void set_log_level(enum gpi_log_levels new_level) +{ + local_level = new_level; +} + // Decode the level into a string matching the Python interpretation struct _log_level_table { long level; @@ -106,7 +113,7 @@ void gpi_log(const char *name, long level, const char *pathname, const char *fun int n; if (!pLogHandler) { - if (level >= 20) { + if (level >= GPIInfo) { va_start(ap, msg); n = vsnprintf(log_buff, LOG_SIZE, msg, ap); va_end(ap); @@ -127,6 +134,9 @@ void gpi_log(const char *name, long level, const char *pathname, const char *fun return; } + if (level < local_level) + return; + // Ignore truncation // calling args is level, filename, lineno, msg, function // diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index f2d19b8a..1dd9339e 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -917,6 +917,26 @@ static PyObject *deregister_callback(PyObject *self, PyObject *args) return value; } +static PyObject *log_level(PyObject *self, PyObject *args) +{ + enum gpi_log_levels new_level; + PyObject *py_level; + PyGILState_STATE gstate; + PyObject *value; + gstate = TAKE_GIL(); + + py_level = PyTuple_GetItem(args, 0); + new_level = (enum gpi_log_levels)PyLong_AsUnsignedLong(py_level); + + set_log_level(new_level); + + value = Py_BuildValue("s", "OK!"); + + DROP_GIL(gstate); + + return value; +} + static void add_module_constants(PyObject* simulator) { // Make the GPI constants accessible from the C world diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 44423cce..795b2dad 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -85,6 +85,8 @@ static PyObject *next(PyObject *self, PyObject *args); static PyObject *get_sim_time(PyObject *self, PyObject *args); static PyObject *deregister_callback(PyObject *self, PyObject *args); +static PyObject *log_level(PyObject *self, PyObject *args); + static PyMethodDef SimulatorMethods[] = { {"log_msg", log_msg, METH_VARARGS, "Log a message"}, {"get_signal_val_long", get_signal_val_long, METH_VARARGS, "Get the value of a signal as a long"}, @@ -110,6 +112,7 @@ static PyMethodDef SimulatorMethods[] = { {"stop_simulator", stop_simulator, METH_VARARGS, "Instruct the attached simulator to stop"}, {"iterate", iterate, METH_VARARGS, "Get an iterator handle to loop over all members in an object"}, {"next", next, METH_VARARGS, "Get the next object from the iterator"}, + {"log_level", log_level, METH_VARARGS, "Set the log level for GPI"}, // FIXME METH_NOARGS => initialization from incompatible pointer type {"get_sim_time", get_sim_time, METH_VARARGS, "Get the current simulation time as a float"}, From 8b2f3e4f6b3ac37a63a41fa151296407fd356e99 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 9 Oct 2015 13:41:06 +0100 Subject: [PATCH 1062/2656] Issue #17: This is quite expensive so comment out as still useful during development. ~5% Improvement --- cocotb/handle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 95264619..3141696a 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -230,8 +230,8 @@ def _discover_all(self): self._sub_handles[name].extend([None]*delta) self._sub_handles[name][index] = hdl self._log.debug("%s.%s[%d] is now %s", self._name, name, index, hdl._name) - for something in self._sub_handles[name]: - self._log.debug("%s: %s" % (type(something), something)) + #for something in self._sub_handles[name]: + # self._log.debug("%s: %s" % (type(something), something)) else: self._log.debug("%s didn't match an index pattern", name) self._sub_handles[hdl._name.split(".")[-1]] = hdl From efdde666cb346d362abd2e1ca7bdc1f1cf07a1c3 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 9 Oct 2015 17:13:59 +0100 Subject: [PATCH 1063/2656] Issue #17: Add vpiGate to object creation, only Riviera-PRO allows the signals to be discovered though. --- lib/vhpi/VhpiCbHdl.cpp | 2 +- lib/vpi/VpiCbHdl.cpp | 10 ++++++++++ lib/vpi/VpiImpl.cpp | 4 ++++ tests/designs/sample_module/sample_module.v | 6 +++++- tests/test_cases/test_discovery/test_discovery.py | 13 ++++++++++++- 5 files changed, 32 insertions(+), 3 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index ef449bf2..c1cc4a4c 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -760,7 +760,7 @@ VhpiIterator::VhpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator vhpi_get(vhpiKindP, vhpi_hdl), vhpi_get_str(vhpiKindStrP, vhpi_hdl), vhpi_get_str(vhpiCaseNameP, vhpi_hdl)); - m_iterator = NULL; + selected = NULL; return; } diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 99d83025..38a1dbd0 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -452,6 +452,14 @@ void vpi_mappings(GpiIteratorMapping &map) 0 }; map.add_to_options(vpiPort, &port_options[0]); + + int32_t gate_options[] = { + vpiPrimTerm, + vpiTableEntry, + vpiUdpDefn, + 0 + }; + map.add_to_options(vpiGate, &gate_options[0]); } GpiIteratorMapping VpiIterator::iterate_over(vpi_mappings); @@ -484,6 +492,7 @@ VpiIterator::VpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(i if (NULL == iterator) { LOG_WARN("vpi_iterate returned NULL for all types"); + selected = NULL; return; } @@ -601,6 +610,7 @@ GpiIterator::Status VpiIterator::next_handle(std::string &name, GpiObjHdl **hdl, return GpiIterator::NOT_NATIVE_NO_NAME; } + LOG_WARN("Did not get name so try Fullname %s", vpi_get_str(vpiFullName, obj)); LOG_DEBUG("Unable to get the name for this object of type %d", type); return GpiIterator::NATIVE_NO_NAME; diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 7cfb1f85..ec3a9ddd 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -111,6 +111,8 @@ gpi_objtype_t to_gpi_objtype(int32_t vpitype) case vpiAlways: case vpiFunction: case vpiInitial: + case vpiGate: + case vpiPrimTerm: return GPI_MODULE; case vpiStringVal: @@ -163,6 +165,8 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, case vpiAlways: case vpiFunction: case vpiInitial: + case vpiGate: + case vpiPrimTerm: new_obj = new GpiObjHdl(this, new_hdl, to_gpi_objtype(type)); break; default: diff --git a/tests/designs/sample_module/sample_module.v b/tests/designs/sample_module/sample_module.v index 86de625b..deb41af5 100644 --- a/tests/designs/sample_module/sample_module.v +++ b/tests/designs/sample_module/sample_module.v @@ -47,7 +47,9 @@ module sample_module ( output integer stream_out_int, `endif output reg [7:0] stream_out_data_comb, - output reg [7:0] stream_out_data_registered + output reg [7:0] stream_out_data_registered, + + output and_output ); always @(posedge clk) @@ -70,6 +72,8 @@ always @(stream_in_int) stream_out_int <= stream_in_int; `endif +and test_and_gate(and_output, stream_in_ready, stream_in_valid); + initial begin $dumpfile("waveform.vcd"); $dumpvars(0,sample_module); diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index bf8395cb..5df54300 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -29,7 +29,7 @@ import logging from cocotb.triggers import Timer from cocotb.result import TestError, TestFailure -from cocotb.handle import IntegerObject, ConstantObject +from cocotb.handle import IntegerObject, ConstantObject, HierarchyObject @cocotb.test() @@ -313,3 +313,14 @@ def skip_a_test(dut): bit = len(dut.stream_in_data) + 4 dut.stream_in_data[bit] <= 1 yield Timer(10) + +@cocotb.test(skip=cocotb.LANGUAGE in ["VHDL"]) +def access_gate(dut): + tlog = logging.getLogger("cocotb.test") + + yield Timer(10) + + gate = dut.test_and_gate + + if not isinstance(gate, HierarchyObject): + raise TestFailure("Gate should be HierarchyObject") From b5fb909e155cc904d89f18dd7bdab04f673bcfb3 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 9 Oct 2015 19:23:56 +0100 Subject: [PATCH 1064/2656] Issue #17: Handle vpiGenScope --- lib/vpi/VpiCbHdl.cpp | 2 +- lib/vpi/VpiImpl.cpp | 4 ++++ tests/designs/sample_module/sample_module.v | 12 ++++++++++++ tests/test_cases/test_discovery/test_discovery.py | 1 + 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 38a1dbd0..a66b08f6 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -408,6 +408,7 @@ void vpi_mappings(GpiIteratorMapping &map) 0 }; map.add_to_options(vpiModule, &module_options[0]); + map.add_to_options(vpiGenScope, &module_options[0]); /* vpiNet */ int32_t net_options[] = { @@ -610,7 +611,6 @@ GpiIterator::Status VpiIterator::next_handle(std::string &name, GpiObjHdl **hdl, return GpiIterator::NOT_NATIVE_NO_NAME; } - LOG_WARN("Did not get name so try Fullname %s", vpi_get_str(vpiFullName, obj)); LOG_DEBUG("Unable to get the name for this object of type %d", type); return GpiIterator::NATIVE_NO_NAME; diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index ec3a9ddd..d02a6cf9 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -85,6 +85,7 @@ gpi_objtype_t to_gpi_objtype(int32_t vpitype) case vpiPackedArrayVar: case vpiRegArray: case vpiNetArray: + case vpiGenScopeArray: return GPI_ARRAY; case vpiEnumNet: @@ -113,6 +114,7 @@ gpi_objtype_t to_gpi_objtype(int32_t vpitype) case vpiInitial: case vpiGate: case vpiPrimTerm: + case vpiGenScope: return GPI_MODULE; case vpiStringVal: @@ -167,6 +169,8 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, case vpiInitial: case vpiGate: case vpiPrimTerm: + case vpiGenScope: + case vpiGenScopeArray: new_obj = new GpiObjHdl(this, new_hdl, to_gpi_objtype(type)); break; default: diff --git a/tests/designs/sample_module/sample_module.v b/tests/designs/sample_module/sample_module.v index deb41af5..5aeda23b 100644 --- a/tests/designs/sample_module/sample_module.v +++ b/tests/designs/sample_module/sample_module.v @@ -82,4 +82,16 @@ initial begin // #500000 $fail_test("Test timed out, failing..."); end +reg[3:0] temp; +parameter NUM_OF_MODULES = 4; +genvar idx; +generate +for (idx = 0; idx < NUM_OF_MODULES; idx=idx+1) begin + always @(posedge clk) begin + temp[idx] <= 1'b0; + end +end +endgenerate + endmodule + diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 5df54300..cb98499e 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -324,3 +324,4 @@ def access_gate(dut): if not isinstance(gate, HierarchyObject): raise TestFailure("Gate should be HierarchyObject") + From 6c334181a6bca0062982d9b4f03916dab93f9f21 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sat, 10 Oct 2015 19:22:30 +0100 Subject: [PATCH 1065/2656] Issue #17: Icarus does not seem to be able to find the gate object --- tests/test_cases/test_discovery/test_discovery.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index cb98499e..75fc80bb 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -314,7 +314,8 @@ def skip_a_test(dut): dut.stream_in_data[bit] <= 1 yield Timer(10) -@cocotb.test(skip=cocotb.LANGUAGE in ["VHDL"]) +@cocotb.test(skip=cocotb.LANGUAGE in ["VHDL"], + expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) def access_gate(dut): tlog = logging.getLogger("cocotb.test") From 42dfe7b1252cd3d2722371bbffc33768af6c3efe Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 12 Oct 2015 17:17:03 +0100 Subject: [PATCH 1066/2656] Cleanup: Fixup missing clean rules --- tests/designs/uart2bus/Makefile | 1 + tests/designs/vhdl_configurations/Makefile | 1 + tests/designs/viterbi_decoder_axi4s/Makefile | 1 + tests/test_cases/test_iteration_verilog/Makefile | 1 + 4 files changed, 4 insertions(+) diff --git a/tests/designs/uart2bus/Makefile b/tests/designs/uart2bus/Makefile index 452cd3a1..95c568eb 100644 --- a/tests/designs/uart2bus/Makefile +++ b/tests/designs/uart2bus/Makefile @@ -43,5 +43,6 @@ ifneq ($(GPI_EXTRA),) else all: @echo "Skipping test as VHDL not supported on simulator=$(SIM)" +clean:: endif diff --git a/tests/designs/vhdl_configurations/Makefile b/tests/designs/vhdl_configurations/Makefile index a4859236..d8e689de 100644 --- a/tests/designs/vhdl_configurations/Makefile +++ b/tests/designs/vhdl_configurations/Makefile @@ -18,5 +18,6 @@ ifneq ($(SIM),) else all: @echo "Skipping test as VHDL not supported on Icarus" +clean:: endif diff --git a/tests/designs/viterbi_decoder_axi4s/Makefile b/tests/designs/viterbi_decoder_axi4s/Makefile index dcaa739c..4af38edf 100644 --- a/tests/designs/viterbi_decoder_axi4s/Makefile +++ b/tests/designs/viterbi_decoder_axi4s/Makefile @@ -69,4 +69,5 @@ include $(COCOTB)/makefiles/Makefile.sim else all: @echo "Skipping test as VHDL not supported on simulator=$(SIM)" +clean:: endif diff --git a/tests/test_cases/test_iteration_verilog/Makefile b/tests/test_cases/test_iteration_verilog/Makefile index 147784ac..4882a53a 100644 --- a/tests/test_cases/test_iteration_verilog/Makefile +++ b/tests/test_cases/test_iteration_verilog/Makefile @@ -38,6 +38,7 @@ MODULE = test_iteration_es ifeq ($(SIM),) all: @echo "Skipping test as ICAURS does not support enough VPI calls" +clean:: else include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim From 82b592e22f40bd393950ad55755966644f802962 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 14 Oct 2015 16:16:26 +0100 Subject: [PATCH 1067/2656] Issue #330: Add the test case --- tests/test_cases/issue_330/Makefile | 33 ++++++++++++++++++++ tests/test_cases/issue_330/issue_330.py | 41 +++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 tests/test_cases/issue_330/Makefile create mode 100644 tests/test_cases/issue_330/issue_330.py diff --git a/tests/test_cases/issue_330/Makefile b/tests/test_cases/issue_330/Makefile new file mode 100644 index 00000000..48b1f7f6 --- /dev/null +++ b/tests/test_cases/issue_330/Makefile @@ -0,0 +1,33 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +include $(COCOTB)/tests/designs/sample_module/Makefile + +MODULE = issue_330 diff --git a/tests/test_cases/issue_330/issue_330.py b/tests/test_cases/issue_330/issue_330.py new file mode 100644 index 00000000..15574148 --- /dev/null +++ b/tests/test_cases/issue_330/issue_330.py @@ -0,0 +1,41 @@ +# A set of regression tests for open issues + +import cocotb +import logging +from cocotb.clock import Clock +from cocotb.triggers import RisingEdge, Timer, ReadOnly +from cocotb.result import TestFailure +from cocotb.binary import BinaryValue + +@cocotb.test() +def issue_330_direct(dut): + """ + Access a structure + """ + + tlog = logging.getLogger("cocotb.test") + yield Timer(10) + + structure = dut.inout_if + + tlog.info("Value of inout_if => a_in = %d ; b_oout = %d" % (structure.a_in, structure.b_out)) + +@cocotb.test() +def issue_330_iteration(dut): + """ + Access a structure via issue_330_iteration + """ + + tlog = logging.getLogger("cocotb.test") + yield Timer(10) + + structure = dut.inout_if + + count = 0 + for member in structure: + tlog.info("Found %s" % member) + count += 1 + + if count != 2: + raise TestFailure("There should have been two members of the structure") + From 977cb5424c5fe32592e8786447a0759a1fa0522d Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 16 Oct 2015 09:15:40 +0100 Subject: [PATCH 1068/2656] Issue #17: use vhdl as skip tag not VHDL --- tests/test_cases/test_discovery/test_discovery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 75fc80bb..b519df4c 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -314,7 +314,7 @@ def skip_a_test(dut): dut.stream_in_data[bit] <= 1 yield Timer(10) -@cocotb.test(skip=cocotb.LANGUAGE in ["VHDL"], +@cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"], expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) def access_gate(dut): tlog = logging.getLogger("cocotb.test") From 6b098baffd11a7e210c83fde1c25dbb73379e247 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 16 Oct 2015 16:10:17 +0100 Subject: [PATCH 1069/2656] Issue #17: Handle slices and more refinement to type detection --- lib/vhpi/VhpiCbHdl.cpp | 12 +- lib/vhpi/VhpiImpl.cpp | 118 +++++++++--------- .../designs/sample_module/sample_module.vhdl | 2 + .../test_discovery/test_discovery.py | 6 +- 4 files changed, 75 insertions(+), 63 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index c1cc4a4c..287cce2b 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -58,8 +58,13 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { vhpiHandleT handle = GpiObjHdl::get_handle(); - vhpi_get_value(get_handle(), &m_value); - check_vhpi_error(); + if (0 > vhpi_get_value(get_handle(), &m_value)) { + if (vhpiSliceNameK == vhpi_get(vhpiKindP, handle)) { + m_value.format = vhpiEnumVecVal; + } else { + LOG_WARN("vhpi_get_value failed and not a vhpiSliceNameK"); + } + } LOG_DEBUG("Found %s of format type %s (%d) format object with %d elems buffsize %d size %d", name.c_str(), @@ -693,6 +698,7 @@ void vhpi_mappings(GpiIteratorMapping &map) }; map.add_to_options(vhpiCondSigAssignStmtK, &simplesig_options[0]); map.add_to_options(vhpiSimpleSigAssignStmtK, &simplesig_options[0]); + map.add_to_options(vhpiSelectSigAssignStmtK, &simplesig_options[0]); /* vhpiPortDeclK */ map.add_to_options(vhpiPortDeclK, &sig_options[0]); @@ -806,7 +812,7 @@ GpiIterator::Status VhpiIterator::next_handle(std::string &name, if (obj && (vhpiProcessStmtK == vhpi_get(vhpiKindP, obj))) { LOG_DEBUG("Skipping %s (%s)", vhpi_get_str(vhpiFullNameP, obj), - vhpi_get_str(vhpiKindStrP, obj)); + vhpi_get_str(vhpiKindStrP, obj)); obj=NULL; continue; } diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 9bf135af..e5e12c82 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -133,6 +133,7 @@ gpi_objtype_t to_gpi_objtype(vhpiIntT vhpitype) case vhpiSelectedNameK: case vhpiVarDeclK: case vhpiVarParamDeclK: + case vhpiSliceNameK: return GPI_REGISTER; case vhpiArrayTypeDeclK: @@ -157,6 +158,7 @@ gpi_objtype_t to_gpi_objtype(vhpiIntT vhpitype) case vhpiProcessStmtK: case vhpiSimpleSigAssignStmtK: case vhpiCondSigAssignStmtK: + case vhpiSelectSigAssignStmtK: return GPI_MODULE; default: @@ -187,17 +189,14 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, vhpiHandleT query_hdl; vhpiHandleT base_hdl = vhpi_handle(vhpiBaseType, new_hdl); - if (!base_hdl) - query_hdl = new_hdl; - else - query_hdl = base_hdl; + query_hdl = base_hdl ? base_hdl : new_hdl; vhpiIntT base_type = vhpi_get(vhpiKindP, query_hdl); vhpiIntT is_static = vhpi_get(vhpiStaticnessP, query_hdl); gpi_type = to_gpi_objtype(base_type); LOG_DEBUG("Creating %s of type %d (%s)", - vhpi_get_str(vhpiFullNameP, query_hdl), + vhpi_get_str(vhpiFullNameP, new_hdl), gpi_type, vhpi_get_str(vhpiKindStrP, query_hdl)); @@ -214,66 +213,70 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, } switch (base_type) { + case vhpiSliceNameK: case vhpiIndexedNameK: - case vhpiSelectedNameK: - case vhpiArrayTypeDeclK: { - /* This may well be an n dimensional array if it is - then we create a non signal object we also need to - look at the format of the array - */ - vhpiValueT value; - value.format = vhpiObjTypeVal; - value.bufSize = 0; - value.numElems = 0; - value.value.str = NULL; - vhpi_get_value(new_hdl, &value); - int num_elems = vhpi_get(vhpiSizeP, new_hdl); - - if (vhpiStrVal == value.format) { + case vhpiSelectedNameK: { + vhpiHandleT sub_type = vhpi_handle(vhpiSubtype, new_hdl); + if (base_hdl) + vhpi_release_handle(base_hdl); + + base_hdl = vhpi_handle(vhpiBaseType, sub_type); + query_hdl = base_hdl; + /* Drop though */ + } + case vhpiArrayTypeDeclK: + case vhpiEnumTypeDeclK: { + const char *type = vhpi_get_str(vhpiNameP, query_hdl); + if (0 == strcmp(type, "STD_ULOGIC") || + 0 == strcmp(type, "STD_LOGIC") || + 0 == strncmp(type, "STD_ULOGIC_VECTOR", sizeof("STD_ULOGIC_VECTOR")-1) || + 0 == strncmp(type, "STD_LOGIC_VECTOR", sizeof("STD_LOGIC_VECTOR")-1)) { + LOG_DEBUG("Detected std_logic %s", fq_name.c_str()); + logic = true; + } else if (0 == strcmp(type, "BOOLEAN") || + 0 == strcmp(type, "boolean") || + 0 == strcmp(type, "UNSIGNED")) { + LOG_DEBUG("Detected boolean/integer %s", fq_name.c_str()); + gpi_type = GPI_INTEGER; + } else if (0 == strncmp(type, "STRING", sizeof("STRING")-1)) { LOG_DEBUG("Detected a STRING type %s", fq_name.c_str()); gpi_type = GPI_STRING; - break; - } - - if (vhpiCharVal == value.format) { + } else if (0 == strcmp(type, "CHARACTER") || + 0 == strcmp(type, "character")) { LOG_DEBUG("Detected an CHAR type %s", fq_name.c_str()); gpi_type = GPI_INTEGER; - break; - } - - if (vhpiRawDataVal == value.format) { - LOG_DEBUG("Detected a RAW type %s", fq_name.c_str()); - gpi_type = GPI_MODULE; - break; - } - - /* More differences between simulators. - Aldec sets value.numElems regardless - IUS does not set for a single dimension. - */ - - if (!value.numElems || (value.numElems == num_elems)) { - LOG_DEBUG("Detected single dimension vector type", fq_name.c_str()); - gpi_type = GPI_ARRAY; - /* Do not break as want to go into vhpiEnumTypeDeclK */ } else { - LOG_DEBUG("Detected an n dimension vector type", fq_name.c_str()); - gpi_type = GPI_MODULE; - modifiable = false; - break; - } - } + /* It not a standard type then we lastly try and use the format, + we do this on the handle we where given on a sub type */ + vhpiValueT value; + value.format = vhpiObjTypeVal; + value.bufSize = 0; + value.numElems = 0; + value.value.str = NULL; + vhpi_get_value(new_hdl, &value); + int num_elems = vhpi_get(vhpiSizeP, new_hdl); + + if (vhpiStrVal == value.format) { + LOG_DEBUG("Detected a STRING type %s", fq_name.c_str()); + gpi_type = GPI_STRING; + } else if (vhpiRawDataVal == value.format) { + LOG_DEBUG("Detected a STRING type %s", fq_name.c_str()); + gpi_type = GPI_MODULE; + } else if (vhpiCharVal == value.format) { + LOG_DEBUG("Detected an CHAR type %s", fq_name.c_str()); + gpi_type = GPI_INTEGER; + } - case vhpiEnumTypeDeclK: { - int num_literals = vhpi_get(vhpiNumLiteralsP, query_hdl); - if (num_literals == 2) { - logic = false; - gpi_type = GPI_INTEGER; - LOG_DEBUG("Detected boolean %s", fq_name.c_str()); - } else { - logic = true; - LOG_DEBUG("Detected std_logic %s", fq_name.c_str()); + if (!value.numElems || (value.numElems == num_elems)) { + LOG_DEBUG("Detected single dimension vector type", fq_name.c_str()); + gpi_type = GPI_ARRAY; + } else { + LOG_DEBUG("Detected an n dimension valueector type", fq_name.c_str()); + gpi_type = GPI_MODULE; + modifiable = false; + } } + break; } @@ -296,6 +299,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, case vhpiSimpleSigAssignStmtK: case vhpiCondSigAssignStmtK: case vhpiRecordTypeDeclK: + case vhpiSelectSigAssignStmtK: modifiable = false; break; diff --git a/tests/designs/sample_module/sample_module.vhdl b/tests/designs/sample_module/sample_module.vhdl index 58612298..d13484c9 100644 --- a/tests/designs/sample_module/sample_module.vhdl +++ b/tests/designs/sample_module/sample_module.vhdl @@ -50,6 +50,7 @@ entity sample_module is stream_out_data_comb : out std_ulogic_vector(7 downto 0); stream_out_data_registered : out std_ulogic_vector(7 downto 0); + stream_out_data_wide : out std_ulogic_vector(63 downto 0); stream_out_ready : in std_ulogic; stream_out_real : out real; stream_out_int : out integer; @@ -100,6 +101,7 @@ stream_out_real <= stream_in_real; stream_out_int <= stream_in_int; stream_out_string <= stream_in_string; stream_out_bool <= stream_in_bool; +stream_out_data_wide(3 downto 2) <= stream_in_data_wide(3 downto 2); isample_module1 : component sample_module_1 generic map ( diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index b519df4c..02918d3c 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -197,7 +197,7 @@ def access_string(dut): tlog.info("Test writing under size") test_string = "cocotb" - dut.stream_in_string <= test_string + dut.stream_in_string.setimmediatevalue(test_string) varible_string = dut.stream_out_string if varible_string != '': @@ -211,7 +211,7 @@ def access_string(dut): test_string = "longer_than_the_array" tlog.info("Test writing over size with '%s'" % test_string) - dut.stream_in_string <= test_string + dut.stream_in_string.setimmediatevalue(test_string) varible_string = dut.stream_out_string yield Timer(10) @@ -239,7 +239,7 @@ def access_string(dut): for i in varible_string: lower = chr(i) upper = lower.upper() - i <= ord(upper) + i.setimmediatevalue(ord(upper)) yield Timer(10) From 053c11b31e04ae6642baa32bf7ecc21b11a62872 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 16 Oct 2015 16:21:34 +0100 Subject: [PATCH 1070/2656] Issue #17: Move log message to match VPI --- lib/vhpi/VhpiCbHdl.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 287cce2b..5b12d33c 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -846,13 +846,14 @@ GpiIterator::Status VhpiIterator::next_handle(std::string &name, const char *c_name = vhpi_get_str(vhpiCaseNameP, obj); if (!c_name) { int type = vhpi_get(vhpiKindP, obj); - LOG_WARN("Unable to get the name for this object of type %d", type); if (type < VHPI_TYPE_MIN) { *raw_hdl = (void*)obj; return GpiIterator::NOT_NATIVE_NO_NAME; } + LOG_DEBUG("Unable to get the name for this object of type %d", type); + return GpiIterator::NATIVE_NO_NAME; } name = c_name; From ca8f80bb606ce60e44182e06f92b1c1b5a570b4e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 16 Oct 2015 16:47:06 +0100 Subject: [PATCH 1071/2656] Issue #17: Fixup debug and do not check for array status in detection fallback if type known, fixup object count for IUS --- lib/vhpi/VhpiImpl.cpp | 8 ++++++-- .../test_cases/test_iteration_mixedlang/test_iteration.py | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index e5e12c82..1395dfd1 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -248,23 +248,27 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, } else { /* It not a standard type then we lastly try and use the format, we do this on the handle we where given on a sub type */ + vhpiValueT value; value.format = vhpiObjTypeVal; value.bufSize = 0; value.numElems = 0; value.value.str = NULL; - vhpi_get_value(new_hdl, &value); int num_elems = vhpi_get(vhpiSizeP, new_hdl); + vhpi_get_value(new_hdl, &value); if (vhpiStrVal == value.format) { LOG_DEBUG("Detected a STRING type %s", fq_name.c_str()); gpi_type = GPI_STRING; + break; } else if (vhpiRawDataVal == value.format) { - LOG_DEBUG("Detected a STRING type %s", fq_name.c_str()); + LOG_DEBUG("Detected a RAW type %s", fq_name.c_str()); gpi_type = GPI_MODULE; + break; } else if (vhpiCharVal == value.format) { LOG_DEBUG("Detected an CHAR type %s", fq_name.c_str()); gpi_type = GPI_INTEGER; + break; } if (!value.numElems || (value.numElems == num_elems)) { diff --git a/tests/test_cases/test_iteration_mixedlang/test_iteration.py b/tests/test_cases/test_iteration_mixedlang/test_iteration.py index 7c42a35f..2463add2 100644 --- a/tests/test_cases/test_iteration_mixedlang/test_iteration.py +++ b/tests/test_cases/test_iteration_mixedlang/test_iteration.py @@ -70,7 +70,7 @@ def recursive_discovery(dut): # vpiAlways = 31 and vpiStructVar = 2 do not show up in IUS # But vhpiSimpleSigAssignStmtK objects do, and ther are 2. # Process statements and all sub handles also show up. - pass_total = 959 + pass_total = 985 else: pass_total = 916 @@ -97,7 +97,7 @@ def recursive_discovery_boundary(dut): if cocotb.SIM_NAME in ["ncsim(64)", "ncsim"]: # # But vhpiSimpleSigAssignStmtK objects only show up on IUS, and ther are 2 - pass_total = 504 + pass_total = 530 else: pass_total = 426 From 0ed07aae1d23c3282edadd0bd27b5e6773209770 Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Thu, 22 Oct 2015 17:54:44 +1100 Subject: [PATCH 1072/2656] Adding testcase for a vhdl type define of type array. [commit from :/mnt/mag_drive/projects/justy/work/luke/justy/sim/chipsim/cocotb] --- tests/designs/sample_module/sample_module.vhdl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/designs/sample_module/sample_module.vhdl b/tests/designs/sample_module/sample_module.vhdl index 58612298..3a34fdd2 100644 --- a/tests/designs/sample_module/sample_module.vhdl +++ b/tests/designs/sample_module/sample_module.vhdl @@ -74,6 +74,8 @@ architecture impl of sample_module is ); end component sample_module_1; + type lutType is array (0 to 3, 0 to 6) of signed(10 downto 0); + function afunc(value : std_ulogic_vector) return std_ulogic_vector is variable i: integer; variable rv: std_ulogic_vector(7 downto 0); @@ -86,6 +88,10 @@ begin return rv; end afunc; + signal cosLut0, sinLut0 : lutType; + signal cosLut1, sinLut1 : lutType; + signal cosLut, sinLut : lutType; + begin process (clk) begin From 0754d5055cdff83c196e26564d34f6993a89068c Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 23 Oct 2015 10:14:27 +0100 Subject: [PATCH 1073/2656] Issue #17: Have custom type declarations on IUS whose format is returned as vhpiObjTypeVal --- lib/vhpi/VhpiCbHdl.cpp | 4 ++- lib/vhpi/VhpiImpl.cpp | 3 +- .../designs/sample_module/sample_module.vhdl | 2 +- .../test_discovery/test_discovery.py | 36 +++++++++++++++++++ 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 5b12d33c..e0175cd2 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -62,7 +62,8 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { if (vhpiSliceNameK == vhpi_get(vhpiKindP, handle)) { m_value.format = vhpiEnumVecVal; } else { - LOG_WARN("vhpi_get_value failed and not a vhpiSliceNameK"); + LOG_DEBUG("vhpi_get_value failed and not a vhpiSliceNameK setting to vhpiRawDataVal"); + m_value.format = vhpiRawDataVal; } } @@ -113,6 +114,7 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { case vhpiRawDataVal: { // This is an internal representation - the only way to determine // the size is to iterate over the members and count sub-elements + m_num_elems = 0; vhpiHandleT result = NULL; vhpiHandleT iterator = vhpi_iterator(vhpiIndexedNames, handle); diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 1395dfd1..a699f422 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -261,7 +261,8 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, LOG_DEBUG("Detected a STRING type %s", fq_name.c_str()); gpi_type = GPI_STRING; break; - } else if (vhpiRawDataVal == value.format) { + } else if (vhpiRawDataVal == value.format || + vhpiObjTypeVal == value.format) { LOG_DEBUG("Detected a RAW type %s", fq_name.c_str()); gpi_type = GPI_MODULE; break; diff --git a/tests/designs/sample_module/sample_module.vhdl b/tests/designs/sample_module/sample_module.vhdl index cf9d91a7..dd4cbd9c 100644 --- a/tests/designs/sample_module/sample_module.vhdl +++ b/tests/designs/sample_module/sample_module.vhdl @@ -50,7 +50,7 @@ entity sample_module is stream_out_data_comb : out std_ulogic_vector(7 downto 0); stream_out_data_registered : out std_ulogic_vector(7 downto 0); - stream_out_data_wide : out std_ulogic_vector(63 downto 0); + stream_out_data_wide : out std_ulogic_vector(63 downto 0); stream_out_ready : in std_ulogic; stream_out_real : out real; stream_out_int : out integer; diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 02918d3c..2ded319e 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -317,6 +317,9 @@ def skip_a_test(dut): @cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"], expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) def access_gate(dut): + """ + Test access to a gate Object + """ tlog = logging.getLogger("cocotb.test") yield Timer(10) @@ -326,3 +329,36 @@ def access_gate(dut): if not isinstance(gate, HierarchyObject): raise TestFailure("Gate should be HierarchyObject") +@cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) +def custom_type(dut): + """ + Test iteration over a custom type + """ + tlog = logging.getLogger("cocotb.test") + + yield Timer(10) + + new_type = dut.cosLut + tlog.info("cosLut object %s %s" % (new_type, type(new_type))) + + expected_top = 28 + count = 0 + + def _discover(obj): + iter_count = 0 + for elem in obj: + iter_count += 1 + iter_count += _discover(elem) + return iter_count + + expected_sub = 11 + + for sub in new_type: + tlog.info("Sub object %s %s" % (sub, type(sub))) + sub_count = _discover(sub) + if sub_count != expected_sub: + raise TestFailure("Expected %d found %d under %s" % (expected_sub, sub_count, sub)) + count += 1 + + if expected_top != count: + raise TestFailure("Expected %d found %d for cosLut" % (expected_top, count)) From 4c7aa22621c8820bc4850193a6cac0608544c9f6 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 23 Oct 2015 10:22:43 +0100 Subject: [PATCH 1074/2656] Cleanup: Tidy output of clean to match IUD --- makefiles/simulators/Makefile.aldec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index d79eced5..e0b0e998 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -125,5 +125,5 @@ results.xml: $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(COCOTB_ $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do runsim.tcl | tee sim.log clean:: - rm -rf $(SIM_BUILD) + @rm -rf $(SIM_BUILD) From dce06abf5241277f6de8e216d8dbd6e7b9d36dc8 Mon Sep 17 00:00:00 2001 From: Tristan Gingold Date: Sat, 7 Nov 2015 21:56:48 +0100 Subject: [PATCH 1075/2656] Fix support for ghdl. With this patch, you can run the endian_swapper example: If you are using the mcode version of ghdl (which is 32 bit), use the following command: make GHDL=install_dir/bin/ghdl SIM=ghdl GPI_IMPL=vhpi ARCH=i686 If you are using the llvm/gcc version of ghdl for x86-64, use that command: make GHDL=install_dir/bin/ghdl SIM=ghdl GPI_IMPL=vhpi In both case, install_dir is the ghdl installation directory. Setting the GHDL variable is not necessary if ghdl is in the PATH. Note that cocotb uses VPI for interfacing with ghdl. --- makefiles/simulators/Makefile.ghdl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/makefiles/simulators/Makefile.ghdl b/makefiles/simulators/Makefile.ghdl index e23f59b5..3ae3714e 100644 --- a/makefiles/simulators/Makefile.ghdl +++ b/makefiles/simulators/Makefile.ghdl @@ -28,15 +28,16 @@ .PHONY: analyse +GHDL=ghdl + # Compilation phase analyse: $(VHDL_SOURCES) $(SIM_BUILD) - cd $(SIM_BUILD) && ghdl -a $(VHDL_SOURCES) + cd $(SIM_BUILD) && $(GHDL) -a $(VHDL_SOURCES) && $(GHDL) -e $(TOPLEVEL) -results.xml: analyse $(COCOTB_LIBS) $(COCOTB_LIB_VPI) - cd $(SIM_BUILD) && LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) ghdl -e -Wl,-L$(LIB_DIR) -Wl,-lvpi $(TOPLEVEL) +results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VPI_LIB) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - ./$(SIM_BUILD)/$(TOPLEVEL) + $(GHDL) -r --workdir=$(SIM_BUILD) $(TOPLEVEL) --vpi=$(LIB_DIR)/libvpi.$(LIB_EXT) clean:: -@rm -rf $(SIM_BUILD) From 84a07326aa1d9c9f2020591061baef6265ac975a Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 18 Nov 2015 18:39:29 +0000 Subject: [PATCH 1076/2656] Cleanup: no need to require COCOTB to be set --- tests/test_cases/test_avalon/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/test_avalon/Makefile b/tests/test_cases/test_avalon/Makefile index 31096bb2..3d1aab47 100644 --- a/tests/test_cases/test_avalon/Makefile +++ b/tests/test_cases/test_avalon/Makefile @@ -28,6 +28,6 @@ ############################################################################### -include $(COCOTB)/tests/designs/avalon_module/Makefile +include ../../designs/avalon_module/Makefile MODULE = test_avalon From b844caad40cb99329cd96110e742b935e64963ea Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 18 Nov 2015 19:28:22 +0000 Subject: [PATCH 1077/2656] Cleanup: no need to require COCOTB to be set --- tests/test_cases/issue_253/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/issue_253/Makefile b/tests/test_cases/issue_253/Makefile index ad4b4094..72766be3 100644 --- a/tests/test_cases/issue_253/Makefile +++ b/tests/test_cases/issue_253/Makefile @@ -32,7 +32,7 @@ all: @echo "Skipping issue_253 only runs on icarus" else -include $(COCOTB)/tests/designs/sample_module/Makefile +include ../../designs/sample_module/Makefile MODULE = issue_253 From e41fd215dbc28aff442c79bc8a3fa094ca26558a Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 18 Nov 2015 19:57:09 +0000 Subject: [PATCH 1078/2656] Issue #330: Some logging tidyups during merge and make sample_module work with IUS which was upset with unpacked struct --- lib/vhpi/VhpiCbHdl.cpp | 10 ++++----- lib/vpi/VpiCbHdl.cpp | 23 +++++++++++++++++++-- lib/vpi/VpiImpl.cpp | 1 + tests/designs/sample_module/sample_module.v | 15 +++++++------- tests/test_cases/issue_330/Makefile | 2 +- tests/test_cases/issue_330/issue_330.py | 8 +++++-- 6 files changed, 41 insertions(+), 18 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 811237b2..cdc0e508 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -762,12 +762,10 @@ VhpiIterator::VhpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator } if (NULL == iterator) { - std::string name = vhpi_get_str(vhpiCaseNameP, vhpi_hdl); - LOG_WARN("vhpi_iterate return NULL for all relationships on %s (%d) kind:%s name:%s", - name.c_str(), - vhpi_get(vhpiKindP, vhpi_hdl), - vhpi_get_str(vhpiKindStrP, vhpi_hdl), - vhpi_get_str(vhpiCaseNameP, vhpi_hdl)); + LOG_DEBUG("vhpi_iterate return NULL for all relationships on %s (%d) kind:%s", + vhpi_get_str(vhpiCaseNameP, vhpi_hdl), + type, + vhpi_get_str(vhpiKindStrP, vhpi_hdl)); selected = NULL; return; } diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index a66b08f6..ac75922a 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -410,6 +410,23 @@ void vpi_mappings(GpiIteratorMapping &map) map.add_to_options(vpiModule, &module_options[0]); map.add_to_options(vpiGenScope, &module_options[0]); + int32_t struct_options[] = { + vpiNet, +#ifndef IUS + vpiNetArray, +#endif + vpiReg, + vpiRegArray, + vpiMemory, + vpiParameter, + vpiPrimitive, + vpiPrimitiveArray, + vpiAttribute, + 0 + }; + map.add_to_options(vpiStructVar, &struct_options[0]); + map.add_to_options(vpiStructNet, &struct_options[0]); + /* vpiNet */ int32_t net_options[] = { //vpiContAssign, // Driver and load handled separately @@ -432,7 +449,6 @@ void vpi_mappings(GpiIteratorMapping &map) }; map.add_to_options(vpiNetArray, &netarray_options[0]); - /* vpiRegArray */ int32_t regarray_options[] = { vpiReg, @@ -492,7 +508,10 @@ VpiIterator::VpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(i } if (NULL == iterator) { - LOG_WARN("vpi_iterate returned NULL for all types"); + LOG_DEBUG("vpi_iterate return NULL for all relationships on %s (%d) type:%s", + vpi_get_str(vpiName, vpi_hdl), + type, + vpi_get_str(vpiType, vpi_hdl)); selected = NULL; return; } diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 69edceaa..1c8fb5ca 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -102,6 +102,7 @@ gpi_objtype_t to_gpi_objtype(int32_t vpitype) case vpiStructVar: + case vpiStructNet: return GPI_STRUCTURE; case vpiModport: diff --git a/tests/designs/sample_module/sample_module.v b/tests/designs/sample_module/sample_module.v index 689e7758..b5b0965d 100644 --- a/tests/designs/sample_module/sample_module.v +++ b/tests/designs/sample_module/sample_module.v @@ -29,11 +29,13 @@ `timescale 1 ps / 1 ps -typedef struct +`ifndef __ICARUS__ +typedef struct packed { logic a_in; logic b_out; } test_if; +`endif module sample_module ( input clk, @@ -43,20 +45,17 @@ module sample_module ( `ifndef __ICARUS__ input real stream_in_real, input integer stream_in_int, + output real stream_out_real, + output integer stream_out_int, + input test_if inout_if, `endif input [7:0] stream_in_data, input [63:0] stream_in_data_wide, input stream_out_ready, -`ifndef __ICARUS__ - output real stream_out_real, - output integer stream_out_int, -`endif output reg [7:0] stream_out_data_comb, output reg [7:0] stream_out_data_registered, - inout test_if inout_if, - output and_output ); @@ -79,6 +78,8 @@ always @(stream_in_real) always @(stream_in_int) stream_out_int <= stream_in_int; + +test_if struct_var; `endif and test_and_gate(and_output, stream_in_ready, stream_in_valid); diff --git a/tests/test_cases/issue_330/Makefile b/tests/test_cases/issue_330/Makefile index 8d084d80..d7d4c67b 100644 --- a/tests/test_cases/issue_330/Makefile +++ b/tests/test_cases/issue_330/Makefile @@ -28,6 +28,6 @@ ############################################################################### -include ../..//designs/sample_module/Makefile +include ../../designs/sample_module/Makefile MODULE = issue_330 diff --git a/tests/test_cases/issue_330/issue_330.py b/tests/test_cases/issue_330/issue_330.py index 15574148..7f6dbfa7 100644 --- a/tests/test_cases/issue_330/issue_330.py +++ b/tests/test_cases/issue_330/issue_330.py @@ -7,7 +7,9 @@ from cocotb.result import TestFailure from cocotb.binary import BinaryValue -@cocotb.test() +@cocotb.test(skip=cocotb.SIM_NAME in ["Icarus Verilog"], + expect_error=cocotb.SIM_NAME in ["ModelSim ALTERA STARTER EDITION", + "ModelSim DE"]) def issue_330_direct(dut): """ Access a structure @@ -20,7 +22,9 @@ def issue_330_direct(dut): tlog.info("Value of inout_if => a_in = %d ; b_oout = %d" % (structure.a_in, structure.b_out)) -@cocotb.test() +@cocotb.test(skip=cocotb.SIM_NAME in ["Icarus Verilog"], + expect_fail=cocotb.SIM_NAME in ["ModelSim ALTERA STARTER EDITION", + "ModelSim DE"]) def issue_330_iteration(dut): """ Access a structure via issue_330_iteration From 4a35ac43169933d217e80ada71b37f24e4923aee Mon Sep 17 00:00:00 2001 From: Chiggs Date: Thu, 19 Nov 2015 08:40:36 +0000 Subject: [PATCH 1079/2656] Issue #341: Search for `riviera` rather than `vsim` to find Aldec install --- makefiles/simulators/Makefile.aldec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index e0b0e998..4ae1c89c 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -90,7 +90,7 @@ endif endif # auto-detect riviera dir from system path -export ALDEC_BIN_DIR = $(shell dirname $(shell which vsim)) +export ALDEC_BIN_DIR = $(shell dirname $(shell which riviera)) ifeq ($(ALDEC_BIN_DIR),) $(error "Directory containing Riviera-Pro binaries must be included in system path") From 65a91f7c551fa2b123071848625840134e27c5fe Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Tue, 1 Dec 2015 11:03:51 +0100 Subject: [PATCH 1080/2656] Non existant hdl name is not necessarily a warning --- lib/gpi/GpiCommon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 25d10346..c9c5fab8 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -309,7 +309,7 @@ gpi_sim_hdl gpi_get_handle_by_name(gpi_sim_hdl parent, const char *name) GpiObjHdl *base = sim_to_hdl(parent); GpiObjHdl *hdl = __gpi_get_handle_by_name(base, s_name, NULL); if (!hdl) { - LOG_WARN("Failed to find a hdl named %s via any registered implementation", + LOG_DEBUG("Failed to find a hdl named %s via any registered implementation", name); } return hdl; From c44d48be74d0b1eb3fd4d6660b849a61742f2f5b Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Sun, 13 Dec 2015 17:41:26 -0700 Subject: [PATCH 1081/2656] Fixes Issue 349 by adding support for RTL_LIBRARY in Makefile.questa --- makefiles/simulators/Makefile.questa | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 493d2e1d..29d6a32c 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -27,6 +27,8 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +RTL_LIBRARY ?= work + ifdef VERILOG_INCLUDE_DIRS VLOG_ARGS += +incdir+$(VERILOG_INCLUDE_DIRS) endif @@ -54,15 +56,15 @@ VSIM_ARGS += $(foreach gen, $(GENERICS),"-G $(gen)") endif $(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) | $(SIM_BUILD) - echo "if [file exists work] {vdel -all}" > $@ - echo "vlib work" > $@ + echo "if [file exists $(RTL_LIBRARY)] {vdel -all}" > $@ + echo "vlib $(RTL_LIBRARY)" >> $@ ifneq ($(VHDL_SOURCES),) - echo "vcom $(VCOM_ARGS) $(VHDL_SOURCES)" >> $@ + echo "vcom -work $(RTL_LIBRARY) $(VCOM_ARGS) $(VHDL_SOURCES)" >> $@ endif ifneq ($(VERILOG_SOURCES),) - echo "vlog -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(VERILOG_SOURCES)" >> $@ + echo "vlog -work $(RTL_LIBRARY) -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(VERILOG_SOURCES)" >> $@ endif - echo "vsim $(VSIM_ARGS) $(TOPLEVEL)" >> $@ + echo "vsim $(VSIM_ARGS) $(RTL_LIBRARY).$(TOPLEVEL)" >> $@ ifeq ($(WAVES),1) echo "log -recursive /*" >> $@ endif From 9cb072eef7cbbb697a47b5a6b1dcb4a13f611965 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Sun, 13 Dec 2015 18:42:00 -0700 Subject: [PATCH 1082/2656] Fixes Issue #352 by moving the declarations before any assignment statements --- tests/designs/avalon_module/burst_read_master.v | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/designs/avalon_module/burst_read_master.v b/tests/designs/avalon_module/burst_read_master.v index c99ade92..c2d4b2a1 100644 --- a/tests/designs/avalon_module/burst_read_master.v +++ b/tests/designs/avalon_module/burst_read_master.v @@ -57,10 +57,6 @@ localparam add_ram_output_register = "OFF"; localparam underflow_checking = "OFF"; localparam overflow_checking = "OFF"; -assign q = buf_out; -assign empty = buf_empty; -assign usedw = fifo_counter; - reg[lpm_width-1:0] buf_out; reg buf_empty, buf_full; reg[lpm_numwords :0] fifo_counter; @@ -68,6 +64,10 @@ reg[lpm_numwords :0] fifo_counter; reg[`FIFODEPTH_LOG2_DEF-1:0] rd_ptr, wr_ptr; reg[lpm_width:0] buf_mem[lpm_numwords-1 : 0]; +assign q = buf_out; +assign empty = buf_empty; +assign usedw = fifo_counter; + always @(fifo_counter) begin buf_empty = (fifo_counter==0); From 893e3227e1cad29ee6875e06d2806d893ff6969b Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 14 Dec 2015 09:08:25 +0000 Subject: [PATCH 1083/2656] Issue #348: Add test cases for Rising/Falling/Either on the same signal --- tests/test_cases/issue_348/Makefile | 31 +++++++++++ tests/test_cases/issue_348/issue_348.py | 71 +++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 tests/test_cases/issue_348/Makefile create mode 100644 tests/test_cases/issue_348/issue_348.py diff --git a/tests/test_cases/issue_348/Makefile b/tests/test_cases/issue_348/Makefile new file mode 100644 index 00000000..931320d4 --- /dev/null +++ b/tests/test_cases/issue_348/Makefile @@ -0,0 +1,31 @@ +############################################################################### +# Copyright (c) 2015 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +include ../../designs/sample_module/Makefile + +MODULE = issue_348 diff --git a/tests/test_cases/issue_348/issue_348.py b/tests/test_cases/issue_348/issue_348.py new file mode 100644 index 00000000..eebe5f31 --- /dev/null +++ b/tests/test_cases/issue_348/issue_348.py @@ -0,0 +1,71 @@ +import cocotb +from cocotb.log import SimLog +from cocotb.triggers import Timer, Edge, RisingEdge, FallingEdge, Join +from cocotb.result import TestFailure, ReturnValue + +import sys + +@cocotb.coroutine +def clock_gen(signal, num): + for x in range(num): + signal <= 0 + yield Timer(500) + signal <= 1 + yield Timer(500) + +@cocotb.coroutine +def signal_mon(signal, idx, edge): + log = SimLog("cocotb.signal_mon.%d.%s" % (idx, signal.name)) + value = signal.value + + edges = 0 + + while True: + yield edge(signal) + edges += 1 + + raise ReturnValue(edges) + +class DualMonitor: + def __init__(self, edge, signal): + self.log = SimLog("cocotb.%s.%s" % (edge, signal)) + self.edge_type = edge + self.monitor_edges = [0, 0] + self.signal = signal + + @cocotb.coroutine + def signal_mon(self, signal, idx, edge): + while True: + yield edge(signal) + self.monitor_edges[idx] += 1 + + @cocotb.coroutine + def start(self): + clock_edges = 10 + + cocotb.fork(clock_gen(self.signal, clock_edges)) + first = cocotb.fork(self.signal_mon(self.signal, 0, self.edge_type)) + second = cocotb.fork(self.signal_mon(self.signal, 1, self.edge_type)) + + yield Timer(10000) + + for mon in self.monitor_edges: + if not mon: + raise TestFailure("Monitor saw nothing") + + + +@cocotb.test() +def issue_348_rising(dut): + """ Start two monitors on RisingEdge """ + yield DualMonitor(RisingEdge, dut.clk).start() + +@cocotb.test() +def issue_348_falling(dut): + """ Start two monitors on FallingEdge """ + yield DualMonitor(FallingEdge, dut.clk).start() + +@cocotb.test() +def issue_348_either(dut): + """ Start two monitors on Edge """ + yield DualMonitor(Edge, dut.clk).start() From b05b1597a71df945b6559587d7fbb88111a57735 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 15 Dec 2015 08:11:39 -0700 Subject: [PATCH 1084/2656] Fixes Issue #355 by updating the Makefiles to support Questa --- tests/designs/uart2bus/Makefile | 3 +++ tests/designs/viterbi_decoder_axi4s/Makefile | 3 +++ 2 files changed, 6 insertions(+) diff --git a/tests/designs/uart2bus/Makefile b/tests/designs/uart2bus/Makefile index 95c568eb..c67d9a78 100644 --- a/tests/designs/uart2bus/Makefile +++ b/tests/designs/uart2bus/Makefile @@ -29,6 +29,9 @@ endif ifeq ($(SIM),modelsim) GPI_EXTRA=fli endif +ifeq ($(SIM),questa) + GPI_EXTRA=fli +endif ifeq ($(SIM),ius) GPI_EXTRA=vhpi endif diff --git a/tests/designs/viterbi_decoder_axi4s/Makefile b/tests/designs/viterbi_decoder_axi4s/Makefile index 4af38edf..45748628 100644 --- a/tests/designs/viterbi_decoder_axi4s/Makefile +++ b/tests/designs/viterbi_decoder_axi4s/Makefile @@ -59,6 +59,9 @@ endif ifeq ($(SIM),modelsim) GPI_IMPL=fli endif +ifeq ($(SIM),questa) + GPI_IMPL=fli +endif ifeq ($(SIM),ius) GPI_IMPL=vhpi endif From c8f583cafdefe411ca00ac81eb64954f17fe04bb Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 15 Dec 2015 15:51:27 +0000 Subject: [PATCH 1085/2656] Issue #348: Change Edge to be a singleton for a signal as per Falling/Rising --- cocotb/handle.py | 3 ++- cocotb/triggers.py | 58 ++++++++++++++++++++++++---------------------- 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 3141696a..f9146785 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -48,7 +48,7 @@ from cocotb.binary import BinaryValue from cocotb.log import SimLog from cocotb.result import TestError -from cocotb.triggers import _RisingEdge, _FallingEdge +from cocotb.triggers import _RisingEdge, _FallingEdge, _Edge from cocotb.utils import get_python_integer_types # Only issue a warning for each deprecated attribute access @@ -345,6 +345,7 @@ def __init__(self, handle): NonHierarchyObject.__init__(self, handle) self._r_edge = _RisingEdge(self) self._f_edge = _FallingEdge(self) + self._e_edge = _Edge(self) def __hash__(self): return self._handle diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 5ed2c5f6..4c8da7f3 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -128,31 +128,6 @@ def prime(self, callback): def __str__(self): return self.__class__.__name__ + "(%dps)" % self.time_ps - -class Edge(GPITrigger): - """ - Execution will resume when an edge occurs on the provided signal - """ - def __init__(self, signal): - GPITrigger.__init__(self) - self.signal = signal - - def prime(self, callback): - """Register notification of a value change via a callback""" - if self.cbhdl is None: - self.cbhdl = simulator.register_value_change_callback(self.signal. - _handle, - callback, - 3, - self) - if self.cbhdl is None: - raise_error(self, "Unable set up %s Trigger" % (str(self))) - Trigger.prime(self) - - def __str__(self): - return self.__class__.__name__ + "(%s)" % self.signal.name - - class _ReadOnly(GPITrigger): """ Execution will resume when the readonly portion of the sim cycles is @@ -229,9 +204,36 @@ def NextTimeStep(): return _nxts -class _RisingOrFallingEdge(Edge): +class _Edge(GPITrigger): + """ + Execution will resume when an edge occurs on the provided signal + """ + def __init__(self, signal): + GPITrigger.__init__(self) + self.signal = signal + + def prime(self, callback): + """Register notification of a value change via a callback""" + if self.cbhdl is None: + self.cbhdl = simulator.register_value_change_callback(self.signal. + _handle, + callback, + 3, + self) + if self.cbhdl is None: + raise_error(self, "Unable set up %s Trigger" % (str(self))) + Trigger.prime(self) + + def __str__(self): + return self.__class__.__name__ + "(%s)" % self.signal.name + +def Edge(signal): + return signal._e_edge + + +class _RisingOrFallingEdge(_Edge): def __init__(self, signal, rising): - Edge.__init__(self, signal) + _Edge.__init__(self, signal) if rising is True: self._rising = 1 else: @@ -276,7 +278,7 @@ def FallingEdge(signal): return signal._f_edge -class ClockCycles(Edge): +class ClockCycles(_Edge): """ Execution will resume after N rising edges """ From eed8db766c180fb31ac1a02e67190a3d8aca033a Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 19 Dec 2015 09:06:31 +0000 Subject: [PATCH 1086/2656] Fixes #360: Use readdatavalid signal if present on AvalonMM --- cocotb/drivers/avalon.py | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index ec79112e..4faabae7 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -81,7 +81,9 @@ def __init__(self, entity, name, clock): if hasattr(self.bus, "cs"): self.bus.cs.setimmediatevalue(0) - self.bus.address.setimmediatevalue(0) + v = self.bus.address.value + v.binstr = "x" * len(self.bus.address) + self.bus.address.setimmediatevalue(v) def read(self, address): pass @@ -140,16 +142,6 @@ def read(self, address, sync=True): # Wait for waitrequest to be low if hasattr(self.bus, "waitrequest"): yield self._wait_for_nsignal(self.bus.waitrequest) - - # Assume readLatency = 1 - # FIXME need to configure this, - # should take a dictionary of Avalon properties. - yield RisingEdge(self.clock) - - # Get the data - yield ReadOnly() - data = self.bus.readdata.value - yield RisingEdge(self.clock) # Deassert read @@ -158,6 +150,24 @@ def read(self, address, sync=True): self.bus.byteenable <= 0 if hasattr(self.bus, "cs"): self.bus.cs <= 0 + v = self.bus.address.value + v.binstr = "x" * len(self.bus.address) + self.bus.address <= v + + if hasattr(self.bus, "readdatavalid"): + while True: + yield ReadOnly() + if int(self.bus.readdatavalid): + break + yield RisingEdge(self.clock) + else: + # Assume readLatency = 1 if no readdatavalid + # FIXME need to configure this, + # should take a dictionary of Avalon properties. + yield ReadOnly() + + # Get the data + data = self.bus.readdata.value self._release_lock() raise ReturnValue(data) @@ -196,6 +206,9 @@ def write(self, address, value): self.bus.byteenable <= 0 if hasattr(self.bus, "cs"): self.bus.cs <= 0 + v = self.bus.address.value + v.binstr = "x" * len(self.bus.address) + self.bus.address <= v v = self.bus.writedata.value v.binstr = "x" * len(self.bus.writedata) From d0b6d0681093d0bf8d079e53a50851de06a2d5fc Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 19 Dec 2015 09:14:22 +0000 Subject: [PATCH 1087/2656] Attempt a workaround for travis-ci/travis-ci/#5301 --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index bf2b0e82..44b0b21f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,8 @@ python: before_install: - sudo add-apt-repository -y ppa:team-electronics/ppa - sudo apt-get update -qq + # Attempt to work-around for travis-ci issue #5301 + - unset PYTHON_CFLAGS install: - sudo apt-get install -qq iverilog-daily - pip install coverage From 5bf91e64d4a8b6b426af22a4dd6109e8d1ad65db Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 19 Dec 2015 09:28:36 +0000 Subject: [PATCH 1088/2656] Issue #362: Extract some more debug from travis --- .travis.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index bf2b0e82..e4f7739c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ env: global: - # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created - # via the "travis encrypt" command using the project repo's public key + # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created via the "travis encrypt" command using the project repo's public key - secure: "YTKZKkNH8t11bZCtR4nMjszifW+QlIAluDAthYq8I4EpuRSKQ1ntVi9MnDs6EQu8CD9LfFOeQ0wymRU71FOf22n54sCVQgbo8OcwHXa/3PUeWi9A/i+BqRZ7iCdeGFJH8rQaCWov6mCyVjjdie52kwWv3jy1ZaTeuUTX54EKFII=" language: python python: @@ -15,15 +14,14 @@ install: - pip install coverage - pip install xunitparser - if [ $( python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") -eq 3 ]; then ln -s /opt/python/3.4.2/bin/python3.4-config /home/travis/virtualenv/python3.4/bin/python-config; fi + - if [ $( python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") -eq 3 ]; then export LD_LIBRARY_PATH=/opt/python/3.4.2/lib:$LD_LIBRARY_PATH; fi + - which python-config - python-config --ldflags -# - export COVERAGE=0 - export PYTHONPATH=$PYTHONPATH:$(python -c "from __future__ import print_function; from distutils.sysconfig import get_python_lib; print(get_python_lib())") - - export LD_LIBRARY_PATH=/opt/python/3.4.2/lib:$LD_LIBRARY_PATH -# Build iverilog from source -# - sudo apt-get install gperf -# - wget -O iverilog-master.zip https://github.com/steveicarus/iverilog/archive/master.zip -# - unzip iverilog-master.zip -# - cd iverilog-master && autoconf && ./configure && make && sudo make install && cd .. + - echo $PYTHONPATH + - echo $LD_LIBRARY_PATH + - which python + - python --version script: - make test addons: From 1ccf947db3d57b0b93ae3026e5ed033beed7c04d Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 19 Dec 2015 09:32:08 +0000 Subject: [PATCH 1089/2656] Issue #362: Attempt to find python-config for 2.7 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index e4f7739c..87364e8c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,6 +22,7 @@ install: - echo $LD_LIBRARY_PATH - which python - python --version + - find /home/travis/virtualenv/python2.7.9/ -name "*config*" script: - make test addons: From 872465af2ab72a2dbefd35972ca44039eaf68f6c Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 19 Dec 2015 11:22:31 +0000 Subject: [PATCH 1090/2656] Issue #362: Trim PATH to avoid picking up system Python install --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 87364e8c..69c6ec1f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,8 @@ python: - "2.7" - "3.4" before_install: + # Attempt workaround for travis-ci/travis-ci/#5326 + - export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g") - sudo add-apt-repository -y ppa:team-electronics/ppa - sudo apt-get update -qq install: From 2e7cfb56b6f61dea8eeca5e87bb18a306d666230 Mon Sep 17 00:00:00 2001 From: Chiggs Date: Sat, 19 Dec 2015 11:27:09 +0000 Subject: [PATCH 1091/2656] Issue #362: Cleanup .travis.yml before merge --- .travis.yml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 00906889..6537a9d1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,26 +7,17 @@ python: - "2.7" - "3.4" before_install: - # Attempt workaround for travis-ci/travis-ci/#5326 + # workaround for travis-ci/travis-ci/#5326 - export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g") - sudo add-apt-repository -y ppa:team-electronics/ppa - sudo apt-get update -qq - # Attempt to work-around for travis-ci issue #5301 - - unset PYTHON_CFLAGS install: - sudo apt-get install -qq iverilog-daily - pip install coverage - pip install xunitparser - if [ $( python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") -eq 3 ]; then ln -s /opt/python/3.4.2/bin/python3.4-config /home/travis/virtualenv/python3.4/bin/python-config; fi - if [ $( python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") -eq 3 ]; then export LD_LIBRARY_PATH=/opt/python/3.4.2/lib:$LD_LIBRARY_PATH; fi - - which python-config - - python-config --ldflags - export PYTHONPATH=$PYTHONPATH:$(python -c "from __future__ import print_function; from distutils.sysconfig import get_python_lib; print(get_python_lib())") - - echo $PYTHONPATH - - echo $LD_LIBRARY_PATH - - which python - - python --version - - find /home/travis/virtualenv/python2.7.9/ -name "*config*" script: - make test addons: From 1ce4370763ea160b35f9ae6a0fb3248e07feaf8f Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Sat, 19 Dec 2015 23:29:11 -0700 Subject: [PATCH 1092/2656] Fixes issue #365 --- cocotb/handle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index f9146785..c9fd5d2a 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -138,7 +138,7 @@ def __setattr__(self, name, value): """ if name.startswith("_") or name in self._compat_mapping: return object.__setattr__(self, name, value) - if self.__hasattr__(name): + if self.__hasattr__(name) is not None: return getattr(self, name)._setcachedvalue(value) raise AttributeError("Attempt to access %s which isn't present in %s" %( name, self._name)) From 2d6132fdc734b8b816e7d9944564ab97766f344f Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Sat, 19 Dec 2015 23:36:24 -0700 Subject: [PATCH 1093/2656] Add initial GPI Object Type support and fixes issue #364. --- lib/fli/FliImpl.cpp | 150 ++++++++++++++++++++++++++++++++++---------- lib/fli/FliImpl.h | 104 ++++++++++++++---------------- 2 files changed, 164 insertions(+), 90 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index a4fb1942..58472d2e 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -100,6 +100,7 @@ void handle_fli_callback(void *data) } // extern "C" + void FliImpl::sim_end(void) { const char *stop = "stop"; @@ -107,22 +108,10 @@ void FliImpl::sim_end(void) mti_Cmd(stop); } -GpiObjHdl* FliImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) -{ - LOG_WARN("%s implementation can not create from raw handle", - get_name_c()); - return NULL; -} - -/** - * @name Native Check Create - * @brief Determine whether a simulation object is native to FLI and create - * a handle if it is - */ -GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) +GpiObjHdl *FliImpl::create_gpi_obj(std::string &name, std::string &fq_name) { GpiObjHdl *new_obj = NULL; - std::string fq_name = parent->get_name() + "/" + name; + std::vector writable(fq_name.begin(), fq_name.end()); writable.push_back('\0'); @@ -130,17 +119,61 @@ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) mtiSignalIdT sig_hdl; mtiVariableIdT var_hdl; - LOG_DEBUG("Looking for child %s from %s", name.c_str(), parent->get_name_str()); - if ((rgn_hdl = mti_FindRegion(&writable[0])) != NULL) { - LOG_DEBUG("Found a region %s -> %p", &writable[0], rgn_hdl); - new_obj = new FliRegionObjHdl(this, rgn_hdl); + LOG_DEBUG("Found region %s -> %p", fq_name.c_str(), rgn_hdl); + new_obj = new GpiObjHdl(this, rgn_hdl, GPI_MODULE); } else if ((sig_hdl = mti_FindSignal(&writable[0])) != NULL) { - LOG_DEBUG("Found a signal %s -> %p", &writable[0], sig_hdl); - new_obj = new FliSignalObjHdl(this, sig_hdl); + LOG_DEBUG("Found a signal %s -> %p", fq_name.c_str(), sig_hdl); + + gpi_objtype_t objtype; + mtiTypeKindT typeKind = mti_GetTypeKind(mti_GetSignalType(sig_hdl)); + + switch (typeKind) { + case MTI_TYPE_ENUM: + objtype = GPI_REGISTER; // Assumes std_logic + break; + case MTI_TYPE_SCALAR: + case MTI_TYPE_PHYSICAL: + objtype = GPI_INTEGER; + break; + case MTI_TYPE_ARRAY: + objtype = GPI_REGISTER; // Assumes std_logic_vector + break; + default: + LOG_ERROR("Unable to handle object type for %s (%d)", + name.c_str(), typeKind); + } + + new_obj = new FliSignalObjHdl(this, sig_hdl, objtype); + } else if ((var_hdl = mti_FindVar(&writable[0])) != NULL) { - LOG_DEBUG("Found a variable %s -> %p", &writable[0], var_hdl); - new_obj = new FliVariableObjHdl(this, var_hdl); + LOG_DEBUG("Found a variable %s -> %p", fq_name.c_str(), var_hdl); + + gpi_objtype_t objtype; + int varKind = mti_GetVarKind(var_hdl); + mtiTypeKindT typeKind = mti_GetTypeKind(mti_GetVarType(var_hdl)); + + switch (typeKind) { + case MTI_TYPE_ENUM: + objtype = GPI_REGISTER; // Assumes std_logic + break; + case MTI_TYPE_SCALAR: + case MTI_TYPE_PHYSICAL: + objtype = GPI_INTEGER; + break; + case MTI_TYPE_ARRAY: + objtype = GPI_REGISTER; // Assumes std_logic_vector + break; + default: + LOG_ERROR("Unable to handle object type for %s (%d)", + name.c_str(), typeKind); + } + + new_obj = new FliVariableObjHdl(this, var_hdl, objtype, (varKind != accVariable)); + } + else { + LOG_DEBUG("Unable to query %s", fq_name.c_str()); + return NULL; } if (NULL == new_obj) { @@ -148,10 +181,39 @@ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) return NULL; } - new_obj->initialise(name, fq_name); + new_obj->initialise(name,fq_name); return new_obj; } +GpiObjHdl* FliImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) +{ + LOG_WARN("%s implementation can not create from raw handle", + get_name_c()); + return NULL; +} + +/** + * @name Native Check Create + * @brief Determine whether a simulation object is native to FLI and create + * a handle if it is + */ +GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) +{ + std::string fq_name = parent->get_fullname(); + if (fq_name == "/") { + fq_name += name; + } else { + fq_name += "/" + name; + } + std::vector writable(fq_name.begin(), fq_name.end()); + writable.push_back('\0'); + + + LOG_DEBUG("Looking for child %s from %s", name.c_str(), parent->get_name_str()); + + return create_gpi_obj(name, fq_name); +} + /** * @name Native Check Create * @brief Determine whether a simulation object is native to FLI and create @@ -159,7 +221,18 @@ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) */ GpiObjHdl* FliImpl::native_check_create(uint32_t index, GpiObjHdl *parent) { - return NULL; + char buff[15]; + snprintf(buff, 15, "(%u)", index); + std::string idx = buff; + std::string name = parent->get_name() + idx; + std::string fq_name = parent->get_fullname() + idx; + std::vector writable(fq_name.begin(), fq_name.end()); + writable.push_back('\0'); + + + LOG_DEBUG("Looking for index %u from %s", index, parent->get_name_str()); + + return create_gpi_obj(name, fq_name); } const char *FliImpl::reason_to_string(int reason) @@ -194,9 +267,10 @@ void FliImpl::get_sim_time(uint32_t *high, uint32_t *low) GpiObjHdl *FliImpl::get_root_handle(const char *name) { mtiRegionIdT root; - GpiObjHdl *rv; char *rgn_name; + char *rgn_fullname; std::string root_name; + std::string root_fullname; for (root = mti_GetTopRegion(); root != NULL; root = mti_NextRegion(root)) { LOG_DEBUG("Iterating over: %s", mti_GetRegionName(root)); @@ -208,18 +282,16 @@ GpiObjHdl *FliImpl::get_root_handle(const char *name) goto error; } - rgn_name = mti_GetRegionFullName(root); + rgn_name = mti_GetRegionName(root); + rgn_fullname = mti_GetRegionFullName(root); - root_name = rgn_name; - mti_VsimFree(rgn_name); + root_name = rgn_name; + root_fullname = rgn_fullname; + mti_VsimFree(rgn_fullname); LOG_DEBUG("Found toplevel: %s, creating handle....", root_name.c_str()); - rv = new FliRegionObjHdl(this, root); - rv->initialise(root_name, root_name); - - LOG_DEBUG("Returning root handle %p", rv); - return rv; + return create_gpi_obj(root_name, root_fullname); error: @@ -407,6 +479,8 @@ static const char value_enum[10] = "UX01ZWLH-"; const char* FliSignalObjHdl::get_signal_value_binstr(void) { + mtiSignalIdT m_fli_hdl = get_handle(); + switch (m_fli_type) { case MTI_TYPE_ENUM: @@ -466,6 +540,8 @@ int FliSignalObjHdl::set_signal_value(const long value) { int rc; char buff[20]; + mtiSignalIdT m_fli_hdl = get_handle(); + snprintf(buff, 20, "16#%016X", (int)value); @@ -480,6 +556,8 @@ int FliSignalObjHdl::set_signal_value(const long value) int FliSignalObjHdl::set_signal_value(std::string &value) { int rc; + mtiSignalIdT m_fli_hdl = get_handle(); + snprintf(m_val_str_buff, m_val_str_len+1, "%d'b%s", m_val_len, value.c_str()); @@ -498,6 +576,8 @@ int FliSignalObjHdl::set_signal_value(const double value) int FliSignalObjHdl::initialise(std::string &name, std::string &fq_name) { + mtiSignalIdT m_fli_hdl = get_handle(); + /* Pre allocte buffers on signal type basis */ m_fli_type = mti_GetTypeKind(mti_GetSignalType(m_fli_hdl)); @@ -547,6 +627,8 @@ GpiCbHdl *FliVariableObjHdl::value_change_cb(unsigned int edge) const char* FliVariableObjHdl::get_signal_value_binstr(void) { + mtiVariableIdT m_fli_hdl = get_handle(); + switch (m_fli_type) { case MTI_TYPE_ENUM: @@ -622,6 +704,8 @@ int FliVariableObjHdl::set_signal_value(const double value) int FliVariableObjHdl::initialise(std::string &name, std::string &fq_name) { + mtiVariableIdT m_fli_hdl = get_handle(); + /* Pre allocte buffers on signal type basis */ m_fli_type = mti_GetTypeKind(mti_GetVarType(m_fli_hdl)); diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index e9d0970e..90be156b 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -71,20 +71,19 @@ class FliSignalCbHdl : public FliProcessCbHdl, public GpiValueCbHdl { mtiSignalIdT m_sig_hdl; }; -class FliSignalObjHdl : public GpiSignalObjHdl { +class FliValueObjHdl : public GpiSignalObjHdl { public: - FliSignalObjHdl(GpiImplInterface *impl, mtiSignalIdT hdl) : GpiSignalObjHdl(impl, hdl, GPI_UNKNOWN, false), - m_fli_hdl(hdl), - m_rising_cb(impl, this, GPI_RISING), - m_falling_cb(impl, this, GPI_FALLING), - m_either_cb(impl, this, GPI_FALLING | GPI_RISING), - m_fli_type(MTI_TYPE_SCALAR), - m_mti_buff(NULL), - m_val_buff(NULL), - m_val_str_buff(NULL), - m_val_len(0), - m_val_str_len(0) { } - virtual ~FliSignalObjHdl() { + FliValueObjHdl(GpiImplInterface *impl, + void *hdl, + gpi_objtype_t objtype, + bool is_const) : GpiSignalObjHdl(impl, hdl, objtype, is_const), + m_fli_type(MTI_TYPE_SCALAR), + m_mti_buff(NULL), + m_val_buff(NULL), + m_val_str_buff(NULL), + m_val_len(0), + m_val_str_len(0) { } + virtual ~FliValueObjHdl() { if (m_val_buff) free(m_val_buff); if (m_mti_buff) @@ -93,6 +92,25 @@ class FliSignalObjHdl : public GpiSignalObjHdl { free(m_val_str_buff); } +protected: + mtiTypeKindT m_fli_type; + mtiInt32T *m_mti_buff; + char *m_val_buff; + char *m_val_str_buff; + int m_val_len; + int m_val_str_len; +}; + +class FliSignalObjHdl : public FliValueObjHdl { +public: + FliSignalObjHdl(GpiImplInterface *impl, + mtiSignalIdT hdl, + gpi_objtype_t objtype) : FliValueObjHdl(impl, hdl, objtype, false), + m_rising_cb(impl, this, GPI_RISING), + m_falling_cb(impl, this, GPI_FALLING), + m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } + virtual ~FliSignalObjHdl() { } + const char* get_signal_value_binstr(void); const char* get_signal_value_str(void); double get_signal_value_real(void); @@ -101,38 +119,25 @@ class FliSignalObjHdl : public GpiSignalObjHdl { int set_signal_value(const long value); int set_signal_value(const double value); int set_signal_value(std::string &value); - int initialise(std::string &name, std::string &fq_name); + + /* Value change callback accessor */ GpiCbHdl *value_change_cb(unsigned int edge); + int initialise(std::string &name, std::string &fq_name); protected: - mtiSignalIdT m_fli_hdl; FliSignalCbHdl m_rising_cb; FliSignalCbHdl m_falling_cb; FliSignalCbHdl m_either_cb; - -private: - mtiTypeKindT m_fli_type; - mtiInt32T *m_mti_buff; - char *m_val_buff; - char *m_val_str_buff; - int m_val_len; - int m_val_str_len; }; -class FliVariableObjHdl : public GpiSignalObjHdl { +class FliVariableObjHdl : public FliValueObjHdl { public: - FliVariableObjHdl(GpiImplInterface *impl, mtiVariableIdT hdl) : GpiSignalObjHdl(impl, hdl, GPI_UNKNOWN, true), - m_fli_hdl(hdl), - m_fli_type(MTI_TYPE_SCALAR), - m_mti_buff(NULL), - m_val_buff(NULL), - m_val_len(0) { } - virtual ~FliVariableObjHdl() { - if (m_val_buff) - free(m_val_buff); - if (m_mti_buff) - free(m_mti_buff); - } + FliVariableObjHdl(GpiImplInterface *impl, + mtiVariableIdT hdl, + gpi_objtype_t objtype, + bool is_const) : FliValueObjHdl(impl, hdl, objtype, is_const) { } + + virtual ~FliVariableObjHdl() { } const char* get_signal_value_binstr(void); const char* get_signal_value_str(void); @@ -142,17 +147,10 @@ class FliVariableObjHdl : public GpiSignalObjHdl { int set_signal_value(const long value); int set_signal_value(std::string &value); int set_signal_value(const double value); - int initialise(std::string &name, std::string &fq_name); - GpiCbHdl *value_change_cb(unsigned int edge); -protected: - mtiVariableIdT m_fli_hdl; - -private: - mtiTypeKindT m_fli_type; - mtiInt32T *m_mti_buff; - char *m_val_buff; - int m_val_len; + /* Value change callback accessor */ + GpiCbHdl *value_change_cb(unsigned int edge); + int initialise(std::string &name, std::string &fq_name); }; @@ -201,17 +199,6 @@ class FliShutdownCbHdl : public GpiCbHdl { virtual ~FliShutdownCbHdl() { } }; - -class FliRegionObjHdl : public GpiObjHdl { -public: - FliRegionObjHdl(GpiImplInterface *impl, mtiRegionIdT hdl) : GpiObjHdl(impl), - m_fli_hdl(hdl) { } - virtual ~FliRegionObjHdl() { } - -protected: - mtiRegionIdT m_fli_hdl; -}; - class FliImpl; class FliTimedCbHdl; class FliTimerCache { @@ -257,6 +244,9 @@ class FliImpl : public GpiImplInterface { /* Method to provide strings from operation types */ const char *reason_to_string(int reason); + /* Method to provide strings from operation types */ + GpiObjHdl *create_gpi_obj(std::string &name, std::string &fq_name); + private: FliReadOnlyCbHdl m_readonly_cbhdl; FliNextPhaseCbHdl m_nexttime_cbhdl; From 7fb7b17829683200bb4fbc51f3716e49b653fde1 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 21 Dec 2015 20:08:31 -0700 Subject: [PATCH 1094/2656] Re-structure FLI Source files --- lib/Makefile | 2 +- lib/fli/FliCbHdl.cpp | 128 ++++++++++++++ lib/fli/FliImpl.cpp | 394 +----------------------------------------- lib/fli/FliImpl.h | 7 + lib/fli/FliObjHdl.cpp | 321 ++++++++++++++++++++++++++++++++++ lib/fli/Makefile | 4 +- 6 files changed, 460 insertions(+), 396 deletions(-) create mode 100644 lib/fli/FliCbHdl.cpp create mode 100644 lib/fli/FliObjHdl.cpp diff --git a/lib/Makefile b/lib/Makefile index 7dc22aa5..d04b7bbb 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -61,7 +61,7 @@ $(LIB_DIR)/libvhpi.$(LIB_EXT): $(SIM_ROOT)/lib/vhpi/VhpiImpl.cpp $(SIM_ROOT)/lib $(LIB_DIR)/libgpi.$(LIB_EXT): $(SIM_ROOT)/lib/gpi/GpiCommon.cpp $(SIM_ROOT)/lib/gpi/GpiCbHdl.cpp | $(LIB_DIR) make -C $(SIM_ROOT)/lib/gpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) -$(LIB_DIR)/libfli.$(LIB_EXT): $(SIM_ROOT)/lib/fli/FliImpl.cpp | $(LIB_DIR) +$(LIB_DIR)/libfli.$(LIB_EXT): $(SIM_ROOT)/lib/fli/FliImpl.cpp $(SIM_ROOT)/lib/fli/FliCbHdl.cpp $(SIM_ROOT)/lib/fli/FliObjHdl.cpp | $(LIB_DIR) make -C $(SIM_ROOT)/lib/fli EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) $(LIB_DIR)/libsim.$(LIB_EXT): $(SIM_ROOT)/lib/simulator/simulatormodule.c | $(LIB_DIR) diff --git a/lib/fli/FliCbHdl.cpp b/lib/fli/FliCbHdl.cpp new file mode 100644 index 00000000..5a347131 --- /dev/null +++ b/lib/fli/FliCbHdl.cpp @@ -0,0 +1,128 @@ +/****************************************************************************** +* Copyright (c) 2014 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +#include "FliImpl.h" + +/** + * @name cleanup callback + * @brief Called while unwinding after a GPI callback + * + * We keep the process but de-sensitise it + * + * NB need a way to determine if should leave it sensitised, hmmm... + * + */ +int FliProcessCbHdl::cleanup_callback(void) +{ + if (m_sensitised) { + mti_Desensitize(m_proc_hdl); + } + m_sensitised = false; + return 0; +} + +FliTimedCbHdl::FliTimedCbHdl(GpiImplInterface *impl, + uint64_t time_ps) : GpiCbHdl(impl), + FliProcessCbHdl(impl), + m_time_ps(time_ps) +{ + m_proc_hdl = mti_CreateProcessWithPriority(NULL, handle_fli_callback, (void *)this, MTI_PROC_IMMEDIATE); +} + +int FliTimedCbHdl::arm_callback(void) +{ + mti_ScheduleWakeup(m_proc_hdl, m_time_ps); + m_sensitised = true; + set_call_state(GPI_PRIMED); + return 0; +} + +int FliTimedCbHdl::cleanup_callback(void) +{ + switch (get_call_state()) { + case GPI_PRIMED: + /* Issue #188: Work around for modelsim that is harmless to othes too, + we tag the time as delete, let it fire then do not pass up + */ + LOG_DEBUG("Not removing PRIMED timer %p", m_time_ps); + set_call_state(GPI_DELETE); + return 0; + case GPI_CALL: + LOG_DEBUG("Not removing CALL timer yet %p", m_time_ps); + set_call_state(GPI_DELETE); + return 0; + case GPI_DELETE: + LOG_DEBUG("Removing Postponed DELETE timer %p", m_time_ps); + break; + default: + break; + } + FliProcessCbHdl::cleanup_callback(); + FliImpl* impl = (FliImpl*)m_impl; + impl->cache.put_timer(this); + return 0; +} + +int FliSignalCbHdl::arm_callback(void) +{ + if (NULL == m_proc_hdl) { + LOG_DEBUG("Creating a new process to sensitise to signal %s", mti_GetSignalName(m_sig_hdl)); + m_proc_hdl = mti_CreateProcess(NULL, handle_fli_callback, (void *)this); + } + + if (!m_sensitised) { + mti_Sensitize(m_proc_hdl, m_sig_hdl, MTI_EVENT); + m_sensitised = true; + } + set_call_state(GPI_PRIMED); + return 0; +} + +int FliSimPhaseCbHdl::arm_callback(void) +{ + if (NULL == m_proc_hdl) { + LOG_DEBUG("Creating a new process to sensitise with priority %d", m_priority); + m_proc_hdl = mti_CreateProcessWithPriority(NULL, handle_fli_callback, (void *)this, m_priority); + } + + if (!m_sensitised) { + mti_ScheduleWakeup(m_proc_hdl, 0); + m_sensitised = true; + } + set_call_state(GPI_PRIMED); + return 0; +} + +FliSignalCbHdl::FliSignalCbHdl(GpiImplInterface *impl, + FliSignalObjHdl *sig_hdl, + unsigned int edge) : GpiCbHdl(impl), + FliProcessCbHdl(impl), + GpiValueCbHdl(impl, sig_hdl, edge) +{ + m_sig_hdl = m_signal->get_handle(); +} + diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 58472d2e..5d2f5fd3 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -25,14 +25,13 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ -#include #include #include "FliImpl.h" +#include "mti.h" #include "acc_vhdl.h" // Messy :( #include "acc_user.h" - extern "C" { static FliImpl *fli_table; @@ -350,396 +349,6 @@ int FliImpl::deregister_callback(GpiCbHdl *gpi_hdl) } -/** - * @name cleanup callback - * @brief Called while unwinding after a GPI callback - * - * We keep the process but de-sensitise it - * - * NB need a way to determine if should leave it sensitised, hmmm... - * - */ -int FliProcessCbHdl::cleanup_callback(void) -{ - if (m_sensitised) { - mti_Desensitize(m_proc_hdl); - } - m_sensitised = false; - return 0; -} - -FliTimedCbHdl::FliTimedCbHdl(GpiImplInterface *impl, - uint64_t time_ps) : GpiCbHdl(impl), - FliProcessCbHdl(impl), - m_time_ps(time_ps) -{ - m_proc_hdl = mti_CreateProcessWithPriority(NULL, handle_fli_callback, (void *)this, MTI_PROC_IMMEDIATE); -} - -int FliTimedCbHdl::arm_callback(void) -{ - mti_ScheduleWakeup(m_proc_hdl, m_time_ps); - m_sensitised = true; - set_call_state(GPI_PRIMED); - return 0; -} - -int FliTimedCbHdl::cleanup_callback(void) -{ - switch (get_call_state()) { - case GPI_PRIMED: - /* Issue #188: Work around for modelsim that is harmless to othes too, - we tag the time as delete, let it fire then do not pass up - */ - LOG_DEBUG("Not removing PRIMED timer %p", m_time_ps); - set_call_state(GPI_DELETE); - return 0; - case GPI_CALL: - LOG_DEBUG("Not removing CALL timer yet %p", m_time_ps); - set_call_state(GPI_DELETE); - return 0; - case GPI_DELETE: - LOG_DEBUG("Removing Postponed DELETE timer %p", m_time_ps); - break; - default: - break; - } - FliProcessCbHdl::cleanup_callback(); - FliImpl* impl = (FliImpl*)m_impl; - impl->cache.put_timer(this); - return 0; -} - -int FliSignalCbHdl::arm_callback(void) -{ - if (NULL == m_proc_hdl) { - LOG_DEBUG("Creating a new process to sensitise to signal %s", mti_GetSignalName(m_sig_hdl)); - m_proc_hdl = mti_CreateProcess(NULL, handle_fli_callback, (void *)this); - } - - if (!m_sensitised) { - mti_Sensitize(m_proc_hdl, m_sig_hdl, MTI_EVENT); - m_sensitised = true; - } - set_call_state(GPI_PRIMED); - return 0; -} - -int FliSimPhaseCbHdl::arm_callback(void) -{ - if (NULL == m_proc_hdl) { - LOG_DEBUG("Creating a new process to sensitise with priority %d", m_priority); - m_proc_hdl = mti_CreateProcessWithPriority(NULL, handle_fli_callback, (void *)this, m_priority); - } - - if (!m_sensitised) { - mti_ScheduleWakeup(m_proc_hdl, 0); - m_sensitised = true; - } - set_call_state(GPI_PRIMED); - return 0; -} - -FliSignalCbHdl::FliSignalCbHdl(GpiImplInterface *impl, - FliSignalObjHdl *sig_hdl, - unsigned int edge) : GpiCbHdl(impl), - FliProcessCbHdl(impl), - GpiValueCbHdl(impl, sig_hdl, edge) -{ - m_sig_hdl = m_signal->get_handle(); -} - - -GpiCbHdl *FliSignalObjHdl::value_change_cb(unsigned int edge) -{ - FliSignalCbHdl *cb = NULL; - - switch (edge) { - case 1: - cb = &m_rising_cb; - break; - case 2: - cb = &m_falling_cb; - break; - case 3: - cb = &m_either_cb; - break; - default: - return NULL; - } - - if (cb->arm_callback()) { - return NULL; - } - - return (GpiValueCbHdl*)cb; -} - -static const char value_enum[10] = "UX01ZWLH-"; - -const char* FliSignalObjHdl::get_signal_value_binstr(void) -{ - mtiSignalIdT m_fli_hdl = get_handle(); - - switch (m_fli_type) { - - case MTI_TYPE_ENUM: - m_val_buff[0] = value_enum[mti_GetSignalValue(m_fli_hdl)]; - break; - case MTI_TYPE_SCALAR: - case MTI_TYPE_PHYSICAL: { - std::bitset<32> value((unsigned long)mti_GetSignalValue(m_fli_hdl)); - std::string bin_str = value.to_string(); - snprintf(m_val_buff, m_val_len+1, "%s", bin_str.c_str()); - } - break; - case MTI_TYPE_ARRAY: { - mti_GetArraySignalValue(m_fli_hdl, m_mti_buff); - if (m_val_len <= 256) { - char *iter = (char*)m_mti_buff; - for (int i = 0; i < m_val_len; i++ ) { - m_val_buff[i] = value_enum[(int)iter[i]]; - } - } else { - for (int i = 0; i < m_val_len; i++ ) { - m_val_buff[i] = value_enum[m_mti_buff[i]]; - } - } - } - break; - default: - LOG_ERROR("Signal %s type %d not currently supported", - m_name.c_str(), m_fli_type); - break; - } - - LOG_DEBUG("Retrieved \"%s\" for signal %s", m_val_buff, m_name.c_str()); - - return m_val_buff; -} - -const char* FliSignalObjHdl::get_signal_value_str(void) -{ - LOG_ERROR("Getting signal value as str not currently supported"); - return NULL; -} - -double FliSignalObjHdl::get_signal_value_real(void) -{ - LOG_ERROR("Getting signal value as double not currently supported!"); - return -1; -} - -long FliSignalObjHdl::get_signal_value_long(void) -{ - LOG_ERROR("Getting signal value as long not currently supported!"); - return -1; -} - -int FliSignalObjHdl::set_signal_value(const long value) -{ - int rc; - char buff[20]; - mtiSignalIdT m_fli_hdl = get_handle(); - - - snprintf(buff, 20, "16#%016X", (int)value); - - rc = mti_ForceSignal(m_fli_hdl, &buff[0], 0, MTI_FORCE_DEPOSIT, -1, -1); - - if (!rc) { - LOG_ERROR("Setting signal value failed!\n"); - } - return rc-1; -} - -int FliSignalObjHdl::set_signal_value(std::string &value) -{ - int rc; - mtiSignalIdT m_fli_hdl = get_handle(); - - - snprintf(m_val_str_buff, m_val_str_len+1, "%d'b%s", m_val_len, value.c_str()); - - rc = mti_ForceSignal(m_fli_hdl, &m_val_str_buff[0], 0, MTI_FORCE_DEPOSIT, -1, -1); - if (!rc) { - LOG_ERROR("Setting signal value failed!\n"); - } - return rc-1; -} - -int FliSignalObjHdl::set_signal_value(const double value) -{ - LOG_ERROR("Setting Signal via double not supported!"); - return -1; -} - -int FliSignalObjHdl::initialise(std::string &name, std::string &fq_name) -{ - mtiSignalIdT m_fli_hdl = get_handle(); - - /* Pre allocte buffers on signal type basis */ - m_fli_type = mti_GetTypeKind(mti_GetSignalType(m_fli_hdl)); - - switch (m_fli_type) { - case MTI_TYPE_ENUM: - m_val_len = 1; - m_val_str_len = 3+m_val_len; - break; - case MTI_TYPE_SCALAR: - case MTI_TYPE_PHYSICAL: - m_val_len = 32; - m_val_str_len = 4+m_val_len; - break; - case MTI_TYPE_ARRAY: - m_val_len = mti_TickLength(mti_GetSignalType(m_fli_hdl)); - m_val_str_len = snprintf(NULL, 0, "%d'b", m_val_len)+m_val_len; - m_mti_buff = (mtiInt32T*)malloc(sizeof(*m_mti_buff) * m_val_len); - if (!m_mti_buff) { - LOG_CRITICAL("Unable to alloc mem for signal mti read buffer: ABORTING"); - } - break; - default: - LOG_ERROR("Unable to handle onject type for %s (%d)", - name.c_str(), m_fli_type); - } - - m_val_buff = (char*)malloc(m_val_len+1); - if (!m_val_buff) { - LOG_CRITICAL("Unable to alloc mem for signal read buffer: ABORTING"); - } - m_val_buff[m_val_len] = '\0'; - m_val_str_buff = (char*)malloc(m_val_str_len+1); - if (!m_val_str_buff) { - LOG_CRITICAL("Unable to alloc mem for signal write buffer: ABORTING"); - } - m_val_str_buff[m_val_str_len] = '\0'; - - GpiObjHdl::initialise(name, fq_name); - - return 0; -} - -GpiCbHdl *FliVariableObjHdl::value_change_cb(unsigned int edge) -{ - return NULL; -} - -const char* FliVariableObjHdl::get_signal_value_binstr(void) -{ - mtiVariableIdT m_fli_hdl = get_handle(); - - switch (m_fli_type) { - - case MTI_TYPE_ENUM: - m_val_buff[0] = value_enum[mti_GetVarValue(m_fli_hdl)]; - break; - case MTI_TYPE_SCALAR: - case MTI_TYPE_PHYSICAL: { - std::bitset<32> value((unsigned long)mti_GetVarValue(m_fli_hdl)); - std::string bin_str = value.to_string(); - snprintf(m_val_buff, m_val_len+1, "%s", bin_str.c_str()); - } - break; - case MTI_TYPE_ARRAY: { - mti_GetArrayVarValue(m_fli_hdl, m_mti_buff); - if (m_val_len <= 256) { - char *iter = (char*)m_mti_buff; - for (int i = 0; i < m_val_len; i++ ) { - m_val_buff[i] = value_enum[(int)iter[i]]; - } - } else { - for (int i = 0; i < m_val_len; i++ ) { - m_val_buff[i] = value_enum[m_mti_buff[i]]; - } - } - } - break; - default: - LOG_ERROR("Variable %s type %d not currently supported", - m_name.c_str(), m_fli_type); - break; - } - - LOG_DEBUG("Retrieved \"%s\" for variable %s", m_val_buff, m_name.c_str()); - - return m_val_buff; -} - -const char* FliVariableObjHdl::get_signal_value_str(void) -{ - LOG_ERROR("Getting signal value as str not currently supported"); - return ""; -} - -double FliVariableObjHdl::get_signal_value_real(void) -{ - LOG_ERROR("Getting variable value as double not currently supported!"); - return -1; -} - -long FliVariableObjHdl::get_signal_value_long(void) -{ - LOG_ERROR("Getting variable value as long not currently supported!"); - return -1; -} - -int FliVariableObjHdl::set_signal_value(const long value) -{ - LOG_ERROR("Setting variable value not currently supported!"); - return -1; -} - -int FliVariableObjHdl::set_signal_value(std::string &value) -{ - LOG_ERROR("Setting variable value not currently supported!"); - return -1; -} - -int FliVariableObjHdl::set_signal_value(const double value) -{ - LOG_ERROR("Setting variable value not currently supported"); - return -1; -} - -int FliVariableObjHdl::initialise(std::string &name, std::string &fq_name) -{ - mtiVariableIdT m_fli_hdl = get_handle(); - - /* Pre allocte buffers on signal type basis */ - m_fli_type = mti_GetTypeKind(mti_GetVarType(m_fli_hdl)); - - switch (m_fli_type) { - case MTI_TYPE_ENUM: - m_val_len = 1; - break; - case MTI_TYPE_SCALAR: - case MTI_TYPE_PHYSICAL: - m_val_len = 32; - break; - case MTI_TYPE_ARRAY: - m_val_len = mti_TickLength(mti_GetVarType(m_fli_hdl)); - m_mti_buff = (mtiInt32T*)malloc(sizeof(*m_mti_buff) * m_val_len); - if (!m_mti_buff) { - LOG_CRITICAL("Unable to alloc mem for signal mti read buffer: ABORTING"); - } - break; - default: - LOG_ERROR("Unable to handle object type for %s (%d)", - name.c_str(), m_fli_type); - } - - m_val_buff = (char*)malloc(m_val_len+1); - if (!m_val_buff) { - LOG_CRITICAL("Unable to alloc mem for signal read buffer: ABORTING"); - } - m_val_buff[m_val_len] = '\0'; - - GpiObjHdl::initialise(name, fq_name); - - return 0; -} - GpiIterator *FliImpl::iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) { /* This function should return a class derived from GpiIterator and follows it's @@ -748,7 +357,6 @@ GpiIterator *FliImpl::iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type return NULL; } -#include FliTimedCbHdl* FliTimerCache::get_timer(uint64_t time_ps) { diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index 90be156b..70f873d2 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -33,6 +33,13 @@ #include +extern "C" { +void fli_elab_cb(void *nothing); +void cocotb_init(void); +void handle_fli_callback(void *data); +} + + // Callback handles // In FLI some callbacks require us to register a process diff --git a/lib/fli/FliObjHdl.cpp b/lib/fli/FliObjHdl.cpp new file mode 100644 index 00000000..dd8115db --- /dev/null +++ b/lib/fli/FliObjHdl.cpp @@ -0,0 +1,321 @@ +/****************************************************************************** +* Copyright (c) 2014 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +#include + +#include "FliImpl.h" + +GpiCbHdl *FliSignalObjHdl::value_change_cb(unsigned int edge) +{ + FliSignalCbHdl *cb = NULL; + + switch (edge) { + case 1: + cb = &m_rising_cb; + break; + case 2: + cb = &m_falling_cb; + break; + case 3: + cb = &m_either_cb; + break; + default: + return NULL; + } + + if (cb->arm_callback()) { + return NULL; + } + + return (GpiValueCbHdl*)cb; +} + +static const char value_enum[10] = "UX01ZWLH-"; + +const char* FliSignalObjHdl::get_signal_value_binstr(void) +{ + mtiSignalIdT m_fli_hdl = get_handle(); + + switch (m_fli_type) { + + case MTI_TYPE_ENUM: + m_val_buff[0] = value_enum[mti_GetSignalValue(m_fli_hdl)]; + break; + case MTI_TYPE_SCALAR: + case MTI_TYPE_PHYSICAL: { + std::bitset<32> value((unsigned long)mti_GetSignalValue(m_fli_hdl)); + std::string bin_str = value.to_string(); + snprintf(m_val_buff, m_val_len+1, "%s", bin_str.c_str()); + } + break; + case MTI_TYPE_ARRAY: { + mti_GetArraySignalValue(m_fli_hdl, m_mti_buff); + if (m_val_len <= 256) { + char *iter = (char*)m_mti_buff; + for (int i = 0; i < m_val_len; i++ ) { + m_val_buff[i] = value_enum[(int)iter[i]]; + } + } else { + for (int i = 0; i < m_val_len; i++ ) { + m_val_buff[i] = value_enum[m_mti_buff[i]]; + } + } + } + break; + default: + LOG_ERROR("Signal %s type %d not currently supported", + m_name.c_str(), m_fli_type); + break; + } + + LOG_DEBUG("Retrieved \"%s\" for signal %s", m_val_buff, m_name.c_str()); + + return m_val_buff; +} + +const char* FliSignalObjHdl::get_signal_value_str(void) +{ + LOG_ERROR("Getting signal value as str not currently supported"); + return NULL; +} + +double FliSignalObjHdl::get_signal_value_real(void) +{ + LOG_ERROR("Getting signal value as double not currently supported!"); + return -1; +} + +long FliSignalObjHdl::get_signal_value_long(void) +{ + LOG_ERROR("Getting signal value as long not currently supported!"); + return -1; +} + +int FliSignalObjHdl::set_signal_value(const long value) +{ + int rc; + char buff[20]; + mtiSignalIdT m_fli_hdl = get_handle(); + + + snprintf(buff, 20, "16#%016X", (int)value); + + rc = mti_ForceSignal(m_fli_hdl, &buff[0], 0, MTI_FORCE_DEPOSIT, -1, -1); + + if (!rc) { + LOG_ERROR("Setting signal value failed!\n"); + } + return rc-1; +} + +int FliSignalObjHdl::set_signal_value(std::string &value) +{ + int rc; + mtiSignalIdT m_fli_hdl = get_handle(); + + + snprintf(m_val_str_buff, m_val_str_len+1, "%d'b%s", m_val_len, value.c_str()); + + rc = mti_ForceSignal(m_fli_hdl, &m_val_str_buff[0], 0, MTI_FORCE_DEPOSIT, -1, -1); + if (!rc) { + LOG_ERROR("Setting signal value failed!\n"); + } + return rc-1; +} + +int FliSignalObjHdl::set_signal_value(const double value) +{ + LOG_ERROR("Setting Signal via double not supported!"); + return -1; +} + +int FliSignalObjHdl::initialise(std::string &name, std::string &fq_name) +{ + mtiSignalIdT m_fli_hdl = get_handle(); + + /* Pre allocte buffers on signal type basis */ + m_fli_type = mti_GetTypeKind(mti_GetSignalType(m_fli_hdl)); + + switch (m_fli_type) { + case MTI_TYPE_ENUM: + m_val_len = 1; + m_val_str_len = 3+m_val_len; + break; + case MTI_TYPE_SCALAR: + case MTI_TYPE_PHYSICAL: + m_val_len = 32; + m_val_str_len = 4+m_val_len; + break; + case MTI_TYPE_ARRAY: + m_val_len = mti_TickLength(mti_GetSignalType(m_fli_hdl)); + m_val_str_len = snprintf(NULL, 0, "%d'b", m_val_len)+m_val_len; + m_mti_buff = (mtiInt32T*)malloc(sizeof(*m_mti_buff) * m_val_len); + if (!m_mti_buff) { + LOG_CRITICAL("Unable to alloc mem for signal mti read buffer: ABORTING"); + } + break; + default: + LOG_ERROR("Unable to handle onject type for %s (%d)", + name.c_str(), m_fli_type); + } + + m_val_buff = (char*)malloc(m_val_len+1); + if (!m_val_buff) { + LOG_CRITICAL("Unable to alloc mem for signal read buffer: ABORTING"); + } + m_val_buff[m_val_len] = '\0'; + m_val_str_buff = (char*)malloc(m_val_str_len+1); + if (!m_val_str_buff) { + LOG_CRITICAL("Unable to alloc mem for signal write buffer: ABORTING"); + } + m_val_str_buff[m_val_str_len] = '\0'; + + GpiObjHdl::initialise(name, fq_name); + + return 0; +} + +GpiCbHdl *FliVariableObjHdl::value_change_cb(unsigned int edge) +{ + return NULL; +} + +const char* FliVariableObjHdl::get_signal_value_binstr(void) +{ + mtiVariableIdT m_fli_hdl = get_handle(); + + switch (m_fli_type) { + + case MTI_TYPE_ENUM: + m_val_buff[0] = value_enum[mti_GetVarValue(m_fli_hdl)]; + break; + case MTI_TYPE_SCALAR: + case MTI_TYPE_PHYSICAL: { + std::bitset<32> value((unsigned long)mti_GetVarValue(m_fli_hdl)); + std::string bin_str = value.to_string(); + snprintf(m_val_buff, m_val_len+1, "%s", bin_str.c_str()); + } + break; + case MTI_TYPE_ARRAY: { + mti_GetArrayVarValue(m_fli_hdl, m_mti_buff); + if (m_val_len <= 256) { + char *iter = (char*)m_mti_buff; + for (int i = 0; i < m_val_len; i++ ) { + m_val_buff[i] = value_enum[(int)iter[i]]; + } + } else { + for (int i = 0; i < m_val_len; i++ ) { + m_val_buff[i] = value_enum[m_mti_buff[i]]; + } + } + } + break; + default: + LOG_ERROR("Variable %s type %d not currently supported", + m_name.c_str(), m_fli_type); + break; + } + + LOG_DEBUG("Retrieved \"%s\" for variable %s", m_val_buff, m_name.c_str()); + + return m_val_buff; +} + +const char* FliVariableObjHdl::get_signal_value_str(void) +{ + LOG_ERROR("Getting signal value as str not currently supported"); + return ""; +} + +double FliVariableObjHdl::get_signal_value_real(void) +{ + LOG_ERROR("Getting variable value as double not currently supported!"); + return -1; +} + +long FliVariableObjHdl::get_signal_value_long(void) +{ + LOG_ERROR("Getting variable value as long not currently supported!"); + return -1; +} + +int FliVariableObjHdl::set_signal_value(const long value) +{ + LOG_ERROR("Setting variable value not currently supported!"); + return -1; +} + +int FliVariableObjHdl::set_signal_value(std::string &value) +{ + LOG_ERROR("Setting variable value not currently supported!"); + return -1; +} + +int FliVariableObjHdl::set_signal_value(const double value) +{ + LOG_ERROR("Setting variable value not currently supported"); + return -1; +} + +int FliVariableObjHdl::initialise(std::string &name, std::string &fq_name) +{ + mtiVariableIdT m_fli_hdl = get_handle(); + + /* Pre allocte buffers on signal type basis */ + m_fli_type = mti_GetTypeKind(mti_GetVarType(m_fli_hdl)); + + switch (m_fli_type) { + case MTI_TYPE_ENUM: + m_val_len = 1; + break; + case MTI_TYPE_SCALAR: + case MTI_TYPE_PHYSICAL: + m_val_len = 32; + break; + case MTI_TYPE_ARRAY: + m_val_len = mti_TickLength(mti_GetVarType(m_fli_hdl)); + m_mti_buff = (mtiInt32T*)malloc(sizeof(*m_mti_buff) * m_val_len); + if (!m_mti_buff) { + LOG_CRITICAL("Unable to alloc mem for signal mti read buffer: ABORTING"); + } + break; + default: + LOG_ERROR("Unable to handle object type for %s (%d)", + name.c_str(), m_fli_type); + } + + m_val_buff = (char*)malloc(m_val_len+1); + if (!m_val_buff) { + LOG_CRITICAL("Unable to alloc mem for signal read buffer: ABORTING"); + } + m_val_buff[m_val_len] = '\0'; + + GpiObjHdl::initialise(name, fq_name); + + return 0; +} + diff --git a/lib/fli/Makefile b/lib/fli/Makefile index 5355298b..ea3c6f71 100644 --- a/lib/fli/Makefile +++ b/lib/fli/Makefile @@ -30,11 +30,11 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += -I$(MODELSIM_BIN_DIR)/../include GXX_ARGS += -DFLI_CHECKING -DUSE_CACHE -LIBS := -lgpilog -lgpi -lstdc++ +LIBS := $(EXTRA_LIBS) -lgpilog -lgpi -lstdc++ LD_PATH := -L$(LIB_DIR) $(EXTRA_LIBDIRS) LIB_NAME := libfli -SRCS := FliImpl.cpp +SRCS := FliImpl.cpp FliCbHdl.cpp FliObjHdl.cpp all: $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) From 4eaf7ecce81b501e324cee14bf8e6d80924bf08d Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 22 Dec 2015 11:24:34 -0700 Subject: [PATCH 1095/2656] Combine Variable object and Signal object to simplify maintainability --- lib/fli/FliCbHdl.cpp | 2 +- lib/fli/FliImpl.cpp | 4 +- lib/fli/FliImpl.h | 178 ++++++++++++++------------------ lib/fli/FliObjHdl.cpp | 231 +++++++++++++----------------------------- 4 files changed, 149 insertions(+), 266 deletions(-) diff --git a/lib/fli/FliCbHdl.cpp b/lib/fli/FliCbHdl.cpp index 5a347131..7446ed40 100644 --- a/lib/fli/FliCbHdl.cpp +++ b/lib/fli/FliCbHdl.cpp @@ -118,7 +118,7 @@ int FliSimPhaseCbHdl::arm_callback(void) } FliSignalCbHdl::FliSignalCbHdl(GpiImplInterface *impl, - FliSignalObjHdl *sig_hdl, + FliValueObjHdl *sig_hdl, unsigned int edge) : GpiCbHdl(impl), FliProcessCbHdl(impl), GpiValueCbHdl(impl, sig_hdl, edge) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 5d2f5fd3..f91ea4a9 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -143,7 +143,7 @@ GpiObjHdl *FliImpl::create_gpi_obj(std::string &name, std::string &fq_name) name.c_str(), typeKind); } - new_obj = new FliSignalObjHdl(this, sig_hdl, objtype); + new_obj = new FliValueObjHdl(this, sig_hdl, objtype, false, false); } else if ((var_hdl = mti_FindVar(&writable[0])) != NULL) { LOG_DEBUG("Found a variable %s -> %p", fq_name.c_str(), var_hdl); @@ -168,7 +168,7 @@ GpiObjHdl *FliImpl::create_gpi_obj(std::string &name, std::string &fq_name) name.c_str(), typeKind); } - new_obj = new FliVariableObjHdl(this, var_hdl, objtype, (varKind != accVariable)); + new_obj = new FliValueObjHdl(this, var_hdl, objtype, (varKind != accVariable), true); } else { LOG_DEBUG("Unable to query %s", fq_name.c_str()); diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index 70f873d2..536d901d 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -39,6 +39,8 @@ void cocotb_init(void); void handle_fli_callback(void *data); } +class FliImpl; +class FliValueObjHdl; // Callback handles @@ -59,13 +61,12 @@ class FliProcessCbHdl : public virtual GpiCbHdl { bool m_sensitised; }; -class FliSignalObjHdl; // One class of callbacks uses mti_Sensitize to react to a signal class FliSignalCbHdl : public FliProcessCbHdl, public GpiValueCbHdl { public: FliSignalCbHdl(GpiImplInterface *impl, - FliSignalObjHdl *sig_hdl, + FliValueObjHdl *sig_hdl, unsigned int edge); virtual ~FliSignalCbHdl() { } @@ -78,90 +79,6 @@ class FliSignalCbHdl : public FliProcessCbHdl, public GpiValueCbHdl { mtiSignalIdT m_sig_hdl; }; -class FliValueObjHdl : public GpiSignalObjHdl { -public: - FliValueObjHdl(GpiImplInterface *impl, - void *hdl, - gpi_objtype_t objtype, - bool is_const) : GpiSignalObjHdl(impl, hdl, objtype, is_const), - m_fli_type(MTI_TYPE_SCALAR), - m_mti_buff(NULL), - m_val_buff(NULL), - m_val_str_buff(NULL), - m_val_len(0), - m_val_str_len(0) { } - virtual ~FliValueObjHdl() { - if (m_val_buff) - free(m_val_buff); - if (m_mti_buff) - free(m_mti_buff); - if (m_val_str_buff) - free(m_val_str_buff); - } - -protected: - mtiTypeKindT m_fli_type; - mtiInt32T *m_mti_buff; - char *m_val_buff; - char *m_val_str_buff; - int m_val_len; - int m_val_str_len; -}; - -class FliSignalObjHdl : public FliValueObjHdl { -public: - FliSignalObjHdl(GpiImplInterface *impl, - mtiSignalIdT hdl, - gpi_objtype_t objtype) : FliValueObjHdl(impl, hdl, objtype, false), - m_rising_cb(impl, this, GPI_RISING), - m_falling_cb(impl, this, GPI_FALLING), - m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } - virtual ~FliSignalObjHdl() { } - - const char* get_signal_value_binstr(void); - const char* get_signal_value_str(void); - double get_signal_value_real(void); - long get_signal_value_long(void); - - int set_signal_value(const long value); - int set_signal_value(const double value); - int set_signal_value(std::string &value); - - /* Value change callback accessor */ - GpiCbHdl *value_change_cb(unsigned int edge); - int initialise(std::string &name, std::string &fq_name); - -protected: - FliSignalCbHdl m_rising_cb; - FliSignalCbHdl m_falling_cb; - FliSignalCbHdl m_either_cb; -}; - -class FliVariableObjHdl : public FliValueObjHdl { -public: - FliVariableObjHdl(GpiImplInterface *impl, - mtiVariableIdT hdl, - gpi_objtype_t objtype, - bool is_const) : FliValueObjHdl(impl, hdl, objtype, is_const) { } - - virtual ~FliVariableObjHdl() { } - - const char* get_signal_value_binstr(void); - const char* get_signal_value_str(void); - double get_signal_value_real(void); - long get_signal_value_long(void); - - int set_signal_value(const long value); - int set_signal_value(std::string &value); - int set_signal_value(const double value); - - /* Value change callback accessor */ - GpiCbHdl *value_change_cb(unsigned int edge); - int initialise(std::string &name, std::string &fq_name); -}; - - - // All other callbacks are related to the simulation phasing class FliSimPhaseCbHdl : public FliProcessCbHdl { @@ -191,6 +108,7 @@ class FliNextPhaseCbHdl : public FliSimPhaseCbHdl { FliSimPhaseCbHdl(impl, MTI_PROC_IMMEDIATE) { } virtual ~FliNextPhaseCbHdl() { } }; + class FliReadOnlyCbHdl : public FliSimPhaseCbHdl { public: FliReadOnlyCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), @@ -206,8 +124,77 @@ class FliShutdownCbHdl : public GpiCbHdl { virtual ~FliShutdownCbHdl() { } }; -class FliImpl; -class FliTimedCbHdl; +class FliTimedCbHdl : public FliProcessCbHdl { +public: + FliTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps); + virtual ~FliTimedCbHdl() { } + + int arm_callback(void); + void reset_time(uint64_t new_time) { + m_time_ps = new_time; + } + int cleanup_callback(void); +private: + uint64_t m_time_ps; +}; + + +// Object Handles + +class FliValueObjHdl : public GpiSignalObjHdl { +public: + FliValueObjHdl(GpiImplInterface *impl, + void *hdl, + gpi_objtype_t objtype, + bool is_const, + bool is_var) : GpiSignalObjHdl(impl, hdl, objtype, is_const), + m_fli_type(MTI_TYPE_SCALAR), + m_is_var(is_var), + m_rising_cb(impl, this, GPI_RISING), + m_falling_cb(impl, this, GPI_FALLING), + m_either_cb(impl, this, GPI_FALLING | GPI_RISING), + m_mti_buff(NULL), + m_val_buff(NULL), + m_val_str_buff(NULL), + m_val_len(0), + m_val_str_len(0) { } + virtual ~FliValueObjHdl() { + if (m_val_buff) + free(m_val_buff); + if (m_mti_buff) + free(m_mti_buff); + if (m_val_str_buff) + free(m_val_str_buff); + } + + virtual const char* get_signal_value_binstr(void); + virtual const char* get_signal_value_str(void); + virtual double get_signal_value_real(void); + virtual long get_signal_value_long(void); + + virtual int set_signal_value(const long value); + virtual int set_signal_value(const double value); + virtual int set_signal_value(std::string &value); + + /* Value change callback accessor */ + virtual GpiCbHdl *value_change_cb(unsigned int edge); + virtual int initialise(std::string &name, std::string &fq_name); + + +protected: + mtiTypeKindT m_fli_type; + bool m_is_var; + FliSignalCbHdl m_rising_cb; + FliSignalCbHdl m_falling_cb; + FliSignalCbHdl m_either_cb; + mtiInt32T *m_mti_buff; + char *m_val_buff; + char *m_val_str_buff; + int m_val_len; + int m_val_str_len; +}; + + class FliTimerCache { public: FliTimerCache(FliImpl* impl) : impl(impl) { } @@ -260,21 +247,6 @@ class FliImpl : public GpiImplInterface { FliReadWriteCbHdl m_readwrite_cbhdl; public: FliTimerCache cache; - -}; - -class FliTimedCbHdl : public FliProcessCbHdl { -public: - FliTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps); - virtual ~FliTimedCbHdl() { } - - int arm_callback(void); - void reset_time(uint64_t new_time) { - m_time_ps = new_time; - } - int cleanup_callback(void); -private: - uint64_t m_time_ps; }; #endif /*COCOTB_FLI_IMPL_H_ */ diff --git a/lib/fli/FliObjHdl.cpp b/lib/fli/FliObjHdl.cpp index dd8115db..fa753d1f 100644 --- a/lib/fli/FliObjHdl.cpp +++ b/lib/fli/FliObjHdl.cpp @@ -29,24 +29,28 @@ #include "FliImpl.h" -GpiCbHdl *FliSignalObjHdl::value_change_cb(unsigned int edge) +GpiCbHdl *FliValueObjHdl::value_change_cb(unsigned int edge) { FliSignalCbHdl *cb = NULL; - switch (edge) { - case 1: - cb = &m_rising_cb; - break; - case 2: - cb = &m_falling_cb; - break; - case 3: - cb = &m_either_cb; - break; - default: + if (m_is_var) { return NULL; } + switch (edge) { + case 1: + cb = &m_rising_cb; + break; + case 2: + cb = &m_falling_cb; + break; + case 3: + cb = &m_either_cb; + break; + default: + return NULL; + } + if (cb->arm_callback()) { return NULL; } @@ -56,24 +60,38 @@ GpiCbHdl *FliSignalObjHdl::value_change_cb(unsigned int edge) static const char value_enum[10] = "UX01ZWLH-"; -const char* FliSignalObjHdl::get_signal_value_binstr(void) +const char* FliValueObjHdl::get_signal_value_binstr(void) { - mtiSignalIdT m_fli_hdl = get_handle(); switch (m_fli_type) { - case MTI_TYPE_ENUM: - m_val_buff[0] = value_enum[mti_GetSignalValue(m_fli_hdl)]; + if (m_is_var) { + m_val_buff[0] = value_enum[mti_GetVarValue(get_handle())]; + } else { + m_val_buff[0] = value_enum[mti_GetSignalValue(get_handle())]; + } break; case MTI_TYPE_SCALAR: case MTI_TYPE_PHYSICAL: { - std::bitset<32> value((unsigned long)mti_GetSignalValue(m_fli_hdl)); + unsigned long val = 0; + + if (m_is_var) { + val = (unsigned long)mti_GetVarValue(get_handle()); + } else { + val = (unsigned long)mti_GetSignalValue(get_handle()); + } + + std::bitset<32> value(val); std::string bin_str = value.to_string(); snprintf(m_val_buff, m_val_len+1, "%s", bin_str.c_str()); } break; case MTI_TYPE_ARRAY: { - mti_GetArraySignalValue(m_fli_hdl, m_mti_buff); + if (m_is_var) { + mti_GetArrayVarValue(get_handle(), m_mti_buff); + } else { + mti_GetArraySignalValue(get_handle(), m_mti_buff); + } if (m_val_len <= 256) { char *iter = (char*)m_mti_buff; for (int i = 0; i < m_val_len; i++ ) { @@ -87,44 +105,47 @@ const char* FliSignalObjHdl::get_signal_value_binstr(void) } break; default: - LOG_ERROR("Signal %s type %d not currently supported", + LOG_ERROR("Value Object %s of type %d is not currently supported", m_name.c_str(), m_fli_type); break; } - LOG_DEBUG("Retrieved \"%s\" for signal %s", m_val_buff, m_name.c_str()); + LOG_DEBUG("Retrieved \"%s\" for value object %s", m_val_buff, m_name.c_str()); return m_val_buff; } -const char* FliSignalObjHdl::get_signal_value_str(void) +const char* FliValueObjHdl::get_signal_value_str(void) { LOG_ERROR("Getting signal value as str not currently supported"); return NULL; } -double FliSignalObjHdl::get_signal_value_real(void) +double FliValueObjHdl::get_signal_value_real(void) { LOG_ERROR("Getting signal value as double not currently supported!"); return -1; } -long FliSignalObjHdl::get_signal_value_long(void) +long FliValueObjHdl::get_signal_value_long(void) { LOG_ERROR("Getting signal value as long not currently supported!"); return -1; } -int FliSignalObjHdl::set_signal_value(const long value) +int FliValueObjHdl::set_signal_value(const long value) { int rc; char buff[20]; - mtiSignalIdT m_fli_hdl = get_handle(); - snprintf(buff, 20, "16#%016X", (int)value); - rc = mti_ForceSignal(m_fli_hdl, &buff[0], 0, MTI_FORCE_DEPOSIT, -1, -1); + if (m_is_var) { + rc = 0; + LOG_WARN("Setting a variable is not currently supported!\n"); + } else { + rc = mti_ForceSignal(get_handle(), &buff[0], 0, MTI_FORCE_DEPOSIT, -1, -1); + } if (!rc) { LOG_ERROR("Setting signal value failed!\n"); @@ -132,33 +153,43 @@ int FliSignalObjHdl::set_signal_value(const long value) return rc-1; } -int FliSignalObjHdl::set_signal_value(std::string &value) +int FliValueObjHdl::set_signal_value(std::string &value) { int rc; - mtiSignalIdT m_fli_hdl = get_handle(); - snprintf(m_val_str_buff, m_val_str_len+1, "%d'b%s", m_val_len, value.c_str()); - rc = mti_ForceSignal(m_fli_hdl, &m_val_str_buff[0], 0, MTI_FORCE_DEPOSIT, -1, -1); + if (m_is_var) { + rc = 0; + LOG_WARN("Setting a variable is not currently supported!\n"); + } else { + rc = mti_ForceSignal(get_handle(), &m_val_str_buff[0], 0, MTI_FORCE_DEPOSIT, -1, -1); + } + if (!rc) { LOG_ERROR("Setting signal value failed!\n"); } return rc-1; } -int FliSignalObjHdl::set_signal_value(const double value) +int FliValueObjHdl::set_signal_value(const double value) { LOG_ERROR("Setting Signal via double not supported!"); return -1; } -int FliSignalObjHdl::initialise(std::string &name, std::string &fq_name) +int FliValueObjHdl::initialise(std::string &name, std::string &fq_name) { - mtiSignalIdT m_fli_hdl = get_handle(); + mtiTypeIdT valType; /* Pre allocte buffers on signal type basis */ - m_fli_type = mti_GetTypeKind(mti_GetSignalType(m_fli_hdl)); + if (m_is_var) { + valType = mti_GetVarType(get_handle()); + } else { + valType = mti_GetSignalType(get_handle()); + } + + m_fli_type = mti_GetTypeKind(valType); switch (m_fli_type) { case MTI_TYPE_ENUM: @@ -171,26 +202,26 @@ int FliSignalObjHdl::initialise(std::string &name, std::string &fq_name) m_val_str_len = 4+m_val_len; break; case MTI_TYPE_ARRAY: - m_val_len = mti_TickLength(mti_GetSignalType(m_fli_hdl)); + m_val_len = mti_TickLength(valType); m_val_str_len = snprintf(NULL, 0, "%d'b", m_val_len)+m_val_len; m_mti_buff = (mtiInt32T*)malloc(sizeof(*m_mti_buff) * m_val_len); if (!m_mti_buff) { - LOG_CRITICAL("Unable to alloc mem for signal mti read buffer: ABORTING"); + LOG_CRITICAL("Unable to alloc mem for value object mti read buffer: ABORTING"); } break; default: - LOG_ERROR("Unable to handle onject type for %s (%d)", + LOG_ERROR("Unable to handle object type for %s (%d)", name.c_str(), m_fli_type); } m_val_buff = (char*)malloc(m_val_len+1); if (!m_val_buff) { - LOG_CRITICAL("Unable to alloc mem for signal read buffer: ABORTING"); + LOG_CRITICAL("Unable to alloc mem for value object read buffer: ABORTING"); } m_val_buff[m_val_len] = '\0'; m_val_str_buff = (char*)malloc(m_val_str_len+1); if (!m_val_str_buff) { - LOG_CRITICAL("Unable to alloc mem for signal write buffer: ABORTING"); + LOG_CRITICAL("Unable to alloc mem for value object write buffer: ABORTING"); } m_val_str_buff[m_val_str_len] = '\0'; @@ -199,123 +230,3 @@ int FliSignalObjHdl::initialise(std::string &name, std::string &fq_name) return 0; } -GpiCbHdl *FliVariableObjHdl::value_change_cb(unsigned int edge) -{ - return NULL; -} - -const char* FliVariableObjHdl::get_signal_value_binstr(void) -{ - mtiVariableIdT m_fli_hdl = get_handle(); - - switch (m_fli_type) { - - case MTI_TYPE_ENUM: - m_val_buff[0] = value_enum[mti_GetVarValue(m_fli_hdl)]; - break; - case MTI_TYPE_SCALAR: - case MTI_TYPE_PHYSICAL: { - std::bitset<32> value((unsigned long)mti_GetVarValue(m_fli_hdl)); - std::string bin_str = value.to_string(); - snprintf(m_val_buff, m_val_len+1, "%s", bin_str.c_str()); - } - break; - case MTI_TYPE_ARRAY: { - mti_GetArrayVarValue(m_fli_hdl, m_mti_buff); - if (m_val_len <= 256) { - char *iter = (char*)m_mti_buff; - for (int i = 0; i < m_val_len; i++ ) { - m_val_buff[i] = value_enum[(int)iter[i]]; - } - } else { - for (int i = 0; i < m_val_len; i++ ) { - m_val_buff[i] = value_enum[m_mti_buff[i]]; - } - } - } - break; - default: - LOG_ERROR("Variable %s type %d not currently supported", - m_name.c_str(), m_fli_type); - break; - } - - LOG_DEBUG("Retrieved \"%s\" for variable %s", m_val_buff, m_name.c_str()); - - return m_val_buff; -} - -const char* FliVariableObjHdl::get_signal_value_str(void) -{ - LOG_ERROR("Getting signal value as str not currently supported"); - return ""; -} - -double FliVariableObjHdl::get_signal_value_real(void) -{ - LOG_ERROR("Getting variable value as double not currently supported!"); - return -1; -} - -long FliVariableObjHdl::get_signal_value_long(void) -{ - LOG_ERROR("Getting variable value as long not currently supported!"); - return -1; -} - -int FliVariableObjHdl::set_signal_value(const long value) -{ - LOG_ERROR("Setting variable value not currently supported!"); - return -1; -} - -int FliVariableObjHdl::set_signal_value(std::string &value) -{ - LOG_ERROR("Setting variable value not currently supported!"); - return -1; -} - -int FliVariableObjHdl::set_signal_value(const double value) -{ - LOG_ERROR("Setting variable value not currently supported"); - return -1; -} - -int FliVariableObjHdl::initialise(std::string &name, std::string &fq_name) -{ - mtiVariableIdT m_fli_hdl = get_handle(); - - /* Pre allocte buffers on signal type basis */ - m_fli_type = mti_GetTypeKind(mti_GetVarType(m_fli_hdl)); - - switch (m_fli_type) { - case MTI_TYPE_ENUM: - m_val_len = 1; - break; - case MTI_TYPE_SCALAR: - case MTI_TYPE_PHYSICAL: - m_val_len = 32; - break; - case MTI_TYPE_ARRAY: - m_val_len = mti_TickLength(mti_GetVarType(m_fli_hdl)); - m_mti_buff = (mtiInt32T*)malloc(sizeof(*m_mti_buff) * m_val_len); - if (!m_mti_buff) { - LOG_CRITICAL("Unable to alloc mem for signal mti read buffer: ABORTING"); - } - break; - default: - LOG_ERROR("Unable to handle object type for %s (%d)", - name.c_str(), m_fli_type); - } - - m_val_buff = (char*)malloc(m_val_len+1); - if (!m_val_buff) { - LOG_CRITICAL("Unable to alloc mem for signal read buffer: ABORTING"); - } - m_val_buff[m_val_len] = '\0'; - - GpiObjHdl::initialise(name, fq_name); - - return 0; -} - From 7ddcfc39a1a5613193fdbfa4c37ead0d31e74e32 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Sun, 27 Dec 2015 23:33:54 -0700 Subject: [PATCH 1096/2656] Add support for multiple types --- lib/fli/FliCbHdl.cpp | 2 +- lib/fli/FliImpl.cpp | 196 +++++++++++------ lib/fli/FliImpl.h | 206 +++++++++++++++--- lib/fli/FliObjHdl.cpp | 492 +++++++++++++++++++++++++++++++++--------- 4 files changed, 706 insertions(+), 190 deletions(-) diff --git a/lib/fli/FliCbHdl.cpp b/lib/fli/FliCbHdl.cpp index 7446ed40..5a347131 100644 --- a/lib/fli/FliCbHdl.cpp +++ b/lib/fli/FliCbHdl.cpp @@ -118,7 +118,7 @@ int FliSimPhaseCbHdl::arm_callback(void) } FliSignalCbHdl::FliSignalCbHdl(GpiImplInterface *impl, - FliValueObjHdl *sig_hdl, + FliSignalObjHdl *sig_hdl, unsigned int edge) : GpiCbHdl(impl), FliProcessCbHdl(impl), GpiValueCbHdl(impl, sig_hdl, edge) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index f91ea4a9..84c7568c 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -107,6 +107,59 @@ void FliImpl::sim_end(void) mti_Cmd(stop); } +bool FliImpl::isValueConst(int kind) +{ + return (kind == accGeneric || kind == accVHDLConstant); +} + +bool FliImpl::isValueLogic(mtiTypeIdT type) +{ + mtiInt32T numEnums = mti_TickLength(type); + if (numEnums == 2) { + char **enum_values = mti_GetEnumValues(type); + std::string str0 = enum_values[0]; + std::string str1 = enum_values[1]; + + if (str0.compare("'0'") == 0 && str1.compare("'1'") == 0) { + return true; + } + } else if (numEnums == 9) { + const char enums[9][4] = {"'U'","'X'","'0'","'1'","'Z'","'W'","'L'","'H'","'-'"}; + char **enum_values = mti_GetEnumValues(type); + + for (int i = 0; i < 9; i++) { + std::string str = enum_values[i]; + if (str.compare(enums[i]) != 0) { + return false; + } + } + + return true; + } + + return false; +} + +bool FliImpl::isValueChar(mtiTypeIdT type) +{ + return (mti_TickLength(type) == 256); +} + +bool FliImpl::isValueBoolean(mtiTypeIdT type) +{ + if (mti_TickLength(type) == 2) { + char **enum_values = mti_GetEnumValues(type); + std::string strFalse = enum_values[0]; + std::string strTrue = enum_values[1]; + + if (strFalse.compare("false") == 0 && strTrue.compare("true") == 0) { + return true; + } + } + + return false; +} + GpiObjHdl *FliImpl::create_gpi_obj(std::string &name, std::string &fq_name) { GpiObjHdl *new_obj = NULL; @@ -114,65 +167,77 @@ GpiObjHdl *FliImpl::create_gpi_obj(std::string &name, std::string &fq_name) std::vector writable(fq_name.begin(), fq_name.end()); writable.push_back('\0'); - mtiRegionIdT rgn_hdl; - mtiSignalIdT sig_hdl; - mtiVariableIdT var_hdl; + void * hdl; - if ((rgn_hdl = mti_FindRegion(&writable[0])) != NULL) { - LOG_DEBUG("Found region %s -> %p", fq_name.c_str(), rgn_hdl); - new_obj = new GpiObjHdl(this, rgn_hdl, GPI_MODULE); - } else if ((sig_hdl = mti_FindSignal(&writable[0])) != NULL) { - LOG_DEBUG("Found a signal %s -> %p", fq_name.c_str(), sig_hdl); + if ((hdl = mti_FindRegion(&writable[0])) != NULL) { + LOG_DEBUG("Found region %s -> %p", fq_name.c_str(), hdl); + new_obj = new GpiObjHdl(this, hdl, GPI_MODULE); + } else { + bool is_var; + bool is_const; + mtiTypeIdT valType; + mtiTypeKindT typeKind; + + if ((hdl = mti_FindSignal(&writable[0])) != NULL) { + LOG_DEBUG("Found a signal %s -> %p", fq_name.c_str(), hdl); + is_var = false; + is_const = false; + valType = mti_GetSignalType(static_cast(hdl)); + } else if ((hdl = mti_FindVar(&writable[0])) != NULL) { + LOG_DEBUG("Found a variable %s -> %p", fq_name.c_str(), hdl); + is_var = true; + is_const = isValueConst(mti_GetVarKind(static_cast(hdl))); + valType = mti_GetVarType(static_cast(hdl)); + } else { + LOG_DEBUG("Didn't find anything named %s", &writable[0]); + return NULL; + } - gpi_objtype_t objtype; - mtiTypeKindT typeKind = mti_GetTypeKind(mti_GetSignalType(sig_hdl)); + typeKind = mti_GetTypeKind(valType); switch (typeKind) { case MTI_TYPE_ENUM: - objtype = GPI_REGISTER; // Assumes std_logic + if (isValueLogic(valType)) { + new_obj = new FliLogicObjHdl(this, hdl, GPI_ENUM, is_const, is_var, valType, typeKind); + } else if (isValueBoolean(valType) || isValueChar(valType)) { + new_obj = new FliIntObjHdl(this, hdl, GPI_INTEGER, is_const, is_var, valType, typeKind); + } else { + new_obj = new FliEnumObjHdl(this, hdl, GPI_ENUM, is_const, is_var, valType, typeKind); + } break; case MTI_TYPE_SCALAR: case MTI_TYPE_PHYSICAL: - objtype = GPI_INTEGER; + new_obj = new FliIntObjHdl(this, hdl, GPI_INTEGER, is_const, is_var, valType, typeKind); break; - case MTI_TYPE_ARRAY: - objtype = GPI_REGISTER; // Assumes std_logic_vector + case MTI_TYPE_REAL: + new_obj = new FliRealObjHdl(this, hdl, GPI_REAL, is_const, is_var, valType, typeKind); break; - default: - LOG_ERROR("Unable to handle object type for %s (%d)", - name.c_str(), typeKind); - } - - new_obj = new FliValueObjHdl(this, sig_hdl, objtype, false, false); - - } else if ((var_hdl = mti_FindVar(&writable[0])) != NULL) { - LOG_DEBUG("Found a variable %s -> %p", fq_name.c_str(), var_hdl); - - gpi_objtype_t objtype; - int varKind = mti_GetVarKind(var_hdl); - mtiTypeKindT typeKind = mti_GetTypeKind(mti_GetVarType(var_hdl)); - - switch (typeKind) { - case MTI_TYPE_ENUM: - objtype = GPI_REGISTER; // Assumes std_logic + case MTI_TYPE_ARRAY: { + mtiTypeIdT elemType = mti_GetArrayElementType(valType); + mtiTypeKindT elemTypeKind = mti_GetTypeKind(elemType); + + switch (elemTypeKind) { + case MTI_TYPE_ENUM: + if (isValueLogic(elemType)) { + new_obj = new FliLogicObjHdl(this, hdl, GPI_ARRAY, is_const, is_var, valType, typeKind); // std_logic_vector + } else if (isValueChar(elemType)) { + new_obj = new FliStringObjHdl(this, hdl, GPI_STRING, is_const, is_var, valType, typeKind); + } else { + new_obj = new GpiObjHdl(this, hdl, GPI_MODULE); // array of enums + } + break; + default: + new_obj = new GpiObjHdl(this, hdl, GPI_MODULE);// array of (array, Integer, Real, Record, etc.) + } + } break; - case MTI_TYPE_SCALAR: - case MTI_TYPE_PHYSICAL: - objtype = GPI_INTEGER; - break; - case MTI_TYPE_ARRAY: - objtype = GPI_REGISTER; // Assumes std_logic_vector + case MTI_TYPE_RECORD: + new_obj = new GpiObjHdl(this, hdl, GPI_STRUCTURE); break; default: - LOG_ERROR("Unable to handle object type for %s (%d)", - name.c_str(), typeKind); + LOG_ERROR("Unable to handle object type for %s (%d)", name.c_str(), typeKind); + return NULL; } - - new_obj = new FliValueObjHdl(this, var_hdl, objtype, (varKind != accVariable), true); - } - else { - LOG_DEBUG("Unable to query %s", fq_name.c_str()); - return NULL; } if (NULL == new_obj) { @@ -180,7 +245,11 @@ GpiObjHdl *FliImpl::create_gpi_obj(std::string &name, std::string &fq_name) return NULL; } - new_obj->initialise(name,fq_name); + if (new_obj->initialise(name,fq_name) < 0) { + LOG_ERROR("Failed to initialise the handle %s", name.c_str()); + return NULL; + } + return new_obj; } @@ -199,15 +268,18 @@ GpiObjHdl* FliImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) { std::string fq_name = parent->get_fullname(); + if (fq_name == "/") { fq_name += name; - } else { + } else if (parent->get_type() == GPI_MODULE) { fq_name += "/" + name; + } else if (parent->get_type() == GPI_STRUCTURE) { + fq_name += "." + name; + } else { + LOG_ERROR("FLI: Parent of type %d must be of type GPI_MODULE or GPI_STRUCTURE to have a child.", parent->get_type()); + return NULL; } - std::vector writable(fq_name.begin(), fq_name.end()); - writable.push_back('\0'); - - + LOG_DEBUG("Looking for child %s from %s", name.c_str(), parent->get_name_str()); return create_gpi_obj(name, fq_name); @@ -220,18 +292,20 @@ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) */ GpiObjHdl* FliImpl::native_check_create(uint32_t index, GpiObjHdl *parent) { - char buff[15]; - snprintf(buff, 15, "(%u)", index); - std::string idx = buff; - std::string name = parent->get_name() + idx; - std::string fq_name = parent->get_fullname() + idx; - std::vector writable(fq_name.begin(), fq_name.end()); - writable.push_back('\0'); - + if (parent->get_type() == GPI_MODULE or parent->get_type() == GPI_ARRAY) { + char buff[15]; + snprintf(buff, 15, "(%u)", index); + std::string idx = buff; + std::string name = parent->get_name() + idx; + std::string fq_name = parent->get_fullname() + idx; - LOG_DEBUG("Looking for index %u from %s", index, parent->get_name_str()); + LOG_DEBUG("Looking for index %u from %s", index, parent->get_name_str()); - return create_gpi_obj(name, fq_name); + return create_gpi_obj(name, fq_name); + } else { + LOG_ERROR("FLI: Parent of type %d must be of type GPI_MODULE or GPI_ARRAY to have an index.", parent->get_type()); + return NULL; + } } const char *FliImpl::reason_to_string(int reason) diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index 536d901d..8144ca82 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -32,6 +32,7 @@ #include "mti.h" #include +#include extern "C" { void fli_elab_cb(void *nothing); @@ -40,7 +41,7 @@ void handle_fli_callback(void *data); } class FliImpl; -class FliValueObjHdl; +class FliSignalObjHdl; // Callback handles @@ -66,7 +67,7 @@ class FliSignalCbHdl : public FliProcessCbHdl, public GpiValueCbHdl { public: FliSignalCbHdl(GpiImplInterface *impl, - FliValueObjHdl *sig_hdl, + FliSignalObjHdl *sig_hdl, unsigned int edge); virtual ~FliSignalCbHdl() { } @@ -141,30 +142,51 @@ class FliTimedCbHdl : public FliProcessCbHdl { // Object Handles -class FliValueObjHdl : public GpiSignalObjHdl { +class FliSignalObjHdl : public GpiSignalObjHdl { +public: + FliSignalObjHdl(GpiImplInterface *impl, + void *hdl, + gpi_objtype_t objtype, + bool is_const, + bool is_var) : + GpiSignalObjHdl(impl, hdl, objtype, is_const), + m_is_var(is_var), + m_rising_cb(impl, this, GPI_RISING), + m_falling_cb(impl, this, GPI_FALLING), + m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } + + virtual ~FliSignalObjHdl() { } + + virtual GpiCbHdl *value_change_cb(unsigned int edge); + virtual int initialise(std::string &name, std::string &fq_name); + +protected: + bool m_is_var; + FliSignalCbHdl m_rising_cb; + FliSignalCbHdl m_falling_cb; + FliSignalCbHdl m_either_cb; +}; + +class FliValueObjHdl : public FliSignalObjHdl { public: FliValueObjHdl(GpiImplInterface *impl, void *hdl, gpi_objtype_t objtype, bool is_const, - bool is_var) : GpiSignalObjHdl(impl, hdl, objtype, is_const), - m_fli_type(MTI_TYPE_SCALAR), - m_is_var(is_var), - m_rising_cb(impl, this, GPI_RISING), - m_falling_cb(impl, this, GPI_FALLING), - m_either_cb(impl, this, GPI_FALLING | GPI_RISING), - m_mti_buff(NULL), - m_val_buff(NULL), - m_val_str_buff(NULL), - m_val_len(0), - m_val_str_len(0) { } + bool is_var, + mtiTypeIdT valType, + mtiTypeKindT typeKind) : + FliSignalObjHdl(impl, hdl, objtype, is_const, is_var), + m_fli_type(typeKind), + m_val_type(valType), + m_mti_buff(NULL), + m_val_buff(NULL) { } + virtual ~FliValueObjHdl() { if (m_val_buff) free(m_val_buff); if (m_mti_buff) free(m_mti_buff); - if (m_val_str_buff) - free(m_val_str_buff); } virtual const char* get_signal_value_binstr(void); @@ -176,24 +198,140 @@ class FliValueObjHdl : public GpiSignalObjHdl { virtual int set_signal_value(const double value); virtual int set_signal_value(std::string &value); - /* Value change callback accessor */ - virtual GpiCbHdl *value_change_cb(unsigned int edge); virtual int initialise(std::string &name, std::string &fq_name); - protected: mtiTypeKindT m_fli_type; - bool m_is_var; - FliSignalCbHdl m_rising_cb; - FliSignalCbHdl m_falling_cb; - FliSignalCbHdl m_either_cb; + mtiTypeIdT m_val_type; mtiInt32T *m_mti_buff; char *m_val_buff; - char *m_val_str_buff; - int m_val_len; - int m_val_str_len; }; +class FliEnumObjHdl : public FliValueObjHdl { +public: + FliEnumObjHdl(GpiImplInterface *impl, + void *hdl, + gpi_objtype_t objtype, + bool is_const, + bool is_var, + mtiTypeIdT valType, + mtiTypeKindT typeKind) : + FliValueObjHdl(impl, hdl, objtype, is_const, is_var, valType, typeKind), + m_value_enum(NULL), + m_num_enum(0) { } + + virtual ~FliEnumObjHdl() { } + + const char* get_signal_value_str(void); + long get_signal_value_long(void); + + int set_signal_value(const long value); + + int initialise(std::string &name, std::string &fq_name); + +private: + char **m_value_enum; // Do Not Free + mtiInt32T m_num_enum; +}; + +class FliLogicObjHdl : public FliValueObjHdl { +public: + FliLogicObjHdl(GpiImplInterface *impl, + void *hdl, + gpi_objtype_t objtype, + bool is_const, + bool is_var, + mtiTypeIdT valType, + mtiTypeKindT typeKind) : + FliValueObjHdl(impl, + hdl, + objtype, + is_const, + is_var, + valType, + typeKind), + m_ascending(false), + m_value_enum(NULL), + m_num_enum(0), + m_enum_map() { } + + virtual ~FliLogicObjHdl() { } + + const char* get_signal_value_binstr(void); + + int set_signal_value(const long value); + int set_signal_value(std::string &value); + + int initialise(std::string &name, std::string &fq_name); + + +private: + bool m_ascending; + char **m_value_enum; // Do Not Free + mtiInt32T m_num_enum; + std::map m_enum_map; +}; + +class FliIntObjHdl : public FliValueObjHdl { +public: + FliIntObjHdl(GpiImplInterface *impl, + void *hdl, + gpi_objtype_t objtype, + bool is_const, + bool is_var, + mtiTypeIdT valType, + mtiTypeKindT typeKind) : + FliValueObjHdl(impl, hdl, objtype, is_const, is_var, valType, typeKind) { } + + virtual ~FliIntObjHdl() { } + + const char* get_signal_value_binstr(void); + long get_signal_value_long(void); + + int set_signal_value(const long value); + + int initialise(std::string &name, std::string &fq_name); +}; + +class FliRealObjHdl : public FliValueObjHdl { +public: + FliRealObjHdl(GpiImplInterface *impl, + void *hdl, + gpi_objtype_t objtype, + bool is_const, + bool is_var, + mtiTypeIdT valType, + mtiTypeKindT typeKind) : + FliValueObjHdl(impl, hdl, objtype, is_const, is_var, valType, typeKind) { } + + virtual ~FliRealObjHdl() { } + + double get_signal_value_real(void); + + int set_signal_value(const double value); + + int initialise(std::string &name, std::string &fq_name); +}; + +class FliStringObjHdl : public FliValueObjHdl { +public: + FliStringObjHdl(GpiImplInterface *impl, + void *hdl, + gpi_objtype_t objtype, + bool is_const, + bool is_var, + mtiTypeIdT valType, + mtiTypeKindT typeKind) : + FliValueObjHdl(impl, hdl, objtype, is_const, is_var, valType, typeKind) { } + + virtual ~FliStringObjHdl() { } + + const char* get_signal_value_str(void); + + int set_signal_value(std::string &value); + + int initialise(std::string &name, std::string &fq_name); +}; class FliTimerCache { public: @@ -208,14 +346,13 @@ class FliTimerCache { FliImpl *impl; }; - class FliImpl : public GpiImplInterface { public: FliImpl(const std::string& name) : GpiImplInterface(name), + cache(this), m_readonly_cbhdl(this), m_nexttime_cbhdl(this), - m_readwrite_cbhdl(this), - cache(this) { } + m_readwrite_cbhdl(this) { } /* Sim related */ void sim_end(void); @@ -241,12 +378,19 @@ class FliImpl : public GpiImplInterface { /* Method to provide strings from operation types */ GpiObjHdl *create_gpi_obj(std::string &name, std::string &fq_name); +private: + bool isValueConst(int kind); + bool isValueLogic(mtiTypeIdT type); + bool isValueChar(mtiTypeIdT type); + bool isValueBoolean(mtiTypeIdT type); + +public: + FliTimerCache cache; + private: FliReadOnlyCbHdl m_readonly_cbhdl; FliNextPhaseCbHdl m_nexttime_cbhdl; FliReadWriteCbHdl m_readwrite_cbhdl; -public: - FliTimerCache cache; }; #endif /*COCOTB_FLI_IMPL_H_ */ diff --git a/lib/fli/FliObjHdl.cpp b/lib/fli/FliObjHdl.cpp index fa753d1f..00af9289 100644 --- a/lib/fli/FliObjHdl.cpp +++ b/lib/fli/FliObjHdl.cpp @@ -26,10 +26,11 @@ ******************************************************************************/ #include +#include #include "FliImpl.h" -GpiCbHdl *FliValueObjHdl::value_change_cb(unsigned int edge) +GpiCbHdl *FliSignalObjHdl::value_change_cb(unsigned int edge) { FliSignalCbHdl *cb = NULL; @@ -55,59 +56,179 @@ GpiCbHdl *FliValueObjHdl::value_change_cb(unsigned int edge) return NULL; } - return (GpiValueCbHdl*)cb; + return (GpiCbHdl *)cb; } -static const char value_enum[10] = "UX01ZWLH-"; +int FliSignalObjHdl::initialise(std::string &name, std::string &fq_name) +{ + return GpiObjHdl::initialise(name, fq_name); +} + +int FliValueObjHdl::initialise(std::string &name, std::string &fq_name) +{ + return FliSignalObjHdl::initialise(name, fq_name); +} const char* FliValueObjHdl::get_signal_value_binstr(void) { + LOG_ERROR("Getting signal/variable value as binstr not supported for %s of type %d", m_fullname.c_str(), m_type); + return NULL; +} + +const char* FliValueObjHdl::get_signal_value_str(void) +{ + LOG_ERROR("Getting signal/variable value as str not supported for %s of type %d", m_fullname.c_str(), m_type); + return NULL; +} +double FliValueObjHdl::get_signal_value_real(void) +{ + LOG_ERROR("Getting signal/variable value as double not supported for %s of type %d", m_fullname.c_str(), m_type); + return -1; +} + +long FliValueObjHdl::get_signal_value_long(void) +{ + LOG_ERROR("Getting signal/variable value as long not supported for %s of type %d", m_fullname.c_str(), m_type); + return -1; +} + +int FliValueObjHdl::set_signal_value(const long value) +{ + LOG_ERROR("Setting signal/variable value via long not supported for %s of type %d", m_fullname.c_str(), m_type); + return -1; +} + +int FliValueObjHdl::set_signal_value(std::string &value) +{ + LOG_ERROR("Setting signal/variable value via string not supported for %s of type %d", m_fullname.c_str(), m_type); + return -1; +} + +int FliValueObjHdl::set_signal_value(const double value) +{ + LOG_ERROR("Setting signal/variable value via double not supported for %s of type %d", m_fullname.c_str(), m_type); + return -1; +} + +int FliEnumObjHdl::initialise(std::string &name, std::string &fq_name) +{ + m_num_elems = 1; + m_value_enum = mti_GetEnumValues(m_val_type); + m_num_enum = mti_TickLength(m_val_type); + + return FliValueObjHdl::initialise(name, fq_name); +} + +const char* FliEnumObjHdl::get_signal_value_str(void) +{ + if (m_is_var) { + return m_value_enum[mti_GetVarValue(get_handle())]; + } else { + return m_value_enum[mti_GetSignalValue(get_handle())]; + } +} + +long FliEnumObjHdl::get_signal_value_long(void) +{ + if (m_is_var) { + return (long)mti_GetVarValue(get_handle()); + } else { + return (long)mti_GetSignalValue(get_handle()); + } +} + +int FliEnumObjHdl::set_signal_value(const long value) +{ + if (m_const) { + LOG_ERROR("Attempted to set a constant signal/variable!\n"); + return -1; + } + + if (value > m_num_enum || value < 0) { + LOG_ERROR("Attempted to set a enum with range [0,%d] with invalid value %d!\n", m_num_enum, value); + return -1; + } + + if (m_is_var) { + mti_SetVarValue(get_handle(), value); + } else { + mti_SetSignalValue(get_handle(), value); + } + + return 0; +} + +int FliLogicObjHdl::initialise(std::string &name, std::string &fq_name) +{ switch (m_fli_type) { case MTI_TYPE_ENUM: - if (m_is_var) { - m_val_buff[0] = value_enum[mti_GetVarValue(get_handle())]; - } else { - m_val_buff[0] = value_enum[mti_GetSignalValue(get_handle())]; + m_num_elems = 1; + m_value_enum = mti_GetEnumValues(m_val_type); + m_num_enum = mti_TickLength(m_val_type); + break; + case MTI_TYPE_ARRAY: { + mtiTypeIdT elemType = mti_GetArrayElementType(m_val_type); + int buff_len = 0; + + m_ascending = (mti_TickDir(m_val_type) == 1); + m_value_enum = mti_GetEnumValues(elemType); + m_num_enum = mti_TickLength(elemType); + m_num_elems = mti_TickLength(m_val_type); + buff_len = (m_num_elems + (sizeof(*m_mti_buff) - 1)) / sizeof(*m_mti_buff); + + m_mti_buff = (mtiInt32T*)malloc(sizeof(*m_mti_buff) * buff_len); + if (!m_mti_buff) { + LOG_CRITICAL("Unable to alloc mem for value object mti read buffer: ABORTING"); + return -1; + } } break; - case MTI_TYPE_SCALAR: - case MTI_TYPE_PHYSICAL: { - unsigned long val = 0; + default: + LOG_CRITICAL("Object type is not 'logic' for %s (%d)", name.c_str(), m_fli_type); + return -1; + } - if (m_is_var) { - val = (unsigned long)mti_GetVarValue(get_handle()); - } else { - val = (unsigned long)mti_GetSignalValue(get_handle()); - } + for (mtiInt32T i = 0; i < m_num_enum; i++) { + m_enum_map[m_value_enum[i][1]] = i; // enum is of the format 'U' or '0', etc. + } + + m_val_buff = (char*)malloc(m_num_elems+1); + if (!m_val_buff) { + LOG_CRITICAL("Unable to alloc mem for value object read buffer: ABORTING"); + } + m_val_buff[m_num_elems] = '\0'; - std::bitset<32> value(val); - std::string bin_str = value.to_string(); - snprintf(m_val_buff, m_val_len+1, "%s", bin_str.c_str()); + return FliValueObjHdl::initialise(name, fq_name); +} + +const char* FliLogicObjHdl::get_signal_value_binstr(void) +{ + switch (m_fli_type) { + case MTI_TYPE_ENUM: + if (m_is_var) { + m_val_buff[0] = m_value_enum[mti_GetVarValue(get_handle())][1]; + } else { + m_val_buff[0] = m_value_enum[mti_GetSignalValue(get_handle())][1]; } break; case MTI_TYPE_ARRAY: { + char *iter = (char*)m_mti_buff; + if (m_is_var) { mti_GetArrayVarValue(get_handle(), m_mti_buff); } else { mti_GetArraySignalValue(get_handle(), m_mti_buff); } - if (m_val_len <= 256) { - char *iter = (char*)m_mti_buff; - for (int i = 0; i < m_val_len; i++ ) { - m_val_buff[i] = value_enum[(int)iter[i]]; - } - } else { - for (int i = 0; i < m_val_len; i++ ) { - m_val_buff[i] = value_enum[m_mti_buff[i]]; - } + + for (int i = 0; i < m_num_elems; i++ ) { + m_val_buff[i] = m_value_enum[(int)iter[i]][1]; } } break; default: - LOG_ERROR("Value Object %s of type %d is not currently supported", - m_name.c_str(), m_fli_type); - break; + LOG_CRITICAL("Object type is not 'logic' for %s (%d)", m_name.c_str(), m_fli_type); + return NULL; } LOG_DEBUG("Retrieved \"%s\" for value object %s", m_val_buff, m_name.c_str()); @@ -115,117 +236,294 @@ const char* FliValueObjHdl::get_signal_value_binstr(void) return m_val_buff; } -const char* FliValueObjHdl::get_signal_value_str(void) +int FliLogicObjHdl::set_signal_value(const long value) { - LOG_ERROR("Getting signal value as str not currently supported"); - return NULL; + if (m_const) { + LOG_ERROR("Attempted to set a constant signal/variable!\n"); + return -1; + } + + if (m_num_elems == 1) { + mtiInt32T enumVal = value ? m_enum_map['1'] : m_enum_map['0']; + + if (m_is_var) { + mti_SetVarValue(get_handle(), enumVal); + } else { + mti_SetSignalValue(get_handle(), enumVal); + } + } else { + int valLen = sizeof(value) * 8; + char *iter = (char *)m_mti_buff; + mtiInt32T enumVal; + int numPad; + int idx; + + numPad = valLen < m_num_elems ? m_num_elems-valLen : 0; + valLen = valLen > m_num_elems ? m_num_elems : valLen; + + LOG_DEBUG("set_signal_value(long)::0x%016x", value); + + // Pad MSB for descending vector + for (idx = 0; idx < numPad && !m_ascending; idx++) { + iter[idx] = (char)m_enum_map['0']; + } + + if (m_ascending) { + for (int i = 0; i < valLen; i++) { + enumVal = value&(1L<(), (long)iter); + } else { + mti_SetSignalValue(get_handle(), (long)iter); + } + } + + return 0; } -double FliValueObjHdl::get_signal_value_real(void) +int FliLogicObjHdl::set_signal_value(std::string &value) { - LOG_ERROR("Getting signal value as double not currently supported!"); - return -1; + if (m_const) { + LOG_ERROR("Attempted to set a constant signal/variable!\n"); + return -1; + } + + if (m_num_elems == 1) { + mtiInt32T enumVal = m_enum_map[value.c_str()[0]]; + + if (m_is_var) { + mti_SetVarValue(get_handle(), enumVal); + } else { + mti_SetSignalValue(get_handle(), enumVal); + } + } else { + int len = value.length(); + int numPad; + + numPad = len < m_num_elems ? m_num_elems-len : 0; + + LOG_DEBUG("set_signal_value(string)::%s", value.c_str()); + + if (len > m_num_elems) { + LOG_DEBUG("FLI: Attempt to write sting longer than (%s) signal %d > %d", m_name.c_str(), len, m_num_elems); + len = m_num_elems; + } + + char *iter = (char *)m_mti_buff; + mtiInt32T enumVal; + std::string::iterator valIter; + int i = 0; + + // Pad MSB for descending vector + for (i = 0; i < numPad && !m_ascending; i++) { + iter[i] = (char)m_enum_map['0']; + } + + for (valIter = value.begin(); (valIter != value.end()) && (i < m_num_elems); valIter++, i++) { + enumVal = m_enum_map[*valIter]; + iter[i] = (char)enumVal; + } + + // Fill bits a the end of the value to 0's + for (i = len; i < m_num_elems && m_ascending; i++) { + iter[i] = (char)m_enum_map['0']; + } + + if (m_is_var) { + mti_SetVarValue(get_handle(), (long)iter); + } else { + mti_SetSignalValue(get_handle(), (long)iter); + } + } + + return 0; } -long FliValueObjHdl::get_signal_value_long(void) +int FliIntObjHdl::initialise(std::string &name, std::string &fq_name) { - LOG_ERROR("Getting signal value as long not currently supported!"); - return -1; + m_num_elems = 1; + + m_val_buff = (char*)malloc(33); // Integers are always 32-bits + if (!m_val_buff) { + LOG_CRITICAL("Unable to alloc mem for value object read buffer: ABORTING"); + return -1; + } + m_val_buff[m_num_elems] = '\0'; + + return FliValueObjHdl::initialise(name, fq_name); } -int FliValueObjHdl::set_signal_value(const long value) +const char* FliIntObjHdl::get_signal_value_binstr(void) { - int rc; - char buff[20]; - - snprintf(buff, 20, "16#%016X", (int)value); + mtiInt32T val; if (m_is_var) { - rc = 0; - LOG_WARN("Setting a variable is not currently supported!\n"); + val = mti_GetVarValue(get_handle()); } else { - rc = mti_ForceSignal(get_handle(), &buff[0], 0, MTI_FORCE_DEPOSIT, -1, -1); + val = mti_GetSignalValue(get_handle()); } - if (!rc) { - LOG_ERROR("Setting signal value failed!\n"); - } - return rc-1; + std::bitset<32> value((unsigned long)val); + std::string bin_str = value.to_string(); + snprintf(m_val_buff, 33, "%s", bin_str.c_str()); + + return m_val_buff; } -int FliValueObjHdl::set_signal_value(std::string &value) +long FliIntObjHdl::get_signal_value_long(void) { - int rc; + mtiInt32T value; + + if (m_is_var) { + value = mti_GetVarValue(get_handle()); + } else { + value = mti_GetSignalValue(get_handle()); + } - snprintf(m_val_str_buff, m_val_str_len+1, "%d'b%s", m_val_len, value.c_str()); + return (long)value; +} + +int FliIntObjHdl::set_signal_value(const long value) +{ + if (m_const) { + LOG_ERROR("Attempted to set a constant signal/variable!\n"); + return -1; + } if (m_is_var) { - rc = 0; - LOG_WARN("Setting a variable is not currently supported!\n"); + mti_SetVarValue(get_handle(), value); } else { - rc = mti_ForceSignal(get_handle(), &m_val_str_buff[0], 0, MTI_FORCE_DEPOSIT, -1, -1); + mti_SetSignalValue(get_handle(), value); } - if (!rc) { - LOG_ERROR("Setting signal value failed!\n"); + return 0; +} + +int FliRealObjHdl::initialise(std::string &name, std::string &fq_name) +{ + int buff_len = 0; + + m_num_elems = 1; + buff_len = (sizeof(double) + (sizeof(*m_mti_buff) - 1)) / sizeof(*m_mti_buff); + + m_mti_buff = (mtiInt32T*)malloc(sizeof(*m_mti_buff) * buff_len); + if (!m_mti_buff) { + LOG_CRITICAL("Unable to alloc mem for value object mti read buffer: ABORTING"); + return -1; } - return rc-1; + + return FliValueObjHdl::initialise(name, fq_name); } -int FliValueObjHdl::set_signal_value(const double value) +double FliRealObjHdl::get_signal_value_real(void) { - LOG_ERROR("Setting Signal via double not supported!"); - return -1; + double *iter = (double *)m_mti_buff; + + if (m_is_var) { + mti_GetVarValueIndirect(get_handle(), m_mti_buff); + } else { + mti_GetSignalValueIndirect(get_handle(), m_mti_buff); + } + + LOG_DEBUG("Retrieved \"%f\" for value object %s", iter[0], m_name.c_str()); + + return iter[0]; } -int FliValueObjHdl::initialise(std::string &name, std::string &fq_name) +int FliRealObjHdl::set_signal_value(const double value) { - mtiTypeIdT valType; + double *iter = (double *)m_mti_buff; + + if (m_const) { + LOG_ERROR("Attempted to set a constant signal/variable!\n"); + return -1; + } + + iter[0] = value; - /* Pre allocte buffers on signal type basis */ if (m_is_var) { - valType = mti_GetVarType(get_handle()); + mti_SetVarValue(get_handle(), (long)iter); } else { - valType = mti_GetSignalType(get_handle()); + mti_SetSignalValue(get_handle(), (long)iter); } - m_fli_type = mti_GetTypeKind(valType); + return 0; +} - switch (m_fli_type) { - case MTI_TYPE_ENUM: - m_val_len = 1; - m_val_str_len = 3+m_val_len; - break; - case MTI_TYPE_SCALAR: - case MTI_TYPE_PHYSICAL: - m_val_len = 32; - m_val_str_len = 4+m_val_len; - break; - case MTI_TYPE_ARRAY: - m_val_len = mti_TickLength(valType); - m_val_str_len = snprintf(NULL, 0, "%d'b", m_val_len)+m_val_len; - m_mti_buff = (mtiInt32T*)malloc(sizeof(*m_mti_buff) * m_val_len); - if (!m_mti_buff) { - LOG_CRITICAL("Unable to alloc mem for value object mti read buffer: ABORTING"); - } - break; - default: - LOG_ERROR("Unable to handle object type for %s (%d)", - name.c_str(), m_fli_type); +int FliStringObjHdl::initialise(std::string &name, std::string &fq_name) +{ + int buff_len = 0; + + m_num_elems = mti_TickLength(m_val_type); + buff_len = (m_num_elems + (sizeof(*m_mti_buff) - 1)) / sizeof(*m_mti_buff); + + m_mti_buff = (mtiInt32T*)malloc(sizeof(*m_mti_buff) * buff_len); + if (!m_mti_buff) { + LOG_CRITICAL("Unable to alloc mem for value object mti read buffer: ABORTING"); + return -1; } - m_val_buff = (char*)malloc(m_val_len+1); + m_val_buff = (char*)malloc(m_num_elems+1); if (!m_val_buff) { LOG_CRITICAL("Unable to alloc mem for value object read buffer: ABORTING"); + return -1; + } + m_val_buff[m_num_elems] = '\0'; + + return FliValueObjHdl::initialise(name, fq_name); +} + +const char* FliStringObjHdl::get_signal_value_str(void) +{ + char *iter = (char *)m_mti_buff; + + if (m_is_var) { + mti_GetArrayVarValue(get_handle(), m_mti_buff); + } else { + mti_GetArraySignalValue(get_handle(), m_mti_buff); } - m_val_buff[m_val_len] = '\0'; - m_val_str_buff = (char*)malloc(m_val_str_len+1); - if (!m_val_str_buff) { - LOG_CRITICAL("Unable to alloc mem for value object write buffer: ABORTING"); + + strncpy(m_val_buff, iter, m_num_elems); + + LOG_DEBUG("Retrieved \"%s\" for value object %s", m_val_buff, m_name.c_str()); + + return m_val_buff; +} + +int FliStringObjHdl::set_signal_value(std::string &value) +{ + char *iter = (char *)m_mti_buff; + + if (m_const) { + LOG_ERROR("Attempted to set a constant signal/variable!\n"); + return -1; } - m_val_str_buff[m_val_str_len] = '\0'; - GpiObjHdl::initialise(name, fq_name); + strncpy(iter, value.c_str(), m_num_elems); + + if (m_is_var) { + mti_SetVarValue(get_handle(), (long)iter); + } else { + mti_SetSignalValue(get_handle(), (long)iter); + } return 0; } From 6bfa07794256a9cdb374e8545e0433eb6681cabf Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Fri, 1 Jan 2016 19:59:46 -0700 Subject: [PATCH 1097/2656] Update makefiles to set GPI_IMPL to vhpi for questa and modelsim. --- tests/designs/uart2bus/Makefile | 4 ++-- tests/designs/viterbi_decoder_axi4s/Makefile | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/designs/uart2bus/Makefile b/tests/designs/uart2bus/Makefile index c67d9a78..5c9ef5f6 100644 --- a/tests/designs/uart2bus/Makefile +++ b/tests/designs/uart2bus/Makefile @@ -27,10 +27,10 @@ ifeq ($(SIM),aldec) GPI_EXTRA=vhpi endif ifeq ($(SIM),modelsim) - GPI_EXTRA=fli + GPI_EXTRA=vhpi endif ifeq ($(SIM),questa) - GPI_EXTRA=fli + GPI_EXTRA=vhpi endif ifeq ($(SIM),ius) GPI_EXTRA=vhpi diff --git a/tests/designs/viterbi_decoder_axi4s/Makefile b/tests/designs/viterbi_decoder_axi4s/Makefile index 45748628..1137e6af 100644 --- a/tests/designs/viterbi_decoder_axi4s/Makefile +++ b/tests/designs/viterbi_decoder_axi4s/Makefile @@ -57,10 +57,10 @@ ifeq ($(SIM),aldec) GPI_IMPL=vhpi endif ifeq ($(SIM),modelsim) - GPI_IMPL=fli + GPI_IMPL=vhpi endif ifeq ($(SIM),questa) - GPI_IMPL=fli + GPI_IMPL=vhpi endif ifeq ($(SIM),ius) GPI_IMPL=vhpi From 7c36c6da34a0cdde693aa818980d5dc5a2f3fc8d Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Fri, 1 Jan 2016 20:15:14 -0700 Subject: [PATCH 1098/2656] Fix Questa makefile to properly delete the RTL_LIBRARY --- makefiles/simulators/Makefile.questa | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 29d6a32c..5c477250 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -56,7 +56,7 @@ VSIM_ARGS += $(foreach gen, $(GENERICS),"-G $(gen)") endif $(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) | $(SIM_BUILD) - echo "if [file exists $(RTL_LIBRARY)] {vdel -all}" > $@ + echo "if [file exists $(RTL_LIBRARY)] {vdel -lib $(RTL_LIBRARY) -all}" > $@ echo "vlib $(RTL_LIBRARY)" >> $@ ifneq ($(VHDL_SOURCES),) echo "vcom -work $(RTL_LIBRARY) $(VCOM_ARGS) $(VHDL_SOURCES)" >> $@ From 67d3c8a09d3376bb5866ba129ef69c334b15ec5a Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Sat, 2 Jan 2016 14:22:13 -0700 Subject: [PATCH 1099/2656] Update GpiObjHdl::get_type_str() to support all GPI Types --- lib/gpi/GpiCbHdl.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index f8141dca..96083fc8 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -62,6 +62,9 @@ const char * GpiObjHdl::get_type_str(void) CASE_OPTION(GPI_ARRAY); CASE_OPTION(GPI_ENUM); CASE_OPTION(GPI_STRUCTURE); + CASE_OPTION(GPI_REAL); + CASE_OPTION(GPI_INTEGER); + CASE_OPTION(GPI_STRING); default: ret = "unknown"; } From 003235cbaddf5e1de9d7068b0c68a18fdff28c93 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Sun, 3 Jan 2016 19:50:46 -0700 Subject: [PATCH 1100/2656] Updates to add iteration to FLI --- lib/fli/FliImpl.cpp | 392 +++++++++++++++++++++++++++++++++++++++++--- lib/fli/FliImpl.h | 36 +++- 2 files changed, 405 insertions(+), 23 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 84c7568c..d031e79f 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -160,16 +160,30 @@ bool FliImpl::isValueBoolean(mtiTypeIdT type) return false; } -GpiObjHdl *FliImpl::create_gpi_obj(std::string &name, std::string &fq_name) +bool FliImpl::isTypeValue(int type) +{ + return (type == accAlias || type == accVHDLConstant || type == accGeneric + || type == accVariable || type == accSignal); +} + +bool FliImpl::isTypeSignal(int type, int full_type) +{ + return (type == accSignal || full_type == accAliasSignal); +} + +GpiObjHdl *FliImpl::create_gpi_obj_from_handle(void *hdl, std::string &name, std::string &fq_name) { GpiObjHdl *new_obj = NULL; - std::vector writable(fq_name.begin(), fq_name.end()); - writable.push_back('\0'); + PLI_INT32 accType = acc_fetch_type(hdl); + PLI_INT32 accFullType = acc_fetch_fulltype(hdl); - void * hdl; + if (!VS_TYPE_IS_VHDL(accFullType)) { + LOG_DEBUG("Handle is not a VHDL type."); + return NULL; + } - if ((hdl = mti_FindRegion(&writable[0])) != NULL) { + if (!isTypeValue(accType)) { LOG_DEBUG("Found region %s -> %p", fq_name.c_str(), hdl); new_obj = new GpiObjHdl(this, hdl, GPI_MODULE); } else { @@ -178,19 +192,16 @@ GpiObjHdl *FliImpl::create_gpi_obj(std::string &name, std::string &fq_name) mtiTypeIdT valType; mtiTypeKindT typeKind; - if ((hdl = mti_FindSignal(&writable[0])) != NULL) { + if (isTypeSignal(accType, accFullType)) { LOG_DEBUG("Found a signal %s -> %p", fq_name.c_str(), hdl); is_var = false; is_const = false; valType = mti_GetSignalType(static_cast(hdl)); - } else if ((hdl = mti_FindVar(&writable[0])) != NULL) { + } else { LOG_DEBUG("Found a variable %s -> %p", fq_name.c_str(), hdl); is_var = true; is_const = isValueConst(mti_GetVarKind(static_cast(hdl))); valType = mti_GetVarType(static_cast(hdl)); - } else { - LOG_DEBUG("Didn't find anything named %s", &writable[0]); - return NULL; } typeKind = mti_GetTypeKind(valType); @@ -241,7 +252,7 @@ GpiObjHdl *FliImpl::create_gpi_obj(std::string &name, std::string &fq_name) } if (NULL == new_obj) { - LOG_DEBUG("Didn't find anything named %s", &writable[0]); + LOG_DEBUG("Didn't find anything named %s", fq_name.c_str()); return NULL; } @@ -255,9 +266,20 @@ GpiObjHdl *FliImpl::create_gpi_obj(std::string &name, std::string &fq_name) GpiObjHdl* FliImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) { - LOG_WARN("%s implementation can not create from raw handle", - get_name_c()); - return NULL; + LOG_DEBUG("Trying to convert a raw handle to an FLI Handle."); + + const char * c_name = acc_fetch_name(raw_hdl); + const char * c_fullname = acc_fetch_fullname(raw_hdl); + + if (!c_name) { + LOG_DEBUG("Unable to query the name of the raw handle."); + return NULL; + } + + std::string name = c_name; + std::string fq_name = c_fullname; + + return create_gpi_obj_from_handle(raw_hdl, name, fq_name); } /** @@ -279,10 +301,26 @@ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) LOG_ERROR("FLI: Parent of type %d must be of type GPI_MODULE or GPI_STRUCTURE to have a child.", parent->get_type()); return NULL; } - + LOG_DEBUG("Looking for child %s from %s", name.c_str(), parent->get_name_str()); - return create_gpi_obj(name, fq_name); + std::vector writable(fq_name.begin(), fq_name.end()); + writable.push_back('\0'); + + HANDLE hdl; + + if ((hdl = mti_FindRegion(&writable[0])) != NULL) { + LOG_DEBUG("Found region %s -> %p", fq_name.c_str(), hdl); + } else if ((hdl = mti_FindSignal(&writable[0])) != NULL) { + LOG_DEBUG("Found a signal %s -> %p", fq_name.c_str(), hdl); + } else if ((hdl = mti_FindVar(&writable[0])) != NULL) { + LOG_DEBUG("Found a variable %s -> %p", fq_name.c_str(), hdl); + } else { + LOG_DEBUG("Didn't find anything named %s", &writable[0]); + return NULL; + } + + return create_gpi_obj_from_handle(hdl, name, fq_name); } /** @@ -301,7 +339,23 @@ GpiObjHdl* FliImpl::native_check_create(uint32_t index, GpiObjHdl *parent) LOG_DEBUG("Looking for index %u from %s", index, parent->get_name_str()); - return create_gpi_obj(name, fq_name); + std::vector writable(fq_name.begin(), fq_name.end()); + writable.push_back('\0'); + + HANDLE hdl; + + if ((hdl = mti_FindRegion(&writable[0])) != NULL) { + LOG_DEBUG("Found region %s -> %p", fq_name.c_str(), hdl); + } else if ((hdl = mti_FindSignal(&writable[0])) != NULL) { + LOG_DEBUG("Found a signal %s -> %p", fq_name.c_str(), hdl); + } else if ((hdl = mti_FindVar(&writable[0])) != NULL) { + LOG_DEBUG("Found a variable %s -> %p", fq_name.c_str(), hdl); + } else { + LOG_DEBUG("Didn't find anything named %s", &writable[0]); + return NULL; + } + + return create_gpi_obj_from_handle(hdl, name, fq_name); } else { LOG_ERROR("FLI: Parent of type %d must be of type GPI_MODULE or GPI_ARRAY to have an index.", parent->get_type()); return NULL; @@ -364,7 +418,7 @@ GpiObjHdl *FliImpl::get_root_handle(const char *name) LOG_DEBUG("Found toplevel: %s, creating handle....", root_name.c_str()); - return create_gpi_obj(root_name, root_fullname); + return create_gpi_obj_from_handle(root, root_name, root_fullname); error: @@ -425,10 +479,304 @@ int FliImpl::deregister_callback(GpiCbHdl *gpi_hdl) GpiIterator *FliImpl::iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) { - /* This function should return a class derived from GpiIterator and follows it's - interface. Specifically it's new_handle(std::string, std::string) method and - return values. Using VpiIterator as an example */ - return NULL; + GpiIterator *new_iter = NULL; + + switch (type) { + case GPI_OBJECTS: + new_iter = new FliIterator(this, obj_hdl); + break; + default: + LOG_WARN("Other iterator types not implemented yet"); + break; + } + + return new_iter; +} + +void fli_mappings(GpiIteratorMapping &map) +{ + FliIterator::OneToMany region_options[] = { + FliIterator::OTM_CONSTANTS, + FliIterator::OTM_SIGNALS, + FliIterator::OTM_REGIONS, + FliIterator::OTM_END, + }; + map.add_to_options(accArchitecture, ®ion_options[0]); + map.add_to_options(accEntityVitalLevel0, ®ion_options[0]); + map.add_to_options(accArchVitalLevel0, ®ion_options[0]); + map.add_to_options(accArchVitalLevel1, ®ion_options[0]); + map.add_to_options(accBlock, ®ion_options[0]); + map.add_to_options(accCompInst, ®ion_options[0]); + map.add_to_options(accDirectInst, ®ion_options[0]); + map.add_to_options(accinlinedBlock, ®ion_options[0]); + map.add_to_options(accinlinedinnerBlock, ®ion_options[0]); + map.add_to_options(accGenerate, ®ion_options[0]); + map.add_to_options(accIfGenerate, ®ion_options[0]); + map.add_to_options(accElsifGenerate, ®ion_options[0]); + map.add_to_options(accElseGenerate, ®ion_options[0]); + map.add_to_options(accForGenerate, ®ion_options[0]); + map.add_to_options(accCaseGenerate, ®ion_options[0]); + map.add_to_options(accCaseOTHERSGenerate, ®ion_options[0]); + map.add_to_options(accConfiguration, ®ion_options[0]); + + FliIterator::OneToMany signal_options[] = { + FliIterator::OTM_SIGNAL_SUB_ELEMENTS, + FliIterator::OTM_END, + }; + map.add_to_options(accSignal, &signal_options[0]); + map.add_to_options(accSignalBit, &signal_options[0]); + map.add_to_options(accSignalSubComposite, &signal_options[0]); + map.add_to_options(accAliasSignal, &signal_options[0]); + + FliIterator::OneToMany variable_options[] = { + FliIterator::OTM_VARIABLE_SUB_ELEMENTS, + FliIterator::OTM_END, + }; + map.add_to_options(accVariable, &variable_options[0]); + map.add_to_options(accGeneric, &variable_options[0]); + map.add_to_options(accGenericConstant, &variable_options[0]); + map.add_to_options(accAliasConstant, &variable_options[0]); + map.add_to_options(accAliasGeneric, &variable_options[0]); + map.add_to_options(accAliasVariable, &variable_options[0]); + map.add_to_options(accVHDLConstant, &variable_options[0]); +} + +GpiIteratorMapping FliIterator::iterate_over(fli_mappings); + +FliIterator::FliIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(impl, hdl), + m_vars(), + m_sigs(), + m_regs(), + m_currentHandles(NULL) +{ + HANDLE fli_hdl = m_parent->get_handle(); + + int type = acc_fetch_fulltype(fli_hdl); + + LOG_DEBUG("fli_iterator::Create iterator for %s of type %s", m_parent->get_fullname().c_str(), acc_fetch_type_str(type)); + + if (NULL == (selected = iterate_over.get_options(type))) { + LOG_WARN("FLI: Implementation does not know how to iterate over %s(%d)", + acc_fetch_type_str(type), type); + return; + } + + /* Find the first mapping type that yields a valid iterator */ + for (one2many = selected->begin(); one2many != selected->end(); one2many++) { + populate_handle_list(*one2many); + + switch (*one2many) { + case FliIterator::OTM_CONSTANTS: + case FliIterator::OTM_VARIABLE_SUB_ELEMENTS: + m_currentHandles = &m_vars; + m_iterator = m_vars.begin(); + break; + case FliIterator::OTM_SIGNALS: + case FliIterator::OTM_SIGNAL_SUB_ELEMENTS: + m_currentHandles = &m_sigs; + m_iterator = m_sigs.begin(); + break; + case FliIterator::OTM_REGIONS: + m_currentHandles = &m_regs; + m_iterator = m_regs.begin(); + break; + default: + LOG_WARN("Unhandled OneToMany Type (%d)", *one2many); + } + + if (m_iterator != m_currentHandles->end()) + break; + + LOG_DEBUG("fli_iterator OneToMany=%d returned NULL", *one2many); + } + + if (m_iterator == m_currentHandles->end()) { + LOG_DEBUG("fli_iterator return NULL for all relationships on %s (%d) kind:%s", + m_parent->get_name_str(), type, acc_fetch_type_str(type)); + selected = NULL; + return; + } + + LOG_DEBUG("Created iterator working from scope %d", + *one2many); +} + +GpiIterator::Status FliIterator::next_handle(std::string &name, GpiObjHdl **hdl, void **raw_hdl) +{ + HANDLE obj; + GpiObjHdl *new_obj; + + if (!selected) + return GpiIterator::END; + + /* We want the next object in the current mapping. + * If the end of mapping is reached then we want to + * try next one until a new object is found + */ + do { + obj = NULL; + + if (m_iterator != m_currentHandles->end()) { + obj = *m_iterator++; + break; + } else { + LOG_DEBUG("No more valid handles in the current OneToMany=%d iterator", *one2many); + } + + if (++one2many >= selected->end()) { + obj = NULL; + break; + } + + populate_handle_list(*one2many); + + switch (*one2many) { + case FliIterator::OTM_CONSTANTS: + case FliIterator::OTM_VARIABLE_SUB_ELEMENTS: + m_currentHandles = &m_vars; + m_iterator = m_vars.begin(); + break; + case FliIterator::OTM_SIGNALS: + case FliIterator::OTM_SIGNAL_SUB_ELEMENTS: + m_currentHandles = &m_sigs; + m_iterator = m_sigs.begin(); + break; + case FliIterator::OTM_REGIONS: + m_currentHandles = &m_regs; + m_iterator = m_regs.begin(); + break; + default: + LOG_WARN("Unhandled OneToMany Type (%d)", *one2many); + } + } while (!obj); + + if (NULL == obj) { + LOG_DEBUG("No more children, all relationships tested"); + return GpiIterator::END; + } + + const char *c_name; + switch (*one2many) { + case FliIterator::OTM_CONSTANTS: + case FliIterator::OTM_VARIABLE_SUB_ELEMENTS: + c_name = mti_GetVarName(static_cast(obj)); + break; + case FliIterator::OTM_SIGNALS: + c_name = mti_GetSignalName(static_cast(obj)); + break; + case FliIterator::OTM_SIGNAL_SUB_ELEMENTS: + c_name = mti_GetSignalNameIndirect(static_cast(obj), NULL, 0); + break; + case FliIterator::OTM_REGIONS: + c_name = mti_GetRegionName(static_cast(obj)); + break; + default: + LOG_WARN("Unhandled OneToMany Type (%d)", *one2many); + } + + if (!c_name) { + int accFullType = acc_fetch_fulltype(obj); + + if (!VS_TYPE_IS_VHDL(accFullType)) { + *raw_hdl = (void *)obj; + return GpiIterator::NOT_NATIVE_NO_NAME; + } + + return GpiIterator::NATIVE_NO_NAME; + } + + name = c_name; + + if (*one2many == FliIterator::OTM_SIGNAL_SUB_ELEMENTS) { + mti_VsimFree((void *)c_name); + } + + std::string fq_name = m_parent->get_fullname(); + if (fq_name == "/") { + fq_name += name; + } else if (m_parent->get_type() == GPI_STRUCTURE) { + fq_name += "." + name; + } else { + fq_name += "/" + name; + } + + FliImpl *fli_impl = reinterpret_cast(m_impl); + new_obj = fli_impl->create_gpi_obj_from_handle(obj, name, fq_name); + if (new_obj) { + *hdl = new_obj; + return GpiIterator::NATIVE; + } else { + return GpiIterator::NOT_NATIVE; + } +} + +void FliIterator::populate_handle_list(FliIterator::OneToMany childType) +{ + switch (childType) { + case FliIterator::OTM_CONSTANTS: { + mtiRegionIdT parent = m_parent->get_handle(); + mtiVariableIdT id; + + for (id = mti_FirstVarByRegion(parent); id; id = mti_NextVar()) { + if (id) { + m_vars.push_back(id); + } + } + } + break; + case FliIterator::OTM_SIGNALS: { + mtiRegionIdT parent = m_parent->get_handle(); + mtiSignalIdT id; + + for (id = mti_FirstSignal(parent); id; id = mti_NextSignal()) { + if (id) { + m_sigs.push_back(id); + } + } + } + break; + case FliIterator::OTM_REGIONS: { + mtiRegionIdT parent = m_parent->get_handle(); + mtiRegionIdT id; + + for (id = mti_FirstLowerRegion(parent); id; id = mti_NextRegion(id)) { + if (id) { + m_regs.push_back(id); + } + } + } + break; + case FliIterator::OTM_SIGNAL_SUB_ELEMENTS: + if (m_parent->get_type() == GPI_MODULE || m_parent->get_type() == GPI_STRUCTURE) { + mtiSignalIdT parent = m_parent->get_handle(); + + mtiTypeIdT type = mti_GetSignalType(parent); + mtiSignalIdT *ids = mti_GetSignalSubelements(parent,0); + + for (int i = 0; i < mti_TickLength(type); i++) { + m_sigs.push_back(ids[i]); + } + + mti_VsimFree(ids); + } + break; + case FliIterator::OTM_VARIABLE_SUB_ELEMENTS: + if (m_parent->get_type() == GPI_MODULE || m_parent->get_type() == GPI_STRUCTURE) { + mtiVariableIdT parent = m_parent->get_handle(); + + mtiTypeIdT type = mti_GetVarType(parent); + mtiVariableIdT *ids = mti_GetVarSubelements(parent,0); + + for (int i = 0; i < mti_TickLength(type); i++) { + m_vars.push_back(ids[i]); + } + + mti_VsimFree(ids); + } + break; + default: + LOG_WARN("Unhandled OneToMany Type (%d)", childType); + } } diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index 8144ca82..d39ebf97 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -346,6 +346,38 @@ class FliTimerCache { FliImpl *impl; }; +class FliIterator : public GpiIterator { +public: + enum OneToMany { + OTM_END = 0, + OTM_CONSTANTS, // include Generics + OTM_SIGNALS, + OTM_REGIONS, + OTM_SIGNAL_SUB_ELEMENTS, + OTM_VARIABLE_SUB_ELEMENTS + }; + + FliIterator(GpiImplInterface *impl, GpiObjHdl *hdl); + + virtual ~FliIterator() { }; + + Status next_handle(std::string &name, GpiObjHdl **hdl, void **raw_hdl); + +private: + void populate_handle_list(OneToMany childType); + +private: + static GpiIteratorMapping iterate_over; /* Possible mappings */ + std::vector *selected; /* Mapping currently in use */ + std::vector::iterator one2many; + + std::vector m_vars; + std::vector m_sigs; + std::vector m_regs; + std::vector *m_currentHandles; + std::vector::iterator m_iterator; +}; + class FliImpl : public GpiImplInterface { public: FliImpl(const std::string& name) : GpiImplInterface(name), @@ -376,13 +408,15 @@ class FliImpl : public GpiImplInterface { const char *reason_to_string(int reason); /* Method to provide strings from operation types */ - GpiObjHdl *create_gpi_obj(std::string &name, std::string &fq_name); + GpiObjHdl *create_gpi_obj_from_handle(void *hdl, std::string &name, std::string &fq_name); private: bool isValueConst(int kind); bool isValueLogic(mtiTypeIdT type); bool isValueChar(mtiTypeIdT type); bool isValueBoolean(mtiTypeIdT type); + bool isTypeValue(int type); + bool isTypeSignal(int type, int full_type); public: FliTimerCache cache; From 6aa2838ab792d89c9230eb19c36ba2ce961698cb Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 4 Jan 2016 16:54:15 -0700 Subject: [PATCH 1101/2656] Updates to address review comments --- lib/fli/FliCbHdl.cpp | 2 +- lib/fli/FliImpl.cpp | 6 ++- lib/fli/FliImpl.h | 33 +++++++++++---- lib/fli/FliObjHdl.cpp | 99 +++++++++++-------------------------------- 4 files changed, 54 insertions(+), 86 deletions(-) diff --git a/lib/fli/FliCbHdl.cpp b/lib/fli/FliCbHdl.cpp index 5a347131..5b965608 100644 --- a/lib/fli/FliCbHdl.cpp +++ b/lib/fli/FliCbHdl.cpp @@ -1,5 +1,5 @@ /****************************************************************************** -* Copyright (c) 2014 Potential Ventures Ltd +* Copyright (c) 2015/16 Potential Ventures Ltd * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index d031e79f..9909ad9d 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -142,7 +142,8 @@ bool FliImpl::isValueLogic(mtiTypeIdT type) bool FliImpl::isValueChar(mtiTypeIdT type) { - return (mti_TickLength(type) == 256); + const int NUM_ENUMS_IN_CHAR_TYPE = 256; + return (mti_TickLength(type) == NUM_ENUMS_IN_CHAR_TYPE); } bool FliImpl::isValueBoolean(mtiTypeIdT type) @@ -152,7 +153,7 @@ bool FliImpl::isValueBoolean(mtiTypeIdT type) std::string strFalse = enum_values[0]; std::string strTrue = enum_values[1]; - if (strFalse.compare("false") == 0 && strTrue.compare("true") == 0) { + if (strFalse.compare("FALSE") == 0 && strTrue.compare("TRUE") == 0) { return true; } } @@ -258,6 +259,7 @@ GpiObjHdl *FliImpl::create_gpi_obj_from_handle(void *hdl, std::string &name, std if (new_obj->initialise(name,fq_name) < 0) { LOG_ERROR("Failed to initialise the handle %s", name.c_str()); + delete new_obj; return NULL; } diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index d39ebf97..328e6cdd 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -179,14 +179,11 @@ class FliValueObjHdl : public FliSignalObjHdl { FliSignalObjHdl(impl, hdl, objtype, is_const, is_var), m_fli_type(typeKind), m_val_type(valType), - m_mti_buff(NULL), m_val_buff(NULL) { } virtual ~FliValueObjHdl() { if (m_val_buff) free(m_val_buff); - if (m_mti_buff) - free(m_mti_buff); } virtual const char* get_signal_value_binstr(void); @@ -203,7 +200,6 @@ class FliValueObjHdl : public FliSignalObjHdl { protected: mtiTypeKindT m_fli_type; mtiTypeIdT m_val_type; - mtiInt32T *m_mti_buff; char *m_val_buff; }; @@ -251,11 +247,15 @@ class FliLogicObjHdl : public FliValueObjHdl { valType, typeKind), m_ascending(false), + m_mti_buff(NULL), m_value_enum(NULL), m_num_enum(0), m_enum_map() { } - virtual ~FliLogicObjHdl() { } + virtual ~FliLogicObjHdl() { + if (m_mti_buff) + free(m_mti_buff); + } const char* get_signal_value_binstr(void); @@ -267,6 +267,7 @@ class FliLogicObjHdl : public FliValueObjHdl { private: bool m_ascending; + char *m_mti_buff; char **m_value_enum; // Do Not Free mtiInt32T m_num_enum; std::map m_enum_map; @@ -302,15 +303,22 @@ class FliRealObjHdl : public FliValueObjHdl { bool is_var, mtiTypeIdT valType, mtiTypeKindT typeKind) : - FliValueObjHdl(impl, hdl, objtype, is_const, is_var, valType, typeKind) { } + FliValueObjHdl(impl, hdl, objtype, is_const, is_var, valType, typeKind), + m_mti_buff(NULL) { } - virtual ~FliRealObjHdl() { } + virtual ~FliRealObjHdl() { + if (m_mti_buff) + free(m_mti_buff); + } double get_signal_value_real(void); int set_signal_value(const double value); int initialise(std::string &name, std::string &fq_name); + +private: + double *m_mti_buff; }; class FliStringObjHdl : public FliValueObjHdl { @@ -322,15 +330,22 @@ class FliStringObjHdl : public FliValueObjHdl { bool is_var, mtiTypeIdT valType, mtiTypeKindT typeKind) : - FliValueObjHdl(impl, hdl, objtype, is_const, is_var, valType, typeKind) { } + FliValueObjHdl(impl, hdl, objtype, is_const, is_var, valType, typeKind), + m_mti_buff(NULL) { } - virtual ~FliStringObjHdl() { } + virtual ~FliStringObjHdl() { + if (m_mti_buff) + free(m_mti_buff); + } const char* get_signal_value_str(void); int set_signal_value(std::string &value); int initialise(std::string &name, std::string &fq_name); + +private: + char *m_mti_buff; }; class FliTimerCache { diff --git a/lib/fli/FliObjHdl.cpp b/lib/fli/FliObjHdl.cpp index 00af9289..9691ef4e 100644 --- a/lib/fli/FliObjHdl.cpp +++ b/lib/fli/FliObjHdl.cpp @@ -1,5 +1,5 @@ /****************************************************************************** -* Copyright (c) 2014 Potential Ventures Ltd +* Copyright (c) 2015/16 Potential Ventures Ltd * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -140,11 +140,6 @@ long FliEnumObjHdl::get_signal_value_long(void) int FliEnumObjHdl::set_signal_value(const long value) { - if (m_const) { - LOG_ERROR("Attempted to set a constant signal/variable!\n"); - return -1; - } - if (value > m_num_enum || value < 0) { LOG_ERROR("Attempted to set a enum with range [0,%d] with invalid value %d!\n", m_num_enum, value); return -1; @@ -169,15 +164,13 @@ int FliLogicObjHdl::initialise(std::string &name, std::string &fq_name) break; case MTI_TYPE_ARRAY: { mtiTypeIdT elemType = mti_GetArrayElementType(m_val_type); - int buff_len = 0; m_ascending = (mti_TickDir(m_val_type) == 1); m_value_enum = mti_GetEnumValues(elemType); m_num_enum = mti_TickLength(elemType); m_num_elems = mti_TickLength(m_val_type); - buff_len = (m_num_elems + (sizeof(*m_mti_buff) - 1)) / sizeof(*m_mti_buff); - m_mti_buff = (mtiInt32T*)malloc(sizeof(*m_mti_buff) * buff_len); + m_mti_buff = (char*)malloc(sizeof(*m_mti_buff) * m_num_elems); if (!m_mti_buff) { LOG_CRITICAL("Unable to alloc mem for value object mti read buffer: ABORTING"); return -1; @@ -213,8 +206,6 @@ const char* FliLogicObjHdl::get_signal_value_binstr(void) } break; case MTI_TYPE_ARRAY: { - char *iter = (char*)m_mti_buff; - if (m_is_var) { mti_GetArrayVarValue(get_handle(), m_mti_buff); } else { @@ -222,7 +213,7 @@ const char* FliLogicObjHdl::get_signal_value_binstr(void) } for (int i = 0; i < m_num_elems; i++ ) { - m_val_buff[i] = m_value_enum[(int)iter[i]][1]; + m_val_buff[i] = m_value_enum[(int)m_mti_buff[i]][1]; } } break; @@ -238,11 +229,6 @@ const char* FliLogicObjHdl::get_signal_value_binstr(void) int FliLogicObjHdl::set_signal_value(const long value) { - if (m_const) { - LOG_ERROR("Attempted to set a constant signal/variable!\n"); - return -1; - } - if (m_num_elems == 1) { mtiInt32T enumVal = value ? m_enum_map['1'] : m_enum_map['0']; @@ -253,7 +239,6 @@ int FliLogicObjHdl::set_signal_value(const long value) } } else { int valLen = sizeof(value) * 8; - char *iter = (char *)m_mti_buff; mtiInt32T enumVal; int numPad; int idx; @@ -265,34 +250,34 @@ int FliLogicObjHdl::set_signal_value(const long value) // Pad MSB for descending vector for (idx = 0; idx < numPad && !m_ascending; idx++) { - iter[idx] = (char)m_enum_map['0']; + m_mti_buff[idx] = (char)m_enum_map['0']; } if (m_ascending) { for (int i = 0; i < valLen; i++) { enumVal = value&(1L<(), (long)iter); + mti_SetVarValue(get_handle(), (long)m_mti_buff); } else { - mti_SetSignalValue(get_handle(), (long)iter); + mti_SetSignalValue(get_handle(), (long)m_mti_buff); } } @@ -301,11 +286,6 @@ int FliLogicObjHdl::set_signal_value(const long value) int FliLogicObjHdl::set_signal_value(std::string &value) { - if (m_const) { - LOG_ERROR("Attempted to set a constant signal/variable!\n"); - return -1; - } - if (m_num_elems == 1) { mtiInt32T enumVal = m_enum_map[value.c_str()[0]]; @@ -327,30 +307,29 @@ int FliLogicObjHdl::set_signal_value(std::string &value) len = m_num_elems; } - char *iter = (char *)m_mti_buff; mtiInt32T enumVal; std::string::iterator valIter; int i = 0; // Pad MSB for descending vector for (i = 0; i < numPad && !m_ascending; i++) { - iter[i] = (char)m_enum_map['0']; + m_mti_buff[i] = (char)m_enum_map['0']; } for (valIter = value.begin(); (valIter != value.end()) && (i < m_num_elems); valIter++, i++) { enumVal = m_enum_map[*valIter]; - iter[i] = (char)enumVal; + m_mti_buff[i] = (char)enumVal; } // Fill bits a the end of the value to 0's for (i = len; i < m_num_elems && m_ascending; i++) { - iter[i] = (char)m_enum_map['0']; + m_mti_buff[i] = (char)m_enum_map['0']; } if (m_is_var) { - mti_SetVarValue(get_handle(), (long)iter); + mti_SetVarValue(get_handle(), (long)m_mti_buff); } else { - mti_SetSignalValue(get_handle(), (long)iter); + mti_SetSignalValue(get_handle(), (long)m_mti_buff); } } @@ -403,11 +382,6 @@ long FliIntObjHdl::get_signal_value_long(void) int FliIntObjHdl::set_signal_value(const long value) { - if (m_const) { - LOG_ERROR("Attempted to set a constant signal/variable!\n"); - return -1; - } - if (m_is_var) { mti_SetVarValue(get_handle(), value); } else { @@ -419,12 +393,10 @@ int FliIntObjHdl::set_signal_value(const long value) int FliRealObjHdl::initialise(std::string &name, std::string &fq_name) { - int buff_len = 0; m_num_elems = 1; - buff_len = (sizeof(double) + (sizeof(*m_mti_buff) - 1)) / sizeof(*m_mti_buff); - m_mti_buff = (mtiInt32T*)malloc(sizeof(*m_mti_buff) * buff_len); + m_mti_buff = (double*)malloc(sizeof(double)); if (!m_mti_buff) { LOG_CRITICAL("Unable to alloc mem for value object mti read buffer: ABORTING"); return -1; @@ -435,34 +407,25 @@ int FliRealObjHdl::initialise(std::string &name, std::string &fq_name) double FliRealObjHdl::get_signal_value_real(void) { - double *iter = (double *)m_mti_buff; - if (m_is_var) { mti_GetVarValueIndirect(get_handle(), m_mti_buff); } else { mti_GetSignalValueIndirect(get_handle(), m_mti_buff); } - LOG_DEBUG("Retrieved \"%f\" for value object %s", iter[0], m_name.c_str()); + LOG_DEBUG("Retrieved \"%f\" for value object %s", m_mti_buff[0], m_name.c_str()); - return iter[0]; + return m_mti_buff[0]; } int FliRealObjHdl::set_signal_value(const double value) { - double *iter = (double *)m_mti_buff; - - if (m_const) { - LOG_ERROR("Attempted to set a constant signal/variable!\n"); - return -1; - } - - iter[0] = value; + m_mti_buff[0] = value; if (m_is_var) { - mti_SetVarValue(get_handle(), (long)iter); + mti_SetVarValue(get_handle(), (long)m_mti_buff); } else { - mti_SetSignalValue(get_handle(), (long)iter); + mti_SetSignalValue(get_handle(), (long)m_mti_buff); } return 0; @@ -470,12 +433,9 @@ int FliRealObjHdl::set_signal_value(const double value) int FliStringObjHdl::initialise(std::string &name, std::string &fq_name) { - int buff_len = 0; - m_num_elems = mti_TickLength(m_val_type); - buff_len = (m_num_elems + (sizeof(*m_mti_buff) - 1)) / sizeof(*m_mti_buff); - m_mti_buff = (mtiInt32T*)malloc(sizeof(*m_mti_buff) * buff_len); + m_mti_buff = (char*)malloc(sizeof(char) * m_num_elems); if (!m_mti_buff) { LOG_CRITICAL("Unable to alloc mem for value object mti read buffer: ABORTING"); return -1; @@ -493,15 +453,13 @@ int FliStringObjHdl::initialise(std::string &name, std::string &fq_name) const char* FliStringObjHdl::get_signal_value_str(void) { - char *iter = (char *)m_mti_buff; - if (m_is_var) { mti_GetArrayVarValue(get_handle(), m_mti_buff); } else { mti_GetArraySignalValue(get_handle(), m_mti_buff); } - strncpy(m_val_buff, iter, m_num_elems); + strncpy(m_val_buff, m_mti_buff, m_num_elems); LOG_DEBUG("Retrieved \"%s\" for value object %s", m_val_buff, m_name.c_str()); @@ -510,19 +468,12 @@ const char* FliStringObjHdl::get_signal_value_str(void) int FliStringObjHdl::set_signal_value(std::string &value) { - char *iter = (char *)m_mti_buff; - - if (m_const) { - LOG_ERROR("Attempted to set a constant signal/variable!\n"); - return -1; - } - - strncpy(iter, value.c_str(), m_num_elems); + strncpy(m_mti_buff, value.c_str(), m_num_elems); if (m_is_var) { - mti_SetVarValue(get_handle(), (long)iter); + mti_SetVarValue(get_handle(), (long)m_mti_buff); } else { - mti_SetSignalValue(get_handle(), (long)iter); + mti_SetSignalValue(get_handle(), (long)m_mti_buff); } return 0; From c9b01140829245af13e513b219374774e80c44ab Mon Sep 17 00:00:00 2001 From: "J.Morarity" Date: Wed, 6 Jan 2016 10:34:58 -0800 Subject: [PATCH 1102/2656] add support for modelsim on 64bit windows. also make sleep function compatible w/ all versions of windows (this was an apparent undetected regression that broke windows support). --- lib/embed/gpi_embed.c | 4 ++++ makefiles/Makefile.inc | 3 +++ makefiles/simulators/Makefile.modelsim | 4 ++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 9a763a91..59847856 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -34,6 +34,10 @@ #include "embed.h" #include "../compat/python3_compat.h" +#if defined(_WIN32) +#include +#define sleep(n) Sleep(1000 * n) +#endif static PyThreadState *gtstate = NULL; #if PY_MAJOR_VERSION >= 3 diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 62b7e942..19942fd1 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -72,6 +72,9 @@ GXX_ARGS += -g -DDEBUG -fpic GCC_ARGS := $(GXX_ARGS) else ifeq ($(OS),Msys) GXX_ARGS += -g -DDEBUG -shared +ifeq ($(ARCH),x86_64) +GXX_ARGS += -DMS_WIN64 +endif GCC_ARGS := $(GXX_ARGS) LIB_EXT := dll PY_EXT := pyd diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index 92a7bec7..36fb4d04 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -27,8 +27,8 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -# Modelsim is 32-bit only -ARCH:=i686 +# Modelsim is (no longer) 32-bit only +#ARCH:=i686 # Everything else is identical to Quest include $(SIM_ROOT)/makefiles/simulators/Makefile.questa From 16a22edd4d05edff385fefadbaed89fc74b6b522 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Wed, 6 Jan 2016 13:43:47 -0700 Subject: [PATCH 1103/2656] Moves __len__() function in handle.py to the Base class. Fixes issue #371 --- cocotb/handle.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index c9fd5d2a..0d338c4d 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -65,7 +65,7 @@ class SimHandleBase(object): # For backwards compatibility we support a mapping of old member names # which may alias with the simulator hierarchy. In these cases the - # simulator takes priority, only falling back + # simulator takes priority, only falling back _compat_mapping = { "log" : "_log", "fullname" : "_fullname", @@ -92,6 +92,14 @@ def __init__(self, handle): def __hash__(self): return self._handle + def __len__(self): + """Returns the 'length' of the underlying object. + + For vectors this is the number of bits. + """ + if self._len is None: + self._len = simulator.get_num_elems(self._handle) + return self._len def __eq__(self, other): @@ -381,17 +389,6 @@ def _getvalue(self): def _get_value_str(self): return simulator.get_signal_val_binstr(self._handle) - def __len__(self): - """Returns the 'length' of the underlying object. - - For vectors this is the number of bits. - - TODO: Handle other types (loops, generate etc) - """ - if self._len is None: - self._len = simulator.get_num_elems(self._handle) - return self._len - def __eq__(self, other): if isinstance(other, SimHandleBase): if self._handle == other._handle: return 0 From d501a8f7b59937c86febacd99e61c508380687af Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sat, 9 Jan 2016 16:05:46 +0000 Subject: [PATCH 1104/2656] Issue #377: Remove const as functions return char* and ubuntu is unhappy --- lib/fli/FliImpl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 9909ad9d..b0dc703f 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -657,7 +657,7 @@ GpiIterator::Status FliIterator::next_handle(std::string &name, GpiObjHdl **hdl, return GpiIterator::END; } - const char *c_name; + char *c_name; switch (*one2many) { case FliIterator::OTM_CONSTANTS: case FliIterator::OTM_VARIABLE_SUB_ELEMENTS: @@ -690,7 +690,7 @@ GpiIterator::Status FliIterator::next_handle(std::string &name, GpiObjHdl **hdl, name = c_name; if (*one2many == FliIterator::OTM_SIGNAL_SUB_ELEMENTS) { - mti_VsimFree((void *)c_name); + mti_VsimFree(c_name); } std::string fq_name = m_parent->get_fullname(); From 2409ffbd889dc7587198a8bf72635e6eba5fc59e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Sat, 9 Jan 2016 16:25:59 +0000 Subject: [PATCH 1105/2656] Issue #377 and #375: Check individual new fields for presence --- lib/fli/FliImpl.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index b0dc703f..c48d7aa3 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -514,11 +514,19 @@ void fli_mappings(GpiIteratorMapping &map) map.add_to_options(accinlinedinnerBlock, ®ion_options[0]); map.add_to_options(accGenerate, ®ion_options[0]); map.add_to_options(accIfGenerate, ®ion_options[0]); +#ifdef accElsifGenerate map.add_to_options(accElsifGenerate, ®ion_options[0]); +#endif +#ifdef accElseGenerate map.add_to_options(accElseGenerate, ®ion_options[0]); - map.add_to_options(accForGenerate, ®ion_options[0]); +#endif +#ifdef accCaseGenerate map.add_to_options(accCaseGenerate, ®ion_options[0]); +#endif +#ifdef accCaseOTHERSGenerate map.add_to_options(accCaseOTHERSGenerate, ®ion_options[0]); +#endif + map.add_to_options(accForGenerate, ®ion_options[0]); map.add_to_options(accConfiguration, ®ion_options[0]); FliIterator::OneToMany signal_options[] = { From e553550a6c0b8fd609a751600203a0d9e71ea5eb Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 11 Jan 2016 09:53:45 -0700 Subject: [PATCH 1106/2656] Makefile clean-up. No longer need for user to specify GPI_IMPL or GPI_EXTRA. Addresses issues #232 and #372 --- documentation/source/quickstart.rst | 4 +- examples/adder/tests/Makefile | 19 +++++--- examples/endian_swapper/tests/Makefile | 10 ++-- examples/mean/tests/Makefile | 20 +++++--- examples/mixed_language/tests/Makefile | 22 ++------- examples/ping_tun_tap/tests/Makefile | 11 +++++ lib/Makefile | 7 --- makefiles/Makefile.inc | 1 - makefiles/simulators/Makefile.aldec | 23 +++++++--- makefiles/simulators/Makefile.cvc | 19 ++++++-- makefiles/simulators/Makefile.ghdl | 11 ++++- makefiles/simulators/Makefile.icarus | 14 +++++- makefiles/simulators/Makefile.ius | 17 +++++-- makefiles/simulators/Makefile.nvc | 11 ++++- makefiles/simulators/Makefile.questa | 19 ++++++-- makefiles/simulators/Makefile.vcs | 11 ++++- tests/designs/avalon_module/Makefile | 15 ++++-- tests/designs/close_module/Makefile | 10 ++++ tests/designs/plusargs_module/Makefile | 10 ++++ tests/designs/sample_module/Makefile | 13 ++---- tests/designs/uart2bus/Makefile | 46 ++++++------------- tests/designs/vhdl_configurations/Makefile | 24 ++++------ tests/designs/viterbi_decoder_axi4s/Makefile | 30 ++++-------- tests/test_cases/test_configuration/Makefile | 4 -- .../test_iteration_mixedlang/Makefile | 3 -- .../test_iteration_verilog/Makefile | 14 +++++- tests/test_cases/test_iteration_vhdl/Makefile | 4 -- tests/test_cases/test_verilog_access/Makefile | 3 -- tests/test_cases/test_vhdl_access/Makefile | 3 -- 29 files changed, 228 insertions(+), 170 deletions(-) diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index 1e5ee087..fe16925f 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -127,11 +127,11 @@ To run a test using a different simulator: Running a VHDL example ---------------------- -The endian swapper example includes both a VHDL and Verilog RTL implementation. The Cocotb testbench can execute against either implementation using VPI for Verilog and VHPI for VHDL. To run the test suite against the VHDL implementation use the following command (a VHPI capable simulator must be used): +The endian swapper example includes both a VHDL and Verilog RTL implementation. The Cocotb testbench can execute against either implementation using VPI for Verilog and VHPI/FLI for VHDL. To run the test suite against the VHDL implementation use the following command (a VHPI or FLI capable simulator must be used): .. code-block:: bash - $> make SIM=aldec GPI_IMPL=vhpi + $> make SIM=aldec TOPLEVEL_LANG=vhdl diff --git a/examples/adder/tests/Makefile b/examples/adder/tests/Makefile index 64abcb52..9c5d7d96 100644 --- a/examples/adder/tests/Makefile +++ b/examples/adder/tests/Makefile @@ -27,10 +27,16 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### - -TOPLEVEL := adder TOPLEVEL_LANG ?= verilog +ifneq ($(TOPLEVEL_LANG),verilog) + +all: + @echo "Skipping example due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being verilog" +clean:: + +else + PWD=$(shell pwd) COCOTB=$(PWD)/../../.. @@ -41,14 +47,13 @@ else WPWD=$(shell pwd) PYTHONPATH := $(WPWD)/../model:$(PYTHONPATH) endif -export PYTHONPATH VERILOG_SOURCES = $(WPWD)/../hdl/adder.v -GPI_IMPL := vpi - -export TOPLEVEL_LANG -MODULE ?= test_adder +TOPLEVEL := adder +MODULE := test_adder include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim + +endif diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index 08a206e4..890e7588 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -41,18 +41,15 @@ else WPWD=$(shell pwd) PYTHONPATH := $(PWD)/../cosim:$(PYTHONPATH) endif -export PYTHONPATH ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES = $(WPWD)/../hdl/endian_swapper.sv TOPLEVEL = endian_swapper_sv - GPI_IMPL=vpi -endif - -ifeq ($(TOPLEVEL_LANG),vhdl) +else ifeq ($(TOPLEVEL_LANG),vhdl) VHDL_SOURCES = $(WPWD)/../hdl/endian_swapper.vhdl TOPLEVEL = endian_swapper_vhdl - GPI_IMPL=vhpi +else + $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") endif MODULE=test_endian_swapper @@ -60,7 +57,6 @@ MODULE=test_endian_swapper CUSTOM_COMPILE_DEPS = hal LD_LIBRARY_PATH := $(PWD)/../cosim:$(LD_LIBRARY_PATH) -export LD_LIBRARY_PATH include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim diff --git a/examples/mean/tests/Makefile b/examples/mean/tests/Makefile index 4d0f1be7..f72fe18f 100644 --- a/examples/mean/tests/Makefile +++ b/examples/mean/tests/Makefile @@ -1,7 +1,16 @@ -TOPLEVEL := mean_sv TOPLEVEL_LANG ?= verilog +ifneq ($(TOPLEVEL_LANG),verilog) + +all: + @echo "Skipping example due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being verilog" +clean:: + +else + +TOPLEVEL := mean_sv + PWD=$(shell pwd) COCOTB=$(PWD)/../../.. @@ -15,17 +24,14 @@ VHDL_SOURCES = $(WPWD)/../hdl/mean_pkg.vhd VHDL_SOURCES += $(WPWD)/../hdl/mean.vhd VCOM_ARGS = -mixedsvvh -export VCOM_ARGS GENERICS = "BUS_WIDTH=2" -export GENERICS VERILOG_SOURCES = $(WPWD)/../hdl/mean_sv.sv -GPI_IMPL := vpi - -export TOPLEVEL_LANG -MODULE ?= test_mean +MODULE := test_mean include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim + +endif diff --git a/examples/mixed_language/tests/Makefile b/examples/mixed_language/tests/Makefile index e5747da3..b4261ac2 100644 --- a/examples/mixed_language/tests/Makefile +++ b/examples/mixed_language/tests/Makefile @@ -1,5 +1,5 @@ # Override this variable to use a VHDL toplevel instead of SystemVerilog -TOPLEVEL_LANG ?= sv +TOPLEVEL_LANG ?= verilog # At the moment the only simulator supporting mixed language VHPI/VPI is Aldec SIM?=aldec @@ -18,25 +18,13 @@ endif VERILOG_SOURCES = $(WPWD)/../../endian_swapper/hdl/endian_swapper.sv VHDL_SOURCES = $(WPWD)/../../endian_swapper/hdl/endian_swapper.vhdl -ifeq ($(TOPLEVEL_LANG),sv) +ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES += $(WPWD)/../hdl/toplevel.sv - GPI_IMPL=vpi - GPI_EXTRA=vhpi -ifeq ($(SIM),modelsim) - GPI_EXTRA=fli -endif -endif - -ifeq ($(TOPLEVEL_LANG),vhdl) +else ifeq ($(TOPLEVEL_LANG),vhdl) $(error "VHDL toplevel not yet implemented") VHDL_SOURCES += $(WPWD)/../hdl/toplevel.vhdl -ifeq ($(SIM),aldec) - GPI_IMPL=vhpi -endif -ifeq ($(SIM),modelsim) - GPI_IMPL=fli -endif - GPI_EXTRA=vpi +else + $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") endif diff --git a/examples/ping_tun_tap/tests/Makefile b/examples/ping_tun_tap/tests/Makefile index f963eeb1..c9425e07 100644 --- a/examples/ping_tun_tap/tests/Makefile +++ b/examples/ping_tun_tap/tests/Makefile @@ -26,6 +26,15 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +TOPLEVEL_LANG ?= verilog + +ifneq ($(TOPLEVEL_LANG),verilog) + +all: + @echo "Skipping example due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being verilog" +clean:: + +else TOPLEVEL = icmp_reply @@ -43,3 +52,5 @@ MODULE=test_icmp_reply include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim + +endif diff --git a/lib/Makefile b/lib/Makefile index d04b7bbb..3af0436c 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -29,13 +29,6 @@ include $(SIM_ROOT)/makefiles/Makefile.inc -# Default to VPI layer for now, override this for VHPI -GPI_IMPL ?= vpi - -ifndef GPI_IMPL - $(error "Unable to determine appropriate GPI layer to use") -endif - # FIXME it's a bit nasty to have the source files listed here as dependencies # but better than re-building the libraries every time. diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 19942fd1..c382de9c 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -57,7 +57,6 @@ include $(SIM_ROOT)/makefiles/Makefile.pylib export LIB_DIR=$(BUILD_DIR)/libs/$(ARCH) export LIB_OBJ_DIR:= $(BUILD_DIR)/obj/$(ARCH) export INCLUDES := -I$(SIM_ROOT)/include -I$(PYTHON_INCLUDEDIR) -export GPI_EXTRA LIB_EXT := so PY_EXT := so diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 4ae1c89c..a0e8cfb9 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -44,20 +44,29 @@ ifeq ($(COVERAGE),1) ALOG_ARGS += -coverage sb endif -ifeq ($(GPI_IMPL),vpi) +GPI_EXTRA:= +ifeq ($(TOPLEVEL_LANG),verilog) + GPI_ARGS = -pli libvpi +ifneq ($(VHDL_SOURCES),) + GPI_EXTRA = vhpi endif -ifeq ($(GPI_IMPL),vhpi) + +else ifeq ($(TOPLEVEL_LANG),vhdl) + ifeq ($(OS),Msys) VHPI_LIB = $(shell sh -c 'cd $(LIB_DIR) && pwd -W')/libvhpi else VHPI_LIB = $(LIB_DIR)/libvhpi endif + GPI_ARGS = -loadvhpi $(VHPI_LIB):vhpi_startup_routines_bootstrap +ifneq ($(VERILOG_SOURCES),) + GPI_EXTRA = vpi endif -ifndef GPI_ARGS - $(error "Unable to determine appropriate GPI layer to use as main entry point") +else + $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") endif @@ -65,10 +74,10 @@ endif $(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(VHDL_SOURCES) $(SIM_BUILD) echo "alib $(RTL_LIBRARY)" > $@ echo "set worklib $(RTL_LIBRARY)" >> $@ -ifdef VHDL_SOURCES +ifneq ($(VHDL_SOURCES),) echo "acom $(ACOM_ARGS) $(VHDL_SOURCES)" >> $@ endif -ifdef VERILOG_SOURCES +ifneq ($(VERILOG_SOURCES),) echo "alog $(ALOG_ARGS) $(VERILOG_SOURCES)" >> $@ endif ifdef SCRIPT_FILE @@ -121,7 +130,7 @@ endif # Note it's the redirection of the output rather than the 'do' command # that turns on batch mode (i.e. exit on completion/error) results.xml: $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(COCOTB_VPI_LIB) $(CUSTOM_SIM_DEPS) - cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) \ + cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do runsim.tcl | tee sim.log clean:: diff --git a/makefiles/simulators/Makefile.cvc b/makefiles/simulators/Makefile.cvc index d185e731..6e5ffc7d 100644 --- a/makefiles/simulators/Makefile.cvc +++ b/makefiles/simulators/Makefile.cvc @@ -25,6 +25,15 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +ifneq ($(VHDL_SOURCES),) + +results.xml: + @echo "Skipping simulation as VHDL is not supported on simulator=$(SIM)" +debug: results.xml +clean:: + +else + #only interpreted mode works for the moment CVC_ITERP ?= 1 @@ -35,7 +44,7 @@ endif # Compilation phase $(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ cvc64 $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=libvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) # Execution phase @@ -44,7 +53,7 @@ ifeq ($(CVC_ITERP),1) else results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) endif @@ -52,16 +61,16 @@ endif ifeq ($(CVC_ITERP),1) debug: $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ gdb --args cvc64 $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=libvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) else debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ gdb --args $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) endif clean:: -@rm -rf $(SIM_BUILD) - +endif diff --git a/makefiles/simulators/Makefile.ghdl b/makefiles/simulators/Makefile.ghdl index 3ae3714e..ac1ec652 100644 --- a/makefiles/simulators/Makefile.ghdl +++ b/makefiles/simulators/Makefile.ghdl @@ -26,6 +26,14 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +ifneq ($(VERILOG_SOURCES),) + +results.xml: + @echo "Skipping simulation as Verilog is not supported on simulator=$(SIM)" +clean:: + +else + .PHONY: analyse GHDL=ghdl @@ -36,8 +44,9 @@ analyse: $(VHDL_SOURCES) $(SIM_BUILD) results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VPI_LIB) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(GHDL) -r --workdir=$(SIM_BUILD) $(TOPLEVEL) --vpi=$(LIB_DIR)/libvpi.$(LIB_EXT) clean:: -@rm -rf $(SIM_BUILD) +endif diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index afaea855..f4cdf25a 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -27,6 +27,15 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +ifneq ($(VHDL_SOURCES),) + +results.xml: + @echo "Skipping simulation as VHDL is not supported on simulator=$(SIM)" +debug: results.xml +clean:: + +else + BUILD_VPI=1 # Compilation phase @@ -58,13 +67,14 @@ endif results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ gdb --args vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) clean:: -@rm -rf $(SIM_BUILD) +endif diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 897cf0ab..8428a083 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -45,19 +45,27 @@ endif # Which does not play nice with out multi language. So we supply vhpi # GPI_EXTRA if needed. -ifeq ($(GPI_IMPL),vpi) +GPI_EXTRA:= +ifeq ($(TOPLEVEL_LANG),verilog) GPI_ARGS = -loadvpi $(LIB_DIR)/libvpi EXTRA_ARGS += -sv -v93 - HDL_SOURCES = $(VERILOG_SOURCES) $(VHDL_SOURCES) + HDL_SOURCES = $(VERILOG_SOURCES) ROOT_LEVEL = $(TOPLEVEL) +ifneq ($(VHDL_SOURCES),) + HDL_SOURCES += $(VHDL_SOURCES) + GPI_EXTRA = vhpi endif - -ifeq ($(GPI_IMPL),vhpi) +else ifeq ($(TOPLEVEL_LANG),vhdl) GPI_EXTRA = vhpi EXTRA_ARGS += -v93 EXTRA_ARGS += -top $(TOPLEVEL) MAKE_LIB = -makelib $(TOPLEVEL) HDL_SOURCES = $(VHDL_SOURCES) +ifneq ($(VHDL_SOURCES),) + HDL_SOURCES += $(VERILOG_SOURCES) +endif +else + $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") endif GPI_LIB = $(COCOTB_VPI_LIB) $(COCOTB_VHPI_LIB) @@ -65,6 +73,7 @@ GPI_LIB = $(COCOTB_VPI_LIB) $(COCOTB_VHPI_LIB) results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(GPI_LIB) LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ irun $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) clean:: diff --git a/makefiles/simulators/Makefile.nvc b/makefiles/simulators/Makefile.nvc index 8473c8c5..9b0ec5fd 100644 --- a/makefiles/simulators/Makefile.nvc +++ b/makefiles/simulators/Makefile.nvc @@ -1,5 +1,13 @@ # -*- mode: makefile-gmake -*- +ifneq ($(VERILOG_SOURCES),) + +results.xml: + @echo "Skipping simulation as Verilog is not supported on simulator=$(SIM)" +clean:: + +else + RTL_LIBRARY ?= work .PHONY: analyse @@ -13,9 +21,10 @@ results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) nvc --work=$(RTL_LIBRARY) -e $(TOPLEVEL) cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ LD_LIBRARY_PATH=$(LIB_DIR) \ nvc --work=$(RTL_LIBRARY) -r --load $(COCOTB_VHPI_LIB) $(TOPLEVEL) clean:: -@rm -rf $(SIM_BUILD) +endif diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 5c477250..fea16134 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -45,10 +45,21 @@ SIM_CMD = vsim -c VSIM_ARGS += -onfinish exit endif -ifeq ($(GPI_IMPL),vhpi) -VSIM_ARGS += -foreign \"cocotb_init libfli.so\" +GPI_EXTRA:= +ifeq ($(TOPLEVEL_LANG),vhdl) + VSIM_ARGS += -foreign \"cocotb_init libfli.so\" +ifneq ($(VERILOG_SOURCES),) + GPI_EXTRA = vpi +endif + +else ifeq ($(TOPLEVEL_LANG),verilog) + VSIM_ARGS += -pli libvpi.$(LIB_EXT) +ifneq ($(VHDL_SOURCES),) + GPI_EXTRA = fli +endif + else -VSIM_ARGS += -pli libvpi.$(LIB_EXT) + $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") endif ifneq ($(GENERICS),) @@ -110,7 +121,7 @@ endif # Append or remove values to INT_LIBS depenending on your license results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) \ + GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) \ $(SIM_CMD) -do runsim.do 2>&1 | tee sim.log clean:: diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index 857337ce..a6e77e38 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -27,6 +27,14 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +ifneq ($(VHDL_SOURCES),) + +results.xml: + @echo "Skipping simulation as VHDL is not supported on simulator=$(SIM)" +clean:: + +else + ifeq ($(ARCH),x86_64) EXTRA_ARGS += -full64 endif @@ -48,6 +56,7 @@ $(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(COCOTB # Execution phase results.xml: $(SIM_BUILD)/simv $(PYTHON_FILES) $(CUSTOM_SIM_DEPS) -PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(SIM_BUILD)/simv +define+COCOTB_SIM=1 $(SIM_ARGS) $(EXTRA_ARGS) @@ -58,4 +67,4 @@ clean:: -@rm -rf cm.log -@rm -rf results.xml -@rm -rf ucli.key - +endif diff --git a/tests/designs/avalon_module/Makefile b/tests/designs/avalon_module/Makefile index f86bd4e1..16ec3103 100644 --- a/tests/designs/avalon_module/Makefile +++ b/tests/designs/avalon_module/Makefile @@ -27,9 +27,17 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +TOPLEVEL_LANG ?= verilog + +ifneq ($(TOPLEVEL_LANG),verilog) + +all: + @echo "Skipping test due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being verilog" +clean:: + +else TOPLEVEL := burst_read_master -TOPLEVEL_LANG ?= verilog PWD=$(shell pwd) COCOTB=$(PWD)/../../.. @@ -41,9 +49,8 @@ WPWD=$(shell pwd) endif VERILOG_SOURCES = $(COCOTB)/tests/designs/avalon_module/burst_read_master.v -GPI_IMPL := vpi - -export TOPLEVEL_LANG include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim + +endif diff --git a/tests/designs/close_module/Makefile b/tests/designs/close_module/Makefile index 2f8d12ac..cb40a95f 100644 --- a/tests/designs/close_module/Makefile +++ b/tests/designs/close_module/Makefile @@ -27,6 +27,15 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +TOPLEVEL_LANG ?= verilog + +ifneq ($(TOPLEVEL_LANG),verilog) + +all: + @echo "Skipping test due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being verilog" +clean:: + +else TOPLEVEL = close_module @@ -48,3 +57,4 @@ all: @echo "Skipping test as system call overrides seqfault VCS" endif +endif diff --git a/tests/designs/plusargs_module/Makefile b/tests/designs/plusargs_module/Makefile index 23142062..44cbb101 100644 --- a/tests/designs/plusargs_module/Makefile +++ b/tests/designs/plusargs_module/Makefile @@ -27,6 +27,15 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +TOPLEVEL_LANG ?= verilog + +ifneq ($(TOPLEVEL_LANG),verilog) + +all: + @echo "Skipping test due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being verilog" +clean:: + +else TOPLEVEL = tb_top @@ -44,3 +53,4 @@ VERILOG_SOURCES = $(COCOTB)/tests/designs/plusargs_module/tb_top.v include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim +endif diff --git a/tests/designs/sample_module/Makefile b/tests/designs/sample_module/Makefile index c1eca3e9..3eee833a 100644 --- a/tests/designs/sample_module/Makefile +++ b/tests/designs/sample_module/Makefile @@ -27,9 +27,9 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +TOPLEVEL_LANG ?= verilog TOPLEVEL := sample_module -TOPLEVEL_LANG ?= verilog PWD=$(shell pwd) COCOTB?=$(PWD)/../../.. @@ -40,16 +40,13 @@ else WPWD=$(shell pwd) endif -ifeq ($(TOPLEVEL_LANG),vhdl) +ifeq ($(TOPLEVEL_LANG),verilog) + VERILOG_SOURCES = $(COCOTB)/tests/designs/sample_module/sample_module.v +else ifeq ($(TOPLEVEL_LANG),vhdl) VHDL_SOURCES = $(COCOTB)/tests/designs/sample_module/sample_module_1.vhdl $(COCOTB)/tests/designs/sample_module/sample_module.vhdl - TOPLEVEL := $(TOPLEVEL) - GPI_IMPL := vhpi else - VERILOG_SOURCES = $(COCOTB)/tests/designs/sample_module/sample_module.v - GPI_IMPL := vpi + $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") endif -export TOPLEVEL_LANG - include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim diff --git a/tests/designs/uart2bus/Makefile b/tests/designs/uart2bus/Makefile index 5c9ef5f6..77c9cc95 100644 --- a/tests/designs/uart2bus/Makefile +++ b/tests/designs/uart2bus/Makefile @@ -1,7 +1,16 @@ -SRC_BASE = $(COCOTB)/tests/designs/uart2bus - TOPLEVEL_LANG ?= verilog +ifneq ($(TOPLEVEL_LANG),verilog) + +all: + @echo "Skipping test due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being verilog" +clean:: + +else + + +SRC_BASE = $(COCOTB)/tests/designs/uart2bus + VHDL_SOURCES = $(SRC_BASE)/vhdl/uart2BusTop_pkg.vhd \ $(SRC_BASE)/vhdl/baudGen.vhd \ $(SRC_BASE)/vhdl/uartParser.vhd \ @@ -17,35 +26,10 @@ VERILOG_SOURCES = $(SRC_BASE)/verilog/baud_gen.v \ $(SRC_BASE)/verilog/uart_top.v \ $(SRC_BASE)/verilog/uart2bus_top.v -GPI_EXTRA:= -ifeq ($(TOPLEVEL_LANG),verilog) - VERILOG_SOURCES += $(SRC_BASE)/top/verilog_toplevel.v - TOPLEVEL = verilog_toplevel - GPI_IMPL=vpi +VERILOG_SOURCES += $(SRC_BASE)/top/verilog_toplevel.v +TOPLEVEL = verilog_toplevel -ifeq ($(SIM),aldec) - GPI_EXTRA=vhpi -endif -ifeq ($(SIM),modelsim) - GPI_EXTRA=vhpi -endif -ifeq ($(SIM),questa) - GPI_EXTRA=vhpi -endif -ifeq ($(SIM),ius) - GPI_EXTRA=vhpi -endif +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim -else -$(error "Only verilog toplevel supported at the moment") endif - -ifneq ($(GPI_EXTRA),) - include $(COCOTB)/makefiles/Makefile.inc - include $(COCOTB)/makefiles/Makefile.sim -else -all: - @echo "Skipping test as VHDL not supported on simulator=$(SIM)" -clean:: -endif - diff --git a/tests/designs/vhdl_configurations/Makefile b/tests/designs/vhdl_configurations/Makefile index d8e689de..91fbdd39 100644 --- a/tests/designs/vhdl_configurations/Makefile +++ b/tests/designs/vhdl_configurations/Makefile @@ -1,23 +1,17 @@ +TOPLEVEL_LANG ?= verilog + TOPLEVEL = testbench VHDL_SOURCES = $(COCOTB)/tests/designs/vhdl_configurations/dut.vhd -ifeq ($(TOPLEVEL_LANG),vhdl) - VHDL_SOURCES += $(COCOTB)/tests/designs/vhdl_configurations/testbench.vhd - GPI_IMPL := vhpi -endif -VHDL_SOURCES += $(COCOTB)/tests/designs/vhdl_configurations/configurations.vhd - ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES = $(COCOTB)/tests/designs/vhdl_configurations/testbench.sv - GPI_IMPL := vpi -endif - -ifneq ($(SIM),) - include $(COCOTB)/makefiles/Makefile.inc - include $(COCOTB)/makefiles/Makefile.sim +else ifeq ($(TOPLEVEL_LANG),vhdl) + VHDL_SOURCES += $(COCOTB)/tests/designs/vhdl_configurations/testbench.vhd else -all: - @echo "Skipping test as VHDL not supported on Icarus" -clean:: + $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") endif +VHDL_SOURCES += $(COCOTB)/tests/designs/vhdl_configurations/configurations.vhd + +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim diff --git a/tests/designs/viterbi_decoder_axi4s/Makefile b/tests/designs/viterbi_decoder_axi4s/Makefile index 1137e6af..3bc078b0 100644 --- a/tests/designs/viterbi_decoder_axi4s/Makefile +++ b/tests/designs/viterbi_decoder_axi4s/Makefile @@ -25,7 +25,15 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -TOPLEVEL_LANG = vhdl +TOPLEVEL_LANG ?= vhdl + +ifneq ($(TOPLEVEL_LANG),vhdl) + +all: + @echo "Skipping test due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being vhdl" +clean:: + +else TOPLEVEL = dec_viterbi @@ -52,25 +60,7 @@ VHDL_SOURCES = $(SRC_BASE)/packages/pkg_helper.vhd \ $(SRC_BASE)/src/recursion.vhd \ $(SRC_BASE)/src/dec_viterbi.vhd \ -GPI_IMPL:= -ifeq ($(SIM),aldec) - GPI_IMPL=vhpi -endif -ifeq ($(SIM),modelsim) - GPI_IMPL=vhpi -endif -ifeq ($(SIM),questa) - GPI_IMPL=vhpi -endif -ifeq ($(SIM),ius) - GPI_IMPL=vhpi -endif - -ifneq ($(GPI_IMPL),) include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim -else -all: - @echo "Skipping test as VHDL not supported on simulator=$(SIM)" -clean:: + endif diff --git a/tests/test_cases/test_configuration/Makefile b/tests/test_cases/test_configuration/Makefile index eb9ade8b..bcfd0acf 100644 --- a/tests/test_cases/test_configuration/Makefile +++ b/tests/test_cases/test_configuration/Makefile @@ -25,9 +25,5 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -PWD := $(shell pwd) -COCOTB := $(PWD)/../../.. -TOPLEVEL_LANG?=vhdl - include ../../designs/vhdl_configurations/Makefile MODULE = test_configurations diff --git a/tests/test_cases/test_iteration_mixedlang/Makefile b/tests/test_cases/test_iteration_mixedlang/Makefile index 8e658638..ba9a83ed 100644 --- a/tests/test_cases/test_iteration_mixedlang/Makefile +++ b/tests/test_cases/test_iteration_mixedlang/Makefile @@ -25,8 +25,5 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -PWD := $(shell pwd) -COCOTB := $(PWD)/../../.. - include ../../designs/uart2bus/Makefile MODULE = test_iteration diff --git a/tests/test_cases/test_iteration_verilog/Makefile b/tests/test_cases/test_iteration_verilog/Makefile index 4882a53a..2876f95e 100644 --- a/tests/test_cases/test_iteration_verilog/Makefile +++ b/tests/test_cases/test_iteration_verilog/Makefile @@ -25,11 +25,19 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +TOPLEVEL_LANG ?= verilog + +ifneq ($(TOPLEVEL_LANG),verilog) + +all: + @echo "Skipping test due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being verilog" +clean:: + +else + PWD := $(shell pwd) COCOTB := $(PWD)/../../.. -TOPLEVEL_LANG = verilog - VERILOG_SOURCES = $(COCOTB)/examples/endian_swapper/hdl/endian_swapper.sv TOPLEVEL = endian_swapper_sv @@ -43,3 +51,5 @@ else include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim endif + +endif diff --git a/tests/test_cases/test_iteration_vhdl/Makefile b/tests/test_cases/test_iteration_vhdl/Makefile index 7fefa2d8..511a9928 100644 --- a/tests/test_cases/test_iteration_vhdl/Makefile +++ b/tests/test_cases/test_iteration_vhdl/Makefile @@ -25,9 +25,5 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -PWD := $(shell pwd) -COCOTB := $(PWD)/../../.. - include ../../designs/viterbi_decoder_axi4s/Makefile -#GPI_EXTRA=vhpi MODULE = test_iteration diff --git a/tests/test_cases/test_verilog_access/Makefile b/tests/test_cases/test_verilog_access/Makefile index a557fd5d..d89b11a6 100644 --- a/tests/test_cases/test_verilog_access/Makefile +++ b/tests/test_cases/test_verilog_access/Makefile @@ -25,8 +25,5 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -PWD := $(shell pwd) -COCOTB := $(PWD)/../../.. - include ../../designs/uart2bus/Makefile MODULE = test_verilog_access diff --git a/tests/test_cases/test_vhdl_access/Makefile b/tests/test_cases/test_vhdl_access/Makefile index f51d0b8c..d7897648 100644 --- a/tests/test_cases/test_vhdl_access/Makefile +++ b/tests/test_cases/test_vhdl_access/Makefile @@ -25,8 +25,5 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -PWD := $(shell pwd) -COCOTB := $(PWD)/../../.. - include ../../designs/viterbi_decoder_axi4s/Makefile MODULE = test_vhdl_access From 1bd682d6f4b0a395c7bd62a7d8b689573203bb7b Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Mon, 11 Jan 2016 19:05:23 +0000 Subject: [PATCH 1107/2656] Cleanup: Some of the newer makefiles did not include COCOTB?= --- tests/designs/uart2bus/Makefile | 3 ++- tests/designs/vhdl_configurations/Makefile | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/designs/uart2bus/Makefile b/tests/designs/uart2bus/Makefile index 77c9cc95..a9c68fa2 100644 --- a/tests/designs/uart2bus/Makefile +++ b/tests/designs/uart2bus/Makefile @@ -8,7 +8,8 @@ clean:: else - +PWD=$(shell pwd) +COCOTB?=$(PWD)/../../.. SRC_BASE = $(COCOTB)/tests/designs/uart2bus VHDL_SOURCES = $(SRC_BASE)/vhdl/uart2BusTop_pkg.vhd \ diff --git a/tests/designs/vhdl_configurations/Makefile b/tests/designs/vhdl_configurations/Makefile index 91fbdd39..1c9f588e 100644 --- a/tests/designs/vhdl_configurations/Makefile +++ b/tests/designs/vhdl_configurations/Makefile @@ -2,6 +2,8 @@ TOPLEVEL_LANG ?= verilog TOPLEVEL = testbench +PWD=$(shell pwd) +COCOTB?=$(PWD)/../../.. VHDL_SOURCES = $(COCOTB)/tests/designs/vhdl_configurations/dut.vhd ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES = $(COCOTB)/tests/designs/vhdl_configurations/testbench.sv From 5fbe67f491fd8be48da395b9599c0506509595fe Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Fri, 20 Nov 2015 16:28:14 +1100 Subject: [PATCH 1108/2656] BinaryValue : fixing up little endian access plus testcase --- cocotb/binary.py | 9 ++++- tests/test_cases/test_cocotb/test_cocotb.py | 44 +++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index e81b3dc5..4abea0b7 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -476,6 +476,10 @@ def __getitem__(self, key): if index > self._bits - 1: raise IndexError('Index greater than number of bits.') _binstr = self.binstr[index] + if self.big_endian: + _binstr = self.binstr[index] + else: + _binstr = self.binstr[self._bits-1-index] rv = BinaryValue(bits=len(_binstr), bigEndian=self.big_endian, binaryRepresentation=self.binaryRepresentation) rv.set_binstr(_binstr) @@ -524,7 +528,10 @@ def __setitem__(self, key, val): index = key if index > self._bits - 1: raise IndexError('Index greater than number of bits.') - self.binstr = self.binstr[:index] + val + self.binstr[index + 1:] + if self.big_endian: + self.binstr = self.binstr[:index] + val + self.binstr[index + 1:] + else: + self.binstr = self.binstr[0:self._bits-index-1] + val + self.binstr[self._bits-index:self._bits] if __name__ == "__main__": import doctest diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index b0555e3f..dc94777c 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -40,6 +40,7 @@ from cocotb.clock import Clock from cocotb.result import ReturnValue, TestFailure, TestError, TestSuccess +from cocotb.binary import BinaryValue # Tests relating to providing meaningful errors if we forget to use the # yield keyword correctly to turn a function into a coroutine @@ -473,3 +474,46 @@ def test_logging_with_args(dut): dut.log.info("No substitution") yield Timer(100) #Make it do something with time + +@cocotb.test() +def test_binary_value(dut): + """ + Test out the cocotb supplied BinaryValue class for manipulating + values in a style familiar to rtl coders. + """ + + vec = BinaryValue(value=0,bits=16) + dut.log.info("Checking default endianess is Big Endian.") + if not vec.big_endian: + raise TestFailure("The default endianess is Little Endian - was expecting Big Endian.") + if vec.integer != 0: + raise TestFailure("Expecting our BinaryValue object to have the value 0.") + + dut.log.info("Checking single index assignment works as expected on a Little Endian BinaryValue.") + vec = BinaryValue(value=0,bits=16,bigEndian=False) + if vec.big_endian: + raise TestFailure("Our BinaryValue object is reporting it is Big Endian - was expecting Little Endian.") + for x in range(vec._bits): + vec[x] = '1' + dut.log.info("Trying vec[%s] = 1" % x) + expected_value = 2**(x+1) - 1 + if vec.integer != expected_value: + raise TestFailure("Failed on assignment to vec[%s] - expecting %s - got %s" % (x,expected_value,vec.integer)) + if vec[x] != 1: + raise TestFailure("Failed on index compare on vec[%s] - expecting 1 - got %s" % (x,vec[x])) + dut.log.info("vec = 'b%s" % vec.binstr) + + dut.log.info("Checking slice assignment works as expected on a Little Endian BinaryValue.") + if vec.integer != 65535: + raise TestFailure("Expecting our BinaryValue object to be 65535 after the end of the previous test.") + vec[7:0] = '00110101' + if vec.binstr != '1111111100110101': + raise TestFailure("Set lower 8-bits to 00110101 but readback %s" % vec.binstr) + if vec[7:0].binstr != '00110101': + raise TestFailure("Set lower 8-bits to 00110101 but readback %s from vec[7:0]" % vec[7:0].binstr) + + dut.log.info("vec[7:0] = 'b%s" % vec[7:0].binstr) + dut.log.info("vec[15:8] = 'b%s" % vec[15:8].binstr) + dut.log.info("vec = 'b%s" % vec.binstr) + + yield Timer(100) #Make it do something with time From 76ca1f8a4cdbdeb6097a8c7a2ce3cb55b72b3683 Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Fri, 22 Jan 2016 12:00:49 +1100 Subject: [PATCH 1109/2656] Issue #382 : introducing simulator time precision into cocotb Currently only works with VPI. Need to work out the equivalent of: vpi_get(vpiTimePrecision, NULL); for VHPI --- cocotb/log.py | 13 +++++++------ include/gpi.h | 2 +- include/vpi_user.h | 2 ++ lib/fli/FliImpl.cpp | 3 ++- lib/fli/FliImpl.h | 2 +- lib/gpi/GpiCommon.cpp | 4 ++-- lib/gpi/gpi_priv.h | 2 +- lib/simulator/simulatormodule.c | 8 +++++--- lib/vhpi/VhpiImpl.cpp | 3 ++- lib/vhpi/VhpiImpl.h | 2 +- lib/vpi/VpiImpl.cpp | 3 ++- lib/vpi/VpiImpl.h | 2 +- 12 files changed, 27 insertions(+), 19 deletions(-) diff --git a/cocotb/log.py b/cocotb/log.py index 9a19eba7..f214eff0 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -162,8 +162,9 @@ def rjust(string, chars): return ".." + string[(chars - 2) * -1:] return string.rjust(chars) - def _format(self, timeh, timel, level, record, msg): - simtime = "% 6d.%02dns" % ((timel / 1000), (timel % 1000) / 10) + def _format(self, timeh, timel, precision, level, record, msg): + time_ns = (timeh << 32 | timel) * (10.0**precision) / 1e-9 + simtime = "%6.2fns" % (time_ns) prefix = simtime + ' ' + level + ' ' + \ self.ljust(record.name, _RECORD_CHARS) + \ self.rjust(os.path.split(record.filename)[1], _FILENAME_CHARS) + \ @@ -182,9 +183,9 @@ def format(self, record): msg = str(msg) level = record.levelname.ljust(_LEVEL_CHARS) - timeh, timel = simulator.get_sim_time() + timeh, timel, precision = simulator.get_sim_time() - return self._format(timeh, timel, level, record, msg) + return self._format(timeh, timel, precision, level, record, msg) class SimColourLogFormatter(SimLogFormatter): @@ -210,5 +211,5 @@ def format(self, record): level = (SimColourLogFormatter.loglevel2colour[record.levelno] % record.levelname.ljust(_LEVEL_CHARS)) - timeh, timel = simulator.get_sim_time() - return self._format(timeh, timel, level, record, msg) + timeh, timel, precision = simulator.get_sim_time() + return self._format(timeh, timel, precision, level, record, msg) diff --git a/include/gpi.h b/include/gpi.h index 58131f59..0ae16766 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -110,7 +110,7 @@ void gpi_sim_end(void); // Returns simulation time as a float. Units are default sim units -void gpi_get_sim_time(uint32_t *high, uint32_t *low); +void gpi_get_sim_time(uint32_t *high, uint32_t *low, int32_t *precision); // Functions for extracting a gpi_sim_hdl to an object diff --git a/include/vpi_user.h b/include/vpi_user.h index 7ff8a375..4bf64652 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -290,6 +290,8 @@ typedef struct t_vpi_value #define vpiUnknown 3 +#define vpiTimePrecision 12 /* module time precision */ + /* normal callback structure */ typedef struct t_cb_data { diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 9909ad9d..7f939a5e 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -376,10 +376,11 @@ const char *FliImpl::reason_to_string(int reason) * * NB units depend on the simulation configuration */ -void FliImpl::get_sim_time(uint32_t *high, uint32_t *low) +void FliImpl::get_sim_time(uint32_t *high, uint32_t *low, int32_t *precision) { *high = mti_NowUpper(); *low = mti_Now(); + *precision = 0; } /** diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index 328e6cdd..81a061f9 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -403,7 +403,7 @@ class FliImpl : public GpiImplInterface { /* Sim related */ void sim_end(void); - void get_sim_time(uint32_t *high, uint32_t *low); + void get_sim_time(uint32_t *high, uint32_t *low, int32_t *precision); /* Hierachy related */ GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index c9c5fab8..7c17a09d 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -196,9 +196,9 @@ void gpi_load_extra_libs(void) gpi_print_registered_impl(); } -void gpi_get_sim_time(uint32_t *high, uint32_t *low) +void gpi_get_sim_time(uint32_t *high, uint32_t *low, int32_t *precision) { - registered_impls[0]->get_sim_time(high, low); + registered_impls[0]->get_sim_time(high, low, precision); } gpi_sim_hdl gpi_get_root_handle(const char *name) diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 192b3d81..f84658b2 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -287,7 +287,7 @@ class GpiImplInterface { /* Sim related */ virtual void sim_end(void) = 0; - virtual void get_sim_time(uint32_t *high, uint32_t *low) = 0; + virtual void get_sim_time(uint32_t *high, uint32_t *low, int32_t *precision) = 0; /* Hierachy related */ virtual GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent) = 0; diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index e5338353..097f7528 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -848,22 +848,24 @@ static PyObject *get_type_string(PyObject *self, PyObject *args) } -// Returns a high, low tuple of simulator time +// Returns a high, low, precision tuple of simulator time // Note we can never log from this function since the logging mechanism calls this to annotate // log messages with the current simulation time static PyObject *get_sim_time(PyObject *self, PyObject *args) { uint32_t high, low; + int32_t precision; PyGILState_STATE gstate; gstate = TAKE_GIL(); - gpi_get_sim_time(&high, &low); + gpi_get_sim_time(&high, &low, &precision); - PyObject *pTuple = PyTuple_New(2); + PyObject *pTuple = PyTuple_New(3); PyTuple_SetItem(pTuple, 0, PyLong_FromUnsignedLong(high)); // Note: This function “steals†a reference to o. PyTuple_SetItem(pTuple, 1, PyLong_FromUnsignedLong(low)); // Note: This function “steals†a reference to o. + PyTuple_SetItem(pTuple, 2, PyLong_FromLong(precision)); // Note: This function “steals†a reference to o. DROP_GIL(gstate); diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index a699f422..000b6e9e 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -103,13 +103,14 @@ const char *VhpiImpl::reason_to_string(int reason) } } -void VhpiImpl::get_sim_time(uint32_t *high, uint32_t *low) +void VhpiImpl::get_sim_time(uint32_t *high, uint32_t *low, int32_t *precision) { vhpiTimeT vhpi_time_s; vhpi_get_time(&vhpi_time_s, NULL); check_vhpi_error(); *high = vhpi_time_s.high; *low = vhpi_time_s.low; + *precision = 0; } // Determine whether a VHPI object type is a constant or not diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 9c9562b4..4f3ea2d2 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -221,7 +221,7 @@ class VhpiImpl : public GpiImplInterface { /* Sim related */ void sim_end(void); - void get_sim_time(uint32_t *high, uint32_t *low); + void get_sim_time(uint32_t *high, uint32_t *low, int32_t *precision); /* Hierachy related */ GpiObjHdl *get_root_handle(const char *name); diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 1c8fb5ca..2349181e 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -59,7 +59,7 @@ const char *VpiImpl::reason_to_string(int reason) } } -void VpiImpl::get_sim_time(uint32_t *high, uint32_t *low) +void VpiImpl::get_sim_time(uint32_t *high, uint32_t *low, int32_t *precision) { s_vpi_time vpi_time_s; vpi_time_s.type = vpiSimTime; //vpiSimTime; @@ -67,6 +67,7 @@ void VpiImpl::get_sim_time(uint32_t *high, uint32_t *low) check_vpi_error(); *high = vpi_time_s.high; *low = vpi_time_s.low; + *precision = vpi_get(vpiTimePrecision, NULL); } gpi_objtype_t to_gpi_objtype(int32_t vpitype) diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 8786fb6a..d6ecbcc2 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -251,7 +251,7 @@ class VpiImpl : public GpiImplInterface { /* Sim related */ void sim_end(void); - void get_sim_time(uint32_t *high, uint32_t *low); + void get_sim_time(uint32_t *high, uint32_t *low, int32_t *precision); /* Hierachy related */ GpiObjHdl *get_root_handle(const char *name); From 4461d71026807414ebf4e2af9fb301f1bce82376 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 5 Feb 2016 08:05:05 +0000 Subject: [PATCH 1110/2656] Issue #389: Use LIB_EXT for fli library on questa/modelsim --- makefiles/simulators/Makefile.questa | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index fea16134..0c6e66ca 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -47,7 +47,7 @@ endif GPI_EXTRA:= ifeq ($(TOPLEVEL_LANG),vhdl) - VSIM_ARGS += -foreign \"cocotb_init libfli.so\" + VSIM_ARGS += -foreign \"cocotb_init libfli.$(LIB_EXT)\" ifneq ($(VERILOG_SOURCES),) GPI_EXTRA = vpi endif From b6d7828b60886ddedc1203f1e868694bac581304 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 5 Feb 2016 09:07:31 +0000 Subject: [PATCH 1111/2656] Cleanup: Track expected failure correctly in the test --- tests/test_cases/test_iteration_verilog/Makefile | 10 ++-------- .../test_iteration_verilog/test_iteration_es.py | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/tests/test_cases/test_iteration_verilog/Makefile b/tests/test_cases/test_iteration_verilog/Makefile index 2876f95e..3df0c8c3 100644 --- a/tests/test_cases/test_iteration_verilog/Makefile +++ b/tests/test_cases/test_iteration_verilog/Makefile @@ -43,13 +43,7 @@ TOPLEVEL = endian_swapper_sv MODULE = test_iteration_es -ifeq ($(SIM),) -all: - @echo "Skipping test as ICAURS does not support enough VPI calls" -clean:: -else - include $(COCOTB)/makefiles/Makefile.inc - include $(COCOTB)/makefiles/Makefile.sim -endif +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim endif diff --git a/tests/test_cases/test_iteration_verilog/test_iteration_es.py b/tests/test_cases/test_iteration_verilog/test_iteration_es.py index 9aaab8fd..9a599e01 100644 --- a/tests/test_cases/test_iteration_verilog/test_iteration_es.py +++ b/tests/test_cases/test_iteration_verilog/test_iteration_es.py @@ -29,7 +29,7 @@ from cocotb.triggers import Timer from cocotb.result import TestError, TestFailure -@cocotb.test() +@cocotb.test(expect_fail=cocotb.SIM_NAME in ["Icarus Verilog"]) def recursive_discovery(dut): """ Recursively discover every single object in the design From 302892100b997fe70e8e65810846a56aa2244cfb Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 10 Feb 2016 10:54:01 +0000 Subject: [PATCH 1112/2656] Cleanup: Correct generate loop discovery in Modelsim --- cocotb/handle.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 0d338c4d..79a1a058 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -65,7 +65,8 @@ class SimHandleBase(object): # For backwards compatibility we support a mapping of old member names # which may alias with the simulator hierarchy. In these cases the - # simulator takes priority, only falling back + # simulator result takes priority, only falling back to the python member + # if there is no colliding object in the elaborated design. _compat_mapping = { "log" : "_log", "fullname" : "_fullname", @@ -226,6 +227,10 @@ def _discover_all(self): if not result: result = re.match("(?P.*)\((?P\d+)\)$", name) + # Modelsim VPI returns names in standard form name[index] + if not result: + result = re.match("(?P.*)\[(?P\d+)\]$", name) + if result: index = int(result.group("index")) name = result.group("name") From d818c3946896c41f6d41925b21fc4f09b223f635 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 11 Feb 2016 16:03:08 +0000 Subject: [PATCH 1113/2656] Cleanup: Ensure we check against int and long on Python2 for int assignment --- cocotb/handle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 79a1a058..102bc9e6 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -555,7 +555,7 @@ def setimmediatevalue(self, value): """ if isinstance(value, BinaryValue): value = int(value) - elif not isinstance(value, int): + elif not isinstance(value, get_python_integer_types()): self._log.critical("Unsupported type for integer value assignment: %s (%s)" % (type(value), repr(value))) raise TypeError("Unable to set simulator value with type %s" % (type(value))) From bb4be420acb95bb3d8e073f251357cdaa4ddebd0 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Thu, 18 Feb 2016 10:51:32 -0700 Subject: [PATCH 1114/2656] Fix test 'issue_330' for TOPLEVEL_LANG=vhdl --- tests/designs/sample_module/Makefile | 2 +- .../designs/sample_module/sample_module.vhdl | 5 +++ .../sample_module/sample_module_pack.vhdl | 45 +++++++++++++++++++ tests/test_cases/issue_330/issue_330.py | 10 ++--- 4 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 tests/designs/sample_module/sample_module_pack.vhdl diff --git a/tests/designs/sample_module/Makefile b/tests/designs/sample_module/Makefile index 3eee833a..8d01c734 100644 --- a/tests/designs/sample_module/Makefile +++ b/tests/designs/sample_module/Makefile @@ -43,7 +43,7 @@ endif ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES = $(COCOTB)/tests/designs/sample_module/sample_module.v else ifeq ($(TOPLEVEL_LANG),vhdl) - VHDL_SOURCES = $(COCOTB)/tests/designs/sample_module/sample_module_1.vhdl $(COCOTB)/tests/designs/sample_module/sample_module.vhdl + VHDL_SOURCES = $(COCOTB)/tests/designs/sample_module/sample_module_pack.vhdl $(COCOTB)/tests/designs/sample_module/sample_module_1.vhdl $(COCOTB)/tests/designs/sample_module/sample_module.vhdl else $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") endif diff --git a/tests/designs/sample_module/sample_module.vhdl b/tests/designs/sample_module/sample_module.vhdl index dd4cbd9c..e6f70f9a 100644 --- a/tests/designs/sample_module/sample_module.vhdl +++ b/tests/designs/sample_module/sample_module.vhdl @@ -34,6 +34,9 @@ library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; +library work; +use work.sample_module_pack.all; + entity sample_module is port ( clk : in std_ulogic; @@ -48,6 +51,8 @@ entity sample_module is stream_in_string : in string(1 to 8); stream_in_bool : in boolean; + inout_if : in test_if; + stream_out_data_comb : out std_ulogic_vector(7 downto 0); stream_out_data_registered : out std_ulogic_vector(7 downto 0); stream_out_data_wide : out std_ulogic_vector(63 downto 0); diff --git a/tests/designs/sample_module/sample_module_pack.vhdl b/tests/designs/sample_module/sample_module_pack.vhdl new file mode 100644 index 00000000..d8ac952e --- /dev/null +++ b/tests/designs/sample_module/sample_module_pack.vhdl @@ -0,0 +1,45 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2014 Potential Ventures Ltd +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- * Redistributions of source code must retain the above copyright +-- notice, this list of conditions and the following disclaimer. +-- * Redistributions in binary form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- * Neither the name of Potential Ventures Ltd, +-- Copyright (c) 2013 SolarFlare Communications Inc nor the +-- names of its contributors may be used to endorse or promote products +-- derived from this software without specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +-- DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +-- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +-- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +------------------------------------------------------------------------------- + + +library ieee; + +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +package sample_module_pack is + + type test_if is record + a_in : std_logic; + b_out : std_logic; + end record test_if; + +end package sample_module_pack; + +package body sample_module_pack is +end package body sample_module_pack; diff --git a/tests/test_cases/issue_330/issue_330.py b/tests/test_cases/issue_330/issue_330.py index 7f6dbfa7..0898433c 100644 --- a/tests/test_cases/issue_330/issue_330.py +++ b/tests/test_cases/issue_330/issue_330.py @@ -7,9 +7,7 @@ from cocotb.result import TestFailure from cocotb.binary import BinaryValue -@cocotb.test(skip=cocotb.SIM_NAME in ["Icarus Verilog"], - expect_error=cocotb.SIM_NAME in ["ModelSim ALTERA STARTER EDITION", - "ModelSim DE"]) +@cocotb.test(skip=cocotb.SIM_NAME in ["Icarus Verilog"]) def issue_330_direct(dut): """ Access a structure @@ -20,11 +18,9 @@ def issue_330_direct(dut): structure = dut.inout_if - tlog.info("Value of inout_if => a_in = %d ; b_oout = %d" % (structure.a_in, structure.b_out)) + tlog.info("Value of inout_if => a_in = %d ; b_out = %d" % (structure.a_in, structure.b_out)) -@cocotb.test(skip=cocotb.SIM_NAME in ["Icarus Verilog"], - expect_fail=cocotb.SIM_NAME in ["ModelSim ALTERA STARTER EDITION", - "ModelSim DE"]) +@cocotb.test(skip=cocotb.SIM_NAME in ["Icarus Verilog"]) def issue_330_iteration(dut): """ Access a structure via issue_330_iteration From c1ed991dfd44e8d296d702315f24fbbc01c2f5c3 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Thu, 18 Feb 2016 11:48:22 -0700 Subject: [PATCH 1115/2656] Fix 'test_avalon' for SIM=ius. Changed localparam to parameter. --- tests/designs/avalon_module/burst_read_master.v | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/designs/avalon_module/burst_read_master.v b/tests/designs/avalon_module/burst_read_master.v index c2d4b2a1..c57b8c10 100644 --- a/tests/designs/avalon_module/burst_read_master.v +++ b/tests/designs/avalon_module/burst_read_master.v @@ -49,13 +49,13 @@ module scfifo ( output [`FIFODEPTH_LOG2_DEF-1:0] usedw, input wrreq); -localparam lpm_width = 32; -localparam lpm_numwords = 32; // FIFODEPTH -localparam lpm_showahead = "ON"; -localparam use_eab = "ON"; -localparam add_ram_output_register = "OFF"; -localparam underflow_checking = "OFF"; -localparam overflow_checking = "OFF"; +parameter lpm_width = 32; +parameter lpm_numwords = 32; // FIFODEPTH +parameter lpm_showahead = "ON"; +parameter use_eab = "ON"; +parameter add_ram_output_register = "OFF"; +parameter underflow_checking = "OFF"; +parameter overflow_checking = "OFF"; reg[lpm_width-1:0] buf_out; reg buf_empty, buf_full; From 90912d56c4d17f89010fefe5410b720d92b222c6 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Thu, 18 Feb 2016 11:51:50 -0700 Subject: [PATCH 1116/2656] Fix 'test_closedown' to expect an error so the test reports as passed --- tests/test_cases/test_closedown/test_closedown.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/test_closedown/test_closedown.py b/tests/test_cases/test_closedown/test_closedown.py index 22b03bd2..0f8d5975 100644 --- a/tests/test_cases/test_closedown/test_closedown.py +++ b/tests/test_cases/test_closedown/test_closedown.py @@ -56,7 +56,7 @@ def clock_mon(dut): yield RisingEdge(dut.clk) -@cocotb.test(expect_fail=True) +@cocotb.test(expect_error=True) def test_failure_from_system_task(dut): """Allow the dut to call $fail_test() from verilog""" clock = Clock(dut.clk, 100) From a97278e1a7d4ca67dfeaaad1798533188da35cd3 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Thu, 18 Feb 2016 12:20:41 -0700 Subject: [PATCH 1117/2656] Fix 'test_cocotb' to work with SIM=questa --- tests/test_cases/test_cocotb/test_cocotb.py | 25 +++++++++------------ 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index b0555e3f..bddb9365 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -188,12 +188,10 @@ def do_test_afterdelay_in_readonly(dut, delay): @cocotb.test(expect_error=True, - expect_fail=cocotb.SIM_NAME in ["Icarus Verilog", - "Riviera-PRO", - "ModelSim ALTERA STARTER EDITION", - "ModelSim DE", - "ncsim(64)", - "ncsim"]) + expect_fail=cocotb.SIM_NAME.lower().startswith(("icarus", + "riviera", + "modelsim", + "ncsim"))) def test_readwrite_in_readonly(dut): """Test doing invalid sim operation""" global exited @@ -206,10 +204,9 @@ def test_readwrite_in_readonly(dut): raise TestFailure -@cocotb.test(expect_fail=cocotb.SIM_NAME in ["Icarus Verilog", - "Chronologic Simulation VCS Release"], - skip=cocotb.SIM_NAME in ["ncsim(64)", - "ncsim"]) +@cocotb.test(expect_fail=cocotb.SIM_NAME.lower().startswith(("icarus", + "chronologic simulation vcs")), + skip=cocotb.SIM_NAME.lower().startswith(("ncsim"))) def test_afterdelay_in_readonly(dut): """Test doing invalid sim operation""" global exited @@ -455,7 +452,7 @@ def test_edge_count(dut): class StrCallCounter(object): def __init__(self): self.str_counter = 0 - + def __str__(self): self.str_counter += 1 return "__str__ called %d time(s)" % self.str_counter @@ -466,10 +463,10 @@ def test_logging_with_args(dut): dut.log.logger.setLevel(logging.INFO) #To avoid logging debug message, to make next line run without error dut.log.debug("%s", counter) assert counter.str_counter == 0 - + dut.log.info("%s", counter) assert counter.str_counter == 1 - + dut.log.info("No substitution") - + yield Timer(100) #Make it do something with time From 933971cdd9e92bc9031f9243500c3ae5f3b31114 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Thu, 18 Feb 2016 13:49:37 -0700 Subject: [PATCH 1118/2656] Fix 'test_configuration' to only work with TOPLEVL_LANG=vhdl --- makefiles/simulators/Makefile.ius | 2 +- tests/designs/vhdl_configurations/Makefile | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 8428a083..83606c20 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -61,7 +61,7 @@ else ifeq ($(TOPLEVEL_LANG),vhdl) EXTRA_ARGS += -top $(TOPLEVEL) MAKE_LIB = -makelib $(TOPLEVEL) HDL_SOURCES = $(VHDL_SOURCES) -ifneq ($(VHDL_SOURCES),) +ifneq ($(VERILOG_SOURCES),) HDL_SOURCES += $(VERILOG_SOURCES) endif else diff --git a/tests/designs/vhdl_configurations/Makefile b/tests/designs/vhdl_configurations/Makefile index 1c9f588e..6aff5c63 100644 --- a/tests/designs/vhdl_configurations/Makefile +++ b/tests/designs/vhdl_configurations/Makefile @@ -1,4 +1,18 @@ -TOPLEVEL_LANG ?= verilog +TOPLEVEL_LANG ?= vhdl + +ifneq ($(TOPLEVEL_LANG),vhdl) + +# Currently, there is an issue with file dependency when dealing with a Mixed Language Simulation like this. +# With the current process, all the VHDL is compiled first and then all the Verilog which doesn't work with a +# dependency like below. Compile order would need to be: +# 1. dut.vhd +# 2. testbench.vhd or testbench.v +# 3. configurations.vhd +all: + @echo "Skipping test due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being vhdl" +clean:: + +else TOPLEVEL = testbench @@ -17,3 +31,4 @@ VHDL_SOURCES += $(COCOTB)/tests/designs/vhdl_configurations/configurations.vhd include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim +endif From 9a709305601a8fca56dcbdaefcef45cf0e2c55e4 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Sun, 21 Feb 2016 08:01:30 -0700 Subject: [PATCH 1119/2656] Fix FLI support for variables and generate loops --- lib/fli/FliImpl.cpp | 111 ++++++++++++++++++++++++++++++++---------- lib/fli/FliImpl.h | 67 ++++++++++++++++++++++--- lib/fli/FliObjHdl.cpp | 41 ++++++++++++++++ 3 files changed, 188 insertions(+), 31 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index c48d7aa3..3ee19206 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -172,13 +172,11 @@ bool FliImpl::isTypeSignal(int type, int full_type) return (type == accSignal || full_type == accAliasSignal); } -GpiObjHdl *FliImpl::create_gpi_obj_from_handle(void *hdl, std::string &name, std::string &fq_name) +GpiObjHdl *FliImpl::create_gpi_obj_from_handle(void *hdl, std::string &name, std::string &fq_name, int accType, int accFullType) { GpiObjHdl *new_obj = NULL; - PLI_INT32 accType = acc_fetch_type(hdl); - PLI_INT32 accFullType = acc_fetch_fulltype(hdl); - + LOG_DEBUG("Attepmting to create GPI object from handle (Type=%d, FullType=%d).", accType, accFullType); if (!VS_TYPE_IS_VHDL(accFullType)) { LOG_DEBUG("Handle is not a VHDL type."); return NULL; @@ -186,7 +184,7 @@ GpiObjHdl *FliImpl::create_gpi_obj_from_handle(void *hdl, std::string &name, std if (!isTypeValue(accType)) { LOG_DEBUG("Found region %s -> %p", fq_name.c_str(), hdl); - new_obj = new GpiObjHdl(this, hdl, GPI_MODULE); + new_obj = new FliObjHdl(this, hdl, GPI_MODULE, accType, accFullType); } else { bool is_var; bool is_const; @@ -201,7 +199,7 @@ GpiObjHdl *FliImpl::create_gpi_obj_from_handle(void *hdl, std::string &name, std } else { LOG_DEBUG("Found a variable %s -> %p", fq_name.c_str(), hdl); is_var = true; - is_const = isValueConst(mti_GetVarKind(static_cast(hdl))); + is_const = isValueConst(accFullType); valType = mti_GetVarType(static_cast(hdl)); } @@ -210,19 +208,19 @@ GpiObjHdl *FliImpl::create_gpi_obj_from_handle(void *hdl, std::string &name, std switch (typeKind) { case MTI_TYPE_ENUM: if (isValueLogic(valType)) { - new_obj = new FliLogicObjHdl(this, hdl, GPI_ENUM, is_const, is_var, valType, typeKind); + new_obj = new FliLogicObjHdl(this, hdl, GPI_ENUM, is_const, accType, accFullType, is_var, valType, typeKind); } else if (isValueBoolean(valType) || isValueChar(valType)) { - new_obj = new FliIntObjHdl(this, hdl, GPI_INTEGER, is_const, is_var, valType, typeKind); + new_obj = new FliIntObjHdl(this, hdl, GPI_INTEGER, is_const, accType, accFullType, is_var, valType, typeKind); } else { - new_obj = new FliEnumObjHdl(this, hdl, GPI_ENUM, is_const, is_var, valType, typeKind); + new_obj = new FliEnumObjHdl(this, hdl, GPI_ENUM, is_const, accType, accFullType, is_var, valType, typeKind); } break; case MTI_TYPE_SCALAR: case MTI_TYPE_PHYSICAL: - new_obj = new FliIntObjHdl(this, hdl, GPI_INTEGER, is_const, is_var, valType, typeKind); + new_obj = new FliIntObjHdl(this, hdl, GPI_INTEGER, is_const, accType, accFullType, is_var, valType, typeKind); break; case MTI_TYPE_REAL: - new_obj = new FliRealObjHdl(this, hdl, GPI_REAL, is_const, is_var, valType, typeKind); + new_obj = new FliRealObjHdl(this, hdl, GPI_REAL, is_const, accType, accFullType, is_var, valType, typeKind); break; case MTI_TYPE_ARRAY: { mtiTypeIdT elemType = mti_GetArrayElementType(valType); @@ -231,20 +229,20 @@ GpiObjHdl *FliImpl::create_gpi_obj_from_handle(void *hdl, std::string &name, std switch (elemTypeKind) { case MTI_TYPE_ENUM: if (isValueLogic(elemType)) { - new_obj = new FliLogicObjHdl(this, hdl, GPI_ARRAY, is_const, is_var, valType, typeKind); // std_logic_vector + new_obj = new FliLogicObjHdl(this, hdl, GPI_ARRAY, is_const, accType, accFullType, is_var, valType, typeKind); // std_logic_vector } else if (isValueChar(elemType)) { - new_obj = new FliStringObjHdl(this, hdl, GPI_STRING, is_const, is_var, valType, typeKind); + new_obj = new FliStringObjHdl(this, hdl, GPI_STRING, is_const, accType, accFullType, is_var, valType, typeKind); } else { - new_obj = new GpiObjHdl(this, hdl, GPI_MODULE); // array of enums + new_obj = new FliObjHdl(this, hdl, GPI_MODULE, accType, accFullType); // array of enums } break; default: - new_obj = new GpiObjHdl(this, hdl, GPI_MODULE);// array of (array, Integer, Real, Record, etc.) + new_obj = new FliObjHdl(this, hdl, GPI_MODULE, accType, accFullType);// array of (array, Integer, Real, Record, etc.) } } break; case MTI_TYPE_RECORD: - new_obj = new GpiObjHdl(this, hdl, GPI_STRUCTURE); + new_obj = new FliObjHdl(this, hdl, GPI_STRUCTURE, accType, accFullType); break; default: LOG_ERROR("Unable to handle object type for %s (%d)", name.c_str(), typeKind); @@ -281,7 +279,10 @@ GpiObjHdl* FliImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) std::string name = c_name; std::string fq_name = c_fullname; - return create_gpi_obj_from_handle(raw_hdl, name, fq_name); + PLI_INT32 accType = acc_fetch_type(raw_hdl); + PLI_INT32 accFullType = acc_fetch_fulltype(raw_hdl); + + return create_gpi_obj_from_handle(raw_hdl, name, fq_name, accType, accFullType); } /** @@ -310,19 +311,46 @@ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) writable.push_back('\0'); HANDLE hdl; + PLI_INT32 accType; + PLI_INT32 accFullType; if ((hdl = mti_FindRegion(&writable[0])) != NULL) { + accType = acc_fetch_type(hdl); + accFullType = acc_fetch_fulltype(hdl); LOG_DEBUG("Found region %s -> %p", fq_name.c_str(), hdl); + LOG_DEBUG(" Type: %d", accType); + LOG_DEBUG(" Full Type: %d", accFullType); } else if ((hdl = mti_FindSignal(&writable[0])) != NULL) { + accType = acc_fetch_type(hdl); + accFullType = acc_fetch_fulltype(hdl); LOG_DEBUG("Found a signal %s -> %p", fq_name.c_str(), hdl); + LOG_DEBUG(" Type: %d", accType); + LOG_DEBUG(" Full Type: %d", accFullType); } else if ((hdl = mti_FindVar(&writable[0])) != NULL) { + accFullType = accType = mti_GetVarKind(static_cast(hdl)); LOG_DEBUG("Found a variable %s -> %p", fq_name.c_str(), hdl); + LOG_DEBUG(" Type: %d", accType); + LOG_DEBUG(" Full Type: %d", accFullType); } else { LOG_DEBUG("Didn't find anything named %s", &writable[0]); return NULL; } - return create_gpi_obj_from_handle(hdl, name, fq_name); + /* Added to behave like vhpi interface. Handle.py will does not support a handle to a + * "for generate" loop that doesn't contain an index. + * + * VHDL (in dut): + * a_loop : for i in 0 to 9 generate + * ... + * end generate a_loop; + * + * FLI will return a vaild handle to "/dut/a_loop" as well as /dut/a_loop(0) + */ + if (accFullType == accForGenerate) { + return NULL; + } + + return create_gpi_obj_from_handle(hdl, name, fq_name, accType, accFullType); } /** @@ -345,19 +373,32 @@ GpiObjHdl* FliImpl::native_check_create(uint32_t index, GpiObjHdl *parent) writable.push_back('\0'); HANDLE hdl; + PLI_INT32 accType; + PLI_INT32 accFullType; if ((hdl = mti_FindRegion(&writable[0])) != NULL) { + accType = acc_fetch_type(hdl); + accFullType = acc_fetch_fulltype(hdl); LOG_DEBUG("Found region %s -> %p", fq_name.c_str(), hdl); + LOG_DEBUG(" Type: %d", accType); + LOG_DEBUG(" Full Type: %d", accFullType); } else if ((hdl = mti_FindSignal(&writable[0])) != NULL) { + accType = acc_fetch_type(hdl); + accFullType = acc_fetch_fulltype(hdl); LOG_DEBUG("Found a signal %s -> %p", fq_name.c_str(), hdl); + LOG_DEBUG(" Type: %d", accType); + LOG_DEBUG(" Full Type: %d", accFullType); } else if ((hdl = mti_FindVar(&writable[0])) != NULL) { + accFullType = accType = mti_GetVarKind(static_cast(hdl)); LOG_DEBUG("Found a variable %s -> %p", fq_name.c_str(), hdl); + LOG_DEBUG(" Type: %d", accType); + LOG_DEBUG(" Full Type: %d", accFullType); } else { LOG_DEBUG("Didn't find anything named %s", &writable[0]); return NULL; } - return create_gpi_obj_from_handle(hdl, name, fq_name); + return create_gpi_obj_from_handle(hdl, name, fq_name, accType, accFullType); } else { LOG_ERROR("FLI: Parent of type %d must be of type GPI_MODULE or GPI_ARRAY to have an index.", parent->get_type()); return NULL; @@ -400,6 +441,8 @@ GpiObjHdl *FliImpl::get_root_handle(const char *name) char *rgn_fullname; std::string root_name; std::string root_fullname; + PLI_INT32 accType; + PLI_INT32 accFullType; for (root = mti_GetTopRegion(); root != NULL; root = mti_NextRegion(root)) { LOG_DEBUG("Iterating over: %s", mti_GetRegionName(root)); @@ -420,7 +463,10 @@ GpiObjHdl *FliImpl::get_root_handle(const char *name) LOG_DEBUG("Found toplevel: %s, creating handle....", root_name.c_str()); - return create_gpi_obj_from_handle(root, root_name, root_fullname); + accType = acc_fetch_type(root); + accFullType = acc_fetch_fulltype(root); + + return create_gpi_obj_from_handle(root, root_name, root_fullname, accType, accFullType); error: @@ -559,11 +605,17 @@ FliIterator::FliIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(i m_regs(), m_currentHandles(NULL) { - HANDLE fli_hdl = m_parent->get_handle(); + int type; - int type = acc_fetch_fulltype(fli_hdl); + if (m_parent->get_type() == GPI_MODULE or m_parent->get_type() == GPI_STRUCTURE) { + FliObjHdl *fli_obj = static_cast(m_parent); + type = fli_obj->get_acc_full_type(); + } else { + FliSignalObjHdl *fli_obj = static_cast(m_parent); + type = fli_obj->get_acc_full_type(); + } - LOG_DEBUG("fli_iterator::Create iterator for %s of type %s", m_parent->get_fullname().c_str(), acc_fetch_type_str(type)); + LOG_DEBUG("fli_iterator::Create iterator for %s of type %d:%s", m_parent->get_fullname().c_str(), type, acc_fetch_type_str(type)); if (NULL == (selected = iterate_over.get_options(type))) { LOG_WARN("FLI: Implementation does not know how to iterate over %s(%d)", @@ -666,19 +718,28 @@ GpiIterator::Status FliIterator::next_handle(std::string &name, GpiObjHdl **hdl, } char *c_name; + PLI_INT32 accType; + PLI_INT32 accFullType; switch (*one2many) { case FliIterator::OTM_CONSTANTS: case FliIterator::OTM_VARIABLE_SUB_ELEMENTS: c_name = mti_GetVarName(static_cast(obj)); + accFullType = accType = mti_GetVarKind(static_cast(obj)); break; case FliIterator::OTM_SIGNALS: c_name = mti_GetSignalName(static_cast(obj)); + accType = acc_fetch_type(obj); + accFullType = acc_fetch_fulltype(obj); break; case FliIterator::OTM_SIGNAL_SUB_ELEMENTS: c_name = mti_GetSignalNameIndirect(static_cast(obj), NULL, 0); + accType = acc_fetch_type(obj); + accFullType = acc_fetch_fulltype(obj); break; case FliIterator::OTM_REGIONS: c_name = mti_GetRegionName(static_cast(obj)); + accType = acc_fetch_type(obj); + accFullType = acc_fetch_fulltype(obj); break; default: LOG_WARN("Unhandled OneToMany Type (%d)", *one2many); @@ -710,8 +771,8 @@ GpiIterator::Status FliIterator::next_handle(std::string &name, GpiObjHdl **hdl, fq_name += "/" + name; } - FliImpl *fli_impl = reinterpret_cast(m_impl); - new_obj = fli_impl->create_gpi_obj_from_handle(obj, name, fq_name); + FliImpl *fli_impl = static_cast(m_impl); + new_obj = fli_impl->create_gpi_obj_from_handle(obj, name, fq_name, accType, accFullType); if (new_obj) { *hdl = new_obj; return GpiIterator::NATIVE; diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index 328e6cdd..a49b3af7 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -141,6 +141,38 @@ class FliTimedCbHdl : public FliProcessCbHdl { // Object Handles +class FliObjHdl : public GpiObjHdl { +public: + FliObjHdl(GpiImplInterface *impl, + void *hdl, + gpi_objtype_t objtype, + int acc_type, + int acc_full_type) : + GpiObjHdl(impl, hdl, objtype, false), + m_acc_type(acc_type), + m_acc_full_type(acc_full_type) { } + + FliObjHdl(GpiImplInterface *impl, + void *hdl, + gpi_objtype_t objtype, + int acc_type, + int acc_full_type, + bool is_const) : + GpiObjHdl(impl, hdl, objtype, is_const), + m_acc_type(acc_type), + m_acc_full_type(acc_full_type) { } + + virtual ~FliObjHdl() { } + + virtual int initialise(std::string &name, std::string &fq_name); + + int get_acc_type(void) { return m_acc_type; } + int get_acc_full_type(void) { return m_acc_full_type; } + +protected: + int m_acc_type; + int m_acc_full_type; +}; class FliSignalObjHdl : public GpiSignalObjHdl { public: @@ -148,8 +180,12 @@ class FliSignalObjHdl : public GpiSignalObjHdl { void *hdl, gpi_objtype_t objtype, bool is_const, + int acc_type, + int acc_full_type, bool is_var) : GpiSignalObjHdl(impl, hdl, objtype, is_const), + m_acc_type(acc_type), + m_acc_full_type(acc_full_type), m_is_var(is_var), m_rising_cb(impl, this, GPI_RISING), m_falling_cb(impl, this, GPI_FALLING), @@ -160,7 +196,12 @@ class FliSignalObjHdl : public GpiSignalObjHdl { virtual GpiCbHdl *value_change_cb(unsigned int edge); virtual int initialise(std::string &name, std::string &fq_name); + int get_acc_type(void) { return m_acc_type; } + int get_acc_full_type(void) { return m_acc_full_type; } + protected: + int m_acc_type; + int m_acc_full_type; bool m_is_var; FliSignalCbHdl m_rising_cb; FliSignalCbHdl m_falling_cb; @@ -173,10 +214,12 @@ class FliValueObjHdl : public FliSignalObjHdl { void *hdl, gpi_objtype_t objtype, bool is_const, + int acc_type, + int acc_full_type, bool is_var, mtiTypeIdT valType, mtiTypeKindT typeKind) : - FliSignalObjHdl(impl, hdl, objtype, is_const, is_var), + FliSignalObjHdl(impl, hdl, objtype, is_const, acc_type, acc_full_type, is_var), m_fli_type(typeKind), m_val_type(valType), m_val_buff(NULL) { } @@ -209,10 +252,12 @@ class FliEnumObjHdl : public FliValueObjHdl { void *hdl, gpi_objtype_t objtype, bool is_const, + int acc_type, + int acc_full_type, bool is_var, mtiTypeIdT valType, mtiTypeKindT typeKind) : - FliValueObjHdl(impl, hdl, objtype, is_const, is_var, valType, typeKind), + FliValueObjHdl(impl, hdl, objtype, is_const, acc_type, acc_full_type, is_var, valType, typeKind), m_value_enum(NULL), m_num_enum(0) { } @@ -236,6 +281,8 @@ class FliLogicObjHdl : public FliValueObjHdl { void *hdl, gpi_objtype_t objtype, bool is_const, + int acc_type, + int acc_full_type, bool is_var, mtiTypeIdT valType, mtiTypeKindT typeKind) : @@ -243,6 +290,8 @@ class FliLogicObjHdl : public FliValueObjHdl { hdl, objtype, is_const, + acc_type, + acc_full_type, is_var, valType, typeKind), @@ -279,10 +328,12 @@ class FliIntObjHdl : public FliValueObjHdl { void *hdl, gpi_objtype_t objtype, bool is_const, + int acc_type, + int acc_full_type, bool is_var, mtiTypeIdT valType, mtiTypeKindT typeKind) : - FliValueObjHdl(impl, hdl, objtype, is_const, is_var, valType, typeKind) { } + FliValueObjHdl(impl, hdl, objtype, is_const, acc_type, acc_full_type, is_var, valType, typeKind) { } virtual ~FliIntObjHdl() { } @@ -300,10 +351,12 @@ class FliRealObjHdl : public FliValueObjHdl { void *hdl, gpi_objtype_t objtype, bool is_const, + int acc_type, + int acc_full_type, bool is_var, mtiTypeIdT valType, mtiTypeKindT typeKind) : - FliValueObjHdl(impl, hdl, objtype, is_const, is_var, valType, typeKind), + FliValueObjHdl(impl, hdl, objtype, is_const, acc_type, acc_full_type, is_var, valType, typeKind), m_mti_buff(NULL) { } virtual ~FliRealObjHdl() { @@ -327,10 +380,12 @@ class FliStringObjHdl : public FliValueObjHdl { void *hdl, gpi_objtype_t objtype, bool is_const, + int acc_type, + int acc_full_type, bool is_var, mtiTypeIdT valType, mtiTypeKindT typeKind) : - FliValueObjHdl(impl, hdl, objtype, is_const, is_var, valType, typeKind), + FliValueObjHdl(impl, hdl, objtype, is_const, acc_type, acc_full_type, is_var, valType, typeKind), m_mti_buff(NULL) { } virtual ~FliStringObjHdl() { @@ -423,7 +478,7 @@ class FliImpl : public GpiImplInterface { const char *reason_to_string(int reason); /* Method to provide strings from operation types */ - GpiObjHdl *create_gpi_obj_from_handle(void *hdl, std::string &name, std::string &fq_name); + GpiObjHdl *create_gpi_obj_from_handle(void *hdl, std::string &name, std::string &fq_name, int accType, int accFullType); private: bool isValueConst(int kind); diff --git a/lib/fli/FliObjHdl.cpp b/lib/fli/FliObjHdl.cpp index 9691ef4e..a6bcf4c8 100644 --- a/lib/fli/FliObjHdl.cpp +++ b/lib/fli/FliObjHdl.cpp @@ -29,6 +29,7 @@ #include #include "FliImpl.h" +#include "acc_vhdl.h" GpiCbHdl *FliSignalObjHdl::value_change_cb(unsigned int edge) { @@ -59,6 +60,46 @@ GpiCbHdl *FliSignalObjHdl::value_change_cb(unsigned int edge) return (GpiCbHdl *)cb; } +int FliObjHdl::initialise(std::string &name, std::string &fq_name) +{ + bool is_signal = (get_acc_type() == accSignal || get_acc_full_type() == accAliasSignal); + bool is_value = (is_signal || get_acc_type() == accAlias || + get_acc_type() == accVariable || get_acc_type() == accVHDLConstant || + get_acc_type() == accGeneric); + + switch (get_type()) { + case GPI_STRUCTURE: { + mtiTypeIdT recType; + if (is_signal) { + recType = mti_GetSignalType(get_handle()); + } else { + recType = mti_GetVarType(get_handle()); + } + m_num_elems = mti_GetNumRecordElements(recType); + } + break; + case GPI_MODULE: + if (!is_value) { + m_num_elems = 1; + } else { + mtiTypeIdT arrayType; + if (is_signal) { + arrayType = mti_GetSignalType(get_handle()); + } else { + arrayType = mti_GetVarType(get_handle()); + } + m_num_elems = mti_TickLength(arrayType); + } + break; + default: + LOG_CRITICAL("Invalid object type for FliObjHdl. (%s (%s))", name.c_str(), get_type_str()); + return -1; + } + + return GpiObjHdl::initialise(name, fq_name); +} + + int FliSignalObjHdl::initialise(std::string &name, std::string &fq_name) { return GpiObjHdl::initialise(name, fq_name); From e019d96c8f9c4d0d128de5a2b8b8efa3c909d288 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Sun, 21 Feb 2016 08:04:23 -0700 Subject: [PATCH 1120/2656] Filx 'test_vhdl_access' to expect the correct type for an Enum --- tests/test_cases/test_vhdl_access/test_vhdl_access.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_cases/test_vhdl_access/test_vhdl_access.py b/tests/test_cases/test_vhdl_access/test_vhdl_access.py index 66fba8f2..f703b766 100644 --- a/tests/test_cases/test_vhdl_access/test_vhdl_access.py +++ b/tests/test_cases/test_vhdl_access/test_vhdl_access.py @@ -30,7 +30,7 @@ from cocotb.triggers import Timer from cocotb.result import TestError, TestFailure -@cocotb.test(expect_fail=True) +@cocotb.test() def check_enum_object(dut): """ Enumerations currently behave as normal signals @@ -38,8 +38,8 @@ def check_enum_object(dut): TODO: Implement an EnumObject class and detect valid string mappings """ yield Timer(100) - if not isinstance(dut.inst_ram_ctrl.write_ram_fsm, IntegerObject): - raise TestFailure("Expected the FSM enum to be an InterObject") + if not isinstance(dut.inst_ram_ctrl.write_ram_fsm, ModifiableObject): + raise TestFailure("Expected the FSM enum to be an ModifiableObject") @cocotb.test() def check_objects(dut): From 66a00d9c871fec4963a19c8ef0349924a3e93113 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 22 Feb 2016 14:30:46 -0700 Subject: [PATCH 1121/2656] Fix FLI support for iterating to properly handle records and arrays --- lib/fli/FliImpl.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 3ee19206..375143a9 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -25,6 +25,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ +#include +#include #include #include "FliImpl.h" @@ -765,8 +767,22 @@ GpiIterator::Status FliIterator::next_handle(std::string &name, GpiObjHdl **hdl, std::string fq_name = m_parent->get_fullname(); if (fq_name == "/") { fq_name += name; - } else if (m_parent->get_type() == GPI_STRUCTURE) { - fq_name += "." + name; + } else if (*one2many == FliIterator::OTM_SIGNAL_SUB_ELEMENTS || + *one2many == FliIterator::OTM_VARIABLE_SUB_ELEMENTS) { + std::size_t found; + + if (m_parent->get_type() == GPI_STRUCTURE) { + found = name.find_last_of("."); + } else { + found = name.find_last_of("("); + } + + if (found != std::string::npos) { + fq_name += name.substr(found); + } else { + LOG_WARN("Unhandled Sub-Element Format - %s", name.c_str()); + fq_name += "/" + name; + } } else { fq_name += "/" + name; } From 12441e109408e5443beb96c3cc0c2cd0049b798a Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 22 Feb 2016 14:32:22 -0700 Subject: [PATCH 1122/2656] Fix 'test_iteration' for VHDL to work with SIM=questa --- .../test_cases/test_iteration_vhdl/test_iteration.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/test_cases/test_iteration_vhdl/test_iteration.py b/tests/test_cases/test_iteration_vhdl/test_iteration.py index 83cbf6a0..eced6319 100644 --- a/tests/test_cases/test_iteration_vhdl/test_iteration.py +++ b/tests/test_cases/test_iteration_vhdl/test_iteration.py @@ -34,12 +34,16 @@ def recursive_discovery(dut): """ Recursively discover every single object in the design """ - if cocotb.SIM_NAME in ["ncsim(64)", - "ncsim"]: + if cocotb.SIM_NAME.lower().startswith("ncsim"): # vpiAlways = 31 and vpiStructVar = 2 do not show up in IUS - # But vhpiSimpleSigAssignStmtK objects do, and ther are 2. + # But vhpiSimpleSigAssignStmtK objects do, and ther are 2. # Process statements and all sub handles also show up. pass_total = 35841 + elif cocotb.SIM_NAME.lower().startswith("modelsim"): + # FLI only finds regions, signal, generics, constants, varibles and ports. + # It does not report any procedures + # Ports behave identically to signals, which seems to differ from the vhpi implementation + pass_total = 34562 else: pass_total = 32306 @@ -108,6 +112,6 @@ def test_n_dimension_array(dut): inner_count += 1 outer_count += 1 - if inner_count != 14 and outer_count != 2: + if inner_count != 14 or outer_count != 2: raise TestFailure("dut.inst_ram_ctrl.config should have a total of 14 elems over 2 loops") From 820e4888d7c1abca1193852f396d5199a86b7dcf Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 23 Feb 2016 07:45:05 -0700 Subject: [PATCH 1123/2656] Fix FLI support for mapping indices from python to indices in RTL --- lib/fli/FliImpl.cpp | 32 ++++++++++++++++++++++++++++++-- lib/fli/FliImpl.h | 4 ++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 375143a9..078cf683 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -362,9 +362,37 @@ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) */ GpiObjHdl* FliImpl::native_check_create(uint32_t index, GpiObjHdl *parent) { - if (parent->get_type() == GPI_MODULE or parent->get_type() == GPI_ARRAY) { + if (parent->get_type() == GPI_MODULE || parent->get_type() == GPI_ARRAY || parent->get_type() == GPI_STRING) { char buff[15]; - snprintf(buff, 15, "(%u)", index); + int type; + + if (parent->get_type() == GPI_MODULE) { + FliObjHdl *fli_obj = static_cast(parent); + type = fli_obj->get_acc_full_type(); + } else { + FliSignalObjHdl *fli_obj = static_cast(parent); + type = fli_obj->get_acc_full_type(); + } + + /* To behave like the current VHPI interface, index needs to be properly translated. + * Index of 0 needs to map to "signal'left" and Index of "length-1" mapping to "signal'right" + */ + if (!isTypeValue(type)) { + /* This case would be for indexing into a generate loop. The way that is currently + * handled, this code should never be executed. + */ + snprintf(buff, 15, "(%u)", index); + } else { + FliValueObjHdl *fli_obj = static_cast(parent); + mtiTypeIdT typeId = fli_obj->get_fli_typeid(); + + if (mti_TickDir(typeId) < 0) { + snprintf(buff, 15, "(%u)", mti_TickLeft(typeId) - index); + } else { + snprintf(buff, 15, "(%u)", mti_TickLeft(typeId) + index); + } + } + std::string idx = buff; std::string name = parent->get_name() + idx; std::string fq_name = parent->get_fullname() + idx; diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index a49b3af7..c1f0bc98 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -198,6 +198,7 @@ class FliSignalObjHdl : public GpiSignalObjHdl { int get_acc_type(void) { return m_acc_type; } int get_acc_full_type(void) { return m_acc_full_type; } + bool is_var(void) { return m_is_var; } protected: int m_acc_type; @@ -240,6 +241,9 @@ class FliValueObjHdl : public FliSignalObjHdl { virtual int initialise(std::string &name, std::string &fq_name); + mtiTypeKindT get_fli_typekind(void) { return m_fli_type; } + mtiTypeIdT get_fli_typeid(void) { return m_val_type; } + protected: mtiTypeKindT m_fli_type; mtiTypeIdT m_val_type; From d3cab701b245dda670070c167955b11582906278 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 23 Feb 2016 07:45:46 -0700 Subject: [PATCH 1124/2656] Fix 'test_discovery' to work with SIM=questa --- .../test_cases/test_discovery/test_discovery.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 2ded319e..1f6b0b0d 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -164,7 +164,7 @@ def access_ulogic(dut): tlog = logging.getLogger("cocotb.test") yield Timer(10) constant_integer = dut.stream_in_valid - + @cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) def access_constant_integer(dut): @@ -315,7 +315,7 @@ def skip_a_test(dut): yield Timer(10) @cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"], - expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) + expect_error=cocotb.SIM_NAME.lower().startswith(("icarus"))) def access_gate(dut): """ Test access to a gate Object @@ -341,7 +341,15 @@ def custom_type(dut): new_type = dut.cosLut tlog.info("cosLut object %s %s" % (new_type, type(new_type))) - expected_top = 28 + # FLI only iterates over one dimension at a time, where vhpi will + # iterate over two dimensions at the same time + if cocotb.SIM_NAME.lower().startswith(("modelsim")): + expected_sub = 84 + expected_top = 4 + else: + expected_sub = 11 + expected_top = 28 + count = 0 def _discover(obj): @@ -351,8 +359,6 @@ def _discover(obj): iter_count += _discover(elem) return iter_count - expected_sub = 11 - for sub in new_type: tlog.info("Sub object %s %s" % (sub, type(sub))) sub_count = _discover(sub) From 521bce7c421192b7f692147c50963a2d19aaea13 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 23 Feb 2016 07:55:45 -0700 Subject: [PATCH 1125/2656] Fix 'test_iteration_verilog' to work with all versions of Modelsim/Questa --- .../test_cases/test_iteration_verilog/test_iteration_es.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/test_cases/test_iteration_verilog/test_iteration_es.py b/tests/test_cases/test_iteration_verilog/test_iteration_es.py index 9a599e01..9223eb11 100644 --- a/tests/test_cases/test_iteration_verilog/test_iteration_es.py +++ b/tests/test_cases/test_iteration_verilog/test_iteration_es.py @@ -34,10 +34,8 @@ def recursive_discovery(dut): """ Recursively discover every single object in the design """ - if cocotb.SIM_NAME in ["ModelSim ALTERA STARTER EDITION", - "ModelSim DE", - "ncsim(64)", - "ncsim"]: + if cocotb.SIM_NAME.lower().startswith(("modelsim", + "ncsim")): # vpiAlways does not show up in IUS pass_total = 259 else: From 70d4c8b6c30b36d4ccc0b83aff300612871f84d1 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 23 Feb 2016 08:04:23 -0700 Subject: [PATCH 1126/2656] Fix 'test_iteration_mixedlang' to work with SIM=questa --- .../test_iteration_mixedlang/test_iteration.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/test_cases/test_iteration_mixedlang/test_iteration.py b/tests/test_cases/test_iteration_mixedlang/test_iteration.py index 2463add2..3b109529 100644 --- a/tests/test_cases/test_iteration_mixedlang/test_iteration.py +++ b/tests/test_cases/test_iteration_mixedlang/test_iteration.py @@ -65,12 +65,13 @@ def recursive_discovery(dut): """ Recursively discover every single object in the design """ - if cocotb.SIM_NAME in ["ncsim(64)", - "ncsim"]: + if cocotb.SIM_NAME.lower().startswith(("ncsim")): # vpiAlways = 31 and vpiStructVar = 2 do not show up in IUS - # But vhpiSimpleSigAssignStmtK objects do, and ther are 2. + # But vhpiSimpleSigAssignStmtK objects do, and ther are 2. # Process statements and all sub handles also show up. pass_total = 985 + elif cocotb.SIM_NAME.lower().startswith(("modelsim")): + pass_total = 933 else: pass_total = 916 @@ -94,10 +95,11 @@ def recursive_discovery_boundary(dut): """ Iteration though the boundary works but this just double checks """ - if cocotb.SIM_NAME in ["ncsim(64)", - "ncsim"]: + if cocotb.SIM_NAME.lower().startswith(("ncsim")): # # But vhpiSimpleSigAssignStmtK objects only show up on IUS, and ther are 2 pass_total = 530 + elif cocotb.SIM_NAME.lower().startswith(("modelsim")): + pass_total = 478 else: pass_total = 426 From 73a029eaa116f35c54a96d9528acab3c702967da Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 23 Feb 2016 16:57:20 -0700 Subject: [PATCH 1127/2656] Update FLI startup and shutdown callbacks to behave like VPI/VHPI --- lib/fli/FliCbHdl.cpp | 61 +++++++++++++++++++ lib/fli/FliImpl.cpp | 141 +++++++++++++++++++++++-------------------- lib/fli/FliImpl.h | 19 ++++-- 3 files changed, 150 insertions(+), 71 deletions(-) diff --git a/lib/fli/FliCbHdl.cpp b/lib/fli/FliCbHdl.cpp index 5b965608..02e4b8de 100644 --- a/lib/fli/FliCbHdl.cpp +++ b/lib/fli/FliCbHdl.cpp @@ -126,3 +126,64 @@ FliSignalCbHdl::FliSignalCbHdl(GpiImplInterface *impl, m_sig_hdl = m_signal->get_handle(); } +int FliStartupCbHdl::arm_callback(void) +{ + mti_AddLoadDoneCB(handle_fli_callback,(void *)this); + set_call_state(GPI_PRIMED); + + return 0; +} + +int FliStartupCbHdl::run_callback(void) +{ + gpi_sim_info_t sim_info; + + char *c_info = mti_GetProductVersion(); // Returned pointer must not be freed + std::string info = c_info; + std::string search = " Version "; + std::size_t found = info.find(search); + + std::string product_str = c_info; + std::string version_str = c_info; + + if (found != std::string::npos) { + product_str = info.substr(0,found); + version_str = info.substr(found+search.length()); + + LOG_DEBUG("Found Version string at %d", found); + LOG_DEBUG(" product: %s", product_str.c_str()); + LOG_DEBUG(" version: %s", version_str.c_str()); + } + + std::vector product(product_str.begin(), product_str.end()); + std::vector version(version_str.begin(), version_str.end()); + product.push_back('\0'); + version.push_back('\0'); + + + // copy in sim_info.product + sim_info.argc = 0; + sim_info.argv = NULL; + sim_info.product = &product[0]; + sim_info.version = &version[0]; + + gpi_embed_init(&sim_info); + + return 0; +} + +int FliShutdownCbHdl::arm_callback(void) +{ + mti_AddQuitCB(handle_fli_callback,(void *)this); + set_call_state(GPI_PRIMED); + + return 0; +} + +int FliShutdownCbHdl::run_callback(void) +{ + gpi_embed_end(); + + return 0; +} + diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 078cf683..ec7e78b1 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -35,78 +35,19 @@ #include "acc_user.h" extern "C" { - -static FliImpl *fli_table; - -void fli_elab_cb(void *nothing) -{ - LOG_INFO("fli_elab_cb called\n"); - - fli_table = new FliImpl("FLI"); - gpi_register_impl(fli_table); - gpi_load_extra_libs(); - - // Elaboration has already happened so jump straight in! - gpi_sim_info_t sim_info; - - char *version = mti_GetProductVersion(); // Returned pointer must not be freed - - // copy in sim_info.product - // FIXME split product and version from returned string? - sim_info.argc = 0; - sim_info.argv = NULL; - sim_info.product = version; - sim_info.version = version; - - gpi_embed_init(&sim_info); -} - -void cocotb_init(void) -{ - LOG_INFO("cocotb_init called\n"); - mti_AddLoadDoneCB(fli_elab_cb, NULL); +static FliProcessCbHdl *sim_init_cb; +static FliProcessCbHdl *sim_finish_cb; +static FliImpl *fli_table; } - - -// Main re-entry point for callbacks from simulator -void handle_fli_callback(void *data) -{ - fflush(stderr); - - FliProcessCbHdl *cb_hdl = (FliProcessCbHdl*)data; - - if (!cb_hdl) { - LOG_CRITICAL("FLI: Callback data corrupted: ABORTING"); - } - - gpi_cb_state_e old_state = cb_hdl->get_call_state(); - - if (old_state == GPI_PRIMED) { - - cb_hdl->set_call_state(GPI_CALL); - - cb_hdl->run_callback(); - gpi_cb_state_e new_state = cb_hdl->get_call_state(); - - /* We have re-primed in the handler */ - if (new_state != GPI_PRIMED) - if (cb_hdl->cleanup_callback()) - delete cb_hdl; - } else { - /* Issue #188 seems to appear via FLI as well */ - cb_hdl->cleanup_callback(); - } -}; - -} // extern "C" - - void FliImpl::sim_end(void) { const char *stop = "stop"; + if (GPI_DELETE != sim_finish_cb->get_call_state()) { + sim_finish_cb->set_call_state(GPI_DELETE); + mti_Cmd(stop); + } - mti_Cmd(stop); } bool FliImpl::isValueConst(int kind) @@ -915,5 +856,71 @@ void FliTimerCache::put_timer(FliTimedCbHdl* hdl) free_list.push(hdl); } -GPI_ENTRY_POINT(fli, cocotb_init); +extern "C" { + +// Main re-entry point for callbacks from simulator +void handle_fli_callback(void *data) +{ + fflush(stderr); + + FliProcessCbHdl *cb_hdl = (FliProcessCbHdl*)data; + + if (!cb_hdl) { + LOG_CRITICAL("FLI: Callback data corrupted: ABORTING"); + } + + gpi_cb_state_e old_state = cb_hdl->get_call_state(); + + if (old_state == GPI_PRIMED) { + + cb_hdl->set_call_state(GPI_CALL); + + cb_hdl->run_callback(); + gpi_cb_state_e new_state = cb_hdl->get_call_state(); + + /* We have re-primed in the handler */ + if (new_state != GPI_PRIMED) + if (cb_hdl->cleanup_callback()) + delete cb_hdl; + } else { + /* Issue #188 seems to appear via FLI as well */ + cb_hdl->cleanup_callback(); + } +}; + +static void register_initial_callback(void) +{ + FENTER + sim_init_cb = new FliStartupCbHdl(fli_table); + sim_init_cb->arm_callback(); + FEXIT +} + +static void register_final_callback(void) +{ + FENTER + sim_finish_cb = new FliShutdownCbHdl(fli_table); + sim_finish_cb->arm_callback(); + FEXIT +} + +static void register_embed(void) +{ + fli_table = new FliImpl("FLI"); + gpi_register_impl(fli_table); + gpi_load_extra_libs(); +} + + +void cocotb_init(void) +{ + LOG_INFO("cocotb_init called\n"); + register_embed(); + register_initial_callback(); + register_final_callback(); +} + +} // extern "C" + +GPI_ENTRY_POINT(fli, register_embed); diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index c1f0bc98..8596af98 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -35,7 +35,6 @@ #include extern "C" { -void fli_elab_cb(void *nothing); void cocotb_init(void); void handle_fli_callback(void *data); } @@ -117,12 +116,24 @@ class FliReadOnlyCbHdl : public FliSimPhaseCbHdl { virtual ~FliReadOnlyCbHdl() { } }; -class FliShutdownCbHdl : public GpiCbHdl { +class FliStartupCbHdl : public FliProcessCbHdl { public: - FliShutdownCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl) { } - int run_callback(void); + FliStartupCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + FliProcessCbHdl(impl) { } + virtual ~FliStartupCbHdl() { } + int arm_callback(void); + int run_callback(void); +}; + +class FliShutdownCbHdl : public FliProcessCbHdl { +public: + FliShutdownCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), + FliProcessCbHdl(impl) { } virtual ~FliShutdownCbHdl() { } + + int arm_callback(void); + int run_callback(void); }; class FliTimedCbHdl : public FliProcessCbHdl { From 379763fb097acdc98288c52dddc38d06370cbf9d Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Wed, 24 Feb 2016 13:43:26 -0700 Subject: [PATCH 1128/2656] Fix FLI issue with getting handle by index causing a bad handle exception --- lib/fli/FliImpl.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index ec7e78b1..5e07cf9f 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -176,11 +176,11 @@ GpiObjHdl *FliImpl::create_gpi_obj_from_handle(void *hdl, std::string &name, std } else if (isValueChar(elemType)) { new_obj = new FliStringObjHdl(this, hdl, GPI_STRING, is_const, accType, accFullType, is_var, valType, typeKind); } else { - new_obj = new FliObjHdl(this, hdl, GPI_MODULE, accType, accFullType); // array of enums + new_obj = new FliValueObjHdl(this, hdl, GPI_MODULE, is_const, accType, accFullType, is_var, valType, typeKind); // array of enums } break; default: - new_obj = new FliObjHdl(this, hdl, GPI_MODULE, accType, accFullType);// array of (array, Integer, Real, Record, etc.) + new_obj = new FliValueObjHdl(this, hdl, GPI_MODULE, is_const, accType, accFullType, is_var, valType, typeKind);// array of (array, Integer, Real, Record, etc.) } } break; @@ -307,12 +307,14 @@ GpiObjHdl* FliImpl::native_check_create(uint32_t index, GpiObjHdl *parent) char buff[15]; int type; + LOG_DEBUG("Looking for index %u from %s", index, parent->get_name_str()); + if (parent->get_type() == GPI_MODULE) { FliObjHdl *fli_obj = static_cast(parent); - type = fli_obj->get_acc_full_type(); + type = fli_obj->get_acc_type(); } else { FliSignalObjHdl *fli_obj = static_cast(parent); - type = fli_obj->get_acc_full_type(); + type = fli_obj->get_acc_type(); } /* To behave like the current VHPI interface, index needs to be properly translated. @@ -338,8 +340,6 @@ GpiObjHdl* FliImpl::native_check_create(uint32_t index, GpiObjHdl *parent) std::string name = parent->get_name() + idx; std::string fq_name = parent->get_fullname() + idx; - LOG_DEBUG("Looking for index %u from %s", index, parent->get_name_str()); - std::vector writable(fq_name.begin(), fq_name.end()); writable.push_back('\0'); From 8f43c63511052b9969320916dc35f20ae52eedb7 Mon Sep 17 00:00:00 2001 From: John Peck Date: Mon, 29 Feb 2016 11:33:11 -0800 Subject: [PATCH 1129/2656] Update quickstart.rst --- documentation/source/quickstart.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index fe16925f..bfdc51d5 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -34,7 +34,7 @@ Redhat based installation .. code-block:: bash - $> sudo yum install gcc gcc-c++ libstdc++-devel python-devel + $> sudo yum install gcc gcc-c++ libstdc++-devel swig python-devel This will allow building of the Cocotb libs for use with a 64 bit native simulator. If a 32 bit simulator is being used then additional steps to install 32bit development libraries and python are needed. From a151a48903c81e729e6d49b0de7a9530bf3c9fbd Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Wed, 2 Mar 2016 14:45:20 +1100 Subject: [PATCH 1130/2656] binary : superfluous assignment of _binstr --- cocotb/binary.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 4abea0b7..8505e0c8 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -475,7 +475,6 @@ def __getitem__(self, key): index = key if index > self._bits - 1: raise IndexError('Index greater than number of bits.') - _binstr = self.binstr[index] if self.big_endian: _binstr = self.binstr[index] else: From aac9b8230ba444dab2f7a0876716c4b1c0fe205a Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Wed, 2 Mar 2016 14:48:05 +0000 Subject: [PATCH 1131/2656] Issue #382: Use a dediciated get_precision function and store in the SimLogFormatter for use later --- cocotb/log.py | 14 ++++++++------ include/gpi.h | 5 +++-- lib/fli/FliImpl.h | 3 ++- lib/gpi/GpiCommon.cpp | 9 +++++++-- lib/gpi/gpi_priv.h | 3 ++- lib/simulator/simulatormodule.c | 25 +++++++++++++++++++------ lib/simulator/simulatormodule.h | 4 +++- lib/vhpi/VhpiImpl.cpp | 10 ++++++++-- lib/vhpi/VhpiImpl.h | 3 ++- lib/vpi/VpiImpl.cpp | 7 ++++++- lib/vpi/VpiImpl.h | 3 ++- 11 files changed, 62 insertions(+), 24 deletions(-) diff --git a/cocotb/log.py b/cocotb/log.py index f214eff0..823f7685 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -148,6 +148,8 @@ def __getattr__(self, attribute): class SimLogFormatter(logging.Formatter): """Log formatter to provide consistent log message handling.""" + def __init__(self): + self._precision = simulator.get_precision() # Justify and truncate @staticmethod @@ -162,8 +164,8 @@ def rjust(string, chars): return ".." + string[(chars - 2) * -1:] return string.rjust(chars) - def _format(self, timeh, timel, precision, level, record, msg): - time_ns = (timeh << 32 | timel) * (10.0**precision) / 1e-9 + def _format(self, timeh, timel, level, record, msg): + time_ns = (timeh << 32 | timel) * (10.0**self._precision) / 1e-9 simtime = "%6.2fns" % (time_ns) prefix = simtime + ' ' + level + ' ' + \ self.ljust(record.name, _RECORD_CHARS) + \ @@ -183,9 +185,9 @@ def format(self, record): msg = str(msg) level = record.levelname.ljust(_LEVEL_CHARS) - timeh, timel, precision = simulator.get_sim_time() + timeh, timel = simulator.get_sim_time() - return self._format(timeh, timel, precision, level, record, msg) + return self._format(timeh, timel, level, record, msg) class SimColourLogFormatter(SimLogFormatter): @@ -211,5 +213,5 @@ def format(self, record): level = (SimColourLogFormatter.loglevel2colour[record.levelno] % record.levelname.ljust(_LEVEL_CHARS)) - timeh, timel, precision = simulator.get_sim_time() - return self._format(timeh, timel, precision, level, record, msg) + timeh, timel = simulator.get_sim_time() + return self._format(timeh, timel, level, record, msg) diff --git a/include/gpi.h b/include/gpi.h index 0ae16766..383b68fc 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -109,8 +109,9 @@ typedef void * gpi_iterator_hdl; void gpi_sim_end(void); -// Returns simulation time as a float. Units are default sim units -void gpi_get_sim_time(uint32_t *high, uint32_t *low, int32_t *precision); +// Returns simulation time as two uints. Units are default sim units +void gpi_get_sim_time(uint32_t *high, uint32_t *low); +void gpi_get_sim_precision(int32_t *precision); // Functions for extracting a gpi_sim_hdl to an object diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index d8eba22f..5d79c63b 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -473,7 +473,8 @@ class FliImpl : public GpiImplInterface { /* Sim related */ void sim_end(void); - void get_sim_time(uint32_t *high, uint32_t *low, int32_t *precision); + void get_sim_time(uint32_t *high, uint32_t *low); + void get_sim_precision(int32_t *precision); /* Hierachy related */ GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 7c17a09d..4a4e83f2 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -196,9 +196,14 @@ void gpi_load_extra_libs(void) gpi_print_registered_impl(); } -void gpi_get_sim_time(uint32_t *high, uint32_t *low, int32_t *precision) +void gpi_get_sim_time(uint32_t *high, uint32_t *low) { - registered_impls[0]->get_sim_time(high, low, precision); + registered_impls[0]->get_sim_time(high, low); +} + +void gpi_get_sim_precision(int32_t *precision) +{ + registered_impls[0]->get_sim_precision(precision); } gpi_sim_hdl gpi_get_root_handle(const char *name) diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index f84658b2..200b9b0e 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -287,7 +287,8 @@ class GpiImplInterface { /* Sim related */ virtual void sim_end(void) = 0; - virtual void get_sim_time(uint32_t *high, uint32_t *low, int32_t *precision) = 0; + virtual void get_sim_time(uint32_t *high, uint32_t *low) = 0; + virtual void get_sim_precision(int32_t *precision) = 0; /* Hierachy related */ virtual GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent) = 0; diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 097f7528..ab6c477b 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -848,30 +848,43 @@ static PyObject *get_type_string(PyObject *self, PyObject *args) } -// Returns a high, low, precision tuple of simulator time +// Returns a high, low, tuple of simulator time // Note we can never log from this function since the logging mechanism calls this to annotate // log messages with the current simulation time static PyObject *get_sim_time(PyObject *self, PyObject *args) { - uint32_t high, low; - int32_t precision; PyGILState_STATE gstate; gstate = TAKE_GIL(); - gpi_get_sim_time(&high, &low, &precision); + gpi_get_sim_time(&high, &low); - PyObject *pTuple = PyTuple_New(3); + PyObject *pTuple = PyTuple_New(2); PyTuple_SetItem(pTuple, 0, PyLong_FromUnsignedLong(high)); // Note: This function “steals†a reference to o. PyTuple_SetItem(pTuple, 1, PyLong_FromUnsignedLong(low)); // Note: This function “steals†a reference to o. - PyTuple_SetItem(pTuple, 2, PyLong_FromLong(precision)); // Note: This function “steals†a reference to o. DROP_GIL(gstate); return pTuple; } +static PyObject *get_precision(PyObject *self, PyObject *args) +{ + int32_t precision; + + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + gpi_get_sim_precision(&precision); + + PyObject *retint = Py_BuildValue("i", precision); + + DROP_GIL(gstate); + + return retint; +} + static PyObject *get_num_elems(PyObject *self, PyObject *args) { gpi_sim_hdl hdl; diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 795b2dad..9e8b4496 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -83,6 +83,7 @@ static PyObject *iterate(PyObject *self, PyObject *args); static PyObject *next(PyObject *self, PyObject *args); static PyObject *get_sim_time(PyObject *self, PyObject *args); +static PyObject *get_precision(PyObject *self, PyObject *args); static PyObject *deregister_callback(PyObject *self, PyObject *args); static PyObject *log_level(PyObject *self, PyObject *args); @@ -115,7 +116,8 @@ static PyMethodDef SimulatorMethods[] = { {"log_level", log_level, METH_VARARGS, "Set the log level for GPI"}, // FIXME METH_NOARGS => initialization from incompatible pointer type - {"get_sim_time", get_sim_time, METH_VARARGS, "Get the current simulation time as a float"}, + {"get_sim_time", get_sim_time, METH_VARARGS, "Get the current simulation time as an int tuple"}, + {"get_precision", get_precision, METH_VARARGS, "Get the precision of the simualator"}, {"deregister_callback", deregister_callback, METH_VARARGS, "Deregister a callback"}, {"error_out", (PyCFunction)error_out, METH_NOARGS, NULL}, diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 000b6e9e..09f95003 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -103,14 +103,20 @@ const char *VhpiImpl::reason_to_string(int reason) } } -void VhpiImpl::get_sim_time(uint32_t *high, uint32_t *low, int32_t *precision) +void VhpiImpl::get_sim_time(uint32_t *high, uint32_t *low) { vhpiTimeT vhpi_time_s; vhpi_get_time(&vhpi_time_s, NULL); check_vhpi_error(); *high = vhpi_time_s.high; *low = vhpi_time_s.low; - *precision = 0; +} + +void VhpiImpl::get_sim_precision(int32_t *precision) +{ + vhpiPhysT prec = vhpi_get_phys(vhpiResolutionLimitP, NULL); + printf("high = %u, low= %u\n", prec.high, prec.low); + *precision = prec.low; } // Determine whether a VHPI object type is a constant or not diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 4f3ea2d2..23c81911 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -221,7 +221,8 @@ class VhpiImpl : public GpiImplInterface { /* Sim related */ void sim_end(void); - void get_sim_time(uint32_t *high, uint32_t *low, int32_t *precision); + void get_sim_time(uint32_t *high, uint32_t *low); + void get_sim_precision(int32_t *precision); /* Hierachy related */ GpiObjHdl *get_root_handle(const char *name); diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 2349181e..1e1f76d0 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -59,7 +59,7 @@ const char *VpiImpl::reason_to_string(int reason) } } -void VpiImpl::get_sim_time(uint32_t *high, uint32_t *low, int32_t *precision) +void VpiImpl::get_sim_time(uint32_t *high, uint32_t *low) { s_vpi_time vpi_time_s; vpi_time_s.type = vpiSimTime; //vpiSimTime; @@ -67,7 +67,12 @@ void VpiImpl::get_sim_time(uint32_t *high, uint32_t *low, int32_t *precision) check_vpi_error(); *high = vpi_time_s.high; *low = vpi_time_s.low; +} + +void VpiImpl::get_sim_precision(int32_t *precision) +{ *precision = vpi_get(vpiTimePrecision, NULL); + check_vpi_error(); } gpi_objtype_t to_gpi_objtype(int32_t vpitype) diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index d6ecbcc2..dbf25c6d 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -251,7 +251,8 @@ class VpiImpl : public GpiImplInterface { /* Sim related */ void sim_end(void); - void get_sim_time(uint32_t *high, uint32_t *low, int32_t *precision); + void get_sim_time(uint32_t *high, uint32_t *low); + void get_sim_precision(int32_t *precision); /* Hierachy related */ GpiObjHdl *get_root_handle(const char *name); From bd9d997d5abf84c9fcb315cca9b44f5565672386 Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 2 Mar 2016 20:27:48 +0000 Subject: [PATCH 1132/2656] Fixe #398: Ensure it's still possible to access constants by the .value member --- cocotb/handle.py | 4 ++++ lib/vpi/VpiImpl.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 102bc9e6..93a60e59 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -343,6 +343,10 @@ def __ne__(self, other): def __repr__(self): return str(self._value) + @property + def value(self): + return self._value + def _setcachedvalue(self, *args, **kwargs): raise ValueError("Not permissible to set values on a constant object") diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 1c8fb5ca..c189df70 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -251,7 +251,7 @@ GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) new_hdl = vpi_handle_by_index(vpi_hdl, index); if (new_hdl == NULL) { - LOG_DEBUG("Unable to vpi_get_handle_by_index %u", index); + LOG_DEBUG("Unable to vpi_get_handle_by_index %s[%u]", vpi_get_str(vpiName, vpi_hdl), index); return NULL; } From 307d35f451472935743a88988796f7809bc1d98d Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 4 Mar 2016 07:33:56 +0000 Subject: [PATCH 1133/2656] Cleanup: Consistently define COCOTB_SIM macro for Mentor simulators --- makefiles/simulators/Makefile.questa | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 0c6e66ca..19aa9913 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -73,7 +73,7 @@ ifneq ($(VHDL_SOURCES),) echo "vcom -work $(RTL_LIBRARY) $(VCOM_ARGS) $(VHDL_SOURCES)" >> $@ endif ifneq ($(VERILOG_SOURCES),) - echo "vlog -work $(RTL_LIBRARY) -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(VERILOG_SOURCES)" >> $@ + echo "vlog -work $(RTL_LIBRARY) +define+COCOTB_SIM -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(VERILOG_SOURCES)" >> $@ endif echo "vsim $(VSIM_ARGS) $(RTL_LIBRARY).$(TOPLEVEL)" >> $@ ifeq ($(WAVES),1) From 903050143e15024a263c2973cda277d462cf3006 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 4 Mar 2016 09:40:53 +0000 Subject: [PATCH 1134/2656] Issue #382:: FLI fixups --- lib/fli/FliImpl.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 83f6f7d4..ce151555 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -388,10 +388,14 @@ const char *FliImpl::reason_to_string(int reason) * * NB units depend on the simulation configuration */ -void FliImpl::get_sim_time(uint32_t *high, uint32_t *low, int32_t *precision) +void FliImpl::get_sim_time(uint32_t *high, uint32_t *low) { *high = mti_NowUpper(); *low = mti_Now(); +} + +void FliImpl::get_sim_precision(int32_t *precision) +{ *precision = 0; } From f47629c21f69b65e1965b2c14d19382a8ec69445 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 4 Mar 2016 15:35:08 +0000 Subject: [PATCH 1135/2656] Issue #382: Align all the layers, clamp the value, make static in SimLogFormatter --- cocotb/log.py | 5 ++--- lib/fli/FliImpl.cpp | 2 +- lib/gpi/GpiCommon.cpp | 12 +++++++++++- lib/vhpi/VhpiImpl.cpp | 7 +++++-- lib/vpi/VpiImpl.cpp | 1 - 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/cocotb/log.py b/cocotb/log.py index 823f7685..1a2c1496 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -147,9 +147,8 @@ def __getattr__(self, attribute): class SimLogFormatter(logging.Formatter): + sim_precision = simulator.get_precision() """Log formatter to provide consistent log message handling.""" - def __init__(self): - self._precision = simulator.get_precision() # Justify and truncate @staticmethod @@ -165,7 +164,7 @@ def rjust(string, chars): return string.rjust(chars) def _format(self, timeh, timel, level, record, msg): - time_ns = (timeh << 32 | timel) * (10.0**self._precision) / 1e-9 + time_ns = (timeh << 32 | timel) * (10.0**SimLogFormatter.sim_precision) / 1e-9 simtime = "%6.2fns" % (time_ns) prefix = simtime + ' ' + level + ' ' + \ self.ljust(record.name, _RECORD_CHARS) + \ diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index ce151555..6272683a 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -396,7 +396,7 @@ void FliImpl::get_sim_time(uint32_t *high, uint32_t *low) void FliImpl::get_sim_precision(int32_t *precision) { - *precision = 0; + *precision = mti_getResolutionLimit();; } /** diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 4a4e83f2..3a5b0775 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -203,7 +203,17 @@ void gpi_get_sim_time(uint32_t *high, uint32_t *low) void gpi_get_sim_precision(int32_t *precision) { - registered_impls[0]->get_sim_precision(precision); + /* We clamp to sensible values here, 1e-15 min and 1e3 max */ + int32_t val; + registered_impls[0]->get_sim_precision(&val); + if (val > 2 ) + val = 2; + + if (val < -15) + val = -15; + + *precision = val; + } gpi_sim_hdl gpi_get_root_handle(const char *name) diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 09f95003..e98fb3ab 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -26,6 +26,7 @@ ******************************************************************************/ #include "VhpiImpl.h" +#include #include extern "C" { @@ -114,9 +115,11 @@ void VhpiImpl::get_sim_time(uint32_t *high, uint32_t *low) void VhpiImpl::get_sim_precision(int32_t *precision) { + /* The value returned is in number of femtoseconds */ vhpiPhysT prec = vhpi_get_phys(vhpiResolutionLimitP, NULL); - printf("high = %u, low= %u\n", prec.high, prec.low); - *precision = prec.low; + uint64_t femtoseconds = ((uint64_t)prec.high << 32) | prec.low; + double base = 1e-15 * femtoseconds; + *precision = (int32_t)log10(base); } // Determine whether a VHPI object type is a constant or not diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 1e1f76d0..65103d71 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -72,7 +72,6 @@ void VpiImpl::get_sim_time(uint32_t *high, uint32_t *low) void VpiImpl::get_sim_precision(int32_t *precision) { *precision = vpi_get(vpiTimePrecision, NULL); - check_vpi_error(); } gpi_objtype_t to_gpi_objtype(int32_t vpitype) From a67997a5c49297de80055a41b728d097e91aff90 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 4 Mar 2016 15:37:35 +0000 Subject: [PATCH 1136/2656] Issue #382: Use the correct function name --- lib/fli/FliImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 6272683a..0427f817 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -396,7 +396,7 @@ void FliImpl::get_sim_time(uint32_t *high, uint32_t *low) void FliImpl::get_sim_precision(int32_t *precision) { - *precision = mti_getResolutionLimit();; + *precision = mti_GetResolutionLimit(); } /** From 62d94788dbd927ca879c6b5f590a096ede2ccba2 Mon Sep 17 00:00:00 2001 From: Steve Murphy Date: Mon, 7 Mar 2016 18:01:13 +0000 Subject: [PATCH 1137/2656] strict type compare should print repr rather than hexdump --- cocotb/scoreboard.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index bf5bc2fa..59ed8931 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -114,14 +114,20 @@ def compare(self, got, exp, log, strict_type=True): strgot, strexp = str(got), str(exp) log.error("Received transaction differed from expected output") - log.info("Expected:\n" + hexdump(strexp)) + if not strict_type: + log.info("Expected:\n" + hexdump(strexp)) + else: + log.info("Expected:\n" + repr(exp)) if not isinstance(exp, str): try: for word in exp: log.info(str(word)) except: pass - log.info("Received:\n" + hexdump(strgot)) + if not strict_type: + log.info("Received:\n" + hexdump(strgot)) + else: + log.info("Received:\n" + repr(got)) if not isinstance(got, str): try: for word in got: From b1e7721b57be29453cfe408d230de2ef87ff0321 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Fri, 4 Mar 2016 09:28:06 +1100 Subject: [PATCH 1138/2656] build fli libs under windows --- makefiles/simulators/Makefile.questa | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 19aa9913..a04b2c43 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -110,7 +110,7 @@ OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ LIB_LOAD := PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) NEW_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-zA-Z]\):\//\/\1\//g' -e 's/;/:/g') -INT_LIBS := $(COCOTB_VPI_LIB) +INT_LIBS := $(COCOTB_VPI_LIB) $(COCOTB_FLI_LIB) else LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) NEW_PYTHONPATH := $(PYTHONPATH) From c8bdb8df877bba5e7f7ff10f721801775f3cc8e6 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Fri, 4 Mar 2016 09:32:10 +1100 Subject: [PATCH 1139/2656] Ensure that command line params are written to runsim.do correctly. --- makefiles/simulators/Makefile.questa | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index a04b2c43..0034616c 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -66,7 +66,7 @@ ifneq ($(GENERICS),) VSIM_ARGS += $(foreach gen, $(GENERICS),"-G $(gen)") endif -$(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) | $(SIM_BUILD) +$(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) $(SIM_BUILD) echo "if [file exists $(RTL_LIBRARY)] {vdel -lib $(RTL_LIBRARY) -all}" > $@ echo "vlib $(RTL_LIBRARY)" >> $@ ifneq ($(VHDL_SOURCES),) From cddf8680842608d1c1b769124df6399566084963 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Fri, 4 Mar 2016 09:56:24 +1100 Subject: [PATCH 1140/2656] enable running tests under windows --- tests/designs/avalon_module/Makefile | 5 ++--- tests/designs/close_module/Makefile | 4 ++-- tests/designs/plusargs_module/Makefile | 9 ++++----- tests/designs/sample_module/Makefile | 5 ++--- tests/designs/uart2bus/Makefile | 10 ++++++++-- tests/designs/vhdl_configurations/Makefile | 10 ++++++++-- tests/designs/viterbi_decoder_axi4s/Makefile | 9 +++++++-- tests/test_cases/test_iteration_verilog/Makefile | 9 +++++++-- 8 files changed, 40 insertions(+), 21 deletions(-) diff --git a/tests/designs/avalon_module/Makefile b/tests/designs/avalon_module/Makefile index 16ec3103..6bc7ff37 100644 --- a/tests/designs/avalon_module/Makefile +++ b/tests/designs/avalon_module/Makefile @@ -39,15 +39,14 @@ else TOPLEVEL := burst_read_master -PWD=$(shell pwd) -COCOTB=$(PWD)/../../.. - ifeq ($(OS),Msys) WPWD=$(shell sh -c 'pwd -W') else WPWD=$(shell pwd) endif +COCOTB?=$(WPWD)/../../.. + VERILOG_SOURCES = $(COCOTB)/tests/designs/avalon_module/burst_read_master.v include $(COCOTB)/makefiles/Makefile.inc diff --git a/tests/designs/close_module/Makefile b/tests/designs/close_module/Makefile index cb40a95f..e001506b 100644 --- a/tests/designs/close_module/Makefile +++ b/tests/designs/close_module/Makefile @@ -45,8 +45,8 @@ else WPWD=$(shell pwd) endif -PWD=$(shell pwd) -COCOTB=$(PWD)/../../.. +COCOTB?=$(WPWD)/../../.. + VERILOG_SOURCES = $(COCOTB)/tests/designs/close_module/close_module.v ifneq ($(SIM),vcs) diff --git a/tests/designs/plusargs_module/Makefile b/tests/designs/plusargs_module/Makefile index 44cbb101..7a795e6d 100644 --- a/tests/designs/plusargs_module/Makefile +++ b/tests/designs/plusargs_module/Makefile @@ -39,15 +39,14 @@ else TOPLEVEL = tb_top -PWD=$(shell pwd) -COCOTB=$(PWD)/../../.. - ifeq ($(OS),Msys) -PWD=$(shell sh -c 'pwd -W') +WPWD=$(shell sh -c 'pwd -W') else -PWD=$(shell pwd) +WPWD=$(shell pwd) endif +COCOTB?=$(WPWD)/../../.. + VERILOG_SOURCES = $(COCOTB)/tests/designs/plusargs_module/tb_top.v include $(COCOTB)/makefiles/Makefile.inc diff --git a/tests/designs/sample_module/Makefile b/tests/designs/sample_module/Makefile index 8d01c734..c7491b29 100644 --- a/tests/designs/sample_module/Makefile +++ b/tests/designs/sample_module/Makefile @@ -31,15 +31,14 @@ TOPLEVEL_LANG ?= verilog TOPLEVEL := sample_module -PWD=$(shell pwd) -COCOTB?=$(PWD)/../../.. - ifeq ($(OS),Msys) WPWD=$(shell sh -c 'pwd -W') else WPWD=$(shell pwd) endif +COCOTB?=$(WPWD)/../../.. + ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES = $(COCOTB)/tests/designs/sample_module/sample_module.v else ifeq ($(TOPLEVEL_LANG),vhdl) diff --git a/tests/designs/uart2bus/Makefile b/tests/designs/uart2bus/Makefile index a9c68fa2..befd94d5 100644 --- a/tests/designs/uart2bus/Makefile +++ b/tests/designs/uart2bus/Makefile @@ -8,8 +8,14 @@ clean:: else -PWD=$(shell pwd) -COCOTB?=$(PWD)/../../.. +ifeq ($(OS),Msys) +WPWD=$(shell sh -c 'pwd -W') +else +WPWD=$(shell pwd) +endif + +COCOTB?=$(WPWD)/../../.. + SRC_BASE = $(COCOTB)/tests/designs/uart2bus VHDL_SOURCES = $(SRC_BASE)/vhdl/uart2BusTop_pkg.vhd \ diff --git a/tests/designs/vhdl_configurations/Makefile b/tests/designs/vhdl_configurations/Makefile index 6aff5c63..8822bcf1 100644 --- a/tests/designs/vhdl_configurations/Makefile +++ b/tests/designs/vhdl_configurations/Makefile @@ -16,8 +16,14 @@ else TOPLEVEL = testbench -PWD=$(shell pwd) -COCOTB?=$(PWD)/../../.. +ifeq ($(OS),Msys) +WPWD=$(shell sh -c 'pwd -W') +else +WPWD=$(shell pwd) +endif + +COCOTB?=$(WPWD)/../../.. + VHDL_SOURCES = $(COCOTB)/tests/designs/vhdl_configurations/dut.vhd ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES = $(COCOTB)/tests/designs/vhdl_configurations/testbench.sv diff --git a/tests/designs/viterbi_decoder_axi4s/Makefile b/tests/designs/viterbi_decoder_axi4s/Makefile index 3bc078b0..36f9fca6 100644 --- a/tests/designs/viterbi_decoder_axi4s/Makefile +++ b/tests/designs/viterbi_decoder_axi4s/Makefile @@ -37,8 +37,13 @@ else TOPLEVEL = dec_viterbi -PWD=$(shell pwd) -COCOTB?=$(PWD)/../../.. +ifeq ($(OS),Msys) +WPWD=$(shell sh -c 'pwd -W') +else +WPWD=$(shell pwd) +endif + +COCOTB?=$(WPWD)/../../.. SRC_BASE = $(COCOTB)/tests/designs/viterbi_decoder_axi4s diff --git a/tests/test_cases/test_iteration_verilog/Makefile b/tests/test_cases/test_iteration_verilog/Makefile index 3df0c8c3..44370847 100644 --- a/tests/test_cases/test_iteration_verilog/Makefile +++ b/tests/test_cases/test_iteration_verilog/Makefile @@ -35,8 +35,13 @@ clean:: else -PWD := $(shell pwd) -COCOTB := $(PWD)/../../.. +ifeq ($(OS),Msys) +WPWD=$(shell sh -c 'pwd -W') +else +WPWD=$(shell pwd) +endif + +COCOTB?=$(WPWD)/../../.. VERILOG_SOURCES = $(COCOTB)/examples/endian_swapper/hdl/endian_swapper.sv TOPLEVEL = endian_swapper_sv From c7c0f6ac586235a5080a0f5143e509b5aa26c577 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Tue, 8 Mar 2016 18:44:08 +1100 Subject: [PATCH 1141/2656] Add VHDL version of adder example and add DATA_WIDTH parameter to verilog version. --- examples/adder/hdl/adder.v | 11 ++++++++--- examples/adder/hdl/adder.vhdl | 24 ++++++++++++++++++++++++ examples/adder/tests/Makefile | 18 +++++++----------- 3 files changed, 39 insertions(+), 14 deletions(-) create mode 100644 examples/adder/hdl/adder.vhdl diff --git a/examples/adder/hdl/adder.v b/examples/adder/hdl/adder.v index deb8eff1..6082e77b 100644 --- a/examples/adder/hdl/adder.v +++ b/examples/adder/hdl/adder.v @@ -1,7 +1,12 @@ // Adder DUT -module adder (input [3:0] A, - input [3:0] B, - output reg [4:0] X); +module adder #( + parameter DATA_WIDTH = 4 +) ( + input [DATA_WIDTH-1:0] A, + input [DATA_WIDTH-1:0] B, + output reg [DATA_WIDTH:0] X + ); + always @(A or B) begin X = A + B; end diff --git a/examples/adder/hdl/adder.vhdl b/examples/adder/hdl/adder.vhdl new file mode 100644 index 00000000..91684bfc --- /dev/null +++ b/examples/adder/hdl/adder.vhdl @@ -0,0 +1,24 @@ +-- Adder DUT +library ieee ; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity adder is +generic( + DATA_WIDTH : positive := 4); +port( + A : in unsigned(DATA_WIDTH-1 downto 0); + B : in unsigned(DATA_WIDTH-1 downto 0); + X : out unsigned(DATA_WIDTH downto 0) + ); +end adder; + +architecture RTL of adder is +begin + + process(A, B) + begin + X <= resize(A, X'length) + B; + end process; + +end RTL; diff --git a/examples/adder/tests/Makefile b/examples/adder/tests/Makefile index 9c5d7d96..507e8c9e 100644 --- a/examples/adder/tests/Makefile +++ b/examples/adder/tests/Makefile @@ -29,14 +29,6 @@ TOPLEVEL_LANG ?= verilog -ifneq ($(TOPLEVEL_LANG),verilog) - -all: - @echo "Skipping example due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being verilog" -clean:: - -else - PWD=$(shell pwd) COCOTB=$(PWD)/../../.. @@ -48,12 +40,16 @@ WPWD=$(shell pwd) PYTHONPATH := $(WPWD)/../model:$(PYTHONPATH) endif -VERILOG_SOURCES = $(WPWD)/../hdl/adder.v +ifeq ($(TOPLEVEL_LANG),verilog) + VERILOG_SOURCES = $(WPWD)/../hdl/adder.v +else ifeq ($(TOPLEVEL_LANG),vhdl) + VHDL_SOURCES = $(WPWD)/../hdl/adder.vhdl +else + $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") +endif TOPLEVEL := adder MODULE := test_adder include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim - -endif From 8f996e209df8a438a021e087bfb2340a5ceb5f07 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Sat, 19 Mar 2016 14:20:09 +1100 Subject: [PATCH 1142/2656] Ensure modelsim stops consistently. --- lib/fli/FliImpl.cpp | 3 +-- makefiles/simulators/Makefile.questa | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 5e07cf9f..17c2628f 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -42,10 +42,9 @@ static FliImpl *fli_table; void FliImpl::sim_end(void) { - const char *stop = "stop"; if (GPI_DELETE != sim_finish_cb->get_call_state()) { sim_finish_cb->set_call_state(GPI_DELETE); - mti_Cmd(stop); + mti_Break(); } } diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 0034616c..5a2e3690 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -82,6 +82,7 @@ endif ifeq ($(GUI),1) echo "add wave -recursive /*" >> $@ else + echo "onbreak resume" >> $@ echo "run -all" >> $@ echo "quit" >> $@ endif From ff0e9de11c773586c606ffd0493d382babe01cf0 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 21 Mar 2016 08:44:50 -0700 Subject: [PATCH 1143/2656] Issue #414: Fix test issue_134 to not generate invalid values --- tests/test_cases/issue_134/test_reals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/issue_134/test_reals.py b/tests/test_cases/issue_134/test_reals.py index 79ba8feb..50224178 100644 --- a/tests/test_cases/issue_134/test_reals.py +++ b/tests/test_cases/issue_134/test_reals.py @@ -15,7 +15,7 @@ def assign_double(dut): Assign a random floating point value, read it back from the DUT and check it matches what we assigned """ - val = random.uniform(sys.float_info.min, sys.float_info.max) + val = random.uniform(-1e307, 1e307) log = logging.getLogger("cocotb.test") yield Timer(1) log.info("Setting the value %g" % val) From 9f883ac53cfc1891908cf6fa7af77d89f5ebc849 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 7 Mar 2016 08:44:34 -0700 Subject: [PATCH 1144/2656] Issue #401: Add support to detect the possible python-config names --- makefiles/Makefile.pylib.Darwin | 12 +++++++++++- makefiles/Makefile.pylib.Linux | 12 +++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/makefiles/Makefile.pylib.Darwin b/makefiles/Makefile.pylib.Darwin index 36182c1c..02f70689 100644 --- a/makefiles/Makefile.pylib.Darwin +++ b/makefiles/Makefile.pylib.Darwin @@ -50,7 +50,17 @@ endif # Since we don't know which modules we might need or whether the simulator # we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports # is we simply link against all dynamic libraries in the Python installation -PYLIBS = $(shell python-config --libs) +ifneq (, $(shell which $(PYTHON_BIN)-config 2>/dev/null)) +PY_CFG_EXE=$(PYTHON_BIN)-config +else ifneq (, $(shell which $(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config 2>/dev/null)) +PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config +else ifneq (, $(shell which $(PYTHON_BIN)$(PYTHON_VERSION)-config 2>/dev/null)) +PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_VERSION)-config +else +$(error ERROR: Unable to find a valid python-config.) +endif + +PYLIBS = $(shell $(PY_CFG_EXE) --libs) PYTHON_INCLUDEDIR := $(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())') PYTHON_DYN_LIB = libpython$(PYTHON_VERSION).dylib diff --git a/makefiles/Makefile.pylib.Linux b/makefiles/Makefile.pylib.Linux index 5fe50b34..a064f658 100644 --- a/makefiles/Makefile.pylib.Linux +++ b/makefiles/Makefile.pylib.Linux @@ -58,7 +58,17 @@ endif # Since we don't know which modules we might need or whether the simulator # we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports # is we simply link against all dynamic libraries in the Python installation -PYLIBS = $(shell $(PYTHON_BIN)-config --libs) +ifneq (, $(shell which $(PYTHON_BIN)-config 2>/dev/null)) +PY_CFG_EXE=$(PYTHON_BIN)-config +else ifneq (, $(shell which $(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config 2>/dev/null)) +PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config +else ifneq (, $(shell which $(PYTHON_BIN)$(PYTHON_VERSION)-config 2>/dev/null)) +PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_VERSION)-config +else +$(error ERROR: Unable to find a valid python-config.) +endif + +PYLIBS = $(shell $(PY_CFG_EXE) --libs) PYTHON_INCLUDEDIR := $(shell $(PYTHON_BIN) -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())') PYTHON_DYN_LIB = libpython$(PYTHON_VERSION)$(DYN_SUFFIX).so From 9ba81fe3f7a129b4888a519df8f9645d811a90a7 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 7 Mar 2016 13:09:38 -0700 Subject: [PATCH 1145/2656] Issue #397: Updated vpi_user.h to match standard --- include/embed.h | 2 +- include/sv_vpi_user.h | 564 +++++++++++++++++++ include/vhpi_user.h | 12 + include/vpi_user.h | 1205 ++++++++++++++++++++++++++++++----------- lib/vpi/VpiImpl.h | 2 +- 5 files changed, 1465 insertions(+), 320 deletions(-) create mode 100644 include/sv_vpi_user.h diff --git a/include/embed.h b/include/embed.h index 0912ac1b..0fe6d975 100644 --- a/include/embed.h +++ b/include/embed.h @@ -32,7 +32,7 @@ #include #include -#include +#include #ifdef __cplusplus extern "C" { diff --git a/include/sv_vpi_user.h b/include/sv_vpi_user.h new file mode 100644 index 00000000..5cbf3722 --- /dev/null +++ b/include/sv_vpi_user.h @@ -0,0 +1,564 @@ +/*************************************************************************** +* sv_vpi_user.h +* +* SystemVerilog VPI extensions. +* +* This file contains the constant definitions, structure definitions, and +* routine declarations used by the SystemVerilog Verification Procedural +* Interface (VPI) access routines. +* +***************************************************************************/ + +/*************************************************************************** +* NOTE: +* The constant values 600 through 999 are reserved for use in this file. +* - the range 600-749 is reserved for SV VPI model extensions +* - the range 750-779 is reserved for the Coverage VPI +* - the range 800-899 is reserved for future use +* Overlaps in the numerical ranges are permitted for different categories +* of identifiers; e.g. +* - object types +* - properties +* - callbacks +**************************************************************************/ + +#ifndef SV_VPI_USER_H +#define SV_VPI_USER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/****************************** OBJECT TYPES ******************************/ +#define vpiPackage 600 +#define vpiInterface 601 +#define vpiProgram 602 +#define vpiInterfaceArray 603 +#define vpiProgramArray 604 +#define vpiTypespec 605 +#define vpiModport 606 +#define vpiInterfaceTfDecl 607 +#define vpiRefObj 608 +#define vpiTypeParameter 609 + +/* variables */ +#define vpiVarBit vpiRegBit +#define vpiLongIntVar 610 +#define vpiShortIntVar 611 +#define vpiIntVar 612 +#define vpiShortRealVar 613 +#define vpiByteVar 614 +#define vpiClassVar 615 +#define vpiStringVar 616 +#define vpiEnumVar 617 +#define vpiStructVar 618 +#define vpiUnionVar 619 +#define vpiBitVar 620 +#define vpiLogicVar vpiReg +#define vpiArrayVar vpiRegArray +#define vpiClassObj 621 +#define vpiChandleVar 622 +#define vpiPackedArrayVar 623 +#define vpiVirtualInterfaceVar 728 + +/* typespecs */ +#define vpiLongIntTypespec 625 +#define vpiShortRealTypespec 626 +#define vpiByteTypespec 627 +#define vpiShortIntTypespec 628 +#define vpiIntTypespec 629 +#define vpiClassTypespec 630 +#define vpiStringTypespec 631 +#define vpiChandleTypespec 632 +#define vpiEnumTypespec 633 +#define vpiEnumConst 634 +#define vpiIntegerTypespec 635 +#define vpiTimeTypespec 636 +#define vpiRealTypespec 637 +#define vpiStructTypespec 638 +#define vpiUnionTypespec 639 +#define vpiBitTypespec 640 +#define vpiLogicTypespec 641 +#define vpiArrayTypespec 642 +#define vpiVoidTypespec 643 +#define vpiTypespecMember 644 +#define vpiPackedArrayTypespec 692 +#define vpiSequenceTypespec 696 +#define vpiPropertyTypespec 697 +#define vpiEventTypespec 698 + +#define vpiClockingBlock 650 +#define vpiClockingIODecl 651 +#define vpiClassDefn 652 +#define vpiConstraint 653 +#define vpiConstraintOrdering 654 + +#define vpiDistItem 645 +#define vpiAliasStmt 646 +#define vpiThread 647 +#define vpiMethodFuncCall 648 +#define vpiMethodTaskCall 649 + +/* concurrent assertions */ +#define vpiAssert 686 +#define vpiAssume 687 +#define vpiCover 688 +#define vpiRestrict 901 + +#define vpiDisableCondition 689 +#define vpiClockingEvent 690 + +/* property decl, spec */ +#define vpiPropertyDecl 655 +#define vpiPropertySpec 656 +#define vpiPropertyExpr 657 +#define vpiMulticlockSequenceExpr 658 +#define vpiClockedSeq 659 +#define vpiClockedProp 902 +#define vpiPropertyInst 660 +#define vpiSequenceDecl 661 +#define vpiCaseProperty 662 /* property case */ +#define vpiCasePropertyItem 905 /* property case item */ +#define vpiSequenceInst 664 +#define vpiImmediateAssert 665 +#define vpiImmediateAssume 694 +#define vpiImmediateCover 695 +#define vpiReturn 666 +/* pattern */ +#define vpiAnyPattern 667 +#define vpiTaggedPattern 668 +#define vpiStructPattern 669 +/* do .. while */ +#define vpiDoWhile 670 +/* waits */ +#define vpiOrderedWait 671 +#define vpiWaitFork 672 +/* disables */ +#define vpiDisableFork 673 +#define vpiExpectStmt 674 +#define vpiForeachStmt 675 +#define vpiReturnStmt 691 +#define vpiFinal 676 +#define vpiExtends 677 +#define vpiDistribution 678 +#define vpiSeqFormalDecl 679 +#define vpiPropFormalDecl 699 +#define vpiArrayNet vpiNetArray +#define vpiEnumNet 680 +#define vpiIntegerNet 681 +#define vpiLogicNet vpiNet +#define vpiTimeNet 682 +#define vpiStructNet 683 +#define vpiBreak 684 +#define vpiContinue 685 +#define vpiPackedArrayNet 693 +#define vpiConstraintExpr 747 +#define vpiElseConst 748 +#define vpiImplication 749 +#define vpiConstrIf 738 +#define vpiConstrIfElse 739 +#define vpiConstrForEach 736 +#define vpiLetDecl 903 +#define vpiLetExpr 904 + +/******************************** METHODS *********************************/ +/************* methods used to traverse 1 to 1 relationships **************/ +#define vpiActual 700 + +#define vpiTypedefAlias 701 + +#define vpiIndexTypespec 702 +#define vpiBaseTypespec 703 +#define vpiElemTypespec 704 + +#define vpiInputSkew 706 +#define vpiOutputSkew 707 +#define vpiGlobalClocking 708 +#define vpiDefaultClocking 709 +#define vpiDefaultDisableIff 710 + +#define vpiOrigin 713 +#define vpiPrefix 714 +#define vpiWith 715 + +#define vpiProperty 718 + +#define vpiValueRange 720 +#define vpiPattern 721 +#define vpiWeight 722 +#define vpiConstraintItem 746 + +/************ methods used to traverse 1 to many relationships ************/ +#define vpiTypedef 725 +#define vpiImport 726 +#define vpiDerivedClasses 727 +#define vpiInterfaceDecl vpiVirtualInterfaceVar /* interface decl deprecated */ + +#define vpiMethods 730 +#define vpiSolveBefore 731 +#define vpiSolveAfter 732 + +#define vpiWaitingProcesses 734 + +#define vpiMessages 735 +#define vpiLoopVars 737 + +#define vpiConcurrentAssertions 740 +#define vpiMatchItem 741 +#define vpiMember 742 +#define vpiElement 743 + +/************* methods used to traverse 1 to many relationships ***************/ +#define vpiAssertion 744 + +/*********** methods used to traverse both 1-1 and 1-many relations ***********/ +#define vpiInstance 745 + +/**************************************************************************/ +/************************ generic object properties ***********************/ +/**************************************************************************/ + +#define vpiTop 600 + +#define vpiUnit 602 +#define vpiJoinType 603 +#define vpiJoin 0 +#define vpiJoinNone 1 +#define vpiJoinAny 2 +#define vpiAccessType 604 +#define vpiForkJoinAcc 1 +#define vpiExternAcc 2 +#define vpiDPIExportAcc 3 +#define vpiDPIImportAcc 4 + +#define vpiArrayType 606 +#define vpiStaticArray 1 +#define vpiDynamicArray 2 +#define vpiAssocArray 3 +#define vpiQueueArray 4 +#define vpiArrayMember 607 + +#define vpiIsRandomized 608 +#define vpiLocalVarDecls 609 +#define vpiOpStrong 656 /* strength of temporal operator */ +#define vpiRandType 610 +#define vpiNotRand 1 +#define vpiRand 2 +#define vpiRandC 3 +#define vpiPortType 611 +#define vpiInterfacePort 1 +#define vpiModportPort 2 +/* vpiPort is also a port type. It is defined in vpi_user.h */ + +#define vpiConstantVariable 612 +#define vpiStructUnionMember 615 + +#define vpiVisibility 620 +#define vpiPublicVis 1 +#define vpiProtectedVis 2 +#define vpiLocalVis 3 + +/* Return values for vpiConstType property */ +#define vpiOneStepConst 9 +#define vpiUnboundedConst 10 +#define vpiNullConst 11 + +#define vpiAlwaysType 624 +#define vpiAlwaysComb 2 +#define vpiAlwaysFF 3 +#define vpiAlwaysLatch 4 + +#define vpiDistType 625 +#define vpiEqualDist 1 /* constraint equal distribution */ +#define vpiDivDist 2 /* constraint divided distribution */ + +#define vpiPacked 630 +#define vpiTagged 632 +#define vpiRef 6 /* Return value for vpiDirection property */ +#define vpiVirtual 635 +#define vpiHasActual 636 +#define vpiIsConstraintEnabled 638 +#define vpiSoft 639 + + +#define vpiClassType 640 +#define vpiMailboxClass 1 +#define vpiSemaphoreClass 2 +#define vpiUserDefinedClass 3 +#define vpiProcessClass 4 +#define vpiMethod 645 +#define vpiIsClockInferred 649 +#define vpiIsDeferred 657 +#define vpiIsFinal 658 +#define vpiIsCoverSequence 659 +#define vpiQualifier 650 +#define vpiNoQualifier 0 +#define vpiUniqueQualifier 1 +#define vpiPriorityQualifier 2 +#define vpiTaggedQualifier 4 +#define vpiRandQualifier 8 +#define vpiInsideQualifier 16 + +#define vpiInputEdge 651 /* returns vpiNoEdge, vpiPosedge, + vpiNegedge */ +#define vpiOutputEdge 652 /* returns vpiNoEdge, vpiPosedge, + vpiNegedge */ +#define vpiGeneric 653 + +/* Compatibility-mode property and values (object argument == NULL) */ +#define vpiCompatibilityMode 654 +#define vpiMode1364v1995 1 +#define vpiMode1364v2001 2 +#define vpiMode1364v2005 3 +#define vpiMode1800v2005 4 +#define vpiMode1800v2009 5 + +#define vpiPackedArrayMember 655 +#define vpiStartLine 661 +#define vpiColumn 662 +#define vpiEndLine 663 +#define vpiEndColumn 664 + +/* memory allocation scheme for transient objects */ +#define vpiAllocScheme 658 +#define vpiAutomaticScheme 1 +#define vpiDynamicScheme 2 +#define vpiOtherScheme 3 + +#define vpiObjId 660 + +#define vpiDPIPure 665 +#define vpiDPIContext 666 +#define vpiDPICStr 667 +#define vpiDPI 1 +#define vpiDPIC 2 +#define vpiDPICIdentifier 668 + +/******************************** Operators *******************************/ +#define vpiImplyOp 50 /* -> implication operator */ +#define vpiNonOverlapImplyOp 51 /* |=> nonoverlapped implication */ +#define vpiOverlapImplyOp 52 /* |-> overlapped implication operator */ +#define vpiAcceptOnOp 83 /* accept_on operator */ +#define vpiRejectOnOp 84 /* reject_on operator */ +#define vpiSyncAcceptOnOp 85 /* sync_accept_on operator */ +#define vpiSyncRejectOnOp 86 /* sync_reject_on operator */ +#define vpiOverlapFollowedByOp 87 /* overlapped followed_by operator */ +#define vpiNonOverlapFollowedByOp 88 /* nonoverlapped followed_by operator */ +#define vpiNexttimeOp 89 /* nexttime operator */ +#define vpiAlwaysOp 90 /* always operator */ +#define vpiEventuallyOp 91 /* eventually operator */ +#define vpiUntilOp 92 /* until operator */ +#define vpiUntilWithOp 93 /* until_with operator */ + +#define vpiUnaryCycleDelayOp 53 /* binary cycle delay (##) operator */ +#define vpiCycleDelayOp 54 /* binary cycle delay (##) operator */ +#define vpiIntersectOp 55 /* intersection operator */ +#define vpiFirstMatchOp 56 /* first_match operator */ +#define vpiThroughoutOp 57 /* throughout operator */ +#define vpiWithinOp 58 /* within operator */ +#define vpiRepeatOp 59 /* [=] nonconsecutive repetition */ +#define vpiConsecutiveRepeatOp 60 /* [*] consecutive repetition */ +#define vpiGotoRepeatOp 61 /* [->] goto repetition */ + +#define vpiPostIncOp 62 /* ++ post-increment */ +#define vpiPreIncOp 63 /* ++ pre-increment */ +#define vpiPostDecOp 64 /* -- post-decrement */ +#define vpiPreDecOp 65 /* -- pre-decrement */ + +#define vpiMatchOp 66 /* match() operator */ +#define vpiCastOp 67 /* type'() operator */ +#define vpiIffOp 68 /* iff operator */ +#define vpiWildEqOp 69 /* ==? operator */ +#define vpiWildNeqOp 70 /* !=? operator */ + +#define vpiStreamLROp 71 /* left-to-right streaming {>>} operator */ +#define vpiStreamRLOp 72 /* right-to-left streaming {<<} operator */ + +#define vpiMatchedOp 73 /* the .matched sequence operation */ +#define vpiTriggeredOp 74 /* the .triggered sequence operation */ +#define vpiAssignmentPatternOp 75 /* '{} assignment pattern */ +#define vpiMultiAssignmentPatternOp 76 /* '{n{}} multi assignment pattern */ +#define vpiIfOp 77 /* if operator */ +#define vpiIfElseOp 78 /* if–else operator */ +#define vpiCompAndOp 79 /* Composite and operator */ +#define vpiCompOrOp 80 /* Composite or operator */ +#define vpiImpliesOp 94 /* implies operator */ +#define vpiInsideOp 95 /* inside operator */ +#define vpiTypeOp 81 /* type operator */ +#define vpiAssignmentOp 82 /* Normal assignment */ + +/*********************** task/function properties ***********************/ +#define vpiOtherFunc 6 /* returns other types; for property vpiFuncType */ +/* vpiValid,vpiValidTrue,vpiValidFalse are deprecated in 1800-2009 */ + +/*********************** value for vpiValid *****************************/ +#define vpiValidUnknown 2 /* Validity of variable is unknown */ + +/************************** STRUCTURE DEFINITIONS *************************/ + +/***************************** structure *****************************/ + +/**************************** CALLBACK REASONS ****************************/ +#define cbStartOfThread 600 /* callback on thread creation */ +#define cbEndOfThread 601 /* callback on thread termination */ +#define cbEnterThread 602 /* callback on reentering thread */ +#define cbStartOfFrame 603 /* callback on frame creation */ +#define cbEndOfFrame 604 /* callback on frame exit */ +#define cbSizeChange 605 /* callback on array variable size change */ +#define cbCreateObj 700 /* callback on class object creation */ +#define cbReclaimObj 701 /* callback on class object reclaimed by automatic memory management */ + +#define cbEndOfObject 702 /* callback on transient object deletion */ + +/************************* FUNCTION DECLARATIONS **************************/ + + +/**************************************************************************/ +/*************************** Coverage VPI *********************************/ +/**************************************************************************/ + +/* coverage control */ +#define vpiCoverageStart 750 +#define vpiCoverageStOp 751 +#define vpiCoverageReset 752 +#define vpiCoverageCheck 753 +#define vpiCoverageMerge 754 +#define vpiCoverageSave 755 + +/* coverage type properties */ +#define vpiAssertCoverage 760 +#define vpiFsmStateCoverage 761 +#define vpiStatementCoverage 762 +#define vpiToggleCoverage 763 + +/* coverage status properties */ +#define vpiCovered 765 +#define vpiCoverMax 766 +#define vpiCoveredCount 767 + +/* assertion-specific coverage status properties */ +#define vpiAssertAttemptCovered 770 +#define vpiAssertSuccessCovered 771 +#define vpiAssertFailureCovered 772 +#define vpiAssertVacuousSuccessCovered 773 +#define vpiAssertDisableCovered 774 +#define vpiAssertKillCovered 777 + +/* FSM-specific coverage status properties */ +#define vpiFsmStates 775 +#define vpiFsmStateExpression 776 + +/* FSM handle types */ +#define vpiFsm 758 +#define vpiFsmHandle 759 + + +/***************************************************************************/ +/***************************** Assertion VPI *******************************/ +/***************************************************************************/ + +/* assertion callback types */ +#define cbAssertionStart 606 +#define cbAssertionSuccess 607 +#define cbAssertionFailure 608 +#define cbAssertionVacuousSuccess 657 +#define cbAssertionDisabledEvaluation 658 +#define cbAssertionStepSuccess 609 +#define cbAssertionStepFailure 610 +#define cbAssertionLock 661 +#define cbAssertionUnlock 662 +#define cbAssertionDisable 611 +#define cbAssertionEnable 612 +#define cbAssertionReset 613 +#define cbAssertionKill 614 +#define cbAssertionEnablePassAction 645 +#define cbAssertionEnableFailAction 646 +#define cbAssertionDisablePassAction 647 +#define cbAssertionDisableFailAction 648 +#define cbAssertionEnableNonvacuousAction 649 +#define cbAssertionDisableVacuousAction 650 + +/* assertion "system" callback types */ +#define cbAssertionSysInitialized 615 +#define cbAssertionSysOn 616 +#define cbAssertionSysOff 617 +#define cbAssertionSysKill 631 +#define cbAssertionSysLock 659 +#define cbAssertionSysUnlock 660 +#define cbAssertionSysEnd 618 +#define cbAssertionSysReset 619 +#define cbAssertionSysEnablePassAction 651 +#define cbAssertionSysEnableFailAction 652 +#define cbAssertionSysDisablePassAction 653 +#define cbAssertionSysDisableFailAction 654 +#define cbAssertionSysEnableNonvacuousAction 655 +#define cbAssertionSysDisableVacuousAction 656 + +/* assertion control constants */ +#define vpiAssertionLock 645 +#define vpiAssertionUnlock 646 +#define vpiAssertionDisable 620 +#define vpiAssertionEnable 621 +#define vpiAssertionReset 622 +#define vpiAssertionKill 623 +#define vpiAssertionEnableStep 624 +#define vpiAssertionDisableStep 625 +#define vpiAssertionClockSteps 626 +#define vpiAssertionSysLock 647 +#define vpiAssertionSysUnlock 648 +#define vpiAssertionSysOn 627 +#define vpiAssertionSysOff 628 +#define vpiAssertionSysKill 632 +#define vpiAssertionSysEnd 629 +#define vpiAssertionSysReset 630 +#define vpiAssertionDisablePassAction 633 +#define vpiAssertionEnablePassAction 634 +#define vpiAssertionDisableFailAction 635 +#define vpiAssertionEnableFailAction 636 +#define vpiAssertionDisableVacuousAction 637 +#define vpiAssertionEnableNonvacuousAction 638 +#define vpiAssertionSysEnablePassAction 639 +#define vpiAssertionSysEnableFailAction 640 +#define vpiAssertionSysDisablePassAction 641 +#define vpiAssertionSysDisableFailAction 642 +#define vpiAssertionSysEnableNonvacuousAction 643 +#define vpiAssertionSysDisableVacuousAction 644 + + +typedef struct t_vpi_assertion_step_info { + PLI_INT32 matched_expression_count; + vpiHandle *matched_exprs; /* array of expressions */ + PLI_INT32 stateFrom, stateTo; /* identify transition */ +} s_vpi_assertion_step_info, *p_vpi_assertion_step_info; + +typedef struct t_vpi_attempt_info { + union { + vpiHandle failExpr; + p_vpi_assertion_step_info step; + } detail; + s_vpi_time attemptStartTime; /* Time attempt triggered */ +} s_vpi_attempt_info, *p_vpi_attempt_info; + +/* typedef for vpi_register_assertion_cb callback function */ +typedef PLI_INT32(vpi_assertion_callback_func)( + PLI_INT32 reason, /* callback reason */ + p_vpi_time cb_time, /* callback time */ + vpiHandle assertion, /* handle to assertion */ + p_vpi_attempt_info info, /* attempt related information */ + PLI_BYTE8 *user_data /* user data entered upon registration */ +); + +vpiHandle vpi_register_assertion_cb( + vpiHandle assertion, /* handle to assertion */ + PLI_INT32 reason, /* reason for which callbacks needed */ + vpi_assertion_callback_func *cb_rtn, + PLI_BYTE8 *user_data /* user data to be supplied to cb */ +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/vhpi_user.h b/include/vhpi_user.h index 0eefeaf5..75c406e0 100644 --- a/include/vhpi_user.h +++ b/include/vhpi_user.h @@ -206,7 +206,11 @@ extern "C" { */ +#ifndef IUS #define vhpiUndefined -1 +#else +#define vhpiUndefined 1000 +#endif /*************** OBJECT KINDS *******************/ typedef enum @@ -326,7 +330,11 @@ typedef enum vhpiWaveformElemK = 1113, vhpiWhileLoopK = 1114, vhpiQualifiedExprK = 1115, +#ifndef IUS vhpiUseClauseK = 1116, +#else + vhpiUseClauseK = 1200, +#endif #ifdef VHPIEXTEND_CLASSES VHPIEXTEND_CLASSES @@ -497,7 +505,11 @@ typedef enum vhpiLocalLoads = 1559, vhpiOptimizedLoads = 1560, vhpiTypes = 1561, +#ifndef IUS vhpiUseClauses = 1562, +#else + vhpiUseClauses = 1650, +#endif #ifdef VHPIEXTEND_MANY_METHODS VHPIEXTEND_MANY_METHODS diff --git a/include/vpi_user.h b/include/vpi_user.h index 4bf64652..28eebb14 100644 --- a/include/vpi_user.h +++ b/include/vpi_user.h @@ -1,261 +1,676 @@ -/****************************************************************************** -* Copyright (c) 2013 Potential Ventures Ltd -* Copyright (c) 2013 SolarFlare Communications Inc -* All rights reserved. +/******************************************************************************* +* vpi_user.h * -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd, -* SolarFlare Communications Inc nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. +* IEEE Std 1800 Programming Language Interface (PLI) +* +* This file contains the constant definitions, structure definitions, and +* routine declarations used by the SystemVerilog Verification Procedural +* Interface (VPI) access routines. * -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ -#ifndef COCOTB_VPI_USER_H_ -#define COCOTB_VPI_USER_H_ +/******************************************************************************* + * NOTE: the constant values 1 through 299 are reserved for use in this + * vpi_user.h file. + ******************************************************************************/ -/* This file (vpi_user.h) contains a limited subset of the IEEE 1394 - * standard that is required for the library to build against - */ +#ifndef VPI_USER_H +#define VPI_USER_H #include - - #ifdef __cplusplus extern "C" { #endif -#if !defined(__linux__) && !defined(__APPLE__) -#ifndef VPI_DLLISPEC -#define VPI_DLLISPEC __declspec(dllimport) -#define VPI_DLL_LOCAL 1 +/*----------------------------------------------------------------------------*/ +/*----------------------------- Portability Help -----------------------------*/ +/*----------------------------------------------------------------------------*/ + +#if defined (_MSC_VER) +typedef unsigned __int64 uint64_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int8 uint8_t; +typedef signed __int64 int64_t; +typedef signed __int32 int32_t; +typedef signed __int8 int8_t; +#elif defined(__MINGW32__) +#include +#elif defined(__linux) +#include +#else +#include +#endif +/* Sized variables */ + + +#ifndef SVPI_TYPES +#define SVPI_TYPES +typedef int64_t PLI_INT64; +typedef uint64_t PLI_UINT64; +#endif + +#ifndef PLI_TYPES +#define PLI_TYPES +typedef int PLI_INT32; +typedef unsigned int PLI_UINT32; +typedef short PLI_INT16; +typedef unsigned short PLI_UINT16; +typedef char PLI_BYTE8; +typedef unsigned char PLI_UBYTE8; +#endif + +/* Use to import a symbol */ + +#if defined(WIN32) || defined(WIN64) +#ifndef PLI_DLLISPEC +#define PLI_DLLISPEC __declspec(dllimport) +#define VPI_USER_DEFINED_DLLISPEC 1 #endif #else -#ifndef VPI_DLLISPEC -#define VPI_DLLISPEC +#ifndef PLI_DLLISPEC +#define PLI_DLLISPEC #endif #endif -typedef uint32_t *vpiHandle; +/* Use to export a symbol */ -/******************************** OBJECT TYPES ********************************/ +#if defined(WIN32) || defined(WIN64) +#ifndef PLI_DLLESPEC +#define PLI_DLLESPEC __declspec(dllexport) +#define VPI_USER_DEFINED_DLLESPEC 1 +#endif +#else +#ifndef PLI_DLLESPEC +#define PLI_DLLESPEC +#endif +#endif -#define vpiAlways 1 /* always procedure */ -#define vpiAssignStmt 2 /* quasi-continuous assignment */ -#define vpiAssignment 3 /* procedural assignment */ -#define vpiBegin 4 /* block statement */ -#define vpiCase 5 /* case statement */ -#define vpiCaseItem 6 /* case statement item */ -#define vpiConstant 7 /* numerical constant or string literal */ -#define vpiContAssign 8 /* continuous assignment */ -#define vpiDeassign 9 /* deassignment statement */ -#define vpiDefParam 10 /* defparam */ -#define vpiDelayControl 11 /* delay statement (e.g., #10) */ -#define vpiDisable 12 /* named block disable statement */ -#define vpiEventControl 13 /* wait on event, e.g., @e */ -#define vpiEventStmt 14 /* event trigger, e.g., ->e */ -#define vpiFor 15 /* for statement */ -#define vpiForce 16 /* force statement */ -#define vpiForever 17 /* forever statement */ -#define vpiFork 18 /* fork-join block */ -#define vpiFuncCall 19 /* function call */ -#define vpiFunction 20 /* function */ -#define vpiGate 21 /* primitive gate */ -#define vpiIfElse 23 /* if-else statement */ -#define vpiInitial 24 /* initial procedure */ -#define vpiIntegerVar 25 /* integer variable */ -#define vpiInterModPath 26 /* intermodule wire delay */ -#define vpiIterator 27 /* iterator */ -#define vpiIODecl 28 /* input/output declaration */ -#define vpiMemory 29 /* behavioral memory */ -#define vpiMemoryWord 30 /* single word of memory */ -#define vpiModPath 31 /* module path for path delays */ -#define vpiModule 32 /* module instance */ -#define vpiNamedBegin 33 /* named block statement */ -#define vpiNamedEvent 34 /* event variable */ -#define vpiNamedFork 35 /* named fork-join block */ -#define vpiNet 36 /* scalar or vector net */ -#define vpiNetBit 37 /* bit of vector net */ -#define vpiNullStmt 38 /* a semicolon. Ie. #10 ; */ -#define vpiOperation 39 /* behavioral operation */ -#define vpiParamAssign 40 /* module parameter assignment */ -#define vpiParameter 41 /* module parameter */ -#define vpiPartSelect 42 /* part-select */ -#define vpiPathTerm 43 /* terminal of module path */ -#define vpiPort 44 /* module port */ -#define vpiPortBit 45 /* bit of vector module port */ -#define vpiPrimTerm 46 /* primitive terminal */ -#define vpiRealVar 47 /* real variable */ -#define vpiReg 48 /* scalar or vector reg */ -#define vpiRegBit 49 /* bit of vector reg */ -#define vpiRelease 50 /* release statement */ -#define vpiRepeat 51 /* repeat statement */ -#define vpiRepeatControl 52 /* repeat control in an assign stmt */ -#define vpiSchedEvent 53 /* vpi_put_value() event */ -#define vpiSpecParam 54 /* specparam */ -#define vpiSwitch 55 /* transistor switch */ -#define vpiSysFuncCall 56 /* system function call */ -#define vpiSysTaskCall 57 /* system task call */ -#define vpiTableEntry 58 /* UDP state table entry */ -#define vpiTask 59 /* task */ -#define vpiTaskCall 60 /* task call */ -#define vpiTchk 61 /* timing check */ -#define vpiTchkTerm 62 /* terminal of timing check */ -#define vpiTimeVar 63 /* time variable */ -#define vpiTimeQueue 64 /* simulation event queue */ -#define vpiUdp 65 /* user-defined primitive */ -#define vpiUdpDefn 66 /* UDP definition */ -#define vpiUserSystf 67 /* user-defined system task/function */ -#define vpiVarSelect 68 /* variable array selection */ -#define vpiWait 69 /* wait statement */ -#define vpiWhile 70 /* while statement */ - - -#define vpiPrimitive 103 /* primitive (gate, switch, UDP) */ +/* Use to mark a function as external */ -/********************** object types added with 1364-2001 *********************/ +#ifndef PLI_EXTERN +#define PLI_EXTERN +#endif -#define vpiAttribute 105 /* attribute of an object */ -#define vpiBitSelect 106 /* Bit-select of parameter, var select */ -#define vpiCallback 107 /* callback object */ -#define vpiDelayTerm 108 /* Delay term which is a load or driver */ -#define vpiDelayDevice 109 /* Delay object within a net */ -#define vpiFrame 110 /* reentrant task/func frame */ -#define vpiGateArray 111 /* gate instance array */ -#define vpiModuleArray 112 /* module instance array */ -#define vpiPrimitiveArray 113 /* vpiprimitiveArray type */ -#define vpiNetArray 114 /* multidimensional net */ -#define vpiRange 115 /* range declaration */ -#define vpiRegArray 116 /* multidimensional reg */ -#define vpiSwitchArray 117 /* switch instance array */ -#define vpiUdpArray 118 /* UDP instance array */ -#define vpiContAssignBit 128 /* Bit of a vector continuous assignment */ -#define vpiNamedEventArray 129 /* multidimensional named event */ - -#define vpiInterface 601 -#define vpiInterfaceArray 603 -#define vpiModport 606 -#define vpiRefObj 608 -#define vpiIntVar 612 -#define vpiEnumVar 617 -#define vpiStructVar 618 -#define vpiPackedArrayVar 623 -#define vpiEnumNet 680 /* SystemVerilog */ -#define vpiIntegerNet 681 -#define vpiStructNet 683 - - -#define vpiStop 66 /* execute simulator's $stop */ -#define vpiFinish 67 /* execute simulator's $finish */ -#define vpiReset 68 /* execute simulator's $reset */ +/* Use to mark a variable as external */ -/********************** object types added with 1364-2005 *********************/ +#ifndef PLI_VEXTERN +#define PLI_VEXTERN extern +#endif -#define vpiIndexedPartSelect 130 /* Indexed part-select object */ -#define vpiGenScopeArray 133 /* array of generated scopes */ -#define vpiGenScope 134 /* A generated scope */ -#define vpiGenVar 135 /* Object used to instantiate gen scopes */ +#ifndef PLI_PROTOTYPES +#define PLI_PROTOTYPES +#define PROTO_PARAMS(params) params -#define vpiType 1 /* type of object */ -#define vpiName 2 /* local name of object */ -#define vpiFullName 3 /* full hierarchical name */ -#define vpiSize 4 /* size of gate, net, port, etc. */ +/* object is defined imported by the application */ -#define vpiNoDelay 1 -#define vpiInertialDelay 2 +#define XXTERN PLI_EXTERN PLI_DLLISPEC -/* One 2 many relationships */ -#define vpiArgument 89 /* argument to (system) task/function */ -#define vpiBit 90 /* bit of vector net or port */ -#define vpiDriver 91 /* driver for a net */ -#define vpiInternalScope 92 /* internal scope in module */ -#define vpiLoad 93 /* load on net or reg */ -#define vpiModDataPathIn 94 /* data terminal of a module path */ -#define vpiModPathIn 95 /* Input terminal of a module path */ -#define vpiModPathOut 96 /* output terminal of a module path */ -#define vpiOperand 97 /* operand of expression */ -#define vpiPortInst 98 /* connected port instance */ -#define vpiProcess 99 /* process in module */ -#define vpiVariables 100 /* variables in module */ -#define vpiUse 101 /* usage */ +/* object is exported by the application */ -#define vpiStop 66 /* execute simulator's $stop */ -#define vpiFinish 67 /* execute simulator's $finish */ -#define vpiReset 68 /* execute simulator's $reset */ +#define EETERN PLI_EXTERN PLI_DLLESPEC +#endif +/********************************** TYPEDEFS **********************************/ +typedef PLI_UINT32 *vpiHandle; + +/******************************** OBJECT TYPES ********************************/ + +#define vpiAlways 1 /* always construct */ +#define vpiAssignStmt 2 /* quasi-continuous assignment */ +#define vpiAssignment 3 /* procedural assignment */ +#define vpiBegin 4 /* block statement */ +#define vpiCase 5 /* case statement */ +#define vpiCaseItem 6 /* case statement item */ +#define vpiConstant 7 /* numerical constant or literal string */ +#define vpiContAssign 8 /* continuous assignment */ +#define vpiDeassign 9 /* deassignment statement */ +#define vpiDefParam 10 /* defparam */ +#define vpiDelayControl 11 /* delay statement (e.g. #10) */ +#define vpiDisable 12 /* named block disable statement */ +#define vpiEventControl 13 /* wait on event, e.g. @e */ +#define vpiEventStmt 14 /* event trigger, e.g. ->e */ +#define vpiFor 15 /* for statement */ +#define vpiForce 16 /* force statement */ +#define vpiForever 17 /* forever statement */ +#define vpiFork 18 /* fork-join block */ +#define vpiFuncCall 19 /* HDL function call */ +#define vpiFunction 20 /* HDL function */ +#define vpiGate 21 /* primitive gate */ +#define vpiIf 22 /* if statement */ +#define vpiIfElse 23 /* if-else statement */ +#define vpiInitial 24 /* initial construct */ +#define vpiIntegerVar 25 /* integer variable */ +#define vpiInterModPath 26 /* intermodule wire delay */ +#define vpiIterator 27 /* iterator */ +#define vpiIODecl 28 /* input/output declaration */ +#define vpiMemory 29 /* behavioral memory */ +#define vpiMemoryWord 30 /* single word of memory */ +#define vpiModPath 31 /* module path for path delays */ +#define vpiModule 32 /* module instance */ +#define vpiNamedBegin 33 /* named block statement */ +#define vpiNamedEvent 34 /* event variable */ +#define vpiNamedFork 35 /* named fork-join block */ +#define vpiNet 36 /* scalar or vector net */ +#define vpiNetBit 37 /* bit of vector net */ +#define vpiNullStmt 38 /* a semicolon. Ie. #10 ; */ +#define vpiOperation 39 /* behavioral operation */ +#define vpiParamAssign 40 /* module parameter assignment */ +#define vpiParameter 41 /* module parameter */ +#define vpiPartSelect 42 /* part-select */ +#define vpiPathTerm 43 /* terminal of module path */ +#define vpiPort 44 /* module port */ +#define vpiPortBit 45 /* bit of vector module port */ +#define vpiPrimTerm 46 /* primitive terminal */ +#define vpiRealVar 47 /* real variable */ +#define vpiReg 48 /* scalar or vector reg */ +#define vpiRegBit 49 /* bit of vector reg */ +#define vpiRelease 50 /* release statement */ +#define vpiRepeat 51 /* repeat statement */ +#define vpiRepeatControl 52 /* repeat control in an assign stmt */ +#define vpiSchedEvent 53 /* vpi_put_value() event */ +#define vpiSpecParam 54 /* specparam */ +#define vpiSwitch 55 /* transistor switch */ +#define vpiSysFuncCall 56 /* system function call */ +#define vpiSysTaskCall 57 /* system task call */ +#define vpiTableEntry 58 /* UDP state table entry */ +#define vpiTask 59 /* HDL task */ +#define vpiTaskCall 60 /* HDL task call */ +#define vpiTchk 61 /* timing check */ +#define vpiTchkTerm 62 /* terminal of timing check */ +#define vpiTimeVar 63 /* time variable */ +#define vpiTimeQueue 64 /* simulation event queue */ +#define vpiUdp 65 /* user-defined primitive */ +#define vpiUdpDefn 66 /* UDP definition */ +#define vpiUserSystf 67 /* user defined system task or function */ +#define vpiVarSelect 68 /* variable array selection */ +#define vpiWait 69 /* wait statement */ +#define vpiWhile 70 /* while statement */ + +/********************** object types added with 1364-2001 *********************/ + +#define vpiAttribute 105 /* attribute of an object */ +#define vpiBitSelect 106 /* Bit-select of parameter, var select */ +#define vpiCallback 107 /* callback object */ +#define vpiDelayTerm 108 /* Delay term which is a load or driver */ +#define vpiDelayDevice 109 /* Delay object within a net */ +#define vpiFrame 110 /* reentrant task/func frame */ +#define vpiGateArray 111 /* gate instance array */ +#define vpiModuleArray 112 /* module instance array */ +#define vpiPrimitiveArray 113 /* vpiprimitiveArray type */ +#define vpiNetArray 114 /* multidimensional net */ +#define vpiRange 115 /* range declaration */ +#define vpiRegArray 116 /* multidimensional reg */ +#define vpiSwitchArray 117 /* switch instance array */ +#define vpiUdpArray 118 /* UDP instance array */ +#define vpiContAssignBit 128 /* Bit of a vector continuous assignment */ +#define vpiNamedEventArray 129 /* multidimensional named event */ + +/********************** object types added with 1364-2005 *********************/ + +#define vpiIndexedPartSelect 130 /* Indexed part-select object */ +#define vpiGenScopeArray 133 /* array of generated scopes */ +#define vpiGenScope 134 /* A generated scope */ +#define vpiGenVar 135 /* Object used to instantiate gen scopes */ + +/*********************************** METHODS **********************************/ +/**************** methods used to traverse 1 to 1 relationships ***************/ + +#define vpiCondition 71 /* condition expression */ +#define vpiDelay 72 /* net or gate delay */ +#define vpiElseStmt 73 /* else statement */ +#define vpiForIncStmt 74 /* increment statement in for loop */ +#define vpiForInitStmt 75 /* initialization statement in for loop */ +#define vpiHighConn 76 /* higher connection to port */ +#define vpiLhs 77 /* left-hand side of assignment */ +#define vpiIndex 78 /* index of var select, bit-select, etc. */ +#define vpiLeftRange 79 /* left range of vector or part-select */ +#define vpiLowConn 80 /* lower connection to port */ +#define vpiParent 81 /* parent object */ +#define vpiRhs 82 /* right-hand side of assignment */ +#define vpiRightRange 83 /* right range of vector or part-select */ +#define vpiScope 84 /* containing scope object */ +#define vpiSysTfCall 85 /* task function call */ +#define vpiTchkDataTerm 86 /* timing check data term */ +#define vpiTchkNotifier 87 /* timing check notifier */ +#define vpiTchkRefTerm 88 /* timing check reference term */ + +/************* methods used to traverse 1 to many relationships ***************/ + +#define vpiArgument 89 /* argument to (system) task/function */ +#define vpiBit 90 /* bit of vector net or port */ +#define vpiDriver 91 /* driver for a net */ +#define vpiInternalScope 92 /* internal scope in module */ +#define vpiLoad 93 /* load on net or reg */ +#define vpiModDataPathIn 94 /* data terminal of a module path */ +#define vpiModPathIn 95 /* Input terminal of a module path */ +#define vpiModPathOut 96 /* output terminal of a module path */ +#define vpiOperand 97 /* operand of expression */ +#define vpiPortInst 98 /* connected port instance */ +#define vpiProcess 99 /* process in module */ +#define vpiVariables 100 /* variables in module */ +#define vpiUse 101 /* usage */ + +/******** methods which can traverse 1 to 1, or 1 to many relationships *******/ + +#define vpiExpr 102 /* connected expression */ +#define vpiPrimitive 103 /* primitive (gate, switch, UDP) */ +#define vpiStmt 104 /* statement in process or task */ + +/************************ methods added with 1364-2001 ************************/ + +#define vpiActiveTimeFormat 119 /* active $timeformat() system task */ +#define vpiInTerm 120 /* To get to a delay device's drivers. */ +#define vpiInstanceArray 121 /* vpiInstance arrays */ +#define vpiLocalDriver 122 /* local drivers (within a module */ +#define vpiLocalLoad 123 /* local loads (within a module */ +#define vpiOutTerm 124 /* To get to a delay device's loads. */ +#define vpiPorts 125 /* Module port */ +#define vpiSimNet 126 /* simulated net after collapsing */ +#define vpiTaskFunc 127 /* HDL task or function */ + +/************************ methods added with 1364-2005 ************************/ + +#define vpiBaseExpr 131 /* Indexed part-select's base expression */ +#define vpiWidthExpr 132 /* Indexed part-select's width expression */ + +/************************ methods added with 1800-2009 ************************/ + +#define vpiAutomatics 136 /* Automatic variables of a frame */ + +/********************************* PROPERTIES *********************************/ +/************************** generic object properties *************************/ + +#define vpiUndefined -1 /* undefined property */ +#define vpiType 1 /* type of object */ +#define vpiName 2 /* local name of object */ +#define vpiFullName 3 /* full hierarchical name */ +#define vpiSize 4 /* size of gate, net, port, etc. */ +#define vpiFile 5 /* File name in which the object is used*/ +#define vpiLineNo 6 /* line number where the object is used */ + +/***************************** module properties ******************************/ + +#define vpiTopModule 7 /* top-level module (boolean) */ +#define vpiCellInstance 8 /* cell (boolean) */ +#define vpiDefName 9 /* module definition name */ +#define vpiProtected 10 /* source protected module (boolean) */ +#define vpiTimeUnit 11 /* module time unit */ +#define vpiTimePrecision 12 /* module time precision */ +#define vpiDefNetType 13 /* default net type */ +#define vpiUnconnDrive 14 /* unconnected port drive strength */ +#define vpiHighZ 1 /* No default drive given */ +#define vpiPull1 2 /* default pull1 drive */ +#define vpiPull0 3 /* default pull0 drive */ +#define vpiDefFile 15 /* File name where the module is defined*/ +#define vpiDefLineNo 16 /* line number for module definition */ +#define vpiDefDelayMode 47 /* Default delay mode for a module */ +#define vpiDelayModeNone 1 /* no delay mode specified */ +#define vpiDelayModePath 2 /* path delay mode */ +#define vpiDelayModeDistrib 3 /* distributed delay mode */ +#define vpiDelayModeUnit 4 /* unit delay mode */ +#define vpiDelayModeZero 5 /* zero delay mode */ +#define vpiDelayModeMTM 6 /* min:typ:max delay mode */ +#define vpiDefDecayTime 48 /* Default decay time for a module */ + +/*************************** port and net properties **************************/ + +#define vpiScalar 17 /* scalar (boolean) */ +#define vpiVector 18 /* vector (boolean) */ +#define vpiExplicitName 19 /* port is explicitly named */ +#define vpiDirection 20 /* direction of port: */ +#define vpiInput 1 /* input */ +#define vpiOutput 2 /* output */ +#define vpiInout 3 /* inout */ +#define vpiMixedIO 4 /* mixed input-output */ +#define vpiNoDirection 5 /* no direction */ +#define vpiConnByName 21 /* connected by name (boolean) */ + +#define vpiNetType 22 /* net subtypes: */ +#define vpiWire 1 /* wire net */ +#define vpiWand 2 /* wire-and net */ +#define vpiWor 3 /* wire-or net */ +#define vpiTri 4 /* three-state net */ +#define vpiTri0 5 /* pull-down net */ +#define vpiTri1 6 /* pull-up net */ +#define vpiTriReg 7 /* tri state reg net */ +#define vpiTriAnd 8 /* three-state wire-and net */ +#define vpiTriOr 9 /* three-state wire-or net */ +#define vpiSupply1 10 /* supply 1 net */ +#define vpiSupply0 11 /* supply zero net */ +#define vpiNone 12 /* no default net type (1364-2001) */ +#define vpiUwire 13 /* unresolved wire net (1364-2005) */ + +#define vpiExplicitScalared 23 /* explicitly scalared (boolean) */ +#define vpiExplicitVectored 24 /* explicitly vectored (boolean) */ +#define vpiExpanded 25 /* expanded vector net (boolean) */ +#define vpiImplicitDecl 26 /* implicitly declared net (boolean) */ +#define vpiChargeStrength 27 /* charge decay strength of net */ + +/* Defined as part of strengths section. +#define vpiLargeCharge 0x10 +#define vpiMediumCharge 0x04 +#define vpiSmallCharge 0x02 +*/ + +#define vpiArray 28 /* variable array (boolean) */ +#define vpiPortIndex 29 /* Port index */ + +/************************ gate and terminal properties ************************/ + +#define vpiTermIndex 30 /* Index of a primitive terminal */ +#define vpiStrength0 31 /* 0-strength of net or gate */ +#define vpiStrength1 32 /* 1-strength of net or gate */ +#define vpiPrimType 33 /* prmitive subtypes: */ +#define vpiAndPrim 1 /* and gate */ +#define vpiNandPrim 2 /* nand gate */ +#define vpiNorPrim 3 /* nor gate */ +#define vpiOrPrim 4 /* or gate */ +#define vpiXorPrim 5 /* xor gate */ +#define vpiXnorPrim 6 /* xnor gate */ +#define vpiBufPrim 7 /* buffer */ +#define vpiNotPrim 8 /* not gate */ +#define vpiBufif0Prim 9 /* zero-enabled buffer */ +#define vpiBufif1Prim 10 /* one-enabled buffer */ +#define vpiNotif0Prim 11 /* zero-enabled not gate */ +#define vpiNotif1Prim 12 /* one-enabled not gate */ +#define vpiNmosPrim 13 /* nmos switch */ +#define vpiPmosPrim 14 /* pmos switch */ +#define vpiCmosPrim 15 /* cmos switch */ +#define vpiRnmosPrim 16 /* resistive nmos switch */ +#define vpiRpmosPrim 17 /* resistive pmos switch */ +#define vpiRcmosPrim 18 /* resistive cmos switch */ +#define vpiRtranPrim 19 /* resistive bidirectional */ +#define vpiRtranif0Prim 20 /* zero-enable resistive bidirectional */ +#define vpiRtranif1Prim 21 /* one-enable resistive bidirectional */ +#define vpiTranPrim 22 /* bidirectional */ +#define vpiTranif0Prim 23 /* zero-enabled bidirectional */ +#define vpiTranif1Prim 24 /* one-enabled bidirectional */ +#define vpiPullupPrim 25 /* pullup */ +#define vpiPulldownPrim 26 /* pulldown */ +#define vpiSeqPrim 27 /* sequential UDP */ +#define vpiCombPrim 28 /* combinational UDP */ + +/**************** path, path terminal, timing check properties ****************/ + +#define vpiPolarity 34 /* polarity of module path... */ +#define vpiDataPolarity 35 /* ...or data path: */ +#define vpiPositive 1 /* positive */ +#define vpiNegative 2 /* negative */ +#define vpiUnknown 3 /* unknown (unspecified) */ + +#define vpiEdge 36 /* edge type of module path: */ +#define vpiNoEdge 0x00 /* no edge */ +#define vpiEdge01 0x01 /* 0 -> 1 */ +#define vpiEdge10 0x02 /* 1 -> 0 */ +#define vpiEdge0x 0x04 /* 0 -> x */ +#define vpiEdgex1 0x08 /* x -> 1 */ +#define vpiEdge1x 0x10 /* 1 -> x */ +#define vpiEdgex0 0x20 /* x -> 0 */ +#define vpiPosedge (vpiEdgex1 | vpiEdge01 | vpiEdge0x) +#define vpiNegedge (vpiEdgex0 | vpiEdge10 | vpiEdge1x) +#define vpiAnyEdge (vpiPosedge | vpiNegedge) + +#define vpiPathType 37 /* path delay connection subtypes: */ +#define vpiPathFull 1 /* ( a *> b ) */ +#define vpiPathParallel 2 /* ( a => b ) */ + +#define vpiTchkType 38 /* timing check subtypes: */ +#define vpiSetup 1 /* $setup */ +#define vpiHold 2 /* $hold */ +#define vpiPeriod 3 /* $period */ +#define vpiWidth 4 /* $width */ +#define vpiSkew 5 /* $skew */ +#define vpiRecovery 6 /* $recovery */ +#define vpiNoChange 7 /* $nochange */ +#define vpiSetupHold 8 /* $setuphold */ +#define vpiFullskew 9 /* $fullskew -- added for 1364-2001 */ +#define vpiRecrem 10 /* $recrem -- added for 1364-2001 */ +#define vpiRemoval 11 /* $removal -- added for 1364-2001 */ +#define vpiTimeskew 12 /* $timeskew -- added for 1364-2001 */ + +/**************************** expression properties ***************************/ + +#define vpiOpType 39 /* operation subtypes: */ +#define vpiMinusOp 1 /* unary minus */ +#define vpiPlusOp 2 /* unary plus */ +#define vpiNotOp 3 /* unary not */ +#define vpiBitNegOp 4 /* bitwise negation */ +#define vpiUnaryAndOp 5 /* bitwise reduction and */ +#define vpiUnaryNandOp 6 /* bitwise reduction nand */ +#define vpiUnaryOrOp 7 /* bitwise reduction or */ +#define vpiUnaryNorOp 8 /* bitwise reduction nor */ +#define vpiUnaryXorOp 9 /* bitwise reduction xor */ +#define vpiUnaryXNorOp 10 /* bitwise reduction xnor */ +#define vpiSubOp 11 /* binary subtraction */ +#define vpiDivOp 12 /* binary division */ +#define vpiModOp 13 /* binary modulus */ +#define vpiEqOp 14 /* binary equality */ +#define vpiNeqOp 15 /* binary inequality */ +#define vpiCaseEqOp 16 /* case (x and z) equality */ +#define vpiCaseNeqOp 17 /* case inequality */ +#define vpiGtOp 18 /* binary greater than */ +#define vpiGeOp 19 /* binary greater than or equal */ +#define vpiLtOp 20 /* binary less than */ +#define vpiLeOp 21 /* binary less than or equal */ +#define vpiLShiftOp 22 /* binary left shift */ +#define vpiRShiftOp 23 /* binary right shift */ +#define vpiAddOp 24 /* binary addition */ +#define vpiMultOp 25 /* binary multiplication */ +#define vpiLogAndOp 26 /* binary logical and */ +#define vpiLogOrOp 27 /* binary logical or */ +#define vpiBitAndOp 28 /* binary bitwise and */ +#define vpiBitOrOp 29 /* binary bitwise or */ +#define vpiBitXorOp 30 /* binary bitwise xor */ +#define vpiBitXNorOp 31 /* binary bitwise xnor */ +#define vpiBitXnorOp vpiBitXNorOp /* added with 1364-2001 */ +#define vpiConditionOp 32 /* ternary conditional */ +#define vpiConcatOp 33 /* n-ary concatenation */ +#define vpiMultiConcatOp 34 /* repeated concatenation */ +#define vpiEventOrOp 35 /* event or */ +#define vpiNullOp 36 /* null operation */ +#define vpiListOp 37 /* list of expressions */ +#define vpiMinTypMaxOp 38 /* min:typ:max: delay expression */ +#define vpiPosedgeOp 39 /* posedge */ +#define vpiNegedgeOp 40 /* negedge */ +#define vpiArithLShiftOp 41 /* arithmetic left shift (1364-2001) */ +#define vpiArithRShiftOp 42 /* arithmetic right shift (1364-2001) */ +#define vpiPowerOp 43 /* arithmetic power op (1364-2001) */ + +#define vpiConstType 40 /* constant subtypes: */ +#define vpiDecConst 1 /* decimal integer */ +#define vpiRealConst 2 /* real */ +#define vpiBinaryConst 3 /* binary integer */ +#define vpiOctConst 4 /* octal integer */ +#define vpiHexConst 5 /* hexadecimal integer */ +#define vpiStringConst 6 /* string literal */ +#define vpiIntConst 7 /* HDL integer constant (1364-2001) */ +#define vpiTimeConst 8 /* time constant */ + +#define vpiBlocking 41 /* blocking assignment (boolean) */ +#define vpiCaseType 42 /* case statement subtypes: */ +#define vpiCaseExact 1 /* exact match */ +#define vpiCaseX 2 /* ignore X's */ +#define vpiCaseZ 3 /* ignore Z's */ +#define vpiNetDeclAssign 43 /* assign part of decl (boolean) */ + +/************************** task/function properties **************************/ + +#define vpiFuncType 44 /* HDL function & system function type */ +#define vpiIntFunc 1 /* returns integer */ +#define vpiRealFunc 2 /* returns real */ +#define vpiTimeFunc 3 /* returns time */ +#define vpiSizedFunc 4 /* returns an arbitrary size */ +#define vpiSizedSignedFunc 5 /* returns sized signed value */ + +/** alias 1364-1995 system function subtypes to 1364-2001 function subtypes ***/ + +#define vpiSysFuncType vpiFuncType +#define vpiSysFuncInt vpiIntFunc +#define vpiSysFuncReal vpiRealFunc +#define vpiSysFuncTime vpiTimeFunc +#define vpiSysFuncSized vpiSizedFunc + +#define vpiUserDefn 45 /*user defined system task/func(boolean)*/ +#define vpiScheduled 46 /* object still scheduled (boolean) */ + +/*********************** properties added with 1364-2001 **********************/ + +#define vpiActive 49 /* reentrant task/func frame is active */ +#define vpiAutomatic 50 /* task/func obj is automatic */ +#define vpiCell 51 /* configuration cell */ +#define vpiConfig 52 /* configuration config file */ +#define vpiConstantSelect 53 /* (boolean) bit-select or part-select + indices are constant expressions */ +#define vpiDecompile 54 /* decompile the object */ +#define vpiDefAttribute 55 /* Attribute defined for the obj */ +#define vpiDelayType 56 /* delay subtype */ +#define vpiModPathDelay 1 /* module path delay */ +#define vpiInterModPathDelay 2 /* intermodule path delay */ +#define vpiMIPDelay 3 /* module input port delay */ +#define vpiIteratorType 57 /* object type of an iterator */ +#define vpiLibrary 58 /* configuration library */ +#define vpiMultiArray 59 /* Object is a multidimensional array */ +#define vpiOffset 60 /* offset from LSB */ +#define vpiResolvedNetType 61 /* net subtype after resolution, returns + same subtypes as vpiNetType */ +#define vpiSaveRestartID 62 /* unique ID for save/restart data */ +#define vpiSaveRestartLocation 63 /* name of save/restart data file */ +#define vpiValid 64 /* reentrant task/func frame or automatic + variable is valid */ +#define vpiValidFalse 0 +#define vpiValidTrue 1 +#define vpiSigned 65 /* TRUE for vpiIODecl and any object in + the expression class if the object + has the signed attribute */ +#define vpiLocalParam 70 /* TRUE when a param is declared as a + localparam */ +#define vpiModPathHasIfNone 71 /* Mod path has an ifnone statement */ + +/*********************** properties added with 1364-2005 **********************/ + +#define vpiIndexedPartSelectType 72 /* Indexed part-select type */ +#define vpiPosIndexed 1 /* +: */ +#define vpiNegIndexed 2 /* -: */ +#define vpiIsMemory 73 /* TRUE for a one-dimensional reg array */ +#define vpiIsProtected 74 /* TRUE for protected design information */ + +/*************** vpi_control() constants (added with 1364-2001) ***************/ + +#define vpiStop 66 /* execute simulator's $stop */ +#define vpiFinish 67 /* execute simulator's $finish */ +#define vpiReset 68 /* execute simulator's $reset */ +#define vpiSetInteractiveScope 69 /* set simulator's interactive scope */ + +/**************************** I/O related defines *****************************/ + +#define VPI_MCD_STDOUT 0x00000001 + +/*************************** STRUCTURE DEFINITIONS ****************************/ + +/******************************* time structure *******************************/ typedef struct t_vpi_time { - int32_t type; /* [vpiScaledRealTime, - vpiSimTime, - vpiSuppressTime] */ - int32_t high; /* vpiSimTime, high */ - int32_t low; /* vpiSimTime, low */ - double real; /* vpiScaledRealTime */ + PLI_INT32 type; /* [vpiScaledRealTime, vpiSimTime, + vpiSuppressTime] */ + PLI_UINT32 high, low; /* for vpiSimTime */ + double real; /* for vpiScaledRealTime */ } s_vpi_time, *p_vpi_time; /* time types */ + #define vpiScaledRealTime 1 #define vpiSimTime 2 #define vpiSuppressTime 3 -/* VPI Simulator information */ -typedef struct t_vpi_vlog_info +/****************************** delay structures ******************************/ + +typedef struct t_vpi_delay { - int32_t argc; - char **argv; - char *product; - char *version; -} s_vpi_vlog_info, *p_vpi_vlog_info; + struct t_vpi_time *da; /* pointer to user allocated array of + delay values */ + PLI_INT32 no_of_delays; /* number of delays */ + PLI_INT32 time_type; /* [vpiScaledRealTime, vpiSimTime, + vpiSuppressTime] */ + PLI_INT32 mtm_flag; /* true for mtm values */ + PLI_INT32 append_flag; /* true for append */ + PLI_INT32 pulsere_flag; /* true for pulsere values */ +} s_vpi_delay, *p_vpi_delay; + +/***************************** value structures *******************************/ + +/* vector value */ + +#ifndef VPI_VECVAL /* added in 1364-2005 */ +#define VPI_VECVAL + +typedef struct t_vpi_vecval +{ + /* following fields are repeated enough times to contain vector */ +PLI_INT32 aval, bval; /* bit encoding: ab: 00=0, 10=1, 11=X, 01=Z */ +} s_vpi_vecval, *p_vpi_vecval; + +#endif + +/* strength (scalar) value */ + +typedef struct t_vpi_strengthval +{ + PLI_INT32 logic; /* vpi[0,1,X,Z] */ + PLI_INT32 s0, s1; /* refer to strength coding below */ +} s_vpi_strengthval, *p_vpi_strengthval; + +/* strength values */ + +#define vpiSupplyDrive 0x80 +#define vpiStrongDrive 0x40 +#define vpiPullDrive 0x20 +#define vpiWeakDrive 0x08 +#define vpiLargeCharge 0x10 +#define vpiMediumCharge 0x04 +#define vpiSmallCharge 0x02 +#define vpiHiZ 0x01 /* generic value */ + typedef struct t_vpi_value { - int32_t format; /* vpi[[Bin, - Oct, - Dec, - Hex]Str, - Scalar, - Int, - Real, - String, - Vector, - Strength, - Suppress, - Time, - ObjType]Val */ - union + PLI_INT32 format; /* vpi[[Bin,Oct,Dec,Hex]Str,Scalar,Int,Real,String, + Vector,Strength,Suppress,Time,ObjType]Val */ + union { - char *str; /* string value */ - int32_t scalar; /* vpi[0,1,X,Z] */ - int32_t integer; /* integer value */ - double real; /* real value */ - struct t_vpi_time *time; /* time value */ - struct t_vpi_vecval *vector; /* vector value */ - struct t_vpi_strengthval *strength; /* strength value */ - void *p_agg_value_handle; /* agg valHandle */ + PLI_BYTE8 *str; /* string value */ + PLI_INT32 scalar; /* vpi[0,1,X,Z] */ + PLI_INT32 integer; /* integer value */ + double real; /* real value */ + struct t_vpi_time *time; /* time value */ + struct t_vpi_vecval *vector; /* vector value */ + struct t_vpi_strengthval *strength; /* strength value */ + PLI_BYTE8 *misc; /* ...other */ } value; } s_vpi_value, *p_vpi_value; +typedef struct t_vpi_arrayvalue +{ + PLI_UINT32 format; + PLI_UINT32 flags; + union + { + PLI_INT32 *integers; + PLI_INT16 *shortints; + PLI_INT64 *longints; + PLI_BYTE8 *rawvals; + struct t_vpi_vecval *vectors; + struct t_vpi_time *times; + double *reals; + float *shortreals; + } value; +} s_vpi_arrayvalue, *p_vpi_arrayvalue; + /* value formats */ + #define vpiBinStrVal 1 #define vpiOctStrVal 2 #define vpiDecStrVal 3 @@ -269,13 +684,43 @@ typedef struct t_vpi_value #define vpiTimeVal 11 #define vpiObjTypeVal 12 #define vpiSuppressVal 13 -#define vpiShortIntVal 14 -#define vpiLongIntVal 15 +#define vpiShortIntVal 14 +#define vpiLongIntVal 15 #define vpiShortRealVal 16 #define vpiRawTwoStateVal 17 #define vpiRawFourStateVal 18 +/* delay modes */ + +#define vpiNoDelay 1 +#define vpiInertialDelay 2 +#define vpiTransportDelay 3 +#define vpiPureTransportDelay 4 + +/* force and release flags */ + +#define vpiForceFlag 5 +#define vpiReleaseFlag 6 + +/* scheduled event cancel flag */ + +#define vpiCancelEvent 7 + +/* bit mask for the flags argument to vpi_put_value() */ + +#define vpiReturnEvent 0x1000 + +/* bit flags for vpi_get_value_array flags field */ + +#define vpiUserAllocFlag 0x2000 + +/* bit flags for vpi_put_value_array flags field */ + +#define vpiOneValue 0x4000 +#define vpiPropagateOff 0x8000 + /* scalar values */ + #define vpi0 0 #define vpi1 1 #define vpiZ 2 @@ -283,39 +728,103 @@ typedef struct t_vpi_value #define vpiH 4 #define vpiL 5 #define vpiDontCare 6 +/* +#define vpiNoChange 7 Defined under vpiTchkType, but + can be used here. +*/ -/* properties */ -#define vpiFile 5 -#define vpiLineNo 6 +/*********************** system task/function structure ***********************/ -#define vpiUnknown 3 +typedef struct t_vpi_systf_data +{ + PLI_INT32 type; /* vpiSysTask, vpiSysFunc */ + PLI_INT32 sysfunctype; /* vpiSysTask, vpi[Int,Real,Time,Sized, + SizedSigned]Func */ + const PLI_BYTE8 *tfname; /* first character must be '$' */ + PLI_INT32 (*calltf)(PLI_BYTE8 *); + PLI_INT32 (*compiletf)(PLI_BYTE8 *); + PLI_INT32 (*sizetf)(PLI_BYTE8 *); /* for sized function callbacks only */ + PLI_BYTE8 *user_data; +} s_vpi_systf_data, *p_vpi_systf_data; + +#define vpiSysTask 1 +#define vpiSysFunc 2 + +/* the subtypes are defined under the vpiFuncType property */ + +/****************** Verilog execution information structure *******************/ + +typedef struct t_vpi_vlog_info +{ + PLI_INT32 argc; + PLI_BYTE8 **argv; + PLI_BYTE8 *product; + PLI_BYTE8 *version; +} s_vpi_vlog_info, *p_vpi_vlog_info; + +/*********************** PLI error information structure **********************/ + +typedef struct t_vpi_error_info +{ + PLI_INT32 state; /* vpi[Compile,PLI,Run] */ + PLI_INT32 level; /* vpi[Notice,Warning,Error,System,Internal] */ + PLI_BYTE8 *message; + PLI_BYTE8 *product; + PLI_BYTE8 *code; + PLI_BYTE8 *file; + PLI_INT32 line; +} s_vpi_error_info, *p_vpi_error_info; + +/* state when error occurred */ + +#define vpiCompile 1 +#define vpiPLI 2 +#define vpiRun 3 + +/* error severity levels */ + +#define vpiNotice 1 +#define vpiWarning 2 +#define vpiError 3 +#define vpiSystem 4 +#define vpiInternal 5 + +/**************************** callback structures *****************************/ #define vpiTimePrecision 12 /* module time precision */ /* normal callback structure */ + typedef struct t_cb_data { - int32_t reason; /* callback reason */ - int32_t (*cb_rtn)(struct t_cb_data *); /* call routine */ - vpiHandle obj; /* trigger object */ - p_vpi_time time; /* callback time */ - p_vpi_value value; /* trigger object value */ - int32_t index; /* index of the memory word or - var select that changed */ - char *user_data; + PLI_INT32 reason; /* callback reason */ + PLI_INT32 (*cb_rtn)(struct t_cb_data *); /* call routine */ + vpiHandle obj; /* trigger object */ + p_vpi_time time; /* callback time */ + p_vpi_value value; /* trigger object value */ + PLI_INT32 index; /* index of the memory word or + var select that changed */ + PLI_BYTE8 *user_data; } s_cb_data, *p_cb_data; +/****************************** CALLBACK REASONS ******************************/ +/***************************** Simulation related *****************************/ + #define cbValueChange 1 #define cbStmt 2 #define cbForce 3 #define cbRelease 4 +/******************************** Time related ********************************/ + #define cbAtStartOfSimTime 5 #define cbReadWriteSynch 6 #define cbReadOnlySynch 7 #define cbNextSimTime 8 #define cbAfterDelay 9 +/******************************* Action related *******************************/ + #define cbEndOfCompile 10 #define cbStartOfSimulation 11 #define cbEndOfSimulation 12 @@ -332,105 +841,165 @@ typedef struct t_cb_data #define cbInteractiveScopeChange 23 #define cbUnresolvedSystf 24 -/* Object Types */ -#define vpiMember 742 - -/* error severity levels */ -#define vpiNotice 1 -#define vpiWarning 2 -#define vpiError 3 -#define vpiSystem 4 -#define vpiInternal 5 - -typedef struct t_vpi_error_info -{ - int32_t state; - int32_t level; - char *message; - char *product; - char *code; - char *file; - int32_t line; -} s_vpi_error_info, *p_vpi_error_info; - - -typedef struct t_vpi_systf_data { - int32_t type; - int32_t sysfunctype; - const char *tfname; - int32_t (*calltf) (char*); - int32_t (*compiletf)(char*); - int32_t (*sizetf) (char*); - char *user_data; -} s_vpi_systf_data, *p_vpi_systf_data; - -#define vpiSysTask 1 -#define vpiSysFunc 2 -#define vpiIntFunc 1 -#define vpiSysTfCall 85 -#define vpiArgument 89 - - -extern VPI_DLLISPEC vpiHandle vpi_register_cb(p_cb_data cb_data_p); - -extern VPI_DLLISPEC int32_t vpi_remove_cb(vpiHandle cb_obj); - -extern VPI_DLLISPEC vpiHandle vpi_handle_by_name(char *name, - vpiHandle scope); - -extern VPI_DLLISPEC vpiHandle vpi_handle_by_index(vpiHandle object, - int32_t indx); - -extern VPI_DLLISPEC vpiHandle vpi_handle(int32_t type, - vpiHandle refHandle); - -extern VPI_DLLISPEC vpiHandle vpi_iterate(int32_t type, - vpiHandle refHandle); - -extern VPI_DLLISPEC vpiHandle vpi_scan(vpiHandle iterator); - -extern VPI_DLLISPEC char *vpi_get_str(int32_t property, - vpiHandle object); - -extern VPI_DLLISPEC void vpi_get_value(vpiHandle expr, - p_vpi_value value_p); - -extern VPI_DLLISPEC vpiHandle vpi_put_value(vpiHandle object, - p_vpi_value value_p, - p_vpi_time time_p, - int32_t flags); - -extern VPI_DLLISPEC void vpi_get_time(vpiHandle object, - p_vpi_time time_p); - -extern VPI_DLLISPEC int32_t vpi_get(int property, - vpiHandle ref); - -extern VPI_DLLISPEC int32_t vpi_free_object(vpiHandle object); - -extern VPI_DLLISPEC int32_t vpi_control(int32_t operation, ...); -extern VPI_DLLISPEC vpiHandle vpi_handle_by_multi_index(vpiHandle obj, - int32_t num_index, - int32_t *index_array); - - -extern VPI_DLLISPEC int32_t vpi_chk_error(p_vpi_error_info); - -extern VPI_DLLISPEC int32_t vpi_get_vlog_info(p_vpi_vlog_info info_p); - -extern VPI_DLLISPEC vpiHandle vpi_register_systf(p_vpi_systf_data data_p); - -extern VPI_DLLISPEC int32_t vpi_printf(const char *fmt, ...) __attribute__((format (printf,1,2))); - -extern VPI_DLLISPEC void (*vlog_startup_routines[])(void); +/**************************** Added with 1364-2001 ****************************/ + +#define cbAssign 25 +#define cbDeassign 26 +#define cbDisable 27 +#define cbPLIError 28 +#define cbSignal 29 + +/**************************** Added with 1364-2005 ****************************/ +#define cbNBASynch 30 +#define cbAtEndOfSimTime 31 + + +/************************* FUNCTION DECLARATIONS **************************/ + +/* callback related */ + +XXTERN vpiHandle vpi_register_cb PROTO_PARAMS((p_cb_data cb_data_p)); +XXTERN PLI_INT32 vpi_remove_cb PROTO_PARAMS((vpiHandle cb_obj)); +XXTERN void vpi_get_cb_info PROTO_PARAMS((vpiHandle object, + p_cb_data cb_data_p)); +XXTERN vpiHandle vpi_register_systf PROTO_PARAMS((p_vpi_systf_data + systf_data_p)); +XXTERN void vpi_get_systf_info PROTO_PARAMS((vpiHandle object, + p_vpi_systf_data + systf_data_p)); + +/* for obtaining handles */ + +XXTERN vpiHandle vpi_handle_by_name PROTO_PARAMS((PLI_BYTE8 *name, + vpiHandle scope)); +XXTERN vpiHandle vpi_handle_by_index PROTO_PARAMS((vpiHandle object, + PLI_INT32 indx)); + +/* for traversing relationships */ + +XXTERN vpiHandle vpi_handle PROTO_PARAMS((PLI_INT32 type, + vpiHandle refHandle)); +XXTERN vpiHandle vpi_handle_multi PROTO_PARAMS((PLI_INT32 type, + vpiHandle refHandle1, + vpiHandle refHandle2, + ... )); +XXTERN vpiHandle vpi_iterate PROTO_PARAMS((PLI_INT32 type, + vpiHandle refHandle)); +XXTERN vpiHandle vpi_scan PROTO_PARAMS((vpiHandle iterator)); + +/* for processing properties */ + +XXTERN PLI_INT32 vpi_get PROTO_PARAMS((PLI_INT32 property, + vpiHandle object)); +XXTERN PLI_INT64 vpi_get64 PROTO_PARAMS((PLI_INT32 property, + vpiHandle object)); +XXTERN PLI_BYTE8 *vpi_get_str PROTO_PARAMS((PLI_INT32 property, + vpiHandle object)); + +/* delay processing */ + +XXTERN void vpi_get_delays PROTO_PARAMS((vpiHandle object, + p_vpi_delay delay_p)); +XXTERN void vpi_put_delays PROTO_PARAMS((vpiHandle object, + p_vpi_delay delay_p)); + +/* value processing */ + +XXTERN void vpi_get_value PROTO_PARAMS((vpiHandle expr, + p_vpi_value value_p)); +XXTERN vpiHandle vpi_put_value PROTO_PARAMS((vpiHandle object, + p_vpi_value value_p, + p_vpi_time time_p, + PLI_INT32 flags)); +XXTERN void vpi_get_value_array PROTO_PARAMS((vpiHandle expr, + p_vpi_arrayvalue arrayvalue_p, + PLI_INT32 *index_p, + PLI_UINT32 num)); + +XXTERN void vpi_put_value_array PROTO_PARAMS((vpiHandle object, + p_vpi_arrayvalue arrayvalue_p, + PLI_INT32 *index_p, + PLI_UINT32 num)); + +/* time processing */ + +XXTERN void vpi_get_time PROTO_PARAMS((vpiHandle object, + p_vpi_time time_p)); + +/* I/O routines */ + +XXTERN PLI_UINT32 vpi_mcd_open PROTO_PARAMS((const PLI_BYTE8 *fileName)); +XXTERN PLI_UINT32 vpi_mcd_close PROTO_PARAMS((PLI_UINT32 mcd)); +XXTERN PLI_BYTE8 *vpi_mcd_name PROTO_PARAMS((PLI_UINT32 cd)); +XXTERN PLI_INT32 vpi_mcd_printf PROTO_PARAMS((PLI_UINT32 mcd, + const PLI_BYTE8 *format, + ...)); +XXTERN PLI_INT32 vpi_printf PROTO_PARAMS((const PLI_BYTE8 *format, + ...)); + +/* utility routines */ + +XXTERN PLI_INT32 vpi_compare_objects PROTO_PARAMS((vpiHandle object1, + vpiHandle object2)); +XXTERN PLI_INT32 vpi_chk_error PROTO_PARAMS((p_vpi_error_info + error_info_p)); +XXTERN PLI_INT32 vpi_free_object PROTO_PARAMS((vpiHandle object)); +XXTERN PLI_INT32 vpi_release_handle PROTO_PARAMS((vpiHandle object)); +XXTERN PLI_INT32 vpi_get_vlog_info PROTO_PARAMS((p_vpi_vlog_info + vlog_info_p)); + +/* routines added with 1364-2001 */ + +XXTERN PLI_INT32 vpi_get_data PROTO_PARAMS((PLI_INT32 id, + PLI_BYTE8 *dataLoc, + PLI_INT32 numOfBytes)); +XXTERN PLI_INT32 vpi_put_data PROTO_PARAMS((PLI_INT32 id, + PLI_BYTE8 *dataLoc, + PLI_INT32 numOfBytes)); +XXTERN void *vpi_get_userdata PROTO_PARAMS((vpiHandle obj)); +XXTERN PLI_INT32 vpi_put_userdata PROTO_PARAMS((vpiHandle obj, + void *userdata)); +XXTERN PLI_INT32 vpi_vprintf PROTO_PARAMS((const PLI_BYTE8 *format, + va_list ap)); +XXTERN PLI_INT32 vpi_mcd_vprintf PROTO_PARAMS((PLI_UINT32 mcd, + const PLI_BYTE8 *format, + va_list ap)); +XXTERN PLI_INT32 vpi_flush PROTO_PARAMS((void)); +XXTERN PLI_INT32 vpi_mcd_flush PROTO_PARAMS((PLI_UINT32 mcd)); +XXTERN PLI_INT32 vpi_control PROTO_PARAMS((PLI_INT32 operation, + ...)); +XXTERN vpiHandle vpi_handle_by_multi_index PROTO_PARAMS((vpiHandle obj, + PLI_INT32 num_index, + PLI_INT32 *index_array)); + +/****************************** GLOBAL VARIABLES ******************************/ + +PLI_VEXTERN PLI_DLLESPEC void (*vlog_startup_routines[])(void); + + /* array of function pointers, last pointer should be null */ + +#undef PLI_EXTERN +#undef PLI_VEXTERN + +#ifdef VPI_USER_DEFINED_DLLISPEC +#undef VPI_USER_DEFINED_DLLISPEC +#undef PLI_DLLISPEC +#endif +#ifdef VPI_USER_DEFINED_DLLESPEC +#undef VPI_USER_DEFINED_DLLESPEC +#undef PLI_DLLESPEC +#endif -#ifdef VPI_DLL_LOCAL -#undef VPI_DLL_LOCAL -#undef VPI_DLLISPEC +#ifdef PLI_PROTOTYPES +#undef PLI_PROTOTYPES +#undef PROTO_PARAMS +#undef XXTERN +#undef EETERN #endif #ifdef __cplusplus } #endif -#endif /* COCOTB_VPI_USER_H_ */ +#endif /* VPI_USER_H */ + diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index dbf25c6d..cedab93c 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -29,7 +29,7 @@ #define COCOTB_VPI_IMPL_H_ #include "../gpi/gpi_priv.h" -#include +#include #include #include From 67190d01eaec5dcac9e94d2dd51b0e507a9f8e27 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Sat, 19 Mar 2016 13:40:11 -0700 Subject: [PATCH 1146/2656] Issue #393: Define SIM macro for compile (e.g. -DALDEC, -DIUS, etc.) and consistently define _BIN_DIR in all makefiles --- makefiles/Makefile.rules | 12 ++++++++--- makefiles/simulators/Makefile.aldec | 29 ++++++++++++++++++--------- makefiles/simulators/Makefile.cvc | 20 +++++++++++++++++-- makefiles/simulators/Makefile.ghdl | 22 ++++++++++++++++---- makefiles/simulators/Makefile.icarus | 30 ++++++++++++++++++---------- makefiles/simulators/Makefile.ius | 18 ++++++++++++++++- makefiles/simulators/Makefile.nvc | 22 +++++++++++++++++--- makefiles/simulators/Makefile.questa | 30 ++++++++++++++++++---------- makefiles/simulators/Makefile.vcs | 18 ++++++++++++++++- 9 files changed, 156 insertions(+), 45 deletions(-) diff --git a/makefiles/Makefile.rules b/makefiles/Makefile.rules index 46ce6cd4..3c03bb03 100644 --- a/makefiles/Makefile.rules +++ b/makefiles/Makefile.rules @@ -37,11 +37,17 @@ $(LIB_NAME)_OBJS:= $(patsubst %.c,$(LIB_OBJ_DIR)/%.o,$(filter %.c,$(SRCS))) $(LIB_NAME)_OBJS+= $(patsubst %.cpp,$(LIB_OBJ_DIR)/%.o,$(filter %.cpp,$(SRCS))) -ifeq ($(SIM),ius) -GCC_ARGS+= -DIUS -GXX_ARGS+= -DIUS +SIM_DEFINE := $(shell echo $(SIM) | tr a-z A-Z) + +# Use a common deine for Questa and Modelsim +ifeq ($(SIM_DEFINE),QUESTA) +SIM_DEFINE = MODELSIM endif +# Add Simulator Define for compilation +GCC_ARGS+= -D$(SIM_DEFINE) +GXX_ARGS+= -D$(SIM_DEFINE) + $(LIB_OBJ_DIR)/%.o: %.c gcc $(GCC_ARGS) -c $(INCLUDES) -o $@ $< diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index a0e8cfb9..56400d97 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -30,9 +30,27 @@ # Common Makefile for Aldec Riviera-PRO simulator ifeq ($(GUI),1) - CMD := riviera -nosplash + CMD_BIN := riviera else - CMD := vsimsa + CMD_BIN := vsimsa +endif + +ifdef ALDEC_BIN_DIR + CMD := $(shell which $(ALDEC_BIN_DIR)/$(CMD_BIN) 2>/dev/null) +else + # auto-detect bin dir from system path + CMD := $(shell which $(CMD_BIN) 2>/dev/null) +endif + +ifeq (, $(CMD)) + $(error "Unable to locate command >$(CMD_BIN)<") +else + ALDEC_BIN_DIR := $(shell dirname $(CMD)) + export ALDEC_BIN_DIR +endif + +ifeq ($(GUI),1) + CMD += -nosplash endif RTL_LIBRARY ?= work @@ -98,13 +116,6 @@ ifeq ($(COVERAGE),1) endif endif -# auto-detect riviera dir from system path -export ALDEC_BIN_DIR = $(shell dirname $(shell which riviera)) - -ifeq ($(ALDEC_BIN_DIR),) -$(error "Directory containing Riviera-Pro binaries must be included in system path") -endif - ifeq ($(OS),Msys) # Windows allows the situation where the libstc++ used at link time as diff --git a/makefiles/simulators/Makefile.cvc b/makefiles/simulators/Makefile.cvc index 6e5ffc7d..72331774 100644 --- a/makefiles/simulators/Makefile.cvc +++ b/makefiles/simulators/Makefile.cvc @@ -34,6 +34,22 @@ clean:: else +CMD_BIN := cvc64 + +ifdef CVC_BIN_DIR + CMD := $(shell which $(CVC_BIN_DIR)/$(CMD_BIN) 2>/dev/null) +else + # auto-detect bin dir from system path + CMD := $(shell which $(CMD_BIN) 2>/dev/null) +endif + +ifeq (, $(CMD)) + $(error "Unable to locate command >$(CMD_BIN)<") +else + CVC_BIN_DIR := $(shell dirname $(CMD)) + export CVC_BIN_DIR +endif + #only interpreted mode works for the moment CVC_ITERP ?= 1 @@ -45,7 +61,7 @@ endif $(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - cvc64 $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=libvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) + $(CMD) $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=libvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) # Execution phase ifeq ($(CVC_ITERP),1) @@ -62,7 +78,7 @@ ifeq ($(CVC_ITERP),1) debug: $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - gdb --args cvc64 $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=libvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) + gdb --args $(CMD) $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=libvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) else debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ diff --git a/makefiles/simulators/Makefile.ghdl b/makefiles/simulators/Makefile.ghdl index ac1ec652..fa686e2a 100644 --- a/makefiles/simulators/Makefile.ghdl +++ b/makefiles/simulators/Makefile.ghdl @@ -34,18 +34,32 @@ clean:: else -.PHONY: analyse +CMD_BIN := ghdl + +ifdef GHDL_BIN_DIR + CMD := $(shell which $(GHDL_BIN_DIR)/$(CMD_BIN) 2>/dev/null) +else + # auto-detect bin dir from system path + CMD := $(shell which $(CMD_BIN) 2>/dev/null) +endif -GHDL=ghdl +ifeq (, $(CMD)) + $(error "Unable to locate command >$(CMD_BIN)<") +else + GHDL_BIN_DIR := $(shell dirname $(CMD)) + export GHDL_BIN_DIR +endif + +.PHONY: analyse # Compilation phase analyse: $(VHDL_SOURCES) $(SIM_BUILD) - cd $(SIM_BUILD) && $(GHDL) -a $(VHDL_SOURCES) && $(GHDL) -e $(TOPLEVEL) + cd $(SIM_BUILD) && $(CMD) -a $(VHDL_SOURCES) && $(GHDL) -e $(TOPLEVEL) results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VPI_LIB) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(GHDL) -r --workdir=$(SIM_BUILD) $(TOPLEVEL) --vpi=$(LIB_DIR)/libvpi.$(LIB_EXT) + $(CMD) -r --workdir=$(SIM_BUILD) $(TOPLEVEL) --vpi=$(LIB_DIR)/libvpi.$(LIB_EXT) clean:: -@rm -rf $(SIM_BUILD) diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index f4cdf25a..88ccdf39 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -36,22 +36,30 @@ clean:: else +CMD_BIN := iverilog + +ifdef ICARUS_BIN_DIR + CMD := $(shell which $(ICARUS_BIN_DIR)/$(CMD_BIN) 2>/dev/null) +else + # auto-detect bin dir from system path + CMD := $(shell which $(CMD_BIN) 2>/dev/null) +endif + +ifeq (, $(CMD)) + $(error "Unable to locate command >$(CMD_BIN)<") +else + ICARUS_BIN_DIR := $(shell dirname $(CMD)) + export ICARUS_BIN_DIR +endif + BUILD_VPI=1 # Compilation phase $(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) - iverilog -o $(SIM_BUILD)/sim.vvp -D COCOTB_SIM=1 $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) + $(CMD) -o $(SIM_BUILD)/sim.vvp -D COCOTB_SIM=1 $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) # Execution phase -# auto-detect modelsim dir from system path -export ICARUS_BIN_DIR = $(shell dirname $(shell which iverilog)) - -# make sure modelsim dir was found -ifeq ($(ICARUS_BIN_DIR),) -$(error "Directory containing Icarus binaries must be included in system path") -endif - ifeq ($(OS),Msys) EXTRA_LIBS := -lvpi @@ -68,12 +76,12 @@ endif results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) + $(CMD_EXE) -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - gdb --args vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) + gdb --args $(CMD_EXE) -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) clean:: -@rm -rf $(SIM_BUILD) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 83606c20..51591b81 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -29,6 +29,22 @@ # Common Makefile for Cadence Incisive +CMD_BIN := irun + +ifdef IUS_BIN_DIR + CMD := $(shell which $(IUS_BIN_DIR)/$(CMD_BIN) 2>/dev/null) +else + # auto-detect bin dir from system path + CMD := $(shell which $(CMD_BIN) 2>/dev/null) +endif + +ifeq (, $(CMD)) + $(error "Unable to locate command >$(CMD_BIN)<") +else + IUS_BIN_DIR := $(shell dirname $(CMD)) + export IUS_BIN_DIR +endif + ifneq ($(ARCH),i686) EXTRA_ARGS += -64 endif @@ -74,7 +90,7 @@ results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEP LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - irun $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) + $(CMD) $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) 2>&1 | tee $(SIM_BUILD)/sim.log clean:: @rm -rf $(SIM_BUILD) diff --git a/makefiles/simulators/Makefile.nvc b/makefiles/simulators/Makefile.nvc index 9b0ec5fd..f5d3c4b1 100644 --- a/makefiles/simulators/Makefile.nvc +++ b/makefiles/simulators/Makefile.nvc @@ -8,22 +8,38 @@ clean:: else +CMD_BIN := nvc + +ifdef NVC_BIN_DIR + CMD := $(shell which $(NVC_BIN_DIR)/$(CMD_BIN) 2>/dev/null) +else + # auto-detect bin dir from system path + CMD := $(shell which $(CMD_BIN) 2>/dev/null) +endif + +ifeq (, $(CMD)) + $(error "Unable to locate command >$(CMD_BIN)<") +else + NVC_BIN_DIR := $(shell dirname $(CMD)) + export NVC_BIN_DIR +endif + RTL_LIBRARY ?= work .PHONY: analyse # Compilation phase analyse: $(VHDL_SOURCES) $(SIM_BUILD) - cd $(SIM_BUILD) && nvc --work=$(RTL_LIBRARY) -a $(VHDL_SOURCES) + cd $(SIM_BUILD) && $(CMD) --work=$(RTL_LIBRARY) -a $(VHDL_SOURCES) results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) cd $(SIM_BUILD) && LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) \ - nvc --work=$(RTL_LIBRARY) -e $(TOPLEVEL) + $(CMD) --work=$(RTL_LIBRARY) -e $(TOPLEVEL) cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ LD_LIBRARY_PATH=$(LIB_DIR) \ - nvc --work=$(RTL_LIBRARY) -r --load $(COCOTB_VHPI_LIB) $(TOPLEVEL) + $(CMD) --work=$(RTL_LIBRARY) -r --load $(COCOTB_VHPI_LIB) $(TOPLEVEL) clean:: -@rm -rf $(SIM_BUILD) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 5a2e3690..414d604e 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -27,6 +27,22 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +CMD_BIN := vsim + +ifdef MODELSIM_BIN_DIR + CMD := $(shell which $(MODELSIM_BIN_DIR)/$(CMD_BIN) 2>/dev/null) +else + # auto-detect bin dir from system path + CMD := $(shell which $(CMD_BIN) 2>/dev/null) +endif + +ifeq (, $(CMD)) + $(error "Unable to locate command >$(CMD_BIN)<") +else + MODELSIM_BIN_DIR := $(shell dirname $(CMD)) + export MODELSIM_BIN_DIR +endif + RTL_LIBRARY ?= work ifdef VERILOG_INCLUDE_DIRS @@ -38,10 +54,10 @@ VLOG_ARGS += $(EXTRA_ARGS) endif ifeq ($(GUI),1) -SIM_CMD = vsim -gui +CMD += -gui VSIM_ARGS += -onfinish stop else -SIM_CMD = vsim -c +CMD += -c VSIM_ARGS += -onfinish exit endif @@ -87,14 +103,6 @@ else echo "quit" >> $@ endif -# auto-detect modelsim dir from system path -export MODELSIM_BIN_DIR = $(shell dirname $(shell which vsim)) - -# make sure modelsim dir was found -ifeq ($(MODELSIM_BIN_DIR),) -$(error "Directory containing ModelSim binaries must be included in system path") -endif - ifeq ($(OS),Msys) # Windows allows the situation where the libstc++ used at link time as @@ -123,7 +131,7 @@ endif results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) \ - $(SIM_CMD) -do runsim.do 2>&1 | tee sim.log + $(CMD) -do runsim.do 2>&1 | tee sim.log clean:: -rm -rf $(SIM_BUILD) diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index a6e77e38..0c5daf36 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -35,6 +35,22 @@ clean:: else +CMD_BIN := vcs + +ifdef VCS_BIN_DIR + CMD := $(shell which $(VCS_BIN_DIR)/$(CMD_BIN) 2>/dev/null) +else + # auto-detect bin dir from system path + CMD := $(shell which $(CMD_BIN) 2>/dev/null) +endif + +ifeq (, $(CMD)) + $(error "Unable to locate command >$(CMD_BIN)<") +else + VCS_BIN_DIR := $(shell dirname $(CMD)) + export VCS_BIN_DIR +endif + ifeq ($(ARCH),x86_64) EXTRA_ARGS += -full64 endif @@ -51,7 +67,7 @@ $(SIM_BUILD)/pli.tab : $(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(COCOTB_LIBS) $(COCOTB_VPI_LIB) $(CUSTOM_COMPILE_DEPS) cd $(SIM_BUILD) && \ LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) TOPLEVEL=$(TOPLEVEL) \ - vcs +acc+1 +vpi -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -debug -load libvpi.so $(COMPILE_ARGS) $(VERILOG_SOURCES) + $(CMD) +acc+1 +vpi -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -debug -load libvpi.so $(COMPILE_ARGS) $(VERILOG_SOURCES) # Execution phase results.xml: $(SIM_BUILD)/simv $(PYTHON_FILES) $(CUSTOM_SIM_DEPS) From 0135b2a3c5169252d1507dea109061b93b2f3ee9 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 21 Mar 2016 14:48:10 -0700 Subject: [PATCH 1147/2656] Issue #412: Updates to fix test issue_142 for VHPI (impacts FLI as well) --- lib/fli/FliObjHdl.cpp | 59 ++++--------------------- lib/vhpi/VhpiCbHdl.cpp | 45 +++++-------------- tests/test_cases/issue_142/issue_142.py | 2 +- 3 files changed, 21 insertions(+), 85 deletions(-) diff --git a/lib/fli/FliObjHdl.cpp b/lib/fli/FliObjHdl.cpp index a6bcf4c8..4a35d232 100644 --- a/lib/fli/FliObjHdl.cpp +++ b/lib/fli/FliObjHdl.cpp @@ -279,42 +279,13 @@ int FliLogicObjHdl::set_signal_value(const long value) mti_SetSignalValue(get_handle(), enumVal); } } else { - int valLen = sizeof(value) * 8; - mtiInt32T enumVal; - int numPad; - int idx; - - numPad = valLen < m_num_elems ? m_num_elems-valLen : 0; - valLen = valLen > m_num_elems ? m_num_elems : valLen; - LOG_DEBUG("set_signal_value(long)::0x%016x", value); + for (int i = 0, idx = m_num_elems-1; i < m_num_elems; i++, idx--) { + mtiInt32T enumVal = value&(1L<(), (long)m_mti_buff); } else { @@ -336,37 +307,23 @@ int FliLogicObjHdl::set_signal_value(std::string &value) mti_SetSignalValue(get_handle(), enumVal); } } else { - int len = value.length(); - int numPad; - - numPad = len < m_num_elems ? m_num_elems-len : 0; + + if ((int)value.length() != m_num_elems) { + LOG_ERROR("FLI: Unable to set logic vector due to the string having incorrect length. Length of %d needs to be %d", value.length(), m_num_elems); + return -1; + } LOG_DEBUG("set_signal_value(string)::%s", value.c_str()); - if (len > m_num_elems) { - LOG_DEBUG("FLI: Attempt to write sting longer than (%s) signal %d > %d", m_name.c_str(), len, m_num_elems); - len = m_num_elems; - } - mtiInt32T enumVal; std::string::iterator valIter; int i = 0; - // Pad MSB for descending vector - for (i = 0; i < numPad && !m_ascending; i++) { - m_mti_buff[i] = (char)m_enum_map['0']; - } - for (valIter = value.begin(); (valIter != value.end()) && (i < m_num_elems); valIter++, i++) { enumVal = m_enum_map[*valIter]; m_mti_buff[i] = (char)enumVal; } - // Fill bits a the end of the value to 0's - for (i = len; i < m_num_elems && m_ascending; i++) { - m_mti_buff[i] = (char)m_enum_map['0']; - } - if (m_is_var) { mti_SetVarValue(get_handle(), (long)m_mti_buff); } else { diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index cdc0e508..77fc779a 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -262,7 +262,7 @@ int VhpiLogicSignalObjHdl::set_signal_value(long value) case vhpiLogicVecVal: { int i; for (i=0; i m_num_elems) { - LOG_DEBUG("VHPI: Attempt to write string longer than (%s) signal %d > %d", - m_name.c_str(), len, m_num_elems); - m_value.numElems = m_num_elems; + if ((int)value.length() != m_num_elems) { + LOG_ERROR("VHPI: Unable to set logic vector due to the string having incorrect length. Length of %d needs to be %d", value.length(), m_num_elems); + return -1; } + m_value.numElems = m_num_elems; + std::string::iterator iter; int i = 0; @@ -315,11 +310,6 @@ int VhpiLogicSignalObjHdl::set_signal_value(std::string &value) m_value.value.enumvs[i] = chr2vhpi(*iter); } - // Fill bits at the end of the value to 0's - for (i = len; i < m_num_elems; i++) { - m_value.value.enumvs[i] = vhpi0; - } - break; } @@ -345,7 +335,7 @@ int VhpiSignalObjHdl::set_signal_value(long value) case vhpiLogicVecVal: { int i; for (i=0; i m_num_elems) { - LOG_DEBUG("VHPI: Attempt to write string longer than (%s) signal %d > %d", - m_name.c_str(), len, m_num_elems); - m_value.numElems = m_num_elems; + if ((int)value.length() != m_num_elems) { + LOG_ERROR("VHPI: Unable to set logic vector due to the string having incorrect length. Length of %d needs to be %d", value.length(), m_num_elems); + return -1; } + m_value.numElems = m_num_elems; + std::string::iterator iter; int i = 0; @@ -443,11 +427,6 @@ int VhpiSignalObjHdl::set_signal_value(std::string &value) m_value.value.enumvs[i] = chr2vhpi(*iter); } - // Fill bits at the end of the value to 0's - for (i = len; i < m_num_elems; i++) { - m_value.value.enumvs[i] = vhpi0; - } - break; } diff --git a/tests/test_cases/issue_142/issue_142.py b/tests/test_cases/issue_142/issue_142.py index 43273ef1..1bc31461 100644 --- a/tests/test_cases/issue_142/issue_142.py +++ b/tests/test_cases/issue_142/issue_142.py @@ -20,7 +20,7 @@ def _compare(value): str(dut.stream_in_data_wide))) # Wider values are transparently converted to BinaryValues - for value in [0, 0x7FFFFFFF, 0x7FFFFFFFFFFF, BinaryValue(0x7FFFFFFFFFFFFF)]: + for value in [0, 0x7FFFFFFF, 0x7FFFFFFFFFFF, BinaryValue(0x7FFFFFFFFFFFFF,len(dut.stream_in_data_wide),bigEndian=False)]: dut.stream_in_data_wide <= value yield RisingEdge(dut.clk) From e5cb3602c5b61c0c6815bbfa7db68d3fb68ef728 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 21 Mar 2016 17:09:10 -0700 Subject: [PATCH 1148/2656] Issue #393: Add new design and test placeholder for exercising arrays --- tests/designs/array_module/Makefile | 56 ++++ tests/designs/array_module/array_module.v | 199 ++++++++++++ tests/designs/array_module/array_module.vhd | 163 ++++++++++ .../array_module/array_module_aldec.vhd | 167 ++++++++++ .../array_module/array_module_pack.vhd | 54 ++++ tests/test_cases/test_array/Makefile | 33 ++ tests/test_cases/test_array/test_array.py | 288 ++++++++++++++++++ 7 files changed, 960 insertions(+) create mode 100644 tests/designs/array_module/Makefile create mode 100644 tests/designs/array_module/array_module.v create mode 100644 tests/designs/array_module/array_module.vhd create mode 100644 tests/designs/array_module/array_module_aldec.vhd create mode 100644 tests/designs/array_module/array_module_pack.vhd create mode 100644 tests/test_cases/test_array/Makefile create mode 100644 tests/test_cases/test_array/test_array.py diff --git a/tests/designs/array_module/Makefile b/tests/designs/array_module/Makefile new file mode 100644 index 00000000..7aa22c8f --- /dev/null +++ b/tests/designs/array_module/Makefile @@ -0,0 +1,56 @@ +############################################################################### +# Copyright (c) 2016 Potential Ventures Ltd +# Copyright (c) 2016 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +TOPLEVEL_LANG ?= verilog + +TOPLEVEL := array_module + +PWD=$(shell pwd) +COCOTB?=$(PWD)/../../.. + +ifeq ($(OS),Msys) +WPWD=$(shell sh -c 'pwd -W') +else +WPWD=$(shell pwd) +endif + +ifeq ($(TOPLEVEL_LANG),verilog) + VERILOG_SOURCES = $(COCOTB)/tests/designs/array_module/array_module.v +else ifeq ($(TOPLEVEL_LANG),vhdl) +ifeq ($(shell echo $(SIM) | tr A-Z a-z),aldec) + VHDL_SOURCES = $(COCOTB)/tests/designs/array_module/array_module_pack.vhd $(COCOTB)/tests/designs/array_module/array_module_aldec.vhd +else + VHDL_SOURCES = $(COCOTB)/tests/designs/array_module/array_module_pack.vhd $(COCOTB)/tests/designs/array_module/array_module.vhd +endif +else + $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") +endif + +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim diff --git a/tests/designs/array_module/array_module.v b/tests/designs/array_module/array_module.v new file mode 100644 index 00000000..c3c70028 --- /dev/null +++ b/tests/designs/array_module/array_module.v @@ -0,0 +1,199 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2016 Potential Ventures Ltd +// Copyright (c) 2016 SolarFlare Communications Inc +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Potential Ventures Ltd, +// Copyright (c) 2013 SolarFlare Communications Inc nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +`timescale 1 ps / 1 ps + +typedef struct { + logic a; + logic [7:0] b[0:2]; +} rec_type; + +module array_module ( + input clk, + + input integer select_in, + + input [7:0] port_desc_in, + input [0:7] port_asc_in, + input [1:8] port_ofst_in, + + output [7:0] port_desc_out, + output [0:7] port_asc_out, + output [1:8] port_ofst_out, + + output logic port_logic_out, + output logic [7:0] port_logic_vec_out, + //output bit port_bool_out, + //output integer port_int_out, + //output real port_real_out, + //output byte port_char_out, + //output string port_str_out, + output rec_type port_rec_out, + output rec_type port_cmplx_out[0:1] +); + +parameter logic param_logic = 1'b1; +parameter logic [7:0] param_logic_vec = 8'hDA; +//parameter bit param_bool = 1'b1; +//parameter integer param_int = 6; +//parameter real param_real = 3.14; +//parameter byte param_char = "p"; +//parameter string param_str = "ARRAYMOD"; +//parameter rec_type param_rec = '{a:'0, b:'{8'h00,8'h00,8'h00}}}; +//parameter rec_type param_cmplx [0:1] = '{'{a:'0, b:'{8'h00,8'h00,8'h00}}, '{a:'0, b:'{8'h00,8'h00,8'h00}}}; + +localparam logic const_logic = 1'b0; +localparam logic [7:0] const_logic_vec = 8'h3D; +//localparam bit const_bool = 1'b0; +//localparam integer const_int = 12; +//localparam real const_real = 6.28; +//localparam byte const_char = "c"; +//localparam string const_str = "MODARRAY"; +//localparam rec_type const_rec = '{a:'1, b:'{8'hFF,8'hFF,8'hFF}}}; +//localparam rec_type const_cmplx [1:2] = '{'{a:'1, b:'{8'hFF,8'hFF,8'hFF}}, '{a:'1, b:'{8'hFF,8'hFF,8'hFF}}}; + +wire [0:3] sig_t1; +wire [7:0] sig_t2[7:4]; +wire [7:0] sig_t3a[1:4]; +wire [7:0] sig_t3b[3:0]; +wire [7:0] sig_t4[0:3][7:4]; +wire [7:0] sig_t5[0:2][0:3]; +wire [7:0] sig_t6[0:1][2:4]; + +wire [16:23] sig_asc; +wire [23:16] sig_desc; +wire logic sig_logic; +wire logic [7:0] sig_logic_vec; +// bit sig_bool; +// integer sig_int; +// real sig_real; +// byte sig_char; +// string sig_str; + rec_type sig_rec; + rec_type sig_cmplx [0:1]; + +assign port_ofst_out = port_ofst_in; + +//assign port_rec_out = (select_in == 1) ? const_rec : (select_in == 2) ? sig_rec : param_rec; +//assign port_cmplx_out = (select_in == 1) ? const_cmplx : (select_in == 2) ? sig_cmplx : param_cmplx; + +always @(posedge clk) begin + if (select_in == 1) begin + port_logic_out = const_logic; + port_logic_vec_out = const_logic_vec; +// port_bool_out = const_bool; +// port_int_out = const_int; +// port_real_out = const_real; +// port_char_out = const_char; +// port_str_out = const_str; + port_rec_out.a = sig_rec.a; + port_rec_out.b[0] = sig_rec.b[0]; + port_rec_out.b[1] = sig_rec.b[1]; + port_rec_out.b[2] = sig_rec.b[2]; + port_cmplx_out[0].a = sig_cmplx[0].a; + port_cmplx_out[0].b[0] = sig_cmplx[0].b[0]; + port_cmplx_out[0].b[1] = sig_cmplx[0].b[1]; + port_cmplx_out[0].b[2] = sig_cmplx[0].b[2]; + port_cmplx_out[1].a = sig_cmplx[1].a; + port_cmplx_out[1].b[0] = sig_cmplx[1].b[0]; + port_cmplx_out[1].b[1] = sig_cmplx[1].b[1]; + port_cmplx_out[1].b[2] = sig_cmplx[1].b[2]; + end else begin + if (select_in == 2) begin + port_logic_out = sig_logic; + port_logic_vec_out = sig_logic_vec; +// port_bool_out = sig_bool; +// port_int_out = sig_int; +// port_real_out = sig_real; +// port_char_out = sig_char; +// port_str_out = sig_str; + port_rec_out.a = sig_rec.a; + port_rec_out.b[0] = sig_rec.b[0]; + port_rec_out.b[1] = sig_rec.b[1]; + port_rec_out.b[2] = sig_rec.b[2]; + port_cmplx_out[0].a = sig_cmplx[0].a; + port_cmplx_out[0].b[0] = sig_cmplx[0].b[0]; + port_cmplx_out[0].b[1] = sig_cmplx[0].b[1]; + port_cmplx_out[0].b[2] = sig_cmplx[0].b[2]; + port_cmplx_out[1].a = sig_cmplx[1].a; + port_cmplx_out[1].b[0] = sig_cmplx[1].b[0]; + port_cmplx_out[1].b[1] = sig_cmplx[1].b[1]; + port_cmplx_out[1].b[2] = sig_cmplx[1].b[2]; + end else begin + port_logic_out = param_logic; + port_logic_vec_out = param_logic_vec; +// port_bool_out = param_bool; +// port_int_out = param_int; +// port_real_out = param_real; +// port_char_out = param_char; +// port_str_out = param_str; + port_rec_out.a = sig_rec.a; + port_rec_out.b[0] = sig_rec.b[0]; + port_rec_out.b[1] = sig_rec.b[1]; + port_rec_out.b[2] = sig_rec.b[2]; + port_cmplx_out[0].a = sig_cmplx[0].a; + port_cmplx_out[0].b[0] = sig_cmplx[0].b[0]; + port_cmplx_out[0].b[1] = sig_cmplx[0].b[1]; + port_cmplx_out[0].b[2] = sig_cmplx[0].b[2]; + port_cmplx_out[1].a = sig_cmplx[1].a; + port_cmplx_out[1].b[0] = sig_cmplx[1].b[0]; + port_cmplx_out[1].b[1] = sig_cmplx[1].b[1]; + port_cmplx_out[1].b[2] = sig_cmplx[1].b[2]; + end + end +end + +genvar idx1; +generate +for (idx1 = 16; idx1 <= 23; idx1=idx1+1) begin:asc_gen + localparam OFFSET = 16-0; + reg sig; + always @(posedge clk) begin + sig <= port_asc_in[idx1-OFFSET]; + end + assign sig_asc[idx1] = sig; + assign port_asc_out[idx1-OFFSET] = sig_asc[idx1]; +end +endgenerate + +genvar idx2; +generate +for (idx2 = 7; idx2 >= 0; idx2=idx2-1) begin:desc_gen + localparam OFFSET = 23-7; + reg sig; + always @(posedge clk) begin + sig <= port_desc_in[idx2]; + end + assign sig_desc[idx2+OFFSET] = sig; + assign port_desc_out[idx2] = sig_desc[idx2+OFFSET]; +end +endgenerate + +endmodule + diff --git a/tests/designs/array_module/array_module.vhd b/tests/designs/array_module/array_module.vhd new file mode 100644 index 00000000..68a7ec95 --- /dev/null +++ b/tests/designs/array_module/array_module.vhd @@ -0,0 +1,163 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2016 Potential Ventures Ltd +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- * Redistributions of source code must retain the above copyright +-- notice, this list of conditions and the following disclaimer. +-- * Redistributions in binary form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- * Neither the name of Potential Ventures Ltd, +-- Copyright (c) 2013 SolarFlare Communications Inc nor the +-- names of its contributors may be used to endorse or promote products +-- derived from this software without specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +-- DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +-- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +-- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +------------------------------------------------------------------------------- + +library ieee; + +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library work; + +use work.array_module_pack.all; + +entity array_module is + generic ( + param_logic : std_logic := '1'; + param_logic_vec : std_logic_vector(7 downto 0) := X"DA"; + param_bool : boolean := TRUE; + param_int : integer := 6; + param_real : real := 3.14; + param_char : character := 'p'; + param_str : string(1 to 8) := "ARRAYMOD"; + param_rec : rec_type := REC_TYPE_ZERO; + param_cmplx : rec_array(0 to 1) := (others=>REC_TYPE_ZERO) + ); + port ( + clk : in std_logic; + + select_in : in integer; + + port_desc_in : in std_logic_vector(7 downto 0); + port_asc_in : in std_logic_vector(0 to 7); + port_ofst_in : in std_logic_vector(1 to 8); + + port_desc_out : out std_logic_vector(7 downto 0); + port_asc_out : out std_logic_vector(0 to 7); + port_ofst_out : out std_logic_vector(1 to 8); + + port_logic_out : out std_logic; + port_logic_vec_out : out std_logic_vector(7 downto 0); + port_bool_out : out boolean; + port_int_out : out integer; + port_real_out : out real; + port_char_out : out character; + port_str_out : out string(1 to 8); + port_rec_out : out rec_type; + port_cmplx_out : out rec_array(0 to 1) + ); +end; + +architecture impl of array_module is + constant const_logic : std_logic := '0'; + constant const_logic_vec : std_logic_vector(7 downto 0) := X"3D"; + constant const_bool : boolean := FALSE; + constant const_int : integer := 12; + constant const_real : real := 6.28; + constant const_char : character := 'c'; + constant const_str : string(1 to 8) := "MODARRAY"; + constant const_rec : rec_type := REC_TYPE_ONE; + constant const_cmplx : rec_array(1 to 2) := (others=>REC_TYPE_ONE); + + signal sig_desc : std_logic_vector(23 downto 16); + signal sig_asc : std_logic_vector(16 to 23); + + signal sig_t1 : t1; + signal sig_t2 : t2; + signal sig_t3a : t3(1 to 4); + signal sig_t3b : t3(3 downto 0); + signal sig_t4 : t4; + signal sig_t5 : t5; + signal sig_t6 : t6(0 to 1, 2 to 4); + + signal sig_logic : std_logic; + signal sig_logic_vec : std_logic_vector(7 downto 0); + signal sig_bool : boolean; + signal sig_int : integer; + signal sig_real : real; + signal sig_char : character; + signal sig_str : string(1 to 8); + signal sig_rec : rec_type; + signal sig_cmplx : rec_array(0 to 1); +begin + port_ofst_out <= port_ofst_in; + + sig_proc : process (clk) + begin + if (rising_edge(clk)) then + if (select_in = 1) then + port_logic_out <= const_logic; + port_logic_vec_out <= const_logic_vec; + port_bool_out <= const_bool; + port_int_out <= const_int; + port_real_out <= const_real; + port_char_out <= const_char; + port_str_out <= const_str; + port_rec_out <= const_rec; + port_cmplx_out <= const_cmplx; + elsif (select_in = 2) then + port_logic_out <= sig_logic; + port_logic_vec_out <= sig_logic_vec; + port_bool_out <= sig_bool; + port_int_out <= sig_int; + port_real_out <= sig_real; + port_char_out <= sig_char; + port_str_out <= sig_str; + port_rec_out <= sig_rec; + port_cmplx_out <= sig_cmplx; + else + port_logic_out <= param_logic; + port_logic_vec_out <= param_logic_vec; + port_bool_out <= param_bool; + port_int_out <= param_int; + port_real_out <= param_real; + port_char_out <= param_char; + port_str_out <= param_str; + port_rec_out <= param_rec; + port_cmplx_out <= param_cmplx; + end if; + end if; + end process sig_proc; + + asc_gen : for idx1 in sig_asc'range generate + constant OFFSET : natural := sig_asc'left - port_asc_in'left; + signal sig : std_logic; + begin + sig <= port_asc_in(idx1-OFFSET) when rising_edge(clk); + sig_asc(idx1) <= sig; + port_asc_out(idx1-OFFSET) <= sig_asc(idx1); + end generate asc_gen; + + desc_gen : for idx2 in port_desc_in'range generate + constant OFFSET : natural := sig_desc'left - port_desc_in'left; + signal sig : std_logic; + begin + sig <= port_desc_in(idx2) when rising_edge(clk); + sig_desc(idx2+OFFSET) <= sig; + port_desc_out(idx2) <= sig_desc(idx2+OFFSET); + end generate desc_gen; +end architecture; diff --git a/tests/designs/array_module/array_module_aldec.vhd b/tests/designs/array_module/array_module_aldec.vhd new file mode 100644 index 00000000..d2a96dcd --- /dev/null +++ b/tests/designs/array_module/array_module_aldec.vhd @@ -0,0 +1,167 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2016 Potential Ventures Ltd +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- * Redistributions of source code must retain the above copyright +-- notice, this list of conditions and the following disclaimer. +-- * Redistributions in binary form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- * Neither the name of Potential Ventures Ltd, +-- Copyright (c) 2013 SolarFlare Communications Inc nor the +-- names of its contributors may be used to endorse or promote products +-- derived from this software without specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +-- DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +-- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +-- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +------------------------------------------------------------------------------- + +library ieee; + +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library work; + +use work.array_module_pack.all; + +entity array_module is + generic ( + param_logic : std_logic := '1'; + param_logic_vec : std_logic_vector(7 downto 0) := X"DA"; + param_bool : boolean := TRUE; + param_int : integer := 6; + param_real : real := 3.14; + param_char : character := 'p'; + param_str : string(1 to 8) := "ARRAYMOD" --; + --param_rec : rec_type := REC_TYPE_ZERO; + --param_cmplx : rec_array(0 to 1) := (others=>REC_TYPE_ZERO) + ); + port ( + clk : in std_logic; + + select_in : in integer; + + port_desc_in : in std_logic_vector(7 downto 0); + port_asc_in : in std_logic_vector(0 to 7); + port_ofst_in : in std_logic_vector(1 to 8); + + port_desc_out : out std_logic_vector(7 downto 0); + port_asc_out : out std_logic_vector(0 to 7); + port_ofst_out : out std_logic_vector(1 to 8); + + port_logic_out : out std_logic; + port_logic_vec_out : out std_logic_vector(7 downto 0); + port_bool_out : out boolean; + port_int_out : out integer; + port_real_out : out real; + port_char_out : out character; + port_str_out : out string(1 to 8); + port_rec_out : out rec_type; + port_cmplx_out : out rec_array(0 to 1) + ); +end; + +architecture impl of array_module is + constant const_logic : std_logic := '0'; + constant const_logic_vec : std_logic_vector(7 downto 0) := X"3D"; + constant const_bool : boolean := FALSE; + constant const_int : integer := 12; + constant const_real : real := 6.28; + constant const_char : character := 'c'; + constant const_str : string(1 to 8) := "MODARRAY"; + --constant const_rec : rec_type := REC_TYPE_ONE; + --constant const_cmplx : rec_array(1 to 2) := (others=>REC_TYPE_ONE); + + signal sig_desc : std_logic_vector(23 downto 16); + signal sig_asc : std_logic_vector(16 to 23); + + signal sig_t1 : t1; + signal sig_t2 : t2; + signal sig_t3a : t3(1 to 4); + signal sig_t3b : t3(3 downto 0); + signal sig_t4 : t4; + signal sig_t5 : t5; + signal sig_t6 : t6(0 to 1, 2 to 4); + + signal sig_logic : std_logic; + signal sig_logic_vec : std_logic_vector(7 downto 0); + signal sig_bool : boolean; + signal sig_int : integer; + signal sig_real : real; + signal sig_char : character; + signal sig_str : string(1 to 8); + signal sig_rec : rec_type; + signal sig_cmplx : rec_array(0 to 1); +begin + port_ofst_out <= port_ofst_in; + + sig_proc : process (clk) + begin + if (rising_edge(clk)) then + if (select_in = 1) then + port_logic_out <= const_logic; + port_logic_vec_out <= const_logic_vec; + port_bool_out <= const_bool; + port_int_out <= const_int; + port_real_out <= const_real; + port_char_out <= const_char; + port_str_out <= const_str; + port_rec_out <= sig_rec; + port_cmplx_out <= sig_cmplx; + --port_rec_out <= const_rec; + --port_cmplx_out <= const_cmplx; + elsif (select_in = 2) then + port_logic_out <= sig_logic; + port_logic_vec_out <= sig_logic_vec; + port_bool_out <= sig_bool; + port_int_out <= sig_int; + port_real_out <= sig_real; + port_char_out <= sig_char; + port_str_out <= sig_str; + port_rec_out <= sig_rec; + port_cmplx_out <= sig_cmplx; + else + port_logic_out <= param_logic; + port_logic_vec_out <= param_logic_vec; + port_bool_out <= param_bool; + port_int_out <= param_int; + port_real_out <= param_real; + port_char_out <= param_char; + port_str_out <= param_str; + port_rec_out <= sig_rec; + port_cmplx_out <= sig_cmplx; + --port_rec_out <= param_rec; + --port_cmplx_out <= param_cmplx; + end if; + end if; + end process sig_proc; + + asc_gen : for idx1 in sig_asc'range generate + constant OFFSET : natural := sig_asc'left - port_asc_in'left; + signal sig : std_logic; + begin + sig <= port_asc_in(idx1-OFFSET) when rising_edge(clk); + sig_asc(idx1) <= sig; + port_asc_out(idx1-OFFSET) <= sig_asc(idx1); + end generate asc_gen; + + desc_gen : for idx2 in port_desc_in'range generate + constant OFFSET : natural := sig_desc'left - port_desc_in'left; + signal sig : std_logic; + begin + sig <= port_desc_in(idx2) when rising_edge(clk); + sig_desc(idx2+OFFSET) <= sig; + port_desc_out(idx2) <= sig_desc(idx2+OFFSET); + end generate desc_gen; +end architecture; diff --git a/tests/designs/array_module/array_module_pack.vhd b/tests/designs/array_module/array_module_pack.vhd new file mode 100644 index 00000000..48dd0cd5 --- /dev/null +++ b/tests/designs/array_module/array_module_pack.vhd @@ -0,0 +1,54 @@ +------------------------------------------------------------------------------- +-- Copyright (c) 2016 Potential Ventures Ltd +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- * Redistributions of source code must retain the above copyright +-- notice, this list of conditions and the following disclaimer. +-- * Redistributions in binary form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- * Neither the name of Potential Ventures Ltd, +-- Copyright (c) 2013 SolarFlare Communications Inc nor the +-- names of its contributors may be used to endorse or promote products +-- derived from this software without specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +-- DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +-- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +-- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +------------------------------------------------------------------------------- + +library ieee; + +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +package array_module_pack is + type t1 is array (0 to 3) of std_logic; + type t2 is array (7 downto 4) of std_logic_vector(7 downto 0); + type t3 is array (natural range <>) of std_logic_vector(7 downto 0); + type t4 is array (0 to 3) of t2; + type t5 is array (0 to 2, 0 to 3) of std_logic_vector(7 downto 0); + type t6 is array (natural range <>, natural range <>) of std_logic_vector(7 downto 0); + + type rec_type is record + a : std_logic; + b : t3(0 to 2); + end record rec_type; + type rec_array is array (natural range <>) of rec_type; + constant REC_TYPE_ZERO : rec_type := ('0', (others=>(others=>'0'))); + constant REC_TYPE_ONE : rec_type := ('1', (others=>(others=>'1'))); + +end package array_module_pack; + +package body array_module_pack is +end package body array_module_pack; + diff --git a/tests/test_cases/test_array/Makefile b/tests/test_cases/test_array/Makefile new file mode 100644 index 00000000..abec2cd0 --- /dev/null +++ b/tests/test_cases/test_array/Makefile @@ -0,0 +1,33 @@ +############################################################################### +# Copyright (c) 2016 Potential Ventures Ltd +# Copyright (c) 2016 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +include ../../designs/array_module/Makefile + +MODULE = test_array diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py new file mode 100644 index 00000000..21cbdcf4 --- /dev/null +++ b/tests/test_cases/test_array/test_array.py @@ -0,0 +1,288 @@ +""" +A set of tests that demonstrate Array structure support +""" + +import cocotb +import logging + +from cocotb.clock import Clock +from cocotb.triggers import Timer, RisingEdge +from cocotb.result import TestError, TestFailure +from cocotb.handle import HierarchyObject, HierarchyArrayObject, ModifiableObject, NonHierarchyIndexableObject, ConstantObject + +def _check_type(tlog, hdl, expected): + if not isinstance(hdl, expected): + raise TestFailure(">{0} ({1})< should be >{2}<".format(hdl._fullname, type(hdl) ,expected)) + else: + tlog.info(" Found %s (%s) with length=%d", hdl._fullname, type(hdl), len(hdl)) + +@cocotb.test() +def test_gen_loop(dut): + """Test accessing Generate Loops""" + tlog = logging.getLogger("cocotb.test") + + yield Timer(1000) + + asc_gen_20 = dut.asc_gen[20] + desc_gen = dut.desc_gen + + if not isinstance(dut.asc_gen, HierarchyArrayObject): + raise TestFailure("Generate Loop parent >{}< should be HierarchyArrayObject".format(dut.asc_gen)) + + if not isinstance(desc_gen, HierarchyArrayObject): + raise TestFailure("Generate Loop parent >{}< should be HierarchyArrayObject".format(desc_gen)) + + if not isinstance(asc_gen_20, HierarchyObject): + raise TestFailure("Generate Loop child >{}< should be HierarchyObject".format(asc_gen_20)) + + tlog.info("Direct access found %s", asc_gen_20) + tlog.info("Direct access found %s", desc_gen) + + for gens in desc_gen: + tlog.info("Iterate access found %s", gens) + + if len(desc_gen) != 8: + raise TestError("Length of desc_gen is >%d< and should be 8".format(len(desc_gen))) + else: + tlog.info("Length of desc_gen is %d", len(desc_gen)) + + if len(dut.asc_gen) != 8: + raise TestError("Length of asc_gen is >%d< and should be 8".format(len(dut.asc_gen))) + else: + tlog.info("Length of asc_gen is %d", len(dut.asc_gen)) + + for gens in dut.asc_gen: + tlog.info("Iterate access found %s", gens) + +@cocotb.test() +def test_discover_all(dut): + """Discover everything in the DUT: + dut + TYPE CNT NOTES EXCEPTIONS + parameters: 7/2 (base types) (VHDL/Verilog) + 6 (param_rec.a, param_rec.b[0:2]) (VHDL only excluding Aldec) + 13 (param_cmplx[0:1].a, param_cmplx[0:1].b[0:2]) (VHDL only excluding Aldec) + ports: 1 (clk) + 1 (select_in) (VPI - Aldec sees as 32 bit register (i.e. cnt = 33) + 9 (port_desc_in) + 9 (port_asc_in) + 9 (port_ofst_in) + 9 (port_desc_out) + 9 (port_asc_out) + 9 (port_ofst_out) + 1 (port_logic_out) + 9 (port_logic_vec_out) + 1 (port_bool_out) (VHDL Only) + 1 (port_int_out) (VHDL Only) + 1 (port_real_out) (VHDL Only) + 1 (port_char_out) (VHDL Only) + 9 (port_str_out) (VHDL Only) + 30 (port_rec_out) (VPI - Aldec sees as a Module and not structure (i.e. cnt = 1)) + 61 (port_cmplx_out) (VPI - Aldec sees as a Module and not structure (i.e. cnt = 1)) + constants: 1 (const_logic) + 1 (const_logic_vec) + 1 (const_bool) (VHDL Only) + 1 (const_int) (VHDL Only) + 1 (const_real) (VHDL Only) + 1 (const_char) (VHDL Only) + 1 (const_str) (VHDL Only) + 6 (const_rec.a, const_rec.b[0:2]) (VHDL only excluding Aldec) + 13 (const_cmplx[1:2].a, const_cmplx[1:2].b[0:2]) (VHDL only excluding Aldec) + signals: 9 (sig_desc) + 9 (sig_asc) + 5 (sig_t1) + 37 (sig_t2[7:4][7:0]) + 37 (sig_t3a[1:4][7:0]) + 37 (sig_t3b[3:0][7:0]) + 149 (sig_t4[0:3][7:4][7:0]) + 112 (sig_t5[0:2][0:3][7:0]) + 57 (sig_t6[0:1][2:4][7:0]) + 1 (sig_logic) + 9 (sig_logic_vec) + 1 (sig_bool) (VHDL Only) + 1 (sig_int) (VHDL Only) + 1 (sig_real) (VHDL Only) + 1 (sig_char) (VHDL Only) + 9 (sig_str) (VHDL Only) + 30 (sig_rec.a, sig_rec.b[0:2][7:0]) (VPI doesn't find, added manually, except for Aldec) + 61 (sig_cmplx[0:1].a, sig_cmplx[0:1].b[0:2][7:0]) (VPI - Aldec doesn't find) + regions: 9 (asc_gen[16:23]) + 8 (asc_gen: signals) (VHPI - Riviera doesn't find, added manually) + 8 (asc_gen: constant) + 8 (asc_gen: variable) + 8 (asc_gen: process "always") (VPI - Aldec only) + 9 (desc_gen[7:0]) + 8 (desc_gen: signals) (VHPI - Riviera doesn't find, added manually) + 8 (desc_gen: constant) + 8 (desc_gen: variable) + 8 (desc_gen: process "always") (VPI - Aldec only) + process: 1 ("always") (VPI - Aldec only) + + TOTAL: 854 (VHDL - Default) + 816 (VHDL - Aldec) + 780 (Verilog - Default) + 649 (Verilog - Aldec) + """ + + tlog = logging.getLogger("cocotb.test") + + yield Timer(1000) + + # Need to clear sub_handles so won't attempt to iterate over handles like sig_rec and sig_rec_array + # + # DO NOT REMOVE. Aldec cannot iterate over the complex records due to bugs in the VPI interface. + if cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")): + if len(dut._sub_handles) != 0: + dut._sub_handles = {} + + # Modelsim/Questa VPI will not find a vpiStructVar from vpiModule so we set a dummy variable + # to ensure the handle is in the dut "sub_handles" for iterating + # + # DO NOT ADD FOR ALDEC. Does not iterate over properly + if cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("modelsim","ncsim")): + dummy = dut.sig_rec + dummy = dut.port_rec_out + + # Riviera-Pro's VHPI implementation does not fine signal declarations when iterating + if cocotb.LANGUAGE in ["vhdl"] and cocotb.SIM_NAME.lower().startswith(("riviera")): + for hdl in dut.asc_gen: + dummy = hdl.sig + for hdl in dut.desc_gen: + dummy = hdl.sig + + if cocotb.LANGUAGE in ["vhdl"] and cocotb.SIM_NAME.lower().startswith(("riviera")): + pass_total = 816 + elif cocotb.LANGUAGE in ["vhdl"]: + pass_total = 854 + elif cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")): + pass_total = 649 + else: + pass_total = 780 + + def _discover(obj,indent): + count = 0 + new_indent = indent+"---" + for thing in obj: + count += 1 + tlog.info("%sFound %s (%s)", indent, thing._fullname, type(thing)) + count += _discover(thing,new_indent) + return count + + tlog.info("Iterating over %s (%s)", dut._fullname, type(dut)) + total = _discover(dut, "") + tlog.info("Found a total of %d things", total) + if total != pass_total: + raise TestFailure("Expected {0} objects but found {1}".format(pass_total, total)) + + +@cocotb.test(skip=(cocotb.LANGUAGE in ["verilog"] or cocotb.SIM_NAME.lower().startswith(("riviera")))) +def test_direct_constant_indexing(dut): + """Test directly accessing constant/parameter data in arrays, i.e. not iterating""" + + tlog = logging.getLogger("cocotb.test") + + yield Timer(2000) + + tlog.info("Checking Types of complex array structures in constants/parameters.") + _check_type(tlog, dut.param_rec, HierarchyObject) + _check_type(tlog, dut.param_rec.a, ConstantObject) + _check_type(tlog, dut.param_rec.b, NonHierarchyIndexableObject) + _check_type(tlog, dut.param_rec.b[1], ConstantObject) + + _check_type(tlog, dut.param_cmplx, NonHierarchyIndexableObject) + _check_type(tlog, dut.param_cmplx[0], HierarchyObject) + _check_type(tlog, dut.param_cmplx[0].a, ConstantObject) + _check_type(tlog, dut.param_cmplx[0].b, NonHierarchyIndexableObject) + _check_type(tlog, dut.param_cmplx[0].b[1], ConstantObject) + + _check_type(tlog, dut.const_rec, HierarchyObject) + _check_type(tlog, dut.const_rec.a, ConstantObject) + _check_type(tlog, dut.const_rec.b, NonHierarchyIndexableObject) + _check_type(tlog, dut.const_rec.b[1], ConstantObject) + + _check_type(tlog, dut.const_cmplx, NonHierarchyIndexableObject) + _check_type(tlog, dut.const_cmplx[1], HierarchyObject) + _check_type(tlog, dut.const_cmplx[1].a, ConstantObject) + _check_type(tlog, dut.const_cmplx[1].b, NonHierarchyIndexableObject) + _check_type(tlog, dut.const_cmplx[1].b[1], ConstantObject) + + +@cocotb.test() +def test_direct_signal_indexing(dut): + """Test directly accessing signal/net data in arrays, i.e. not iterating""" + + tlog = logging.getLogger("cocotb.test") + + cocotb.fork(Clock(dut.clk, 1000).start()) + + dut.port_desc_in <= 0 + dut.port_asc_in <= 0 + dut.port_ofst_in <= 0 + + yield Timer(2000) + + dut.port_desc_in[2] <= 1 + dut.port_asc_in[2] <= 1 + dut.port_ofst_in[2] <= 1 + + yield Timer(2000) + + tlog.info("Checking bit mapping from input to generate loops.") + if int(dut.desc_gen[2].sig) != 1: + raise TestFailure("Expected dut.desc_gen[2].sig to be a 1 but got {}".format(int(dut.desc_gen[2].sig))) + else: + tlog.info(" dut.desc_gen[2].sig = %d", int(dut.desc_gen[2].sig)) + + if int(dut.asc_gen[18].sig) != 1: + raise TestFailure("Expected dut.asc_gen[18].sig to be a 1 but got {}".format(int(dut.asc_gen[18].sig))) + else: + tlog.info(" dut.asc_gen[18].sig = %d", int(dut.asc_gen[18].sig)) + + tlog.info("Checking indexing of data with offset index.") + if int(dut.port_ofst_out) != 64: + raise TestFailure("Expected dut.port_ofst_out to be a 64 but got {}".format(int(dut.port_ofst_out))) + else: + tlog.info(" dut.port_ofst_out = %d (%s)", int(dut.port_ofst_out), dut.port_ofst_out.value.binstr) + + tlog.info("Checking Types of complex array structures in signals.") + _check_type(tlog, dut.sig_desc[20], ModifiableObject) + _check_type(tlog, dut.sig_asc[17], ModifiableObject) + _check_type(tlog, dut.sig_t1, ModifiableObject) + _check_type(tlog, dut.sig_t2, NonHierarchyIndexableObject) + _check_type(tlog, dut.sig_t2[5], ModifiableObject) + _check_type(tlog, dut.sig_t2[5][3], ModifiableObject) + _check_type(tlog, dut.sig_t3a[2][3], ModifiableObject) + _check_type(tlog, dut.sig_t3b[3], ModifiableObject) + _check_type(tlog, dut.sig_t3a, NonHierarchyIndexableObject) + _check_type(tlog, dut.sig_t4, NonHierarchyIndexableObject) + _check_type(tlog, dut.sig_t4[3], NonHierarchyIndexableObject) + _check_type(tlog, dut.sig_t4[3][4], ModifiableObject) + _check_type(tlog, dut.sig_t4[3][4][1], ModifiableObject) + _check_type(tlog, dut.sig_t5, NonHierarchyIndexableObject) + _check_type(tlog, dut.sig_t5[1], NonHierarchyIndexableObject) + _check_type(tlog, dut.sig_t5[1][0], ModifiableObject) + _check_type(tlog, dut.sig_t5[1][0][6], ModifiableObject) + _check_type(tlog, dut.sig_t6, NonHierarchyIndexableObject) + _check_type(tlog, dut.sig_t6[1], NonHierarchyIndexableObject) + _check_type(tlog, dut.sig_t6[0][3], ModifiableObject) + _check_type(tlog, dut.sig_t6[0][3][7], ModifiableObject) + _check_type(tlog, dut.sig_cmplx, NonHierarchyIndexableObject) + + # Riviera has a bug and finds dut.sig_cmplx[1], but the type returned is a vpiBitVar + if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera"))): + _check_type(tlog, dut.sig_cmplx[1], HierarchyObject) + _check_type(tlog, dut.sig_cmplx[1].a, ModifiableObject) + _check_type(tlog, dut.sig_cmplx[1].b, NonHierarchyIndexableObject) + _check_type(tlog, dut.sig_cmplx[1].b[1], ModifiableObject) + _check_type(tlog, dut.sig_cmplx[1].b[1][2], ModifiableObject) + tlog.info(" dut.sig_cmplx[1].a = %d (%s)", int(dut.sig_cmplx[1].a), dut.sig_cmplx[1].a.value.binstr) + + _check_type(tlog, dut.sig_rec, HierarchyObject) + _check_type(tlog, dut.sig_rec.a, ModifiableObject) + _check_type(tlog, dut.sig_rec.b, NonHierarchyIndexableObject) + + # Riviera has a bug and finds dut.sig_rec.b[1], but the type returned is 0 which is unknown + if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera"))): + _check_type(tlog, dut.sig_rec.b[1], ModifiableObject) + _check_type(tlog, dut.sig_rec.b[1][2], ModifiableObject) + From 9738bcf7f2423506c14911c2d8f6a21d7533a9c1 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 7 Mar 2016 13:42:10 -0700 Subject: [PATCH 1149/2656] Issue #393: Fli Interface clean-up, move some attributes to common class --- lib/fli/FliImpl.cpp | 13 +++---------- lib/fli/FliImpl.h | 42 +++++++++++++++++++++++------------------- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index b634ee4d..e0b2f5cd 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -580,15 +580,8 @@ FliIterator::FliIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(i m_regs(), m_currentHandles(NULL) { - int type; - - if (m_parent->get_type() == GPI_MODULE or m_parent->get_type() == GPI_STRUCTURE) { - FliObjHdl *fli_obj = static_cast(m_parent); - type = fli_obj->get_acc_full_type(); - } else { - FliSignalObjHdl *fli_obj = static_cast(m_parent); - type = fli_obj->get_acc_full_type(); - } + FliObj *fli_obj = dynamic_cast(m_parent); + int type = fli_obj->get_acc_full_type(); LOG_DEBUG("fli_iterator::Create iterator for %s of type %d:%s", m_parent->get_fullname().c_str(), type, acc_fetch_type_str(type)); @@ -760,7 +753,7 @@ GpiIterator::Status FliIterator::next_handle(std::string &name, GpiObjHdl **hdl, fq_name += "/" + name; } - FliImpl *fli_impl = static_cast(m_impl); + FliImpl *fli_impl = reinterpret_cast(m_impl); new_obj = fli_impl->create_gpi_obj_from_handle(obj, name, fq_name, accType, accFullType); if (new_obj) { *hdl = new_obj; diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index 5d79c63b..f6bb541e 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -152,7 +152,25 @@ class FliTimedCbHdl : public FliProcessCbHdl { // Object Handles -class FliObjHdl : public GpiObjHdl { +class FliObj { +public: + FliObj(int acc_type, + int acc_full_type) : + m_acc_type(acc_type), + m_acc_full_type(acc_full_type) { } + + virtual ~FliObj() { } + + int get_acc_type(void) { return m_acc_type; } + int get_acc_full_type(void) { return m_acc_full_type; } + + +protected: + int m_acc_type; + int m_acc_full_type; +}; + +class FliObjHdl : public GpiObjHdl, public FliObj { public: FliObjHdl(GpiImplInterface *impl, void *hdl, @@ -160,8 +178,7 @@ class FliObjHdl : public GpiObjHdl { int acc_type, int acc_full_type) : GpiObjHdl(impl, hdl, objtype, false), - m_acc_type(acc_type), - m_acc_full_type(acc_full_type) { } + FliObj(acc_type, acc_full_type) { } FliObjHdl(GpiImplInterface *impl, void *hdl, @@ -170,22 +187,14 @@ class FliObjHdl : public GpiObjHdl { int acc_full_type, bool is_const) : GpiObjHdl(impl, hdl, objtype, is_const), - m_acc_type(acc_type), - m_acc_full_type(acc_full_type) { } + FliObj(acc_type, acc_full_type) { } virtual ~FliObjHdl() { } virtual int initialise(std::string &name, std::string &fq_name); - - int get_acc_type(void) { return m_acc_type; } - int get_acc_full_type(void) { return m_acc_full_type; } - -protected: - int m_acc_type; - int m_acc_full_type; }; -class FliSignalObjHdl : public GpiSignalObjHdl { +class FliSignalObjHdl : public GpiSignalObjHdl, public FliObj { public: FliSignalObjHdl(GpiImplInterface *impl, void *hdl, @@ -195,8 +204,7 @@ class FliSignalObjHdl : public GpiSignalObjHdl { int acc_full_type, bool is_var) : GpiSignalObjHdl(impl, hdl, objtype, is_const), - m_acc_type(acc_type), - m_acc_full_type(acc_full_type), + FliObj(acc_type, acc_full_type), m_is_var(is_var), m_rising_cb(impl, this, GPI_RISING), m_falling_cb(impl, this, GPI_FALLING), @@ -207,13 +215,9 @@ class FliSignalObjHdl : public GpiSignalObjHdl { virtual GpiCbHdl *value_change_cb(unsigned int edge); virtual int initialise(std::string &name, std::string &fq_name); - int get_acc_type(void) { return m_acc_type; } - int get_acc_full_type(void) { return m_acc_full_type; } bool is_var(void) { return m_is_var; } protected: - int m_acc_type; - int m_acc_full_type; bool m_is_var; FliSignalCbHdl m_rising_cb; FliSignalCbHdl m_falling_cb; From e22185a2a6a9fc3587533b0ead407b72c20a2425 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 21 Mar 2016 17:43:16 -0700 Subject: [PATCH 1150/2656] Issue #393: Restrict gpi_get_handle_by_index() to only check the parent handles Interface --- lib/gpi/GpiCommon.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 3a5b0775..d1294f1d 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -334,18 +334,18 @@ gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) { vector::iterator iter; - GpiObjHdl *hdl = NULL; - GpiObjHdl *base = sim_to_hdl(parent); - - for (iter = registered_impls.begin(); - iter != registered_impls.end(); - iter++) { - LOG_DEBUG("Checking if index %d native though impl %s ", index, (*iter)->get_name_c()); - if ((hdl = (*iter)->native_check_create(index, base))) { - LOG_DEBUG("Found %d via %s", index, (*iter)->get_name_c()); - break; - } - } + GpiObjHdl *hdl = NULL; + GpiObjHdl *base = sim_to_hdl(parent); + GpiImplInterface *intf = base->m_impl; + + /* Shouldn't need to iterate over interfaces because indexing into a handle shouldn't + * cross the interface boundaries. + * + * NOTE: IUS's VPI interface returned valid VHDL handles, but then couldn't + * use the handle properly. + */ + LOG_DEBUG("Checking if index %d native though impl %s ", index, intf->get_name_c()); + hdl = intf->native_check_create(index, base); if (hdl) return CHECK_AND_STORE(hdl); From 7623edf6299ba9d50ec2256ed4aff75ea07eab9f Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 8 Mar 2016 07:24:35 -0700 Subject: [PATCH 1151/2656] Issue #393: Update GPI interface to support GPI_GENARRAY --- cocotb/handle.py | 62 +++++++++++++++++++++++++++++++++ include/gpi.h | 1 + lib/gpi/GpiCbHdl.cpp | 1 + lib/simulator/simulatormodule.c | 1 + 4 files changed, 65 insertions(+) diff --git a/cocotb/handle.py b/cocotb/handle.py index 93a60e59..41fc0c90 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -288,6 +288,67 @@ def __getitem__(self, index): self._sub_handles[index] = SimHandle(new_handle) return self._sub_handles[index] +class HierarchyArrayObject(HierarchyObject): + """ + Hierarchy Array objects don't have values, they are effectively scopes or namespaces + """ + + def _discover_all(self): + """ + When iterating or performing tab completion, we run through ahead of + time and discover all possible children, populating the _sub_handle + mapping. Hierarchy can't change after elaboration so we only have to + do this once. + """ + if self._discovered: return + self._log.debug("Discovering all on %s", self._name) + iterator = simulator.iterate(self._handle, simulator.OBJECTS) + while True: + try: + thing = simulator.next(iterator) + except StopIteration: + # Iterator is cleaned up internally in GPI + break + name = simulator.get_name_string(thing) + try: + hdl = SimHandle(thing) + except TestError as e: + self._log.debug("%s" % e) + continue + + # This is slightly hacky, but we need to extract the index from the name + # + # FLI and VHPI(IUS): _name(X) where X is the index + # VHPI(ALDEC): _name__X where X is the index + # VPI: _name[X] where X is the index + import re + result = re.match("{}__(?P\d+)$".format(self._name), name) + if not result: + result = re.match("{}\((?P\d+)\)$".format(self._name), name) + if not result: + result = re.match("{}\[(?P\d+)\]$".format(self._name), name) + + if result: + index = int(result.group("index")) + + self._sub_handles[index] = hdl + self._log.debug("Added %s[%d] to the cache", self._name, index) + else: + self._log.error("Dropping invalid Hierarchy Array handle >%s<", name) + + self._discovered = True + + def __len__(self): + """Returns the 'length' of the generate block.""" + if self._len is None: + if not self._discovered: + self._discover_all() + + self._len = len(self._sub_handles) + return self._len + + + class NonHierarchyObject(SimHandleBase): """ @@ -623,6 +684,7 @@ def SimHandle(handle): simulator.INTEGER: IntegerObject, simulator.ENUM: ModifiableObject, simulator.STRING: StringObject, + simulator.GENARRAY: HierarchyArrayObject, } # Enforce singletons since it's possible to retrieve handles avoiding diff --git a/include/gpi.h b/include/gpi.h index 383b68fc..3f09e858 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -138,6 +138,7 @@ typedef enum gpi_objtype_e { GPI_REAL = 9, GPI_INTEGER = 10, GPI_STRING = 11, + GPI_GENARRAY = 12, } gpi_objtype_t; // When iterating, we can chose to either get child objects, drivers or loads diff --git a/lib/gpi/GpiCbHdl.cpp b/lib/gpi/GpiCbHdl.cpp index 96083fc8..fc1dee6f 100644 --- a/lib/gpi/GpiCbHdl.cpp +++ b/lib/gpi/GpiCbHdl.cpp @@ -65,6 +65,7 @@ const char * GpiObjHdl::get_type_str(void) CASE_OPTION(GPI_REAL); CASE_OPTION(GPI_INTEGER); CASE_OPTION(GPI_STRING); + CASE_OPTION(GPI_GENARRAY); default: ret = "unknown"; } diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index ab6c477b..dc1afb72 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -973,6 +973,7 @@ static void add_module_constants(PyObject* simulator) rc |= PyModule_AddIntConstant(simulator, "REAL", GPI_REAL); rc |= PyModule_AddIntConstant(simulator, "INTEGER", GPI_INTEGER); rc |= PyModule_AddIntConstant(simulator, "STRING", GPI_STRING); + rc |= PyModule_AddIntConstant(simulator, "GENARRAY", GPI_GENARRAY); rc |= PyModule_AddIntConstant(simulator, "OBJECTS", GPI_OBJECTS); rc |= PyModule_AddIntConstant(simulator, "DRIVERS", GPI_DRIVERS); rc |= PyModule_AddIntConstant(simulator, "LOADS", GPI_LOADS); From eb3471a864dbe82674ed6a3d7265cba6ab105e90 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 8 Mar 2016 07:25:39 -0700 Subject: [PATCH 1152/2656] Issue #393: Update the FLI to use GPI_GENARRAY --- lib/fli/FliImpl.cpp | 132 +++++++++++++++++++++++++++++++----------- lib/fli/FliObjHdl.cpp | 3 + 2 files changed, 102 insertions(+), 33 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index e0b2f5cd..45623773 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -125,8 +125,17 @@ GpiObjHdl *FliImpl::create_gpi_obj_from_handle(void *hdl, std::string &name, std } if (!isTypeValue(accType)) { - LOG_DEBUG("Found region %s -> %p", fq_name.c_str(), hdl); - new_obj = new FliObjHdl(this, hdl, GPI_MODULE, accType, accFullType); + /* Need a Pseudo-region to handle generate loops in a consistent manner across interfaces + * and across the different methods of accessing data. + */ + std::string rgn_name = mti_GetRegionName(static_cast(hdl)); + if (name != rgn_name) { + LOG_DEBUG("Found pseudo-region %s -> %p", fq_name.c_str(), hdl); + new_obj = new FliObjHdl(this, hdl, GPI_GENARRAY, accType, accFullType); + } else { + LOG_DEBUG("Found region %s -> %p", fq_name.c_str(), hdl); + new_obj = new FliObjHdl(this, hdl, GPI_MODULE, accType, accFullType); + } } else { bool is_var; bool is_const; @@ -274,22 +283,33 @@ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) LOG_DEBUG(" Type: %d", accType); LOG_DEBUG(" Full Type: %d", accFullType); } else { + mtiRegionIdT rgn; + + /* If not found, check to see if the name of a generate loop and create a pseudo-region */ + for (rgn = mti_FirstLowerRegion(parent->get_handle()); rgn != NULL; rgn = mti_NextRegion(rgn)) { + if (acc_fetch_fulltype(hdl) == accForGenerate) { + std::string rgn_name = mti_GetRegionName(static_cast(rgn)); + if (rgn_name.compare(0,name.length(),name) == 0) { + FliObj *fli_obj = dynamic_cast(parent); + return create_gpi_obj_from_handle(parent->get_handle(), name, fq_name, fli_obj->get_acc_type(), fli_obj->get_acc_full_type()); + } + } + } + LOG_DEBUG("Didn't find anything named %s", &writable[0]); return NULL; } - /* Added to behave like vhpi interface. Handle.py will does not support a handle to a - * "for generate" loop that doesn't contain an index. - * - * VHDL (in dut): - * a_loop : for i in 0 to 9 generate - * ... - * end generate a_loop; + /* Generate Loops have inconsistent behavior across fli. A "name" + * without an index, i.e. dut.loop vs dut.loop(0), will attempt to map + * to index 0, if index 0 exists. If it doesn't then it won't find anything. * - * FLI will return a vaild handle to "/dut/a_loop" as well as /dut/a_loop(0) + * If this unique case is hit, we need to create the Pseudo-region, with the handle + * being equivalent to the parent handle. */ if (accFullType == accForGenerate) { - return NULL; + FliObj *fli_obj = dynamic_cast(parent); + return create_gpi_obj_from_handle(parent->get_handle(), name, fq_name, fli_obj->get_acc_type(), fli_obj->get_acc_full_type()); } return create_gpi_obj_from_handle(hdl, name, fq_name, accType, accFullType); @@ -302,30 +322,21 @@ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) */ GpiObjHdl* FliImpl::native_check_create(uint32_t index, GpiObjHdl *parent) { - if (parent->get_type() == GPI_MODULE || parent->get_type() == GPI_ARRAY || parent->get_type() == GPI_STRING) { + gpi_objtype_t obj_type = parent->get_type(); + if (obj_type == GPI_MODULE || obj_type == GPI_GENARRAY || obj_type == GPI_ARRAY || obj_type == GPI_STRING) { + FliObj *fli_obj = dynamic_cast(parent); + int type = fli_obj->get_acc_type(); char buff[15]; - int type; LOG_DEBUG("Looking for index %u from %s", index, parent->get_name_str()); - if (parent->get_type() == GPI_MODULE) { - FliObjHdl *fli_obj = static_cast(parent); - type = fli_obj->get_acc_type(); - } else { - FliSignalObjHdl *fli_obj = static_cast(parent); - type = fli_obj->get_acc_type(); - } - /* To behave like the current VHPI interface, index needs to be properly translated. * Index of 0 needs to map to "signal'left" and Index of "length-1" mapping to "signal'right" */ if (!isTypeValue(type)) { - /* This case would be for indexing into a generate loop. The way that is currently - * handled, this code should never be executed. - */ snprintf(buff, 15, "(%u)", index); } else { - FliValueObjHdl *fli_obj = static_cast(parent); + FliValueObjHdl *fli_obj = reinterpret_cast(parent); mtiTypeIdT typeId = fli_obj->get_fli_typeid(); if (mti_TickDir(typeId) < 0) { @@ -370,7 +381,7 @@ GpiObjHdl* FliImpl::native_check_create(uint32_t index, GpiObjHdl *parent) return create_gpi_obj_from_handle(hdl, name, fq_name, accType, accFullType); } else { - LOG_ERROR("FLI: Parent of type %d must be of type GPI_MODULE or GPI_ARRAY to have an index.", parent->get_type()); + LOG_ERROR("FLI: Parent of type %d must be of type GPI_MODULE, GPI_GENARRAY, GPI_STRING or GPI_ARRAY to have an index.", parent->get_type()); return NULL; } } @@ -593,6 +604,12 @@ FliIterator::FliIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(i /* Find the first mapping type that yields a valid iterator */ for (one2many = selected->begin(); one2many != selected->end(); one2many++) { + /* GPI_GENARRAY are pseudo-regions and all that should be searched for are the sub-regions */ + if (m_parent->get_type() == GPI_GENARRAY && *one2many != FliIterator::OTM_REGIONS) { + LOG_DEBUG("fli_iterator OneToMany=%d skipped for GPI_GENARRAY type", *one2many); + continue; + } + populate_handle_list(*one2many); switch (*one2many) { @@ -639,6 +656,9 @@ GpiIterator::Status FliIterator::next_handle(std::string &name, GpiObjHdl **hdl, if (!selected) return GpiIterator::END; + gpi_objtype_t obj_type = m_parent->get_type(); + std::string parent_name = m_parent->get_name(); + /* We want the next object in the current mapping. * If the end of mapping is reached then we want to * try next one until a new object is found @@ -648,6 +668,23 @@ GpiIterator::Status FliIterator::next_handle(std::string &name, GpiObjHdl **hdl, if (m_iterator != m_currentHandles->end()) { obj = *m_iterator++; + + /* For GPI_GENARRAY, only allow the generate statements through that match the name + * of the generate block. + */ + if (obj_type == GPI_GENARRAY) { + if (acc_fetch_fulltype(obj) == accForGenerate) { + std::string rgn_name = mti_GetRegionName(static_cast(obj)); + if (rgn_name.compare(0,parent_name.length(),parent_name) != 0) { + obj = NULL; + continue; + } + } else { + obj = NULL; + continue; + } + } + break; } else { LOG_DEBUG("No more valid handles in the current OneToMany=%d iterator", *one2many); @@ -658,6 +695,12 @@ GpiIterator::Status FliIterator::next_handle(std::string &name, GpiObjHdl **hdl, break; } + /* GPI_GENARRAY are pseudo-regions and all that should be searched for are the sub-regions */ + if (obj_type == GPI_GENARRAY && *one2many != FliIterator::OTM_REGIONS) { + LOG_DEBUG("fli_iterator OneToMany=%d skipped for GPI_GENARRAY type", *one2many); + continue; + } + populate_handle_list(*one2many); switch (*one2many) { @@ -686,8 +729,8 @@ GpiIterator::Status FliIterator::next_handle(std::string &name, GpiObjHdl **hdl, } char *c_name; - PLI_INT32 accType; - PLI_INT32 accFullType; + PLI_INT32 accType = 0; + PLI_INT32 accFullType = 0; switch (*one2many) { case FliIterator::OTM_CONSTANTS: case FliIterator::OTM_VARIABLE_SUB_ELEMENTS: @@ -714,8 +757,6 @@ GpiIterator::Status FliIterator::next_handle(std::string &name, GpiObjHdl **hdl, } if (!c_name) { - int accFullType = acc_fetch_fulltype(obj); - if (!VS_TYPE_IS_VHDL(accFullType)) { *raw_hdl = (void *)obj; return GpiIterator::NOT_NATIVE_NO_NAME; @@ -724,7 +765,31 @@ GpiIterator::Status FliIterator::next_handle(std::string &name, GpiObjHdl **hdl, return GpiIterator::NATIVE_NO_NAME; } - name = c_name; + /* + * If the parent is not a generate loop, then watch for generate handles and create + * the pseudo-region. + * + * NOTE: Taking advantage of the "caching" to only create one pseudo-region object. + * Otherwise a list would be required and checked while iterating + */ + if (*one2many == FliIterator::OTM_REGIONS && obj_type != GPI_GENARRAY && accFullType == accForGenerate) { + std::string idx_str = c_name; + std::size_t found = idx_str.find_last_of("("); + + if (found != std::string::npos && found != 0) { + FliObj *fli_obj = dynamic_cast(m_parent); + + name = idx_str.substr(0,found); + obj = m_parent->get_handle(); + accType = fli_obj->get_acc_type(); + accFullType = fli_obj->get_acc_full_type(); + } else { + LOG_WARN("Unhandled Generate Loop Format - %s", name.c_str()); + name = c_name; + } + } else { + name = c_name; + } if (*one2many == FliIterator::OTM_SIGNAL_SUB_ELEMENTS) { mti_VsimFree(c_name); @@ -734,10 +799,11 @@ GpiIterator::Status FliIterator::next_handle(std::string &name, GpiObjHdl **hdl, if (fq_name == "/") { fq_name += name; } else if (*one2many == FliIterator::OTM_SIGNAL_SUB_ELEMENTS || - *one2many == FliIterator::OTM_VARIABLE_SUB_ELEMENTS) { + *one2many == FliIterator::OTM_VARIABLE_SUB_ELEMENTS || + obj_type == GPI_GENARRAY) { std::size_t found; - if (m_parent->get_type() == GPI_STRUCTURE) { + if (obj_type == GPI_STRUCTURE) { found = name.find_last_of("."); } else { found = name.find_last_of("("); diff --git a/lib/fli/FliObjHdl.cpp b/lib/fli/FliObjHdl.cpp index 4a35d232..ce314593 100644 --- a/lib/fli/FliObjHdl.cpp +++ b/lib/fli/FliObjHdl.cpp @@ -91,6 +91,9 @@ int FliObjHdl::initialise(std::string &name, std::string &fq_name) m_num_elems = mti_TickLength(arrayType); } break; + case GPI_GENARRAY: + m_num_elems = 1; //Unable to determine length of this pseudo-region + break; default: LOG_CRITICAL("Invalid object type for FliObjHdl. (%s (%s))", name.c_str(), get_type_str()); return -1; From 117bb7882d935aa8ec5069c81b7938647c2a8c10 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 8 Mar 2016 12:40:32 -0700 Subject: [PATCH 1153/2656] Issue #393: Update the VHPI interface to use GPI_GENARRAY --- lib/vhpi/VhpiCbHdl.cpp | 72 +++++++++++++++++++-- lib/vhpi/VhpiImpl.cpp | 143 ++++++++++++++++++++++++++++++----------- lib/vhpi/VhpiImpl.h | 9 +++ 3 files changed, 184 insertions(+), 40 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 77fc779a..05ce9a16 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -572,7 +572,7 @@ int VhpiStartupCbHdl::run_callback(void) { gpi_sim_info_t sim_info; sim_info.argc = 0; sim_info.argv = NULL; - sim_info.product = gpi_copy_name(vhpi_get_str(vhpiNameP, NULL)); + sim_info.product = gpi_copy_name(vhpi_get_str(vhpiCaseNameP, NULL)); sim_info.version = gpi_copy_name(vhpi_get_str(vhpiToolVersionP, NULL)); gpi_embed_init(&sim_info); @@ -732,6 +732,13 @@ VhpiIterator::VhpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator for (one2many = selected->begin(); one2many != selected->end(); one2many++) { + + /* GPI_GENARRAY are pseudo-regions and all that should be searched for are the sub-regions */ + if (m_parent->get_type() == GPI_GENARRAY && *one2many != vhpiInternalRegions) { + LOG_DEBUG("vhpi_iterator vhpiOneToManyT=%d skipped for GPI_GENARRAY type", *one2many); + continue; + } + iterator = vhpi_iterator(*one2many, vhpi_hdl); if (iterator) @@ -779,6 +786,9 @@ GpiIterator::Status VhpiIterator::next_handle(std::string &name, if (!selected) return GpiIterator::END; + gpi_objtype_t obj_type = m_parent->get_type(); + std::string parent_name = m_parent->get_name(); + /* We want the next object in the current mapping. * If the end of mapping is reached then we want to * try then next one until a new object is found @@ -789,14 +799,30 @@ GpiIterator::Status VhpiIterator::next_handle(std::string &name, if (m_iterator) { obj = vhpi_scan(m_iterator); - if (obj && (vhpiProcessStmtK == vhpi_get(vhpiKindP, obj))) { + /* For GPI_GENARRAY, only allow the generate statements through that match the name + * of the generate block. + */ + if (obj != NULL && obj_type == GPI_GENARRAY) { + if (vhpi_get(vhpiKindP, obj) == vhpiForGenerateK) { + std::string rgn_name = vhpi_get_str(vhpiCaseNameP, obj); + if (rgn_name.compare(0,parent_name.length(),parent_name) != 0) { + obj = NULL; + continue; + } + } else { + obj = NULL; + continue; + } + } + + if (obj != NULL && (vhpiProcessStmtK == vhpi_get(vhpiKindP, obj))) { LOG_DEBUG("Skipping %s (%s)", vhpi_get_str(vhpiFullNameP, obj), vhpi_get_str(vhpiKindStrP, obj)); obj=NULL; continue; } - if (obj) { + if (obj != NULL) { LOG_DEBUG("Found an item %s", vhpi_get_str(vhpiFullNameP, obj)); break; } else { @@ -813,6 +839,13 @@ GpiIterator::Status VhpiIterator::next_handle(std::string &name, obj = NULL; break; } + + /* GPI_GENARRAY are pseudo-regions and all that should be searched for are the sub-regions */ + if (obj_type == GPI_GENARRAY && *one2many != vhpiInternalRegions) { + LOG_DEBUG("vhpi_iterator vhpiOneToManyT=%d skipped for GPI_GENARRAY type", *one2many); + continue; + } + m_iterator = vhpi_iterator(*one2many, m_iter_obj); } while (!obj); @@ -835,7 +868,28 @@ GpiIterator::Status VhpiIterator::next_handle(std::string &name, return GpiIterator::NATIVE_NO_NAME; } - name = c_name; + + /* + * If the parent is not a generate loop, then watch for generate handles and create + * the pseudo-region. + * + * NOTE: Taking advantage of the "caching" to only create one pseudo-region object. + * Otherwise a list would be required and checked while iterating + */ + if (*one2many == vhpiInternalRegions && obj_type != GPI_GENARRAY && vhpi_get(vhpiKindP, obj) == vhpiForGenerateK) { + std::string idx_str = c_name; + std::size_t found = idx_str.rfind(GEN_IDX_SEP_LHS); + + if (found != std::string::npos && found != 0) { + name = idx_str.substr(0,found); + obj = m_parent->get_handle(); + } else { + LOG_WARN("Unhandled Generate Loop Format - %s", name.c_str()); + name = c_name; + } + } else { + name = c_name; + } LOG_DEBUG("vhpi_scan found %s (%d) kind:%s name:%s", name.c_str(), vhpi_get(vhpiKindP, obj), @@ -848,6 +902,16 @@ GpiIterator::Status VhpiIterator::next_handle(std::string &name, std::string fq_name = m_parent->get_fullname(); if (fq_name == ":") { fq_name += name; + } else if (obj_type == GPI_GENARRAY) { + std::size_t found = name.rfind(GEN_IDX_SEP_LHS); + + if (found != std::string::npos) { + fq_name += name.substr(found); + } else { + LOG_WARN("Unhandled Sub-Element Format - %s", name.c_str()); + fq_name += "." + name; + } + } else { fq_name += "." + name; } diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index e98fb3ab..b4888d1c 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -206,7 +206,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, gpi_type = to_gpi_objtype(base_type); LOG_DEBUG("Creating %s of type %d (%s)", - vhpi_get_str(vhpiFullNameP, new_hdl), + vhpi_get_str(vhpiFullCaseNameP, new_hdl), gpi_type, vhpi_get_str(vhpiKindStrP, query_hdl)); @@ -307,16 +307,41 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, break; } - case vhpiForGenerateK: - case vhpiIfGenerateK: - case vhpiCompInstStmtK: case vhpiProcessStmtK: case vhpiSimpleSigAssignStmtK: case vhpiCondSigAssignStmtK: case vhpiRecordTypeDeclK: - case vhpiSelectSigAssignStmtK: + case vhpiSelectSigAssignStmtK: { + modifiable = false; + break; + } + + case vhpiRootInstK: + case vhpiIfGenerateK: + case vhpiForGenerateK: + case vhpiCompInstStmtK: { + std::string hdl_name = vhpi_get_str(vhpiCaseNameP, new_hdl); + + if (base_type == vhpiRootInstK && hdl_name != name) { + vhpiHandleT arch = vhpi_handle(vhpiDesignUnit, new_hdl); + + if (NULL != arch) { + vhpiHandleT prim = vhpi_handle(vhpiPrimaryUnit, arch); + + if (NULL != prim) { + hdl_name = vhpi_get_str(vhpiCaseNameP, prim); + } + } + } + modifiable = false; + + if (name != hdl_name) { + LOG_DEBUG("Found pseudo-region %s", fq_name.c_str()); + gpi_type = GPI_GENARRAY; + } break; + } default: { LOG_ERROR("Not able to map type (%s) %u to object", @@ -355,7 +380,7 @@ GpiObjHdl *VhpiImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) vhpiHandleT new_hdl = (vhpiHandleT)raw_hdl; std::string fq_name = parent->get_fullname(); - const char *c_name = vhpi_get_str(vhpiNameP, new_hdl); + const char *c_name = vhpi_get_str(vhpiCaseNameP, new_hdl); if (!c_name) { LOG_DEBUG("Unable to query name of passed in handle"); return NULL; @@ -394,8 +419,39 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) new_hdl = vhpi_handle_by_name(&writable[0], NULL); if (new_hdl == NULL) { - LOG_DEBUG("Unable to query vhpi_handle_by_name %s", fq_name.c_str()); - return NULL; + /* If not found, check to see if the name of a generate loop */ + vhpiHandleT iter = vhpi_iterator(vhpiInternalRegions, parent->get_handle()); + + if (iter != NULL) { + vhpiHandleT rgn; + for (rgn = vhpi_scan(iter); rgn != NULL; rgn = vhpi_scan(iter)) { + if (vhpi_get(vhpiKindP, rgn) == vhpiForGenerateK) { + std::string rgn_name = vhpi_get_str(vhpiCaseNameP, rgn); + if (rgn_name.compare(0,name.length(),name) == 0) { + new_hdl = parent->get_handle(); + vhpi_release_handle(iter); + break; + } + } + } + } + if (new_hdl == NULL) { + LOG_DEBUG("Unable to query vhpi_handle_by_name %s", fq_name.c_str()); + return NULL; + } + } + + /* Generate Loops have inconsistent behavior across vhpi. A "name" + * without an index, i.e. dut.loop vs dut.loop(0), may or may not map to + * to the start index. If it doesn't then it won't find anything. + * + * If this unique case is hit, we need to create the Pseudo-region, with the handle + * being equivalent to the parent handle. + */ + if (vhpi_get(vhpiKindP, new_hdl) == vhpiForGenerateK) { + vhpi_release_handle(new_hdl); + + new_hdl = parent->get_handle(); } GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); @@ -414,31 +470,48 @@ GpiObjHdl *VhpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) vhpiHandleT vhpi_hdl = parent_hdl->get_handle(); vhpiHandleT new_hdl; - LOG_DEBUG("Native check create for index %u of parent %s (%s)", - index, - vhpi_get_str(vhpiNameP, vhpi_hdl), - vhpi_get_str(vhpiKindStrP, vhpi_hdl)); - - new_hdl = vhpi_handle_by_index(vhpiIndexedNames, vhpi_hdl, index); - if (!new_hdl) { - /* Support for the above seems poor, so if it did not work - try an iteration instead */ - - vhpiHandleT iter = vhpi_iterator(vhpiIndexedNames, vhpi_hdl); - if (iter) { - uint32_t curr_index = 0; - while (true) { - new_hdl = vhpi_scan(iter); - if (!new_hdl) { - break; - } - if (index == curr_index) { - LOG_DEBUG("Index match %u == %u", curr_index, index); - break; + if (parent_hdl->get_type() == GPI_GENARRAY) { + char buff[11]; // needs to be large enough to hold 2^32-1 in string form (10 + '\0') + + snprintf(buff, 11, "%u", index); + + LOG_DEBUG("Native check create for index %u of parent %s (pseudo-region)", + index, + parent_hdl->get_name_str()); + + std::string idx = buff; + std::string hdl_name = parent_hdl->get_fullname() + GEN_IDX_SEP_LHS + idx + GEN_IDX_SEP_RHS; + std::vector writable(hdl_name.begin(), hdl_name.end()); + writable.push_back('\0'); + + new_hdl = vhpi_handle_by_name(&writable[0], NULL); + } else { + LOG_DEBUG("Native check create for index %u of parent %s (%s)", + index, + vhpi_get_str(vhpiCaseNameP, vhpi_hdl), + vhpi_get_str(vhpiKindStrP, vhpi_hdl)); + + new_hdl = vhpi_handle_by_index(vhpiIndexedNames, vhpi_hdl, index); + if (!new_hdl) { + /* Support for the above seems poor, so if it did not work + try an iteration instead */ + + vhpiHandleT iter = vhpi_iterator(vhpiIndexedNames, vhpi_hdl); + if (iter) { + uint32_t curr_index = 0; + while (true) { + new_hdl = vhpi_scan(iter); + if (!new_hdl) { + break; + } + if (index == curr_index) { + LOG_DEBUG("Index match %u == %u", curr_index, index); + break; + } + curr_index++; } - curr_index++; + vhpi_release_handle(iter); } - vhpi_release_handle(iter); } } @@ -447,7 +520,7 @@ GpiObjHdl *VhpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) return NULL; } - std::string name = vhpi_get_str(vhpiNameP, new_hdl); + std::string name = vhpi_get_str(vhpiCaseNameP, new_hdl); std::string fq_name = parent->get_fullname(); if (fq_name == ":") { fq_name += name; @@ -470,7 +543,6 @@ GpiObjHdl *VhpiImpl::get_root_handle(const char* name) vhpiHandleT root = NULL; vhpiHandleT arch = NULL; vhpiHandleT dut = NULL; - GpiObjHdl *rv = NULL; std::string root_name; const char *found; @@ -531,10 +603,9 @@ GpiObjHdl *VhpiImpl::get_root_handle(const char* name) } root_name = found; - rv = new GpiObjHdl(this, dut, to_gpi_objtype(vhpi_get(vhpiKindP, dut))); - rv->initialise(root_name, root_name); - return rv; + return create_gpi_obj_from_handle(dut, root_name, root_name); + } GpiIterator *VhpiImpl::iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 23c81911..e088e3a3 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -33,6 +33,15 @@ #include #include +// Define Index separator +#ifdef IUS +#define GEN_IDX_SEP_LHS "(" +#define GEN_IDX_SEP_RHS ")" +#else +#define GEN_IDX_SEP_LHS "__" +#define GEN_IDX_SEP_RHS "" +#endif + // Should be run after every VHPI call to check error status static inline int __check_vhpi_error(const char *file, const char *func, long line) { From d3e781e789269efe70ab15fefd71143f8c9a8275 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Wed, 23 Mar 2016 12:55:34 -0700 Subject: [PATCH 1154/2656] Issue #393: Fixed a bug in FLI for locating the Pseudo-region --- lib/fli/FliImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 45623773..c20939e8 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -287,7 +287,7 @@ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) /* If not found, check to see if the name of a generate loop and create a pseudo-region */ for (rgn = mti_FirstLowerRegion(parent->get_handle()); rgn != NULL; rgn = mti_NextRegion(rgn)) { - if (acc_fetch_fulltype(hdl) == accForGenerate) { + if (acc_fetch_fulltype(rgn) == accForGenerate) { std::string rgn_name = mti_GetRegionName(static_cast(rgn)); if (rgn_name.compare(0,name.length(),name) == 0) { FliObj *fli_obj = dynamic_cast(parent); From 85d080bbf4199f06bec6e9c0f1469173c4e7e09d Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 8 Mar 2016 16:52:32 -0700 Subject: [PATCH 1155/2656] Issue #393: Update the VPI interface to use GPI_GENARRAY --- lib/vpi/VpiCbHdl.cpp | 70 +++++++++++++++++++++++++++++++++++++++++-- lib/vpi/VpiImpl.cpp | 71 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 134 insertions(+), 7 deletions(-) diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index ac75922a..81dfb670 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -498,6 +498,13 @@ VpiIterator::VpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(i for (one2many = selected->begin(); one2many != selected->end(); one2many++) { + + /* GPI_GENARRAY are pseudo-regions and all that should be searched for are the sub-regions */ + if (m_parent->get_type() == GPI_GENARRAY && *one2many != vpiInternalScope) { + LOG_DEBUG("vpi_iterator vpiOneToManyT=%d skipped for GPI_GENARRAY type", *one2many); + continue; + } + iterator = vpi_iterate(*one2many, vpi_hdl); if (iterator) { @@ -582,12 +589,31 @@ GpiIterator::Status VpiIterator::next_handle(std::string &name, GpiObjHdl **hdl, if (!selected) return GpiIterator::END; + gpi_objtype_t obj_type = m_parent->get_type(); + std::string parent_name = m_parent->get_name(); + do { obj = NULL; if (m_iterator) { obj = vpi_scan(m_iterator); + /* For GPI_GENARRAY, only allow the generate statements through that match the name + * of the generate block. + */ + if (obj != NULL && obj_type == GPI_GENARRAY) { + if (vpi_get(vpiType, obj) == vpiGenScope) { + std::string rgn_name = vpi_get_str(vpiName, obj); + if (rgn_name.compare(0,parent_name.length(),parent_name) != 0) { + obj = NULL; + continue; + } + } else { + obj = NULL; + continue; + } + } + if (NULL == obj) { /* m_iterator will already be free'd internally here */ m_iterator = NULL; @@ -605,6 +631,12 @@ GpiIterator::Status VpiIterator::next_handle(std::string &name, GpiObjHdl **hdl, break; } + /* GPI_GENARRAY are pseudo-regions and all that should be searched for are the sub-regions */ + if (obj_type == GPI_GENARRAY && *one2many != vpiInternalScope) { + LOG_DEBUG("vpi_iterator vpiOneToManyT=%d skipped for GPI_GENARRAY type", *one2many); + continue; + } + m_iterator = vpi_iterate(*one2many, iter_obj); } while (!obj); @@ -634,13 +666,47 @@ GpiIterator::Status VpiIterator::next_handle(std::string &name, GpiObjHdl **hdl, return GpiIterator::NATIVE_NO_NAME; } - name = c_name; + + /* + * If the parent is not a generate loop, then watch for generate handles and create + * the pseudo-region. + * + * NOTE: Taking advantage of the "caching" to only create one pseudo-region object. + * Otherwise a list would be required and checked while iterating + */ + if (*one2many == vpiInternalScope && obj_type != GPI_GENARRAY && vpi_get(vpiType, obj) == vpiGenScope) { + std::string idx_str = c_name; + std::size_t found = idx_str.rfind("["); + + if (found != std::string::npos && found != 0) { + name = idx_str.substr(0,found); + obj = m_parent->get_handle(); + } else { + LOG_WARN("Unhandled Generate Loop Format - %s", name.c_str()); + name = c_name; + } + } else { + name = c_name; + } /* We try and create a handle internally, if this is not possible we return and GPI will try other implementations with the name */ - std::string fq_name = m_parent->get_fullname() + "." + name; + std::string fq_name = m_parent->get_fullname(); + + if (obj_type == GPI_GENARRAY) { + std::size_t found = name.rfind("["); + + if (found != std::string::npos) { + fq_name += name.substr(found); + } else { + LOG_WARN("Unhandled Sub-Element Format - %s", name.c_str()); + fq_name += "." + name; + } + } else { + fq_name += "." + name; + } LOG_DEBUG("vpi_scan found '%s'", fq_name.c_str()); VpiImpl *vpi_impl = reinterpret_cast(m_impl); diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index f3db64d8..1bfe0d2d 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -177,9 +177,17 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, case vpiGate: case vpiPrimTerm: case vpiGenScope: - case vpiGenScopeArray: - new_obj = new GpiObjHdl(this, new_hdl, to_gpi_objtype(type)); + case vpiGenScopeArray: { + std::string hdl_name = vpi_get_str(vpiName, new_hdl); + + if (hdl_name != name) { + LOG_DEBUG("Found pseudo-region %s", fq_name.c_str()); + new_obj = new GpiObjHdl(this, new_hdl, GPI_GENARRAY); + } else { + new_obj = new GpiObjHdl(this, new_hdl, to_gpi_objtype(type)); + } break; + } default: /* We should only print a warning here if the type is really verilog, It could be vhdl as some simulators allow qurying of both languages @@ -235,10 +243,45 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) writable.push_back('\0'); new_hdl = vpi_handle_by_name(&writable[0], NULL); + if (new_hdl == NULL) { - LOG_DEBUG("Unable to query vpi_get_handle_by_name %s", fq_name.c_str()); - return NULL; + /* If not found, check to see if the name of a generate loop */ + vpiHandle iter = vpi_iterate(vpiInternalScope, parent->get_handle()); + + if (iter != NULL) { + vpiHandle rgn; + for (rgn = vpi_scan(iter); rgn != NULL; rgn = vpi_scan(iter)) { + if (vpi_get(vpiType, rgn) == vpiGenScope) { + std::string rgn_name = vpi_get_str(vpiName, rgn); + if (rgn_name.compare(0,name.length(),name) == 0) { + new_hdl = parent->get_handle(); + vpi_free_object(iter); + break; + } + } + } + } + if (new_hdl == NULL) { + LOG_DEBUG("Unable to query vpi_get_handle_by_name %s", fq_name.c_str()); + return NULL; + } + } + + /* Generate Loops have inconsistent behavior across vpi tools. A "name" + * without an index, i.e. dut.loop vs dut.loop[0], will find a handl to vpiGenScopeArray, + * but not all tools support iterating over the vpiGenScopeArray. We don't want to create + * a GpiObjHdl to this type of vpiHandle. + * + * If this unique case is hit, we need to create the Pseudo-region, with the handle + * being equivalent to the parent handle. + */ + if (vpi_get(vpiType, new_hdl) == vpiGenScopeArray) { + vpi_free_object(new_hdl); + + new_hdl = parent->get_handle(); } + + GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { vpi_free_object(new_hdl); @@ -254,7 +297,25 @@ GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) vpiHandle vpi_hdl = parent_hdl->get_handle(); vpiHandle new_hdl; - new_hdl = vpi_handle_by_index(vpi_hdl, index); + if (parent_hdl->get_type() == GPI_GENARRAY) { + char buff[13]; // needs to be large enough to hold 2^32-1 in string form ('['+10+']'+'\0') + + snprintf(buff, 13, "[%u]", index); + + LOG_DEBUG("Native check create for index %u of parent %s (pseudo-region)", + index, + parent_hdl->get_name_str()); + + std::string idx = buff; + std::string hdl_name = parent_hdl->get_fullname() + idx; + std::vector writable(hdl_name.begin(), hdl_name.end()); + writable.push_back('\0'); + + new_hdl = vpi_handle_by_name(&writable[0], NULL); + } else { + new_hdl = vpi_handle_by_index(vpi_hdl, index); + } + if (new_hdl == NULL) { LOG_DEBUG("Unable to vpi_get_handle_by_index %s[%u]", vpi_get_str(vpiName, vpi_hdl), index); return NULL; From 1799899721bad0bbdf069d10cc432d24be40b672 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 14 Mar 2016 09:33:26 -0700 Subject: [PATCH 1156/2656] Issue #393: Update GPI interface and handle.py to properly handle arrays. --- cocotb/handle.py | 101 ++++++++++++++++---------------- include/gpi.h | 10 +++- lib/gpi/GpiCommon.cpp | 22 ++++++- lib/gpi/gpi_priv.h | 25 ++++++-- lib/simulator/simulatormodule.c | 66 ++++++++++++++++++++- lib/simulator/simulatormodule.h | 6 ++ 6 files changed, 170 insertions(+), 60 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 41fc0c90..7884a30a 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -220,34 +220,7 @@ def _discover_all(self): self._log.debug("%s" % e) continue - # This is slightly hacky, but we want generate loops to result in a list - # These are renamed in VHPI to __X where X is the index - import re - result = re.match("(?P.*)__(?P\d+)$", name) - if not result: - result = re.match("(?P.*)\((?P\d+)\)$", name) - - # Modelsim VPI returns names in standard form name[index] - if not result: - result = re.match("(?P.*)\[(?P\d+)\]$", name) - - if result: - index = int(result.group("index")) - name = result.group("name") - - if name not in self._sub_handles: - self._sub_handles[name] = [] - self._log.debug("creating new group for %s", name) - if len(self._sub_handles[name]) < index + 1: - delta = index - len(self._sub_handles[name]) + 1 - self._sub_handles[name].extend([None]*delta) - self._sub_handles[name][index] = hdl - self._log.debug("%s.%s[%d] is now %s", self._name, name, index, hdl._name) - #for something in self._sub_handles[name]: - # self._log.debug("%s: %s" % (type(something), something)) - else: - self._log.debug("%s didn't match an index pattern", name) - self._sub_handles[hdl._name.split(".")[-1]] = hdl + self._sub_handles[hdl._name.split(".")[-1]] = hdl self._discovered = True @@ -279,15 +252,6 @@ def __hasattr__(self, name): return new_handle - def __getitem__(self, index): - if index in self._sub_handles: - return self._sub_handles[index] - new_handle = simulator.get_handle_by_index(self._handle, index) - if not new_handle: - self._raise_testerror("%s contains no object at index %d" % (self._name, index)) - self._sub_handles[index] = SimHandle(new_handle) - return self._sub_handles[index] - class HierarchyArrayObject(HierarchyObject): """ Hierarchy Array objects don't have values, they are effectively scopes or namespaces @@ -347,6 +311,14 @@ def __len__(self): self._len = len(self._sub_handles) return self._len + def __getitem__(self, index): + if index in self._sub_handles: + return self._sub_handles[index] + new_handle = simulator.get_handle_by_index(self._handle, index) + if not new_handle: + self._raise_testerror("%s contains no object at index %d" % (self._name, index)) + self._sub_handles[index] = SimHandle(new_handle) + return self._sub_handles[index] class NonHierarchyObject(SimHandleBase): @@ -414,40 +386,61 @@ def _setcachedvalue(self, *args, **kwargs): def __le__(self, *args, **kwargs): raise ValueError("Not permissible to set values on a constant object") -class NonConstantObject(NonHierarchyObject): +class NonHierarchyIndexableObject(NonHierarchyObject): def __init__(self, handle): """ Args: _handle [integer] : vpi/vhpi handle to the simulator object """ NonHierarchyObject.__init__(self, handle) - self._r_edge = _RisingEdge(self) - self._f_edge = _FallingEdge(self) - self._e_edge = _Edge(self) - - def __hash__(self): - return self._handle + self._range_left = simulator.get_range_left(self._handle) + self._range_right = simulator.get_range_right(self._handle) + self._indexable = simulator.get_indexable(self._handle) def __getitem__(self, index): + if not self._indexable: + self._raise_testerror("%s %s is not indexable. Unable to get object at index %d" % (self._name, simulator.get_type_string(self._handle), index)) if index in self._sub_handles: return self._sub_handles[index] new_handle = simulator.get_handle_by_index(self._handle, index) if not new_handle: - self._raise_testerror("%s %s contains no object at index %d" % (self._name, simulator.get_type(self._handle), index)) + self._raise_testerror("%s %s contains no object at index %d" % (self._name, simulator.get_type_string(self._handle), index)) self._sub_handles[index] = SimHandle(new_handle) return self._sub_handles[index] def __iter__(self): - if len(self) == 1: + if not self._indexable: raise StopIteration - self._log.debug("Iterating with length %d" % len(self)) - for i in range(len(self)): + self._log.debug("Iterating with range [%d:%d]" % (self._range_left, self._range_right)) + for i in self._range(self._range_left, self._range_right): try: result = self[i] yield result except: continue + def _range(self, left, right): + if left > right: + while left >= right: + yield left + left = left - 1 + else: + while left <= right: + yield left + left = left + 1 + + +class NonConstantObject(NonHierarchyIndexableObject): + def __init__(self, handle): + """ + Args: + _handle [integer] : vpi/vhpi handle to the simulator object + """ + NonHierarchyIndexableObject.__init__(self, handle) + self._r_edge = _RisingEdge(self) + self._f_edge = _FallingEdge(self) + self._e_edge = _Edge(self) + def _getvalue(self): result = BinaryValue() result.binstr = self._get_value_str() @@ -679,7 +672,7 @@ def SimHandle(handle): simulator.MODULE: HierarchyObject, simulator.STRUCTURE: HierarchyObject, simulator.REG: ModifiableObject, - simulator.NETARRAY: ModifiableObject, + simulator.NETARRAY: NonHierarchyIndexableObject, simulator.REAL: RealObject, simulator.INTEGER: IntegerObject, simulator.ENUM: ModifiableObject, @@ -695,13 +688,17 @@ def SimHandle(handle): except KeyError: pass + t = simulator.get_type(handle) + # Special case for constants - if simulator.get_const(handle): - obj = ConstantObject(handle, simulator.get_type(handle)) + if simulator.get_const(handle) and not t in [simulator.MODULE, + simulator.STRUCTURE, + simulator.NETARRAY, + simulator.GENARRAY]: + obj = ConstantObject(handle, t) _handle2obj[handle] = obj return obj - t = simulator.get_type(handle) if t not in _type2cls: raise TestError("Couldn't find a matching object for GPI type %d" % t) obj = _type2cls[t](handle) diff --git a/include/gpi.h b/include/gpi.h index 3f09e858..f96309e0 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -119,7 +119,7 @@ void gpi_get_sim_precision(int32_t *precision); // Should be freed with gpi_free_handle gpi_sim_hdl gpi_get_root_handle(const char *name); gpi_sim_hdl gpi_get_handle_by_name(gpi_sim_hdl parent, const char *name); -gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index); +gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, int32_t index); void gpi_free_handle(gpi_sim_hdl gpi_hdl); // Types that can be passed to the iterator. @@ -162,6 +162,12 @@ gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator); // Returns the number of objects in the collection of the handle int gpi_get_num_elems(gpi_sim_hdl gpi_sim_hdl); +// Returns the left side of the range constraint +int gpi_get_range_left(gpi_sim_hdl gpi_sim_hdl); + +// Returns the right side of the range constraint +int gpi_get_range_right(gpi_sim_hdl gpi_sim_hdl); + // Functions for querying the properties of a handle // Caller responsible for freeing the returned string. // This is all slightly verbose but it saves having to enumerate various value types @@ -179,6 +185,8 @@ gpi_objtype_t gpi_get_object_type(gpi_sim_hdl gpi_hdl); // Determine whether an object value is constant (parameters / generics etc) int gpi_is_constant(gpi_sim_hdl gpi_hdl); +// Determine whether an object is indexable +int gpi_is_indexable(gpi_sim_hdl gpi_hdl); // Functions for setting the properties of a handle void gpi_set_signal_value_real(gpi_sim_hdl gpi_hdl, double value); diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index d1294f1d..71203f42 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -330,7 +330,7 @@ gpi_sim_hdl gpi_get_handle_by_name(gpi_sim_hdl parent, const char *name) return hdl; } -gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, uint32_t index) +gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, int32_t index) { vector::iterator iter; @@ -456,6 +456,14 @@ int gpi_is_constant(gpi_sim_hdl sig_hdl) return 0; } +int gpi_is_indexable(gpi_sim_hdl sig_hdl) +{ + GpiObjHdl *obj_hdl = sim_to_hdl(sig_hdl); + if (obj_hdl->get_indexable()) + return 1; + return 0; +} + void gpi_set_signal_value_long(gpi_sim_hdl sig_hdl, long value) { GpiSignalObjHdl *obj_hdl = sim_to_hdl(sig_hdl); @@ -481,6 +489,18 @@ int gpi_get_num_elems(gpi_sim_hdl sig_hdl) return obj_hdl->get_num_elems(); } +int gpi_get_range_left(gpi_sim_hdl sig_hdl) +{ + GpiObjHdl *obj_hdl = sim_to_hdl(sig_hdl); + return obj_hdl->get_range_left(); +} + +int gpi_get_range_right(gpi_sim_hdl sig_hdl) +{ + GpiObjHdl *obj_hdl = sim_to_hdl(sig_hdl); + return obj_hdl->get_range_right(); +} + gpi_sim_hdl gpi_register_value_change_callback(int (*gpi_function)(const void *), void *gpi_cb_data, gpi_sim_hdl sig_hdl, diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 200b9b0e..07574335 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -95,17 +95,26 @@ class GpiObjHdl : public GpiHdl { public: GpiObjHdl(GpiImplInterface *impl) : GpiHdl(impl, NULL), m_num_elems(0), + m_indexable(false), + m_range_left(-1), + m_range_right(-1), m_fullname("unknown"), m_type(GPI_UNKNOWN), m_const(false) { } GpiObjHdl(GpiImplInterface *impl, void *hdl, gpi_objtype_t objtype) : GpiHdl(impl, hdl), m_num_elems(0), + m_indexable(false), + m_range_left(-1), + m_range_right(-1), m_fullname("unknown"), m_type(objtype), m_const(false) { } GpiObjHdl(GpiImplInterface *impl, void *hdl, gpi_objtype_t objtype, bool is_const) : GpiHdl(impl, hdl), m_num_elems(0), + m_indexable(false), + m_range_left(-1), + m_range_right(-1), m_fullname("unknown"), m_type(objtype), m_const(is_const) { } @@ -120,6 +129,9 @@ class GpiObjHdl : public GpiHdl { LOG_DEBUG("%s has %d elements", m_name.c_str(), m_num_elems); return m_num_elems; } + int get_range_left(void) { return m_range_left; } + int get_range_right(void) { return m_range_right; } + int get_indexable(void) { return m_indexable; } const std::string & get_name(void); const std::string & get_fullname(void); @@ -128,11 +140,14 @@ class GpiObjHdl : public GpiHdl { virtual int initialise(std::string &name, std::string &full_name); protected: - int m_num_elems; - std::string m_name; - std::string m_fullname; + int m_num_elems; + bool m_indexable; + int m_range_left; + int m_range_right; + std::string m_name; + std::string m_fullname; gpi_objtype_t m_type; - bool m_const; + bool m_const; }; @@ -292,7 +307,7 @@ class GpiImplInterface { /* Hierachy related */ virtual GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent) = 0; - virtual GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent) = 0; + virtual GpiObjHdl* native_check_create(int32_t index, GpiObjHdl *parent) = 0; virtual GpiObjHdl* native_check_create(void *raw_hdl, GpiObjHdl *parent) = 0; virtual GpiObjHdl *get_root_handle(const char *name) = 0; virtual GpiIterator *iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) = 0; diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index dc1afb72..2366423a 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -708,7 +708,7 @@ static PyObject *get_handle_by_name(PyObject *self, PyObject *args) static PyObject *get_handle_by_index(PyObject *self, PyObject *args) { - uint32_t index; + int32_t index; gpi_sim_hdl hdl; gpi_sim_hdl result; PyObject *value; @@ -825,6 +825,28 @@ static PyObject *get_const(PyObject *self, PyObject *args) return pyresult; } +static PyObject *get_indexable(PyObject *self, PyObject *args) +{ + int result; + gpi_sim_hdl hdl; + PyObject *pyresult; + + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + if (!PyArg_ParseTuple(args, "l", &hdl)) { + DROP_GIL(gstate); + return NULL; + } + + result = gpi_is_indexable((gpi_sim_hdl)hdl); + pyresult = Py_BuildValue("i", result); + + DROP_GIL(gstate); + + return pyresult; +} + static PyObject *get_type_string(PyObject *self, PyObject *args) { const char *result; @@ -906,6 +928,48 @@ static PyObject *get_num_elems(PyObject *self, PyObject *args) return retstr; } +static PyObject *get_range_left(PyObject *self, PyObject *args) +{ + gpi_sim_hdl hdl; + PyObject *retstr; + + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + if (!PyArg_ParseTuple(args, "l", &hdl)) { + DROP_GIL(gstate); + return NULL; + } + + int rng_left = gpi_get_range_left((gpi_sim_hdl)hdl); + retstr = Py_BuildValue("i", rng_left); + + DROP_GIL(gstate); + + return retstr; +} + +static PyObject *get_range_right(PyObject *self, PyObject *args) +{ + gpi_sim_hdl hdl; + PyObject *retstr; + + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + if (!PyArg_ParseTuple(args, "l", &hdl)) { + DROP_GIL(gstate); + return NULL; + } + + int rng_right = gpi_get_range_right((gpi_sim_hdl)hdl); + retstr = Py_BuildValue("i", rng_right); + + DROP_GIL(gstate); + + return retstr; +} + static PyObject *stop_simulator(PyObject *self, PyObject *args) { gpi_sim_end(); diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 9e8b4496..1aec2741 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -70,8 +70,11 @@ static PyObject *get_root_handle(PyObject *self, PyObject *args); static PyObject *get_name_string(PyObject *self, PyObject *args); static PyObject *get_type(PyObject *self, PyObject *args); static PyObject *get_const(PyObject *self, PyObject *args); +static PyObject *get_indexable(PyObject *self, PyObject *args); static PyObject *get_type_string(PyObject *self, PyObject *args); static PyObject *get_num_elems(PyObject *self, PyObject *args); +static PyObject *get_range_left(PyObject *self, PyObject *args); +static PyObject *get_range_right(PyObject *self, PyObject *args); static PyObject *register_timed_callback(PyObject *self, PyObject *args); static PyObject *register_value_change_callback(PyObject *self, PyObject *args); static PyObject *register_readonly_callback(PyObject *self, PyObject *args); @@ -104,7 +107,10 @@ static PyMethodDef SimulatorMethods[] = { {"get_type_string", get_type_string, METH_VARARGS, "Get the type of an object as a string"}, {"get_type", get_type, METH_VARARGS, "Get the type of an object, mapped to a GPI enumeration"}, {"get_const", get_const, METH_VARARGS, "Get a flag indicating whether the object is a constant"}, + {"get_indexable", get_indexable, METH_VARARGS, "Get a flag indicating whether the object is indexable"}, {"get_num_elems", get_num_elems, METH_VARARGS, "Get the number of elements contained in the handle"}, + {"get_range_left", get_range_left, METH_VARARGS, "Get the left-side range of elements contained in the handle"}, + {"get_range_right", get_range_right, METH_VARARGS, "Get the right-side range of elements contained in the handle"}, {"register_timed_callback", register_timed_callback, METH_VARARGS, "Register a timed callback"}, {"register_value_change_callback", register_value_change_callback, METH_VARARGS, "Register a signal change callback"}, {"register_readonly_callback", register_readonly_callback, METH_VARARGS, "Register a callback for readonly section"}, From 5ec787ebca77dcfe7a3a302b30fc5300a09e2a6b Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 14 Mar 2016 09:35:09 -0700 Subject: [PATCH 1157/2656] Issue #393: Update FLI for the new array interface --- lib/fli/FliImpl.cpp | 164 ++++++++++++++++++++++++++++-------------- lib/fli/FliImpl.h | 20 +++--- lib/fli/FliObjHdl.cpp | 78 +++++++++++++------- 3 files changed, 176 insertions(+), 86 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index c20939e8..beb4d6f9 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -159,7 +159,7 @@ GpiObjHdl *FliImpl::create_gpi_obj_from_handle(void *hdl, std::string &name, std switch (typeKind) { case MTI_TYPE_ENUM: if (isValueLogic(valType)) { - new_obj = new FliLogicObjHdl(this, hdl, GPI_ENUM, is_const, accType, accFullType, is_var, valType, typeKind); + new_obj = new FliLogicObjHdl(this, hdl, GPI_REGISTER, is_const, accType, accFullType, is_var, valType, typeKind); } else if (isValueBoolean(valType) || isValueChar(valType)) { new_obj = new FliIntObjHdl(this, hdl, GPI_INTEGER, is_const, accType, accFullType, is_var, valType, typeKind); } else { @@ -180,15 +180,15 @@ GpiObjHdl *FliImpl::create_gpi_obj_from_handle(void *hdl, std::string &name, std switch (elemTypeKind) { case MTI_TYPE_ENUM: if (isValueLogic(elemType)) { - new_obj = new FliLogicObjHdl(this, hdl, GPI_ARRAY, is_const, accType, accFullType, is_var, valType, typeKind); // std_logic_vector + new_obj = new FliLogicObjHdl(this, hdl, GPI_REGISTER, is_const, accType, accFullType, is_var, valType, typeKind); // std_logic_vector } else if (isValueChar(elemType)) { new_obj = new FliStringObjHdl(this, hdl, GPI_STRING, is_const, accType, accFullType, is_var, valType, typeKind); } else { - new_obj = new FliValueObjHdl(this, hdl, GPI_MODULE, is_const, accType, accFullType, is_var, valType, typeKind); // array of enums + new_obj = new FliValueObjHdl(this, hdl, GPI_ARRAY, false, accType, accFullType, is_var, valType, typeKind); // array of enums } break; default: - new_obj = new FliValueObjHdl(this, hdl, GPI_MODULE, is_const, accType, accFullType, is_var, valType, typeKind);// array of (array, Integer, Real, Record, etc.) + new_obj = new FliValueObjHdl(this, hdl, GPI_ARRAY, false, accType, accFullType, is_var, valType, typeKind);// array of (array, Integer, Real, Record, etc.) } } break; @@ -320,31 +320,19 @@ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) * @brief Determine whether a simulation object is native to FLI and create * a handle if it is */ -GpiObjHdl* FliImpl::native_check_create(uint32_t index, GpiObjHdl *parent) +GpiObjHdl* FliImpl::native_check_create(int32_t index, GpiObjHdl *parent) { - gpi_objtype_t obj_type = parent->get_type(); - if (obj_type == GPI_MODULE || obj_type == GPI_GENARRAY || obj_type == GPI_ARRAY || obj_type == GPI_STRING) { - FliObj *fli_obj = dynamic_cast(parent); - int type = fli_obj->get_acc_type(); - char buff[15]; + HANDLE hdl; + PLI_INT32 accType; + PLI_INT32 accFullType; + char buff[14]; - LOG_DEBUG("Looking for index %u from %s", index, parent->get_name_str()); + gpi_objtype_t obj_type = parent->get_type(); - /* To behave like the current VHPI interface, index needs to be properly translated. - * Index of 0 needs to map to "signal'left" and Index of "length-1" mapping to "signal'right" - */ - if (!isTypeValue(type)) { - snprintf(buff, 15, "(%u)", index); - } else { - FliValueObjHdl *fli_obj = reinterpret_cast(parent); - mtiTypeIdT typeId = fli_obj->get_fli_typeid(); + if (obj_type == GPI_GENARRAY) { + LOG_DEBUG("Looking for index %d from %s", index, parent->get_name_str()); - if (mti_TickDir(typeId) < 0) { - snprintf(buff, 15, "(%u)", mti_TickLeft(typeId) - index); - } else { - snprintf(buff, 15, "(%u)", mti_TickLeft(typeId) + index); - } - } + snprintf(buff, 14, "(%d)", index); std::string idx = buff; std::string name = parent->get_name() + idx; @@ -353,35 +341,64 @@ GpiObjHdl* FliImpl::native_check_create(uint32_t index, GpiObjHdl *parent) std::vector writable(fq_name.begin(), fq_name.end()); writable.push_back('\0'); - HANDLE hdl; - PLI_INT32 accType; - PLI_INT32 accFullType; - if ((hdl = mti_FindRegion(&writable[0])) != NULL) { accType = acc_fetch_type(hdl); accFullType = acc_fetch_fulltype(hdl); LOG_DEBUG("Found region %s -> %p", fq_name.c_str(), hdl); LOG_DEBUG(" Type: %d", accType); LOG_DEBUG(" Full Type: %d", accFullType); - } else if ((hdl = mti_FindSignal(&writable[0])) != NULL) { + } else { + LOG_DEBUG("Didn't find anything named %s", &writable[0]); + return NULL; + } + + return create_gpi_obj_from_handle(hdl, name, fq_name, accType, accFullType); + } else if (obj_type == GPI_REGISTER || obj_type == GPI_ARRAY || obj_type == GPI_STRING) { + int left = parent->get_range_left(); + int right = parent->get_range_right(); + bool ascending = (left < right); + + if (( ascending && (index < left || index > right)) || + (!ascending && (index > left || index < right))) { + LOG_ERROR("Invalid Index - Index %d is not in the range of [%d:%d]", index, left, right); + return NULL; + } + + FliValueObjHdl *fli_obj = reinterpret_cast(parent); + + if (obj_type == GPI_REGISTER && fli_obj->get_fli_typekind() == MTI_TYPE_ENUM) { + LOG_ERROR("Logic Type is not a vector that can be indexed"); + return NULL; + } + + LOG_DEBUG("Looking for index %u from %s", index, parent->get_name_str()); + + if ((hdl = fli_obj->get_sub_hdl(index)) == NULL) { + LOG_DEBUG("Didn't find the index %d", index); + return NULL; + } + + snprintf(buff, 14, "(%d)", index); + + std::string idx = buff; + std::string name = parent->get_name() + idx; + std::string fq_name = parent->get_fullname() + idx; + + if (!(fli_obj->is_var())) { accType = acc_fetch_type(hdl); accFullType = acc_fetch_fulltype(hdl); LOG_DEBUG("Found a signal %s -> %p", fq_name.c_str(), hdl); LOG_DEBUG(" Type: %d", accType); LOG_DEBUG(" Full Type: %d", accFullType); - } else if ((hdl = mti_FindVar(&writable[0])) != NULL) { + } else { accFullType = accType = mti_GetVarKind(static_cast(hdl)); LOG_DEBUG("Found a variable %s -> %p", fq_name.c_str(), hdl); LOG_DEBUG(" Type: %d", accType); LOG_DEBUG(" Full Type: %d", accFullType); - } else { - LOG_DEBUG("Didn't find anything named %s", &writable[0]); - return NULL; } - return create_gpi_obj_from_handle(hdl, name, fq_name, accType, accFullType); } else { - LOG_ERROR("FLI: Parent of type %d must be of type GPI_MODULE, GPI_GENARRAY, GPI_STRING or GPI_ARRAY to have an index.", parent->get_type()); + LOG_ERROR("FLI: Parent of type %d must be of type GPI_GENARRAY, GPI_REGISTER, GPI_ARRAY, or GPI_STRING to have an index.", parent->get_type()); return NULL; } } @@ -866,31 +883,74 @@ void FliIterator::populate_handle_list(FliIterator::OneToMany childType) } break; case FliIterator::OTM_SIGNAL_SUB_ELEMENTS: - if (m_parent->get_type() == GPI_MODULE || m_parent->get_type() == GPI_STRUCTURE) { - mtiSignalIdT parent = m_parent->get_handle(); - - mtiTypeIdT type = mti_GetSignalType(parent); - mtiSignalIdT *ids = mti_GetSignalSubelements(parent,0); + if (m_parent->get_type() == GPI_REGISTER || m_parent->get_type() == GPI_ARRAY || m_parent->get_type() == GPI_STRUCTURE) { + FliValueObjHdl *fli_obj = reinterpret_cast(m_parent); - for (int i = 0; i < mti_TickLength(type); i++) { - m_sigs.push_back(ids[i]); + if (m_parent->get_type() == GPI_REGISTER && fli_obj->get_fli_typekind() == MTI_TYPE_ENUM) { + break; } - mti_VsimFree(ids); + if (m_parent->get_type() == GPI_STRUCTURE) { + mtiSignalIdT parent = m_parent->get_handle(); + + mtiTypeIdT type = mti_GetSignalType(parent); + mtiSignalIdT *ids = mti_GetSignalSubelements(parent,NULL); + + LOG_DEBUG("GPI_STRUCTURE: %d fields", mti_TickLength(type)); + for (int i = 0; i < mti_TickLength(type); i++) { + m_sigs.push_back(ids[i]); + } + mti_VsimFree(ids); + } else { + int left = m_parent->get_range_left(); + int right = m_parent->get_range_right(); + + if (left > right) { + for (int i = left; i >= right; i--) { + m_sigs.push_back(static_cast(fli_obj->get_sub_hdl(i))); + } + } else { + for (int i = left; i <= right; i++) { + m_sigs.push_back(static_cast(fli_obj->get_sub_hdl(i))); + } + } + } } break; case FliIterator::OTM_VARIABLE_SUB_ELEMENTS: - if (m_parent->get_type() == GPI_MODULE || m_parent->get_type() == GPI_STRUCTURE) { - mtiVariableIdT parent = m_parent->get_handle(); - - mtiTypeIdT type = mti_GetVarType(parent); - mtiVariableIdT *ids = mti_GetVarSubelements(parent,0); + if (m_parent->get_type() == GPI_REGISTER || m_parent->get_type() == GPI_ARRAY || m_parent->get_type() == GPI_STRUCTURE) { + FliValueObjHdl *fli_obj = reinterpret_cast(m_parent); - for (int i = 0; i < mti_TickLength(type); i++) { - m_vars.push_back(ids[i]); + if (m_parent->get_type() == GPI_REGISTER && fli_obj->get_fli_typekind() == MTI_TYPE_ENUM) { + break; } - mti_VsimFree(ids); + if (m_parent->get_type() == GPI_STRUCTURE) { + mtiVariableIdT parent = m_parent->get_handle(); + + mtiTypeIdT type = mti_GetVarType(parent); + mtiVariableIdT *ids = mti_GetVarSubelements(parent,NULL); + + LOG_DEBUG("GPI_STRUCTURE: %d fields", mti_TickLength(type)); + for (int i = 0; i < mti_TickLength(type); i++) { + m_vars.push_back(ids[i]); + } + + mti_VsimFree(ids); + } else { + int left = m_parent->get_range_left(); + int right = m_parent->get_range_right(); + + if (left > right) { + for (int i = left; i >= right; i--) { + m_vars.push_back(static_cast(fli_obj->get_sub_hdl(i))); + } + } else { + for (int i = left; i <= right; i++) { + m_vars.push_back(static_cast(fli_obj->get_sub_hdl(i))); + } + } + } } break; default: diff --git a/lib/fli/FliImpl.h b/lib/fli/FliImpl.h index f6bb541e..aae50ceb 100644 --- a/lib/fli/FliImpl.h +++ b/lib/fli/FliImpl.h @@ -238,11 +238,14 @@ class FliValueObjHdl : public FliSignalObjHdl { FliSignalObjHdl(impl, hdl, objtype, is_const, acc_type, acc_full_type, is_var), m_fli_type(typeKind), m_val_type(valType), - m_val_buff(NULL) { } + m_val_buff(NULL), + m_sub_hdls(NULL) { } virtual ~FliValueObjHdl() { - if (m_val_buff) + if (m_val_buff != NULL) free(m_val_buff); + if (m_sub_hdls != NULL) + mti_VsimFree(m_sub_hdls); } virtual const char* get_signal_value_binstr(void); @@ -254,6 +257,8 @@ class FliValueObjHdl : public FliSignalObjHdl { virtual int set_signal_value(const double value); virtual int set_signal_value(std::string &value); + virtual void *get_sub_hdl(int index); + virtual int initialise(std::string &name, std::string &fq_name); mtiTypeKindT get_fli_typekind(void) { return m_fli_type; } @@ -263,6 +268,7 @@ class FliValueObjHdl : public FliSignalObjHdl { mtiTypeKindT m_fli_type; mtiTypeIdT m_val_type; char *m_val_buff; + void **m_sub_hdls; }; class FliEnumObjHdl : public FliValueObjHdl { @@ -314,14 +320,13 @@ class FliLogicObjHdl : public FliValueObjHdl { is_var, valType, typeKind), - m_ascending(false), m_mti_buff(NULL), m_value_enum(NULL), m_num_enum(0), m_enum_map() { } virtual ~FliLogicObjHdl() { - if (m_mti_buff) + if (m_mti_buff != NULL) free(m_mti_buff); } @@ -334,7 +339,6 @@ class FliLogicObjHdl : public FliValueObjHdl { private: - bool m_ascending; char *m_mti_buff; char **m_value_enum; // Do Not Free mtiInt32T m_num_enum; @@ -379,7 +383,7 @@ class FliRealObjHdl : public FliValueObjHdl { m_mti_buff(NULL) { } virtual ~FliRealObjHdl() { - if (m_mti_buff) + if (m_mti_buff != NULL) free(m_mti_buff); } @@ -408,7 +412,7 @@ class FliStringObjHdl : public FliValueObjHdl { m_mti_buff(NULL) { } virtual ~FliStringObjHdl() { - if (m_mti_buff) + if (m_mti_buff != NULL) free(m_mti_buff); } @@ -482,7 +486,7 @@ class FliImpl : public GpiImplInterface { /* Hierachy related */ GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); - GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); + GpiObjHdl* native_check_create(int32_t index, GpiObjHdl *parent); GpiObjHdl* native_check_create(void *raw_hdl, GpiObjHdl *paret); GpiObjHdl *get_root_handle(const char *name); GpiIterator *iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type); diff --git a/lib/fli/FliObjHdl.cpp b/lib/fli/FliObjHdl.cpp index ce314593..db6165ac 100644 --- a/lib/fli/FliObjHdl.cpp +++ b/lib/fli/FliObjHdl.cpp @@ -63,36 +63,22 @@ GpiCbHdl *FliSignalObjHdl::value_change_cb(unsigned int edge) int FliObjHdl::initialise(std::string &name, std::string &fq_name) { bool is_signal = (get_acc_type() == accSignal || get_acc_full_type() == accAliasSignal); - bool is_value = (is_signal || get_acc_type() == accAlias || - get_acc_type() == accVariable || get_acc_type() == accVHDLConstant || - get_acc_type() == accGeneric); + mtiTypeIdT typeId; switch (get_type()) { - case GPI_STRUCTURE: { - mtiTypeIdT recType; - if (is_signal) { - recType = mti_GetSignalType(get_handle()); - } else { - recType = mti_GetVarType(get_handle()); - } - m_num_elems = mti_GetNumRecordElements(recType); - } - break; - case GPI_MODULE: - if (!is_value) { - m_num_elems = 1; + case GPI_STRUCTURE: + if (is_signal) { + typeId = mti_GetSignalType(get_handle()); } else { - mtiTypeIdT arrayType; - if (is_signal) { - arrayType = mti_GetSignalType(get_handle()); - } else { - arrayType = mti_GetVarType(get_handle()); - } - m_num_elems = mti_TickLength(arrayType); + typeId = mti_GetVarType(get_handle()); } + + m_num_elems = mti_GetNumRecordElements(typeId); break; case GPI_GENARRAY: - m_num_elems = 1; //Unable to determine length of this pseudo-region + m_indexable = true; + case GPI_MODULE: + m_num_elems = 1; break; default: LOG_CRITICAL("Invalid object type for FliObjHdl. (%s (%s))", name.c_str(), get_type_str()); @@ -110,6 +96,13 @@ int FliSignalObjHdl::initialise(std::string &name, std::string &fq_name) int FliValueObjHdl::initialise(std::string &name, std::string &fq_name) { + if (get_type() == GPI_ARRAY) { + m_range_left = mti_TickLeft(m_val_type); + m_range_right = mti_TickRight(m_val_type); + m_num_elems = mti_TickLength(m_val_type); + m_indexable = true; + } + return FliSignalObjHdl::initialise(name, fq_name); } @@ -155,6 +148,33 @@ int FliValueObjHdl::set_signal_value(const double value) return -1; } +void *FliValueObjHdl::get_sub_hdl(int index) { + if (m_sub_hdls == NULL) { + if (is_var()) { + m_sub_hdls = (void **)mti_GetVarSubelements(get_handle(), NULL); + } else { + m_sub_hdls = (void **)mti_GetSignalSubelements(get_handle(), NULL); + } + } + + int idx; + + if (m_range_left > m_range_right) { + idx = m_range_left - index; + + if (idx < 0 || idx > (m_range_left-m_range_right)) { + return NULL; + } + } else { + idx = index - m_range_left; + + if (idx < 0 || idx > (m_range_right-m_range_left)) { + return NULL; + } + } + return m_sub_hdls[idx]; +} + int FliEnumObjHdl::initialise(std::string &name, std::string &fq_name) { m_num_elems = 1; @@ -209,10 +229,13 @@ int FliLogicObjHdl::initialise(std::string &name, std::string &fq_name) case MTI_TYPE_ARRAY: { mtiTypeIdT elemType = mti_GetArrayElementType(m_val_type); - m_ascending = (mti_TickDir(m_val_type) == 1); + m_range_left = mti_TickLeft(m_val_type); + m_range_right = mti_TickRight(m_val_type); + m_num_elems = mti_TickLength(m_val_type); + m_indexable = true; + m_value_enum = mti_GetEnumValues(elemType); m_num_enum = mti_TickLength(elemType); - m_num_elems = mti_TickLength(m_val_type); m_mti_buff = (char*)malloc(sizeof(*m_mti_buff) * m_num_elems); if (!m_mti_buff) { @@ -434,7 +457,10 @@ int FliRealObjHdl::set_signal_value(const double value) int FliStringObjHdl::initialise(std::string &name, std::string &fq_name) { + m_range_left = mti_TickLeft(m_val_type); + m_range_right = mti_TickRight(m_val_type); m_num_elems = mti_TickLength(m_val_type); + m_indexable = true; m_mti_buff = (char*)malloc(sizeof(char) * m_num_elems); if (!m_mti_buff) { From 54d2965dc29f226f3971ca6e5b6b3cc67149fbc1 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 15 Mar 2016 13:05:55 -0700 Subject: [PATCH 1158/2656] Issue #393: Update the VPI Interface for the new array interface --- lib/vpi/VpiCbHdl.cpp | 139 +++++++++++++++++++++++++++++++++++++++++++ lib/vpi/VpiImpl.cpp | 111 +++++++++++++++++++++++++++++----- lib/vpi/VpiImpl.h | 11 +++- 3 files changed, 244 insertions(+), 17 deletions(-) diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 81dfb670..62bba6ea 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -119,6 +119,91 @@ int VpiCbHdl::cleanup_callback(void) return 0; } +int VpiArrayObjHdl::initialise(std::string &name, std::string &fq_name) { + vpiHandle hdl = GpiObjHdl::get_handle(); + + m_indexable = true; + + int range_idx = 0; + + /* Need to determine if this is a pseudo-handle to be able to select the correct range */ + std::string hdl_name = vpi_get_str(vpiName, hdl); + + /* Removing the hdl_name from the name will leave the psuedo-indices */ + if (hdl_name.length() < name.length()) { + std::string idx_str = name.substr(hdl_name.length()); + + while (idx_str.length() > 0) { + std::size_t found = idx_str.find_first_of("]"); + + if (found != std::string::npos) { + ++range_idx; + idx_str = idx_str.substr(found+1); + } else { + break; + } + } + } + + /* After determining the range_idx, get the range and set the limits */ + vpiHandle iter = vpi_iterate(vpiRange, hdl); + + s_vpi_value val; + val.format = vpiIntVal; + + if (iter != NULL) { + vpiHandle rangeHdl; + int idx = 0; + + while ((rangeHdl = vpi_scan(iter)) != NULL) { + if (idx == range_idx) { + break; + } + ++idx; + } + + if (rangeHdl == NULL) { + LOG_CRITICAL("Unable to get Range for indexable object"); + } else { + vpi_free_object(iter); // Need to free iterator since exited early + + vpi_get_value(vpi_handle(vpiLeftRange,rangeHdl),&val); + check_vpi_error(); + m_range_left = val.value.integer; + + vpi_get_value(vpi_handle(vpiRightRange,rangeHdl),&val); + check_vpi_error(); + m_range_right = val.value.integer; + } + } else if (range_idx == 0) { + vpi_get_value(vpi_handle(vpiLeftRange,hdl),&val); + check_vpi_error(); + m_range_left = val.value.integer; + + vpi_get_value(vpi_handle(vpiRightRange,hdl),&val); + check_vpi_error(); + m_range_right = val.value.integer; + } else { + LOG_CRITICAL("Unable to get Range for indexable object"); + } + + /* vpiSize will return a size that is incorrect for multi-dimensional arrays so use the range + * to calculate the m_num_elems. + * + * For example: + * wire [7:0] sig_t4 [0:3][7:4] + * + * The size of "sig_t4" will be reported as 16 through the vpi interface. + */ + if (m_range_left > m_range_right) { + m_num_elems = m_range_left - m_range_right + 1; + } else { + m_num_elems = m_range_right - m_range_left + 1; + } + + return GpiObjHdl::initialise(name, fq_name); +} + int VpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { int32_t type = vpi_get(vpiType, GpiObjHdl::get_handle()); if ((vpiIntVar == type) || @@ -127,6 +212,55 @@ int VpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { m_num_elems = 1; } else { m_num_elems = vpi_get(vpiSize, GpiObjHdl::get_handle()); + + if (GpiObjHdl::get_type() == GPI_STRING) { + m_indexable = false; // Don't want to iterate over indices + m_range_left = 0; + m_range_right = m_num_elems-1; + } else if (GpiObjHdl::get_type() == GPI_REGISTER) { + vpiHandle hdl = GpiObjHdl::get_handle(); + + m_indexable = vpi_get(vpiVector, hdl); + + if (m_indexable) { + s_vpi_value val; + vpiHandle iter; + + val.format = vpiIntVal; + + iter = vpi_iterate(vpiRange, hdl); + + /* Only ever need the first "range" */ + if (iter != NULL) { + vpiHandle rangeHdl = vpi_scan(iter); + + vpi_free_object(iter); + + if (rangeHdl != NULL) { + vpi_get_value(vpi_handle(vpiLeftRange,rangeHdl),&val); + check_vpi_error(); + m_range_left = val.value.integer; + + vpi_get_value(vpi_handle(vpiRightRange,rangeHdl),&val); + check_vpi_error(); + m_range_right = val.value.integer; + } else { + LOG_CRITICAL("Unable to get Range for indexable object"); + } + } + else { + vpi_get_value(vpi_handle(vpiLeftRange,hdl),&val); + check_vpi_error(); + m_range_left = val.value.integer; + + vpi_get_value(vpi_handle(vpiRightRange,hdl),&val); + check_vpi_error(); + m_range_right = val.value.integer; + } + + LOG_DEBUG("VPI: Indexable Object initialised with range [%d:%d] and length >%d<", m_range_left, m_range_right, m_num_elems); + } + } } LOG_DEBUG("VPI: %s initialised with %d elements", name.c_str(), m_num_elems); return GpiObjHdl::initialise(name, fq_name); @@ -387,6 +521,10 @@ void vpi_mappings(GpiIteratorMapping &map) vpiReg, vpiRegArray, vpiMemory, + vpiIntegerVar, + vpiRealVar, + vpiStructVar, + vpiStructNet, //vpiVariables // Aldec SEGV on plain Verilog vpiNamedEvent, vpiNamedEventArray, @@ -422,6 +560,7 @@ void vpi_mappings(GpiIteratorMapping &map) vpiPrimitive, vpiPrimitiveArray, vpiAttribute, + vpiMember, 0 }; map.add_to_options(vpiStructVar, &struct_options[0]); diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 1bfe0d2d..fbc1c465 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -149,8 +149,6 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, case vpiNetBit: case vpiReg: case vpiRegBit: - case vpiRegArray: - case vpiNetArray: case vpiEnumNet: case vpiEnumVar: case vpiIntVar: @@ -162,14 +160,18 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, case vpiParameter: new_obj = new VpiSignalObjHdl(this, new_hdl, to_gpi_objtype(type), true); break; + case vpiRegArray: + case vpiNetArray: + case vpiInterfaceArray: + case vpiPackedArrayVar: + new_obj = new VpiArrayObjHdl(this, new_hdl, to_gpi_objtype(type)); + break; case vpiStructVar: case vpiStructNet: case vpiModule: case vpiInterface: case vpiModport: - case vpiInterfaceArray: case vpiRefObj: - case vpiPackedArrayVar: case vpiPort: case vpiAlways: case vpiFunction: @@ -291,18 +293,20 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) return new_obj; } -GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) +GpiObjHdl* VpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) { GpiObjHdl *parent_hdl = sim_to_hdl(parent); vpiHandle vpi_hdl = parent_hdl->get_handle(); - vpiHandle new_hdl; + vpiHandle new_hdl = NULL; + + char buff[14]; // needs to be large enough to hold -2^31 to 2^31-1 in string form ('['+'-'10+']'+'\0') - if (parent_hdl->get_type() == GPI_GENARRAY) { - char buff[13]; // needs to be large enough to hold 2^32-1 in string form ('['+10+']'+'\0') + gpi_objtype_t obj_type = parent_hdl->get_type(); - snprintf(buff, 13, "[%u]", index); + if (obj_type == GPI_GENARRAY) { + snprintf(buff, 14, "[%d]", index); - LOG_DEBUG("Native check create for index %u of parent %s (pseudo-region)", + LOG_DEBUG("Native check create for index %d of parent %s (pseudo-region)", index, parent_hdl->get_name_str()); @@ -312,22 +316,97 @@ GpiObjHdl* VpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) writable.push_back('\0'); new_hdl = vpi_handle_by_name(&writable[0], NULL); - } else { + } else if (obj_type == GPI_REGISTER || obj_type == GPI_ARRAY || obj_type == GPI_STRING) { new_hdl = vpi_handle_by_index(vpi_hdl, index); + + /* vpi_handle_by_index() doesn't work for all simulators when dealing with a two-dimensional array. + * For example: + * wire [7:0] sig_t4 [0:1][0:2]; + * + * Assume vpi_hdl is for "sig_t4": + * vpi_handl_by_index(vpi_hdl, 0); // Returns a handle to sig_t4[0] for IUS, but NULL on Questa + * + * Questa only works when both indicies are provided, i.e. will need a pseudo-handle to behave like the first index. + */ + if (new_hdl == NULL) { + int left = parent_hdl->get_range_left(); + int right = parent_hdl->get_range_right(); + bool ascending = (left < right); + + LOG_DEBUG("Unable to find handle through vpi_handle_by_index(), attempting second method"); + + if (( ascending && (index < left || index > right)) || + (!ascending && (index > left || index < right))) { + LOG_ERROR("Invalid Index - Index %d is not in the range of [%d:%d]", index, left, right); + return NULL; + } + + /* Get the number of constraints to determine if the index will result in a pseudo-handle or should be found */ + vpiHandle p_hdl = parent_hdl->get_handle(); + vpiHandle it = vpi_iterate(vpiRange, p_hdl); + int constraint_cnt = 0; + if (it != NULL) { + while (vpi_scan(it) != NULL) { + ++constraint_cnt; + } + } else { + constraint_cnt = 1; + } + + std::string act_hdl_name = vpi_get_str(vpiName, p_hdl); + + /* Removing the act_hdl_name from the parent->get_name() will leave the psuedo-indices */ + if (act_hdl_name.length() < parent_hdl->get_name().length()) { + std::string idx_str = parent_hdl->get_name().substr(act_hdl_name.length()); + + while (idx_str.length() > 0) { + std::size_t found = idx_str.find_first_of("]"); + + if (found != std::string::npos) { + --constraint_cnt; + idx_str = idx_str.substr(found+1); + } else { + break; + } + } + } + + snprintf(buff, 14, "[%d]", index); + + std::string idx = buff; + std::string hdl_name = parent_hdl->get_fullname() + idx; + + std::vector writable(hdl_name.begin(), hdl_name.end()); + writable.push_back('\0'); + + new_hdl = vpi_handle_by_name(&writable[0], NULL); + + /* Create a pseudo-handle if not the last index into a multi-dimensional array */ + if (new_hdl == NULL && constraint_cnt > 1) { + new_hdl = p_hdl; + } + } + } else { + LOG_ERROR("VPI: Parent of type %s must be of type GPI_GENARRAY, GPI_REGISTER, GPI_ARRAY, or GPI_STRING to have an index.", parent_hdl->get_type_str()); + return NULL; } + if (new_hdl == NULL) { - LOG_DEBUG("Unable to vpi_get_handle_by_index %s[%u]", vpi_get_str(vpiName, vpi_hdl), index); + LOG_DEBUG("Unable to vpi_get_handle_by_index %s[%d]", parent_hdl->get_name_str(), index); return NULL; } - std::string name = vpi_get_str(vpiName, new_hdl); - std::string fq_name = parent->get_fullname() + "." + name; + snprintf(buff, 14, "[%d]", index); + + std::string idx = buff; + std::string name = parent_hdl->get_name()+idx; + std::string fq_name = parent_hdl->get_fullname()+idx; GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { vpi_free_object(new_hdl); - LOG_DEBUG("Unable to fetch object below entity (%s) at index (%u)", - parent->get_name_str(), index); + LOG_DEBUG("Unable to fetch object below entity (%s) at index (%d)", + parent_hdl->get_name_str(), index); return NULL; } return new_obj; diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index cedab93c..55c1de64 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -173,6 +173,15 @@ class VpiShutdownCbHdl : public VpiCbHdl { virtual ~VpiShutdownCbHdl() { } }; +class VpiArrayObjHdl : public GpiObjHdl { +public: + VpiArrayObjHdl(GpiImplInterface *impl, vpiHandle hdl, gpi_objtype_t objtype) : + GpiObjHdl(impl, hdl, objtype) { } + virtual ~VpiArrayObjHdl() { } + + int initialise(std::string &name, std::string &fq_name); +}; + class VpiSignalObjHdl : public GpiSignalObjHdl { public: VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl, gpi_objtype_t objtype, bool is_const) : @@ -266,7 +275,7 @@ class VpiImpl : public GpiImplInterface { GpiCbHdl *register_readwrite_callback(void); int deregister_callback(GpiCbHdl *obj_hdl); GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); - GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); + GpiObjHdl* native_check_create(int32_t index, GpiObjHdl *parent); GpiObjHdl* native_check_create(void *raw_hdl, GpiObjHdl *parent); const char * reason_to_string(int reason); GpiObjHdl* create_gpi_obj_from_handle(vpiHandle new_hdl, From c878f54f56202fa70c95633b6e9ecb5e49f6c7f3 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 22 Mar 2016 06:30:26 -0700 Subject: [PATCH 1159/2656] Issue #393: Update the VHPI Interface for the new array interface --- lib/vhpi/VhpiCbHdl.cpp | 255 +++++++++++++--- lib/vhpi/VhpiImpl.cpp | 666 +++++++++++++++++++++++++++-------------- lib/vhpi/VhpiImpl.h | 36 ++- 3 files changed, 670 insertions(+), 287 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 05ce9a16..986a9988 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -44,6 +44,148 @@ VhpiSignalObjHdl::~VhpiSignalObjHdl() free(m_binvalue.value.str); } +bool get_range(vhpiHandleT hdl, vhpiIntT dim, int *left, int *right) { +#ifdef IUS + /* IUS does not appear to set the vhpiIsUnconstrainedP property. IUS Docs say will return + * -1 if unconstrained, but with vhpiIntT being unsigned, the value returned is below. + */ + const vhpiIntT UNCONSTRAINED = 2147483647; +#endif + + bool error = true; + + vhpiHandleT base_hdl = vhpi_handle(vhpiBaseType, hdl); + + if (base_hdl == NULL) { + vhpiHandleT st_hdl = vhpi_handle(vhpiSubtype, hdl); + + if (st_hdl != NULL) { + base_hdl = vhpi_handle(vhpiBaseType, st_hdl); + vhpi_release_handle(st_hdl); + } + } + + if (base_hdl != NULL) { + vhpiHandleT it = vhpi_iterator(vhpiConstraints, base_hdl); + vhpiIntT curr_idx = 0; + + if (it != NULL) { + vhpiHandleT constraint; + while ((constraint = vhpi_scan(it)) != NULL) { + if (curr_idx == dim) { + + vhpi_release_handle(it); + vhpiIntT l_rng = vhpi_get(vhpiLeftBoundP, constraint); + vhpiIntT r_rng = vhpi_get(vhpiRightBoundP, constraint); +#ifdef IUS + if (l_rng != UNCONSTRAINED && r_rng != UNCONSTRAINED) { +#else + if (vhpi_get(vhpiIsUnconstrainedP, constraint)) { +#endif + error = false; + *left = l_rng; + *right = r_rng; + } + break; + } + ++curr_idx; + } + } + vhpi_release_handle(base_hdl); + } + + if (error) { + vhpiHandleT sub_type_hdl = vhpi_handle(vhpiSubtype, hdl); + + if (sub_type_hdl != NULL) { + vhpiHandleT it = vhpi_iterator(vhpiConstraints, sub_type_hdl); + vhpiIntT curr_idx = 0; + + if (it != NULL) { + vhpiHandleT constraint; + while ((constraint = vhpi_scan(it)) != NULL) { + if (curr_idx == dim) { + vhpi_release_handle(it); + + /* IUS only sets the vhpiIsUnconstrainedP incorrectly on the base type */ + if (!vhpi_get(vhpiIsUnconstrainedP, constraint)) { + error = false; + *left = vhpi_get(vhpiLeftBoundP, constraint); + *right = vhpi_get(vhpiRightBoundP, constraint); + } + break; + } + ++curr_idx; + } + } + vhpi_release_handle(sub_type_hdl); + } + } + + return error; + +} + +int VhpiArrayObjHdl::initialise(std::string &name, std::string &fq_name) { + vhpiHandleT handle = GpiObjHdl::get_handle(); + + m_indexable = true; + + vhpiHandleT type = vhpi_handle(vhpiBaseType, handle); + + if (type == NULL) { + vhpiHandleT st_hdl = vhpi_handle(vhpiSubtype, handle); + + if (st_hdl != NULL) { + type = vhpi_handle(vhpiBaseType, st_hdl); + vhpi_release_handle(st_hdl); + } + } + + if (NULL == type) { + LOG_ERROR("Unable to get vhpiBaseType for %s", fq_name.c_str()); + return -1; + } + + vhpiIntT num_dim = vhpi_get(vhpiNumDimensionsP, type); + vhpiIntT dim_idx = 0; + + /* Need to determine which dimension constraint is needed */ + if (num_dim > 1) { + std::string hdl_name = vhpi_get_str(vhpiCaseNameP, handle); + + if (hdl_name.length() < name.length()) { + std::string pseudo_idx = name.substr(hdl_name.length()); + + while (pseudo_idx.length() > 0) { + std::size_t found = pseudo_idx.find_first_of(")"); + + if (found != std::string::npos) { + ++dim_idx; + pseudo_idx = pseudo_idx.substr(found+1); + } else { + break; + } + } + } + } + + bool error = get_range(handle, dim_idx, &m_range_left, &m_range_right); + + if (error) { + LOG_ERROR("Unable to obtain constraints for an indexable object %s.", fq_name.c_str()); + return -1; + } + + if (m_range_left > m_range_right) { + m_num_elems = m_range_left - m_range_right + 1; + } else { + m_num_elems = m_range_right - m_range_left + 1; + } + + return GpiObjHdl::initialise(name, fq_name); +} + int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { // Determine the type of object, either scalar or vector m_value.format = vhpiObjTypeVal; @@ -59,12 +201,8 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { vhpiHandleT handle = GpiObjHdl::get_handle(); if (0 > vhpi_get_value(get_handle(), &m_value)) { - if (vhpiSliceNameK == vhpi_get(vhpiKindP, handle)) { - m_value.format = vhpiEnumVecVal; - } else { - LOG_DEBUG("vhpi_get_value failed and not a vhpiSliceNameK setting to vhpiRawDataVal"); - m_value.format = vhpiRawDataVal; - } + LOG_ERROR("vhpi_get_value failed for %s (%s)", fq_name.c_str(), vhpi_get_str(vhpiKindStrP, handle)); + return -1; } LOG_DEBUG("Found %s of format type %s (%d) format object with %d elems buffsize %d size %d", @@ -81,26 +219,13 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { switch (m_value.format) { case vhpiIntVal: case vhpiEnumVal: - case vhpiLogicVal: case vhpiRealVal: case vhpiCharVal: { break; } - case vhpiIntVecVal: - case vhpiEnumVecVal: - case vhpiLogicVecVal: { - m_num_elems = vhpi_get(vhpiSizeP, handle); - m_value.bufSize = m_num_elems*sizeof(vhpiEnumT); - m_value.value.enumvs = (vhpiEnumT *)malloc(m_value.bufSize + 1); - if (!m_value.value.enumvs) { - LOG_CRITICAL("Unable to alloc mem for write buffer: ABORTING"); - } - LOG_DEBUG("Overriding num_elems to %d", m_num_elems); - break; - } - case vhpiStrVal: { + m_indexable = true; m_num_elems = vhpi_get(vhpiSizeP, handle); m_value.bufSize = (m_num_elems)*sizeof(vhpiCharT) + 1; m_value.value.str = (vhpiCharT *)malloc(m_value.bufSize); @@ -111,29 +236,18 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { LOG_DEBUG("Overriding num_elems to %d", m_num_elems); break; } - case vhpiRawDataVal: { - // This is an internal representation - the only way to determine - // the size is to iterate over the members and count sub-elements - m_num_elems = 0; - vhpiHandleT result = NULL; - vhpiHandleT iterator = vhpi_iterator(vhpiIndexedNames, - handle); - while (true) { - result = vhpi_scan(iterator); - if (NULL == result) - break; - m_num_elems++; - } - LOG_DEBUG("Found vhpiRawDataVal with %d elements", m_num_elems); - goto gpi_init; - } default: { LOG_ERROR("Unable to determine property for %s (%d) format object", ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format), m_value.format); + return -1; } } + if (m_indexable && get_range(handle, 0, &m_range_left, &m_range_right)) { + m_indexable = false; + } + if (m_num_elems) { m_binvalue.bufSize = m_num_elems*sizeof(vhpiCharT) + 1; m_binvalue.value.str = (vhpiCharT *)calloc(m_binvalue.bufSize, sizeof(vhpiCharT)); @@ -143,10 +257,52 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { } } -gpi_init: return GpiObjHdl::initialise(name, fq_name); } +int VhpiLogicSignalObjHdl::initialise(std::string &name, std::string &fq_name) { + // Determine the type of object, either scalar or vector + m_value.format = vhpiLogicVal; + m_value.bufSize = 0; + m_value.value.str = NULL; + m_value.numElems = 0; + /* We also alloc a second value member for use with read string operations */ + m_binvalue.format = vhpiBinStrVal; + m_binvalue.bufSize = 0; + m_binvalue.numElems = 0; + m_binvalue.value.str = NULL; + + vhpiHandleT handle = GpiObjHdl::get_handle(); + + m_num_elems = vhpi_get(vhpiSizeP, handle); + + if (m_num_elems > 1) { + m_indexable = true; + m_value.format = vhpiLogicVecVal; + m_value.bufSize = m_num_elems*sizeof(vhpiEnumT); + m_value.value.enumvs = (vhpiEnumT *)malloc(m_value.bufSize + 1); + if (!m_value.value.enumvs) { + LOG_CRITICAL("Unable to alloc mem for write buffer: ABORTING"); + } + } + + if (m_indexable && get_range(handle, 0, &m_range_left, &m_range_right)) { + m_indexable = false; + } + + if (m_num_elems) { + m_binvalue.bufSize = m_num_elems*sizeof(vhpiCharT) + 1; + m_binvalue.value.str = (vhpiCharT *)calloc(m_binvalue.bufSize, sizeof(vhpiCharT)); + + if (!m_binvalue.value.str) { + LOG_CRITICAL("Unable to alloc mem for read buffer of signal %s", name.c_str()); + } + } + + return GpiObjHdl::initialise(name, fq_name); +} + + VhpiCbHdl::VhpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl) { cb_data.reason = 0; @@ -572,7 +728,7 @@ int VhpiStartupCbHdl::run_callback(void) { gpi_sim_info_t sim_info; sim_info.argc = 0; sim_info.argv = NULL; - sim_info.product = gpi_copy_name(vhpi_get_str(vhpiCaseNameP, NULL)); + sim_info.product = gpi_copy_name(vhpi_get_str(vhpiNameP, NULL)); sim_info.version = gpi_copy_name(vhpi_get_str(vhpiToolVersionP, NULL)); gpi_embed_init(&sim_info); @@ -646,6 +802,7 @@ void vhpi_mappings(GpiIteratorMapping &map) vhpiVarDecls, vhpiPortDecls, vhpiGenericDecls, + vhpiConstDecls, // vhpiIndexedNames, vhpiCompInstStmts, vhpiBlockStmts, @@ -686,20 +843,17 @@ void vhpi_mappings(GpiIteratorMapping &map) /* vhpiForGenerateK */ vhpiOneToManyT gen_options[] = { - vhpiDecls, - vhpiCompInstStmts, - (vhpiOneToManyT)0, - }; - map.add_to_options(vhpiForGenerateK, &gen_options[0]); - - /* vhpiIfGenerateK */ - vhpiOneToManyT ifgen_options[] = { vhpiDecls, vhpiInternalRegions, + vhpiSigDecls, + vhpiVarDecls, + vhpiConstDecls, vhpiCompInstStmts, + vhpiBlockStmts, (vhpiOneToManyT)0, }; - map.add_to_options(vhpiIfGenerateK, &ifgen_options[0]); + map.add_to_options(vhpiForGenerateK, &gen_options[0]); + map.add_to_options(vhpiIfGenerateK, &gen_options[0]); /* vhpiConstDeclK */ vhpiOneToManyT const_options[] = { @@ -815,7 +969,10 @@ GpiIterator::Status VhpiIterator::next_handle(std::string &name, } } - if (obj != NULL && (vhpiProcessStmtK == vhpi_get(vhpiKindP, obj))) { + if (obj != NULL && (vhpiProcessStmtK == vhpi_get(vhpiKindP, obj) || + vhpiCondSigAssignStmtK == vhpi_get(vhpiKindP, obj) || + vhpiSimpleSigAssignStmtK == vhpi_get(vhpiKindP, obj) || + vhpiSelectSigAssignStmtK == vhpi_get(vhpiKindP, obj))) { LOG_DEBUG("Skipping %s (%s)", vhpi_get_str(vhpiFullNameP, obj), vhpi_get_str(vhpiKindStrP, obj)); obj=NULL; @@ -916,7 +1073,7 @@ GpiIterator::Status VhpiIterator::next_handle(std::string &name, fq_name += "." + name; } VhpiImpl *vhpi_impl = reinterpret_cast(m_impl); - new_obj = vhpi_impl->create_gpi_obj_from_handle(obj, name, fq_name); + new_obj = vhpi_impl->create_gpi_obj_from_handle(obj, name, fq_name, m_parent->get_const()); if (new_obj) { *hdl = new_obj; return GpiIterator::NATIVE; diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index b4888d1c..19a66cd0 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -28,6 +28,7 @@ #include "VhpiImpl.h" #include #include +#include extern "C" { static VhpiCbHdl *sim_init_cb; @@ -35,75 +36,53 @@ static VhpiCbHdl *sim_finish_cb; static VhpiImpl *vhpi_table; } +#define CASE_STR(_X) \ + case _X: return #_X + const char * VhpiImpl::format_to_string(int format) { switch (format) { - case vhpiBinStrVal: - return "vhpiBinStrVal"; - case vhpiOctStrVal: - return "vhpiOctStrVal"; - case vhpiDecStrVal: - return "vhpiDecStrVal"; - case vhpiHexStrVal: - return "vhpiHexStrVal"; - case vhpiEnumVal: - return "vhpiEnumVal"; - case vhpiIntVal: - return "vhpiIntVal"; - case vhpiLogicVal: - return "vhpiLogicVal"; - case vhpiRealVal: - return "vhpiRealVal"; - case vhpiStrVal: - return "vhpiStrVal"; - case vhpiCharVal: - return "vhpiCharVal"; - case vhpiTimeVal: - return "vhpiTimeVal"; - case vhpiPhysVal: - return "vhpiPhysVal"; - case vhpiObjTypeVal: - return "vhpiObjTypeVal"; - case vhpiPtrVal: - return "vhpiPtrVal"; - case vhpiEnumVecVal: - return "vhpiEnumVecVal"; - case vhpiRawDataVal: - return "vhpiRawDataVal"; - - default: - return "unknown"; + CASE_STR(vhpiBinStrVal); + CASE_STR(vhpiOctStrVal); + CASE_STR(vhpiDecStrVal); + CASE_STR(vhpiHexStrVal); + CASE_STR(vhpiEnumVal); + CASE_STR(vhpiIntVal); + CASE_STR(vhpiLogicVal); + CASE_STR(vhpiRealVal); + CASE_STR(vhpiStrVal); + CASE_STR(vhpiCharVal); + CASE_STR(vhpiTimeVal); + CASE_STR(vhpiPhysVal); + CASE_STR(vhpiObjTypeVal); + CASE_STR(vhpiPtrVal); + CASE_STR(vhpiEnumVecVal); + CASE_STR(vhpiRawDataVal); + + default: return "unknown"; } } const char *VhpiImpl::reason_to_string(int reason) { switch (reason) { - case vhpiCbValueChange: - return "vhpiCbValueChange"; - case vhpiCbStartOfNextCycle: - return "vhpiCbStartOfNextCycle"; - case vhpiCbStartOfPostponed: - return "vhpiCbStartOfPostponed"; - case vhpiCbEndOfTimeStep: - return "vhpiCbEndOfTimeStep"; - case vhpiCbNextTimeStep: - return "vhpiCbNextTimeStep"; - case vhpiCbAfterDelay: - return "vhpiCbAfterDelay"; - case vhpiCbStartOfSimulation: - return "vhpiCbStartOfSimulation"; - case vhpiCbEndOfSimulation: - return "vhpiCbEndOfSimulation"; - case vhpiCbEndOfProcesses: - return "vhpiCbEndOfProcesses"; - case vhpiCbLastKnownDeltaCycle: - return "vhpiCbLastKnownDeltaCycle"; - default: - return "unknown"; + CASE_STR(vhpiCbValueChange); + CASE_STR(vhpiCbStartOfNextCycle); + CASE_STR(vhpiCbStartOfPostponed); + CASE_STR(vhpiCbEndOfTimeStep); + CASE_STR(vhpiCbNextTimeStep); + CASE_STR(vhpiCbAfterDelay); + CASE_STR(vhpiCbStartOfSimulation); + CASE_STR(vhpiCbEndOfSimulation); + CASE_STR(vhpiCbEndOfProcesses); + CASE_STR(vhpiCbLastKnownDeltaCycle); + + default: return "unknown"; } } +#undef CASE_STR + void VhpiImpl::get_sim_time(uint32_t *high, uint32_t *low) { vhpiTimeT vhpi_time_s; @@ -134,164 +113,208 @@ bool is_const(vhpiIntT vhpitype) } } -gpi_objtype_t to_gpi_objtype(vhpiIntT vhpitype) -{ - switch (vhpitype) { - case vhpiPortDeclK: - case vhpiSigDeclK: - case vhpiIndexedNameK: - case vhpiSelectedNameK: - case vhpiVarDeclK: - case vhpiVarParamDeclK: - case vhpiSliceNameK: - return GPI_REGISTER; - - case vhpiArrayTypeDeclK: - return GPI_ARRAY; - - case vhpiEnumLiteralK: - case vhpiEnumTypeDeclK: - return GPI_ENUM; +bool is_enum_logic(vhpiHandleT hdl) { + const char *type = vhpi_get_str(vhpiNameP, hdl); - case vhpiConstDeclK: - case vhpiGenericDeclK: - return GPI_PARAMETER; + if (0 == strncmp(type, "BIT" , sizeof("BIT")-1) || + 0 == strncmp(type, "STD_ULOGIC", sizeof("STD_ULOGIC")-1) || + 0 == strncmp(type, "STD_LOGIC" , sizeof("STD_LOGIC")-1)) { + return true; + } else { + vhpiIntT num_enum = vhpi_get(vhpiNumLiteralsP, hdl); + + if (2 == num_enum) { + vhpiHandleT it = vhpi_iterator(vhpiEnumLiterals, hdl); + if (it != NULL) { + const char *enums_1[2] = { "0", "1"}; //Aldec does not return the single quotes + const char *enums_2[2] = {"'0'", "'1'"}; + vhpiHandleT enum_hdl; + int cnt = 0; + + while ((enum_hdl = vhpi_scan(it)) != NULL) { + const char *etype = vhpi_get_str(vhpiStrValP, enum_hdl); + if (1 < cnt || + (0 != strncmp(etype, enums_1[cnt], strlen(enums_1[cnt])) && + 0 != strncmp(etype, enums_2[cnt], strlen(enums_2[cnt])))) { + vhpi_release_handle(it); + return false; + } + ++cnt; + } + return true; + } + } else if (9 == num_enum) { + vhpiHandleT it = vhpi_iterator(vhpiEnumLiterals, hdl); + if (it != NULL) { + const char *enums_1[9] = { "U", "X", "0", "1", "Z", "W", "L", "H", "-"}; //Aldec does not return the single quotes + const char *enums_2[9] = {"'U'", "'X'", "'0'", "'1'", "'Z'", "'W'", "'L'", "'H'", "'-'"}; + vhpiHandleT enum_hdl; + int cnt = 0; + + while ((enum_hdl = vhpi_scan(it)) != NULL) { + const char *etype = vhpi_get_str(vhpiStrValP, enum_hdl); + if (8 < cnt || + (0 != strncmp(etype, enums_1[cnt], strlen(enums_1[cnt])) && + 0 != strncmp(etype, enums_2[cnt], strlen(enums_2[cnt])))) { + vhpi_release_handle(it); + return false; + } + ++cnt; + } + return true; + } + } + } - case vhpiRecordTypeDeclK: - return GPI_STRUCTURE; + return false; +} - case vhpiForGenerateK: - case vhpiIfGenerateK: - case vhpiCompInstStmtK: - case vhpiEntityDeclK: - case vhpiRootInstK: - case vhpiProcessStmtK: - case vhpiSimpleSigAssignStmtK: - case vhpiCondSigAssignStmtK: - case vhpiSelectSigAssignStmtK: - return GPI_MODULE; +bool is_enum_char(vhpiHandleT hdl) { + const vhpiIntT NUM_ENUMS_IN_CHAR_TYPE = 256; - default: - LOG_DEBUG("Unable to map VHPI type %d onto GPI type", vhpitype); - return GPI_UNKNOWN; + const char *type = vhpi_get_str(vhpiNameP, hdl); + + if (0 == strncmp(type, "CHARACTER", sizeof("STD_ULOGIC")-1)) { + return true; + } else { + return (vhpi_get(vhpiNumLiteralsP, hdl) == NUM_ENUMS_IN_CHAR_TYPE); } } +bool is_enum_boolean(vhpiHandleT hdl) { + const char *type = vhpi_get_str(vhpiNameP, hdl); + if (0 == strncmp(type, "BOOLEAN", sizeof("BOOLEAN")-1)) { + return true; + } else { + vhpiIntT num_enum = vhpi_get(vhpiNumLiteralsP, hdl); + + if (2 == num_enum) { + vhpiHandleT it = vhpi_iterator(vhpiEnumLiterals, hdl); + if (it != NULL) { + vhpiHandleT enum_hdl; + int cnt = 0; + + while ((enum_hdl = vhpi_scan(it)) != NULL) { + const char *etype = vhpi_get_str(vhpiStrValP, enum_hdl); + if (((0 == cnt && 0 != strncmp(etype, "FALSE", strlen("FALSE"))) && + (0 == cnt && 0 != strncmp(etype, "false", strlen("false")))) || + ((1 == cnt && 0 != strncmp(etype, "TRUE" , strlen("TRUE"))) && + (1 == cnt && 0 != strncmp(etype, "true" , strlen("true")))) || + 2 <= cnt) { + vhpi_release_handle(it); + return false; + } + ++cnt; + } + return true; + } + } + } + + return false; +} GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string &name, - std::string &fq_name) + std::string &fq_name, + bool parentConst) { vhpiIntT type; gpi_objtype_t gpi_type; GpiObjHdl *new_obj = NULL; - bool modifiable; - bool logic; if (vhpiVerilog == (type = vhpi_get(vhpiKindP, new_hdl))) { LOG_DEBUG("vhpiVerilog returned from vhpi_get(vhpiType, ...)") return NULL; } + bool is_constant = is_const(type) || parentConst; + /* We need to delve further here to detemine how to later set the values of an object */ - vhpiHandleT query_hdl; vhpiHandleT base_hdl = vhpi_handle(vhpiBaseType, new_hdl); - query_hdl = base_hdl ? base_hdl : new_hdl; + if (base_hdl == NULL) { + vhpiHandleT st_hdl = vhpi_handle(vhpiSubtype, new_hdl); + + if (st_hdl != NULL) { + base_hdl = vhpi_handle(vhpiBaseType, st_hdl); + vhpi_release_handle(st_hdl); + } + } + + vhpiHandleT query_hdl = (base_hdl != NULL) ? base_hdl : new_hdl; vhpiIntT base_type = vhpi_get(vhpiKindP, query_hdl); vhpiIntT is_static = vhpi_get(vhpiStaticnessP, query_hdl); - gpi_type = to_gpi_objtype(base_type); - LOG_DEBUG("Creating %s of type %d (%s)", - vhpi_get_str(vhpiFullCaseNameP, new_hdl), - gpi_type, - vhpi_get_str(vhpiKindStrP, query_hdl)); - /* Non locally static objects are not accessible for read/write so we create this as a GpiObjType */ if (is_static == vhpiGloballyStatic) { - modifiable = false; - logic = false; + gpi_type = GPI_MODULE; goto create; - } else { - modifiable = true; - logic = false; } switch (base_type) { - case vhpiSliceNameK: - case vhpiIndexedNameK: - case vhpiSelectedNameK: { - vhpiHandleT sub_type = vhpi_handle(vhpiSubtype, new_hdl); - if (base_hdl) - vhpi_release_handle(base_hdl); - - base_hdl = vhpi_handle(vhpiBaseType, sub_type); - query_hdl = base_hdl; - /* Drop though */ - } - case vhpiArrayTypeDeclK: - case vhpiEnumTypeDeclK: { - const char *type = vhpi_get_str(vhpiNameP, query_hdl); - if (0 == strcmp(type, "STD_ULOGIC") || - 0 == strcmp(type, "STD_LOGIC") || - 0 == strncmp(type, "STD_ULOGIC_VECTOR", sizeof("STD_ULOGIC_VECTOR")-1) || - 0 == strncmp(type, "STD_LOGIC_VECTOR", sizeof("STD_LOGIC_VECTOR")-1)) { - LOG_DEBUG("Detected std_logic %s", fq_name.c_str()); - logic = true; - } else if (0 == strcmp(type, "BOOLEAN") || - 0 == strcmp(type, "boolean") || - 0 == strcmp(type, "UNSIGNED")) { - LOG_DEBUG("Detected boolean/integer %s", fq_name.c_str()); - gpi_type = GPI_INTEGER; - } else if (0 == strncmp(type, "STRING", sizeof("STRING")-1)) { - LOG_DEBUG("Detected a STRING type %s", fq_name.c_str()); - gpi_type = GPI_STRING; - } else if (0 == strcmp(type, "CHARACTER") || - 0 == strcmp(type, "character")) { - LOG_DEBUG("Detected an CHAR type %s", fq_name.c_str()); - gpi_type = GPI_INTEGER; + case vhpiArrayTypeDeclK: { + vhpiIntT num_dim = vhpi_get(vhpiNumDimensionsP, query_hdl); + + if (num_dim > 1) { + LOG_DEBUG("Detected a MULTI-DIMENSIONAL ARRAY type %s", fq_name.c_str()); + gpi_type = GPI_ARRAY; } else { - /* It not a standard type then we lastly try and use the format, - we do this on the handle we where given on a sub type */ - - vhpiValueT value; - value.format = vhpiObjTypeVal; - value.bufSize = 0; - value.numElems = 0; - value.value.str = NULL; - int num_elems = vhpi_get(vhpiSizeP, new_hdl); - vhpi_get_value(new_hdl, &value); - - if (vhpiStrVal == value.format) { - LOG_DEBUG("Detected a STRING type %s", fq_name.c_str()); - gpi_type = GPI_STRING; - break; - } else if (vhpiRawDataVal == value.format || - vhpiObjTypeVal == value.format) { - LOG_DEBUG("Detected a RAW type %s", fq_name.c_str()); - gpi_type = GPI_MODULE; - break; - } else if (vhpiCharVal == value.format) { - LOG_DEBUG("Detected an CHAR type %s", fq_name.c_str()); - gpi_type = GPI_INTEGER; - break; + vhpiHandleT elem_base_type_hdl = NULL; + vhpiIntT elem_base_type = 0; + + /* vhpiElemSubtype is deprecated. Should be using vhpiElemType, but not supported in all simulators. */ + vhpiHandleT elem_sub_type_hdl = vhpi_handle(vhpiElemSubtype, query_hdl); + + if (elem_sub_type_hdl != NULL) { + elem_base_type_hdl = vhpi_handle(vhpiBaseType, elem_sub_type_hdl); + vhpi_release_handle(elem_sub_type_hdl); } - if (!value.numElems || (value.numElems == num_elems)) { - LOG_DEBUG("Detected single dimension vector type", fq_name.c_str()); - gpi_type = GPI_ARRAY; + if (elem_base_type_hdl != NULL) { + elem_base_type = vhpi_get(vhpiKindP, elem_base_type_hdl); + if (elem_base_type == vhpiEnumTypeDeclK) { + if (is_enum_logic(elem_base_type_hdl)) { + LOG_DEBUG("Detected a LOGIC VECTOR type %s", fq_name.c_str()); + gpi_type = GPI_REGISTER; + } else if (is_enum_char(elem_base_type_hdl)) { + LOG_DEBUG("Detected a STRING type %s", fq_name.c_str()); + gpi_type = GPI_STRING; + } else { + LOG_DEBUG("Detected a NON-LOGIC ENUM VECTOR type %s", fq_name.c_str()); + gpi_type = GPI_ARRAY; + } + } else { + LOG_DEBUG("Detected a NON-ENUM VECTOR type %s", fq_name.c_str()); + gpi_type = GPI_ARRAY; + } } else { - LOG_DEBUG("Detected an n dimension valueector type", fq_name.c_str()); - gpi_type = GPI_MODULE; - modifiable = false; + LOG_ERROR("Unable to determine the Array Element Base Type for %s. Defaulting to GPI_ARRAY.", vhpi_get_str(vhpiFullCaseNameP, new_hdl)); + gpi_type = GPI_ARRAY; } } + break; + } + case vhpiEnumTypeDeclK: { + if (is_enum_logic(query_hdl)) { + LOG_DEBUG("Detected a LOGIC type %s", fq_name.c_str()); + gpi_type = GPI_REGISTER; + } else if (is_enum_char(query_hdl)) { + LOG_DEBUG("Detected a CHAR type %s", fq_name.c_str()); + gpi_type = GPI_INTEGER; + } else if (is_enum_boolean(query_hdl)) { + LOG_DEBUG("Detected a BOOLEAN/INTEGER type %s", fq_name.c_str()); + gpi_type = GPI_INTEGER; + } else { + LOG_DEBUG("Detected an ENUM type %s", fq_name.c_str()); + gpi_type = GPI_ENUM; + } break; } @@ -307,12 +330,17 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, break; } + case vhpiRecordTypeDeclK: { + LOG_DEBUG("Detected a STRUCTURE type %s", fq_name.c_str()); + gpi_type = GPI_STRUCTURE; + break; + } + case vhpiProcessStmtK: case vhpiSimpleSigAssignStmtK: case vhpiCondSigAssignStmtK: - case vhpiRecordTypeDeclK: case vhpiSelectSigAssignStmtK: { - modifiable = false; + gpi_type = GPI_MODULE; break; } @@ -334,11 +362,11 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, } } - modifiable = false; - if (name != hdl_name) { LOG_DEBUG("Found pseudo-region %s", fq_name.c_str()); gpi_type = GPI_GENARRAY; + } else { + gpi_type = GPI_MODULE; } break; } @@ -352,14 +380,21 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, } create: - if (modifiable) { - if (logic) - new_obj = new VhpiLogicSignalObjHdl(this, new_hdl, gpi_type, is_const(type)); + LOG_DEBUG("Creating %s of type %d (%s)", + vhpi_get_str(vhpiFullCaseNameP, new_hdl), + gpi_type, + vhpi_get_str(vhpiKindStrP, query_hdl)); + + if (gpi_type != GPI_ARRAY && gpi_type != GPI_GENARRAY && gpi_type != GPI_MODULE && gpi_type != GPI_STRUCTURE) { + if (gpi_type == GPI_REGISTER) + new_obj = new VhpiLogicSignalObjHdl(this, new_hdl, gpi_type, is_constant); else - new_obj = new VhpiSignalObjHdl(this, new_hdl, gpi_type, is_const(type)); + new_obj = new VhpiSignalObjHdl(this, new_hdl, gpi_type, is_constant); + } else if (gpi_type == GPI_ARRAY) { + new_obj = new VhpiArrayObjHdl(this, new_hdl, gpi_type, is_constant); + } else { + new_obj = new GpiObjHdl(this, new_hdl, gpi_type, is_constant); } - else - new_obj = new GpiObjHdl(this, new_hdl, gpi_type); if (new_obj->initialise(name, fq_name)) { delete new_obj; @@ -367,7 +402,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, } out: - if (base_hdl) + if (base_hdl != NULL) vhpi_release_handle(base_hdl); return new_obj; @@ -394,7 +429,7 @@ GpiObjHdl *VhpiImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) fq_name += "." + name; } - GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); + GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name, parent->get_const()); if (new_obj == NULL) { vhpi_release_handle(new_hdl); LOG_DEBUG("Unable to fetch object %s", fq_name.c_str()); @@ -418,7 +453,25 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) new_hdl = vhpi_handle_by_name(&writable[0], NULL); - if (new_hdl == NULL) { + if (new_hdl == NULL && parent->get_type() == GPI_STRUCTURE) { + /* vhpi_handle_by_name() doesn't always work for records, specificaly records in generics */ + vhpiHandleT iter = vhpi_iterator(vhpiSelectedNames, parent->get_handle()); + if (iter != NULL) { + while ((new_hdl = vhpi_scan(iter)) != NULL) { + std::string selected_name = vhpi_get_str(vhpiCaseNameP, new_hdl); + std::size_t found = selected_name.find_last_of("."); + + if (found != std::string::npos) { + selected_name = selected_name.substr(found+1); + } + + if (selected_name == name) { + vhpi_release_handle(iter); + break; + } + } + } + } else if (new_hdl == NULL) { /* If not found, check to see if the name of a generate loop */ vhpiHandleT iter = vhpi_iterator(vhpiInternalRegions, parent->get_handle()); @@ -454,7 +507,7 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) new_hdl = parent->get_handle(); } - GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); + GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name, parent->get_const()); if (new_obj == NULL) { vhpi_release_handle(new_hdl); LOG_DEBUG("Unable to fetch object %s", fq_name.c_str()); @@ -464,74 +517,233 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) return new_obj; } -GpiObjHdl *VhpiImpl::native_check_create(uint32_t index, GpiObjHdl *parent) +GpiObjHdl *VhpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) { GpiObjHdl *parent_hdl = sim_to_hdl(parent); - vhpiHandleT vhpi_hdl = parent_hdl->get_handle(); - vhpiHandleT new_hdl; - - if (parent_hdl->get_type() == GPI_GENARRAY) { - char buff[11]; // needs to be large enough to hold 2^32-1 in string form (10 + '\0') + vhpiHandleT vhpi_hdl = parent_hdl->get_handle(); + std::string name = parent->get_name(); + std::string fq_name = parent->get_fullname(); + vhpiHandleT new_hdl = NULL; + char buff[14]; // needs to be large enough to hold -2^31 to 2^31-1 in string form ('(''-'10+'')'\0') - snprintf(buff, 11, "%u", index); + gpi_objtype_t obj_type = parent_hdl->get_type(); - LOG_DEBUG("Native check create for index %u of parent %s (pseudo-region)", + if (obj_type == GPI_GENARRAY) { + LOG_DEBUG("Native check create for index %d of parent %s (pseudo-region)", index, parent_hdl->get_name_str()); - std::string idx = buff; - std::string hdl_name = parent_hdl->get_fullname() + GEN_IDX_SEP_LHS + idx + GEN_IDX_SEP_RHS; - std::vector writable(hdl_name.begin(), hdl_name.end()); + snprintf(buff, sizeof(buff), "%d", index); + + std::string idx_str = buff; + name += (GEN_IDX_SEP_LHS + idx_str + GEN_IDX_SEP_RHS); + fq_name += (GEN_IDX_SEP_LHS + idx_str + GEN_IDX_SEP_RHS); + + std::vector writable(fq_name.begin(), fq_name.end()); writable.push_back('\0'); new_hdl = vhpi_handle_by_name(&writable[0], NULL); - } else { - LOG_DEBUG("Native check create for index %u of parent %s (%s)", + } else if (obj_type == GPI_REGISTER || obj_type == GPI_ARRAY || obj_type == GPI_STRING) { + LOG_DEBUG("Native check create for index %d of parent %s (%s)", index, - vhpi_get_str(vhpiCaseNameP, vhpi_hdl), + parent_hdl->get_fullname_str(), vhpi_get_str(vhpiKindStrP, vhpi_hdl)); - new_hdl = vhpi_handle_by_index(vhpiIndexedNames, vhpi_hdl, index); - if (!new_hdl) { - /* Support for the above seems poor, so if it did not work - try an iteration instead */ - - vhpiHandleT iter = vhpi_iterator(vhpiIndexedNames, vhpi_hdl); - if (iter) { - uint32_t curr_index = 0; - while (true) { - new_hdl = vhpi_scan(iter); - if (!new_hdl) { + snprintf(buff, sizeof(buff), "(%d)", index); + + std::string idx_str = buff; + name += idx_str; + fq_name += idx_str; + + vhpiHandleT base_hdl = vhpi_handle(vhpiBaseType, vhpi_hdl); + + if (base_hdl == NULL) { + vhpiHandleT st_hdl = vhpi_handle(vhpiSubtype, vhpi_hdl); + + if (st_hdl != NULL) { + base_hdl = vhpi_handle(vhpiBaseType, st_hdl); + vhpi_release_handle(st_hdl); + } + } + + if (base_hdl == NULL) { + LOG_ERROR("Unable to get the vhpiBaseType of %s", parent_hdl->get_fullname_str()); + return NULL; + } + + vhpiIntT num_dim = vhpi_get(vhpiNumDimensionsP,base_hdl); + uint32_t idx = 0; + + /* Need to translate the index into a zero-based flattened array index */ + if (num_dim > 1) { + std::string hdl_name = vhpi_get_str(vhpiCaseNameP, vhpi_hdl); + std::vector indices; + + /* Need to determine how many indices have been received. A valid handle will only + * be found when all indices are received, otherwise need a pseudo-handle. + * + * When working with pseudo-handles: + * hdl_name: sig_name + * parent->get_name(): sig_name(x)(y)... where x,y,... are the indices to a multi-dimensional array. + * pseudo_idx: (x)(y)... + */ + if (hdl_name.length() < parent_hdl->get_name().length()) { + std::string pseudo_idx = parent_hdl->get_name().substr(hdl_name.length()); + + while (pseudo_idx.length() > 0) { + std::size_t found = pseudo_idx.find_first_of(")"); + + if (found != std::string::npos) { + indices.push_back(atoi(pseudo_idx.substr(1,found-1).c_str())); + pseudo_idx = pseudo_idx.substr(found+1); + } else { break; } - if (index == curr_index) { - LOG_DEBUG("Index match %u == %u", curr_index, index); - break; + } + } + + indices.push_back(index); + + if (indices.size() == num_dim) { +#ifdef IUS + /* IUS does not appear to set the vhpiIsUnconstrainedP property. IUS Docs say will return + * -1 if unconstrained, but with vhpiIntT being unsigned, the value returned is below. + */ + const vhpiIntT UNCONSTRAINED = 2147483647; +#endif + + std::vector constraints; + + /* All necessary indices are available, need to iterate over dimension constraints to + * determine the index into the zero-based flattened array. + * + * Check the constraints on the base type first. (always works for Aldec, but not unconstrained types in IUS) + * If the base type fails, then try the sub-type. (sub-type is listed as deprecated for Aldec) + */ + vhpiHandleT it, constraint; + + it = vhpi_iterator(vhpiConstraints, base_hdl); + + if (it != NULL) { + while ((constraint = vhpi_scan(it)) != NULL) { +#ifdef IUS + vhpiIntT l_rng = vhpi_get(vhpiLeftBoundP, constraint); + vhpiIntT r_rng = vhpi_get(vhpiRightBoundP, constraint); + if (l_rng == UNCONSTRAINED || r_rng == UNCONSTRAINED) { +#else + if (vhpi_get(vhpiIsUnconstrainedP, constraint)) { +#endif + /* Bail and try the sub-type handle */ + vhpi_release_handle(it); + break; + } + constraints.push_back(constraint); } - curr_index++; } - vhpi_release_handle(iter); + + /* If all the dimensions were not obtained, try again with the sub-type handle */ + if (constraints.size() != num_dim) { + vhpiHandleT sub_hdl = vhpi_handle(vhpiSubtype, vhpi_hdl);; + + constraints.clear(); + + if (sub_hdl != NULL) { + it = vhpi_iterator(vhpiConstraints, sub_hdl); + + if (it != NULL) { + while ((constraint = vhpi_scan(it)) != NULL) { + /* IUS only sets the vhpiIsUnconstrainedP incorrectly on the base type */ + if (vhpi_get(vhpiIsUnconstrainedP, constraint)) { + vhpi_release_handle(it); + break; + } + constraints.push_back(constraint); + } + } + } + } + + if (constraints.size() == num_dim) { + int scale = 1; + + while (constraints.size() > 0) { + int raw_idx = indices.back(); + constraint = constraints.back(); + + vhpiIntT left = vhpi_get(vhpiLeftBoundP, constraint); + vhpiIntT right = vhpi_get(vhpiRightBoundP, constraint); + vhpiIntT len = 0; + + if (left > right) { + idx += (scale * (left - raw_idx)); + len = left - right + 1; + } else { + idx += (scale * (raw_idx - left)); + len = right - left + 1; + } + scale = scale * len; + + indices.pop_back(); + constraints.pop_back(); + } + } else { + LOG_ERROR("Unable to access all constraints for %s", parent_hdl->get_fullname_str()); + return NULL; + } + + } else { + new_hdl = vhpi_hdl; // Set to the parent handle to create the pseudo-handle + } + } else { + int left = parent_hdl->get_range_left(); + int right = parent_hdl->get_range_right(); + + if (left > right) { + idx = left - index; + } else { + idx = index - left; + } + } + + if (new_hdl == NULL) { + new_hdl = vhpi_handle_by_index(vhpiIndexedNames, vhpi_hdl, idx); + if (!new_hdl) { + /* Support for the above seems poor, so if it did not work + try an iteration instead, spotty support for multi-dimensional arrays */ + + vhpiHandleT iter = vhpi_iterator(vhpiIndexedNames, vhpi_hdl); + if (iter != NULL) { + uint32_t curr_index = 0; + while ((new_hdl = vhpi_scan(iter)) != NULL) { + if (idx == curr_index) { + vhpi_release_handle(iter); + break; + } + curr_index++; + } + } + } + + if (new_hdl != NULL) { + LOG_DEBUG("Index (%d->%d) found %s (%s)", index, idx, vhpi_get_str(vhpiCaseNameP, new_hdl), vhpi_get_str(vhpiKindStrP, new_hdl)); } } + } else { + LOG_ERROR("VHPI: Parent of type %s must be of type GPI_GENARRAY, GPI_REGISTER, GPI_ARRAY, or GPI_STRING to have an index.", parent_hdl->get_type_str()); + return NULL; } + if (new_hdl == NULL) { - LOG_DEBUG("Unable to query vhpi_handle_by_index %u", index); + LOG_DEBUG("Unable to query vhpi_handle_by_index %d", index); return NULL; } - std::string name = vhpi_get_str(vhpiCaseNameP, new_hdl); - std::string fq_name = parent->get_fullname(); - if (fq_name == ":") { - fq_name += name; - } else { - fq_name += "." + name; - } - GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); + GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name, parent_hdl->get_const()); if (new_obj == NULL) { vhpi_release_handle(new_hdl); - LOG_DEBUG("Could not fetch object below entity (%s) at index (%u)", - parent->get_name_str(), index); + LOG_DEBUG("Could not fetch object below entity (%s) at index (%d)", + parent_hdl->get_name_str(), index); return NULL; } @@ -604,7 +816,7 @@ GpiObjHdl *VhpiImpl::get_root_handle(const char* name) root_name = found; - return create_gpi_obj_from_handle(dut, root_name, root_name); + return create_gpi_obj_from_handle(dut, root_name, root_name, false); } diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index e088e3a3..94069d05 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -158,6 +158,17 @@ class VhpiReadwriteCbHdl : public VhpiCbHdl { virtual ~VhpiReadwriteCbHdl() { } }; +class VhpiArrayObjHdl : public GpiObjHdl { +public: + VhpiArrayObjHdl(GpiImplInterface *impl, + vhpiHandleT hdl, + gpi_objtype_t objtype, + bool is_const) : GpiObjHdl(impl, hdl, objtype, is_const) { } + virtual ~VhpiArrayObjHdl() { } + + int initialise(std::string &name, std::string &fq_name); +}; + class VhpiSignalObjHdl : public GpiSignalObjHdl { public: VhpiSignalObjHdl(GpiImplInterface *impl, @@ -169,19 +180,19 @@ class VhpiSignalObjHdl : public GpiSignalObjHdl { m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } virtual ~VhpiSignalObjHdl(); - const char* get_signal_value_binstr(void); - const char* get_signal_value_str(void); - double get_signal_value_real(void); - long get_signal_value_long(void); + virtual const char* get_signal_value_binstr(void); + virtual const char* get_signal_value_str(void); + virtual double get_signal_value_real(void); + virtual long get_signal_value_long(void); - int set_signal_value(const long value); - int set_signal_value(const double value); - int set_signal_value(std::string &value); + virtual int set_signal_value(const long value); + virtual int set_signal_value(const double value); + virtual int set_signal_value(std::string &value); /* Value change callback accessor */ - GpiCbHdl *value_change_cb(unsigned int edge); - int initialise(std::string &name, std::string &fq_name); + virtual GpiCbHdl *value_change_cb(unsigned int edge); + virtual int initialise(std::string &name, std::string &fq_name); protected: const vhpiEnumT chr2vhpi(const char value); @@ -203,6 +214,8 @@ class VhpiLogicSignalObjHdl : public VhpiSignalObjHdl { int set_signal_value(const long value); int set_signal_value(std::string &value); + + int initialise(std::string &name, std::string &fq_name); }; class VhpiIterator : public GpiIterator { @@ -244,7 +257,7 @@ class VhpiImpl : public GpiImplInterface { GpiCbHdl *register_readwrite_callback(void); int deregister_callback(GpiCbHdl *obj_hdl); GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent); - GpiObjHdl* native_check_create(uint32_t index, GpiObjHdl *parent); + GpiObjHdl* native_check_create(int32_t index, GpiObjHdl *parent); GpiObjHdl* native_check_create(void *raw_hdl, GpiObjHdl *parent); const char * reason_to_string(int reason); @@ -252,7 +265,8 @@ class VhpiImpl : public GpiImplInterface { GpiObjHdl *create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string &name, - std::string &fq_name); + std::string &fq_name, + bool parentConst); private: VhpiReadwriteCbHdl m_read_write; From 6d63e6f106350120b0f312526cf92abcd4f1e2f9 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 21 Mar 2016 13:35:27 -0700 Subject: [PATCH 1160/2656] Issue #393: Update expected results in tests --- .../test_discovery/test_discovery.py | 18 +++++++----------- .../test_iteration_mixedlang/test_iteration.py | 13 ++++--------- .../test_iteration_vhdl/test_iteration.py | 15 ++++----------- 3 files changed, 15 insertions(+), 31 deletions(-) diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 1f6b0b0d..ea9667f0 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -134,8 +134,8 @@ def access_single_bit_erroneous(dut): dut.stream_in_data[bit] <= 1 yield Timer(10) -@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"], - expect_fail=cocotb.SIM_NAME in ["Riviera-PRO"]) +@cocotb.test(expect_error=cocotb.SIM_NAME.lower().startswith(("Icarus Verilog")), + expect_fail=cocotb.SIM_NAME.lower().startswith(("riviera")) and cocotb.LANGUAGE in ["verilog"]) def access_integer(dut): """Integer should show as an IntegerObject""" bitfail = False @@ -228,7 +228,9 @@ def access_string(dut): idx = 3 result_slice = varible_string[idx] - if chr(result_slice) != test_string[idx]: + + # String is defined as string(1 to 8) so idx=3 will access the 3rd character + if chr(result_slice) != test_string[idx-1]: raise TestFailure("Single character did not match '%c' != '%c'" % (result_slice, test_string[idx])) @@ -341,14 +343,8 @@ def custom_type(dut): new_type = dut.cosLut tlog.info("cosLut object %s %s" % (new_type, type(new_type))) - # FLI only iterates over one dimension at a time, where vhpi will - # iterate over two dimensions at the same time - if cocotb.SIM_NAME.lower().startswith(("modelsim")): - expected_sub = 84 - expected_top = 4 - else: - expected_sub = 11 - expected_top = 28 + expected_sub = 84 + expected_top = 4 count = 0 diff --git a/tests/test_cases/test_iteration_mixedlang/test_iteration.py b/tests/test_cases/test_iteration_mixedlang/test_iteration.py index 3b109529..bc7ccef3 100644 --- a/tests/test_cases/test_iteration_mixedlang/test_iteration.py +++ b/tests/test_cases/test_iteration_mixedlang/test_iteration.py @@ -67,13 +67,11 @@ def recursive_discovery(dut): """ if cocotb.SIM_NAME.lower().startswith(("ncsim")): # vpiAlways = 31 and vpiStructVar = 2 do not show up in IUS - # But vhpiSimpleSigAssignStmtK objects do, and ther are 2. - # Process statements and all sub handles also show up. - pass_total = 985 + pass_total = 917 elif cocotb.SIM_NAME.lower().startswith(("modelsim")): pass_total = 933 else: - pass_total = 916 + pass_total = 966 tlog = logging.getLogger("cocotb.test") yield Timer(100) @@ -96,12 +94,9 @@ def recursive_discovery_boundary(dut): Iteration though the boundary works but this just double checks """ if cocotb.SIM_NAME.lower().startswith(("ncsim")): - # # But vhpiSimpleSigAssignStmtK objects only show up on IUS, and ther are 2 - pass_total = 530 - elif cocotb.SIM_NAME.lower().startswith(("modelsim")): - pass_total = 478 + pass_total = 462 else: - pass_total = 426 + pass_total = 478 tlog = logging.getLogger("cocotb.test") yield Timer(100) diff --git a/tests/test_cases/test_iteration_vhdl/test_iteration.py b/tests/test_cases/test_iteration_vhdl/test_iteration.py index eced6319..63af0200 100644 --- a/tests/test_cases/test_iteration_vhdl/test_iteration.py +++ b/tests/test_cases/test_iteration_vhdl/test_iteration.py @@ -34,18 +34,11 @@ def recursive_discovery(dut): """ Recursively discover every single object in the design """ - if cocotb.SIM_NAME.lower().startswith("ncsim"): - # vpiAlways = 31 and vpiStructVar = 2 do not show up in IUS - # But vhpiSimpleSigAssignStmtK objects do, and ther are 2. - # Process statements and all sub handles also show up. - pass_total = 35841 - elif cocotb.SIM_NAME.lower().startswith("modelsim"): - # FLI only finds regions, signal, generics, constants, varibles and ports. - # It does not report any procedures - # Ports behave identically to signals, which seems to differ from the vhpi implementation - pass_total = 34562 + if cocotb.SIM_NAME.lower().startswith(("ncsim","modelsim")): + # Finds regions, signal, generics, constants, varibles and ports. + pass_total = 34569 else: - pass_total = 32306 + pass_total = 32393 tlog = logging.getLogger("cocotb.test") yield Timer(100) From 2117e9912d8435525669b3da48df4e73c152deee Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 22 Mar 2016 08:09:32 -0700 Subject: [PATCH 1161/2656] Issue #393: Clean-up handle.py interface for getting range constraints --- cocotb/handle.py | 14 ++++----- lib/simulator/simulatormodule.c | 53 +++++---------------------------- lib/simulator/simulatormodule.h | 8 ++--- 3 files changed, 16 insertions(+), 59 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 7884a30a..59f7df37 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -393,12 +393,10 @@ def __init__(self, handle): _handle [integer] : vpi/vhpi handle to the simulator object """ NonHierarchyObject.__init__(self, handle) - self._range_left = simulator.get_range_left(self._handle) - self._range_right = simulator.get_range_right(self._handle) - self._indexable = simulator.get_indexable(self._handle) + self._range = simulator.get_range(self._handle) def __getitem__(self, index): - if not self._indexable: + if self._range is None: self._raise_testerror("%s %s is not indexable. Unable to get object at index %d" % (self._name, simulator.get_type_string(self._handle), index)) if index in self._sub_handles: return self._sub_handles[index] @@ -409,17 +407,17 @@ def __getitem__(self, index): return self._sub_handles[index] def __iter__(self): - if not self._indexable: + if self._range is None: raise StopIteration - self._log.debug("Iterating with range [%d:%d]" % (self._range_left, self._range_right)) - for i in self._range(self._range_left, self._range_right): + self._log.debug("Iterating with range [%d:%d]" % (self._range[0], self._range[1])) + for i in self._range_iter(self._range[0], self._range[1]): try: result = self[i] yield result except: continue - def _range(self, left, right): + def _range_iter(self, left, right): if left > right: while left >= right: yield left diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 2366423a..1e0e9430 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -825,28 +825,6 @@ static PyObject *get_const(PyObject *self, PyObject *args) return pyresult; } -static PyObject *get_indexable(PyObject *self, PyObject *args) -{ - int result; - gpi_sim_hdl hdl; - PyObject *pyresult; - - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - - if (!PyArg_ParseTuple(args, "l", &hdl)) { - DROP_GIL(gstate); - return NULL; - } - - result = gpi_is_indexable((gpi_sim_hdl)hdl); - pyresult = Py_BuildValue("i", result); - - DROP_GIL(gstate); - - return pyresult; -} - static PyObject *get_type_string(PyObject *self, PyObject *args) { const char *result; @@ -928,28 +906,7 @@ static PyObject *get_num_elems(PyObject *self, PyObject *args) return retstr; } -static PyObject *get_range_left(PyObject *self, PyObject *args) -{ - gpi_sim_hdl hdl; - PyObject *retstr; - - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - - if (!PyArg_ParseTuple(args, "l", &hdl)) { - DROP_GIL(gstate); - return NULL; - } - - int rng_left = gpi_get_range_left((gpi_sim_hdl)hdl); - retstr = Py_BuildValue("i", rng_left); - - DROP_GIL(gstate); - - return retstr; -} - -static PyObject *get_range_right(PyObject *self, PyObject *args) +static PyObject *get_range(PyObject *self, PyObject *args) { gpi_sim_hdl hdl; PyObject *retstr; @@ -962,8 +919,14 @@ static PyObject *get_range_right(PyObject *self, PyObject *args) return NULL; } + int indexable = gpi_is_indexable((gpi_sim_hdl)hdl); + int rng_left = gpi_get_range_left((gpi_sim_hdl)hdl); int rng_right = gpi_get_range_right((gpi_sim_hdl)hdl); - retstr = Py_BuildValue("i", rng_right); + + if (indexable) + retstr = Py_BuildValue("(i,i)", rng_left,rng_right); + else + retstr = Py_BuildValue(""); DROP_GIL(gstate); diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 1aec2741..fe1acbee 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -70,11 +70,9 @@ static PyObject *get_root_handle(PyObject *self, PyObject *args); static PyObject *get_name_string(PyObject *self, PyObject *args); static PyObject *get_type(PyObject *self, PyObject *args); static PyObject *get_const(PyObject *self, PyObject *args); -static PyObject *get_indexable(PyObject *self, PyObject *args); static PyObject *get_type_string(PyObject *self, PyObject *args); static PyObject *get_num_elems(PyObject *self, PyObject *args); -static PyObject *get_range_left(PyObject *self, PyObject *args); -static PyObject *get_range_right(PyObject *self, PyObject *args); +static PyObject *get_range(PyObject *self, PyObject *args); static PyObject *register_timed_callback(PyObject *self, PyObject *args); static PyObject *register_value_change_callback(PyObject *self, PyObject *args); static PyObject *register_readonly_callback(PyObject *self, PyObject *args); @@ -107,10 +105,8 @@ static PyMethodDef SimulatorMethods[] = { {"get_type_string", get_type_string, METH_VARARGS, "Get the type of an object as a string"}, {"get_type", get_type, METH_VARARGS, "Get the type of an object, mapped to a GPI enumeration"}, {"get_const", get_const, METH_VARARGS, "Get a flag indicating whether the object is a constant"}, - {"get_indexable", get_indexable, METH_VARARGS, "Get a flag indicating whether the object is indexable"}, {"get_num_elems", get_num_elems, METH_VARARGS, "Get the number of elements contained in the handle"}, - {"get_range_left", get_range_left, METH_VARARGS, "Get the left-side range of elements contained in the handle"}, - {"get_range_right", get_range_right, METH_VARARGS, "Get the right-side range of elements contained in the handle"}, + {"get_range", get_range, METH_VARARGS, "Get the range of elements (tuple) contained in the handle, Returns None if not indexable"}, {"register_timed_callback", register_timed_callback, METH_VARARGS, "Register a timed callback"}, {"register_value_change_callback", register_value_change_callback, METH_VARARGS, "Register a signal change callback"}, {"register_readonly_callback", register_readonly_callback, METH_VARARGS, "Register a callback for readonly section"}, From 55e5d6a3e32c5dd3c977611e10f59800b2f50680 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Wed, 23 Mar 2016 13:15:24 -0700 Subject: [PATCH 1162/2656] Issue #393: Clean-up handle.py --- cocotb/handle.py | 340 ++++++++---------- .../test_discovery/test_discovery.py | 2 +- .../test_vhdl_access/test_vhdl_access.py | 4 +- 3 files changed, 162 insertions(+), 184 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 59f7df37..e6532186 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -82,7 +82,6 @@ def __init__(self, handle): self._len = None self._sub_handles = {} # Dictionary of children self._invalid_sub_handles = {} # Dictionary of invalid queries - self._discovered = False self._name = simulator.get_name_string(self._handle) self._type = simulator.get_type_string(self._handle) @@ -115,67 +114,34 @@ def __ne__(self, other): def __repr__(self): return self._fullname - def _raise_testerror(self, msg): - lastframe = sys._getframe(2) - if sys.version_info[0] >= 3: - buff = StringIO() - traceback.print_stack(lastframe, file=buff) - else: - buff_bytes = BytesIO() - traceback.print_stack(lastframe, file=buff_bytes) - buff = StringIO(buff_bytes.getvalue().decode("UTF8")) - self._log.error("%s\n%s" % (msg, buff.getvalue())) - exception = TestError(msg) - exception.stderr.write(buff.getvalue()) - buff.close() - raise exception - - -class HierarchyObject(SimHandleBase): - """ - Hierarchy objects don't have values, they are effectively scopes or namespaces - """ + def __str__(self): + return "%s @0x%x" % (self._name, self._handle) def __setattr__(self, name, value): - """ - Provide transparent access to signals via the hierarchy - - Slightly hacky version of operator overloading in Python - - Raise an AttributeError if users attempt to create new members which - don't exist in the design. - """ - if name.startswith("_") or name in self._compat_mapping: + if name in self._compat_mapping: + if name not in _deprecation_warned: + warnings.warn("Use of %s attribute is deprecated" % name) + _deprecation_warned[name] = True + return setattr(self, self._compat_mapping[name]) + else: return object.__setattr__(self, name, value) - if self.__hasattr__(name) is not None: - return getattr(self, name)._setcachedvalue(value) - raise AttributeError("Attempt to access %s which isn't present in %s" %( - name, self._name)) def __getattr__(self, name): - """ - Query the simulator for a object with the specified name - and cache the result to build a tree of objects - """ - if name in self._sub_handles: - return self._sub_handles[name] - if name in self._compat_mapping: if name not in _deprecation_warned: warnings.warn("Use of %s attribute is deprecated" % name) _deprecation_warned[name] = True return getattr(self, self._compat_mapping[name]) + else: + return object.__getattr__(self, name) - new_handle = simulator.get_handle_by_name(self._handle, name) - - if not new_handle: - # To find generated indices we have to discover all - self._discover_all() - if name in self._sub_handles: - return self._sub_handles[name] - raise AttributeError("%s contains no object named %s" % (self._name, name)) - self._sub_handles[name] = SimHandle(new_handle) - return self._sub_handles[name] +class RegionObject(SimHandleBase): + """ + Region objects don't have values, they are effectively scopes or namespaces + """ + def __init__(self, handle): + SimHandleBase.__init__(self, handle) + self._discovered = False def __iter__(self): """ @@ -220,15 +186,71 @@ def _discover_all(self): self._log.debug("%s" % e) continue - self._sub_handles[hdl._name.split(".")[-1]] = hdl + key = self._sub_handle_key(hdl) + + if not key is None: + self._sub_handles[key] = hdl + else: + self._log.debug("Unable to translate handle >%s< to a valid _sub_handle key" % hdl._name) + continue self._discovered = True + def _sub_handle_key(self, hdl): + """ + Translates the handle name to a key to use in _sub_handles dictionary. + """ + return hdl._name.split(".")[-1] + def _getAttributeNames(self): """Permits IPython tab completion to work""" self._discover_all() return dir(self) + +class HierarchyObject(RegionObject): + """ + Hierarchy objects are namespace/scope objects + """ + + def __setattr__(self, name, value): + """ + Provide transparent access to signals via the hierarchy + + Slightly hacky version of operator overloading in Python + + Raise an AttributeError if users attempt to create new members which + don't exist in the design. + """ + if name.startswith("_") or name in self._compat_mapping: + return SimHandleBase.__setattr__(self, name, value) + if self.__hasattr__(name) is not None: + return getattr(self, name)._setcachedvalue(value) + raise AttributeError("Attempt to access %s which isn't present in %s" %( + name, self._name)) + + def __getattr__(self, name): + """ + Query the simulator for a object with the specified name + and cache the result to build a tree of objects + """ + if name in self._sub_handles: + return self._sub_handles[name] + + if name.startswith("_") or name in self._compat_mapping: + return SimHandleBase.__getattr__(self, name) + + new_handle = simulator.get_handle_by_name(self._handle, name) + + if not new_handle: + # To find generated indices we have to discover all + self._discover_all() + if name in self._sub_handles: + return self._sub_handles[name] + raise AttributeError("%s contains no object named %s" % (self._name, name)) + self._sub_handles[name] = SimHandle(new_handle) + return self._sub_handles[name] + def __hasattr__(self, name): """ Since calling hasattr(handle, "something") will print out a @@ -252,55 +274,31 @@ def __hasattr__(self, name): return new_handle -class HierarchyArrayObject(HierarchyObject): +class HierarchyArrayObject(RegionObject): """ - Hierarchy Array objects don't have values, they are effectively scopes or namespaces + Hierarchy Array are containers of Hierarchy Objects """ - def _discover_all(self): + def _sub_handle_key(self, hdl): """ - When iterating or performing tab completion, we run through ahead of - time and discover all possible children, populating the _sub_handle - mapping. Hierarchy can't change after elaboration so we only have to - do this once. + Translates the handle name to a key to use in _sub_handles dictionary. """ - if self._discovered: return - self._log.debug("Discovering all on %s", self._name) - iterator = simulator.iterate(self._handle, simulator.OBJECTS) - while True: - try: - thing = simulator.next(iterator) - except StopIteration: - # Iterator is cleaned up internally in GPI - break - name = simulator.get_name_string(thing) - try: - hdl = SimHandle(thing) - except TestError as e: - self._log.debug("%s" % e) - continue - - # This is slightly hacky, but we need to extract the index from the name - # - # FLI and VHPI(IUS): _name(X) where X is the index - # VHPI(ALDEC): _name__X where X is the index - # VPI: _name[X] where X is the index - import re - result = re.match("{}__(?P\d+)$".format(self._name), name) - if not result: - result = re.match("{}\((?P\d+)\)$".format(self._name), name) - if not result: - result = re.match("{}\[(?P\d+)\]$".format(self._name), name) - - if result: - index = int(result.group("index")) - - self._sub_handles[index] = hdl - self._log.debug("Added %s[%d] to the cache", self._name, index) - else: - self._log.error("Dropping invalid Hierarchy Array handle >%s<", name) + # This is slightly hacky, but we need to extract the index from the name + # + # FLI and VHPI(IUS): _name(X) where X is the index + # VHPI(ALDEC): _name__X where X is the index + # VPI: _name[X] where X is the index + import re + result = re.match("{}__(?P\d+)$".format(self._name), hdl._name) + if not result: + result = re.match("{}\((?P\d+)\)$".format(self._name), hdl._name) + if not result: + result = re.match("{}\[(?P\d+)\]$".format(self._name), hdl._name) - self._discovered = True + if result: + return int(result.group("index")) + else: + return None def __len__(self): """Returns the 'length' of the generate block.""" @@ -312,14 +310,20 @@ def __len__(self): return self._len def __getitem__(self, index): + if isinstance(index, slice): + raise IndexError("Slice indexing is not supported") if index in self._sub_handles: return self._sub_handles[index] new_handle = simulator.get_handle_by_index(self._handle, index) if not new_handle: - self._raise_testerror("%s contains no object at index %d" % (self._name, index)) + raise IndexError("%s contains no object at index %d" % (self._name, index)) self._sub_handles[index] = SimHandle(new_handle) return self._sub_handles[index] + def __setitem__(self, index, value): + """Provide transparent assignment to indexed array handles""" + raise TypeError("Not permissible to set %s at index %d" % (self._name, index)) + class NonHierarchyObject(SimHandleBase): @@ -329,14 +333,47 @@ class NonHierarchyObject(SimHandleBase): def __init__(self, handle): SimHandleBase.__init__(self, handle) - for name, attribute in SimHandleBase._compat_mapping.items(): - setattr(self, name, getattr(self, attribute)) - - def __str__(self): - return "%s @0x%x" % (self._name, self._handle) def __iter__(self): - return iter(()) + raise StopIteration + + def _getvalue(self): + raise TypeError("Not permissible to get values on object %s" % (self._name)) + + def setimmediatevalue(self, value): + raise TypeError("Not permissible to set values on object %s" % (self._name)) + + def _setcachedvalue(self, value): + raise TypeError("Not permissible to set values on object %s" % (self._name)) + + def __le__(self, value): + """Overload the less than or equal to operator to + provide an hdl-like shortcut + module.signal <= 2 + """ + self.value = value + + def __eq__(self, other): + if isinstance(other, SimHandleBase): + if self._handle == other._handle: return 0 + return 1 + + # Use the comparison method of the other object against our value + return self.value == other + + def __ne__(self, other): + return not self.__eq__(other) + + + # We want to maintain compatability with python 2.5 so we can't use @property with a setter + value = property(fget=lambda self: self._getvalue(), + fset=lambda self,v: self._setcachedvalue(v), + fdel=None, + doc="A reference to the value") + + # Re-define hash becasue Python 3 has issues when using the above property + def __hash__(self): + return SimHandleBase.__hash__(self) class ConstantObject(NonHierarchyObject): """ @@ -362,47 +399,40 @@ def __init__(self, handle, handle_type): self._value = val def __int__(self): - return int(self._value) - - def __eq__(self, other): - return self._value.__eq__(other) + return int(self.value) - def __ne__(self, other): - if isinstance(self._value, str): - return self._value.__ne__(other) - else: - return self._value != other + def __float__(self): + return float(self.value) def __repr__(self): - return str(self._value) + return str(self.value) - @property - def value(self): + def _getvalue(self): return self._value - def _setcachedvalue(self, *args, **kwargs): - raise ValueError("Not permissible to set values on a constant object") - - def __le__(self, *args, **kwargs): - raise ValueError("Not permissible to set values on a constant object") - class NonHierarchyIndexableObject(NonHierarchyObject): def __init__(self, handle): """ Args: - _handle [integer] : vpi/vhpi handle to the simulator object + _handle [integer] : fli/vpi/vhpi handle to the simulator object """ NonHierarchyObject.__init__(self, handle) self._range = simulator.get_range(self._handle) + def __setitem__(self, index, value): + """Provide transparent assignment to indexed array handles""" + self.__getitem__(index).value = value + def __getitem__(self, index): + if isinstance(index, slice): + raise IndexError("Slice indexing is not supported") if self._range is None: - self._raise_testerror("%s %s is not indexable. Unable to get object at index %d" % (self._name, simulator.get_type_string(self._handle), index)) + raise IndexError("%s is not indexable. Unable to get object at index %d" % (self._fullname, index)) if index in self._sub_handles: return self._sub_handles[index] new_handle = simulator.get_handle_by_index(self._handle, index) if not new_handle: - self._raise_testerror("%s %s contains no object at index %d" % (self._name, simulator.get_type_string(self._handle), index)) + raise IndexError("%s contains no object at index %d" % (self._fullname, index)) self._sub_handles[index] = SimHandle(new_handle) return self._sub_handles[index] @@ -439,36 +469,6 @@ def __init__(self, handle): self._f_edge = _FallingEdge(self) self._e_edge = _Edge(self) - def _getvalue(self): - result = BinaryValue() - result.binstr = self._get_value_str() - return result - - ## We want to maintain compatability with python 2.5 so we can't use @property with a setter - #value = property(_getvalue, None, None, "A reference to the value") - - def _get_value_str(self): - return simulator.get_signal_val_binstr(self._handle) - - def __eq__(self, other): - if isinstance(other, SimHandleBase): - if self._handle == other._handle: return 0 - return 1 - - # Use the comparison method of the other object against our value - return self.value.__eq__(other) - - def __ne__(self, other): - return not self.__eq__(other) - - def __int__(self): - val = self.value - return int(self.value) - - def __repr__(self): - return repr(int(self)) - - def drivers(self): """ An iterator for gathering all drivers for a signal @@ -490,11 +490,6 @@ class ModifiableObject(NonConstantObject): """ Base class for simulator objects whose values can be modified """ - - def __setitem__(self, index, value): - """Provide transparent assignment to bit index""" - self.__getitem__(index)._setcachedvalue(value) - def setimmediatevalue(self, value): """ Set the value of the underlying simulation object to value. @@ -529,7 +524,7 @@ def setimmediatevalue(self, value): def _getvalue(self): result = BinaryValue() - result.binstr = self._get_value_str() + result.binstr = simulator.get_signal_val_binstr(self._handle) return result def _setcachedvalue(self, value): @@ -542,19 +537,11 @@ def _setcachedvalue(self, value): """ cocotb.scheduler.save_write(self, value) + def __int__(self): + return int(self.value) - # We want to maintain compatability with python 2.5 so we can't use @property with a setter - value = property(_getvalue, _setcachedvalue, None, "A reference to the value") - - - def __le__(self, value): - """Overload the less than or equal to operator to - provide an hdl-like shortcut - module.signal <= 2 - """ - self.value = value - - + def __repr__(self): + return repr(int(self)) class RealObject(ModifiableObject): """ @@ -584,9 +571,6 @@ def setimmediatevalue(self, value): def _getvalue(self): return simulator.get_signal_val_real(self._handle) - # We want to maintain compatability with python 2.5 so we can't use @property with a setter - value = property(_getvalue, ModifiableObject._setcachedvalue, None, "A reference to the value") - def __float__(self): return self._getvalue() @@ -620,9 +604,6 @@ def setimmediatevalue(self, value): def _getvalue(self): return simulator.get_signal_val_long(self._handle) - # We want to maintain compatability with python 2.5 so we can't use @property with a setter - value = property(_getvalue, ModifiableObject._setcachedvalue, None, "A reference to the value") - def __int__(self): return self._getvalue() @@ -654,11 +635,8 @@ def setimmediatevalue(self, value): def _getvalue(self): return simulator.get_signal_val_str(self._handle) - # We want to maintain compatability with python 2.5 so we can't use @property with a setter - value = property(_getvalue, ModifiableObject._setcachedvalue, None, "A reference to the value") - def __repr__(self): - return repr(self._getvalue()) + return str(self.value) _handle2obj = {} diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index ea9667f0..cea890d5 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -147,7 +147,7 @@ def access_integer(dut): try: bit = test_int[3] - except TestError as e: + except IndexError as e: tlog.info("Access to bit is an error as expected") bitFail = True diff --git a/tests/test_cases/test_vhdl_access/test_vhdl_access.py b/tests/test_cases/test_vhdl_access/test_vhdl_access.py index f703b766..73d1c404 100644 --- a/tests/test_cases/test_vhdl_access/test_vhdl_access.py +++ b/tests/test_cases/test_vhdl_access/test_vhdl_access.py @@ -79,14 +79,14 @@ def check_instance(obj, objtype): dut.inst_axi4s_buffer.DATA_WIDTH = 42 tlog.error("Shouldn't be allowed to set a value on constant object") fails += 1 - except ValueError as e: + except TypeError as e: pass try: dut.inst_axi4s_buffer.DATA_WIDTH <= 42 tlog.error("Shouldn't be allowed to set a value on constant object using __le__") fails += 1 - except ValueError as e: + except TypeError as e: pass if fails: From 7f0432d79bd3341a655fb38589d2d283dbf4bd51 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 22 Mar 2016 11:31:44 -0700 Subject: [PATCH 1163/2656] Issue #393: Clean-up FLI interface --- lib/fli/FliImpl.cpp | 171 ++++++++++++++++++++---------------------- lib/fli/FliObjHdl.cpp | 17 ++--- 2 files changed, 90 insertions(+), 98 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index beb4d6f9..e7b312dd 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -193,7 +193,7 @@ GpiObjHdl *FliImpl::create_gpi_obj_from_handle(void *hdl, std::string &name, std } break; case MTI_TYPE_RECORD: - new_obj = new FliObjHdl(this, hdl, GPI_STRUCTURE, accType, accFullType); + new_obj = new FliValueObjHdl(this, hdl, GPI_STRUCTURE, false, accType, accFullType, is_var, valType, typeKind); break; default: LOG_ERROR("Unable to handle object type for %s (%d)", name.c_str(), typeKind); @@ -243,59 +243,78 @@ GpiObjHdl* FliImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) */ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) { - std::string fq_name = parent->get_fullname(); + GpiObjHdl *parent_hdl = sim_to_hdl(parent); + bool search_rgn = false; + bool search_sig = false; + bool search_var = false; + + std::string fq_name = parent_hdl->get_fullname(); + gpi_objtype_t obj_type = parent_hdl->get_type(); if (fq_name == "/") { fq_name += name; - } else if (parent->get_type() == GPI_MODULE) { + search_rgn = true; + search_sig = true; + search_var = true; + } else if (obj_type == GPI_MODULE) { fq_name += "/" + name; - } else if (parent->get_type() == GPI_STRUCTURE) { + search_rgn = true; + search_sig = true; + search_var = true; + } else if (obj_type == GPI_STRUCTURE) { + FliValueObjHdl *fli_obj = reinterpret_cast(parent_hdl); + fq_name += "." + name; + search_rgn = false; + search_var = fli_obj->is_var(); + search_sig = !search_var; } else { - LOG_ERROR("FLI: Parent of type %d must be of type GPI_MODULE or GPI_STRUCTURE to have a child.", parent->get_type()); + LOG_ERROR("FLI: Parent of type %d must be of type GPI_MODULE or GPI_STRUCTURE to have a child.", obj_type); return NULL; } - LOG_DEBUG("Looking for child %s from %s", name.c_str(), parent->get_name_str()); + LOG_DEBUG("Looking for child %s from %s", name.c_str(), parent_hdl->get_name_str()); std::vector writable(fq_name.begin(), fq_name.end()); writable.push_back('\0'); - HANDLE hdl; + HANDLE hdl = NULL; PLI_INT32 accType; PLI_INT32 accFullType; - if ((hdl = mti_FindRegion(&writable[0])) != NULL) { + if (search_rgn && (hdl = mti_FindRegion(&writable[0])) != NULL) { accType = acc_fetch_type(hdl); accFullType = acc_fetch_fulltype(hdl); LOG_DEBUG("Found region %s -> %p", fq_name.c_str(), hdl); LOG_DEBUG(" Type: %d", accType); LOG_DEBUG(" Full Type: %d", accFullType); - } else if ((hdl = mti_FindSignal(&writable[0])) != NULL) { + } else if (search_sig && (hdl = mti_FindSignal(&writable[0])) != NULL) { accType = acc_fetch_type(hdl); accFullType = acc_fetch_fulltype(hdl); LOG_DEBUG("Found a signal %s -> %p", fq_name.c_str(), hdl); LOG_DEBUG(" Type: %d", accType); LOG_DEBUG(" Full Type: %d", accFullType); - } else if ((hdl = mti_FindVar(&writable[0])) != NULL) { + } else if (search_var && (hdl = mti_FindVar(&writable[0])) != NULL) { accFullType = accType = mti_GetVarKind(static_cast(hdl)); LOG_DEBUG("Found a variable %s -> %p", fq_name.c_str(), hdl); LOG_DEBUG(" Type: %d", accType); LOG_DEBUG(" Full Type: %d", accFullType); - } else { + } else if (search_rgn){ mtiRegionIdT rgn; /* If not found, check to see if the name of a generate loop and create a pseudo-region */ - for (rgn = mti_FirstLowerRegion(parent->get_handle()); rgn != NULL; rgn = mti_NextRegion(rgn)) { + for (rgn = mti_FirstLowerRegion(parent_hdl->get_handle()); rgn != NULL; rgn = mti_NextRegion(rgn)) { if (acc_fetch_fulltype(rgn) == accForGenerate) { std::string rgn_name = mti_GetRegionName(static_cast(rgn)); if (rgn_name.compare(0,name.length(),name) == 0) { - FliObj *fli_obj = dynamic_cast(parent); - return create_gpi_obj_from_handle(parent->get_handle(), name, fq_name, fli_obj->get_acc_type(), fli_obj->get_acc_full_type()); + FliObj *fli_obj = dynamic_cast(parent_hdl); + return create_gpi_obj_from_handle(parent_hdl->get_handle(), name, fq_name, fli_obj->get_acc_type(), fli_obj->get_acc_full_type()); } } } + } + if (NULL == hdl) { LOG_DEBUG("Didn't find anything named %s", &writable[0]); return NULL; } @@ -308,8 +327,8 @@ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) * being equivalent to the parent handle. */ if (accFullType == accForGenerate) { - FliObj *fli_obj = dynamic_cast(parent); - return create_gpi_obj_from_handle(parent->get_handle(), name, fq_name, fli_obj->get_acc_type(), fli_obj->get_acc_full_type()); + FliObj *fli_obj = dynamic_cast(parent_hdl); + return create_gpi_obj_from_handle(parent_hdl->get_handle(), name, fq_name, fli_obj->get_acc_type(), fli_obj->get_acc_full_type()); } return create_gpi_obj_from_handle(hdl, name, fq_name, accType, accFullType); @@ -322,21 +341,22 @@ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) */ GpiObjHdl* FliImpl::native_check_create(int32_t index, GpiObjHdl *parent) { + GpiObjHdl *parent_hdl = sim_to_hdl(parent); + gpi_objtype_t obj_type = parent_hdl->get_type(); + HANDLE hdl; PLI_INT32 accType; PLI_INT32 accFullType; char buff[14]; - gpi_objtype_t obj_type = parent->get_type(); - if (obj_type == GPI_GENARRAY) { - LOG_DEBUG("Looking for index %d from %s", index, parent->get_name_str()); + LOG_DEBUG("Looking for index %d from %s", index, parent_hdl->get_name_str()); snprintf(buff, 14, "(%d)", index); std::string idx = buff; - std::string name = parent->get_name() + idx; - std::string fq_name = parent->get_fullname() + idx; + std::string name = parent_hdl->get_name() + idx; + std::string fq_name = parent_hdl->get_fullname() + idx; std::vector writable(fq_name.begin(), fq_name.end()); writable.push_back('\0'); @@ -354,24 +374,9 @@ GpiObjHdl* FliImpl::native_check_create(int32_t index, GpiObjHdl *parent) return create_gpi_obj_from_handle(hdl, name, fq_name, accType, accFullType); } else if (obj_type == GPI_REGISTER || obj_type == GPI_ARRAY || obj_type == GPI_STRING) { - int left = parent->get_range_left(); - int right = parent->get_range_right(); - bool ascending = (left < right); - - if (( ascending && (index < left || index > right)) || - (!ascending && (index > left || index < right))) { - LOG_ERROR("Invalid Index - Index %d is not in the range of [%d:%d]", index, left, right); - return NULL; - } - - FliValueObjHdl *fli_obj = reinterpret_cast(parent); + FliValueObjHdl *fli_obj = reinterpret_cast(parent_hdl); - if (obj_type == GPI_REGISTER && fli_obj->get_fli_typekind() == MTI_TYPE_ENUM) { - LOG_ERROR("Logic Type is not a vector that can be indexed"); - return NULL; - } - - LOG_DEBUG("Looking for index %u from %s", index, parent->get_name_str()); + LOG_DEBUG("Looking for index %u from %s", index, parent_hdl->get_name_str()); if ((hdl = fli_obj->get_sub_hdl(index)) == NULL) { LOG_DEBUG("Didn't find the index %d", index); @@ -381,8 +386,8 @@ GpiObjHdl* FliImpl::native_check_create(int32_t index, GpiObjHdl *parent) snprintf(buff, 14, "(%d)", index); std::string idx = buff; - std::string name = parent->get_name() + idx; - std::string fq_name = parent->get_fullname() + idx; + std::string name = parent_hdl->get_name() + idx; + std::string fq_name = parent_hdl->get_fullname() + idx; if (!(fli_obj->is_var())) { accType = acc_fetch_type(hdl); @@ -398,7 +403,7 @@ GpiObjHdl* FliImpl::native_check_create(int32_t index, GpiObjHdl *parent) } return create_gpi_obj_from_handle(hdl, name, fq_name, accType, accFullType); } else { - LOG_ERROR("FLI: Parent of type %d must be of type GPI_GENARRAY, GPI_REGISTER, GPI_ARRAY, or GPI_STRING to have an index.", parent->get_type()); + LOG_ERROR("FLI: Parent of type %d must be of type GPI_GENARRAY, GPI_REGISTER, GPI_ARRAY, or GPI_STRING to have an index.", obj_type); return NULL; } } @@ -883,72 +888,60 @@ void FliIterator::populate_handle_list(FliIterator::OneToMany childType) } break; case FliIterator::OTM_SIGNAL_SUB_ELEMENTS: - if (m_parent->get_type() == GPI_REGISTER || m_parent->get_type() == GPI_ARRAY || m_parent->get_type() == GPI_STRUCTURE) { - FliValueObjHdl *fli_obj = reinterpret_cast(m_parent); + if (m_parent->get_type() == GPI_STRUCTURE) { + mtiSignalIdT parent = m_parent->get_handle(); - if (m_parent->get_type() == GPI_REGISTER && fli_obj->get_fli_typekind() == MTI_TYPE_ENUM) { - break; - } + mtiTypeIdT type = mti_GetSignalType(parent); + mtiSignalIdT *ids = mti_GetSignalSubelements(parent,NULL); - if (m_parent->get_type() == GPI_STRUCTURE) { - mtiSignalIdT parent = m_parent->get_handle(); + LOG_DEBUG("GPI_STRUCTURE: %d fields", mti_TickLength(type)); + for (int i = 0; i < mti_TickLength(type); i++) { + m_sigs.push_back(ids[i]); + } + mti_VsimFree(ids); + } else if (m_parent->get_indexable()) { + FliValueObjHdl *fli_obj = reinterpret_cast(m_parent); - mtiTypeIdT type = mti_GetSignalType(parent); - mtiSignalIdT *ids = mti_GetSignalSubelements(parent,NULL); + int left = m_parent->get_range_left(); + int right = m_parent->get_range_right(); - LOG_DEBUG("GPI_STRUCTURE: %d fields", mti_TickLength(type)); - for (int i = 0; i < mti_TickLength(type); i++) { - m_sigs.push_back(ids[i]); + if (left > right) { + for (int i = left; i >= right; i--) { + m_sigs.push_back(static_cast(fli_obj->get_sub_hdl(i))); } - mti_VsimFree(ids); } else { - int left = m_parent->get_range_left(); - int right = m_parent->get_range_right(); - - if (left > right) { - for (int i = left; i >= right; i--) { - m_sigs.push_back(static_cast(fli_obj->get_sub_hdl(i))); - } - } else { - for (int i = left; i <= right; i++) { - m_sigs.push_back(static_cast(fli_obj->get_sub_hdl(i))); - } + for (int i = left; i <= right; i++) { + m_sigs.push_back(static_cast(fli_obj->get_sub_hdl(i))); } } } break; case FliIterator::OTM_VARIABLE_SUB_ELEMENTS: - if (m_parent->get_type() == GPI_REGISTER || m_parent->get_type() == GPI_ARRAY || m_parent->get_type() == GPI_STRUCTURE) { - FliValueObjHdl *fli_obj = reinterpret_cast(m_parent); + if (m_parent->get_type() == GPI_STRUCTURE) { + mtiVariableIdT parent = m_parent->get_handle(); + + mtiTypeIdT type = mti_GetVarType(parent); + mtiVariableIdT *ids = mti_GetVarSubelements(parent,NULL); - if (m_parent->get_type() == GPI_REGISTER && fli_obj->get_fli_typekind() == MTI_TYPE_ENUM) { - break; + LOG_DEBUG("GPI_STRUCTURE: %d fields", mti_TickLength(type)); + for (int i = 0; i < mti_TickLength(type); i++) { + m_vars.push_back(ids[i]); } - if (m_parent->get_type() == GPI_STRUCTURE) { - mtiVariableIdT parent = m_parent->get_handle(); + mti_VsimFree(ids); + } else if (m_parent->get_indexable()) { + FliValueObjHdl *fli_obj = reinterpret_cast(m_parent); - mtiTypeIdT type = mti_GetVarType(parent); - mtiVariableIdT *ids = mti_GetVarSubelements(parent,NULL); + int left = m_parent->get_range_left(); + int right = m_parent->get_range_right(); - LOG_DEBUG("GPI_STRUCTURE: %d fields", mti_TickLength(type)); - for (int i = 0; i < mti_TickLength(type); i++) { - m_vars.push_back(ids[i]); + if (left > right) { + for (int i = left; i >= right; i--) { + m_vars.push_back(static_cast(fli_obj->get_sub_hdl(i))); } - - mti_VsimFree(ids); } else { - int left = m_parent->get_range_left(); - int right = m_parent->get_range_right(); - - if (left > right) { - for (int i = left; i >= right; i--) { - m_vars.push_back(static_cast(fli_obj->get_sub_hdl(i))); - } - } else { - for (int i = left; i <= right; i++) { - m_vars.push_back(static_cast(fli_obj->get_sub_hdl(i))); - } + for (int i = left; i <= right; i++) { + m_vars.push_back(static_cast(fli_obj->get_sub_hdl(i))); } } } diff --git a/lib/fli/FliObjHdl.cpp b/lib/fli/FliObjHdl.cpp index db6165ac..06e9b797 100644 --- a/lib/fli/FliObjHdl.cpp +++ b/lib/fli/FliObjHdl.cpp @@ -149,6 +149,9 @@ int FliValueObjHdl::set_signal_value(const double value) } void *FliValueObjHdl::get_sub_hdl(int index) { + if (!m_indexable) + return NULL; + if (m_sub_hdls == NULL) { if (is_var()) { m_sub_hdls = (void **)mti_GetVarSubelements(get_handle(), NULL); @@ -161,18 +164,14 @@ void *FliValueObjHdl::get_sub_hdl(int index) { if (m_range_left > m_range_right) { idx = m_range_left - index; - - if (idx < 0 || idx > (m_range_left-m_range_right)) { - return NULL; - } } else { idx = index - m_range_left; - - if (idx < 0 || idx > (m_range_right-m_range_left)) { - return NULL; - } } - return m_sub_hdls[idx]; + + if (idx < 0 || idx >= m_num_elems) + return NULL; + else + return m_sub_hdls[idx]; } int FliEnumObjHdl::initialise(std::string &name, std::string &fq_name) From 3632469557bf91690f7a4d6b07be5a878663816e Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 22 Mar 2016 13:30:26 -0700 Subject: [PATCH 1164/2656] Issue #393: Clean-up VPI interface --- lib/vpi/VpiCbHdl.cpp | 1 - lib/vpi/VpiImpl.cpp | 70 +++++++++++++++++--------------------------- 2 files changed, 27 insertions(+), 44 deletions(-) diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 62bba6ea..6b72e1ee 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -821,7 +821,6 @@ GpiIterator::Status VpiIterator::next_handle(std::string &name, GpiObjHdl **hdl, name = idx_str.substr(0,found); obj = m_parent->get_handle(); } else { - LOG_WARN("Unhandled Generate Loop Format - %s", name.c_str()); name = c_name; } } else { diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index fbc1c465..aabf150d 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -35,30 +35,27 @@ static VpiImpl *vpi_table; } +#define CASE_STR(_X) \ + case _X: return #_X + const char *VpiImpl::reason_to_string(int reason) { switch (reason) { - case cbValueChange: - return "cbValueChange"; - case cbAtStartOfSimTime: - return "cbAtStartOfSimTime"; - case cbReadWriteSynch: - return "cbReadWriteSynch"; - case cbReadOnlySynch: - return "cbReadOnlySynch"; - case cbNextSimTime: - return "cbNextSimTime"; - case cbAfterDelay: - return "cbAfterDelay"; - case cbStartOfSimulation: - return "cbStartOfSimulation"; - case cbEndOfSimulation: - return "cbEndOfSimulation"; - default: - return "unknown"; + CASE_STR(cbValueChange); + CASE_STR(cbAtStartOfSimTime); + CASE_STR(cbReadWriteSynch); + CASE_STR(cbReadOnlySynch); + CASE_STR(cbNextSimTime); + CASE_STR(cbAfterDelay); + CASE_STR(cbStartOfSimulation); + CASE_STR(cbEndOfSimulation); + + default: return "unknown"; } } +#undef CASE_STR + void VpiImpl::get_sim_time(uint32_t *high, uint32_t *low) { s_vpi_time vpi_time_s; @@ -123,7 +120,7 @@ gpi_objtype_t to_gpi_objtype(int32_t vpitype) case vpiGenScope: return GPI_MODULE; - case vpiStringVal: + case vpiStringVar: return GPI_STRING; default: @@ -215,6 +212,8 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, GpiObjHdl* VpiImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) { + GpiObjHdl *parent_hdl = sim_to_hdl(parent); + LOG_DEBUG("Trying to convert raw to VPI handle"); vpiHandle new_hdl = (vpiHandle)raw_hdl; @@ -226,7 +225,7 @@ GpiObjHdl* VpiImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) } std::string name = c_name; - std::string fq_name = parent->get_fullname() + "." + name; + std::string fq_name = parent_hdl->get_fullname() + "." + name; GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { @@ -239,38 +238,23 @@ GpiObjHdl* VpiImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) { + GpiObjHdl *parent_hdl = sim_to_hdl(parent); + vpiHandle new_hdl; - std::string fq_name = parent->get_fullname() + "." + name; + std::string fq_name = parent_hdl->get_fullname() + "." + name; std::vector writable(fq_name.begin(), fq_name.end()); writable.push_back('\0'); new_hdl = vpi_handle_by_name(&writable[0], NULL); + /* No need to iterate to look for generate loops as the tools will at least find vpiGenScopeArray */ if (new_hdl == NULL) { - /* If not found, check to see if the name of a generate loop */ - vpiHandle iter = vpi_iterate(vpiInternalScope, parent->get_handle()); - - if (iter != NULL) { - vpiHandle rgn; - for (rgn = vpi_scan(iter); rgn != NULL; rgn = vpi_scan(iter)) { - if (vpi_get(vpiType, rgn) == vpiGenScope) { - std::string rgn_name = vpi_get_str(vpiName, rgn); - if (rgn_name.compare(0,name.length(),name) == 0) { - new_hdl = parent->get_handle(); - vpi_free_object(iter); - break; - } - } - } - } - if (new_hdl == NULL) { - LOG_DEBUG("Unable to query vpi_get_handle_by_name %s", fq_name.c_str()); - return NULL; - } + LOG_DEBUG("Unable to query vpi_get_handle_by_name %s", fq_name.c_str()); + return NULL; } /* Generate Loops have inconsistent behavior across vpi tools. A "name" - * without an index, i.e. dut.loop vs dut.loop[0], will find a handl to vpiGenScopeArray, + * without an index, i.e. dut.loop vs dut.loop[0], will find a handle to vpiGenScopeArray, * but not all tools support iterating over the vpiGenScopeArray. We don't want to create * a GpiObjHdl to this type of vpiHandle. * @@ -280,7 +264,7 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) if (vpi_get(vpiType, new_hdl) == vpiGenScopeArray) { vpi_free_object(new_hdl); - new_hdl = parent->get_handle(); + new_hdl = parent_hdl->get_handle(); } From 70f6a1408742ffe0d421b98f3aeab4bb4cd6281f Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 22 Mar 2016 14:47:13 -0700 Subject: [PATCH 1165/2656] Issue #393: Clean-up VHPI interface --- lib/vhpi/VhpiCbHdl.cpp | 2 +- lib/vhpi/VhpiImpl.cpp | 63 +++++++++++++++++++++++------------------- lib/vhpi/VhpiImpl.h | 6 ++-- 3 files changed, 38 insertions(+), 33 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 986a9988..5738b732 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -1073,7 +1073,7 @@ GpiIterator::Status VhpiIterator::next_handle(std::string &name, fq_name += "." + name; } VhpiImpl *vhpi_impl = reinterpret_cast(m_impl); - new_obj = vhpi_impl->create_gpi_obj_from_handle(obj, name, fq_name, m_parent->get_const()); + new_obj = vhpi_impl->create_gpi_obj_from_handle(obj, name, fq_name); if (new_obj) { *hdl = new_obj; return GpiIterator::NATIVE; diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 19a66cd0..c99606ad 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -102,15 +102,20 @@ void VhpiImpl::get_sim_precision(int32_t *precision) } // Determine whether a VHPI object type is a constant or not -bool is_const(vhpiIntT vhpitype) +bool is_const(vhpiHandleT hdl) { - switch (vhpitype) { - case vhpiConstDeclK: - case vhpiGenericDeclK: + vhpiHandleT tmp = hdl; + + /* Need to walk the prefix's back to the original handle to get a type + * that is not vhpiSelectedNameK or vhpiIndexedNameK + */ + do { + vhpiIntT vhpitype = vhpi_get(vhpiKindP, tmp); + if (vhpiConstDeclK == vhpitype || vhpiGenericDeclK == vhpitype) return true; - default: - return false; - } + } while ((tmp = vhpi_handle(vhpiPrefix,tmp)) != NULL); + + return false; } bool is_enum_logic(vhpiHandleT hdl) { @@ -217,8 +222,7 @@ bool is_enum_boolean(vhpiHandleT hdl) { GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string &name, - std::string &fq_name, - bool parentConst) + std::string &fq_name) { vhpiIntT type; gpi_objtype_t gpi_type; @@ -229,8 +233,6 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, return NULL; } - bool is_constant = is_const(type) || parentConst; - /* We need to delve further here to detemine how to later set the values of an object */ vhpiHandleT base_hdl = vhpi_handle(vhpiBaseType, new_hdl); @@ -387,13 +389,13 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, if (gpi_type != GPI_ARRAY && gpi_type != GPI_GENARRAY && gpi_type != GPI_MODULE && gpi_type != GPI_STRUCTURE) { if (gpi_type == GPI_REGISTER) - new_obj = new VhpiLogicSignalObjHdl(this, new_hdl, gpi_type, is_constant); + new_obj = new VhpiLogicSignalObjHdl(this, new_hdl, gpi_type, is_const(new_hdl)); else - new_obj = new VhpiSignalObjHdl(this, new_hdl, gpi_type, is_constant); + new_obj = new VhpiSignalObjHdl(this, new_hdl, gpi_type, is_const(new_hdl)); } else if (gpi_type == GPI_ARRAY) { - new_obj = new VhpiArrayObjHdl(this, new_hdl, gpi_type, is_constant); + new_obj = new VhpiArrayObjHdl(this, new_hdl, gpi_type); } else { - new_obj = new GpiObjHdl(this, new_hdl, gpi_type, is_constant); + new_obj = new GpiObjHdl(this, new_hdl, gpi_type); } if (new_obj->initialise(name, fq_name)) { @@ -410,11 +412,13 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, GpiObjHdl *VhpiImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) { + GpiObjHdl *parent_hdl = sim_to_hdl(parent); + LOG_DEBUG("Trying to convert raw to VHPI handle"); vhpiHandleT new_hdl = (vhpiHandleT)raw_hdl; - std::string fq_name = parent->get_fullname(); + std::string fq_name = parent_hdl->get_fullname(); const char *c_name = vhpi_get_str(vhpiCaseNameP, new_hdl); if (!c_name) { LOG_DEBUG("Unable to query name of passed in handle"); @@ -429,7 +433,7 @@ GpiObjHdl *VhpiImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) fq_name += "." + name; } - GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name, parent->get_const()); + GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { vhpi_release_handle(new_hdl); LOG_DEBUG("Unable to fetch object %s", fq_name.c_str()); @@ -441,8 +445,11 @@ GpiObjHdl *VhpiImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) { + GpiObjHdl *parent_hdl = sim_to_hdl(parent); + vhpiHandleT vhpi_hdl = parent_hdl->get_handle(); + vhpiHandleT new_hdl; - std::string fq_name = parent->get_fullname(); + std::string fq_name = parent_hdl->get_fullname(); if (fq_name == ":") { fq_name += name; } else { @@ -453,9 +460,9 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) new_hdl = vhpi_handle_by_name(&writable[0], NULL); - if (new_hdl == NULL && parent->get_type() == GPI_STRUCTURE) { + if (new_hdl == NULL && parent_hdl->get_type() == GPI_STRUCTURE) { /* vhpi_handle_by_name() doesn't always work for records, specificaly records in generics */ - vhpiHandleT iter = vhpi_iterator(vhpiSelectedNames, parent->get_handle()); + vhpiHandleT iter = vhpi_iterator(vhpiSelectedNames, vhpi_hdl); if (iter != NULL) { while ((new_hdl = vhpi_scan(iter)) != NULL) { std::string selected_name = vhpi_get_str(vhpiCaseNameP, new_hdl); @@ -473,7 +480,7 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) } } else if (new_hdl == NULL) { /* If not found, check to see if the name of a generate loop */ - vhpiHandleT iter = vhpi_iterator(vhpiInternalRegions, parent->get_handle()); + vhpiHandleT iter = vhpi_iterator(vhpiInternalRegions, vhpi_hdl); if (iter != NULL) { vhpiHandleT rgn; @@ -481,7 +488,7 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) if (vhpi_get(vhpiKindP, rgn) == vhpiForGenerateK) { std::string rgn_name = vhpi_get_str(vhpiCaseNameP, rgn); if (rgn_name.compare(0,name.length(),name) == 0) { - new_hdl = parent->get_handle(); + new_hdl = vhpi_hdl; vhpi_release_handle(iter); break; } @@ -504,10 +511,10 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) if (vhpi_get(vhpiKindP, new_hdl) == vhpiForGenerateK) { vhpi_release_handle(new_hdl); - new_hdl = parent->get_handle(); + new_hdl = vhpi_hdl; } - GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name, parent->get_const()); + GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { vhpi_release_handle(new_hdl); LOG_DEBUG("Unable to fetch object %s", fq_name.c_str()); @@ -521,8 +528,8 @@ GpiObjHdl *VhpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) { GpiObjHdl *parent_hdl = sim_to_hdl(parent); vhpiHandleT vhpi_hdl = parent_hdl->get_handle(); - std::string name = parent->get_name(); - std::string fq_name = parent->get_fullname(); + std::string name = parent_hdl->get_name(); + std::string fq_name = parent_hdl->get_fullname(); vhpiHandleT new_hdl = NULL; char buff[14]; // needs to be large enough to hold -2^31 to 2^31-1 in string form ('(''-'10+'')'\0') @@ -739,7 +746,7 @@ GpiObjHdl *VhpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) return NULL; } - GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name, parent_hdl->get_const()); + GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { vhpi_release_handle(new_hdl); LOG_DEBUG("Could not fetch object below entity (%s) at index (%d)", @@ -816,7 +823,7 @@ GpiObjHdl *VhpiImpl::get_root_handle(const char* name) root_name = found; - return create_gpi_obj_from_handle(dut, root_name, root_name, false); + return create_gpi_obj_from_handle(dut, root_name, root_name); } diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index 94069d05..b8967075 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -162,8 +162,7 @@ class VhpiArrayObjHdl : public GpiObjHdl { public: VhpiArrayObjHdl(GpiImplInterface *impl, vhpiHandleT hdl, - gpi_objtype_t objtype, - bool is_const) : GpiObjHdl(impl, hdl, objtype, is_const) { } + gpi_objtype_t objtype) : GpiObjHdl(impl, hdl, objtype) { } virtual ~VhpiArrayObjHdl() { } int initialise(std::string &name, std::string &fq_name); @@ -265,8 +264,7 @@ class VhpiImpl : public GpiImplInterface { GpiObjHdl *create_gpi_obj_from_handle(vhpiHandleT new_hdl, std::string &name, - std::string &fq_name, - bool parentConst); + std::string &fq_name); private: VhpiReadwriteCbHdl m_read_write; From 1496e1eb728ecab2a52af98354524c06a20d0ab6 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Wed, 23 Mar 2016 13:16:01 -0700 Subject: [PATCH 1166/2656] Issue #393: Add more tests --- tests/test_cases/test_array/test_array.py | 179 +++++++++++++++++++++- 1 file changed, 177 insertions(+), 2 deletions(-) diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py index 21cbdcf4..57b217f7 100644 --- a/tests/test_cases/test_array/test_array.py +++ b/tests/test_cases/test_array/test_array.py @@ -16,6 +16,166 @@ def _check_type(tlog, hdl, expected): else: tlog.info(" Found %s (%s) with length=%d", hdl._fullname, type(hdl), len(hdl)) +def _check_int(tlog, hdl, expected): + if int(hdl) != expected: + raise TestFailure("{2}: Expected >{0}< but got >{1}<".format(expected, int(hdl), hdl._fullname)) + else: + tlog.info(" Found {0} ({1}) with value={2}".format(hdl._fullname, type(hdl), int(hdl))) + +def _check_logic(tlog, hdl, expected): + if int(hdl) != expected: + raise TestFailure("{2}: Expected >0x{0:X}< but got >0x{1:X}<".format(expected, int(hdl), hdl._fullname)) + else: + tlog.info(" Found {0} ({1}) with value=0x{2:X}".format(hdl._fullname, type(hdl), int(hdl))) + +def _check_str(tlog, hdl, expected): + if repr(hdl) != expected: + raise TestFailure("{2}: Expected >{0}< but got >{1}<".format(expected, repr(hdl), hdl._fullname)) + else: + tlog.info(" Found {0} ({1}) with value={2}".format(hdl._fullname, type(hdl), repr(hdl))) + +def _check_real(tlog, hdl, expected): + if float(hdl) != expected: + raise TestFailure("{2}: Expected >{0}< but got >{1}<".format(expected, float(hdl), hdl._fullname)) + else: + tlog.info(" Found {0} ({1}) with value={2}".format(hdl._fullname, type(hdl), float(hdl))) + +@cocotb.test() +def test_read_write(dut): + """Test handle inheritance""" + tlog = logging.getLogger("cocotb.test") + + cocotb.fork(Clock(dut.clk, 1000).start()) + + yield Timer(1000) + + tlog.info("Checking Generics/Parameters:") + _check_logic(tlog, dut.param_logic , 1) + _check_logic(tlog, dut.param_logic_vec, 0xDA) + + if cocotb.LANGUAGE in ["vhdl"]: + _check_int (tlog, dut.param_bool, 1) + _check_int (tlog, dut.param_int , 6) + _check_real(tlog, dut.param_real, 3.14) + _check_int (tlog, dut.param_char, ord('p')) + _check_str (tlog, dut.param_str , "ARRAYMOD") + + if not cocotb.SIM_NAME.lower().startswith(("riviera")): + _check_logic(tlog, dut.param_rec.a , 0) + _check_logic(tlog, dut.param_rec.b[0] , 0) + _check_logic(tlog, dut.param_rec.b[1] , 0) + _check_logic(tlog, dut.param_rec.b[2] , 0) + _check_logic(tlog, dut.param_cmplx[0].a , 0) + _check_logic(tlog, dut.param_cmplx[0].b[0], 0) + _check_logic(tlog, dut.param_cmplx[0].b[1], 0) + _check_logic(tlog, dut.param_cmplx[0].b[2], 0) + _check_logic(tlog, dut.param_cmplx[1].a , 0) + _check_logic(tlog, dut.param_cmplx[1].b[0], 0) + _check_logic(tlog, dut.param_cmplx[1].b[1], 0) + _check_logic(tlog, dut.param_cmplx[1].b[2], 0) + + tlog.info("Checking Constants:") + _check_logic(tlog, dut.const_logic , 0) + _check_logic(tlog, dut.const_logic_vec, 0x3D) + + if cocotb.LANGUAGE in ["vhdl"]: + _check_int (tlog, dut.const_bool, 0) + _check_int (tlog, dut.const_int , 12) + _check_real(tlog, dut.const_real, 6.28) + _check_int (tlog, dut.const_char, ord('c')) + _check_str (tlog, dut.const_str , "MODARRAY") + + if not cocotb.SIM_NAME.lower().startswith(("riviera")): + _check_logic(tlog, dut.const_rec.a , 1) + _check_logic(tlog, dut.const_rec.b[0] , 0xFF) + _check_logic(tlog, dut.const_rec.b[1] , 0xFF) + _check_logic(tlog, dut.const_rec.b[2] , 0xFF) + _check_logic(tlog, dut.const_cmplx[1].a , 1) + _check_logic(tlog, dut.const_cmplx[1].b[0], 0xFF) + _check_logic(tlog, dut.const_cmplx[1].b[1], 0xFF) + _check_logic(tlog, dut.const_cmplx[1].b[2], 0xFF) + _check_logic(tlog, dut.const_cmplx[2].a , 1) + _check_logic(tlog, dut.const_cmplx[2].b[0], 0xFF) + _check_logic(tlog, dut.const_cmplx[2].b[1], 0xFF) + _check_logic(tlog, dut.const_cmplx[2].b[2], 0xFF) + + dut.select_in = 2 + + yield Timer(1000) + + tlog.info("Writing the signals!!!") + dut.sig_logic = 1 + dut.sig_logic_vec = 0xCC + if cocotb.LANGUAGE in ["vhdl"]: + dut.sig_bool = 1 + dut.sig_int = 5000 + dut.sig_real = 22.54 + dut.sig_char = ord('Z') + dut.sig_str = "Testing" + dut.sig_rec.a = 1 + dut.sig_rec.b[0] = 0x01 + dut.sig_rec.b[1] = 0x23 + dut.sig_rec.b[2] = 0x45 + dut.sig_cmplx[0].a = 0 + dut.sig_cmplx[0].b[0] = 0x67 + dut.sig_cmplx[0].b[1] = 0x89 + dut.sig_cmplx[0].b[2] = 0xAB + dut.sig_cmplx[1].a = 1 + dut.sig_cmplx[1].b[0] = 0xCD + dut.sig_cmplx[1].b[1] = 0xEF + dut.sig_cmplx[1].b[2] = 0x55 + + yield Timer(1000) + + tlog.info("Checking writes:") + _check_logic(tlog, dut.port_logic_out , 1) + _check_logic(tlog, dut.port_logic_vec_out, 0xCC) + + if cocotb.LANGUAGE in ["vhdl"]: + _check_int (tlog, dut.port_bool_out, 1) + _check_int (tlog, dut.port_int_out , 5000) + _check_real(tlog, dut.port_real_out, 22.54) + _check_int (tlog, dut.port_char_out, ord('Z')) + _check_str (tlog, dut.port_str_out , "Testing") + + _check_logic(tlog, dut.port_rec_out.a , 1) + _check_logic(tlog, dut.port_rec_out.b[0] , 0x01) + _check_logic(tlog, dut.port_rec_out.b[1] , 0x23) + _check_logic(tlog, dut.port_rec_out.b[2] , 0x45) + _check_logic(tlog, dut.port_cmplx_out[0].a , 0) + _check_logic(tlog, dut.port_cmplx_out[0].b[0], 0x67) + _check_logic(tlog, dut.port_cmplx_out[0].b[1], 0x89) + _check_logic(tlog, dut.port_cmplx_out[0].b[2], 0xAB) + _check_logic(tlog, dut.port_cmplx_out[1].a , 1) + _check_logic(tlog, dut.port_cmplx_out[1].b[0], 0xCD) + _check_logic(tlog, dut.port_cmplx_out[1].b[1], 0xEF) + _check_logic(tlog, dut.port_cmplx_out[1].b[2], 0x55) + + tlog.info("Writing a few signal sub-idices!!!") + dut.sig_logic_vec[2] = 0 + if cocotb.LANGUAGE in ["vhdl"] or not cocotb.SIM_NAME.lower().startswith(("ncsim")): + dut.sig_t6[1][3][2] = 1 + dut.sig_t6[0][2][7] = 0 + + if cocotb.LANGUAGE in ["vhdl"]: + dut.sig_str[2] = ord('E') + dut.sig_rec.b[1][7] = 1 + dut.sig_cmplx[1].b[1][0] = 0 + + yield Timer(1000) + + tlog.info("Checking writes (2):") + _check_logic(tlog, dut.port_logic_vec_out, 0xC8) + if cocotb.LANGUAGE in ["vhdl"] or not cocotb.SIM_NAME.lower().startswith(("ncsim")): + _check_logic(tlog, dut.sig_t6[1][3][2], 1) + _check_logic(tlog, dut.sig_t6[0][2][7], 0) + + if cocotb.LANGUAGE in ["vhdl"]: + _check_str (tlog, dut.port_str_out , "TEsting") + + _check_logic(tlog, dut.port_rec_out.b[1] , 0xA3) + _check_logic(tlog, dut.port_cmplx_out[1].b[1], 0xEE) + @cocotb.test() def test_gen_loop(dut): """Test accessing Generate Loops""" @@ -42,12 +202,12 @@ def test_gen_loop(dut): tlog.info("Iterate access found %s", gens) if len(desc_gen) != 8: - raise TestError("Length of desc_gen is >%d< and should be 8".format(len(desc_gen))) + raise TestError("Length of desc_gen is >{}< and should be 8".format(len(desc_gen))) else: tlog.info("Length of desc_gen is %d", len(desc_gen)) if len(dut.asc_gen) != 8: - raise TestError("Length of asc_gen is >%d< and should be 8".format(len(dut.asc_gen))) + raise TestError("Length of asc_gen is >{}< and should be 8".format(len(dut.asc_gen))) else: tlog.info("Length of asc_gen is %d", len(dut.asc_gen)) @@ -174,6 +334,12 @@ def _discover(obj,indent): if total != pass_total: raise TestFailure("Expected {0} objects but found {1}".format(pass_total, total)) +def test_basic_constant_access(dut): + """Test accessing constant/parameter basic data types""" + + tlog = logging.getLogger("cocotb.test") + + yield Timer(2000) @cocotb.test(skip=(cocotb.LANGUAGE in ["verilog"] or cocotb.SIM_NAME.lower().startswith(("riviera")))) def test_direct_constant_indexing(dut): @@ -286,3 +452,12 @@ def test_direct_signal_indexing(dut): _check_type(tlog, dut.sig_rec.b[1], ModifiableObject) _check_type(tlog, dut.sig_rec.b[1][2], ModifiableObject) +@cocotb.test() +def delay_test(dut): + """Waits for time then ends""" + + yield Timer(1000) + + #tlog = logging.getLogger("cocotb.test") + #for thing in dut: + # tlog.info("Found %s (%s)", thing._fullname, type(thing)) From e3df1864cc42f30bbf2373f208ef2671b4e45e16 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Thu, 24 Mar 2016 08:25:03 -0700 Subject: [PATCH 1167/2656] Issue #393: Fix a bug that causes the build to fail if SIM=<> is not on the command-line --- lib/Makefile | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/Makefile b/lib/Makefile index 3af0436c..4be1d216 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -40,28 +40,28 @@ $(LIB_DIR): $(LIB_OBJ_DIR) mkdir -p $@ $(LIB_DIR)/libgpilog.$(LIB_EXT): $(SIM_ROOT)/lib/gpi_log/gpi_logging.c | $(LIB_DIR) - make -C $(SIM_ROOT)/lib/gpi_log + make -C $(SIM_ROOT)/lib/gpi_log SIM=$(SIM) $(LIB_DIR)/libcocotb.$(LIB_EXT): $(SIM_ROOT)/lib/embed/gpi_embed.c | $(LIB_DIR) - make -C $(SIM_ROOT)/lib/embed + make -C $(SIM_ROOT)/lib/embed SIM=$(SIM) $(LIB_DIR)/libvpi.$(LIB_EXT): $(SIM_ROOT)/lib/vpi/VpiImpl.cpp $(SIM_ROOT)/lib/vpi/VpiCbHdl.cpp | $(LIB_DIR) - make -C $(SIM_ROOT)/lib/vpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) + make -C $(SIM_ROOT)/lib/vpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) SIM=$(SIM) $(LIB_DIR)/libvhpi.$(LIB_EXT): $(SIM_ROOT)/lib/vhpi/VhpiImpl.cpp $(SIM_ROOT)/lib/vhpi/VhpiCbHdl.cpp | $(LIB_DIR) - make -C $(SIM_ROOT)/lib/vhpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) + make -C $(SIM_ROOT)/lib/vhpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) SIM=$(SIM) $(LIB_DIR)/libgpi.$(LIB_EXT): $(SIM_ROOT)/lib/gpi/GpiCommon.cpp $(SIM_ROOT)/lib/gpi/GpiCbHdl.cpp | $(LIB_DIR) - make -C $(SIM_ROOT)/lib/gpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) + make -C $(SIM_ROOT)/lib/gpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) SIM=$(SIM) $(LIB_DIR)/libfli.$(LIB_EXT): $(SIM_ROOT)/lib/fli/FliImpl.cpp $(SIM_ROOT)/lib/fli/FliCbHdl.cpp $(SIM_ROOT)/lib/fli/FliObjHdl.cpp | $(LIB_DIR) - make -C $(SIM_ROOT)/lib/fli EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) + make -C $(SIM_ROOT)/lib/fli EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) SIM=$(SIM) $(LIB_DIR)/libsim.$(LIB_EXT): $(SIM_ROOT)/lib/simulator/simulatormodule.c | $(LIB_DIR) - make -C $(SIM_ROOT)/lib/simulator + make -C $(SIM_ROOT)/lib/simulator SIM=$(SIM) $(LIB_DIR)/libcocotbutils.$(LIB_EXT): $(SIM_ROOT)/lib/utils/cocotb_utils.c | $(LIB_DIR) - make -C $(SIM_ROOT)/lib/utils + make -C $(SIM_ROOT)/lib/utils SIM=$(SIM) COCOTB_LIBS := $(LIB_DIR)/libcocotbutils.$(LIB_EXT) \ $(LIB_DIR)/libgpilog.$(LIB_EXT) \ From 98ea1c65b2dbda3f7820c486f3436a53a1a6d5d9 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Thu, 24 Mar 2016 08:35:56 -0700 Subject: [PATCH 1168/2656] Issue #393: Fix a bug in icarus Makefile using the wrong variable --- makefiles/simulators/Makefile.icarus | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index 88ccdf39..149a6cc3 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -76,12 +76,12 @@ endif results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(CMD_EXE) -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) + $(CMD) -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - gdb --args $(CMD_EXE) -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) + gdb --args $(CMD) -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) clean:: -@rm -rf $(SIM_BUILD) From a66dd6019e02e6b77dd8f49d3b871e7676f5fc19 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Thu, 24 Mar 2016 09:45:19 -0700 Subject: [PATCH 1169/2656] Issue #393: Fix a bug in handle.py that was not caught by test --- cocotb/handle.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index e6532186..06403cb8 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -335,7 +335,7 @@ def __init__(self, handle): SimHandleBase.__init__(self, handle) def __iter__(self): - raise StopIteration + return iter(()) def _getvalue(self): raise TypeError("Not permissible to get values on object %s" % (self._name)) @@ -439,6 +439,7 @@ def __getitem__(self, index): def __iter__(self): if self._range is None: raise StopIteration + self._log.debug("Iterating with range [%d:%d]" % (self._range[0], self._range[1])) for i in self._range_iter(self._range[0], self._range[1]): try: From 3d284fbf3b4ad4b313188d1f80eae693c5eb46bb Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Thu, 24 Mar 2016 12:03:06 -0700 Subject: [PATCH 1170/2656] Issue #393: Fix a bug in Icarus makefile not using vpp and test_discovery not skipping properly for Icarus --- makefiles/simulators/Makefile.icarus | 4 ++-- tests/test_cases/test_discovery/test_discovery.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index 149a6cc3..28442532 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -76,12 +76,12 @@ endif results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(CMD) -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) + $(ICARUS_BIN_DIR)/vpp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - gdb --args $(CMD) -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) + gdb --args $(ICARUS_BIN_DIR)/vpp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) clean:: -@rm -rf $(SIM_BUILD) diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index cea890d5..0cbe8357 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -134,7 +134,7 @@ def access_single_bit_erroneous(dut): dut.stream_in_data[bit] <= 1 yield Timer(10) -@cocotb.test(expect_error=cocotb.SIM_NAME.lower().startswith(("Icarus Verilog")), +@cocotb.test(expect_error=cocotb.SIM_NAME.lower().startswith(("icarus")), expect_fail=cocotb.SIM_NAME.lower().startswith(("riviera")) and cocotb.LANGUAGE in ["verilog"]) def access_integer(dut): """Integer should show as an IntegerObject""" From 830f35eeb4d96541b6d8e7893ab301d6bcbfc4fa Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Thu, 24 Mar 2016 12:18:30 -0700 Subject: [PATCH 1171/2656] Issue #393: Fix a typo in the Icarus Makefile --- makefiles/simulators/Makefile.icarus | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index 28442532..abf3a892 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -76,12 +76,12 @@ endif results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(ICARUS_BIN_DIR)/vpp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) + $(ICARUS_BIN_DIR)/vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - gdb --args $(ICARUS_BIN_DIR)/vpp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) + gdb --args $(ICARUS_BIN_DIR)/vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) clean:: -@rm -rf $(SIM_BUILD) From 270f5f766683f0d5180bfa3ed07a6987530825af Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Thu, 24 Mar 2016 12:42:19 -0700 Subject: [PATCH 1172/2656] Issue #393: Updates to remove struct for Icarus --- tests/designs/array_module/array_module.v | 14 +++++++--- tests/test_cases/test_array/test_array.py | 32 +++++++++++------------ 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/tests/designs/array_module/array_module.v b/tests/designs/array_module/array_module.v index c3c70028..f8b3b4d2 100644 --- a/tests/designs/array_module/array_module.v +++ b/tests/designs/array_module/array_module.v @@ -29,10 +29,12 @@ `timescale 1 ps / 1 ps +`ifndef __ICARUS__ typedef struct { logic a; logic [7:0] b[0:2]; } rec_type; +`endif module array_module ( input clk, @@ -47,15 +49,17 @@ module array_module ( output [0:7] port_asc_out, output [1:8] port_ofst_out, - output logic port_logic_out, - output logic [7:0] port_logic_vec_out, +`ifndef __ICARUS__ + output rec_type port_rec_out, + output rec_type port_cmplx_out[0:1], +`endif //output bit port_bool_out, //output integer port_int_out, //output real port_real_out, //output byte port_char_out, //output string port_str_out, - output rec_type port_rec_out, - output rec_type port_cmplx_out[0:1] + output logic port_logic_out, + output logic [7:0] port_logic_vec_out ); parameter logic param_logic = 1'b1; @@ -95,8 +99,10 @@ wire logic [7:0] sig_logic_vec; // real sig_real; // byte sig_char; // string sig_str; +`ifndef __ICARUS__ rec_type sig_rec; rec_type sig_cmplx [0:1]; +`endif assign port_ofst_out = port_ofst_in; diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py index 57b217f7..963dee21 100644 --- a/tests/test_cases/test_array/test_array.py +++ b/tests/test_cases/test_array/test_array.py @@ -238,7 +238,9 @@ def test_discover_all(dut): 1 (port_char_out) (VHDL Only) 9 (port_str_out) (VHDL Only) 30 (port_rec_out) (VPI - Aldec sees as a Module and not structure (i.e. cnt = 1)) + (VPI - Not present for Icarus) 61 (port_cmplx_out) (VPI - Aldec sees as a Module and not structure (i.e. cnt = 1)) + (VPI - Not present for Icarus) constants: 1 (const_logic) 1 (const_logic_vec) 1 (const_bool) (VHDL Only) @@ -264,8 +266,8 @@ def test_discover_all(dut): 1 (sig_real) (VHDL Only) 1 (sig_char) (VHDL Only) 9 (sig_str) (VHDL Only) - 30 (sig_rec.a, sig_rec.b[0:2][7:0]) (VPI doesn't find, added manually, except for Aldec) - 61 (sig_cmplx[0:1].a, sig_cmplx[0:1].b[0:2][7:0]) (VPI - Aldec doesn't find) + 30 (sig_rec.a, sig_rec.b[0:2][7:0]) (VPI doesn't find, added manually, except for Aldec and Icarus) + 61 (sig_cmplx[0:1].a, sig_cmplx[0:1].b[0:2][7:0]) (VPI - Aldec doesn't find, Not available in Icarus) regions: 9 (asc_gen[16:23]) 8 (asc_gen: signals) (VHPI - Riviera doesn't find, added manually) 8 (asc_gen: constant) @@ -282,6 +284,7 @@ def test_discover_all(dut): 816 (VHDL - Aldec) 780 (Verilog - Default) 649 (Verilog - Aldec) + 647 (Verilog - Aldec) """ tlog = logging.getLogger("cocotb.test") @@ -291,7 +294,7 @@ def test_discover_all(dut): # Need to clear sub_handles so won't attempt to iterate over handles like sig_rec and sig_rec_array # # DO NOT REMOVE. Aldec cannot iterate over the complex records due to bugs in the VPI interface. - if cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")): + if cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera","icarus")): if len(dut._sub_handles) != 0: dut._sub_handles = {} @@ -316,6 +319,8 @@ def test_discover_all(dut): pass_total = 854 elif cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")): pass_total = 649 + elif cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("icarus")): + pass_total = 647 else: pass_total = 780 @@ -334,13 +339,6 @@ def _discover(obj,indent): if total != pass_total: raise TestFailure("Expected {0} objects but found {1}".format(pass_total, total)) -def test_basic_constant_access(dut): - """Test accessing constant/parameter basic data types""" - - tlog = logging.getLogger("cocotb.test") - - yield Timer(2000) - @cocotb.test(skip=(cocotb.LANGUAGE in ["verilog"] or cocotb.SIM_NAME.lower().startswith(("riviera")))) def test_direct_constant_indexing(dut): """Test directly accessing constant/parameter data in arrays, i.e. not iterating""" @@ -432,10 +430,11 @@ def test_direct_signal_indexing(dut): _check_type(tlog, dut.sig_t6[1], NonHierarchyIndexableObject) _check_type(tlog, dut.sig_t6[0][3], ModifiableObject) _check_type(tlog, dut.sig_t6[0][3][7], ModifiableObject) - _check_type(tlog, dut.sig_cmplx, NonHierarchyIndexableObject) + if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("icarus"))): + _check_type(tlog, dut.sig_cmplx, NonHierarchyIndexableObject) # Riviera has a bug and finds dut.sig_cmplx[1], but the type returned is a vpiBitVar - if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera"))): + if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera","icarus"))): _check_type(tlog, dut.sig_cmplx[1], HierarchyObject) _check_type(tlog, dut.sig_cmplx[1].a, ModifiableObject) _check_type(tlog, dut.sig_cmplx[1].b, NonHierarchyIndexableObject) @@ -443,12 +442,13 @@ def test_direct_signal_indexing(dut): _check_type(tlog, dut.sig_cmplx[1].b[1][2], ModifiableObject) tlog.info(" dut.sig_cmplx[1].a = %d (%s)", int(dut.sig_cmplx[1].a), dut.sig_cmplx[1].a.value.binstr) - _check_type(tlog, dut.sig_rec, HierarchyObject) - _check_type(tlog, dut.sig_rec.a, ModifiableObject) - _check_type(tlog, dut.sig_rec.b, NonHierarchyIndexableObject) + if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("icarus"))): + _check_type(tlog, dut.sig_rec, HierarchyObject) + _check_type(tlog, dut.sig_rec.a, ModifiableObject) + _check_type(tlog, dut.sig_rec.b, NonHierarchyIndexableObject) # Riviera has a bug and finds dut.sig_rec.b[1], but the type returned is 0 which is unknown - if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera"))): + if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera","icarus"))): _check_type(tlog, dut.sig_rec.b[1], ModifiableObject) _check_type(tlog, dut.sig_rec.b[1][2], ModifiableObject) From 1aa721dce7036174d8cfc196a8106300c4da3d4e Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Thu, 24 Mar 2016 12:55:46 -0700 Subject: [PATCH 1173/2656] Issue #393: Updates to remove select_in for Icarus --- tests/designs/array_module/array_module.v | 6 ++++++ tests/test_cases/test_array/test_array.py | 10 ++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/designs/array_module/array_module.v b/tests/designs/array_module/array_module.v index f8b3b4d2..bc4b9af5 100644 --- a/tests/designs/array_module/array_module.v +++ b/tests/designs/array_module/array_module.v @@ -39,7 +39,9 @@ typedef struct { module array_module ( input clk, +`ifndef __ICARUS__ input integer select_in, +`endif input [7:0] port_desc_in, input [0:7] port_asc_in, @@ -110,6 +112,7 @@ assign port_ofst_out = port_ofst_in; //assign port_cmplx_out = (select_in == 1) ? const_cmplx : (select_in == 2) ? sig_cmplx : param_cmplx; always @(posedge clk) begin +`ifndef __ICARUS__ if (select_in == 1) begin port_logic_out = const_logic; port_logic_vec_out = const_logic_vec; @@ -132,8 +135,10 @@ always @(posedge clk) begin port_cmplx_out[1].b[2] = sig_cmplx[1].b[2]; end else begin if (select_in == 2) begin +`endif port_logic_out = sig_logic; port_logic_vec_out = sig_logic_vec; +`ifndef __ICARUS__ // port_bool_out = sig_bool; // port_int_out = sig_int; // port_real_out = sig_real; @@ -173,6 +178,7 @@ always @(posedge clk) begin port_cmplx_out[1].b[2] = sig_cmplx[1].b[2]; end end +`endif end genvar idx1; diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py index 963dee21..009f68ed 100644 --- a/tests/test_cases/test_array/test_array.py +++ b/tests/test_cases/test_array/test_array.py @@ -99,7 +99,8 @@ def test_read_write(dut): _check_logic(tlog, dut.const_cmplx[2].b[1], 0xFF) _check_logic(tlog, dut.const_cmplx[2].b[2], 0xFF) - dut.select_in = 2 + if not cocotb.SIM_NAME.lower().startswith(("icarus")): + dut.select_in = 2 yield Timer(1000) @@ -224,6 +225,7 @@ def test_discover_all(dut): 13 (param_cmplx[0:1].a, param_cmplx[0:1].b[0:2]) (VHDL only excluding Aldec) ports: 1 (clk) 1 (select_in) (VPI - Aldec sees as 32 bit register (i.e. cnt = 33) + (VPI - Not present for Icarus) 9 (port_desc_in) 9 (port_asc_in) 9 (port_ofst_in) @@ -284,7 +286,7 @@ def test_discover_all(dut): 816 (VHDL - Aldec) 780 (Verilog - Default) 649 (Verilog - Aldec) - 647 (Verilog - Aldec) + 614 (Verilog - Icarus) """ tlog = logging.getLogger("cocotb.test") @@ -319,8 +321,8 @@ def test_discover_all(dut): pass_total = 854 elif cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")): pass_total = 649 - elif cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("icarus")): - pass_total = 647 + elif cocotb.SIM_NAME.lower().startswith(("icarus")): + pass_total = 614 else: pass_total = 780 From e28b49fc2b2ea175428334ced4ead7c3b65b89de Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Thu, 24 Mar 2016 13:18:52 -0700 Subject: [PATCH 1174/2656] Revert the last two changes and skip test_array for Icarus --- tests/designs/array_module/array_module.v | 20 +++---------- tests/test_cases/test_array/Makefile | 9 ++++++ tests/test_cases/test_array/test_array.py | 36 +++++++++++------------ 3 files changed, 30 insertions(+), 35 deletions(-) diff --git a/tests/designs/array_module/array_module.v b/tests/designs/array_module/array_module.v index bc4b9af5..c3c70028 100644 --- a/tests/designs/array_module/array_module.v +++ b/tests/designs/array_module/array_module.v @@ -29,19 +29,15 @@ `timescale 1 ps / 1 ps -`ifndef __ICARUS__ typedef struct { logic a; logic [7:0] b[0:2]; } rec_type; -`endif module array_module ( input clk, -`ifndef __ICARUS__ input integer select_in, -`endif input [7:0] port_desc_in, input [0:7] port_asc_in, @@ -51,17 +47,15 @@ module array_module ( output [0:7] port_asc_out, output [1:8] port_ofst_out, -`ifndef __ICARUS__ - output rec_type port_rec_out, - output rec_type port_cmplx_out[0:1], -`endif + output logic port_logic_out, + output logic [7:0] port_logic_vec_out, //output bit port_bool_out, //output integer port_int_out, //output real port_real_out, //output byte port_char_out, //output string port_str_out, - output logic port_logic_out, - output logic [7:0] port_logic_vec_out + output rec_type port_rec_out, + output rec_type port_cmplx_out[0:1] ); parameter logic param_logic = 1'b1; @@ -101,10 +95,8 @@ wire logic [7:0] sig_logic_vec; // real sig_real; // byte sig_char; // string sig_str; -`ifndef __ICARUS__ rec_type sig_rec; rec_type sig_cmplx [0:1]; -`endif assign port_ofst_out = port_ofst_in; @@ -112,7 +104,6 @@ assign port_ofst_out = port_ofst_in; //assign port_cmplx_out = (select_in == 1) ? const_cmplx : (select_in == 2) ? sig_cmplx : param_cmplx; always @(posedge clk) begin -`ifndef __ICARUS__ if (select_in == 1) begin port_logic_out = const_logic; port_logic_vec_out = const_logic_vec; @@ -135,10 +126,8 @@ always @(posedge clk) begin port_cmplx_out[1].b[2] = sig_cmplx[1].b[2]; end else begin if (select_in == 2) begin -`endif port_logic_out = sig_logic; port_logic_vec_out = sig_logic_vec; -`ifndef __ICARUS__ // port_bool_out = sig_bool; // port_int_out = sig_int; // port_real_out = sig_real; @@ -178,7 +167,6 @@ always @(posedge clk) begin port_cmplx_out[1].b[2] = sig_cmplx[1].b[2]; end end -`endif end genvar idx1; diff --git a/tests/test_cases/test_array/Makefile b/tests/test_cases/test_array/Makefile index abec2cd0..eaa40916 100644 --- a/tests/test_cases/test_array/Makefile +++ b/tests/test_cases/test_array/Makefile @@ -27,7 +27,16 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +ifeq ($(SIM),) +all: + @echo "Skipping test_array since icarus doesn't support indexing" +else ifeq ($(shell echo $(SIM) | tr A-Z a-z),icarus) +all: + @echo "Skipping test_array since icarus doesn't support indexing" +else include ../../designs/array_module/Makefile MODULE = test_array + +endif diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py index 009f68ed..57b217f7 100644 --- a/tests/test_cases/test_array/test_array.py +++ b/tests/test_cases/test_array/test_array.py @@ -99,8 +99,7 @@ def test_read_write(dut): _check_logic(tlog, dut.const_cmplx[2].b[1], 0xFF) _check_logic(tlog, dut.const_cmplx[2].b[2], 0xFF) - if not cocotb.SIM_NAME.lower().startswith(("icarus")): - dut.select_in = 2 + dut.select_in = 2 yield Timer(1000) @@ -225,7 +224,6 @@ def test_discover_all(dut): 13 (param_cmplx[0:1].a, param_cmplx[0:1].b[0:2]) (VHDL only excluding Aldec) ports: 1 (clk) 1 (select_in) (VPI - Aldec sees as 32 bit register (i.e. cnt = 33) - (VPI - Not present for Icarus) 9 (port_desc_in) 9 (port_asc_in) 9 (port_ofst_in) @@ -240,9 +238,7 @@ def test_discover_all(dut): 1 (port_char_out) (VHDL Only) 9 (port_str_out) (VHDL Only) 30 (port_rec_out) (VPI - Aldec sees as a Module and not structure (i.e. cnt = 1)) - (VPI - Not present for Icarus) 61 (port_cmplx_out) (VPI - Aldec sees as a Module and not structure (i.e. cnt = 1)) - (VPI - Not present for Icarus) constants: 1 (const_logic) 1 (const_logic_vec) 1 (const_bool) (VHDL Only) @@ -268,8 +264,8 @@ def test_discover_all(dut): 1 (sig_real) (VHDL Only) 1 (sig_char) (VHDL Only) 9 (sig_str) (VHDL Only) - 30 (sig_rec.a, sig_rec.b[0:2][7:0]) (VPI doesn't find, added manually, except for Aldec and Icarus) - 61 (sig_cmplx[0:1].a, sig_cmplx[0:1].b[0:2][7:0]) (VPI - Aldec doesn't find, Not available in Icarus) + 30 (sig_rec.a, sig_rec.b[0:2][7:0]) (VPI doesn't find, added manually, except for Aldec) + 61 (sig_cmplx[0:1].a, sig_cmplx[0:1].b[0:2][7:0]) (VPI - Aldec doesn't find) regions: 9 (asc_gen[16:23]) 8 (asc_gen: signals) (VHPI - Riviera doesn't find, added manually) 8 (asc_gen: constant) @@ -286,7 +282,6 @@ def test_discover_all(dut): 816 (VHDL - Aldec) 780 (Verilog - Default) 649 (Verilog - Aldec) - 614 (Verilog - Icarus) """ tlog = logging.getLogger("cocotb.test") @@ -296,7 +291,7 @@ def test_discover_all(dut): # Need to clear sub_handles so won't attempt to iterate over handles like sig_rec and sig_rec_array # # DO NOT REMOVE. Aldec cannot iterate over the complex records due to bugs in the VPI interface. - if cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera","icarus")): + if cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")): if len(dut._sub_handles) != 0: dut._sub_handles = {} @@ -321,8 +316,6 @@ def test_discover_all(dut): pass_total = 854 elif cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")): pass_total = 649 - elif cocotb.SIM_NAME.lower().startswith(("icarus")): - pass_total = 614 else: pass_total = 780 @@ -341,6 +334,13 @@ def _discover(obj,indent): if total != pass_total: raise TestFailure("Expected {0} objects but found {1}".format(pass_total, total)) +def test_basic_constant_access(dut): + """Test accessing constant/parameter basic data types""" + + tlog = logging.getLogger("cocotb.test") + + yield Timer(2000) + @cocotb.test(skip=(cocotb.LANGUAGE in ["verilog"] or cocotb.SIM_NAME.lower().startswith(("riviera")))) def test_direct_constant_indexing(dut): """Test directly accessing constant/parameter data in arrays, i.e. not iterating""" @@ -432,11 +432,10 @@ def test_direct_signal_indexing(dut): _check_type(tlog, dut.sig_t6[1], NonHierarchyIndexableObject) _check_type(tlog, dut.sig_t6[0][3], ModifiableObject) _check_type(tlog, dut.sig_t6[0][3][7], ModifiableObject) - if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("icarus"))): - _check_type(tlog, dut.sig_cmplx, NonHierarchyIndexableObject) + _check_type(tlog, dut.sig_cmplx, NonHierarchyIndexableObject) # Riviera has a bug and finds dut.sig_cmplx[1], but the type returned is a vpiBitVar - if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera","icarus"))): + if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera"))): _check_type(tlog, dut.sig_cmplx[1], HierarchyObject) _check_type(tlog, dut.sig_cmplx[1].a, ModifiableObject) _check_type(tlog, dut.sig_cmplx[1].b, NonHierarchyIndexableObject) @@ -444,13 +443,12 @@ def test_direct_signal_indexing(dut): _check_type(tlog, dut.sig_cmplx[1].b[1][2], ModifiableObject) tlog.info(" dut.sig_cmplx[1].a = %d (%s)", int(dut.sig_cmplx[1].a), dut.sig_cmplx[1].a.value.binstr) - if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("icarus"))): - _check_type(tlog, dut.sig_rec, HierarchyObject) - _check_type(tlog, dut.sig_rec.a, ModifiableObject) - _check_type(tlog, dut.sig_rec.b, NonHierarchyIndexableObject) + _check_type(tlog, dut.sig_rec, HierarchyObject) + _check_type(tlog, dut.sig_rec.a, ModifiableObject) + _check_type(tlog, dut.sig_rec.b, NonHierarchyIndexableObject) # Riviera has a bug and finds dut.sig_rec.b[1], but the type returned is 0 which is unknown - if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera","icarus"))): + if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera"))): _check_type(tlog, dut.sig_rec.b[1], ModifiableObject) _check_type(tlog, dut.sig_rec.b[1][2], ModifiableObject) From 2a717c0eef7e36e292a57702c4cedf57822902e4 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Thu, 24 Mar 2016 13:33:30 -0700 Subject: [PATCH 1175/2656] Issue #393: Update VPI/VHPI/FLI to remove call to sim_to_hdl<>() --- lib/fli/FliImpl.cpp | 36 +++++++++++++++++------------------- lib/vhpi/VhpiImpl.cpp | 40 ++++++++++++++++++---------------------- lib/vpi/VpiImpl.cpp | 41 ++++++++++++++++++----------------------- 3 files changed, 53 insertions(+), 64 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index e7b312dd..82647c86 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -243,13 +243,12 @@ GpiObjHdl* FliImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) */ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) { - GpiObjHdl *parent_hdl = sim_to_hdl(parent); bool search_rgn = false; bool search_sig = false; bool search_var = false; - std::string fq_name = parent_hdl->get_fullname(); - gpi_objtype_t obj_type = parent_hdl->get_type(); + std::string fq_name = parent->get_fullname(); + gpi_objtype_t obj_type = parent->get_type(); if (fq_name == "/") { fq_name += name; @@ -262,7 +261,7 @@ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) search_sig = true; search_var = true; } else if (obj_type == GPI_STRUCTURE) { - FliValueObjHdl *fli_obj = reinterpret_cast(parent_hdl); + FliValueObjHdl *fli_obj = reinterpret_cast(parent); fq_name += "." + name; search_rgn = false; @@ -273,7 +272,7 @@ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) return NULL; } - LOG_DEBUG("Looking for child %s from %s", name.c_str(), parent_hdl->get_name_str()); + LOG_DEBUG("Looking for child %s from %s", name.c_str(), parent->get_name_str()); std::vector writable(fq_name.begin(), fq_name.end()); writable.push_back('\0'); @@ -303,12 +302,12 @@ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) mtiRegionIdT rgn; /* If not found, check to see if the name of a generate loop and create a pseudo-region */ - for (rgn = mti_FirstLowerRegion(parent_hdl->get_handle()); rgn != NULL; rgn = mti_NextRegion(rgn)) { + for (rgn = mti_FirstLowerRegion(parent->get_handle()); rgn != NULL; rgn = mti_NextRegion(rgn)) { if (acc_fetch_fulltype(rgn) == accForGenerate) { std::string rgn_name = mti_GetRegionName(static_cast(rgn)); if (rgn_name.compare(0,name.length(),name) == 0) { - FliObj *fli_obj = dynamic_cast(parent_hdl); - return create_gpi_obj_from_handle(parent_hdl->get_handle(), name, fq_name, fli_obj->get_acc_type(), fli_obj->get_acc_full_type()); + FliObj *fli_obj = dynamic_cast(parent); + return create_gpi_obj_from_handle(parent->get_handle(), name, fq_name, fli_obj->get_acc_type(), fli_obj->get_acc_full_type()); } } } @@ -327,8 +326,8 @@ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) * being equivalent to the parent handle. */ if (accFullType == accForGenerate) { - FliObj *fli_obj = dynamic_cast(parent_hdl); - return create_gpi_obj_from_handle(parent_hdl->get_handle(), name, fq_name, fli_obj->get_acc_type(), fli_obj->get_acc_full_type()); + FliObj *fli_obj = dynamic_cast(parent); + return create_gpi_obj_from_handle(parent->get_handle(), name, fq_name, fli_obj->get_acc_type(), fli_obj->get_acc_full_type()); } return create_gpi_obj_from_handle(hdl, name, fq_name, accType, accFullType); @@ -341,8 +340,7 @@ GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) */ GpiObjHdl* FliImpl::native_check_create(int32_t index, GpiObjHdl *parent) { - GpiObjHdl *parent_hdl = sim_to_hdl(parent); - gpi_objtype_t obj_type = parent_hdl->get_type(); + gpi_objtype_t obj_type = parent->get_type(); HANDLE hdl; PLI_INT32 accType; @@ -350,13 +348,13 @@ GpiObjHdl* FliImpl::native_check_create(int32_t index, GpiObjHdl *parent) char buff[14]; if (obj_type == GPI_GENARRAY) { - LOG_DEBUG("Looking for index %d from %s", index, parent_hdl->get_name_str()); + LOG_DEBUG("Looking for index %d from %s", index, parent->get_name_str()); snprintf(buff, 14, "(%d)", index); std::string idx = buff; - std::string name = parent_hdl->get_name() + idx; - std::string fq_name = parent_hdl->get_fullname() + idx; + std::string name = parent->get_name() + idx; + std::string fq_name = parent->get_fullname() + idx; std::vector writable(fq_name.begin(), fq_name.end()); writable.push_back('\0'); @@ -374,9 +372,9 @@ GpiObjHdl* FliImpl::native_check_create(int32_t index, GpiObjHdl *parent) return create_gpi_obj_from_handle(hdl, name, fq_name, accType, accFullType); } else if (obj_type == GPI_REGISTER || obj_type == GPI_ARRAY || obj_type == GPI_STRING) { - FliValueObjHdl *fli_obj = reinterpret_cast(parent_hdl); + FliValueObjHdl *fli_obj = reinterpret_cast(parent); - LOG_DEBUG("Looking for index %u from %s", index, parent_hdl->get_name_str()); + LOG_DEBUG("Looking for index %u from %s", index, parent->get_name_str()); if ((hdl = fli_obj->get_sub_hdl(index)) == NULL) { LOG_DEBUG("Didn't find the index %d", index); @@ -386,8 +384,8 @@ GpiObjHdl* FliImpl::native_check_create(int32_t index, GpiObjHdl *parent) snprintf(buff, 14, "(%d)", index); std::string idx = buff; - std::string name = parent_hdl->get_name() + idx; - std::string fq_name = parent_hdl->get_fullname() + idx; + std::string name = parent->get_name() + idx; + std::string fq_name = parent->get_fullname() + idx; if (!(fli_obj->is_var())) { accType = acc_fetch_type(hdl); diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index c99606ad..1cfc4420 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -412,13 +412,11 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, GpiObjHdl *VhpiImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) { - GpiObjHdl *parent_hdl = sim_to_hdl(parent); - LOG_DEBUG("Trying to convert raw to VHPI handle"); vhpiHandleT new_hdl = (vhpiHandleT)raw_hdl; - std::string fq_name = parent_hdl->get_fullname(); + std::string fq_name = parent->get_fullname(); const char *c_name = vhpi_get_str(vhpiCaseNameP, new_hdl); if (!c_name) { LOG_DEBUG("Unable to query name of passed in handle"); @@ -445,11 +443,10 @@ GpiObjHdl *VhpiImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) { - GpiObjHdl *parent_hdl = sim_to_hdl(parent); - vhpiHandleT vhpi_hdl = parent_hdl->get_handle(); + vhpiHandleT vhpi_hdl = parent->get_handle(); vhpiHandleT new_hdl; - std::string fq_name = parent_hdl->get_fullname(); + std::string fq_name = parent->get_fullname(); if (fq_name == ":") { fq_name += name; } else { @@ -460,7 +457,7 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) new_hdl = vhpi_handle_by_name(&writable[0], NULL); - if (new_hdl == NULL && parent_hdl->get_type() == GPI_STRUCTURE) { + if (new_hdl == NULL && parent->get_type() == GPI_STRUCTURE) { /* vhpi_handle_by_name() doesn't always work for records, specificaly records in generics */ vhpiHandleT iter = vhpi_iterator(vhpiSelectedNames, vhpi_hdl); if (iter != NULL) { @@ -526,19 +523,18 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) GpiObjHdl *VhpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) { - GpiObjHdl *parent_hdl = sim_to_hdl(parent); - vhpiHandleT vhpi_hdl = parent_hdl->get_handle(); - std::string name = parent_hdl->get_name(); - std::string fq_name = parent_hdl->get_fullname(); + vhpiHandleT vhpi_hdl = parent->get_handle(); + std::string name = parent->get_name(); + std::string fq_name = parent->get_fullname(); vhpiHandleT new_hdl = NULL; char buff[14]; // needs to be large enough to hold -2^31 to 2^31-1 in string form ('(''-'10+'')'\0') - gpi_objtype_t obj_type = parent_hdl->get_type(); + gpi_objtype_t obj_type = parent->get_type(); if (obj_type == GPI_GENARRAY) { LOG_DEBUG("Native check create for index %d of parent %s (pseudo-region)", index, - parent_hdl->get_name_str()); + parent->get_name_str()); snprintf(buff, sizeof(buff), "%d", index); @@ -553,7 +549,7 @@ GpiObjHdl *VhpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) } else if (obj_type == GPI_REGISTER || obj_type == GPI_ARRAY || obj_type == GPI_STRING) { LOG_DEBUG("Native check create for index %d of parent %s (%s)", index, - parent_hdl->get_fullname_str(), + parent->get_fullname_str(), vhpi_get_str(vhpiKindStrP, vhpi_hdl)); snprintf(buff, sizeof(buff), "(%d)", index); @@ -574,7 +570,7 @@ GpiObjHdl *VhpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) } if (base_hdl == NULL) { - LOG_ERROR("Unable to get the vhpiBaseType of %s", parent_hdl->get_fullname_str()); + LOG_ERROR("Unable to get the vhpiBaseType of %s", parent->get_fullname_str()); return NULL; } @@ -594,8 +590,8 @@ GpiObjHdl *VhpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) * parent->get_name(): sig_name(x)(y)... where x,y,... are the indices to a multi-dimensional array. * pseudo_idx: (x)(y)... */ - if (hdl_name.length() < parent_hdl->get_name().length()) { - std::string pseudo_idx = parent_hdl->get_name().substr(hdl_name.length()); + if (hdl_name.length() < parent->get_name().length()) { + std::string pseudo_idx = parent->get_name().substr(hdl_name.length()); while (pseudo_idx.length() > 0) { std::size_t found = pseudo_idx.find_first_of(")"); @@ -694,7 +690,7 @@ GpiObjHdl *VhpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) constraints.pop_back(); } } else { - LOG_ERROR("Unable to access all constraints for %s", parent_hdl->get_fullname_str()); + LOG_ERROR("Unable to access all constraints for %s", parent->get_fullname_str()); return NULL; } @@ -702,8 +698,8 @@ GpiObjHdl *VhpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) new_hdl = vhpi_hdl; // Set to the parent handle to create the pseudo-handle } } else { - int left = parent_hdl->get_range_left(); - int right = parent_hdl->get_range_right(); + int left = parent->get_range_left(); + int right = parent->get_range_right(); if (left > right) { idx = left - index; @@ -736,7 +732,7 @@ GpiObjHdl *VhpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) } } } else { - LOG_ERROR("VHPI: Parent of type %s must be of type GPI_GENARRAY, GPI_REGISTER, GPI_ARRAY, or GPI_STRING to have an index.", parent_hdl->get_type_str()); + LOG_ERROR("VHPI: Parent of type %s must be of type GPI_GENARRAY, GPI_REGISTER, GPI_ARRAY, or GPI_STRING to have an index.", parent->get_type_str()); return NULL; } @@ -750,7 +746,7 @@ GpiObjHdl *VhpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) if (new_obj == NULL) { vhpi_release_handle(new_hdl); LOG_DEBUG("Could not fetch object below entity (%s) at index (%d)", - parent_hdl->get_name_str(), index); + parent->get_name_str(), index); return NULL; } diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index aabf150d..18520e55 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -212,8 +212,6 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, GpiObjHdl* VpiImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) { - GpiObjHdl *parent_hdl = sim_to_hdl(parent); - LOG_DEBUG("Trying to convert raw to VPI handle"); vpiHandle new_hdl = (vpiHandle)raw_hdl; @@ -225,7 +223,7 @@ GpiObjHdl* VpiImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) } std::string name = c_name; - std::string fq_name = parent_hdl->get_fullname() + "." + name; + std::string fq_name = parent->get_fullname() + "." + name; GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { @@ -238,10 +236,8 @@ GpiObjHdl* VpiImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) { - GpiObjHdl *parent_hdl = sim_to_hdl(parent); - vpiHandle new_hdl; - std::string fq_name = parent_hdl->get_fullname() + "." + name; + std::string fq_name = parent->get_fullname() + "." + name; std::vector writable(fq_name.begin(), fq_name.end()); writable.push_back('\0'); @@ -264,7 +260,7 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) if (vpi_get(vpiType, new_hdl) == vpiGenScopeArray) { vpi_free_object(new_hdl); - new_hdl = parent_hdl->get_handle(); + new_hdl = parent->get_handle(); } @@ -279,23 +275,22 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) GpiObjHdl* VpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) { - GpiObjHdl *parent_hdl = sim_to_hdl(parent); - vpiHandle vpi_hdl = parent_hdl->get_handle(); + vpiHandle vpi_hdl = parent->get_handle(); vpiHandle new_hdl = NULL; char buff[14]; // needs to be large enough to hold -2^31 to 2^31-1 in string form ('['+'-'10+']'+'\0') - gpi_objtype_t obj_type = parent_hdl->get_type(); + gpi_objtype_t obj_type = parent->get_type(); if (obj_type == GPI_GENARRAY) { snprintf(buff, 14, "[%d]", index); LOG_DEBUG("Native check create for index %d of parent %s (pseudo-region)", index, - parent_hdl->get_name_str()); + parent->get_name_str()); std::string idx = buff; - std::string hdl_name = parent_hdl->get_fullname() + idx; + std::string hdl_name = parent->get_fullname() + idx; std::vector writable(hdl_name.begin(), hdl_name.end()); writable.push_back('\0'); @@ -313,8 +308,8 @@ GpiObjHdl* VpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) * Questa only works when both indicies are provided, i.e. will need a pseudo-handle to behave like the first index. */ if (new_hdl == NULL) { - int left = parent_hdl->get_range_left(); - int right = parent_hdl->get_range_right(); + int left = parent->get_range_left(); + int right = parent->get_range_right(); bool ascending = (left < right); LOG_DEBUG("Unable to find handle through vpi_handle_by_index(), attempting second method"); @@ -326,7 +321,7 @@ GpiObjHdl* VpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) } /* Get the number of constraints to determine if the index will result in a pseudo-handle or should be found */ - vpiHandle p_hdl = parent_hdl->get_handle(); + vpiHandle p_hdl = parent->get_handle(); vpiHandle it = vpi_iterate(vpiRange, p_hdl); int constraint_cnt = 0; if (it != NULL) { @@ -340,8 +335,8 @@ GpiObjHdl* VpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) std::string act_hdl_name = vpi_get_str(vpiName, p_hdl); /* Removing the act_hdl_name from the parent->get_name() will leave the psuedo-indices */ - if (act_hdl_name.length() < parent_hdl->get_name().length()) { - std::string idx_str = parent_hdl->get_name().substr(act_hdl_name.length()); + if (act_hdl_name.length() < parent->get_name().length()) { + std::string idx_str = parent->get_name().substr(act_hdl_name.length()); while (idx_str.length() > 0) { std::size_t found = idx_str.find_first_of("]"); @@ -358,7 +353,7 @@ GpiObjHdl* VpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) snprintf(buff, 14, "[%d]", index); std::string idx = buff; - std::string hdl_name = parent_hdl->get_fullname() + idx; + std::string hdl_name = parent->get_fullname() + idx; std::vector writable(hdl_name.begin(), hdl_name.end()); writable.push_back('\0'); @@ -371,26 +366,26 @@ GpiObjHdl* VpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) } } } else { - LOG_ERROR("VPI: Parent of type %s must be of type GPI_GENARRAY, GPI_REGISTER, GPI_ARRAY, or GPI_STRING to have an index.", parent_hdl->get_type_str()); + LOG_ERROR("VPI: Parent of type %s must be of type GPI_GENARRAY, GPI_REGISTER, GPI_ARRAY, or GPI_STRING to have an index.", parent->get_type_str()); return NULL; } if (new_hdl == NULL) { - LOG_DEBUG("Unable to vpi_get_handle_by_index %s[%d]", parent_hdl->get_name_str(), index); + LOG_DEBUG("Unable to vpi_get_handle_by_index %s[%d]", parent->get_name_str(), index); return NULL; } snprintf(buff, 14, "[%d]", index); std::string idx = buff; - std::string name = parent_hdl->get_name()+idx; - std::string fq_name = parent_hdl->get_fullname()+idx; + std::string name = parent->get_name()+idx; + std::string fq_name = parent->get_fullname()+idx; GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { vpi_free_object(new_hdl); LOG_DEBUG("Unable to fetch object below entity (%s) at index (%d)", - parent_hdl->get_name_str(), index); + parent->get_name_str(), index); return NULL; } return new_obj; From d68374c86446a4b6b52e11a002776e06dc0b76cd Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 28 Mar 2016 14:24:28 -0700 Subject: [PATCH 1176/2656] Issue #393: Fix an issue with FLI/VPI/VHPI not providing the correct name for structures --- lib/fli/FliImpl.cpp | 3 +++ lib/vhpi/VhpiCbHdl.cpp | 11 ++++++++++- lib/vpi/VpiCbHdl.cpp | 12 +++++++++++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 82647c86..58539ab8 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -831,6 +831,9 @@ GpiIterator::Status FliIterator::next_handle(std::string &name, GpiObjHdl **hdl, if (found != std::string::npos) { fq_name += name.substr(found); + if (obj_type != GPI_GENARRAY) { + name = name.substr(found+1); + } } else { LOG_WARN("Unhandled Sub-Element Format - %s", name.c_str()); fq_name += "/" + name; diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 5738b732..37d5a2ac 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -1061,14 +1061,23 @@ GpiIterator::Status VhpiIterator::next_handle(std::string &name, fq_name += name; } else if (obj_type == GPI_GENARRAY) { std::size_t found = name.rfind(GEN_IDX_SEP_LHS); - + if (found != std::string::npos) { fq_name += name.substr(found); } else { LOG_WARN("Unhandled Sub-Element Format - %s", name.c_str()); fq_name += "." + name; } + } else if (obj_type == GPI_STRUCTURE) { + std::size_t found = name.rfind("."); + if (found != std::string::npos) { + fq_name += name.substr(found); + name = name.substr(found+1); + } else { + LOG_WARN("Unhandled Sub-Element Format - %s", name.c_str()); + fq_name += "." + name; + } } else { fq_name += "." + name; } diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 6b72e1ee..fff06355 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -835,9 +835,19 @@ GpiIterator::Status VpiIterator::next_handle(std::string &name, GpiObjHdl **hdl, if (obj_type == GPI_GENARRAY) { std::size_t found = name.rfind("["); - + + if (found != std::string::npos) { + fq_name += name.substr(found); + } else { + LOG_WARN("Unhandled Sub-Element Format - %s", name.c_str()); + fq_name += "." + name; + } + } else if (obj_type == GPI_STRUCTURE) { + std::size_t found = name.rfind("."); + if (found != std::string::npos) { fq_name += name.substr(found); + name = name.substr(found+1); } else { LOG_WARN("Unhandled Sub-Element Format - %s", name.c_str()); fq_name += "." + name; From 826cf6572444031b205e6c8b3ee3361d3922badb Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 28 Mar 2016 15:20:25 -0700 Subject: [PATCH 1177/2656] Issue #417: Standardize use of __repr__() and __str__() in handle.py --- cocotb/handle.py | 127 ++++++++++++------ tests/test_cases/test_array/test_array.py | 64 ++++----- .../test_discovery/test_discovery.py | 12 +- .../test_vhdl_access/test_vhdl_access.py | 6 +- 4 files changed, 118 insertions(+), 91 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 06403cb8..79ed677a 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -73,10 +73,11 @@ class SimHandleBase(object): "name" : "_name", } - def __init__(self, handle): + def __init__(self, handle, path): """ Args: handle (integer) : the GPI handle to the simulator object + path (string) : path to this handle, None if root """ self._handle = handle self._len = None @@ -86,6 +87,7 @@ def __init__(self, handle): self._name = simulator.get_name_string(self._handle) self._type = simulator.get_type_string(self._handle) self._fullname = self._name + "(%s)" % self._type + self._path = self._name if path is None else path self._log = SimLog("cocotb.%s" % self._name) self._log.debug("Created") @@ -112,10 +114,10 @@ def __ne__(self, other): return not self.__eq__(other) def __repr__(self): - return self._fullname + return type(self).__name__ + "(" + self._path + ")" def __str__(self): - return "%s @0x%x" % (self._name, self._handle) + return self._path def __setattr__(self, name, value): if name in self._compat_mapping: @@ -139,8 +141,8 @@ class RegionObject(SimHandleBase): """ Region objects don't have values, they are effectively scopes or namespaces """ - def __init__(self, handle): - SimHandleBase.__init__(self, handle) + def __init__(self, handle, path): + SimHandleBase.__init__(self, handle, path) self._discovered = False def __iter__(self): @@ -181,12 +183,12 @@ def _discover_all(self): break name = simulator.get_name_string(thing) try: - hdl = SimHandle(thing) + hdl = SimHandle(thing, self._child_path(name)) except TestError as e: self._log.debug("%s" % e) continue - key = self._sub_handle_key(hdl) + key = self._sub_handle_key(name) if not key is None: self._sub_handles[key] = hdl @@ -196,11 +198,17 @@ def _discover_all(self): self._discovered = True - def _sub_handle_key(self, hdl): + def _child_path(self, name): + """ + Returns a string of the path of the child SimHandle for a given name + """ + return self._path + "." + name + + def _sub_handle_key(self, name): """ Translates the handle name to a key to use in _sub_handles dictionary. """ - return hdl._name.split(".")[-1] + return name.split(".")[-1] def _getAttributeNames(self): """Permits IPython tab completion to work""" @@ -248,7 +256,7 @@ def __getattr__(self, name): if name in self._sub_handles: return self._sub_handles[name] raise AttributeError("%s contains no object named %s" % (self._name, name)) - self._sub_handles[name] = SimHandle(new_handle) + self._sub_handles[name] = SimHandle(new_handle, self._child_path(name)) return self._sub_handles[name] def __hasattr__(self, name): @@ -268,7 +276,7 @@ def __hasattr__(self, name): new_handle = simulator.get_handle_by_name(self._handle, name) if new_handle: - self._sub_handles[name] = SimHandle(new_handle) + self._sub_handles[name] = SimHandle(new_handle, self._child_path(name)) else: self._invalid_sub_handles[name] = None return new_handle @@ -279,7 +287,7 @@ class HierarchyArrayObject(RegionObject): Hierarchy Array are containers of Hierarchy Objects """ - def _sub_handle_key(self, hdl): + def _sub_handle_key(self, name): """ Translates the handle name to a key to use in _sub_handles dictionary. """ @@ -289,15 +297,16 @@ def _sub_handle_key(self, hdl): # VHPI(ALDEC): _name__X where X is the index # VPI: _name[X] where X is the index import re - result = re.match("{}__(?P\d+)$".format(self._name), hdl._name) + result = re.match("{}__(?P\d+)$".format(self._name), name) if not result: - result = re.match("{}\((?P\d+)\)$".format(self._name), hdl._name) + result = re.match("{}\((?P\d+)\)$".format(self._name), name) if not result: - result = re.match("{}\[(?P\d+)\]$".format(self._name), hdl._name) + result = re.match("{}\[(?P\d+)\]$".format(self._name), name) if result: return int(result.group("index")) else: + self._log.error("Unable to match an index pattern: %s", name); return None def __len__(self): @@ -317,9 +326,17 @@ def __getitem__(self, index): new_handle = simulator.get_handle_by_index(self._handle, index) if not new_handle: raise IndexError("%s contains no object at index %d" % (self._name, index)) - self._sub_handles[index] = SimHandle(new_handle) + path = self._path + "[" + str(index) + "]" + self._sub_handles[index] = SimHandle(new_handle, path) return self._sub_handles[index] + def _child_path(self, name): + """ + Returns a string of the path of the child SimHandle for a given name + """ + index = self._sub_handle_key(name) + return self._path + "[" + str(index) + "]" + def __setitem__(self, index, value): """Provide transparent assignment to indexed array handles""" raise TypeError("Not permissible to set %s at index %d" % (self._name, index)) @@ -331,8 +348,8 @@ class NonHierarchyObject(SimHandleBase): Common base class for all non-hierarchy objects """ - def __init__(self, handle): - SimHandleBase.__init__(self, handle) + def __init__(self, handle, path): + SimHandleBase.__init__(self, handle, path) def __iter__(self): return iter(()) @@ -382,8 +399,8 @@ class ConstantObject(NonHierarchyObject): We can also cache the value since it is elaboration time fixed and won't change within a simulation """ - def __init__(self, handle, handle_type): - NonHierarchyObject.__init__(self, handle) + def __init__(self, handle, path, handle_type): + NonHierarchyObject.__init__(self, handle, path) if handle_type in [simulator.INTEGER, simulator.ENUM]: self._value = simulator.get_signal_val_long(self._handle) elif handle_type == simulator.REAL: @@ -404,19 +421,19 @@ def __int__(self): def __float__(self): return float(self.value) - def __repr__(self): - return str(self.value) - def _getvalue(self): return self._value + def __str__(self): + return str(self.value) + class NonHierarchyIndexableObject(NonHierarchyObject): - def __init__(self, handle): + def __init__(self, handle, path): """ Args: _handle [integer] : fli/vpi/vhpi handle to the simulator object """ - NonHierarchyObject.__init__(self, handle) + NonHierarchyObject.__init__(self, handle, path) self._range = simulator.get_range(self._handle) def __setitem__(self, index, value): @@ -433,7 +450,8 @@ def __getitem__(self, index): new_handle = simulator.get_handle_by_index(self._handle, index) if not new_handle: raise IndexError("%s contains no object at index %d" % (self._fullname, index)) - self._sub_handles[index] = SimHandle(new_handle) + path = self._path + "[" + str(index) + "]" + self._sub_handles[index] = SimHandle(new_handle, path) return self._sub_handles[index] def __iter__(self): @@ -460,12 +478,12 @@ def _range_iter(self, left, right): class NonConstantObject(NonHierarchyIndexableObject): - def __init__(self, handle): + def __init__(self, handle, path): """ Args: _handle [integer] : vpi/vhpi handle to the simulator object """ - NonHierarchyIndexableObject.__init__(self, handle) + NonHierarchyIndexableObject.__init__(self, handle, path) self._r_edge = _RisingEdge(self) self._f_edge = _FallingEdge(self) self._e_edge = _Edge(self) @@ -476,6 +494,7 @@ def drivers(self): """ iterator = simulator.iterate(self._handle, simulator.DRIVERS) while True: + # Path is left as the default None since handles are not derived from the hierarchy yield SimHandle(simulator.next(iterator)) def loads(self): @@ -484,6 +503,7 @@ def loads(self): """ iterator = simulator.iterate(self._handle, simulator.LOADS) while True: + # Path is left as the default None since handles are not derived from the hierarchy yield SimHandle(simulator.next(iterator)) @@ -541,8 +561,8 @@ def _setcachedvalue(self, value): def __int__(self): return int(self.value) - def __repr__(self): - return repr(int(self)) + def __str__(self): + return str(self.value) class RealObject(ModifiableObject): """ @@ -573,7 +593,38 @@ def _getvalue(self): return simulator.get_signal_val_real(self._handle) def __float__(self): - return self._getvalue() + return float(self.value) + +class EnumObject(ModifiableObject): + """ + Specific object handle for ENUM signals and variables + """ + + def setimmediatevalue(self, value): + """ + Set the value of the underlying simulation object to value. + + Args: + value (int) + The value to drive onto the simulator object + + Raises: + TypeError + + This operation will fail unless the handle refers to a modifiable + object eg net, signal or variable. + """ + if isinstance(value, BinaryValue): + value = int(value) + elif not isinstance(value, get_python_integer_types()): + self._log.critical("Unsupported type for integer value assignment: %s (%s)" % (type(value), repr(value))) + raise TypeError("Unable to set simulator value with type %s" % (type(value))) + + simulator.set_signal_val_long(self._handle, value) + + def _getvalue(self): + return simulator.get_signal_val_long(self._handle) + class IntegerObject(ModifiableObject): """ @@ -605,9 +656,6 @@ def setimmediatevalue(self, value): def _getvalue(self): return simulator.get_signal_val_long(self._handle) - def __int__(self): - return self._getvalue() - class StringObject(ModifiableObject): """ Specific object handle for String variables @@ -636,12 +684,9 @@ def setimmediatevalue(self, value): def _getvalue(self): return simulator.get_signal_val_str(self._handle) - def __repr__(self): - return str(self.value) - _handle2obj = {} -def SimHandle(handle): +def SimHandle(handle, path=None): """ Factory function to create the correct type of SimHandle object """ @@ -652,7 +697,7 @@ def SimHandle(handle): simulator.NETARRAY: NonHierarchyIndexableObject, simulator.REAL: RealObject, simulator.INTEGER: IntegerObject, - simulator.ENUM: ModifiableObject, + simulator.ENUM: EnumObject, simulator.STRING: StringObject, simulator.GENARRAY: HierarchyArrayObject, } @@ -672,12 +717,12 @@ def SimHandle(handle): simulator.STRUCTURE, simulator.NETARRAY, simulator.GENARRAY]: - obj = ConstantObject(handle, t) + obj = ConstantObject(handle, path, t) _handle2obj[handle] = obj return obj if t not in _type2cls: raise TestError("Couldn't find a matching object for GPI type %d" % t) - obj = _type2cls[t](handle) + obj = _type2cls[t](handle, path) _handle2obj[handle] = obj return obj diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py index 57b217f7..892bd824 100644 --- a/tests/test_cases/test_array/test_array.py +++ b/tests/test_cases/test_array/test_array.py @@ -12,33 +12,33 @@ def _check_type(tlog, hdl, expected): if not isinstance(hdl, expected): - raise TestFailure(">{0} ({1})< should be >{2}<".format(hdl._fullname, type(hdl) ,expected)) + raise TestFailure(">{0!r} ({1})< should be >{2}<".format(hdl, hdl._type ,expected)) else: - tlog.info(" Found %s (%s) with length=%d", hdl._fullname, type(hdl), len(hdl)) + tlog.info(" Found %r (%s) with length=%d", hdl, hdl._type, len(hdl)) def _check_int(tlog, hdl, expected): if int(hdl) != expected: - raise TestFailure("{2}: Expected >{0}< but got >{1}<".format(expected, int(hdl), hdl._fullname)) + raise TestFailure("{2!r}: Expected >{0}< but got >{1}<".format(expected, int(hdl), hdl)) else: - tlog.info(" Found {0} ({1}) with value={2}".format(hdl._fullname, type(hdl), int(hdl))) + tlog.info(" Found {0!r} ({1}) with value={2}".format(hdl, hdl._type, int(hdl))) def _check_logic(tlog, hdl, expected): if int(hdl) != expected: - raise TestFailure("{2}: Expected >0x{0:X}< but got >0x{1:X}<".format(expected, int(hdl), hdl._fullname)) + raise TestFailure("{2!r}: Expected >0x{0:X}< but got >0x{1:X}<".format(expected, int(hdl), hdl)) else: - tlog.info(" Found {0} ({1}) with value=0x{2:X}".format(hdl._fullname, type(hdl), int(hdl))) + tlog.info(" Found {0!r} ({1}) with value=0x{2:X}".format(hdl, hdl._type, int(hdl))) def _check_str(tlog, hdl, expected): - if repr(hdl) != expected: - raise TestFailure("{2}: Expected >{0}< but got >{1}<".format(expected, repr(hdl), hdl._fullname)) + if str(hdl) != expected: + raise TestFailure("{2!r}: Expected >{0}< but got >{1}<".format(expected, str(hdl), hdl)) else: - tlog.info(" Found {0} ({1}) with value={2}".format(hdl._fullname, type(hdl), repr(hdl))) + tlog.info(" Found {0!r} ({1}) with value={2}".format(hdl, hdl._type, str(hdl))) def _check_real(tlog, hdl, expected): if float(hdl) != expected: - raise TestFailure("{2}: Expected >{0}< but got >{1}<".format(expected, float(hdl), hdl._fullname)) + raise TestFailure("{2!r}: Expected >{0}< but got >{1}<".format(expected, float(hdl), hdl)) else: - tlog.info(" Found {0} ({1}) with value={2}".format(hdl._fullname, type(hdl), float(hdl))) + tlog.info(" Found {0!r} ({1}) with value={2}".format(hdl, hdl._type, float(hdl))) @cocotb.test() def test_read_write(dut): @@ -151,7 +151,7 @@ def test_read_write(dut): _check_logic(tlog, dut.port_cmplx_out[1].b[1], 0xEF) _check_logic(tlog, dut.port_cmplx_out[1].b[2], 0x55) - tlog.info("Writing a few signal sub-idices!!!") + tlog.info("Writing a few signal sub-indices!!!") dut.sig_logic_vec[2] = 0 if cocotb.LANGUAGE in ["vhdl"] or not cocotb.SIM_NAME.lower().startswith(("ncsim")): dut.sig_t6[1][3][2] = 1 @@ -187,13 +187,13 @@ def test_gen_loop(dut): desc_gen = dut.desc_gen if not isinstance(dut.asc_gen, HierarchyArrayObject): - raise TestFailure("Generate Loop parent >{}< should be HierarchyArrayObject".format(dut.asc_gen)) + raise TestFailure("Generate Loop parent >{!r}< should be HierarchyArrayObject".format(dut.asc_gen)) if not isinstance(desc_gen, HierarchyArrayObject): - raise TestFailure("Generate Loop parent >{}< should be HierarchyArrayObject".format(desc_gen)) + raise TestFailure("Generate Loop parent >{!r}< should be HierarchyArrayObject".format(desc_gen)) if not isinstance(asc_gen_20, HierarchyObject): - raise TestFailure("Generate Loop child >{}< should be HierarchyObject".format(asc_gen_20)) + raise TestFailure("Generate Loop child >{!r}< should be HierarchyObject".format(asc_gen_20)) tlog.info("Direct access found %s", asc_gen_20) tlog.info("Direct access found %s", desc_gen) @@ -324,23 +324,16 @@ def _discover(obj,indent): new_indent = indent+"---" for thing in obj: count += 1 - tlog.info("%sFound %s (%s)", indent, thing._fullname, type(thing)) + tlog.info("%sFound %r (%s)", indent, thing, thing._type) count += _discover(thing,new_indent) return count - tlog.info("Iterating over %s (%s)", dut._fullname, type(dut)) + tlog.info("Iterating over %r (%s)", dut, dut._type) total = _discover(dut, "") tlog.info("Found a total of %d things", total) if total != pass_total: raise TestFailure("Expected {0} objects but found {1}".format(pass_total, total)) -def test_basic_constant_access(dut): - """Test accessing constant/parameter basic data types""" - - tlog = logging.getLogger("cocotb.test") - - yield Timer(2000) - @cocotb.test(skip=(cocotb.LANGUAGE in ["verilog"] or cocotb.SIM_NAME.lower().startswith(("riviera")))) def test_direct_constant_indexing(dut): """Test directly accessing constant/parameter data in arrays, i.e. not iterating""" @@ -395,20 +388,20 @@ def test_direct_signal_indexing(dut): tlog.info("Checking bit mapping from input to generate loops.") if int(dut.desc_gen[2].sig) != 1: - raise TestFailure("Expected dut.desc_gen[2].sig to be a 1 but got {}".format(int(dut.desc_gen[2].sig))) + raise TestFailure("Expected {0!r} to be a 1 but got {1}".format(dut.desc_gen[2].sig,int(dut.desc_gen[2].sig))) else: - tlog.info(" dut.desc_gen[2].sig = %d", int(dut.desc_gen[2].sig)) + tlog.info(" %r = %d", dut.desc_gen[2].sig, int(dut.desc_gen[2].sig)) if int(dut.asc_gen[18].sig) != 1: - raise TestFailure("Expected dut.asc_gen[18].sig to be a 1 but got {}".format(int(dut.asc_gen[18].sig))) + raise TestFailure("Expected {0!r} to be a 1 but got {1}".format(dut.asc_gen[18].sig,int(dut.asc_gen[18].sig))) else: - tlog.info(" dut.asc_gen[18].sig = %d", int(dut.asc_gen[18].sig)) + tlog.info(" %r = %d", dut.asc_gen[18].sig, int(dut.asc_gen[18].sig)) tlog.info("Checking indexing of data with offset index.") if int(dut.port_ofst_out) != 64: - raise TestFailure("Expected dut.port_ofst_out to be a 64 but got {}".format(int(dut.port_ofst_out))) + raise TestFailure("Expected {0!r} to be a 64 but got {0}".format(dut.port_ofst_out, int(dut.port_ofst_out))) else: - tlog.info(" dut.port_ofst_out = %d (%s)", int(dut.port_ofst_out), dut.port_ofst_out.value.binstr) + tlog.info(" %r = %d (%s)", dut.port_ofst_out, int(dut.port_ofst_out), dut.port_ofst_out.value.binstr) tlog.info("Checking Types of complex array structures in signals.") _check_type(tlog, dut.sig_desc[20], ModifiableObject) @@ -441,7 +434,6 @@ def test_direct_signal_indexing(dut): _check_type(tlog, dut.sig_cmplx[1].b, NonHierarchyIndexableObject) _check_type(tlog, dut.sig_cmplx[1].b[1], ModifiableObject) _check_type(tlog, dut.sig_cmplx[1].b[1][2], ModifiableObject) - tlog.info(" dut.sig_cmplx[1].a = %d (%s)", int(dut.sig_cmplx[1].a), dut.sig_cmplx[1].a.value.binstr) _check_type(tlog, dut.sig_rec, HierarchyObject) _check_type(tlog, dut.sig_rec.a, ModifiableObject) @@ -451,13 +443,3 @@ def test_direct_signal_indexing(dut): if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera"))): _check_type(tlog, dut.sig_rec.b[1], ModifiableObject) _check_type(tlog, dut.sig_rec.b[1][2], ModifiableObject) - -@cocotb.test() -def delay_test(dut): - """Waits for time then ends""" - - yield Timer(1000) - - #tlog = logging.getLogger("cocotb.test") - #for thing in dut: - # tlog.info("Found %s (%s)", thing._fullname, type(thing)) diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 0cbe8357..79845157 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -188,7 +188,7 @@ def access_string(dut): tlog = logging.getLogger("cocotb.test") yield Timer(10) constant_string = dut.isample_module1.EXAMPLE_STRING; - tlog.info("%s is %s" % (constant_string, repr(constant_string))) + tlog.info("%r is %s" % (constant_string, str(constant_string))) if not isinstance(constant_string, ConstantObject): raise TestFailure("EXAMPLE_STRING was not constant") if constant_string != "TESTING": @@ -201,12 +201,12 @@ def access_string(dut): varible_string = dut.stream_out_string if varible_string != '': - raise TestFailure("%s not \'\'" % varible_string) + raise TestFailure("%r not \'\'" % varible_string) yield Timer(10) if varible_string != test_string: - raise TestFailure("%s %s != '%s'" % (varible_string, repr(varible_string), test_string)) + raise TestFailure("%r %s != '%s'" % (varible_string, str(varible_string), test_string)) test_string = "longer_than_the_array" tlog.info("Test writing over size with '%s'" % test_string) @@ -219,7 +219,7 @@ def access_string(dut): test_string = test_string[:len(varible_string)] if varible_string != test_string: - raise TestFailure("%s %s != '%s'" % (varible_string, repr(varible_string), test_string)) + raise TestFailure("%r %s != '%s'" % (varible_string, str(varible_string), test_string)) tlog.info("Test read access to a string character") @@ -247,10 +247,10 @@ def access_string(dut): test_string = test_string.upper() - result = repr(varible_string); + result = str(varible_string); tlog.info("After setting bytes of string value is %s" % result) if varible_string != test_string: - raise TestFailure("%s %s != '%s'" % (varible_string, result, test_string)) + raise TestFailure("%r %s != '%s'" % (varible_string, result, test_string)) @cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) def access_constant_boolean(dut): diff --git a/tests/test_cases/test_vhdl_access/test_vhdl_access.py b/tests/test_cases/test_vhdl_access/test_vhdl_access.py index 73d1c404..f3ec610d 100644 --- a/tests/test_cases/test_vhdl_access/test_vhdl_access.py +++ b/tests/test_cases/test_vhdl_access/test_vhdl_access.py @@ -26,7 +26,7 @@ import logging import cocotb -from cocotb.handle import HierarchyObject, ModifiableObject, RealObject, IntegerObject, ConstantObject +from cocotb.handle import HierarchyObject, ModifiableObject, RealObject, IntegerObject, ConstantObject, EnumObject from cocotb.triggers import Timer from cocotb.result import TestError, TestFailure @@ -38,8 +38,8 @@ def check_enum_object(dut): TODO: Implement an EnumObject class and detect valid string mappings """ yield Timer(100) - if not isinstance(dut.inst_ram_ctrl.write_ram_fsm, ModifiableObject): - raise TestFailure("Expected the FSM enum to be an ModifiableObject") + if not isinstance(dut.inst_ram_ctrl.write_ram_fsm, EnumObject): + raise TestFailure("Expected the FSM enum to be an EnumObject") @cocotb.test() def check_objects(dut): From 08e4bcd4a07cabe092624b2335145d5b007e5b4e Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 29 Mar 2016 12:07:37 -0700 Subject: [PATCH 1178/2656] Issue #419: Update how PYTHON_DYN_LIB is set --- makefiles/Makefile.pylib.Darwin | 2 +- makefiles/Makefile.pylib.Linux | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/makefiles/Makefile.pylib.Darwin b/makefiles/Makefile.pylib.Darwin index 02f70689..cf746797 100644 --- a/makefiles/Makefile.pylib.Darwin +++ b/makefiles/Makefile.pylib.Darwin @@ -63,4 +63,4 @@ endif PYLIBS = $(shell $(PY_CFG_EXE) --libs) PYTHON_INCLUDEDIR := $(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())') -PYTHON_DYN_LIB = libpython$(PYTHON_VERSION).dylib +PYTHON_DYN_LIB:=$(shell python -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("LDLIBRARY"))') diff --git a/makefiles/Makefile.pylib.Linux b/makefiles/Makefile.pylib.Linux index a064f658..711690f1 100644 --- a/makefiles/Makefile.pylib.Linux +++ b/makefiles/Makefile.pylib.Linux @@ -32,7 +32,6 @@ ifneq ($(COCOTB_PYTHON_DEBUG),) DBG_PYTHON_VERSION=$(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') PYTHON_BIN=python$(DBG_PYTHON_VERSION)-dbg -DYN_SUFFIX="_d" else PYTHON_BIN=python endif @@ -71,4 +70,4 @@ endif PYLIBS = $(shell $(PY_CFG_EXE) --libs) PYTHON_INCLUDEDIR := $(shell $(PYTHON_BIN) -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())') -PYTHON_DYN_LIB = libpython$(PYTHON_VERSION)$(DYN_SUFFIX).so +PYTHON_DYN_LIB:=$(shell $(PYTHON_BIN) -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("LDLIBRARY"))') From 08f827764b795701cbc5ea38abfa99778b2ce331 Mon Sep 17 00:00:00 2001 From: Martin Zabel Date: Wed, 30 Mar 2016 18:32:07 +0200 Subject: [PATCH 1179/2656] Simple D-FF example demonstrating drivers and monitors. --- examples/dff/hdl/dff.v | 17 ++++++ examples/dff/hdl/dff.vhdl | 21 +++++++ examples/dff/tests/Makefile | 30 ++++++++++ examples/dff/tests/dff_cocotb.py | 96 ++++++++++++++++++++++++++++++++ 4 files changed, 164 insertions(+) create mode 100644 examples/dff/hdl/dff.v create mode 100644 examples/dff/hdl/dff.vhdl create mode 100644 examples/dff/tests/Makefile create mode 100644 examples/dff/tests/dff_cocotb.py diff --git a/examples/dff/hdl/dff.v b/examples/dff/hdl/dff.v new file mode 100644 index 00000000..bf5762d4 --- /dev/null +++ b/examples/dff/hdl/dff.v @@ -0,0 +1,17 @@ +// Copyright (c) 2016 Technische Universitaet Dresden, Germany +// Chair for VLSI-Design, Diagnostic and Architecture +// Author: Martin Zabel +// All rights reserved. +// +// A simple D flip-flop + +module dff (c,d,q); + input wire c, d; + output reg q = 1'b0; + + always @(posedge c) + begin + q <= #1 d; + end + +endmodule // dff diff --git a/examples/dff/hdl/dff.vhdl b/examples/dff/hdl/dff.vhdl new file mode 100644 index 00000000..8791c683 --- /dev/null +++ b/examples/dff/hdl/dff.vhdl @@ -0,0 +1,21 @@ +-- Copyright (c) 2016 Technische Universitaet Dresden, Germany +-- Chair for VLSI-Design, Diagnostic and Architecture +-- Author: Martin Zabel +-- All rights reserved. +-- +-- A simple D flip-flop + +library ieee; +use ieee.std_logic_1164.all; + +entity dff is + port ( + c : in std_logic; + d : in std_logic; + q : out std_logic := '0'); +end entity dff; + +architecture rtl of dff is +begin + q <= d when rising_edge(c); +end architecture rtl; diff --git a/examples/dff/tests/Makefile b/examples/dff/tests/Makefile new file mode 100644 index 00000000..980cc3be --- /dev/null +++ b/examples/dff/tests/Makefile @@ -0,0 +1,30 @@ +# Copyright (c) 2016 Technische Universitaet Dresden, Germany +# Chair for VLSI-Design, Diagnostic and Architecture +# Author: Martin Zabel +# All rights reserved. + +CWD=$(shell pwd) +COCOTB=$(CWD)/../../.. + +TOPLEVEL_LANG ?=verilog + +ifeq ($(TOPLEVEL_LANG),verilog) + VERILOG_SOURCES =$(CWD)/../hdl/dff.v +else ifeq ($(TOPLEVEL_LANG),vhdl) + VHDL_SOURCES =$(CWD)/../hdl/dff.vhdl +else + $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") +endif + +TOPLEVEL=dff +MODULE=$(TOPLEVEL)_cocotb + +CUSTOM_SIM_DEPS=$(CWD)/Makefile + +VSIM_ARGS=-t 1ps + +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim + +# list all required Python files here +sim: $(MODULE).py diff --git a/examples/dff/tests/dff_cocotb.py b/examples/dff/tests/dff_cocotb.py new file mode 100644 index 00000000..4b0470b7 --- /dev/null +++ b/examples/dff/tests/dff_cocotb.py @@ -0,0 +1,96 @@ +'''Copyright (c) 2016 Technische Universitaet Dresden, Germany +Chair for VLSI-Design, Diagnostic and Architecture +Author: Martin Zabel +All rights reserved. + +Structure of this testbench is based on Endian Swapper Example delivered with +Cocotb.''' + +#import traceback +import random + +import cocotb +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 single input or output of DUT.''' + def __init__(self, name, signal, clk, callback=None, event=None): + self.name = name + self.signal = signal + self.clk = clk + Monitor.__init__(self, callback, event) + + @coroutine + def _monitor_recv(self): + clkedge = RisingEdge(self.clk) + + while True: + yield clkedge + vec = self.signal.value + self._recv(vec) + +def input_gen(): + while True: + yield random.randint(1,5), random.randint(1,5) + +class DFF_TB(object): + def __init__(self, dut, init_val): + self.dut = dut + self.stopped = False + 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.''' + if not self.stopped: + self.expected_output.append(transaction) + + def start(self): + self.input_drv.start() + + def stop(self): + self.input_drv.stop() + self.stopped = True + +@cocotb.coroutine +def clock_gen(signal): + while True: + signal <= 0 + yield Timer(5000) # ps + signal <= 1 + yield Timer(5000) # ps + +@cocotb.coroutine +def run_test(dut): + cocotb.fork(clock_gen(dut.c)) + tb = DFF_TB(dut, BinaryValue(0,1)) + clkedge = RisingEdge(dut.c) + + tb.start() + for i in range(100): + yield clkedge + + tb.stop() + yield clkedge + + raise tb.scoreboard.result + +factory = TestFactory(run_test) +factory.generate_tests() From 50f3a5eda86c6deb0a9dd26292c4c765c0bceb37 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Wed, 30 Mar 2016 12:50:09 -0700 Subject: [PATCH 1180/2656] Issue #423: Update FLI to properly handle setting a vector of length 1 --- lib/fli/FliObjHdl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/fli/FliObjHdl.cpp b/lib/fli/FliObjHdl.cpp index 06e9b797..b315e773 100644 --- a/lib/fli/FliObjHdl.cpp +++ b/lib/fli/FliObjHdl.cpp @@ -295,7 +295,7 @@ const char* FliLogicObjHdl::get_signal_value_binstr(void) int FliLogicObjHdl::set_signal_value(const long value) { - if (m_num_elems == 1) { + if (m_fli_type == MTI_TYPE_ENUM) { mtiInt32T enumVal = value ? m_enum_map['1'] : m_enum_map['0']; if (m_is_var) { @@ -323,7 +323,7 @@ int FliLogicObjHdl::set_signal_value(const long value) int FliLogicObjHdl::set_signal_value(std::string &value) { - if (m_num_elems == 1) { + if (m_fli_type == MTI_TYPE_ENUM) { mtiInt32T enumVal = m_enum_map[value.c_str()[0]]; if (m_is_var) { From bbfd0e7228ad24d1b97a3885589e8fb219732021 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Wed, 30 Mar 2016 13:09:31 -0700 Subject: [PATCH 1181/2656] Issue #423: Update VHPI to properly handle setting a vector of length 1 --- lib/vhpi/VhpiCbHdl.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index 37d5a2ac..b8279559 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -261,6 +261,7 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { } int VhpiLogicSignalObjHdl::initialise(std::string &name, std::string &fq_name) { + // Determine the type of object, either scalar or vector m_value.format = vhpiLogicVal; m_value.bufSize = 0; @@ -272,11 +273,23 @@ int VhpiLogicSignalObjHdl::initialise(std::string &name, std::string &fq_name) { m_binvalue.numElems = 0; m_binvalue.value.str = NULL; - vhpiHandleT handle = GpiObjHdl::get_handle(); + vhpiHandleT handle = GpiObjHdl::get_handle(); + vhpiHandleT base_hdl = vhpi_handle(vhpiBaseType, handle); + + if (base_hdl == NULL) { + vhpiHandleT st_hdl = vhpi_handle(vhpiSubtype, handle); + + if (st_hdl != NULL) { + base_hdl = vhpi_handle(vhpiBaseType, st_hdl); + vhpi_release_handle(st_hdl); + } + } + + vhpiHandleT query_hdl = (base_hdl != NULL) ? base_hdl : handle; m_num_elems = vhpi_get(vhpiSizeP, handle); - if (m_num_elems > 1) { + if (vhpi_get(vhpiKindP, query_hdl) == vhpiArrayTypeDeclK) { m_indexable = true; m_value.format = vhpiLogicVecVal; m_value.bufSize = m_num_elems*sizeof(vhpiEnumT); From 165babb8eaf1f717429a68496a183933b486eda2 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Wed, 30 Mar 2016 14:20:09 -0700 Subject: [PATCH 1182/2656] Issue #421: Update the Avalon Packet Driver to drive BinaryValues of the correct length --- cocotb/drivers/avalon.py | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 4faabae7..5572dfb3 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -419,14 +419,21 @@ def __init__(self, *args, **kwargs): self.log.debug("Setting config option %s to %s" % (configoption, str(value))) - word = BinaryValue(bits=len(self.bus.data), - bigEndian=self.config['firstSymbolInHighOrderBits']) - word.binstr = ("x"*len(self.bus.data)) + word = BinaryValue(bits=len(self.bus.data), + bigEndian=self.config['firstSymbolInHighOrderBits']) + + empty = BinaryValue(bits=len(self.bus.empty), bigEndian=False) + single = BinaryValue(bits=1, bigEndian=False) + + word.binstr = ("x"*len(self.bus.data)) + empty.binstr = ("x"*len(self.bus.empty)) + single.binstr = ("x") + self.bus.valid <= 0 self.bus.data <= word - self.bus.empty <= word - self.bus.startofpacket <= word - self.bus.endofpacket <= word + self.bus.empty <= empty + self.bus.startofpacket <= single + self.bus.endofpacket <= single @coroutine def _wait_ready(self): @@ -457,6 +464,10 @@ def _send_string(self, string, sync=True): word = BinaryValue(bits=len(self.bus.data), bigEndian=self.config['firstSymbolInHighOrderBits']) + empty = BinaryValue(bits=len(self.bus.empty), bigEndian=False) + single = BinaryValue(bits=1, bigEndian=False) + + # Drive some defaults since we don't know what state we're in # self.bus.empty <= 0 self.bus.startofpacket <= 0 @@ -512,11 +523,13 @@ def _send_string(self, string, sync=True): yield clkedge self.bus.valid <= 0 self.bus.endofpacket <= 0 - word.binstr = ("x"*len(self.bus.data)) + word.binstr = ("x"*len(self.bus.data)) + empty.binstr = ("x"*len(self.bus.empty)) + single.binstr = ("x") self.bus.data <= word - self.bus.empty <= word - self.bus.startofpacket <= word - self.bus.endofpacket <= word + self.bus.empty <= empty + self.bus.startofpacket <= single + self.bus.endofpacket <= single @coroutine def _send_iterable(self, pkt, sync=True): From abc4cbb431401f10e605e0a68ebf64078f388ab6 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Wed, 30 Mar 2016 14:00:06 -0700 Subject: [PATCH 1183/2656] Issue #422: Update BinaryValue __str__() to return binstr --- cocotb/binary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 8505e0c8..a5e01295 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -340,7 +340,7 @@ def __le__(self, other): self.assign(other) def __str__(self): - return "%d" % (self.value) + return self.binstr def __bool__(self): return self.__nonzero__() From d9f475063f08f8c6e2ba6b965fde43e63dde85c6 Mon Sep 17 00:00:00 2001 From: Martin Zabel Date: Fri, 1 Apr 2016 13:36:02 +0200 Subject: [PATCH 1184/2656] Added license and documentation. --- examples/dff/hdl/dff.v | 34 ++++++++++--- examples/dff/hdl/dff.vhdl | 31 ++++++++++-- examples/dff/tests/dff_cocotb.py | 84 ++++++++++++++++++++++++++------ 3 files changed, 122 insertions(+), 27 deletions(-) diff --git a/examples/dff/hdl/dff.v b/examples/dff/hdl/dff.v index bf5762d4..d9307d9d 100644 --- a/examples/dff/hdl/dff.v +++ b/examples/dff/hdl/dff.v @@ -1,9 +1,29 @@ -// Copyright (c) 2016 Technische Universitaet Dresden, Germany -// Chair for VLSI-Design, Diagnostic and Architecture -// Author: Martin Zabel -// All rights reserved. +// ============================================================================= +// Authors: Martin Zabel +// +// Module: A simple D-FF +// +// Description: +// ------------------------------------ +// A simple D-FF with an initial state of '0'. // -// A simple D flip-flop +// 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. +// ============================================================================= module dff (c,d,q); input wire c, d; @@ -11,7 +31,9 @@ module dff (c,d,q); always @(posedge c) begin - q <= #1 d; + // It is also possible to add an delay of less than one clock period + // here. + q <= d; end endmodule // dff diff --git a/examples/dff/hdl/dff.vhdl b/examples/dff/hdl/dff.vhdl index 8791c683..4d9f3a01 100644 --- a/examples/dff/hdl/dff.vhdl +++ b/examples/dff/hdl/dff.vhdl @@ -1,9 +1,29 @@ --- Copyright (c) 2016 Technische Universitaet Dresden, Germany --- Chair for VLSI-Design, Diagnostic and Architecture --- Author: Martin Zabel --- All rights reserved. +-- ============================================================================= +-- Authors: Martin Zabel +-- +-- Module: A simple D-FF +-- +-- Description: +-- ------------------------------------ +-- A simple D-FF with an initial state of '0'. -- --- A simple D flip-flop +-- 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. +-- ============================================================================= library ieee; use ieee.std_logic_1164.all; @@ -17,5 +37,6 @@ end entity dff; architecture rtl of dff 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; diff --git a/examples/dff/tests/dff_cocotb.py b/examples/dff/tests/dff_cocotb.py index 4b0470b7..48395fb1 100644 --- a/examples/dff/tests/dff_cocotb.py +++ b/examples/dff/tests/dff_cocotb.py @@ -1,12 +1,30 @@ -'''Copyright (c) 2016 Technische Universitaet Dresden, Germany -Chair for VLSI-Design, Diagnostic and Architecture -Author: Martin Zabel -All rights reserved. +# ============================================================================== +# 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. +# ============================================================================== -Structure of this testbench is based on Endian Swapper Example delivered with -Cocotb.''' - -#import traceback import random import cocotb @@ -19,31 +37,46 @@ from cocotb.scoreboard import Scoreboard from cocotb.result import TestFailure, TestSuccess +# ============================================================================== class BitMonitor(Monitor): - '''Observes single input or output of DUT.''' - def __init__(self, name, signal, clk, callback=None, event=None): + """Observes single input or output of DUT.""" + def __init__(self, name, signal, clock, callback=None, event=None): self.name = name self.signal = signal - self.clk = clk + self.clock = clock Monitor.__init__(self, callback, event) @coroutine def _monitor_recv(self): - clkedge = RisingEdge(self.clk) + 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 risign 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) @@ -58,39 +91,58 @@ def __init__(self, dut, init_val): callback=self.model) def model(self, transaction): - '''Model the DUT based on the input 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, output of + D-FF can be checked. + """ self.input_drv.stop() self.stopped = True +# ============================================================================== @cocotb.coroutine def clock_gen(signal): + """Generate the clock signal.""" while True: signal <= 0 yield Timer(5000) # ps signal <= 1 yield Timer(5000) # ps +# ============================================================================== @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(100): yield clkedge - + + # 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 +# ============================================================================== +# Register test. factory = TestFactory(run_test) factory.generate_tests() From 85e514c9613d74723f4c7e3c5b885e4831ca1ada Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 1 Apr 2016 14:16:50 +0100 Subject: [PATCH 1185/2656] Cleanup: Move clean rule for issue_253 so an error is not shown on make clean --- tests/test_cases/issue_253/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_cases/issue_253/Makefile b/tests/test_cases/issue_253/Makefile index 72766be3..39f78e09 100644 --- a/tests/test_cases/issue_253/Makefile +++ b/tests/test_cases/issue_253/Makefile @@ -61,6 +61,7 @@ no_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VP vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) mkdir -p $@_result && mv results.xml $@_result/ +endif + clean:: @rm -rf empty_top_level_result no_top_level_result notset_top_level_result -endif From d40fdb9a4b87ae11061dc885da4e158076b9582e Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Fri, 1 Apr 2016 14:47:30 +0100 Subject: [PATCH 1186/2656] Issue #389: Pull forward a couple of changes from @elgorwi --- makefiles/simulators/Makefile.questa | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 414d604e..aee6a060 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -82,6 +82,7 @@ ifneq ($(GENERICS),) VSIM_ARGS += $(foreach gen, $(GENERICS),"-G $(gen)") endif +.PHONY : $(SIM_BUILD)/runsim.do $(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) $(SIM_BUILD) echo "if [file exists $(RTL_LIBRARY)] {vdel -lib $(RTL_LIBRARY) -all}" > $@ echo "vlib $(RTL_LIBRARY)" >> $@ @@ -119,13 +120,13 @@ OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ LIB_LOAD := PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) NEW_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-zA-Z]\):\//\/\1\//g' -e 's/;/:/g') -INT_LIBS := $(COCOTB_VPI_LIB) $(COCOTB_FLI_LIB) else LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) NEW_PYTHONPATH := $(PYTHONPATH) -INT_LIBS := $(COCOTB_VPI_LIB) $(COCOTB_FLI_LIB) endif +INT_LIBS := $(COCOTB_VPI_LIB) $(COCOTB_FLI_LIB) + # Depending on the version of modelsim the interfaces that it supports can change # Append or remove values to INT_LIBS depenending on your license results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) From 25ff3c98264b1fe0ef0ae6758b56a25cf7e0f85e Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Fri, 1 Apr 2016 13:23:48 -0700 Subject: [PATCH 1187/2656] Issue #425: Remove use of deprecated 'name' attribute on SimHandle objects --- cocotb/bus.py | 2 +- cocotb/clock.py | 2 +- cocotb/drivers/__init__.py | 2 +- cocotb/monitors/__init__.py | 2 +- cocotb/scoreboard.py | 2 +- cocotb/triggers.py | 6 +++--- cocotb/wavedrom.py | 2 +- examples/mixed_language/tests/test_mixed_language.py | 4 ++-- tests/test_cases/issue_348/issue_348.py | 4 ++-- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cocotb/bus.py b/cocotb/bus.py index 6b7f5596..d24f2901 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -110,7 +110,7 @@ def drive(self, obj, strict=False): if not hasattr(obj, name): if strict: msg = ("Unable to drive onto %s.%s because %s is missing " - "attribute %s" % self._entity.name, self._name, + "attribute %s" % self._entity._name, self._name, obj.__class__.__name__, name) raise AttributeError(msg) else: diff --git a/cocotb/clock.py b/cocotb/clock.py index 27283b7e..703ca467 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -39,7 +39,7 @@ class BaseClock(object): def __init__(self, signal): self.signal = signal self.log = SimLog("cocotb.%s.%s" % - (self.__class__.__name__, self.signal.name)) + (self.__class__.__name__, self.signal._name)) class Clock(BaseClock): diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index 7d454a1f..51c9f44b 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -214,7 +214,7 @@ def __init__(self, entity, name, clock): but could work here as well) clock (SimHandle) : A handle to the clock associated with this bus """ - self.log = SimLog("cocotb.%s.%s" % (entity.name, name)) + self.log = SimLog("cocotb.%s.%s" % (entity._name, name)) Driver.__init__(self) self.entity = entity self.name = name diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index 7a27f767..3928b5d6 100644 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -151,7 +151,7 @@ class BusMonitor(Monitor): def __init__(self, entity, name, clock, reset=None, reset_n=None, callback=None, event=None): - self.log = SimLog("cocotb.%s.%s" % (entity.name, name)) + self.log = SimLog("cocotb.%s.%s" % (entity._name, name)) self.entity = entity self.name = name self.clock = clock diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index 59ed8931..14392c24 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -53,7 +53,7 @@ class Scoreboard(object): def __init__(self, dut, reorder_depth=0, fail_immediately=True): self.dut = dut - self.log = SimLog("cocotb.scoreboard.%s" % self.dut.name) + self.log = SimLog("cocotb.scoreboard.%s" % self.dut._name) self.errors = 0 self.expected = {} self._imm = fail_immediately diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 4c8da7f3..aca33142 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -225,7 +225,7 @@ def prime(self, callback): Trigger.prime(self) def __str__(self): - return self.__class__.__name__ + "(%s)" % self.signal.name + return self.__class__.__name__ + "(%s)" % self.signal._name def Edge(signal): return signal._e_edge @@ -251,7 +251,7 @@ def prime(self, callback): Trigger.prime(self) def __str__(self): - return self.__class__.__name__ + "(%s)" % self.signal.name + return self.__class__.__name__ + "(%s)" % self.signal._name class _RisingEdge(_RisingOrFallingEdge): @@ -313,7 +313,7 @@ def _check(obj): Trigger.prime(self) def __str__(self): - return self.__class__.__name__ + "(%s)" % self.signal.name + return self.__class__.__name__ + "(%s)" % self.signal._name class Combine(PythonTrigger): diff --git a/cocotb/wavedrom.py b/cocotb/wavedrom.py index 044d237c..0bccb90a 100644 --- a/cocotb/wavedrom.py +++ b/cocotb/wavedrom.py @@ -44,7 +44,7 @@ def __init__(self, obj): self._hdls[name] = obj._signals[name] self._name = obj._name else: - self._hdls[obj.name.split(".")[-1]] = obj + self._hdls[obj._name.split(".")[-1]] = obj self.clear() diff --git a/examples/mixed_language/tests/test_mixed_language.py b/examples/mixed_language/tests/test_mixed_language.py index 1d079c7c..f2979df4 100644 --- a/examples/mixed_language/tests/test_mixed_language.py +++ b/examples/mixed_language/tests/test_mixed_language.py @@ -11,10 +11,10 @@ def mixed_language_test(dut): yield Timer(100) verilog = dut.i_swapper_sv - dut.log.info("Got: %s" % repr(verilog.name)) + dut.log.info("Got: %s" % repr(verilog._name)) vhdl = dut.i_swapper_vhdl - dut.log.info("Got: %s" % repr(vhdl.name)) + dut.log.info("Got: %s" % repr(vhdl._name)) verilog.reset_n = 1 yield Timer(100) diff --git a/tests/test_cases/issue_348/issue_348.py b/tests/test_cases/issue_348/issue_348.py index eebe5f31..8ba5390f 100644 --- a/tests/test_cases/issue_348/issue_348.py +++ b/tests/test_cases/issue_348/issue_348.py @@ -15,7 +15,7 @@ def clock_gen(signal, num): @cocotb.coroutine def signal_mon(signal, idx, edge): - log = SimLog("cocotb.signal_mon.%d.%s" % (idx, signal.name)) + log = SimLog("cocotb.signal_mon.%d.%s" % (idx, signal._name)) value = signal.value edges = 0 @@ -23,7 +23,7 @@ def signal_mon(signal, idx, edge): while True: yield edge(signal) edges += 1 - + raise ReturnValue(edges) class DualMonitor: From 365592d017abad307d225c9fc53bf7eccb6a050a Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Fri, 1 Apr 2016 13:26:52 -0700 Subject: [PATCH 1188/2656] Issue #425: Remove use of deprecated 'fullname' attribute on SimHandle objects --- tests/test_cases/test_discovery/test_discovery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 79845157..4f48e6dd 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -48,7 +48,7 @@ def discover_module_values(dut): yield Timer(0) count = 0 for thing in dut: - thing.log.info("Found something: %s" % thing.fullname) + thing.log.info("Found something: %s" % thing._fullname) count += 1 if count < 2: raise TestFailure("Expected to discover things in the DUT") From 05b8937b945e195fbedf556e73ff2878dfc729c5 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Fri, 1 Apr 2016 17:11:09 -0700 Subject: [PATCH 1189/2656] Issue #425: Remove use of deprecated 'log' attribute on SimHandle objects --- cocotb/bus.py | 4 +- cocotb/drivers/xgmii.py | 2 +- cocotb/monitors/xgmii.py | 2 +- documentation/source/endian_swapper.rst | 2 +- documentation/source/examples.rst | 6 +-- documentation/source/ping_tun_tap.rst | 2 +- documentation/source/quickstart.rst | 12 +++--- examples/adder/tests/test_adder.py | 4 +- .../tests/test_endian_swapper.py | 8 ++-- .../tests/test_endian_swapper_hal.py | 8 ++-- examples/mean/tests/test_mean.py | 4 +- .../tests/test_mixed_language.py | 6 +-- .../ping_tun_tap/tests/test_icmp_reply.py | 4 +- .../test_closedown/test_closedown.py | 2 +- tests/test_cases/test_cocotb/test_cocotb.py | 42 +++++++++---------- .../test_discovery/test_discovery.py | 10 ++--- .../test_cases/test_external/test_external.py | 26 ++++++------ .../test_iteration_vhdl/test_iteration.py | 2 +- 18 files changed, 73 insertions(+), 73 deletions(-) diff --git a/cocotb/bus.py b/cocotb/bus.py index d24f2901..33c297a8 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -89,8 +89,8 @@ def __init__(self, entity, name, signals, optional_signals=[]): setattr(self, signal, hdl) self._signals[signal] = getattr(self, signal) else: - self._entity.log.debug("Ignoring optional missing signal " - "%s on bus %s" % (signal, name)) + self._entity._log.debug("Ignoring optional missing signal " + "%s on bus %s" % (signal, name)) def drive(self, obj, strict=False): """ diff --git a/cocotb/drivers/xgmii.py b/cocotb/drivers/xgmii.py index 29a5d638..d34d75e3 100644 --- a/cocotb/drivers/xgmii.py +++ b/cocotb/drivers/xgmii.py @@ -145,7 +145,7 @@ def __init__(self, signal, clock, interleaved=True): byte0, byte1, ..., byte0_control, byte1_control... """ - self.log = signal.log + self.log = signal._log self.signal = signal self.clock = clock self.bus = _XGMIIBus(len(signal)/9, interleaved=interleaved) diff --git a/cocotb/monitors/xgmii.py b/cocotb/monitors/xgmii.py index 22e3194e..e739ab86 100644 --- a/cocotb/monitors/xgmii.py +++ b/cocotb/monitors/xgmii.py @@ -78,7 +78,7 @@ def __init__(self, signal, clock, interleaved=True, callback=None, byte0, byte1, ..., byte0_control, byte1_control... """ - self.log = signal.log + self.log = signal._log self.clock = clock self.signal = signal self.bytes = len(self.signal) / 9 diff --git a/documentation/source/endian_swapper.rst b/documentation/source/endian_swapper.rst index f2d864c6..f5175fff 100644 --- a/documentation/source/endian_swapper.rst +++ b/documentation/source/endian_swapper.rst @@ -150,7 +150,7 @@ We want to run different variations of tests but they will all have a very simil raise TestFailure("DUT recorded %d packets but tb counted %d" % ( pkt_count.integer, tb.pkts_sent)) else: - dut.log.info("DUT correctly counted %d packets" % pkt_count.integer) + dut._log.info("DUT correctly counted %d packets" % pkt_count.integer) raise tb.scoreboard.result diff --git a/documentation/source/examples.rst b/documentation/source/examples.rst index 9b1ff38b..5b958f5d 100644 --- a/documentation/source/examples.rst +++ b/documentation/source/examples.rst @@ -39,10 +39,10 @@ Example testbench for snipped of code from `comp.lang.verilog expect: raise TestFailure("Task didn't complete in expected time") if result is timer: - dut.log.info("Count %d: Task still running" % count) + dut._log.info("Count %d: Task still running" % count) count += 1 else: break @@ -326,8 +326,8 @@ def count_edges_cycles(signal, edges): edge = RisingEdge(signal) for i in range(edges): yield edge - signal.log.info("Rising edge %d detected" % i) - signal.log.info("Finished, returning %d" % edges) + signal._log.info("Rising edge %d detected" % i) + signal._log.info("Finished, returning %d" % edges) raise ReturnValue(edges) @@ -335,7 +335,7 @@ def count_edges_cycles(signal, edges): def do_single_edge_check(dut, level): """Do test for rising edge""" old_value = dut.clk.value.integer - dut.log.info("Value of %s is %d" % (dut.clk, old_value)) + dut._log.info("Value of %s is %d" % (dut.clk, old_value)) if old_value is level: raise TestError("%s not to %d start with" % (dut.clk, not level)) if level == 1: @@ -343,7 +343,7 @@ def do_single_edge_check(dut, level): else: yield FallingEdge(dut.clk) new_value = dut.clk.value.integer - dut.log.info("Value of %s is %d" % (dut.clk, new_value)) + dut._log.info("Value of %s is %d" % (dut.clk, new_value)) if new_value is not level: raise TestError("%s not %d at end" % (dut.clk, level)) @@ -461,14 +461,14 @@ def __str__(self): @cocotb.test() def test_logging_with_args(dut): counter = StrCallCounter() - dut.log.logger.setLevel(logging.INFO) #To avoid logging debug message, to make next line run without error - dut.log.debug("%s", counter) + dut._log.logger.setLevel(logging.INFO) #To avoid logging debug message, to make next line run without error + dut._log.debug("%s", counter) assert counter.str_counter == 0 - dut.log.info("%s", counter) + dut._log.info("%s", counter) assert counter.str_counter == 1 - dut.log.info("No substitution") + dut._log.info("No substitution") yield Timer(100) #Make it do something with time @@ -478,29 +478,29 @@ def test_binary_value(dut): Test out the cocotb supplied BinaryValue class for manipulating values in a style familiar to rtl coders. """ - + vec = BinaryValue(value=0,bits=16) - dut.log.info("Checking default endianess is Big Endian.") + dut._log.info("Checking default endianess is Big Endian.") if not vec.big_endian: raise TestFailure("The default endianess is Little Endian - was expecting Big Endian.") if vec.integer != 0: raise TestFailure("Expecting our BinaryValue object to have the value 0.") - dut.log.info("Checking single index assignment works as expected on a Little Endian BinaryValue.") + dut._log.info("Checking single index assignment works as expected on a Little Endian BinaryValue.") vec = BinaryValue(value=0,bits=16,bigEndian=False) if vec.big_endian: raise TestFailure("Our BinaryValue object is reporting it is Big Endian - was expecting Little Endian.") for x in range(vec._bits): vec[x] = '1' - dut.log.info("Trying vec[%s] = 1" % x) + dut._log.info("Trying vec[%s] = 1" % x) expected_value = 2**(x+1) - 1 if vec.integer != expected_value: raise TestFailure("Failed on assignment to vec[%s] - expecting %s - got %s" % (x,expected_value,vec.integer)) if vec[x] != 1: raise TestFailure("Failed on index compare on vec[%s] - expecting 1 - got %s" % (x,vec[x])) - dut.log.info("vec = 'b%s" % vec.binstr) + dut._log.info("vec = 'b%s" % vec.binstr) - dut.log.info("Checking slice assignment works as expected on a Little Endian BinaryValue.") + dut._log.info("Checking slice assignment works as expected on a Little Endian BinaryValue.") if vec.integer != 65535: raise TestFailure("Expecting our BinaryValue object to be 65535 after the end of the previous test.") vec[7:0] = '00110101' @@ -509,8 +509,8 @@ def test_binary_value(dut): if vec[7:0].binstr != '00110101': raise TestFailure("Set lower 8-bits to 00110101 but readback %s from vec[7:0]" % vec[7:0].binstr) - dut.log.info("vec[7:0] = 'b%s" % vec[7:0].binstr) - dut.log.info("vec[15:8] = 'b%s" % vec[15:8].binstr) - dut.log.info("vec = 'b%s" % vec.binstr) + dut._log.info("vec[7:0] = 'b%s" % vec[7:0].binstr) + dut._log.info("vec[15:8] = 'b%s" % vec[15:8].binstr) + dut._log.info("vec = 'b%s" % vec.binstr) yield Timer(100) #Make it do something with time diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 4f48e6dd..f18ffdcd 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -48,7 +48,7 @@ def discover_module_values(dut): yield Timer(0) count = 0 for thing in dut: - thing.log.info("Found something: %s" % thing._fullname) + thing._log.info("Found something: %s" % thing._fullname) count += 1 if count < 2: raise TestFailure("Expected to discover things in the DUT") @@ -94,7 +94,7 @@ def access_single_bit(dut): """ dut.stream_in_data <= 0 yield Timer(10) - dut.log.info("%s = %d bits" % + dut._log.info("%s = %d bits" % (str(dut.stream_in_data), len(dut.stream_in_data))) dut.stream_in_data[2] <= 1 yield Timer(10) @@ -114,7 +114,7 @@ def access_single_bit_assignment(dut): """ dut.stream_in_data = 0 yield Timer(10) - dut.log.info("%s = %d bits" % + dut._log.info("%s = %d bits" % (str(dut.stream_in_data), len(dut.stream_in_data))) dut.stream_in_data[2] = 1 yield Timer(10) @@ -128,7 +128,7 @@ def access_single_bit_assignment(dut): def access_single_bit_erroneous(dut): """Access a non-existent single bit""" yield Timer(10) - dut.log.info("%s = %d bits" % + dut._log.info("%s = %d bits" % (str(dut.stream_in_data), len(dut.stream_in_data))) bit = len(dut.stream_in_data) + 4 dut.stream_in_data[bit] <= 1 @@ -310,7 +310,7 @@ def access_boolean(dut): def skip_a_test(dut): """This test shouldn't execute""" yield Timer(10) - dut.log.info("%s = %d bits" % + dut._log.info("%s = %d bits" % (str(dut.stream_in_data), len(dut.stream_in_data))) bit = len(dut.stream_in_data) + 4 dut.stream_in_data[bit] <= 1 diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index 290824d4..27fd0bd6 100644 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -51,7 +51,7 @@ @cocotb.function def decorated_test_read(dut, signal): global test_count - dut.log.info("Inside decorated_test_read") + dut._log.info("Inside decorated_test_read") test_count = 0 while test_count is not 5: yield RisingEdge(dut.clk) @@ -62,7 +62,7 @@ def decorated_test_read(dut, signal): def test_read(dut, signal): global test_count - dut.log.info("Inside test_read") + dut._log.info("Inside test_read") while test_count is not 5: yield RisingEdge(dut.clk) test_count += 1 @@ -73,7 +73,7 @@ def hal_read(function): global test_count test_count = 0 function(g_dut, g_dut.stream_out_ready) - g_dut.log.info("Cycles seen is %d" % test_count) + g_dut._log.info("Cycles seen is %d" % test_count) def create_thread(function): @@ -94,7 +94,7 @@ def clock_gen(clock): clock <= 1 yield Timer(100) - clock.log.warning("Clock generator finished!") + clock._log.warning("Clock generator finished!") @cocotb.test(expect_fail=False, skip=True) @@ -110,7 +110,7 @@ def test_callable(dut): global test_count g_dut = dut create_thread(decorated_test_read) - dut.log.info("Test thread created") + dut._log.info("Test thread created") clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) yield Timer(100000) clk_gen.kill() @@ -132,7 +132,7 @@ def test_callable_fail(dut): global test_count g_dut = dut create_thread(test_read) - dut.log.info("Test thread created") + dut._log.info("Test thread created") clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) yield Timer(100000) clk_gen.kill() @@ -141,14 +141,14 @@ def test_callable_fail(dut): def test_ext_function(dut): - # dut.log.info("Sleeping") + # dut._log.info("Sleeping") return 2 @cocotb.function def yield_to_readwrite(dut): yield RisingEdge(dut.clk) - dut.log.info("Returning from yield_to_readwrite") + dut._log.info("Returning from yield_to_readwrite") raise ReturnValue(2) @@ -158,7 +158,7 @@ def test_ext_function_access(dut): def test_ext_function_return(dut): value = dut.clk.value.integer - # dut.log.info("Sleeping and returning %s" % value) + # dut._log.info("Sleeping and returning %s" % value) # time.sleep(0.2) return value @@ -179,7 +179,7 @@ def test_ext_call_return(dut): mon = cocotb.scheduler.queue(clock_monitor(dut)) clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) value = yield external(test_ext_function)(dut) - dut.log.info("Value was %d" % value) + dut._log.info("Value was %d" % value) @cocotb.test(expect_fail=False) @@ -195,9 +195,9 @@ def test_ext_call_nreturn(dut): def test_multiple_externals(dut): clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) value = yield external(test_ext_function)(dut) - dut.log.info("First one completed") + dut._log.info("First one completed") value = yield external(test_ext_function)(dut) - dut.log.info("Second one completed") + dut._log.info("Second one completed") @cocotb.test(expect_fail=False) @@ -205,7 +205,7 @@ def test_external_from_readonly(dut): clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) yield ReadOnly() - dut.log.info("In readonly") + dut._log.info("In readonly") value = yield external(test_ext_function_access)(dut) diff --git a/tests/test_cases/test_iteration_vhdl/test_iteration.py b/tests/test_cases/test_iteration_vhdl/test_iteration.py index 63af0200..8e8a58ed 100644 --- a/tests/test_cases/test_iteration_vhdl/test_iteration.py +++ b/tests/test_cases/test_iteration_vhdl/test_iteration.py @@ -83,7 +83,7 @@ def dual_iteration(dut): @cocotb.test() def get_clock(dut): - dut.log.info("dut.aclk is %s" % dut.aclk.__class__.__name__) + dut._log.info("dut.aclk is %s" % dut.aclk.__class__.__name__) dut.aclk <= 0 yield Timer(1) dut.aclk <= 1 From b59e4fdfb74d6b3871fd82d69d46bb254c31ccd7 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Fri, 1 Apr 2016 17:27:21 -0700 Subject: [PATCH 1190/2656] Issue #425: Update HierarchyObject to check simulator for attribute before checking deprecated attributes --- cocotb/handle.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 79ed677a..968dc9bd 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -230,10 +230,12 @@ def __setattr__(self, name, value): Raise an AttributeError if users attempt to create new members which don't exist in the design. """ - if name.startswith("_") or name in self._compat_mapping: + if name.startswith("_"): return SimHandleBase.__setattr__(self, name, value) if self.__hasattr__(name) is not None: return getattr(self, name)._setcachedvalue(value) + if name in self._compat_mapping: + return SimHandleBase.__setattr__(self, name, value) raise AttributeError("Attempt to access %s which isn't present in %s" %( name, self._name)) @@ -245,16 +247,14 @@ def __getattr__(self, name): if name in self._sub_handles: return self._sub_handles[name] - if name.startswith("_") or name in self._compat_mapping: + if name.startswith("_"): return SimHandleBase.__getattr__(self, name) new_handle = simulator.get_handle_by_name(self._handle, name) if not new_handle: - # To find generated indices we have to discover all - self._discover_all() - if name in self._sub_handles: - return self._sub_handles[name] + if name in self._compat_mapping: + return SimHandleBase.__getattr__(self, name) raise AttributeError("%s contains no object named %s" % (self._name, name)) self._sub_handles[name] = SimHandle(new_handle, self._child_path(name)) return self._sub_handles[name] From db6ff847e54502cd433a3add5e5062ade49ef873 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Fri, 1 Apr 2016 18:55:04 -0700 Subject: [PATCH 1191/2656] Issue #426: Add a method for accessing extended identifiers in VHDL --- cocotb/handle.py | 11 ++++++++++ tests/designs/array_module/array_module.vhd | 3 +++ .../array_module/array_module_aldec.vhd | 3 +++ tests/test_cases/test_array/test_array.py | 22 +++++++++++++++---- 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 968dc9bd..cdb06f5c 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -281,6 +281,17 @@ def __hasattr__(self, name): self._invalid_sub_handles[name] = None return new_handle + def _id(self, name, extended=True): + """ + Query the simulator for a object with the specified name, including extended identifiers, + and cache the result to build a tree of objects + """ + if extended: + name = "\\"+name+"\\" + + if self.__hasattr__(name) is not None: + return getattr(self, name) + raise AttributeError("%s contains no object named %s" % (self._name, name)) class HierarchyArrayObject(RegionObject): """ diff --git a/tests/designs/array_module/array_module.vhd b/tests/designs/array_module/array_module.vhd index 68a7ec95..115ff918 100644 --- a/tests/designs/array_module/array_module.vhd +++ b/tests/designs/array_module/array_module.vhd @@ -86,6 +86,9 @@ architecture impl of array_module is signal sig_desc : std_logic_vector(23 downto 16); signal sig_asc : std_logic_vector(16 to 23); + signal \ext_id\ : std_logic; + signal \!\ : std_logic; + signal sig_t1 : t1; signal sig_t2 : t2; signal sig_t3a : t3(1 to 4); diff --git a/tests/designs/array_module/array_module_aldec.vhd b/tests/designs/array_module/array_module_aldec.vhd index d2a96dcd..b87ffeda 100644 --- a/tests/designs/array_module/array_module_aldec.vhd +++ b/tests/designs/array_module/array_module_aldec.vhd @@ -86,6 +86,9 @@ architecture impl of array_module is signal sig_desc : std_logic_vector(23 downto 16); signal sig_asc : std_logic_vector(16 to 23); + signal \ext_id\ : std_logic; + signal \!\ : std_logic; + signal sig_t1 : t1; signal sig_t2 : t2; signal sig_t3a : t3(1 to 4); diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py index 892bd824..8c866a13 100644 --- a/tests/test_cases/test_array/test_array.py +++ b/tests/test_cases/test_array/test_array.py @@ -250,6 +250,8 @@ def test_discover_all(dut): 13 (const_cmplx[1:2].a, const_cmplx[1:2].b[0:2]) (VHDL only excluding Aldec) signals: 9 (sig_desc) 9 (sig_asc) + 1 (\ext_id\) (VHDL only) + 1 (\!\) (VHDL only) 5 (sig_t1) 37 (sig_t2[7:4][7:0]) 37 (sig_t3a[1:4][7:0]) @@ -278,8 +280,8 @@ def test_discover_all(dut): 8 (desc_gen: process "always") (VPI - Aldec only) process: 1 ("always") (VPI - Aldec only) - TOTAL: 854 (VHDL - Default) - 816 (VHDL - Aldec) + TOTAL: 856 (VHDL - Default) + 818 (VHDL - Aldec) 780 (Verilog - Default) 649 (Verilog - Aldec) """ @@ -311,9 +313,9 @@ def test_discover_all(dut): dummy = hdl.sig if cocotb.LANGUAGE in ["vhdl"] and cocotb.SIM_NAME.lower().startswith(("riviera")): - pass_total = 816 + pass_total = 818 elif cocotb.LANGUAGE in ["vhdl"]: - pass_total = 854 + pass_total = 856 elif cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")): pass_total = 649 else: @@ -443,3 +445,15 @@ def test_direct_signal_indexing(dut): if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera"))): _check_type(tlog, dut.sig_rec.b[1], ModifiableObject) _check_type(tlog, dut.sig_rec.b[1][2], ModifiableObject) + +@cocotb.test(skip=(cocotb.LANGUAGE in ["verilog"])) +def test_extended_identifiers(dut): + """Test accessing extended identifiers""" + + tlog = logging.getLogger("cocotb.test") + + yield Timer(2000) + + tlog.info("Checking extended identifiers.") + _check_type(tlog, dut._id("\\ext_id\\",extended=False), ModifiableObject) + _check_type(tlog, dut._id("!"), ModifiableObject) From 1719e9875168e1ecb76d0e5e59710576c2b123c7 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 4 Apr 2016 13:30:38 -0700 Subject: [PATCH 1192/2656] Issue #429: Add support for GeneratorExit to SimHandles --- cocotb/handle.py | 97 ++++++++++++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 41 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index cdb06f5c..5d35334d 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -149,21 +149,24 @@ def __iter__(self): """ Iterate over all known objects in this layer of hierarchy """ - if not self._discovered: - self._discover_all() - - for name, handle in self._sub_handles.items(): - if isinstance(handle, list): - self._log.debug("Found index list length %d" % len(handle)) - for subindex, subhdl in enumerate(handle): - if subhdl is None: - self._log.warning("Index %d doesn't exist in %s.%s", subindex, self._name, name) - continue - self._log.debug("Yielding index %d from %s (%s)" % (subindex, name, type(subhdl))) - yield subhdl - else: - self._log.debug("Yielding %s (%s)" % (name, handle)) - yield handle + try: + if not self._discovered: + self._discover_all() + + for name, handle in self._sub_handles.items(): + if isinstance(handle, list): + self._log.debug("Found index list length %d" % len(handle)) + for subindex, subhdl in enumerate(handle): + if subhdl is None: + self._log.warning("Index %d doesn't exist in %s.%s", subindex, self._name, name) + continue + self._log.debug("Yielding index %d from %s (%s)" % (subindex, name, type(subhdl))) + yield subhdl + else: + self._log.debug("Yielding %s (%s)" % (name, handle)) + yield handle + except GeneratorExit: + pass def _discover_all(self): """ @@ -466,27 +469,33 @@ def __getitem__(self, index): return self._sub_handles[index] def __iter__(self): - if self._range is None: - raise StopIteration + try: + if self._range is None: + raise StopIteration - self._log.debug("Iterating with range [%d:%d]" % (self._range[0], self._range[1])) - for i in self._range_iter(self._range[0], self._range[1]): - try: - result = self[i] - yield result - except: - continue + self._log.debug("Iterating with range [%d:%d]" % (self._range[0], self._range[1])) + for i in self._range_iter(self._range[0], self._range[1]): + try: + result = self[i] + yield result + except IndexError: + continue + except GeneratorExit: + pass - def _range_iter(self, left, right): - if left > right: - while left >= right: - yield left - left = left - 1 - else: - while left <= right: - yield left - left = left + 1 + def _range_iter(self, left, right): + try: + if left > right: + while left >= right: + yield left + left = left - 1 + else: + while left <= right: + yield left + left = left + 1 + except GeneratorExit: + pass class NonConstantObject(NonHierarchyIndexableObject): def __init__(self, handle, path): @@ -503,19 +512,25 @@ def drivers(self): """ An iterator for gathering all drivers for a signal """ - iterator = simulator.iterate(self._handle, simulator.DRIVERS) - while True: - # Path is left as the default None since handles are not derived from the hierarchy - yield SimHandle(simulator.next(iterator)) + try: + iterator = simulator.iterate(self._handle, simulator.DRIVERS) + while True: + # Path is left as the default None since handles are not derived from the hierarchy + yield SimHandle(simulator.next(iterator)) + except GeneratorExit: + pass def loads(self): """ An iterator for gathering all loads on a signal """ - iterator = simulator.iterate(self._handle, simulator.LOADS) - while True: - # Path is left as the default None since handles are not derived from the hierarchy - yield SimHandle(simulator.next(iterator)) + try: + iterator = simulator.iterate(self._handle, simulator.LOADS) + while True: + # Path is left as the default None since handles are not derived from the hierarchy + yield SimHandle(simulator.next(iterator)) + except GeneratorExit: + pass class ModifiableObject(NonConstantObject): From bde3a58fb2de2a320bc46f2f75c1e36ccc145111 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 4 Apr 2016 15:33:20 -0700 Subject: [PATCH 1193/2656] Issue #430: Update BinaryValue to support all std_logic values --- cocotb/binary.py | 10 ++++++++-- examples/mixed_language/tests/test_mixed_language.py | 4 ++-- tests/test_cases/issue_330/issue_330.py | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index a5e01295..37c53f96 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -35,6 +35,10 @@ def resolve(string): for char in BinaryValue._resolve_to_0: string = string.replace(char, "0") + for char in BinaryValue._resolve_to_1: + string = string.replace(char, "1") + if any(char in string for char in BinaryValue._resolve_to_error): + raise ValueError("Unable to resolve to binary >%s<" % string) return string @@ -66,8 +70,10 @@ class BinaryValue(object): '*' """ - _resolve_to_0 = "xXzZuU" # noqa - _permitted_chars = "xXzZuU" + "01" # noqa + _resolve_to_0 = "-lL" # noqa + _resolve_to_1 = "hH" # noqa + _resolve_to_error = "xXzZuUwW" # Resolve to a ValueError() since these usually mean something is wrong + _permitted_chars = _resolve_to_0 +_resolve_to_1 + _resolve_to_error + "01" # noqa def __init__(self, value=None, bits=None, bigEndian=True, binaryRepresentation=BinaryRepresentation.UNSIGNED): diff --git a/examples/mixed_language/tests/test_mixed_language.py b/examples/mixed_language/tests/test_mixed_language.py index a4577a51..2201581a 100644 --- a/examples/mixed_language/tests/test_mixed_language.py +++ b/examples/mixed_language/tests/test_mixed_language.py @@ -28,5 +28,5 @@ def mixed_language_test(dut): raise TestFailure("reset_n signals were different") # Try accessing an object other than a port... - verilog_flush = int(verilog.flush_pipe) - vhdl_flush = int(vhdl.flush_pipe) + verilog_flush = str(verilog.flush_pipe) + vhdl_flush = str(vhdl.flush_pipe) diff --git a/tests/test_cases/issue_330/issue_330.py b/tests/test_cases/issue_330/issue_330.py index 0898433c..c14bc50d 100644 --- a/tests/test_cases/issue_330/issue_330.py +++ b/tests/test_cases/issue_330/issue_330.py @@ -18,7 +18,7 @@ def issue_330_direct(dut): structure = dut.inout_if - tlog.info("Value of inout_if => a_in = %d ; b_out = %d" % (structure.a_in, structure.b_out)) + tlog.info("Value of inout_if => a_in = %s ; b_out = %s" % (structure.a_in, structure.b_out)) @cocotb.test(skip=cocotb.SIM_NAME in ["Icarus Verilog"]) def issue_330_iteration(dut): From d040de7a5e82623a29f02373f86131ef25879dbf Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 4 Apr 2016 19:27:49 -0700 Subject: [PATCH 1194/2656] Issue #430: Attempt to fix test issue_142 for icarus --- tests/test_cases/issue_142/issue_142.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_cases/issue_142/issue_142.py b/tests/test_cases/issue_142/issue_142.py index 1bc31461..3aadbd5c 100644 --- a/tests/test_cases/issue_142/issue_142.py +++ b/tests/test_cases/issue_142/issue_142.py @@ -13,6 +13,8 @@ def issue_142_overflow_error(dut): through the GPI interface natively into BinaryValues""" cocotb.fork(Clock(dut.clk, 2500).start()) + dut.stream_in_data_wide <= BinaryValue(0,len(dut.stream_in_data_wide),bigEndian=False) + def _compare(value): if int(dut.stream_in_data_wide.value) != int(value): raise TestFailure("Expecting 0x%x but got 0x%x on %s" % ( From f323cceaf754f034dfcf4fdfb4d7bbc8a3536188 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 4 Apr 2016 19:35:51 -0700 Subject: [PATCH 1195/2656] Issue #430: Update test issue_142 to expect a failure for icarus --- tests/test_cases/issue_142/issue_142.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_cases/issue_142/issue_142.py b/tests/test_cases/issue_142/issue_142.py index 3aadbd5c..ad4c53d9 100644 --- a/tests/test_cases/issue_142/issue_142.py +++ b/tests/test_cases/issue_142/issue_142.py @@ -7,14 +7,12 @@ from cocotb.binary import BinaryValue -@cocotb.test() +@cocotb.test(expect_fail=cocotb.SIM_NAME.lower().startswith(("icarus"))) def issue_142_overflow_error(dut): """Tranparently convert ints too long to pass through the GPI interface natively into BinaryValues""" cocotb.fork(Clock(dut.clk, 2500).start()) - dut.stream_in_data_wide <= BinaryValue(0,len(dut.stream_in_data_wide),bigEndian=False) - def _compare(value): if int(dut.stream_in_data_wide.value) != int(value): raise TestFailure("Expecting 0x%x but got 0x%x on %s" % ( From d234d27403f2e11be7d08d1e3746e2f0723a2b4f Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 4 Apr 2016 19:43:24 -0700 Subject: [PATCH 1196/2656] Issue #430: Update test issue_142 to expect an error for icarus --- tests/test_cases/issue_142/issue_142.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/issue_142/issue_142.py b/tests/test_cases/issue_142/issue_142.py index ad4c53d9..d2fec00f 100644 --- a/tests/test_cases/issue_142/issue_142.py +++ b/tests/test_cases/issue_142/issue_142.py @@ -7,7 +7,7 @@ from cocotb.binary import BinaryValue -@cocotb.test(expect_fail=cocotb.SIM_NAME.lower().startswith(("icarus"))) +@cocotb.test(expect_error=cocotb.SIM_NAME.lower().startswith(("icarus"))) def issue_142_overflow_error(dut): """Tranparently convert ints too long to pass through the GPI interface natively into BinaryValues""" From 135e378ea6de43b3cca24873adfe959a8c4cee06 Mon Sep 17 00:00:00 2001 From: Stuart Hodgson Date: Tue, 5 Apr 2016 09:55:56 +0100 Subject: [PATCH 1197/2656] Cleanup: Backout redundant change --- makefiles/simulators/Makefile.questa | 1 - 1 file changed, 1 deletion(-) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index aee6a060..6e491f80 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -82,7 +82,6 @@ ifneq ($(GENERICS),) VSIM_ARGS += $(foreach gen, $(GENERICS),"-G $(gen)") endif -.PHONY : $(SIM_BUILD)/runsim.do $(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) $(SIM_BUILD) echo "if [file exists $(RTL_LIBRARY)] {vdel -lib $(RTL_LIBRARY) -all}" > $@ echo "vlib $(RTL_LIBRARY)" >> $@ From 3a126623d670f07b7604b1f1c6557209aa117e23 Mon Sep 17 00:00:00 2001 From: Martin Zabel Date: Tue, 5 Apr 2016 16:04:03 +0200 Subject: [PATCH 1198/2656] Resolve don't care value from std_logic ('-') to 0 in BinaryValue. --- cocotb/binary.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 8505e0c8..a734eda7 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -66,8 +66,8 @@ class BinaryValue(object): '*' """ - _resolve_to_0 = "xXzZuU" # noqa - _permitted_chars = "xXzZuU" + "01" # noqa + _resolve_to_0 = "xXzZuU-" # noqa + _permitted_chars = "xXzZuU-" + "01" # noqa def __init__(self, value=None, bits=None, bigEndian=True, binaryRepresentation=BinaryRepresentation.UNSIGNED): From 731f63e21dacec7e751cfbbaa15dfeacdc53ad53 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Wed, 6 Apr 2016 13:29:14 -0700 Subject: [PATCH 1199/2656] Issue #433: FLI update to use mti_ScheduleWakeup64() --- lib/fli/FliCbHdl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fli/FliCbHdl.cpp b/lib/fli/FliCbHdl.cpp index 02e4b8de..6e16a646 100644 --- a/lib/fli/FliCbHdl.cpp +++ b/lib/fli/FliCbHdl.cpp @@ -55,7 +55,7 @@ FliTimedCbHdl::FliTimedCbHdl(GpiImplInterface *impl, int FliTimedCbHdl::arm_callback(void) { - mti_ScheduleWakeup(m_proc_hdl, m_time_ps); + mti_ScheduleWakeup64(m_proc_hdl, m_time_ps); m_sensitised = true; set_call_state(GPI_PRIMED); return 0; From 0ab237b758c01f919f3cc7325ac9837c977d64ac Mon Sep 17 00:00:00 2001 From: elgorwi Date: Thu, 7 Apr 2016 20:16:43 +1000 Subject: [PATCH 1200/2656] fix compile error: cast from 'char*' to 'long int' loses precision --- lib/fli/FliObjHdl.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/fli/FliObjHdl.cpp b/lib/fli/FliObjHdl.cpp index 06e9b797..3dbf653d 100644 --- a/lib/fli/FliObjHdl.cpp +++ b/lib/fli/FliObjHdl.cpp @@ -312,9 +312,9 @@ int FliLogicObjHdl::set_signal_value(const long value) } if (m_is_var) { - mti_SetVarValue(get_handle(), (long)m_mti_buff); + mti_SetVarValue(get_handle(), (mtiLongT)m_mti_buff); } else { - mti_SetSignalValue(get_handle(), (long)m_mti_buff); + mti_SetSignalValue(get_handle(), (mtiLongT)m_mti_buff); } } @@ -350,9 +350,9 @@ int FliLogicObjHdl::set_signal_value(std::string &value) } if (m_is_var) { - mti_SetVarValue(get_handle(), (long)m_mti_buff); + mti_SetVarValue(get_handle(), (mtiLongT)m_mti_buff); } else { - mti_SetSignalValue(get_handle(), (long)m_mti_buff); + mti_SetSignalValue(get_handle(), (mtiLongT)m_mti_buff); } } @@ -446,9 +446,9 @@ int FliRealObjHdl::set_signal_value(const double value) m_mti_buff[0] = value; if (m_is_var) { - mti_SetVarValue(get_handle(), (long)m_mti_buff); + mti_SetVarValue(get_handle(), (mtiLongT)m_mti_buff); } else { - mti_SetSignalValue(get_handle(), (long)m_mti_buff); + mti_SetSignalValue(get_handle(), (mtiLongT)m_mti_buff); } return 0; @@ -497,9 +497,9 @@ int FliStringObjHdl::set_signal_value(std::string &value) strncpy(m_mti_buff, value.c_str(), m_num_elems); if (m_is_var) { - mti_SetVarValue(get_handle(), (long)m_mti_buff); + mti_SetVarValue(get_handle(), (mtiLongT)m_mti_buff); } else { - mti_SetSignalValue(get_handle(), (long)m_mti_buff); + mti_SetSignalValue(get_handle(), (mtiLongT)m_mti_buff); } return 0; From 18a599cded8dae97af5d602886c49415408f6cd5 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Thu, 7 Apr 2016 20:17:47 +1000 Subject: [PATCH 1201/2656] enable running array module test under windows --- tests/designs/array_module/Makefile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/designs/array_module/Makefile b/tests/designs/array_module/Makefile index 7aa22c8f..b4328a11 100644 --- a/tests/designs/array_module/Makefile +++ b/tests/designs/array_module/Makefile @@ -31,15 +31,14 @@ TOPLEVEL_LANG ?= verilog TOPLEVEL := array_module -PWD=$(shell pwd) -COCOTB?=$(PWD)/../../.. - ifeq ($(OS),Msys) WPWD=$(shell sh -c 'pwd -W') else WPWD=$(shell pwd) endif +COCOTB?=$(WPWD)/../../.. + ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES = $(COCOTB)/tests/designs/array_module/array_module.v else ifeq ($(TOPLEVEL_LANG),vhdl) From cc8c7f450133b82fe520730ea3452a0598b062fb Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Thu, 21 Apr 2016 14:36:23 -0700 Subject: [PATCH 1202/2656] Issue #438: Fix log.py to work with SPHINX_BUILD --- cocotb/log.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cocotb/log.py b/cocotb/log.py index 1a2c1496..9219bdfa 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -36,8 +36,10 @@ # For autodocumentation don't need the extension modules if "SPHINX_BUILD" in os.environ: simulator = None + _SIM_PRECISION = 1000 else: import simulator + _SIM_PRECISION = simulator.get_precision() import cocotb.ANSI as ANSI from pdb import set_trace @@ -146,8 +148,6 @@ def __getattr__(self, attribute): class SimLogFormatter(logging.Formatter): - - sim_precision = simulator.get_precision() """Log formatter to provide consistent log message handling.""" # Justify and truncate @@ -164,7 +164,7 @@ def rjust(string, chars): return string.rjust(chars) def _format(self, timeh, timel, level, record, msg): - time_ns = (timeh << 32 | timel) * (10.0**SimLogFormatter.sim_precision) / 1e-9 + time_ns = (timeh << 32 | timel) * (10.0**_SIM_PRECISION) / 1e-9 simtime = "%6.2fns" % (time_ns) prefix = simtime + ' ' + level + ' ' + \ self.ljust(record.name, _RECORD_CHARS) + \ From 503295fc54e561fbcd35448fce1ccb9bc2856976 Mon Sep 17 00:00:00 2001 From: Nevada Sanchez Date: Fri, 13 May 2016 15:41:59 -0400 Subject: [PATCH 1203/2656] Fix typo in handling of compat_mapping in handle.py. --- cocotb/handle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 79ed677a..69242445 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -124,7 +124,7 @@ def __setattr__(self, name, value): if name not in _deprecation_warned: warnings.warn("Use of %s attribute is deprecated" % name) _deprecation_warned[name] = True - return setattr(self, self._compat_mapping[name]) + return setattr(self, self._compat_mapping[name], value) else: return object.__setattr__(self, name, value) From ea048e7f639a676215145ac2e12f954dec6ee2c2 Mon Sep 17 00:00:00 2001 From: Jeremy Herbert Date: Mon, 6 Jun 2016 17:17:26 +1000 Subject: [PATCH 1204/2656] replaced list with deque in monitor and driver --- cocotb/drivers/__init__.py | 6 +++--- cocotb/monitors/__init__.py | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index 7d454a1f..b694c601 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -32,6 +32,7 @@ """ import logging +from collections import deque import cocotb from cocotb.decorators import coroutine @@ -93,7 +94,7 @@ def __init__(self): """ # self._busy = Lock() self._pending = Event(name="Driver._pending") - self._sendQ = [] + self._sendQ = deque() # Subclasses may already set up logging if not hasattr(self, "log"): @@ -126,7 +127,7 @@ def clear(self): """ Clear any queued transactions without sending them onto the bus """ - self._sendQ = [] + self._sendQ = deque() @coroutine def send(self, transaction, sync=True): @@ -339,4 +340,3 @@ def polled_socket_attachment(driver, sock): driver.log.info("Remote end closed the connection") break driver.append(data) - diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index 7a27f767..54404a62 100644 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -36,6 +36,7 @@ """ import math +from collections import deque import cocotb from cocotb.decorators import coroutine @@ -65,7 +66,7 @@ def __init__(self, callback=None, event=None): """ self._event = event self._wait_event = None - self._recvQ = [] + self._recvQ = deque() self._callbacks = [] self.stats = MonitorStatistics() self._wait_event = Event() From 7af6beba307847d7ca489d99ddb19cf12fc700fb Mon Sep 17 00:00:00 2001 From: Jeremy Herbert Date: Mon, 6 Jun 2016 17:31:19 +1000 Subject: [PATCH 1205/2656] changed pop(0) operation on deque to popleft() --- cocotb/drivers/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index b694c601..8b9615d9 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -186,7 +186,7 @@ def _send_thread(self): # Send in all the queued packets, # only synchronise on the first send while self._sendQ: - transaction, callback, event = self._sendQ.pop(0) + transaction, callback, event = self._sendQ.popleft() self.log.debug("Sending queued packet...") yield self._send(transaction, callback, event, sync=not synchronised) From 064ec14fddf1c32fbd69e64ac216b9dbd16abc09 Mon Sep 17 00:00:00 2001 From: Jeremy Herbert Date: Thu, 9 Jun 2016 18:10:35 -0700 Subject: [PATCH 1206/2656] scheduling a write during the read-only phase now generates an exception --- cocotb/scheduler.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 53fdbc61..aaefef16 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -375,6 +375,8 @@ def unschedule(self, coro): del coro._join def save_write(self, handle, value): + if self._mode == Scheduler._MODE_READONLY: + raise Exception("Write to object {} was scheduled during a read-only sync phase.".format(handle._name)) self._writes[handle] = value def _coroutine_yielded(self, coro, triggers): From 21864dfe141ffcc7e35d36dc764ca9985475f78b Mon Sep 17 00:00:00 2001 From: Jeremy Herbert Date: Thu, 9 Jun 2016 18:24:52 -0700 Subject: [PATCH 1207/2656] test added for write during read-only phase (issue #448), fixed related avalon driver error --- cocotb/drivers/avalon.py | 3 ++- tests/test_cases/test_cocotb/test_cocotb.py | 28 +++++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 4faabae7..56cbea2d 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -35,7 +35,7 @@ import cocotb from cocotb.decorators import coroutine -from cocotb.triggers import RisingEdge, ReadOnly, Event +from cocotb.triggers import RisingEdge, ReadOnly, NextTimeStep, Event from cocotb.drivers import BusDriver, ValidatedBusDriver from cocotb.utils import hexdump from cocotb.binary import BinaryValue @@ -359,6 +359,7 @@ def _respond(self): # toggle waitrequest # TODO: configure waitrequest time with avalon properties + yield NextTimeStep() # can't write during read-only phase self.bus.waitrequest <= 1 yield edge yield edge diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index d046f622..50a0506d 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -174,11 +174,19 @@ def do_test_readwrite_in_readonly(dut): global exited yield RisingEdge(dut.clk) yield ReadOnly() - dut.clk <= 0 yield ReadWrite() exited = True +@cocotb.coroutine +def do_test_cached_write_in_readonly(dut): + global exited + yield RisingEdge(dut.clk) + yield ReadOnly() + dut.clk <= 0 + exited = True + + @cocotb.coroutine def do_test_afterdelay_in_readonly(dut, delay): global exited @@ -204,6 +212,22 @@ def test_readwrite_in_readonly(dut): if exited is not True: raise TestFailure +@cocotb.test(expect_error=True, + expect_fail=cocotb.SIM_NAME.lower().startswith(("icarus", + "riviera", + "modelsim", + "ncsim"))) +def test_cached_write_in_readonly(dut): + """Test doing invalid sim operation""" + global exited + exited = False + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + coro = cocotb.fork(do_test_cached_write_in_readonly(dut)) + yield [Join(coro), Timer(10000)] + clk_gen.kill() + if exited is not True: + raise TestFailure + @cocotb.test(expect_fail=cocotb.SIM_NAME.lower().startswith(("icarus", "chronologic simulation vcs")), @@ -478,7 +502,7 @@ def test_binary_value(dut): Test out the cocotb supplied BinaryValue class for manipulating values in a style familiar to rtl coders. """ - + vec = BinaryValue(value=0,bits=16) dut.log.info("Checking default endianess is Big Endian.") if not vec.big_endian: From 046b1d93e089a589a74dee45288ebc6fd36055c5 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 13 Jun 2016 05:20:55 -0500 Subject: [PATCH 1208/2656] Add support for SCRIPT_FILE to Makefile.questa for consistency --- makefiles/simulators/Makefile.questa | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 6e491f80..d9fa92e5 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -83,15 +83,16 @@ VSIM_ARGS += $(foreach gen, $(GENERICS),"-G $(gen)") endif $(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) $(SIM_BUILD) - echo "if [file exists $(RTL_LIBRARY)] {vdel -lib $(RTL_LIBRARY) -all}" > $@ - echo "vlib $(RTL_LIBRARY)" >> $@ ifneq ($(VHDL_SOURCES),) echo "vcom -work $(RTL_LIBRARY) $(VCOM_ARGS) $(VHDL_SOURCES)" >> $@ endif ifneq ($(VERILOG_SOURCES),) echo "vlog -work $(RTL_LIBRARY) +define+COCOTB_SIM -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(VERILOG_SOURCES)" >> $@ endif - echo "vsim $(VSIM_ARGS) $(RTL_LIBRARY).$(TOPLEVEL)" >> $@ +ifdef SCRIPT_FILE + echo "do $(SCRIPT_FILE)" >> $@ +endif + echo "vsim $(VSIM_ARGS) $(TOPLEVEL)" >> $@ ifeq ($(WAVES),1) echo "log -recursive /*" >> $@ endif From 5c9c6d47d96a0aff91aaf9d06ec053773c168299 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 13 Jun 2016 06:06:17 -0500 Subject: [PATCH 1209/2656] Correct bug in result.xml reporting of a simulator originated failure via $fatal --- cocotb/regression.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cocotb/regression.py b/cocotb/regression.py index 64d24188..b9234a4c 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -238,6 +238,9 @@ def _result_was(): else: self.log.error("Test error has lead to simulator shuttting us " "down") + self.xunit.add_failure(stdout=repr(str(result)), + stderr="\n".join( + self._running_test.error_messages)) self.failures += 1 self.tear_down() return From 6b664f7d842574c0e3ac9206a181713d912df29c Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 13 Jun 2016 06:06:59 -0500 Subject: [PATCH 1210/2656] Uprev version to 1.0.1 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index d53e27cf..1f29be95 100644 --- a/version +++ b/version @@ -1 +1 @@ -VERSION=1.0 +VERSION=1.0.1 From feaa765c3fe62134f28ed0d708ca3ba10080d2ee Mon Sep 17 00:00:00 2001 From: Tristan Gingold Date: Thu, 16 Jun 2016 20:27:52 +0200 Subject: [PATCH 1211/2656] Fix Makefile.ghdl Always use CMD for ghdl command Run simulation in the same directory as the analysis. --- makefiles/simulators/Makefile.ghdl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/makefiles/simulators/Makefile.ghdl b/makefiles/simulators/Makefile.ghdl index fa686e2a..3249029f 100644 --- a/makefiles/simulators/Makefile.ghdl +++ b/makefiles/simulators/Makefile.ghdl @@ -54,12 +54,13 @@ endif # Compilation phase analyse: $(VHDL_SOURCES) $(SIM_BUILD) - cd $(SIM_BUILD) && $(CMD) -a $(VHDL_SOURCES) && $(GHDL) -e $(TOPLEVEL) + cd $(SIM_BUILD) && $(CMD) -a $(VHDL_SOURCES) && $(CMD) -e $(TOPLEVEL) results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VPI_LIB) + cd $(SIM_BUILD); \ PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(CMD) -r --workdir=$(SIM_BUILD) $(TOPLEVEL) --vpi=$(LIB_DIR)/libvpi.$(LIB_EXT) + $(CMD) -r $(TOPLEVEL) --vpi=$(LIB_DIR)/libvpi.$(LIB_EXT) clean:: -@rm -rf $(SIM_BUILD) From 548f3a820e40fab46387054d370c501f8cec2e58 Mon Sep 17 00:00:00 2001 From: Nathan Kohagen Date: Thu, 16 Jun 2016 16:28:43 -0700 Subject: [PATCH 1212/2656] ClockCycles() now sets edge parameter when scheduling with simulator Sets simulator.register_value_change_callback() edge parameter correctly (wasn't set before) Added parameter "rising" for selecting rising (default) or falling edges unprime() called at top of callback _check() --- cocotb/triggers.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 4c8da7f3..da6f81a2 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -280,16 +280,22 @@ def FallingEdge(signal): class ClockCycles(_Edge): """ - Execution will resume after N rising edges + Execution will resume after N rising edges or N falling edges """ - def __init__(self, signal, num_cycles): + def __init__(self, signal, num_cycles, rising=True): Edge.__init__(self, signal) self.num_cycles = num_cycles + if rising is True: + self._rising = 1 + else: + self._rising = 2 def prime(self, callback): self._callback = callback def _check(obj): + self.unprime() + if self.signal.value: self.num_cycles -= 1 @@ -300,6 +306,7 @@ def _check(obj): self.cbhdl = simulator.register_value_change_callback(self.signal. _handle, _check, + self._rising, self) if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) @@ -307,6 +314,7 @@ def _check(obj): self.cbhdl = simulator.register_value_change_callback(self.signal. _handle, _check, + self._rising, self) if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) From 115ab03322e7bc8d19c9fff49cf2050ba9e25712 Mon Sep 17 00:00:00 2001 From: Jeremy Herbert Date: Sun, 19 Jun 2016 15:16:42 +1000 Subject: [PATCH 1213/2656] fixed PYTHON_BIN in Darwin makefile --- makefiles/Makefile.pylib.Darwin | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/makefiles/Makefile.pylib.Darwin b/makefiles/Makefile.pylib.Darwin index cf746797..d4598e9d 100644 --- a/makefiles/Makefile.pylib.Darwin +++ b/makefiles/Makefile.pylib.Darwin @@ -29,6 +29,13 @@ # All common pyhon related rules +ifneq ($(COCOTB_PYTHON_DEBUG),) + DBG_PYTHON_VERSION=$(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') + PYTHON_BIN=python$(DBG_PYTHON_VERSION)-dbg +else + PYTHON_BIN=python +endif + NATIVE_ARCH=$(shell uname -m) ARCH?=$(NATIVE_ARCH) From f6732318edc4ac7b065b386357f05994d8d266f2 Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Tue, 21 Jun 2016 12:59:47 +1000 Subject: [PATCH 1214/2656] doc : instructions on how to get the required packages installed for mac This may not be definitive, I'm guessing I already had a bunch of the required packages installed before I tried cocotb on my mac. --- documentation/source/quickstart.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index c03fc056..918458c5 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -108,6 +108,19 @@ It is beneficial to add the path to Python to the windows system PATH variable s Once inside the Msys shell commands as given here will work as expected. +MAC Packages +------------ + +You need a few packages installed to get cocotb running on mac. +Installing a package manager really helps things out here. + +Brew_ seems to be the most popular, so we'll assume you have that installed. +.. _Brew: http://www.brew.sh + +.. code-block::bash + + $> brew install python icarus-verilog gtkwave + Running an example ------------------ From 4849f85d289f824c1628b241c08104be90a866c3 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Tue, 21 Jun 2016 15:27:49 +0200 Subject: [PATCH 1215/2656] BUG: fix BinaryValue size on discovery --- cocotb/handle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 67c907e7..f3f70bcd 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -570,8 +570,8 @@ def setimmediatevalue(self, value): simulator.set_signal_val_str(self._handle, value.binstr) def _getvalue(self): - result = BinaryValue() - result.binstr = simulator.get_signal_val_binstr(self._handle) + binstr = simulator.get_signal_val_binstr(self._handle) + result = BinaryValue(binstr, len(binstr)) return result def _setcachedvalue(self, value): From ddc8bd1592ef37c5024890aa2a5f92dfe2c42e3a Mon Sep 17 00:00:00 2001 From: elgorwi Date: Thu, 23 Jun 2016 20:42:38 +1000 Subject: [PATCH 1216/2656] Fix compile error under 32bit modelsim: could not convert from 'uint64_t {aka long long unsigned int}' to 'mtiTime64T {aka mtiTime64Union_}' --- lib/fli/FliCbHdl.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/fli/FliCbHdl.cpp b/lib/fli/FliCbHdl.cpp index 6e16a646..ce56c2bf 100644 --- a/lib/fli/FliCbHdl.cpp +++ b/lib/fli/FliCbHdl.cpp @@ -55,7 +55,13 @@ FliTimedCbHdl::FliTimedCbHdl(GpiImplInterface *impl, int FliTimedCbHdl::arm_callback(void) { - mti_ScheduleWakeup64(m_proc_hdl, m_time_ps); + #if defined(__LP64__) || defined(_WIN64) + mti_ScheduleWakeup64(m_proc_hdl, m_time_ps); + #else + mtiTime64T m_time_union_ps; + MTI_TIME64_ASGN(m_time_union_ps, (mtiInt32T)((m_time_ps) >> 32), (mtiUInt32T)(m_time_ps)); + mti_ScheduleWakeup64(m_proc_hdl, m_time_union_ps); + #endif m_sensitised = true; set_call_state(GPI_PRIMED); return 0; From 9e499657b31b495eb7b421febecfa5314d0a4f78 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Fri, 24 Jun 2016 15:23:00 +0200 Subject: [PATCH 1217/2656] cocotb/drivers/avalon.py: adding write burst on avalon-MM --- cocotb/drivers/avalon.py | 115 ++++++++++++++++++++++++++++++++------- 1 file changed, 96 insertions(+), 19 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index e3cfd3fb..f0a1b1a6 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -227,6 +227,8 @@ class AvalonMemory(BusDriver): "burstCountUnits": "symbols", # symbols or words "addressUnits": "symbols", # symbols or words "readLatency": 1, # number of cycles + "WriteBurstWaitReq": True, # generate random waitrequest + "MaxWaitReqLen": 4, # maximum value of waitrequest } def __init__(self, entity, name, clock, readlatency_min=1, @@ -251,6 +253,7 @@ def __init__(self, entity, name, clock, readlatency_min=1, if hasattr(self.bus, "readdata"): self._width = len(self.bus.readdata) + self.dataByteSize = self._width/8 self._readable = True if hasattr(self.bus, "writedata"): @@ -259,13 +262,9 @@ def __init__(self, entity, name, clock, readlatency_min=1, self.log.error("readdata and writedata bus" + " are not the same size") self._width = width + self.dataByteSize = self._width/8 self._writeable = True - if hasattr(self.bus, "burstcount"): - if hasattr(self.bus, "readdatavalid"): - self._burstread = True - self._burstwrite = True - if not self._readable and not self._writeable: raise TestError("Attempt to instantiate useless memory") @@ -287,6 +286,16 @@ def __init__(self, entity, name, clock, readlatency_min=1, if hasattr(self.bus, "waitrequest"): self.bus.waitrequest.setimmediatevalue(0) + if hasattr(self.bus, "burstcount"): + if hasattr(self.bus, "readdatavalid"): + self._burstread = True + self._burstwrite = True + if self._avalon_properties.get("WriteBurstWaitReq", True): + self.bus.waitrequest <= 1 + else: + self.bus.waitrequest <= 0 + + if hasattr(self.bus, "readdatavalid"): self.bus.readdatavalid.setimmediatevalue(0) @@ -315,13 +324,59 @@ def _do_response(self): elif hasattr(self.bus, "readdatavalid"): self.bus.readdatavalid <= 0 + def _write_burst_addr(self): + """ reading write burst address, burstcount, byteenable """ + addr = self.bus.address.value.integer + if addr % self.dataByteSize != 0: + self.log.error("Address must be aligned to data width" + + "(addr = " + hex(addr) + + ", width = " + str(self._width)) + + byteenable = self.bus.byteenable.value + if byteenable != int("1"*len(self.bus.byteenable), 2): + self.log.error("Only full word access is supported " + + "for burst write (byteenable must be " + + "0b" + "1" * len(self.bus.byteenable) + + ")") + + burstcount = self.bus.burstcount.value.integer + if burstcount == 0: + self.log.error("Write burstcount must be 1 at least") + + return (addr, byteenable, burstcount) + + @coroutine + def _writing_byte_value(self, byteaddr): + """Writing value in _mem with byteaddr size """ + yield FallingEdge(self.clock) + for i in range(self.dataByteSize): + data = self.bus.writedata.value.integer + addrtmp = byteaddr + i + datatmp = (data >> (i*8)) & 0xff + self._mem[addrtmp] = datatmp + + @coroutine + def _waitrequest(self): + """ generate waitrequest randomly """ + if self._avalon_properties.get("WriteBurstWaitReq", True): + if random.choice([True, False, False, False]): + randmax = self._avalon_properties.get("MaxWaitReqLen", 0) + waitingtime = range(random.randint(0, randmax)) + for waitreq in waitingtime: + self.bus.waitrequest <= 1 + yield RisingEdge(self.clock) + else: + yield NextTimeStep() + + self.bus.waitrequest <= 0 + + @coroutine def _respond(self): """ Coroutine to response to the actual requests """ edge = RisingEdge(self.clock) - dataByteSize = self._width/8 while True: yield edge self._do_response() @@ -342,11 +397,11 @@ def _respond(self): self._responses.append(self._mem[addr]) else: addr = self.bus.address.value.integer - if addr % dataByteSize != 0: + if addr % self.dataByteSize != 0: self.log.error("Address must be aligned to data width" + "(addr = " + hex(addr) + ", width = " + str(self._width)) - addr = addr / dataByteSize + addr = addr / self.dataByteSize burstcount = self.bus.burstcount.value.integer byteenable = self.bus.byteenable.value if byteenable != int("1"*len(self.bus.byteenable), 2): @@ -368,30 +423,52 @@ def _respond(self): # wait for read data for i in range(self._avalon_properties["readLatency"]): yield edge - for count in range(burstcount): - if (addr + count)*dataByteSize not in self._mem: + if (addr + count)*self.dataByteSize not in self._mem: self.log.warning( "Attempt to burst read from uninitialised " + "address 0x%x (addr 0x%x count 0x%x)" % - ((addr + count)*dataByteSize, addr, count) ) + ((addr + count)*self.dataByteSize, addr, count) ) self._responses.append(True) else: value = 0 - for i in range(dataByteSize): - value +=\ - self._mem[(addr + count)*dataByteSize + i] << i*8 + for i in range(self.dataByteSize): + rvalue = self._mem[(addr + count)*self.dataByteSize + i] + value += rvalue << i*8 self.log.debug("Read from address 0x%x returning 0x%x" % - (addr*dataByteSize, value)) + ((addr + count)*self.dataByteSize, value)) self._responses.append(value) yield edge self._do_response() if self._writeable and self.bus.write.value: - addr = self.bus.address.value.integer - data = self.bus.writedata.value.integer - self.log.debug("Write to address 0x%x -> 0x%x" % (addr, data)) - self._mem[addr] = data + if not self._burstwrite: + addr = self.bus.address.value.integer + data = self.bus.writedata.value.integer + self.log.debug("Write to address 0x%x -> 0x%x" % (addr, data)) + self._mem[addr] = data + else: + self.log.debug("writing burst") + # maintain waitrequest high randomly + yield self._waitrequest() + + addr, byteenable, burstcount = self._write_burst_addr() + + count = 0 + for count in range(burstcount): + while self.bus.write.value == 0: + yield NextTimeStep() + # self._mem is aligned on 8 bits words + yield self._writing_byte_value(addr + count*self.dataByteSize) + self.log.debug("writing %016X @ %08X"%( + self.bus.writedata.value.integer, + addr + count*self.dataByteSize)) + yield edge + # generate waitrequest randomly + yield self._waitrequest() + + if self._avalon_properties.get("WriteBurstWaitReq", True): + self.bus.waitrequest <= 1 class AvalonST(ValidatedBusDriver): From 88b29ded3cc303054c2f653aa85685ec14046c51 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Fri, 24 Jun 2016 15:27:30 +0200 Subject: [PATCH 1218/2656] Makefile.ghdl: adding simulator args --- makefiles/simulators/Makefile.ghdl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.ghdl b/makefiles/simulators/Makefile.ghdl index 3249029f..502e64ea 100644 --- a/makefiles/simulators/Makefile.ghdl +++ b/makefiles/simulators/Makefile.ghdl @@ -60,7 +60,7 @@ results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VPI_LIB) cd $(SIM_BUILD); \ PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(CMD) -r $(TOPLEVEL) --vpi=$(LIB_DIR)/libvpi.$(LIB_EXT) + $(CMD) -r $(TOPLEVEL) --vpi=$(LIB_DIR)/libvpi.$(LIB_EXT) $(SIM_ARGS) clean:: -@rm -rf $(SIM_BUILD) From ed4f43b4e2904a5cda08b06addd09af26784f505 Mon Sep 17 00:00:00 2001 From: Martin Zabel Date: Fri, 24 Jun 2016 18:10:02 +0200 Subject: [PATCH 1219/2656] Pass RTL_LIBRARY to GHDL analyse and elaborate. --- makefiles/simulators/Makefile.ghdl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.ghdl b/makefiles/simulators/Makefile.ghdl index 3249029f..ab01bf50 100644 --- a/makefiles/simulators/Makefile.ghdl +++ b/makefiles/simulators/Makefile.ghdl @@ -54,7 +54,7 @@ endif # Compilation phase analyse: $(VHDL_SOURCES) $(SIM_BUILD) - cd $(SIM_BUILD) && $(CMD) -a $(VHDL_SOURCES) && $(CMD) -e $(TOPLEVEL) + cd $(SIM_BUILD) && $(CMD) -a --work=$(RTL_LIBRARY) $(VHDL_SOURCES) && $(CMD) -e --work=$(RTL_LIBRARY) $(TOPLEVEL) results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VPI_LIB) cd $(SIM_BUILD); \ From 8abe2c81f7566a193b47c962f0b9ab7ae67872c3 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Mon, 27 Jun 2016 11:58:57 +0200 Subject: [PATCH 1220/2656] Documentation: SIM_ARGS is available with GHDL --- documentation/source/building.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/building.rst b/documentation/source/building.rst index f3ff2b71..4755cf75 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -59,7 +59,7 @@ Any arguments or flags to pass to the compile stage of the simulation. Only appl SIM_ARGS ~~~~~~~~ -Any arguments or flags to pass to the execution of the compiled simulation. Only applies to simulators with a separate compilation stage (currently Icarus and VCS). +Any arguments or flags to pass to the execution of the compiled simulation. Only applies to simulators with a separate compilation stage (currently Icarus, VCS and GHDL). EXTRA_ARGS ~~~~~~~~~~ From 0d09d6fb603fd01b83b9125e5d9a2772e470a8c8 Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Tue, 28 Jun 2016 15:56:02 +1000 Subject: [PATCH 1221/2656] questa needs this switch to run on 64-bit machines. --- makefiles/simulators/Makefile.questa | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 6e491f80..c63033eb 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -126,6 +126,10 @@ endif INT_LIBS := $(COCOTB_VPI_LIB) $(COCOTB_FLI_LIB) +ifneq ($(ARCH),i686) +CMD += -64 +endif + # Depending on the version of modelsim the interfaces that it supports can change # Append or remove values to INT_LIBS depenending on your license results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) From efe59d4b609c02047e75387d31782209031850aa Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Mon, 27 Jun 2016 21:42:31 +1000 Subject: [PATCH 1222/2656] fix for runaway sims on ius - #463 Calling vpi_control in a callback with reason cbStartOfSimulation doesn't stop a simulation in ius. --- lib/vpi/VpiCbHdl.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index fff06355..7662f58c 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -430,7 +430,14 @@ int VpiValueCbHdl::cleanup_callback(void) VpiStartupCbHdl::VpiStartupCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), VpiCbHdl(impl) { +#ifndef IUS cb_data.reason = cbStartOfSimulation; +#else + vpi_time.high = (uint32_t)(0); + vpi_time.low = (uint32_t)(0); + vpi_time.type = vpiSimTime; + cb_data.reason = cbAfterDelay; +#endif } int VpiStartupCbHdl::run_callback(void) { From d44ef0eab6e76971b9ae3fcff2ac6790088005c2 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 11 Jul 2016 05:01:54 -0500 Subject: [PATCH 1223/2656] Update Questasim makefile to avoid tracing waves which crashes it on big designs --- makefiles/simulators/Makefile.questa | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 6e491f80..2077df27 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -83,20 +83,22 @@ VSIM_ARGS += $(foreach gen, $(GENERICS),"-G $(gen)") endif $(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) $(SIM_BUILD) - echo "if [file exists $(RTL_LIBRARY)] {vdel -lib $(RTL_LIBRARY) -all}" > $@ - echo "vlib $(RTL_LIBRARY)" >> $@ + echo "# Autogenerated file" > $@ ifneq ($(VHDL_SOURCES),) echo "vcom -work $(RTL_LIBRARY) $(VCOM_ARGS) $(VHDL_SOURCES)" >> $@ endif ifneq ($(VERILOG_SOURCES),) echo "vlog -work $(RTL_LIBRARY) +define+COCOTB_SIM -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(VERILOG_SOURCES)" >> $@ endif - echo "vsim $(VSIM_ARGS) $(RTL_LIBRARY).$(TOPLEVEL)" >> $@ +ifdef SCRIPT_FILE + echo "do $(SCRIPT_FILE)" >> $@ +endif + echo "vsim $(VSIM_ARGS) $(TOPLEVEL)" >> $@ ifeq ($(WAVES),1) echo "log -recursive /*" >> $@ endif ifeq ($(GUI),1) - echo "add wave -recursive /*" >> $@ + echo "log -recursive /*" >> $@ else echo "onbreak resume" >> $@ echo "run -all" >> $@ @@ -132,6 +134,8 @@ results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) \ $(CMD) -do runsim.do 2>&1 | tee sim.log + # Potential fix for buffered stdout, YMMV + #STDOUT=$(SIM_BUILD)/sim.log stdbuf --output=0 $(CMD) -do runsim.do 2>&1 && stdbuf --output=0 --input=0 tail -f sim.log && exit $${PIPESTATUS[0]} clean:: -rm -rf $(SIM_BUILD) From 9222e7bdc8c9bdfa7d195a487b50c23be3f836cd Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 11 Jul 2016 05:03:34 -0500 Subject: [PATCH 1224/2656] Justify simulation time to avoid logs changing alignment during longrunning simulations --- cocotb/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/log.py b/cocotb/log.py index 1a2c1496..e93de427 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -166,7 +166,7 @@ def rjust(string, chars): def _format(self, timeh, timel, level, record, msg): time_ns = (timeh << 32 | timel) * (10.0**SimLogFormatter.sim_precision) / 1e-9 simtime = "%6.2fns" % (time_ns) - prefix = simtime + ' ' + level + ' ' + \ + prefix = simtime.rjust(10) + ' ' + level + ' ' + \ self.ljust(record.name, _RECORD_CHARS) + \ self.rjust(os.path.split(record.filename)[1], _FILENAME_CHARS) + \ ':' + self.ljust(str(record.lineno), _LINENO_CHARS) + \ From 22cccd57b8eb0f522b980327a8039797811dffa0 Mon Sep 17 00:00:00 2001 From: Martin Zabel Date: Tue, 12 Jul 2016 18:04:46 +0200 Subject: [PATCH 1225/2656] Added default value for RTL_LIBRARY in Makefile.ghdl. --- makefiles/simulators/Makefile.ghdl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/makefiles/simulators/Makefile.ghdl b/makefiles/simulators/Makefile.ghdl index 4c86330d..7c1bd78d 100644 --- a/makefiles/simulators/Makefile.ghdl +++ b/makefiles/simulators/Makefile.ghdl @@ -50,6 +50,8 @@ else export GHDL_BIN_DIR endif +RTL_LIBRARY ?= work + .PHONY: analyse # Compilation phase From a2085c9d912ea545574f1133f86c88b24f02f6d2 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Wed, 6 Jul 2016 11:47:00 -0700 Subject: [PATCH 1226/2656] Issue #388: Add utility function to get simulation time and update log.py to use it --- cocotb/log.py | 19 ++++++------------- cocotb/utils.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/cocotb/log.py b/cocotb/log.py index 9219bdfa..6b3109c6 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -33,13 +33,8 @@ import sys import logging import inspect -# For autodocumentation don't need the extension modules -if "SPHINX_BUILD" in os.environ: - simulator = None - _SIM_PRECISION = 1000 -else: - import simulator - _SIM_PRECISION = simulator.get_precision() + +from cocotb.utils import get_sim_time import cocotb.ANSI as ANSI from pdb import set_trace @@ -163,8 +158,8 @@ def rjust(string, chars): return ".." + string[(chars - 2) * -1:] return string.rjust(chars) - def _format(self, timeh, timel, level, record, msg): - time_ns = (timeh << 32 | timel) * (10.0**_SIM_PRECISION) / 1e-9 + def _format(self, level, record, msg): + time_ns = get_sim_time('ns') simtime = "%6.2fns" % (time_ns) prefix = simtime + ' ' + level + ' ' + \ self.ljust(record.name, _RECORD_CHARS) + \ @@ -184,9 +179,8 @@ def format(self, record): msg = str(msg) level = record.levelname.ljust(_LEVEL_CHARS) - timeh, timel = simulator.get_sim_time() - return self._format(timeh, timel, level, record, msg) + return self._format(level, record, msg) class SimColourLogFormatter(SimLogFormatter): @@ -212,5 +206,4 @@ def format(self, record): level = (SimColourLogFormatter.loglevel2colour[record.levelno] % record.levelname.ljust(_LEVEL_CHARS)) - timeh, timel = simulator.get_sim_time() - return self._format(timeh, timel, level, record, msg) + return self._format(level, record, msg) diff --git a/cocotb/utils.py b/cocotb/utils.py index 88479666..4c6e5a31 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -29,8 +29,17 @@ """Collection of handy functions""" +import os import ctypes +# For autodocumentation don't need the extension modules +if "SPHINX_BUILD" in os.environ: + simulator = None + _SIM_PRECISION = 1000 +else: + import simulator + _SIM_PRECISION = simulator.get_precision() # request once and cache + # python2 to python3 helper functions def get_python_integer_types(): @@ -41,6 +50,40 @@ def get_python_integer_types(): else: return (int, long) # python 2 +# Simulator helper functions +def get_sim_time(units=None): + """Retrieves the simulation time from the simulator + + Kwargs: + units (str): String specifying the units of the result. (None,'fs','ps','ns','us','ms','sec','min','hr') + None will return the raw simulation time. + + Returns: + The simulation time in the specified units + """ + scale = { + 'fs' : 1.0e-15, + 'ps' : 1.0e-12, + 'ns' : 1.0e-9, + 'us' : 1.0e-6, + 'ms' : 1.0e-3, + 'sec': 1.0, + 'min': 60.0, + 'hr' : 3600.0} + + timeh, timel = simulator.get_sim_time() + + result = (timeh << 32 | timel) + + if units is not None: + units_lwr = units.lower() + if units_lwr not in scale: + raise ValueError("Invalid unit ({}) provided".format(units)) + else: + result = result * (10.0**_SIM_PRECISION) / scale[units_lwr] + + return result + # Ctypes helper functions From 70b768b0782b7148243d881f14c61e95c07e61c0 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Wed, 6 Jul 2016 12:08:12 -0700 Subject: [PATCH 1227/2656] Issue #388: Add sim_time in ns and ratio (sim_time/real_time) to results --- cocotb/decorators.py | 3 +++ cocotb/regression.py | 13 ++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index d981b8da..e2b24b4d 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -39,6 +39,7 @@ from cocotb.triggers import _Join, PythonTrigger, Timer, Event, NullTrigger from cocotb.result import (TestComplete, TestError, TestFailure, TestSuccess, ReturnValue, raise_error) +from cocotb.utils import get_sim_time def public(f): @@ -175,6 +176,7 @@ def __init__(self, inst, parent): RunningCoroutine.__init__(self, inst, parent) self.started = False self.start_time = 0 + self.start_sim_time = 0 self.expect_fail = parent.expect_fail self.expect_error = parent.expect_error self.skip = parent.skip @@ -188,6 +190,7 @@ def send(self, value): self.log.info("Starting test: \"%s\"\nDescription: %s" % (self.funcname, self.__doc__)) self.start_time = time.time() + self.start_sim_time = get_sim_time('ns') self.started = True try: self.log.debug("Sending trigger %s" % (str(value))) diff --git a/cocotb/regression.py b/cocotb/regression.py index 64d24188..b3ec6925 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -56,6 +56,7 @@ import cocotb.ANSI as ANSI from cocotb.log import SimLog from cocotb.result import TestError, TestFailure, TestSuccess, SimFailure +from cocotb.utils import get_sim_time from cocotb.xunit_reporter import XUnitReporter @@ -145,7 +146,9 @@ def initialise(self): self.log.info("Skipping test %s" % thing.name) self.xunit.add_testcase(name=thing.name, classname=module_name, - time="0.0") + time="0.0", + sim_time_ns="0.0", + ratio_time="0.0") self.xunit.add_skipped() self.skipped += 1 else: @@ -190,10 +193,14 @@ def handle_result(self, result): Args: result (TestComplete exception) """ + real_time = time.time() - self._running_test.start_time + sim_time_ns = get_sim_time('ns') - self._running_test.start_sim_time + ratio_time = sim_time_ns / real_time self.xunit.add_testcase(name=self._running_test.funcname, classname=self._running_test.module, - time=repr(time.time() - - self._running_test.start_time)) + time=repr(real_time), + sim_time_ns=repr(sim_time_ns), + ratio_time=repr(ratio_time)) running_test_funcname = self._running_test.funcname From 4664017018efd5e5eb4d76cfd47556dd3ba67278 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Thu, 7 Jul 2016 13:25:24 -0700 Subject: [PATCH 1228/2656] Issue #388: Add a test summary and simulation summary banner --- cocotb/regression.py | 86 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/cocotb/regression.py b/cocotb/regression.py index b3ec6925..3904512f 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -89,6 +89,8 @@ def __init__(self, root_name, modules, tests=None): def initialise(self): + self.start_time = time.time() + self.test_results = [] self.ntests = 0 self.count = 1 self.skipped = 0 @@ -151,6 +153,7 @@ def initialise(self): ratio_time="0.0") self.xunit.add_skipped() self.skipped += 1 + self._store_test_result(module_name, thing.name, None, 0.0, 0.0, 0.0) else: self._queue.append(test) self.ntests += 1 @@ -176,6 +179,8 @@ def tear_down(self): self.log.info("Writing coverage data") self._cov.save() self._cov.html_report() + self._log_test_summary() + self._log_sim_summary() self.log.info("Shutting down...") self.xunit.write() simulator.stop_simulator() @@ -210,6 +215,8 @@ def _result_was(): (running_test_funcname, result.__class__.__name__)) return result_was + result_pass = True + if (isinstance(result, TestSuccess) and not self._running_test.expect_fail and not self._running_test.expect_error): @@ -227,6 +234,7 @@ def _result_was(): stderr="\n".join( self._running_test.error_messages)) self.failures += 1 + result_pass = False elif isinstance(result, TestSuccess): self.log.error("Test passed but we expected a failure: " + @@ -235,6 +243,7 @@ def _result_was(): stderr="\n".join( self._running_test.error_messages)) self.failures += 1 + result_pass = False elif isinstance(result, TestError) and self._running_test.expect_error: self.log.info("Test errored as expected: " + _result_was()) @@ -246,6 +255,7 @@ def _result_was(): self.log.error("Test error has lead to simulator shuttting us " "down") self.failures += 1 + self._store_test_result(self._running_test.module, self._running_test.funcname, False, sim_time_ns, real_time, ratio_time) self.tear_down() return @@ -255,6 +265,9 @@ def _result_was(): stderr="\n".join( self._running_test.error_messages)) self.failures += 1 + result_pass = False + + self._store_test_result(self._running_test.module, self._running_test.funcname, result_pass, sim_time_ns, real_time, ratio_time) self.execute() @@ -275,6 +288,79 @@ def execute(self): else: self.tear_down() + def _log_test_summary(self): + TEST_FIELD = 'TEST' + RESULT_FIELD = 'PASS/FAIL' + SIM_FIELD = 'SIM TIME(NS)' + REAL_FIELD = 'REAL TIME(S)' + RATIO_FIELD = 'RATIO(NS/S)' + + TEST_FIELD_LEN = max(len(TEST_FIELD),len(max([x['test'] for x in self.test_results],key=len))) + RESULT_FIELD_LEN = len(RESULT_FIELD) + SIM_FIELD_LEN = len(SIM_FIELD) + REAL_FIELD_LEN = len(REAL_FIELD) + RATIO_FIELD_LEN = len(RATIO_FIELD) + + LINE_LEN = 3 + TEST_FIELD_LEN + 2 + RESULT_FIELD_LEN + 2 + SIM_FIELD_LEN + 2 + REAL_FIELD_LEN + 2 + RATIO_FIELD_LEN + 3 + + LINE_SEP = "*"*LINE_LEN+"\n" + + summary = "" + summary += LINE_SEP + summary += "** {a:<{a_len}} {b:^{b_len}} {c:>{c_len}} {d:>{d_len}} {e:>{e_len}} **\n".format(a=TEST_FIELD, a_len=TEST_FIELD_LEN, + b=RESULT_FIELD, b_len=RESULT_FIELD_LEN, + c=SIM_FIELD, c_len=SIM_FIELD_LEN, + d=REAL_FIELD, d_len=REAL_FIELD_LEN, + e=RATIO_FIELD, e_len=RATIO_FIELD_LEN) + summary += LINE_SEP + for result in self.test_results: + dflt = ANSI.DEFAULT_FG + ANSI.DEFAULT_BG + hilite = dflt + + if result['pass'] is None: + pass_fail_str = "N/A" + elif result['pass']: + pass_fail_str = "PASS" + else: + pass_fail_str = "FAIL" + hilite = ANSI.WHITE_FG + ANSI.RED_BG + + summary += "{start}** {a:<{a_len}} {b:^{b_len}} {c:>{c_len}.2f} {d:>{d_len}.2f} {e:>{e_len}.2f} **{end}\n".format(a=result['test'], a_len=TEST_FIELD_LEN, + b=pass_fail_str, b_len=RESULT_FIELD_LEN, + c=result['sim'], c_len=SIM_FIELD_LEN-1, + d=result['real'], d_len=REAL_FIELD_LEN-1, + e=result['ratio'], e_len=RATIO_FIELD_LEN-1, + start=hilite, end=dflt) + summary += LINE_SEP + + self.log.info(summary) + + def _log_sim_summary(self): + real_time = time.time() - self.start_time + sim_time_ns = get_sim_time('ns') + ratio_time = sim_time_ns / real_time + + summary = "" + + summary += "*************************************************************************************\n" + summary += "** ERRORS : {:<39}**\n".format(self.failures) + summary += "*************************************************************************************\n" + summary += "** SIM TIME : {:<39}**\n".format('{:.2f} NS'.format(sim_time_ns)) + summary += "** REAL TIME : {:<39}**\n".format('{:.2f} S'.format(real_time)) + summary += "** SIM / REAL TIME : {:<39}**\n".format('{:.2f} NS/S'.format(ratio_time)) + summary += "*************************************************************************************\n" + + self.log.info(summary) + + def _store_test_result(self, module_name, test_name, result_pass, sim_time, real_time, ratio): + result = { + 'test' : '.'.join([module_name, test_name]), + 'pass' : result_pass, + 'sim' : sim_time, + 'real' : real_time, + 'ratio' : ratio} + self.test_results.append(result) + def _create_test(function, name, documentation, mod, *args, **kwargs): """Factory function to create tests, avoids late binding From 5e23f41b2b2009b81db95accc847008c53a63234 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 11 Jul 2016 06:52:47 -0700 Subject: [PATCH 1229/2656] Add the ability to suppress some logging information through an environment variable --- cocotb/log.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/cocotb/log.py b/cocotb/log.py index 6b3109c6..75ef3a15 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -39,6 +39,11 @@ import cocotb.ANSI as ANSI from pdb import set_trace +if "COCOTB_REDUCED_LOG_FMT" in os.environ: + _suppress = True +else: + _suppress = False + # Column alignment _LEVEL_CHARS = len("CRITICAL") # noqa _RECORD_CHARS = 35 # noqa @@ -161,11 +166,13 @@ def rjust(string, chars): def _format(self, level, record, msg): time_ns = get_sim_time('ns') simtime = "%6.2fns" % (time_ns) - prefix = simtime + ' ' + level + ' ' + \ - self.ljust(record.name, _RECORD_CHARS) + \ - self.rjust(os.path.split(record.filename)[1], _FILENAME_CHARS) + \ - ':' + self.ljust(str(record.lineno), _LINENO_CHARS) + \ - ' in ' + self.ljust(str(record.funcName), _FUNCNAME_CHARS) + ' ' + + prefix = simtime + ' ' + level + ' ' + if not _suppress: + prefix += self.ljust(record.name, _RECORD_CHARS) + \ + self.rjust(os.path.split(record.filename)[1], _FILENAME_CHARS) + \ + ':' + self.ljust(str(record.lineno), _LINENO_CHARS) + \ + ' in ' + self.ljust(str(record.funcName), _FUNCNAME_CHARS) + ' ' pad = "\n" + " " * (len(prefix)) return prefix + pad.join(msg.split('\n')) From d2eeba5ab59448b80173f62741e959871b02bd7d Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 11 Jul 2016 07:02:25 -0700 Subject: [PATCH 1230/2656] Update Questa/Modelsim to copy the modelsim.ini file and map the library locally --- makefiles/simulators/Makefile.questa | 2 ++ 1 file changed, 2 insertions(+) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index c63033eb..b7e2d583 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -85,6 +85,8 @@ endif $(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) $(SIM_BUILD) echo "if [file exists $(RTL_LIBRARY)] {vdel -lib $(RTL_LIBRARY) -all}" > $@ echo "vlib $(RTL_LIBRARY)" >> $@ + echo "vmap -c" >> $@ + echo "vmap $(RTL_LIBRARY) $(RTL_LIBRARY)" >> $@ ifneq ($(VHDL_SOURCES),) echo "vcom -work $(RTL_LIBRARY) $(VCOM_ARGS) $(VHDL_SOURCES)" >> $@ endif From 581d462fddfff51f9a0dffb79c4ff8da89a2df6b Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 12 Jul 2016 08:31:49 -0700 Subject: [PATCH 1231/2656] Issue #115: Update Timer() and Clock() to support concept of time. See PR #458 for additional details --- cocotb/clock.py | 11 ++- cocotb/triggers.py | 9 +- cocotb/utils.py | 87 +++++++++++++---- tests/test_cases/test_cocotb/test_cocotb.py | 100 ++++++++++++++++++++ 4 files changed, 178 insertions(+), 29 deletions(-) diff --git a/cocotb/clock.py b/cocotb/clock.py index 703ca467..4d743527 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -32,6 +32,7 @@ import cocotb from cocotb.log import SimLog from cocotb.triggers import Timer, RisingEdge +from cocotb.utils import get_sim_steps, get_time_from_sim_steps class BaseClock(object): @@ -46,11 +47,11 @@ class Clock(BaseClock): """ simple 50:50 duty cycle clock """ - def __init__(self, signal, period): + def __init__(self, signal, period, units=None): BaseClock.__init__(self, signal) - self.period = period - self.half_period = period / 2 - self.frequency = 1.0 / period * 1000000 + self.period = get_sim_steps(period, units) + self.half_period = get_sim_steps(period / 2.0, units) + self.frequency = 1.0 / get_time_from_sim_steps(self.period,units='us') self.hdl = None self.signal = signal self.coro = None @@ -66,4 +67,4 @@ def start(self, cycles=0): yield t def __str__(self): - return self.__class__.__name__ + "(%3.1fMHz)" % self.frequency + return self.__class__.__name__ + "(%3.1f MHz)" % self.frequency diff --git a/cocotb/triggers.py b/cocotb/triggers.py index aca33142..69b5f74c 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -37,6 +37,7 @@ import simulator from cocotb.log import SimLog from cocotb.result import raise_error +from cocotb.utils import get_sim_steps, get_time_from_sim_steps class TriggerException(Exception): @@ -112,21 +113,21 @@ class Timer(GPITrigger): Consumes simulation time """ - def __init__(self, time_ps): + def __init__(self, time_ps, units=None): GPITrigger.__init__(self) - self.time_ps = time_ps + self.sim_steps = get_sim_steps(time_ps, units) def prime(self, callback): """Register for a timed callback""" if self.cbhdl is None: - self.cbhdl = simulator.register_timed_callback(self.time_ps, + self.cbhdl = simulator.register_timed_callback(self.sim_steps, callback, self) if self.cbhdl is None: raise_error(self, "Unable set up %s Trigger" % (str(self))) Trigger.prime(self) def __str__(self): - return self.__class__.__name__ + "(%dps)" % self.time_ps + return self.__class__.__name__ + "(%1.2fps)" % get_time_from_sim_steps(self.sim_steps,units='ps') class _ReadOnly(GPITrigger): """ diff --git a/cocotb/utils.py b/cocotb/utils.py index 4c6e5a31..e835f209 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -29,17 +29,17 @@ """Collection of handy functions""" -import os import ctypes +import math +import os # For autodocumentation don't need the extension modules if "SPHINX_BUILD" in os.environ: simulator = None - _SIM_PRECISION = 1000 + _LOG_SIM_PRECISION = -15 else: import simulator - _SIM_PRECISION = simulator.get_precision() # request once and cache - + _LOG_SIM_PRECISION = simulator.get_precision() # request once and cache # python2 to python3 helper functions def get_python_integer_types(): @@ -55,35 +55,82 @@ def get_sim_time(units=None): """Retrieves the simulation time from the simulator Kwargs: - units (str): String specifying the units of the result. (None,'fs','ps','ns','us','ms','sec','min','hr') + units (str): String specifying the units of the result. (None,'fs','ps','ns','us','ms','sec') None will return the raw simulation time. Returns: The simulation time in the specified units """ - scale = { - 'fs' : 1.0e-15, - 'ps' : 1.0e-12, - 'ns' : 1.0e-9, - 'us' : 1.0e-6, - 'ms' : 1.0e-3, - 'sec': 1.0, - 'min': 60.0, - 'hr' : 3600.0} - timeh, timel = simulator.get_sim_time() result = (timeh << 32 | timel) if units is not None: - units_lwr = units.lower() - if units_lwr not in scale: - raise ValueError("Invalid unit ({}) provided".format(units)) - else: - result = result * (10.0**_SIM_PRECISION) / scale[units_lwr] + result = get_time_from_sim_steps(result, units) + + return result + +def get_time_from_sim_steps(steps, units): + """Calculates simulation time in the specified units from the steps based on the simulator precision. + + Args: + steps (int): Number of simulation steps + units (str): String specifying the units of the result. ('fs','ps','ns','us','ms','sec') + + Returns: + The simulation time in the specified units + """ + result = steps * (10.0**(_LOG_SIM_PRECISION - _get_log_time_scale(units))) return result +def get_sim_steps(time, units=None): + """Calculates the number of Simulation time steps for a given amount of time + + Args: + time (int/float): The value to convert to simulation time steps. + + Kwargs: + units (str): String specifying the units of the result. (None,'fs','ps','ns','us','ms','sec') + None means time is already in simulation time steps. + + Returns: + The number of simulation time steps + """ + result = time + if units is not None: + result = result * (10.0**(_get_log_time_scale(units) - _LOG_SIM_PRECISION)) + + err = int(result) - math.ceil(result) + + if err: + raise ValueError("Unable to accurately represent {0}({1}) with the simulator precision of 1e{2}".format(time,units,_LOG_SIM_PRECISION)) + + return int(result) + +def _get_log_time_scale(units): + """Retrieves the log10() of the scale factor for a given time unit + + Args: + units (str): String specifying the units. ('fs','ps','ns','us','ms','sec') + + Returns: + The the log10() of the scale factor for the time unit + """ + scale = { + 'fs' : -15, + 'ps' : -12, + 'ns' : -9, + 'us' : -6, + 'ms' : -3, + 'sec': 0} + + units_lwr = units.lower() + if units_lwr not in scale: + raise ValueError("Invalid unit ({}) provided".format(units)) + else: + return scale[units_lwr] + # Ctypes helper functions diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 4d8a9ae6..1e3fe93a 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -39,6 +39,7 @@ ReadOnly, ReadWrite) from cocotb.clock import Clock from cocotb.result import ReturnValue, TestFailure, TestError, TestSuccess +from cocotb.utils import get_sim_time from cocotb.binary import BinaryValue @@ -155,6 +156,104 @@ def test_adding_a_coroutine_without_starting(dut): yield Timer(100) +@cocotb.test(expect_fail=False) +def test_clock_with_units(dut): + clk_1mhz = Clock(dut.clk, 1.0, units='us') + clk_250mhz = Clock(dut.clk, 4.0, units='ns') + + if str(clk_1mhz) != "Clock(1.0 MHz)": + raise TestFailure("{} != 'Clock(1.0 MHz)'".format(str(clk_1mhz))) + else: + dut._log.info('Created clock >{}<'.format(str(clk_1mhz))) + + if str(clk_250mhz) != "Clock(250.0 MHz)": + raise TestFailure("{} != 'Clock(250.0 MHz)'".format(str(clk_250mhz))) + else: + dut._log.info('Created clock >{}<'.format(str(clk_250mhz))) + + clk_gen = cocotb.fork(clk_1mhz.start()) + + start_time_ns = get_sim_time(units='ns') + + yield Timer(1) + + yield RisingEdge(dut.clk) + + edge_time_ns = get_sim_time(units='ns') + if edge_time_ns != start_time_ns + 1000.0: + raise TestFailure("Expected a period of 1 us") + + start_time_ns = edge_time_ns + + yield RisingEdge(dut.clk) + edge_time_ns = get_sim_time(units='ns') + if edge_time_ns != start_time_ns + 1000.0: + raise TestFailure("Expected a period of 1 us") + + clk_gen.kill() + + clk_gen = cocotb.fork(clk_250mhz.start()) + + start_time_ns = get_sim_time(units='ns') + + yield Timer(1) + + yield RisingEdge(dut.clk) + + edge_time_ns = get_sim_time(units='ns') + if edge_time_ns != start_time_ns + 4.0: + raise TestFailure("Expected a period of 4 ns") + + start_time_ns = edge_time_ns + + yield RisingEdge(dut.clk) + edge_time_ns = get_sim_time(units='ns') + if edge_time_ns != start_time_ns + 4.0: + raise TestFailure("Expected a period of 4 ns") + + clk_gen.kill() + +@cocotb.test(expect_fail=False) +def test_timer_with_units(dut): + time_fs = get_sim_time(units='fs') + + # Yield for one simulation time step + yield Timer(1) + time_step = get_sim_time(units='fs') - time_fs + + try: + #Yield for 2.5 timesteps, should throw exception + yield Timer(2.5*time_step, units='fs') + raise TestFailure("Timers should throw exception if time cannot be achieved with simulator resolution") + except ValueError: + dut._log.info("As expected, unable to create a timer of 2.5 simulator time steps") + + time_fs = get_sim_time(units='fs') + + yield Timer(3, "ns") + + if get_sim_time(units='fs') != time_fs+3000000.0: + raise TestFailure("Expected a delay of 3 ns") + + time_fs = get_sim_time(units='fs') + yield Timer(1.5, "ns") + + if get_sim_time(units='fs') != time_fs+1500000.0: + raise TestFailure("Expected a delay of 1.5 ns") + + time_fs = get_sim_time(units='fs') + yield Timer(10.0, "ps") + + if get_sim_time(units='fs') != time_fs+10000.0: + raise TestFailure("Expected a delay of 10 ps") + + time_fs = get_sim_time(units='fs') + yield Timer(1.0, "us") + + if get_sim_time(units='fs') != time_fs+1000000000.0: + raise TestFailure("Expected a delay of 1 us") + + @cocotb.test(expect_fail=False) def test_anternal_clock(dut): """Test ability to yeild on an external non cocotb coroutine decorated @@ -301,6 +400,7 @@ def test_syntax_error(dut): fail +#@cocotb.test(expect_error=True) @cocotb.test(expect_error=True) def test_coroutine_syntax_error(dut): """Syntax error in a coroutine that we yield""" From 8616dc8c594239e102d04f4feb33a5a6d30720e0 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 15 Jul 2016 07:19:06 -0500 Subject: [PATCH 1232/2656] Catch exception thrown by threading code when calling the supplied routine. Still would be nice to handle errors inside the called code but really not sure how we can do that --- cocotb/decorators.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index d981b8da..f7b3afa4 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -318,8 +318,10 @@ def wrapped(*args, **kwargs): bridge = test_locker() def execute_external(func, _event): - _event.result = func(*args, **kwargs) - # Queue a co-routine to + try: + _event.result = func(*args, **kwargs) + except TypeError as e: + _event.result = None unblock_external(_event) thread = threading.Thread(group=None, target=execute_external, From 9ec1bb6a2e2d03408aa87bc71fda44ff0ec39136 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Tue, 19 Jul 2016 07:46:32 -0700 Subject: [PATCH 1233/2656] Issue #475: Fix a bug if initialsation fails --- lib/embed/gpi_embed.c | 1 + lib/fli/FliImpl.cpp | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 59847856..417e28ef 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -300,6 +300,7 @@ int embed_sim_init(gpi_sim_info_t *info) } else { PyErr_Print(); fprintf(stderr,"Cocotb initialisation failed - exiting\n"); + goto cleanup; } FEXIT diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 58539ab8..621236d2 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -44,9 +44,12 @@ void FliImpl::sim_end(void) { if (GPI_DELETE != sim_finish_cb->get_call_state()) { sim_finish_cb->set_call_state(GPI_DELETE); - mti_Break(); + if (mti_NowUpper() == 0 && mti_Now() == 0 && mti_Delta() == 0) { + mti_Quit(); + } else { + mti_Break(); + } } - } bool FliImpl::isValueConst(int kind) From 0f6e7b5334b23c4f830d32ae3f97e4573c202fa8 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 21 Jul 2016 12:00:00 -0500 Subject: [PATCH 1234/2656] change Questa logging from add wave to add log --- makefiles/simulators/Makefile.questa | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 2077df27..c958093b 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -98,7 +98,7 @@ ifeq ($(WAVES),1) echo "log -recursive /*" >> $@ endif ifeq ($(GUI),1) - echo "log -recursive /*" >> $@ + echo "add log -r *" >> $@ else echo "onbreak resume" >> $@ echo "run -all" >> $@ From ef0dd885e73d9b39210fbabeb1c05d5c2b395fd1 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Wed, 27 Jul 2016 18:33:01 -0700 Subject: [PATCH 1235/2656] Issue #478: Fix alignment issue when colouring is applied to multi-line messages --- cocotb/log.py | 9 ++++++--- makefiles/Makefile.pylib.Linux | 8 ++++---- tests/test_cases/test_cocotb/test_cocotb.py | 2 ++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/cocotb/log.py b/cocotb/log.py index 75ef3a15..fe3468f4 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -163,7 +163,7 @@ def rjust(string, chars): return ".." + string[(chars - 2) * -1:] return string.rjust(chars) - def _format(self, level, record, msg): + def _format(self, level, record, msg, coloured=False): time_ns = get_sim_time('ns') simtime = "%6.2fns" % (time_ns) @@ -174,7 +174,10 @@ def _format(self, level, record, msg): ':' + self.ljust(str(record.lineno), _LINENO_CHARS) + \ ' in ' + self.ljust(str(record.funcName), _FUNCNAME_CHARS) + ' ' - pad = "\n" + " " * (len(prefix)) + prefix_len = len(prefix) + if coloured: + prefix_len -= (len(level) - _LEVEL_CHARS) + pad = "\n" + " " * (prefix_len) return prefix + pad.join(msg.split('\n')) def format(self, record): @@ -213,4 +216,4 @@ def format(self, record): level = (SimColourLogFormatter.loglevel2colour[record.levelno] % record.levelname.ljust(_LEVEL_CHARS)) - return self._format(level, record, msg) + return self._format(level, record, msg, coloured=True) diff --git a/makefiles/Makefile.pylib.Linux b/makefiles/Makefile.pylib.Linux index 711690f1..5fa4b6c5 100644 --- a/makefiles/Makefile.pylib.Linux +++ b/makefiles/Makefile.pylib.Linux @@ -57,12 +57,12 @@ endif # Since we don't know which modules we might need or whether the simulator # we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports # is we simply link against all dynamic libraries in the Python installation -ifneq (, $(shell which $(PYTHON_BIN)-config 2>/dev/null)) -PY_CFG_EXE=$(PYTHON_BIN)-config +ifneq (, $(shell which $(PYTHON_BIN)$(PYTHON_VERSION)-config 2>/dev/null)) +PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_VERSION)-config else ifneq (, $(shell which $(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config 2>/dev/null)) PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config -else ifneq (, $(shell which $(PYTHON_BIN)$(PYTHON_VERSION)-config 2>/dev/null)) -PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_VERSION)-config +else ifneq (, $(shell which $(PYTHON_BIN)-config 2>/dev/null)) +PY_CFG_EXE=$(PYTHON_BIN)-config else $(error ERROR: Unable to find a valid python-config.) endif diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 1e3fe93a..a3b23c59 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -594,6 +594,8 @@ def test_logging_with_args(dut): dut._log.info("No substitution") + dut._log.warning("Testing multiple line\nmessage") + yield Timer(100) #Make it do something with time @cocotb.test() From b5bb02e716ab8a8391a031bec3e093e17ea5a788 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Wed, 27 Jul 2016 18:39:41 -0700 Subject: [PATCH 1236/2656] Issue #479: Fixed issue with using raise_error in exception handling --- cocotb/decorators.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index e2b24b4d..aaf669e6 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -38,7 +38,7 @@ from cocotb.log import SimLog from cocotb.triggers import _Join, PythonTrigger, Timer, Event, NullTrigger from cocotb.result import (TestComplete, TestError, TestFailure, TestSuccess, - ReturnValue, raise_error) + ReturnValue, create_error) from cocotb.utils import get_sim_time @@ -126,7 +126,7 @@ def send(self, value): raise CoroutineComplete(callback=self._finished_cb) except Exception as e: self._finished = True - raise_error(self, "Send raised exception: %s" % (str(e))) + raise create_error(self, "Send raised exception: %s" % (str(e))) def throw(self, exc): return self._coro.throw(exc) @@ -209,7 +209,7 @@ def send(self, value): except StopIteration: raise TestSuccess() except Exception as e: - raise_error(self, "Send raised exception: %s" % (str(e))) + raise create_error(self, "Send raised exception: %s" % (str(e))) def _handle_error_message(self, msg): self.error_messages.append(msg) @@ -371,7 +371,7 @@ def _wrapped_test(*args, **kwargs): try: return RunningTest(self._func(*args, **kwargs), self) except Exception as e: - raise_error(self, str(e)) + raise create_error(self, str(e)) _wrapped_test.im_test = True # For auto-regressions _wrapped_test.name = self._func.__name__ From ee83dc32a82ea54ca402a14831fd18f9fae57467 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 5 Aug 2016 14:20:49 -0500 Subject: [PATCH 1237/2656] fix so that nameless BusMonitors will be okay this will close issue #4 --- cocotb/scoreboard.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index 14392c24..8dfbf1f1 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -180,7 +180,12 @@ def check_received_transaction(transaction): """Called back by the monitor when a new transaction has been received""" - log = logging.getLogger(self.log.name + '.' + monitor.name) + if monitor.name: + log_name = self.log.name + '.' + monitor.name + else: + log_name = self.log.name + '.' + monitor.__class__.__name__ + + log = logging.getLogger(log_name) if callable(expected_output): exp = expected_output(transaction) From e3f92b31857c0d53323da9dee9aa0bea700974b3 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 5 Aug 2016 14:22:42 -0500 Subject: [PATCH 1238/2656] preserve irun exit code thru tee pipeline --- makefiles/simulators/Makefile.ius | 1 + 1 file changed, 1 insertion(+) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 51591b81..1d7b3493 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -90,6 +90,7 @@ results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEP LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ + set -o pipefail; \ $(CMD) $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) 2>&1 | tee $(SIM_BUILD)/sim.log clean:: From 429e0fcac66d362e1300921e31530ff76e266d28 Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 10 Aug 2016 14:28:33 -0500 Subject: [PATCH 1239/2656] add a capture method to the Bus class The capture method provides a symetric method to the drive method which already exists on the Bus class --- cocotb/bus.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/cocotb/bus.py b/cocotb/bus.py index 33c297a8..d57662b4 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -118,6 +118,32 @@ def drive(self, obj, strict=False): val = getattr(obj, name) hdl <= val + def capture(self, obj, strict=False): + """ + Capture the values from the bus. + + Args: + obj (any type) : object with attribute names that match the bus + signals + + Kwargs: + strict (bool) : Check that all signals are being assigned + + Raises: + AttributeError + """ + for name, hdl in self._signals.items(): + if not hasattr(obj, name): + if strict: + msg = ("Unable to capture from %s.%s because %s is missing " + "attribute %s" % self._entity._name, self._name, + obj.__class__.__name__, name) + raise AttributeError(msg) + else: + continue + setattr(obj, name, hdl.value) + + def __le__(self, value): """Overload the less than or equal to operator for value assignment""" self.drive(value) From 4fb86ec6aced375fc10ddd628ce58bcf9542293b Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 11 Aug 2016 11:57:22 -0500 Subject: [PATCH 1240/2656] Correcting usage of SIM_BUILD for ius scratch dir Here's an excerpt from Cadence docs regarding the usage of nclibdirname and nclibdirpath. Changing the Name of the INCA_libs Directory By default, irun generates a scratch directory during simulation called INCA_libs and saves it to the current working directory. Use the -nclibdirname option to change the name. For example: % irun -nclibdirname IRUN_libs dut.v tb.v This command changes the name of the scratch directory to IRUN_libs, which is generated during simulation and saved to the current working directory by irun. Changing the Path to a Custom Scratch Directory You can include path information when specifying a custom name for the INCA_libs scratch directory. Path statements passed to the -nclibdirname command-line option can be relative or absolute, as shown: % irun -nclibdirname ../IRUN_libs dut.v tb.v In this example irun is used to change the name of the scratch directory to IRUN_libs, but instead of saving it to the current working directory, the tool will save the directory using a relative path that is located above the current working directory. % irun -nclibdirname ./dut/IRUN_libs dut.v tb.v This example specifies a relative path in the current working directory with multiple hierarchical levels. The directory ./dut must exist in order for irun to create the new IRUN_libs location. If ./dut does not exist, irun will halt and generate an NWRKDRA error. Changing the Path to the INCA_libs Directory Alternatively, you can use the -nclibdirpath option on the irun command line when specifying a relative path for storing new libraries. This option can be used by itself or together with together with -nclibdirname. When using -nclibdirname together with -nclibdirpath, the directory name specified with -nclibdirname must not contain any path information. For example: % irun -nclibdirpath ./libraries dut.v tb.v This irun command will generate a new INCA_libs directory in the relative location ./libraries, which is located in the current working directory. Note that ./libraries must exist in order to avoid an error. % irun -nclibdirname IRUN_libs -nclibdirbath ../libraries dut.v tb.v This irun command will generate a new IRUN_libs directory in the relative location ../libraries, which is one level above the current working directory. Note that ../libraries must exist in order to avoid an error. The tool will automatically generate IRUN_libs, if ../libraries is available. --- makefiles/simulators/Makefile.ius | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 1d7b3493..b3ef61cc 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -49,7 +49,8 @@ ifneq ($(ARCH),i686) EXTRA_ARGS += -64 endif -EXTRA_ARGS += -nclibdirname $(SIM_BUILD) -plinowarn +EXTRA_ARGS += -nclibdirpath $(SIM_BUILD) +EXTRA_ARGS += -plinowarn ifeq ($(GUI),1) EXTRA_ARGS += -gui From 3e99c307f3e26ac31ed9a5fe80c64615f6e67faa Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 11 Aug 2016 11:59:59 -0500 Subject: [PATCH 1241/2656] Adding missing doc for make var SIM_BUILD --- documentation/source/building.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/documentation/source/building.rst b/documentation/source/building.rst index f3ff2b71..f747ebd5 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -77,6 +77,11 @@ CUSTOM_SIM_DEPS Use to add additional dependencies to the simulation target. +SIM_BUILD +~~~~~~~~~ + +Use to define a scratch directory for use by the simulator. The path is relative to the Makefile location. If not provided, the default scratch directory is "sim_build". + Environment Variables ===================== From 51a0c341611b07930a6a15846138c77060a19dc4 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 19 Aug 2016 10:45:49 -0500 Subject: [PATCH 1242/2656] Issue #11: Pass any exception generated in an external function to the calling coroutine --- cocotb/decorators.py | 21 +++++++++++++++++---- cocotb/result.py | 5 +++++ cocotb/scheduler.py | 13 ++++++++++--- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index f7b3afa4..115ea7c0 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -38,7 +38,7 @@ from cocotb.log import SimLog from cocotb.triggers import _Join, PythonTrigger, Timer, Event, NullTrigger from cocotb.result import (TestComplete, TestError, TestFailure, TestSuccess, - ReturnValue, raise_error) + ReturnValue, raise_error, ExternalException) def public(f): @@ -111,11 +111,18 @@ def __str__(self): def send(self, value): try: + if isinstance(value, ExternalException): + self.log.debug("Injecting ExternalException(%s)" % (repr(value))) + return self._coro.throw(value.exception) return self._coro.send(value) except TestComplete as e: if isinstance(e, TestFailure): self.log.warning(str(e)) raise + except ExternalException as e: + self.retval = e + self._finished = True + raise CoroutineComplete(callback=self._finished_cb) except ReturnValue as e: self.retval = e.retval self._finished = True @@ -190,6 +197,9 @@ def send(self, value): self.start_time = time.time() self.started = True try: + if isinstance(value, ExternalException): + self.log.debug("Injecting ExternalException(%s)" % (repr(value))) + return self._coro.throw(value.exception) self.log.debug("Sending trigger %s" % (str(value))) return self._coro.send(value) except TestComplete as e: @@ -320,8 +330,8 @@ def wrapped(*args, **kwargs): def execute_external(func, _event): try: _event.result = func(*args, **kwargs) - except TypeError as e: - _event.result = None + except Exception as e: + _event.result = e unblock_external(_event) thread = threading.Thread(group=None, target=execute_external, @@ -332,7 +342,10 @@ def execute_external(func, _event): yield bridge.out_event.wait() if bridge.result is not None: - raise ReturnValue(bridge.result) + if isinstance(bridge.result, Exception): + raise ExternalException(bridge.result) + else: + raise ReturnValue(bridge.result) return wrapped diff --git a/cocotb/result.py b/cocotb/result.py index 4241146b..89f76d93 100644 --- a/cocotb/result.py +++ b/cocotb/result.py @@ -81,6 +81,11 @@ def __init__(self, *args, **kwargs): self.stderr = StringIO() +class ExternalException(StopIteration): + def __init__(self, exception): + self.exception = exception + + class TestError(TestComplete): pass diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 53fdbc61..4e16fe8f 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -68,7 +68,7 @@ _NextTimeStep, _ReadWrite) from cocotb.log import SimLog from cocotb.result import (TestComplete, TestError, ReturnValue, raise_error, - create_error) + create_error, ExternalException) class Scheduler(object): @@ -336,6 +336,8 @@ def react(self, trigger, depth=0): del self._trigger2coros[pending] for coro in scheduling: + if _debug: + self.log.debug("Scheduling coroutine %s" % (coro.__name__)) self.schedule(coro, trigger=trigger) if _debug: self.log.debug("Scheduled coroutine %s" % (coro.__name__)) @@ -452,8 +454,13 @@ def schedule(self, coroutine, trigger=None): if hasattr(trigger, "pass_retval"): sendval = trigger.retval if _debug: - coroutine.log.debug("Scheduling with ReturnValue(%s)" % - (repr(sendval))) + if isinstance(sendval, ReturnValue): + coroutine.log.debug("Scheduling with ReturnValue(%s)" % + (repr(sendval))) + elif isinstance(sendval, ExternalException): + coroutine.log.debug("Scheduling with ExternalException(%s)" % + (repr(sendval.exception))) + else: sendval = trigger if _debug: From 12ba8f4cd1ab2db785fb965bea5ee5d210ad3bb5 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Sun, 21 Aug 2016 16:53:37 -0700 Subject: [PATCH 1243/2656] Issue #478: Fix colouring issues of multi-line messages --- cocotb/ANSI.py | 2 ++ cocotb/log.py | 13 ++++++++----- cocotb/regression.py | 29 +++++++++++++++++------------ 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/cocotb/ANSI.py b/cocotb/ANSI.py index dd8abc4e..8f240b00 100644 --- a/cocotb/ANSI.py +++ b/cocotb/ANSI.py @@ -51,3 +51,5 @@ CYAN_BG = _ESCAPE + "46m" WHITE_BG = _ESCAPE + "47m" DEFAULT_BG = _ESCAPE + "49m" + +DEFAULT = DEFAULT_BG + DEFAULT_FG diff --git a/cocotb/log.py b/cocotb/log.py index fe3468f4..c7ae7483 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -61,8 +61,10 @@ def __init__(self, name): want_ansi = want_ansi == '1' if want_ansi: hdlr.setFormatter(SimColourLogFormatter()) + self.colour = True else: hdlr.setFormatter(SimLogFormatter()) + self.colour = False self.name = name self.handlers = [] self.disabled = False @@ -198,11 +200,11 @@ class SimColourLogFormatter(SimLogFormatter): """Log formatter to provide consistent log message handling.""" loglevel2colour = { logging.DEBUG : "%s", - logging.INFO : ANSI.BLUE_FG + "%s" + ANSI.DEFAULT_FG, - logging.WARNING : ANSI.YELLOW_FG + "%s" + ANSI.DEFAULT_FG, - logging.ERROR : ANSI.RED_FG + "%s" + ANSI.DEFAULT_FG, + logging.INFO : ANSI.BLUE_FG + "%s" + ANSI.DEFAULT, + logging.WARNING : ANSI.YELLOW_FG + "%s" + ANSI.DEFAULT, + logging.ERROR : ANSI.RED_FG + "%s" + ANSI.DEFAULT, logging.CRITICAL: ANSI.RED_BG + ANSI.BLACK_FG + "%s" + - ANSI.DEFAULT_FG + ANSI.DEFAULT_BG} + ANSI.DEFAULT} def format(self, record): """pretify the log output, annotate with simulation time""" @@ -212,7 +214,8 @@ def format(self, record): else: msg = record.msg - msg = SimColourLogFormatter.loglevel2colour[record.levelno] % msg + # Need to colour each line in case coloring is applied in the message + msg = '\n'.join([SimColourLogFormatter.loglevel2colour[record.levelno] % line for line in msg.split('\n')]) level = (SimColourLogFormatter.loglevel2colour[record.levelno] % record.levelname.ljust(_LEVEL_CHARS)) diff --git a/cocotb/regression.py b/cocotb/regression.py index 3904512f..8f87ecc0 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -274,11 +274,16 @@ def _result_was(): def execute(self): self._running_test = cocotb.regression.next_test() if self._running_test: + start = '' + end = '' + if self.log.colour: + start = ANSI.BLUE_BG + ANSI.BLACK_FG + end = ANSI.DEFAULT # Want this to stand out a little bit self.log.info("%sRunning test %d/%d:%s %s" % - (ANSI.BLUE_BG + ANSI.BLACK_FG, + (start, self.count, self.ntests, - ANSI.DEFAULT_FG + ANSI.DEFAULT_BG, + end, self._running_test.funcname)) if self.count is 1: test = cocotb.scheduler.add(self._running_test) @@ -314,8 +319,7 @@ def _log_test_summary(self): e=RATIO_FIELD, e_len=RATIO_FIELD_LEN) summary += LINE_SEP for result in self.test_results: - dflt = ANSI.DEFAULT_FG + ANSI.DEFAULT_BG - hilite = dflt + hilite = '' if result['pass'] is None: pass_fail_str = "N/A" @@ -323,14 +327,15 @@ def _log_test_summary(self): pass_fail_str = "PASS" else: pass_fail_str = "FAIL" - hilite = ANSI.WHITE_FG + ANSI.RED_BG - - summary += "{start}** {a:<{a_len}} {b:^{b_len}} {c:>{c_len}.2f} {d:>{d_len}.2f} {e:>{e_len}.2f} **{end}\n".format(a=result['test'], a_len=TEST_FIELD_LEN, - b=pass_fail_str, b_len=RESULT_FIELD_LEN, - c=result['sim'], c_len=SIM_FIELD_LEN-1, - d=result['real'], d_len=REAL_FIELD_LEN-1, - e=result['ratio'], e_len=RATIO_FIELD_LEN-1, - start=hilite, end=dflt) + if self.log.colour: + hilite = ANSI.WHITE_FG + ANSI.RED_BG + + summary += "{start}** {a:<{a_len}} {b:^{b_len}} {c:>{c_len}.2f} {d:>{d_len}.2f} {e:>{e_len}.2f} **\n".format(a=result['test'], a_len=TEST_FIELD_LEN, + b=pass_fail_str, b_len=RESULT_FIELD_LEN, + c=result['sim'], c_len=SIM_FIELD_LEN-1, + d=result['real'], d_len=REAL_FIELD_LEN-1, + e=result['ratio'], e_len=RATIO_FIELD_LEN-1, + start=hilite) summary += LINE_SEP self.log.info(summary) From 3656c5f9991756b2fbf49d71411a491187441eb2 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 25 Aug 2016 11:10:08 -0500 Subject: [PATCH 1244/2656] Add TESTSUITE and TESTPACKAGE environment variables which feed into results.xml --- cocotb/regression.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index b9234a4c..6eb441f3 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -93,8 +93,12 @@ def initialise(self): self.skipped = 0 self.failures = 0 self.xunit = XUnitReporter() - self.xunit.add_testsuite(name="all", tests=repr(self.ntests), - package="all") + suite_name = os.getenv('TESTSUITE') if os.getenv('TESTSUITE') else "all" + package_name = os.getenv('TESTPACKAGE') if os.getenv('TESTPACKAGE') else "all" + + + self.xunit.add_testsuite(name=suite_name, tests=repr(self.ntests), + package=package_name) if coverage is not None: self.log.info("Enabling coverage collection of Python code") From e9b6ccbc1e1dc5f1b8ece92cc0008dff32d9e944 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 25 Aug 2016 12:44:27 -0500 Subject: [PATCH 1245/2656] Improve combine_results in line with #16 - backwards compatible but preserves more info in XML if originally supplied --- bin/combine_results.py | 80 ++++++++++++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 22 deletions(-) diff --git a/bin/combine_results.py b/bin/combine_results.py index f8cf78ac..e13ddfe3 100755 --- a/bin/combine_results.py +++ b/bin/combine_results.py @@ -7,6 +7,7 @@ import os import sys +import argparse from xml.etree import cElementTree as ET @@ -16,33 +17,68 @@ def find_all(name, path): if name in files: yield os.path.join(root, name) +def get_parser(): + """Return the cmdline parser""" + parser = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.ArgumentDefaultsHelpFormatter) -def main(path, output): - rc = 0 - testsuite = ET.Element("testsuite", name="all", package="all", tests="0") + parser.add_argument("--directory", dest="directory", type=str, required=False, + default=".", + help="Name of base directory to search from") - for fname in find_all("results.xml", path): - tree = ET.parse(fname) - for element in tree.getiterator("testcase"): - testsuite.append(element) - - for child in element: - if child.tag in ["failure", "error"]: - sys.stderr.write("FAILURE: %s.%s\n" % - (element.attrib["classname"], - element.attrib["name"])) - rc = 1 + parser.add_argument("--output_file", dest="output_file", type=str, required=False, + default="combined_results.xml", + help="Name of output file") + parser.add_argument("--testsuites_name", dest="testsuites_name", type=str, required=False, + default="results", + help="Name value for testsuites tag") + parser.add_argument("--verbose", dest="debug", action='store_const', required=False, + const=True, default=False, + help="Verbose/debug output") + parser.add_argument("--suppress_rc", dest="set_rc", action='store_const', required=False, + const=False, default=True, + help="Supress return code if failures found") + + return parser - result = ET.Element("testsuites", name="results") - result.append(testsuite) - ET.ElementTree(result).write(output, encoding="UTF-8") +def main(): + + parser = get_parser() + args = parser.parse_args() + rc = 0; + + result = ET.Element("testsuites", name=args.testsuites_name); + + for fname in find_all("results.xml", args.directory): + if args.debug : print "Reading file %s" % fname + tree = ET.parse(fname) + for ts in tree.getiterator("testsuite"): + if args.debug : print ("Ts name : %s, package : %s" % ( ts.get('name'), ts.get('package'))) + use_element = None + for existing in result: + if ((existing.get('name') == ts.get('name') and (existing.get('package') == ts.get('package')))): + if args.debug : print "Already found" + use_element=existing + break + if use_element is None: + result.append(ts) + else: + #for tc in ts.getiterator("testcase"): + use_element.extend(list(ts)); + + if args.debug : ET.dump(result) + + for failure in result.iter('failure'): + if args.set_rc: rc=1 + if args.debug : print "Found failure" + break + + ET.ElementTree(result).write(args.output_file, encoding="UTF-8") return rc + + if __name__ == "__main__": - rc = main(".", "combined_results.xml") - # Suppress exit code if run with any arguments - # Slightly hacky but argparse isnt' in 2.6 - if len(sys.argv) > 1: - sys.exit(0) + rc = main() sys.exit(rc) From 791b64b00ec5845d952b288985db8b856dc020dd Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 26 Aug 2016 03:01:25 -0500 Subject: [PATCH 1246/2656] As requested by @chiggs - always print all failing cases --- bin/combine_results.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/bin/combine_results.py b/bin/combine_results.py index e13ddfe3..1370b71c 100755 --- a/bin/combine_results.py +++ b/bin/combine_results.py @@ -69,10 +69,12 @@ def main(): if args.debug : ET.dump(result) - for failure in result.iter('failure'): - if args.set_rc: rc=1 - if args.debug : print "Found failure" - break + for testsuite in result.iter('testsuite'): + for testcase in testsuite.iter('testcase'): + for failure in testcase.iter('failure'): + if args.set_rc: rc=1 + print "Failure in testsuite: '%s' testcase: '%s' with parameters '%s'" % (testsuite.get('name'), testcase.get('name'), testsuite.get('package')) + ET.ElementTree(result).write(args.output_file, encoding="UTF-8") return rc From bba5679d58349b9869a57e2e37cb805451b1b310 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 26 Aug 2016 03:38:09 -0500 Subject: [PATCH 1247/2656] Change env var names to make purpose more clear --- cocotb/regression.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 6eb441f3..cc66b171 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -93,8 +93,8 @@ def initialise(self): self.skipped = 0 self.failures = 0 self.xunit = XUnitReporter() - suite_name = os.getenv('TESTSUITE') if os.getenv('TESTSUITE') else "all" - package_name = os.getenv('TESTPACKAGE') if os.getenv('TESTPACKAGE') else "all" + suite_name = os.getenv('RESULT_TESTSUITE') if os.getenv('RESULT_TESTSUITE') else "all" + package_name = os.getenv('RESULT_TESTPACKAGE') if os.getenv('RESULT_TESTPACKAGE') else "all" self.xunit.add_testsuite(name=suite_name, tests=repr(self.ntests), From 481d0aea98e815a0cdb51979d5dc098978842470 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 26 Aug 2016 15:14:56 -0500 Subject: [PATCH 1248/2656] updates to allow bus seperators other than underscore: allows access to structs --- cocotb/bus.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/cocotb/bus.py b/cocotb/bus.py index d57662b4..375a07c3 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -50,7 +50,7 @@ class Bus(object): TODO: Support for struct/record ports where signals are member names """ - def __init__(self, entity, name, signals, optional_signals=[]): + def __init__(self, entity, name, signals, optional_signals=[], bus_seperator="_"): """ Args: entity (SimHandle): SimHandle instance to the entity containing the @@ -62,7 +62,10 @@ def __init__(self, entity, name, signals, optional_signals=[]): signals (list): array of signal names Kwargs: - optiona_signals (list): array of optional signal names + optional_signals (list): array of optional signal names + + bus_seperator (str): characters to use as seperator between bus + name and signal name """ self._entity = entity self._name = name @@ -70,7 +73,7 @@ def __init__(self, entity, name, signals, optional_signals=[]): for signal in signals: if name: - signame = name + "_" + signal + signame = name + bus_seperator + signal else: signame = signal setattr(self, signal, getattr(entity, signame)) From 5ba7d0e9f594bee4768fa3241d467055521bae73 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 26 Aug 2016 15:19:16 -0500 Subject: [PATCH 1249/2656] fix spelling --- cocotb/bus.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cocotb/bus.py b/cocotb/bus.py index 375a07c3..b70d9db8 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -50,7 +50,7 @@ class Bus(object): TODO: Support for struct/record ports where signals are member names """ - def __init__(self, entity, name, signals, optional_signals=[], bus_seperator="_"): + def __init__(self, entity, name, signals, optional_signals=[], bus_separator="_"): """ Args: entity (SimHandle): SimHandle instance to the entity containing the @@ -64,7 +64,7 @@ def __init__(self, entity, name, signals, optional_signals=[], bus_seperator="_" Kwargs: optional_signals (list): array of optional signal names - bus_seperator (str): characters to use as seperator between bus + bus_separator (str): characters to use as separator between bus name and signal name """ self._entity = entity @@ -73,7 +73,7 @@ def __init__(self, entity, name, signals, optional_signals=[], bus_seperator="_" for signal in signals: if name: - signame = name + bus_seperator + signal + signame = name + bus_separator + signal else: signame = signal setattr(self, signal, getattr(entity, signame)) From e708353aea2376a813f7589ae6814ca6d8b2bb13 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Fri, 26 Aug 2016 13:53:18 -0700 Subject: [PATCH 1250/2656] Issue #483: Fix a bug calling the wrong parent class in the ClockCycles.__init__() --- cocotb/triggers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 3bf083ba..a8008cfd 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -284,7 +284,7 @@ class ClockCycles(_Edge): Execution will resume after N rising edges or N falling edges """ def __init__(self, signal, num_cycles, rising=True): - Edge.__init__(self, signal) + _Edge.__init__(self, signal) self.num_cycles = num_cycles if rising is True: self._rising = 1 From 157d8ead3ffa719857ac5ee5dee83c9d1fe60a76 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Fri, 26 Aug 2016 14:05:33 -0700 Subject: [PATCH 1251/2656] Issue #478: Try changes to makefile to see if it fixes Icarus issue --- makefiles/Makefile.pylib.Linux | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/makefiles/Makefile.pylib.Linux b/makefiles/Makefile.pylib.Linux index 5fa4b6c5..988135f1 100644 --- a/makefiles/Makefile.pylib.Linux +++ b/makefiles/Makefile.pylib.Linux @@ -31,9 +31,9 @@ ifneq ($(COCOTB_PYTHON_DEBUG),) DBG_PYTHON_VERSION=$(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') -PYTHON_BIN=python$(DBG_PYTHON_VERSION)-dbg +PYTHON_BIN?=python$(DBG_PYTHON_VERSION)-dbg else -PYTHON_BIN=python +PYTHON_BIN?=python endif NATIVE_ARCH=$(shell uname -m) @@ -57,15 +57,17 @@ endif # Since we don't know which modules we might need or whether the simulator # we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports # is we simply link against all dynamic libraries in the Python installation -ifneq (, $(shell which $(PYTHON_BIN)$(PYTHON_VERSION)-config 2>/dev/null)) -PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_VERSION)-config +ifeq ($(PY_CFG_EXE),) +ifneq (, $(shell which $(PYTHON_BIN)-config 2>/dev/null)) +PY_CFG_EXE=$(PYTHON_BIN)-config else ifneq (, $(shell which $(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config 2>/dev/null)) PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config -else ifneq (, $(shell which $(PYTHON_BIN)-config 2>/dev/null)) -PY_CFG_EXE=$(PYTHON_BIN)-config +else ifneq (, $(shell which $(PYTHON_BIN)$(PYTHON_VERSION)-config 2>/dev/null)) +PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_VERSION)-config else $(error ERROR: Unable to find a valid python-config.) endif +endif PYLIBS = $(shell $(PY_CFG_EXE) --libs) From 5802f2cd0b1a159f96621d19d732b0d77b3a6e2e Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Fri, 26 Aug 2016 14:34:42 -0700 Subject: [PATCH 1252/2656] Revert "Issue #478: Try changes to makefile to see if it fixes Icarus issue" This reverts commit 157d8ead3ffa719857ac5ee5dee83c9d1fe60a76. --- makefiles/Makefile.pylib.Linux | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/makefiles/Makefile.pylib.Linux b/makefiles/Makefile.pylib.Linux index 988135f1..5fa4b6c5 100644 --- a/makefiles/Makefile.pylib.Linux +++ b/makefiles/Makefile.pylib.Linux @@ -31,9 +31,9 @@ ifneq ($(COCOTB_PYTHON_DEBUG),) DBG_PYTHON_VERSION=$(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') -PYTHON_BIN?=python$(DBG_PYTHON_VERSION)-dbg +PYTHON_BIN=python$(DBG_PYTHON_VERSION)-dbg else -PYTHON_BIN?=python +PYTHON_BIN=python endif NATIVE_ARCH=$(shell uname -m) @@ -57,17 +57,15 @@ endif # Since we don't know which modules we might need or whether the simulator # we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports # is we simply link against all dynamic libraries in the Python installation -ifeq ($(PY_CFG_EXE),) -ifneq (, $(shell which $(PYTHON_BIN)-config 2>/dev/null)) -PY_CFG_EXE=$(PYTHON_BIN)-config +ifneq (, $(shell which $(PYTHON_BIN)$(PYTHON_VERSION)-config 2>/dev/null)) +PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_VERSION)-config else ifneq (, $(shell which $(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config 2>/dev/null)) PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config -else ifneq (, $(shell which $(PYTHON_BIN)$(PYTHON_VERSION)-config 2>/dev/null)) -PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_VERSION)-config +else ifneq (, $(shell which $(PYTHON_BIN)-config 2>/dev/null)) +PY_CFG_EXE=$(PYTHON_BIN)-config else $(error ERROR: Unable to find a valid python-config.) endif -endif PYLIBS = $(shell $(PY_CFG_EXE) --libs) From 167dfa534cae2b9f8332513bd9a43d3f0a79ac57 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Fri, 26 Aug 2016 14:37:19 -0700 Subject: [PATCH 1253/2656] Issue #478: Add some logging to troubleshoot the Icarus Issue --- makefiles/Makefile.pylib.Linux | 3 +++ 1 file changed, 3 insertions(+) diff --git a/makefiles/Makefile.pylib.Linux b/makefiles/Makefile.pylib.Linux index 5fa4b6c5..a12339d2 100644 --- a/makefiles/Makefile.pylib.Linux +++ b/makefiles/Makefile.pylib.Linux @@ -69,5 +69,8 @@ endif PYLIBS = $(shell $(PY_CFG_EXE) --libs) +$(info "*** PY_CFG_EXE: $(PY_CFG_EXE)") +$(info "*** PY_LIBS: $(PYLIBS)") + PYTHON_INCLUDEDIR := $(shell $(PYTHON_BIN) -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())') PYTHON_DYN_LIB:=$(shell $(PYTHON_BIN) -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("LDLIBRARY"))') From f7bbcb7e46c0c79639d88e3b4bfeca5939df611d Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Fri, 26 Aug 2016 14:55:52 -0700 Subject: [PATCH 1254/2656] Issue #478: Add more logging --- makefiles/Makefile.pylib.Linux | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/makefiles/Makefile.pylib.Linux b/makefiles/Makefile.pylib.Linux index a12339d2..0d2dfc05 100644 --- a/makefiles/Makefile.pylib.Linux +++ b/makefiles/Makefile.pylib.Linux @@ -69,8 +69,15 @@ endif PYLIBS = $(shell $(PY_CFG_EXE) --libs) -$(info "*** PY_CFG_EXE: $(PY_CFG_EXE)") -$(info "*** PY_LIBS: $(PYLIBS)") +$(info "*** PY_CFG_EXE: $(PY_CFG_EXE)") +$(info "*** PY_LIBS: $(PYLIBS)") +$(info "*** PY_VER: $(PYTHON_VERSION)") +$(info "*** PY_MAJ: $(PYTHON_MAJOR_VERSION)") +$(info "*** PY_MIN: $(PYTHON_MINOR_VERSION)") +$(info "*** PYTHON: $(shell which python 2>/dev/null)") +$(info "*** PYTHON-CONFIG: $(shell which python-config 2>/dev/null)") +$(info "*** PYTHON3-CONFIG: $(shell which python3-config 2>/dev/null)") +$(info "*** PYTHON3.4-CONFIG: $(shell which python3.4-config 2>/dev/null)") PYTHON_INCLUDEDIR := $(shell $(PYTHON_BIN) -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())') PYTHON_DYN_LIB:=$(shell $(PYTHON_BIN) -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("LDLIBRARY"))') From 359493100e66e7e4231a3bafe120c84b15317e6a Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Fri, 26 Aug 2016 15:05:09 -0700 Subject: [PATCH 1255/2656] Issue #478: Add more logging --- makefiles/Makefile.pylib.Linux | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/makefiles/Makefile.pylib.Linux b/makefiles/Makefile.pylib.Linux index 0d2dfc05..ee195961 100644 --- a/makefiles/Makefile.pylib.Linux +++ b/makefiles/Makefile.pylib.Linux @@ -69,15 +69,16 @@ endif PYLIBS = $(shell $(PY_CFG_EXE) --libs) -$(info "*** PY_CFG_EXE: $(PY_CFG_EXE)") -$(info "*** PY_LIBS: $(PYLIBS)") -$(info "*** PY_VER: $(PYTHON_VERSION)") -$(info "*** PY_MAJ: $(PYTHON_MAJOR_VERSION)") -$(info "*** PY_MIN: $(PYTHON_MINOR_VERSION)") -$(info "*** PYTHON: $(shell which python 2>/dev/null)") -$(info "*** PYTHON-CONFIG: $(shell which python-config 2>/dev/null)") -$(info "*** PYTHON3-CONFIG: $(shell which python3-config 2>/dev/null)") -$(info "*** PYTHON3.4-CONFIG: $(shell which python3.4-config 2>/dev/null)") +$(info "*** PY_CFG_EXE: $(PY_CFG_EXE)") +$(info "*** PY_LIBS: $(PYLIBS)") +$(info "*** PY_VER: $(PYTHON_VERSION)") +$(info "*** PY_MAJ: $(PYTHON_MAJOR_VERSION)") +$(info "*** PY_MIN: $(PYTHON_MINOR_VERSION)") +$(info "*** PYTHON: $(shell which python 2>/dev/null)") +$(info "*** PYTHON-CONFIG: $(shell which python-config 2>/dev/null)") +$(info "*** PYTHON-CONFIG: $(shell which $(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config 2>/dev/null)") +$(info "*** PYTHON-CONFIG: $(shell which $(PYTHON_BIN)$(PYTHON_VERSION)-config 2>/dev/null)") +$(info "*** PYTHON3.4.4-CONFIG: $(shell which python3.4.4-config 2>/dev/null)") PYTHON_INCLUDEDIR := $(shell $(PYTHON_BIN) -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())') PYTHON_DYN_LIB:=$(shell $(PYTHON_BIN) -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("LDLIBRARY"))') From 95f82de4b7678ad2ada4658c7279719c2b1c0706 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Fri, 26 Aug 2016 15:23:51 -0700 Subject: [PATCH 1256/2656] Issue #478: Fix travis-ci file --- .travis.yml | 5 +++-- makefiles/Makefile.pylib.Linux | 11 ----------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6537a9d1..5e22ed06 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,8 +15,9 @@ install: - sudo apt-get install -qq iverilog-daily - pip install coverage - pip install xunitparser - - if [ $( python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") -eq 3 ]; then ln -s /opt/python/3.4.2/bin/python3.4-config /home/travis/virtualenv/python3.4/bin/python-config; fi - - if [ $( python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") -eq 3 ]; then export LD_LIBRARY_PATH=/opt/python/3.4.2/lib:$LD_LIBRARY_PATH; fi + - if [ $( python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") -eq 3 ]; then ln -s /opt/python/3.4.4/bin/python3.4-config /home/travis/virtualenv/python3.4/bin/python3-config; fi + - if [ $( python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") -eq 3 ]; then ln -s /opt/python/3.4.4/bin/python3.4-config /home/travis/virtualenv/python3.4/bin/python-config; fi + - if [ $( python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") -eq 3 ]; then export LD_LIBRARY_PATH=/opt/python/3.4.4/lib:$LD_LIBRARY_PATH; fi - export PYTHONPATH=$PYTHONPATH:$(python -c "from __future__ import print_function; from distutils.sysconfig import get_python_lib; print(get_python_lib())") script: - make test diff --git a/makefiles/Makefile.pylib.Linux b/makefiles/Makefile.pylib.Linux index ee195961..5fa4b6c5 100644 --- a/makefiles/Makefile.pylib.Linux +++ b/makefiles/Makefile.pylib.Linux @@ -69,16 +69,5 @@ endif PYLIBS = $(shell $(PY_CFG_EXE) --libs) -$(info "*** PY_CFG_EXE: $(PY_CFG_EXE)") -$(info "*** PY_LIBS: $(PYLIBS)") -$(info "*** PY_VER: $(PYTHON_VERSION)") -$(info "*** PY_MAJ: $(PYTHON_MAJOR_VERSION)") -$(info "*** PY_MIN: $(PYTHON_MINOR_VERSION)") -$(info "*** PYTHON: $(shell which python 2>/dev/null)") -$(info "*** PYTHON-CONFIG: $(shell which python-config 2>/dev/null)") -$(info "*** PYTHON-CONFIG: $(shell which $(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config 2>/dev/null)") -$(info "*** PYTHON-CONFIG: $(shell which $(PYTHON_BIN)$(PYTHON_VERSION)-config 2>/dev/null)") -$(info "*** PYTHON3.4.4-CONFIG: $(shell which python3.4.4-config 2>/dev/null)") - PYTHON_INCLUDEDIR := $(shell $(PYTHON_BIN) -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())') PYTHON_DYN_LIB:=$(shell $(PYTHON_BIN) -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("LDLIBRARY"))') From 3a40344aad7229e20ef80ecc9c22d2b802ba6858 Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 31 Aug 2016 11:57:28 -0500 Subject: [PATCH 1257/2656] fixed error message --- cocotb/decorators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index a51160b0..0433dbb6 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -100,7 +100,7 @@ def __init__(self, inst, parent): self.retval = None if not hasattr(self._coro, "send"): - self.log.error("%s isn't a value coroutine! Did you use the yield " + self.log.error("%s isn't a valid coroutine! Did you use the yield " "keyword?" % self.funcname) raise CoroutineComplete(callback=self._finished_cb) From 84664c99449f13edc09d9c743376c2c6914a7008 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 9 Sep 2016 12:36:35 -0500 Subject: [PATCH 1258/2656] adding LD_LIBRARY_PATH to go along with unduemake-1.2.12 --- makefiles/simulators/Makefile.questa | 1 + 1 file changed, 1 insertion(+) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index c958093b..4a9bb018 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -132,6 +132,7 @@ INT_LIBS := $(COCOTB_VPI_LIB) $(COCOTB_FLI_LIB) # Append or remove values to INT_LIBS depenending on your license results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) \ GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) \ $(CMD) -do runsim.do 2>&1 | tee sim.log # Potential fix for buffered stdout, YMMV From f724c520cf610aa243c6bbc99b9223247e9570e5 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 9 Sep 2016 12:38:33 -0500 Subject: [PATCH 1259/2656] replace spaces with tab --- makefiles/simulators/Makefile.questa | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 4a9bb018..422ae99a 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -132,7 +132,7 @@ INT_LIBS := $(COCOTB_VPI_LIB) $(COCOTB_FLI_LIB) # Append or remove values to INT_LIBS depenending on your license results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) \ + LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) \ GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) \ $(CMD) -do runsim.do 2>&1 | tee sim.log # Potential fix for buffered stdout, YMMV From 20c98b38b32b0da4b05158ddfe8c24005d7408e8 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 12 Sep 2016 08:30:41 -0500 Subject: [PATCH 1260/2656] use recursively expanded assignment to LIB_LOAD instead of simply expanded assignment --- makefiles/simulators/Makefile.questa | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 422ae99a..4cdf0e5a 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -122,7 +122,7 @@ OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ LIB_LOAD := PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) NEW_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-zA-Z]\):\//\/\1\//g' -e 's/;/:/g') else -LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) +LIB_LOAD = LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) NEW_PYTHONPATH := $(PYTHONPATH) endif @@ -132,7 +132,6 @@ INT_LIBS := $(COCOTB_VPI_LIB) $(COCOTB_FLI_LIB) # Append or remove values to INT_LIBS depenending on your license results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) \ GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) \ $(CMD) -do runsim.do 2>&1 | tee sim.log # Potential fix for buffered stdout, YMMV From 868e48f07197c10deaae906d3ab4e68639c5690d Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 13 Sep 2016 05:15:27 -0500 Subject: [PATCH 1261/2656] Fixes #21: Update sims that do not append to LD_LIBRARY_PATH already --- makefiles/simulators/Makefile.aldec | 4 ++-- makefiles/simulators/Makefile.icarus | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 56400d97..07f7d1e6 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -129,12 +129,12 @@ MINGW_BIN_DIR = $(shell dirname $(shell which gcc)) EXTRA_LIBS := -laldecpli EXTRA_LIBDIRS := -L$(ALDEC_BIN_DIR) OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') -LIB_LOAD := PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) +LIB_LOAD = PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) NEW_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-zA-Z]\):\//\/\1\//g' -e 's/;/:/g') else EXTRA_LIBS = -laldecpli EXTRA_LIBDIRS = -L$(ALDEC_BIN_DIR)/Linux64 -LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) +LIB_LOAD = LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) NEW_PYTHONPATH := $(PYTHONPATH) endif diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index abf3a892..af024e37 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -65,7 +65,7 @@ ifeq ($(OS),Msys) EXTRA_LIBS := -lvpi EXTRA_LIBDIRS := -L$(ICARUS_BIN_DIR)/../lib OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') -LIB_LOAD := PATH=$(OLD_PATH):$(LIB_DIR) +LIB_LOAD = PATH=$(OLD_PATH):$(LIB_DIR) NEW_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-zA-Z]\):\//\/\1\//g' -e 's/;/:/g') else From c5277f18d58a2269c02da849c518414e1c164808 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 13 Sep 2016 11:59:31 -0500 Subject: [PATCH 1262/2656] changes that allow driving and capturing of troublesome signal names For example: a signal name such as 'alert.sub_alert[1].trigger' is not a legal python attribute name. Allow the used to provide a dictionary in the signals argument that gives the mapping between signal names and valid python attribute names. --- cocotb/bus.py | 81 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 32 deletions(-) diff --git a/cocotb/bus.py b/cocotb/bus.py index b70d9db8..62af759d 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -34,6 +34,15 @@ """ from cocotb.result import TestError +def _build_sig_attr_dict(signals): + if isinstance(signals, dict): + return signals + else: + sig_to_attr = {} + for sig in signals: + sig_to_attr[sig] = sig + return sig_to_attr + class Bus(object): """ @@ -53,16 +62,21 @@ class Bus(object): def __init__(self, entity, name, signals, optional_signals=[], bus_separator="_"): """ Args: - entity (SimHandle): SimHandle instance to the entity containing the - bus - name (str): name of the bus. None for nameless bus, e.g. - bus-signals in an interface or a modport - (untested on struct/record, but could work here - as well) - signals (list): array of signal names + entity (SimHandle): SimHandle instance to the entity containing the + bus + name (str): name of the bus. None for nameless bus, e.g. + bus-signals in an interface or a modport + (untested on struct/record, but could work here + as well) + signals (list/dict): In the case of an obj (passed to drive/capture) + that has the same attribute names as the signal + names of the bus, the signals agument can be a list + of those names. When obj has different attribute names, + the signals arg should be a dict that maps bus attribute names + to obj signal names Kwargs: - optional_signals (list): array of optional signal names + optional_signals (list/dict): optional signals (see signals argument above for details) bus_separator (str): characters to use as separator between bus name and signal name @@ -71,29 +85,28 @@ def __init__(self, entity, name, signals, optional_signals=[], bus_separator="_" self._name = name self._signals = {} - for signal in signals: + for attr_name, sig_name in _build_sig_attr_dict(signals).iteritems(): if name: - signame = name + bus_separator + signal + signame = name + bus_separator + sig_name else: - signame = signal - setattr(self, signal, getattr(entity, signame)) - self._signals[signal] = getattr(self, signal) + signame = sig_name + setattr(self, attr_name, getattr(entity, signame)) + self._signals[attr_name] = getattr(self, attr_name) # Also support a set of optional signals that don't have to be present - for signal in optional_signals: + for attr_name, sig_name in _build_sig_attr_dict(optional_signals).iteritems(): if name: - signame = name + "_" + signal + signame = name + bus_separator + sig_name else: - signame = signal + signame = sig_name # Attempts to access a signal that doesn't exist will print a # backtrace so we 'peek' first, slightly un-pythonic if entity.__hasattr__(signame): - hdl = getattr(entity, signame) - setattr(self, signal, hdl) - self._signals[signal] = getattr(self, signal) + setattr(self, attr_name, getattr(entity, signame)) + self._signals[attr_name] = getattr(self, attr_name) else: self._entity._log.debug("Ignoring optional missing signal " - "%s on bus %s" % (signal, name)) + "%s on bus %s" % (sig_name, name)) def drive(self, obj, strict=False): """ @@ -109,16 +122,18 @@ def drive(self, obj, strict=False): Raises: AttributeError """ - for name, hdl in self._signals.items(): - if not hasattr(obj, name): + for attr_name, hdl in self._signals.iteritems(): + if not hasattr(obj, attr_name): if strict: - msg = ("Unable to drive onto %s.%s because %s is missing " - "attribute %s" % self._entity._name, self._name, - obj.__class__.__name__, name) + msg = ("Unable to drive onto {0}.{1} because {2} is missing " + "attribute {3}".format(self._entity._name, + self._name, + obj.__class__.__name__, + attr_name)) raise AttributeError(msg) else: continue - val = getattr(obj, name) + val = getattr(obj, attr_name) hdl <= val def capture(self, obj, strict=False): @@ -135,16 +150,18 @@ def capture(self, obj, strict=False): Raises: AttributeError """ - for name, hdl in self._signals.items(): - if not hasattr(obj, name): + for attr_name, hdl in self._signals.iteritems(): + if not hasattr(obj, attr_name): if strict: - msg = ("Unable to capture from %s.%s because %s is missing " - "attribute %s" % self._entity._name, self._name, - obj.__class__.__name__, name) + msg = ("Unable to capture from {0}.{1} because {2} is missing " + "attribute {3}".format(self._entity._name, + self._name, + obj.__class__.__name__, + attr_name)) raise AttributeError(msg) else: continue - setattr(obj, name, hdl.value) + setattr(obj, attr_name, hdl.value) def __le__(self, value): From b9314804e64df1c17ad3f78ffc2a2afdd8501412 Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 14 Sep 2016 10:39:45 -0500 Subject: [PATCH 1263/2656] changing the create_error calls to raise_error to match import --- cocotb/decorators.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index f6824736..af71fa21 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -133,7 +133,7 @@ def send(self, value): raise CoroutineComplete(callback=self._finished_cb) except Exception as e: self._finished = True - raise create_error(self, "Send raised exception: %s" % (str(e))) + raise raise_error(self, "Send raised exception: %s" % (str(e))) def throw(self, exc): return self._coro.throw(exc) @@ -219,7 +219,7 @@ def send(self, value): except StopIteration: raise TestSuccess() except Exception as e: - raise create_error(self, "Send raised exception: %s" % (str(e))) + raise raise_error(self, "Send raised exception: %s" % (str(e))) def _handle_error_message(self, msg): self.error_messages.append(msg) @@ -386,7 +386,7 @@ def _wrapped_test(*args, **kwargs): try: return RunningTest(self._func(*args, **kwargs), self) except Exception as e: - raise create_error(self, str(e)) + raise raise_error(self, str(e)) _wrapped_test.im_test = True # For auto-regressions _wrapped_test.name = self._func.__name__ From 5208f66f8e64ed1a470df1bc2750675d81e41d0c Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 14 Sep 2016 10:39:45 -0500 Subject: [PATCH 1264/2656] changing the create_error calls to raise_error to match import --- cocotb/decorators.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index f6824736..af71fa21 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -133,7 +133,7 @@ def send(self, value): raise CoroutineComplete(callback=self._finished_cb) except Exception as e: self._finished = True - raise create_error(self, "Send raised exception: %s" % (str(e))) + raise raise_error(self, "Send raised exception: %s" % (str(e))) def throw(self, exc): return self._coro.throw(exc) @@ -219,7 +219,7 @@ def send(self, value): except StopIteration: raise TestSuccess() except Exception as e: - raise create_error(self, "Send raised exception: %s" % (str(e))) + raise raise_error(self, "Send raised exception: %s" % (str(e))) def _handle_error_message(self, msg): self.error_messages.append(msg) @@ -386,7 +386,7 @@ def _wrapped_test(*args, **kwargs): try: return RunningTest(self._func(*args, **kwargs), self) except Exception as e: - raise create_error(self, str(e)) + raise raise_error(self, str(e)) _wrapped_test.im_test = True # For auto-regressions _wrapped_test.name = self._func.__name__ From b8b7d9e6e1a435b47a01811d57669aa67f93dc6e Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 20 Sep 2016 04:38:33 -0500 Subject: [PATCH 1265/2656] Issue #483: Add test for ClockCycles --- tests/test_cases/test_cocotb/test_cocotb.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index a3b23c59..fd479bf3 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -36,7 +36,7 @@ import cocotb from cocotb.triggers import (Timer, Join, RisingEdge, FallingEdge, Edge, - ReadOnly, ReadWrite) + ReadOnly, ReadWrite, ClockCycles) from cocotb.clock import Clock from cocotb.result import ReturnValue, TestFailure, TestError, TestSuccess from cocotb.utils import get_sim_time @@ -598,6 +598,24 @@ def test_logging_with_args(dut): yield Timer(100) #Make it do something with time +@cocotb.test() +def test_clock_cycles(dut): + """ + Test the ClockCycles Trigger + """ + + clk = dut.clk + + clk_gen = cocotb.fork(Clock(clk, 100).start()) + + yield RisingEdge(clk) + + dut.log.info("After one edge") + + yield ClockCycles(clk, 10) + + dut.log.info("After 10 edges") + @cocotb.test() def test_binary_value(dut): """ From fa6e323caa8b620d736344bb8ea2a2427f414661 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 20 Sep 2016 05:05:12 -0500 Subject: [PATCH 1266/2656] Fixes #29 - respect byteenable signal if it's present --- cocotb/drivers/avalon.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 4faabae7..a025360b 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -389,6 +389,31 @@ def _respond(self): if self._writeable and self.bus.write.value: addr = self.bus.address.value.integer data = self.bus.writedata.value.integer + #This should mask on byteenable... + if hasattr(self.bus, "byteenable"): + byteenable = int(self.bus.byteenable.value) + mask = 0 + oldmask = 0 + olddata= 0 + if (addr in self._mem): + olddata = self._mem[addr] + self.log.debug("Old Data : %x" % olddata) + self.log.debug("Data in : %x" % data) + self.log.debug("Width : %d" % self._width) + self.log.debug("Byteenable: %x" % byteenable) + for i in xrange(self._width/8): + if (byteenable & 2**i): + mask |= 0xFF << (8*i) + else: + oldmask |= 0xFF << (8*i) + + self.log.debug("Data mask : %x" % mask) + self.log.debug("Old mask : %x" % oldmask) + + data = (data & mask) | (olddata & oldmask) + + self.log.debug("Data out : %x" % data) + self.log.debug("Write to address 0x%x -> 0x%x" % (addr, data)) self._mem[addr] = data From 53a1a42b28021ca9eb0b5237bac9b051be57a9cf Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 20 Sep 2016 05:17:48 -0500 Subject: [PATCH 1267/2656] Fixes #31: Correct the arguments to `combine_results.py` for Jenkins rule --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7c3338c3..db291da2 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ do_tests: # For jenkins we use the exit code to detect compile errors or catestrphic # failures and the xml to track test results jenkins: do_tests - ./bin/combine_results.py --squash_rc + ./bin/combine_results.py --suppress_rc --testsuites_name=cocotb_regression # By default want the exit code to indicate the test results test: do_tests From 50245586c9711f228f3adcd3b1699de72bef0f00 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 20 Sep 2016 05:21:10 -0500 Subject: [PATCH 1268/2656] Combine results expects suppress_rc not squash_rc --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7c3338c3..a4fedc16 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ do_tests: # For jenkins we use the exit code to detect compile errors or catestrphic # failures and the xml to track test results jenkins: do_tests - ./bin/combine_results.py --squash_rc + ./bin/combine_results.py --suppress_rc # By default want the exit code to indicate the test results test: do_tests From 4acd1899c9090e3154d3ee311e1cdde5e11f87d1 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 24 Oct 2016 06:09:37 -0500 Subject: [PATCH 1269/2656] Add random seed to results.xml output as a testsuite property --- cocotb/__init__.py | 2 +- cocotb/regression.py | 5 ++++- cocotb/xunit_reporter.py | 6 ++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 8be9def5..65bd14e5 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -145,7 +145,7 @@ def _initialise_testbench(root_name): global regression - regression = RegressionManager(root_name, modules, tests=test_str) + regression = RegressionManager(root_name, modules, tests=test_str, seed=seed) regression.initialise() regression.execute() diff --git a/cocotb/regression.py b/cocotb/regression.py index b9234a4c..61c4c7a6 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -70,7 +70,7 @@ def _my_import(name): class RegressionManager(object): """Encapsulates all regression capability into a single place""" - def __init__(self, root_name, modules, tests=None): + def __init__(self, root_name, modules, tests=None, seed=None): """ Args: modules (list): A list of python module names to run @@ -85,6 +85,7 @@ def __init__(self, root_name, modules, tests=None): self._running_test = None self._cov = None self.log = SimLog("cocotb.regression") + self._seed = seed def initialise(self): @@ -95,6 +96,8 @@ def initialise(self): self.xunit = XUnitReporter() self.xunit.add_testsuite(name="all", tests=repr(self.ntests), package="all") + if (self._seed is not None): + self.xunit.add_property(name="random_seed", value=self._seed) if coverage is not None: self.log.info("Enabling coverage collection of Python code") diff --git a/cocotb/xunit_reporter.py b/cocotb/xunit_reporter.py index 2a9c1cd0..e20cf13c 100644 --- a/cocotb/xunit_reporter.py +++ b/cocotb/xunit_reporter.py @@ -79,6 +79,12 @@ def add_testcase(self, testsuite=None, **kwargs): self.last_testcase = SubElement(testsuite, "testcase", **kwargs) return self.last_testcase + def add_property(self, testsuite=None, **kwargs): + if testsuite is None: + testsuite = self.last_testsuite + self.last_property = SubElement(testsuite, "property", **kwargs) + return self.last_property + def update_testsuite(self, testsuite=None, **kwargs): if testsuite is None: testsuite = self.last_testsuite From cdb320275845e9270e60d5f45161c1810305a639 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 24 Oct 2016 06:42:13 -0500 Subject: [PATCH 1270/2656] Need to pass value in as a string --- cocotb/regression.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 14b7b057..f8ef932c 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -105,7 +105,7 @@ def initialise(self): package=package_name) if (self._seed is not None): - self.xunit.add_property(name="random_seed", value=self._seed) + self.xunit.add_property(name="random_seed", value=("%d"%self._seed)) if coverage is not None: self.log.info("Enabling coverage collection of Python code") From 611f5f47361d03b5c6d2f6af4e3d15c5c330708c Mon Sep 17 00:00:00 2001 From: Nathan Kohagen Date: Tue, 25 Oct 2016 17:27:53 -0700 Subject: [PATCH 1271/2656] python3 compatibility fix - print as a keyword replaced with print() as a function --- makefiles/Makefile.pylib.Msys | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/makefiles/Makefile.pylib.Msys b/makefiles/Makefile.pylib.Msys index 93eee3e9..7520fd4e 100644 --- a/makefiles/Makefile.pylib.Msys +++ b/makefiles/Makefile.pylib.Msys @@ -42,14 +42,14 @@ endif PYTHON_BIN=$(PYTHON_DIR)/python.exe # We might work with other Python versions -LOCAL_PYTHON_VERSION?=$(shell $(PYTHON_BIN) -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_version()') -PYTHON_DYNLIBDIR:=$(shell $(PYTHON_BIN) -c 'from distutils import sysconfig; print sysconfig.get_config_var("DESTSHARED")') +LOCAL_PYTHON_VERSION?=$(shell $(PYTHON_BIN) -c 'import distutils.sysconfig; print( distutils.sysconfig.get_python_version() )') +PYTHON_DYNLIBDIR:=$(shell $(PYTHON_BIN) -c 'from distutils import sysconfig; print( sysconfig.get_config_var("DESTSHARED") )') PYTHON_LIBDIR=$(PYTHON_DIR)/libs period := . empty := PYTHON_VERSION=$(subst $(period),$(empty),$(LOCAL_PYTHON_VERSION)) -PYTHON_INCLUDEDIR := $(shell $(PYTHON_BIN) -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_inc()') +PYTHON_INCLUDEDIR := $(shell $(PYTHON_BIN) -c 'import distutils.sysconfig; print( distutils.sysconfig.get_python_inc() )') PYLIBS = -lPython$(PYTHON_VERSION) -PYTHON_DYN_LIB = Python$(PYTHON_VERSION).dll \ No newline at end of file +PYTHON_DYN_LIB = Python$(PYTHON_VERSION).dll From 673e36673bfdc65f08f1204a800681fffbf63f4c Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 27 Oct 2016 09:20:50 -0500 Subject: [PATCH 1272/2656] Fixes #40: Don't assume the iterable has a length --- cocotb/drivers/avalon.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 47175c7b..28c0ccb1 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -688,12 +688,12 @@ def _driver_send(self, pkt, sync=True): # Avoid spurious object creation by recycling - self.log.debug("Sending packet of length %d bytes" % len(pkt)) - self.log.debug(hexdump(pkt)) if isinstance(pkt, str): + self.log.debug("Sending packet of length %d bytes" % len(pkt)) + self.log.debug(hexdump(pkt)) yield self._send_string(pkt, sync=sync) + self.log.info("Sucessfully sent packet of length %d bytes" % len(pkt)) else: yield self._send_iterable(pkt, sync=sync) - self.log.info("Sucessfully sent packet of length %d bytes" % len(pkt)) From b554cde535e6713bd769b785b7715f9398e05455 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 10 Nov 2016 22:39:56 -0600 Subject: [PATCH 1273/2656] Added capture method to be symmetric with drive --- cocotb/bus.py | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/cocotb/bus.py b/cocotb/bus.py index 62af759d..8df5a39b 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -136,33 +136,31 @@ def drive(self, obj, strict=False): val = getattr(obj, attr_name) hdl <= val - def capture(self, obj, strict=False): + def capture(self): """ Capture the values from the bus. - Args: - obj (any type) : object with attribute names that match the bus - signals + Returns: + A dict that supports access by attribute, each attribute corresponds to each signal's value + """ + class _Capture(dict): + def __getattr__(self, name): + if name in self: + return self[name] + else: + raise RuntimeError('signal {} not present in bus'.format(name)) - Kwargs: - strict (bool) : Check that all signals are being assigned + def __setattr__(self, name, value): + raise RuntimeError('modifying a bus capture is not supported') - Raises: - AttributeError - """ + def __delattr__(self, name): + raise RuntimeError('modifying a bus capture is not supported') + + _capture = _Capture() for attr_name, hdl in self._signals.iteritems(): - if not hasattr(obj, attr_name): - if strict: - msg = ("Unable to capture from {0}.{1} because {2} is missing " - "attribute {3}".format(self._entity._name, - self._name, - obj.__class__.__name__, - attr_name)) - raise AttributeError(msg) - else: - continue - setattr(obj, attr_name, hdl.value) + _capture[attr_name] = hdl.value + return _capture def __le__(self, value): """Overload the less than or equal to operator for value assignment""" From f58a01913b37f8e519624e3f939348f2a0b16d44 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 21 Nov 2016 13:01:48 -0600 Subject: [PATCH 1274/2656] Added sample fn, modifies obj in place Capture returns a new object, sample modifies in place --- cocotb/bus.py | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/cocotb/bus.py b/cocotb/bus.py index 8df5a39b..138b4c0c 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -32,7 +32,7 @@ A bus is simply defined as a collection of signals """ -from cocotb.result import TestError + def _build_sig_attr_dict(signals): if isinstance(signals, dict): @@ -138,7 +138,7 @@ def drive(self, obj, strict=False): def capture(self): """ - Capture the values from the bus. + Capture the values from the bus, returning an object representing the capture Returns: A dict that supports access by attribute, each attribute corresponds to each signal's value @@ -151,10 +151,10 @@ def __getattr__(self, name): raise RuntimeError('signal {} not present in bus'.format(name)) def __setattr__(self, name, value): - raise RuntimeError('modifying a bus capture is not supported') + raise RuntimeError('modifying a bus capture is not supported') def __delattr__(self, name): - raise RuntimeError('modifying a bus capture is not supported') + raise RuntimeError('modifying a bus capture is not supported') _capture = _Capture() for attr_name, hdl in self._signals.iteritems(): @@ -162,6 +162,33 @@ def __delattr__(self, name): return _capture + def sample(self, obj, strict=False): + """ + Sample the values from the bus, assigning them to obj. + + Args: + obj (any type) : object with attribute names that match the bus + signals + + Kwargs: + strict (bool) : Check that all signals being sampled are present in obj + + Raises: + AttributeError + """ + for attr_name, hdl in self._signals.items(): + if not hasattr(obj, attr_name): + if strict: + msg = ("Unable to sample from {0}.{1} because {2} is missing " + "attribute {3}".format(self._entity._name, + self._name, + obj.__class__.__name__, + attr_name)) + raise AttributeError(msg) + else: + continue + setattr(obj, attr_name, hdl.value) + def __le__(self, value): """Overload the less than or equal to operator for value assignment""" self.drive(value) From 5c96dd9f30cca7e1de462d34416dce2c79756296 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 29 Nov 2016 04:16:39 -0600 Subject: [PATCH 1275/2656] Testcase files --- .../multi_dimension_array/cocotb_array_pkg.sv | 6 ++++ .../cocotb_struct_pkg.sv | 35 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/designs/multi_dimension_array/cocotb_array_pkg.sv create mode 100644 tests/designs/multi_dimension_array/cocotb_struct_pkg.sv diff --git a/tests/designs/multi_dimension_array/cocotb_array_pkg.sv b/tests/designs/multi_dimension_array/cocotb_array_pkg.sv new file mode 100644 index 00000000..65857026 --- /dev/null +++ b/tests/designs/multi_dimension_array/cocotb_array_pkg.sv @@ -0,0 +1,6 @@ +package cocotb_array_pkg; + + typedef logic [2:0] test_array_entry_t; + typedef test_array_entry_t [2:0] test_2d_array_t; + typedef test_2d_array_t [2:0] test_3d_array_t; +endpackage diff --git a/tests/designs/multi_dimension_array/cocotb_struct_pkg.sv b/tests/designs/multi_dimension_array/cocotb_struct_pkg.sv new file mode 100644 index 00000000..9809d3e7 --- /dev/null +++ b/tests/designs/multi_dimension_array/cocotb_struct_pkg.sv @@ -0,0 +1,35 @@ +package cocotb_struct_pkg; + + typedef logic [2:0] test_array_entry_t; + typedef test_array_entry_t [2:0] test_2d_array_t; + typedef test_2d_array_t [2:0] test_3d_array_t; + + typedef struct packed { + logic [2:0] vect_packed; + logic [2:0][2:0] vect_packed_packed; + test_array_entry_t array_packed; + test_array_entry_t [2:0] array_packed_packed; + } struct_packed_t; + + typedef struct_packed_t [2:0] struct_packed_arr_packed_t; + typedef struct_packed_t struct_packed_arr_unpacked_t [2:0]; + + typedef struct_packed_t [2:0][2:0] struct_packed_arr_packed_packed_t; + typedef struct_packed_t [2:0] struct_packed_arr_packed_unpacked_t [2:0]; + typedef struct_packed_t struct_packed_arr_unpacked_unpacked_t [2:0][2:0]; + + typedef struct unpacked { + logic [2:0] vect_packed; + logic vect_unpacked[2:0]; + + logic [2:0] vect_packed_unpacked[2:0]; + logic vect_unpacked_unpacked[2:0]; + + test_array_entry_t array_packed; + test_array_entry_t [2:0] array_packed_packed; + test_array_entry_t array_packed_unpacked[2:0]; + + test_array_entry_t [2:0] array_packed_packed_unpacked[2:0]; + } struct_unpacked_t; + +endpackage From 5c1c9a50714fb9ea85932774343fc1f0dbf35fb4 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 29 Nov 2016 05:06:33 -0600 Subject: [PATCH 1276/2656] Tidy up testcase --- .../test_cocotb_array.py | 198 ++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 tests/test_cases/test_multi_dimension_array/test_cocotb_array.py diff --git a/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py b/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py new file mode 100644 index 00000000..fcb8df57 --- /dev/null +++ b/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py @@ -0,0 +1,198 @@ +import logging +import random + +import cocotb +from cocotb.result import TestFailure +from cocotb.triggers import Timer + +import simulator + +@cocotb.test() +def test_in_vect_packed(dut): + yield Timer(10) + print"Setting: dut.in_vect_packed type %s" % type(dut.in_vect_packed) + dut.in_vect_packed = 0x5 + yield Timer(10) + print"Getting: dut.out_vect_packed type %s" % type(dut.out_vect_packed) + if dut.out_vect_packed != 0x5: + raise TestFailure("Failed to readback dut.out_vect_packed") + +@cocotb.test() +def test_in_vect_unpacked(dut): + yield Timer(10) + print"Setting: dut.in_vect_unpacked type %s" % type(dut.in_vect_unpacked) + dut.in_vect_unpacked = [0x1, 0x0, 0x1] + yield Timer(10) + print"Getting: dut.out_vect_unpacked type %s" % type( dut.out_vect_unpacked) + if dut.out_vect_unpacked != [0x1, 0x0, 0x1]: + raise TestFailure("Failed to readback dut.out_vect_unpacked") + +@cocotb.test() +def test_in_arr(dut): + yield Timer(10) + print"Setting: dut.in_arr type %s" % type(dut.in_arr) + dut.in_arr = 0x5 + yield Timer(10) + print"Getting: dut.out_arr type %s" % type( dut.out_arr) + if dut.out_arr != 0x5: + raise TestFailure("Failed to readback dut.out_arr") + +@cocotb.test() +def test_in_2d_vect_packed_packed(dut): + yield Timer(10) + print"Setting: dut.in_2d_vect_packed_packed type %s" % type(dut.in_2d_vect_packed_packed) + dut.in_2d_vect_packed_packed = (0x5 << 6) | (0x5 << 3) | 0x5 + yield Timer(10) + print"Getting: dut.out_2d_vect_packed_packed type %s" % type( dut.out_2d_vect_packed_packed) + if dut.out_2d_vect_packed_packed != (0x5 << 6) | (0x5 << 3) | 0x5: + raise TestFailure("Failed to readback dut.out_2d_vect_packed_packed") + +@cocotb.test() +def test_in_2d_vect_packed_unpacked(dut): + yield Timer(10) + print"Setting: dut.in_2d_vect_packed_unpacked type %s" % type(dut.in_2d_vect_packed_unpacked) + dut.in_2d_vect_packed_unpacked = [0x5, 0x5, 0x5] + yield Timer(10) + print"Getting: dut.out_2d_vect_packed_unpacked type %s" % type( dut.out_2d_vect_packed_unpacked) + if dut.out_2d_vect_packed_unpacked != [0x5, 0x5, 0x5]: + raise TestFailure("Failed to readback dut.out_2d_vect_packed_unpacked") + +@cocotb.test() +def test_in_2d_vect_unpacked_unpacked(dut): + yield Timer(10) + print"Setting: dut.in_2d_vect_unpacked_unpacked type %s" % type(dut.in_2d_vect_unpacked_unpacked) + dut.in_2d_vect_unpacked_unpacked = 3*[[0x1, 0x0, 0x1]] + yield Timer(10) + print"Getting: dut.out_2d_vect_unpacked_unpacked type %s" % type( dut.out_2d_vect_unpacked_unpacked) + if dut.out_2d_vect_unpacked_unpacked != 3*[[0x1, 0x0, 0x1]]: + raise TestFailure("Failed to readback dut.out_2d_vect_unpacked_unpacked") + +@cocotb.test() +def test_in_arr_packed(dut): + yield Timer(10) + print"Setting: dut.in_arr_packed type %s" % type(dut.in_arr_packed) + dut.in_arr_packed = 365 + yield Timer(10) + print"Getting: dut.out_arr_packed type %s" % type( dut.out_arr_packed) + if dut.out_arr_packed != 365: + raise TestFailure("Failed to readback dut.out_arr_packed") + +@cocotb.test() +def test_in_arr_unpacked(dut): + yield Timer(10) + print"Setting: dut.in_arr_unpackedtype %s" % type(dut.in_arr_unpacked) + dut.in_arr_unpacked = [0x5, 0x5, 0x5] + yield Timer(10) + print"Getting: dut.out_arr_unpackedtype %s" % type( dut.out_arr_unpacked) + if dut.out_arr_unpacked != [0x5, 0x5, 0x5]: + raise TestFailure("Failed to readback dut.out_arr_unpacked") + +@cocotb.test() +def test_in_2d_arr(dut): + yield Timer(10) + print"Setting: dut.in_2d_arr type %s" % type(dut.in_2d_arr) + dut.in_2d_arr = 365 + yield Timer(10) + print"Getting: dut.out_2d_arr type %s" % type( dut.out_2d_arr) + if dut.out_2d_arr != 365: + raise TestFailure("Failed to readback dut.out_2d_arr") + +@cocotb.test() +def test_in_vect_packed_packed_packed(dut): + yield Timer(10) + print"Setting: dut.in_vect_packed_packed_packed type %s" % type(dut.in_vect_packed_packed_packed) + dut.in_vect_packed_packed_packed = 95869805 + yield Timer(10) + print"Getting: dut.out_vect_packed_packed_packed type %s" % type( dut.out_vect_packed_packed_packed) + if dut.out_vect_packed_packed_packed != 95869805: + raise TestFailure("Failed to readback dut.out_vect_packed_packed_packed") + +@cocotb.test() +def test_in_vect_packed_packed_unpacked(dut): + yield Timer(10) + print"Setting: dut.in_vect_packed_packed_unpacked type %s" % type(dut.in_vect_packed_packed_unpacked) + dut.in_vect_packed_packed_unpacked = [95869805, 95869805, 95869805] + yield Timer(10) + print"Getting: dut.out_vect_packed_packed_unpacked type %s" % type( dut.out_vect_packed_packed_unpacked) + if dut.out_vect_packed_packed_unpacked != [365, 365, 365]: + raise TestFailure("Failed to readback dut.out_vect_packed_packed_unpacked") + +@cocotb.test() +def test_in_vect_packed_unpacked_unpacked(dut): + yield Timer(10) + print"Setting: dut.in_vect_packed_unpacked_unpacked type %s" % type(dut.in_vect_packed_unpacked_unpacked) + dut.in_vect_packed_unpacked_unpacked = 3 *[3 * [5] ] + yield Timer(10) + print"Getting: dut.out_vect_packed_unpacked_unpacked type %s" % type( dut.out_vect_packed_unpacked_unpacked) + if dut.out_vect_packed_unpacked_unpacked != 3 *[3 * [5] ]: + raise TestFailure("Failed to readback dut.out_vect_packed_unpacked_unpacked") + +@cocotb.test() +def test_in_vect_unpacked_unpacked_unpacked(dut): + yield Timer(10) + print"Setting: dut.in_vect_unpacked_unpacked_unpacked type %s" % type(dut.in_vect_unpacked_unpacked_unpacked) + dut.in_vect_unpacked_unpacked_unpacked = 3 *[3 * [[1, 0, 1]]] + yield Timer(10) + print"Getting: dut.out_vect_unpacked_unpacked_unpacked type %s" % type( dut.out_vect_unpacked_unpacked_unpacked) + if dut.out_vect_unpacked_unpacked_unpacked != 3 *[3 * [[1, 0, 1]]]: + raise TestFailure("Failed to readback dut.out_vect_unpacked_unpacked_unpacked") + +@cocotb.test() +def test_in_arr_packed_packed(dut): + yield Timer(10) + print"Setting: dut.in_arr_packed_packed type %s" % type(dut.in_arr_packed_packed) + dut.in_arr_packed_packed = (365 << 18) | (365 << 9) | (365) + yield Timer(10) + print"Getting: dut.out_arr_packed_packed type %s" % type( dut.out_arr_packed_packed) + if dut.out_arr_packed_packed != (365 << 18) | (365 << 9) | (365): + raise TestFailure("Failed to readback dut.out_arr_packed_packed") + +@cocotb.test() +def test_in_arr_packed_unpacked(dut): + yield Timer(10) + print"Setting: dut.in_arr_packed_unpacked type %s" % type(dut.in_arr_packed_unpacked) + dut.in_arr_packed_unpacked = [365, 365, 365] + yield Timer(10) + print"Getting: dut.out_arr_packed_unpacked type %s" % type( dut.out_arr_packed_unpacked) + if dut.out_arr_packed_unpacked != [365, 365, 365]: + raise TestFailure("Failed to readback dut.out_arr_packed_unpacked") + +@cocotb.test() +def test_in_arr_unpacked_unpacked(dut): + yield Timer(10) + print"Setting: dut.in_arr_unpacked_unpacked type %s" % type(dut.in_arr_unpacked_unpacked) + dut.in_arr_unpacked_unpacked = 3 *[3 * [5] ] + yield Timer(10) + print"Getting: dut.out_arr_unpacked_unpacked type %s" % type( dut.out_arr_unpacked_unpacked) + if dut.out_arr_unpacked_unpacked != 3 *[3 * [5] ]: + raise TestFailure("Failed to readback dut.out_arr_unpacked_unpacked") + +@cocotb.test() +def test_in_2d_arr_packed(dut): + yield Timer(10) + print"Setting: dut.in_2d_arr_packed type %s" % type(dut.in_2d_arr_packed) + dut.in_2d_arr_packed = (365 << 18) | (365 << 9) | (365) + yield Timer(10) + print"Getting: dut.out_2d_arr_packed type %s" % type( dut.out_2d_arr_packed) + if dut.out_2d_arr_packed != (365 << 18) | (365 << 9) | (365): + raise TestFailure("Failed to readback dut.out_2d_arr_packed") + +@cocotb.test() +def test_in_2d_arr_unpacked(dut): + yield Timer(10) + print"Setting: dut.in_2d_arr_unpacked type %s" % type(dut.in_2d_arr_unpacked) + dut.in_2d_arr_unpacked = [365, 365, 365] + yield Timer(10) + print"Getting: dut.out_2d_arr_unpacked type %s" % type( dut.out_2d_arr_unpacked) + if dut.out_2d_arr_unpacked != [365, 365, 365]: + raise TestFailure("Failed to readback dut.out_2d_arr_unpacked") + +@cocotb.test() +def test_in_3d_arr(dut): + yield Timer(10) + print"Setting: dut.in_3d_arr type %s" % type(dut.in_3d_arr) + dut.in_3d_arr = (365 << 18) | (365 << 9) | (365) + yield Timer(10) + print"Getting: dut.out_3d_arr type %s" % type( dut.out_3d_arr) + if dut.out_3d_arr != (365 << 18) | (365 << 9) | (365): + raise TestFailure("Failed to readback dut.out_3d_arr") From 2679e396242fb2b736e39a37bbe16bec9f19a587 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 29 Nov 2016 05:06:55 -0600 Subject: [PATCH 1277/2656] Makefile for testcase --- .../test_multi_dimension_array/Makefile | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/test_cases/test_multi_dimension_array/Makefile diff --git a/tests/test_cases/test_multi_dimension_array/Makefile b/tests/test_cases/test_multi_dimension_array/Makefile new file mode 100644 index 00000000..ac5194cd --- /dev/null +++ b/tests/test_cases/test_multi_dimension_array/Makefile @@ -0,0 +1,42 @@ +############################################################################### +# Copyright (c) 2016 Potential Ventures Ltd +# Copyright (c) 2016 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +ifeq ($(SIM),) +all: + @echo "Skipping test_array since icarus doesn't support indexing" +else ifeq ($(shell echo $(SIM) | tr A-Z a-z),icarus) +all: + @echo "Skipping test_array since icarus doesn't support indexing" +else + +include ../../designs/multi_dimension_array/Makefile + +MODULE = test_cocotb_array + +endif From 7ba7cf8fc1917872883d6a229c98a423337bf723 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 29 Nov 2016 05:07:02 -0600 Subject: [PATCH 1278/2656] Makefile for design --- tests/designs/multi_dimension_array/Makefile | 56 ++++++++++ .../multi_dimension_array/cocotb_array.sv | 101 ++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 tests/designs/multi_dimension_array/Makefile create mode 100644 tests/designs/multi_dimension_array/cocotb_array.sv diff --git a/tests/designs/multi_dimension_array/Makefile b/tests/designs/multi_dimension_array/Makefile new file mode 100644 index 00000000..a666580d --- /dev/null +++ b/tests/designs/multi_dimension_array/Makefile @@ -0,0 +1,56 @@ +############################################################################### +# Copyright (c) 2016 Potential Ventures Ltd +# Copyright (c) 2016 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +TOPLEVEL_LANG ?= verilog + +TOPLEVEL := cocotb_array + +ifneq ($(TOPLEVEL_LANG),verilog) + +all: + @echo "Skipping test due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being verilog" +clean:: + +else + +ifeq ($(OS),Msys) +WPWD=$(shell sh -c 'pwd -W') +else +WPWD=$(shell pwd) +endif + +COCOTB?=$(WPWD)/../../.. + +VERILOG_SOURCES = $(COCOTB)/tests/designs/multi_dimension_array/cocotb_array_pkg.sv \ + $(COCOTB)/tests/designs/multi_dimension_array/cocotb_array.sv + +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim + +endif diff --git a/tests/designs/multi_dimension_array/cocotb_array.sv b/tests/designs/multi_dimension_array/cocotb_array.sv new file mode 100644 index 00000000..65c1c8ec --- /dev/null +++ b/tests/designs/multi_dimension_array/cocotb_array.sv @@ -0,0 +1,101 @@ +import cocotb_array_pkg::*; + +module cocotb_array ( + + //INPUTS + + //Single dimensions + input logic [2:0] in_vect_packed , + input logic in_vect_unpacked[2:0] , + input test_array_entry_t in_arr , + + //2 dimensions + input logic [2:0][2:0] in_2d_vect_packed_packed , + input logic [2:0] in_2d_vect_packed_unpacked[2:0] , + input logic in_2d_vect_unpacked_unpacked[2:0][2:0] , + + input test_array_entry_t [2:0] in_arr_packed , + input test_array_entry_t in_arr_unpacked[2:0] , + input test_2d_array_t in_2d_arr , + + //3 dimensions + input logic [2:0][2:0][2:0] in_vect_packed_packed_packed , + input logic [2:0][2:0] in_vect_packed_packed_unpacked[2:0] , + input logic [2:0] in_vect_packed_unpacked_unpacked[2:0][2:0] , + input logic in_vect_unpacked_unpacked_unpacked[2:0][2:0][2:0] , + + input test_array_entry_t [2:0][2:0] in_arr_packed_packed , + input test_array_entry_t [2:0] in_arr_packed_unpacked[2:0] , + input test_array_entry_t in_arr_unpacked_unpacked[2:0][2:0] , + + input test_2d_array_t [2:0] in_2d_arr_packed , + input test_2d_array_t in_2d_arr_unpacked[2:0] , + + input test_3d_array_t in_3d_arr , + + + //OUTPUTS + //Single dimensions + output logic [2:0] out_vect_packed , + output logic out_vect_unpacked[2:0] , + output test_array_entry_t out_arr , + + //2 dimensions + output logic [2:0][2:0] out_2d_vect_packed_packed , + output logic [2:0] out_2d_vect_packed_unpacked[2:0] , + output logic out_2d_vect_unpacked_unpacked[2:0][2:0] , + + output test_array_entry_t [2:0] out_arr_packed , + output test_array_entry_t out_arr_unpacked[2:0] , + output test_2d_array_t out_2d_arr , + + //3 dimensions + output logic [2:0][2:0][2:0] out_vect_packed_packed_packed , + output logic [2:0][2:0] out_vect_packed_packed_unpacked[2:0] , + output logic [2:0] out_vect_packed_unpacked_unpacked[2:0][2:0] , + output logic out_vect_unpacked_unpacked_unpacked[2:0][2:0][2:0] , + + output test_array_entry_t [2:0][2:0] out_arr_packed_packed , + output test_array_entry_t [2:0] out_arr_packed_unpacked[2:0] , + output test_array_entry_t out_arr_unpacked_unpacked[2:0][2:0] , + + output test_2d_array_t [2:0] out_2d_arr_packed , + output test_2d_array_t out_2d_arr_unpacked[2:0] , + + output test_3d_array_t out_3d_arr + +); + +//Fairly simple passthrough of all the values... + +assign out_vect_packed = in_vect_packed ; +assign out_vect_unpacked = in_vect_unpacked ; +assign out_arr = in_arr ; + + +assign out_2d_vect_packed_packed = in_2d_vect_packed_packed ; +assign out_2d_vect_packed_unpacked = in_2d_vect_packed_unpacked ; +assign out_2d_vect_unpacked_unpacked = in_2d_vect_unpacked_unpacked ; + +assign out_arr_packed = in_arr_packed ; +assign out_arr_unpacked = in_arr_unpacked ; +assign out_2d_arr = in_2d_arr ; + + +assign out_vect_packed_packed_packed = in_vect_packed_packed_packed ; +assign out_vect_packed_packed_unpacked = in_vect_packed_packed_unpacked ; +assign out_vect_packed_unpacked_unpacked = in_vect_packed_unpacked_unpacked ; +assign out_vect_unpacked_unpacked_unpacked = in_vect_unpacked_unpacked_unpacked ; + +assign out_arr_packed_packed = in_arr_packed_packed ; +assign out_arr_packed_unpacked = in_arr_packed_unpacked ; +assign out_arr_unpacked_unpacked = in_arr_unpacked_unpacked ; + +assign out_2d_arr_packed = in_2d_arr_packed ; +assign out_2d_arr_unpacked = in_2d_arr_unpacked ; + +assign out_3d_arr = in_3d_arr ; + + + +endmodule; From d1f36856ab1b84ecb469d18dc023a02d9cc28bbc Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 29 Nov 2016 05:09:33 -0600 Subject: [PATCH 1279/2656] Update handle.py to support array set/gets a bit better --- cocotb/handle.py | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index f3f70bcd..f9bbe476 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -236,7 +236,19 @@ def __setattr__(self, name, value): if name.startswith("_"): return SimHandleBase.__setattr__(self, name, value) if self.__hasattr__(name) is not None: - return getattr(self, name)._setcachedvalue(value) + sub = self.__getattr__(name) + if type(sub) is NonHierarchyIndexableObject: + if type(value) is not list: + raise AttributeError("Attempting to set %s which is a NonHierarchyIndexableObject to something other than a list?" % (name)) + + if len(sub) != len(value): + raise IndexError("Attempting to set %s with list length %d but target has length %d" % ( + name, len(value), len(sub))) + for idx in xrange(len(value)): + sub[idx] = value[idx] + return + else: + return sub._setcachedvalue(value) if name in self._compat_mapping: return SimHandleBase.__setattr__(self, name, value) raise AttributeError("Attempt to access %s which isn't present in %s" %( @@ -248,6 +260,7 @@ def __getattr__(self, name): and cache the result to build a tree of objects """ if name in self._sub_handles: + sub = self._sub_handles[name] return self._sub_handles[name] if name.startswith("_"): @@ -352,7 +365,6 @@ def _child_path(self, name): return self._path + "[" + str(index) + "]" def __setitem__(self, index, value): - """Provide transparent assignment to indexed array handles""" raise TypeError("Not permissible to set %s at index %d" % (self._name, index)) @@ -369,13 +381,20 @@ def __iter__(self): return iter(()) def _getvalue(self): - raise TypeError("Not permissible to get values on object %s" % (self._name)) + if type(self) is NonHierarchyIndexableObject: + #Need to iterate over the sub-object + result =[] + for x in xrange(len(self)): + result.append(self[x]._getvalue()) + return result + else: + raise TypeError("Not permissible to get values on object %s type %s" % (self._name, type(self))) def setimmediatevalue(self, value): - raise TypeError("Not permissible to set values on object %s" % (self._name)) + raise TypeError("Not permissible to set values on object %s type %s" % (self._name, type(self))) def _setcachedvalue(self, value): - raise TypeError("Not permissible to set values on object %s" % (self._name)) + raise TypeError("Not permissible to set values on object %s type %s" % (self._name, type(self))) def __le__(self, value): """Overload the less than or equal to operator to @@ -452,7 +471,15 @@ def __init__(self, handle, path): def __setitem__(self, index, value): """Provide transparent assignment to indexed array handles""" - self.__getitem__(index).value = value + if type(value) is list: + if len(value) != len(self.__getitem__(index)): + raise IndexError("Assigning list of length %d to object %s of length %d" % ( + len(value), self.__getitem__(index)._fullname, len(self.__getitem__(index)))) + self._log.info("Setting item %s to %s" % (self.__getitem__(index)._fullname, value)) + for idx in xrange(len(value)): + self.__getitem__(index).__setitem__(idx, value[idx]) + else: + self.__getitem__(index).value = value def __getitem__(self, index): if isinstance(index, slice): From 2db12325b89a07ff5c307e5078d08160b7a6152c Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 29 Nov 2016 06:22:22 -0600 Subject: [PATCH 1280/2656] Assigning a list to a struct entry - requires a dict specifying the structure e.g. {"values":[1,2,3], "bits":8}, but seems to work --- cocotb/handle.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/cocotb/handle.py b/cocotb/handle.py index f3f70bcd..c39b073f 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -563,6 +563,22 @@ def setimmediatevalue(self, value): value = BinaryValue(value=cocotb.utils.pack(value), bits=len(self)) elif isinstance(value, get_python_integer_types()): value = BinaryValue(value=value, bits=len(self), bigEndian=False) + elif isinstance(value, dict): + print value + #We're given a dictionary with a list of values and a bit size... + num = 0; + vallist = value["values"] + vallist.reverse() + if len(vallist) * value["bits"] != len(self): + self._log.critical("Unable to set with array length %d of %d bit entries = %d total, target is only %d bits long" % + (len(value["values"]), value["bits"], len(value["values"]) * value["bits"], len(self))); + raise TypeError("Unable to set with array length %d of %d bit entries = %d total, target is only %d bits long" % + (len(value["values"]), value["bits"], len(value["values"]) * value["bits"], len(self))); + + for val in vallist: + num = (num << value["bits"]) + val; + value = BinaryValue(value=num, bits=len(self), bigEndian=False) + elif not isinstance(value, BinaryValue): self._log.critical("Unsupported type for value assignment: %s (%s)" % (type(value), repr(value))) raise TypeError("Unable to set simulator value with type %s" % (type(value))) From 0d28b57758f44e2a21dbd561ee19ab337e1d5152 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 29 Nov 2016 06:31:23 -0600 Subject: [PATCH 1281/2656] Remove stray debug line --- cocotb/handle.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index aff5ea11..84d67d16 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -591,7 +591,6 @@ def setimmediatevalue(self, value): elif isinstance(value, get_python_integer_types()): value = BinaryValue(value=value, bits=len(self), bigEndian=False) elif isinstance(value, dict): - print value #We're given a dictionary with a list of values and a bit size... num = 0; vallist = value["values"] From 6b1a09bee914cec59b4070af0bd0acc788791756 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 29 Nov 2016 10:25:55 -0600 Subject: [PATCH 1282/2656] Reverse in place is nastygit diff --- cocotb/handle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 84d67d16..205eac56 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -593,7 +593,7 @@ def setimmediatevalue(self, value): elif isinstance(value, dict): #We're given a dictionary with a list of values and a bit size... num = 0; - vallist = value["values"] + vallist = list(value["values"]) vallist.reverse() if len(vallist) * value["bits"] != len(self): self._log.critical("Unable to set with array length %d of %d bit entries = %d total, target is only %d bits long" % From 312e00067560f611a249e392e20e257e27c11971 Mon Sep 17 00:00:00 2001 From: Alexis Jeandet Date: Thu, 1 Dec 2016 17:54:34 +0100 Subject: [PATCH 1283/2656] Fix C++11 error message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When building on Fedora 25 gcc complains with the following message: ``` GpiCommon.cpp:136:21: erreur : suffixe de chaîne invalide ; C++11 requière un espace entre une chaîne et une macro de chaîne [-Werror=literal-suffix] #define DOT_LIB_EXT "."xstr(LIB_EXT) ``` --- lib/gpi/GpiCommon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 71203f42..5ea6eab8 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -133,7 +133,7 @@ void gpi_embed_event(gpi_event_t level, const char *msg) static void gpi_load_libs(std::vector to_load) { -#define DOT_LIB_EXT "."xstr(LIB_EXT) +#define DOT_LIB_EXT "." xstr(LIB_EXT) std::vector::iterator iter; for (iter = to_load.begin(); From cbd597b189547e4f9ef188dff73ec40ce73f4dda Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 1 Dec 2016 18:14:49 -0500 Subject: [PATCH 1284/2656] Fixed __invert__ magic function to not modify in place --- cocotb/binary.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 37c53f96..6171d706 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -444,8 +444,7 @@ def __irshift__(self, other): def __invert__(self): """Preserves X values""" - self.binstr = self._invert(self.binstr) - return self + return self._invert(self.binstr) def __len__(self): return len(self.binstr) From 627b8245762197ce1842cffd23795edc7e447336 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 2 Dec 2016 14:18:45 -0500 Subject: [PATCH 1285/2656] Updated _setitem__ to work with an integer value - also added a value range check for the case where a single bit index is used --- cocotb/binary.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 37c53f96..44383ff4 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -493,10 +493,25 @@ def __getitem__(self, key): def __setitem__(self, key, val): ''' BinaryValue uses verilog/vhdl style slices as opposed to python style''' - if not isinstance(val, str): - raise TypeError('BinaryValue slices only accept string values') + if not isinstance(val, str) and not isinstance(val, (int, long)): + raise TypeError('BinaryValue slices only accept string or integer values') + + # convert integer to string + if isinstance(val, (int, long)): + if isinstance(key, slice): + num_slice_bits = abs(key.start - key.stop) + 1 + else: + num_slice_bits = 1 + if val < 0: + raise ValueError('Integer must be positive') + if val >= 2**num_slice_bits: + raise ValueError('Integer is too large for the specified slice ' + 'length') + val = "{:0{width}b}".format(val, width=num_slice_bits) + if isinstance(key, slice): first, second = key.start, key.stop + if self.big_endian: if first < 0 or second < 0: raise IndexError('BinaryValue does not support negative ' @@ -530,6 +545,9 @@ def __setitem__(self, key, val): slice_2 = self.binstr[high:] self.binstr = slice_1 + val + slice_2 else: + if len(val) != 1: + raise ValueError('String length must be equal to slice ' + 'length') index = key if index > self._bits - 1: raise IndexError('Index greater than number of bits.') From 899c98c15aec731295d10ec9ad67b73a4570ff41 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 26 Jan 2017 16:11:13 -0600 Subject: [PATCH 1286/2656] Propagate riviera return code, non-zero on error Fixes #51 --- makefiles/simulators/Makefile.aldec | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 07f7d1e6..5b25b360 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -90,7 +90,11 @@ endif # Create a TCL script based on the list of $(VERILOG_SOURCES) $(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(VHDL_SOURCES) $(SIM_BUILD) - echo "alib $(RTL_LIBRARY)" > $@ + echo "onerror {" > $@ + echo " puts [read [open sim.log r]]" >> $@ + echo " quit -code 1" >> $@ + echo "}" >> $@ + echo "alib $(RTL_LIBRARY)" >> $@ echo "set worklib $(RTL_LIBRARY)" >> $@ ifneq ($(VHDL_SOURCES),) echo "acom $(ACOM_ARGS) $(VHDL_SOURCES)" >> $@ @@ -141,7 +145,7 @@ endif # Note it's the redirection of the output rather than the 'do' command # that turns on batch mode (i.e. exit on completion/error) results.xml: $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(COCOTB_VPI_LIB) $(CUSTOM_SIM_DEPS) - cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ + set -o pipefail; cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do runsim.tcl | tee sim.log clean:: From 6b33c414880344e23149f4add6c2d90d7304c6c6 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 26 Jan 2017 16:12:49 -0600 Subject: [PATCH 1287/2656] Hide echo statements You don't need to see these in normal operation. #51 --- makefiles/simulators/Makefile.aldec | 32 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 5b25b360..57c6adbe 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -90,33 +90,33 @@ endif # Create a TCL script based on the list of $(VERILOG_SOURCES) $(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(VHDL_SOURCES) $(SIM_BUILD) - echo "onerror {" > $@ - echo " puts [read [open sim.log r]]" >> $@ - echo " quit -code 1" >> $@ - echo "}" >> $@ - echo "alib $(RTL_LIBRARY)" >> $@ - echo "set worklib $(RTL_LIBRARY)" >> $@ + @echo "onerror {" > $@ + @echo " puts [read [open sim.log r]]" >> $@ + @echo " quit -code 1" >> $@ + @echo "}" >> $@ + @echo "alib $(RTL_LIBRARY)" >> $@ + @echo "set worklib $(RTL_LIBRARY)" >> $@ ifneq ($(VHDL_SOURCES),) - echo "acom $(ACOM_ARGS) $(VHDL_SOURCES)" >> $@ + @echo "acom $(ACOM_ARGS) $(VHDL_SOURCES)" >> $@ endif ifneq ($(VERILOG_SOURCES),) - echo "alog $(ALOG_ARGS) $(VERILOG_SOURCES)" >> $@ + @echo "alog $(ALOG_ARGS) $(VERILOG_SOURCES)" >> $@ endif ifdef SCRIPT_FILE - echo "do $(SCRIPT_FILE)" >> $@ + @echo "do $(SCRIPT_FILE)" >> $@ endif - echo "asim $(ASIM_ARGS) +access +w -interceptcoutput -O2 -dbg $(GPI_ARGS) $(TOPLEVEL)" >> $@ + @echo "asim $(ASIM_ARGS) +access +w -interceptcoutput -O2 -dbg $(GPI_ARGS) $(TOPLEVEL)" >> $@ ifeq ($(WAVES),1) - echo "log -recursive *" >> $@ + @echo "log -recursive *" >> $@ endif ifeq ($(GUI),1) - echo "wave -rec *" >> $@ + @echo "wave -rec *" >> $@ else - echo "run -all" >> $@ - echo "endsim" >> $@ + @echo "run -all" >> $@ + @echo "endsim" >> $@ ifeq ($(COVERAGE),1) - echo "acdb report -db $(RTL_LIBRARY).acdb -html -o coverage/acdb_report.html" >> $@ - echo "acdb report -db $(RTL_LIBRARY).acdb -txt -o coverage/acdb_report.txt" >> $@ + @echo "acdb report -db $(RTL_LIBRARY).acdb -html -o coverage/acdb_report.html" >> $@ + @echo "acdb report -db $(RTL_LIBRARY).acdb -txt -o coverage/acdb_report.txt" >> $@ endif endif From 5035063f9ab1ffe3e571bdf4998faac9c55a0f06 Mon Sep 17 00:00:00 2001 From: Mike Wild Date: Wed, 8 Feb 2017 18:45:17 +0000 Subject: [PATCH 1288/2656] Retry creating tun device until we find an unused name --- .../ping_tun_tap/tests/test_icmp_reply.py | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/examples/ping_tun_tap/tests/test_icmp_reply.py b/examples/ping_tun_tap/tests/test_icmp_reply.py index 6d3442d8..d295074b 100644 --- a/examples/ping_tun_tap/tests/test_icmp_reply.py +++ b/examples/ping_tun_tap/tests/test_icmp_reply.py @@ -47,11 +47,29 @@ def create_tun(name="tun0", ip="192.168.255.1"): IFF_TUN = 0x0001 IFF_NO_PI = 0x1000 tun = open('/dev/net/tun', 'r+b') - ifr = struct.pack('16sH', name, IFF_TUN | IFF_NO_PI) - fcntl.ioctl(tun, TUNSETIFF, ifr) + tun_num = int(name.split('tun')[-1]) + + # Try and create tun device until we find a name not in use + # eg. tun0, tun1, tun2... + while True: + try: + name = 'tun{}'.format(tun_num) + ifr = struct.pack('16sH', name, IFF_TUN | IFF_NO_PI) + cocotb.log.info(name) + fcntl.ioctl(tun, TUNSETIFF, ifr) + break + except IOError as e: + # Errno 16 if tun device already exists, otherwise this + # failed for different reason. + if e.errno != 16: + raise e + + tun_num += 1 + fcntl.ioctl(tun, TUNSETOWNER, 1000) - subprocess.check_call('ifconfig tun0 %s up pointopoint 192.168.255.2 up' % - ip, shell=True) + subprocess.check_call('ifconfig %s %s up pointopoint 192.168.255.2 up' % + (name, ip), shell=True) + cocotb.log.info("Created interface %s (%s)" % (name, ip)) return tun From f6a9acf3d1206a718c84dbd1f9f2ec6a2f72530f Mon Sep 17 00:00:00 2001 From: Bruno Kremel Date: Fri, 10 Feb 2017 10:33:31 +0100 Subject: [PATCH 1289/2656] Add GHDL_ARGS to support switches (e.g. std=08) Akin to VCOM_ARGS allow to pass specific arguments for GHDL. --- makefiles/simulators/Makefile.ghdl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.ghdl b/makefiles/simulators/Makefile.ghdl index 7c1bd78d..bbba1216 100644 --- a/makefiles/simulators/Makefile.ghdl +++ b/makefiles/simulators/Makefile.ghdl @@ -51,12 +51,13 @@ else endif RTL_LIBRARY ?= work +GHDL_ARGS ?= .PHONY: analyse # Compilation phase analyse: $(VHDL_SOURCES) $(SIM_BUILD) - cd $(SIM_BUILD) && $(CMD) -a --work=$(RTL_LIBRARY) $(VHDL_SOURCES) && $(CMD) -e --work=$(RTL_LIBRARY) $(TOPLEVEL) + cd $(SIM_BUILD) && $(CMD) -a $(GHDL_ARGS) --work=$(RTL_LIBRARY) $(VHDL_SOURCES) && $(CMD) -e $(GHDL_ARGS) --work=$(RTL_LIBRARY) $(TOPLEVEL) results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VPI_LIB) cd $(SIM_BUILD); \ From 2554b53a5a30788b8d122854aa0778a0c75b3f78 Mon Sep 17 00:00:00 2001 From: Bruno Kremel Date: Fri, 10 Feb 2017 15:07:46 +0100 Subject: [PATCH 1290/2656] Add $(GHDL_ARGS) also to -r --- makefiles/simulators/Makefile.ghdl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.ghdl b/makefiles/simulators/Makefile.ghdl index bbba1216..d93fcead 100644 --- a/makefiles/simulators/Makefile.ghdl +++ b/makefiles/simulators/Makefile.ghdl @@ -63,7 +63,7 @@ results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VPI_LIB) cd $(SIM_BUILD); \ PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(CMD) -r $(TOPLEVEL) --vpi=$(LIB_DIR)/libvpi.$(LIB_EXT) $(SIM_ARGS) + $(CMD) -r $(GHDL_ARGS) $(TOPLEVEL) --vpi=$(LIB_DIR)/libvpi.$(LIB_EXT) $(SIM_ARGS) clean:: -@rm -rf $(SIM_BUILD) From cf12aa4b586272cc5756e70322ba40ae081b324e Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 21 Feb 2017 17:12:03 -0600 Subject: [PATCH 1291/2656] Suppressing cmd echo, #57 --- makefiles/simulators/Makefile.questa | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 83aac9dc..38077f13 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -83,30 +83,30 @@ VSIM_ARGS += $(foreach gen, $(GENERICS),"-G $(gen)") endif $(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) $(SIM_BUILD) - echo "# Autogenerated file" > $@ - echo "if [file exists $(RTL_LIBRARY)] {vdel -lib $(RTL_LIBRARY) -all}" > $@ - echo "vlib $(RTL_LIBRARY)" >> $@ - echo "vmap -c" >> $@ - echo "vmap $(RTL_LIBRARY) $(RTL_LIBRARY)" >> $@ + @echo "# Autogenerated file" > $@ + @echo "if [file exists $(RTL_LIBRARY)] {vdel -lib $(RTL_LIBRARY) -all}" > $@ + @echo "vlib $(RTL_LIBRARY)" >> $@ + @echo "vmap -c" >> $@ + @echo "vmap $(RTL_LIBRARY) $(RTL_LIBRARY)" >> $@ ifneq ($(VHDL_SOURCES),) - echo "vcom -work $(RTL_LIBRARY) $(VCOM_ARGS) $(VHDL_SOURCES)" >> $@ + @echo "vcom -work $(RTL_LIBRARY) $(VCOM_ARGS) $(VHDL_SOURCES)" >> $@ endif ifneq ($(VERILOG_SOURCES),) - echo "vlog -work $(RTL_LIBRARY) +define+COCOTB_SIM -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(VERILOG_SOURCES)" >> $@ + @echo "vlog -work $(RTL_LIBRARY) +define+COCOTB_SIM -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(VERILOG_SOURCES)" >> $@ endif ifdef SCRIPT_FILE - echo "do $(SCRIPT_FILE)" >> $@ + @echo "do $(SCRIPT_FILE)" >> $@ endif - echo "vsim $(VSIM_ARGS) $(TOPLEVEL)" >> $@ + @echo "vsim $(VSIM_ARGS) $(TOPLEVEL)" >> $@ ifeq ($(WAVES),1) - echo "log -recursive /*" >> $@ + @echo "log -recursive /*" >> $@ endif ifeq ($(GUI),1) - echo "add log -r *" >> $@ + @echo "add log -r *" >> $@ else - echo "onbreak resume" >> $@ - echo "run -all" >> $@ - echo "quit" >> $@ + @echo "onbreak resume" >> $@ + @echo "run -all" >> $@ + @echo "quit" >> $@ endif ifeq ($(OS),Msys) From 627eac88c3e4a4f16a333e5c1bd9f5f43fc3b590 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 21 Feb 2017 17:36:32 -0600 Subject: [PATCH 1292/2656] Cleaning up spurious comment in output, #57 --- makefiles/simulators/Makefile.questa | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 38077f13..c374204f 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -142,8 +142,8 @@ results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) \ $(CMD) -do runsim.do 2>&1 | tee sim.log - # Potential fix for buffered stdout, YMMV - #STDOUT=$(SIM_BUILD)/sim.log stdbuf --output=0 $(CMD) -do runsim.do 2>&1 && stdbuf --output=0 --input=0 tail -f sim.log && exit $${PIPESTATUS[0]} +# Potential fix for buffered stdout, YMMV +# STDOUT=$(SIM_BUILD)/sim.log stdbuf --output=0 $(CMD) -do runsim.do 2>&1 && stdbuf --output=0 --input=0 tail -f sim.log && exit $${PIPESTATUS[0]} clean:: -rm -rf $(SIM_BUILD) From 84396cf866639a78cd04b7046416aa6241e820e3 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 21 Feb 2017 17:41:28 -0600 Subject: [PATCH 1293/2656] Propagate questa return code, #57 --- makefiles/simulators/Makefile.questa | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index c374204f..f0494ab5 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -139,7 +139,7 @@ endif # Depending on the version of modelsim the interfaces that it supports can change # Append or remove values to INT_LIBS depenending on your license results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) - cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + set -o pipefail; cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) \ $(CMD) -do runsim.do 2>&1 | tee sim.log # Potential fix for buffered stdout, YMMV From e4b46d81fa91559bf90974735a1f33e8ee23a562 Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 22 Feb 2017 17:30:01 -0600 Subject: [PATCH 1294/2656] Added error handler, #57 --- makefiles/simulators/Makefile.questa | 3 +++ 1 file changed, 3 insertions(+) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index f0494ab5..cb5a816f 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -84,6 +84,9 @@ endif $(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) $(SIM_BUILD) @echo "# Autogenerated file" > $@ + @echo "onerror {" >> $@ + @echo " quit -f -code 1" >> $@ + @echo "}" >> $@ @echo "if [file exists $(RTL_LIBRARY)] {vdel -lib $(RTL_LIBRARY) -all}" > $@ @echo "vlib $(RTL_LIBRARY)" >> $@ @echo "vmap -c" >> $@ From 1218ebe4ab0eae2a0b0cdc946859d2fafdc535b2 Mon Sep 17 00:00:00 2001 From: Stefan Wallentowitz Date: Fri, 3 Mar 2017 08:58:18 +0100 Subject: [PATCH 1295/2656] Fix env variable in installation documentation --- documentation/source/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/installation.rst b/documentation/source/installation.rst index 94e874ac..692fe9e8 100644 --- a/documentation/source/installation.rst +++ b/documentation/source/installation.rst @@ -21,4 +21,4 @@ The list of supported simulators for the version you have can be found by *make Centralised Usage ================= -A build can be installed in a centralised location with *make install INSTALL_PATH=
      *. This will also generate an uninstall script. +A build can be installed in a centralised location with *make install FULL_INSTALL_DIR=*. This will also generate an uninstall script. From 1cf7f0f229ffbbe5a6debd9e73391e6345822e6c Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 7 Mar 2017 05:11:54 -0600 Subject: [PATCH 1296/2656] Issue #54: The previous fix only worked in one case resolve being called, this works for all --- cocotb/binary.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 37c53f96..227f07d8 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -37,8 +37,16 @@ def resolve(string): string = string.replace(char, "0") for char in BinaryValue._resolve_to_1: string = string.replace(char, "1") - if any(char in string for char in BinaryValue._resolve_to_error): - raise ValueError("Unable to resolve to binary >%s<" % string) + for char in BinaryValue._resolve_to_error: + if resolve_x_to == "VALUE_ERROR": + raise ValueError("Unable to resolve to binary >%s<" % string) + elif resolve_x_to == "ZEROS": + string = string.replace(char, "0") + elif resolve_x_to == "ONES": + string = string.replace(char, "1") + elif resolve_x_to == "RANDOM": + bits = "{0:b}".format(random.getrandbits(1)) + string = string.replace(char, bits) return string @@ -274,6 +282,7 @@ def get_buff(self): """ bits = resolve(self._str) + if len(bits) % 8: bits = "0" * (8 - len(bits) % 8) + bits From 8eec283027b41ebe372e7ddde3ae2899c8222b3a Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 7 Mar 2017 05:11:54 -0600 Subject: [PATCH 1297/2656] Issue #54: The previous fix only worked in one case resolve being called, this works for all --- cocotb/binary.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 37c53f96..227f07d8 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -37,8 +37,16 @@ def resolve(string): string = string.replace(char, "0") for char in BinaryValue._resolve_to_1: string = string.replace(char, "1") - if any(char in string for char in BinaryValue._resolve_to_error): - raise ValueError("Unable to resolve to binary >%s<" % string) + for char in BinaryValue._resolve_to_error: + if resolve_x_to == "VALUE_ERROR": + raise ValueError("Unable to resolve to binary >%s<" % string) + elif resolve_x_to == "ZEROS": + string = string.replace(char, "0") + elif resolve_x_to == "ONES": + string = string.replace(char, "1") + elif resolve_x_to == "RANDOM": + bits = "{0:b}".format(random.getrandbits(1)) + string = string.replace(char, bits) return string @@ -274,6 +282,7 @@ def get_buff(self): """ bits = resolve(self._str) + if len(bits) % 8: bits = "0" * (8 - len(bits) % 8) + bits From c91f4c0d005f92ebf5270cc7a985e82b4b1496c1 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 7 Mar 2017 05:18:39 -0600 Subject: [PATCH 1298/2656] Issue #54: Pull in the reading of the env, got lost in a cherry pick --- cocotb/binary.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cocotb/binary.py b/cocotb/binary.py index 227f07d8..11c36423 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -31,6 +31,10 @@ from math import log, ceil from cocotb.utils import get_python_integer_types +import os +import random + +resolve_x_to = os.getenv('COCOTB_RESOLVE_X', "VALUE_ERROR") def resolve(string): for char in BinaryValue._resolve_to_0: From d7ec50c1d8269396893d8d24cf2e04f03ed17758 Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 8 Mar 2017 08:44:25 -0600 Subject: [PATCH 1299/2656] fix resolve_x_to == VALUE_ERROR case Closes #61 --- cocotb/binary.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 7f283d82..bbb15e8b 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -42,14 +42,14 @@ def resolve(string): for char in BinaryValue._resolve_to_1: string = string.replace(char, "1") for char in BinaryValue._resolve_to_error: - if resolve_x_to == "VALUE_ERROR": - raise ValueError("Unable to resolve to binary >%s<" % string) - elif resolve_x_to == "ZEROS": - string = string.replace(char, "0") - elif resolve_x_to == "ONES": - string = string.replace(char, "1") - elif resolve_x_to == "RANDOM": - bits = "{0:b}".format(random.getrandbits(1)) + if resolve_x_to == "VALUE_ERROR" and char in string: + raise ValueError("Unable to resolve to binary >%s<" % string) + elif resolve_x_to == "ZEROS": + string = string.replace(char, "0") + elif resolve_x_to == "ONES": + string = string.replace(char, "1") + elif resolve_x_to == "RANDOM": + bits = "{0:b}".format(random.getrandbits(1)) string = string.replace(char, bits) return string From bac3f85bde3aff288c847f5535affc00cb70c0ce Mon Sep 17 00:00:00 2001 From: cospan Date: Fri, 24 Mar 2017 16:36:34 -0400 Subject: [PATCH 1300/2656] Added AXI Lite Slave Test Created a test for AXI Lite Slaves. Included is a core that can interact with the AXI Lite master. The core will extract the address and data send from the master and interact with the demo module with a very simple handshake that the axi_lite_demo module can interface with without a state machine. The axi_lite_demo can interact with the axi_lite_slave with a simple handshake. The axi_lite_slave will wrap up a response from the core in a AXI Lite Transaction. The testbench can be run by navigating to the following directory: /examples/axi_lite_slave/tests and running 'make' Two of the tests fail on purpose, they attempt to read/write from/to an illigal address. --- cocotb/drivers/amba.py | 6 +- examples/axi_lite_slave/README.md | 27 +++ examples/axi_lite_slave/hdl/axi_defines.v | 68 ++++++ examples/axi_lite_slave/hdl/axi_lite_demo.v | 210 +++++++++++++++++ examples/axi_lite_slave/hdl/axi_lite_slave.v | 218 ++++++++++++++++++ .../axi_lite_slave/hdl/tb_axi_lite_slave.v | 98 ++++++++ examples/axi_lite_slave/tests/Makefile | 29 +++ .../tests/test_axi_lite_slave.py | 208 +++++++++++++++++ 8 files changed, 861 insertions(+), 3 deletions(-) create mode 100644 examples/axi_lite_slave/README.md create mode 100644 examples/axi_lite_slave/hdl/axi_defines.v create mode 100644 examples/axi_lite_slave/hdl/axi_lite_demo.v create mode 100644 examples/axi_lite_slave/hdl/axi_lite_slave.v create mode 100644 examples/axi_lite_slave/hdl/tb_axi_lite_slave.v create mode 100644 examples/axi_lite_slave/tests/Makefile create mode 100644 examples/axi_lite_slave/tests/test_axi_lite_slave.py diff --git a/cocotb/drivers/amba.py b/cocotb/drivers/amba.py index 30481a61..70eebbd2 100644 --- a/cocotb/drivers/amba.py +++ b/cocotb/drivers/amba.py @@ -36,7 +36,7 @@ import array -class AXIReadError(Exception): +class AXIProtocolError(Exception): pass @@ -141,7 +141,7 @@ def write(self, address, value, byte_enable=0xf, address_latency=0, yield RisingEdge(self.clock) if int(result): - raise AXIReadError("Write to address 0x%08x failed with BRESP: %d" + raise AXIProtocolError("Write to address 0x%08x failed with BRESP: %d" % (address, int(result))) raise ReturnValue(result) @@ -175,7 +175,7 @@ def read(self, address, sync=True): yield RisingEdge(self.clock) if int(result): - raise AXIReadError("Read address 0x%08x failed with RRESP: %d" % + raise AXIProtocolError("Read address 0x%08x failed with RRESP: %d" % (address, int(result))) raise ReturnValue(data) diff --git a/examples/axi_lite_slave/README.md b/examples/axi_lite_slave/README.md new file mode 100644 index 00000000..feca064b --- /dev/null +++ b/examples/axi_lite_slave/README.md @@ -0,0 +1,27 @@ +# AXI Lite Demo + +Description: In order to simplify AXI Lite Transactions we use an module that +translates AXI Lite Slave transactions to a simple register read/write. + +There are other projects that translate AXI Slave signals to wishbone. + + +## Source Files + +* tb\_axi\_lite\_slave: Testbench interface, primarily used to translate + the cocotb AXI Lite bus signals to the signals within axi_lite_demo core + +* axi\_lite\_demo: demonstration module, anyone who wishes to interact + with an AXI Lite slave core can use this as a template. + +* axi\_lite\_slave: Performs all the low level AXI Translactions. + * Addresses and data sent from the master are decoded from the AXI Bus and + sent to the user with a simple 'ready', 'acknowledge' handshake. + * Address and data are read from the slave using a simple + 'request', 'ready' handshake. + +## NOTE: Test ID + +If you use a logic analyzer wave viewer it's hard to determine which test is +currently running so I added a value called 'test\_id' in the test bench +so when viewing the waveforms the individual tests can easily be identified diff --git a/examples/axi_lite_slave/hdl/axi_defines.v b/examples/axi_lite_slave/hdl/axi_defines.v new file mode 100644 index 00000000..46581b74 --- /dev/null +++ b/examples/axi_lite_slave/hdl/axi_defines.v @@ -0,0 +1,68 @@ +`ifndef __AXI_DEFINES__ +`define __AXI_DEFINES__ + +//AXI Defines + +//--Burst +`define AXI_BURST_FIXED 2'b00 +`define AXI_BURST_INCR 2'b01 +`define AXI_BURST_WRAP 2'b10 + +//--Burst Size +`define AXI_BURST_SIZE_8BIT 3'b000 +`define AXI_BURST_SIZE_16BIT 3'b001 +`define AXI_BURST_SIZE_32BIT 3'b010 +`define AXI_BURST_SIZE_64BIT 3'b011 +`define AXI_BURST_SIZE_128BIT 3'b100 +`define AXI_BURST_SIZE_256BIT 3'b101 +`define AXI_BURST_SIZE_512BIT 3'b110 +`define AXI_BURST_SIZE_1024BIT 3'b111 + +//--Response +`define AXI_RESP_OKAY 2'b00 +`define AXI_RESP_EXOKAY 2'b01 +`define AXI_RESP_SLVERR 2'b10 +`define AXI_RESP_DECERR 2'b11 + +//--Lock +`define AXI_LOCK_NORMAL 2'b00 +`define AXI_LOCK_EXCLUSIVE 2'b01 +`define AXI_LOCK_LOCKED 2'b10 + +//--cache +//----Bufferable +`define AXI_CACHE_NON_BUF 1'b0 +`define AXI_CACHE_BUF 1'b1 +//----Cachable +`define AXI_CACHE_NON_CACHE 1'b0 +`define AXI_CACHE_CACHE 1'b1 +//----Read Allocate +`define AXI_CACHE_NON_RA 1'b0 +`define AXI_CACHE_RA 1'b1 +//----Write Allocate +`define AXI_CACHE_NON_WA 1'b0 +`define AXI_CACHE_WA 1'b1 +//----Unused +`define AXI_CACHE_UNUSED 4'b0000 + +//--Protection +//----ARPROT[0] +`define AXI_PROT_NORMAL 1'b0 +`define AXI_PROT_PRIVLEDGE 1'b1 + +//----ARPROT[1] +`define AXI_PROT_SECURE 1'b0 +`define AXI_PROT_NON_SECURE 1'b1 + +//----ARPROT[2] +`define AXI_PROT_DATA 1'b0 +`define AXI_PROT_INST 1'b1 + +//----Unused: +`define AXI_PROT_UNUSED {`AXI_PROT_NORMAL, `AXI_PROT_NON_SECURE, `AXI_PROT_DATA} + +//--Low Power Mode +`define AXI_POWER_LOW 1'b0 +`define AXI_POWER_NORMAL 1'b1 + +`endif //__AXI_DEFINES__ diff --git a/examples/axi_lite_slave/hdl/axi_lite_demo.v b/examples/axi_lite_slave/hdl/axi_lite_demo.v new file mode 100644 index 00000000..0f8ba19d --- /dev/null +++ b/examples/axi_lite_slave/hdl/axi_lite_demo.v @@ -0,0 +1,210 @@ +/* +Distributed under the MIT license. +Copyright (c) 2016 Dave McCoy (dave.mccoy@cospandesign.com) + +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. +*/ + +/* + * Author: + * Description: + * + * Changes: + */ + +`timescale 1ps / 1ps + +module axi_lite_demo #( + parameter ADDR_WIDTH = 32, + parameter DATA_WIDTH = 32, + parameter STROBE_WIDTH = (DATA_WIDTH / 8) +)( + input clk, + input rst, + + //Write Address Channel + input i_awvalid, + input [ADDR_WIDTH - 1: 0] i_awaddr, + output o_awready, + + //Write Data Channel + input i_wvalid, + output o_wready, + input [STROBE_WIDTH - 1:0] i_wstrb, + input [DATA_WIDTH - 1: 0] i_wdata, + + //Write Response Channel + output o_bvalid, + input i_bready, + output [1:0] o_bresp, + + //Read Address Channel + input i_arvalid, + output o_arready, + input [ADDR_WIDTH - 1: 0] i_araddr, + + //Read Data Channel + output o_rvalid, + input i_rready, + output [1:0] o_rresp, + output [DATA_WIDTH - 1: 0] o_rdata +); +//local parameters + +//Address Map +localparam ADDR_0 = 0; +localparam ADDR_1 = 1; + +localparam MAX_ADDR = ADDR_1; + +//registes/wires + +//User Interface +wire [ADDR_WIDTH - 1: 0] w_reg_address; +reg r_reg_invalid_addr; + +wire w_reg_in_rdy; +reg r_reg_in_ack_stb; +wire [DATA_WIDTH - 1: 0] w_reg_in_data; + +wire w_reg_out_req; +reg r_reg_out_rdy_stb; +reg [DATA_WIDTH - 1: 0] r_reg_out_data; + + +//TEMP DATA, JUST FOR THE DEMO +reg [DATA_WIDTH - 1: 0] r_temp_0; +reg [DATA_WIDTH - 1: 0] r_temp_1; + +//submodules + +//Convert AXI Slave bus to a simple register/address strobe +axi_lite_slave #( + .ADDR_WIDTH (ADDR_WIDTH ), + .DATA_WIDTH (DATA_WIDTH ) + +) axi_lite_reg_interface ( + .clk (clk ), + .rst (rst ), + + + .i_awvalid (i_awvalid ), + .i_awaddr (i_awaddr ), + .o_awready (o_awready ), + + .i_wvalid (i_wvalid ), + .o_wready (o_wready ), + .i_wstrb (i_wstrb ), + .i_wdata (i_wdata ), + + .o_bvalid (o_bvalid ), + .i_bready (i_bready ), + .o_bresp (o_bresp ), + + .i_arvalid (i_arvalid ), + .o_arready (o_arready ), + .i_araddr (i_araddr ), + + .o_rvalid (o_rvalid ), + .i_rready (i_rready ), + .o_rresp (o_rresp ), + .o_rdata (o_rdata ), + + + //Simple Register Interface + .o_reg_address (w_reg_address ), + .i_reg_invalid_addr (r_reg_invalid_addr ), + + //Ingress Path (From Master) + .o_reg_in_rdy (w_reg_in_rdy ), + .i_reg_in_ack_stb (r_reg_in_ack_stb ), + .o_reg_in_data (w_reg_in_data ), + + //Egress Path (To Master) + .o_reg_out_req (w_reg_out_req ), + .i_reg_out_rdy_stb (r_reg_out_rdy_stb ), + .i_reg_out_data (r_reg_out_data ) +); + +//asynchronous logic + +//synchronous logic +always @ (posedge clk) begin + //De-assert Strobes + r_reg_in_ack_stb <= 0; + r_reg_out_rdy_stb <= 0; + r_reg_invalid_addr <= 0; + + if (rst) begin + r_reg_out_data <= 0; + + //Reset the temporary Data + r_temp_0 <= 0; + r_temp_1 <= 0; + end + else begin + + if (w_reg_in_rdy) begin + //From master + case (w_reg_address) + ADDR_0: begin + //$display("Incomming data on address: 0x%h: 0x%h", w_reg_address, w_reg_in_data); + r_temp_0 <= w_reg_in_data; + end + ADDR_1: begin + //$display("Incomming data on address: 0x%h: 0x%h", w_reg_address, w_reg_in_data); + r_temp_1 <= w_reg_in_data; + end + default: begin + $display ("Unknown address: 0x%h", w_reg_address); + end + endcase + if (w_reg_address > MAX_ADDR) begin + //Tell the host they wrote to an invalid address + r_reg_invalid_addr <= 1; + end + //Tell the AXI Slave Control we're done with the data + r_reg_in_ack_stb <= 1; + end + else if (w_reg_out_req) begin + //To master + //$display("User is reading from address 0x%0h", w_reg_address); + case (w_reg_address) + ADDR_0: begin + r_reg_out_data <= r_temp_0; + end + ADDR_1: begin + r_reg_out_data <= r_temp_1; + end + default: begin + //Unknown address + r_reg_out_data <= 32'h00; + end + endcase + if (w_reg_address > MAX_ADDR) begin + //Tell the host they are reading from an invalid address + r_reg_invalid_addr <= 1; + end + //Tell the AXI Slave to send back this packet + r_reg_out_rdy_stb <= 1; + end + end +end + +endmodule diff --git a/examples/axi_lite_slave/hdl/axi_lite_slave.v b/examples/axi_lite_slave/hdl/axi_lite_slave.v new file mode 100644 index 00000000..198f93b8 --- /dev/null +++ b/examples/axi_lite_slave/hdl/axi_lite_slave.v @@ -0,0 +1,218 @@ +/* +Distributed under the MIT license. +Copyright (c) 2017 Dave McCoy (dave.mccoy@cospandesign.com) + +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. +*/ + +/* + * Author: David McCoy (dave.mccoy@cospandesign.com) + * Description: An AXI Lite Slave interface that simplifies register access + * XXX: Currently there is no strobe level access + * + * Changes: + * 3/24/2017: Initial Commit + */ + +`timescale 1ps / 1ps + +`include "axi_defines.v" + +module axi_lite_slave #( + parameter ADDR_WIDTH = 32, + parameter DATA_WIDTH = 32, + parameter STROBE_WIDTH = (DATA_WIDTH / 8) + +)( + + input clk, + input rst, + + //Write Address Channel + input i_awvalid, + input [ADDR_WIDTH - 1: 0] i_awaddr, + output reg o_awready, + + //Write Data Channel + input i_wvalid, + output reg o_wready, + input [STROBE_WIDTH - 1:0] i_wstrb, + input [DATA_WIDTH - 1: 0] i_wdata, + + //Write Response Channel + output reg o_bvalid, + input i_bready, + output reg [1:0] o_bresp, + + //Read Address Channel + input i_arvalid, + output reg o_arready, + input [ADDR_WIDTH - 1: 0] i_araddr, + + //Read Data Channel + output reg o_rvalid, + input i_rready, + output reg [1:0] o_rresp, + output reg [DATA_WIDTH - 1: 0] o_rdata, + + + + //Simple User Interface + output reg o_reg_in_rdy, + input i_reg_in_ack_stb, + output reg [ADDR_WIDTH - 1: 0] o_reg_address, + output reg [DATA_WIDTH - 1: 0] o_reg_in_data, + + output reg o_reg_out_req, + input i_reg_out_rdy_stb, + input [DATA_WIDTH - 1: 0] i_reg_out_data, + input i_reg_invalid_addr + +); + + + +//local parameters +localparam IDLE = 4'h0; +localparam RECEIVE_WRITE_DATA = 4'h1; +localparam WRITE_WAIT_FOR_USER = 4'h2; +localparam SEND_WRITE_RESP = 4'h3; +localparam READ_WAIT_FOR_USER = 4'h4; +localparam SEND_READ_DATA = 4'h5; + +//registes/wires +reg [3:0] state; + +//submodules +//asynchronous logic +//synchronous logic + +always @ (posedge clk) begin + //Deassert Strobes + if (rst) begin + o_reg_address <= 0; + o_arready <= 0; + o_awready <= 0; + o_wready <= 0; + + o_rvalid <= 0; + o_rdata <= 0; + o_bresp <= 0; + o_rresp <= 0; + o_bvalid <= 0; + + //Demo values + state <= IDLE; + o_reg_in_rdy <= 0; + o_reg_in_data <= 0; + o_reg_out_req <= 0; + end + else begin + case (state) + IDLE: begin + o_reg_in_rdy <= 0; + o_reg_address <= 0; + o_awready <= 1; + o_arready <= 1; + o_rvalid <= 0; + o_bresp <= 0; + o_rresp <= 0; + o_bvalid <= 0; + o_reg_out_req <= 0; + o_wready <= 0; + + //Only handle read or write at one time, not both + if (i_awvalid && o_awready) begin + o_reg_address <= i_awaddr; + o_wready <= 1; + o_arready <= 0; + state <= RECEIVE_WRITE_DATA; + end + else if (i_arvalid && o_arready) begin + o_reg_address <= i_araddr; + o_awready <= 0; + o_reg_out_req <= 1; + state <= READ_WAIT_FOR_USER; + end + end + RECEIVE_WRITE_DATA: begin + o_awready <= 0; + + if (i_wvalid) begin + //Assume everything is okay unless the o_reg_address is wrong, + //We don't want to clutter our states with this statement over and over again + o_reg_in_data <= i_wdata; + state <= WRITE_WAIT_FOR_USER; + o_reg_in_rdy <= 1; + end + end + WRITE_WAIT_FOR_USER: begin + //Wait for the user to ackknowledge the new info, user can introduce a delay + if (i_reg_in_ack_stb) begin + state <= SEND_WRITE_RESP; + o_reg_in_rdy <= 0; + o_bvalid <= 1; + if (i_reg_invalid_addr) begin + o_bresp <= `AXI_RESP_DECERR; + end + else begin + o_bresp <= `AXI_RESP_OKAY; + end + end + end + SEND_WRITE_RESP: begin + if (i_bready && o_bvalid) begin + o_bvalid <= 0; + state <= IDLE; + end + end + + //Read Path + READ_WAIT_FOR_USER: begin + o_arready <= 0; + if (i_reg_out_rdy_stb) begin + //The data in i_reg_out_data should be valid now + o_rdata <= i_reg_out_data; + if (i_reg_invalid_addr) begin + o_rresp <= `AXI_RESP_DECERR; + end + else begin + o_rresp <= `AXI_RESP_OKAY; + end + o_rvalid <= 1; + state <= SEND_READ_DATA; + end + end + SEND_READ_DATA: begin + //If more time is needed for a response another state should be added here + if (i_rready && o_rvalid) begin + o_rvalid <= 0; + state <= IDLE; + end + end + default: begin + $display("AXI Lite Slave: Shouldn't have gotten here!"); + end + endcase + end +end + + + +endmodule diff --git a/examples/axi_lite_slave/hdl/tb_axi_lite_slave.v b/examples/axi_lite_slave/hdl/tb_axi_lite_slave.v new file mode 100644 index 00000000..e68d41f6 --- /dev/null +++ b/examples/axi_lite_slave/hdl/tb_axi_lite_slave.v @@ -0,0 +1,98 @@ +`timescale 1ns/1ps + + +module tb_axi_lite_slave #( + parameter ADDR_WIDTH = 32, + parameter DATA_WIDTH = 32, + parameter STROBE_WIDTH = (DATA_WIDTH / 8) +)( + +input clk, +input rst, + +//Write Address Channel +input AXIML_AWVALID, +input [ADDR_WIDTH - 1: 0] AXIML_AWADDR, +output AXIML_AWREADY, + +//Write Data Channel +input AXIML_WVALID, +output AXIML_WREADY, +input [STROBE_WIDTH - 1:0] AXIML_WSTRB, +input [DATA_WIDTH - 1: 0] AXIML_WDATA, + +//Write Response Channel +output AXIML_BVALID, +input AXIML_BREADY, +output [1:0] AXIML_BRESP, + +//Read Address Channel +input AXIML_ARVALID, +output AXIML_ARREADY, +input [ADDR_WIDTH - 1: 0] AXIML_ARADDR, + +//Read Data Channel +output AXIML_RVALID, +input AXIML_RREADY, +output [1:0] AXIML_RRESP, +output [DATA_WIDTH - 1: 0] AXIML_RDATA + +); + + +//Local Parameters +//Registers + +reg r_rst; +reg test_id = 0; + +//Workaround for weird icarus simulator bug +always @ (*) r_rst = rst; + +//submodules +axi_lite_demo #( + .ADDR_WIDTH (ADDR_WIDTH ), + .DATA_WIDTH (DATA_WIDTH ) +) dut ( + .clk (clk ), + .rst (r_rst ), + + + .i_awvalid (AXIML_AWVALID ), + .i_awaddr (AXIML_AWADDR ), + .o_awready (AXIML_AWREADY ), + + + .i_wvalid (AXIML_WVALID ), + .o_wready (AXIML_WREADY ), + .i_wstrb (AXIML_WSTRB ), + .i_wdata (AXIML_WDATA ), + + + .o_bvalid (AXIML_BVALID ), + .i_bready (AXIML_BREADY ), + .o_bresp (AXIML_BRESP ), + + + .i_arvalid (AXIML_ARVALID ), + .o_arready (AXIML_ARREADY ), + .i_araddr (AXIML_ARADDR ), + + + .o_rvalid (AXIML_RVALID ), + .i_rready (AXIML_RREADY ), + .o_rresp (AXIML_RRESP ), + .o_rdata (AXIML_RDATA ) + +); + +//asynchronus logic +//synchronous logic + +initial begin + $dumpfile ("design.vcd"); + $dumpvars(0, tb_axi_lite_slave); +end + + +endmodule diff --git a/examples/axi_lite_slave/tests/Makefile b/examples/axi_lite_slave/tests/Makefile new file mode 100644 index 00000000..4d2e8f47 --- /dev/null +++ b/examples/axi_lite_slave/tests/Makefile @@ -0,0 +1,29 @@ + +TOPLEVEL_LANG ?= verilog +PWD=$(shell pwd) +TOPDIR=$(PWD)/.. +COCOTB=$(PWD)/../../.. +PYTHONPATH := ./model:$(PYTHONPATH) + +export PYTHONPATH +export PYTHONHOME=$(shell python -c "from distutils.sysconfig import get_config_var; print(get_config_var('prefix'))") + +EXTRA_ARGS+=-I$(TOPDIR)/hdl/ + +#DUT +VERILOG_SOURCES = $(TOPDIR)/hdl/axi_lite_slave.v +VERILOG_SOURCES += $(TOPDIR)/hdl/axi_lite_demo.v + +#Test Bench +VERILOG_SOURCES += $(TOPDIR)/hdl/tb_axi_lite_slave.v + +TOPLEVEL = tb_axi_lite_slave + +GPI_IMPL := vpi + +export TOPLEVEL_LANG +MODULE=test_axi_lite_slave + +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim + diff --git a/examples/axi_lite_slave/tests/test_axi_lite_slave.py b/examples/axi_lite_slave/tests/test_axi_lite_slave.py new file mode 100644 index 00000000..a347f675 --- /dev/null +++ b/examples/axi_lite_slave/tests/test_axi_lite_slave.py @@ -0,0 +1,208 @@ +import os +import sys +import cocotb +import logging +from cocotb.result import TestFailure +from cocotb.result import TestSuccess +from cocotb.clock import Clock +import time +from array import array as Array +from cocotb.triggers import Timer +from cocotb.drivers.amba import AXI4LiteMaster +from cocotb.drivers.amba import AXIProtocolError + +CLK_PERIOD = 10 + +MODULE_PATH = os.path.join(os.path.dirname(__file__), os.pardir, "hdl") +MODULE_PATH = os.path.abspath(MODULE_PATH) + +def setup_dut(dut): + cocotb.fork(Clock(dut.clk, CLK_PERIOD).start()) + +#Write to address 0 and verify that value got through +@cocotb.test(skip = False) +def write_address_0(dut): + """ + Description: + Write to the register at address 0 + verify the value has changed + + Test ID: 0 + + Expected Results: + The value read directly from the register is the same as the + value written + """ + + #Reset + dut.rst <= 1 + dut.test_id <= 0 + axim = AXI4LiteMaster(dut, "AXIML", dut.clk) + setup_dut(dut) + yield Timer(CLK_PERIOD * 10) + dut.rst <= 0 + + ADDRESS = 0x00 + DATA = 0xAB + + yield axim.write(ADDRESS, DATA) + yield Timer(CLK_PERIOD * 10) + + value = dut.dut.r_temp_0 + if value != DATA: + #Fail + raise TestFailure("Register at address 0x%08X should have been: \ + 0x%08X but was 0x%08X" % (ADDRESS, DATA, value)) + + dut.log.info("Write 0x%08X to addres 0x%08X" % (value, ADDRESS)) + + +#Read back a value at address 0x01 +@cocotb.test(skip = False) +def read_address_1(dut): + """ + Description + Use cocotb to set the value of the register at address 0x01 + Use AXIML to read the contents of that register and + compare the values + + Test ID: 1 + + Expected Results: + The value read from the register is the same as the value written + """ + #Reset + dut.rst <= 1 + dut.test_id <= 0 + axim = AXI4LiteMaster(dut, "AXIML", dut.clk) + setup_dut(dut) + yield Timer(CLK_PERIOD * 10) + dut.rst <= 0 + + ADDRESS = 0x01 + DATA = 0xCD + + dut.dut.r_temp_1 <= DATA + yield Timer(CLK_PERIOD * 10) + + value = yield axim.read(ADDRESS) + yield Timer(CLK_PERIOD * 10) + + if value != DATA: + #Fail + raise TestFailure("Register at address 0x%08X should have been: \ + 0x%08X but was 0x%08X" % (ADDRESS, DATA, value)) + + dut.log.info("Read: 0x%08X From Address: 0x%08X" % (value, ADDRESS)) + + + +@cocotb.test(skip = False) +def write_and_read(dut): + """ + Description: + Write to the register at address 0 + read back from that register and verify the value is the same + + Test ID: 2 + + Expected Results: + The contents of the register is the same as the value written + """ + + #Reset + dut.rst <= 1 + dut.test_id <= 2 + axim = AXI4LiteMaster(dut, "AXIML", dut.clk) + setup_dut(dut) + yield Timer(CLK_PERIOD * 10) + dut.rst <= 0 + + ADDRESS = 0x00 + DATA = 0xAB + + #Write to the register + yield axim.write(ADDRESS, DATA) + yield Timer(CLK_PERIOD * 10) + + #Read back the value + value = yield axim.read(ADDRESS) + yield Timer(CLK_PERIOD * 10) + + value = dut.dut.r_temp_0 + if value != DATA: + #Fail + raise TestFailure("Register at address 0x%08X should have been: \ + 0x%08X but was 0x%08X" % (ADDRESS, DATA, value)) + + dut.log.info("Write 0x%08X to addres 0x%08X" % (value, ADDRESS)) + +@cocotb.test(skip = False) +def write_fail(dut): + """ + Description: + Attemp to write data to an address that doesn't exist. This test + should fail + + Test ID: 3 + + Expected Results: + The AXIML bus should throw an exception because the user attempted + to write to an invalid address + """ + #Reset + dut.rst <= 1 + dut.test_id <= 3 + axim = AXI4LiteMaster(dut, "AXIML", dut.clk) + setup_dut(dut) + yield Timer(CLK_PERIOD * 10) + dut.rst <= 0 + + ADDRESS = 0x02 + DATA = 0xAB + + try: + yield axim.write(ADDRESS, DATA) + yield Timer(CLK_PERIOD * 10) + except AXIProtocolError as e: + print "Exception: %s" % str(e) + dut.log.info("Bus Successfully Raised an Error") + raise TestSuccess() + raise TestFaileure("AXI Bus Should have raised an ERROR when writing to \ + the wrong bus") + +@cocotb.test(skip = False) +def read_fail(dut): + """ + Description: + Attemp to write data to an address that doesn't exist. This test + should fail + + Test ID: 4 + + Expected Results: + The AXIML bus should throw an exception because the user attempted + to write to an invalid address + """ + #Reset + dut.rst <= 1 + dut.test_id <= 4 + axim = AXI4LiteMaster(dut, "AXIML", dut.clk) + setup_dut(dut) + yield Timer(CLK_PERIOD * 10) + dut.rst <= 0 + + ADDRESS = 0x02 + DATA = 0xAB + + try: + yield axim.read(ADDRESS, DATA) + yield Timer(CLK_PERIOD * 10) + except AXIProtocolError as e: + print "Exception: %s" % str(e) + dut.log.info("Bus Successfully Raised an Error") + raise TestSuccess() + raise TestFaileure("AXI Bus Should have raised an ERROR when writing to \ + the wrong bus") + + From 5ac98878ad7f2c969da6b3ad2d01d2fcdb85f020 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 30 Mar 2017 13:28:45 -0500 Subject: [PATCH 1301/2656] do not clobber attributes on sample; Closes #70 --- cocotb/bus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/bus.py b/cocotb/bus.py index 138b4c0c..8d596104 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -187,7 +187,7 @@ def sample(self, obj, strict=False): raise AttributeError(msg) else: continue - setattr(obj, attr_name, hdl.value) + getattr(obj, attr_name).set_binstr(hdl.value.get_binstr()) def __le__(self, value): """Overload the less than or equal to operator for value assignment""" From 3e9b042d71038eb3ae132d86e91c0c3bb4b4c819 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 31 Mar 2017 07:35:46 -0500 Subject: [PATCH 1302/2656] make non-clobbering sample() method work for objects that do not implement get/set_binstr Addresses #70 --- cocotb/bus.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cocotb/bus.py b/cocotb/bus.py index 8d596104..82cf57e5 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -187,7 +187,13 @@ def sample(self, obj, strict=False): raise AttributeError(msg) else: continue - getattr(obj, attr_name).set_binstr(hdl.value.get_binstr()) + #Try to use the get/set_binstr methods because they will not clobber the properties + #of obj.attr_name on assignment. Otherwise use setattr() to crush whatever type of + #object was in obj.attr_name with hdl.value: + try: + getattr(obj, attr_name).set_binstr(hdl.value.get_binstr()) + except AttributeError: + setattr(obj, attr_name, hdl.value) def __le__(self, value): """Overload the less than or equal to operator for value assignment""" From 4efb0edadceb7e9ec7a7f47d1484df59c41a782c Mon Sep 17 00:00:00 2001 From: Stefan Wallentowitz Date: Thu, 20 Apr 2017 08:09:09 +0200 Subject: [PATCH 1303/2656] Fix coverty scan status link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e25bc0c2..08387deb 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Documentation Status](https://readthedocs.org/projects/cocotb/badge/?version=latest)](http://cocotb.readthedocs.org/en/latest/) [![Build Status](https://travis-ci.org/potentialventures/cocotb.svg?branch=master)](https://travis-ci.org/potentialventures/cocotb) -[![Coverity Scan Status] (https://scan.coverity.com/projects/6110/badge.svg)](https://scan.coverity.com/projects/cocotb) +[![Coverity Scan Status](https://scan.coverity.com/projects/6110/badge.svg)](https://scan.coverity.com/projects/cocotb) * Skim the introductory presentation: http://potential.ventures * Read the [documentation](http://cocotb.readthedocs.org) From 1ab2e9c13db46b709a4178b54c7b8015de0fbdd7 Mon Sep 17 00:00:00 2001 From: Stefan Wallentowitz Date: Thu, 20 Apr 2017 08:11:17 +0200 Subject: [PATCH 1304/2656] Add mailing list to README --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e25bc0c2..7e67225c 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,9 @@ * Skim the introductory presentation: http://potential.ventures * Read the [documentation](http://cocotb.readthedocs.org) -* Get involved: [Raise a bug / request an enhancement](https://github.com/potentialventures/cocotb/issues/new) (Requires a GitHub account) +* Get involved: + * [Raise a bug / request an enhancement](https://github.com/potentialventures/cocotb/issues/new) (Requires a GitHub account) + * [Join the mailing list](https://lists.librecores.org/listinfo/cocotb) * Get in contact: [E-mail us](mailto:cocotb@potentialventures.com) * Follow us on twitter: [@PVCocotb](https://twitter.com/PVCocotb) From bd33af74b1c37664dd7b590da62c7f8a2b7f280f Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 26 Apr 2017 21:38:35 -0500 Subject: [PATCH 1305/2656] Issue 79: don't requeue already scheduled coroutine --- cocotb/scheduler.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 796157b3..fae5a6d9 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -499,7 +499,6 @@ def schedule(self, coroutine, trigger=None): self.log.debug("Scheduling nested co-routine: %s" % result.__name__) - self.queue(result) new_trigger = result.join() self._coroutine_yielded(coroutine, [new_trigger]) From 72331eacb12ba88ef45ea1b5b46e0f4bb6a2ad92 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 27 Apr 2017 13:55:57 -0500 Subject: [PATCH 1306/2656] Don't break other stuff --- cocotb/decorators.py | 5 +++++ cocotb/scheduler.py | 3 +++ 2 files changed, 8 insertions(+) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 0210499e..b7bfe773 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -90,6 +90,7 @@ def __init__(self, inst, parent): else: self.log = SimLog("cocotb.coroutine.fail") self._coro = inst + self._started = False self._finished = False self._callbacks = [] self._join = _Join(self) @@ -115,6 +116,7 @@ def send(self, value): if isinstance(value, ExternalException): self.log.debug("Injecting ExternalException(%s)" % (repr(value))) return self._coro.throw(value.exception) + self._started = True return self._coro.send(value) except TestComplete as e: if isinstance(e, TestFailure): @@ -160,6 +162,9 @@ def join(self): else: return self._join + def has_started(self): + return self._started + def __nonzero__(self): """Provide boolean testing if the coroutine has finished return false diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index fae5a6d9..c02681e0 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -499,6 +499,9 @@ def schedule(self, coroutine, trigger=None): self.log.debug("Scheduling nested co-routine: %s" % result.__name__) + if not result.has_started(): + self.queue(result) + new_trigger = result.join() self._coroutine_yielded(coroutine, [new_trigger]) From d68a499a9000847520a9c79bd59c7c4280f614c4 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 27 Apr 2017 14:15:48 -0500 Subject: [PATCH 1307/2656] Issue 79: better debug message --- cocotb/scheduler.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index c02681e0..c27dd0d8 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -495,12 +495,16 @@ def schedule(self, coroutine, trigger=None): # Queue current routine to schedule when the nested routine exits if isinstance(result, cocotb.decorators.RunningCoroutine): - if _debug: - self.log.debug("Scheduling nested co-routine: %s" % - result.__name__) if not result.has_started(): self.queue(result) + if _debug: + self.log.debug("Scheduling nested co-routine: %s" % + result.__name__) + else: + if _debug: + self.log.debug("Joining to already running co-routine: %s" % + result.__name__) new_trigger = result.join() self._coroutine_yielded(coroutine, [new_trigger]) From 1fd5bb360cb3afd66635f27c251e4d73351c835d Mon Sep 17 00:00:00 2001 From: cospan Date: Fri, 28 Apr 2017 17:09:32 -0400 Subject: [PATCH 1308/2656] Changed amba exception Modified Exception from AXIReadError to AXIProtocolError Because that exception was raised during writes and reads --- cocotb/drivers/amba.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cocotb/drivers/amba.py b/cocotb/drivers/amba.py index 30481a61..70eebbd2 100644 --- a/cocotb/drivers/amba.py +++ b/cocotb/drivers/amba.py @@ -36,7 +36,7 @@ import array -class AXIReadError(Exception): +class AXIProtocolError(Exception): pass @@ -141,7 +141,7 @@ def write(self, address, value, byte_enable=0xf, address_latency=0, yield RisingEdge(self.clock) if int(result): - raise AXIReadError("Write to address 0x%08x failed with BRESP: %d" + raise AXIProtocolError("Write to address 0x%08x failed with BRESP: %d" % (address, int(result))) raise ReturnValue(result) @@ -175,7 +175,7 @@ def read(self, address, sync=True): yield RisingEdge(self.clock) if int(result): - raise AXIReadError("Read address 0x%08x failed with RRESP: %d" % + raise AXIProtocolError("Read address 0x%08x failed with RRESP: %d" % (address, int(result))) raise ReturnValue(data) From 138308a0477a7fc5df5497507c1749a9df33ed54 Mon Sep 17 00:00:00 2001 From: cospan Date: Fri, 28 Apr 2017 17:23:02 -0400 Subject: [PATCH 1309/2656] Created a test for AXI Lite Slaves. Included is a core that can interact with the AXI Lite master. The core will extract the address and data send from the master and interact with the demo module with a very simple handshake that the axi_lite_demo module can interface without a state machine. The master can read data from axi_lite_demo through the axi_lite_slave using a simple handshake. The axi_lite_slave will wrap up a response from the core in an AXI Lite Transaction. The testbench can be run by navigating to the following directory: /examples/axi_lite_slave/tests and running 'make' Two of the tests fail on purpose, they attempt to read/write from/to an illegal address. --- examples/axi_lite_slave/README.md | 27 +++ examples/axi_lite_slave/hdl/axi_defines.v | 68 ++++++ examples/axi_lite_slave/hdl/axi_lite_demo.v | 210 +++++++++++++++++ examples/axi_lite_slave/hdl/axi_lite_slave.v | 218 ++++++++++++++++++ .../axi_lite_slave/hdl/tb_axi_lite_slave.v | 98 ++++++++ examples/axi_lite_slave/tests/Makefile | 29 +++ .../tests/test_axi_lite_slave.py | 208 +++++++++++++++++ 7 files changed, 858 insertions(+) create mode 100644 examples/axi_lite_slave/README.md create mode 100644 examples/axi_lite_slave/hdl/axi_defines.v create mode 100644 examples/axi_lite_slave/hdl/axi_lite_demo.v create mode 100644 examples/axi_lite_slave/hdl/axi_lite_slave.v create mode 100644 examples/axi_lite_slave/hdl/tb_axi_lite_slave.v create mode 100644 examples/axi_lite_slave/tests/Makefile create mode 100644 examples/axi_lite_slave/tests/test_axi_lite_slave.py diff --git a/examples/axi_lite_slave/README.md b/examples/axi_lite_slave/README.md new file mode 100644 index 00000000..feca064b --- /dev/null +++ b/examples/axi_lite_slave/README.md @@ -0,0 +1,27 @@ +# AXI Lite Demo + +Description: In order to simplify AXI Lite Transactions we use an module that +translates AXI Lite Slave transactions to a simple register read/write. + +There are other projects that translate AXI Slave signals to wishbone. + + +## Source Files + +* tb\_axi\_lite\_slave: Testbench interface, primarily used to translate + the cocotb AXI Lite bus signals to the signals within axi_lite_demo core + +* axi\_lite\_demo: demonstration module, anyone who wishes to interact + with an AXI Lite slave core can use this as a template. + +* axi\_lite\_slave: Performs all the low level AXI Translactions. + * Addresses and data sent from the master are decoded from the AXI Bus and + sent to the user with a simple 'ready', 'acknowledge' handshake. + * Address and data are read from the slave using a simple + 'request', 'ready' handshake. + +## NOTE: Test ID + +If you use a logic analyzer wave viewer it's hard to determine which test is +currently running so I added a value called 'test\_id' in the test bench +so when viewing the waveforms the individual tests can easily be identified diff --git a/examples/axi_lite_slave/hdl/axi_defines.v b/examples/axi_lite_slave/hdl/axi_defines.v new file mode 100644 index 00000000..46581b74 --- /dev/null +++ b/examples/axi_lite_slave/hdl/axi_defines.v @@ -0,0 +1,68 @@ +`ifndef __AXI_DEFINES__ +`define __AXI_DEFINES__ + +//AXI Defines + +//--Burst +`define AXI_BURST_FIXED 2'b00 +`define AXI_BURST_INCR 2'b01 +`define AXI_BURST_WRAP 2'b10 + +//--Burst Size +`define AXI_BURST_SIZE_8BIT 3'b000 +`define AXI_BURST_SIZE_16BIT 3'b001 +`define AXI_BURST_SIZE_32BIT 3'b010 +`define AXI_BURST_SIZE_64BIT 3'b011 +`define AXI_BURST_SIZE_128BIT 3'b100 +`define AXI_BURST_SIZE_256BIT 3'b101 +`define AXI_BURST_SIZE_512BIT 3'b110 +`define AXI_BURST_SIZE_1024BIT 3'b111 + +//--Response +`define AXI_RESP_OKAY 2'b00 +`define AXI_RESP_EXOKAY 2'b01 +`define AXI_RESP_SLVERR 2'b10 +`define AXI_RESP_DECERR 2'b11 + +//--Lock +`define AXI_LOCK_NORMAL 2'b00 +`define AXI_LOCK_EXCLUSIVE 2'b01 +`define AXI_LOCK_LOCKED 2'b10 + +//--cache +//----Bufferable +`define AXI_CACHE_NON_BUF 1'b0 +`define AXI_CACHE_BUF 1'b1 +//----Cachable +`define AXI_CACHE_NON_CACHE 1'b0 +`define AXI_CACHE_CACHE 1'b1 +//----Read Allocate +`define AXI_CACHE_NON_RA 1'b0 +`define AXI_CACHE_RA 1'b1 +//----Write Allocate +`define AXI_CACHE_NON_WA 1'b0 +`define AXI_CACHE_WA 1'b1 +//----Unused +`define AXI_CACHE_UNUSED 4'b0000 + +//--Protection +//----ARPROT[0] +`define AXI_PROT_NORMAL 1'b0 +`define AXI_PROT_PRIVLEDGE 1'b1 + +//----ARPROT[1] +`define AXI_PROT_SECURE 1'b0 +`define AXI_PROT_NON_SECURE 1'b1 + +//----ARPROT[2] +`define AXI_PROT_DATA 1'b0 +`define AXI_PROT_INST 1'b1 + +//----Unused: +`define AXI_PROT_UNUSED {`AXI_PROT_NORMAL, `AXI_PROT_NON_SECURE, `AXI_PROT_DATA} + +//--Low Power Mode +`define AXI_POWER_LOW 1'b0 +`define AXI_POWER_NORMAL 1'b1 + +`endif //__AXI_DEFINES__ diff --git a/examples/axi_lite_slave/hdl/axi_lite_demo.v b/examples/axi_lite_slave/hdl/axi_lite_demo.v new file mode 100644 index 00000000..0f8ba19d --- /dev/null +++ b/examples/axi_lite_slave/hdl/axi_lite_demo.v @@ -0,0 +1,210 @@ +/* +Distributed under the MIT license. +Copyright (c) 2016 Dave McCoy (dave.mccoy@cospandesign.com) + +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. +*/ + +/* + * Author: + * Description: + * + * Changes: + */ + +`timescale 1ps / 1ps + +module axi_lite_demo #( + parameter ADDR_WIDTH = 32, + parameter DATA_WIDTH = 32, + parameter STROBE_WIDTH = (DATA_WIDTH / 8) +)( + input clk, + input rst, + + //Write Address Channel + input i_awvalid, + input [ADDR_WIDTH - 1: 0] i_awaddr, + output o_awready, + + //Write Data Channel + input i_wvalid, + output o_wready, + input [STROBE_WIDTH - 1:0] i_wstrb, + input [DATA_WIDTH - 1: 0] i_wdata, + + //Write Response Channel + output o_bvalid, + input i_bready, + output [1:0] o_bresp, + + //Read Address Channel + input i_arvalid, + output o_arready, + input [ADDR_WIDTH - 1: 0] i_araddr, + + //Read Data Channel + output o_rvalid, + input i_rready, + output [1:0] o_rresp, + output [DATA_WIDTH - 1: 0] o_rdata +); +//local parameters + +//Address Map +localparam ADDR_0 = 0; +localparam ADDR_1 = 1; + +localparam MAX_ADDR = ADDR_1; + +//registes/wires + +//User Interface +wire [ADDR_WIDTH - 1: 0] w_reg_address; +reg r_reg_invalid_addr; + +wire w_reg_in_rdy; +reg r_reg_in_ack_stb; +wire [DATA_WIDTH - 1: 0] w_reg_in_data; + +wire w_reg_out_req; +reg r_reg_out_rdy_stb; +reg [DATA_WIDTH - 1: 0] r_reg_out_data; + + +//TEMP DATA, JUST FOR THE DEMO +reg [DATA_WIDTH - 1: 0] r_temp_0; +reg [DATA_WIDTH - 1: 0] r_temp_1; + +//submodules + +//Convert AXI Slave bus to a simple register/address strobe +axi_lite_slave #( + .ADDR_WIDTH (ADDR_WIDTH ), + .DATA_WIDTH (DATA_WIDTH ) + +) axi_lite_reg_interface ( + .clk (clk ), + .rst (rst ), + + + .i_awvalid (i_awvalid ), + .i_awaddr (i_awaddr ), + .o_awready (o_awready ), + + .i_wvalid (i_wvalid ), + .o_wready (o_wready ), + .i_wstrb (i_wstrb ), + .i_wdata (i_wdata ), + + .o_bvalid (o_bvalid ), + .i_bready (i_bready ), + .o_bresp (o_bresp ), + + .i_arvalid (i_arvalid ), + .o_arready (o_arready ), + .i_araddr (i_araddr ), + + .o_rvalid (o_rvalid ), + .i_rready (i_rready ), + .o_rresp (o_rresp ), + .o_rdata (o_rdata ), + + + //Simple Register Interface + .o_reg_address (w_reg_address ), + .i_reg_invalid_addr (r_reg_invalid_addr ), + + //Ingress Path (From Master) + .o_reg_in_rdy (w_reg_in_rdy ), + .i_reg_in_ack_stb (r_reg_in_ack_stb ), + .o_reg_in_data (w_reg_in_data ), + + //Egress Path (To Master) + .o_reg_out_req (w_reg_out_req ), + .i_reg_out_rdy_stb (r_reg_out_rdy_stb ), + .i_reg_out_data (r_reg_out_data ) +); + +//asynchronous logic + +//synchronous logic +always @ (posedge clk) begin + //De-assert Strobes + r_reg_in_ack_stb <= 0; + r_reg_out_rdy_stb <= 0; + r_reg_invalid_addr <= 0; + + if (rst) begin + r_reg_out_data <= 0; + + //Reset the temporary Data + r_temp_0 <= 0; + r_temp_1 <= 0; + end + else begin + + if (w_reg_in_rdy) begin + //From master + case (w_reg_address) + ADDR_0: begin + //$display("Incomming data on address: 0x%h: 0x%h", w_reg_address, w_reg_in_data); + r_temp_0 <= w_reg_in_data; + end + ADDR_1: begin + //$display("Incomming data on address: 0x%h: 0x%h", w_reg_address, w_reg_in_data); + r_temp_1 <= w_reg_in_data; + end + default: begin + $display ("Unknown address: 0x%h", w_reg_address); + end + endcase + if (w_reg_address > MAX_ADDR) begin + //Tell the host they wrote to an invalid address + r_reg_invalid_addr <= 1; + end + //Tell the AXI Slave Control we're done with the data + r_reg_in_ack_stb <= 1; + end + else if (w_reg_out_req) begin + //To master + //$display("User is reading from address 0x%0h", w_reg_address); + case (w_reg_address) + ADDR_0: begin + r_reg_out_data <= r_temp_0; + end + ADDR_1: begin + r_reg_out_data <= r_temp_1; + end + default: begin + //Unknown address + r_reg_out_data <= 32'h00; + end + endcase + if (w_reg_address > MAX_ADDR) begin + //Tell the host they are reading from an invalid address + r_reg_invalid_addr <= 1; + end + //Tell the AXI Slave to send back this packet + r_reg_out_rdy_stb <= 1; + end + end +end + +endmodule diff --git a/examples/axi_lite_slave/hdl/axi_lite_slave.v b/examples/axi_lite_slave/hdl/axi_lite_slave.v new file mode 100644 index 00000000..198f93b8 --- /dev/null +++ b/examples/axi_lite_slave/hdl/axi_lite_slave.v @@ -0,0 +1,218 @@ +/* +Distributed under the MIT license. +Copyright (c) 2017 Dave McCoy (dave.mccoy@cospandesign.com) + +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. +*/ + +/* + * Author: David McCoy (dave.mccoy@cospandesign.com) + * Description: An AXI Lite Slave interface that simplifies register access + * XXX: Currently there is no strobe level access + * + * Changes: + * 3/24/2017: Initial Commit + */ + +`timescale 1ps / 1ps + +`include "axi_defines.v" + +module axi_lite_slave #( + parameter ADDR_WIDTH = 32, + parameter DATA_WIDTH = 32, + parameter STROBE_WIDTH = (DATA_WIDTH / 8) + +)( + + input clk, + input rst, + + //Write Address Channel + input i_awvalid, + input [ADDR_WIDTH - 1: 0] i_awaddr, + output reg o_awready, + + //Write Data Channel + input i_wvalid, + output reg o_wready, + input [STROBE_WIDTH - 1:0] i_wstrb, + input [DATA_WIDTH - 1: 0] i_wdata, + + //Write Response Channel + output reg o_bvalid, + input i_bready, + output reg [1:0] o_bresp, + + //Read Address Channel + input i_arvalid, + output reg o_arready, + input [ADDR_WIDTH - 1: 0] i_araddr, + + //Read Data Channel + output reg o_rvalid, + input i_rready, + output reg [1:0] o_rresp, + output reg [DATA_WIDTH - 1: 0] o_rdata, + + + + //Simple User Interface + output reg o_reg_in_rdy, + input i_reg_in_ack_stb, + output reg [ADDR_WIDTH - 1: 0] o_reg_address, + output reg [DATA_WIDTH - 1: 0] o_reg_in_data, + + output reg o_reg_out_req, + input i_reg_out_rdy_stb, + input [DATA_WIDTH - 1: 0] i_reg_out_data, + input i_reg_invalid_addr + +); + + + +//local parameters +localparam IDLE = 4'h0; +localparam RECEIVE_WRITE_DATA = 4'h1; +localparam WRITE_WAIT_FOR_USER = 4'h2; +localparam SEND_WRITE_RESP = 4'h3; +localparam READ_WAIT_FOR_USER = 4'h4; +localparam SEND_READ_DATA = 4'h5; + +//registes/wires +reg [3:0] state; + +//submodules +//asynchronous logic +//synchronous logic + +always @ (posedge clk) begin + //Deassert Strobes + if (rst) begin + o_reg_address <= 0; + o_arready <= 0; + o_awready <= 0; + o_wready <= 0; + + o_rvalid <= 0; + o_rdata <= 0; + o_bresp <= 0; + o_rresp <= 0; + o_bvalid <= 0; + + //Demo values + state <= IDLE; + o_reg_in_rdy <= 0; + o_reg_in_data <= 0; + o_reg_out_req <= 0; + end + else begin + case (state) + IDLE: begin + o_reg_in_rdy <= 0; + o_reg_address <= 0; + o_awready <= 1; + o_arready <= 1; + o_rvalid <= 0; + o_bresp <= 0; + o_rresp <= 0; + o_bvalid <= 0; + o_reg_out_req <= 0; + o_wready <= 0; + + //Only handle read or write at one time, not both + if (i_awvalid && o_awready) begin + o_reg_address <= i_awaddr; + o_wready <= 1; + o_arready <= 0; + state <= RECEIVE_WRITE_DATA; + end + else if (i_arvalid && o_arready) begin + o_reg_address <= i_araddr; + o_awready <= 0; + o_reg_out_req <= 1; + state <= READ_WAIT_FOR_USER; + end + end + RECEIVE_WRITE_DATA: begin + o_awready <= 0; + + if (i_wvalid) begin + //Assume everything is okay unless the o_reg_address is wrong, + //We don't want to clutter our states with this statement over and over again + o_reg_in_data <= i_wdata; + state <= WRITE_WAIT_FOR_USER; + o_reg_in_rdy <= 1; + end + end + WRITE_WAIT_FOR_USER: begin + //Wait for the user to ackknowledge the new info, user can introduce a delay + if (i_reg_in_ack_stb) begin + state <= SEND_WRITE_RESP; + o_reg_in_rdy <= 0; + o_bvalid <= 1; + if (i_reg_invalid_addr) begin + o_bresp <= `AXI_RESP_DECERR; + end + else begin + o_bresp <= `AXI_RESP_OKAY; + end + end + end + SEND_WRITE_RESP: begin + if (i_bready && o_bvalid) begin + o_bvalid <= 0; + state <= IDLE; + end + end + + //Read Path + READ_WAIT_FOR_USER: begin + o_arready <= 0; + if (i_reg_out_rdy_stb) begin + //The data in i_reg_out_data should be valid now + o_rdata <= i_reg_out_data; + if (i_reg_invalid_addr) begin + o_rresp <= `AXI_RESP_DECERR; + end + else begin + o_rresp <= `AXI_RESP_OKAY; + end + o_rvalid <= 1; + state <= SEND_READ_DATA; + end + end + SEND_READ_DATA: begin + //If more time is needed for a response another state should be added here + if (i_rready && o_rvalid) begin + o_rvalid <= 0; + state <= IDLE; + end + end + default: begin + $display("AXI Lite Slave: Shouldn't have gotten here!"); + end + endcase + end +end + + + +endmodule diff --git a/examples/axi_lite_slave/hdl/tb_axi_lite_slave.v b/examples/axi_lite_slave/hdl/tb_axi_lite_slave.v new file mode 100644 index 00000000..e68d41f6 --- /dev/null +++ b/examples/axi_lite_slave/hdl/tb_axi_lite_slave.v @@ -0,0 +1,98 @@ +`timescale 1ns/1ps + + +module tb_axi_lite_slave #( + parameter ADDR_WIDTH = 32, + parameter DATA_WIDTH = 32, + parameter STROBE_WIDTH = (DATA_WIDTH / 8) +)( + +input clk, +input rst, + +//Write Address Channel +input AXIML_AWVALID, +input [ADDR_WIDTH - 1: 0] AXIML_AWADDR, +output AXIML_AWREADY, + +//Write Data Channel +input AXIML_WVALID, +output AXIML_WREADY, +input [STROBE_WIDTH - 1:0] AXIML_WSTRB, +input [DATA_WIDTH - 1: 0] AXIML_WDATA, + +//Write Response Channel +output AXIML_BVALID, +input AXIML_BREADY, +output [1:0] AXIML_BRESP, + +//Read Address Channel +input AXIML_ARVALID, +output AXIML_ARREADY, +input [ADDR_WIDTH - 1: 0] AXIML_ARADDR, + +//Read Data Channel +output AXIML_RVALID, +input AXIML_RREADY, +output [1:0] AXIML_RRESP, +output [DATA_WIDTH - 1: 0] AXIML_RDATA + +); + + +//Local Parameters +//Registers + +reg r_rst; +reg test_id = 0; + +//Workaround for weird icarus simulator bug +always @ (*) r_rst = rst; + +//submodules +axi_lite_demo #( + .ADDR_WIDTH (ADDR_WIDTH ), + .DATA_WIDTH (DATA_WIDTH ) +) dut ( + .clk (clk ), + .rst (r_rst ), + + + .i_awvalid (AXIML_AWVALID ), + .i_awaddr (AXIML_AWADDR ), + .o_awready (AXIML_AWREADY ), + + + .i_wvalid (AXIML_WVALID ), + .o_wready (AXIML_WREADY ), + .i_wstrb (AXIML_WSTRB ), + .i_wdata (AXIML_WDATA ), + + + .o_bvalid (AXIML_BVALID ), + .i_bready (AXIML_BREADY ), + .o_bresp (AXIML_BRESP ), + + + .i_arvalid (AXIML_ARVALID ), + .o_arready (AXIML_ARREADY ), + .i_araddr (AXIML_ARADDR ), + + + .o_rvalid (AXIML_RVALID ), + .i_rready (AXIML_RREADY ), + .o_rresp (AXIML_RRESP ), + .o_rdata (AXIML_RDATA ) + +); + +//asynchronus logic +//synchronous logic + +initial begin + $dumpfile ("design.vcd"); + $dumpvars(0, tb_axi_lite_slave); +end + + +endmodule diff --git a/examples/axi_lite_slave/tests/Makefile b/examples/axi_lite_slave/tests/Makefile new file mode 100644 index 00000000..4d2e8f47 --- /dev/null +++ b/examples/axi_lite_slave/tests/Makefile @@ -0,0 +1,29 @@ + +TOPLEVEL_LANG ?= verilog +PWD=$(shell pwd) +TOPDIR=$(PWD)/.. +COCOTB=$(PWD)/../../.. +PYTHONPATH := ./model:$(PYTHONPATH) + +export PYTHONPATH +export PYTHONHOME=$(shell python -c "from distutils.sysconfig import get_config_var; print(get_config_var('prefix'))") + +EXTRA_ARGS+=-I$(TOPDIR)/hdl/ + +#DUT +VERILOG_SOURCES = $(TOPDIR)/hdl/axi_lite_slave.v +VERILOG_SOURCES += $(TOPDIR)/hdl/axi_lite_demo.v + +#Test Bench +VERILOG_SOURCES += $(TOPDIR)/hdl/tb_axi_lite_slave.v + +TOPLEVEL = tb_axi_lite_slave + +GPI_IMPL := vpi + +export TOPLEVEL_LANG +MODULE=test_axi_lite_slave + +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim + diff --git a/examples/axi_lite_slave/tests/test_axi_lite_slave.py b/examples/axi_lite_slave/tests/test_axi_lite_slave.py new file mode 100644 index 00000000..ad0eb9d8 --- /dev/null +++ b/examples/axi_lite_slave/tests/test_axi_lite_slave.py @@ -0,0 +1,208 @@ +import os +import sys +import cocotb +import logging +from cocotb.result import TestFailure +from cocotb.result import TestSuccess +from cocotb.clock import Clock +import time +from array import array as Array +from cocotb.triggers import Timer +from cocotb.drivers.amba import AXI4LiteMaster +from cocotb.drivers.amba import AXIProtocolError + +CLK_PERIOD = 10 + +MODULE_PATH = os.path.join(os.path.dirname(__file__), os.pardir, "hdl") +MODULE_PATH = os.path.abspath(MODULE_PATH) + +def setup_dut(dut): + cocotb.fork(Clock(dut.clk, CLK_PERIOD).start()) + +#Write to address 0 and verify that value got through +@cocotb.test(skip = False) +def write_address_0(dut): + """ + Description: + Write to the register at address 0 + verify the value has changed + + Test ID: 0 + + Expected Results: + The value read directly from the register is the same as the + value written + """ + + #Reset + dut.rst <= 1 + dut.test_id <= 0 + axim = AXI4LiteMaster(dut, "AXIML", dut.clk) + setup_dut(dut) + yield Timer(CLK_PERIOD * 10) + dut.rst <= 0 + + ADDRESS = 0x00 + DATA = 0xAB + + yield axim.write(ADDRESS, DATA) + yield Timer(CLK_PERIOD * 10) + + value = dut.dut.r_temp_0 + if value != DATA: + #Fail + raise TestFailure("Register at address 0x%08X should have been: \ + 0x%08X but was 0x%08X" % (ADDRESS, DATA, value)) + + dut.log.info("Write 0x%08X to addres 0x%08X" % (value, ADDRESS)) + + +#Read back a value at address 0x01 +@cocotb.test(skip = False) +def read_address_1(dut): + """ + Description + Use cocotb to set the value of the register at address 0x01 + Use AXIML to read the contents of that register and + compare the values + + Test ID: 1 + + Expected Results: + The value read from the register is the same as the value written + """ + #Reset + dut.rst <= 1 + dut.test_id <= 0 + axim = AXI4LiteMaster(dut, "AXIML", dut.clk) + setup_dut(dut) + yield Timer(CLK_PERIOD * 10) + dut.rst <= 0 + + ADDRESS = 0x01 + DATA = 0xCD + + dut.dut.r_temp_1 <= DATA + yield Timer(CLK_PERIOD * 10) + + value = yield axim.read(ADDRESS) + yield Timer(CLK_PERIOD * 10) + + if value != DATA: + #Fail + raise TestFailure("Register at address 0x%08X should have been: \ + 0x%08X but was 0x%08X" % (ADDRESS, DATA, value)) + + dut.log.info("Read: 0x%08X From Address: 0x%08X" % (value, ADDRESS)) + + + +@cocotb.test(skip = False) +def write_and_read(dut): + """ + Description: + Write to the register at address 0 + read back from that register and verify the value is the same + + Test ID: 2 + + Expected Results: + The contents of the register is the same as the value written + """ + + #Reset + dut.rst <= 1 + dut.test_id <= 2 + axim = AXI4LiteMaster(dut, "AXIML", dut.clk) + setup_dut(dut) + yield Timer(CLK_PERIOD * 10) + dut.rst <= 0 + + ADDRESS = 0x00 + DATA = 0xAB + + #Write to the register + yield axim.write(ADDRESS, DATA) + yield Timer(CLK_PERIOD * 10) + + #Read back the value + value = yield axim.read(ADDRESS) + yield Timer(CLK_PERIOD * 10) + + value = dut.dut.r_temp_0 + if value != DATA: + #Fail + raise TestFailure("Register at address 0x%08X should have been: \ + 0x%08X but was 0x%08X" % (ADDRESS, DATA, value)) + + dut.log.info("Write 0x%08X to addres 0x%08X" % (value, ADDRESS)) + +@cocotb.test(skip = False, expect_error=True) +def write_fail(dut): + """ + Description: + Attemp to write data to an address that doesn't exist. This test + should fail + + Test ID: 3 + + Expected Results: + The AXIML bus should throw an exception because the user attempted + to write to an invalid address + """ + #Reset + dut.rst <= 1 + dut.test_id <= 3 + axim = AXI4LiteMaster(dut, "AXIML", dut.clk) + setup_dut(dut) + yield Timer(CLK_PERIOD * 10) + dut.rst <= 0 + + ADDRESS = 0x02 + DATA = 0xAB + + try: + yield axim.write(ADDRESS, DATA) + yield Timer(CLK_PERIOD * 10) + except AXIProtocolError as e: + print "Exception: %s" % str(e) + dut.log.info("Bus Successfully Raised an Error") + raise TestSuccess() + raise TestFailure("AXI Bus Should have raised an ERROR when writing to \ + the wrong bus") + +@cocotb.test(skip = False, expect_error=True) +def read_fail(dut): + """ + Description: + Attemp to write data to an address that doesn't exist. This test + should fail + + Test ID: 4 + + Expected Results: + The AXIML bus should throw an exception because the user attempted + to write to an invalid address + """ + #Reset + dut.rst <= 1 + dut.test_id <= 4 + axim = AXI4LiteMaster(dut, "AXIML", dut.clk) + setup_dut(dut) + yield Timer(CLK_PERIOD * 10) + dut.rst <= 0 + + ADDRESS = 0x02 + DATA = 0xAB + + try: + yield axim.read(ADDRESS, DATA) + yield Timer(CLK_PERIOD * 10) + except AXIProtocolError as e: + print "Exception: %s" % str(e) + dut.log.info("Bus Successfully Raised an Error") + raise TestSuccess() + raise TestFailure("AXI Bus Should have raised an ERROR when writing to \ + the wrong bus") + + From 3f427dc5b87b4686b37675099ef6dc4d17a0148c Mon Sep 17 00:00:00 2001 From: "Oliver A. Gubler" Date: Thu, 4 May 2017 09:12:36 +0200 Subject: [PATCH 1310/2656] use built-in Clock() instead of separate clock_gen() function also makes it match the version on edaplayground.com --- documentation/source/endian_swapper.rst | 2 +- examples/endian_swapper/tests/test_endian_swapper.py | 12 +----------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/documentation/source/endian_swapper.rst b/documentation/source/endian_swapper.rst index f5175fff..e2b2300e 100644 --- a/documentation/source/endian_swapper.rst +++ b/documentation/source/endian_swapper.rst @@ -120,7 +120,7 @@ We want to run different variations of tests but they will all have a very simil @cocotb.coroutine def run_test(dut, data_in=None, config_coroutine=None, idle_inserter=None, backpressure_inserter=None): - cocotb.fork(clock_gen(dut.clk)) + cocotb.fork(Clock(dut.clk, 5000).start()) tb = EndianSwapperTB(dut) yield tb.reset() diff --git a/examples/endian_swapper/tests/test_endian_swapper.py b/examples/endian_swapper/tests/test_endian_swapper.py index de86f712..62ff4b07 100644 --- a/examples/endian_swapper/tests/test_endian_swapper.py +++ b/examples/endian_swapper/tests/test_endian_swapper.py @@ -111,21 +111,11 @@ def reset(self, duration=10000): self.dut._log.debug("Out of reset") -@cocotb.coroutine -def clock_gen(signal): - while True: - signal <= 0 - yield Timer(5000) - signal <= 1 - yield Timer(5000) - - @cocotb.coroutine def run_test(dut, data_in=None, config_coroutine=None, idle_inserter=None, backpressure_inserter=None): - cocotb.fork(clock_gen(dut.clk)) - yield RisingEdge(dut.clk) + cocotb.fork(Clock(dut.clk, 5000).start()) tb = EndianSwapperTB(dut) yield tb.reset() From e14b7deb2e0f9904721e56b84d32703d0751bd5e Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 11 May 2017 12:37:59 -0500 Subject: [PATCH 1311/2656] Issue 85: get information about object's definition (VPI only) --- cocotb/handle.py | 22 ++++++++++- include/gpi.h | 5 +++ lib/gpi/GpiCommon.cpp | 18 +++++++++ lib/gpi/gpi_priv.h | 9 +++++ lib/simulator/simulatormodule.c | 66 +++++++++++++++++++++++++++++++++ lib/simulator/simulatormodule.h | 6 +++ lib/vpi/VpiCbHdl.cpp | 16 ++++++++ lib/vpi/VpiImpl.cpp | 4 +- lib/vpi/VpiImpl.h | 9 +++++ 9 files changed, 152 insertions(+), 3 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 205eac56..14f0e7c4 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -90,7 +90,19 @@ def __init__(self, handle, path): self._path = self._name if path is None else path self._log = SimLog("cocotb.%s" % self._name) self._log.debug("Created") + self._def_name = simulator.get_definition_name(self._handle) + self._def_file = simulator.get_definition_file(self._handle) + self._def_line = simulator.get_definition_line(self._handle) + def get_definition_name(self): + return object.__getattribute__(self, "_def_name") + + def get_definition_file(self): + return object.__getattribute__(self, "_def_file") + + def get_definition_line(self): + return object.__getattribute__(self, "_def_line") + def __hash__(self): return self._handle @@ -114,7 +126,15 @@ def __ne__(self, other): return not self.__eq__(other) def __repr__(self): - return type(self).__name__ + "(" + self._path + ")" + desc = self._path + defname = object.__getattribute__(self, "_def_name") + if defname: + desc += " with definition "+defname + deffile = object.__getattribute__(self, "_def_file") + if deffile: + defline = object.__getattribute__(self, "_def_line") + desc += " (at "+deffile+" line "+str(defline)+")" + return type(self).__name__ + "(" + desc + ")" def __str__(self): return self._path diff --git a/include/gpi.h b/include/gpi.h index f96309e0..66a0a8bd 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -182,6 +182,11 @@ const char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl); // Returns on of the types defined above e.g. gpiMemory etc. gpi_objtype_t gpi_get_object_type(gpi_sim_hdl gpi_hdl); +// Get information about the definition of a handle +const char* gpi_get_definition_name(gpi_sim_hdl gpi_hdl); +const char* gpi_get_definition_file(gpi_sim_hdl gpi_hdl); +int gpi_get_definition_line(gpi_sim_hdl gpi_hdl); + // Determine whether an object value is constant (parameters / generics etc) int gpi_is_constant(gpi_sim_hdl gpi_hdl); diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 71203f42..3f148cbd 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -406,6 +406,24 @@ gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator) } } +const char* gpi_get_definition_name(gpi_sim_hdl sig_hdl) +{ + GpiObjHdl *obj_hdl = sim_to_hdl(sig_hdl); + return obj_hdl->get_definition_name(); +} + +const char* gpi_get_definition_file(gpi_sim_hdl sig_hdl) +{ + GpiObjHdl *obj_hdl = sim_to_hdl(sig_hdl); + return obj_hdl->get_definition_file(); +} + +int gpi_get_definition_line(gpi_sim_hdl sig_hdl) +{ + GpiObjHdl *obj_hdl = sim_to_hdl(sig_hdl); + return obj_hdl->get_definition_line(); +} + const char *gpi_get_signal_value_binstr(gpi_sim_hdl sig_hdl) { GpiSignalObjHdl *obj_hdl = sim_to_hdl(sig_hdl); diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index 07574335..ddec706e 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -136,6 +136,10 @@ class GpiObjHdl : public GpiHdl { const std::string & get_name(void); const std::string & get_fullname(void); + virtual const char* get_definition_name() { return m_definition_name.c_str(); }; + virtual const char* get_definition_file() { return m_definition_file.c_str(); }; + virtual int get_definition_line() { return m_definition_line; }; + bool is_native_impl(GpiImplInterface *impl); virtual int initialise(std::string &name, std::string &full_name); @@ -146,6 +150,11 @@ class GpiObjHdl : public GpiHdl { int m_range_right; std::string m_name; std::string m_fullname; + + std::string m_definition_name; + std::string m_definition_file; + int m_definition_line; + gpi_objtype_t m_type; bool m_const; }; diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 1e0e9430..e7a3f186 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -682,6 +682,72 @@ static PyObject *set_signal_val_long(PyObject *self, PyObject *args) return res; } +static PyObject *get_definition_name(PyObject *self, PyObject *args) +{ + const char* result; + gpi_sim_hdl hdl; + PyObject *retstr; + + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + if (!PyArg_ParseTuple(args, "l", &hdl)) { + DROP_GIL(gstate); + return NULL; + } + + result = gpi_get_definition_name((gpi_sim_hdl)hdl); + retstr = Py_BuildValue("s", result); + + DROP_GIL(gstate); + + return retstr; +} + +static PyObject *get_definition_file(PyObject *self, PyObject *args) +{ + const char* result; + gpi_sim_hdl hdl; + PyObject *retstr; + + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + if (!PyArg_ParseTuple(args, "l", &hdl)) { + DROP_GIL(gstate); + return NULL; + } + + result = gpi_get_definition_file((gpi_sim_hdl)hdl); + retstr = Py_BuildValue("s", result); + + DROP_GIL(gstate); + + return retstr; +} + +static PyObject *get_definition_line(PyObject *self, PyObject *args) +{ + int result; + gpi_sim_hdl hdl; + PyObject *retval; + + PyGILState_STATE gstate; + gstate = TAKE_GIL(); + + if (!PyArg_ParseTuple(args, "l", &hdl)) { + DROP_GIL(gstate); + return NULL; + } + + result = gpi_get_definition_line((gpi_sim_hdl)hdl); + retval = Py_BuildValue("l", result); + + DROP_GIL(gstate); + + return retval; +} + static PyObject *get_handle_by_name(PyObject *self, PyObject *args) { const char *name; diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index fe1acbee..60257126 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -64,6 +64,9 @@ static PyObject *get_signal_val_binstr(PyObject *self, PyObject *args); static PyObject *set_signal_val_long(PyObject *self, PyObject *args); static PyObject *set_signal_val_real(PyObject *self, PyObject *args); static PyObject *set_signal_val_str(PyObject *self, PyObject *args); +static PyObject *get_definition_name(PyObject *self, PyObject *args); +static PyObject *get_definition_file(PyObject *self, PyObject *args); +static PyObject *get_definition_line(PyObject *self, PyObject *args); static PyObject *get_handle_by_name(PyObject *self, PyObject *args); static PyObject *get_handle_by_index(PyObject *self, PyObject *args); static PyObject *get_root_handle(PyObject *self, PyObject *args); @@ -98,6 +101,9 @@ static PyMethodDef SimulatorMethods[] = { {"set_signal_val_long", set_signal_val_long, METH_VARARGS, "Set the value of a signal using a long"}, {"set_signal_val_str", set_signal_val_str, METH_VARARGS, "Set the value of a signal using a binary string"}, {"set_signal_val_real", set_signal_val_real, METH_VARARGS, "Set the value of a signal using a double precision float"}, + {"get_definition_name", get_definition_name, METH_VARARGS, "Get the name of a GPI object's definition"}, + {"get_definition_file", get_definition_file, METH_VARARGS, "Get the file that sources the object's definition"}, + {"get_definition_line", get_definition_line, METH_VARARGS, "Get the line number that sources the object's definition"}, {"get_handle_by_name", get_handle_by_name, METH_VARARGS, "Get handle of a named object"}, {"get_handle_by_index", get_handle_by_index, METH_VARARGS, "Get handle of a object at an index in a parent"}, {"get_root_handle", get_root_handle, METH_VARARGS, "Get the root handle"}, diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 7662f58c..bde7c1e5 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -204,6 +204,22 @@ int VpiArrayObjHdl::initialise(std::string &name, std::string &fq_name) { return GpiObjHdl::initialise(name, fq_name); } +int VpiObjHdl::initialise(std::string &name, std::string &fq_name) { + char * str; + vpiHandle hdl = GpiObjHdl::get_handle(); + + str = vpi_get_str(vpiDefName, hdl); + if (str != NULL) + m_definition_name = str; + str = vpi_get_str(vpiDefFile, hdl); + if (str != NULL) + m_definition_file = str; + + m_definition_line = vpi_get(vpiDefLineNo, hdl); + + return GpiObjHdl::initialise(name, fq_name); +} + int VpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { int32_t type = vpi_get(vpiType, GpiObjHdl::get_handle()); if ((vpiIntVar == type) || diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 18520e55..fe3c7814 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -181,9 +181,9 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, if (hdl_name != name) { LOG_DEBUG("Found pseudo-region %s", fq_name.c_str()); - new_obj = new GpiObjHdl(this, new_hdl, GPI_GENARRAY); + new_obj = new VpiObjHdl(this, new_hdl, GPI_GENARRAY); } else { - new_obj = new GpiObjHdl(this, new_hdl, to_gpi_objtype(type)); + new_obj = new VpiObjHdl(this, new_hdl, to_gpi_objtype(type)); } break; } diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 55c1de64..f2e43271 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -182,6 +182,15 @@ class VpiArrayObjHdl : public GpiObjHdl { int initialise(std::string &name, std::string &fq_name); }; +class VpiObjHdl : public GpiObjHdl { +public: + VpiObjHdl(GpiImplInterface *impl, vpiHandle hdl, gpi_objtype_t objtype) : + GpiObjHdl(impl, hdl, objtype) { } + virtual ~VpiObjHdl() { } + + int initialise(std::string &name, std::string &fq_name); +}; + class VpiSignalObjHdl : public GpiSignalObjHdl { public: VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl, gpi_objtype_t objtype, bool is_const) : From b43e7fea59d202a66d666a7039e745275983aa75 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 12 May 2017 13:34:21 -0500 Subject: [PATCH 1312/2656] Issue 86: implement pre-test hooks for cocotb, passed in via environment variable COCOTB_HOOKS --- cocotb/__init__.py | 9 +++++++-- cocotb/decorators.py | 22 ++++++++++++++++++++++ cocotb/regression.py | 17 ++++++++++++++++- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 65bd14e5..84622607 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -45,7 +45,7 @@ # Things we want in the cocotb namespace -from cocotb.decorators import test, coroutine, function, external +from cocotb.decorators import test, coroutine, hook, function, external # Singleton scheduler instance # NB this cheekily ensures a singleton since we're replacing the reference @@ -96,6 +96,9 @@ def _initialise_testbench(root_name): The test must be defined by the environment variables MODULE TESTCASE + + The environment variable COCOTB_HOOKS contains a comma-separated list of + modules that should be executed before the first test. """ _rlock.acquire() @@ -136,16 +139,18 @@ def _initialise_testbench(root_name): module_str = os.getenv('MODULE') test_str = os.getenv('TESTCASE') + hooks_str = os.getenv('COCOTB_HOOKS', '') if not module_str: raise ImportError("Environment variables defining the module(s) to \ execute not defined. MODULE=\"%s\"\"" % (module_str)) modules = module_str.split(',') + hooks = hooks_str.split(',') if hooks_str else [] global regression - regression = RegressionManager(root_name, modules, tests=test_str, seed=seed) + regression = RegressionManager(root_name, modules, tests=test_str, seed=seed, hooks=hooks) regression.initialise() regression.execute() diff --git a/cocotb/decorators.py b/cocotb/decorators.py index b7bfe773..75b57964 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -357,6 +357,28 @@ def execute_external(func, _event): return wrapped +@public +class hook(coroutine): + """Decorator to mark a function as a hook for cocotb + + All hooks are run at the beginning of a cocotb test suite, prior to any + test code being run.""" + def __init__(self): + pass + + def __call__(self, f): + super(hook, self).__init__(f) + + def _wrapped_hook(*args, **kwargs): + try: + return RunningCoroutine(self._func(*args, **kwargs), self) + except Exception as e: + raise raise_error(self, str(e)) + + _wrapped_hook.im_hook = True + _wrapped_hook.name = self._func.__name__ + _wrapped_hook.__name__ = self._func.__name__ + return _wrapped_hook @public class test(coroutine): diff --git a/cocotb/regression.py b/cocotb/regression.py index f8ef932c..54de1f54 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -71,7 +71,7 @@ def _my_import(name): class RegressionManager(object): """Encapsulates all regression capability into a single place""" - def __init__(self, root_name, modules, tests=None, seed=None): + def __init__(self, root_name, modules, tests=None, seed=None, hooks=[]): """ Args: modules (list): A list of python module names to run @@ -87,6 +87,7 @@ def __init__(self, root_name, modules, tests=None, seed=None): self._cov = None self.log = SimLog("cocotb.regression") self._seed = seed + self._hooks = hooks def initialise(self): @@ -174,6 +175,20 @@ def initialise(self): (valid_tests.module, valid_tests.funcname)) + for module_name in self._hooks: + self.log.info("Loading hook from module '"+module_name+"'") + module = _my_import(module_name) + + for thing in vars(module).values(): + if hasattr(thing, "im_hook"): + try: + test = thing(self._dut) + except TestError: + self.log.warning("Failed to initialize hook %s" % thing.name) + else: + cocotb.scheduler.add(test) + + def tear_down(self): """It's the end of the world as we know it""" if self.failures: From 6faec7b883c6f1a85e385ee5610ff9c5e14e47e3 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 30 May 2017 15:55:16 -0500 Subject: [PATCH 1313/2656] Jenkins is stuck; trigger rebuild --- documentation/Makefile | 1 + documentation/source/index.rst | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Makefile b/documentation/Makefile index e410134c..5248dbf6 100644 --- a/documentation/Makefile +++ b/documentation/Makefile @@ -175,3 +175,4 @@ pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." + diff --git a/documentation/source/index.rst b/documentation/source/index.rst index 5070e624..523c9ad0 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -26,4 +26,3 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` - From b063376466ece37c1b1a46c2c6bb68e2e1d46733 Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 31 May 2017 12:42:16 -0500 Subject: [PATCH 1314/2656] Issue 85: add support for FLI and VHPI; remove line number which doesn't seem obtainable with FLI --- cocotb/handle.py | 7 +------ include/gpi.h | 1 - lib/fli/FliObjHdl.cpp | 11 ++++++++++- lib/gpi/GpiCommon.cpp | 6 ------ lib/gpi/gpi_priv.h | 2 -- lib/simulator/simulatormodule.c | 22 ---------------------- lib/simulator/simulatormodule.h | 2 -- lib/vhpi/VhpiCbHdl.cpp | 22 ++++++++++++++++++++++ lib/vhpi/VhpiImpl.cpp | 2 +- lib/vhpi/VhpiImpl.h | 10 ++++++++++ lib/vpi/VpiCbHdl.cpp | 2 -- 11 files changed, 44 insertions(+), 43 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 14f0e7c4..06b546e1 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -92,7 +92,6 @@ def __init__(self, handle, path): self._log.debug("Created") self._def_name = simulator.get_definition_name(self._handle) self._def_file = simulator.get_definition_file(self._handle) - self._def_line = simulator.get_definition_line(self._handle) def get_definition_name(self): return object.__getattribute__(self, "_def_name") @@ -100,9 +99,6 @@ def get_definition_name(self): def get_definition_file(self): return object.__getattribute__(self, "_def_file") - def get_definition_line(self): - return object.__getattribute__(self, "_def_line") - def __hash__(self): return self._handle @@ -132,8 +128,7 @@ def __repr__(self): desc += " with definition "+defname deffile = object.__getattribute__(self, "_def_file") if deffile: - defline = object.__getattribute__(self, "_def_line") - desc += " (at "+deffile+" line "+str(defline)+")" + desc += " (at "+deffile+")" return type(self).__name__ + "(" + desc + ")" def __str__(self): diff --git a/include/gpi.h b/include/gpi.h index 66a0a8bd..0527b38e 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -185,7 +185,6 @@ gpi_objtype_t gpi_get_object_type(gpi_sim_hdl gpi_hdl); // Get information about the definition of a handle const char* gpi_get_definition_name(gpi_sim_hdl gpi_hdl); const char* gpi_get_definition_file(gpi_sim_hdl gpi_hdl); -int gpi_get_definition_line(gpi_sim_hdl gpi_hdl); // Determine whether an object value is constant (parameters / generics etc) int gpi_is_constant(gpi_sim_hdl gpi_hdl); diff --git a/lib/fli/FliObjHdl.cpp b/lib/fli/FliObjHdl.cpp index ac0d82d7..d2e7daa9 100644 --- a/lib/fli/FliObjHdl.cpp +++ b/lib/fli/FliObjHdl.cpp @@ -64,6 +64,7 @@ int FliObjHdl::initialise(std::string &name, std::string &fq_name) { bool is_signal = (get_acc_type() == accSignal || get_acc_full_type() == accAliasSignal); mtiTypeIdT typeId; + char * str; switch (get_type()) { case GPI_STRUCTURE: @@ -84,7 +85,15 @@ int FliObjHdl::initialise(std::string &name, std::string &fq_name) LOG_CRITICAL("Invalid object type for FliObjHdl. (%s (%s))", name.c_str(), get_type_str()); return -1; } - + + str = mti_GetPrimaryName(get_handle()); + if (str != NULL) + m_definition_name = str; + + str = mti_GetRegionSourceName(get_handle()); + if (str != NULL) + m_definition_file = str; + return GpiObjHdl::initialise(name, fq_name); } diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 3f148cbd..db0d116e 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -418,12 +418,6 @@ const char* gpi_get_definition_file(gpi_sim_hdl sig_hdl) return obj_hdl->get_definition_file(); } -int gpi_get_definition_line(gpi_sim_hdl sig_hdl) -{ - GpiObjHdl *obj_hdl = sim_to_hdl(sig_hdl); - return obj_hdl->get_definition_line(); -} - const char *gpi_get_signal_value_binstr(gpi_sim_hdl sig_hdl) { GpiSignalObjHdl *obj_hdl = sim_to_hdl(sig_hdl); diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index ddec706e..a272edd8 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -138,7 +138,6 @@ class GpiObjHdl : public GpiHdl { virtual const char* get_definition_name() { return m_definition_name.c_str(); }; virtual const char* get_definition_file() { return m_definition_file.c_str(); }; - virtual int get_definition_line() { return m_definition_line; }; bool is_native_impl(GpiImplInterface *impl); virtual int initialise(std::string &name, std::string &full_name); @@ -153,7 +152,6 @@ class GpiObjHdl : public GpiHdl { std::string m_definition_name; std::string m_definition_file; - int m_definition_line; gpi_objtype_t m_type; bool m_const; diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index e7a3f186..bd7bf313 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -726,28 +726,6 @@ static PyObject *get_definition_file(PyObject *self, PyObject *args) return retstr; } -static PyObject *get_definition_line(PyObject *self, PyObject *args) -{ - int result; - gpi_sim_hdl hdl; - PyObject *retval; - - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - - if (!PyArg_ParseTuple(args, "l", &hdl)) { - DROP_GIL(gstate); - return NULL; - } - - result = gpi_get_definition_line((gpi_sim_hdl)hdl); - retval = Py_BuildValue("l", result); - - DROP_GIL(gstate); - - return retval; -} - static PyObject *get_handle_by_name(PyObject *self, PyObject *args) { const char *name; diff --git a/lib/simulator/simulatormodule.h b/lib/simulator/simulatormodule.h index 60257126..2098b622 100644 --- a/lib/simulator/simulatormodule.h +++ b/lib/simulator/simulatormodule.h @@ -66,7 +66,6 @@ static PyObject *set_signal_val_real(PyObject *self, PyObject *args); static PyObject *set_signal_val_str(PyObject *self, PyObject *args); static PyObject *get_definition_name(PyObject *self, PyObject *args); static PyObject *get_definition_file(PyObject *self, PyObject *args); -static PyObject *get_definition_line(PyObject *self, PyObject *args); static PyObject *get_handle_by_name(PyObject *self, PyObject *args); static PyObject *get_handle_by_index(PyObject *self, PyObject *args); static PyObject *get_root_handle(PyObject *self, PyObject *args); @@ -103,7 +102,6 @@ static PyMethodDef SimulatorMethods[] = { {"set_signal_val_real", set_signal_val_real, METH_VARARGS, "Set the value of a signal using a double precision float"}, {"get_definition_name", get_definition_name, METH_VARARGS, "Get the name of a GPI object's definition"}, {"get_definition_file", get_definition_file, METH_VARARGS, "Get the file that sources the object's definition"}, - {"get_definition_line", get_definition_line, METH_VARARGS, "Get the line number that sources the object's definition"}, {"get_handle_by_name", get_handle_by_name, METH_VARARGS, "Get handle of a named object"}, {"get_handle_by_index", get_handle_by_index, METH_VARARGS, "Get handle of a object at an index in a parent"}, {"get_root_handle", get_root_handle, METH_VARARGS, "Get the root handle"}, diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index b8279559..b6945065 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -186,6 +186,28 @@ int VhpiArrayObjHdl::initialise(std::string &name, std::string &fq_name) { return GpiObjHdl::initialise(name, fq_name); } +int VhpiObjHdl::initialise(std::string &name, std::string &fq_name) { + vhpiHandleT handle = GpiObjHdl::get_handle(); + if (handle != NULL) { + vhpiHandleT du_handle = vhpi_handle(vhpiDesignUnit, handle); + if (du_handle != NULL) { + vhpiHandleT pu_handle = vhpi_handle(vhpiPrimaryUnit, du_handle); + if (pu_handle != NULL) { + const char * str; + str = vhpi_get_str(vhpiNameP, pu_handle); + if (str != NULL) + m_definition_name = str; + + str = vhpi_get_str(vhpiFileNameP, pu_handle); + if (str != NULL) + m_definition_file = str; + } + } + } + + return GpiObjHdl::initialise(name, fq_name); +} + int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { // Determine the type of object, either scalar or vector m_value.format = vhpiObjTypeVal; diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index 1cfc4420..ce92d3cc 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -395,7 +395,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, } else if (gpi_type == GPI_ARRAY) { new_obj = new VhpiArrayObjHdl(this, new_hdl, gpi_type); } else { - new_obj = new GpiObjHdl(this, new_hdl, gpi_type); + new_obj = new VhpiObjHdl(this, new_hdl, gpi_type); } if (new_obj->initialise(name, fq_name)) { diff --git a/lib/vhpi/VhpiImpl.h b/lib/vhpi/VhpiImpl.h index b8967075..f5660ac8 100644 --- a/lib/vhpi/VhpiImpl.h +++ b/lib/vhpi/VhpiImpl.h @@ -168,6 +168,16 @@ class VhpiArrayObjHdl : public GpiObjHdl { int initialise(std::string &name, std::string &fq_name); }; +class VhpiObjHdl : public GpiObjHdl { +public: + VhpiObjHdl(GpiImplInterface *impl, + vhpiHandleT hdl, + gpi_objtype_t objtype) : GpiObjHdl(impl, hdl, objtype) { } + virtual ~VhpiObjHdl() { } + + int initialise(std::string &name, std::string &fq_name); +}; + class VhpiSignalObjHdl : public GpiSignalObjHdl { public: VhpiSignalObjHdl(GpiImplInterface *impl, diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index bde7c1e5..89d52518 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -215,8 +215,6 @@ int VpiObjHdl::initialise(std::string &name, std::string &fq_name) { if (str != NULL) m_definition_file = str; - m_definition_line = vpi_get(vpiDefLineNo, hdl); - return GpiObjHdl::initialise(name, fq_name); } From 21ba379e7c7b135861cef7a104bf8c4584d22765 Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 7 Jun 2017 15:39:02 -0500 Subject: [PATCH 1315/2656] Update AvalonST monitor to not require 'empty' when using one data symbol --- cocotb/monitors/avalon.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index 97882936..7e9fd90a 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -70,8 +70,8 @@ class AvalonSTPkts(BusMonitor): """ Packetised AvalonST bus """ - _signals = ["valid", "data", "startofpacket", "endofpacket", "empty"] - _optional_signals = ["error", "channel", "ready"] + _signals = ["valid", "data", "startofpacket", "endofpacket"] + _optional_signals = ["error", "channel", "ready", "empty"] _default_config = { "dataBitsPerSymbol" : 8, @@ -91,6 +91,15 @@ def __init__(self, *args, **kwargs): self.log.debug("Setting config option %s to %s" % (configoption, str(value))) + num_data_symbols = (len(self.bus.data) / + self.config["dataBitsPerSymbol"]) + if (num_data_symbols > 1 and not hasattr(self.bus, 'empty')): + raise AttributeError( + "%s has %i data symbols, but contains no object named empty" % + (self.name, num_data_symbols)) + + self.config["useEmpty"] = (num_data_symbols > 1) + @coroutine def _monitor_recv(self): """Watch the pins and reconstruct transactions""" @@ -132,7 +141,8 @@ def valid(): if self.bus.endofpacket.value: # Truncate the empty bits - if self.bus.empty.value.integer: + if (self.config["useEmpty"] and + self.bus.empty.value.integer): pkt = pkt[:-self.bus.empty.value.integer] self.log.info("Received a packet of %d bytes" % len(pkt)) self.log.debug(hexdump(str((pkt)))) From 9937f767deccc66704a9481abbdaa2d7c77b2d19 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 9 Jun 2017 11:19:33 +0100 Subject: [PATCH 1316/2656] Fix #91 : add timeout for AvalonSTPkt monitor AvalonSTPkt has now invalidTimeout config which is off by default. The monitor will raise an error in the amount of continuous invalid cycles within a packet is larger than invalidTimeout. --- cocotb/monitors/avalon.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index 7e9fd90a..e6916453 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -77,7 +77,8 @@ class AvalonSTPkts(BusMonitor): "dataBitsPerSymbol" : 8, "firstSymbolInHighOrderBits" : True, "maxChannel" : 0, - "readyLatency" : 0 + "readyLatency" : 0, + "invalidTimeout" : 0, } def __init__(self, *args, **kwargs): @@ -109,6 +110,7 @@ def _monitor_recv(self): rdonly = ReadOnly() pkt = "" in_pkt = False + invalid_cyclecount = 0 def valid(): if hasattr(self.bus, 'ready'): @@ -123,6 +125,8 @@ def valid(): continue if valid(): + invalid_cyclecount = 0 + if self.bus.startofpacket.value: if pkt: raise AvalonProtocolError( @@ -149,3 +153,11 @@ def valid(): self._recv(pkt) pkt = "" in_pkt = False + else : + if in_pkt : + invalid_cyclecount += 1 + if self.config["invalidTimeout"] : + if invalid_cyclecount >= self.config["invalidTimeout"] : + raise AvalonProtocolError( + "In-Packet Timeout. Didn't receive any valid data for %d cycles!" % + invalid_cyclecount) From 3dd57738888d8cbbdc6b3df088fda36d63316c31 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Mon, 12 Jun 2017 09:07:02 +0200 Subject: [PATCH 1317/2656] adding the ability to trace nvc callback --- makefiles/simulators/Makefile.nvc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.nvc b/makefiles/simulators/Makefile.nvc index f5d3c4b1..e4592d5a 100644 --- a/makefiles/simulators/Makefile.nvc +++ b/makefiles/simulators/Makefile.nvc @@ -8,6 +8,7 @@ clean:: else +#TRACE :=--vhpi-trace CMD_BIN := nvc ifdef NVC_BIN_DIR @@ -39,7 +40,7 @@ results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ LD_LIBRARY_PATH=$(LIB_DIR) \ - $(CMD) --work=$(RTL_LIBRARY) -r --load $(COCOTB_VHPI_LIB) $(TOPLEVEL) + $(CMD) --work=$(RTL_LIBRARY) -r --load $(COCOTB_VHPI_LIB) $(TRACE) $(TOPLEVEL) clean:: -@rm -rf $(SIM_BUILD) From 61bc60660f518b7390234660a4079c3e95d6bc0c Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Mon, 19 Jun 2017 10:49:13 +0200 Subject: [PATCH 1318/2656] documentation: adding GHDL as supported simulator --- documentation/source/introduction.rst | 1 + documentation/source/simulator_support.rst | 2 ++ 2 files changed, 3 insertions(+) diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index 31889985..36485601 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -14,6 +14,7 @@ What is cocotb? Linux Platforms * `Icarus Verilog `_ +* `GHDL `_ * `Aldec `_ Riviera-PRO * `Synopsys `_ VCS * `Cadence `_ Incisive diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 479776d1..36f80182 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -28,3 +28,5 @@ Mentor Questa Cadence Incisive ---------------- +GHDL +---- From 004a39b23adea720fb2451a6eeac655d3af65a98 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Mon, 19 Jun 2017 10:54:51 +0200 Subject: [PATCH 1319/2656] documentation:adding environment variable COCOTB_REDUCED_LOG_FMT --- documentation/source/building.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/documentation/source/building.rst b/documentation/source/building.rst index 4755cf75..8648b6db 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -116,6 +116,11 @@ COCOTB_ANSI_OUTPUT=1 forces output to be ANSI regardless of the type stdout COCOTB_ANSI_OUTPUT=0 supresses the ANSI output in the log messages +COCOTB_REDUCED_LOG_FMT +---------------------- + +If defined, log lines displayed in terminal will be shorter. It will print only +time, message type (INFO, WARNING, ERROR) and log message. MODULE ------ From ad3dd5d190425bbbb647725450d3f1f544c4f68b Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 19 Jun 2017 08:44:21 -0500 Subject: [PATCH 1320/2656] Issue #93: Use child class to get defalt args if present --- cocotb/monitors/avalon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index 7e9fd90a..06dbe757 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -84,7 +84,7 @@ def __init__(self, *args, **kwargs): config = kwargs.pop('config', {}) BusMonitor.__init__(self, *args, **kwargs) - self.config = AvalonSTPkts._default_config.copy() + self.config = self._default_config.copy() for configoption, value in config.items(): self.config[configoption] = value From 6c550dbb5c3c956f7897c316b0e17c47912963d7 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 19 Jun 2017 08:44:21 -0500 Subject: [PATCH 1321/2656] Issue #93: Use child class to get defalt args if present --- cocotb/monitors/avalon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index e6916453..b00f00b5 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -85,7 +85,7 @@ def __init__(self, *args, **kwargs): config = kwargs.pop('config', {}) BusMonitor.__init__(self, *args, **kwargs) - self.config = AvalonSTPkts._default_config.copy() + self.config = self._default_config.copy() for configoption, value in config.items(): self.config[configoption] = value From e50fe1448c12453a13b415074d6a28e1b3e358f6 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 19 Jun 2017 16:17:55 -0500 Subject: [PATCH 1322/2656] implement clog2; Closes #95 --- cocotb/binary.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 7a40b1e6..0492fd2b 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -28,7 +28,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' from __future__ import print_function -from math import log, ceil from cocotb.utils import get_python_integer_types import os @@ -54,6 +53,16 @@ def resolve(string): return string +def _clog2(val): + if val < 0: + raise ValueError("_clog2 can't take a negative") + exp = 0 + while True: + if (1 << exp) >= val: + return exp + exp += 1 + + class BinaryRepresentation(): UNSIGNED = 0 # noqa SIGNED_MAGNITUDE = 1 # noqa @@ -153,8 +162,7 @@ def _convert_to_signed_mag(self, x): def _convert_to_twos_comp(self, x): if x < 0: - ceil_log2 = int(ceil(log(abs(x), 2))) - binstr = bin(2 ** (ceil_log2 + 1) + x)[2:] + binstr = bin(2 ** (_clog2(abs(x)) + 1) + x)[2:] binstr = self._adjust_twos_comp(binstr) else: binstr = self._adjust_twos_comp('0' + bin(x)[2:]) From 8a741abd320e3c659be68d07f869fc819cbd5a7c Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 30 Jun 2017 09:18:00 -0500 Subject: [PATCH 1323/2656] write the message attribute for testcase failures; Closes #97 --- cocotb/regression.py | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 54de1f54..490d5cd9 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -214,6 +214,12 @@ def next_test(self): return None return self._queue.pop(0) + def _add_failure(self, result): + self.xunit.add_failure(stdout=repr(str(result)), + stderr="\n".join(self._running_test.error_messages), + message="Test failed with random_seed={}".format(self._seed)) + self.failures += 1 + def handle_result(self, result): """Handle a test result @@ -253,19 +259,13 @@ def _result_was(): self._running_test.expect_error): self.log.error("Test passed but we expected an error: " + _result_was()) - self.xunit.add_failure(stdout=repr(str(result)), - stderr="\n".join( - self._running_test.error_messages)) - self.failures += 1 + self._add_failure(result) result_pass = False elif isinstance(result, TestSuccess): self.log.error("Test passed but we expected a failure: " + _result_was()) - self.xunit.add_failure(stdout=repr(str(result)), - stderr="\n".join( - self._running_test.error_messages)) - self.failures += 1 + self._add_failure(result) result_pass = False elif isinstance(result, TestError) and self._running_test.expect_error: @@ -277,20 +277,14 @@ def _result_was(): else: self.log.error("Test error has lead to simulator shuttting us " "down") - self.xunit.add_failure(stdout=repr(str(result)), - stderr="\n".join( - self._running_test.error_messages)) - self.failures += 1 + self._add_failure(result) self._store_test_result(self._running_test.module, self._running_test.funcname, False, sim_time_ns, real_time, ratio_time) self.tear_down() return else: self.log.error("Test Failed: " + _result_was()) - self.xunit.add_failure(stdout=repr(str(result)), - stderr="\n".join( - self._running_test.error_messages)) - self.failures += 1 + self._add_failure(result) result_pass = False self._store_test_result(self._running_test.module, self._running_test.funcname, result_pass, sim_time_ns, real_time, ratio_time) From e435f4dca8355b4304ac42ed362430ed0c19809e Mon Sep 17 00:00:00 2001 From: Ben Rosser Date: Fri, 30 Jun 2017 17:51:28 +0200 Subject: [PATCH 1324/2656] Add labels to all str.format calls to fix Python 2.6 build In Python 2.6 and below, a label (i.e. 0, 1, etc.) is necessary in calls to str.format(). That is, {} is not valid in 2.6.x; instead, it needs to be {0}. There were a handful of places in the codebase using format() without such labels. This commit adds the relevant labels. The cocotb documentation claims Python 2.6+ is still supported. Without this change, when ran against Python 2.6, a "Called callback returned null" message will be generated at the end of simulation, when cocotb tries to print out the number of failed tests and the consumed simulation time, In particular, RHEL/SL/CentOS 6 is sadly still using Python 2.6, so this change makes cocotb work with Python out-of-the-box on those distros. --- cocotb/handle.py | 6 +++--- cocotb/regression.py | 8 ++++---- cocotb/scheduler.py | 2 +- cocotb/utils.py | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index f3f70bcd..020d4d23 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -311,11 +311,11 @@ def _sub_handle_key(self, name): # VHPI(ALDEC): _name__X where X is the index # VPI: _name[X] where X is the index import re - result = re.match("{}__(?P\d+)$".format(self._name), name) + result = re.match("{0}__(?P\d+)$".format(self._name), name) if not result: - result = re.match("{}\((?P\d+)\)$".format(self._name), name) + result = re.match("{0}\((?P\d+)\)$".format(self._name), name) if not result: - result = re.match("{}\[(?P\d+)\]$".format(self._name), name) + result = re.match("{0}\[(?P\d+)\]$".format(self._name), name) if result: return int(result.group("index")) diff --git a/cocotb/regression.py b/cocotb/regression.py index 8f87ecc0..183191b0 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -348,11 +348,11 @@ def _log_sim_summary(self): summary = "" summary += "*************************************************************************************\n" - summary += "** ERRORS : {:<39}**\n".format(self.failures) + summary += "** ERRORS : {0:<39}**\n".format(self.failures) summary += "*************************************************************************************\n" - summary += "** SIM TIME : {:<39}**\n".format('{:.2f} NS'.format(sim_time_ns)) - summary += "** REAL TIME : {:<39}**\n".format('{:.2f} S'.format(real_time)) - summary += "** SIM / REAL TIME : {:<39}**\n".format('{:.2f} NS/S'.format(ratio_time)) + summary += "** SIM TIME : {0:<39}**\n".format('{0:.2f} NS'.format(sim_time_ns)) + summary += "** REAL TIME : {0:<39}**\n".format('{0:.2f} S'.format(real_time)) + summary += "** SIM / REAL TIME : {0:<39}**\n".format('{0:.2f} NS/S'.format(ratio_time)) summary += "*************************************************************************************\n" self.log.info(summary) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index aaefef16..a5e48635 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -376,7 +376,7 @@ def unschedule(self, coro): def save_write(self, handle, value): if self._mode == Scheduler._MODE_READONLY: - raise Exception("Write to object {} was scheduled during a read-only sync phase.".format(handle._name)) + raise Exception("Write to object {0} was scheduled during a read-only sync phase.".format(handle._name)) self._writes[handle] = value def _coroutine_yielded(self, coro, triggers): diff --git a/cocotb/utils.py b/cocotb/utils.py index e835f209..d7d0fa45 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -127,7 +127,7 @@ def _get_log_time_scale(units): units_lwr = units.lower() if units_lwr not in scale: - raise ValueError("Invalid unit ({}) provided".format(units)) + raise ValueError("Invalid unit ({0}) provided".format(units)) else: return scale[units_lwr] From 69d1eaf1210bbe2d3f7a19ded513ea55d8702ec8 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Tue, 4 Jul 2017 12:05:06 +0200 Subject: [PATCH 1325/2656] Use env var to enable nvc traces --- makefiles/simulators/Makefile.nvc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.nvc b/makefiles/simulators/Makefile.nvc index e4592d5a..ee66982e 100644 --- a/makefiles/simulators/Makefile.nvc +++ b/makefiles/simulators/Makefile.nvc @@ -8,7 +8,10 @@ clean:: else -#TRACE :=--vhpi-trace +ifeq ($(COCOTB_NVC_TRACE), 1) + TRACE :=--vhpi-trace +endif + CMD_BIN := nvc ifdef NVC_BIN_DIR From 40cfcf00878cc34a150f048eeb3bd6de7e4a318c Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Thu, 6 Jul 2017 11:37:35 +0200 Subject: [PATCH 1326/2656] documentation: document COCOTB_NVC_TRACE env variable --- documentation/source/building.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/documentation/source/building.rst b/documentation/source/building.rst index 4755cf75..d15f86fa 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -76,6 +76,10 @@ CUSTOM_SIM_DEPS Use to add additional dependencies to the simulation target. +COCOTB_NVC_TRACE +~~~~~~~~~~~~~~~~ + +Set this to 1 to enable display VHPI trace when using nvc VHDL simulator. Environment Variables ===================== From 7942b0a65f30164783e5ab666775ae2ce9310f11 Mon Sep 17 00:00:00 2001 From: AlexanderSpirin Date: Mon, 10 Jul 2017 17:56:03 +0300 Subject: [PATCH 1327/2656] Fix memory leak in Scheduler._trigger2coros --- cocotb/scheduler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index aaefef16..12e186ca 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -366,6 +366,7 @@ def unschedule(self, coro): self._trigger2coros[trigger].remove(coro) if not self._trigger2coros[trigger]: trigger.unprime() + del self._trigger2coros[trigger] del self._coro2triggers[coro] if coro._join in self._trigger2coros: From 041704b4a9ea2a974d465a0cb42e89c9e3e82a17 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 11 Jul 2017 05:41:30 -0500 Subject: [PATCH 1328/2656] Fixes #99 - make AvalonStPkts respect valid signals provided in AvalonSTWord if present --- cocotb/drivers/avalon.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 28c0ccb1..8f908642 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -662,13 +662,17 @@ def _send_iterable(self, pkt, sync=True): if self.on is not True and self.on: self.on -= 1 - self.bus <= word - self.bus.valid <= 1 + if not hasattr(word, "valid"): + self.bus.valid <= 1 + else: + self.bus <= word # If this is a bus with a ready signal, wait for this word to # be acknowledged if hasattr(self.bus, "ready"): - yield self._wait_ready() + yield ReadOnly() + if (self.bus.valid): + yield self._wait_ready() yield clkedge self.bus.valid <= 0 From b14d9c3fd460bcaa031d46a81fa1e3232ca787a8 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Wed, 19 Jul 2017 08:50:51 +0200 Subject: [PATCH 1329/2656] Avalon.py: FallingEdge trigger was not declared --- cocotb/drivers/avalon.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index f0a1b1a6..84f44baf 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -35,7 +35,8 @@ import cocotb from cocotb.decorators import coroutine -from cocotb.triggers import RisingEdge, ReadOnly, NextTimeStep, Event +from cocotb.triggers import RisingEdge, FallingEdge +from cocotb.triggers import ReadOnly, NextTimeStep, Event from cocotb.drivers import BusDriver, ValidatedBusDriver from cocotb.utils import hexdump from cocotb.binary import BinaryValue From b1c1c68cbae571c6c3348a57bc032dd2b1d22acc Mon Sep 17 00:00:00 2001 From: AlexanderSpirin Date: Wed, 26 Jul 2017 17:49:18 +0300 Subject: [PATCH 1330/2656] Prevent _trigger2coros change during iteration in Python3. We must iterate over copy of _trigger2coros in Scheduler.cleanup(), because in called Scheduler.unschedule() dictionary change. --- cocotb/scheduler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 43c39e24..10147688 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -542,7 +542,7 @@ def cleanup(self): Unprime all pending triggers and kill off any coroutines """ - for trigger, waiting in self._trigger2coros.items(): + for trigger, waiting in dict(self._trigger2coros).items(): for coro in waiting: if _debug: self.log.debug("Killing %s" % str(coro)) From 448882a43290a5ea0e57ef9d72a0376757c4a5af Mon Sep 17 00:00:00 2001 From: Ben Rosser Date: Thu, 27 Jul 2017 15:51:35 -0400 Subject: [PATCH 1331/2656] Python 2.6: use sys.version_info[0], not version_info.major This is another Python 2.6 compatibility change. The sys.version_info fields having names was introduced in Python 2.7. Thus attempting to access them by .major, .minor, etc. will cause an exception in 2.6. --- cocotb/decorators.py | 2 +- makefiles/Makefile.pylib.Darwin | 4 ++-- makefiles/Makefile.pylib.Linux | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index aaf669e6..714509d2 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -234,7 +234,7 @@ def __call__(self, *args, **kwargs): except Exception as e: traceback.print_exc() result = TestError(str(e)) - if sys.version_info.major >= 3: + if sys.version_info[0] >= 3: buff = StringIO() traceback.print_exc(file=buff) else: diff --git a/makefiles/Makefile.pylib.Darwin b/makefiles/Makefile.pylib.Darwin index d4598e9d..1df68d5a 100644 --- a/makefiles/Makefile.pylib.Darwin +++ b/makefiles/Makefile.pylib.Darwin @@ -40,8 +40,8 @@ NATIVE_ARCH=$(shell uname -m) ARCH?=$(NATIVE_ARCH) # We might work with other Python versions -PYTHON_MAJOR_VERSION?=$(shell python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") -PYTHON_MINOR_VERSION?=$(shell python -c "from __future__ import print_function; import sys; print(sys.version_info.minor)") +PYTHON_MAJOR_VERSION?=$(shell python -c "from __future__ import print_function; import sys; print(sys.version_info[0])") +PYTHON_MINOR_VERSION?=$(shell python -c "from __future__ import print_function; import sys; print(sys.version_info[1])") PYTHON_VERSION?=$(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') PYTHON_LIBDIR:=$(shell python -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("LIBDIR"))') diff --git a/makefiles/Makefile.pylib.Linux b/makefiles/Makefile.pylib.Linux index 5fa4b6c5..b224b7aa 100644 --- a/makefiles/Makefile.pylib.Linux +++ b/makefiles/Makefile.pylib.Linux @@ -40,8 +40,8 @@ NATIVE_ARCH=$(shell uname -m) ARCH?=$(NATIVE_ARCH) # We might work with other Python versions -PYTHON_MAJOR_VERSION?=$(shell $(PYTHON_BIN) -c "from __future__ import print_function; import sys; print(sys.version_info.major)") -PYTHON_MINOR_VERSION?=$(shell $(PYTHON_BIN) -c "from __future__ import print_function; import sys; print(sys.version_info.minor)") +PYTHON_MAJOR_VERSION?=$(shell $(PYTHON_BIN) -c "from __future__ import print_function; import sys; print(sys.version_info[0])") +PYTHON_MINOR_VERSION?=$(shell $(PYTHON_BIN) -c "from __future__ import print_function; import sys; print(sys.version_info[1])") PYTHON_VERSION?=$(shell $(PYTHON_BIN) -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') PYTHON_LIBDIR:=$(shell $(PYTHON_BIN) -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("LIBDIR"))') From 4252a71a6bf3da8a8fdda58c7fdae08392fc1481 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 31 Jul 2017 10:55:31 -0500 Subject: [PATCH 1332/2656] Issue #19: First tests running again with new external code that does really block simulation time for the calling thread --- cocotb/decorators.py | 86 +++++++++---------- cocotb/scheduler.py | 71 ++++++++++++++- .../test_cases/test_external/test_external.py | 27 ++++++ 3 files changed, 139 insertions(+), 45 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index b7bfe773..0ed02f05 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -29,14 +29,14 @@ import time import logging import traceback -import threading import pdb +import threading from io import StringIO, BytesIO import cocotb from cocotb.log import SimLog -from cocotb.triggers import _Join, PythonTrigger, Timer, Event, NullTrigger +from cocotb.triggers import _Join, PythonTrigger, Timer, Event, NullTrigger, Join from cocotb.result import (TestComplete, TestError, TestFailure, TestSuccess, ReturnValue, raise_error, ExternalException) from cocotb.utils import get_sim_time @@ -302,61 +302,59 @@ def __get__(self, obj, type=None): and standalone functions""" return self.__class__(self._func.__get__(obj, type)) - -@function -def unblock_external(bridge): - yield NullTrigger() - bridge.set_out() - - @public -class test_locker(object): - def __init__(self): - self.in_event = None - self.out_event = Event() - self.result = None - - def set_in(self): - self.in_event.set() - - def set_out(self): - self.out_event.set() - - -def external(func): +class external(object): """Decorator to apply to an external function to enable calling from cocotb This currently creates a new execution context for each function that is call. Scope for this to be streamlined to a queue in future """ + def __init__(self, func): + self._func = func + self._log = SimLog("cocotb.externel.%s" % self._func.__name__, id(self)) - @coroutine - def wrapped(*args, **kwargs): - # Start up the thread, this is done in coroutine context - bridge = test_locker() + def __call__(self, *args, **kwargs): - def execute_external(func, _event): - try: - _event.result = func(*args, **kwargs) - except Exception as e: - _event.result = e - unblock_external(_event) + @coroutine + def wrapper(): + ext = cocotb.scheduler.run_in_executor(self._func, *args, **kwargs) - thread = threading.Thread(group=None, target=execute_external, - name=func.__name__ + "thread", - args=([func, bridge]), kwargs={}) - thread.start() + yield ext.event.wait() - yield bridge.out_event.wait() + self._log.warn("External resuming") - if bridge.result is not None: - if isinstance(bridge.result, Exception): - raise ExternalException(bridge.result) - else: - raise ReturnValue(bridge.result) + if ext.result is not None: + if isinstance(ext.result, Exception): + self._log.warn("Had an exception") + raise ExternalException(ext.result) + else: + self._log.warn("Had a return value") + raise ReturnValue(ext.result) + + return wrapper() - return wrapped +@public +class hook(coroutine): + """Decorator to mark a function as a hook for cocotb + + All hooks are run at the beginning of a cocotb test suite, prior to any + test code being run.""" + def __init__(self): + pass + + def __call__(self, f): + super(hook, self).__init__(f) + + def _wrapped_hook(*args, **kwargs): + try: + return RunningCoroutine(self._func(*args, **kwargs), self) + except Exception as e: + raise raise_error(self, str(e)) + _wrapped_hook.im_hook = True + _wrapped_hook.name = self._func.__name__ + _wrapped_hook.__name__ = self._func.__name__ + return _wrapped_hook @public class test(coroutine): diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index c27dd0d8..f96f6442 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -37,7 +37,9 @@ """ import collections import os +import time import logging +import threading # For autodocumentation don't need the extension modules @@ -65,11 +67,34 @@ import cocotb import cocotb.decorators from cocotb.triggers import (Trigger, GPITrigger, Timer, ReadOnly, - _NextTimeStep, _ReadWrite) + _NextTimeStep, _ReadWrite, Event, NullTrigger) from cocotb.log import SimLog from cocotb.result import (TestComplete, TestError, ReturnValue, raise_error, create_error, ExternalException) +@cocotb.decorators.function +def unblock_external(bridge): + yield NullTrigger() + bridge.set_out() + + +@cocotb.decorators.public +class external_waiter(object): + def __init__(self): + self.result = None + self.thread = None + self.event = Event() + self._log = SimLog("cocotb.externel.thead.%s" % self.thread, id(self)) + + def thread_done(self): + self._log.debug("Thread finished from %s" % (threading.current_thread())) + # trigger the pending co-routine + self.event.set() + + def thread_start(self): + if not self.thread.is_alive(): + self._log.debug("Thread being started by %s" % (threading.current_thread())) + self.thread.start() class Scheduler(object): """ @@ -153,10 +178,12 @@ def __init__(self): self._pending_coros = [] self._pending_callbacks = [] self._pending_triggers = [] + self._pending_threads = [] self._terminate = False self._test_result = None self._entrypoint = None + self._main_thread = threading.current_thread() # Select the appropriate scheduling algorithm for this simulator self.advance = self.default_scheduling_algorithm @@ -403,6 +430,35 @@ def queue(self, coroutine): """Queue a coroutine for execution""" self._pending_coros.append(coroutine) + def run_in_executor(self, func, *args, **kwargs): + """ + Run the corouting in a seperate execution thread + and return a yieldable object for the caller + """ + # Create a thread + # Create a trigger that is called as a result of the thread finishing + # Create an Event object that the caller can yield on + # Event object set when the thread finishes execution, this blocks the + # calling coroutine (but not the thread) until the external completes + + def execute_external(func, _waiter): + try: + _waiter.result = func(*args, **kwargs) + except Exception as e: + _waiter.result = e + _waiter.thread_done() + + waiter = external_waiter() + thread = threading.Thread(group=None, target=execute_external, + name=func.__name__ + "thread", + args=([func, waiter]), kwargs={}) + + waiter.thread = thread; + self._pending_threads.append(waiter) + self.log.info("Added %s to scheduler" % waiter) + + return waiter + def add(self, coroutine): """ Add a new coroutine. @@ -510,6 +566,8 @@ def schedule(self, coroutine, trigger=None): self._coroutine_yielded(coroutine, [new_trigger]) elif isinstance(result, Trigger): + if _debug: + self.log.debug("%s: is instance of Trigger" % result) self._coroutine_yielded(coroutine, [result]) elif (isinstance(result, list) and @@ -527,6 +585,16 @@ def schedule(self, coroutine, trigger=None): except Exception as e: self.finish_test(e) + # We do not return from here until pending threads have completed, but only + # from the main thread, this seems like it could be problematic in cases + # where a sim might change what this thread is. + if self._main_thread is threading.current_thread(): + for ext in self._pending_threads: + ext.thread_start() + self.log.debug("Blocking from %s on %s" % (threading.current_thread(), ext.thread)) + ext.thread.join() + self._pending_threads.remove(ext) + # Handle any newly queued coroutines that need to be scheduled while self._pending_coros: self.add(self._pending_coros.pop(0)) @@ -534,6 +602,7 @@ def schedule(self, coroutine, trigger=None): while self._pending_callbacks: self._pending_callbacks.pop(0)() + def finish_test(self, test_result): """Cache the test result and set the terminate flag""" self.log.debug("finish_test called with %s" % (repr(test_result))) diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index 27fd0bd6..bf3fbf87 100644 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -41,6 +41,7 @@ from cocotb.triggers import Timer, Join, RisingEdge, ReadOnly, Edge, ReadWrite from cocotb.clock import Clock from cocotb.decorators import external +from cocotb.utils import get_sim_time test_count = 0 g_dut = None @@ -162,6 +163,17 @@ def test_ext_function_return(dut): # time.sleep(0.2) return value +def test_print_sim_time(dut, base_time): + # We are not calling out here so time should not advance + # And should also remain consistent + for _ in range(100): + _t = get_sim_time('ns') + dut._log.info("Time reported = %d", _t) + if _t != base_time: + raise TestFailure("Time reported does not match base_time %f != %f" % + (_t, base_time)) + dut._log.info("external function has ended") + @cocotb.coroutine def clock_monitor(dut): @@ -171,6 +183,21 @@ def clock_monitor(dut): yield Timer(1000) count += 1 +@cocotb.test(expect_fail=False) +def test_time_in_external(dut): + """Test that the simulation time does no advance if the wrapped external + routine does not its self yield""" + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + yield Timer(10, 'ns') + time = get_sim_time('ns') + dut._log.info("Time at start of test = %d" % time) + yield external(test_print_sim_time)(dut, time) + time_now = get_sim_time('ns') + yield Timer(10, 'ns') + + if time != time_now: + raise TestFailure("Time has elapsed over external call") + @cocotb.test(expect_fail=False) def test_ext_call_return(dut): From 3d8a70fd088f8f66f8973a41aa1987b23ccc7e9c Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 31 Jul 2017 11:03:16 -0500 Subject: [PATCH 1333/2656] Issue #19: Change test_external_from_readonly since this was not really what it did and add a new test that does this, all tests that are not skipped now pass again --- tests/test_cases/test_external/test_external.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index bf3fbf87..0b49aa1c 100644 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -233,8 +233,13 @@ def test_external_from_readonly(dut): yield ReadOnly() dut._log.info("In readonly") - value = yield external(test_ext_function_access)(dut) + value = yield external(test_ext_function)(dut) + +@cocotb.test(expect_fail=False, skip=True) +def test_external_that_yields(dut): + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + value = yield external(test_ext_function_access)(dut) @cocotb.test(expect_fail=True, skip=True) def ztest_ext_exit_error(dut): From 34cb6599e8a80c55735a709d4addb1a8b0a58a37 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 10 Aug 2017 08:40:47 -0500 Subject: [PATCH 1334/2656] Issue #19: All external test cases passing --- cocotb/decorators.py | 12 ++- cocotb/scheduler.py | 95 +++++++++++++++++-- .../test_cases/test_external/test_external.py | 3 + 3 files changed, 95 insertions(+), 15 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 0ed02f05..74059f6d 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -288,12 +288,16 @@ def __call__(self, *args, **kwargs): @coroutine def execute_function(self, event): event.result = yield cocotb.coroutine(self._func)(*args, **kwargs) + self.log.warn("Function Back from Yield %s" % threading.current_thread()) event.set() self._event = threading.Event() self._event.result = None - coro = cocotb.scheduler.queue(execute_function(self, self._event)) + waiter = cocotb.scheduler.queue_function(execute_function(self, self._event)) + # This blocks the calling external thread until the coroutine finishes self._event.wait() + waiter.thread_resume() + self.log.warn("Function back from coroutine %s" % threading.current_thread()) return self._event.result @@ -321,14 +325,12 @@ def wrapper(): yield ext.event.wait() - self._log.warn("External resuming") - if ext.result is not None: if isinstance(ext.result, Exception): - self._log.warn("Had an exception") + self._log.warn("External Complete with exception %s" % threading.current_thread()) raise ExternalException(ext.result) else: - self._log.warn("Had a return value") + self._log.warn("External Complete with return value %s" % threading.current_thread()) raise ReturnValue(ext.result) return wrapper() diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index f96f6442..e5fbd177 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -72,30 +72,72 @@ from cocotb.result import (TestComplete, TestError, ReturnValue, raise_error, create_error, ExternalException) -@cocotb.decorators.function -def unblock_external(bridge): - yield NullTrigger() - bridge.set_out() - +class external_state(object): + INIT = 0 + RUNNING = 1 + PAUSED = 2 + EXITED = 3 @cocotb.decorators.public class external_waiter(object): + def __init__(self): self.result = None self.thread = None self.event = Event() + self.state = external_state.INIT + self.cond = threading.Condition() self._log = SimLog("cocotb.externel.thead.%s" % self.thread, id(self)) + def _propogate_state(self, new_state): + """ This can be called from threads other than main so _log is only called from main thread""" + self.cond.acquire() + print("Chainging state from %d -> %d from %s" % (self.state, new_state, threading.current_thread())) + self.state = new_state + self.cond.notify() + self.cond.release() + def thread_done(self): - self._log.debug("Thread finished from %s" % (threading.current_thread())) + self._log.warn("Thread finished from %s" % (threading.current_thread())) # trigger the pending co-routine self.event.set() + self._propogate_state(external_state.EXITED) + + def thread_suspend(self): + """ only called from an external so _log not safe""" + print("%s has yielded so telling main thread to carry on" % (threading.current_thread())) + self._propogate_state(external_state.PAUSED) def thread_start(self): + if self.state > external_state.INIT: + return if not self.thread.is_alive(): - self._log.debug("Thread being started by %s" % (threading.current_thread())) + self._log.info("Thread %s being started by %s" % (self.thread, threading.current_thread())) + self._propogate_state(external_state.RUNNING) self.thread.start() + def thread_resume(self): + self._propogate_state(external_state.RUNNING) + + def thread_wait(self): + self._log.warn("Waiting for the condition lock %s" % threading.current_thread()) + self.cond.acquire() + + while self.state == external_state.RUNNING: + self.cond.wait() + + if self.state == external_state.EXITED: + self._log.warn("Thread %s has exited from %s" % (self.thread, threading.current_thread())) + elif self.state == external_state.PAUSED: + self._log.warn("Thread %s has called yield from %s" % (self.thread, threading.current_thread())) + elif self.state == external_state.RUNNING: + self._log.warn("Thread %s is in RUNNING from %d" % (self.thread, threading.current_thread())) + else: + raise Exception("Thread %s state was not allowed from %s" % (self.thread, threading.current_thread())) + + self.cond.release() + return self.state + class Scheduler(object): """ The main scheduler. @@ -179,6 +221,7 @@ def __init__(self): self._pending_callbacks = [] self._pending_triggers = [] self._pending_threads = [] + self._paused_threads = [] self._terminate = False self._test_result = None @@ -430,6 +473,22 @@ def queue(self, coroutine): """Queue a coroutine for execution""" self._pending_coros.append(coroutine) + def queue_function(self, coroutine): + """ + Queue a coroutine for execution and move the containing thread + so that it does not block execution of the main thread any longer + """ + + # We should be able to find ourselves inside the _pending_threads list + + for t in self._pending_threads: + if t.thread == threading.current_thread(): + self.log.info("Found myself telling main") + t.thread_suspend() + self._pending_coros.append(coroutine) + return t + + def run_in_executor(self, func, *args, **kwargs): """ Run the corouting in a seperate execution thread @@ -444,6 +503,7 @@ def run_in_executor(self, func, *args, **kwargs): def execute_external(func, _waiter): try: _waiter.result = func(*args, **kwargs) + self.log.warn("Execution of external routine done %s" % threading.current_thread()) except Exception as e: _waiter.result = e _waiter.thread_done() @@ -588,12 +648,27 @@ def schedule(self, coroutine, trigger=None): # We do not return from here until pending threads have completed, but only # from the main thread, this seems like it could be problematic in cases # where a sim might change what this thread is. + if self._main_thread is threading.current_thread(): + + #for ext in self._paused_threads: + # self.log.info("Have a paused thread in state %s" % ext.state) + # if external_state.RUNNING == ext.state: + # self._pending_threads.append(ext) + # self._paused_threads.remove(ext) + # #if external_state.EXITED == ext.state: + # # self._paused_threads.remove(ext) + for ext in self._pending_threads: ext.thread_start() - self.log.debug("Blocking from %s on %s" % (threading.current_thread(), ext.thread)) - ext.thread.join() - self._pending_threads.remove(ext) + self.log.info("Blocking from %s on %s" % (threading.current_thread(), ext.thread)) + state = ext.thread_wait() + self.log.info("Back from wait on self %s with newstate %d" % (threading.current_thread(), state)) + #if external_state.PAUSED == state: + # self._paused_threads.append(ext) + #self._pending_threads.remove(ext) + if state == external_state.EXITED: + self._pending_threads.remove(ext) # Handle any newly queued coroutines that need to be scheduled while self._pending_coros: diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index 0b49aa1c..34df5533 100644 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -150,6 +150,9 @@ def test_ext_function(dut): def yield_to_readwrite(dut): yield RisingEdge(dut.clk) dut._log.info("Returning from yield_to_readwrite") + yield RisingEdge(dut.clk) + dut._log.info("Returning from yield_to_readwrite") + yield Timer(1, "ns") raise ReturnValue(2) From b04cc54e33656573ea51815c97f2946c0338763e Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 11 Aug 2017 03:38:43 -0500 Subject: [PATCH 1335/2656] Issue #19: Add test derived from failing real test, was noted that a time was being created by the external thread when it was exiting rather than main --- tests/test_cases/test_external/test_external.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index 34df5533..3bc5b749 100644 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -238,12 +238,22 @@ def test_external_from_readonly(dut): dut._log.info("In readonly") value = yield external(test_ext_function)(dut) -@cocotb.test(expect_fail=False, skip=True) +@cocotb.test(expect_fail=False) def test_external_that_yields(dut): clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) value = yield external(test_ext_function_access)(dut) +@cocotb.test(expect_fail=False) +def test_external_and_continue(dut): + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + + value = yield external(test_ext_function_access)(dut) + + yield Timer(10, "ns") + yield RisingEdge(dut.clk) + + @cocotb.test(expect_fail=True, skip=True) def ztest_ext_exit_error(dut): """Test that a premature exit of the sim at it's request still results in From a63879aeb0c67fd4470d3f9d108de4654794e4ab Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 11 Aug 2017 03:41:18 -0500 Subject: [PATCH 1336/2656] Issue #19: Reduce logging and unblock waiting external yield from main thread rather than external thread --- cocotb/decorators.py | 8 ++++---- cocotb/scheduler.py | 40 ++++++++++++++-------------------------- 2 files changed, 18 insertions(+), 30 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 74059f6d..44b6363e 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -288,7 +288,7 @@ def __call__(self, *args, **kwargs): @coroutine def execute_function(self, event): event.result = yield cocotb.coroutine(self._func)(*args, **kwargs) - self.log.warn("Function Back from Yield %s" % threading.current_thread()) + self.log.debug("Function Back from Yield %s" % threading.current_thread()) event.set() self._event = threading.Event() @@ -297,7 +297,7 @@ def execute_function(self, event): # This blocks the calling external thread until the coroutine finishes self._event.wait() waiter.thread_resume() - self.log.warn("Function back from coroutine %s" % threading.current_thread()) + self.log.debug("Function back from coroutine %s" % threading.current_thread()) return self._event.result @@ -327,10 +327,10 @@ def wrapper(): if ext.result is not None: if isinstance(ext.result, Exception): - self._log.warn("External Complete with exception %s" % threading.current_thread()) + self._log.debug("External Complete with exception %s" % threading.current_thread()) raise ExternalException(ext.result) else: - self._log.warn("External Complete with return value %s" % threading.current_thread()) + self._log.debug("External Complete with return value %s" % threading.current_thread()) raise ReturnValue(ext.result) return wrapper() diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index e5fbd177..00792249 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -92,27 +92,25 @@ def __init__(self): def _propogate_state(self, new_state): """ This can be called from threads other than main so _log is only called from main thread""" self.cond.acquire() - print("Chainging state from %d -> %d from %s" % (self.state, new_state, threading.current_thread())) + #print("Chainging state from %d -> %d from %s" % (self.state, new_state, threading.current_thread())) self.state = new_state self.cond.notify() self.cond.release() def thread_done(self): - self._log.warn("Thread finished from %s" % (threading.current_thread())) - # trigger the pending co-routine - self.event.set() + self._log.debug("Thread finished from %s" % (threading.current_thread())) self._propogate_state(external_state.EXITED) def thread_suspend(self): """ only called from an external so _log not safe""" - print("%s has yielded so telling main thread to carry on" % (threading.current_thread())) + #print("%s has yielded so telling main thread to carry on" % (threading.current_thread())) self._propogate_state(external_state.PAUSED) def thread_start(self): if self.state > external_state.INIT: return if not self.thread.is_alive(): - self._log.info("Thread %s being started by %s" % (self.thread, threading.current_thread())) + self._log.debug("Thread %s being started by %s" % (self.thread, threading.current_thread())) self._propogate_state(external_state.RUNNING) self.thread.start() @@ -120,18 +118,18 @@ def thread_resume(self): self._propogate_state(external_state.RUNNING) def thread_wait(self): - self._log.warn("Waiting for the condition lock %s" % threading.current_thread()) + self._log.debug("Waiting for the condition lock %s" % threading.current_thread()) self.cond.acquire() while self.state == external_state.RUNNING: self.cond.wait() if self.state == external_state.EXITED: - self._log.warn("Thread %s has exited from %s" % (self.thread, threading.current_thread())) + self._log.debug("Thread %s has exited from %s" % (self.thread, threading.current_thread())) elif self.state == external_state.PAUSED: - self._log.warn("Thread %s has called yield from %s" % (self.thread, threading.current_thread())) + self._log.debug("Thread %s has called yield from %s" % (self.thread, threading.current_thread())) elif self.state == external_state.RUNNING: - self._log.warn("Thread %s is in RUNNING from %d" % (self.thread, threading.current_thread())) + self._log.debug("Thread %s is in RUNNING from %d" % (self.thread, threading.current_thread())) else: raise Exception("Thread %s state was not allowed from %s" % (self.thread, threading.current_thread())) @@ -483,7 +481,7 @@ def queue_function(self, coroutine): for t in self._pending_threads: if t.thread == threading.current_thread(): - self.log.info("Found myself telling main") + self.log.debug("Found myself telling main") t.thread_suspend() self._pending_coros.append(coroutine) return t @@ -503,7 +501,7 @@ def run_in_executor(self, func, *args, **kwargs): def execute_external(func, _waiter): try: _waiter.result = func(*args, **kwargs) - self.log.warn("Execution of external routine done %s" % threading.current_thread()) + self.log.debug("Execution of external routine done %s" % threading.current_thread()) except Exception as e: _waiter.result = e _waiter.thread_done() @@ -515,7 +513,7 @@ def execute_external(func, _waiter): waiter.thread = thread; self._pending_threads.append(waiter) - self.log.info("Added %s to scheduler" % waiter) + self.log.debug("Added %s to scheduler" % waiter) return waiter @@ -651,24 +649,14 @@ def schedule(self, coroutine, trigger=None): if self._main_thread is threading.current_thread(): - #for ext in self._paused_threads: - # self.log.info("Have a paused thread in state %s" % ext.state) - # if external_state.RUNNING == ext.state: - # self._pending_threads.append(ext) - # self._paused_threads.remove(ext) - # #if external_state.EXITED == ext.state: - # # self._paused_threads.remove(ext) - for ext in self._pending_threads: ext.thread_start() - self.log.info("Blocking from %s on %s" % (threading.current_thread(), ext.thread)) + self.log.debug("Blocking from %s on %s" % (threading.current_thread(), ext.thread)) state = ext.thread_wait() - self.log.info("Back from wait on self %s with newstate %d" % (threading.current_thread(), state)) - #if external_state.PAUSED == state: - # self._paused_threads.append(ext) - #self._pending_threads.remove(ext) + self.log.debug("Back from wait on self %s with newstate %d" % (threading.current_thread(), state)) if state == external_state.EXITED: self._pending_threads.remove(ext) + ext.event.set() # Handle any newly queued coroutines that need to be scheduled while self._pending_coros: From ef234459f18b0f6ac8c570d053ebde73d812b3e8 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 20 Sep 2016 04:38:33 -0500 Subject: [PATCH 1337/2656] Issue #483: Add test for ClockCycles --- tests/test_cases/test_cocotb/test_cocotb.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index a3b23c59..fd479bf3 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -36,7 +36,7 @@ import cocotb from cocotb.triggers import (Timer, Join, RisingEdge, FallingEdge, Edge, - ReadOnly, ReadWrite) + ReadOnly, ReadWrite, ClockCycles) from cocotb.clock import Clock from cocotb.result import ReturnValue, TestFailure, TestError, TestSuccess from cocotb.utils import get_sim_time @@ -598,6 +598,24 @@ def test_logging_with_args(dut): yield Timer(100) #Make it do something with time +@cocotb.test() +def test_clock_cycles(dut): + """ + Test the ClockCycles Trigger + """ + + clk = dut.clk + + clk_gen = cocotb.fork(Clock(clk, 100).start()) + + yield RisingEdge(clk) + + dut.log.info("After one edge") + + yield ClockCycles(clk, 10) + + dut.log.info("After 10 edges") + @cocotb.test() def test_binary_value(dut): """ From ff653dd5d6a89ced21c2a8aae62573f93eff48bb Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 14 Aug 2017 11:25:58 -0500 Subject: [PATCH 1338/2656] Cleanup: Fixup merge faffage --- cocotb/binary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 48da78b5..2aff070b 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -41,7 +41,7 @@ def resolve(string): for char in BinaryValue._resolve_to_1: string = string.replace(char, "1") for char in BinaryValue._resolve_to_error: - if resolve_x_to == "VALUE_ERROR": + if resolve_x_to == "VALUE_ERROR" and char in string: raise ValueError("Unable to resolve to binary >%s<" % string) elif resolve_x_to == "ZEROS": string = string.replace(char, "0") From 3711bbdffb6c45602f858974130a187c6fcd0c07 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 31 Jul 2017 10:55:31 -0500 Subject: [PATCH 1339/2656] Issue #19: First tests running again with new external code that does really block simulation time for the calling thread --- cocotb/decorators.py | 82 +++++++++---------- cocotb/scheduler.py | 71 +++++++++++++++- .../test_cases/test_external/test_external.py | 27 ++++++ 3 files changed, 135 insertions(+), 45 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 75b57964..6143f6ce 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -29,14 +29,14 @@ import time import logging import traceback -import threading import pdb +import threading from io import StringIO, BytesIO import cocotb from cocotb.log import SimLog -from cocotb.triggers import _Join, PythonTrigger, Timer, Event, NullTrigger +from cocotb.triggers import _Join, PythonTrigger, Timer, Event, NullTrigger, Join from cocotb.result import (TestComplete, TestError, TestFailure, TestSuccess, ReturnValue, raise_error, ExternalException) from cocotb.utils import get_sim_time @@ -302,60 +302,54 @@ def __get__(self, obj, type=None): and standalone functions""" return self.__class__(self._func.__get__(obj, type)) - -@function -def unblock_external(bridge): - yield NullTrigger() - bridge.set_out() - - @public -class test_locker(object): - def __init__(self): - self.in_event = None - self.out_event = Event() - self.result = None - - def set_in(self): - self.in_event.set() - - def set_out(self): - self.out_event.set() - - -def external(func): +class external(object): """Decorator to apply to an external function to enable calling from cocotb This currently creates a new execution context for each function that is call. Scope for this to be streamlined to a queue in future """ + def __init__(self, func): + self._func = func + self._log = SimLog("cocotb.externel.%s" % self._func.__name__, id(self)) - @coroutine - def wrapped(*args, **kwargs): - # Start up the thread, this is done in coroutine context - bridge = test_locker() + def __call__(self, *args, **kwargs): - def execute_external(func, _event): - try: - _event.result = func(*args, **kwargs) - except Exception as e: - _event.result = e - unblock_external(_event) + @coroutine + def wrapper(): + ext = cocotb.scheduler.run_in_executor(self._func, *args, **kwargs) - thread = threading.Thread(group=None, target=execute_external, - name=func.__name__ + "thread", - args=([func, bridge]), kwargs={}) - thread.start() + yield ext.event.wait() - yield bridge.out_event.wait() + self._log.warn("External resuming") - if bridge.result is not None: - if isinstance(bridge.result, Exception): - raise ExternalException(bridge.result) - else: - raise ReturnValue(bridge.result) + if ext.result is not None: + if isinstance(ext.result, Exception): + self._log.warn("Had an exception") + raise ExternalException(ext.result) + else: + self._log.warn("Had a return value") + raise ReturnValue(ext.result) + + return wrapper() - return wrapped +@public +class hook(coroutine): + """Decorator to mark a function as a hook for cocotb + + All hooks are run at the beginning of a cocotb test suite, prior to any + test code being run.""" + def __init__(self): + pass + + def __call__(self, f): + super(hook, self).__init__(f) + + def _wrapped_hook(*args, **kwargs): + try: + return RunningCoroutine(self._func(*args, **kwargs), self) + except Exception as e: + raise raise_error(self, str(e)) @public class hook(coroutine): diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index c27dd0d8..f96f6442 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -37,7 +37,9 @@ """ import collections import os +import time import logging +import threading # For autodocumentation don't need the extension modules @@ -65,11 +67,34 @@ import cocotb import cocotb.decorators from cocotb.triggers import (Trigger, GPITrigger, Timer, ReadOnly, - _NextTimeStep, _ReadWrite) + _NextTimeStep, _ReadWrite, Event, NullTrigger) from cocotb.log import SimLog from cocotb.result import (TestComplete, TestError, ReturnValue, raise_error, create_error, ExternalException) +@cocotb.decorators.function +def unblock_external(bridge): + yield NullTrigger() + bridge.set_out() + + +@cocotb.decorators.public +class external_waiter(object): + def __init__(self): + self.result = None + self.thread = None + self.event = Event() + self._log = SimLog("cocotb.externel.thead.%s" % self.thread, id(self)) + + def thread_done(self): + self._log.debug("Thread finished from %s" % (threading.current_thread())) + # trigger the pending co-routine + self.event.set() + + def thread_start(self): + if not self.thread.is_alive(): + self._log.debug("Thread being started by %s" % (threading.current_thread())) + self.thread.start() class Scheduler(object): """ @@ -153,10 +178,12 @@ def __init__(self): self._pending_coros = [] self._pending_callbacks = [] self._pending_triggers = [] + self._pending_threads = [] self._terminate = False self._test_result = None self._entrypoint = None + self._main_thread = threading.current_thread() # Select the appropriate scheduling algorithm for this simulator self.advance = self.default_scheduling_algorithm @@ -403,6 +430,35 @@ def queue(self, coroutine): """Queue a coroutine for execution""" self._pending_coros.append(coroutine) + def run_in_executor(self, func, *args, **kwargs): + """ + Run the corouting in a seperate execution thread + and return a yieldable object for the caller + """ + # Create a thread + # Create a trigger that is called as a result of the thread finishing + # Create an Event object that the caller can yield on + # Event object set when the thread finishes execution, this blocks the + # calling coroutine (but not the thread) until the external completes + + def execute_external(func, _waiter): + try: + _waiter.result = func(*args, **kwargs) + except Exception as e: + _waiter.result = e + _waiter.thread_done() + + waiter = external_waiter() + thread = threading.Thread(group=None, target=execute_external, + name=func.__name__ + "thread", + args=([func, waiter]), kwargs={}) + + waiter.thread = thread; + self._pending_threads.append(waiter) + self.log.info("Added %s to scheduler" % waiter) + + return waiter + def add(self, coroutine): """ Add a new coroutine. @@ -510,6 +566,8 @@ def schedule(self, coroutine, trigger=None): self._coroutine_yielded(coroutine, [new_trigger]) elif isinstance(result, Trigger): + if _debug: + self.log.debug("%s: is instance of Trigger" % result) self._coroutine_yielded(coroutine, [result]) elif (isinstance(result, list) and @@ -527,6 +585,16 @@ def schedule(self, coroutine, trigger=None): except Exception as e: self.finish_test(e) + # We do not return from here until pending threads have completed, but only + # from the main thread, this seems like it could be problematic in cases + # where a sim might change what this thread is. + if self._main_thread is threading.current_thread(): + for ext in self._pending_threads: + ext.thread_start() + self.log.debug("Blocking from %s on %s" % (threading.current_thread(), ext.thread)) + ext.thread.join() + self._pending_threads.remove(ext) + # Handle any newly queued coroutines that need to be scheduled while self._pending_coros: self.add(self._pending_coros.pop(0)) @@ -534,6 +602,7 @@ def schedule(self, coroutine, trigger=None): while self._pending_callbacks: self._pending_callbacks.pop(0)() + def finish_test(self, test_result): """Cache the test result and set the terminate flag""" self.log.debug("finish_test called with %s" % (repr(test_result))) diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index 27fd0bd6..bf3fbf87 100644 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -41,6 +41,7 @@ from cocotb.triggers import Timer, Join, RisingEdge, ReadOnly, Edge, ReadWrite from cocotb.clock import Clock from cocotb.decorators import external +from cocotb.utils import get_sim_time test_count = 0 g_dut = None @@ -162,6 +163,17 @@ def test_ext_function_return(dut): # time.sleep(0.2) return value +def test_print_sim_time(dut, base_time): + # We are not calling out here so time should not advance + # And should also remain consistent + for _ in range(100): + _t = get_sim_time('ns') + dut._log.info("Time reported = %d", _t) + if _t != base_time: + raise TestFailure("Time reported does not match base_time %f != %f" % + (_t, base_time)) + dut._log.info("external function has ended") + @cocotb.coroutine def clock_monitor(dut): @@ -171,6 +183,21 @@ def clock_monitor(dut): yield Timer(1000) count += 1 +@cocotb.test(expect_fail=False) +def test_time_in_external(dut): + """Test that the simulation time does no advance if the wrapped external + routine does not its self yield""" + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + yield Timer(10, 'ns') + time = get_sim_time('ns') + dut._log.info("Time at start of test = %d" % time) + yield external(test_print_sim_time)(dut, time) + time_now = get_sim_time('ns') + yield Timer(10, 'ns') + + if time != time_now: + raise TestFailure("Time has elapsed over external call") + @cocotb.test(expect_fail=False) def test_ext_call_return(dut): From 35c53419c3ebe73266c1b22b1438ababac410b3c Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 31 Jul 2017 11:03:16 -0500 Subject: [PATCH 1340/2656] Issue #19: Change test_external_from_readonly since this was not really what it did and add a new test that does this, all tests that are not skipped now pass again --- tests/test_cases/test_external/test_external.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index bf3fbf87..0b49aa1c 100644 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -233,8 +233,13 @@ def test_external_from_readonly(dut): yield ReadOnly() dut._log.info("In readonly") - value = yield external(test_ext_function_access)(dut) + value = yield external(test_ext_function)(dut) + +@cocotb.test(expect_fail=False, skip=True) +def test_external_that_yields(dut): + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + value = yield external(test_ext_function_access)(dut) @cocotb.test(expect_fail=True, skip=True) def ztest_ext_exit_error(dut): From ad183e1f333a2f45744730334b45b97c1dd87da7 Mon Sep 17 00:00:00 2001 From: chiggs Date: Thu, 10 Aug 2017 08:40:47 -0500 Subject: [PATCH 1341/2656] Issue #19: All external test cases passing --- cocotb/decorators.py | 12 ++- cocotb/scheduler.py | 95 +++++++++++++++++-- .../test_cases/test_external/test_external.py | 3 + 3 files changed, 95 insertions(+), 15 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 6143f6ce..14dcf843 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -288,12 +288,16 @@ def __call__(self, *args, **kwargs): @coroutine def execute_function(self, event): event.result = yield cocotb.coroutine(self._func)(*args, **kwargs) + self.log.warn("Function Back from Yield %s" % threading.current_thread()) event.set() self._event = threading.Event() self._event.result = None - coro = cocotb.scheduler.queue(execute_function(self, self._event)) + waiter = cocotb.scheduler.queue_function(execute_function(self, self._event)) + # This blocks the calling external thread until the coroutine finishes self._event.wait() + waiter.thread_resume() + self.log.warn("Function back from coroutine %s" % threading.current_thread()) return self._event.result @@ -321,14 +325,12 @@ def wrapper(): yield ext.event.wait() - self._log.warn("External resuming") - if ext.result is not None: if isinstance(ext.result, Exception): - self._log.warn("Had an exception") + self._log.warn("External Complete with exception %s" % threading.current_thread()) raise ExternalException(ext.result) else: - self._log.warn("Had a return value") + self._log.warn("External Complete with return value %s" % threading.current_thread()) raise ReturnValue(ext.result) return wrapper() diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index f96f6442..e5fbd177 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -72,30 +72,72 @@ from cocotb.result import (TestComplete, TestError, ReturnValue, raise_error, create_error, ExternalException) -@cocotb.decorators.function -def unblock_external(bridge): - yield NullTrigger() - bridge.set_out() - +class external_state(object): + INIT = 0 + RUNNING = 1 + PAUSED = 2 + EXITED = 3 @cocotb.decorators.public class external_waiter(object): + def __init__(self): self.result = None self.thread = None self.event = Event() + self.state = external_state.INIT + self.cond = threading.Condition() self._log = SimLog("cocotb.externel.thead.%s" % self.thread, id(self)) + def _propogate_state(self, new_state): + """ This can be called from threads other than main so _log is only called from main thread""" + self.cond.acquire() + print("Chainging state from %d -> %d from %s" % (self.state, new_state, threading.current_thread())) + self.state = new_state + self.cond.notify() + self.cond.release() + def thread_done(self): - self._log.debug("Thread finished from %s" % (threading.current_thread())) + self._log.warn("Thread finished from %s" % (threading.current_thread())) # trigger the pending co-routine self.event.set() + self._propogate_state(external_state.EXITED) + + def thread_suspend(self): + """ only called from an external so _log not safe""" + print("%s has yielded so telling main thread to carry on" % (threading.current_thread())) + self._propogate_state(external_state.PAUSED) def thread_start(self): + if self.state > external_state.INIT: + return if not self.thread.is_alive(): - self._log.debug("Thread being started by %s" % (threading.current_thread())) + self._log.info("Thread %s being started by %s" % (self.thread, threading.current_thread())) + self._propogate_state(external_state.RUNNING) self.thread.start() + def thread_resume(self): + self._propogate_state(external_state.RUNNING) + + def thread_wait(self): + self._log.warn("Waiting for the condition lock %s" % threading.current_thread()) + self.cond.acquire() + + while self.state == external_state.RUNNING: + self.cond.wait() + + if self.state == external_state.EXITED: + self._log.warn("Thread %s has exited from %s" % (self.thread, threading.current_thread())) + elif self.state == external_state.PAUSED: + self._log.warn("Thread %s has called yield from %s" % (self.thread, threading.current_thread())) + elif self.state == external_state.RUNNING: + self._log.warn("Thread %s is in RUNNING from %d" % (self.thread, threading.current_thread())) + else: + raise Exception("Thread %s state was not allowed from %s" % (self.thread, threading.current_thread())) + + self.cond.release() + return self.state + class Scheduler(object): """ The main scheduler. @@ -179,6 +221,7 @@ def __init__(self): self._pending_callbacks = [] self._pending_triggers = [] self._pending_threads = [] + self._paused_threads = [] self._terminate = False self._test_result = None @@ -430,6 +473,22 @@ def queue(self, coroutine): """Queue a coroutine for execution""" self._pending_coros.append(coroutine) + def queue_function(self, coroutine): + """ + Queue a coroutine for execution and move the containing thread + so that it does not block execution of the main thread any longer + """ + + # We should be able to find ourselves inside the _pending_threads list + + for t in self._pending_threads: + if t.thread == threading.current_thread(): + self.log.info("Found myself telling main") + t.thread_suspend() + self._pending_coros.append(coroutine) + return t + + def run_in_executor(self, func, *args, **kwargs): """ Run the corouting in a seperate execution thread @@ -444,6 +503,7 @@ def run_in_executor(self, func, *args, **kwargs): def execute_external(func, _waiter): try: _waiter.result = func(*args, **kwargs) + self.log.warn("Execution of external routine done %s" % threading.current_thread()) except Exception as e: _waiter.result = e _waiter.thread_done() @@ -588,12 +648,27 @@ def schedule(self, coroutine, trigger=None): # We do not return from here until pending threads have completed, but only # from the main thread, this seems like it could be problematic in cases # where a sim might change what this thread is. + if self._main_thread is threading.current_thread(): + + #for ext in self._paused_threads: + # self.log.info("Have a paused thread in state %s" % ext.state) + # if external_state.RUNNING == ext.state: + # self._pending_threads.append(ext) + # self._paused_threads.remove(ext) + # #if external_state.EXITED == ext.state: + # # self._paused_threads.remove(ext) + for ext in self._pending_threads: ext.thread_start() - self.log.debug("Blocking from %s on %s" % (threading.current_thread(), ext.thread)) - ext.thread.join() - self._pending_threads.remove(ext) + self.log.info("Blocking from %s on %s" % (threading.current_thread(), ext.thread)) + state = ext.thread_wait() + self.log.info("Back from wait on self %s with newstate %d" % (threading.current_thread(), state)) + #if external_state.PAUSED == state: + # self._paused_threads.append(ext) + #self._pending_threads.remove(ext) + if state == external_state.EXITED: + self._pending_threads.remove(ext) # Handle any newly queued coroutines that need to be scheduled while self._pending_coros: diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index 0b49aa1c..34df5533 100644 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -150,6 +150,9 @@ def test_ext_function(dut): def yield_to_readwrite(dut): yield RisingEdge(dut.clk) dut._log.info("Returning from yield_to_readwrite") + yield RisingEdge(dut.clk) + dut._log.info("Returning from yield_to_readwrite") + yield Timer(1, "ns") raise ReturnValue(2) From 5a4c8df3de2ad8db1a7e9cc36e5b5629fd24a975 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 11 Aug 2017 03:38:43 -0500 Subject: [PATCH 1342/2656] Issue #19: Add test derived from failing real test, was noted that a time was being created by the external thread when it was exiting rather than main --- tests/test_cases/test_external/test_external.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index 34df5533..3bc5b749 100644 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -238,12 +238,22 @@ def test_external_from_readonly(dut): dut._log.info("In readonly") value = yield external(test_ext_function)(dut) -@cocotb.test(expect_fail=False, skip=True) +@cocotb.test(expect_fail=False) def test_external_that_yields(dut): clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) value = yield external(test_ext_function_access)(dut) +@cocotb.test(expect_fail=False) +def test_external_and_continue(dut): + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + + value = yield external(test_ext_function_access)(dut) + + yield Timer(10, "ns") + yield RisingEdge(dut.clk) + + @cocotb.test(expect_fail=True, skip=True) def ztest_ext_exit_error(dut): """Test that a premature exit of the sim at it's request still results in From 01969fae2757bd842049102eeb4574bbb230b99e Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 11 Aug 2017 03:41:18 -0500 Subject: [PATCH 1343/2656] Issue #19: Reduce logging and unblock waiting external yield from main thread rather than external thread --- cocotb/decorators.py | 8 ++++---- cocotb/scheduler.py | 40 ++++++++++++++-------------------------- 2 files changed, 18 insertions(+), 30 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 14dcf843..7a574331 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -288,7 +288,7 @@ def __call__(self, *args, **kwargs): @coroutine def execute_function(self, event): event.result = yield cocotb.coroutine(self._func)(*args, **kwargs) - self.log.warn("Function Back from Yield %s" % threading.current_thread()) + self.log.debug("Function Back from Yield %s" % threading.current_thread()) event.set() self._event = threading.Event() @@ -297,7 +297,7 @@ def execute_function(self, event): # This blocks the calling external thread until the coroutine finishes self._event.wait() waiter.thread_resume() - self.log.warn("Function back from coroutine %s" % threading.current_thread()) + self.log.debug("Function back from coroutine %s" % threading.current_thread()) return self._event.result @@ -327,10 +327,10 @@ def wrapper(): if ext.result is not None: if isinstance(ext.result, Exception): - self._log.warn("External Complete with exception %s" % threading.current_thread()) + self._log.debug("External Complete with exception %s" % threading.current_thread()) raise ExternalException(ext.result) else: - self._log.warn("External Complete with return value %s" % threading.current_thread()) + self._log.debug("External Complete with return value %s" % threading.current_thread()) raise ReturnValue(ext.result) return wrapper() diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index e5fbd177..00792249 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -92,27 +92,25 @@ def __init__(self): def _propogate_state(self, new_state): """ This can be called from threads other than main so _log is only called from main thread""" self.cond.acquire() - print("Chainging state from %d -> %d from %s" % (self.state, new_state, threading.current_thread())) + #print("Chainging state from %d -> %d from %s" % (self.state, new_state, threading.current_thread())) self.state = new_state self.cond.notify() self.cond.release() def thread_done(self): - self._log.warn("Thread finished from %s" % (threading.current_thread())) - # trigger the pending co-routine - self.event.set() + self._log.debug("Thread finished from %s" % (threading.current_thread())) self._propogate_state(external_state.EXITED) def thread_suspend(self): """ only called from an external so _log not safe""" - print("%s has yielded so telling main thread to carry on" % (threading.current_thread())) + #print("%s has yielded so telling main thread to carry on" % (threading.current_thread())) self._propogate_state(external_state.PAUSED) def thread_start(self): if self.state > external_state.INIT: return if not self.thread.is_alive(): - self._log.info("Thread %s being started by %s" % (self.thread, threading.current_thread())) + self._log.debug("Thread %s being started by %s" % (self.thread, threading.current_thread())) self._propogate_state(external_state.RUNNING) self.thread.start() @@ -120,18 +118,18 @@ def thread_resume(self): self._propogate_state(external_state.RUNNING) def thread_wait(self): - self._log.warn("Waiting for the condition lock %s" % threading.current_thread()) + self._log.debug("Waiting for the condition lock %s" % threading.current_thread()) self.cond.acquire() while self.state == external_state.RUNNING: self.cond.wait() if self.state == external_state.EXITED: - self._log.warn("Thread %s has exited from %s" % (self.thread, threading.current_thread())) + self._log.debug("Thread %s has exited from %s" % (self.thread, threading.current_thread())) elif self.state == external_state.PAUSED: - self._log.warn("Thread %s has called yield from %s" % (self.thread, threading.current_thread())) + self._log.debug("Thread %s has called yield from %s" % (self.thread, threading.current_thread())) elif self.state == external_state.RUNNING: - self._log.warn("Thread %s is in RUNNING from %d" % (self.thread, threading.current_thread())) + self._log.debug("Thread %s is in RUNNING from %d" % (self.thread, threading.current_thread())) else: raise Exception("Thread %s state was not allowed from %s" % (self.thread, threading.current_thread())) @@ -483,7 +481,7 @@ def queue_function(self, coroutine): for t in self._pending_threads: if t.thread == threading.current_thread(): - self.log.info("Found myself telling main") + self.log.debug("Found myself telling main") t.thread_suspend() self._pending_coros.append(coroutine) return t @@ -503,7 +501,7 @@ def run_in_executor(self, func, *args, **kwargs): def execute_external(func, _waiter): try: _waiter.result = func(*args, **kwargs) - self.log.warn("Execution of external routine done %s" % threading.current_thread()) + self.log.debug("Execution of external routine done %s" % threading.current_thread()) except Exception as e: _waiter.result = e _waiter.thread_done() @@ -515,7 +513,7 @@ def execute_external(func, _waiter): waiter.thread = thread; self._pending_threads.append(waiter) - self.log.info("Added %s to scheduler" % waiter) + self.log.debug("Added %s to scheduler" % waiter) return waiter @@ -651,24 +649,14 @@ def schedule(self, coroutine, trigger=None): if self._main_thread is threading.current_thread(): - #for ext in self._paused_threads: - # self.log.info("Have a paused thread in state %s" % ext.state) - # if external_state.RUNNING == ext.state: - # self._pending_threads.append(ext) - # self._paused_threads.remove(ext) - # #if external_state.EXITED == ext.state: - # # self._paused_threads.remove(ext) - for ext in self._pending_threads: ext.thread_start() - self.log.info("Blocking from %s on %s" % (threading.current_thread(), ext.thread)) + self.log.debug("Blocking from %s on %s" % (threading.current_thread(), ext.thread)) state = ext.thread_wait() - self.log.info("Back from wait on self %s with newstate %d" % (threading.current_thread(), state)) - #if external_state.PAUSED == state: - # self._paused_threads.append(ext) - #self._pending_threads.remove(ext) + self.log.debug("Back from wait on self %s with newstate %d" % (threading.current_thread(), state)) if state == external_state.EXITED: self._pending_threads.remove(ext) + ext.event.set() # Handle any newly queued coroutines that need to be scheduled while self._pending_coros: From ecab55ada3026d40a51b4ac4f75bb44233bb6991 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 14 Aug 2017 08:11:57 -0500 Subject: [PATCH 1344/2656] Issue #19: Add some more test cases --- tests/test_cases/test_external/test_external.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index 3bc5b749..48d0471a 100644 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -253,6 +253,20 @@ def test_external_and_continue(dut): yield Timer(10, "ns") yield RisingEdge(dut.clk) +@cocotb.coroutine +def run_external(dut): + value = yield external(test_ext_function_access)(dut) + raise ReturnValue(value) + +@cocotb.test(expect_fail=False) +def test_external_from_fork(dut): + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + + coro = cocotb.fork(run_external(dut)) + yield coro.join() + + dut._log.info("Back from join") + @cocotb.test(expect_fail=True, skip=True) def ztest_ext_exit_error(dut): From a513d18a67e4af677f133a57e7ec108584678265 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 14 Aug 2017 08:13:10 -0500 Subject: [PATCH 1345/2656] Issue #19: All simulations now working --- cocotb/scheduler.py | 11 +++++++-- include/cocotb_utils.h | 24 +++++++++++++++++++ lib/embed/gpi_embed.c | 8 +++++++ lib/simulator/simulatormodule.c | 42 +++++++++++++++++++++++++-------- 4 files changed, 73 insertions(+), 12 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 00792249..4343e923 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -219,7 +219,6 @@ def __init__(self): self._pending_callbacks = [] self._pending_triggers = [] self._pending_threads = [] - self._paused_threads = [] self._terminate = False self._test_result = None @@ -684,10 +683,18 @@ def cleanup(self): """ Clear up all our state - Unprime all pending triggers and kill off any coroutines + Unprime all pending triggers and kill off any coroutines stop all externals """ for trigger, waiting in self._trigger2coros.items(): for coro in waiting: if _debug: self.log.debug("Killing %s" % str(coro)) coro.kill() + + if self._main_thread is not threading.current_thread(): + raise Exception("Cleanup() called outside of the main thread") + + for ext in self._pending_threads: + self.log.info("Waiting for %s to exit", ext.thread) + + diff --git a/include/cocotb_utils.h b/include/cocotb_utils.h index 6494307e..c1e20246 100644 --- a/include/cocotb_utils.h +++ b/include/cocotb_utils.h @@ -34,12 +34,36 @@ extern "C" { #endif +#include +#include + #define xstr(a) str(a) #define str(a) #a extern void* utils_dyn_open(const char* lib_name); extern void* utils_dyn_sym(void *handle, const char* sym_name); +extern int context; + +void to_python(void) { + if (context) { + fprintf(stderr, "FATAL: We are calling up again\n"); + exit(1); + } + ++context; + //fprintf(stderr, "INFO: Calling up to python %d\n", context); +} + +void to_simulator(void) { + if (!context) { + fprintf(stderr, "FATAL: We have returned twice from python\n"); + exit(1); + } + + --context; + //fprintf(stderr, "INFO: Returning back to simulator %d\n", context); +} + #ifdef __cplusplus } #endif diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 417e28ef..d17ac412 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -50,6 +50,8 @@ static char *argv[] = { progname }; static PyObject *pEventFn = NULL; +// Tracks if we are in the context of Python or Simulator +int context = 0; /** * @name Initialise the python interpreter @@ -82,6 +84,7 @@ void embed_init_python(void) fprintf(stderr, "Failed to find python lib\n"); } + to_python(); Py_SetProgramName(progname); Py_Initialize(); /* Initialize the interpreter */ PySys_SetArgvEx(1, argv, 0); @@ -89,6 +92,7 @@ void embed_init_python(void) /* Swap out and return current thread state and release the GIL */ gtstate = PyEval_SaveThread(); + to_simulator(); /* Before returning we check if the user wants pause the simulator thread such that they can attach */ @@ -183,6 +187,7 @@ int embed_sim_init(gpi_sim_info_t *info) //Ensure that the current thread is ready to callthe Python C API PyGILState_STATE gstate = PyGILState_Ensure(); + to_python(); if (get_module_ref(COCOTB_MODULE, &cocotb_module)) goto cleanup; @@ -316,6 +321,7 @@ int embed_sim_init(gpi_sim_info_t *info) Py_DECREF(arg_dict); } PyGILState_Release(gstate); + to_simulator(); return ret; } @@ -327,6 +333,7 @@ void embed_sim_event(gpi_event_t level, const char *msg) if (pEventFn) { PyGILState_STATE gstate; + to_python(); gstate = PyGILState_Ensure(); PyObject *fArgs = PyTuple_New(2); @@ -343,6 +350,7 @@ void embed_sim_event(gpi_event_t level, const char *msg) Py_DECREF(fArgs); PyGILState_Release(gstate); + to_simulator(); } FEXIT diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index bd7bf313..101d635b 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -38,6 +38,7 @@ static int takes = 0; static int releases = 0; #include "simulatormodule.h" +#include typedef int (*gpi_function_t)(const void *); @@ -54,6 +55,13 @@ void DROP_GIL(PyGILState_STATE state) releases++; } +struct sim_time { + uint32_t high; + uint32_t low; +}; + +static struct sim_time cache_time; + /** * @name Callback Handling * @brief Handle a callback coming from GPI @@ -81,14 +89,19 @@ void DROP_GIL(PyGILState_STATE state) */ int handle_gpi_callback(void *user_data) { + int ret = 0; + to_python(); p_callback_data callback_data_p = (p_callback_data)user_data; if (callback_data_p->id_value != COCOTB_ACTIVE_ID) { fprintf(stderr, "Userdata corrupted!\n"); - return 1; + ret = 1; + goto err; } callback_data_p->id_value = COCOTB_INACTIVE_ID; + /* Cache the sim time */ + gpi_get_sim_time(&cache_time.high, &cache_time.low); PyGILState_STATE gstate; gstate = TAKE_GIL(); @@ -97,8 +110,8 @@ int handle_gpi_callback(void *user_data) if (!PyCallable_Check(callback_data_p->function)) { fprintf(stderr, "Callback fired but function isn't callable?!\n"); - DROP_GIL(gstate); - return 1; + ret = 1; + goto out; } // Call the callback @@ -112,7 +125,8 @@ int handle_gpi_callback(void *user_data) fprintf(stderr, "ERROR: called callback function returned NULL\n"); fprintf(stderr, "Failed to execute callback\n"); gpi_sim_end(); - return 0; + ret = 0; + goto out; } // Free up our mess @@ -127,9 +141,12 @@ int handle_gpi_callback(void *user_data) free(callback_data_p); } +out: DROP_GIL(gstate); - return 0; +err: + to_simulator(); + return ret; } static PyObject *log_msg(PyObject *self, PyObject *args) @@ -897,16 +914,21 @@ static PyObject *get_type_string(PyObject *self, PyObject *args) // log messages with the current simulation time static PyObject *get_sim_time(PyObject *self, PyObject *args) { - uint32_t high, low; + struct sim_time local_time; PyGILState_STATE gstate; - gstate = TAKE_GIL(); - gpi_get_sim_time(&high, &low); + if (context) { + gpi_get_sim_time(&local_time.high, &local_time.low); + } else { + local_time = cache_time; + } + + gstate = TAKE_GIL(); PyObject *pTuple = PyTuple_New(2); - PyTuple_SetItem(pTuple, 0, PyLong_FromUnsignedLong(high)); // Note: This function “steals†a reference to o. - PyTuple_SetItem(pTuple, 1, PyLong_FromUnsignedLong(low)); // Note: This function “steals†a reference to o. + PyTuple_SetItem(pTuple, 0, PyLong_FromUnsignedLong(local_time.high)); // Note: This function “steals†a reference to o. + PyTuple_SetItem(pTuple, 1, PyLong_FromUnsignedLong(local_time.low)); // Note: This function “steals†a reference to o. DROP_GIL(gstate); From 3f4b170db7ba4dedc0a1247d14cdf74f797f8269 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 14 Aug 2017 08:18:07 -0500 Subject: [PATCH 1346/2656] Issue #19: Move debug under the _debug flag set via COCOTB_SCHEDULR_DEBUG=1 --- cocotb/decorators.py | 4 ---- cocotb/scheduler.py | 39 +++++++++++++++++++++++---------------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 7a574331..437abd6e 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -288,7 +288,6 @@ def __call__(self, *args, **kwargs): @coroutine def execute_function(self, event): event.result = yield cocotb.coroutine(self._func)(*args, **kwargs) - self.log.debug("Function Back from Yield %s" % threading.current_thread()) event.set() self._event = threading.Event() @@ -297,7 +296,6 @@ def execute_function(self, event): # This blocks the calling external thread until the coroutine finishes self._event.wait() waiter.thread_resume() - self.log.debug("Function back from coroutine %s" % threading.current_thread()) return self._event.result @@ -327,10 +325,8 @@ def wrapper(): if ext.result is not None: if isinstance(ext.result, Exception): - self._log.debug("External Complete with exception %s" % threading.current_thread()) raise ExternalException(ext.result) else: - self._log.debug("External Complete with return value %s" % threading.current_thread()) raise ReturnValue(ext.result) return wrapper() diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 4343e923..a1b8758b 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -98,7 +98,8 @@ def _propogate_state(self, new_state): self.cond.release() def thread_done(self): - self._log.debug("Thread finished from %s" % (threading.current_thread())) + if _debug: + self._log.debug("Thread finished from %s" % (threading.current_thread())) self._propogate_state(external_state.EXITED) def thread_suspend(self): @@ -110,7 +111,8 @@ def thread_start(self): if self.state > external_state.INIT: return if not self.thread.is_alive(): - self._log.debug("Thread %s being started by %s" % (self.thread, threading.current_thread())) + if _debug: + self._log.debug("Thread %s being started by %s" % (self.thread, threading.current_thread())) self._propogate_state(external_state.RUNNING) self.thread.start() @@ -118,19 +120,23 @@ def thread_resume(self): self._propogate_state(external_state.RUNNING) def thread_wait(self): - self._log.debug("Waiting for the condition lock %s" % threading.current_thread()) + if _debug: + self._log.debug("Waiting for the condition lock %s" % threading.current_thread()) + self.cond.acquire() while self.state == external_state.RUNNING: self.cond.wait() - if self.state == external_state.EXITED: - self._log.debug("Thread %s has exited from %s" % (self.thread, threading.current_thread())) - elif self.state == external_state.PAUSED: - self._log.debug("Thread %s has called yield from %s" % (self.thread, threading.current_thread())) - elif self.state == external_state.RUNNING: - self._log.debug("Thread %s is in RUNNING from %d" % (self.thread, threading.current_thread())) - else: + if _debug: + if self.state == external_state.EXITED: + self._log.debug("Thread %s has exited from %s" % (self.thread, threading.current_thread())) + elif self.state == external_state.PAUSED: + self._log.debug("Thread %s has called yield from %s" % (self.thread, threading.current_thread())) + elif self.state == external_state.RUNNING: + self._log.debug("Thread %s is in RUNNING from %d" % (self.thread, threading.current_thread())) + + if self.state == INIT: raise Exception("Thread %s state was not allowed from %s" % (self.thread, threading.current_thread())) self.cond.release() @@ -480,7 +486,6 @@ def queue_function(self, coroutine): for t in self._pending_threads: if t.thread == threading.current_thread(): - self.log.debug("Found myself telling main") t.thread_suspend() self._pending_coros.append(coroutine) return t @@ -500,7 +505,8 @@ def run_in_executor(self, func, *args, **kwargs): def execute_external(func, _waiter): try: _waiter.result = func(*args, **kwargs) - self.log.debug("Execution of external routine done %s" % threading.current_thread()) + if _debug: + self.log.debug("Execution of external routine done %s" % threading.current_thread()) except Exception as e: _waiter.result = e _waiter.thread_done() @@ -512,7 +518,6 @@ def execute_external(func, _waiter): waiter.thread = thread; self._pending_threads.append(waiter) - self.log.debug("Added %s to scheduler" % waiter) return waiter @@ -650,9 +655,11 @@ def schedule(self, coroutine, trigger=None): for ext in self._pending_threads: ext.thread_start() - self.log.debug("Blocking from %s on %s" % (threading.current_thread(), ext.thread)) + if _debug: + self.log.debug("Blocking from %s on %s" % (threading.current_thread(), ext.thread)) state = ext.thread_wait() - self.log.debug("Back from wait on self %s with newstate %d" % (threading.current_thread(), state)) + if _debug: + self.log.debug("Back from wait on self %s with newstate %d" % (threading.current_thread(), state)) if state == external_state.EXITED: self._pending_threads.remove(ext) ext.event.set() @@ -695,6 +702,6 @@ def cleanup(self): raise Exception("Cleanup() called outside of the main thread") for ext in self._pending_threads: - self.log.info("Waiting for %s to exit", ext.thread) + self.log.warn("Waiting for %s to exit", ext.thread) From 1552b4d199ff2035cf6686931046f15c085f4580 Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 14 Aug 2017 10:14:11 -0500 Subject: [PATCH 1347/2656] Issue #19: Fixup typo --- cocotb/scheduler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index a1b8758b..1a634d59 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -136,7 +136,7 @@ def thread_wait(self): elif self.state == external_state.RUNNING: self._log.debug("Thread %s is in RUNNING from %d" % (self.thread, threading.current_thread())) - if self.state == INIT: + if self.state == external_state.INIT: raise Exception("Thread %s state was not allowed from %s" % (self.thread, threading.current_thread())) self.cond.release() From f324b1fc9e6308fcedd1c381aa88c8a0f1471ebb Mon Sep 17 00:00:00 2001 From: chiggs Date: Mon, 14 Aug 2017 10:16:29 -0500 Subject: [PATCH 1348/2656] Issue #19: Should print the python exception if there is one during the callback execution --- lib/simulator/simulatormodule.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 101d635b..8b52619c 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -123,7 +123,13 @@ int handle_gpi_callback(void *user_data) if (pValue == NULL) { fprintf(stderr, "ERROR: called callback function returned NULL\n"); - fprintf(stderr, "Failed to execute callback\n"); + if (PyErr_Occurred()) { + fprintf(stderr, "Failed to execute callback due to python exception\n"); + PyErr_Print(); + } else { + fprintf(stderr, "Failed to execute callback\n"); + } + gpi_sim_end(); ret = 0; goto out; From e77f7f501517fd6aee0b3f5cd224bd3f87711a6c Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 15 Aug 2017 06:43:39 -0500 Subject: [PATCH 1349/2656] Issue #19: Review comments --- cocotb/decorators.py | 20 +------------------- cocotb/scheduler.py | 12 ++++++------ 2 files changed, 7 insertions(+), 25 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 23ae9190..e5d725d1 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -311,7 +311,7 @@ class external(object): """ def __init__(self, func): self._func = func - self._log = SimLog("cocotb.externel.%s" % self._func.__name__, id(self)) + self._log = SimLog("cocotb.external.%s" % self._func.__name__, id(self)) def __call__(self, *args, **kwargs): @@ -329,24 +329,6 @@ def wrapper(): return wrapper() -@public -class hook(coroutine): - """Decorator to mark a function as a hook for cocotb - - All hooks are run at the beginning of a cocotb test suite, prior to any - test code being run.""" - def __init__(self): - pass - - def __call__(self, f): - super(hook, self).__init__(f) - - def _wrapped_hook(*args, **kwargs): - try: - return RunningCoroutine(self._func(*args, **kwargs), self) - except Exception as e: - raise raise_error(self, str(e)) - @public class hook(coroutine): """Decorator to mark a function as a hook for cocotb diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 1a634d59..7105eabc 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -87,12 +87,12 @@ def __init__(self): self.event = Event() self.state = external_state.INIT self.cond = threading.Condition() - self._log = SimLog("cocotb.externel.thead.%s" % self.thread, id(self)) + self._log = SimLog("cocotb.external.thead.%s" % self.thread, id(self)) def _propogate_state(self, new_state): - """ This can be called from threads other than main so _log is only called from main thread""" self.cond.acquire() - #print("Chainging state from %d -> %d from %s" % (self.state, new_state, threading.current_thread())) + if _debug: + self._log.debug("Changing state from %d -> %d from %s" % (self.state, new_state, threading.current_thread())) self.state = new_state self.cond.notify() self.cond.release() @@ -103,8 +103,8 @@ def thread_done(self): self._propogate_state(external_state.EXITED) def thread_suspend(self): - """ only called from an external so _log not safe""" - #print("%s has yielded so telling main thread to carry on" % (threading.current_thread())) + if _debug: + self._log.debug("%s has yielded so telling main thread to carry on" % str(threading.current_thread())) self._propogate_state(external_state.PAUSED) def thread_start(self): @@ -513,7 +513,7 @@ def execute_external(func, _waiter): waiter = external_waiter() thread = threading.Thread(group=None, target=execute_external, - name=func.__name__ + "thread", + name=func.__name__ + "_thread", args=([func, waiter]), kwargs={}) waiter.thread = thread; From bb72de12a2cdb9c470b51d863644d8d3823bea19 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 15 Aug 2017 06:46:45 -0500 Subject: [PATCH 1350/2656] Issue #19: Remove tabs --- cocotb/binary.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 2aff070b..b4e1e311 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -41,14 +41,14 @@ def resolve(string): for char in BinaryValue._resolve_to_1: string = string.replace(char, "1") for char in BinaryValue._resolve_to_error: - if resolve_x_to == "VALUE_ERROR" and char in string: - raise ValueError("Unable to resolve to binary >%s<" % string) - elif resolve_x_to == "ZEROS": - string = string.replace(char, "0") - elif resolve_x_to == "ONES": - string = string.replace(char, "1") - elif resolve_x_to == "RANDOM": - bits = "{0:b}".format(random.getrandbits(1)) + if resolve_x_to == "VALUE_ERROR" and char in string: + raise ValueError("Unable to resolve to binary >%s<" % string) + elif resolve_x_to == "ZEROS": + string = string.replace(char, "0") + elif resolve_x_to == "ONES": + string = string.replace(char, "1") + elif resolve_x_to == "RANDOM": + bits = "{0:b}".format(random.getrandbits(1)) string = string.replace(char, bits) return string From f87160708e974270e1fcd556fad52e0ad85a34ab Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 15 Aug 2017 08:04:36 -0500 Subject: [PATCH 1351/2656] Issue #19: Add some more spaces --- cocotb/binary.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index b4e1e311..2a9995cf 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -41,15 +41,15 @@ def resolve(string): for char in BinaryValue._resolve_to_1: string = string.replace(char, "1") for char in BinaryValue._resolve_to_error: - if resolve_x_to == "VALUE_ERROR" and char in string: - raise ValueError("Unable to resolve to binary >%s<" % string) - elif resolve_x_to == "ZEROS": - string = string.replace(char, "0") - elif resolve_x_to == "ONES": - string = string.replace(char, "1") - elif resolve_x_to == "RANDOM": - bits = "{0:b}".format(random.getrandbits(1)) - string = string.replace(char, bits) + if resolve_x_to == "VALUE_ERROR" and char in string: + raise ValueError("Unable to resolve to binary >%s<" % string) + elif resolve_x_to == "ZEROS": + string = string.replace(char, "0") + elif resolve_x_to == "ONES": + string = string.replace(char, "1") + elif resolve_x_to == "RANDOM": + bits = "{0:b}".format(random.getrandbits(1)) + string = string.replace(char, bits) return string From d70078efbd55ca47dc54643327e8597141e6013d Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 15 Aug 2017 08:06:38 -0500 Subject: [PATCH 1352/2656] Issue #19: Remove some spaces --- cocotb/binary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 2a9995cf..0492fd2b 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -49,7 +49,7 @@ def resolve(string): string = string.replace(char, "1") elif resolve_x_to == "RANDOM": bits = "{0:b}".format(random.getrandbits(1)) - string = string.replace(char, bits) + string = string.replace(char, bits) return string From 2f4382da9ea9c069b9f35bc4b8d24bb525606e99 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 15 Aug 2017 12:13:18 -0500 Subject: [PATCH 1353/2656] Issue #19: Add testcase that exposed we were never unwinding from schedule when finishing an external --- tests/test_cases/test_external/test_external.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index 69c0954a..41a15cfb 100644 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -194,7 +194,10 @@ def test_time_in_external(dut): yield Timer(10, 'ns') time = get_sim_time('ns') dut._log.info("Time at start of test = %d" % time) - yield external(test_print_sim_time)(dut, time) + for i in range(100): + dut._log.info("Loop call %d" % i) + yield external(test_print_sim_time)(dut, time) + time_now = get_sim_time('ns') yield Timer(10, 'ns') From dd88e7732bae6d402c2588558ddef6be87606890 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 15 Aug 2017 12:13:49 -0500 Subject: [PATCH 1354/2656] Issue #19: Unwind back to react after exiting a coroutine and signal the waiting event from there --- cocotb/scheduler.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 7105eabc..cdad6c6e 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -66,7 +66,7 @@ import cocotb import cocotb.decorators -from cocotb.triggers import (Trigger, GPITrigger, Timer, ReadOnly, +from cocotb.triggers import (Trigger, GPITrigger, Timer, ReadOnly, PythonTrigger, _NextTimeStep, _ReadWrite, Event, NullTrigger) from cocotb.log import SimLog from cocotb.result import (TestComplete, TestError, ReturnValue, raise_error, @@ -103,16 +103,13 @@ def thread_done(self): self._propogate_state(external_state.EXITED) def thread_suspend(self): - if _debug: - self._log.debug("%s has yielded so telling main thread to carry on" % str(threading.current_thread())) self._propogate_state(external_state.PAUSED) def thread_start(self): if self.state > external_state.INIT: return + if not self.thread.is_alive(): - if _debug: - self._log.debug("Thread %s being started by %s" % (self.thread, threading.current_thread())) self._propogate_state(external_state.RUNNING) self.thread.start() @@ -225,6 +222,7 @@ def __init__(self): self._pending_callbacks = [] self._pending_triggers = [] self._pending_threads = [] + self._pending_events = [] # Events we need to call set on once we've unwound self._terminate = False self._test_result = None @@ -415,6 +413,13 @@ def react(self, trigger, depth=0): if _debug: self.log.debug("Scheduled coroutine %s" % (coro.__name__)) + # Schedule may have queued up some events so we'll burn through those + while self._pending_events: + if _debug: + self.log.debug("Scheduling pending event %s" % + (str(self._pending_events[0]))) + self._pending_events.pop(0).set() + while self._pending_triggers: if _debug: self.log.debug("Scheduling pending trigger %s" % @@ -650,6 +655,11 @@ def schedule(self, coroutine, trigger=None): # We do not return from here until pending threads have completed, but only # from the main thread, this seems like it could be problematic in cases # where a sim might change what this thread is. + def unblock_event(ext): + @cocotb.coroutine + def wrapper(): + ext.event.set() + yield PythonTrigger() if self._main_thread is threading.current_thread(): @@ -662,7 +672,7 @@ def schedule(self, coroutine, trigger=None): self.log.debug("Back from wait on self %s with newstate %d" % (threading.current_thread(), state)) if state == external_state.EXITED: self._pending_threads.remove(ext) - ext.event.set() + self._pending_events.append(ext.event) # Handle any newly queued coroutines that need to be scheduled while self._pending_coros: From 6168ed3076c2cb9ef5765967d37956943d6b6e81 Mon Sep 17 00:00:00 2001 From: chiggs Date: Tue, 15 Aug 2017 15:46:45 -0500 Subject: [PATCH 1355/2656] Fixes #105 - make busmonitor a bit more flexible --- cocotb/bus.py | 11 ++++++++++- cocotb/drivers/__init__.py | 4 ++-- cocotb/monitors/__init__.py | 5 +++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/cocotb/bus.py b/cocotb/bus.py index 82cf57e5..3fd217c9 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -59,7 +59,7 @@ class Bus(object): TODO: Support for struct/record ports where signals are member names """ - def __init__(self, entity, name, signals, optional_signals=[], bus_separator="_"): + def __init__(self, entity, name, signals, optional_signals=[], bus_separator="_", array_idx=None): """ Args: entity (SimHandle): SimHandle instance to the entity containing the @@ -90,6 +90,10 @@ def __init__(self, entity, name, signals, optional_signals=[], bus_separator="_" signame = name + bus_separator + sig_name else: signame = sig_name + + if array_idx is not None: + signame += "[{:d}]".format(array_idx) + self._entity._log.debug("Signal name {}".format(signame)) setattr(self, attr_name, getattr(entity, signame)) self._signals[attr_name] = getattr(self, attr_name) @@ -99,6 +103,11 @@ def __init__(self, entity, name, signals, optional_signals=[], bus_separator="_" signame = name + bus_separator + sig_name else: signame = sig_name + + if array_idx is not None: + signame += "[{:d}]".format(array_idx) + + self._entity._log.debug("Signal name {}".format(signame)) # Attempts to access a signal that doesn't exist will print a # backtrace so we 'peek' first, slightly un-pythonic if entity.__hasattr__(signame): diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index befb48ba..d67375fc 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -204,7 +204,7 @@ class BusDriver(Driver): """ _optional_signals = [] - def __init__(self, entity, name, clock): + def __init__(self, entity, name, clock, **kwargs): """ Args: entity (SimHandle) : a handle to the simulator entity @@ -221,7 +221,7 @@ def __init__(self, entity, name, clock): self.name = name self.clock = clock self.bus = Bus(self.entity, self.name, self._signals, - self._optional_signals) + self._optional_signals, array_idx=kwargs.get("array_idx")) @coroutine def _driver_send(self, transaction, sync=True): diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index ea7dd461..24a958f6 100644 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -151,13 +151,14 @@ class BusMonitor(Monitor): _optional_signals = [] def __init__(self, entity, name, clock, reset=None, reset_n=None, - callback=None, event=None): + callback=None, event=None, bus_separator="_"): self.log = SimLog("cocotb.%s.%s" % (entity._name, name)) self.entity = entity self.name = name self.clock = clock self.bus = Bus(self.entity, self.name, self._signals, - optional_signals=self._optional_signals) + optional_signals=self._optional_signals, + bus_separator=bus_separator) self._reset = reset self._reset_n = reset_n Monitor.__init__(self, callback=callback, event=event) From dfdbe0c4b5e4182dd5c94742859979adaa64b4b6 Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 16 Aug 2017 09:10:15 -0500 Subject: [PATCH 1356/2656] Fixes #99 - save reading back from bus --- cocotb/drivers/avalon.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 8f908642..29521690 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -667,12 +667,9 @@ def _send_iterable(self, pkt, sync=True): else: self.bus <= word - # If this is a bus with a ready signal, wait for this word to - # be acknowledged - if hasattr(self.bus, "ready"): - yield ReadOnly() - if (self.bus.valid): - yield self._wait_ready() + # Wait for valid words to be acknowledged + if not hasattr(word, "valid") or word.valid: + yield self._wait_ready() yield clkedge self.bus.valid <= 0 From d4ffb1aca4ed8ffe24e4dda92030718e419e583d Mon Sep 17 00:00:00 2001 From: chiggs Date: Wed, 16 Aug 2017 09:11:33 -0500 Subject: [PATCH 1357/2656] Issue #99 - still need to confirm 'ready' exists --- cocotb/drivers/avalon.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 29521690..f3bbbdfa 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -669,7 +669,8 @@ def _send_iterable(self, pkt, sync=True): # Wait for valid words to be acknowledged if not hasattr(word, "valid") or word.valid: - yield self._wait_ready() + if hasattr(self.bus, "ready"): + yield self._wait_ready() yield clkedge self.bus.valid <= 0 From f873ceec32e5b2395dbb886410e3170cce877ba1 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 18 Aug 2017 07:53:34 -0500 Subject: [PATCH 1358/2656] Issue #19: Extend testcase to 1000 loops which is over the standard Python recursion threshold of 999 --- tests/test_cases/test_external/test_external.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index 41a15cfb..32ad1ddd 100644 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -169,9 +169,9 @@ def test_ext_function_return(dut): def test_print_sim_time(dut, base_time): # We are not calling out here so time should not advance # And should also remain consistent - for _ in range(100): + for _ in range(1): _t = get_sim_time('ns') - dut._log.info("Time reported = %d", _t) + #dut._log.info("Time reported = %d", _t) if _t != base_time: raise TestFailure("Time reported does not match base_time %f != %f" % (_t, base_time)) @@ -194,7 +194,7 @@ def test_time_in_external(dut): yield Timer(10, 'ns') time = get_sim_time('ns') dut._log.info("Time at start of test = %d" % time) - for i in range(100): + for i in range(1000): dut._log.info("Loop call %d" % i) yield external(test_print_sim_time)(dut, time) From 1b6f48ceb58d9985879bc9751083e78803ffcd81 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 18 Aug 2017 07:54:31 -0500 Subject: [PATCH 1359/2656] Issue #19: Only play back events when we have unwound to the initial react --- cocotb/scheduler.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index cdad6c6e..e48500eb 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -413,12 +413,13 @@ def react(self, trigger, depth=0): if _debug: self.log.debug("Scheduled coroutine %s" % (coro.__name__)) - # Schedule may have queued up some events so we'll burn through those - while self._pending_events: - if _debug: - self.log.debug("Scheduling pending event %s" % - (str(self._pending_events[0]))) - self._pending_events.pop(0).set() + if not depth: + # Schedule may have queued up some events so we'll burn through those + while self._pending_events: + if _debug: + self.log.debug("Scheduling pending event %s" % + (str(self._pending_events[0]))) + self._pending_events.pop(0).set() while self._pending_triggers: if _debug: From 05e31f12bd17f6c85862cdddc1e4768bd766b4b6 Mon Sep 17 00:00:00 2001 From: chiggs Date: Fri, 18 Aug 2017 08:01:07 -0500 Subject: [PATCH 1360/2656] Issue #19: Fixup test I had changed --- tests/test_cases/test_external/test_external.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index 32ad1ddd..e435e589 100644 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -169,9 +169,9 @@ def test_ext_function_return(dut): def test_print_sim_time(dut, base_time): # We are not calling out here so time should not advance # And should also remain consistent - for _ in range(1): + for _ in range(10): _t = get_sim_time('ns') - #dut._log.info("Time reported = %d", _t) + dut._log.info("Time reported = %d", _t) if _t != base_time: raise TestFailure("Time reported does not match base_time %f != %f" % (_t, base_time)) From 755286bf0eae4c38f4da77432eb38774d4d92a64 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Mon, 13 Nov 2017 14:31:45 +0100 Subject: [PATCH 1361/2656] Make coroutines documentable with autodoc/Sphinx Currently it is impossible to add API documentation to functions decorated with @cocotb.coroutine: the __doc__ string is always taken from the decorator, not from the documented function itself. This behavior is mentioned in the autodoc documentation: > Note > > If you document decorated functions or methods, keep in mind that > autodoc retrieves its docstrings by importing the module and > inspecting the __doc__ attribute of the given function or method. > That means that if a decorator replaces the decorated function with > another, it must copy the original __doc__ to the new function. > > From Python 2.5, functools.wraps() can be used to create well-behaved > decorating functions. -- http://www.sphinx-doc.org/en/stable/ext/autodoc.html As mentioned in the documentation, using functools to wrap the required attributes resolves the issue. --- cocotb/decorators.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 714509d2..ae34bacc 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -31,6 +31,7 @@ import traceback import threading import pdb +import functools from io import StringIO, BytesIO @@ -227,6 +228,7 @@ def __init__(self, func): self._func = func self.log = SimLog("cocotb.function.%s" % self._func.__name__, id(self)) self.__name__ = self._func.__name__ + functools.update_wrapper(self, func) def __call__(self, *args, **kwargs): try: From e0488f71253fc3daf9feba1cf1ac349e3e9880c3 Mon Sep 17 00:00:00 2001 From: David Stanford Date: Sun, 17 Dec 2017 11:47:42 -0600 Subject: [PATCH 1362/2656] Fix the endian_swapper test the wavedrom test runs again --- examples/endian_swapper/tests/test_endian_swapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/endian_swapper/tests/test_endian_swapper.py b/examples/endian_swapper/tests/test_endian_swapper.py index 62ff4b07..7f3a1def 100644 --- a/examples/endian_swapper/tests/test_endian_swapper.py +++ b/examples/endian_swapper/tests/test_endian_swapper.py @@ -182,7 +182,7 @@ def wavedrom_test(dut): """ Generate a JSON wavedrom diagram of a trace """ - cocotb.fork(clock_gen(dut.clk)) + cocotb.fork(Clock(dut.clk,5000).start()) yield RisingEdge(dut.clk) tb = EndianSwapperTB(dut) yield tb.reset() From 6c4320ee732b2af11faee17de57b9f40a4a5662c Mon Sep 17 00:00:00 2001 From: David Stanford Date: Sun, 17 Dec 2017 13:13:05 -0600 Subject: [PATCH 1363/2656] update travis.yml to force linking even if the file already exists. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5e22ed06..b5f94984 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,8 +15,8 @@ install: - sudo apt-get install -qq iverilog-daily - pip install coverage - pip install xunitparser - - if [ $( python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") -eq 3 ]; then ln -s /opt/python/3.4.4/bin/python3.4-config /home/travis/virtualenv/python3.4/bin/python3-config; fi - - if [ $( python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") -eq 3 ]; then ln -s /opt/python/3.4.4/bin/python3.4-config /home/travis/virtualenv/python3.4/bin/python-config; fi + - if [ $( python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") -eq 3 ]; then ln -sf /opt/python/3.4.4/bin/python3.4-config /home/travis/virtualenv/python3.4/bin/python3-config; fi + - if [ $( python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") -eq 3 ]; then ln -sf /opt/python/3.4.4/bin/python3.4-config /home/travis/virtualenv/python3.4/bin/python-config; fi - if [ $( python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") -eq 3 ]; then export LD_LIBRARY_PATH=/opt/python/3.4.4/lib:$LD_LIBRARY_PATH; fi - export PYTHONPATH=$PYTHONPATH:$(python -c "from __future__ import print_function; from distutils.sysconfig import get_python_lib; print(get_python_lib())") script: From 0c2cdfd1c7a87cf8aa66b83f56081108d6e803ed Mon Sep 17 00:00:00 2001 From: David Stanford Date: Sun, 17 Dec 2017 17:36:13 -0600 Subject: [PATCH 1364/2656] run the examples test files as part of the regression test since you don't want their tests to break with an update --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 7c3338c3..fa0495c4 100644 --- a/Makefile +++ b/Makefile @@ -45,6 +45,7 @@ clean: do_tests: $(MAKE) -k -C tests + $(MAKE) -k -C examples # For jenkins we use the exit code to detect compile errors or catestrphic # failures and the xml to track test results From b6c0e8597ed3c59594b209008a295cfb1c8ef888 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Wed, 3 Jan 2018 21:38:52 +0100 Subject: [PATCH 1365/2656] WIP: Use librecores-ci base image for Travis --- .travis.yml | 38 ++++++++------------------------------ Dockerfile | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 30 deletions(-) create mode 100644 Dockerfile diff --git a/.travis.yml b/.travis.yml index b5f94984..cbb6a70d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,32 +1,10 @@ -env: - global: - # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created via the "travis encrypt" command using the project repo's public key - - secure: "YTKZKkNH8t11bZCtR4nMjszifW+QlIAluDAthYq8I4EpuRSKQ1ntVi9MnDs6EQu8CD9LfFOeQ0wymRU71FOf22n54sCVQgbo8OcwHXa/3PUeWi9A/i+BqRZ7iCdeGFJH8rQaCWov6mCyVjjdie52kwWv3jy1ZaTeuUTX54EKFII=" -language: python -python: - - "2.7" - - "3.4" -before_install: - # workaround for travis-ci/travis-ci/#5326 - - export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g") - - sudo add-apt-repository -y ppa:team-electronics/ppa - - sudo apt-get update -qq -install: - - sudo apt-get install -qq iverilog-daily - - pip install coverage - - pip install xunitparser - - if [ $( python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") -eq 3 ]; then ln -sf /opt/python/3.4.4/bin/python3.4-config /home/travis/virtualenv/python3.4/bin/python3-config; fi - - if [ $( python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") -eq 3 ]; then ln -sf /opt/python/3.4.4/bin/python3.4-config /home/travis/virtualenv/python3.4/bin/python-config; fi - - if [ $( python -c "from __future__ import print_function; import sys; print(sys.version_info.major)") -eq 3 ]; then export LD_LIBRARY_PATH=/opt/python/3.4.4/lib:$LD_LIBRARY_PATH; fi - - export PYTHONPATH=$PYTHONPATH:$(python -c "from __future__ import print_function; from distutils.sysconfig import get_python_lib; print(get_python_lib())") +# See Dockerfile for the full build instructions +sudo: required +language: cpp + +services: + - docker + script: - - make test -addons: - coverity_scan: - project: - name: "potentialventures/cocotb" - description: "Python Co-simulation library for RTL verification" - notification_email: github@potentialventures.com - build_command: "make" - branch_pattern: coverity_scan + - docker build . diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..56b27417 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM librecores/ci-osstools:2018.1-rc1 + +# make sources available in docker image +RUN mkdir -p /src +ADD . /src +WORKDIR /src + +RUN apt-get update; apt-get install -y virtualenv python3-venv python3-dev + +# Build and test using Python 2 +RUN mkdir /build-py2; cp -r /src /build-py2 +ENV COCOTB=/build-py2/src +RUN bash -lc 'virtualenv /build-py2/venv; source /build-py2/venv/bin/activate; pip install coverage xunitparser; make -C /build-py2/src test' + +# Build and test using Python 3 +RUN mkdir /build-py3; cp -r /src /build-py3 +ENV COCOTB=/build-py3/src +RUN bash -lce 'python3 -m venv /build-py3/venv; source /build-py3/venv/bin/activate; pip install coverage xunitparser; make -C /build-py3/src test' + From 09d26d17fa00e344eeffc9fa67b94d77e72fae60 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Thu, 4 Jan 2018 10:40:59 +0100 Subject: [PATCH 1366/2656] Fix (sporadic) failures of test_clock_with_units This test was doing floating point equal comparisons, which might or might not work. Replacing with a floating-point comparision with a small boundary around the expected value. --- tests/test_cases/test_cocotb/test_cocotb.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index fd479bf3..1982cffe 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -155,6 +155,14 @@ def test_adding_a_coroutine_without_starting(dut): yield Join(forked) yield Timer(100) +def isclose(a, b, rel_tol=1e-09, abs_tol=0.0): + """ + Polyfill for math.isclose() (Python 3.5+): floating-point "equal" + + Implementation taken from + https://www.python.org/dev/peps/pep-0485/#proposed-implementation + """ + return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol) @cocotb.test(expect_fail=False) def test_clock_with_units(dut): @@ -180,14 +188,14 @@ def test_clock_with_units(dut): yield RisingEdge(dut.clk) edge_time_ns = get_sim_time(units='ns') - if edge_time_ns != start_time_ns + 1000.0: + if not isclose(edge_time_ns, start_time_ns + 1000.0): raise TestFailure("Expected a period of 1 us") start_time_ns = edge_time_ns yield RisingEdge(dut.clk) edge_time_ns = get_sim_time(units='ns') - if edge_time_ns != start_time_ns + 1000.0: + if not isclose(edge_time_ns, start_time_ns + 1000.0): raise TestFailure("Expected a period of 1 us") clk_gen.kill() @@ -201,14 +209,14 @@ def test_clock_with_units(dut): yield RisingEdge(dut.clk) edge_time_ns = get_sim_time(units='ns') - if edge_time_ns != start_time_ns + 4.0: + if not isclose(edge_time_ns, start_time_ns + 4.0): raise TestFailure("Expected a period of 4 ns") start_time_ns = edge_time_ns yield RisingEdge(dut.clk) edge_time_ns = get_sim_time(units='ns') - if edge_time_ns != start_time_ns + 4.0: + if not isclose(edge_time_ns, start_time_ns + 4.0): raise TestFailure("Expected a period of 4 ns") clk_gen.kill() From 101ad9ae56aa2367981f5e1d1e1046d3f3df50f6 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Fri, 12 Jan 2018 16:44:51 +0100 Subject: [PATCH 1367/2656] Python 3 compat: no "long" data type exists in Py3 --- cocotb/binary.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 0492fd2b..fb53ac94 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -513,11 +513,11 @@ def __getitem__(self, key): def __setitem__(self, key, val): ''' BinaryValue uses verilog/vhdl style slices as opposed to python style''' - if not isinstance(val, str) and not isinstance(val, (int, long)): + if not isinstance(val, str) and not isinstance(val, get_python_integer_types()): raise TypeError('BinaryValue slices only accept string or integer values') # convert integer to string - if isinstance(val, (int, long)): + if isinstance(val, get_python_integer_types()): if isinstance(key, slice): num_slice_bits = abs(key.start - key.stop) + 1 else: From eb4184cb39f78d28f85fa2426b0b76835cf151a9 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Fri, 12 Jan 2018 16:21:46 +0100 Subject: [PATCH 1368/2656] Fix combine_results.py for Python 3 --- bin/combine_results.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/combine_results.py b/bin/combine_results.py index 1370b71c..083fe69a 100755 --- a/bin/combine_results.py +++ b/bin/combine_results.py @@ -51,14 +51,14 @@ def main(): result = ET.Element("testsuites", name=args.testsuites_name); for fname in find_all("results.xml", args.directory): - if args.debug : print "Reading file %s" % fname + if args.debug : print("Reading file %s" % fname) tree = ET.parse(fname) for ts in tree.getiterator("testsuite"): - if args.debug : print ("Ts name : %s, package : %s" % ( ts.get('name'), ts.get('package'))) + if args.debug : print("Ts name : %s, package : %s" % ( ts.get('name'), ts.get('package'))) use_element = None for existing in result: if ((existing.get('name') == ts.get('name') and (existing.get('package') == ts.get('package')))): - if args.debug : print "Already found" + if args.debug : print("Already found") use_element=existing break if use_element is None: @@ -73,7 +73,7 @@ def main(): for testcase in testsuite.iter('testcase'): for failure in testcase.iter('failure'): if args.set_rc: rc=1 - print "Failure in testsuite: '%s' testcase: '%s' with parameters '%s'" % (testsuite.get('name'), testcase.get('name'), testsuite.get('package')) + print("Failure in testsuite: '%s' testcase: '%s' with parameters '%s'" % (testsuite.get('name'), testcase.get('name'), testsuite.get('package'))) ET.ElementTree(result).write(args.output_file, encoding="UTF-8") From 5ad511f72e95b73b32a0bd784a5e14fcd47b3b76 Mon Sep 17 00:00:00 2001 From: David Stanford Date: Mon, 8 Jan 2018 12:57:10 -0600 Subject: [PATCH 1369/2656] corrections to get the axi_lite_slave example to work in python 3 and still work in python 2 --- cocotb/bus.py | 4 ++-- examples/axi_lite_slave/hdl/axi_lite_slave.v | 2 +- .../axi_lite_slave/hdl/tb_axi_lite_slave.v | 2 +- .../tests/test_axi_lite_slave.py | 24 +++++++++---------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/cocotb/bus.py b/cocotb/bus.py index 3fd217c9..76d1fd6b 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -85,7 +85,7 @@ def __init__(self, entity, name, signals, optional_signals=[], bus_separator="_" self._name = name self._signals = {} - for attr_name, sig_name in _build_sig_attr_dict(signals).iteritems(): + for attr_name, sig_name in _build_sig_attr_dict(signals).items(): if name: signame = name + bus_separator + sig_name else: @@ -98,7 +98,7 @@ def __init__(self, entity, name, signals, optional_signals=[], bus_separator="_" self._signals[attr_name] = getattr(self, attr_name) # Also support a set of optional signals that don't have to be present - for attr_name, sig_name in _build_sig_attr_dict(optional_signals).iteritems(): + for attr_name, sig_name in _build_sig_attr_dict(optional_signals).items(): if name: signame = name + bus_separator + sig_name else: diff --git a/examples/axi_lite_slave/hdl/axi_lite_slave.v b/examples/axi_lite_slave/hdl/axi_lite_slave.v index 198f93b8..7ec0eabd 100644 --- a/examples/axi_lite_slave/hdl/axi_lite_slave.v +++ b/examples/axi_lite_slave/hdl/axi_lite_slave.v @@ -97,7 +97,7 @@ localparam READ_WAIT_FOR_USER = 4'h4; localparam SEND_READ_DATA = 4'h5; //registes/wires -reg [3:0] state; +reg [3:0] state = IDLE; //submodules //asynchronous logic diff --git a/examples/axi_lite_slave/hdl/tb_axi_lite_slave.v b/examples/axi_lite_slave/hdl/tb_axi_lite_slave.v index e68d41f6..94c6b3ef 100644 --- a/examples/axi_lite_slave/hdl/tb_axi_lite_slave.v +++ b/examples/axi_lite_slave/hdl/tb_axi_lite_slave.v @@ -44,7 +44,7 @@ output [DATA_WIDTH - 1: 0] AXIML_RDATA //Registers reg r_rst; -reg test_id = 0; +reg [7:0] test_id = 0; //Workaround for weird icarus simulator bug always @ (*) r_rst = rst; diff --git a/examples/axi_lite_slave/tests/test_axi_lite_slave.py b/examples/axi_lite_slave/tests/test_axi_lite_slave.py index ad0eb9d8..430cc089 100644 --- a/examples/axi_lite_slave/tests/test_axi_lite_slave.py +++ b/examples/axi_lite_slave/tests/test_axi_lite_slave.py @@ -52,9 +52,9 @@ def write_address_0(dut): if value != DATA: #Fail raise TestFailure("Register at address 0x%08X should have been: \ - 0x%08X but was 0x%08X" % (ADDRESS, DATA, value)) + 0x%08X but was 0x%08X" % (ADDRESS, DATA, int(value))) - dut.log.info("Write 0x%08X to addres 0x%08X" % (value, ADDRESS)) + dut.log.info("Write 0x%08X to addres 0x%08X" % (int(value), ADDRESS)) #Read back a value at address 0x01 @@ -73,12 +73,12 @@ def read_address_1(dut): """ #Reset dut.rst <= 1 - dut.test_id <= 0 + dut.test_id <= 1 axim = AXI4LiteMaster(dut, "AXIML", dut.clk) setup_dut(dut) yield Timer(CLK_PERIOD * 10) dut.rst <= 0 - + yield Timer(CLK_PERIOD) ADDRESS = 0x01 DATA = 0xCD @@ -91,9 +91,9 @@ def read_address_1(dut): if value != DATA: #Fail raise TestFailure("Register at address 0x%08X should have been: \ - 0x%08X but was 0x%08X" % (ADDRESS, DATA, value)) + 0x%08X but was 0x%08X" % (ADDRESS, DATA, int(value))) - dut.log.info("Read: 0x%08X From Address: 0x%08X" % (value, ADDRESS)) + dut._log.info("Read: 0x%08X From Address: 0x%08X" % (int(value), ADDRESS)) @@ -133,9 +133,9 @@ def write_and_read(dut): if value != DATA: #Fail raise TestFailure("Register at address 0x%08X should have been: \ - 0x%08X but was 0x%08X" % (ADDRESS, DATA, value)) + 0x%08X but was 0x%08X" % (ADDRESS, DATA, int(value))) - dut.log.info("Write 0x%08X to addres 0x%08X" % (value, ADDRESS)) + dut._log.info("Write 0x%08X to addres 0x%08X" % (int(value), ADDRESS)) @cocotb.test(skip = False, expect_error=True) def write_fail(dut): @@ -165,8 +165,8 @@ def write_fail(dut): yield axim.write(ADDRESS, DATA) yield Timer(CLK_PERIOD * 10) except AXIProtocolError as e: - print "Exception: %s" % str(e) - dut.log.info("Bus Successfully Raised an Error") + print ("Exception: %s" % str(e)) + dut._log.info("Bus Successfully Raised an Error") raise TestSuccess() raise TestFailure("AXI Bus Should have raised an ERROR when writing to \ the wrong bus") @@ -199,8 +199,8 @@ def read_fail(dut): yield axim.read(ADDRESS, DATA) yield Timer(CLK_PERIOD * 10) except AXIProtocolError as e: - print "Exception: %s" % str(e) - dut.log.info("Bus Successfully Raised an Error") + print ("Exception: %s" % str(e)) + dut._log.info("Bus Successfully Raised an Error") raise TestSuccess() raise TestFailure("AXI Bus Should have raised an ERROR when writing to \ the wrong bus") From 18a53ae7da01c134a0d39e4aeb5a7445a40df94d Mon Sep 17 00:00:00 2001 From: David Stanford Date: Tue, 9 Jan 2018 11:53:56 -0600 Subject: [PATCH 1370/2656] add axi_lite_slave to top example makefile so it'll be checked with the other 'globally' working examples' --- examples/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/Makefile b/examples/Makefile index e48d9a88..dee9f214 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -32,6 +32,7 @@ export COCOTB EXAMPLES := adder/tests \ endian_swapper/tests \ + axi_lite_slave/tests \ .PHONY: $(EXAMPLES) From 47294a5a79989e9fb6a22e66084ce66fbdc60f6e Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Fri, 12 Jan 2018 17:01:31 +0100 Subject: [PATCH 1371/2656] Don't set PYTHONHOME in axi lite example Makefile Setting PYTHONHOME leads to broken builds in virtual environments (venvs), as the venv then contains partially Python 2 and 3 components. --- examples/axi_lite_slave/tests/Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/axi_lite_slave/tests/Makefile b/examples/axi_lite_slave/tests/Makefile index 4d2e8f47..94e785ba 100644 --- a/examples/axi_lite_slave/tests/Makefile +++ b/examples/axi_lite_slave/tests/Makefile @@ -6,7 +6,6 @@ COCOTB=$(PWD)/../../.. PYTHONPATH := ./model:$(PYTHONPATH) export PYTHONPATH -export PYTHONHOME=$(shell python -c "from distutils.sysconfig import get_config_var; print(get_config_var('prefix'))") EXTRA_ARGS+=-I$(TOPDIR)/hdl/ From 26d466a3161a72504f705bc982077e1820ec447c Mon Sep 17 00:00:00 2001 From: "J. R. Petrus" Date: Mon, 15 Jan 2018 16:18:22 -0500 Subject: [PATCH 1372/2656] Replace iteritems() with items() for Python3. --- cocotb/bus.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cocotb/bus.py b/cocotb/bus.py index 3fd217c9..2e7f765b 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -85,7 +85,7 @@ def __init__(self, entity, name, signals, optional_signals=[], bus_separator="_" self._name = name self._signals = {} - for attr_name, sig_name in _build_sig_attr_dict(signals).iteritems(): + for attr_name, sig_name in _build_sig_attr_dict(signals).items(): if name: signame = name + bus_separator + sig_name else: @@ -98,7 +98,7 @@ def __init__(self, entity, name, signals, optional_signals=[], bus_separator="_" self._signals[attr_name] = getattr(self, attr_name) # Also support a set of optional signals that don't have to be present - for attr_name, sig_name in _build_sig_attr_dict(optional_signals).iteritems(): + for attr_name, sig_name in _build_sig_attr_dict(optional_signals).items(): if name: signame = name + bus_separator + sig_name else: @@ -131,7 +131,7 @@ def drive(self, obj, strict=False): Raises: AttributeError """ - for attr_name, hdl in self._signals.iteritems(): + for attr_name, hdl in self._signals.items(): if not hasattr(obj, attr_name): if strict: msg = ("Unable to drive onto {0}.{1} because {2} is missing " @@ -152,7 +152,7 @@ def capture(self): Returns: A dict that supports access by attribute, each attribute corresponds to each signal's value """ - class _Capture(dict): + class _Capture(dict): def __getattr__(self, name): if name in self: return self[name] @@ -166,7 +166,7 @@ def __delattr__(self, name): raise RuntimeError('modifying a bus capture is not supported') _capture = _Capture() - for attr_name, hdl in self._signals.iteritems(): + for attr_name, hdl in self._signals.items(): _capture[attr_name] = hdl.value return _capture From e9a9cbc6b88228d0273497ce2fe84ea417b0e1ec Mon Sep 17 00:00:00 2001 From: Ard Wiersma Date: Tue, 6 Feb 2018 14:59:33 +0100 Subject: [PATCH 1373/2656] Fixed 'external' decorator to handle decoration of class methods similar to functions --- cocotb/decorators.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 0e27260e..1a82f678 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -329,6 +329,11 @@ def wrapper(): return wrapper() + def __get__(self, obj, type=None): + """Permit the decorator to be used on class methods + and standalone functions""" + return self.__class__(self._func.__get__(obj, type)) + @public class hook(coroutine): """Decorator to mark a function as a hook for cocotb From 6c20cc8d0ca77719ec7b59391c4933927cde6a8a Mon Sep 17 00:00:00 2001 From: gideon Date: Wed, 6 Dec 2017 13:06:35 +0100 Subject: [PATCH 1374/2656] Fix for random crashes under Ubuntu 16.04 LTS. Problem was caused by faulty pointer passing between C and Python. Under Ubuntu 16.04 LTS, the C library alloc (or loader?) causes virtual addresses to be used in the upper half of the 32 bit address space, when a 32-bit simulator and 32-bit Python is used. The integer value in Python can not be converted using an "Unsigned" conversion function in that case. The resulting pointer is -1, which causes a SEGFAULT. --- lib/simulator/simulatormodule.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 8b52619c..9b70708e 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -437,7 +437,7 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) } PyObject *pSihHdl = PyTuple_GetItem(args, 0); - sig_hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(pSihHdl); + sig_hdl = (gpi_sim_hdl)PyLong_AsLong(pSihHdl); // Extract the callback function function = PyTuple_GetItem(args, 1); @@ -448,7 +448,7 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) Py_INCREF(function); PyObject *pedge = PyTuple_GetItem(args, 2); - edge = (unsigned int)PyLong_AsUnsignedLong(pedge); + edge = (unsigned int)PyLong_AsLong(pedge); // Remaining args for function if (numargs > 3) @@ -1024,7 +1024,7 @@ static PyObject *deregister_callback(PyObject *self, PyObject *args) gstate = TAKE_GIL(); pSihHdl = PyTuple_GetItem(args, 0); - hdl = (gpi_sim_hdl)PyLong_AsUnsignedLong(pSihHdl); + hdl = (gpi_sim_hdl)PyLong_AsLong(pSihHdl); gpi_deregister_callback(hdl); @@ -1045,7 +1045,7 @@ static PyObject *log_level(PyObject *self, PyObject *args) gstate = TAKE_GIL(); py_level = PyTuple_GetItem(args, 0); - new_level = (enum gpi_log_levels)PyLong_AsUnsignedLong(py_level); + new_level = (enum gpi_log_levels)PyLong_AsLong(py_level); set_log_level(new_level); From afe64dca99c30943986487159b5047bc2d6acac5 Mon Sep 17 00:00:00 2001 From: Sjors Hettinga Date: Mon, 11 Dec 2017 12:29:42 +0100 Subject: [PATCH 1375/2656] Make the 32-bit aldec simulator work on a 64-bit platform --- makefiles/simulators/Makefile.aldec | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 57c6adbe..4feb17aa 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -137,8 +137,12 @@ LIB_LOAD = PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) NEW_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-zA-Z]\):\//\/\1\//g' -e 's/;/:/g') else EXTRA_LIBS = -laldecpli +ifeq ($(ARCH),i686) +EXTRA_LIBDIRS = -L$(ALDEC_BIN_DIR)/Linux +else EXTRA_LIBDIRS = -L$(ALDEC_BIN_DIR)/Linux64 -LIB_LOAD = LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) +endif +LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) NEW_PYTHONPATH := $(PYTHONPATH) endif From 5003711a86a2cb0aef1eba693cc4906fe5332834 Mon Sep 17 00:00:00 2001 From: Ard Wiersma Date: Tue, 12 Dec 2017 10:31:06 +0100 Subject: [PATCH 1376/2656] Fixed handling of std_logic_vector'length=0 signals on dut interface. --- lib/vhpi/VhpiCbHdl.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/vhpi/VhpiCbHdl.cpp b/lib/vhpi/VhpiCbHdl.cpp index b6945065..baf20174 100644 --- a/lib/vhpi/VhpiCbHdl.cpp +++ b/lib/vhpi/VhpiCbHdl.cpp @@ -311,6 +311,11 @@ int VhpiLogicSignalObjHdl::initialise(std::string &name, std::string &fq_name) { m_num_elems = vhpi_get(vhpiSizeP, handle); + if (m_num_elems == 0) { + LOG_DEBUG("Null vector... Delete object") + return -1; + } + if (vhpi_get(vhpiKindP, query_hdl) == vhpiArrayTypeDeclK) { m_indexable = true; m_value.format = vhpiLogicVecVal; From 29dc07dd725827d738e750c379bddc8f73555eba Mon Sep 17 00:00:00 2001 From: potentialventures Date: Wed, 21 Feb 2018 21:27:15 +0000 Subject: [PATCH 1377/2656] Remove link to website In preparation to handing more oversight to FOSSi Foundation --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 5c3c1d21..7d68e429 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ [![Build Status](https://travis-ci.org/potentialventures/cocotb.svg?branch=master)](https://travis-ci.org/potentialventures/cocotb) [![Coverity Scan Status](https://scan.coverity.com/projects/6110/badge.svg)](https://scan.coverity.com/projects/cocotb) -* Skim the introductory presentation: http://potential.ventures * Read the [documentation](http://cocotb.readthedocs.org) * Get involved: * [Raise a bug / request an enhancement](https://github.com/potentialventures/cocotb/issues/new) (Requires a GitHub account) From 6e06e21354a9047c3da69ab0f02980c70bb485e8 Mon Sep 17 00:00:00 2001 From: Nevada Sanchez Date: Sat, 10 Mar 2018 01:58:56 +0000 Subject: [PATCH 1378/2656] Fix test_multi_dimensional array for Python 3. --- .../test_cocotb_array.py | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py b/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py index fcb8df57..26c77276 100644 --- a/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py +++ b/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py @@ -10,189 +10,189 @@ @cocotb.test() def test_in_vect_packed(dut): yield Timer(10) - print"Setting: dut.in_vect_packed type %s" % type(dut.in_vect_packed) + print("Setting: dut.in_vect_packed type %s" % type(dut.in_vect_packed)) dut.in_vect_packed = 0x5 yield Timer(10) - print"Getting: dut.out_vect_packed type %s" % type(dut.out_vect_packed) + print("Getting: dut.out_vect_packed type %s" % type(dut.out_vect_packed)) if dut.out_vect_packed != 0x5: raise TestFailure("Failed to readback dut.out_vect_packed") @cocotb.test() def test_in_vect_unpacked(dut): yield Timer(10) - print"Setting: dut.in_vect_unpacked type %s" % type(dut.in_vect_unpacked) + print("Setting: dut.in_vect_unpacked type %s" % type(dut.in_vect_unpacked)) dut.in_vect_unpacked = [0x1, 0x0, 0x1] yield Timer(10) - print"Getting: dut.out_vect_unpacked type %s" % type( dut.out_vect_unpacked) + print("Getting: dut.out_vect_unpacked type %s" % type( dut.out_vect_unpacked)) if dut.out_vect_unpacked != [0x1, 0x0, 0x1]: raise TestFailure("Failed to readback dut.out_vect_unpacked") @cocotb.test() def test_in_arr(dut): yield Timer(10) - print"Setting: dut.in_arr type %s" % type(dut.in_arr) + print("Setting: dut.in_arr type %s" % type(dut.in_arr)) dut.in_arr = 0x5 yield Timer(10) - print"Getting: dut.out_arr type %s" % type( dut.out_arr) + print("Getting: dut.out_arr type %s" % type( dut.out_arr)) if dut.out_arr != 0x5: raise TestFailure("Failed to readback dut.out_arr") @cocotb.test() def test_in_2d_vect_packed_packed(dut): yield Timer(10) - print"Setting: dut.in_2d_vect_packed_packed type %s" % type(dut.in_2d_vect_packed_packed) + print("Setting: dut.in_2d_vect_packed_packed type %s" % type(dut.in_2d_vect_packed_packed)) dut.in_2d_vect_packed_packed = (0x5 << 6) | (0x5 << 3) | 0x5 yield Timer(10) - print"Getting: dut.out_2d_vect_packed_packed type %s" % type( dut.out_2d_vect_packed_packed) + print("Getting: dut.out_2d_vect_packed_packed type %s" % type( dut.out_2d_vect_packed_packed)) if dut.out_2d_vect_packed_packed != (0x5 << 6) | (0x5 << 3) | 0x5: raise TestFailure("Failed to readback dut.out_2d_vect_packed_packed") @cocotb.test() def test_in_2d_vect_packed_unpacked(dut): yield Timer(10) - print"Setting: dut.in_2d_vect_packed_unpacked type %s" % type(dut.in_2d_vect_packed_unpacked) + print("Setting: dut.in_2d_vect_packed_unpacked type %s" % type(dut.in_2d_vect_packed_unpacked)) dut.in_2d_vect_packed_unpacked = [0x5, 0x5, 0x5] yield Timer(10) - print"Getting: dut.out_2d_vect_packed_unpacked type %s" % type( dut.out_2d_vect_packed_unpacked) + print("Getting: dut.out_2d_vect_packed_unpacked type %s" % type( dut.out_2d_vect_packed_unpacked)) if dut.out_2d_vect_packed_unpacked != [0x5, 0x5, 0x5]: raise TestFailure("Failed to readback dut.out_2d_vect_packed_unpacked") @cocotb.test() def test_in_2d_vect_unpacked_unpacked(dut): yield Timer(10) - print"Setting: dut.in_2d_vect_unpacked_unpacked type %s" % type(dut.in_2d_vect_unpacked_unpacked) + print("Setting: dut.in_2d_vect_unpacked_unpacked type %s" % type(dut.in_2d_vect_unpacked_unpacked)) dut.in_2d_vect_unpacked_unpacked = 3*[[0x1, 0x0, 0x1]] yield Timer(10) - print"Getting: dut.out_2d_vect_unpacked_unpacked type %s" % type( dut.out_2d_vect_unpacked_unpacked) + print("Getting: dut.out_2d_vect_unpacked_unpacked type %s" % type( dut.out_2d_vect_unpacked_unpacked)) if dut.out_2d_vect_unpacked_unpacked != 3*[[0x1, 0x0, 0x1]]: raise TestFailure("Failed to readback dut.out_2d_vect_unpacked_unpacked") @cocotb.test() def test_in_arr_packed(dut): yield Timer(10) - print"Setting: dut.in_arr_packed type %s" % type(dut.in_arr_packed) + print("Setting: dut.in_arr_packed type %s" % type(dut.in_arr_packed)) dut.in_arr_packed = 365 yield Timer(10) - print"Getting: dut.out_arr_packed type %s" % type( dut.out_arr_packed) + print("Getting: dut.out_arr_packed type %s" % type( dut.out_arr_packed)) if dut.out_arr_packed != 365: raise TestFailure("Failed to readback dut.out_arr_packed") @cocotb.test() def test_in_arr_unpacked(dut): yield Timer(10) - print"Setting: dut.in_arr_unpackedtype %s" % type(dut.in_arr_unpacked) + print("Setting: dut.in_arr_unpackedtype %s" % type(dut.in_arr_unpacked)) dut.in_arr_unpacked = [0x5, 0x5, 0x5] yield Timer(10) - print"Getting: dut.out_arr_unpackedtype %s" % type( dut.out_arr_unpacked) + print("Getting: dut.out_arr_unpackedtype %s" % type( dut.out_arr_unpacked)) if dut.out_arr_unpacked != [0x5, 0x5, 0x5]: raise TestFailure("Failed to readback dut.out_arr_unpacked") @cocotb.test() def test_in_2d_arr(dut): yield Timer(10) - print"Setting: dut.in_2d_arr type %s" % type(dut.in_2d_arr) + print("Setting: dut.in_2d_arr type %s" % type(dut.in_2d_arr)) dut.in_2d_arr = 365 yield Timer(10) - print"Getting: dut.out_2d_arr type %s" % type( dut.out_2d_arr) + print("Getting: dut.out_2d_arr type %s" % type( dut.out_2d_arr)) if dut.out_2d_arr != 365: raise TestFailure("Failed to readback dut.out_2d_arr") @cocotb.test() def test_in_vect_packed_packed_packed(dut): yield Timer(10) - print"Setting: dut.in_vect_packed_packed_packed type %s" % type(dut.in_vect_packed_packed_packed) + print("Setting: dut.in_vect_packed_packed_packed type %s" % type(dut.in_vect_packed_packed_packed)) dut.in_vect_packed_packed_packed = 95869805 yield Timer(10) - print"Getting: dut.out_vect_packed_packed_packed type %s" % type( dut.out_vect_packed_packed_packed) + print("Getting: dut.out_vect_packed_packed_packed type %s" % type( dut.out_vect_packed_packed_packed)) if dut.out_vect_packed_packed_packed != 95869805: raise TestFailure("Failed to readback dut.out_vect_packed_packed_packed") @cocotb.test() def test_in_vect_packed_packed_unpacked(dut): yield Timer(10) - print"Setting: dut.in_vect_packed_packed_unpacked type %s" % type(dut.in_vect_packed_packed_unpacked) + print("Setting: dut.in_vect_packed_packed_unpacked type %s" % type(dut.in_vect_packed_packed_unpacked)) dut.in_vect_packed_packed_unpacked = [95869805, 95869805, 95869805] yield Timer(10) - print"Getting: dut.out_vect_packed_packed_unpacked type %s" % type( dut.out_vect_packed_packed_unpacked) + print("Getting: dut.out_vect_packed_packed_unpacked type %s" % type( dut.out_vect_packed_packed_unpacked)) if dut.out_vect_packed_packed_unpacked != [365, 365, 365]: raise TestFailure("Failed to readback dut.out_vect_packed_packed_unpacked") @cocotb.test() def test_in_vect_packed_unpacked_unpacked(dut): yield Timer(10) - print"Setting: dut.in_vect_packed_unpacked_unpacked type %s" % type(dut.in_vect_packed_unpacked_unpacked) + print("Setting: dut.in_vect_packed_unpacked_unpacked type %s" % type(dut.in_vect_packed_unpacked_unpacked)) dut.in_vect_packed_unpacked_unpacked = 3 *[3 * [5] ] yield Timer(10) - print"Getting: dut.out_vect_packed_unpacked_unpacked type %s" % type( dut.out_vect_packed_unpacked_unpacked) + print("Getting: dut.out_vect_packed_unpacked_unpacked type %s" % type( dut.out_vect_packed_unpacked_unpacked)) if dut.out_vect_packed_unpacked_unpacked != 3 *[3 * [5] ]: raise TestFailure("Failed to readback dut.out_vect_packed_unpacked_unpacked") @cocotb.test() def test_in_vect_unpacked_unpacked_unpacked(dut): yield Timer(10) - print"Setting: dut.in_vect_unpacked_unpacked_unpacked type %s" % type(dut.in_vect_unpacked_unpacked_unpacked) + print("Setting: dut.in_vect_unpacked_unpacked_unpacked type %s" % type(dut.in_vect_unpacked_unpacked_unpacked)) dut.in_vect_unpacked_unpacked_unpacked = 3 *[3 * [[1, 0, 1]]] yield Timer(10) - print"Getting: dut.out_vect_unpacked_unpacked_unpacked type %s" % type( dut.out_vect_unpacked_unpacked_unpacked) + print("Getting: dut.out_vect_unpacked_unpacked_unpacked type %s" % type( dut.out_vect_unpacked_unpacked_unpacked)) if dut.out_vect_unpacked_unpacked_unpacked != 3 *[3 * [[1, 0, 1]]]: raise TestFailure("Failed to readback dut.out_vect_unpacked_unpacked_unpacked") @cocotb.test() def test_in_arr_packed_packed(dut): yield Timer(10) - print"Setting: dut.in_arr_packed_packed type %s" % type(dut.in_arr_packed_packed) + print("Setting: dut.in_arr_packed_packed type %s" % type(dut.in_arr_packed_packed)) dut.in_arr_packed_packed = (365 << 18) | (365 << 9) | (365) yield Timer(10) - print"Getting: dut.out_arr_packed_packed type %s" % type( dut.out_arr_packed_packed) + print("Getting: dut.out_arr_packed_packed type %s" % type( dut.out_arr_packed_packed)) if dut.out_arr_packed_packed != (365 << 18) | (365 << 9) | (365): raise TestFailure("Failed to readback dut.out_arr_packed_packed") @cocotb.test() def test_in_arr_packed_unpacked(dut): yield Timer(10) - print"Setting: dut.in_arr_packed_unpacked type %s" % type(dut.in_arr_packed_unpacked) + print("Setting: dut.in_arr_packed_unpacked type %s" % type(dut.in_arr_packed_unpacked)) dut.in_arr_packed_unpacked = [365, 365, 365] yield Timer(10) - print"Getting: dut.out_arr_packed_unpacked type %s" % type( dut.out_arr_packed_unpacked) + print("Getting: dut.out_arr_packed_unpacked type %s" % type( dut.out_arr_packed_unpacked)) if dut.out_arr_packed_unpacked != [365, 365, 365]: raise TestFailure("Failed to readback dut.out_arr_packed_unpacked") @cocotb.test() def test_in_arr_unpacked_unpacked(dut): yield Timer(10) - print"Setting: dut.in_arr_unpacked_unpacked type %s" % type(dut.in_arr_unpacked_unpacked) + print("Setting: dut.in_arr_unpacked_unpacked type %s" % type(dut.in_arr_unpacked_unpacked)) dut.in_arr_unpacked_unpacked = 3 *[3 * [5] ] yield Timer(10) - print"Getting: dut.out_arr_unpacked_unpacked type %s" % type( dut.out_arr_unpacked_unpacked) + print("Getting: dut.out_arr_unpacked_unpacked type %s" % type( dut.out_arr_unpacked_unpacked)) if dut.out_arr_unpacked_unpacked != 3 *[3 * [5] ]: raise TestFailure("Failed to readback dut.out_arr_unpacked_unpacked") @cocotb.test() def test_in_2d_arr_packed(dut): yield Timer(10) - print"Setting: dut.in_2d_arr_packed type %s" % type(dut.in_2d_arr_packed) + print("Setting: dut.in_2d_arr_packed type %s" % type(dut.in_2d_arr_packed)) dut.in_2d_arr_packed = (365 << 18) | (365 << 9) | (365) yield Timer(10) - print"Getting: dut.out_2d_arr_packed type %s" % type( dut.out_2d_arr_packed) + print("Getting: dut.out_2d_arr_packed type %s" % type( dut.out_2d_arr_packed)) if dut.out_2d_arr_packed != (365 << 18) | (365 << 9) | (365): raise TestFailure("Failed to readback dut.out_2d_arr_packed") @cocotb.test() def test_in_2d_arr_unpacked(dut): yield Timer(10) - print"Setting: dut.in_2d_arr_unpacked type %s" % type(dut.in_2d_arr_unpacked) + print("Setting: dut.in_2d_arr_unpacked type %s" % type(dut.in_2d_arr_unpacked)) dut.in_2d_arr_unpacked = [365, 365, 365] yield Timer(10) - print"Getting: dut.out_2d_arr_unpacked type %s" % type( dut.out_2d_arr_unpacked) + print("Getting: dut.out_2d_arr_unpacked type %s" % type( dut.out_2d_arr_unpacked)) if dut.out_2d_arr_unpacked != [365, 365, 365]: raise TestFailure("Failed to readback dut.out_2d_arr_unpacked") @cocotb.test() def test_in_3d_arr(dut): yield Timer(10) - print"Setting: dut.in_3d_arr type %s" % type(dut.in_3d_arr) + print("Setting: dut.in_3d_arr type %s" % type(dut.in_3d_arr)) dut.in_3d_arr = (365 << 18) | (365 << 9) | (365) yield Timer(10) - print"Getting: dut.out_3d_arr type %s" % type( dut.out_3d_arr) + print("Getting: dut.out_3d_arr type %s" % type( dut.out_3d_arr)) if dut.out_3d_arr != (365 << 18) | (365 << 9) | (365): raise TestFailure("Failed to readback dut.out_3d_arr") From 3bac23e29a7e2ae4f47ad1a6815e2ef67e5eef63 Mon Sep 17 00:00:00 2001 From: Nevada Sanchez Date: Sat, 10 Mar 2018 02:10:46 +0000 Subject: [PATCH 1379/2656] Fix print statements across repo for Python 3. --- bin/create_files.py | 2 +- cocotb/binary.py | 12 ++++++------ cocotb/drivers/ad9361.py | 6 +++--- documentation/source/quickstart.rst | 10 +++++----- examples/axi_lite_slave/tests/test_axi_lite_slave.py | 6 ++---- tests/test_cases/test_avalon/test_avalon.py | 5 ++--- 6 files changed, 19 insertions(+), 22 deletions(-) diff --git a/bin/create_files.py b/bin/create_files.py index a8fbe727..22fd13e9 100755 --- a/bin/create_files.py +++ b/bin/create_files.py @@ -67,7 +67,7 @@ def print_files(path): def check_args(args): if len(args) is not 1: - print "Please specify a path" + print("Please specify a path") return print_files(args[0]) diff --git a/cocotb/binary.py b/cocotb/binary.py index fb53ac94..cf87f7c8 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -85,9 +85,9 @@ class BinaryValue(object): >>> vec = BinaryValue() >>> vec.integer = 42 - >>> print vec.binstr + >>> print(vec.binstr) 101010 - >>> print repr(vec.buff) + >>> print(repr(vec.buff)) '*' """ @@ -376,12 +376,12 @@ def __nonzero__(self): """Provide boolean testing of a binstr. >>> val = BinaryValue("0000") - >>> if val: print "True" - ... else: print "False" + >>> if val: print("True") + ... else: print("False") False >>> val.integer = 42 - >>> if val: print "True" - ... else: print "False" + >>> if val: print("True") + ... else: print("False") True """ diff --git a/cocotb/drivers/ad9361.py b/cocotb/drivers/ad9361.py index d04d402c..b4f82469 100644 --- a/cocotb/drivers/ad9361.py +++ b/cocotb/drivers/ad9361.py @@ -48,7 +48,7 @@ def _rx_clock(self): def send_data(self, i_data, q_data, i_data2=None, q_data2=None, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT): - print binaryRepresentation + print(binaryRepresentation) cocotb.fork(self.rx_data_to_ad9361(i_data, q_data, i_data2, q_data2, binaryRepresentation)) @@ -145,8 +145,8 @@ def _tx_data_from_ad9361(self): i_bin_val[11:6] = self.dut.tx_data_out_p.value.get_binstr() else: i_bin_val[5:0] = self.dut.tx_data_out_p.value.get_binstr() - # print "i_data",i_bin_val.get_value() - # print "q_data",q_bin_val.get_value() + # print("i_data",i_bin_val.get_value()) + # print("q_data",q_bin_val.get_value()) self.lbqi.append(i_bin_val) self.lbqq.append(q_bin_val) self.got_tx.set([i_bin_val, q_bin_val]) diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index 918458c5..ec2fe7dd 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -245,18 +245,18 @@ Accessing the .value property of a handle object will return a :class:`BinaryVal >>> # Read a value back from the dut >>> count = dut.counter.value - >>> - >>> print count.binstr + >>> + >>> print(count.binstr) 1X1010 >>> # Resolve the value to an integer (X or Z treated as 0) - >>> print count.integer + >>> print(count.integer) 42 We can also cast the signal handle directly to an integer: .. code-block:: python - - >>> print int(dut.counter) + + >>> print(int(dut.counter)) 42 diff --git a/examples/axi_lite_slave/tests/test_axi_lite_slave.py b/examples/axi_lite_slave/tests/test_axi_lite_slave.py index 430cc089..cb57ed94 100644 --- a/examples/axi_lite_slave/tests/test_axi_lite_slave.py +++ b/examples/axi_lite_slave/tests/test_axi_lite_slave.py @@ -165,7 +165,7 @@ def write_fail(dut): yield axim.write(ADDRESS, DATA) yield Timer(CLK_PERIOD * 10) except AXIProtocolError as e: - print ("Exception: %s" % str(e)) + print("Exception: %s" % str(e)) dut._log.info("Bus Successfully Raised an Error") raise TestSuccess() raise TestFailure("AXI Bus Should have raised an ERROR when writing to \ @@ -199,10 +199,8 @@ def read_fail(dut): yield axim.read(ADDRESS, DATA) yield Timer(CLK_PERIOD * 10) except AXIProtocolError as e: - print ("Exception: %s" % str(e)) + print("Exception: %s" % str(e)) dut._log.info("Bus Successfully Raised an Error") raise TestSuccess() raise TestFailure("AXI Bus Should have raised an ERROR when writing to \ the wrong bus") - - diff --git a/tests/test_cases/test_avalon/test_avalon.py b/tests/test_cases/test_avalon/test_avalon.py index 44b41f10..3541eb48 100644 --- a/tests/test_cases/test_avalon/test_avalon.py +++ b/tests/test_cases/test_avalon/test_avalon.py @@ -103,8 +103,8 @@ def test_burst_read(dut): dut.user_read_buffer = 0 yield RisingEdge(dut.clk) - print str(read_mem) - print str(len(read_mem)) + " 8bits values read" + print(str(read_mem)) + print(str(len(read_mem)) + " 8bits values read") # checking values read for key, value in read_mem.items(): @@ -122,4 +122,3 @@ def test_burst_read(dut): yield Timer(10) dut.user_read_buffer = 0 yield Timer(10) - From 4e315f4f5dcf28e7725f412e93df6de4a7c963cd Mon Sep 17 00:00:00 2001 From: Nevada Sanchez Date: Fri, 9 Mar 2018 22:09:58 +0000 Subject: [PATCH 1380/2656] Replace xrange with range for Python 3 compatibility. --- cocotb/drivers/avalon.py | 2 +- cocotb/handle.py | 6 +++--- documentation/source/endian_swapper.rst | 2 +- documentation/source/ping_tun_tap.rst | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index a33dd313..10ff11c9 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -457,7 +457,7 @@ def _respond(self): self.log.debug("Data in : %x" % data) self.log.debug("Width : %d" % self._width) self.log.debug("Byteenable: %x" % byteenable) - for i in xrange(self._width/8): + for i in range(self._width/8): if (byteenable & 2**i): mask |= 0xFF << (8*i) else: diff --git a/cocotb/handle.py b/cocotb/handle.py index 18be1a2e..4faf8d67 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -259,7 +259,7 @@ def __setattr__(self, name, value): if len(sub) != len(value): raise IndexError("Attempting to set %s with list length %d but target has length %d" % ( name, len(value), len(sub))) - for idx in xrange(len(value)): + for idx in range(len(value)): sub[idx] = value[idx] return else: @@ -399,7 +399,7 @@ def _getvalue(self): if type(self) is NonHierarchyIndexableObject: #Need to iterate over the sub-object result =[] - for x in xrange(len(self)): + for x in range(len(self)): result.append(self[x]._getvalue()) return result else: @@ -491,7 +491,7 @@ def __setitem__(self, index, value): raise IndexError("Assigning list of length %d to object %s of length %d" % ( len(value), self.__getitem__(index)._fullname, len(self.__getitem__(index)))) self._log.info("Setting item %s to %s" % (self.__getitem__(index)._fullname, value)) - for idx in xrange(len(value)): + for idx in range(len(value)): self.__getitem__(index).__setitem__(idx, value[idx]) else: self.__getitem__(index).value = value diff --git a/documentation/source/endian_swapper.rst b/documentation/source/endian_swapper.rst index e2b2300e..03a12984 100644 --- a/documentation/source/endian_swapper.rst +++ b/documentation/source/endian_swapper.rst @@ -139,7 +139,7 @@ We want to run different variations of tests but they will all have a very simil yield tb.stream_in.send(transaction) # Wait at least 2 cycles where output ready is low before ending the test - for i in xrange(2): + for i in range(2): yield RisingEdge(dut.clk) while not dut.stream_out_ready.value: yield RisingEdge(dut.clk) diff --git a/documentation/source/ping_tun_tap.rst b/documentation/source/ping_tun_tap.rst index 5c192562..2bf0c9de 100644 --- a/documentation/source/ping_tun_tap.rst +++ b/documentation/source/ping_tun_tap.rst @@ -118,7 +118,7 @@ write the received packet back to the TUN file descriptor. subprocess.check_call('ping -c 5 192.168.255.2 &', shell=True) # Respond to 5 pings, then quit - for i in xrange(5): + for i in range(5): cocotb.log.info("Waiting for packets on tun interface") packet = os.read(fd, 2048) From e4122927a95c1f7e02800c9e05e762e5fdf0518e Mon Sep 17 00:00:00 2001 From: Nevada Sanchez Date: Sat, 10 Mar 2018 03:09:12 +0000 Subject: [PATCH 1381/2656] Fix avalon driver for Python 3. --- cocotb/drivers/avalon.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 10ff11c9..5614cc56 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -254,7 +254,7 @@ def __init__(self, entity, name, clock, readlatency_min=1, if hasattr(self.bus, "readdata"): self._width = len(self.bus.readdata) - self.dataByteSize = self._width/8 + self.dataByteSize = int(self._width/8) self._readable = True if hasattr(self.bus, "writedata"): @@ -263,7 +263,7 @@ def __init__(self, entity, name, clock, readlatency_min=1, self.log.error("readdata and writedata bus" + " are not the same size") self._width = width - self.dataByteSize = self._width/8 + self.dataByteSize = int(self._width/8) self._writeable = True if not self._readable and not self._writeable: @@ -402,7 +402,7 @@ def _respond(self): self.log.error("Address must be aligned to data width" + "(addr = " + hex(addr) + ", width = " + str(self._width)) - addr = addr / self.dataByteSize + addr = int(addr / self.dataByteSize) burstcount = self.bus.burstcount.value.integer byteenable = self.bus.byteenable.value if byteenable != int("1"*len(self.bus.byteenable), 2): @@ -699,4 +699,3 @@ def _driver_send(self, pkt, sync=True): self.log.info("Sucessfully sent packet of length %d bytes" % len(pkt)) else: yield self._send_iterable(pkt, sync=sync) - From 876c21a753b4d17bcf9f2ca59627546eab890411 Mon Sep 17 00:00:00 2001 From: Nevada Sanchez Date: Sat, 10 Mar 2018 03:19:02 +0000 Subject: [PATCH 1382/2656] Don't mask errors during GPI init. --- lib/gpi/GpiCommon.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gpi/GpiCommon.cpp b/lib/gpi/GpiCommon.cpp index 1f1d375a..e11908d9 100644 --- a/lib/gpi/GpiCommon.cpp +++ b/lib/gpi/GpiCommon.cpp @@ -112,7 +112,7 @@ int gpi_register_impl(GpiImplInterface *func_tbl) void gpi_embed_init(gpi_sim_info_t *info) { if (embed_sim_init(info)) - gpi_sim_end(); + gpi_embed_end(); } void gpi_embed_end(void) @@ -338,7 +338,7 @@ gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, int32_t index) GpiObjHdl *base = sim_to_hdl(parent); GpiImplInterface *intf = base->m_impl; - /* Shouldn't need to iterate over interfaces because indexing into a handle shouldn't + /* Shouldn't need to iterate over interfaces because indexing into a handle shouldn't * cross the interface boundaries. * * NOTE: IUS's VPI interface returned valid VHDL handles, but then couldn't From 52579ab09b54ed06292ccc7a4c54538865b5ea3e Mon Sep 17 00:00:00 2001 From: Luke Darnell Date: Thu, 15 Mar 2018 10:31:38 +1100 Subject: [PATCH 1383/2656] hexdiffs should honour COCOTB_ANSI_OUTPUT #603 --- cocotb/utils.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/cocotb/utils.py b/cocotb/utils.py index d7d0fa45..799a75ab 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -32,6 +32,7 @@ import ctypes import math import os +import sys # For autodocumentation don't need the extension modules if "SPHINX_BUILD" in os.environ: @@ -231,7 +232,16 @@ def sane(x): return r def highlight(string, colour=ANSI.YELLOW_FG): - return colour + string + ANSI.DEFAULT_FG + ANSI.DEFAULT_BG + want_ansi = os.getenv("COCOTB_ANSI_OUTPUT") + if want_ansi is None: + want_ansi = sys.stdout.isatty() # default to ANSI for TTYs + else: + want_ansi = want_ansi == '1' + + if want_ansi: + return colour + string + ANSI.DEFAULT_FG + ANSI.DEFAULT_BG + else: + return string rs = "" From 0f07358be67491b0c89e919deac84e2aee9a9066 Mon Sep 17 00:00:00 2001 From: Marco Eppenberger Date: Thu, 17 May 2018 18:20:41 +0200 Subject: [PATCH 1384/2656] Add the __repr__() function to BinaryValue to make it immediately compatible with the default scoreboard implementation. --- cocotb/binary.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cocotb/binary.py b/cocotb/binary.py index cf87f7c8..86c07ded 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -369,6 +369,9 @@ def __le__(self, other): def __str__(self): return self.binstr + def __repr__(self): + return self.__str__() + def __bool__(self): return self.__nonzero__() From 5b9d1fdee22df468ad253bc42974eb1bea7f14a3 Mon Sep 17 00:00:00 2001 From: Marco Eppenberger Date: Thu, 17 May 2018 18:33:41 +0200 Subject: [PATCH 1385/2656] Adhere to VIRTUAL_ENV Setting when Starting Python Interpreter In the python-boot-up phase, the path to the python executable is now taken from VIRTUAL_ENV environment variable if present, otherwise its the same behavior as before. --- lib/embed/gpi_embed.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index d17ac412..d2f23d30 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -33,6 +33,11 @@ #include #include "embed.h" #include "../compat/python3_compat.h" +#include "locale.h" + +#include +#include +#include #if defined(_WIN32) #include @@ -85,7 +90,32 @@ void embed_init_python(void) } to_python(); - Py_SetProgramName(progname); + + // reset Program Name (i.e. argv[0]) if we are in a virtual environment + char *venv_path_home = getenv("VIRTUAL_ENV"); + if (venv_path_home) { + char venv_path[strlen(venv_path_home)+64]; + strcpy(venv_path, venv_path_home); + strcat(venv_path, "/bin/python"); // this is universal in any VIRTUAL_ENV the python interpreter +#if PY_MAJOR_VERSION >= 3 + static wchar_t venv_path_w[1024]; +#if PY_MINOR_VERSION >= 5 + // Python3.5 + provides the locale decoder + wcscpy(venv_path_w, Py_DecodeLocale(venv_path, NULL)); +#else + // for lesser python versions, we just hope the user specified his locales correctly + setlocale (LC_ALL, ""); + mbstowcs(venv_path_w, venv_path, sizeof(venv_path_w)); +#endif + LOG_INFO("Using virtualenv at %ls.", venv_path_w); + Py_SetProgramName(venv_path_w); +#else + // Python2 case + LOG_INFO("Using virtualenv at %s.", venv_path); + Py_SetProgramName(venv_path); +#endif + } + Py_Initialize(); /* Initialize the interpreter */ PySys_SetArgvEx(1, argv, 0); PyEval_InitThreads(); /* Create (and acquire) the interpreter lock */ From 952db867f0c425e795a2643d83ced89b5709a57f Mon Sep 17 00:00:00 2001 From: Marco Eppenberger Date: Thu, 17 May 2018 18:38:01 +0200 Subject: [PATCH 1386/2656] Removed unnecessary header imports. --- lib/embed/gpi_embed.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index d2f23d30..d812b557 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -35,10 +35,6 @@ #include "../compat/python3_compat.h" #include "locale.h" -#include -#include -#include - #if defined(_WIN32) #include #define sleep(n) Sleep(1000 * n) From 224a1562cc5eb95a5daef99cac6f351f6026bf6f Mon Sep 17 00:00:00 2001 From: Ard Wiersma Date: Tue, 29 May 2018 09:49:28 +0200 Subject: [PATCH 1387/2656] Added exception traceback logging for call to regression.initialisation() during CocoTb init phase. --- cocotb/regression.py | 8 ++++++++ 1 file changed, 8 insertions(+) mode change 100644 => 100755 cocotb/regression.py diff --git a/cocotb/regression.py b/cocotb/regression.py old mode 100644 new mode 100755 index 5045c586..87959848 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -90,6 +90,14 @@ def __init__(self, root_name, modules, tests=None, seed=None, hooks=[]): self._hooks = hooks def initialise(self): + try: + self._initialise() + except Exception as e: + import traceback + self.log.error(traceback.format_exc()) + raise + + def _initialise(self): self.start_time = time.time() self.test_results = [] From dd24da389538a71088da8d3797d9927e12fbb2ce Mon Sep 17 00:00:00 2001 From: elgorwi Date: Mon, 4 Jun 2018 19:38:17 +1000 Subject: [PATCH 1388/2656] Fix issue driving values on 32-bit systems. --- cocotb/handle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 4faf8d67..eb3e76d1 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -597,7 +597,7 @@ def setimmediatevalue(self, value): Assigning integers less than 32-bits is faster """ - if isinstance(value, get_python_integer_types()) and value < 0x7fffffff: + if isinstance(value, get_python_integer_types()) and value < 0x7fffffff and len(self) <= 32: simulator.set_signal_val_long(self._handle, value) return From 68051e616f461508272decffe8dbe5abd4532de4 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Mon, 4 Jun 2018 10:30:28 +0000 Subject: [PATCH 1389/2656] Update issue_142.py Fix regression --- tests/test_cases/issue_142/issue_142.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/issue_142/issue_142.py b/tests/test_cases/issue_142/issue_142.py index d2fec00f..1bc31461 100644 --- a/tests/test_cases/issue_142/issue_142.py +++ b/tests/test_cases/issue_142/issue_142.py @@ -7,7 +7,7 @@ from cocotb.binary import BinaryValue -@cocotb.test(expect_error=cocotb.SIM_NAME.lower().startswith(("icarus"))) +@cocotb.test() def issue_142_overflow_error(dut): """Tranparently convert ints too long to pass through the GPI interface natively into BinaryValues""" From a323bd7057e4fc93c46ee3fa8533829abbcf979c Mon Sep 17 00:00:00 2001 From: Marco Eppenberger Date: Tue, 12 Jun 2018 11:41:33 +0200 Subject: [PATCH 1390/2656] Added printing of the error traceback on testbench-module import errors. This gives much clearer error messages. --- cocotb/regression.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 5045c586..5ad106b4 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -24,6 +24,7 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' +import traceback """ All things relating to regression capabilities @@ -124,11 +125,14 @@ def initialise(self): # Auto discovery for module_name in self._modules: try: + self.log.debug("Python Path: " + ",".join(sys.path)) + self.log.debug("PWD: " + os.getcwd()) module = _my_import(module_name) - except ImportError: - self.log.critical("Failed to import module %s", module_name) - self.log.info("MODULE variable was \"%s\"", - ",".join(self._modules)) + except Exception as E: + self.log.critical("Failed to import module %s: %s", (module_name, E)) + self.log.info("MODULE variable was \"%s\"", ".".join(self._modules)) + self.log.info("Traceback: ") + self.log.info(traceback.format_exc()) raise if self._functions: From a65d832bee409d40a1bd63b4cc478cb09a4f1e0f Mon Sep 17 00:00:00 2001 From: Marco Eppenberger Date: Tue, 12 Jun 2018 12:45:11 +0200 Subject: [PATCH 1391/2656] Put import statement at correct location. --- cocotb/regression.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 5ad106b4..fe906bbf 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -24,7 +24,6 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' -import traceback """ All things relating to regression capabilities @@ -36,6 +35,7 @@ from itertools import product import sys import os +import traceback # For autodocumentation don't need the extension modules if "SPHINX_BUILD" in os.environ: simulator = None From b61e95a01f6fb4032069fc71a0315231cf1179bc Mon Sep 17 00:00:00 2001 From: Marco Eppenberger Date: Tue, 12 Jun 2018 13:42:26 +0200 Subject: [PATCH 1392/2656] Added debug info when not using virtualenv. --- lib/embed/gpi_embed.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index d812b557..d6dccb11 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -110,6 +110,8 @@ void embed_init_python(void) LOG_INFO("Using virtualenv at %s.", venv_path); Py_SetProgramName(venv_path); #endif + } else { + LOG_INFO("Did not detect virtual environment. Using system-wide Python interpreter."); } Py_Initialize(); /* Initialize the interpreter */ From ea6801db06d61264081ccadf55a2df44cb7c44bf Mon Sep 17 00:00:00 2001 From: elgorwi Date: Sun, 3 Jun 2018 08:55:52 +1000 Subject: [PATCH 1393/2656] Fix undefined reference to context error on windows --- lib/embed/gpi_embed.c | 3 --- lib/simulator/Makefile | 2 +- lib/utils/cocotb_utils.c | 3 +++ 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index d17ac412..a8a7d5de 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -50,9 +50,6 @@ static char *argv[] = { progname }; static PyObject *pEventFn = NULL; -// Tracks if we are in the context of Python or Simulator -int context = 0; - /** * @name Initialise the python interpreter * @brief Create and initialise the python interpreter diff --git a/lib/simulator/Makefile b/lib/simulator/Makefile index 42176091..5496b6fc 100644 --- a/lib/simulator/Makefile +++ b/lib/simulator/Makefile @@ -31,7 +31,7 @@ include $(SIM_ROOT)/makefiles/Makefile.inc INCLUDES += GCC_ARGS += -LIBS := -lgpi -lgpilog $(PYLIBS) +LIBS := -lcocotbutils -lgpi -lgpilog $(PYLIBS) LD_PATH := -L$(LIB_DIR) LIB_NAME := libsim diff --git a/lib/utils/cocotb_utils.c b/lib/utils/cocotb_utils.c index 58adcb1b..ebd5f553 100644 --- a/lib/utils/cocotb_utils.c +++ b/lib/utils/cocotb_utils.c @@ -36,6 +36,9 @@ #include #endif +// Tracks if we are in the context of Python or Simulator +int context = 0; + void* utils_dyn_open(const char* lib_name) { void *ret = NULL; From 6e16d28511bb464ef4270ca4fa672e940d2e1c1b Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 28 Jul 2018 13:53:46 -0700 Subject: [PATCH 1394/2656] ENH: Add support for using `return` in coroutines in python >= 3.3 This eliminates the need to write `raise ReturnValue(x)`, allowing `return x` instead The test for this is rather involved, because we need the test file to be valid python2 syntax, but what we're testing is a SyntaxError in older versions of python. --- cocotb/decorators.py | 3 +- cocotb/result.py | 2 ++ tests/test_cases/test_cocotb/test_cocotb.py | 39 +++++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index db0c8df7..a9b7fc48 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -131,8 +131,9 @@ def send(self, value): self.retval = e.retval self._finished = True raise CoroutineComplete(callback=self._finished_cb) - except StopIteration: + except StopIteration as e: self._finished = True + self.retval = getattr(e, 'value', None) # for python >=3.3 raise CoroutineComplete(callback=self._finished_cb) except Exception as e: self._finished = True diff --git a/cocotb/result.py b/cocotb/result.py index 89f76d93..fcc9c0e4 100644 --- a/cocotb/result.py +++ b/cocotb/result.py @@ -68,6 +68,8 @@ def create_error(obj, msg): class ReturnValue(StopIteration): def __init__(self, retval): + # The base class in python >= 3.3 holds a return value too + super(ReturnValue, self).__init__(retval) self.retval = retval diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 1982cffe..134c6d5b 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -27,6 +27,8 @@ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' import logging +import sys +import textwrap """ A set of tests that demonstrate cocotb functionality @@ -666,3 +668,40 @@ def test_binary_value(dut): dut._log.info("vec = 'b%s" % vec.binstr) yield Timer(100) #Make it do something with time + + +# This is essentially six.exec_ +if sys.version_info.major == 3: + # this has to not be a syntax error in py2 + import builtins + exec_ = getattr(builtins, 'exec') +else: + # this has to not be a syntax error in py3 + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + +@cocotb.test(skip=sys.version_info[:2] < (3, 3)) +def test_coroutine_return(dut): + """ Test that the python 3.3 syntax for returning from generators works """ + # this would be a syntax error in older python, so we do the whole + # thing inside exec + exec_(textwrap.dedent(""" + @cocotb.coroutine + def return_it(x): + return x + yield + + ret = yield return_it(42) + if ret != 42: + raise TestFailure("Return statement did not work") + """)) From 56c4ec1428343b63caad02a9b576b4b5cf9b2403 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 28 Jul 2018 17:30:45 -0700 Subject: [PATCH 1395/2656] Use PyLong_FromVoidPtr for converting a pointer to an integer --- lib/simulator/simulatormodule.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 9b70708e..ed561727 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -223,7 +223,7 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) hdl = gpi_register_readonly_callback((gpi_function_t)handle_gpi_callback, callback_data_p); - PyObject *rv = Py_BuildValue("l", hdl); + PyObject *rv = PyLong_FromVoidPtr(hdl); DROP_GIL(gstate); FEXIT @@ -279,7 +279,7 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) hdl = gpi_register_readwrite_callback((gpi_function_t)handle_gpi_callback, callback_data_p); - PyObject *rv = Py_BuildValue("l", hdl); + PyObject *rv = PyLong_FromVoidPtr(hdl); DROP_GIL(gstate); FEXIT @@ -335,7 +335,7 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) hdl = gpi_register_nexttime_callback((gpi_function_t)handle_gpi_callback, callback_data_p); - PyObject *rv = Py_BuildValue("l", hdl); + PyObject *rv = PyLong_FromVoidPtr(hdl); DROP_GIL(gstate); FEXIT @@ -402,7 +402,7 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) hdl = gpi_register_timed_callback((gpi_function_t)handle_gpi_callback, callback_data_p, time_ps); // Check success - PyObject *rv = Py_BuildValue("l", hdl); + PyObject *rv = PyLong_FromVoidPtr(hdl); DROP_GIL(gstate); FEXIT @@ -476,7 +476,7 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) edge); // Check success - PyObject *rv = Py_BuildValue("l", hdl); + PyObject *rv = PyLong_FromVoidPtr(hdl); DROP_GIL(gstate); FEXIT @@ -502,7 +502,7 @@ static PyObject *iterate(PyObject *self, PyObject *args) result = gpi_iterate(hdl, (gpi_iterator_sel_t)type); - res = Py_BuildValue("l", result); + res = PyLong_FromVoidPtr(result); DROP_GIL(gstate); @@ -541,7 +541,7 @@ static PyObject *next(PyObject *self, PyObject *args) return NULL; } - res = Py_BuildValue("l", result); + res = PyLong_FromVoidPtr(result); DROP_GIL(gstate); @@ -766,7 +766,7 @@ static PyObject *get_handle_by_name(PyObject *self, PyObject *args) result = gpi_get_handle_by_name((gpi_sim_hdl)hdl, name); - res = Py_BuildValue("l", result); + res = PyLong_FromVoidPtr(result); DROP_GIL(gstate); @@ -790,7 +790,7 @@ static PyObject *get_handle_by_index(PyObject *self, PyObject *args) result = gpi_get_handle_by_index((gpi_sim_hdl)hdl, index); - value = Py_BuildValue("l", result); + value = PyLong_FromVoidPtr(result); DROP_GIL(gstate); @@ -818,7 +818,7 @@ static PyObject *get_root_handle(PyObject *self, PyObject *args) } - value = Py_BuildValue("l", result); + value = PyLong_FromVoidPtr(result); DROP_GIL(gstate); From 3a95ab7ac2d04805cdedf1cdac213a4abb7acf05 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 28 Jul 2018 17:35:20 -0700 Subject: [PATCH 1396/2656] BUG: `GPITrigger.cbhdl` is an integer, so can never be `None Change the comparisons to be against 0 instead (ie, a null pointer in C) --- cocotb/triggers.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index a8008cfd..7d13fcc5 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -91,18 +91,18 @@ def __init__(self): # if simulator is not None: # self.cbhdl = simulator.create_callback(self) # else: - self.cbhdl = None + self.cbhdl = 0 def unprime(self): """Disable a primed trigger, can be reprimed""" - if self.cbhdl: + if self.cbhdl != 0: simulator.deregister_callback(self.cbhdl) - self.cbhdl = None + self.cbhdl = 0 Trigger.unprime(self) def __del__(self): """Remove knowledge of the trigger""" - if self.cbhdl is not None: + if self.cbhdl != 0: self.unprime() Trigger.__del__(self) @@ -119,10 +119,10 @@ def __init__(self, time_ps, units=None): def prime(self, callback): """Register for a timed callback""" - if self.cbhdl is None: + if self.cbhdl == 0: self.cbhdl = simulator.register_timed_callback(self.sim_steps, callback, self) - if self.cbhdl is None: + if self.cbhdl == 0: raise_error(self, "Unable set up %s Trigger" % (str(self))) Trigger.prime(self) @@ -138,9 +138,9 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): - if self.cbhdl is None: + if self.cbhdl == 0: self.cbhdl = simulator.register_readonly_callback(callback, self) - if self.cbhdl is None: + if self.cbhdl == 0: raise_error(self, "Unable set up %s Trigger" % (str(self))) Trigger.prime(self) @@ -163,11 +163,11 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): - if self.cbhdl is None: + if self.cbhdl == 0: # import pdb # pdb.set_trace() self.cbhdl = simulator.register_rwsynch_callback(callback, self) - if self.cbhdl is None: + if self.cbhdl == 0: raise_error(self, "Unable set up %s Trigger" % (str(self))) Trigger.prime(self) @@ -189,9 +189,9 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): - if self.cbhdl is None: + if self.cbhdl == 0: self.cbhdl = simulator.register_nextstep_callback(callback, self) - if self.cbhdl is None: + if self.cbhdl == 0: raise_error(self, "Unable set up %s Trigger" % (str(self))) Trigger.prime(self) @@ -215,13 +215,13 @@ def __init__(self, signal): def prime(self, callback): """Register notification of a value change via a callback""" - if self.cbhdl is None: + if self.cbhdl == 0: self.cbhdl = simulator.register_value_change_callback(self.signal. _handle, callback, 3, self) - if self.cbhdl is None: + if self.cbhdl == 0: raise_error(self, "Unable set up %s Trigger" % (str(self))) Trigger.prime(self) @@ -241,13 +241,13 @@ def __init__(self, signal, rising): self._rising = 2 def prime(self, callback): - if self.cbhdl is None: + if self.cbhdl == 0: self.cbhdl = simulator.register_value_change_callback(self.signal. _handle, callback, self._rising, self) - if self.cbhdl is None: + if self.cbhdl == 0: raise_error(self, "Unable set up %s Trigger" % (str(self))) Trigger.prime(self) @@ -309,7 +309,7 @@ def _check(obj): _check, self._rising, self) - if self.cbhdl is None: + if self.cbhdl == 0: raise_error(self, "Unable set up %s Trigger" % (str(self))) self.cbhdl = simulator.register_value_change_callback(self.signal. @@ -317,7 +317,7 @@ def _check(obj): _check, self._rising, self) - if self.cbhdl is None: + if self.cbhdl == 0: raise_error(self, "Unable set up %s Trigger" % (str(self))) Trigger.prime(self) From e43525524407a33b6179b327414c622f0ac1c39f Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 28 Jul 2018 22:12:33 -0700 Subject: [PATCH 1397/2656] MAINT: Simplify double-negative in condition --- cocotb/scheduler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 83242a81..bd7621e8 100644 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -640,7 +640,7 @@ def schedule(self, coroutine, trigger=None): self._coroutine_yielded(coroutine, [result]) elif (isinstance(result, list) and - not [t for t in result if not isinstance(t, Trigger)]): + all(isinstance(t, Trigger) for t in result)): self._coroutine_yielded(coroutine, result) else: From 5009059edc9e961468e4b9a4f12eb449e380c27a Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 29 Jul 2018 15:07:11 -0700 Subject: [PATCH 1398/2656] MAINT: Move `exec_` to utils --- cocotb/utils.py | 20 +++++++ tests/test_cases/test_cocotb/test_cocotb.py | 62 ++++++++++++++------- 2 files changed, 61 insertions(+), 21 deletions(-) diff --git a/cocotb/utils.py b/cocotb/utils.py index 799a75ab..224b17e6 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -361,6 +361,26 @@ def highlight(string, colour=ANSI.YELLOW_FG): return rs +# This is essentially six.exec_ +if sys.version_info.major == 3: + # this has to not be a syntax error in py2 + import builtins + exec_ = getattr(builtins, 'exec') +else: + # this has to not be a syntax error in py3 + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + if __name__ == "__main__": import random a = "" diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 134c6d5b..7f6a9fde 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -670,32 +670,12 @@ def test_binary_value(dut): yield Timer(100) #Make it do something with time -# This is essentially six.exec_ -if sys.version_info.major == 3: - # this has to not be a syntax error in py2 - import builtins - exec_ = getattr(builtins, 'exec') -else: - # this has to not be a syntax error in py3 - def exec_(_code_, _globs_=None, _locs_=None): - """Execute code in a namespace.""" - if _globs_ is None: - frame = sys._getframe(1) - _globs_ = frame.f_globals - if _locs_ is None: - _locs_ = frame.f_locals - del frame - elif _locs_ is None: - _locs_ = _globs_ - exec("""exec _code_ in _globs_, _locs_""") - - @cocotb.test(skip=sys.version_info[:2] < (3, 3)) def test_coroutine_return(dut): """ Test that the python 3.3 syntax for returning from generators works """ # this would be a syntax error in older python, so we do the whole # thing inside exec - exec_(textwrap.dedent(""" + cocotb.utils.exec_(textwrap.dedent(""" @cocotb.coroutine def return_it(x): return x @@ -705,3 +685,43 @@ def return_it(x): if ret != 42: raise TestFailure("Return statement did not work") """)) + + +@cocotb.test(skip=sys.version_info[:2] < (3, 5)) +def test_async(dut): + # this would be a syntax error in older python, so we do the whole + # thing inside exec + cocotb.utils.exec_(textwrap.dedent(''' + # inner functions don't need the cocotb.coroutine decorator - but + # adding it should have no effect anyway + @cocotb.coroutine + async def wait_5_cycles_marked(): + await ClockCycles(clk, 5) + + async def wait_5_cycles_unmarked(): + await ClockCycles(clk, 5) + + + @cocotb.coroutine + async def test_clock_cycles_async(dut): + """ + Test the ClockCycles Trigger + """ + clk = dut.clk + + clk_gen = cocotb.fork(Clock(clk, 100).start()) + + await RisingEdge(clk) + + dut.log.info("After one edge") + + await wait_5_cycles_marked() + await wait_5_cycles_unmarked() + + dut.log.info("After 10 edges") + + # as along as an async function is marked with @cocotb.coroutine, it + # can be used in a non-async function, where `await` is a syntaxerror, + # with a yield + yield test_clock_cycles_async() + ''')) From 3def01dd0d24b5afc2f2f3264e8c50282fdeb821 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 29 Jul 2018 19:02:57 -0700 Subject: [PATCH 1399/2656] DOC: Explain how to return a value from a coroutine, and use a value returned from one --- documentation/source/coroutines.rst | 39 +++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/documentation/source/coroutines.rst b/documentation/source/coroutines.rst index c0ec8aa9..440b97bc 100644 --- a/documentation/source/coroutines.rst +++ b/documentation/source/coroutines.rst @@ -1,9 +1,9 @@ Coroutines ========== -Testbenches built using Cocotb use coroutines. While the coroutine is executing +Testbenches built using Cocotb use coroutines. While the coroutine is executing the simulation is paused. The coroutine uses the :keyword:`yield` keyword to -pass control of execution back to the simulator and simulation time can advance +pass control of execution back to the simulator and simulation time can advance again. Typically coroutines :keyword:`yield` a :py:class:`Trigger` object which @@ -11,7 +11,7 @@ indicates to the simulator some event which will cause the coroutine to be woken when it occurs. For example: .. code-block:: python - + @cocotb.coroutine def wait_10ns(): cocotb.log.info("About to wait for 10ns") @@ -21,18 +21,41 @@ when it occurs. For example: Coroutines may also yield other coroutines: .. code-block:: python - + @cocotb.coroutine def wait_100ns(): for i in range(10): yield wait_10ns() - -Coroutines may also yield a list of triggers to indicate that execution should +Coroutines can return a value, so that they can be used by other coroutines. +Before python 3.3, this requires a `ReturnValue` to be raised. + +.. code-block:: python + + @cocotb.coroutine + def get_signal(clk, signal): + yield RisingEdge(clk) + raise ReturnValue(signal.value) + + @cocotb.coroutine + def get_signal_python_33(clk, signal): + # newer versions of python can use return normally + yield RisingEdge(clk) + return signal.value + + @cocotb.coroutine + def check_signal_changes(dut): + first = yield get_signal(dut.clk, dut.signal) + second = yield get_signal(dut.clk, dut.signal) + if first == second: + raise TestFailure("Signal did not change") + + +Coroutines may also yield a list of triggers to indicate that execution should resume if *any* of them fires: .. code-block:: python - + @cocotb.coroutine def packet_with_timeout(monitor, timeout): """Wait for a packet but timeout if nothing arrives""" @@ -43,7 +66,7 @@ The trigger that caused execution to resume is passed back to the coroutine, allowing them to distinguish which trigger fired: .. code-block:: python - + @cocotb.coroutine def packet_with_timeout(monitor, timeout): """Wait for a packet but timeout if nothing arrives""" From 5c3929df0a8b49191c41061aa0aea5e13895f547 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Wed, 8 Aug 2018 23:44:48 -0700 Subject: [PATCH 1400/2656] MAINT: Don't acquire the GIL when called from a python thread All of these functions are callable only from python in the first place. So if someone is calling them, the caller must have already acquired the GIL. Only the GPI callback handler actually does need to acquire the GIL. --- lib/simulator/simulatormodule.c | 165 +------------------------------- 1 file changed, 1 insertion(+), 164 deletions(-) diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index ed561727..eb292833 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -183,9 +183,6 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) PyObject *function; gpi_sim_hdl hdl; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - p_callback_data callback_data_p; Py_ssize_t numargs = PyTuple_Size(args); @@ -224,7 +221,6 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) hdl = gpi_register_readonly_callback((gpi_function_t)handle_gpi_callback, callback_data_p); PyObject *rv = PyLong_FromVoidPtr(hdl); - DROP_GIL(gstate); FEXIT return rv; @@ -239,9 +235,6 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) PyObject *function; gpi_sim_hdl hdl; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - p_callback_data callback_data_p; Py_ssize_t numargs = PyTuple_Size(args); @@ -280,7 +273,6 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) hdl = gpi_register_readwrite_callback((gpi_function_t)handle_gpi_callback, callback_data_p); PyObject *rv = PyLong_FromVoidPtr(hdl); - DROP_GIL(gstate); FEXIT return rv; @@ -295,9 +287,6 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) PyObject *function; gpi_sim_hdl hdl; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - p_callback_data callback_data_p; Py_ssize_t numargs = PyTuple_Size(args); @@ -336,7 +325,6 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) hdl = gpi_register_nexttime_callback((gpi_function_t)handle_gpi_callback, callback_data_p); PyObject *rv = PyLong_FromVoidPtr(hdl); - DROP_GIL(gstate); FEXIT return rv; @@ -358,9 +346,6 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) p_callback_data callback_data_p; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - Py_ssize_t numargs = PyTuple_Size(args); if (numargs < 2) { @@ -403,7 +388,6 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) // Check success PyObject *rv = PyLong_FromVoidPtr(hdl); - DROP_GIL(gstate); FEXIT return rv; @@ -424,9 +408,6 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) gpi_sim_hdl hdl; unsigned int edge; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - p_callback_data callback_data_p; Py_ssize_t numargs = PyTuple_Size(args); @@ -477,8 +458,6 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) // Check success PyObject *rv = PyLong_FromVoidPtr(hdl); - - DROP_GIL(gstate); FEXIT return rv; @@ -492,11 +471,7 @@ static PyObject *iterate(PyObject *self, PyObject *args) gpi_iterator_hdl result; PyObject *res; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "li", &hdl, &type)) { - DROP_GIL(gstate); return NULL; } @@ -504,8 +479,6 @@ static PyObject *iterate(PyObject *self, PyObject *args) res = PyLong_FromVoidPtr(result); - DROP_GIL(gstate); - return res; } @@ -516,11 +489,7 @@ static PyObject *next(PyObject *self, PyObject *args) gpi_sim_hdl result; PyObject *res; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { - DROP_GIL(gstate); return NULL; } @@ -528,7 +497,6 @@ static PyObject *next(PyObject *self, PyObject *args) // intuitive we simply raise StopIteration on the first iteration if (!hdl) { PyErr_SetNone(PyExc_StopIteration); - DROP_GIL(gstate); return NULL; } @@ -537,14 +505,11 @@ static PyObject *next(PyObject *self, PyObject *args) // Raise stopiteration when we're done if (!result) { PyErr_SetNone(PyExc_StopIteration); - DROP_GIL(gstate); return NULL; } res = PyLong_FromVoidPtr(result); - DROP_GIL(gstate); - return res; } @@ -555,19 +520,13 @@ static PyObject *get_signal_val_binstr(PyObject *self, PyObject *args) const char *result; PyObject *retstr; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { - DROP_GIL(gstate); return NULL; } result = gpi_get_signal_value_binstr(hdl); retstr = Py_BuildValue("s", result); - DROP_GIL(gstate); - return retstr; } @@ -577,19 +536,13 @@ static PyObject *get_signal_val_str(PyObject *self, PyObject *args) const char *result; PyObject *retstr; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { - DROP_GIL(gstate); return NULL; } result = gpi_get_signal_value_str(hdl); retstr = Py_BuildValue("s", result); - DROP_GIL(gstate); - return retstr; } @@ -599,19 +552,13 @@ static PyObject *get_signal_val_real(PyObject *self, PyObject *args) double result; PyObject *retval; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { - DROP_GIL(gstate); return NULL; } result = gpi_get_signal_value_real(hdl); retval = Py_BuildValue("d", result); - DROP_GIL(gstate); - return retval; } @@ -622,19 +569,13 @@ static PyObject *get_signal_val_long(PyObject *self, PyObject *args) long result; PyObject *retval; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { - DROP_GIL(gstate); return NULL; } result = gpi_get_signal_value_long(hdl); retval = Py_BuildValue("l", result); - DROP_GIL(gstate); - return retval; } @@ -645,19 +586,13 @@ static PyObject *set_signal_val_str(PyObject *self, PyObject *args) const char *binstr; PyObject *res; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "ls", &hdl, &binstr)) { - DROP_GIL(gstate); return NULL; } gpi_set_signal_value_str(hdl,binstr); res = Py_BuildValue("s", "OK!"); - DROP_GIL(gstate); - return res; } @@ -667,19 +602,13 @@ static PyObject *set_signal_val_real(PyObject *self, PyObject *args) double value; PyObject *res; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "ld", &hdl, &value)) { - DROP_GIL(gstate); return NULL; } gpi_set_signal_value_real(hdl, value); res = Py_BuildValue("s", "OK!"); - DROP_GIL(gstate); - return res; } @@ -689,19 +618,13 @@ static PyObject *set_signal_val_long(PyObject *self, PyObject *args) long value; PyObject *res; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "ll", &hdl, &value)) { - DROP_GIL(gstate); return NULL; } gpi_set_signal_value_long(hdl, value); res = Py_BuildValue("s", "OK!"); - DROP_GIL(gstate); - return res; } @@ -711,19 +634,13 @@ static PyObject *get_definition_name(PyObject *self, PyObject *args) gpi_sim_hdl hdl; PyObject *retstr; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { - DROP_GIL(gstate); return NULL; } result = gpi_get_definition_name((gpi_sim_hdl)hdl); retstr = Py_BuildValue("s", result); - DROP_GIL(gstate); - return retstr; } @@ -733,19 +650,13 @@ static PyObject *get_definition_file(PyObject *self, PyObject *args) gpi_sim_hdl hdl; PyObject *retstr; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { - DROP_GIL(gstate); return NULL; } result = gpi_get_definition_file((gpi_sim_hdl)hdl); retstr = Py_BuildValue("s", result); - DROP_GIL(gstate); - return retstr; } @@ -756,11 +667,7 @@ static PyObject *get_handle_by_name(PyObject *self, PyObject *args) gpi_sim_hdl result; PyObject *res; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "ls", &hdl, &name)) { - DROP_GIL(gstate); return NULL; } @@ -768,8 +675,6 @@ static PyObject *get_handle_by_name(PyObject *self, PyObject *args) res = PyLong_FromVoidPtr(result); - DROP_GIL(gstate); - return res; } @@ -780,11 +685,7 @@ static PyObject *get_handle_by_index(PyObject *self, PyObject *args) gpi_sim_hdl result; PyObject *value; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "li", &hdl, &index)) { - DROP_GIL(gstate); return NULL; } @@ -792,8 +693,6 @@ static PyObject *get_handle_by_index(PyObject *self, PyObject *args) value = PyLong_FromVoidPtr(result); - DROP_GIL(gstate); - return value; } @@ -803,25 +702,18 @@ static PyObject *get_root_handle(PyObject *self, PyObject *args) gpi_sim_hdl result; PyObject *value; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "z", &name)) { - DROP_GIL(gstate); return NULL; } result = gpi_get_root_handle(name); if (NULL == result) { - DROP_GIL(gstate); Py_RETURN_NONE; } value = PyLong_FromVoidPtr(result); - DROP_GIL(gstate); - return value; } @@ -832,19 +724,13 @@ static PyObject *get_name_string(PyObject *self, PyObject *args) gpi_sim_hdl hdl; PyObject *retstr; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { - DROP_GIL(gstate); return NULL; } result = gpi_get_signal_name_str((gpi_sim_hdl)hdl); retstr = Py_BuildValue("s", result); - DROP_GIL(gstate); - return retstr; } @@ -854,19 +740,13 @@ static PyObject *get_type(PyObject *self, PyObject *args) gpi_sim_hdl hdl; PyObject *pyresult; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { - DROP_GIL(gstate); return NULL; } result = gpi_get_object_type((gpi_sim_hdl)hdl); pyresult = Py_BuildValue("i", result); - DROP_GIL(gstate); - return pyresult; } @@ -876,19 +756,13 @@ static PyObject *get_const(PyObject *self, PyObject *args) gpi_sim_hdl hdl; PyObject *pyresult; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { - DROP_GIL(gstate); return NULL; } result = gpi_is_constant((gpi_sim_hdl)hdl); pyresult = Py_BuildValue("i", result); - DROP_GIL(gstate); - return pyresult; } @@ -898,19 +772,13 @@ static PyObject *get_type_string(PyObject *self, PyObject *args) gpi_sim_hdl hdl; PyObject *retstr; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { - DROP_GIL(gstate); return NULL; } result = gpi_get_signal_type_str((gpi_sim_hdl)hdl); retstr = Py_BuildValue("s", result); - DROP_GIL(gstate); - return retstr; } @@ -922,22 +790,16 @@ static PyObject *get_sim_time(PyObject *self, PyObject *args) { struct sim_time local_time; - PyGILState_STATE gstate; - if (context) { gpi_get_sim_time(&local_time.high, &local_time.low); } else { local_time = cache_time; } - gstate = TAKE_GIL(); - PyObject *pTuple = PyTuple_New(2); PyTuple_SetItem(pTuple, 0, PyLong_FromUnsignedLong(local_time.high)); // Note: This function “steals†a reference to o. PyTuple_SetItem(pTuple, 1, PyLong_FromUnsignedLong(local_time.low)); // Note: This function “steals†a reference to o. - DROP_GIL(gstate); - return pTuple; } @@ -945,14 +807,10 @@ static PyObject *get_precision(PyObject *self, PyObject *args) { int32_t precision; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - gpi_get_sim_precision(&precision); PyObject *retint = Py_BuildValue("i", precision); - - DROP_GIL(gstate); + return retint; } @@ -962,19 +820,13 @@ static PyObject *get_num_elems(PyObject *self, PyObject *args) gpi_sim_hdl hdl; PyObject *retstr; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { - DROP_GIL(gstate); return NULL; } int elems = gpi_get_num_elems((gpi_sim_hdl)hdl); retstr = Py_BuildValue("i", elems); - DROP_GIL(gstate); - return retstr; } @@ -983,11 +835,7 @@ static PyObject *get_range(PyObject *self, PyObject *args) gpi_sim_hdl hdl; PyObject *retstr; - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { - DROP_GIL(gstate); return NULL; } @@ -1000,8 +848,6 @@ static PyObject *get_range(PyObject *self, PyObject *args) else retstr = Py_BuildValue(""); - DROP_GIL(gstate); - return retstr; } @@ -1020,9 +866,6 @@ static PyObject *deregister_callback(PyObject *self, PyObject *args) FENTER - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - pSihHdl = PyTuple_GetItem(args, 0); hdl = (gpi_sim_hdl)PyLong_AsLong(pSihHdl); @@ -1030,8 +873,6 @@ static PyObject *deregister_callback(PyObject *self, PyObject *args) value = Py_BuildValue("s", "OK!"); - DROP_GIL(gstate); - FEXIT return value; } @@ -1040,9 +881,7 @@ static PyObject *log_level(PyObject *self, PyObject *args) { enum gpi_log_levels new_level; PyObject *py_level; - PyGILState_STATE gstate; PyObject *value; - gstate = TAKE_GIL(); py_level = PyTuple_GetItem(args, 0); new_level = (enum gpi_log_levels)PyLong_AsLong(py_level); @@ -1051,8 +890,6 @@ static PyObject *log_level(PyObject *self, PyObject *args) value = Py_BuildValue("s", "OK!"); - DROP_GIL(gstate); - return value; } From 465ea2cf793ad9a95d24f3d5fc9a27d204d22d55 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Wed, 8 Aug 2018 23:45:22 -0700 Subject: [PATCH 1401/2656] BUG: Don't do a hard exit if malloc fails PyErr_NoMemory() is the typical response to a memory allocation failing - it gives python a chance to unwind the stack to free up memory, and provides the user with a traceback. --- lib/simulator/simulatormodule.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index eb292833..6518a750 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -208,7 +208,7 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); if (callback_data_p == NULL) { - LOG_CRITICAL("Failed to allocate user data\n"); + return PyErr_NoMemory(); } // Set up the user data (no more python API calls after this! @@ -260,7 +260,7 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); if (callback_data_p == NULL) { - LOG_CRITICAL("Failed to allocate user data\n"); + return PyErr_NoMemory(); } // Set up the user data (no more python API calls after this! @@ -312,7 +312,7 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); if (callback_data_p == NULL) { - LOG_CRITICAL("Failed to allocate user data\n"); + return PyErr_NoMemory(); } // Set up the user data (no more python API calls after this! @@ -374,7 +374,7 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); if (callback_data_p == NULL) { - LOG_CRITICAL("Failed to allocate user data\n"); + return PyErr_NoMemory(); } // Set up the user data (no more python API calls after this! @@ -440,7 +440,7 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); if (callback_data_p == NULL) { - LOG_CRITICAL("Failed to allocate user data\n"); + return PyErr_NoMemory(); } // Set up the user data (no more python API calls after this! From 5af440f18d02835f1921d9b4738eb641a660b3f9 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Wed, 8 Aug 2018 23:45:57 -0700 Subject: [PATCH 1402/2656] BUG: Don't segfault if the tuple slice fails PyTuple_GetSlice can fail if malloc fails, and this would go on to dereference a NULL pointer. Also removes special casing for zero-argument tuples, which slice just fine. --- lib/simulator/simulatormodule.c | 41 ++++++++++++++++----------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 6518a750..9012618f 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -201,10 +201,10 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) Py_INCREF(function); // Remaining args for function - if (numargs > 1) - fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference - else - fArgs = PyTuple_New(0); // args must not be NULL, use an empty tuple if no arguments are needed. + fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference + if (fArgs == NULL) { + return NULL; + } callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); if (callback_data_p == NULL) { @@ -253,10 +253,10 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) Py_INCREF(function); // Remaining args for function - if (numargs > 1) - fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference - else - fArgs = PyTuple_New(0); // args must not be NULL, use an empty tuple if no arguments are needed. + fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference + if (fArgs == NULL) { + return NULL; + } callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); if (callback_data_p == NULL) { @@ -305,10 +305,10 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) Py_INCREF(function); // Remaining args for function - if (numargs > 1) - fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference - else - fArgs = PyTuple_New(0); // args must not be NULL, use an empty tuple if no arguments are needed. + fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference + if (fArgs == NULL) { + return NULL; + } callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); if (callback_data_p == NULL) { @@ -366,11 +366,10 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) Py_INCREF(function); // Remaining args for function - if (numargs > 2) - fArgs = PyTuple_GetSlice(args, 2, numargs); // New reference - else - fArgs = PyTuple_New(0); // args must not be NULL, use an empty tuple if no arguments are needed. - + fArgs = PyTuple_GetSlice(args, 2, numargs); // New reference + if (fArgs == NULL) { + return NULL; + } callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); if (callback_data_p == NULL) { @@ -432,10 +431,10 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) edge = (unsigned int)PyLong_AsLong(pedge); // Remaining args for function - if (numargs > 3) - fArgs = PyTuple_GetSlice(args, 3, numargs); // New reference - else - fArgs = PyTuple_New(0); // args must not be NULL, use an empty tuple if no arguments are needed. + fArgs = PyTuple_GetSlice(args, 3, numargs); // New reference + if (fArgs == NULL) { + return NULL; + } callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); From 5459e3e67e4ff072d24bdb744ad57152eb6acd8b Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 9 Aug 2018 00:19:43 -0700 Subject: [PATCH 1403/2656] BUG: Do not segfault if an invalid handle is passed to register_value_change_callback or deregister_callback Fixed here: * handles work correctly on platforms with a 32-bit long but 64-bit pointers, rather than being truncated * non-integer handles cause an exception, not a null pointer dereference * negative handles do the same --- lib/simulator/simulatormodule.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 9012618f..53644026 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -417,7 +417,14 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) } PyObject *pSihHdl = PyTuple_GetItem(args, 0); - sig_hdl = (gpi_sim_hdl)PyLong_AsLong(pSihHdl); + sig_hdl = (gpi_sim_hdl)PyLong_AsVoidPtr(pSihHdl); + if (sig_hdl == NULL) { + // distinguish "parsing failed" from "parsed as NULL" + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_ValueError, "handle cannot be 0"); + } + return NULL; + } // Extract the callback function function = PyTuple_GetItem(args, 1); @@ -866,7 +873,14 @@ static PyObject *deregister_callback(PyObject *self, PyObject *args) FENTER pSihHdl = PyTuple_GetItem(args, 0); - hdl = (gpi_sim_hdl)PyLong_AsLong(pSihHdl); + hdl = (gpi_sim_hdl)PyLong_AsVoidPtr(pSihHdl); + if (hdl == NULL) { + // distinguish "parsing failed" from "parsed as NULL" + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_ValueError, "handle cannot be 0"); + } + return NULL; + } gpi_deregister_callback(hdl); From 274a90ce4b7386caab8d1250e81c0ff9fc82024b Mon Sep 17 00:00:00 2001 From: Andreas Galauner Date: Sun, 19 Aug 2018 05:38:35 +0200 Subject: [PATCH 1404/2656] Fix cocotb on Python 3.7 --- cocotb/log.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cocotb/log.py b/cocotb/log.py index c378fdc6..7555fb64 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -65,6 +65,7 @@ def __init__(self, name): else: hdlr.setFormatter(SimLogFormatter()) self.colour = False + self._cache = {} self.name = name self.handlers = [] self.disabled = False From 341c7ffabbc771105bb859c1e4bc6f58664ccda6 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Mon, 20 Aug 2018 19:36:00 -0700 Subject: [PATCH 1405/2656] BUG: Fix RuntimeErrors on python 3.7 PEP 479 makes it illegal to manually throw StopIteration in a generator, which in turn makes all of the `cocotb.result.*` exceptions unusable. This emits warnings from python 3.6, and is an error on 3.7 (or with `from __future__ import generator_stop` on 3.6) The simple fix here is to just not derive from StopIteration. https://www.python.org/dev/peps/pep-0479/ --- cocotb/decorators.py | 4 ++-- cocotb/result.py | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index a9b7fc48..4377afff 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -60,7 +60,7 @@ def public(f): @public -class CoroutineComplete(StopIteration): +class CoroutineComplete(Exception): """ To ensure that a coroutine has completed before we fire any triggers that are blocked waiting for the coroutine to end, we create a subclass @@ -68,7 +68,7 @@ class CoroutineComplete(StopIteration): here. """ def __init__(self, text="", callback=None): - StopIteration.__init__(self, text) + Exception.__init__(self, text) self.callback = callback diff --git a/cocotb/result.py b/cocotb/result.py index fcc9c0e4..6d16f9d6 100644 --- a/cocotb/result.py +++ b/cocotb/result.py @@ -66,14 +66,12 @@ def create_error(obj, msg): return TestError("Creating error traceback failed") -class ReturnValue(StopIteration): +class ReturnValue(Exception): def __init__(self, retval): - # The base class in python >= 3.3 holds a return value too - super(ReturnValue, self).__init__(retval) self.retval = retval -class TestComplete(StopIteration): +class TestComplete(Exception): """ Exceptions are used to pass test results around. """ @@ -83,7 +81,7 @@ def __init__(self, *args, **kwargs): self.stderr = StringIO() -class ExternalException(StopIteration): +class ExternalException(Exception): def __init__(self, exception): self.exception = exception From f50a14d46e014dac7f70449408c5dd16c93dabe8 Mon Sep 17 00:00:00 2001 From: Jeroen van Straten Date: Thu, 23 Aug 2018 12:02:44 +0200 Subject: [PATCH 1406/2656] Use PyLong_AsVoidPtr everywhere now that PyLong_FromVoidPtr is used in the other direction --- lib/simulator/simulatormodule.c | 60 +++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index ed561727..f93e758f 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -62,6 +62,18 @@ struct sim_time { static struct sim_time cache_time; +// Converter function for turning a Python long into a sim handle, such that it +// can be used by PyArg_ParseTuple format O&. +static int gpi_sim_hdl_converter(PyObject *o, gpi_sim_hdl *data) +{ + void *p = PyLong_AsVoidPtr(o); + if ((p == NULL) && PyErr_Occurred()) { + return 0; + } + *data = (gpi_sim_hdl)p; + return 1; +} + /** * @name Callback Handling * @brief Handle a callback coming from GPI @@ -437,7 +449,10 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) } PyObject *pSihHdl = PyTuple_GetItem(args, 0); - sig_hdl = (gpi_sim_hdl)PyLong_AsLong(pSihHdl); + if (!gpi_sim_hdl_converter(pSihHdl, &sig_hdl)) { + DROP_GIL(gstate); + return NULL; + } // Extract the callback function function = PyTuple_GetItem(args, 1); @@ -495,7 +510,7 @@ static PyObject *iterate(PyObject *self, PyObject *args) PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "li", &hdl, &type)) { + if (!PyArg_ParseTuple(args, "O&i", gpi_sim_hdl_converter, &hdl, &type)) { DROP_GIL(gstate); return NULL; } @@ -519,7 +534,7 @@ static PyObject *next(PyObject *self, PyObject *args) PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { + if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { DROP_GIL(gstate); return NULL; } @@ -558,7 +573,7 @@ static PyObject *get_signal_val_binstr(PyObject *self, PyObject *args) PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { + if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { DROP_GIL(gstate); return NULL; } @@ -580,7 +595,7 @@ static PyObject *get_signal_val_str(PyObject *self, PyObject *args) PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { + if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { DROP_GIL(gstate); return NULL; } @@ -602,7 +617,7 @@ static PyObject *get_signal_val_real(PyObject *self, PyObject *args) PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { + if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { DROP_GIL(gstate); return NULL; } @@ -625,7 +640,7 @@ static PyObject *get_signal_val_long(PyObject *self, PyObject *args) PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { + if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { DROP_GIL(gstate); return NULL; } @@ -648,7 +663,7 @@ static PyObject *set_signal_val_str(PyObject *self, PyObject *args) PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "ls", &hdl, &binstr)) { + if (!PyArg_ParseTuple(args, "O&s", gpi_sim_hdl_converter, &hdl, &binstr)) { DROP_GIL(gstate); return NULL; } @@ -670,7 +685,7 @@ static PyObject *set_signal_val_real(PyObject *self, PyObject *args) PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "ld", &hdl, &value)) { + if (!PyArg_ParseTuple(args, "O&d", gpi_sim_hdl_converter, &hdl, &value)) { DROP_GIL(gstate); return NULL; } @@ -692,7 +707,7 @@ static PyObject *set_signal_val_long(PyObject *self, PyObject *args) PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "ll", &hdl, &value)) { + if (!PyArg_ParseTuple(args, "O&l", gpi_sim_hdl_converter, &hdl, &value)) { DROP_GIL(gstate); return NULL; } @@ -714,7 +729,7 @@ static PyObject *get_definition_name(PyObject *self, PyObject *args) PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { + if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { DROP_GIL(gstate); return NULL; } @@ -736,7 +751,7 @@ static PyObject *get_definition_file(PyObject *self, PyObject *args) PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { + if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { DROP_GIL(gstate); return NULL; } @@ -759,7 +774,7 @@ static PyObject *get_handle_by_name(PyObject *self, PyObject *args) PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "ls", &hdl, &name)) { + if (!PyArg_ParseTuple(args, "O&s", gpi_sim_hdl_converter, &hdl, &name)) { DROP_GIL(gstate); return NULL; } @@ -783,7 +798,7 @@ static PyObject *get_handle_by_index(PyObject *self, PyObject *args) PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "li", &hdl, &index)) { + if (!PyArg_ParseTuple(args, "O&i", gpi_sim_hdl_converter, &hdl, &index)) { DROP_GIL(gstate); return NULL; } @@ -835,7 +850,7 @@ static PyObject *get_name_string(PyObject *self, PyObject *args) PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { + if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { DROP_GIL(gstate); return NULL; } @@ -857,7 +872,7 @@ static PyObject *get_type(PyObject *self, PyObject *args) PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { + if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { DROP_GIL(gstate); return NULL; } @@ -879,7 +894,7 @@ static PyObject *get_const(PyObject *self, PyObject *args) PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { + if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { DROP_GIL(gstate); return NULL; } @@ -901,7 +916,7 @@ static PyObject *get_type_string(PyObject *self, PyObject *args) PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { + if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { DROP_GIL(gstate); return NULL; } @@ -965,7 +980,7 @@ static PyObject *get_num_elems(PyObject *self, PyObject *args) PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { + if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { DROP_GIL(gstate); return NULL; } @@ -986,7 +1001,7 @@ static PyObject *get_range(PyObject *self, PyObject *args) PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "l", &hdl)) { + if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { DROP_GIL(gstate); return NULL; } @@ -1024,7 +1039,10 @@ static PyObject *deregister_callback(PyObject *self, PyObject *args) gstate = TAKE_GIL(); pSihHdl = PyTuple_GetItem(args, 0); - hdl = (gpi_sim_hdl)PyLong_AsLong(pSihHdl); + if (!gpi_sim_hdl_converter(pSihHdl, &hdl)) { + DROP_GIL(gstate); + return NULL; + } gpi_deregister_callback(hdl); From 1dca723643798836fe6c2931ae032bf16661a224 Mon Sep 17 00:00:00 2001 From: Jeroen van Straten Date: Fri, 24 Aug 2018 11:43:44 +0200 Subject: [PATCH 1407/2656] Fix usage of gpi_sim_hdl_converter for parsing gpi_iterator_hdl --- lib/simulator/simulatormodule.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index f93e758f..c95713b1 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -74,6 +74,17 @@ static int gpi_sim_hdl_converter(PyObject *o, gpi_sim_hdl *data) return 1; } +// Same as above, for an iterator handle. +static int gpi_iterator_hdl_converter(PyObject *o, gpi_iterator_hdl *data) +{ + void *p = PyLong_AsVoidPtr(o); + if ((p == NULL) && PyErr_Occurred()) { + return 0; + } + *data = (gpi_iterator_hdl)p; + return 1; +} + /** * @name Callback Handling * @brief Handle a callback coming from GPI @@ -534,7 +545,7 @@ static PyObject *next(PyObject *self, PyObject *args) PyGILState_STATE gstate; gstate = TAKE_GIL(); - if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { + if (!PyArg_ParseTuple(args, "O&", gpi_iterator_hdl_converter, &hdl)) { DROP_GIL(gstate); return NULL; } From d17d4c48fdc2dedde067351b9697390ec3e132f4 Mon Sep 17 00:00:00 2001 From: Jeroen van Straten Date: Fri, 24 Aug 2018 11:44:21 +0200 Subject: [PATCH 1408/2656] Throw an error when deregister_callback is called with too many arguments --- lib/simulator/simulatormodule.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index c95713b1..1fb8f96c 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -1041,7 +1041,6 @@ static PyObject *stop_simulator(PyObject *self, PyObject *args) static PyObject *deregister_callback(PyObject *self, PyObject *args) { gpi_sim_hdl hdl; - PyObject *pSihHdl; PyObject *value; FENTER @@ -1049,8 +1048,7 @@ static PyObject *deregister_callback(PyObject *self, PyObject *args) PyGILState_STATE gstate; gstate = TAKE_GIL(); - pSihHdl = PyTuple_GetItem(args, 0); - if (!gpi_sim_hdl_converter(pSihHdl, &hdl)) { + if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { DROP_GIL(gstate); return NULL; } From 2711bb8d8c3f996e7b6310252c1bd1c762dc3564 Mon Sep 17 00:00:00 2001 From: Jeroen van Straten Date: Tue, 4 Sep 2018 11:10:27 +0200 Subject: [PATCH 1409/2656] Ensure that stdout/stderr are buffered --- cocotb/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 84622607..c3e9af2d 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -37,6 +37,12 @@ import random import time +# If stdout/stderr are not line buffered because they're pipes, make them line +# buffered now to ensure that prints such as stack traces always appear. +if not sys.stdout.line_buffering: + sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1) +if not sys.stderr.line_buffering: + sys.stderr = os.fdopen(sys.stderr.fileno(), 'w', 1) import cocotb.handle from cocotb.scheduler import Scheduler From f507dfa6424984f97bd056e6143d7e2df0b24968 Mon Sep 17 00:00:00 2001 From: Jeroen van Straten Date: Tue, 4 Sep 2018 11:11:18 +0200 Subject: [PATCH 1410/2656] Revert "Added exception traceback logging for call to regression.initialisation() during CocoTb init phase." Previous commit already ensures that this exception is printed, so without revert this exception is printed twice This reverts commit 224a1562cc5eb95a5daef99cac6f351f6026bf6f. --- cocotb/regression.py | 8 -------- 1 file changed, 8 deletions(-) mode change 100755 => 100644 cocotb/regression.py diff --git a/cocotb/regression.py b/cocotb/regression.py old mode 100755 new mode 100644 index 1b99cbd5..fe906bbf --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -91,14 +91,6 @@ def __init__(self, root_name, modules, tests=None, seed=None, hooks=[]): self._hooks = hooks def initialise(self): - try: - self._initialise() - except Exception as e: - import traceback - self.log.error(traceback.format_exc()) - raise - - def _initialise(self): self.start_time = time.time() self.test_results = [] From c5f80bcac0380050006be1c6c591f33606c8040a Mon Sep 17 00:00:00 2001 From: Jeroen van Straten Date: Tue, 4 Sep 2018 11:56:43 +0200 Subject: [PATCH 1411/2656] Fix exception handling for test module import failure --- cocotb/regression.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 1b99cbd5..d6f7dcac 100755 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -137,7 +137,7 @@ def _initialise(self): self.log.debug("PWD: " + os.getcwd()) module = _my_import(module_name) except Exception as E: - self.log.critical("Failed to import module %s: %s", (module_name, E)) + self.log.critical("Failed to import module %s: %s", module_name, E) self.log.info("MODULE variable was \"%s\"", ".".join(self._modules)) self.log.info("Traceback: ") self.log.info(traceback.format_exc()) From 1e010e6b7bb7ebbc494a9e4d78c8c5067f46ea72 Mon Sep 17 00:00:00 2001 From: Jeroen van Straten Date: Tue, 4 Sep 2018 11:59:24 +0200 Subject: [PATCH 1412/2656] Fix error message for missing $MODULE --- cocotb/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 84622607..57785655 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -142,8 +142,8 @@ def _initialise_testbench(root_name): hooks_str = os.getenv('COCOTB_HOOKS', '') if not module_str: - raise ImportError("Environment variables defining the module(s) to \ - execute not defined. MODULE=\"%s\"\"" % (module_str)) + raise ImportError("Environment variables defining the module(s) to " + + "execute not defined. MODULE=\"%s\"" % (module_str)) modules = module_str.split(',') hooks = hooks_str.split(',') if hooks_str else [] From 075a3d61e6e229729801ba8ca8a7a2252cdd8b4b Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 8 Sep 2018 20:41:38 -0700 Subject: [PATCH 1413/2656] Move null-checks into converter --- lib/simulator/simulatormodule.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index f0e2817d..32fa8df5 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -70,6 +70,10 @@ static int gpi_sim_hdl_converter(PyObject *o, gpi_sim_hdl *data) if ((p == NULL) && PyErr_Occurred()) { return 0; } + if (p == NULL) { + PyErr_SetString(PyExc_ValueError, "handle cannot be 0"); + return 0; + } *data = (gpi_sim_hdl)p; return 1; } @@ -81,6 +85,10 @@ static int gpi_iterator_hdl_converter(PyObject *o, gpi_iterator_hdl *data) if ((p == NULL) && PyErr_Occurred()) { return 0; } + if (p == NULL) { + PyErr_SetString(PyExc_ValueError, "handle cannot be 0"); + return 0; + } *data = (gpi_iterator_hdl)p; return 1; } @@ -443,10 +451,6 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) if (!gpi_sim_hdl_converter(pSihHdl, &sig_hdl)) { return NULL; } - if (sig_hdl == NULL) { - PyErr_SetString(PyExc_ValueError, "handle cannot be 0"); - return NULL; - } // Extract the callback function function = PyTuple_GetItem(args, 1); @@ -896,10 +900,6 @@ static PyObject *deregister_callback(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { return NULL; } - if (hdl == NULL) { - PyErr_SetString(PyExc_ValueError, "handle cannot be 0"); - return NULL; - } gpi_deregister_callback(hdl); From 465bd4e93ee8d240c46f3511077ec7a17a0662b0 Mon Sep 17 00:00:00 2001 From: nuess0r Date: Tue, 11 Sep 2018 21:31:12 +0200 Subject: [PATCH 1414/2656] Updated documentation to reflect which Modelsim versions support FLI I was in contact with the Modelsim support to clarify which versions of Modelsim support FLI and which do not. --- documentation/source/introduction.rst | 4 ++-- documentation/source/simulator_support.rst | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index 36485601..f2d72a54 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -18,13 +18,13 @@ Linux Platforms * `Aldec `_ Riviera-PRO * `Synopsys `_ VCS * `Cadence `_ Incisive -* `Mentor `_ Modelsim +* `Mentor `_ Modelsim (DE and SE) Windows Platform * `Icarus Verilog `_ * `Aldec `_ Riviera-PRO -* `Mentor `_ Modelsim +* `Mentor `_ Modelsim (DE and SE) **Cocotb** can be used live in a web-browser using `EDA Playground `_. diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 36f80182..fa808dd0 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -25,6 +25,17 @@ Aldec Riviera-PRO Mentor Questa ------------- +Mentor Modelsim +--------------- +Any ModelSim-PE or ModelSim-PE derivative (like ModelSim Microsemi, Altera, Lattice Edition) does not support the VHDL FLI feature. +If you try to run with FLI enabled, you will see a vsim-FLI-3155 error: + +.. code-block:: bash + + ** Error (suppressible): (vsim-FLI-3155) The FLI is not enabled in this version of ModelSim. + +ModelSim DE and SE (and Questa, of course) supports the FLI. + Cadence Incisive ---------------- From 23dce5baed56540d3eb335e8dbd160de0d268f2a Mon Sep 17 00:00:00 2001 From: Jeroen van Straten Date: Wed, 12 Sep 2018 13:42:32 +0200 Subject: [PATCH 1415/2656] Improved compatibility of stdout/stderr line buffering across platforms --- cocotb/__init__.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index c3e9af2d..f94a8c12 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -37,13 +37,6 @@ import random import time -# If stdout/stderr are not line buffered because they're pipes, make them line -# buffered now to ensure that prints such as stack traces always appear. -if not sys.stdout.line_buffering: - sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1) -if not sys.stderr.line_buffering: - sys.stderr = os.fdopen(sys.stderr.fileno(), 'w', 1) - import cocotb.handle from cocotb.scheduler import Scheduler from cocotb.log import SimLogFormatter, SimBaseLog, SimLog @@ -76,6 +69,21 @@ # Notify GPI of log level simulator.log_level(_default_log) + # If stdout/stderr are not TTYs, Python may not have opened them with line + # buffering. In that case, try to reopen them with line buffering + # explicitly enabled. This ensures that prints such as stack traces always + # appear. Continue silently if this fails. + try: + if not sys.stdout.isatty(): + sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1) + log.debug("Reopened stdout with line buffering") + if not sys.stderr.isatty(): + sys.stderr = os.fdopen(sys.stderr.fileno(), 'w', 1) + log.debug("Reopened stderr with line buffering") + except Exception as e: + log.warning("Failed to ensure that stdout/stderr are line buffered: %s", e) + log.warning("Some stack traces may not appear because of this.") + scheduler = Scheduler() regression = None From 793fb695c7f5cf0372f7dee0c7d617b108a685a3 Mon Sep 17 00:00:00 2001 From: Martin Hofherr Date: Tue, 18 Sep 2018 09:35:23 +0200 Subject: [PATCH 1416/2656] Fix AvalonSTPkts _send_string As pointed out by issue #662 the ping tun tap example crashes, because a write is attempted during a read-only phase. This fix was inspired by the discussion on issue #537. It waits for the NExtTimeStep before driving default values on the bus in AvalonSTPkts::_send_string --- cocotb/drivers/avalon.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 5614cc56..dbc1c2b2 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -573,6 +573,7 @@ def _send_string(self, string, sync=True): # Drive some defaults since we don't know what state we're in # self.bus.empty <= 0 + yield NextTimeStep() self.bus.startofpacket <= 0 self.bus.endofpacket <= 0 self.bus.valid <= 0 From ef1a9588ecd6174da2b6667e1b93824dd182cdce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20G=C3=A4nsler?= Date: Tue, 18 Sep 2018 09:33:17 +0200 Subject: [PATCH 1417/2656] Let ping_tun_tap example only reply to ICMP echo requests When creating a new interface, one can not be sure that nobody else sends packets to that interface. In an IPv4 + IPv6 dual stack environment I experienced IPv6 router solicitation packets which got forwarded to DUT. This change checks the content of the packages for being an ICMP packet (packet[9] == '\x01') and for being an echo request (packet[20] == '\x08') before forwarding that packet to DUT. Otherwise packets are thrown away and testbench waits for the next packet. With this change, ping statistics now doesn't show any more packet loss, all ping packets get handled gracefully. --- examples/ping_tun_tap/tests/test_icmp_reply.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/examples/ping_tun_tap/tests/test_icmp_reply.py b/examples/ping_tun_tap/tests/test_icmp_reply.py index d295074b..affea4ac 100644 --- a/examples/ping_tun_tap/tests/test_icmp_reply.py +++ b/examples/ping_tun_tap/tests/test_icmp_reply.py @@ -112,14 +112,24 @@ def tun_tap_example_test(dut): subprocess.check_call('ping -c 5 192.168.255.2 &', shell=True) # Respond to 5 pings, then quit - for i in range(5): - + pingcounter = 0 + while True: cocotb.log.info("Waiting for packets on tun interface") packet = os.read(fd, 2048) cocotb.log.info("Received a packet!") + if packet[9] == '\x01' and packet[20] == '\x08': + cocotb.log.debug("Packet is an ICMP echo request") + pingcounter += 1 + else: + cocotb.log.info("Packet is no ICMP echo request, throwing away packet") + continue + stream_in.append(packet) result = yield stream_out.wait_for_recv() cocotb.log.info("Rtl replied!") os.write(fd, str(result)) + + if pingcounter == 5: + break From a465523e6197b096b41e281cae6256dd61e9b997 Mon Sep 17 00:00:00 2001 From: Stefan Wallentowitz Date: Wed, 24 Oct 2018 13:07:27 +0200 Subject: [PATCH 1418/2656] Allow ordering of tests by stage To allow some logical sorting of tests, add a stage parameter to the decorator. This can be used to ease the analysis of output files or the test outputs. The stage has no other functionality except sorting. --- cocotb/decorators.py | 6 +++++- cocotb/regression.py | 7 ++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index a9b7fc48..1229d474 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -194,6 +194,7 @@ def __init__(self, inst, parent): self.expect_fail = parent.expect_fail self.expect_error = parent.expect_error self.skip = parent.skip + self.stage = parent.stage self.handler = RunningTest.ErrorLogHandler(self._handle_error_message) cocotb.log.addHandler(self.handler) @@ -378,13 +379,16 @@ class test(coroutine): This is for cocotb internal regression use skip: (bool): Don't execute this test as part of the regression + stage: (int) + Order tests logically into stages, where multiple tests can share a stage """ def __init__(self, timeout=None, expect_fail=False, expect_error=False, - skip=False): + skip=False, stage=0): self.timeout = timeout self.expect_fail = expect_fail self.expect_error = expect_error self.skip = skip + self.stage = stage def __call__(self, f): super(test, self).__init__(f) diff --git a/cocotb/regression.py b/cocotb/regression.py index d6f7dcac..6bbe5563 100755 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -179,12 +179,13 @@ def _initialise(self): self._queue.append(test) self.ntests += 1 - self._queue.sort(key=lambda test: "%s.%s" % - (test.module, test.funcname)) + self._queue.sort(key=lambda test: "%s.%i.%s" % + (test.module, test.stage, test.funcname)) for valid_tests in self._queue: - self.log.info("Found test %s.%s" % + self.log.info("Found test %s.%i.%s" % (valid_tests.module, + valid_tests.stage, valid_tests.funcname)) for module_name in self._hooks: From 0ab8341dce9888104825872af4ce158c148b76ac Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 26 Oct 2018 00:10:58 +0200 Subject: [PATCH 1419/2656] Create troubleshooting.rst Start new troubleshooting section that documents COCOTB_ATTACH for now. --- documentation/source/troubleshooting.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 documentation/source/troubleshooting.rst diff --git a/documentation/source/troubleshooting.rst b/documentation/source/troubleshooting.rst new file mode 100644 index 00000000..0bc132d5 --- /dev/null +++ b/documentation/source/troubleshooting.rst @@ -0,0 +1,12 @@ +Troubleshooting +--------------- + +Attaching a Debugger +==================== + +In order to give yourself time to attach a debugger to the simulator process before it starts to run, +you can set the environment variable ``COCOTB_ATTACH`` to a pause time value in seconds. +If set, Cocotb will print the process ID (PID) to attach to and wait the specified time before +actually letting the simulator run. + +For the GNU debugger GDB, the command is ``attach ``. From a76e419b00f94ba091edb86cc0a4923bdbde40da Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 26 Oct 2018 00:14:57 +0200 Subject: [PATCH 1420/2656] Add troubleshooting section. --- documentation/source/index.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/documentation/source/index.rst b/documentation/source/index.rst index 523c9ad0..6b3f91a0 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -14,7 +14,8 @@ Contents: library_reference endian_swapper ping_tun_tap - hal_cosimulation + hal_cosimulation + troubleshooting roadmap simulator_support From 9bc944e38ca7f4ff75ecb52c2adf1968fae4a23f Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 26 Oct 2018 00:31:56 +0200 Subject: [PATCH 1421/2656] Add section Increasing Verbosity --- documentation/source/troubleshooting.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/documentation/source/troubleshooting.rst b/documentation/source/troubleshooting.rst index 0bc132d5..1cf88dc5 100644 --- a/documentation/source/troubleshooting.rst +++ b/documentation/source/troubleshooting.rst @@ -1,6 +1,14 @@ Troubleshooting --------------- +Increasing Verbosity +==================== + +If things fail in the VPI/VHPI/FLI area, check your simulator's documentation to see if it has options to +increase its verbosity about what may be wrong. You can then set these options on the ``make`` command line +as ``COMPILE_ARGS, ``SIM_ARGS``or ``EXTRA_OPTS`` (see :doc:`building` for details). + + Attaching a Debugger ==================== From d6b2f921386e5d2f0b5387c7b0e224e808eb8d98 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 26 Oct 2018 00:34:58 +0200 Subject: [PATCH 1422/2656] Typofixes. --- documentation/source/troubleshooting.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/documentation/source/troubleshooting.rst b/documentation/source/troubleshooting.rst index 1cf88dc5..660ee03e 100644 --- a/documentation/source/troubleshooting.rst +++ b/documentation/source/troubleshooting.rst @@ -1,12 +1,13 @@ +############### Troubleshooting ---------------- +############### Increasing Verbosity ==================== If things fail in the VPI/VHPI/FLI area, check your simulator's documentation to see if it has options to increase its verbosity about what may be wrong. You can then set these options on the ``make`` command line -as ``COMPILE_ARGS, ``SIM_ARGS``or ``EXTRA_OPTS`` (see :doc:`building` for details). +as ``COMPILE_ARGS``, ``SIM_ARGS`` or ``EXTRA_OPTS`` (see :doc:`building` for details). Attaching a Debugger From ed0c3e6c4f8429e7552745f2cd0065452f59a820 Mon Sep 17 00:00:00 2001 From: Stefan Wallentowitz Date: Fri, 26 Oct 2018 09:01:51 +0200 Subject: [PATCH 1423/2656] Refine test stages * Tests without stage will be run as last instead of stage 0 * Don't print stage --- cocotb/decorators.py | 7 ++++++- cocotb/regression.py | 6 ++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 1229d474..b6bad3c5 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -173,6 +173,11 @@ def __nonzero__(self): otherwise return true""" return not self._finished + def sort_name(self): + if self.stage is None: + return "%s.%s" % (self.module, self.funcname) + else: + return "%s.%d.%s" % (self.module, self.stage, self.funcname) class RunningTest(RunningCoroutine): """Add some useful Test functionality to a RunningCoroutine""" @@ -383,7 +388,7 @@ class test(coroutine): Order tests logically into stages, where multiple tests can share a stage """ def __init__(self, timeout=None, expect_fail=False, expect_error=False, - skip=False, stage=0): + skip=False, stage=None): self.timeout = timeout self.expect_fail = expect_fail self.expect_error = expect_error diff --git a/cocotb/regression.py b/cocotb/regression.py index 6bbe5563..9e65333e 100755 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -179,13 +179,11 @@ def _initialise(self): self._queue.append(test) self.ntests += 1 - self._queue.sort(key=lambda test: "%s.%i.%s" % - (test.module, test.stage, test.funcname)) + self._queue.sort(key=lambda test: test.sort_name()) for valid_tests in self._queue: - self.log.info("Found test %s.%i.%s" % + self.log.info("Found test %s.%s" % (valid_tests.module, - valid_tests.stage, valid_tests.funcname)) for module_name in self._hooks: From b489d56d6d53121ec6983ecd0697ec8d0a27be6e Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 26 Oct 2018 11:52:56 +0200 Subject: [PATCH 1424/2656] Add link to Gitter channel --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7d68e429..f757066b 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ * Get involved: * [Raise a bug / request an enhancement](https://github.com/potentialventures/cocotb/issues/new) (Requires a GitHub account) * [Join the mailing list](https://lists.librecores.org/listinfo/cocotb) + * [Join the Gitter chat room](https://gitter.im/cocotb) * Get in contact: [E-mail us](mailto:cocotb@potentialventures.com) * Follow us on twitter: [@PVCocotb](https://twitter.com/PVCocotb) From aa3d6a189cb817c2134ae326e1671b7a02e46da5 Mon Sep 17 00:00:00 2001 From: Stefan Wallentowitz Date: Sun, 28 Oct 2018 16:21:48 +0100 Subject: [PATCH 1425/2656] iverilog: Set toplevel at build When a file contains modules that wrap the toplevel module, iverilog will not have the toplevel at its own toplevel, but only inside the wrapper modules. Therefore, explicitly pass the toplevel module at build to iverilog. --- makefiles/simulators/Makefile.icarus | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index af024e37..9681cddd 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -56,7 +56,7 @@ BUILD_VPI=1 # Compilation phase $(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) - $(CMD) -o $(SIM_BUILD)/sim.vvp -D COCOTB_SIM=1 $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) + $(CMD) -o $(SIM_BUILD)/sim.vvp -D COCOTB_SIM=1 -s $(TOPLEVEL) $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) # Execution phase From 66340159ddf1298948831e46486400c6601b041d Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 29 Oct 2018 23:52:23 +0100 Subject: [PATCH 1426/2656] Fix link to UIO. --- documentation/source/hal_cosimulation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/hal_cosimulation.rst b/documentation/source/hal_cosimulation.rst index 16660528..b5208d5a 100644 --- a/documentation/source/hal_cosimulation.rst +++ b/documentation/source/hal_cosimulation.rst @@ -173,7 +173,7 @@ software needs to execute on a different processor architecture. .. _SWIG: http://www.swig.org/ -.. _UIO framework: https://www.kernel.org/doc/htmldocs/uio-howto/about.html +.. _UIO framework: https://www.kernel.org/doc/html/latest/driver-api/uio-howto.html .. _QEMU: http://wiki.qemu.org/Main_Page From ae39be6a6e12b676588c8876c796281ae647375b Mon Sep 17 00:00:00 2001 From: Stefan Wallentowitz Date: Tue, 30 Oct 2018 09:45:28 +0100 Subject: [PATCH 1427/2656] trivial: Don't try to print results if none there If self.test_results contains no elements, the calculation of the lengths of the fields fails. Don't try to print the results then at all. --- cocotb/regression.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 9e65333e..0b77990f 100755 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -213,7 +213,8 @@ def tear_down(self): self.log.info("Writing coverage data") self._cov.save() self._cov.html_report() - self._log_test_summary() + if len(self.test_results) > 0: + self._log_test_summary() self._log_sim_summary() self.log.info("Shutting down...") self.xunit.write() From f6af706aa8ee9244c5b83e7685bf1effb7e23e7d Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sun, 4 Nov 2018 23:24:37 +0100 Subject: [PATCH 1428/2656] Implement abstraction layer for ANSI colors. --- cocotb/ANSI.py | 79 ++++++++++++++++++++++++++++++++------------ cocotb/log.py | 16 ++++----- cocotb/regression.py | 18 +++++----- cocotb/utils.py | 16 ++++----- 4 files changed, 82 insertions(+), 47 deletions(-) diff --git a/cocotb/ANSI.py b/cocotb/ANSI.py index 8f240b00..7ed674ae 100644 --- a/cocotb/ANSI.py +++ b/cocotb/ANSI.py @@ -1,4 +1,4 @@ -''' Copyright (c) 2013 Potential Ventures Ltd +''' Copyright (c) 2013, 2018 Potential Ventures Ltd Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. @@ -31,25 +31,60 @@ # flake8: noqa (skip this file for flake8: pypi.python.org/pypi/flake8) _ESCAPE = "\033[" +# see https://en.wikipedia.org/wiki/ANSI_escape_code#Colors -BLACK_FG = _ESCAPE + "30m" -RED_FG = _ESCAPE + "31m" -GREEN_FG = _ESCAPE + "32m" -YELLOW_FG = _ESCAPE + "33m" -BLUE_FG = _ESCAPE + "34m" -MAGENTA_FG = _ESCAPE + "35m" -CYAN_FG = _ESCAPE + "36m" -WHITE_FG = _ESCAPE + "37m" -DEFAULT_FG = _ESCAPE + "39m" - -BLACK_BG = _ESCAPE + "40m" -RED_BG = _ESCAPE + "41m" -GREEN_BG = _ESCAPE + "42m" -YELLOW_BG = _ESCAPE + "43m" -BLUE_BG = _ESCAPE + "44m" -MAGENTA_BG = _ESCAPE + "45m" -CYAN_BG = _ESCAPE + "46m" -WHITE_BG = _ESCAPE + "47m" -DEFAULT_BG = _ESCAPE + "49m" - -DEFAULT = DEFAULT_BG + DEFAULT_FG +DEFAULT_FG = _ESCAPE + "39m" +DEFAULT_BG = _ESCAPE + "49m" +DEFAULT = DEFAULT_BG + DEFAULT_FG + +BLACK_FG = _ESCAPE + "30m" +RED_FG = _ESCAPE + "31m" +GREEN_FG = _ESCAPE + "32m" +YELLOW_FG = _ESCAPE + "33m" +BLUE_FG = _ESCAPE + "34m" +MAGENTA_FG = _ESCAPE + "35m" +CYAN_FG = _ESCAPE + "36m" +WHITE_FG = _ESCAPE + "37m" + +BLACK_BG = _ESCAPE + "40m" +RED_BG = _ESCAPE + "41m" +GREEN_BG = _ESCAPE + "42m" +YELLOW_BG = _ESCAPE + "43m" +BLUE_BG = _ESCAPE + "44m" +MAGENTA_BG = _ESCAPE + "45m" +CYAN_BG = _ESCAPE + "46m" +WHITE_BG = _ESCAPE + "47m" + +BRIGHT_BLACK_FG = _ESCAPE + "90m" +BRIGHT_RED_FG = _ESCAPE + "91m" +BRIGHT_GREEN_FG = _ESCAPE + "92m" +BRIGHT_YELLOW_FG = _ESCAPE + "93m" +BRIGHT_BLUE_FG = _ESCAPE + "94m" +BRIGHT_MAGENTA_FG = _ESCAPE + "95m" +BRIGHT_CYAN_FG = _ESCAPE + "96m" +BRIGHT_WHITE_FG = _ESCAPE + "97m" + +BRIGHT_BLACK_BG = _ESCAPE + "100m" +BRIGHT_RED_BG = _ESCAPE + "101m" +BRIGHT_GREEN_BG = _ESCAPE + "102m" +BRIGHT_YELLOW_BG = _ESCAPE + "103m" +BRIGHT_BLUE_BG = _ESCAPE + "104m" +BRIGHT_MAGENTA_BG = _ESCAPE + "105m" +BRIGHT_CYAN_BG = _ESCAPE + "106m" +BRIGHT_WHITE_BG = _ESCAPE + "107m" + + +COLOR_DEFAULT = DEFAULT + +COLOR_INFO = BLUE_FG +COLOR_WARNING = YELLOW_FG +COLOR_ERROR = RED_FG +COLOR_CRITICAL = RED_BG + BLACK_FG +COLOR_TEST = BLUE_BG + BLACK_FG + +COLOR_HILITE_SUMMARY = WHITE_FG + RED_BG +COLOR_HILITE_HEXDIFF_DEFAULT = YELLOW_FG +COLOR_HILITE_HEXDIFF_1 = CYAN_FG +COLOR_HILITE_HEXDIFF_2 = RED_FG +COLOR_HILITE_HEXDIFF_3 = MAGENTA_BG +COLOR_HILITE_HEXDIFF_4 = CYAN_BG + BLACK_FG diff --git a/cocotb/log.py b/cocotb/log.py index 7555fb64..19aa45e5 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -1,4 +1,4 @@ -''' Copyright (c) 2013 Potential Ventures Ltd +''' Copyright (c) 2013, 2018 Potential Ventures Ltd Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. @@ -183,7 +183,7 @@ def _format(self, level, record, msg, coloured=False): return prefix + pad.join(msg.split('\n')) def format(self, record): - """pretify the log output, annotate with simulation time""" + """Prettify the log output, annotate with simulation time""" if record.args: msg = record.msg % record.args else: @@ -200,14 +200,14 @@ class SimColourLogFormatter(SimLogFormatter): """Log formatter to provide consistent log message handling.""" loglevel2colour = { logging.DEBUG : "%s", - logging.INFO : ANSI.BLUE_FG + "%s" + ANSI.DEFAULT, - logging.WARNING : ANSI.YELLOW_FG + "%s" + ANSI.DEFAULT, - logging.ERROR : ANSI.RED_FG + "%s" + ANSI.DEFAULT, - logging.CRITICAL: ANSI.RED_BG + ANSI.BLACK_FG + "%s" + - ANSI.DEFAULT} + logging.INFO : ANSI.COLOR_INFO + "%s" + ANSI.COLOR_DEFAULT, + logging.WARNING : ANSI.COLOR_WARNING + "%s" + ANSI.COLOR_DEFAULT, + logging.ERROR : ANSI.COLOR_ERROR + "%s" + ANSI.COLOR_DEFAULT, + logging.CRITICAL: ANSI.COLOR_CRITICAL + "%s" + ANSI.COLOR_DEFAULT, + } def format(self, record): - """pretify the log output, annotate with simulation time""" + """Prettify the log output, annotate with simulation time""" if record.args: msg = record.msg % record.args diff --git a/cocotb/regression.py b/cocotb/regression.py index 9e65333e..6fda8f19 100755 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -1,4 +1,4 @@ -''' Copyright (c) 2013 Potential Ventures Ltd +''' Copyright (c) 2013, 2018 Potential Ventures Ltd Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. @@ -48,7 +48,7 @@ try: import coverage except ImportError as e: - msg = ("Coverage collection requested but coverage module not availble" + msg = ("Coverage collection requested but coverage module not available" "\n" "Import error was: %s\n" % repr(e)) sys.stderr.write(msg) @@ -75,7 +75,7 @@ class RegressionManager(object): def __init__(self, root_name, modules, tests=None, seed=None, hooks=[]): """ Args: - modules (list): A list of python module names to run + modules (list): A list of Python module names to run Kwargs """ @@ -162,7 +162,7 @@ def _initialise(self): skip = test.skip except TestError: skip = True - self.log.warning("Failed to initialise test %s" % + self.log.warning("Failed to initialize test %s" % thing.name) if skip: @@ -286,7 +286,7 @@ def _result_was(): if self._running_test.expect_error: self.log.info("Test errored as expected: " + _result_was()) else: - self.log.error("Test error has lead to simulator shuttting us " + self.log.error("Test error has lead to simulator shutting us " "down") self._add_failure(result) self._store_test_result(self._running_test.module, self._running_test.funcname, False, sim_time_ns, real_time, ratio_time) @@ -308,8 +308,8 @@ def execute(self): start = '' end = '' if self.log.colour: - start = ANSI.BLUE_BG + ANSI.BLACK_FG - end = ANSI.DEFAULT + start = ANSI.COLOR_TEST + end = ANSI.COLOR_DEFAULT # Want this to stand out a little bit self.log.info("%sRunning test %d/%d:%s %s" % (start, @@ -359,7 +359,7 @@ def _log_test_summary(self): else: pass_fail_str = "FAIL" if self.log.colour: - hilite = ANSI.WHITE_FG + ANSI.RED_BG + hilite = ANSI.COLOR_HILITE_SUMMARY summary += "{start}** {a:<{a_len}} {b:^{b_len}} {c:>{c_len}.2f} {d:>{d_len}.2f} {e:>{e_len}.2f} **\n".format(a=result['test'], a_len=TEST_FIELD_LEN, b=pass_fail_str, b_len=RESULT_FIELD_LEN, @@ -504,7 +504,7 @@ def add_option(self, name, optionlist): def generate_tests(self, prefix="", postfix=""): """ - Generates exhasutive set of tests using the cartesian product of the + Generates exhaustive set of tests using the cartesian product of the possible keyword arguments. The generated tests are appended to the namespace of the calling diff --git a/cocotb/utils.py b/cocotb/utils.py index 799a75ab..2f8ea315 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -1,6 +1,6 @@ from __future__ import print_function -''' Copyright (c) 2013 Potential Ventures Ltd +''' Copyright (c) 2013, 2018 Potential Ventures Ltd Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. @@ -231,7 +231,7 @@ def sane(x): r = r + i return r - def highlight(string, colour=ANSI.YELLOW_FG): + def highlight(string, colour=ANSI.COLOR_HILITE_HEXDIFF_DEFAULT): want_ansi = os.getenv("COCOTB_ANSI_OUTPUT") if want_ansi is None: want_ansi = sys.stdout.isatty() # default to ANSI for TTYs @@ -239,7 +239,7 @@ def highlight(string, colour=ANSI.YELLOW_FG): want_ansi = want_ansi == '1' if want_ansi: - return colour + string + ANSI.DEFAULT_FG + ANSI.DEFAULT_BG + return colour + string + ANSI.COLOR_DEFAULT else: return string @@ -301,7 +301,7 @@ def highlight(string, colour=ANSI.YELLOW_FG): if dox != doy: rs += highlight("%04x" % xd) + " " else: - rs += highlight("%04x" % xd, colour=ANSI.CYAN_FG) + " " + rs += highlight("%04x" % xd, colour=ANSI.COLOR_HILITE_HEXDIFF_1) + " " x += xx line = linex else: @@ -315,7 +315,7 @@ def highlight(string, colour=ANSI.YELLOW_FG): if doy - dox != 0: rs += " " + highlight("%04x" % yd) else: - rs += highlight("%04x" % yd, colour=ANSI.CYAN_FG) + rs += highlight("%04x" % yd, colour=ANSI.COLOR_HILITE_HEXDIFF_1) y += yy line = liney else: @@ -329,15 +329,15 @@ def highlight(string, colour=ANSI.YELLOW_FG): if line[j]: if linex[j] != liney[j]: rs += highlight("%02X" % ord(line[j]), - colour=ANSI.RED_FG) + colour=ANSI.COLOR_HILITE_HEXDIFF_2) else: rs += "%02X" % ord(line[j]) if linex[j] == liney[j]: cl += highlight(_sane_color(line[j]), - colour=ANSI.MAGENTA_FG) + colour=ANSI.COLOR_HILITE_HEXDIFF_3) else: cl += highlight(sane(line[j]), - colour=ANSI.CYAN_BG + ANSI.BLACK_FG) + colour=ANSI.COLOR_HILITE_HEXDIFF_4) else: rs += " " cl += " " From 77ccd80573242f0aa1698c8d0019d056fa9ec517 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 14 Nov 2018 22:49:25 +0100 Subject: [PATCH 1429/2656] Remove GENERICS makefile variable. --- examples/mean/tests/Makefile | 2 -- makefiles/simulators/Makefile.questa | 6 +----- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/examples/mean/tests/Makefile b/examples/mean/tests/Makefile index f72fe18f..91ccfd4d 100644 --- a/examples/mean/tests/Makefile +++ b/examples/mean/tests/Makefile @@ -25,8 +25,6 @@ VHDL_SOURCES += $(WPWD)/../hdl/mean.vhd VCOM_ARGS = -mixedsvvh -GENERICS = "BUS_WIDTH=2" - VERILOG_SOURCES = $(WPWD)/../hdl/mean_sv.sv MODULE := test_mean diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index cb5a816f..657dc745 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013, 2018 Potential Ventures Ltd # Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # @@ -78,10 +78,6 @@ else $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") endif -ifneq ($(GENERICS),) -VSIM_ARGS += $(foreach gen, $(GENERICS),"-G $(gen)") -endif - $(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) $(SIM_BUILD) @echo "# Autogenerated file" > $@ @echo "onerror {" >> $@ From 2d1f6fb421b20f16bdfc77ba8c65e7935eada01b Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 13 Nov 2018 22:27:50 +0100 Subject: [PATCH 1430/2656] Add files to ignore for Cadence simulators. --- .gitignore | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.gitignore b/.gitignore index 4d01d206..bcafcae0 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,10 @@ __pycache__ *.swp *~ +# Emacs tmp files +\#*\# +\.\#* + # Waveforms *.vcd @@ -34,3 +38,22 @@ combined_results.xml *.tab sim_build ucli.key + +# Cadence Incisive/Xcelium +*.elog +irun.log +xrun.log +irun.key +xrun.key +irun.history +xrun.history +INCA_libs +xcelium.d +ncelab_*.err +xmelab_*.err +ncsim_*.err +xmsim_*.err +bpad_*.err +.bpad/ +.simvision/ +waves.shm/ From f983c34653abdf7246720860c38e11177f60f714 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 13 Nov 2018 22:54:34 +0100 Subject: [PATCH 1431/2656] Add case for Synopsys VCS. --- tests/test_cases/test_iteration_verilog/test_iteration_es.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_cases/test_iteration_verilog/test_iteration_es.py b/tests/test_cases/test_iteration_verilog/test_iteration_es.py index 9223eb11..420aac54 100644 --- a/tests/test_cases/test_iteration_verilog/test_iteration_es.py +++ b/tests/test_cases/test_iteration_verilog/test_iteration_es.py @@ -1,4 +1,4 @@ -''' Copyright (c) 2015 Potential Ventures Ltd +''' Copyright (c) 2015, 2018 Potential Ventures Ltd All rights reserved. Redistribution and use in source and binary forms, with or without @@ -38,6 +38,8 @@ def recursive_discovery(dut): "ncsim")): # vpiAlways does not show up in IUS pass_total = 259 + elif cocotb.SIM_NAME.lower().startswith(("chronologic simulation vcs")): + pass_total = 59 else: pass_total = 265 From e98f7399610ba5d273046c7dfbbd5bbb1772f6c3 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 15 Nov 2018 22:18:12 +0100 Subject: [PATCH 1432/2656] Rename sample_module.v to .sv because that's what it is. --- tests/designs/sample_module/Makefile | 4 ++-- .../sample_module/{sample_module.v => sample_module.sv} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename tests/designs/sample_module/{sample_module.v => sample_module.sv} (98%) diff --git a/tests/designs/sample_module/Makefile b/tests/designs/sample_module/Makefile index c7491b29..a1950565 100644 --- a/tests/designs/sample_module/Makefile +++ b/tests/designs/sample_module/Makefile @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013, 2018 Potential Ventures Ltd # Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # @@ -40,7 +40,7 @@ endif COCOTB?=$(WPWD)/../../.. ifeq ($(TOPLEVEL_LANG),verilog) - VERILOG_SOURCES = $(COCOTB)/tests/designs/sample_module/sample_module.v + VERILOG_SOURCES = $(COCOTB)/tests/designs/sample_module/sample_module.sv else ifeq ($(TOPLEVEL_LANG),vhdl) VHDL_SOURCES = $(COCOTB)/tests/designs/sample_module/sample_module_pack.vhdl $(COCOTB)/tests/designs/sample_module/sample_module_1.vhdl $(COCOTB)/tests/designs/sample_module/sample_module.vhdl else diff --git a/tests/designs/sample_module/sample_module.v b/tests/designs/sample_module/sample_module.sv similarity index 98% rename from tests/designs/sample_module/sample_module.v rename to tests/designs/sample_module/sample_module.sv index b5b0965d..9d44dedd 100644 --- a/tests/designs/sample_module/sample_module.v +++ b/tests/designs/sample_module/sample_module.sv @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2013 Potential Ventures Ltd +// Copyright (c) 2013, 2018 Potential Ventures Ltd // Copyright (c) 2013 SolarFlare Communications Inc // All rights reserved. // From 91b5dbf4ed699fffce37699f2a379e33bde0a67c Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 15 Nov 2018 23:15:45 +0100 Subject: [PATCH 1433/2656] Fix lots of small typos in strings and comments. --- bin/combine_results.py | 2 +- cocotb/scheduler.py | 14 ++++---- examples/Makefile | 4 ++- examples/endian_swapper/tests/Makefile | 4 +-- include/gpi.h | 4 +-- lib/embed/gpi_embed.c | 8 ++--- lib/fli/FliImpl.cpp | 4 +-- lib/gpi/gpi_priv.h | 20 +++++------ lib/simulator/simulatormodule.c | 18 +++++----- lib/vhpi/VhpiImpl.cpp | 8 ++--- lib/vpi/VpiCbHdl.cpp | 25 +++++++------ lib/vpi/VpiImpl.cpp | 10 +++--- lib/vpi/VpiImpl.h | 4 +-- makefiles/Makefile.rules | 4 +-- makefiles/Makefile.sim | 4 +-- tests/test_cases/test_array/test_array.py | 2 +- tests/test_cases/test_avalon/test_avalon.py | 6 ++-- tests/test_cases/test_cocotb/test_cocotb.py | 36 +++++++++---------- tests/test_cases/test_exit_error/test_exit.py | 4 +-- .../test_cases/test_external/test_external.py | 16 ++++----- .../test_multi_dimension_array/Makefile | 6 ++-- 21 files changed, 102 insertions(+), 101 deletions(-) mode change 100644 => 100755 cocotb/scheduler.py mode change 100644 => 100755 tests/test_cases/test_avalon/test_avalon.py mode change 100644 => 100755 tests/test_cases/test_cocotb/test_cocotb.py mode change 100644 => 100755 tests/test_cases/test_external/test_external.py diff --git a/bin/combine_results.py b/bin/combine_results.py index 083fe69a..fd8cf4b8 100755 --- a/bin/combine_results.py +++ b/bin/combine_results.py @@ -37,7 +37,7 @@ def get_parser(): help="Verbose/debug output") parser.add_argument("--suppress_rc", dest="set_rc", action='store_const', required=False, const=False, default=True, - help="Supress return code if failures found") + help="Suppress return code if failures found") return parser diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py old mode 100644 new mode 100755 index 83242a81..02b28c3f --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -''' Copyright (c) 2013 Potential Ventures Ltd +''' Copyright (c) 2013, 2018 Potential Ventures Ltd Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. @@ -287,7 +287,7 @@ def begin_test(self, trigger=None): # Issue previous test result, if there is one if self._test_result is not None: if _debug: - self.log.debug("Issue test result to regresssion object") + self.log.debug("Issue test result to regression object") cocotb.regression.handle_result(self._test_result) self._test_result = None if self._entrypoint is not None: @@ -500,7 +500,7 @@ def queue_function(self, coroutine): def run_in_executor(self, func, *args, **kwargs): """ - Run the corouting in a seperate execution thread + Run the coroutine in a separate execution thread and return a yieldable object for the caller """ # Create a thread @@ -607,7 +607,7 @@ def schedule(self, coroutine, trigger=None): self.finish_test(test_result) return - # Normal co-routine completion + # Normal coroutine completion except cocotb.decorators.CoroutineComplete as exc: if _debug: self.log.debug("Coroutine completed: %s" % str(coroutine)) @@ -624,11 +624,11 @@ def schedule(self, coroutine, trigger=None): if not result.has_started(): self.queue(result) if _debug: - self.log.debug("Scheduling nested co-routine: %s" % + self.log.debug("Scheduling nested coroutine: %s" % result.__name__) else: if _debug: - self.log.debug("Joining to already running co-routine: %s" % + self.log.debug("Joining to already running coroutine: %s" % result.__name__) new_trigger = result.join() @@ -695,7 +695,7 @@ def finish_test(self, test_result): def finish_scheduler(self, test_result): """Directly call into the regression manager and end test once we return the sim will close us so no cleanup is needed""" - self.log.debug("Issue sim closedown result to regresssion object") + self.log.debug("Issue sim closedown result to regression object") cocotb.regression.handle_result(test_result) def cleanup(self): diff --git a/examples/Makefile b/examples/Makefile index dee9f214..a753539f 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013, 2018 Potential Ventures Ltd # Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # @@ -36,10 +36,12 @@ EXAMPLES := adder/tests \ .PHONY: $(EXAMPLES) +.PHONY: all all: $(EXAMPLES) $(EXAMPLES): @cd $@ && $(MAKE) +.PHONY: clean clean: $(foreach TEST, $(EXAMPLES), $(MAKE) -C $(TEST) clean;) diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index 890e7588..4256c7ce 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013, 2018 Potential Ventures Ltd # Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # @@ -66,7 +66,7 @@ hal: -cd ../cosim && make # Stuff below is useful for profiling -# Need grof2dot from https://github.com/jrfonseca/gprof2dot +# Need gprof2dot from https://github.com/jrfonseca/gprof2dot test_profile.pstat: sim callgraph.svg: test_profile.pstat diff --git a/include/gpi.h b/include/gpi.h index 0527b38e..f212f47e 100644 --- a/include/gpi.h +++ b/include/gpi.h @@ -1,5 +1,5 @@ /****************************************************************************** -* Copyright (c) 2013 Potential Ventures Ltd +* Copyright (c) 2013, 2018 Potential Ventures Ltd * Copyright (c) 2013 SolarFlare Communications Inc * All rights reserved. * @@ -179,7 +179,7 @@ long gpi_get_signal_value_long(gpi_sim_hdl gpi_hdl); const char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl); const char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl); -// Returns on of the types defined above e.g. gpiMemory etc. +// Returns one of the types defined above e.g. gpiMemory etc. gpi_objtype_t gpi_get_object_type(gpi_sim_hdl gpi_hdl); // Get information about the definition of a handle diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index caa2935b..8d60cbe1 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -1,5 +1,5 @@ /****************************************************************************** -* Copyright (c) 2013 Potential Ventures Ltd +* Copyright (c) 2013, 2018 Potential Ventures Ltd * Copyright (c) 2013 SolarFlare Communications Inc * All rights reserved. * @@ -84,7 +84,7 @@ void embed_init_python(void) to_python(); - // reset Program Name (i.e. argv[0]) if we are in a virtual environment + // reset Program Name (i.e. argv[0]) if we are in a Python virtual environment char *venv_path_home = getenv("VIRTUAL_ENV"); if (venv_path_home) { char venv_path[strlen(venv_path_home)+64]; @@ -108,7 +108,7 @@ void embed_init_python(void) Py_SetProgramName(venv_path); #endif } else { - LOG_INFO("Did not detect virtual environment. Using system-wide Python interpreter."); + LOG_INFO("Did not detect Python virtual environment. Using system-wide Python interpreter."); } Py_Initialize(); /* Initialize the interpreter */ @@ -134,7 +134,7 @@ void embed_init_python(void) goto out; } - fprintf(stderr, "Waiting for %lu seconds - Attach to %d\n", sleep_time, getpid()); + fprintf(stderr, "Waiting for %lu seconds - attach to PID %d with your debugger\n", sleep_time, getpid()); sleep(sleep_time); } out: diff --git a/lib/fli/FliImpl.cpp b/lib/fli/FliImpl.cpp index 621236d2..b6b85f2a 100644 --- a/lib/fli/FliImpl.cpp +++ b/lib/fli/FliImpl.cpp @@ -1,5 +1,5 @@ /****************************************************************************** -* Copyright (c) 2014 Potential Ventures Ltd +* Copyright (c) 2014, 2018 Potential Ventures Ltd * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -121,7 +121,7 @@ GpiObjHdl *FliImpl::create_gpi_obj_from_handle(void *hdl, std::string &name, std { GpiObjHdl *new_obj = NULL; - LOG_DEBUG("Attepmting to create GPI object from handle (Type=%d, FullType=%d).", accType, accFullType); + LOG_DEBUG("Attempting to create GPI object from handle (Type=%d, FullType=%d).", accType, accFullType); if (!VS_TYPE_IS_VHDL(accFullType)) { LOG_DEBUG("Handle is not a VHDL type."); return NULL; diff --git a/lib/gpi/gpi_priv.h b/lib/gpi/gpi_priv.h index a272edd8..3a55b8e1 100644 --- a/lib/gpi/gpi_priv.h +++ b/lib/gpi/gpi_priv.h @@ -1,5 +1,5 @@ /****************************************************************************** -* Copyright (c) 2013 Potential Ventures Ltd +* Copyright (c) 2013, 2018 Potential Ventures Ltd * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -78,7 +78,7 @@ class GpiHdl { public: GpiImplInterface *m_impl; // VPI/VHPI/FLI routines char *gpi_copy_name(const char *name); // Might not be needed - bool is_this_impl(GpiImplInterface *impl); // Is the passed interface the one this object uses + bool is_this_impl(GpiImplInterface *impl); // Is the passed interface the one this object uses? protected: void *m_obj_hdl; @@ -88,7 +88,7 @@ class GpiHdl { // An object is any item in the hierarchy // Provides methods for iterating through children or finding by name // Initial object is returned by call to GpiImplInterface::get_root_handle() -// Susequent operations to get children go through this handle. +// Subsequent operations to get children go through this handle. // GpiObjHdl::get_handle_by_name/get_handle_by_index are really factories // that construct an object derived from GpiSignalObjHdl or GpiObjHdl class GpiObjHdl : public GpiHdl { @@ -196,11 +196,11 @@ class GpiCbHdl : public GpiHdl { m_cb_data(NULL), m_state(GPI_FREE) { } // Pure virtual functions for derived classes - virtual int arm_callback(void) = 0; // Register with siumlator + virtual int arm_callback(void) = 0; // Register with simulator virtual int run_callback(void); // Entry point from simulator virtual int cleanup_callback(void) = 0; // Cleanup the callback, arm can be called after - // Set the data to be used for run callback, seperate to arm_callback so data can be re-used + // Set the data to be used for run callback, separate to arm_callback so data can be re-used int set_user_data(int (*gpi_function)(const void*), const void *data); const void *get_user_data(void); @@ -242,8 +242,8 @@ class GpiIterator : public GpiHdl { enum Status { NATIVE, // Fully resolved object was created NATIVE_NO_NAME, // Native object was found but unable to fully create - NOT_NATIVE, // Mon native object was found but we did get a name - NOT_NATIVE_NO_NAME, // Mon native object was found without a name + NOT_NATIVE, // Non-native object was found but we did get a name + NOT_NATIVE_NO_NAME, // Non-native object was found without a name END }; @@ -312,14 +312,14 @@ class GpiImplInterface { virtual void get_sim_time(uint32_t *high, uint32_t *low) = 0; virtual void get_sim_precision(int32_t *precision) = 0; - /* Hierachy related */ + /* Hierarchy related */ virtual GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent) = 0; virtual GpiObjHdl* native_check_create(int32_t index, GpiObjHdl *parent) = 0; virtual GpiObjHdl* native_check_create(void *raw_hdl, GpiObjHdl *parent) = 0; virtual GpiObjHdl *get_root_handle(const char *name) = 0; virtual GpiIterator *iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) = 0; - /* Callback related, these may (will) return the same handle*/ + /* Callback related, these may (will) return the same handle */ virtual GpiCbHdl *register_timed_callback(uint64_t time_ps) = 0; virtual GpiCbHdl *register_readonly_callback(void) = 0; virtual GpiCbHdl *register_nexttime_callback(void) = 0; @@ -333,7 +333,7 @@ class GpiImplInterface { std::string m_name; }; -/* Called from implementaton layers back up the stack */ +/* Called from implementation layers back up the stack */ int gpi_register_impl(GpiImplInterface *func_tbl); void gpi_embed_init(gpi_sim_info_t *info); diff --git a/lib/simulator/simulatormodule.c b/lib/simulator/simulatormodule.c index 1fb8f96c..13fda3a3 100644 --- a/lib/simulator/simulatormodule.c +++ b/lib/simulator/simulatormodule.c @@ -1,5 +1,5 @@ /****************************************************************************** -* Copyright (c) 2013 Potential Ventures Ltd +* Copyright (c) 2013, 2018 Potential Ventures Ltd * Copyright (c) 2013 SolarFlare Communications Inc * All rights reserved. * @@ -237,7 +237,7 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) LOG_CRITICAL("Failed to allocate user data\n"); } - // Set up the user data (no more python API calls after this! + // Set up the user data (no more python API calls after this!) callback_data_p->_saved_thread_state = PyThreadState_Get(); callback_data_p->id_value = COCOTB_ACTIVE_ID; callback_data_p->function = function; @@ -293,7 +293,7 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) LOG_CRITICAL("Failed to allocate user data\n"); } - // Set up the user data (no more python API calls after this! + // Set up the user data (no more python API calls after this!) callback_data_p->_saved_thread_state = PyThreadState_Get(); callback_data_p->id_value = COCOTB_ACTIVE_ID; callback_data_p->function = function; @@ -349,7 +349,7 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) LOG_CRITICAL("Failed to allocate user data\n"); } - // Set up the user data (no more python API calls after this! + // Set up the user data (no more python API calls after this!) callback_data_p->_saved_thread_state = PyThreadState_Get(); callback_data_p->id_value = COCOTB_ACTIVE_ID; callback_data_p->function = function; @@ -415,7 +415,7 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) LOG_CRITICAL("Failed to allocate user data\n"); } - // Set up the user data (no more python API calls after this! + // Set up the user data (no more python API calls after this!) callback_data_p->_saved_thread_state = PyThreadState_Get(); callback_data_p->id_value = COCOTB_ACTIVE_ID; callback_data_p->function = function; @@ -488,7 +488,7 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) LOG_CRITICAL("Failed to allocate user data\n"); } - // Set up the user data (no more python API calls after this! + // Set up the user data (no more python API calls after this!) // Causes segfault? callback_data_p->_saved_thread_state = PyThreadState_Get();//PyThreadState_Get(); callback_data_p->id_value = COCOTB_ACTIVE_ID; @@ -560,7 +560,7 @@ static PyObject *next(PyObject *self, PyObject *args) result = gpi_next(hdl); - // Raise stopiteration when we're done + // Raise StopIteration when we're done if (!result) { PyErr_SetNone(PyExc_StopIteration); DROP_GIL(gstate); @@ -679,7 +679,7 @@ static PyObject *set_signal_val_str(PyObject *self, PyObject *args) return NULL; } - gpi_set_signal_value_str(hdl,binstr); + gpi_set_signal_value_str(hdl, binstr); res = Py_BuildValue("s", "OK!"); DROP_GIL(gstate); @@ -1022,7 +1022,7 @@ static PyObject *get_range(PyObject *self, PyObject *args) int rng_right = gpi_get_range_right((gpi_sim_hdl)hdl); if (indexable) - retstr = Py_BuildValue("(i,i)", rng_left,rng_right); + retstr = Py_BuildValue("(i,i)", rng_left, rng_right); else retstr = Py_BuildValue(""); diff --git a/lib/vhpi/VhpiImpl.cpp b/lib/vhpi/VhpiImpl.cpp index ce92d3cc..10faa8d4 100644 --- a/lib/vhpi/VhpiImpl.cpp +++ b/lib/vhpi/VhpiImpl.cpp @@ -1,5 +1,5 @@ /****************************************************************************** -* Copyright (c) 2014 Potential Ventures Ltd +* Copyright (c) 2014, 2018 Potential Ventures Ltd * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -113,7 +113,7 @@ bool is_const(vhpiHandleT hdl) vhpiIntT vhpitype = vhpi_get(vhpiKindP, tmp); if (vhpiConstDeclK == vhpitype || vhpiGenericDeclK == vhpitype) return true; - } while ((tmp = vhpi_handle(vhpiPrefix,tmp)) != NULL); + } while ((tmp = vhpi_handle(vhpiPrefix, tmp)) != NULL); return false; } @@ -233,7 +233,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, return NULL; } - /* We need to delve further here to detemine how to later set + /* We need to delve further here to determine how to later set the values of an object */ vhpiHandleT base_hdl = vhpi_handle(vhpiBaseType, new_hdl); @@ -574,7 +574,7 @@ GpiObjHdl *VhpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) return NULL; } - vhpiIntT num_dim = vhpi_get(vhpiNumDimensionsP,base_hdl); + vhpiIntT num_dim = vhpi_get(vhpiNumDimensionsP, base_hdl); uint32_t idx = 0; /* Need to translate the index into a zero-based flattened array index */ diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index 89d52518..c1488fe8 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -1,5 +1,5 @@ /****************************************************************************** -* Copyright (c) 2013 Potential Ventures Ltd +* Copyright (c) 2013, 2018 Potential Ventures Ltd * Copyright (c) 2013 SolarFlare Communications Inc * All rights reserved. * @@ -59,7 +59,7 @@ int VpiCbHdl::arm_callback(void) { } // Only a problem if we have not been asked to deregister and register - // in the same simultion callback + // in the same simulation callback if (m_obj_hdl != NULL && m_state != GPI_DELETE) { fprintf(stderr, "We seem to already be registered, deregistering %s!\n", @@ -99,7 +99,7 @@ int VpiCbHdl::cleanup_callback(void) } if (!(vpi_remove_cb(get_handle()))) { - LOG_CRITICAL("VPI: unbale to remove callback : ABORTING"); + LOG_CRITICAL("VPI: unable to remove callback : ABORTING"); } check_vpi_error(); @@ -107,7 +107,7 @@ int VpiCbHdl::cleanup_callback(void) #ifndef MODELSIM /* This is disabled for now, causes a small leak going to put back in */ if (!(vpi_free_object(get_handle()))) { - LOG_CRITICAL("VPI: unbale to free handle : ABORTING"); + LOG_CRITICAL("VPI: unable to free handle : ABORTING"); } #endif } @@ -129,7 +129,7 @@ int VpiArrayObjHdl::initialise(std::string &name, std::string &fq_name) { /* Need to determine if this is a pseudo-handle to be able to select the correct range */ std::string hdl_name = vpi_get_str(vpiName, hdl); - /* Removing the hdl_name from the name will leave the psuedo-indices */ + /* Removing the hdl_name from the name will leave the pseudo-indices */ if (hdl_name.length() < name.length()) { std::string idx_str = name.substr(hdl_name.length()); @@ -163,7 +163,7 @@ int VpiArrayObjHdl::initialise(std::string &name, std::string &fq_name) { } if (rangeHdl == NULL) { - LOG_CRITICAL("Unable to get Range for indexable object"); + LOG_CRITICAL("Unable to get range for indexable object"); } else { vpi_free_object(iter); // Need to free iterator since exited early @@ -184,7 +184,7 @@ int VpiArrayObjHdl::initialise(std::string &name, std::string &fq_name) { check_vpi_error(); m_range_right = val.value.integer; } else { - LOG_CRITICAL("Unable to get Range for indexable object"); + LOG_CRITICAL("Unable to get range for indexable object"); } /* vpiSize will return a size that is incorrect for multi-dimensional arrays so use the range @@ -207,7 +207,6 @@ int VpiArrayObjHdl::initialise(std::string &name, std::string &fq_name) { int VpiObjHdl::initialise(std::string &name, std::string &fq_name) { char * str; vpiHandle hdl = GpiObjHdl::get_handle(); - str = vpi_get_str(vpiDefName, hdl); if (str != NULL) m_definition_name = str; @@ -259,7 +258,7 @@ int VpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { check_vpi_error(); m_range_right = val.value.integer; } else { - LOG_CRITICAL("Unable to get Range for indexable object"); + LOG_CRITICAL("Unable to get range for indexable object"); } } else { @@ -272,7 +271,7 @@ int VpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { m_range_right = val.value.integer; } - LOG_DEBUG("VPI: Indexable Object initialised with range [%d:%d] and length >%d<", m_range_left, m_range_right, m_num_elems); + LOG_DEBUG("VPI: Indexable object initialised with range [%d:%d] and length >%d<", m_range_left, m_range_right, m_num_elems); } } } @@ -495,14 +494,14 @@ int VpiTimedCbHdl::cleanup_callback(void) { switch (m_state) { case GPI_PRIMED: - /* Issue #188: Work around for modelsim that is harmless to othes too, + /* Issue #188: Work around for modelsim that is harmless to others too, we tag the time as delete, let it fire then do not pass up */ - LOG_DEBUG("Not removing PRIMED timer %d\n",vpi_time.low); + LOG_DEBUG("Not removing PRIMED timer %d\n", vpi_time.low); m_state = GPI_DELETE; return 0; case GPI_DELETE: - LOG_DEBUG("Removing DELETE timer %d\n",vpi_time.low); + LOG_DEBUG("Removing DELETE timer %d\n", vpi_time.low); default: break; } diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index fe3c7814..4289a5e0 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -1,5 +1,5 @@ /****************************************************************************** -* Copyright (c) 2013 Potential Ventures Ltd +* Copyright (c) 2013, 2018 Potential Ventures Ltd * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -188,8 +188,8 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, break; } default: - /* We should only print a warning here if the type is really verilog, - It could be vhdl as some simulators allow qurying of both languages + /* We should only print a warning here if the type is really Verilog, + It could be VHDL as some simulators allow querying of both languages via the same handle */ const char *type_name = vpi_get_str(vpiType, new_hdl); @@ -417,7 +417,7 @@ GpiObjHdl *VpiImpl::get_root_handle(const char* name) goto error; } - //Need to free the iterator if it didn't return NULL + // Need to free the iterator if it didn't return NULL if (iterator && !vpi_free_object(iterator)) { LOG_WARN("VPI: Attempting to free root iterator failed!"); check_vpi_error(); @@ -508,7 +508,7 @@ int VpiImpl::deregister_callback(GpiCbHdl *gpi_hdl) return 0; } -// If the Pything world wants things to shut down then unregister +// If the Python world wants things to shut down then unregister // the callback for end of sim void VpiImpl::sim_end(void) { diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index f2e43271..5ccc4088 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -1,5 +1,5 @@ /****************************************************************************** -* Copyright (c) 2013 Potential Ventures Ltd +* Copyright (c) 2013, 2018 Potential Ventures Ltd * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -272,7 +272,7 @@ class VpiImpl : public GpiImplInterface { void get_sim_time(uint32_t *high, uint32_t *low); void get_sim_precision(int32_t *precision); - /* Hierachy related */ + /* Hierarchy related */ GpiObjHdl *get_root_handle(const char *name); GpiIterator *iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type); GpiObjHdl *next_handle(GpiIterator *iter); diff --git a/makefiles/Makefile.rules b/makefiles/Makefile.rules index 3c03bb03..ed2d1ba5 100644 --- a/makefiles/Makefile.rules +++ b/makefiles/Makefile.rules @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013, 2018 Potential Ventures Ltd # Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # @@ -39,7 +39,7 @@ $(LIB_NAME)_OBJS+= $(patsubst %.cpp,$(LIB_OBJ_DIR)/%.o,$(filter %.cpp,$(SRCS))) SIM_DEFINE := $(shell echo $(SIM) | tr a-z A-Z) -# Use a common deine for Questa and Modelsim +# Use a common define for Questa and Modelsim ifeq ($(SIM_DEFINE),QUESTA) SIM_DEFINE = MODELSIM endif diff --git a/makefiles/Makefile.sim b/makefiles/Makefile.sim index bf046bd2..d4dba5a7 100644 --- a/makefiles/Makefile.sim +++ b/makefiles/Makefile.sim @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013, 2018 Potential Ventures Ltd # Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # @@ -45,7 +45,7 @@ HAVE_SIMULATOR = $(shell if [ -f $(SIM_ROOT)/makefiles/simulators/Makefile.$(SIM AVAILABLE_SIMULATORS = $(patsubst .%,%,$(suffix $(wildcard $(SIM_ROOT)/makefiles/simulators/Makefile.*))) ifeq ($(HAVE_SIMULATOR),0) -$(error "Couldn't find makefile for simulator: "$(SIM_LOWERCASE)"! Available simualtors: $(AVAILABLE_SIMULATORS)") +$(error "Couldn't find makefile for simulator: "$(SIM_LOWERCASE)"! Available simulators: $(AVAILABLE_SIMULATORS)") endif # We want to include the Python files from Cocotb in the dependencies diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py index 8c866a13..fb55cfdb 100644 --- a/tests/test_cases/test_array/test_array.py +++ b/tests/test_cases/test_array/test_array.py @@ -455,5 +455,5 @@ def test_extended_identifiers(dut): yield Timer(2000) tlog.info("Checking extended identifiers.") - _check_type(tlog, dut._id("\\ext_id\\",extended=False), ModifiableObject) + _check_type(tlog, dut._id("\\ext_id\\", extended=False), ModifiableObject) _check_type(tlog, dut._id("!"), ModifiableObject) diff --git a/tests/test_cases/test_avalon/test_avalon.py b/tests/test_cases/test_avalon/test_avalon.py old mode 100644 new mode 100755 index 3541eb48..e3f33611 --- a/tests/test_cases/test_avalon/test_avalon.py +++ b/tests/test_cases/test_avalon/test_avalon.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -''' Copyright (c) 2013 Potential Ventures Ltd +''' Copyright (c) 2013, 2018 Potential Ventures Ltd Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. @@ -31,7 +31,7 @@ """ A set of tests that demonstrate cocotb functionality -Also used a regression test of cocotb capabilities +Also used as regression test of cocotb capabilities """ import cocotb @@ -61,7 +61,7 @@ def __init__(self, dut, avlproperties={}): avl_properties=avlproperties) @cocotb.coroutine def init_sig(self, burstcount_w, address): - """ initialize all signals""" + """ Initialize all signals """ yield Timer(10) self.dut.reset = 0 self.dut.user_read_buffer = 0 diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py old mode 100644 new mode 100755 index 134c6d5b..ea3f9189 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -''' Copyright (c) 2013 Potential Ventures Ltd +''' Copyright (c) 2013, 2018 Potential Ventures Ltd Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. @@ -33,7 +33,7 @@ """ A set of tests that demonstrate cocotb functionality -Also used a regression test of cocotb capabilities +Also used as regression test of cocotb capabilities """ import cocotb @@ -109,7 +109,7 @@ def clock_gen(clock): @cocotb.test(expect_fail=False) def test_yield_list(dut): - """Example of yeilding on a list of triggers""" + """Example of yielding on a list of triggers""" clock = dut.clk cocotb.scheduler.add(clock_gen(clock)) yield [Timer(1000), Timer(2000)] @@ -232,7 +232,7 @@ def test_timer_with_units(dut): time_step = get_sim_time(units='fs') - time_fs try: - #Yield for 2.5 timesteps, should throw exception + # Yield for 2.5 timesteps, should throw exception yield Timer(2.5*time_step, units='fs') raise TestFailure("Timers should throw exception if time cannot be achieved with simulator resolution") except ValueError: @@ -266,7 +266,7 @@ def test_timer_with_units(dut): @cocotb.test(expect_fail=False) def test_anternal_clock(dut): - """Test ability to yeild on an external non cocotb coroutine decorated + """Test ability to yield on an external non cocotb coroutine decorated function""" clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) count = 0 @@ -581,7 +581,7 @@ def test_edge_count(dut): yield Timer(clk_period * (edge_count + 1)) if edge_count is not edges_seen: - raise TestFailure("Correct edge count failed saw %d wanted %d" % + raise TestFailure("Correct edge count failed - saw %d wanted %d" % (edges_seen, edge_count)) class StrCallCounter(object): @@ -595,7 +595,7 @@ def __str__(self): @cocotb.test() def test_logging_with_args(dut): counter = StrCallCounter() - dut._log.logger.setLevel(logging.INFO) #To avoid logging debug message, to make next line run without error + dut._log.logger.setLevel(logging.INFO) # To avoid logging debug message, to make next line run without error dut._log.debug("%s", counter) assert counter.str_counter == 0 @@ -606,7 +606,7 @@ def test_logging_with_args(dut): dut._log.warning("Testing multiple line\nmessage") - yield Timer(100) #Make it do something with time + yield Timer(100) # Make it do something with time @cocotb.test() def test_clock_cycles(dut): @@ -633,15 +633,15 @@ def test_binary_value(dut): values in a style familiar to rtl coders. """ - vec = BinaryValue(value=0,bits=16) - dut._log.info("Checking default endianess is Big Endian.") + vec = BinaryValue(value=0, bits=16) + dut._log.info("Checking default endianness is Big Endian.") if not vec.big_endian: - raise TestFailure("The default endianess is Little Endian - was expecting Big Endian.") + raise TestFailure("The default endianness is Little Endian - was expecting Big Endian.") if vec.integer != 0: raise TestFailure("Expecting our BinaryValue object to have the value 0.") dut._log.info("Checking single index assignment works as expected on a Little Endian BinaryValue.") - vec = BinaryValue(value=0,bits=16,bigEndian=False) + vec = BinaryValue(value=0, bits=16, bigEndian=False) if vec.big_endian: raise TestFailure("Our BinaryValue object is reporting it is Big Endian - was expecting Little Endian.") for x in range(vec._bits): @@ -649,9 +649,9 @@ def test_binary_value(dut): dut._log.info("Trying vec[%s] = 1" % x) expected_value = 2**(x+1) - 1 if vec.integer != expected_value: - raise TestFailure("Failed on assignment to vec[%s] - expecting %s - got %s" % (x,expected_value,vec.integer)) + raise TestFailure("Failed on assignment to vec[%s] - expecting %s - got %s" % (x, expected_value, vec.integer)) if vec[x] != 1: - raise TestFailure("Failed on index compare on vec[%s] - expecting 1 - got %s" % (x,vec[x])) + raise TestFailure("Failed on index compare on vec[%s] - expecting 1 - got %s" % (x, vec[x])) dut._log.info("vec = 'b%s" % vec.binstr) dut._log.info("Checking slice assignment works as expected on a Little Endian BinaryValue.") @@ -659,15 +659,15 @@ def test_binary_value(dut): raise TestFailure("Expecting our BinaryValue object to be 65535 after the end of the previous test.") vec[7:0] = '00110101' if vec.binstr != '1111111100110101': - raise TestFailure("Set lower 8-bits to 00110101 but readback %s" % vec.binstr) + raise TestFailure("Set lower 8-bits to 00110101 but read back %s" % vec.binstr) if vec[7:0].binstr != '00110101': - raise TestFailure("Set lower 8-bits to 00110101 but readback %s from vec[7:0]" % vec[7:0].binstr) + raise TestFailure("Set lower 8-bits to 00110101 but read back %s from vec[7:0]" % vec[7:0].binstr) dut._log.info("vec[7:0] = 'b%s" % vec[7:0].binstr) dut._log.info("vec[15:8] = 'b%s" % vec[15:8].binstr) dut._log.info("vec = 'b%s" % vec.binstr) - yield Timer(100) #Make it do something with time + yield Timer(100) # Make it do something with time # This is essentially six.exec_ @@ -692,7 +692,7 @@ def exec_(_code_, _globs_=None, _locs_=None): @cocotb.test(skip=sys.version_info[:2] < (3, 3)) def test_coroutine_return(dut): - """ Test that the python 3.3 syntax for returning from generators works """ + """ Test that the Python 3.3 syntax for returning from generators works """ # this would be a syntax error in older python, so we do the whole # thing inside exec exec_(textwrap.dedent(""" diff --git a/tests/test_cases/test_exit_error/test_exit.py b/tests/test_cases/test_exit_error/test_exit.py index 9b65eacb..5968382d 100644 --- a/tests/test_cases/test_exit_error/test_exit.py +++ b/tests/test_cases/test_exit_error/test_exit.py @@ -6,8 +6,8 @@ from cocotb.result import TestFailure from cocotb.binary import BinaryValue -# This will cause the sim to exit but we want to to do this nicely -# If thi was in another module then the remaining tests would also fail +# This will cause the sim to exit but we want to do this nicely +# If this was in another module then the remaining tests would also fail @cocotb.test(expect_error=True) def typosyntax_error(): diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py old mode 100644 new mode 100755 index e435e589..50fac0f8 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -''' Copyright (c) 2013 Potential Ventures Ltd +''' Copyright (c) 2013, 2018 Potential Ventures Ltd Copyright (c) 2013 SolarFlare Communications Inc All rights reserved. @@ -127,7 +127,7 @@ def test_callable_fail(dut): Test creates a thread to simulate another context. This thread will then "block" for 5 clock cycles but not using the function decorator. - No cycls should be seen. + No cycles should be seen. """ global g_dut global test_count @@ -188,8 +188,8 @@ def clock_monitor(dut): @cocotb.test(expect_fail=False) def test_time_in_external(dut): - """Test that the simulation time does no advance if the wrapped external - routine does not its self yield""" + """Test that the simulation time does not advance if the wrapped external + routine does not itself yield""" clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) yield Timer(10, 'ns') time = get_sim_time('ns') @@ -207,7 +207,7 @@ def test_time_in_external(dut): @cocotb.test(expect_fail=False) def test_ext_call_return(dut): - """Test ability to yeild on an external non cocotb coroutine decorated + """Test ability to yield on an external non cocotb coroutine decorated function""" mon = cocotb.scheduler.queue(clock_monitor(dut)) clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) @@ -217,7 +217,7 @@ def test_ext_call_return(dut): @cocotb.test(expect_fail=False) def test_ext_call_nreturn(dut): - """Test ability to yeild on an external non cocotb coroutine decorated + """Test ability to yield on an external non cocotb coroutine decorated function""" mon = cocotb.scheduler.queue(clock_monitor(dut)) clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) @@ -271,8 +271,8 @@ def test_external_from_fork(dut): dut._log.info("Back from join") @cocotb.test(expect_fail=True, skip=True) -def ztest_ext_exit_error(dut): - """Test that a premature exit of the sim at it's request still results in +def test_ext_exit_error(dut): + """Test that a premature exit of the sim at its request still results in the clean close down of the sim world""" yield external(test_ext_function)(dut) yield Timer(1000) diff --git a/tests/test_cases/test_multi_dimension_array/Makefile b/tests/test_cases/test_multi_dimension_array/Makefile index ac5194cd..64bd739c 100644 --- a/tests/test_cases/test_multi_dimension_array/Makefile +++ b/tests/test_cases/test_multi_dimension_array/Makefile @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2016 Potential Ventures Ltd +# Copyright (c) 2016, 2018 Potential Ventures Ltd # Copyright (c) 2016 SolarFlare Communications Inc # All rights reserved. # @@ -29,10 +29,10 @@ ifeq ($(SIM),) all: - @echo "Skipping test_array since icarus doesn't support indexing" + @echo "Skipping test_multi_dimension_array since icarus doesn't support indexing" else ifeq ($(shell echo $(SIM) | tr A-Z a-z),icarus) all: - @echo "Skipping test_array since icarus doesn't support indexing" + @echo "Skipping test_multi_dimension_array since icarus doesn't support indexing" else include ../../designs/multi_dimension_array/Makefile From 9f180322e4e6e80abae2e29ea46076dbb84cebf3 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Wed, 14 Nov 2018 15:19:23 +0100 Subject: [PATCH 1434/2656] Add contribution guidelines To revive the cocotb development community a group of developers has agreed to create a contribution guide which formalizes existing processes and adds new one. The goal of this guide is to eliminate bottlenecks in the development process, and ultimately provide a pleasant development experience for all parties involved. The most critical aspect for developers is a fast process from opening a PR to merging changes. This guide tries to streamline reviews by - stating explicitly that reviews are expected within one week, - requiring only a single positive review in cases where no maintainer objects, - making "merging" a process step which can be done by any maintainer if a well-defined process (including reviews) has been followed. The guide also helps to relieve stress from maintainers by sharing the review load among multiple parties. Further parts of this guide are more on the logistics side, including a description of GitHub labels. The release process isn't very well defined at the moment, I see this as a first shot and expect clearifications in the future. The list of maintainers is based on the notes I've taken during the conference call, please correct and extend as needed. Also please add areas of expertise if possible. Finally, the code of conduct is something we hopefully never need to make use of. It's standard practice these days to include one; I've chosen the FOSSi Foundation one, which is itself based on the Apache Code of Conduct. --- CONTRIBUTING.md | 140 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..edf446c3 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,140 @@ +Cococtb Contribution Guidelines +=============================== + +Welcome to the cocotb development! +We are an inclusive community with the common goal of improving the cocotb, a coroutine based cosimulation library for writing VHDL and Verilog testbenches in Python. +This guide explains how to contribute to cocotb, and documents the processes we agreed on to manage the project. +All processes in this document are designed to streamline the development effort, to avoid bottlenecks, and to ultimately give a pleasant experience to all involved. + +Architecture and Scope of Cocotb +-------------------------------- + +Cocotb has seen adoption in a wide variety of scenarios with sometimes conflicting requirements. +To foster experimentation and to decentralize the development process the architecture of cocotb is highly modular. +A solid core forms the foundation upon which extensions can provide higher-level functionality. + +The core of cocotb are +- the infrastructure to write testbenches with coroutines, threads, etc., +- the abstraction and interaction with simulators through interfaces like VPI, GPI, etc., +- tooling to run tests, and +- core primitives to interact with the simulation: triggers, data access classes, etc. + +As a general rule, functionality beyond this core set should go into extensions. +However, none of these rules are set in stone. +They can and should be challenged at times to ensure the project stays relevant to the majority of its users. + + +How to Get Changes Merged +------------------------- + +Have you fixed a bug in cocotb, or want to add new functionality to it? +Cocotb follows the typical [GitHub flow](https://guides.github.com/introduction/flow/) and makes use of pull requests and reviews. +Follow the steps below to get your changes merged, i.e. integrated into the main cocotb codebase. + +1. Create an issue ticket on [cocotb's GitHub issue tracker](https://github.com/potentialventures/cocotb/issues) describing the problem. + Issues are also a good place to discuss design options with others before writing code. +2. [Fork](https://help.github.com/articles/fork-a-repo/) the [cocotb GitHub repository](https://github.com/potentialventures/cocotb) into your personal namespace. +3. Create a new branch off the `master` branch for your set of changes. + Use one branch per "topic," i.e. per set of changes which belong together. +4. Create one or multiple commits to address the issue. + Make sure to read and follow the [Patch Requirements](#patch-requirements) when preparing your commits. +5. Create new [pull request (PR)](https://github.com/potentialventures/cocotb/pulls). +6. When you submit (or update) the pull request, a suite of regression tests will run. + If any of them turns "red," i.e. reports a failure, you most likely need to fix your code before it can be merged. +7. The pull request needs to be reviewed by at least one maintainer. + We aim to give feedback to all pull requests within a week, but as so often, life can get in the way. + If you receive no feedback from a maintainer within that time, please contact him/her directly (e.g. on [Gitter](https://gitter.im/cocotb) or email). You can find a [list of all maintainers](#maintainers) and their main area of expertise [below](#maintainers). + If a maintainer asks you to explain or modify code, try to do so. +8. Once your code has at least one positive review from a maintainer and no maintainer strongly objects it your code is ready to be merged into the `master` branch. + + +Patch Requirements +------------------ + +All changes which should go into the main codebase of cocotb must follow this set of requirements. + +- The code must be within the [scope of cocotb](#architecture-and-scope-of-cocotb). +- All code must be licensed under the [Revised BSD License](https://github.com/potentialventures/cocotb/blob/master/LICENSE). + By contributing to this project you signal your agreement with these license terms. +- All code must follow the established coding standards. + For Python code, follow the [PEP 8](https://www.python.org/dev/peps/pep-0008/) style guide. +- All code must pass existing tests. + New functionality must be accompanied by tests, and bug fixes should add tests to increase the test coverage and prevent regressions. +- If code changes or enhances documented behavior the documentation should be updated. +- All pull requests must be accepted by at least one maintainer, with no maintainer strongly objecting. + Reviews must be performed by a person other than the primary author of the code. +- All commits should follow established best practices when creating a commit message: + - The first line of the commit message is the short summary of what the code change does. + Keep this line below 50 characters. + - Then have one blank line. + - Now comes the long description of the commit. + Use this text to discuss things which are not obvious from the code, especially *why* changes were made. + Include the GitHub issue number (if one exists) in the form "Fixes #nnn" ([read more about that](https://help.github.com/articles/closing-issues-using-keywords/)). + Keep each description line below 72 characters. + + +Managing of Issues and Pull Requests +------------------------------------ + +The cocotb project makes use of GitHub labels attached to issues and pull requests to structure the development process. +Each issue and pull request can have multiple labels assigned. + +The `type` labels define the type of issue or PR: +- `type:bug`: a bug in existing functionality +- `type:feature`: new functionality +- `type:question`: a support question + +The `status` labels give a quick impression of the current status of the issue or PR: +- `status:worksforme`: the issue it not reproducible, or intended behavior (i.e. not a bug) +- `status:on-hold`: further progress is blocked by a dependency, e.g. other code which must be commited first. +- `status:needinfo`: feedback from someone is required. The issue/PR text gives more details. +- `status:duplicate`: the same issue is already being handled in another issue/PR. + +For the use in pull requests the following additional status labels are defined: +- `status:review-needed`: this PR needs at least one review +- `status:changes-requested`: changes are requested to the code +- `status:ready-for-merge`: this PR is ready (according to the [Patch Requirements](#patch-requirements)) to be merged + +The `category` labels help maintainers to filter issues which are relevant to their area of expertise: +- `category:windows`: Microsoft Windows-specific issues +- `category:simulators`: simulator support, including VPI/GPI/etc. +- `category:packaging`: issues related to (PyPi) packaging, etc. +- `category:docs`: documentation issues and fixes + +To help new contributors find a good issue to work on one more label is used (following [GitHub standard practices](#https://help.github.com/articles/helping-new-contributors-find-your-project-with-labels/)): +- `good first issue`: this issue is a good starting point for new contributors. + The issue should give an actionable description of what to do to complete this task, along with contact information of a mentor for this task. + +cocotb explicitly uses no priority labels, as experience indicates that they provide little value. + +Issues and pull requests which are invalid, or where feedback is lacking for four weeks, should be closed. + +Cocotb Releases +--------------- + +cocotb aims to keep the `master` branch always in a releasable state. +At least four times a year an official release should be created. +It is the job of the maintainers to find a suitable time for a release, to communicate it to the community, and to coordinate it. + + +Maintainers +----------- + +Cocotb uses a shared maintainer model. +Most maintainers are experts in part of the cocotb codebase, and are primarily responsible for reviews in this area. + +- Julius Baxter (@juliusbaxter) +- Luke Darnell (@lukedarnell) +- Tomasz Hemperek (@themperek) +- Chris Higgs (@chiggs). + Founder of cocotb. +- Stuart Hodgson (@stuarthodgson). + Founder of cocotb. +- Philipp Wagner (@imphil) + +Code of Conduct +--------------- + +The cocotb development community aims to be welcoming to everyone. +The [FOSSi Foundation Code of Conduct](https://www.fossi-foundation.org/code-of-conduct) applies. +Please contact any of the maintainers if you feel uncomfortable in the cocotb development community. \ No newline at end of file From 69f5fb53632cd721487bfa4908c1dd0c7d8e1e46 Mon Sep 17 00:00:00 2001 From: mciepluc Date: Tue, 27 Nov 2018 10:35:51 +0100 Subject: [PATCH 1435/2656] makefiles syntax standarization --- makefiles/simulators/Makefile.aldec | 12 ++++++------ makefiles/simulators/Makefile.ghdl | 5 +++-- makefiles/simulators/Makefile.ius | 3 +++ makefiles/simulators/Makefile.nvc | 4 ++-- makefiles/simulators/Makefile.questa | 14 +++++++------- makefiles/simulators/Makefile.vcs | 2 +- 6 files changed, 22 insertions(+), 18 deletions(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 4feb17aa..fd2bca4f 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -53,6 +53,11 @@ ifeq ($(GUI),1) CMD += -nosplash endif +# below allows for maintaining legacy syntax as well as enables using cross-simulator vars COMPILE_ARGS/SIM_ARGS +ALOG_ARGS += $(COMPILE_ARGS) +ACOM_ARGS += $(COMPILE_ARGS) +ASIM_ARGS += $(SIM_ARGS) + RTL_LIBRARY ?= work ALOG_ARGS += +define+COCOTB_SIM -dbg -pli libgpi ACOM_ARGS += -dbg @@ -64,7 +69,6 @@ endif GPI_EXTRA:= ifeq ($(TOPLEVEL_LANG),verilog) - GPI_ARGS = -pli libvpi ifneq ($(VHDL_SOURCES),) GPI_EXTRA = vhpi @@ -114,10 +118,6 @@ ifeq ($(GUI),1) else @echo "run -all" >> $@ @echo "endsim" >> $@ -ifeq ($(COVERAGE),1) - @echo "acdb report -db $(RTL_LIBRARY).acdb -html -o coverage/acdb_report.html" >> $@ - @echo "acdb report -db $(RTL_LIBRARY).acdb -txt -o coverage/acdb_report.txt" >> $@ -endif endif ifeq ($(OS),Msys) @@ -150,7 +150,7 @@ endif # that turns on batch mode (i.e. exit on completion/error) results.xml: $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(COCOTB_VPI_LIB) $(CUSTOM_SIM_DEPS) set -o pipefail; cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) -do runsim.tcl | tee sim.log + $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) $(PLUSARGS) -do runsim.tcl | tee sim.log clean:: @rm -rf $(SIM_BUILD) diff --git a/makefiles/simulators/Makefile.ghdl b/makefiles/simulators/Makefile.ghdl index d93fcead..17c03abc 100644 --- a/makefiles/simulators/Makefile.ghdl +++ b/makefiles/simulators/Makefile.ghdl @@ -52,18 +52,19 @@ endif RTL_LIBRARY ?= work GHDL_ARGS ?= +GHDL_ARGS += $(EXTRA_ARGS) .PHONY: analyse # Compilation phase analyse: $(VHDL_SOURCES) $(SIM_BUILD) - cd $(SIM_BUILD) && $(CMD) -a $(GHDL_ARGS) --work=$(RTL_LIBRARY) $(VHDL_SOURCES) && $(CMD) -e $(GHDL_ARGS) --work=$(RTL_LIBRARY) $(TOPLEVEL) + cd $(SIM_BUILD) && $(CMD) -a $(GHDL_ARGS) $(COMPILE_ARGS) --work=$(RTL_LIBRARY) $(VHDL_SOURCES) && $(CMD) -e $(GHDL_ARGS) $(COMPILE_ARGS) --work=$(RTL_LIBRARY) $(TOPLEVEL) results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VPI_LIB) cd $(SIM_BUILD); \ PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(CMD) -r $(GHDL_ARGS) $(TOPLEVEL) --vpi=$(LIB_DIR)/libvpi.$(LIB_EXT) $(SIM_ARGS) + $(CMD) -r $(GHDL_ARGS) $(TOPLEVEL) --vpi=$(LIB_DIR)/libvpi.$(LIB_EXT) $(SIM_ARGS) $(PLUSARGS) clean:: -@rm -rf $(SIM_BUILD) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index b3ef61cc..2c8d2920 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -45,6 +45,9 @@ else export IUS_BIN_DIR endif +EXTRA_ARGS += $(COMPILE_ARGS) +EXTRA_ARGS += $(SIM_ARGS) + ifneq ($(ARCH),i686) EXTRA_ARGS += -64 endif diff --git a/makefiles/simulators/Makefile.nvc b/makefiles/simulators/Makefile.nvc index ee66982e..213a3980 100644 --- a/makefiles/simulators/Makefile.nvc +++ b/makefiles/simulators/Makefile.nvc @@ -34,7 +34,7 @@ RTL_LIBRARY ?= work # Compilation phase analyse: $(VHDL_SOURCES) $(SIM_BUILD) - cd $(SIM_BUILD) && $(CMD) --work=$(RTL_LIBRARY) -a $(VHDL_SOURCES) + cd $(SIM_BUILD) && $(CMD) --work=$(RTL_LIBRARY) -a $(VHDL_SOURCES) $(COMPILE_ARGS) $(EXTRA_ARGS) results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) cd $(SIM_BUILD) && LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) \ @@ -43,7 +43,7 @@ results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ LD_LIBRARY_PATH=$(LIB_DIR) \ - $(CMD) --work=$(RTL_LIBRARY) -r --load $(COCOTB_VHPI_LIB) $(TRACE) $(TOPLEVEL) + $(CMD) $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) --work=$(RTL_LIBRARY) -r --load $(COCOTB_VHPI_LIB) $(TRACE) $(TOPLEVEL) clean:: -@rm -rf $(SIM_BUILD) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index cb5a816f..76a09d33 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -45,14 +45,14 @@ endif RTL_LIBRARY ?= work +# below allows for maintaining legacy syntax as well as enables using cross-simulator vars COMPILE_ARGS/SIM_ARGS +VLOG_ARGS += $(COMPILE_ARGS) +VSIM_ARGS += $(SIM_ARGS) + ifdef VERILOG_INCLUDE_DIRS VLOG_ARGS += +incdir+$(VERILOG_INCLUDE_DIRS) endif -ifdef EXTRA_ARGS -VLOG_ARGS += $(EXTRA_ARGS) -endif - ifeq ($(GUI),1) CMD += -gui VSIM_ARGS += -onfinish stop @@ -95,12 +95,12 @@ ifneq ($(VHDL_SOURCES),) @echo "vcom -work $(RTL_LIBRARY) $(VCOM_ARGS) $(VHDL_SOURCES)" >> $@ endif ifneq ($(VERILOG_SOURCES),) - @echo "vlog -work $(RTL_LIBRARY) +define+COCOTB_SIM -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(VERILOG_SOURCES)" >> $@ + @echo "vlog -work $(RTL_LIBRARY) +define+COCOTB_SIM -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES)" >> $@ endif ifdef SCRIPT_FILE @echo "do $(SCRIPT_FILE)" >> $@ endif - @echo "vsim $(VSIM_ARGS) $(TOPLEVEL)" >> $@ + @echo "vsim $(VSIM_ARGS) $(EXTRA_ARGS) $(TOPLEVEL)" >> $@ ifeq ($(WAVES),1) @echo "log -recursive /*" >> $@ endif @@ -144,7 +144,7 @@ endif results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) set -o pipefail; cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) \ - $(CMD) -do runsim.do 2>&1 | tee sim.log + $(CMD) $(PLUSARGS) -do runsim.do 2>&1 | tee sim.log # Potential fix for buffered stdout, YMMV # STDOUT=$(SIM_BUILD)/sim.log stdbuf --output=0 $(CMD) -do runsim.do 2>&1 && stdbuf --output=0 --input=0 tail -f sim.log && exit $${PIPESTATUS[0]} diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index 0c5daf36..c5c48b17 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -67,7 +67,7 @@ $(SIM_BUILD)/pli.tab : $(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(COCOTB_LIBS) $(COCOTB_VPI_LIB) $(CUSTOM_COMPILE_DEPS) cd $(SIM_BUILD) && \ LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) TOPLEVEL=$(TOPLEVEL) \ - $(CMD) +acc+1 +vpi -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -debug -load libvpi.so $(COMPILE_ARGS) $(VERILOG_SOURCES) + $(CMD) $(PLUSARGS) +acc+1 +vpi -P pli.tab +define+COCOTB_SIM=1 -sverilog $(EXTRA_ARGS) -debug -load libvpi.so $(COMPILE_ARGS) $(VERILOG_SOURCES) # Execution phase results.xml: $(SIM_BUILD)/simv $(PYTHON_FILES) $(CUSTOM_SIM_DEPS) From 7499502e712a3a198bb616a00a104dc11e753fd0 Mon Sep 17 00:00:00 2001 From: mciepluc Date: Tue, 27 Nov 2018 12:15:14 +0100 Subject: [PATCH 1436/2656] workaround for #602 --- makefiles/simulators/Makefile.questa | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 76a09d33..28924ac2 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -45,6 +45,12 @@ endif RTL_LIBRARY ?= work +ifdef VLOG_ARGS +VLOG_ARGS = $(VLOG_ARGS) +else +VLOG_ARGS = -timescale 1ns/100ps -mfcu +acc=rmb +endif + # below allows for maintaining legacy syntax as well as enables using cross-simulator vars COMPILE_ARGS/SIM_ARGS VLOG_ARGS += $(COMPILE_ARGS) VSIM_ARGS += $(SIM_ARGS) From 11bd4391a195acb0088a1e9bfcb01d9359bae34b Mon Sep 17 00:00:00 2001 From: mciepluc Date: Tue, 27 Nov 2018 13:20:18 +0100 Subject: [PATCH 1437/2656] update examples --- examples/axi_lite_slave/tests/Makefile | 9 ++++++++- examples/dff/tests/Makefile | 4 +++- examples/mean/tests/Makefile | 4 +++- makefiles/simulators/Makefile.questa | 7 ++----- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/examples/axi_lite_slave/tests/Makefile b/examples/axi_lite_slave/tests/Makefile index 94e785ba..84a1110c 100644 --- a/examples/axi_lite_slave/tests/Makefile +++ b/examples/axi_lite_slave/tests/Makefile @@ -7,7 +7,14 @@ PYTHONPATH := ./model:$(PYTHONPATH) export PYTHONPATH -EXTRA_ARGS+=-I$(TOPDIR)/hdl/ +SIM ?= icarus + +#not sure if only icarus include is by -I ... +ifeq ($(SIM),icarus) +COMPILE_ARGS+=-I$(TOPDIR)/hdl/ +else +COMPILE_ARGS+=+incdir+$(TOPDIR)/hdl/ +endif #DUT VERILOG_SOURCES = $(TOPDIR)/hdl/axi_lite_slave.v diff --git a/examples/dff/tests/Makefile b/examples/dff/tests/Makefile index 980cc3be..20d92c46 100644 --- a/examples/dff/tests/Makefile +++ b/examples/dff/tests/Makefile @@ -21,7 +21,9 @@ MODULE=$(TOPLEVEL)_cocotb CUSTOM_SIM_DEPS=$(CWD)/Makefile -VSIM_ARGS=-t 1ps +ifeq ($(SIM),questa) +SIM_ARGS=-t 1ps +endif include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim diff --git a/examples/mean/tests/Makefile b/examples/mean/tests/Makefile index 91ccfd4d..cce95112 100644 --- a/examples/mean/tests/Makefile +++ b/examples/mean/tests/Makefile @@ -23,7 +23,9 @@ endif VHDL_SOURCES = $(WPWD)/../hdl/mean_pkg.vhd VHDL_SOURCES += $(WPWD)/../hdl/mean.vhd -VCOM_ARGS = -mixedsvvh +ifeq ($(SIM),questa) +COMPILE_ARGS = -mixedsvvh +endif VERILOG_SOURCES = $(WPWD)/../hdl/mean_sv.sv diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 5c41538b..00814d35 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -53,12 +53,9 @@ endif # below allows for maintaining legacy syntax as well as enables using cross-simulator vars COMPILE_ARGS/SIM_ARGS VLOG_ARGS += $(COMPILE_ARGS) +VCOM_ARGS += $(COMPILE_ARGS) VSIM_ARGS += $(SIM_ARGS) -ifdef VERILOG_INCLUDE_DIRS -VLOG_ARGS += +incdir+$(VERILOG_INCLUDE_DIRS) -endif - ifeq ($(GUI),1) CMD += -gui VSIM_ARGS += -onfinish stop @@ -97,7 +94,7 @@ ifneq ($(VHDL_SOURCES),) @echo "vcom -work $(RTL_LIBRARY) $(VCOM_ARGS) $(VHDL_SOURCES)" >> $@ endif ifneq ($(VERILOG_SOURCES),) - @echo "vlog -work $(RTL_LIBRARY) +define+COCOTB_SIM -timescale 1ns/100ps -mfcu +acc=rmb -sv $(VLOG_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES)" >> $@ + @echo "vlog -work $(RTL_LIBRARY) +define+COCOTB_SIM -sv $(VLOG_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES)" >> $@ endif ifdef SCRIPT_FILE @echo "do $(SCRIPT_FILE)" >> $@ From 64bc3cc7ca3547704aca1b04cedcf88f95464395 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 29 Nov 2018 11:12:16 +0100 Subject: [PATCH 1438/2656] Typofix: self.endain -> self.endian --- cocotb/drivers/amba.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cocotb/drivers/amba.py b/cocotb/drivers/amba.py index 70eebbd2..913522d3 100644 --- a/cocotb/drivers/amba.py +++ b/cocotb/drivers/amba.py @@ -216,7 +216,7 @@ def __init__(self, entity, name, clock, memory, callback=None, event=None, BusDriver.__init__(self, entity, name, clock) self.clock = clock - self.big_endain = big_endian + self.big_endian = big_endian self.bus.ARREADY.setimmediatevalue(1) self.bus.RVALID.setimmediatevalue(0) self.bus.RLAST.setimmediatevalue(0) @@ -258,7 +258,7 @@ def _write_data(self): burst_length = _awlen + 1 bytes_in_beat = self._size_to_bytes_in_beat(_awsize) - word = BinaryValue(bits=bytes_in_beat*8, bigEndian=self.big_endain) + word = BinaryValue(bits=bytes_in_beat*8, bigEndian=self.big_endian) if __debug__: self.log.debug( @@ -276,7 +276,7 @@ def _write_data(self): while True: if self.bus.WVALID.value: word = self.bus.WDATA.value - word.big_endian = self.big_endain + word.big_endian = self.big_endian _burst_diff = burst_length - burst_count _st = _awaddr + (_burst_diff * bytes_in_beat) # start _end = _awaddr + ((_burst_diff + 1) * bytes_in_beat) # end @@ -307,7 +307,7 @@ def _read_data(self): burst_length = _arlen + 1 bytes_in_beat = self._size_to_bytes_in_beat(_arsize) - word = BinaryValue(bits=bytes_in_beat*8, bigEndian=self.big_endain) + word = BinaryValue(bits=bytes_in_beat*8, bigEndian=self.big_endian) if __debug__: self.log.debug( From a463cee498346cb26fc215ced25c088039490665 Mon Sep 17 00:00:00 2001 From: mciepluc Date: Mon, 3 Dec 2018 11:46:29 +0100 Subject: [PATCH 1439/2656] log simplification --- tests/test_cases/test_external/test_external.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index 50fac0f8..6bef3e39 100755 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -169,7 +169,7 @@ def test_ext_function_return(dut): def test_print_sim_time(dut, base_time): # We are not calling out here so time should not advance # And should also remain consistent - for _ in range(10): + for _ in range(5): _t = get_sim_time('ns') dut._log.info("Time reported = %d", _t) if _t != base_time: @@ -194,7 +194,7 @@ def test_time_in_external(dut): yield Timer(10, 'ns') time = get_sim_time('ns') dut._log.info("Time at start of test = %d" % time) - for i in range(1000): + for i in range(100): dut._log.info("Loop call %d" % i) yield external(test_print_sim_time)(dut, time) From 64731e00fd1cc02b05f70547967b67e59441455c Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 7 Dec 2018 12:49:42 +0100 Subject: [PATCH 1440/2656] Document more environment variables Document more environment variables. Fixes #676 --- documentation/source/building.rst | 265 ++++++++++++++++-------------- 1 file changed, 142 insertions(+), 123 deletions(-) diff --git a/documentation/source/building.rst b/documentation/source/building.rst index 8f78b30a..85803d61 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -5,143 +5,162 @@ Build options and Environment Variables Make System =========== -Makefiles are provided for a variety of simulators in cocotb/makefiles/simulators. The common Makefile cocotb/makefiles/Makefile.sim includes the appropriate simulator makefile based on the contents of the SIM variable. +Makefiles are provided for a variety of simulators in :file:`cocotb/makefiles/simulators`. The common Makefile :file:`cocotb/makefiles/Makefile.sim` includes the appropriate simulator Makefile based on the contents of the ``SIM`` variable. Make Targets ------------ -Makefiles define two targets, 'regression' and 'sim', the default target is sim. +Makefiles define two targets, ``regression`` and ``sim``, the default target is ``sim``. -Both rules create a results file in the calling directory called 'results.xml'. This file is a JUnit-compatible output file suitable for use with `Jenkins `_. The 'sim' targets unconditionally re-runs the simulator whereas the regression target only re-builds if any dependencies have changed. +Both rules create a results file in the calling directory called :file:`results.xml`. This file is a JUnit-compatible output file suitable for use with `Jenkins `_. The ``sim`` targets unconditionally re-runs the simulator whereas the ``regression`` target only re-builds if any dependencies have changed. -Make phases +Make Phases ----------- -Typically the makefiles provided with Cocotb for various simulators use a separate *compile* and *run* target. This allows for a rapid re-running of a simulator if none of the RTL source files have changed and therefore the simulator does not need to recompile the RTL. +Typically the makefiles provided with Cocotb for various simulators use a separate ``compile`` and ``run`` target. This allows for a rapid re-running of a simulator if none of the RTL source files have changed and therefore the simulator does not need to recompile the RTL. Make Variables -------------- -GUI -~~~ - -Set this to 1 to enable the GUI mode in the simulator (if supported). - - - -SIM -~~~ - -Selects which simulator Makefile to use. Attempts to include a simulator specific makefile from cocotb/makefiles/makefile.$(SIM) - - -VERILOG_SOURCES -~~~~~~~~~~~~~~~ - -A list of the Verilog source files to include. - - -VHDL_SOURCES -~~~~~~~~~~~~~~~ - -A list of the VHDL source files to include. - - -COMPILE_ARGS -~~~~~~~~~~~~ - -Any arguments or flags to pass to the compile stage of the simulation. Only applies to simulators with a separate compilation stage (currently Icarus and VCS). - - -SIM_ARGS -~~~~~~~~ - -Any arguments or flags to pass to the execution of the compiled simulation. Only applies to simulators with a separate compilation stage (currently Icarus, VCS and GHDL). - -EXTRA_ARGS -~~~~~~~~~~ - -Passed to both the compile and execute phases of simulators with two rules, or passed to the single compile and run command for simulators which don't have a distinct compilation stage. - -CUSTOM_COMPILE_DEPS -~~~~~~~~~~~~~~~~~~~ - -Use to add additional dependencies to the compilation target; useful for defining additional rules to run pre-compilation or if the compilation phase depends on files other than the RTL sources listed in **VERILOG_SOURCES** or **VHDL_SOURCES**. - -CUSTOM_SIM_DEPS -~~~~~~~~~~~~~~~ - -Use to add additional dependencies to the simulation target. - -COCOTB_NVC_TRACE -~~~~~~~~~~~~~~~~ - -Set this to 1 to enable display VHPI trace when using nvc VHDL simulator. - -SIM_BUILD -~~~~~~~~~ - -Use to define a scratch directory for use by the simulator. The path is relative to the Makefile location. If not provided, the default scratch directory is "sim_build". - +.. glossary:: + + ``GUI`` + Set this to 1 to enable the GUI mode in the simulator (if supported). + + ``SIM`` + Selects which simulator Makefile to use. Attempts to include a simulator specific makefile from :file:`cocotb/makefiles/makefile.$(SIM)` + + ``VERILOG_SOURCES`` + A list of the Verilog source files to include. + + ``VHDL_SOURCES`` + A list of the VHDL source files to include. + + ``COMPILE_ARGS`` + Any arguments or flags to pass to the compile stage of the simulation. Only applies to simulators with a separate compilation stage (currently Icarus and VCS). + + ``SIM_ARGS`` + Any arguments or flags to pass to the execution of the compiled simulation. Only applies to simulators with a separate compilation stage (currently Icarus, VCS and GHDL). + + ``EXTRA_ARGS`` + Passed to both the compile and execute phases of simulators with two rules, or passed to the single compile and run command for simulators which don't have a distinct compilation stage. + + ``CUSTOM_COMPILE_DEPS`` + Use to add additional dependencies to the compilation target; useful for defining additional rules to run pre-compilation or if the compilation phase depends on files other than the RTL sources listed in :term:`VERILOG_SOURCES` or :term:`VHDL_SOURCES`. + + ``CUSTOM_SIM_DEPS`` + Use to add additional dependencies to the simulation target. + + ``COCOTB_NVC_TRACE`` + Set this to 1 to enable display of VHPI traces when using the nvc VHDL simulator. + + ``SIM_BUILD`` + Use to define a scratch directory for use by the simulator. The path is relative to the Makefile location. + If not provided, the default scratch directory is :file:`sim_build`. + + Environment Variables ===================== - - - -TOPLEVEL --------- - -Used to indicate the instance in the hierarchy to use as the DUT. If this isn't defined then the first root instance is used. - - -RANDOM_SEED ------------ - -Seed the Python random module to recreate a previous test stimulus. At the beginning of every test a message is displayed with the seed used for that execution: - -.. code-block:: bash - - INFO cocotb.gpi __init__.py:89 in _initialise_testbench Seeding Python random module with 1377424946 - - -To recreate the same stimulis use the following: - -.. code-block:: bash - - make RANDOM_SEED=1377424946 - - - -COCOTB_ANSI_OUTPUT ------------------- - -Use this to override the default behaviour of annotating cocotb output with -ANSI colour codes if the output is a terminal (isatty()). - -COCOTB_ANSI_OUTPUT=1 forces output to be ANSI regardless of the type stdout - -COCOTB_ANSI_OUTPUT=0 supresses the ANSI output in the log messages - -COCOTB_REDUCED_LOG_FMT ----------------------- - -If defined, log lines displayed in terminal will be shorter. It will print only -time, message type (INFO, WARNING, ERROR) and log message. - -MODULE ------- - -The name of the module(s) to search for test functions. Multiple modules can be specified using a comma-separated list. - - -TESTCASE --------- - -The name of the test function(s) to run. If this variable is not defined cocotb discovers and executes all functions decorated with @cocotb.test() decorator in the supplied modules. - -Multiple functions can be specified in a comma-separated list. - - + +.. glossary:: + + ``TOPLEVEL`` + Used to indicate the instance in the hierarchy to use as the DUT. + If this isn't defined then the first root instance is used. + + ``RANDOM_SEED`` + Seed the Python random module to recreate a previous test stimulus. + At the beginning of every test a message is displayed with the seed used for that execution: + + .. code-block:: bash + + INFO cocotb.gpi __init__.py:89 in _initialise_testbench Seeding Python random module with 1377424946 + + + To recreate the same stimuli use the following: + + .. code-block:: bash + + make RANDOM_SEED=1377424946 + + ``COCOTB_ANSI_OUTPUT`` + Use this to override the default behaviour of annotating Cocotb output with + ANSI colour codes if the output is a terminal (``isatty()``). + + ``COCOTB_ANSI_OUTPUT=1`` forces output to be ANSI regardless of the type stdout + + ``COCOTB_ANSI_OUTPUT=0`` supresses the ANSI output in the log messages + + ``COCOTB_REDUCED_LOG_FMT`` + If defined, log lines displayed in terminal will be shorter. It will print only + time, message type (``INFO``, ``WARNING``, ``ERROR``) and log message. + + ``MODULE`` + The name of the module(s) to search for test functions. Multiple modules can be specified using a comma-separated list. + + + ``TESTCASE`` + The name of the test function(s) to run. If this variable is not defined Cocotb + discovers and executes all functions decorated with the :py:class:`cocotb.test` decorator in the supplied modules. + + Multiple functions can be specified in a comma-separated list. + + +Additional Environment Variables +-------------------------------- + +.. glossary:: + + ``COCOTB_ATTACH`` + In order to give yourself time to attach a debugger to the simulator process before it starts to run, + you can set the environment variable ``COCOTB_ATTACH`` to a pause time value in seconds. + If set, Cocotb will print the process ID (PID) to attach to and wait the specified time before + actually letting the simulator run. + + ``COCOTB_ENABLE_PROFILING`` + Enable performance analysis of the Python portion of Cocotb. When set, a file :file:`test_profile.pstat` + will be written which contains statistics about the cumulative time spent in the functions. + + From this, a callgraph diagram can be generated with `gprof2dot `_ and ``graphviz``. + See the ``profile`` Make target in the ``endian_swapper`` example on how to set this up. + + ``COCOTB_HOOKS`` + A comma-separated list of modules that should be executed before the first test. + You can also use the :py:class:`cocotb.hook` decorator to mark a function to be run before test code. + + ``COCOTB_LOG_LEVEL`` + Default logging level to use. This is set to ``INFO`` unless overridden. + + ``COCOTB_RESOLVE_X`` + Defines how to resolve bits with a value of ``X``, ``Z``, ``U`` or ``W`` when being converted to integer. + Valid settings are: + + ``VALUE_ERROR`` + raise a ``ValueError`` exception + ``ZEROS`` + resolve to ``0`` + ``ONES`` + resolve to ``1`` + ``RANDOM`` + randomly resolve to a ``0`` or a ``1`` + + Set to ``VALUE_ERROR`` by default. + + ``COCOTB_SCHEDULER_DEBUG`` + Enable additional log output of the coroutine scheduler. + + ``MEMCHECK`` + HTTP port to use for debugging Python's memory usage. + When set to e.g. ``8088``, data will be presented at ``_. + + This needs the :py:mod:`cherrypy` and :py:mod:`dowser` Python modules installed. + + ``SIM_ROOT`` + The root directory of the Cocotb installation. + + ``VERSION`` + The version of the Cocotb installation. You probably don't want to modify this. + From f5c5407547f0cbcea07d40256ca12b4ed2123824 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Fri, 7 Dec 2018 11:51:45 +0000 Subject: [PATCH 1441/2656] CI: Rework travis testing - build iverilog as part of the CI run - split up py27 and py35 sims into indepentent tests - fold travis output for better readability - add flake8 test (marked with --exit-zero otherwise cocotb would fail) Fixes #658 --- .travis.yml | 39 ++++++++++++++++++++++++++++++--- Dockerfile | 58 ++++++++++++++++++++++++++++++++++++++------------ tests/Makefile | 8 +++++++ 3 files changed, 88 insertions(+), 17 deletions(-) diff --git a/.travis.yml b/.travis.yml index cbb6a70d..af982984 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,43 @@ # See Dockerfile for the full build instructions sudo: required -language: cpp +language: python +cache: pip + +# mainly for flake8 as everything else runs in docker +python: + - 3.5 + +git: + quiet: true services: - docker -script: - - docker build . +jobs: + include: + - stage: PythonLint + install: pip install flake8 + script: flake8 --exit-zero cocotb/ + - stage: SimTests + # env only here for easier visibility in travis-ci + env: + - COCOTB_PY27 + before_install: &docker_prep + - docker build -t cocotb . + - docker images -a + - docker run -d --name cocotb cocotb tail -f /dev/null + - docker ps -a + script: + - echo 'Running cocotb tests with Python 2.7 ...' && echo 'travis_fold:start:cocotb_py27' + - docker exec -it -e TRAVIS=true -e COCOTB=/build-py2/src cocotb bash -lc 'source /build-py2/venv/bin/activate; make -C /build-py2/src test' + - echo -e 'travis_fold:end:cocotb_py27' + - stage: SimTests + # env only here for easier visibility in travis-ci + env: + - COCOTB_PY35 + before_install: *docker_prep + script: + - echo 'Running cocotb tests with Python 3.5 ...' && echo -en 'travis_fold:start:cocotb_py35' + - docker exec -it -e TRAVIS=true -e COCOTB=/build-py3/src cocotb bash -lc 'set -x; source /build-py3/venv/bin/activate; make -C /build-py3/src test' + - echo 'travis_fold:end:cocotb_py35' diff --git a/Dockerfile b/Dockerfile index 56b27417..4100e943 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,49 @@ -FROM librecores/ci-osstools:2018.1-rc1 +FROM ubuntu:16.04 -# make sources available in docker image -RUN mkdir -p /src -ADD . /src -WORKDIR /src +# travis-ci only provides 2 +ARG MAKE_JOBS=-j2 -RUN apt-get update; apt-get install -y virtualenv python3-venv python3-dev +# Simulation +ARG ICARUS_VERILOG_VERSION=10_2 -# Build and test using Python 2 -RUN mkdir /build-py2; cp -r /src /build-py2 -ENV COCOTB=/build-py2/src -RUN bash -lc 'virtualenv /build-py2/venv; source /build-py2/venv/bin/activate; pip install coverage xunitparser; make -C /build-py2/src test' +RUN apt-get -qq update && apt-get -qq install -y --no-install-recommends \ + wget \ + git \ + gperf \ + make \ + autoconf \ + g++ \ + flex \ + bison \ + python2.7-dev python3-dev\ + python-pip \ + python3 \ + virtualenv \ + python3-venv \ + && rm -rf /var/lib/apt/lists/* \ + && apt-get clean \ + && pip install --upgrade pip \ + && g++ --version + +# Icarus Verilog +ENV ICARUS_VERILOG_VERSION=${ICARUS_VERILOG_VERSION} +WORKDIR /usr/src/iverilog +RUN git clone https://github.com/steveicarus/iverilog.git --depth=1 --branch v${ICARUS_VERILOG_VERSION} . \ + && sh autoconf.sh \ + && ./configure \ + && make -s ${MAKE_JOBS} \ + && make -s install \ + && rm -r /usr/src/iverilog + + +# make sources available in docker image - one copy per python version +COPY . /build-py2/src +COPY . /build-py3/src + +# Build and prepare using Python 2 +RUN bash -c 'virtualenv /build-py2/venv; source /build-py2/venv/bin/activate; pip install coverage xunitparser; deactivate' + +# Build and prepare virtual env using Python 3 +RUN bash -c 'python3 -m venv /build-py3/venv; source /build-py3/venv/bin/activate; pip install coverage xunitparser; deactivate' -# Build and test using Python 3 -RUN mkdir /build-py3; cp -r /src /build-py3 -ENV COCOTB=/build-py3/src -RUN bash -lce 'python3 -m venv /build-py3/venv; source /build-py3/venv/bin/activate; pip install coverage xunitparser; make -C /build-py3/src test' diff --git a/tests/Makefile b/tests/Makefile index fe874f3d..f7097d01 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -34,7 +34,15 @@ REGRESSIONS := $(shell ls test_cases/) all: $(REGRESSIONS) $(REGRESSIONS): +ifeq ($(TRAVIS),true) + @echo "travis_fold:start:$@" +endif cd test_cases/$@ && $(MAKE) +ifeq ($(TRAVIS),true) + @echo "travis_fold:end:$@" +endif + + clean: $(foreach TEST, $(REGRESSIONS), $(MAKE) -C test_cases/$(TEST) clean;) From b6a1666bfd9c67da01abc3f559ab2e60f63d0174 Mon Sep 17 00:00:00 2001 From: Ben Rosser Date: Mon, 21 May 2018 16:45:23 -0400 Subject: [PATCH 1442/2656] Allow the scheduler to handle yielding a list of coroutines and triggers Currently the cocotb scheduler understands how to yield a coroutine from another coroutine; it queues it and calls .join() automatically. It also understands how to yield a list of triggers and resume execution when the first one finishes. But it does not understand how to yield a list of triggers and coroutines (or just coroutines); it will complain if the list contains anything that's not a trigger. This commit fixes it so that if the scheduler encounters a list, it will iterate through that list and build a new one, calling .join() automatically on any objects of type RunningCoroutine. If the list contains something that was not a trigger or coroutine, it is dropped. Then the size of the new and old lists are compared; if they differ it means that the list contained something that was not a trigger or a coroutine, and so we do nothing. Otherwise we yield it. The commit also includes a very minimal test for this functionality. This resolves issue #588. --- cocotb/scheduler.py | 26 +++++++++++++++++---- tests/test_cases/issue_588/Makefile | 31 +++++++++++++++++++++++++ tests/test_cases/issue_588/issue_588.py | 29 +++++++++++++++++++++++ 3 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 tests/test_cases/issue_588/Makefile create mode 100644 tests/test_cases/issue_588/issue_588.py diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index f322053a..98b6e056 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -115,7 +115,7 @@ def thread_start(self): def thread_resume(self): self._propogate_state(external_state.RUNNING) - + def thread_wait(self): if _debug: self._log.debug("Waiting for the condition lock %s" % threading.current_thread()) @@ -639,9 +639,27 @@ def schedule(self, coroutine, trigger=None): self.log.debug("%s: is instance of Trigger" % result) self._coroutine_yielded(coroutine, [result]) - elif (isinstance(result, list) and - all(isinstance(t, Trigger) for t in result)): - self._coroutine_yielded(coroutine, result) + # If we get a list, make sure it's a list of triggers or coroutines. + # For every coroutine, replace it with coroutine.join(). + # This could probably be done more elegantly via list comprehension. + elif isinstance(result, list): + new_triggers = [] + for listobj in result: + if isinstance(listobj, Trigger): + new_triggers.append(listobj) + elif isinstance(listobj, cocotb.decorators.RunningCoroutine): + if _debug: + self.log.debug("Scheduling coroutine in list: %s" % + listobj.__name__) + if not listobj.has_started(): + self.queue(listobj) + new_trigger = listobj.join() + new_triggers.append(new_trigger) + + # Make sure the lists are the same size. If they are not, it means + # it contained something not a trigger/coroutine, so do nothing. + if len(new_triggers) == len(result): + self._coroutine_yielded(coroutine, new_triggers) else: msg = ("Coroutine %s yielded something the scheduler can't handle" diff --git a/tests/test_cases/issue_588/Makefile b/tests/test_cases/issue_588/Makefile new file mode 100644 index 00000000..fa8015e2 --- /dev/null +++ b/tests/test_cases/issue_588/Makefile @@ -0,0 +1,31 @@ +############################################################################### +# Copyright (c) 2018 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +include ../../designs/sample_module/Makefile + +MODULE = issue_588 diff --git a/tests/test_cases/issue_588/issue_588.py b/tests/test_cases/issue_588/issue_588.py new file mode 100644 index 00000000..311fad77 --- /dev/null +++ b/tests/test_cases/issue_588/issue_588.py @@ -0,0 +1,29 @@ +# Test case for issue 588- yielding both coroutines and triggers in a list. +# This is a very simple test; it just makes sure we can yield a list of both. + +import cocotb +from cocotb import triggers, result, utils, clock + +import sys + +@cocotb.coroutine +def sample_coroutine(dut): + """ Very simple coroutine that waits 5 ns.""" + yield triggers.Timer(5, "ns") + dut._log.info("Sample coroutine yielded.") + +@cocotb.test() +def issue_588_coroutine_list(dut): + """ Yield a list of triggers and coroutines.""" + + # Record simulation time. + current_time = utils.get_sim_time("ns") + + # Yield a list, containing a RisingEdge trigger and a coroutine. + yield [sample_coroutine(dut), triggers.Timer(100, "ns")] + + # Make sure that only 5 ns passed, because the sample coroutine + # terminated first. + new_time = utils.get_sim_time("ns") + if int(new_time - current_time) != 5: + raise result.TestFailure("Did not yield coroutine in list.") From 56f8d8f3443369ac8dcdc23e11aa2c672f5fe235 Mon Sep 17 00:00:00 2001 From: Ben Rosser Date: Tue, 22 May 2018 10:51:58 -0400 Subject: [PATCH 1443/2656] Properly handle scheduler case when list contains something unyieldable This reworks the "schedule" function in the scheduler slightly. To avoid re-adding the list comprehension to the "elif isinstance(list)" case, I added a flag called "yield_successful" to the function that gets set if the coroutine is able to successfully yield something. If this flag is set to False the "coroutine yielded something the scheduler can't handle" error is fired. I did it this way because, in the list case, we already need to loop through the list to schedule any coroutines present. So being able to set a flag (when encountering something that's not a coroutine or a trigger) and break from this loop seemed better than having to loop through twice. --- cocotb/scheduler.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 98b6e056..cf7465f9 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -619,6 +619,7 @@ def schedule(self, coroutine, trigger=None): return # Queue current routine to schedule when the nested routine exits + yield_successful = False if isinstance(result, cocotb.decorators.RunningCoroutine): if not result.has_started(): @@ -633,11 +634,13 @@ def schedule(self, coroutine, trigger=None): new_trigger = result.join() self._coroutine_yielded(coroutine, [new_trigger]) + yield_successful = True elif isinstance(result, Trigger): if _debug: self.log.debug("%s: is instance of Trigger" % result) self._coroutine_yielded(coroutine, [result]) + yield_successful = True # If we get a list, make sure it's a list of triggers or coroutines. # For every coroutine, replace it with coroutine.join(). @@ -655,13 +658,21 @@ def schedule(self, coroutine, trigger=None): self.queue(listobj) new_trigger = listobj.join() new_triggers.append(new_trigger) + else: + # If we encounter something not a coroutine or trigger, + # set the success flag to False and break out of the loop. + yield_successful = False + break # Make sure the lists are the same size. If they are not, it means # it contained something not a trigger/coroutine, so do nothing. if len(new_triggers) == len(result): self._coroutine_yielded(coroutine, new_triggers) + yield_successful = True - else: + # If we didn't successfully yield anything, thrown an error. + # Do it this way to make the logic in the list case simpler. + if not yield_successful: msg = ("Coroutine %s yielded something the scheduler can't handle" % str(coroutine)) msg += ("\nGot type: %s repr: %s str: %s" % From 3945446d330cd55a78d1649c61c7117cee99abeb Mon Sep 17 00:00:00 2001 From: Ben Rosser Date: Fri, 7 Dec 2018 16:50:32 +0100 Subject: [PATCH 1444/2656] Update docs to indicate that a list of triggers and coroutines can be yielded. --- documentation/source/coroutines.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/source/coroutines.rst b/documentation/source/coroutines.rst index 440b97bc..aea6e7ed 100644 --- a/documentation/source/coroutines.rst +++ b/documentation/source/coroutines.rst @@ -51,8 +51,8 @@ Before python 3.3, this requires a `ReturnValue` to be raised. raise TestFailure("Signal did not change") -Coroutines may also yield a list of triggers to indicate that execution should -resume if *any* of them fires: +Coroutines may also yield a list of triggers and coroutines to indicate that +execution should resume if *any* of them fires: .. code-block:: python From b292c74a34eff5b6fb599653c81bfd9979b1feca Mon Sep 17 00:00:00 2001 From: Joey Date: Fri, 7 Dec 2018 13:53:14 -0600 Subject: [PATCH 1445/2656] Update documentation * Updated reference. * Added Monitor doc string and updated lib ref so it shows up. Added private method documentation printing for sphinx. * Updated lib ref rst to included implementations included in cocotb package. Added info message to notify user when strict_type checking is on. --- cocotb/monitors/__init__.py | 28 +++++--- cocotb/scoreboard.py | 8 +-- documentation/source/library_reference.rst | 81 ++++++++++++++++++++++ 3 files changed, 105 insertions(+), 12 deletions(-) diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index 24a958f6..77385e0d 100644 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -54,16 +54,28 @@ def __init__(self): class Monitor(object): + """Base class for Monitor objects. Monitors are passive 'listening' objects + that monitor pins in or out of a DUT. This class should not be used + directly, but should be subclassed and the internal `_monitor_recv` method + should be overridden and decorated as a @coroutine. This `_monitor_recv` + method should capture some behavior of the pins, form a transaction, and + pass this transaction to the internal `_recv` method. The `_monitor_recv` + method is added to the cocotb scheduler during the `__init__` phase, so it + should not be yielded anywhere. + + The primary use of a Monitor is as an interface for a :Scoreboard:. + + :type callback: callable + :param callback: Will be called with each recovered transaction as the + argument. If the callback isn't used, received transactions will be placed + on a queue and the event used to notify any consumers. + + :type event: event + :param event: Object that supports a `set` method that will be called when + a transaction is received through the internal `_recv` method. + """ def __init__(self, callback=None, event=None): - """ - Constructor for a monitor instance - - callback will be called with each recovered transaction as the argument - - If the callback isn't used, received transactions will be placed on a - queue and the event used to notify any consumers. - """ self._event = event self._wait_event = None self._recvQ = deque() diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index 8dfbf1f1..61919a5d 100644 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -95,12 +95,12 @@ def compare(self, got, exp, log, strict_type=True): # Compare the types if strict_type and type(got) != type(exp): self.errors += 1 - log.error("Received transaction is a different type to expected " - "transaction") - log.info("Got: %s but expected %s" % + log.error("Received transaction type is different than expected") + log.info("Received: %s but expected %s" % (str(type(got)), str(type(exp)))) if self._imm: - raise TestFailure("Received transaction of wrong type") + raise TestFailure("Received transaction of wrong type. " + "Set strict_type=False to avoid this.") return # Or convert to a string before comparison elif not strict_type: diff --git a/documentation/source/library_reference.rst b/documentation/source/library_reference.rst index 9fd8baf3..49a01f47 100644 --- a/documentation/source/library_reference.rst +++ b/documentation/source/library_reference.rst @@ -79,12 +79,93 @@ Testbench Structure .. autoclass:: cocotb.drivers.Driver :members: + :private-members: .. autoclass:: cocotb.monitors.Monitor :members: + :private-members: + +.. autoclass:: cocotb.monitors.BusMonitor + :members: .. autoclass:: cocotb.scoreboard.Scoreboard :members: +Utilities +========= + +.. automodule:: cocotb.utils + :members: + +Implemented Testbench Structures +================================ + +Drivers +------- + +AD9361 +~~~~~~ +.. autoclass:: cocotb.drivers.ad9361.AD9361 + :members: + +AMBA +~~~~ + +Advanced Microcontroller Bus Archicture + +.. autoclass:: cocotb.drivers.amba.AXI4LiteMaster + :members: + +.. autoclass:: cocotb.drivers.amba.AXI4LiteSlave + :members: + + +Avalon +~~~~~~ + +.. autoclass:: cocotb.drivers.avalon.AvalonMM + :members: + +.. autoclass:: cocotb.drivers.avalon.AvalonMaster + :members: + +.. autoclass:: cocotb.drivers.avalon.AvalonMemory + :members: + +.. autoclass:: cocotb.drivers.avalon.AvalonST + :members: + +.. autoclass:: cocotb.drivers.avalon.AvalonSTPkts + :members: + +OPB +~~~ + +.. autoclass:: cocotb.drivers.opb.OPBMaster + :members: + +XGMII +~~~~~ + +.. autoclass:: cocotb.drivers.xgmii.XGMII + :members: + +Monitors +-------- + +Avalon +~~~~~~ + +.. autoclass:: cocotb.monitors.avalon.AvalonST + :members: + +.. autoclass:: cocotb.monitors.avalon.AvalonSTPkts + :members: + +XGMII +~~~~~ + +.. autoclass:: cocotb.monitors.xgmii.XGMII + :members: From 3044bbe902092fe742363bce980ac5fc8851f0fa Mon Sep 17 00:00:00 2001 From: pgfarley Date: Fri, 7 Dec 2018 20:24:12 +0000 Subject: [PATCH 1446/2656] Add Memory and MemoryWord to recognized array like handles This is necessary to introspect register arrays in Icarus. Fixes #685 --- lib/vpi/VpiImpl.cpp | 4 ++++ tests/designs/sample_module/sample_module.sv | 7 ++++++- tests/test_cases/test_discovery/test_discovery.py | 14 ++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 4289a5e0..3e36c50f 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -78,6 +78,7 @@ gpi_objtype_t to_gpi_objtype(int32_t vpitype) case vpiNetBit: case vpiReg: case vpiRegBit: + case vpiMemoryWord: return GPI_REGISTER; case vpiRealVar: @@ -88,6 +89,7 @@ gpi_objtype_t to_gpi_objtype(int32_t vpitype) case vpiRegArray: case vpiNetArray: case vpiGenScopeArray: + case vpiMemory: return GPI_ARRAY; case vpiEnumNet: @@ -152,6 +154,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, case vpiIntegerVar: case vpiIntegerNet: case vpiRealVar: + case vpiMemoryWord: new_obj = new VpiSignalObjHdl(this, new_hdl, to_gpi_objtype(type), false); break; case vpiParameter: @@ -161,6 +164,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, case vpiNetArray: case vpiInterfaceArray: case vpiPackedArrayVar: + case vpiMemory: new_obj = new VpiArrayObjHdl(this, new_hdl, to_gpi_objtype(type)); break; case vpiStructVar: diff --git a/tests/designs/sample_module/sample_module.sv b/tests/designs/sample_module/sample_module.sv index 9d44dedd..51c98c3c 100644 --- a/tests/designs/sample_module/sample_module.sv +++ b/tests/designs/sample_module/sample_module.sv @@ -103,5 +103,10 @@ for (idx = 0; idx < NUM_OF_MODULES; idx=idx+1) begin end endgenerate -endmodule +reg [7:0] register_array [1:0]; +always @(posedge clk) begin + // Ensure internal array is not optimized out + register_array[0] <= 0; +end +endmodule diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index f18ffdcd..0485377d 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -305,6 +305,19 @@ def access_boolean(dut): if (int(curr_val) == int(output_bool)): raise TestFailure("Value did not propogate") +@cocotb.test() +def access_internal_register_array(dut): + """Test access to an internal register array""" + + if (dut.register_array[0].value.binstr != "xxxxxxxx"): + raise TestFailure("Failed to access internal register array value") + + dut.register_array[1].setimmediatevalue(4) + + yield Timer(1) + + if (dut.register_array[1].value != 4): + raise TestFailure("Failed to set internal register array value") @cocotb.test(skip=True) def skip_a_test(dut): @@ -364,3 +377,4 @@ def _discover(obj): if expected_top != count: raise TestFailure("Expected %d found %d for cosLut" % (expected_top, count)) + From dd62b2b81ffbe6d652eedc4f88d43baac58f8fd3 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Fri, 7 Dec 2018 18:36:51 -0800 Subject: [PATCH 1447/2656] Log the exception type, not just the message Previously this would omit an important part of the exception, the type! --- cocotb/decorators.py | 8 ++++---- cocotb/result.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 9d774849..4b4fd369 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -137,7 +137,7 @@ def send(self, value): raise CoroutineComplete(callback=self._finished_cb) except Exception as e: self._finished = True - raise raise_error(self, "Send raised exception: %s" % (str(e))) + raise raise_error(self, "Send raised exception:") def throw(self, exc): return self._coro.throw(exc) @@ -232,7 +232,7 @@ def send(self, value): except StopIteration: raise TestSuccess() except Exception as e: - raise raise_error(self, "Send raised exception: %s" % (str(e))) + raise raise_error(self, "Send raised exception:") def _handle_error_message(self, msg): self.error_messages.append(msg) @@ -359,7 +359,7 @@ def _wrapped_hook(*args, **kwargs): try: return RunningCoroutine(self._func(*args, **kwargs), self) except Exception as e: - raise raise_error(self, str(e)) + raise raise_error(self, "Hook raised exception:") _wrapped_hook.im_hook = True _wrapped_hook.name = self._func.__name__ @@ -402,7 +402,7 @@ def _wrapped_test(*args, **kwargs): try: return RunningTest(self._func(*args, **kwargs), self) except Exception as e: - raise raise_error(self, str(e)) + raise raise_error(self, "Test raised exception:") _wrapped_test.im_test = True # For auto-regressions _wrapped_test.name = self._func.__name__ diff --git a/cocotb/result.py b/cocotb/result.py index 6d16f9d6..e78854e7 100644 --- a/cocotb/result.py +++ b/cocotb/result.py @@ -39,14 +39,14 @@ def raise_error(obj, msg): obj has a log method msg is a string """ - exc_type, exc_value, exc_traceback = sys.exc_info() + exc_info = sys.exc_info() # 2.6 cannot use named access if sys.version_info[0] >= 3: buff = StringIO() - traceback.print_tb(exc_traceback, file=buff) + traceback.print_exception(*exc_info, file=buff) else: buff_bytes = BytesIO() - traceback.print_tb(exc_traceback, file=buff_bytes) + traceback.print_exception(*exc_info, file=buff_bytes) buff = StringIO(buff_bytes.getvalue().decode("UTF-8")) obj.log.error("%s\n%s" % (msg, buff.getvalue())) exception = TestError(msg) From cf13e40e011bd3d04251cb567b9b97acaeeeab0f Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Fri, 7 Dec 2018 19:07:32 -0800 Subject: [PATCH 1448/2656] Enable and fix tests that were silently being skipped It turns out you can't use yield in exec - so we just conditionally create the test instead. The return test was falling afoul of gh-637, while the async test was committed accidentally in gh-635 --- tests/test_cases/test_cocotb/test_cocotb.py | 61 +++++---------------- 1 file changed, 14 insertions(+), 47 deletions(-) diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index bc98796f..61f1858e 100755 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -670,58 +670,25 @@ def test_binary_value(dut): yield Timer(100) # Make it do something with time -@cocotb.test(skip=sys.version_info[:2] < (3, 3)) -def test_coroutine_return(dut): - """ Test that the Python 3.3 syntax for returning from generators works """ - # this would be a syntax error in older python, so we do the whole - # thing inside exec - cocotb.utils.exec_(textwrap.dedent(""" - @cocotb.coroutine - def return_it(x): - return x - yield - - ret = yield return_it(42) - if ret != 42: - raise TestFailure("Return statement did not work") - """)) - - -@cocotb.test(skip=sys.version_info[:2] < (3, 5)) -def test_async(dut): +if sys.version_info[:2] >= (3, 3): # this would be a syntax error in older python, so we do the whole # thing inside exec cocotb.utils.exec_(textwrap.dedent(''' - # inner functions don't need the cocotb.coroutine decorator - but - # adding it should have no effect anyway - @cocotb.coroutine - async def wait_5_cycles_marked(): - await ClockCycles(clk, 5) - - async def wait_5_cycles_unmarked(): - await ClockCycles(clk, 5) - - + @cocotb.test() + def test_coroutine_return(dut): + """ Test that the Python 3.3 syntax for returning from generators works """ @cocotb.coroutine - async def test_clock_cycles_async(dut): - """ - Test the ClockCycles Trigger - """ - clk = dut.clk - - clk_gen = cocotb.fork(Clock(clk, 100).start()) - - await RisingEdge(clk) - - dut.log.info("After one edge") + def return_it(x): + # workaround for #gh-637 - need to yield something before finishing + yield Timer(1) - await wait_5_cycles_marked() - await wait_5_cycles_unmarked() + return x - dut.log.info("After 10 edges") + # this makes `return_it` a coroutine, even after we remove the + # workaround above + yield - # as along as an async function is marked with @cocotb.coroutine, it - # can be used in a non-async function, where `await` is a syntaxerror, - # with a yield - yield test_clock_cycles_async() + ret = yield return_it(42) + if ret != 42: + raise TestFailure("Return statement did not work") ''')) From 753c62ba4bb2810edb28224851d4445e21047808 Mon Sep 17 00:00:00 2001 From: David Stanford Date: Mon, 8 Jan 2018 12:57:10 -0600 Subject: [PATCH 1449/2656] corrections to get the axi_lite_slave example to work in python 3 and still work in python 2 --- examples/axi_lite_slave/tests/test_axi_lite_slave.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/axi_lite_slave/tests/test_axi_lite_slave.py b/examples/axi_lite_slave/tests/test_axi_lite_slave.py index cb57ed94..864bf6e2 100644 --- a/examples/axi_lite_slave/tests/test_axi_lite_slave.py +++ b/examples/axi_lite_slave/tests/test_axi_lite_slave.py @@ -165,7 +165,7 @@ def write_fail(dut): yield axim.write(ADDRESS, DATA) yield Timer(CLK_PERIOD * 10) except AXIProtocolError as e: - print("Exception: %s" % str(e)) + print ("Exception: %s" % str(e)) dut._log.info("Bus Successfully Raised an Error") raise TestSuccess() raise TestFailure("AXI Bus Should have raised an ERROR when writing to \ @@ -199,7 +199,7 @@ def read_fail(dut): yield axim.read(ADDRESS, DATA) yield Timer(CLK_PERIOD * 10) except AXIProtocolError as e: - print("Exception: %s" % str(e)) + print ("Exception: %s" % str(e)) dut._log.info("Bus Successfully Raised an Error") raise TestSuccess() raise TestFailure("AXI Bus Should have raised an ERROR when writing to \ From 618ab4013d323aa4862d11e3b18893f74d8e5027 Mon Sep 17 00:00:00 2001 From: David Stanford Date: Tue, 13 Feb 2018 08:39:15 -0600 Subject: [PATCH 1450/2656] in progress documentation update --- cocotb/triggers.py | 2 +- documentation/Makefile | 9 ++- documentation/source/coroutines.rst | 73 +++++++++++++++++++++- documentation/source/index.rst | 2 + documentation/source/introduction.rst | 2 +- documentation/source/library_reference.rst | 2 + documentation/source/quickstart.rst | 47 ++++++-------- documentation/source/simulator_support.rst | 1 + documentation/source/testbench_tools.rst | 15 +++++ documentation/source/triggers.rst | 4 +- 10 files changed, 121 insertions(+), 36 deletions(-) create mode 100644 documentation/source/testbench_tools.rst diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 7d13fcc5..969507b3 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -156,7 +156,7 @@ def ReadOnly(): class _ReadWrite(GPITrigger): """ - Execution will resume when the readwrite porttion of the sim cycles is + Execution will resume when the readwrite portion of the sim cycles is reached """ def __init__(self): diff --git a/documentation/Makefile b/documentation/Makefile index 5248dbf6..88087ad5 100644 --- a/documentation/Makefile +++ b/documentation/Makefile @@ -5,7 +5,9 @@ SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = -BUILDDIR = build +BUILDDIR = ../../cocotb_docs +PDFBUILDDIR = /tmp +PDF = ../manual.pdf # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) @@ -111,9 +113,10 @@ latex: "(use \`make latexpdf' here to do that automatically)." latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(PDFBUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf + $(MAKE) -C $(PDFBUILDDIR)/latex all-pdf + cp $(PDFBUILDDIR)/latex/*.pdf $(PDF) @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: diff --git a/documentation/source/coroutines.rst b/documentation/source/coroutines.rst index aea6e7ed..f4f41161 100644 --- a/documentation/source/coroutines.rst +++ b/documentation/source/coroutines.rst @@ -59,7 +59,7 @@ execution should resume if *any* of them fires: @cocotb.coroutine def packet_with_timeout(monitor, timeout): """Wait for a packet but timeout if nothing arrives""" - yield [Timer(timeout), monitor.wait_for_recv()] + yield [Timer(timeout), RisingEdge(dut.ready)] The trigger that caused execution to resume is passed back to the coroutine, @@ -71,7 +71,76 @@ allowing them to distinguish which trigger fired: def packet_with_timeout(monitor, timeout): """Wait for a packet but timeout if nothing arrives""" tout_trigger = Timer(timeout) - result = yield [tout_trigger, monitor.wait_for_recv()] + result = yield [tout_trigger, RisingEdge(dut.ready)] if result is tout_trigger: raise TestFailure("Timed out waiting for packet") + +Coroutines can be forked for parallel operation within a function of that code and the forked code. + +.. code-block:: python + @cocotb.test() + def test_act_during_reset(dut): + """ + while reset is active, toggle signals + """ + tb = uart_tb(dut) + cocotb.fork(Clock(dut.clk, 1000).start()) #Clock is a built in class for toggling a clock signal + + cocotb.fork(tb.reset_dut(dut.rstn,20000)) #reset_dut is a function part of the user generated uart_tb class. + + yield Timer(10000) + print("Reset is still active: %d" % dut.rstn) + yield Timer(15000) + print("Reset has gone inactive: %d" % dut.rstn) + + +Coroutines can be joined to end parallel operation within a function. + +.. code-block:: python + @cocotb.test() + def test_count_edge_cycles(dut, period=1000, clocks=6): + cocotb.fork(Clock(dut.clk, period).start()) + yield RisingEdge(dut.clk) + + timer = Timer(period + 10) + task = cocotb.fork(count_edges_cycles(dut.clk, clocks)) + count = 0 + expect = clocks - 1 + + while True: + result = yield [timer, task.join()] + if count > expect: + raise TestFailure("Task didn't complete in expected time") + if result is timer: + dut._log.info("Count %d: Task still running" % count) + count += 1 + else: + break + +Coroutines can be killed before they complete, forcing their completion before they'd naturally end. + +.. code-block:: python + @cocotb.test(expect_fail=False) + def test_different_clocks(dut): + clk_1mhz = Clock(dut.clk, 1.0, units='us') + clk_250mhz = Clock(dut.clk, 4.0, units='ns') + + clk_gen = cocotb.fork(clk_1mhz.start()) + start_time_ns = get_sim_time(units='ns') + yield Timer(1) + yield RisingEdge(dut.clk) + edge_time_ns = get_sim_time(units='ns') + if not isclose(edge_time_ns, start_time_ns + 1000.0): + raise TestFailure("Expected a period of 1 us") + + clk_gen.kill() + + clk_gen = cocotb.fork(clk_250mhz.start()) + start_time_ns = get_sim_time(units='ns') + yield Timer(1) + yield RisingEdge(dut.clk) + edge_time_ns = get_sim_time(units='ns') + if not isclose(edge_time_ns, start_time_ns + 4.0): + raise TestFailure("Expected a period of 4 ns") + diff --git a/documentation/source/index.rst b/documentation/source/index.rst index 6b3f91a0..1b24ca05 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -6,11 +6,13 @@ Contents: .. toctree:: :maxdepth: 2 + includeme introduction quickstart building coroutines triggers + testbench_tools library_reference endian_swapper ping_tun_tap diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index f2d72a54..9ab23aa2 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -26,7 +26,7 @@ Windows Platform * `Aldec `_ Riviera-PRO * `Mentor `_ Modelsim (DE and SE) -**Cocotb** can be used live in a web-browser using `EDA Playground `_. +An older version of **Cocotb** can be used live in a web-browser using `EDA Playground `_. diff --git a/documentation/source/library_reference.rst b/documentation/source/library_reference.rst index 49a01f47..a57d4145 100644 --- a/documentation/source/library_reference.rst +++ b/documentation/source/library_reference.rst @@ -38,6 +38,8 @@ Interacting with the Simulator .. autoclass:: cocotb.bus.Bus :members: +.. autoclass:: cocotb.clock.Clock + Triggers -------- diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index ec2fe7dd..ac21eec5 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -15,7 +15,7 @@ Cocotb has the following requirements: * Python-dev packages * GCC and associated development packages * GNU Make -* A Verilog simulator +* A Verilog or VHDL simulator, depending on your source RTL code Internal development is performed on Linux Mint 17 (x64). We also use Redhat 6.5(x64). Other Redhat and Ubuntu based distributions (x32 and x64) should work too but due fragmented nature of Linux we can not test everything. Instructions are provided for the main distributions we use. @@ -144,7 +144,7 @@ The endian swapper example includes both a VHDL and Verilog RTL implementation. .. code-block:: bash - $> make SIM=aldec TOPLEVEL_LANG=vhdl + $> make SIM=ghdl TOPLEVEL_LANG=vhdl @@ -167,8 +167,8 @@ python test script to load. .. code-block:: bash VERILOG_SOURCES = $(PWD)/submodule.sv $(PWD)/my_design.sv - TOPLEVEL=my_design - MODULE=test_my_design + TOPLEVEL=my_design #the module name in your verilog or vhdl file + MODULE=test_my_design # the name of the python test file include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim @@ -178,8 +178,9 @@ We would then create a file called ``test_my_design.py`` containing our tests. Creating a test --------------- -The test is written in Python. Assuming we have a toplevel port called ``clk`` -we could create a test file containing the following: +The test is written in Python. Cocotb wraps your top level with the handle **dut** +to be used in all python files referencing your RTL project. Assuming we have a +toplevel port called ``clk`` we could create a test file containing the following: .. code-block:: python @@ -230,12 +231,17 @@ Values can be assigned to signals using either the .value property of a handle o clk.value = 1 # Direct assignment through the hierarchy - dut.input_signal = 12 + dut.input_signal <= 12 # Assign a value to a memory deep in the hierarchy - dut.sub_block.memory.array[4] = 2 - - + dut.sub_block.memory.array[4] <= 2 + + +The "<=" operator is overridden by cocotb to help make it more clear when an object +being assigned a value is part of the RTL source as compared to the python test code. + + + Reading values from signals --------------------------- @@ -245,12 +251,15 @@ Accessing the .value property of a handle object will return a :class:`BinaryVal >>> # Read a value back from the dut >>> count = dut.counter.value - >>> + >>> >>> print(count.binstr) 1X1010 >>> # Resolve the value to an integer (X or Z treated as 0) >>> print(count.integer) 42 + >>> # Show number of bits in a value + >>> print(count.bits) + 6 We can also cast the signal handle directly to an integer: @@ -292,19 +301,3 @@ Parallel and sequential execution of coroutines yield reset_thread.join() dut._log.debug("After reset") - -Creating a test ---------------- - -.. code-block:: python - - import cocotb - from cocotb.triggers import Timer - - @cocotb.test(timeout=None) - def my_first_test(dut): - - # drive the reset signal on the dut - dut.reset_n <= 0 - yield Timer(12345) - dut.reset_n <= 1 diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index fa808dd0..88c3a384 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -41,3 +41,4 @@ Cadence Incisive GHDL ---- +Support is preliminary. diff --git a/documentation/source/testbench_tools.rst b/documentation/source/testbench_tools.rst new file mode 100644 index 00000000..0f0130a3 --- /dev/null +++ b/documentation/source/testbench_tools.rst @@ -0,0 +1,15 @@ + +Logging +======= + +Buses +===== + +Driving Busses +============== + +Monitoring Busses +================= + +Tracking testbench errors +========================= diff --git a/documentation/source/triggers.rst b/documentation/source/triggers.rst index 849e4460..c93883dc 100644 --- a/documentation/source/triggers.rst +++ b/documentation/source/triggers.rst @@ -1,7 +1,7 @@ Triggers ======== -Triggers are used to indicate when the scheduler should resume coroutine execution. Typically a coroutine will **yield** a trigger or a list of triggers. +Triggers are used to indicate when the cocotb scheduler should resume coroutine execution. Typically a coroutine will **yield** a trigger or a list of triggers, while it's waiting for them to complete. Simulation Timing ----------------- @@ -18,7 +18,7 @@ Registers a timed callback with the simulator to continue execution of the corou ReadOnly() ^^^^^^^^^^ -Registers a callback which will continue execution of the coroutine when the current simulation timestep moves to the ReadOnly phase. Useful for monitors which need to wait for all processes to execute (both RTL and cocotb) to ensure sampled signal values are final. +Registers a callback which will continue execution of the coroutine when the current simulation timestep moves to the ReadOnly phase of the rtl simulator. The ReadOnly phase is entered when the current timestep no longer has any further delta steps. This should be point where all the signal values are stable as there are no more rtl events scheduled for the timestep. The simulator should not allow scheduling of more events in this timestep. Useful for monitors which need to wait for all processes to execute (both RTL and cocotb) to ensure sampled signal values are final. From 3db3f16a2d73d7b1f6265d056b8585023de826aa Mon Sep 17 00:00:00 2001 From: David Stanford Date: Wed, 14 Feb 2018 15:57:28 -0600 Subject: [PATCH 1451/2656] update testbench documentation --- documentation/source/testbench_tools.rst | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/documentation/source/testbench_tools.rst b/documentation/source/testbench_tools.rst index 0f0130a3..8a6e7361 100644 --- a/documentation/source/testbench_tools.rst +++ b/documentation/source/testbench_tools.rst @@ -1,15 +1,35 @@ +################ +Test Bench Tools +################ Logging ======= +Cocotb extends the python logging library. Each dut, monitor, driver, and scoreboard (as well as any other function using the **coroutine** decorator) implements it's own logging object, and each can be set to it's own logging level. + +When logging hdl objects, beware that **_log** is the preferred way to use logging. This helps minimize the change of name collisions with an hdl log component with the python logging functionality. + Buses ===== +Busses are simply defined as collection of signals. The **Bus** class will automatically bundle any group of signals together that are named similar to dut.. for instance, + dut.stream_in_valid + dut.stream_in_data +have a bus name of ``stream_in``, a seperator of ``_``, and signal names of ``valid`` and ``data``. a list of signal names, or a dictionary mapping attribute names to signal names is also passed into the **Bus** class. Busses can have values driven onto them, be captured (returning a dictionary), or sampled and stored into a similar object. + + Driving Busses ============== +examples and specific bus implementation bus drivers (amba, avalon, xgmii, and others) exist in the **Driver** class enabling a test to append transactions to perform the serialization of transactions onto a physical interface. + + Monitoring Busses ================= +For our testbenches to actually be useful, we have to monitor some of these busses, and not just drive them. That's where the **Monitor** class comes in, with prebuilt Monitors for ``avalon`` and ``xgmii`` busses. The Monitor class is a base class which you are expected to derive for your particular purpose. You must create a `_monitor_recv()` function which is responsible for determining 1) at what points in simulation to call the `_recv()` function, and 2) what transaction values to pass to be stored in the monitors receiving queue. Monitors are good for both outputs of the dut for verification, and for the inputs of the dut, to drive a test model of the dut to be compared to the actual dut. For this purpose, input monitors will often have a callback function passed that is a model. This model will often generate expected transactions, which are then compared using the **Scoreboard** class. + Tracking testbench errors ========================= + +The **Scoreboard** class is used to compare the actual outputs to expected outputs. Monitors are added to the scoreboard for the actual outputs, and the expected outputs can be either a simple list, or a function that provides a transaction. From 046e0abde311b66615f6ddf887344a3931fe476a Mon Sep 17 00:00:00 2001 From: David Stanford Date: Wed, 21 Feb 2018 10:02:01 -0600 Subject: [PATCH 1452/2656] examples added to logging section of testbench_tools --- documentation/source/includeme.rst | 1 + documentation/source/introduction.rst | 2 +- documentation/source/testbench_tools.rst | 51 +++++++++++++++++++++++- 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 documentation/source/includeme.rst diff --git a/documentation/source/includeme.rst b/documentation/source/includeme.rst new file mode 100644 index 00000000..071aed38 --- /dev/null +++ b/documentation/source/includeme.rst @@ -0,0 +1 @@ +.. include :: ../../README.rst diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index 9ab23aa2..de403ba2 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -14,7 +14,7 @@ What is cocotb? Linux Platforms * `Icarus Verilog `_ -* `GHDL `_ +* `GHDL `_ * `Aldec `_ Riviera-PRO * `Synopsys `_ VCS * `Cadence `_ Incisive diff --git a/documentation/source/testbench_tools.rst b/documentation/source/testbench_tools.rst index 8a6e7361..d4540921 100644 --- a/documentation/source/testbench_tools.rst +++ b/documentation/source/testbench_tools.rst @@ -7,8 +7,57 @@ Logging Cocotb extends the python logging library. Each dut, monitor, driver, and scoreboard (as well as any other function using the **coroutine** decorator) implements it's own logging object, and each can be set to it's own logging level. -When logging hdl objects, beware that **_log** is the preferred way to use logging. This helps minimize the change of name collisions with an hdl log component with the python logging functionality. +When logging hdl objects, beware that **_log** is the preferred way to use logging. This helps minimize the change of name collisions with an hdl log component with the python logging functionality. +Log printing levels can also be set on a per object basis. + +.. code-block:: python +class EndianSwapperTB(object): + + def __init__(self, dut, debug=False): + self.dut = dut + self.stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) + self.stream_in_recovered = AvalonSTMonitor(dut, "stream_in", dut.clk, + callback=self.model) + + # Set verbosity on our various interfaces + level = logging.DEBUG if debug else logging.WARNING + self.stream_in.log.setLevel(level) + self.stream_in_recovered.log.setLevel(level) + + +And when the logging is actually called + +.. code-block:: python + + class AvalonSTPkts(BusMonitor): + ... + @coroutine + def _monitor_recv(self): + ... + self.log.info("Received a packet of %d bytes" % len(pkt)) + @cocotb.coroutine + class Scoreboard(object): + ... + def add_interface(self): + ... + self.log.info("Created with reorder_depth %d" % reorder_depth) + class EndianSwapTB(object): + ... + @cocotb.coroutine + def reset(): + self.dut._log.debug("Resetting DUT") + + +will display as something like + +.. code-block:: bash + + 0.00ns INFO cocotb.scoreboard.endian_swapper_sv scoreboard.py:177 in add_interface Created with reorder_depth 0 + 0.00ns DEBUG cocotb.endian_swapper_sv .._endian_swapper.py:106 in reset Resetting DUT + 160000000000000.00ns INFO cocotb.endian_swapper_sv.stream_out avalon.py:151 in _monitor_recv Received a packet of 125 bytes + + Buses ===== From 1c8cd040b8426e41e95c1e770ba5d953eb3a3877 Mon Sep 17 00:00:00 2001 From: David Stanford Date: Wed, 21 Feb 2018 10:19:49 -0600 Subject: [PATCH 1453/2656] cleanup logging example in testbench_tools --- documentation/source/testbench_tools.rst | 28 ++++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/documentation/source/testbench_tools.rst b/documentation/source/testbench_tools.rst index d4540921..3361c563 100644 --- a/documentation/source/testbench_tools.rst +++ b/documentation/source/testbench_tools.rst @@ -5,26 +5,26 @@ Test Bench Tools Logging ======= -Cocotb extends the python logging library. Each dut, monitor, driver, and scoreboard (as well as any other function using the **coroutine** decorator) implements it's own logging object, and each can be set to it's own logging level. +Cocotb extends the python logging library. Each dut, monitor, driver, and scoreboard (as well as any other function using the **coroutine** decorator) implements it's own logging object, and each can be set to it's own logging level. Within a dut, each heirarchical object can also have individual logging levels set. When logging hdl objects, beware that **_log** is the preferred way to use logging. This helps minimize the change of name collisions with an hdl log component with the python logging functionality. Log printing levels can also be set on a per object basis. .. code-block:: python -class EndianSwapperTB(object): - - def __init__(self, dut, debug=False): - self.dut = dut - self.stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) - self.stream_in_recovered = AvalonSTMonitor(dut, "stream_in", dut.clk, - callback=self.model) - - # Set verbosity on our various interfaces - level = logging.DEBUG if debug else logging.WARNING - self.stream_in.log.setLevel(level) - self.stream_in_recovered.log.setLevel(level) - + class EndianSwapperTB(object): + + def __init__(self, dut, debug=False): + self.dut = dut + self.stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) + self.stream_in_recovered = AvalonSTMonitor(dut, "stream_in", dut.clk, + callback=self.model) + + # Set verbosity on our various interfaces + level = logging.DEBUG if debug else logging.WARNING + self.stream_in.log.setLevel(level) + self.stream_in_recovered.log.setLevel(level) + self.dut.reset_n._log(setLevel(logging.DEBUG) And when the logging is actually called From 2fb82e92125e6d035dd94cc201c56e23eeed6619 Mon Sep 17 00:00:00 2001 From: David Stanford Date: Wed, 21 Feb 2018 10:23:33 -0600 Subject: [PATCH 1454/2656] formatting is tricky --- documentation/source/testbench_tools.rst | 29 ++++++++++++------------ 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/documentation/source/testbench_tools.rst b/documentation/source/testbench_tools.rst index 3361c563..dbb46a74 100644 --- a/documentation/source/testbench_tools.rst +++ b/documentation/source/testbench_tools.rst @@ -12,19 +12,20 @@ When logging hdl objects, beware that **_log** is the preferred way to use loggi Log printing levels can also be set on a per object basis. .. code-block:: python - class EndianSwapperTB(object): - - def __init__(self, dut, debug=False): - self.dut = dut - self.stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) - self.stream_in_recovered = AvalonSTMonitor(dut, "stream_in", dut.clk, - callback=self.model) + + class EndianSwapperTB(object): - # Set verbosity on our various interfaces - level = logging.DEBUG if debug else logging.WARNING - self.stream_in.log.setLevel(level) - self.stream_in_recovered.log.setLevel(level) - self.dut.reset_n._log(setLevel(logging.DEBUG) + def __init__(self, dut, debug=False): + self.dut = dut + self.stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) + self.stream_in_recovered = AvalonSTMonitor(dut, "stream_in", dut.clk, + callback=self.model) + + # Set verbosity on our various interfaces + level = logging.DEBUG if debug else logging.WARNING + self.stream_in.log.setLevel(level) + self.stream_in_recovered.log.setLevel(level) + self.dut.reset_n._log(setLevel(logging.DEBUG) And when the logging is actually called @@ -53,8 +54,8 @@ will display as something like .. code-block:: bash - 0.00ns INFO cocotb.scoreboard.endian_swapper_sv scoreboard.py:177 in add_interface Created with reorder_depth 0 - 0.00ns DEBUG cocotb.endian_swapper_sv .._endian_swapper.py:106 in reset Resetting DUT + 0.00ns INFO cocotb.scoreboard.endian_swapper_sv scoreboard.py:177 in add_interface Created with reorder_depth 0 + 0.00ns DEBUG cocotb.endian_swapper_sv .._endian_swapper.py:106 in reset Resetting DUT 160000000000000.00ns INFO cocotb.endian_swapper_sv.stream_out avalon.py:151 in _monitor_recv Received a packet of 125 bytes From fb92e017aa449e241ebd4a701decc4d5eed07d7f Mon Sep 17 00:00:00 2001 From: David Stanford Date: Wed, 21 Feb 2018 10:53:00 -0600 Subject: [PATCH 1455/2656] add bus examples --- documentation/source/testbench_tools.rst | 28 +++++++++++++++--------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/documentation/source/testbench_tools.rst b/documentation/source/testbench_tools.rst index dbb46a74..665a2f13 100644 --- a/documentation/source/testbench_tools.rst +++ b/documentation/source/testbench_tools.rst @@ -5,7 +5,7 @@ Test Bench Tools Logging ======= -Cocotb extends the python logging library. Each dut, monitor, driver, and scoreboard (as well as any other function using the **coroutine** decorator) implements it's own logging object, and each can be set to it's own logging level. Within a dut, each heirarchical object can also have individual logging levels set. +Cocotb extends the python logging library. Each dut, monitor, driver, and scoreboard (as well as any other function using the **coroutine** decorator) implements it's own :class:`logging` object, and each can be set to it's own logging level. Within a dut, each heirarchical object can also have individual logging levels set. When logging hdl objects, beware that **_log** is the preferred way to use logging. This helps minimize the change of name collisions with an hdl log component with the python logging functionality. @@ -33,16 +33,17 @@ And when the logging is actually called class AvalonSTPkts(BusMonitor): ... - @coroutine - def _monitor_recv(self): - ... - self.log.info("Received a packet of %d bytes" % len(pkt)) - @cocotb.coroutine + @coroutine + def _monitor_recv(self): + ... + self.log.info("Received a packet of %d bytes" % len(pkt)) + class Scoreboard(object): ... def add_interface(self): ... self.log.info("Created with reorder_depth %d" % reorder_depth) + class EndianSwapTB(object): ... @cocotb.coroutine @@ -62,10 +63,17 @@ will display as something like Buses ===== -Busses are simply defined as collection of signals. The **Bus** class will automatically bundle any group of signals together that are named similar to dut.. for instance, - dut.stream_in_valid - dut.stream_in_data -have a bus name of ``stream_in``, a seperator of ``_``, and signal names of ``valid`` and ``data``. a list of signal names, or a dictionary mapping attribute names to signal names is also passed into the **Bus** class. Busses can have values driven onto them, be captured (returning a dictionary), or sampled and stored into a similar object. +Busses are simply defined as collection of signals. The :py:class:`Bus` class will automatically bundle any group of signals together that are named similar to dut.. for instance, +.. code-block:: python + + dut.stream_in_valid + dut.stream_in_data + +have a bus name of ``stream_in``, a seperator of ``_``, and signal names of ``valid`` and ``data``. a list of signal names, or a dictionary mapping attribute names to signal names is also passed into the :py:class:`Bus` class. Busses can have values driven onto them, be captured (returning a dictionary), or sampled and stored into a similar object. + +.. code-block:: python + + stream_in_bus = Bus(dut, "stream_in", ["valid", "data"]) # _ is the default seperator Driving Busses From 37586a9af980be65433dc0a65bac4aef51234211 Mon Sep 17 00:00:00 2001 From: David Stanford Date: Wed, 21 Feb 2018 11:14:53 -0600 Subject: [PATCH 1456/2656] add driving example --- documentation/source/testbench_tools.rst | 35 +++++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/documentation/source/testbench_tools.rst b/documentation/source/testbench_tools.rst index 665a2f13..a0d83dc8 100644 --- a/documentation/source/testbench_tools.rst +++ b/documentation/source/testbench_tools.rst @@ -60,10 +60,11 @@ will display as something like 160000000000000.00ns INFO cocotb.endian_swapper_sv.stream_out avalon.py:151 in _monitor_recv Received a packet of 125 bytes -Buses -===== +Busses +====== Busses are simply defined as collection of signals. The :py:class:`Bus` class will automatically bundle any group of signals together that are named similar to dut.. for instance, + .. code-block:: python dut.stream_in_valid @@ -73,13 +74,39 @@ have a bus name of ``stream_in``, a seperator of ``_``, and signal names of ``va .. code-block:: python - stream_in_bus = Bus(dut, "stream_in", ["valid", "data"]) # _ is the default seperator + stream_in_bus = Bus(dut, "stream_in", ["valid", "data"]) # '_' is the default seperator Driving Busses ============== -examples and specific bus implementation bus drivers (amba, avalon, xgmii, and others) exist in the **Driver** class enabling a test to append transactions to perform the serialization of transactions onto a physical interface. +examples and specific bus implementation bus drivers (amba, avalon, xgmii, and others) exist in the :py:class:`Driver` class enabling a test to append transactions to perform the serialization of transactions onto a physical interface. Here's an example using the avalon bus driver in the endian swapper example + +..code-block:: python + +@cocotb.coroutine + +class EndianSwapperTB(object): + + def __init__(self, dut, debug=False): + self.dut = dut + self.stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) + +def run_test(dut, data_in=None, config_coroutine=None, idle_inserter=None, + backpressure_inserter=None): + + cocotb.fork(Clock(dut.clk, 5000).start()) + tb = EndianSwapperTB(dut) + + yield tb.reset() + dut.stream_out_ready <= 1 + + if idle_inserter is not None: + tb.stream_in.set_valid_generator(idle_inserter()) + + # Send in the packets + for transaction in data_in(): + yield tb.stream_in.send(transaction) Monitoring Busses From 382a33c9649e86db38087d8799a0fdea5589a417 Mon Sep 17 00:00:00 2001 From: David Stanford Date: Wed, 21 Feb 2018 14:30:22 -0600 Subject: [PATCH 1457/2656] add final examples --- documentation/source/testbench_tools.rst | 77 +++++++++++++++++++++++- 1 file changed, 74 insertions(+), 3 deletions(-) diff --git a/documentation/source/testbench_tools.rst b/documentation/source/testbench_tools.rst index a0d83dc8..16f9ee2e 100644 --- a/documentation/source/testbench_tools.rst +++ b/documentation/source/testbench_tools.rst @@ -82,7 +82,7 @@ Driving Busses examples and specific bus implementation bus drivers (amba, avalon, xgmii, and others) exist in the :py:class:`Driver` class enabling a test to append transactions to perform the serialization of transactions onto a physical interface. Here's an example using the avalon bus driver in the endian swapper example -..code-block:: python +.. code-block:: python @cocotb.coroutine @@ -112,9 +112,80 @@ def run_test(dut, data_in=None, config_coroutine=None, idle_inserter=None, Monitoring Busses ================= -For our testbenches to actually be useful, we have to monitor some of these busses, and not just drive them. That's where the **Monitor** class comes in, with prebuilt Monitors for ``avalon`` and ``xgmii`` busses. The Monitor class is a base class which you are expected to derive for your particular purpose. You must create a `_monitor_recv()` function which is responsible for determining 1) at what points in simulation to call the `_recv()` function, and 2) what transaction values to pass to be stored in the monitors receiving queue. Monitors are good for both outputs of the dut for verification, and for the inputs of the dut, to drive a test model of the dut to be compared to the actual dut. For this purpose, input monitors will often have a callback function passed that is a model. This model will often generate expected transactions, which are then compared using the **Scoreboard** class. +For our testbenches to actually be useful, we have to monitor some of these busses, and not just drive them. That's where the :py:class:`Monitor` class comes in, with prebuilt Monitors for **avalon** and **xgmii** busses. The Monitor class is a base class which you are expected to derive for your particular purpose. You must create a `_monitor_recv()` function which is responsible for determining 1) at what points in simulation to call the `_recv()` function, and 2) what transaction values to pass to be stored in the monitors receiving queue. Monitors are good for both outputs of the dut for verification, and for the inputs of the dut, to drive a test model of the dut to be compared to the actual dut. For this purpose, input monitors will often have a callback function passed that is a model. This model will often generate expected transactions, which are then compared using the **Scoreboard** class. + +.. code-block:: python + +# ============================================================================== +class BitMonitor(Monitor): + """Observes single 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): + + self.dut = dut + + # 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 ] + + # 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) + Tracking testbench errors ========================= -The **Scoreboard** class is used to compare the actual outputs to expected outputs. Monitors are added to the scoreboard for the actual outputs, and the expected outputs can be either a simple list, or a function that provides a transaction. +The :py:class:`Scoreboard` class is used to compare the actual outputs to expected outputs. Monitors are added to the scoreboard for the actual outputs, and the expected outputs can be either a simple list, or a function that provides a transaction. Here's some code from the **DFF** example, similar to above with the scoreboard added. + +.. code-block:: python + +class DFF_TB(object): + def __init__(self, dut, init_val): + self.dut = dut + + # 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) From bdd45589410d05f7e27aba8d9c45b885d59f0e0c Mon Sep 17 00:00:00 2001 From: David Stanford Date: Wed, 21 Feb 2018 14:37:50 -0600 Subject: [PATCH 1458/2656] spacing for codeblocks --- documentation/source/testbench_tools.rst | 174 +++++++++++------------ 1 file changed, 86 insertions(+), 88 deletions(-) diff --git a/documentation/source/testbench_tools.rst b/documentation/source/testbench_tools.rst index 16f9ee2e..5a793288 100644 --- a/documentation/source/testbench_tools.rst +++ b/documentation/source/testbench_tools.rst @@ -84,30 +84,28 @@ examples and specific bus implementation bus drivers (amba, avalon, xgmii, and o .. code-block:: python -@cocotb.coroutine - -class EndianSwapperTB(object): - - def __init__(self, dut, debug=False): - self.dut = dut - self.stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) - -def run_test(dut, data_in=None, config_coroutine=None, idle_inserter=None, - backpressure_inserter=None): - - cocotb.fork(Clock(dut.clk, 5000).start()) - tb = EndianSwapperTB(dut) - - yield tb.reset() - dut.stream_out_ready <= 1 - - if idle_inserter is not None: - tb.stream_in.set_valid_generator(idle_inserter()) - - # Send in the packets - for transaction in data_in(): - yield tb.stream_in.send(transaction) - + class EndianSwapperTB(object): + + def __init__(self, dut, debug=False): + self.dut = dut + self.stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) + + def run_test(dut, data_in=None, config_coroutine=None, idle_inserter=None, + backpressure_inserter=None): + + cocotb.fork(Clock(dut.clk, 5000).start()) + tb = EndianSwapperTB(dut) + + yield tb.reset() + dut.stream_out_ready <= 1 + + if idle_inserter is not None: + tb.stream_in.set_valid_generator(idle_inserter()) + + # Send in the packets + for transaction in data_in(): + yield tb.stream_in.send(transaction) + Monitoring Busses ================= @@ -116,55 +114,55 @@ For our testbenches to actually be useful, we have to monitor some of these buss .. code-block:: python -# ============================================================================== -class BitMonitor(Monitor): - """Observes single 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) - + # ============================================================================== + class BitMonitor(Monitor): + """Observes single 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: - # 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): - - self.dut = dut - - # 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 ] - - # 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) - + yield random.randint(1,5), random.randint(1,5) + + # ============================================================================== + class DFF_TB(object): + def __init__(self, dut, init_val): + + self.dut = dut + + # 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 ] + + # 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) + Tracking testbench errors ========================= @@ -173,19 +171,19 @@ The :py:class:`Scoreboard` class is used to compare the actual outputs to expect .. code-block:: python -class DFF_TB(object): - def __init__(self, dut, init_val): - self.dut = dut - - # 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) + class DFF_TB(object): + def __init__(self, dut, init_val): + self.dut = dut + + # 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) From 9bd301013ddfb10b00a099b01af8647ce1709023 Mon Sep 17 00:00:00 2001 From: David Stanford Date: Wed, 21 Feb 2018 15:06:38 -0600 Subject: [PATCH 1459/2656] change Makefile back to standard --- documentation/Makefile | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/documentation/Makefile b/documentation/Makefile index 88087ad5..5248dbf6 100644 --- a/documentation/Makefile +++ b/documentation/Makefile @@ -5,9 +5,7 @@ SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = -BUILDDIR = ../../cocotb_docs -PDFBUILDDIR = /tmp -PDF = ../manual.pdf +BUILDDIR = build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) @@ -113,10 +111,9 @@ latex: "(use \`make latexpdf' here to do that automatically)." latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(PDFBUILDDIR)/latex + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(PDFBUILDDIR)/latex all-pdf - cp $(PDFBUILDDIR)/latex/*.pdf $(PDF) + $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: From 2157662d42a73b137b8b3392d7b9e7ac842825ea Mon Sep 17 00:00:00 2001 From: Joseph Glover Date: Wed, 5 Dec 2018 14:50:54 -0600 Subject: [PATCH 1460/2656] Added Monitor doc string and updated lib ref so it shows up. Added private method documentation printing for sphinx. Updated phrasing. --- documentation/source/library_reference.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/documentation/source/library_reference.rst b/documentation/source/library_reference.rst index 49a01f47..86a2ae4e 100644 --- a/documentation/source/library_reference.rst +++ b/documentation/source/library_reference.rst @@ -77,10 +77,16 @@ Python Triggers Testbench Structure =================== +Driver +------ + .. autoclass:: cocotb.drivers.Driver :members: :private-members: +Monitor +------- + .. autoclass:: cocotb.monitors.Monitor :members: :private-members: From 5d46cb93d4b9338b57667f502c7c85c0623ec77e Mon Sep 17 00:00:00 2001 From: Joseph Glover Date: Wed, 5 Dec 2018 15:53:29 -0600 Subject: [PATCH 1461/2656] Updated lib ref rst to included implementations included in cocotb package. Added info message to notify user when strict_type checking is on. Added requested changes, moved the message about strict_type to the test_failure. Touch up of the documentation once more. Added sphinx usage of the napoleon extension which should be available in a standard install of sphinx > 1.3 Added copyright update on build by importing the datetime module and grabbing the current year. Added all the handles found in the handle module plus the SimHandle function to documentation. Fixed a typo, added hooks to be autodoc'd, fixed a py class autodoc reference. --- cocotb/decorators.py | 2 +- cocotb/monitors/__init__.py | 19 ++++--- documentation/source/conf.py | 13 ++++- documentation/source/library_reference.rst | 61 ++++++++++++++++++++-- 4 files changed, 79 insertions(+), 16 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 9d774849..1918d4f5 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -282,7 +282,7 @@ def __str__(self): @public class function(object): - """Decorator class that allows a a function to block + """Decorator class that allows a function to block This allows a function to internally block while externally appear to yield diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index 77385e0d..22ff80a4 100644 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -63,16 +63,15 @@ class Monitor(object): method is added to the cocotb scheduler during the `__init__` phase, so it should not be yielded anywhere. - The primary use of a Monitor is as an interface for a :Scoreboard:. - - :type callback: callable - :param callback: Will be called with each recovered transaction as the - argument. If the callback isn't used, received transactions will be placed - on a queue and the event used to notify any consumers. - - :type event: event - :param event: Object that supports a `set` method that will be called when - a transaction is received through the internal `_recv` method. + The primary use of a Monitor is as an interface for a + :py:class:`cocotb.scoreboard.Scoreboard`. + + Args: + callback (callable): Callback to be called with each recovered transaction + as the argument. If the callback isn't used, received transactions will + be placed on a queue and the event used to notify any consumers. + event (event): Object that supports a `set` method that will be called when + a transaction is received through the internal `_recv` method. """ def __init__(self, callback=None, event=None): diff --git a/documentation/source/conf.py b/documentation/source/conf.py index ee281624..972e3fa6 100644 --- a/documentation/source/conf.py +++ b/documentation/source/conf.py @@ -12,6 +12,7 @@ # serve to show the default. import sys, os +import datetime # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -26,7 +27,15 @@ # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.pngmath', 'sphinx.ext.viewcode'] +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.doctest', + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.pngmath', + 'sphinx.ext.viewcode', + 'sphinx.ext.napoleon', + ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -42,7 +51,7 @@ # General information about the project. project = u'cocotb' -copyright = u'2014, PotentialVentures' +copyright = u'2014-{0}, PotentialVentures'.format(datetime.datetime.now().year) # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/documentation/source/library_reference.rst b/documentation/source/library_reference.rst index 86a2ae4e..97d8695a 100644 --- a/documentation/source/library_reference.rst +++ b/documentation/source/library_reference.rst @@ -26,6 +26,7 @@ Writing and Generating tests .. autoclass:: cocotb.regression.TestFactory :members: +.. autoclass:: cocotb.hook Interacting with the Simulator @@ -77,9 +78,6 @@ Python Triggers Testbench Structure =================== -Driver ------- - .. autoclass:: cocotb.drivers.Driver :members: :private-members: @@ -104,6 +102,63 @@ Utilities .. automodule:: cocotb.utils :members: +Simulation Object Handles +========================= + +.. autofunction:: cocotb.handle.SimHandle + +.. autoclass:: cocotb.handle.SimHandleBase + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.RegionObject + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.HierarchyObject + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.HierarchyArrayObject + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.NonHierarchyObject + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.ConstantObject + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.NonHierarchyIndexableObject + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.NonConstantObject + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.ModifiableObject + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.RealObject + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.EnumObject + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.IntegerObject + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.StringObject + :members: + :show-inheritance: + Implemented Testbench Structures ================================ From 373a8cf53c6d1b63036fd9f4b15a8bddf3d705c0 Mon Sep 17 00:00:00 2001 From: David Stanford Date: Tue, 11 Dec 2018 20:44:47 -0600 Subject: [PATCH 1462/2656] make updates to the changes based on the reviews here: https://github.com/potentialventures/cocotb/pull/601. Thanks eespecially to cmarqu for their feedback. --- documentation/source/coroutines.rst | 8 ++--- documentation/source/endian_swapper.rst | 2 +- documentation/source/hal_cosimulation.rst | 8 ++--- documentation/source/library_reference.rst | 2 +- documentation/source/quickstart.rst | 6 ++-- documentation/source/testbench_tools.rst | 42 +++++++++++----------- documentation/source/triggers.rst | 18 +++++----- 7 files changed, 44 insertions(+), 42 deletions(-) diff --git a/documentation/source/coroutines.rst b/documentation/source/coroutines.rst index f4f41161..269cd309 100644 --- a/documentation/source/coroutines.rst +++ b/documentation/source/coroutines.rst @@ -1,13 +1,13 @@ Coroutines ========== -Testbenches built using Cocotb use coroutines. While the coroutine is executing -the simulation is paused. The coroutine uses the :keyword:`yield` keyword to +Testbenches built using Cocotb use coroutines. While the `coroutine` is executing +the simulation is paused. The `coroutine` uses the :keyword:`yield` keyword to pass control of execution back to the simulator and simulation time can advance again. Typically coroutines :keyword:`yield` a :py:class:`Trigger` object which -indicates to the simulator some event which will cause the coroutine to be woken +indicates to the simulator some event which will cause the `coroutine` to be woken when it occurs. For example: .. code-block:: python @@ -62,7 +62,7 @@ execution should resume if *any* of them fires: yield [Timer(timeout), RisingEdge(dut.ready)] -The trigger that caused execution to resume is passed back to the coroutine, +The trigger that caused execution to resume is passed back to the `coroutine`, allowing them to distinguish which trigger fired: .. code-block:: python diff --git a/documentation/source/endian_swapper.rst b/documentation/source/endian_swapper.rst index 03a12984..dddca5c4 100644 --- a/documentation/source/endian_swapper.rst +++ b/documentation/source/endian_swapper.rst @@ -154,7 +154,7 @@ We want to run different variations of tests but they will all have a very simil raise tb.scoreboard.result -We can see that this test function creates an instance of the testbench, resets the DUT by running the coroutine ``tb.reset()`` and then starts off any optional coroutines passed in using the keyword arguments. We then send in all the packets from ``data_in``, ensure that all the packets have been received by waiting 2 cycles at the end. We read the packet count and compare this with the number of packets. Finally we use the ``tb.scoreboard.result`` to determine the status of the test. If any transactions didn't match the expected output then this member would be an instance of the ``TestFailure`` result. +We can see that this test function creates an instance of the testbench, resets the DUT by running the `coroutine` ``tb.reset()`` and then starts off any optional coroutines passed in using the keyword arguments. We then send in all the packets from ``data_in``, ensure that all the packets have been received by waiting 2 cycles at the end. We read the packet count and compare this with the number of packets. Finally we use the ``tb.scoreboard.result`` to determine the status of the test. If any transactions didn't match the expected output then this member would be an instance of the ``TestFailure`` result. Test permutations diff --git a/documentation/source/hal_cosimulation.rst b/documentation/source/hal_cosimulation.rst index b5208d5a..7b465bb7 100644 --- a/documentation/source/hal_cosimulation.rst +++ b/documentation/source/hal_cosimulation.rst @@ -54,9 +54,9 @@ Cocotb infrastructure There are two decorators provided to enable this flow, which are typically used together to achieve the required functionality. The ``cocotb.external`` -decorator turns a normal function that isn't a coroutine into a blocking -coroutine (by running the function in a separate thread). The -``cocotb.function`` decorator allows a coroutine that consumes simulation time +decorator turns a normal function that isn't a `coroutine` into a blocking +`coroutine` (by running the function in a separate thread). The +``cocotb.function`` decorator allows a `coroutine` that consumes simulation time to be called by a normal thread. The call sequence looks like this: @@ -144,7 +144,7 @@ a read or write. These are then passed to the `IO Module`_: We can then intialise the HAL and call functions, using the ``cocotb.external`` -decorator to turn the normal function into a blocking coroutine that we can +decorator to turn the normal function into a blocking `coroutine` that we can ``yield``: diff --git a/documentation/source/library_reference.rst b/documentation/source/library_reference.rst index a57d4145..d725daf5 100644 --- a/documentation/source/library_reference.rst +++ b/documentation/source/library_reference.rst @@ -44,7 +44,7 @@ Interacting with the Simulator Triggers -------- -Triggers are used to indicate when the scheduler should resume coroutine execution. Typically a coroutine will **yield** a trigger or a list of triggers. +Triggers are used to indicate when the scheduler should resume `coroutine` execution. Typically a `coroutine` will **yield** a trigger or a list of triggers. Simulation Timing ~~~~~~~~~~~~~~~~~ diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index ac21eec5..98f599da 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -178,8 +178,10 @@ We would then create a file called ``test_my_design.py`` containing our tests. Creating a test --------------- -The test is written in Python. Cocotb wraps your top level with the handle **dut** -to be used in all python files referencing your RTL project. Assuming we have a +The test is written in Python. Cocotb wraps your top level with the handle you +pass it. In this documentation, and most of the examples in the project, that +handle is **dut**, but you can pass your own preferred name in instead. The handle +is used in all python files referencing your RTL project. Assuming we have a toplevel port called ``clk`` we could create a test file containing the following: .. code-block:: python diff --git a/documentation/source/testbench_tools.rst b/documentation/source/testbench_tools.rst index 5a793288..3a51aec3 100644 --- a/documentation/source/testbench_tools.rst +++ b/documentation/source/testbench_tools.rst @@ -5,7 +5,7 @@ Test Bench Tools Logging ======= -Cocotb extends the python logging library. Each dut, monitor, driver, and scoreboard (as well as any other function using the **coroutine** decorator) implements it's own :class:`logging` object, and each can be set to it's own logging level. Within a dut, each heirarchical object can also have individual logging levels set. +Cocotb extends the python logging library. Each dut, monitor, driver, and scoreboard (as well as any other function using the `coroutine` decorator) implements it's own :class:`logging` object, and each can be set to it's own logging level. Within a dut, each hierarchical object can also have individual logging levels set. When logging hdl objects, beware that **_log** is the preferred way to use logging. This helps minimize the change of name collisions with an hdl log component with the python logging functionality. @@ -20,7 +20,7 @@ Log printing levels can also be set on a per object basis. self.stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) self.stream_in_recovered = AvalonSTMonitor(dut, "stream_in", dut.clk, callback=self.model) - + # Set verbosity on our various interfaces level = logging.DEBUG if debug else logging.WARNING self.stream_in.log.setLevel(level) @@ -32,24 +32,24 @@ And when the logging is actually called .. code-block:: python class AvalonSTPkts(BusMonitor): - ... - @coroutine - def _monitor_recv(self): - ... + ... + @coroutine + def _monitor_recv(self): + ... self.log.info("Received a packet of %d bytes" % len(pkt)) - class Scoreboard(object): - ... - def add_interface(self): - ... + class Scoreboard(object): + ... + def add_interface(self): + ... self.log.info("Created with reorder_depth %d" % reorder_depth) class EndianSwapTB(object): - ... + ... @cocotb.coroutine - def reset(): + def reset(): self.dut._log.debug("Resetting DUT") - + will display as something like @@ -59,22 +59,22 @@ will display as something like 0.00ns DEBUG cocotb.endian_swapper_sv .._endian_swapper.py:106 in reset Resetting DUT 160000000000000.00ns INFO cocotb.endian_swapper_sv.stream_out avalon.py:151 in _monitor_recv Received a packet of 125 bytes - + Busses ====== -Busses are simply defined as collection of signals. The :py:class:`Bus` class will automatically bundle any group of signals together that are named similar to dut.. for instance, +Busses are simply defined as collection of signals. The :py:class:`Bus` class will automatically bundle any group of signals together that are named similar to dut.. for instance, .. code-block:: python dut.stream_in_valid dut.stream_in_data - -have a bus name of ``stream_in``, a seperator of ``_``, and signal names of ``valid`` and ``data``. a list of signal names, or a dictionary mapping attribute names to signal names is also passed into the :py:class:`Bus` class. Busses can have values driven onto them, be captured (returning a dictionary), or sampled and stored into a similar object. + +have a bus name of ``stream_in``, a separator of ``_``, and signal names of ``valid`` and ``data``. A list of signal names, or a dictionary mapping attribute names to signal names is also passed into the :py:class:`Bus` class. Busses can have values driven onto them, be captured (returning a dictionary), or sampled and stored into a similar object. .. code-block:: python - stream_in_bus = Bus(dut, "stream_in", ["valid", "data"]) # '_' is the default seperator + stream_in_bus = Bus(dut, "stream_in", ["valid", "data"]) # '_' is the default separator Driving Busses @@ -110,7 +110,7 @@ examples and specific bus implementation bus drivers (amba, avalon, xgmii, and o Monitoring Busses ================= -For our testbenches to actually be useful, we have to monitor some of these busses, and not just drive them. That's where the :py:class:`Monitor` class comes in, with prebuilt Monitors for **avalon** and **xgmii** busses. The Monitor class is a base class which you are expected to derive for your particular purpose. You must create a `_monitor_recv()` function which is responsible for determining 1) at what points in simulation to call the `_recv()` function, and 2) what transaction values to pass to be stored in the monitors receiving queue. Monitors are good for both outputs of the dut for verification, and for the inputs of the dut, to drive a test model of the dut to be compared to the actual dut. For this purpose, input monitors will often have a callback function passed that is a model. This model will often generate expected transactions, which are then compared using the **Scoreboard** class. +For our testbenches to actually be useful, we have to monitor some of these busses, and not just drive them. That's where the :py:class:`Monitor` class comes in, with prebuilt Monitors for **avalon** and **xgmii** busses. The Monitor class is a base class which you are expected to derive for your particular purpose. You must create a `_monitor_recv()` function which is responsible for determining 1) at what points in simulation to call the `_recv()` function, and 2) what transaction values to pass to be stored in the monitors receiving queue. Monitors are good for both outputs of the dut for verification, and for the inputs of the dut, to drive a test model of the dut to be compared to the actual dut. For this purpose, input monitors will often have a callback function passed that is a model. This model will often generate expected transactions, which are then compared using the :py:class:`Scoreboard` class. .. code-block:: python @@ -162,7 +162,7 @@ For our testbenches to actually be useful, we have to monitor some of these buss # simulation, that is, after stop() has been called. if not self.stopped: self.expected_output.append(transaction) - + Tracking testbench errors ========================= @@ -170,7 +170,7 @@ Tracking testbench errors The :py:class:`Scoreboard` class is used to compare the actual outputs to expected outputs. Monitors are added to the scoreboard for the actual outputs, and the expected outputs can be either a simple list, or a function that provides a transaction. Here's some code from the **DFF** example, similar to above with the scoreboard added. .. code-block:: python - + class DFF_TB(object): def __init__(self, dut, init_val): self.dut = dut diff --git a/documentation/source/triggers.rst b/documentation/source/triggers.rst index c93883dc..815bcd36 100644 --- a/documentation/source/triggers.rst +++ b/documentation/source/triggers.rst @@ -1,7 +1,7 @@ Triggers ======== -Triggers are used to indicate when the cocotb scheduler should resume coroutine execution. Typically a coroutine will **yield** a trigger or a list of triggers, while it's waiting for them to complete. +Triggers are used to indicate when the cocotb scheduler should resume `coroutine` execution. Typically a `coroutine` will **yield** a trigger or a list of triggers, while it's waiting for them to complete. Simulation Timing ----------------- @@ -9,7 +9,7 @@ Simulation Timing Timer(time) ^^^^^^^^^^^ -Registers a timed callback with the simulator to continue execution of the coroutine after a specified simulation time period has elapsed. +Registers a timed callback with the simulator to continue execution of the `coroutine` after a specified simulation time period has elapsed. .. todo:: What is the behaviour if time=0? @@ -18,7 +18,7 @@ Registers a timed callback with the simulator to continue execution of the corou ReadOnly() ^^^^^^^^^^ -Registers a callback which will continue execution of the coroutine when the current simulation timestep moves to the ReadOnly phase of the rtl simulator. The ReadOnly phase is entered when the current timestep no longer has any further delta steps. This should be point where all the signal values are stable as there are no more rtl events scheduled for the timestep. The simulator should not allow scheduling of more events in this timestep. Useful for monitors which need to wait for all processes to execute (both RTL and cocotb) to ensure sampled signal values are final. +Registers a callback which will continue execution of the `coroutine` when the current simulation timestep moves to the ReadOnly phase of the rtl simulator. The ReadOnly phase is entered when the current timestep no longer has any further delta steps. This should be point where all the signal values are stable as there are no more rtl events scheduled for the timestep. The simulator should not allow scheduling of more events in this timestep. Useful for monitors which need to wait for all processes to execute (both RTL and cocotb) to ensure sampled signal values are final. @@ -28,7 +28,7 @@ Signal related Edge(signal) ^^^^^^^^^^^^ -Registers a callback that will continue execution of the coroutine on any value change of a signal. +Registers a callback that will continue execution of the `coroutine` on any value change of a signal. .. todo:: Behaviour for vectors @@ -37,19 +37,19 @@ Registers a callback that will continue execution of the coroutine on any value RisingEdge(signal) ^^^^^^^^^^^^^^^^^^ -Registers a callback that will continue execution of the coroutine on a transition from 0 to 1 of signal. +Registers a callback that will continue execution of the `coroutine` on a transition from 0 to 1 of signal. FallingEdge(signal) ^^^^^^^^^^^^^^^^^^ -Registers a callback that will continue execution of the coroutine on a transition from 1 to 0 of signal. +Registers a callback that will continue execution of the `coroutine` on a transition from 1 to 0 of signal. ClockCycles(signal, num_cycles) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Registers a callback that will continue execution of the coroutine when num_cycles transistions from 0 to 1 have occured. +Registers a callback that will continue execution of the `coroutine` when num_cycles transistions from 0 to 1 have occured. Python Triggers @@ -58,13 +58,13 @@ Python Triggers Event() ^^^^^^^ -Can be used to synchronise between coroutines. yielding Event.wait() will block the coroutine until Event.set() is called somewhere else. +Can be used to synchronise between coroutines. yielding Event.wait() will block the `coroutine` until Event.set() is called somewhere else. Join(coroutine) ^^^^^^^^^^^^^^^ -Will block the coroutine until another coroutine has completed. +Will block the `coroutine` until another `coroutine` has completed. From ebe86e241c18f2e55879100c340cd9230d694915 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 2 Aug 2018 23:34:43 -0700 Subject: [PATCH 1463/2656] Removed unused state from CoroutineComplete Nothing used this callback, and finished was always set after the exception was thrown anyway --- cocotb/decorators.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index e7e76da8..247bcacd 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -67,9 +67,8 @@ class CoroutineComplete(Exception): exception that the Scheduler catches and the callbacks are attached here. """ - def __init__(self, text="", callback=None): + def __init__(self, text=""): Exception.__init__(self, text) - self.callback = callback class RunningCoroutine(object): @@ -104,7 +103,7 @@ def __init__(self, inst, parent): if not hasattr(self._coro, "send"): self.log.error("%s isn't a valid coroutine! Did you use the yield " "keyword?" % self.funcname) - raise CoroutineComplete(callback=self._finished_cb) + raise CoroutineComplete() def __iter__(self): return self @@ -126,15 +125,15 @@ def send(self, value): except ExternalException as e: self.retval = e self._finished = True - raise CoroutineComplete(callback=self._finished_cb) + raise CoroutineComplete() except ReturnValue as e: self.retval = e.retval self._finished = True - raise CoroutineComplete(callback=self._finished_cb) + raise CoroutineComplete() except StopIteration as e: self._finished = True self.retval = getattr(e, 'value', None) # for python >=3.3 - raise CoroutineComplete(callback=self._finished_cb) + raise CoroutineComplete() except Exception as e: self._finished = True raise raise_error(self, "Send raised exception:") @@ -150,13 +149,6 @@ def kill(self): self.log.debug("kill() called on coroutine") cocotb.scheduler.unschedule(self) - def _finished_cb(self): - """Called when the coroutine completes. - Allows us to mark the coroutine as finished so that boolean testing - works. - Also call any callbacks, usually the result of coroutine.join()""" - self._finished = True - def join(self): """Return a trigger that will fire when the wrapped coroutine exits""" if self._finished: From 089ce7c2e3e19ec8b98558d6208e29d6dcd3052a Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 2 Aug 2018 23:36:46 -0700 Subject: [PATCH 1464/2656] Remove ExternalException, which seems like a half-baked attempt to handle exceptions --- cocotb/decorators.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 247bcacd..80b0896e 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -113,19 +113,12 @@ def __str__(self): def send(self, value): try: - if isinstance(value, ExternalException): - self.log.debug("Injecting ExternalException(%s)" % (repr(value))) - return self._coro.throw(value.exception) self._started = True return self._coro.send(value) except TestComplete as e: if isinstance(e, TestFailure): self.log.warning(str(e)) raise - except ExternalException as e: - self.retval = e - self._finished = True - raise CoroutineComplete() except ReturnValue as e: self.retval = e.retval self._finished = True @@ -205,9 +198,6 @@ def send(self, value): self.start_sim_time = get_sim_time('ns') self.started = True try: - if isinstance(value, ExternalException): - self.log.debug("Injecting ExternalException(%s)" % (repr(value))) - return self._coro.throw(value.exception) self.log.debug("Sending trigger %s" % (str(value))) return self._coro.send(value) except TestComplete as e: From b514cad8b083e07acfd68340f53c5d57f2352a08 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 2 Aug 2018 23:40:39 -0700 Subject: [PATCH 1465/2656] Remove double-catch of TestFailure - the top level coroutine should log it --- cocotb/decorators.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 80b0896e..29e0d669 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -115,10 +115,6 @@ def send(self, value): try: self._started = True return self._coro.send(value) - except TestComplete as e: - if isinstance(e, TestFailure): - self.log.warning(str(e)) - raise except ReturnValue as e: self.retval = e.retval self._finished = True @@ -127,7 +123,7 @@ def send(self, value): self._finished = True self.retval = getattr(e, 'value', None) # for python >=3.3 raise CoroutineComplete() - except Exception as e: + except BaseException as e: self._finished = True raise raise_error(self, "Send raised exception:") From f44356adfbbb8bafb7ecc694f1c001f3cc7762ee Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 2 Aug 2018 23:49:49 -0700 Subject: [PATCH 1466/2656] Don't misuse .send to advance the scheduler, especially since `throw` does not anyway --- cocotb/decorators.py | 7 +++++-- cocotb/scheduler.py | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 29e0d669..925c188f 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -111,7 +111,7 @@ def __iter__(self): def __str__(self): return str(self.__name__) - def send(self, value): + def _advance(self, value): try: self._started = True return self._coro.send(value) @@ -127,6 +127,9 @@ def send(self, value): self._finished = True raise raise_error(self, "Send raised exception:") + def send(self, value): + return self._coro.send(value) + def throw(self, exc): return self._coro.throw(exc) @@ -185,7 +188,7 @@ def __init__(self, inst, parent): self.handler = RunningTest.ErrorLogHandler(self._handle_error_message) cocotb.log.addHandler(self.handler) - def send(self, value): + def _advance(self, value): if not self.started: self.error_messages = [] self.log.info("Starting test: \"%s\"\nDescription: %s" % diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index cf7465f9..c16ae49a 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -594,7 +594,7 @@ def schedule(self, coroutine, trigger=None): coroutine.log.debug("Scheduling with %s" % str(trigger)) try: - result = coroutine.send(sendval) + result = coroutine._advance(sendval) if _debug: self.log.debug("Coroutine %s yielded %s (mode %d)" % (coroutine.__name__, str(result), self._mode)) From 4f48ce40208d0d72b1195bb9179486ee40bceda8 Mon Sep 17 00:00:00 2001 From: Joseph Glover Date: Wed, 12 Dec 2018 13:01:17 -0600 Subject: [PATCH 1467/2656] Updated documentation for utils and library reference. --- cocotb/utils.py | 75 ++++++++---- documentation/source/library_reference.rst | 132 +++++++++++---------- 2 files changed, 124 insertions(+), 83 deletions(-) diff --git a/cocotb/utils.py b/cocotb/utils.py index 1c19dace..3a0284ec 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -55,9 +55,10 @@ def get_python_integer_types(): def get_sim_time(units=None): """Retrieves the simulation time from the simulator - Kwargs: - units (str): String specifying the units of the result. (None,'fs','ps','ns','us','ms','sec') - None will return the raw simulation time. + Args: + units (str, optional): String specifying the units of the result. + (None,'fs','ps','ns','us','ms','sec') None will return the raw + simulation time. Returns: The simulation time in the specified units @@ -72,11 +73,13 @@ def get_sim_time(units=None): return result def get_time_from_sim_steps(steps, units): - """Calculates simulation time in the specified units from the steps based on the simulator precision. + """Calculates simulation time in the specified units from the steps based + on the simulator precision. Args: steps (int): Number of simulation steps - units (str): String specifying the units of the result. ('fs','ps','ns','us','ms','sec') + units (str): String specifying the units of the result. + ('fs','ps','ns','us','ms','sec') Returns: The simulation time in the specified units @@ -90,10 +93,9 @@ def get_sim_steps(time, units=None): Args: time (int/float): The value to convert to simulation time steps. - - Kwargs: - units (str): String specifying the units of the result. (None,'fs','ps','ns','us','ms','sec') - None means time is already in simulation time steps. + units (str, optional): String specifying the units of the result. + (None,'fs','ps','ns','us','ms','sec') None means time is already in + simulation time steps. Returns: The number of simulation time steps @@ -105,7 +107,9 @@ def get_sim_steps(time, units=None): err = int(result) - math.ceil(result) if err: - raise ValueError("Unable to accurately represent {0}({1}) with the simulator precision of 1e{2}".format(time,units,_LOG_SIM_PRECISION)) + raise ValueError("Unable to accurately represent {0}({1}) with the " + "simulator precision of 1e{2}".format( + time,units,_LOG_SIM_PRECISION)) return int(result) @@ -113,7 +117,8 @@ def _get_log_time_scale(units): """Retrieves the log10() of the scale factor for a given time unit Args: - units (str): String specifying the units. ('fs','ps','ns','us','ms','sec') + units (str): String specifying the units. + ('fs','ps','ns','us','ms','sec') Returns: The the log10() of the scale factor for the time unit @@ -138,11 +143,9 @@ def _get_log_time_scale(units): def pack(ctypes_obj): """Convert a ctypes structure into a python string - Args: ctypes_obj (ctypes.Structure): ctypes structure to convert to a string - Returns: New python string containing the bytes from memory holding ctypes_obj """ @@ -155,14 +158,13 @@ def unpack(ctypes_obj, string, bytes=None): Args: ctypes_obj (ctypes.Structure): ctypes structure to pack into - string (str): String to copy over the ctypes_obj memory space - - Kwargs: - bytes: Number of bytes to copy + bytes (int, optional): Number of bytes to copy. Defaults to None. Raises: - ValueError, MemoryError + ValueError: If length of ``string`` and size of ``ctypes_obj`` + are not equal. + MemoryError: If ``bytes`` is longer than size of ``ctypes_obj``. If the length of the string is not the correct size for the memory footprint of the ctypes structure then the bytes keyword argument must @@ -196,7 +198,24 @@ def _sane_color(x): def hexdump(x): - """Hexdump a buffer""" + """Hexdump a buffer + + Args: + x: Object that supports conversion via the ``str`` built-in. + + Returns: + A string containing the hexdump + + Example: + .. code-block:: python + + print(hexdump('this somewhat long string')) + + .. code-block:: + + 0000 74 68 69 73 20 73 6F 6D 65 77 68 61 74 20 6C 6F this somewhat lo + 0010 6E 67 20 73 74 72 69 6E 67 ng string + """ # adapted from scapy.utils.hexdump rs = "" x = str(x) @@ -218,7 +237,23 @@ def hexdump(x): def hexdiffs(x, y): - """Return a diff string showing differences between 2 binary strings""" + """Return a diff string showing differences between 2 binary strings + + Args: + x: Object that supports conversion via the ``str`` built-in + y: Object that supports conversion via the ``str`` built-in + + Example: + .. code-block:: python + + print(hexdiffs('this short thing', 'this also short')) + + .. code-block:: + + 0000 746869732073686F 7274207468696E67 this short thing + 0000 7468697320616C73 6F 2073686F7274 this also short + + """ # adapted from scapy.utils.hexdiff def sane(x): diff --git a/documentation/source/library_reference.rst b/documentation/source/library_reference.rst index 97d8695a..e828083b 100644 --- a/documentation/source/library_reference.rst +++ b/documentation/source/library_reference.rst @@ -78,6 +78,9 @@ Python Triggers Testbench Structure =================== +Driver +------ + .. autoclass:: cocotb.drivers.Driver :members: :private-members: @@ -92,72 +95,12 @@ Monitor .. autoclass:: cocotb.monitors.BusMonitor :members: -.. autoclass:: cocotb.scoreboard.Scoreboard - :members: - - -Utilities -========= - -.. automodule:: cocotb.utils - :members: - -Simulation Object Handles -========================= - -.. autofunction:: cocotb.handle.SimHandle - -.. autoclass:: cocotb.handle.SimHandleBase - :members: - :show-inheritance: - -.. autoclass:: cocotb.handle.RegionObject - :members: - :show-inheritance: - -.. autoclass:: cocotb.handle.HierarchyObject - :members: - :show-inheritance: - -.. autoclass:: cocotb.handle.HierarchyArrayObject - :members: - :show-inheritance: - -.. autoclass:: cocotb.handle.NonHierarchyObject - :members: - :show-inheritance: - -.. autoclass:: cocotb.handle.ConstantObject - :members: - :show-inheritance: - -.. autoclass:: cocotb.handle.NonHierarchyIndexableObject - :members: - :show-inheritance: - -.. autoclass:: cocotb.handle.NonConstantObject - :members: - :show-inheritance: - -.. autoclass:: cocotb.handle.ModifiableObject - :members: - :show-inheritance: - -.. autoclass:: cocotb.handle.RealObject - :members: - :show-inheritance: - -.. autoclass:: cocotb.handle.EnumObject - :members: - :show-inheritance: +Scoreboard +---------- -.. autoclass:: cocotb.handle.IntegerObject +.. autoclass:: cocotb.scoreboard.Scoreboard :members: - :show-inheritance: -.. autoclass:: cocotb.handle.StringObject - :members: - :show-inheritance: Implemented Testbench Structures ================================ @@ -230,3 +173,66 @@ XGMII .. autoclass:: cocotb.monitors.xgmii.XGMII :members: +Utilities +========= + +.. automodule:: cocotb.utils + :members: + +Simulation Object Handles +========================= + +.. autofunction:: cocotb.handle.SimHandle + +.. autoclass:: cocotb.handle.SimHandleBase + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.RegionObject + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.HierarchyObject + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.HierarchyArrayObject + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.NonHierarchyObject + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.ConstantObject + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.NonHierarchyIndexableObject + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.NonConstantObject + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.ModifiableObject + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.RealObject + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.EnumObject + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.IntegerObject + :members: + :show-inheritance: + +.. autoclass:: cocotb.handle.StringObject + :members: + :show-inheritance: + From 35816a54704bd992f8842a3c394822ac1415c5ad Mon Sep 17 00:00:00 2001 From: Joseph Glover Date: Wed, 12 Dec 2018 14:59:21 -0600 Subject: [PATCH 1468/2656] Updated clock docs. --- cocotb/clock.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/cocotb/clock.py b/cocotb/clock.py index 4d743527..99f7c5d7 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -44,8 +44,23 @@ def __init__(self, signal): class Clock(BaseClock): - """ - simple 50:50 duty cycle clock + """Simple 50:50 duty cycle clock driver + + Instances of this class should call its ``start`` method and fork the + result. This will create a clocking thread that drives the signal at the + desired period/frequency. + + Example: + .. code-block:: python + + c = Clock(dut.clk, 10, 'ns') + cocotb.fork(c.start()) + + Args: + signal (pin): The clock pin/signal to be driven. + period (int): The clock period. Should be an even number. + units (str, optional): One of (None,'fs','ps','ns','us','ms','sec'). + Defaults to ``None``. """ def __init__(self, signal, period, units=None): BaseClock.__init__(self, signal) @@ -59,6 +74,12 @@ def __init__(self, signal, period, units=None): @cocotb.coroutine def start(self, cycles=0): + """Clocking coroutine. Start driving your clock by forking a call to + this. + + Args: + cycles (int, optional): Not Implemented. Defaults to 0. + """ t = Timer(self.half_period) while True: self.signal <= 1 From ea3e6e7b645acbe503b111abdbacf397b08a3fb9 Mon Sep 17 00:00:00 2001 From: Joseph Glover Date: Wed, 12 Dec 2018 15:01:04 -0600 Subject: [PATCH 1469/2656] Added clock docs to lib ref. --- documentation/source/library_reference.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/documentation/source/library_reference.rst b/documentation/source/library_reference.rst index e828083b..74a177de 100644 --- a/documentation/source/library_reference.rst +++ b/documentation/source/library_reference.rst @@ -101,6 +101,11 @@ Scoreboard .. autoclass:: cocotb.scoreboard.Scoreboard :members: +Clock +----- + +.. autoclass:: cocotb.clock.Clock + :members: Implemented Testbench Structures ================================ From a20ded781eca92a60865c6d2cbc61359be680caa Mon Sep 17 00:00:00 2001 From: Joseph Glover Date: Wed, 12 Dec 2018 15:33:06 -0600 Subject: [PATCH 1470/2656] Updated Clock to cycle given number of times. --- cocotb/clock.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/cocotb/clock.py b/cocotb/clock.py index 99f7c5d7..dbf92c48 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -78,14 +78,23 @@ def start(self, cycles=0): this. Args: - cycles (int, optional): Not Implemented. Defaults to 0. + cycles (int, optional): Cycle clock ``cycles`` number of times. + Defaults to 0, meaning will cycle forever. """ t = Timer(self.half_period) - while True: - self.signal <= 1 - yield t - self.signal <= 0 - yield t + if cycles > 0: + while cycles: + self.signal <= 1 + yield t + self.signal <= 0 + yield t + cycles -= 1 + else: + while True: + self.signal <= 1 + yield t + self.signal <= 0 + yield t def __str__(self): return self.__class__.__name__ + "(%3.1f MHz)" % self.frequency From 451d6a904e191b1dbecb9ba2228163810b168051 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Wed, 12 Dec 2018 14:26:59 +0000 Subject: [PATCH 1471/2656] Make number of bits in BinaryValue accessible Up to now, the number of bits in a BinaryValue can be set, but not queried any more. Add this functionality. This patch changes the naming from |bits| to |n_bits| to make it more obvious that the variable/property doesn't hold a list of bits, but actually a number. To keep backwards-compat with the existing constructor (people using named arguments when constructing a BinaryValue object) I've retained the old name and marked it as deprecated. Tests are added based on the code by @forensicgarlic in #591. This replaces #591 and build on the code by Oliver A. Gubler (@oligu). --- cocotb/binary.py | 119 ++++++++++++-------- tests/test_cases/test_cocotb/test_cocotb.py | 46 +++++++- 2 files changed, 115 insertions(+), 50 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 86c07ded..1995d67f 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -32,6 +32,7 @@ import os import random +import warnings resolve_x_to = os.getenv('COCOTB_RESOLVE_X', "VALUE_ERROR") @@ -96,22 +97,37 @@ class BinaryValue(object): _resolve_to_error = "xXzZuUwW" # Resolve to a ValueError() since these usually mean something is wrong _permitted_chars = _resolve_to_0 +_resolve_to_1 + _resolve_to_error + "01" # noqa - def __init__(self, value=None, bits=None, bigEndian=True, - binaryRepresentation=BinaryRepresentation.UNSIGNED): + def __init__(self, value=None, n_bits=None, bigEndian=True, + binaryRepresentation=BinaryRepresentation.UNSIGNED, + bits=None): """ Kwagrs: Value (string or int or long): value to assign to the bus - bits (int): Number of bits to use for the underlying binary - representation + n_bits (int): Number of bits to use for the underlying binary + representation bigEndian (bool): Interpret the binary as big-endian when converting to/from a string buffer. + + bits (int): Deprecated: Compatibility wrapper for n_bits """ self._str = "" - self._bits = bits self.big_endian = bigEndian self.binaryRepresentation = binaryRepresentation + + # bits is the deprecated name for n_bits, allow its use for + # backward-compat reasons. + if (bits is not None and n_bits is not None): + raise TypeError("You cannot use n_bits and bits at the same time.") + if bits is not None: + warnings.warn( + "The bits argument to BinaryValue has been renamed to n_bits", + DeprecationWarning, stacklevel=2) + n_bits = bits + + self._n_bits = n_bits + self._convert_to = { BinaryRepresentation.UNSIGNED : self._convert_to_unsigned , BinaryRepresentation.SIGNED_MAGNITUDE : self._convert_to_signed_mag , @@ -202,62 +218,62 @@ def _invert(self, x): return inverted def _adjust_unsigned(self, x): - if self._bits is None: + if self._n_bits is None: return x l = len(x) - if l <= self._bits: + if l <= self._n_bits: if self.big_endian: - rv = x + '0' * (self._bits - l) + rv = x + '0' * (self._n_bits - l) else: - rv = '0' * (self._bits - l) + x - elif l > self._bits: + rv = '0' * (self._n_bits - l) + x + elif l > self._n_bits: print("WARNING truncating value to match requested number of bits " - "(%d -> %d)" % (l, self._bits)) + "(%d -> %d)" % (l, self._n_bits)) if self.big_endian: - rv = x[l - self._bits:] + rv = x[l - self._n_bits:] else: - rv = x[:l - self._bits] + rv = x[:l - self._n_bits] return rv def _adjust_signed_mag(self, x): """Pad/truncate the bit string to the correct length""" - if self._bits is None: + if self._n_bits is None: return x l = len(x) - if l <= self._bits: + if l <= self._n_bits: if self.big_endian: - rv = x[:-1] + '0' * (self._bits - 1 - l) + rv = x[:-1] + '0' * (self._n_bits - 1 - l) rv = rv + x[-1] else: - rv = '0' * (self._bits - 1 - l) + x[1:] + rv = '0' * (self._n_bits - 1 - l) + x[1:] rv = x[0] + rv - elif l > self._bits: + elif l > self._n_bits: print("WARNING truncating value to match requested number of bits " - "(%d -> %d)" % (l, self._bits)) + "(%d -> %d)" % (l, self._n_bits)) if self.big_endian: - rv = x[l - self._bits:] + rv = x[l - self._n_bits:] else: - rv = x[:-(l - self._bits)] + rv = x[:-(l - self._n_bits)] else: rv = x return rv def _adjust_twos_comp(self, x): - if self._bits is None: + if self._n_bits is None: return x l = len(x) - if l <= self._bits: + if l <= self._n_bits: if self.big_endian: - rv = x + x[-1] * (self._bits - l) + rv = x + x[-1] * (self._n_bits - l) else: - rv = x[0] * (self._bits - l) + x - elif l > self._bits: + rv = x[0] * (self._n_bits - l) + x + elif l > self._n_bits: print("WARNING truncating value to match requested number of bits " - "(%d -> %d)" % (l, self._bits)) + "(%d -> %d)" % (l, self._n_bits)) if self.big_endian: - rv = x[l - self._bits:] + rv = x[l - self._n_bits:] else: - rv = x[:-(l - self._bits)] + rv = x[:-(l - self._n_bits)] else: rv = x return rv @@ -325,18 +341,18 @@ def set_buff(self, buff): def _adjust(self): """Pad/truncate the bit string to the correct length""" - if self._bits is None: + if self._n_bits is None: return l = len(self._str) - if l < self._bits: + if l < self._n_bits: if self.big_endian: - self._str = self._str + "0" * (self._bits - l) + self._str = self._str + "0" * (self._n_bits - l) else: - self._str = "0" * (self._bits - l) + self._str - elif l > self._bits: + self._str = "0" * (self._n_bits - l) + self._str + elif l > self._n_bits: print("WARNING truncating value to match requested number of bits " - " (%d)" % l) - self._str = self._str[l - self._bits:] + "(%d -> %d)" % (l, self._n_bits)) + self._str = self._str[l - self._n_bits:] buff = property(get_buff, set_buff, None, "Access to the value as a buffer") @@ -357,6 +373,13 @@ def set_binstr(self, string): binstr = property(get_binstr, set_binstr, None, "Access to the binary string") + def _get_n_bits(self): + """The number of bits of the binary value""" + return self._n_bits + + n_bits = property(_get_n_bits, None, None, + "Access to the number of bits of the binary value") + def hex(self): try: return hex(self.get_value()) @@ -482,7 +505,7 @@ def __getitem__(self, key): if first < 0 or second < 0: raise IndexError('BinaryValue does not support negative ' 'indices') - if second > self._bits - 1: + if second > self._n_bits - 1: raise IndexError('High index greater than number of bits.') if first > second: raise IndexError('Big Endian indices must be specified ' @@ -492,22 +515,22 @@ def __getitem__(self, key): if first < 0 or second < 0: raise IndexError('BinaryValue does not support negative ' 'indices') - if first > self._bits - 1: + if first > self._n_bits - 1: raise IndexError('High index greater than number of bits.') if second > first: raise IndexError('Litte Endian indices must be specified ' 'high to low') - high = self._bits - second - low = self._bits - 1 - first + high = self._n_bits - second + low = self._n_bits - 1 - first _binstr = self.binstr[low:high] else: index = key - if index > self._bits - 1: + if index > self._n_bits - 1: raise IndexError('Index greater than number of bits.') if self.big_endian: _binstr = self.binstr[index] else: - _binstr = self.binstr[self._bits-1-index] + _binstr = self.binstr[self._n_bits-1-index] rv = BinaryValue(bits=len(_binstr), bigEndian=self.big_endian, binaryRepresentation=self.binaryRepresentation) rv.set_binstr(_binstr) @@ -539,7 +562,7 @@ def __setitem__(self, key, val): if first < 0 or second < 0: raise IndexError('BinaryValue does not support negative ' 'indices') - if second > self._bits - 1: + if second > self._n_bits - 1: raise IndexError('High index greater than number of bits.') if first > second: raise IndexError('Big Endian indices must be specified ' @@ -554,13 +577,13 @@ def __setitem__(self, key, val): if first < 0 or second < 0: raise IndexError('BinaryValue does not support negative ' 'indices') - if first > self._bits - 1: + if first > self._n_bits - 1: raise IndexError('High index greater than number of bits.') if second > first: raise IndexError('Litte Endian indices must be specified ' 'high to low') - high = self._bits - second - low = self._bits - 1 - first + high = self._n_bits - second + low = self._n_bits - 1 - first if len(val) > (high - low): raise ValueError('String length must be equal to slice ' 'length') @@ -572,12 +595,12 @@ def __setitem__(self, key, val): raise ValueError('String length must be equal to slice ' 'length') index = key - if index > self._bits - 1: + if index > self._n_bits - 1: raise IndexError('Index greater than number of bits.') if self.big_endian: self.binstr = self.binstr[:index] + val + self.binstr[index + 1:] else: - self.binstr = self.binstr[0:self._bits-index-1] + val + self.binstr[self._bits-index:self._bits] + self.binstr = self.binstr[0:self._n_bits-index-1] + val + self.binstr[self._n_bits-index:self._n_bits] if __name__ == "__main__": import doctest diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 61f1858e..9e602e57 100755 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -29,6 +29,7 @@ import logging import sys import textwrap +import warnings """ A set of tests that demonstrate cocotb functionality @@ -633,7 +634,12 @@ def test_binary_value(dut): values in a style familiar to rtl coders. """ - vec = BinaryValue(value=0, bits=16) + vec = BinaryValue(value=0, n_bits=16) + + dut._log.info("Checking read access to the n_bits property") + if vec.n_bits != 16: + raise TestFailure("n_bits is not set correctly - expected %d, got %d" % (16, vec.n_bits)) + dut._log.info("Checking default endianness is Big Endian.") if not vec.big_endian: raise TestFailure("The default endianness is Little Endian - was expecting Big Endian.") @@ -644,7 +650,7 @@ def test_binary_value(dut): vec = BinaryValue(value=0, bits=16, bigEndian=False) if vec.big_endian: raise TestFailure("Our BinaryValue object is reporting it is Big Endian - was expecting Little Endian.") - for x in range(vec._bits): + for x in range(vec.n_bits): vec[x] = '1' dut._log.info("Trying vec[%s] = 1" % x) expected_value = 2**(x+1) - 1 @@ -670,6 +676,42 @@ def test_binary_value(dut): yield Timer(100) # Make it do something with time +@cocotb.test() +def test_binary_value_compat(dut): + """ + Test backwards-compatibility wrappers for BinaryValue + """ + + dut._log.info("Checking the renaming of bits -> n_bits") + vec = BinaryValue(value=0, bits=16) + if vec.n_bits != 16: + raise TestFailure("n_bits is not set correctly - expected %d, got %d" % (16, vec.n_bits)) + + vec = BinaryValue(0, 16) + if vec.n_bits != 16: + raise TestFailure("n_bits is not set correctly - expected %d, got %d" % (16, vec.n_bits)) + + try: + vec = BinaryValue(value=0, bits=16, n_bits=17) + except TypeError: + pass + else: + raise TestFailure("Expected TypeError when using bits and n_bits at the same time.") + + # Test for the DeprecationWarning when using |bits| + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("error") + + try: + vec = BinaryValue(value=0, bits=16) + except DeprecationWarning: + pass + else: + TestFailure("Expected DeprecationWarning when using bits instead of n_bits.") + + yield Timer(100) # Make it do something with time + + if sys.version_info[:2] >= (3, 3): # this would be a syntax error in older python, so we do the whole # thing inside exec From 412f8e001e8fd0c905e3cb818abde3351c8d1184 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Wed, 12 Dec 2018 14:10:05 +0000 Subject: [PATCH 1472/2656] Fix spelling typos in binary.py --- cocotb/binary.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 1995d67f..35bab558 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -71,7 +71,7 @@ class BinaryRepresentation(): class BinaryValue(object): - """Represenatation of values in binary format. + """Representation of values in binary format. The underlying value can be set or accessed using three aliasing attributes @@ -102,7 +102,7 @@ def __init__(self, value=None, n_bits=None, bigEndian=True, bits=None): """ Kwagrs: - Value (string or int or long): value to assign to the bus + value (string or int or long): value to assign to the bus n_bits (int): Number of bits to use for the underlying binary representation @@ -279,11 +279,11 @@ def _adjust_twos_comp(self, x): return rv def get_value(self): - """value is an integer representaion of the underlying vector""" + """value is an integer representation of the underlying vector""" return self._convert_from[self.binaryRepresentation](self._str) def get_value_signed(self): - """value is an signed integer representaion of the underlying vector""" + """value is an signed integer representation of the underlying vector""" ival = int(resolve(self._str), 2) bits = len(self._str) signbit = (1 << (bits - 1)) From 873d7aee62654c804a3846c3827ce7b165f66f6e Mon Sep 17 00:00:00 2001 From: Joseph Glover Date: Thu, 13 Dec 2018 10:47:22 -0600 Subject: [PATCH 1473/2656] Added sphinx_build check for simulator import. The simulator import is unnecessary for the module as far as I can tell, but I'll leave it for posterity. --- cocotb/clock.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cocotb/clock.py b/cocotb/clock.py index dbf92c48..9c5791ea 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -28,7 +28,11 @@ """ A clock class """ -import simulator +import os +if "SPHINX_BUILD" in os.environ: + simulator = None +else: + import simulator import cocotb from cocotb.log import SimLog from cocotb.triggers import Timer, RisingEdge From 771ace1dba2ff867c89153af3b3f231b6063e7db Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Fri, 3 Aug 2018 00:20:42 -0700 Subject: [PATCH 1474/2656] Propagate exceptions from nested coroutines --- cocotb/decorators.py | 46 ++++++++++++----- cocotb/outcomes.py | 57 +++++++++++++++++++++ cocotb/scheduler.py | 33 +++++++----- cocotb/triggers.py | 10 +++- tests/test_cases/test_cocotb/test_cocotb.py | 15 ++++++ 5 files changed, 133 insertions(+), 28 deletions(-) create mode 100644 cocotb/outcomes.py diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 925c188f..56061085 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -41,6 +41,7 @@ from cocotb.result import (TestComplete, TestError, TestFailure, TestSuccess, ReturnValue, raise_error, ExternalException) from cocotb.utils import get_sim_time +from cocotb import outcomes def public(f): @@ -91,41 +92,58 @@ def __init__(self, inst, parent): self.log = SimLog("cocotb.coroutine.fail") self._coro = inst self._started = False - self._finished = False self._callbacks = [] self._join = _Join(self) self._parent = parent self.__doc__ = parent._func.__doc__ self.module = parent._func.__module__ self.funcname = parent._func.__name__ - self.retval = None + self._outcome = None if not hasattr(self._coro, "send"): self.log.error("%s isn't a valid coroutine! Did you use the yield " "keyword?" % self.funcname) raise CoroutineComplete() + @property + def retval(self): + if self._outcome is None: + raise RuntimeError("coroutine is not complete") + return self._outcome.get() + + @property + def _finished(self): + return self._outcome is not None + def __iter__(self): return self def __str__(self): return str(self.__name__) - def _advance(self, value): + def _advance(self, outcome): + """ + Advance to the next yield in this coroutine + + :param outcome: The `outcomes.Outcome` object to resume with. + :returns: The object yielded from the coroutine + + If the coroutine returns or throws an error, self._outcome is set, and + this throws `CoroutineComplete`. + """ try: self._started = True - return self._coro.send(value) + return outcome.send(self._coro) except ReturnValue as e: - self.retval = e.retval - self._finished = True + self._outcome = outcomes.Value(e.retval) raise CoroutineComplete() except StopIteration as e: - self._finished = True - self.retval = getattr(e, 'value', None) # for python >=3.3 + retval = getattr(e, 'value', None) # for python >=3.3 + self._outcome = outcomes.Value(retval) raise CoroutineComplete() except BaseException as e: - self._finished = True - raise raise_error(self, "Send raised exception:") + self._outcome = outcomes.Error(e) + raise CoroutineComplete() def send(self, value): return self._coro.send(value) @@ -139,6 +157,8 @@ def close(self): def kill(self): """Kill a coroutine""" self.log.debug("kill() called on coroutine") + # todo: probably better to throw an exception for anyone waiting on the coroutine + self._outcome = outcomes.Value(None) cocotb.scheduler.unschedule(self) def join(self): @@ -188,7 +208,7 @@ def __init__(self, inst, parent): self.handler = RunningTest.ErrorLogHandler(self._handle_error_message) cocotb.log.addHandler(self.handler) - def _advance(self, value): + def _advance(self, outcome): if not self.started: self.error_messages = [] self.log.info("Starting test: \"%s\"\nDescription: %s" % @@ -197,8 +217,8 @@ def _advance(self, value): self.start_sim_time = get_sim_time('ns') self.started = True try: - self.log.debug("Sending trigger %s" % (str(value))) - return self._coro.send(value) + self.log.debug("Sending {}".format(outcome)) + return outcome.send(self._coro) except TestComplete as e: if isinstance(e, TestFailure): self.log.warning(str(e)) diff --git a/cocotb/outcomes.py b/cocotb/outcomes.py new file mode 100644 index 00000000..f21c9c30 --- /dev/null +++ b/cocotb/outcomes.py @@ -0,0 +1,57 @@ +""" +Inspired by https://github.com/python-trio/outcome + +An outcome is similar to the builtin `concurrent.futures.Future` +or `asyncio.Future`, but without being tied to a particular task model. +""" +import abc + +# https://stackoverflow.com/a/38668373 +# Backport of abc.ABC, compatible with Python 2 and 3 +abc_ABC = abc.ABCMeta('ABC', (object,), {'__slots__': ()}) + + +def capture(fn, *args, **kwargs): + """ Obtain an `Outcome` representing the result of a function call """ + try: + return Value(fn(*args, **kwargs)) + except BaseException as e: + return Error(e) + + +class Outcome(abc_ABC): + @abc.abstractmethod + def send(self, gen): + """ Send or throw this outcome into a generator """ + + @abc.abstractmethod + def get(self, gen): + """ Get the value of this outcome, or throw its exception """ + + +class Value(Outcome): + def __init__(self, value): + self.value = value + + def send(self, gen): + return gen.send(self.value) + + def get(self): + return self.value + + def __repr__(self): + return "Value({!r})".format(self.value) + + +class Error(Outcome): + def __init__(self, error): + self.error = error + + def send(self, gen): + return gen.throw(self.error) + + def get(self): + raise self.error + + def __repr__(self): + return "Error({!r})".format(self.error) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index c16ae49a..4d387ed7 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -72,6 +72,8 @@ from cocotb.result import (TestComplete, TestError, ReturnValue, raise_error, create_error, ExternalException) +from cocotb import outcomes + class external_state(object): INIT = 0 RUNNING = 1 @@ -452,6 +454,17 @@ def unschedule(self, coro): if coro._join in self._trigger2coros: self._pending_triggers.append(coro._join) + else: + try: + # throws an error if the background coroutine errored + # and no one was monitoring it + coro.retval + except Exception as e: + self._test_result = TestError( + "Forked coroutine {} raised exception {}" + .format(coro, e) + ) + self._terminate = True # Remove references to allow GC to clean up del coro._join @@ -578,23 +591,15 @@ def schedule(self, coroutine, trigger=None): trigger (cocotb.triggers.Trigger): The trigger that caused this coroutine to be scheduled """ - if hasattr(trigger, "pass_retval"): - sendval = trigger.retval - if _debug: - if isinstance(sendval, ReturnValue): - coroutine.log.debug("Scheduling with ReturnValue(%s)" % - (repr(sendval))) - elif isinstance(sendval, ExternalException): - coroutine.log.debug("Scheduling with ExternalException(%s)" % - (repr(sendval.exception))) - + if trigger is None: + send_outcome = outcomes.Value(None) else: - sendval = trigger - if _debug: - coroutine.log.debug("Scheduling with %s" % str(trigger)) + send_outcome = trigger._outcome + if _debug: + self.log.debug("Scheduling with {}".format(send_outcome)) try: - result = coroutine._advance(sendval) + result = coroutine._advance(send_outcome) if _debug: self.log.debug("Coroutine %s yielded %s (mode %d)" % (coroutine.__name__, str(result), self._mode)) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 7d13fcc5..f9817e21 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -38,7 +38,7 @@ from cocotb.log import SimLog from cocotb.result import raise_error from cocotb.utils import get_sim_steps, get_time_from_sim_steps - +from cocotb import outcomes class TriggerException(Exception): pass @@ -66,6 +66,10 @@ def __del__(self): def __str__(self): return self.__class__.__name__ + @property + def _outcome(self): + return outcomes.Value(self) + class PythonTrigger(Trigger): """Python triggers don't use GPI at all @@ -527,6 +531,10 @@ def __init__(self, coroutine): self._coroutine = coroutine self.pass_retval = True + @property + def _outcome(self): + return self._coroutine._outcome + @property def retval(self): return self._coroutine.retval diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 61f1858e..884338fd 100755 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -692,3 +692,18 @@ def return_it(x): if ret != 42: raise TestFailure("Return statement did not work") ''')) + + +@cocotb.test() +def test_exceptions(): + @cocotb.coroutine + def raise_soon(): + yield Timer(10) + raise ValueError('It is soon now') + + try: + yield raise_soon() + except ValueError: + pass + else: + raise TestFailure("Exception was not raised") From 0ab3f1cafa0431b7417d988985ca65bd64a5d87d Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Tue, 7 Aug 2018 21:59:58 -0700 Subject: [PATCH 1475/2656] Fix broken tests that tried and failed to catch exceptions in the past, and were marked `expect_error` as a workaround --- examples/axi_lite_slave/tests/test_axi_lite_slave.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/axi_lite_slave/tests/test_axi_lite_slave.py b/examples/axi_lite_slave/tests/test_axi_lite_slave.py index cb57ed94..d317ceef 100644 --- a/examples/axi_lite_slave/tests/test_axi_lite_slave.py +++ b/examples/axi_lite_slave/tests/test_axi_lite_slave.py @@ -137,7 +137,7 @@ def write_and_read(dut): dut._log.info("Write 0x%08X to addres 0x%08X" % (int(value), ADDRESS)) -@cocotb.test(skip = False, expect_error=True) +@cocotb.test(skip = False) def write_fail(dut): """ Description: @@ -171,7 +171,7 @@ def write_fail(dut): raise TestFailure("AXI Bus Should have raised an ERROR when writing to \ the wrong bus") -@cocotb.test(skip = False, expect_error=True) +@cocotb.test(skip = False) def read_fail(dut): """ Description: From 5e12232b9874512bd2801fa8cc7e6ffd2ab91af0 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Wed, 8 Aug 2018 08:24:00 -0700 Subject: [PATCH 1476/2656] Fixes gh-624, and adds tests for exceptions in @function and @external --- cocotb/decorators.py | 17 ++-- cocotb/scheduler.py | 16 ++-- .../test_cases/test_external/test_external.py | 81 +++++++++++++++++++ 3 files changed, 99 insertions(+), 15 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 56061085..4236de5f 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -297,7 +297,12 @@ def __call__(self, *args, **kwargs): @coroutine def execute_function(self, event): - event.result = yield cocotb.coroutine(self._func)(*args, **kwargs) + coro = cocotb.coroutine(self._func)(*args, **kwargs) + try: + _outcome = outcomes.Value((yield coro)) + except BaseException as e: + _outcome = outcomes.Error(e) + event.outcome = _outcome event.set() self._event = threading.Event() @@ -306,7 +311,7 @@ def execute_function(self, event): # This blocks the calling external thread until the coroutine finishes self._event.wait() waiter.thread_resume() - return self._event.result + return self._event.outcome.get() def __get__(self, obj, type=None): """Permit the decorator to be used on class methods @@ -328,14 +333,10 @@ def __call__(self, *args, **kwargs): @coroutine def wrapper(): ext = cocotb.scheduler.run_in_executor(self._func, *args, **kwargs) - yield ext.event.wait() - if ext.result is not None: - if isinstance(ext.result, Exception): - raise ExternalException(ext.result) - else: - raise ReturnValue(ext.result) + ret = ext.result # raises if there was an exception + raise ReturnValue(ret) return wrapper() diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 4d387ed7..1e07f80a 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -84,13 +84,18 @@ class external_state(object): class external_waiter(object): def __init__(self): - self.result = None + self._outcome = None self.thread = None self.event = Event() self.state = external_state.INIT self.cond = threading.Condition() self._log = SimLog("cocotb.external.thead.%s" % self.thread, id(self)) + @property + def result(self): + return self._outcome.get() + + def _propogate_state(self, new_state): self.cond.acquire() if _debug: @@ -523,12 +528,9 @@ def run_in_executor(self, func, *args, **kwargs): # calling coroutine (but not the thread) until the external completes def execute_external(func, _waiter): - try: - _waiter.result = func(*args, **kwargs) - if _debug: - self.log.debug("Execution of external routine done %s" % threading.current_thread()) - except Exception as e: - _waiter.result = e + _waiter._outcome = outcomes.capture(func, *args, **kwargs) + if _debug: + self.log.debug("Execution of external routine done %s" % threading.current_thread()) _waiter.thread_done() waiter = external_waiter() diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index 6bef3e39..1742037d 100755 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -276,3 +276,84 @@ def test_ext_exit_error(dut): the clean close down of the sim world""" yield external(test_ext_function)(dut) yield Timer(1000) + + +@cocotb.test() +def test_external_raised_exception(dut): + """ Test that exceptions thrown by @external functions can be caught """ + # workaround for gh-637 + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + + @external + def func(): + raise ValueError() + + try: + yield func() + except ValueError: + pass + else: + raise TestFailure('Exception was not thrown') + +@cocotb.test() +def test_external_returns_exception(dut): + """ Test that exceptions can be returned by @external functions """ + # workaround for gh-637 + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + + @external + def func(): + return ValueError() + + try: + result = yield func() + except ValueError: + raise TestFailure('Exception should not have been thrown') + + if not isinstance(result, ValueError): + raise TestFailure('Exception was not returned') + +@cocotb.test(skip=True) +def test_function_raised_exception(dut): + """ Test that exceptions thrown by @function coroutines can be caught """ + # workaround for gh-637 + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + + @cocotb.function + def func(): + raise ValueError() + yield + + @external + def ext(): + return func() + + try: + yield ext() + except ValueError: + pass + else: + raise TestFailure('Exception was not thrown') + +@cocotb.test() +def test_function_returns_exception(dut): + """ Test that exceptions can be returned by @function coroutines """ + # workaround for gh-637 + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + + @cocotb.function + def func(): + return ValueError() + yield + + @external + def ext(): + return func() + + try: + result = yield ext() + except ValueError: + raise TestFailure('Exception should not have been thrown') + + if not isinstance(result, ValueError): + raise TestFailure('Exception was not returned') From 0378fd59b348234e0a06f47f8dae02525c1a175a Mon Sep 17 00:00:00 2001 From: Marek Cieplucha Date: Fri, 14 Dec 2018 03:18:04 -0800 Subject: [PATCH 1477/2656] changed PYTHON_BIN variable assignment from = to ?= --- makefiles/Makefile.pylib.Darwin | 14 +++++++------- makefiles/Makefile.pylib.Linux | 14 +++++++------- makefiles/Makefile.pylib.Msys | 6 +++--- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/makefiles/Makefile.pylib.Darwin b/makefiles/Makefile.pylib.Darwin index 1df68d5a..b173d98f 100644 --- a/makefiles/Makefile.pylib.Darwin +++ b/makefiles/Makefile.pylib.Darwin @@ -27,13 +27,13 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -# All common pyhon related rules +# All common python related rules ifneq ($(COCOTB_PYTHON_DEBUG),) DBG_PYTHON_VERSION=$(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') - PYTHON_BIN=python$(DBG_PYTHON_VERSION)-dbg + PYTHON_BIN?=python$(DBG_PYTHON_VERSION)-dbg else - PYTHON_BIN=python + PYTHON_BIN?=python endif NATIVE_ARCH=$(shell uname -m) @@ -58,13 +58,13 @@ endif # we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports # is we simply link against all dynamic libraries in the Python installation ifneq (, $(shell which $(PYTHON_BIN)-config 2>/dev/null)) -PY_CFG_EXE=$(PYTHON_BIN)-config + PY_CFG_EXE=$(PYTHON_BIN)-config else ifneq (, $(shell which $(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config 2>/dev/null)) -PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config + PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config else ifneq (, $(shell which $(PYTHON_BIN)$(PYTHON_VERSION)-config 2>/dev/null)) -PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_VERSION)-config + PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_VERSION)-config else -$(error ERROR: Unable to find a valid python-config.) + $(error ERROR: Unable to find a valid python-config.) endif PYLIBS = $(shell $(PY_CFG_EXE) --libs) diff --git a/makefiles/Makefile.pylib.Linux b/makefiles/Makefile.pylib.Linux index b224b7aa..c82836ca 100644 --- a/makefiles/Makefile.pylib.Linux +++ b/makefiles/Makefile.pylib.Linux @@ -30,10 +30,10 @@ # All common python related rules ifneq ($(COCOTB_PYTHON_DEBUG),) -DBG_PYTHON_VERSION=$(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') -PYTHON_BIN=python$(DBG_PYTHON_VERSION)-dbg + DBG_PYTHON_VERSION=$(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') + PYTHON_BIN?=python$(DBG_PYTHON_VERSION)-dbg else -PYTHON_BIN=python + PYTHON_BIN?=python endif NATIVE_ARCH=$(shell uname -m) @@ -58,13 +58,13 @@ endif # we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports # is we simply link against all dynamic libraries in the Python installation ifneq (, $(shell which $(PYTHON_BIN)$(PYTHON_VERSION)-config 2>/dev/null)) -PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_VERSION)-config + PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_VERSION)-config else ifneq (, $(shell which $(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config 2>/dev/null)) -PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config + PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config else ifneq (, $(shell which $(PYTHON_BIN)-config 2>/dev/null)) -PY_CFG_EXE=$(PYTHON_BIN)-config + PY_CFG_EXE=$(PYTHON_BIN)-config else -$(error ERROR: Unable to find a valid python-config.) + $(error ERROR: Unable to find a valid python-config.) endif PYLIBS = $(shell $(PY_CFG_EXE) --libs) diff --git a/makefiles/Makefile.pylib.Msys b/makefiles/Makefile.pylib.Msys index 7520fd4e..d9c4ea72 100644 --- a/makefiles/Makefile.pylib.Msys +++ b/makefiles/Makefile.pylib.Msys @@ -31,15 +31,15 @@ # if not explicitly set, auto-detect python dir from system path ifeq ($(PYTHON_DIR),) -PYTHON_DIR = $(shell dirname $(shell which python)) + PYTHON_DIR ?= $(shell dirname $(shell which python)) endif # make sure python dir was set properly ifeq ($(PYTHON_DIR),) -$(error "Path to Python directory must be included in system path or defined in PYTHON_DIR environment variable") + $(error "Path to Python directory must be included in system path or defined in PYTHON_DIR environment variable") endif -PYTHON_BIN=$(PYTHON_DIR)/python.exe +PYTHON_BIN?=$(PYTHON_DIR)/python.exe # We might work with other Python versions LOCAL_PYTHON_VERSION?=$(shell $(PYTHON_BIN) -c 'import distutils.sysconfig; print( distutils.sysconfig.get_python_version() )') From 337056356cc9e5a08f4d08a1d54a624399fd46d8 Mon Sep 17 00:00:00 2001 From: Marek Cieplucha Date: Fri, 14 Dec 2018 05:09:23 -0800 Subject: [PATCH 1478/2656] change spaces to tabs in Makefiles --- makefiles/Makefile.pylib.Darwin | 18 +++++++++--------- makefiles/Makefile.pylib.Linux | 18 +++++++++--------- makefiles/Makefile.pylib.Msys | 4 ++-- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/makefiles/Makefile.pylib.Darwin b/makefiles/Makefile.pylib.Darwin index b173d98f..9021a713 100644 --- a/makefiles/Makefile.pylib.Darwin +++ b/makefiles/Makefile.pylib.Darwin @@ -30,10 +30,10 @@ # All common python related rules ifneq ($(COCOTB_PYTHON_DEBUG),) - DBG_PYTHON_VERSION=$(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') - PYTHON_BIN?=python$(DBG_PYTHON_VERSION)-dbg + DBG_PYTHON_VERSION=$(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') + PYTHON_BIN?=python$(DBG_PYTHON_VERSION)-dbg else - PYTHON_BIN?=python + PYTHON_BIN?=python endif NATIVE_ARCH=$(shell uname -m) @@ -50,21 +50,21 @@ PYTHON_DYNLIBDIR:=$(shell python -c 'from __future__ import print_function; from # Nasty hack but there's no way in Python properly get the directories for 32-bit Python on a 64-bit system # TODO: Under OSX we can use "export VERSIONER_PYTHON_PREFER_32_BIT=yes", no Linux equivalent though ifneq ($(NATIVE_ARCH),$(ARCH)) - PYTHON_LIBDIR:=$(subst 64,,$(PYTHON_LIBDIR)) - PYTHON_DYNLIBDIR:=$(subst 64,,$(PYTHON_DYNLIBDIR)) + PYTHON_LIBDIR:=$(subst 64,,$(PYTHON_LIBDIR)) + PYTHON_DYNLIBDIR:=$(subst 64,,$(PYTHON_DYNLIBDIR)) endif # Since we don't know which modules we might need or whether the simulator # we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports # is we simply link against all dynamic libraries in the Python installation ifneq (, $(shell which $(PYTHON_BIN)-config 2>/dev/null)) - PY_CFG_EXE=$(PYTHON_BIN)-config + PY_CFG_EXE=$(PYTHON_BIN)-config else ifneq (, $(shell which $(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config 2>/dev/null)) - PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config + PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config else ifneq (, $(shell which $(PYTHON_BIN)$(PYTHON_VERSION)-config 2>/dev/null)) - PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_VERSION)-config + PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_VERSION)-config else - $(error ERROR: Unable to find a valid python-config.) + $(error ERROR: Unable to find a valid python-config.) endif PYLIBS = $(shell $(PY_CFG_EXE) --libs) diff --git a/makefiles/Makefile.pylib.Linux b/makefiles/Makefile.pylib.Linux index c82836ca..53379dc2 100644 --- a/makefiles/Makefile.pylib.Linux +++ b/makefiles/Makefile.pylib.Linux @@ -30,10 +30,10 @@ # All common python related rules ifneq ($(COCOTB_PYTHON_DEBUG),) - DBG_PYTHON_VERSION=$(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') - PYTHON_BIN?=python$(DBG_PYTHON_VERSION)-dbg + DBG_PYTHON_VERSION=$(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') + PYTHON_BIN?=python$(DBG_PYTHON_VERSION)-dbg else - PYTHON_BIN?=python + PYTHON_BIN?=python endif NATIVE_ARCH=$(shell uname -m) @@ -50,21 +50,21 @@ PYTHON_DYNLIBDIR:=$(shell $(PYTHON_BIN) -c 'from __future__ import print_functio # Nasty hack but there's no way in Python properly get the directories for 32-bit Python on a 64-bit system # TODO: Under OSX we can use "export VERSIONER_PYTHON_PREFER_32_BIT=yes", no Linux equivalent though ifneq ($(NATIVE_ARCH),$(ARCH)) - PYTHON_LIBDIR:=$(subst 64,,$(PYTHON_LIBDIR)) - PYTHON_DYNLIBDIR:=$(subst 64,,$(PYTHON_DYNLIBDIR)) + PYTHON_LIBDIR:=$(subst 64,,$(PYTHON_LIBDIR)) + PYTHON_DYNLIBDIR:=$(subst 64,,$(PYTHON_DYNLIBDIR)) endif # Since we don't know which modules we might need or whether the simulator # we're using passes the RTLD_GLOBAL to dlopen or whether the OS supports # is we simply link against all dynamic libraries in the Python installation ifneq (, $(shell which $(PYTHON_BIN)$(PYTHON_VERSION)-config 2>/dev/null)) - PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_VERSION)-config + PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_VERSION)-config else ifneq (, $(shell which $(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config 2>/dev/null)) - PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config + PY_CFG_EXE=$(PYTHON_BIN)$(PYTHON_MAJOR_VERSION)-config else ifneq (, $(shell which $(PYTHON_BIN)-config 2>/dev/null)) - PY_CFG_EXE=$(PYTHON_BIN)-config + PY_CFG_EXE=$(PYTHON_BIN)-config else - $(error ERROR: Unable to find a valid python-config.) + $(error ERROR: Unable to find a valid python-config.) endif PYLIBS = $(shell $(PY_CFG_EXE) --libs) diff --git a/makefiles/Makefile.pylib.Msys b/makefiles/Makefile.pylib.Msys index d9c4ea72..f66462c6 100644 --- a/makefiles/Makefile.pylib.Msys +++ b/makefiles/Makefile.pylib.Msys @@ -31,12 +31,12 @@ # if not explicitly set, auto-detect python dir from system path ifeq ($(PYTHON_DIR),) - PYTHON_DIR ?= $(shell dirname $(shell which python)) + PYTHON_DIR ?= $(shell dirname $(shell which python)) endif # make sure python dir was set properly ifeq ($(PYTHON_DIR),) - $(error "Path to Python directory must be included in system path or defined in PYTHON_DIR environment variable") + $(error "Path to Python directory must be included in system path or defined in PYTHON_DIR environment variable") endif PYTHON_BIN?=$(PYTHON_DIR)/python.exe From ef8b24bbd8586c78c9438a37d0f67015dde481f6 Mon Sep 17 00:00:00 2001 From: jeroenvanstraten <40433108+jeroenvanstraten@users.noreply.github.com> Date: Fri, 14 Dec 2018 15:17:31 +0100 Subject: [PATCH 1479/2656] Add missing __bool__ entries for py3 compatibility (#655) --- cocotb/decorators.py | 2 ++ cocotb/triggers.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index e7e76da8..5b117146 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -173,6 +173,8 @@ def __nonzero__(self): otherwise return true""" return not self._finished + __bool__ = __nonzero__ + def sort_name(self): if self.stage is None: return "%s.%s" % (self.module, self.funcname) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 7d13fcc5..6d960380 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -503,6 +503,8 @@ def __nonzero__(self): """Provide boolean of a Lock""" return self.locked + __bool__ = __nonzero__ + class NullTrigger(Trigger): """ From 94eeaba607f580b7b666d110c1a1b87c32426499 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Fri, 14 Dec 2018 20:39:31 -0800 Subject: [PATCH 1480/2656] Use weakref to deal with GC issues, rather than trying to clean up ourselves This prevents the error messages in #650. It also: * Fixes a bug where `coro.join()` returns the result the first time, but `None` on subsequent times * Replaces four ways to spell join (`coro._join`, `coro.join()`, `_Join(coro)`, `Join(coro)`) with two (`coro.join()`, `Join(coro)`) --- cocotb/decorators.py | 8 +--- cocotb/scheduler.py | 8 ++-- cocotb/triggers.py | 30 ++++++++---- tests/test_cases/test_cocotb/test_cocotb.py | 52 +++++++++++++++++++++ 4 files changed, 78 insertions(+), 20 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 5b117146..43a0761c 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -37,7 +37,7 @@ import cocotb from cocotb.log import SimLog -from cocotb.triggers import _Join, PythonTrigger, Timer, Event, NullTrigger, Join +from cocotb.triggers import Join, PythonTrigger, Timer, Event, NullTrigger from cocotb.result import (TestComplete, TestError, TestFailure, TestSuccess, ReturnValue, raise_error, ExternalException) from cocotb.utils import get_sim_time @@ -94,7 +94,6 @@ def __init__(self, inst, parent): self._started = False self._finished = False self._callbacks = [] - self._join = _Join(self) self._parent = parent self.__doc__ = parent._func.__doc__ self.module = parent._func.__module__ @@ -159,10 +158,7 @@ def _finished_cb(self): def join(self): """Return a trigger that will fire when the wrapped coroutine exits""" - if self._finished: - return NullTrigger() - else: - return self._join + return Join(self) def has_started(self): return self._started diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index cf7465f9..db86b20c 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -67,7 +67,7 @@ import cocotb import cocotb.decorators from cocotb.triggers import (Trigger, GPITrigger, Timer, ReadOnly, PythonTrigger, - _NextTimeStep, _ReadWrite, Event, NullTrigger) + _NextTimeStep, _ReadWrite, Event, Join) from cocotb.log import SimLog from cocotb.result import (TestComplete, TestError, ReturnValue, raise_error, create_error, ExternalException) @@ -450,11 +450,9 @@ def unschedule(self, coro): del self._trigger2coros[trigger] del self._coro2triggers[coro] - if coro._join in self._trigger2coros: - self._pending_triggers.append(coro._join) + if Join(coro) in self._trigger2coros: + self._pending_triggers.append(Join(coro)) - # Remove references to allow GC to clean up - del coro._join def save_write(self, handle, value): if self._mode == Scheduler._MODE_READONLY: diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 6d960380..a1e2a053 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -29,6 +29,7 @@ A collections of triggers which a testbench can 'yield' """ import os +import weakref # For autodocumentation don't need the extension modules if "SPHINX_BUILD" in os.environ: @@ -520,12 +521,25 @@ def prime(self, callback): callback(self) -class _Join(PythonTrigger): +class Join(PythonTrigger): """ Join a coroutine, firing when it exits """ + # Ensure that each coroutine has at most one join trigger. + # Using a weak dictionary ensures we don't create a reference cycle + _instances = weakref.WeakValueDictionary() + + def __new__(cls, coroutine): + # find the existing instance, if possible - else create a new one + try: + return cls._instances[coroutine] + except KeyError: + instance = super(Join, cls).__new__(cls) + cls._instances[coroutine] = instance + return instance + def __init__(self, coroutine): - PythonTrigger.__init__(self) + super(Join, self).__init__() self._coroutine = coroutine self.pass_retval = True @@ -533,13 +547,11 @@ def __init__(self, coroutine): def retval(self): return self._coroutine.retval - # def prime(self, callback): - # """Register our calback for when the coroutine exits""" - # Trigger.prime(self) + def prime(self, callback): + if self._coroutine._finished: + callback(self) + else: + super(Join, self).prime(callback) def __str__(self): return self.__class__.__name__ + "(%s)" % self._coroutine.__name__ - - -def Join(coro): - return coro._join diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 9e602e57..63da61f7 100755 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -712,6 +712,58 @@ def test_binary_value_compat(dut): yield Timer(100) # Make it do something with time +@cocotb.test() +def join_finished(dut): + """ + Test that joining a coroutine that has already been joined gives + the same result as it did the first time. + """ + + retval = None + + @cocotb.coroutine + def some_coro(): + yield Timer(1) + raise ReturnValue(retval) + + coro = cocotb.fork(some_coro()) + + retval = 1 + x = yield coro.join() + assert x == 1 + + # joining the second time should give the same result. + # we chage retval here to prove it does not run again + retval = 2 + x = yield coro.join() + assert x == 1 + + +@cocotb.test() +def test_kill_twice(dut): + """ + Test that killing a coroutine that has already been killed does not crash + """ + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + yield Timer(1) + clk_gen.kill() + yield Timer(1) + clk_gen.kill() + + +@cocotb.test() +def test_join_identity(dut): + """ + Test that Join() returns the same object each time + """ + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + + assert Join(clk_gen) is Join(clk_gen) + yield Timer(1) + clk_gen.kill() + + + if sys.version_info[:2] >= (3, 3): # this would be a syntax error in older python, so we do the whole # thing inside exec From 48064bd07a813444ba47ba4140736658cf95a784 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 15 Dec 2018 22:14:43 -0800 Subject: [PATCH 1481/2656] Remove the _Edge base class from ClockCycles Rationale: * ClockCycles shares only one line of implementation with _Edge, and it's easier just to repeat it. * ClockCycles doesn't make much sense as a subclass of _Edge * _Edge is private anyway, so no consumer of the public API will care --- cocotb/triggers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index a1e2a053..92187cd2 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -280,12 +280,13 @@ def FallingEdge(signal): return signal._f_edge -class ClockCycles(_Edge): +class ClockCycles(GPITrigger): """ Execution will resume after N rising edges or N falling edges """ def __init__(self, signal, num_cycles, rising=True): - _Edge.__init__(self, signal) + super(ClockCycles, self).__init__() + self.signal = signal self.num_cycles = num_cycles if rising is True: self._rising = 1 From 435f7c9740ee715049bc18d84ee9af062cada744 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 15 Dec 2018 22:15:59 -0800 Subject: [PATCH 1482/2656] Break reference cycles in the edge triggers Previously there was a cyclic reference between `Signal._r_edge -> _Edge.signal -> ...` This takes the same approach as 94eeaba607f580b7b666d110c1a1b87c32426499, using a weak dictionary to ensure edges keep their signal alive, but not vice versa. --- cocotb/handle.py | 4 --- cocotb/triggers.py | 89 +++++++++++++++++++--------------------------- 2 files changed, 37 insertions(+), 56 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index eb3e76d1..b46f592f 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -48,7 +48,6 @@ from cocotb.binary import BinaryValue from cocotb.log import SimLog from cocotb.result import TestError -from cocotb.triggers import _RisingEdge, _FallingEdge, _Edge from cocotb.utils import get_python_integer_types # Only issue a warning for each deprecated attribute access @@ -546,9 +545,6 @@ def __init__(self, handle, path): _handle [integer] : vpi/vhpi handle to the simulator object """ NonHierarchyIndexableObject.__init__(self, handle, path) - self._r_edge = _RisingEdge(self) - self._f_edge = _FallingEdge(self) - self._e_edge = _Edge(self) def drivers(self): """ diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 92187cd2..a4892940 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -206,78 +206,63 @@ def NextTimeStep(): return _nxts -class _Edge(GPITrigger): +class _EdgeBase(GPITrigger): """ Execution will resume when an edge occurs on the provided signal """ - def __init__(self, signal): - GPITrigger.__init__(self) - self.signal = signal - - def prime(self, callback): - """Register notification of a value change via a callback""" - if self.cbhdl == 0: - self.cbhdl = simulator.register_value_change_callback(self.signal. - _handle, - callback, - 3, - self) - if self.cbhdl == 0: - raise_error(self, "Unable set up %s Trigger" % (str(self))) - Trigger.prime(self) - - def __str__(self): - return self.__class__.__name__ + "(%s)" % self.signal._name + @classmethod + @property + def _edge_type(self): + """ + The edge type, as understood by the C code. Must be set in subclasses + """ + raise NotImplementedError -def Edge(signal): - return signal._e_edge + # Ensure that each signal has at most one edge trigger per edge type. + # Using a weak dictionary ensures we don't create a reference cycle + _instances = weakref.WeakValueDictionary() + def __new__(cls, signal): + # find the existing instance, if possible - else create a new one + key = (signal, cls._edge_type) + try: + return cls._instances[key] + except KeyError: + instance = super(_EdgeBase, cls).__new__(cls) + cls._instances[key] = instance + return instance -class _RisingOrFallingEdge(_Edge): - def __init__(self, signal, rising): - _Edge.__init__(self, signal) - if rising is True: - self._rising = 1 - else: - self._rising = 2 + def __init__(self, signal): + super(_EdgeBase, self).__init__() + self.signal = signal def prime(self, callback): + """Register notification of a value change via a callback""" if self.cbhdl == 0: - self.cbhdl = simulator.register_value_change_callback(self.signal. - _handle, - callback, - self._rising, - self) + self.cbhdl = simulator.register_value_change_callback( + self.signal._handle, callback, type(self)._edge_type, self + ) if self.cbhdl == 0: raise_error(self, "Unable set up %s Trigger" % (str(self))) - Trigger.prime(self) + super(_EdgeBase, self).prime() def __str__(self): return self.__class__.__name__ + "(%s)" % self.signal._name -class _RisingEdge(_RisingOrFallingEdge): - """ - Execution will resume when a rising edge occurs on the provided signal - """ - def __init__(self, signal): - _RisingOrFallingEdge.__init__(self, signal, rising=True) +class RisingEdge(_EdgeBase): + """ Triggers on the rising edge of the provided signal """ + _edge_type = 1 -def RisingEdge(signal): - return signal._r_edge - - -class _FallingEdge(_RisingOrFallingEdge): - """ - Execution will resume when a falling edge occurs on the provided signal - """ - def __init__(self, signal): - _RisingOrFallingEdge.__init__(self, signal, rising=False) +class FallingEdge(_EdgeBase): + """ Triggers on the falling edge of the provided signal """ + _edge_type = 2 -def FallingEdge(signal): - return signal._f_edge +class Edge(_EdgeBase): + """ Triggers on either edge in a signal """ + _edge_type = 3 class ClockCycles(GPITrigger): From 16ca79bf93b44f98a274140f1d7b859f572eec2e Mon Sep 17 00:00:00 2001 From: David Stanford Date: Sun, 16 Dec 2018 07:58:07 -0600 Subject: [PATCH 1483/2656] More updates to documentation based on review: https://github.com/potentialventures/cocotb/pull/601. Thanks to imphil --- documentation/source/coroutines.rst | 17 ++-- documentation/source/quickstart.rst | 80 +++++++++++++------ documentation/source/testbench_tools.rst | 48 +++++++++-- .../tests/test_axi_lite_slave.py | 4 +- 4 files changed, 109 insertions(+), 40 deletions(-) diff --git a/documentation/source/coroutines.rst b/documentation/source/coroutines.rst index 269cd309..ac2946eb 100644 --- a/documentation/source/coroutines.rst +++ b/documentation/source/coroutines.rst @@ -76,7 +76,8 @@ allowing them to distinguish which trigger fired: raise TestFailure("Timed out waiting for packet") -Coroutines can be forked for parallel operation within a function of that code and the forked code. +Coroutines can be forked for parallel operation within a function of that code and +the forked code. .. code-block:: python @cocotb.test() @@ -85,9 +86,10 @@ Coroutines can be forked for parallel operation within a function of that code a while reset is active, toggle signals """ tb = uart_tb(dut) - cocotb.fork(Clock(dut.clk, 1000).start()) #Clock is a built in class for toggling a clock signal - - cocotb.fork(tb.reset_dut(dut.rstn,20000)) #reset_dut is a function part of the user generated uart_tb class. + #Clock is a built in class for toggling a clock signal + cocotb.fork(Clock(dut.clk, 1000).start()) + #reset_dut is a function- part of the user generated uart_tb class. + cocotb.fork(tb.reset_dut(dut.rstn,20000)) yield Timer(10000) print("Reset is still active: %d" % dut.rstn) @@ -118,10 +120,11 @@ Coroutines can be joined to end parallel operation within a function. else: break -Coroutines can be killed before they complete, forcing their completion before they'd naturally end. +Coroutines can be killed before they complete, forcing their completion before +they'd naturally end. .. code-block:: python - @cocotb.test(expect_fail=False) + @cocotb.test() def test_different_clocks(dut): clk_1mhz = Clock(dut.clk, 1.0, units='us') clk_250mhz = Clock(dut.clk, 4.0, units='ns') @@ -131,6 +134,7 @@ Coroutines can be killed before they complete, forcing their completion before t yield Timer(1) yield RisingEdge(dut.clk) edge_time_ns = get_sim_time(units='ns') + # note, isclose is a python 3.5+ feature. if not isclose(edge_time_ns, start_time_ns + 1000.0): raise TestFailure("Expected a period of 1 us") @@ -141,6 +145,7 @@ Coroutines can be killed before they complete, forcing their completion before t yield Timer(1) yield RisingEdge(dut.clk) edge_time_ns = get_sim_time(units='ns') + # note, isclose is a python 3.5+ feature if not isclose(edge_time_ns, start_time_ns + 4.0): raise TestFailure("Expected a period of 4 ns") diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index 98f599da..b7c961fc 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -17,7 +17,10 @@ Cocotb has the following requirements: * GNU Make * A Verilog or VHDL simulator, depending on your source RTL code -Internal development is performed on Linux Mint 17 (x64). We also use Redhat 6.5(x64). Other Redhat and Ubuntu based distributions (x32 and x64) should work too but due fragmented nature of Linux we can not test everything. Instructions are provided for the main distributions we use. +Internal development is performed on Linux Mint 17 (x64). We also use Redhat +6.5(x64). Other Redhat and Ubuntu based distributions (x32 and x64) should work +too but due fragmented nature of Linux we can not test everything. Instructions +are provided for the main distributions we use. Linux native arch installation ------------------------------ @@ -28,7 +31,9 @@ Ubuntu based installation $> sudo apt-get install git make gcc g++ swig python-dev -This will allow building of the Cocotb libs for use with a 64 bit native simulator. If a 32 bit simulator is being used then additional steps to install 32bit development libraries and python are needed. +This will allow building of the Cocotb libs for use with a 64 bit native +simulator. If a 32 bit simulator is being used then additional steps to install +32bit development libraries and python are needed. Redhat based installation @@ -36,13 +41,16 @@ Redhat based installation $> sudo yum install gcc gcc-c++ libstdc++-devel swig python-devel -This will allow building of the Cocotb libs for use with a 64 bit native simulator. If a 32 bit simulator is being used then additional steps to install 32bit development libraries and python are needed. +This will allow building of the Cocotb libs for use with a 64 bit native +simulator. If a 32 bit simulator is being used then additional steps to install +32bit development libraries and python are needed. 32 bit Python ------------- -Additional development libraries are needed for building 32bit python on 64 bit systems. +Additional development libraries are needed for building 32bit python on 64 bit +systems. Ubuntu based installation @@ -50,7 +58,9 @@ Ubuntu based installation $> sudo apt-get install libx32gcc1 gcc-4.8-multilib lib32stdc++-4.8-dev -Replace 4.8 with the version of gcc that was installed on the system in the step above. Unlike on Redhat where 32 bit python can co-exist with native python ubuntu requires the source to be downloaded and built. +Replace 4.8 with the version of gcc that was installed on the system in the step +above. Unlike on Redhat where 32 bit python can co-exist with native python +ubuntu requires the source to be downloaded and built. Redhat based installation @@ -71,7 +81,9 @@ Specific releases can be downloaded from https://www.python.org/downloads/ . $> make $> sudo make install -Cocotb can now be built against 32bit python by setting the architecture and placing the 32bit python ahead of the native version in the path when running a test +Cocotb can now be built against 32bit python by setting the architecture and +placing the 32bit python ahead of the native version in the path when running a +test .. code-block:: bash @@ -82,13 +94,18 @@ Cocotb can now be built against 32bit python by setting the architecture and pla Windows 7 installation ---------------------- -Recent work has been done with the support of the Cocotb community to enable Windows support using the MinGW/Msys environment. Download the MinGQ installer from. +Recent work has been done with the support of the Cocotb community to enable +Windows support using the MinGW/Msys environment. Download the MinGQ installer +from. http://sourceforge.net/projects/mingw/files/latest/download?source=files . -Run the GUI installer and specify a directory you would like the environment installed in. The installer will retrieve a list of possible packages, when this is done press continue. The MinGW Installation Manager is then launched. +Run the GUI installer and specify a directory you would like the environment +installed in. The installer will retrieve a list of possible packages, when this +is done press continue. The MinGW Installation Manager is then launched. -The following packages need selecting by checking the tick box and selecting "Mark for installation" +The following packages need selecting by checking the tick box and selecting +"Mark for installation" .. code-block:: bash @@ -98,13 +115,18 @@ The following packages need selecting by checking the tick box and selecting "Ma -- mingw32-gcc-g++ -- msys-base -From the Installation menu then select "Apply Changes", in the next dialog select "Apply". +From the Installation menu then select "Apply Changes", in the next dialog +select "Apply". -When installed a shell can be opened using the "msys.bat" file located under the /msys/1.0/ +When installed a shell can be opened using the "msys.bat" file located under +the /msys/1.0/ -Python can be downloaded from https://www.python.org/ftp/python/2.7.9/python-2.7.9.msi, other versions of python can be used as well. Run the installer and download to your chosen location. +Python can be downloaded from https://www.python.org/ftp/python/2.7.9/python-2.7.9.msi, +other versions of python can be used as well. Run the installer and download to +your chosen location. -It is beneficial to add the path to Python to the windows system PATH variable so it can be used easily from inside Msys. +It is beneficial to add the path to Python to the windows system PATH variable +so it can be used easily from inside Msys. Once inside the Msys shell commands as given here will work as expected. @@ -140,7 +162,11 @@ To run a test using a different simulator: Running a VHDL example ---------------------- -The endian swapper example includes both a VHDL and Verilog RTL implementation. The Cocotb testbench can execute against either implementation using VPI for Verilog and VHPI/FLI for VHDL. To run the test suite against the VHDL implementation use the following command (a VHPI or FLI capable simulator must be used): +The endian swapper example includes both a VHDL and Verilog RTL implementation. +The Cocotb testbench can execute against either implementation using VPI for +Verilog and VHPI/FLI for VHDL. To run the test suite against the VHDL +implementation use the following command (a VHPI or FLI capable simulator must +be used): .. code-block:: bash @@ -152,8 +178,10 @@ Using Cocotb ============ A typical Cocotb testbench requires no additional RTL code. -The Design Under Test (DUT) is instantiated as the toplevel in the simulator without any wrapper code. -Cocotb drives stimulus onto the inputs to the DUT and monitors the outputs directly from Python. +The Design Under Test (DUT) is instantiated as the toplevel in the simulator +without any wrapper code. +Cocotb drives stimulus onto the inputs to the DUT and monitors the outputs +directly from Python. Creating a Makefile @@ -180,9 +208,10 @@ Creating a test The test is written in Python. Cocotb wraps your top level with the handle you pass it. In this documentation, and most of the examples in the project, that -handle is **dut**, but you can pass your own preferred name in instead. The handle -is used in all python files referencing your RTL project. Assuming we have a -toplevel port called ``clk`` we could create a test file containing the following: +handle is **dut**, but you can pass your own preferred name in instead. The +handle is used in all python files referencing your RTL project. Assuming we +have a toplevel port called ``clk`` we could create a test file containing the +following: .. code-block:: python @@ -208,9 +237,10 @@ This will drive a square wave clock onto the ``clk`` port of the toplevel. Accessing the design -------------------- -When cocotb initialises it finds the top-level instantiation in the simulator and creates a handle called **dut**. -Top-level signals can be accessed using the "dot" notation used for accessing object attributes in Python. -The same mechanism can be used to access signals inside the design. +When cocotb initialises it finds the top-level instantiation in the simulator +and creates a handle called **dut**. Top-level signals can be accessed using the +"dot" notation used for accessing object attributes in Python. The same mechanism +can be used to access signals inside the design. .. code-block:: python @@ -239,8 +269,10 @@ Values can be assigned to signals using either the .value property of a handle o dut.sub_block.memory.array[4] <= 2 -The "<=" operator is overridden by cocotb to help make it more clear when an object -being assigned a value is part of the RTL source as compared to the python test code. +The syntax `sig <= new_value` is a short form of `sig.value = new_value`. It not +only resembles HDL-syntax, but also has the same semantics: writes are not +applied immediately, but delayed until the next write cycle. Use +`sig.setimmediatevalue(new_val)` to set a new value immediately. diff --git a/documentation/source/testbench_tools.rst b/documentation/source/testbench_tools.rst index 3a51aec3..27ffb9f2 100644 --- a/documentation/source/testbench_tools.rst +++ b/documentation/source/testbench_tools.rst @@ -5,9 +5,15 @@ Test Bench Tools Logging ======= -Cocotb extends the python logging library. Each dut, monitor, driver, and scoreboard (as well as any other function using the `coroutine` decorator) implements it's own :class:`logging` object, and each can be set to it's own logging level. Within a dut, each hierarchical object can also have individual logging levels set. +Cocotb extends the python logging library. Each dut, monitor, driver, and +scoreboard (as well as any other function using the `coroutine` decorator) +implements it's own :class:`logging` object, and each can be set to it's own +logging level. Within a dut, each hierarchical object can also have individual +logging levels set. -When logging hdl objects, beware that **_log** is the preferred way to use logging. This helps minimize the change of name collisions with an hdl log component with the python logging functionality. +When logging hdl objects, beware that **_log** is the preferred way to use +logging. This helps minimize the change of name collisions with an hdl log +component with the python logging functionality. Log printing levels can also be set on a per object basis. @@ -25,7 +31,7 @@ Log printing levels can also be set on a per object basis. level = logging.DEBUG if debug else logging.WARNING self.stream_in.log.setLevel(level) self.stream_in_recovered.log.setLevel(level) - self.dut.reset_n._log(setLevel(logging.DEBUG) + self.dut.reset_n._log.setLevel(logging.DEBUG) And when the logging is actually called @@ -63,14 +69,20 @@ will display as something like Busses ====== -Busses are simply defined as collection of signals. The :py:class:`Bus` class will automatically bundle any group of signals together that are named similar to dut.. for instance, +Busses are simply defined as collection of signals. The :py:class:`Bus` class +will automatically bundle any group of signals together that are named similar +to dut.. for instance, .. code-block:: python dut.stream_in_valid dut.stream_in_data -have a bus name of ``stream_in``, a separator of ``_``, and signal names of ``valid`` and ``data``. A list of signal names, or a dictionary mapping attribute names to signal names is also passed into the :py:class:`Bus` class. Busses can have values driven onto them, be captured (returning a dictionary), or sampled and stored into a similar object. +have a bus name of ``stream_in``, a separator of ``_``, and signal names of +``valid`` and ``data``. A list of signal names, or a dictionary mapping attribute +names to signal names is also passed into the :py:class:`Bus` class. Busses can +have values driven onto them, be captured (returning a dictionary), or sampled +and stored into a similar object. .. code-block:: python @@ -80,7 +92,11 @@ have a bus name of ``stream_in``, a separator of ``_``, and signal names of ``va Driving Busses ============== -examples and specific bus implementation bus drivers (amba, avalon, xgmii, and others) exist in the :py:class:`Driver` class enabling a test to append transactions to perform the serialization of transactions onto a physical interface. Here's an example using the avalon bus driver in the endian swapper example +Examples and specific bus implementation bus drivers (amba, avalon, xgmii, and +others) exist in the :py:class:`Driver` class enabling a test to append +transactions to perform the serialization of transactions onto a physical +interface. Here's an example using the avalon bus driver in the endian swapper +example .. code-block:: python @@ -110,7 +126,19 @@ examples and specific bus implementation bus drivers (amba, avalon, xgmii, and o Monitoring Busses ================= -For our testbenches to actually be useful, we have to monitor some of these busses, and not just drive them. That's where the :py:class:`Monitor` class comes in, with prebuilt Monitors for **avalon** and **xgmii** busses. The Monitor class is a base class which you are expected to derive for your particular purpose. You must create a `_monitor_recv()` function which is responsible for determining 1) at what points in simulation to call the `_recv()` function, and 2) what transaction values to pass to be stored in the monitors receiving queue. Monitors are good for both outputs of the dut for verification, and for the inputs of the dut, to drive a test model of the dut to be compared to the actual dut. For this purpose, input monitors will often have a callback function passed that is a model. This model will often generate expected transactions, which are then compared using the :py:class:`Scoreboard` class. +For our testbenches to actually be useful, we have to monitor some of these +busses, and not just drive them. That's where the :py:class:`Monitor` class +comes in, with prebuilt Monitors for **avalon** and **xgmii** busses. The +Monitor class is a base class which you are expected to derive for your +particular purpose. You must create a `_monitor_recv()` function which is +responsible for determining 1) at what points in simulation to call the +`_recv()` function, and 2) what transaction values to pass to be stored in the +monitors receiving queue. Monitors are good for both outputs of the dut for +verification, and for the inputs of the dut, to drive a test model of the dut +to be compared to the actual dut. For this purpose, input monitors will often +have a callback function passed that is a model. This model will often generate +expected transactions, which are then compared using the :py:class:`Scoreboard` +class. .. code-block:: python @@ -167,7 +195,11 @@ For our testbenches to actually be useful, we have to monitor some of these buss Tracking testbench errors ========================= -The :py:class:`Scoreboard` class is used to compare the actual outputs to expected outputs. Monitors are added to the scoreboard for the actual outputs, and the expected outputs can be either a simple list, or a function that provides a transaction. Here's some code from the **DFF** example, similar to above with the scoreboard added. +The :py:class:`Scoreboard` class is used to compare the actual outputs to +expected outputs. Monitors are added to the scoreboard for the actual outputs, +and the expected outputs can be either a simple list, or a function that +provides a transaction. Here's some code from the **DFF** example, similar to +above with the scoreboard added. .. code-block:: python diff --git a/examples/axi_lite_slave/tests/test_axi_lite_slave.py b/examples/axi_lite_slave/tests/test_axi_lite_slave.py index 864bf6e2..cb57ed94 100644 --- a/examples/axi_lite_slave/tests/test_axi_lite_slave.py +++ b/examples/axi_lite_slave/tests/test_axi_lite_slave.py @@ -165,7 +165,7 @@ def write_fail(dut): yield axim.write(ADDRESS, DATA) yield Timer(CLK_PERIOD * 10) except AXIProtocolError as e: - print ("Exception: %s" % str(e)) + print("Exception: %s" % str(e)) dut._log.info("Bus Successfully Raised an Error") raise TestSuccess() raise TestFailure("AXI Bus Should have raised an ERROR when writing to \ @@ -199,7 +199,7 @@ def read_fail(dut): yield axim.read(ADDRESS, DATA) yield Timer(CLK_PERIOD * 10) except AXIProtocolError as e: - print ("Exception: %s" % str(e)) + print("Exception: %s" % str(e)) dut._log.info("Bus Successfully Raised an Error") raise TestSuccess() raise TestFailure("AXI Bus Should have raised an ERROR when writing to \ From de6b75a819919fd48798d8a3002827b172eec9ed Mon Sep 17 00:00:00 2001 From: Joseph Glover Date: Mon, 17 Dec 2018 12:43:54 -0600 Subject: [PATCH 1484/2656] Updated with suggested changes. Changed clock to create an iterable and loop over that to reduce the clocker to a single for loop instead of nested if/while. --- cocotb/clock.py | 36 +++++++++++++++++++----------------- cocotb/utils.py | 2 +- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/cocotb/clock.py b/cocotb/clock.py index 9c5791ea..d6aab0e7 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -29,6 +29,8 @@ A clock class """ import os +import itertools + if "SPHINX_BUILD" in os.environ: simulator = None else: @@ -62,9 +64,12 @@ class Clock(BaseClock): Args: signal (pin): The clock pin/signal to be driven. - period (int): The clock period. Should be an even number. + period (int): The clock period. Must convert to an even number of + timesteps. units (str, optional): One of (None,'fs','ps','ns','us','ms','sec'). - Defaults to ``None``. + When no units are given (``None``) the timestep is determined by + the simulator. E.g. Cadence simulators have a default timestep of + 'ps', so the clock will cycle every `period` picoseconds. """ def __init__(self, signal, period, units=None): BaseClock.__init__(self, signal) @@ -77,28 +82,25 @@ def __init__(self, signal, period, units=None): self.mcoro = None @cocotb.coroutine - def start(self, cycles=0): + def start(self, cycles=None): """Clocking coroutine. Start driving your clock by forking a call to this. Args: - cycles (int, optional): Cycle clock ``cycles`` number of times. - Defaults to 0, meaning will cycle forever. + cycles (int, optional): Cycle the clock `cycles` number of times. + Defaults to ``None`` (cycle the clock forever). """ t = Timer(self.half_period) - if cycles > 0: - while cycles: - self.signal <= 1 - yield t - self.signal <= 0 - yield t - cycles -= 1 + if cycles: + it = range(cycles) else: - while True: - self.signal <= 1 - yield t - self.signal <= 0 - yield t + it = itertools.count() + + for _ in it: + self.signal <= 1 + yield t + self.signal <= 0 + yield t def __str__(self): return self.__class__.__name__ + "(%3.1f MHz)" % self.frequency diff --git a/cocotb/utils.py b/cocotb/utils.py index 3a0284ec..c9532aff 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -109,7 +109,7 @@ def get_sim_steps(time, units=None): if err: raise ValueError("Unable to accurately represent {0}({1}) with the " "simulator precision of 1e{2}".format( - time,units,_LOG_SIM_PRECISION)) + time, units, _LOG_SIM_PRECISION)) return int(result) From 2b2a2e21450f960ef50197fb6966d1222a75e774 Mon Sep 17 00:00:00 2001 From: Joseph Glover Date: Tue, 18 Dec 2018 12:20:17 -0600 Subject: [PATCH 1485/2656] Updated clock to only cycle forever on None --- cocotb/clock.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cocotb/clock.py b/cocotb/clock.py index d6aab0e7..529dcfed 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -87,14 +87,15 @@ def start(self, cycles=None): this. Args: - cycles (int, optional): Cycle the clock `cycles` number of times. - Defaults to ``None`` (cycle the clock forever). + cycles (int, optional): Cycle the clock `cycles` number of times, + or if ``None`` then cycle the clock forever. Note: ``0`` is not + the same as ``None``, as 0 will cycle no times. """ t = Timer(self.half_period) - if cycles: - it = range(cycles) - else: + if cycles is None: it = itertools.count() + else: + it = range(cycles) for _ in it: self.signal <= 1 From dd27032c5244951837b45b07e078e89a69fa9723 Mon Sep 17 00:00:00 2001 From: Joseph Glover Date: Wed, 2 Jan 2019 09:51:42 -0600 Subject: [PATCH 1486/2656] Removed sentence specifying an example. --- cocotb/clock.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cocotb/clock.py b/cocotb/clock.py index 529dcfed..bdfc552c 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -68,8 +68,7 @@ class Clock(BaseClock): timesteps. units (str, optional): One of (None,'fs','ps','ns','us','ms','sec'). When no units are given (``None``) the timestep is determined by - the simulator. E.g. Cadence simulators have a default timestep of - 'ps', so the clock will cycle every `period` picoseconds. + the simulator. """ def __init__(self, signal, period, units=None): BaseClock.__init__(self, signal) From 6b6c62308588467bea5e3991a993dda5d6f7d751 Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Fri, 4 Jan 2019 20:49:05 +0000 Subject: [PATCH 1487/2656] Update combine_results.py to be more verbose And fix a couple whitespace issues. --- bin/combine_results.py | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/bin/combine_results.py b/bin/combine_results.py index fd8cf4b8..f3f9efd7 100755 --- a/bin/combine_results.py +++ b/bin/combine_results.py @@ -27,29 +27,29 @@ def get_parser(): help="Name of base directory to search from") parser.add_argument("--output_file", dest="output_file", type=str, required=False, - default="combined_results.xml", + default="combined_results.xml", help="Name of output file") parser.add_argument("--testsuites_name", dest="testsuites_name", type=str, required=False, - default="results", + default="results", help="Name value for testsuites tag") parser.add_argument("--verbose", dest="debug", action='store_const', required=False, - const=True, default=False, + const=True, default=False, help="Verbose/debug output") parser.add_argument("--suppress_rc", dest="set_rc", action='store_const', required=False, - const=False, default=True, + const=False, default=True, help="Suppress return code if failures found") - + return parser def main(): - + parser = get_parser() args = parser.parse_args() rc = 0; - + result = ET.Element("testsuites", name=args.testsuites_name); - + for fname in find_all("results.xml", args.directory): if args.debug : print("Reading file %s" % fname) tree = ET.parse(fname) @@ -66,20 +66,21 @@ def main(): else: #for tc in ts.getiterator("testcase"): use_element.extend(list(ts)); - + if args.debug : ET.dump(result) - - for testsuite in result.iter('testsuite'): - for testcase in testsuite.iter('testcase'): + + for testsuite_count, testsuite in enumerate(result.iter('testsuite'),1): + for testcase_count, testcase in enumerate(testsuite.iter('testcase'),1): for failure in testcase.iter('failure'): if args.set_rc: rc=1 - print("Failure in testsuite: '%s' testcase: '%s' with parameters '%s'" % (testsuite.get('name'), testcase.get('name'), testsuite.get('package'))) - - + print("Failure in testsuite: '%s' classname: '%s' testcase: '%s' with parameters '%s'" % (testsuite.get('name'), testcase.get('classname'), testcase.get('name'), testsuite.get('package'))) + + print("Ran a total of %d TestSuites and %d TestCases" % (testsuite_count, testcase_count)) + + ET.ElementTree(result).write(args.output_file, encoding="UTF-8") return rc - - + if __name__ == "__main__": rc = main() From 64670661f5a5fe50b980d9589f20521561be4a86 Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Fri, 4 Jan 2019 20:50:37 +0000 Subject: [PATCH 1488/2656] Aldec: Enable users to choose a license queue --- documentation/source/simulator_support.rst | 1 + makefiles/simulators/Makefile.aldec | 3 +++ 2 files changed, 4 insertions(+) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 88c3a384..34195c17 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -21,6 +21,7 @@ Synopsys VCS Aldec Riviera-PRO ----------------- +The `$LICENSE_QUEUE` environmental variable can be used for this simulator - this setting will be mirrored in the TCL `license_queue` variable to control runtime license checkouts. Mentor Questa ------------- diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index fd2bca4f..9e4835fa 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -98,6 +98,9 @@ $(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(VHDL_SOURCES) $(SIM_BUILD) @echo " puts [read [open sim.log r]]" >> $@ @echo " quit -code 1" >> $@ @echo "}" >> $@ + @echo "@if [string length [array get env LICENSE_QUEUE]] {" >> $@ + @echo " set LICENSE_QUEUE $$::env(LICENSE_QUEUE)" >> $@ + @echo "}" >> $@ @echo "alib $(RTL_LIBRARY)" >> $@ @echo "set worklib $(RTL_LIBRARY)" >> $@ ifneq ($(VHDL_SOURCES),) From 830fcc810ecb45ac0176949a6ad4c4bd65008dcf Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Fri, 4 Jan 2019 20:52:43 +0000 Subject: [PATCH 1489/2656] BinaryValue: Add a is_resolvable method This method tells the user if the value contains any X's. --- cocotb/binary.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cocotb/binary.py b/cocotb/binary.py index 35bab558..47a08cba 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -295,6 +295,11 @@ def get_value_signed(self): def set_value(self, integer): self._str = self._convert_to[self.binaryRepresentation](integer) + @property + def is_resolvable(self): + """Does the value contain any X's? Inquiring minds want to know""" + return not any(char in self._str for char in BinaryValue._resolve_to_error) + value = property(get_value, set_value, None, "Integer access to the value *** deprecated ***") integer = property(get_value, set_value, None, From 5c399d9a7a2fe092e8ee9a3fe92e4daa6b34afd9 Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Fri, 4 Jan 2019 20:54:06 +0000 Subject: [PATCH 1490/2656] Do not use colors in GUI mode --- cocotb/log.py | 2 +- cocotb/utils.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cocotb/log.py b/cocotb/log.py index 19aa45e5..d56d9964 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -54,7 +54,7 @@ class SimBaseLog(logging.getLoggerClass()): def __init__(self, name): hdlr = logging.StreamHandler(sys.stdout) - want_ansi = os.getenv("COCOTB_ANSI_OUTPUT") + want_ansi = os.getenv("COCOTB_ANSI_OUTPUT") and not os.getenv("GUI") if want_ansi is None: want_ansi = sys.stdout.isatty() # default to ANSI for TTYs else: diff --git a/cocotb/utils.py b/cocotb/utils.py index c9532aff..5d55b870 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -267,7 +267,8 @@ def sane(x): return r def highlight(string, colour=ANSI.COLOR_HILITE_HEXDIFF_DEFAULT): - want_ansi = os.getenv("COCOTB_ANSI_OUTPUT") + """Highlight only with ansi output if it's requested and we are not in a GUI""" + want_ansi = os.getenv("COCOTB_ANSI_OUTPUT") and not os.getenv("GUI") if want_ansi is None: want_ansi = sys.stdout.isatty() # default to ANSI for TTYs else: From 7b46fd5ccf4f7765b1b924f604f2625dfc07220e Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Fri, 4 Jan 2019 20:54:33 +0000 Subject: [PATCH 1491/2656] Icarus: Default to latest SystemVerilog standard --- makefiles/simulators/Makefile.icarus | 2 ++ 1 file changed, 2 insertions(+) diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index 9681cddd..26c711aa 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -54,6 +54,8 @@ endif BUILD_VPI=1 +COMPILE_ARGS += -g2012 # Default to latest SystemVerilog standard + # Compilation phase $(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CMD) -o $(SIM_BUILD)/sim.vvp -D COCOTB_SIM=1 -s $(TOPLEVEL) $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) From bd921d4ab2a06b07a31d4af4fa362db1975d6d11 Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Fri, 4 Jan 2019 20:55:33 +0000 Subject: [PATCH 1492/2656] Make version of simulator available in Python The version of the simulator is now available as cocotb.SIM_VERSION. --- lib/embed/gpi_embed.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/embed/gpi_embed.c b/lib/embed/gpi_embed.c index 8d60cbe1..cc3e6c6f 100644 --- a/lib/embed/gpi_embed.c +++ b/lib/embed/gpi_embed.c @@ -285,6 +285,12 @@ int embed_sim_init(gpi_sim_info_t *info) goto cleanup; } + if (-1 == PyObject_SetAttrString(cocotb_module, "SIM_VERSION", PyString_FromString(info->version))) { + PyErr_Print(); + fprintf(stderr, "Unable to set SIM_VERSION"); + goto cleanup; + } + // Set language in use as an attribute to cocotb module, or None if not provided const char *lang = getenv("TOPLEVEL_LANG"); PyObject* PyLang; From bf6cda70403a6f6302fcacc141cbb8799b1f022f Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Fri, 4 Jan 2019 21:00:27 +0000 Subject: [PATCH 1493/2656] IUS: Always wait for license --- makefiles/simulators/Makefile.ius | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 2c8d2920..a3b1a7b2 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -47,12 +47,13 @@ endif EXTRA_ARGS += $(COMPILE_ARGS) EXTRA_ARGS += $(SIM_ARGS) +EXTRA_ARGS += -licqueue ifneq ($(ARCH),i686) EXTRA_ARGS += -64 endif -EXTRA_ARGS += -nclibdirpath $(SIM_BUILD) +EXTRA_ARGS += -nclibdirpath $(SIM_BUILD) EXTRA_ARGS += -plinowarn ifeq ($(GUI),1) From 5f43d9c679d286fc1418f2aabfa607e37ffa492f Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Fri, 4 Jan 2019 21:01:13 +0000 Subject: [PATCH 1494/2656] Questa: Toplevel is expected to be fully qualified --- makefiles/simulators/Makefile.questa | 2 ++ 1 file changed, 2 insertions(+) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 00814d35..0e9d7865 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -45,6 +45,8 @@ endif RTL_LIBRARY ?= work +TOPLEVEL := "$(RTL_LIBRARY).$(TOPLEVEL)" + ifdef VLOG_ARGS VLOG_ARGS = $(VLOG_ARGS) else From 91b71450b4535762f04f97b99a2d3d8860e2c316 Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Fri, 4 Jan 2019 21:01:45 +0000 Subject: [PATCH 1495/2656] Questa: Pass include directories to simulator --- makefiles/simulators/Makefile.questa | 3 +++ 1 file changed, 3 insertions(+) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 0e9d7865..2911211d 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -52,6 +52,9 @@ VLOG_ARGS = $(VLOG_ARGS) else VLOG_ARGS = -timescale 1ns/100ps -mfcu +acc=rmb endif +ifdef VERILOG_INCLUDE_DIRS +VLOG_ARGS += +incdir+$(VERILOG_INCLUDE_DIRS) +endif # below allows for maintaining legacy syntax as well as enables using cross-simulator vars COMPILE_ARGS/SIM_ARGS VLOG_ARGS += $(COMPILE_ARGS) From 1199cbeacb1a23a84929c8b2c3cdf4d96964954f Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Fri, 4 Jan 2019 21:02:33 +0000 Subject: [PATCH 1496/2656] Aldec: Enable finer grained coverage selection --- makefiles/simulators/Makefile.aldec | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 9e4835fa..6aa6e3b7 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -62,9 +62,19 @@ RTL_LIBRARY ?= work ALOG_ARGS += +define+COCOTB_SIM -dbg -pli libgpi ACOM_ARGS += -dbg +# Aldec-specific coverage types: +# - (s)tatement +# - (b)ranch +# - (e)xpression +# - (c)ondition +# - (a)ssertion +# - (p)ath +# - finite state (m)achine +# Documentation: Riviera Pro 2017.02 Documentation - Page 359 +COVERAGE_TYPES ?= sb ifeq ($(COVERAGE),1) - ASIM_ARGS += -acdb - ALOG_ARGS += -coverage sb + ASIM_ARGS += -acdb -acdb_cov $(COVERAGE_TYPES) + ALOG_ARGS += -coverage $(COVERAGE_TYPES) endif GPI_EXTRA:= @@ -121,6 +131,10 @@ ifeq ($(GUI),1) else @echo "run -all" >> $@ @echo "endsim" >> $@ +ifeq ($(COVERAGE),1) + @echo "acdb report -cov $(COVERAGE_TYPES) -db $(RTL_LIBRARY).acdb -html -o coverage/acdb_report.html" >> $@ + @echo "acdb report -cov $(COVERAGE_TYPES) -db $(RTL_LIBRARY).acdb -txt -o coverage/acdb_report.txt" >> $@ +endif endif ifeq ($(OS),Msys) From d89bdd46f1203f048ce37df317284fa557b1d6f3 Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Fri, 4 Jan 2019 21:04:24 +0000 Subject: [PATCH 1497/2656] Aldec: Prefer CFG_TOPLEVEL over TOPLEVEL if set --- makefiles/simulators/Makefile.aldec | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 6aa6e3b7..6fa179ab 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -122,7 +122,11 @@ endif ifdef SCRIPT_FILE @echo "do $(SCRIPT_FILE)" >> $@ endif - @echo "asim $(ASIM_ARGS) +access +w -interceptcoutput -O2 -dbg $(GPI_ARGS) $(TOPLEVEL)" >> $@ +ifneq ($(CFG_TOPLEVEL),) + @echo "asim $(ASIM_ARGS) +access +w -interceptcoutput -O2 -dbg $(GPI_ARGS) $(CFG_TOPLEVEL) $(EXTRA_TOPS)" >> $@ +else + @echo "asim $(ASIM_ARGS) +access +w -interceptcoutput -O2 -dbg $(GPI_ARGS) $(TOPLEVEL) $(EXTRA_TOPS)" >> $@ +endif ifeq ($(WAVES),1) @echo "log -recursive *" >> $@ endif From 5d959f5027fa8e2eea541aea4d0148c0997a6b1c Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Fri, 4 Jan 2019 21:14:55 +0000 Subject: [PATCH 1498/2656] AMBA Driver: Add sync option to write --- cocotb/drivers/amba.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cocotb/drivers/amba.py b/cocotb/drivers/amba.py index 913522d3..b074aa64 100644 --- a/cocotb/drivers/amba.py +++ b/cocotb/drivers/amba.py @@ -112,12 +112,16 @@ def _send_write_data(self, data, delay=0, byte_enable=0xF): @cocotb.coroutine def write(self, address, value, byte_enable=0xf, address_latency=0, - data_latency=0): + data_latency=0, sync=True): """ Write a value to an address. The *_latency KWargs allow control over the delta + + sync dictates whether we wait for a clock edge or not """ + if sync: + yield RisingEdge(self.clock) c_addr = cocotb.fork(self._send_write_address(address, delay=address_latency)) From 591d032fe58e3cd5b86af5afae67c485f807b0c6 Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Fri, 4 Jan 2019 21:15:29 +0000 Subject: [PATCH 1499/2656] AXI4LiteMaster: Add __len__ --- cocotb/drivers/amba.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cocotb/drivers/amba.py b/cocotb/drivers/amba.py index b074aa64..295d21b7 100644 --- a/cocotb/drivers/amba.py +++ b/cocotb/drivers/amba.py @@ -184,6 +184,8 @@ def read(self, address, sync=True): raise ReturnValue(data) + def __len__(self): + return 2**len(self.bus.ARADDR) class AXI4Slave(BusDriver): ''' From 89d376d96eeba684e2f8c18eb650caffa132bbf6 Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Fri, 4 Jan 2019 21:17:10 +0000 Subject: [PATCH 1500/2656] Bus: Reduce code duplication --- cocotb/bus.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/cocotb/bus.py b/cocotb/bus.py index 2e7f765b..c95e1d6f 100644 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -93,9 +93,7 @@ def __init__(self, entity, name, signals, optional_signals=[], bus_separator="_" if array_idx is not None: signame += "[{:d}]".format(array_idx) - self._entity._log.debug("Signal name {}".format(signame)) - setattr(self, attr_name, getattr(entity, signame)) - self._signals[attr_name] = getattr(self, attr_name) + self._add_signal(attr_name, signame) # Also support a set of optional signals that don't have to be present for attr_name, sig_name in _build_sig_attr_dict(optional_signals).items(): @@ -111,12 +109,16 @@ def __init__(self, entity, name, signals, optional_signals=[], bus_separator="_" # Attempts to access a signal that doesn't exist will print a # backtrace so we 'peek' first, slightly un-pythonic if entity.__hasattr__(signame): - setattr(self, attr_name, getattr(entity, signame)) - self._signals[attr_name] = getattr(self, attr_name) + self._add_signal(attr_name, signame) else: self._entity._log.debug("Ignoring optional missing signal " "%s on bus %s" % (sig_name, name)) + def _add_signal(self, attr_name, signame): + self._entity._log.debug("Signal name {}".format(signame)) + setattr(self, attr_name, getattr(self._entity, signame)) + self._signals[attr_name] = getattr(self, attr_name) + def drive(self, obj, strict=False): """ Drives values onto the bus. From ff64145ffb025c1f49dbf6f15bbe9cc0883b52bc Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Fri, 4 Jan 2019 21:17:36 +0000 Subject: [PATCH 1501/2656] Regression: Improve error handling --- cocotb/regression.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index bb46265d..8da92360 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -140,9 +140,13 @@ def initialise(self): # Specific functions specified, don't auto discover for test in self._functions.rsplit(','): if not hasattr(module, test): + self.log.error("Requested test %s wasn't found in module %s", test, module_name) raise AttributeError("Test %s doesn't exist in %s" % (test, module_name)) - + _test = getattr(module, test) + if not hasattr(_test, "im_test"): + self.log.error("Requested %s from module %s isn't a cocotb.test decorated coroutine", test, module_name) + raise ImportError("Failed to find requested test %s" % test) self._queue.append(getattr(module, test)(self._dut)) self.ntests += 1 break From 03d2368b4e1228b8e0a6e253cb690c67ea01b67a Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Fri, 4 Jan 2019 21:23:19 +0000 Subject: [PATCH 1502/2656] Add functools wrappers to more decorators These wrappers enable decorated functions to be documented. --- cocotb/decorators.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 43a0761c..6efd88e8 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -247,8 +247,7 @@ class coroutine(object): def __init__(self, func): self._func = func self.log = SimLog("cocotb.function.%s" % self._func.__name__, id(self)) - self.__name__ = self._func.__name__ - functools.update_wrapper(self, func) + functools.update_wrapper(self, self._func) def __call__(self, *args, **kwargs): try: @@ -289,6 +288,7 @@ class function(object): def __init__(self, func): self._func = func self.log = SimLog("cocotb.function.%s" % self._func.__name__, id(self)) + functools.update_wrapper(self, self._func) def __call__(self, *args, **kwargs): @@ -319,6 +319,7 @@ class external(object): def __init__(self, func): self._func = func self._log = SimLog("cocotb.external.%s" % self._func.__name__, id(self)) + functools.update_wrapper(self, self._func) def __call__(self, *args, **kwargs): From c1bc813eee2b3486ccadf6cf368cdde571f23233 Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Fri, 4 Jan 2019 21:33:02 +0000 Subject: [PATCH 1503/2656] Changes to tests Modified verilog model and assocated python for array test. Change to simulator check logic in iteration test. --- tests/designs/array_module/array_module.v | 5 ++ tests/test_cases/test_array/test_array.py | 66 ++++++++++++++----- .../test_iteration_vhdl/test_iteration.py | 3 +- 3 files changed, 55 insertions(+), 19 deletions(-) diff --git a/tests/designs/array_module/array_module.v b/tests/designs/array_module/array_module.v index c3c70028..e222e89a 100644 --- a/tests/designs/array_module/array_module.v +++ b/tests/designs/array_module/array_module.v @@ -98,6 +98,11 @@ wire logic [7:0] sig_logic_vec; rec_type sig_rec; rec_type sig_cmplx [0:1]; +typedef logic [7:0] uint16_t; + +uint16_t sig_t7 [3:0][3:0]; +uint16_t [3:0][3:0] sig_t8; + assign port_ofst_out = port_ofst_in; //assign port_rec_out = (select_in == 1) ? const_rec : (select_in == 2) ? sig_rec : param_rec; diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py index fb55cfdb..0eb412b9 100644 --- a/tests/test_cases/test_array/test_array.py +++ b/tests/test_cases/test_array/test_array.py @@ -153,7 +153,9 @@ def test_read_write(dut): tlog.info("Writing a few signal sub-indices!!!") dut.sig_logic_vec[2] = 0 - if cocotb.LANGUAGE in ["vhdl"] or not cocotb.SIM_NAME.lower().startswith(("ncsim")): + if cocotb.LANGUAGE in ["vhdl"] or not (cocotb.SIM_NAME.lower().startswith(("ncsim")) or + (cocotb.SIM_NAME.lower().startswith(("riviera")) and + cocotb.SIM_VERSION.startswith(("2016.06","2016.10","2017.02")))): dut.sig_t6[1][3][2] = 1 dut.sig_t6[0][2][7] = 0 @@ -166,7 +168,9 @@ def test_read_write(dut): tlog.info("Checking writes (2):") _check_logic(tlog, dut.port_logic_vec_out, 0xC8) - if cocotb.LANGUAGE in ["vhdl"] or not cocotb.SIM_NAME.lower().startswith(("ncsim")): + if cocotb.LANGUAGE in ["vhdl"] or not (cocotb.SIM_NAME.lower().startswith(("ncsim")) or + (cocotb.SIM_NAME.lower().startswith(("riviera")) and + cocotb.SIM_VERSION.startswith(("2016.06","2016.10","2017.02")))): _check_logic(tlog, dut.sig_t6[1][3][2], 1) _check_logic(tlog, dut.sig_t6[0][2][7], 0) @@ -259,6 +263,8 @@ def test_discover_all(dut): 149 (sig_t4[0:3][7:4][7:0]) 112 (sig_t5[0:2][0:3][7:0]) 57 (sig_t6[0:1][2:4][7:0]) + 149 (sig_t7[3:0][3:0]) + 149 ([3:0][3:0]sig_t8) 1 (sig_logic) 9 (sig_logic_vec) 1 (sig_bool) (VHDL Only) @@ -267,7 +273,7 @@ def test_discover_all(dut): 1 (sig_char) (VHDL Only) 9 (sig_str) (VHDL Only) 30 (sig_rec.a, sig_rec.b[0:2][7:0]) (VPI doesn't find, added manually, except for Aldec) - 61 (sig_cmplx[0:1].a, sig_cmplx[0:1].b[0:2][7:0]) (VPI - Aldec doesn't find) + 61 (sig_cmplx[0:1].a, sig_cmplx[0:1].b[0:2][7:0]) (VPI - Aldec older than 2017.10.67 doesn't find) regions: 9 (asc_gen[16:23]) 8 (asc_gen: signals) (VHPI - Riviera doesn't find, added manually) 8 (asc_gen: constant) @@ -280,10 +286,10 @@ def test_discover_all(dut): 8 (desc_gen: process "always") (VPI - Aldec only) process: 1 ("always") (VPI - Aldec only) - TOTAL: 856 (VHDL - Default) - 818 (VHDL - Aldec) - 780 (Verilog - Default) - 649 (Verilog - Aldec) + TOTAL: 1154 (VHDL - Default) + 818 (VHDL - Aldec) + 1078 (Verilog - Default) + 947/1038 (Verilog - Aldec) """ tlog = logging.getLogger("cocotb.test") @@ -293,7 +299,8 @@ def test_discover_all(dut): # Need to clear sub_handles so won't attempt to iterate over handles like sig_rec and sig_rec_array # # DO NOT REMOVE. Aldec cannot iterate over the complex records due to bugs in the VPI interface. - if cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")): + if (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")) and + cocotb.SIM_VERSION.startswith(("2016.02"))) : if len(dut._sub_handles) != 0: dut._sub_handles = {} @@ -313,13 +320,20 @@ def test_discover_all(dut): dummy = hdl.sig if cocotb.LANGUAGE in ["vhdl"] and cocotb.SIM_NAME.lower().startswith(("riviera")): - pass_total = 818 + pass_total = 1116 elif cocotb.LANGUAGE in ["vhdl"]: - pass_total = 856 + pass_total = 1154 elif cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")): - pass_total = 649 + if cocotb.SIM_VERSION.startswith(("2017.10.61")): + pass_total = 803 + elif cocotb.SIM_VERSION.startswith(("2016.06","2016.10","2017.02")): + pass_total = 813 + elif cocotb.SIM_VERSION.startswith(("2016.02")): + pass_total = 947 + else : + pass_total = 1038 else: - pass_total = 780 + pass_total = 1078 def _discover(obj,indent): count = 0 @@ -417,20 +431,34 @@ def test_direct_signal_indexing(dut): _check_type(tlog, dut.sig_t3a, NonHierarchyIndexableObject) _check_type(tlog, dut.sig_t4, NonHierarchyIndexableObject) _check_type(tlog, dut.sig_t4[3], NonHierarchyIndexableObject) - _check_type(tlog, dut.sig_t4[3][4], ModifiableObject) - _check_type(tlog, dut.sig_t4[3][4][1], ModifiableObject) + # the following version cannot index into those arrays and will error out + if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")) and + cocotb.SIM_VERSION.startswith(("2016.06","2016.10","2017.02"))) : + _check_type(tlog, dut.sig_t4[3][4], ModifiableObject) + _check_type(tlog, dut.sig_t4[3][4][1], ModifiableObject) _check_type(tlog, dut.sig_t5, NonHierarchyIndexableObject) _check_type(tlog, dut.sig_t5[1], NonHierarchyIndexableObject) _check_type(tlog, dut.sig_t5[1][0], ModifiableObject) _check_type(tlog, dut.sig_t5[1][0][6], ModifiableObject) _check_type(tlog, dut.sig_t6, NonHierarchyIndexableObject) _check_type(tlog, dut.sig_t6[1], NonHierarchyIndexableObject) - _check_type(tlog, dut.sig_t6[0][3], ModifiableObject) - _check_type(tlog, dut.sig_t6[0][3][7], ModifiableObject) + # the following version cannot index into those arrays and will error out + if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")) and + cocotb.SIM_VERSION.startswith(("2016.06","2016.10","2017.02"))) : + _check_type(tlog, dut.sig_t6[0][3], ModifiableObject) + _check_type(tlog, dut.sig_t6[0][3][7], ModifiableObject) _check_type(tlog, dut.sig_cmplx, NonHierarchyIndexableObject) + _check_type(tlog, dut.sig_t7[1], NonHierarchyIndexableObject) + _check_type(tlog, dut.sig_t7[0][3], ModifiableObject) + _check_type(tlog, dut.sig_t8[1], NonHierarchyIndexableObject) + _check_type(tlog, dut.sig_t8[0][3], ModifiableObject) + # Riviera has a bug and finds dut.sig_cmplx[1], but the type returned is a vpiBitVar - if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera"))): + # only true for version 2016.02 + if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")) and + cocotb.SIM_VERSION.startswith(("2016.02"))) : + _check_type(tlog, dut.sig_cmplx[1], HierarchyObject) _check_type(tlog, dut.sig_cmplx[1].a, ModifiableObject) _check_type(tlog, dut.sig_cmplx[1].b, NonHierarchyIndexableObject) @@ -442,7 +470,9 @@ def test_direct_signal_indexing(dut): _check_type(tlog, dut.sig_rec.b, NonHierarchyIndexableObject) # Riviera has a bug and finds dut.sig_rec.b[1], but the type returned is 0 which is unknown - if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera"))): + # only true for version 2016.02 + if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")) and + cocotb.SIM_VERSION.startswith(("2016.02"))) : _check_type(tlog, dut.sig_rec.b[1], ModifiableObject) _check_type(tlog, dut.sig_rec.b[1][2], ModifiableObject) diff --git a/tests/test_cases/test_iteration_vhdl/test_iteration.py b/tests/test_cases/test_iteration_vhdl/test_iteration.py index 8e8a58ed..a2e58823 100644 --- a/tests/test_cases/test_iteration_vhdl/test_iteration.py +++ b/tests/test_cases/test_iteration_vhdl/test_iteration.py @@ -34,7 +34,8 @@ def recursive_discovery(dut): """ Recursively discover every single object in the design """ - if cocotb.SIM_NAME.lower().startswith(("ncsim","modelsim")): + if (cocotb.SIM_NAME.lower().startswith(("ncsim","modelsim")) or + (cocotb.SIM_NAME.lower().startswith("riviera") and not cocotb.SIM_VERSION.startswith("2016.02"))): # Finds regions, signal, generics, constants, varibles and ports. pass_total = 34569 else: From c500f0cf5d90b98545648652a54bc823ed779f94 Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Fri, 4 Jan 2019 21:41:41 +0000 Subject: [PATCH 1504/2656] Test for issue 142: Resolve X to Zeros --- tests/test_cases/issue_142/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_cases/issue_142/Makefile b/tests/test_cases/issue_142/Makefile index ceb5b7b8..7338c712 100644 --- a/tests/test_cases/issue_142/Makefile +++ b/tests/test_cases/issue_142/Makefile @@ -29,3 +29,5 @@ include ../../designs/sample_module/Makefile MODULE = issue_142 + +export COCOTB_RESOLVE_X=ZEROS From 93049cdb8a04b8c6336445a3d25ce4d3ae7ed78c Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Fri, 4 Jan 2019 21:45:33 +0000 Subject: [PATCH 1505/2656] VPI: Improve logging --- lib/vpi/VpiImpl.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 3e36c50f..9845e00a 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -169,6 +169,8 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, break; case vpiStructVar: case vpiStructNet: + new_obj = new VpiObjHdl(this, new_hdl, to_gpi_objtype(type)); + break; case vpiModule: case vpiInterface: case vpiModport: @@ -184,7 +186,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string hdl_name = vpi_get_str(vpiName, new_hdl); if (hdl_name != name) { - LOG_DEBUG("Found pseudo-region %s", fq_name.c_str()); + LOG_DEBUG("Found pseudo-region %s (hdl_name=%s but name=%s)", fq_name.c_str(), hdl_name.c_str(), name.c_str()); new_obj = new VpiObjHdl(this, new_hdl, GPI_GENARRAY); } else { new_obj = new VpiObjHdl(this, new_hdl, to_gpi_objtype(type)); @@ -643,7 +645,7 @@ static int system_function_overload(char *userdata) msg = argval.value.str; } - gpi_log("simulator", *userdata, vpi_get_str(vpiFile, systfref), "", (long)vpi_get(vpiLineNo, systfref), msg ); + gpi_log("simulator", *userdata, vpi_get_str(vpiFile, systfref), "", (long)vpi_get(vpiLineNo, systfref), "%s", msg ); // Fail the test for critical errors if (GPICritical == *userdata) From a61c371be6608f111c10a9550b8a274b02bb80a2 Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Fri, 4 Jan 2019 21:49:22 +0000 Subject: [PATCH 1506/2656] Various Avalon bus driver/monitor improvements - Implement Avalon Streaming - Implement multiple channels --- cocotb/drivers/__init__.py | 14 +- cocotb/drivers/avalon.py | 183 +++++++++++++++--- cocotb/monitors/__init__.py | 4 +- cocotb/monitors/avalon.py | 96 ++++++++- .../designs/avalon_streaming_module/Makefile | 26 +++ .../avalon_streaming.sv | 51 +++++ tests/test_cases/test_avalon_stream/Makefile | 3 + .../test_avalon_stream/test_avalon_stream.py | 67 +++++++ 8 files changed, 406 insertions(+), 38 deletions(-) create mode 100644 tests/designs/avalon_streaming_module/Makefile create mode 100644 tests/designs/avalon_streaming_module/avalon_streaming.sv create mode 100644 tests/test_cases/test_avalon_stream/Makefile create mode 100644 tests/test_cases/test_avalon_stream/test_avalon_stream.py diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index d67375fc..a8f7d77f 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -218,10 +218,13 @@ def __init__(self, entity, name, clock, **kwargs): self.log = SimLog("cocotb.%s.%s" % (entity._name, name)) Driver.__init__(self) self.entity = entity - self.name = name self.clock = clock - self.bus = Bus(self.entity, self.name, self._signals, - self._optional_signals, array_idx=kwargs.get("array_idx")) + index = kwargs.get("array_idx") + self.bus = Bus(self.entity, name, self._signals, + self._optional_signals, array_idx=index) + + # Give this instance a unique name + self.name = name if index is None else "%s_%d" % (name, index) @coroutine def _driver_send(self, transaction, sync=True): @@ -266,7 +269,7 @@ class ValidatedBusDriver(BusDriver): which cycles are valid """ - def __init__(self, entity, name, clock, valid_generator=None): + def __init__(self, entity, name, clock, **kwargs): """ Args: entity (SimHandle) : a handle to the simulator entity @@ -278,7 +281,8 @@ def __init__(self, entity, name, clock, valid_generator=None): valid_generator (generator): a generator that yields tuples of (valid, invalid) cycles to insert """ - BusDriver.__init__(self, entity, name, clock) + valid_generator = kwargs.pop("valid_generator", None) + BusDriver.__init__(self, entity, name, clock, **kwargs) self.set_valid_generator(valid_generator=valid_generator) def _next_valids(self): diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index dbc1c2b2..88f6d300 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -35,8 +35,7 @@ import cocotb from cocotb.decorators import coroutine -from cocotb.triggers import RisingEdge, FallingEdge -from cocotb.triggers import ReadOnly, NextTimeStep, Event +from cocotb.triggers import RisingEdge, FallingEdge, ReadOnly, NextTimeStep, Event from cocotb.drivers import BusDriver, ValidatedBusDriver from cocotb.utils import hexdump from cocotb.binary import BinaryValue @@ -59,8 +58,8 @@ class AvalonMM(BusDriver): "cs"] - def __init__(self, entity, name, clock): - BusDriver.__init__(self, entity, name, clock) + def __init__(self, entity, name, clock, **kwargs): + BusDriver.__init__(self, entity, name, clock, **kwargs) self._can_read = False self._can_write = False @@ -96,8 +95,8 @@ def write(self, address, value): class AvalonMaster(AvalonMM): """Avalon-MM master """ - def __init__(self, entity, name, clock): - AvalonMM.__init__(self, entity, name, clock) + def __init__(self, entity, name, clock, **kwargs): + AvalonMM.__init__(self, entity, name, clock, **kwargs) self.log.debug("AvalonMaster created") self.busy_event = Event("%s_busy" % name) self.busy = False @@ -498,11 +497,94 @@ def _respond(self): class AvalonST(ValidatedBusDriver): _signals = ["valid", "data"] + _optional_signals = ["ready"] + + _default_config = { + "firstSymbolInHighOrderBits" : True + } + + def __init__(self, *args, **kwargs): + config = kwargs.pop('config', {}) + ValidatedBusDriver.__init__(self, *args, **kwargs) + + self.config = AvalonST._default_config.copy() + + for configoption, value in config.items(): + self.config[configoption] = value + self.log.debug("Setting config option %s to %s" % (configoption, str(value))) + + word = BinaryValue(bits=len(self.bus.data), bigEndian=self.config['firstSymbolInHighOrderBits']) + + self.bus.valid <= 0 + self.bus.data <= word + + @coroutine + def _wait_ready(self): + """Wait for a ready cycle on the bus before continuing + + Can no longer drive values this cycle... + + FIXME assumes readyLatency of 0 + """ + yield ReadOnly() + while not self.bus.ready.value: + yield RisingEdge(self.clock) + yield ReadOnly() + + @coroutine + def _driver_send(self, value, sync=True): + """Send a transmission over the bus + + Args: + value: data to drive onto the bus + """ + self.log.debug("Sending avalon transmission: %d" % value) + + # Avoid spurious object creation by recycling + clkedge = RisingEdge(self.clock) + + word = BinaryValue(bits=len(self.bus.data), bigEndian=False) + + # Drive some defaults since we don't know what state we're in + self.bus.valid <= 0 + + if sync: + yield clkedge + + # Insert a gap where valid is low + if not self.on: + self.bus.valid <= 0 + for i in range(self.off): + yield clkedge + + # Grab the next set of on/off values + self._next_valids() + + # Consume a valid cycle + if self.on is not True and self.on: + self.on -= 1 + + self.bus.valid <= 1 + + word.assign(value) + self.bus.data <= word + + # If this is a bus with a ready signal, wait for this word to + # be acknowledged + if hasattr(self.bus, "ready"): + yield self._wait_ready() + + yield clkedge + self.bus.valid <= 0 + word.binstr = ("x"*len(self.bus.data)) + self.bus.data <= word + + self.log.debug("Successfully sent avalon transmission: %d" % value) class AvalonSTPkts(ValidatedBusDriver): - _signals = ["valid", "data", "startofpacket", "endofpacket", "empty"] - _optional_signals = ["error", "channel", "ready"] + _signals = ["valid", "data", "startofpacket", "endofpacket"] + _optional_signals = ["error", "channel", "ready", "empty"] _default_config = { "dataBitsPerSymbol" : 8, @@ -517,27 +599,58 @@ def __init__(self, *args, **kwargs): self.config = AvalonSTPkts._default_config.copy() + # Set default config maxChannel to max value on channel bus + if hasattr(self.bus, 'channel'): + self.config['maxChannel'] = (2 ** len(self.bus.channel)) -1 + for configoption, value in config.items(): self.config[configoption] = value self.log.debug("Setting config option %s to %s" % (configoption, str(value))) + num_data_symbols = (len(self.bus.data) / + self.config["dataBitsPerSymbol"]) + if (num_data_symbols > 1 and not hasattr(self.bus, 'empty')): + raise AttributeError( + "%s has %i data symbols, but contains no object named empty" % + (self.name, num_data_symbols)) + + self.use_empty = (num_data_symbols > 1) + self.config["useEmpty"] = self.use_empty + word = BinaryValue(bits=len(self.bus.data), bigEndian=self.config['firstSymbolInHighOrderBits']) - empty = BinaryValue(bits=len(self.bus.empty), bigEndian=False) single = BinaryValue(bits=1, bigEndian=False) word.binstr = ("x"*len(self.bus.data)) - empty.binstr = ("x"*len(self.bus.empty)) single.binstr = ("x") self.bus.valid <= 0 self.bus.data <= word - self.bus.empty <= empty self.bus.startofpacket <= single self.bus.endofpacket <= single + if self.use_empty: + empty = BinaryValue(bits=len(self.bus.empty), bigEndian=False) + empty.binstr = ("x"*len(self.bus.empty)) + self.bus.empty <= empty + + if hasattr(self.bus, 'channel'): + if len(self.bus.channel) > 128: + raise AttributeError( + "AvalonST interface specification defines channel width as 1-128. %d channel width is %d" % + (self.name, len(self.bus.channel)) + ) + maxChannel = (2 ** len(self.bus.channel)) -1 + if self.config['maxChannel'] > maxChannel: + raise AttributeError( + "%s has maxChannel=%d, but can only support a maximum channel of (2**channel_width)-1=%d, channel_width=%d" % + (self.name,self.config['maxChannel'],maxChannel,len(self.bus.channel))) + channel = BinaryValue(bits=len(self.bus.channel), bigEndian=False) + channel.binstr = ("x"*len(self.bus.channel)) + self.bus.channel <= channel + @coroutine def _wait_ready(self): """Wait for a ready cycle on the bus before continuing @@ -552,10 +665,11 @@ def _wait_ready(self): yield ReadOnly() @coroutine - def _send_string(self, string, sync=True): + def _send_string(self, string, sync=True, channel=None): """ Args: string (str): A string of bytes to send over the bus + channel (int): Channel to send the data on """ # Avoid spurious object creation by recycling clkedge = RisingEdge(self.clock) @@ -567,19 +681,24 @@ def _send_string(self, string, sync=True): word = BinaryValue(bits=len(self.bus.data), bigEndian=self.config['firstSymbolInHighOrderBits']) - empty = BinaryValue(bits=len(self.bus.empty), bigEndian=False) single = BinaryValue(bits=1, bigEndian=False) - + if self.use_empty: + empty = BinaryValue(bits=len(self.bus.empty), bigEndian=False) # Drive some defaults since we don't know what state we're in - # self.bus.empty <= 0 - yield NextTimeStep() + if self.use_empty: + self.bus.empty <= 0 self.bus.startofpacket <= 0 self.bus.endofpacket <= 0 self.bus.valid <= 0 if hasattr(self.bus, 'error'): self.bus.error <= 0 + if hasattr(self.bus, 'channel'): + self.bus.channel <= 0 + elif channel is not None: + raise TestError("%s does not have a channel signal" % self.name) + while string: if not firstword or (firstword and sync): yield clkedge @@ -598,9 +717,17 @@ def _send_string(self, string, sync=True): self.on -= 1 self.bus.valid <= 1 + if hasattr(self.bus, 'channel'): + if channel is None: + self.bus.channel <= 0 + elif channel > self.config['maxChannel'] or channel < 0: + raise TestError( + "%s: Channel value %d is outside range 0-%d" % + (self.name,channel,self.config['maxChannel'])) + else: + self.bus.channel <= channel if firstword: - #self.bus.empty <= 0 self.bus.startofpacket <= 1 firstword = False else: @@ -612,7 +739,8 @@ def _send_string(self, string, sync=True): if len(string) <= bus_width: self.bus.endofpacket <= 1 - self.bus.empty <= bus_width - len(string) + if self.use_empty: + self.bus.empty <= bus_width - len(string) string = "" else: string = string[bus_width:] @@ -628,13 +756,19 @@ def _send_string(self, string, sync=True): self.bus.valid <= 0 self.bus.endofpacket <= 0 word.binstr = ("x"*len(self.bus.data)) - empty.binstr = ("x"*len(self.bus.empty)) single.binstr = ("x") self.bus.data <= word - self.bus.empty <= empty self.bus.startofpacket <= single self.bus.endofpacket <= single + if self.use_empty: + empty.binstr = ("x"*len(self.bus.empty)) + self.bus.empty <= empty + if hasattr(self.bus, 'channel'): + channel_value = BinaryValue(n_bits=len(self.bus.channel), bigEndian=False) + channel_value.binstr = ("x"*len(self.bus.channel)) + self.bus.channel <= channel_value + @coroutine def _send_iterable(self, pkt, sync=True): """ @@ -678,11 +812,12 @@ def _send_iterable(self, pkt, sync=True): self.bus.valid <= 0 @coroutine - def _driver_send(self, pkt, sync=True): + def _driver_send(self, pkt, sync=True, channel=None): """Send a packet over the bus Args: pkt (str or iterable): packet to drive onto the bus + channel (None or int): channel attributed to the packet If pkt is a string, we simply send it word by word @@ -696,7 +831,9 @@ def _driver_send(self, pkt, sync=True): if isinstance(pkt, str): self.log.debug("Sending packet of length %d bytes" % len(pkt)) self.log.debug(hexdump(pkt)) - yield self._send_string(pkt, sync=sync) - self.log.info("Sucessfully sent packet of length %d bytes" % len(pkt)) + yield self._send_string(pkt, sync=sync, channel=channel) + self.log.debug("Successfully sent packet of length %d bytes" % len(pkt)) else: + if channel is not None: + self.log.warning("%s is ignoring channel=%d because pkt is an iterable" % (self.name, channel)) yield self._send_iterable(pkt, sync=sync) diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index 22ff80a4..aec58c19 100644 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -162,14 +162,14 @@ class BusMonitor(Monitor): _optional_signals = [] def __init__(self, entity, name, clock, reset=None, reset_n=None, - callback=None, event=None, bus_separator="_"): + callback=None, event=None, bus_separator="_", array_idx=None): self.log = SimLog("cocotb.%s.%s" % (entity._name, name)) self.entity = entity self.name = name self.clock = clock self.bus = Bus(self.entity, self.name, self._signals, optional_signals=self._optional_signals, - bus_separator=bus_separator) + bus_separator=bus_separator,array_idx=array_idx) self._reset = reset self._reset_n = reset_n Monitor.__init__(self, callback=callback, event=event) diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index b00f00b5..a60541ee 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -35,7 +35,7 @@ from cocotb.decorators import coroutine from cocotb.monitors import BusMonitor from cocotb.triggers import RisingEdge, ReadOnly - +from cocotb.binary import BinaryValue class AvalonProtocolError(Exception): pass @@ -48,6 +48,21 @@ class AvalonST(BusMonitor): Non-packetised so each valid word is a separate transaction """ _signals = ["valid", "data"] + _optional_signals = ["ready"] + + _default_config = { + "firstSymbolInHighOrderBits" : True + } + + def __init__(self, *args, **kwargs): + config = kwargs.pop('config', {}) + BusMonitor.__init__(self, *args, **kwargs) + + self.config = self._default_config.copy() + + for configoption, value in config.items(): + self.config[configoption] = value + self.log.debug("Setting config option %s to %s" % (configoption, str(value))) @coroutine def _monitor_recv(self): @@ -57,12 +72,18 @@ def _monitor_recv(self): clkedge = RisingEdge(self.clock) rdonly = ReadOnly() + def valid(): + if hasattr(self.bus, "ready"): + return self.bus.valid.value and self.bus.ready.value + return self.bus.valid.value + # NB could yield on valid here more efficiently? while True: yield clkedge yield rdonly - if self.bus.valid.value: + if valid(): vec = self.bus.data.value + vec.big_endian = self.config["firstSymbolInHighOrderBits"] self._recv(vec.buff) @@ -87,6 +108,10 @@ def __init__(self, *args, **kwargs): self.config = self._default_config.copy() + # Set default config maxChannel to max value on channel bus + if hasattr(self.bus, 'channel'): + self.config['maxChannel'] = (2 ** len(self.bus.channel)) -1 + for configoption, value in config.items(): self.config[configoption] = value self.log.debug("Setting config option %s to %s" % @@ -101,6 +126,21 @@ def __init__(self, *args, **kwargs): self.config["useEmpty"] = (num_data_symbols > 1) + if hasattr(self.bus, 'channel'): + if "channel" in self._optional_signals: + self.log.warning("Channel is not fully implemented in this monitor. Recommend use of AvalonSTPktsWithChannel.") + + if len(self.bus.channel) > 128: + raise AttributeError( + "AvalonST interface specification defines channel width as 1-128. %d channel width is %d" % + (self.name, len(self.bus.channel)) + ) + maxChannel = (2 ** len(self.bus.channel)) -1 + if self.config['maxChannel'] > maxChannel: + raise AttributeError( + "%s has maxChannel=%d, but can only support a maximum channel of (2**channel_width)-1=%d, channel_width=%d" % + (self.name,self.config['maxChannel'],maxChannel,len(self.bus.channel))) + @coroutine def _monitor_recv(self): """Watch the pins and reconstruct transactions""" @@ -111,6 +151,7 @@ def _monitor_recv(self): pkt = "" in_pkt = False invalid_cyclecount = 0 + channel = None def valid(): if hasattr(self.bus, 'ready'): @@ -139,21 +180,42 @@ def valid(): raise AvalonProtocolError("Data transfer outside of " "packet") - vec = self.bus.data.value + #Handle empty and X's in empty / data + vec = BinaryValue() + if not self.bus.endofpacket.value: + vec = self.bus.data.value + else: + value = self.bus.data.value.get_binstr() + if self.config["useEmpty"] and self.bus.empty.value.integer: + empty = self.bus.empty.value.integer * self.config["dataBitsPerSymbol"] + if self.config["firstSymbolInHighOrderBits"]: + value = value[:-empty] + else: + value = value[empty:] + vec.assign(value) + if not vec.is_resolvable: + raise AvalonProtocolError("After empty masking value is still bad? Had empty {:d}, got value {:s}".format(empty, self.bus.data.value.get_binstr())) + vec.big_endian = self.config['firstSymbolInHighOrderBits'] pkt += vec.buff + if hasattr(self.bus, 'channel'): + if channel is None: + channel = self.bus.channel.value.integer + if channel > self.config["maxChannel"]: + raise AvalonProtocolError("Channel value (%d) is greater than maxChannel (%d)" % (channel,self.config["maxChannel"])) + elif self.bus.channel.value.integer != channel: + raise AvalonProtocolError("Channel value changed during packet") + if self.bus.endofpacket.value: - # Truncate the empty bits - if (self.config["useEmpty"] and - self.bus.empty.value.integer): - pkt = pkt[:-self.bus.empty.value.integer] self.log.info("Received a packet of %d bytes" % len(pkt)) self.log.debug(hexdump(str((pkt)))) + self.channel = channel self._recv(pkt) pkt = "" in_pkt = False - else : + channel = None + else : if in_pkt : invalid_cyclecount += 1 if self.config["invalidTimeout"] : @@ -161,3 +223,21 @@ def valid(): raise AvalonProtocolError( "In-Packet Timeout. Didn't receive any valid data for %d cycles!" % invalid_cyclecount) + +class AvalonSTPktsWithChannel(AvalonSTPkts): + """ + Packetised AvalonST bus using channel + """ + _signals = ["valid", "data", "startofpacket", "endofpacket", "channel"] + _optional_signals = ["error", "ready", "empty"] + + def __init__(self, *args, **kwargs): + AvalonSTPkts.__init__(self, *args, **kwargs) + + def _recv(self,pkt): + """Force use of channel in recv function + + args: + pkt: (string) Monitored data + """ + AvalonSTPkts._recv(self,{"data":pkt,"channel":self.channel}) diff --git a/tests/designs/avalon_streaming_module/Makefile b/tests/designs/avalon_streaming_module/Makefile new file mode 100644 index 00000000..cd44e2ec --- /dev/null +++ b/tests/designs/avalon_streaming_module/Makefile @@ -0,0 +1,26 @@ +TOPLEVEL_LANG ?= verilog + +ifneq ($(TOPLEVEL_LANG),verilog) + +all: + @echo "Skipping test due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being verilog" +clean:: + +else + +TOPLEVEL := avalon_streaming + +ifeq ($(OS),Msys) +WPWD=$(shell sh -c 'pwd -W') +else +WPWD=$(shell pwd) +endif + +COCOTB?=$(WPWD)/../../.. + +VERILOG_SOURCES = $(COCOTB)/tests/designs/avalon_streaming_module/avalon_streaming.sv + +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim + +endif diff --git a/tests/designs/avalon_streaming_module/avalon_streaming.sv b/tests/designs/avalon_streaming_module/avalon_streaming.sv new file mode 100644 index 00000000..cd6f6179 --- /dev/null +++ b/tests/designs/avalon_streaming_module/avalon_streaming.sv @@ -0,0 +1,51 @@ +module avalon_streaming ( + input wire clk, + input wire reset, + + input wire logic asi_valid, + input wire logic[7:0] asi_data, + output logic asi_ready, + + output logic aso_valid, + output logic[7:0] aso_data, + input wire logic aso_ready +); + +logic [7:0] queue[10]; +integer size; + +always @ (posedge clk or negedge reset) begin + if (reset == 0) begin + size = 0; + asi_ready <= 1'b0; + aso_valid <= 1'b0; + aso_data <= 'x; + end else begin + asi_ready <= size < 10; + if (asi_valid && asi_ready) begin + queue[size] = asi_data; + size = size + 1; + end + if (aso_valid && aso_ready) begin + queue[0] = queue[1]; + queue[1] = queue[2]; + queue[2] = queue[3]; + queue[3] = queue[4]; + queue[4] = queue[5]; + queue[5] = queue[6]; + queue[6] = queue[7]; + queue[7] = queue[8]; + queue[8] = queue[9]; + size = size - 1; + end + aso_data <= queue[0]; + aso_valid <= size > 0; + end +end + +initial begin + $dumpfile("waveform.vcd"); + $dumpvars; +end + +endmodule : avalon_streaming diff --git a/tests/test_cases/test_avalon_stream/Makefile b/tests/test_cases/test_avalon_stream/Makefile new file mode 100644 index 00000000..6b69b874 --- /dev/null +++ b/tests/test_cases/test_avalon_stream/Makefile @@ -0,0 +1,3 @@ +include ../../designs/avalon_streaming_module/Makefile + +MODULE = test_avalon_stream diff --git a/tests/test_cases/test_avalon_stream/test_avalon_stream.py b/tests/test_cases/test_avalon_stream/test_avalon_stream.py new file mode 100644 index 00000000..c18dbae0 --- /dev/null +++ b/tests/test_cases/test_avalon_stream/test_avalon_stream.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +"""Test to demonstrate functionality of the avalon basic streaming interface""" + +import logging +import random +import struct +import sys + +import cocotb +from cocotb.drivers import BitDriver +from cocotb.drivers.avalon import AvalonST as AvalonSTDriver +from cocotb.monitors.avalon import AvalonST as AvalonSTMonitor +from cocotb.triggers import RisingEdge +from cocotb.clock import Clock +from cocotb.scoreboard import Scoreboard +from cocotb.generators.bit import wave + +class AvalonSTTB(object): + """Testbench for avalon basic stream""" + def __init__(self, dut): + self.dut = dut + + self.clkedge = RisingEdge(dut.clk) + + self.stream_in = AvalonSTDriver(self.dut, "asi", dut.clk) + self.stream_out = AvalonSTMonitor(self.dut, "aso", dut.clk) + self.scoreboard = Scoreboard(self.dut, fail_immediately=True) + + self.expected_output = [] + self.scoreboard.add_interface(self.stream_out, self.expected_output) + + self.backpressure = BitDriver(self.dut.aso_ready, self.dut.clk) + + @cocotb.coroutine + def initialise(self): + self.dut.reset <= 0 + cocotb.fork(Clock(self.dut.clk, 10).start()) + for _ in range(3): + yield self.clkedge + self.dut.reset <= 1 + yield self.clkedge + + @cocotb.coroutine + def send_data(self, data): + exp_data = struct.pack("B",data) + if sys.version_info >= (3, 0): + exp_data = exp_data.decode('ascii') + self.expected_output.append(exp_data) + yield self.stream_in.send(data) + +@cocotb.test(expect_fail=False) +def test_avalon_stream(dut): + """Test stream of avalon data""" + + tb = AvalonSTTB(dut) + yield tb.initialise() + tb.backpressure.start(wave()) + + for _ in range(20): + data = random.randint(0,(2^7)-1) + yield tb.send_data(data) + yield tb.clkedge + + for _ in range(5): + yield tb.clkedge + + raise tb.scoreboard.result From 1c35b8df295dbdeee5c4849e32454c5b7894ee03 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 6 Jan 2019 16:14:17 -0800 Subject: [PATCH 1507/2656] Add a port of six.with_metaclass --- cocotb/utils.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/cocotb/utils.py b/cocotb/utils.py index 1c19dace..82d7c8a9 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -381,6 +381,37 @@ def exec_(_code_, _globs_=None, _locs_=None): exec("""exec _code_ in _globs_, _locs_""") +# this is six.with_metaclass, with a clearer docstring +def with_metaclass(meta, *bases): + """ + This provides: + + class Foo(with_metaclass(Meta, Base1, Base2)): pass + + which is a unifying syntax for: + + # python 3 + class Foo(Base1, Base2, metaclass=Meta): pass + + # python 2 + class Foo(Base1, Base2) + __metaclass__ = Meta + + """ + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(type): + + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + + @classmethod + def __prepare__(cls, name, this_bases): + return meta.__prepare__(name, bases) + return type.__new__(metaclass, 'temporary_class', (), {}) + + if __name__ == "__main__": import random a = "" From 1ebf47a068cfed6b994182c68f903b0a86e47f11 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Wed, 19 Dec 2018 21:39:23 -0800 Subject: [PATCH 1508/2656] Fix a regression introduced in gh-727 and gh-723, caused by a misunderstanding of how __new__ works In those patches, I declared classes that were roughly ```python class SomeClass: def __new__(cls, arg): try: return existing[arg] except KeyError: return super(SomeClass, cls).__new__(cls, arg) def __init__(self, arg): self.arg = arg self.state = 0 ``` This approach has a fatal flaw (gh-729), with function calls shown in the following code: ```python A = SomeClass(1) B = SomeClass(1) ``` We need to override class-construction without allowing `__init__` to run a second time. One option would be to just remove `__init__` entirely, and move the contents into `__new__`. The other option, which I take in this patch, is to introduce a metaclass overriding the `__call__` operator on class types. I'm not convinced this is the best approach, but it does fix the problem. --- cocotb/triggers.py | 40 +++++++-------------- cocotb/utils.py | 36 +++++++++++++++++++ tests/test_cases/test_cocotb/test_cocotb.py | 18 ++++++++++ 3 files changed, 66 insertions(+), 28 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index a4892940..13590779 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -38,7 +38,10 @@ import simulator from cocotb.log import SimLog from cocotb.result import raise_error -from cocotb.utils import get_sim_steps, get_time_from_sim_steps +from cocotb.utils import ( + get_sim_steps, get_time_from_sim_steps, with_metaclass, + ParametrizedSingleton +) class TriggerException(Exception): @@ -206,7 +209,7 @@ def NextTimeStep(): return _nxts -class _EdgeBase(GPITrigger): +class _EdgeBase(with_metaclass(ParametrizedSingleton, GPITrigger)): """ Execution will resume when an edge occurs on the provided signal """ @@ -218,19 +221,9 @@ def _edge_type(self): """ raise NotImplementedError - # Ensure that each signal has at most one edge trigger per edge type. - # Using a weak dictionary ensures we don't create a reference cycle - _instances = weakref.WeakValueDictionary() - - def __new__(cls, signal): - # find the existing instance, if possible - else create a new one - key = (signal, cls._edge_type) - try: - return cls._instances[key] - except KeyError: - instance = super(_EdgeBase, cls).__new__(cls) - cls._instances[key] = instance - return instance + @classmethod + def __singleton_key__(cls, signal): + return signal def __init__(self, signal): super(_EdgeBase, self).__init__() @@ -507,22 +500,13 @@ def prime(self, callback): callback(self) -class Join(PythonTrigger): +class Join(with_metaclass(ParametrizedSingleton, PythonTrigger)): """ Join a coroutine, firing when it exits """ - # Ensure that each coroutine has at most one join trigger. - # Using a weak dictionary ensures we don't create a reference cycle - _instances = weakref.WeakValueDictionary() - - def __new__(cls, coroutine): - # find the existing instance, if possible - else create a new one - try: - return cls._instances[coroutine] - except KeyError: - instance = super(Join, cls).__new__(cls) - cls._instances[coroutine] = instance - return instance + @classmethod + def __singleton_key__(cls, coroutine): + return coroutine def __init__(self, coroutine): super(Join, self).__init__() diff --git a/cocotb/utils.py b/cocotb/utils.py index 82d7c8a9..13c5d5f5 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -33,6 +33,7 @@ import math import os import sys +import weakref # For autodocumentation don't need the extension modules if "SPHINX_BUILD" in os.environ: @@ -412,6 +413,41 @@ def __prepare__(cls, name, this_bases): return type.__new__(metaclass, 'temporary_class', (), {}) +class ParametrizedSingleton(type): + """ + A metaclass that allows class construction to reuse an existing instance + + We use this so that `RisingEdge(sig)` and `Join(coroutine)` always return + the same instance, rather than creating new copies. + """ + + def __init__(cls, *args, **kwargs): + # Attach a lookup table to this class. + # Weak such that if the instance is no longer referenced, it can be + # collected. + cls.__instances = weakref.WeakValueDictionary() + + def __singleton_key__(cls, *args, **kwargs): + """ + Convert the construction arguments into a normalized representation that + uniquely identifies this singleton. + """ + # Once we drop python 2, we can implement a default like the following, + # which will work in 99% of cases: + # return tuple(inspect.Signature(cls).bind(*args, **kwargs).arguments.items()) + raise NotImplementedError + + def __call__(cls, *args, **kwargs): + key = cls.__singleton_key__(*args, **kwargs) + try: + return cls.__instances[key] + except KeyError: + # construct the object as normal + self = super(ParametrizedSingleton, cls).__call__(*args, **kwargs) + cls.__instances[key] = self + return self + + if __name__ == "__main__": import random a = "" diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 63da61f7..3e496627 100755 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -763,6 +763,24 @@ def test_join_identity(dut): clk_gen.kill() +@cocotb.test() +def test_edge_identity(dut): + """ + Test that Edge triggers returns the same object each time + """ + + re = RisingEdge(dut.clk) + fe = FallingEdge(dut.clk) + e = Edge(dut.clk) + + assert re is RisingEdge(dut.clk) + assert fe is FallingEdge(dut.clk) + assert e is Edge(dut.clk) + + # check they are all unique + assert len({re, fe, e}) == 3 + yield Timer(1) + if sys.version_info[:2] >= (3, 3): # this would be a syntax error in older python, so we do the whole From 270a5a8ad6deec512c50b28c94078c896616f876 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 6 Jan 2019 21:33:38 -0800 Subject: [PATCH 1509/2656] Use sets when checking for consequence-less triggers This avoids the same trigger being visited more than once, and also speeds up membership tests --- cocotb/scheduler.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index db86b20c..fe56b21d 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -394,17 +394,20 @@ def react(self, trigger, depth=0): # If the coroutine was waiting on multiple triggers we may be able # to unprime the other triggers that didn't fire - for coro in scheduling: - for pending in self._coro2triggers[coro]: - for others in self._trigger2coros[pending]: - if others not in scheduling: - break - else: - # if pending is not trigger and pending.primed: - # pending.unprime() - if pending.primed: - pending.unprime() - del self._trigger2coros[pending] + scheduling_set = set(scheduling) + other_triggers = { + t + for coro in scheduling + for t in self._coro2triggers[coro] + } + for pending in other_triggers: + # every coroutine waiting on this trigger is already being woken + if scheduling_set.issuperset(self._trigger2coros[pending]): + # if pending is not trigger and pending.primed: + # pending.unprime() + if pending.primed: + pending.unprime() + del self._trigger2coros[pending] for coro in scheduling: if _debug: From 0e5b5f93c3a6aabed8b115df34117a56e7919b05 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 6 Jan 2019 21:36:38 -0800 Subject: [PATCH 1510/2656] Remove the currently firing trigger from the list of other triggers. This resolves the confusion that caused this commented out code to remain. As previously written, this would essentially call: self._trigger2coros[trigger] = [] trigger.unprime() del self._trigger2coros[trigger] It's simpler just to unprime the current trigger up front. --- cocotb/scheduler.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index fe56b21d..43ba5c78 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -392,6 +392,9 @@ def react(self, trigger, depth=0): self.log.debug("%d pending coroutines for event %s%s" % (len(scheduling), str(trigger), debugstr)) + # This trigger isn't needed any more + trigger.unprime() + # If the coroutine was waiting on multiple triggers we may be able # to unprime the other triggers that didn't fire scheduling_set = set(scheduling) @@ -399,12 +402,11 @@ def react(self, trigger, depth=0): t for coro in scheduling for t in self._coro2triggers[coro] - } + } - {trigger} + for pending in other_triggers: # every coroutine waiting on this trigger is already being woken if scheduling_set.issuperset(self._trigger2coros[pending]): - # if pending is not trigger and pending.primed: - # pending.unprime() if pending.primed: pending.unprime() del self._trigger2coros[pending] From 6e48847c770c1393c708e8794345a530bad45cce Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 4 Jan 2019 23:09:43 +0100 Subject: [PATCH 1511/2656] Add .svg extension to image refs --- documentation/source/endian_swapper.rst | 4 ++-- documentation/source/hal_cosimulation.rst | 2 +- documentation/source/introduction.rst | 2 +- documentation/source/ping_tun_tap.rst | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/documentation/source/endian_swapper.rst b/documentation/source/endian_swapper.rst index dddca5c4..736a5fed 100644 --- a/documentation/source/endian_swapper.rst +++ b/documentation/source/endian_swapper.rst @@ -17,7 +17,7 @@ Design We have a relatively simplistic RTL block called the endian_swapper. The DUT has three interfaces, all conforming to the Avalon standard: -.. image:: diagrams/svg/endian_swapper_design.* +.. image:: diagrams/svg/endian_swapper_design.svg The DUT will swap the endianness of packets on the Avalon-ST bus if a configuration bit is set. For every packet arriving on the "stream_in" interface the entire packet will be endian swapped if the configuration bit is set, otherwise the entire packet will pass through unmodified. @@ -46,7 +46,7 @@ To begin with we create a class to encapsulate all the common code for the testb With the above code we have created a testbench with the following structure: -.. image:: diagrams/svg/endian_swapper_testbench.* +.. image:: diagrams/svg/endian_swapper_testbench.svg If we inspect this line-by-line: diff --git a/documentation/source/hal_cosimulation.rst b/documentation/source/hal_cosimulation.rst index 7b465bb7..92547535 100644 --- a/documentation/source/hal_cosimulation.rst +++ b/documentation/source/hal_cosimulation.rst @@ -60,7 +60,7 @@ decorator turns a normal function that isn't a `coroutine` into a blocking to be called by a normal thread. The call sequence looks like this: -.. image:: diagrams/svg/hal_cosimulation.* +.. image:: diagrams/svg/hal_cosimulation.svg Implementation diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index de403ba2..ebea7a26 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -63,7 +63,7 @@ Overview A typical cocotb testbench requires no additional RTL code. The Design Under Test (DUT) is instantiated as the toplevel in the simulator without any wrapper code. Cocotb drives stimulus onto the inputs to the DUT (or further down the hierarchy) and monitors the outputs directly from Python. -.. image:: diagrams/svg/cocotb_overview.* +.. image:: diagrams/svg/cocotb_overview.svg A test is simply a Python function. At any given time either the simulator is advancing time or the Python code is executing. The **yield** keyword is used to indicate when to pass control of execution back to the simulator. A test can spawn multiple coroutines, allowing for independent flows of execution. diff --git a/documentation/source/ping_tun_tap.rst b/documentation/source/ping_tun_tap.rst index 2bf0c9de..82c4400c 100644 --- a/documentation/source/ping_tun_tap.rst +++ b/documentation/source/ping_tun_tap.rst @@ -31,7 +31,7 @@ Linux has a `TUN/TAP`_ virtual network device which we can use for this purpose, allowing `ping`_ to run unmodified and unaware that it is communicating with our simulation rather than a remote network endpoint. -.. image:: diagrams/svg/ping_tun_tap.* +.. image:: diagrams/svg/ping_tun_tap.svg Implementation From 46b5617d83ff42b20ce81d8616e6b76ca60437b5 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 4 Jan 2019 23:26:23 +0100 Subject: [PATCH 1512/2656] Enable SVG output in PDFs Using the cairosvgconverter extension we convert SVG files to PDF when building a PDF of the documentation, and leave them as SVG files for the HTML documentation. To ease the build process this commit adds a new Makefile which mimicks ReadTheDocs builds and doesn't rely on installed software on the developer PC. It creates a Python 3 venv and installs all build dependencies from the requirements.txt file into it before calling Sphinx. All commands are passed through to Sphinx, which doesn't need a full Makefile any more (that's a feature of newer Sphinx versions, not of this commit). --- documentation/.gitignore | 1 + documentation/Makefile | 194 +++---------------- documentation/requirements.txt | 4 + documentation/source/conf.py | 7 +- documentation/sphinxext/cairosvgconverter.py | 38 ++++ 5 files changed, 78 insertions(+), 166 deletions(-) create mode 100644 documentation/.gitignore create mode 100644 documentation/requirements.txt create mode 100644 documentation/sphinxext/cairosvgconverter.py diff --git a/documentation/.gitignore b/documentation/.gitignore new file mode 100644 index 00000000..1d17dae1 --- /dev/null +++ b/documentation/.gitignore @@ -0,0 +1 @@ +.venv diff --git a/documentation/Makefile b/documentation/Makefile index 5248dbf6..0bad6349 100644 --- a/documentation/Makefile +++ b/documentation/Makefile @@ -1,178 +1,42 @@ -# Makefile for Sphinx documentation +# Build the cocotb documentation +# +# Note: this Makefile is *not* used by Read The Docs, it exec()'s the conf.py +# file directly. Hence, all special build steps here are only relevant for +# local builds, not for RTD builds. Add all build steps which should be executed +# in RTD builds as Python code into the conf.py file. # # You can set these variables from the command line. SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = +SPHINXBUILD = .venv/bin/sphinx-build +SOURCEDIR = source BUILDDIR = build -# User-friendly check for sphinx-build -ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you do not have Sphinx installed, grab it from http://sphinx-doc.org/) -endif - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " xml to make Docutils-native XML files" - @echo " pseudoxml to make pseudoxml-XML files for display purposes" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/cocotb.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/cocotb.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/cocotb" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/cocotb" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -latexpdfja: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through platex and dvipdfmx..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." +# Put it first so that "make" without argument is like "make help". +help: .venv + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." +.venv: + echo Creating Python venv for Sphinx build + python3 -m venv .venv + .venv/bin/pip -q install --upgrade pip + .venv/bin/pip -q install -r requirements.txt -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." +clean: .venv Makefile + @$(SPHINXBUILD) -M clean "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." +distclean: + -rm -rf .venv build sphinxext/__pycache__ -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." -xml: - $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml - @echo - @echo "Build finished. The XML files are in $(BUILDDIR)/xml." +.PHONY: help Makefile -pseudoxml: - $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml - @echo - @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +.DEFAULT: +# Dependencies for this special target are ignored by make, as discussed in +# http://stackoverflow.com/questions/26875072/dependencies-for-special-make-target-default-not-firing +# We hack around that by calling the target explicitly. + make .venv + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/documentation/requirements.txt b/documentation/requirements.txt new file mode 100644 index 00000000..8208293e --- /dev/null +++ b/documentation/requirements.txt @@ -0,0 +1,4 @@ +# Python dependencies to build the documentation +Sphinx>=1.6 +sphinx-rtd-theme +cairosvg diff --git a/documentation/source/conf.py b/documentation/source/conf.py index 972e3fa6..adfde050 100644 --- a/documentation/source/conf.py +++ b/documentation/source/conf.py @@ -18,6 +18,10 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('../..')) + +# Add in-tree extensions to path +sys.path.insert(0, os.path.abspath('../sphinxext')) + os.environ["SPHINX_BUILD"] = "1" # -- General configuration ----------------------------------------------------- @@ -32,9 +36,10 @@ 'sphinx.ext.doctest', 'sphinx.ext.todo', 'sphinx.ext.coverage', - 'sphinx.ext.pngmath', + 'sphinx.ext.imgmath', 'sphinx.ext.viewcode', 'sphinx.ext.napoleon', + 'cairosvgconverter', ] # Add any paths that contain templates here, relative to this directory. diff --git a/documentation/sphinxext/cairosvgconverter.py b/documentation/sphinxext/cairosvgconverter.py new file mode 100644 index 00000000..11bf2fc6 --- /dev/null +++ b/documentation/sphinxext/cairosvgconverter.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +""" +""" +from sphinx.errors import ExtensionError +from sphinx.locale import _ +from sphinx.transforms.post_transforms.images import ImageConverter +from sphinx.util import logging +from sphinx.util.osutil import ENOENT, EPIPE, EINVAL +from cairosvg import svg2pdf + +logger = logging.getLogger(__name__) + +class CairoSvgConverter(ImageConverter): + conversion_rules = [ + ('image/svg+xml', 'application/pdf'), + ] + + def is_available(self): + # type: () -> bool + return True + + def convert(self, _from, _to): + # type: (unicode, unicode) -> bool + """Converts the image to expected one.""" + svg2pdf(url=_from, write_to=_to) + + return True + + +def setup(app): + # type: (Sphinx) -> Dict[unicode, Any] + app.add_post_transform(CairoSvgConverter) + + return { + 'version': 'builtin', + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } From a1241f738b84c6af3a9b2e78b0c36bc71b69a8e7 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Tue, 8 Jan 2019 21:20:13 +0000 Subject: [PATCH 1513/2656] Add README with documentation build help --- documentation/README.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 documentation/README.md diff --git a/documentation/README.md b/documentation/README.md new file mode 100644 index 00000000..e42f4599 --- /dev/null +++ b/documentation/README.md @@ -0,0 +1,38 @@ +Cocotb documentation +==================== + +This directory contains the documentation of cocotb. + +Build the HTML documentation to view it in a browser: + +```sh +# build the HTML documentation +make html + +# view it in your browser +xdg-open build/html/index.html +``` + +It is also possible to build the documentation as PDF file. +Building the PDFs requires a texlive installation, the example below assumes Ubuntu or Debian distributions +Replace the commands for your distribution as necessary. + +```sh +# install build dependencies +apt-get install texlive + +# build the PDF documentation +make latexpdf + +# open the file in your PDF viewer +xdg-open build/latex/cocotb.pdf +``` + +To clean the build tree run + +```sh +make clean + +# or to clean even more build output +make distclean +``` From c168ce4f1a2e788d22f887239ff2c79b79faee6c Mon Sep 17 00:00:00 2001 From: Matt A-R Date: Wed, 9 Jan 2019 10:25:40 +0000 Subject: [PATCH 1514/2656] Check if an event has fired when waiting Fixes #743 --- cocotb/triggers.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 0fdc2363..7e309d25 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -394,7 +394,13 @@ def set(self, data=None): def wait(self): """This can be yielded to block this coroutine - until another wakes it""" + until another wakes it + + If the Event has already been fired, this returns NullTrigger() + To reset the event (and enable the use of wait() again), clear() should be called + """ + if self.fired: + return NullTrigger() return _Event(self) def clear(self): From a9326feaf949c21c220834167083aa42ee059977 Mon Sep 17 00:00:00 2001 From: Matt A-R Date: Wed, 9 Jan 2019 10:27:35 +0000 Subject: [PATCH 1515/2656] Added kwargs to base driver to allow extensibility Added kwargs to base driver to allow extensibility. Currently used in avalon driver. --- cocotb/drivers/__init__.py | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index a8f7d77f..fce90ce4 100644 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -108,7 +108,7 @@ def kill(self): self._thread.kill() self._thread = None - def append(self, transaction, callback=None, event=None): + def append(self, transaction, callback=None, event=None, **kwargs): """ Queue up a transaction to be sent over the bus. @@ -119,8 +119,10 @@ def append(self, transaction, callback=None, event=None): sent event: event to be set when the tansaction has been sent + + **kwargs: Any additional arguments used in child class' _driver_send method """ - self._sendQ.append((transaction, callback, event)) + self._sendQ.append((transaction, callback, event, kwargs)) self._pending.set() def clear(self): @@ -130,7 +132,7 @@ def clear(self): self._sendQ = deque() @coroutine - def send(self, transaction, sync=True): + def send(self, transaction, sync=True, **kwargs): """ Blocking send call (hence must be "yielded" rather than called) @@ -141,27 +143,35 @@ def send(self, transaction, sync=True): Kwargs: sync (boolean): synchronise the transfer by waiting for risingedge + kwargs (dict): Adddition arguments used in child class' _driver_send method """ - yield self._send(transaction, None, None, sync=sync) + yield self._send(transaction, None, None, sync=sync, **kwargs) - def _driver_send(self, transaction, sync=True): + def _driver_send(self, transaction, sync=True, **kwargs): """ actual impementation of the send. subclasses should override this method to implement the actual send routine + + Args: + transaction (any): the transaction to send + + Kwargs: + sync (boolean): synchronise the transfer by waiting for rising edge + kwargs (dict): additional arguments if required for protocol implemented in subclass """ raise NotImplementedError("Subclasses of Driver should define a " "_driver_send coroutine") @coroutine - def _send(self, transaction, callback, event, sync=True): + def _send(self, transaction, callback, event, sync=True, **kwargs): """ assumes the caller has already acquired the busy lock releases busy lock once sending is complete """ - yield self._driver_send(transaction, sync=sync) + yield self._driver_send(transaction, sync=sync, **kwargs) # Notify the world that this transaction is complete if event: @@ -186,10 +196,10 @@ def _send_thread(self): # Send in all the queued packets, # only synchronise on the first send while self._sendQ: - transaction, callback, event = self._sendQ.popleft() + transaction, callback, event, kwargs = self._sendQ.popleft() self.log.debug("Sending queued packet...") yield self._send(transaction, callback, event, - sync=not synchronised) + sync=not synchronised, **kwargs) synchronised = True From 79a5c23abcca3b41a729a2491fc5d4381996bf8a Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Wed, 9 Jan 2019 09:31:09 +0000 Subject: [PATCH 1516/2656] Support for vpiUnionVar. --- lib/vpi/VpiImpl.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index 9845e00a..b0f1b73c 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -107,6 +107,7 @@ gpi_objtype_t to_gpi_objtype(int32_t vpitype) case vpiStructVar: case vpiStructNet: + case vpiUnionVar: return GPI_STRUCTURE; case vpiModport: @@ -169,6 +170,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, break; case vpiStructVar: case vpiStructNet: + case vpiUnionVar: new_obj = new VpiObjHdl(this, new_hdl, to_gpi_objtype(type)); break; case vpiModule: From 3147e4c5faef01967721026df8eafe5fc796d2df Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Wed, 9 Jan 2019 11:39:27 +0000 Subject: [PATCH 1517/2656] New numerical operators for BinaryValue(). --- cocotb/binary.py | 101 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 3 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 47a08cba..78ef025d 100644 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -450,6 +450,9 @@ def __iadd__(self, other): self.integer = self.integer + int(other) return self + def __radd__(self, other): + return self.integer + other + def __sub__(self, other): return self.integer - int(other) @@ -457,6 +460,9 @@ def __isub__(self, other): self.integer = self.integer - int(other) return self + def __rsub__(self, other): + return other - self.integer + def __mul__(self, other): return self.integer * int(other) @@ -464,13 +470,25 @@ def __imul__(self, other): self.integer = self.integer * int(other) return self - def __divmod__(self, other): + def __rmul__(self, other): + return self.integer * other + + def __floordiv__(self, other): return self.integer // int(other) - def __idivmod__(self, other): - self.integer = self.integer // int(other) + def __ifloordiv__(self, other): + self.integer = self.__floordiv__(other) return self + def __rfloordiv__(self, other): + return other // self.integer + + def __divmod__(self, other): + return (self.integer // other, self.integer % other) + + def __rdivmod__(self, other): + return other // self.integer + def __mod__(self, other): return self.integer % int(other) @@ -478,6 +496,19 @@ def __imod__(self, other): self.integer = self.integer % int(other) return self + def __rmod__(self, other): + return other % self.integer + + def __pow__(self, other, modulo): + return pow(self.integer, other, modulo) + + def __ipow__(self, other, modulo): + self.integer = pow(self.integer, other, modulo) + return self + + def __rpow__(self, other): + return pow(other, self.integer, modulo) + def __lshift__(self, other): return int(self) << int(other) @@ -486,6 +517,9 @@ def __ilshift__(self, other): self.binstr = self.binstr[other:] + self.binstr[:other] return self + def __rlshift__(self, other): + return other << self.integer + def __rshift__(self, other): return int(self) >> int(other) @@ -494,10 +528,71 @@ def __irshift__(self, other): self.binstr = self.binstr[-other:] + self.binstr[:-other] return self + def __rrshift__(self, other): + return other >> self.integer + + def __and__(self, other): + return self.integer & other + + def __iand__(self, other): + self.integer &= other + return self + + def __rand__(self, other): + return self.integer & other + + def __xor__(self, other): + return self.integer ^ other + + def __ixor__(self, other): + self.integer ^= other + return self + + def __rxor__(self, other): + return self.__xor__(other) + + def __or__(self, other): + return self.integer | other + + def __ior__(self, other): + self.integer |= other + return self + + def __ror__(self, other): + return self.__or__(other) + + def __div__(self, other): + return self.integer / other + + def __idiv__(self, other): + self.integer /= other + return self + + def __rdiv__(self, other): + return other / self.integer + + def __neg__(self, other): + return - self.integer + + def __pos__(self): + return + self.integer + + def __abs__(self): + return abs(self.integer) + def __invert__(self): """Preserves X values""" return self._invert(self.binstr) + def __oct__(self): + return oct(self.integer) + + def __hex__(self): + return hex(self.integer) + + def __index__(self): + return self.integer + def __len__(self): return len(self.binstr) From 175995c4f013f837129732f5d9c34afb1fd5bdae Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Thu, 10 Jan 2019 10:54:01 +0000 Subject: [PATCH 1518/2656] Changed bits to n_bits where appropriate. 'bits' has been deprecated. --- cocotb/drivers/ad9361.py | 12 ++++++------ cocotb/drivers/amba.py | 4 ++-- cocotb/drivers/avalon.py | 20 ++++++++++---------- cocotb/drivers/xgmii.py | 2 +- cocotb/handle.py | 8 ++++---- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/cocotb/drivers/ad9361.py b/cocotb/drivers/ad9361.py index b4f82469..3981eeee 100644 --- a/cocotb/drivers/ad9361.py +++ b/cocotb/drivers/ad9361.py @@ -55,9 +55,9 @@ def send_data(self, i_data, q_data, i_data2=None, q_data2=None, @cocotb.coroutine def rx_data_to_ad9361(self, i_data, q_data, i_data2=None, q_data2=None, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT): - i_bin_val = BinaryValue(bits=12, bigEndian=False, + i_bin_val = BinaryValue(n_bits=12, bigEndian=False, binaryRepresentation=binaryRepresentation) - q_bin_val = BinaryValue(bits=12, bigEndian=False, + q_bin_val = BinaryValue(n_bits=12, bigEndian=False, binaryRepresentation=binaryRepresentation) index = 0 if i_data2 is None and q_data2 is None: @@ -132,8 +132,8 @@ def rx_data_to_ad9361(self, i_data, q_data, i_data2=None, q_data2=None, @cocotb.coroutine def _tx_data_from_ad9361(self): - i_bin_val = BinaryValue(bits=12, bigEndian=False) - q_bin_val = BinaryValue(bits=12, bigEndian=False) + i_bin_val = BinaryValue(n_bits=12, bigEndian=False) + q_bin_val = BinaryValue(n_bits=12, bigEndian=False) while True: yield RisingEdge(self.dut.tx_clk_out_p) if self.dut.tx_frame_out_p.value.integer == 1: @@ -154,8 +154,8 @@ def _tx_data_from_ad9361(self): @cocotb.coroutine def _ad9361_tx_to_rx_loopback(self): cocotb.fork(self._tx_data_from_ad9361()) - i_bin_val = BinaryValue(bits=12, bigEndian=False) - q_bin_val = BinaryValue(bits=12, bigEndian=False) + i_bin_val = BinaryValue(n_bits=12, bigEndian=False) + q_bin_val = BinaryValue(n_bits=12, bigEndian=False) while True: yield RisingEdge(self.dut.rx_clk_in_p) if self.rx_frame_asserted: diff --git a/cocotb/drivers/amba.py b/cocotb/drivers/amba.py index 295d21b7..672614e2 100644 --- a/cocotb/drivers/amba.py +++ b/cocotb/drivers/amba.py @@ -264,7 +264,7 @@ def _write_data(self): burst_length = _awlen + 1 bytes_in_beat = self._size_to_bytes_in_beat(_awsize) - word = BinaryValue(bits=bytes_in_beat*8, bigEndian=self.big_endian) + word = BinaryValue(n_bits=bytes_in_beat*8, bigEndian=self.big_endian) if __debug__: self.log.debug( @@ -313,7 +313,7 @@ def _read_data(self): burst_length = _arlen + 1 bytes_in_beat = self._size_to_bytes_in_beat(_arsize) - word = BinaryValue(bits=bytes_in_beat*8, bigEndian=self.big_endian) + word = BinaryValue(n_bits=bytes_in_beat*8, bigEndian=self.big_endian) if __debug__: self.log.debug( diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 88f6d300..5024fee9 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -274,7 +274,7 @@ def __init__(self, entity, name, clock, readlatency_min=1, else: self._mem = memory - self._val = BinaryValue(bits=self._width, bigEndian=False) + self._val = BinaryValue(n_bits=self._width, bigEndian=False) self._readlatency_min = readlatency_min self._readlatency_max = readlatency_max self._responses = [] @@ -513,7 +513,7 @@ def __init__(self, *args, **kwargs): self.config[configoption] = value self.log.debug("Setting config option %s to %s" % (configoption, str(value))) - word = BinaryValue(bits=len(self.bus.data), bigEndian=self.config['firstSymbolInHighOrderBits']) + word = BinaryValue(n_bits=len(self.bus.data), bigEndian=self.config['firstSymbolInHighOrderBits']) self.bus.valid <= 0 self.bus.data <= word @@ -543,7 +543,7 @@ def _driver_send(self, value, sync=True): # Avoid spurious object creation by recycling clkedge = RisingEdge(self.clock) - word = BinaryValue(bits=len(self.bus.data), bigEndian=False) + word = BinaryValue(n_bits=len(self.bus.data), bigEndian=False) # Drive some defaults since we don't know what state we're in self.bus.valid <= 0 @@ -618,10 +618,10 @@ def __init__(self, *args, **kwargs): self.use_empty = (num_data_symbols > 1) self.config["useEmpty"] = self.use_empty - word = BinaryValue(bits=len(self.bus.data), + word = BinaryValue(n_bits=len(self.bus.data), bigEndian=self.config['firstSymbolInHighOrderBits']) - single = BinaryValue(bits=1, bigEndian=False) + single = BinaryValue(n_bits=1, bigEndian=False) word.binstr = ("x"*len(self.bus.data)) single.binstr = ("x") @@ -632,7 +632,7 @@ def __init__(self, *args, **kwargs): self.bus.endofpacket <= single if self.use_empty: - empty = BinaryValue(bits=len(self.bus.empty), bigEndian=False) + empty = BinaryValue(n_bits=len(self.bus.empty), bigEndian=False) empty.binstr = ("x"*len(self.bus.empty)) self.bus.empty <= empty @@ -647,7 +647,7 @@ def __init__(self, *args, **kwargs): raise AttributeError( "%s has maxChannel=%d, but can only support a maximum channel of (2**channel_width)-1=%d, channel_width=%d" % (self.name,self.config['maxChannel'],maxChannel,len(self.bus.channel))) - channel = BinaryValue(bits=len(self.bus.channel), bigEndian=False) + channel = BinaryValue(n_bits=len(self.bus.channel), bigEndian=False) channel.binstr = ("x"*len(self.bus.channel)) self.bus.channel <= channel @@ -678,12 +678,12 @@ def _send_string(self, string, sync=True, channel=None): # FIXME busses that aren't integer numbers of bytes bus_width = int(len(self.bus.data) / 8) - word = BinaryValue(bits=len(self.bus.data), + word = BinaryValue(n_bits=len(self.bus.data), bigEndian=self.config['firstSymbolInHighOrderBits']) - single = BinaryValue(bits=1, bigEndian=False) + single = BinaryValue(n_bits=1, bigEndian=False) if self.use_empty: - empty = BinaryValue(bits=len(self.bus.empty), bigEndian=False) + empty = BinaryValue(n_bits=len(self.bus.empty), bigEndian=False) # Drive some defaults since we don't know what state we're in if self.use_empty: diff --git a/cocotb/drivers/xgmii.py b/cocotb/drivers/xgmii.py index d34d75e3..ac842c19 100644 --- a/cocotb/drivers/xgmii.py +++ b/cocotb/drivers/xgmii.py @@ -77,7 +77,7 @@ def __init__(self, nbytes, interleaved=True): byte plus a control bit per byte in the MSBs. """ - self._value = BinaryValue(bits=nbytes*9, bigEndian=False) + self._value = BinaryValue(n_bits=nbytes*9, bigEndian=False) self._integer = long(0) self._interleaved = interleaved self._nbytes = nbytes diff --git a/cocotb/handle.py b/cocotb/handle.py index b46f592f..bd89cabe 100644 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -456,7 +456,7 @@ def __init__(self, handle, path, handle_type): self._value = simulator.get_signal_val_str(self._handle) else: val = simulator.get_signal_val_binstr(self._handle) - self._value = BinaryValue(bits=len(val)) + self._value = BinaryValue(n_bits=len(val)) try: self._value.binstr = val except: @@ -598,9 +598,9 @@ def setimmediatevalue(self, value): return if isinstance(value, ctypes.Structure): - value = BinaryValue(value=cocotb.utils.pack(value), bits=len(self)) + value = BinaryValue(value=cocotb.utils.pack(value), n_bits=len(self)) elif isinstance(value, get_python_integer_types()): - value = BinaryValue(value=value, bits=len(self), bigEndian=False) + value = BinaryValue(value=value, n_bits=len(self), bigEndian=False) elif isinstance(value, dict): #We're given a dictionary with a list of values and a bit size... num = 0; @@ -614,7 +614,7 @@ def setimmediatevalue(self, value): for val in vallist: num = (num << value["bits"]) + val; - value = BinaryValue(value=num, bits=len(self), bigEndian=False) + value = BinaryValue(value=num, n_bits=len(self), bigEndian=False) elif not isinstance(value, BinaryValue): self._log.critical("Unsupported type for value assignment: %s (%s)" % (type(value), repr(value))) From 71bb89143297be51f0364a52856dd139b201ea15 Mon Sep 17 00:00:00 2001 From: Matt Abdul-Rahim Date: Thu, 10 Jan 2019 10:57:22 +0000 Subject: [PATCH 1519/2656] Test case for consistent behaviour of join. Ensure that yielding a forked coroutine with the join() method called on it outputs the final returned value. Closes #748. --- tests/test_cases/test_cocotb/test_cocotb.py | 23 +++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 3e496627..354a7bb9 100755 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -739,6 +739,29 @@ def some_coro(): assert x == 1 +@cocotb.test() +def consistent_join(dut): + """ + Test that joining a coroutine returns the finished value + """ + @cocotb.coroutine + def wait_for(clk, cycles): + rising_edge = RisingEdge(clk) + for _ in range(cycles): + yield rising_edge + raise ReturnValue(3) + + cocotb.fork(Clock(dut.clk, 2000, 'ps').start()) + + short_wait = cocotb.fork(wait_for(dut.clk, 10)) + long_wait = cocotb.fork(wait_for(dut.clk, 30)) + + yield wait_for(dut.clk, 20) + a = yield short_wait.join() + b = yield long_wait.join() + assert a == b == 3 + + @cocotb.test() def test_kill_twice(dut): """ From 3f895c4270d4b354dede86bfb6ec63355ac8c093 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 6 Jan 2019 17:16:06 -0800 Subject: [PATCH 1520/2656] Use the new ParametrizedSingleton for unparametrized singleton types too This means that `help(ReadOnly)` and `isinstance(Readonly(), Readonly)` behave how you'd expect them too. For some reason, the scheduler requires non-singleton instances of NextTimestep and ReadWrite. If normal singleton instances are used, `test_cocotb.test_readwrite_in_readonly` causes the test that follows it to fail. This can be investigated more later. --- cocotb/scheduler.py | 11 ++++-- cocotb/triggers.py | 37 +++++++++------------ tests/test_cases/test_cocotb/test_cocotb.py | 15 +++++++++ 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 43ba5c78..75047b8a 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -36,6 +36,7 @@ the ReadOnly (and this is invalid, at least in Modelsim). """ import collections +import copy import os import time import logging @@ -67,7 +68,7 @@ import cocotb import cocotb.decorators from cocotb.triggers import (Trigger, GPITrigger, Timer, ReadOnly, PythonTrigger, - _NextTimeStep, _ReadWrite, Event, Join) + NextTimeStep, ReadWrite, Event, Join) from cocotb.log import SimLog from cocotb.result import (TestComplete, TestError, ReturnValue, raise_error, create_error, ExternalException) @@ -194,8 +195,12 @@ class Scheduler(object): # Singleton events, recycled to avoid spurious object creation _readonly = ReadOnly() - _next_timestep = _NextTimeStep() - _readwrite = _ReadWrite() + # TODO[gh-759]: For some reason, the scheduler requires that these triggers + # are _not_ the same instances used by the tests themselves. This is risky, + # because it can lead to them overwriting each other's callbacks. We should + # try to remove this `copy.copy` in future. + _next_timestep = copy.copy(NextTimeStep()) + _readwrite = copy.copy(ReadWrite()) _timer1 = Timer(1) _timer0 = Timer(0) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 7e309d25..2ddf1edb 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -133,11 +133,16 @@ def prime(self, callback): def __str__(self): return self.__class__.__name__ + "(%1.2fps)" % get_time_from_sim_steps(self.sim_steps,units='ps') -class _ReadOnly(GPITrigger): + +class ReadOnly(with_metaclass(ParametrizedSingleton, GPITrigger)): """ Execution will resume when the readonly portion of the sim cycles is readched """ + @classmethod + def __singleton_key__(cls): + return None + def __init__(self): GPITrigger.__init__(self) @@ -151,18 +156,16 @@ def prime(self, callback): def __str__(self): return self.__class__.__name__ + "(readonly)" -_ro = _ReadOnly() - -def ReadOnly(): - return _ro - - -class _ReadWrite(GPITrigger): +class ReadWrite(with_metaclass(ParametrizedSingleton, GPITrigger)): """ Execution will resume when the readwrite portion of the sim cycles is reached """ + @classmethod + def __singleton_key__(cls): + return None + def __init__(self): GPITrigger.__init__(self) @@ -178,17 +181,15 @@ def prime(self, callback): def __str__(self): return self.__class__.__name__ + "(readwritesync)" -_rw = _ReadWrite() - -def ReadWrite(): - return _rw - - -class _NextTimeStep(GPITrigger): +class NextTimeStep(with_metaclass(ParametrizedSingleton, GPITrigger)): """ Execution will resume when the next time step is started """ + @classmethod + def __singleton_key__(cls): + return None + def __init__(self): GPITrigger.__init__(self) @@ -202,12 +203,6 @@ def prime(self, callback): def __str__(self): return self.__class__.__name__ + "(nexttimestep)" -_nxts = _NextTimeStep() - - -def NextTimeStep(): - return _nxts - class _EdgeBase(with_metaclass(ParametrizedSingleton, GPITrigger)): """ diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 354a7bb9..a3f09f94 100755 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -805,6 +805,21 @@ def test_edge_identity(dut): yield Timer(1) +@cocotb.test() +def test_singleton_isinstance(dut): + """ + Test that the result of trigger expression have a predictable type + """ + assert isinstance(RisingEdge(dut.clk), RisingEdge) + assert isinstance(FallingEdge(dut.clk), FallingEdge) + assert isinstance(Edge(dut.clk), Edge) + assert isinstance(NextTimestep(), NextTimestep) + assert isinstance(ReadOnly(), ReadOnly) + assert isinstance(ReadWrite(), ReadWrite) + + yield Timer(1) + + if sys.version_info[:2] >= (3, 3): # this would be a syntax error in older python, so we do the whole # thing inside exec From bfc4d1615a9f4c1e56110a3327a41382fa999c70 Mon Sep 17 00:00:00 2001 From: Jevin Sweval Date: Fri, 11 Jan 2019 16:02:19 -0500 Subject: [PATCH 1521/2656] Fix typo from '>' to '>>' for appending file concatenation creating Questa runsim.do --- makefiles/simulators/Makefile.questa | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index 2911211d..e15df051 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -91,7 +91,7 @@ $(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) $ @echo "onerror {" >> $@ @echo " quit -f -code 1" >> $@ @echo "}" >> $@ - @echo "if [file exists $(RTL_LIBRARY)] {vdel -lib $(RTL_LIBRARY) -all}" > $@ + @echo "if [file exists $(RTL_LIBRARY)] {vdel -lib $(RTL_LIBRARY) -all}" >> $@ @echo "vlib $(RTL_LIBRARY)" >> $@ @echo "vmap -c" >> $@ @echo "vmap $(RTL_LIBRARY) $(RTL_LIBRARY)" >> $@ From fc8d1bdb6bde1d30e768cb28995fbcd6968e2b48 Mon Sep 17 00:00:00 2001 From: Ben Rosser Date: Fri, 11 Jan 2019 17:03:07 -0500 Subject: [PATCH 1522/2656] Minor fix to new test_singleton_isinstance in test_cocotb.py This is cleaning up after a change made in #752. The NextTimeStep trigger wasn't imported from cocotb.triggers, and also the "s" in Timestep was not capitalized in the test itself. I was testing the latest cocotb git master on ius (Cadence Incisive) and happened to notice this. All the other tests seem to be passing, which is good! --- tests/test_cases/test_cocotb/test_cocotb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index a3f09f94..5341e4f5 100755 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -39,7 +39,7 @@ import cocotb from cocotb.triggers import (Timer, Join, RisingEdge, FallingEdge, Edge, - ReadOnly, ReadWrite, ClockCycles) + ReadOnly, ReadWrite, ClockCycles, NextTimeStep) from cocotb.clock import Clock from cocotb.result import ReturnValue, TestFailure, TestError, TestSuccess from cocotb.utils import get_sim_time @@ -813,7 +813,7 @@ def test_singleton_isinstance(dut): assert isinstance(RisingEdge(dut.clk), RisingEdge) assert isinstance(FallingEdge(dut.clk), FallingEdge) assert isinstance(Edge(dut.clk), Edge) - assert isinstance(NextTimestep(), NextTimestep) + assert isinstance(NextTimeStep(), NextTimeStep) assert isinstance(ReadOnly(), ReadOnly) assert isinstance(ReadWrite(), ReadWrite) From ce70d7c2559db5797964f82ca100ee47969d40fc Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sun, 13 Jan 2019 22:23:40 +0100 Subject: [PATCH 1523/2656] Documentation improvements Various documentation improvements on content and formatting. Fixes #715 --- cocotb/binary.py | 120 +++++++------ cocotb/bus.py | 149 +++++++-------- cocotb/clock.py | 6 +- cocotb/decorators.py | 35 ++-- cocotb/drivers/__init__.py | 200 ++++++++++----------- cocotb/drivers/ad9361.py | 45 +++-- cocotb/drivers/amba.py | 35 +++- cocotb/drivers/avalon.py | 151 +++++++++------- cocotb/drivers/opb.py | 82 +++++---- cocotb/drivers/xgmii.py | 138 +++++++------- cocotb/monitors/__init__.py | 72 ++++---- cocotb/monitors/avalon.py | 60 ++++--- cocotb/monitors/xgmii.py | 89 +++++---- cocotb/regression.py | 137 +++++++------- cocotb/result.py | 81 +++++---- cocotb/triggers.py | 10 +- cocotb/utils.py | 113 ++++++------ documentation/source/coroutines.rst | 19 +- documentation/source/includeme.rst | 1 - documentation/source/index.rst | 1 - documentation/source/library_reference.rst | 57 ++++-- documentation/source/triggers.rst | 2 +- 22 files changed, 844 insertions(+), 759 deletions(-) mode change 100644 => 100755 cocotb/binary.py mode change 100644 => 100755 cocotb/bus.py mode change 100644 => 100755 cocotb/drivers/__init__.py mode change 100644 => 100755 cocotb/monitors/__init__.py delete mode 100644 documentation/source/includeme.rst diff --git a/cocotb/binary.py b/cocotb/binary.py old mode 100644 new mode 100755 index 78ef025d..5b306383 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -1,31 +1,31 @@ #!/usr/bin/env python -''' Copyright (c) 2013 Potential Ventures Ltd -Copyright (c) 2013 SolarFlare Communications Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from __future__ import print_function from cocotb.utils import get_python_integer_types @@ -73,14 +73,13 @@ class BinaryRepresentation(): class BinaryValue(object): """Representation of values in binary format. - The underlying value can be set or accessed using three aliasing attributes + The underlying value can be set or accessed using these aliasing attributes: - - BinaryValue.integer is an integer - - BinaryValue.signed_integer is a signed integer - - BinaryValue.binstr is a string of "01xXzZ" - - BinaryValue.buff is a binary buffer of bytes - - - BinaryValue.value is an integer *** deprecated *** + - `BinaryValue.integer` is an integer + - `BinaryValue.signed_integer` is a signed integer + - `BinaryValue.binstr` is a string of "01xXzZ" + - `BinaryValue.buff` is a binary buffer of bytes + - `BinaryValue.value` is an integer **deprecated** For example: @@ -100,17 +99,17 @@ class BinaryValue(object): def __init__(self, value=None, n_bits=None, bigEndian=True, binaryRepresentation=BinaryRepresentation.UNSIGNED, bits=None): - """ - Kwagrs: - value (string or int or long): value to assign to the bus - - n_bits (int): Number of bits to use for the underlying binary - representation - - bigEndian (bool): Interpret the binary as big-endian when - converting to/from a string buffer. - - bits (int): Deprecated: Compatibility wrapper for n_bits + """Args: + value (str or int or long, optional): Value to assign to the bus. + n_bits (int, optional): Number of bits to use for the underlying + binary representation. + bigEndian (bool, optional): Interpret the binary as big-endian + when converting to/from a string buffer. + binaryRepresentation (BinaryRepresentation): The representation + of the binary value + (one of ``UNSIGNED``, ``SIGNED_MAGNITUDE``, ``TWOS_COMPLEMENT``). + Defaults to unsigned representation. + bits (int, optional): Deprecated: Compatibility wrapper for n_bits. """ self._str = "" self.big_endian = bigEndian @@ -144,12 +143,15 @@ def __init__(self, value=None, n_bits=None, bigEndian=True, self.assign(value) def assign(self, value): - """Decides how best to assign the value to the vector + """Decides how best to assign the value to the vector. We possibly try to be a bit too clever here by first of all trying to assign the raw string as a binstring, however if the string contains any characters that aren't 0, 1, X or Z - then we interpret the string as a binary buffer... + then we interpret the string as a binary buffer. + + Args: + value (str or int or long): The value to assign. """ if isinstance(value, get_python_integer_types()): self.value = value @@ -227,7 +229,7 @@ def _adjust_unsigned(self, x): else: rv = '0' * (self._n_bits - l) + x elif l > self._n_bits: - print("WARNING truncating value to match requested number of bits " + print("WARNING: truncating value to match requested number of bits " "(%d -> %d)" % (l, self._n_bits)) if self.big_endian: rv = x[l - self._n_bits:] @@ -248,7 +250,7 @@ def _adjust_signed_mag(self, x): rv = '0' * (self._n_bits - 1 - l) + x[1:] rv = x[0] + rv elif l > self._n_bits: - print("WARNING truncating value to match requested number of bits " + print("WARNING: truncating value to match requested number of bits " "(%d -> %d)" % (l, self._n_bits)) if self.big_endian: rv = x[l - self._n_bits:] @@ -268,7 +270,7 @@ def _adjust_twos_comp(self, x): else: rv = x[0] * (self._n_bits - l) + x elif l > self._n_bits: - print("WARNING truncating value to match requested number of bits " + print("WARNING: truncating value to match requested number of bits " "(%d -> %d)" % (l, self._n_bits)) if self.big_endian: rv = x[l - self._n_bits:] @@ -279,11 +281,11 @@ def _adjust_twos_comp(self, x): return rv def get_value(self): - """value is an integer representation of the underlying vector""" + """Return the integer representation of the underlying vector.""" return self._convert_from[self.binaryRepresentation](self._str) def get_value_signed(self): - """value is an signed integer representation of the underlying vector""" + """Return the signed integer representation of the underlying vector.""" ival = int(resolve(self._str), 2) bits = len(self._str) signbit = (1 << (bits - 1)) @@ -301,11 +303,11 @@ def is_resolvable(self): return not any(char in self._str for char in BinaryValue._resolve_to_error) value = property(get_value, set_value, None, - "Integer access to the value *** deprecated ***") + "Integer access to the value. *** deprecated ***") integer = property(get_value, set_value, None, - "Integer access to the value") + "The integer representation of the underlying vector.") signed_integer = property(get_value_signed, set_value, None, - "Signed integer access to the value") + "The signed integer representation of the underlying vector.") def get_buff(self): """Attribute self.buff represents the value as a binary string buffer @@ -355,7 +357,7 @@ def _adjust(self): else: self._str = "0" * (self._n_bits - l) + self._str elif l > self._n_bits: - print("WARNING truncating value to match requested number of bits " + print("WARNING: truncating value to match requested number of bits " "(%d -> %d)" % (l, self._n_bits)) self._str = self._str[l - self._n_bits:] @@ -597,8 +599,8 @@ def __len__(self): return len(self.binstr) def __getitem__(self, key): - ''' BinaryValue uses verilog/vhdl style slices as opposed to python - style''' + """BinaryValue uses Verilog/VHDL style slices as opposed to Python + style""" if isinstance(key, slice): first, second = key.start, key.stop if self.big_endian: @@ -637,8 +639,8 @@ def __getitem__(self, key): return rv def __setitem__(self, key, val): - ''' BinaryValue uses verilog/vhdl style slices as opposed to python - style''' + """BinaryValue uses Verilog/VHDL style slices as opposed to Python + style""" if not isinstance(val, str) and not isinstance(val, get_python_integer_types()): raise TypeError('BinaryValue slices only accept string or integer values') diff --git a/cocotb/bus.py b/cocotb/bus.py old mode 100644 new mode 100755 index c95e1d6f..5b88fe57 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -1,36 +1,35 @@ #!/usr/bin/env python -''' Copyright (c) 2013 Potential Ventures Ltd -Copyright (c) 2013 SolarFlare Communications Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ - Common bus related functionality - - A bus is simply defined as a collection of signals +Common bus related functionality. +A bus is simply defined as a collection of signals. """ @@ -45,41 +44,33 @@ def _build_sig_attr_dict(signals): class Bus(object): - """ - Wraps up a collection of signals - - Assumes we have a set of signals/nets named: + """Wraps up a collection of signals. - entity.bus_name_signal + Assumes we have a set of signals/nets named ``entity._``. - for example a bus named "stream_in" with signals ["valid", "data"] - dut.stream_in_valid - dut.stream_in_data + For example a bus ``stream_in`` with signals ``valid`` and ``data`` is assumed + to be named ``dut.stream_in_valid`` and ``dut.stream_in_data``. - TODO: - Support for struct/record ports where signals are member names + TODO: + Support for struct/record ports where signals are member names. """ def __init__(self, entity, name, signals, optional_signals=[], bus_separator="_", array_idx=None): - """ - Args: - entity (SimHandle): SimHandle instance to the entity containing the - bus - name (str): name of the bus. None for nameless bus, e.g. - bus-signals in an interface or a modport - (untested on struct/record, but could work here - as well) - signals (list/dict): In the case of an obj (passed to drive/capture) - that has the same attribute names as the signal - names of the bus, the signals agument can be a list - of those names. When obj has different attribute names, - the signals arg should be a dict that maps bus attribute names - to obj signal names - - Kwargs: - optional_signals (list/dict): optional signals (see signals argument above for details) - - bus_separator (str): characters to use as separator between bus - name and signal name + """Args: + entity (SimHandle): SimHandle instance to the entity containing the bus. + name (str): Name of the bus. ``None`` for nameless bus, e.g. bus-signals + in an interface or a modport (untested on struct/record, + but could work here as well). + signals (list/dict): In the case of an obj (passed to drive/capture) that + has the same attribute names as the signal names of the bus, + the signals argument can be a list of those names. + When obj has different attribute names, the signals arg should be + a dict that maps bus attribute names to obj signal names. + optional_signals (list/dict, optional): Signals that don't have to be present + on the interface. + See ``signals`` argument above for details. + bus_separator (str, optional): Character(s) to use as separator between bus + name and signal name. Defaults to '_'. + array_idx (int or None, optional): Optional index when signal is an array. """ self._entity = entity self._name = name @@ -120,18 +111,14 @@ def _add_signal(self, attr_name, signame): self._signals[attr_name] = getattr(self, attr_name) def drive(self, obj, strict=False): - """ - Drives values onto the bus. + """Drives values onto the bus. Args: - obj (any type) : object with attribute names that match the bus - signals - - Kwargs: - strict (bool) : Check that all signals are being assigned + obj: Object with attribute names that match the bus signals. + strict (bool, optional): Check that all signals are being assigned. Raises: - AttributeError + AttributeError: If not all signals have been assigned when ``strict=True``. """ for attr_name, hdl in self._signals.items(): if not hasattr(obj, attr_name): @@ -148,11 +135,14 @@ def drive(self, obj, strict=False): hdl <= val def capture(self): - """ - Capture the values from the bus, returning an object representing the capture + """Capture the values from the bus, returning an object representing the capture. Returns: - A dict that supports access by attribute, each attribute corresponds to each signal's value + dict: A dictionary that supports access by attribute, + where each attribute corresponds to each signal's value. + Raises: + RuntimeError: If signal not present in bus, + or attempt to modify a bus capture. """ class _Capture(dict): def __getattr__(self, name): @@ -174,18 +164,15 @@ def __delattr__(self, name): return _capture def sample(self, obj, strict=False): - """ - Sample the values from the bus, assigning them to obj. + """Sample the values from the bus, assigning them to ``obj``. Args: - obj (any type) : object with attribute names that match the bus - signals - - Kwargs: - strict (bool) : Check that all signals being sampled are present in obj + obj: Object with attribute names that match the bus signals. + strict (bool, optional): Check that all signals being sampled + are present in ``obj``. Raises: - AttributeError + AttributeError: If attribute is missing in ``obj`` when ``strict=True``. """ for attr_name, hdl in self._signals.items(): if not hasattr(obj, attr_name): @@ -198,9 +185,9 @@ def sample(self, obj, strict=False): raise AttributeError(msg) else: continue - #Try to use the get/set_binstr methods because they will not clobber the properties - #of obj.attr_name on assignment. Otherwise use setattr() to crush whatever type of - #object was in obj.attr_name with hdl.value: + # Try to use the get/set_binstr methods because they will not clobber the properties + # of obj.attr_name on assignment. Otherwise use setattr() to crush whatever type of + # object was in obj.attr_name with hdl.value: try: getattr(obj, attr_name).set_binstr(hdl.value.get_binstr()) except AttributeError: diff --git a/cocotb/clock.py b/cocotb/clock.py index bdfc552c..9a0dad1a 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -26,8 +26,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' """ - A clock class +A clock class """ + import os import itertools @@ -50,13 +51,14 @@ def __init__(self, signal): class Clock(BaseClock): - """Simple 50:50 duty cycle clock driver + """Simple 50:50 duty cycle clock driver. Instances of this class should call its ``start`` method and fork the result. This will create a clocking thread that drives the signal at the desired period/frequency. Example: + .. code-block:: python c = Clock(dut.clk, 10, 'ns') diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 6efd88e8..6b94be03 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -239,9 +239,9 @@ def _handle_error_message(self, msg): class coroutine(object): """Decorator class that allows us to provide common coroutine mechanisms: - log methods will will log to cocotb.coroutines.name + ``log`` methods will will log to ``cocotb.coroutines.name`` - join() method returns an event which will fire when the coroutine exits + ``join()`` method returns an event which will fire when the coroutine exits """ def __init__(self, func): @@ -282,8 +282,7 @@ class function(object): """Decorator class that allows a function to block This allows a function to internally block while - externally appear to yield - + externally appear to yield. """ def __init__(self, func): self._func = func @@ -312,9 +311,9 @@ def __get__(self, obj, type=None): @public class external(object): - """Decorator to apply to an external function to enable calling from cocotb + """Decorator to apply to an external function to enable calling from cocotb. This currently creates a new execution context for each function that is - call. Scope for this to be streamlined to a queue in future + called. Scope for this to be streamlined to a queue in future. """ def __init__(self, func): self._func = func @@ -373,18 +372,18 @@ class test(coroutine): some common reporting etc, a test timeout and allows us to mark tests as expected failures. - KWargs: - timeout: (int) - value representing simulation timeout (not implemented) - expect_fail: (bool): - Don't mark the result as a failure if the test fails - expect_error: (bool): - Don't make the result as an error if an error is raised - This is for cocotb internal regression use - skip: (bool): - Don't execute this test as part of the regression - stage: (int) - Order tests logically into stages, where multiple tests can share a stage + Args: + timeout (int, optional): + value representing simulation timeout (not implemented). + expect_fail (bool, optional): + Don't mark the result as a failure if the test fails. + expect_error (bool, optional): + Don't mark the result as an error if an error is raised. + This is for cocotb internal regression use. + skip (bool, optional): + Don't execute this test as part of the regression. + stage (int, optional) + Order tests logically into stages, where multiple tests can share a stage. """ def __init__(self, timeout=None, expect_fail=False, expect_error=False, skip=False, stage=None): diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py old mode 100644 new mode 100755 index fce90ce4..7fd173f0 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -1,35 +1,33 @@ #!/bin/env python -''' Copyright (c) 2013 Potential Ventures Ltd -Copyright (c) 2013 SolarFlare Communications Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - -""" - Set of common driver base classes -""" +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Set of common driver base classes.""" import logging from collections import deque @@ -44,10 +42,9 @@ class BitDriver(object): - """ - Drives a signal onto a single bit + """Drives a signal onto a single bit. - Useful for exercising ready / valid + Useful for exercising ready / valid. """ def __init__(self, signal, clk, generator=None): self._signal = signal @@ -81,17 +78,13 @@ def _cr_twiddler(self, generator=None): class Driver(object): - """ - - Class defining the standard interface for a driver within a testbench + """Class defining the standard interface for a driver within a testbench. The driver is responsible for serialising transactions onto the physical pins of the interface. This may consume simulation time. """ def __init__(self): - """ - Constructor for a driver instance - """ + """Constructor for a driver instance.""" # self._busy = Lock() self._pending = Event(name="Driver._pending") self._sendQ = deque() @@ -109,67 +102,69 @@ def kill(self): self._thread = None def append(self, transaction, callback=None, event=None, **kwargs): - """ - Queue up a transaction to be sent over the bus. + """Queue up a transaction to be sent over the bus. Mechanisms are provided to permit the caller to know when the - transaction is processed - - callback: optional function to be called when the transaction has been - sent - - event: event to be set when the tansaction has been sent + transaction is processed. - **kwargs: Any additional arguments used in child class' _driver_send method + Args: + transaction (any): The transaction to be sent. + callback (callable, optional): Optional function to be called + when the transaction has been sent. + event (optional): event to be set when the transaction has been sent. + **kwargs: Any additional arguments used in child class' + ``_driver_send`` method. """ self._sendQ.append((transaction, callback, event, kwargs)) self._pending.set() def clear(self): - """ - Clear any queued transactions without sending them onto the bus - """ + """Clear any queued transactions without sending them onto the bus.""" self._sendQ = deque() @coroutine def send(self, transaction, sync=True, **kwargs): - """ - Blocking send call (hence must be "yielded" rather than called) + """Blocking send call (hence must be "yielded" rather than called). - Sends the transaction over the bus + Sends the transaction over the bus. Args: - transaction (any): the transaction to send - - Kwargs: - sync (boolean): synchronise the transfer by waiting for risingedge - kwargs (dict): Adddition arguments used in child class' _driver_send method + transaction (any): The transaction to be sent. + sync (bool, optional): Synchronise the transfer by waiting for rising edge. + **kwargs (dict): Additional arguments used in child class' + ``_driver_send`` method. """ yield self._send(transaction, None, None, sync=sync, **kwargs) def _driver_send(self, transaction, sync=True, **kwargs): """ - actual impementation of the send. + Actual implementation of the send. - subclasses should override this method to implement the actual send - routine + Subclasses should override this method to implement the actual ``send`` + routine. Args: - transaction (any): the transaction to send - - Kwargs: - sync (boolean): synchronise the transfer by waiting for rising edge - kwargs (dict): additional arguments if required for protocol implemented in subclass + transaction (any): The transaction to be sent. + sync (boolean, optional): Synchronise the transfer by waiting for rising edge. + **kwargs: Additional arguments if required for protocol implemented in subclass. """ raise NotImplementedError("Subclasses of Driver should define a " "_driver_send coroutine") @coroutine def _send(self, transaction, callback, event, sync=True, **kwargs): - """ - assumes the caller has already acquired the busy lock + """Assumes the caller has already acquired the busy lock. + + Releases busy lock once sending is complete. FIXME: not true - releases busy lock once sending is complete + Args: + transaction (any): The transaction to be sent. + callback (callable, optional): Optional function to be called + when the transaction has been sent. + event (optional): event to be set when the transaction has been sent. + sync (boolean, optional): Synchronise the transfer by waiting for rising edge. + **kwargs: Any additional arguments used in child class' + ``_driver_send`` method. """ yield self._driver_send(transaction, sync=sync, **kwargs) @@ -204,26 +199,23 @@ def _send_thread(self): class BusDriver(Driver): - """ - Wrapper around common functionality for busses which have: - a list of _signals (class attribute) - a list of _optional_signals (class attribute) - a clock - a name - an entity + """Wrapper around common functionality for busses which have: + * a list of _signals (class attribute) + * a list of _optional_signals (class attribute) + * a clock + * a name + * an entity """ _optional_signals = [] def __init__(self, entity, name, clock, **kwargs): - """ - Args: - entity (SimHandle) : a handle to the simulator entity + """Args: + entity (SimHandle): A handle to the simulator entity. - name (str) : name of this bus. None for nameless bus, e.g. - bus-signals in an interface or a modport - (untested on struct/record, - but could work here as well) - clock (SimHandle) : A handle to the clock associated with this bus + name (str): Name of this bus. ``None`` for nameless bus, e.g. + bus-signals in an interface or a modport + (untested on struct/record, but could work here as well). + clock (SimHandle): A handle to the clock associated with this bus. """ self.log = SimLog("cocotb.%s.%s" % (entity._name, name)) Driver.__init__(self) @@ -247,7 +239,7 @@ def _wait_for_signal(self, signal): """This method will return with the specified signal has hit logic 1. The state will be in the ReadOnly phase so sim will need to move to NextTimeStep before - registering more callbacks can occour + registering more callbacks can occur. """ yield ReadOnly() while signal.value.integer != 1: @@ -260,7 +252,7 @@ def _wait_for_nsignal(self, signal): """This method will return with the specified signal has hit logic 0. The state will be in the ReadOnly phase so sim will need to move to NextTimeStep before - registering more callbacks can occour + registering more callbacks can occur. """ yield ReadOnly() while signal.value.integer != 0: @@ -274,33 +266,29 @@ def __str__(self): class ValidatedBusDriver(BusDriver): - """ - Same as a BusDriver except we support an optional generator to control - which cycles are valid + """Same as a BusDriver except we support an optional generator to control + which cycles are valid. """ def __init__(self, entity, name, clock, **kwargs): - """ - Args: - entity (SimHandle) : a handle to the simulator entity - - name (str) : name of this bus - - clock (SimHandle) : A handle to the clock associated with this bus + # FIXME: "Kwargs:" section not understood like this by Sphinx Napoleon + """Args: + entity (SimHandle): A handle to the simulator entity. + name (str): Name of this bus. + clock (SimHandle): A handle to the clock associated with this bus. Kwargs: valid_generator (generator): a generator that yields tuples of - (valid, invalid) cycles to insert + (valid, invalid) cycles to insert. """ valid_generator = kwargs.pop("valid_generator", None) BusDriver.__init__(self, entity, name, clock, **kwargs) self.set_valid_generator(valid_generator=valid_generator) def _next_valids(self): - """ - Optionally insert invalid cycles every N cycles + """Optionally insert invalid cycles every N cycles Generator should return a tuple with the number of cycles to be on followed by the number of cycles to be off. - The 'on' cycles should be non-zero, we skip invalid generator entries + The 'on' cycles should be non-zero, we skip invalid generator entries. """ self.on = False @@ -323,19 +311,15 @@ def _next_valids(self): self.log.debug("Not using valid generator") def set_valid_generator(self, valid_generator=None): - """ - Set a new valid generator for this bus - """ - + """Set a new valid generator for this bus.""" self.valid_generator = valid_generator self._next_valids() @cocotb.coroutine def polled_socket_attachment(driver, sock): - """ - Non-blocking socket attachment that queues any payload received from the - socket to be queued for sending into the driver + """Non-blocking socket attachment that queues any payload received from the + socket to be queued for sending into the driver. """ import socket, errno sock.setblocking(False) diff --git a/cocotb/drivers/ad9361.py b/cocotb/drivers/ad9361.py index 3981eeee..49502a3b 100644 --- a/cocotb/drivers/ad9361.py +++ b/cocotb/drivers/ad9361.py @@ -1,8 +1,6 @@ -''' -Created on Aug 24, 2014 - -@author: msnook -''' +# Created on Aug 24, 2014 +# +# @author: msnook import cocotb from cocotb.triggers import Timer, RisingEdge, ReadOnly, Lock, Event @@ -15,16 +13,11 @@ class AD9361(BusDriver): - ''' - classdocs - ''' + """Driver for the AD9361 RF Transceiver.""" def __init__(self, dut, rx_channels=1, tx_channels=1, tx_clock_half_period=16276, rx_clock_half_period=16276, loopback_queue_maxlen=16): - ''' - Constructor - ''' self.dut = dut self.tx_clock_half_period = tx_clock_half_period self.rx_clock_half_period = rx_clock_half_period @@ -48,6 +41,16 @@ def _rx_clock(self): def send_data(self, i_data, q_data, i_data2=None, q_data2=None, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT): + """Forks the ``rx_data_to_ad9361`` coroutine to send data. + + Args: + i_data (int): Data of the I0 channel. + q_data (int): Data of the Q0 channel. + i_data2 (int, optional): Data of the I1 channel. + q_data2 (int, optional): Data of the Q1 channel. + binaryRepresentation (BinaryRepresentation): The representation of the binary value. + Default is ``BinaryRepresentation.TWOS_COMPLEMENT``. + """ print(binaryRepresentation) cocotb.fork(self.rx_data_to_ad9361(i_data, q_data, i_data2, q_data2, binaryRepresentation)) @@ -55,6 +58,18 @@ def send_data(self, i_data, q_data, i_data2=None, q_data2=None, @cocotb.coroutine def rx_data_to_ad9361(self, i_data, q_data, i_data2=None, q_data2=None, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT): + """Receive data to AD9361. + + This is a coroutine. + + Args: + i_data (int): Data of the I0 channel. + q_data (int): Data of the Q0 channel. + i_data2 (int, optional): Data of the I1 channel. + q_data2 (int, optional): Data of the Q1 channel. + binaryRepresentation (BinaryRepresentation): The representation of the binary value. + Default is ``BinaryRepresentation.TWOS_COMPLEMENT``. + """ i_bin_val = BinaryValue(n_bits=12, bigEndian=False, binaryRepresentation=binaryRepresentation) q_bin_val = BinaryValue(n_bits=12, bigEndian=False, @@ -187,7 +202,15 @@ def _ad9361_tx_to_rx_loopback(self): self.dut.rx_data_in_n <= ~q_bin_val[5:0] def ad9361_tx_to_rx_loopback(self): + """Create loopback from tx to rx. + + Forks a coroutine doing the actual task. + """ cocotb.fork(self._ad9361_tx_to_rx_loopback()) def tx_data_from_ad9361(self): + """Transmit data from AD9361. + + Forks a coroutine doing the actual task. + """ cocotb.fork(self._tx_data_from_ad9361()) diff --git a/cocotb/drivers/amba.py b/cocotb/drivers/amba.py index 672614e2..3d7b7fe0 100644 --- a/cocotb/drivers/amba.py +++ b/cocotb/drivers/amba.py @@ -70,7 +70,7 @@ def __init__(self, entity, name, clock): @cocotb.coroutine def _send_write_address(self, address, delay=0): """ - Send the write address, with optional delay + Send the write address, with optional delay (in clocks) """ yield self.write_address_busy.acquire() for cycle in range(delay): @@ -91,7 +91,7 @@ def _send_write_address(self, address, delay=0): @cocotb.coroutine def _send_write_data(self, data, delay=0, byte_enable=0xF): """ - Send the write address, with optional delay + Send the write address, with optional delay (in clocks) """ yield self.write_data_busy.acquire() for cycle in range(delay): @@ -116,9 +116,23 @@ def write(self, address, value, byte_enable=0xf, address_latency=0, """ Write a value to an address. - The *_latency KWargs allow control over the delta - - sync dictates whether we wait for a clock edge or not + Args: + address (int): The address to write to + value (int): The data value to write + byte_enable (int, optional): Which bytes in value to actually write. + Default is to write all bytes. + address_latency (int, optional): Delay before setting the address (in clock cycles). + Default is no delay. + data_latency (int, optional): Delay before setting the data value (in clock cycles). + Default is no delay. + sync (bool, optional): Wait for rising edge on clock initially. + Defaults to True. + + Returns: + BinaryValue: The write response value + + Raises: + AXIProtocolError: If write response from AXI is not ``OKAY`` """ if sync: yield RisingEdge(self.clock) @@ -154,6 +168,17 @@ def write(self, address, value, byte_enable=0xf, address_latency=0, def read(self, address, sync=True): """ Read from an address. + + Args: + address (int): The address to read from + sync (bool, optional): Wait for rising edge on clock initially. + Defaults to True. + + Returns: + BinaryValue: The read data value + + Raises: + AXIProtocolError: If read response from AXI is not ``OKAY`` """ if sync: yield RisingEdge(self.clock) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 5024fee9..0375b8ad 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -1,29 +1,30 @@ -''' Copyright (c) 2013 Potential Ventures Ltd -Copyright (c) 2013 SolarFlare Communications Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + """ Drivers for Altera Avalon interfaces. @@ -31,6 +32,7 @@ NB Currently we only support a very small subset of functionality """ + import random import cocotb @@ -43,13 +45,13 @@ class AvalonMM(BusDriver): - """Avalon-MM Driver + """Avalon Memory Mapped Interface (Avalon-MM) Driver. Currently we only support the mode required to communicate with SF - avalon_mapper which is a limited subset of all the signals + avalon_mapper which is a limited subset of all the signals. Blocking operation is all that is supported at the moment, and for the near - future as well + future as well. Posted responses from a slave are not supported. """ _signals = ["address"] @@ -93,8 +95,7 @@ def write(self, address, value): class AvalonMaster(AvalonMM): - """Avalon-MM master - """ + """Avalon Memory Mapped Interface (Avalon-MM) Master""" def __init__(self, entity, name, clock, **kwargs): AvalonMM.__init__(self, entity, name, clock, **kwargs) self.log.debug("AvalonMaster created") @@ -117,11 +118,21 @@ def _release_lock(self): @coroutine def read(self, address, sync=True): - """ - Issue a request to the bus and block until this + """Issue a request to the bus and block until this comes back. Simulation time still progresses but syntactically it blocks. See http://www.altera.com/literature/manual/mnl_avalon_spec_1_3.pdf + + Args: + address (int): The address to read from. + sync (bool, optional): Wait for rising edge on clock initially. + Defaults to True. + + Returns: + BinaryValue: The read data value. + + Raises: + TestError: If master is write-only. """ if not self._can_read: self.log.error("Cannot read - have no read signal") @@ -174,10 +185,16 @@ def read(self, address, sync=True): @coroutine def write(self, address, value): - """ - Issue a write to the given address with the specified + """Issue a write to the given address with the specified value. See http://www.altera.com/literature/manual/mnl_avalon_spec_1_3.pdf + + Args: + address (int): The address to write to. + value (int): The data value to write. + + Raises: + TestError: If master is read-only. """ if not self._can_write: self.log.error("Cannot write - have no write signal") @@ -185,7 +202,7 @@ def write(self, address, value): yield self._acquire_lock() - # Apply valuse to bus + # Apply values to bus yield RisingEdge(self.clock) self.bus.address <= address self.bus.writedata <= value @@ -217,9 +234,7 @@ def write(self, address, value): class AvalonMemory(BusDriver): - """ - Emulate a memory, with back-door access - """ + """Emulate a memory, with back-door access.""" _signals = ["address"] _optional_signals = ["write", "read", "writedata", "readdatavalid", "readdata", "waitrequest", "burstcount", "byteenable"] @@ -300,7 +315,7 @@ def __init__(self, entity, name, clock, readlatency_min=1, self.bus.readdatavalid.setimmediatevalue(0) def _pad(self): - """Pad response queue up to read latency""" + """Pad response queue up to read latency.""" l = random.randint(self._readlatency_min, self._readlatency_max) while len(self._responses) < l: self._responses.append(None) @@ -325,7 +340,7 @@ def _do_response(self): self.bus.readdatavalid <= 0 def _write_burst_addr(self): - """ reading write burst address, burstcount, byteenable """ + """Reading write burst address, burstcount, byteenable.""" addr = self.bus.address.value.integer if addr % self.dataByteSize != 0: self.log.error("Address must be aligned to data width" + @@ -347,7 +362,7 @@ def _write_burst_addr(self): @coroutine def _writing_byte_value(self, byteaddr): - """Writing value in _mem with byteaddr size """ + """Writing value in _mem with byteaddr size.""" yield FallingEdge(self.clock) for i in range(self.dataByteSize): data = self.bus.writedata.value.integer @@ -357,7 +372,7 @@ def _writing_byte_value(self, byteaddr): @coroutine def _waitrequest(self): - """ generate waitrequest randomly """ + """Generate waitrequest randomly.""" if self._avalon_properties.get("WriteBurstWaitReq", True): if random.choice([True, False, False, False]): randmax = self._avalon_properties.get("MaxWaitReqLen", 0) @@ -373,9 +388,7 @@ def _waitrequest(self): @coroutine def _respond(self): - """ - Coroutine to response to the actual requests - """ + """Coroutine to respond to the actual requests.""" edge = RisingEdge(self.clock) while True: yield edge @@ -449,7 +462,7 @@ def _respond(self): byteenable = int(self.bus.byteenable.value) mask = 0 oldmask = 0 - olddata= 0 + olddata = 0 if (addr in self._mem): olddata = self._mem[addr] self.log.debug("Old Data : %x" % olddata) @@ -496,6 +509,8 @@ def _respond(self): class AvalonST(ValidatedBusDriver): + """Avalon Streaming Interface (Avalon-ST) Driver""" + _signals = ["valid", "data"] _optional_signals = ["ready"] @@ -520,7 +535,7 @@ def __init__(self, *args, **kwargs): @coroutine def _wait_ready(self): - """Wait for a ready cycle on the bus before continuing + """Wait for a ready cycle on the bus before continuing. Can no longer drive values this cycle... @@ -533,12 +548,12 @@ def _wait_ready(self): @coroutine def _driver_send(self, value, sync=True): - """Send a transmission over the bus + """Send a transmission over the bus. Args: - value: data to drive onto the bus + value: data to drive onto the bus. """ - self.log.debug("Sending avalon transmission: %d" % value) + self.log.debug("Sending Avalon transmission: %d" % value) # Avoid spurious object creation by recycling clkedge = RisingEdge(self.clock) @@ -579,10 +594,12 @@ def _driver_send(self, value, sync=True): word.binstr = ("x"*len(self.bus.data)) self.bus.data <= word - self.log.debug("Successfully sent avalon transmission: %d" % value) + self.log.debug("Successfully sent Avalon transmission: %d" % value) class AvalonSTPkts(ValidatedBusDriver): + """Avalon Streaming Interface (Avalon-ST) Driver, packetised.""" + _signals = ["valid", "data", "startofpacket", "endofpacket"] _optional_signals = ["error", "channel", "ready", "empty"] @@ -639,7 +656,7 @@ def __init__(self, *args, **kwargs): if hasattr(self.bus, 'channel'): if len(self.bus.channel) > 128: raise AttributeError( - "AvalonST interface specification defines channel width as 1-128. %d channel width is %d" % + "Avalon-ST interface specification defines channel width as 1-128. %d channel width is %d" % (self.name, len(self.bus.channel)) ) maxChannel = (2 ** len(self.bus.channel)) -1 @@ -653,7 +670,7 @@ def __init__(self, *args, **kwargs): @coroutine def _wait_ready(self): - """Wait for a ready cycle on the bus before continuing + """Wait for a ready cycle on the bus before continuing. Can no longer drive values this cycle... @@ -666,10 +683,9 @@ def _wait_ready(self): @coroutine def _send_string(self, string, sync=True, channel=None): - """ - Args: - string (str): A string of bytes to send over the bus - channel (int): Channel to send the data on + """Args: + string (str): A string of bytes to send over the bus. + channel (int): Channel to send the data on. """ # Avoid spurious object creation by recycling clkedge = RisingEdge(self.clock) @@ -771,10 +787,9 @@ def _send_string(self, string, sync=True, channel=None): @coroutine def _send_iterable(self, pkt, sync=True): - """ - Args: + """Args: pkt (iterable): Will yield objects with attributes matching the - signal names for each individual bus cycle + signal names for each individual bus cycle. """ clkedge = RisingEdge(self.clock) firstword = True @@ -813,21 +828,19 @@ def _send_iterable(self, pkt, sync=True): @coroutine def _driver_send(self, pkt, sync=True, channel=None): - """Send a packet over the bus + """Send a packet over the bus. Args: - pkt (str or iterable): packet to drive onto the bus - channel (None or int): channel attributed to the packet + pkt (str or iterable): Packet to drive onto the bus. + channel (None or int): Channel attributed to the packet. - If pkt is a string, we simply send it word by word + If ``pkt`` is a string, we simply send it word by word - If pkt is an iterable, it's assumed to yield objects with attributes - matching the signal names + If ``pkt`` is an iterable, it's assumed to yield objects with + attributes matching the signal names. """ # Avoid spurious object creation by recycling - - if isinstance(pkt, str): self.log.debug("Sending packet of length %d bytes" % len(pkt)) self.log.debug(hexdump(pkt)) diff --git a/cocotb/drivers/opb.py b/cocotb/drivers/opb.py index 9a01236d..bd3fdb5b 100644 --- a/cocotb/drivers/opb.py +++ b/cocotb/drivers/opb.py @@ -1,34 +1,35 @@ -''' -Copyright (c) 2015 Potential Ventures Ltd -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' +# Copyright (c) 2015 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + """ -Drivers for On-chip Peripheral Bus +Drivers for On-chip Peripheral Bus. NB Currently we only support a very small subset of functionality """ + import random import cocotb @@ -42,9 +43,7 @@ class OPBException(Exception): class OPBMaster(BusDriver): - """ - On-chip peripheral bus master - """ + """On-chip peripheral bus master.""" _signals = ["xferAck", "errAck", "toutSup", "retry", "DBus_out", "select", "RNW", "BE", "ABus", "DBus_in"] _optional_signals = ["seqAddr"] @@ -74,6 +73,17 @@ def read(self, address, sync=True): Issue a request to the bus and block until this comes back. Simulation time still progresses but syntactically it blocks. + + Args: + address (int): The address to read from. + sync (bool, optional): Wait for rising edge on clock initially. + Defaults to True. + + Returns: + BinaryValue: The read data value. + + Raises: + OPBException: If read took longer than 16 cycles. """ yield self._acquire_lock() @@ -105,7 +115,17 @@ def read(self, address, sync=True): @cocotb.coroutine def write(self, address, value, sync=True): - """ + """Issue a write to the given address with the specified + value. + + Args: + address (int): The address to read from. + value (int): The data value to write. + sync (bool, optional): Wait for rising edge on clock initially. + Defaults to True. + + Raises: + OPBException: If write took longer than 16 cycles """ yield self._acquire_lock() diff --git a/cocotb/drivers/xgmii.py b/cocotb/drivers/xgmii.py index ac842c19..6c7a17a7 100644 --- a/cocotb/drivers/xgmii.py +++ b/cocotb/drivers/xgmii.py @@ -1,31 +1,29 @@ -''' Copyright (c) 2013 Potential Ventures Ltd -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - -""" -Drivers for XGMII -""" +# Copyright (c) 2013 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Drivers for XGMII (10 Gigabit Media Independent Interface).""" import struct import zlib @@ -47,34 +45,31 @@ class _XGMIIBus(object): - """ - Helper object for abstracting the underlying bus format + """Helper object for abstracting the underlying bus format. - Index bytes directly on this object, pass a tuple of (value, ctrl) to + Index bytes directly on this object, pass a tuple of ``(value, ctrl)`` to set a byte. For example: >>> xgmii = _XGMIIBus(4) - >>> xgmii[0] = (_XGMII_IDLE, True) # Control byte - >>> xgmii[1] = ("\x55", False) # Data byte + >>> xgmii[0] = (_XGMII_IDLE, True) # Control byte + >>> xgmii[1] = ("\x55", False) # Data byte """ def __init__(self, nbytes, interleaved=True): - """ - Args: - nbytes (int): The number of bytes transferred per clock cycle - (usually 8 for SDR, 4 for DDR) + """Args: + nbytes (int): The number of bytes transferred per clock cycle + (usually 8 for SDR, 4 for DDR). - Kwargs: - interleaved (bool): The arrangement of control bits on the bus. + interleaved (bool, optional): The arrangement of control bits on the bus. - If interleaved we have a bus with 9-bits per - byte, the control bit being the 9th bit of each - byte. + If interleaved we have a bus with 9-bits per + byte, the control bit being the 9th bit of each + byte. - If not interleaved then we have a byte per data - byte plus a control bit per byte in the MSBs. + If not interleaved then we have a byte per data + byte plus a control bit per byte in the MSBs. """ self._value = BinaryValue(n_bits=nbytes*9, bigEndian=False) @@ -107,11 +102,10 @@ def __setitem__(self, index, value): @property def value(self): - """ - Get the integer representation of this data word suitable for driving + """Get the integer representation of this data word suitable for driving onto the bus. - NB clears the value + NB clears the value. """ self._value.integer = self._integer self._integer = long(0) @@ -122,28 +116,21 @@ def __len__(self): class XGMII(Driver): - """ - XGMII driver - """ + """XGMII (10 Gigabit Media Independent Interface) driver.""" def __init__(self, signal, clock, interleaved=True): - """ - Args: - signal (SimHandle): The xgmii data bus - - clock (SimHandle): The associated clock (assumed to be - driven by another coroutine) - - Kwargs: - interleaved (bool: Whether control bits are interleaved - with the data bytes or not. + """Args: + signal (SimHandle): The xgmii data bus. + clock (SimHandle): The associated clock (assumed to be + driven by another coroutine). + interleaved (bool, optional): Whether control bits are interleaved + with the data bytes or not. If interleaved the bus is - byte0, byte0_control, byte1, byte1_control .... - - Otherwise expect: + byte0, byte0_control, byte1, byte1_control, ... - byte0, byte1, ..., byte0_control, byte1_control... + Otherwise expect + byte0, byte1, ..., byte0_control, byte1_control, ... """ self.log = signal._log self.signal = signal @@ -153,10 +140,15 @@ def __init__(self, signal, clock, interleaved=True): @staticmethod def layer1(packet): - """Take an Ethernet packet (as a string) and format as a layer 1 packet + """Take an Ethernet packet (as a string) and format as a layer 1 packet. + + Pad to 64 bytes, prepend preamble and append 4-byte CRC on the end. + + Args: + packet (str): The Ethernet packet to format. - Pads to 64-bytes, - prepends preamble and appends 4-byte CRC on the end + Returns: + str: The formatted layer 1 packet. """ if len(packet) < 60: padding = "\x00" * (60 - len(packet)) @@ -165,13 +157,17 @@ def layer1(packet): struct.pack(">> tf = TestFactory(run_test) >>> tf.add_option('data_in', [gen_a, gen_b]) @@ -449,18 +448,18 @@ class TestFactory(object): >>> tf.generate_tests() We would get the following tests: - * gen_a with no backpressure and no idles - * gen_a with no backpressure and random_idles - * gen_a with random_backpressure and no idles - * gen_a with random_backpressure and random_idles - * gen_b with no backpressure and no idles - * gen_b with no backpressure and random_idles - * gen_b with random_backpressure and no idles - * gen_b with random_backpressure and random_idles + * ``gen_a`` with no backpressure and no idles + * ``gen_a`` with no backpressure and ``random_idles`` + * ``gen_a`` with ``random_backpressure`` and no idles + * ``gen_a`` with ``random_backpressure`` and ``random_idles`` + * ``gen_b`` with no backpressure and no idles + * ``gen_b`` with no backpressure and ``random_idles`` + * ``gen_b`` with ``random_backpressure`` and no idles + * ``gen_b`` with ``random_backpressure`` and ``random_idles`` The tests are appended to the calling module for auto-discovery. - Tests are simply named test_function_N. The docstring for the test (hence + Tests are simply named ``test_function_N``. The docstring for the test (hence the test description) includes the name and description of each generator. """ @@ -492,10 +491,10 @@ def add_option(self, name, optionlist): """Add a named option to the test. Args: - name (string): name of the option. passed to test as a keyword - argument + name (string): Name of the option. Passed to test as a keyword + argument. - optionlist (list): A list of possible options for this test knob + optionlist (list): A list of possible options for this test knob. """ self.kwargs[name] = optionlist @@ -508,13 +507,13 @@ def generate_tests(self, prefix="", postfix=""): module. Args: - prefix: Text string to append to start of test_function name + prefix (str): Text string to append to start of ``test_function`` name when naming generated test cases. This allows reuse of - a single test_function with multiple TestFactories without + a single ``test_function`` with multiple TestFactories without name clashes. - postfix: Text string to append to end of test_function name + postfix (str): Text string to append to end of ``test_function`` name when naming generated test cases. This allows reuse of - a single test_function with multiple TestFactories without + a single ``test_function`` with multiple TestFactories without name clashes. """ @@ -549,7 +548,7 @@ def generate_tests(self, prefix="", postfix=""): kwargs.update(testoptions) if hasattr(mod, name): cocotb.log.error("Overwriting %s in module %s. " - "This causes previously defined testcase " + "This causes a previously defined testcase " "not to be run. Consider setting/changing " "name_postfix" % (name, mod)) setattr(mod, name, _create_test(self.test_function, name, doc, mod, diff --git a/cocotb/result.py b/cocotb/result.py index e78854e7..e68e8c1a 100644 --- a/cocotb/result.py +++ b/cocotb/result.py @@ -1,31 +1,31 @@ -''' Copyright (c) 2013 Potential Ventures Ltd -Copyright (c) 2013 SolarFlare Communications Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - -# TODO: Coule use cStringIO? +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# TODO: Could use cStringIO? import traceback import sys # from StringIO import StringIO @@ -33,11 +33,11 @@ def raise_error(obj, msg): - """ - Creates a TestError exception and raises it after printing a traceback + """Creates a TestError exception and raises it after printing a traceback. - obj has a log method - msg is a string + Args: + obj: Object with a log method. + msg (str): The log message. """ exc_info = sys.exc_info() # 2.6 cannot use named access @@ -55,9 +55,12 @@ def raise_error(obj, msg): def create_error(obj, msg): - """ - As above, but return the exception rather than raise it, simply to avoid - too many levels of nested try/except blocks + """Like `raise_error`, but return the exception rather than raise it, + simply to avoid too many levels of nested try/except blocks. + + Args: + obj: Object with a log method. + msg (str): The log message. """ try: raise_error(obj, msg) @@ -72,9 +75,7 @@ def __init__(self, retval): class TestComplete(Exception): - """ - Exceptions are used to pass test results around. - """ + """Exception showing that test was completed. Sub-exceptions detail the exit status.""" def __init__(self, *args, **kwargs): super(TestComplete, self).__init__(*args, **kwargs) self.stdout = StringIO() @@ -87,16 +88,20 @@ def __init__(self, exception): class TestError(TestComplete): + """Exception showing that test was completed with severity Error.""" pass class TestFailure(TestComplete): + """Exception showing that test was completed with severity Failure.""" pass class TestSuccess(TestComplete): + """Exception showing that test was completed successfully.""" pass class SimFailure(TestComplete): + """Exception showing that simulator exited unsuccessfully.""" pass diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 2ddf1edb..555e4977 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -137,7 +137,7 @@ def __str__(self): class ReadOnly(with_metaclass(ParametrizedSingleton, GPITrigger)): """ Execution will resume when the readonly portion of the sim cycles is - readched + reached """ @classmethod def __singleton_key__(cls): @@ -239,17 +239,17 @@ def __str__(self): class RisingEdge(_EdgeBase): - """ Triggers on the rising edge of the provided signal """ + """Triggers on the rising edge of the provided signal""" _edge_type = 1 class FallingEdge(_EdgeBase): - """ Triggers on the falling edge of the provided signal """ + """Triggers on the falling edge of the provided signal""" _edge_type = 2 class Edge(_EdgeBase): - """ Triggers on either edge in a signal """ + """Triggers on either edge of the provided signal""" _edge_type = 3 @@ -490,7 +490,7 @@ def __nonzero__(self): class NullTrigger(Trigger): """ Trigger for internal interfacing use call the callback as soon - as it is primed and then remove it's self from the scheduler + as it is primed and then remove itself from the scheduler """ def __init__(self, name=""): Trigger.__init__(self) diff --git a/cocotb/utils.py b/cocotb/utils.py index cffa2a98..80ac3e55 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -1,31 +1,31 @@ from __future__ import print_function -''' Copyright (c) 2013, 2018 Potential Ventures Ltd -Copyright (c) 2013 SolarFlare Communications Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' +# Copyright (c) 2013, 2018 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """Collection of handy functions""" @@ -54,15 +54,15 @@ def get_python_integer_types(): # Simulator helper functions def get_sim_time(units=None): - """Retrieves the simulation time from the simulator + """Retrieves the simulation time from the simulator. Args: - units (str, optional): String specifying the units of the result. - (None,'fs','ps','ns','us','ms','sec') None will return the raw + units (str or None, optional): String specifying the units of the result + (``None``,'fs','ps','ns','us','ms','sec'). ``None`` will return the raw simulation time. Returns: - The simulation time in the specified units + The simulation time in the specified units. """ timeh, timel = simulator.get_sim_time() @@ -78,9 +78,9 @@ def get_time_from_sim_steps(steps, units): on the simulator precision. Args: - steps (int): Number of simulation steps - units (str): String specifying the units of the result. - ('fs','ps','ns','us','ms','sec') + steps (int): Number of simulation steps + units (str): String specifying the units of the result + ('fs','ps','ns','us','ms','sec'). Returns: The simulation time in the specified units @@ -90,16 +90,19 @@ def get_time_from_sim_steps(steps, units): return result def get_sim_steps(time, units=None): - """Calculates the number of Simulation time steps for a given amount of time + """Calculates the number of simulation time steps for a given amount of time Args: time (int/float): The value to convert to simulation time steps. - units (str, optional): String specifying the units of the result. - (None,'fs','ps','ns','us','ms','sec') None means time is already in + units (str or None, optional): String specifying the units of the result + (``None``,'fs','ps','ns','us','ms','sec'). ``None`` means time is already in simulation time steps. Returns: - The number of simulation time steps + int: The number of simulation time steps + + Raises: + ValueError: If given time cannot be represented by simulator precision """ result = time if units is not None: @@ -118,8 +121,8 @@ def _get_log_time_scale(units): """Retrieves the log10() of the scale factor for a given time unit Args: - units (str): String specifying the units. - ('fs','ps','ns','us','ms','sec') + units (str): String specifying the units + ('fs','ps','ns','us','ms','sec'). Returns: The the log10() of the scale factor for the time unit @@ -142,25 +145,26 @@ def _get_log_time_scale(units): def pack(ctypes_obj): - """Convert a ctypes structure into a python string + """Convert a ctypes structure into a Python string Args: ctypes_obj (ctypes.Structure): ctypes structure to convert to a string Returns: - New python string containing the bytes from memory holding ctypes_obj + New Python string containing the bytes from memory holding ctypes_obj """ return ctypes.string_at(ctypes.addressof(ctypes_obj), ctypes.sizeof(ctypes_obj)) def unpack(ctypes_obj, string, bytes=None): - """Unpack a python string into a ctypes structure + """Unpack a Python string into a ctypes structure Args: ctypes_obj (ctypes.Structure): ctypes structure to pack into string (str): String to copy over the ctypes_obj memory space - bytes (int, optional): Number of bytes to copy. Defaults to None. + bytes (int, optional): Number of bytes to copy. + Defaults to ``None``, meaning the length of the string is used. Raises: ValueError: If length of ``string`` and size of ``ctypes_obj`` @@ -168,8 +172,8 @@ def unpack(ctypes_obj, string, bytes=None): MemoryError: If ``bytes`` is longer than size of ``ctypes_obj``. If the length of the string is not the correct size for the memory - footprint of the ctypes structure then the bytes keyword argument must - be used + footprint of the ctypes structure then the ``bytes`` keyword argument + must be used. """ if bytes is None: if len(string) != ctypes.sizeof(ctypes_obj): @@ -208,11 +212,12 @@ def hexdump(x): A string containing the hexdump Example: + .. code-block:: python print(hexdump('this somewhat long string')) - .. code-block:: + .. code-block:: none 0000 74 68 69 73 20 73 6F 6D 65 77 68 61 74 20 6C 6F this somewhat lo 0010 6E 67 20 73 74 72 69 6E 67 ng string @@ -245,11 +250,12 @@ def hexdiffs(x, y): y: Object that supports conversion via the ``str`` built-in Example: + .. code-block:: python print(hexdiffs('this short thing', 'this also short')) - .. code-block:: + .. code-block:: none 0000 746869732073686F 7274207468696E67 this short thing 0000 7468697320616C73 6F 2073686F7274 this also short @@ -420,20 +426,22 @@ def exec_(_code_, _globs_=None, _locs_=None): # this is six.with_metaclass, with a clearer docstring def with_metaclass(meta, *bases): - """ - This provides: + """This provides: + + .. code-block:: python class Foo(with_metaclass(Meta, Base1, Base2)): pass which is a unifying syntax for: + .. code-block:: python + # python 3 class Foo(Base1, Base2, metaclass=Meta): pass # python 2 - class Foo(Base1, Base2) + class Foo(Base1, Base2): __metaclass__ = Meta - """ # This requires a bit of explanation: the basic idea is to make a dummy # metaclass for one level of class instantiation that replaces itself with @@ -450,8 +458,7 @@ def __prepare__(cls, name, this_bases): class ParametrizedSingleton(type): - """ - A metaclass that allows class construction to reuse an existing instance + """A metaclass that allows class construction to reuse an existing instance We use this so that `RisingEdge(sig)` and `Join(coroutine)` always return the same instance, rather than creating new copies. diff --git a/documentation/source/coroutines.rst b/documentation/source/coroutines.rst index ac2946eb..863012bf 100644 --- a/documentation/source/coroutines.rst +++ b/documentation/source/coroutines.rst @@ -80,26 +80,26 @@ Coroutines can be forked for parallel operation within a function of that code a the forked code. .. code-block:: python + @cocotb.test() def test_act_during_reset(dut): - """ - while reset is active, toggle signals - """ + """While reset is active, toggle signals""" tb = uart_tb(dut) - #Clock is a built in class for toggling a clock signal + #Clock is a built in class for toggling a clock signal cocotb.fork(Clock(dut.clk, 1000).start()) #reset_dut is a function- part of the user generated uart_tb class. cocotb.fork(tb.reset_dut(dut.rstn,20000)) yield Timer(10000) - print("Reset is still active: %d" % dut.rstn) + print("Reset is still active: %d" % dut.rstn) yield Timer(15000) - print("Reset has gone inactive: %d" % dut.rstn) - + print("Reset has gone inactive: %d" % dut.rstn) + Coroutines can be joined to end parallel operation within a function. .. code-block:: python + @cocotb.test() def test_count_edge_cycles(dut, period=1000, clocks=6): cocotb.fork(Clock(dut.clk, period).start()) @@ -124,6 +124,7 @@ Coroutines can be killed before they complete, forcing their completion before they'd naturally end. .. code-block:: python + @cocotb.test() def test_different_clocks(dut): clk_1mhz = Clock(dut.clk, 1.0, units='us') @@ -134,7 +135,7 @@ they'd naturally end. yield Timer(1) yield RisingEdge(dut.clk) edge_time_ns = get_sim_time(units='ns') - # note, isclose is a python 3.5+ feature. + # NOTE: isclose is a python 3.5+ feature. if not isclose(edge_time_ns, start_time_ns + 1000.0): raise TestFailure("Expected a period of 1 us") @@ -145,7 +146,7 @@ they'd naturally end. yield Timer(1) yield RisingEdge(dut.clk) edge_time_ns = get_sim_time(units='ns') - # note, isclose is a python 3.5+ feature + # note, isclose is a python 3.5+ feature if not isclose(edge_time_ns, start_time_ns + 4.0): raise TestFailure("Expected a period of 4 ns") diff --git a/documentation/source/includeme.rst b/documentation/source/includeme.rst deleted file mode 100644 index 071aed38..00000000 --- a/documentation/source/includeme.rst +++ /dev/null @@ -1 +0,0 @@ -.. include :: ../../README.rst diff --git a/documentation/source/index.rst b/documentation/source/index.rst index 1b24ca05..f18281c5 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -6,7 +6,6 @@ Contents: .. toctree:: :maxdepth: 2 - includeme introduction quickstart building diff --git a/documentation/source/library_reference.rst b/documentation/source/library_reference.rst index 41201dce..8acc9abf 100644 --- a/documentation/source/library_reference.rst +++ b/documentation/source/library_reference.rst @@ -117,7 +117,16 @@ Drivers AD9361 ~~~~~~ -.. autoclass:: cocotb.drivers.ad9361.AD9361 + +.. currentmodule:: cocotb.drivers.ad9361 + +.. autoclass:: AD9361 + + .. automethod:: send_data(i_data, q_data, i_data2=None, q_data2=None, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) + .. automethod:: rx_data_to_ad9361(i_data, q_data, i_data2=None, q_data2=None, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) + .. automethod:: ad9361_tx_to_rx_loopback() + .. automethod:: tx_data_from_ad9361() + :members: AMBA @@ -125,41 +134,61 @@ AMBA Advanced Microcontroller Bus Archicture -.. autoclass:: cocotb.drivers.amba.AXI4LiteMaster +.. currentmodule:: cocotb.drivers.amba + +.. autoclass:: AXI4LiteMaster + + .. automethod:: write(address, value, byte_enable=0xf, address_latency=0, data_latency=0) + .. automethod:: read(address, sync=True) + :members: -.. autoclass:: cocotb.drivers.amba.AXI4LiteSlave +.. autoclass:: AXI4Slave :members: Avalon ~~~~~~ -.. autoclass:: cocotb.drivers.avalon.AvalonMM +.. currentmodule:: cocotb.drivers.avalon + +.. autoclass:: AvalonMM :members: -.. autoclass:: cocotb.drivers.avalon.AvalonMaster +.. autoclass:: AvalonMaster + + .. automethod:: write(address, value) + .. automethod:: read(address, sync=True) + :members: -.. autoclass:: cocotb.drivers.avalon.AvalonMemory +.. autoclass:: AvalonMemory :members: -.. autoclass:: cocotb.drivers.avalon.AvalonST +.. autoclass:: AvalonST :members: -.. autoclass:: cocotb.drivers.avalon.AvalonSTPkts +.. autoclass:: AvalonSTPkts :members: OPB ~~~ -.. autoclass:: cocotb.drivers.opb.OPBMaster +.. currentmodule:: cocotb.drivers.opb + +.. autoclass:: OPBMaster + + .. automethod:: write(address, value, sync=True) + .. automethod:: read(address, sync=True) + :members: XGMII ~~~~~ -.. autoclass:: cocotb.drivers.xgmii.XGMII +.. currentmodule:: cocotb.drivers.xgmii + +.. autoclass:: XGMII :members: Monitors @@ -168,10 +197,12 @@ Monitors Avalon ~~~~~~ -.. autoclass:: cocotb.monitors.avalon.AvalonST +.. currentmodule:: cocotb.monitors.avalon + +.. autoclass:: AvalonST :members: -.. autoclass:: cocotb.monitors.avalon.AvalonSTPkts +.. autoclass:: AvalonSTPkts :members: XGMII @@ -189,6 +220,8 @@ Utilities Simulation Object Handles ========================= +.. currentmodule:: cocotb.handle + .. autofunction:: cocotb.handle.SimHandle .. autoclass:: cocotb.handle.SimHandleBase diff --git a/documentation/source/triggers.rst b/documentation/source/triggers.rst index 815bcd36..27c50d55 100644 --- a/documentation/source/triggers.rst +++ b/documentation/source/triggers.rst @@ -41,7 +41,7 @@ Registers a callback that will continue execution of the `coroutine` on a transi FallingEdge(signal) -^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^ Registers a callback that will continue execution of the `coroutine` on a transition from 1 to 0 of signal. From 92ed79a31e8dd0506abac623d3155c5b1eba715e Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 16 Jan 2019 01:01:58 +0100 Subject: [PATCH 1524/2656] Add intersphhinx extension for Python 3. --- documentation/source/conf.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/documentation/source/conf.py b/documentation/source/conf.py index adfde050..4fbe4d08 100644 --- a/documentation/source/conf.py +++ b/documentation/source/conf.py @@ -39,9 +39,12 @@ 'sphinx.ext.imgmath', 'sphinx.ext.viewcode', 'sphinx.ext.napoleon', + 'sphinx.ext.intersphinx', 'cairosvgconverter', ] +intersphinx_mapping = {'https://docs.python.org/3': None} + # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] From 790c2fde91bb27b1effcfd3a08732f19587076ea Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 16 Jan 2019 01:36:04 +0100 Subject: [PATCH 1525/2656] Improve documentation; use Sphinx automodule for some parts. --- cocotb/binary.py | 53 ++-- cocotb/bus.py | 18 +- cocotb/clock.py | 80 +++--- cocotb/drivers/__init__.py | 70 +++--- cocotb/drivers/ad9361.py | 4 +- cocotb/drivers/avalon.py | 7 +- cocotb/handle.py | 277 +++++++++------------ cocotb/monitors/__init__.py | 29 ++- cocotb/regression.py | 10 +- cocotb/result.py | 9 +- cocotb/scheduler.py | 109 ++++---- cocotb/scoreboard.py | 132 ++++++---- cocotb/triggers.py | 177 ++++++------- cocotb/utils.py | 78 +++--- documentation/source/building.rst | 8 +- documentation/source/coroutines.rst | 29 +-- documentation/source/endian_swapper.rst | 85 ++++--- documentation/source/library_reference.rst | 165 ++++++------ documentation/source/quickstart.rst | 95 +++---- documentation/source/testbench_tools.rst | 54 ++-- documentation/source/triggers.rst | 72 ++---- 21 files changed, 768 insertions(+), 793 deletions(-) mode change 100644 => 100755 cocotb/handle.py mode change 100644 => 100755 cocotb/scoreboard.py diff --git a/cocotb/binary.py b/cocotb/binary.py index 5b306383..3c597be1 100755 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -64,10 +64,10 @@ def _clog2(val): exp += 1 -class BinaryRepresentation(): - UNSIGNED = 0 # noqa - SIGNED_MAGNITUDE = 1 # noqa - TWOS_COMPLEMENT = 2 # noqa +class BinaryRepresentation(): # noqa + UNSIGNED = 0 #: Unsigned format + SIGNED_MAGNITUDE = 1 #: Sign and magnitude format + TWOS_COMPLEMENT = 2 #: Two's complement format class BinaryValue(object): @@ -75,11 +75,11 @@ class BinaryValue(object): The underlying value can be set or accessed using these aliasing attributes: - - `BinaryValue.integer` is an integer - - `BinaryValue.signed_integer` is a signed integer - - `BinaryValue.binstr` is a string of "01xXzZ" - - `BinaryValue.buff` is a binary buffer of bytes - - `BinaryValue.value` is an integer **deprecated** + - :attr:`BinaryValue.integer` is an integer + - :attr:`BinaryValue.signed_integer` is a signed integer + - :attr:`BinaryValue.binstr` is a string of "01xXzZ" + - :attr:`BinaryValue.buff` is a binary buffer of bytes + - :attr:`BinaryValue.value` is an integer **deprecated** For example: @@ -107,9 +107,9 @@ def __init__(self, value=None, n_bits=None, bigEndian=True, when converting to/from a string buffer. binaryRepresentation (BinaryRepresentation): The representation of the binary value - (one of ``UNSIGNED``, ``SIGNED_MAGNITUDE``, ``TWOS_COMPLEMENT``). + (one of :any:`UNSIGNED`, :any:`SIGNED_MAGNITUDE`, :any:`TWOS_COMPLEMENT`). Defaults to unsigned representation. - bits (int, optional): Deprecated: Compatibility wrapper for n_bits. + bits (int, optional): Deprecated: Compatibility wrapper for :attr:`n_bits`. """ self._str = "" self.big_endian = bigEndian @@ -147,7 +147,8 @@ def assign(self, value): We possibly try to be a bit too clever here by first of all trying to assign the raw string as a binstring, however - if the string contains any characters that aren't 0, 1, X or Z + if the string contains any characters that aren't + ``0``, ``1``, ``X`` or ``Z`` then we interpret the string as a binary buffer. Args: @@ -238,7 +239,7 @@ def _adjust_unsigned(self, x): return rv def _adjust_signed_mag(self, x): - """Pad/truncate the bit string to the correct length""" + """Pad/truncate the bit string to the correct length.""" if self._n_bits is None: return x l = len(x) @@ -299,22 +300,21 @@ def set_value(self, integer): @property def is_resolvable(self): - """Does the value contain any X's? Inquiring minds want to know""" + """Does the value contain any ``X``'s? Inquiring minds want to know.""" return not any(char in self._str for char in BinaryValue._resolve_to_error) value = property(get_value, set_value, None, - "Integer access to the value. *** deprecated ***") + "Integer access to the value. **deprecated**") integer = property(get_value, set_value, None, "The integer representation of the underlying vector.") signed_integer = property(get_value_signed, set_value, None, "The signed integer representation of the underlying vector.") def get_buff(self): - """Attribute self.buff represents the value as a binary string buffer + """Attribute :attr:`buff` represents the value as a binary string buffer. >>> "0100000100101111".buff == "\x41\x2F" True - """ bits = resolve(self._str) @@ -347,7 +347,7 @@ def set_buff(self, buff): self._adjust() def _adjust(self): - """Pad/truncate the bit string to the correct length""" + """Pad/truncate the bit string to the correct length.""" if self._n_bits is None: return l = len(self._str) @@ -362,11 +362,11 @@ def _adjust(self): self._str = self._str[l - self._n_bits:] buff = property(get_buff, set_buff, None, - "Access to the value as a buffer") + "Access to the value as a buffer.") def get_binstr(self): - """Attribute binstr is the binary representation stored as a string of - 1s and 0s""" + """Attribute :attr:`binstr` is the binary representation stored as + a string of ``1`` and ``0``.""" return self._str def set_binstr(self, string): @@ -378,14 +378,14 @@ def set_binstr(self, string): self._adjust() binstr = property(get_binstr, set_binstr, None, - "Access to the binary string") + "Access to the binary string.") def _get_n_bits(self): - """The number of bits of the binary value""" + """The number of bits of the binary value.""" return self._n_bits n_bits = property(_get_n_bits, None, None, - "Access to the number of bits of the binary value") + "Access to the number of bits of the binary value.") def hex(self): try: @@ -406,7 +406,7 @@ def __bool__(self): return self.__nonzero__() def __nonzero__(self): - """Provide boolean testing of a binstr. + """Provide boolean testing of a :attr:`binstr`. >>> val = BinaryValue("0000") >>> if val: print("True") @@ -639,8 +639,7 @@ def __getitem__(self, key): return rv def __setitem__(self, key, val): - """BinaryValue uses Verilog/VHDL style slices as opposed to Python - style""" + """BinaryValue uses Verilog/VHDL style slices as opposed to Python style.""" if not isinstance(val, str) and not isinstance(val, get_python_integer_types()): raise TypeError('BinaryValue slices only accept string or integer values') diff --git a/cocotb/bus.py b/cocotb/bus.py index 5b88fe57..e93e341c 100755 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -27,12 +27,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -""" -Common bus related functionality. +"""Common bus related functionality. A bus is simply defined as a collection of signals. """ - def _build_sig_attr_dict(signals): if isinstance(signals, dict): return signals @@ -139,7 +137,7 @@ def capture(self): Returns: dict: A dictionary that supports access by attribute, - where each attribute corresponds to each signal's value. + where each attribute corresponds to each signal's value. Raises: RuntimeError: If signal not present in bus, or attempt to modify a bus capture. @@ -149,13 +147,13 @@ def __getattr__(self, name): if name in self: return self[name] else: - raise RuntimeError('signal {} not present in bus'.format(name)) + raise RuntimeError('Signal {} not present in bus'.format(name)) def __setattr__(self, name, value): - raise RuntimeError('modifying a bus capture is not supported') + raise RuntimeError('Modifying a bus capture is not supported') def __delattr__(self, name): - raise RuntimeError('modifying a bus capture is not supported') + raise RuntimeError('Modifying a bus capture is not supported') _capture = _Capture() for attr_name, hdl in self._signals.items(): @@ -164,15 +162,15 @@ def __delattr__(self, name): return _capture def sample(self, obj, strict=False): - """Sample the values from the bus, assigning them to ``obj``. + """Sample the values from the bus, assigning them to *obj*. Args: obj: Object with attribute names that match the bus signals. strict (bool, optional): Check that all signals being sampled - are present in ``obj``. + are present in *obj*. Raises: - AttributeError: If attribute is missing in ``obj`` when ``strict=True``. + AttributeError: If attribute is missing in *obj* when ``strict=True``. """ for attr_name, hdl in self._signals.items(): if not hasattr(obj, attr_name): diff --git a/cocotb/clock.py b/cocotb/clock.py index 9a0dad1a..504a8508 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -1,33 +1,31 @@ -''' Copyright (c) 2013 Potential Ventures Ltd -Copyright (c) 2013 SolarFlare Communications Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - -""" -A clock class -""" +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""A clock class.""" import os import itertools @@ -43,7 +41,7 @@ class BaseClock(object): - """Base class to derive from""" + """Base class to derive from.""" def __init__(self, signal): self.signal = signal self.log = SimLog("cocotb.%s.%s" % @@ -53,7 +51,7 @@ def __init__(self, signal): class Clock(BaseClock): """Simple 50:50 duty cycle clock driver. - Instances of this class should call its ``start`` method and fork the + Instances of this class should call its :any:`start` method and fork the result. This will create a clocking thread that drives the signal at the desired period/frequency. @@ -65,13 +63,15 @@ class Clock(BaseClock): cocotb.fork(c.start()) Args: - signal (pin): The clock pin/signal to be driven. + signal: The clock pin/signal to be driven. period (int): The clock period. Must convert to an even number of timesteps. - units (str, optional): One of (None,'fs','ps','ns','us','ms','sec'). - When no units are given (``None``) the timestep is determined by + units (str, optional): One of + ``None``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``. + When no *units* is given (``None``) the timestep is determined by the simulator. """ + def __init__(self, signal, period, units=None): BaseClock.__init__(self, signal) self.period = get_sim_steps(period, units) @@ -84,13 +84,13 @@ def __init__(self, signal, period, units=None): @cocotb.coroutine def start(self, cycles=None): - """Clocking coroutine. Start driving your clock by forking a call to - this. + """Clocking coroutine. Start driving your clock by forking a + call to this. Args: - cycles (int, optional): Cycle the clock `cycles` number of times, - or if ``None`` then cycle the clock forever. Note: ``0`` is not - the same as ``None``, as 0 will cycle no times. + cycles (int, optional): Cycle the clock *cycles* number of times, + or if ``None`` then cycle the clock forever. + Note: ``0`` is not the same as ``None``, as ``0`` will cycle no times. """ t = Timer(self.half_period) if cycles is None: diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index 7fd173f0..bba0e8d2 100755 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -85,7 +85,6 @@ class Driver(object): """ def __init__(self): """Constructor for a driver instance.""" - # self._busy = Lock() self._pending = Event(name="Driver._pending") self._sendQ = deque() @@ -111,9 +110,10 @@ def append(self, transaction, callback=None, event=None, **kwargs): transaction (any): The transaction to be sent. callback (callable, optional): Optional function to be called when the transaction has been sent. - event (optional): event to be set when the transaction has been sent. + event (optional): :class:`~cocotb.triggers.Event` to be set + when the transaction has been sent. **kwargs: Any additional arguments used in child class' - ``_driver_send`` method. + :any:`_driver_send` method. """ self._sendQ.append((transaction, callback, event, kwargs)) self._pending.set() @@ -132,16 +132,15 @@ def send(self, transaction, sync=True, **kwargs): transaction (any): The transaction to be sent. sync (bool, optional): Synchronise the transfer by waiting for rising edge. **kwargs (dict): Additional arguments used in child class' - ``_driver_send`` method. + :any:`_driver_send` method. """ yield self._send(transaction, None, None, sync=sync, **kwargs) def _driver_send(self, transaction, sync=True, **kwargs): - """ - Actual implementation of the send. + """Actual implementation of the send. - Subclasses should override this method to implement the actual ``send`` - routine. + Subclasses should override this method to implement the actual + :meth:`~cocotb.drivers.Driver.send` routine. Args: transaction (any): The transaction to be sent. @@ -153,9 +152,7 @@ def _driver_send(self, transaction, sync=True, **kwargs): @coroutine def _send(self, transaction, callback, event, sync=True, **kwargs): - """Assumes the caller has already acquired the busy lock. - - Releases busy lock once sending is complete. FIXME: not true + """Send coroutine. Args: transaction (any): The transaction to be sent. @@ -164,7 +161,7 @@ def _send(self, transaction, callback, event, sync=True, **kwargs): event (optional): event to be set when the transaction has been sent. sync (boolean, optional): Synchronise the transfer by waiting for rising edge. **kwargs: Any additional arguments used in child class' - ``_driver_send`` method. + :any:`_driver_send` method. """ yield self._driver_send(transaction, sync=sync, **kwargs) @@ -174,9 +171,6 @@ def _send(self, transaction, callback, event, sync=True, **kwargs): if callback: callback(transaction) - # No longer hogging the bus - # self.busy.release() - @coroutine def _send_thread(self): while True: @@ -200,23 +194,25 @@ def _send_thread(self): class BusDriver(Driver): """Wrapper around common functionality for busses which have: - * a list of _signals (class attribute) - * a list of _optional_signals (class attribute) + + * a list of :attr:`_signals` (class attribute) + * a list of :attr:`_optional_signals` (class attribute) * a clock * a name * an entity - """ - _optional_signals = [] - def __init__(self, entity, name, clock, **kwargs): - """Args: + Args: entity (SimHandle): A handle to the simulator entity. - - name (str): Name of this bus. ``None`` for nameless bus, e.g. - bus-signals in an interface or a modport + name (str or None): Name of this bus. ``None`` for nameless bus, e.g. + bus-signals in an interface or a modport. (untested on struct/record, but could work here as well). clock (SimHandle): A handle to the clock associated with this bus. - """ + array_idx (int or None, optional): Optional index when signal is an array. + """ + + _optional_signals = [] + + def __init__(self, entity, name, clock, **kwargs): self.log = SimLog("cocotb.%s.%s" % (entity._name, name)) Driver.__init__(self) self.entity = entity @@ -237,8 +233,8 @@ def _driver_send(self, transaction, sync=True): @coroutine def _wait_for_signal(self, signal): """This method will return with the specified signal - has hit logic 1. The state will be in the ReadOnly phase - so sim will need to move to NextTimeStep before + has hit logic ``1``. The state will be in the :any:`ReadOnly` phase + so sim will need to move to :any:`NextTimeStep` before registering more callbacks can occur. """ yield ReadOnly() @@ -250,8 +246,8 @@ def _wait_for_signal(self, signal): @coroutine def _wait_for_nsignal(self, signal): """This method will return with the specified signal - has hit logic 0. The state will be in the ReadOnly phase - so sim will need to move to NextTimeStep before + has hit logic ``0``. The state will be in the :any:`ReadOnly` phase + so sim will need to move to :any:`NextTimeStep` before registering more callbacks can occur. """ yield ReadOnly() @@ -268,18 +264,16 @@ def __str__(self): class ValidatedBusDriver(BusDriver): """Same as a BusDriver except we support an optional generator to control which cycles are valid. + + Args: + entity (SimHandle): A handle to the simulator entity. + name (str): Name of this bus. + clock (SimHandle): A handle to the clock associated with this bus. + valid_generator (generator, optional): a generator that yields tuples of + (valid, invalid) cycles to insert. """ def __init__(self, entity, name, clock, **kwargs): - # FIXME: "Kwargs:" section not understood like this by Sphinx Napoleon - """Args: - entity (SimHandle): A handle to the simulator entity. - name (str): Name of this bus. - clock (SimHandle): A handle to the clock associated with this bus. - Kwargs: - valid_generator (generator): a generator that yields tuples of - (valid, invalid) cycles to insert. - """ valid_generator = kwargs.pop("valid_generator", None) BusDriver.__init__(self, entity, name, clock, **kwargs) self.set_valid_generator(valid_generator=valid_generator) diff --git a/cocotb/drivers/ad9361.py b/cocotb/drivers/ad9361.py index 49502a3b..c00ce7f2 100644 --- a/cocotb/drivers/ad9361.py +++ b/cocotb/drivers/ad9361.py @@ -49,7 +49,7 @@ def send_data(self, i_data, q_data, i_data2=None, q_data2=None, i_data2 (int, optional): Data of the I1 channel. q_data2 (int, optional): Data of the Q1 channel. binaryRepresentation (BinaryRepresentation): The representation of the binary value. - Default is ``BinaryRepresentation.TWOS_COMPLEMENT``. + Default is :any:`TWOS_COMPLEMENT`. """ print(binaryRepresentation) cocotb.fork(self.rx_data_to_ad9361(i_data, q_data, i_data2, q_data2, @@ -68,7 +68,7 @@ def rx_data_to_ad9361(self, i_data, q_data, i_data2=None, q_data2=None, i_data2 (int, optional): Data of the I1 channel. q_data2 (int, optional): Data of the Q1 channel. binaryRepresentation (BinaryRepresentation): The representation of the binary value. - Default is ``BinaryRepresentation.TWOS_COMPLEMENT``. + Default is :any:`TWOS_COMPLEMENT`. """ i_bin_val = BinaryValue(n_bits=12, bigEndian=False, binaryRepresentation=binaryRepresentation) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 0375b8ad..b8ff4d94 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -25,8 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -""" -Drivers for Altera Avalon interfaces. +"""Drivers for Altera Avalon interfaces. See http://www.altera.co.uk/literature/manual/mnl_avalon_spec.pdf @@ -132,7 +131,7 @@ def read(self, address, sync=True): BinaryValue: The read data value. Raises: - TestError: If master is write-only. + :any:`TestError`: If master is write-only. """ if not self._can_read: self.log.error("Cannot read - have no read signal") @@ -194,7 +193,7 @@ def write(self, address, value): value (int): The data value to write. Raises: - TestError: If master is read-only. + :any:`TestError`: If master is read-only. """ if not self._can_write: self.log.error("Cannot write - have no write signal") diff --git a/cocotb/handle.py b/cocotb/handle.py old mode 100644 new mode 100755 index bd89cabe..81add74b --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -1,31 +1,31 @@ #!/usr/bin/env python -''' Copyright (c) 2013 Potential Ventures Ltd -Copyright (c) 2013 SolarFlare Communications Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # -*- coding: utf-8 -*- @@ -54,12 +54,10 @@ _deprecation_warned = {} - class SimHandleBase(object): - """ - Base class for all simulation objects. + """Base class for all simulation objects. - We maintain a handle which we can use for GPI calls + We maintain a handle which we can use for GPI calls. """ # For backwards compatibility we support a mapping of old member names @@ -152,17 +150,13 @@ def __getattr__(self, name): return object.__getattr__(self, name) class RegionObject(SimHandleBase): - """ - Region objects don't have values, they are effectively scopes or namespaces - """ + """Region objects don't have values, they are effectively scopes or namespaces.""" def __init__(self, handle, path): SimHandleBase.__init__(self, handle, path) self._discovered = False def __iter__(self): - """ - Iterate over all known objects in this layer of hierarchy - """ + """Iterate over all known objects in this layer of hierarchy.""" try: if not self._discovered: self._discover_all() @@ -183,9 +177,8 @@ def __iter__(self): pass def _discover_all(self): - """ - When iterating or performing tab completion, we run through ahead of - time and discover all possible children, populating the _sub_handle + """When iterating or performing tab completion, we run through ahead of + time and discover all possible children, populating the ``_sub_handles`` mapping. Hierarchy can't change after elaboration so we only have to do this once. """ @@ -216,35 +209,28 @@ def _discover_all(self): self._discovered = True def _child_path(self, name): - """ - Returns a string of the path of the child SimHandle for a given name - """ + """Returns a string of the path of the child :any:`SimHandle` for a given name.""" return self._path + "." + name def _sub_handle_key(self, name): - """ - Translates the handle name to a key to use in _sub_handles dictionary. - """ + """Translates the handle name to a key to use in ``_sub_handles`` dictionary.""" return name.split(".")[-1] def _getAttributeNames(self): - """Permits IPython tab completion to work""" + """Permits IPython tab completion to work.""" self._discover_all() return dir(self) class HierarchyObject(RegionObject): - """ - Hierarchy objects are namespace/scope objects - """ + """Hierarchy objects are namespace/scope objects.""" def __setattr__(self, name, value): - """ - Provide transparent access to signals via the hierarchy + """Provide transparent access to signals via the hierarchy. - Slightly hacky version of operator overloading in Python + Slightly hacky version of operator overloading in Python. - Raise an AttributeError if users attempt to create new members which + Raise an :exc:`AttributeError` if users attempt to create new members which don't exist in the design. """ if name.startswith("_"): @@ -269,9 +255,8 @@ def __setattr__(self, name, value): name, self._name)) def __getattr__(self, name): - """ - Query the simulator for a object with the specified name - and cache the result to build a tree of objects + """Query the simulator for a object with the specified name + and cache the result to build a tree of objects. """ if name in self._sub_handles: sub = self._sub_handles[name] @@ -290,10 +275,9 @@ def __getattr__(self, name): return self._sub_handles[name] def __hasattr__(self, name): - """ - Since calling hasattr(handle, "something") will print out a - backtrace to the log since usually attempting to access a - non-existent member is an error we provide a 'peek function + """Since calling ``hasattr(handle, "something")`` will print out a + backtrace to the log (since usually attempting to access a + non-existent member is an error) we provide a 'peek' function. We still add the found handle to our dictionary to prevent leaking handles. @@ -312,9 +296,9 @@ def __hasattr__(self, name): return new_handle def _id(self, name, extended=True): - """ - Query the simulator for a object with the specified name, including extended identifiers, - and cache the result to build a tree of objects + """Query the simulator for a object with the specified name, + including extended identifiers, + and cache the result to build a tree of objects. """ if extended: name = "\\"+name+"\\" @@ -324,14 +308,10 @@ def _id(self, name, extended=True): raise AttributeError("%s contains no object named %s" % (self._name, name)) class HierarchyArrayObject(RegionObject): - """ - Hierarchy Array are containers of Hierarchy Objects - """ + """Hierarchy Arrays are containers of Hierarchy Objects.""" def _sub_handle_key(self, name): - """ - Translates the handle name to a key to use in _sub_handles dictionary. - """ + """Translates the handle name to a key to use in ``_sub_handles`` dictionary.""" # This is slightly hacky, but we need to extract the index from the name # # FLI and VHPI(IUS): _name(X) where X is the index @@ -372,9 +352,7 @@ def __getitem__(self, index): return self._sub_handles[index] def _child_path(self, name): - """ - Returns a string of the path of the child SimHandle for a given name - """ + """Returns a string of the path of the child :any:`SimHandle` for a given name.""" index = self._sub_handle_key(name) return self._path + "[" + str(index) + "]" @@ -383,10 +361,7 @@ def __setitem__(self, index, value): class NonHierarchyObject(SimHandleBase): - - """ - Common base class for all non-hierarchy objects - """ + """Common base class for all non-hierarchy objects.""" def __init__(self, handle, path): SimHandleBase.__init__(self, handle, path) @@ -396,7 +371,7 @@ def __iter__(self): def _getvalue(self): if type(self) is NonHierarchyIndexableObject: - #Need to iterate over the sub-object + # need to iterate over the sub-object result =[] for x in range(len(self)): result.append(self[x]._getvalue()) @@ -429,22 +404,21 @@ def __ne__(self, other): return not self.__eq__(other) - # We want to maintain compatability with python 2.5 so we can't use @property with a setter + # We want to maintain compatibility with python 2.5 so we can't use @property with a setter value = property(fget=lambda self: self._getvalue(), fset=lambda self,v: self._setcachedvalue(v), fdel=None, doc="A reference to the value") - # Re-define hash becasue Python 3 has issues when using the above property + # Re-define hash because Python 3 has issues when using the above property def __hash__(self): return SimHandleBase.__hash__(self) class ConstantObject(NonHierarchyObject): - """ - Constant objects have a value that can be read, but not set. + """Constant objects have a value that can be read, but not set. We can also cache the value since it is elaboration time fixed and won't - change within a simulation + change within a simulation. """ def __init__(self, handle, path, handle_type): NonHierarchyObject.__init__(self, handle, path) @@ -476,15 +450,14 @@ def __str__(self): class NonHierarchyIndexableObject(NonHierarchyObject): def __init__(self, handle, path): - """ - Args: - _handle [integer] : fli/vpi/vhpi handle to the simulator object + """Args: + handle (int): FLI/VPI/VHPI handle to the simulator object. """ NonHierarchyObject.__init__(self, handle, path) self._range = simulator.get_range(self._handle) def __setitem__(self, index, value): - """Provide transparent assignment to indexed array handles""" + """Provide transparent assignment to indexed array handles.""" if type(value) is list: if len(value) != len(self.__getitem__(index)): raise IndexError("Assigning list of length %d to object %s of length %d" % ( @@ -539,17 +512,16 @@ def _range_iter(self, left, right): pass class NonConstantObject(NonHierarchyIndexableObject): + # FIXME: what is the difference to ModifiableObject? Explain in docstring. + def __init__(self, handle, path): - """ - Args: - _handle [integer] : vpi/vhpi handle to the simulator object + """Args: + handle (int): FLI/VPI/VHPI handle to the simulator object. """ NonHierarchyIndexableObject.__init__(self, handle, path) def drivers(self): - """ - An iterator for gathering all drivers for a signal - """ + """An iterator for gathering all drivers for a signal.""" try: iterator = simulator.iterate(self._handle, simulator.DRIVERS) while True: @@ -559,9 +531,7 @@ def drivers(self): pass def loads(self): - """ - An iterator for gathering all loads on a signal - """ + """An iterator for gathering all loads on a signal.""" try: iterator = simulator.iterate(self._handle, simulator.LOADS) while True: @@ -572,26 +542,24 @@ def loads(self): class ModifiableObject(NonConstantObject): - """ - Base class for simulator objects whose values can be modified - """ + """Base class for simulator objects whose values can be modified.""" + def setimmediatevalue(self, value): - """ - Set the value of the underlying simulation object to value. - - Args: - value (ctypes.Structure, cocotb.binary.BinaryValue, int, double) - The value to drive onto the simulator object - - Raises: - TypeError + """Set the value of the underlying simulation object to value. This operation will fail unless the handle refers to a modifiable - object eg net, signal or variable. + object, e.g. net, signal or variable. We determine the library call to make based on the type of the value + because assigning integers less than 32 bits is faster. + + Args: + value (ctypes.Structure, cocotb.binary.BinaryValue, int, double): + The value to drive onto the simulator object. - Assigning integers less than 32-bits is faster + Raises: + TypeError: If target is not wide enough or has an unsupported type + for value assignment. """ if isinstance(value, get_python_integer_types()) and value < 0x7fffffff and len(self) <= 32: simulator.set_signal_val_long(self._handle, value) @@ -602,7 +570,7 @@ def setimmediatevalue(self, value): elif isinstance(value, get_python_integer_types()): value = BinaryValue(value=value, n_bits=len(self), bigEndian=False) elif isinstance(value, dict): - #We're given a dictionary with a list of values and a bit size... + # We're given a dictionary with a list of values and a bit size... num = 0; vallist = list(value["values"]) vallist.reverse() @@ -628,12 +596,11 @@ def _getvalue(self): return result def _setcachedvalue(self, value): - """ - Intercept the store of a value and hold in cache. + """Intercept the store of a value and hold in cache. - This operation is to enable all of the scheduled callbacks to completed - with the same read data and for the writes to occour on the next - sim time + This operation is to enable all of the scheduled callbacks to complete + with the same read data and for the writes to occur on the next + sim time. """ cocotb.scheduler.save_write(self, value) @@ -644,23 +611,20 @@ def __str__(self): return str(self.value) class RealObject(ModifiableObject): - """ - Specific object handle for Real signals and variables - """ + """Specific object handle for Real signals and variables.""" def setimmediatevalue(self, value): - """ - Set the value of the underlying simulation object to value. + """Set the value of the underlying simulation object to value. + + This operation will fail unless the handle refers to a modifiable + object, e.g. net, signal or variable. Args: - value (float) - The value to drive onto the simulator object + value (float): The value to drive onto the simulator object. Raises: - TypeError - - This operation will fail unless the handle refers to a modifiable - object eg net, signal or variable. + TypeError: If target has an unsupported type for + real value assignment. """ if not isinstance(value, float): self._log.critical("Unsupported type for real value assignment: %s (%s)" % (type(value), repr(value))) @@ -675,23 +639,20 @@ def __float__(self): return float(self.value) class EnumObject(ModifiableObject): - """ - Specific object handle for ENUM signals and variables - """ + """Specific object handle for enumeration signals and variables.""" def setimmediatevalue(self, value): - """ - Set the value of the underlying simulation object to value. + """Set the value of the underlying simulation object to value. + + This operation will fail unless the handle refers to a modifiable + object, e.g. net, signal or variable. Args: - value (int) - The value to drive onto the simulator object + value (int): The value to drive onto the simulator object. Raises: - TypeError - - This operation will fail unless the handle refers to a modifiable - object eg net, signal or variable. + TypeError: If target has an unsupported type for + integer value assignment. """ if isinstance(value, BinaryValue): value = int(value) @@ -706,23 +667,20 @@ def _getvalue(self): class IntegerObject(ModifiableObject): - """ - Specific object handle for Integer and Enum signals and variables - """ + """Specific object handle for Integer and Enum signals and variables.""" def setimmediatevalue(self, value): - """ - Set the value of the underlying simulation object to value. + """Set the value of the underlying simulation object to value. + + This operation will fail unless the handle refers to a modifiable + object, e.g. net, signal or variable. Args: - value (int) - The value to drive onto the simulator object + value (int): The value to drive onto the simulator object. Raises: - TypeError - - This operation will fail unless the handle refers to a modifiable - object eg net, signal or variable. + TypeError: If target has an unsupported type for + integer value assignment. """ if isinstance(value, BinaryValue): value = int(value) @@ -736,23 +694,20 @@ def _getvalue(self): return simulator.get_signal_val_long(self._handle) class StringObject(ModifiableObject): - """ - Specific object handle for String variables - """ + """Specific object handle for String variables.""" def setimmediatevalue(self, value): - """ - Set the value of the underlying simulation object to value. + """Set the value of the underlying simulation object to value. + + This operation will fail unless the handle refers to a modifiable + object, e.g. net, signal or variable. Args: - value (string) - The value to drive onto the simulator object + value (str): The value to drive onto the simulator object. Raises: - TypeError - - This operation will fail unless the handle refers to a modifiable - object eg net, signal or variable. + TypeError: If target has an unsupported type for + string value assignment. """ if not isinstance(value, str): self._log.critical("Unsupported type for string value assignment: %s (%s)" % (type(value), repr(value))) @@ -766,9 +721,7 @@ def _getvalue(self): _handle2obj = {} def SimHandle(handle, path=None): - """ - Factory function to create the correct type of SimHandle object - """ + """Factory function to create the correct type of :any:`SimHandle` object.""" _type2cls = { simulator.MODULE: HierarchyObject, simulator.STRUCTURE: HierarchyObject, diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index 264a0a2c..7f390a9f 100755 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -52,24 +52,26 @@ def __init__(self): class Monitor(object): - """Base class for Monitor objects. Monitors are passive 'listening' objects - that monitor pins in or out of a DUT. This class should not be used - directly, but should be subclassed and the internal `_monitor_recv` method - should be overridden and decorated as a @coroutine. This `_monitor_recv` + """Base class for Monitor objects. + + Monitors are passive 'listening' objects that monitor pins in or out of a DUT. + This class should not be used + directly, but should be subclassed and the internal :any:`_monitor_recv` method + should be overridden and decorated as a :any:`coroutine`. This :any:`_monitor_recv` method should capture some behavior of the pins, form a transaction, and - pass this transaction to the internal `_recv` method. The `_monitor_recv` - method is added to the cocotb scheduler during the `__init__` phase, so it + pass this transaction to the internal :any:`_recv` method. The :any:`_monitor_recv` + method is added to the cocotb scheduler during the ``__init__`` phase, so it should not be yielded anywhere. The primary use of a Monitor is as an interface for a - :py:class:`cocotb.scoreboard.Scoreboard`. + :class:`~cocotb.scoreboard.Scoreboard`. Args: callback (callable): Callback to be called with each recovered transaction as the argument. If the callback isn't used, received transactions will be placed on a queue and the event used to notify any consumers. - event (event): Object that supports a `set` method that will be called when - a transaction is received through the internal `_recv` method. + event (event): Object that supports a ``set`` method that will be called when + a transaction is received through the internal :any:`_recv` method. """ def __init__(self, callback=None, event=None): @@ -121,11 +123,10 @@ def wait_for_recv(self, timeout=None): @coroutine def _monitor_recv(self): - """ - Actual implementation of the receiver + """Actual implementation of the receiver. Subclasses should override this method to implement the actual receive - routine and call ``self._recv()`` with the recovered transaction. + routine and call :any:`_recv` with the recovered transaction. """ raise NotImplementedError("Attempt to use base monitor class without " "providing a ``_monitor_recv`` method") @@ -153,9 +154,7 @@ def _recv(self, transaction): class BusMonitor(Monitor): - """ - Wrapper providing common functionality for monitoring busses - """ + """Wrapper providing common functionality for monitoring busses.""" _signals = [] _optional_signals = [] diff --git a/cocotb/regression.py b/cocotb/regression.py index 1a074ab6..2ad17f34 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -491,7 +491,7 @@ def add_option(self, name, optionlist): """Add a named option to the test. Args: - name (string): Name of the option. Passed to test as a keyword + name (str): Name of the option. Passed to test as a keyword argument. optionlist (list): A list of possible options for this test knob. @@ -509,12 +509,12 @@ def generate_tests(self, prefix="", postfix=""): Args: prefix (str): Text string to append to start of ``test_function`` name when naming generated test cases. This allows reuse of - a single ``test_function`` with multiple TestFactories without - name clashes. + a single ``test_function`` with multiple + :class:`TestFactories <.TestFactory>` without name clashes. postfix (str): Text string to append to end of ``test_function`` name when naming generated test cases. This allows reuse of - a single ``test_function`` with multiple TestFactories without - name clashes. + a single ``test_function`` with multiple + :class:`TestFactories <.TestFactory>` without name clashes. """ frm = inspect.stack()[1] diff --git a/cocotb/result.py b/cocotb/result.py index e68e8c1a..1daf8967 100644 --- a/cocotb/result.py +++ b/cocotb/result.py @@ -31,9 +31,10 @@ # from StringIO import StringIO from io import StringIO, BytesIO +"""Exceptions and functions for simulation result handling.""" def raise_error(obj, msg): - """Creates a TestError exception and raises it after printing a traceback. + """Creates a :exc:`TestError` exception and raises it after printing a traceback. Args: obj: Object with a log method. @@ -55,8 +56,8 @@ def raise_error(obj, msg): def create_error(obj, msg): - """Like `raise_error`, but return the exception rather than raise it, - simply to avoid too many levels of nested try/except blocks. + """Like :func:`raise_error`, but return the exception rather than raise it, + simply to avoid too many levels of nested `try/except` blocks. Args: obj: Object with a log method. @@ -70,6 +71,7 @@ def create_error(obj, msg): class ReturnValue(Exception): + """Helper exception needed for Python versions prior to 3.3.""" def __init__(self, retval): self.retval = retval @@ -83,6 +85,7 @@ def __init__(self, *args, **kwargs): class ExternalException(Exception): + """Exception thrown by external functions.""" def __init__(self, exception): self.exception = exception diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 75047b8a..633ff5d4 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -1,34 +1,33 @@ #!/usr/bin/env python -''' Copyright (c) 2013, 2018 Potential Ventures Ltd -Copyright (c) 2013 SolarFlare Communications Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - -""" - Coroutine scheduler. +# Copyright (c) 2013, 2018 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Coroutine scheduler. FIXME: We have a problem here. If a coroutine schedules a read-only but we @@ -57,7 +56,7 @@ else: _profiling = False -# Sadly the python standard logging module is very slow so it's better not to +# Sadly the Python standard logging module is very slow so it's better not to # make any calls by testing a boolean flag first if "COCOTB_SCHEDULER_DEBUG" in os.environ: _debug = True @@ -141,13 +140,12 @@ def thread_wait(self): return self.state class Scheduler(object): - """ - The main scheduler. + """The main scheduler. Here we accept callbacks from the simulator and schedule the appropriate coroutines. - A callback fires, causing the `react`_ method to be called, with the + A callback fires, causing the :any:`react` method to be called, with the trigger that caused the callback as the first argument. We look up a list of coroutines to schedule (indexed by the trigger) and @@ -272,10 +270,9 @@ def default_scheduling_algorithm(self): self._mode = Scheduler._MODE_TERM def begin_test(self, trigger=None): - """ - Called to initiate a test. + """Called to initiate a test. - Could be called on start-up or from a callback + Could be called on start-up or from a callback. """ if _debug: self.log.debug("begin_test called with trigger: %s" % @@ -305,8 +302,7 @@ def begin_test(self, trigger=None): _profile.disable() def react(self, trigger, depth=0): - """ - React called when a trigger fires. + """React called when a trigger fires. We find any coroutines that are waiting on the particular trigger and schedule them. @@ -470,9 +466,7 @@ def save_write(self, handle, value): self._writes[handle] = value def _coroutine_yielded(self, coro, triggers): - """ - Prime the triggers and update our internal mappings - """ + """Prime the triggers and update our internal mappings.""" self._coro2triggers[coro] = triggers for trigger in triggers: @@ -492,9 +486,8 @@ def queue(self, coroutine): self._pending_coros.append(coroutine) def queue_function(self, coroutine): - """ - Queue a coroutine for execution and move the containing thread - so that it does not block execution of the main thread any longer + """Queue a coroutine for execution and move the containing thread + so that it does not block execution of the main thread any longer. """ # We should be able to find ourselves inside the _pending_threads list @@ -507,9 +500,8 @@ def queue_function(self, coroutine): def run_in_executor(self, func, *args, **kwargs): - """ - Run the coroutine in a separate execution thread - and return a yieldable object for the caller + """Run the coroutine in a separate execution thread + and return a yieldable object for the caller. """ # Create a thread # Create a trigger that is called as a result of the thread finishing @@ -537,11 +529,10 @@ def execute_external(func, _waiter): return waiter def add(self, coroutine): - """ - Add a new coroutine. + """Add a new coroutine. Just a wrapper around self.schedule which provides some debug and - useful error mesages in the event of common gotchas + useful error messages in the event of common gotchas. """ if isinstance(coroutine, cocotb.decorators.coroutine): self.log.critical( @@ -577,14 +568,12 @@ def new_test(self, coroutine): self._entrypoint = coroutine def schedule(self, coroutine, trigger=None): - """ - Schedule a coroutine by calling the send method + """Schedule a coroutine by calling the send method. Args: - coroutine (cocotb.decorators.coroutine): The coroutine to schedule - + coroutine (cocotb.decorators.coroutine): The coroutine to schedule. trigger (cocotb.triggers.Trigger): The trigger that caused this - coroutine to be scheduled + coroutine to be scheduled. """ if hasattr(trigger, "pass_retval"): sendval = trigger.retval @@ -722,7 +711,7 @@ def wrapper(): def finish_test(self, test_result): - """Cache the test result and set the terminate flag""" + """Cache the test result and set the terminate flag.""" self.log.debug("finish_test called with %s" % (repr(test_result))) if not self._terminate: self._terminate = True @@ -731,15 +720,15 @@ def finish_test(self, test_result): def finish_scheduler(self, test_result): """Directly call into the regression manager and end test - once we return the sim will close us so no cleanup is needed""" + once we return the sim will close us so no cleanup is needed. + """ self.log.debug("Issue sim closedown result to regression object") cocotb.regression.handle_result(test_result) def cleanup(self): - """ - Clear up all our state + """Clear up all our state. - Unprime all pending triggers and kill off any coroutines stop all externals + Unprime all pending triggers and kill off any coroutines stop all externals. """ for trigger, waiting in dict(self._trigger2coros).items(): for coro in waiting: diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py old mode 100644 new mode 100755 index 61919a5d..16d302c7 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -1,35 +1,34 @@ #!/usr/bin/env python -''' Copyright (c) 2013 Potential Ventures Ltd -Copyright (c) 2013 SolarFlare Communications Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - -""" - Common scoreboarding capability. -""" +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Common scoreboarding capability.""" + import logging import cocotb @@ -40,18 +39,28 @@ class Scoreboard(object): - """Generic scoreboarding class + """Generic scoreboarding class. - We can add interfaces by providing a monitor and an expected output queue + We can add interfaces by providing a monitor and an expected output queue. The expected output can either be a function which provides a transaction or a simple list containing the expected output. TODO: Statistics for end-of-test summary etc. + + Args: + dut (SimHandle): Handle to the DUT. + reorder_depth (int, optional): Consider up to `reorder_depth` elements + of the expected result list as passing matches. + Default is 0, meaning only the first element in the expected result list + is considered for a passing match. + fail_immediately (bool, optional): Raise :any:`TestFailure` + immediately when something is wrong instead of just + recording an error. Default is ``True``. """ - - def __init__(self, dut, reorder_depth=0, fail_immediately=True): + + def __init__(self, dut, reorder_depth=0, fail_immediately=True): # FIXME: reorder_depth needed here? self.dut = dut self.log = SimLog("cocotb.scoreboard.%s" % self.dut._name) self.errors = 0 @@ -60,7 +69,12 @@ def __init__(self, dut, reorder_depth=0, fail_immediately=True): @property def result(self): - """Determine the test result, do we have any pending data remaining?""" + """Determine the test result, do we have any pending data remaining? + + Returns: + :any:`TestFailure`: If not all expected output was received or + error were recorded during the test. + """ fail = False for monitor, expected_output in self.expected.items(): if callable(expected_output): @@ -86,10 +100,22 @@ def result(self): return TestSuccess() def compare(self, got, exp, log, strict_type=True): - """ - Common function for comparing two transactions. + """Common function for comparing two transactions. Can be re-implemented by a subclass. + + Args: + got: The received transaction. + exp: The expected transaction. + log: The logger for reporting messages. + strict_type (bool, optional): Require transaction type to match + exactly if ``True``, otherwise compare its string representation. + + Raises: + :any:`TestFailure`: If received transaction differed from + expected transaction when :attr:`fail_immediately` is ``True``. + If *strict_type* is ``True``, + also the transaction type must match. """ # Compare the types @@ -152,11 +178,25 @@ def add_interface(self, monitor, expected_output, compare_fn=None, reorder_depth=0, strict_type=True): """Add an interface to be scoreboarded. - Provides a function which the monitor will callback with received - transactions - - Simply check against the expected output. - + Provides a function which the monitor will callback with received + transactions. + + Simply check against the expected output. + + Args: + monitor: The monitor object. + expected_output: Queue of expected outputs. + compare_fn (callable, optional): + reorder_depth (int, optional): Consider up to *reorder_depth* elements + of the expected result list as passing matches. + Default is 0, meaning only the first element in the expected result list + is considered for a passing match. + strict_type (bool, optional): Require transaction type to match + exactly if ``True``, otherwise compare its string representation. + + Raises: + :any:`TypeError`: If no monitor is on the interface or + *compare_fn* is not a callable function. """ # save a handle to the expected output so we can check if all expected # data has been received at the end of a test. @@ -178,7 +218,7 @@ def add_interface(self, monitor, expected_output, compare_fn=None, def check_received_transaction(transaction): """Called back by the monitor when a new transaction has been - received""" + received.""" if monitor.name: log_name = self.log.name + '.' + monitor.name @@ -190,11 +230,11 @@ def check_received_transaction(transaction): if callable(expected_output): exp = expected_output(transaction) - elif len(expected_output): + elif len(expected_output): # we expect something for i in range(min((reorder_depth + 1), len(expected_output))): if expected_output[i] == transaction: - break - else: + break # break out of enclosing for loop + else: # run when for loop is exhausted (but no break occurs) i = 0 exp = expected_output.pop(i) else: diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 555e4977..4948202b 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -1,33 +1,32 @@ -''' Copyright (c) 2013 Potential Ventures Ltd -Copyright (c) 2013 SolarFlare Communications Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - -""" - A collections of triggers which a testbench can 'yield' -""" +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""A collections of triggers which a testbench can yield.""" + import os import weakref @@ -43,13 +42,12 @@ ParametrizedSingleton ) - class TriggerException(Exception): pass - class Trigger(object): - """Base class to derive from""" + """Base class to derive from.""" + def __init__(self): self.log = SimLog("cocotb.%s" % (self.__class__.__name__), id(self)) self.signal = None @@ -72,22 +70,20 @@ def __str__(self): class PythonTrigger(Trigger): - """Python triggers don't use GPI at all - - For example notification of coroutine completion etc + """Python triggers don't use GPI at all. + For example notification of coroutine completion etc. TODO: - Still need to implement unprime + Still need to implement unprime. """ pass class GPITrigger(Trigger): + """Base Trigger class for GPI triggers. + Consumes simulation time. """ - Base Trigger class for GPI triggers - - Consumes simulation time - """ + def __init__(self): Trigger.__init__(self) @@ -112,10 +108,9 @@ def __del__(self): class Timer(GPITrigger): - """ - Execution will resume when the specified time period expires + """Execution will resume when the specified time period expires. - Consumes simulation time + Consumes simulation time. """ def __init__(self, time_ps, units=None): GPITrigger.__init__(self) @@ -135,10 +130,10 @@ def __str__(self): class ReadOnly(with_metaclass(ParametrizedSingleton, GPITrigger)): + """Execution will resume when the readonly portion of the sim cycles is + reached. """ - Execution will resume when the readonly portion of the sim cycles is - reached - """ + @classmethod def __singleton_key__(cls): return None @@ -158,10 +153,10 @@ def __str__(self): class ReadWrite(with_metaclass(ParametrizedSingleton, GPITrigger)): + """Execution will resume when the readwrite portion of the sim cycles is + reached. """ - Execution will resume when the readwrite portion of the sim cycles is - reached - """ + @classmethod def __singleton_key__(cls): return None @@ -183,9 +178,8 @@ def __str__(self): class NextTimeStep(with_metaclass(ParametrizedSingleton, GPITrigger)): - """ - Execution will resume when the next time step is started - """ + """Execution will resume when the next time step is started.""" + @classmethod def __singleton_key__(cls): return None @@ -205,15 +199,12 @@ def __str__(self): class _EdgeBase(with_metaclass(ParametrizedSingleton, GPITrigger)): - """ - Execution will resume when an edge occurs on the provided signal - """ + """Execution will resume when an edge occurs on the provided signal.""" + @classmethod @property def _edge_type(self): - """ - The edge type, as understood by the C code. Must be set in subclasses - """ + """The edge type, as understood by the C code. Must be set in subclasses.""" raise NotImplementedError @classmethod @@ -239,24 +230,25 @@ def __str__(self): class RisingEdge(_EdgeBase): - """Triggers on the rising edge of the provided signal""" + """Triggers on the rising edge of the provided signal.""" + _edge_type = 1 class FallingEdge(_EdgeBase): - """Triggers on the falling edge of the provided signal""" + """Triggers on the falling edge of the provided signal.""" + _edge_type = 2 class Edge(_EdgeBase): - """Triggers on either edge of the provided signal""" + """Triggers on either edge of the provided signal.""" _edge_type = 3 class ClockCycles(GPITrigger): - """ - Execution will resume after N rising edges or N falling edges - """ + """Execution will resume after *num_cycles* rising edges or *num_cycles* falling edges.""" + def __init__(self, signal, num_cycles, rising=True): super(ClockCycles, self).__init__() self.signal = signal @@ -301,9 +293,8 @@ def __str__(self): class Combine(PythonTrigger): - """ - Combines multiple triggers together. Coroutine will continue when all - triggers have fired + """Combines multiple triggers together. Coroutine will continue when all + triggers have fired. """ def __init__(self, *args): @@ -339,14 +330,14 @@ def unprime(self): class _Event(PythonTrigger): - """ - Unique instance used by the Event object. + """Unique instance used by the Event object. One created for each attempt to wait on the event so that the scheduler - can maintain a dictionary of indexing each individual coroutine + can maintain a dictionary of indexing each individual coroutine. FIXME: This will leak - need to use peers to ensure everything is removed """ + def __init__(self, parent): PythonTrigger.__init__(self) self.parent = parent @@ -361,9 +352,8 @@ def __call__(self): class Event(PythonTrigger): - """ - Event to permit synchronisation between two coroutines - """ + """Event to permit synchronisation between two coroutines.""" + def __init__(self, name=""): PythonTrigger.__init__(self) self._pending = [] @@ -376,7 +366,7 @@ def prime(self, callback, trigger): Trigger.prime(self) def set(self, data=None): - """Wake up any coroutines blocked on this event""" + """Wake up any coroutines blocked on this event.""" self.fired = True self.data = data @@ -389,19 +379,21 @@ def set(self, data=None): def wait(self): """This can be yielded to block this coroutine - until another wakes it + until another wakes it. - If the Event has already been fired, this returns NullTrigger() - To reset the event (and enable the use of wait() again), clear() should be called + If the event has already been fired, this returns ``NullTrigger``. + To reset the event (and enable the use of ``wait`` again), + :meth:`~cocotb.triggers.Event.clear` should be called. """ if self.fired: return NullTrigger() return _Event(self) def clear(self): - """Clear this event that's fired. + """Clear this event that has fired. - Subsequent calls to wait will block until set() is called again""" + Subsequent calls to :meth:`~cocotb.triggers.Event.wait` will block until + :meth:`~cocotb.triggers.Event.set` is called again.""" self.fired = False def __str__(self): @@ -409,14 +401,14 @@ def __str__(self): class _Lock(PythonTrigger): - """ - Unique instance used by the Lock object. + """Unique instance used by the Lock object. One created for each attempt to acquire the Lock so that the scheduler - can maintain a dictionary of indexing each individual coroutine + can maintain a dictionary of indexing each individual coroutine. - FIXME: This will leak - need to use peers to ensure everything is removed + FIXME: This will leak - need to use peers to ensure everything is removed. """ + def __init__(self, parent): PythonTrigger.__init__(self) self.parent = parent @@ -431,9 +423,7 @@ def __call__(self): class Lock(PythonTrigger): - """ - Lock primitive (not re-entrant) - """ + """Lock primitive (not re-entrant).""" def __init__(self, name=""): PythonTrigger.__init__(self) @@ -454,13 +444,12 @@ def prime(self, callback, trigger): self._pending_primed.append(trigger) def acquire(self): - """This can be yielded to block until the lock is acquired""" + """This can be yielded to block until the lock is acquired.""" trig = _Lock(self) self._pending_unprimed.append(trig) return trig def release(self): - if not self.locked: raise_error(self, "Attempt to release an unacquired Lock %s" % (str(self))) @@ -488,9 +477,8 @@ def __nonzero__(self): class NullTrigger(Trigger): - """ - Trigger for internal interfacing use call the callback as soon - as it is primed and then remove itself from the scheduler + """Trigger for internal interfacing use call the callback as soon + as it is primed and then remove itself from the scheduler. """ def __init__(self, name=""): Trigger.__init__(self) @@ -502,9 +490,8 @@ def prime(self, callback): class Join(with_metaclass(ParametrizedSingleton, PythonTrigger)): - """ - Join a coroutine, firing when it exits - """ + """Join a coroutine, firing when it exits.""" + @classmethod def __singleton_key__(cls, coroutine): return coroutine diff --git a/cocotb/utils.py b/cocotb/utils.py index 80ac3e55..6ff6aeb2 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -27,7 +27,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -"""Collection of handy functions""" +"""Collection of handy functions.""" import ctypes import math @@ -58,8 +58,8 @@ def get_sim_time(units=None): Args: units (str or None, optional): String specifying the units of the result - (``None``,'fs','ps','ns','us','ms','sec'). ``None`` will return the raw - simulation time. + (one of ``None``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``). + ``None`` will return the raw simulation time. Returns: The simulation time in the specified units. @@ -74,35 +74,35 @@ def get_sim_time(units=None): return result def get_time_from_sim_steps(steps, units): - """Calculates simulation time in the specified units from the steps based + """Calculates simulation time in the specified *units* from the *steps* based on the simulator precision. Args: - steps (int): Number of simulation steps + steps (int): Number of simulation steps. units (str): String specifying the units of the result - ('fs','ps','ns','us','ms','sec'). + (one of ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``). Returns: - The simulation time in the specified units + The simulation time in the specified units. """ result = steps * (10.0**(_LOG_SIM_PRECISION - _get_log_time_scale(units))) return result def get_sim_steps(time, units=None): - """Calculates the number of simulation time steps for a given amount of time + """Calculates the number of simulation time steps for a given amount of *time*. Args: - time (int/float): The value to convert to simulation time steps. + time (int or float): The value to convert to simulation time steps. units (str or None, optional): String specifying the units of the result - (``None``,'fs','ps','ns','us','ms','sec'). ``None`` means time is already in - simulation time steps. + (one of ``None``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``). + ``None`` means time is already in simulation time steps. Returns: - int: The number of simulation time steps + int: The number of simulation time steps. Raises: - ValueError: If given time cannot be represented by simulator precision + :exc:`ValueError`: If given *time* cannot be represented by simulator precision. """ result = time if units is not None: @@ -118,14 +118,14 @@ def get_sim_steps(time, units=None): return int(result) def _get_log_time_scale(units): - """Retrieves the log10() of the scale factor for a given time unit + """Retrieves the ``log10()`` of the scale factor for a given time unit. Args: units (str): String specifying the units - ('fs','ps','ns','us','ms','sec'). + (one of ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``). Returns: - The the log10() of the scale factor for the time unit + The the ``log10()`` of the scale factor for the time unit. """ scale = { 'fs' : -15, @@ -145,35 +145,35 @@ def _get_log_time_scale(units): def pack(ctypes_obj): - """Convert a ctypes structure into a Python string + """Convert a :mod:`ctypes` structure into a Python string. Args: - ctypes_obj (ctypes.Structure): ctypes structure to convert to a string + ctypes_obj (ctypes.Structure): The ctypes structure to convert to a string. Returns: - New Python string containing the bytes from memory holding ctypes_obj + New Python string containing the bytes from memory holding *ctypes_obj*. """ return ctypes.string_at(ctypes.addressof(ctypes_obj), ctypes.sizeof(ctypes_obj)) def unpack(ctypes_obj, string, bytes=None): - """Unpack a Python string into a ctypes structure + """Unpack a Python string into a :mod:`ctypes` structure. + + If the length of *string* is not the correct size for the memory + footprint of the ctypes structure then the *bytes* keyword argument + must be used. Args: - ctypes_obj (ctypes.Structure): ctypes structure to pack into - string (str): String to copy over the ctypes_obj memory space + ctypes_obj (ctypes.Structure): The ctypes structure to pack into. + string (str): String to copy over the ctypes_obj memory space. bytes (int, optional): Number of bytes to copy. - Defaults to ``None``, meaning the length of the string is used. + Defaults to ``None``, meaning the length of *string* is used. Raises: - ValueError: If length of ``string`` and size of ``ctypes_obj`` + :exc:`ValueError`: If length of *string* and size of *ctypes_obj* are not equal. - MemoryError: If ``bytes`` is longer than size of ``ctypes_obj``. - - If the length of the string is not the correct size for the memory - footprint of the ctypes structure then the ``bytes`` keyword argument - must be used. + :exc:`MemoryError`: If *bytes* is longer than size of *ctypes_obj*. """ if bytes is None: if len(string) != ctypes.sizeof(ctypes_obj): @@ -203,13 +203,13 @@ def _sane_color(x): def hexdump(x): - """Hexdump a buffer + """Hexdump a buffer. Args: x: Object that supports conversion via the ``str`` built-in. Returns: - A string containing the hexdump + A string containing the hexdump. Example: @@ -243,11 +243,11 @@ def hexdump(x): def hexdiffs(x, y): - """Return a diff string showing differences between 2 binary strings + """Return a diff string showing differences between two binary strings. Args: - x: Object that supports conversion via the ``str`` built-in - y: Object that supports conversion via the ``str`` built-in + x: Object that supports conversion via the ``str`` built-in. + y: Object that supports conversion via the ``str`` built-in. Example: @@ -274,7 +274,8 @@ def sane(x): return r def highlight(string, colour=ANSI.COLOR_HILITE_HEXDIFF_DEFAULT): - """Highlight only with ansi output if it's requested and we are not in a GUI""" + """Highlight only with ANSI output if it's requested and we are not in a GUI.""" + want_ansi = os.getenv("COCOTB_ANSI_OUTPUT") and not os.getenv("GUI") if want_ansi is None: want_ansi = sys.stdout.isatty() # default to ANSI for TTYs @@ -458,9 +459,9 @@ def __prepare__(cls, name, this_bases): class ParametrizedSingleton(type): - """A metaclass that allows class construction to reuse an existing instance + """A metaclass that allows class construction to reuse an existing instance. - We use this so that `RisingEdge(sig)` and `Join(coroutine)` always return + We use this so that :class:`RisingEdge(sig) ` and :class:`Join(coroutine) ` always return the same instance, rather than creating new copies. """ @@ -471,8 +472,7 @@ def __init__(cls, *args, **kwargs): cls.__instances = weakref.WeakValueDictionary() def __singleton_key__(cls, *args, **kwargs): - """ - Convert the construction arguments into a normalized representation that + """Convert the construction arguments into a normalized representation that uniquely identifies this singleton. """ # Once we drop python 2, we can implement a default like the following, diff --git a/documentation/source/building.rst b/documentation/source/building.rst index 85803d61..32a9ae2b 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -104,7 +104,7 @@ Environment Variables ``TESTCASE`` The name of the test function(s) to run. If this variable is not defined Cocotb - discovers and executes all functions decorated with the :py:class:`cocotb.test` decorator in the supplied modules. + discovers and executes all functions decorated with the :class:`cocotb.test` decorator in the supplied modules. Multiple functions can be specified in a comma-separated list. @@ -129,7 +129,7 @@ Additional Environment Variables ``COCOTB_HOOKS`` A comma-separated list of modules that should be executed before the first test. - You can also use the :py:class:`cocotb.hook` decorator to mark a function to be run before test code. + You can also use the :class:`cocotb.hook` decorator to mark a function to be run before test code. ``COCOTB_LOG_LEVEL`` Default logging level to use. This is set to ``INFO`` unless overridden. @@ -139,7 +139,7 @@ Additional Environment Variables Valid settings are: ``VALUE_ERROR`` - raise a ``ValueError`` exception + raise a :exc:`ValueError` exception ``ZEROS`` resolve to ``0`` ``ONES`` @@ -156,7 +156,7 @@ Additional Environment Variables HTTP port to use for debugging Python's memory usage. When set to e.g. ``8088``, data will be presented at ``_. - This needs the :py:mod:`cherrypy` and :py:mod:`dowser` Python modules installed. + This needs the :mod:`cherrypy` and :mod:`dowser` Python modules installed. ``SIM_ROOT`` The root directory of the Cocotb installation. diff --git a/documentation/source/coroutines.rst b/documentation/source/coroutines.rst index 863012bf..e382e772 100644 --- a/documentation/source/coroutines.rst +++ b/documentation/source/coroutines.rst @@ -1,13 +1,13 @@ Coroutines ========== -Testbenches built using Cocotb use coroutines. While the `coroutine` is executing -the simulation is paused. The `coroutine` uses the :keyword:`yield` keyword to +Testbenches built using cocotb use coroutines. While the coroutine is executing +the simulation is paused. The coroutine uses the :keyword:`yield` keyword to pass control of execution back to the simulator and simulation time can advance again. -Typically coroutines :keyword:`yield` a :py:class:`Trigger` object which -indicates to the simulator some event which will cause the `coroutine` to be woken +Typically coroutines :keyword:`yield` a :any:`Trigger` object which +indicates to the simulator some event which will cause the coroutine to be woken when it occurs. For example: .. code-block:: python @@ -28,7 +28,7 @@ Coroutines may also yield other coroutines: yield wait_10ns() Coroutines can return a value, so that they can be used by other coroutines. -Before python 3.3, this requires a `ReturnValue` to be raised. +Before Python 3.3, this requires a :any:`ReturnValue` to be raised. .. code-block:: python @@ -39,7 +39,7 @@ Before python 3.3, this requires a `ReturnValue` to be raised. @cocotb.coroutine def get_signal_python_33(clk, signal): - # newer versions of python can use return normally + # newer versions of Python can use return normally yield RisingEdge(clk) return signal.value @@ -58,18 +58,18 @@ execution should resume if *any* of them fires: @cocotb.coroutine def packet_with_timeout(monitor, timeout): - """Wait for a packet but timeout if nothing arrives""" + """Wait for a packet but time out if nothing arrives""" yield [Timer(timeout), RisingEdge(dut.ready)] -The trigger that caused execution to resume is passed back to the `coroutine`, +The trigger that caused execution to resume is passed back to the coroutine, allowing them to distinguish which trigger fired: .. code-block:: python @cocotb.coroutine def packet_with_timeout(monitor, timeout): - """Wait for a packet but timeout if nothing arrives""" + """Wait for a packet but time out if nothing arrives""" tout_trigger = Timer(timeout) result = yield [tout_trigger, RisingEdge(dut.ready)] if result is tout_trigger: @@ -85,10 +85,11 @@ the forked code. def test_act_during_reset(dut): """While reset is active, toggle signals""" tb = uart_tb(dut) - #Clock is a built in class for toggling a clock signal + # "Clock" is a built in class for toggling a clock signal cocotb.fork(Clock(dut.clk, 1000).start()) - #reset_dut is a function- part of the user generated uart_tb class. - cocotb.fork(tb.reset_dut(dut.rstn,20000)) + # reset_dut is a function - + # part of the user-generated "uart_tb" class + cocotb.fork(tb.reset_dut(dut.rstn, 20000)) yield Timer(10000) print("Reset is still active: %d" % dut.rstn) @@ -135,7 +136,7 @@ they'd naturally end. yield Timer(1) yield RisingEdge(dut.clk) edge_time_ns = get_sim_time(units='ns') - # NOTE: isclose is a python 3.5+ feature. + # NOTE: isclose is a python 3.5+ feature if not isclose(edge_time_ns, start_time_ns + 1000.0): raise TestFailure("Expected a period of 1 us") @@ -146,7 +147,7 @@ they'd naturally end. yield Timer(1) yield RisingEdge(dut.clk) edge_time_ns = get_sim_time(units='ns') - # note, isclose is a python 3.5+ feature + # NOTE: isclose is a python 3.5+ feature if not isclose(edge_time_ns, start_time_ns + 4.0): raise TestFailure("Expected a period of 4 ns") diff --git a/documentation/source/endian_swapper.rst b/documentation/source/endian_swapper.rst index 736a5fed..12aa369a 100644 --- a/documentation/source/endian_swapper.rst +++ b/documentation/source/endian_swapper.rst @@ -1,12 +1,12 @@ - Tutorial: Endian Swapper ======================== -In this tutorial we'll use some of the built-in features of Cocotb to quickly create a complex testbench. +In this tutorial we'll use some of the built-in features of cocotb to quickly create a complex testbench. .. note:: All the code and sample output from this example are available on `EDA Playground `_ -For the impatient this tutorial is provided as an example with Cocotb. You can run this example from a fresh checkout:: +For the impatient this tutorial is provided as an example with cocotb. +You can run this example from a fresh checkout:: cd examples/endian_swapper/tests make @@ -15,16 +15,21 @@ For the impatient this tutorial is provided as an example with Cocotb. You can r Design ------ -We have a relatively simplistic RTL block called the endian_swapper. The DUT has three interfaces, all conforming to the Avalon standard: +We have a relatively simplistic RTL block called the ``endian_swapper``. +The DUT has three interfaces, all conforming to the Avalon standard: .. image:: diagrams/svg/endian_swapper_design.svg -The DUT will swap the endianness of packets on the Avalon-ST bus if a configuration bit is set. For every packet arriving on the "stream_in" interface the entire packet will be endian swapped if the configuration bit is set, otherwise the entire packet will pass through unmodified. +The DUT will swap the endianness of packets on the Avalon-ST bus if a configuration bit is set. +For every packet arriving on the ``stream_in`` interface the entire packet will be endian swapped +if the configuration bit is set, otherwise the entire packet will pass through unmodified. Testbench --------- -To begin with we create a class to encapsulate all the common code for the testbench. It is possible to write directed tests without using a testbench class however to encourage code re-use it is good practice to create a distinct class. +To begin with we create a class to encapsulate all the common code for the testbench. +It is possible to write directed tests without using a testbench class +however to encourage code re-use it is good practice to create a distinct class. .. code-block:: python @@ -54,20 +59,24 @@ If we inspect this line-by-line: self.stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) -Here we're creating an AvalonSTDriver instance. The constructor requires 3 arguments - a handle to the entity containing the interface (**dut**), the name of the interface (**stream_in**) and the associated clock with which to drive the interface (**dut.clk**). The driver will auto-discover the signals for the interface, assuming that they follow the naming convention **interface_name** _ *signal*. - -In this case we have the following signals defined for the **stream_in** interface: - -======================= =============== ============================================================================================== -Name Type Description (from Avalon Specification) -======================= =============== ============================================================================================== -stream_in_data data The data signal from the source to the sink -stream_in_empty empty Indicates the number of symbols that are empty during cycles that contain the end of a packet -stream_in_valid valid Asserted by the source to qualify all other source to sink signals -stream_in_startofpacket startofpacket Asserted by the source to mark the beginning of a packet -stream_in_endofpacket endofpacket Asserted by the source to mark the end of a packet -stream_in_ready ready Asserted high to indicate that the sink can accept data -======================= =============== ============================================================================================== +Here we are creating an :class:`AvalonSTDriver ` instance. +The constructor requires 3 arguments - a handle to the entity containing the interface (``dut``), +the name of the interface (``stream_in``) and the associated clock with which to drive the interface (``dut.clk``). +The driver will auto-discover the signals for the interface, +assuming that they follow the naming convention ``_``. + +In this case we have the following signals defined for the ``stream_in`` interface: + +=========================== ================= ============================================================================================== +Name Type Description (from Avalon Specification) +=========================== ================= ============================================================================================== +``stream_in_data`` ``data`` The data signal from the source to the sink +``stream_in_empty`` ``empty`` Indicates the number of symbols that are empty during cycles that contain the end of a packet +``stream_in_valid`` ``valid`` Asserted by the source to qualify all other source to sink signals +``stream_in_startofpacket`` ``startofpacket`` Asserted by the source to mark the beginning of a packet +``stream_in_endofpacket`` ``endofpacket`` Asserted by the source to mark the end of a packet +``stream_in_ready`` ``ready`` Asserted high to indicate that the sink can accept data +=========================== ================= ============================================================================================== By following the signal naming convention the driver can find the signals associated with this interface automatically. @@ -77,7 +86,7 @@ By following the signal naming convention the driver can find the signals associ self.stream_out = AvalonSTMonitor(dut, "stream_out", dut.clk) self.csr = AvalonMaster(dut, "csr", dut.clk) -We do the same to create the monitor on **stream_out** and the CSR interface. +We do the same to create the :class:`monitor ` on ``stream_out`` and the CSR interface. .. code-block:: python @@ -86,14 +95,25 @@ We do the same to create the monitor on **stream_out** and the CSR interface. self.scoreboard = Scoreboard(dut) self.scoreboard.add_interface(self.stream_out, self.expected_output) -The above lines create a Scoreboard instance and attach it to the **stream_out** monitor instance. The scoreboard is used to check that the DUT behaviour is correct. The call to **add_interface** takes a Monitor instance as the first argument and the second argument is a mechanism for describing the expected output for that interface. This could be a callable function but in this example a simple list of expected transactions is sufficient. +The above lines create a :class:`.Scoreboard` instance and attach it to the ``stream_out`` monitor instance. +The scoreboard is used to check that the DUT behaviour is correct. +The call to :meth:`.add_interface()` takes a Monitor instance as the first argument and +the second argument is a mechanism for describing the expected output for that interface. +This could be a callable function but in this example a simple list of expected transactions is sufficient. .. code-block:: python # Reconstruct the input transactions from the pins and send them to our 'model' self.stream_in_recovered = AvalonSTMonitor(dut, "stream_in", dut.clk, callback=self.model) -Finally we create another Monitor instance, this time connected to the **stream_in** interface. This is to reconstruct the transactions being driven into the DUT. It's good practice to use a monitor to reconstruct the transactions from the pin interactions rather than snooping them from a higher abstraction layer as we can gain confidence that our drivers and monitors are functioning correctly. We also pass the keyword argument **callback** to the monitor constructor which will result in the supplied function being called for each transaction seen on the bus with the transaction as the first argument. Our model function is quite straightforward in this case - we simply append the transaction to the expected output list and increment a counter: +Finally we create another Monitor instance, this time connected to the ``stream_in`` interface. +This is to reconstruct the transactions being driven into the DUT. +It's good practice to use a monitor to reconstruct the transactions from the pin interactions +rather than snooping them from a higher abstraction layer as we can gain confidence that our drivers and monitors are functioning correctly. + +We also pass the keyword argument ``callback`` to the monitor constructor which will result +in the supplied function being called for each transaction seen on the bus with the transaction as the first argument. +Our model function is quite straightforward in this case - we simply append the transaction to the expected output list and increment a counter: .. code-block:: python @@ -109,11 +129,12 @@ Test Function There are various 'knobs' we can tweak on this testbench to vary the behaviour: * Packet size -* Backpressure on the **stream_out** interface -* Idle cycles on the **stream_in** interface +* Backpressure on the ``stream_out`` interface +* Idle cycles on the ``stream_in`` interface * Configuration switching of the endian swap register during the test. -We want to run different variations of tests but they will all have a very similar structure so we create a common ``run_test`` function. To generate backpressure on the **stream_out** interface we use the ``BitDriver`` class from ``cocotb.drivers``. +We want to run different variations of tests but they will all have a very similar structure so we create a common ``run_test`` function. +To generate backpressure on the ``stream_out`` interface we use the :class:`.BitDriver` class from :mod:`cocotb.drivers`. .. code-block:: python @@ -154,13 +175,18 @@ We want to run different variations of tests but they will all have a very simil raise tb.scoreboard.result -We can see that this test function creates an instance of the testbench, resets the DUT by running the `coroutine` ``tb.reset()`` and then starts off any optional coroutines passed in using the keyword arguments. We then send in all the packets from ``data_in``, ensure that all the packets have been received by waiting 2 cycles at the end. We read the packet count and compare this with the number of packets. Finally we use the ``tb.scoreboard.result`` to determine the status of the test. If any transactions didn't match the expected output then this member would be an instance of the ``TestFailure`` result. +We can see that this test function creates an instance of the testbench, +resets the DUT by running the coroutine ``tb.reset()`` and then starts off any optional coroutines passed in using the keyword arguments. +We then send in all the packets from ``data_in``, ensure that all the packets have been received by waiting 2 cycles at the end. +We read the packet count and compare this with the number of packets. +Finally we use the :any:`tb.scoreboard.result ` to determine the status of the test. +If any transactions didn't match the expected output then this member would be an instance of the :exc:`.TestFailure` result. Test permutations ~~~~~~~~~~~~~~~~~ -Having defined a test function we can now auto-generate different permutations of tests using the ``TestFactory`` class: +Having defined a test function we can now auto-generate different permutations of tests using the :class:`.TestFactory` class: .. code-block:: python @@ -171,4 +197,5 @@ Having defined a test function we can now auto-generate different permutations o factory.add_option("backpressure_inserter", [None, wave, intermittent_single_cycles, random_50_percent]) factory.generate_tests() -This will generate 32 tests (named run_test_001 to run_test_032) with all possible permutations of options provided for each argument. Note that we utilise some of the built-in generators to toggle backpressure and insert idle cycles. +This will generate 32 tests (named ``run_test_001`` to ``run_test_032``) with all possible permutations of the options provided for each argument. +Note that we utilise some of the built-in generators to toggle backpressure and insert idle cycles. diff --git a/documentation/source/library_reference.rst b/documentation/source/library_reference.rst index 8acc9abf..e82225bd 100644 --- a/documentation/source/library_reference.rst +++ b/documentation/source/library_reference.rst @@ -5,15 +5,12 @@ Library Reference Test Results ============ -The following exceptions can be raised at any point by any code and will terminate the test: +The exceptions in this module can be raised at any point by any code and will terminate the test. -.. autoclass:: cocotb.result.TestComplete - -.. autoclass:: cocotb.result.TestError - -.. autoclass:: cocotb.result.TestFailure - -.. autoclass:: cocotb.result.TestSuccess +.. automodule:: cocotb.result + :members: + :member-order: bysource + :synopsis: Exceptions and functions for simulation result handling. Writing and Generating tests @@ -25,6 +22,7 @@ Writing and Generating tests .. autoclass:: cocotb.regression.TestFactory :members: + :member-order: bysource .. autoclass:: cocotb.hook @@ -32,12 +30,19 @@ Writing and Generating tests Interacting with the Simulator ============================== +.. currentmodule:: cocotb.binary + +.. autoclass:: BinaryRepresentation + :members: + :member-order: bysource -.. autoclass:: cocotb.binary.BinaryValue +.. autoclass:: BinaryValue :members: + :member-order: bysource .. autoclass:: cocotb.bus.Bus :members: + :member-order: bysource .. autoclass:: cocotb.clock.Clock @@ -45,7 +50,8 @@ Interacting with the Simulator Triggers -------- -Triggers are used to indicate when the scheduler should resume `coroutine` execution. Typically a `coroutine` will **yield** a trigger or a list of triggers. +Triggers are used to indicate when the scheduler should resume coroutine execution. +Typically a coroutine will :keyword:`yield` a trigger or a list of triggers. Simulation Timing ~~~~~~~~~~~~~~~~~ @@ -54,6 +60,9 @@ Simulation Timing .. autoclass:: cocotb.triggers.ReadOnly +.. autoclass:: cocotb.triggers.NextTimeStep + +.. autoclass:: cocotb.triggers.ClockCycles Signal related ~~~~~~~~~~~~~~ @@ -62,19 +71,23 @@ Signal related .. autoclass:: cocotb.triggers.RisingEdge +.. autoclass:: cocotb.triggers.FallingEdge + Python Triggers ~~~~~~~~~~~~~~~ - .. autoclass:: cocotb.triggers.Event :members: + :member-order: bysource .. autoclass:: cocotb.triggers.Lock :members: + :member-order: bysource .. autoclass:: cocotb.triggers.Join :members: + :member-order: bysource Testbench Structure @@ -85,6 +98,19 @@ Driver .. autoclass:: cocotb.drivers.Driver :members: + :member-order: bysource + :private-members: + +.. autoclass:: cocotb.drivers.BitDriver + :members: + :member-order: bysource + :show-inheritance: + :private-members: + +.. autoclass:: cocotb.drivers.BusDriver + :members: + :member-order: bysource + :show-inheritance: :private-members: Monitor @@ -92,22 +118,52 @@ Monitor .. autoclass:: cocotb.monitors.Monitor :members: + :member-order: bysource :private-members: .. autoclass:: cocotb.monitors.BusMonitor :members: + :member-order: bysource + :show-inheritance: + :private-members: Scoreboard ---------- -.. autoclass:: cocotb.scoreboard.Scoreboard +.. currentmodule:: cocotb.scoreboard + +.. automodule:: cocotb.scoreboard :members: + :member-order: bysource + :show-inheritance: + :synopsis: Class for scoreboards. Clock ----- .. autoclass:: cocotb.clock.Clock :members: + :member-order: bysource + + +Utilities +========= + +.. automodule:: cocotb.utils + :members: + :member-order: bysource + :synopsis: Various utilities for testbench writers. + +Simulation Object Handles +========================= + +.. currentmodule:: cocotb.handle + +.. automodule:: cocotb.handle + :members: + :member-order: bysource + :show-inheritance: + :synopsis: Classes for simulation objects. Implemented Testbench Structures ================================ @@ -118,6 +174,8 @@ Drivers AD9361 ~~~~~~ +Analog Devices AD9361 RF Transceiver. + .. currentmodule:: cocotb.drivers.ad9361 .. autoclass:: AD9361 @@ -127,12 +185,10 @@ AD9361 .. automethod:: ad9361_tx_to_rx_loopback() .. automethod:: tx_data_from_ad9361() - :members: - AMBA ~~~~ -Advanced Microcontroller Bus Archicture +Advanced Microcontroller Bus Architecture. .. currentmodule:: cocotb.drivers.amba @@ -142,9 +198,11 @@ Advanced Microcontroller Bus Archicture .. automethod:: read(address, sync=True) :members: + :member-order: bysource .. autoclass:: AXI4Slave :members: + :member-order: bysource Avalon @@ -154,6 +212,7 @@ Avalon .. autoclass:: AvalonMM :members: + :member-order: bysource .. autoclass:: AvalonMaster @@ -161,15 +220,20 @@ Avalon .. automethod:: read(address, sync=True) :members: + :member-order: bysource .. autoclass:: AvalonMemory :members: + :member-order: bysource .. autoclass:: AvalonST :members: + :member-order: bysource .. autoclass:: AvalonSTPkts :members: + :member-order: bysource + OPB ~~~ @@ -182,6 +246,7 @@ OPB .. automethod:: read(address, sync=True) :members: + :member-order: bysource XGMII ~~~~~ @@ -190,6 +255,7 @@ XGMII .. autoclass:: XGMII :members: + :member-order: bysource Monitors -------- @@ -201,78 +267,15 @@ Avalon .. autoclass:: AvalonST :members: + :member-order: bysource .. autoclass:: AvalonSTPkts :members: + :member-order: bysource XGMII ~~~~~ .. autoclass:: cocotb.monitors.xgmii.XGMII :members: - -Utilities -========= - -.. automodule:: cocotb.utils - :members: - -Simulation Object Handles -========================= - -.. currentmodule:: cocotb.handle - -.. autofunction:: cocotb.handle.SimHandle - -.. autoclass:: cocotb.handle.SimHandleBase - :members: - :show-inheritance: - -.. autoclass:: cocotb.handle.RegionObject - :members: - :show-inheritance: - -.. autoclass:: cocotb.handle.HierarchyObject - :members: - :show-inheritance: - -.. autoclass:: cocotb.handle.HierarchyArrayObject - :members: - :show-inheritance: - -.. autoclass:: cocotb.handle.NonHierarchyObject - :members: - :show-inheritance: - -.. autoclass:: cocotb.handle.ConstantObject - :members: - :show-inheritance: - -.. autoclass:: cocotb.handle.NonHierarchyIndexableObject - :members: - :show-inheritance: - -.. autoclass:: cocotb.handle.NonConstantObject - :members: - :show-inheritance: - -.. autoclass:: cocotb.handle.ModifiableObject - :members: - :show-inheritance: - -.. autoclass:: cocotb.handle.RealObject - :members: - :show-inheritance: - -.. autoclass:: cocotb.handle.EnumObject - :members: - :show-inheritance: - -.. autoclass:: cocotb.handle.IntegerObject - :members: - :show-inheritance: - -.. autoclass:: cocotb.handle.StringObject - :members: - :show-inheritance: - + :member-order: bysource diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index b7c961fc..cc78fd7c 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -17,8 +17,8 @@ Cocotb has the following requirements: * GNU Make * A Verilog or VHDL simulator, depending on your source RTL code -Internal development is performed on Linux Mint 17 (x64). We also use Redhat -6.5(x64). Other Redhat and Ubuntu based distributions (x32 and x64) should work +Internal development is performed on Linux Mint 17 (x64). We also use RedHat +6.5(x64). Other RedHat and Ubuntu based distributions (x32 and x64) should work too but due fragmented nature of Linux we can not test everything. Instructions are provided for the main distributions we use. @@ -31,25 +31,25 @@ Ubuntu based installation $> sudo apt-get install git make gcc g++ swig python-dev -This will allow building of the Cocotb libs for use with a 64 bit native -simulator. If a 32 bit simulator is being used then additional steps to install -32bit development libraries and python are needed. +This will allow building of the cocotb libs for use with a 64-bit native +simulator. If a 32-bit simulator is being used then additional steps to install +32-bit development libraries and Python are needed. -Redhat based installation +RedHat based installation .. code-block:: bash $> sudo yum install gcc gcc-c++ libstdc++-devel swig python-devel -This will allow building of the Cocotb libs for use with a 64 bit native -simulator. If a 32 bit simulator is being used then additional steps to install -32bit development libraries and python are needed. +This will allow building of the cocotb libs for use with a 64-bit native +simulator. If a 32-bit simulator is being used then additional steps to install +32-bit development libraries and Python are needed. -32 bit Python +32-bit Python ------------- -Additional development libraries are needed for building 32bit python on 64 bit +Additional development libraries are needed for building 32-bit Python on 64-bit systems. Ubuntu based installation @@ -58,11 +58,11 @@ Ubuntu based installation $> sudo apt-get install libx32gcc1 gcc-4.8-multilib lib32stdc++-4.8-dev -Replace 4.8 with the version of gcc that was installed on the system in the step -above. Unlike on Redhat where 32 bit python can co-exist with native python -ubuntu requires the source to be downloaded and built. +Replace 4.8 with the version of GCC that was installed on the system in the step +above. Unlike on RedHat where 32-bit Python can co-exist with native Python, +Ubuntu requires the source to be downloaded and built. -Redhat based installation +RedHat based installation .. code-block:: bash @@ -81,9 +81,9 @@ Specific releases can be downloaded from https://www.python.org/downloads/ . $> make $> sudo make install -Cocotb can now be built against 32bit python by setting the architecture and -placing the 32bit python ahead of the native version in the path when running a -test +Cocotb can now be built against 32-bit Python by setting the architecture and +placing the 32-bit Python ahead of the native version in the path when running a +test. .. code-block:: bash @@ -94,7 +94,7 @@ test Windows 7 installation ---------------------- -Recent work has been done with the support of the Cocotb community to enable +Work has been done with the support of the cocotb community to enable Windows support using the MinGW/Msys environment. Download the MinGQ installer from. @@ -122,22 +122,21 @@ When installed a shell can be opened using the "msys.bat" file located under the /msys/1.0/ Python can be downloaded from https://www.python.org/ftp/python/2.7.9/python-2.7.9.msi, -other versions of python can be used as well. Run the installer and download to +other versions of Python can be used as well. Run the installer and download to your chosen location. -It is beneficial to add the path to Python to the windows system PATH variable +It is beneficial to add the path to Python to the Windows system ``PATH`` variable so it can be used easily from inside Msys. Once inside the Msys shell commands as given here will work as expected. -MAC Packages +Mac Packages ------------ -You need a few packages installed to get cocotb running on mac. +You need a few packages installed to get cocotb running on Mac. Installing a package manager really helps things out here. -Brew_ seems to be the most popular, so we'll assume you have that installed. -.. _Brew: http://www.brew.sh +`Brew `_ seems to be the most popular, so we'll assume you have that installed. .. code-block::bash @@ -162,8 +161,8 @@ To run a test using a different simulator: Running a VHDL example ---------------------- -The endian swapper example includes both a VHDL and Verilog RTL implementation. -The Cocotb testbench can execute against either implementation using VPI for +The ``endian_swapper`` example includes both a VHDL and a Verilog RTL implementation. +The cocotb testbench can execute against either implementation using VPI for Verilog and VHPI/FLI for VHDL. To run the test suite against the VHDL implementation use the following command (a VHPI or FLI capable simulator must be used): @@ -177,7 +176,7 @@ be used): Using Cocotb ============ -A typical Cocotb testbench requires no additional RTL code. +A typical cocotb testbench requires no additional RTL code. The Design Under Test (DUT) is instantiated as the toplevel in the simulator without any wrapper code. Cocotb drives stimulus onto the inputs to the DUT and monitors the outputs @@ -187,16 +186,16 @@ directly from Python. Creating a Makefile ------------------- -To create a Cocotb test we typically have to create a Makefile. Cocotb provides -rules which make it easy to get started. We simply inform Cocotb of the +To create a cocotb test we typically have to create a Makefile. Cocotb provides +rules which make it easy to get started. We simply inform cocotb of the source files we need compiling, the toplevel entity to instantiate and the -python test script to load. +Python test script to load. .. code-block:: bash VERILOG_SOURCES = $(PWD)/submodule.sv $(PWD)/my_design.sv - TOPLEVEL=my_design #the module name in your verilog or vhdl file - MODULE=test_my_design # the name of the python test file + TOPLEVEL=my_design # the module name in your Verilog or VHDL file + MODULE=test_my_design # the name of the Python test file include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim @@ -208,8 +207,8 @@ Creating a test The test is written in Python. Cocotb wraps your top level with the handle you pass it. In this documentation, and most of the examples in the project, that -handle is **dut**, but you can pass your own preferred name in instead. The -handle is used in all python files referencing your RTL project. Assuming we +handle is ``dut``, but you can pass your own preferred name in instead. The +handle is used in all Python files referencing your RTL project. Assuming we have a toplevel port called ``clk`` we could create a test file containing the following: @@ -238,7 +237,7 @@ Accessing the design -------------------- When cocotb initialises it finds the top-level instantiation in the simulator -and creates a handle called **dut**. Top-level signals can be accessed using the +and creates a handle called ``dut``. Top-level signals can be accessed using the "dot" notation used for accessing object attributes in Python. The same mechanism can be used to access signals inside the design. @@ -247,14 +246,17 @@ can be used to access signals inside the design. # Get a reference to the "clk" signal on the top-level clk = dut.clk - # Get a reference to a register "count" in a sub-block "inst_sub_block" + # Get a reference to a register "count" + # in a sub-block "inst_sub_block" count = dut.inst_sub_block.count Assigning values to signals --------------------------- -Values can be assigned to signals using either the .value property of a handle object or using direct assignment while traversing the hierarchy. +Values can be assigned to signals using either the +:attr:`~cocotb.handle.NonHierarchyObject.value` property of a handle object +or using direct assignment while traversing the hierarchy. .. code-block:: python @@ -269,21 +271,24 @@ Values can be assigned to signals using either the .value property of a handle o dut.sub_block.memory.array[4] <= 2 -The syntax `sig <= new_value` is a short form of `sig.value = new_value`. It not -only resembles HDL-syntax, but also has the same semantics: writes are not -applied immediately, but delayed until the next write cycle. Use -`sig.setimmediatevalue(new_val)` to set a new value immediately. +The syntax ``sig <= new_value`` is a short form of ``sig.value = new_value``. +It not only resembles HDL-syntax, but also has the same semantics: +writes are not applied immediately, but delayed until the next write cycle. +Use ``sig.setimmediatevalue(new_val)`` to set a new value immediately +(see :meth:`~cocotb.handle.ModifiableObject.setimmediatevalue`). Reading values from signals --------------------------- -Accessing the .value property of a handle object will return a :class:`BinaryValue` object. Any unresolved bits are preserved and can be accessed using the binstr attribute, or a resolved integer value can be accessed using the value attribute. +Accessing the :attr:`~cocotb.handle.NonHierarchyObject.value` property of a handle object will return a :any:`BinaryValue` object. +Any unresolved bits are preserved and can be accessed using the :attr:`~cocotb.binary.BinaryValue.binstr` attribute, +or a resolved integer value can be accessed using the :attr:`~cocotb.binary.BinaryValue.integer` attribute. .. code-block:: python - >>> # Read a value back from the dut + >>> # Read a value back from the DUT >>> count = dut.counter.value >>> >>> print(count.binstr) @@ -292,7 +297,7 @@ Accessing the .value property of a handle object will return a :class:`BinaryVal >>> print(count.integer) 42 >>> # Show number of bits in a value - >>> print(count.bits) + >>> print(count.n_bits) 6 We can also cast the signal handle directly to an integer: diff --git a/documentation/source/testbench_tools.rst b/documentation/source/testbench_tools.rst index 27ffb9f2..1c29ffa9 100644 --- a/documentation/source/testbench_tools.rst +++ b/documentation/source/testbench_tools.rst @@ -1,21 +1,21 @@ ################ -Test Bench Tools +Testbench Tools ################ Logging ======= -Cocotb extends the python logging library. Each dut, monitor, driver, and -scoreboard (as well as any other function using the `coroutine` decorator) -implements it's own :class:`logging` object, and each can be set to it's own -logging level. Within a dut, each hierarchical object can also have individual +Cocotb extends the Python logging library. Each DUT, monitor, driver, and +scoreboard (as well as any other function using the coroutine decorator) +implements its own :class:`logging` object, and each can be set to its own +logging level. Within a DUT, each hierarchical object can also have individual logging levels set. -When logging hdl objects, beware that **_log** is the preferred way to use -logging. This helps minimize the change of name collisions with an hdl log -component with the python logging functionality. +When logging HDL objects, beware that ``_log`` is the preferred way to use +logging. This helps minimize the change of name collisions with an HDL log +component with the Python logging functionality. -Log printing levels can also be set on a per object basis. +Log printing levels can also be set on a per-object basis. .. code-block:: python @@ -69,9 +69,9 @@ will display as something like Busses ====== -Busses are simply defined as collection of signals. The :py:class:`Bus` class +Busses are simply defined as collection of signals. The :class:`.Bus` class will automatically bundle any group of signals together that are named similar -to dut.. for instance, +to ``dut.``. For instance, .. code-block:: python @@ -80,23 +80,23 @@ to dut.. for instance, have a bus name of ``stream_in``, a separator of ``_``, and signal names of ``valid`` and ``data``. A list of signal names, or a dictionary mapping attribute -names to signal names is also passed into the :py:class:`Bus` class. Busses can +names to signal names is also passed into the :class:`.Bus` class. Busses can have values driven onto them, be captured (returning a dictionary), or sampled and stored into a similar object. .. code-block:: python - stream_in_bus = Bus(dut, "stream_in", ["valid", "data"]) # '_' is the default separator + stream_in_bus = Bus(dut, "stream_in", ["valid", "data"]) # '_' is the default separator Driving Busses ============== -Examples and specific bus implementation bus drivers (amba, avalon, xgmii, and -others) exist in the :py:class:`Driver` class enabling a test to append +Examples and specific bus implementation bus drivers (AMBA, Avalon, XGMII, and +others) exist in the :class:`.Driver` class enabling a test to append transactions to perform the serialization of transactions onto a physical -interface. Here's an example using the avalon bus driver in the endian swapper -example +interface. Here is an example using the Avalon bus driver in the ``endian_swapper`` +example: .. code-block:: python @@ -127,17 +127,17 @@ Monitoring Busses ================= For our testbenches to actually be useful, we have to monitor some of these -busses, and not just drive them. That's where the :py:class:`Monitor` class -comes in, with prebuilt Monitors for **avalon** and **xgmii** busses. The +busses, and not just drive them. That's where the :class:`.Monitor` class +comes in, with prebuilt monitors for Avalon and XGMII busses. The Monitor class is a base class which you are expected to derive for your -particular purpose. You must create a `_monitor_recv()` function which is +particular purpose. You must create a :any:`_monitor_recv()` function which is responsible for determining 1) at what points in simulation to call the -`_recv()` function, and 2) what transaction values to pass to be stored in the -monitors receiving queue. Monitors are good for both outputs of the dut for -verification, and for the inputs of the dut, to drive a test model of the dut -to be compared to the actual dut. For this purpose, input monitors will often +:any:`_recv()` function, and 2) what transaction values to pass to be stored in the +monitors receiving queue. Monitors are good for both outputs of the DUT for +verification, and for the inputs of the DUT, to drive a test model of the DUT +to be compared to the actual DUT. For this purpose, input monitors will often have a callback function passed that is a model. This model will often generate -expected transactions, which are then compared using the :py:class:`Scoreboard` +expected transactions, which are then compared using the :class:`.Scoreboard` class. .. code-block:: python @@ -195,10 +195,10 @@ class. Tracking testbench errors ========================= -The :py:class:`Scoreboard` class is used to compare the actual outputs to +The :class:`.Scoreboard` class is used to compare the actual outputs to expected outputs. Monitors are added to the scoreboard for the actual outputs, and the expected outputs can be either a simple list, or a function that -provides a transaction. Here's some code from the **DFF** example, similar to +provides a transaction. Here is some code from the ``dff`` example, similar to above with the scoreboard added. .. code-block:: python diff --git a/documentation/source/triggers.rst b/documentation/source/triggers.rst index 27c50d55..4425dcbd 100644 --- a/documentation/source/triggers.rst +++ b/documentation/source/triggers.rst @@ -1,70 +1,48 @@ Triggers ======== -Triggers are used to indicate when the cocotb scheduler should resume `coroutine` execution. Typically a `coroutine` will **yield** a trigger or a list of triggers, while it's waiting for them to complete. +Triggers are used to indicate when the cocotb scheduler should resume coroutine execution. +Typically a coroutine will :keyword:`yield` a trigger or a list of triggers, +while it is waiting for them to complete. Simulation Timing ----------------- -Timer(time) -^^^^^^^^^^^ +:class:`Timer(time) <.Timer()>`: + Registers a timed callback with the simulator to continue execution of the coroutine + after a specified simulation time period has elapsed. -Registers a timed callback with the simulator to continue execution of the `coroutine` after a specified simulation time period has elapsed. - -.. todo:: - What is the behaviour if time=0? - - -ReadOnly() -^^^^^^^^^^ - -Registers a callback which will continue execution of the `coroutine` when the current simulation timestep moves to the ReadOnly phase of the rtl simulator. The ReadOnly phase is entered when the current timestep no longer has any further delta steps. This should be point where all the signal values are stable as there are no more rtl events scheduled for the timestep. The simulator should not allow scheduling of more events in this timestep. Useful for monitors which need to wait for all processes to execute (both RTL and cocotb) to ensure sampled signal values are final. +:class:`.ReadOnly()`: + Registers a callback which will continue execution of the coroutine when the current simulation timestep moves to the :any:`ReadOnly` phase of the RTL simulator. + The :any:`ReadOnly` phase is entered when the current timestep no longer has any further delta steps. + This should be a point where all the signal values are stable as there are no more RTL events scheduled for the timestep. + The simulator should not allow scheduling of more events in this timestep. + Useful for monitors which need to wait for all processes to execute (both RTL and cocotb) to ensure sampled signal values are final. Signal related -------------- -Edge(signal) -^^^^^^^^^^^^ - -Registers a callback that will continue execution of the `coroutine` on any value change of a signal. - -.. todo:: - Behaviour for vectors - - -RisingEdge(signal) -^^^^^^^^^^^^^^^^^^ - -Registers a callback that will continue execution of the `coroutine` on a transition from 0 to 1 of signal. +:class:`Edge(signal) <.Edge()>`: + Registers a callback that will continue execution of the coroutine on any value change of *signal*. +:class:`RisingEdge(signal) <.RisingEdge()>`: + Registers a callback that will continue execution of the coroutine on a transition from ``0`` to ``1`` of *signal*. -FallingEdge(signal) -^^^^^^^^^^^^^^^^^^^ +:class:`FallingEdge(signal) <.FallingEdge()>`: + Registers a callback that will continue execution of the coroutine on a transition from ``1`` to ``0`` of *signal*. -Registers a callback that will continue execution of the `coroutine` on a transition from 1 to 0 of signal. - - -ClockCycles(signal, num_cycles) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Registers a callback that will continue execution of the `coroutine` when num_cycles transistions from 0 to 1 have occured. +:class:`ClockCycles(signal, num_cycles) <.ClockCycles>`: + Registers a callback that will continue execution of the coroutine when *num_cycles* transitions from ``0`` to ``1`` have occured on *signal*. Python Triggers --------------- -Event() -^^^^^^^ - -Can be used to synchronise between coroutines. yielding Event.wait() will block the `coroutine` until Event.set() is called somewhere else. - - - -Join(coroutine) -^^^^^^^^^^^^^^^ - -Will block the `coroutine` until another `coroutine` has completed. - +:class:`.Event()`: + Can be used to synchronise between coroutines. + Yielding :meth:`.Event.wait()` will block the coroutine until :meth:`.Event.set()` is called somewhere else. +:class:`Join(coroutine_2) <.Join()>`: + Will block the coroutine until *coroutine_2* has completed. From 42e72ba3691685e985ff976c62a413b26a3e6f74 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Wed, 23 Jan 2019 21:31:07 +0000 Subject: [PATCH 1526/2656] Update version number to 1.1 --- documentation/source/conf.py | 4 ++-- version | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/source/conf.py b/documentation/source/conf.py index 4fbe4d08..cf81a560 100644 --- a/documentation/source/conf.py +++ b/documentation/source/conf.py @@ -66,9 +66,9 @@ # built documents. # # The short X.Y version. -version = '1.0' +version = '1.1' # The full version, including alpha/beta/rc tags. -release = '1.0' +release = '1.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/version b/version index 1f29be95..bb64ea34 100644 --- a/version +++ b/version @@ -1 +1 @@ -VERSION=1.0.1 +VERSION=1.1.0 From f692e8da1a75d3f3f938a21727514204725e52b8 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 12 Jan 2019 15:40:28 -0800 Subject: [PATCH 1527/2656] Use context managers to handle profiling This avoids needing to remember to disable the profiler by every return statement, and reads more cleanly than using a `try` / `finally` --- cocotb/scheduler.py | 271 ++++++++++++++++++++++---------------------- cocotb/utils.py | 20 ++++ 2 files changed, 158 insertions(+), 133 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 633ff5d4..b08969c6 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -71,6 +71,17 @@ from cocotb.log import SimLog from cocotb.result import (TestComplete, TestError, ReturnValue, raise_error, create_error, ExternalException) +from cocotb.utils import nullcontext + + +class profiling_context(object): + """ Context manager that profiles its contents """ + def __enter__(self): + _profile.enable() + + def __exit__(self, *excinfo): + _profile.disable() + class external_state(object): INIT = 0 @@ -280,26 +291,26 @@ def begin_test(self, trigger=None): if _profiling: ps = pstats.Stats(_profile).sort_stats('cumulative') ps.dump_stats("test_profile.pstat") - _profile.enable() - - self._mode = Scheduler._MODE_NORMAL - if trigger is not None: - trigger.unprime() + ctx = profiling_context() + else: + ctx = nullcontext() - # Issue previous test result, if there is one - if self._test_result is not None: - if _debug: - self.log.debug("Issue test result to regression object") - cocotb.regression.handle_result(self._test_result) - self._test_result = None - if self._entrypoint is not None: - test = self._entrypoint - self._entrypoint = None - self.schedule(test) - self.advance() + with ctx: + self._mode = Scheduler._MODE_NORMAL + if trigger is not None: + trigger.unprime() - if _profiling: - _profile.disable() + # Issue previous test result, if there is one + if self._test_result is not None: + if _debug: + self.log.debug("Issue test result to regression object") + cocotb.regression.handle_result(self._test_result) + self._test_result = None + if self._entrypoint is not None: + test = self._entrypoint + self._entrypoint = None + self.schedule(test) + self.advance() def react(self, trigger, depth=0): """React called when a trigger fires. @@ -308,142 +319,136 @@ def react(self, trigger, depth=0): schedule them. """ if _profiling and not depth: - _profile.enable() - - # When a trigger fires it is unprimed internally - if _debug: - self.log.debug("Trigger fired: %s" % str(trigger)) - # trigger.unprime() + ctx = profiling_context() + else: + ctx = nullcontext() - if self._mode == Scheduler._MODE_TERM: + with ctx: + # When a trigger fires it is unprimed internally if _debug: - self.log.debug("Ignoring trigger %s since we're terminating" % - str(trigger)) - return + self.log.debug("Trigger fired: %s" % str(trigger)) + # trigger.unprime() - if trigger is self._readonly: - self._mode = Scheduler._MODE_READONLY - # Only GPI triggers affect the simulator scheduling mode - elif isinstance(trigger, GPITrigger): - self._mode = Scheduler._MODE_NORMAL + if self._mode == Scheduler._MODE_TERM: + if _debug: + self.log.debug("Ignoring trigger %s since we're terminating" % + str(trigger)) + return - # We're the only source of ReadWrite triggers which are only used for - # playing back any cached signal updates - if trigger is self._readwrite: + if trigger is self._readonly: + self._mode = Scheduler._MODE_READONLY + # Only GPI triggers affect the simulator scheduling mode + elif isinstance(trigger, GPITrigger): + self._mode = Scheduler._MODE_NORMAL - if _debug: - self.log.debug("Writing cached signal updates") + # We're the only source of ReadWrite triggers which are only used for + # playing back any cached signal updates + if trigger is self._readwrite: + + if _debug: + self.log.debug("Writing cached signal updates") - while self._writes: - handle, value = self._writes.popitem() - handle.setimmediatevalue(value) + while self._writes: + handle, value = self._writes.popitem() + handle.setimmediatevalue(value) - self._readwrite.unprime() + self._readwrite.unprime() - if _profiling: - _profile.disable() - return + return - # Similarly if we've scheduled our next_timestep on way to readwrite - if trigger is self._next_timestep: + # Similarly if we've scheduled our next_timestep on way to readwrite + if trigger is self._next_timestep: - if not self._writes: - self.log.error( - "Moved to next timestep without any pending writes!") - else: - self.log.debug( - "Priming ReadWrite trigger so we can playback writes") - self._readwrite.prime(self.react) + if not self._writes: + self.log.error( + "Moved to next timestep without any pending writes!") + else: + self.log.debug( + "Priming ReadWrite trigger so we can playback writes") + self._readwrite.prime(self.react) - if _profiling: - _profile.disable() - return + return - if trigger not in self._trigger2coros: - - # GPI triggers should only be ever pending if there is an - # associated coroutine waiting on that trigger, otherwise it would - # have been unprimed already - if isinstance(trigger, GPITrigger): - self.log.critical( - "No coroutines waiting on trigger that fired: %s" % - str(trigger)) - - trigger.log.info("I'm the culprit") - # For Python triggers this isn't actually an error - we might do - # event.set() without knowing whether any coroutines are actually - # waiting on this event, for example - elif _debug: - self.log.debug( - "No coroutines waiting on trigger that fired: %s" % - str(trigger)) - - if _profiling: - _profile.disable() - return + if trigger not in self._trigger2coros: - # Scheduled coroutines may append to our waiting list so the first - # thing to do is pop all entries waiting on this trigger. - scheduling = self._trigger2coros.pop(trigger) + # GPI triggers should only be ever pending if there is an + # associated coroutine waiting on that trigger, otherwise it would + # have been unprimed already + if isinstance(trigger, GPITrigger): + self.log.critical( + "No coroutines waiting on trigger that fired: %s" % + str(trigger)) + + trigger.log.info("I'm the culprit") + # For Python triggers this isn't actually an error - we might do + # event.set() without knowing whether any coroutines are actually + # waiting on this event, for example + elif _debug: + self.log.debug( + "No coroutines waiting on trigger that fired: %s" % + str(trigger)) + + return + + # Scheduled coroutines may append to our waiting list so the first + # thing to do is pop all entries waiting on this trigger. + scheduling = self._trigger2coros.pop(trigger) - if _debug: - debugstr = "\n\t".join([coro.__name__ for coro in scheduling]) - if len(scheduling): - debugstr = "\n\t" + debugstr - self.log.debug("%d pending coroutines for event %s%s" % - (len(scheduling), str(trigger), debugstr)) - - # This trigger isn't needed any more - trigger.unprime() - - # If the coroutine was waiting on multiple triggers we may be able - # to unprime the other triggers that didn't fire - scheduling_set = set(scheduling) - other_triggers = { - t - for coro in scheduling - for t in self._coro2triggers[coro] - } - {trigger} - - for pending in other_triggers: - # every coroutine waiting on this trigger is already being woken - if scheduling_set.issuperset(self._trigger2coros[pending]): - if pending.primed: - pending.unprime() - del self._trigger2coros[pending] - - for coro in scheduling: - if _debug: - self.log.debug("Scheduling coroutine %s" % (coro.__name__)) - self.schedule(coro, trigger=trigger) if _debug: - self.log.debug("Scheduled coroutine %s" % (coro.__name__)) + debugstr = "\n\t".join([coro.__name__ for coro in scheduling]) + if len(scheduling): + debugstr = "\n\t" + debugstr + self.log.debug("%d pending coroutines for event %s%s" % + (len(scheduling), str(trigger), debugstr)) + + # This trigger isn't needed any more + trigger.unprime() - if not depth: - # Schedule may have queued up some events so we'll burn through those - while self._pending_events: + # If the coroutine was waiting on multiple triggers we may be able + # to unprime the other triggers that didn't fire + scheduling_set = set(scheduling) + other_triggers = { + t + for coro in scheduling + for t in self._coro2triggers[coro] + } - {trigger} + + for pending in other_triggers: + # every coroutine waiting on this trigger is already being woken + if scheduling_set.issuperset(self._trigger2coros[pending]): + if pending.primed: + pending.unprime() + del self._trigger2coros[pending] + + for coro in scheduling: if _debug: - self.log.debug("Scheduling pending event %s" % - (str(self._pending_events[0]))) - self._pending_events.pop(0).set() + self.log.debug("Scheduling coroutine %s" % (coro.__name__)) + self.schedule(coro, trigger=trigger) + if _debug: + self.log.debug("Scheduled coroutine %s" % (coro.__name__)) - while self._pending_triggers: - if _debug: - self.log.debug("Scheduling pending trigger %s" % - (str(self._pending_triggers[0]))) - self.react(self._pending_triggers.pop(0), depth=depth + 1) + if not depth: + # Schedule may have queued up some events so we'll burn through those + while self._pending_events: + if _debug: + self.log.debug("Scheduling pending event %s" % + (str(self._pending_events[0]))) + self._pending_events.pop(0).set() - # We only advance for GPI triggers - if not depth and isinstance(trigger, GPITrigger): - self.advance() + while self._pending_triggers: + if _debug: + self.log.debug("Scheduling pending trigger %s" % + (str(self._pending_triggers[0]))) + self.react(self._pending_triggers.pop(0), depth=depth + 1) - if _debug: - self.log.debug("All coroutines scheduled, handing control back" - " to simulator") + # We only advance for GPI triggers + if not depth and isinstance(trigger, GPITrigger): + self.advance() + + if _debug: + self.log.debug("All coroutines scheduled, handing control back" + " to simulator") - if _profiling: - _profile.disable() - return def unschedule(self, coro): """Unschedule a coroutine. Unprime any pending triggers""" diff --git a/cocotb/utils.py b/cocotb/utils.py index 6ff6aeb2..2f1cf863 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -491,6 +491,26 @@ def __call__(cls, *args, **kwargs): return self +# backport of Python 3.7's contextlib.nullcontext +class nullcontext(object): + """Context manager that does no additional processing. + Used as a stand-in for a normal context manager, when a particular + block of code is only sometimes used with a normal context manager: + cm = optional_cm if condition else nullcontext() + with cm: + # Perform operation, using optional_cm if condition is True + """ + + def __init__(self, enter_result=None): + self.enter_result = enter_result + + def __enter__(self): + return self.enter_result + + def __exit__(self, *excinfo): + pass + + if __name__ == "__main__": import random a = "" From d48ab99d7f69644010db1d52ae1bf60c4b692ff1 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 12 Jan 2019 18:17:59 -0800 Subject: [PATCH 1528/2656] Make `<=` raise an exception if confused with comparison --- cocotb/bus.py | 2 ++ cocotb/handle.py | 22 +++++++++++++++++++++ tests/test_cases/test_cocotb/test_cocotb.py | 19 ++++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/cocotb/bus.py b/cocotb/bus.py index e93e341c..744e9bb9 100755 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -30,6 +30,7 @@ """Common bus related functionality. A bus is simply defined as a collection of signals. """ +from cocotb.handle import AssignmentResult def _build_sig_attr_dict(signals): if isinstance(signals, dict): @@ -194,3 +195,4 @@ def sample(self, obj, strict=False): def __le__(self, value): """Overload the less than or equal to operator for value assignment""" self.drive(value) + return AssignmentResult(self, value) diff --git a/cocotb/handle.py b/cocotb/handle.py index 81add74b..d9df3293 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -360,6 +360,27 @@ def __setitem__(self, index, value): raise TypeError("Not permissible to set %s at index %d" % (self._name, index)) +class AssignmentResult(object): + """ + Object that exists solely to provide an error message if the caller + is not aware of cocotb's meaning of ``<=``. + """ + def __init__(self, signal, value): + self._signal = signal + self._value = value + + def __bool__(self): + raise TypeError( + "Attempted to use `{0._signal!r} <= {0._value!r}` (a cocotb " + "delayed write) as if it were a numeric comparison. To perform " + "comparison, use `{0._signal!r}.value <= {0._value!r}` instead." + .format(self) + ) + + # python 2 + __nonzero__ = __bool__ + + class NonHierarchyObject(SimHandleBase): """Common base class for all non-hierarchy objects.""" @@ -391,6 +412,7 @@ def __le__(self, value): module.signal <= 2 """ self.value = value + return AssignmentResult(self, value) def __eq__(self, other): if isinstance(other, SimHandleBase): diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 5341e4f5..adb59a37 100755 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -820,6 +820,25 @@ def test_singleton_isinstance(dut): yield Timer(1) +@cocotb.test() +def test_lessthan_raises_error(dut): + """ + Test that trying to use <= as if it were a comparison produces an error + """ + ret = dut.stream_in_data <= 0x12 + try: + bool(ret) + except TypeError: + pass + else: + raise TestFailure( + "No exception was raised when confusing comparison with assignment" + ) + + # to make this a generator + if False: yield + + if sys.version_info[:2] >= (3, 3): # this would be a syntax error in older python, so we do the whole # thing inside exec From 4b9e28feffa663c131fea83f14776d9ffcd82128 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sun, 20 Jan 2019 15:55:25 +0100 Subject: [PATCH 1529/2656] Typofix "varible" --- .../test_discovery/test_discovery.py | 88 +++++++++---------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 0485377d..72bb53a1 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -1,29 +1,29 @@ -''' Copyright (c) 2013 Potential Ventures Ltd -Copyright (c) 2013 SolarFlare Communications Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import cocotb import logging @@ -34,7 +34,7 @@ @cocotb.test() def recursive_discover(dut): - """Discover absolutely everything in the dut""" + """Discover absolutely everything in the DUT""" yield Timer(0) def _discover(obj): for thing in obj: @@ -44,7 +44,7 @@ def _discover(obj): @cocotb.test() def discover_module_values(dut): - """Discover everything in the dut""" + """Discover everything in the DUT""" yield Timer(0) count = 0 for thing in dut: @@ -88,7 +88,7 @@ def access_signal(dut): skip=cocotb.LANGUAGE in ["vhdl"]) def access_single_bit(dut): """ - Access a single bit in a vector of the dut + Access a single bit in a vector of the DUT Icarus v0.96 doesn't support single bit access to vectors """ @@ -108,7 +108,7 @@ def access_single_bit(dut): skip=cocotb.LANGUAGE in ["vhdl"]) def access_single_bit_assignment(dut): """ - Access a single bit in a vector of the dut using the assignment mechanism + Access a single bit in a vector of the DUT using the assignment mechanism Icarus v0.96 doesn't support single bit access to vectors """ @@ -199,27 +199,27 @@ def access_string(dut): test_string = "cocotb" dut.stream_in_string.setimmediatevalue(test_string) - varible_string = dut.stream_out_string - if varible_string != '': - raise TestFailure("%r not \'\'" % varible_string) + variable_string = dut.stream_out_string + if variable_string != '': + raise TestFailure("%r not \'\'" % variable_string) yield Timer(10) - if varible_string != test_string: - raise TestFailure("%r %s != '%s'" % (varible_string, str(varible_string), test_string)) + if variable_string != test_string: + raise TestFailure("%r %s != '%s'" % (variable_string, str(variable_string), test_string)) test_string = "longer_than_the_array" tlog.info("Test writing over size with '%s'" % test_string) dut.stream_in_string.setimmediatevalue(test_string) - varible_string = dut.stream_out_string + variable_string = dut.stream_out_string yield Timer(10) - test_string = test_string[:len(varible_string)] + test_string = test_string[:len(variable_string)] - if varible_string != test_string: - raise TestFailure("%r %s != '%s'" % (varible_string, str(varible_string), test_string)) + if variable_string != test_string: + raise TestFailure("%r %s != '%s'" % (variable_string, str(variable_string), test_string)) tlog.info("Test read access to a string character") @@ -227,7 +227,7 @@ def access_string(dut): idx = 3 - result_slice = varible_string[idx] + result_slice = variable_string[idx] # String is defined as string(1 to 8) so idx=3 will access the 3rd character if chr(result_slice) != test_string[idx-1]: @@ -238,7 +238,7 @@ def access_string(dut): yield Timer(10) - for i in varible_string: + for i in variable_string: lower = chr(i) upper = lower.upper() i.setimmediatevalue(ord(upper)) @@ -247,10 +247,10 @@ def access_string(dut): test_string = test_string.upper() - result = str(varible_string); + result = str(variable_string); tlog.info("After setting bytes of string value is %s" % result) - if varible_string != test_string: - raise TestFailure("%r %s != '%s'" % (varible_string, result, test_string)) + if variable_string != test_string: + raise TestFailure("%r %s != '%s'" % (variable_string, result, test_string)) @cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) def access_constant_boolean(dut): From 10b755b2d95822a654bfce418c2f8a59157b3dd7 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sun, 20 Jan 2019 14:57:42 +0100 Subject: [PATCH 1530/2656] Fix up links, making the https when possible. Further doc work. --- cocotb/decorators.py | 98 +++++++++++----------- cocotb/drivers/avalon.py | 6 +- cocotb/monitors/__init__.py | 16 ++++ cocotb/monitors/avalon.py | 33 ++++---- cocotb/regression.py | 28 +++---- documentation/source/building.rst | 2 +- documentation/source/endian_swapper.rst | 2 +- documentation/source/examples.rst | 12 +-- documentation/source/hal_cosimulation.rst | 55 ++++++------ documentation/source/index.rst | 1 + documentation/source/installation.rst | 24 ------ documentation/source/introduction.rst | 51 ++++++----- documentation/source/library_reference.rst | 32 +++++-- documentation/source/ping_tun_tap.rst | 27 +++--- documentation/source/quickstart.rst | 16 ++-- documentation/source/roadmap.rst | 6 +- documentation/source/simulator_support.rst | 14 ++-- documentation/source/tests.rst | 7 -- 18 files changed, 218 insertions(+), 212 deletions(-) delete mode 100644 documentation/source/installation.rst delete mode 100644 documentation/source/tests.rst diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 6b94be03..86e2c196 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -1,29 +1,30 @@ -''' Copyright (c) 2013 Potential Ventures Ltd -Copyright (c) 2013 SolarFlare Communications Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + from __future__ import print_function import sys import time @@ -61,11 +62,10 @@ def public(f): @public class CoroutineComplete(Exception): - """ - To ensure that a coroutine has completed before we fire any triggers - that are blocked waiting for the coroutine to end, we create a subclass - exception that the Scheduler catches and the callbacks are attached - here. + """To ensure that a coroutine has completed before we fire any triggers + that are blocked waiting for the coroutine to end, we create a subclass + exception that the Scheduler catches and the callbacks are attached + here. """ def __init__(self, text="", callback=None): Exception.__init__(self, text) @@ -73,16 +73,15 @@ def __init__(self, text="", callback=None): class RunningCoroutine(object): - """Per instance wrapper around an function to turn it into a coroutine - + """Per instance wrapper around an function to turn it into a coroutine. - Provides the following: + Provides the following: - coro.join() creates a Trigger that will fire when this coroutine - completes + coro.join() creates a Trigger that will fire when this coroutine + completes. - coro.kill() will destroy a coroutine instance (and cause any Join - triggers to fire + coro.kill() will destroy a coroutine instance (and cause any Join + triggers to fire. """ def __init__(self, inst, parent): if hasattr(inst, "__name__"): @@ -145,7 +144,7 @@ def close(self): return self._coro.close() def kill(self): - """Kill a coroutine""" + """Kill a coroutine.""" self.log.debug("kill() called on coroutine") cocotb.scheduler.unschedule(self) @@ -157,7 +156,7 @@ def _finished_cb(self): self._finished = True def join(self): - """Return a trigger that will fire when the wrapped coroutine exits""" + """Return a trigger that will fire when the wrapped coroutine exits.""" return Join(self) def has_started(self): @@ -178,7 +177,7 @@ def sort_name(self): return "%s.%d.%s" % (self.module, self.stage, self.funcname) class RunningTest(RunningCoroutine): - """Add some useful Test functionality to a RunningCoroutine""" + """Add some useful Test functionality to a RunningCoroutine.""" class ErrorLogHandler(logging.Handler): def __init__(self, fn): @@ -239,15 +238,16 @@ def _handle_error_message(self, msg): class coroutine(object): """Decorator class that allows us to provide common coroutine mechanisms: - ``log`` methods will will log to ``cocotb.coroutines.name`` + ``log`` methods will will log to ``cocotb.coroutines.name``. - ``join()`` method returns an event which will fire when the coroutine exits + ``join()`` method returns an event which will fire when the coroutine exits. """ def __init__(self, func): self._func = func self.log = SimLog("cocotb.function.%s" % self._func.__name__, id(self)) - functools.update_wrapper(self, self._func) + self.__name__ = self._func.__name__ + functools.update_wrapper(self, func) def __call__(self, *args, **kwargs): try: @@ -279,7 +279,7 @@ def __str__(self): @public class function(object): - """Decorator class that allows a function to block + """Decorator class that allows a function to block. This allows a function to internally block while externally appear to yield. @@ -287,7 +287,6 @@ class function(object): def __init__(self, func): self._func = func self.log = SimLog("cocotb.function.%s" % self._func.__name__, id(self)) - functools.update_wrapper(self, self._func) def __call__(self, *args, **kwargs): @@ -318,7 +317,6 @@ class external(object): def __init__(self, func): self._func = func self._log = SimLog("cocotb.external.%s" % self._func.__name__, id(self)) - functools.update_wrapper(self, self._func) def __call__(self, *args, **kwargs): @@ -343,7 +341,7 @@ def __get__(self, obj, type=None): @public class hook(coroutine): - """Decorator to mark a function as a hook for cocotb + """Decorator to mark a function as a hook for cocotb. All hooks are run at the beginning of a cocotb test suite, prior to any test code being run.""" @@ -366,10 +364,10 @@ def _wrapped_hook(*args, **kwargs): @public class test(coroutine): - """Decorator to mark a function as a test + """Decorator to mark a function as a test. All tests are coroutines. The test decorator provides - some common reporting etc, a test timeout and allows + some common reporting etc., a test timeout and allows us to mark tests as expected failures. Args: diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index b8ff4d94..9e1104d3 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -25,9 +25,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -"""Drivers for Altera Avalon interfaces. +"""Drivers for Intel Avalon interfaces. -See http://www.altera.co.uk/literature/manual/mnl_avalon_spec.pdf +See https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/manual/mnl_avalon_spec_1_3.pdf NB Currently we only support a very small subset of functionality """ @@ -120,7 +120,6 @@ def read(self, address, sync=True): """Issue a request to the bus and block until this comes back. Simulation time still progresses but syntactically it blocks. - See http://www.altera.com/literature/manual/mnl_avalon_spec_1_3.pdf Args: address (int): The address to read from. @@ -186,7 +185,6 @@ def read(self, address, sync=True): def write(self, address, value): """Issue a write to the given address with the specified value. - See http://www.altera.com/literature/manual/mnl_avalon_spec_1_3.pdf Args: address (int): The address to write to. diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index 7f390a9f..9f923960 100755 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -93,6 +93,7 @@ def __init__(self, callback=None, event=None): self._thread = cocotb.scheduler.add(self._monitor_recv()) def kill(self): + """Kill the monitor coroutine.""" if self._thread: self._thread.kill() self._thread = None @@ -104,12 +105,26 @@ def __getitem__(self, idx): return self._recvQ[idx] def add_callback(self, callback): + """Add function as a callback. + + Args: + callback (callable): The function to call back. + """ self.log.debug("Adding callback of function %s to monitor" % (callback.__name__)) self._callbacks.append(callback) @coroutine def wait_for_recv(self, timeout=None): + """With *timeout*, :meth:`.wait` for transaction to arrive on monitor + and return its data. + + Args: + timeout (optional): The timeout value for :class:`~.triggers.Timer`. + Defaults to ``None``. + + Returns: Data of received transaction. + """ if timeout: t = Timer(timeout) fired = yield [self._wait_event.wait(), t] @@ -173,6 +188,7 @@ def __init__(self, entity, name, clock, reset=None, reset_n=None, @property def in_reset(self): + """Boolean flag showing whether the bus is in reset state or not.""" if self._reset_n is not None: return not bool(self._reset_n.value.integer) if self._reset is not None: diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index 0a110c2c..4dde8d4e 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -25,12 +25,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -""" -Monitors for Altera Avalon interfaces. +"""Monitors for Intel Avalon interfaces. -See http://www.altera.co.uk/literature/manual/mnl_avalon_spec.pdf +See https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/manual/mnl_avalon_spec_1_3.pdf -NB Currently we only support a very small subset of functionality +NB Currently we only support a very small subset of functionality. """ from cocotb.utils import hexdump @@ -44,11 +43,11 @@ class AvalonProtocolError(Exception): class AvalonST(BusMonitor): - """ - Avalon-ST bus. + """Avalon-ST bus. Non-packetised so each valid word is a separate transaction. """ + _signals = ["valid", "data"] _optional_signals = ["ready"] @@ -68,7 +67,7 @@ def __init__(self, *args, **kwargs): @coroutine def _monitor_recv(self): - """Watch the pins and reconstruct transactions""" + """Watch the pins and reconstruct transactions.""" # Avoid spurious object creation by recycling clkedge = RisingEdge(self.clock) @@ -90,9 +89,8 @@ def valid(): class AvalonSTPkts(BusMonitor): - """ - Packetised Avalon-ST bus. - """ + """Packetised Avalon-ST bus.""" + _signals = ["valid", "data", "startofpacket", "endofpacket"] _optional_signals = ["error", "channel", "ready", "empty"] @@ -145,7 +143,7 @@ def __init__(self, *args, **kwargs): @coroutine def _monitor_recv(self): - """Watch the pins and reconstruct transactions""" + """Watch the pins and reconstruct transactions.""" # Avoid spurious object creation by recycling clkedge = RisingEdge(self.clock) @@ -182,7 +180,7 @@ def valid(): raise AvalonProtocolError("Data transfer outside of " "packet") - #Handle empty and X's in empty / data + # Handle empty and X's in empty / data vec = BinaryValue() if not self.bus.endofpacket.value: vec = self.bus.data.value @@ -227,9 +225,8 @@ def valid(): invalid_cyclecount) class AvalonSTPktsWithChannel(AvalonSTPkts): - """ - Packetised AvalonST bus using channel - """ + """Packetised AvalonST bus using channel.""" + _signals = ["valid", "data", "startofpacket", "endofpacket", "channel"] _optional_signals = ["error", "ready", "empty"] @@ -237,9 +234,9 @@ def __init__(self, *args, **kwargs): AvalonSTPkts.__init__(self, *args, **kwargs) def _recv(self,pkt): - """Force use of channel in recv function + """Force use of channel in recv function. - args: - pkt: (string) Monitored data + Args: + pkt: (string) Monitored data. """ AvalonSTPkts._recv(self,{"data":pkt,"channel":self.channel}) diff --git a/cocotb/regression.py b/cocotb/regression.py index 2ad17f34..bdcd1f28 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -232,13 +232,12 @@ def _add_failure(self, result): self.failures += 1 def handle_result(self, result): - """ - Handle a test result. + """Handle a test result. Dump result to XML and schedule the next test (if any). Args: - result: The sub-exception of TestComplete to raise + result: The sub-exception of TestComplete to raise. """ real_time = time.time() - self._running_test.start_time sim_time_ns = get_sim_time('ns') - self._running_test.start_sim_time @@ -401,19 +400,18 @@ def _store_test_result(self, module_name, test_name, result_pass, sim_time, real def _create_test(function, name, documentation, mod, *args, **kwargs): - """ - Factory function to create tests, avoids late binding. + """Factory function to create tests, avoids late binding. Creates a test dynamically. The test will call the supplied function with the supplied arguments. Args: - function (function): The test function to run - name (str): The name of the test - documentation (str): The docstring for the test - mod (module): The module this function belongs to - *args: Remaining args to pass to test function - **kwargs: Passed to the test function + function (function): The test function to run. + name (str): The name of the test. + documentation (str): The docstring for the test. + mod (module): The module this function belongs to. + *args: Remaining args to pass to test function. + **kwargs: Passed to the test function. Returns: Decorated test function @@ -428,8 +426,7 @@ def _my_test(dut): class TestFactory(object): - """ - Used to automatically generate tests. + """Used to automatically generate tests. Assuming we have a common test function that will run a test. This test function will take keyword arguments (for example generators for each of @@ -486,6 +483,7 @@ def __init__(self, test_function, *args, **kwargs): self.args = args self.kwargs_constant = kwargs self.kwargs = {} + self.log = SimLog("cocotb.regression") def add_option(self, name, optionlist): """Add a named option to the test. @@ -541,13 +539,13 @@ def generate_tests(self, prefix="", postfix=""): else: doc += "\t%s: %s\n" % (optname, repr(optvalue)) - cocotb.log.debug("Adding generated test \"%s\" to module \"%s\"" % + self.log.debug("Adding generated test \"%s\" to module \"%s\"" % (name, mod.__name__)) kwargs = {} kwargs.update(self.kwargs_constant) kwargs.update(testoptions) if hasattr(mod, name): - cocotb.log.error("Overwriting %s in module %s. " + self.log.error("Overwriting %s in module %s. " "This causes a previously defined testcase " "not to be run. Consider setting/changing " "name_postfix" % (name, mod)) diff --git a/documentation/source/building.rst b/documentation/source/building.rst index 32a9ae2b..32c035c6 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -12,7 +12,7 @@ Make Targets Makefiles define two targets, ``regression`` and ``sim``, the default target is ``sim``. -Both rules create a results file in the calling directory called :file:`results.xml`. This file is a JUnit-compatible output file suitable for use with `Jenkins `_. The ``sim`` targets unconditionally re-runs the simulator whereas the ``regression`` target only re-builds if any dependencies have changed. +Both rules create a results file in the calling directory called :file:`results.xml`. This file is a JUnit-compatible output file suitable for use with `Jenkins `_. The ``sim`` targets unconditionally re-runs the simulator whereas the ``regression`` target only re-builds if any dependencies have changed. Make Phases diff --git a/documentation/source/endian_swapper.rst b/documentation/source/endian_swapper.rst index 12aa369a..fbe1928d 100644 --- a/documentation/source/endian_swapper.rst +++ b/documentation/source/endian_swapper.rst @@ -3,7 +3,7 @@ Tutorial: Endian Swapper In this tutorial we'll use some of the built-in features of cocotb to quickly create a complex testbench. -.. note:: All the code and sample output from this example are available on `EDA Playground `_ +.. note:: All the code and sample output from this example are available on `EDA Playground `_ For the impatient this tutorial is provided as an example with cocotb. You can run this example from a fresh checkout:: diff --git a/documentation/source/examples.rst b/documentation/source/examples.rst index 5b958f5d..60220311 100644 --- a/documentation/source/examples.rst +++ b/documentation/source/examples.rst @@ -2,7 +2,7 @@ Examples ######## -These code samples show some typical usage of Cocotb based on real-world problems. +These code samples show some typical usage of cocotb based on real-world problems. Example testbench for snipped of code from `comp.lang.verilog `_: @@ -11,9 +11,7 @@ Example testbench for snipped of code from `comp.lang.verilog ` +interface and reset the DUT. +Then we create two functions that are wrapped with the :class:`cocotb.function` decorator +to be called when the HAL attempts to perform a read or write. +These are then passed to the `IO Module`_: .. code-block:: python @@ -143,11 +143,10 @@ a read or write. These are then passed to the `IO Module`_: io_module.set_read_function(read) -We can then intialise the HAL and call functions, using the ``cocotb.external`` -decorator to turn the normal function into a blocking `coroutine` that we can +We can then initialise the HAL and call functions, using the :class:`cocotb.external` +decorator to turn the normal function into a blocking coroutine that we can ``yield``: - .. code-block:: python state = hal.endian_swapper_init(0) @@ -155,10 +154,10 @@ decorator to turn the normal function into a blocking `coroutine` that we can The HAL will perform whatever calls it needs, accessing the DUT through the -Avalon-MM driver, and control will return to the testbench when the function -returns. +:class:`Avalon-MM driver `, +and control will return to the testbench when the function returns. -.. note:: The decorator is applied to the function before it is called +.. note:: The decorator is applied to the function before it is called. @@ -171,10 +170,8 @@ interfacing with emulators like `QEMU`_ to allow us to co-simulate when the software needs to execute on a different processor architecture. -.. _SWIG: http://www.swig.org/ +.. _SWIG: https://www.swig.org/ .. _UIO framework: https://www.kernel.org/doc/html/latest/driver-api/uio-howto.html -.. _QEMU: http://wiki.qemu.org/Main_Page - - +.. _QEMU: https://wiki.qemu.org/Main_Page diff --git a/documentation/source/index.rst b/documentation/source/index.rst index f18281c5..e2305979 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -16,6 +16,7 @@ Contents: endian_swapper ping_tun_tap hal_cosimulation + examples troubleshooting roadmap simulator_support diff --git a/documentation/source/installation.rst b/documentation/source/installation.rst deleted file mode 100644 index 692fe9e8..00000000 --- a/documentation/source/installation.rst +++ /dev/null @@ -1,24 +0,0 @@ -############ -Installation -############ - -Get the Source -============== - -Source can be obtained as a tar ball for the current `release `_. - -Or by cloning the repository `git@github.com:potentialventures/cocotb.git` - -There are two supported installation options for Cocotb, standalone or centralised. - -Standalone Usage -================ - -Simply check out the code and hit make at the root of the tree. This will run the test cases and exampkles against `Icarus `_. - -The list of supported simulators for the version you have can be found by *make help*. - -Centralised Usage -================= - -A build can be installed in a centralised location with *make install FULL_INSTALL_DIR=*. This will also generate an uninstall script. diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index ebea7a26..80cb8da6 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -5,70 +5,81 @@ Introduction What is cocotb? =============== -**Cocotb** is a *COroutine* based *COsimulation* *TestBench* environment for verifying VHDL/Verilog RTL using `Python `_. +**cocotb** is a *COroutine* based *COsimulation* *TestBench* environment for verifying VHDL/Verilog RTL using `Python `_. -**Cocotb** is completely free, open source (under the `BSD License `_) and hosted on `GitHub `_. +cocotb is completely free, open source (under the `BSD License `_) and hosted on `GitHub `_. -**Cocotb** requires a simulator to simulate the RTL. Simulators that have been tested and known to work with Cocotb: +cocotb requires a simulator to simulate the RTL. Simulators that have been tested and known to work with cocotb: Linux Platforms * `Icarus Verilog `_ * `GHDL `_ * `Aldec `_ Riviera-PRO -* `Synopsys `_ VCS -* `Cadence `_ Incisive -* `Mentor `_ Modelsim (DE and SE) +* `Synopsys `_ VCS +* `Cadence `_ Incisive +* `Mentor `_ Modelsim (DE and SE) Windows Platform * `Icarus Verilog `_ * `Aldec `_ Riviera-PRO -* `Mentor `_ Modelsim (DE and SE) +* `Mentor `_ Modelsim (DE and SE) -An older version of **Cocotb** can be used live in a web-browser using `EDA Playground `_. +A (possibly older) version of cocotb can be used live in a web-browser using `EDA Playground `_. -How is Cocotb different? +How is cocotb different? ======================== -**Cocotb** encourages the same philosophy of design re-use and randomised testing as UVM, however is implemented in Python rather than SystemVerilog. +cocotb encourages the same philosophy of design re-use and randomised testing as UVM, however is implemented in Python rather than SystemVerilog. -In Cocotb VHDL/Verilog/SystemVerilog are only used for the synthesisable design. +In cocotb, VHDL/Verilog/SystemVerilog are only used for the synthesisable design. -**Cocotb** has built-in support for integrating with the `Jenkins `_ continuous integration system. +cocotb has built-in support for integrating with the `Jenkins `_ continuous integration system. -Cocotb was specifically designed to lower the overhead of creating a test. +cocotb was specifically designed to lower the overhead of creating a test. -**Cocotb** automatically discovers tests so that no additional step is required to add a test to a regression. +cocotb automatically discovers tests so that no additional step is required to add a test to a regression. All verification is done using Python which has various advantages over using SystemVerilog or VHDL for verification: * Writing Python is **fast** - it's a very productive language * It's **easy** to interface to other languages from Python -* Python has a huge library of existing code to **re-use** like `packet generation `_ libraries. +* Python has a huge library of existing code to **re-use** like `packet generation `_ libraries. * Python is **interpreted**. Tests can be edited and re-run them without having to recompile the design or exit the simulator GUI. * Python is **popular** - far more engineers know Python than SystemVerilog or VHDL - -How does Cocotb work? +How does cocotb work? ===================== Overview -------- -A typical cocotb testbench requires no additional RTL code. The Design Under Test (DUT) is instantiated as the toplevel in the simulator without any wrapper code. Cocotb drives stimulus onto the inputs to the DUT (or further down the hierarchy) and monitors the outputs directly from Python. +A typical cocotb testbench requires no additional RTL code. +The Design Under Test (DUT) is instantiated as the toplevel in the simulator without any wrapper code. +cocotb drives stimulus onto the inputs to the DUT (or further down the hierarchy) and monitors the outputs directly from Python. .. image:: diagrams/svg/cocotb_overview.svg -A test is simply a Python function. At any given time either the simulator is advancing time or the Python code is executing. The **yield** keyword is used to indicate when to pass control of execution back to the simulator. A test can spawn multiple coroutines, allowing for independent flows of execution. +A test is simply a Python function. +At any given time either the simulator is advancing time or the Python code is executing. +The ``yield`` keyword is used to indicate when to pass control of execution back to the simulator. +A test can spawn multiple coroutines, allowing for independent flows of execution. Contributors ============ -**Cocotb** was developed by `Potential Ventures `_ with the support of `Solarflare Communications Ltd `_ and contributions from Gordon McGregor and Finn Grimwood (see `contributers `_ for full list of contributions). +cocotb was developed by `Potential Ventures `_ with the support of +`Solarflare Communications Ltd `_ +and contributions from Gordon McGregor and Finn Grimwood +(see `contributers `_ for the full list of contributions). + +We also have a list of talks and papers, libraries and examples at our wiki page +`Further Resources https://github.com/potentialventures/cocotb/wiki/Further-Resources>`_. +Feel free to add links to cocotb-related content that we are still missing! diff --git a/documentation/source/library_reference.rst b/documentation/source/library_reference.rst index e82225bd..ad41d374 100644 --- a/documentation/source/library_reference.rst +++ b/documentation/source/library_reference.rst @@ -20,12 +20,16 @@ Writing and Generating tests .. autoclass:: cocotb.coroutine +.. autoclass:: cocotb.external + +.. autoclass:: cocotb.function + +.. autoclass:: cocotb.hook + .. autoclass:: cocotb.regression.TestFactory :members: :member-order: bysource -.. autoclass:: cocotb.hook - Interacting with the Simulator ============================== @@ -53,6 +57,8 @@ Triggers Triggers are used to indicate when the scheduler should resume coroutine execution. Typically a coroutine will :keyword:`yield` a trigger or a list of triggers. +.. autoclass:: cocotb.triggers.Trigger + Simulation Timing ~~~~~~~~~~~~~~~~~ @@ -116,12 +122,16 @@ Driver Monitor ------- -.. autoclass:: cocotb.monitors.Monitor - :members: +.. currentmodule:: cocotb.monitors + +.. autoclass:: Monitor + :members: _monitor_recv, _recv :member-order: bysource :private-members: -.. autoclass:: cocotb.monitors.BusMonitor + .. automethod:: wait_for_recv(timeout=None) + +.. autoclass:: BusMonitor :members: :member-order: bysource :show-inheritance: @@ -199,10 +209,12 @@ Advanced Microcontroller Bus Architecture. :members: :member-order: bysource + :show-inheritance: .. autoclass:: AXI4Slave :members: :member-order: bysource + :show-inheritance: Avalon @@ -213,6 +225,7 @@ Avalon .. autoclass:: AvalonMM :members: :member-order: bysource + :show-inheritance: .. autoclass:: AvalonMaster @@ -221,18 +234,22 @@ Avalon :members: :member-order: bysource + :show-inheritance: .. autoclass:: AvalonMemory :members: :member-order: bysource + :show-inheritance: .. autoclass:: AvalonST :members: :member-order: bysource + :show-inheritance: .. autoclass:: AvalonSTPkts :members: :member-order: bysource + :show-inheritance: OPB @@ -247,6 +264,7 @@ OPB :members: :member-order: bysource + :show-inheritance: XGMII ~~~~~ @@ -256,6 +274,7 @@ XGMII .. autoclass:: XGMII :members: :member-order: bysource + :show-inheritance: Monitors -------- @@ -268,10 +287,12 @@ Avalon .. autoclass:: AvalonST :members: :member-order: bysource + :show-inheritance: .. autoclass:: AvalonSTPkts :members: :member-order: bysource + :show-inheritance: XGMII ~~~~~ @@ -279,3 +300,4 @@ XGMII .. autoclass:: cocotb.monitors.xgmii.XGMII :members: :member-order: bysource + :show-inheritance: diff --git a/documentation/source/ping_tun_tap.rst b/documentation/source/ping_tun_tap.rst index 82c4400c..033f77fb 100644 --- a/documentation/source/ping_tun_tap.rst +++ b/documentation/source/ping_tun_tap.rst @@ -6,14 +6,16 @@ In this tutorial we'll look at interfacing the standard GNU `ping`_ command to the simulator. Using Python we can ping our DUT with fewer than 50 lines of code. -For the impatient this tutorial is provided as an example with Cocotb. You can -run this example from a fresh checkout:: +For the impatient this tutorial is provided as an example with cocotb. You can +run this example from a fresh checkout: + +.. code-block:: bash cd examples/ping_tun_tap/tests sudo make - -.. note:: To create a virtual interface the test either needs root permissions or have CAP_NET_ADMIN capability. +.. note:: To create a virtual interface the test either needs root permissions or + have ``CAP_NET_ADMIN`` capability. Architecture @@ -59,9 +61,10 @@ we write a function that will create our virtual interface: return tun Now we can get started on the actual test. First of all we'll create a clock -signal and connect up the Avalon driver and monitor to the DUT. To help debug +signal and connect up the :class:`Avalon driver ` and +:class:`monitor ` to the DUT. To help debug the testbench we'll enable verbose debug on the drivers and monitors by setting -the log level to **logging.DEBUG**. +the log level to ``logging.DEBUG``. .. code-block:: python @@ -83,8 +86,8 @@ the log level to **logging.DEBUG**. We also need to reset the DUT and drive some default values onto some of the -bus signals. Note that we'll need to import the **Timer** and **RisingEdge** -triggers. +bus signals. Note that we'll need to import the :class:`~.triggers.Timer` +and :class:`~.triggers.RisingEdge` triggers. .. code-block:: python @@ -100,11 +103,11 @@ triggers. The rest of the test becomes fairly straightforward. We create our TUN interface using our function defined previously. We'll also use the -**subprocess** module to actually start the ping command. +:mod:`subprocess` module to actually start the ping command. We then wait for a packet by calling a blocking read call on the TUN file descriptor and simply append that to the queue on the driver. We wait for -a packet to arrive on the monitor by yielding on wait_for_recv() and then +a packet to arrive on the monitor by yielding on :meth:`.wait_for_recv()` and then write the received packet back to the TUN file descriptor. @@ -145,8 +148,8 @@ and receiving of packets. .. _TUN example: https://gist.github.com/glacjay/585369 -.. _Ping: http://www.gnu.org/software/inetutils/manual/html_node/ping-invocation.html +.. _Ping: https://www.gnu.org/software/inetutils/manual/html_node/ping-invocation.html -.. _TUN/TAP: http://en.wikipedia.org/wiki/TUN/TAP +.. _TUN/TAP: https://en.wikipedia.org/wiki/TUN/TAP diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index cc78fd7c..a84b434d 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -98,7 +98,7 @@ Work has been done with the support of the cocotb community to enable Windows support using the MinGW/Msys environment. Download the MinGQ installer from. -http://sourceforge.net/projects/mingw/files/latest/download?source=files . +https://sourceforge.net/projects/mingw/files/latest/download?source=files . Run the GUI installer and specify a directory you would like the environment installed in. The installer will retrieve a list of possible packages, when this @@ -130,10 +130,10 @@ so it can be used easily from inside Msys. Once inside the Msys shell commands as given here will work as expected. -Mac Packages ------------- +macOS Packages +-------------- -You need a few packages installed to get cocotb running on Mac. +You need a few packages installed to get cocotb running on macOS. Installing a package manager really helps things out here. `Brew `_ seems to be the most popular, so we'll assume you have that installed. @@ -172,8 +172,7 @@ be used): $> make SIM=ghdl TOPLEVEL_LANG=vhdl - -Using Cocotb +Using cocotb ============ A typical cocotb testbench requires no additional RTL code. @@ -219,9 +218,8 @@ following: @cocotb.test() def my_first_test(dut): - """ - Try accessing the design - """ + """Try accessing the design.""" + dut._log.info("Running test!") for cycle in range(10): dut.clk = 0 diff --git a/documentation/source/roadmap.rst b/documentation/source/roadmap.rst index 807ebcaf..fb99a750 100644 --- a/documentation/source/roadmap.rst +++ b/documentation/source/roadmap.rst @@ -2,9 +2,9 @@ Roadmap ####### -Cocotb is in active development. +cocotb is in active development. -We use GitHub issues to track our pending tasks. Take a look at the `open Enhancements `_ to see the work that's lined up. +We use GitHub issues to track our pending tasks. +Take a look at the `open Feature List `_ to see the work that's lined up. If you have a GitHub account you can also `raise an enhancement request `_ to suggest new features. - diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 34195c17..55b92e02 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -13,7 +13,7 @@ Accessing bits of a vector doesn't work: dut.stream_in_data[2] <= 1 -See "access_single_bit" test in examples/functionality/tests/test_discovery.py. +See ``access_single_bit`` test in :file:`examples/functionality/tests/test_discovery.py`. Synopsys VCS @@ -21,15 +21,17 @@ Synopsys VCS Aldec Riviera-PRO ----------------- -The `$LICENSE_QUEUE` environmental variable can be used for this simulator - this setting will be mirrored in the TCL `license_queue` variable to control runtime license checkouts. +The ``$LICENSE_QUEUE`` environment variable can be used for this simulator – +this setting will be mirrored in the TCL ``license_queue`` variable to control runtime license checkouts. Mentor Questa ------------- Mentor Modelsim --------------- -Any ModelSim-PE or ModelSim-PE derivative (like ModelSim Microsemi, Altera, Lattice Edition) does not support the VHDL FLI feature. -If you try to run with FLI enabled, you will see a vsim-FLI-3155 error: + +Any ModelSim PE or ModelSim PE derivative (like ModelSim Microsemi, Intel, Lattice Edition) does not support the VHDL FLI feature. +If you try to run with FLI enabled, you will see a ``vsim-FLI-3155`` error: .. code-block:: bash @@ -37,8 +39,8 @@ If you try to run with FLI enabled, you will see a vsim-FLI-3155 error: ModelSim DE and SE (and Questa, of course) supports the FLI. -Cadence Incisive ----------------- +Cadence Incisive, Cadence Xcelium +--------------------------------- GHDL ---- diff --git a/documentation/source/tests.rst b/documentation/source/tests.rst deleted file mode 100644 index e938e399..00000000 --- a/documentation/source/tests.rst +++ /dev/null @@ -1,7 +0,0 @@ -Tests -===== - -Tests are defined using the @test decorator - -.. autoclass:: cocotb.decorators.test - From cd1463d87712acdc1b3f05d9e863c1225b59b518 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 24 Jan 2019 21:52:41 -0800 Subject: [PATCH 1531/2656] Ignore files created by mergetools --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index bcafcae0..5d3b157c 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,10 @@ __pycache__ \#*\# \.\#* +# Mergetool tmp files +*.orig +*.bak + # Waveforms *.vcd From 2c563ca47af5a4cafe392bad0a2d38f3c06ef72d Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 27 Jan 2019 20:59:00 -0800 Subject: [PATCH 1532/2656] Fix gh-768 There are three `set_signal_value` overload - two of them (reals, int32s) use `vpiInertialDelay`, but the third uses vpiNoDelay. This is bad, because it means the behavior of `.setimmediatevalue` depends on whether the value fits in a string. The assumption here is that the integer case is the correct one. The string and integer overload were introduced simultaneously in e9104a8a1cb58774acfbd5c185f46a7ea969350e - neither was changed in response to a bug fix. The best way to ensure three pieces of code do the same thing is put that thing in one piece of code - so this commit introduces a `set_signal_value(s_vpi_value)` function. Unfortunately, this bug only presented itself if the test was the first (or in practice, only) test in the file. As such, we need two standalone testsuites to regression-test this one issue. --- lib/vpi/VpiCbHdl.cpp | 44 +++++++++-------------- lib/vpi/VpiImpl.h | 2 ++ tests/test_cases/issue_768_a/Makefile | 3 ++ tests/test_cases/issue_768_a/issue_768.py | 21 +++++++++++ tests/test_cases/issue_768_b/Makefile | 3 ++ tests/test_cases/issue_768_b/issue_768.py | 20 +++++++++++ 6 files changed, 66 insertions(+), 27 deletions(-) create mode 100644 tests/test_cases/issue_768_a/Makefile create mode 100644 tests/test_cases/issue_768_a/issue_768.py create mode 100644 tests/test_cases/issue_768_b/Makefile create mode 100644 tests/test_cases/issue_768_b/issue_768.py diff --git a/lib/vpi/VpiCbHdl.cpp b/lib/vpi/VpiCbHdl.cpp index c1488fe8..8ed4518d 100644 --- a/lib/vpi/VpiCbHdl.cpp +++ b/lib/vpi/VpiCbHdl.cpp @@ -325,50 +325,26 @@ long VpiSignalObjHdl::get_signal_value_long(void) // Value related functions int VpiSignalObjHdl::set_signal_value(long value) { - FENTER s_vpi_value value_s; value_s.value.integer = value; value_s.format = vpiIntVal; - s_vpi_time vpi_time_s; - - vpi_time_s.type = vpiSimTime; - vpi_time_s.high = 0; - vpi_time_s.low = 0; - - // Use Inertial delay to schedule an event, thus behaving like a verilog testbench - vpi_put_value(GpiObjHdl::get_handle(), &value_s, &vpi_time_s, vpiInertialDelay); - check_vpi_error(); - - FEXIT - return 0; + return set_signal_value(value_s); } int VpiSignalObjHdl::set_signal_value(double value) { - FENTER s_vpi_value value_s; value_s.value.real = value; value_s.format = vpiRealVal; - s_vpi_time vpi_time_s; - - vpi_time_s.type = vpiSimTime; - vpi_time_s.high = 0; - vpi_time_s.low = 0; - - vpi_put_value(GpiObjHdl::get_handle(), &value_s, &vpi_time_s, vpiInertialDelay); - check_vpi_error(); - - FEXIT - return 0; + return set_signal_value(value_s); } int VpiSignalObjHdl::set_signal_value(std::string &value) { - FENTER s_vpi_value value_s; std::vector writable(value.begin(), value.end()); @@ -377,7 +353,21 @@ int VpiSignalObjHdl::set_signal_value(std::string &value) value_s.value.str = &writable[0]; value_s.format = vpiBinStrVal; - vpi_put_value(GpiObjHdl::get_handle(), &value_s, NULL, vpiNoDelay); + return set_signal_value(value_s); +} + +int VpiSignalObjHdl::set_signal_value(s_vpi_value value_s) +{ + FENTER + + s_vpi_time vpi_time_s; + + vpi_time_s.type = vpiSimTime; + vpi_time_s.high = 0; + vpi_time_s.low = 0; + + // Use Inertial delay to schedule an event, thus behaving like a verilog testbench + vpi_put_value(GpiObjHdl::get_handle(), &value_s, &vpi_time_s, vpiInertialDelay); check_vpi_error(); FEXIT diff --git a/lib/vpi/VpiImpl.h b/lib/vpi/VpiImpl.h index 5ccc4088..5ac57fc1 100644 --- a/lib/vpi/VpiImpl.h +++ b/lib/vpi/VpiImpl.h @@ -214,6 +214,8 @@ class VpiSignalObjHdl : public GpiSignalObjHdl { int initialise(std::string &name, std::string &fq_name); private: + int set_signal_value(s_vpi_value value); + VpiValueCbHdl m_rising_cb; VpiValueCbHdl m_falling_cb; VpiValueCbHdl m_either_cb; diff --git a/tests/test_cases/issue_768_a/Makefile b/tests/test_cases/issue_768_a/Makefile new file mode 100644 index 00000000..09c51217 --- /dev/null +++ b/tests/test_cases/issue_768_a/Makefile @@ -0,0 +1,3 @@ +include ../../designs/sample_module/Makefile + +MODULE = issue_768 diff --git a/tests/test_cases/issue_768_a/issue_768.py b/tests/test_cases/issue_768_a/issue_768.py new file mode 100644 index 00000000..4c601cba --- /dev/null +++ b/tests/test_cases/issue_768_a/issue_768.py @@ -0,0 +1,21 @@ +""" +Control case for https://github.com/potentialventures/cocotb/issues/768. + +This passed before the bug fix, and should continue to pass + +Note that the bug only occurred if the test in question runs first - so +no more tests can be added to this file. +""" +import cocotb +from cocotb.triggers import Timer, ReadOnly +from cocotb.binary import BinaryValue + +# this line is different between the two files +value = 0 + +@cocotb.test() +def test(dut): + dut.stream_in_data.setimmediatevalue(value) + yield Timer(1) + assert dut.stream_in_data.value == 0 + yield ReadOnly() \ No newline at end of file diff --git a/tests/test_cases/issue_768_b/Makefile b/tests/test_cases/issue_768_b/Makefile new file mode 100644 index 00000000..09c51217 --- /dev/null +++ b/tests/test_cases/issue_768_b/Makefile @@ -0,0 +1,3 @@ +include ../../designs/sample_module/Makefile + +MODULE = issue_768 diff --git a/tests/test_cases/issue_768_b/issue_768.py b/tests/test_cases/issue_768_b/issue_768.py new file mode 100644 index 00000000..4ba8eb6a --- /dev/null +++ b/tests/test_cases/issue_768_b/issue_768.py @@ -0,0 +1,20 @@ +""" +Failing case for https://github.com/potentialventures/cocotb/issues/768. + +Note that the bug only occurred if the test in question runs first - so +no more tests can be added to this file. +""" + +import cocotb +from cocotb.triggers import Timer, ReadOnly +from cocotb.binary import BinaryValue + +# this line is different between the two files +value = BinaryValue(0) + +@cocotb.test() +def do_test(dut): + dut.stream_in_data.setimmediatevalue(value) + yield Timer(1) + assert dut.stream_in_data.value == 0 + yield ReadOnly() \ No newline at end of file From b2b1ddb0ecdc3a89b2be4718e779bf4ad2e2cac7 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Tue, 5 Feb 2019 13:15:46 +0000 Subject: [PATCH 1533/2656] Make USER_DIR independent of SIM_ROOT USER_DIR is the path containing the user-called Makefile, i.e. where the user code lives. Extract this path from MAKEFILE_LIST intead of relying on SIM_ROOT. This is preparing for a world where the cocotb directory structure and with it SIM_ROOT will change. No user-visible changes are expected after applying this commit. --- makefiles/Makefile.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index c382de9c..51bac0cb 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -34,7 +34,7 @@ export SIM_ROOT:=$(abspath $(dir $(lastword $(MAKEFILE_LIST)))/..) endif ifeq ($(USER_DIR),) -export USER_DIR:=$(SIM_ROOT) +export USER_DIR:=$(abspath $(dir $(firstword $(MAKEFILE_LIST)))) endif BUILD_DIR=$(USER_DIR)/build From 1444543c47273331f43abd3bbdca6e5621cdd1c6 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 5 Feb 2019 22:41:47 +0100 Subject: [PATCH 1534/2656] Comment out unused html_static_path definition. Prevents "WARNING: html_static_path entry '/home/colin/cocotb/documentation/source/_static' does not exist". --- documentation/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/conf.py b/documentation/source/conf.py index cf81a560..191965c2 100644 --- a/documentation/source/conf.py +++ b/documentation/source/conf.py @@ -141,7 +141,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +#html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. From e7aa25fd5973de79b47e674c34afd1bd30a6f2e8 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 5 Feb 2019 00:50:22 +0100 Subject: [PATCH 1535/2656] Add sphinxcontrib-trio to required packages. --- documentation/requirements.txt | 1 + documentation/source/conf.py | 1 + 2 files changed, 2 insertions(+) diff --git a/documentation/requirements.txt b/documentation/requirements.txt index 8208293e..26374d45 100644 --- a/documentation/requirements.txt +++ b/documentation/requirements.txt @@ -2,3 +2,4 @@ Sphinx>=1.6 sphinx-rtd-theme cairosvg +sphinxcontrib-trio diff --git a/documentation/source/conf.py b/documentation/source/conf.py index 191965c2..892fafe0 100644 --- a/documentation/source/conf.py +++ b/documentation/source/conf.py @@ -41,6 +41,7 @@ 'sphinx.ext.napoleon', 'sphinx.ext.intersphinx', 'cairosvgconverter', + 'sphinxcontrib_trio', ] intersphinx_mapping = {'https://docs.python.org/3': None} From 61e330b0ad44c56d508e6dfa9f52add03fe32e05 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Sun, 3 Feb 2019 19:46:56 +0000 Subject: [PATCH 1536/2656] Don't require SPHINX_BUILD to import packages Previously, the SPHINX_BUILD environment variable was required to import the cocotb packages without the simulator library being imported at the same time. In some cases, this behavior is not desired, and the cocotb package should be importable without the simulator library in at least these cases: - When documenting code with Sphinx, which imports the code to read the documentation from it. - When importing the cocotb package in helper scripts. A future commit will add a helper script called cocotb-config, which relies on this functionality. This commit turns things around: In the default case, cocotb can be imported without the simulator extensions. Only if COCOTB_SIM is set, the simulator extensions are being included as well. In all simulator Makefiles we now set COCOTB_SIM. --- cocotb/__init__.py | 3 +-- cocotb/handle.py | 7 +++---- cocotb/regression.py | 18 +++++++++--------- cocotb/scheduler.py | 8 +++----- cocotb/triggers.py | 8 ++++---- cocotb/utils.py | 9 ++++----- makefiles/simulators/Makefile.aldec | 2 +- makefiles/simulators/Makefile.cvc | 8 ++++---- makefiles/simulators/Makefile.ghdl | 2 +- makefiles/simulators/Makefile.icarus | 4 ++-- makefiles/simulators/Makefile.ius | 2 +- makefiles/simulators/Makefile.nvc | 2 +- makefiles/simulators/Makefile.questa | 2 +- makefiles/simulators/Makefile.vcs | 2 +- tests/test_cases/issue_253/Makefile | 6 +++--- 15 files changed, 39 insertions(+), 44 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 049e5a6f..2f1558dc 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -52,8 +52,7 @@ # scheduler package # GPI logging instance -# For autodocumentation don't need the extension modules -if "SPHINX_BUILD" not in os.environ: +if "COCOTB_SIM" in os.environ: import simulator logging.basicConfig() logging.setLoggerClass(SimBaseLog) diff --git a/cocotb/handle.py b/cocotb/handle.py index d9df3293..4c366e4d 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -38,11 +38,10 @@ import os -# For autodocumentation don't need the extension modules -if "SPHINX_BUILD" in os.environ: - simulator = None -else: +if "COCOTB_SIM" in os.environ: import simulator +else: + simulator = None import cocotb from cocotb.binary import BinaryValue diff --git a/cocotb/regression.py b/cocotb/regression.py index bdcd1f28..90584e78 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -1,7 +1,7 @@ # Copyright (c) 2013, 2018 Potential Ventures Ltd # Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. -# +# # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # * Redistributions of source code must retain the above copyright @@ -13,7 +13,7 @@ # SolarFlare Communications Inc nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. -# +# # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -34,11 +34,11 @@ import sys import os import traceback -# For autodocumentation don't need the extension modules -if "SPHINX_BUILD" in os.environ: - simulator = None -else: + +if "COCOTB_SIM" in os.environ: import simulator +else: + simulator = None # Optional support for coverage collection of testbench files coverage = None @@ -105,10 +105,10 @@ def initialise(self): suite_name = os.getenv('RESULT_TESTSUITE') if os.getenv('RESULT_TESTSUITE') else "all" package_name = os.getenv('RESULT_TESTPACKAGE') if os.getenv('RESULT_TESTPACKAGE') else "all" - + self.xunit.add_testsuite(name=suite_name, tests=repr(self.ntests), package=package_name) - + if (self._seed is not None): self.xunit.add_property(name="random_seed", value=("%d"%self._seed)) @@ -236,7 +236,7 @@ def handle_result(self, result): Dump result to XML and schedule the next test (if any). - Args: + Args: result: The sub-exception of TestComplete to raise. """ real_time = time.time() - self._running_test.start_time diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 714cb98d..11c88753 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -41,12 +41,10 @@ import logging import threading - -# For autodocumentation don't need the extension modules -if "SPHINX_BUILD" in os.environ: - simulator = None -else: +if "COCOTB_SIM" in os.environ: import simulator +else: + simulator = None # Debug mode controlled by environment variables if "COCOTB_ENABLE_PROFILING" in os.environ: diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 7a008ba5..50c4b687 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -30,11 +30,11 @@ import os import weakref -# For autodocumentation don't need the extension modules -if "SPHINX_BUILD" in os.environ: - simulator = None -else: +if "COCOTB_SIM" in os.environ: import simulator +else: + simulator = None + from cocotb.log import SimLog from cocotb.result import raise_error from cocotb.utils import ( diff --git a/cocotb/utils.py b/cocotb/utils.py index 2f1cf863..c8f2678d 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -35,13 +35,12 @@ import sys import weakref -# For autodocumentation don't need the extension modules -if "SPHINX_BUILD" in os.environ: - simulator = None - _LOG_SIM_PRECISION = -15 -else: +if "COCOTB_SIM" in os.environ: import simulator _LOG_SIM_PRECISION = simulator.get_precision() # request once and cache +else: + simulator = None + _LOG_SIM_PRECISION = -15 # python2 to python3 helper functions def get_python_integer_types(): diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 6fa179ab..e168401f 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -171,7 +171,7 @@ endif # that turns on batch mode (i.e. exit on completion/error) results.xml: $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(COCOTB_VPI_LIB) $(CUSTOM_SIM_DEPS) set -o pipefail; cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) $(PLUSARGS) -do runsim.tcl | tee sim.log + $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 $(CMD) $(PLUSARGS) -do runsim.tcl | tee sim.log clean:: @rm -rf $(SIM_BUILD) diff --git a/makefiles/simulators/Makefile.cvc b/makefiles/simulators/Makefile.cvc index 72331774..d1f8dfdf 100644 --- a/makefiles/simulators/Makefile.cvc +++ b/makefiles/simulators/Makefile.cvc @@ -60,7 +60,7 @@ endif # Compilation phase $(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(CMD) $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=libvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) # Execution phase @@ -69,7 +69,7 @@ ifeq ($(CVC_ITERP),1) else results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) endif @@ -77,12 +77,12 @@ endif ifeq ($(CVC_ITERP),1) debug: $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ gdb --args $(CMD) $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=libvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) else debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ gdb --args $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) endif diff --git a/makefiles/simulators/Makefile.ghdl b/makefiles/simulators/Makefile.ghdl index 17c03abc..46d01c0f 100644 --- a/makefiles/simulators/Makefile.ghdl +++ b/makefiles/simulators/Makefile.ghdl @@ -63,7 +63,7 @@ analyse: $(VHDL_SOURCES) $(SIM_BUILD) results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VPI_LIB) cd $(SIM_BUILD); \ PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(CMD) -r $(GHDL_ARGS) $(TOPLEVEL) --vpi=$(LIB_DIR)/libvpi.$(LIB_EXT) $(SIM_ARGS) $(PLUSARGS) clean:: diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index 26c711aa..51e5d710 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -77,12 +77,12 @@ endif results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(ICARUS_BIN_DIR)/vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ gdb --args $(ICARUS_BIN_DIR)/vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) clean:: diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index a3b1a7b2..852a37a8 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -93,7 +93,7 @@ GPI_LIB = $(COCOTB_VPI_LIB) $(COCOTB_VHPI_LIB) results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(GPI_LIB) LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ - LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ set -o pipefail; \ $(CMD) $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) 2>&1 | tee $(SIM_BUILD)/sim.log diff --git a/makefiles/simulators/Makefile.nvc b/makefiles/simulators/Makefile.nvc index 213a3980..0610e0fb 100644 --- a/makefiles/simulators/Makefile.nvc +++ b/makefiles/simulators/Makefile.nvc @@ -41,7 +41,7 @@ results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(CMD) --work=$(RTL_LIBRARY) -e $(TOPLEVEL) cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ LD_LIBRARY_PATH=$(LIB_DIR) \ $(CMD) $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) --work=$(RTL_LIBRARY) -r --load $(COCOTB_VHPI_LIB) $(TRACE) $(TOPLEVEL) diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index e15df051..a5b70fa3 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -146,7 +146,7 @@ endif # Depending on the version of modelsim the interfaces that it supports can change # Append or remove values to INT_LIBS depenending on your license results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) - set -o pipefail; cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + set -o pipefail; cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) \ $(CMD) $(PLUSARGS) -do runsim.do 2>&1 | tee sim.log # Potential fix for buffered stdout, YMMV diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index c5c48b17..483cf349 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -71,7 +71,7 @@ $(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(COCOTB # Execution phase results.xml: $(SIM_BUILD)/simv $(PYTHON_FILES) $(CUSTOM_SIM_DEPS) - -PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + -PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(SIM_BUILD)/simv +define+COCOTB_SIM=1 $(SIM_ARGS) $(EXTRA_ARGS) diff --git a/tests/test_cases/issue_253/Makefile b/tests/test_cases/issue_253/Makefile index 39f78e09..76bc9979 100644 --- a/tests/test_cases/issue_253/Makefile +++ b/tests/test_cases/issue_253/Makefile @@ -45,19 +45,19 @@ results.xml: notset_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ - TESTCASE=issue_253_notset TOPLEVEL= \ + COCOTB_SIM=1 TESTCASE=issue_253_notset TOPLEVEL= \ vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) mkdir -p $@_result && mv results.xml $@_result/ empty_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ - TESTCASE=issue_253_empty TOPLEVEL="" \ + COCOTB_SIM=1 TESTCASE=issue_253_empty TOPLEVEL="" \ vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) mkdir -p $@_result && mv results.xml $@_result/ no_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ - TESTCASE=issue_253_none \ + COCOTB_SIM=1 TESTCASE=issue_253_none \ vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) mkdir -p $@_result && mv results.xml $@_result/ From 72133aa26bc9db86fbaec61e417af63aeb0a3fbc Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Tue, 5 Feb 2019 17:40:56 +0000 Subject: [PATCH 1537/2656] Split SIM_ROOT to COCOTB_PY_DIR & COCOTB_SHARE_DIR SIM_ROOT was defined to be the root of the source directory of cocotb, and used in various places. Soon we will be moving code around in this directory, moving the {include,lib,makefiles} directories into cocotb/share. To prepare for this change this commit changes the code to use two separate variables for the different directories. COCOTB_PY_DIR refers to the directory containing the "cocotb" subdirectory with the Python package. Add COCOTB_PY_DIR to PYTHONPATH to use the cocotb Python code. COCOTB_SHARE_DIR refers to the directory containing the {include,lib,makefiles} directories. These will soon move to cocotb/share in the next step, that's where the name comes from. No change in behavior should be observed after applying this patch, it's an internals-only change. --- Makefile | 2 +- bin/create_files.py | 3 ++- cocotb/__init__.py | 2 +- documentation/source/building.rst | 7 ++++-- examples/endian_swapper/cosim/Makefile | 4 +-- lib/Makefile | 34 +++++++++++++------------- lib/embed/Makefile | 4 +-- lib/fli/Makefile | 4 +-- lib/gpi/Makefile | 4 +-- lib/gpi_log/Makefile | 4 +-- lib/simulator/Makefile | 6 ++--- lib/utils/Makefile | 4 +-- lib/vhpi/Makefile | 4 +-- lib/vpi/Makefile | 4 +-- makefiles/Makefile.inc | 17 +++++++++---- makefiles/Makefile.pylib | 2 +- makefiles/Makefile.sim | 16 ++++++------ makefiles/simulators/Makefile.aldec | 4 +-- makefiles/simulators/Makefile.cvc | 8 +++--- makefiles/simulators/Makefile.ghdl | 2 +- makefiles/simulators/Makefile.icarus | 4 +-- makefiles/simulators/Makefile.ius | 4 +-- makefiles/simulators/Makefile.modelsim | 2 +- makefiles/simulators/Makefile.nvc | 2 +- makefiles/simulators/Makefile.questa | 10 ++++---- makefiles/simulators/Makefile.vcs | 2 +- tests/test_cases/issue_253/Makefile | 8 +++--- 27 files changed, 90 insertions(+), 77 deletions(-) diff --git a/Makefile b/Makefile index 1ab6e29c..efa701cf 100644 --- a/Makefile +++ b/Makefile @@ -57,7 +57,7 @@ test: do_tests ./bin/combine_results.py pycode: - @cp -R $(SIM_ROOT)/cocotb $(FULL_INSTALL_DIR)/ + @cp -R $(COCOTB_PY_DIR)/cocotb $(FULL_INSTALL_DIR)/ src_install: @mkdir -p $(FULL_INSTALL_DIR)/lib diff --git a/bin/create_files.py b/bin/create_files.py index 22fd13e9..b78ea158 100755 --- a/bin/create_files.py +++ b/bin/create_files.py @@ -40,7 +40,8 @@ def print_make_inc(path): makefile = open("/tmp/Makefile.inc", "w") makefile.write("export ARCH:=$(shell uname -m)\n") - makefile.write("export SIM_ROOT:=" + path + "\n") + makefile.write("export COCOTB_PY_DIR:=" + path + "\n") + makefile.write("export COCOTB_SHARE_DIR:=" + path + "\n") makefile.write("export LIB_DIR:=" + path + "/lib/$(ARCH)\n") makefile.close() diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 2f1558dc..7d022634 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -119,7 +119,7 @@ def _initialise_testbench(root_name): if memcheck_port is not None: mem_debug(int(memcheck_port)) - exec_path = os.getenv('SIM_ROOT') + exec_path = os.getenv('COCOTB_PY_DIR') if exec_path is None: exec_path = 'Unknown' diff --git a/documentation/source/building.rst b/documentation/source/building.rst index 32c035c6..e42347ab 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -158,8 +158,11 @@ Additional Environment Variables This needs the :mod:`cherrypy` and :mod:`dowser` Python modules installed. - ``SIM_ROOT`` - The root directory of the Cocotb installation. + ``COCOTB_PY_DIR`` + Path to the directory containing the cocotb Python package in the ``cocotb`` subdirectory. + + ``COCOTB_SHARE_DIR`` + Path to the directory containing the cocotb Makefiles and simulator libraries in the subdirectories ``lib``, ``include``, and ``makefiles``. ``VERSION`` The version of the Cocotb installation. You probably don't want to modify this. diff --git a/examples/endian_swapper/cosim/Makefile b/examples/endian_swapper/cosim/Makefile index c1110354..cca000a0 100644 --- a/examples/endian_swapper/cosim/Makefile +++ b/examples/endian_swapper/cosim/Makefile @@ -1,5 +1,5 @@ -include $(SIM_ROOT)/makefiles/Makefile.pylib -include $(SIM_ROOT)/makefiles/Makefile.inc +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.pylib +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.inc PYTHON_LIBDIR ?= /usr/lib64 SWIG ?= swig diff --git a/lib/Makefile b/lib/Makefile index 4be1d216..f4b7bfe3 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -27,7 +27,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -include $(SIM_ROOT)/makefiles/Makefile.inc +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.inc # FIXME it's a bit nasty to have the source files listed here as dependencies @@ -39,29 +39,29 @@ $(LIB_OBJ_DIR): $(LIB_DIR): $(LIB_OBJ_DIR) mkdir -p $@ -$(LIB_DIR)/libgpilog.$(LIB_EXT): $(SIM_ROOT)/lib/gpi_log/gpi_logging.c | $(LIB_DIR) - make -C $(SIM_ROOT)/lib/gpi_log SIM=$(SIM) +$(LIB_DIR)/libgpilog.$(LIB_EXT): $(COCOTB_SHARE_DIR)/lib/gpi_log/gpi_logging.c | $(LIB_DIR) + make -C $(COCOTB_SHARE_DIR)/lib/gpi_log SIM=$(SIM) -$(LIB_DIR)/libcocotb.$(LIB_EXT): $(SIM_ROOT)/lib/embed/gpi_embed.c | $(LIB_DIR) - make -C $(SIM_ROOT)/lib/embed SIM=$(SIM) +$(LIB_DIR)/libcocotb.$(LIB_EXT): $(COCOTB_SHARE_DIR)/lib/embed/gpi_embed.c | $(LIB_DIR) + make -C $(COCOTB_SHARE_DIR)/lib/embed SIM=$(SIM) -$(LIB_DIR)/libvpi.$(LIB_EXT): $(SIM_ROOT)/lib/vpi/VpiImpl.cpp $(SIM_ROOT)/lib/vpi/VpiCbHdl.cpp | $(LIB_DIR) - make -C $(SIM_ROOT)/lib/vpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) SIM=$(SIM) +$(LIB_DIR)/libvpi.$(LIB_EXT): $(COCOTB_SHARE_DIR)/lib/vpi/VpiImpl.cpp $(COCOTB_SHARE_DIR)/lib/vpi/VpiCbHdl.cpp | $(LIB_DIR) + make -C $(COCOTB_SHARE_DIR)/lib/vpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) SIM=$(SIM) -$(LIB_DIR)/libvhpi.$(LIB_EXT): $(SIM_ROOT)/lib/vhpi/VhpiImpl.cpp $(SIM_ROOT)/lib/vhpi/VhpiCbHdl.cpp | $(LIB_DIR) - make -C $(SIM_ROOT)/lib/vhpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) SIM=$(SIM) +$(LIB_DIR)/libvhpi.$(LIB_EXT): $(COCOTB_SHARE_DIR)/lib/vhpi/VhpiImpl.cpp $(COCOTB_SHARE_DIR)/lib/vhpi/VhpiCbHdl.cpp | $(LIB_DIR) + make -C $(COCOTB_SHARE_DIR)/lib/vhpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) SIM=$(SIM) -$(LIB_DIR)/libgpi.$(LIB_EXT): $(SIM_ROOT)/lib/gpi/GpiCommon.cpp $(SIM_ROOT)/lib/gpi/GpiCbHdl.cpp | $(LIB_DIR) - make -C $(SIM_ROOT)/lib/gpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) SIM=$(SIM) +$(LIB_DIR)/libgpi.$(LIB_EXT): $(COCOTB_SHARE_DIR)/lib/gpi/GpiCommon.cpp $(COCOTB_SHARE_DIR)/lib/gpi/GpiCbHdl.cpp | $(LIB_DIR) + make -C $(COCOTB_SHARE_DIR)/lib/gpi EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) SIM=$(SIM) -$(LIB_DIR)/libfli.$(LIB_EXT): $(SIM_ROOT)/lib/fli/FliImpl.cpp $(SIM_ROOT)/lib/fli/FliCbHdl.cpp $(SIM_ROOT)/lib/fli/FliObjHdl.cpp | $(LIB_DIR) - make -C $(SIM_ROOT)/lib/fli EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) SIM=$(SIM) +$(LIB_DIR)/libfli.$(LIB_EXT): $(COCOTB_SHARE_DIR)/lib/fli/FliImpl.cpp $(COCOTB_SHARE_DIR)/lib/fli/FliCbHdl.cpp $(COCOTB_SHARE_DIR)/lib/fli/FliObjHdl.cpp | $(LIB_DIR) + make -C $(COCOTB_SHARE_DIR)/lib/fli EXTRA_LIBS=$(EXTRA_LIBS) EXTRA_LIBDIRS=$(EXTRA_LIBDIRS) SIM=$(SIM) -$(LIB_DIR)/libsim.$(LIB_EXT): $(SIM_ROOT)/lib/simulator/simulatormodule.c | $(LIB_DIR) - make -C $(SIM_ROOT)/lib/simulator SIM=$(SIM) +$(LIB_DIR)/libsim.$(LIB_EXT): $(COCOTB_SHARE_DIR)/lib/simulator/simulatormodule.c | $(LIB_DIR) + make -C $(COCOTB_SHARE_DIR)/lib/simulator SIM=$(SIM) -$(LIB_DIR)/libcocotbutils.$(LIB_EXT): $(SIM_ROOT)/lib/utils/cocotb_utils.c | $(LIB_DIR) - make -C $(SIM_ROOT)/lib/utils SIM=$(SIM) +$(LIB_DIR)/libcocotbutils.$(LIB_EXT): $(COCOTB_SHARE_DIR)/lib/utils/cocotb_utils.c | $(LIB_DIR) + make -C $(COCOTB_SHARE_DIR)/lib/utils SIM=$(SIM) COCOTB_LIBS := $(LIB_DIR)/libcocotbutils.$(LIB_EXT) \ $(LIB_DIR)/libgpilog.$(LIB_EXT) \ diff --git a/lib/embed/Makefile b/lib/embed/Makefile index 2ed725be..773eb424 100644 --- a/lib/embed/Makefile +++ b/lib/embed/Makefile @@ -27,7 +27,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -include $(SIM_ROOT)/makefiles/Makefile.inc +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.inc INCLUDES += GCC_ARGS += -DPYTHON_SO_LIB=$(PYTHON_DYN_LIB) @@ -42,4 +42,4 @@ all: $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) clean: -@rm -rf $(OBJ_DIR) -include $(SIM_ROOT)/makefiles/Makefile.rules +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.rules diff --git a/lib/fli/Makefile b/lib/fli/Makefile index ea3c6f71..912e2811 100644 --- a/lib/fli/Makefile +++ b/lib/fli/Makefile @@ -26,7 +26,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -include $(SIM_ROOT)/makefiles/Makefile.inc +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.inc INCLUDES += -I$(MODELSIM_BIN_DIR)/../include GXX_ARGS += -DFLI_CHECKING -DUSE_CACHE @@ -41,4 +41,4 @@ all: $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) clean: -@rm -rf $(LIB_DIR)/$(LIB_NAME) -include $(SIM_ROOT)/makefiles/Makefile.rules +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.rules diff --git a/lib/gpi/Makefile b/lib/gpi/Makefile index 027fde1f..f0153f5c 100644 --- a/lib/gpi/Makefile +++ b/lib/gpi/Makefile @@ -27,7 +27,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -include $(SIM_ROOT)/makefiles/Makefile.inc +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.inc INCLUDES += GXX_ARGS += -DVPI_CHECKING -DLIB_EXT=$(LIB_EXT) -DSINGLETON_HANDLES @@ -42,4 +42,4 @@ all: $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) clean: -@rm -rf $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) -include $(SIM_ROOT)/makefiles/Makefile.rules +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.rules diff --git a/lib/gpi_log/Makefile b/lib/gpi_log/Makefile index e8fe95f2..9f28e02c 100644 --- a/lib/gpi_log/Makefile +++ b/lib/gpi_log/Makefile @@ -27,7 +27,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -include $(SIM_ROOT)/makefiles/Makefile.inc +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.inc INCLUDES += GCC_ARGS += -DFILTER @@ -41,4 +41,4 @@ all: $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) clean: -@rm -rf $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) -include $(SIM_ROOT)/makefiles/Makefile.rules +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.rules diff --git a/lib/simulator/Makefile b/lib/simulator/Makefile index 5496b6fc..a0c851d5 100644 --- a/lib/simulator/Makefile +++ b/lib/simulator/Makefile @@ -27,7 +27,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -include $(SIM_ROOT)/makefiles/Makefile.inc +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.inc INCLUDES += GCC_ARGS += @@ -35,7 +35,7 @@ LIBS := -lcocotbutils -lgpi -lgpilog $(PYLIBS) LD_PATH := -L$(LIB_DIR) LIB_NAME := libsim -SRCS := simulatormodule.c +SRCS := simulatormodule.c CLIBS += $(LIB_DIR)/$(LIB_NAME) @@ -46,4 +46,4 @@ clean: -@rm -rf $(LIB_DIR)/simulator.$(LIB_EXT) -@rm -rf $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) -include $(SIM_ROOT)/makefiles/Makefile.rules +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.rules diff --git a/lib/utils/Makefile b/lib/utils/Makefile index 8799c387..c88deef7 100644 --- a/lib/utils/Makefile +++ b/lib/utils/Makefile @@ -27,7 +27,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -include $(SIM_ROOT)/makefiles/Makefile.inc +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.inc INCLUDES += SRCS := cocotb_utils.c @@ -39,4 +39,4 @@ all: $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) clean: -@rm -rf $(OBJ_DIR) -include $(SIM_ROOT)/makefiles/Makefile.rules +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.rules diff --git a/lib/vhpi/Makefile b/lib/vhpi/Makefile index a2fadb4b..7ba973b5 100644 --- a/lib/vhpi/Makefile +++ b/lib/vhpi/Makefile @@ -27,7 +27,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -include $(SIM_ROOT)/makefiles/Makefile.inc +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.inc INCLUDES += GXX_ARGS += -DVHPI_CHECKING @@ -42,4 +42,4 @@ all: $(LIB_DIR)/$(LIB_NAME).$(LIB_EXT) clean: -@rm -rf $(LIB_DIR)/$(LIB_NAME) -include $(SIM_ROOT)/makefiles/Makefile.rules +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.rules diff --git a/lib/vpi/Makefile b/lib/vpi/Makefile index 3acbf3ef..85830117 100644 --- a/lib/vpi/Makefile +++ b/lib/vpi/Makefile @@ -27,7 +27,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -include $(SIM_ROOT)/makefiles/Makefile.inc +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.inc INCLUDES += GXX_ARGS += -DVPI_CHECKING @@ -61,4 +61,4 @@ clean: -@rm -rf $(LIB_DIR)/gpivpi.vpl -@rm -rf $(LIB_DIR)/cocotb.vpi -include $(SIM_ROOT)/makefiles/Makefile.rules +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.rules diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 51bac0cb..b30fa445 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -29,8 +29,15 @@ # Common makefile included by everything -ifeq ($(SIM_ROOT),) -export SIM_ROOT:=$(abspath $(dir $(lastword $(MAKEFILE_LIST)))/..) +# Directory containing the cocotb Python module +ifeq ($(COCOTB_PY_DIR),) +export COCOTB_PY_DIR:=$(abspath $(dir $(lastword $(MAKEFILE_LIST)))/..) +endif + +# Directory containing all support files required to build cocotb-based +# simulations: Makefile fragments, and the simulator libraries. +ifeq ($(COCOTB_SHARE_DIR),) +export COCOTB_SHARE_DIR:=$(abspath $(dir $(lastword $(MAKEFILE_LIST)))/../) endif ifeq ($(USER_DIR),) @@ -51,12 +58,12 @@ OS=Msys endif export OS -include $(SIM_ROOT)/makefiles/Makefile.paths -include $(SIM_ROOT)/makefiles/Makefile.pylib +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.paths +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.pylib export LIB_DIR=$(BUILD_DIR)/libs/$(ARCH) export LIB_OBJ_DIR:= $(BUILD_DIR)/obj/$(ARCH) -export INCLUDES := -I$(SIM_ROOT)/include -I$(PYTHON_INCLUDEDIR) +export INCLUDES := -I$(COCOTB_SHARE_DIR)/include -I$(PYTHON_INCLUDEDIR) LIB_EXT := so PY_EXT := so diff --git a/makefiles/Makefile.pylib b/makefiles/Makefile.pylib index bd177a5a..d11d7e4e 100644 --- a/makefiles/Makefile.pylib +++ b/makefiles/Makefile.pylib @@ -32,7 +32,7 @@ NATIVE_ARCH=$(shell uname -m) ARCH?=$(NATIVE_ARCH) -include $(SIM_ROOT)/makefiles/Makefile.pylib.$(OS) +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.pylib.$(OS) export LD_LIBRARY_PATH := $(LD_LIBRARY_PATH):$(PYTHON_LIBDIR) diff --git a/makefiles/Makefile.sim b/makefiles/Makefile.sim index d4dba5a7..306db4b1 100644 --- a/makefiles/Makefile.sim +++ b/makefiles/Makefile.sim @@ -41,20 +41,22 @@ SIM ?= icarus # Maintain backwards compatibility by supporting upper and lower case SIM variable SIM_LOWERCASE := $(shell echo $(SIM) | tr A-Z a-z) -HAVE_SIMULATOR = $(shell if [ -f $(SIM_ROOT)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) ]; then echo 1; else echo 0; fi;) -AVAILABLE_SIMULATORS = $(patsubst .%,%,$(suffix $(wildcard $(SIM_ROOT)/makefiles/simulators/Makefile.*))) +HAVE_SIMULATOR = $(shell if [ -f $(COCOTB_SHARE_DIR)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) ]; then echo 1; else echo 0; fi;) +AVAILABLE_SIMULATORS = $(patsubst .%,%,$(suffix $(wildcard $(COCOTB_SHARE_DIR)/makefiles/simulators/Makefile.*))) ifeq ($(HAVE_SIMULATOR),0) $(error "Couldn't find makefile for simulator: "$(SIM_LOWERCASE)"! Available simulators: $(AVAILABLE_SIMULATORS)") endif -# We want to include the Python files from Cocotb in the dependencies -CUSTOM_SIM_DEPS += $(shell find $(SIM_ROOT)/cocotb/ -name "*.py") +# Depend on all Python from the cocotb package. This triggers a +# recompilation of the simulation if cocotb is updated. +CUSTOM_SIM_DEPS += $(shell find $(COCOTB_PY_DIR)/cocotb/ -name "*.py") -include $(SIM_ROOT)/lib/Makefile -include $(SIM_ROOT)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) +include $(COCOTB_SHARE_DIR)/lib/Makefile +include $(COCOTB_SHARE_DIR)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) -include $(SIM_ROOT)/version +# XXX: This assumes COCOTB_SHARE_DIR == root dir of the cocotb src tree +include $(COCOTB_SHARE_DIR)/version export VERSION $(SIM_BUILD): diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index e168401f..7a0568fc 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -94,7 +94,7 @@ endif GPI_ARGS = -loadvhpi $(VHPI_LIB):vhpi_startup_routines_bootstrap ifneq ($(VERILOG_SOURCES),) - GPI_EXTRA = vpi + GPI_EXTRA = vpi endif else @@ -170,7 +170,7 @@ endif # Note it's the redirection of the output rather than the 'do' command # that turns on batch mode (i.e. exit on completion/error) results.xml: $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(COCOTB_VPI_LIB) $(CUSTOM_SIM_DEPS) - set -o pipefail; cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ + set -o pipefail; cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(NEW_PYTHONPATH) GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 $(CMD) $(PLUSARGS) -do runsim.tcl | tee sim.log clean:: diff --git a/makefiles/simulators/Makefile.cvc b/makefiles/simulators/Makefile.cvc index d1f8dfdf..6ce01dfc 100644 --- a/makefiles/simulators/Makefile.cvc +++ b/makefiles/simulators/Makefile.cvc @@ -59,7 +59,7 @@ endif # Compilation phase $(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(CMD) $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=libvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) @@ -68,7 +68,7 @@ ifeq ($(CVC_ITERP),1) results.xml: $(SIM_BUILD)/sim.vvp else results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) endif @@ -76,12 +76,12 @@ endif # Execution phase ifeq ($(CVC_ITERP),1) debug: $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ gdb --args $(CMD) $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=libvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) else debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ gdb --args $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) endif diff --git a/makefiles/simulators/Makefile.ghdl b/makefiles/simulators/Makefile.ghdl index 46d01c0f..ed060f69 100644 --- a/makefiles/simulators/Makefile.ghdl +++ b/makefiles/simulators/Makefile.ghdl @@ -62,7 +62,7 @@ analyse: $(VHDL_SOURCES) $(SIM_BUILD) results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VPI_LIB) cd $(SIM_BUILD); \ - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(CMD) -r $(GHDL_ARGS) $(TOPLEVEL) --vpi=$(LIB_DIR)/libvpi.$(LIB_EXT) $(SIM_ARGS) $(PLUSARGS) diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index 51e5d710..9fac52ea 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -76,12 +76,12 @@ NEW_PYTHONPATH := $(PYTHONPATH) endif results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(ICARUS_BIN_DIR)/vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ gdb --args $(ICARUS_BIN_DIR)/vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index 852a37a8..f9582411 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -74,7 +74,7 @@ ifeq ($(TOPLEVEL_LANG),verilog) ROOT_LEVEL = $(TOPLEVEL) ifneq ($(VHDL_SOURCES),) HDL_SOURCES += $(VHDL_SOURCES) - GPI_EXTRA = vhpi + GPI_EXTRA = vhpi endif else ifeq ($(TOPLEVEL_LANG),vhdl) GPI_EXTRA = vhpi @@ -92,7 +92,7 @@ endif GPI_LIB = $(COCOTB_VPI_LIB) $(COCOTB_VHPI_LIB) results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(GPI_LIB) - LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ + LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(PYTHONPATH) \ LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ set -o pipefail; \ diff --git a/makefiles/simulators/Makefile.modelsim b/makefiles/simulators/Makefile.modelsim index 36fb4d04..e437dac3 100644 --- a/makefiles/simulators/Makefile.modelsim +++ b/makefiles/simulators/Makefile.modelsim @@ -31,5 +31,5 @@ #ARCH:=i686 # Everything else is identical to Quest -include $(SIM_ROOT)/makefiles/simulators/Makefile.questa +include $(COCOTB_SHARE_DIR)/makefiles/simulators/Makefile.questa diff --git a/makefiles/simulators/Makefile.nvc b/makefiles/simulators/Makefile.nvc index 0610e0fb..8e5a222f 100644 --- a/makefiles/simulators/Makefile.nvc +++ b/makefiles/simulators/Makefile.nvc @@ -39,7 +39,7 @@ analyse: $(VHDL_SOURCES) $(SIM_BUILD) results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) cd $(SIM_BUILD) && LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) \ $(CMD) --work=$(RTL_LIBRARY) -e $(TOPLEVEL) - cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ + cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(PYTHONPATH) \ LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ LD_LIBRARY_PATH=$(LIB_DIR) \ diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index a5b70fa3..c74cfd67 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -73,13 +73,13 @@ GPI_EXTRA:= ifeq ($(TOPLEVEL_LANG),vhdl) VSIM_ARGS += -foreign \"cocotb_init libfli.$(LIB_EXT)\" ifneq ($(VERILOG_SOURCES),) - GPI_EXTRA = vpi + GPI_EXTRA = vpi endif else ifeq ($(TOPLEVEL_LANG),verilog) VSIM_ARGS += -pli libvpi.$(LIB_EXT) ifneq ($(VHDL_SOURCES),) - GPI_EXTRA = fli + GPI_EXTRA = fli endif else @@ -95,8 +95,8 @@ $(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) $ @echo "vlib $(RTL_LIBRARY)" >> $@ @echo "vmap -c" >> $@ @echo "vmap $(RTL_LIBRARY) $(RTL_LIBRARY)" >> $@ -ifneq ($(VHDL_SOURCES),) - @echo "vcom -work $(RTL_LIBRARY) $(VCOM_ARGS) $(VHDL_SOURCES)" >> $@ +ifneq ($(VHDL_SOURCES),) + @echo "vcom -work $(RTL_LIBRARY) $(VCOM_ARGS) $(VHDL_SOURCES)" >> $@ endif ifneq ($(VERILOG_SOURCES),) @echo "vlog -work $(RTL_LIBRARY) +define+COCOTB_SIM -sv $(VLOG_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES)" >> $@ @@ -147,7 +147,7 @@ endif # Append or remove values to INT_LIBS depenending on your license results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) set -o pipefail; cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ - GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) \ + GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(NEW_PYTHONPATH) \ $(CMD) $(PLUSARGS) -do runsim.do 2>&1 | tee sim.log # Potential fix for buffered stdout, YMMV # STDOUT=$(SIM_BUILD)/sim.log stdbuf --output=0 $(CMD) -do runsim.do 2>&1 && stdbuf --output=0 --input=0 tail -f sim.log && exit $${PIPESTATUS[0]} diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index 483cf349..450db9a9 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -71,7 +71,7 @@ $(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(COCOTB # Execution phase results.xml: $(SIM_BUILD)/simv $(PYTHON_FILES) $(CUSTOM_SIM_DEPS) - -PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ + -PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(SIM_BUILD)/simv +define+COCOTB_SIM=1 $(SIM_ARGS) $(EXTRA_ARGS) diff --git a/tests/test_cases/issue_253/Makefile b/tests/test_cases/issue_253/Makefile index 76bc9979..6977ffdf 100644 --- a/tests/test_cases/issue_253/Makefile +++ b/tests/test_cases/issue_253/Makefile @@ -44,19 +44,19 @@ results.xml: @echo "Skipping" notset_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ COCOTB_SIM=1 TESTCASE=issue_253_notset TOPLEVEL= \ vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) mkdir -p $@_result && mv results.xml $@_result/ empty_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ COCOTB_SIM=1 TESTCASE=issue_253_empty TOPLEVEL="" \ vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) mkdir -p $@_result && mv results.xml $@_result/ no_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ COCOTB_SIM=1 TESTCASE=issue_253_none \ vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) mkdir -p $@_result && mv results.xml $@_result/ @@ -64,4 +64,4 @@ no_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VP endif clean:: - @rm -rf empty_top_level_result no_top_level_result notset_top_level_result + @rm -rf empty_top_level_result no_top_level_result notset_top_level_result From ccb8a8311271a1f333553897e58b392d1655487c Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Tue, 5 Feb 2019 13:18:54 +0000 Subject: [PATCH 1538/2656] Set PYTHONPATH for cocotb package centrally Every simulation needs two things present in PYTHONPATH: - The path to the cocotb Python code, living in COCOTB_PY_DIR - The path to the compiled cocotb simulator library, living (after compilation) in LIB_DIR. Simplify the simulator-specific Makefiles by giving the path to the first item centrally in Makefile.sim. The deeper motivation for this change is the packaging work. If cocotb is installed from a package into a location which is already in the PYTHONPATH the Python code directory doesn't need to be added to PYTHONPATH in the Makefiles any more. --- makefiles/Makefile.pylib.Msys | 2 +- makefiles/Makefile.sim | 6 ++++++ makefiles/simulators/Makefile.aldec | 2 +- makefiles/simulators/Makefile.cvc | 8 ++++---- makefiles/simulators/Makefile.ghdl | 2 +- makefiles/simulators/Makefile.icarus | 4 ++-- makefiles/simulators/Makefile.ius | 2 +- makefiles/simulators/Makefile.nvc | 2 +- makefiles/simulators/Makefile.questa | 2 +- makefiles/simulators/Makefile.vcs | 2 +- 10 files changed, 19 insertions(+), 13 deletions(-) diff --git a/makefiles/Makefile.pylib.Msys b/makefiles/Makefile.pylib.Msys index f66462c6..b3acd914 100644 --- a/makefiles/Makefile.pylib.Msys +++ b/makefiles/Makefile.pylib.Msys @@ -47,7 +47,7 @@ PYTHON_DYNLIBDIR:=$(shell $(PYTHON_BIN) -c 'from distutils import sysconfig; pri PYTHON_LIBDIR=$(PYTHON_DIR)/libs period := . -empty := +empty := PYTHON_VERSION=$(subst $(period),$(empty),$(LOCAL_PYTHON_VERSION)) PYTHON_INCLUDEDIR := $(shell $(PYTHON_BIN) -c 'import distutils.sysconfig; print( distutils.sysconfig.get_python_inc() )') diff --git a/makefiles/Makefile.sim b/makefiles/Makefile.sim index 306db4b1..8d52712b 100644 --- a/makefiles/Makefile.sim +++ b/makefiles/Makefile.sim @@ -59,6 +59,12 @@ include $(COCOTB_SHARE_DIR)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) include $(COCOTB_SHARE_DIR)/version export VERSION +# Append directory containing the cocotb Python package to PYTHONPATH +# XXX: This is only needed if cocotb is not installed into the default +# Python search path. +PYTHONPATH := $(COCOTB_PY_DIR):$(PYTHONPATH) +export PYTHONPATH + $(SIM_BUILD): mkdir -p $@ diff --git a/makefiles/simulators/Makefile.aldec b/makefiles/simulators/Makefile.aldec index 7a0568fc..35df08b1 100644 --- a/makefiles/simulators/Makefile.aldec +++ b/makefiles/simulators/Makefile.aldec @@ -170,7 +170,7 @@ endif # Note it's the redirection of the output rather than the 'do' command # that turns on batch mode (i.e. exit on completion/error) results.xml: $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(COCOTB_VPI_LIB) $(CUSTOM_SIM_DEPS) - set -o pipefail; cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(NEW_PYTHONPATH) GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ + set -o pipefail; cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(PWD):$(NEW_PYTHONPATH) GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 $(CMD) $(PLUSARGS) -do runsim.tcl | tee sim.log clean:: diff --git a/makefiles/simulators/Makefile.cvc b/makefiles/simulators/Makefile.cvc index 6ce01dfc..cfeaffe4 100644 --- a/makefiles/simulators/Makefile.cvc +++ b/makefiles/simulators/Makefile.cvc @@ -59,7 +59,7 @@ endif # Compilation phase $(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + PYTHONPATH=$(LIB_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(CMD) $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=libvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) @@ -68,7 +68,7 @@ ifeq ($(CVC_ITERP),1) results.xml: $(SIM_BUILD)/sim.vvp else results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) - PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + PYTHONPATH=$(LIB_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) endif @@ -76,12 +76,12 @@ endif # Execution phase ifeq ($(CVC_ITERP),1) debug: $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + PYTHONPATH=$(LIB_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ gdb --args $(CMD) $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=libvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) else debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) - PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + PYTHONPATH=$(LIB_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ gdb --args $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) endif diff --git a/makefiles/simulators/Makefile.ghdl b/makefiles/simulators/Makefile.ghdl index ed060f69..de113681 100644 --- a/makefiles/simulators/Makefile.ghdl +++ b/makefiles/simulators/Makefile.ghdl @@ -62,7 +62,7 @@ analyse: $(VHDL_SOURCES) $(SIM_BUILD) results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VPI_LIB) cd $(SIM_BUILD); \ - PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + PYTHONPATH=$(LIB_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(CMD) -r $(GHDL_ARGS) $(TOPLEVEL) --vpi=$(LIB_DIR)/libvpi.$(LIB_EXT) $(SIM_ARGS) $(PLUSARGS) diff --git a/makefiles/simulators/Makefile.icarus b/makefiles/simulators/Makefile.icarus index 9fac52ea..5d031874 100644 --- a/makefiles/simulators/Makefile.icarus +++ b/makefiles/simulators/Makefile.icarus @@ -76,12 +76,12 @@ NEW_PYTHONPATH := $(PYTHONPATH) endif results.xml: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(LIB_DIR):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(ICARUS_BIN_DIR)/vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) - PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(LIB_DIR):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ gdb --args $(ICARUS_BIN_DIR)/vvp -M $(LIB_DIR) -m gpivpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) diff --git a/makefiles/simulators/Makefile.ius b/makefiles/simulators/Makefile.ius index f9582411..7530337e 100644 --- a/makefiles/simulators/Makefile.ius +++ b/makefiles/simulators/Makefile.ius @@ -92,7 +92,7 @@ endif GPI_LIB = $(COCOTB_VPI_LIB) $(COCOTB_VHPI_LIB) results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(GPI_LIB) - LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(PYTHONPATH) \ + LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(PWD):$(PYTHONPATH) \ LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ set -o pipefail; \ diff --git a/makefiles/simulators/Makefile.nvc b/makefiles/simulators/Makefile.nvc index 8e5a222f..b0262ab8 100644 --- a/makefiles/simulators/Makefile.nvc +++ b/makefiles/simulators/Makefile.nvc @@ -39,7 +39,7 @@ analyse: $(VHDL_SOURCES) $(SIM_BUILD) results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) cd $(SIM_BUILD) && LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) \ $(CMD) --work=$(RTL_LIBRARY) -e $(TOPLEVEL) - cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(PYTHONPATH) \ + cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(PWD):$(PYTHONPATH) \ LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ LD_LIBRARY_PATH=$(LIB_DIR) \ diff --git a/makefiles/simulators/Makefile.questa b/makefiles/simulators/Makefile.questa index c74cfd67..f25b4725 100644 --- a/makefiles/simulators/Makefile.questa +++ b/makefiles/simulators/Makefile.questa @@ -147,7 +147,7 @@ endif # Append or remove values to INT_LIBS depenending on your license results.xml: $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) set -o pipefail; cd $(SIM_BUILD) && $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ - GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(NEW_PYTHONPATH) \ + GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) PYTHONPATH=$(LIB_DIR):$(PWD):$(NEW_PYTHONPATH) \ $(CMD) $(PLUSARGS) -do runsim.do 2>&1 | tee sim.log # Potential fix for buffered stdout, YMMV # STDOUT=$(SIM_BUILD)/sim.log stdbuf --output=0 $(CMD) -do runsim.do 2>&1 && stdbuf --output=0 --input=0 tail -f sim.log && exit $${PIPESTATUS[0]} diff --git a/makefiles/simulators/Makefile.vcs b/makefiles/simulators/Makefile.vcs index 450db9a9..0444c500 100644 --- a/makefiles/simulators/Makefile.vcs +++ b/makefiles/simulators/Makefile.vcs @@ -71,7 +71,7 @@ $(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(COCOTB # Execution phase results.xml: $(SIM_BUILD)/simv $(PYTHON_FILES) $(CUSTOM_SIM_DEPS) - -PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ + -PYTHONPATH=$(LIB_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(SIM_BUILD)/simv +define+COCOTB_SIM=1 $(SIM_ARGS) $(EXTRA_ARGS) From 7fba3e75afa224022f5adc8861bc88fb0941ccf7 Mon Sep 17 00:00:00 2001 From: hash Date: Fri, 8 Feb 2019 02:41:51 +0800 Subject: [PATCH 1539/2656] fix: new_iter should always be initialized. GCC-8.2 is unforgiving on this. --- lib/vpi/VpiImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vpi/VpiImpl.cpp b/lib/vpi/VpiImpl.cpp index b0f1b73c..7b6e0155 100644 --- a/lib/vpi/VpiImpl.cpp +++ b/lib/vpi/VpiImpl.cpp @@ -456,7 +456,7 @@ GpiObjHdl *VpiImpl::get_root_handle(const char* name) GpiIterator *VpiImpl::iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) { - GpiIterator *new_iter; + GpiIterator *new_iter = NULL; switch (type) { case GPI_OBJECTS: new_iter = new VpiIterator(this, obj_hdl); From bbc572dfc752a380b536fbed259e553b34c2ac10 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Sun, 3 Feb 2019 19:33:16 +0000 Subject: [PATCH 1540/2656] Move all installed files into the cocotb directory setuptools depends on having all installed files below the cocotb directory. Move the files there. This commit is preparing to add Python packaging to cocotb, but should not yet require any changes for people using cocotb by pointing the COCOTB environment variable to the cocotb top-level source directory. For backwards-compat two Makefiles, $COCOTB/makefiles/Makefile.{sim,inc} which call the Makefile.{sim,inc} at the new path. Note that this commit does *not* add symlinks for the old directory paths, as this approach wouldn't work on Windows. --- Makefile | 2 +- .../share/include}/cocotb_utils.h | 0 {include => cocotb/share/include}/embed.h | 0 {include => cocotb/share/include}/gpi.h | 0 .../share/include}/gpi_logging.h | 0 .../share/include}/sv_vpi_user.h | 0 {include => cocotb/share/include}/vhpi_user.h | 0 {include => cocotb/share/include}/vpi_user.h | 0 {lib => cocotb/share/lib}/Makefile | 0 .../share/lib}/compat/python3_compat.h | 0 {lib => cocotb/share/lib}/embed/Makefile | 0 {lib => cocotb/share/lib}/embed/gpi_embed.c | 0 {lib => cocotb/share/lib}/fli/FliCbHdl.cpp | 0 {lib => cocotb/share/lib}/fli/FliImpl.cpp | 0 {lib => cocotb/share/lib}/fli/FliImpl.h | 0 {lib => cocotb/share/lib}/fli/FliObjHdl.cpp | 0 {lib => cocotb/share/lib}/fli/Makefile | 0 {lib => cocotb/share/lib}/fli/entrypoint.vhdl | 0 {lib => cocotb/share/lib}/gpi/GpiCbHdl.cpp | 0 {lib => cocotb/share/lib}/gpi/GpiCommon.cpp | 0 {lib => cocotb/share/lib}/gpi/Makefile | 0 {lib => cocotb/share/lib}/gpi/gpi_priv.h | 0 {lib => cocotb/share/lib}/gpi_log/Makefile | 0 .../share/lib}/gpi_log/gpi_logging.c | 0 {lib => cocotb/share/lib}/simulator/Makefile | 0 .../share/lib}/simulator/simulatormodule.c | 0 .../share/lib}/simulator/simulatormodule.h | 0 .../lib}/simulator/simulatormodule_python2.c | 0 .../lib}/simulator/simulatormodule_python3.c | 0 {lib => cocotb/share/lib}/utils/Makefile | 0 .../share/lib}/utils/cocotb_utils.c | 0 {lib => cocotb/share/lib}/vhpi/Makefile | 0 {lib => cocotb/share/lib}/vhpi/VhpiCbHdl.cpp | 0 {lib => cocotb/share/lib}/vhpi/VhpiImpl.cpp | 0 {lib => cocotb/share/lib}/vhpi/VhpiImpl.h | 0 {lib => cocotb/share/lib}/vpi/Makefile | 0 {lib => cocotb/share/lib}/vpi/VpiCbHdl.cpp | 0 {lib => cocotb/share/lib}/vpi/VpiImpl.cpp | 0 {lib => cocotb/share/lib}/vpi/VpiImpl.h | 0 .../share/makefiles}/Makefile.doc | 0 cocotb/share/makefiles/Makefile.inc | 105 ++++++++++++++++++ .../share/makefiles}/Makefile.paths | 0 .../share/makefiles}/Makefile.pylib | 0 .../share/makefiles}/Makefile.pylib.Darwin | 0 .../share/makefiles}/Makefile.pylib.Linux | 0 .../share/makefiles}/Makefile.pylib.Msys | 0 .../share/makefiles}/Makefile.rules | 0 cocotb/share/makefiles/Makefile.sim | 83 ++++++++++++++ .../makefiles}/simulators/Makefile.aldec | 0 .../share/makefiles}/simulators/Makefile.cvc | 0 .../share/makefiles}/simulators/Makefile.ghdl | 0 .../makefiles}/simulators/Makefile.icarus | 0 .../share/makefiles}/simulators/Makefile.ius | 0 .../makefiles}/simulators/Makefile.modelsim | 0 .../share/makefiles}/simulators/Makefile.nvc | 0 .../makefiles}/simulators/Makefile.questa | 0 .../share/makefiles}/simulators/Makefile.vcs | 0 makefiles/Makefile.inc | 81 +------------- makefiles/Makefile.sim | 61 ++-------- 59 files changed, 205 insertions(+), 127 deletions(-) rename {include => cocotb/share/include}/cocotb_utils.h (100%) rename {include => cocotb/share/include}/embed.h (100%) rename {include => cocotb/share/include}/gpi.h (100%) rename {include => cocotb/share/include}/gpi_logging.h (100%) rename {include => cocotb/share/include}/sv_vpi_user.h (100%) rename {include => cocotb/share/include}/vhpi_user.h (100%) rename {include => cocotb/share/include}/vpi_user.h (100%) rename {lib => cocotb/share/lib}/Makefile (100%) rename {lib => cocotb/share/lib}/compat/python3_compat.h (100%) rename {lib => cocotb/share/lib}/embed/Makefile (100%) rename {lib => cocotb/share/lib}/embed/gpi_embed.c (100%) rename {lib => cocotb/share/lib}/fli/FliCbHdl.cpp (100%) rename {lib => cocotb/share/lib}/fli/FliImpl.cpp (100%) rename {lib => cocotb/share/lib}/fli/FliImpl.h (100%) rename {lib => cocotb/share/lib}/fli/FliObjHdl.cpp (100%) rename {lib => cocotb/share/lib}/fli/Makefile (100%) rename {lib => cocotb/share/lib}/fli/entrypoint.vhdl (100%) rename {lib => cocotb/share/lib}/gpi/GpiCbHdl.cpp (100%) rename {lib => cocotb/share/lib}/gpi/GpiCommon.cpp (100%) rename {lib => cocotb/share/lib}/gpi/Makefile (100%) rename {lib => cocotb/share/lib}/gpi/gpi_priv.h (100%) rename {lib => cocotb/share/lib}/gpi_log/Makefile (100%) rename {lib => cocotb/share/lib}/gpi_log/gpi_logging.c (100%) rename {lib => cocotb/share/lib}/simulator/Makefile (100%) rename {lib => cocotb/share/lib}/simulator/simulatormodule.c (100%) rename {lib => cocotb/share/lib}/simulator/simulatormodule.h (100%) rename {lib => cocotb/share/lib}/simulator/simulatormodule_python2.c (100%) rename {lib => cocotb/share/lib}/simulator/simulatormodule_python3.c (100%) rename {lib => cocotb/share/lib}/utils/Makefile (100%) rename {lib => cocotb/share/lib}/utils/cocotb_utils.c (100%) rename {lib => cocotb/share/lib}/vhpi/Makefile (100%) rename {lib => cocotb/share/lib}/vhpi/VhpiCbHdl.cpp (100%) rename {lib => cocotb/share/lib}/vhpi/VhpiImpl.cpp (100%) rename {lib => cocotb/share/lib}/vhpi/VhpiImpl.h (100%) rename {lib => cocotb/share/lib}/vpi/Makefile (100%) rename {lib => cocotb/share/lib}/vpi/VpiCbHdl.cpp (100%) rename {lib => cocotb/share/lib}/vpi/VpiImpl.cpp (100%) rename {lib => cocotb/share/lib}/vpi/VpiImpl.h (100%) rename {makefiles => cocotb/share/makefiles}/Makefile.doc (100%) create mode 100644 cocotb/share/makefiles/Makefile.inc rename {makefiles => cocotb/share/makefiles}/Makefile.paths (100%) rename {makefiles => cocotb/share/makefiles}/Makefile.pylib (100%) rename {makefiles => cocotb/share/makefiles}/Makefile.pylib.Darwin (100%) rename {makefiles => cocotb/share/makefiles}/Makefile.pylib.Linux (100%) rename {makefiles => cocotb/share/makefiles}/Makefile.pylib.Msys (100%) rename {makefiles => cocotb/share/makefiles}/Makefile.rules (100%) create mode 100644 cocotb/share/makefiles/Makefile.sim rename {makefiles => cocotb/share/makefiles}/simulators/Makefile.aldec (100%) rename {makefiles => cocotb/share/makefiles}/simulators/Makefile.cvc (100%) rename {makefiles => cocotb/share/makefiles}/simulators/Makefile.ghdl (100%) rename {makefiles => cocotb/share/makefiles}/simulators/Makefile.icarus (100%) rename {makefiles => cocotb/share/makefiles}/simulators/Makefile.ius (100%) rename {makefiles => cocotb/share/makefiles}/simulators/Makefile.modelsim (100%) rename {makefiles => cocotb/share/makefiles}/simulators/Makefile.nvc (100%) rename {makefiles => cocotb/share/makefiles}/simulators/Makefile.questa (100%) rename {makefiles => cocotb/share/makefiles}/simulators/Makefile.vcs (100%) diff --git a/Makefile b/Makefile index efa701cf..34e0982e 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ FULL_INSTALL_DIR=$(INSTALL_DIR)/cocotb-$(VERSION) all: test -include makefiles/Makefile.inc +include cocotb/share/makefiles/Makefile.inc include version clean: diff --git a/include/cocotb_utils.h b/cocotb/share/include/cocotb_utils.h similarity index 100% rename from include/cocotb_utils.h rename to cocotb/share/include/cocotb_utils.h diff --git a/include/embed.h b/cocotb/share/include/embed.h similarity index 100% rename from include/embed.h rename to cocotb/share/include/embed.h diff --git a/include/gpi.h b/cocotb/share/include/gpi.h similarity index 100% rename from include/gpi.h rename to cocotb/share/include/gpi.h diff --git a/include/gpi_logging.h b/cocotb/share/include/gpi_logging.h similarity index 100% rename from include/gpi_logging.h rename to cocotb/share/include/gpi_logging.h diff --git a/include/sv_vpi_user.h b/cocotb/share/include/sv_vpi_user.h similarity index 100% rename from include/sv_vpi_user.h rename to cocotb/share/include/sv_vpi_user.h diff --git a/include/vhpi_user.h b/cocotb/share/include/vhpi_user.h similarity index 100% rename from include/vhpi_user.h rename to cocotb/share/include/vhpi_user.h diff --git a/include/vpi_user.h b/cocotb/share/include/vpi_user.h similarity index 100% rename from include/vpi_user.h rename to cocotb/share/include/vpi_user.h diff --git a/lib/Makefile b/cocotb/share/lib/Makefile similarity index 100% rename from lib/Makefile rename to cocotb/share/lib/Makefile diff --git a/lib/compat/python3_compat.h b/cocotb/share/lib/compat/python3_compat.h similarity index 100% rename from lib/compat/python3_compat.h rename to cocotb/share/lib/compat/python3_compat.h diff --git a/lib/embed/Makefile b/cocotb/share/lib/embed/Makefile similarity index 100% rename from lib/embed/Makefile rename to cocotb/share/lib/embed/Makefile diff --git a/lib/embed/gpi_embed.c b/cocotb/share/lib/embed/gpi_embed.c similarity index 100% rename from lib/embed/gpi_embed.c rename to cocotb/share/lib/embed/gpi_embed.c diff --git a/lib/fli/FliCbHdl.cpp b/cocotb/share/lib/fli/FliCbHdl.cpp similarity index 100% rename from lib/fli/FliCbHdl.cpp rename to cocotb/share/lib/fli/FliCbHdl.cpp diff --git a/lib/fli/FliImpl.cpp b/cocotb/share/lib/fli/FliImpl.cpp similarity index 100% rename from lib/fli/FliImpl.cpp rename to cocotb/share/lib/fli/FliImpl.cpp diff --git a/lib/fli/FliImpl.h b/cocotb/share/lib/fli/FliImpl.h similarity index 100% rename from lib/fli/FliImpl.h rename to cocotb/share/lib/fli/FliImpl.h diff --git a/lib/fli/FliObjHdl.cpp b/cocotb/share/lib/fli/FliObjHdl.cpp similarity index 100% rename from lib/fli/FliObjHdl.cpp rename to cocotb/share/lib/fli/FliObjHdl.cpp diff --git a/lib/fli/Makefile b/cocotb/share/lib/fli/Makefile similarity index 100% rename from lib/fli/Makefile rename to cocotb/share/lib/fli/Makefile diff --git a/lib/fli/entrypoint.vhdl b/cocotb/share/lib/fli/entrypoint.vhdl similarity index 100% rename from lib/fli/entrypoint.vhdl rename to cocotb/share/lib/fli/entrypoint.vhdl diff --git a/lib/gpi/GpiCbHdl.cpp b/cocotb/share/lib/gpi/GpiCbHdl.cpp similarity index 100% rename from lib/gpi/GpiCbHdl.cpp rename to cocotb/share/lib/gpi/GpiCbHdl.cpp diff --git a/lib/gpi/GpiCommon.cpp b/cocotb/share/lib/gpi/GpiCommon.cpp similarity index 100% rename from lib/gpi/GpiCommon.cpp rename to cocotb/share/lib/gpi/GpiCommon.cpp diff --git a/lib/gpi/Makefile b/cocotb/share/lib/gpi/Makefile similarity index 100% rename from lib/gpi/Makefile rename to cocotb/share/lib/gpi/Makefile diff --git a/lib/gpi/gpi_priv.h b/cocotb/share/lib/gpi/gpi_priv.h similarity index 100% rename from lib/gpi/gpi_priv.h rename to cocotb/share/lib/gpi/gpi_priv.h diff --git a/lib/gpi_log/Makefile b/cocotb/share/lib/gpi_log/Makefile similarity index 100% rename from lib/gpi_log/Makefile rename to cocotb/share/lib/gpi_log/Makefile diff --git a/lib/gpi_log/gpi_logging.c b/cocotb/share/lib/gpi_log/gpi_logging.c similarity index 100% rename from lib/gpi_log/gpi_logging.c rename to cocotb/share/lib/gpi_log/gpi_logging.c diff --git a/lib/simulator/Makefile b/cocotb/share/lib/simulator/Makefile similarity index 100% rename from lib/simulator/Makefile rename to cocotb/share/lib/simulator/Makefile diff --git a/lib/simulator/simulatormodule.c b/cocotb/share/lib/simulator/simulatormodule.c similarity index 100% rename from lib/simulator/simulatormodule.c rename to cocotb/share/lib/simulator/simulatormodule.c diff --git a/lib/simulator/simulatormodule.h b/cocotb/share/lib/simulator/simulatormodule.h similarity index 100% rename from lib/simulator/simulatormodule.h rename to cocotb/share/lib/simulator/simulatormodule.h diff --git a/lib/simulator/simulatormodule_python2.c b/cocotb/share/lib/simulator/simulatormodule_python2.c similarity index 100% rename from lib/simulator/simulatormodule_python2.c rename to cocotb/share/lib/simulator/simulatormodule_python2.c diff --git a/lib/simulator/simulatormodule_python3.c b/cocotb/share/lib/simulator/simulatormodule_python3.c similarity index 100% rename from lib/simulator/simulatormodule_python3.c rename to cocotb/share/lib/simulator/simulatormodule_python3.c diff --git a/lib/utils/Makefile b/cocotb/share/lib/utils/Makefile similarity index 100% rename from lib/utils/Makefile rename to cocotb/share/lib/utils/Makefile diff --git a/lib/utils/cocotb_utils.c b/cocotb/share/lib/utils/cocotb_utils.c similarity index 100% rename from lib/utils/cocotb_utils.c rename to cocotb/share/lib/utils/cocotb_utils.c diff --git a/lib/vhpi/Makefile b/cocotb/share/lib/vhpi/Makefile similarity index 100% rename from lib/vhpi/Makefile rename to cocotb/share/lib/vhpi/Makefile diff --git a/lib/vhpi/VhpiCbHdl.cpp b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp similarity index 100% rename from lib/vhpi/VhpiCbHdl.cpp rename to cocotb/share/lib/vhpi/VhpiCbHdl.cpp diff --git a/lib/vhpi/VhpiImpl.cpp b/cocotb/share/lib/vhpi/VhpiImpl.cpp similarity index 100% rename from lib/vhpi/VhpiImpl.cpp rename to cocotb/share/lib/vhpi/VhpiImpl.cpp diff --git a/lib/vhpi/VhpiImpl.h b/cocotb/share/lib/vhpi/VhpiImpl.h similarity index 100% rename from lib/vhpi/VhpiImpl.h rename to cocotb/share/lib/vhpi/VhpiImpl.h diff --git a/lib/vpi/Makefile b/cocotb/share/lib/vpi/Makefile similarity index 100% rename from lib/vpi/Makefile rename to cocotb/share/lib/vpi/Makefile diff --git a/lib/vpi/VpiCbHdl.cpp b/cocotb/share/lib/vpi/VpiCbHdl.cpp similarity index 100% rename from lib/vpi/VpiCbHdl.cpp rename to cocotb/share/lib/vpi/VpiCbHdl.cpp diff --git a/lib/vpi/VpiImpl.cpp b/cocotb/share/lib/vpi/VpiImpl.cpp similarity index 100% rename from lib/vpi/VpiImpl.cpp rename to cocotb/share/lib/vpi/VpiImpl.cpp diff --git a/lib/vpi/VpiImpl.h b/cocotb/share/lib/vpi/VpiImpl.h similarity index 100% rename from lib/vpi/VpiImpl.h rename to cocotb/share/lib/vpi/VpiImpl.h diff --git a/makefiles/Makefile.doc b/cocotb/share/makefiles/Makefile.doc similarity index 100% rename from makefiles/Makefile.doc rename to cocotb/share/makefiles/Makefile.doc diff --git a/cocotb/share/makefiles/Makefile.inc b/cocotb/share/makefiles/Makefile.inc new file mode 100644 index 00000000..20d7dd31 --- /dev/null +++ b/cocotb/share/makefiles/Makefile.inc @@ -0,0 +1,105 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################## + +# Common makefile included by everything + +# Directory containing the cocotb Python module +ifeq ($(COCOTB_PY_DIR),) +export COCOTB_PY_DIR:=$(abspath $(dir $(lastword $(MAKEFILE_LIST)))/../../..) +endif + +# Directory containing all support files required to build cocotb-based +# simulations: Makefile fragments, and the simulator libraries. +ifeq ($(COCOTB_SHARE_DIR),) +export COCOTB_SHARE_DIR:=$(abspath $(dir $(lastword $(MAKEFILE_LIST)))/..) +endif + +ifeq ($(USER_DIR),) +export USER_DIR:=$(abspath $(dir $(firstword $(MAKEFILE_LIST)))) +endif + +BUILD_DIR=$(USER_DIR)/build +export BUILD_DIR + +ARCH?=$(shell uname -m) +export ARCH + +OS=$(shell uname) +ifneq (, $(findstring MINGW, $(OS))) +OS=Msys +else ifneq (, $(findstring MSYS, $(OS))) +OS=Msys +endif +export OS + +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.paths +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.pylib + +export LIB_DIR=$(BUILD_DIR)/libs/$(ARCH) +export LIB_OBJ_DIR:= $(BUILD_DIR)/obj/$(ARCH) +export INCLUDES := -I$(COCOTB_SHARE_DIR)/include -I$(PYTHON_INCLUDEDIR) + +LIB_EXT := so +PY_EXT := so + +# Base GCC flags +ifeq ($(ARCH),i686) +GXX_ARGS := -m32 +endif + +ifeq ($(OS),Darwin) +GXX_ARGS += -g -DDEBUG -fpic +GCC_ARGS := $(GXX_ARGS) +else ifeq ($(OS),Msys) +GXX_ARGS += -g -DDEBUG -shared +ifeq ($(ARCH),x86_64) +GXX_ARGS += -DMS_WIN64 +endif +GCC_ARGS := $(GXX_ARGS) +LIB_EXT := dll +PY_EXT := pyd +else ifeq ($(OS),Linux) +GXX_ARGS += -Werror -Wcast-qual -Wcast-align -Wwrite-strings \ + -Wall -Wno-unused-parameter \ + -fno-common -g -DDEBUG -fpic +GCC_ARGS = $(GXX_ARGS) -Wstrict-prototypes -Waggregate-return +else +$(error "Unsupported os " $(OS)) +endif + +ifeq ($(OS),Darwin) +SO_LINKER_ARGS := -shared -undefined suppress -flat_namespace -L$(PYTHON_LIBDIR) +else ifeq ($(OS),Linux) +SO_LINKER_ARGS := -shared -Xlinker -L$(PYTHON_LIBDIR) +else ifeq ($(OS),Msys) +SO_LINKER_ARGS := -shared -Wl,-no-undefined -Wl,-enable-runtime-pseudo-reloc-v2 \ + -Wl,--enable-auto-import -L$(PYTHON_LIBDIR) +else +$(error "Unsupported os" $(OS)) +endif diff --git a/makefiles/Makefile.paths b/cocotb/share/makefiles/Makefile.paths similarity index 100% rename from makefiles/Makefile.paths rename to cocotb/share/makefiles/Makefile.paths diff --git a/makefiles/Makefile.pylib b/cocotb/share/makefiles/Makefile.pylib similarity index 100% rename from makefiles/Makefile.pylib rename to cocotb/share/makefiles/Makefile.pylib diff --git a/makefiles/Makefile.pylib.Darwin b/cocotb/share/makefiles/Makefile.pylib.Darwin similarity index 100% rename from makefiles/Makefile.pylib.Darwin rename to cocotb/share/makefiles/Makefile.pylib.Darwin diff --git a/makefiles/Makefile.pylib.Linux b/cocotb/share/makefiles/Makefile.pylib.Linux similarity index 100% rename from makefiles/Makefile.pylib.Linux rename to cocotb/share/makefiles/Makefile.pylib.Linux diff --git a/makefiles/Makefile.pylib.Msys b/cocotb/share/makefiles/Makefile.pylib.Msys similarity index 100% rename from makefiles/Makefile.pylib.Msys rename to cocotb/share/makefiles/Makefile.pylib.Msys diff --git a/makefiles/Makefile.rules b/cocotb/share/makefiles/Makefile.rules similarity index 100% rename from makefiles/Makefile.rules rename to cocotb/share/makefiles/Makefile.rules diff --git a/cocotb/share/makefiles/Makefile.sim b/cocotb/share/makefiles/Makefile.sim new file mode 100644 index 00000000..ad5fdc08 --- /dev/null +++ b/cocotb/share/makefiles/Makefile.sim @@ -0,0 +1,83 @@ +############################################################################### +# Copyright (c) 2013, 2018 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +# This is a simple wrapper Makefile to pull in the appropriate Makefile for +# the desired simulator and set up common paths for the sims to use to build with + +all: sim + +SIM_BUILD ?= sim_build +export SIM_BUILD + +# Default to Icarus if no simulator is defined +SIM ?= icarus + +# Maintain backwards compatibility by supporting upper and lower case SIM variable +SIM_LOWERCASE := $(shell echo $(SIM) | tr A-Z a-z) + +HAVE_SIMULATOR = $(shell if [ -f $(COCOTB_SHARE_DIR)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) ]; then echo 1; else echo 0; fi;) +AVAILABLE_SIMULATORS = $(patsubst .%,%,$(suffix $(wildcard $(COCOTB_SHARE_DIR)/makefiles/simulators/Makefile.*))) + +ifeq ($(HAVE_SIMULATOR),0) +$(error "Couldn't find makefile for simulator: "$(SIM_LOWERCASE)"! Available simulators: $(AVAILABLE_SIMULATORS)") +endif + +# Depend on all Python from the cocotb package. This triggers a +# recompilation of the simulation if cocotb is updated. +CUSTOM_SIM_DEPS += $(shell find $(COCOTB_PY_DIR)/cocotb/ -name "*.py") + +include $(COCOTB_SHARE_DIR)/lib/Makefile +include $(COCOTB_SHARE_DIR)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) + +ifeq ($(COCOTB_INSTALL_METHOD),srctree) + include $(COCOTB_SHARE_DIR)/../../version + export VERSION +else + $(error Unsupported installation method $(COCOTB_INSTALL_METHOD)) +endif + +# Append directory containing the cocotb Python package to PYTHONPATH +# XXX: This is only needed if cocotb is not installed into the default +# Python search path. +PYTHONPATH := $(COCOTB_PY_DIR):$(PYTHONPATH) +export PYTHONPATH + +$(SIM_BUILD): + mkdir -p $@ + +# Regression rule uses Make dependencies to determine whether to run the simulation +.PHONY: regression +regression: results.xml + +# Default sim rule will force a re-run of the simulation (though the cocotb library +# and RTL compilation phases are still evaluated by makefile dependencies) +.PHONY: sim +sim: + -@rm -f results.xml + $(MAKE) results.xml diff --git a/makefiles/simulators/Makefile.aldec b/cocotb/share/makefiles/simulators/Makefile.aldec similarity index 100% rename from makefiles/simulators/Makefile.aldec rename to cocotb/share/makefiles/simulators/Makefile.aldec diff --git a/makefiles/simulators/Makefile.cvc b/cocotb/share/makefiles/simulators/Makefile.cvc similarity index 100% rename from makefiles/simulators/Makefile.cvc rename to cocotb/share/makefiles/simulators/Makefile.cvc diff --git a/makefiles/simulators/Makefile.ghdl b/cocotb/share/makefiles/simulators/Makefile.ghdl similarity index 100% rename from makefiles/simulators/Makefile.ghdl rename to cocotb/share/makefiles/simulators/Makefile.ghdl diff --git a/makefiles/simulators/Makefile.icarus b/cocotb/share/makefiles/simulators/Makefile.icarus similarity index 100% rename from makefiles/simulators/Makefile.icarus rename to cocotb/share/makefiles/simulators/Makefile.icarus diff --git a/makefiles/simulators/Makefile.ius b/cocotb/share/makefiles/simulators/Makefile.ius similarity index 100% rename from makefiles/simulators/Makefile.ius rename to cocotb/share/makefiles/simulators/Makefile.ius diff --git a/makefiles/simulators/Makefile.modelsim b/cocotb/share/makefiles/simulators/Makefile.modelsim similarity index 100% rename from makefiles/simulators/Makefile.modelsim rename to cocotb/share/makefiles/simulators/Makefile.modelsim diff --git a/makefiles/simulators/Makefile.nvc b/cocotb/share/makefiles/simulators/Makefile.nvc similarity index 100% rename from makefiles/simulators/Makefile.nvc rename to cocotb/share/makefiles/simulators/Makefile.nvc diff --git a/makefiles/simulators/Makefile.questa b/cocotb/share/makefiles/simulators/Makefile.questa similarity index 100% rename from makefiles/simulators/Makefile.questa rename to cocotb/share/makefiles/simulators/Makefile.questa diff --git a/makefiles/simulators/Makefile.vcs b/cocotb/share/makefiles/simulators/Makefile.vcs similarity index 100% rename from makefiles/simulators/Makefile.vcs rename to cocotb/share/makefiles/simulators/Makefile.vcs diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index b30fa445..66502440 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -27,79 +27,10 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################## -# Common makefile included by everything +# Backwards-compatibility wrapper for people using +# "include $COCOTB/makefiles/Makefile.inc" in their Makefile. +COCOTB_INSTALL_METHOD = srctree +export COCOTB_INSTALL_METHOD -# Directory containing the cocotb Python module -ifeq ($(COCOTB_PY_DIR),) -export COCOTB_PY_DIR:=$(abspath $(dir $(lastword $(MAKEFILE_LIST)))/..) -endif - -# Directory containing all support files required to build cocotb-based -# simulations: Makefile fragments, and the simulator libraries. -ifeq ($(COCOTB_SHARE_DIR),) -export COCOTB_SHARE_DIR:=$(abspath $(dir $(lastword $(MAKEFILE_LIST)))/../) -endif - -ifeq ($(USER_DIR),) -export USER_DIR:=$(abspath $(dir $(firstword $(MAKEFILE_LIST)))) -endif - -BUILD_DIR=$(USER_DIR)/build -export BUILD_DIR - -ARCH?=$(shell uname -m) -export ARCH - -OS=$(shell uname) -ifneq (, $(findstring MINGW, $(OS))) -OS=Msys -else ifneq (, $(findstring MSYS, $(OS))) -OS=Msys -endif -export OS - -include $(COCOTB_SHARE_DIR)/makefiles/Makefile.paths -include $(COCOTB_SHARE_DIR)/makefiles/Makefile.pylib - -export LIB_DIR=$(BUILD_DIR)/libs/$(ARCH) -export LIB_OBJ_DIR:= $(BUILD_DIR)/obj/$(ARCH) -export INCLUDES := -I$(COCOTB_SHARE_DIR)/include -I$(PYTHON_INCLUDEDIR) - -LIB_EXT := so -PY_EXT := so - -# Base GCC flags -ifeq ($(ARCH),i686) -GXX_ARGS := -m32 -endif - -ifeq ($(OS),Darwin) -GXX_ARGS += -g -DDEBUG -fpic -GCC_ARGS := $(GXX_ARGS) -else ifeq ($(OS),Msys) -GXX_ARGS += -g -DDEBUG -shared -ifeq ($(ARCH),x86_64) -GXX_ARGS += -DMS_WIN64 -endif -GCC_ARGS := $(GXX_ARGS) -LIB_EXT := dll -PY_EXT := pyd -else ifeq ($(OS),Linux) -GXX_ARGS += -Werror -Wcast-qual -Wcast-align -Wwrite-strings \ - -Wall -Wno-unused-parameter \ - -fno-common -g -DDEBUG -fpic -GCC_ARGS = $(GXX_ARGS) -Wstrict-prototypes -Waggregate-return -else -$(error "Unsupported os " $(OS)) -endif - -ifeq ($(OS),Darwin) -SO_LINKER_ARGS := -shared -undefined suppress -flat_namespace -L$(PYTHON_LIBDIR) -else ifeq ($(OS),Linux) -SO_LINKER_ARGS := -shared -Xlinker -L$(PYTHON_LIBDIR) -else ifeq ($(OS),Msys) -SO_LINKER_ARGS := -shared -Wl,-no-undefined -Wl,-enable-runtime-pseudo-reloc-v2 \ - -Wl,--enable-auto-import -L$(PYTHON_LIBDIR) -else -$(error "Unsupported os" $(OS)) -endif +_NEW_SHARE_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))/../cocotb/share +include $(_NEW_SHARE_DIR)/makefiles/Makefile.inc diff --git a/makefiles/Makefile.sim b/makefiles/Makefile.sim index 8d52712b..b0531c33 100644 --- a/makefiles/Makefile.sim +++ b/makefiles/Makefile.sim @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2013, 2018 Potential Ventures Ltd +# Copyright (c) 2013,2018 Potential Ventures Ltd # Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # @@ -25,56 +25,15 @@ # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -# This is a simple wrapper Makefile to pull in the appropriate Makefile for -# the desired simulator and set up common paths for the sims to use to build with - -all: sim - -SIM_BUILD ?= sim_build -export SIM_BUILD - -# Default to Icarus if no simulator is defined -SIM ?= icarus - -# Maintain backwards compatibility by supporting upper and lower case SIM variable -SIM_LOWERCASE := $(shell echo $(SIM) | tr A-Z a-z) - -HAVE_SIMULATOR = $(shell if [ -f $(COCOTB_SHARE_DIR)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) ]; then echo 1; else echo 0; fi;) -AVAILABLE_SIMULATORS = $(patsubst .%,%,$(suffix $(wildcard $(COCOTB_SHARE_DIR)/makefiles/simulators/Makefile.*))) - -ifeq ($(HAVE_SIMULATOR),0) -$(error "Couldn't find makefile for simulator: "$(SIM_LOWERCASE)"! Available simulators: $(AVAILABLE_SIMULATORS)") -endif - -# Depend on all Python from the cocotb package. This triggers a -# recompilation of the simulation if cocotb is updated. -CUSTOM_SIM_DEPS += $(shell find $(COCOTB_PY_DIR)/cocotb/ -name "*.py") - -include $(COCOTB_SHARE_DIR)/lib/Makefile -include $(COCOTB_SHARE_DIR)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) - -# XXX: This assumes COCOTB_SHARE_DIR == root dir of the cocotb src tree -include $(COCOTB_SHARE_DIR)/version -export VERSION - -# Append directory containing the cocotb Python package to PYTHONPATH -# XXX: This is only needed if cocotb is not installed into the default -# Python search path. -PYTHONPATH := $(COCOTB_PY_DIR):$(PYTHONPATH) -export PYTHONPATH +############################################################################## -$(SIM_BUILD): - mkdir -p $@ +# Backwards-compatibility wrapper for people using +# "include $COCOTB/makefiles/Makefile.sim" in their Makefile. -# Regression rule uses Make dependencies to determine whether to run the simulation -.PHONY: regression -regression: results.xml +# COCOTB_INSTALL_METHOD should already have been set in Makefile.inc, but +# better be safe than sorry to cover unusual ways of people using cocotb. +COCOTB_INSTALL_METHOD = srctree +export COCOTB_INSTALL_METHOD -# Default sim rule will force a re-run of the simulation (though the cocotb library -# and RTL compilation phases are still evaluated by makefile dependencies) -.PHONY: sim -sim: - -@rm -f results.xml - $(MAKE) results.xml +_NEW_SHARE_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))/../cocotb/share +include $(_NEW_SHARE_DIR)/makefiles/Makefile.sim From 1b516dba0f7c0f40f3032afc7da8ccde2edb8c5c Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 16 Jan 2019 01:36:04 +0100 Subject: [PATCH 1541/2656] More doc improvements. --- cocotb/clock.py | 2 +- cocotb/drivers/__init__.py | 33 ++++++-- cocotb/monitors/__init__.py | 2 +- cocotb/triggers.py | 13 ++- cocotb/utils.py | 9 +- documentation/source/examples.rst | 84 +++++++++++++++++-- documentation/source/introduction.rst | 2 +- documentation/source/library_reference.rst | 16 ++-- documentation/source/simulator_support.rst | 1 + examples/axi_lite_slave/README.md | 18 ++-- examples/axi_lite_slave/hdl/axi_lite_demo.v | 2 +- .../tests/test_axi_lite_slave.py | 81 ++++++++---------- examples/dff/tests/dff_cocotb.py | 29 +++---- examples/mean/tests/test_mean.py | 15 ++-- .../tests/test_mixed_language.py | 8 +- 15 files changed, 195 insertions(+), 120 deletions(-) diff --git a/cocotb/clock.py b/cocotb/clock.py index 504a8508..5fa1b32b 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -51,7 +51,7 @@ def __init__(self, signal): class Clock(BaseClock): """Simple 50:50 duty cycle clock driver. - Instances of this class should call its :any:`start` method and fork the + Instances of this class should call its :meth:`start` method and fork the result. This will create a clocking thread that drives the signal at the desired period/frequency. diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index bba0e8d2..ca23d0b3 100755 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -52,9 +52,15 @@ def __init__(self, signal, clk, generator=None): self._generator = generator def start(self, generator=None): + """Start generating data. + + Args: + generator (optional): Generator yielding data. + """ self._cr = cocotb.fork(self._cr_twiddler(generator=generator)) def stop(self): + """Stop generating data.""" self._cr.kill() @cocotb.coroutine @@ -96,6 +102,7 @@ def __init__(self): self._thread = cocotb.scheduler.add(self._send_thread()) def kill(self): + """Kill the coroutine sending stuff.""" if self._thread: self._thread.kill() self._thread = None @@ -130,7 +137,7 @@ def send(self, transaction, sync=True, **kwargs): Args: transaction (any): The transaction to be sent. - sync (bool, optional): Synchronise the transfer by waiting for rising edge. + sync (bool, optional): Synchronise the transfer by waiting for a rising edge. **kwargs (dict): Additional arguments used in child class' :any:`_driver_send` method. """ @@ -144,7 +151,7 @@ def _driver_send(self, transaction, sync=True, **kwargs): Args: transaction (any): The transaction to be sent. - sync (boolean, optional): Synchronise the transfer by waiting for rising edge. + sync (boolean, optional): Synchronise the transfer by waiting for a rising edge. **kwargs: Additional arguments if required for protocol implemented in subclass. """ raise NotImplementedError("Subclasses of Driver should define a " @@ -159,7 +166,7 @@ def _send(self, transaction, callback, event, sync=True, **kwargs): callback (callable, optional): Optional function to be called when the transaction has been sent. event (optional): event to be set when the transaction has been sent. - sync (boolean, optional): Synchronise the transfer by waiting for rising edge. + sync (boolean, optional): Synchronise the transfer by waiting for a rising edge. **kwargs: Any additional arguments used in child class' :any:`_driver_send` method. """ @@ -226,15 +233,22 @@ def __init__(self, entity, name, clock, **kwargs): @coroutine def _driver_send(self, transaction, sync=True): + """Implementation for BusDriver. + + Args: + transaction: The transaction to send. + sync (bool, optional): Synchronise the transfer by waiting for a rising edge. + """ if sync: yield RisingEdge(self.clock) self.bus <= transaction @coroutine def _wait_for_signal(self, signal): - """This method will return with the specified signal - has hit logic ``1``. The state will be in the :any:`ReadOnly` phase - so sim will need to move to :any:`NextTimeStep` before + """This method will return when the specified signal + has hit logic ``1``. The state will be in the + :class:`~cocotb.triggers.ReadOnly` phase so sim will need + to move to :class:`~cocotb.triggers.NextTimeStep` before registering more callbacks can occur. """ yield ReadOnly() @@ -245,9 +259,10 @@ def _wait_for_signal(self, signal): @coroutine def _wait_for_nsignal(self, signal): - """This method will return with the specified signal - has hit logic ``0``. The state will be in the :any:`ReadOnly` phase - so sim will need to move to :any:`NextTimeStep` before + """This method will return when the specified signal + has hit logic ``0``. The state will be in the + :class:`~cocotb.triggers.ReadOnly` phase so sim will need + to move to :class:`~cocotb.triggers.NextTimeStep` before registering more callbacks can occur. """ yield ReadOnly() diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index 9f923960..f7680e4e 100755 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -54,7 +54,7 @@ def __init__(self): class Monitor(object): """Base class for Monitor objects. - Monitors are passive 'listening' objects that monitor pins in or out of a DUT. + Monitors are passive 'listening' objects that monitor pins going in or out of a DUT. This class should not be used directly, but should be subclassed and the internal :any:`_monitor_recv` method should be overridden and decorated as a :any:`coroutine`. This :any:`_monitor_recv` diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 50c4b687..03ed2b6a 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -55,15 +55,16 @@ def __init__(self): self.primed = False def prime(self, *args): + """FIXME: document""" self.primed = True def unprime(self): - """Remove any pending callbacks if necessary""" + """Remove any pending callbacks if necessary.""" self.primed = False def __del__(self): """Ensure if a trigger drops out of scope we remove any pending - callbacks""" + callbacks.""" self.unprime() def __str__(self): @@ -147,6 +148,7 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): + """FIXME: document""" if self.cbhdl == 0: self.cbhdl = simulator.register_readonly_callback(callback, self) if self.cbhdl == 0: @@ -170,6 +172,7 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): + """FIXME: document""" if self.cbhdl == 0: # import pdb # pdb.set_trace() @@ -264,6 +267,7 @@ def __init__(self, signal, num_cycles, rising=True): self._rising = 2 def prime(self, callback): + """FIXME: document""" self._callback = callback def _check(obj): @@ -330,6 +334,7 @@ def _check_all_fired(self, trigger): self._callback(self) def unprime(self): + """FIXME: document""" for trigger in self._triggers: trigger.unprime() @@ -367,6 +372,7 @@ def __init__(self, name=""): self.data = None def prime(self, callback, trigger): + """FIXME: document""" self._pending.append(trigger) Trigger.prime(self) @@ -455,6 +461,7 @@ def acquire(self): return trig def release(self): + """Release the lock.""" if not self.locked: raise_error(self, "Attempt to release an unacquired Lock %s" % (str(self))) @@ -512,9 +519,11 @@ def _outcome(self): @property def retval(self): + """FIXME: document""" return self._coroutine.retval def prime(self, callback): + """FIXME: document""" if self._coroutine._finished: callback(self) else: diff --git a/cocotb/utils.py b/cocotb/utils.py index c8f2678d..5d69e383 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -1,6 +1,6 @@ from __future__ import print_function -# Copyright (c) 2013, 2018 Potential Ventures Ltd +# Copyright (c) 2013 Potential Ventures Ltd # Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. # @@ -495,9 +495,10 @@ class nullcontext(object): """Context manager that does no additional processing. Used as a stand-in for a normal context manager, when a particular block of code is only sometimes used with a normal context manager: - cm = optional_cm if condition else nullcontext() - with cm: - # Perform operation, using optional_cm if condition is True + + >>> cm = optional_cm if condition else nullcontext() + >>> with cm: + >>> # Perform operation, using optional_cm if condition is True """ def __init__(self, enter_result=None): diff --git a/documentation/source/examples.rst b/documentation/source/examples.rst index 60220311..8368bfe9 100644 --- a/documentation/source/examples.rst +++ b/documentation/source/examples.rst @@ -1,11 +1,85 @@ -######## -Examples -######## +############# +More Examples +############# -These code samples show some typical usage of cocotb based on real-world problems. +Apart from the examples covered with full tutorials in the previous sections, +the directory :file:`cocotb/examples/` contains some more smaller modules you may want to take a look at. -Example testbench for snipped of code from `comp.lang.verilog `_: +Adder +===== + +The directory :file:`cocotb/examples/adder/` contains an ``adder`` RTL in both Verilog and VHDL, +an ``adder_model`` implemented in Python, +and the cocotb testbench with two defined tests ­ a simple :func:`adder_basic_test` and +a slightly more advanced :func:`adder_randomised_test`. + +This example does not use any :class:`.Driver`, :class:`.Monitor`, or :class:`.Scoreboard`; not even a clock. + + +D Flip-Flop +=========== + +The directory :file:`cocotb/examples/dff/` contains a simple D flip-flop, implemented in both VDHL and Verilog. + +The HDL has the data input port ``d``, the clock port ``c``, and the data output ``q`` with an initial state of ``0``. +No reset port exists. + +The cocotb testbench checks the initial state first, then applies random data to the data input. +The flip-flop output is captured at each rising edge of the clock and compared to the applied input data using a :class:`.Scoreboard`. + +The testbench defines a ``BitMonitor`` (a subclass of :class:`.Monitor`) as a pendant to the cocotb-provided :class:`.BitDriver`. +The :class:`.BitDriver`'s :meth:`~.BitDriver.start` and :meth:`~.BitDriver.stop` methods are used +to start and stop generation of input data. + +A :class:`.TestFactory` is used to generate the random tests. + + +Mean +==== + +The directory :file:`cocotb/examples/mean/` contains a module that calculates the mean value of a +data input bus ``i`` (with signals ``i_data`` and ``i_valid``) and +outputs it on ``o`` (with ``i_data`` and ``o_valid``). + +It has implementations in both VHDL and SystemVerilog. + +The testbench defines a ``StreamBusMonitor`` (a subclass of :class:`.BusMonitor`), a clock generator, +a ``value_test`` helper coroutine and a few tests. +Test ``mean_randomised_test`` uses the ``StreamBusMonitor`` to +feed a :class:`.Scoreboard` with the collected transactions on input bus ``i``. + +Mixed Language +============== + +The directory :file:`cocotb/examples/mixed_language/` contains two toplevel HDL files, +one in VHDL, one in SystemVerilog, that each instantiate the ``endian_swapper`` in +SystemVerilog and VHDL in parallel and chains them together so that the endianness is swapped twice. + +Thus, we end up with SystemVerilog+VHDL instantiated in VHDL and +SystemVerilog+VHDL instantiated in SystemVerilog. + +The cocotb testbench pulls the reset on both instances and checks that they behave the same. + +.. todo:: + + This example is not complete. + + +AXI Lite Slave +============== + +The directory :file:`cocotb/examples/axi_lite_slave/` contains ... + +.. todo:: + + Write documentation, see README.md + + +Sorter +====== + +Example testbench for snippet of code from `comp.lang.verilog `_: .. code-block:: python diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst index 80cb8da6..681654ad 100644 --- a/documentation/source/introduction.rst +++ b/documentation/source/introduction.rst @@ -81,5 +81,5 @@ and contributions from Gordon McGregor and Finn Grimwood (see `contributers `_ for the full list of contributions). We also have a list of talks and papers, libraries and examples at our wiki page -`Further Resources https://github.com/potentialventures/cocotb/wiki/Further-Resources>`_. +`Further Resources `_. Feel free to add links to cocotb-related content that we are still missing! diff --git a/documentation/source/library_reference.rst b/documentation/source/library_reference.rst index ad41d374..5f2cf794 100644 --- a/documentation/source/library_reference.rst +++ b/documentation/source/library_reference.rst @@ -83,6 +83,10 @@ Signal related Python Triggers ~~~~~~~~~~~~~~~ +.. autoclass:: cocotb.triggers.Combine + :members: + :member-order: bysource + .. autoclass:: cocotb.triggers.Event :members: :member-order: bysource @@ -130,6 +134,7 @@ Monitor :private-members: .. automethod:: wait_for_recv(timeout=None) + .. autoclass:: BusMonitor :members: @@ -194,6 +199,7 @@ Analog Devices AD9361 RF Transceiver. .. automethod:: rx_data_to_ad9361(i_data, q_data, i_data2=None, q_data2=None, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) .. automethod:: ad9361_tx_to_rx_loopback() .. automethod:: tx_data_from_ad9361() + AMBA ~~~~ @@ -207,14 +213,10 @@ Advanced Microcontroller Bus Architecture. .. automethod:: write(address, value, byte_enable=0xf, address_latency=0, data_latency=0) .. automethod:: read(address, sync=True) - :members: - :member-order: bysource - :show-inheritance: .. autoclass:: AXI4Slave :members: :member-order: bysource - :show-inheritance: Avalon @@ -232,9 +234,6 @@ Avalon .. automethod:: write(address, value) .. automethod:: read(address, sync=True) - :members: - :member-order: bysource - :show-inheritance: .. autoclass:: AvalonMemory :members: @@ -262,9 +261,6 @@ OPB .. automethod:: write(address, value, sync=True) .. automethod:: read(address, sync=True) - :members: - :member-order: bysource - :show-inheritance: XGMII ~~~~~ diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 55b92e02..f87af8d4 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -45,3 +45,4 @@ Cadence Incisive, Cadence Xcelium GHDL ---- Support is preliminary. +Noteworthy is that despite GHDL being a VHDL simulator, it implements the VPI interface. diff --git a/examples/axi_lite_slave/README.md b/examples/axi_lite_slave/README.md index feca064b..3ee88a3a 100644 --- a/examples/axi_lite_slave/README.md +++ b/examples/axi_lite_slave/README.md @@ -3,25 +3,25 @@ Description: In order to simplify AXI Lite Transactions we use an module that translates AXI Lite Slave transactions to a simple register read/write. -There are other projects that translate AXI Slave signals to wishbone. +There are other projects that translate AXI Slave signals to Wishbone. ## Source Files -* tb\_axi\_lite\_slave: Testbench interface, primarily used to translate - the cocotb AXI Lite bus signals to the signals within axi_lite_demo core +* ``tb_axi_lite_slave``: Testbench interface, primarily used to translate + the cocotb AXI Lite bus signals to the signals within ``axi_lite_demo`` core. -* axi\_lite\_demo: demonstration module, anyone who wishes to interact +* ``axi_lite_demo``: demonstration module, anyone who wishes to interact with an AXI Lite slave core can use this as a template. -* axi\_lite\_slave: Performs all the low level AXI Translactions. +* ``axi_lite_slave``: Performs all the low level AXI Translactions. * Addresses and data sent from the master are decoded from the AXI Bus and - sent to the user with a simple 'ready', 'acknowledge' handshake. + sent to the user with a simple ``ready``, ``acknowledge`` handshake. * Address and data are read from the slave using a simple - 'request', 'ready' handshake. + ``request``, ``ready`` handshake. ## NOTE: Test ID If you use a logic analyzer wave viewer it's hard to determine which test is -currently running so I added a value called 'test\_id' in the test bench -so when viewing the waveforms the individual tests can easily be identified +currently running so there is a value called ``test_id`` in the test bench +so when viewing the waveforms the individual tests can easily be identified. diff --git a/examples/axi_lite_slave/hdl/axi_lite_demo.v b/examples/axi_lite_slave/hdl/axi_lite_demo.v index 0f8ba19d..5a591dab 100644 --- a/examples/axi_lite_slave/hdl/axi_lite_demo.v +++ b/examples/axi_lite_slave/hdl/axi_lite_demo.v @@ -73,7 +73,7 @@ localparam ADDR_1 = 1; localparam MAX_ADDR = ADDR_1; -//registes/wires +//registers/wires //User Interface wire [ADDR_WIDTH - 1: 0] w_reg_address; diff --git a/examples/axi_lite_slave/tests/test_axi_lite_slave.py b/examples/axi_lite_slave/tests/test_axi_lite_slave.py index d317ceef..f7d25c62 100644 --- a/examples/axi_lite_slave/tests/test_axi_lite_slave.py +++ b/examples/axi_lite_slave/tests/test_axi_lite_slave.py @@ -19,22 +19,19 @@ def setup_dut(dut): cocotb.fork(Clock(dut.clk, CLK_PERIOD).start()) -#Write to address 0 and verify that value got through +# Write to address 0 and verify that value got through @cocotb.test(skip = False) def write_address_0(dut): - """ - Description: - Write to the register at address 0 - verify the value has changed + """Write to the register at address 0, verify the value has changed. Test ID: 0 Expected Results: The value read directly from the register is the same as the - value written + value written. """ - #Reset + # Reset dut.rst <= 1 dut.test_id <= 0 axim = AXI4LiteMaster(dut, "AXIML", dut.clk) @@ -50,28 +47,26 @@ def write_address_0(dut): value = dut.dut.r_temp_0 if value != DATA: - #Fail + # Fail raise TestFailure("Register at address 0x%08X should have been: \ 0x%08X but was 0x%08X" % (ADDRESS, DATA, int(value))) - dut.log.info("Write 0x%08X to addres 0x%08X" % (int(value), ADDRESS)) + dut.log.info("Write 0x%08X to address 0x%08X" % (int(value), ADDRESS)) -#Read back a value at address 0x01 +# Read back a value at address 0x01 @cocotb.test(skip = False) def read_address_1(dut): - """ - Description - Use cocotb to set the value of the register at address 0x01 - Use AXIML to read the contents of that register and - compare the values + """Use cocotb to set the value of the register at address 0x01. + Use AXIML to read the contents of that register and + compare the values. Test ID: 1 Expected Results: - The value read from the register is the same as the value written + The value read from the register is the same as the value written. """ - #Reset + # Reset dut.rst <= 1 dut.test_id <= 1 axim = AXI4LiteMaster(dut, "AXIML", dut.clk) @@ -89,7 +84,7 @@ def read_address_1(dut): yield Timer(CLK_PERIOD * 10) if value != DATA: - #Fail + # Fail raise TestFailure("Register at address 0x%08X should have been: \ 0x%08X but was 0x%08X" % (ADDRESS, DATA, int(value))) @@ -99,18 +94,16 @@ def read_address_1(dut): @cocotb.test(skip = False) def write_and_read(dut): - """ - Description: - Write to the register at address 0 - read back from that register and verify the value is the same + """Write to the register at address 0. + Read back from that register and verify the value is the same. Test ID: 2 Expected Results: - The contents of the register is the same as the value written + The contents of the register is the same as the value written. """ - #Reset + # Reset dut.rst <= 1 dut.test_id <= 2 axim = AXI4LiteMaster(dut, "AXIML", dut.clk) @@ -121,36 +114,34 @@ def write_and_read(dut): ADDRESS = 0x00 DATA = 0xAB - #Write to the register + # Write to the register yield axim.write(ADDRESS, DATA) yield Timer(CLK_PERIOD * 10) - #Read back the value + # Read back the value value = yield axim.read(ADDRESS) yield Timer(CLK_PERIOD * 10) value = dut.dut.r_temp_0 if value != DATA: - #Fail + # Fail raise TestFailure("Register at address 0x%08X should have been: \ 0x%08X but was 0x%08X" % (ADDRESS, DATA, int(value))) - dut._log.info("Write 0x%08X to addres 0x%08X" % (int(value), ADDRESS)) + dut._log.info("Write 0x%08X to address 0x%08X" % (int(value), ADDRESS)) @cocotb.test(skip = False) def write_fail(dut): - """ - Description: - Attemp to write data to an address that doesn't exist. This test - should fail + """Attempt to write data to an address that doesn't exist. This test + should fail. Test ID: 3 Expected Results: The AXIML bus should throw an exception because the user attempted - to write to an invalid address + to write to an invalid address. """ - #Reset + # Reset dut.rst <= 1 dut.test_id <= 3 axim = AXI4LiteMaster(dut, "AXIML", dut.clk) @@ -166,26 +157,24 @@ def write_fail(dut): yield Timer(CLK_PERIOD * 10) except AXIProtocolError as e: print("Exception: %s" % str(e)) - dut._log.info("Bus Successfully Raised an Error") + dut._log.info("Bus successfully raised an error") raise TestSuccess() - raise TestFailure("AXI Bus Should have raised an ERROR when writing to \ - the wrong bus") + raise TestFailure("AXI bus should have raised an error when writing to \ + an invalid address") @cocotb.test(skip = False) def read_fail(dut): - """ - Description: - Attemp to write data to an address that doesn't exist. This test - should fail + """Attempt to read data from an address that doesn't exist. This test + should fail. Test ID: 4 Expected Results: The AXIML bus should throw an exception because the user attempted - to write to an invalid address + to read from an invalid address. """ - #Reset - dut.rst <= 1 + # Reset + dut.rst <= 1 dut.test_id <= 4 axim = AXI4LiteMaster(dut, "AXIML", dut.clk) setup_dut(dut) @@ -202,5 +191,5 @@ def read_fail(dut): print("Exception: %s" % str(e)) dut._log.info("Bus Successfully Raised an Error") raise TestSuccess() - raise TestFailure("AXI Bus Should have raised an ERROR when writing to \ - the wrong bus") + raise TestFailure("AXI bus should have raised an error when reading from \ + an invalid address") diff --git a/examples/dff/tests/dff_cocotb.py b/examples/dff/tests/dff_cocotb.py index 48395fb1..b1c6c848 100644 --- a/examples/dff/tests/dff_cocotb.py +++ b/examples/dff/tests/dff_cocotb.py @@ -28,6 +28,7 @@ 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 @@ -39,7 +40,7 @@ # ============================================================================== class BitMonitor(Monitor): - """Observes single input or output of DUT.""" + """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 @@ -69,7 +70,7 @@ def __init__(self, dut, init_val): Setup testbench. init_val signifies the BinaryValue which must be captured by the - output monitor with the first risign edge. This is actually the initial + output monitor with the first rising clock edge. This is actually the initial state of the flip-flop. """ # Some internal state @@ -102,31 +103,23 @@ def start(self): self.input_drv.start() def stop(self): - """ - Stop generation of input data. + """Stop generation of input data. Also stop generation of expected output transactions. - One more clock cycle must be executed afterwards, so that, output of + 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): - """Generate the clock signal.""" - while True: - signal <= 0 - yield Timer(5000) # ps - signal <= 1 - yield Timer(5000) # ps - # ============================================================================== @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)) + + cocotb.fork(Clock(dut.c, 5000).start()) + + tb = DFF_TB(dut, BinaryValue(0, 1)) + clkedge = RisingEdge(dut.c) # Apply random input data by input_gen via BitDriver for 100 clock cycle. @@ -143,6 +136,6 @@ def run_test(dut): raise tb.scoreboard.result # ============================================================================== -# Register test. +# Register the test. factory = TestFactory(run_test) factory.generate_tests() diff --git a/examples/mean/tests/test_mean.py b/examples/mean/tests/test_mean.py index 9f580695..067a04a0 100644 --- a/examples/mean/tests/test_mean.py +++ b/examples/mean/tests/test_mean.py @@ -13,14 +13,13 @@ class StreamBusMonitor(BusMonitor): - """ - streaming bus monitor - """ + """Streaming bus monitor.""" + _signals = ["valid", "data"] @cocotb.coroutine def _monitor_recv(self): - """Watch the pins and reconstruct transactions""" + """Watch the pins and reconstruct transactions.""" while True: yield RisingEdge(self.clock) @@ -40,7 +39,7 @@ def clock_gen(signal, period=10000): @cocotb.coroutine def value_test(dut, num): - """ Test n*num/n = num """ + """Test n*num/n = num""" data_width = int(dut.DATA_WIDTH.value) bus_width = int(dut.BUS_WIDTH.value) @@ -72,20 +71,20 @@ def value_test(dut, num): @cocotb.test() def mean_basic_test(dut): - """ Test n*5/n = 5 """ + """Test n*5/n = 5""" yield value_test(dut, 5) @cocotb.test() def mean_overflow_test(dut): - """ Test for overflow n*max_val/n = max_val """ + """Test for overflow n*max_val/n = max_val""" data_width = int(dut.DATA_WIDTH.value) yield value_test(dut, 2**data_width - 1) @cocotb.test() def mean_randomised_test(dut): - """ Test mean of random numbers multiple times """ + """Test mean of random numbers multiple times""" # dut_in = StreamBusMonitor(dut, "i", dut.clk) # this doesn't work: # VPI Error vpi_get_value(): diff --git a/examples/mixed_language/tests/test_mixed_language.py b/examples/mixed_language/tests/test_mixed_language.py index 2201581a..a0f72b78 100644 --- a/examples/mixed_language/tests/test_mixed_language.py +++ b/examples/mixed_language/tests/test_mixed_language.py @@ -5,9 +5,7 @@ @cocotb.test() def mixed_language_test(dut): - """ - Try accessing handles and setting values in a mixed language environment - """ + """Try accessing handles and setting values in a mixed language environment.""" yield Timer(100) verilog = dut.i_swapper_sv @@ -16,10 +14,10 @@ def mixed_language_test(dut): vhdl = dut.i_swapper_vhdl dut._log.info("Got: %s" % repr(vhdl._name)) - verilog.reset_n = 1 + verilog.reset_n <= 1 yield Timer(100) - vhdl.reset_n = 1 + vhdl.reset_n <= 1 yield Timer(100) if int(verilog.reset_n) == int(vhdl.reset_n): From 5f74665fca909ea6ec8b4ab4d3108df12d5f286d Mon Sep 17 00:00:00 2001 From: Ben Rosser Date: Tue, 19 Feb 2019 12:27:41 -0500 Subject: [PATCH 1542/2656] Update cocotb docs to indicate that Python 2.6 is no longer supported We no longer support Python 2.6-- various backwards-compatibility-breaking changes (such as 2.7+ string formatting styles) have been merged over the past year or so. Thus, the documentation should be updated. --- documentation/source/quickstart.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index a84b434d..6fbfcd94 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -11,7 +11,7 @@ Pre-requisites Cocotb has the following requirements: -* Python 2.6+ +* Python 2.7+ * Python-dev packages * GCC and associated development packages * GNU Make From cb2495d0562b9f31f7ec64fbf1abb44332fd8621 Mon Sep 17 00:00:00 2001 From: LANCE E EFTINK Date: Wed, 20 Feb 2019 17:35:42 -0700 Subject: [PATCH 1543/2656] Issue #822: Fix errors in the test for signals that are only defined in verilog and not vhdl --- tests/test_cases/test_array/test_array.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py index 0eb412b9..4e5ca63c 100644 --- a/tests/test_cases/test_array/test_array.py +++ b/tests/test_cases/test_array/test_array.py @@ -263,8 +263,8 @@ def test_discover_all(dut): 149 (sig_t4[0:3][7:4][7:0]) 112 (sig_t5[0:2][0:3][7:0]) 57 (sig_t6[0:1][2:4][7:0]) - 149 (sig_t7[3:0][3:0]) - 149 ([3:0][3:0]sig_t8) + 149 (sig_t7[3:0][3:0]) (VPI Only) + 149 ([3:0][3:0]sig_t8) (VPI Only) 1 (sig_logic) 9 (sig_logic_vec) 1 (sig_bool) (VHDL Only) @@ -286,8 +286,8 @@ def test_discover_all(dut): 8 (desc_gen: process "always") (VPI - Aldec only) process: 1 ("always") (VPI - Aldec only) - TOTAL: 1154 (VHDL - Default) - 818 (VHDL - Aldec) + TOTAL: 856 (VHDL - Default) + 818 (VHDL - Aldec) 1078 (Verilog - Default) 947/1038 (Verilog - Aldec) """ @@ -320,9 +320,9 @@ def test_discover_all(dut): dummy = hdl.sig if cocotb.LANGUAGE in ["vhdl"] and cocotb.SIM_NAME.lower().startswith(("riviera")): - pass_total = 1116 + pass_total = 818 elif cocotb.LANGUAGE in ["vhdl"]: - pass_total = 1154 + pass_total = 856 elif cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")): if cocotb.SIM_VERSION.startswith(("2017.10.61")): pass_total = 803 @@ -448,10 +448,12 @@ def test_direct_signal_indexing(dut): _check_type(tlog, dut.sig_t6[0][3], ModifiableObject) _check_type(tlog, dut.sig_t6[0][3][7], ModifiableObject) _check_type(tlog, dut.sig_cmplx, NonHierarchyIndexableObject) - _check_type(tlog, dut.sig_t7[1], NonHierarchyIndexableObject) - _check_type(tlog, dut.sig_t7[0][3], ModifiableObject) - _check_type(tlog, dut.sig_t8[1], NonHierarchyIndexableObject) - _check_type(tlog, dut.sig_t8[0][3], ModifiableObject) + + if cocotb.LANGUAGE in ["verilog"]: + _check_type(tlog, dut.sig_t7[1], NonHierarchyIndexableObject) + _check_type(tlog, dut.sig_t7[0][3], ModifiableObject) + _check_type(tlog, dut.sig_t8[1], NonHierarchyIndexableObject) + _check_type(tlog, dut.sig_t8[0][3], ModifiableObject) # Riviera has a bug and finds dut.sig_cmplx[1], but the type returned is a vpiBitVar From 4772a566c5849d0491bd28d592da75c7400eaee6 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Tue, 5 Feb 2019 14:33:01 +0000 Subject: [PATCH 1544/2656] Add cocotb-config script to give install paths Users of cocotb need to have a way to find the cocotb installation directories containing the Makefiles, C library files, and other files. For this purpose we add a small script which should be added to PATH and returns the requested path information. This script follows in name and style other *-config scripts, the most well-known and generic one being pkg-config of course. --- cocotb/config.py | 68 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100755 cocotb/config.py diff --git a/cocotb/config.py b/cocotb/config.py new file mode 100755 index 00000000..4fa21e7d --- /dev/null +++ b/cocotb/config.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +import os +import sys +import cocotb +import argparse +import pkg_resources + + +class PrintAction(argparse.Action): + def __init__(self, option_strings, dest, text=None, **kwargs): + super(PrintAction, self).__init__(option_strings, dest, nargs=0, **kwargs) + self.text = text + + def __call__(self, parser, namespace, values, option_string=None): + print(self.text) + parser.exit() + +def main(): + + share_dir = os.path.join(os.path.dirname(cocotb.__file__),'share') + prefix_dir = os.path.dirname(os.path.dirname(cocotb.__file__)) + makefiles_dir = os.path.join(os.path.dirname(cocotb.__file__),'share', 'makefiles') + version = pkg_resources.get_distribution('cocotb').version + + parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter) + parser.add_argument('--prefix', help='echos the package-prefix of cocotb', action=PrintAction, text=prefix_dir) + parser.add_argument('--share', help='echos the package-share of cocotb', action=PrintAction, text=share_dir) + parser.add_argument('--makefiles', help='echos the package-makefiles of cocotb', action=PrintAction, text=makefiles_dir) + parser.add_argument('-v', '--version', help='echos version of cocotb', action=PrintAction, text=version) + + if len(sys.argv)==1: + parser.print_help(sys.stderr) + sys.exit(1) + + args = parser.parse_args() + +if __name__ == "__main__": + main() + From 4d51b1c891dda7339b71bfa20c3a6cdff881e80b Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Tue, 5 Feb 2019 14:41:33 +0000 Subject: [PATCH 1545/2656] Add Python packaging to cocotb This patch adds a setup.py file creating a Python package for cocotb. Most files are auto-declared as dependency and hence don't need to be present in MANIFEST.in. If cocotb is installed from a Python package, the VERSION environment variable, containing the cocotb version, is now read from the installed package and no longer from the version file in cocotb's top-level source directory. If cocotb is used from the source tree, the VERSION variable remains to be read from the srcroot/version file as before. --- MANIFEST.in | 11 +---- cocotb/share/makefiles/Makefile.sim | 6 ++- setup.py | 68 +++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 11 deletions(-) create mode 100644 setup.py diff --git a/MANIFEST.in b/MANIFEST.in index 75c6c5c8..71584113 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,10 +1,3 @@ -recursive-include bin * -recursive-include cocotb *.py *.c Makefile -recursive-include gpi *.c *.h Makefile -recursive-include vpi_shim *.c Makefile +recursive-include cocotb/share *.c* *.h Makefile.* Makefile recursive-include examples *.v *.py Makefile *.tcl *.cfg -recursive-include scripts * -recursive-include simulator *.c *.h Makefile -recursive-include modules *.py -recursive-include makefiles Makefile.* -include Makefile README.md +include version diff --git a/cocotb/share/makefiles/Makefile.sim b/cocotb/share/makefiles/Makefile.sim index ad5fdc08..900591e1 100644 --- a/cocotb/share/makefiles/Makefile.sim +++ b/cocotb/share/makefiles/Makefile.sim @@ -56,11 +56,13 @@ include $(COCOTB_SHARE_DIR)/lib/Makefile include $(COCOTB_SHARE_DIR)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) ifeq ($(COCOTB_INSTALL_METHOD),srctree) + # cocotb is used from the source tree include $(COCOTB_SHARE_DIR)/../../version - export VERSION else - $(error Unsupported installation method $(COCOTB_INSTALL_METHOD)) + # cocotb has been installed from a Python package + VERSION=$(shell $(PYTHON_BIN) -c 'import pkg_resources; print(pkg_resources.get_distribution("cocotb").version)') endif +export VERSION # Append directory containing the cocotb Python package to PYTHONPATH # XXX: This is only needed if cocotb is not installed into the default diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..c4646bad --- /dev/null +++ b/setup.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +from setuptools import setup +from setuptools import find_packages +from os import path, walk + +def read_file(fname): + return open(path.join(path.dirname(__file__), fname)).read() + +def package_files(directory): + paths = [] + for (fpath, directories, filenames) in walk(directory): + for filename in filenames: + paths.append(path.join('..', fpath, filename)) + return paths + +version = read_file('version')[8:].strip() + +setup( + name='cocotb', + version=version, + description='cocotb is a coroutine based cosimulation library for writing VHDL and Verilog testbenches in Python.', + url='https://github.com/potentialventures/cocotb', + license='BSD', + long_description=read_file('README.md'), + long_description_content_type='text/markdown', + author='Chris Higgs, Stuart Hodgson', + author_email='cocotb@potentialventures.com', + install_requires=[], + packages=find_packages(), + include_package_data=True, + package_data={'cocotb': package_files('cocotb/share')}, + entry_points={ + 'console_scripts': [ + 'cocotb-config=cocotb.config:main', + ] + }, + platforms='any', +) + From 03ff423d23e224d34a4a5ff588c752ff7a2c2bd2 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Sun, 3 Feb 2019 19:59:00 +0000 Subject: [PATCH 1546/2656] Convert examples to use cocotb-config --- examples/Makefile | 2 -- examples/adder/tests/Makefile | 5 ++--- examples/axi_lite_slave/tests/Makefile | 5 ++--- examples/dff/tests/Makefile | 5 ++--- examples/endian_swapper/tests/Makefile | 5 ++--- examples/mean/tests/Makefile | 5 ++--- examples/mixed_language/tests/Makefile | 6 ++---- examples/ping_tun_tap/tests/Makefile | 5 ++--- 8 files changed, 14 insertions(+), 24 deletions(-) mode change 100644 => 100755 examples/dff/tests/Makefile diff --git a/examples/Makefile b/examples/Makefile index a753539f..5bc1c78c 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -27,8 +27,6 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -COCOTB?=$(shell pwd)/../ -export COCOTB EXAMPLES := adder/tests \ endian_swapper/tests \ diff --git a/examples/adder/tests/Makefile b/examples/adder/tests/Makefile index 507e8c9e..4d5f7552 100644 --- a/examples/adder/tests/Makefile +++ b/examples/adder/tests/Makefile @@ -30,7 +30,6 @@ TOPLEVEL_LANG ?= verilog PWD=$(shell pwd) -COCOTB=$(PWD)/../../.. ifeq ($(OS),Msys) WPWD=$(shell sh -c 'pwd -W') @@ -51,5 +50,5 @@ endif TOPLEVEL := adder MODULE := test_adder -include $(COCOTB)/makefiles/Makefile.inc -include $(COCOTB)/makefiles/Makefile.sim +include $(shell cocotb-config --makefiles)/Makefile.inc +include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/examples/axi_lite_slave/tests/Makefile b/examples/axi_lite_slave/tests/Makefile index 84a1110c..e4c9c8cf 100644 --- a/examples/axi_lite_slave/tests/Makefile +++ b/examples/axi_lite_slave/tests/Makefile @@ -2,7 +2,6 @@ TOPLEVEL_LANG ?= verilog PWD=$(shell pwd) TOPDIR=$(PWD)/.. -COCOTB=$(PWD)/../../.. PYTHONPATH := ./model:$(PYTHONPATH) export PYTHONPATH @@ -30,6 +29,6 @@ GPI_IMPL := vpi export TOPLEVEL_LANG MODULE=test_axi_lite_slave -include $(COCOTB)/makefiles/Makefile.inc -include $(COCOTB)/makefiles/Makefile.sim +include $(shell cocotb-config --makefiles)/Makefile.inc +include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/examples/dff/tests/Makefile b/examples/dff/tests/Makefile old mode 100644 new mode 100755 index 20d92c46..04aab193 --- a/examples/dff/tests/Makefile +++ b/examples/dff/tests/Makefile @@ -4,7 +4,6 @@ # All rights reserved. CWD=$(shell pwd) -COCOTB=$(CWD)/../../.. TOPLEVEL_LANG ?=verilog @@ -25,8 +24,8 @@ ifeq ($(SIM),questa) SIM_ARGS=-t 1ps endif -include $(COCOTB)/makefiles/Makefile.inc -include $(COCOTB)/makefiles/Makefile.sim +include $(shell cocotb-config --makefiles)/Makefile.inc +include $(shell cocotb-config --makefiles)/Makefile.sim # list all required Python files here sim: $(MODULE).py diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index 4256c7ce..ba1521f6 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -32,7 +32,6 @@ TOPLEVEL_LANG ?= verilog WPWD=$(shell sh -c 'pwd -W') PWD=$(shell pwd) -COCOTB=$(PWD)/../../.. ifeq ($(OS),Msys) WPWD=$(shell sh -c 'pwd -W') @@ -58,8 +57,8 @@ CUSTOM_COMPILE_DEPS = hal LD_LIBRARY_PATH := $(PWD)/../cosim:$(LD_LIBRARY_PATH) -include $(COCOTB)/makefiles/Makefile.inc -include $(COCOTB)/makefiles/Makefile.sim +include $(shell cocotb-config --makefiles)/Makefile.inc +include $(shell cocotb-config --makefiles)/Makefile.sim .PHONY: hal hal: diff --git a/examples/mean/tests/Makefile b/examples/mean/tests/Makefile index cce95112..005fdc4d 100644 --- a/examples/mean/tests/Makefile +++ b/examples/mean/tests/Makefile @@ -12,7 +12,6 @@ else TOPLEVEL := mean_sv PWD=$(shell pwd) -COCOTB=$(PWD)/../../.. ifeq ($(OS),Msys) WPWD=$(shell sh -c 'pwd -W') @@ -31,7 +30,7 @@ VERILOG_SOURCES = $(WPWD)/../hdl/mean_sv.sv MODULE := test_mean -include $(COCOTB)/makefiles/Makefile.inc -include $(COCOTB)/makefiles/Makefile.sim +include $(shell cocotb-config --makefiles)/Makefile.inc +include $(shell cocotb-config --makefiles)/Makefile.sim endif diff --git a/examples/mixed_language/tests/Makefile b/examples/mixed_language/tests/Makefile index b4261ac2..4b1938ff 100644 --- a/examples/mixed_language/tests/Makefile +++ b/examples/mixed_language/tests/Makefile @@ -7,7 +7,6 @@ SIM?=aldec TOPLEVEL = endian_swapper_mixed PWD=$(shell pwd) -COCOTB=$(PWD)/../../.. ifeq ($(OS),Msys) WPWD=$(shell sh -c 'pwd -W') @@ -30,6 +29,5 @@ endif MODULE=test_mixed_language -include $(COCOTB)/makefiles/Makefile.inc -include $(COCOTB)/makefiles/Makefile.sim - +include $(shell cocotb-config --makefiles)/Makefile.inc +include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/examples/ping_tun_tap/tests/Makefile b/examples/ping_tun_tap/tests/Makefile index c9425e07..8e30eea2 100644 --- a/examples/ping_tun_tap/tests/Makefile +++ b/examples/ping_tun_tap/tests/Makefile @@ -45,12 +45,11 @@ WPWD=$(shell pwd) endif PWD=$(shell pwd) -COCOTB=$(PWD)/../../.. VERILOG_SOURCES = $(PWD)/../hdl/icmp_reply.sv MODULE=test_icmp_reply -include $(COCOTB)/makefiles/Makefile.inc -include $(COCOTB)/makefiles/Makefile.sim +include $(shell cocotb-config --makefiles)/Makefile.inc +include $(shell cocotb-config --makefiles)/Makefile.sim endif From feeb70c999fcd03ee1bbbcc2dcc8d542344da424 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Tue, 5 Feb 2019 19:24:10 +0000 Subject: [PATCH 1547/2656] Remove old way of installing cocotb from Makefile --- Makefile | 27 +--------------- bin/create_files.py | 77 --------------------------------------------- 2 files changed, 1 insertion(+), 103 deletions(-) delete mode 100755 bin/create_files.py diff --git a/Makefile b/Makefile index 34e0982e..3d2b3e8f 100644 --- a/Makefile +++ b/Makefile @@ -27,9 +27,6 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -INSTALL_DIR?=/usr/local -FULL_INSTALL_DIR=$(INSTALL_DIR)/cocotb-$(VERSION) - all: test include cocotb/share/makefiles/Makefile.inc @@ -43,7 +40,7 @@ clean: $(MAKE) -C examples clean $(MAKE) -C tests clean -do_tests: +do_tests: $(MAKE) -k -C tests $(MAKE) -k -C examples @@ -56,30 +53,8 @@ jenkins: do_tests test: do_tests ./bin/combine_results.py -pycode: - @cp -R $(COCOTB_PY_DIR)/cocotb $(FULL_INSTALL_DIR)/ - -src_install: - @mkdir -p $(FULL_INSTALL_DIR)/lib - @mkdir -p $(FULL_INSTALL_DIR)/bin - @mkdir -p $(FULL_INSTALL_DIR)/include - @cp -R lib/* $(FULL_INSTALL_DIR)/lib/ - @cp -R include/* $(FULL_INSTALL_DIR)/include/ - -common_install: - @cp -R makefiles $(FULL_INSTALL_DIR)/ - @cp version $(FULL_INSTALL_DIR)/ - -create_files: - bin/create_files.py $(FULL_INSTALL_DIR) - -install: src_install common_install pycode create_files - @echo -e "\nInstalled to $(FULL_INSTALL_DIR)" - @echo -e "To uninstall run $(FULL_INSTALL_DIR)/bin/cocotb_uninstall\n" - help: @echo -e "\nCocotb make help\n\nall\t- Build libaries for native" - @echo -e "install\t- Build and install libaries to FULL_INSTALL_DIR (default=$(FULL_INSTALL_DIR))" @echo -e "clean\t- Clean the build dir" @echo -e "debug\t- Dump out some useful debug info\n\n" @echo -e "To build natively just run make.\nTo build for 32bit on a 64 bit system set ARCH=i686\n" diff --git a/bin/create_files.py b/bin/create_files.py deleted file mode 100755 index b78ea158..00000000 --- a/bin/create_files.py +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env python - -''' Copyright (c) 2013 Potential Ventures Ltd -Copyright (c) 2013 SolarFlare Communications Inc -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Potential Ventures Ltd, - SolarFlare Communications Inc nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' - -""" -script takes an input path and creates - Makefile.inc, used to provide common information once installed - cocotb_uninstall, uninstall script -""" -import sys -import platform -from subprocess import call - - -def print_make_inc(path): - makefile = open("/tmp/Makefile.inc", "w") - makefile.write("export ARCH:=$(shell uname -m)\n") - makefile.write("export COCOTB_PY_DIR:=" + path + "\n") - makefile.write("export COCOTB_SHARE_DIR:=" + path + "\n") - makefile.write("export LIB_DIR:=" + path + "/lib/$(ARCH)\n") - makefile.close() - - -def print_uninstall(path): - uninstall = open("/tmp/cocotb_uninstall", "w") - file_contents = "#!/usr/bin/env python\n" - file_contents = file_contents + "import sys\nfrom subprocess import call\n\n" - file_contents = file_contents + "def remove():\n" - file_contents = file_contents + " print(\"Removing cocotb from " + path + "\")\n" - file_contents = file_contents + " call(\"rm -rf " + path + "\", shell=True)\n\n" - file_contents = file_contents + "if __name__ == \"__main__\":\n" - file_contents = file_contents + " remove()\n" - - uninstall.write(file_contents) - - -def print_files(path): - print_uninstall(path) - - call("install -m 544 /tmp/cocotb_uninstall " + path + "/bin/cocotb_uninstall", shell=True) - call("rm -rf /tmp/cocotb_uninstall", shell=True) - - -def check_args(args): - if len(args) is not 1: - print("Please specify a path") - return - - print_files(args[0]) - -if __name__ == "__main__": - check_args(sys.argv[1:]) From 15a81dd73a9caff335fc6930dd70ca110bf86f2c Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Tue, 5 Feb 2019 14:44:57 +0000 Subject: [PATCH 1548/2656] Test packaged cocotb builds with tox --- .travis.yml | 17 +++++------------ Dockerfile | 13 ++----------- tox.ini | 11 +++++++++++ 3 files changed, 18 insertions(+), 23 deletions(-) create mode 100644 tox.ini diff --git a/.travis.yml b/.travis.yml index af982984..f9abb06e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,6 @@ sudo: required language: python cache: pip -# mainly for flake8 as everything else runs in docker -python: - - 3.5 - git: quiet: true @@ -16,12 +12,11 @@ services: jobs: include: - stage: PythonLint + python: 3.5 install: pip install flake8 script: flake8 --exit-zero cocotb/ - stage: SimTests - # env only here for easier visibility in travis-ci - env: - - COCOTB_PY27 + python: 2.7 before_install: &docker_prep - docker build -t cocotb . - docker images -a @@ -29,15 +24,13 @@ jobs: - docker ps -a script: - echo 'Running cocotb tests with Python 2.7 ...' && echo 'travis_fold:start:cocotb_py27' - - docker exec -it -e TRAVIS=true -e COCOTB=/build-py2/src cocotb bash -lc 'source /build-py2/venv/bin/activate; make -C /build-py2/src test' + - docker exec -it -e TRAVIS=true cocotb bash -lc 'pip install tox; cd /src; tox -e py27' - echo -e 'travis_fold:end:cocotb_py27' - stage: SimTests - # env only here for easier visibility in travis-ci - env: - - COCOTB_PY35 + python: 3.5 before_install: *docker_prep script: - echo 'Running cocotb tests with Python 3.5 ...' && echo -en 'travis_fold:start:cocotb_py35' - - docker exec -it -e TRAVIS=true -e COCOTB=/build-py3/src cocotb bash -lc 'set -x; source /build-py3/venv/bin/activate; make -C /build-py3/src test' + - docker exec -it -e TRAVIS=true cocotb bash -lc 'pip install tox; cd /src; tox -e py35' - echo 'travis_fold:end:cocotb_py35' diff --git a/Dockerfile b/Dockerfile index 4100e943..a758717d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,6 +17,7 @@ RUN apt-get -qq update && apt-get -qq install -y --no-install-recommends \ bison \ python2.7-dev python3-dev\ python-pip \ + python-setuptools \ python3 \ virtualenv \ python3-venv \ @@ -35,15 +36,5 @@ RUN git clone https://github.com/steveicarus/iverilog.git --depth=1 --branch v${ && make -s install \ && rm -r /usr/src/iverilog - # make sources available in docker image - one copy per python version -COPY . /build-py2/src -COPY . /build-py3/src - -# Build and prepare using Python 2 -RUN bash -c 'virtualenv /build-py2/venv; source /build-py2/venv/bin/activate; pip install coverage xunitparser; deactivate' - -# Build and prepare virtual env using Python 3 -RUN bash -c 'python3 -m venv /build-py3/venv; source /build-py3/venv/bin/activate; pip install coverage xunitparser; deactivate' - - +COPY . /src diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..f4cb8cca --- /dev/null +++ b/tox.ini @@ -0,0 +1,11 @@ +[tox] +envlist = py27,py35 + +[testenv] +deps = + coverage + xunitparser + pytest + +commands = + make test From 15eef4520bf8cd24a95d987238b886fb80690c07 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Sun, 3 Feb 2019 19:50:16 +0000 Subject: [PATCH 1549/2656] Warn if old-style cocotb usage is detected Before cocotb had a Python package, cocotb was referenced in Makefiles using the COCOTB environment variable. Instead, the cocotb-config tool should be used from now on. Inform users about that change by displaying a warning during the build process. --- cocotb/share/makefiles/Makefile.inc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cocotb/share/makefiles/Makefile.inc b/cocotb/share/makefiles/Makefile.inc index 20d7dd31..1618e915 100644 --- a/cocotb/share/makefiles/Makefile.inc +++ b/cocotb/share/makefiles/Makefile.inc @@ -40,6 +40,10 @@ ifeq ($(COCOTB_SHARE_DIR),) export COCOTB_SHARE_DIR:=$(abspath $(dir $(lastword $(MAKEFILE_LIST)))/..) endif +ifeq ($(COCOTB_PY_DIR),$(COCOTB_SHARE_DIR)) +$(warning 'Deprecated cocotb file structure detected. Please use "include $$(shell cocotb-config --makefile)/Makefile.inc" in Makefiles') +endif + ifeq ($(USER_DIR),) export USER_DIR:=$(abspath $(dir $(firstword $(MAKEFILE_LIST)))) endif From 7c7203e2e0e2f530c1351a41156b1ebe66d87498 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Mon, 11 Mar 2019 14:17:04 +0100 Subject: [PATCH 1550/2656] Add osx and linux 3.7 (travis) --- .travis.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/.travis.yml b/.travis.yml index f9abb06e..02a9e78c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,10 +12,13 @@ services: jobs: include: - stage: PythonLint + os: linux python: 3.5 install: pip install flake8 script: flake8 --exit-zero cocotb/ + - stage: SimTests + os: linux python: 2.7 before_install: &docker_prep - docker build -t cocotb . @@ -26,7 +29,9 @@ jobs: - echo 'Running cocotb tests with Python 2.7 ...' && echo 'travis_fold:start:cocotb_py27' - docker exec -it -e TRAVIS=true cocotb bash -lc 'pip install tox; cd /src; tox -e py27' - echo -e 'travis_fold:end:cocotb_py27' + - stage: SimTests + os: linux python: 3.5 before_install: *docker_prep script: @@ -34,3 +39,32 @@ jobs: - docker exec -it -e TRAVIS=true cocotb bash -lc 'pip install tox; cd /src; tox -e py35' - echo 'travis_fold:end:cocotb_py35' + - stage: SimTests + python: 3.7 + before_install: &travis_linx_prep + - sudo apt-get install gperf + - git clone https://github.com/steveicarus/iverilog.git --depth=1 --branch v10_2 + - cd iverilog && autoconf && ./configure && make -j2 && sudo make install && cd .. + - pip install tox + script: + - echo 'Running cocotb tests with Python 3.7 ...' && echo -en 'travis_fold:start:cocotb_py37' + - docker exec -it -e TRAVIS=true cocotb bash -lc 'pip install tox; cd /src; tox -e py37' + - echo 'travis_fold:end:cocotb_py37' + + - stage: SimTests + os: osx + language: generic + python: 2.7 + before_install: &osx_prep + - brew install icarus-verilog + - pip install tox + script: + - tox -e py27 + + - stage: SimTests + os: osx + language: generic + python: 3.6 + before_install: *osx_prep + script: + - tox -e py36 From cc37a525719ad8a5cbc3f7874fda7b4888c7a5db Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Mon, 11 Mar 2019 13:24:30 +0000 Subject: [PATCH 1551/2656] Revert "Add osx and linux 3.7 (travis)" This reverts commit 7c7203e2e0e2f530c1351a41156b1ebe66d87498. This commit was accidentially introduced. --- .travis.yml | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/.travis.yml b/.travis.yml index 02a9e78c..f9abb06e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,13 +12,10 @@ services: jobs: include: - stage: PythonLint - os: linux python: 3.5 install: pip install flake8 script: flake8 --exit-zero cocotb/ - - stage: SimTests - os: linux python: 2.7 before_install: &docker_prep - docker build -t cocotb . @@ -29,9 +26,7 @@ jobs: - echo 'Running cocotb tests with Python 2.7 ...' && echo 'travis_fold:start:cocotb_py27' - docker exec -it -e TRAVIS=true cocotb bash -lc 'pip install tox; cd /src; tox -e py27' - echo -e 'travis_fold:end:cocotb_py27' - - stage: SimTests - os: linux python: 3.5 before_install: *docker_prep script: @@ -39,32 +34,3 @@ jobs: - docker exec -it -e TRAVIS=true cocotb bash -lc 'pip install tox; cd /src; tox -e py35' - echo 'travis_fold:end:cocotb_py35' - - stage: SimTests - python: 3.7 - before_install: &travis_linx_prep - - sudo apt-get install gperf - - git clone https://github.com/steveicarus/iverilog.git --depth=1 --branch v10_2 - - cd iverilog && autoconf && ./configure && make -j2 && sudo make install && cd .. - - pip install tox - script: - - echo 'Running cocotb tests with Python 3.7 ...' && echo -en 'travis_fold:start:cocotb_py37' - - docker exec -it -e TRAVIS=true cocotb bash -lc 'pip install tox; cd /src; tox -e py37' - - echo 'travis_fold:end:cocotb_py37' - - - stage: SimTests - os: osx - language: generic - python: 2.7 - before_install: &osx_prep - - brew install icarus-verilog - - pip install tox - script: - - tox -e py27 - - - stage: SimTests - os: osx - language: generic - python: 3.6 - before_install: *osx_prep - script: - - tox -e py36 From cc01ac83060139cb34c59adae937715016245c8b Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 11 Mar 2019 22:39:35 +0100 Subject: [PATCH 1552/2656] Add classifiers. --- setup.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/setup.py b/setup.py index c4646bad..085c94bc 100644 --- a/setup.py +++ b/setup.py @@ -64,5 +64,11 @@ def package_files(directory): ] }, platforms='any', + classifiers=[ + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "License :: OSI Approved :: BSD License", + "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", + ], ) From b561e107d4ddcea38f681b533ee88554232f66f7 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 15 Mar 2019 11:34:58 +0100 Subject: [PATCH 1553/2656] Remove IUS option -sv which is detrimental to AMS (#783) IUS is doing language detection based on file extension by default. Using -sv *forces* SystemVerilog even for Verilog-AMS files, which then fails. I believe cocotb only had -sv in the Makefile because the sample_module was wrongly using the .v extension earlier. --- cocotb/share/makefiles/simulators/Makefile.ius | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.ius b/cocotb/share/makefiles/simulators/Makefile.ius index 7530337e..7bf04a01 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ius +++ b/cocotb/share/makefiles/simulators/Makefile.ius @@ -69,7 +69,7 @@ endif GPI_EXTRA:= ifeq ($(TOPLEVEL_LANG),verilog) GPI_ARGS = -loadvpi $(LIB_DIR)/libvpi - EXTRA_ARGS += -sv -v93 + EXTRA_ARGS += -v93 HDL_SOURCES = $(VERILOG_SOURCES) ROOT_LEVEL = $(TOPLEVEL) ifneq ($(VHDL_SOURCES),) From 7ded80828a4351d8e478d2cef23a9f5f81bae724 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Tue, 12 Mar 2019 20:55:13 +0100 Subject: [PATCH 1554/2656] Fix python 3.7 (remove StopIteration) --- cocotb/handle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 4c366e4d..9049be55 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -506,7 +506,7 @@ def __getitem__(self, index): def __iter__(self): try: if self._range is None: - raise StopIteration + return self._log.debug("Iterating with range [%d:%d]" % (self._range[0], self._range[1])) for i in self._range_iter(self._range[0], self._range[1]): From 4191ddb36940cf319ec53854acd131dc48c6619d Mon Sep 17 00:00:00 2001 From: killruana Date: Sat, 16 Mar 2019 16:13:28 +0100 Subject: [PATCH 1555/2656] Fix import cocotb.regression Rename global variable "regression" in cocotb module for avoiding name conflict with the cocotb.regression module Fixes #857 --- cocotb/__init__.py | 10 ++++---- cocotb/regression.py | 2 +- cocotb/scheduler.py | 4 ++-- tests/test_cases/issue_857/Makefile | 31 +++++++++++++++++++++++++ tests/test_cases/issue_857/issue_857.py | 29 +++++++++++++++++++++++ 5 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 tests/test_cases/issue_857/Makefile create mode 100644 tests/test_cases/issue_857/issue_857.py diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 7d022634..238c22f0 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -85,7 +85,7 @@ scheduler = Scheduler() -regression = None +regression_manager = None plusargs = {} @@ -161,11 +161,11 @@ def _initialise_testbench(root_name): modules = module_str.split(',') hooks = hooks_str.split(',') if hooks_str else [] - global regression + global regression_manager - regression = RegressionManager(root_name, modules, tests=test_str, seed=seed, hooks=hooks) - regression.initialise() - regression.execute() + regression_manager = RegressionManager(root_name, modules, tests=test_str, seed=seed, hooks=hooks) + regression_manager.initialise() + regression_manager.execute() _rlock.release() return True diff --git a/cocotb/regression.py b/cocotb/regression.py index 90584e78..c267392c 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -304,7 +304,7 @@ def _result_was(): self.execute() def execute(self): - self._running_test = cocotb.regression.next_test() + self._running_test = cocotb.regression_manager.next_test() if self._running_test: start = '' end = '' diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 11c88753..931032a3 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -309,7 +309,7 @@ def begin_test(self, trigger=None): if self._test_result is not None: if _debug: self.log.debug("Issue test result to regression object") - cocotb.regression.handle_result(self._test_result) + cocotb.regression_manager.handle_result(self._test_result) self._test_result = None if self._entrypoint is not None: test = self._entrypoint @@ -732,7 +732,7 @@ def finish_scheduler(self, test_result): once we return the sim will close us so no cleanup is needed. """ self.log.debug("Issue sim closedown result to regression object") - cocotb.regression.handle_result(test_result) + cocotb.regression_manager.handle_result(test_result) def cleanup(self): """Clear up all our state. diff --git a/tests/test_cases/issue_857/Makefile b/tests/test_cases/issue_857/Makefile new file mode 100644 index 00000000..4142731e --- /dev/null +++ b/tests/test_cases/issue_857/Makefile @@ -0,0 +1,31 @@ +############################################################################### +# Copyright (c) 2015 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +include ../../designs/sample_module/Makefile + +MODULE = issue_857 diff --git a/tests/test_cases/issue_857/issue_857.py b/tests/test_cases/issue_857/issue_857.py new file mode 100644 index 00000000..7d4acd94 --- /dev/null +++ b/tests/test_cases/issue_857/issue_857.py @@ -0,0 +1,29 @@ +import cocotb +import cocotb.regression +import cocotb.triggers + + +@cocotb.coroutine +def dummy_coroutine(dut): + yield cocotb.triggers.Timer(10, "ns") + + +# Try to instantiate the TestFactory class using its full specifier name. +# +# In issue #857, a global variable named "regression" in the cocotb module hide +# the module cocotb.regression, so the TestFactory class is not accessible with +# an import like +# +# >>> import cocotb.regression +# >>> factory = cocotb.regression.FactoryManager() +# +# The class is still accessible by an import like +# +# >>> from cocotb.regression import TestFactory +# >>> factory = TestFactory() +# +# but the discoverer of the bug prefers the former approach. +# +# And in general, it's probably a good idea to not have name conflicts ;) +test_factory = cocotb.regression.TestFactory(dummy_coroutine) +test_factory.generate_tests() From 796f60b15b6a51ba3e56e60bfe15e34d22f55fd7 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sat, 16 Mar 2019 16:27:20 +0100 Subject: [PATCH 1556/2656] Add support for Verilog strings This add support for strings in SystemVerilog and two new testcases to test_discovery. Unfortunately, Cadence Xcelium 18.09 fails with the access_constant_string_verilog (allows modification of const string) and passes with access_variable_string_verilog; while for Synopsys VCS-MX 2017-12, it's the other way round (cannot modify the variable string) - simulator bugs I guess. Fixes #784 --- cocotb/share/lib/vpi/VpiImpl.cpp | 2 +- tests/designs/sample_module/sample_module.sv | 8 +++ .../test_discovery/test_discovery.py | 55 +++++++++++++++++-- 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/cocotb/share/lib/vpi/VpiImpl.cpp b/cocotb/share/lib/vpi/VpiImpl.cpp index 7b6e0155..ee4fab37 100644 --- a/cocotb/share/lib/vpi/VpiImpl.cpp +++ b/cocotb/share/lib/vpi/VpiImpl.cpp @@ -104,7 +104,6 @@ gpi_objtype_t to_gpi_objtype(int32_t vpitype) case vpiParameter: return GPI_PARAMETER; - case vpiStructVar: case vpiStructNet: case vpiUnionVar: @@ -155,6 +154,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, case vpiIntegerVar: case vpiIntegerNet: case vpiRealVar: + case vpiStringVar: case vpiMemoryWord: new_obj = new VpiSignalObjHdl(this, new_hdl, to_gpi_objtype(type), false); break; diff --git a/tests/designs/sample_module/sample_module.sv b/tests/designs/sample_module/sample_module.sv index 51c98c3c..ca06f794 100644 --- a/tests/designs/sample_module/sample_module.sv +++ b/tests/designs/sample_module/sample_module.sv @@ -48,6 +48,7 @@ module sample_module ( output real stream_out_real, output integer stream_out_int, input test_if inout_if, + input string string_input_port, `endif input [7:0] stream_in_data, input [63:0] stream_in_data_wide, @@ -60,6 +61,13 @@ module sample_module ( ); +`ifndef __ICARUS__ +localparam string STRING_LOCALPARAM = "TESTING_LOCALPARAM"; + +var string STRING_VAR = "TESTING_VAR"; +const string STRING_CONST = "TESTING_CONST"; +`endif + always @(posedge clk) stream_out_data_registered <= stream_in_data; diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 72bb53a1..a70ed1cb 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -29,7 +29,7 @@ import logging from cocotb.triggers import Timer from cocotb.result import TestError, TestFailure -from cocotb.handle import IntegerObject, ConstantObject, HierarchyObject +from cocotb.handle import IntegerObject, ConstantObject, HierarchyObject, StringObject @cocotb.test() @@ -181,10 +181,8 @@ def access_constant_integer(dut): raise TestFailure("EXAMPLE_WIDTH was not 7") @cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) -def access_string(dut): - """ - Access to a string, both constant and signal - """ +def access_string_vhdl(dut): + """Access to a string, both constant and signal.""" tlog = logging.getLogger("cocotb.test") yield Timer(10) constant_string = dut.isample_module1.EXAMPLE_STRING; @@ -252,6 +250,53 @@ def access_string(dut): if variable_string != test_string: raise TestFailure("%r %s != '%s'" % (variable_string, result, test_string)) + +# TODO: add tests for Verilog "string_input_port" and "STRING_LOCALPARAM" (see issue #802) + +@cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"], + expect_error=cocotb.SIM_NAME.lower().startswith("icarus")) +def access_const_string_verilog(dut): + """Access to a const Verilog string.""" + tlog = logging.getLogger("cocotb.test") + + yield Timer(10) + string_const = dut.STRING_CONST; + tlog.info("%r is %s" % (string_const, str(string_const))) + if not isinstance(string_const, StringObject): + raise TestFailure("STRING_CONST was not StringObject") + if string_const != "TESTING_CONST": + raise TestFailure("STRING_CONST was not == \'TESTING_CONST\'") + + tlog.info("Modifying const string") + string_const <= "MODIFIED" + yield Timer(10) + string_const = dut.STRING_CONST; + if string_const != "TESTING_CONST": + raise TestFailure("STRING_CONST was not still \'TESTING_CONST\'") + + +@cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"], + expect_error=cocotb.SIM_NAME.lower().startswith("icarus")) +def access_var_string_verilog(dut): + """Access to a var Verilog string.""" + tlog = logging.getLogger("cocotb.test") + + yield Timer(10) + string_var = dut.STRING_VAR; + tlog.info("%r is %s" % (string_var, str(string_var))) + if not isinstance(string_var, StringObject): + raise TestFailure("STRING_VAR was not StringObject") + if string_var != "TESTING_VAR": + raise TestFailure("STRING_VAR was not == \'TESTING_VAR\'") + + tlog.info("Modifying var string") + string_var <= "MODIFIED" + yield Timer(10) + string_var = dut.STRING_VAR; + if string_var != "MODIFIED": + raise TestFailure("STRING_VAR was not == \'MODIFIED\'") + + @cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) def access_constant_boolean(dut): """Test access to a constant boolean""" From df6fc20ee1f1fe0e62c27a046168cde773f6865d Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 22 Feb 2019 00:45:57 +0100 Subject: [PATCH 1557/2656] Remove obsolete tab completion method for IPython. --- cocotb/handle.py | 4 ++-- .../test_discovery/test_discovery.py | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 9049be55..f8f9b9b4 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -215,10 +215,10 @@ def _sub_handle_key(self, name): """Translates the handle name to a key to use in ``_sub_handles`` dictionary.""" return name.split(".")[-1] - def _getAttributeNames(self): + def __dir__(self): """Permits IPython tab completion to work.""" self._discover_all() - return dir(self) + return super(RegionObject, self).__dir__() + [str(k) for k in self._sub_handles] class HierarchyObject(RegionObject): diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index a70ed1cb..7fd9b794 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -27,6 +27,8 @@ import cocotb import logging +import os +import textwrap from cocotb.triggers import Timer from cocotb.result import TestError, TestFailure from cocotb.handle import IntegerObject, ConstantObject, HierarchyObject, StringObject @@ -60,7 +62,25 @@ def ipython_embed(dut): IPython.embed() +@cocotb.test(skip=True) +def ipython_embed_kernel(dut): + """Start an interactive Python shell.""" + yield Timer(0) + import IPython + print(textwrap.dedent(""" + ############################################################################### + Running IPython embed_kernel() + + You can now send this process into the background with "Ctrl-Z bg" and run + jupyter console --existing + or + jupyter qtconsole --existing + or + jupyter console --existing kernel-{}.json + ###############################################################################""".format(os.getpid()))) + IPython.embed_kernel() + @cocotb.test(expect_error=True) def discover_value_not_in_dut(dut): """Try and get a value from the DUT that is not there""" From 99f8f9d6dfc0b18da5a314b60711cde5f51dbbb1 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Thu, 7 Feb 2019 11:05:48 -0800 Subject: [PATCH 1558/2656] Added info to NullTrigger from Event.wait() NullTriggers returned as a result of waiting on a fired Event now include the Event name. In the case of a looped coroutine waiting on an Event that has been fired but not cleared, the exhibited behavior is usually hitting the recursion limit in the scheduler and throwing an exception. Having the information about the Event in the NullTrigger object is very helpful when debugging. --- cocotb/triggers.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 03ed2b6a..2756df78 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -397,7 +397,7 @@ def wait(self): :meth:`~cocotb.triggers.Event.clear` should be called. """ if self.fired: - return NullTrigger() + return NullTrigger(name="{}.wait()".format(str(self))) return _Event(self) def clear(self): @@ -500,6 +500,9 @@ def __init__(self, name=""): def prime(self, callback): callback(self) + def __str__(self): + return self.__class__.__name__ + "(%s)" % self.name + class Join(with_metaclass(ParametrizedSingleton, PythonTrigger)): """Join a coroutine, firing when it exits.""" From d26928f2b00156898cb93325ed4d906e11a11dcf Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Mon, 11 Mar 2019 16:54:43 +0100 Subject: [PATCH 1559/2656] Add osx and more python version to travis --- .travis.yml | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/.travis.yml b/.travis.yml index f9abb06e..06db683b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ sudo: required language: python cache: pip +dist: xenial git: quiet: true @@ -12,10 +13,13 @@ services: jobs: include: - stage: PythonLint + os: linux python: 3.5 install: pip install flake8 script: flake8 --exit-zero cocotb/ + - stage: SimTests + os: linux python: 2.7 before_install: &docker_prep - docker build -t cocotb . @@ -26,7 +30,9 @@ jobs: - echo 'Running cocotb tests with Python 2.7 ...' && echo 'travis_fold:start:cocotb_py27' - docker exec -it -e TRAVIS=true cocotb bash -lc 'pip install tox; cd /src; tox -e py27' - echo -e 'travis_fold:end:cocotb_py27' + - stage: SimTests + os: linux python: 3.5 before_install: *docker_prep script: @@ -34,3 +40,41 @@ jobs: - docker exec -it -e TRAVIS=true cocotb bash -lc 'pip install tox; cd /src; tox -e py35' - echo 'travis_fold:end:cocotb_py35' + - stage: SimTests + python: 3.7 + before_install: &travis_linx_prep + - sudo apt-get install gperf + - git clone https://github.com/steveicarus/iverilog.git --depth=1 --branch v10_2 + - cd iverilog && autoconf && ./configure && make -j2 && sudo make install && cd .. + - pip install tox + - pyenv global system $TRAVIS_PYTHON_VERSION + script: + - echo 'Running cocotb tests with Python 3.7 ...' && echo -en 'travis_fold:start:cocotb_py37' + - tox -e py37 + - echo 'travis_fold:end:cocotb_py37' + + - stage: SimTests + python: 3.6.7 + before_install: *travis_linx_prep + script: + - echo 'Running cocotb tests with Python 3.6.7 ...' && echo -en 'travis_fold:start:cocotb_py36' + - tox -e py36 + - echo 'travis_fold:end:cocotb_py36' + + - stage: SimTests + os: osx + language: generic + python: 2.7 + before_install: &osx_prep + - brew install icarus-verilog + - pip install tox + script: + - tox -e py27 + + - stage: SimTests + os: osx + language: generic + python: 3.6 + before_install: *osx_prep + script: + - tox -e py36 From 2ebdbfdbae741d6b9cc2e6f923d72b6901e8d078 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 27 Jan 2019 18:33:23 -0800 Subject: [PATCH 1560/2656] Make things decorated with `cocotb.test` actually instances of `cocotb.test` Does the same for hooks. This removes the truly bizarre pattern of calling `super().__init__` from within `__call__`. As a nice bonus, this means that the following no longer calls `coroutine.__init__` twice on the same instance: ``` failing_test = cocotb.test(expect_fail=True) @failing_test def will_fail(): yield Timer(1) assert 1 == 2 @failing_test def will_also_fail(): yield Timer(1) assert 1 == 2 ``` --- cocotb/decorators.py | 78 +++++++++++++-------- tests/test_cases/test_cocotb/test_cocotb.py | 10 +++ 2 files changed, 60 insertions(+), 28 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 10cbecee..ab7c2ac0 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -41,7 +41,7 @@ from cocotb.triggers import Join, PythonTrigger, Timer, Event, NullTrigger from cocotb.result import (TestComplete, TestError, TestFailure, TestSuccess, ReturnValue, raise_error, ExternalException) -from cocotb.utils import get_sim_time +from cocotb.utils import get_sim_time, with_metaclass from cocotb import outcomes @@ -242,6 +242,8 @@ class coroutine(object): ``log`` methods will will log to ``cocotb.coroutines.name``. ``join()`` method returns an event which will fire when the coroutine exits. + + Used as ``@cocotb.coroutine``. """ def __init__(self, func): @@ -341,37 +343,60 @@ def __get__(self, obj, type=None): and standalone functions""" return self.__class__(self._func.__get__(obj, type)) + +class _decorator_helper(type): + """ + Metaclass that allows a type to be constructed using decorator syntax, + passing the decorated function as the first argument. + + So: + + @MyClass(construction, args='go here') + def this_is_passed_as_f(...): + pass + + ends up calling + + MyClass.__init__(this_is_passed_as_f, construction, args='go here') + """ + def __call__(cls, *args, **kwargs): + def decorator(f): + # fall back to the normal way of constructing an object, now that + # we have all the arguments + return type.__call__(cls, f, *args, **kwargs) + return decorator + + @public -class hook(coroutine): +class hook(with_metaclass(_decorator_helper, coroutine)): """Decorator to mark a function as a hook for cocotb. + Used as ``@cocotb.hook()``. + All hooks are run at the beginning of a cocotb test suite, prior to any test code being run.""" def __init__(self): - pass - - def __call__(self, f): super(hook, self).__init__(f) + self.im_hook = True + self.name = self._func.__name__ - def _wrapped_hook(*args, **kwargs): - try: - return RunningCoroutine(self._func(*args, **kwargs), self) - except Exception as e: - raise raise_error(self, "Hook raised exception:") + def __call__(self, *args, **kwargs): + try: + return RunningCoroutine(self._func(*args, **kwargs), self) + except Exception as e: + raise raise_error(self, "Hook raised exception:") - _wrapped_hook.im_hook = True - _wrapped_hook.name = self._func.__name__ - _wrapped_hook.__name__ = self._func.__name__ - return _wrapped_hook @public -class test(coroutine): +class test(with_metaclass(_decorator_helper, coroutine)): """Decorator to mark a function as a test. All tests are coroutines. The test decorator provides some common reporting etc., a test timeout and allows us to mark tests as expected failures. + Used as ``@cocotb.test(...)``. + Args: timeout (int, optional): value representing simulation timeout (not implemented). @@ -385,24 +410,21 @@ class test(coroutine): stage (int, optional) Order tests logically into stages, where multiple tests can share a stage. """ - def __init__(self, timeout=None, expect_fail=False, expect_error=False, + def __init__(self, f, timeout=None, expect_fail=False, expect_error=False, skip=False, stage=None): + super(test, self).__init__(f) + self.timeout = timeout self.expect_fail = expect_fail self.expect_error = expect_error self.skip = skip self.stage = stage + self.im_test = True # For auto-regressions + self.name = self._func.__name__ - def __call__(self, f): - super(test, self).__init__(f) + def __call__(self, *args, **kwargs): + try: + return RunningTest(self._func(*args, **kwargs), self) + except Exception as e: + raise raise_error(self, "Test raised exception:") - def _wrapped_test(*args, **kwargs): - try: - return RunningTest(self._func(*args, **kwargs), self) - except Exception as e: - raise raise_error(self, "Test raised exception:") - - _wrapped_test.im_test = True # For auto-regressions - _wrapped_test.name = self._func.__name__ - _wrapped_test.__name__ = self._func.__name__ - return _wrapped_test diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index c6641dcb..72fe8eb2 100755 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -839,6 +839,16 @@ def test_lessthan_raises_error(dut): if False: yield +@cocotb.test() +def test_tests_are_tests(dut): + """ + Test that things annotated with cocotb.test are tests + """ + yield Timer(1) + + assert isinstance(test_tests_are_tests, cocotb.test) + + if sys.version_info[:2] >= (3, 3): # this would be a syntax error in older python, so we do the whole # thing inside exec From 643c395d089b2c15666aaf74138c26e23c9d7383 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Fri, 25 Jan 2019 23:29:56 -0800 Subject: [PATCH 1561/2656] Avoid recursing in the scheduler By doing this, we don't have to worry about hitting the recursion limit. It is still possible to recurse through `NullTrigger`, but I will attempt to handle that in a later patch. --- cocotb/scheduler.py | 151 ++++++++++++++++++++++++-------------------- 1 file changed, 83 insertions(+), 68 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 931032a3..5c7d7290 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -317,17 +317,19 @@ def begin_test(self, trigger=None): self.schedule(test) self.advance() - def react(self, trigger, depth=0): + def react(self, trigger): """React called when a trigger fires. We find any coroutines that are waiting on the particular trigger and schedule them. """ - if _profiling and not depth: + if _profiling: ctx = profiling_context() else: ctx = nullcontext() + called_by_simulator = isinstance(trigger, GPITrigger) + with ctx: # When a trigger fires it is unprimed internally if _debug: @@ -359,6 +361,8 @@ def react(self, trigger, depth=0): self._readwrite.unprime() + # this should be the only trigger, probably? + assert not self._pending_triggers return # Similarly if we've scheduled our next_timestep on way to readwrite @@ -372,82 +376,93 @@ def react(self, trigger, depth=0): "Priming ReadWrite trigger so we can playback writes") self._readwrite.prime(self.react) + # this should be the only trigger, probably? + assert not self._pending_triggers return - if trigger not in self._trigger2coros: - - # GPI triggers should only be ever pending if there is an - # associated coroutine waiting on that trigger, otherwise it would - # have been unprimed already - if isinstance(trigger, GPITrigger): - self.log.critical( - "No coroutines waiting on trigger that fired: %s" % - str(trigger)) - - trigger.log.info("I'm the culprit") - # For Python triggers this isn't actually an error - we might do - # event.set() without knowing whether any coroutines are actually - # waiting on this event, for example - elif _debug: - self.log.debug( - "No coroutines waiting on trigger that fired: %s" % - str(trigger)) - - return - - # Scheduled coroutines may append to our waiting list so the first - # thing to do is pop all entries waiting on this trigger. - scheduling = self._trigger2coros.pop(trigger) + # work through triggers one by one + is_first = True + self._pending_triggers.append(trigger) + while self._pending_triggers: + trigger = self._pending_triggers.pop(0) + + if not is_first and isinstance(trigger, GPITrigger): + self.log.warning( + "A GPI trigger occurred after entering react - this " + "should not happen." + ) + assert False + + # this only exists to enable the warning above + is_first = False + + if trigger not in self._trigger2coros: + + # GPI triggers should only be ever pending if there is an + # associated coroutine waiting on that trigger, otherwise it would + # have been unprimed already + if isinstance(trigger, GPITrigger): + self.log.critical( + "No coroutines waiting on trigger that fired: %s" % + str(trigger)) + + trigger.log.info("I'm the culprit") + # For Python triggers this isn't actually an error - we might do + # event.set() without knowing whether any coroutines are actually + # waiting on this event, for example + elif _debug: + self.log.debug( + "No coroutines waiting on trigger that fired: %s" % + str(trigger)) + + continue + + # Scheduled coroutines may append to our waiting list so the first + # thing to do is pop all entries waiting on this trigger. + scheduling = self._trigger2coros.pop(trigger) - if _debug: - debugstr = "\n\t".join([coro.__name__ for coro in scheduling]) - if len(scheduling): - debugstr = "\n\t" + debugstr - self.log.debug("%d pending coroutines for event %s%s" % - (len(scheduling), str(trigger), debugstr)) - - # This trigger isn't needed any more - trigger.unprime() - - # If the coroutine was waiting on multiple triggers we may be able - # to unprime the other triggers that didn't fire - scheduling_set = set(scheduling) - other_triggers = { - t - for coro in scheduling - for t in self._coro2triggers[coro] - } - {trigger} - - for pending in other_triggers: - # every coroutine waiting on this trigger is already being woken - if scheduling_set.issuperset(self._trigger2coros[pending]): - if pending.primed: - pending.unprime() - del self._trigger2coros[pending] - - for coro in scheduling: - if _debug: - self.log.debug("Scheduling coroutine %s" % (coro.__name__)) - self.schedule(coro, trigger=trigger) if _debug: - self.log.debug("Scheduled coroutine %s" % (coro.__name__)) + debugstr = "\n\t".join([coro.__name__ for coro in scheduling]) + if len(scheduling): + debugstr = "\n\t" + debugstr + self.log.debug("%d pending coroutines for event %s%s" % + (len(scheduling), str(trigger), debugstr)) + + # This trigger isn't needed any more + trigger.unprime() - if not depth: - # Schedule may have queued up some events so we'll burn through those - while self._pending_events: + # If the coroutine was waiting on multiple triggers we may be able + # to unprime the other triggers that didn't fire + scheduling_set = set(scheduling) + other_triggers = { + t + for coro in scheduling + for t in self._coro2triggers[coro] + } - {trigger} + + for pending in other_triggers: + # every coroutine waiting on this trigger is already being woken + if scheduling_set.issuperset(self._trigger2coros[pending]): + if pending.primed: + pending.unprime() + del self._trigger2coros[pending] + + for coro in scheduling: + if _debug: + self.log.debug("Scheduling coroutine %s" % (coro.__name__)) + self.schedule(coro, trigger=trigger) if _debug: - self.log.debug("Scheduling pending event %s" % - (str(self._pending_events[0]))) - self._pending_events.pop(0).set() + self.log.debug("Scheduled coroutine %s" % (coro.__name__)) - while self._pending_triggers: + # Schedule may have queued up some events so we'll burn through those + while self._pending_events: if _debug: - self.log.debug("Scheduling pending trigger %s" % - (str(self._pending_triggers[0]))) - self.react(self._pending_triggers.pop(0), depth=depth + 1) + self.log.debug("Scheduling pending event %s" % + (str(self._pending_events[0]))) + self._pending_events.pop(0).set() # We only advance for GPI triggers - if not depth and isinstance(trigger, GPITrigger): + if called_by_simulator: self.advance() if _debug: From d8697ab8ede86deddc613d952859c8cc97c93c66 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 26 Jan 2019 00:46:20 -0800 Subject: [PATCH 1562/2656] Mostly fix gh-637 This prevents the innards of `react` being entered more than once, which as a bonus seems to fix the mentioned issue This leaves a lingering bug for tests which exit before yielding, but that's not nearly as useful as coroutines that do --- cocotb/scheduler.py | 61 +++++++++++++-------- tests/test_cases/test_cocotb/test_cocotb.py | 49 ++++++++++++++++- 2 files changed, 86 insertions(+), 24 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 5c7d7290..cf16ea1c 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -250,6 +250,7 @@ def __init__(self): # Select the appropriate scheduling algorithm for this simulator self.advance = self.default_scheduling_algorithm + self._is_reacting = False def default_scheduling_algorithm(self): """ @@ -318,18 +319,40 @@ def begin_test(self, trigger=None): self.advance() def react(self, trigger): - """React called when a trigger fires. + """ + Called when a trigger fires. + + We ensure that we only start the event loop once, rather than + letting it recurse. + """ + if self._is_reacting: + # queue up the trigger, the event loop will get to it + self._pending_triggers.append(trigger) + return + + # start the event loop + self._is_reacting = True + try: + self._event_loop(trigger) + finally: + self._is_reacting = False + + + def _event_loop(self, trigger): + """ + Run an event loop triggered by the given trigger. + + The loop will keep running until no further triggers fire. - We find any coroutines that are waiting on the particular trigger and - schedule them. + This should be triggered by only: + * The beginning of a test, when there is no trigger to react to + * A GPI trigger """ if _profiling: ctx = profiling_context() else: ctx = nullcontext() - called_by_simulator = isinstance(trigger, GPITrigger) - with ctx: # When a trigger fires it is unprimed internally if _debug: @@ -361,8 +384,6 @@ def react(self, trigger): self._readwrite.unprime() - # this should be the only trigger, probably? - assert not self._pending_triggers return # Similarly if we've scheduled our next_timestep on way to readwrite @@ -376,8 +397,6 @@ def react(self, trigger): "Priming ReadWrite trigger so we can playback writes") self._readwrite.prime(self.react) - # this should be the only trigger, probably? - assert not self._pending_triggers return # work through triggers one by one @@ -454,20 +473,18 @@ def react(self, trigger): if _debug: self.log.debug("Scheduled coroutine %s" % (coro.__name__)) - # Schedule may have queued up some events so we'll burn through those - while self._pending_events: - if _debug: - self.log.debug("Scheduling pending event %s" % - (str(self._pending_events[0]))) - self._pending_events.pop(0).set() - - # We only advance for GPI triggers - if called_by_simulator: - self.advance() + # Schedule may have queued up some events so we'll burn through those + while self._pending_events: + if _debug: + self.log.debug("Scheduling pending event %s" % + (str(self._pending_events[0]))) + self._pending_events.pop(0).set() - if _debug: - self.log.debug("All coroutines scheduled, handing control back" - " to simulator") + # no more pending triggers + self.advance() + if _debug: + self.log.debug("All coroutines scheduled, handing control back" + " to simulator") def unschedule(self, coro): diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 72fe8eb2..908e3f14 100755 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -39,7 +39,8 @@ import cocotb from cocotb.triggers import (Timer, Join, RisingEdge, FallingEdge, Edge, - ReadOnly, ReadWrite, ClockCycles, NextTimeStep) + ReadOnly, ReadWrite, ClockCycles, NextTimeStep, + NullTrigger) from cocotb.clock import Clock from cocotb.result import ReturnValue, TestFailure, TestError, TestSuccess from cocotb.utils import get_sim_time @@ -879,10 +880,54 @@ def test_exceptions(): def raise_soon(): yield Timer(10) raise ValueError('It is soon now') - + try: yield raise_soon() except ValueError: pass else: raise TestFailure("Exception was not raised") + +@cocotb.test() +def test_stack_overflow(dut): + """ + Test against stack overflows when starting many coroutines that terminate + before passing control to the simulator. + """ + @cocotb.coroutine + def null_coroutine(): + yield NullTrigger() + + for _ in range(10000): + yield null_coroutine() + + yield Timer(100) + + +@cocotb.test() +def test_immediate_coro(dut): + """ + Test that coroutines can return immediately + """ + # note: it seems that the test still has to yield at least once, even + # if the subroutines do not + yield Timer(1) + + @cocotb.coroutine + def immediate_value(): + raise ReturnValue(42) + yield + + @cocotb.coroutine + def immediate_exception(): + raise ValueError + yield + + assert (yield immediate_value()) == 42 + + try: + yield immediate_exception() + except ValueError: + pass + else: + raise TestFailure("Exception was not raised") From 5b4737b37fd8ded2f3904108d6d036cd0ddf5629 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 16 Mar 2019 17:29:07 -0700 Subject: [PATCH 1563/2656] Reject extra kwargs in BusDriver.__init__ Silently discarded misspelt kwargs is a recipe for disaster. This fixes #830 by rejecting the kwargs in BusDriver rather than accepting them in BusMonitor This also fixes the same problem in wavedrom --- cocotb/drivers/__init__.py | 6 +++++- cocotb/utils.py | 25 +++++++++++++++++++++++++ cocotb/wavedrom.py | 5 ++++- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index ca23d0b3..e4786c73 100755 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -39,6 +39,7 @@ from cocotb.bus import Bus from cocotb.log import SimLog from cocotb.result import ReturnValue +from cocotb.utils import reject_remaining_kwargs class BitDriver(object): @@ -220,11 +221,14 @@ class BusDriver(Driver): _optional_signals = [] def __init__(self, entity, name, clock, **kwargs): + # emulate keyword-only arguments in python 2 + index = kwargs.pop("array_idx", None) + reject_remaining_kwargs('__init__', kwargs) + self.log = SimLog("cocotb.%s.%s" % (entity._name, name)) Driver.__init__(self) self.entity = entity self.clock = clock - index = kwargs.get("array_idx") self.bus = Bus(self.entity, name, self._signals, self._optional_signals, array_idx=index) diff --git a/cocotb/utils.py b/cocotb/utils.py index 5d69e383..4317ac7a 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -511,6 +511,31 @@ def __exit__(self, *excinfo): pass +def reject_remaining_kwargs(name, kwargs): + """ + Helper function to emulate python 3 keyword-only arguments. + + Use as:: + + def func(x1, **kwargs): + a = kwargs.pop('a', 1) + b = kwargs.pop('b', 2) + reject_remaining_kwargs('func', kwargs) + ... + + To emulate the Python 3 syntax:: + + def func(x1, *, a=1, b=2): + ... + """ + if kwargs: + # match the error message to what python 3 produces + bad_arg = next(iter(kwargs)) + raise TypeError( + '{}() got an unexpected keyword argument {!r}'.format(name, bad_arg) + ) + + if __name__ == "__main__": import random a = "" diff --git a/cocotb/wavedrom.py b/cocotb/wavedrom.py index 0bccb90a..99711626 100644 --- a/cocotb/wavedrom.py +++ b/cocotb/wavedrom.py @@ -30,6 +30,7 @@ import cocotb from cocotb.bus import Bus from cocotb.triggers import RisingEdge, ReadOnly +from cocotb.utils import reject_remaining_kwargs class Wavedrom(object): @@ -140,7 +141,9 @@ class trace(object): """ def __init__(self, *args, **kwargs): - self._clock = kwargs.get("clk", None) + # emulate keyword-only arguments in python 2 + self._clock = kwargs.pop("clk", None) + reject_remaining_kwargs('__init__', kwargs) self._signals = [] for arg in args: From 68f979d8e67cf173e314129b2c55772e73553aff Mon Sep 17 00:00:00 2001 From: JarrettR Date: Tue, 19 Jul 2016 21:12:37 -0700 Subject: [PATCH 1564/2656] Improved WaveDrom header/footer handling, added config handler --- cocotb/wavedrom.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cocotb/wavedrom.py b/cocotb/wavedrom.py index 99711626..a8aad6d6 100644 --- a/cocotb/wavedrom.py +++ b/cocotb/wavedrom.py @@ -196,14 +196,16 @@ def write(self, filename, **kwargs): with open(filename, "w") as f: f.write(self.dumpj(**kwargs)) - def dumpj(self, header="", footer=""): + def dumpj(self, header="", footer="", config=""): trace = {"signal": []} trace["signal"].append( {"name": "clock", "wave": "p" + "."*(self._clocks-1)}) for sig in self._signals: trace["signal"].extend(sig.get(add_clock=False)) if header: - trace["head"] = {"text": header} + trace["head"] = header if footer: - trace["foot"] = {"text": footer} + trace["foot"] = footer + if config: + trace["config"] = config return json.dumps(trace, indent=4, sort_keys=False) From 0142c57af5e30ed714f3576267a0ced70580dcf7 Mon Sep 17 00:00:00 2001 From: JarrettR Date: Tue, 2 Aug 2016 22:32:29 -0700 Subject: [PATCH 1565/2656] Made backwards compatible with simplified input --- cocotb/wavedrom.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cocotb/wavedrom.py b/cocotb/wavedrom.py index a8aad6d6..be4428df 100644 --- a/cocotb/wavedrom.py +++ b/cocotb/wavedrom.py @@ -203,9 +203,15 @@ def dumpj(self, header="", footer="", config=""): for sig in self._signals: trace["signal"].extend(sig.get(add_clock=False)) if header: - trace["head"] = header + if isinstance(header, dict): + trace["head"] = header + else: + trace["head"] = {"text": header} if footer: - trace["foot"] = footer + if isinstance(footer, dict): + trace["foot"] = footer + else: + trace["foot"] = {"text": footer} if config: trace["config"] = config return json.dumps(trace, indent=4, sort_keys=False) From cec5ce4bb96370a7b4b1dd9da6e3a85e23a88f8f Mon Sep 17 00:00:00 2001 From: JarrettR Date: Thu, 10 Aug 2017 16:39:32 -0700 Subject: [PATCH 1566/2656] Updated example with advanced wavedrom options --- examples/endian_swapper/tests/test_endian_swapper.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/endian_swapper/tests/test_endian_swapper.py b/examples/endian_swapper/tests/test_endian_swapper.py index 7f3a1def..6d07177c 100644 --- a/examples/endian_swapper/tests/test_endian_swapper.py +++ b/examples/endian_swapper/tests/test_endian_swapper.py @@ -180,7 +180,7 @@ def randomly_switch_config(csr): @cocotb.test() def wavedrom_test(dut): """ - Generate a JSON wavedrom diagram of a trace + Generate a JSON wavedrom diagram of a trace and save it to wavedrom.json """ cocotb.fork(Clock(dut.clk,5000).start()) yield RisingEdge(dut.clk) @@ -192,4 +192,5 @@ def wavedrom_test(dut): yield tb.csr.read(0) yield RisingEdge(dut.clk) yield RisingEdge(dut.clk) - dut._log.info(waves.dumpj()) + dut._log.info(waves.dumpj(header = {'text':'WaveDrom example', 'tick':0})) + waves.write('wavedrom.json', header = {'tick':0}, config = {'hscale':3}) From 6d11544ae52ff7bb7616546128f858d391d3e022 Mon Sep 17 00:00:00 2001 From: LANCE E EFTINK Date: Thu, 21 Feb 2019 17:50:59 -0700 Subject: [PATCH 1567/2656] Fix test_integers Remove the double test decorator causing incorrect data in the results.xml --- tests/test_cases/issue_134/test_integers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_cases/issue_134/test_integers.py b/tests/test_cases/issue_134/test_integers.py index a51e0f99..517186be 100644 --- a/tests/test_cases/issue_134/test_integers.py +++ b/tests/test_cases/issue_134/test_integers.py @@ -10,7 +10,6 @@ @cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) -@cocotb.test() def test_integer(dut): """ Test access to integers From 2af39722bad780de3c45a5f95c06420c48932617 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Mon, 30 Jul 2018 00:28:34 -0700 Subject: [PATCH 1568/2656] Add support for async/await This only aims for minimal support via `cocotb.coroutine`s. The `__await__` method can be understood as "convert this object into a plain generator object that can be used by the scheduler". For `Trigger` objects, this is just a generator that yields the trigger and returns its result. This should not be confused with having any support added for the builtin `asyncio` module - this adds support only for syntax, not for a different scheduler. --- cocotb/decorators.py | 26 ++++- cocotb/triggers.py | 12 +- documentation/source/coroutines.rst | 62 ++++++++++ tests/test_cases/test_cocotb/test_cocotb.py | 3 + .../test_cases/test_cocotb/test_cocotb_35.py | 109 ++++++++++++++++++ 5 files changed, 209 insertions(+), 3 deletions(-) create mode 100644 tests/test_cases/test_cocotb/test_cocotb_35.py diff --git a/cocotb/decorators.py b/cocotb/decorators.py index ab7c2ac0..b8a857ba 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -33,6 +33,8 @@ import pdb import functools import threading +import inspect +import textwrap from io import StringIO, BytesIO @@ -41,7 +43,7 @@ from cocotb.triggers import Join, PythonTrigger, Timer, Event, NullTrigger from cocotb.result import (TestComplete, TestError, TestFailure, TestSuccess, ReturnValue, raise_error, ExternalException) -from cocotb.utils import get_sim_time, with_metaclass +from cocotb.utils import get_sim_time, with_metaclass, exec_ from cocotb import outcomes @@ -89,7 +91,13 @@ def __init__(self, inst, parent): self.log = SimLog("cocotb.coroutine.%s" % self.__name__, id(self)) else: self.log = SimLog("cocotb.coroutine.fail") - self._coro = inst + + if sys.version_info[:2] >= (3, 5) and inspect.iscoroutine(inst): + self._natively_awaitable = True + self._coro = inst.__await__() + else: + self._natively_awaitable = False + self._coro = inst self._started = False self._callbacks = [] self._parent = parent @@ -172,6 +180,20 @@ def __nonzero__(self): otherwise return true""" return not self._finished + # Needs `yield from` syntax to implement this correctly. + # Once 2.7 is dropped, this can be run unconditionally + if sys.version_info >= (3, 3): + exec_(textwrap.dedent(""" + def __await__(self): + if self._natively_awaitable: + # use the native trampoline, which will only hand back nested + # non-natively awaitable objects + return (yield from self._coro) + else: + # hand the coroutine back to the scheduler trampoline + return (yield self) + """)) + __bool__ = __nonzero__ def sort_name(self): diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 2756df78..a3209655 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -29,6 +29,8 @@ import os import weakref +import sys +import textwrap if "COCOTB_SIM" in os.environ: import simulator @@ -39,7 +41,7 @@ from cocotb.result import raise_error from cocotb.utils import ( get_sim_steps, get_time_from_sim_steps, with_metaclass, - ParametrizedSingleton + ParametrizedSingleton, exec_ ) from cocotb import outcomes @@ -74,6 +76,14 @@ def __str__(self): def _outcome(self): return outcomes.Value(self) + # Once 2.7 is dropped, this can be run unconditionally + if sys.version_info >= (3, 3): + exec_(textwrap.dedent(""" + def __await__(self): + # hand the trigger back to the scheduler trampoline + return (yield self) + """)) + class PythonTrigger(Trigger): """Python triggers don't use GPI at all. diff --git a/documentation/source/coroutines.rst b/documentation/source/coroutines.rst index e382e772..ef7ac782 100644 --- a/documentation/source/coroutines.rst +++ b/documentation/source/coroutines.rst @@ -151,3 +151,65 @@ they'd naturally end. if not isclose(edge_time_ns, start_time_ns + 4.0): raise TestFailure("Expected a period of 4 ns") + +Async functions +--------------- + +Python 3.5 introduces :keyword:`async` functions, which provide an alternative +syntax. For example: + +.. code-block:: python + + @cocotb.coroutine + async def wait_10ns(): + cocotb.log.info("About to wait for 10 ns") + await Timer(10000) + cocotb.log.info("Simulation time has advanced by 10 ns") + +To wait on a trigger or a nested coroutine, these use :keyword:`await` instead +of :keyword:`yield`. Provided they are decorated with ``@cocotb.coroutine``, +``async def`` functions using :keyword:`await` and regular functions using +:keyword:`yield` can be used interchangeable - the appropriate keyword to use +is determined by which type of function it appears in, not by the +sub-coroutinue being called. + +..note:: + + It is currently not possible to ``await`` a list of triggers as can be done + in a ``yield``-based coroutine with ``yield [trig1, trig2]``. + Until this becomes possible, a simple workaround is to create a helper + function like: + + .. code-block:: python + + @cocotb.coroutine + def first_of(triggers): + return (yield triggers) + + which thanks to the interoperability between the two types of coroutinue, + can then be used as ``await first_of([trig1, trig2])``. + + +Async generators +~~~~~~~~~~~~~~~~ + +In Python 3.6, a ``yield`` statement within an ``async`` function has a new +meaning (rather than being a ``SyntaxError``) which matches the typical meaning +of ``yield`` within regular python code. It can be used to create a special +type of generator function that can be iterated with ``async for``: + +.. code-block:: python + + async def ten_samples_of(clk, signal): + for i in range(10): + await RisingEdge(clk) + yield signal.value # this means "send back to the for loop" + + @cocotb.test() + async def test_samples_are_even(dut): + async for sample in ten_samples_of(dut.clk, dut.signal): + assert sample % 2 == 0 + +More details on this type of generator can be found in `PEP 525`_. + +.. PEP 525: https://www.python.org/dev/peps/pep-0525/ diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 908e3f14..dab6b3b7 100755 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -931,3 +931,6 @@ def immediate_exception(): pass else: raise TestFailure("Exception was not raised") + +if sys.version_info[:2] >= (3, 5): + from test_cocotb_35 import * diff --git a/tests/test_cases/test_cocotb/test_cocotb_35.py b/tests/test_cases/test_cocotb/test_cocotb_35.py new file mode 100644 index 00000000..455b8d14 --- /dev/null +++ b/tests/test_cases/test_cocotb/test_cocotb_35.py @@ -0,0 +1,109 @@ +""" +This file contains tests that use syntax introduced in Python 3.5. + +This is likely to mean this file only tests `async def` functions. +""" + +import cocotb +from cocotb.triggers import Timer +from cocotb.outcomes import Value, Error +from cocotb.result import TestFailure + + +class produce: + """ Test helpers that produce a value / exception in different ways """ + @staticmethod + @cocotb.coroutine + def coro(outcome): + yield Timer(1) + return outcome.get() + + @staticmethod + @cocotb.coroutine + async def async_annotated(outcome): + await Timer(1) + return outcome.get() + + @staticmethod + async def async_(outcome): + await Timer(1) + return outcome.get() + + +class SomeException(Exception): + """ Custom exception to test for that can't be thrown by internals """ + pass + + +# just to be sure... +@cocotb.test(expect_fail=True) +async def test_async_test_can_fail(dut): + await Timer(1) + raise TestFailure + + +@cocotb.test() +def test_annotated_async_from_coro(dut): + """ + Test that normal coroutines are able to call async functions annotated + with `@cocotb.coroutine` + """ + v = yield produce.async_annotated(Value(1)) + assert v == 1 + + try: + yield produce.async_annotated(Error(SomeException)) + except SomeException: + pass + else: + assert False + + +@cocotb.test() +async def test_annotated_async_from_async(dut): + """ Test that async coroutines are able to call themselves """ + v = await produce.async_annotated(Value(1)) + assert v == 1 + + try: + await produce.async_annotated(Error(SomeException)) + except SomeException: + pass + else: + assert False + + +@cocotb.test() +async def test_annotated_async_from_async(dut): + """ Test that async coroutines are able to call raw async functions """ + v = await produce.async_(Value(1)) + assert v == 1 + + try: + await produce.async_(Error(SomeException)) + except SomeException: + pass + else: + assert False + + +@cocotb.test() +async def test_coro_from_async(dut): + """ Test that async coroutines are able to call regular ones """ + v = await produce.coro(Value(1)) + assert v == 1 + + try: + await produce.coro(Error(SomeException)) + except SomeException: + pass + else: + assert False + + +@cocotb.test() +async def test_trigger_await_gives_self(dut): + """ Test that await returns the trigger itself for triggers """ + t = Timer(1) + t2 = await t + assert t2 is t From a59cd930a7cc31b8c90e6a75346b35586cb00b21 Mon Sep 17 00:00:00 2001 From: LANCE E EFTINK Date: Thu, 21 Feb 2019 18:04:05 -0700 Subject: [PATCH 1569/2656] Fix test issue_768 The Binary value did not have the correct number of bits --- tests/test_cases/issue_768_b/issue_768.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_cases/issue_768_b/issue_768.py b/tests/test_cases/issue_768_b/issue_768.py index 4ba8eb6a..8e653227 100644 --- a/tests/test_cases/issue_768_b/issue_768.py +++ b/tests/test_cases/issue_768_b/issue_768.py @@ -10,11 +10,11 @@ from cocotb.binary import BinaryValue # this line is different between the two files -value = BinaryValue(0) +value = BinaryValue(0, n_bits=8) @cocotb.test() def do_test(dut): dut.stream_in_data.setimmediatevalue(value) yield Timer(1) assert dut.stream_in_data.value == 0 - yield ReadOnly() \ No newline at end of file + yield ReadOnly() From 62eb8ee7e6729d954fe93dbc915cb1f03b744c96 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 17 Mar 2019 11:10:01 -0700 Subject: [PATCH 1570/2656] Add missing dut argument to test Without it this test was being dropped with a warning, and never actually run. --- tests/test_cases/test_cocotb/test_cocotb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index dab6b3b7..bf04c3e8 100755 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -875,7 +875,7 @@ def return_it(x): @cocotb.test() -def test_exceptions(): +def test_exceptions(dut): @cocotb.coroutine def raise_soon(): yield Timer(10) From 9f98023431a6e94b0f06bf5265c543a5673f17cd Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 17 Mar 2019 22:38:06 -0700 Subject: [PATCH 1571/2656] Remove an incorrect optimization in __await__ Directly yielding from the inner coroutine to bypasses the scheduler turns out to break parts of the public API. This adds a previously-failing test, and fixes it. If you want the efficiency of bypassing the scheduler, then the answer is simply to not attach a `@coroutine` decorator. --- cocotb/decorators.py | 17 +++++++++-------- tests/test_cases/test_cocotb/test_cocotb_35.py | 9 +++++++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index b8a857ba..204c3e8f 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -180,18 +180,19 @@ def __nonzero__(self): otherwise return true""" return not self._finished - # Needs `yield from` syntax to implement this correctly. # Once 2.7 is dropped, this can be run unconditionally if sys.version_info >= (3, 3): exec_(textwrap.dedent(""" def __await__(self): - if self._natively_awaitable: - # use the native trampoline, which will only hand back nested - # non-natively awaitable objects - return (yield from self._coro) - else: - # hand the coroutine back to the scheduler trampoline - return (yield self) + # It's tempting to use `return (yield from self._coro)` here, + # which bypasses the scheduler. Unfortunately, this means that + # we can't keep track of the result or state of the coroutine, + # things which we expose in our public API. If you want the + # efficiency of bypassing the scheduler, remove the `@coroutine` + # decorator from your `async` functions. + + # Hand the coroutine back to the scheduler trampoline. + return (yield self) """)) __bool__ = __nonzero__ diff --git a/tests/test_cases/test_cocotb/test_cocotb_35.py b/tests/test_cases/test_cocotb/test_cocotb_35.py index 455b8d14..b1bf2359 100644 --- a/tests/test_cases/test_cocotb/test_cocotb_35.py +++ b/tests/test_cases/test_cocotb/test_cocotb_35.py @@ -107,3 +107,12 @@ async def test_trigger_await_gives_self(dut): t = Timer(1) t2 = await t assert t2 is t + + +@cocotb.test() +async def test_await_causes_start(dut): + """ Test that an annotated async coroutine gets marked as started """ + coro = produce.async_annotated(Value(1)) + assert not coro.has_started() + await coro + assert coro.has_started() From 0fd76bbc0dfe7a4c4a4161810c30ed12e75ec2f5 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Mon, 11 Mar 2019 13:48:49 -0700 Subject: [PATCH 1572/2656] Add argv information for VHPI Get argc/argv information from vhpiTool class. Construct argv pointer array and free after gpi_embed_init() call. Add VHDL case to test_plusargs and make test fail if +foo=bar not passed to plusargs. Enables use of cocotb.plusargs when using VHPI. --- cocotb/share/lib/vhpi/VhpiCbHdl.cpp | 40 +++++++++++++++---- .../share/makefiles/simulators/Makefile.aldec | 5 ++- tests/designs/plusargs_module/Makefile | 20 ++++------ tests/designs/plusargs_module/tb_top.vhd | 17 ++++++++ tests/test_cases/test_plusargs/plusargs.py | 4 ++ 5 files changed, 65 insertions(+), 21 deletions(-) create mode 100644 tests/designs/plusargs_module/tb_top.vhd diff --git a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp index baf20174..06477ee6 100644 --- a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp +++ b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp @@ -25,6 +25,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ +#include #include "VhpiImpl.h" extern "C" void handle_vhpi_callback(const vhpiCbDataT *cb_data); @@ -764,16 +765,39 @@ VhpiStartupCbHdl::VhpiStartupCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), cb_data.reason = vhpiCbStartOfSimulation; } -int VhpiStartupCbHdl::run_callback(void) { +int VhpiStartupCbHdl::run_callback() { + vhpiHandleT tool, argv_iter, argv_hdl; gpi_sim_info_t sim_info; - sim_info.argc = 0; - sim_info.argv = NULL; - sim_info.product = gpi_copy_name(vhpi_get_str(vhpiNameP, NULL)); - sim_info.version = gpi_copy_name(vhpi_get_str(vhpiToolVersionP, NULL)); - gpi_embed_init(&sim_info); + char **tool_argv = NULL; + uint32_t tool_argc = 0; + int i = 0; + + tool = vhpi_handle(vhpiTool, NULL); + + sim_info.product = const_cast(static_cast(vhpi_get_str(vhpiNameP, tool))); + sim_info.version = const_cast(static_cast(vhpi_get_str(vhpiToolVersionP, tool))); + + if (tool) { + tool_argc = vhpi_get(vhpiArgcP, tool); + tool_argv = (char **)malloc(sizeof(char *) * tool_argc); + assert(tool_argv); + + argv_iter = vhpi_iterator(vhpiArgvs, tool); + if (argv_iter) { + while (argv_hdl = vhpi_scan(argv_iter)) { + tool_argv[i] = const_cast(static_cast(vhpi_get_str(vhpiStrValP, argv_hdl))); + i++; + } + vhpi_release_handle(argv_iter); + } + sim_info.argc = tool_argc; + sim_info.argv = tool_argv; - free(sim_info.product); - free(sim_info.version); + vhpi_release_handle(tool); + } + + gpi_embed_init(&sim_info); + free(tool_argv); return 0; } diff --git a/cocotb/share/makefiles/simulators/Makefile.aldec b/cocotb/share/makefiles/simulators/Makefile.aldec index 35df08b1..c3e6ac5d 100644 --- a/cocotb/share/makefiles/simulators/Makefile.aldec +++ b/cocotb/share/makefiles/simulators/Makefile.aldec @@ -58,6 +58,9 @@ ALOG_ARGS += $(COMPILE_ARGS) ACOM_ARGS += $(COMPILE_ARGS) ASIM_ARGS += $(SIM_ARGS) +# Plusargs need to be passed to ASIM command not vsimsa +ASIM_ARGS += $(PLUSARGS) + RTL_LIBRARY ?= work ALOG_ARGS += +define+COCOTB_SIM -dbg -pli libgpi ACOM_ARGS += -dbg @@ -171,7 +174,7 @@ endif # that turns on batch mode (i.e. exit on completion/error) results.xml: $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(COCOTB_VPI_LIB) $(CUSTOM_SIM_DEPS) set -o pipefail; cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(PWD):$(NEW_PYTHONPATH) GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 $(CMD) $(PLUSARGS) -do runsim.tcl | tee sim.log + $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 $(CMD) -do runsim.tcl | tee sim.log clean:: @rm -rf $(SIM_BUILD) diff --git a/tests/designs/plusargs_module/Makefile b/tests/designs/plusargs_module/Makefile index 7a795e6d..fc736882 100644 --- a/tests/designs/plusargs_module/Makefile +++ b/tests/designs/plusargs_module/Makefile @@ -29,15 +29,7 @@ TOPLEVEL_LANG ?= verilog -ifneq ($(TOPLEVEL_LANG),verilog) - -all: - @echo "Skipping test due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being verilog" -clean:: - -else - -TOPLEVEL = tb_top +TOPLEVEL := tb_top ifeq ($(OS),Msys) WPWD=$(shell sh -c 'pwd -W') @@ -47,9 +39,13 @@ endif COCOTB?=$(WPWD)/../../.. -VERILOG_SOURCES = $(COCOTB)/tests/designs/plusargs_module/tb_top.v +ifeq ($(TOPLEVEL_LANG),verilog) + VERILOG_SOURCES = $(COCOTB)/tests/designs/plusargs_module/tb_top.v +else ifeq ($(TOPLEVEL_LANG),vhdl) + VHDL_SOURCES = $(COCOTB)/tests/designs/plusargs_module/tb_top.vhd +else + $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") +endif include $(COCOTB)/makefiles/Makefile.inc include $(COCOTB)/makefiles/Makefile.sim - -endif diff --git a/tests/designs/plusargs_module/tb_top.vhd b/tests/designs/plusargs_module/tb_top.vhd new file mode 100644 index 00000000..952c736e --- /dev/null +++ b/tests/designs/plusargs_module/tb_top.vhd @@ -0,0 +1,17 @@ +library ieee; + +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity tb_top is +end; + +architecture impl of tb_top is + signal dummy_sig : std_logic := '0'; +begin + process + begin + wait for 10ns; + dummy_sig <= '1'; + end process; +end architecture; diff --git a/tests/test_cases/test_plusargs/plusargs.py b/tests/test_cases/test_plusargs/plusargs.py index a7592e11..09298cd3 100644 --- a/tests/test_cases/test_plusargs/plusargs.py +++ b/tests/test_cases/test_plusargs/plusargs.py @@ -32,6 +32,7 @@ import cocotb from cocotb.decorators import coroutine +from cocotb.result import TestFailure from cocotb.triggers import Timer, Edge, Event import sys @@ -45,5 +46,8 @@ def plusargs_test(dut): for name in cocotb.plusargs: print("COCOTB:", name, cocotb.plusargs[name]) + + if cocotb.plusargs['foo'] != 'bar': + raise TestFailure("plusargs 'foo' value '{}' does not match expected 'bar'".format(cocotb.plusargs['foo'])) yield Timer(10000) From 2fe76cabb4808deb7b437a419334b353f8ee63ac Mon Sep 17 00:00:00 2001 From: Marlon James Date: Tue, 12 Mar 2019 15:03:48 -0700 Subject: [PATCH 1573/2656] Fix __check_vhpi_error to log to correct level --- cocotb/share/lib/vhpi/VhpiImpl.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cocotb/share/lib/vhpi/VhpiImpl.h b/cocotb/share/lib/vhpi/VhpiImpl.h index f5660ac8..d2ecff03 100644 --- a/cocotb/share/lib/vhpi/VhpiImpl.h +++ b/cocotb/share/lib/vhpi/VhpiImpl.h @@ -45,15 +45,15 @@ // Should be run after every VHPI call to check error status static inline int __check_vhpi_error(const char *file, const char *func, long line) { - int level=0; + int err_occurred = 0; #if VHPI_CHECKING vhpiErrorInfoT info; int loglevel; - level = vhpi_check_error(&info); - if (level == 0) + err_occurred = vhpi_check_error(&info); + if (!err_occurred) return 0; - switch (level) { + switch (info.severity) { case vhpiNote: loglevel = GPIInfo; break; @@ -75,7 +75,7 @@ static inline int __check_vhpi_error(const char *file, const char *func, long li info.severity, info.message, info.file, info.line); #endif - return level; + return err_occurred; } #define check_vhpi_error() do { \ From 244eb3997e8ae212bd8b2ed7204d5dc4577a1f01 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Mon, 11 Mar 2019 14:22:59 -0700 Subject: [PATCH 1574/2656] Change gpi_log() formatting to match Python Make gpi_log() formatting match Python SimLogFormatter.format(). It now properly truncates long path names to show ".." followed by the last 18 characters. fprintf formatting strings will not truncate passed in strings, but will print the entire string until the null terminator. Changed Python logging to print 6 digits of sim time nanoseconds. --- cocotb/log.py | 2 +- cocotb/share/lib/gpi_log/gpi_logging.c | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/cocotb/log.py b/cocotb/log.py index d56d9964..52ca1205 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -169,7 +169,7 @@ def rjust(string, chars): def _format(self, level, record, msg, coloured=False): time_ns = get_sim_time('ns') simtime = "%6.2fns" % (time_ns) - prefix = simtime.rjust(10) + ' ' + level + ' ' + prefix = simtime.rjust(11) + ' ' + level + ' ' if not _suppress: prefix += self.ljust(record.name, _RECORD_CHARS) + \ self.rjust(os.path.split(record.filename)[1], _FILENAME_CHARS) + \ diff --git a/cocotb/share/lib/gpi_log/gpi_logging.c b/cocotb/share/lib/gpi_log/gpi_logging.c index 751b6e0e..53648ef8 100644 --- a/cocotb/share/lib/gpi_log/gpi_logging.c +++ b/cocotb/share/lib/gpi_log/gpi_logging.c @@ -118,14 +118,21 @@ void gpi_log(const char *name, long level, const char *pathname, const char *fun n = vsnprintf(log_buff, LOG_SIZE, msg, ap); va_end(ap); - if (0 > n) { + if (n < 0) { fprintf(stderr, "Log message construction failed\n"); } - + fprintf(stdout, " -.--ns "); fprintf(stdout, "%-9s", log_level(level)); fprintf(stdout, "%-35s", name); - fprintf(stdout, "%20s:", pathname); + + n = strlen(pathname); + if (n > 20) { + fprintf(stdout, "..%18s:", (pathname + (n - 18))); + } else { + fprintf(stdout, "%20s:", pathname); + } + fprintf(stdout, "%-4ld", lineno); fprintf(stdout, " in %-31s ", funcname); fprintf(stdout, "%s", log_buff); From 353bc83959e5eb265e4ff5c5cb0d7eab4c29735e Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Tue, 19 Mar 2019 09:36:04 +0100 Subject: [PATCH 1575/2656] Update README.md with install instruction (#876) * Update README.md with install instruction --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index f757066b..b3b01ca3 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,9 @@ # Checkout git repositories git clone https://github.com/potentialventures/cocotb.git + # Install cocotb + pip install ./cocotb + # Run the tests... cd cocotb/examples/endian_swapper/tests make From 751b9ae4419b24ed872e1388e2f49232b327bcc8 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Tue, 19 Mar 2019 16:05:36 +0100 Subject: [PATCH 1576/2656] Run VHDL tests in travis with GHDL The tests are currently failing until #875 is fixed. --- .travis.yml | 31 +++++++++++++++++++++++++------ tox.ini | 3 +++ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 06db683b..efdc813b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,10 @@ # See Dockerfile for the full build instructions sudo: required language: python -cache: pip dist: xenial +cache: + directories: + - iverilog git: quiet: true @@ -17,7 +19,7 @@ jobs: python: 3.5 install: pip install flake8 script: flake8 --exit-zero cocotb/ - + - stage: SimTests os: linux python: 2.7 @@ -30,7 +32,7 @@ jobs: - echo 'Running cocotb tests with Python 2.7 ...' && echo 'travis_fold:start:cocotb_py27' - docker exec -it -e TRAVIS=true cocotb bash -lc 'pip install tox; cd /src; tox -e py27' - echo -e 'travis_fold:end:cocotb_py27' - + - stage: SimTests os: linux python: 3.5 @@ -44,7 +46,7 @@ jobs: python: 3.7 before_install: &travis_linx_prep - sudo apt-get install gperf - - git clone https://github.com/steveicarus/iverilog.git --depth=1 --branch v10_2 + - if [[ ! -e "./iverilog/README.txt" ]]; then rm -rf iverilog; git clone https://github.com/steveicarus/iverilog.git --depth=1 --branch v10_2; fi - cd iverilog && autoconf && ./configure && make -j2 && sudo make install && cd .. - pip install tox - pyenv global system $TRAVIS_PYTHON_VERSION @@ -52,7 +54,7 @@ jobs: - echo 'Running cocotb tests with Python 3.7 ...' && echo -en 'travis_fold:start:cocotb_py37' - tox -e py37 - echo 'travis_fold:end:cocotb_py37' - + - stage: SimTests python: 3.6.7 before_install: *travis_linx_prep @@ -60,7 +62,7 @@ jobs: - echo 'Running cocotb tests with Python 3.6.7 ...' && echo -en 'travis_fold:start:cocotb_py36' - tox -e py36 - echo 'travis_fold:end:cocotb_py36' - + - stage: SimTests os: osx language: generic @@ -78,3 +80,20 @@ jobs: before_install: *osx_prep script: - tox -e py36 + + - stage: SimTests + python: 3.7 + env: SIM=ghdl + before_install: + - sudo apt-get install gnat + - git clone https://github.com/ghdl/ghdl.git --depth=1 --branch v0.36 + - cd ghdl && ./configure && make -j2 && sudo make install && cd .. + - pip install tox + - pyenv global system $TRAVIS_PYTHON_VERSION + script: + - tox -e py37 + + allow_failures: + - stage: SimTests + python: 3.7 + env: SIM=ghdl diff --git a/tox.ini b/tox.ini index f4cb8cca..e8cb4245 100644 --- a/tox.ini +++ b/tox.ini @@ -2,6 +2,9 @@ envlist = py27,py35 [testenv] +passenv = + SIM + deps = coverage xunitparser From cbe03d3598f94a678a63251cb3c956bc55cee39c Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Tue, 19 Mar 2019 14:29:58 +0100 Subject: [PATCH 1577/2656] Add windows build to travis (conda based) --- .travis.yml | 9 +++++++++ Dockerfile.windows | 15 +++++++++++++++ cocotb/share/makefiles/simulators/Makefile.icarus | 3 ++- 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 Dockerfile.windows diff --git a/.travis.yml b/.travis.yml index efdc813b..c765ff8a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -73,6 +73,15 @@ jobs: script: - tox -e py27 + - stage: SimTests + python: 3.7 + os: windows + language: shell + before_install: + - docker build -t cocotb -f Dockerfile.windows . + script: + - docker run cocotb powershell 'python setup.py install ; make test' + - stage: SimTests os: osx language: generic diff --git a/Dockerfile.windows b/Dockerfile.windows new file mode 100644 index 00000000..2b74954a --- /dev/null +++ b/Dockerfile.windows @@ -0,0 +1,15 @@ +FROM mcr.microsoft.com/windowsservercore:1803 + +SHELL ["PowerShell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'Continue'; $verbosePreference='Continue';"] + +COPY . /src +WORKDIR "c:\src" + +RUN Invoke-WebRequest -outfile miniconda3.exe https://repo.continuum.io/miniconda/Miniconda3-latest-Windows-x86_64.exe +RUN Start-Process .\miniconda3.exe -ArgumentList '/S /D=C:\miniconda3' -Wait +RUN [System.Environment]::SetEnvironmentVariable('Path', $env:Path + ';C:\miniconda3;C:\miniconda3\Library\mingw-w64\bin;C:\miniconda3\Library\usr\bin;C:\miniconda3\Library\bin;C:\miniconda3\Scripts;C:\miniconda3\bin;C:\miniconda3\condabin', 'Machine') +RUN conda install --yes -c msys2 m2-base m2-make m2w64-toolchain libpython + +RUN Invoke-WebRequest -Uri http://bleyer.org/icarus/iverilog-10.1.1-x64_setup.exe -OutFile iverilog-10.1.1-x64_setup.exe +RUN Start-Process .\iverilog-10.1.1-x64_setup -ArgumentList '/VERYSILENT' -Wait +RUN [System.Environment]::SetEnvironmentVariable('Path', $env:Path+';C:\iverilog\bin', 'Machine') diff --git a/cocotb/share/makefiles/simulators/Makefile.icarus b/cocotb/share/makefiles/simulators/Makefile.icarus index 5d031874..eac5067b 100644 --- a/cocotb/share/makefiles/simulators/Makefile.icarus +++ b/cocotb/share/makefiles/simulators/Makefile.icarus @@ -68,7 +68,8 @@ EXTRA_LIBS := -lvpi EXTRA_LIBDIRS := -L$(ICARUS_BIN_DIR)/../lib OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') LIB_LOAD = PATH=$(OLD_PATH):$(LIB_DIR) -NEW_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-zA-Z]\):\//\/\1\//g' -e 's/;/:/g') +NEW_PYTHONPATH:=$(shell python -c "import sys, os; print(':'.join(['/'+dir.replace(os.sep,'/').replace(':','') for dir in sys.path]))") +PWD = $(shell pwd) else LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) From 024ab22ed08627cb9c8457f75d735e9cda43535f Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Wed, 20 Mar 2019 11:50:24 +0000 Subject: [PATCH 1578/2656] Docs: Use RTD theme in local builds Currently we use the default Sphinx theme for local builds, and the ReadTheDocs theme online. Use the same theme in both cases to make it easier to see locally how the documentation will look online. --- documentation/source/conf.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/documentation/source/conf.py b/documentation/source/conf.py index 892fafe0..0b5e8ed0 100644 --- a/documentation/source/conf.py +++ b/documentation/source/conf.py @@ -113,7 +113,24 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' + +# The Read the Docs theme is available from +# https://github.com/snide/sphinx_rtd_theme +# +# Install with +# - pip install sphinx_rtd_theme +# or +# - apt-get install python-sphinx-rtd-theme + +try: + import sphinx_rtd_theme + html_theme = 'sphinx_rtd_theme' +except ImportError: + sys.stderr.write('Warning: The Sphinx \'sphinx_rtd_theme\' HTML theme was '+ + 'not found. Make sure you have the theme installed to produce pretty '+ + 'HTML output. Falling back to the default theme.\n') + + html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the From b236a41575c8cd80a7e721205b83f9f699a888b3 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Wed, 20 Mar 2019 11:51:24 +0000 Subject: [PATCH 1579/2656] Docs: Fix inline link --- documentation/source/coroutines.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/documentation/source/coroutines.rst b/documentation/source/coroutines.rst index ef7ac782..b779cd0f 100644 --- a/documentation/source/coroutines.rst +++ b/documentation/source/coroutines.rst @@ -210,6 +210,5 @@ type of generator function that can be iterated with ``async for``: async for sample in ten_samples_of(dut.clk, dut.signal): assert sample % 2 == 0 -More details on this type of generator can be found in `PEP 525`_. +More details on this type of generator can be found in :pep:`525`. -.. PEP 525: https://www.python.org/dev/peps/pep-0525/ From 5ac9bfbd7cd3b1257c313400919673811e865a45 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Wed, 20 Mar 2019 11:51:35 +0000 Subject: [PATCH 1580/2656] Docs: Switch to Python 3 syntax highlighting With our docs now using Python 3 keywords we need to switch the source code highlighting from python (for Python 2) to python3. Fixes #871 --- documentation/source/coroutines.rst | 20 ++++++++++---------- documentation/source/endian_swapper.rst | 16 ++++++++-------- documentation/source/examples.rst | 2 +- documentation/source/hal_cosimulation.rst | 4 ++-- documentation/source/ping_tun_tap.rst | 8 ++++---- documentation/source/quickstart.rst | 12 ++++++------ documentation/source/simulator_support.rst | 2 +- documentation/source/testbench_tools.rst | 14 +++++++------- 8 files changed, 39 insertions(+), 39 deletions(-) diff --git a/documentation/source/coroutines.rst b/documentation/source/coroutines.rst index b779cd0f..dd7deea3 100644 --- a/documentation/source/coroutines.rst +++ b/documentation/source/coroutines.rst @@ -10,7 +10,7 @@ Typically coroutines :keyword:`yield` a :any:`Trigger` object which indicates to the simulator some event which will cause the coroutine to be woken when it occurs. For example: -.. code-block:: python +.. code-block:: python3 @cocotb.coroutine def wait_10ns(): @@ -20,7 +20,7 @@ when it occurs. For example: Coroutines may also yield other coroutines: -.. code-block:: python +.. code-block:: python3 @cocotb.coroutine def wait_100ns(): @@ -30,7 +30,7 @@ Coroutines may also yield other coroutines: Coroutines can return a value, so that they can be used by other coroutines. Before Python 3.3, this requires a :any:`ReturnValue` to be raised. -.. code-block:: python +.. code-block:: python3 @cocotb.coroutine def get_signal(clk, signal): @@ -54,7 +54,7 @@ Before Python 3.3, this requires a :any:`ReturnValue` to be raised. Coroutines may also yield a list of triggers and coroutines to indicate that execution should resume if *any* of them fires: -.. code-block:: python +.. code-block:: python3 @cocotb.coroutine def packet_with_timeout(monitor, timeout): @@ -65,7 +65,7 @@ execution should resume if *any* of them fires: The trigger that caused execution to resume is passed back to the coroutine, allowing them to distinguish which trigger fired: -.. code-block:: python +.. code-block:: python3 @cocotb.coroutine def packet_with_timeout(monitor, timeout): @@ -79,7 +79,7 @@ allowing them to distinguish which trigger fired: Coroutines can be forked for parallel operation within a function of that code and the forked code. -.. code-block:: python +.. code-block:: python3 @cocotb.test() def test_act_during_reset(dut): @@ -99,7 +99,7 @@ the forked code. Coroutines can be joined to end parallel operation within a function. -.. code-block:: python +.. code-block:: python3 @cocotb.test() def test_count_edge_cycles(dut, period=1000, clocks=6): @@ -124,7 +124,7 @@ Coroutines can be joined to end parallel operation within a function. Coroutines can be killed before they complete, forcing their completion before they'd naturally end. -.. code-block:: python +.. code-block:: python3 @cocotb.test() def test_different_clocks(dut): @@ -158,7 +158,7 @@ Async functions Python 3.5 introduces :keyword:`async` functions, which provide an alternative syntax. For example: -.. code-block:: python +.. code-block:: python3 @cocotb.coroutine async def wait_10ns(): @@ -198,7 +198,7 @@ meaning (rather than being a ``SyntaxError``) which matches the typical meaning of ``yield`` within regular python code. It can be used to create a special type of generator function that can be iterated with ``async for``: -.. code-block:: python +.. code-block:: python3 async def ten_samples_of(clk, signal): for i in range(10): diff --git a/documentation/source/endian_swapper.rst b/documentation/source/endian_swapper.rst index fbe1928d..0b38be83 100644 --- a/documentation/source/endian_swapper.rst +++ b/documentation/source/endian_swapper.rst @@ -32,7 +32,7 @@ It is possible to write directed tests without using a testbench class however to encourage code re-use it is good practice to create a distinct class. -.. code-block:: python +.. code-block:: python3 class EndianSwapperTB(object): @@ -55,7 +55,7 @@ With the above code we have created a testbench with the following structure: If we inspect this line-by-line: -.. code-block:: python +.. code-block:: python3 self.stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) @@ -81,7 +81,7 @@ Name Type Description (from Avalon Specifica By following the signal naming convention the driver can find the signals associated with this interface automatically. -.. code-block:: python +.. code-block:: python3 self.stream_out = AvalonSTMonitor(dut, "stream_out", dut.clk) self.csr = AvalonMaster(dut, "csr", dut.clk) @@ -89,7 +89,7 @@ By following the signal naming convention the driver can find the signals associ We do the same to create the :class:`monitor ` on ``stream_out`` and the CSR interface. -.. code-block:: python +.. code-block:: python3 self.expected_output = [] self.scoreboard = Scoreboard(dut) @@ -101,7 +101,7 @@ The call to :meth:`.add_interface()` takes a Monitor instance as the first argum the second argument is a mechanism for describing the expected output for that interface. This could be a callable function but in this example a simple list of expected transactions is sufficient. -.. code-block:: python +.. code-block:: python3 # Reconstruct the input transactions from the pins and send them to our 'model' self.stream_in_recovered = AvalonSTMonitor(dut, "stream_in", dut.clk, callback=self.model) @@ -115,7 +115,7 @@ We also pass the keyword argument ``callback`` to the monitor constructor which in the supplied function being called for each transaction seen on the bus with the transaction as the first argument. Our model function is quite straightforward in this case - we simply append the transaction to the expected output list and increment a counter: -.. code-block:: python +.. code-block:: python3 def model(self, transaction): """Model the DUT based on the input transaction""" @@ -136,7 +136,7 @@ There are various 'knobs' we can tweak on this testbench to vary the behaviour: We want to run different variations of tests but they will all have a very similar structure so we create a common ``run_test`` function. To generate backpressure on the ``stream_out`` interface we use the :class:`.BitDriver` class from :mod:`cocotb.drivers`. -.. code-block:: python +.. code-block:: python3 @cocotb.coroutine def run_test(dut, data_in=None, config_coroutine=None, idle_inserter=None, backpressure_inserter=None): @@ -188,7 +188,7 @@ Test permutations Having defined a test function we can now auto-generate different permutations of tests using the :class:`.TestFactory` class: -.. code-block:: python +.. code-block:: python3 factory = TestFactory(run_test) factory.add_option("data_in", [random_packet_sizes]) diff --git a/documentation/source/examples.rst b/documentation/source/examples.rst index 8368bfe9..7fc3aee3 100644 --- a/documentation/source/examples.rst +++ b/documentation/source/examples.rst @@ -81,7 +81,7 @@ Sorter Example testbench for snippet of code from `comp.lang.verilog `_: -.. code-block:: python +.. code-block:: python3 @cocotb.coroutine def run_test(dut, data_generator=random_data, delay_cycles=2): diff --git a/documentation/source/hal_cosimulation.rst b/documentation/source/hal_cosimulation.rst index 68c3c400..2130d611 100644 --- a/documentation/source/hal_cosimulation.rst +++ b/documentation/source/hal_cosimulation.rst @@ -123,7 +123,7 @@ to be called when the HAL attempts to perform a read or write. These are then passed to the `IO Module`_: -.. code-block:: python +.. code-block:: python3 @cocotb.function @@ -147,7 +147,7 @@ We can then initialise the HAL and call functions, using the :class:`cocotb.exte decorator to turn the normal function into a blocking coroutine that we can ``yield``: -.. code-block:: python +.. code-block:: python3 state = hal.endian_swapper_init(0) yield cocotb.external(hal.endian_swapper_enable)(state) diff --git a/documentation/source/ping_tun_tap.rst b/documentation/source/ping_tun_tap.rst index 033f77fb..6acd44b9 100644 --- a/documentation/source/ping_tun_tap.rst +++ b/documentation/source/ping_tun_tap.rst @@ -44,7 +44,7 @@ a huge developer base and a quick search of the web reveals a `TUN example`_ that looks like an ideal starting point for our testbench. Using this example we write a function that will create our virtual interface: -.. code-block:: python +.. code-block:: python3 import subprocess, fcntl, struct @@ -66,7 +66,7 @@ signal and connect up the :class:`Avalon driver >> # Read a value back from the DUT >>> count = dut.counter.value @@ -300,7 +300,7 @@ or a resolved integer value can be accessed using the :attr:`~cocotb.binary.Bina We can also cast the signal handle directly to an integer: -.. code-block:: python +.. code-block:: python3 >>> print(int(dut.counter)) 42 @@ -310,7 +310,7 @@ We can also cast the signal handle directly to an integer: Parallel and sequential execution of coroutines ----------------------------------------------- -.. code-block:: python +.. code-block:: python3 @cocotb.coroutine def reset_dut(reset_n, duration): diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index f87af8d4..5cc545f1 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -9,7 +9,7 @@ Icarus Accessing bits of a vector doesn't work: -.. code-block:: python +.. code-block:: python3 dut.stream_in_data[2] <= 1 diff --git a/documentation/source/testbench_tools.rst b/documentation/source/testbench_tools.rst index 1c29ffa9..0c4e853f 100644 --- a/documentation/source/testbench_tools.rst +++ b/documentation/source/testbench_tools.rst @@ -17,7 +17,7 @@ component with the Python logging functionality. Log printing levels can also be set on a per-object basis. -.. code-block:: python +.. code-block:: python3 class EndianSwapperTB(object): @@ -35,7 +35,7 @@ Log printing levels can also be set on a per-object basis. And when the logging is actually called -.. code-block:: python +.. code-block:: python3 class AvalonSTPkts(BusMonitor): ... @@ -73,7 +73,7 @@ Busses are simply defined as collection of signals. The :class:`.Bus` class will automatically bundle any group of signals together that are named similar to ``dut.``. For instance, -.. code-block:: python +.. code-block:: python3 dut.stream_in_valid dut.stream_in_data @@ -84,7 +84,7 @@ names to signal names is also passed into the :class:`.Bus` class. Busses can have values driven onto them, be captured (returning a dictionary), or sampled and stored into a similar object. -.. code-block:: python +.. code-block:: python3 stream_in_bus = Bus(dut, "stream_in", ["valid", "data"]) # '_' is the default separator @@ -98,7 +98,7 @@ transactions to perform the serialization of transactions onto a physical interface. Here is an example using the Avalon bus driver in the ``endian_swapper`` example: -.. code-block:: python +.. code-block:: python3 class EndianSwapperTB(object): @@ -140,7 +140,7 @@ have a callback function passed that is a model. This model will often generate expected transactions, which are then compared using the :class:`.Scoreboard` class. -.. code-block:: python +.. code-block:: python3 # ============================================================================== class BitMonitor(Monitor): @@ -201,7 +201,7 @@ and the expected outputs can be either a simple list, or a function that provides a transaction. Here is some code from the ``dff`` example, similar to above with the scoreboard added. -.. code-block:: python +.. code-block:: python3 class DFF_TB(object): def __init__(self, dut, init_val): From a3543276b724f11d164efddf984c1577f736b3e5 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 17 Mar 2019 22:45:51 -0700 Subject: [PATCH 1581/2656] Fix scheduler issues caused by compound triggers. This adds support for a new type of object to the scheduler, a `Waitable`. A `Waitable` is something that produces a new coroutine every time it is yielded. `yield some_waitable` is nothing more than syntactic sugar for `yield some_waitable._wait()`. With this new primitive, we can: * Fix ClockCycles (gh-520) * Fix Combine (gh-852) * Remove `yield [a, b]` as a primitive, and build it into a new `yield First(a, b)` waitable. This allows a reasonable amount of simplification to the scheduler, and also enables the feature to be used via async functions. --- cocotb/decorators.py | 7 +- cocotb/scheduler.py | 112 ++++------ cocotb/triggers.py | 219 ++++++++++++-------- documentation/source/coroutines.rst | 20 +- tests/test_cases/test_cocotb/test_cocotb.py | 62 +++++- 5 files changed, 240 insertions(+), 180 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 204c3e8f..33c070ed 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -40,7 +40,6 @@ import cocotb from cocotb.log import SimLog -from cocotb.triggers import Join, PythonTrigger, Timer, Event, NullTrigger from cocotb.result import (TestComplete, TestError, TestFailure, TestSuccess, ReturnValue, raise_error, ExternalException) from cocotb.utils import get_sim_time, with_metaclass, exec_ @@ -162,6 +161,10 @@ def close(self): def kill(self): """Kill a coroutine.""" + if self._outcome is not None: + # already finished, nothing to kill + return + self.log.debug("kill() called on coroutine") # todo: probably better to throw an exception for anyone waiting on the coroutine self._outcome = outcomes.Value(None) @@ -169,7 +172,7 @@ def kill(self): def join(self): """Return a trigger that will fire when the wrapped coroutine exits.""" - return Join(self) + return cocotb.triggers.Join(self) def has_started(self): return self._started diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index cf16ea1c..bdb5294a 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -228,8 +228,8 @@ def __init__(self): # indexed by trigger self._trigger2coros = collections.defaultdict(list) - # A dictionary of pending triggers for each coroutine, indexed by coro - self._coro2triggers = collections.defaultdict(list) + # A dictionary mapping coroutines to the trigger they are waiting for + self._coro2trigger = {} # Our main state self._mode = Scheduler._MODE_NORMAL @@ -282,7 +282,7 @@ def default_scheduling_algorithm(self): self._timer1.prime(self.begin_test) self._trigger2coros = collections.defaultdict(list) - self._coro2triggers = collections.defaultdict(list) + self._coro2trigger = {} self._terminate = False self._mode = Scheduler._MODE_TERM @@ -450,22 +450,6 @@ def _event_loop(self, trigger): # This trigger isn't needed any more trigger.unprime() - # If the coroutine was waiting on multiple triggers we may be able - # to unprime the other triggers that didn't fire - scheduling_set = set(scheduling) - other_triggers = { - t - for coro in scheduling - for t in self._coro2triggers[coro] - } - {trigger} - - for pending in other_triggers: - # every coroutine waiting on this trigger is already being woken - if scheduling_set.issuperset(self._trigger2coros[pending]): - if pending.primed: - pending.unprime() - del self._trigger2coros[pending] - for coro in scheduling: if _debug: self.log.debug("Scheduling coroutine %s" % (coro.__name__)) @@ -490,13 +474,18 @@ def _event_loop(self, trigger): def unschedule(self, coro): """Unschedule a coroutine. Unprime any pending triggers""" - for trigger in self._coro2triggers[coro]: + # Unprime the trigger this coroutine is waiting on + try: + trigger = self._coro2trigger.pop(coro) + except KeyError: + # coroutine probably finished + pass + else: if coro in self._trigger2coros[trigger]: self._trigger2coros[trigger].remove(coro) if not self._trigger2coros[trigger]: trigger.unprime() del self._trigger2coros[trigger] - del self._coro2triggers[coro] if Join(coro) in self._trigger2coros: self._pending_triggers.append(Join(coro)) @@ -517,21 +506,19 @@ def save_write(self, handle, value): raise Exception("Write to object {0} was scheduled during a read-only sync phase.".format(handle._name)) self._writes[handle] = value - def _coroutine_yielded(self, coro, triggers): - """Prime the triggers and update our internal mappings.""" - self._coro2triggers[coro] = triggers - - for trigger in triggers: + def _coroutine_yielded(self, coro, trigger): + """Prime the trigger and update our internal mappings.""" + self._coro2trigger[coro] = trigger - self._trigger2coros[trigger].append(coro) - if not trigger.primed: - try: - trigger.prime(self.react) - except Exception as e: - # Convert any exceptions into a test result - self.finish_test( - create_error(self, "Unable to prime trigger %s: %s" % - (str(trigger), str(e)))) + self._trigger2coros[trigger].append(coro) + if not trigger.primed: + try: + trigger.prime(self.react) + except Exception as e: + # Convert any exceptions into a test result + self.finish_test( + create_error(self, "Unable to prime trigger %s: %s" % + (str(trigger), str(e)))) def queue(self, coroutine): """Queue a coroutine for execution""" @@ -656,10 +643,16 @@ def schedule(self, coroutine, trigger=None): if self._terminate: return - # Queue current routine to schedule when the nested routine exits - yield_successful = False - if isinstance(result, cocotb.decorators.RunningCoroutine): + # convert lists into `First` Waitables. + if isinstance(result, list): + result = cocotb.triggers.First(*result) + # convert waitables into coroutines + if isinstance(result, cocotb.triggers.Waitable): + result = result._wait() + + # convert coroutinues into triggers + if isinstance(result, cocotb.decorators.RunningCoroutine): if not result.has_started(): self.queue(result) if _debug: @@ -670,47 +663,14 @@ def schedule(self, coroutine, trigger=None): self.log.debug("Joining to already running coroutine: %s" % result.__name__) - new_trigger = result.join() - self._coroutine_yielded(coroutine, [new_trigger]) - yield_successful = True + result = result.join() - elif isinstance(result, Trigger): + if isinstance(result, Trigger): if _debug: self.log.debug("%s: is instance of Trigger" % result) - self._coroutine_yielded(coroutine, [result]) - yield_successful = True - - # If we get a list, make sure it's a list of triggers or coroutines. - # For every coroutine, replace it with coroutine.join(). - # This could probably be done more elegantly via list comprehension. - elif isinstance(result, list): - new_triggers = [] - for listobj in result: - if isinstance(listobj, Trigger): - new_triggers.append(listobj) - elif isinstance(listobj, cocotb.decorators.RunningCoroutine): - if _debug: - self.log.debug("Scheduling coroutine in list: %s" % - listobj.__name__) - if not listobj.has_started(): - self.queue(listobj) - new_trigger = listobj.join() - new_triggers.append(new_trigger) - else: - # If we encounter something not a coroutine or trigger, - # set the success flag to False and break out of the loop. - yield_successful = False - break - - # Make sure the lists are the same size. If they are not, it means - # it contained something not a trigger/coroutine, so do nothing. - if len(new_triggers) == len(result): - self._coroutine_yielded(coroutine, new_triggers) - yield_successful = True - - # If we didn't successfully yield anything, thrown an error. - # Do it this way to make the logic in the list case simpler. - if not yield_successful: + self._coroutine_yielded(coroutine, result) + + else: msg = ("Coroutine %s yielded something the scheduler can't handle" % str(coroutine)) msg += ("\nGot type: %s repr: %s str: %s" % diff --git a/cocotb/triggers.py b/cocotb/triggers.py index a3209655..dfe9bc04 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -38,12 +38,15 @@ simulator = None from cocotb.log import SimLog -from cocotb.result import raise_error +from cocotb.result import raise_error, ReturnValue from cocotb.utils import ( get_sim_steps, get_time_from_sim_steps, with_metaclass, ParametrizedSingleton, exec_ ) +from cocotb import decorators from cocotb import outcomes +import cocotb + class TriggerException(Exception): pass @@ -264,90 +267,6 @@ class Edge(_EdgeBase): _edge_type = 3 -class ClockCycles(GPITrigger): - """Execution will resume after *num_cycles* rising edges or *num_cycles* falling edges.""" - - def __init__(self, signal, num_cycles, rising=True): - super(ClockCycles, self).__init__() - self.signal = signal - self.num_cycles = num_cycles - if rising is True: - self._rising = 1 - else: - self._rising = 2 - - def prime(self, callback): - """FIXME: document""" - self._callback = callback - - def _check(obj): - self.unprime() - - if self.signal.value: - self.num_cycles -= 1 - - if self.num_cycles <= 0: - self._callback(self) - return - - self.cbhdl = simulator.register_value_change_callback(self.signal. - _handle, - _check, - self._rising, - self) - if self.cbhdl == 0: - raise_error(self, "Unable set up %s Trigger" % (str(self))) - - self.cbhdl = simulator.register_value_change_callback(self.signal. - _handle, - _check, - self._rising, - self) - if self.cbhdl == 0: - raise_error(self, "Unable set up %s Trigger" % (str(self))) - Trigger.prime(self) - - def __str__(self): - return self.__class__.__name__ + "(%s)" % self.signal._name - - -class Combine(PythonTrigger): - """Combines multiple triggers together. Coroutine will continue when all - triggers have fired. - """ - - def __init__(self, *args): - PythonTrigger.__init__(self) - self._triggers = args - # TODO: check that trigger is an iterable containing - # only Trigger objects - try: - for trigger in self._triggers: - if not isinstance(trigger, Trigger): - raise TriggerException("All combined triggers must be " - "instances of Trigger! Got: %s" % - trigger.__class__.__name__) - except Exception: - raise TriggerException("%s requires a list of Trigger objects" % - self.__class__.__name__) - - def prime(self, callback): - self._callback = callback - self._fired = [] - for trigger in self._triggers: - trigger.prime(self._check_all_fired) - Trigger.prime(self) - - def _check_all_fired(self, trigger): - self._fired.append(trigger) - if self._fired == self._triggers: - self._callback(self) - - def unprime(self): - """FIXME: document""" - for trigger in self._triggers: - trigger.unprime() - class _Event(PythonTrigger): """Unique instance used by the Event object. @@ -544,3 +463,133 @@ def prime(self, callback): def __str__(self): return self.__class__.__name__ + "(%s)" % self._coroutine.__name__ + + +class Waitable(object): + """ + Compatibility layer that emulates `collections.abc.Awaitable`. + + This converts a `_wait` abstract method into a suitable `__await__` on + supporting python versions (>=3.3). + """ + @decorators.coroutine + def _wait(self): + """ + Should be implemented by the subclass. Called by `yield self` to + convert the waitable object into a coroutine. + + ReturnValue can be used here + """ + raise NotImplementedError + yield + + if sys.version_info >= (3, 3): + def __await__(self): + return self._wait().__await__() + + +class _AggregateWaitable(Waitable): + """ + Base class for Waitables that take mutiple triggers in their constructor + """ + def __init__(self, *args): + self.triggers = tuple(args) + + # Do some basic type-checking up front, rather than waiting until we + # yield them. + allowed_types = (Trigger, Waitable, decorators.RunningCoroutine) + for trigger in self.triggers: + if not isinstance(trigger, allowed_types): + raise TypeError( + "All triggers must be instances of Trigger! Got: {}" + .format(type(trigger).__name__) + ) + + +class Combine(_AggregateWaitable): + """ + Waits until all the passed triggers have fired. + + Like most triggers, this simply returns itself. + """ + @decorators.coroutine + def _wait(self): + waiters = [] + e = Event() + triggers = list(self.triggers) + + # start a parallel task for each trigger + for t in triggers: + @cocotb.coroutine + def waiter(t=t): + try: + yield t + finally: + triggers.remove(t) + if not triggers: + e.set() + waiters.append(cocotb.fork(waiter())) + + # wait for the last waiter to complete + yield e.wait() + raise ReturnValue(self) + + +class First(_AggregateWaitable): + """ + Wait for the first of multiple triggers. + + Returns the result of the trigger that fired. + """ + @decorators.coroutine + def _wait(self): + waiters = [] + e = Event() + triggers = list(self.triggers) + completed = [] + # start a parallel task for each trigger + for t in triggers: + @cocotb.coroutine + def waiter(t=t): + # capture the outcome of this trigger + try: + ret = outcomes.Value((yield t)) + except BaseException as exc: + ret = outcomes.Error(exc) + + completed.append(ret) + e.set() + waiters.append(cocotb.fork(waiter())) + + # wait for a waiter to complete + yield e.wait() + + # kill all the other waiters + # TODO: Should this kill the coroutines behind any Join triggers? + # Right now it does not. + for w in waiters: + w.kill() + + # get the result from the first task + ret = completed[0] + raise ReturnValue(ret.get()) + + +class ClockCycles(Waitable): + """ + Execution will resume after *num_cycles* rising edges or *num_cycles* falling edges. + """ + def __init__(self, signal, num_cycles, rising=True): + self.signal = signal + self.num_cycles = num_cycles + if rising is True: + self._type = RisingEdge + else: + self._type = FallingEdge + + @decorators.coroutine + def _wait(self): + trigger = self._type(self.signal) + for _ in range(self.num_cycles): + yield trigger + raise ReturnValue(self) diff --git a/documentation/source/coroutines.rst b/documentation/source/coroutines.rst index dd7deea3..972d2b11 100644 --- a/documentation/source/coroutines.rst +++ b/documentation/source/coroutines.rst @@ -173,22 +173,10 @@ of :keyword:`yield`. Provided they are decorated with ``@cocotb.coroutine``, is determined by which type of function it appears in, not by the sub-coroutinue being called. -..note:: - - It is currently not possible to ``await`` a list of triggers as can be done - in a ``yield``-based coroutine with ``yield [trig1, trig2]``. - Until this becomes possible, a simple workaround is to create a helper - function like: - - .. code-block:: python - - @cocotb.coroutine - def first_of(triggers): - return (yield triggers) - - which thanks to the interoperability between the two types of coroutinue, - can then be used as ``await first_of([trig1, trig2])``. - +.. note:: + It is not legal to ``await`` a list of triggers as can be done in + ``yield``-based coroutine with ``yield [trig1, trig2]``. Use + ``await First(trig1, trig2)`` instead. Async generators ~~~~~~~~~~~~~~~~ diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index bf04c3e8..26bfde15 100755 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -40,7 +40,7 @@ import cocotb from cocotb.triggers import (Timer, Join, RisingEdge, FallingEdge, Edge, ReadOnly, ReadWrite, ClockCycles, NextTimeStep, - NullTrigger) + NullTrigger, Combine, Event, First) from cocotb.clock import Clock from cocotb.result import ReturnValue, TestFailure, TestError, TestSuccess from cocotb.utils import get_sim_time @@ -932,5 +932,65 @@ def immediate_exception(): else: raise TestFailure("Exception was not raised") + +@cocotb.test() +def test_combine(dut): + """ Test the Combine trigger. """ + # gh-852 + + @cocotb.coroutine + def do_something(delay): + yield Timer(delay) + + crs = [cocotb.fork(do_something(dly)) for dly in [10, 30, 20]] + + yield Combine(*(cr.join() for cr in crs)) + + +@cocotb.test() +def test_clock_cycles_forked(dut): + """ Test that ClockCycles can be used in forked coroutines """ + # gh-520 + + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + + @cocotb.coroutine + def wait_ten(): + yield ClockCycles(dut.clk, 10) + + a = cocotb.fork(wait_ten()) + b = cocotb.fork(wait_ten()) + yield a.join() + yield b.join() + + +@cocotb.test() +def test_nested_first(dut): + """ Test that nested First triggers behave as expected """ + events = [Event() for i in range(3)] + waiters = [e.wait() for e in events] + + @cocotb.coroutine + def fire_events(): + """ fire the events in order """ + for e in events: + yield Timer(1) + e.set() + + + @cocotb.coroutine + def wait_for_nested_first(): + inner_first = First(waiters[0], waiters[1]) + ret = yield First(inner_first, waiters[2]) + + # should unpack completely, rather than just by one level + assert ret is not inner_first + assert ret is waiters[0] + + fire_task = cocotb.fork(fire_events()) + yield wait_for_nested_first() + yield fire_task.join() + + if sys.version_info[:2] >= (3, 5): from test_cocotb_35 import * From 1803a7fef7248d3f57f753ba22ca336624e110f6 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Mon, 18 Mar 2019 19:16:00 -0700 Subject: [PATCH 1582/2656] Add a test for gh-843 --- tests/test_cases/test_cocotb/test_cocotb.py | 38 +++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 26bfde15..6a4e98c5 100755 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -964,6 +964,44 @@ def wait_ten(): yield b.join() +@cocotb.test() +def test_yield_list_stale(dut): + """ Test that a trigger yielded as part of a list can't cause a spurious wakeup """ + # gh-843 + events = [Event() for i in range(3)] + + waiters = [e.wait() for e in events] + + @cocotb.coroutine + def wait_for_lists(): + ret_i = waiters.index((yield [waiters[0], waiters[1]])) + assert ret_i == 0, "Expected event 0 to fire, not {}".format(ret_i) + + ret_i = waiters.index((yield [waiters[2]])) + assert ret_i == 2, "Expected event 2 to fire, not {}".format(ret_i) + + @cocotb.coroutine + def wait_for_e1(): + """ wait on the event that didn't wake `wait_for_lists` """ + ret_i = waiters.index((yield waiters[1])) + assert ret_i == 1, "Expected event 1 to fire, not {}".format(ret_i) + + @cocotb.coroutine + def fire_events(): + """ fire the events in order """ + for e in events: + yield Timer(1) + e.set() + + fire_task = cocotb.fork(fire_events()) + e1_task = cocotb.fork(wait_for_e1()) + yield wait_for_lists() + + # make sure the other tasks finish + yield fire_task.join() + yield e1_task.join() + + @cocotb.test() def test_nested_first(dut): """ Test that nested First triggers behave as expected """ From 0f6320df0a8ae3e8133a7bd364ed147f8dd2ff52 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Wed, 20 Mar 2019 20:59:38 -0700 Subject: [PATCH 1583/2656] Document how simultaneous triggers cannot exist --- cocotb/triggers.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index dfe9bc04..994216fb 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -540,6 +540,16 @@ class First(_AggregateWaitable): Wait for the first of multiple triggers. Returns the result of the trigger that fired. + + .. note:: + The event loop is single threaded, so while events may be simultaneous + in simulation time, they can never be simultaneous in real time. + For this reason, the value of ``t_ret is t1`` in the following example + is implementation-defined, and will vary by simulator:: + + t1 = Timer(10, units='ps') + t2 = Timer(10, units='ps') + t_ret = yield First(t1, t2) """ @decorators.coroutine def _wait(self): From 846af1673493a868a59cdb525cdf15b2734be5e7 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Fri, 22 Mar 2019 12:27:34 -0700 Subject: [PATCH 1584/2656] Change makefiles to use realpath function. The realpath function returns the canonical absolute name for files and directories. It removes any . or .. components and resolves symlinks. It also requires that the file or directory exists. In case of a failure the empty string is returned. --- cocotb/share/makefiles/Makefile.inc | 6 +++--- makefiles/Makefile.inc | 2 +- makefiles/Makefile.sim | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cocotb/share/makefiles/Makefile.inc b/cocotb/share/makefiles/Makefile.inc index 1618e915..c27d40d3 100644 --- a/cocotb/share/makefiles/Makefile.inc +++ b/cocotb/share/makefiles/Makefile.inc @@ -31,13 +31,13 @@ # Directory containing the cocotb Python module ifeq ($(COCOTB_PY_DIR),) -export COCOTB_PY_DIR:=$(abspath $(dir $(lastword $(MAKEFILE_LIST)))/../../..) +export COCOTB_PY_DIR:=$(realpath $(dir $(lastword $(MAKEFILE_LIST)))/../../..) endif # Directory containing all support files required to build cocotb-based # simulations: Makefile fragments, and the simulator libraries. ifeq ($(COCOTB_SHARE_DIR),) -export COCOTB_SHARE_DIR:=$(abspath $(dir $(lastword $(MAKEFILE_LIST)))/..) +export COCOTB_SHARE_DIR:=$(realpath $(dir $(lastword $(MAKEFILE_LIST)))/..) endif ifeq ($(COCOTB_PY_DIR),$(COCOTB_SHARE_DIR)) @@ -45,7 +45,7 @@ $(warning 'Deprecated cocotb file structure detected. Please use "include $$(she endif ifeq ($(USER_DIR),) -export USER_DIR:=$(abspath $(dir $(firstword $(MAKEFILE_LIST)))) +export USER_DIR:=$(realpath $(dir $(firstword $(MAKEFILE_LIST)))) endif BUILD_DIR=$(USER_DIR)/build diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 66502440..2635d989 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -32,5 +32,5 @@ COCOTB_INSTALL_METHOD = srctree export COCOTB_INSTALL_METHOD -_NEW_SHARE_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))/../cocotb/share +_NEW_SHARE_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST))))/../cocotb/share include $(_NEW_SHARE_DIR)/makefiles/Makefile.inc diff --git a/makefiles/Makefile.sim b/makefiles/Makefile.sim index b0531c33..bed12388 100644 --- a/makefiles/Makefile.sim +++ b/makefiles/Makefile.sim @@ -35,5 +35,5 @@ COCOTB_INSTALL_METHOD = srctree export COCOTB_INSTALL_METHOD -_NEW_SHARE_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))/../cocotb/share +_NEW_SHARE_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST))))/../cocotb/share include $(_NEW_SHARE_DIR)/makefiles/Makefile.sim From e8d3b861f33c51548d682779cdbaca25f3460be7 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Tue, 12 Mar 2019 14:58:30 -0700 Subject: [PATCH 1585/2656] Add VHDL Block statement support --- cocotb/share/lib/vhpi/VhpiCbHdl.cpp | 1 + cocotb/share/lib/vhpi/VhpiImpl.cpp | 1 + .../sample_module/sample_module_1.vhdl | 6 +++ tests/test_cases/test_discovery/Makefile | 2 +- .../test_discovery/test_vhdl_block.py | 47 +++++++++++++++++++ 5 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 tests/test_cases/test_discovery/test_vhdl_block.py diff --git a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp index 06477ee6..6ef5870c 100644 --- a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp +++ b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp @@ -918,6 +918,7 @@ void vhpi_mappings(GpiIteratorMapping &map) }; map.add_to_options(vhpiForGenerateK, &gen_options[0]); map.add_to_options(vhpiIfGenerateK, &gen_options[0]); + map.add_to_options(vhpiBlockStmtK, &gen_options[0]); /* vhpiConstDeclK */ vhpiOneToManyT const_options[] = { diff --git a/cocotb/share/lib/vhpi/VhpiImpl.cpp b/cocotb/share/lib/vhpi/VhpiImpl.cpp index 10faa8d4..afd914ef 100644 --- a/cocotb/share/lib/vhpi/VhpiImpl.cpp +++ b/cocotb/share/lib/vhpi/VhpiImpl.cpp @@ -349,6 +349,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, case vhpiRootInstK: case vhpiIfGenerateK: case vhpiForGenerateK: + case vhpiBlockStmtK: case vhpiCompInstStmtK: { std::string hdl_name = vhpi_get_str(vhpiCaseNameP, new_hdl); diff --git a/tests/designs/sample_module/sample_module_1.vhdl b/tests/designs/sample_module/sample_module_1.vhdl index 7ca86fa1..9a1823de 100644 --- a/tests/designs/sample_module/sample_module_1.vhdl +++ b/tests/designs/sample_module/sample_module_1.vhdl @@ -27,4 +27,10 @@ begin stream_out_data_valid <= '1' when (stream_out_data_registered(EXAMPLE_WIDTH) = '1') else '0'; + SAMPLE_BLOCK : block + signal clk_inv : std_ulogic; + begin + clk_inv <= not clk; + end block; + end architecture; diff --git a/tests/test_cases/test_discovery/Makefile b/tests/test_cases/test_discovery/Makefile index e43e3ba7..5c3c4685 100644 --- a/tests/test_cases/test_discovery/Makefile +++ b/tests/test_cases/test_discovery/Makefile @@ -29,7 +29,7 @@ include ../../designs/sample_module/Makefile ifeq ($(TOPLEVEL_LANG), vhdl) -MODULE = test_discovery,test_vhdl_indexed_name +MODULE = test_discovery,test_vhdl_indexed_name,test_vhdl_block else MODULE = test_discovery endif diff --git a/tests/test_cases/test_discovery/test_vhdl_block.py b/tests/test_cases/test_discovery/test_vhdl_block.py new file mode 100644 index 00000000..1ce38474 --- /dev/null +++ b/tests/test_cases/test_discovery/test_vhdl_block.py @@ -0,0 +1,47 @@ +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import cocotb +import logging +from cocotb.handle import ModifiableObject +from cocotb.triggers import Timer +from cocotb.result import TestError, TestFailure +from cocotb.handle import IntegerObject + + +@cocotb.test() +def block_iter(dut): + """Access a VHDL block statement""" + yield Timer(0) + + try: + dut._log.info("Block: {} ({})".format(dut.isample_module1.SAMPLE_BLOCK._name, + type(dut.isample_module1.SAMPLE_BLOCK))) + dut._log.info("Signal inside Block: {} ({})".format(dut.isample_module1.SAMPLE_BLOCK.clk_inv._name, + type(dut.isample_module1.SAMPLE_BLOCK.clk_inv))) + except AttributeError: + raise TestFailure("Could not traverse into vhpiBlockStmtK") From 20b09439124d4d32d751928692322c59ca756516 Mon Sep 17 00:00:00 2001 From: David Lamb Date: Thu, 4 Apr 2019 14:36:27 -0700 Subject: [PATCH 1586/2656] Fixed compile error in VhpiCbHdl.cpp (missing parentheses) --- cocotb/share/lib/vhpi/VhpiCbHdl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp index 6ef5870c..375ff9f0 100644 --- a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp +++ b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp @@ -784,7 +784,7 @@ int VhpiStartupCbHdl::run_callback() { argv_iter = vhpi_iterator(vhpiArgvs, tool); if (argv_iter) { - while (argv_hdl = vhpi_scan(argv_iter)) { + while ((argv_hdl = vhpi_scan(argv_iter))) { tool_argv[i] = const_cast(static_cast(vhpi_get_str(vhpiStrValP, argv_hdl))); i++; } From 6f25a9051239dd08e92569842b3ca1dd3f8ed8d7 Mon Sep 17 00:00:00 2001 From: David Lamb Date: Thu, 4 Apr 2019 14:51:40 -0700 Subject: [PATCH 1587/2656] Fixed compile error in VhpiCbHdl.cpp (missing parentheses) --- cocotb/share/lib/vhpi/VhpiCbHdl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp index 375ff9f0..25a9f4ea 100644 --- a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp +++ b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp @@ -784,7 +784,7 @@ int VhpiStartupCbHdl::run_callback() { argv_iter = vhpi_iterator(vhpiArgvs, tool); if (argv_iter) { - while ((argv_hdl = vhpi_scan(argv_iter))) { + while ((argv_hdl = vhpi_scan(argv_iter))) { tool_argv[i] = const_cast(static_cast(vhpi_get_str(vhpiStrValP, argv_hdl))); i++; } From a576231b46e6d9bccf3d2f2abf989e1f86d261db Mon Sep 17 00:00:00 2001 From: David Lamb Date: Thu, 4 Apr 2019 15:04:40 -0700 Subject: [PATCH 1588/2656] Fixed compile error in VhpiCbHdl.cpp (missing parentheses) --- cocotb/share/lib/vhpi/VhpiCbHdl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp index 25a9f4ea..375ff9f0 100644 --- a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp +++ b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp @@ -784,7 +784,7 @@ int VhpiStartupCbHdl::run_callback() { argv_iter = vhpi_iterator(vhpiArgvs, tool); if (argv_iter) { - while ((argv_hdl = vhpi_scan(argv_iter))) { + while ((argv_hdl = vhpi_scan(argv_iter))) { tool_argv[i] = const_cast(static_cast(vhpi_get_str(vhpiStrValP, argv_hdl))); i++; } From 54aecbd22ae829c840a273a6452fb9e3c42a0438 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Wed, 27 Mar 2019 12:03:54 -0700 Subject: [PATCH 1589/2656] Cleanup typos and copy/paste errors. --- cocotb/decorators.py | 5 +++-- cocotb/handle.py | 10 +++++----- cocotb/scheduler.py | 6 +----- cocotb/share/lib/embed/gpi_embed.c | 10 ++++------ cocotb/share/lib/simulator/simulatormodule.c | 8 ++++---- cocotb/share/lib/simulator/simulatormodule.h | 2 +- 6 files changed, 18 insertions(+), 23 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 33c070ed..79028091 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -220,6 +220,7 @@ def handle(self, record): def __init__(self, inst, parent): self.error_messages = [] RunningCoroutine.__init__(self, inst, parent) + self.log = SimLog("cocotb.test.%s" % self.__name__, id(self)) self.started = False self.start_time = 0 self.start_sim_time = 0 @@ -265,7 +266,7 @@ def _handle_error_message(self, msg): class coroutine(object): """Decorator class that allows us to provide common coroutine mechanisms: - ``log`` methods will will log to ``cocotb.coroutines.name``. + ``log`` methods will log to ``cocotb.coroutine.name``. ``join()`` method returns an event which will fire when the coroutine exits. @@ -274,7 +275,7 @@ class coroutine(object): def __init__(self, func): self._func = func - self.log = SimLog("cocotb.function.%s" % self._func.__name__, id(self)) + self.log = SimLog("cocotb.coroutine.%s" % self._func.__name__, id(self)) self.__name__ = self._func.__name__ functools.update_wrapper(self, func) diff --git a/cocotb/handle.py b/cocotb/handle.py index f8f9b9b4..1d7113d9 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -326,7 +326,7 @@ def _sub_handle_key(self, name): if result: return int(result.group("index")) else: - self._log.error("Unable to match an index pattern: %s", name); + self._log.error("Unable to match an index pattern: %s", name) return None def __len__(self): @@ -592,17 +592,17 @@ def setimmediatevalue(self, value): value = BinaryValue(value=value, n_bits=len(self), bigEndian=False) elif isinstance(value, dict): # We're given a dictionary with a list of values and a bit size... - num = 0; + num = 0 vallist = list(value["values"]) vallist.reverse() if len(vallist) * value["bits"] != len(self): self._log.critical("Unable to set with array length %d of %d bit entries = %d total, target is only %d bits long" % - (len(value["values"]), value["bits"], len(value["values"]) * value["bits"], len(self))); + (len(value["values"]), value["bits"], len(value["values"]) * value["bits"], len(self))) raise TypeError("Unable to set with array length %d of %d bit entries = %d total, target is only %d bits long" % - (len(value["values"]), value["bits"], len(value["values"]) * value["bits"], len(self))); + (len(value["values"]), value["bits"], len(value["values"]) * value["bits"], len(self))) for val in vallist: - num = (num << value["bits"]) + val; + num = (num << value["bits"]) + val value = BinaryValue(value=num, n_bits=len(self), bigEndian=False) elif not isinstance(value, BinaryValue): diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index bdb5294a..1e0366c4 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -103,7 +103,6 @@ def __init__(self): @property def result(self): return self._outcome.get() - def _propogate_state(self, new_state): self.cond.acquire() @@ -537,7 +536,6 @@ def queue_function(self, coroutine): self._pending_coros.append(coroutine) return t - def run_in_executor(self, func, *args, **kwargs): """Run the coroutine in a separate execution thread and return a yieldable object for the caller. @@ -559,7 +557,7 @@ def execute_external(func, _waiter): name=func.__name__ + "_thread", args=([func, waiter]), kwargs={}) - waiter.thread = thread; + waiter.thread = thread self._pending_threads.append(waiter) return waiter @@ -742,5 +740,3 @@ def cleanup(self): for ext in self._pending_threads: self.log.warn("Waiting for %s to exit", ext.thread) - - diff --git a/cocotb/share/lib/embed/gpi_embed.c b/cocotb/share/lib/embed/gpi_embed.c index cc3e6c6f..15d34baa 100644 --- a/cocotb/share/lib/embed/gpi_embed.c +++ b/cocotb/share/lib/embed/gpi_embed.c @@ -77,8 +77,8 @@ void embed_init_python(void) if (gtstate) return; - void * ret = utils_dyn_open(PY_SO_LIB); - if (!ret) { + void * lib_handle = utils_dyn_open(PY_SO_LIB); + if (!lib_handle) { fprintf(stderr, "Failed to find python lib\n"); } @@ -105,7 +105,7 @@ void embed_init_python(void) #else // Python2 case LOG_INFO("Using virtualenv at %s.", venv_path); - Py_SetProgramName(venv_path); + Py_SetProgramName(venv_path); #endif } else { LOG_INFO("Did not detect Python virtual environment. Using system-wide Python interpreter."); @@ -141,8 +141,6 @@ void embed_init_python(void) FEXIT; } - - /** * @name Initialisation * @brief Called by the simulator on initialisation. Load cocotb python module @@ -210,7 +208,7 @@ int embed_sim_init(gpi_sim_info_t *info) cocotb_module = NULL; arg_dict = NULL; - //Ensure that the current thread is ready to callthe Python C API + //Ensure that the current thread is ready to call the Python C API PyGILState_STATE gstate = PyGILState_Ensure(); to_python(); diff --git a/cocotb/share/lib/simulator/simulatormodule.c b/cocotb/share/lib/simulator/simulatormodule.c index 23e0f049..ce3bdc3a 100644 --- a/cocotb/share/lib/simulator/simulatormodule.c +++ b/cocotb/share/lib/simulator/simulatormodule.c @@ -271,14 +271,14 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) Py_ssize_t numargs = PyTuple_Size(args); if (numargs < 1) { - fprintf(stderr, "Attempt to register ReadOnly callback with!\n"); + fprintf(stderr, "Attempt to register ReadWrite callback with!\n"); return NULL; } // Extract the callback function function = PyTuple_GetItem(args, 0); if (!PyCallable_Check(function)) { - fprintf(stderr, "Attempt to register ReadOnly without supplying a callback!\n"); + fprintf(stderr, "Attempt to register ReadWrite without supplying a callback!\n"); return NULL; } Py_INCREF(function); @@ -323,14 +323,14 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) Py_ssize_t numargs = PyTuple_Size(args); if (numargs < 1) { - fprintf(stderr, "Attempt to register ReadOnly callback with!\n"); + fprintf(stderr, "Attempt to register NextStep callback with!\n"); return NULL; } // Extract the callback function function = PyTuple_GetItem(args, 0); if (!PyCallable_Check(function)) { - fprintf(stderr, "Attempt to register ReadOnly without supplying a callback!\n"); + fprintf(stderr, "Attempt to register NextStep without supplying a callback!\n"); return NULL; } Py_INCREF(function); diff --git a/cocotb/share/lib/simulator/simulatormodule.h b/cocotb/share/lib/simulator/simulatormodule.h index 2098b622..5227272b 100644 --- a/cocotb/share/lib/simulator/simulatormodule.h +++ b/cocotb/share/lib/simulator/simulatormodule.h @@ -123,7 +123,7 @@ static PyMethodDef SimulatorMethods[] = { // FIXME METH_NOARGS => initialization from incompatible pointer type {"get_sim_time", get_sim_time, METH_VARARGS, "Get the current simulation time as an int tuple"}, - {"get_precision", get_precision, METH_VARARGS, "Get the precision of the simualator"}, + {"get_precision", get_precision, METH_VARARGS, "Get the precision of the simulator"}, {"deregister_callback", deregister_callback, METH_VARARGS, "Deregister a callback"}, {"error_out", (PyCFunction)error_out, METH_NOARGS, NULL}, From 6569786978a18e8647314dbc483007b8e1f61dc1 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 19 Apr 2019 11:59:48 +0200 Subject: [PATCH 1590/2656] Add support for Cadence Xcelium, based on the existing IUS support. (#688) * Add support for Cadence Xcelium, based on the existing IUS support. --- .../makefiles/simulators/Makefile.xcelium | 107 ++++++++++++++++++ tests/test_cases/test_array/test_array.py | 8 +- tests/test_cases/test_cocotb/test_cocotb.py | 8 +- .../test_iteration.py | 6 +- .../test_iteration_es.py | 5 +- .../test_iteration_vhdl/test_iteration.py | 4 +- 6 files changed, 124 insertions(+), 14 deletions(-) create mode 100644 cocotb/share/makefiles/simulators/Makefile.xcelium diff --git a/cocotb/share/makefiles/simulators/Makefile.xcelium b/cocotb/share/makefiles/simulators/Makefile.xcelium new file mode 100644 index 00000000..cbd7b204 --- /dev/null +++ b/cocotb/share/makefiles/simulators/Makefile.xcelium @@ -0,0 +1,107 @@ +############################################################################### +# Copyright (c) 2013, 2018 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +# Common Makefile for Cadence Xcelium + +CMD_BIN := xrun + +ifdef XCELIUM_BIN_DIR + CMD := $(shell which $(XCELIUM_BIN_DIR)/$(CMD_BIN) 2>/dev/null) +else + # auto-detect bin dir from system path + CMD := $(shell which $(CMD_BIN) 2>/dev/null) +endif + +ifeq (, $(CMD)) + $(error "Unable to locate command >$(CMD_BIN)<") +else + XCELIUM_BIN_DIR := $(shell dirname $(CMD)) + export XCELIUM_BIN_DIR +endif + +ifneq ($(ARCH),i686) + EXTRA_ARGS += -64 +endif + +EXTRA_ARGS += -xmlibdirpath $(SIM_BUILD) +ifeq ($(DEBUG),1) + EXTRA_ARGS += -pliverbose + EXTRA_ARGS += -messages + EXTRA_ARGS += -plidebug # Enhance the profile output with PLI info + EXTRA_ARGS += -plierr_verbose # Expand handle info in PLI/VPI/VHPI messages + EXTRA_ARGS += -vpicompat 1800v2005 # <1364v1995|1364v2001|1364v2005|1800v2005> Specify the IEEE VPI +else + EXTRA_ARGS += -plinowarn +endif + +ifeq ($(GUI),1) + EXTRA_ARGS += -gui +else + EXTRA_ARGS += +endif + +# Xcelium loads VPI regardless of what is asked for, then VHPI if asked to, +# which does not play nice with our multi language, so we supply GPI_EXTRA=vhpi if needed. + +#GPI_EXTRA:= +ifeq ($(TOPLEVEL_LANG),verilog) + GPI_ARGS = -loadvpi $(LIB_DIR)/libvpi + HDL_SOURCES = $(VERILOG_SOURCES) + ROOT_LEVEL = $(TOPLEVEL) + EXTRA_ARGS += -top $(TOPLEVEL) + ifneq ($(VHDL_SOURCES),) + HDL_SOURCES += $(VHDL_SOURCES) + GPI_EXTRA = vhpi + endif +else ifeq ($(TOPLEVEL_LANG),vhdl) + GPI_EXTRA = vhpi + EXTRA_ARGS += -top $(TOPLEVEL) + MAKE_LIB = -makelib $(TOPLEVEL) + HDL_SOURCES = $(VHDL_SOURCES) + ifneq ($(VERILOG_SOURCES),) + HDL_SOURCES += $(VERILOG_SOURCES) + endif +else + $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") +endif + +GPI_LIB = $(COCOTB_VPI_LIB) $(COCOTB_VHPI_LIB) + +results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(GPI_LIB) + LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ + LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ + set -o pipefail; \ + $(CMD) $(EXTRA_ARGS) $(GPI_ARGS) $(INCDIRS) -access +rwc -createdebugdb $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) 2>&1 | tee $(SIM_BUILD)/sim.log + +clean:: + @rm -rf $(SIM_BUILD) + @rm -rf xrun.* + @rm -rf xmsim.* + @rm -rf gdb_cmd_xmsim diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py index 4e5ca63c..995d89a0 100644 --- a/tests/test_cases/test_array/test_array.py +++ b/tests/test_cases/test_array/test_array.py @@ -153,7 +153,7 @@ def test_read_write(dut): tlog.info("Writing a few signal sub-indices!!!") dut.sig_logic_vec[2] = 0 - if cocotb.LANGUAGE in ["vhdl"] or not (cocotb.SIM_NAME.lower().startswith(("ncsim")) or + if cocotb.LANGUAGE in ["vhdl"] or not (cocotb.SIM_NAME.lower().startswith(("ncsim", "xmsim")) or (cocotb.SIM_NAME.lower().startswith(("riviera")) and cocotb.SIM_VERSION.startswith(("2016.06","2016.10","2017.02")))): dut.sig_t6[1][3][2] = 1 @@ -168,14 +168,14 @@ def test_read_write(dut): tlog.info("Checking writes (2):") _check_logic(tlog, dut.port_logic_vec_out, 0xC8) - if cocotb.LANGUAGE in ["vhdl"] or not (cocotb.SIM_NAME.lower().startswith(("ncsim")) or + if cocotb.LANGUAGE in ["vhdl"] or not (cocotb.SIM_NAME.lower().startswith(("ncsim", "xmsim")) or (cocotb.SIM_NAME.lower().startswith(("riviera")) and cocotb.SIM_VERSION.startswith(("2016.06","2016.10","2017.02")))): _check_logic(tlog, dut.sig_t6[1][3][2], 1) _check_logic(tlog, dut.sig_t6[0][2][7], 0) if cocotb.LANGUAGE in ["vhdl"]: - _check_str (tlog, dut.port_str_out , "TEsting") + _check_str (tlog, dut.port_str_out , "Testing") _check_logic(tlog, dut.port_rec_out.b[1] , 0xA3) _check_logic(tlog, dut.port_cmplx_out[1].b[1], 0xEE) @@ -308,7 +308,7 @@ def test_discover_all(dut): # to ensure the handle is in the dut "sub_handles" for iterating # # DO NOT ADD FOR ALDEC. Does not iterate over properly - if cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("modelsim","ncsim")): + if cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("modelsim", "ncsim", "xmsim")): dummy = dut.sig_rec dummy = dut.port_rec_out diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 6a4e98c5..0a829881 100755 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -311,7 +311,8 @@ def do_test_afterdelay_in_readonly(dut, delay): expect_fail=cocotb.SIM_NAME.lower().startswith(("icarus", "riviera", "modelsim", - "ncsim"))) + "ncsim", + "xmsim"))) def test_readwrite_in_readonly(dut): """Test doing invalid sim operation""" global exited @@ -327,7 +328,8 @@ def test_readwrite_in_readonly(dut): expect_fail=cocotb.SIM_NAME.lower().startswith(("icarus", "riviera", "modelsim", - "ncsim"))) + "ncsim", + "xmsim"))) def test_cached_write_in_readonly(dut): """Test doing invalid sim operation""" global exited @@ -342,7 +344,7 @@ def test_cached_write_in_readonly(dut): @cocotb.test(expect_fail=cocotb.SIM_NAME.lower().startswith(("icarus", "chronologic simulation vcs")), - skip=cocotb.SIM_NAME.lower().startswith(("ncsim"))) + skip=cocotb.SIM_NAME.lower().startswith(("ncsim", "xmsim"))) def test_afterdelay_in_readonly(dut): """Test doing invalid sim operation""" global exited diff --git a/tests/test_cases/test_iteration_mixedlang/test_iteration.py b/tests/test_cases/test_iteration_mixedlang/test_iteration.py index bc7ccef3..f8e1ead7 100644 --- a/tests/test_cases/test_iteration_mixedlang/test_iteration.py +++ b/tests/test_cases/test_iteration_mixedlang/test_iteration.py @@ -65,8 +65,8 @@ def recursive_discovery(dut): """ Recursively discover every single object in the design """ - if cocotb.SIM_NAME.lower().startswith(("ncsim")): - # vpiAlways = 31 and vpiStructVar = 2 do not show up in IUS + if cocotb.SIM_NAME.lower().startswith(("ncsim", "xmsim")): + # vpiAlways = 31 and vpiStructVar = 2 do not show up in IUS/Xcelium pass_total = 917 elif cocotb.SIM_NAME.lower().startswith(("modelsim")): pass_total = 933 @@ -93,7 +93,7 @@ def recursive_discovery_boundary(dut): """ Iteration though the boundary works but this just double checks """ - if cocotb.SIM_NAME.lower().startswith(("ncsim")): + if cocotb.SIM_NAME.lower().startswith(("ncsim", "xmsim")): pass_total = 462 else: pass_total = 478 diff --git a/tests/test_cases/test_iteration_verilog/test_iteration_es.py b/tests/test_cases/test_iteration_verilog/test_iteration_es.py index 420aac54..d139508d 100644 --- a/tests/test_cases/test_iteration_verilog/test_iteration_es.py +++ b/tests/test_cases/test_iteration_verilog/test_iteration_es.py @@ -35,8 +35,9 @@ def recursive_discovery(dut): Recursively discover every single object in the design """ if cocotb.SIM_NAME.lower().startswith(("modelsim", - "ncsim")): - # vpiAlways does not show up in IUS + "ncsim", + "xmsim")): + # vpiAlways does not show up in IUS/Xcelium pass_total = 259 elif cocotb.SIM_NAME.lower().startswith(("chronologic simulation vcs")): pass_total = 59 diff --git a/tests/test_cases/test_iteration_vhdl/test_iteration.py b/tests/test_cases/test_iteration_vhdl/test_iteration.py index a2e58823..6dbd7df1 100644 --- a/tests/test_cases/test_iteration_vhdl/test_iteration.py +++ b/tests/test_cases/test_iteration_vhdl/test_iteration.py @@ -1,4 +1,4 @@ -''' Copyright (c) 2015 Potential Ventures Ltd +''' Copyright (c) 2015, 2018 Potential Ventures Ltd All rights reserved. Redistribution and use in source and binary forms, with or without @@ -34,7 +34,7 @@ def recursive_discovery(dut): """ Recursively discover every single object in the design """ - if (cocotb.SIM_NAME.lower().startswith(("ncsim","modelsim")) or + if (cocotb.SIM_NAME.lower().startswith(("ncsim", "xmsim", "modelsim")) or (cocotb.SIM_NAME.lower().startswith("riviera") and not cocotb.SIM_VERSION.startswith("2016.02"))): # Finds regions, signal, generics, constants, varibles and ports. pass_total = 34569 From 5d74b12f7fb9dfb6975c8336fe17e47f24db5399 Mon Sep 17 00:00:00 2001 From: Curtis McEnroe Date: Wed, 24 Apr 2019 02:12:57 -0400 Subject: [PATCH 1591/2656] Use PYTHON_BIN in Darwin Makefile (#905) This allows actually using an installed Python 3 rather than always using the system Python 2.7, as can be done elsewhere. --- cocotb/share/makefiles/Makefile.pylib.Darwin | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cocotb/share/makefiles/Makefile.pylib.Darwin b/cocotb/share/makefiles/Makefile.pylib.Darwin index 9021a713..e7246854 100644 --- a/cocotb/share/makefiles/Makefile.pylib.Darwin +++ b/cocotb/share/makefiles/Makefile.pylib.Darwin @@ -40,12 +40,12 @@ NATIVE_ARCH=$(shell uname -m) ARCH?=$(NATIVE_ARCH) # We might work with other Python versions -PYTHON_MAJOR_VERSION?=$(shell python -c "from __future__ import print_function; import sys; print(sys.version_info[0])") -PYTHON_MINOR_VERSION?=$(shell python -c "from __future__ import print_function; import sys; print(sys.version_info[1])") -PYTHON_VERSION?=$(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') +PYTHON_MAJOR_VERSION?=$(shell $(PYTHON_BIN) -c "from __future__ import print_function; import sys; print(sys.version_info[0])") +PYTHON_MINOR_VERSION?=$(shell $(PYTHON_BIN) -c "from __future__ import print_function; import sys; print(sys.version_info[1])") +PYTHON_VERSION?=$(shell $(PYTHON_BIN) -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') -PYTHON_LIBDIR:=$(shell python -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("LIBDIR"))') -PYTHON_DYNLIBDIR:=$(shell python -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("DESTSHARED"))') +PYTHON_LIBDIR:=$(shell $(PYTHON_BIN) -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("LIBDIR"))') +PYTHON_DYNLIBDIR:=$(shell $(PYTHON_BIN) -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("DESTSHARED"))') # Nasty hack but there's no way in Python properly get the directories for 32-bit Python on a 64-bit system # TODO: Under OSX we can use "export VERSIONER_PYTHON_PREFER_32_BIT=yes", no Linux equivalent though @@ -69,5 +69,5 @@ endif PYLIBS = $(shell $(PY_CFG_EXE) --libs) -PYTHON_INCLUDEDIR := $(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())') -PYTHON_DYN_LIB:=$(shell python -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("LDLIBRARY"))') +PYTHON_INCLUDEDIR := $(shell $(PYTHON_BIN) -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())') +PYTHON_DYN_LIB:=$(shell $(PYTHON_BIN) -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("LDLIBRARY"))') From 62ef8134a07b9d4ccde2b1e62cdc701db6917c79 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Sun, 28 Apr 2019 14:56:54 +0200 Subject: [PATCH 1592/2656] Update documation with package installation instructions (#881) Show how to install cooctb through a Python package. --- README.md | 4 ++++ documentation/source/quickstart.rst | 30 ++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b3b01ca3..5589b294 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,10 @@ * Get in contact: [E-mail us](mailto:cocotb@potentialventures.com) * Follow us on twitter: [@PVCocotb](https://twitter.com/PVCocotb) +## Installation + +Cocotb can be installed by running `pip install cocotb`. + ## Quickstart # Install pre-requisites (waveform viewer optional) diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index 3e52775d..b9b30e94 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -138,9 +138,33 @@ Installing a package manager really helps things out here. `Brew `_ seems to be the most popular, so we'll assume you have that installed. -.. code-block::bash +.. code-block:: bash $> brew install python icarus-verilog gtkwave + +Installing cocotb +================= + +Cocotb can be installed by running (recommended Python3) + +.. code-block:: bash + + $> pip3 install cocotb + +or + +.. code-block:: bash + + $> pip install cocotb + +*\*For user local install follow* `pip User Guide `_. + +For development version: + +.. code-block:: bash + + $> git clone https://github.com/potentialventures/cocotb + $> pip install -e ./cocotb Running an example ------------------ @@ -195,8 +219,8 @@ Python test script to load. VERILOG_SOURCES = $(PWD)/submodule.sv $(PWD)/my_design.sv TOPLEVEL=my_design # the module name in your Verilog or VHDL file MODULE=test_my_design # the name of the Python test file - include $(COCOTB)/makefiles/Makefile.inc - include $(COCOTB)/makefiles/Makefile.sim + include $(shell cocotb-config --makefiles)/Makefile.inc + include $(shell cocotb-config --makefiles)/Makefile.sim We would then create a file called ``test_my_design.py`` containing our tests. From aca406ddd660f23a5f65e459c7ac4120335bc04c Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 21 Mar 2019 00:27:30 -0700 Subject: [PATCH 1593/2656] Remove special casing of readonly, which will be handled like any other cleanup --- cocotb/scheduler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 1e0366c4..21cb9746 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -274,7 +274,7 @@ def default_scheduling_algorithm(self): for t in self._trigger2coros: t.unprime() - for t in [self._readwrite, self._readonly, self._next_timestep, + for t in [self._readwrite, self._next_timestep, self._timer1, self._timer0]: if t.primed: t.unprime() From 07d28be1559f0b424703f67ad55e998de164f0db Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 21 Mar 2019 00:59:56 -0700 Subject: [PATCH 1594/2656] Remove cloned ReadWrite trigger and the scheduler and Gpi workarounds needed to support it. Before this commit, the scheduler manages pending writes (assigned through <=) through the manual priming and unpriming of triggers within its event loop. What makes this particularly troublesome is the fact that these replace the callbacks primed to schedule regular user coroutines (gh-759). To support this hack, and (mostly) keep the user callbacks primed anyway, there was a hack in VpiReadwriteCbHdl that means that only upon the second `.unprime()` does ReadWrite actually get unprimed This removes all of this in favor of a simple model: * If any delayed writes are requested, kick off a background coroutine to time them appropriately * When ReadWrite.unprime() is called, actually unprime it * Make sure the background coroutine gets to process the triggers first, by putting it first in the list of coroutines to respond to This closes #759 The first test failed without this commit. The second test is used to verify the need for the special-casing in `_coroutine_yielded` --- cocotb/scheduler.py | 105 +++++++++----------- cocotb/share/lib/vpi/VpiCbHdl.cpp | 1 - cocotb/share/lib/vpi/VpiImpl.h | 22 ---- tests/test_cases/test_cocotb/test_cocotb.py | 32 ++++++ 4 files changed, 81 insertions(+), 79 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 21cb9746..d44fc0f4 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -208,12 +208,6 @@ class Scheduler(object): # Singleton events, recycled to avoid spurious object creation _readonly = ReadOnly() - # TODO[gh-759]: For some reason, the scheduler requires that these triggers - # are _not_ the same instances used by the tests themselves. This is risky, - # because it can lead to them overwriting each other's callbacks. We should - # try to remove this `copy.copy` in future. - _next_timestep = copy.copy(NextTimeStep()) - _readwrite = copy.copy(ReadWrite()) _timer1 = Timer(1) _timer0 = Timer(0) @@ -247,35 +241,42 @@ def __init__(self): self._entrypoint = None self._main_thread = threading.current_thread() - # Select the appropriate scheduling algorithm for this simulator - self.advance = self.default_scheduling_algorithm self._is_reacting = False - def default_scheduling_algorithm(self): - """ - Decide whether we need to schedule our own triggers (if at all) in - order to progress to the next mode. + self._write_coro_inst = None + self._writes_pending = Event() - This algorithm has been tested against the following simulators: - Icarus Verilog - """ - if not self._terminate and self._writes: + @cocotb.decorators.coroutine + def _do_writes(self): + """ An internal coroutine that performs pending writes """ + while True: + yield self._writes_pending.wait() + if self._mode != Scheduler._MODE_NORMAL: + yield NextTimeStep() + + yield ReadWrite() - if self._mode == Scheduler._MODE_NORMAL: - if not self._readwrite.primed: - self._readwrite.prime(self.react) - elif not self._next_timestep.primed: - self._next_timestep.prime(self.react) + while self._writes: + handle, value = self._writes.popitem() + handle.setimmediatevalue(value) + self._writes_pending.clear() - elif self._terminate: + def _check_termination(self): + """ + Handle a termination that causes us to move onto the next test. + """ + if self._terminate: if _debug: self.log.debug("Test terminating, scheduling Timer") + if self._write_coro_inst is not None: + self._write_coro_inst.kill() + self._write_coro_inst = None + for t in self._trigger2coros: t.unprime() - for t in [self._readwrite, self._next_timestep, - self._timer1, self._timer0]: + for t in [self._timer1, self._timer0]: if t.primed: t.unprime() @@ -283,6 +284,8 @@ def default_scheduling_algorithm(self): self._trigger2coros = collections.defaultdict(list) self._coro2trigger = {} self._terminate = False + self._writes = {} + self._writes_pending.clear() self._mode = Scheduler._MODE_TERM def begin_test(self, trigger=None): @@ -315,7 +318,7 @@ def begin_test(self, trigger=None): test = self._entrypoint self._entrypoint = None self.schedule(test) - self.advance() + self._check_termination() def react(self, trigger): """ @@ -329,6 +332,8 @@ def react(self, trigger): self._pending_triggers.append(trigger) return + assert not self._pending_triggers + # start the event loop self._is_reacting = True try: @@ -370,34 +375,6 @@ def _event_loop(self, trigger): elif isinstance(trigger, GPITrigger): self._mode = Scheduler._MODE_NORMAL - # We're the only source of ReadWrite triggers which are only used for - # playing back any cached signal updates - if trigger is self._readwrite: - - if _debug: - self.log.debug("Writing cached signal updates") - - while self._writes: - handle, value = self._writes.popitem() - handle.setimmediatevalue(value) - - self._readwrite.unprime() - - return - - # Similarly if we've scheduled our next_timestep on way to readwrite - if trigger is self._next_timestep: - - if not self._writes: - self.log.error( - "Moved to next timestep without any pending writes!") - else: - self.log.debug( - "Priming ReadWrite trigger so we can playback writes") - self._readwrite.prime(self.react) - - return - # work through triggers one by one is_first = True self._pending_triggers.append(trigger) @@ -464,7 +441,7 @@ def _event_loop(self, trigger): self._pending_events.pop(0).set() # no more pending triggers - self.advance() + self._check_termination() if _debug: self.log.debug("All coroutines scheduled, handing control back" " to simulator") @@ -503,13 +480,29 @@ def unschedule(self, coro): def save_write(self, handle, value): if self._mode == Scheduler._MODE_READONLY: raise Exception("Write to object {0} was scheduled during a read-only sync phase.".format(handle._name)) + + # TODO: we should be able to better keep track of when this needs to + # be scheduled + if self._write_coro_inst is None: + self._write_coro_inst = self._do_writes() + self.schedule(self._write_coro_inst) + self._writes[handle] = value + self._writes_pending.set() def _coroutine_yielded(self, coro, trigger): """Prime the trigger and update our internal mappings.""" self._coro2trigger[coro] = trigger - self._trigger2coros[trigger].append(coro) + if coro is self._write_coro_inst: + # Our internal write coroutine always runs before any user coroutines. + # This preserves the behavior prior to the refactoring of writes to + # this coroutine. + self._trigger2coros[trigger].insert(0, coro) + else: + # Everything else joins the back of the queue + self._trigger2coros[trigger].append(coro) + if not trigger.primed: try: trigger.prime(self.react) @@ -595,7 +588,7 @@ def add(self, coroutine): self.log.debug("Adding new coroutine %s" % coroutine.__name__) self.schedule(coroutine) - self.advance() + self._check_termination() return coroutine def new_test(self, coroutine): diff --git a/cocotb/share/lib/vpi/VpiCbHdl.cpp b/cocotb/share/lib/vpi/VpiCbHdl.cpp index 8ed4518d..69efdbef 100644 --- a/cocotb/share/lib/vpi/VpiCbHdl.cpp +++ b/cocotb/share/lib/vpi/VpiCbHdl.cpp @@ -504,7 +504,6 @@ VpiReadwriteCbHdl::VpiReadwriteCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), VpiCbHdl(impl) { cb_data.reason = cbReadWriteSynch; - delay_kill = false; } VpiReadOnlyCbHdl::VpiReadOnlyCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), diff --git a/cocotb/share/lib/vpi/VpiImpl.h b/cocotb/share/lib/vpi/VpiImpl.h index 5ac57fc1..b372cddb 100644 --- a/cocotb/share/lib/vpi/VpiImpl.h +++ b/cocotb/share/lib/vpi/VpiImpl.h @@ -127,28 +127,6 @@ class VpiReadwriteCbHdl : public VpiCbHdl { public: VpiReadwriteCbHdl(GpiImplInterface *impl); virtual ~VpiReadwriteCbHdl() { } - int run_callback(void) { - if (delay_kill) { - delay_kill = false; - return 0; - } else { - return VpiCbHdl::run_callback(); - } - } - int cleanup_callback(void) { - if (m_state == GPI_PRIMED) { - delay_kill = true; - return 0; - } else { - return VpiCbHdl::cleanup_callback(); - } - } - int arm_callback(void) { - delay_kill = false; - return VpiCbHdl::arm_callback(); - } -private: - bool delay_kill; }; class VpiStartupCbHdl : public VpiCbHdl { diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 0a829881..317a6f22 100755 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -1032,5 +1032,37 @@ def wait_for_nested_first(): yield fire_task.join() +@cocotb.test() +def test_readwrite(dut): + """ Test that ReadWrite can be waited on """ + # gh-759 + yield Timer(1) + dut.clk <= 1 + yield ReadWrite() + + +@cocotb.test() +def test_writes_have_taken_effect_after_readwrite(dut): + """ Test that ReadWrite fires first for the background write coro """ + dut.stream_in_data.setimmediatevalue(0) + + @cocotb.coroutine + def write_manually(): + yield ReadWrite() + # this should overwrite the write written below + dut.stream_in_data.setimmediatevalue(2) + + # queue a backround task to do a manual write + waiter = cocotb.fork(write_manually()) + + # do a delayed write. This will be overwritten + dut.stream_in_data <= 3 + yield waiter + + # check that the write we expected took precedence + yield ReadOnly() + assert dut.stream_in_data.value == 2 + + if sys.version_info[:2] >= (3, 5): from test_cocotb_35 import * From 7ba4e30620f1e4db549bd4d3ef133af8142b2695 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Wed, 27 Mar 2019 13:00:27 -0700 Subject: [PATCH 1595/2656] Rename context for improved clarity. --- cocotb/share/include/cocotb_utils.h | 14 +++++++------- cocotb/share/lib/simulator/simulatormodule.c | 2 +- cocotb/share/lib/utils/cocotb_utils.c | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cocotb/share/include/cocotb_utils.h b/cocotb/share/include/cocotb_utils.h index c1e20246..bc0702d0 100644 --- a/cocotb/share/include/cocotb_utils.h +++ b/cocotb/share/include/cocotb_utils.h @@ -43,25 +43,25 @@ extern "C" { extern void* utils_dyn_open(const char* lib_name); extern void* utils_dyn_sym(void *handle, const char* sym_name); -extern int context; +extern int is_python_context; void to_python(void) { - if (context) { + if (is_python_context) { fprintf(stderr, "FATAL: We are calling up again\n"); exit(1); } - ++context; - //fprintf(stderr, "INFO: Calling up to python %d\n", context); + ++is_python_context; + //fprintf(stderr, "INFO: Calling up to python %d\n", is_python_context); } void to_simulator(void) { - if (!context) { + if (!is_python_context) { fprintf(stderr, "FATAL: We have returned twice from python\n"); exit(1); } - --context; - //fprintf(stderr, "INFO: Returning back to simulator %d\n", context); + --is_python_context; + //fprintf(stderr, "INFO: Returning back to simulator %d\n", is_python_context); } #ifdef __cplusplus diff --git a/cocotb/share/lib/simulator/simulatormodule.c b/cocotb/share/lib/simulator/simulatormodule.c index ce3bdc3a..7e580699 100644 --- a/cocotb/share/lib/simulator/simulatormodule.c +++ b/cocotb/share/lib/simulator/simulatormodule.c @@ -822,7 +822,7 @@ static PyObject *get_sim_time(PyObject *self, PyObject *args) { struct sim_time local_time; - if (context) { + if (is_python_context) { gpi_get_sim_time(&local_time.high, &local_time.low); } else { local_time = cache_time; diff --git a/cocotb/share/lib/utils/cocotb_utils.c b/cocotb/share/lib/utils/cocotb_utils.c index ebd5f553..ecf0e607 100644 --- a/cocotb/share/lib/utils/cocotb_utils.c +++ b/cocotb/share/lib/utils/cocotb_utils.c @@ -37,7 +37,7 @@ #endif // Tracks if we are in the context of Python or Simulator -int context = 0; +int is_python_context = 0; void* utils_dyn_open(const char* lib_name) { From 63a06bf2b85e760bd2ea999555bb675861ef9a4d Mon Sep 17 00:00:00 2001 From: Yahia Tachwali Date: Sun, 28 Apr 2019 10:26:27 -0400 Subject: [PATCH 1596/2656] Fix Python 3 compile error in io.c (#895) Fix io.c file to work for python 3 by using PyInit_io_module instead of initio_module --- examples/endian_swapper/cosim/io.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/examples/endian_swapper/cosim/io.c b/examples/endian_swapper/cosim/io.c index 917f0d18..9e817fc8 100644 --- a/examples/endian_swapper/cosim/io.c +++ b/examples/endian_swapper/cosim/io.c @@ -113,14 +113,16 @@ int IOWR(unsigned int base, unsigned int address, unsigned int value) return 0; } -PyMODINIT_FUNC -initio_module(void) -{ +#if PY_MAJOR_VERSION >= 3 + #define MOD_RETVAL(x) x + #define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void) +#else + #define MOD_RETVAL(x) + #define MOD_INIT(name) PyMODINIT_FUNC init##name(void) +#endif + +MOD_INIT(io_module) { PyObject* io_module; io_module = Py_InitModule("io_module", io_module_methods); - if (!io_module) { - printf("Failed to load io_module\n"); - exit(1); - } + return MOD_RETVAL(io_module); } - From f2f2604f4c07cbd23e22c5436d44efe20753a0ec Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 4 May 2019 22:42:09 -0700 Subject: [PATCH 1597/2656] Remove scheduler._timer0 This member is private and never used --- cocotb/scheduler.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index d44fc0f4..733a2af0 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -209,7 +209,6 @@ class Scheduler(object): # Singleton events, recycled to avoid spurious object creation _readonly = ReadOnly() _timer1 = Timer(1) - _timer0 = Timer(0) def __init__(self): @@ -276,9 +275,8 @@ def _check_termination(self): for t in self._trigger2coros: t.unprime() - for t in [self._timer1, self._timer0]: - if t.primed: - t.unprime() + if self._timer1.primed: + self._timer1.unprime() self._timer1.prime(self.begin_test) self._trigger2coros = collections.defaultdict(list) From 24f3f2d790410973f42e3054502a115e27cfef9e Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 4 May 2019 21:05:15 -0700 Subject: [PATCH 1598/2656] Replace Python3-only syntax in test with syntax that works in python 2 Fixes gh-904 --- tests/test_cases/test_external/test_external.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index 1742037d..0b615982 100755 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -343,7 +343,8 @@ def test_function_returns_exception(dut): @cocotb.function def func(): - return ValueError() + # avoid using `return` syntax here since that requires Python >= 3.3 + raise ReturnValue(ValueError()) yield @external From b6f24c142b14ade52a99e8c8c3d2fd46d49a9e77 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 4 May 2019 22:58:12 -0700 Subject: [PATCH 1599/2656] Fix typo in hooks that made them unusable --- cocotb/decorators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 79028091..bae8e5f2 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -402,7 +402,7 @@ class hook(with_metaclass(_decorator_helper, coroutine)): All hooks are run at the beginning of a cocotb test suite, prior to any test code being run.""" - def __init__(self): + def __init__(self, f): super(hook, self).__init__(f) self.im_hook = True self.name = self._func.__name__ From 8bf587f79672812f1d02ae6fa8f98d308345c96f Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 4 May 2019 21:11:50 -0700 Subject: [PATCH 1600/2656] Always respond to triggers through react, so that the event loop is always involved Fixes gh-910 --- cocotb/scheduler.py | 2 +- tests/test_cases/test_external/test_external.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 733a2af0..fef3851f 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -462,7 +462,7 @@ def unschedule(self, coro): del self._trigger2coros[trigger] if Join(coro) in self._trigger2coros: - self._pending_triggers.append(Join(coro)) + self.react(Join(coro)) else: try: # throws an error if the background coroutine errored diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index 0b615982..c19cae42 100755 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -313,7 +313,7 @@ def func(): if not isinstance(result, ValueError): raise TestFailure('Exception was not returned') -@cocotb.test(skip=True) +@cocotb.test() def test_function_raised_exception(dut): """ Test that exceptions thrown by @function coroutines can be caught """ # workaround for gh-637 From 6dc836f28e01295310470bd61e22b1e39d97bdf1 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Sun, 5 May 2019 13:30:07 +0100 Subject: [PATCH 1601/2656] VCS now reports the same signals as the other big3 (#908) Tested with VCS M-2017.03-1. --- .../test_cases/test_iteration_verilog/test_iteration_es.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/test_cases/test_iteration_verilog/test_iteration_es.py b/tests/test_cases/test_iteration_verilog/test_iteration_es.py index d139508d..01c8cc22 100644 --- a/tests/test_cases/test_iteration_verilog/test_iteration_es.py +++ b/tests/test_cases/test_iteration_verilog/test_iteration_es.py @@ -36,11 +36,10 @@ def recursive_discovery(dut): """ if cocotb.SIM_NAME.lower().startswith(("modelsim", "ncsim", - "xmsim")): - # vpiAlways does not show up in IUS/Xcelium + "xmsim", + "chronologic simulation vcs")): + # vpiAlways does not show up pass_total = 259 - elif cocotb.SIM_NAME.lower().startswith(("chronologic simulation vcs")): - pass_total = 59 else: pass_total = 265 From c09c633030146d30f77b1d8cb0b80cc09c214b55 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Sun, 28 Apr 2019 15:52:15 +0200 Subject: [PATCH 1602/2656] Whitelist make command in tox Fix the following warning when running tox: WARNING:test command found but not installed in testenv cmd: /usr/bin/make env: /data/home/xxx/src/cocotb/.tox/py35 Maybe you forgot to specify a dependency? See also the whitelist_externals envconfig setting. --- tox.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tox.ini b/tox.ini index e8cb4245..880d84c5 100644 --- a/tox.ini +++ b/tox.ini @@ -12,3 +12,6 @@ deps = commands = make test + +whitelist_externals = + make From 36b508a60a52f862e4124dbe4d03adf76d5bc865 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Wed, 8 May 2019 01:26:15 -0700 Subject: [PATCH 1603/2656] Make the scheduler deterministic on old versions of python The scheduler has various points where it iterates over keys in a dictionary. However, this iteration order is random before CPython 3.6 / Python 3.7. To fix that, we use `collections.OrderedDict`, which is ordered in all versions. We cannot use `defaultdict` with this, so use the `setdefault` method, which is a little clumsier. The test now fails rather than errors, which looking at the implementation of the `$fatal` handler looks like what was intended to happen in the first place. This fixes gh-934 and gh-928. --- cocotb/scheduler.py | 28 ++++++++++++------- .../test_closedown/test_closedown.py | 2 +- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index fef3851f..a4adcc4a 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -216,18 +216,20 @@ def __init__(self): if _debug: self.log.setLevel(logging.DEBUG) + # Use OrderedDict here for deterministic behavior (gh-934) + # A dictionary of pending coroutines for each trigger, # indexed by trigger - self._trigger2coros = collections.defaultdict(list) + self._trigger2coros = collections.OrderedDict() # A dictionary mapping coroutines to the trigger they are waiting for - self._coro2trigger = {} + self._coro2trigger = collections.OrderedDict() # Our main state self._mode = Scheduler._MODE_NORMAL # A dictionary of pending writes - self._writes = {} + self._writes = collections.OrderedDict() self._pending_coros = [] self._pending_callbacks = [] @@ -279,10 +281,10 @@ def _check_termination(self): self._timer1.unprime() self._timer1.prime(self.begin_test) - self._trigger2coros = collections.defaultdict(list) - self._coro2trigger = {} + self._trigger2coros = collections.OrderedDict() + self._coro2trigger = collections.OrderedDict() self._terminate = False - self._writes = {} + self._writes = collections.OrderedDict() self._writes_pending.clear() self._mode = Scheduler._MODE_TERM @@ -455,7 +457,7 @@ def unschedule(self, coro): # coroutine probably finished pass else: - if coro in self._trigger2coros[trigger]: + if coro in self._trigger2coros.setdefault(trigger, []): self._trigger2coros[trigger].remove(coro) if not self._trigger2coros[trigger]: trigger.unprime() @@ -492,14 +494,15 @@ def _coroutine_yielded(self, coro, trigger): """Prime the trigger and update our internal mappings.""" self._coro2trigger[coro] = trigger + trigger_coros = self._trigger2coros.setdefault(trigger, []) if coro is self._write_coro_inst: # Our internal write coroutine always runs before any user coroutines. # This preserves the behavior prior to the refactoring of writes to # this coroutine. - self._trigger2coros[trigger].insert(0, coro) + trigger_coros.insert(0, coro) else: # Everything else joins the back of the queue - self._trigger2coros[trigger].append(coro) + trigger_coros.append(coro) if not trigger.primed: try: @@ -720,7 +723,12 @@ def cleanup(self): Unprime all pending triggers and kill off any coroutines stop all externals. """ - for trigger, waiting in dict(self._trigger2coros).items(): + # copy since we modify this in kill + items = list(self._trigger2coros.items()) + + # reversing seems to fix gh-928, although the order is still somewhat + # arbitrary. + for trigger, waiting in items[::-1]: for coro in waiting: if _debug: self.log.debug("Killing %s" % str(coro)) diff --git a/tests/test_cases/test_closedown/test_closedown.py b/tests/test_cases/test_closedown/test_closedown.py index ac8c05f7..79ce781b 100644 --- a/tests/test_cases/test_closedown/test_closedown.py +++ b/tests/test_cases/test_closedown/test_closedown.py @@ -56,7 +56,7 @@ def clock_mon(dut): yield RisingEdge(dut.clk) -@cocotb.test(expect_error=True) +@cocotb.test(expect_fail=True) def test_failure_from_system_task(dut): """Allow the dut to call $fail_test() from verilog""" clock = Clock(dut.clk, 100) From 1e1ca0e92f5dc7e0508c6f32093673cc208ebfad Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Mon, 6 May 2019 20:14:21 -0700 Subject: [PATCH 1604/2656] Remove scheduler._pending_callbacks This member variable is: * Private * Undocumented * Untested * Read from but never written to Let's remove it --- cocotb/scheduler.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index a4adcc4a..0092e7ad 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -232,7 +232,6 @@ def __init__(self): self._writes = collections.OrderedDict() self._pending_coros = [] - self._pending_callbacks = [] self._pending_triggers = [] self._pending_threads = [] self._pending_events = [] # Events we need to call set on once we've unwound @@ -699,10 +698,6 @@ def wrapper(): while self._pending_coros: self.add(self._pending_coros.pop(0)) - while self._pending_callbacks: - self._pending_callbacks.pop(0)() - - def finish_test(self, test_result): """Cache the test result and set the terminate flag.""" self.log.debug("finish_test called with %s" % (repr(test_result))) From cb9d021feb50de2556371a0178fde60815e6a890 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Mon, 6 May 2019 22:00:41 -0700 Subject: [PATCH 1605/2656] Prevent a hang when a @function is used in a background thread In the past this would hang: * `function.__call__` would call `Scheduler.queue_function` with a callback that sets an event * `Scheduler.queue_function` would do nothing with its callback * `function.__call__` would wait forever on an event that never fires Now it throws a RuntimeError. It might be possible to spawn a new waiter instance for the background thread and not fail at all, but that's a task for another time. --- cocotb/scheduler.py | 22 ++++++--- .../test_cases/test_external/test_external.py | 45 +++++++++++++++++++ 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 0092e7ad..8ad9f4ea 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -520,14 +520,22 @@ def queue_function(self, coroutine): """Queue a coroutine for execution and move the containing thread so that it does not block execution of the main thread any longer. """ - # We should be able to find ourselves inside the _pending_threads list - - for t in self._pending_threads: - if t.thread == threading.current_thread(): - t.thread_suspend() - self._pending_coros.append(coroutine) - return t + matching_threads = [ + t + for t in self._pending_threads + if t.thread == threading.current_thread() + ] + if len(matching_threads) == 0: + raise RuntimeError("queue_function called from unrecognized thread") + + # Raises if there is more than one match. This can never happen, since + # each entry always has a unique thread. + t, = matching_threads + + t.thread_suspend() + self._pending_coros.append(coroutine) + return t def run_in_executor(self, func, *args, **kwargs): """Run the coroutine in a separate execution thread diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index c19cae42..5c5fba36 100755 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -358,3 +358,48 @@ def ext(): if not isinstance(result, ValueError): raise TestFailure('Exception was not returned') + +@cocotb.test() +def test_function_from_weird_thread_fails(dut): + """ + Test that background threads caling a @function do not hang forever + """ + # workaround for gh-637 + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + + # workaround the lack of `nonlocal` in Python 2 + class vals: + func_started = False + caller_resumed = False + raised = False + + @cocotb.function + def func(): + vals.started = True + yield Timer(10) + + def function_caller(): + try: + func() + except RuntimeError: + vals.raised = True + finally: + vals.caller_resumed = True + + @external + def ext(): + result = [] + + t = threading.Thread(target=function_caller) + t.start() + t.join() + + task = cocotb.fork(ext()) + + yield Timer(20) + + assert vals.caller_resumed, "Caller was never resumed" + assert not vals.func_started, "Function should never have started" + assert vals.raised, "No exception was raised to warn the user" + + yield task.join() From 7888449d466e59fac2e5bb96d4eba60aefcaafd9 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Mon, 6 May 2019 20:00:26 -0700 Subject: [PATCH 1606/2656] Use a with statement to ensure the threading.Condition is always released ```python cond.acquire() stuff() cond.release() ``` is better spelt ```python with cond: stuff() ``` as the latter calls release even if an exception is thrown. I don't think this causes any observable bugs right now. --- cocotb/scheduler.py | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 8ad9f4ea..ff2f2bce 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -105,12 +105,11 @@ def result(self): return self._outcome.get() def _propogate_state(self, new_state): - self.cond.acquire() - if _debug: - self._log.debug("Changing state from %d -> %d from %s" % (self.state, new_state, threading.current_thread())) - self.state = new_state - self.cond.notify() - self.cond.release() + with self.cond: + if _debug: + self._log.debug("Changing state from %d -> %d from %s" % (self.state, new_state, threading.current_thread())) + self.state = new_state + self.cond.notify() def thread_done(self): if _debug: @@ -135,23 +134,21 @@ def thread_wait(self): if _debug: self._log.debug("Waiting for the condition lock %s" % threading.current_thread()) - self.cond.acquire() - - while self.state == external_state.RUNNING: - self.cond.wait() + with self.cond: + while self.state == external_state.RUNNING: + self.cond.wait() - if _debug: - if self.state == external_state.EXITED: - self._log.debug("Thread %s has exited from %s" % (self.thread, threading.current_thread())) - elif self.state == external_state.PAUSED: - self._log.debug("Thread %s has called yield from %s" % (self.thread, threading.current_thread())) - elif self.state == external_state.RUNNING: - self._log.debug("Thread %s is in RUNNING from %d" % (self.thread, threading.current_thread())) + if _debug: + if self.state == external_state.EXITED: + self._log.debug("Thread %s has exited from %s" % (self.thread, threading.current_thread())) + elif self.state == external_state.PAUSED: + self._log.debug("Thread %s has called yield from %s" % (self.thread, threading.current_thread())) + elif self.state == external_state.RUNNING: + self._log.debug("Thread %s is in RUNNING from %d" % (self.thread, threading.current_thread())) - if self.state == external_state.INIT: - raise Exception("Thread %s state was not allowed from %s" % (self.thread, threading.current_thread())) + if self.state == external_state.INIT: + raise Exception("Thread %s state was not allowed from %s" % (self.thread, threading.current_thread())) - self.cond.release() return self.state class Scheduler(object): From 76d332189cc92dd978dd40154faa2cd6b58b3b13 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Mon, 6 May 2019 19:45:48 -0700 Subject: [PATCH 1607/2656] Fix crash due to invoking a function storing state in the function itself This test previously failed with: ``` Traceback (most recent call last): File "/mnt/c/Users/wiese/Repos/cocotb/cocotb/decorators.py", line 245, in _advance return outcome.send(self._coro) File "/mnt/c/Users/wiese/Repos/cocotb/cocotb/outcomes.py", line 51, in send return gen.throw(self.error) File "/home/eric/win-home/Repos/cocotb/tests/test_cases/test_external/test_external.py", line 380, in test_function_called_in_parallel v1 = yield t1 File "/mnt/c/Users/wiese/Repos/cocotb/cocotb/decorators.py", line 141, in _advance return outcome.send(self._coro) File "/mnt/c/Users/wiese/Repos/cocotb/cocotb/outcomes.py", line 37, in send return gen.send(self.value) File "/mnt/c/Users/wiese/Repos/cocotb/cocotb/decorators.py", line 363, in wrapper ret = ext.result # raises if there was an exception File "/mnt/c/Users/wiese/Repos/cocotb/cocotb/scheduler.py", line 105, in result return self._outcome.get() File "/mnt/c/Users/wiese/Repos/cocotb/cocotb/outcomes.py", line 54, in get raise self.error File "/mnt/c/Users/wiese/Repos/cocotb/cocotb/outcomes.py", line 17, in capture return Value(fn(*args, **kwargs)) File "/home/eric/win-home/Repos/cocotb/tests/test_cases/test_external/test_external.py", line 375, in call_function return function(x) File "/mnt/c/Users/wiese/Repos/cocotb/cocotb/decorators.py", line 339, in __call__ return self._event.outcome.get() AttributeError: 'Event' object has no attribute 'outcome' ``` The cause was sharing an Event object between all function invocations, when there should have been one per call. --- cocotb/decorators.py | 9 +++---- .../test_cases/test_external/test_external.py | 25 +++++++++++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index bae8e5f2..dad3d01e 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -330,13 +330,12 @@ def execute_function(self, event): event.outcome = _outcome event.set() - self._event = threading.Event() - self._event.result = None - waiter = cocotb.scheduler.queue_function(execute_function(self, self._event)) + event = threading.Event() + waiter = cocotb.scheduler.queue_function(execute_function(self, event)) # This blocks the calling external thread until the coroutine finishes - self._event.wait() + event.wait() waiter.thread_resume() - return self._event.outcome.get() + return event.outcome.get() def __get__(self, obj, type=None): """Permit the decorator to be used on class methods diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index 5c5fba36..d80edda6 100755 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -403,3 +403,28 @@ def ext(): assert vals.raised, "No exception was raised to warn the user" yield task.join() + +@cocotb.test() +def test_function_called_in_parallel(dut): + """ + Test that the same `@function` can be called from two parallel background + threads. + """ + # workaround for gh-637 + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + + @cocotb.function + def function(x): + yield Timer(1) + raise ReturnValue(x) + + @cocotb.external + def call_function(x): + return function(x) + + t1 = cocotb.fork(call_function(1)) + t2 = cocotb.fork(call_function(2)) + v1 = yield t1 + v2 = yield t2 + assert v1 == 1, v1 + assert v2 == 2, v2 From e3297de87bb66b8604cf6118beaaaba6f1594398 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 4 May 2019 18:22:29 -0500 Subject: [PATCH 1608/2656] Added test for issue #893 --- tests/test_cases/issue_893/Makefile | 33 +++++++++++++++++++++++++ tests/test_cases/issue_893/issue_893.py | 13 ++++++++++ 2 files changed, 46 insertions(+) create mode 100644 tests/test_cases/issue_893/Makefile create mode 100644 tests/test_cases/issue_893/issue_893.py diff --git a/tests/test_cases/issue_893/Makefile b/tests/test_cases/issue_893/Makefile new file mode 100644 index 00000000..6147ade0 --- /dev/null +++ b/tests/test_cases/issue_893/Makefile @@ -0,0 +1,33 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +include ../../designs/sample_module/Makefile + +MODULE = issue_893 diff --git a/tests/test_cases/issue_893/issue_893.py b/tests/test_cases/issue_893/issue_893.py new file mode 100644 index 00000000..020fdd74 --- /dev/null +++ b/tests/test_cases/issue_893/issue_893.py @@ -0,0 +1,13 @@ +import cocotb +from cocotb.triggers import Timer, NullTrigger + +@cocotb.coroutine +def coroutine_with_undef(): + new_variable = undefined_variable + yield Timer(1, units='ns') + +@cocotb.test(expect_error=True) +def fork_erroring_coroutine(dut): + cocotb.fork(coroutine_with_undef()) + yield Timer(10, units='ns') + From bb0dbc2bc8cb6dbfadcc73fe0316b60f36b87b03 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 4 May 2019 18:23:30 -0500 Subject: [PATCH 1609/2656] Potentially fixes #893 --- cocotb/scheduler.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index ff2f2bce..aa8d6271 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -467,11 +467,7 @@ def unschedule(self, coro): # and no one was monitoring it coro.retval except Exception as e: - self._test_result = TestError( - "Forked coroutine {} raised exception {}" - .format(coro, e) - ) - self._terminate = True + self.finish_test(create_error(self, "Forked coroutine {} raised exception: {}".format(coro, e))) def save_write(self, handle, value): if self._mode == Scheduler._MODE_READONLY: From c67550e1624d1c83b9c777a1bffcd183069ff560 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 4 May 2019 22:47:56 -0500 Subject: [PATCH 1610/2656] Added test for issue #892 --- tests/test_cases/issue_892/Makefile | 33 +++++++++++++++++++++++++ tests/test_cases/issue_892/issue_892.py | 13 ++++++++++ 2 files changed, 46 insertions(+) create mode 100644 tests/test_cases/issue_892/Makefile create mode 100644 tests/test_cases/issue_892/issue_892.py diff --git a/tests/test_cases/issue_892/Makefile b/tests/test_cases/issue_892/Makefile new file mode 100644 index 00000000..0cbf05c3 --- /dev/null +++ b/tests/test_cases/issue_892/Makefile @@ -0,0 +1,33 @@ +############################################################################### +# Copyright (c) 2013 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + + +include ../../designs/sample_module/Makefile + +MODULE = issue_892 diff --git a/tests/test_cases/issue_892/issue_892.py b/tests/test_cases/issue_892/issue_892.py new file mode 100644 index 00000000..2365fe46 --- /dev/null +++ b/tests/test_cases/issue_892/issue_892.py @@ -0,0 +1,13 @@ +import cocotb +from cocotb.triggers import Timer +from cocotb.result import TestSuccess + +@cocotb.coroutine +def raise_test_success(): + yield Timer(1, units='ns') + raise TestSuccess("TestSuccess") + +@cocotb.test() +def error_test(dut): + cocotb.fork(raise_test_success()) + yield Timer(10, units='ns') From 455c77bb4f6952b3c8fc6ba42e77930e2f233ba1 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 4 May 2019 22:50:03 -0500 Subject: [PATCH 1611/2656] Fixes #892 --- cocotb/scheduler.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index aa8d6271..a084ae03 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -466,6 +466,9 @@ def unschedule(self, coro): # throws an error if the background coroutine errored # and no one was monitoring it coro.retval + except TestComplete as test_result: + self.log.debug("TestComplete received: {}".format(test_result.__class__.__name__)) + self.finish_test(test_result) except Exception as e: self.finish_test(create_error(self, "Forked coroutine {} raised exception: {}".format(coro, e))) From 2fd84126d75f83dd4987298add26e4197170dad0 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 16 May 2019 23:13:30 +0200 Subject: [PATCH 1612/2656] Add missing envvar COCOTB_SIM=1. --- cocotb/share/makefiles/simulators/Makefile.xcelium | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.xcelium b/cocotb/share/makefiles/simulators/Makefile.xcelium index cbd7b204..ee7c9d39 100644 --- a/cocotb/share/makefiles/simulators/Makefile.xcelium +++ b/cocotb/share/makefiles/simulators/Makefile.xcelium @@ -94,8 +94,8 @@ endif GPI_LIB = $(COCOTB_VPI_LIB) $(COCOTB_VHPI_LIB) results.xml: $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(GPI_LIB) - LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(SIM_ROOT):$(PWD):$(PYTHONPATH) \ - LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ + LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(PWD):$(PYTHONPATH) \ + LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ set -o pipefail; \ $(CMD) $(EXTRA_ARGS) $(GPI_ARGS) $(INCDIRS) -access +rwc -createdebugdb $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) 2>&1 | tee $(SIM_BUILD)/sim.log From 7b760b1326ad53cff82ef6b6d9ac78c19e36d205 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 16 May 2019 22:39:56 +0200 Subject: [PATCH 1613/2656] Print path for missing object. --- cocotb/handle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 1d7113d9..949ce56b 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -775,7 +775,7 @@ def SimHandle(handle, path=None): return obj if t not in _type2cls: - raise TestError("Couldn't find a matching object for GPI type %d" % t) + raise TestError("Couldn't find a matching object for GPI type %d (path=%s)" % (t, path)) obj = _type2cls[t](handle, path) _handle2obj[handle] = obj return obj From 6919d9487959d079efb2a6a5f55f8b510627a662 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Mon, 8 Apr 2019 14:34:36 -0700 Subject: [PATCH 1614/2656] Fix ratio for very short tests. Some very short tests measure 0.00s for real time, and this causes an ZeroDivisionError exception. Set ratio to infinite when this happens. Also check if the reported sim time is 0.00ns and set ratio to Not a Number. --- cocotb/regression.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index c267392c..79767c27 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -241,7 +241,13 @@ def handle_result(self, result): """ real_time = time.time() - self._running_test.start_time sim_time_ns = get_sim_time('ns') - self._running_test.start_sim_time - ratio_time = sim_time_ns / real_time + try: + ratio_time = sim_time_ns / real_time + except ZeroDivisionError: + if round(sim_time_ns, 2) == 0: + ratio_time = float('nan') + else: + ratio_time = float('inf') self.xunit.add_testcase(name=self._running_test.funcname, classname=self._running_test.module, time=repr(real_time), From fe2f2a24a2eb8c660d7da680c0b11d459fff595d Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 16 May 2019 22:52:02 +0200 Subject: [PATCH 1615/2656] Add support for vpiInterconnectNet, new file vpi_user_ext.h. --- cocotb/share/include/embed.h | 1 + cocotb/share/include/vpi_user_ext.h | 46 +++++++++++++++++++++++++++++ cocotb/share/lib/vpi/VpiImpl.cpp | 3 ++ cocotb/share/lib/vpi/VpiImpl.h | 1 + 4 files changed, 51 insertions(+) create mode 100644 cocotb/share/include/vpi_user_ext.h diff --git a/cocotb/share/include/embed.h b/cocotb/share/include/embed.h index 0fe6d975..c1afe5f2 100644 --- a/cocotb/share/include/embed.h +++ b/cocotb/share/include/embed.h @@ -32,6 +32,7 @@ #include #include +#include #include #ifdef __cplusplus diff --git a/cocotb/share/include/vpi_user_ext.h b/cocotb/share/include/vpi_user_ext.h new file mode 100644 index 00000000..92854fa1 --- /dev/null +++ b/cocotb/share/include/vpi_user_ext.h @@ -0,0 +1,46 @@ +/****************************************************************************** +* Copyright (c) 2013, 2019 Potential Ventures Ltd +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of Potential Ventures Ltd +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +******************************************************************************/ + +/* extensions to vpi_user.h */ + +#ifndef VPI_USER_EXT_H +#define VPI_USER_EXT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* used by Cadence Xcelium for Verilog-AMS */ +#define vpiRealNet 526 +#define vpiInterconnectNet 533 +#define vpiInterconnectArray 534 + +#ifdef __cplusplus +} +#endif + +#endif /* VPI_USER_EXT_H */ diff --git a/cocotb/share/lib/vpi/VpiImpl.cpp b/cocotb/share/lib/vpi/VpiImpl.cpp index ee4fab37..17ae56fb 100644 --- a/cocotb/share/lib/vpi/VpiImpl.cpp +++ b/cocotb/share/lib/vpi/VpiImpl.cpp @@ -154,8 +154,10 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, case vpiIntegerVar: case vpiIntegerNet: case vpiRealVar: + case vpiRealNet: case vpiStringVar: case vpiMemoryWord: + case vpiInterconnectNet: new_obj = new VpiSignalObjHdl(this, new_hdl, to_gpi_objtype(type), false); break; case vpiParameter: @@ -166,6 +168,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, case vpiInterfaceArray: case vpiPackedArrayVar: case vpiMemory: + case vpiInterconnectArray: new_obj = new VpiArrayObjHdl(this, new_hdl, to_gpi_objtype(type)); break; case vpiStructVar: diff --git a/cocotb/share/lib/vpi/VpiImpl.h b/cocotb/share/lib/vpi/VpiImpl.h index b372cddb..dc17abea 100644 --- a/cocotb/share/lib/vpi/VpiImpl.h +++ b/cocotb/share/lib/vpi/VpiImpl.h @@ -29,6 +29,7 @@ #define COCOTB_VPI_IMPL_H_ #include "../gpi/gpi_priv.h" +#include #include #include #include From 5ff51feb2c18017e176beede72609f7209a3c1d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Wiecha?= Date: Tue, 11 Jun 2019 15:03:38 +0200 Subject: [PATCH 1616/2656] Make seed available through cocotb.RANDOM_SEED The seed used to initialize the random number generator is now available for tests as `cocotb.RANDOM_SEED`. Fixes #814 --- cocotb/__init__.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 238c22f0..352f7e45 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -135,20 +135,21 @@ def _initialise_testbench(root_name): process_plusargs() # Seed the Python random number generator to make this repeatable - seed = os.getenv('RANDOM_SEED') + global RANDOM_SEED + RANDOM_SEED = os.getenv('RANDOM_SEED') - if seed is None: + if RANDOM_SEED is None: if 'ntb_random_seed' in plusargs: - seed = eval(plusargs['ntb_random_seed']) + RANDOM_SEED = eval(plusargs['ntb_random_seed']) elif 'seed' in plusargs: - seed = eval(plusargs['seed']) + RANDOM_SEED = eval(plusargs['seed']) else: - seed = int(time.time()) - log.info("Seeding Python random module with %d" % (seed)) + RANDOM_SEED = int(time.time()) + log.info("Seeding Python random module with %d" % (RANDOM_SEED)) else: - seed = int(seed) - log.info("Seeding Python random module with supplied seed %d" % (seed)) - random.seed(seed) + RANDOM_SEED = int(RANDOM_SEED) + log.info("Seeding Python random module with supplied seed %d" % (RANDOM_SEED)) + random.seed(RANDOM_SEED) module_str = os.getenv('MODULE') test_str = os.getenv('TESTCASE') @@ -163,7 +164,7 @@ def _initialise_testbench(root_name): global regression_manager - regression_manager = RegressionManager(root_name, modules, tests=test_str, seed=seed, hooks=hooks) + regression_manager = RegressionManager(root_name, modules, tests=test_str, seed=RANDOM_SEED, hooks=hooks) regression_manager.initialise() regression_manager.execute() From ede23e4a40c4c3dc27a95294e1b033ae52a07b9b Mon Sep 17 00:00:00 2001 From: Fabio Garzetti Date: Wed, 29 May 2019 10:58:14 +0200 Subject: [PATCH 1617/2656] Add VHDL_SOURCES_lib (GHDL only) Currently, to include external libraries, the user has to manually run ghdl before launching cocotb. With this commit, it is now possible to specify in which library a set of VHDL files has to be placed into. To do so, the files can be specified directly in the makefile under the VHDL_SOURCES_lib variable. For example: VHDL_SOURCES = top.vhd VHDL_SOURCES_mylib = libfile.vhd libfile2.vhd Here, libfile.vhd and libfile2.vhd will be analyzed with the --work=mylib flag in GHDL before analyzing top.vhd. --- cocotb/share/makefiles/simulators/Makefile.ghdl | 6 +++++- documentation/source/building.rst | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.ghdl b/cocotb/share/makefiles/simulators/Makefile.ghdl index de113681..379f7634 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ghdl +++ b/cocotb/share/makefiles/simulators/Makefile.ghdl @@ -58,7 +58,11 @@ GHDL_ARGS += $(EXTRA_ARGS) # Compilation phase analyse: $(VHDL_SOURCES) $(SIM_BUILD) - cd $(SIM_BUILD) && $(CMD) -a $(GHDL_ARGS) $(COMPILE_ARGS) --work=$(RTL_LIBRARY) $(VHDL_SOURCES) && $(CMD) -e $(GHDL_ARGS) $(COMPILE_ARGS) --work=$(RTL_LIBRARY) $(TOPLEVEL) + cd $(SIM_BUILD) && \ + $(foreach SOURCES_VAR, $(filter VHDL_SOURCES_%, $(.VARIABLES)), \ + $(CMD) -a $(GHDL_ARGS) $(COMPILE_ARGS) --work=$(SOURCES_VAR:VHDL_SOURCES_%=%) $($(SOURCES_VAR)) && ) \ + $(CMD) -a $(GHDL_ARGS) $(COMPILE_ARGS) --work=$(RTL_LIBRARY) $(VHDL_SOURCES) && \ + $(CMD) -e $(GHDL_ARGS) $(COMPILE_ARGS) --work=$(RTL_LIBRARY) $(TOPLEVEL) results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VPI_LIB) cd $(SIM_BUILD); \ diff --git a/documentation/source/building.rst b/documentation/source/building.rst index e42347ab..4af9b14c 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -39,6 +39,9 @@ Make Variables ``VHDL_SOURCES`` A list of the VHDL source files to include. + ``VHDL_SOURCES_lib`` + A list of the VHDL source files to include in the VHDL library *lib* (currently GHDL only). + ``COMPILE_ARGS`` Any arguments or flags to pass to the compile stage of the simulation. Only applies to simulators with a separate compilation stage (currently Icarus and VCS). From b36337bcb792e38523e7bf0243ce4b4e1bdf6d94 Mon Sep 17 00:00:00 2001 From: Fabio Garzetti Date: Wed, 12 Jun 2019 11:32:22 +0200 Subject: [PATCH 1618/2656] Honor RTL_LIBRARY choice in ghdl run stage Without this, files are elaborated in RTL_LIBRARY library, but the top file in run stage is searched in "work" library. As result, specifying a RTL_LIBRARY different from "work" raises errors. --- cocotb/share/makefiles/simulators/Makefile.ghdl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.ghdl b/cocotb/share/makefiles/simulators/Makefile.ghdl index 379f7634..d4accc68 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ghdl +++ b/cocotb/share/makefiles/simulators/Makefile.ghdl @@ -68,7 +68,7 @@ results.xml: analyse $(COCOTB_LIBS) $(COCOTB_VPI_LIB) cd $(SIM_BUILD); \ PYTHONPATH=$(LIB_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ - $(CMD) -r $(GHDL_ARGS) $(TOPLEVEL) --vpi=$(LIB_DIR)/libvpi.$(LIB_EXT) $(SIM_ARGS) $(PLUSARGS) + $(CMD) -r $(GHDL_ARGS) --work=$(RTL_LIBRARY) $(TOPLEVEL) --vpi=$(LIB_DIR)/libvpi.$(LIB_EXT) $(SIM_ARGS) $(PLUSARGS) clean:: -@rm -rf $(SIM_BUILD) From 1fbe0c95399a163bbf739dead4d00572d2934b28 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 13 Jun 2019 11:13:58 +0200 Subject: [PATCH 1619/2656] More doc improvements Various documentation cleanups --- cocotb/bus.py | 5 ++-- cocotb/decorators.py | 3 +- cocotb/drivers/amba.py | 36 +++++++++++------------- cocotb/drivers/opb.py | 14 ++++----- cocotb/monitors/xgmii.py | 4 +-- cocotb/scoreboard.py | 2 +- documentation/source/triggers.rst | 16 +++++------ documentation/source/troubleshooting.rst | 8 +++++- 8 files changed, 45 insertions(+), 43 deletions(-) diff --git a/cocotb/bus.py b/cocotb/bus.py index 744e9bb9..d7751104 100755 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -45,10 +45,11 @@ def _build_sig_attr_dict(signals): class Bus(object): """Wraps up a collection of signals. - Assumes we have a set of signals/nets named ``entity._``. + Assumes we have a set of signals/nets named ``entity.``. For example a bus ``stream_in`` with signals ``valid`` and ``data`` is assumed - to be named ``dut.stream_in_valid`` and ``dut.stream_in_data``. + to be named ``dut.stream_in_valid`` and ``dut.stream_in_data`` (with + the default separator '_'). TODO: Support for struct/record ports where signals are member names. diff --git a/cocotb/decorators.py b/cocotb/decorators.py index dad3d01e..5fa0b7ce 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -430,7 +430,8 @@ class test(with_metaclass(_decorator_helper, coroutine)): Don't mark the result as a failure if the test fails. expect_error (bool, optional): Don't mark the result as an error if an error is raised. - This is for cocotb internal regression use. + This is for cocotb internal regression use + when a simulator error is expected. skip (bool, optional): Don't execute this test as part of the regression. stage (int, optional) diff --git a/cocotb/drivers/amba.py b/cocotb/drivers/amba.py index 3d7b7fe0..c13bf3e6 100644 --- a/cocotb/drivers/amba.py +++ b/cocotb/drivers/amba.py @@ -23,9 +23,9 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' -""" -Drivers for Advanced Microcontroller Bus Architecture -""" + +"""Drivers for Advanced Microcontroller Bus Architecture.""" + import cocotb from cocotb.triggers import RisingEdge, ReadOnly, Lock from cocotb.drivers import BusDriver @@ -41,11 +41,11 @@ class AXIProtocolError(Exception): class AXI4LiteMaster(BusDriver): - """ - AXI4-Lite Master + """AXI4-Lite Master. - TODO: Kill all pending transactions if reset is asserted... + TODO: Kill all pending transactions if reset is asserted. """ + _signals = ["AWVALID", "AWADDR", "AWREADY", # Write address channel "WVALID", "WREADY", "WDATA", "WSTRB", # Write data channel "BVALID", "BREADY", "BRESP", # Write response channel @@ -90,9 +90,7 @@ def _send_write_address(self, address, delay=0): @cocotb.coroutine def _send_write_data(self, data, delay=0, byte_enable=0xF): - """ - Send the write address, with optional delay (in clocks) - """ + """Send the write address, with optional delay (in clocks).""" yield self.write_data_busy.acquire() for cycle in range(delay): yield RisingEdge(self.clock) @@ -113,12 +111,11 @@ def _send_write_data(self, data, delay=0, byte_enable=0xF): @cocotb.coroutine def write(self, address, value, byte_enable=0xf, address_latency=0, data_latency=0, sync=True): - """ - Write a value to an address. + """Write a value to an address. Args: - address (int): The address to write to - value (int): The data value to write + address (int): The address to write to. + value (int): The data value to write. byte_enable (int, optional): Which bytes in value to actually write. Default is to write all bytes. address_latency (int, optional): Delay before setting the address (in clock cycles). @@ -129,10 +126,10 @@ def write(self, address, value, byte_enable=0xf, address_latency=0, Defaults to True. Returns: - BinaryValue: The write response value + BinaryValue: The write response value. Raises: - AXIProtocolError: If write response from AXI is not ``OKAY`` + AXIProtocolError: If write response from AXI is not ``OKAY``. """ if sync: yield RisingEdge(self.clock) @@ -166,19 +163,18 @@ def write(self, address, value, byte_enable=0xf, address_latency=0, @cocotb.coroutine def read(self, address, sync=True): - """ - Read from an address. + """Read from an address. Args: - address (int): The address to read from + address (int): The address to read from. sync (bool, optional): Wait for rising edge on clock initially. Defaults to True. Returns: - BinaryValue: The read data value + BinaryValue: The read data value. Raises: - AXIProtocolError: If read response from AXI is not ``OKAY`` + AXIProtocolError: If read response from AXI is not ``OKAY``. """ if sync: yield RisingEdge(self.clock) diff --git a/cocotb/drivers/opb.py b/cocotb/drivers/opb.py index bd3fdb5b..c26dae41 100644 --- a/cocotb/drivers/opb.py +++ b/cocotb/drivers/opb.py @@ -27,7 +27,7 @@ """ Drivers for On-chip Peripheral Bus. -NB Currently we only support a very small subset of functionality +NOTE: Currently we only support a very small subset of functionality. """ import random @@ -69,10 +69,9 @@ def _release_lock(self): @cocotb.coroutine def read(self, address, sync=True): - """ - Issue a request to the bus and block until this - comes back. Simulation time still progresses - but syntactically it blocks. + """Issue a request to the bus and block until this comes back. + + Simulation time still progresses but syntactically it blocks. Args: address (int): The address to read from. @@ -115,8 +114,7 @@ def read(self, address, sync=True): @cocotb.coroutine def write(self, address, value, sync=True): - """Issue a write to the given address with the specified - value. + """Issue a write to the given address with the specified value. Args: address (int): The address to read from. @@ -125,7 +123,7 @@ def write(self, address, value, sync=True): Defaults to True. Raises: - OPBException: If write took longer than 16 cycles + OPBException: If write took longer than 16 cycles. """ yield self._acquire_lock() diff --git a/cocotb/monitors/xgmii.py b/cocotb/monitors/xgmii.py index 292c152a..2621e46e 100644 --- a/cocotb/monitors/xgmii.py +++ b/cocotb/monitors/xgmii.py @@ -52,7 +52,7 @@ class XGMII(Monitor): Assumes a single vector, either 4 or 8 bytes plus control bit for each byte. - If interleaved is true then the control bits are adjacent to the bytes. + If interleaved is ``True`` then the control bits are adjacent to the bytes. """ def __init__(self, signal, clock, interleaved=True, callback=None, @@ -78,7 +78,7 @@ def __init__(self, signal, clock, interleaved=True, callback=None, Monitor.__init__(self, callback=callback, event=event) def _get_bytes(self): - """Take a value and extract the individual bytes / ctrl bits. + """Take a value and extract the individual bytes and control bits. Returns a tuple of lists. """ diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index 16d302c7..8631dfbe 100755 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -186,7 +186,7 @@ def add_interface(self, monitor, expected_output, compare_fn=None, Args: monitor: The monitor object. expected_output: Queue of expected outputs. - compare_fn (callable, optional): + compare_fn (callable, optional): Function doing the actual comparison. reorder_depth (int, optional): Consider up to *reorder_depth* elements of the expected result list as passing matches. Default is 0, meaning only the first element in the expected result list diff --git a/documentation/source/triggers.rst b/documentation/source/triggers.rst index 4425dcbd..17f89ab8 100644 --- a/documentation/source/triggers.rst +++ b/documentation/source/triggers.rst @@ -8,12 +8,12 @@ while it is waiting for them to complete. Simulation Timing ----------------- -:class:`Timer(time) <.Timer()>`: +:class:`Timer(time) <.Timer()>` Registers a timed callback with the simulator to continue execution of the coroutine after a specified simulation time period has elapsed. -:class:`.ReadOnly()`: +:class:`.ReadOnly()` Registers a callback which will continue execution of the coroutine when the current simulation timestep moves to the :any:`ReadOnly` phase of the RTL simulator. The :any:`ReadOnly` phase is entered when the current timestep no longer has any further delta steps. This should be a point where all the signal values are stable as there are no more RTL events scheduled for the timestep. @@ -24,25 +24,25 @@ Simulation Timing Signal related -------------- -:class:`Edge(signal) <.Edge()>`: +:class:`Edge(signal) <.Edge()>` Registers a callback that will continue execution of the coroutine on any value change of *signal*. -:class:`RisingEdge(signal) <.RisingEdge()>`: +:class:`RisingEdge(signal) <.RisingEdge()>` Registers a callback that will continue execution of the coroutine on a transition from ``0`` to ``1`` of *signal*. -:class:`FallingEdge(signal) <.FallingEdge()>`: +:class:`FallingEdge(signal) <.FallingEdge()>` Registers a callback that will continue execution of the coroutine on a transition from ``1`` to ``0`` of *signal*. -:class:`ClockCycles(signal, num_cycles) <.ClockCycles>`: +:class:`ClockCycles(signal, num_cycles) <.ClockCycles>` Registers a callback that will continue execution of the coroutine when *num_cycles* transitions from ``0`` to ``1`` have occured on *signal*. Python Triggers --------------- -:class:`.Event()`: +:class:`.Event()` Can be used to synchronise between coroutines. Yielding :meth:`.Event.wait()` will block the coroutine until :meth:`.Event.set()` is called somewhere else. -:class:`Join(coroutine_2) <.Join()>`: +:class:`Join(coroutine_2) <.Join()>` Will block the coroutine until *coroutine_2* has completed. diff --git a/documentation/source/troubleshooting.rst b/documentation/source/troubleshooting.rst index 660ee03e..fb5d16e6 100644 --- a/documentation/source/troubleshooting.rst +++ b/documentation/source/troubleshooting.rst @@ -2,6 +2,12 @@ Troubleshooting ############### +Simulation Hangs +================ + +Did you call a function that is marked as a coroutine directly, i.e. without using ``yield``? + + Increasing Verbosity ==================== @@ -15,7 +21,7 @@ Attaching a Debugger In order to give yourself time to attach a debugger to the simulator process before it starts to run, you can set the environment variable ``COCOTB_ATTACH`` to a pause time value in seconds. -If set, Cocotb will print the process ID (PID) to attach to and wait the specified time before +If set, cocotb will print the process ID (PID) to attach to and wait the specified time before actually letting the simulator run. For the GNU debugger GDB, the command is ``attach ``. From 5a84aac9bfbb45afb11ecf38b96b5940ac1f1ba1 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 13 Jun 2019 11:15:23 +0200 Subject: [PATCH 1620/2656] Use breathe for converting XML to ReST. (#776) --- documentation/.gitignore | 2 + documentation/Doxyfile | 2427 ++++++++++++++++++ documentation/Makefile | 5 +- documentation/requirements.txt | 3 +- documentation/source/conf.py | 29 +- documentation/source/index.rst | 1 + documentation/source/library_reference_c.rst | 21 + 7 files changed, 2478 insertions(+), 10 deletions(-) create mode 100644 documentation/Doxyfile create mode 100644 documentation/source/library_reference_c.rst diff --git a/documentation/.gitignore b/documentation/.gitignore index 1d17dae1..9504ecc8 100644 --- a/documentation/.gitignore +++ b/documentation/.gitignore @@ -1 +1,3 @@ .venv +source/generated +source/doxygen diff --git a/documentation/Doxyfile b/documentation/Doxyfile new file mode 100644 index 00000000..bfb46ea3 --- /dev/null +++ b/documentation/Doxyfile @@ -0,0 +1,2427 @@ +# Doxyfile 1.8.11 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "cocotb" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = source/doxygen + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = NO + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = NO + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = NO + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = ../cocotb/share/lib/ + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f, *.for, *.tcl, +# *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js. + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse-libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = NO + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , / + +{{ super() }} +{% endblock %} From f069c12b1544a26438589e1b021364dc18f34841 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Fri, 3 Apr 2020 10:36:36 +0100 Subject: [PATCH 2055/2656] Ensure source files are read as utf8, rather than a system-specific encoding. (#1557) We authored the files being read here, so we know their encoding in advance. This fixes a CI failure in Python 3.5. Also switch to use a `with` statement to ensure the file is closed a little sooner. --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index f25e7a37..bbf31013 100755 --- a/setup.py +++ b/setup.py @@ -44,7 +44,8 @@ from cocotb._build_libs import get_ext, build_ext def read_file(fname): - return open(path.join(path.dirname(__file__), fname)).read() + with open(path.join(path.dirname(__file__), fname), encoding='utf8') as f: + return f.read() def package_files(directory): paths = [] From 1d19858daa8c82160f0874c7625b4c7dec4d547f Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 3 Apr 2020 03:11:13 -0700 Subject: [PATCH 2056/2656] Change type for HDL StringObject to bytes (#1381) The simulator interfaces only deal in bytes, however the current implementation takes a string object. By default PyArg_ParseTuple will encode the string using UTF-8. Now, string encoding/decoding responsibility has been lifted into userspace. --- cocotb/handle.py | 17 ++++++++-- .../share/lib/simulator/simulatormodule.cpp | 4 +-- .../source/newsfragments/1381.removal.rst | 1 + tests/test_cases/test_cocotb/test_cocotb.py | 10 ++++++ .../test_cases/test_cocotb/test_deprecated.py | 31 +++++++++++++++---- .../test_discovery/test_discovery.py | 12 +++---- 6 files changed, 59 insertions(+), 16 deletions(-) create mode 100644 documentation/source/newsfragments/1381.removal.rst diff --git a/cocotb/handle.py b/cocotb/handle.py index a1eb9f12..a2f7061b 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -802,15 +802,28 @@ def setimmediatevalue(self, value): object, e.g. net, signal or variable. Args: - value (str): The value to drive onto the simulator object. + value (bytes): The value to drive onto the simulator object. Raises: TypeError: If target has an unsupported type for string value assignment. + + .. versionchanged:: 1.4 + Takes :class:`bytes` instead of :class:`str`. + Users are now expected to choose an encoding when using these objects. + As a convenience, when assigning :class:`str` values, ASCII encoding will be used as a safe default. + """ value, set_action = self._check_for_set_action(value) - if not isinstance(value, str): + if isinstance(value, str): + warnings.warn( + "Handles on string objects will soon not accept `str` objects. " + "Please use a bytes object by encoding the string as you see fit. " + "`str.encode('ascii')` is typically sufficient.", DeprecationWarning, stacklevel=2) + value = value.encode('ascii') # may throw UnicodeEncodeError + + if not isinstance(value, bytes): self._log.critical("Unsupported type for string value assignment: %s (%s)", type(value), repr(value)) raise TypeError("Unable to set simulator value with type %s" % (type(value))) diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index cb530959..f9137430 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -626,7 +626,7 @@ static PyObject *get_signal_val_str(PyObject *self, PyObject *args) } result = gpi_get_signal_value_str(hdl); - retstr = Py_BuildValue("s", result); + retstr = PyBytes_FromString(result); return retstr; } @@ -688,7 +688,7 @@ static PyObject *set_signal_val_str(PyObject *self, PyObject *args) gpi_set_action_t action; const char *str; - if (!PyArg_ParseTuple(args, "O&is", gpi_sim_hdl_converter, &hdl, &action, &str)) { + if (!PyArg_ParseTuple(args, "O&iy", gpi_sim_hdl_converter, &hdl, &action, &str)) { return NULL; } diff --git a/documentation/source/newsfragments/1381.removal.rst b/documentation/source/newsfragments/1381.removal.rst new file mode 100644 index 00000000..baa04db6 --- /dev/null +++ b/documentation/source/newsfragments/1381.removal.rst @@ -0,0 +1 @@ +The value of :class:`cocotb.handle.StringObject`\ s is now of type :class:`bytes`, instead of :class:`str` with an implied ASCII encoding scheme. diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 825f9e58..f341f262 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -1436,3 +1436,13 @@ async def test_except_lock(dut): pass async with lock: pass + + +# strings are not supported on Icarus +@cocotb.test(skip=cocotb.SIM_NAME.lower().startswith("icarus")) +async def test_string_handle_takes_bytes(dut): + dut.string_input_port.value = b"bytes" + await cocotb.triggers.Timer(10, 'ns') + val = dut.string_input_port.value + assert isinstance(val, bytes) + assert val == b"bytes" diff --git a/tests/test_cases/test_cocotb/test_deprecated.py b/tests/test_cases/test_cocotb/test_deprecated.py index 14cc46f8..a2bc69b7 100644 --- a/tests/test_cases/test_cocotb/test_deprecated.py +++ b/tests/test_cases/test_cocotb/test_deprecated.py @@ -1,19 +1,38 @@ import cocotb import warnings +from contextlib import contextmanager + + +@contextmanager +def assert_deprecated(): + warns = [] + try: + with warnings.catch_warnings(record=True) as warns: + # Cause all warnings to always be triggered. + warnings.simplefilter("always") + yield warns # note: not a cocotb yield, but a contextlib one! + finally: + assert len(warns) == 1 + assert issubclass(warns[0].category, DeprecationWarning) @cocotb.test() async def test_returnvalue_deprecated(dut): + @cocotb.coroutine def get_value(): yield cocotb.triggers.Timer(1, units='ns') raise cocotb.result.ReturnValue(42) - with warnings.catch_warnings(record=True) as w: - # Cause all warnings to always be triggered. - warnings.simplefilter("always") + with assert_deprecated() as warns: val = await get_value() assert val == 42 - assert len(w) == 1 - assert issubclass(w[-1].category, DeprecationWarning) - assert "return statement instead" in str(w[-1].message) + assert "return statement instead" in str(warns[0].message) + + +# strings are not supported on Icarus +@cocotb.test(skip=cocotb.SIM_NAME.lower().startswith("icarus")) +async def test_unicode_handle_assignment_deprecated(dut): + with assert_deprecated() as warns: + dut.string_input_port <= "Bad idea" + assert "bytes" in str(warns[0].message) diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 023f12b6..6a42fff5 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -285,14 +285,14 @@ def access_const_string_verilog(dut): tlog.info("%r is %s" % (string_const, str(string_const))) if not isinstance(string_const, StringObject): raise TestFailure("STRING_CONST was not StringObject") - if string_const != "TESTING_CONST": + if string_const != b"TESTING_CONST": raise TestFailure("STRING_CONST was not == \'TESTING_CONST\'") tlog.info("Modifying const string") - string_const <= "MODIFIED" + string_const <= b"MODIFIED" yield Timer(10) string_const = dut.STRING_CONST; - if string_const != "TESTING_CONST": + if string_const != b"TESTING_CONST": raise TestFailure("STRING_CONST was not still \'TESTING_CONST\'") @@ -307,14 +307,14 @@ def access_var_string_verilog(dut): tlog.info("%r is %s" % (string_var, str(string_var))) if not isinstance(string_var, StringObject): raise TestFailure("STRING_VAR was not StringObject") - if string_var != "TESTING_VAR": + if string_var != b"TESTING_VAR": raise TestFailure("STRING_VAR was not == \'TESTING_VAR\'") tlog.info("Modifying var string") - string_var <= "MODIFIED" + string_var <= b"MODIFIED" yield Timer(10) string_var = dut.STRING_VAR; - if string_var != "MODIFIED": + if string_var != b"MODIFIED": raise TestFailure("STRING_VAR was not == \'MODIFIED\'") From 1d27d5bedd6fccdefd4660b2ab84e3795b865286 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Thu, 2 Apr 2020 19:34:46 +0100 Subject: [PATCH 2057/2656] Remove non-ASCII character from README.md It breaks CI on Python 3.5. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dc7245e1..17805c54 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -**Your input is needed!** Please help out by taking 10 minutes to fill out this year’s cocotb user survey. This survey gives the development community important feedback to steer the future of cocotb into the right direction for your use case. +**Your input is needed!** Please help out by taking 10 minutes to fill out this year's cocotb user survey. This survey gives the development community important feedback to steer the future of cocotb into the right direction for your use case. [**Take the cocotb user survey now**](https://docs.google.com/forms/d/e/1FAIpQLSfD36PldzszbuZjysss3AMvxkf6XCtSbDTVh9qVNNYDaHTZ_w/viewform). --- From 4df899f52231925f37fad0d5f626043954df2100 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Fri, 3 Apr 2020 14:57:49 +0100 Subject: [PATCH 2058/2656] More helpful Python version check in setup.py In setup.py we check if Python is sufficiently recent. For users running the installation from Python 2 this can be a blocker to install (and subsequently use) cocotb, we therefore need to invest into making it easy for users to diagnose and fix the problem. In many cases Python 3 is available on a user's system, but not used, commonly due to the use of 'pip' instead of 'pip3'. Explicitly add a hint in this regard and point to the documentation for more details. To make the error message even more friendly, remove the backtrace generated as part of `RuntimeError()` and use `SystemExit()` instead. On Ubuntu 18.04/2.7.17: Before: ``` philipp@philipp-VirtualBox:~/src/cocotb$ pip install . Processing /home/philipp/src/cocotb Complete output from command python setup.py egg_info: Traceback (most recent call last): File "", line 1, in File "/tmp/pip-kZcdoF-build/setup.py", line 33, in raise RuntimeError("Python version >= 3.5 required.") RuntimeError: Python version >= 3.5 required. ---------------------------------------- Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-kZcdoF-build/ ``` after: ``` philipp@philipp-VirtualBox:~/src/cocotb$ pip install . Processing /home/philipp/src/cocotb Complete output from command python setup.py egg_info: This version of cocotb requires at least Python 3.5, you are running Python 2.7.17. If you have Python 3 installed on your machine try using 'python3 -m pip' instead of 'pip' to install cocotb. For more information please refer to the documentation at https://cocotb.readthedocs.io. ---------------------------------------- Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-_N_j3r-build/ ``` --- setup.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index bbf31013..0397a321 100755 --- a/setup.py +++ b/setup.py @@ -30,7 +30,22 @@ import sys if sys.version_info[:2] < (3, 5): - raise RuntimeError("Python version >= 3.5 required.") + msg = [ + "This version of cocotb requires at least Python 3.5,", + "you are running Python %d.%d.%d." % ( + sys.version_info[0], sys.version_info[1], sys.version_info[2]) + ] + if sys.version_info[0] == 2: + msg += [ + "If you have Python 3 installed on your machine try ", + "using 'python3 -m pip' instead of 'pip' to install cocotb." + ] + msg += [ + "For more information please refer to the documentation at ", + "https://cocotb.readthedocs.io." + ] + + raise SystemExit("\n".join(msg)) import logging from setuptools import setup From c164a20a8ffbfa6d6ea72303dcb695910980a7a4 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 12 Mar 2020 00:49:02 +0100 Subject: [PATCH 2059/2656] Deprecate COCOTB_NVC_TRACE with doc reference Introduce a new Makefile.deprecations to collect make variable deprecation warnings in a central place. --- cocotb/share/makefiles/Makefile.deprecations | 7 +++++++ cocotb/share/makefiles/Makefile.inc | 1 + cocotb/share/makefiles/Makefile.sim | 1 - documentation/source/building.rst | 4 ++++ documentation/source/newsfragments/1495.removal.rst | 5 +++++ documentation/source/simulator_support.rst | 7 +++++++ 6 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 cocotb/share/makefiles/Makefile.deprecations create mode 100644 documentation/source/newsfragments/1495.removal.rst diff --git a/cocotb/share/makefiles/Makefile.deprecations b/cocotb/share/makefiles/Makefile.deprecations new file mode 100644 index 00000000..3d1e3bce --- /dev/null +++ b/cocotb/share/makefiles/Makefile.deprecations @@ -0,0 +1,7 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause + +ifdef COCOTB_NVC_TRACE + $(warning COCOTB_NVC_TRACE is deprecated, see the "Simulator Support" section in the documentation.) +endif diff --git a/cocotb/share/makefiles/Makefile.inc b/cocotb/share/makefiles/Makefile.inc index 08677c9c..dcfbe317 100644 --- a/cocotb/share/makefiles/Makefile.inc +++ b/cocotb/share/makefiles/Makefile.inc @@ -62,6 +62,7 @@ OS=Msys endif export OS +include $(COCOTB_SHARE_DIR)/makefiles/Makefile.deprecations include $(COCOTB_SHARE_DIR)/makefiles/Makefile.pylib # Default to Icarus if no simulator is defined diff --git a/cocotb/share/makefiles/Makefile.sim b/cocotb/share/makefiles/Makefile.sim index 9eb75d69..72c0e906 100644 --- a/cocotb/share/makefiles/Makefile.sim +++ b/cocotb/share/makefiles/Makefile.sim @@ -63,7 +63,6 @@ COCOTB_HDL_TIMEUNIT Default time unit for simulation COCOTB_HDL_TIMEPRECISION Default time precision for simulation CUSTOM_COMPILE_DEPS Add additional dependencies to the compilation target CUSTOM_SIM_DEPS Add additional dependencies to the simulation target -COCOTB_NVC_TRACE Set this to 1 to enable display of VHPI traces for NVC SIM_BUILD Define a scratch directory for use by the simulator Environment Variables diff --git a/documentation/source/building.rst b/documentation/source/building.rst index f09cdc12..8d5f92c0 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -132,6 +132,10 @@ and Set this to 1 to enable display of VHPI traces when using the NVC VHDL simulator. + .. deprecated:: 1.4 + + Replaced by the instructions in :ref:`sim-nvc`. + .. make:var:: SIM_BUILD Use to define a scratch directory for use by the simulator. The path is relative to the Makefile location. diff --git a/documentation/source/newsfragments/1495.removal.rst b/documentation/source/newsfragments/1495.removal.rst new file mode 100644 index 00000000..a776f6d8 --- /dev/null +++ b/documentation/source/newsfragments/1495.removal.rst @@ -0,0 +1,5 @@ +The makefile variable +:make:var:`COCOTB_NVC_TRACE` +that was not supported for all simulators has been deprecated. +Using it prints a deprecation warning and points to documentation (:ref:`simulator-support`) +explaining how to replace it by other means. diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index b9c5f44e..7f508f38 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -150,3 +150,10 @@ GHDL Support is preliminary. Noteworthy is that despite GHDL being a VHDL simulator, it implements the VPI interface. + +.. _sim-nvc: + +NVC +=== + +To enable display of VHPI traces, use ``SIM_ARGS=--vhpi-trace make ...``. From a7f4e3ee8f2ff8aedf25a8e0767d378daf19bfd1 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Sat, 4 Apr 2020 07:05:38 +0200 Subject: [PATCH 2060/2656] Compile all common libraries once to a single directory (#1425) With this change, common libraries are compiled once to the `/cocotb/libs` directory, rather than to simulator-specific subdirectories which contain identical copies of the same libraries. This results in a faster compilation, less disk space, and easier use. To avoid name collisions, the `libcocotb{vpi,vhpi,fli}` libraries are compiled with a `_simulator_name` suffix. `_` was used as a separator instead of `.` as the latter is interpreted as a directory separator by setuptools. As a result of all the libraries being in the same place, there is no need to set `LD_LIBRARY_PATH` in any of the makefiles any more. This should mean less potential collision with user environment settings. Finally, this moves the `simulator` module from `cocotb/libs/` to `cocotb/`, meaning python should now access it as `cocotb.simulator`. Checked with all supported (Linux) simulators. Resolves gh-1181 Co-Authored-By: Eric Wieser --- cocotb/_build_libs.py | 59 +++++++++++-------- cocotb/handle.py | 2 +- cocotb/log.py | 2 +- cocotb/regression.py | 2 +- cocotb/share/lib/fli/FliImpl.cpp | 2 +- cocotb/share/makefiles/Makefile.inc | 2 +- cocotb/share/makefiles/Makefile.sim | 1 - .../share/makefiles/simulators/Makefile.aldec | 16 +++-- .../share/makefiles/simulators/Makefile.cvc | 12 ++-- .../share/makefiles/simulators/Makefile.ghdl | 4 +- .../makefiles/simulators/Makefile.icarus | 12 ++-- .../share/makefiles/simulators/Makefile.ius | 10 ++-- .../share/makefiles/simulators/Makefile.nvc | 9 ++- .../makefiles/simulators/Makefile.questa | 12 ++-- .../share/makefiles/simulators/Makefile.vcs | 7 +-- .../makefiles/simulators/Makefile.verilator | 8 +-- .../makefiles/simulators/Makefile.xcelium | 10 ++-- cocotb/triggers.py | 2 +- cocotb/utils.py | 2 +- .../source/newsfragments/1425.change.rst | 4 ++ examples/endian_swapper/tests/Makefile | 1 + tests/test_cases/issue_253/Makefile | 12 ++-- .../test_cocotb_array.py | 2 +- 23 files changed, 102 insertions(+), 91 deletions(-) create mode 100644 documentation/source/newsfragments/1425.change.rst diff --git a/cocotb/_build_libs.py b/cocotb/_build_libs.py index d3bf5d3a..7b1e4cb4 100755 --- a/cocotb/_build_libs.py +++ b/cocotb/_build_libs.py @@ -38,6 +38,24 @@ def run(self): super().run() + # On osx need extra extra rpath with install_name_tool to find libraries + if sys.platform == "darwin": + def build_extension(self, ext): + super().build_extension(ext) + + subprocess.run([ + "install_name_tool", + "-add_rpath", + "@loader_path", + os.path.join(self.build_lib, ext._file_name), + ], check=True) + subprocess.run([ + "install_name_tool", + "-add_rpath", + "@loader_path/libs", + os.path.join(self.build_lib, ext._file_name), + ], check=True) + # Needed for Windows to not assume python module (generate interface in def file) def get_export_symbols(self, ext): return None @@ -62,9 +80,8 @@ def get_ext_filename(self, ext_name): filename_short = os.path.join(head, tail_split[0] + "." + _get_lib_ext_name()) # icarus requires vpl extension - if "icarus" in filename: - filename_short = filename_short.replace("libcocotbvpi.so", "libcocotbvpi.vpl") - filename_short = filename_short.replace("libcocotbvpi.dll", "libcocotbvpi.vpl") + filename_short = filename_short.replace("libcocotbvpi_icarus.so", "libcocotbvpi_icarus.vpl") + filename_short = filename_short.replace("libcocotbvpi_icarus.dll", "libcocotbvpi_icarus.vpl") return filename_short @@ -74,9 +91,7 @@ def finalize_options(self): super().finalize_options() for ext in self.extensions: - ext.library_dirs.append( - os.path.join(self.build_lib, os.path.dirname(ext._full_name)) - ) + ext.library_dirs.append(os.path.join(self.build_lib, "cocotb", "libs")) def copy_extensions_to_source(self): """ Like the base class method, but copy libs into proper directory in develop. """ @@ -126,7 +141,7 @@ def _extra_link_args(lib_name): """ if sys.platform == "darwin": - return ["-Wl,-install_name,@loader_path/%s.so" % lib_name] + return ["-Wl,-install_name,@rpath/%s.so" % lib_name] else: return [] @@ -158,6 +173,7 @@ def _get_python_lib(): return python_lib + # TODO [gh-1372]: make this work for MSVC which has a different flag syntax _base_warns = ["-Wall", "-Wextra", "-Wcast-qual", "-Wwrite-strings", "-Wconversion"] _ccx_warns = _base_warns + ["-Wnon-virtual-dtor", "-Woverloaded-virtual"] @@ -166,7 +182,8 @@ def _get_python_lib(): # Make PRI* format macros available with C++11 compiler but older libc, e.g. on RHEL6. _extra_cxx_compile_args += ["-D__STDC_FORMAT_MACROS"] -def _get_common_lib_ext(include_dir, share_lib_dir, sim_define): +def _get_common_lib_ext(include_dir, share_lib_dir): + """ Defines common libraries. @@ -178,7 +195,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir, sim_define): # libcocotbutils # libcocotbutils = Extension( - os.path.join("cocotb", "libs", sim_define.lower(), "libcocotbutils"), + os.path.join("cocotb", "libs", "libcocotbutils"), include_dirs=[include_dir], sources=[os.path.join(share_lib_dir, "utils", "cocotb_utils.cpp")], extra_link_args=_extra_link_args("libcocotbutils"), @@ -193,7 +210,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir, sim_define): python_lib_dirs = [sysconfig.get_config_var("LIBDIR")] libgpilog = Extension( - os.path.join("cocotb", "libs", sim_define.lower(), "libgpilog"), + os.path.join("cocotb", "libs", "libgpilog"), include_dirs=[include_dir], libraries=[_get_python_lib_link(), "cocotbutils"], library_dirs=python_lib_dirs, @@ -206,7 +223,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir, sim_define): # libcocotb # libcocotb = Extension( - os.path.join("cocotb", "libs", sim_define.lower(), "libcocotb"), + os.path.join("cocotb", "libs", "libcocotb"), define_macros=[("PYTHON_SO_LIB", _get_python_lib())], include_dirs=[include_dir], libraries=[_get_python_lib_link(), "gpilog", "cocotbutils"], @@ -220,7 +237,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir, sim_define): # libgpi # libgpi = Extension( - os.path.join("cocotb", "libs", sim_define.lower(), "libgpi"), + os.path.join("cocotb", "libs", "libgpi"), define_macros=[("LIB_EXT", _get_lib_ext_name()), ("SINGLETON_HANDLES", "")], include_dirs=[include_dir], libraries=["cocotbutils", "gpilog", "cocotb", "stdc++"], @@ -236,12 +253,13 @@ def _get_common_lib_ext(include_dir, share_lib_dir, sim_define): # simulator # libsim = Extension( - os.path.join("cocotb", "libs", sim_define.lower(), "simulator"), + os.path.join("cocotb", "simulator"), include_dirs=[include_dir], libraries=[_get_python_lib_link(), "cocotbutils", "gpilog", "gpi"], library_dirs=python_lib_dirs, sources=[os.path.join(share_lib_dir, "simulator", "simulatormodule.cpp")], extra_compile_args=_extra_cxx_compile_args, + extra_link_args=["-Wl,-rpath,$ORIGIN/libs"], ) return [libcocotbutils, libgpilog, libcocotb, libgpi, libsim] @@ -251,7 +269,7 @@ def _get_vpi_lib_ext( include_dir, share_lib_dir, sim_define, extra_lib=[], extra_lib_dir=[] ): libcocotbvpi = Extension( - os.path.join("cocotb", "libs", sim_define.lower(), "libcocotbvpi"), + os.path.join("cocotb", "libs", "libcocotbvpi_" + sim_define.lower()), define_macros=[("VPI_CHECKING", "1")] + [(sim_define, "")], include_dirs=[include_dir], libraries=["gpi", "gpilog"] + extra_lib, @@ -271,7 +289,7 @@ def _get_vhpi_lib_ext( include_dir, share_lib_dir, sim_define, extra_lib=[], extra_lib_dir=[] ): libcocotbvhpi = Extension( - os.path.join("cocotb", "libs", sim_define.lower(), "libcocotbvhpi"), + os.path.join("cocotb", "libs", "libcocotbvhpi_" + sim_define.lower()), include_dirs=[include_dir], define_macros=[("VHPI_CHECKING", 1)] + [(sim_define, "")], libraries=["gpi", "gpilog", "stdc++"] + extra_lib, @@ -302,6 +320,8 @@ def get_ext(): logger.info("Compiling interface libraries for cocotb ...") + ext += _get_common_lib_ext(include_dir, share_lib_dir) + # # Icarus Verilog # @@ -312,7 +332,6 @@ def get_ext(): icarus_extra_lib = ["icarus"] icarus_extra_lib_path = [share_def_dir] - ext += _get_common_lib_ext(include_dir, share_lib_dir, sim_define="ICARUS") icarus_vpi_ext = _get_vpi_lib_ext( include_dir=include_dir, share_lib_dir=share_lib_dir, @@ -332,7 +351,6 @@ def get_ext(): modelsim_extra_lib = ["modelsim"] modelsim_extra_lib_path = [share_def_dir] - ext += _get_common_lib_ext(include_dir, share_lib_dir, sim_define="MODELSIM") modelsim_vpi_ext = _get_vpi_lib_ext( include_dir=include_dir, share_lib_dir=share_lib_dir, @@ -353,7 +371,7 @@ def get_ext(): mti_path = os.path.join(modelsim_include_dir, "mti.h") if os.path.isfile(mti_path): fli_ext = Extension( - os.path.join("cocotb", "libs", "modelsim", "libfli"), + os.path.join("cocotb", "libs", "libcocotbfli_modelsim"), include_dirs=[include_dir, modelsim_include_dir], libraries=["gpi", "gpilog", "stdc++"] + modelsim_extra_lib, library_dirs=modelsim_extra_lib_path, @@ -381,7 +399,6 @@ def get_ext(): # if os.name == "posix": logger.info("Compiling libraries for GHDL") - ext += _get_common_lib_ext(include_dir, share_lib_dir, sim_define="GHDL") ghdl_vpi_ext = _get_vpi_lib_ext( include_dir=include_dir, share_lib_dir=share_lib_dir, sim_define="GHDL" ) @@ -392,7 +409,6 @@ def get_ext(): # if os.name == "posix": logger.info("Compiling libraries for Incisive/Xcelium") - ext += _get_common_lib_ext(include_dir, share_lib_dir, sim_define="IUS") ius_vpi_ext = _get_vpi_lib_ext( include_dir=include_dir, share_lib_dir=share_lib_dir, sim_define="IUS" ) @@ -408,7 +424,6 @@ def get_ext(): # if os.name == "posix": logger.info("Compiling libraries for VCS") - ext += _get_common_lib_ext(include_dir, share_lib_dir, sim_define="VCS") vcs_vpi_ext = _get_vpi_lib_ext( include_dir=include_dir, share_lib_dir=share_lib_dir, sim_define="VCS" ) @@ -424,7 +439,6 @@ def get_ext(): aldec_extra_lib = ["aldec"] aldec_extra_lib_path = [share_def_dir] - ext += _get_common_lib_ext(include_dir, share_lib_dir, sim_define="ALDEC") aldec_vpi_ext = _get_vpi_lib_ext( include_dir=include_dir, share_lib_dir=share_lib_dir, @@ -448,7 +462,6 @@ def get_ext(): # if os.name == "posix": logger.info("Compiling libraries for Verilator") - ext += _get_common_lib_ext(include_dir, share_lib_dir, sim_define="VERILATOR") verilator_vpi_ext = _get_vpi_lib_ext( include_dir=include_dir, share_lib_dir=share_lib_dir, sim_define="VERILATOR" ) diff --git a/cocotb/handle.py b/cocotb/handle.py index a2f7061b..4ad0db1c 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -36,7 +36,7 @@ import os if "COCOTB_SIM" in os.environ: - import simulator + from cocotb import simulator else: simulator = None diff --git a/cocotb/log.py b/cocotb/log.py index 2eb32aba..ac3c0e13 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -96,7 +96,7 @@ def default_config(): # Notify GPI of log level, which it uses as an optimization to avoid # calling into Python. if "COCOTB_SIM" in os.environ: - import simulator + from cocotb import simulator simulator.log_level(_default_log) diff --git a/cocotb/regression.py b/cocotb/regression.py index c4c18a1a..6707e357 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -41,7 +41,7 @@ _pdb_on_exception = False if "COCOTB_SIM" in os.environ: - import simulator + from cocotb import simulator else: simulator = None diff --git a/cocotb/share/lib/fli/FliImpl.cpp b/cocotb/share/lib/fli/FliImpl.cpp index 9a82d674..c69e76b3 100644 --- a/cocotb/share/lib/fli/FliImpl.cpp +++ b/cocotb/share/lib/fli/FliImpl.cpp @@ -1047,5 +1047,5 @@ void cocotb_init() } // extern "C" -GPI_ENTRY_POINT(fli, register_embed); +GPI_ENTRY_POINT(cocotbfli, register_embed); diff --git a/cocotb/share/makefiles/Makefile.inc b/cocotb/share/makefiles/Makefile.inc index dcfbe317..78c63287 100644 --- a/cocotb/share/makefiles/Makefile.inc +++ b/cocotb/share/makefiles/Makefile.inc @@ -80,7 +80,7 @@ ifeq ($(SIM_DEFINE),XCELIUM) SIM_DEFINE = IUS endif -export LIB_DIR=$(COCOTB_PY_DIR)/cocotb/libs/$(shell echo $(SIM_DEFINE) | tr A-Z a-z) +export LIB_DIR=$(COCOTB_PY_DIR)/cocotb/libs ifeq ($(OS),Msys) LIB_EXT := dll diff --git a/cocotb/share/makefiles/Makefile.sim b/cocotb/share/makefiles/Makefile.sim index 72c0e906..c409a8f6 100644 --- a/cocotb/share/makefiles/Makefile.sim +++ b/cocotb/share/makefiles/Makefile.sim @@ -129,7 +129,6 @@ CUSTOM_SIM_DEPS += $(shell find $(COCOTB_PY_DIR)/cocotb/ -name "*.py") PWD := $(shell pwd) -#include $(COCOTB_SHARE_DIR)/lib/Makefile include $(COCOTB_SHARE_DIR)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) # Append directory containing the cocotb Python package to PYTHONPATH diff --git a/cocotb/share/makefiles/simulators/Makefile.aldec b/cocotb/share/makefiles/simulators/Makefile.aldec index 9b2a0e05..47e1f952 100644 --- a/cocotb/share/makefiles/simulators/Makefile.aldec +++ b/cocotb/share/makefiles/simulators/Makefile.aldec @@ -84,29 +84,28 @@ endif GPI_EXTRA:= ifeq ($(TOPLEVEL_LANG),verilog) - GPI_ARGS = -pli $(LIB_DIR)/libcocotbvpi + GPI_ARGS = -pli $(LIB_DIR)/libcocotbvpi_aldec ifneq ($(VHDL_SOURCES),) - GPI_EXTRA = cocotbvhpi + GPI_EXTRA = cocotbvhpi_aldec:cocotbvhpi_entry_point endif else ifeq ($(TOPLEVEL_LANG),vhdl) ifeq ($(OS),Msys) - VHPI_LIB = $(shell sh -c 'cd $(LIB_DIR) && pwd -W')/libcocotbvhpi + VHPI_LIB = $(shell sh -c 'cd $(LIB_DIR) && pwd -W')/libcocotbvhpi_aldec else - VHPI_LIB = $(LIB_DIR)/libcocotbvhpi + VHPI_LIB = $(LIB_DIR)/libcocotbvhpi_aldec endif GPI_ARGS = -loadvhpi $(VHPI_LIB):vhpi_startup_routines_bootstrap ifneq ($(VERILOG_SOURCES),) - GPI_EXTRA = cocotbvpi + GPI_EXTRA = cocotbvpi_aldec:cocotbvpi_entry_point endif else $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") endif - # Create a TCL script based on the list of $(VERILOG_SOURCES) $(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(VHDL_SOURCES) $(SIM_BUILD) @echo "onerror {" > $@ @@ -160,7 +159,7 @@ OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ LIB_LOAD = PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) NEW_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-zA-Z]\):\//\/\1\//g' -e 's/;/:/g') else -LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) +LIB_LOAD := NEW_PYTHONPATH := $(PYTHONPATH) endif @@ -169,7 +168,7 @@ endif $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(COCOTB_VPI_LIB) $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) - set -o pipefail; cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(PWD):$(NEW_PYTHONPATH) GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ + set -o pipefail; cd $(SIM_BUILD) && PYTHONPATH=$(PWD):$(NEW_PYTHONPATH) GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 $(CMD) $(RUN_ARGS) -do runsim.tcl | tee sim.log # check that the file was actually created @@ -177,4 +176,3 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB clean:: @rm -rf $(SIM_BUILD) - diff --git a/cocotb/share/makefiles/simulators/Makefile.cvc b/cocotb/share/makefiles/simulators/Makefile.cvc index 3ab83cf1..f8e99b69 100644 --- a/cocotb/share/makefiles/simulators/Makefile.cvc +++ b/cocotb/share/makefiles/simulators/Makefile.cvc @@ -59,16 +59,16 @@ endif # Compilation phase $(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(LIB_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + PYTHONPATH=$(PWD):$(PYTHONPATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ - $(CMD) $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=libcocotbvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) + $(CMD) $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=$(LIB_DIR)/libcocotbvpi_modelsim:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) # Execution phase ifeq ($(CVC_ITERP),1) $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp else $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) - PYTHONPATH=$(LIB_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + PYTHONPATH=$(PWD):$(PYTHONPATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) endif @@ -76,12 +76,12 @@ endif # Execution phase ifeq ($(CVC_ITERP),1) debug: $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(LIB_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + PYTHONPATH=$(PWD):$(PYTHONPATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ - gdb --args $(CMD) $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=libcocotbvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) + gdb --args $(CMD) $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=$(LIB_DIR)/modelsim/libcocotbvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) else debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) - PYTHONPATH=$(LIB_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + PYTHONPATH=$(PWD):$(PYTHONPATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ gdb --args $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) endif diff --git a/cocotb/share/makefiles/simulators/Makefile.ghdl b/cocotb/share/makefiles/simulators/Makefile.ghdl index e67feeff..5da73457 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ghdl +++ b/cocotb/share/makefiles/simulators/Makefile.ghdl @@ -68,9 +68,9 @@ $(COCOTB_RESULTS_FILE): analyse $(COCOTB_LIBS) $(COCOTB_VPI_LIB) -@rm -f $(COCOTB_RESULTS_FILE) cd $(SIM_BUILD); \ - PYTHONPATH=$(LIB_DIR):$(PWD):$(PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + PYTHONPATH=$(PWD):$(PYTHONPATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ - $(CMD) -r $(GHDL_ARGS) --work=$(RTL_LIBRARY) $(TOPLEVEL) --vpi=$(LIB_DIR)/libcocotbvpi.$(LIB_EXT) $(SIM_ARGS) $(PLUSARGS) + $(CMD) -r $(GHDL_ARGS) --work=$(RTL_LIBRARY) $(TOPLEVEL) --vpi=$(LIB_DIR)/libcocotbvpi_ghdl.$(LIB_EXT) $(SIM_ARGS) $(PLUSARGS) # check that the file was actually created, since we can't set an exit code from cocotb test -f $(COCOTB_RESULTS_FILE) diff --git a/cocotb/share/makefiles/simulators/Makefile.icarus b/cocotb/share/makefiles/simulators/Makefile.icarus index a177301b..dff2fcf5 100644 --- a/cocotb/share/makefiles/simulators/Makefile.icarus +++ b/cocotb/share/makefiles/simulators/Makefile.icarus @@ -52,8 +52,6 @@ else export ICARUS_BIN_DIR endif -BUILD_VPI=1 - COMPILE_ARGS += -g2012 # Default to latest SystemVerilog standard # Compilation phase @@ -69,16 +67,16 @@ LIB_LOAD = PATH=$(OLD_PATH):$(LIB_DIR) NEW_PYTHONPATH:=$(shell python -c "import sys, os; print(':'.join(['/'+dir.replace(os.sep,'/').replace(':','') for dir in sys.path]))") else -LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) +LIB_LOAD := NEW_PYTHONPATH := $(PYTHONPATH) endif $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) -@rm -f $(COCOTB_RESULTS_FILE) - PYTHONPATH=$(LIB_DIR):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ - $(ICARUS_BIN_DIR)/vvp -M $(LIB_DIR) -m libcocotbvpi $(SIM_ARGS) $(EXTRA_ARGS) $(SIM_BUILD)/sim.vvp $(PLUSARGS) + $(ICARUS_BIN_DIR)/vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_ARGS) $(EXTRA_ARGS) $(SIM_BUILD)/sim.vvp $(PLUSARGS) # check that the file was actually created, since we can't set an exit code from cocotb test -f $(COCOTB_RESULTS_FILE) @@ -86,9 +84,9 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $ debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) -@rm -f $(COCOTB_RESULTS_FILE) - PYTHONPATH=$(LIB_DIR):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ - gdb --args $(ICARUS_BIN_DIR)/vvp -M $(LIB_DIR) -m libcocotbvpi $(SIM_ARGS) $(EXTRA_ARGS) $(SIM_BUILD)/sim.vvp $(PLUSARGS) + gdb --args $(ICARUS_BIN_DIR)/vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) # check that the file was actually created, since we can't set an exit code from cocotb test -f $(COCOTB_RESULTS_FILE) diff --git a/cocotb/share/makefiles/simulators/Makefile.ius b/cocotb/share/makefiles/simulators/Makefile.ius index e62441d3..f1c1137a 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ius +++ b/cocotb/share/makefiles/simulators/Makefile.ius @@ -71,7 +71,7 @@ endif # GPI_EXTRA=cocotbvhpi if needed. # Xcelium will use default vlog_startup_routines symbol only if vpi library name is libvpi.so -GPI_ARGS = -loadvpi $(LIB_DIR)/libcocotbvpi:vlog_startup_routines_bootstrap +GPI_ARGS = -loadvpi $(LIB_DIR)/libcocotbvpi_ius:vlog_startup_routines_bootstrap ifeq ($(TOPLEVEL_LANG),verilog) EXTRA_ARGS += -v93 @@ -79,10 +79,10 @@ ifeq ($(TOPLEVEL_LANG),verilog) ROOT_LEVEL = $(TOPLEVEL) ifneq ($(VHDL_SOURCES),) HDL_SOURCES += $(VHDL_SOURCES) - GPI_EXTRA = cocotbvhpi + GPI_EXTRA = cocotbvhpi_ius:cocotbvhpi_entry_point endif else ifeq ($(TOPLEVEL_LANG),vhdl) - GPI_EXTRA = cocotbvhpi + GPI_EXTRA = cocotbvhpi_ius:cocotbvhpi_entry_point EXTRA_ARGS += -v93 EXTRA_ARGS += -top $(TOPLEVEL) RTL_LIBRARY ?= $(TOPLEVEL) @@ -101,8 +101,8 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUS -@rm -f $(COCOTB_RESULTS_FILE) set -o pipefail; \ - LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(PWD):$(PYTHONPATH) \ - LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ + PYTHONPATH=$(PWD):$(PYTHONPATH) \ + MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ \ $(CMD) -timescale $(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) \ diff --git a/cocotb/share/makefiles/simulators/Makefile.nvc b/cocotb/share/makefiles/simulators/Makefile.nvc index 660e65bf..212bc4a6 100644 --- a/cocotb/share/makefiles/simulators/Makefile.nvc +++ b/cocotb/share/makefiles/simulators/Makefile.nvc @@ -39,13 +39,12 @@ analyse: $(VHDL_SOURCES) $(SIM_BUILD) $(COCOTB_RESULTS_FILE): analyse $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) -@rm -f $(COCOTB_RESULTS_FILE) - cd $(SIM_BUILD) && LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) \ + cd $(SIM_BUILD) && \ $(CMD) --work=$(RTL_LIBRARY) -e $(TOPLEVEL) - cd $(SIM_BUILD) && PYTHONPATH=$(LIB_DIR):$(PWD):$(PYTHONPATH) \ - LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) \ + cd $(SIM_BUILD) && PYTHONPATH=$(PWD):$(PYTHONPATH) \ + MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ - LD_LIBRARY_PATH=$(LIB_DIR) \ - $(CMD) $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) --work=$(RTL_LIBRARY) -r --load $(COCOTB_VHPI_LIB) $(TRACE) $(TOPLEVEL) + $(CMD) $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) --work=$(RTL_LIBRARY) -r --load $(LIB_DIR)/libcocotbvhpi_ius.$(LIB_EXT) $(TRACE) $(TOPLEVEL) # check that the file was actually created, since we can't set an exit code from cocotb test -f $(COCOTB_RESULTS_FILE) diff --git a/cocotb/share/makefiles/simulators/Makefile.questa b/cocotb/share/makefiles/simulators/Makefile.questa index 49a60f6e..d1d0987e 100644 --- a/cocotb/share/makefiles/simulators/Makefile.questa +++ b/cocotb/share/makefiles/simulators/Makefile.questa @@ -69,15 +69,15 @@ endif GPI_EXTRA:= ifeq ($(TOPLEVEL_LANG),vhdl) - VSIM_ARGS += -foreign \"cocotb_init $(LIB_DIR)/libfli.$(LIB_EXT)\" + VSIM_ARGS += -foreign \"cocotb_init $(LIB_DIR)/libcocotbfli_modelsim.$(LIB_EXT)\" ifneq ($(VERILOG_SOURCES),) - GPI_EXTRA = vpi + GPI_EXTRA = cocotbvpi_modelsim:cocotbvpi_entry_point endif else ifeq ($(TOPLEVEL_LANG),verilog) - VSIM_ARGS += -pli $(LIB_DIR)/libcocotbvpi.$(LIB_EXT) + VSIM_ARGS += -pli $(LIB_DIR)/libcocotbvpi_modelsim.$(LIB_EXT) ifneq ($(VHDL_SOURCES),) - GPI_EXTRA = fli + GPI_EXTRA = cocotbfli_modelsim:cocotbfli_entry_point endif else @@ -129,7 +129,7 @@ OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ LIB_LOAD := PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) NEW_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-zA-Z]\):\//\/\1\//g' -e 's/;/:/g') else -LIB_LOAD = LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) +LIB_LOAD := NEW_PYTHONPATH := $(PYTHONPATH) endif @@ -145,7 +145,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) -@rm -f $(COCOTB_RESULTS_FILE) set -o pipefail; $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ - GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) PYTHONPATH=$(LIB_DIR):$(PWD):$(NEW_PYTHONPATH) \ + GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) PYTHONPATH=$(PWD):$(NEW_PYTHONPATH) \ $(CMD) $(RUN_ARGS) -do $(SIM_BUILD)/runsim.do $(PLUSARGS) 2>&1 | tee $(SIM_BUILD)/sim.log # check that the file was actually created, since we can't set an exit code from cocotb diff --git a/cocotb/share/makefiles/simulators/Makefile.vcs b/cocotb/share/makefiles/simulators/Makefile.vcs index 381e758c..dc0310e1 100644 --- a/cocotb/share/makefiles/simulators/Makefile.vcs +++ b/cocotb/share/makefiles/simulators/Makefile.vcs @@ -70,17 +70,16 @@ $(SIM_BUILD)/pli.tab : # Compilation phase $(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(COCOTB_LIBS) $(COCOTB_VPI_LIB) $(CUSTOM_COMPILE_DEPS) cd $(SIM_BUILD) && \ - LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) TOPLEVEL=$(TOPLEVEL) \ + TOPLEVEL=$(TOPLEVEL) \ $(CMD) -top $(TOPLEVEL) $(PLUSARGS) +acc+1 +vpi -P pli.tab +define+COCOTB_SIM=1 -sverilog \ -timescale=$(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) \ - $(EXTRA_ARGS) -debug -load $(LIB_DIR)/libcocotbvpi.so $(COMPILE_ARGS) $(VERILOG_SOURCES) + $(EXTRA_ARGS) -debug -load $(LIB_DIR)/libcocotbvpi_vcs.so $(COMPILE_ARGS) $(VERILOG_SOURCES) # Execution phase $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/simv $(PYTHON_FILES) $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) - -PYTHONPATH=$(LIB_DIR):$(PWD):$(PYTHONPATH) \ - LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) \ + -PYTHONPATH=$(PWD):$(PYTHONPATH) \ MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(SIM_BUILD)/simv +define+COCOTB_SIM=1 $(SIM_ARGS) $(EXTRA_ARGS) diff --git a/cocotb/share/makefiles/simulators/Makefile.verilator b/cocotb/share/makefiles/simulators/Makefile.verilator index ca55de14..40e38977 100644 --- a/cocotb/share/makefiles/simulators/Makefile.verilator +++ b/cocotb/share/makefiles/simulators/Makefile.verilator @@ -34,7 +34,7 @@ endif SIM_BUILD_FLAGS += -std=c++11 -COMPILE_ARGS += --vpi --public-flat-rw --prefix Vtop -o $(TOPLEVEL) -LDFLAGS "-L$(LIB_DIR) -lcocotbvpi -lgpi -lcocotb -lgpilog -lcocotbutils" +COMPILE_ARGS += --vpi --public-flat-rw --prefix Vtop -o $(TOPLEVEL) -LDFLAGS "-Wl,-rpath,$(LIB_DIR) -L$(LIB_DIR) -lcocotbvpi_verilator -lgpi -lcocotb -lgpilog -lcocotbutils" $(SIM_BUILD)/Vtop.mk: $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(COCOTB_SHARE_DIR)/lib/verilator/verilator.cpp $(CMD) -cc --exe -Mdir $(SIM_BUILD) -DCOCOTB_SIM=1 --top-module $(TOPLEVEL) $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) $(COCOTB_SHARE_DIR)/lib/verilator/verilator.cpp @@ -50,14 +50,14 @@ LIB_LOAD = PATH=$(OLD_PATH):$(LIB_DIR) NEW_PYTHONPATH:=$(shell python -c "import sys, os; print(':'.join(['/'+dir.replace(os.sep,'/').replace(':','') for dir in sys.path]))") else -LIB_LOAD := LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) +LIB_LOAD := NEW_PYTHONPATH := $(PYTHONPATH) endif $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) -@rm -f $(COCOTB_RESULTS_FILE) - PYTHONPATH=$(LIB_DIR):$(PWD):$(NEW_PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $< $(PLUSARGS) @@ -67,7 +67,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) $(COCOTB_LIB debug: $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) -@rm -f $(COCOTB_RESULTS_FILE) - PYTHONPATH=$(LIB_DIR):$(PWD):$(NEW_PYTHONPATH) LD_LIBRARY_PATH=$(LIB_DIR) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ gdb --args $< $(PLUSARGS) diff --git a/cocotb/share/makefiles/simulators/Makefile.xcelium b/cocotb/share/makefiles/simulators/Makefile.xcelium index 781c006d..6810e521 100644 --- a/cocotb/share/makefiles/simulators/Makefile.xcelium +++ b/cocotb/share/makefiles/simulators/Makefile.xcelium @@ -79,18 +79,18 @@ endif # GPI_EXTRA=cocotbvhpi if needed. # Xcelium will use default vlog_startup_routines symbol only if VPI library name is libvpi.so -GPI_ARGS = -loadvpi $(LIB_DIR)/libcocotbvpi:vlog_startup_routines_bootstrap +GPI_ARGS = -loadvpi $(LIB_DIR)/libcocotbvpi_ius:vlog_startup_routines_bootstrap ifeq ($(TOPLEVEL_LANG),verilog) HDL_SOURCES = $(VERILOG_SOURCES) ROOT_LEVEL = $(TOPLEVEL) EXTRA_ARGS += -top $(TOPLEVEL) ifneq ($(VHDL_SOURCES),) HDL_SOURCES += $(VHDL_SOURCES) - GPI_EXTRA = cocotbvhpi + GPI_EXTRA = cocotbvhpi_ius:cocotbvhpi_entry_point endif else ifeq ($(TOPLEVEL_LANG),vhdl) # GPI_EXTRA will internally be extended to lib.so - GPI_EXTRA = cocotbvhpi + GPI_EXTRA = cocotbvhpi_ius:cocotbvhpi_entry_point EXTRA_ARGS += -top $(TOPLEVEL) RTL_LIBRARY ?= $(TOPLEVEL) MAKE_LIB = -makelib $(RTL_LIBRARY) @@ -108,8 +108,8 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUS -@rm -f $(COCOTB_RESULTS_FILE) set -o pipefail; \ - LD_RUN_PATH=$(PYTHON_LIBDIR):$(LD_RUN_PATH) PYTHONPATH=$(LIB_DIR):$(PWD):$(PYTHONPATH) \ - LD_LIBRARY_PATH=$(LIB_DIR):$(LD_LIBRARY_PATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ + PYTHONPATH=$(PWD):$(PYTHONPATH) \ + MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ \ $(CMD) -timescale $(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) \ diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 315560bb..ecbcc43b 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -31,7 +31,7 @@ import abc if "COCOTB_SIM" in os.environ: - import simulator + from cocotb import simulator else: simulator = None diff --git a/cocotb/utils.py b/cocotb/utils.py index e7ae2277..b7b2c42e 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -38,7 +38,7 @@ import warnings if "COCOTB_SIM" in os.environ: - import simulator + from cocotb import simulator _LOG_SIM_PRECISION = simulator.get_precision() # request once and cache else: simulator = None diff --git a/documentation/source/newsfragments/1425.change.rst b/documentation/source/newsfragments/1425.change.rst new file mode 100644 index 00000000..375c4caa --- /dev/null +++ b/documentation/source/newsfragments/1425.change.rst @@ -0,0 +1,4 @@ +All libraries are compiled during installation to the ``cocotb/libs`` directory. +The interface libraries ``libcocotbvpi`` and ``libcocotbvhpi`` have been renamed to have a ``_simulator_name`` postfix. +The ``simulator`` module has moved to :mod:`cocotb.simulator`. +The ``LD_LIBRARY_PATH`` environment variable no longer needs to be set by the makefiles, as the libraries now discover each other via ``RPATH`` settings. diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index 209f37ab..997a24cd 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -61,6 +61,7 @@ ifeq ($(OS),Linux) MODULE := $(MODULE),test_endian_swapper_hal CUSTOM_COMPILE_DEPS = hal LD_LIBRARY_PATH := $(PWD)/../cosim:$(LD_LIBRARY_PATH) + export LD_LIBRARY_PATH endif include $(shell cocotb-config --makefiles)/Makefile.inc diff --git a/tests/test_cases/issue_253/Makefile b/tests/test_cases/issue_253/Makefile index 40af3122..54805b87 100644 --- a/tests/test_cases/issue_253/Makefile +++ b/tests/test_cases/issue_253/Makefile @@ -44,21 +44,21 @@ $(COCOTB_RESULTS_FILE): @echo "Skipping" notset_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ COCOTB_SIM=1 TESTCASE=issue_253_notset TOPLEVEL= \ - vvp -M $(LIB_DIR) -m libcocotbvpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) + vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) mkdir -p $@_result && mv results.xml $@_result/ empty_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ COCOTB_SIM=1 TESTCASE=issue_253_empty TOPLEVEL="" \ - vvp -M $(LIB_DIR) -m libcocotbvpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) + vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) mkdir -p $@_result && mv results.xml $@_result/ no_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(LIB_DIR):$(COCOTB_PY_DIR):$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ COCOTB_SIM=1 TESTCASE=issue_253_none \ - vvp -M $(LIB_DIR) -m libcocotbvpi $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) + vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) mkdir -p $@_result && mv results.xml $@_result/ endif diff --git a/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py b/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py index 26c77276..f474fa8b 100644 --- a/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py +++ b/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py @@ -5,7 +5,7 @@ from cocotb.result import TestFailure from cocotb.triggers import Timer -import simulator +from cocotb import simulator @cocotb.test() def test_in_vect_packed(dut): From 67bf79b997b630416b1f98742fbeab09d9e90fef Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Sat, 22 Feb 2020 20:07:06 +0100 Subject: [PATCH 2061/2656] Use cocotb-config instead of MAKEFILE_LIST and add warning about deprecated file structre Co-Authored-By: Eric Wieser --- Makefile | 2 -- cocotb/share/makefiles/Makefile.inc | 14 +++----------- .../source/newsfragments/1445.change.rst | 1 + documentation/source/quickstart.rst | 2 ++ examples/endian_swapper/cosim/Makefile | 4 ++-- makefiles/Makefile.inc | 16 ++++++++++------ makefiles/Makefile.sim | 19 ++++++++++--------- tests/Makefile | 2 -- 8 files changed, 28 insertions(+), 32 deletions(-) create mode 100644 documentation/source/newsfragments/1445.change.rst diff --git a/Makefile b/Makefile index d062e987..9158e862 100644 --- a/Makefile +++ b/Makefile @@ -30,8 +30,6 @@ .PHONY: all all: test -include $(shell cocotb-config --makefiles)/Makefile.inc - .PHONY: clean clean: -@rm -rf $(BUILD_DIR) diff --git a/cocotb/share/makefiles/Makefile.inc b/cocotb/share/makefiles/Makefile.inc index 78c63287..0e55a37f 100644 --- a/cocotb/share/makefiles/Makefile.inc +++ b/cocotb/share/makefiles/Makefile.inc @@ -29,20 +29,12 @@ # Common makefile included by everything -# Directory containing the cocotb Python module -ifeq ($(COCOTB_PY_DIR),) -export COCOTB_PY_DIR:=$(realpath $(dir $(lastword $(MAKEFILE_LIST)))/../../..) -endif +# Directory containing the cocotb Python module (realpath for Windows compatibility) +COCOTB_PY_DIR := $(realpath $(shell cocotb-config --prefix)) # Directory containing all support files required to build cocotb-based # simulations: Makefile fragments, and the simulator libraries. -ifeq ($(COCOTB_SHARE_DIR),) -export COCOTB_SHARE_DIR:=$(realpath $(dir $(lastword $(MAKEFILE_LIST)))/..) -endif - -ifeq ($(COCOTB_PY_DIR),$(COCOTB_SHARE_DIR)) -$(warning 'Deprecated cocotb file structure detected. Please use "include $$(shell cocotb-config --makefile)/Makefile.inc" in Makefiles') -endif +COCOTB_SHARE_DIR := $(COCOTB_PY_DIR)/cocotb/share ifeq ($(USER_DIR),) export USER_DIR:=$(realpath $(dir $(firstword $(MAKEFILE_LIST)))) diff --git a/documentation/source/newsfragments/1445.change.rst b/documentation/source/newsfragments/1445.change.rst new file mode 100644 index 00000000..cfa48003 --- /dev/null +++ b/documentation/source/newsfragments/1445.change.rst @@ -0,0 +1 @@ +Cocotb must now be :ref:`installed ` before it can be used. diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index 2186c583..25d2447c 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -18,6 +18,8 @@ Cocotb has the following requirements: .. versionchanged:: 1.4 Dropped Python 2 support +.. _installation_via_pip: + Installation via PIP -------------------- diff --git a/examples/endian_swapper/cosim/Makefile b/examples/endian_swapper/cosim/Makefile index cca000a0..430b189a 100644 --- a/examples/endian_swapper/cosim/Makefile +++ b/examples/endian_swapper/cosim/Makefile @@ -1,5 +1,5 @@ -include $(COCOTB_SHARE_DIR)/makefiles/Makefile.pylib -include $(COCOTB_SHARE_DIR)/makefiles/Makefile.inc +include $(shell cocotb-config --makefiles)/Makefile.inc +include $(shell cocotb-config --makefiles)/Makefile.pylib PYTHON_LIBDIR ?= /usr/lib64 SWIG ?= swig diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 2635d989..b231f424 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -27,10 +27,14 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################## -# Backwards-compatibility wrapper for people using -# "include $COCOTB/makefiles/Makefile.inc" in their Makefile. -COCOTB_INSTALL_METHOD = srctree -export COCOTB_INSTALL_METHOD +COCOTB_CONFIG_CMD := $(shell :; command -v cocotb-config) +ifeq ($(COCOTB_CONFIG_CMD),) + $(error 'Cocotb is not installed (cocotb-config not found). \ + Please refer to https://cocotb.readthedocs.io/en/latest/quickstart.html#installing-cocotb \ + for installation instructions.') +endif -_NEW_SHARE_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST))))/../cocotb/share -include $(_NEW_SHARE_DIR)/makefiles/Makefile.inc +$(error 'Deprecated cocotb file structure detected. \ + Use "include $$(shell cocotb-config --makefile)/Makefile.inc" instead. \ + Please refer to https://cocotb.readthedocs.io/en/latest/quickstart.html#creating-a-makefile \ + for instructions.') diff --git a/makefiles/Makefile.sim b/makefiles/Makefile.sim index bed12388..34734707 100644 --- a/makefiles/Makefile.sim +++ b/makefiles/Makefile.sim @@ -27,13 +27,14 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################## -# Backwards-compatibility wrapper for people using -# "include $COCOTB/makefiles/Makefile.sim" in their Makefile. +COCOTB_CONFIG_CMD := $(shell :; command -v cocotb-config) +ifeq ($(COCOTB_CONFIG_CMD),) + $(error 'Cocotb is not installed (cocotb-config not found). \ + Please refer to https://cocotb.readthedocs.io/en/latest/quickstart.html#installing-cocotb \ + for installation instructions.') +endif -# COCOTB_INSTALL_METHOD should already have been set in Makefile.inc, but -# better be safe than sorry to cover unusual ways of people using cocotb. -COCOTB_INSTALL_METHOD = srctree -export COCOTB_INSTALL_METHOD - -_NEW_SHARE_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST))))/../cocotb/share -include $(_NEW_SHARE_DIR)/makefiles/Makefile.sim +$(error 'Deprecated cocotb file structure detected. \ + Use "include $$(shell cocotb-config --makefile)/Makefile.sim" instead. \ + Please refer to https://cocotb.readthedocs.io/en/latest/quickstart.html#creating-a-makefile \ + for instructions.') diff --git a/tests/Makefile b/tests/Makefile index c4c4ecd0..935cbc0f 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -25,8 +25,6 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -include ../makefiles/Makefile.inc - # distutils warnings are caused by an old version of virtualenv in travis export PYTHONWARNINGS = error,ignore::DeprecationWarning:distutils From 6b776489e2be4af0d74530d725b831abc0b8faea Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Sat, 4 Apr 2020 11:26:11 +0200 Subject: [PATCH 2062/2656] Better error message when missing COCOTB_RESULTS_FILE and cleaner log Co-Authored-By: Eric Wieser --- cocotb/share/makefiles/Makefile.sim | 5 +++++ cocotb/share/makefiles/simulators/Makefile.aldec | 3 +-- cocotb/share/makefiles/simulators/Makefile.ghdl | 3 +-- cocotb/share/makefiles/simulators/Makefile.icarus | 6 ++---- cocotb/share/makefiles/simulators/Makefile.ius | 3 +-- cocotb/share/makefiles/simulators/Makefile.nvc | 3 +-- cocotb/share/makefiles/simulators/Makefile.questa | 3 +-- cocotb/share/makefiles/simulators/Makefile.vcs | 3 +-- cocotb/share/makefiles/simulators/Makefile.verilator | 6 ++---- cocotb/share/makefiles/simulators/Makefile.xcelium | 3 +-- 10 files changed, 16 insertions(+), 22 deletions(-) diff --git a/cocotb/share/makefiles/Makefile.sim b/cocotb/share/makefiles/Makefile.sim index c409a8f6..6e7bdb22 100644 --- a/cocotb/share/makefiles/Makefile.sim +++ b/cocotb/share/makefiles/Makefile.sim @@ -131,6 +131,11 @@ PWD := $(shell pwd) include $(COCOTB_SHARE_DIR)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) +# Check that the COCOTB_RESULTS_FILE was created, since we can't set an exit code from cocotb. +define check_for_results_file + @test -f $(COCOTB_RESULTS_FILE) || (echo "ERROR: $(COCOTB_RESULTS_FILE) was not written by the simulation!" >&2 && exit 1) +endef + # Append directory containing the cocotb Python package to PYTHONPATH # XXX: This is only needed if cocotb is not installed into the default # Python search path. diff --git a/cocotb/share/makefiles/simulators/Makefile.aldec b/cocotb/share/makefiles/simulators/Makefile.aldec index 47e1f952..c9a68237 100644 --- a/cocotb/share/makefiles/simulators/Makefile.aldec +++ b/cocotb/share/makefiles/simulators/Makefile.aldec @@ -171,8 +171,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB set -o pipefail; cd $(SIM_BUILD) && PYTHONPATH=$(PWD):$(NEW_PYTHONPATH) GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 $(CMD) $(RUN_ARGS) -do runsim.tcl | tee sim.log - # check that the file was actually created - test -f $(COCOTB_RESULTS_FILE) + $(call check_for_results_file) clean:: @rm -rf $(SIM_BUILD) diff --git a/cocotb/share/makefiles/simulators/Makefile.ghdl b/cocotb/share/makefiles/simulators/Makefile.ghdl index 5da73457..83fe40fe 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ghdl +++ b/cocotb/share/makefiles/simulators/Makefile.ghdl @@ -72,8 +72,7 @@ $(COCOTB_RESULTS_FILE): analyse $(COCOTB_LIBS) $(COCOTB_VPI_LIB) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(CMD) -r $(GHDL_ARGS) --work=$(RTL_LIBRARY) $(TOPLEVEL) --vpi=$(LIB_DIR)/libcocotbvpi_ghdl.$(LIB_EXT) $(SIM_ARGS) $(PLUSARGS) - # check that the file was actually created, since we can't set an exit code from cocotb - test -f $(COCOTB_RESULTS_FILE) + $(call check_for_results_file) clean:: -@rm -rf $(SIM_BUILD) diff --git a/cocotb/share/makefiles/simulators/Makefile.icarus b/cocotb/share/makefiles/simulators/Makefile.icarus index dff2fcf5..895be34c 100644 --- a/cocotb/share/makefiles/simulators/Makefile.icarus +++ b/cocotb/share/makefiles/simulators/Makefile.icarus @@ -78,8 +78,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(ICARUS_BIN_DIR)/vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_ARGS) $(EXTRA_ARGS) $(SIM_BUILD)/sim.vvp $(PLUSARGS) - # check that the file was actually created, since we can't set an exit code from cocotb - test -f $(COCOTB_RESULTS_FILE) + $(call check_for_results_file) debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) -@rm -f $(COCOTB_RESULTS_FILE) @@ -88,8 +87,7 @@ debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ gdb --args $(ICARUS_BIN_DIR)/vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) - # check that the file was actually created, since we can't set an exit code from cocotb - test -f $(COCOTB_RESULTS_FILE) + $(call check_for_results_file) clean:: -@rm -rf $(SIM_BUILD) diff --git a/cocotb/share/makefiles/simulators/Makefile.ius b/cocotb/share/makefiles/simulators/Makefile.ius index f1c1137a..cffa7652 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ius +++ b/cocotb/share/makefiles/simulators/Makefile.ius @@ -108,8 +108,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUS $(CMD) -timescale $(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) \ $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) 2>&1 | tee $(SIM_BUILD)/sim.log - # check that the file was actually created, since we can't set an exit code from cocotb - test -f $(COCOTB_RESULTS_FILE) + $(call check_for_results_file) clean:: @rm -rf $(SIM_BUILD) diff --git a/cocotb/share/makefiles/simulators/Makefile.nvc b/cocotb/share/makefiles/simulators/Makefile.nvc index 212bc4a6..27cd20bc 100644 --- a/cocotb/share/makefiles/simulators/Makefile.nvc +++ b/cocotb/share/makefiles/simulators/Makefile.nvc @@ -46,8 +46,7 @@ $(COCOTB_RESULTS_FILE): analyse $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(CMD) $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) --work=$(RTL_LIBRARY) -r --load $(LIB_DIR)/libcocotbvhpi_ius.$(LIB_EXT) $(TRACE) $(TOPLEVEL) - # check that the file was actually created, since we can't set an exit code from cocotb - test -f $(COCOTB_RESULTS_FILE) + $(call check_for_results_file) clean:: -@rm -rf $(SIM_BUILD) diff --git a/cocotb/share/makefiles/simulators/Makefile.questa b/cocotb/share/makefiles/simulators/Makefile.questa index d1d0987e..90ecb910 100644 --- a/cocotb/share/makefiles/simulators/Makefile.questa +++ b/cocotb/share/makefiles/simulators/Makefile.questa @@ -148,8 +148,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) PYTHONPATH=$(PWD):$(NEW_PYTHONPATH) \ $(CMD) $(RUN_ARGS) -do $(SIM_BUILD)/runsim.do $(PLUSARGS) 2>&1 | tee $(SIM_BUILD)/sim.log - # check that the file was actually created, since we can't set an exit code from cocotb - test -f $(COCOTB_RESULTS_FILE) + $(call check_for_results_file) # Potential fix for buffered stdout, YMMV # STDOUT=$(SIM_BUILD)/sim.log stdbuf --output=0 $(CMD) -do runsim.do 2>&1 && stdbuf --output=0 --input=0 tail -f sim.log && exit $${PIPESTATUS[0]} diff --git a/cocotb/share/makefiles/simulators/Makefile.vcs b/cocotb/share/makefiles/simulators/Makefile.vcs index dc0310e1..7e2f6adc 100644 --- a/cocotb/share/makefiles/simulators/Makefile.vcs +++ b/cocotb/share/makefiles/simulators/Makefile.vcs @@ -84,8 +84,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/simv $(PYTHON_FILES) $(CUSTOM_SIM_DEPS) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(SIM_BUILD)/simv +define+COCOTB_SIM=1 $(SIM_ARGS) $(EXTRA_ARGS) - # check that the file was actually created, since we can't set an exit code from cocotb - test -f $(COCOTB_RESULTS_FILE) + $(call check_for_results_file) .PHONY: clean clean:: diff --git a/cocotb/share/makefiles/simulators/Makefile.verilator b/cocotb/share/makefiles/simulators/Makefile.verilator index 40e38977..b3923fb2 100644 --- a/cocotb/share/makefiles/simulators/Makefile.verilator +++ b/cocotb/share/makefiles/simulators/Makefile.verilator @@ -61,8 +61,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) $(COCOTB_LIB TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $< $(PLUSARGS) - # check that the file was actually created, since we can't set an exit code from cocotb - test -f $(COCOTB_RESULTS_FILE) + $(call check_for_results_file) debug: $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) -@rm -f $(COCOTB_RESULTS_FILE) @@ -71,8 +70,7 @@ debug: $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_L TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ gdb --args $< $(PLUSARGS) - # check that the file was actually created, since we can't set an exit code from cocotb - test -f $(COCOTB_RESULTS_FILE) + $(call check_for_results_file) clean:: @rm -rf $(SIM_BUILD) diff --git a/cocotb/share/makefiles/simulators/Makefile.xcelium b/cocotb/share/makefiles/simulators/Makefile.xcelium index 6810e521..3ae8ae47 100644 --- a/cocotb/share/makefiles/simulators/Makefile.xcelium +++ b/cocotb/share/makefiles/simulators/Makefile.xcelium @@ -115,8 +115,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUS $(CMD) -timescale $(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) \ $(EXTRA_ARGS) $(GPI_ARGS) $(INCDIRS) -access +rwc -createdebugdb $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) 2>&1 | tee $(SIM_BUILD)/sim.log - # check that the file was actually created, since we can't set an exit code from cocotb - test -f $(COCOTB_RESULTS_FILE) + $(call check_for_results_file) clean:: @rm -rf $(SIM_BUILD) From 4283c9c9531238986fdff7a59afbbaee1e49b48b Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 31 Mar 2020 22:55:50 +0200 Subject: [PATCH 2063/2656] Add CUSTOM_*_DEPS as dependencies where missing Add support for CUSTOM_COMPILE_DEPS and CUSTOM_SIM_DEPS in Makefile.ghdl, Makefile.nvc. Add support for CUSTOM_COMPILE_DEPS in Makefile.questa and Makefile.aldec. Closes #1549. --- cocotb/share/makefiles/simulators/Makefile.aldec | 2 +- cocotb/share/makefiles/simulators/Makefile.ghdl | 4 ++-- cocotb/share/makefiles/simulators/Makefile.nvc | 4 ++-- cocotb/share/makefiles/simulators/Makefile.questa | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.aldec b/cocotb/share/makefiles/simulators/Makefile.aldec index c9a68237..0fc382d9 100644 --- a/cocotb/share/makefiles/simulators/Makefile.aldec +++ b/cocotb/share/makefiles/simulators/Makefile.aldec @@ -165,7 +165,7 @@ endif # Note it's the redirection of the output rather than the 'do' command # that turns on batch mode (i.e. exit on completion/error) -$(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(COCOTB_VPI_LIB) $(CUSTOM_SIM_DEPS) +$(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(COCOTB_VPI_LIB) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) set -o pipefail; cd $(SIM_BUILD) && PYTHONPATH=$(PWD):$(NEW_PYTHONPATH) GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ diff --git a/cocotb/share/makefiles/simulators/Makefile.ghdl b/cocotb/share/makefiles/simulators/Makefile.ghdl index 83fe40fe..47ae346e 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ghdl +++ b/cocotb/share/makefiles/simulators/Makefile.ghdl @@ -57,14 +57,14 @@ GHDL_ARGS += $(EXTRA_ARGS) .PHONY: analyse # Compilation phase -analyse: $(VHDL_SOURCES) $(SIM_BUILD) +analyse: $(VHDL_SOURCES) $(SIM_BUILD) $(CUSTOM_COMPILE_DEPS) cd $(SIM_BUILD) && \ $(foreach SOURCES_VAR, $(filter VHDL_SOURCES_%, $(.VARIABLES)), \ $(CMD) -i $(GHDL_ARGS) $(COMPILE_ARGS) --work=$(SOURCES_VAR:VHDL_SOURCES_%=%) $($(SOURCES_VAR)) && ) \ $(CMD) -i $(GHDL_ARGS) $(COMPILE_ARGS) --work=$(RTL_LIBRARY) $(VHDL_SOURCES) && \ $(CMD) -m $(GHDL_ARGS) $(COMPILE_ARGS) --work=$(RTL_LIBRARY) $(TOPLEVEL) -$(COCOTB_RESULTS_FILE): analyse $(COCOTB_LIBS) $(COCOTB_VPI_LIB) +$(COCOTB_RESULTS_FILE): analyse $(COCOTB_LIBS) $(COCOTB_VPI_LIB) $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) cd $(SIM_BUILD); \ diff --git a/cocotb/share/makefiles/simulators/Makefile.nvc b/cocotb/share/makefiles/simulators/Makefile.nvc index 27cd20bc..1fece4af 100644 --- a/cocotb/share/makefiles/simulators/Makefile.nvc +++ b/cocotb/share/makefiles/simulators/Makefile.nvc @@ -33,10 +33,10 @@ RTL_LIBRARY ?= work .PHONY: analyse # Compilation phase -analyse: $(VHDL_SOURCES) $(SIM_BUILD) +analyse: $(VHDL_SOURCES) $(SIM_BUILD) $(CUSTOM_COMPILE_DEPS) cd $(SIM_BUILD) && $(CMD) --work=$(RTL_LIBRARY) -a $(VHDL_SOURCES) $(COMPILE_ARGS) $(EXTRA_ARGS) -$(COCOTB_RESULTS_FILE): analyse $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) +$(COCOTB_RESULTS_FILE): analyse $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) cd $(SIM_BUILD) && \ diff --git a/cocotb/share/makefiles/simulators/Makefile.questa b/cocotb/share/makefiles/simulators/Makefile.questa index 90ecb910..5e404532 100644 --- a/cocotb/share/makefiles/simulators/Makefile.questa +++ b/cocotb/share/makefiles/simulators/Makefile.questa @@ -84,7 +84,7 @@ else $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") endif -$(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_SIM_DEPS) $(SIM_BUILD) +$(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(SIM_BUILD) @echo "# Autogenerated file" > $@ @echo "onerror {" >> $@ @echo " quit -f -code 1" >> $@ From 41dc58d236674a271032b375946f122429910c29 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 26 Jan 2020 23:11:37 +0000 Subject: [PATCH 2064/2656] Use syntax for keyword-only arguments This avoids the need for popping kwargs from dictionaries. It also leads to better documentation both in sphinx and from the `help` builtin. This removes the last caller of `reject_remaining_kwargs`, although we leave the function around for now in case downstream users are using it. --- cocotb/drivers/__init__.py | 3 +-- cocotb/drivers/avalon.py | 6 ++---- cocotb/monitors/avalon.py | 7 ++----- cocotb/wavedrom.py | 8 ++------ 4 files changed, 7 insertions(+), 17 deletions(-) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index 131f4f87..173d8607 100755 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -299,8 +299,7 @@ class ValidatedBusDriver(BusDriver): ``(valid, invalid)`` cycles to insert. """ - def __init__(self, entity, name, clock, **kwargs): - valid_generator = kwargs.pop("valid_generator", None) + def __init__(self, entity, name, clock, *, valid_generator=None, **kwargs): BusDriver.__init__(self, entity, name, clock, **kwargs) self.set_valid_generator(valid_generator=valid_generator) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 2abcb6b7..e548cd82 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -512,8 +512,7 @@ class AvalonST(ValidatedBusDriver): _default_config = {"firstSymbolInHighOrderBits" : True} - def __init__(self, entity, name, clock, **kwargs): - config = kwargs.pop('config', {}) + def __init__(self, entity, name, clock, *, config={}, **kwargs): ValidatedBusDriver.__init__(self, entity, name, clock, **kwargs) self.config = AvalonST._default_config.copy() @@ -605,8 +604,7 @@ class AvalonSTPkts(ValidatedBusDriver): "readyLatency" : 0 } - def __init__(self, entity, name, clock, **kwargs): - config = kwargs.pop('config', {}) + def __init__(self, entity, name, clock, *, config={}, **kwargs): ValidatedBusDriver.__init__(self, entity, name, clock, **kwargs) self.config = AvalonSTPkts._default_config.copy() diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index 63725c63..7d15cdbc 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -55,8 +55,7 @@ class AvalonST(BusMonitor): _default_config = {"firstSymbolInHighOrderBits": True} - def __init__(self, entity, name, clock, **kwargs): - config = kwargs.pop('config', {}) + def __init__(self, entity, name, clock, *, config={}, **kwargs): BusMonitor.__init__(self, entity, name, clock, **kwargs) self.config = self._default_config.copy() @@ -109,9 +108,7 @@ class AvalonSTPkts(BusMonitor): "invalidTimeout" : 0, } - def __init__(self, entity, name, clock, **kwargs): - config = kwargs.pop('config', {}) - report_channel = kwargs.pop('report_channel', False) + def __init__(self, entity, name, clock, *, config={}, report_channel=False, **kwargs): BusMonitor.__init__(self, entity, name , clock, **kwargs) self.config = self._default_config.copy() diff --git a/cocotb/wavedrom.py b/cocotb/wavedrom.py index af76549a..23882acd 100644 --- a/cocotb/wavedrom.py +++ b/cocotb/wavedrom.py @@ -29,7 +29,6 @@ import cocotb from cocotb.bus import Bus from cocotb.triggers import RisingEdge, ReadOnly -from cocotb.utils import reject_remaining_kwargs class Wavedrom(object): @@ -129,11 +128,8 @@ class trace(object): j = waves.dumpj() """ - def __init__(self, *args, **kwargs): - # emulate keyword-only arguments in python 2 - self._clock = kwargs.pop("clk", None) - reject_remaining_kwargs('__init__', kwargs) - + def __init__(self, *args, clk=None): + self._clock = clk self._signals = [] for arg in args: self._signals.append(Wavedrom(arg)) From ff44cad9f72bbef8882edcf9bf67c93de1c191af Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Tue, 18 Feb 2020 22:03:15 +0100 Subject: [PATCH 2065/2656] Deprecate `cocotb.utils.reject_remaining_kwargs` No callers remain in cocotb, and the only cases when this function is needed are: * When supporting python 2 * When writing extremely complexly-overloaded function signatures like in matplotlib, which we should strive to continue avoiding. --- cocotb/utils.py | 8 ++++++++ documentation/source/newsfragments/1339.removal.rst | 2 ++ 2 files changed, 10 insertions(+) create mode 100644 documentation/source/newsfragments/1339.removal.rst diff --git a/cocotb/utils.py b/cocotb/utils.py index b7b2c42e..7de3df55 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -478,7 +478,15 @@ def func(x1, **kwargs): def func(x1, *, a=1, b=2): ... + + .. deprecated:: 1.4 + Since the minimum supported Python version is now 3.5, this function + is not needed. """ + warnings.warn( + "reject_remaining_kwargs is deprecated and will be removed, use " + "Python 3 keyword-only arguments directly.", DeprecationWarning, + stacklevel=2) if kwargs: # match the error message to what Python 3 produces bad_arg = next(iter(kwargs)) diff --git a/documentation/source/newsfragments/1339.removal.rst b/documentation/source/newsfragments/1339.removal.rst new file mode 100644 index 00000000..adea304d --- /dev/null +++ b/documentation/source/newsfragments/1339.removal.rst @@ -0,0 +1,2 @@ +:func:`cocotb.utils.reject_remaining_kwargs` is deprecated, as it is no longer +needed now that we only support Python 3.5 and newer. From 16201694f83ce0fc8ef135fe95afadf334226c62 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 22 Mar 2020 18:25:35 +0000 Subject: [PATCH 2066/2656] Add a test to prove that coroutines keep running when passed to First --- tests/test_cases/test_cocotb/test_cocotb.py | 24 +++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index f341f262..b20ccc16 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -1093,6 +1093,30 @@ def wait_for_nested_first(): yield fire_task.join() +@cocotb.test() +async def test_first_does_not_kill(dut): + """ Test that `First` does not kill coroutines that did not finish first """ + ran = False + + # decorating `async def` is required to use `First` + @cocotb.coroutine + async def coro(): + nonlocal ran + await Timer(2, units='ns') + ran = True + + # Coroutine runs for 2ns, so we expect the timer to fire first + timer = Timer(1, units='ns') + t = await First(timer, coro()) + assert t is timer + assert not ran + + # the background routine is still running, but should finish after 1ns + await Timer(2, units='ns') + + assert ran + + @cocotb.test() def test_readwrite(dut): """ Test that ReadWrite can be waited on """ From 37dff824bc1d7de3f9dc362eee01c3cd9b0f4864 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 20 Mar 2020 22:36:16 -0500 Subject: [PATCH 2067/2656] Updated core libraries to use async/await Instead of cocotb.coroutine/yield. Also updated docstrings associated with those functions. --- cocotb/clock.py | 32 +++--- cocotb/decorators.py | 9 +- cocotb/regression.py | 4 +- cocotb/scheduler.py | 30 +++--- cocotb/triggers.py | 110 ++++++++------------ cocotb/wavedrom.py | 7 +- tests/test_cases/test_cocotb/test_cocotb.py | 6 +- 7 files changed, 86 insertions(+), 112 deletions(-) diff --git a/cocotb/clock.py b/cocotb/clock.py index 347bebe8..d7f92f69 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -29,7 +29,6 @@ import itertools -import cocotb from cocotb.log import SimLog from cocotb.triggers import Timer from cocotb.utils import get_sim_steps, get_time_from_sim_steps, lazy_property @@ -76,17 +75,16 @@ class Clock(BaseClock): .. code-block:: python - @cocotb.coroutine - def custom_clock(): + async def custom_clock(): # pre-construct triggers for performance high_time = Timer(high_delay, units="ns") low_time = Timer(low_delay, units="ns") - yield Timer(initial_delay, units="ns") + await Timer(initial_delay, units="ns") while True: dut.clk <= 1 - yield high_time + await high_time dut.clk <= 0 - yield low_time + await low_time If you also want to change the timing during simulation, use this slightly more inefficient example instead where @@ -95,19 +93,18 @@ def custom_clock(): .. code-block:: python - @cocotb.coroutine - def custom_clock(): + async def custom_clock(): while True: dut.clk <= 1 - yield Timer(high_delay, units="ns") + await Timer(high_delay, units="ns") dut.clk <= 0 - yield Timer(low_delay, units="ns") + await Timer(low_delay, units="ns") high_delay = low_delay = 100 cocotb.fork(custom_clock()) - yield Timer(1000, units="ns") + await Timer(1000, units="ns") high_delay = low_delay = 10 # change the clock speed - yield Timer(1000, units="ns") + await Timer(1000, units="ns") """ def __init__(self, signal, period, units=None): @@ -120,8 +117,7 @@ def __init__(self, signal, period, units=None): self.coro = None self.mcoro = None - @cocotb.coroutine - def start(self, cycles=None, start_high=True): + async def start(self, cycles=None, start_high=True): r"""Clocking coroutine. Start driving your clock by :func:`fork`\ ing a call to this. @@ -145,15 +141,15 @@ def start(self, cycles=None, start_high=True): if start_high: for _ in it: self.signal <= 1 - yield t + await t self.signal <= 0 - yield t + await t else: for _ in it: self.signal <= 0 - yield t + await t self.signal <= 1 - yield t + await t def __str__(self): return self.__class__.__name__ + "(%3.1f MHz)" % self.frequency diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 1ac16f18..e671b3c0 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -434,9 +434,9 @@ class test(coroutine, metaclass=_decorator_helper): Users are encouraged to use the following idiom instead:: @cocotb.test() - def my_test(dut): + async def my_test(dut): try: - yield thing_that_should_fail() + await thing_that_should_fail() except ExceptionIExpect: pass else: @@ -463,10 +463,11 @@ def __init__(self, f, timeout_time=None, timeout_unit=None, co = coroutine(f) @functools.wraps(f) - def f(*args, **kwargs): + async def f(*args, **kwargs): running_co = co(*args, **kwargs) + try: - res = yield cocotb.triggers.with_timeout(running_co, self.timeout_time, self.timeout_unit) + res = await cocotb.triggers.with_timeout(running_co, self.timeout_time, self.timeout_unit) except cocotb.result.SimTimeoutError: running_co.kill() raise diff --git a/cocotb/regression.py b/cocotb/regression.py index 6707e357..e1f52bed 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -525,8 +525,8 @@ def _create_test(function, name, documentation, mod, *args, **kwargs): Returns: Decorated test function """ - def _my_test(dut): - yield function(dut, *args, **kwargs) + async def _my_test(dut): + await function(dut, *args, **kwargs) _my_test.__name__ = name _my_test.__doc__ = documentation diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 2068165a..a4e01650 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -246,15 +246,14 @@ def __init__(self): self._write_coro_inst = None self._writes_pending = Event() - @cocotb.decorators.coroutine - def _do_writes(self): + async def _do_writes(self): """ An internal coroutine that performs pending writes """ while True: - yield self._writes_pending.wait() + await self._writes_pending.wait() if self._mode != Scheduler._MODE_NORMAL: - yield self._next_time_step + await self._next_time_step - yield self._read_write + await self._read_write while self._writes: handle, value = self._writes.popitem() @@ -511,8 +510,7 @@ def save_write(self, handle, value): # TODO: we should be able to better keep track of when this needs to # be scheduled if self._write_coro_inst is None: - self._write_coro_inst = self._do_writes() - self.schedule(self._write_coro_inst) + self._write_coro_inst = self.add(self._do_writes()) self._writes[handle] = value self._writes_pending.set() @@ -573,11 +571,10 @@ def queue_function(self, coro): # each entry always has a unique thread. t, = matching_threads - @cocotb.coroutine - def wrapper(): + async def wrapper(): # This function runs in the scheduler thread try: - _outcome = outcomes.Value((yield coro)) + _outcome = outcomes.Value(await coro) except BaseException as e: _outcome = outcomes.Error(e) event.outcome = _outcome @@ -591,7 +588,7 @@ def wrapper(): event.set() event = threading.Event() - self._pending_coros.append(wrapper()) + self._pending_coros.append(cocotb.decorators.RunningTask(wrapper())) # The scheduler thread blocks in `thread_wait`, and is woken when we # call `thread_suspend` - so we need to make sure the coroutine is # queued before that. @@ -602,11 +599,11 @@ def wrapper(): def run_in_executor(self, func, *args, **kwargs): """Run the coroutine in a separate execution thread - and return a yieldable object for the caller. + and return an awaitable object for the caller. """ # Create a thread # Create a trigger that is called as a result of the thread finishing - # Create an Event object that the caller can yield on + # Create an Event object that the caller can await on # Event object set when the thread finishes execution, this blocks the # calling coroutine (but not the thread) until the external completes @@ -616,8 +613,7 @@ def execute_external(func, _waiter): self.log.debug("Execution of external routine done %s" % threading.current_thread()) _waiter.thread_done() - @cocotb.coroutine - def wrapper(): + async def wrapper(): waiter = external_waiter() thread = threading.Thread(group=None, target=execute_external, name=func.__name__ + "_thread", @@ -626,7 +622,7 @@ def wrapper(): waiter.thread = thread self._pending_threads.append(waiter) - yield waiter.event.wait() + await waiter.event.wait() return waiter.result # raises if there was an exception @@ -697,7 +693,7 @@ def _trigger_from_unstarted_coro(self, result: cocotb.decorators.RunningTask) -> return result.join() def _trigger_from_waitable(self, result: cocotb.triggers.Waitable) -> Trigger: - return self._trigger_from_unstarted_coro(result._wait()) + return self._trigger_from_unstarted_coro(cocotb.decorators.RunningTask(result._wait())) def _trigger_from_list(self, result: list) -> Trigger: return self._trigger_from_waitable(cocotb.triggers.First(*result)) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index ecbcc43b..f53e4718 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -25,10 +25,11 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -"""A collections of triggers which a testbench can yield.""" +"""A collections of triggers which a testbench can await.""" import os import abc +from collections.abc import Awaitable if "COCOTB_SIM" in os.environ: from cocotb import simulator @@ -40,7 +41,6 @@ get_sim_steps, get_time_from_sim_steps, ParametrizedSingleton, lazy_property, remove_traceback_frames, ) -from cocotb import decorators from cocotb import outcomes import cocotb @@ -59,7 +59,7 @@ def _pointer_str(obj): class TriggerException(Exception): pass -class Trigger(metaclass=abc.ABCMeta): +class Trigger(Awaitable): """Base class to derive from.""" # __dict__ is needed here for the `.log` lazy_property below to work. @@ -115,7 +115,7 @@ def __del__(self): @property def _outcome(self): - """The result that `yield this_trigger` produces in a coroutine. + """The result that `await this_trigger` produces in a coroutine. The default is to produce the trigger itself, which is done for ease of use with :class:`~cocotb.triggers.First`. @@ -173,24 +173,24 @@ def __init__(self, time_ps, units=None): Examples: - >>> yield Timer(100, units='ps') + >>> await Timer(100, units='ps') The time can also be a ``float``: - >>> yield Timer(100e-9, units='sec') + >>> await Timer(100e-9, units='sec') which is particularly convenient when working with frequencies: >>> freq = 10e6 # 10 MHz - >>> yield Timer(1 / freq, units='sec') + >>> await Timer(1 / freq, units='sec') Other builtin exact numeric types can be used too: >>> from fractions import Fraction - >>> yield Timer(Fraction(1, 10), units='ns') + >>> await Timer(Fraction(1, 10), units='ns') >>> from decimal import Decimal - >>> yield Timer(Decimal('100e-9'), units='sec') + >>> await Timer(Decimal('100e-9'), units='sec') These are most useful when using computed durations while avoiding floating point inaccuracies. @@ -376,7 +376,7 @@ def __repr__(self): class Event(object): """Event to permit synchronization between two coroutines. - Yielding :meth:`wait()` from one coroutine will block the coroutine until + Awaiting :meth:`wait()` from one coroutine will block the coroutine until :meth:`set()` is called somewhere else. """ @@ -567,20 +567,6 @@ class Join(PythonTrigger, metaclass=_ParameterizedSingletonAndABC): The result of blocking on the trigger can be used to get the coroutine result:: - @cocotb.coroutine() - def coro_inner(): - yield Timer(1, units='ns') - return "Hello world" - - task = cocotb.fork(coro_inner()) - result = yield Join(task) - assert result == "Hello world" - - Or using the syntax in Python 3.5 onwards: - - .. code-block:: python3 - - @cocotb.coroutine() async def coro_inner(): await Timer(1, units='ns') return "Hello world" @@ -589,8 +575,7 @@ async def coro_inner(): result = await Join(task) assert result == "Hello world" - If the coroutine threw an exception, the :keyword:`await` or :keyword:`yield` - will re-raise it. + If the coroutine threw an exception, the :keyword:`await` will re-raise it. """ __slots__ = ('_coroutine',) @@ -617,13 +602,13 @@ def retval(self): forked = cocotb.fork(mycoro()) j = Join(forked) - yield j + await j result = j.retval :: forked = cocotb.fork(mycoro()) - result = yield Join(forked) + result = await Join(forked) """ return self._coroutine.retval @@ -637,24 +622,20 @@ def __repr__(self): return "{}({!r})".format(type(self).__name__, self._coroutine) -class Waitable(object): +class Waitable(Awaitable): """ - Compatibility layer that emulates `collections.abc.Awaitable`. + Base class for trigger-like objects implemented using coroutines. - This converts a `_wait` abstract method into a suitable `__await__` on - supporting Python versions (>=3.3). + This converts a `_wait` abstract method into a suitable `__await__`. """ __slots__ = () - @decorators.coroutine - def _wait(self): + + async def _wait(self): """ - Should be implemented by the sub-class. Called by `yield self` to + Should be implemented by the sub-class. Called by `await self` to convert the waitable object into a coroutine. - - ReturnValue can be used here. """ raise NotImplementedError - yield def __await__(self): return self._wait().__await__() @@ -670,8 +651,8 @@ def __init__(self, *triggers): self.triggers = tuple(triggers) # Do some basic type-checking up front, rather than waiting until we - # yield them. - allowed_types = (Trigger, Waitable, decorators.RunningTask) + # await them. + allowed_types = (Trigger, Waitable, cocotb.decorators.RunningTask) for trigger in self.triggers: if not isinstance(trigger, allowed_types): raise TypeError( @@ -687,13 +668,13 @@ def __repr__(self): ) -@decorators.coroutine -def _wait_callback(trigger, callback): +async def _wait_callback(trigger, callback): """ - Wait for a trigger, and call `callback` with the outcome of the yield. + Wait for a trigger, and call `callback` with the outcome of the await. """ + trigger = cocotb.scheduler._trigger_from_any(trigger) try: - ret = outcomes.Value((yield trigger)) + ret = outcomes.Value(await trigger) except BaseException as exc: # hide this from the traceback ret = outcomes.Error(remove_traceback_frames(exc, ['_wait_callback'])) @@ -708,8 +689,7 @@ class Combine(_AggregateWaitable): """ __slots__ = () - @decorators.coroutine - def _wait(self): + async def _wait(self): waiters = [] e = Event() triggers = list(self.triggers) @@ -725,7 +705,7 @@ def on_done(ret, t=t): waiters.append(cocotb.fork(_wait_callback(t, on_done))) # wait for the last waiter to complete - yield e.wait() + await e.wait() return self @@ -735,10 +715,6 @@ class First(_AggregateWaitable): Returns the result of the trigger that fired. - As a shorthand, ``t = yield [a, b]`` can be used instead of - ``t = yield First(a, b)``. Note that this shorthand is not available when - using :keyword:`await`. - .. note:: The event loop is single threaded, so while events may be simultaneous in simulation time, they can never be simultaneous in real time. @@ -747,12 +723,16 @@ class First(_AggregateWaitable): t1 = Timer(10, units='ps') t2 = Timer(10, units='ps') - t_ret = yield First(t1, t2) + t_ret = await First(t1, t2) + + .. note:: + When using the old-style :keyword:`yield`-based coroutines, ``t = yield [a, b]`` was another spelling of + ``t = yield First(a, b)``. This spelling is no longer available when using :keyword:`await`-based + coroutines. """ __slots__ = () - @decorators.coroutine - def _wait(self): + async def _wait(self): waiters = [] e = Event() triggers = list(self.triggers) @@ -765,7 +745,7 @@ def on_done(ret): waiters.append(cocotb.fork(_wait_callback(t, on_done))) # wait for a waiter to complete - yield e.wait() + await e.wait() # kill all the other waiters # TODO: Should this kill the coroutines behind any Join triggers? @@ -779,7 +759,7 @@ def on_done(ret): # - Using `NullTrigger` here instead of `result = completed[0].get()` # means we avoid inserting an `outcome.get` frame in the traceback first_trigger = NullTrigger(outcome=completed[0]) - return (yield first_trigger) # the first of multiple triggers that fired + return await first_trigger # the first of multiple triggers that fired class ClockCycles(Waitable): @@ -799,11 +779,10 @@ def __init__(self, signal, num_cycles, rising=True): else: self._type = FallingEdge - @decorators.coroutine - def _wait(self): + async def _wait(self): trigger = self._type(self.signal) for _ in range(self.num_cycles): - yield trigger + await trigger return self def __repr__(self): @@ -816,8 +795,7 @@ def __repr__(self): return fmt.format(type(self).__name__, self.signal, self.num_cycles) -@decorators.coroutine -def with_timeout(trigger, timeout_time, timeout_unit=None): +async def with_timeout(trigger, timeout_time, timeout_unit=None): """ Waits on triggers, throws an exception if it waits longer than the given time. @@ -825,13 +803,12 @@ def with_timeout(trigger, timeout_time, timeout_unit=None): .. code-block:: python - yield with_timeout(coro, 100, 'ns') - yield with_timeout(First(coro, event.wait()), 100, 'ns') + await with_timeout(coro, 100, 'ns') + await with_timeout(First(coro, event.wait()), 100, 'ns') Args: trigger (cocotb_waitable): - A single object that could be right of a :keyword:`yield` - (or :keyword:`await` in Python 3) expression in cocotb. + A single object that could be right of an :keyword:`await` expression in cocotb. timeout_time (numbers.Real or decimal.Decimal): Time duration. timeout_unit (str or None, optional): @@ -845,8 +822,9 @@ def with_timeout(trigger, timeout_time, timeout_unit=None): .. versionadded:: 1.3 """ + timeout_timer = cocotb.triggers.Timer(timeout_time, timeout_unit) - res = yield [timeout_timer, trigger] + res = await First(timeout_timer, trigger) if res is timeout_timer: raise cocotb.result.SimTimeoutError else: diff --git a/cocotb/wavedrom.py b/cocotb/wavedrom.py index 23882acd..1076a86b 100644 --- a/cocotb/wavedrom.py +++ b/cocotb/wavedrom.py @@ -140,12 +140,11 @@ def __init__(self, *args, clk=None): if self._clock is None: raise ValueError("Trace requires a clock to sample") - @cocotb.coroutine - def _monitor(self): + async def _monitor(self): self._clocks = 0 while True: - yield RisingEdge(self._clock) - yield ReadOnly() + await RisingEdge(self._clock) + await ReadOnly() if not self._enabled: continue self._clocks += 1 diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index b20ccc16..17df22a0 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -940,7 +940,11 @@ def raise_soon(): File ".*test_cocotb\.py", line \d+, in raise_soon yield cocotb\.triggers\.First\(raise_inner\(\)\) File ".*triggers\.py", line \d+, in _wait - return \(yield first_trigger\)[^\n]* + return await first_trigger[^\n]* + File ".*triggers.py", line \d+, in __await__ + return \(yield self\) + File ".*triggers.py", line \d+, in __await__ + return \(yield self\) File ".*test_cocotb\.py", line \d+, in raise_inner raise ValueError\('It is soon now'\) ValueError: It is soon now""").strip() From a4304bbd803d21e63bcfd8a6b63500c13e439691 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 5 Apr 2020 13:03:57 +0100 Subject: [PATCH 2068/2656] Only link libcocotb against libpython (#1571) libcocotb is the library that actually _embeds_ python, so therefore must be linked against it. All the other modules are loaded within python or libcocotb, so do not need to link against this library. --- cocotb/_build_libs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/_build_libs.py b/cocotb/_build_libs.py index 7b1e4cb4..d53f6f79 100755 --- a/cocotb/_build_libs.py +++ b/cocotb/_build_libs.py @@ -212,7 +212,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): libgpilog = Extension( os.path.join("cocotb", "libs", "libgpilog"), include_dirs=[include_dir], - libraries=[_get_python_lib_link(), "cocotbutils"], + libraries=["cocotbutils"], library_dirs=python_lib_dirs, sources=[os.path.join(share_lib_dir, "gpi_log", "gpi_logging.cpp")], extra_link_args=_extra_link_args("libgpilog"), @@ -255,7 +255,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): libsim = Extension( os.path.join("cocotb", "simulator"), include_dirs=[include_dir], - libraries=[_get_python_lib_link(), "cocotbutils", "gpilog", "gpi"], + libraries=["cocotbutils", "gpilog", "gpi"], library_dirs=python_lib_dirs, sources=[os.path.join(share_lib_dir, "simulator", "simulatormodule.cpp")], extra_compile_args=_extra_cxx_compile_args, From 7de2087f0e4aeb54a470ba9153b9196a221e75b2 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 5 Apr 2020 13:53:46 +0100 Subject: [PATCH 2069/2656] Add a readthedocs configuration file (#1572) As recommended by the readthedocs build logs. This means if we rename these files in a PR, we don't have to globally change settings across all branches. It also makes it easier to test solutions to gh-1569. --- .readthedocs.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .readthedocs.yml diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 00000000..15810e6d --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,13 @@ +# .readthedocs.yml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +version: 2 + +python: + version: 3.7 + install: + - requirements: documentation/requirements.txt + +sphinx: + configuration: documentation/source/conf.py From 23eee8df953a4b863ff18fbb0da5079af55d1d55 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Sat, 4 Apr 2020 20:06:37 +0200 Subject: [PATCH 2070/2656] Improve error message when cocotb-config not found Co-Authored-By: Colin Marquardt Co-Authored-By: Eric Wieser --- makefiles/Makefile.inc | 33 ++++----------------------------- makefiles/Makefile.sim | 33 ++++----------------------------- 2 files changed, 8 insertions(+), 58 deletions(-) diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index b231f424..3f9faea9 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -1,35 +1,10 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################## +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause COCOTB_CONFIG_CMD := $(shell :; command -v cocotb-config) ifeq ($(COCOTB_CONFIG_CMD),) - $(error 'Cocotb is not installed (cocotb-config not found). \ + $(error 'cocotb-config not found! Cocotb is not installed (or is installed but not on the PATH). \ Please refer to https://cocotb.readthedocs.io/en/latest/quickstart.html#installing-cocotb \ for installation instructions.') endif diff --git a/makefiles/Makefile.sim b/makefiles/Makefile.sim index 34734707..47866fec 100644 --- a/makefiles/Makefile.sim +++ b/makefiles/Makefile.sim @@ -1,35 +1,10 @@ -############################################################################### -# Copyright (c) 2013,2018 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################## +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause COCOTB_CONFIG_CMD := $(shell :; command -v cocotb-config) ifeq ($(COCOTB_CONFIG_CMD),) - $(error 'Cocotb is not installed (cocotb-config not found). \ + $(error 'cocotb-config not found! Cocotb is not installed (or is installed but not on the PATH). \ Please refer to https://cocotb.readthedocs.io/en/latest/quickstart.html#installing-cocotb \ for installation instructions.') endif From 09effc69f914820d4aa50217b9fad6ba29c4248f Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 5 Apr 2020 17:55:31 +0100 Subject: [PATCH 2071/2656] Pin sphinx before version 3, until an upstream bug is fixed. (#1577) --- documentation/requirements.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/documentation/requirements.txt b/documentation/requirements.txt index 2eddbe4d..d12c7d11 100644 --- a/documentation/requirements.txt +++ b/documentation/requirements.txt @@ -1,5 +1,10 @@ # Python dependencies to build the documentation -Sphinx>=2.1 + +# Pinned below 3 until https://github.com/sphinx-contrib/domaintools/pull/9 is +# merged and released. +Sphinx~=2.1 + +# other dependencies sphinx-rtd-theme cairosvg breathe From 5e6b7375b3ab11090e13bbbad350053f5fa7a9ab Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Mon, 6 Apr 2020 00:06:33 +0200 Subject: [PATCH 2072/2656] Fix "from Unknown" in the log file. (#1581) --- cocotb/__init__.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index e2678eea..7f0ec22f 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -136,12 +136,8 @@ def _initialise_testbench(root_name): if memcheck_port is not None: mem_debug(int(memcheck_port)) - exec_path = os.getenv('COCOTB_PY_DIR') - if exec_path is None: - exec_path = 'Unknown' - log.info("Running tests with cocotb v%s from %s" % - (__version__, exec_path)) + (__version__, os.path.dirname(__file__))) # Create the base handle type From 848cd948bbddab5047c7553c307115fbb82281e5 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Sat, 4 Apr 2020 21:10:36 +0200 Subject: [PATCH 2073/2656] Add documentaion note for missing cocotb-config Co-Authored-By: Colin Marquardt Co-Authored-By: Eric Wieser --- documentation/source/quickstart.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index 25d2447c..3ae0c965 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -41,6 +41,11 @@ To install the development version of cocotb: git clone https://github.com/cocotb/cocotb pip3 install -e ./cocotb +.. note:: + + After installation, you should be able to execute ``cocotb-config``. + If it is not found, you need to append its location to the ``PATH`` environment variable. + This may happen when you use the ``--user`` option to ``pip``, in which case the location is documented :ref:`here `. Native Linux Installation ------------------------- From 67fe4886d939a1a7f9dbe8649aff993e6d7b53ef Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Mon, 6 Apr 2020 10:38:26 +0200 Subject: [PATCH 2074/2656] Update modelsim.def to fix compilation error. (#1578) Also add a .gitignore for the generated import library files. --- cocotb/share/def/.gitignore | 2 ++ cocotb/share/def/modelsim.def | 13 +++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 cocotb/share/def/.gitignore diff --git a/cocotb/share/def/.gitignore b/cocotb/share/def/.gitignore new file mode 100644 index 00000000..322161e3 --- /dev/null +++ b/cocotb/share/def/.gitignore @@ -0,0 +1,2 @@ +# Generated import libraries on Windows +*.a diff --git a/cocotb/share/def/modelsim.def b/cocotb/share/def/modelsim.def index d6dcb053..8674e179 100644 --- a/cocotb/share/def/modelsim.def +++ b/cocotb/share/def/modelsim.def @@ -100,3 +100,16 @@ mti_TickLeft mti_TickLength mti_TickRight mti_VsimFree +mti_Interp +mti_Cmd +Tcl_GetStringResult +Tcl_ResetResult +Tcl_GetObjResult +Tcl_ResetResult +Tcl_ListObjGetElements +Tcl_GetStringResult +TclFreeObj +Tcl_ResetResult +Tcl_ResetResult +Tcl_GetString +TclFreeObj From 5e313a09350034f8552656ec4d88c846de3f4489 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Mon, 6 Apr 2020 10:14:28 +0100 Subject: [PATCH 2075/2656] Add a missing `static` on a private function (#1583) --- cocotb/share/lib/embed/gpi_embed.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/share/lib/embed/gpi_embed.cpp b/cocotb/share/lib/embed/gpi_embed.cpp index cc2607fb..6b65a3ed 100644 --- a/cocotb/share/lib/embed/gpi_embed.cpp +++ b/cocotb/share/lib/embed/gpi_embed.cpp @@ -205,7 +205,7 @@ extern "C" void embed_sim_cleanup(void) * Loads the Python module called cocotb and calls the _initialise_testbench function */ -int get_module_ref(const char *modname, PyObject **mod) +static int get_module_ref(const char *modname, PyObject **mod) { PyObject *pModule = PyImport_ImportModule(modname); From 0238e703d8bee70c6ea6445c24e6b3220af98431 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Mon, 6 Apr 2020 10:32:22 +0100 Subject: [PATCH 2076/2656] Use docs.cocotb.org for documentation Use docs.cocotb.org as entry point for our documentation (it continues to be hosted by readthedocs). Also ensure that we use HTTPS links. --- README.md | 8 ++++---- cocotb/__init__.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 17805c54..4ed8b1f8 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,11 @@ **cocotb** is a coroutine based cosimulation library for writing VHDL and Verilog testbenches in Python. -[![Documentation Status](https://readthedocs.org/projects/cocotb/badge/?version=latest)](http://cocotb.readthedocs.org/en/latest/) +[![Documentation Status](https://readthedocs.org/projects/cocotb/badge/?version=latest)](https://docs.cocotb.org/en/latest/) [![Build Status](https://travis-ci.org/cocotb/cocotb.svg?branch=master)](https://travis-ci.org/cocotb/cocotb) [![PyPI](https://img.shields.io/pypi/dm/cocotb.svg?label=PyPI%20downloads)](https://pypi.org/project/cocotb/) -* Read the [documentation](http://cocotb.readthedocs.org) +* Read the [documentation](https://docs.cocotb.org) * Get involved: * [Raise a bug / request an enhancement](https://github.com/cocotb/cocotb/issues/new) (Requires a GitHub account) * [Join the mailing list](https://lists.librecores.org/listinfo/cocotb) @@ -40,6 +40,6 @@ Cocotb can be installed by running `pip install cocotb`. ## Tutorials and examples -* [Endian Swapper tutorial](https://cocotb.readthedocs.org/en/latest/endian_swapper.html) -* [Ping using TUN/TAP tutorial](https://cocotb.readthedocs.org/en/latest/ping_tun_tap.html) +* [Endian Swapper tutorial](https://docs.cocotb.org/en/latest/endian_swapper.html) +* [Ping using TUN/TAP tutorial](https://docs.cocotb.org/en/latest/ping_tun_tap.html) * [OpenCores JPEG Encoder example](https://github.com/chiggs/oc_jpegencode/) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 7f0ec22f..5c17f985 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -28,7 +28,7 @@ """ Cocotb is a coroutine, cosimulation framework for writing testbenches in Python. -See http://cocotb.readthedocs.org for full documentation +See https://docs.cocotb.org for full documentation """ import os import sys From 688d391376eda47446d8f949d9e62f8ca93d51b0 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Mon, 6 Apr 2020 12:27:41 +0200 Subject: [PATCH 2077/2656] Set PYTHON_BIN using cocotb-config (#1574) Co-Authored-By: Eric Wieser --- cocotb/share/makefiles/Makefile.inc | 3 +++ cocotb/share/makefiles/Makefile.pylib.Darwin | 7 ------- cocotb/share/makefiles/Makefile.pylib.Linux | 7 ------- cocotb/share/makefiles/Makefile.pylib.Msys | 14 +------------- documentation/source/newsfragments/1574.change.rst | 1 + 5 files changed, 5 insertions(+), 27 deletions(-) create mode 100644 documentation/source/newsfragments/1574.change.rst diff --git a/cocotb/share/makefiles/Makefile.inc b/cocotb/share/makefiles/Makefile.inc index 0e55a37f..62a0c470 100644 --- a/cocotb/share/makefiles/Makefile.inc +++ b/cocotb/share/makefiles/Makefile.inc @@ -54,6 +54,9 @@ OS=Msys endif export OS +# this ensures we use the same python as the one cocotb was installed into +PYTHON_BIN ?= $(shell cocotb-config --python-bin) + include $(COCOTB_SHARE_DIR)/makefiles/Makefile.deprecations include $(COCOTB_SHARE_DIR)/makefiles/Makefile.pylib diff --git a/cocotb/share/makefiles/Makefile.pylib.Darwin b/cocotb/share/makefiles/Makefile.pylib.Darwin index 8c004bb7..1f447ec2 100644 --- a/cocotb/share/makefiles/Makefile.pylib.Darwin +++ b/cocotb/share/makefiles/Makefile.pylib.Darwin @@ -29,13 +29,6 @@ # All common python related rules -ifneq ($(COCOTB_PYTHON_DEBUG),) - DBG_PYTHON_VERSION=$(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') - PYTHON_BIN?=python$(DBG_PYTHON_VERSION)-dbg -else - PYTHON_BIN?=python -endif - PYTHON_LIBDIR:=$(shell $(PYTHON_BIN) -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("LIBDIR"))') # Nasty hack but there's no way in Python properly get the directories for 32-bit Python on a 64-bit system diff --git a/cocotb/share/makefiles/Makefile.pylib.Linux b/cocotb/share/makefiles/Makefile.pylib.Linux index 745b58f1..0b46a9c5 100644 --- a/cocotb/share/makefiles/Makefile.pylib.Linux +++ b/cocotb/share/makefiles/Makefile.pylib.Linux @@ -29,13 +29,6 @@ # All common python related rules -ifneq ($(COCOTB_PYTHON_DEBUG),) - DBG_PYTHON_VERSION=$(shell python -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_version())') - PYTHON_BIN?=python$(DBG_PYTHON_VERSION)-dbg -else - PYTHON_BIN?=python -endif - PYTHON_LIBDIR:=$(shell $(PYTHON_BIN) -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("LIBDIR"))') # Nasty hack but there's no way in Python properly get the directories for 32-bit Python on a 64-bit system diff --git a/cocotb/share/makefiles/Makefile.pylib.Msys b/cocotb/share/makefiles/Makefile.pylib.Msys index 979769fc..48fc6d92 100644 --- a/cocotb/share/makefiles/Makefile.pylib.Msys +++ b/cocotb/share/makefiles/Makefile.pylib.Msys @@ -29,21 +29,9 @@ # All common python related rules -# if not explicitly set, auto-detect python dir from system path -ifeq ($(PYTHON_DIR),) - PYTHON_DIR ?= $(shell dirname $(shell :; command -v python)) -endif - -# make sure python dir was set properly -ifeq ($(PYTHON_DIR),) - $(error "Path to Python directory must be included in system path or defined in PYTHON_DIR environment variable") -endif - -PYTHON_BIN?=$(PYTHON_DIR)/python.exe - PYTHON_LIBDIR:=$(shell $(PYTHON_BIN) -c 'from distutils import sysconfig; print( sysconfig.get_config_var("LIBDIR") )') ifeq ($(PYTHON_LIBDIR),None) - PYTHON_LIBDIR=$(PYTHON_DIR)/libs + PYTHON_LIBDIR=$(shell dirname $(PYTHON_BIN))/libs endif PYTHON_INCLUDEDIR := $(shell $(PYTHON_BIN) -c 'import distutils.sysconfig; print( distutils.sysconfig.get_python_inc() )') diff --git a/documentation/source/newsfragments/1574.change.rst b/documentation/source/newsfragments/1574.change.rst new file mode 100644 index 00000000..4d7384c8 --- /dev/null +++ b/documentation/source/newsfragments/1574.change.rst @@ -0,0 +1 @@ +There's no longer any need to set the ``PYTHON_BIN`` makefile variable, the Python executable automatically matches the one cocotb was installed into. \ No newline at end of file From 787d6c936dd897c9e37be7381e6fded22385627a Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Mon, 6 Apr 2020 12:42:55 +0100 Subject: [PATCH 2078/2656] Use sets instead of dicts with meaningless values (#1586) A dict where all the values are true is just a set with extra overhead. --- cocotb/handle.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 4ad0db1c..7559e509 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -46,7 +46,7 @@ from cocotb.result import TestError # Only issue a warning for each deprecated attribute access -_deprecation_warned = {} +_deprecation_warned = set() class SimHandleBase(object): @@ -76,7 +76,7 @@ def __init__(self, handle, path): self._handle = handle self._len = None self._sub_handles = {} # Dictionary of children - self._invalid_sub_handles = {} # Dictionary of invalid queries + self._invalid_sub_handles = set() # Set of invalid queries self._name = simulator.get_name_string(self._handle) self._type = simulator.get_type_string(self._handle) @@ -139,7 +139,7 @@ def __setattr__(self, name, value): if name in self._compat_mapping: if name not in _deprecation_warned: warnings.warn("Use of attribute %r is deprecated, use %r instead" % (name, self._compat_mapping[name])) - _deprecation_warned[name] = True + _deprecation_warned.add(name) return setattr(self, self._compat_mapping[name], value) else: return object.__setattr__(self, name, value) @@ -148,7 +148,7 @@ def __getattr__(self, name): if name in self._compat_mapping: if name not in _deprecation_warned: warnings.warn("Use of attribute %r is deprecated, use %r instead" % (name, self._compat_mapping[name])) - _deprecation_warned[name] = True + _deprecation_warned.add(name) return getattr(self, self._compat_mapping[name]) else: return object.__getattribute__(self, name) @@ -285,7 +285,7 @@ def __hasattr__(self, name): if new_handle: self._sub_handles[name] = SimHandle(new_handle, self._child_path(name)) else: - self._invalid_sub_handles[name] = None + self._invalid_sub_handles.add(name) return new_handle def _id(self, name, extended=True): From 72b0cd03f62cd2d96186b20a77b4ece79ca78b96 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Mon, 6 Apr 2020 14:42:22 +0100 Subject: [PATCH 2079/2656] Remove log lines that duplicate exception information. (#1587) By using exceptions instead of logs, it makes it possible to silence expected exceptions, rather than them leaving noisy logs behind. --- cocotb/handle.py | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 7559e509..857ac575 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -198,14 +198,14 @@ def _discover_all(self): self._log.debug("%s", e) continue - key = self._sub_handle_key(name) - - if key is not None: - self._sub_handles[key] = hdl - else: + try: + key = self._sub_handle_key(name) + except ValueError: self._log.debug("Unable to translate handle >%s< to a valid _sub_handle key", hdl._name) continue + self._sub_handles[key] = hdl + self._discovered = True def _child_path(self, name): @@ -320,8 +320,7 @@ def _sub_handle_key(self, name): if result: return int(result.group("index")) else: - self._log.error("Unable to match an index pattern: %s", name) - return None + raise ValueError("Unable to match an index pattern: {}".format(name)) def __len__(self): """Returns the 'length' of the generate block.""" @@ -646,8 +645,6 @@ def setimmediatevalue(self, value): vallist = list(value["values"]) vallist.reverse() if len(vallist) * value["bits"] != len(self): - self._log.critical("Unable to set with array length %d of %d bit entries = %d total, target is only %d bits long", - len(value["values"]), value["bits"], len(value["values"]) * value["bits"], len(self)) raise TypeError("Unable to set with array length %d of %d bit entries = %d total, target is only %d bits long" % (len(value["values"]), value["bits"], len(value["values"]) * value["bits"], len(self))) @@ -656,8 +653,9 @@ def setimmediatevalue(self, value): value = BinaryValue(value=num, n_bits=len(self), bigEndian=False) elif not isinstance(value, BinaryValue): - self._log.critical("Unsupported type for value assignment: %s (%s)", type(value), repr(value)) - raise TypeError("Unable to set simulator value with type %s" % (type(value))) + raise TypeError( + "Unsupported type for value assignment: {} ({!r})" + .format(type(value), value)) simulator.set_signal_val_binstr(self._handle, set_action, value.binstr) @@ -716,9 +714,9 @@ def setimmediatevalue(self, value): try: value = float(value) except ValueError: - self._log.critical("Unsupported type for real value assignment: %s (%s)" % - (type(value), repr(value))) - raise TypeError("Unable to set simulator value with type %s" % (type(value))) + raise TypeError( + "Unsupported type for real value assignment: {} ({!r})" + .format(type(value), value)) simulator.set_signal_val_real(self._handle, set_action, value) @@ -751,8 +749,9 @@ def setimmediatevalue(self, value): if isinstance(value, BinaryValue): value = int(value) elif not isinstance(value, int): - self._log.critical("Unsupported type for integer value assignment: %s (%s)", type(value), repr(value)) - raise TypeError("Unable to set simulator value with type %s" % (type(value))) + raise TypeError( + "Unsupported type for enum value assignment: {} ({!r})" + .format(type(value), value)) simulator.set_signal_val_long(self._handle, set_action, value) @@ -782,8 +781,9 @@ def setimmediatevalue(self, value): if isinstance(value, BinaryValue): value = int(value) elif not isinstance(value, int): - self._log.critical("Unsupported type for integer value assignment: %s (%s)", type(value), repr(value)) - raise TypeError("Unable to set simulator value with type %s" % (type(value))) + raise TypeError( + "Unsupported type for integer value assignment: {} ({!r})" + .format(type(value), value)) simulator.set_signal_val_long(self._handle, set_action, value) @@ -824,8 +824,9 @@ def setimmediatevalue(self, value): value = value.encode('ascii') # may throw UnicodeEncodeError if not isinstance(value, bytes): - self._log.critical("Unsupported type for string value assignment: %s (%s)", type(value), repr(value)) - raise TypeError("Unable to set simulator value with type %s" % (type(value))) + raise TypeError( + "Unsupported type for string value assignment: {} ({!r})" + .format(type(value), value)) simulator.set_signal_val_str(self._handle, set_action, value) From 8584e8b0fde896c13d37470efb54e4b6caa9c536 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Mon, 6 Apr 2020 16:15:00 +0100 Subject: [PATCH 2080/2656] Remove BinaryValue.__cmp__ (#1589) The __cmp__ magic method is meaningless in python 3. The equivalent is the set of `__eq__`, `__ne__`, `__lt__`, `__le__`, `__gt__`, `__ge__`. Furthermore, an explicit call to this method would just fail because `int.__cmp__` is missing. We already implement `__eq__` and `__ne__`, and overload `__le__` to mean `.assign` - implementing the other operators would be unwise. --- cocotb/binary.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index cdacfb58..215c1ce0 100755 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -443,12 +443,6 @@ def __ne__(self, other): other = other.value return self.value != other - def __cmp__(self, other): - """Comparison against other values""" - if isinstance(other, BinaryValue): - other = other.value - return self.value.__cmp__(other) - def __int__(self): return self.integer From 1bf1ad8d2f5b51b7034ee7d0aeffbeadd7c02788 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 5 Apr 2020 11:00:48 -0500 Subject: [PATCH 2081/2656] Remove __future__ from cocotb runtime libraries --- cocotb/binary.py | 2 -- cocotb/decorators.py | 1 - cocotb/utils.py | 2 -- 3 files changed, 5 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 215c1ce0..632cba92 100755 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -27,8 +27,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from __future__ import print_function - import os import random import warnings diff --git a/cocotb/decorators.py b/cocotb/decorators.py index e671b3c0..8a7c8b9c 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -25,7 +25,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -from __future__ import print_function import sys import time import logging diff --git a/cocotb/utils.py b/cocotb/utils.py index 7de3df55..f010674b 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -1,5 +1,3 @@ -from __future__ import print_function, division - # Copyright (c) 2013 Potential Ventures Ltd # Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. From bceac6efafcc215aa6a86ec56fae59cf45650a05 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 5 Apr 2020 11:11:48 -0500 Subject: [PATCH 2082/2656] Cleanup class definitions that have no base class --- cocotb/_py_compat.py | 2 +- cocotb/binary.py | 4 ++-- cocotb/bus.py | 2 +- cocotb/clock.py | 2 +- cocotb/decorators.py | 8 ++++---- cocotb/drivers/__init__.py | 4 ++-- cocotb/drivers/xgmii.py | 2 +- cocotb/handle.py | 4 ++-- cocotb/monitors/__init__.py | 4 ++-- cocotb/regression.py | 4 ++-- cocotb/scheduler.py | 8 ++++---- cocotb/scoreboard.py | 2 +- cocotb/triggers.py | 4 ++-- cocotb/utils.py | 2 +- cocotb/wavedrom.py | 4 ++-- cocotb/xunit_reporter.py | 2 +- 16 files changed, 29 insertions(+), 29 deletions(-) diff --git a/cocotb/_py_compat.py b/cocotb/_py_compat.py index 30ea7492..687930c7 100644 --- a/cocotb/_py_compat.py +++ b/cocotb/_py_compat.py @@ -32,7 +32,7 @@ # backport of Python 3.7's contextlib.nullcontext -class nullcontext(object): +class nullcontext: """Context manager that does no additional processing. Used as a stand-in for a normal context manager, when a particular block of code is only sometimes used with a normal context manager: diff --git a/cocotb/binary.py b/cocotb/binary.py index 632cba92..2ee5840a 100755 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -61,13 +61,13 @@ def _clog2(val): exp += 1 -class BinaryRepresentation(): # noqa +class BinaryRepresentation: # noqa UNSIGNED = 0 #: Unsigned format SIGNED_MAGNITUDE = 1 #: Sign and magnitude format TWOS_COMPLEMENT = 2 #: Two's complement format -class BinaryValue(object): +class BinaryValue: """Representation of values in binary format. The underlying value can be set or accessed using these aliasing attributes: diff --git a/cocotb/bus.py b/cocotb/bus.py index 405412e8..da1e19e5 100755 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -39,7 +39,7 @@ def _build_sig_attr_dict(signals): return {sig: sig for sig in signals} -class Bus(object): +class Bus: """Wraps up a collection of signals. Assumes we have a set of signals/nets named ``entity.``. diff --git a/cocotb/clock.py b/cocotb/clock.py index d7f92f69..84c2890c 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -34,7 +34,7 @@ from cocotb.utils import get_sim_steps, get_time_from_sim_steps, lazy_property -class BaseClock(object): +class BaseClock: """Base class to derive from.""" def __init__(self, signal): self.signal = signal diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 8a7c8b9c..18ad6e79 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -73,7 +73,7 @@ def __init__(self, text=""): Exception.__init__(self, text) -class RunningTask(object): +class RunningTask: """Per instance wrapper around a running generator. Provides the following: @@ -285,7 +285,7 @@ def sort_name(self): return "%s.%d.%s" % (self.module, self.stage, self.funcname) -class coroutine(object): +class coroutine: """Decorator class that allows us to provide common coroutine mechanisms: ``log`` methods will log to ``cocotb.coroutine.name``. @@ -320,7 +320,7 @@ def __str__(self): @public -class function(object): +class function: """Decorator class that allows a function to block. This allows a coroutine that consumes simulation time @@ -344,7 +344,7 @@ def __get__(self, obj, type=None): return self.__class__(self._coro._func.__get__(obj, type)) @public -class external(object): +class external: """Decorator to apply to an external function to enable calling from cocotb. This turns a normal function that isn't a coroutine into a blocking coroutine. diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index 173d8607..0b114e8b 100755 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -39,7 +39,7 @@ from cocotb.log import SimLog -class BitDriver(object): +class BitDriver: """Drives a signal onto a single bit. Useful for exercising ready/valid flags. @@ -89,7 +89,7 @@ def _cr_twiddler(self, generator=None): yield edge -class Driver(object): +class Driver: """Class defining the standard interface for a driver within a testbench. The driver is responsible for serializing transactions onto the physical diff --git a/cocotb/drivers/xgmii.py b/cocotb/drivers/xgmii.py index 46d9f5df..9c4c9622 100644 --- a/cocotb/drivers/xgmii.py +++ b/cocotb/drivers/xgmii.py @@ -44,7 +44,7 @@ _PREAMBLE_SFD = b"\x55\x55\x55\x55\x55\x55\xD5" -class _XGMIIBus(object): +class _XGMIIBus: r"""Helper object for abstracting the underlying bus format. Index bytes directly on this object, pass a tuple of ``(value, ctrl)`` to diff --git a/cocotb/handle.py b/cocotb/handle.py index 857ac575..d5e763c4 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -49,7 +49,7 @@ _deprecation_warned = set() -class SimHandleBase(object): +class SimHandleBase: """Base class for all simulation objects. We maintain a handle which we can use for GPI calls. @@ -352,7 +352,7 @@ def __setitem__(self, index, value): raise TypeError("Not permissible to set %s at index %d" % (self._name, index)) -class _AssignmentResult(object): +class _AssignmentResult: """ An object that exists solely to provide an error message if the caller is not aware of cocotb's meaning of ``<=``. diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index 2ac02c80..0a18494c 100755 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -42,14 +42,14 @@ from cocotb.triggers import Event, Timer -class MonitorStatistics(object): +class MonitorStatistics: """Wrapper class for storing Monitor statistics""" def __init__(self): self.received_transactions = 0 -class Monitor(object): +class Monitor: """Base class for Monitor objects. Monitors are passive 'listening' objects that monitor pins going in or out of a DUT. diff --git a/cocotb/regression.py b/cocotb/regression.py index e1f52bed..9201dda6 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -72,7 +72,7 @@ def _my_import(name): return mod -class RegressionManager(object): +class RegressionManager: """Encapsulates all regression capability into a single place""" def __init__(self, root_name, modules, tests=None, seed=None, hooks=[]): @@ -534,7 +534,7 @@ async def _my_test(dut): return cocotb.test()(_my_test) -class TestFactory(object): +class TestFactory: """Factory to automatically generate tests. Args: diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index a4e01650..da749c4a 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -71,7 +71,7 @@ class InternalError(RuntimeError): pass -class profiling_context(object): +class profiling_context: """ Context manager that profiles its contents """ def __enter__(self): _profile.enable() @@ -82,14 +82,14 @@ def __exit__(self, *excinfo): from cocotb import outcomes -class external_state(object): +class external_state: INIT = 0 RUNNING = 1 PAUSED = 2 EXITED = 3 @cocotb.decorators.public -class external_waiter(object): +class external_waiter: def __init__(self): self._outcome = None @@ -150,7 +150,7 @@ def thread_wait(self): return self.state -class Scheduler(object): +class Scheduler: """The main scheduler. Here we accept callbacks from the simulator and schedule the appropriate diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index 07b3fd53..b8f1d7e2 100755 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -37,7 +37,7 @@ from cocotb.result import TestFailure, TestSuccess -class Scoreboard(object): +class Scoreboard: """Generic scoreboarding class. We can add interfaces by providing a monitor and an expected output queue. diff --git a/cocotb/triggers.py b/cocotb/triggers.py index f53e4718..291de208 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -373,7 +373,7 @@ def __repr__(self): return "<{!r}.wait() at {}>".format(self.parent, _pointer_str(self)) -class Event(object): +class Event: """Event to permit synchronization between two coroutines. Awaiting :meth:`wait()` from one coroutine will block the coroutine until @@ -453,7 +453,7 @@ def __repr__(self): return "<{!r}.acquire() at {}>".format(self.parent, _pointer_str(self)) -class Lock(object): +class Lock: """Lock primitive (not re-entrant). This can be used as:: diff --git a/cocotb/utils.py b/cocotb/utils.py index f010674b..ee72d430 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -493,7 +493,7 @@ def func(x1, *, a=1, b=2): ) -class lazy_property(object): +class lazy_property: """ A property that is executed the first time, then cached forever. diff --git a/cocotb/wavedrom.py b/cocotb/wavedrom.py index 1076a86b..fc6dbb51 100644 --- a/cocotb/wavedrom.py +++ b/cocotb/wavedrom.py @@ -31,7 +31,7 @@ from cocotb.triggers import RisingEdge, ReadOnly -class Wavedrom(object): +class Wavedrom: """Base class for a WaveDrom compatible tracer.""" def __init__(self, obj): @@ -113,7 +113,7 @@ def get(self, add_clock=True): return siglist -class trace(object): +class trace: """Context manager to enable tracing of signals. Arguments are an arbitrary number of signals or buses to trace. diff --git a/cocotb/xunit_reporter.py b/cocotb/xunit_reporter.py index f89124f8..bd8a1efa 100644 --- a/cocotb/xunit_reporter.py +++ b/cocotb/xunit_reporter.py @@ -63,7 +63,7 @@ def tail(self, lines_2find=1): return line_list[-lines_2find:] -class XUnitReporter(object): +class XUnitReporter: def __init__(self, filename="results.xml"): self.results = Element("testsuites", name="results") From 065dcd7a77c19b7116ed4aa85ae7038cdeed02c0 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 5 Apr 2020 11:14:19 -0500 Subject: [PATCH 2083/2656] Remove __nonzero__ where aliasing __bool__ --- cocotb/binary.py | 3 --- cocotb/decorators.py | 4 +--- cocotb/triggers.py | 4 +--- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 2ee5840a..24d1256b 100755 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -411,9 +411,6 @@ def __repr__(self): return self.__str__() def __bool__(self): - return self.__nonzero__() - - def __nonzero__(self): """Provide boolean testing of a :attr:`binstr`. >>> val = BinaryValue("0000") diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 18ad6e79..397dc685 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -184,7 +184,7 @@ def join(self): def has_started(self): return self._started - def __nonzero__(self): + def __bool__(self): """Provide boolean testing if the coroutine has finished return false otherwise return true""" @@ -201,8 +201,6 @@ def __await__(self): # Hand the coroutine back to the scheduler trampoline. return (yield self) - __bool__ = __nonzero__ - class RunningCoroutine(RunningTask): """ diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 291de208..28d6535c 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -520,12 +520,10 @@ def __repr__(self): _pointer_str(self) ) - def __nonzero__(self): + def __bool__(self): """Provide boolean of a Lock""" return self.locked - __bool__ = __nonzero__ - async def __aenter__(self): return await self.acquire() From 60f63569503724f9bcbd8e55c312da1f2dd25bac Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 5 Apr 2020 12:00:33 -0500 Subject: [PATCH 2084/2656] Remove u-prefix from Python strings --- documentation/source/conf.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/documentation/source/conf.py b/documentation/source/conf.py index 09b590c3..73c2d327 100644 --- a/documentation/source/conf.py +++ b/documentation/source/conf.py @@ -68,8 +68,8 @@ master_doc = 'index' # General information about the project. -project = u'cocotb' -copyright = u'2014-{0}, PotentialVentures'.format(datetime.datetime.now().year) +project = 'cocotb' +copyright = '2014-{0}, PotentialVentures'.format(datetime.datetime.now().year) # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -241,8 +241,8 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'cocotb.tex', u'cocotb Documentation', - u'PotentialVentures', 'manual'), + ('index', 'cocotb.tex', 'cocotb Documentation', + 'PotentialVentures', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -271,8 +271,8 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'cocotb', u'cocotb Documentation', - [u'PotentialVentures'], 1) + ('index', 'cocotb', 'cocotb Documentation', + ['PotentialVentures'], 1) ] # If true, show URL addresses after external links. @@ -285,8 +285,8 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'cocotb', u'cocotb Documentation', - u'PotentialVentures', 'cocotb', 'Coroutine Cosimulation TestBench \ + ('index', 'cocotb', 'cocotb Documentation', + 'PotentialVentures', 'cocotb', 'Coroutine Cosimulation TestBench \ environment for efficient verification of RTL using Python.', 'Miscellaneous'), ] From 5af9ac47547ca3febfa43281015bce3cef6405fa Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 5 Apr 2020 12:08:44 -0500 Subject: [PATCH 2085/2656] Change obj.__class__ to type(obj) --- cocotb/binary.py | 2 +- cocotb/bus.py | 4 ++-- cocotb/clock.py | 4 ++-- cocotb/decorators.py | 12 ++++++------ cocotb/drivers/__init__.py | 2 +- cocotb/monitors/__init__.py | 4 ++-- cocotb/regression.py | 2 +- cocotb/scoreboard.py | 4 ++-- cocotb/triggers.py | 2 +- .../test_iteration_mixedlang/test_iteration.py | 2 +- .../test_cases/test_iteration_vhdl/test_iteration.py | 2 +- .../test_verilog_access/test_verilog_access.py | 4 ++-- .../test_cases/test_vhdl_access/test_vhdl_access.py | 8 ++++---- 13 files changed, 26 insertions(+), 26 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 24d1256b..afabfd5b 100755 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -383,7 +383,7 @@ def binstr(self, string): for char in string: if char not in BinaryValue._permitted_chars: raise ValueError("Attempting to assign character %s to a %s" % - (char, self.__class__.__name__)) + (char, type(self).__name__)) self._str = string self._adjust() diff --git a/cocotb/bus.py b/cocotb/bus.py index da1e19e5..3d21f5d6 100755 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -124,7 +124,7 @@ def drive(self, obj, strict=False): msg = ("Unable to drive onto {0}.{1} because {2} is missing " "attribute {3}".format(self._entity._name, self._name, - obj.__class__.__name__, + type(obj).__name__, attr_name)) raise AttributeError(msg) else: @@ -178,7 +178,7 @@ def sample(self, obj, strict=False): msg = ("Unable to sample from {0}.{1} because {2} is missing " "attribute {3}".format(self._entity._name, self._name, - obj.__class__.__name__, + type(obj).__name__, attr_name)) raise AttributeError(msg) else: diff --git a/cocotb/clock.py b/cocotb/clock.py index 84c2890c..8aa0a0d8 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -42,7 +42,7 @@ def __init__(self, signal): @lazy_property def log(self): return SimLog("cocotb.%s.%s" % ( - self.__class__.__name__, self.signal._name + type(self).__name__, self.signal._name )) @@ -152,4 +152,4 @@ async def start(self, cycles=None, start_high=True): await t def __str__(self): - return self.__class__.__name__ + "(%3.1f MHz)" % self.frequency + return type(self).__name__ + "(%3.1f MHz)" % self.frequency diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 397dc685..f8fb4818 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -305,10 +305,10 @@ def log(self): def __call__(self, *args, **kwargs): return RunningCoroutine(self._func(*args, **kwargs), self) - def __get__(self, obj, type=None): + def __get__(self, obj, owner=None): """Permit the decorator to be used on class methods and standalone functions""" - return self.__class__(self._func.__get__(obj, type)) + return type(self)(self._func.__get__(obj, owner)) def __iter__(self): return self @@ -336,10 +336,10 @@ def log(self): def __call__(self, *args, **kwargs): return cocotb.scheduler.queue_function(self._coro(*args, **kwargs)) - def __get__(self, obj, type=None): + def __get__(self, obj, owner=None): """Permit the decorator to be used on class methods and standalone functions""" - return self.__class__(self._coro._func.__get__(obj, type)) + return type(self)(self._coro._func.__get__(obj, owner)) @public class external: @@ -357,10 +357,10 @@ def __init__(self, func): def __call__(self, *args, **kwargs): return cocotb.scheduler.run_in_executor(self._func, *args, **kwargs) - def __get__(self, obj, type=None): + def __get__(self, obj, owner=None): """Permit the decorator to be used on class methods and standalone functions""" - return self.__class__(self._func.__get__(obj, type)) + return type(self)(self._func.__get__(obj, owner)) class _decorator_helper(type): diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index 0b114e8b..e3f12095 100755 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -102,7 +102,7 @@ def __init__(self): # Sub-classes may already set up logging if not hasattr(self, "log"): - self.log = SimLog("cocotb.driver.%s" % (self.__class__.__name__)) + self.log = SimLog("cocotb.driver.%s" % (type(self).__name__)) # Create an independent coroutine which can send stuff self._thread = cocotb.scheduler.add(self._send_thread()) diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index 0a18494c..a1f8302f 100755 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -82,7 +82,7 @@ def __init__(self, callback=None, event=None): # Sub-classes may already set up logging if not hasattr(self, "log"): - self.log = SimLog("cocotb.monitor.%s" % (self.__class__.__name__)) + self.log = SimLog("cocotb.monitor.%s" % (type(self).__name__)) if callback is not None: self.add_callback(callback) @@ -194,4 +194,4 @@ def in_reset(self): return False def __str__(self): - return "%s(%s)" % (self.__class__.__name__, self.name) + return "%s(%s)" % (type(self).__name__, self.name) diff --git a/cocotb/regression.py b/cocotb/regression.py index 9201dda6..c5f3f401 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -341,7 +341,7 @@ def _score_test(self, test, outcome): # Helper for logging result def _result_was(): result_was = ("{} (result was {})".format - (test.__name__, result.__class__.__name__)) + (test.__name__, type(result).__name__)) return result_was # scoring outcomes diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index b8f1d7e2..24a91465 100755 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -204,7 +204,7 @@ def add_interface(self, monitor, expected_output, compare_fn=None, # Enforce some type checking as we only work with a real monitor if not isinstance(monitor, Monitor): raise TypeError("Expected monitor on the interface but got %s" % - (monitor.__class__.__name__)) + (type(monitor).__name__)) if compare_fn is not None: if callable(compare_fn): @@ -222,7 +222,7 @@ def check_received_transaction(transaction): if monitor.name: log_name = self.log.name + '.' + monitor.name else: - log_name = self.log.name + '.' + monitor.__class__.__name__ + log_name = self.log.name + '.' + type(monitor).__name__ log = logging.getLogger(log_name) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 28d6535c..3411fda1 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -78,7 +78,7 @@ def __init__(self): @lazy_property def log(self): - return SimLog("cocotb.%s" % (self.__class__.__name__), id(self)) + return SimLog("cocotb.%s" % (type(self).__name__), id(self)) @abc.abstractmethod def prime(self, callback): diff --git a/tests/test_cases/test_iteration_mixedlang/test_iteration.py b/tests/test_cases/test_iteration_mixedlang/test_iteration.py index 1d6d2a54..8ed645ab 100644 --- a/tests/test_cases/test_iteration_mixedlang/test_iteration.py +++ b/tests/test_cases/test_iteration_mixedlang/test_iteration.py @@ -84,7 +84,7 @@ def recursive_discovery(dut): if not isinstance(dut.i_verilog.uart1.baud_gen_1.baud_freq, cocotb.handle.ModifiableObject): tlog.error("Expected dut.i_verilog.uart1.baud_gen_1.baud_freq to be modifiable") - tlog.error("but it was %s" % dut.i_verilog.uart1.baud_gen_1.baud_freq.__class__.__name__) + tlog.error("but it was %s" % type(dut.i_verilog.uart1.baud_gen_1.baud_freq).__name__) raise TestFailure() diff --git a/tests/test_cases/test_iteration_vhdl/test_iteration.py b/tests/test_cases/test_iteration_vhdl/test_iteration.py index 1af66f65..4b8bb54e 100644 --- a/tests/test_cases/test_iteration_vhdl/test_iteration.py +++ b/tests/test_cases/test_iteration_vhdl/test_iteration.py @@ -84,7 +84,7 @@ def dual_iteration(dut): @cocotb.test() def get_clock(dut): - dut._log.info("dut.aclk is %s", dut.aclk.__class__.__name__) + dut._log.info("dut.aclk is %s", type(dut.aclk).__name__) dut.aclk <= 0 yield Timer(1) dut.aclk <= 1 diff --git a/tests/test_cases/test_verilog_access/test_verilog_access.py b/tests/test_cases/test_verilog_access/test_verilog_access.py index a99d2dcc..596985c9 100644 --- a/tests/test_cases/test_verilog_access/test_verilog_access.py +++ b/tests/test_cases/test_verilog_access/test_verilog_access.py @@ -43,9 +43,9 @@ def port_not_hierarchy(dut): def check_instance(obj, objtype): if not isinstance(obj, objtype): tlog.error("Expected %s to be of type %s but got %s" % ( - obj._fullname, objtype.__name__, obj.__class__.__name__)) + obj._fullname, objtype.__name__, type(obj).__name__)) return 1 - tlog.info("%s is %s" % (obj._fullname, obj.__class__.__name__)) + tlog.info("%s is %s" % (obj._fullname, type(obj).__name__)) return 0 fails += check_instance(dut.clk, ModifiableObject) diff --git a/tests/test_cases/test_vhdl_access/test_vhdl_access.py b/tests/test_cases/test_vhdl_access/test_vhdl_access.py index ee45e6c8..b9979f61 100644 --- a/tests/test_cases/test_vhdl_access/test_vhdl_access.py +++ b/tests/test_cases/test_vhdl_access/test_vhdl_access.py @@ -53,9 +53,9 @@ def check_objects(dut): def check_instance(obj, objtype): if not isinstance(obj, objtype): tlog.error("Expected %s to be of type %s but got %s" % ( - obj._fullname, objtype.__name__, obj.__class__.__name__)) + obj._fullname, objtype.__name__, type(obj).__name__)) return 1 - tlog.info("%s is %s" % (obj._fullname, obj.__class__.__name__)) + tlog.info("%s is %s" % (obj._fullname, type(obj).__name__)) return 0 # Hierarchy checks @@ -101,12 +101,12 @@ def port_not_hierarchy(dut): tlog = logging.getLogger("cocotb.test") yield Timer(100) if not isinstance(dut.aclk, ModifiableObject): - tlog.error("dut.aclk should be ModifiableObject but got %s", dut.aclk.__class__.__name__) + tlog.error("dut.aclk should be ModifiableObject but got %s", type(dut.aclk).__name__) else: tlog.info("dut.aclk is ModifiableObject") for _ in dut: pass if not isinstance(dut.aclk, ModifiableObject): - tlog.error("dut.aclk should be ModifiableObject but got %s", dut.aclk.__class__.__name__) + tlog.error("dut.aclk should be ModifiableObject but got %s", type(dut.aclk).__name__) else: tlog.info("dut.aclk is ModifiableObject") From 9ad9b422a41be8ed227f524aa6eae0d5397271b6 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 1 Apr 2020 23:59:23 +0200 Subject: [PATCH 2086/2656] Improve some expect_error --- tests/test_cases/test_discovery/test_discovery.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 6a42fff5..c79fecda 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -154,7 +154,7 @@ def access_single_bit_erroneous(dut): dut.stream_in_data[bit] <= 1 yield Timer(10) -@cocotb.test(expect_error=cocotb.SIM_NAME.lower().startswith(("icarus")), +@cocotb.test(expect_error=cocotb.SIM_NAME.lower().startswith(("icarus", "chronologic simulation vcs")), expect_fail=cocotb.SIM_NAME.lower().startswith(("riviera")) and cocotb.LANGUAGE in ["verilog"]) def access_integer(dut): """Integer should show as an IntegerObject""" @@ -274,8 +274,7 @@ def access_string_vhdl(dut): # TODO: add tests for Verilog "string_input_port" and "STRING_LOCALPARAM" (see issue #802) @cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"] or cocotb.SIM_NAME.lower().startswith("icarus"), - expect_fail=cocotb.SIM_NAME.lower().startswith("modelsim"), - expect_error=cocotb.SIM_NAME.startswith(("xmsim", "ncsim"))) + expect_error=cocotb.result.TestFailure if cocotb.SIM_NAME.lower().startswith(("xmsim", "ncsim", "modelsim", "chronologic simulation vcs")) else False) def access_const_string_verilog(dut): """Access to a const Verilog string.""" tlog = logging.getLogger("cocotb.test") From b60a65bb39d06991cd112b4ef69f1475dc930345 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 2 Apr 2020 00:01:00 +0200 Subject: [PATCH 2087/2656] Add better debug output for fails --- .../test_discovery/test_discovery.py | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index c79fecda..2a0d45c4 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -163,15 +163,15 @@ def access_integer(dut): yield Timer(10) test_int = dut.stream_in_int if not isinstance(test_int, IntegerObject): - raise TestFailure("dut.stream_in_int is not an integer") + raise TestFailure("dut.stream_in_int is not an integer but {} instead".format(type(test_int))) try: bit = test_int[3] except IndexError as e: tlog.info("Access to bit is an error as expected") - bitFail = True + bitfail = True - if not bitFail: + if not bitfail: raise TestFailure("Access into an integer should be invalid") length = len(test_int) @@ -205,7 +205,7 @@ def access_string_vhdl(dut): """Access to a string, both constant and signal.""" tlog = logging.getLogger("cocotb.test") yield Timer(10) - constant_string = dut.isample_module1.EXAMPLE_STRING; + constant_string = dut.isample_module1.EXAMPLE_STRING tlog.info("%r is %s" % (constant_string, str(constant_string))) if not isinstance(constant_string, ConstantObject): raise TestFailure("EXAMPLE_STRING was not constant") @@ -265,7 +265,7 @@ def access_string_vhdl(dut): test_string = test_string.upper() - result = str(variable_string); + result = str(variable_string) tlog.info("After setting bytes of string value is %s" % result) if variable_string != test_string: raise TestFailure("%r %s != '%s'" % (variable_string, result, test_string)) @@ -278,21 +278,20 @@ def access_string_vhdl(dut): def access_const_string_verilog(dut): """Access to a const Verilog string.""" tlog = logging.getLogger("cocotb.test") + string_const = dut.STRING_CONST - yield Timer(10) - string_const = dut.STRING_CONST; + yield Timer(10, 'ns') tlog.info("%r is %s" % (string_const, str(string_const))) if not isinstance(string_const, StringObject): raise TestFailure("STRING_CONST was not StringObject") if string_const != b"TESTING_CONST": - raise TestFailure("STRING_CONST was not == \'TESTING_CONST\'") + raise TestFailure("Initial value of STRING_CONST was not == b\'TESTING_CONST\' but {} instead".format(string_const)) tlog.info("Modifying const string") string_const <= b"MODIFIED" - yield Timer(10) - string_const = dut.STRING_CONST; + yield Timer(10, 'ns') if string_const != b"TESTING_CONST": - raise TestFailure("STRING_CONST was not still \'TESTING_CONST\'") + raise TestFailure("STRING_CONST was not still b\'TESTING_CONST\' after modification but {} instead".format(string_const)) @cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"], @@ -300,21 +299,20 @@ def access_const_string_verilog(dut): def access_var_string_verilog(dut): """Access to a var Verilog string.""" tlog = logging.getLogger("cocotb.test") + string_var = dut.STRING_VAR - yield Timer(10) - string_var = dut.STRING_VAR; + yield Timer(10, 'ns') tlog.info("%r is %s" % (string_var, str(string_var))) if not isinstance(string_var, StringObject): raise TestFailure("STRING_VAR was not StringObject") if string_var != b"TESTING_VAR": - raise TestFailure("STRING_VAR was not == \'TESTING_VAR\'") + raise TestFailure("Initial value of STRING_VAR was not == b\'TESTING_VAR\' but {} instead".format(string_var)) tlog.info("Modifying var string") string_var <= b"MODIFIED" - yield Timer(10) - string_var = dut.STRING_VAR; + yield Timer(10, 'ns') if string_var != b"MODIFIED": - raise TestFailure("STRING_VAR was not == \'MODIFIED\'") + raise TestFailure("STRING_VAR was not == b\'MODIFIED\' after modification but {} instead".format(string_var)) @cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) @@ -346,9 +344,9 @@ def access_boolean(dut): bit = boolean[3] except TestError as e: tlog.info("Access to bit is an error as expected") - bitFail = True + bitfail = True - if not bitFail: + if not bitfail: raise TestFailure("Access into an integer should be invalid") length = len(boolean) @@ -368,7 +366,7 @@ def access_boolean(dut): tlog.info("Value of %s is now %d" % (output_bool, output_bool)) if (int(curr_val) == int(output_bool)): - raise TestFailure("Value did not propogate") + raise TestFailure("Value did not propagate") @cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"]) def access_internal_register_array(dut): From 44f7bdee9f1024462b7e11770060dc77767c9278 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Tue, 7 Apr 2020 14:04:07 +0200 Subject: [PATCH 2088/2656] Improve windows support in Makefiles (#1588) This: * sets PYTHONHOME in order to properly populate sys.path in embedded python interpreter. * sets PATH to include a path to cocotb compiled libraries. * cleans up conversion between windows and unix paths * removes the workaround for a libstdc++ issue with mingw that doesn't appear to be reproducible In order to make `PYTHONPATH` be correctly converted from unix to windows paths, the order has been swapped from `PWD:PYTHONPATH` to `PYTHONPATH:PWD`. While strictly less correct, this avoids a mingw issue with trailing colons in the path. A future PR will likely remove PYTHONPATH entirely. Co-Authored-By: Eric Wieser --- .gitignore | 1 + cocotb/_build_libs.py | 5 +++- cocotb/share/makefiles/Makefile.inc | 17 +++++++++-- cocotb/share/makefiles/Makefile.sim | 6 +--- .../share/makefiles/simulators/Makefile.aldec | 29 ++----------------- .../makefiles/simulators/Makefile.icarus | 15 ++-------- .../makefiles/simulators/Makefile.questa | 24 ++------------- .../makefiles/simulators/Makefile.verilator | 15 ++-------- examples/endian_swapper/tests/Makefile | 10 +++---- tests/test_cases/issue_253/Makefile | 6 ++-- 10 files changed, 36 insertions(+), 92 deletions(-) diff --git a/.gitignore b/.gitignore index bddbbd4a..7b3e3a79 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ # C extensions *.so +*.dll # C objects *.o diff --git a/cocotb/_build_libs.py b/cocotb/_build_libs.py index d53f6f79..0fd5b81f 100755 --- a/cocotb/_build_libs.py +++ b/cocotb/_build_libs.py @@ -64,7 +64,7 @@ def get_export_symbols(self, ext): def get_ext_filename(self, ext_name): """ Like the base class method, but for libraries that are not python extension: - - removes the ``.cpython-36m-x86_64-linux-gnu.`` part before the extension + - removes the ``.cpython-36m-x86_64-linux-gnu.`` or ``-cpython-36m.`` part before the extension - replaces ``.pyd`` with ``.dll`` on windows. """ @@ -77,6 +77,9 @@ def get_ext_filename(self, ext_name): head, tail = os.path.split(filename) tail_split = tail.split(".") + # mingw on msys2 uses `-` as seperator + tail_split = tail_split[0].split("-") + filename_short = os.path.join(head, tail_split[0] + "." + _get_lib_ext_name()) # icarus requires vpl extension diff --git a/cocotb/share/makefiles/Makefile.inc b/cocotb/share/makefiles/Makefile.inc index 62a0c470..ce54358c 100644 --- a/cocotb/share/makefiles/Makefile.inc +++ b/cocotb/share/makefiles/Makefile.inc @@ -55,7 +55,8 @@ endif export OS # this ensures we use the same python as the one cocotb was installed into -PYTHON_BIN ?= $(shell cocotb-config --python-bin) +# realpath to convert windows paths to unix paths, like cygpath -u +PYTHON_BIN ?= $(realpath $(shell cocotb-config --python-bin)) include $(COCOTB_SHARE_DIR)/makefiles/Makefile.deprecations include $(COCOTB_SHARE_DIR)/makefiles/Makefile.pylib @@ -75,10 +76,22 @@ ifeq ($(SIM_DEFINE),XCELIUM) SIM_DEFINE = IUS endif -export LIB_DIR=$(COCOTB_PY_DIR)/cocotb/libs +LIB_DIR=$(COCOTB_PY_DIR)/cocotb/libs ifeq ($(OS),Msys) LIB_EXT := dll else LIB_EXT := so endif + +# Set PYTHONHOME to properly populate sys.path in embedded python interpreter +export PYTHONHOME := $(shell $(PYTHON_BIN) -c 'from distutils.sysconfig import get_config_var; print(get_config_var("prefix"))') + +ifeq ($(OS),Msys) + # On Windows there is no support for @rpath/LD_LIBRARY_PATH so we need to set PATH to find cocotb libraries + # https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order?redirectedfrom=MSDN#standard-search-order-for-desktop-applications + export PATH := $(LIB_DIR):$(PATH) + + # For tcl scripts the path to dll has to be in form of C:/User/.. + LIB_DIR := $(shell cygpath -m '$(LIB_DIR)') +endif diff --git a/cocotb/share/makefiles/Makefile.sim b/cocotb/share/makefiles/Makefile.sim index 6e7bdb22..fb91813f 100644 --- a/cocotb/share/makefiles/Makefile.sim +++ b/cocotb/share/makefiles/Makefile.sim @@ -136,11 +136,7 @@ define check_for_results_file @test -f $(COCOTB_RESULTS_FILE) || (echo "ERROR: $(COCOTB_RESULTS_FILE) was not written by the simulation!" >&2 && exit 1) endef -# Append directory containing the cocotb Python package to PYTHONPATH -# XXX: This is only needed if cocotb is not installed into the default -# Python search path. -PYTHONPATH := $(COCOTB_PY_DIR):$(PYTHONPATH) -export PYTHONPATH +PYTHONPATH := $(PYTHONPATH):$(PWD) $(SIM_BUILD): mkdir -p $@ diff --git a/cocotb/share/makefiles/simulators/Makefile.aldec b/cocotb/share/makefiles/simulators/Makefile.aldec index 0fc382d9..4fd73213 100644 --- a/cocotb/share/makefiles/simulators/Makefile.aldec +++ b/cocotb/share/makefiles/simulators/Makefile.aldec @@ -90,14 +90,7 @@ ifneq ($(VHDL_SOURCES),) endif else ifeq ($(TOPLEVEL_LANG),vhdl) - -ifeq ($(OS),Msys) - VHPI_LIB = $(shell sh -c 'cd $(LIB_DIR) && pwd -W')/libcocotbvhpi_aldec -else - VHPI_LIB = $(LIB_DIR)/libcocotbvhpi_aldec -endif - - GPI_ARGS = -loadvhpi $(VHPI_LIB):vhpi_startup_routines_bootstrap + GPI_ARGS = -loadvhpi $(LIB_DIR)/libcocotbvhpi_aldec:vhpi_startup_routines_bootstrap ifneq ($(VERILOG_SOURCES),) GPI_EXTRA = cocotbvpi_aldec:cocotbvpi_entry_point endif @@ -145,30 +138,12 @@ ifeq ($(COVERAGE),1) endif endif -ifeq ($(OS),Msys) - -# Windows allows the situation where the libstc++ used at link time as -# specified by -L can be different to the one that is used at runtime which -# comes from the first libstdc++ that it finds in the path. As such -# we use the mingw lib used at build time and put this at the start of the path -# before running - -MINGW_BIN_DIR = $(shell dirname $(shell :; command -v gcc)) - -OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') -LIB_LOAD = PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) -NEW_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-zA-Z]\):\//\/\1\//g' -e 's/;/:/g') -else -LIB_LOAD := -NEW_PYTHONPATH := $(PYTHONPATH) -endif - # Note it's the redirection of the output rather than the 'do' command # that turns on batch mode (i.e. exit on completion/error) $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(COCOTB_VPI_LIB) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) - set -o pipefail; cd $(SIM_BUILD) && PYTHONPATH=$(PWD):$(NEW_PYTHONPATH) GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ + set -o pipefail; cd $(SIM_BUILD) && PYTHONPATH=$(PYTHONPATH) GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 $(CMD) $(RUN_ARGS) -do runsim.tcl | tee sim.log $(call check_for_results_file) diff --git a/cocotb/share/makefiles/simulators/Makefile.icarus b/cocotb/share/makefiles/simulators/Makefile.icarus index 895be34c..2b85c661 100644 --- a/cocotb/share/makefiles/simulators/Makefile.icarus +++ b/cocotb/share/makefiles/simulators/Makefile.icarus @@ -60,21 +60,10 @@ $(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) # Execution phase -ifeq ($(OS),Msys) - -OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') -LIB_LOAD = PATH=$(OLD_PATH):$(LIB_DIR) -NEW_PYTHONPATH:=$(shell python -c "import sys, os; print(':'.join(['/'+dir.replace(os.sep,'/').replace(':','') for dir in sys.path]))") - -else -LIB_LOAD := -NEW_PYTHONPATH := $(PYTHONPATH) -endif - $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) -@rm -f $(COCOTB_RESULTS_FILE) - PYTHONPATH=$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(PYTHONPATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(ICARUS_BIN_DIR)/vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_ARGS) $(EXTRA_ARGS) $(SIM_BUILD)/sim.vvp $(PLUSARGS) @@ -83,7 +72,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $ debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) -@rm -f $(COCOTB_RESULTS_FILE) - PYTHONPATH=$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(PYTHONPATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ gdb --args $(ICARUS_BIN_DIR)/vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) diff --git a/cocotb/share/makefiles/simulators/Makefile.questa b/cocotb/share/makefiles/simulators/Makefile.questa index 5e404532..a31f3b35 100644 --- a/cocotb/share/makefiles/simulators/Makefile.questa +++ b/cocotb/share/makefiles/simulators/Makefile.questa @@ -114,25 +114,6 @@ else @echo "quit" >> $@ endif -ifeq ($(OS),Msys) - -# Windows allows the situation where the libstc++ used at link time as -# specified by -L can be different to the one that is used at runtime which -# comes from the first libstdc++ that it finds in the path. As such -# we use the mingw lib used at build time and put this at the start of the path -# before running - -MINGW_BIN_DIR = $(shell dirname $(shell :; command -v gcc)) - -OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') - -LIB_LOAD := PATH=$(MINGW_BIN_DIR):$(OLD_PATH):$(LIB_DIR) -NEW_PYTHONPATH := $(shell echo "$(PYTHONPATH)" | sed -e 's/\\/\//g' -e 's/\([a-zA-Z]\):\//\/\1\//g' -e 's/;/:/g') -else -LIB_LOAD := -NEW_PYTHONPATH := $(PYTHONPATH) -endif - INT_LIBS := $(COCOTB_VPI_LIB) $(COCOTB_FLI_LIB) ifneq ($(ARCH),i686) @@ -144,8 +125,8 @@ endif $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) -@rm -f $(COCOTB_RESULTS_FILE) - set -o pipefail; $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ - GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) PYTHONPATH=$(PWD):$(NEW_PYTHONPATH) \ + set -o pipefail; PYTHONPATH=$(PYTHONPATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ + GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(CMD) $(RUN_ARGS) -do $(SIM_BUILD)/runsim.do $(PLUSARGS) 2>&1 | tee $(SIM_BUILD)/sim.log $(call check_for_results_file) @@ -155,4 +136,3 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) clean:: -rm -rf $(SIM_BUILD) - diff --git a/cocotb/share/makefiles/simulators/Makefile.verilator b/cocotb/share/makefiles/simulators/Makefile.verilator index b3923fb2..ec3dbb8e 100644 --- a/cocotb/share/makefiles/simulators/Makefile.verilator +++ b/cocotb/share/makefiles/simulators/Makefile.verilator @@ -43,21 +43,10 @@ $(SIM_BUILD)/Vtop.mk: $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(COCOTB_SHARE_D $(SIM_BUILD)/$(TOPLEVEL): $(SIM_BUILD)/Vtop.mk $(COCOTB_LIBS) $(COCOTB_VPI_LIB) CPPFLAGS="$(SIM_BUILD_FLAGS)" make -C $(SIM_BUILD) -f Vtop.mk -ifeq ($(OS),Msys) - -OLD_PATH := $(shell echo "$(PATH)" | sed 's/(/\\(/g' | sed 's/)/\\)/g' | sed 's/ /\\ /g') -LIB_LOAD = PATH=$(OLD_PATH):$(LIB_DIR) -NEW_PYTHONPATH:=$(shell python -c "import sys, os; print(':'.join(['/'+dir.replace(os.sep,'/').replace(':','') for dir in sys.path]))") - -else -LIB_LOAD := -NEW_PYTHONPATH := $(PYTHONPATH) -endif - $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) -@rm -f $(COCOTB_RESULTS_FILE) - PYTHONPATH=$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(PYTHONPATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $< $(PLUSARGS) @@ -66,7 +55,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) $(COCOTB_LIB debug: $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) -@rm -f $(COCOTB_RESULTS_FILE) - PYTHONPATH=$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(PYTHONPATH) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ gdb --args $< $(PLUSARGS) diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index 997a24cd..bbb40bee 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -30,17 +30,16 @@ # Default to verilog TOPLEVEL_LANG ?= verilog -WPWD=$(shell sh -c 'pwd -W') PWD=$(shell pwd) ifeq ($(OS),Msys) -WPWD=$(shell sh -c 'pwd -W') -PYTHONPATH := $(PWD)/../cosim;$(PYTHONPATH) + WPWD=$(shell cygpath -m "$(PWD)") else -WPWD=$(shell pwd) -PYTHONPATH := $(PWD)/../cosim:$(PYTHONPATH) + WPWD=$(PWD) endif +PYTHONPATH := $(PWD)/../cosim:$(PYTHONPATH) + ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES = $(WPWD)/../hdl/endian_swapper.sv TOPLEVEL = endian_swapper_sv @@ -91,4 +90,3 @@ profile: clean:: -rm -rf test_profile.pstat -rm -rf callgraph.svg - diff --git a/tests/test_cases/issue_253/Makefile b/tests/test_cases/issue_253/Makefile index 54805b87..0ad0652d 100644 --- a/tests/test_cases/issue_253/Makefile +++ b/tests/test_cases/issue_253/Makefile @@ -44,19 +44,19 @@ $(COCOTB_RESULTS_FILE): @echo "Skipping" notset_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(PYTHONPATH) MODULE=$(MODULE) \ COCOTB_SIM=1 TESTCASE=issue_253_notset TOPLEVEL= \ vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) mkdir -p $@_result && mv results.xml $@_result/ empty_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(PYTHONPATH) MODULE=$(MODULE) \ COCOTB_SIM=1 TESTCASE=issue_253_empty TOPLEVEL="" \ vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) mkdir -p $@_result && mv results.xml $@_result/ no_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(PWD):$(NEW_PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + PYTHONPATH=$(PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ COCOTB_SIM=1 TESTCASE=issue_253_none \ vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) mkdir -p $@_result && mv results.xml $@_result/ From afeb587cd239ad955860555a30f0f5add95f14bf Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Tue, 7 Apr 2020 14:51:00 +0200 Subject: [PATCH 2089/2656] Remove PYTHONPATH from simulator invocation (#1595) Previously, we messed with PYTHONPATH in the makefiles in order to add the current directory. Normally, python includes this directory by default, but it does not when python is embedded in another application. The easiest way to do this is to prepend `""` to `sys.path` when cocotb is loaded, although in future we should do it as part of launching the embedded interpreter. This resolves a minor issue with the order of PYTHONPATH from a previous PR. It is now up to the user to append and export PYTHONPATH if needed. --- cocotb/__init__.py | 5 +++++ cocotb/share/makefiles/Makefile.sim | 2 -- cocotb/share/makefiles/simulators/Makefile.aldec | 2 +- cocotb/share/makefiles/simulators/Makefile.cvc | 8 ++++---- cocotb/share/makefiles/simulators/Makefile.ghdl | 2 +- cocotb/share/makefiles/simulators/Makefile.icarus | 4 ++-- cocotb/share/makefiles/simulators/Makefile.ius | 1 - cocotb/share/makefiles/simulators/Makefile.nvc | 3 +-- cocotb/share/makefiles/simulators/Makefile.questa | 2 +- cocotb/share/makefiles/simulators/Makefile.vcs | 3 +-- cocotb/share/makefiles/simulators/Makefile.verilator | 4 ++-- cocotb/share/makefiles/simulators/Makefile.xcelium | 1 - examples/adder/tests/Makefile | 2 +- examples/endian_swapper/tests/Makefile | 2 +- tests/test_cases/issue_253/Makefile | 6 +++--- 15 files changed, 23 insertions(+), 24 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 5c17f985..8746af1d 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -57,6 +57,11 @@ # GPI logging instance if "COCOTB_SIM" in os.environ: + # sys.path normally includes "" (the current directory), but does not appear to when python is embedded. + # Add it back because users expect to be able to import files in their test directory. + # TODO: move this to gpi_embed.cpp + sys.path.insert(0, "") + def _reopen_stream_with_buffering(stream_name): try: if not getattr(sys, stream_name).isatty(): diff --git a/cocotb/share/makefiles/Makefile.sim b/cocotb/share/makefiles/Makefile.sim index fb91813f..f8e9dcf5 100644 --- a/cocotb/share/makefiles/Makefile.sim +++ b/cocotb/share/makefiles/Makefile.sim @@ -136,8 +136,6 @@ define check_for_results_file @test -f $(COCOTB_RESULTS_FILE) || (echo "ERROR: $(COCOTB_RESULTS_FILE) was not written by the simulation!" >&2 && exit 1) endef -PYTHONPATH := $(PYTHONPATH):$(PWD) - $(SIM_BUILD): mkdir -p $@ diff --git a/cocotb/share/makefiles/simulators/Makefile.aldec b/cocotb/share/makefiles/simulators/Makefile.aldec index 4fd73213..096dd231 100644 --- a/cocotb/share/makefiles/simulators/Makefile.aldec +++ b/cocotb/share/makefiles/simulators/Makefile.aldec @@ -143,7 +143,7 @@ endif $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(COCOTB_VPI_LIB) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) - set -o pipefail; cd $(SIM_BUILD) && PYTHONPATH=$(PYTHONPATH) GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ + set -o pipefail; cd $(SIM_BUILD) && GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 $(CMD) $(RUN_ARGS) -do runsim.tcl | tee sim.log $(call check_for_results_file) diff --git a/cocotb/share/makefiles/simulators/Makefile.cvc b/cocotb/share/makefiles/simulators/Makefile.cvc index f8e99b69..e810f993 100644 --- a/cocotb/share/makefiles/simulators/Makefile.cvc +++ b/cocotb/share/makefiles/simulators/Makefile.cvc @@ -59,7 +59,7 @@ endif # Compilation phase $(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(PWD):$(PYTHONPATH) MODULE=$(MODULE) \ + MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(CMD) $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=$(LIB_DIR)/libcocotbvpi_modelsim:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) @@ -68,7 +68,7 @@ ifeq ($(CVC_ITERP),1) $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp else $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) - PYTHONPATH=$(PWD):$(PYTHONPATH) MODULE=$(MODULE) \ + MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) endif @@ -76,12 +76,12 @@ endif # Execution phase ifeq ($(CVC_ITERP),1) debug: $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(PWD):$(PYTHONPATH) MODULE=$(MODULE) \ + MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ gdb --args $(CMD) $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=$(LIB_DIR)/modelsim/libcocotbvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) else debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) - PYTHONPATH=$(PWD):$(PYTHONPATH) MODULE=$(MODULE) \ + MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ gdb --args $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) endif diff --git a/cocotb/share/makefiles/simulators/Makefile.ghdl b/cocotb/share/makefiles/simulators/Makefile.ghdl index 47ae346e..e4378d9a 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ghdl +++ b/cocotb/share/makefiles/simulators/Makefile.ghdl @@ -68,7 +68,7 @@ $(COCOTB_RESULTS_FILE): analyse $(COCOTB_LIBS) $(COCOTB_VPI_LIB) $(CUSTOM_SIM_DE -@rm -f $(COCOTB_RESULTS_FILE) cd $(SIM_BUILD); \ - PYTHONPATH=$(PWD):$(PYTHONPATH) MODULE=$(MODULE) \ + MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(CMD) -r $(GHDL_ARGS) --work=$(RTL_LIBRARY) $(TOPLEVEL) --vpi=$(LIB_DIR)/libcocotbvpi_ghdl.$(LIB_EXT) $(SIM_ARGS) $(PLUSARGS) diff --git a/cocotb/share/makefiles/simulators/Makefile.icarus b/cocotb/share/makefiles/simulators/Makefile.icarus index 2b85c661..76f96a6a 100644 --- a/cocotb/share/makefiles/simulators/Makefile.icarus +++ b/cocotb/share/makefiles/simulators/Makefile.icarus @@ -63,7 +63,7 @@ $(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) -@rm -f $(COCOTB_RESULTS_FILE) - PYTHONPATH=$(PYTHONPATH) MODULE=$(MODULE) \ + MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(ICARUS_BIN_DIR)/vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_ARGS) $(EXTRA_ARGS) $(SIM_BUILD)/sim.vvp $(PLUSARGS) @@ -72,7 +72,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $ debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) -@rm -f $(COCOTB_RESULTS_FILE) - PYTHONPATH=$(PYTHONPATH) MODULE=$(MODULE) \ + MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ gdb --args $(ICARUS_BIN_DIR)/vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) diff --git a/cocotb/share/makefiles/simulators/Makefile.ius b/cocotb/share/makefiles/simulators/Makefile.ius index cffa7652..7f3d292f 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ius +++ b/cocotb/share/makefiles/simulators/Makefile.ius @@ -101,7 +101,6 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUS -@rm -f $(COCOTB_RESULTS_FILE) set -o pipefail; \ - PYTHONPATH=$(PWD):$(PYTHONPATH) \ MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ \ diff --git a/cocotb/share/makefiles/simulators/Makefile.nvc b/cocotb/share/makefiles/simulators/Makefile.nvc index 1fece4af..2a32d109 100644 --- a/cocotb/share/makefiles/simulators/Makefile.nvc +++ b/cocotb/share/makefiles/simulators/Makefile.nvc @@ -41,8 +41,7 @@ $(COCOTB_RESULTS_FILE): analyse $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(CUSTOM_SIM_D cd $(SIM_BUILD) && \ $(CMD) --work=$(RTL_LIBRARY) -e $(TOPLEVEL) - cd $(SIM_BUILD) && PYTHONPATH=$(PWD):$(PYTHONPATH) \ - MODULE=$(MODULE) \ + cd $(SIM_BUILD) && MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(CMD) $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) --work=$(RTL_LIBRARY) -r --load $(LIB_DIR)/libcocotbvhpi_ius.$(LIB_EXT) $(TRACE) $(TOPLEVEL) diff --git a/cocotb/share/makefiles/simulators/Makefile.questa b/cocotb/share/makefiles/simulators/Makefile.questa index a31f3b35..6ecb5e3e 100644 --- a/cocotb/share/makefiles/simulators/Makefile.questa +++ b/cocotb/share/makefiles/simulators/Makefile.questa @@ -125,7 +125,7 @@ endif $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) -@rm -f $(COCOTB_RESULTS_FILE) - set -o pipefail; PYTHONPATH=$(PYTHONPATH) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ + set -o pipefail; MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(CMD) $(RUN_ARGS) -do $(SIM_BUILD)/runsim.do $(PLUSARGS) 2>&1 | tee $(SIM_BUILD)/sim.log diff --git a/cocotb/share/makefiles/simulators/Makefile.vcs b/cocotb/share/makefiles/simulators/Makefile.vcs index 7e2f6adc..52e18afd 100644 --- a/cocotb/share/makefiles/simulators/Makefile.vcs +++ b/cocotb/share/makefiles/simulators/Makefile.vcs @@ -79,8 +79,7 @@ $(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(COCOTB $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/simv $(PYTHON_FILES) $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) - -PYTHONPATH=$(PWD):$(PYTHONPATH) \ - MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ + -MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(SIM_BUILD)/simv +define+COCOTB_SIM=1 $(SIM_ARGS) $(EXTRA_ARGS) diff --git a/cocotb/share/makefiles/simulators/Makefile.verilator b/cocotb/share/makefiles/simulators/Makefile.verilator index ec3dbb8e..5c7b1e60 100644 --- a/cocotb/share/makefiles/simulators/Makefile.verilator +++ b/cocotb/share/makefiles/simulators/Makefile.verilator @@ -46,7 +46,7 @@ $(SIM_BUILD)/$(TOPLEVEL): $(SIM_BUILD)/Vtop.mk $(COCOTB_LIBS) $(COCOTB_VPI_LIB) $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) -@rm -f $(COCOTB_RESULTS_FILE) - PYTHONPATH=$(PYTHONPATH) MODULE=$(MODULE) \ + MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $< $(PLUSARGS) @@ -55,7 +55,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) $(COCOTB_LIB debug: $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) -@rm -f $(COCOTB_RESULTS_FILE) - PYTHONPATH=$(PYTHONPATH) MODULE=$(MODULE) \ + MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ gdb --args $< $(PLUSARGS) diff --git a/cocotb/share/makefiles/simulators/Makefile.xcelium b/cocotb/share/makefiles/simulators/Makefile.xcelium index 3ae8ae47..17e63797 100644 --- a/cocotb/share/makefiles/simulators/Makefile.xcelium +++ b/cocotb/share/makefiles/simulators/Makefile.xcelium @@ -108,7 +108,6 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUS -@rm -f $(COCOTB_RESULTS_FILE) set -o pipefail; \ - PYTHONPATH=$(PWD):$(PYTHONPATH) \ MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ \ diff --git a/examples/adder/tests/Makefile b/examples/adder/tests/Makefile index d80b1c5a..f735f58d 100644 --- a/examples/adder/tests/Makefile +++ b/examples/adder/tests/Makefile @@ -37,7 +37,7 @@ else WPWD=$(shell pwd) endif -PYTHONPATH := $(PWD)/../model:$(PYTHONPATH) +export PYTHONPATH := $(PWD)/../model:$(PYTHONPATH) ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES = $(WPWD)/../hdl/adder.v diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index bbb40bee..216ebd41 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -38,7 +38,7 @@ else WPWD=$(PWD) endif -PYTHONPATH := $(PWD)/../cosim:$(PYTHONPATH) +export PYTHONPATH := $(PWD)/../cosim:$(PYTHONPATH) ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES = $(WPWD)/../hdl/endian_swapper.sv diff --git a/tests/test_cases/issue_253/Makefile b/tests/test_cases/issue_253/Makefile index 0ad0652d..4a6bc477 100644 --- a/tests/test_cases/issue_253/Makefile +++ b/tests/test_cases/issue_253/Makefile @@ -44,19 +44,19 @@ $(COCOTB_RESULTS_FILE): @echo "Skipping" notset_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(PYTHONPATH) MODULE=$(MODULE) \ + MODULE=$(MODULE) \ COCOTB_SIM=1 TESTCASE=issue_253_notset TOPLEVEL= \ vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) mkdir -p $@_result && mv results.xml $@_result/ empty_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(PYTHONPATH) MODULE=$(MODULE) \ + MODULE=$(MODULE) \ COCOTB_SIM=1 TESTCASE=issue_253_empty TOPLEVEL="" \ vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) mkdir -p $@_result && mv results.xml $@_result/ no_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) - PYTHONPATH=$(PYTHONPATH) $(LIB_LOAD) MODULE=$(MODULE) \ + $(LIB_LOAD) MODULE=$(MODULE) \ COCOTB_SIM=1 TESTCASE=issue_253_none \ vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) mkdir -p $@_result && mv results.xml $@_result/ From a934cf3f9b10366ecaa0034999141c63f90cc6b7 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Tue, 7 Apr 2020 17:37:47 +0200 Subject: [PATCH 2090/2656] Align ghdl simulators with others based on #1063 --- cocotb/share/makefiles/simulators/Makefile.ghdl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.ghdl b/cocotb/share/makefiles/simulators/Makefile.ghdl index e4378d9a..50d08222 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ghdl +++ b/cocotb/share/makefiles/simulators/Makefile.ghdl @@ -67,10 +67,9 @@ analyse: $(VHDL_SOURCES) $(SIM_BUILD) $(CUSTOM_COMPILE_DEPS) $(COCOTB_RESULTS_FILE): analyse $(COCOTB_LIBS) $(COCOTB_VPI_LIB) $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) - cd $(SIM_BUILD); \ MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ - $(CMD) -r $(GHDL_ARGS) --work=$(RTL_LIBRARY) $(TOPLEVEL) --vpi=$(LIB_DIR)/libcocotbvpi_ghdl.$(LIB_EXT) $(SIM_ARGS) $(PLUSARGS) + $(CMD) -r $(GHDL_ARGS) --workdir=$(SIM_BUILD) --work=$(RTL_LIBRARY) $(TOPLEVEL) --vpi=$(LIB_DIR)/libcocotbvpi_ghdl.$(LIB_EXT) $(SIM_ARGS) $(PLUSARGS) $(call check_for_results_file) From 9b0e1646cdf4df0f8d462185dca4805151b87251 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Tue, 7 Apr 2020 16:33:25 +0200 Subject: [PATCH 2091/2656] Align Aldec simulators with others based on #1063 --- .gitignore | 8 ++++++++ cocotb/share/makefiles/simulators/Makefile.aldec | 9 ++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 7b3e3a79..f9b23872 100644 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,11 @@ tests/test_cases/*/transcript examples/*/tests/modelsim.ini examples/*/tests/transcript *.wlf + +# Riviera +tests/test_cases/*/library.cfg +tests/test_cases/*/dataset.asdb +tests/test_cases/*/compile +examples/*/tests/library.cfg +examples/*/tests/dataset.asdb +examples/*/tests/compile diff --git a/cocotb/share/makefiles/simulators/Makefile.aldec b/cocotb/share/makefiles/simulators/Makefile.aldec index 096dd231..6a6f9216 100644 --- a/cocotb/share/makefiles/simulators/Makefile.aldec +++ b/cocotb/share/makefiles/simulators/Makefile.aldec @@ -63,7 +63,7 @@ ASIM_ARGS += $(SIM_ARGS) # Plusargs need to be passed to ASIM command not vsimsa ASIM_ARGS += $(PLUSARGS) -RTL_LIBRARY ?= work +RTL_LIBRARY ?= $(SIM_BUILD)/work ALOG_ARGS += +define+COCOTB_SIM -dbg -pli libgpi ACOM_ARGS += -dbg @@ -143,10 +143,13 @@ endif $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(COCOTB_VPI_LIB) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) - set -o pipefail; cd $(SIM_BUILD) && GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 $(CMD) $(RUN_ARGS) -do runsim.tcl | tee sim.log + set -o pipefail; GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ + $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 $(CMD) $(RUN_ARGS) -do $(SIM_BUILD)/runsim.tcl | tee $(SIM_BUILD)/sim.log $(call check_for_results_file) clean:: @rm -rf $(SIM_BUILD) + @rm -rf compile + @rm -rf library.cfg + @rm -rf dataset.asdb From 59c00e2295af89c2b1663217f8b97c1477f600f9 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 5 Apr 2020 10:34:15 -0500 Subject: [PATCH 2092/2656] Update coroutine docs to use async/await syntax --- cocotb/triggers.py | 2 +- documentation/source/coroutines.rst | 216 ++++++++++++++++------------ documentation/source/quickstart.rst | 30 ++-- documentation/source/triggers.rst | 11 +- 4 files changed, 139 insertions(+), 120 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 3411fda1..18c1638f 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -724,7 +724,7 @@ class First(_AggregateWaitable): t_ret = await First(t1, t2) .. note:: - When using the old-style :keyword:`yield`-based coroutines, ``t = yield [a, b]`` was another spelling of + In the old-style :ref:`generator-based coroutines `, ``t = yield [a, b]`` was another spelling of ``t = yield First(a, b)``. This spelling is no longer available when using :keyword:`await`-based coroutines. """ diff --git a/documentation/source/coroutines.rst b/documentation/source/coroutines.rst index 1314aa54..4bd6d0df 100644 --- a/documentation/source/coroutines.rst +++ b/documentation/source/coroutines.rst @@ -5,80 +5,50 @@ Coroutines ********** Testbenches built using cocotb use coroutines. While the coroutine is executing -the simulation is paused. The coroutine uses the :keyword:`yield` keyword to -pass control of execution back to the simulator and simulation time can advance -again. +the simulation is paused. The coroutine uses the :keyword:`await` keyword to +block on another coroutine's execution or pass control of execution back to the +simulator, allowing simulation time to advance. -Typically coroutines :keyword:`yield` a :any:`Trigger` object which +Typically coroutines :keyword:`await` a :class:`~cocotb.triggers.Trigger` object which indicates to the simulator some event which will cause the coroutine to be woken when it occurs. For example: .. code-block:: python3 - @cocotb.coroutine - def wait_10ns(): + async def wait_10ns(): cocotb.log.info("About to wait for 10 ns") - yield Timer(10, units='ns') + await Timer(10, units='ns') cocotb.log.info("Simulation time has advanced by 10 ns") -Coroutines may also yield other coroutines: +Coroutines may also :keyword:`await` on other coroutines: .. code-block:: python3 - @cocotb.coroutine - def wait_100ns(): + async def wait_100ns(): for i in range(10): - yield wait_10ns() + await wait_10ns() -Coroutines can return a value, so that they can be used by other coroutines. +Coroutines can :keyword:`return` a value, so that they can be used by other coroutines. .. code-block:: python3 - @cocotb.coroutine - def get_signal(clk, signal): - yield RisingEdge(clk) + async def get_signal(clk, signal): + await RisingEdge(clk) return signal.value - @cocotb.coroutine - def check_signal_changes(dut): - first = yield get_signal(dut.clk, dut.signal) - second = yield get_signal(dut.clk, dut.signal) + async def check_signal_changes(dut): + first = await get_signal(dut.clk, dut.signal) + second = await get_signal(dut.clk, dut.signal) if first == second: raise TestFailure("Signal did not change") -Coroutines may also yield a list of triggers and coroutines to indicate that -execution should resume if *any* of them fires: - -.. code-block:: python3 - - @cocotb.coroutine - def packet_with_timeout(monitor, timeout): - """Wait for a packet but time out if nothing arrives""" - yield [Timer(timeout, units='ns'), RisingEdge(dut.ready)] - - -The trigger that caused execution to resume is passed back to the coroutine, -allowing them to distinguish which trigger fired: - -.. code-block:: python3 - - @cocotb.coroutine - def packet_with_timeout(monitor, timeout): - """Wait for a packet but time out if nothing arrives""" - tout_trigger = Timer(timeout, units='ns') - result = yield [tout_trigger, RisingEdge(dut.ready)] - if result is tout_trigger: - raise TestFailure("Timed out waiting for packet") - - -Coroutines can be forked for parallel operation within a function of that code and -the forked code. +Coroutines can be used with :func:`~cocotb.fork` for concurrent operation. .. code-block:: python3 @cocotb.test() - def test_act_during_reset(dut): + async def test_act_during_reset(dut): """While reset is active, toggle signals""" tb = uart_tb(dut) # "Clock" is a built in class for toggling a clock signal @@ -87,28 +57,28 @@ the forked code. # part of the user-generated "uart_tb" class cocotb.fork(tb.reset_dut(dut.rstn, 20)) - yield Timer(10, units='ns') + await Timer(10, units='ns') print("Reset is still active: %d" % dut.rstn) - yield Timer(15, units='ns') - print("Reset has gone inactive: %d" % dut.rstn) + await Timer(15, units='ns') + await("Reset has gone inactive: %d" % dut.rstn) -Coroutines can be joined to end parallel operation within a function. +Forked coroutines can be used in an :keyword:`await` statement to block until the forked coroutine finishes. .. code-block:: python3 @cocotb.test() - def test_count_edge_cycles(dut, period=1, clocks=6): - cocotb.fork(Clock(dut.clk, period, units='ns').start()) - yield RisingEdge(dut.clk) + async def test_count_edge_cycles(dut, period_ns=1, clocks=6): + cocotb.fork(Clock(dut.clk, period_ns, units='ns').start()) + await RisingEdge(dut.clk) - timer = Timer(period + 10) + timer = Timer(period_ns + 10, 'ns') task = cocotb.fork(count_edges_cycles(dut.clk, clocks)) count = 0 expect = clocks - 1 while True: - result = yield [timer, task.join()] + result = await First(timer, task) if count > expect: raise TestFailure("Task didn't complete in expected time") if result is timer: @@ -117,71 +87,42 @@ Coroutines can be joined to end parallel operation within a function. else: break -Coroutines can be killed before they complete, forcing their completion before +Forked coroutines can be killed before they complete, forcing their completion before they'd naturally end. .. code-block:: python3 @cocotb.test() - def test_different_clocks(dut): + async def test_different_clocks(dut): clk_1mhz = Clock(dut.clk, 1.0, units='us') clk_250mhz = Clock(dut.clk, 4.0, units='ns') clk_gen = cocotb.fork(clk_1mhz.start()) start_time_ns = get_sim_time(units='ns') - yield Timer(1, units='ns') - yield RisingEdge(dut.clk) + await Timer(1, units='ns') + await RisingEdge(dut.clk) edge_time_ns = get_sim_time(units='ns') - # NOTE: isclose is a Python 3.5+ feature if not isclose(edge_time_ns, start_time_ns + 1000.0): raise TestFailure("Expected a period of 1 us") - clk_gen.kill() + clk_gen.kill() # kill clock coroutine here clk_gen = cocotb.fork(clk_250mhz.start()) start_time_ns = get_sim_time(units='ns') - yield Timer(1, units='ns') - yield RisingEdge(dut.clk) + await Timer(1, units='ns') + await RisingEdge(dut.clk) edge_time_ns = get_sim_time(units='ns') - # NOTE: isclose is a Python 3.5+ feature if not isclose(edge_time_ns, start_time_ns + 4.0): raise TestFailure("Expected a period of 4 ns") -.. _async_functions: - -Async functions -=============== -Python 3.5 introduces :keyword:`async` functions, which provide an alternative -syntax. For example: - -.. code-block:: python3 - - @cocotb.coroutine - async def wait_10ns(): - cocotb.log.info("About to wait for 10 ns") - await Timer(10, units='ns') - cocotb.log.info("Simulation time has advanced by 10 ns") - -To wait on a trigger or a nested coroutine, these use :keyword:`await` instead -of :keyword:`yield`. Provided they are decorated with ``@cocotb.coroutine``, -``async def`` functions using :keyword:`await` and regular functions using -:keyword:`yield` can be used interchangeable - the appropriate keyword to use -is determined by which type of function it appears in, not by the -sub-coroutine being called. - -.. versionadded:: 1.4 +.. versionchanged:: 1.4 The :any:`cocotb.coroutine` decorator is no longer necessary for ``async def`` coroutines. ``async def`` coroutines can be used, without the ``@cocotb.coroutine`` decorator, wherever decorated coroutines are accepted, including :keyword:`yield` statements and :any:`cocotb.fork`. -.. note:: - It is not legal to ``await`` a list of triggers as can be done in - ``yield``-based coroutine with ``yield [trig1, trig2]``. Use - ``await First(trig1, trig2)`` instead. - Async generators ----------------- +================ In Python 3.6, a ``yield`` statement within an ``async`` function has a new meaning (rather than being a ``SyntaxError``) which matches the typical meaning @@ -202,3 +143,90 @@ type of generator function that can be iterated with ``async for``: More details on this type of generator can be found in :pep:`525`. + +.. _yield-syntax: + +Generator-based coroutines +========================== + +.. note:: This style is no longer recommended and support may someday be removed. + +Prior to Python 3.5, and the introduction of :keyword:`async` and :keyword:`await`, coroutines were implemented as wrappers around generators. +Coroutine functions would be decorated with :class:`~cocotb.decorators.coroutine` and would use :keyword:`yield` to block on other coroutines or triggers. +You may see existing code that uses this syntax for coroutines, but do not worry, it is compatible with async coroutines. + +Any object that can be used in an :keyword:`await` statement can also be used in a :keyword:`yield` statement while in a generator-based coroutine; +including triggers like :class:`~cocotb.triggers.Timer`. + +.. code-block:: python3 + + @cocotb.coroutine + def simple_clock(signal, half_period, half_period_units): + signal <= 0 + while True: + # in generator-based coroutines triggers are yielded + yield Timer(half_period, half_period_units) + signal <= ~signal + +Likewise, any place that will accept async coroutines will also accept generator-based coroutines; +including :func:`~cocotb.fork`. + +.. code-block:: python3 + + @cocotb.coroutine + def start_clock(clk): + # generator-based coroutines can still be forked + cocotb.fork(simple_clock(clk, 5, units='ns')) + yield RisingEdge(clk) + +Async coroutines can be yielded in generator-based coroutines. + +.. code-block:: python3 + + async def detect_transaction(clk, valid): + await RisingEdge(clk) + while not valid.value: + await RisingEdge(clk) + + @cocotb.coroutine + def monitor(clk, valid, data): + # async coroutines can be yielded + yield detect_transaction(clk, valid) + return data.value + +Generator-based coroutines can also be awaited in async coroutines. + +.. code-block:: python3 + + async def check_incrementing(clk, valid, data): + # generator-based coroutines can be awaited + prev_count = await monitor() + while True: + count = await monitor() + assert count == (prev_count + 1) + prev_count = count + +You may also see syntax like ``yield [trigger_a, trigger_b, ...]``, which is syntactic sugar for :class:`~cocotb.triggers.First`. + +.. code-block:: python3 + + @cocotb.coroutine + def run_for(coro, time, units): + timeout = Timer(time, units='ps') + # block until first trigger fires + yield [timeout, coro] + +Tests can also be generator-based coroutines. +Tests are not required to be decorated with :class:`~cocotb.decorators.coroutine` as the :class:`~cocotb.decorators.test` decorator will handle this case automatically. + +.. code-block:: python3 + + # just need the test decorator + @cocotb.test() + def run_test(dut): + yield start_clock(dut.clk) + checker = check_incrementing( + clk=dut.clk, + valid=dut.valid, + data=dut.cnt) + yield run_for(checker, 1, 'us') diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index 3ae0c965..4c9b1aec 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -310,38 +310,38 @@ We can also cast the signal handle directly to an integer: Parallel and sequential execution --------------------------------- -A :keyword:`yield` will run a function (that must be marked as a "coroutine", see :ref:`coroutines`) -sequentially, i.e. wait for it to complete. -If a coroutine should be run "in the background", i.e. in parallel to other coroutines, -the way to do this is to :func:`~cocotb.fork` it. -The end of such a forked coroutine can be waited on by using :meth:`~cocotb.decorators.RunningCoroutine.join`. +An :keyword:`await` will run an :keyword:`async` coroutine and wait for it to complete. +The called coroutine "blocks" the execution of the current coroutine. +Wrapping the call in :func:`~cocotb.fork` runs the coroutine concurrently, allowing the current coroutine to continue executing. +At any time you can :keyword:`await` the result of the forked coroutine, which will block until the forked coroutine finishes. The following example shows these in action: .. code-block:: python3 - @cocotb.coroutine - def reset_dut(reset_n, duration): + async def reset_dut(reset_n, duration_ns): reset_n <= 0 - yield Timer(duration, units='ns') + await Timer(duration_ns, units='ns') reset_n <= 1 reset_n._log.debug("Reset complete") @cocotb.test() - def parallel_example(dut): + async def parallel_example(dut): reset_n = dut.reset - # This will call reset_dut sequentially # Execution will block until reset_dut has completed - yield reset_dut(reset_n, 500) + await reset_dut(reset_n, 500) dut._log.debug("After reset") - # Call reset_dut in parallel with the 250 ns timer - reset_thread = cocotb.fork(reset_dut(reset_n, 500)) + # Run reset_dut concurrently + reset_thread = cocotb.fork(reset_dut(reset_n, duration_ns=500)) - yield Timer(250, units='ns') + # This timer will complete before the timer in the concurrently executing "reset_thread" + await Timer(250, units='ns') dut._log.debug("During reset (reset_n = %s)" % reset_n.value) # Wait for the other thread to complete - yield reset_thread.join() + await reset_thread dut._log.debug("After reset") + +See :ref:`coroutines` for more examples of what can be done with coroutines. diff --git a/documentation/source/triggers.rst b/documentation/source/triggers.rst index 55cec2b8..6a0f8487 100644 --- a/documentation/source/triggers.rst +++ b/documentation/source/triggers.rst @@ -3,21 +3,12 @@ Triggers ******** Triggers are used to indicate when the cocotb scheduler should resume coroutine execution. -To use a trigger, a coroutine should :keyword:`await` or :keyword:`yield` it. +To use a trigger, a coroutine should :keyword:`await` it. This will cause execution of the current coroutine to pause. When the trigger fires, execution of the paused coroutine will resume:: - @cocotb.coroutine - def coro(): - print("Some time before the edge") - yield RisingEdge(clk) - print("Immediately after the edge") - -Or using the syntax in Python 3.5 onwards: - .. code-block:: python3 - @cocotb.coroutine async def coro(): print("Some time before the edge") await RisingEdge(clk) From 4c99ef423bf7b1fd815333e62c18043ebe311b8d Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Wed, 8 Apr 2020 09:38:58 +0100 Subject: [PATCH 2093/2656] Pin breathe to unbreak CI (#1608) Breathe 4.15 depends on Sphinx 3.0.0, and Sphinx 3.0.0 is incompatible with sphinxcontrib-makedomain (gh-1577) until an upstream fix is merged. I don't know why we need to pin breathe, pip is supposed to work that out for us. Either way, this is harmless as long as we remember to eventually unpin both. Fixes gh-1603 --- documentation/requirements.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/documentation/requirements.txt b/documentation/requirements.txt index d12c7d11..479759a9 100644 --- a/documentation/requirements.txt +++ b/documentation/requirements.txt @@ -4,10 +4,13 @@ # merged and released. Sphinx~=2.1 +# 4.15 depends on sphinx 3.0.0, but for some reason ReadTheDocs tries to install +# it anyway. +breathe < 4.15 + # other dependencies sphinx-rtd-theme cairosvg -breathe sphinxcontrib-makedomain sphinxcontrib-spelling pyenchant From 046d3995e93064f80f0ec39d7a6e76b72e9bcbe6 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Wed, 8 Apr 2020 15:06:37 +0200 Subject: [PATCH 2094/2656] Fix library runpaths for newer Debian (#1606) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Due to changes in Debian (a new default of `--enable-new-dtagshas`) rpath (which now becomes RUNPATH) has to be set on dependent libraries. See https://stackoverflow.com/questions/52018092 for some more context. Co-Authored-By: Eric Wieser --- cocotb/_build_libs.py | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/cocotb/_build_libs.py b/cocotb/_build_libs.py index 0fd5b81f..03478906 100755 --- a/cocotb/_build_libs.py +++ b/cocotb/_build_libs.py @@ -137,16 +137,19 @@ def _gen_import_libs(self, def_dir): ) -def _extra_link_args(lib_name): +def _extra_link_args(lib_name=None, rpath=None): """ - Add linker `install_name` argument on osx to load dependencies from the directory - where vpi/vhpi/fli library is located + Add linker argument to load dependencies from the directory where vpi/vhpi/fli library is located + On osx use `install_name`. + Use `rpath` on all platforms """ - if sys.platform == "darwin": - return ["-Wl,-install_name,@rpath/%s.so" % lib_name] - else: - return [] + args = [] + if sys.platform == "darwin" and lib_name is not None: + args += ["-Wl,-install_name,@rpath/%s.so" % lib_name] + if rpath is not None: + args += ["-Wl,-rpath,%s" % rpath] + return args def _get_python_lib_link(): @@ -201,7 +204,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): os.path.join("cocotb", "libs", "libcocotbutils"), include_dirs=[include_dir], sources=[os.path.join(share_lib_dir, "utils", "cocotb_utils.cpp")], - extra_link_args=_extra_link_args("libcocotbutils"), + extra_link_args=_extra_link_args(lib_name="libcocotbutils", rpath="$ORIGIN"), extra_compile_args=_extra_cxx_compile_args, ) @@ -218,7 +221,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): libraries=["cocotbutils"], library_dirs=python_lib_dirs, sources=[os.path.join(share_lib_dir, "gpi_log", "gpi_logging.cpp")], - extra_link_args=_extra_link_args("libgpilog"), + extra_link_args=_extra_link_args(lib_name="libgpilog", rpath="$ORIGIN"), extra_compile_args=_extra_cxx_compile_args, ) @@ -232,7 +235,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): libraries=[_get_python_lib_link(), "gpilog", "cocotbutils"], library_dirs=python_lib_dirs, sources=[os.path.join(share_lib_dir, "embed", "gpi_embed.cpp")], - extra_link_args=_extra_link_args("libcocotb"), + extra_link_args=_extra_link_args(lib_name="libcocotb", rpath="$ORIGIN"), extra_compile_args=_extra_cxx_compile_args, ) @@ -248,7 +251,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): os.path.join(share_lib_dir, "gpi", "GpiCbHdl.cpp"), os.path.join(share_lib_dir, "gpi", "GpiCommon.cpp"), ], - extra_link_args=_extra_link_args("libgpi"), + extra_link_args=_extra_link_args(lib_name="libgpi", rpath="$ORIGIN"), extra_compile_args=_extra_cxx_compile_args, ) @@ -262,7 +265,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): library_dirs=python_lib_dirs, sources=[os.path.join(share_lib_dir, "simulator", "simulatormodule.cpp")], extra_compile_args=_extra_cxx_compile_args, - extra_link_args=["-Wl,-rpath,$ORIGIN/libs"], + extra_link_args=_extra_link_args(rpath="$ORIGIN/libs"), ) return [libcocotbutils, libgpilog, libcocotb, libgpi, libsim] @@ -281,7 +284,7 @@ def _get_vpi_lib_ext( os.path.join(share_lib_dir, "vpi", "VpiImpl.cpp"), os.path.join(share_lib_dir, "vpi", "VpiCbHdl.cpp"), ], - extra_link_args=["-Wl,-rpath,$ORIGIN"], + extra_link_args=_extra_link_args(rpath="$ORIGIN"), extra_compile_args=_extra_cxx_compile_args, ) @@ -301,7 +304,7 @@ def _get_vhpi_lib_ext( os.path.join(share_lib_dir, "vhpi", "VhpiImpl.cpp"), os.path.join(share_lib_dir, "vhpi", "VhpiCbHdl.cpp"), ], - extra_link_args=["-Wl,-rpath,$ORIGIN"], + extra_link_args=_extra_link_args(rpath="$ORIGIN"), extra_compile_args=_extra_cxx_compile_args, ) @@ -383,7 +386,7 @@ def get_ext(): os.path.join(share_lib_dir, "fli", "FliCbHdl.cpp"), os.path.join(share_lib_dir, "fli", "FliObjHdl.cpp"), ], - extra_link_args=["-Wl,-rpath,$ORIGIN"], + extra_link_args=_extra_link_args(rpath="$ORIGIN"), extra_compile_args=_extra_cxx_compile_args, ) From 0cc07c7d6acc74ee53ecc7ec68f12362236061f8 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 8 Apr 2020 06:08:28 -0700 Subject: [PATCH 2095/2656] Address issues with unexpected MODULE values (#1592) Add supports for stripping unneeded whitespace from the MODULE and HOOKS variables. We now warn if no tests are found. --- cocotb/__init__.py | 9 ++++----- cocotb/regression.py | 5 +++++ tests/test_cases/test_module_var_empty/Makefile | 5 +++++ tests/test_cases/test_module_var_messy/Makefile | 5 +++++ tests/test_cases/test_module_var_messy/test_nothing.py | 1 + tests/test_cases/test_module_without_tests/Makefile | 5 +++++ .../test_cases/test_module_without_tests/test_nothing.py | 1 + 7 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 tests/test_cases/test_module_var_empty/Makefile create mode 100644 tests/test_cases/test_module_var_messy/Makefile create mode 100644 tests/test_cases/test_module_var_messy/test_nothing.py create mode 100644 tests/test_cases/test_module_without_tests/Makefile create mode 100644 tests/test_cases/test_module_without_tests/test_nothing.py diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 8746af1d..6dba6678 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -169,12 +169,11 @@ def _initialise_testbench(root_name): test_str = os.getenv('TESTCASE') hooks_str = os.getenv('COCOTB_HOOKS', '') - if not module_str: - raise ImportError("Environment variables defining the module(s) to " + - "execute not defined. MODULE=\"%s\"" % (module_str)) + if module_str is None: + raise ValueError("Environment variable MODULE, which defines the module(s) to execute, is not defined.") - modules = module_str.split(',') - hooks = hooks_str.split(',') if hooks_str else [] + modules = [s.strip() for s in module_str.split(',') if s.strip()] + hooks = [s.strip() for s in hooks_str.split(',') if s.strip()] global regression_manager diff --git a/cocotb/regression.py b/cocotb/regression.py index c5f3f401..a33014b4 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -144,6 +144,7 @@ def initialise(self): # Test Discovery #################### + have_tests = False for module_name in self._modules: try: self.log.debug("Python Path: " + ",".join(sys.path)) @@ -180,6 +181,10 @@ def initialise(self): for thing in vars(module).values(): if hasattr(thing, "im_test"): self._init_test(thing) + have_tests = True + + if not have_tests: + self.log.warning("No tests were discovered") self._queue.sort(key=lambda test: (test.stage, test._id)) diff --git a/tests/test_cases/test_module_var_empty/Makefile b/tests/test_cases/test_module_var_empty/Makefile new file mode 100644 index 00000000..f8552f8a --- /dev/null +++ b/tests/test_cases/test_module_var_empty/Makefile @@ -0,0 +1,5 @@ +include ../../designs/sample_module/Makefile + +# test MODULE is empty + +MODULE := " " diff --git a/tests/test_cases/test_module_var_messy/Makefile b/tests/test_cases/test_module_var_messy/Makefile new file mode 100644 index 00000000..dd33a79d --- /dev/null +++ b/tests/test_cases/test_module_var_messy/Makefile @@ -0,0 +1,5 @@ +include ../../designs/sample_module/Makefile + +# test MODULE contains leading and trailing separators + +MODULE=" , test_nothing ," diff --git a/tests/test_cases/test_module_var_messy/test_nothing.py b/tests/test_cases/test_module_var_messy/test_nothing.py new file mode 100644 index 00000000..0b9efadd --- /dev/null +++ b/tests/test_cases/test_module_var_messy/test_nothing.py @@ -0,0 +1 @@ +""" This test module is purposefully empty """ diff --git a/tests/test_cases/test_module_without_tests/Makefile b/tests/test_cases/test_module_without_tests/Makefile new file mode 100644 index 00000000..2799c094 --- /dev/null +++ b/tests/test_cases/test_module_without_tests/Makefile @@ -0,0 +1,5 @@ +include ../../designs/sample_module/Makefile + +# test MODULE is set, but there are no tests across any of the modules + +MODULE=test_nothing diff --git a/tests/test_cases/test_module_without_tests/test_nothing.py b/tests/test_cases/test_module_without_tests/test_nothing.py new file mode 100644 index 00000000..0b9efadd --- /dev/null +++ b/tests/test_cases/test_module_without_tests/test_nothing.py @@ -0,0 +1 @@ +""" This test module is purposefully empty """ From f8637192ceb6f2ba0ec81f31fcfdd38a2a14f19e Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Wed, 8 Apr 2020 15:58:37 +0100 Subject: [PATCH 2096/2656] Remove HierarchyObject.__hasattr__ (#1510) This was introduced in 7ce68f1e745b14c1a2e5a07e3ac1845636ea19be as a hack because `__getattr__` contained log statements. This is not a real magic method, and now that we do not log in `__getattr__` there is no need to have a "peek" accessor. --- cocotb/bus.py | 4 +-- cocotb/handle.py | 85 ++++++++++++++++++++++++------------------------ 2 files changed, 44 insertions(+), 45 deletions(-) diff --git a/cocotb/bus.py b/cocotb/bus.py index 3d21f5d6..6570472b 100755 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -95,9 +95,7 @@ def __init__(self, entity, name, signals, optional_signals=[], bus_separator="_" signame += "[{:d}]".format(array_idx) self._entity._log.debug("Signal name {}".format(signame)) - # Attempts to access a signal that doesn't exist will print a - # backtrace so we 'peek' first, slightly un-pythonic - if entity.__hasattr__(signame): + if hasattr(entity, signame): self._add_signal(attr_name, signame) else: self._entity._log.debug("Ignoring optional missing signal " diff --git a/cocotb/handle.py b/cocotb/handle.py index d5e763c4..c1798680 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -225,6 +225,28 @@ def __dir__(self): class HierarchyObject(RegionObject): """Hierarchy objects are namespace/scope objects.""" + def __get_sub_handle_by_name(self, name): + try: + return self._sub_handles[name] + except KeyError: + pass + + # Cache to avoid a call to the simulator if we already know the name is + # invalid. Unclear if we care, but we had this before. + if name in self._invalid_sub_handles: + return None + + new_handle = simulator.get_handle_by_name(self._handle, name) + + if not new_handle: + self._invalid_sub_handles.add(name) + return None + + sub_handle = SimHandle(new_handle, self._child_path(name)) + self._sub_handles[name] = sub_handle + return sub_handle + + def __setattr__(self, name, value): """Provide transparent access to signals via the hierarchy. @@ -233,60 +255,36 @@ def __setattr__(self, name, value): Raise an :exc:`AttributeError` if users attempt to create new members which don't exist in the design. """ + + # private attributes pass through directly if name.startswith("_"): return SimHandleBase.__setattr__(self, name, value) - if self.__hasattr__(name) is not None: - sub = self.__getattr__(name) + + # then try handles + sub = self.__get_sub_handle_by_name(name) + if sub is not None: sub.value = value return + + # compat behavior if name in self._compat_mapping: return SimHandleBase.__setattr__(self, name, value) - raise AttributeError("Attempt to access %s which isn't present in %s" %( - name, self._name)) + + raise AttributeError("%s contains no object named %s" % (self._name, name)) def __getattr__(self, name): """Query the simulator for a object with the specified name and cache the result to build a tree of objects. """ - try: - return self._sub_handles[name] - except KeyError: - pass - - if name.startswith("_"): - return SimHandleBase.__getattr__(self, name) - - new_handle = simulator.get_handle_by_name(self._handle, name) - - if not new_handle: - if name in self._compat_mapping: - return SimHandleBase.__getattr__(self, name) - raise AttributeError("%s contains no object named %s" % (self._name, name)) - - sub_handle = SimHandle(new_handle, self._child_path(name)) - self._sub_handles[name] = sub_handle - return sub_handle - def __hasattr__(self, name): - """Since calling ``hasattr(handle, "something")`` will print out a - backtrace to the log (since usually attempting to access a - non-existent member is an error) we provide a 'peek' function. + handle = self.__get_sub_handle_by_name(name) + if handle is not None: + return handle - We still add the found handle to our dictionary to prevent leaking - handles. - """ - if name in self._sub_handles: - return self._sub_handles[name] - - if name in self._invalid_sub_handles: - return None + if name in self._compat_mapping: + return SimHandleBase.__getattr__(self, name) - new_handle = simulator.get_handle_by_name(self._handle, name) - if new_handle: - self._sub_handles[name] = SimHandle(new_handle, self._child_path(name)) - else: - self._invalid_sub_handles.add(name) - return new_handle + raise AttributeError("%s contains no object named %s" % (self._name, name)) def _id(self, name, extended=True): """Query the simulator for a object with the specified name, @@ -296,10 +294,13 @@ def _id(self, name, extended=True): if extended: name = "\\"+name+"\\" - if self.__hasattr__(name) is not None: - return getattr(self, name) + handle = self.__get_sub_handle_by_name(name) + if handle is not None: + return handle + raise AttributeError("%s contains no object named %s" % (self._name, name)) + class HierarchyArrayObject(RegionObject): """Hierarchy Arrays are containers of Hierarchy Objects.""" From 99daabc21da22257c0461dabb36970c674c76805 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Wed, 8 Apr 2020 17:01:17 +0200 Subject: [PATCH 2097/2656] Cleanup and fix tcl sources paths on Windows (#1596) This removes the need for a `WPWD` variable containing a windows path name. Rather than passing windows paths around the makefile, we now convert them at the last moment when they need to be inserted into a tcl script. This removes a lot of windows-specific cases from the makefiles. Co-Authored-By: Eric Wieser --- cocotb/share/makefiles/Makefile.inc | 7 +++++-- cocotb/share/makefiles/simulators/Makefile.aldec | 8 ++++---- cocotb/share/makefiles/simulators/Makefile.questa | 8 ++++---- examples/adder/tests/Makefile | 10 ++-------- examples/mean/tests/Makefile | 10 ++-------- examples/mixed_language/tests/Makefile | 10 ++-------- examples/ping_tun_tap/tests/Makefile | 6 ------ tests/designs/array_module/Makefile | 8 ++------ tests/designs/avalon_module/Makefile | 8 +------- tests/designs/avalon_streaming_module/Makefile | 8 ++------ tests/designs/basic_hierarchy_module/Makefile | 8 ++------ tests/designs/close_module/Makefile | 8 ++------ tests/designs/multi_dimension_array/Makefile | 8 ++------ tests/designs/plusargs_module/Makefile | 8 ++------ tests/designs/sample_module/Makefile | 8 ++------ tests/designs/uart2bus/Makefile | 8 ++------ tests/designs/vhdl_configurations/Makefile | 8 ++------ tests/designs/viterbi_decoder_axi4s/Makefile | 8 ++------ tests/test_cases/test_iteration_verilog/Makefile | 8 ++------ 19 files changed, 42 insertions(+), 113 deletions(-) diff --git a/cocotb/share/makefiles/Makefile.inc b/cocotb/share/makefiles/Makefile.inc index ce54358c..22c3f601 100644 --- a/cocotb/share/makefiles/Makefile.inc +++ b/cocotb/share/makefiles/Makefile.inc @@ -91,7 +91,10 @@ ifeq ($(OS),Msys) # On Windows there is no support for @rpath/LD_LIBRARY_PATH so we need to set PATH to find cocotb libraries # https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order?redirectedfrom=MSDN#standard-search-order-for-desktop-applications export PATH := $(LIB_DIR):$(PATH) +endif - # For tcl scripts the path to dll has to be in form of C:/User/.. - LIB_DIR := $(shell cygpath -m '$(LIB_DIR)') +ifeq ($(OS),Msys) + to_tcl_path = $(shell cygpath -m $(1) ) +else + to_tcl_path = $(1) endif diff --git a/cocotb/share/makefiles/simulators/Makefile.aldec b/cocotb/share/makefiles/simulators/Makefile.aldec index 6a6f9216..855fc662 100644 --- a/cocotb/share/makefiles/simulators/Makefile.aldec +++ b/cocotb/share/makefiles/simulators/Makefile.aldec @@ -84,13 +84,13 @@ endif GPI_EXTRA:= ifeq ($(TOPLEVEL_LANG),verilog) - GPI_ARGS = -pli $(LIB_DIR)/libcocotbvpi_aldec + GPI_ARGS = -pli $(call to_tcl_path,$(LIB_DIR)/libcocotbvpi_aldec) ifneq ($(VHDL_SOURCES),) GPI_EXTRA = cocotbvhpi_aldec:cocotbvhpi_entry_point endif else ifeq ($(TOPLEVEL_LANG),vhdl) - GPI_ARGS = -loadvhpi $(LIB_DIR)/libcocotbvhpi_aldec:vhpi_startup_routines_bootstrap + GPI_ARGS = -loadvhpi $(call to_tcl_path,$(LIB_DIR)/libcocotbvhpi_aldec):vhpi_startup_routines_bootstrap ifneq ($(VERILOG_SOURCES),) GPI_EXTRA = cocotbvpi_aldec:cocotbvpi_entry_point endif @@ -111,10 +111,10 @@ $(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(VHDL_SOURCES) $(SIM_BUILD) @echo "alib $(RTL_LIBRARY)" >> $@ @echo "set worklib $(RTL_LIBRARY)" >> $@ ifneq ($(VHDL_SOURCES),) - @echo "acom $(ACOM_ARGS) $(VHDL_SOURCES)" >> $@ + @echo "acom $(ACOM_ARGS) $(call to_tcl_path,$(VHDL_SOURCES))" >> $@ endif ifneq ($(VERILOG_SOURCES),) - @echo "alog $(ALOG_ARGS) $(VERILOG_SOURCES)" >> $@ + @echo "alog $(ALOG_ARGS) $(call to_tcl_path,$(VERILOG_SOURCES))" >> $@ endif ifdef SCRIPT_FILE @echo "do $(SCRIPT_FILE)" >> $@ diff --git a/cocotb/share/makefiles/simulators/Makefile.questa b/cocotb/share/makefiles/simulators/Makefile.questa index 6ecb5e3e..b4986e92 100644 --- a/cocotb/share/makefiles/simulators/Makefile.questa +++ b/cocotb/share/makefiles/simulators/Makefile.questa @@ -69,13 +69,13 @@ endif GPI_EXTRA:= ifeq ($(TOPLEVEL_LANG),vhdl) - VSIM_ARGS += -foreign \"cocotb_init $(LIB_DIR)/libcocotbfli_modelsim.$(LIB_EXT)\" + VSIM_ARGS += -foreign \"cocotb_init $(call to_tcl_path,$(LIB_DIR)/libcocotbfli_modelsim.$(LIB_EXT))\" ifneq ($(VERILOG_SOURCES),) GPI_EXTRA = cocotbvpi_modelsim:cocotbvpi_entry_point endif else ifeq ($(TOPLEVEL_LANG),verilog) - VSIM_ARGS += -pli $(LIB_DIR)/libcocotbvpi_modelsim.$(LIB_EXT) + VSIM_ARGS += -pli $(call to_tcl_path,$(LIB_DIR)/libcocotbvpi_modelsim.$(LIB_EXT)) ifneq ($(VHDL_SOURCES),) GPI_EXTRA = cocotbfli_modelsim:cocotbfli_entry_point endif @@ -94,10 +94,10 @@ $(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEP @echo "vmap -c" >> $@ @echo "vmap $(RTL_LIBRARY) $(SIM_BUILD)/$(RTL_LIBRARY)" >> $@ ifneq ($(VHDL_SOURCES),) - @echo "vcom -work $(RTL_LIBRARY) $(VCOM_ARGS) $(VHDL_SOURCES)" >> $@ + @echo "vcom -work $(RTL_LIBRARY) $(VCOM_ARGS) $(call to_tcl_path,$(VHDL_SOURCES))" >> $@ endif ifneq ($(VERILOG_SOURCES),) - @echo "vlog -work $(RTL_LIBRARY) +define+COCOTB_SIM -sv $(VLOG_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES)" >> $@ + @echo "vlog -work $(RTL_LIBRARY) +define+COCOTB_SIM -sv $(VLOG_ARGS) $(EXTRA_ARGS) $(call to_tcl_path,$(VERILOG_SOURCES))" >> $@ endif ifdef SCRIPT_FILE @echo "do $(SCRIPT_FILE)" >> $@ diff --git a/examples/adder/tests/Makefile b/examples/adder/tests/Makefile index f735f58d..3ca98be9 100644 --- a/examples/adder/tests/Makefile +++ b/examples/adder/tests/Makefile @@ -31,18 +31,12 @@ TOPLEVEL_LANG ?= verilog PWD=$(shell pwd) -ifeq ($(OS),Msys) -WPWD=$(shell sh -c 'pwd -W') -else -WPWD=$(shell pwd) -endif - export PYTHONPATH := $(PWD)/../model:$(PYTHONPATH) ifeq ($(TOPLEVEL_LANG),verilog) - VERILOG_SOURCES = $(WPWD)/../hdl/adder.v + VERILOG_SOURCES = $(PWD)/../hdl/adder.v else ifeq ($(TOPLEVEL_LANG),vhdl) - VHDL_SOURCES = $(WPWD)/../hdl/adder.vhdl + VHDL_SOURCES = $(PWD)/../hdl/adder.vhdl else $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") endif diff --git a/examples/mean/tests/Makefile b/examples/mean/tests/Makefile index 8a1ba085..352cef72 100644 --- a/examples/mean/tests/Makefile +++ b/examples/mean/tests/Makefile @@ -13,14 +13,8 @@ TOPLEVEL := mean_sv PWD=$(shell pwd) -ifeq ($(OS),Msys) -WPWD=$(shell sh -c 'pwd -W') -else -WPWD=$(shell pwd) -endif - -VHDL_SOURCES = $(WPWD)/../hdl/mean_pkg.vhd -VHDL_SOURCES += $(WPWD)/../hdl/mean.vhd +VHDL_SOURCES = $(PWD)/../hdl/mean_pkg.vhd +VHDL_SOURCES += $(PWD)/../hdl/mean.vhd ifeq ($(SIM),questa) COMPILE_ARGS = -mixedsvvh diff --git a/examples/mixed_language/tests/Makefile b/examples/mixed_language/tests/Makefile index 4075dd19..f56c46de 100644 --- a/examples/mixed_language/tests/Makefile +++ b/examples/mixed_language/tests/Makefile @@ -15,14 +15,8 @@ TOPLEVEL = endian_swapper_mixed PWD=$(shell pwd) -ifeq ($(OS),Msys) -WPWD=$(shell sh -c 'pwd -W') -else -WPWD=$(shell pwd) -endif - -VERILOG_SOURCES = $(WPWD)/../../endian_swapper/hdl/endian_swapper.sv -VHDL_SOURCES = $(WPWD)/../../endian_swapper/hdl/endian_swapper.vhdl +VERILOG_SOURCES = $(PWD)/../../endian_swapper/hdl/endian_swapper.sv +VHDL_SOURCES = $(PWD)/../../endian_swapper/hdl/endian_swapper.vhdl ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES += $(WPWD)/../hdl/toplevel.sv diff --git a/examples/ping_tun_tap/tests/Makefile b/examples/ping_tun_tap/tests/Makefile index 8e30eea2..c7d9cef1 100644 --- a/examples/ping_tun_tap/tests/Makefile +++ b/examples/ping_tun_tap/tests/Makefile @@ -38,12 +38,6 @@ else TOPLEVEL = icmp_reply -ifeq ($(OS),Msys) -WPWD=$(shell sh -c 'pwd -W') -else -WPWD=$(shell pwd) -endif - PWD=$(shell pwd) VERILOG_SOURCES = $(PWD)/../hdl/icmp_reply.sv diff --git a/tests/designs/array_module/Makefile b/tests/designs/array_module/Makefile index 5c22655c..d270d221 100644 --- a/tests/designs/array_module/Makefile +++ b/tests/designs/array_module/Makefile @@ -31,13 +31,9 @@ TOPLEVEL_LANG ?= verilog TOPLEVEL := array_module -ifeq ($(OS),Msys) -WPWD=$(shell sh -c 'pwd -W') -else -WPWD=$(shell pwd) -endif +PWD=$(shell pwd) -COCOTB?=$(WPWD)/../../.. +COCOTB?=$(PWD)/../../.. ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES = $(COCOTB)/tests/designs/array_module/array_module.sv diff --git a/tests/designs/avalon_module/Makefile b/tests/designs/avalon_module/Makefile index 85338836..f5051a72 100644 --- a/tests/designs/avalon_module/Makefile +++ b/tests/designs/avalon_module/Makefile @@ -39,13 +39,7 @@ else TOPLEVEL := burst_read_master -ifeq ($(OS),Msys) -WPWD=$(shell sh -c 'pwd -W') -else -WPWD=$(shell pwd) -endif - -COCOTB?=$(WPWD)/../../.. +COCOTB?=$(PWD)/../../.. VERILOG_SOURCES = $(COCOTB)/tests/designs/avalon_module/burst_read_master.v diff --git a/tests/designs/avalon_streaming_module/Makefile b/tests/designs/avalon_streaming_module/Makefile index ecb695fc..97cb2203 100644 --- a/tests/designs/avalon_streaming_module/Makefile +++ b/tests/designs/avalon_streaming_module/Makefile @@ -10,13 +10,9 @@ else TOPLEVEL := avalon_streaming -ifeq ($(OS),Msys) -WPWD=$(shell sh -c 'pwd -W') -else -WPWD=$(shell pwd) -endif +PWD=$(shell pwd) -COCOTB?=$(WPWD)/../../.. +COCOTB?=$(PWD)/../../.. VERILOG_SOURCES = $(COCOTB)/tests/designs/avalon_streaming_module/avalon_streaming.sv diff --git a/tests/designs/basic_hierarchy_module/Makefile b/tests/designs/basic_hierarchy_module/Makefile index 1f036090..27bd5136 100644 --- a/tests/designs/basic_hierarchy_module/Makefile +++ b/tests/designs/basic_hierarchy_module/Makefile @@ -14,13 +14,9 @@ else TOPLEVEL := basic_hierarchy_module -ifeq ($(OS),Msys) -WPWD=$(shell sh -c 'pwd -W') -else -WPWD=$(shell pwd) -endif +PWD=$(shell pwd) -COCOTB?=$(WPWD)/../../.. +COCOTB?=$(PWD)/../../.. VERILOG_SOURCES = $(COCOTB)/tests/designs/basic_hierarchy_module/basic_hierarchy_module.v diff --git a/tests/designs/close_module/Makefile b/tests/designs/close_module/Makefile index 9e122d8a..0743720b 100644 --- a/tests/designs/close_module/Makefile +++ b/tests/designs/close_module/Makefile @@ -39,13 +39,9 @@ else TOPLEVEL = close_module -ifeq ($(OS),Msys) -WPWD=$(shell sh -c 'pwd -W') -else -WPWD=$(shell pwd) -endif +PWD=$(shell pwd) -COCOTB?=$(WPWD)/../../.. +COCOTB?=$(PWD)/../../.. VERILOG_SOURCES = $(COCOTB)/tests/designs/close_module/close_module.v diff --git a/tests/designs/multi_dimension_array/Makefile b/tests/designs/multi_dimension_array/Makefile index ee6d3eb5..12b4deb8 100644 --- a/tests/designs/multi_dimension_array/Makefile +++ b/tests/designs/multi_dimension_array/Makefile @@ -39,13 +39,9 @@ clean:: else -ifeq ($(OS),Msys) -WPWD=$(shell sh -c 'pwd -W') -else -WPWD=$(shell pwd) -endif +PWD=$(shell pwd) -COCOTB?=$(WPWD)/../../.. +COCOTB?=$(PWD)/../../.. VERILOG_SOURCES = $(COCOTB)/tests/designs/multi_dimension_array/cocotb_array_pkg.sv \ $(COCOTB)/tests/designs/multi_dimension_array/cocotb_array.sv diff --git a/tests/designs/plusargs_module/Makefile b/tests/designs/plusargs_module/Makefile index 08338d89..482138a1 100644 --- a/tests/designs/plusargs_module/Makefile +++ b/tests/designs/plusargs_module/Makefile @@ -31,13 +31,9 @@ TOPLEVEL_LANG ?= verilog TOPLEVEL := tb_top -ifeq ($(OS),Msys) -WPWD=$(shell sh -c 'pwd -W') -else -WPWD=$(shell pwd) -endif +PWD=$(shell pwd) -COCOTB?=$(WPWD)/../../.. +COCOTB?=$(PWD)/../../.. ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES = $(COCOTB)/tests/designs/plusargs_module/tb_top.v diff --git a/tests/designs/sample_module/Makefile b/tests/designs/sample_module/Makefile index a587e38f..8c3801dd 100644 --- a/tests/designs/sample_module/Makefile +++ b/tests/designs/sample_module/Makefile @@ -31,13 +31,9 @@ TOPLEVEL_LANG ?= verilog TOPLEVEL := sample_module -ifeq ($(OS),Msys) -WPWD=$(shell sh -c 'pwd -W') -else -WPWD=$(shell pwd) -endif +PWD=$(shell pwd) -COCOTB?=$(WPWD)/../../.. +COCOTB?=$(PWD)/../../.. ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES = $(COCOTB)/tests/designs/sample_module/sample_module.sv diff --git a/tests/designs/uart2bus/Makefile b/tests/designs/uart2bus/Makefile index 2148e5a2..cdc6d022 100644 --- a/tests/designs/uart2bus/Makefile +++ b/tests/designs/uart2bus/Makefile @@ -8,13 +8,9 @@ clean:: else -ifeq ($(OS),Msys) -WPWD=$(shell sh -c 'pwd -W') -else -WPWD=$(shell pwd) -endif +PWD=$(shell pwd) -COCOTB?=$(WPWD)/../../.. +COCOTB?=$(PWD)/../../.. SRC_BASE = $(COCOTB)/tests/designs/uart2bus diff --git a/tests/designs/vhdl_configurations/Makefile b/tests/designs/vhdl_configurations/Makefile index 43bf7173..162767a5 100644 --- a/tests/designs/vhdl_configurations/Makefile +++ b/tests/designs/vhdl_configurations/Makefile @@ -16,13 +16,9 @@ else TOPLEVEL = testbench -ifeq ($(OS),Msys) -WPWD=$(shell sh -c 'pwd -W') -else -WPWD=$(shell pwd) -endif +PWD=$(shell pwd) -COCOTB?=$(WPWD)/../../.. +COCOTB?=$(PWD)/../../.. VHDL_SOURCES = $(COCOTB)/tests/designs/vhdl_configurations/dut.vhd ifeq ($(TOPLEVEL_LANG),verilog) diff --git a/tests/designs/viterbi_decoder_axi4s/Makefile b/tests/designs/viterbi_decoder_axi4s/Makefile index 2da9130c..f15159ad 100644 --- a/tests/designs/viterbi_decoder_axi4s/Makefile +++ b/tests/designs/viterbi_decoder_axi4s/Makefile @@ -37,13 +37,9 @@ else TOPLEVEL = dec_viterbi_ent -ifeq ($(OS),Msys) -WPWD=$(shell sh -c 'pwd -W') -else -WPWD=$(shell pwd) -endif +PWD=$(shell pwd) -COCOTB?=$(WPWD)/../../.. +COCOTB?=$(PWD)/../../.. SRC_BASE = $(COCOTB)/tests/designs/viterbi_decoder_axi4s diff --git a/tests/test_cases/test_iteration_verilog/Makefile b/tests/test_cases/test_iteration_verilog/Makefile index 69d5509c..9d70b585 100644 --- a/tests/test_cases/test_iteration_verilog/Makefile +++ b/tests/test_cases/test_iteration_verilog/Makefile @@ -35,13 +35,9 @@ clean:: else -ifeq ($(OS),Msys) -WPWD=$(shell sh -c 'pwd -W') -else -WPWD=$(shell pwd) -endif +PWD=$(shell pwd) -COCOTB?=$(WPWD)/../../.. +COCOTB?=$(PWD)/../../.. VERILOG_SOURCES = $(COCOTB)/examples/endian_swapper/hdl/endian_swapper.sv TOPLEVEL = endian_swapper_sv From b1ac2db9637d3e1dc321d278a420da4ccb01907f Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Wed, 8 Apr 2020 16:27:44 +0100 Subject: [PATCH 2098/2656] Clean up leftover uses of WPWD (#1609) These were missed in #1596, but for some reason CI didn't care. --- examples/endian_swapper/tests/Makefile | 10 ++-------- examples/mean/tests/Makefile | 2 +- examples/mixed_language/tests/Makefile | 4 ++-- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index 216ebd41..bcdb9f16 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -32,19 +32,13 @@ TOPLEVEL_LANG ?= verilog PWD=$(shell pwd) -ifeq ($(OS),Msys) - WPWD=$(shell cygpath -m "$(PWD)") -else - WPWD=$(PWD) -endif - export PYTHONPATH := $(PWD)/../cosim:$(PYTHONPATH) ifeq ($(TOPLEVEL_LANG),verilog) - VERILOG_SOURCES = $(WPWD)/../hdl/endian_swapper.sv + VERILOG_SOURCES = $(PWD)/../hdl/endian_swapper.sv TOPLEVEL = endian_swapper_sv else ifeq ($(TOPLEVEL_LANG),vhdl) - VHDL_SOURCES = $(WPWD)/../hdl/endian_swapper.vhdl + VHDL_SOURCES = $(PWD)/../hdl/endian_swapper.vhdl TOPLEVEL = endian_swapper_vhdl ifneq ($(filter $(SIM),ius xcelium),) SIM_ARGS += -v93 diff --git a/examples/mean/tests/Makefile b/examples/mean/tests/Makefile index 352cef72..82e88f88 100644 --- a/examples/mean/tests/Makefile +++ b/examples/mean/tests/Makefile @@ -20,7 +20,7 @@ ifeq ($(SIM),questa) COMPILE_ARGS = -mixedsvvh endif -VERILOG_SOURCES = $(WPWD)/../hdl/mean_sv.sv +VERILOG_SOURCES = $(PWD)/../hdl/mean_sv.sv MODULE := test_mean diff --git a/examples/mixed_language/tests/Makefile b/examples/mixed_language/tests/Makefile index f56c46de..f95d3325 100644 --- a/examples/mixed_language/tests/Makefile +++ b/examples/mixed_language/tests/Makefile @@ -19,10 +19,10 @@ VERILOG_SOURCES = $(PWD)/../../endian_swapper/hdl/endian_swapper.sv VHDL_SOURCES = $(PWD)/../../endian_swapper/hdl/endian_swapper.vhdl ifeq ($(TOPLEVEL_LANG),verilog) - VERILOG_SOURCES += $(WPWD)/../hdl/toplevel.sv + VERILOG_SOURCES += $(PWD)/../hdl/toplevel.sv else ifeq ($(TOPLEVEL_LANG),vhdl) $(error "VHDL toplevel not yet implemented") - VHDL_SOURCES += $(WPWD)/../hdl/toplevel.vhdl + VHDL_SOURCES += $(PWD)/../hdl/toplevel.vhdl else $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") endif From 0dcc5316990de25419ca224885a8b9949a22d87e Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Sun, 22 Mar 2020 19:43:17 +0100 Subject: [PATCH 2099/2656] Update README with simple example Co-Authored-By: Eric Wieser Co-Authored-By: Colin Marquardt Co-Authored-By: Philipp Wagner Co-Authored-By: Kaleb Barrett --- README.md | 98 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 83 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 4ed8b1f8..a05a5881 100644 --- a/README.md +++ b/README.md @@ -17,29 +17,97 @@ ## Installation -Cocotb can be installed by running `pip install cocotb`. +Cocotb requires: -## Quickstart +- Python 3.5+ +- A C++ compiler +- An HDL simulator (such as [Icarus Verilog](http://iverilog.icarus.com/)) - # Install pre-requisites (waveform viewer optional) - sudo yum install -y iverilog python-devel gtkwave +After installing these dependencies, the latest stable version of cocotb can be installed with pip. - # Checkout git repositories - git clone https://github.com/cocotb/cocotb.git +```command +pip install cocotb +``` - # Install cocotb - pip install ./cocotb +For more details, including how to install a development version of cocotb, see [the documentation](https://docs.cocotb.org/en/latest/quickstart.html#pre-requisites). - # Run the tests... - cd cocotb/examples/endian_swapper/tests - make +## Usage - # View the waveform - gtkwave waveform.vcd +As a first trivial introduction to cocotb, the following example "tests" a flip-flop. +First, we need a hardware design which we can test. For this example, create a file `dff.sv` with SystemVerilog code for a simple [D flip-flop](https://en.wikipedia.org/wiki/Flip-flop_(electronics)#D_flip-flop). You could also use any other language a [cocotb-supported simulator](https://docs.cocotb.org/en/latest/introduction.html) understands, e.g. VHDL. -## Tutorials and examples +```systemverilog +// dff.sv + +`timescale 1us/1ns + +module dff ( + output logic q, + input logic clk, d +); + +always @(posedge clk) begin + q <= d; +end + +endmodule +``` + +An example of a simple randomized cocotb testbench: + +```python +# test_dff.py + +import random +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import FallingEdge + +@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 + + 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 == val, "output q was incorrect on the {}th cycle".format(i) +``` + +A simple Makefile: + +```make +# Makefile + +TOPLEVEL_LANG = verilog +VERILOG_SOURCES = $(shell pwd)/dff.sv +TOPLEVEL = dff +MODULE = test_dff + +include $(shell cocotb-config --makefiles)/Makefile.inc +include $(shell cocotb-config --makefiles)/Makefile.sim +``` + +In order to run the test with Icarus Verilog, execute: + +```command +make SIM=icarus +``` + +[![asciicast](https://asciinema.org/a/317220.svg)](https://asciinema.org/a/317220) + +For more information please see the [cocotb documentation](https://docs.cocotb.org/) and the [wiki](https://github.com/cocotb/cocotb/wiki). + +## Tutorials, examples and related projects * [Endian Swapper tutorial](https://docs.cocotb.org/en/latest/endian_swapper.html) * [Ping using TUN/TAP tutorial](https://docs.cocotb.org/en/latest/ping_tun_tap.html) -* [OpenCores JPEG Encoder example](https://github.com/chiggs/oc_jpegencode/) +* [Cocotb based USB 1.1 test suite for FPGA IP, with testbenches for a variety of open source USB cores](https://github.com/antmicro/usb-test-suite-build) +* [Functional Coverage and Constrained Randomization Extensions for Cocotb](https://github.com/mciepluc/cocotb-coverage) +* [UVM 1.2 port to Python](https://github.com/tpoikela/uvm-python) + +For more related resources please check the [wiki](https://github.com/cocotb/cocotb/wiki/Further-Resources) and the [list of projects depending on cocotb](https://github.com/cocotb/cocotb/network/dependents). From 00dbdeb5614cff89b19af709f97b5e45e60e3dfb Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Mon, 17 Feb 2020 17:06:14 +0100 Subject: [PATCH 2100/2656] remove unused COCOTB_LIBS, COCOTB_VPI_LIB, COCOTB_FLI_LIB, COCOTB_VHPI_LIB COCOTB_LIBS and COCOTB_*_LIB variable ware pointing to cocotb compiled libraries use instead CUSTOM_SIM_DEPS to add dependency on libraries --- cocotb/share/makefiles/Makefile.sim | 3 +++ cocotb/share/makefiles/simulators/Makefile.aldec | 2 +- cocotb/share/makefiles/simulators/Makefile.cvc | 8 ++++---- cocotb/share/makefiles/simulators/Makefile.ghdl | 2 +- cocotb/share/makefiles/simulators/Makefile.icarus | 4 ++-- cocotb/share/makefiles/simulators/Makefile.ius | 4 +--- cocotb/share/makefiles/simulators/Makefile.nvc | 2 +- cocotb/share/makefiles/simulators/Makefile.questa | 6 +----- cocotb/share/makefiles/simulators/Makefile.vcs | 2 +- cocotb/share/makefiles/simulators/Makefile.verilator | 6 +++--- cocotb/share/makefiles/simulators/Makefile.xcelium | 4 +--- tests/test_cases/issue_253/Makefile | 6 +++--- 12 files changed, 22 insertions(+), 27 deletions(-) diff --git a/cocotb/share/makefiles/Makefile.sim b/cocotb/share/makefiles/Makefile.sim index f8e9dcf5..8043f41b 100644 --- a/cocotb/share/makefiles/Makefile.sim +++ b/cocotb/share/makefiles/Makefile.sim @@ -127,6 +127,9 @@ endif # recompilation of the simulation if cocotb is updated. CUSTOM_SIM_DEPS += $(shell find $(COCOTB_PY_DIR)/cocotb/ -name "*.py") +# This triggers a recompilation of the simulation if cocotb library is updated. +CUSTOM_SIM_DEPS += $(shell find $(LIB_DIR)/ -name "*") + PWD := $(shell pwd) include $(COCOTB_SHARE_DIR)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) diff --git a/cocotb/share/makefiles/simulators/Makefile.aldec b/cocotb/share/makefiles/simulators/Makefile.aldec index 855fc662..b80a296f 100644 --- a/cocotb/share/makefiles/simulators/Makefile.aldec +++ b/cocotb/share/makefiles/simulators/Makefile.aldec @@ -140,7 +140,7 @@ endif # Note it's the redirection of the output rather than the 'do' command # that turns on batch mode (i.e. exit on completion/error) -$(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.tcl $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(COCOTB_VPI_LIB) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) +$(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.tcl $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) set -o pipefail; GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ diff --git a/cocotb/share/makefiles/simulators/Makefile.cvc b/cocotb/share/makefiles/simulators/Makefile.cvc index e810f993..b3ab9e26 100644 --- a/cocotb/share/makefiles/simulators/Makefile.cvc +++ b/cocotb/share/makefiles/simulators/Makefile.cvc @@ -58,7 +58,7 @@ ifeq ($(CVC_ITERP),1) endif # Compilation phase -$(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) +$(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(CMD) $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=$(LIB_DIR)/libcocotbvpi_modelsim:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) @@ -67,7 +67,7 @@ $(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(C ifeq ($(CVC_ITERP),1) $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp else - $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) + $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) @@ -75,12 +75,12 @@ endif # Execution phase ifeq ($(CVC_ITERP),1) - debug: $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) + debug: $(CUSTOM_SIM_DEPS) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ gdb --args $(CMD) $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=$(LIB_DIR)/modelsim/libcocotbvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) else - debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) + debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ gdb --args $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) diff --git a/cocotb/share/makefiles/simulators/Makefile.ghdl b/cocotb/share/makefiles/simulators/Makefile.ghdl index 50d08222..b43b0343 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ghdl +++ b/cocotb/share/makefiles/simulators/Makefile.ghdl @@ -64,7 +64,7 @@ analyse: $(VHDL_SOURCES) $(SIM_BUILD) $(CUSTOM_COMPILE_DEPS) $(CMD) -i $(GHDL_ARGS) $(COMPILE_ARGS) --work=$(RTL_LIBRARY) $(VHDL_SOURCES) && \ $(CMD) -m $(GHDL_ARGS) $(COMPILE_ARGS) --work=$(RTL_LIBRARY) $(TOPLEVEL) -$(COCOTB_RESULTS_FILE): analyse $(COCOTB_LIBS) $(COCOTB_VPI_LIB) $(CUSTOM_SIM_DEPS) +$(COCOTB_RESULTS_FILE): analyse $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) MODULE=$(MODULE) \ diff --git a/cocotb/share/makefiles/simulators/Makefile.icarus b/cocotb/share/makefiles/simulators/Makefile.icarus index 76f96a6a..08eb4100 100644 --- a/cocotb/share/makefiles/simulators/Makefile.icarus +++ b/cocotb/share/makefiles/simulators/Makefile.icarus @@ -60,7 +60,7 @@ $(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) # Execution phase -$(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) +$(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) MODULE=$(MODULE) \ @@ -69,7 +69,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $ $(call check_for_results_file) -debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) +debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) MODULE=$(MODULE) \ diff --git a/cocotb/share/makefiles/simulators/Makefile.ius b/cocotb/share/makefiles/simulators/Makefile.ius index 7f3d292f..be85cc86 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ius +++ b/cocotb/share/makefiles/simulators/Makefile.ius @@ -95,9 +95,7 @@ else $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") endif -GPI_LIB = $(COCOTB_VPI_LIB) $(COCOTB_VHPI_LIB) - -$(COCOTB_RESULTS_FILE): $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(GPI_LIB) +$(COCOTB_RESULTS_FILE): $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) set -o pipefail; \ diff --git a/cocotb/share/makefiles/simulators/Makefile.nvc b/cocotb/share/makefiles/simulators/Makefile.nvc index 2a32d109..1fff0fec 100644 --- a/cocotb/share/makefiles/simulators/Makefile.nvc +++ b/cocotb/share/makefiles/simulators/Makefile.nvc @@ -36,7 +36,7 @@ RTL_LIBRARY ?= work analyse: $(VHDL_SOURCES) $(SIM_BUILD) $(CUSTOM_COMPILE_DEPS) cd $(SIM_BUILD) && $(CMD) --work=$(RTL_LIBRARY) -a $(VHDL_SOURCES) $(COMPILE_ARGS) $(EXTRA_ARGS) -$(COCOTB_RESULTS_FILE): analyse $(COCOTB_LIBS) $(COCOTB_VHPI_LIB) $(CUSTOM_SIM_DEPS) +$(COCOTB_RESULTS_FILE): analyse $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) cd $(SIM_BUILD) && \ diff --git a/cocotb/share/makefiles/simulators/Makefile.questa b/cocotb/share/makefiles/simulators/Makefile.questa index b4986e92..8cfa83e0 100644 --- a/cocotb/share/makefiles/simulators/Makefile.questa +++ b/cocotb/share/makefiles/simulators/Makefile.questa @@ -114,15 +114,11 @@ else @echo "quit" >> $@ endif -INT_LIBS := $(COCOTB_VPI_LIB) $(COCOTB_FLI_LIB) - ifneq ($(ARCH),i686) CMD += -64 endif -# Depending on the version of modelsim the interfaces that it supports can change -# Append or remove values to INT_LIBS depending on your license -$(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.do $(COCOTB_LIBS) $(INT_LIBS) +$(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.do -@rm -f $(COCOTB_RESULTS_FILE) set -o pipefail; MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ diff --git a/cocotb/share/makefiles/simulators/Makefile.vcs b/cocotb/share/makefiles/simulators/Makefile.vcs index 52e18afd..36d75f57 100644 --- a/cocotb/share/makefiles/simulators/Makefile.vcs +++ b/cocotb/share/makefiles/simulators/Makefile.vcs @@ -68,7 +68,7 @@ $(SIM_BUILD)/pli.tab : echo "acc+=rw,wn:*" > $@ # Compilation phase -$(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(COCOTB_LIBS) $(COCOTB_VPI_LIB) $(CUSTOM_COMPILE_DEPS) +$(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(CUSTOM_COMPILE_DEPS) cd $(SIM_BUILD) && \ TOPLEVEL=$(TOPLEVEL) \ $(CMD) -top $(TOPLEVEL) $(PLUSARGS) +acc+1 +vpi -P pli.tab +define+COCOTB_SIM=1 -sverilog \ diff --git a/cocotb/share/makefiles/simulators/Makefile.verilator b/cocotb/share/makefiles/simulators/Makefile.verilator index 5c7b1e60..2139423b 100644 --- a/cocotb/share/makefiles/simulators/Makefile.verilator +++ b/cocotb/share/makefiles/simulators/Makefile.verilator @@ -40,10 +40,10 @@ $(SIM_BUILD)/Vtop.mk: $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(COCOTB_SHARE_D $(CMD) -cc --exe -Mdir $(SIM_BUILD) -DCOCOTB_SIM=1 --top-module $(TOPLEVEL) $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) $(COCOTB_SHARE_DIR)/lib/verilator/verilator.cpp # Compilation phase -$(SIM_BUILD)/$(TOPLEVEL): $(SIM_BUILD)/Vtop.mk $(COCOTB_LIBS) $(COCOTB_VPI_LIB) +$(SIM_BUILD)/$(TOPLEVEL): $(SIM_BUILD)/Vtop.mk CPPFLAGS="$(SIM_BUILD_FLAGS)" make -C $(SIM_BUILD) -f Vtop.mk -$(COCOTB_RESULTS_FILE): $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) +$(COCOTB_RESULTS_FILE): $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) MODULE=$(MODULE) \ @@ -52,7 +52,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) $(COCOTB_LIB $(call check_for_results_file) -debug: $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) +debug: $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) MODULE=$(MODULE) \ diff --git a/cocotb/share/makefiles/simulators/Makefile.xcelium b/cocotb/share/makefiles/simulators/Makefile.xcelium index 17e63797..97b5c37c 100644 --- a/cocotb/share/makefiles/simulators/Makefile.xcelium +++ b/cocotb/share/makefiles/simulators/Makefile.xcelium @@ -102,9 +102,7 @@ else $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") endif -GPI_LIB = $(COCOTB_VPI_LIB) $(COCOTB_VHPI_LIB) - -$(COCOTB_RESULTS_FILE): $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(GPI_LIB) +$(COCOTB_RESULTS_FILE): $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) set -o pipefail; \ diff --git a/tests/test_cases/issue_253/Makefile b/tests/test_cases/issue_253/Makefile index 4a6bc477..379505f1 100644 --- a/tests/test_cases/issue_253/Makefile +++ b/tests/test_cases/issue_253/Makefile @@ -43,19 +43,19 @@ all: empty_top_level no_top_level notset_top_level $(COCOTB_RESULTS_FILE): @echo "Skipping" -notset_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) +notset_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) MODULE=$(MODULE) \ COCOTB_SIM=1 TESTCASE=issue_253_notset TOPLEVEL= \ vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) mkdir -p $@_result && mv results.xml $@_result/ -empty_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) +empty_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) MODULE=$(MODULE) \ COCOTB_SIM=1 TESTCASE=issue_253_empty TOPLEVEL="" \ vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) mkdir -p $@_result && mv results.xml $@_result/ -no_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(COCOTB_LIBS) $(COCOTB_VPI_LIB) +no_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(LIB_LOAD) MODULE=$(MODULE) \ COCOTB_SIM=1 TESTCASE=issue_253_none \ vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) From 68662cc7cfe053953994d0b36d1c0e8aa74aff84 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 19 Mar 2020 14:45:17 +0000 Subject: [PATCH 2101/2656] Make `BinaryValue.buff` bytes not str. This makes the behavior consistent with python 2, where the two types were equivalent. `str` is not appropriate for storing binary data. The changes to the avalon code were needed for it to pass. --- cocotb/binary.py | 59 +++++++++++-------- cocotb/drivers/avalon.py | 8 ++- cocotb/monitors/avalon.py | 6 +- .../source/newsfragments/1514.change.rst | 7 +++ tests/pytest/test_binary_value.py | 8 +-- .../test_avalon_stream/test_avalon_stream.py | 1 - 6 files changed, 54 insertions(+), 35 deletions(-) create mode 100644 documentation/source/newsfragments/1514.change.rst diff --git a/cocotb/binary.py b/cocotb/binary.py index afabfd5b..053b8690 100755 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -84,8 +84,8 @@ class BinaryValue: >>> vec.integer = 42 >>> print(vec.binstr) 101010 - >>> print(repr(vec.buff)) - '*' + >>> print(vec.buff) + b'*' """ _resolve_to_0 = "-lL" # noqa @@ -143,22 +143,30 @@ def __init__(self, value=None, n_bits=None, bigEndian=True, def assign(self, value): """Decides how best to assign the value to the vector. - We possibly try to be a bit too clever here by first of - all trying to assign the raw string as a :attr:`BinaryValue.binstr`, - however if the string contains any characters that aren't - ``0``, ``1``, ``X`` or ``Z`` - then we interpret the string as a binary buffer. + Picks from the type of its argument whether to set :attr:`integer`, + :attr:`binstr`, or :attr:`buff`. Args: - value (str or int or long): The value to assign. + value (str or int or bytes): The value to assign. + + .. versionchanged:: 1.4 + + This no longer falls back to setting :attr:`buff` if a :class:`str` + containining any characters that aren't ``0``, ``1``, ``X`` or ``Z`` + is used, since :attr:`buff` now accepts only :class:`bytes`. Instead, + an error is raised. """ if isinstance(value, int): - self.value = value + self.integer = value elif isinstance(value, str): - try: - self.binstr = value - except ValueError: - self.buff = value + self.binstr = value + elif isinstance(value, bytes): + self.buff = value + else: + raise TypeError( + "value must be int, str, or bytes, not {!r}" + .format(type(value).__name__) + ) def _convert_to_unsigned(self, x): x = bin(x) @@ -323,36 +331,41 @@ def is_resolvable(self): return not any(char in self._str for char in BinaryValue._resolve_to_error) @property - def buff(self): - """Attribute :attr:`buff` represents the value as a binary string buffer. + def buff(self) -> bytes: + r"""The value as a binary string buffer. - >>> BinaryValue("0100000100101111").buff == "\x41\x2F" + >>> BinaryValue("01000001" + "00101111").buff == b"\x41\x2F" True + + .. versionchanged:: 1.4 + This changed from :class:`str` to :class:`bytes`. + Note that for older versions used with Python 2 these types were + indistinguishable. """ bits = resolve(self._str) if len(bits) % 8: bits = "0" * (8 - len(bits) % 8) + bits - buff = "" + buff = [] while bits: byte = bits[:8] bits = bits[8:] val = int(byte, 2) if self.big_endian: - buff += chr(val) + buff += [val] else: - buff = chr(val) + buff - return buff + buff = [val] + buff + return bytes(buff) @buff.setter - def buff(self, val): + def buff(self, val: bytes): self._str = "" for char in val: if self.big_endian: - self._str += "{0:08b}".format(ord(char)) + self._str += "{0:08b}".format(char) else: - self._str = "{0:08b}".format(ord(char)) + self._str + self._str = "{0:08b}".format(char) + self._str self._adjust() def _adjust(self): diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index e548cd82..2057eac5 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -675,7 +675,7 @@ def _wait_ready(self): @coroutine def _send_string(self, string, sync=True, channel=None): """Args: - string (str): A string of bytes to send over the bus. + string (bytes): A string of bytes to send over the bus. channel (int): Channel to send the data on. """ # Avoid spurious object creation by recycling @@ -747,7 +747,7 @@ def _send_string(self, string, sync=True, channel=None): self.bus.endofpacket <= 1 if self.use_empty: self.bus.empty <= bus_width - len(string) - string = "" + string = b"" else: string = string[bus_width:] @@ -831,11 +831,13 @@ def _driver_send(self, pkt, sync=True, channel=None): """ # Avoid spurious object creation by recycling - if isinstance(pkt, str): + if isinstance(pkt, bytes): self.log.debug("Sending packet of length %d bytes", len(pkt)) self.log.debug(hexdump(pkt)) yield self._send_string(pkt, sync=sync, channel=channel) self.log.debug("Successfully sent packet of length %d bytes", len(pkt)) + elif isinstance(pkt, str): + raise TypeError("pkt must be a bytestring, not a unicode string") else: if channel is not None: self.log.warning("%s is ignoring channel=%d because pkt is an iterable", self.name, channel) diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index 7d15cdbc..b664c102 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -153,7 +153,7 @@ def _monitor_recv(self): # Avoid spurious object creation by recycling clkedge = RisingEdge(self.clock) rdonly = ReadOnly() - pkt = "" + pkt = b"" in_pkt = False invalid_cyclecount = 0 channel = None @@ -177,7 +177,7 @@ def valid(): if pkt: raise AvalonProtocolError("Duplicate start-of-packet received on %s" % str(self.bus.startofpacket)) - pkt = "" + pkt = b"" in_pkt = True if not in_pkt: @@ -222,7 +222,7 @@ def valid(): self._recv({"data": pkt, "channel": channel}) else: self._recv(pkt) - pkt = "" + pkt = b"" in_pkt = False channel = None else: diff --git a/documentation/source/newsfragments/1514.change.rst b/documentation/source/newsfragments/1514.change.rst new file mode 100644 index 00000000..a220f2f1 --- /dev/null +++ b/documentation/source/newsfragments/1514.change.rst @@ -0,0 +1,7 @@ +Various binary representations have changed type from :class:`str` to :class:`bytes`. These include: + +* :attr:`cocotb.binary.BinaryValue.buff`, which as a consequence means :meth:`cocotb.binary.BinaryValue.assign` no longer accepts malformed ``10xz``-style :class:`str`\ s (which were treated as binary). +* The packets produced by the :class:`~cocotb.drivers.avalon.AvalonSTPkts`. + +Code working with these objects may find it needs to switch from creating :class:`str` objects like ``"this"`` to :class:`bytes` objects like ``b"this"``. +This change is a consequence of the move to Python 3. diff --git a/tests/pytest/test_binary_value.py b/tests/pytest/test_binary_value.py index 2df84acc..68b2e211 100644 --- a/tests/pytest/test_binary_value.py +++ b/tests/pytest/test_binary_value.py @@ -94,11 +94,9 @@ def test_init_little_endian_twos_comp(): temp_bin = BinaryValue(value="11111111111111111111110000101100", bigEndian=False, binaryRepresentation=BinaryRepresentation.UNSIGNED) - # Silently fails to set value when another BinaryValue object is passed in - bin6 = BinaryValue(value=temp_bin, n_bits=32, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) - assert bin6._str == "" - assert bin6.binstr == "" - assert bin6.n_bits == 32 + # Illegal to construct from another BinaryValue (used to silently fail) + with pytest.raises(TypeError): + BinaryValue(value=temp_bin, n_bits=32, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) bin7 = BinaryValue(value=temp_bin.binstr, n_bits=32, bigEndian=False, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) diff --git a/tests/test_cases/test_avalon_stream/test_avalon_stream.py b/tests/test_cases/test_avalon_stream/test_avalon_stream.py index d3e6c6a1..bf288b92 100644 --- a/tests/test_cases/test_avalon_stream/test_avalon_stream.py +++ b/tests/test_cases/test_avalon_stream/test_avalon_stream.py @@ -43,7 +43,6 @@ def initialise(self): @cocotb.coroutine def send_data(self, data): exp_data = struct.pack("B",data) - exp_data = exp_data.decode('ascii') self.expected_output.append(exp_data) yield self.stream_in.send(data) From 1c6e7f1459173399640ce290c576fda28fc17fb1 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 9 Apr 2020 15:31:20 +0100 Subject: [PATCH 2102/2656] Fix the byte generator to actually return bytes. Also add type annotations, for clarity. --- cocotb/generators/byte.py | 40 +++++++++++-------- .../source/newsfragments/1514.change.rst | 1 + 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/cocotb/generators/byte.py b/cocotb/generators/byte.py index 546f99f5..6d34b7f4 100644 --- a/cocotb/generators/byte.py +++ b/cocotb/generators/byte.py @@ -30,40 +30,48 @@ """ Collection of generators for creating byte streams + + Note that on Python 3, individual bytes are represented with integers. """ import random +import itertools from cocotb.decorators import public +from typing import Iterator @public -def get_bytes(nbytes, generator): - """Get nbytes from generator""" - result = "" - for i in range(nbytes): - result += next(generator) - return result +def get_bytes(nbytes: int, generator: Iterator[int]) -> bytes: + """Get nbytes from generator + + .. versionchanged:: 1.4.0 + This now returns :type:`bytes` not :type:`str`. """ + return bytes(next(generator) for i in range(nbytes)) @public -def random_data(): - """Random bytes""" +def random_data() -> Iterator[int]: + r"""Random bytes + + .. versionchanged:: 1.4.0 + This now returns integers not single-character :type:`str`\ s. """ while True: - yield chr(random.randint(0, 255)) + yield random.randrange(256) @public -def incrementing_data(increment=1): - """Incrementing bytes""" +def incrementing_data(increment=1) -> Iterator[int]: + r"""Incrementing bytes + + .. versionchanged:: 1.4.0 + This now returns integers not single-character :type:`str`\ s. """ val = 0 while True: - yield chr(val) + yield val val += increment val = val & 0xFF @public -def repeating_bytes(pattern="\x00"): +def repeating_bytes(pattern : bytes = b"\x00") -> Iterator[int]: """Repeat a pattern of bytes""" - while True: - for byte in pattern: - yield byte + return itertools.cycle(pattern) diff --git a/documentation/source/newsfragments/1514.change.rst b/documentation/source/newsfragments/1514.change.rst index a220f2f1..f05b4706 100644 --- a/documentation/source/newsfragments/1514.change.rst +++ b/documentation/source/newsfragments/1514.change.rst @@ -1,6 +1,7 @@ Various binary representations have changed type from :class:`str` to :class:`bytes`. These include: * :attr:`cocotb.binary.BinaryValue.buff`, which as a consequence means :meth:`cocotb.binary.BinaryValue.assign` no longer accepts malformed ``10xz``-style :class:`str`\ s (which were treated as binary). +* The objects produced by :mod:`cocotb.generators.byte`, which means that single bytes are represented by :class:`int` instead of 1-character :class:`str`\ s. * The packets produced by the :class:`~cocotb.drivers.avalon.AvalonSTPkts`. Code working with these objects may find it needs to switch from creating :class:`str` objects like ``"this"`` to :class:`bytes` objects like ``b"this"``. From bb1440da201ec6975fe2d22a87106ace96f21226 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 8 Apr 2020 23:02:54 +0200 Subject: [PATCH 2103/2656] Deprecate VERILATOR_TRACE with doc reference The documentation itself is handled by #1607. Refs #1495. --- cocotb/share/makefiles/Makefile.deprecations | 6 +++++- documentation/source/newsfragments/1495.removal.rst | 10 +++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/cocotb/share/makefiles/Makefile.deprecations b/cocotb/share/makefiles/Makefile.deprecations index 3d1e3bce..5a7736a4 100644 --- a/cocotb/share/makefiles/Makefile.deprecations +++ b/cocotb/share/makefiles/Makefile.deprecations @@ -3,5 +3,9 @@ # SPDX-License-Identifier: BSD-3-Clause ifdef COCOTB_NVC_TRACE - $(warning COCOTB_NVC_TRACE is deprecated, see the "Simulator Support" section in the documentation.) + $(warning COCOTB_NVC_TRACE is deprecated, see the "Simulator Support" section in the documentation.) +endif + +ifdef VERILATOR_TRACE + $(warning VERILATOR_TRACE is deprecated, see the "Simulator Support" section in the documentation.) endif diff --git a/documentation/source/newsfragments/1495.removal.rst b/documentation/source/newsfragments/1495.removal.rst index a776f6d8..e7f18879 100644 --- a/documentation/source/newsfragments/1495.removal.rst +++ b/documentation/source/newsfragments/1495.removal.rst @@ -1,5 +1,5 @@ -The makefile variable -:make:var:`COCOTB_NVC_TRACE` -that was not supported for all simulators has been deprecated. -Using it prints a deprecation warning and points to documentation (:ref:`simulator-support`) -explaining how to replace it by other means. +The makefile variables +:make:var:`COCOTB_NVC_TRACE`, :make:var:`VERILATOR_TRACE` +that were not supported for all simulators have been deprecated. +Using them prints a deprecation warning and points to the documentation section +:ref:`simulator-support` explaining how to get the same effect by other means. From b86739e01e0423b1674b3962b8d2cb16661ef12c Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 5 Apr 2020 12:36:33 -0500 Subject: [PATCH 2104/2656] Move tests related to deprecation from test_cocotb to test_deprecation --- tests/test_cases/test_cocotb/test_cocotb.py | 45 +------------------ .../test_cases/test_cocotb/test_deprecated.py | 28 +++++++++++- 2 files changed, 28 insertions(+), 45 deletions(-) diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 17df22a0..5246f073 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -27,13 +27,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import contextlib import logging import re -import sys import textwrap import traceback -import warnings from fractions import Fraction from decimal import Decimal from math import isclose @@ -49,34 +46,9 @@ ReadOnly, ReadWrite, ClockCycles, NextTimeStep, NullTrigger, Combine, Event, First, Trigger, Lock) from cocotb.clock import Clock -from cocotb.result import ( - TestFailure, TestError, TestSuccess, raise_error, create_error -) +from cocotb.result import TestFailure, TestError from cocotb.utils import get_sim_time from cocotb.outcomes import Value, Error -from cocotb.binary import BinaryValue - - -@contextlib.contextmanager -def assert_deprecated(): - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - yield - if len(w) == 1 and issubclass(w[-1].category, DeprecationWarning): - return - raise AssertionError( - "Expected exactly one DeprecationWarning, got {}".format(w) - ) - - -@contextlib.contextmanager -def assert_raises(exc_type): - try: - yield - except exc_type: - pass - else: - raise AssertionError("{} was not raised".format(exc_type.__name__)) # Tests relating to providing meaningful errors if we forget to use the @@ -1169,21 +1141,6 @@ def prime(self, callback): raise TestFailure -@cocotb.test() -def test_create_error_deprecated(dut): - yield Timer(1) - with assert_deprecated(): - e = create_error(Timer(1), "A test exception") - - -@cocotb.test() -def test_raise_error_deprecated(dut): - yield Timer(1) - with assert_deprecated(): - with assert_raises(TestError): - raise_error(Timer(1), "A test exception") - - @cocotb.test(expect_fail=True) def test_assertion_is_failure(dut): yield Timer(1) diff --git a/tests/test_cases/test_cocotb/test_deprecated.py b/tests/test_cases/test_cocotb/test_deprecated.py index a2bc69b7..f70bc7d4 100644 --- a/tests/test_cases/test_cocotb/test_deprecated.py +++ b/tests/test_cases/test_cocotb/test_deprecated.py @@ -1,3 +1,6 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause import cocotb import warnings from contextlib import contextmanager @@ -13,7 +16,17 @@ def assert_deprecated(): yield warns # note: not a cocotb yield, but a contextlib one! finally: assert len(warns) == 1 - assert issubclass(warns[0].category, DeprecationWarning) + assert issubclass(warns[0].category, DeprecationWarning), "Expected DeprecationWarning" + + +@contextmanager +def assert_raises(exc_type): + try: + yield + except exc_type: + pass + else: + raise AssertionError("{} was not raised".format(exc_type.__name__)) @cocotb.test() @@ -36,3 +49,16 @@ async def test_unicode_handle_assignment_deprecated(dut): with assert_deprecated() as warns: dut.string_input_port <= "Bad idea" assert "bytes" in str(warns[0].message) + + +@cocotb.test() +async def test_create_error_deprecated(dut): + with assert_deprecated(): + _ = cocotb.result.create_error(cocotb.triggers.Timer(1), "A test exception") + + +@cocotb.test() +async def test_raise_error_deprecated(dut): + with assert_deprecated(): + with assert_raises(cocotb.result.TestError): + cocotb.result.raise_error(cocotb.triggers.Timer(1), "A test exception") From e20de547f1ed24d2966708fb27c255999f433fce Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 6 Apr 2020 20:28:56 -0500 Subject: [PATCH 2105/2656] Move tests related to Lock and Event into their own module --- tests/test_cases/test_cocotb/Makefile | 9 +++- tests/test_cases/test_cocotb/test_cocotb.py | 45 ---------------- .../test_synchronization_primitives.py | 53 +++++++++++++++++++ 3 files changed, 60 insertions(+), 47 deletions(-) create mode 100644 tests/test_cases/test_cocotb/test_synchronization_primitives.py diff --git a/tests/test_cases/test_cocotb/Makefile b/tests/test_cases/test_cocotb/Makefile index 6c34d7c8..42a84335 100644 --- a/tests/test_cases/test_cocotb/Makefile +++ b/tests/test_cases/test_cocotb/Makefile @@ -29,8 +29,13 @@ include ../../designs/sample_module/Makefile -MODULE := test_cocotb,test_deprecated,test_doctests +MODULE := "\ + test_cocotb,\ + test_deprecated,\ + test_doctests,\ + test_synchronization_primitives,\ + " ifeq ($(shell python -c "import sys; print(sys.version_info >= (3, 6))"), "True") -MODULE += ,test_async_generators +MODULE += test_async_generators, endif diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 5246f073..e57f6f00 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -1378,51 +1378,6 @@ async def test_ordering_1(dut): assert val == 2 -@cocotb.test() -async def test_trigger_lock(dut): - """ - Simple test that checks to see if context management is kept. The - resource value is checked at certain points if it equals the expected - amount, which is easily predictable if the context management is working. - """ - resource = 0 - lock = Lock() - - async def co(): - nonlocal resource - await Timer(10, "ns") - async with lock: - for i in range(4): - await Timer(10, "ns") - resource += 1 - - cocotb.fork(co()) - async with lock: - for i in range(4): - resource += 1 - await Timer(10, "ns") - assert resource == 4 - await Timer(10, "ns") - async with lock: - assert resource == 8 - - -@cocotb.test(timeout_time=100, timeout_unit="ns") -async def test_except_lock(dut): - """ - Checks to see if exceptions cause the lock to be - released. - """ - lock = Lock() - try: - async with lock: - raise RuntimeError() - except RuntimeError: - pass - async with lock: - pass - - # strings are not supported on Icarus @cocotb.test(skip=cocotb.SIM_NAME.lower().startswith("icarus")) async def test_string_handle_takes_bytes(dut): diff --git a/tests/test_cases/test_cocotb/test_synchronization_primitives.py b/tests/test_cases/test_cocotb/test_synchronization_primitives.py new file mode 100644 index 00000000..79455e4f --- /dev/null +++ b/tests/test_cases/test_cocotb/test_synchronization_primitives.py @@ -0,0 +1,53 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause +""" +Tests for synchronization primitives like Lock and Event +""" +import cocotb +from cocotb.triggers import Lock, Timer + + +@cocotb.test() +async def test_trigger_lock(dut): + """ + Simple test that checks to see if context management is kept. The + resource value is checked at certain points if it equals the expected + amount, which is easily predictable if the context management is working. + """ + resource = 0 + lock = Lock() + + async def co(): + nonlocal resource + await Timer(10, "ns") + async with lock: + for i in range(4): + await Timer(10, "ns") + resource += 1 + + cocotb.fork(co()) + async with lock: + for i in range(4): + resource += 1 + await Timer(10, "ns") + assert resource == 4 + await Timer(10, "ns") + async with lock: + assert resource == 8 + + +@cocotb.test(timeout_time=100, timeout_unit="ns") +async def test_except_lock(dut): + """ + Checks to see if exceptions cause the lock to be + released. + """ + lock = Lock() + try: + async with lock: + raise RuntimeError() + except RuntimeError: + pass + async with lock: + pass From 44082648c187200b4d008e2389fca1b2f1da2552 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 6 Apr 2020 21:56:21 -0500 Subject: [PATCH 2106/2656] Move tests for First and Combine into their own module --- tests/test_cases/test_cocotb/Makefile | 1 + tests/test_cases/test_cocotb/test_cocotb.py | 138 ---------------- .../test_concurrency_primitives.py | 147 ++++++++++++++++++ 3 files changed, 148 insertions(+), 138 deletions(-) create mode 100644 tests/test_cases/test_cocotb/test_concurrency_primitives.py diff --git a/tests/test_cases/test_cocotb/Makefile b/tests/test_cases/test_cocotb/Makefile index 42a84335..693e3f9a 100644 --- a/tests/test_cases/test_cocotb/Makefile +++ b/tests/test_cases/test_cocotb/Makefile @@ -34,6 +34,7 @@ MODULE := "\ test_deprecated,\ test_doctests,\ test_synchronization_primitives,\ + test_concurrency_primitives,\ " ifeq ($(shell python -c "import sys; print(sys.version_info >= (3, 6))"), "True") diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index e57f6f00..bf2dcb39 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -890,40 +890,6 @@ def raise_soon(): yield _check_traceback(raise_soon(), ValueError, expected) -@cocotb.test() -def test_exceptions_first(dut): - """ Test exception propagation via cocotb.triggers.First """ - @cocotb.coroutine - def raise_inner(): - yield Timer(10) - raise ValueError('It is soon now') - - @cocotb.coroutine - def raise_soon(): - yield Timer(1) - yield cocotb.triggers.First(raise_inner()) - - # it's ok to change this value if the traceback changes - just make sure - # that when changed, it doesn't become harder to read. - expected = textwrap.dedent(r""" - Traceback \(most recent call last\): - File ".*test_cocotb\.py", line \d+, in _check_traceback - yield running_coro - File ".*test_cocotb\.py", line \d+, in raise_soon - yield cocotb\.triggers\.First\(raise_inner\(\)\) - File ".*triggers\.py", line \d+, in _wait - return await first_trigger[^\n]* - File ".*triggers.py", line \d+, in __await__ - return \(yield self\) - File ".*triggers.py", line \d+, in __await__ - return \(yield self\) - File ".*test_cocotb\.py", line \d+, in raise_inner - raise ValueError\('It is soon now'\) - ValueError: It is soon now""").strip() - - yield _check_traceback(raise_soon(), ValueError, expected) - - @cocotb.test() def test_stack_overflow(dut): """ @@ -972,20 +938,6 @@ def immediate_exception(): raise TestFailure("Exception was not raised") -@cocotb.test() -def test_combine(dut): - """ Test the Combine trigger. """ - # gh-852 - - @cocotb.coroutine - def do_something(delay): - yield Timer(delay) - - crs = [cocotb.fork(do_something(dly)) for dly in [10, 30, 20]] - - yield Combine(*(cr.join() for cr in crs)) - - @cocotb.test() def test_clock_cycles_forked(dut): """ Test that ClockCycles can be used in forked coroutines """ @@ -1003,96 +955,6 @@ def wait_ten(): yield b.join() -@cocotb.test() -def test_yield_list_stale(dut): - """ Test that a trigger yielded as part of a list can't cause a spurious wakeup """ - # gh-843 - events = [Event() for i in range(3)] - - waiters = [e.wait() for e in events] - - @cocotb.coroutine - def wait_for_lists(): - ret_i = waiters.index((yield [waiters[0], waiters[1]])) - assert ret_i == 0, "Expected event 0 to fire, not {}".format(ret_i) - - ret_i = waiters.index((yield [waiters[2]])) - assert ret_i == 2, "Expected event 2 to fire, not {}".format(ret_i) - - @cocotb.coroutine - def wait_for_e1(): - """ wait on the event that didn't wake `wait_for_lists` """ - ret_i = waiters.index((yield waiters[1])) - assert ret_i == 1, "Expected event 1 to fire, not {}".format(ret_i) - - @cocotb.coroutine - def fire_events(): - """ fire the events in order """ - for e in events: - yield Timer(1) - e.set() - - fire_task = cocotb.fork(fire_events()) - e1_task = cocotb.fork(wait_for_e1()) - yield wait_for_lists() - - # make sure the other tasks finish - yield fire_task.join() - yield e1_task.join() - - -@cocotb.test() -def test_nested_first(dut): - """ Test that nested First triggers behave as expected """ - events = [Event() for i in range(3)] - waiters = [e.wait() for e in events] - - @cocotb.coroutine - def fire_events(): - """ fire the events in order """ - for e in events: - yield Timer(1) - e.set() - - - @cocotb.coroutine - def wait_for_nested_first(): - inner_first = First(waiters[0], waiters[1]) - ret = yield First(inner_first, waiters[2]) - - # should unpack completely, rather than just by one level - assert ret is not inner_first - assert ret is waiters[0] - - fire_task = cocotb.fork(fire_events()) - yield wait_for_nested_first() - yield fire_task.join() - - -@cocotb.test() -async def test_first_does_not_kill(dut): - """ Test that `First` does not kill coroutines that did not finish first """ - ran = False - - # decorating `async def` is required to use `First` - @cocotb.coroutine - async def coro(): - nonlocal ran - await Timer(2, units='ns') - ran = True - - # Coroutine runs for 2ns, so we expect the timer to fire first - timer = Timer(1, units='ns') - t = await First(timer, coro()) - assert t is timer - assert not ran - - # the background routine is still running, but should finish after 1ns - await Timer(2, units='ns') - - assert ran - - @cocotb.test() def test_readwrite(dut): """ Test that ReadWrite can be waited on """ diff --git a/tests/test_cases/test_cocotb/test_concurrency_primitives.py b/tests/test_cases/test_cocotb/test_concurrency_primitives.py new file mode 100644 index 00000000..18aedb23 --- /dev/null +++ b/tests/test_cases/test_cocotb/test_concurrency_primitives.py @@ -0,0 +1,147 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause +""" +Tests for concurrency primitives like First and Combine +""" +import cocotb +from cocotb.triggers import Timer, First, Event, Combine +import textwrap +from test_cocotb import _check_traceback + + +@cocotb.test() +def test_yield_list_stale(dut): + """ Test that a trigger yielded as part of a list can't cause a spurious wakeup """ + # gh-843 + events = [Event() for i in range(3)] + + waiters = [e.wait() for e in events] + + @cocotb.coroutine + def wait_for_lists(): + ret_i = waiters.index((yield [waiters[0], waiters[1]])) + assert ret_i == 0, "Expected event 0 to fire, not {}".format(ret_i) + + ret_i = waiters.index((yield [waiters[2]])) + assert ret_i == 2, "Expected event 2 to fire, not {}".format(ret_i) + + @cocotb.coroutine + def wait_for_e1(): + """ wait on the event that didn't wake `wait_for_lists` """ + ret_i = waiters.index((yield waiters[1])) + assert ret_i == 1, "Expected event 1 to fire, not {}".format(ret_i) + + @cocotb.coroutine + def fire_events(): + """ fire the events in order """ + for e in events: + yield Timer(1) + e.set() + + fire_task = cocotb.fork(fire_events()) + e1_task = cocotb.fork(wait_for_e1()) + yield wait_for_lists() + + # make sure the other tasks finish + yield fire_task.join() + yield e1_task.join() + + +@cocotb.test() +def test_nested_first(dut): + """ Test that nested First triggers behave as expected """ + events = [Event() for i in range(3)] + waiters = [e.wait() for e in events] + + @cocotb.coroutine + def fire_events(): + """ fire the events in order """ + for e in events: + yield Timer(1) + e.set() + + @cocotb.coroutine + def wait_for_nested_first(): + inner_first = First(waiters[0], waiters[1]) + ret = yield First(inner_first, waiters[2]) + + # should unpack completely, rather than just by one level + assert ret is not inner_first + assert ret is waiters[0] + + fire_task = cocotb.fork(fire_events()) + yield wait_for_nested_first() + yield fire_task.join() + + +@cocotb.test() +async def test_first_does_not_kill(dut): + """ Test that `First` does not kill coroutines that did not finish first """ + ran = False + + # decorating `async def` is required to use `First` + @cocotb.coroutine + async def coro(): + nonlocal ran + await Timer(2, units='ns') + ran = True + + # Coroutine runs for 2ns, so we expect the timer to fire first + timer = Timer(1, units='ns') + t = await First(timer, coro()) + assert t is timer + assert not ran + + # the background routine is still running, but should finish after 1ns + await Timer(2, units='ns') + + assert ran + + +@cocotb.test() +def test_exceptions_first(dut): + """ Test exception propagation via cocotb.triggers.First """ + @cocotb.coroutine + def raise_inner(): + yield Timer(10) + raise ValueError('It is soon now') + + @cocotb.coroutine + def raise_soon(): + yield Timer(1) + yield cocotb.triggers.First(raise_inner()) + + # it's ok to change this value if the traceback changes - just make sure + # that when changed, it doesn't become harder to read. + expected = textwrap.dedent(r""" + Traceback \(most recent call last\): + File ".*test_cocotb\.py", line \d+, in _check_traceback + yield running_coro + File ".*test_concurrency_primitives\.py", line \d+, in raise_soon + yield cocotb\.triggers\.First\(raise_inner\(\)\) + File ".*triggers\.py", line \d+, in _wait + return await first_trigger[^\n]* + File ".*triggers.py", line \d+, in __await__ + return \(yield self\) + File ".*triggers.py", line \d+, in __await__ + return \(yield self\) + File ".*test_concurrency_primitives\.py", line \d+, in raise_inner + raise ValueError\('It is soon now'\) + ValueError: It is soon now""").strip() + + yield _check_traceback(raise_soon(), ValueError, expected) + + +@cocotb.test() +def test_combine(dut): + """ Test the Combine trigger. """ + # gh-852 + + @cocotb.coroutine + def do_something(delay): + yield Timer(delay) + + crs = [cocotb.fork(do_something(dly)) for dly in [10, 30, 20]] + + yield Combine(*(cr.join() for cr in crs)) From 465a18ccf2fe2a4aa4a5a5896b29509613c801f6 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 6 Apr 2020 22:30:38 -0500 Subject: [PATCH 2107/2656] Move tests related to cocotb.test into their own module --- tests/test_cases/test_cocotb/Makefile | 1 + tests/test_cases/test_cocotb/test_cocotb.py | 106 ------------------ tests/test_cases/test_cocotb/test_tests.py | 115 ++++++++++++++++++++ 3 files changed, 116 insertions(+), 106 deletions(-) create mode 100644 tests/test_cases/test_cocotb/test_tests.py diff --git a/tests/test_cases/test_cocotb/Makefile b/tests/test_cases/test_cocotb/Makefile index 693e3f9a..ab4f4f13 100644 --- a/tests/test_cases/test_cocotb/Makefile +++ b/tests/test_cases/test_cocotb/Makefile @@ -35,6 +35,7 @@ MODULE := "\ test_doctests,\ test_synchronization_primitives,\ test_concurrency_primitives,\ + test_tests,\ " ifeq ($(shell python -c "import sys; print(sys.version_info >= (3, 6))"), "True") diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index bf2dcb39..435c0de2 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -54,11 +54,6 @@ # Tests relating to providing meaningful errors if we forget to use the # yield keyword correctly to turn a function into a coroutine -@cocotb.test(expect_error=TypeError) -def test_not_a_coroutine(dut): - """Example of a failing to use the yield keyword in a test""" - dut._log.warning("This test will fail because we don't yield anything") - @cocotb.coroutine def function_not_a_coroutine(): @@ -431,13 +426,6 @@ def syntax_error(): fail -@cocotb.test(expect_error=True) -def test_syntax_error(dut): - """Syntax error in the test""" - yield clock_gen(dut.clk) - fail - - #@cocotb.test(expect_error=True) @cocotb.test(expect_error=True) def test_coroutine_syntax_error(dut): @@ -788,16 +776,6 @@ def test_lessthan_raises_error(dut): if False: yield -@cocotb.test() -def test_tests_are_tests(dut): - """ - Test that things annotated with cocotb.test are tests - """ - yield Timer(1) - - assert isinstance(test_tests_are_tests, cocotb.test) - - @cocotb.test() def test_coroutine_return(dut): """ Test that the Python 3.3 syntax for returning from generators works """ @@ -906,13 +884,6 @@ def null_coroutine(): yield Timer(100) -@cocotb.test() -def test_immediate_test(dut): - """ Test that tests can return immediately """ - return - yield - - @cocotb.test() def test_immediate_coro(dut): """ @@ -1003,28 +974,6 @@ def prime(self, callback): raise TestFailure -@cocotb.test(expect_fail=True) -def test_assertion_is_failure(dut): - yield Timer(1) - assert False - - -class MyException(Exception): - pass - - -@cocotb.test(expect_error=MyException) -def test_expect_particular_exception(dut): - yield Timer(1) - raise MyException() - - -@cocotb.test(expect_error=(MyException, ValueError)) -def test_expect_exception_list(dut): - yield Timer(1) - raise MyException() - - @cocotb.coroutine def example(): yield Timer(10, 'ns') @@ -1047,29 +996,6 @@ def test_timeout_func_pass(dut): assert res == 1 -@cocotb.test(expect_error=cocotb.result.SimTimeoutError, timeout_time=1, timeout_unit='ns') -def test_timeout_testdec_fail(dut): - yield Timer(10, 'ns') - - -@cocotb.test(timeout_time=100, timeout_unit='ns') -def test_timeout_testdec_pass(dut): - yield Timer(10, 'ns') - - -@cocotb.test(timeout_time=10, timeout_unit='ns') -def test_timeout_testdec_simultaneous(dut): - try: - yield cocotb.triggers.with_timeout(Timer(1, 'ns'), timeout_time=1, timeout_unit='ns') - except cocotb.result.SimTimeoutError: - pass - else: - assert False, "Expected a Timeout" - # Whether this test fails or passes depends on the behavior of the - # scheduler, simulator, and the implementation of the timeout function. - # CAUTION: THIS MAY CHANGE - - @cocotb.test() def test_bad_attr(dut): yield cocotb.triggers.NullTrigger() @@ -1106,13 +1032,6 @@ class SomeException(Exception): pass -# just to be sure... -@cocotb.test(expect_fail=True) -async def test_async_test_can_fail(dut): - await Timer(1) - raise TestFailure - - @cocotb.test() def test_annotated_async_from_coro(dut): """ @@ -1215,31 +1134,6 @@ async def example(): assert ran -# these tests should run in definition order, not lexicographic order -last_ordered_test = None - - -@cocotb.test() -async def test_ordering_3(dut): - global last_ordered_test - val, last_ordered_test = last_ordered_test, 3 - assert val is None - - -@cocotb.test() -async def test_ordering_2(dut): - global last_ordered_test - val, last_ordered_test = last_ordered_test, 2 - assert val == 3 - - -@cocotb.test() -async def test_ordering_1(dut): - global last_ordered_test - val, last_ordered_test = last_ordered_test, 1 - assert val == 2 - - # strings are not supported on Icarus @cocotb.test(skip=cocotb.SIM_NAME.lower().startswith("icarus")) async def test_string_handle_takes_bytes(dut): diff --git a/tests/test_cases/test_cocotb/test_tests.py b/tests/test_cases/test_cocotb/test_tests.py new file mode 100644 index 00000000..1793c293 --- /dev/null +++ b/tests/test_cases/test_cocotb/test_tests.py @@ -0,0 +1,115 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause +""" +Tests of cocotb.test functionality + +* expect_error +* expect_fail +* timeout +""" +import cocotb +from cocotb.triggers import Timer +from cocotb.result import TestFailure +from test_cocotb import clock_gen + + +@cocotb.test(expect_error=True) +def test_syntax_error(dut): + """Syntax error in the test""" + yield clock_gen(dut.clk) + fail + + +@cocotb.test() +def test_tests_are_tests(dut): + """ + Test that things annotated with cocotb.test are tests + """ + yield Timer(1) + + assert isinstance(test_tests_are_tests, cocotb.test) + + +# just to be sure... +@cocotb.test(expect_fail=True) +async def test_async_test_can_fail(dut): + await Timer(1) + raise TestFailure + + +@cocotb.test() +def test_immediate_test(dut): + """ Test that tests can return immediately """ + return + yield + + +@cocotb.test(expect_fail=True) +def test_assertion_is_failure(dut): + yield Timer(1) + assert False + + +class MyException(Exception): + pass + + +@cocotb.test(expect_error=MyException) +def test_expect_particular_exception(dut): + yield Timer(1) + raise MyException() + + +@cocotb.test(expect_error=(MyException, ValueError)) +def test_expect_exception_list(dut): + yield Timer(1) + raise MyException() + + +@cocotb.test(expect_error=cocotb.result.SimTimeoutError, timeout_time=1, timeout_unit='ns') +def test_timeout_testdec_fail(dut): + yield Timer(10, 'ns') + + +@cocotb.test(timeout_time=100, timeout_unit='ns') +def test_timeout_testdec_pass(dut): + yield Timer(10, 'ns') + + +@cocotb.test(timeout_time=10, timeout_unit='ns') +def test_timeout_testdec_simultaneous(dut): + try: + yield cocotb.triggers.with_timeout(Timer(1, 'ns'), timeout_time=1, timeout_unit='ns') + except cocotb.result.SimTimeoutError: + pass + else: + assert False, "Expected a Timeout" + # Whether this test fails or passes depends on the behavior of the + # scheduler, simulator, and the implementation of the timeout function. + # CAUTION: THIS MAY CHANGE + + +# these tests should run in definition order, not lexicographic order +last_ordered_test = None + + +@cocotb.test() +async def test_ordering_3(dut): + global last_ordered_test + val, last_ordered_test = last_ordered_test, 3 + assert val is None + + +@cocotb.test() +async def test_ordering_2(dut): + global last_ordered_test + val, last_ordered_test = last_ordered_test, 2 + assert val == 3 + + +@cocotb.test() +async def test_ordering_1(dut): + global last_ordered_test + val, last_ordered_test = last_ordered_test, 1 + assert val == 2 From 87391ff08fcf2d212e765cee5c29e4e09939c587 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 6 Apr 2020 22:53:48 -0500 Subject: [PATCH 2108/2656] Move tests for generator-based coroutine support to their own module --- tests/test_cases/test_cocotb/Makefile | 1 + tests/test_cases/test_cocotb/test_cocotb.py | 193 ---------------- .../test_cocotb/test_generator_coroutines.py | 209 ++++++++++++++++++ 3 files changed, 210 insertions(+), 193 deletions(-) create mode 100644 tests/test_cases/test_cocotb/test_generator_coroutines.py diff --git a/tests/test_cases/test_cocotb/Makefile b/tests/test_cases/test_cocotb/Makefile index ab4f4f13..fd141b73 100644 --- a/tests/test_cases/test_cocotb/Makefile +++ b/tests/test_cases/test_cocotb/Makefile @@ -36,6 +36,7 @@ MODULE := "\ test_synchronization_primitives,\ test_concurrency_primitives,\ test_tests,\ + test_generator_coroutines,\ " ifeq ($(shell python -c "import sys; print(sys.version_info >= (3, 6))"), "True") diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 435c0de2..ed0a9676 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -51,58 +51,6 @@ from cocotb.outcomes import Value, Error -# Tests relating to providing meaningful errors if we forget to use the -# yield keyword correctly to turn a function into a coroutine - - -@cocotb.coroutine -def function_not_a_coroutine(): - """If we don't yield, this isn't a coroutine""" - return "This should fail" - - -@cocotb.test() -def test_function_not_a_coroutine(dut): - """Example of trying to yield a coroutine that isn't a coroutine""" - yield Timer(500) - try: - # failure should occur before we even try to yield or fork the coroutine - coro = function_not_a_coroutine() - except TypeError as exc: - assert "isn't a valid coroutine" in str(exc) - else: - raise TestFailure - - -def normal_function(dut): - return True - - -@cocotb.test() -def test_function_not_decorated(dut): - try: - yield normal_function(dut) - except TypeError as exc: - assert "yielded" in str(exc) - assert "scheduler can't handle" in str(exc) - except: - raise TestFailure - - -@cocotb.test() -def test_function_not_decorated_fork(dut): - """Example of trying to fork a coroutine that isn't a coroutine""" - yield Timer(500) - try: - cocotb.fork(normal_function(dut)) - except TypeError as exc: - assert "isn't a coroutine" in str(exc) - else: - raise TestFailure() - - yield Timer(500) - - @cocotb.test(expect_fail=False) def test_function_reentrant_clock(dut): """Test yielding a reentrant clock""" @@ -126,15 +74,6 @@ def clock_gen(clock): clock._log.warning("Clock generator finished!") -@cocotb.test(expect_fail=False) -def test_yield_list(dut): - """Example of yielding on a list of triggers""" - clock = dut.clk - cocotb.scheduler.add(clock_gen(clock)) - yield [Timer(1000), Timer(2000)] - - yield Timer(10000) - test_flag = False @@ -161,19 +100,6 @@ def test_coroutine_kill(dut): raise TestFailure -@cocotb.test() -def test_adding_a_coroutine_without_starting(dut): - """Catch (and provide useful error) for attempts to fork coroutines - incorrectly""" - yield Timer(100) - try: - forked = cocotb.fork(clock_gen) - except TypeError as exc: - assert "a coroutine that hasn't started" in str(exc) - else: - raise TestFailure - - @cocotb.test(expect_fail=False) def test_clock_with_units(dut): clk_1mhz = Clock(dut.clk, 1.0, units='us') @@ -420,28 +346,6 @@ def test_coroutine_close_down(dut): dut._log.info("Back from joins") -@cocotb.coroutine -def syntax_error(): - yield Timer(100) - fail - - -#@cocotb.test(expect_error=True) -@cocotb.test(expect_error=True) -def test_coroutine_syntax_error(dut): - """Syntax error in a coroutine that we yield""" - yield clock_gen(dut.clk) - yield syntax_error() - - -@cocotb.test(expect_error=True) -def test_fork_syntax_error(dut): - """Syntax error in a coroutine that we fork""" - yield clock_gen(dut.clk) - cocotb.fork(syntax_error()) - yield clock_gen(dut.clk) - - @cocotb.test() def test_fork_and_monitor(dut, period=1000, clocks=6): cocotb.fork(Clock(dut.clk, period).start()) @@ -776,21 +680,6 @@ def test_lessthan_raises_error(dut): if False: yield -@cocotb.test() -def test_coroutine_return(dut): - """ Test that the Python 3.3 syntax for returning from generators works """ - @cocotb.coroutine - def return_it(x): - return x - - # this makes `return_it` a coroutine - yield - - ret = yield return_it(42) - if ret != 42: - raise TestFailure("Return statement did not work") - - @cocotb.coroutine def _check_traceback(running_coro, exc_type, pattern): try: @@ -811,63 +700,6 @@ def _check_traceback(running_coro, exc_type, pattern): ) -@cocotb.test() -def test_exceptions_direct(dut): - """ Test exception propagation via a direct yield statement """ - @cocotb.coroutine - def raise_inner(): - yield Timer(10) - raise ValueError('It is soon now') - - @cocotb.coroutine - def raise_soon(): - yield Timer(1) - yield raise_inner() - - # it's ok to change this value if the traceback changes - just make sure - # that when changed, it doesn't become harder to read. - expected = textwrap.dedent(r""" - Traceback \(most recent call last\): - File ".*test_cocotb\.py", line \d+, in _check_traceback - yield running_coro - File ".*test_cocotb\.py", line \d+, in raise_soon - yield raise_inner\(\) - File ".*test_cocotb\.py", line \d+, in raise_inner - raise ValueError\('It is soon now'\) - ValueError: It is soon now""").strip() - - yield _check_traceback(raise_soon(), ValueError, expected) - - -@cocotb.test() -def test_exceptions_forked(dut): - """ Test exception propagation via cocotb.fork """ - @cocotb.coroutine - def raise_inner(): - yield Timer(10) - raise ValueError('It is soon now') - - @cocotb.coroutine - def raise_soon(): - yield Timer(1) - coro = cocotb.fork(raise_inner()) - yield coro.join() - - # it's ok to change this value if the traceback changes - just make sure - # that when changed, it doesn't become harder to read. - expected = textwrap.dedent(r""" - Traceback \(most recent call last\): - File ".*test_cocotb\.py", line \d+, in _check_traceback - yield running_coro - File ".*test_cocotb\.py", line \d+, in raise_soon - yield coro\.join\(\) - File ".*test_cocotb\.py", line \d+, in raise_inner - raise ValueError\('It is soon now'\) - ValueError: It is soon now""").strip() - - yield _check_traceback(raise_soon(), ValueError, expected) - - @cocotb.test() def test_stack_overflow(dut): """ @@ -884,31 +716,6 @@ def null_coroutine(): yield Timer(100) -@cocotb.test() -def test_immediate_coro(dut): - """ - Test that coroutines can return immediately - """ - @cocotb.coroutine - def immediate_value(): - return 42 - yield - - @cocotb.coroutine - def immediate_exception(): - raise ValueError - yield - - assert (yield immediate_value()) == 42 - - try: - yield immediate_exception() - except ValueError: - pass - else: - raise TestFailure("Exception was not raised") - - @cocotb.test() def test_clock_cycles_forked(dut): """ Test that ClockCycles can be used in forked coroutines """ diff --git a/tests/test_cases/test_cocotb/test_generator_coroutines.py b/tests/test_cases/test_cocotb/test_generator_coroutines.py new file mode 100644 index 00000000..f16d215e --- /dev/null +++ b/tests/test_cases/test_cocotb/test_generator_coroutines.py @@ -0,0 +1,209 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause +""" +Tests that sepcifically test generator-based coroutines +""" +import cocotb +from cocotb.triggers import Timer +from cocotb.result import TestFailure +from test_cocotb import clock_gen, _check_traceback +import textwrap + + +# Tests relating to providing meaningful errors if we forget to use the +# yield keyword correctly to turn a function into a coroutine + +@cocotb.test(expect_error=TypeError) +def test_not_a_coroutine(dut): + """Example of a failing to use the yield keyword in a test""" + dut._log.warning("This test will fail because we don't yield anything") + + +@cocotb.coroutine +def function_not_a_coroutine(): + """If we don't yield, this isn't a coroutine""" + return "This should fail" + + +@cocotb.test() +def test_function_not_a_coroutine(dut): + """Example of trying to yield a coroutine that isn't a coroutine""" + yield Timer(500) + try: + # failure should occur before we even try to yield or fork the coroutine + coro = function_not_a_coroutine() + except TypeError as exc: + assert "isn't a valid coroutine" in str(exc) + else: + raise TestFailure + + +def normal_function(dut): + return True + + +@cocotb.test() +def test_function_not_decorated(dut): + try: + yield normal_function(dut) + except TypeError as exc: + assert "yielded" in str(exc) + assert "scheduler can't handle" in str(exc) + except: + raise TestFailure + + +@cocotb.test() +def test_function_not_decorated_fork(dut): + """Example of trying to fork a coroutine that isn't a coroutine""" + yield Timer(500) + try: + cocotb.fork(normal_function(dut)) + except TypeError as exc: + assert "isn't a coroutine" in str(exc) + else: + raise TestFailure() + + yield Timer(500) + + +@cocotb.test() +def test_adding_a_coroutine_without_starting(dut): + """Catch (and provide useful error) for attempts to fork coroutines + incorrectly""" + yield Timer(100) + try: + forked = cocotb.fork(clock_gen) + except TypeError as exc: + assert "a coroutine that hasn't started" in str(exc) + else: + raise TestFailure + + +@cocotb.test(expect_fail=False) +def test_yield_list(dut): + """Example of yielding on a list of triggers""" + clock = dut.clk + cocotb.scheduler.add(clock_gen(clock)) + yield [Timer(1000), Timer(2000)] + + yield Timer(10000) + + +@cocotb.coroutine +def syntax_error(): + yield Timer(100) + fail + + +@cocotb.test(expect_error=True) +def test_coroutine_syntax_error(dut): + """Syntax error in a coroutine that we yield""" + yield clock_gen(dut.clk) + yield syntax_error() + + +@cocotb.test(expect_error=True) +def test_fork_syntax_error(dut): + """Syntax error in a coroutine that we fork""" + yield clock_gen(dut.clk) + cocotb.fork(syntax_error()) + yield clock_gen(dut.clk) + + +@cocotb.test() +def test_coroutine_return(dut): + """ Test that the Python 3.3 syntax for returning from generators works """ + @cocotb.coroutine + def return_it(x): + return x + + # this makes `return_it` a coroutine + yield + + ret = yield return_it(42) + if ret != 42: + raise TestFailure("Return statement did not work") + + +@cocotb.test() +def test_immediate_coro(dut): + """ + Test that coroutines can return immediately + """ + @cocotb.coroutine + def immediate_value(): + return 42 + yield + + @cocotb.coroutine + def immediate_exception(): + raise ValueError + yield + + assert (yield immediate_value()) == 42 + + try: + yield immediate_exception() + except ValueError: + pass + else: + raise TestFailure("Exception was not raised") + + +@cocotb.test() +def test_exceptions_direct(dut): + """ Test exception propagation via a direct yield statement """ + @cocotb.coroutine + def raise_inner(): + yield Timer(10) + raise ValueError('It is soon now') + + @cocotb.coroutine + def raise_soon(): + yield Timer(1) + yield raise_inner() + + # it's ok to change this value if the traceback changes - just make sure + # that when changed, it doesn't become harder to read. + expected = textwrap.dedent(r""" + Traceback \(most recent call last\): + File ".*test_cocotb\.py", line \d+, in _check_traceback + yield running_coro + File ".*test_generator_coroutines\.py", line \d+, in raise_soon + yield raise_inner\(\) + File ".*test_generator_coroutines\.py", line \d+, in raise_inner + raise ValueError\('It is soon now'\) + ValueError: It is soon now""").strip() + + yield _check_traceback(raise_soon(), ValueError, expected) + + +@cocotb.test() +def test_exceptions_forked(dut): + """ Test exception propagation via cocotb.fork """ + @cocotb.coroutine + def raise_inner(): + yield Timer(10) + raise ValueError('It is soon now') + + @cocotb.coroutine + def raise_soon(): + yield Timer(1) + coro = cocotb.fork(raise_inner()) + yield coro.join() + + # it's ok to change this value if the traceback changes - just make sure + # that when changed, it doesn't become harder to read. + expected = textwrap.dedent(r""" + Traceback \(most recent call last\): + File ".*test_cocotb\.py", line \d+, in _check_traceback + yield running_coro + File ".*test_generator_coroutines\.py", line \d+, in raise_soon + yield coro\.join\(\) + File ".*test_generator_coroutines\.py", line \d+, in raise_inner + raise ValueError\('It is soon now'\) + ValueError: It is soon now""").strip() + + yield _check_traceback(raise_soon(), ValueError, expected) From 27cf7b7feb6b97d09181d1e5d291c956a7ccf097 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 6 Apr 2020 23:11:00 -0500 Subject: [PATCH 2109/2656] Move tests for timing-related triggers to their own module --- tests/test_cases/test_cocotb/Makefile | 1 + tests/test_cases/test_cocotb/test_cocotb.py | 211 ---------------- .../test_cocotb/test_timing_triggers.py | 235 ++++++++++++++++++ 3 files changed, 236 insertions(+), 211 deletions(-) create mode 100644 tests/test_cases/test_cocotb/test_timing_triggers.py diff --git a/tests/test_cases/test_cocotb/Makefile b/tests/test_cases/test_cocotb/Makefile index fd141b73..e9d61a5f 100644 --- a/tests/test_cases/test_cocotb/Makefile +++ b/tests/test_cases/test_cocotb/Makefile @@ -37,6 +37,7 @@ MODULE := "\ test_concurrency_primitives,\ test_tests,\ test_generator_coroutines,\ + test_timing_triggers,\ " ifeq ($(shell python -c "import sys; print(sys.version_info >= (3, 6))"), "True") diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index ed0a9676..96e1b621 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -51,18 +51,6 @@ from cocotb.outcomes import Value, Error -@cocotb.test(expect_fail=False) -def test_function_reentrant_clock(dut): - """Test yielding a reentrant clock""" - clock = dut.clk - timer = Timer(100) - for i in range(10): - clock <= 0 - yield timer - clock <= 1 - yield timer - - @cocotb.coroutine def clock_gen(clock): """Example clock gen for test use""" @@ -157,59 +145,6 @@ def test_clock_with_units(dut): clk_gen.kill() -@cocotb.test(expect_fail=False) -def test_timer_with_units(dut): - time_fs = get_sim_time(units='fs') - - # Yield for one simulation time step - yield Timer(1) - time_step = get_sim_time(units='fs') - time_fs - - try: - # Yield for 2.5 timesteps, should throw exception - yield Timer(2.5*time_step, units='fs') - raise TestFailure("Timers should throw exception if time cannot be achieved with simulator resolution") - except ValueError: - dut._log.info("As expected, unable to create a timer of 2.5 simulator time steps") - - time_fs = get_sim_time(units='fs') - - yield Timer(3, "ns") - - if get_sim_time(units='fs') != time_fs+3000000.0: - raise TestFailure("Expected a delay of 3 ns") - - time_fs = get_sim_time(units='fs') - yield Timer(1.5, "ns") - - if get_sim_time(units='fs') != time_fs+1500000.0: - raise TestFailure("Expected a delay of 1.5 ns") - - time_fs = get_sim_time(units='fs') - yield Timer(10.0, "ps") - - if get_sim_time(units='fs') != time_fs+10000.0: - raise TestFailure("Expected a delay of 10 ps") - - time_fs = get_sim_time(units='fs') - yield Timer(1.0, "us") - - if get_sim_time(units='fs') != time_fs+1000000000.0: - raise TestFailure("Expected a delay of 1 us") - -@cocotb.test() -def test_timer_with_rational_units(dut): - """ Test that rounding errors are not introduced in exact values """ - # now with fractions - time_fs = get_sim_time(units='fs') - yield Timer(Fraction(1, int(1e9)), units='sec') - assert get_sim_time(units='fs') == time_fs + 1000000.0, "Expected a delay of 1 ns" - - # now with decimals - time_fs = get_sim_time(units='fs') - yield Timer(Decimal('1e-9'), units='sec') - assert get_sim_time(units='fs') == time_fs + 1000000.0, "Expected a delay of 1 ns" - @cocotb.test(expect_fail=False) def test_anternal_clock(dut): @@ -222,98 +157,6 @@ def test_anternal_clock(dut): count += 1 clk_gen.kill() -exited = False - - -@cocotb.coroutine -def do_test_readwrite_in_readonly(dut): - global exited - yield RisingEdge(dut.clk) - yield ReadOnly() - yield ReadWrite() - exited = True - - -@cocotb.coroutine -def do_test_cached_write_in_readonly(dut): - global exited - yield RisingEdge(dut.clk) - yield ReadOnly() - dut.clk <= 0 - exited = True - - -@cocotb.coroutine -def do_test_afterdelay_in_readonly(dut, delay): - global exited - yield RisingEdge(dut.clk) - yield ReadOnly() - yield Timer(delay) - exited = True - - -@cocotb.test(expect_error=True, - expect_fail=cocotb.SIM_NAME.lower().startswith(("icarus", - "riviera", - "modelsim", - "ncsim", - "xmsim"))) -def test_readwrite_in_readonly(dut): - """Test doing invalid sim operation""" - global exited - exited = False - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) - coro = cocotb.fork(do_test_readwrite_in_readonly(dut)) - yield [Join(coro), Timer(10000)] - clk_gen.kill() - if exited is not True: - raise TestFailure - -@cocotb.test(expect_error=True, - expect_fail=cocotb.SIM_NAME.lower().startswith(("icarus", - "riviera", - "modelsim", - "ncsim", - "xmsim"))) -def test_cached_write_in_readonly(dut): - """Test doing invalid sim operation""" - global exited - exited = False - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) - coro = cocotb.fork(do_test_cached_write_in_readonly(dut)) - yield [Join(coro), Timer(10000)] - clk_gen.kill() - if exited is not True: - raise TestFailure - - -@cocotb.test(expect_fail=cocotb.SIM_NAME.lower().startswith(("icarus", - "chronologic simulation vcs")), - skip=cocotb.SIM_NAME.lower().startswith(("ncsim", "xmsim"))) -def test_afterdelay_in_readonly(dut): - """Test doing invalid sim operation""" - global exited - exited = False - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) - coro = cocotb.fork(do_test_afterdelay_in_readonly(dut, 0)) - yield [Join(coro), Timer(1000)] - clk_gen.kill() - if exited is not True: - raise TestFailure - - -@cocotb.test() -def test_afterdelay_in_readonly_valid(dut): - """Same as test_afterdelay_in_readonly but with valid delay > 0""" - global exited - exited = False - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) - coro = cocotb.fork(do_test_afterdelay_in_readonly(dut, 1)) - yield [Join(coro), Timer(100000)] - clk_gen.kill() - if exited is not True: - raise TestFailure - @cocotb.coroutine def clock_one(dut): @@ -733,38 +576,6 @@ def wait_ten(): yield b.join() -@cocotb.test() -def test_readwrite(dut): - """ Test that ReadWrite can be waited on """ - # gh-759 - yield Timer(1) - dut.clk <= 1 - yield ReadWrite() - - -@cocotb.test() -def test_writes_have_taken_effect_after_readwrite(dut): - """ Test that ReadWrite fires first for the background write coro """ - dut.stream_in_data.setimmediatevalue(0) - - @cocotb.coroutine - def write_manually(): - yield ReadWrite() - # this should overwrite the write written below - dut.stream_in_data.setimmediatevalue(2) - - # queue a backround task to do a manual write - waiter = cocotb.fork(write_manually()) - - # do a delayed write. This will be overwritten - dut.stream_in_data <= 3 - yield waiter - - # check that the write we expected took precedence - yield ReadOnly() - assert dut.stream_in_data.value == 2 - - @cocotb.test() def test_trigger_with_failing_prime(dut): """ Test that a trigger failing to prime throws """ @@ -781,28 +592,6 @@ def prime(self, callback): raise TestFailure -@cocotb.coroutine -def example(): - yield Timer(10, 'ns') - return 1 - - -@cocotb.test() -def test_timeout_func_fail(dut): - try: - yield cocotb.triggers.with_timeout(example(), timeout_time=1, timeout_unit='ns') - except cocotb.result.SimTimeoutError: - pass - else: - assert False, "Expected a Timeout" - - -@cocotb.test() -def test_timeout_func_pass(dut): - res = yield cocotb.triggers.with_timeout(example(), timeout_time=100, timeout_unit='ns') - assert res == 1 - - @cocotb.test() def test_bad_attr(dut): yield cocotb.triggers.NullTrigger() diff --git a/tests/test_cases/test_cocotb/test_timing_triggers.py b/tests/test_cases/test_cocotb/test_timing_triggers.py new file mode 100644 index 00000000..285521c5 --- /dev/null +++ b/tests/test_cases/test_cocotb/test_timing_triggers.py @@ -0,0 +1,235 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause +""" +Tests related to timing triggers + +* Timer +* ReadWrite +* ReadOnly +* NextTimeStep +* with_timeout +""" +import cocotb +from cocotb.triggers import Timer, RisingEdge, ReadOnly, ReadWrite, Join +from cocotb.utils import get_sim_time +from cocotb.result import TestFailure +from cocotb.clock import Clock + +from fractions import Fraction +from decimal import Decimal + + +@cocotb.test(expect_fail=False) +def test_function_reentrant_clock(dut): + """Test yielding a reentrant clock""" + clock = dut.clk + timer = Timer(100) + for i in range(10): + clock <= 0 + yield timer + clock <= 1 + yield timer + + +@cocotb.test(expect_fail=False) +def test_timer_with_units(dut): + time_fs = get_sim_time(units='fs') + + # Yield for one simulation time step + yield Timer(1) + time_step = get_sim_time(units='fs') - time_fs + + try: + # Yield for 2.5 timesteps, should throw exception + yield Timer(2.5*time_step, units='fs') + raise TestFailure("Timers should throw exception if time cannot be achieved with simulator resolution") + except ValueError: + dut._log.info("As expected, unable to create a timer of 2.5 simulator time steps") + + time_fs = get_sim_time(units='fs') + + yield Timer(3, "ns") + + if get_sim_time(units='fs') != time_fs+3000000.0: + raise TestFailure("Expected a delay of 3 ns") + + time_fs = get_sim_time(units='fs') + yield Timer(1.5, "ns") + + if get_sim_time(units='fs') != time_fs+1500000.0: + raise TestFailure("Expected a delay of 1.5 ns") + + time_fs = get_sim_time(units='fs') + yield Timer(10.0, "ps") + + if get_sim_time(units='fs') != time_fs+10000.0: + raise TestFailure("Expected a delay of 10 ps") + + time_fs = get_sim_time(units='fs') + yield Timer(1.0, "us") + + if get_sim_time(units='fs') != time_fs+1000000000.0: + raise TestFailure("Expected a delay of 1 us") + + +@cocotb.test() +def test_timer_with_rational_units(dut): + """ Test that rounding errors are not introduced in exact values """ + # now with fractions + time_fs = get_sim_time(units='fs') + yield Timer(Fraction(1, int(1e9)), units='sec') + assert get_sim_time(units='fs') == time_fs + 1000000.0, "Expected a delay of 1 ns" + + # now with decimals + time_fs = get_sim_time(units='fs') + yield Timer(Decimal('1e-9'), units='sec') + assert get_sim_time(units='fs') == time_fs + 1000000.0, "Expected a delay of 1 ns" + + +exited = False + + +@cocotb.coroutine +def do_test_readwrite_in_readonly(dut): + global exited + yield RisingEdge(dut.clk) + yield ReadOnly() + yield ReadWrite() + exited = True + + +@cocotb.coroutine +def do_test_cached_write_in_readonly(dut): + global exited + yield RisingEdge(dut.clk) + yield ReadOnly() + dut.clk <= 0 + exited = True + + +@cocotb.coroutine +def do_test_afterdelay_in_readonly(dut, delay): + global exited + yield RisingEdge(dut.clk) + yield ReadOnly() + yield Timer(delay) + exited = True + + +@cocotb.test(expect_error=True, + expect_fail=cocotb.SIM_NAME.lower().startswith(("icarus", + "riviera", + "modelsim", + "ncsim", + "xmsim"))) +def test_readwrite_in_readonly(dut): + """Test doing invalid sim operation""" + global exited + exited = False + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + coro = cocotb.fork(do_test_readwrite_in_readonly(dut)) + yield [Join(coro), Timer(10000)] + clk_gen.kill() + if exited is not True: + raise TestFailure + + +@cocotb.test(expect_error=True, + expect_fail=cocotb.SIM_NAME.lower().startswith(("icarus", + "riviera", + "modelsim", + "ncsim", + "xmsim"))) +def test_cached_write_in_readonly(dut): + """Test doing invalid sim operation""" + global exited + exited = False + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + coro = cocotb.fork(do_test_cached_write_in_readonly(dut)) + yield [Join(coro), Timer(10000)] + clk_gen.kill() + if exited is not True: + raise TestFailure + + +@cocotb.test(expect_fail=cocotb.SIM_NAME.lower().startswith(("icarus", + "chronologic simulation vcs")), + skip=cocotb.SIM_NAME.lower().startswith(("ncsim", "xmsim"))) +def test_afterdelay_in_readonly(dut): + """Test doing invalid sim operation""" + global exited + exited = False + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + coro = cocotb.fork(do_test_afterdelay_in_readonly(dut, 0)) + yield [Join(coro), Timer(1000)] + clk_gen.kill() + if exited is not True: + raise TestFailure + + +@cocotb.test() +def test_afterdelay_in_readonly_valid(dut): + """Same as test_afterdelay_in_readonly but with valid delay > 0""" + global exited + exited = False + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + coro = cocotb.fork(do_test_afterdelay_in_readonly(dut, 1)) + yield [Join(coro), Timer(100000)] + clk_gen.kill() + if exited is not True: + raise TestFailure + + +@cocotb.test() +def test_writes_have_taken_effect_after_readwrite(dut): + """ Test that ReadWrite fires first for the background write coro """ + dut.stream_in_data.setimmediatevalue(0) + + @cocotb.coroutine + def write_manually(): + yield ReadWrite() + # this should overwrite the write written below + dut.stream_in_data.setimmediatevalue(2) + + # queue a backround task to do a manual write + waiter = cocotb.fork(write_manually()) + + # do a delayed write. This will be overwritten + dut.stream_in_data <= 3 + yield waiter + + # check that the write we expected took precedence + yield ReadOnly() + assert dut.stream_in_data.value == 2 + + +@cocotb.coroutine +def example(): + yield Timer(10, 'ns') + return 1 + + +@cocotb.test() +def test_timeout_func_fail(dut): + try: + yield cocotb.triggers.with_timeout(example(), timeout_time=1, timeout_unit='ns') + except cocotb.result.SimTimeoutError: + pass + else: + assert False, "Expected a Timeout" + + +@cocotb.test() +def test_timeout_func_pass(dut): + res = yield cocotb.triggers.with_timeout(example(), timeout_time=100, timeout_unit='ns') + assert res == 1 + + +@cocotb.test() +def test_readwrite(dut): + """ Test that ReadWrite can be waited on """ + # gh-759 + yield Timer(1) + dut.clk <= 1 + yield ReadWrite() From 155949918272f76fca6393f03203221e94d1d231 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 6 Apr 2020 23:34:52 -0500 Subject: [PATCH 2110/2656] Move tests related to the scheduler into their own module --- tests/test_cases/test_cocotb/Makefile | 1 + tests/test_cases/test_cocotb/test_cocotb.py | 163 ---------------- .../test_cases/test_cocotb/test_scheduler.py | 180 ++++++++++++++++++ 3 files changed, 181 insertions(+), 163 deletions(-) create mode 100644 tests/test_cases/test_cocotb/test_scheduler.py diff --git a/tests/test_cases/test_cocotb/Makefile b/tests/test_cases/test_cocotb/Makefile index e9d61a5f..939988da 100644 --- a/tests/test_cases/test_cocotb/Makefile +++ b/tests/test_cases/test_cocotb/Makefile @@ -38,6 +38,7 @@ MODULE := "\ test_tests,\ test_generator_coroutines,\ test_timing_triggers,\ + test_scheduler,\ " ifeq ($(shell python -c "import sys; print(sys.version_info >= (3, 6))"), "True") diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 96e1b621..2cbd4153 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -62,32 +62,6 @@ def clock_gen(clock): clock._log.warning("Clock generator finished!") -test_flag = False - - -@cocotb.coroutine -def clock_yield(generator): - global test_flag - yield Join(generator) - test_flag = True - - -@cocotb.test(expect_fail=False) -def test_coroutine_kill(dut): - """Test that killing a coroutine causes pending routine continue""" - global test_flag - clk_gen = cocotb.scheduler.add(clock_gen(dut.clk)) - yield Timer(100) - clk_gen_two = cocotb.fork(clock_yield(clk_gen)) - yield Timer(100) - clk_gen.kill() - if test_flag is not False: - raise TestFailure - yield Timer(1000) - if test_flag is not True: - raise TestFailure - - @cocotb.test(expect_fail=False) def test_clock_with_units(dut): clk_1mhz = Clock(dut.clk, 1.0, units='us') @@ -158,37 +132,6 @@ def test_anternal_clock(dut): clk_gen.kill() -@cocotb.coroutine -def clock_one(dut): - count = 0 - while count != 50: - yield RisingEdge(dut.clk) - yield Timer(1000) - count += 1 - - -@cocotb.coroutine -def clock_two(dut): - count = 0 - while count != 50: - yield RisingEdge(dut.clk) - yield Timer(10000) - count += 1 - - -@cocotb.test(expect_fail=False) -def test_coroutine_close_down(dut): - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) - - coro_one = cocotb.fork(clock_one(dut)) - coro_two = cocotb.fork(clock_two(dut)) - - yield Join(coro_one) - yield Join(coro_two) - - dut._log.info("Back from joins") - - @cocotb.test() def test_fork_and_monitor(dut, period=1000, clocks=6): cocotb.fork(Clock(dut.clk, period).start()) @@ -396,80 +339,6 @@ def test_clock_cycles(dut): dut._log.info("After 10 edges") -@cocotb.test() -def join_finished(dut): - """ - Test that joining a coroutine that has already been joined gives - the same result as it did the first time. - """ - - retval = None - - @cocotb.coroutine - def some_coro(): - yield Timer(1) - return retval - - coro = cocotb.fork(some_coro()) - - retval = 1 - x = yield coro.join() - assert x == 1 - - # joining the second time should give the same result. - # we change retval here to prove it does not run again - retval = 2 - x = yield coro.join() - assert x == 1 - - -@cocotb.test() -def consistent_join(dut): - """ - Test that joining a coroutine returns the finished value - """ - @cocotb.coroutine - def wait_for(clk, cycles): - rising_edge = RisingEdge(clk) - for _ in range(cycles): - yield rising_edge - return 3 - - cocotb.fork(Clock(dut.clk, 2000, 'ps').start()) - - short_wait = cocotb.fork(wait_for(dut.clk, 10)) - long_wait = cocotb.fork(wait_for(dut.clk, 30)) - - yield wait_for(dut.clk, 20) - a = yield short_wait.join() - b = yield long_wait.join() - assert a == b == 3 - - -@cocotb.test() -def test_kill_twice(dut): - """ - Test that killing a coroutine that has already been killed does not crash - """ - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) - yield Timer(1) - clk_gen.kill() - yield Timer(1) - clk_gen.kill() - - -@cocotb.test() -def test_join_identity(dut): - """ - Test that Join() returns the same object each time - """ - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) - - assert Join(clk_gen) is Join(clk_gen) - yield Timer(1) - clk_gen.kill() - - @cocotb.test() def test_edge_identity(dut): """ @@ -543,22 +412,6 @@ def _check_traceback(running_coro, exc_type, pattern): ) -@cocotb.test() -def test_stack_overflow(dut): - """ - Test against stack overflows when starting many coroutines that terminate - before passing control to the simulator. - """ - @cocotb.coroutine - def null_coroutine(): - yield NullTrigger() - - for _ in range(10000): - yield null_coroutine() - - yield Timer(100) - - @cocotb.test() def test_clock_cycles_forked(dut): """ Test that ClockCycles can be used in forked coroutines """ @@ -576,22 +429,6 @@ def wait_ten(): yield b.join() -@cocotb.test() -def test_trigger_with_failing_prime(dut): - """ Test that a trigger failing to prime throws """ - class ABadTrigger(Trigger): - def prime(self, callback): - raise RuntimeError("oops") - - yield Timer(1) - try: - yield ABadTrigger() - except RuntimeError as exc: - assert "oops" in str(exc) - else: - raise TestFailure - - @cocotb.test() def test_bad_attr(dut): yield cocotb.triggers.NullTrigger() diff --git a/tests/test_cases/test_cocotb/test_scheduler.py b/tests/test_cases/test_cocotb/test_scheduler.py new file mode 100644 index 00000000..649bf9b4 --- /dev/null +++ b/tests/test_cases/test_cocotb/test_scheduler.py @@ -0,0 +1,180 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause +""" +Test for scheduler and coroutine behavior + +* fork +* join +* kill +""" + +import cocotb +from cocotb.triggers import Join, Timer, RisingEdge, Trigger, NullTrigger +from cocotb.result import TestFailure +from cocotb.clock import Clock +from test_cocotb import clock_gen + + +test_flag = False + + +@cocotb.coroutine +def clock_yield(generator): + global test_flag + yield Join(generator) + test_flag = True + + +@cocotb.test(expect_fail=False) +def test_coroutine_kill(dut): + """Test that killing a coroutine causes pending routine continue""" + global test_flag + clk_gen = cocotb.scheduler.add(clock_gen(dut.clk)) + yield Timer(100) + clk_gen_two = cocotb.fork(clock_yield(clk_gen)) + yield Timer(100) + clk_gen.kill() + if test_flag is not False: + raise TestFailure + yield Timer(1000) + if test_flag is not True: + raise TestFailure + + +@cocotb.coroutine +def clock_one(dut): + count = 0 + while count != 50: + yield RisingEdge(dut.clk) + yield Timer(1000) + count += 1 + + +@cocotb.coroutine +def clock_two(dut): + count = 0 + while count != 50: + yield RisingEdge(dut.clk) + yield Timer(10000) + count += 1 + + +@cocotb.test(expect_fail=False) +def test_coroutine_close_down(dut): + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + + coro_one = cocotb.fork(clock_one(dut)) + coro_two = cocotb.fork(clock_two(dut)) + + yield Join(coro_one) + yield Join(coro_two) + + dut._log.info("Back from joins") + + +@cocotb.test() +def join_finished(dut): + """ + Test that joining a coroutine that has already been joined gives + the same result as it did the first time. + """ + + retval = None + + @cocotb.coroutine + def some_coro(): + yield Timer(1) + return retval + + coro = cocotb.fork(some_coro()) + + retval = 1 + x = yield coro.join() + assert x == 1 + + # joining the second time should give the same result. + # we change retval here to prove it does not run again + retval = 2 + x = yield coro.join() + assert x == 1 + + +@cocotb.test() +def consistent_join(dut): + """ + Test that joining a coroutine returns the finished value + """ + @cocotb.coroutine + def wait_for(clk, cycles): + rising_edge = RisingEdge(clk) + for _ in range(cycles): + yield rising_edge + return 3 + + cocotb.fork(Clock(dut.clk, 2000, 'ps').start()) + + short_wait = cocotb.fork(wait_for(dut.clk, 10)) + long_wait = cocotb.fork(wait_for(dut.clk, 30)) + + yield wait_for(dut.clk, 20) + a = yield short_wait.join() + b = yield long_wait.join() + assert a == b == 3 + + +@cocotb.test() +def test_kill_twice(dut): + """ + Test that killing a coroutine that has already been killed does not crash + """ + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + yield Timer(1) + clk_gen.kill() + yield Timer(1) + clk_gen.kill() + + +@cocotb.test() +def test_join_identity(dut): + """ + Test that Join() returns the same object each time + """ + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + + assert Join(clk_gen) is Join(clk_gen) + yield Timer(1) + clk_gen.kill() + + +@cocotb.test() +def test_trigger_with_failing_prime(dut): + """ Test that a trigger failing to prime throws """ + class ABadTrigger(Trigger): + def prime(self, callback): + raise RuntimeError("oops") + + yield Timer(1) + try: + yield ABadTrigger() + except RuntimeError as exc: + assert "oops" in str(exc) + else: + raise TestFailure + + +@cocotb.test() +def test_stack_overflow(dut): + """ + Test against stack overflows when starting many coroutines that terminate + before passing control to the simulator. + """ + # gh-637 + @cocotb.coroutine + def null_coroutine(): + yield NullTrigger() + + for _ in range(10000): + yield null_coroutine() + + yield Timer(100) From 377ae368490bc70a25b037beead312130d5211c8 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 6 Apr 2020 23:38:18 -0500 Subject: [PATCH 2111/2656] Move tests related to Clock into their own module --- tests/test_cases/test_cocotb/Makefile | 1 + tests/test_cases/test_cocotb/test_clock.py | 82 +++++++++++++++++++++ tests/test_cases/test_cocotb/test_cocotb.py | 70 ------------------ 3 files changed, 83 insertions(+), 70 deletions(-) create mode 100644 tests/test_cases/test_cocotb/test_clock.py diff --git a/tests/test_cases/test_cocotb/Makefile b/tests/test_cases/test_cocotb/Makefile index 939988da..9711b707 100644 --- a/tests/test_cases/test_cocotb/Makefile +++ b/tests/test_cases/test_cocotb/Makefile @@ -39,6 +39,7 @@ MODULE := "\ test_generator_coroutines,\ test_timing_triggers,\ test_scheduler,\ + test_clock,\ " ifeq ($(shell python -c "import sys; print(sys.version_info >= (3, 6))"), "True") diff --git a/tests/test_cases/test_cocotb/test_clock.py b/tests/test_cases/test_cocotb/test_clock.py new file mode 100644 index 00000000..9c2e038b --- /dev/null +++ b/tests/test_cases/test_cocotb/test_clock.py @@ -0,0 +1,82 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause +""" +Tests relating to cocotb.clock.Clock +""" +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import Timer, RisingEdge +from cocotb.result import TestFailure +from cocotb.utils import get_sim_time +from math import isclose + + +@cocotb.test(expect_fail=False) +def test_clock_with_units(dut): + clk_1mhz = Clock(dut.clk, 1.0, units='us') + clk_250mhz = Clock(dut.clk, 4.0, units='ns') + + if str(clk_1mhz) != "Clock(1.0 MHz)": + raise TestFailure("{} != 'Clock(1.0 MHz)'".format(str(clk_1mhz))) + else: + dut._log.info('Created clock >{}<'.format(str(clk_1mhz))) + + if str(clk_250mhz) != "Clock(250.0 MHz)": + raise TestFailure("{} != 'Clock(250.0 MHz)'".format(str(clk_250mhz))) + else: + dut._log.info('Created clock >{}<'.format(str(clk_250mhz))) + + clk_gen = cocotb.fork(clk_1mhz.start()) + + start_time_ns = get_sim_time(units='ns') + + yield Timer(1) + + yield RisingEdge(dut.clk) + + edge_time_ns = get_sim_time(units='ns') + if not isclose(edge_time_ns, start_time_ns + 1000.0): + raise TestFailure("Expected a period of 1 us") + + start_time_ns = edge_time_ns + + yield RisingEdge(dut.clk) + edge_time_ns = get_sim_time(units='ns') + if not isclose(edge_time_ns, start_time_ns + 1000.0): + raise TestFailure("Expected a period of 1 us") + + clk_gen.kill() + + clk_gen = cocotb.fork(clk_250mhz.start()) + + start_time_ns = get_sim_time(units='ns') + + yield Timer(1) + + yield RisingEdge(dut.clk) + + edge_time_ns = get_sim_time(units='ns') + if not isclose(edge_time_ns, start_time_ns + 4.0): + raise TestFailure("Expected a period of 4 ns") + + start_time_ns = edge_time_ns + + yield RisingEdge(dut.clk) + edge_time_ns = get_sim_time(units='ns') + if not isclose(edge_time_ns, start_time_ns + 4.0): + raise TestFailure("Expected a period of 4 ns") + + clk_gen.kill() + + +@cocotb.test(expect_fail=False) +def test_anternal_clock(dut): + """Test ability to yield on an external non cocotb coroutine decorated + function""" + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + count = 0 + while count != 100: + yield RisingEdge(dut.clk) + count += 1 + clk_gen.kill() diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 2cbd4153..3b212a8d 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -62,76 +62,6 @@ def clock_gen(clock): clock._log.warning("Clock generator finished!") -@cocotb.test(expect_fail=False) -def test_clock_with_units(dut): - clk_1mhz = Clock(dut.clk, 1.0, units='us') - clk_250mhz = Clock(dut.clk, 4.0, units='ns') - - if str(clk_1mhz) != "Clock(1.0 MHz)": - raise TestFailure("{} != 'Clock(1.0 MHz)'".format(str(clk_1mhz))) - else: - dut._log.info('Created clock >{}<'.format(str(clk_1mhz))) - - if str(clk_250mhz) != "Clock(250.0 MHz)": - raise TestFailure("{} != 'Clock(250.0 MHz)'".format(str(clk_250mhz))) - else: - dut._log.info('Created clock >{}<'.format(str(clk_250mhz))) - - clk_gen = cocotb.fork(clk_1mhz.start()) - - start_time_ns = get_sim_time(units='ns') - - yield Timer(1) - - yield RisingEdge(dut.clk) - - edge_time_ns = get_sim_time(units='ns') - if not isclose(edge_time_ns, start_time_ns + 1000.0): - raise TestFailure("Expected a period of 1 us") - - start_time_ns = edge_time_ns - - yield RisingEdge(dut.clk) - edge_time_ns = get_sim_time(units='ns') - if not isclose(edge_time_ns, start_time_ns + 1000.0): - raise TestFailure("Expected a period of 1 us") - - clk_gen.kill() - - clk_gen = cocotb.fork(clk_250mhz.start()) - - start_time_ns = get_sim_time(units='ns') - - yield Timer(1) - - yield RisingEdge(dut.clk) - - edge_time_ns = get_sim_time(units='ns') - if not isclose(edge_time_ns, start_time_ns + 4.0): - raise TestFailure("Expected a period of 4 ns") - - start_time_ns = edge_time_ns - - yield RisingEdge(dut.clk) - edge_time_ns = get_sim_time(units='ns') - if not isclose(edge_time_ns, start_time_ns + 4.0): - raise TestFailure("Expected a period of 4 ns") - - clk_gen.kill() - - -@cocotb.test(expect_fail=False) -def test_anternal_clock(dut): - """Test ability to yield on an external non cocotb coroutine decorated - function""" - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) - count = 0 - while count != 100: - yield RisingEdge(dut.clk) - count += 1 - clk_gen.kill() - - @cocotb.test() def test_fork_and_monitor(dut, period=1000, clocks=6): cocotb.fork(Clock(dut.clk, period).start()) From e6783ebcb2f763d971bfbc3a70ff2485120030b3 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 6 Apr 2020 23:42:44 -0500 Subject: [PATCH 2112/2656] Move tests for edge-related triggers to their own module --- tests/test_cases/test_cocotb/Makefile | 1 + tests/test_cases/test_cocotb/test_cocotb.py | 227 ---------------- .../test_cocotb/test_edge_triggers.py | 244 ++++++++++++++++++ 3 files changed, 245 insertions(+), 227 deletions(-) create mode 100644 tests/test_cases/test_cocotb/test_edge_triggers.py diff --git a/tests/test_cases/test_cocotb/Makefile b/tests/test_cases/test_cocotb/Makefile index 9711b707..7aeb2749 100644 --- a/tests/test_cases/test_cocotb/Makefile +++ b/tests/test_cases/test_cocotb/Makefile @@ -40,6 +40,7 @@ MODULE := "\ test_timing_triggers,\ test_scheduler,\ test_clock,\ + test_edge_triggers,\ " ifeq ($(shell python -c "import sys; print(sys.version_info >= (3, 6))"), "True") diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 3b212a8d..0fbcc13b 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -62,164 +62,6 @@ def clock_gen(clock): clock._log.warning("Clock generator finished!") -@cocotb.test() -def test_fork_and_monitor(dut, period=1000, clocks=6): - cocotb.fork(Clock(dut.clk, period).start()) - - # Ensure the clock has started - yield RisingEdge(dut.clk) - - timer = Timer(period + 10) - task = cocotb.fork(count_edges_cycles(dut.clk, clocks)) - count = 0 - expect = clocks - 1 - - while True: - result = yield [timer, task.join()] - if count > expect: - raise TestFailure("Task didn't complete in expected time") - if result is timer: - dut._log.info("Count %d: Task still running" % count) - count += 1 - else: - break - if count != expect: - raise TestFailure("Expected to monitor the task %d times but got %d" % - (expect, count)) - if result != clocks: - raise TestFailure("Expected task to return %d but got %s" % - (clocks, repr(result))) - - -@cocotb.coroutine -def count_edges_cycles(signal, edges): - edge = RisingEdge(signal) - for i in range(edges): - yield edge - signal._log.info("Rising edge %d detected" % i) - signal._log.info("Finished, returning %d" % edges) - return edges - - -@cocotb.coroutine -def do_single_edge_check(dut, level): - """Do test for rising edge""" - old_value = dut.clk.value.integer - dut._log.info("Value of %s is %d" % (dut.clk, old_value)) - if old_value is level: - raise TestError("%s not to %d start with" % (dut.clk, not level)) - if level == 1: - yield RisingEdge(dut.clk) - else: - yield FallingEdge(dut.clk) - new_value = dut.clk.value.integer - dut._log.info("Value of %s is %d" % (dut.clk, new_value)) - if new_value is not level: - raise TestError("%s not %d at end" % (dut.clk, level)) - - -@cocotb.test() -def test_rising_edge(dut): - """Test that a rising edge can be yielded on""" - dut.clk <= 0 - yield Timer(1) - test = cocotb.fork(do_single_edge_check(dut, 1)) - yield Timer(10) - dut.clk <= 1 - fail_timer = Timer(1000) - result = yield [fail_timer, test.join()] - if result is fail_timer: - raise TestError("Test timed out") - - -@cocotb.test() -def test_falling_edge(dut): - """Test that a falling edge can be yielded on""" - dut.clk <= 1 - yield Timer(1) - test = cocotb.fork(do_single_edge_check(dut, 0)) - yield Timer(10) - dut.clk <= 0 - fail_timer = Timer(1000) - result = yield [fail_timer, test.join()] - if result is fail_timer: - raise TestError("Test timed out") - - -@cocotb.test() -def test_either_edge(dut): - """Test that either edge can be triggered on""" - dut.clk <= 0 - yield Timer(1) - dut.clk <= 1 - yield Edge(dut.clk) - if dut.clk.value.integer != 1: - raise TestError("Value should be 0") - yield Timer(10) - dut.clk <= 0 - yield Edge(dut.clk) - if dut.clk.value.integer != 0: - raise TestError("Value should be 0") - yield Timer(10) - dut.clk <= 1 - yield Edge(dut.clk) - if dut.clk.value.integer != 1: - raise TestError("Value should be 0") - yield Timer(10) - dut.clk <= 0 - yield Edge(dut.clk) - if dut.clk.value.integer != 0: - raise TestError("Value should be 0") - yield Timer(10) - dut.clk <= 1 - yield Edge(dut.clk) - if dut.clk.value.integer != 1: - raise TestError("Value should be 0") - yield Timer(10) - dut.clk <= 0 - yield Edge(dut.clk) - if dut.clk.value.integer != 0: - raise TestError("Value should be 0") - - -@cocotb.coroutine -def do_clock(dut, limit, period): - """Simple clock with a limit""" - wait_period = period / 2 - while limit: - yield Timer(wait_period) - dut.clk <= 0 - yield Timer(wait_period) - dut.clk <= 1 - limit -= 1 - - -@cocotb.coroutine -def do_edge_count(dut, signal): - """Count the edges""" - global edges_seen - count = 0 - while True: - yield RisingEdge(signal) - edges_seen += 1 - - -@cocotb.test() -def test_edge_count(dut): - """Count the number of edges is as expected""" - global edges_seen - edges_seen = 0 - clk_period = 100 - edge_count = 10 - clock = cocotb.fork(do_clock(dut, edge_count, clk_period)) - test = cocotb.fork(do_edge_count(dut, dut.clk)) - - yield Timer(clk_period * (edge_count + 1)) - - if edge_count is not edges_seen: - raise TestFailure("Correct edge count failed - saw %d wanted %d" % - (edges_seen, edge_count)) - class StrCallCounter(object): def __init__(self): self.str_counter = 0 @@ -250,58 +92,6 @@ def test_logging_with_args(dut): yield Timer(100) # Make it do something with time -@cocotb.test() -def test_clock_cycles(dut): - """ - Test the ClockCycles Trigger - """ - - clk = dut.clk - - clk_gen = cocotb.fork(Clock(clk, 100).start()) - - yield RisingEdge(clk) - - dut._log.info("After one edge") - - yield ClockCycles(clk, 10) - - dut._log.info("After 10 edges") - - -@cocotb.test() -def test_edge_identity(dut): - """ - Test that Edge triggers returns the same object each time - """ - - re = RisingEdge(dut.clk) - fe = FallingEdge(dut.clk) - e = Edge(dut.clk) - - assert re is RisingEdge(dut.clk) - assert fe is FallingEdge(dut.clk) - assert e is Edge(dut.clk) - - # check they are all unique - assert len({re, fe, e}) == 3 - yield Timer(1) - - -@cocotb.test() -def test_singleton_isinstance(dut): - """ - Test that the result of trigger expression have a predictable type - """ - assert isinstance(RisingEdge(dut.clk), RisingEdge) - assert isinstance(FallingEdge(dut.clk), FallingEdge) - assert isinstance(Edge(dut.clk), Edge) - assert isinstance(NextTimeStep(), NextTimeStep) - assert isinstance(ReadOnly(), ReadOnly) - assert isinstance(ReadWrite(), ReadWrite) - - yield Timer(1) - @cocotb.test() def test_lessthan_raises_error(dut): @@ -342,23 +132,6 @@ def _check_traceback(running_coro, exc_type, pattern): ) -@cocotb.test() -def test_clock_cycles_forked(dut): - """ Test that ClockCycles can be used in forked coroutines """ - # gh-520 - - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) - - @cocotb.coroutine - def wait_ten(): - yield ClockCycles(dut.clk, 10) - - a = cocotb.fork(wait_ten()) - b = cocotb.fork(wait_ten()) - yield a.join() - yield b.join() - - @cocotb.test() def test_bad_attr(dut): yield cocotb.triggers.NullTrigger() diff --git a/tests/test_cases/test_cocotb/test_edge_triggers.py b/tests/test_cases/test_cocotb/test_edge_triggers.py new file mode 100644 index 00000000..ca567eef --- /dev/null +++ b/tests/test_cases/test_cocotb/test_edge_triggers.py @@ -0,0 +1,244 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause +""" +Tests for edge triggers + +* Edge +* RisingEdge +* FallingEdge +* ClockCycles +""" +import cocotb +from cocotb.triggers import RisingEdge, FallingEdge, Edge, Timer, NextTimeStep, ReadWrite, ReadOnly, ClockCycles +from cocotb.clock import Clock +from cocotb.result import TestError, TestFailure + + +@cocotb.coroutine +def count_edges_cycles(signal, edges): + edge = RisingEdge(signal) + for i in range(edges): + yield edge + signal._log.info("Rising edge %d detected" % i) + signal._log.info("Finished, returning %d" % edges) + return edges + + +@cocotb.coroutine +def do_single_edge_check(dut, level): + """Do test for rising edge""" + old_value = dut.clk.value.integer + dut._log.info("Value of %s is %d" % (dut.clk, old_value)) + if old_value is level: + raise TestError("%s not to %d start with" % (dut.clk, not level)) + if level == 1: + yield RisingEdge(dut.clk) + else: + yield FallingEdge(dut.clk) + new_value = dut.clk.value.integer + dut._log.info("Value of %s is %d" % (dut.clk, new_value)) + if new_value is not level: + raise TestError("%s not %d at end" % (dut.clk, level)) + + +@cocotb.test() +def test_rising_edge(dut): + """Test that a rising edge can be yielded on""" + dut.clk <= 0 + yield Timer(1) + test = cocotb.fork(do_single_edge_check(dut, 1)) + yield Timer(10) + dut.clk <= 1 + fail_timer = Timer(1000) + result = yield [fail_timer, test.join()] + if result is fail_timer: + raise TestError("Test timed out") + + +@cocotb.test() +def test_falling_edge(dut): + """Test that a falling edge can be yielded on""" + dut.clk <= 1 + yield Timer(1) + test = cocotb.fork(do_single_edge_check(dut, 0)) + yield Timer(10) + dut.clk <= 0 + fail_timer = Timer(1000) + result = yield [fail_timer, test.join()] + if result is fail_timer: + raise TestError("Test timed out") + + +@cocotb.test() +def test_either_edge(dut): + """Test that either edge can be triggered on""" + dut.clk <= 0 + yield Timer(1) + dut.clk <= 1 + yield Edge(dut.clk) + if dut.clk.value.integer != 1: + raise TestError("Value should be 0") + yield Timer(10) + dut.clk <= 0 + yield Edge(dut.clk) + if dut.clk.value.integer != 0: + raise TestError("Value should be 0") + yield Timer(10) + dut.clk <= 1 + yield Edge(dut.clk) + if dut.clk.value.integer != 1: + raise TestError("Value should be 0") + yield Timer(10) + dut.clk <= 0 + yield Edge(dut.clk) + if dut.clk.value.integer != 0: + raise TestError("Value should be 0") + yield Timer(10) + dut.clk <= 1 + yield Edge(dut.clk) + if dut.clk.value.integer != 1: + raise TestError("Value should be 0") + yield Timer(10) + dut.clk <= 0 + yield Edge(dut.clk) + if dut.clk.value.integer != 0: + raise TestError("Value should be 0") + + +@cocotb.test() +def test_fork_and_monitor(dut, period=1000, clocks=6): + cocotb.fork(Clock(dut.clk, period).start()) + + # Ensure the clock has started + yield RisingEdge(dut.clk) + + timer = Timer(period + 10) + task = cocotb.fork(count_edges_cycles(dut.clk, clocks)) + count = 0 + expect = clocks - 1 + + while True: + result = yield [timer, task.join()] + if count > expect: + raise TestFailure("Task didn't complete in expected time") + if result is timer: + dut._log.info("Count %d: Task still running" % count) + count += 1 + else: + break + if count != expect: + raise TestFailure("Expected to monitor the task %d times but got %d" % + (expect, count)) + if result != clocks: + raise TestFailure("Expected task to return %d but got %s" % + (clocks, repr(result))) + + +@cocotb.coroutine +def do_clock(dut, limit, period): + """Simple clock with a limit""" + wait_period = period / 2 + while limit: + yield Timer(wait_period) + dut.clk <= 0 + yield Timer(wait_period) + dut.clk <= 1 + limit -= 1 + + +@cocotb.coroutine +def do_edge_count(dut, signal): + """Count the edges""" + global edges_seen + count = 0 + while True: + yield RisingEdge(signal) + edges_seen += 1 + + +@cocotb.test() +def test_edge_count(dut): + """Count the number of edges is as expected""" + global edges_seen + edges_seen = 0 + clk_period = 100 + edge_count = 10 + clock = cocotb.fork(do_clock(dut, edge_count, clk_period)) + test = cocotb.fork(do_edge_count(dut, dut.clk)) + + yield Timer(clk_period * (edge_count + 1)) + + if edge_count is not edges_seen: + raise TestFailure("Correct edge count failed - saw %d wanted %d" % + (edges_seen, edge_count)) + + +@cocotb.test() +def test_edge_identity(dut): + """ + Test that Edge triggers returns the same object each time + """ + + re = RisingEdge(dut.clk) + fe = FallingEdge(dut.clk) + e = Edge(dut.clk) + + assert re is RisingEdge(dut.clk) + assert fe is FallingEdge(dut.clk) + assert e is Edge(dut.clk) + + # check they are all unique + assert len({re, fe, e}) == 3 + yield Timer(1) + + +@cocotb.test() +def test_singleton_isinstance(dut): + """ + Test that the result of trigger expression have a predictable type + """ + assert isinstance(RisingEdge(dut.clk), RisingEdge) + assert isinstance(FallingEdge(dut.clk), FallingEdge) + assert isinstance(Edge(dut.clk), Edge) + assert isinstance(NextTimeStep(), NextTimeStep) + assert isinstance(ReadOnly(), ReadOnly) + assert isinstance(ReadWrite(), ReadWrite) + + yield Timer(1) + + +@cocotb.test() +def test_clock_cycles(dut): + """ + Test the ClockCycles Trigger + """ + + clk = dut.clk + + clk_gen = cocotb.fork(Clock(clk, 100).start()) + + yield RisingEdge(clk) + + dut._log.info("After one edge") + + yield ClockCycles(clk, 10) + + dut._log.info("After 10 edges") + + +@cocotb.test() +def test_clock_cycles_forked(dut): + """ Test that ClockCycles can be used in forked coroutines """ + # gh-520 + + clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + + @cocotb.coroutine + def wait_ten(): + yield ClockCycles(dut.clk, 10) + + a = cocotb.fork(wait_ten()) + b = cocotb.fork(wait_ten()) + yield a.join() + yield b.join() From f844b11ee729d9cc119c5948a4b1a11da5169b86 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 6 Apr 2020 23:44:54 -0500 Subject: [PATCH 2113/2656] Move tests related to async coros to their own module --- tests/test_cases/test_cocotb/Makefile | 1 + .../test_cocotb/test_async_coroutines.py | 137 ++++++++++++++++++ tests/test_cases/test_cocotb/test_cocotb.py | 127 ---------------- 3 files changed, 138 insertions(+), 127 deletions(-) create mode 100644 tests/test_cases/test_cocotb/test_async_coroutines.py diff --git a/tests/test_cases/test_cocotb/Makefile b/tests/test_cases/test_cocotb/Makefile index 7aeb2749..ce36cd19 100644 --- a/tests/test_cases/test_cocotb/Makefile +++ b/tests/test_cases/test_cocotb/Makefile @@ -41,6 +41,7 @@ MODULE := "\ test_scheduler,\ test_clock,\ test_edge_triggers,\ + test_async_coroutines,\ " ifeq ($(shell python -c "import sys; print(sys.version_info >= (3, 6))"), "True") diff --git a/tests/test_cases/test_cocotb/test_async_coroutines.py b/tests/test_cases/test_cocotb/test_async_coroutines.py new file mode 100644 index 00000000..8c06b898 --- /dev/null +++ b/tests/test_cases/test_cocotb/test_async_coroutines.py @@ -0,0 +1,137 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause +""" +Test function and substitutablility of async coroutines +""" + +import cocotb +from cocotb.triggers import Timer +from cocotb.outcomes import Value, Error + + +class produce: + """ Test helpers that produce a value / exception in different ways """ + @staticmethod + @cocotb.coroutine + def coro(outcome): + yield Timer(1) + return outcome.get() + + @staticmethod + @cocotb.coroutine + async def async_annotated(outcome): + await Timer(1) + return outcome.get() + + @staticmethod + async def async_(outcome): + await Timer(1) + return outcome.get() + + +class SomeException(Exception): + """ Custom exception to test for that can't be thrown by internals """ + pass + + +@cocotb.test() +def test_annotated_async_from_coro(dut): + """ + Test that normal coroutines are able to call async functions annotated + with `@cocotb.coroutine` + """ + v = yield produce.async_annotated(Value(1)) + assert v == 1 + + try: + yield produce.async_annotated(Error(SomeException)) + except SomeException: + pass + else: + assert False + + +@cocotb.test() +async def test_annotated_async_from_async(dut): + """ Test that async coroutines are able to call themselves """ + v = await produce.async_annotated(Value(1)) + assert v == 1 + + try: + await produce.async_annotated(Error(SomeException)) + except SomeException: + pass + else: + assert False + + +@cocotb.test() +async def test_async_from_async(dut): + """ Test that async coroutines are able to call raw async functions """ + v = await produce.async_(Value(1)) + assert v == 1 + + try: + await produce.async_(Error(SomeException)) + except SomeException: + pass + else: + assert False + + +@cocotb.test() +async def test_coro_from_async(dut): + """ Test that async coroutines are able to call regular ones """ + v = await produce.coro(Value(1)) + assert v == 1 + + try: + await produce.coro(Error(SomeException)) + except SomeException: + pass + else: + assert False + + +@cocotb.test() +async def test_trigger_await_gives_self(dut): + """ Test that await returns the trigger itself for triggers """ + t = Timer(1) + t2 = await t + assert t2 is t + + +@cocotb.test() +async def test_await_causes_start(dut): + """ Test that an annotated async coroutine gets marked as started """ + coro = produce.async_annotated(Value(1)) + assert not coro.has_started() + await coro + assert coro.has_started() + + +@cocotb.test() +def test_undecorated_coroutine_fork(dut): + ran = False + + async def example(): + nonlocal ran + await cocotb.triggers.Timer(1, 'ns') + ran = True + + yield cocotb.fork(example()).join() + assert ran + + +@cocotb.test() +def test_undecorated_coroutine_yield(dut): + ran = False + + async def example(): + nonlocal ran + await cocotb.triggers.Timer(1, 'ns') + ran = True + + yield example() + assert ran diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 0fbcc13b..1c59b94c 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -143,133 +143,6 @@ def test_bad_attr(dut): assert False, "Expected AttributeError" -class produce: - """ Test helpers that produce a value / exception in different ways """ - @staticmethod - @cocotb.coroutine - def coro(outcome): - yield Timer(1) - return outcome.get() - - @staticmethod - @cocotb.coroutine - async def async_annotated(outcome): - await Timer(1) - return outcome.get() - - @staticmethod - async def async_(outcome): - await Timer(1) - return outcome.get() - - -class SomeException(Exception): - """ Custom exception to test for that can't be thrown by internals """ - pass - - -@cocotb.test() -def test_annotated_async_from_coro(dut): - """ - Test that normal coroutines are able to call async functions annotated - with `@cocotb.coroutine` - """ - v = yield produce.async_annotated(Value(1)) - assert v == 1 - - try: - yield produce.async_annotated(Error(SomeException)) - except SomeException: - pass - else: - assert False - - -@cocotb.test() -async def test_annotated_async_from_async(dut): - """ Test that async coroutines are able to call themselves """ - v = await produce.async_annotated(Value(1)) - assert v == 1 - - try: - await produce.async_annotated(Error(SomeException)) - except SomeException: - pass - else: - assert False - - -@cocotb.test() -async def test_async_from_async(dut): - """ Test that async coroutines are able to call raw async functions """ - v = await produce.async_(Value(1)) - assert v == 1 - - try: - await produce.async_(Error(SomeException)) - except SomeException: - pass - else: - assert False - - -@cocotb.test() -async def test_coro_from_async(dut): - """ Test that async coroutines are able to call regular ones """ - v = await produce.coro(Value(1)) - assert v == 1 - - try: - await produce.coro(Error(SomeException)) - except SomeException: - pass - else: - assert False - - -@cocotb.test() -async def test_trigger_await_gives_self(dut): - """ Test that await returns the trigger itself for triggers """ - t = Timer(1) - t2 = await t - assert t2 is t - - -@cocotb.test() -async def test_await_causes_start(dut): - """ Test that an annotated async coroutine gets marked as started """ - coro = produce.async_annotated(Value(1)) - assert not coro.has_started() - await coro - assert coro.has_started() - - -@cocotb.test() -def test_undecorated_coroutine_fork(dut): - ran = False - - async def example(): - nonlocal ran - await cocotb.triggers.Timer(1, 'ns') - ran = True - - yield cocotb.fork(example()).join() - assert ran - - -@cocotb.test() -def test_undecorated_coroutine_yield(dut): - ran = False - - async def example(): - nonlocal ran - await cocotb.triggers.Timer(1, 'ns') - ran = True - - yield example() - assert ran - - # strings are not supported on Icarus @cocotb.test(skip=cocotb.SIM_NAME.lower().startswith("icarus")) async def test_string_handle_takes_bytes(dut): From 494eb55e818a063a44dd66926acbf5c782a360e1 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Tue, 7 Apr 2020 00:24:19 -0500 Subject: [PATCH 2114/2656] Move tests related to handles to their own module --- tests/test_cases/test_cocotb/Makefile | 1 + tests/test_cases/test_cocotb/test_cocotb.py | 40 ----------------- tests/test_cases/test_cocotb/test_handle.py | 48 +++++++++++++++++++++ 3 files changed, 49 insertions(+), 40 deletions(-) create mode 100644 tests/test_cases/test_cocotb/test_handle.py diff --git a/tests/test_cases/test_cocotb/Makefile b/tests/test_cases/test_cocotb/Makefile index ce36cd19..1043c7bc 100644 --- a/tests/test_cases/test_cocotb/Makefile +++ b/tests/test_cases/test_cocotb/Makefile @@ -42,6 +42,7 @@ MODULE := "\ test_clock,\ test_edge_triggers,\ test_async_coroutines,\ + test_handle,\ " ifeq ($(shell python -c "import sys; print(sys.version_info >= (3, 6))"), "True") diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 1c59b94c..1c395d7a 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -93,25 +93,6 @@ def test_logging_with_args(dut): yield Timer(100) # Make it do something with time -@cocotb.test() -def test_lessthan_raises_error(dut): - """ - Test that trying to use <= as if it were a comparison produces an error - """ - ret = dut.stream_in_data <= 0x12 - try: - bool(ret) - except TypeError: - pass - else: - raise TestFailure( - "No exception was raised when confusing comparison with assignment" - ) - - # to make this a generator - if False: yield - - @cocotb.coroutine def _check_traceback(running_coro, exc_type, pattern): try: @@ -130,24 +111,3 @@ def _check_traceback(running_coro, exc_type, pattern): "{}" ).format(tb_text, pattern) ) - - -@cocotb.test() -def test_bad_attr(dut): - yield cocotb.triggers.NullTrigger() - try: - _ = dut.stream_in_data.whoops - except AttributeError as e: - assert 'whoops' in str(e) - else: - assert False, "Expected AttributeError" - - -# strings are not supported on Icarus -@cocotb.test(skip=cocotb.SIM_NAME.lower().startswith("icarus")) -async def test_string_handle_takes_bytes(dut): - dut.string_input_port.value = b"bytes" - await cocotb.triggers.Timer(10, 'ns') - val = dut.string_input_port.value - assert isinstance(val, bytes) - assert val == b"bytes" diff --git a/tests/test_cases/test_cocotb/test_handle.py b/tests/test_cases/test_cocotb/test_handle.py new file mode 100644 index 00000000..ee0dc886 --- /dev/null +++ b/tests/test_cases/test_cocotb/test_handle.py @@ -0,0 +1,48 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause +""" +Tests for handles +""" +import cocotb +from cocotb.result import TestFailure + + +@cocotb.test() +def test_lessthan_raises_error(dut): + """ + Test that trying to use <= as if it were a comparison produces an error + """ + ret = dut.stream_in_data <= 0x12 + try: + bool(ret) + except TypeError: + pass + else: + raise TestFailure( + "No exception was raised when confusing comparison with assignment" + ) + + # to make this a generator + if False: yield + + +@cocotb.test() +def test_bad_attr(dut): + yield cocotb.triggers.NullTrigger() + try: + _ = dut.stream_in_data.whoops + except AttributeError as e: + assert 'whoops' in str(e) + else: + assert False, "Expected AttributeError" + + +# strings are not supported on Icarus +@cocotb.test(skip=cocotb.SIM_NAME.lower().startswith("icarus")) +async def test_string_handle_takes_bytes(dut): + dut.string_input_port.value = b"bytes" + await cocotb.triggers.Timer(10, 'ns') + val = dut.string_input_port.value + assert isinstance(val, bytes) + assert val == b"bytes" From 49fc0100f7a6a2a77196fa7ab476e9072f0f5dfc Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Tue, 7 Apr 2020 00:40:51 -0500 Subject: [PATCH 2115/2656] Move tests related to logging to their own module --- tests/test_cases/test_cocotb/Makefile | 1 + tests/test_cases/test_cocotb/test_cocotb.py | 31 --------------- tests/test_cases/test_cocotb/test_logging.py | 41 ++++++++++++++++++++ 3 files changed, 42 insertions(+), 31 deletions(-) create mode 100644 tests/test_cases/test_cocotb/test_logging.py diff --git a/tests/test_cases/test_cocotb/Makefile b/tests/test_cases/test_cocotb/Makefile index 1043c7bc..092f9d84 100644 --- a/tests/test_cases/test_cocotb/Makefile +++ b/tests/test_cases/test_cocotb/Makefile @@ -43,6 +43,7 @@ MODULE := "\ test_edge_triggers,\ test_async_coroutines,\ test_handle,\ + test_logging,\ " ifeq ($(shell python -c "import sys; print(sys.version_info >= (3, 6))"), "True") diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py index 1c395d7a..612436cb 100644 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ b/tests/test_cases/test_cocotb/test_cocotb.py @@ -62,37 +62,6 @@ def clock_gen(clock): clock._log.warning("Clock generator finished!") -class StrCallCounter(object): - def __init__(self): - self.str_counter = 0 - - def __str__(self): - self.str_counter += 1 - return "__str__ called %d time(s)" % self.str_counter - -@cocotb.test() -def test_logging_with_args(dut): - counter = StrCallCounter() - dut._log.setLevel(logging.INFO) # To avoid logging debug message, to make next line run without error - dut._log.debug("%s", counter) - assert counter.str_counter == 0 - - dut._log.info("%s", counter) - assert counter.str_counter == 1 - - # now try again on the root cocotb logger, which unlike nested loggers - # is captured - counter = StrCallCounter() - cocotb.log.info("%s", counter) - assert counter.str_counter == 2 # once for stdout, once for captured logs - - dut._log.info("No substitution") - - dut._log.warning("Testing multiple line\nmessage") - - yield Timer(100) # Make it do something with time - - @cocotb.coroutine def _check_traceback(running_coro, exc_type, pattern): try: diff --git a/tests/test_cases/test_cocotb/test_logging.py b/tests/test_cases/test_cocotb/test_logging.py new file mode 100644 index 00000000..07d2583b --- /dev/null +++ b/tests/test_cases/test_cocotb/test_logging.py @@ -0,0 +1,41 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause +""" +Tests for the cocotb logger +""" +import cocotb +from cocotb.triggers import Timer +import logging + + +class StrCallCounter(object): + def __init__(self): + self.str_counter = 0 + + def __str__(self): + self.str_counter += 1 + return "__str__ called %d time(s)" % self.str_counter + + +@cocotb.test() +def test_logging_with_args(dut): + counter = StrCallCounter() + dut._log.setLevel(logging.INFO) # To avoid logging debug message, to make next line run without error + dut._log.debug("%s", counter) + assert counter.str_counter == 0 + + dut._log.info("%s", counter) + assert counter.str_counter == 1 + + # now try again on the root cocotb logger, which unlike nested loggers + # is captured + counter = StrCallCounter() + cocotb.log.info("%s", counter) + assert counter.str_counter == 2 # once for stdout, once for captured logs + + dut._log.info("No substitution") + + dut._log.warning("Testing multiple line\nmessage") + + yield Timer(100) # Make it do something with time From 78883e6b8add0e2487f9a55fc4c1beae1459e67e Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Tue, 7 Apr 2020 00:46:06 -0500 Subject: [PATCH 2116/2656] Rename test_cocotb.py to common and cleanup imports --- tests/test_cases/test_cocotb/Makefile | 32 +------- tests/test_cases/test_cocotb/common.py | 43 ++++++++++ tests/test_cases/test_cocotb/test_cocotb.py | 82 ------------------- .../test_concurrency_primitives.py | 4 +- .../test_cocotb/test_generator_coroutines.py | 6 +- .../test_cases/test_cocotb/test_scheduler.py | 2 +- tests/test_cases/test_cocotb/test_tests.py | 2 +- 7 files changed, 53 insertions(+), 118 deletions(-) create mode 100644 tests/test_cases/test_cocotb/common.py delete mode 100644 tests/test_cases/test_cocotb/test_cocotb.py diff --git a/tests/test_cases/test_cocotb/Makefile b/tests/test_cases/test_cocotb/Makefile index 092f9d84..58e125a8 100644 --- a/tests/test_cases/test_cocotb/Makefile +++ b/tests/test_cases/test_cocotb/Makefile @@ -1,36 +1,10 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause include ../../designs/sample_module/Makefile MODULE := "\ - test_cocotb,\ test_deprecated,\ test_doctests,\ test_synchronization_primitives,\ diff --git a/tests/test_cases/test_cocotb/common.py b/tests/test_cases/test_cocotb/common.py new file mode 100644 index 00000000..26a50a11 --- /dev/null +++ b/tests/test_cases/test_cocotb/common.py @@ -0,0 +1,43 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause +""" +Common utilities shared my many tests in this directory +""" +import re +import traceback + +import cocotb +from cocotb.result import TestFailure +from cocotb.triggers import Timer + + +@cocotb.coroutine +def clock_gen(clock): + """Example clock gen for test use""" + for i in range(5): + clock <= 0 + yield Timer(100) + clock <= 1 + yield Timer(100) + clock._log.warning("Clock generator finished!") + + +@cocotb.coroutine +def _check_traceback(running_coro, exc_type, pattern): + try: + yield running_coro + except exc_type: + tb_text = traceback.format_exc() + else: + raise TestFailure("Exception was not raised") + + if not re.match(pattern, tb_text): + raise TestFailure( + ( + "Traceback didn't match - got:\n\n" + "{}\n" + "which did not match the pattern:\n\n" + "{}" + ).format(tb_text, pattern) + ) diff --git a/tests/test_cases/test_cocotb/test_cocotb.py b/tests/test_cases/test_cocotb/test_cocotb.py deleted file mode 100644 index 612436cb..00000000 --- a/tests/test_cases/test_cocotb/test_cocotb.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2013, 2018 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import logging -import re -import textwrap -import traceback -from fractions import Fraction -from decimal import Decimal -from math import isclose - -""" -A set of tests that demonstrate cocotb functionality - -Also used as regression test of cocotb capabilities -""" - -import cocotb -from cocotb.triggers import (Timer, Join, RisingEdge, FallingEdge, Edge, - ReadOnly, ReadWrite, ClockCycles, NextTimeStep, - NullTrigger, Combine, Event, First, Trigger, Lock) -from cocotb.clock import Clock -from cocotb.result import TestFailure, TestError -from cocotb.utils import get_sim_time -from cocotb.outcomes import Value, Error - - -@cocotb.coroutine -def clock_gen(clock): - """Example clock gen for test use""" - for i in range(5): - clock <= 0 - yield Timer(100) - clock <= 1 - yield Timer(100) - clock._log.warning("Clock generator finished!") - - -@cocotb.coroutine -def _check_traceback(running_coro, exc_type, pattern): - try: - yield running_coro - except exc_type: - tb_text = traceback.format_exc() - else: - raise TestFailure("Exception was not raised") - - if not re.match(pattern, tb_text): - raise TestFailure( - ( - "Traceback didn't match - got:\n\n" - "{}\n" - "which did not match the pattern:\n\n" - "{}" - ).format(tb_text, pattern) - ) diff --git a/tests/test_cases/test_cocotb/test_concurrency_primitives.py b/tests/test_cases/test_cocotb/test_concurrency_primitives.py index 18aedb23..fb4f77b3 100644 --- a/tests/test_cases/test_cocotb/test_concurrency_primitives.py +++ b/tests/test_cases/test_cocotb/test_concurrency_primitives.py @@ -7,7 +7,7 @@ import cocotb from cocotb.triggers import Timer, First, Event, Combine import textwrap -from test_cocotb import _check_traceback +from common import _check_traceback @cocotb.test() @@ -116,7 +116,7 @@ def raise_soon(): # that when changed, it doesn't become harder to read. expected = textwrap.dedent(r""" Traceback \(most recent call last\): - File ".*test_cocotb\.py", line \d+, in _check_traceback + File ".*common\.py", line \d+, in _check_traceback yield running_coro File ".*test_concurrency_primitives\.py", line \d+, in raise_soon yield cocotb\.triggers\.First\(raise_inner\(\)\) diff --git a/tests/test_cases/test_cocotb/test_generator_coroutines.py b/tests/test_cases/test_cocotb/test_generator_coroutines.py index f16d215e..f720b41f 100644 --- a/tests/test_cases/test_cocotb/test_generator_coroutines.py +++ b/tests/test_cases/test_cocotb/test_generator_coroutines.py @@ -7,7 +7,7 @@ import cocotb from cocotb.triggers import Timer from cocotb.result import TestFailure -from test_cocotb import clock_gen, _check_traceback +from common import clock_gen, _check_traceback import textwrap @@ -169,7 +169,7 @@ def raise_soon(): # that when changed, it doesn't become harder to read. expected = textwrap.dedent(r""" Traceback \(most recent call last\): - File ".*test_cocotb\.py", line \d+, in _check_traceback + File ".*common\.py", line \d+, in _check_traceback yield running_coro File ".*test_generator_coroutines\.py", line \d+, in raise_soon yield raise_inner\(\) @@ -198,7 +198,7 @@ def raise_soon(): # that when changed, it doesn't become harder to read. expected = textwrap.dedent(r""" Traceback \(most recent call last\): - File ".*test_cocotb\.py", line \d+, in _check_traceback + File ".*common\.py", line \d+, in _check_traceback yield running_coro File ".*test_generator_coroutines\.py", line \d+, in raise_soon yield coro\.join\(\) diff --git a/tests/test_cases/test_cocotb/test_scheduler.py b/tests/test_cases/test_cocotb/test_scheduler.py index 649bf9b4..d27ddda1 100644 --- a/tests/test_cases/test_cocotb/test_scheduler.py +++ b/tests/test_cases/test_cocotb/test_scheduler.py @@ -13,7 +13,7 @@ from cocotb.triggers import Join, Timer, RisingEdge, Trigger, NullTrigger from cocotb.result import TestFailure from cocotb.clock import Clock -from test_cocotb import clock_gen +from common import clock_gen test_flag = False diff --git a/tests/test_cases/test_cocotb/test_tests.py b/tests/test_cases/test_cocotb/test_tests.py index 1793c293..170f0662 100644 --- a/tests/test_cases/test_cocotb/test_tests.py +++ b/tests/test_cases/test_cocotb/test_tests.py @@ -11,7 +11,7 @@ import cocotb from cocotb.triggers import Timer from cocotb.result import TestFailure -from test_cocotb import clock_gen +from common import clock_gen @cocotb.test(expect_error=True) From ad1e718eab6a107b56c47032f53f282f7d2dbae0 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Thu, 9 Apr 2020 13:01:16 -0500 Subject: [PATCH 2117/2656] Split test_singleton_instance for edge and timing triggers --- tests/test_cases/test_cocotb/test_edge_triggers.py | 5 +---- tests/test_cases/test_cocotb/test_timing_triggers.py | 12 +++++++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/tests/test_cases/test_cocotb/test_edge_triggers.py b/tests/test_cases/test_cocotb/test_edge_triggers.py index ca567eef..34c6bc8b 100644 --- a/tests/test_cases/test_cocotb/test_edge_triggers.py +++ b/tests/test_cases/test_cocotb/test_edge_triggers.py @@ -10,7 +10,7 @@ * ClockCycles """ import cocotb -from cocotb.triggers import RisingEdge, FallingEdge, Edge, Timer, NextTimeStep, ReadWrite, ReadOnly, ClockCycles +from cocotb.triggers import RisingEdge, FallingEdge, Edge, Timer, ClockCycles from cocotb.clock import Clock from cocotb.result import TestError, TestFailure @@ -201,9 +201,6 @@ def test_singleton_isinstance(dut): assert isinstance(RisingEdge(dut.clk), RisingEdge) assert isinstance(FallingEdge(dut.clk), FallingEdge) assert isinstance(Edge(dut.clk), Edge) - assert isinstance(NextTimeStep(), NextTimeStep) - assert isinstance(ReadOnly(), ReadOnly) - assert isinstance(ReadWrite(), ReadWrite) yield Timer(1) diff --git a/tests/test_cases/test_cocotb/test_timing_triggers.py b/tests/test_cases/test_cocotb/test_timing_triggers.py index 285521c5..2e312abe 100644 --- a/tests/test_cases/test_cocotb/test_timing_triggers.py +++ b/tests/test_cases/test_cocotb/test_timing_triggers.py @@ -11,7 +11,7 @@ * with_timeout """ import cocotb -from cocotb.triggers import Timer, RisingEdge, ReadOnly, ReadWrite, Join +from cocotb.triggers import Timer, RisingEdge, ReadOnly, ReadWrite, Join, NextTimeStep from cocotb.utils import get_sim_time from cocotb.result import TestFailure from cocotb.clock import Clock @@ -233,3 +233,13 @@ def test_readwrite(dut): yield Timer(1) dut.clk <= 1 yield ReadWrite() + + +@cocotb.test() +async def test_singleton_isinstance(dut): + """ + Test that the result of trigger expression have a predictable type + """ + assert isinstance(NextTimeStep(), NextTimeStep) + assert isinstance(ReadOnly(), ReadOnly) + assert isinstance(ReadWrite(), ReadWrite) From e6bc8958a3588cd4b36a604205b62879d005b688 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 10 Apr 2020 10:39:08 +0200 Subject: [PATCH 2118/2656] Restructure Documentation (#1482) This documentation restructuring tries to follow https://www.divio.com/blog/documentation/ (by Daniele Procida). Other media about the same topic: - https://ep2018.europython.eu/media/conference/slides/get-your-documentation-right.pdf - https://www.youtube.com/watch?v=t4vKPhjcMZg - A good example: http://docs.django-cms.org/en/latest/contributing/documentation.html#contributing-documentation See also https://github.com/cocotb/cocotb/wiki/Howto:-Writing-Documentation This also: * Extends info on simulator specifics * Moves the TOC entirely to the sidebar * Folds introduction.rst into index.rst --- documentation/source/conf.py | 3 +- documentation/source/index.rst | 220 ++++++++++++++++-- documentation/source/introduction.rst | 90 ------- .../source/newsfragments/1482.doc.rst | 2 + documentation/source/simulator_support.rst | 12 +- 5 files changed, 213 insertions(+), 114 deletions(-) delete mode 100644 documentation/source/introduction.rst create mode 100644 documentation/source/newsfragments/1482.doc.rst diff --git a/documentation/source/conf.py b/documentation/source/conf.py index 73c2d327..ba5ea6c4 100644 --- a/documentation/source/conf.py +++ b/documentation/source/conf.py @@ -303,8 +303,7 @@ # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False -# For now show the todos -todo_include_todos = True +todo_include_todos = False # -- Extra setup for C documentation with Doxygen and breathe ------------------ # see also https://breathe.readthedocs.io/en/latest/readthedocs.html diff --git a/documentation/source/index.rst b/documentation/source/index.rst index e3b9fe19..64fe09b8 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -1,37 +1,219 @@ -********************************** -Welcome to Cocotb's documentation! -********************************** +################################## +Welcome to cocotb's documentation! +################################## -Contents: +.. + This documentation tries to follow https://www.divio.com/blog/documentation/ (Daniele Procida) + Other media about the same topic: + - https://ep2018.europython.eu/media/conference/slides/get-your-documentation-right.pdf + - https://www.youtube.com/watch?v=t4vKPhjcMZg + - A good example: http://docs.django-cms.org/en/latest/contributing/documentation.html#contributing-documentation + + See also https://github.com/cocotb/cocotb/wiki/Howto:-Writing-Documentation + +*************** +What is cocotb? +*************** + +**cocotb** is a *COroutine* based *COsimulation* *TestBench* environment for verifying VHDL and SystemVerilog RTL using `Python `_. + +cocotb is completely free, open source (under the `BSD License `_) and hosted on `GitHub `_. + +cocotb requires a simulator to simulate the HDL design +and has been used with a variety of simulators on Linux, Windows and macOS. +Please check the :ref:`simulator-support` page for specifics. + +A (possibly older) version of cocotb can be used live in a web browser on `EDA Playground `_. + + +************************ +How is cocotb different? +************************ + +cocotb encourages the same philosophy of design re-use and randomized testing as +`UVM `_, +however is implemented in Python. + +With cocotb, VHDL or SystemVerilog are normally only used for the design itself, not the testbench. + +cocotb has built-in support for integrating with continuous integration systems, +such as Jenkins, GitLab, etc. through standardized, machine-readable test reporting formats. + +cocotb was specifically designed to lower the overhead of creating a test. + +cocotb automatically discovers tests so that no additional step is required to add a test to a regression. + +All verification is done using Python which has various advantages over using SystemVerilog or VHDL for verification: + +* Writing Python is **fast** - it's a very productive language. +* It's **easy** to interface to other languages from Python. +* Python has a huge library of existing code to **re-use**. +* Python is **interpreted** - tests can be edited and re-run without having to recompile the design or exit the simulator GUI. +* Python is **popular** - far more engineers know Python than SystemVerilog or VHDL. + + +********************* +How does cocotb work? +********************* + +A typical cocotb testbench requires no additional RTL code. +The Design Under Test (DUT) is instantiated as the toplevel in the simulator without any wrapper code. +cocotb drives stimulus onto the inputs to the DUT (or further down the hierarchy) and monitors the outputs directly from Python. + + +.. image:: diagrams/svg/cocotb_overview.svg + +A test is simply a Python function. +At any given time either the simulator is advancing time or the Python code is executing. +The :keyword:`await` keyword is used to indicate when to pass control of execution back to the simulator. +A test can spawn multiple coroutines, allowing for independent flows of execution. + + +************ +Contributors +************ + +.. spelling:: + McGregor + Grimwood + +cocotb was developed by `Potential Ventures `_ with the support of +`Solarflare Communications Ltd `_ +and contributions from Gordon McGregor and Finn Grimwood +(see `contributors `_ for the full list of contributions). + +We also have a list of talks and papers, libraries and examples at our Wiki page +`Further Resources `_. +Feel free to add links to cocotb-related content that we are still missing! + + +.. todo:: + - Move "Contributors" section to "Development & Community" + - Move "Installation" section to here + +.. + Tutorials - lessons that take the reader by the hand through a series of steps to complete a project + (Example: kid cooking; learning-oriented) + + - learning by doing + - getting started + - inspiring confidence + - repeatability + - immediate sense of achievement + - concreteness, not abstraction + - minimum necessary explanation + - no distractions .. toctree:: - :maxdepth: 2 + :maxdepth: 1 + :caption: Tutorials + :name: tutorials + :hidden: - introduction quickstart - building - writing_testbenches - coroutines - triggers - testbench_tools - library_reference - library_reference_c endian_swapper ping_tun_tap hal_cosimulation examples + + +.. + How-To Guides - guides that take the reader through the steps required to solve a common problem + (Example: recipe; problem-oriented) + + - a series of steps + - a focus on the goal + - addressing a specific question + - no unnecessary explanation + - a little flexibility + - practical usability + - good naming + +.. toctree:: + :maxdepth: 1 + :caption: How-to Guides + :name: howto_guides + :hidden: + + writing_testbenches + coroutines + triggers + testbench_tools + +.. todo:: + - Add WaveDrom, IPython sections + - How to deal with existing Verification IP? + - Point to https://github.com/cocotb/cocotb/wiki/Code-Examples + + +.. + Explanation (Background, Discussions) - discussions that clarify and illuminate a particular topic + (Example: history of cooking; understanding-oriented) + + - giving context + - explaining why + - multiple examples, alternative approaches + - making connections + - no instruction or technical description + +.. toctree:: + :maxdepth: 1 + :caption: Key topics + :name: key_topics + :hidden: + troubleshooting + +.. todo:: + - Move section "How does cocotb work?" from Introduction to here + - Add some info from :doc:`coroutines` + - Add GPI section + - Explain ReadOnly/ReadWrite/... phases + - Add pitfall from https://github.com/cocotb/cocotb/issues/526#issuecomment-300371629 to troubleshooting + + +.. + Reference - technical descriptions of the machinery and its operation + (Example: Wikipedia pages of ingredients; information-oriented) + + - structure + - consistency + - description + - accuracy + +.. toctree:: + :maxdepth: 1 + :caption: Reference + :name: reference + :hidden: + + building + Python Code Library Reference + C/C++ Code Library Reference simulator_support extensions + +.. todo:: + - *Maybe* add a glossary (Coroutine, Driver, Monitor, Scoreboard, HDL, RTL, GPI, V(H)PI, FLI, VIP, UVM, MDV, DUT/DUV) + +.. toctree:: + :maxdepth: 1 + :caption: Development & Community + :name: development_community + :hidden: + roadmap release_notes +.. todo:: + - Add "Join us online" and "Contributing" + - In Contributing, add explanation on how to provide a PR, how to test existing PRs, etc. + -****************** -Indices and tables -****************** +####### +Indices +####### -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` +* :ref:`Index of Classes, Methods, Variables etc.` +* :ref:`Index of Python Modules ` diff --git a/documentation/source/introduction.rst b/documentation/source/introduction.rst deleted file mode 100644 index 6ab138fd..00000000 --- a/documentation/source/introduction.rst +++ /dev/null @@ -1,90 +0,0 @@ -************ -Introduction -************ - -What is cocotb? -=============== - -**cocotb** is a *COroutine* based *COsimulation* *TestBench* environment for verifying VHDL/Verilog RTL using `Python `_. - -cocotb is completely free, open source (under the `BSD License `_) and hosted on `GitHub `_. - -cocotb requires a simulator to simulate the RTL. Simulators that have been tested and known to work with cocotb: - -Linux Platforms - -* `Icarus Verilog `_ -* `GHDL `_ -* `Aldec `_ Riviera-PRO -* `Synopsys `_ VCS -* `Cadence `_ Incisive and Xcelium -* `Mentor `_ ModelSim (DE and SE) -* `Verilator `_ - -Windows Platform - -* `Icarus Verilog `_ -* `Aldec `_ Riviera-PRO -* `Mentor `_ ModelSim (DE and SE) - -A (possibly older) version of cocotb can be used live in a web-browser using `EDA Playground `_. - - - -How is cocotb different? -======================== - - -cocotb encourages the same philosophy of design re-use and randomized testing as UVM, however is implemented in Python rather than SystemVerilog. - -With cocotb, VHDL/Verilog/SystemVerilog are normally only used for the design itself, not the testbench. - -cocotb has built-in support for integrating with the `Jenkins `_ continuous integration system. - -cocotb was specifically designed to lower the overhead of creating a test. - -cocotb automatically discovers tests so that no additional step is required to add a test to a regression. - -All verification is done using Python which has various advantages over using SystemVerilog or VHDL for verification: - -* Writing Python is **fast** - it's a very productive language -* It's **easy** to interface to other languages from Python -* Python has a huge library of existing code to **re-use** like `packet generation `_ libraries. -* Python is **interpreted**. Tests can be edited and re-run them without having to recompile the design or exit the simulator GUI. -* Python is **popular** - far more engineers know Python than SystemVerilog or VHDL - - -How does cocotb work? -===================== - -Overview --------- - -A typical cocotb testbench requires no additional RTL code. -The Design Under Test (DUT) is instantiated as the toplevel in the simulator without any wrapper code. -cocotb drives stimulus onto the inputs to the DUT (or further down the hierarchy) and monitors the outputs directly from Python. - - -.. image:: diagrams/svg/cocotb_overview.svg - -A test is simply a Python function. -At any given time either the simulator is advancing time or the Python code is executing. -The :keyword:`yield` keyword is used to indicate when to pass control of execution back to the simulator. -A test can spawn multiple coroutines, allowing for independent flows of execution. - - -Contributors -============ - -.. spelling:: - McGregor - Grimwood - -cocotb was developed by `Potential Ventures `_ with the support of -`Solarflare Communications Ltd `_ -and contributions from Gordon McGregor and Finn Grimwood -(see `contributers `_ for the full list of contributions). - -We also have a list of talks and papers, libraries and examples at our Wiki page -`Further Resources `_. -Feel free to add links to cocotb-related content that we are still missing! diff --git a/documentation/source/newsfragments/1482.doc.rst b/documentation/source/newsfragments/1482.doc.rst new file mode 100644 index 00000000..c78af0d7 --- /dev/null +++ b/documentation/source/newsfragments/1482.doc.rst @@ -0,0 +1,2 @@ +The documentation at http://docs.cocotb.org/ has been restructured, +making it easier to find relevant information. diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 7f508f38..7a947e04 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -4,7 +4,7 @@ Simulator Support ***************** -This page documents any known quirks and gotchas in the various simulators. +This page documents specifics, limitations, workarounds etc. in the various simulators. .. _sim-icarus: @@ -23,7 +23,7 @@ Accessing bits of a vector doesn't work: dut.stream_in_data[2] <= 1 -See ``access_single_bit`` test in :file:`examples/functionality/tests/test_discovery.py`. +See the ``access_single_bit`` test in :file:`examples/functionality/tests/test_discovery.py`. .. _sim-icarus-waveforms: @@ -59,7 +59,7 @@ Time unit and precision ----------------------- Setting the time unit and time precision is not possible from the command-line, -and therefore make variables :make:var:`COCOTB_HDL_TIMEUNIT` and :make:var:`COCOTB_HDL_TIMEPRECISION` are ignored. +and therefore the make variables :make:var:`COCOTB_HDL_TIMEUNIT` and :make:var:`COCOTB_HDL_TIMEPRECISION` are ignored. .. _sim-verilator: @@ -99,6 +99,8 @@ This will result in coverage data being written to ``coverage.dat``. Synopsys VCS ============ +cocotb currently only supports VPI for Synopsys VCS, not VHPI. + .. _sim-aldec: @@ -114,6 +116,7 @@ this setting will be mirrored in the TCL ``license_queue`` variable to control r Mentor Questa ============= +See :ref:`sim-modelsim`. .. _sim-modelsim: @@ -136,12 +139,15 @@ ModelSim DE and SE (and Questa, of course) supports the FLI. Cadence Incisive ================ +See :ref:`sim-xcelium`. .. _sim-xcelium: Cadence Xcelium =============== +The simulator automatically loads VPI even when only VHPI is requested. + .. _sim-ghdl: From 803f814c3510fc68e0291e0f64f104ac4b7623a2 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Fri, 10 Apr 2020 11:13:12 +0200 Subject: [PATCH 2119/2656] Remove unused and undocumented Makefile variables. (#1623) Variables: USER_DIR, BUILD_DIR, PWD. Note that one test was using the `PWD` variable without defining it, so that was fixed in this commit too. It worked anyway because `PWD` is set by the shell. --- Makefile | 1 - cocotb/share/makefiles/Makefile.inc | 7 ------- cocotb/share/makefiles/Makefile.sim | 2 -- tests/designs/avalon_module/Makefile | 2 ++ 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 9158e862..686a58c1 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,6 @@ all: test .PHONY: clean clean: - -@rm -rf $(BUILD_DIR) -@find . -name "obj" | xargs rm -rf -@find . -name "*.pyc" | xargs rm -rf -@find . -name "*results.xml" | xargs rm -rf diff --git a/cocotb/share/makefiles/Makefile.inc b/cocotb/share/makefiles/Makefile.inc index 22c3f601..de415c62 100644 --- a/cocotb/share/makefiles/Makefile.inc +++ b/cocotb/share/makefiles/Makefile.inc @@ -36,13 +36,6 @@ COCOTB_PY_DIR := $(realpath $(shell cocotb-config --prefix)) # simulations: Makefile fragments, and the simulator libraries. COCOTB_SHARE_DIR := $(COCOTB_PY_DIR)/cocotb/share -ifeq ($(USER_DIR),) -export USER_DIR:=$(realpath $(dir $(firstword $(MAKEFILE_LIST)))) -endif - -BUILD_DIR=$(USER_DIR)/build -export BUILD_DIR - ARCH?=$(shell uname -m) export ARCH diff --git a/cocotb/share/makefiles/Makefile.sim b/cocotb/share/makefiles/Makefile.sim index 8043f41b..ae25d261 100644 --- a/cocotb/share/makefiles/Makefile.sim +++ b/cocotb/share/makefiles/Makefile.sim @@ -130,8 +130,6 @@ CUSTOM_SIM_DEPS += $(shell find $(COCOTB_PY_DIR)/cocotb/ -name "*.py") # This triggers a recompilation of the simulation if cocotb library is updated. CUSTOM_SIM_DEPS += $(shell find $(LIB_DIR)/ -name "*") -PWD := $(shell pwd) - include $(COCOTB_SHARE_DIR)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) # Check that the COCOTB_RESULTS_FILE was created, since we can't set an exit code from cocotb. diff --git a/tests/designs/avalon_module/Makefile b/tests/designs/avalon_module/Makefile index f5051a72..c12585b5 100644 --- a/tests/designs/avalon_module/Makefile +++ b/tests/designs/avalon_module/Makefile @@ -39,6 +39,8 @@ else TOPLEVEL := burst_read_master +PWD=$(shell pwd) + COCOTB?=$(PWD)/../../.. VERILOG_SOURCES = $(COCOTB)/tests/designs/avalon_module/burst_read_master.v From 0b8698f1d1e4f5096698a60ab168b4004f8a7e93 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Fri, 10 Apr 2020 10:13:28 +0100 Subject: [PATCH 2120/2656] Fix weird indentation in sample_module (#1624) This was a weird mix of 2-space, 4-space, and plain nonsensical indentation. I've basically never written any VHDL, but this formatting seems to match the semantic structure a little better. Also fixes some weird alignment likely caused by conversion from tabs to spaces. --- tests/designs/sample_module/sample_module.sv | 12 +- .../designs/sample_module/sample_module.vhdl | 130 +++++++++--------- .../sample_module/sample_module_1.vhdl | 38 ++--- 3 files changed, 90 insertions(+), 90 deletions(-) diff --git a/tests/designs/sample_module/sample_module.sv b/tests/designs/sample_module/sample_module.sv index dff558f7..dcff4364 100644 --- a/tests/designs/sample_module/sample_module.sv +++ b/tests/designs/sample_module/sample_module.sv @@ -93,8 +93,8 @@ test_if struct_var; and test_and_gate(and_output, stream_in_ready, stream_in_valid); initial begin - $dumpfile("waveform.vcd"); - $dumpvars(0,sample_module); + $dumpfile("waveform.vcd"); + $dumpvars(0,sample_module); // TODO: Move into a separate test // #500000 $fail_test("Test timed out, failing..."); @@ -104,11 +104,11 @@ reg[3:0] temp; parameter NUM_OF_MODULES = 4; genvar idx; generate -for (idx = 0; idx < NUM_OF_MODULES; idx=idx+1) begin - always @(posedge clk) begin - temp[idx] <= 1'b0; + for (idx = 0; idx < NUM_OF_MODULES; idx=idx+1) begin + always @(posedge clk) begin + temp[idx] <= 1'b0; + end end -end endgenerate reg [7:0] register_array [1:0]; diff --git a/tests/designs/sample_module/sample_module.vhdl b/tests/designs/sample_module/sample_module.vhdl index 85e4c762..394ccb1a 100644 --- a/tests/designs/sample_module/sample_module.vhdl +++ b/tests/designs/sample_module/sample_module.vhdl @@ -66,74 +66,74 @@ end; architecture impl of sample_module is - component sample_module_1 is - generic ( - EXAMPLE_STRING : string; - EXAMPLE_BOOL : boolean; - EXAMPLE_WIDTH : integer - ); - port ( - clk : in std_ulogic; - stream_in_data : in std_ulogic_vector(EXAMPLE_WIDTH downto 0); - stream_out_data_registered : buffer std_ulogic_vector(EXAMPLE_WIDTH downto 0); - stream_out_data_valid : out std_ulogic - ); -end component sample_module_1; - - type lutType is array (0 to 3, 0 to 6) of signed(10 downto 0); - -function afunc(value : std_ulogic_vector) return std_ulogic_vector is - variable i: integer; - variable rv: std_ulogic_vector(7 downto 0); -begin - i := 0; - while i <= 7 loop - rv(i) := value(7-i); - i := i + 1; - end loop; - return rv; -end afunc; - - signal cosLut0, sinLut0 : lutType; - signal cosLut1, sinLut1 : lutType; - signal cosLut, sinLut : lutType; - - type unsignedArrayType is array (natural range <>) of unsigned(7 downto 0); - signal array_7_downto_4 : unsignedArrayType(7 downto 4); - signal array_4_to_7 : unsignedArrayType(4 to 7); - signal array_3_downto_0 : unsignedArrayType(3 downto 0); - signal array_0_to_3 : unsignedArrayType(0 to 3); - - type twoDimArrayType is array (natural range <>) of unsignedArrayType(31 downto 28); - signal array_2d : twoDimArrayType(0 to 1); + component sample_module_1 is + generic ( + EXAMPLE_STRING : string; + EXAMPLE_BOOL : boolean; + EXAMPLE_WIDTH : integer + ); + port ( + clk : in std_ulogic; + stream_in_data : in std_ulogic_vector(EXAMPLE_WIDTH downto 0); + stream_out_data_registered : buffer std_ulogic_vector(EXAMPLE_WIDTH downto 0); + stream_out_data_valid : out std_ulogic + ); + end component sample_module_1; + + type lutType is array (0 to 3, 0 to 6) of signed(10 downto 0); + + function afunc(value : std_ulogic_vector) return std_ulogic_vector is + variable i: integer; + variable rv: std_ulogic_vector(7 downto 0); + begin + i := 0; + while i <= 7 loop + rv(i) := value(7-i); + i := i + 1; + end loop; + return rv; + end afunc; + + signal cosLut0, sinLut0 : lutType; + signal cosLut1, sinLut1 : lutType; + signal cosLut, sinLut : lutType; + + type unsignedArrayType is array (natural range <>) of unsigned(7 downto 0); + signal array_7_downto_4 : unsignedArrayType(7 downto 4); + signal array_4_to_7 : unsignedArrayType(4 to 7); + signal array_3_downto_0 : unsignedArrayType(3 downto 0); + signal array_0_to_3 : unsignedArrayType(0 to 3); + + type twoDimArrayType is array (natural range <>) of unsignedArrayType(31 downto 28); + signal array_2d : twoDimArrayType(0 to 1); begin -process (clk) begin - if rising_edge(clk) then - stream_out_data_registered <= stream_in_data; - end if; -end process; - -stream_out_data_comb <= afunc(stream_in_data) when stream_in_func_en = '0' else stream_in_data; -stream_in_ready <= stream_out_ready; -stream_out_real <= stream_in_real; -stream_out_int <= stream_in_int; -stream_out_string <= stream_in_string; -stream_out_bool <= stream_in_bool; -stream_out_data_wide(3 downto 2) <= stream_in_data_wide(3 downto 2); - -isample_module1 : component sample_module_1 - generic map ( - EXAMPLE_STRING => "TESTING", - EXAMPLE_BOOL => true, - EXAMPLE_WIDTH => 7 + process (clk) begin + if rising_edge(clk) then + stream_out_data_registered <= stream_in_data; + end if; + end process; + + stream_out_data_comb <= afunc(stream_in_data) when stream_in_func_en = '0' else stream_in_data; + stream_in_ready <= stream_out_ready; + stream_out_real <= stream_in_real; + stream_out_int <= stream_in_int; + stream_out_string <= stream_in_string; + stream_out_bool <= stream_in_bool; + stream_out_data_wide(3 downto 2) <= stream_in_data_wide(3 downto 2); + + isample_module1 : component sample_module_1 + generic map ( + EXAMPLE_STRING => "TESTING", + EXAMPLE_BOOL => true, + EXAMPLE_WIDTH => 7 ) - port map ( - clk => clk, - stream_in_data => stream_in_data, - stream_out_data_registered => open, - stream_out_data_valid => open - ); + port map ( + clk => clk, + stream_in_data => stream_in_data, + stream_out_data_registered => open, + stream_out_data_valid => open + ); end architecture; diff --git a/tests/designs/sample_module/sample_module_1.vhdl b/tests/designs/sample_module/sample_module_1.vhdl index 9a1823de..d6221b44 100644 --- a/tests/designs/sample_module/sample_module_1.vhdl +++ b/tests/designs/sample_module/sample_module_1.vhdl @@ -4,33 +4,33 @@ use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity sample_module_1 is - generic ( - EXAMPLE_STRING : string; - EXAMPLE_BOOL : boolean; - EXAMPLE_WIDTH : integer + generic ( + EXAMPLE_STRING : string; + EXAMPLE_BOOL : boolean; + EXAMPLE_WIDTH : integer ); port ( - clk : in std_ulogic; - stream_in_data : in std_ulogic_vector(EXAMPLE_WIDTH downto 0); - stream_out_data_registered : buffer std_ulogic_vector(EXAMPLE_WIDTH downto 0); - stream_out_data_valid : out std_ulogic + clk : in std_ulogic; + stream_in_data : in std_ulogic_vector(EXAMPLE_WIDTH downto 0); + stream_out_data_registered : buffer std_ulogic_vector(EXAMPLE_WIDTH downto 0); + stream_out_data_valid : out std_ulogic ); end; architecture impl of sample_module_1 is begin - process (clk) begin - if rising_edge(clk) then - stream_out_data_registered <= stream_in_data; - end if; - end process; + process (clk) begin + if rising_edge(clk) then + stream_out_data_registered <= stream_in_data; + end if; + end process; - stream_out_data_valid <= '1' when (stream_out_data_registered(EXAMPLE_WIDTH) = '1') else '0'; + stream_out_data_valid <= '1' when (stream_out_data_registered(EXAMPLE_WIDTH) = '1') else '0'; - SAMPLE_BLOCK : block - signal clk_inv : std_ulogic; - begin - clk_inv <= not clk; - end block; + SAMPLE_BLOCK : block + signal clk_inv : std_ulogic; + begin + clk_inv <= not clk; + end block; end architecture; From 26c2f215d9bfa54521ed14a1893335814bb50a6e Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Fri, 10 Apr 2020 10:55:11 +0100 Subject: [PATCH 2121/2656] Update urls in setup.py (#1627) We now have a domain name (that redirects to the docs). These urls appears in the sidebar on PyPI. --- setup.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0397a321..4e93fa80 100755 --- a/setup.py +++ b/setup.py @@ -85,7 +85,7 @@ def package_files(directory): cmdclass={'build_ext': build_ext}, version=__version__, # noqa: F821 description='cocotb is a coroutine based cosimulation library for writing VHDL and Verilog testbenches in Python.', - url='https://github.com/cocotb/cocotb', + url='https://cocotb.org', license='BSD', long_description=read_file('README.md'), long_description_content_type='text/markdown', @@ -112,6 +112,13 @@ def package_files(directory): "License :: OSI Approved :: BSD License", "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", ], + + # these appear in the sidebar on PyPI + project_urls={ + "Bug Tracker": "https://github.com/cocotb/cocotb/issues", + "Source Code": "https://github.com/cocotb/cocotb", + "Documentation": "https://docs.cocotb.org", + }, ) print(log_stream.getvalue()) From 133e6ed77e3466e2b0780ec05b3c24bd48912a12 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Fri, 10 Apr 2020 14:22:57 +0200 Subject: [PATCH 2122/2656] Remove dependency on pylib Makefiles in endian_swapper (#1619) The only consumer of the variables defined in`Makefile.pylib` was the `test_endian_swapper_hal` example. By defining those variables locally, we now have the option to eliminate that makefile altogether. Note this also drops the attempt at cross-compilation support (gh-1585). --- examples/endian_swapper/cosim/Makefile | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/examples/endian_swapper/cosim/Makefile b/examples/endian_swapper/cosim/Makefile index 430b189a..ff85ea48 100644 --- a/examples/endian_swapper/cosim/Makefile +++ b/examples/endian_swapper/cosim/Makefile @@ -1,30 +1,34 @@ -include $(shell cocotb-config --makefiles)/Makefile.inc -include $(shell cocotb-config --makefiles)/Makefile.pylib +PYTHON_BIN ?= $(realpath $(shell cocotb-config --python-bin)) -PYTHON_LIBDIR ?= /usr/lib64 -SWIG ?= swig -CC=gcc -ifeq ($(ARCH),i686) - CC += -m32 +PYTHON_LIBDIR := $(shell $(PYTHON_BIN) -c 'from distutils import sysconfig; print( sysconfig.get_config_var("LIBDIR") )') +ifeq ($(PYTHON_LIBDIR),None) + PYTHON_LIBDIR := $(shell dirname $(PYTHON_BIN))/libs endif +PYTHON_INCLUDEDIR := $(shell $(PYTHON_BIN) -c 'import distutils.sysconfig; print( distutils.sysconfig.get_python_inc() )') + +SWIG ?= swig +CC ?= gcc + +OS := $(shell uname) + ifeq ($(OS),Darwin) -PYTHON_LD_FLAGS += -undefined dynamic_lookup + CFLAGS += -undefined dynamic_lookup endif .PHONY: all all: io_module.so _hal.so io_module.o: io.c io_module.h io.h - $(CC) $(GCC_FLAGS) -fPIC -pthread -fno-strict-aliasing -O2 -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 \ + $(CC) $(CFLAGS) -fPIC -pthread -fno-strict-aliasing -O2 -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 \ -fexceptions --param=ssp-buffer-size=4 -D_GNU_SOURCE \ -fwrapv -DNDEBUG -I$(PYTHON_INCLUDEDIR) -c $< -o $@ io_module.so: io_module.o - $(CC) -pthread -shared $< -L$(PYTHON_LIBDIR) $(PYTHON_LD_FLAGS) -o $@ + $(CC) $(CFLAGS) -pthread -shared $< -L$(PYTHON_LIBDIR) -o $@ _hal.so: ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so - $(CC) -g -ldl -shared -fPIC -I$(shell pwd) $(PYTHON_LD_FLAGS) -I$(PYTHON_INCLUDEDIR) -I../hal ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so -o $@ + $(CC) $(CFLAGS) -g -ldl -shared -fPIC -I$(shell pwd) -I$(PYTHON_INCLUDEDIR) -I../hal ../hal/endian_swapper_hal.c endian_swapper_hal_wrap.c io_module.so -o $@ if [ $(OS) = Darwin ]; then \ install_name_tool -change io_module.so $(shell pwd)/io_module.so $@; \ fi From 395d5af5f75adb52aa0d5c18dd563083d3c54d82 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Fri, 10 Apr 2020 07:30:22 +0200 Subject: [PATCH 2123/2656] Remove NDEBUG from endian_swapper example --- examples/endian_swapper/cosim/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/endian_swapper/cosim/Makefile b/examples/endian_swapper/cosim/Makefile index ff85ea48..65d04709 100644 --- a/examples/endian_swapper/cosim/Makefile +++ b/examples/endian_swapper/cosim/Makefile @@ -22,7 +22,7 @@ all: io_module.so _hal.so io_module.o: io.c io_module.h io.h $(CC) $(CFLAGS) -fPIC -pthread -fno-strict-aliasing -O2 -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 \ -fexceptions --param=ssp-buffer-size=4 -D_GNU_SOURCE \ - -fwrapv -DNDEBUG -I$(PYTHON_INCLUDEDIR) -c $< -o $@ + -fwrapv -I$(PYTHON_INCLUDEDIR) -c $< -o $@ io_module.so: io_module.o $(CC) $(CFLAGS) -pthread -shared $< -L$(PYTHON_LIBDIR) -o $@ From f61afcedc0b501098634eddc599c6404a14d92b6 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Fri, 10 Apr 2020 19:16:57 +0100 Subject: [PATCH 2124/2656] Move `contributors` off the index page, as requested by a TODO (#1628) --- documentation/source/contributors.rst | 12 ++++++++++++ documentation/source/further_resources.rst | 7 +++++++ documentation/source/index.rst | 22 +++------------------- 3 files changed, 22 insertions(+), 19 deletions(-) create mode 100644 documentation/source/contributors.rst create mode 100644 documentation/source/further_resources.rst diff --git a/documentation/source/contributors.rst b/documentation/source/contributors.rst new file mode 100644 index 00000000..87909f05 --- /dev/null +++ b/documentation/source/contributors.rst @@ -0,0 +1,12 @@ +************ +Contributors +************ + +.. spelling:: + McGregor + Grimwood + +cocotb was developed by `Potential Ventures `_ with the support of +`Solarflare Communications Ltd `_ +and contributions from Gordon McGregor and Finn Grimwood +(see `contributors `_ for the full list of contributions). diff --git a/documentation/source/further_resources.rst b/documentation/source/further_resources.rst new file mode 100644 index 00000000..ab9fc24c --- /dev/null +++ b/documentation/source/further_resources.rst @@ -0,0 +1,7 @@ +***************** +Further Resources +***************** + +We have a list of talks and papers, libraries and examples at our Wiki page +`Further Resources `_. +Feel free to add links to cocotb-related content that we are still missing! diff --git a/documentation/source/index.rst b/documentation/source/index.rst index 64fe09b8..36ef9702 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -69,26 +69,7 @@ The :keyword:`await` keyword is used to indicate when to pass control of executi A test can spawn multiple coroutines, allowing for independent flows of execution. -************ -Contributors -************ - -.. spelling:: - McGregor - Grimwood - -cocotb was developed by `Potential Ventures `_ with the support of -`Solarflare Communications Ltd `_ -and contributions from Gordon McGregor and Finn Grimwood -(see `contributors `_ for the full list of contributions). - -We also have a list of talks and papers, libraries and examples at our Wiki page -`Further Resources `_. -Feel free to add links to cocotb-related content that we are still missing! - - .. todo:: - - Move "Contributors" section to "Development & Community" - Move "Installation" section to here .. @@ -203,11 +184,14 @@ Feel free to add links to cocotb-related content that we are still missing! :hidden: roadmap + contributors release_notes + further_resources .. todo:: - Add "Join us online" and "Contributing" - In Contributing, add explanation on how to provide a PR, how to test existing PRs, etc. + - merge `further_resources` into Contributing From 90023310e8f6b6c9124c725266733578de3665f8 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 11 Apr 2020 08:33:36 -0500 Subject: [PATCH 2125/2656] Fix trigger documentation error (#1631) --- documentation/source/triggers.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/triggers.rst b/documentation/source/triggers.rst index 6a0f8487..06d87bf0 100644 --- a/documentation/source/triggers.rst +++ b/documentation/source/triggers.rst @@ -5,7 +5,7 @@ Triggers Triggers are used to indicate when the cocotb scheduler should resume coroutine execution. To use a trigger, a coroutine should :keyword:`await` it. This will cause execution of the current coroutine to pause. -When the trigger fires, execution of the paused coroutine will resume:: +When the trigger fires, execution of the paused coroutine will resume: .. code-block:: python3 From 6911d3910e3f228bad01143148be5b96affcc690 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 12 Apr 2020 10:34:58 +0100 Subject: [PATCH 2126/2656] Unpin Sphinx and breathe (#1641) The upstream bug in sphinxcontrib-domaintools has been fixed and released in 0.3, so we should be able to unpin these again. Note that we now need to pin Sphinx to be at least 3.0, else we get the default of an ancient 1.x install which doesn't work with breathe. --- documentation/requirements.txt | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/documentation/requirements.txt b/documentation/requirements.txt index 479759a9..2a7790fc 100644 --- a/documentation/requirements.txt +++ b/documentation/requirements.txt @@ -1,14 +1,7 @@ # Python dependencies to build the documentation -# Pinned below 3 until https://github.com/sphinx-contrib/domaintools/pull/9 is -# merged and released. -Sphinx~=2.1 - -# 4.15 depends on sphinx 3.0.0, but for some reason ReadTheDocs tries to install -# it anyway. -breathe < 4.15 - -# other dependencies +Sphinx ~= 3.0 +breathe sphinx-rtd-theme cairosvg sphinxcontrib-makedomain From da00fd8d63c8df4cee6f0e904466a864e6dc11fd Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 12 Apr 2020 11:02:12 -0500 Subject: [PATCH 2127/2656] Unpin importlib-resources, it has been fixed upstream --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3dc95a81..4b296aa4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,8 +36,7 @@ jobs: - docker ps -a script: - echo 'Running cocotb tests with Python 3.5 ...' && echo -en 'travis_fold:start:cocotb_py35' - # - docker exec -it -e TRAVIS=true cocotb bash -lc 'pip3 install tox; cd /src; tox -e py35' - - docker exec -it -e TRAVIS=true cocotb bash -lc 'pip3 install importlib-resources==1.0.2 tox; cd /src; tox -e py35' # remove once gh-1469's upstream bug is resolved + - docker exec -it -e TRAVIS=true cocotb bash -lc 'pip3 install tox; cd /src; tox -e py35' - echo 'travis_fold:end:cocotb_py35' - stage: SimTests From 2ec67c8f3a934a17305c6b03c68627500eaf7ec0 Mon Sep 17 00:00:00 2001 From: pstrueb <62699315+pstrueb@users.noreply.github.com> Date: Sun, 12 Apr 2020 23:23:34 +0200 Subject: [PATCH 2128/2656] Active hdl support (#1614) Add support for the Aldec Active-HDL simulator It uses a ``runsim.do`` file with a Tcl-like format (without being full Tcl). Fixes #1601 --- .../makefiles/simulators/Makefile.activehdl | 70 +++++++++++++++++++ .../source/newsfragments/1601.feature.rst | 1 + documentation/source/simulator_support.rst | 5 ++ 3 files changed, 76 insertions(+) create mode 100644 cocotb/share/makefiles/simulators/Makefile.activehdl create mode 100644 documentation/source/newsfragments/1601.feature.rst diff --git a/cocotb/share/makefiles/simulators/Makefile.activehdl b/cocotb/share/makefiles/simulators/Makefile.activehdl new file mode 100644 index 00000000..387a6d00 --- /dev/null +++ b/cocotb/share/makefiles/simulators/Makefile.activehdl @@ -0,0 +1,70 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause + +# Common Makefile for the Aldec Active-HDL simulator + +CMD_BIN := vsimsa + +ifdef ACTIVEHDL_BIN_DIR + CMD := $(shell :; command -v $(ACTIVEHDL_BIN_DIR)/$(CMD_BIN) 2>/dev/null) +else + # auto-detect bin dir from system path + CMD := $(shell :; command -v $(CMD_BIN) 2>/dev/null) +endif + +ALOG_ARGS += -timescale $(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) + +# below allows for maintaining legacy syntax as well as enables using cross-simulator vars COMPILE_ARGS/SIM_ARGS +ALOG_ARGS += $(COMPILE_ARGS) +ACOM_ARGS += $(COMPILE_ARGS) +ASIM_ARGS += $(SIM_ARGS) + +RTL_LIBRARY ?= work +ALOG_ARGS += +define+COCOTB_SIM -dbg +ACOM_ARGS += -dbg + +GPI_EXTRA:= +ifeq ($(TOPLEVEL_LANG),verilog) + # backslashes needed because we embed in `echo` below + GPI_ARGS = -pli \"$(call to_tcl_path,$(LIB_DIR)/libcocotbvpi_aldec)\" +ifneq ($(VHDL_SOURCES),) + GPI_EXTRA = cocotbvhpi_aldec:cocotbvhpi_entry_point +endif + +else ifeq ($(TOPLEVEL_LANG),vhdl) + # backslashes needed because we embed in `echo` below + GPI_ARGS = -loadvhpi \"$(call to_tcl_path,$(LIB_DIR)/libcocotbvhpi_aldec):vhpi_startup_routines_bootstrap\" +ifneq ($(VERILOG_SOURCES),) + GPI_EXTRA = cocotbvpi_aldec:cocotbvpi_entry_point +endif +else + $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") +endif + +# Create a DO script (Tcl-like but not fully compatible) based on the list of $(VERILOG_SOURCES) +$(SIM_BUILD)/runsim.do : $(VERILOG_SOURCES) $(VHDL_SOURCES) $(SIM_BUILD) + @echo "alib $(RTL_LIBRARY)" >> $@ + @echo "set worklib $(RTL_LIBRARY)" >> $@ +ifneq ($(VHDL_SOURCES),) + @echo "acom $(ACOM_ARGS) $(call to_tcl_path,$(VHDL_SOURCES))" >> $@ +endif +ifneq ($(VERILOG_SOURCES),) + @echo "alog $(ALOG_ARGS) $(call to_tcl_path,$(VERILOG_SOURCES))" >> $@ +endif + @echo "asim $(ASIM_ARGS) $(PLUSARGS) +access +w -interceptcoutput -O2 -dbg $(GPI_ARGS) $(TOPLEVEL) $(EXTRA_TOPS)" >> $@ + @echo "run -all" >> $@ + @echo "endsim" >> $@ + +$(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.do $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) + -@rm -f $(COCOTB_RESULTS_FILE) + + set -o pipefail; GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ + $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 $(CMD) $(RUN_ARGS) -do $(SIM_BUILD)/runsim.do | tee $(SIM_BUILD)/sim.log + + $(call check_for_results_file) + +clean:: + @rm -rf $(SIM_BUILD) + @rm -rf work + @rm -rf wave.asdb diff --git a/documentation/source/newsfragments/1601.feature.rst b/documentation/source/newsfragments/1601.feature.rst new file mode 100644 index 00000000..01b93453 --- /dev/null +++ b/documentation/source/newsfragments/1601.feature.rst @@ -0,0 +1 @@ +Added support for Aldec's Active-HDL simulator. diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 7a947e04..eac54c96 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -110,6 +110,11 @@ Aldec Riviera-PRO The :envvar:`LICENSE_QUEUE` environment variable can be used for this simulator – this setting will be mirrored in the TCL ``license_queue`` variable to control runtime license checkouts. +.. _sim-activehdl: + +Aldec Active-HDL +================ + .. _sim-questa: From 197d8eeb2e91898c88de204b3ac703a3c03dc07b Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 13 Apr 2020 05:12:51 -0500 Subject: [PATCH 2129/2656] Change __name__ to __qualname__ in Python code (#1646) In most cases these are equivalent, but `__qualname__` will occasionally contain additional scope information. --- cocotb/binary.py | 4 ++-- cocotb/bus.py | 4 ++-- cocotb/clock.py | 4 ++-- cocotb/decorators.py | 21 +++++++++------------ cocotb/drivers/__init__.py | 2 +- cocotb/handle.py | 2 +- cocotb/monitors/__init__.py | 6 +++--- cocotb/regression.py | 11 ++++++----- cocotb/scheduler.py | 16 ++++++++-------- cocotb/scoreboard.py | 4 ++-- cocotb/triggers.py | 26 +++++++++++++------------- cocotb/utils.py | 2 +- 12 files changed, 50 insertions(+), 52 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 053b8690..a1a4b07b 100755 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -165,7 +165,7 @@ def assign(self, value): else: raise TypeError( "value must be int, str, or bytes, not {!r}" - .format(type(value).__name__) + .format(type(value).__qualname__) ) def _convert_to_unsigned(self, x): @@ -396,7 +396,7 @@ def binstr(self, string): for char in string: if char not in BinaryValue._permitted_chars: raise ValueError("Attempting to assign character %s to a %s" % - (char, type(self).__name__)) + (char, type(self).__qualname__)) self._str = string self._adjust() diff --git a/cocotb/bus.py b/cocotb/bus.py index 6570472b..20f8e182 100755 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -122,7 +122,7 @@ def drive(self, obj, strict=False): msg = ("Unable to drive onto {0}.{1} because {2} is missing " "attribute {3}".format(self._entity._name, self._name, - type(obj).__name__, + type(obj).__qualname__, attr_name)) raise AttributeError(msg) else: @@ -176,7 +176,7 @@ def sample(self, obj, strict=False): msg = ("Unable to sample from {0}.{1} because {2} is missing " "attribute {3}".format(self._entity._name, self._name, - type(obj).__name__, + type(obj).__qualname__, attr_name)) raise AttributeError(msg) else: diff --git a/cocotb/clock.py b/cocotb/clock.py index 8aa0a0d8..438d7e31 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -42,7 +42,7 @@ def __init__(self, signal): @lazy_property def log(self): return SimLog("cocotb.%s.%s" % ( - type(self).__name__, self.signal._name + type(self).__qualname__, self.signal._name )) @@ -152,4 +152,4 @@ async def start(self, cycles=None, start_high=True): await t def __str__(self): - return type(self).__name__ + "(%3.1f MHz)" % self.frequency + return type(self).__qualname__ + "(%3.1f MHz)" % self.frequency diff --git a/cocotb/decorators.py b/cocotb/decorators.py index f8fb4818..fc66805c 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -100,7 +100,8 @@ def __init__(self, inst): raise TypeError( "%s isn't a valid coroutine! Did you forget to use the yield keyword?" % inst) self._coro = inst - self.__name__ = "%s" % inst.__name__ + self.__name__ = inst.__name__ + self.__qualname__ = inst.__qualname__ self._started = False self._callbacks = [] self._outcome = None @@ -109,10 +110,7 @@ def __init__(self, inst): def log(self): # Creating a logger is expensive, only do it if we actually plan to # log anything - if hasattr(self, "__name__"): - return SimLog("cocotb.coroutine.%s" % self.__name__, id(self)) - else: - return SimLog("cocotb.coroutine.fail") + return SimLog("cocotb.coroutine.%s" % self.__qualname__, id(self)) @property def retval(self): @@ -128,7 +126,7 @@ def __iter__(self): return self def __str__(self): - return str(self.__name__) + return str(self.__qualname__) def _advance(self, outcome): """Advance to the next yield in this coroutine. @@ -236,7 +234,7 @@ def handle(self, record): def __init__(self, inst, parent): self.error_messages = [] RunningCoroutine.__init__(self, inst, parent) - self.log = SimLog("cocotb.test.%s" % self.__name__, id(self)) + self.log = SimLog("cocotb.test.%s" % self.__qualname__, id(self)) self.started = False self.start_time = 0 self.start_sim_time = 0 @@ -295,12 +293,11 @@ class coroutine: def __init__(self, func): self._func = func - self.__name__ = self._func.__name__ functools.update_wrapper(self, func) @lazy_property def log(self): - return SimLog("cocotb.coroutine.%s" % self._func.__name__, id(self)) + return SimLog("cocotb.coroutine.%s" % self._func.__qualname__, id(self)) def __call__(self, *args, **kwargs): return RunningCoroutine(self._func(*args, **kwargs), self) @@ -314,7 +311,7 @@ def __iter__(self): return self def __str__(self): - return str(self._func.__name__) + return str(self._func.__qualname__) @public @@ -331,7 +328,7 @@ def __init__(self, func): @lazy_property def log(self): - return SimLog("cocotb.function.%s" % self._coro.__name__, id(self)) + return SimLog("cocotb.function.%s" % self._coro.__qualname__, id(self)) def __call__(self, *args, **kwargs): return cocotb.scheduler.queue_function(self._coro(*args, **kwargs)) @@ -352,7 +349,7 @@ class external: """ def __init__(self, func): self._func = func - self._log = SimLog("cocotb.external.%s" % self._func.__name__, id(self)) + self._log = SimLog("cocotb.external.%s" % self._func.__qualname__, id(self)) def __call__(self, *args, **kwargs): return cocotb.scheduler.run_in_executor(self._func, *args, **kwargs) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index e3f12095..b9fd4118 100755 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -102,7 +102,7 @@ def __init__(self): # Sub-classes may already set up logging if not hasattr(self, "log"): - self.log = SimLog("cocotb.driver.%s" % (type(self).__name__)) + self.log = SimLog("cocotb.driver.%s" % (type(self).__qualname__)) # Create an independent coroutine which can send stuff self._thread = cocotb.scheduler.add(self._send_thread()) diff --git a/cocotb/handle.py b/cocotb/handle.py index c1798680..f6dfc0e0 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -130,7 +130,7 @@ def __repr__(self): deffile = self._def_file if deffile: desc += " (at "+deffile+")" - return type(self).__name__ + "(" + desc + ")" + return type(self).__qualname__ + "(" + desc + ")" def __str__(self): return self._path diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index a1f8302f..2ebcfa9a 100755 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -82,7 +82,7 @@ def __init__(self, callback=None, event=None): # Sub-classes may already set up logging if not hasattr(self, "log"): - self.log = SimLog("cocotb.monitor.%s" % (type(self).__name__)) + self.log = SimLog("cocotb.monitor.%s" % (type(self).__qualname__)) if callback is not None: self.add_callback(callback) @@ -109,7 +109,7 @@ def add_callback(self, callback): callback (callable): The function to call back. """ self.log.debug("Adding callback of function %s to monitor", - callback.__name__) + callback.__qualname__) self._callbacks.append(callback) @coroutine @@ -194,4 +194,4 @@ def in_reset(self): return False def __str__(self): - return "%s(%s)" % (type(self).__name__, self.name) + return "%s(%s)" % (type(self).__qualname__, self.name) diff --git a/cocotb/regression.py b/cocotb/regression.py index a33014b4..64489897 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -313,7 +313,7 @@ def _init_test(self, test_func): ratio_time="0.0") result_pass, sim_failed = self._score_test(test_func, test_init_outcome) # Save results - self._store_test_result(test_func.__module__, test_func.__name__, result_pass, 0.0, 0.0, 0.0) + self._store_test_result(test_func.__module__, test_func.__qualname__, result_pass, 0.0, 0.0, 0.0) if not result_pass: self.xunit.add_failure() self.failures += 1 @@ -346,7 +346,7 @@ def _score_test(self, test, outcome): # Helper for logging result def _result_was(): result_was = ("{} (result was {})".format - (test.__name__, type(result).__name__)) + (test.__qualname__, type(result).__qualname__)) return result_was # scoring outcomes @@ -363,7 +363,7 @@ def _result_was(): if (isinstance(result, TestSuccess) and not test.expect_fail and not test.expect_error): - self.log.info("Test Passed: %s" % test.__name__) + self.log.info("Test Passed: %s" % test.__qualname__) elif (isinstance(result, AssertionError) and test.expect_fail): @@ -534,6 +534,7 @@ async def _my_test(dut): await function(dut, *args, **kwargs) _my_test.__name__ = name + _my_test.__qualname__ = name _my_test.__doc__ = documentation _my_test.__module__ = mod.__name__ return cocotb.test()(_my_test) @@ -591,7 +592,7 @@ def __init__(self, test_function, *args, **kwargs): if not isinstance(test_function, cocotb.coroutine): raise TypeError("TestFactory requires a cocotb coroutine") self.test_function = test_function - self.name = self.test_function._func.__name__ + self.name = self.test_function._func.__qualname__ self.args = args self.kwargs_constant = kwargs @@ -647,7 +648,7 @@ def generate_tests(self, prefix="", postfix=""): desc = "No docstring supplied" else: desc = optvalue.__doc__.split('\n')[0] - doc += "\t%s: %s (%s)\n" % (optname, optvalue.__name__, + doc += "\t%s: %s (%s)\n" % (optname, optvalue.__qualname__, desc) else: doc += "\t%s: %s\n" % (optname, repr(optvalue)) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index da749c4a..d334157c 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -424,7 +424,7 @@ def _event_loop(self, trigger): if _debug: - debugstr = "\n\t".join([coro.__name__ for coro in scheduling]) + debugstr = "\n\t".join([coro.__qualname__ for coro in scheduling]) if len(scheduling): debugstr = "\n\t" + debugstr self.log.debug("%d pending coroutines for event %s%s" % @@ -435,10 +435,10 @@ def _event_loop(self, trigger): for coro in scheduling: if _debug: - self.log.debug("Scheduling coroutine %s" % (coro.__name__)) + self.log.debug("Scheduling coroutine %s" % (coro.__qualname__)) self.schedule(coro, trigger=trigger) if _debug: - self.log.debug("Scheduled coroutine %s" % (coro.__name__)) + self.log.debug("Scheduled coroutine %s" % (coro.__qualname__)) # Schedule may have queued up some events so we'll burn through those while self._pending_events: @@ -616,7 +616,7 @@ def execute_external(func, _waiter): async def wrapper(): waiter = external_waiter() thread = threading.Thread(group=None, target=execute_external, - name=func.__name__ + "_thread", + name=func.__qualname__ + "_thread", args=([func, waiter]), kwargs={}) waiter.thread = thread @@ -660,7 +660,7 @@ def add(self, coroutine): ) if _debug: - self.log.debug("Adding new coroutine %s" % coroutine.__name__) + self.log.debug("Adding new coroutine %s" % coroutine.__qualname__) self.schedule(coroutine) self._check_termination() @@ -682,14 +682,14 @@ def add_test(self, test_coro): def _trigger_from_started_coro(self, result: cocotb.decorators.RunningTask) -> Trigger: if _debug: self.log.debug("Joining to already running coroutine: %s" % - result.__name__) + result.__qualname__) return result.join() def _trigger_from_unstarted_coro(self, result: cocotb.decorators.RunningTask) -> Trigger: self.queue(result) if _debug: self.log.debug("Scheduling nested coroutine: %s" % - result.__name__) + result.__qualname__) return result.join() def _trigger_from_waitable(self, result: cocotb.triggers.Waitable) -> Trigger: @@ -753,7 +753,7 @@ def schedule(self, coroutine, trigger=None): result = coroutine._advance(send_outcome) if _debug: self.log.debug("Coroutine %s yielded %s (mode %d)" % - (coroutine.__name__, str(result), self._mode)) + (coroutine.__qualname__, str(result), self._mode)) except cocotb.decorators.CoroutineComplete as exc: if _debug: diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index 24a91465..082007ea 100755 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -204,7 +204,7 @@ def add_interface(self, monitor, expected_output, compare_fn=None, # Enforce some type checking as we only work with a real monitor if not isinstance(monitor, Monitor): raise TypeError("Expected monitor on the interface but got %s" % - (type(monitor).__name__)) + (type(monitor).__qualname__)) if compare_fn is not None: if callable(compare_fn): @@ -222,7 +222,7 @@ def check_received_transaction(transaction): if monitor.name: log_name = self.log.name + '.' + monitor.name else: - log_name = self.log.name + '.' + type(monitor).__name__ + log_name = self.log.name + '.' + type(monitor).__qualname__ log = logging.getLogger(log_name) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 18c1638f..2ea95b56 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -78,7 +78,7 @@ def __init__(self): @lazy_property def log(self): - return SimLog("cocotb.%s" % (type(self).__name__), id(self)) + return SimLog("cocotb.%s" % (type(self).__qualname__), id(self)) @abc.abstractmethod def prime(self, callback): @@ -212,7 +212,7 @@ def prime(self, callback): def __repr__(self): return "<{} of {:1.2f}ps at {}>".format( - type(self).__name__, + type(self).__qualname__, get_time_from_sim_steps(self.sim_steps, units='ps'), _pointer_str(self) ) @@ -249,7 +249,7 @@ def prime(self, callback): GPITrigger.prime(self, callback) def __repr__(self): - return "{}()".format(type(self).__name__) + return "{}()".format(type(self).__qualname__) class ReadWrite(GPITrigger, metaclass=_ParameterizedSingletonAndABC): @@ -273,7 +273,7 @@ def prime(self, callback): GPITrigger.prime(self, callback) def __repr__(self): - return "{}()".format(type(self).__name__) + return "{}()".format(type(self).__qualname__) class NextTimeStep(GPITrigger, metaclass=_ParameterizedSingletonAndABC): @@ -295,7 +295,7 @@ def prime(self, callback): GPITrigger.prime(self, callback) def __repr__(self): - return "{}()".format(type(self).__name__) + return "{}()".format(type(self).__qualname__) class _EdgeBase(GPITrigger, metaclass=_ParameterizedSingletonAndABC): @@ -327,7 +327,7 @@ def prime(self, callback): super(_EdgeBase, self).prime(callback) def __repr__(self): - return "{}({!r})".format(type(self).__name__, self.signal) + return "{}({!r})".format(type(self).__qualname__, self.signal) class RisingEdge(_EdgeBase): @@ -425,7 +425,7 @@ def __repr__(self): fmt = "<{0} at {2}>" else: fmt = "<{0} for {1} at {2}>" - return fmt.format(type(self).__name__, self.name, _pointer_str(self)) + return fmt.format(type(self).__qualname__, self.name, _pointer_str(self)) class _Lock(PythonTrigger): @@ -516,7 +516,7 @@ def __repr__(self): else: fmt = "<{0} for {1} [{2} waiting] at {3}>" return fmt.format( - type(self).__name__, self.name, len(self._pending_primed), + type(self).__qualname__, self.name, len(self._pending_primed), _pointer_str(self) ) @@ -556,7 +556,7 @@ def __repr__(self): fmt = "<{0} at {2}>" else: fmt = "<{0} for {1} at {2}>" - return fmt.format(type(self).__name__, self.name, _pointer_str(self)) + return fmt.format(type(self).__qualname__, self.name, _pointer_str(self)) class Join(PythonTrigger, metaclass=_ParameterizedSingletonAndABC): @@ -617,7 +617,7 @@ def prime(self, callback): super(Join, self).prime(callback) def __repr__(self): - return "{}({!r})".format(type(self).__name__, self._coroutine) + return "{}({!r})".format(type(self).__qualname__, self._coroutine) class Waitable(Awaitable): @@ -655,14 +655,14 @@ def __init__(self, *triggers): if not isinstance(trigger, allowed_types): raise TypeError( "All triggers must be instances of Trigger! Got: {}" - .format(type(trigger).__name__) + .format(type(trigger).__qualname__) ) def __repr__(self): # no _pointer_str here, since this is not a trigger, so identity # doesn't matter. return "{}({})".format( - type(self).__name__, ", ".join(repr(t) for t in self.triggers) + type(self).__qualname__, ", ".join(repr(t) for t in self.triggers) ) @@ -790,7 +790,7 @@ def __repr__(self): fmt = "{}({!r}, {!r})" else: fmt = "{}({!r}, {!r}, rising=False)" - return fmt.format(type(self).__name__, self.signal, self.num_cycles) + return fmt.format(type(self).__qualname__, self.signal, self.num_cycles) async def with_timeout(trigger, timeout_time, timeout_unit=None): diff --git a/cocotb/utils.py b/cocotb/utils.py index ee72d430..a72581e3 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -514,7 +514,7 @@ def __get__(self, obj, cls): return self value = self.fget(obj) - setattr(obj, self.fget.__name__, value) + setattr(obj, self.fget.__qualname__, value) return value From e5f888af5a64edc5753a9de2d24e1724d547f5f4 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Sun, 12 Apr 2020 15:30:04 +0200 Subject: [PATCH 2130/2656] GHDL support on Windows --- cocotb/_build_libs.py | 23 +++++++--- cocotb/share/def/ghdl.def | 43 +++++++++++++++++++ .../share/makefiles/simulators/Makefile.ghdl | 5 +++ 3 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 cocotb/share/def/ghdl.def diff --git a/cocotb/_build_libs.py b/cocotb/_build_libs.py index 03478906..0ac5779b 100755 --- a/cocotb/_build_libs.py +++ b/cocotb/_build_libs.py @@ -125,7 +125,7 @@ def _gen_import_libs(self, def_dir): """ if os.name == "nt": - for sim in ["icarus", "modelsim", "aldec"]: + for sim in ["icarus", "modelsim", "aldec", "ghdl"]: subprocess.run( [ "dlltool", @@ -403,12 +403,21 @@ def get_ext(): # # GHDL # - if os.name == "posix": - logger.info("Compiling libraries for GHDL") - ghdl_vpi_ext = _get_vpi_lib_ext( - include_dir=include_dir, share_lib_dir=share_lib_dir, sim_define="GHDL" - ) - ext.append(ghdl_vpi_ext) + ghdl_extra_lib = [] + ghdl_extra_lib_path = [] + logger.info("Compiling libraries for GHDL") + if os.name == "nt": + ghdl_extra_lib = ["ghdl"] + ghdl_extra_lib_path = [share_def_dir] + + ghdl_vpi_ext = _get_vpi_lib_ext( + include_dir=include_dir, + share_lib_dir=share_lib_dir, + sim_define="GHDL", + extra_lib=ghdl_extra_lib, + extra_lib_dir=ghdl_extra_lib_path, + ) + ext.append(ghdl_vpi_ext) # # IUS diff --git a/cocotb/share/def/ghdl.def b/cocotb/share/def/ghdl.def new file mode 100644 index 00000000..bc9b1506 --- /dev/null +++ b/cocotb/share/def/ghdl.def @@ -0,0 +1,43 @@ +LIBRARY "libghdlvpi.dll" +EXPORTS +vpi_compare_objects +vpi_control +vpi_flush +vpi_fopen +vpi_free_object +vpi_get +vpi_get_delays +vpi_get_file +vpi_get_str +vpi_get_systf_info +vpi_get_time +vpi_get_userdata +vpi_get_value +vpi_get_vlog_info +vpi_handle +vpi_handle_by_index +vpi_handle_by_name +vpi_iterate +vpi_mcd_close +vpi_mcd_flush +vpi_mcd_name +vpi_mcd_printf +vpi_mcd_vprintf +vpi_printf +vpi_put_delays +vpi_put_userdata +vpi_put_value +vpi_register_cb +vpi_register_systf +vpi_remove_cb +vpi_scan +vpi_sim_control +vpi_sim_vcontrol +vpi_mcd_open +vpi_vprintf +vpip_count_drivers +vpip_format_strength +vpip_make_systf_system_defined +vpip_mcd_rawwrite +vpip_set_return_value +vpi_chk_error diff --git a/cocotb/share/makefiles/simulators/Makefile.ghdl b/cocotb/share/makefiles/simulators/Makefile.ghdl index b43b0343..09e4793c 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ghdl +++ b/cocotb/share/makefiles/simulators/Makefile.ghdl @@ -54,6 +54,11 @@ RTL_LIBRARY ?= work GHDL_ARGS ?= GHDL_ARGS += $(EXTRA_ARGS) +# On Windows add GHDL lib folder to PATH to find libraries +ifeq ($(OS),Msys) + export PATH := $(GHDL_BIN_DIR)/../lib:$(PATH) +endif + .PHONY: analyse # Compilation phase From 840e52c466499afbd5526ca3f03974898afd4904 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Wed, 19 Feb 2020 15:58:25 +0100 Subject: [PATCH 2131/2656] Use extension types for handles instead of integers This means that: * Passing handles back and forth should be faster, as there is no need to convert back and forth between big integers and pointers * The simulator module becomes more type safe, and does not allow passing objects of one type to functions expecting a different type * It is no longer possible to pass arbitrary integers in that don't correspond to actual handles * Instead of returning `0`, failure is indicated by returning `None`. This results in a `TypeError` rather than a segfault if used again unchecked. There is one behavior change here, in that `simulator.next(simulator.iterate(not_iterable))` no longer raises StopIteration. If you are seeing this in the commit history, then this change didn't manifest in any regression tests. --- cocotb/handle.py | 2 +- .../share/lib/simulator/simulatormodule.cpp | 336 +++++++++++------- cocotb/triggers.py | 26 +- 3 files changed, 221 insertions(+), 143 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index f6dfc0e0..8d7dc2c7 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -94,7 +94,7 @@ def get_definition_file(self): return self._def_file def __hash__(self): - return self._handle + return hash(self._handle) def __len__(self): """Returns the 'length' of the underlying object. diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index f9137430..4b9fcbb7 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -41,11 +41,123 @@ static int sim_ending = 0; #include "simulatormodule.h" #include // COCOTB_UNUSED +#include + struct module_state { PyObject *error; }; + +/* define the extension types as templates */ +namespace { + template + struct gpi_hdl_Object { + PyObject_HEAD + gpi_hdl hdl; + + // The python type object, in a place that is easy to retrieve in templates + static PyTypeObject py_type; + }; + + /** __repr__ shows the memory address of the internal handle */ + template + static PyObject *gpi_hdl_repr(gpi_hdl_Object *self) { + auto *type = Py_TYPE(self); + return PyUnicode_FromFormat("<%s at %p>", type->tp_name, self->hdl); + } + + /** __hash__ returns the pointer itself */ + template + static Py_hash_t gpi_hdl_hash(gpi_hdl_Object *self) { + auto ret = reinterpret_cast(self->hdl); + // hash must never return -1 + if (ret == (Py_hash_t)-1) { + ret = (Py_hash_t)-2; + } + return ret; + } + + /** + * Create a new python handle object from a pointer, returning None if the + * pointer is NULL. + */ + template + static PyObject *gpi_hdl_New(gpi_hdl hdl) { + if (hdl == NULL) { + Py_RETURN_NONE; + } + auto *obj = PyObject_New(gpi_hdl_Object, &gpi_hdl_Object::py_type); + if (obj == NULL) { + return NULL; + } + obj->hdl = hdl; + return (PyObject *)obj; + } + + /** Comparison checks if the types match, and then compares pointers */ + template + static PyObject *gpi_hdl_richcompare(PyObject *self, PyObject *other, int op) { + if ( + Py_TYPE(self) != &gpi_hdl_Object::py_type || + Py_TYPE(other) != &gpi_hdl_Object::py_type + ) { + Py_RETURN_NOTIMPLEMENTED; + } + + auto self_hdl_obj = reinterpret_cast*>(self); + auto other_hdl_obj = reinterpret_cast*>(other); + + switch (op) { + case Py_EQ: return PyBool_FromLong(self_hdl_obj->hdl == other_hdl_obj->hdl); + case Py_NE: return PyBool_FromLong(self_hdl_obj->hdl != other_hdl_obj->hdl); + default: Py_RETURN_NOTIMPLEMENTED; + } + } + + // Initialize the python type slots + template + PyTypeObject fill_common_slots() { + PyTypeObject type; + type.ob_base = {PyObject_HEAD_INIT(NULL) 0}; + type.tp_basicsize = sizeof(gpi_hdl_Object); + type.tp_repr = (reprfunc)gpi_hdl_repr; + type.tp_hash = (hashfunc)gpi_hdl_hash; + type.tp_flags = Py_TPFLAGS_DEFAULT; + type.tp_richcompare = gpi_hdl_richcompare; + return type; + } + + template<> + PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { + auto type = fill_common_slots(); + type.tp_name = "gpi_sim_hdl"; + return type; + }(); + + template<> + PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { + auto type = fill_common_slots(); + type.tp_name = "gpi_iterator_hdl"; + return type; + }(); + + template<> + PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { + auto type = fill_common_slots(); + type.tp_name = "gpi_cb_hdl"; + return type; + }(); + + // get the python type corresponding to the static type of a PyObject pointer + template + PyTypeObject* python_type_for() { + return &std::remove_pointer::type::py_type; + } +} + + + #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) typedef int (*gpi_function_t)(const void *); @@ -70,53 +182,6 @@ struct sim_time { static struct sim_time cache_time; -// Converter function for turning a Python long into a sim handle, such that it -// can be used by PyArg_ParseTuple format O&. -static int gpi_sim_hdl_converter(PyObject *o, gpi_sim_hdl *data) -{ - void *p = PyLong_AsVoidPtr(o); - if ((p == NULL) && PyErr_Occurred()) { - return 0; - } - if (p == NULL) { - PyErr_SetString(PyExc_ValueError, "handle cannot be 0"); - return 0; - } - *data = (gpi_sim_hdl)p; - return 1; -} - -// Same as above, for a callback handle. -static int gpi_cb_hdl_converter(PyObject *o, gpi_cb_hdl *data) -{ - void *p = PyLong_AsVoidPtr(o); - if ((p == NULL) && PyErr_Occurred()) { - return 0; - } - if (p == NULL) { - PyErr_SetString(PyExc_ValueError, "handle cannot be 0"); - return 0; - } - *data = (gpi_cb_hdl)p; - return 1; -} - - -// Same as above, for an iterator handle. -static int gpi_iterator_hdl_converter(PyObject *o, gpi_iterator_hdl *data) -{ - void *p = PyLong_AsVoidPtr(o); - if ((p == NULL) && PyErr_Occurred()) { - return 0; - } - if (p == NULL) { - PyErr_SetString(PyExc_ValueError, "handle cannot be 0"); - return 0; - } - *data = (gpi_iterator_hdl)p; - return 1; -} - /** * @name Callback Handling * @brief Handle a callback coming from GPI @@ -287,7 +352,7 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) hdl = gpi_register_readonly_callback((gpi_function_t)handle_gpi_callback, callback_data_p); - PyObject *rv = PyLong_FromVoidPtr(hdl); + PyObject *rv = gpi_hdl_New(hdl); FEXIT return rv; @@ -340,7 +405,7 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) hdl = gpi_register_readwrite_callback((gpi_function_t)handle_gpi_callback, callback_data_p); - PyObject *rv = PyLong_FromVoidPtr(hdl); + PyObject *rv = gpi_hdl_New(hdl); FEXIT return rv; @@ -393,7 +458,7 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) hdl = gpi_register_nexttime_callback((gpi_function_t)handle_gpi_callback, callback_data_p); - PyObject *rv = PyLong_FromVoidPtr(hdl); + PyObject *rv = gpi_hdl_New(hdl); FEXIT return rv; @@ -465,7 +530,7 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) hdl = gpi_register_timed_callback((gpi_function_t)handle_gpi_callback, callback_data_p, time_ps); // Check success - PyObject *rv = PyLong_FromVoidPtr(hdl); + PyObject *rv = gpi_hdl_New(hdl); FEXIT return rv; @@ -496,10 +561,12 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) return NULL; } - PyObject *pSihHdl = PyTuple_GetItem(args, 0); - if (!gpi_sim_hdl_converter(pSihHdl, &sig_hdl)) { + PyObject *pSigHdl = PyTuple_GetItem(args, 0); + if (Py_TYPE(pSigHdl) != &gpi_hdl_Object::py_type) { + PyErr_SetString(PyExc_TypeError, "First argument must be a gpi_sim_hdl"); return NULL; } + sig_hdl = ((gpi_hdl_Object *)pSigHdl)->hdl; // Extract the callback function function = PyTuple_GetItem(args, 1); @@ -538,7 +605,7 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) edge); // Check success - PyObject *rv = PyLong_FromVoidPtr(hdl); + PyObject *rv = gpi_hdl_New(hdl); FEXIT return rv; @@ -548,18 +615,18 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) static PyObject *iterate(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); - gpi_sim_hdl hdl; + gpi_hdl_Object *hdl_obj; int type; gpi_iterator_hdl result; PyObject *res; - if (!PyArg_ParseTuple(args, "O&i", gpi_sim_hdl_converter, &hdl, &type)) { + if (!PyArg_ParseTuple(args, "O!i", python_type_for(), &hdl_obj, &type)) { return NULL; } - result = gpi_iterate(hdl, (gpi_iterator_sel_t)type); + result = gpi_iterate(hdl_obj->hdl, (gpi_iterator_sel_t)type); - res = PyLong_FromVoidPtr(result); + res = gpi_hdl_New(result); return res; } @@ -568,22 +635,22 @@ static PyObject *iterate(PyObject *self, PyObject *args) static PyObject *next(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); - gpi_iterator_hdl hdl; + gpi_hdl_Object* hdl_obj; gpi_sim_hdl result; PyObject *res; - if (!PyArg_ParseTuple(args, "O&", gpi_iterator_hdl_converter, &hdl)) { + if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { return NULL; } - // It's valid for iterate to return a NULL handle, to make the Python + // TODO:eric-wieser: It's valid for iterate to return a NULL handle, to make the Python // intuitive we simply raise StopIteration on the first iteration - if (!hdl) { - PyErr_SetNone(PyExc_StopIteration); - return NULL; - } + // if (!hdl) { + // PyErr_SetNone(PyExc_StopIteration); + // return NULL; + // } - result = gpi_next(hdl); + result = gpi_next(hdl_obj->hdl); // Raise StopIteration when we're done if (!result) { @@ -591,7 +658,7 @@ static PyObject *next(PyObject *self, PyObject *args) return NULL; } - res = PyLong_FromVoidPtr(result); + res = gpi_hdl_New(result); return res; } @@ -600,15 +667,15 @@ static PyObject *next(PyObject *self, PyObject *args) static PyObject *get_signal_val_binstr(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); - gpi_sim_hdl hdl; + gpi_hdl_Object *hdl_obj; const char *result; PyObject *retstr; - if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { + if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { return NULL; } - result = gpi_get_signal_value_binstr(hdl); + result = gpi_get_signal_value_binstr(hdl_obj->hdl); retstr = Py_BuildValue("s", result); return retstr; @@ -617,15 +684,15 @@ static PyObject *get_signal_val_binstr(PyObject *self, PyObject *args) static PyObject *get_signal_val_str(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); - gpi_sim_hdl hdl; + gpi_hdl_Object *hdl_obj; const char *result; PyObject *retstr; - if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { + if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { return NULL; } - result = gpi_get_signal_value_str(hdl); + result = gpi_get_signal_value_str(hdl_obj->hdl); retstr = PyBytes_FromString(result); return retstr; @@ -634,15 +701,15 @@ static PyObject *get_signal_val_str(PyObject *self, PyObject *args) static PyObject *get_signal_val_real(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); - gpi_sim_hdl hdl; + gpi_hdl_Object *hdl_obj; double result; PyObject *retval; - if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { + if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { return NULL; } - result = gpi_get_signal_value_real(hdl); + result = gpi_get_signal_value_real(hdl_obj->hdl); retval = Py_BuildValue("d", result); return retval; @@ -652,15 +719,15 @@ static PyObject *get_signal_val_real(PyObject *self, PyObject *args) static PyObject *get_signal_val_long(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); - gpi_sim_hdl hdl; + gpi_hdl_Object *hdl_obj; long result; PyObject *retval; - if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { + if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { return NULL; } - result = gpi_get_signal_value_long(hdl); + result = gpi_get_signal_value_long(hdl_obj->hdl); retval = Py_BuildValue("l", result); return retval; @@ -669,60 +736,60 @@ static PyObject *get_signal_val_long(PyObject *self, PyObject *args) static PyObject *set_signal_val_binstr(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); - gpi_sim_hdl hdl; + gpi_hdl_Object *hdl_obj; const char *binstr; gpi_set_action_t action; - if (!PyArg_ParseTuple(args, "O&is", gpi_sim_hdl_converter, &hdl, &action, &binstr)) { + if (!PyArg_ParseTuple(args, "O!is", python_type_for(), &hdl_obj, &action, &binstr)) { return NULL; } - gpi_set_signal_value_binstr(hdl, binstr, action); + gpi_set_signal_value_binstr(hdl_obj->hdl, binstr, action); Py_RETURN_NONE; } static PyObject *set_signal_val_str(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); - gpi_sim_hdl hdl; + gpi_hdl_Object *hdl_obj; gpi_set_action_t action; const char *str; - if (!PyArg_ParseTuple(args, "O&iy", gpi_sim_hdl_converter, &hdl, &action, &str)) { + if (!PyArg_ParseTuple(args, "O!iy", python_type_for(), &hdl_obj, &action, &str)) { return NULL; } - gpi_set_signal_value_str(hdl, str, action); + gpi_set_signal_value_str(hdl_obj->hdl, str, action); Py_RETURN_NONE; } static PyObject *set_signal_val_real(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); - gpi_sim_hdl hdl; + gpi_hdl_Object *hdl_obj; double value; gpi_set_action_t action; - if (!PyArg_ParseTuple(args, "O&id", gpi_sim_hdl_converter, &hdl, &action, &value)) { + if (!PyArg_ParseTuple(args, "O!id", python_type_for(), &hdl_obj, &action, &value)) { return NULL; } - gpi_set_signal_value_real(hdl, value, action); + gpi_set_signal_value_real(hdl_obj->hdl, value, action); Py_RETURN_NONE; } static PyObject *set_signal_val_long(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); - gpi_sim_hdl hdl; + gpi_hdl_Object *hdl_obj; long value; gpi_set_action_t action; - if (!PyArg_ParseTuple(args, "O&il", gpi_sim_hdl_converter, &hdl, &action, &value)) { + if (!PyArg_ParseTuple(args, "O!il", python_type_for(), &hdl_obj, &action, &value)) { return NULL; } - gpi_set_signal_value_long(hdl, value, action); + gpi_set_signal_value_long(hdl_obj->hdl, value, action); Py_RETURN_NONE; } @@ -730,14 +797,14 @@ static PyObject *get_definition_name(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); const char* result; - gpi_sim_hdl hdl; + gpi_hdl_Object *hdl_obj; PyObject *retstr; - if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { + if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { return NULL; } - result = gpi_get_definition_name((gpi_sim_hdl)hdl); + result = gpi_get_definition_name(hdl_obj->hdl); retstr = Py_BuildValue("s", result); return retstr; @@ -747,14 +814,14 @@ static PyObject *get_definition_file(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); const char* result; - gpi_sim_hdl hdl; + gpi_hdl_Object *hdl_obj; PyObject *retstr; - if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { + if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { return NULL; } - result = gpi_get_definition_file((gpi_sim_hdl)hdl); + result = gpi_get_definition_file(hdl_obj->hdl); retstr = Py_BuildValue("s", result); return retstr; @@ -764,17 +831,17 @@ static PyObject *get_handle_by_name(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); const char *name; - gpi_sim_hdl hdl; + gpi_hdl_Object *hdl_obj; gpi_sim_hdl result; PyObject *res; - if (!PyArg_ParseTuple(args, "O&s", gpi_sim_hdl_converter, &hdl, &name)) { + if (!PyArg_ParseTuple(args, "O!s", python_type_for(), &hdl_obj, &name)) { return NULL; } - result = gpi_get_handle_by_name((gpi_sim_hdl)hdl, name); + result = gpi_get_handle_by_name(hdl_obj->hdl, name); - res = PyLong_FromVoidPtr(result); + res = gpi_hdl_New(result); return res; } @@ -783,17 +850,17 @@ static PyObject *get_handle_by_index(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); int32_t index; - gpi_sim_hdl hdl; + gpi_hdl_Object *hdl_obj; gpi_sim_hdl result; PyObject *value; - if (!PyArg_ParseTuple(args, "O&i", gpi_sim_hdl_converter, &hdl, &index)) { + if (!PyArg_ParseTuple(args, "O!i", python_type_for(), &hdl_obj, &index)) { return NULL; } - result = gpi_get_handle_by_index((gpi_sim_hdl)hdl, index); + result = gpi_get_handle_by_index(hdl_obj->hdl, index); - value = PyLong_FromVoidPtr(result); + value = gpi_hdl_New(result); return value; } @@ -815,7 +882,7 @@ static PyObject *get_root_handle(PyObject *self, PyObject *args) } - value = PyLong_FromVoidPtr(result); + value = gpi_hdl_New(result); return value; } @@ -825,14 +892,14 @@ static PyObject *get_name_string(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); const char *result; - gpi_sim_hdl hdl; + gpi_hdl_Object *hdl_obj; PyObject *retstr; - if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { + if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { return NULL; } - result = gpi_get_signal_name_str((gpi_sim_hdl)hdl); + result = gpi_get_signal_name_str(hdl_obj->hdl); retstr = Py_BuildValue("s", result); return retstr; @@ -842,14 +909,14 @@ static PyObject *get_type(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); gpi_objtype_t result; - gpi_sim_hdl hdl; + gpi_hdl_Object *hdl_obj; PyObject *pyresult; - if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { + if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { return NULL; } - result = gpi_get_object_type((gpi_sim_hdl)hdl); + result = gpi_get_object_type(hdl_obj->hdl); pyresult = Py_BuildValue("i", (int)result); return pyresult; @@ -859,14 +926,14 @@ static PyObject *get_const(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); int result; - gpi_sim_hdl hdl; + gpi_hdl_Object *hdl_obj; PyObject *pyresult; - if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { + if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { return NULL; } - result = gpi_is_constant((gpi_sim_hdl)hdl); + result = gpi_is_constant(hdl_obj->hdl); pyresult = Py_BuildValue("i", result); return pyresult; @@ -876,14 +943,14 @@ static PyObject *get_type_string(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); const char *result; - gpi_sim_hdl hdl; + gpi_hdl_Object *hdl_obj; PyObject *retstr; - if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { + if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { return NULL; } - result = gpi_get_signal_type_str((gpi_sim_hdl)hdl); + result = gpi_get_signal_type_str(hdl_obj->hdl); retstr = Py_BuildValue("s", result); return retstr; @@ -929,14 +996,14 @@ static PyObject *get_precision(PyObject *self, PyObject *args) static PyObject *get_num_elems(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); - gpi_sim_hdl hdl; + gpi_hdl_Object *hdl_obj; PyObject *retstr; - if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { + if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { return NULL; } - int elems = gpi_get_num_elems((gpi_sim_hdl)hdl); + int elems = gpi_get_num_elems(hdl_obj->hdl); retstr = Py_BuildValue("i", elems); return retstr; @@ -945,16 +1012,16 @@ static PyObject *get_num_elems(PyObject *self, PyObject *args) static PyObject *get_range(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); - gpi_sim_hdl hdl; + gpi_hdl_Object *hdl_obj; PyObject *retstr; - if (!PyArg_ParseTuple(args, "O&", gpi_sim_hdl_converter, &hdl)) { + if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { return NULL; } - int indexable = gpi_is_indexable((gpi_sim_hdl)hdl); - int rng_left = gpi_get_range_left((gpi_sim_hdl)hdl); - int rng_right = gpi_get_range_right((gpi_sim_hdl)hdl); + int indexable = gpi_is_indexable(hdl_obj->hdl); + int rng_left = gpi_get_range_left(hdl_obj->hdl); + int rng_right = gpi_get_range_right(hdl_obj->hdl); if (indexable) retstr = Py_BuildValue("(i,i)", rng_left, rng_right); @@ -977,15 +1044,15 @@ static PyObject *stop_simulator(PyObject *self, PyObject *args) static PyObject *deregister_callback(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); - gpi_cb_hdl hdl; + gpi_hdl_Object *hdl_obj; FENTER - if (!PyArg_ParseTuple(args, "O&", gpi_cb_hdl_converter, &hdl)) { + if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { return NULL; } - gpi_deregister_callback(hdl); + gpi_deregister_callback(hdl_obj->hdl); FEXIT Py_RETURN_NONE; @@ -1064,6 +1131,17 @@ PyMODINIT_FUNC PyInit_simulator(void) { PyObject* simulator; + /* initialize the extension types */ + if (PyType_Ready(&gpi_hdl_Object::py_type) < 0) { + return NULL; + } + if (PyType_Ready(&gpi_hdl_Object::py_type) < 0) { + return NULL; + } + if (PyType_Ready(&gpi_hdl_Object::py_type) < 0) { + return NULL; + } + simulator = PyModule_Create(&moduledef); if (simulator == NULL) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 2ea95b56..b218ddeb 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -148,13 +148,13 @@ def __init__(self): # if simulator is not None: # self.cbhdl = simulator.create_callback(self) # else: - self.cbhdl = 0 + self.cbhdl = None def unprime(self): """Disable a primed trigger, can be re-primed.""" - if self.cbhdl != 0: + if self.cbhdl is not None: simulator.deregister_callback(self.cbhdl) - self.cbhdl = 0 + self.cbhdl = None Trigger.unprime(self) @@ -203,10 +203,10 @@ def __init__(self, time_ps, units=None): def prime(self, callback): """Register for a timed callback.""" - if self.cbhdl == 0: + if self.cbhdl is None: self.cbhdl = simulator.register_timed_callback(self.sim_steps, callback, self) - if self.cbhdl == 0: + if self.cbhdl is None: raise TriggerException("Unable set up %s Trigger" % (str(self))) GPITrigger.prime(self, callback) @@ -242,9 +242,9 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): - if self.cbhdl == 0: + if self.cbhdl is None: self.cbhdl = simulator.register_readonly_callback(callback, self) - if self.cbhdl == 0: + if self.cbhdl is None: raise TriggerException("Unable set up %s Trigger" % (str(self))) GPITrigger.prime(self, callback) @@ -264,11 +264,11 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): - if self.cbhdl == 0: + if self.cbhdl is None: # import pdb # pdb.set_trace() self.cbhdl = simulator.register_rwsynch_callback(callback, self) - if self.cbhdl == 0: + if self.cbhdl is None: raise TriggerException("Unable set up %s Trigger" % (str(self))) GPITrigger.prime(self, callback) @@ -288,9 +288,9 @@ def __init__(self): GPITrigger.__init__(self) def prime(self, callback): - if self.cbhdl == 0: + if self.cbhdl is None: self.cbhdl = simulator.register_nextstep_callback(callback, self) - if self.cbhdl == 0: + if self.cbhdl is None: raise TriggerException("Unable set up %s Trigger" % (str(self))) GPITrigger.prime(self, callback) @@ -318,11 +318,11 @@ def __init__(self, signal): def prime(self, callback): """Register notification of a value change via a callback""" - if self.cbhdl == 0: + if self.cbhdl is None: self.cbhdl = simulator.register_value_change_callback( self.signal._handle, callback, type(self)._edge_type, self ) - if self.cbhdl == 0: + if self.cbhdl is None: raise TriggerException("Unable set up %s Trigger" % (str(self))) super(_EdgeBase, self).prime(callback) From c7efd418c0a5c19bc75356f3e9e55d3f8aeac40a Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Tue, 14 Apr 2020 10:43:26 +0100 Subject: [PATCH 2132/2656] Remove some inaccurate comments about the behavior of gpi_iterate --- cocotb/share/include/gpi.h | 5 +++-- cocotb/share/lib/simulator/simulatormodule.cpp | 7 ------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/cocotb/share/include/gpi.h b/cocotb/share/include/gpi.h index 8e61aede..647a8bdd 100644 --- a/cocotb/share/include/gpi.h +++ b/cocotb/share/include/gpi.h @@ -176,8 +176,9 @@ typedef enum gpi_set_action_e { // Functions for iterating over entries of a handle // Returns an iterator handle which can then be used in gpi_next calls // -// NB the iterator handle may be NULL if no objects of the requested type are -// found +// Unlike `vpi_iterate` the iterator handle may only be NULL if the `type` is +// not supported, If no objects of the requested type are found, an empty +// iterator is returned. gpi_iterator_hdl gpi_iterate(gpi_sim_hdl base, gpi_iterator_sel_t type); // Returns NULL when there are no more objects diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index 4b9fcbb7..88ffae03 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -643,13 +643,6 @@ static PyObject *next(PyObject *self, PyObject *args) return NULL; } - // TODO:eric-wieser: It's valid for iterate to return a NULL handle, to make the Python - // intuitive we simply raise StopIteration on the first iteration - // if (!hdl) { - // PyErr_SetNone(PyExc_StopIteration); - // return NULL; - // } - result = gpi_next(hdl_obj->hdl); // Raise StopIteration when we're done From 4c2d7771d5934c25fab00b5b839f85ba1bc8cfdf Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 20 Feb 2020 11:00:47 +0100 Subject: [PATCH 2133/2656] Make `gpi_iterator_hdl` objects natively iterable This saves some python wrapping and some argument parsing --- cocotb/handle.py | 17 +---- .../share/lib/simulator/simulatormodule.cpp | 64 ++++++++++--------- cocotb/share/lib/simulator/simulatormodule.h | 2 - 3 files changed, 36 insertions(+), 47 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 8d7dc2c7..1be1d82e 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -31,7 +31,6 @@ import ctypes import warnings -import collections.abc import os @@ -190,7 +189,7 @@ def _discover_all(self): if self._discovered: return self._log.debug("Discovering all on %s", self._name) - for thing in _SimIterator(self._handle, simulator.OBJECTS): + for thing in simulator.iterate(self._handle, simulator.OBJECTS): name = simulator.get_name_string(thing) try: hdl = SimHandle(thing, self._child_path(name)) @@ -559,27 +558,17 @@ def value(self, value): self[self_idx].value = value[val_idx] -class _SimIterator(collections.abc.Iterator): - """Iterator over simulator objects. For internal use only.""" - - def __init__(self, handle, mode): - self._iter = simulator.iterate(handle, mode) - - def __next__(self): - return simulator.next(self._iter) - - class NonConstantObject(NonHierarchyIndexableObject): """ A non-constant object""" # FIXME: what is the difference to ModifiableObject? Explain in docstring. def drivers(self): """An iterator for gathering all drivers for a signal.""" - return _SimIterator(self._handle, simulator.DRIVERS) + return simulator.iterate(self._handle, simulator.DRIVERS) def loads(self): """An iterator for gathering all loads on a signal.""" - return _SimIterator(self._handle, simulator.LOADS) + return simulator.iterate(self._handle, simulator.LOADS) class _SetAction: """Base class representing the type of action used while write-accessing a handle.""" diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index 88ffae03..b35305b1 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -128,26 +128,14 @@ namespace { return type; } + // these will be initialized later, once the members are all defined template<> - PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { - auto type = fill_common_slots(); - type.tp_name = "gpi_sim_hdl"; - return type; - }(); - + PyTypeObject gpi_hdl_Object::py_type; template<> - PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { - auto type = fill_common_slots(); - type.tp_name = "gpi_iterator_hdl"; - return type; - }(); - + PyTypeObject gpi_hdl_Object::py_type; template<> - PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { - auto type = fill_common_slots(); - type.tp_name = "gpi_cb_hdl"; - return type; - }(); + PyTypeObject gpi_hdl_Object::py_type; + // get the python type corresponding to the static type of a PyObject pointer template @@ -632,18 +620,9 @@ static PyObject *iterate(PyObject *self, PyObject *args) } -static PyObject *next(PyObject *self, PyObject *args) +static PyObject *next(gpi_hdl_Object *self) { - COCOTB_UNUSED(self); - gpi_hdl_Object* hdl_obj; - gpi_sim_hdl result; - PyObject *res; - - if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { - return NULL; - } - - result = gpi_next(hdl_obj->hdl); + gpi_sim_hdl result = gpi_next(self->hdl); // Raise StopIteration when we're done if (!result) { @@ -651,9 +630,7 @@ static PyObject *next(PyObject *self, PyObject *args) return NULL; } - res = gpi_hdl_New(result); - - return res; + return gpi_hdl_New(result); } @@ -1150,3 +1127,28 @@ PyMODINIT_FUNC PyInit_simulator(void) add_module_constants(simulator); return simulator; } + + +// putting these at the bottom means that all the functions above are accessible +template<> +PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { + auto type = fill_common_slots(); + type.tp_name = "gpi_sim_hdl"; + return type; +}(); + +template<> +PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { + auto type = fill_common_slots(); + type.tp_name = "gpi_iterator_hdl"; + type.tp_iter = PyObject_SelfIter; + type.tp_iternext = (iternextfunc)next; + return type; +}(); + +template<> +PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { + auto type = fill_common_slots(); + type.tp_name = "gpi_cb_hdl"; + return type; +}(); diff --git a/cocotb/share/lib/simulator/simulatormodule.h b/cocotb/share/lib/simulator/simulatormodule.h index f1096c37..1a88d5e9 100644 --- a/cocotb/share/lib/simulator/simulatormodule.h +++ b/cocotb/share/lib/simulator/simulatormodule.h @@ -83,7 +83,6 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args); static PyObject *stop_simulator(PyObject *self, PyObject *args); static PyObject *iterate(PyObject *self, PyObject *args); -static PyObject *next(PyObject *self, PyObject *args); static PyObject *get_sim_time(PyObject *self, PyObject *args); static PyObject *get_precision(PyObject *self, PyObject *args); @@ -119,7 +118,6 @@ static PyMethodDef SimulatorMethods[] = { {"register_rwsynch_callback", register_rwsynch_callback, METH_VARARGS, "Register a callback for the read-write section"}, {"stop_simulator", stop_simulator, METH_VARARGS, "Instruct the attached simulator to stop"}, {"iterate", iterate, METH_VARARGS, "Get an iterator handle to loop over all members in an object"}, - {"next", next, METH_VARARGS, "Get the next object from the iterator"}, {"log_level", log_level, METH_VARARGS, "Set the log level for GPI"}, // FIXME METH_NOARGS => initialization from incompatible pointer type From cb514c238c8fcead299c02caa9bdf5749215a90d Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Sat, 11 Apr 2020 14:45:29 +0200 Subject: [PATCH 2134/2656] Enable/Fix 32bit cross simulation Change ARCH to PYTHON_ARCH (32bit/64bit) Co-Authored-By: Eric Wieser --- cocotb/share/makefiles/Makefile.inc | 8 +++++--- cocotb/share/makefiles/simulators/Makefile.ius | 4 ++-- cocotb/share/makefiles/simulators/Makefile.questa | 4 ++-- cocotb/share/makefiles/simulators/Makefile.vcs | 2 +- cocotb/share/makefiles/simulators/Makefile.xcelium | 2 +- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/cocotb/share/makefiles/Makefile.inc b/cocotb/share/makefiles/Makefile.inc index de415c62..1835117c 100644 --- a/cocotb/share/makefiles/Makefile.inc +++ b/cocotb/share/makefiles/Makefile.inc @@ -36,9 +36,6 @@ COCOTB_PY_DIR := $(realpath $(shell cocotb-config --prefix)) # simulations: Makefile fragments, and the simulator libraries. COCOTB_SHARE_DIR := $(COCOTB_PY_DIR)/cocotb/share -ARCH?=$(shell uname -m) -export ARCH - OS=$(shell uname) ifneq (, $(findstring MINGW, $(OS))) OS=Msys @@ -77,6 +74,11 @@ else LIB_EXT := so endif +PYTHON_ARCH := $(shell $(PYTHON_BIN) -c 'from platform import architecture; print(architecture()[0])') +ifeq ($(filter $(PYTHON_ARCH),64bit 32bit),) + $(error Unknown Python architecture: $(PYTHON_ARCH)) +endif + # Set PYTHONHOME to properly populate sys.path in embedded python interpreter export PYTHONHOME := $(shell $(PYTHON_BIN) -c 'from distutils.sysconfig import get_config_var; print(get_config_var("prefix"))') diff --git a/cocotb/share/makefiles/simulators/Makefile.ius b/cocotb/share/makefiles/simulators/Makefile.ius index be85cc86..077cb8c0 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ius +++ b/cocotb/share/makefiles/simulators/Makefile.ius @@ -49,8 +49,8 @@ EXTRA_ARGS += $(COMPILE_ARGS) EXTRA_ARGS += $(SIM_ARGS) EXTRA_ARGS += -licqueue -ifneq ($(ARCH),i686) -EXTRA_ARGS += -64 +ifeq ($(PYTHON_ARCH),64bit) + EXTRA_ARGS += -64 endif EXTRA_ARGS += -nclibdirpath $(SIM_BUILD) diff --git a/cocotb/share/makefiles/simulators/Makefile.questa b/cocotb/share/makefiles/simulators/Makefile.questa index 8cfa83e0..f6333563 100644 --- a/cocotb/share/makefiles/simulators/Makefile.questa +++ b/cocotb/share/makefiles/simulators/Makefile.questa @@ -114,8 +114,8 @@ else @echo "quit" >> $@ endif -ifneq ($(ARCH),i686) -CMD += -64 +ifeq ($(PYTHON_ARCH),64bit) + CMD += -64 endif $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.do diff --git a/cocotb/share/makefiles/simulators/Makefile.vcs b/cocotb/share/makefiles/simulators/Makefile.vcs index 36d75f57..14527f8c 100644 --- a/cocotb/share/makefiles/simulators/Makefile.vcs +++ b/cocotb/share/makefiles/simulators/Makefile.vcs @@ -51,7 +51,7 @@ else export VCS_BIN_DIR endif -ifeq ($(ARCH),x86_64) +ifeq ($(PYTHON_ARCH),64bit) EXTRA_ARGS += -full64 endif diff --git a/cocotb/share/makefiles/simulators/Makefile.xcelium b/cocotb/share/makefiles/simulators/Makefile.xcelium index 97b5c37c..14aebfe2 100644 --- a/cocotb/share/makefiles/simulators/Makefile.xcelium +++ b/cocotb/share/makefiles/simulators/Makefile.xcelium @@ -49,7 +49,7 @@ EXTRA_ARGS += $(COMPILE_ARGS) EXTRA_ARGS += $(SIM_ARGS) EXTRA_ARGS += -licqueue -ifneq ($(ARCH),i686) +ifeq ($(PYTHON_ARCH),64bit) EXTRA_ARGS += -64 endif From 30e34516cb67626f6476f8719f02057d8a0ddb8e Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 15 Apr 2020 04:27:06 -0500 Subject: [PATCH 2135/2656] Link to with_timeout from the test decorator (#1630) Also improve the docs of both. --- cocotb/decorators.py | 13 +++++++++---- cocotb/triggers.py | 6 +++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index fc66805c..eadb289a 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -410,12 +410,17 @@ class test(coroutine, metaclass=_decorator_helper): Used as ``@cocotb.test(...)``. Args: - timeout_time (int, optional): - Value representing simulation timeout. + timeout_time (numbers.Real or decimal.Decimal, optional): + Simulation time duration before timeout occurs. .. versionadded:: 1.3 - timeout_unit (str, optional): - Unit of timeout value, see :class:`~cocotb.triggers.Timer` for more info. + + .. note:: + Test timeout is intended for protection against deadlock. + Users should use :class:`~cocotb.triggers.with_timeout` if they require a + more general-purpose timeout mechanism. + timeout_unit (str or None, optional): + Units of timeout_time, accepts any units that :class:`~cocotb.triggers.Timer` does. .. versionadded:: 1.3 expect_fail (bool, optional): diff --git a/cocotb/triggers.py b/cocotb/triggers.py index b218ddeb..cbbd79e1 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -805,12 +805,12 @@ async def with_timeout(trigger, timeout_time, timeout_unit=None): await with_timeout(First(coro, event.wait()), 100, 'ns') Args: - trigger (cocotb_waitable): + trigger (:class:`~cocotb.triggers.Trigger` or :class:`~cocotb.triggers.Waitable` or :class:`~cocotb.decorators.RunningTask`): A single object that could be right of an :keyword:`await` expression in cocotb. timeout_time (numbers.Real or decimal.Decimal): - Time duration. + Simulation time duration before timeout occurs. timeout_unit (str or None, optional): - Units of duration, accepts any values that :class:`~cocotb.triggers.Timer` does. + Units of timeout_time, accepts any units that :class:`~cocotb.triggers.Timer` does. Returns: First trigger that completed if timeout did not occur. From b4c7a20dc0dfef01d404524cd7531266ca93e821 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Wed, 15 Apr 2020 11:54:26 +0200 Subject: [PATCH 2136/2656] Remove unused Makefile.pylib files. (#1632) The variables `PYTHON_LIBDIR` and `PYTHON_INCLUDEDIR` defined by these files are no longer used in any of our tests and examples. Any users still using them should redefine them themselves. Co-authored-by: Eric Wieser --- cocotb/share/makefiles/Makefile.inc | 1 - cocotb/share/makefiles/Makefile.pylib | 37 ----------------- cocotb/share/makefiles/Makefile.pylib.Darwin | 40 ------------------- cocotb/share/makefiles/Makefile.pylib.Linux | 40 ------------------- cocotb/share/makefiles/Makefile.pylib.Msys | 37 ----------------- .../source/newsfragments/1632.removal.rst | 3 ++ 6 files changed, 3 insertions(+), 155 deletions(-) delete mode 100644 cocotb/share/makefiles/Makefile.pylib delete mode 100644 cocotb/share/makefiles/Makefile.pylib.Darwin delete mode 100644 cocotb/share/makefiles/Makefile.pylib.Linux delete mode 100644 cocotb/share/makefiles/Makefile.pylib.Msys create mode 100644 documentation/source/newsfragments/1632.removal.rst diff --git a/cocotb/share/makefiles/Makefile.inc b/cocotb/share/makefiles/Makefile.inc index 1835117c..4f46a343 100644 --- a/cocotb/share/makefiles/Makefile.inc +++ b/cocotb/share/makefiles/Makefile.inc @@ -49,7 +49,6 @@ export OS PYTHON_BIN ?= $(realpath $(shell cocotb-config --python-bin)) include $(COCOTB_SHARE_DIR)/makefiles/Makefile.deprecations -include $(COCOTB_SHARE_DIR)/makefiles/Makefile.pylib # Default to Icarus if no simulator is defined SIM ?= icarus diff --git a/cocotb/share/makefiles/Makefile.pylib b/cocotb/share/makefiles/Makefile.pylib deleted file mode 100644 index 0e4bf80d..00000000 --- a/cocotb/share/makefiles/Makefile.pylib +++ /dev/null @@ -1,37 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -# All common python related rules, kept only for downstream C extensions modules -# which copy the pattern of the endian_swapper HAL example. In future these modules -# should use `setuptools` to build their HAL interface - -NATIVE_ARCH=$(shell uname -m) -ARCH?=$(NATIVE_ARCH) - -include $(COCOTB_SHARE_DIR)/makefiles/Makefile.pylib.$(OS) diff --git a/cocotb/share/makefiles/Makefile.pylib.Darwin b/cocotb/share/makefiles/Makefile.pylib.Darwin deleted file mode 100644 index 1f447ec2..00000000 --- a/cocotb/share/makefiles/Makefile.pylib.Darwin +++ /dev/null @@ -1,40 +0,0 @@ -############################################################################## -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -# All common python related rules - -PYTHON_LIBDIR:=$(shell $(PYTHON_BIN) -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("LIBDIR"))') - -# Nasty hack but there's no way in Python properly get the directories for 32-bit Python on a 64-bit system -# TODO: Under OSX we can use "export VERSIONER_PYTHON_PREFER_32_BIT=yes", no Linux equivalent though -ifneq ($(NATIVE_ARCH),$(ARCH)) - PYTHON_LIBDIR:=$(subst 64,,$(PYTHON_LIBDIR)) -endif - -PYTHON_INCLUDEDIR := $(shell $(PYTHON_BIN) -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())') diff --git a/cocotb/share/makefiles/Makefile.pylib.Linux b/cocotb/share/makefiles/Makefile.pylib.Linux deleted file mode 100644 index 0b46a9c5..00000000 --- a/cocotb/share/makefiles/Makefile.pylib.Linux +++ /dev/null @@ -1,40 +0,0 @@ -############################################################################## -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -# All common python related rules - -PYTHON_LIBDIR:=$(shell $(PYTHON_BIN) -c 'from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_config_var("LIBDIR"))') - -# Nasty hack but there's no way in Python properly get the directories for 32-bit Python on a 64-bit system -# TODO: Under OSX we can use "export VERSIONER_PYTHON_PREFER_32_BIT=yes", no Linux equivalent though -ifneq ($(NATIVE_ARCH),$(ARCH)) - PYTHON_LIBDIR:=$(subst 64,,$(PYTHON_LIBDIR)) -endif - -PYTHON_INCLUDEDIR := $(shell $(PYTHON_BIN) -c 'from __future__ import print_function; import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())') diff --git a/cocotb/share/makefiles/Makefile.pylib.Msys b/cocotb/share/makefiles/Makefile.pylib.Msys deleted file mode 100644 index 48fc6d92..00000000 --- a/cocotb/share/makefiles/Makefile.pylib.Msys +++ /dev/null @@ -1,37 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -# All common python related rules - -PYTHON_LIBDIR:=$(shell $(PYTHON_BIN) -c 'from distutils import sysconfig; print( sysconfig.get_config_var("LIBDIR") )') -ifeq ($(PYTHON_LIBDIR),None) - PYTHON_LIBDIR=$(shell dirname $(PYTHON_BIN))/libs -endif - -PYTHON_INCLUDEDIR := $(shell $(PYTHON_BIN) -c 'import distutils.sysconfig; print( distutils.sysconfig.get_python_inc() )') diff --git a/documentation/source/newsfragments/1632.removal.rst b/documentation/source/newsfragments/1632.removal.rst new file mode 100644 index 00000000..4a027180 --- /dev/null +++ b/documentation/source/newsfragments/1632.removal.rst @@ -0,0 +1,3 @@ +``Makefile.pylib``, which provided helpers for building C extension modules for Python, has been removed. +Users of the ``PYTHON_LIBDIR`` and ``PYTHON_INCLUDEDIR`` variables will now have to compute these values themselves. +See the ``endian_swapper`` example for how to do this. From 318afb617a0ad15f00a36a140046809487bd26ee Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sat, 11 Apr 2020 11:54:11 +0200 Subject: [PATCH 2137/2656] Add FOSSi Foundation to Contributors section Replace "PotentialVentures" with "cocotb Contributors" in Sphinx output. Closes #1625 --- documentation/source/conf.py | 8 ++++---- documentation/source/contributors.rst | 15 +++++++++++---- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/documentation/source/conf.py b/documentation/source/conf.py index ba5ea6c4..c4058482 100644 --- a/documentation/source/conf.py +++ b/documentation/source/conf.py @@ -69,7 +69,7 @@ # General information about the project. project = 'cocotb' -copyright = '2014-{0}, PotentialVentures'.format(datetime.datetime.now().year) +copyright = '2014-{0}, cocotb contributors'.format(datetime.datetime.now().year) # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -242,7 +242,7 @@ # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'cocotb.tex', 'cocotb Documentation', - 'PotentialVentures', 'manual'), + 'cocotb contributors', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -272,7 +272,7 @@ # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'cocotb', 'cocotb Documentation', - ['PotentialVentures'], 1) + ['cocotb contributors'], 1) ] # If true, show URL addresses after external links. @@ -286,7 +286,7 @@ # dir menu entry, description, category) texinfo_documents = [ ('index', 'cocotb', 'cocotb Documentation', - 'PotentialVentures', 'cocotb', 'Coroutine Cosimulation TestBench \ + 'cocotb contributors', 'cocotb', 'Coroutine Cosimulation TestBench \ environment for efficient verification of RTL using Python.', 'Miscellaneous'), ] diff --git a/documentation/source/contributors.rst b/documentation/source/contributors.rst index 87909f05..89609a51 100644 --- a/documentation/source/contributors.rst +++ b/documentation/source/contributors.rst @@ -6,7 +6,14 @@ Contributors McGregor Grimwood -cocotb was developed by `Potential Ventures `_ with the support of -`Solarflare Communications Ltd `_ -and contributions from Gordon McGregor and Finn Grimwood -(see `contributors `_ for the full list of contributions). +cocotb is developed and maintained by an active community. +Our GitHub repository contains a list of all `contributors `_. + +Since mid-2018 cocotb is an independent project under the umbrella of the +`Free and Open Source Silicon Foundation `_. +The FOSSi Foundation provides the cocotb project with financial, +legal and administrative support, and holds cocotb assets. + +cocotb was originally developed by Stuart Hodgson and Chris Higgs of Potential Ventures +with the support of Solarflare Communications Ltd. +and contributions from Gordon McGregor and Finn Grimwood. From e57507602f8ce6840f3c0f5e4da4c9665359cddd Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Sat, 11 Apr 2020 20:55:08 +0200 Subject: [PATCH 2138/2656] Update installation instructions Co-Authored-By: Colin Marquardt Co-Authored-By: Eric Wieser --- documentation/source/quickstart.rst | 118 ++++++++++----------- documentation/source/simulator_support.rst | 16 ++- 2 files changed, 70 insertions(+), 64 deletions(-) diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index 4c9b1aec..3be4b144 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -5,8 +5,8 @@ Quickstart Guide Installing cocotb ================= -Pre-requisites --------------- +Installation of Pre-requisites +------------------------------ Cocotb has the following requirements: @@ -18,104 +18,100 @@ Cocotb has the following requirements: .. versionchanged:: 1.4 Dropped Python 2 support -.. _installation_via_pip: +.. note:: In order to use a 32-bit simulator you need to use a 32-bit version of Python. -Installation via PIP --------------------- +Installation with conda (all OS) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. versionadded:: 1.2 +`Conda `_ is an open-source package and environment management system that runs on Windows, macOS and Linux. -Cocotb can be installed by running +Download and install `Miniconda `_ from https://conda.io/ for your OS and architecture. -.. code-block:: bash +You will also need to install a compiler (GCC or Clang) and GNU Make. + +On Windows you can easily do this with conda: - pip3 install cocotb +.. code-block:: bash -For user local installation follow the -`pip User Guide `_. + conda install -c msys2 m2-base m2-make m2w64-toolchain libpython -To install the development version of cocotb: +Native Installation for Debian/Ubuntu-based Systems +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: bash - git clone https://github.com/cocotb/cocotb - pip3 install -e ./cocotb + sudo apt-get install make gcc g++ python3 python3-dev python3-pip + sudo apt-get install swig # optional, needed for one of the examples -.. note:: +Native Installation for Red Hat-based Systems +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - After installation, you should be able to execute ``cocotb-config``. - If it is not found, you need to append its location to the ``PATH`` environment variable. - This may happen when you use the ``--user`` option to ``pip``, in which case the location is documented :ref:`here `. +.. code-block:: bash -Native Linux Installation -------------------------- + sudo yum install make gcc gcc-c++ libstdc++-devel python3 python3-devel python3-pip + sudo yum install swig # optional, needed for one of the examples -The following instructions will allow building of the cocotb libraries -for use with a 64-bit native simulator. +Installation for macOS +^^^^^^^^^^^^^^^^^^^^^^ -If a 32-bit simulator is being used then additional steps are needed, please see -`our Wiki `_. +You need a few packages installed to get cocotb running on macOS. +Installing a package manager really helps things out here. -Debian/Ubuntu-based -^^^^^^^^^^^^^^^^^^^ +`Brew `_ seems to be the most popular, so we'll assume you have that installed. .. code-block:: bash - sudo apt-get install git make gcc g++ swig python-dev + brew install python icarus-verilog gtkwave -Red Hat-based -^^^^^^^^^^^^^ +.. seealso:: -.. code-block:: bash + For more installation options (also for 32-bit) please see `our Wiki `_. - sudo yum install gcc gcc-c++ libstdc++-devel swig python-devel +Simulator Installation +^^^^^^^^^^^^^^^^^^^^^^ -Windows Installation --------------------- - -Download the MinGW installer from https://osdn.net/projects/mingw/releases/. +For detailed instructions and the list of supported simulators see :ref:`simulator-support`. -Run the GUI installer and specify a directory you would like the environment -installed in. The installer will retrieve a list of possible packages, when this -is done press "Continue". The MinGW Installation Manager is then launched. +.. note:: -The following packages need selecting by checking the tick box and selecting -"Mark for installation" + Ensure that path to the simulator executable is appended to the ``PATH`` environment variable. -.. code-block:: bash - Basic Installation - -- mingw-developer-tools - -- mingw32-base - -- mingw32-gcc-g++ - -- msys-base +.. _installation_via_pip:: -From the Installation menu then select "Apply Changes", in the next dialog -select "Apply". +Cocotb Installation via PIP +--------------------------- -When installed a shell can be opened using the :file:`msys.bat` file located under -the :file:`/msys/1.0/` +.. versionadded:: 1.2 -Python can be downloaded from https://www.python.org/downloads/windows/. -Run the installer and download to your chosen location. +Cocotb can be installed by running -It is beneficial to add the path to Python to the Windows system ``PATH`` variable -so it can be used easily from inside Msys. +.. code-block:: bash -Once inside the Msys shell commands as given here will work as expected. + pip install cocotb -macOS Packages --------------- +.. seealso:: -You need a few packages installed to get cocotb running on macOS. -Installing a package manager really helps things out here. + For user-local installation, follow the `pip User Guide `_. -`Brew `_ seems to be the most popular, so we'll assume you have that installed. +To install the development version of cocotb: .. code-block:: bash - brew install python icarus-verilog gtkwave + pip install https://github.com/cocotb/cocotb/archive/master.zip + +.. warning:: + + ``pip`` may belong to a different python installation to what you expect. + Use ``pip -V`` to check. + If this shows Python 2.7, use ``pip3`` or ``python3 -m pip`` in place of ``pip`` in the commands below. + +.. note:: + + After installation, you should be able to execute ``cocotb-config``. + If it is not found, you need to append its location to the ``PATH`` environment variable. + This may happen when you use the ``--user`` option to ``pip``, in which case the location is documented :ref:`here `. Running your first Example diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index eac54c96..06697253 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -107,6 +107,11 @@ cocotb currently only supports VPI for Synopsys VCS, not VHPI. Aldec Riviera-PRO ================= +.. note:: + + On Windows, do not install the C++ compiler, i.e. unselect it during the installation process of Riviera-PRO. + (A workaround is to remove or rename the ``mingw`` directory located in the Riviera-PRO installation directory.) + The :envvar:`LICENSE_QUEUE` environment variable can be used for this simulator – this setting will be mirrored in the TCL ``license_queue`` variable to control runtime license checkouts. @@ -129,14 +134,19 @@ See :ref:`sim-modelsim`. Mentor ModelSim =============== -Any ModelSim PE or ModelSim PE derivative (like ModelSim Microsemi, Intel, Lattice Edition) does not support the VHDL FLI feature. -If you try to run with FLI enabled, you will see a ``vsim-FLI-3155`` error: +.. note:: + + In order to use FLI (for VHDL), a ``vdbg`` executable from the simulator installation directory needs to be available on the ``PATH`` during cocotb installation. + This is needed to access the proprietary ``mti.h`` header file. + +Any ModelSim PE or ModelSim PE derivatives (like the ModelSim Microsemi, Intel, Lattice Editions) do not support the VHDL FLI feature. +If you try to use them with FLI, you will see a ``vsim-FLI-3155`` error: .. code-block:: bash ** Error (suppressible): (vsim-FLI-3155) The FLI is not enabled in this version of ModelSim. -ModelSim DE and SE (and Questa, of course) supports the FLI. +ModelSim DE and SE (and Questa, of course) support the FLI. .. _sim-incisive: From 7ae29eab31e6482a5f0524a3981b4f565c5df20d Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Tue, 14 Apr 2020 11:58:43 +0100 Subject: [PATCH 2139/2656] Move simulator module methods to instance methods. This changes instances of `simulator.some_function(some_handle, *args)` to `some_handle.some_function(*args)`. Specifically, this makes all of the following methods of `gpi_sim_hdl`: * `get_signal_val_long` * `set_signal_val_long` * `get_signal_val_str` * `set_signal_val_str` * `get_signal_val_binstr` * `set_signal_val_binstr` * `get_signal_val_real` * `set_signal_val_real` * `get_definition_name` * `get_definition_file` * `get_handle_by_name` * `get_handle_by_index` * `get_name_string` * `get_type_string` * `get_type` * `get_const` * `get_num_elems` * `get_range` * `iterate` Additionally, `simulator.deregister_callback(gpi_cb_hdl)` is moved to `gpi_cb_hdl.deregister`. This removes a lot of type-checking from our code, making it significantly shorter and hopefully faster. This potentially also makes it easier to mock out simulator handle objects. --- cocotb/handle.py | 68 +++-- .../share/lib/simulator/simulatormodule.cpp | 281 +++++++++--------- cocotb/share/lib/simulator/simulatormodule.h | 46 +-- cocotb/triggers.py | 2 +- 4 files changed, 171 insertions(+), 226 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 1be1d82e..a01bc275 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -77,14 +77,14 @@ def __init__(self, handle, path): self._sub_handles = {} # Dictionary of children self._invalid_sub_handles = set() # Set of invalid queries - self._name = simulator.get_name_string(self._handle) - self._type = simulator.get_type_string(self._handle) + self._name = self._handle.get_name_string() + self._type = self._handle.get_type_string() self._fullname = self._name + "(%s)" % self._type self._path = self._name if path is None else path self._log = SimLog("cocotb.%s" % self._name) self._log.debug("Created") - self._def_name = simulator.get_definition_name(self._handle) - self._def_file = simulator.get_definition_file(self._handle) + self._def_name = self._handle.get_definition_name() + self._def_file = self._handle.get_definition_file() def get_definition_name(self): return self._def_name @@ -101,7 +101,7 @@ def __len__(self): For vectors this is the number of bits. """ if self._len is None: - self._len = simulator.get_num_elems(self._handle) + self._len = self._handle.get_num_elems() return self._len def __eq__(self, other): @@ -189,8 +189,8 @@ def _discover_all(self): if self._discovered: return self._log.debug("Discovering all on %s", self._name) - for thing in simulator.iterate(self._handle, simulator.OBJECTS): - name = simulator.get_name_string(thing) + for thing in self._handle.iterate(simulator.OBJECTS): + name = thing.get_name_string() try: hdl = SimHandle(thing, self._child_path(name)) except TestError as e: @@ -235,7 +235,7 @@ def __get_sub_handle_by_name(self, name): if name in self._invalid_sub_handles: return None - new_handle = simulator.get_handle_by_name(self._handle, name) + new_handle = self._handle.get_handle_by_name(name) if not new_handle: self._invalid_sub_handles.add(name) @@ -336,7 +336,7 @@ def __getitem__(self, index): raise IndexError("Slice indexing is not supported") if index in self._sub_handles: return self._sub_handles[index] - new_handle = simulator.get_handle_by_index(self._handle, index) + new_handle = self._handle.get_handle_by_index(index) if not new_handle: raise IndexError("%s contains no object at index %d" % (self._name, index)) path = self._path + "[" + str(index) + "]" @@ -434,13 +434,13 @@ def __init__(self, handle, path, handle_type): """ NonHierarchyObject.__init__(self, handle, path) if handle_type in [simulator.INTEGER, simulator.ENUM]: - self._value = simulator.get_signal_val_long(self._handle) + self._value = self._handle.get_signal_val_long() elif handle_type == simulator.REAL: - self._value = simulator.get_signal_val_real(self._handle) + self._value = self._handle.get_signal_val_real() elif handle_type == simulator.STRING: - self._value = simulator.get_signal_val_str(self._handle) + self._value = self._handle.get_signal_val_str() else: - val = simulator.get_signal_val_binstr(self._handle) + val = self._handle.get_signal_val_binstr() self._value = BinaryValue(n_bits=len(val)) try: self._value.binstr = val @@ -466,7 +466,7 @@ class NonHierarchyIndexableObject(NonHierarchyObject): """ A non-hierarchy indexable object. """ def __init__(self, handle, path): NonHierarchyObject.__init__(self, handle, path) - self._range = simulator.get_range(self._handle) + self._range = self._handle.get_range() def __setitem__(self, index, value): """Provide transparent assignment to indexed array handles.""" @@ -479,7 +479,7 @@ def __getitem__(self, index): raise IndexError("%s is not indexable. Unable to get object at index %d" % (self._fullname, index)) if index in self._sub_handles: return self._sub_handles[index] - new_handle = simulator.get_handle_by_index(self._handle, index) + new_handle = self._handle.get_handle_by_index(index) if not new_handle: raise IndexError("%s contains no object at index %d" % (self._fullname, index)) path = self._path + "[" + str(index) + "]" @@ -564,11 +564,11 @@ class NonConstantObject(NonHierarchyIndexableObject): def drivers(self): """An iterator for gathering all drivers for a signal.""" - return simulator.iterate(self._handle, simulator.DRIVERS) + return self._handle.iterate(simulator.DRIVERS) def loads(self): """An iterator for gathering all loads on a signal.""" - return simulator.iterate(self._handle, simulator.LOADS) + return self._handle.iterate(simulator.LOADS) class _SetAction: """Base class representing the type of action used while write-accessing a handle.""" @@ -623,7 +623,7 @@ def setimmediatevalue(self, value): value, set_action = self._check_for_set_action(value) if isinstance(value, int) and value < 0x7fffffff and len(self) <= 32: - simulator.set_signal_val_long(self._handle, set_action, value) + self._handle.set_signal_val_long(set_action, value) return if isinstance(value, ctypes.Structure): value = BinaryValue(value=cocotb.utils.pack(value), n_bits=len(self)) @@ -647,7 +647,7 @@ def setimmediatevalue(self, value): "Unsupported type for value assignment: {} ({!r})" .format(type(value), value)) - simulator.set_signal_val_binstr(self._handle, set_action, value.binstr) + self._handle.set_signal_val_binstr(set_action, value.binstr) def _check_for_set_action(self, value): if not isinstance(value, _SetAction): @@ -664,7 +664,7 @@ def value(self): Use :meth:`setimmediatevalue` to set the value immediately. """ - binstr = simulator.get_signal_val_binstr(self._handle) + binstr = self._handle.get_signal_val_binstr() result = BinaryValue(binstr, len(binstr)) return result @@ -708,11 +708,11 @@ def setimmediatevalue(self, value): "Unsupported type for real value assignment: {} ({!r})" .format(type(value), value)) - simulator.set_signal_val_real(self._handle, set_action, value) + self._handle.set_signal_val_real(set_action, value) @ModifiableObject.value.getter def value(self): - return simulator.get_signal_val_real(self._handle) + return self._handle.get_signal_val_real() def __float__(self): return float(self.value) @@ -743,11 +743,11 @@ def setimmediatevalue(self, value): "Unsupported type for enum value assignment: {} ({!r})" .format(type(value), value)) - simulator.set_signal_val_long(self._handle, set_action, value) + self._handle.set_signal_val_long(set_action, value) @ModifiableObject.value.getter def value(self): - return simulator.get_signal_val_long(self._handle) + return self._handle.get_signal_val_long() class IntegerObject(ModifiableObject): @@ -775,11 +775,11 @@ def setimmediatevalue(self, value): "Unsupported type for integer value assignment: {} ({!r})" .format(type(value), value)) - simulator.set_signal_val_long(self._handle, set_action, value) + self._handle.set_signal_val_long(set_action, value) @ModifiableObject.value.getter def value(self): - return simulator.get_signal_val_long(self._handle) + return self._handle.get_signal_val_long() class StringObject(ModifiableObject): @@ -818,11 +818,11 @@ def setimmediatevalue(self, value): "Unsupported type for string value assignment: {} ({!r})" .format(type(value), value)) - simulator.set_signal_val_str(self._handle, set_action, value) + self._handle.set_signal_val_str(set_action, value) @ModifiableObject.value.getter def value(self): - return simulator.get_signal_val_str(self._handle) + return self._handle.get_signal_val_str() _handle2obj = {} @@ -860,13 +860,15 @@ def SimHandle(handle, path=None): except KeyError: pass - t = simulator.get_type(handle) + t = handle.get_type() # Special case for constants - if simulator.get_const(handle) and t not in [simulator.MODULE, - simulator.STRUCTURE, - simulator.NETARRAY, - simulator.GENARRAY]: + if handle.get_const() and t not in [ + simulator.MODULE, + simulator.STRUCTURE, + simulator.NETARRAY, + simulator.GENARRAY, + ]: obj = ConstantObject(handle, path, t) _handle2obj[handle] = obj return obj diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index b35305b1..72fcffd5 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -135,13 +135,6 @@ namespace { PyTypeObject gpi_hdl_Object::py_type; template<> PyTypeObject gpi_hdl_Object::py_type; - - - // get the python type corresponding to the static type of a PyObject pointer - template - PyTypeObject* python_type_for() { - return &std::remove_pointer::type::py_type; - } } @@ -600,19 +593,17 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) } -static PyObject *iterate(PyObject *self, PyObject *args) +static PyObject *iterate(gpi_hdl_Object *self, PyObject *args) { - COCOTB_UNUSED(self); - gpi_hdl_Object *hdl_obj; int type; gpi_iterator_hdl result; PyObject *res; - if (!PyArg_ParseTuple(args, "O!i", python_type_for(), &hdl_obj, &type)) { + if (!PyArg_ParseTuple(args, "i", &type)) { return NULL; } - result = gpi_iterate(hdl_obj->hdl, (gpi_iterator_sel_t)type); + result = gpi_iterate(self->hdl, (gpi_iterator_sel_t)type); res = gpi_hdl_New(result); @@ -633,202 +624,162 @@ static PyObject *next(gpi_hdl_Object *self) return gpi_hdl_New(result); } +// Raise an exception on failure +// Return None if for example get bin_string on enum? -static PyObject *get_signal_val_binstr(PyObject *self, PyObject *args) +static PyObject *get_signal_val_binstr(gpi_hdl_Object *self, PyObject *args) { - COCOTB_UNUSED(self); - gpi_hdl_Object *hdl_obj; + COCOTB_UNUSED(args); const char *result; PyObject *retstr; - if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { - return NULL; - } - - result = gpi_get_signal_value_binstr(hdl_obj->hdl); + result = gpi_get_signal_value_binstr(self->hdl); retstr = Py_BuildValue("s", result); return retstr; } -static PyObject *get_signal_val_str(PyObject *self, PyObject *args) +static PyObject *get_signal_val_str(gpi_hdl_Object *self, PyObject *args) { - COCOTB_UNUSED(self); - gpi_hdl_Object *hdl_obj; + COCOTB_UNUSED(args); const char *result; PyObject *retstr; - if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { - return NULL; - } - - result = gpi_get_signal_value_str(hdl_obj->hdl); + result = gpi_get_signal_value_str(self->hdl); retstr = PyBytes_FromString(result); return retstr; } -static PyObject *get_signal_val_real(PyObject *self, PyObject *args) +static PyObject *get_signal_val_real(gpi_hdl_Object *self, PyObject *args) { - COCOTB_UNUSED(self); - gpi_hdl_Object *hdl_obj; + COCOTB_UNUSED(args); double result; PyObject *retval; - if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { - return NULL; - } - - result = gpi_get_signal_value_real(hdl_obj->hdl); + result = gpi_get_signal_value_real(self->hdl); retval = Py_BuildValue("d", result); return retval; } -static PyObject *get_signal_val_long(PyObject *self, PyObject *args) +static PyObject *get_signal_val_long(gpi_hdl_Object *self, PyObject *args) { - COCOTB_UNUSED(self); - gpi_hdl_Object *hdl_obj; + COCOTB_UNUSED(args); long result; PyObject *retval; - if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { - return NULL; - } - - result = gpi_get_signal_value_long(hdl_obj->hdl); + result = gpi_get_signal_value_long(self->hdl); retval = Py_BuildValue("l", result); return retval; } -static PyObject *set_signal_val_binstr(PyObject *self, PyObject *args) +static PyObject *set_signal_val_binstr(gpi_hdl_Object *self, PyObject *args) { - COCOTB_UNUSED(self); - gpi_hdl_Object *hdl_obj; const char *binstr; gpi_set_action_t action; - if (!PyArg_ParseTuple(args, "O!is", python_type_for(), &hdl_obj, &action, &binstr)) { + if (!PyArg_ParseTuple(args, "is", &action, &binstr)) { return NULL; } - gpi_set_signal_value_binstr(hdl_obj->hdl, binstr, action); + gpi_set_signal_value_binstr(self->hdl, binstr, action); Py_RETURN_NONE; } -static PyObject *set_signal_val_str(PyObject *self, PyObject *args) +static PyObject *set_signal_val_str(gpi_hdl_Object *self, PyObject *args) { - COCOTB_UNUSED(self); - gpi_hdl_Object *hdl_obj; gpi_set_action_t action; const char *str; - if (!PyArg_ParseTuple(args, "O!iy", python_type_for(), &hdl_obj, &action, &str)) { + if (!PyArg_ParseTuple(args, "iy", &action, &str)) { return NULL; } - gpi_set_signal_value_str(hdl_obj->hdl, str, action); + gpi_set_signal_value_str(self->hdl, str, action); Py_RETURN_NONE; } -static PyObject *set_signal_val_real(PyObject *self, PyObject *args) +static PyObject *set_signal_val_real(gpi_hdl_Object *self, PyObject *args) { - COCOTB_UNUSED(self); - gpi_hdl_Object *hdl_obj; double value; gpi_set_action_t action; - if (!PyArg_ParseTuple(args, "O!id", python_type_for(), &hdl_obj, &action, &value)) { + if (!PyArg_ParseTuple(args, "id", &action, &value)) { return NULL; } - gpi_set_signal_value_real(hdl_obj->hdl, value, action); + gpi_set_signal_value_real(self->hdl, value, action); Py_RETURN_NONE; } -static PyObject *set_signal_val_long(PyObject *self, PyObject *args) +static PyObject *set_signal_val_long(gpi_hdl_Object *self, PyObject *args) { - COCOTB_UNUSED(self); - gpi_hdl_Object *hdl_obj; long value; gpi_set_action_t action; - if (!PyArg_ParseTuple(args, "O!il", python_type_for(), &hdl_obj, &action, &value)) { + if (!PyArg_ParseTuple(args, "il", &action, &value)) { return NULL; } - gpi_set_signal_value_long(hdl_obj->hdl, value, action); + gpi_set_signal_value_long(self->hdl, value, action); Py_RETURN_NONE; } -static PyObject *get_definition_name(PyObject *self, PyObject *args) +static PyObject *get_definition_name(gpi_hdl_Object *self, PyObject *args) { - COCOTB_UNUSED(self); + COCOTB_UNUSED(args); const char* result; - gpi_hdl_Object *hdl_obj; PyObject *retstr; - if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { - return NULL; - } - - result = gpi_get_definition_name(hdl_obj->hdl); + result = gpi_get_definition_name(self->hdl); retstr = Py_BuildValue("s", result); return retstr; } -static PyObject *get_definition_file(PyObject *self, PyObject *args) +static PyObject *get_definition_file(gpi_hdl_Object *self, PyObject *args) { - COCOTB_UNUSED(self); + COCOTB_UNUSED(args); const char* result; - gpi_hdl_Object *hdl_obj; PyObject *retstr; - if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { - return NULL; - } - - result = gpi_get_definition_file(hdl_obj->hdl); + result = gpi_get_definition_file(self->hdl); retstr = Py_BuildValue("s", result); return retstr; } -static PyObject *get_handle_by_name(PyObject *self, PyObject *args) +static PyObject *get_handle_by_name(gpi_hdl_Object *self, PyObject *args) { - COCOTB_UNUSED(self); const char *name; - gpi_hdl_Object *hdl_obj; gpi_sim_hdl result; PyObject *res; - if (!PyArg_ParseTuple(args, "O!s", python_type_for(), &hdl_obj, &name)) { + if (!PyArg_ParseTuple(args, "s", &name)) { return NULL; } - result = gpi_get_handle_by_name(hdl_obj->hdl, name); + result = gpi_get_handle_by_name(self->hdl, name); res = gpi_hdl_New(result); return res; } -static PyObject *get_handle_by_index(PyObject *self, PyObject *args) +static PyObject *get_handle_by_index(gpi_hdl_Object *self, PyObject *args) { - COCOTB_UNUSED(self); int32_t index; - gpi_hdl_Object *hdl_obj; gpi_sim_hdl result; PyObject *value; - if (!PyArg_ParseTuple(args, "O!i", python_type_for(), &hdl_obj, &index)) { + if (!PyArg_ParseTuple(args, "i", &index)) { return NULL; } - result = gpi_get_handle_by_index(hdl_obj->hdl, index); + result = gpi_get_handle_by_index(self->hdl, index); value = gpi_hdl_New(result); @@ -858,69 +809,50 @@ static PyObject *get_root_handle(PyObject *self, PyObject *args) } -static PyObject *get_name_string(PyObject *self, PyObject *args) +static PyObject *get_name_string(gpi_hdl_Object *self, PyObject *args) { + COCOTB_UNUSED(args); COCOTB_UNUSED(self); const char *result; - gpi_hdl_Object *hdl_obj; PyObject *retstr; - if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { - return NULL; - } - - result = gpi_get_signal_name_str(hdl_obj->hdl); + result = gpi_get_signal_name_str(self->hdl); retstr = Py_BuildValue("s", result); return retstr; } -static PyObject *get_type(PyObject *self, PyObject *args) +static PyObject *get_type(gpi_hdl_Object *self, PyObject *args) { - COCOTB_UNUSED(self); + COCOTB_UNUSED(args); gpi_objtype_t result; - gpi_hdl_Object *hdl_obj; PyObject *pyresult; - if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { - return NULL; - } - - result = gpi_get_object_type(hdl_obj->hdl); + result = gpi_get_object_type(self->hdl); pyresult = Py_BuildValue("i", (int)result); return pyresult; } -static PyObject *get_const(PyObject *self, PyObject *args) +static PyObject *get_const(gpi_hdl_Object *self, PyObject *args) { - COCOTB_UNUSED(self); + COCOTB_UNUSED(args); int result; - gpi_hdl_Object *hdl_obj; PyObject *pyresult; - if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { - return NULL; - } - - result = gpi_is_constant(hdl_obj->hdl); + result = gpi_is_constant(self->hdl); pyresult = Py_BuildValue("i", result); return pyresult; } -static PyObject *get_type_string(PyObject *self, PyObject *args) +static PyObject *get_type_string(gpi_hdl_Object *self, PyObject *args) { - COCOTB_UNUSED(self); + COCOTB_UNUSED(args); const char *result; - gpi_hdl_Object *hdl_obj; PyObject *retstr; - if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { - return NULL; - } - - result = gpi_get_signal_type_str(hdl_obj->hdl); + result = gpi_get_signal_type_str(self->hdl); retstr = Py_BuildValue("s", result); return retstr; @@ -963,35 +895,25 @@ static PyObject *get_precision(PyObject *self, PyObject *args) return retint; } -static PyObject *get_num_elems(PyObject *self, PyObject *args) +static PyObject *get_num_elems(gpi_hdl_Object *self, PyObject *args) { - COCOTB_UNUSED(self); - gpi_hdl_Object *hdl_obj; + COCOTB_UNUSED(args); PyObject *retstr; - if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { - return NULL; - } - - int elems = gpi_get_num_elems(hdl_obj->hdl); + int elems = gpi_get_num_elems(self->hdl); retstr = Py_BuildValue("i", elems); return retstr; } -static PyObject *get_range(PyObject *self, PyObject *args) +static PyObject *get_range(gpi_hdl_Object *self, PyObject *args) { - COCOTB_UNUSED(self); - gpi_hdl_Object *hdl_obj; + COCOTB_UNUSED(args); PyObject *retstr; - if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { - return NULL; - } - - int indexable = gpi_is_indexable(hdl_obj->hdl); - int rng_left = gpi_get_range_left(hdl_obj->hdl); - int rng_right = gpi_get_range_right(hdl_obj->hdl); + int indexable = gpi_is_indexable(self->hdl); + int rng_left = gpi_get_range_left(self->hdl); + int rng_right = gpi_get_range_right(self->hdl); if (indexable) retstr = Py_BuildValue("(i,i)", rng_left, rng_right); @@ -1011,18 +933,12 @@ static PyObject *stop_simulator(PyObject *self, PyObject *args) } -static PyObject *deregister_callback(PyObject *self, PyObject *args) +static PyObject *deregister(gpi_hdl_Object *self, PyObject *args) { - COCOTB_UNUSED(self); - gpi_hdl_Object *hdl_obj; - + COCOTB_UNUSED(args); FENTER - if (!PyArg_ParseTuple(args, "O!", python_type_for(), &hdl_obj)) { - return NULL; - } - - gpi_deregister_callback(hdl_obj->hdl); + gpi_deregister_callback(self->hdl); FEXIT Py_RETURN_NONE; @@ -1128,12 +1044,73 @@ PyMODINIT_FUNC PyInit_simulator(void) return simulator; } +static PyMethodDef gpi_sim_hdl_methods[] = { + {"get_signal_val_long", + (PyCFunction)get_signal_val_long, METH_NOARGS, + "Get the value of a signal as a long"}, + {"get_signal_val_str", + (PyCFunction)get_signal_val_str, METH_NOARGS, + "Get the value of a signal as an ASCII string"}, + {"get_signal_val_binstr", + (PyCFunction)get_signal_val_binstr, METH_NOARGS, + "Get the value of a signal as a binary string"}, + {"get_signal_val_real", + (PyCFunction)get_signal_val_real, METH_NOARGS, + "Get the value of a signal as a double precision float"}, + {"set_signal_val_long", + (PyCFunction)set_signal_val_long, METH_VARARGS, + "Set the value of a signal using a long"}, + {"set_signal_val_str", + (PyCFunction)set_signal_val_str, METH_VARARGS, + "Set the value of a signal using an NUL-terminated 8-bit string"}, + {"set_signal_val_binstr", + (PyCFunction)set_signal_val_binstr, METH_VARARGS, + "Set the value of a signal using a string with a character per bit"}, + {"set_signal_val_real", + (PyCFunction)set_signal_val_real, METH_VARARGS, + "Set the value of a signal using a double precision float"}, + {"get_definition_name", + (PyCFunction)get_definition_name, METH_NOARGS, + "Get the name of a GPI object's definition"}, + {"get_definition_file", + (PyCFunction)get_definition_file, METH_NOARGS, + "Get the file that sources the object's definition"}, + {"get_handle_by_name", + (PyCFunction)get_handle_by_name, METH_VARARGS, + "Get handle of a named object"}, + {"get_handle_by_index", + (PyCFunction)get_handle_by_index, METH_VARARGS, + "Get handle of a object at an index in a parent"}, + {"get_name_string", + (PyCFunction)get_name_string, METH_NOARGS, + "Get the name of an object as a string"}, + {"get_type_string", + (PyCFunction)get_type_string, METH_NOARGS, + "Get the type of an object as a string"}, + {"get_type", + (PyCFunction)get_type, METH_NOARGS, + "Get the type of an object, mapped to a GPI enumeration"}, + {"get_const", + (PyCFunction)get_const, METH_NOARGS, + "Get a flag indicating whether the object is a constant"}, + {"get_num_elems", + (PyCFunction)get_num_elems, METH_NOARGS, + "Get the number of elements contained in the handle"}, + {"get_range", + (PyCFunction)get_range, METH_NOARGS, + "Get the range of elements (tuple) contained in the handle, returns None if not indexable"}, + {"iterate", + (PyCFunction)iterate, METH_VARARGS, + "Get an iterator handle to loop over all members in an object"}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; // putting these at the bottom means that all the functions above are accessible template<> PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { auto type = fill_common_slots(); type.tp_name = "gpi_sim_hdl"; + type.tp_methods = gpi_sim_hdl_methods; return type; }(); @@ -1146,9 +1123,17 @@ PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { return type; }(); +static PyMethodDef gpi_cb_hdl_methods[] = { + {"deregister", + (PyCFunction)deregister, METH_NOARGS, + "De-register this callback"}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + template<> PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { auto type = fill_common_slots(); type.tp_name = "gpi_cb_hdl"; + type.tp_methods = gpi_cb_hdl_methods; return type; }(); diff --git a/cocotb/share/lib/simulator/simulatormodule.h b/cocotb/share/lib/simulator/simulatormodule.h index 1a88d5e9..9f25242c 100644 --- a/cocotb/share/lib/simulator/simulatormodule.h +++ b/cocotb/share/lib/simulator/simulatormodule.h @@ -54,76 +54,34 @@ typedef struct t_callback_data { static PyObject *error_out(PyObject *m, PyObject *args); static PyObject *log_msg(PyObject *self, PyObject *args); -// Raise an exception on failure -// Return None if for example get bin_string on enum? -static PyObject *get_signal_val_long(PyObject *self, PyObject *args); -static PyObject *get_signal_val_real(PyObject *self, PyObject *args); -static PyObject *get_signal_val_str(PyObject *self, PyObject *args); -static PyObject *get_signal_val_binstr(PyObject *self, PyObject *args); -static PyObject *set_signal_val_long(PyObject *self, PyObject *args); -static PyObject *set_signal_val_real(PyObject *self, PyObject *args); -static PyObject *set_signal_val_str(PyObject *self, PyObject *args); -static PyObject *set_signal_val_binstr(PyObject *self, PyObject *args); -static PyObject *get_definition_name(PyObject *self, PyObject *args); -static PyObject *get_definition_file(PyObject *self, PyObject *args); -static PyObject *get_handle_by_name(PyObject *self, PyObject *args); -static PyObject *get_handle_by_index(PyObject *self, PyObject *args); -static PyObject *get_root_handle(PyObject *self, PyObject *args); -static PyObject *get_name_string(PyObject *self, PyObject *args); -static PyObject *get_type(PyObject *self, PyObject *args); -static PyObject *get_const(PyObject *self, PyObject *args); -static PyObject *get_type_string(PyObject *self, PyObject *args); -static PyObject *get_num_elems(PyObject *self, PyObject *args); -static PyObject *get_range(PyObject *self, PyObject *args); static PyObject *register_timed_callback(PyObject *self, PyObject *args); static PyObject *register_value_change_callback(PyObject *self, PyObject *args); static PyObject *register_readonly_callback(PyObject *self, PyObject *args); static PyObject *register_nextstep_callback(PyObject *self, PyObject *args); static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args); -static PyObject *stop_simulator(PyObject *self, PyObject *args); -static PyObject *iterate(PyObject *self, PyObject *args); +static PyObject *get_root_handle(PyObject *self, PyObject *args); +static PyObject *stop_simulator(PyObject *self, PyObject *args); static PyObject *get_sim_time(PyObject *self, PyObject *args); static PyObject *get_precision(PyObject *self, PyObject *args); -static PyObject *deregister_callback(PyObject *self, PyObject *args); static PyObject *log_level(PyObject *self, PyObject *args); static PyMethodDef SimulatorMethods[] = { {"log_msg", log_msg, METH_VARARGS, "Log a message"}, - {"get_signal_val_long", get_signal_val_long, METH_VARARGS, "Get the value of a signal as a long"}, - {"get_signal_val_str", get_signal_val_str, METH_VARARGS, "Get the value of a signal as an ASCII string"}, - {"get_signal_val_binstr", get_signal_val_binstr, METH_VARARGS, "Get the value of a signal as a binary string"}, - {"get_signal_val_real", get_signal_val_real, METH_VARARGS, "Get the value of a signal as a double precision float"}, - {"set_signal_val_long", set_signal_val_long, METH_VARARGS, "Set the value of a signal using a long"}, - {"set_signal_val_str", set_signal_val_str, METH_VARARGS, "Set the value of a signal using an NUL-terminated 8-bit string"}, - {"set_signal_val_binstr", set_signal_val_binstr, METH_VARARGS, "Set the value of a signal using a string with a character per bit"}, - {"set_signal_val_real", set_signal_val_real, METH_VARARGS, "Set the value of a signal using a double precision float"}, - {"get_definition_name", get_definition_name, METH_VARARGS, "Get the name of a GPI object's definition"}, - {"get_definition_file", get_definition_file, METH_VARARGS, "Get the file that sources the object's definition"}, - {"get_handle_by_name", get_handle_by_name, METH_VARARGS, "Get handle of a named object"}, - {"get_handle_by_index", get_handle_by_index, METH_VARARGS, "Get handle of a object at an index in a parent"}, {"get_root_handle", get_root_handle, METH_VARARGS, "Get the root handle"}, - {"get_name_string", get_name_string, METH_VARARGS, "Get the name of an object as a string"}, - {"get_type_string", get_type_string, METH_VARARGS, "Get the type of an object as a string"}, - {"get_type", get_type, METH_VARARGS, "Get the type of an object, mapped to a GPI enumeration"}, - {"get_const", get_const, METH_VARARGS, "Get a flag indicating whether the object is a constant"}, - {"get_num_elems", get_num_elems, METH_VARARGS, "Get the number of elements contained in the handle"}, - {"get_range", get_range, METH_VARARGS, "Get the range of elements (tuple) contained in the handle, returns None if not indexable"}, {"register_timed_callback", register_timed_callback, METH_VARARGS, "Register a timed callback"}, {"register_value_change_callback", register_value_change_callback, METH_VARARGS, "Register a signal change callback"}, {"register_readonly_callback", register_readonly_callback, METH_VARARGS, "Register a callback for the read-only section"}, {"register_nextstep_callback", register_nextstep_callback, METH_VARARGS, "Register a callback for the NextSimTime callback"}, {"register_rwsynch_callback", register_rwsynch_callback, METH_VARARGS, "Register a callback for the read-write section"}, {"stop_simulator", stop_simulator, METH_VARARGS, "Instruct the attached simulator to stop"}, - {"iterate", iterate, METH_VARARGS, "Get an iterator handle to loop over all members in an object"}, {"log_level", log_level, METH_VARARGS, "Set the log level for GPI"}, // FIXME METH_NOARGS => initialization from incompatible pointer type {"get_sim_time", get_sim_time, METH_VARARGS, "Get the current simulation time as an int tuple"}, {"get_precision", get_precision, METH_VARARGS, "Get the precision of the simulator"}, - {"deregister_callback", deregister_callback, METH_VARARGS, "De-register a callback"}, {"error_out", error_out, METH_NOARGS, NULL}, {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/cocotb/triggers.py b/cocotb/triggers.py index cbbd79e1..23e81b81 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -153,7 +153,7 @@ def __init__(self): def unprime(self): """Disable a primed trigger, can be re-primed.""" if self.cbhdl is not None: - simulator.deregister_callback(self.cbhdl) + self.cbhdl.deregister() self.cbhdl = None Trigger.unprime(self) From ae2383a29bde981486491118b23df74150e35f17 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Tue, 14 Apr 2020 12:36:44 +0100 Subject: [PATCH 2140/2656] Combine declarations and initializations to save 70 lines These weren't really adding any clarity. --- .../share/lib/simulator/simulatormodule.cpp | 141 ++++-------------- 1 file changed, 32 insertions(+), 109 deletions(-) diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index 72fcffd5..d19d24fa 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -596,18 +596,14 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) static PyObject *iterate(gpi_hdl_Object *self, PyObject *args) { int type; - gpi_iterator_hdl result; - PyObject *res; if (!PyArg_ParseTuple(args, "i", &type)) { return NULL; } - result = gpi_iterate(self->hdl, (gpi_iterator_sel_t)type); + gpi_iterator_hdl result = gpi_iterate(self->hdl, (gpi_iterator_sel_t)type); - res = gpi_hdl_New(result); - - return res; + return gpi_hdl_New(result); } @@ -630,50 +626,30 @@ static PyObject *next(gpi_hdl_Object *self) static PyObject *get_signal_val_binstr(gpi_hdl_Object *self, PyObject *args) { COCOTB_UNUSED(args); - const char *result; - PyObject *retstr; - - result = gpi_get_signal_value_binstr(self->hdl); - retstr = Py_BuildValue("s", result); - - return retstr; + const char *result = gpi_get_signal_value_binstr(self->hdl); + return Py_BuildValue("s", result); } static PyObject *get_signal_val_str(gpi_hdl_Object *self, PyObject *args) { COCOTB_UNUSED(args); - const char *result; - PyObject *retstr; - - result = gpi_get_signal_value_str(self->hdl); - retstr = PyBytes_FromString(result); - - return retstr; + const char *result = gpi_get_signal_value_str(self->hdl); + return PyBytes_FromString(result); } static PyObject *get_signal_val_real(gpi_hdl_Object *self, PyObject *args) { COCOTB_UNUSED(args); - double result; - PyObject *retval; - - result = gpi_get_signal_value_real(self->hdl); - retval = Py_BuildValue("d", result); - - return retval; + double result = gpi_get_signal_value_real(self->hdl); + return Py_BuildValue("d", result); } static PyObject *get_signal_val_long(gpi_hdl_Object *self, PyObject *args) { COCOTB_UNUSED(args); - long result; - PyObject *retval; - - result = gpi_get_signal_value_long(self->hdl); - retval = Py_BuildValue("l", result); - - return retval; + long result = gpi_get_signal_value_long(self->hdl); + return Py_BuildValue("l", result); } static PyObject *set_signal_val_binstr(gpi_hdl_Object *self, PyObject *args) @@ -731,81 +707,58 @@ static PyObject *set_signal_val_long(gpi_hdl_Object *self, PyObject static PyObject *get_definition_name(gpi_hdl_Object *self, PyObject *args) { COCOTB_UNUSED(args); - const char* result; - PyObject *retstr; - - result = gpi_get_definition_name(self->hdl); - retstr = Py_BuildValue("s", result); - - return retstr; + const char* result = gpi_get_definition_name(self->hdl); + return Py_BuildValue("s", result); } static PyObject *get_definition_file(gpi_hdl_Object *self, PyObject *args) { COCOTB_UNUSED(args); - const char* result; - PyObject *retstr; - - result = gpi_get_definition_file(self->hdl); - retstr = Py_BuildValue("s", result); - - return retstr; + const char* result = gpi_get_definition_file(self->hdl); + return Py_BuildValue("s", result); } static PyObject *get_handle_by_name(gpi_hdl_Object *self, PyObject *args) { const char *name; - gpi_sim_hdl result; - PyObject *res; if (!PyArg_ParseTuple(args, "s", &name)) { return NULL; } - result = gpi_get_handle_by_name(self->hdl, name); - - res = gpi_hdl_New(result); + gpi_sim_hdl result = gpi_get_handle_by_name(self->hdl, name); - return res; + return gpi_hdl_New(result); } static PyObject *get_handle_by_index(gpi_hdl_Object *self, PyObject *args) { int32_t index; - gpi_sim_hdl result; - PyObject *value; if (!PyArg_ParseTuple(args, "i", &index)) { return NULL; } - result = gpi_get_handle_by_index(self->hdl, index); - - value = gpi_hdl_New(result); + gpi_sim_hdl result = gpi_get_handle_by_index(self->hdl, index); - return value; + return gpi_hdl_New(result); } static PyObject *get_root_handle(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); const char *name; - gpi_sim_hdl result; - PyObject *value; if (!PyArg_ParseTuple(args, "z", &name)) { return NULL; } - result = gpi_get_root_handle(name); + gpi_sim_hdl result = gpi_get_root_handle(name); if (NULL == result) { Py_RETURN_NONE; } - - value = gpi_hdl_New(result); - - return value; + return gpi_hdl_New(result); } @@ -813,49 +766,29 @@ static PyObject *get_name_string(gpi_hdl_Object *self, PyObject *ar { COCOTB_UNUSED(args); COCOTB_UNUSED(self); - const char *result; - PyObject *retstr; - - result = gpi_get_signal_name_str(self->hdl); - retstr = Py_BuildValue("s", result); - - return retstr; + const char *result = gpi_get_signal_name_str(self->hdl); + return Py_BuildValue("s", result); } static PyObject *get_type(gpi_hdl_Object *self, PyObject *args) { COCOTB_UNUSED(args); - gpi_objtype_t result; - PyObject *pyresult; - - result = gpi_get_object_type(self->hdl); - pyresult = Py_BuildValue("i", (int)result); - - return pyresult; + gpi_objtype_t result = gpi_get_object_type(self->hdl); + return Py_BuildValue("i", (int)result); } static PyObject *get_const(gpi_hdl_Object *self, PyObject *args) { COCOTB_UNUSED(args); - int result; - PyObject *pyresult; - - result = gpi_is_constant(self->hdl); - pyresult = Py_BuildValue("i", result); - - return pyresult; + int result = gpi_is_constant(self->hdl); + return Py_BuildValue("i", result); } static PyObject *get_type_string(gpi_hdl_Object *self, PyObject *args) { COCOTB_UNUSED(args); - const char *result; - PyObject *retstr; - - result = gpi_get_signal_type_str(self->hdl); - retstr = Py_BuildValue("s", result); - - return retstr; + const char *result = gpi_get_signal_type_str(self->hdl); + return Py_BuildValue("s", result); } @@ -889,38 +822,28 @@ static PyObject *get_precision(PyObject *self, PyObject *args) gpi_get_sim_precision(&precision); - PyObject *retint = Py_BuildValue("i", precision); - - - return retint; + return Py_BuildValue("i", precision); } static PyObject *get_num_elems(gpi_hdl_Object *self, PyObject *args) { COCOTB_UNUSED(args); - PyObject *retstr; - int elems = gpi_get_num_elems(self->hdl); - retstr = Py_BuildValue("i", elems); - - return retstr; + return Py_BuildValue("i", elems); } static PyObject *get_range(gpi_hdl_Object *self, PyObject *args) { COCOTB_UNUSED(args); - PyObject *retstr; int indexable = gpi_is_indexable(self->hdl); int rng_left = gpi_get_range_left(self->hdl); int rng_right = gpi_get_range_right(self->hdl); if (indexable) - retstr = Py_BuildValue("(i,i)", rng_left, rng_right); + return Py_BuildValue("(i,i)", rng_left, rng_right); else - retstr = Py_BuildValue(""); - - return retstr; + return Py_BuildValue(""); } static PyObject *stop_simulator(PyObject *self, PyObject *args) From 03363b192c2245cde2c542163af908116cd25fbc Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Wed, 15 Apr 2020 23:08:10 +0100 Subject: [PATCH 2141/2656] Add tests for getting and setting `BinaryValue.buff` (#1615) Add tests for getting and setting `BinaryValue.buff` --- tests/pytest/test_binary_value.py | 36 +++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/pytest/test_binary_value.py b/tests/pytest/test_binary_value.py index 68b2e211..318da381 100644 --- a/tests/pytest/test_binary_value.py +++ b/tests/pytest/test_binary_value.py @@ -242,3 +242,39 @@ def test_backwards_compatibility(): with pytest.raises(TypeError): BinaryValue(value=0, bits=16, n_bits=17) + + +def test_buff_big_endian(): + orig_str = "011011001001" + orig_bytes = b'\x06\xC9' # padding is the high bits of the first byte + + v = BinaryValue(value=orig_str, n_bits=12, bigEndian=True) + assert v.buff == orig_bytes + + # should be unchanged + v.buff = orig_bytes + assert v.buff == orig_bytes + assert v.binstr == orig_str + + # extra bits are stripped because they don't fit into the 12 bits + v.buff = b'\xF6\xC9' + assert v.buff == orig_bytes + assert v.binstr == orig_str + + +def test_buff_little_endian(): + orig_str = "011011001001" + orig_bytes = b'\xC9\x06' # padding is the high bits of the last byte + + v = BinaryValue(value=orig_str, n_bits=12, bigEndian=False) + assert v.buff == orig_bytes + + # should be unchanged + v.buff = orig_bytes + assert v.buff == orig_bytes + assert v.binstr == orig_str + +# extra bits are stripped because they don't fit into the 12 bits + v.buff = b'\xC9\xF6' + assert v.buff == orig_bytes + assert v.binstr == orig_str From e5b1f2f431d4806016917d891b312b36179e329a Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 16 Apr 2020 09:41:26 +0100 Subject: [PATCH 2142/2656] Remove python3 conditional --- examples/endian_swapper/cosim/io.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/examples/endian_swapper/cosim/io.c b/examples/endian_swapper/cosim/io.c index 83f0a1e0..2d07e318 100644 --- a/examples/endian_swapper/cosim/io.c +++ b/examples/endian_swapper/cosim/io.c @@ -113,7 +113,6 @@ int IOWR(unsigned int base, unsigned int address, unsigned int value) return 0; } -#if PY_MAJOR_VERSION >= 3 static struct PyModuleDef io_module = { PyModuleDef_HEAD_INIT, @@ -128,10 +127,4 @@ PyMODINIT_FUNC PyInit_io_module(void) { return PyModule_Create(&io_module); } -#else -PyMODINIT_FUNC initio_module(void) -{ - Py_InitModule("io_module", io_module_methods); -} -#endif From df27322b3674d62894dba0e9396c0b5e14b70328 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 16 Apr 2020 09:55:39 +0100 Subject: [PATCH 2143/2656] Fix sloppy Python error handling in the endian hal example Rather than building argument tuples ourselves, it's easier to just use `PyObject_CallFunction`. This also changes `PyErr_Print` to the somewhat more ominous `PyErr_WriteUnraisable`. --- examples/endian_swapper/cosim/io.c | 99 +++++++++++++++--------------- 1 file changed, 48 insertions(+), 51 deletions(-) diff --git a/examples/endian_swapper/cosim/io.c b/examples/endian_swapper/cosim/io.c index 2d07e318..2f91060b 100644 --- a/examples/endian_swapper/cosim/io.c +++ b/examples/endian_swapper/cosim/io.c @@ -36,80 +36,77 @@ #include #include "io_module.h" +/* Py_SETREF was added in 3.5.2, and only if Py_LIMITED_API is absent */ +#ifndef Py_SETREF + #define Py_SETREF(op, op2) \ + do { \ + PyObject *_py_tmp = (PyObject *)(op); \ + (op) = (op2); \ + Py_DECREF(_py_tmp); \ + } while (0) +#endif + + // Python read function just takes an offset in bytes and a buffer -static PyObject *pWrFunction; +static PyObject *pWrFunction = Py_None; // Python read function just takes an offset in bytes and returns a value -static PyObject *pRdFunction; +static PyObject *pRdFunction = Py_None; // Functions called by Python -static PyObject *set_write_function(PyObject *self, PyObject *args) { - - pWrFunction = PyTuple_GetItem(args, 0); - Py_INCREF(pWrFunction); +static PyObject *set_write_function(PyObject *self, PyObject *args) +{ + PyObject *func; + if (!PyArg_ParseTuple(args, "O:set_write_function", &func)) { + return NULL; + } + Py_INCREF(func); + Py_SETREF(pWrFunction, func); - PyObject *retstr = Py_BuildValue("s", "OK!"); - return retstr; + Py_RETURN_NONE; } -static PyObject *set_read_function(PyObject *self, PyObject *args) { - - pRdFunction = PyTuple_GetItem(args, 0); - Py_INCREF(pRdFunction); +static PyObject *set_read_function(PyObject *self, PyObject *args) +{ + PyObject *func; + if (!PyArg_ParseTuple(args, "O:set_read_function", &func)) { + return NULL; + } + Py_INCREF(func); + Py_SETREF(pRdFunction, func); - PyObject *retstr = Py_BuildValue("s", "OK!"); - return retstr; + Py_RETURN_NONE; } // Functions called by C (exported in a shared library) -unsigned int IORD(unsigned int base, unsigned int address) { - - unsigned int value; - - if (!PyCallable_Check(pRdFunction)) { - printf("Read function not callable...\n"); +unsigned int IORD(unsigned int base, unsigned int address) +{ + PyObject *rv = PyObject_CallFunction(pRdFunction, "I", base + address); + if (rv == NULL) { + PyErr_WriteUnraisable(NULL); return 0; } - PyObject *call_args = PyTuple_New(1); - PyObject *rv; - - PyTuple_SetItem(call_args, 0, PyInt_FromLong(base + address)); - - rv = PyObject_CallObject(pRdFunction, call_args); - value = PyInt_AsLong(rv); - - if (PyErr_Occurred()) - PyErr_Print(); - + long value = PyInt_AsLong(rv); Py_DECREF(rv); - Py_DECREF(call_args); - return value; + if (value == -1 && PyErr_Occurred()) { + PyErr_WriteUnraisable(NULL); + return 0; + } + + return (unsigned int)value; } int IOWR(unsigned int base, unsigned int address, unsigned int value) { - - if (!PyCallable_Check(pWrFunction)) { - printf("Write function isn't callable...\n"); - return -1; + /* `I` is `unsigned int` */ + PyObject *rv = PyObject_CallFunction(pWrFunction, "II", base + address, value); + if (rv == NULL) { + PyErr_WriteUnraisable(NULL); + return 0; } - - PyObject *call_args = PyTuple_New(2); - PyObject *rv; - - PyTuple_SetItem(call_args, 0, PyInt_FromLong(base + address)); - PyTuple_SetItem(call_args, 1, PyInt_FromLong(value)); - - rv = PyObject_CallObject(pWrFunction, call_args); - - if (PyErr_Occurred()) - PyErr_Print(); - Py_DECREF(rv); - Py_DECREF(call_args); - return 0; } From 67df0bf05514d9244f8267fa96afa725136ffd39 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 15 Apr 2020 16:03:57 -0500 Subject: [PATCH 2144/2656] Fix bug in test_deprecated for non-Icarus VPI --- tests/test_cases/test_cocotb/test_deprecated.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_cases/test_cocotb/test_deprecated.py b/tests/test_cases/test_cocotb/test_deprecated.py index f70bc7d4..5b433ca6 100644 --- a/tests/test_cases/test_cocotb/test_deprecated.py +++ b/tests/test_cases/test_cocotb/test_deprecated.py @@ -48,6 +48,7 @@ def get_value(): async def test_unicode_handle_assignment_deprecated(dut): with assert_deprecated() as warns: dut.string_input_port <= "Bad idea" + await cocotb.triggers.ReadWrite() assert "bytes" in str(warns[0].message) From 0daa15a4ceda34f3da7f62779cd14ba06482b29e Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 15 Apr 2020 18:01:47 -0500 Subject: [PATCH 2145/2656] Fix port name in sample_module.sv and related tests --- tests/designs/sample_module/sample_module.sv | 2 +- tests/test_cases/test_cocotb/test_deprecated.py | 2 +- tests/test_cases/test_cocotb/test_handle.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/designs/sample_module/sample_module.sv b/tests/designs/sample_module/sample_module.sv index dcff4364..84ae22d5 100644 --- a/tests/designs/sample_module/sample_module.sv +++ b/tests/designs/sample_module/sample_module.sv @@ -48,7 +48,7 @@ module sample_module ( output real stream_out_real, output integer stream_out_int, input test_if inout_if, - input string string_input_port, + input string stream_in_string, `endif input [7:0] stream_in_data, input [63:0] stream_in_data_wide, diff --git a/tests/test_cases/test_cocotb/test_deprecated.py b/tests/test_cases/test_cocotb/test_deprecated.py index 5b433ca6..f0c3dfbd 100644 --- a/tests/test_cases/test_cocotb/test_deprecated.py +++ b/tests/test_cases/test_cocotb/test_deprecated.py @@ -47,7 +47,7 @@ def get_value(): @cocotb.test(skip=cocotb.SIM_NAME.lower().startswith("icarus")) async def test_unicode_handle_assignment_deprecated(dut): with assert_deprecated() as warns: - dut.string_input_port <= "Bad idea" + dut.stream_in_string <= "Bad idea" await cocotb.triggers.ReadWrite() assert "bytes" in str(warns[0].message) diff --git a/tests/test_cases/test_cocotb/test_handle.py b/tests/test_cases/test_cocotb/test_handle.py index ee0dc886..40299dea 100644 --- a/tests/test_cases/test_cocotb/test_handle.py +++ b/tests/test_cases/test_cocotb/test_handle.py @@ -41,8 +41,8 @@ def test_bad_attr(dut): # strings are not supported on Icarus @cocotb.test(skip=cocotb.SIM_NAME.lower().startswith("icarus")) async def test_string_handle_takes_bytes(dut): - dut.string_input_port.value = b"bytes" + dut.stream_in_string.value = b"bytes" await cocotb.triggers.Timer(10, 'ns') - val = dut.string_input_port.value + val = dut.stream_in_string.value assert isinstance(val, bytes) assert val == b"bytes" From 169e96585e497858bad4150f3387a7058721029c Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 15 Apr 2020 20:05:13 -0500 Subject: [PATCH 2146/2656] Fix access_string_vhdl test after change to StringObject to use bytes --- tests/test_cases/test_discovery/test_discovery.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 2a0d45c4..fe73ff1b 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -209,16 +209,16 @@ def access_string_vhdl(dut): tlog.info("%r is %s" % (constant_string, str(constant_string))) if not isinstance(constant_string, ConstantObject): raise TestFailure("EXAMPLE_STRING was not constant") - if constant_string != "TESTING": + if constant_string != b"TESTING": raise TestFailure("EXAMPLE_STRING was not == \'TESTING\'") tlog.info("Test writing under size") - test_string = "cocotb" + test_string = b"cocotb" dut.stream_in_string.setimmediatevalue(test_string) variable_string = dut.stream_out_string - if variable_string != '': + if variable_string != b'': raise TestFailure("%r not \'\'" % variable_string) yield Timer(10) @@ -226,7 +226,7 @@ def access_string_vhdl(dut): if variable_string != test_string: raise TestFailure("%r %s != '%s'" % (variable_string, str(variable_string), test_string)) - test_string = "longer_than_the_array" + test_string = b"longer_than_the_array" tlog.info("Test writing over size with '%s'" % test_string) dut.stream_in_string.setimmediatevalue(test_string) @@ -248,9 +248,8 @@ def access_string_vhdl(dut): result_slice = variable_string[idx] # String is defined as string(1 to 8) so idx=3 will access the 3rd character - if chr(result_slice) != test_string[idx-1]: - raise TestFailure("Single character did not match '%c' != '%c'" % - (result_slice, test_string[idx])) + if result_slice != test_string[idx - 1]: + raise TestFailure("Single character did not match {} != {}".format(result_slice, test_string[idx - 1])) tlog.info("Test write access to a string character") From fb9c32a7f4eb4c288c2a30525c689fbb9a1a74a9 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 15 Apr 2020 20:15:45 -0500 Subject: [PATCH 2147/2656] Fix test_read_write after changing StringObject to use bytes --- tests/test_cases/test_array/test_array.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py index f0d66da9..13cb493f 100644 --- a/tests/test_cases/test_array/test_array.py +++ b/tests/test_cases/test_array/test_array.py @@ -29,7 +29,7 @@ def _check_logic(tlog, hdl, expected): tlog.info(" Found {0!r} ({1}) with value=0x{2:X}".format(hdl, hdl._type, int(hdl))) def _check_str(tlog, hdl, expected): - if str(hdl) != expected: + if hdl.value != expected: raise TestFailure("{2!r}: Expected >{0}< but got >{1}<".format(expected, str(hdl), hdl)) else: tlog.info(" Found {0!r} ({1}) with value={2}".format(hdl, hdl._type, str(hdl))) @@ -65,7 +65,7 @@ def test_read_write(dut): _check_int (tlog, dut.param_int , 6) _check_real(tlog, dut.param_real, 3.14) _check_int (tlog, dut.param_char, ord('p')) - _check_str (tlog, dut.param_str , "ARRAYMOD") + _check_str (tlog, dut.param_str , b"ARRAYMOD") if not cocotb.SIM_NAME.lower().startswith(("riviera")): _check_logic(tlog, dut.param_rec.a , 0) @@ -90,7 +90,7 @@ def test_read_write(dut): _check_int (tlog, dut.const_int , 12) _check_real(tlog, dut.const_real, 6.28) _check_int (tlog, dut.const_char, ord('c')) - _check_str (tlog, dut.const_str , "MODARRAY") + _check_str (tlog, dut.const_str , b"MODARRAY") if not cocotb.SIM_NAME.lower().startswith(("riviera")): _check_logic(tlog, dut.const_rec.a , 1) @@ -156,7 +156,7 @@ def test_read_write(dut): _check_int (tlog, dut.port_int_out , 5000) _check_real(tlog, dut.port_real_out, 22.54) _check_int (tlog, dut.port_char_out, ord('Z')) - _check_str (tlog, dut.port_str_out , "Testing") + _check_str (tlog, dut.port_str_out , b"Testing") _check_logic(tlog, dut.port_rec_out.a , 1) _check_logic(tlog, dut.port_rec_out.b[0] , 0x01) @@ -195,7 +195,7 @@ def test_read_write(dut): _check_logic(tlog, dut.sig_t6[0][2][7], 0) if cocotb.LANGUAGE in ["vhdl"]: - _check_str(tlog, dut.port_str_out, "TEsting") # the uppercase "E" from a few lines before + _check_str(tlog, dut.port_str_out, b"TEsting") # the uppercase "E" from a few lines before _check_logic(tlog, dut.port_rec_out.b[1] , 0xA3) _check_logic(tlog, dut.port_cmplx_out[1].b[1], 0xEE) From cbf07469921fc8ffe5792c92248a0fbd294d0eeb Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 16 Apr 2020 10:48:18 +0100 Subject: [PATCH 2148/2656] Remove handling of NULL without PyErr_Occurred This code path is imposible, becaues PyObject_Call sets SystemError if no error is set. --- cocotb/share/lib/simulator/simulatormodule.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index d19d24fa..c95ecbff 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -222,15 +222,9 @@ int handle_gpi_callback(void *user_data) // If the return value is NULL a Python exception has occurred // The best thing to do here is shutdown as any subsequent // calls will go back to Python which is now in an unknown state - if (pValue == NULL) - { - fprintf(stderr, "ERROR: called callback function returned NULL\n"); - if (PyErr_Occurred()) { - fprintf(stderr, "Failed to execute callback due to Python exception\n"); - PyErr_Print(); - } else { - fprintf(stderr, "Failed to execute callback\n"); - } + if (pValue == NULL) { + fprintf(stderr, "ERROR: called callback function threw exception\n"); + PyErr_Print(); gpi_sim_end(); sim_ending = 1; From 56c331ff6d1627919c9847f01b4a1e24a05525b4 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 16 Apr 2020 10:49:23 +0100 Subject: [PATCH 2149/2656] Make it an error to call `get_sim_time` or `get_precision` with arguments Previously arguments were allowed but ignored. --- cocotb/share/lib/simulator/simulatormodule.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cocotb/share/lib/simulator/simulatormodule.h b/cocotb/share/lib/simulator/simulatormodule.h index 9f25242c..9fa3a351 100644 --- a/cocotb/share/lib/simulator/simulatormodule.h +++ b/cocotb/share/lib/simulator/simulatormodule.h @@ -79,9 +79,8 @@ static PyMethodDef SimulatorMethods[] = { {"stop_simulator", stop_simulator, METH_VARARGS, "Instruct the attached simulator to stop"}, {"log_level", log_level, METH_VARARGS, "Set the log level for GPI"}, - // FIXME METH_NOARGS => initialization from incompatible pointer type - {"get_sim_time", get_sim_time, METH_VARARGS, "Get the current simulation time as an int tuple"}, - {"get_precision", get_precision, METH_VARARGS, "Get the precision of the simulator"}, + {"get_sim_time", get_sim_time, METH_NOARGS, "Get the current simulation time as an int tuple"}, + {"get_precision", get_precision, METH_NOARGS, "Get the precision of the simulator"}, {"error_out", error_out, METH_NOARGS, NULL}, {NULL, NULL, 0, NULL} /* Sentinel */ }; From c5c307c104fc0be526bc93e1947175c9bb4e2bce Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 16 Apr 2020 10:52:09 +0100 Subject: [PATCH 2150/2656] Ensure errors are propagated from `add_module_constants` These errors are very unlikely, but we should propagate them anyway just in case. --- .../share/lib/simulator/simulatormodule.cpp | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index c95ecbff..c253f612 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -875,29 +875,32 @@ static PyObject *log_level(PyObject *self, PyObject *args) Py_RETURN_NONE; } -static void add_module_constants(PyObject* simulator) +static int add_module_constants(PyObject* simulator) { // Make the GPI constants accessible from the C world - int rc = 0; - rc |= PyModule_AddIntConstant(simulator, "UNKNOWN", GPI_UNKNOWN); - rc |= PyModule_AddIntConstant(simulator, "MEMORY", GPI_MEMORY); - rc |= PyModule_AddIntConstant(simulator, "MODULE", GPI_MODULE); - rc |= PyModule_AddIntConstant(simulator, "NET", GPI_NET); - rc |= PyModule_AddIntConstant(simulator, "PARAMETER", GPI_PARAMETER); - rc |= PyModule_AddIntConstant(simulator, "REG", GPI_REGISTER); - rc |= PyModule_AddIntConstant(simulator, "NETARRAY", GPI_ARRAY); - rc |= PyModule_AddIntConstant(simulator, "ENUM", GPI_ENUM); - rc |= PyModule_AddIntConstant(simulator, "STRUCTURE", GPI_STRUCTURE); - rc |= PyModule_AddIntConstant(simulator, "REAL", GPI_REAL); - rc |= PyModule_AddIntConstant(simulator, "INTEGER", GPI_INTEGER); - rc |= PyModule_AddIntConstant(simulator, "STRING", GPI_STRING); - rc |= PyModule_AddIntConstant(simulator, "GENARRAY", GPI_GENARRAY); - rc |= PyModule_AddIntConstant(simulator, "OBJECTS", GPI_OBJECTS); - rc |= PyModule_AddIntConstant(simulator, "DRIVERS", GPI_DRIVERS); - rc |= PyModule_AddIntConstant(simulator, "LOADS", GPI_LOADS); - - if (rc != 0) - fprintf(stderr, "Failed to add module constants!\n"); + if ( + PyModule_AddIntConstant(simulator, "UNKNOWN", GPI_UNKNOWN ) < 0 || + PyModule_AddIntConstant(simulator, "MEMORY", GPI_MEMORY ) < 0 || + PyModule_AddIntConstant(simulator, "MODULE", GPI_MODULE ) < 0 || + PyModule_AddIntConstant(simulator, "NET", GPI_NET ) < 0 || + PyModule_AddIntConstant(simulator, "PARAMETER", GPI_PARAMETER ) < 0 || + PyModule_AddIntConstant(simulator, "REG", GPI_REGISTER ) < 0 || + PyModule_AddIntConstant(simulator, "NETARRAY", GPI_ARRAY ) < 0 || + PyModule_AddIntConstant(simulator, "ENUM", GPI_ENUM ) < 0 || + PyModule_AddIntConstant(simulator, "STRUCTURE", GPI_STRUCTURE ) < 0 || + PyModule_AddIntConstant(simulator, "REAL", GPI_REAL ) < 0 || + PyModule_AddIntConstant(simulator, "INTEGER", GPI_INTEGER ) < 0 || + PyModule_AddIntConstant(simulator, "STRING", GPI_STRING ) < 0 || + PyModule_AddIntConstant(simulator, "GENARRAY", GPI_GENARRAY ) < 0 || + PyModule_AddIntConstant(simulator, "OBJECTS", GPI_OBJECTS ) < 0 || + PyModule_AddIntConstant(simulator, "DRIVERS", GPI_DRIVERS ) < 0 || + PyModule_AddIntConstant(simulator, "LOADS", GPI_LOADS ) < 0 || + false + ) { + return -1; + } + + return 0; } static PyObject *error_out(PyObject *m, PyObject *args) @@ -957,7 +960,10 @@ PyMODINIT_FUNC PyInit_simulator(void) return NULL; } - add_module_constants(simulator); + if (add_module_constants(simulator) < 0) { + Py_DECREF(simulator); + return NULL; + } return simulator; } From fdce0b6246a38d283e801c60d8880d99112ebb0e Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 16 Apr 2020 10:53:26 +0100 Subject: [PATCH 2151/2656] Remove the unused `simulator.error_out`, and all the machinery needed to enable it. --- .../share/lib/simulator/simulatormodule.cpp | 45 +++---------------- cocotb/share/lib/simulator/simulatormodule.h | 2 - 2 files changed, 5 insertions(+), 42 deletions(-) diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index c253f612..c91e81a7 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -44,11 +44,6 @@ static int sim_ending = 0; #include -struct module_state { - PyObject *error; -}; - - /* define the extension types as templates */ namespace { template @@ -138,9 +133,6 @@ namespace { } - -#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) - typedef int (*gpi_function_t)(const void *); PyGILState_STATE TAKE_GIL(void) @@ -903,40 +895,20 @@ static int add_module_constants(PyObject* simulator) return 0; } -static PyObject *error_out(PyObject *m, PyObject *args) -{ - COCOTB_UNUSED(args); - struct module_state *st = GETSTATE(m); - PyErr_SetString(st->error, "something bad happened"); - return NULL; -} - -static int simulator_traverse(PyObject *m, visitproc visit, void *arg) { - Py_VISIT(GETSTATE(m)->error); - return 0; -} - -static int simulator_clear(PyObject *m) { - Py_CLEAR(GETSTATE(m)->error); - return 0; -} - static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, MODULE_NAME, NULL, - sizeof(struct module_state), + -1, SimulatorMethods, NULL, - simulator_traverse, - simulator_clear, + NULL, + NULL, NULL }; PyMODINIT_FUNC PyInit_simulator(void) { - PyObject* simulator; - /* initialize the extension types */ if (PyType_Ready(&gpi_hdl_Object::py_type) < 0) { return NULL; @@ -948,15 +920,8 @@ PyMODINIT_FUNC PyInit_simulator(void) return NULL; } - simulator = PyModule_Create(&moduledef); - - if (simulator == NULL) - return NULL; - struct module_state *st = GETSTATE(simulator); - - st->error = PyErr_NewException(MODULE_NAME ".Error", NULL, NULL); - if (st->error == NULL) { - Py_DECREF(simulator); + PyObject* simulator = PyModule_Create(&moduledef); + if (simulator == NULL) { return NULL; } diff --git a/cocotb/share/lib/simulator/simulatormodule.h b/cocotb/share/lib/simulator/simulatormodule.h index 9fa3a351..1aaaa82c 100644 --- a/cocotb/share/lib/simulator/simulatormodule.h +++ b/cocotb/share/lib/simulator/simulatormodule.h @@ -51,7 +51,6 @@ typedef struct t_callback_data { gpi_sim_hdl cb_hdl; } s_callback_data, *p_callback_data; -static PyObject *error_out(PyObject *m, PyObject *args); static PyObject *log_msg(PyObject *self, PyObject *args); static PyObject *register_timed_callback(PyObject *self, PyObject *args); @@ -81,7 +80,6 @@ static PyMethodDef SimulatorMethods[] = { {"get_sim_time", get_sim_time, METH_NOARGS, "Get the current simulation time as an int tuple"}, {"get_precision", get_precision, METH_NOARGS, "Get the precision of the simulator"}, - {"error_out", error_out, METH_NOARGS, NULL}, {NULL, NULL, 0, NULL} /* Sentinel */ }; From c24f80647bf64a5686e10462c3302b22ceea251a Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 16 Apr 2020 10:56:21 +0100 Subject: [PATCH 2152/2656] Reject additional arguments parssed to `simulator.log_level` Previously these were silently ignored --- cocotb/share/lib/simulator/simulatormodule.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index c91e81a7..61d995ee 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -856,13 +856,13 @@ static PyObject *deregister(gpi_hdl_Object *self, PyObject *args) static PyObject *log_level(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); - enum gpi_log_levels new_level; - PyObject *py_level; + long l_level; - py_level = PyTuple_GetItem(args, 0); - new_level = (enum gpi_log_levels)PyLong_AsLong(py_level); + if (!PyArg_ParseTuple(args, "l", &l_level)) { + return NULL; + } - set_log_level(new_level); + set_log_level((enum gpi_log_levels)l_level); Py_RETURN_NONE; } From 0a0d84940e4fe620b82d61c96709dd70f9b37eea Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 16 Apr 2020 11:54:43 +0100 Subject: [PATCH 2153/2656] Replace Py_BuildValue with a direct function call for simplicity --- .../share/lib/simulator/simulatormodule.cpp | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index 61d995ee..ce1a62b6 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -613,7 +613,7 @@ static PyObject *get_signal_val_binstr(gpi_hdl_Object *self, PyObje { COCOTB_UNUSED(args); const char *result = gpi_get_signal_value_binstr(self->hdl); - return Py_BuildValue("s", result); + return PyUnicode_FromString(result); } static PyObject *get_signal_val_str(gpi_hdl_Object *self, PyObject *args) @@ -627,7 +627,7 @@ static PyObject *get_signal_val_real(gpi_hdl_Object *self, PyObject { COCOTB_UNUSED(args); double result = gpi_get_signal_value_real(self->hdl); - return Py_BuildValue("d", result); + return PyFloat_FromDouble(result); } @@ -635,7 +635,7 @@ static PyObject *get_signal_val_long(gpi_hdl_Object *self, PyObject { COCOTB_UNUSED(args); long result = gpi_get_signal_value_long(self->hdl); - return Py_BuildValue("l", result); + return PyLong_FromLong(result); } static PyObject *set_signal_val_binstr(gpi_hdl_Object *self, PyObject *args) @@ -694,14 +694,14 @@ static PyObject *get_definition_name(gpi_hdl_Object *self, PyObject { COCOTB_UNUSED(args); const char* result = gpi_get_definition_name(self->hdl); - return Py_BuildValue("s", result); + return PyUnicode_FromString(result); } static PyObject *get_definition_file(gpi_hdl_Object *self, PyObject *args) { COCOTB_UNUSED(args); const char* result = gpi_get_definition_file(self->hdl); - return Py_BuildValue("s", result); + return PyUnicode_FromString(result); } static PyObject *get_handle_by_name(gpi_hdl_Object *self, PyObject *args) @@ -753,28 +753,28 @@ static PyObject *get_name_string(gpi_hdl_Object *self, PyObject *ar COCOTB_UNUSED(args); COCOTB_UNUSED(self); const char *result = gpi_get_signal_name_str(self->hdl); - return Py_BuildValue("s", result); + return PyUnicode_FromString(result); } static PyObject *get_type(gpi_hdl_Object *self, PyObject *args) { COCOTB_UNUSED(args); gpi_objtype_t result = gpi_get_object_type(self->hdl); - return Py_BuildValue("i", (int)result); + return PyLong_FromLong(result); } static PyObject *get_const(gpi_hdl_Object *self, PyObject *args) { COCOTB_UNUSED(args); int result = gpi_is_constant(self->hdl); - return Py_BuildValue("i", result); + return PyBool_FromLong(result); } static PyObject *get_type_string(gpi_hdl_Object *self, PyObject *args) { COCOTB_UNUSED(args); const char *result = gpi_get_signal_type_str(self->hdl); - return Py_BuildValue("s", result); + return PyUnicode_FromString(result); } @@ -808,14 +808,14 @@ static PyObject *get_precision(PyObject *self, PyObject *args) gpi_get_sim_precision(&precision); - return Py_BuildValue("i", precision); + return PyLong_FromLong(precision); } static PyObject *get_num_elems(gpi_hdl_Object *self, PyObject *args) { COCOTB_UNUSED(args); int elems = gpi_get_num_elems(self->hdl); - return Py_BuildValue("i", elems); + return PyLong_FromLong(elems); } static PyObject *get_range(gpi_hdl_Object *self, PyObject *args) @@ -826,10 +826,12 @@ static PyObject *get_range(gpi_hdl_Object *self, PyObject *args) int rng_left = gpi_get_range_left(self->hdl); int rng_right = gpi_get_range_right(self->hdl); - if (indexable) + if (indexable) { return Py_BuildValue("(i,i)", rng_left, rng_right); - else - return Py_BuildValue(""); + } + else { + Py_RETURN_NONE; + } } static PyObject *stop_simulator(PyObject *self, PyObject *args) From 281e6c0f120956921536f3215719c4534c521de1 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 16 Apr 2020 22:35:22 +0100 Subject: [PATCH 2154/2656] Ensure errors happen immediately when using delayed assignment. (#1661) This also implements `NonHierarchyIndexableObject.setimmediate`. --- cocotb/handle.py | 137 +++++++++--------- cocotb/scheduler.py | 17 ++- .../source/newsfragments/1661.bugfix.rst | 1 + tests/test_cases/test_cocotb/common.py | 11 ++ .../test_cases/test_cocotb/test_deprecated.py | 10 +- tests/test_cases/test_cocotb/test_handle.py | 18 +++ 6 files changed, 110 insertions(+), 84 deletions(-) create mode 100644 documentation/source/newsfragments/1661.bugfix.rst diff --git a/cocotb/handle.py b/cocotb/handle.py index a01bc275..3a4aab9d 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -378,13 +378,37 @@ def __iter__(self): @property def value(self): - raise TypeError("Not permissible to get values of object %s of type %s" % (self._name, type(self))) + """The value of this simulation object. - def setimmediatevalue(self, value): - raise TypeError("Not permissible to set values on object %s of type %s" % (self._name, type(self))) + .. note:: + When setting this property, the value is stored by the :class:`~cocotb.scheduler.Scheduler` + and all stored values are written at the same time at the end of the current simulator time step. + + Use :meth:`setimmediatevalue` to set the value immediately. + """ + raise TypeError("Not permissible to get values of object %s of type %s" % (self._name, type(self))) @value.setter def value(self, value): + def _call_on_readwrite(f, *args): + cocotb.scheduler._schedule_write(self, f, *args) + self._set_value(value, _call_on_readwrite) + + def setimmediatevalue(self, value): + """ Assign a value to this simulation object immediately. """ + def _call_now(f, *args): + f(*args) + self._set_value(value, _call_now) + + def _set_value(self, value, call_sim): + """ This should be overriden in subclasses. + + This is used to implement both the setter for :attr:`value`, and the + :meth:`setimmediatevalue` method. + + ``call_sim(f, *args)`` should be used to schedule simulator writes, + rather than performing them directly as ``f(*args)``. + """ raise TypeError("Not permissible to set values on object %s of type %s" % (self._name, type(self))) def __le__(self, value): @@ -463,7 +487,29 @@ def __str__(self): class NonHierarchyIndexableObject(NonHierarchyObject): - """ A non-hierarchy indexable object. """ + """ A non-hierarchy indexable object. + + Getting and setting the current value of an array is done + by iterating through sub-handles in left-to-right order. + + Given an HDL array ``arr``: + + +--------------+---------------------+--------------------------------------------------------------+ + | Verilog | VHDL | ``arr.value`` is equivalent to | + +==============+=====================+==============================================================+ + | ``arr[4:7]`` | ``arr(4 to 7)`` | ``[arr[4].value, arr[5].value, arr[6].value, arr[7].value]`` | + +--------------+---------------------+--------------------------------------------------------------+ + | ``arr[7:4]`` | ``arr(7 downto 4)`` | ``[arr[7].value, arr[6].value, arr[5].value, arr[4].value]`` | + +--------------+---------------------+--------------------------------------------------------------+ + + When setting the signal as in ``arr.value = ...``, the same index equivalence as noted in the table holds. + + .. warning:: + Assigning a value to a sub-handle: + + - **Wrong**: ``dut.some_array.value[0] = 1`` (gets value as a list then updates index 0) + - **Correct**: ``dut.some_array[0].value = 1`` + """ def __init__(self, handle, path): NonHierarchyObject.__init__(self, handle, path) self._range = self._handle.get_range() @@ -509,45 +555,18 @@ def _range_iter(self, left, right): left = left + 1 @NonHierarchyObject.value.getter - def value(self): - """A list of each value within this simulation object. - - Getting and setting the current value of an array is done - by iterating through sub-handles in left-to-right order. - - Given an HDL array ``arr``: - - +--------------+---------------------+--------------------------------------------------------------+ - | Verilog | VHDL | ``arr.value`` is equivalent to | - +==============+=====================+==============================================================+ - | ``arr[4:7]`` | ``arr(4 to 7)`` | ``[arr[4].value, arr[5].value, arr[6].value, arr[7].value]`` | - +--------------+---------------------+--------------------------------------------------------------+ - | ``arr[7:4]`` | ``arr(7 downto 4)`` | ``[arr[7].value, arr[6].value, arr[5].value, arr[4].value]`` | - +--------------+---------------------+--------------------------------------------------------------+ - - When setting this property as in ``arr.value = ...``, the same index equivalence as noted in the table holds. - - .. note:: - When setting this property, the values will be cached as explained in :attr:`ModifiableObject.value`. - - .. warning:: - Assigning a value to a sub-handle: - - - **Wrong**: ``dut.some_array.value[0] = 1`` (gets value as a list then updates index 0) - - **Correct**: ``dut.some_array[0].value = 1`` - """ + def value(self) -> list: # Don't use self.__iter__, because it has an unwanted `except IndexError` return [ self[i].value for i in self._range_iter(self._range[0], self._range[1]) ] - @value.setter - def value(self, value): + def _set_value(self, value, call_sim): """Assign value from a list of same length to an array in left-to-right order. Index 0 of the list maps to the left-most index in the array. - See the docstring for :attr:`value` above. + See the docstring for this class. """ if type(value) is not list: raise TypeError("Assigning non-list value to object %s of type %s" % (self._name, type(self))) @@ -555,7 +574,7 @@ def value(self, value): raise ValueError("Assigning list of length %d to object %s of length %d" % ( len(value), self._name, len(self))) for val_idx, self_idx in enumerate(self._range_iter(self._range[0], self._range[1])): - self[self_idx].value = value[val_idx] + self[self_idx]._set_value(value[val_idx], call_sim) class NonConstantObject(NonHierarchyIndexableObject): @@ -603,7 +622,7 @@ def _as_gpi_args_for(self, hdl): class ModifiableObject(NonConstantObject): """Base class for simulator objects whose values can be modified.""" - def setimmediatevalue(self, value): + def _set_value(self, value, call_sim): """Set the value of the underlying simulation object to *value*. This operation will fail unless the handle refers to a modifiable @@ -623,7 +642,7 @@ def setimmediatevalue(self, value): value, set_action = self._check_for_set_action(value) if isinstance(value, int) and value < 0x7fffffff and len(self) <= 32: - self._handle.set_signal_val_long(set_action, value) + call_sim(self._handle.set_signal_val_long, set_action, value) return if isinstance(value, ctypes.Structure): value = BinaryValue(value=cocotb.utils.pack(value), n_bits=len(self)) @@ -647,7 +666,7 @@ def setimmediatevalue(self, value): "Unsupported type for value assignment: {} ({!r})" .format(type(value), value)) - self._handle.set_signal_val_binstr(set_action, value.binstr) + call_sim(self._handle.set_signal_val_binstr, set_action, value.binstr) def _check_for_set_action(self, value): if not isinstance(value, _SetAction): @@ -655,27 +674,11 @@ def _check_for_set_action(self, value): return value._as_gpi_args_for(self) @NonConstantObject.value.getter - def value(self): - """The value of this simulation object. - - .. note:: - When setting this property, the value is stored by the :class:`~cocotb.scheduler.Scheduler` - and all stored values are written at the same time at the end of the current simulator time step. - - Use :meth:`setimmediatevalue` to set the value immediately. - """ + def value(self) -> BinaryValue: binstr = self._handle.get_signal_val_binstr() result = BinaryValue(binstr, len(binstr)) return result - @value.setter - def value(self, value): - """Assign value to this simulation object. - - See the docstring for :attr:`value` above. - """ - cocotb.scheduler.save_write(self, value) - def __int__(self): return int(self.value) @@ -686,7 +689,7 @@ def __str__(self): class RealObject(ModifiableObject): """Specific object handle for Real signals and variables.""" - def setimmediatevalue(self, value): + def _set_value(self, value, call_sim): """Set the value of the underlying simulation object to value. This operation will fail unless the handle refers to a modifiable @@ -708,10 +711,10 @@ def setimmediatevalue(self, value): "Unsupported type for real value assignment: {} ({!r})" .format(type(value), value)) - self._handle.set_signal_val_real(set_action, value) + call_sim(self._handle.set_signal_val_real, set_action, value) @ModifiableObject.value.getter - def value(self): + def value(self) -> float: return self._handle.get_signal_val_real() def __float__(self): @@ -721,7 +724,7 @@ def __float__(self): class EnumObject(ModifiableObject): """Specific object handle for enumeration signals and variables.""" - def setimmediatevalue(self, value): + def _set_value(self, value, call_sim): """Set the value of the underlying simulation object to *value*. This operation will fail unless the handle refers to a modifiable @@ -743,17 +746,17 @@ def setimmediatevalue(self, value): "Unsupported type for enum value assignment: {} ({!r})" .format(type(value), value)) - self._handle.set_signal_val_long(set_action, value) + call_sim(self._handle.set_signal_val_long, set_action, value) @ModifiableObject.value.getter - def value(self): + def value(self) -> int: return self._handle.get_signal_val_long() class IntegerObject(ModifiableObject): """Specific object handle for Integer and Enum signals and variables.""" - def setimmediatevalue(self, value): + def _set_value(self, value, call_sim): """Set the value of the underlying simulation object to *value*. This operation will fail unless the handle refers to a modifiable @@ -775,17 +778,17 @@ def setimmediatevalue(self, value): "Unsupported type for integer value assignment: {} ({!r})" .format(type(value), value)) - self._handle.set_signal_val_long(set_action, value) + call_sim(self._handle.set_signal_val_long, set_action, value) @ModifiableObject.value.getter - def value(self): + def value(self) -> int: return self._handle.get_signal_val_long() class StringObject(ModifiableObject): """Specific object handle for String variables.""" - def setimmediatevalue(self, value): + def _set_value(self, value, call_sim): """Set the value of the underlying simulation object to *value*. This operation will fail unless the handle refers to a modifiable @@ -818,10 +821,10 @@ def setimmediatevalue(self, value): "Unsupported type for string value assignment: {} ({!r})" .format(type(value), value)) - self._handle.set_signal_val_str(set_action, value) + call_sim(self._handle.set_signal_val_str, set_action, value) @ModifiableObject.value.getter - def value(self): + def value(self) -> bytes: return self._handle.get_signal_val_str() _handle2obj = {} diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index d334157c..7de3239c 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -229,8 +229,8 @@ def __init__(self): # Our main state self._mode = Scheduler._MODE_NORMAL - # A dictionary of pending writes - self._writes = _py_compat.insertion_ordered_dict() + # A list of pending (write_func, args) + self._write_calls = [] self._pending_coros = [] self._pending_triggers = [] @@ -255,9 +255,9 @@ async def _do_writes(self): await self._read_write - while self._writes: - handle, value = self._writes.popitem() - handle.setimmediatevalue(value) + while self._write_calls: + func, args = self._write_calls.pop() + func(*args) self._writes_pending.clear() def _check_termination(self): @@ -282,7 +282,7 @@ def _check_termination(self): self._trigger2coros = _py_compat.insertion_ordered_dict() self._coro2trigger = _py_compat.insertion_ordered_dict() self._terminate = False - self._writes = _py_compat.insertion_ordered_dict() + self._write_calls = [] self._writes_pending.clear() self._mode = Scheduler._MODE_TERM @@ -503,7 +503,8 @@ def unschedule(self, coro): e = remove_traceback_frames(e, ['unschedule', 'get']) self._test.abort(e) - def save_write(self, handle, value): + def _schedule_write(self, handle, write_func, *args): + """ Queue `write_func` to be called on the next ReadWrite trigger. """ if self._mode == Scheduler._MODE_READONLY: raise Exception("Write to object {0} was scheduled during a read-only sync phase.".format(handle._name)) @@ -512,7 +513,7 @@ def save_write(self, handle, value): if self._write_coro_inst is None: self._write_coro_inst = self.add(self._do_writes()) - self._writes[handle] = value + self._write_calls.append((write_func, args)) self._writes_pending.set() def _coroutine_yielded(self, coro, trigger): diff --git a/documentation/source/newsfragments/1661.bugfix.rst b/documentation/source/newsfragments/1661.bugfix.rst new file mode 100644 index 00000000..a21aa042 --- /dev/null +++ b/documentation/source/newsfragments/1661.bugfix.rst @@ -0,0 +1 @@ +``signal <= value_of_wrong_type`` no longer breaks the scheduler, and throws an error immediately. diff --git a/tests/test_cases/test_cocotb/common.py b/tests/test_cases/test_cocotb/common.py index 26a50a11..2c0494aa 100644 --- a/tests/test_cases/test_cocotb/common.py +++ b/tests/test_cases/test_cocotb/common.py @@ -6,6 +6,7 @@ """ import re import traceback +from contextlib import contextmanager import cocotb from cocotb.result import TestFailure @@ -41,3 +42,13 @@ def _check_traceback(running_coro, exc_type, pattern): "{}" ).format(tb_text, pattern) ) + + +@contextmanager +def assert_raises(exc_type): + try: + yield + except exc_type: + pass + else: + raise AssertionError("{} was not raised".format(exc_type.__name__)) diff --git a/tests/test_cases/test_cocotb/test_deprecated.py b/tests/test_cases/test_cocotb/test_deprecated.py index f0c3dfbd..19df58aa 100644 --- a/tests/test_cases/test_cocotb/test_deprecated.py +++ b/tests/test_cases/test_cocotb/test_deprecated.py @@ -4,6 +4,7 @@ import cocotb import warnings from contextlib import contextmanager +from common import assert_raises @contextmanager @@ -19,15 +20,6 @@ def assert_deprecated(): assert issubclass(warns[0].category, DeprecationWarning), "Expected DeprecationWarning" -@contextmanager -def assert_raises(exc_type): - try: - yield - except exc_type: - pass - else: - raise AssertionError("{} was not raised".format(exc_type.__name__)) - @cocotb.test() async def test_returnvalue_deprecated(dut): diff --git a/tests/test_cases/test_cocotb/test_handle.py b/tests/test_cases/test_cocotb/test_handle.py index 40299dea..15516b67 100644 --- a/tests/test_cases/test_cocotb/test_handle.py +++ b/tests/test_cases/test_cocotb/test_handle.py @@ -7,6 +7,8 @@ import cocotb from cocotb.result import TestFailure +from common import assert_raises + @cocotb.test() def test_lessthan_raises_error(dut): @@ -46,3 +48,19 @@ async def test_string_handle_takes_bytes(dut): val = dut.stream_in_string.value assert isinstance(val, bytes) assert val == b"bytes" + + +async def test_delayed_assignment_still_errors(dut): + """ Writing a bad value should fail even if the write is scheduled to happen later """ + + # note: all these fail because BinaryValue.assign rejects them + + with assert_raises(ValueError): + dut.stream_in_int.setimmediatevalue("1010 not a real binary string") + with assert_raises(TypeError): + dut.stream_in_int.setimmediatevalue([]) + + with assert_raises(ValueError): + dut.stream_in_int <= "1010 not a real binary string" + with assert_raises(TypeError): + dut.stream_in_int <= [] From 2134164d0a12340cf58b89a4c82453a536fcf986 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Fri, 17 Apr 2020 09:54:29 +0100 Subject: [PATCH 2155/2656] Include function name in argument parsing error messages (#1664) `:name` in `PyArg_ParseTuple` is used to make the error message clearer. --- .../share/lib/simulator/simulatormodule.cpp | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index ce1a62b6..5243476a 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -261,7 +261,7 @@ static PyObject *log_msg(PyObject *self, PyObject *args) const char *funcname; int lineno; - if (!PyArg_ParseTuple(args, "sssis", &name, &path, &funcname, &lineno, &msg)) + if (!PyArg_ParseTuple(args, "sssis:log_msg", &name, &path, &funcname, &lineno, &msg)) return NULL; gpi_log(name, GPIInfo, path, funcname, lineno, msg); @@ -583,7 +583,7 @@ static PyObject *iterate(gpi_hdl_Object *self, PyObject *args) { int type; - if (!PyArg_ParseTuple(args, "i", &type)) { + if (!PyArg_ParseTuple(args, "i:iterate", &type)) { return NULL; } @@ -643,7 +643,7 @@ static PyObject *set_signal_val_binstr(gpi_hdl_Object *self, PyObje const char *binstr; gpi_set_action_t action; - if (!PyArg_ParseTuple(args, "is", &action, &binstr)) { + if (!PyArg_ParseTuple(args, "is:set_signal_val_binstr", &action, &binstr)) { return NULL; } @@ -656,7 +656,7 @@ static PyObject *set_signal_val_str(gpi_hdl_Object *self, PyObject gpi_set_action_t action; const char *str; - if (!PyArg_ParseTuple(args, "iy", &action, &str)) { + if (!PyArg_ParseTuple(args, "iy:set_signal_val_str", &action, &str)) { return NULL; } @@ -669,7 +669,7 @@ static PyObject *set_signal_val_real(gpi_hdl_Object *self, PyObject double value; gpi_set_action_t action; - if (!PyArg_ParseTuple(args, "id", &action, &value)) { + if (!PyArg_ParseTuple(args, "id:set_signal_val_real", &action, &value)) { return NULL; } @@ -682,7 +682,7 @@ static PyObject *set_signal_val_long(gpi_hdl_Object *self, PyObject long value; gpi_set_action_t action; - if (!PyArg_ParseTuple(args, "il", &action, &value)) { + if (!PyArg_ParseTuple(args, "il:set_signal_val_long", &action, &value)) { return NULL; } @@ -708,7 +708,7 @@ static PyObject *get_handle_by_name(gpi_hdl_Object *self, PyObject { const char *name; - if (!PyArg_ParseTuple(args, "s", &name)) { + if (!PyArg_ParseTuple(args, "s:get_handle_by_name", &name)) { return NULL; } @@ -721,7 +721,7 @@ static PyObject *get_handle_by_index(gpi_hdl_Object *self, PyObject { int32_t index; - if (!PyArg_ParseTuple(args, "i", &index)) { + if (!PyArg_ParseTuple(args, "i:get_handle_by_index", &index)) { return NULL; } @@ -735,7 +735,7 @@ static PyObject *get_root_handle(PyObject *self, PyObject *args) COCOTB_UNUSED(self); const char *name; - if (!PyArg_ParseTuple(args, "z", &name)) { + if (!PyArg_ParseTuple(args, "z:get_root_handle", &name)) { return NULL; } @@ -860,7 +860,7 @@ static PyObject *log_level(PyObject *self, PyObject *args) COCOTB_UNUSED(self); long l_level; - if (!PyArg_ParseTuple(args, "l", &l_level)) { + if (!PyArg_ParseTuple(args, "l:log_level", &l_level)) { return NULL; } From e3fef1d46ff223747c3a3b237e8de349e5d33fab Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Fri, 17 Apr 2020 09:33:20 +0100 Subject: [PATCH 2156/2656] Deduplicate code to allocate callback data --- .../share/lib/simulator/simulatormodule.cpp | 81 +++++++------------ 1 file changed, 28 insertions(+), 53 deletions(-) diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index 5243476a..b43eea05 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -270,6 +270,24 @@ static PyObject *log_msg(PyObject *self, PyObject *args) } +static p_callback_data make_callback_data(PyObject *func, PyObject *args, PyObject *kwargs) +{ + p_callback_data data = (p_callback_data)malloc(sizeof(s_callback_data)); + if (data == NULL) { + PyErr_NoMemory(); + return NULL; + } + + data->_saved_thread_state = PyThreadState_Get(); + data->id_value = COCOTB_ACTIVE_ID; + data->function = func; + data->args = args; + data->kwargs = kwargs; + + return data; +} + + // Register a callback for read-only state of sim // First argument is the function to call // Remaining arguments are keyword arguments to be passed to the callback @@ -282,8 +300,6 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) PyObject *function; gpi_cb_hdl hdl; - p_callback_data callback_data_p; - Py_ssize_t numargs = PyTuple_Size(args); if (numargs < 1) { @@ -305,17 +321,11 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) return NULL; } - callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); + p_callback_data callback_data_p = make_callback_data(function, fArgs, NULL); if (callback_data_p == NULL) { - return PyErr_NoMemory(); + return NULL; } - // Set up the user data (no more Python API calls after this!) - callback_data_p->_saved_thread_state = PyThreadState_Get(); - callback_data_p->id_value = COCOTB_ACTIVE_ID; - callback_data_p->function = function; - callback_data_p->args = fArgs; - callback_data_p->kwargs = NULL; hdl = gpi_register_readonly_callback((gpi_function_t)handle_gpi_callback, callback_data_p); @@ -335,8 +345,6 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) PyObject *function; gpi_cb_hdl hdl; - p_callback_data callback_data_p; - Py_ssize_t numargs = PyTuple_Size(args); if (numargs < 1) { @@ -358,18 +366,11 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) return NULL; } - callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); + p_callback_data callback_data_p = make_callback_data(function, fArgs, NULL); if (callback_data_p == NULL) { - return PyErr_NoMemory(); + return NULL; } - // Set up the user data (no more Python API calls after this!) - callback_data_p->_saved_thread_state = PyThreadState_Get(); - callback_data_p->id_value = COCOTB_ACTIVE_ID; - callback_data_p->function = function; - callback_data_p->args = fArgs; - callback_data_p->kwargs = NULL; - hdl = gpi_register_readwrite_callback((gpi_function_t)handle_gpi_callback, callback_data_p); PyObject *rv = gpi_hdl_New(hdl); @@ -388,8 +389,6 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) PyObject *function; gpi_cb_hdl hdl; - p_callback_data callback_data_p; - Py_ssize_t numargs = PyTuple_Size(args); if (numargs < 1) { @@ -411,17 +410,11 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) return NULL; } - callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); + p_callback_data callback_data_p = make_callback_data(function, fArgs, NULL); if (callback_data_p == NULL) { - return PyErr_NoMemory(); + return NULL; } - // Set up the user data (no more Python API calls after this!) - callback_data_p->_saved_thread_state = PyThreadState_Get(); - callback_data_p->id_value = COCOTB_ACTIVE_ID; - callback_data_p->function = function; - callback_data_p->args = fArgs; - callback_data_p->kwargs = NULL; hdl = gpi_register_nexttime_callback((gpi_function_t)handle_gpi_callback, callback_data_p); @@ -446,8 +439,6 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) gpi_cb_hdl hdl; uint64_t time_ps; - p_callback_data callback_data_p; - Py_ssize_t numargs = PyTuple_Size(args); if (numargs < 2) { @@ -482,17 +473,11 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) return NULL; } - callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); + p_callback_data callback_data_p = make_callback_data(function, fArgs, NULL); if (callback_data_p == NULL) { - return PyErr_NoMemory(); + return NULL; } - // Set up the user data (no more Python API calls after this!) - callback_data_p->_saved_thread_state = PyThreadState_Get(); - callback_data_p->id_value = COCOTB_ACTIVE_ID; - callback_data_p->function = function; - callback_data_p->args = fArgs; - callback_data_p->kwargs = NULL; hdl = gpi_register_timed_callback((gpi_function_t)handle_gpi_callback, callback_data_p, time_ps); @@ -519,8 +504,6 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) gpi_cb_hdl hdl; int edge; - p_callback_data callback_data_p; - Py_ssize_t numargs = PyTuple_Size(args); if (numargs < 3) { @@ -552,19 +535,11 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) return NULL; } - - callback_data_p = (p_callback_data)malloc(sizeof(s_callback_data)); + p_callback_data callback_data_p = make_callback_data(function, fArgs, NULL); if (callback_data_p == NULL) { - return PyErr_NoMemory(); + return NULL; } - // Set up the user data (no more Python API calls after this!) - // Causes segfault? - callback_data_p->_saved_thread_state = PyThreadState_Get();//PyThreadState_Get(); - callback_data_p->id_value = COCOTB_ACTIVE_ID; - callback_data_p->function = function; - callback_data_p->args = fArgs; - callback_data_p->kwargs = NULL; hdl = gpi_register_value_change_callback((gpi_function_t)handle_gpi_callback, callback_data_p, From f3a93c8cb95d8752f56c870b3c644110459ac84c Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Fri, 17 Apr 2020 09:36:44 +0100 Subject: [PATCH 2157/2656] Merge declarations and initializations --- .../share/lib/simulator/simulatormodule.cpp | 69 ++++++------------- 1 file changed, 22 insertions(+), 47 deletions(-) diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index b43eea05..10c79a18 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -296,10 +296,6 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) COCOTB_UNUSED(self); FENTER - PyObject *fArgs; - PyObject *function; - gpi_cb_hdl hdl; - Py_ssize_t numargs = PyTuple_Size(args); if (numargs < 1) { @@ -308,7 +304,7 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) } // Extract the callback function - function = PyTuple_GetItem(args, 0); + PyObject *function = PyTuple_GetItem(args, 0); if (!PyCallable_Check(function)) { PyErr_SetString(PyExc_TypeError, "Attempt to register ReadOnly without supplying a callback!\n"); return NULL; @@ -316,7 +312,7 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) Py_INCREF(function); // Remaining args for function - fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference + PyObject *fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference if (fArgs == NULL) { return NULL; } @@ -326,8 +322,7 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) return NULL; } - - hdl = gpi_register_readonly_callback((gpi_function_t)handle_gpi_callback, callback_data_p); + gpi_cb_hdl hdl = gpi_register_readonly_callback((gpi_function_t)handle_gpi_callback, callback_data_p); PyObject *rv = gpi_hdl_New(hdl); FEXIT @@ -341,10 +336,6 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) COCOTB_UNUSED(self); FENTER - PyObject *fArgs; - PyObject *function; - gpi_cb_hdl hdl; - Py_ssize_t numargs = PyTuple_Size(args); if (numargs < 1) { @@ -353,7 +344,7 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) } // Extract the callback function - function = PyTuple_GetItem(args, 0); + PyObject *function = PyTuple_GetItem(args, 0); if (!PyCallable_Check(function)) { PyErr_SetString(PyExc_TypeError, "Attempt to register ReadWrite without supplying a callback!\n"); return NULL; @@ -361,7 +352,7 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) Py_INCREF(function); // Remaining args for function - fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference + PyObject *fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference if (fArgs == NULL) { return NULL; } @@ -371,7 +362,8 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) return NULL; } - hdl = gpi_register_readwrite_callback((gpi_function_t)handle_gpi_callback, callback_data_p); + gpi_cb_hdl hdl = gpi_register_readwrite_callback( + (gpi_function_t)handle_gpi_callback, callback_data_p); PyObject *rv = gpi_hdl_New(hdl); FEXIT @@ -385,10 +377,6 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) COCOTB_UNUSED(self); FENTER - PyObject *fArgs; - PyObject *function; - gpi_cb_hdl hdl; - Py_ssize_t numargs = PyTuple_Size(args); if (numargs < 1) { @@ -397,7 +385,7 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) } // Extract the callback function - function = PyTuple_GetItem(args, 0); + PyObject *function = PyTuple_GetItem(args, 0); if (!PyCallable_Check(function)) { PyErr_SetString(PyExc_TypeError, "Attempt to register NextStep without supplying a callback!\n"); return NULL; @@ -405,7 +393,7 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) Py_INCREF(function); // Remaining args for function - fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference + PyObject *fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference if (fArgs == NULL) { return NULL; } @@ -415,8 +403,8 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) return NULL; } - - hdl = gpi_register_nexttime_callback((gpi_function_t)handle_gpi_callback, callback_data_p); + gpi_cb_hdl hdl = gpi_register_nexttime_callback( + (gpi_function_t)handle_gpi_callback, callback_data_p); PyObject *rv = gpi_hdl_New(hdl); FEXIT @@ -434,11 +422,6 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) COCOTB_UNUSED(self); FENTER - PyObject *fArgs; - PyObject *function; - gpi_cb_hdl hdl; - uint64_t time_ps; - Py_ssize_t numargs = PyTuple_Size(args); if (numargs < 2) { @@ -446,6 +429,7 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) return NULL; } + uint64_t time_ps; { // Extract the time PyObject *pTime = PyTuple_GetItem(args, 0); long long pTime_as_longlong = PyLong_AsLongLong(pTime); @@ -460,7 +444,7 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) } // Extract the callback function - function = PyTuple_GetItem(args, 1); + PyObject *function = PyTuple_GetItem(args, 1); if (!PyCallable_Check(function)) { PyErr_SetString(PyExc_TypeError, "Attempt to register timed callback without passing a callable callback!\n"); return NULL; @@ -468,7 +452,7 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) Py_INCREF(function); // Remaining args for function - fArgs = PyTuple_GetSlice(args, 2, numargs); // New reference + PyObject *fArgs = PyTuple_GetSlice(args, 2, numargs); // New reference if (fArgs == NULL) { return NULL; } @@ -478,8 +462,8 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) return NULL; } - - hdl = gpi_register_timed_callback((gpi_function_t)handle_gpi_callback, callback_data_p, time_ps); + gpi_cb_hdl hdl = gpi_register_timed_callback( + (gpi_function_t)handle_gpi_callback, callback_data_p, time_ps); // Check success PyObject *rv = gpi_hdl_New(hdl); @@ -498,12 +482,6 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) COCOTB_UNUSED(self); FENTER - PyObject *fArgs; - PyObject *function; - gpi_sim_hdl sig_hdl; - gpi_cb_hdl hdl; - int edge; - Py_ssize_t numargs = PyTuple_Size(args); if (numargs < 3) { @@ -516,10 +494,10 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) PyErr_SetString(PyExc_TypeError, "First argument must be a gpi_sim_hdl"); return NULL; } - sig_hdl = ((gpi_hdl_Object *)pSigHdl)->hdl; + gpi_sim_hdl sig_hdl = ((gpi_hdl_Object *)pSigHdl)->hdl; // Extract the callback function - function = PyTuple_GetItem(args, 1); + PyObject *function = PyTuple_GetItem(args, 1); if (!PyCallable_Check(function)) { PyErr_SetString(PyExc_TypeError, "Attempt to register value change callback without passing a callable callback!\n"); return NULL; @@ -527,10 +505,10 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) Py_INCREF(function); PyObject *pedge = PyTuple_GetItem(args, 2); - edge = (int)PyLong_AsLong(pedge); + int edge = (int)PyLong_AsLong(pedge); // Remaining args for function - fArgs = PyTuple_GetSlice(args, 3, numargs); // New reference + PyObject *fArgs = PyTuple_GetSlice(args, 3, numargs); // New reference if (fArgs == NULL) { return NULL; } @@ -540,11 +518,8 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) return NULL; } - - hdl = gpi_register_value_change_callback((gpi_function_t)handle_gpi_callback, - callback_data_p, - sig_hdl, - edge); + gpi_cb_hdl hdl = gpi_register_value_change_callback( + (gpi_function_t)handle_gpi_callback, callback_data_p, sig_hdl, edge); // Check success PyObject *rv = gpi_hdl_New(hdl); From ed4d6dc90dcef758d972469c9a422894ba67a17b Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Fri, 17 Apr 2020 10:34:08 +0100 Subject: [PATCH 2158/2656] Rename make_callback_data and simplify typedefs Since this is now C++ code, we no longer need typedefs around our structs. --- .../share/lib/simulator/simulatormodule.cpp | 52 +++++++++---------- cocotb/share/lib/simulator/simulatormodule.h | 4 +- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index 10c79a18..ca61efad 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -184,14 +184,14 @@ int handle_gpi_callback(void *user_data) { int ret = 0; to_python(); - p_callback_data callback_data_p = (p_callback_data)user_data; + callback_data *cb_data = (callback_data *)user_data; - if (callback_data_p->id_value != COCOTB_ACTIVE_ID) { + if (cb_data->id_value != COCOTB_ACTIVE_ID) { fprintf(stderr, "Userdata corrupted!\n"); ret = 1; goto err; } - callback_data_p->id_value = COCOTB_INACTIVE_ID; + cb_data->id_value = COCOTB_INACTIVE_ID; /* Cache the sim time */ gpi_get_sim_time(&cache_time.high, &cache_time.low); @@ -201,7 +201,7 @@ int handle_gpi_callback(void *user_data) // Python allowed - if (!PyCallable_Check(callback_data_p->function)) { + if (!PyCallable_Check(cb_data->function)) { fprintf(stderr, "Callback fired but function isn't callable?!\n"); ret = 1; goto out; @@ -209,7 +209,7 @@ int handle_gpi_callback(void *user_data) { // Call the callback - PyObject *pValue = PyObject_Call(callback_data_p->function, callback_data_p->args, callback_data_p->kwargs); + PyObject *pValue = PyObject_Call(cb_data->function, cb_data->args, cb_data->kwargs); // If the return value is NULL a Python exception has occurred // The best thing to do here is shutdown as any subsequent @@ -229,12 +229,12 @@ int handle_gpi_callback(void *user_data) } // Callbacks may have been re-enabled - if (callback_data_p->id_value == COCOTB_INACTIVE_ID) { - Py_DECREF(callback_data_p->function); - Py_DECREF(callback_data_p->args); + if (cb_data->id_value == COCOTB_INACTIVE_ID) { + Py_DECREF(cb_data->function); + Py_DECREF(cb_data->args); // Free the callback data - free(callback_data_p); + free(cb_data); } out: @@ -270,9 +270,9 @@ static PyObject *log_msg(PyObject *self, PyObject *args) } -static p_callback_data make_callback_data(PyObject *func, PyObject *args, PyObject *kwargs) +static callback_data *callback_data_new(PyObject *func, PyObject *args, PyObject *kwargs) { - p_callback_data data = (p_callback_data)malloc(sizeof(s_callback_data)); + callback_data *data = (callback_data *)malloc(sizeof(callback_data)); if (data == NULL) { PyErr_NoMemory(); return NULL; @@ -317,12 +317,12 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) return NULL; } - p_callback_data callback_data_p = make_callback_data(function, fArgs, NULL); - if (callback_data_p == NULL) { + callback_data *cb_data = callback_data_new(function, fArgs, NULL); + if (cb_data == NULL) { return NULL; } - gpi_cb_hdl hdl = gpi_register_readonly_callback((gpi_function_t)handle_gpi_callback, callback_data_p); + gpi_cb_hdl hdl = gpi_register_readonly_callback((gpi_function_t)handle_gpi_callback, cb_data); PyObject *rv = gpi_hdl_New(hdl); FEXIT @@ -357,13 +357,13 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) return NULL; } - p_callback_data callback_data_p = make_callback_data(function, fArgs, NULL); - if (callback_data_p == NULL) { + callback_data *cb_data = callback_data_new(function, fArgs, NULL); + if (cb_data == NULL) { return NULL; } gpi_cb_hdl hdl = gpi_register_readwrite_callback( - (gpi_function_t)handle_gpi_callback, callback_data_p); + (gpi_function_t)handle_gpi_callback, cb_data); PyObject *rv = gpi_hdl_New(hdl); FEXIT @@ -398,13 +398,13 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) return NULL; } - p_callback_data callback_data_p = make_callback_data(function, fArgs, NULL); - if (callback_data_p == NULL) { + callback_data *cb_data = callback_data_new(function, fArgs, NULL); + if (cb_data == NULL) { return NULL; } gpi_cb_hdl hdl = gpi_register_nexttime_callback( - (gpi_function_t)handle_gpi_callback, callback_data_p); + (gpi_function_t)handle_gpi_callback, cb_data); PyObject *rv = gpi_hdl_New(hdl); FEXIT @@ -457,13 +457,13 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) return NULL; } - p_callback_data callback_data_p = make_callback_data(function, fArgs, NULL); - if (callback_data_p == NULL) { + callback_data *cb_data = callback_data_new(function, fArgs, NULL); + if (cb_data == NULL) { return NULL; } gpi_cb_hdl hdl = gpi_register_timed_callback( - (gpi_function_t)handle_gpi_callback, callback_data_p, time_ps); + (gpi_function_t)handle_gpi_callback, cb_data, time_ps); // Check success PyObject *rv = gpi_hdl_New(hdl); @@ -513,13 +513,13 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) return NULL; } - p_callback_data callback_data_p = make_callback_data(function, fArgs, NULL); - if (callback_data_p == NULL) { + callback_data *cb_data = callback_data_new(function, fArgs, NULL); + if (cb_data == NULL) { return NULL; } gpi_cb_hdl hdl = gpi_register_value_change_callback( - (gpi_function_t)handle_gpi_callback, callback_data_p, sig_hdl, edge); + (gpi_function_t)handle_gpi_callback, cb_data, sig_hdl, edge); // Check success PyObject *rv = gpi_hdl_New(hdl); diff --git a/cocotb/share/lib/simulator/simulatormodule.h b/cocotb/share/lib/simulator/simulatormodule.h index 1aaaa82c..61f072ca 100644 --- a/cocotb/share/lib/simulator/simulatormodule.h +++ b/cocotb/share/lib/simulator/simulatormodule.h @@ -42,14 +42,14 @@ #define MODULE_NAME "simulator" // callback user data -typedef struct t_callback_data { +struct callback_data { PyThreadState *_saved_thread_state; // Thread state of the calling thread FIXME is this required? uint32_t id_value; // COCOTB_ACTIVE_ID or COCOTB_INACTIVE_ID PyObject *function; // Function to call when the callback fires PyObject *args; // The arguments to call the function with PyObject *kwargs; // Keyword arguments to call the function with gpi_sim_hdl cb_hdl; -} s_callback_data, *p_callback_data; +}; static PyObject *log_msg(PyObject *self, PyObject *args); From a5a2b48420ba9b6c787e5deaccc8e4cde63d6b8f Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Fri, 17 Apr 2020 16:06:15 +0100 Subject: [PATCH 2159/2656] Remove dead code (#1666) The C++ standard mandates that ` new T ` can never return NULL. If an allocation fails, it must throw `std::bad_alloc`. The only way `new` can return NULL is in the case `new (std::nothrow) T`, which we do not use. --- cocotb/share/lib/fli/FliObjHdl.cpp | 23 ----------------------- cocotb/share/lib/vhpi/VhpiCbHdl.cpp | 14 -------------- 2 files changed, 37 deletions(-) diff --git a/cocotb/share/lib/fli/FliObjHdl.cpp b/cocotb/share/lib/fli/FliObjHdl.cpp index 503ec328..e7d8f1f2 100644 --- a/cocotb/share/lib/fli/FliObjHdl.cpp +++ b/cocotb/share/lib/fli/FliObjHdl.cpp @@ -266,10 +266,6 @@ int FliLogicObjHdl::initialise(std::string &name, std::string &fq_name) m_num_enum = mti_TickLength(elemType); m_mti_buff = new char[m_num_elems+1]; - if (!m_mti_buff) { - LOG_CRITICAL("Unable to alloc mem for value object mti read buffer: ABORTING"); - return -1; - } } break; default: @@ -282,9 +278,6 @@ int FliLogicObjHdl::initialise(std::string &name, std::string &fq_name) } m_val_buff = new char[m_num_elems+1]; - if (!m_val_buff) { - LOG_CRITICAL("Unable to alloc mem for value object read buffer: ABORTING"); - } m_val_buff[m_num_elems] = '\0'; return FliValueObjHdl::initialise(name, fq_name); @@ -403,10 +396,6 @@ int FliIntObjHdl::initialise(std::string &name, std::string &fq_name) m_num_elems = 1; m_val_buff = new char[33]; // Integers are always 32-bits - if (!m_val_buff) { - LOG_CRITICAL("Unable to alloc mem for value object read buffer: ABORTING"); - return -1; - } m_val_buff[m_num_elems] = '\0'; return FliValueObjHdl::initialise(name, fq_name); @@ -465,10 +454,6 @@ int FliRealObjHdl::initialise(std::string &name, std::string &fq_name) m_num_elems = 1; m_mti_buff = new double; - if (!m_mti_buff) { - LOG_CRITICAL("Unable to alloc mem for value object mti read buffer: ABORTING"); - return -1; - } return FliValueObjHdl::initialise(name, fq_name); } @@ -512,16 +497,8 @@ int FliStringObjHdl::initialise(std::string &name, std::string &fq_name) m_indexable = true; m_mti_buff = new char[m_num_elems]; - if (!m_mti_buff) { - LOG_CRITICAL("Unable to alloc mem for value object mti read buffer: ABORTING"); - return -1; - } m_val_buff = new char[m_num_elems+1]; - if (!m_val_buff) { - LOG_CRITICAL("Unable to alloc mem for value object read buffer: ABORTING"); - return -1; - } m_val_buff[m_num_elems] = '\0'; return FliValueObjHdl::initialise(name, fq_name); diff --git a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp index 4168035b..e5f9bff5 100644 --- a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp +++ b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp @@ -296,9 +296,6 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { m_value.bufSize = static_cast(bufSize); m_value.value.str = new vhpiCharT[bufSize]; m_value.numElems = m_num_elems; - if (!m_value.value.str) { - LOG_CRITICAL("Unable to alloc mem for write buffer"); - } LOG_DEBUG("Overriding num_elems to %d", m_num_elems); break; } @@ -318,10 +315,6 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { int bufSize = m_num_elems * static_cast(sizeof(vhpiCharT)) + 1; m_binvalue.bufSize = static_cast(bufSize); m_binvalue.value.str = new vhpiCharT[bufSize]; - - if (!m_binvalue.value.str) { - LOG_CRITICAL("Unable to alloc mem for read buffer of signal %s", name.c_str()); - } } return GpiObjHdl::initialise(name, fq_name); @@ -367,9 +360,6 @@ int VhpiLogicSignalObjHdl::initialise(std::string &name, std::string &fq_name) { int bufSize = m_num_elems * static_cast(sizeof(vhpiEnumT)); m_value.bufSize = static_cast(bufSize); m_value.value.enumvs = new vhpiEnumT[bufSize]; - if (!m_value.value.enumvs) { - LOG_CRITICAL("Unable to alloc mem for write buffer: ABORTING"); - } } if (m_indexable && get_range(handle, 0, &m_range_left, &m_range_right)) { @@ -380,10 +370,6 @@ int VhpiLogicSignalObjHdl::initialise(std::string &name, std::string &fq_name) { int bufSize = m_num_elems * static_cast(sizeof(vhpiCharT)) + 1; m_binvalue.bufSize = static_cast(bufSize); m_binvalue.value.str = new vhpiCharT[bufSize]; - - if (!m_binvalue.value.str) { - LOG_CRITICAL("Unable to alloc mem for read buffer of signal %s", name.c_str()); - } } return GpiObjHdl::initialise(name, fq_name); From b30cf07cc81a650b71548ce3e24d0820b6f7d802 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 16 Apr 2020 18:49:42 +0200 Subject: [PATCH 2160/2656] Show how to set SIM to start each simulator --- documentation/source/simulator_support.rst | 74 +++++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 06697253..6a858be1 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -12,6 +12,12 @@ This page documents specifics, limitations, workarounds etc. in the various simu Icarus ====== +In order to use this simulator, set :make:var:`SIM` to ``icarus``: + +.. code-block:: bash + + make SIM=icarus + .. _sim-icarus-accessing-bits: Accessing bits in a vector @@ -67,6 +73,12 @@ and therefore the make variables :make:var:`COCOTB_HDL_TIMEUNIT` and :make:var:` Verilator ========= +In order to use this simulator, set :make:var:`SIM` to ``verilator``: + +.. code-block:: bash + + make SIM=verilator + cocotb supports Verilator 4.020 and above. Verilator converts Verilog code to C++ code that is compiled. It does not support VHDL. @@ -94,11 +106,18 @@ To enable HDL code coverage, add Verilator's coverage option(s) to the :make:var This will result in coverage data being written to ``coverage.dat``. + .. _sim-vcs: Synopsys VCS ============ +In order to use this simulator, set :make:var:`SIM` to ``vcs``: + +.. code-block:: bash + + make SIM=vcs + cocotb currently only supports VPI for Synopsys VCS, not VHPI. @@ -107,6 +126,12 @@ cocotb currently only supports VPI for Synopsys VCS, not VHPI. Aldec Riviera-PRO ================= +In order to use this simulator, set :make:var:`SIM` to ``aldec``: + +.. code-block:: bash + + make SIM=aldec + .. note:: On Windows, do not install the C++ compiler, i.e. unselect it during the installation process of Riviera-PRO. @@ -115,18 +140,31 @@ Aldec Riviera-PRO The :envvar:`LICENSE_QUEUE` environment variable can be used for this simulator – this setting will be mirrored in the TCL ``license_queue`` variable to control runtime license checkouts. + .. _sim-activehdl: Aldec Active-HDL ================ +In order to use this simulator, set :make:var:`SIM` to ``activehdl``: + +.. code-block:: bash + + make SIM=activehdl + .. _sim-questa: Mentor Questa ============= -See :ref:`sim-modelsim`. +In order to use this simulator, set :make:var:`SIM` to ``questa``: + +.. code-block:: bash + + make SIM=questa + +For more information, see :ref:`sim-modelsim`. .. _sim-modelsim: @@ -134,6 +172,12 @@ See :ref:`sim-modelsim`. Mentor ModelSim =============== +In order to use this simulator, set :make:var:`SIM` to ``modelsim``: + +.. code-block:: bash + + make SIM=modelsim + .. note:: In order to use FLI (for VHDL), a ``vdbg`` executable from the simulator installation directory needs to be available on the ``PATH`` during cocotb installation. @@ -154,13 +198,26 @@ ModelSim DE and SE (and Questa, of course) support the FLI. Cadence Incisive ================ -See :ref:`sim-xcelium`. +In order to use this simulator, set :make:var:`SIM` to ``ius``: + +.. code-block:: bash + + make SIM=ius + +For more information, see :ref:`sim-xcelium`. + .. _sim-xcelium: Cadence Xcelium =============== +In order to use this simulator, set :make:var:`SIM` to ``xcelium``: + +.. code-block:: bash + + make SIM=xcelium + The simulator automatically loads VPI even when only VHPI is requested. @@ -169,12 +226,25 @@ The simulator automatically loads VPI even when only VHPI is requested. GHDL ==== +In order to use this simulator, set :make:var:`SIM` to ``ghdl``: + +.. code-block:: bash + + make SIM=ghdl + Support is preliminary. Noteworthy is that despite GHDL being a VHDL simulator, it implements the VPI interface. + .. _sim-nvc: NVC === +In order to use this simulator, set :make:var:`SIM` to ``nvc``: + +.. code-block:: bash + + make SIM=nvc + To enable display of VHPI traces, use ``SIM_ARGS=--vhpi-trace make ...``. From 7db4c187e2c303d31f8226782b2afece6432eade Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Fri, 10 Apr 2020 15:43:26 +0200 Subject: [PATCH 2161/2656] =?UTF-8?q?Rearrange=C2=A0Makefiles.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Move all "logic" to Makefile.inc - Makefile.sim serves only to include appropriate simulator Makefiles depending on SIM environmental variable - Include Makefile.inc in every simulator Makefile User need to include ONLY Makefile.sim via include $(shell cocotb-config --makefiles)/Makefile.sim. User can create/copy any of simulator Makefiles, modify as needed and include instead of Makefile.sim. This way can easily use custom/advanced configuration/options and cocotb does not need to support them. Co-Authored-By: Eric Wieser Co-Authored-By: Philipp Wagner --- cocotb/share/makefiles/Makefile.inc | 66 ++++++++++----- cocotb/share/makefiles/Makefile.sim | 81 ++++++++----------- .../makefiles/simulators/Makefile.activehdl | 2 + .../share/makefiles/simulators/Makefile.aldec | 2 + .../share/makefiles/simulators/Makefile.cvc | 2 + .../share/makefiles/simulators/Makefile.ghdl | 2 + .../makefiles/simulators/Makefile.icarus | 2 + .../share/makefiles/simulators/Makefile.ius | 2 + .../share/makefiles/simulators/Makefile.nvc | 2 + .../makefiles/simulators/Makefile.questa | 2 + .../share/makefiles/simulators/Makefile.vcs | 2 + .../makefiles/simulators/Makefile.verilator | 2 + .../makefiles/simulators/Makefile.xcelium | 2 + .../source/newsfragments/1629.feature.rst | 1 + 14 files changed, 102 insertions(+), 68 deletions(-) create mode 100644 documentation/source/newsfragments/1629.feature.rst diff --git a/cocotb/share/makefiles/Makefile.inc b/cocotb/share/makefiles/Makefile.inc index 4f46a343..71698d08 100644 --- a/cocotb/share/makefiles/Makefile.inc +++ b/cocotb/share/makefiles/Makefile.inc @@ -29,6 +29,19 @@ # Common makefile included by everything +ifndef COCOTB_MAKEFILE_INC_INCLUDED # Protect against multiple includes +COCOTB_MAKEFILE_INC_INCLUDED = 1 + +# Default sim rule will force a re-run of the simulation (though the cocotb library +# and RTL compilation phases are still evaluated by makefile dependencies) +.PHONY: sim +sim: + -@rm -f $(COCOTB_RESULTS_FILE) + $(MAKE) $(COCOTB_RESULTS_FILE) + +# Make sure to use bash for the pipefail option used in many simulator Makefiles +SHELL := bash + # Directory containing the cocotb Python module (realpath for Windows compatibility) COCOTB_PY_DIR := $(realpath $(shell cocotb-config --prefix)) @@ -38,9 +51,9 @@ COCOTB_SHARE_DIR := $(COCOTB_PY_DIR)/cocotb/share OS=$(shell uname) ifneq (, $(findstring MINGW, $(OS))) -OS=Msys + OS := Msys else ifneq (, $(findstring MSYS, $(OS))) -OS=Msys + OS := Msys endif export OS @@ -50,27 +63,12 @@ PYTHON_BIN ?= $(realpath $(shell cocotb-config --python-bin)) include $(COCOTB_SHARE_DIR)/makefiles/Makefile.deprecations -# Default to Icarus if no simulator is defined -SIM ?= icarus - -SIM_DEFINE := $(shell echo $(SIM) | tr a-z A-Z) - -# Use a common define for Questa and Modelsim and cvc -ifeq ($(SIM_DEFINE),$(filter $(SIM_DEFINE),QUESTA CVC)) -SIM_DEFINE = MODELSIM -endif - -# Use a common define for Xcelium and IUS -ifeq ($(SIM_DEFINE),XCELIUM) -SIM_DEFINE = IUS -endif - LIB_DIR=$(COCOTB_PY_DIR)/cocotb/libs ifeq ($(OS),Msys) -LIB_EXT := dll + LIB_EXT := dll else -LIB_EXT := so + LIB_EXT := so endif PYTHON_ARCH := $(shell $(PYTHON_BIN) -c 'from platform import architecture; print(architecture()[0])') @@ -92,3 +90,33 @@ ifeq ($(OS),Msys) else to_tcl_path = $(1) endif + +# Check that the COCOTB_RESULTS_FILE was created, since we can't set an exit code from cocotb. +define check_for_results_file + @test -f $(COCOTB_RESULTS_FILE) || (echo "ERROR: $(COCOTB_RESULTS_FILE) was not written by the simulation!" >&2 && exit 1) +endef + +SIM_BUILD ?= sim_build +export SIM_BUILD + +COCOTB_RESULTS_FILE ?= results.xml +COCOTB_HDL_TIMEUNIT ?= 1ns +COCOTB_HDL_TIMEPRECISION ?= 1ps + +# Depend on all Python from the cocotb package. This triggers a +# recompilation of the simulation if cocotb is updated. +CUSTOM_SIM_DEPS += $(shell find $(COCOTB_PY_DIR)/cocotb/ -name "*.py") + +# This triggers a recompilation of the simulation if cocotb library is updated. +CUSTOM_SIM_DEPS += $(shell find $(LIB_DIR)/ -name "*") + +$(SIM_BUILD): + mkdir -p $@ + +# Regression rule uses Make dependencies to determine whether to run the simulation +.PHONY: regression +regression: $(COCOTB_RESULTS_FILE) + +else + $(warning "Including Makefile.inc from a user makefile is a no-op and deprecated. Remove the Makefile.inc inclusion from your makefile, and only leave the Makefile.sim include.") +endif # COCOTB_MAKEFILE_INC_INCLUDED diff --git a/cocotb/share/makefiles/Makefile.sim b/cocotb/share/makefiles/Makefile.sim index ae25d261..29c4ad9f 100644 --- a/cocotb/share/makefiles/Makefile.sim +++ b/cocotb/share/makefiles/Makefile.sim @@ -27,11 +27,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -# This is a simple wrapper Makefile to pull in the appropriate Makefile for -# the desired simulator and set up common paths for the sims to use to build with - -# Make sure to use bash for the pipefail option used in many simulator Makefiles -SHELL := bash +# This file includes an appropriate makefile depending on the SIM variable. .PHONY: all all: sim @@ -94,59 +90,46 @@ endef # this cannot be a regular target because of the way Makefile.$(SIM) is included ifeq ($(MAKECMDGOALS),help) -_VERSION := $(shell cocotb-config -v) -# for non-intuitive use of ifneq see https://www.gnu.org/software/make/manual/make.html#Testing-Flags -ifneq (,$(findstring dev,$(_VERSION))) -DOCLINK := https://cocotb.readthedocs.io/en/latest/building.html -else -DOCLINK := https://cocotb.readthedocs.io/en/v$(_VERSION)/building.html -endif -$(info $(helpmsg)) -# is there a cleaner way to exit here? -$(error "Stopping after printing help") + _VERSION := $(shell cocotb-config -v) + # for non-intuitive use of ifneq see https://www.gnu.org/software/make/manual/make.html#Testing-Flags + ifneq (,$(findstring dev,$(_VERSION))) + DOCLINK := https://cocotb.readthedocs.io/en/latest/building.html + else + DOCLINK := https://cocotb.readthedocs.io/en/v$(_VERSION)/building.html + endif + $(info $(helpmsg)) + # is there a cleaner way to exit here? + $(error "Stopping after printing help") endif -SIM_BUILD ?= sim_build -export SIM_BUILD - -COCOTB_RESULTS_FILE ?= results.xml -COCOTB_HDL_TIMEUNIT ?= 1ns -COCOTB_HDL_TIMEPRECISION ?= 1ps - -# Maintain backwards compatibility by supporting upper and lower case SIM variable -SIM_LOWERCASE := $(shell echo $(SIM) | tr A-Z a-z) +# Default to Icarus if no simulator is defined +SIM ?= icarus -HAVE_SIMULATOR = $(shell if [ -f $(COCOTB_SHARE_DIR)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) ]; then echo 1; else echo 0; fi;) -AVAILABLE_SIMULATORS = $(patsubst .%,%,$(suffix $(wildcard $(COCOTB_SHARE_DIR)/makefiles/simulators/Makefile.*))) +SIM_DEFINE := $(shell echo $(SIM) | tr a-z A-Z) -ifeq ($(HAVE_SIMULATOR),0) - $(error "Couldn't find makefile for simulator: "$(SIM_LOWERCASE)"! Available simulators: $(AVAILABLE_SIMULATORS)") +# Use a common define for Questa and Modelsim and cvc +ifeq ($(SIM_DEFINE),$(filter $(SIM_DEFINE),QUESTA CVC)) + SIM_DEFINE = MODELSIM endif -# Depend on all Python from the cocotb package. This triggers a -# recompilation of the simulation if cocotb is updated. -CUSTOM_SIM_DEPS += $(shell find $(COCOTB_PY_DIR)/cocotb/ -name "*.py") +# Use a common define for Xcelium and IUS +ifeq ($(SIM_DEFINE),XCELIUM) + SIM_DEFINE = IUS +endif -# This triggers a recompilation of the simulation if cocotb library is updated. -CUSTOM_SIM_DEPS += $(shell find $(LIB_DIR)/ -name "*") +# Maintain backwards compatibility by supporting upper and lower case SIM variable +SIM_LOWERCASE := $(shell echo $(SIM) | tr A-Z a-z) -include $(COCOTB_SHARE_DIR)/makefiles/simulators/Makefile.$(SIM_LOWERCASE) +# Directory containing the cocotb Makfiles (realpath for Windows compatibility) +COCOTB_MAKEFILES_DIR := $(realpath $(shell cocotb-config --makefiles)) -# Check that the COCOTB_RESULTS_FILE was created, since we can't set an exit code from cocotb. -define check_for_results_file - @test -f $(COCOTB_RESULTS_FILE) || (echo "ERROR: $(COCOTB_RESULTS_FILE) was not written by the simulation!" >&2 && exit 1) -endef +include $(COCOTB_MAKEFILES_DIR)/Makefile.deprecations -$(SIM_BUILD): - mkdir -p $@ +HAVE_SIMULATOR = $(shell if [ -f $(COCOTB_MAKEFILES_DIR)/simulators/Makefile.$(SIM_LOWERCASE) ]; then echo 1; else echo 0; fi;) +AVAILABLE_SIMULATORS = $(patsubst .%,%,$(suffix $(wildcard $(COCOTB_MAKEFILES_DIR)/simulators/Makefile.*))) -# Regression rule uses Make dependencies to determine whether to run the simulation -.PHONY: regression -regression: $(COCOTB_RESULTS_FILE) +ifeq ($(HAVE_SIMULATOR),0) + $(error "Couldn't find makefile for simulator: "$(SIM_LOWERCASE)"! Available simulators: $(AVAILABLE_SIMULATORS)") +endif -# Default sim rule will force a re-run of the simulation (though the cocotb library -# and RTL compilation phases are still evaluated by makefile dependencies) -.PHONY: sim -sim: - -@rm -f $(COCOTB_RESULTS_FILE) - $(MAKE) $(COCOTB_RESULTS_FILE) +include $(COCOTB_MAKEFILES_DIR)/simulators/Makefile.$(SIM_LOWERCASE) diff --git a/cocotb/share/makefiles/simulators/Makefile.activehdl b/cocotb/share/makefiles/simulators/Makefile.activehdl index 387a6d00..ee71dc81 100644 --- a/cocotb/share/makefiles/simulators/Makefile.activehdl +++ b/cocotb/share/makefiles/simulators/Makefile.activehdl @@ -4,6 +4,8 @@ # Common Makefile for the Aldec Active-HDL simulator +include $(shell cocotb-config --makefiles)/Makefile.inc + CMD_BIN := vsimsa ifdef ACTIVEHDL_BIN_DIR diff --git a/cocotb/share/makefiles/simulators/Makefile.aldec b/cocotb/share/makefiles/simulators/Makefile.aldec index b80a296f..9bbf89a1 100644 --- a/cocotb/share/makefiles/simulators/Makefile.aldec +++ b/cocotb/share/makefiles/simulators/Makefile.aldec @@ -29,6 +29,8 @@ # Common Makefile for Aldec Riviera-PRO simulator +include $(shell cocotb-config --makefiles)/Makefile.inc + ifeq ($(GUI),1) CMD_BIN := riviera else diff --git a/cocotb/share/makefiles/simulators/Makefile.cvc b/cocotb/share/makefiles/simulators/Makefile.cvc index b3ab9e26..e68bcf39 100644 --- a/cocotb/share/makefiles/simulators/Makefile.cvc +++ b/cocotb/share/makefiles/simulators/Makefile.cvc @@ -25,6 +25,8 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +include $(shell cocotb-config --makefiles)/Makefile.inc + ifneq ($(VHDL_SOURCES),) $(COCOTB_RESULTS_FILE): diff --git a/cocotb/share/makefiles/simulators/Makefile.ghdl b/cocotb/share/makefiles/simulators/Makefile.ghdl index 09e4793c..3dc3d66c 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ghdl +++ b/cocotb/share/makefiles/simulators/Makefile.ghdl @@ -26,6 +26,8 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +include $(shell cocotb-config --makefiles)/Makefile.inc + ifneq ($(VERILOG_SOURCES),) $(COCOTB_RESULTS_FILE): diff --git a/cocotb/share/makefiles/simulators/Makefile.icarus b/cocotb/share/makefiles/simulators/Makefile.icarus index 08eb4100..75438164 100644 --- a/cocotb/share/makefiles/simulators/Makefile.icarus +++ b/cocotb/share/makefiles/simulators/Makefile.icarus @@ -27,6 +27,8 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +include $(shell cocotb-config --makefiles)/Makefile.inc + ifneq ($(VHDL_SOURCES),) $(COCOTB_RESULTS_FILE): diff --git a/cocotb/share/makefiles/simulators/Makefile.ius b/cocotb/share/makefiles/simulators/Makefile.ius index 077cb8c0..a49530f6 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ius +++ b/cocotb/share/makefiles/simulators/Makefile.ius @@ -29,6 +29,8 @@ # Common Makefile for Cadence Incisive +include $(shell cocotb-config --makefiles)/Makefile.inc + CMD_BIN := irun ifdef IUS_BIN_DIR diff --git a/cocotb/share/makefiles/simulators/Makefile.nvc b/cocotb/share/makefiles/simulators/Makefile.nvc index 1fff0fec..f696f265 100644 --- a/cocotb/share/makefiles/simulators/Makefile.nvc +++ b/cocotb/share/makefiles/simulators/Makefile.nvc @@ -1,5 +1,7 @@ # -*- mode: makefile-gmake -*- +include $(shell cocotb-config --makefiles)/Makefile.inc + ifneq ($(VERILOG_SOURCES),) $(COCOTB_RESULTS_FILE): diff --git a/cocotb/share/makefiles/simulators/Makefile.questa b/cocotb/share/makefiles/simulators/Makefile.questa index f6333563..4a62c6b6 100644 --- a/cocotb/share/makefiles/simulators/Makefile.questa +++ b/cocotb/share/makefiles/simulators/Makefile.questa @@ -27,6 +27,8 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +include $(shell cocotb-config --makefiles)/Makefile.inc + CMD_BIN := vsim ifdef MODELSIM_BIN_DIR diff --git a/cocotb/share/makefiles/simulators/Makefile.vcs b/cocotb/share/makefiles/simulators/Makefile.vcs index 14527f8c..5374a215 100644 --- a/cocotb/share/makefiles/simulators/Makefile.vcs +++ b/cocotb/share/makefiles/simulators/Makefile.vcs @@ -27,6 +27,8 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +include $(shell cocotb-config --makefiles)/Makefile.inc + ifneq ($(VHDL_SOURCES),) $(COCOTB_RESULTS_FILE): diff --git a/cocotb/share/makefiles/simulators/Makefile.verilator b/cocotb/share/makefiles/simulators/Makefile.verilator index 2139423b..ed798f31 100644 --- a/cocotb/share/makefiles/simulators/Makefile.verilator +++ b/cocotb/share/makefiles/simulators/Makefile.verilator @@ -2,6 +2,8 @@ # Licensed under the Revised BSD License, see LICENSE for details. # SPDX-License-Identifier: BSD-3-Clause +include $(shell cocotb-config --makefiles)/Makefile.inc + ifneq ($(VHDL_SOURCES),) results.xml: diff --git a/cocotb/share/makefiles/simulators/Makefile.xcelium b/cocotb/share/makefiles/simulators/Makefile.xcelium index 14aebfe2..3453e139 100644 --- a/cocotb/share/makefiles/simulators/Makefile.xcelium +++ b/cocotb/share/makefiles/simulators/Makefile.xcelium @@ -27,6 +27,8 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +include $(shell cocotb-config --makefiles)/Makefile.inc + # Common Makefile for Cadence Xcelium CMD_BIN := xrun diff --git a/documentation/source/newsfragments/1629.feature.rst b/documentation/source/newsfragments/1629.feature.rst new file mode 100644 index 00000000..f2bc8af8 --- /dev/null +++ b/documentation/source/newsfragments/1629.feature.rst @@ -0,0 +1 @@ +Including ``Makefile.inc`` from user makefiles is now a no-op and deprecated. Lines like ``include $(shell cocotb-config --makefiles)/Makefile.inc`` can be removed from user makefiles without loss in functionality. From 61fbc8709d2780c1596ee73e54cb8ed6505b1036 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sat, 18 Apr 2020 23:26:35 +0200 Subject: [PATCH 2162/2656] Add subsection for CVC for completeness (#1669) Add subsection for CVC for completeness --- documentation/source/simulator_support.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 6a858be1..23e79a86 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -248,3 +248,18 @@ In order to use this simulator, set :make:var:`SIM` to ``nvc``: make SIM=nvc To enable display of VHPI traces, use ``SIM_ARGS=--vhpi-trace make ...``. + + +.. _sim-cvc: + +Tachyon DA CVC +============== + +In order to use `Tachyon DA `_'s `CVC `_ simulator, +set :make:var:`SIM` to ``cvc``: + +.. code-block:: bash + + make SIM=cvc + +Note that cocotb's makefile is using CVC's interpreted mode. From 804f8a74e8dd26f55233d880d6fb69455c43bfa7 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Sat, 18 Apr 2020 23:31:27 +0200 Subject: [PATCH 2163/2656] adding verilator waveform capability in documentation (#1607) Add info about Verilator waveform tracing to docs --- documentation/source/simulator_support.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 23e79a86..5c0a6bfb 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -106,6 +106,20 @@ To enable HDL code coverage, add Verilator's coverage option(s) to the :make:var This will result in coverage data being written to ``coverage.dat``. +.. _sim-verilator-waveforms: + +Waveforms +--------- + +To get waveforms in VCD format, add Verilator's trace option(s) to the +:make:var:`EXTRA_ARGS` make variable, for example in a Makefile: + + .. code-block:: make + + EXTRA_ARGS += --trace --trace-structs + +To set the same options on the command line, use ``EXTRA_ARGS="--trace --trace-structs" make ...``. +A VCD file named ``dump.vcd`` will be generated in the current directory. .. _sim-vcs: From 2a6ed6d3f9fbdf59950280953ff4928f56fb3b73 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 18 Apr 2020 22:37:39 +0100 Subject: [PATCH 2164/2656] Simplify `cocotb.generators` to use builtins where possible (#1493) --- cocotb/generators/__init__.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/cocotb/generators/__init__.py b/cocotb/generators/__init__.py index ae79532d..600fc87e 100644 --- a/cocotb/generators/__init__.py +++ b/cocotb/generators/__init__.py @@ -30,6 +30,7 @@ """ import math import random +import itertools from cocotb.decorators import public @@ -44,11 +45,9 @@ def repeat(obj, nrepeat=None): nrepeat (int): The number of times to repeatedly yield obj """ if nrepeat is None: - while True: - yield obj + return itertools.repeat(obj) else: - for i in range(nrepeat): - yield obj + return itertools.repeat(obj, times=nrepeat) @public @@ -59,9 +58,7 @@ def combine(generators): Args: generators (iterable): Generators to combine together """ - for gen in generators: - for item in gen: - yield item + return itertools.chain.from_iterable(generators) @public From 7fd2b03a445463cc261dc2203a32e47d6b84d07f Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sat, 18 Apr 2020 23:41:45 +0200 Subject: [PATCH 2165/2656] Replace link to removed "introduction" page --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a05a5881..296e8cfd 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ For more details, including how to install a development version of cocotb, see As a first trivial introduction to cocotb, the following example "tests" a flip-flop. -First, we need a hardware design which we can test. For this example, create a file `dff.sv` with SystemVerilog code for a simple [D flip-flop](https://en.wikipedia.org/wiki/Flip-flop_(electronics)#D_flip-flop). You could also use any other language a [cocotb-supported simulator](https://docs.cocotb.org/en/latest/introduction.html) understands, e.g. VHDL. +First, we need a hardware design which we can test. For this example, create a file `dff.sv` with SystemVerilog code for a simple [D flip-flop](https://en.wikipedia.org/wiki/Flip-flop_(electronics)#D_flip-flop). You could also use any other language a [cocotb-supported simulator](https://docs.cocotb.org/en/latest/simulator_support.html) understands, e.g. VHDL. ```systemverilog // dff.sv From 1a4d7bf5a3251afd8cde5e52d114d15c497b525c Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Sun, 19 Apr 2020 09:52:08 +0200 Subject: [PATCH 2166/2656] Fix include path in modelsim makefile --- cocotb/share/makefiles/simulators/Makefile.modelsim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.modelsim b/cocotb/share/makefiles/simulators/Makefile.modelsim index e437dac3..67704b92 100644 --- a/cocotb/share/makefiles/simulators/Makefile.modelsim +++ b/cocotb/share/makefiles/simulators/Makefile.modelsim @@ -31,5 +31,4 @@ #ARCH:=i686 # Everything else is identical to Quest -include $(COCOTB_SHARE_DIR)/makefiles/simulators/Makefile.questa - +include $(shell cocotb-config --makefiles)/simulators/Makefile.questa From c4cfcd3e3faae6ea12851c0336fa92924ffce176 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Sun, 19 Apr 2020 14:31:44 +0200 Subject: [PATCH 2167/2656] Cleanup of modeslim and questa makefiles Fix indentation Remove outdated comments --- .../makefiles/simulators/Makefile.modelsim | 4 +--- .../makefiles/simulators/Makefile.questa | 19 +++++++------------ 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.modelsim b/cocotb/share/makefiles/simulators/Makefile.modelsim index 67704b92..6efd01ac 100644 --- a/cocotb/share/makefiles/simulators/Makefile.modelsim +++ b/cocotb/share/makefiles/simulators/Makefile.modelsim @@ -27,8 +27,6 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -# Modelsim is (no longer) 32-bit only -#ARCH:=i686 -# Everything else is identical to Quest +# Identical to Questa include $(shell cocotb-config --makefiles)/simulators/Makefile.questa diff --git a/cocotb/share/makefiles/simulators/Makefile.questa b/cocotb/share/makefiles/simulators/Makefile.questa index 4a62c6b6..9c1de376 100644 --- a/cocotb/share/makefiles/simulators/Makefile.questa +++ b/cocotb/share/makefiles/simulators/Makefile.questa @@ -40,9 +40,6 @@ endif ifeq (, $(CMD)) $(error "Unable to locate command >$(CMD_BIN)<") -else - MODELSIM_BIN_DIR := $(shell dirname $(CMD)) - export MODELSIM_BIN_DIR endif RTL_LIBRARY ?= work @@ -50,10 +47,11 @@ RTL_LIBRARY ?= work TOPLEVEL := "$(RTL_LIBRARY).$(TOPLEVEL)" ifndef VLOG_ARGS -VLOG_ARGS = -timescale $(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) -mfcu +acc=rmb + VLOG_ARGS = -timescale $(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) -mfcu +acc=rmb endif + ifdef VERILOG_INCLUDE_DIRS -VLOG_ARGS += +incdir+$(VERILOG_INCLUDE_DIRS) + VLOG_ARGS += +incdir+$(VERILOG_INCLUDE_DIRS) endif # below allows for maintaining legacy syntax as well as enables using cross-simulator vars COMPILE_ARGS/SIM_ARGS @@ -62,11 +60,11 @@ VCOM_ARGS += $(COMPILE_ARGS) VSIM_ARGS += $(SIM_ARGS) ifeq ($(GUI),1) -CMD += -gui -VSIM_ARGS += -onfinish stop + CMD += -gui + VSIM_ARGS += -onfinish stop else -CMD += -c -VSIM_ARGS += -onfinish exit + CMD += -c + VSIM_ARGS += -onfinish exit endif GPI_EXTRA:= @@ -129,8 +127,5 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.do $(call check_for_results_file) -# Potential fix for buffered stdout, YMMV -# STDOUT=$(SIM_BUILD)/sim.log stdbuf --output=0 $(CMD) -do runsim.do 2>&1 && stdbuf --output=0 --input=0 tail -f sim.log && exit $${PIPESTATUS[0]} - clean:: -rm -rf $(SIM_BUILD) From 3ae8c308e58da2399f5a79f427bec7c3af3fd353 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Fri, 17 Apr 2020 23:28:37 +0200 Subject: [PATCH 2168/2656] Remove not needed include of Makefile.inc --- README.md | 1 - documentation/source/quickstart.rst | 1 - examples/adder/tests/Makefile | 1 - examples/axi_lite_slave/tests/Makefile | 1 - examples/dff/tests/Makefile | 1 - examples/endian_swapper/tests/Makefile | 1 - examples/mean/tests/Makefile | 1 - examples/mixed_language/tests/Makefile | 1 - examples/ping_tun_tap/tests/Makefile | 1 - makefiles/Makefile.inc | 1 - tests/designs/array_module/Makefile | 1 - tests/designs/avalon_module/Makefile | 1 - tests/designs/avalon_streaming_module/Makefile | 1 - tests/designs/basic_hierarchy_module/Makefile | 1 - tests/designs/close_module/Makefile | 1 - tests/designs/multi_dimension_array/Makefile | 1 - tests/designs/plusargs_module/Makefile | 1 - tests/designs/sample_module/Makefile | 1 - tests/designs/uart2bus/Makefile | 1 - tests/designs/vhdl_configurations/Makefile | 1 - tests/designs/viterbi_decoder_axi4s/Makefile | 1 - tests/test_cases/test_iteration_verilog/Makefile | 1 - 22 files changed, 22 deletions(-) diff --git a/README.md b/README.md index 296e8cfd..2e5f0fa7 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,6 @@ VERILOG_SOURCES = $(shell pwd)/dff.sv TOPLEVEL = dff MODULE = test_dff -include $(shell cocotb-config --makefiles)/Makefile.inc include $(shell cocotb-config --makefiles)/Makefile.sim ``` diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index 3be4b144..427d9b7a 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -173,7 +173,6 @@ Python test script to load. # MODULE is the name of the Python test file: MODULE=test_my_design - include $(shell cocotb-config --makefiles)/Makefile.inc include $(shell cocotb-config --makefiles)/Makefile.sim We would then create a file called ``test_my_design.py`` containing our tests. diff --git a/examples/adder/tests/Makefile b/examples/adder/tests/Makefile index 3ca98be9..1865e8ca 100644 --- a/examples/adder/tests/Makefile +++ b/examples/adder/tests/Makefile @@ -44,5 +44,4 @@ endif TOPLEVEL := adder MODULE := test_adder -include $(shell cocotb-config --makefiles)/Makefile.inc include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/examples/axi_lite_slave/tests/Makefile b/examples/axi_lite_slave/tests/Makefile index e4c9c8cf..7d8f50a3 100644 --- a/examples/axi_lite_slave/tests/Makefile +++ b/examples/axi_lite_slave/tests/Makefile @@ -29,6 +29,5 @@ GPI_IMPL := vpi export TOPLEVEL_LANG MODULE=test_axi_lite_slave -include $(shell cocotb-config --makefiles)/Makefile.inc include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/examples/dff/tests/Makefile b/examples/dff/tests/Makefile index de669540..439ef338 100755 --- a/examples/dff/tests/Makefile +++ b/examples/dff/tests/Makefile @@ -30,7 +30,6 @@ ifneq ($(filter $(SIM),ius xcelium),) SIM_ARGS += -v93 endif -include $(shell cocotb-config --makefiles)/Makefile.inc include $(shell cocotb-config --makefiles)/Makefile.sim # list all required Python files here diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index bcdb9f16..6402e28d 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -57,7 +57,6 @@ ifeq ($(OS),Linux) export LD_LIBRARY_PATH endif -include $(shell cocotb-config --makefiles)/Makefile.inc include $(shell cocotb-config --makefiles)/Makefile.sim ifeq ($(OS),Linux) diff --git a/examples/mean/tests/Makefile b/examples/mean/tests/Makefile index 82e88f88..2326f8cf 100644 --- a/examples/mean/tests/Makefile +++ b/examples/mean/tests/Makefile @@ -28,7 +28,6 @@ ifneq ($(filter $(SIM),ius xcelium),) SIM_ARGS += -v93 endif -include $(shell cocotb-config --makefiles)/Makefile.inc include $(shell cocotb-config --makefiles)/Makefile.sim endif diff --git a/examples/mixed_language/tests/Makefile b/examples/mixed_language/tests/Makefile index f95d3325..740514fa 100644 --- a/examples/mixed_language/tests/Makefile +++ b/examples/mixed_language/tests/Makefile @@ -34,7 +34,6 @@ ifneq ($(filter $(SIM),ius xcelium),) SIM_ARGS += -v93 endif -include $(shell cocotb-config --makefiles)/Makefile.inc include $(shell cocotb-config --makefiles)/Makefile.sim endif diff --git a/examples/ping_tun_tap/tests/Makefile b/examples/ping_tun_tap/tests/Makefile index c7d9cef1..e5f73e42 100644 --- a/examples/ping_tun_tap/tests/Makefile +++ b/examples/ping_tun_tap/tests/Makefile @@ -43,7 +43,6 @@ PWD=$(shell pwd) VERILOG_SOURCES = $(PWD)/../hdl/icmp_reply.sv MODULE=test_icmp_reply -include $(shell cocotb-config --makefiles)/Makefile.inc include $(shell cocotb-config --makefiles)/Makefile.sim endif diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 3f9faea9..6edc9d76 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -10,6 +10,5 @@ ifeq ($(COCOTB_CONFIG_CMD),) endif $(error 'Deprecated cocotb file structure detected. \ - Use "include $$(shell cocotb-config --makefile)/Makefile.inc" instead. \ Please refer to https://cocotb.readthedocs.io/en/latest/quickstart.html#creating-a-makefile \ for instructions.') diff --git a/tests/designs/array_module/Makefile b/tests/designs/array_module/Makefile index d270d221..a340df3d 100644 --- a/tests/designs/array_module/Makefile +++ b/tests/designs/array_module/Makefile @@ -50,5 +50,4 @@ else $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") endif -include $(shell cocotb-config --makefiles)/Makefile.inc include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/tests/designs/avalon_module/Makefile b/tests/designs/avalon_module/Makefile index c12585b5..14ba94b3 100644 --- a/tests/designs/avalon_module/Makefile +++ b/tests/designs/avalon_module/Makefile @@ -45,7 +45,6 @@ COCOTB?=$(PWD)/../../.. VERILOG_SOURCES = $(COCOTB)/tests/designs/avalon_module/burst_read_master.v -include $(shell cocotb-config --makefiles)/Makefile.inc include $(shell cocotb-config --makefiles)/Makefile.sim endif diff --git a/tests/designs/avalon_streaming_module/Makefile b/tests/designs/avalon_streaming_module/Makefile index 97cb2203..d3a65683 100644 --- a/tests/designs/avalon_streaming_module/Makefile +++ b/tests/designs/avalon_streaming_module/Makefile @@ -16,7 +16,6 @@ COCOTB?=$(PWD)/../../.. VERILOG_SOURCES = $(COCOTB)/tests/designs/avalon_streaming_module/avalon_streaming.sv -include $(shell cocotb-config --makefiles)/Makefile.inc include $(shell cocotb-config --makefiles)/Makefile.sim endif diff --git a/tests/designs/basic_hierarchy_module/Makefile b/tests/designs/basic_hierarchy_module/Makefile index 27bd5136..a34df552 100644 --- a/tests/designs/basic_hierarchy_module/Makefile +++ b/tests/designs/basic_hierarchy_module/Makefile @@ -20,7 +20,6 @@ COCOTB?=$(PWD)/../../.. VERILOG_SOURCES = $(COCOTB)/tests/designs/basic_hierarchy_module/basic_hierarchy_module.v -include $(shell cocotb-config --makefiles)/Makefile.inc include $(shell cocotb-config --makefiles)/Makefile.sim endif diff --git a/tests/designs/close_module/Makefile b/tests/designs/close_module/Makefile index 0743720b..9cfff4a4 100644 --- a/tests/designs/close_module/Makefile +++ b/tests/designs/close_module/Makefile @@ -46,7 +46,6 @@ COCOTB?=$(PWD)/../../.. VERILOG_SOURCES = $(COCOTB)/tests/designs/close_module/close_module.v ifneq ($(SIM),vcs) -include $(shell cocotb-config --makefiles)/Makefile.inc include $(shell cocotb-config --makefiles)/Makefile.sim else all: diff --git a/tests/designs/multi_dimension_array/Makefile b/tests/designs/multi_dimension_array/Makefile index 12b4deb8..9efbd459 100644 --- a/tests/designs/multi_dimension_array/Makefile +++ b/tests/designs/multi_dimension_array/Makefile @@ -46,7 +46,6 @@ COCOTB?=$(PWD)/../../.. VERILOG_SOURCES = $(COCOTB)/tests/designs/multi_dimension_array/cocotb_array_pkg.sv \ $(COCOTB)/tests/designs/multi_dimension_array/cocotb_array.sv -include $(shell cocotb-config --makefiles)/Makefile.inc include $(shell cocotb-config --makefiles)/Makefile.sim endif diff --git a/tests/designs/plusargs_module/Makefile b/tests/designs/plusargs_module/Makefile index 482138a1..1d79946b 100644 --- a/tests/designs/plusargs_module/Makefile +++ b/tests/designs/plusargs_module/Makefile @@ -47,5 +47,4 @@ ifneq ($(filter $(SIM),ius xcelium),) SIM_ARGS += -v93 endif -include $(shell cocotb-config --makefiles)/Makefile.inc include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/tests/designs/sample_module/Makefile b/tests/designs/sample_module/Makefile index 8c3801dd..275400ed 100644 --- a/tests/designs/sample_module/Makefile +++ b/tests/designs/sample_module/Makefile @@ -47,5 +47,4 @@ else $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") endif -include $(shell cocotb-config --makefiles)/Makefile.inc include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/tests/designs/uart2bus/Makefile b/tests/designs/uart2bus/Makefile index cdc6d022..fc0d9606 100644 --- a/tests/designs/uart2bus/Makefile +++ b/tests/designs/uart2bus/Makefile @@ -36,7 +36,6 @@ ifeq ($(SIM),$(filter $(SIM),ius xcelium)) SIM_ARGS += -v93 endif -include $(shell cocotb-config --makefiles)/Makefile.inc include $(shell cocotb-config --makefiles)/Makefile.sim endif diff --git a/tests/designs/vhdl_configurations/Makefile b/tests/designs/vhdl_configurations/Makefile index 162767a5..af52a372 100644 --- a/tests/designs/vhdl_configurations/Makefile +++ b/tests/designs/vhdl_configurations/Makefile @@ -34,7 +34,6 @@ ifneq ($(filter $(SIM),ius xcelium),) SIM_ARGS += -v93 endif -include $(shell cocotb-config --makefiles)/Makefile.inc include $(shell cocotb-config --makefiles)/Makefile.sim endif diff --git a/tests/designs/viterbi_decoder_axi4s/Makefile b/tests/designs/viterbi_decoder_axi4s/Makefile index f15159ad..ffd49411 100644 --- a/tests/designs/viterbi_decoder_axi4s/Makefile +++ b/tests/designs/viterbi_decoder_axi4s/Makefile @@ -65,7 +65,6 @@ ifneq ($(filter $(SIM),ius xcelium),) SIM_ARGS += -v93 endif -include $(shell cocotb-config --makefiles)/Makefile.inc include $(shell cocotb-config --makefiles)/Makefile.sim endif diff --git a/tests/test_cases/test_iteration_verilog/Makefile b/tests/test_cases/test_iteration_verilog/Makefile index 9d70b585..fd4e4c60 100644 --- a/tests/test_cases/test_iteration_verilog/Makefile +++ b/tests/test_cases/test_iteration_verilog/Makefile @@ -44,7 +44,6 @@ TOPLEVEL = endian_swapper_sv MODULE = test_iteration_es -include $(shell cocotb-config --makefiles)/Makefile.inc include $(shell cocotb-config --makefiles)/Makefile.sim endif From 92647166f10d004930fa4cb975ea77724bbe529a Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sun, 19 Apr 2020 23:43:43 +0200 Subject: [PATCH 2169/2656] Update Icarus Verilog to version 10.3 See https://github.com/steveicarus/iverilog/releases --- .travis.yml | 2 +- Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4b296aa4..35301e1e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,7 +43,7 @@ jobs: python: 3.7 before_install: &travis_linx_prep - sudo apt-get -y install gperf swig - - if [[ ! -e "./iverilog/README.txt" ]]; then rm -rf iverilog; git clone https://github.com/steveicarus/iverilog.git --depth=1 --branch v10_2; fi + - if [[ ! -e "./iverilog/README.txt" ]]; then rm -rf iverilog; git clone https://github.com/steveicarus/iverilog.git --depth=1 --branch v10_3; fi - cd iverilog && autoconf && ./configure && make -j2 && sudo make install && cd .. - pip install tox - pyenv global system $TRAVIS_PYTHON_VERSION diff --git a/Dockerfile b/Dockerfile index 6f34f08a..ea0c0b51 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ FROM ubuntu:16.04 ARG MAKE_JOBS=-j2 # Simulation -ARG ICARUS_VERILOG_VERSION=10_2 +ARG ICARUS_VERILOG_VERSION=10_3 RUN apt-get -qq update && apt-get -qq install -y --no-install-recommends \ wget \ From 207d09d60ec088c26b9a9eba2b614376c33c440b Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 21 Apr 2020 09:44:53 +0200 Subject: [PATCH 2170/2656] Mention that new files should get the SPDX header (#1678) --- CONTRIBUTING.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7150ccbb..d5132a6e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -72,7 +72,12 @@ All changes which should go into the main codebase of cocotb must follow this se Use this text to discuss things which are not obvious from the code, especially *why* changes were made. Include the GitHub issue number (if one exists) in the form "Fixes #nnn" ([read more about that](https://help.github.com/articles/closing-issues-using-keywords/)). Keep each description line below 72 characters. - +- Use the following header for new files: + ```python + # Copyright cocotb contributors + # Licensed under the Revised BSD License, see LICENSE for details. + # SPDX-License-Identifier: BSD-3-Clause + ``` Managing of Issues and Pull Requests ------------------------------------ From dfaac9732e3b6b07e37c928c25bc93b7cc9e6d77 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 22 Apr 2020 21:26:28 +0200 Subject: [PATCH 2171/2656] Ignore files that CVC produces --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index f9b23872..f3d2eb66 100644 --- a/.gitignore +++ b/.gitignore @@ -88,3 +88,7 @@ tests/test_cases/*/compile examples/*/tests/library.cfg examples/*/tests/dataset.asdb examples/*/tests/compile + +# Tachyon DA CVC +tests/test_cases/*/verilog.log +examples/*/tests/verilog.log From 88a5bcb1c675062ae3dd3b7b31403c50997d1538 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 22 Apr 2020 22:03:46 +0200 Subject: [PATCH 2172/2656] Remove explicit function signatures Sphinx didn't show signatures for coroutines so they had to be added via automethod before we were using async functions. --- documentation/source/library_reference.rst | 23 +++++++++------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/documentation/source/library_reference.rst b/documentation/source/library_reference.rst index b6cca035..b9611266 100644 --- a/documentation/source/library_reference.rst +++ b/documentation/source/library_reference.rst @@ -111,13 +111,10 @@ Monitor .. currentmodule:: cocotb.monitors .. autoclass:: Monitor - :members: _monitor_recv, _recv + :members: :member-order: bysource :private-members: - .. automethod:: wait_for_recv(timeout=None) - - .. autoclass:: BusMonitor :members: :member-order: bysource @@ -230,9 +227,8 @@ Advanced Microcontroller Bus Architecture. .. currentmodule:: cocotb.drivers.amba .. autoclass:: AXI4LiteMaster - - .. automethod:: write(address, value, byte_enable=0xf, address_latency=0, data_latency=0) - .. automethod:: read(address, sync=True) + :members: + :member-order: bysource .. autoclass:: AXI4Slave @@ -251,10 +247,9 @@ Avalon :show-inheritance: .. autoclass:: AvalonMaster - - .. automethod:: write(address, value) - .. automethod:: read(address, sync=True) - + :members: + :member-order: bysource + :show-inheritance: .. autoclass:: AvalonMemory :members: @@ -278,9 +273,9 @@ OPB .. currentmodule:: cocotb.drivers.opb .. autoclass:: OPBMaster - - .. automethod:: write(address, value, sync=True) - .. automethod:: read(address, sync=True) + :members: + :member-order: bysource + :show-inheritance: XGMII From 7f07ddac362423d701b7e90f05c1c9c295e5b1e8 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 23 Apr 2020 22:36:53 +0200 Subject: [PATCH 2173/2656] Remove NVC without deprecation phase (#1694) Remove NVC without deprecation phase Closes #1693 --- cocotb/share/makefiles/Makefile.deprecations | 4 -- .../share/makefiles/simulators/Makefile.nvc | 54 ------------------- documentation/source/building.rst | 8 --- .../source/newsfragments/1495.removal.rst | 7 ++- .../source/newsfragments/1693.removal.rst | 1 + documentation/source/simulator_support.rst | 14 ----- documentation/source/spelling_wordlist.txt | 1 - 7 files changed, 4 insertions(+), 85 deletions(-) delete mode 100644 cocotb/share/makefiles/simulators/Makefile.nvc create mode 100644 documentation/source/newsfragments/1693.removal.rst diff --git a/cocotb/share/makefiles/Makefile.deprecations b/cocotb/share/makefiles/Makefile.deprecations index 5a7736a4..56d05807 100644 --- a/cocotb/share/makefiles/Makefile.deprecations +++ b/cocotb/share/makefiles/Makefile.deprecations @@ -2,10 +2,6 @@ # Licensed under the Revised BSD License, see LICENSE for details. # SPDX-License-Identifier: BSD-3-Clause -ifdef COCOTB_NVC_TRACE - $(warning COCOTB_NVC_TRACE is deprecated, see the "Simulator Support" section in the documentation.) -endif - ifdef VERILATOR_TRACE $(warning VERILATOR_TRACE is deprecated, see the "Simulator Support" section in the documentation.) endif diff --git a/cocotb/share/makefiles/simulators/Makefile.nvc b/cocotb/share/makefiles/simulators/Makefile.nvc deleted file mode 100644 index f696f265..00000000 --- a/cocotb/share/makefiles/simulators/Makefile.nvc +++ /dev/null @@ -1,54 +0,0 @@ -# -*- mode: makefile-gmake -*- - -include $(shell cocotb-config --makefiles)/Makefile.inc - -ifneq ($(VERILOG_SOURCES),) - -$(COCOTB_RESULTS_FILE): - @echo "Skipping simulation as Verilog is not supported on simulator=$(SIM)" -clean:: - -else - -ifeq ($(COCOTB_NVC_TRACE), 1) - TRACE :=--vhpi-trace -endif - -CMD_BIN := nvc - -ifdef NVC_BIN_DIR - CMD := $(shell :; command -v $(NVC_BIN_DIR)/$(CMD_BIN) 2>/dev/null) -else - # auto-detect bin dir from system path - CMD := $(shell :; command -v $(CMD_BIN) 2>/dev/null) -endif - -ifeq (, $(CMD)) - $(error "Unable to locate command >$(CMD_BIN)<") -else - NVC_BIN_DIR := $(shell dirname $(CMD)) - export NVC_BIN_DIR -endif - -RTL_LIBRARY ?= work - -.PHONY: analyse - -# Compilation phase -analyse: $(VHDL_SOURCES) $(SIM_BUILD) $(CUSTOM_COMPILE_DEPS) - cd $(SIM_BUILD) && $(CMD) --work=$(RTL_LIBRARY) -a $(VHDL_SOURCES) $(COMPILE_ARGS) $(EXTRA_ARGS) - -$(COCOTB_RESULTS_FILE): analyse $(CUSTOM_SIM_DEPS) - -@rm -f $(COCOTB_RESULTS_FILE) - - cd $(SIM_BUILD) && \ - $(CMD) --work=$(RTL_LIBRARY) -e $(TOPLEVEL) - cd $(SIM_BUILD) && MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ - $(CMD) $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) --work=$(RTL_LIBRARY) -r --load $(LIB_DIR)/libcocotbvhpi_ius.$(LIB_EXT) $(TRACE) $(TOPLEVEL) - - $(call check_for_results_file) - -clean:: - -@rm -rf $(SIM_BUILD) -endif diff --git a/documentation/source/building.rst b/documentation/source/building.rst index 8d5f92c0..e7eb3d8c 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -128,14 +128,6 @@ and Use to add additional dependencies to the simulation target. -.. make:var:: COCOTB_NVC_TRACE - - Set this to 1 to enable display of VHPI traces when using the NVC VHDL simulator. - - .. deprecated:: 1.4 - - Replaced by the instructions in :ref:`sim-nvc`. - .. make:var:: SIM_BUILD Use to define a scratch directory for use by the simulator. The path is relative to the Makefile location. diff --git a/documentation/source/newsfragments/1495.removal.rst b/documentation/source/newsfragments/1495.removal.rst index e7f18879..89e4275e 100644 --- a/documentation/source/newsfragments/1495.removal.rst +++ b/documentation/source/newsfragments/1495.removal.rst @@ -1,5 +1,4 @@ -The makefile variables -:make:var:`COCOTB_NVC_TRACE`, :make:var:`VERILATOR_TRACE` -that were not supported for all simulators have been deprecated. -Using them prints a deprecation warning and points to the documentation section +The makefile variable :make:var:`VERILATOR_TRACE` +that was not supported for all simulators has been deprecated. +Using it prints a deprecation warning and points to the documentation section :ref:`simulator-support` explaining how to get the same effect by other means. diff --git a/documentation/source/newsfragments/1693.removal.rst b/documentation/source/newsfragments/1693.removal.rst new file mode 100644 index 00000000..1fdb6eea --- /dev/null +++ b/documentation/source/newsfragments/1693.removal.rst @@ -0,0 +1 @@ +Makefile and documentation for the NVC simulator which has never worked have been removed. diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 5c0a6bfb..8f80a87d 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -250,20 +250,6 @@ Support is preliminary. Noteworthy is that despite GHDL being a VHDL simulator, it implements the VPI interface. -.. _sim-nvc: - -NVC -=== - -In order to use this simulator, set :make:var:`SIM` to ``nvc``: - -.. code-block:: bash - - make SIM=nvc - -To enable display of VHPI traces, use ``SIM_ARGS=--vhpi-trace make ...``. - - .. _sim-cvc: Tachyon DA CVC diff --git a/documentation/source/spelling_wordlist.txt b/documentation/source/spelling_wordlist.txt index 9725ce5c..ef4d53bb 100644 --- a/documentation/source/spelling_wordlist.txt +++ b/documentation/source/spelling_wordlist.txt @@ -19,7 +19,6 @@ Microsemi MinGW ModelSim Msys -NVC Questa Quickstart README From 737edca64625803b19e24dd2c9ecddca99c90d28 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 24 Apr 2020 12:40:10 +0200 Subject: [PATCH 2174/2656] Fix broken doc references (#1696) --- documentation/source/coroutines.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/source/coroutines.rst b/documentation/source/coroutines.rst index 4bd6d0df..7c2f0fca 100644 --- a/documentation/source/coroutines.rst +++ b/documentation/source/coroutines.rst @@ -1,4 +1,5 @@ .. _coroutines: +.. _async_functions: ********** Coroutines From ae6072cac6dc00f0cf4f0db693c644c87b0ed8ab Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 24 Apr 2020 07:34:36 -0500 Subject: [PATCH 2175/2656] Prevent killed coroutines from running in corner case (#1684) Add test and fix for #1348 --- cocotb/scheduler.py | 3 ++ .../test_cases/test_cocotb/test_scheduler.py | 30 ++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 7de3239c..c93c91c8 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -434,6 +434,9 @@ def _event_loop(self, trigger): trigger.unprime() for coro in scheduling: + if coro._outcome is not None: + # coroutine was killed by another coroutine waiting on the same trigger + continue if _debug: self.log.debug("Scheduling coroutine %s" % (coro.__qualname__)) self.schedule(coro, trigger=trigger) diff --git a/tests/test_cases/test_cocotb/test_scheduler.py b/tests/test_cases/test_cocotb/test_scheduler.py index d27ddda1..200c586f 100644 --- a/tests/test_cases/test_cocotb/test_scheduler.py +++ b/tests/test_cases/test_cocotb/test_scheduler.py @@ -10,7 +10,7 @@ """ import cocotb -from cocotb.triggers import Join, Timer, RisingEdge, Trigger, NullTrigger +from cocotb.triggers import Join, Timer, RisingEdge, Trigger, NullTrigger, ReadOnly from cocotb.result import TestFailure from cocotb.clock import Clock from common import clock_gen @@ -178,3 +178,31 @@ def null_coroutine(): yield null_coroutine() yield Timer(100) + + +@cocotb.test() +async def test_kill_coroutine_waiting_on_the_same_trigger(dut): + # gh-1348 + # NOTE: this test depends on scheduling priority. + # It assumes that the first task to wait on a trigger will be woken first. + # The fix for gh-1348 should prevent that from mattering. + dut.clk.setimmediatevalue(0) + + victim_resumed = False + + async def victim(): + await Timer(1) # prevent scheduling of RisingEdge before killer + await RisingEdge(dut.clk) + nonlocal victim_resumed + victim_resumed = True + victim_task = cocotb.fork(victim()) + + async def killer(): + await RisingEdge(dut.clk) + victim_task.kill() + cocotb.fork(killer()) + + await Timer(2) # allow Timer in victim to pass making it schedule RisingEdge after the killer + dut.clk <= 1 + await Timer(1) + assert not victim_resumed From a3fb4b559da411ecc863e26a84c2948bc71de67b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kruszewski?= Date: Fri, 24 Apr 2020 06:45:18 +0200 Subject: [PATCH 2176/2656] Improve Makefile.ghdl Make Makefile for GHDL work with any GHDL back end. Previously only mcode back end worked. --- cocotb/share/makefiles/simulators/Makefile.ghdl | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.ghdl b/cocotb/share/makefiles/simulators/Makefile.ghdl index 3dc3d66c..5acb9073 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ghdl +++ b/cocotb/share/makefiles/simulators/Makefile.ghdl @@ -65,17 +65,15 @@ endif # Compilation phase analyse: $(VHDL_SOURCES) $(SIM_BUILD) $(CUSTOM_COMPILE_DEPS) - cd $(SIM_BUILD) && \ $(foreach SOURCES_VAR, $(filter VHDL_SOURCES_%, $(.VARIABLES)), \ - $(CMD) -i $(GHDL_ARGS) $(COMPILE_ARGS) --work=$(SOURCES_VAR:VHDL_SOURCES_%=%) $($(SOURCES_VAR)) && ) \ - $(CMD) -i $(GHDL_ARGS) $(COMPILE_ARGS) --work=$(RTL_LIBRARY) $(VHDL_SOURCES) && \ - $(CMD) -m $(GHDL_ARGS) $(COMPILE_ARGS) --work=$(RTL_LIBRARY) $(TOPLEVEL) + $(CMD) -i $(GHDL_ARGS) $(COMPILE_ARGS) --workdir=$(SIM_BUILD) --work=$(SOURCES_VAR:VHDL_SOURCES_%=%) $($(SOURCES_VAR)) && ) \ + $(CMD) -i $(GHDL_ARGS) $(COMPILE_ARGS) --workdir=$(SIM_BUILD) --work=$(RTL_LIBRARY) $(VHDL_SOURCES) && \ + $(CMD) -m $(GHDL_ARGS) $(COMPILE_ARGS) --workdir=$(SIM_BUILD) --work=$(RTL_LIBRARY) $(TOPLEVEL) $(COCOTB_RESULTS_FILE): analyse $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) - MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ + MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ $(CMD) -r $(GHDL_ARGS) --workdir=$(SIM_BUILD) --work=$(RTL_LIBRARY) $(TOPLEVEL) --vpi=$(LIB_DIR)/libcocotbvpi_ghdl.$(LIB_EXT) $(SIM_ARGS) $(PLUSARGS) $(call check_for_results_file) From 073e36f3d0d13a1fb5c342cd19649052a2fd2689 Mon Sep 17 00:00:00 2001 From: Mehul Tikekar Date: Sun, 26 Apr 2020 05:16:09 +0530 Subject: [PATCH 2177/2656] Time unit and precision in Icarus It is possible to set timescale through a command file even though a command-line argument does not exist --- cocotb/share/makefiles/simulators/Makefile.icarus | 3 ++- documentation/source/simulator_support.rst | 8 -------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.icarus b/cocotb/share/makefiles/simulators/Makefile.icarus index 75438164..4edb24a0 100644 --- a/cocotb/share/makefiles/simulators/Makefile.icarus +++ b/cocotb/share/makefiles/simulators/Makefile.icarus @@ -54,10 +54,11 @@ else export ICARUS_BIN_DIR endif -COMPILE_ARGS += -g2012 # Default to latest SystemVerilog standard +COMPILE_ARGS += -f $(SIM_BUILD)/cmds.f -g2012 # Default to latest SystemVerilog standard # Compilation phase $(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) + @echo "+timescale+$(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION)" > $(SIM_BUILD)/cmds.f $(CMD) -o $(SIM_BUILD)/sim.vvp -D COCOTB_SIM=1 -s $(TOPLEVEL) $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) # Execution phase diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 8f80a87d..ea26b19b 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -59,14 +59,6 @@ to the top component as shown in the example below: `endif endmodule -.. _sim-icarus-time: - -Time unit and precision ------------------------ - -Setting the time unit and time precision is not possible from the command-line, -and therefore the make variables :make:var:`COCOTB_HDL_TIMEUNIT` and :make:var:`COCOTB_HDL_TIMEPRECISION` are ignored. - .. _sim-verilator: From a797fcb086f032eeb12b05172ad94482a54b729f Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Sun, 26 Apr 2020 20:56:30 +0200 Subject: [PATCH 2178/2656] Change documentation on download of examples (#1712) Change documentation for examples download --- documentation/source/quickstart.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index 427d9b7a..09758dc3 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -117,12 +117,18 @@ To install the development version of cocotb: Running your first Example ========================== +Download and extract the cocotb source files according to the version you are using from +https://github.com/cocotb/cocotb/releases +You can check your cocotb version with ``cocotb-config --version``. + +The sources for cocotb's development version are available from +https://github.com/cocotb/cocotb/archive/master.zip + Assuming you have installed the prerequisites as above, the following lines are all you need to run a first simulation with cocotb: .. code-block:: bash - git clone https://github.com/cocotb/cocotb cd cocotb/examples/endian_swapper/tests make From 5ea7ab965ac7c21d56157d525b105706c3cdd5c3 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 8 Apr 2020 20:13:28 -0500 Subject: [PATCH 2179/2656] Merge RegressionManager.initialize with constructor --- cocotb/__init__.py | 1 - cocotb/regression.py | 3 --- 2 files changed, 4 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 6dba6678..84f6eea9 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -178,7 +178,6 @@ def _initialise_testbench(root_name): global regression_manager regression_manager = RegressionManager(root_name, modules, tests=test_str, seed=RANDOM_SEED, hooks=hooks) - regression_manager.initialise() regression_manager.execute() _rlock.release() diff --git a/cocotb/regression.py b/cocotb/regression.py index 64489897..2861bdf7 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -97,9 +97,6 @@ def __init__(self, root_name, modules, tests=None, seed=None, hooks=[]): self.log = SimLog("cocotb.regression") self._seed = seed self._hooks = hooks - - def initialise(self): - self.start_time = time.time() self.test_results = [] self.ntests = 0 From 5d30d255174868ada744ff7651ca096c6c9eee99 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 8 Apr 2020 21:10:48 -0500 Subject: [PATCH 2180/2656] Move test and hook discovery to regression manager * moved MODULE, TESTCASE, and SEED handling to RM and removed from constructor * split test and hook discovery from __init__ into seperate functions * split hook initialization behavior into separate function * necessary cleanup --- cocotb/__init__.py | 13 +----- cocotb/regression.py | 102 +++++++++++++++++++++++-------------------- 2 files changed, 55 insertions(+), 60 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 84f6eea9..30db4362 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -165,19 +165,8 @@ def _initialise_testbench(root_name): log.info("Seeding Python random module with supplied seed %d" % (RANDOM_SEED)) random.seed(RANDOM_SEED) - module_str = os.getenv('MODULE') - test_str = os.getenv('TESTCASE') - hooks_str = os.getenv('COCOTB_HOOKS', '') - - if module_str is None: - raise ValueError("Environment variable MODULE, which defines the module(s) to execute, is not defined.") - - modules = [s.strip() for s in module_str.split(',') if s.strip()] - hooks = [s.strip() for s in hooks_str.split(',') if s.strip()] - global regression_manager - - regression_manager = RegressionManager(root_name, modules, tests=test_str, seed=RANDOM_SEED, hooks=hooks) + regression_manager = RegressionManager(root_name) regression_manager.execute() _rlock.release() diff --git a/cocotb/regression.py b/cocotb/regression.py index 2861bdf7..f394cbdf 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -75,28 +75,17 @@ def _my_import(name): class RegressionManager: """Encapsulates all regression capability into a single place""" - def __init__(self, root_name, modules, tests=None, seed=None, hooks=[]): + def __init__(self, root_name): """ Args: root_name (str): The name of the root handle. - modules (list): A list of Python module names to run. - tests (list, optional): A list of tests to run. - Defaults to ``None``, meaning all discovered tests will be run. - seed (int, optional): The seed for the random number generator to use. - Defaults to ``None``. - hooks (list, optional): A list of hook modules to import. - Defaults to the empty list. """ self._queue = [] self._root_name = root_name self._dut = None - self._modules = modules - self._functions = tests self._running_test = None self._cov = None self.log = SimLog("cocotb.regression") - self._seed = seed - self._hooks = hooks self.start_time = time.time() self.test_results = [] self.ntests = 0 @@ -113,11 +102,9 @@ def __init__(self, root_name, modules, tests=None, seed=None, hooks=[]): self.xunit = XUnitReporter(filename=results_filename) - self.xunit.add_testsuite(name=suite_name, tests=repr(self.ntests), - package=package_name) + self.xunit.add_testsuite(name=suite_name, package=package_name) - if (self._seed is not None): - self.xunit.add_property(name="random_seed", value=("%d" % self._seed)) + self.xunit.add_property(name="random_seed", value=str(cocotb.RANDOM_SEED)) # Setup Coverage #################### @@ -140,36 +127,61 @@ def __init__(self, root_name, modules, tests=None, seed=None, hooks=[]): # Test Discovery #################### - have_tests = False - for module_name in self._modules: + for test in self.discover_tests(): + self.log.info("Found test {}.{}".format(test.__module__, test.__qualname__)) + self._init_test(test) + have_tests = True + + if not have_tests: + self.log.warning("No tests were discovered") + + self._queue.sort(key=lambda test: (test.stage, test._id)) + + # Process Hooks + ################### + for hook in self.discover_hooks(): + self.log.info("Found hook {}.{}".format(hook.__module__, hook.__qualname__)) + self._init_hook(hook) + + def discover_tests(self): + + module_str = os.getenv('MODULE') + test_str = os.getenv('TESTCASE') + + if module_str is None: + raise ValueError("Environment variable MODULE, which defines the module(s) to execute, is not defined.") + + modules = [s.strip() for s in module_str.split(',') if s.strip()] + + for module_name in modules: try: self.log.debug("Python Path: " + ",".join(sys.path)) self.log.debug("PWD: " + os.getcwd()) module = _my_import(module_name) except Exception as E: self.log.critical("Failed to import module %s: %s", module_name, E) - self.log.info("MODULE variable was \"%s\"", ".".join(self._modules)) + self.log.info("MODULE variable was \"%s\"", ".".join(modules)) self.log.info("Traceback: ") self.log.info(traceback.format_exc()) raise - if self._functions: + if test_str: # Specific functions specified, don't auto-discover - for test in self._functions.rsplit(','): + for test_name in test_str.rsplit(','): try: - _test = getattr(module, test) + test = getattr(module, test_name) except AttributeError: - self.log.error("Requested test %s wasn't found in module %s", test, module_name) - err = AttributeError("Test %s doesn't exist in %s" % (test, module_name)) + self.log.error("Requested test %s wasn't found in module %s", test_name, module_name) + err = AttributeError("Test %s doesn't exist in %s" % (test_name, module_name)) raise err from None # discard nested traceback - if not hasattr(_test, "im_test"): + if not hasattr(test, "im_test"): self.log.error("Requested %s from module %s isn't a cocotb.test decorated coroutine", - test, module_name) - raise ImportError("Failed to find requested test %s" % test) - self._init_test(_test) + test_name, module_name) + raise ImportError("Failed to find requested test %s" % test_name) + yield test # only look in first module for all functions and don't complain if all functions are not found break @@ -177,34 +189,28 @@ def __init__(self, root_name, modules, tests=None, seed=None, hooks=[]): # auto-discover for thing in vars(module).values(): if hasattr(thing, "im_test"): - self._init_test(thing) - have_tests = True + yield thing - if not have_tests: - self.log.warning("No tests were discovered") + def discover_hooks(self): - self._queue.sort(key=lambda test: (test.stage, test._id)) + hooks_str = os.getenv('COCOTB_HOOKS', '') + hooks = [s.strip() for s in hooks_str.split(',') if s.strip()] - for valid_tests in self._queue: - self.log.info("Found test %s.%s" % - (valid_tests.module, - valid_tests.funcname)) - - # Process Hooks - ################### - - for module_name in self._hooks: + for module_name in hooks: self.log.info("Loading hook from module '" + module_name + "'") module = _my_import(module_name) for thing in vars(module).values(): if hasattr(thing, "im_hook"): - try: - test = thing(self._dut) - except Exception: - self.log.warning("Failed to initialize hook %s" % thing.name, exc_info=True) - else: - cocotb.scheduler.add(test) + yield thing + + def _init_hook(self, hook): + try: + test = hook(self._dut) + except Exception: + self.log.warning("Failed to initialize hook %s" % hook.name, exc_info=True) + else: + return cocotb.scheduler.add(test) def tear_down(self): # fail remaining tests From 000566faf6652854cb273b6c998a08cf5f26e3de Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 12 Apr 2020 20:38:19 -0500 Subject: [PATCH 2181/2656] Change RegressionManager to save test function and defer test init Previously, test initialization was done during RM initialization, and only initialized tests were saved in the object. This required that the test coroutine be a special case of a `RunningTask` that duplicate the test object's interface for interchangability. Of course, this duplication was very sloppy, but now we can get rid of it. Now, we store all discovered tests, regardless if they are skipped or not, in the test queue. The main `execute` loop now does test initialization and checks for skipped tests. For every test we save both the test function and the `RunningTask` object locally. We have also updated all locations that previously used the `RunningTask` object to obtain test information to use the save test function instead. --- cocotb/regression.py | 82 +++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index f394cbdf..f73d4c94 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -80,7 +80,6 @@ def __init__(self, root_name): Args: root_name (str): The name of the root handle. """ - self._queue = [] self._root_name = root_name self._dut = None self._running_test = None @@ -88,8 +87,7 @@ def __init__(self, root_name): self.log = SimLog("cocotb.regression") self.start_time = time.time() self.test_results = [] - self.ntests = 0 - self.count = 1 + self.count = 0 self.skipped = 0 self.failures = 0 @@ -127,13 +125,13 @@ def __init__(self, root_name): # Test Discovery #################### - have_tests = False + self._queue = [] for test in self.discover_tests(): self.log.info("Found test {}.{}".format(test.__module__, test.__qualname__)) - self._init_test(test) - have_tests = True + self._queue.append(test) + self.ntests = len(self._queue) - if not have_tests: + if not self._queue: self.log.warning("No tests were discovered") self._queue.sort(key=lambda test: (test.stage, test._id)) @@ -218,13 +216,13 @@ def tear_down(self): test = self.next_test() if test is None: break - self.xunit.add_testcase(name=test.funcname, - classname=test.module, + self.xunit.add_testcase(name=test.__qualname__, + classname=test.__module__, time=repr(0), sim_time_ns=repr(0), ratio_time=repr(0)) result_pass, _ = self._score_test(test, cocotb.outcomes.Error(SimFailure())) - self._store_test_result(test.module, test.funcname, result_pass, 0, 0, 0) + self._store_test_result(test.__module__, test.__qualname__, result_pass, 0, 0, 0) if not result_pass: self.xunit.add_failure() self.failures += 1 @@ -232,10 +230,10 @@ def tear_down(self): # Write out final log messages if self.failures: self.log.error("Failed %d out of %d tests (%d skipped)" % - (self.failures, self.count - 1, self.skipped)) + (self.failures, self.count, self.skipped)) else: self.log.info("Passed %d tests (%d skipped)" % - (self.count - 1, self.skipped)) + (self.count, self.skipped)) if len(self.test_results) > 0: self._log_test_summary() self._log_sim_summary() @@ -256,6 +254,7 @@ def next_test(self): """Get the next test to run""" if not self._queue: return None + self.count += 1 return self._queue.pop(0) def handle_result(self, test): @@ -272,20 +271,21 @@ def handle_result(self, test): sim_time_ns = get_sim_time('ns') - test.start_sim_time ratio_time = self._safe_divide(sim_time_ns, real_time) - self.xunit.add_testcase(name=test.funcname, - classname=test.module, + self.xunit.add_testcase(name=self._test.__qualname__, + classname=self._test.__module__, time=repr(real_time), sim_time_ns=repr(sim_time_ns), ratio_time=repr(ratio_time)) # score test - result_pass, sim_failed = self._score_test(test, test._outcome) + result_pass, sim_failed = self._score_test(self._test, test._outcome) # stop capturing log output cocotb.log.removeHandler(test.handler) # Save results - self._store_test_result(test.module, test.funcname, result_pass, sim_time_ns, real_time, ratio_time) + self._store_test_result(self._test.__module__, self._test.__qualname__, result_pass, + sim_time_ns, real_time, ratio_time) if not result_pass: self.xunit.add_failure() self.failures += 1 @@ -338,8 +338,7 @@ def _init_test(self, test_func): self.skipped += 1 self._store_test_result(test.module, test_func.name, None, 0.0, 0.0, 0.0) else: - self._queue.append(test) - self.ntests += 1 + return test def _score_test(self, test, outcome): """ @@ -410,27 +409,32 @@ def _result_was(): return result_pass, sim_failed def execute(self): - self._running_test = cocotb.regression_manager.next_test() - if self._running_test: - start = '' - end = '' - if want_color_output(): - start = ANSI.COLOR_TEST - end = ANSI.COLOR_DEFAULT - # Want this to stand out a little bit - self.log.info("%sRunning test %d/%d:%s %s" % - (start, - self.count, self.ntests, - end, - self._running_test.funcname)) - - # start capturing log output - cocotb.log.addHandler(self._running_test.handler) - - cocotb.scheduler.add_test(self._running_test) - self.count += 1 - else: - self.tear_down() + while True: + self._test = self.next_test() + if self._test is None: + return self.tear_down() + + self._running_test = self._init_test(self._test) + if self._running_test: + return self._start_test() + + def _start_test(self): + start = '' + end = '' + if want_color_output(): + start = ANSI.COLOR_TEST + end = ANSI.COLOR_DEFAULT + # Want this to stand out a little bit + self.log.info("%sRunning test %d/%d:%s %s" % + (start, + self.count, self.ntests, + end, + self._test.__qualname__)) + + # start capturing log output + cocotb.log.addHandler(self._running_test.handler) + + cocotb.scheduler.add_test(self._running_test) def _log_test_summary(self): TEST_FIELD = 'TEST' From bf8594b3ef8ef1f2f2467bbec995943f12f4c7d3 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 19 Apr 2020 11:55:42 -0500 Subject: [PATCH 2182/2656] Make skipping tests have same highlighting as running --- cocotb/regression.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index f73d4c94..1b710a32 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -328,7 +328,15 @@ def _init_test(self, test_func): else: test = test_init_outcome.get() if test.skip: - self.log.info("Skipping test %s" % test_func.name) + hilight_start = ANSI.COLOR_TEST if want_color_output() else '' + hilight_end = ANSI.COLOR_DEFAULT if want_color_output() else '' + # Want this to stand out a little bit + self.log.info("{}Skipping test {}/{}:{} {}".format( + hilight_start, + self.count, + self.ntests, + hilight_end, + test_func.__qualname__)) self.xunit.add_testcase(name=test_func.name, classname=test.module, time="0.0", From 687521d6dcd81d350b16202e1e933811b96f78d3 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 12 Apr 2020 21:04:55 -0500 Subject: [PATCH 2183/2656] Add type annotations to RegressionManager --- cocotb/regression.py | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 1b710a32..0c7c7b46 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -34,6 +34,7 @@ import os import traceback import pdb +from typing import Any, Optional, Tuple, Iterable if "COCOTB_PDB_ON_EXCEPTION" in os.environ: _pdb_on_exception = True @@ -62,9 +63,11 @@ from cocotb.result import TestSuccess, SimFailure from cocotb.utils import get_sim_time, remove_traceback_frames, want_color_output from cocotb.xunit_reporter import XUnitReporter +from cocotb.decorators import test as Test, hook as Hook, RunningTask +from cocotb.outcomes import Outcome -def _my_import(name): +def _my_import(name: str) -> Any: mod = __import__(name) components = name.split('.') for comp in components[1:]: @@ -75,7 +78,7 @@ def _my_import(name): class RegressionManager: """Encapsulates all regression capability into a single place""" - def __init__(self, root_name): + def __init__(self, root_name: str): """ Args: root_name (str): The name of the root handle. @@ -142,7 +145,7 @@ def __init__(self, root_name): self.log.info("Found hook {}.{}".format(hook.__module__, hook.__qualname__)) self._init_hook(hook) - def discover_tests(self): + def discover_tests(self) -> Iterable[Test]: module_str = os.getenv('MODULE') test_str = os.getenv('TESTCASE') @@ -189,7 +192,7 @@ def discover_tests(self): if hasattr(thing, "im_test"): yield thing - def discover_hooks(self): + def discover_hooks(self) -> Iterable[Hook]: hooks_str = os.getenv('COCOTB_HOOKS', '') hooks = [s.strip() for s in hooks_str.split(',') if s.strip()] @@ -202,7 +205,7 @@ def discover_hooks(self): if hasattr(thing, "im_hook"): yield thing - def _init_hook(self, hook): + def _init_hook(self, hook: Hook) -> Optional[RunningTask]: try: test = hook(self._dut) except Exception: @@ -210,7 +213,7 @@ def _init_hook(self, hook): else: return cocotb.scheduler.add(test) - def tear_down(self): + def tear_down(self) -> None: # fail remaining tests while True: test = self.next_test() @@ -250,14 +253,14 @@ def tear_down(self): # Setup simulator finalization simulator.stop_simulator() - def next_test(self): + def next_test(self) -> Optional[Test]: """Get the next test to run""" if not self._queue: return None self.count += 1 return self._queue.pop(0) - def handle_result(self, test): + def handle_result(self, test: RunningTask) -> None: """Handle a test completing. Dump result to XML and schedule the next test (if any). Entered by the scheduler. @@ -297,7 +300,7 @@ def handle_result(self, test): self.execute() - def _init_test(self, test_func): + def _init_test(self, test_func: Test) -> Optional[RunningTask]: """ Initializes a test. @@ -348,7 +351,7 @@ def _init_test(self, test_func): else: return test - def _score_test(self, test, outcome): + def _score_test(self, test: Test, outcome: Outcome) -> Tuple[bool, bool]: """ Given a test and the test's outcome, determine if the test met expectations and log pertinent information """ @@ -416,7 +419,7 @@ def _result_was(): return result_pass, sim_failed - def execute(self): + def execute(self) -> None: while True: self._test = self.next_test() if self._test is None: @@ -426,7 +429,7 @@ def execute(self): if self._running_test: return self._start_test() - def _start_test(self): + def _start_test(self) -> None: start = '' end = '' if want_color_output(): @@ -444,7 +447,7 @@ def _start_test(self): cocotb.scheduler.add_test(self._running_test) - def _log_test_summary(self): + def _log_test_summary(self) -> None: TEST_FIELD = 'TEST' RESULT_FIELD = 'PASS/FAIL' SIM_FIELD = 'SIM TIME(NS)' @@ -491,7 +494,7 @@ def _log_test_summary(self): self.log.info(summary) - def _log_sim_summary(self): + def _log_sim_summary(self) -> None: real_time = time.time() - self.start_time sim_time_ns = get_sim_time('ns') ratio_time = self._safe_divide(sim_time_ns, real_time) @@ -509,7 +512,7 @@ def _log_sim_summary(self): self.log.info(summary) @staticmethod - def _safe_divide(a, b): + def _safe_divide(a: float, b: float) -> float: try: return a / b except ZeroDivisionError: @@ -518,7 +521,15 @@ def _safe_divide(a, b): else: return float('inf') - def _store_test_result(self, module_name, test_name, result_pass, sim_time, real_time, ratio): + def _store_test_result( + self, + module_name: str, + test_name: str, + result_pass: bool, + sim_time: float, + real_time: float, + ratio: float + ) -> None: result = { 'test' : '.'.join([module_name, test_name]), 'pass' : result_pass, From d4f11189cf0fd8eb5bd3120feda1fdec35e5926d Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 12 Apr 2020 21:41:34 -0500 Subject: [PATCH 2184/2656] Move dut object creation from RegressionManager to __init__ --- cocotb/__init__.py | 12 +++++++++++- cocotb/regression.py | 19 ++++--------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 30db4362..6d345a5f 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -165,8 +165,18 @@ def _initialise_testbench(root_name): log.info("Seeding Python random module with supplied seed %d" % (RANDOM_SEED)) random.seed(RANDOM_SEED) + # Setup DUT object + from cocotb import simulator + + handle = simulator.get_root_handle(root_name) + if not handle: + raise RuntimeError("Can not find root handle ({})".format(root_name)) + + dut = cocotb.handle.SimHandle(handle) + + # start Regression Manager global regression_manager - regression_manager = RegressionManager(root_name) + regression_manager = RegressionManager(dut) regression_manager.execute() _rlock.release() diff --git a/cocotb/regression.py b/cocotb/regression.py index 0c7c7b46..d767c7c6 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -65,6 +65,7 @@ from cocotb.xunit_reporter import XUnitReporter from cocotb.decorators import test as Test, hook as Hook, RunningTask from cocotb.outcomes import Outcome +from cocotb.handle import SimHandle def _my_import(name: str) -> Any: @@ -78,13 +79,12 @@ def _my_import(name: str) -> Any: class RegressionManager: """Encapsulates all regression capability into a single place""" - def __init__(self, root_name: str): + def __init__(self, dut: SimHandle): """ Args: - root_name (str): The name of the root handle. + dut (SimHandle): The root handle to pass into test functions. """ - self._root_name = root_name - self._dut = None + self._dut = dut self._running_test = None self._cov = None self.log = SimLog("cocotb.regression") @@ -115,17 +115,6 @@ def __init__(self, root_name: str): self._cov = coverage.coverage(branch=True, omit=["*cocotb*"]) self._cov.start() - # Setup DUT object - ####################### - - handle = simulator.get_root_handle(self._root_name) - - self._dut = cocotb.handle.SimHandle(handle) if handle else None - - if self._dut is None: - raise AttributeError("Can not find Root Handle (%s)" % - self._root_name) - # Test Discovery #################### self._queue = [] From 96c169ae7f21fba8608065fb230e646f53c5fe95 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 12 Apr 2020 22:36:14 -0500 Subject: [PATCH 2185/2656] Move test timing to RegressionManager --- cocotb/regression.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index d767c7c6..d1a6d007 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -259,8 +259,8 @@ def handle_result(self, test: RunningTask) -> None: """ assert test is self._running_test - real_time = time.time() - test.start_time - sim_time_ns = get_sim_time('ns') - test.start_sim_time + real_time = time.time() - self._test_start_time + sim_time_ns = get_sim_time('ns') - self._test_start_sim_time ratio_time = self._safe_divide(sim_time_ns, real_time) self.xunit.add_testcase(name=self._test.__qualname__, @@ -434,6 +434,8 @@ def _start_test(self) -> None: # start capturing log output cocotb.log.addHandler(self._running_test.handler) + self._test_start_time = time.time() + self._test_start_sim_time = get_sim_time('ns') cocotb.scheduler.add_test(self._running_test) def _log_test_summary(self) -> None: From f8179c23579e9bc034a83bab5d0180453ba8456b Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 17 Apr 2020 16:41:54 -0500 Subject: [PATCH 2186/2656] Centralize test result reporting in regression manager --- cocotb/regression.py | 179 ++++++++++++++++++++----------------------- 1 file changed, 82 insertions(+), 97 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index d1a6d007..21765b92 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -64,7 +64,7 @@ from cocotb.utils import get_sim_time, remove_traceback_frames, want_color_output from cocotb.xunit_reporter import XUnitReporter from cocotb.decorators import test as Test, hook as Hook, RunningTask -from cocotb.outcomes import Outcome +from cocotb.outcomes import Outcome, Error from cocotb.handle import SimHandle @@ -208,26 +208,14 @@ def tear_down(self) -> None: test = self.next_test() if test is None: break - self.xunit.add_testcase(name=test.__qualname__, - classname=test.__module__, - time=repr(0), - sim_time_ns=repr(0), - ratio_time=repr(0)) - result_pass, _ = self._score_test(test, cocotb.outcomes.Error(SimFailure())) - self._store_test_result(test.__module__, test.__qualname__, result_pass, 0, 0, 0) - if not result_pass: - self.xunit.add_failure() - self.failures += 1 + self._record_result( + test=test, + outcome=Error(SimFailure), + wall_time_s=0, + sim_time_ns=0) # Write out final log messages - if self.failures: - self.log.error("Failed %d out of %d tests (%d skipped)" % - (self.failures, self.count, self.skipped)) - else: - self.log.info("Passed %d tests (%d skipped)" % - (self.count, self.skipped)) - if len(self.test_results) > 0: - self._log_test_summary() + self._log_test_summary() self._log_sim_summary() self.log.info("Shutting down...") @@ -261,35 +249,19 @@ def handle_result(self, test: RunningTask) -> None: real_time = time.time() - self._test_start_time sim_time_ns = get_sim_time('ns') - self._test_start_sim_time - ratio_time = self._safe_divide(sim_time_ns, real_time) - - self.xunit.add_testcase(name=self._test.__qualname__, - classname=self._test.__module__, - time=repr(real_time), - sim_time_ns=repr(sim_time_ns), - ratio_time=repr(ratio_time)) - - # score test - result_pass, sim_failed = self._score_test(self._test, test._outcome) # stop capturing log output cocotb.log.removeHandler(test.handler) - # Save results - self._store_test_result(self._test.__module__, self._test.__qualname__, result_pass, - sim_time_ns, real_time, ratio_time) - if not result_pass: - self.xunit.add_failure() - self.failures += 1 - - # Fail if required - if sim_failed: - self.tear_down() - return + self._record_result( + test=self._test, + outcome=self._running_test._outcome, + wall_time_s=real_time, + sim_time_ns=sim_time_ns) self.execute() - def _init_test(self, test_func: Test) -> Optional[RunningTask]: + def _init_test(self, test: Test) -> Optional[RunningTask]: """ Initializes a test. @@ -297,48 +269,29 @@ def _init_test(self, test_func: Test) -> Optional[RunningTask]: Records skip if the test is skipped. Saves the initialized test if it successfully initializes. """ - test_init_outcome = cocotb.outcomes.capture(test_func, self._dut) + + if test.skip: + hilight_start = ANSI.COLOR_TEST if want_color_output() else '' + hilight_end = ANSI.COLOR_DEFAULT if want_color_output() else '' + # Want this to stand out a little bit + self.log.info("{}Skipping test {}/{}:{} {}".format( + hilight_start, + self.count, + self.ntests, + hilight_end, + test.__qualname__)) + self._record_result(test, None, 0, 0) + return + + test_init_outcome = cocotb.outcomes.capture(test, self._dut) if isinstance(test_init_outcome, cocotb.outcomes.Error): - self.log.error("Failed to initialize test %s" % test_func.name, exc_info=True) - self.xunit.add_testcase(name=test_func.name, - classname=test_func.__module__, - time="0.0", - sim_time_ns="0.0", - ratio_time="0.0") - result_pass, sim_failed = self._score_test(test_func, test_init_outcome) - # Save results - self._store_test_result(test_func.__module__, test_func.__qualname__, result_pass, 0.0, 0.0, 0.0) - if not result_pass: - self.xunit.add_failure() - self.failures += 1 - # Fail if required - if sim_failed: - self.tear_down() - raise SimFailure("Test initialization caused a simulator failure. Shutting down.") + self.log.error("Failed to initialize test %s" % test.__qualname__, exc_info=True) + self._record_result(test, test_init_outcome, 0, 0) + return - else: - test = test_init_outcome.get() - if test.skip: - hilight_start = ANSI.COLOR_TEST if want_color_output() else '' - hilight_end = ANSI.COLOR_DEFAULT if want_color_output() else '' - # Want this to stand out a little bit - self.log.info("{}Skipping test {}/{}:{} {}".format( - hilight_start, - self.count, - self.ntests, - hilight_end, - test_func.__qualname__)) - self.xunit.add_testcase(name=test_func.name, - classname=test.module, - time="0.0", - sim_time_ns="0.0", - ratio_time="0.0") - self.xunit.add_skipped() - self.skipped += 1 - self._store_test_result(test.module, test_func.name, None, 0.0, 0.0, 0.0) - else: - return test + test = test_init_outcome.get() + return test def _score_test(self, test: Test, outcome: Outcome) -> Tuple[bool, bool]: """ @@ -408,6 +361,44 @@ def _result_was(): return result_pass, sim_failed + def _record_result( + self, + test: Test, + outcome: Optional[Outcome], + wall_time_s: float, + sim_time_ns: float + ) -> None: + + ratio_time = self._safe_divide(sim_time_ns, wall_time_s) + + self.xunit.add_testcase(name=test.__qualname__, + classname=test.__module__, + time=repr(wall_time_s), + sim_time_ns=repr(sim_time_ns), + ratio_time=repr(ratio_time)) + + if outcome is None: # skipped + test_pass, sim_failed = None, False + self.xunit.add_skipped() + self.skipped += 1 + + else: + test_pass, sim_failed = self._score_test(test, outcome) + if not test_pass: + self.xunit.add_failure() + self.failures += 1 + + self.test_results.append({ + 'test': '.'.join([test.__module__, test.__qualname__]), + 'pass': test_pass, + 'sim': sim_time_ns, + 'real': wall_time_s, + 'ratio': ratio_time}) + + if sim_failed: + self.tear_down() + return + def execute(self) -> None: while True: self._test = self.next_test() @@ -439,6 +430,17 @@ def _start_test(self) -> None: cocotb.scheduler.add_test(self._running_test) def _log_test_summary(self) -> None: + + if self.failures: + self.log.error("Failed %d out of %d tests (%d skipped)" % + (self.failures, self.count, self.skipped)) + else: + self.log.info("Passed %d tests (%d skipped)" % + (self.count, self.skipped)) + + if len(self.test_results) == 0: + return + TEST_FIELD = 'TEST' RESULT_FIELD = 'PASS/FAIL' SIM_FIELD = 'SIM TIME(NS)' @@ -512,23 +514,6 @@ def _safe_divide(a: float, b: float) -> float: else: return float('inf') - def _store_test_result( - self, - module_name: str, - test_name: str, - result_pass: bool, - sim_time: float, - real_time: float, - ratio: float - ) -> None: - result = { - 'test' : '.'.join([module_name, test_name]), - 'pass' : result_pass, - 'sim' : sim_time, - 'real' : real_time, - 'ratio' : ratio} - self.test_results.append(result) - def _create_test(function, name, documentation, mod, *args, **kwargs): """Factory function to create tests, avoids late binding. From 21ca5bccbe5c7404480341cab740360bc4fcd275 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 19 Apr 2020 10:08:15 -0500 Subject: [PATCH 2187/2656] PEP8 cleanup in RegressionManager --- cocotb/regression.py | 94 +++++++++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 37 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 21765b92..8949ad28 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -36,6 +36,16 @@ import pdb from typing import Any, Optional, Tuple, Iterable +import cocotb +import cocotb.ANSI as ANSI +from cocotb.log import SimLog +from cocotb.result import TestSuccess, SimFailure +from cocotb.utils import get_sim_time, remove_traceback_frames, want_color_output +from cocotb.xunit_reporter import XUnitReporter +from cocotb.decorators import test as Test, hook as Hook, RunningTask +from cocotb.outcomes import Outcome, Error +from cocotb.handle import SimHandle + if "COCOTB_PDB_ON_EXCEPTION" in os.environ: _pdb_on_exception = True else: @@ -57,16 +67,6 @@ "Import error was: %s\n" % repr(e)) sys.stderr.write(msg) -import cocotb -import cocotb.ANSI as ANSI -from cocotb.log import SimLog -from cocotb.result import TestSuccess, SimFailure -from cocotb.utils import get_sim_time, remove_traceback_frames, want_color_output -from cocotb.xunit_reporter import XUnitReporter -from cocotb.decorators import test as Test, hook as Hook, RunningTask -from cocotb.outcomes import Outcome, Error -from cocotb.handle import SimHandle - def _my_import(name: str) -> Any: mod = __import__(name) @@ -411,10 +411,10 @@ def execute(self) -> None: def _start_test(self) -> None: start = '' - end = '' + end = '' if want_color_output(): start = ANSI.COLOR_TEST - end = ANSI.COLOR_DEFAULT + end = ANSI.COLOR_DEFAULT # Want this to stand out a little bit self.log.info("%sRunning test %d/%d:%s %s" % (start, @@ -441,30 +441,41 @@ def _log_test_summary(self) -> None: if len(self.test_results) == 0: return - TEST_FIELD = 'TEST' + TEST_FIELD = 'TEST' RESULT_FIELD = 'PASS/FAIL' - SIM_FIELD = 'SIM TIME(NS)' - REAL_FIELD = 'REAL TIME(S)' - RATIO_FIELD = 'RATIO(NS/S)' + SIM_FIELD = 'SIM TIME(NS)' + REAL_FIELD = 'REAL TIME(S)' + RATIO_FIELD = 'RATIO(NS/S)' - TEST_FIELD_LEN = max(len(TEST_FIELD),len(max([x['test'] for x in self.test_results],key=len))) + TEST_FIELD_LEN = max(len(TEST_FIELD), len(max([x['test'] for x in self.test_results], key=len))) RESULT_FIELD_LEN = len(RESULT_FIELD) - SIM_FIELD_LEN = len(SIM_FIELD) - REAL_FIELD_LEN = len(REAL_FIELD) - RATIO_FIELD_LEN = len(RATIO_FIELD) - - LINE_LEN = 3 + TEST_FIELD_LEN + 2 + RESULT_FIELD_LEN + 2 + SIM_FIELD_LEN + 2 + REAL_FIELD_LEN + 2 + RATIO_FIELD_LEN + 3 - - LINE_SEP = "*"*LINE_LEN+"\n" + SIM_FIELD_LEN = len(SIM_FIELD) + REAL_FIELD_LEN = len(REAL_FIELD) + RATIO_FIELD_LEN = len(RATIO_FIELD) + + header_dict = dict( + a=TEST_FIELD, + b=RESULT_FIELD, + c=SIM_FIELD, + d=REAL_FIELD, + e=RATIO_FIELD, + a_len=TEST_FIELD_LEN, + b_len=RESULT_FIELD_LEN, + c_len=SIM_FIELD_LEN, + d_len=REAL_FIELD_LEN, + e_len=RATIO_FIELD_LEN) + + LINE_LEN = 3 + TEST_FIELD_LEN + 2 + RESULT_FIELD_LEN + 2 + SIM_FIELD_LEN + 2 + \ + REAL_FIELD_LEN + 2 + RATIO_FIELD_LEN + 3 + + LINE_SEP = "*" * LINE_LEN + "\n" summary = "" summary += LINE_SEP - summary += "** {a:<{a_len}} {b:^{b_len}} {c:>{c_len}} {d:>{d_len}} {e:>{e_len}} **\n".format(a=TEST_FIELD, a_len=TEST_FIELD_LEN, - b=RESULT_FIELD, b_len=RESULT_FIELD_LEN, - c=SIM_FIELD, c_len=SIM_FIELD_LEN, - d=REAL_FIELD, d_len=REAL_FIELD_LEN, - e=RATIO_FIELD, e_len=RATIO_FIELD_LEN) + summary += "** {a:<{a_len}} {b:^{b_len}} {c:>{c_len}} {d:>{d_len}} {e:>{e_len}} **\n".format(**header_dict) summary += LINE_SEP + + test_line = "{start}** {a:<{a_len}} {b:^{b_len}} {c:>{c_len}.2f} {d:>{d_len}.2f} {e:>{e_len}.2f} **\n" for result in self.test_results: hilite = '' @@ -477,20 +488,29 @@ def _log_test_summary(self) -> None: if want_color_output(): hilite = ANSI.COLOR_HILITE_SUMMARY - summary += "{start}** {a:<{a_len}} {b:^{b_len}} {c:>{c_len}.2f} {d:>{d_len}.2f} {e:>{e_len}.2f} **\n".format(a=result['test'], a_len=TEST_FIELD_LEN, - b=pass_fail_str, b_len=RESULT_FIELD_LEN, - c=result['sim'], c_len=SIM_FIELD_LEN-1, - d=result['real'], d_len=REAL_FIELD_LEN-1, - e=result['ratio'], e_len=RATIO_FIELD_LEN-1, - start=hilite) + test_dict = dict( + a=result['test'], + b=pass_fail_str, + c=result['sim'], + d=result['real'], + e=result['ratio'], + a_len=TEST_FIELD_LEN, + b_len=RESULT_FIELD_LEN, + c_len=SIM_FIELD_LEN - 1, + d_len=REAL_FIELD_LEN - 1, + e_len=RATIO_FIELD_LEN - 1, + start=hilite) + + summary += test_line.format(**test_dict) + summary += LINE_SEP self.log.info(summary) def _log_sim_summary(self) -> None: - real_time = time.time() - self.start_time + real_time = time.time() - self.start_time sim_time_ns = get_sim_time('ns') - ratio_time = self._safe_divide(sim_time_ns, real_time) + ratio_time = self._safe_divide(sim_time_ns, real_time) summary = "" From 8fe0f5f029ce64dabdd36cebd55a4dd31c816c8b Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Tue, 21 Apr 2020 10:15:22 -0500 Subject: [PATCH 2188/2656] Change name of RegressionManager member for clarity --- cocotb/regression.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 8949ad28..5b27dbfc 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -85,7 +85,7 @@ def __init__(self, dut: SimHandle): dut (SimHandle): The root handle to pass into test functions. """ self._dut = dut - self._running_test = None + self._test_task = None self._cov = None self.log = SimLog("cocotb.regression") self.start_time = time.time() @@ -245,7 +245,7 @@ def handle_result(self, test: RunningTask) -> None: Args: test: The test that completed """ - assert test is self._running_test + assert test is self._test_task real_time = time.time() - self._test_start_time sim_time_ns = get_sim_time('ns') - self._test_start_sim_time @@ -255,7 +255,7 @@ def handle_result(self, test: RunningTask) -> None: self._record_result( test=self._test, - outcome=self._running_test._outcome, + outcome=self._test_task._outcome, wall_time_s=real_time, sim_time_ns=sim_time_ns) @@ -405,8 +405,8 @@ def execute(self) -> None: if self._test is None: return self.tear_down() - self._running_test = self._init_test(self._test) - if self._running_test: + self._test_task = self._init_test(self._test) + if self._test_task: return self._start_test() def _start_test(self) -> None: @@ -423,11 +423,11 @@ def _start_test(self) -> None: self._test.__qualname__)) # start capturing log output - cocotb.log.addHandler(self._running_test.handler) + cocotb.log.addHandler(self._test_task.handler) self._test_start_time = time.time() self._test_start_sim_time = get_sim_time('ns') - cocotb.scheduler.add_test(self._running_test) + cocotb.scheduler.add_test(self._test_task) def _log_test_summary(self) -> None: From f61957b0ce7d0505126acb3ebf0ec1faa6184d52 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Tue, 21 Apr 2020 10:37:35 -0500 Subject: [PATCH 2189/2656] Add ability to construct RegressionManager with test/hook list Standard constructor that uses discovery has been moved to the `from_discovery` class method. --- cocotb/__init__.py | 2 +- cocotb/regression.py | 63 ++++++++++++++++++++++++++++++++------------ 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 6d345a5f..b1cbb992 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -176,7 +176,7 @@ def _initialise_testbench(root_name): # start Regression Manager global regression_manager - regression_manager = RegressionManager(dut) + regression_manager = RegressionManager.from_discovery(dut) regression_manager.execute() _rlock.release() diff --git a/cocotb/regression.py b/cocotb/regression.py index 5b27dbfc..fb8934da 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -76,18 +76,23 @@ def _my_import(name: str) -> Any: return mod +_logger = SimLog(__name__) + + class RegressionManager: """Encapsulates all regression capability into a single place""" - def __init__(self, dut: SimHandle): + def __init__(self, dut: SimHandle, tests: Iterable[Test], hooks: Iterable[Hook]): """ Args: dut (SimHandle): The root handle to pass into test functions. + tests (Iterable[Test]): tests to run + hooks (Iterable[Hook]): hooks to tun """ self._dut = dut self._test_task = None self._cov = None - self.log = SimLog("cocotb.regression") + self.log = _logger self.start_time = time.time() self.test_results = [] self.count = 0 @@ -118,7 +123,7 @@ def __init__(self, dut: SimHandle): # Test Discovery #################### self._queue = [] - for test in self.discover_tests(): + for test in tests: self.log.info("Found test {}.{}".format(test.__module__, test.__qualname__)) self._queue.append(test) self.ntests = len(self._queue) @@ -130,12 +135,31 @@ def __init__(self, dut: SimHandle): # Process Hooks ################### - for hook in self.discover_hooks(): + for hook in hooks: self.log.info("Found hook {}.{}".format(hook.__module__, hook.__qualname__)) self._init_hook(hook) - def discover_tests(self) -> Iterable[Test]: + @classmethod + def from_discovery(cls, dut: SimHandle): + """ + Obtains the test and hook lists by discovery. + + See :envvar:`MODULE` and :envvar:`TESTCASE` for details on how tests are discovered. + + Args: + dut (SimHandle): The root handle to pass into test functions. + """ + tests = cls._discover_tests() + hooks = cls._discover_hooks() + return cls(dut, tests, hooks) + @staticmethod + def _discover_tests() -> Iterable[Test]: + """ + Discovers tests in files automatically. + + See :envvar:`MODULE` and :envvar:`TESTCASE` for details on how tests are discovered. + """ module_str = os.getenv('MODULE') test_str = os.getenv('TESTCASE') @@ -146,14 +170,14 @@ def discover_tests(self) -> Iterable[Test]: for module_name in modules: try: - self.log.debug("Python Path: " + ",".join(sys.path)) - self.log.debug("PWD: " + os.getcwd()) + _logger.debug("Python Path: " + ",".join(sys.path)) + _logger.debug("PWD: " + os.getcwd()) module = _my_import(module_name) except Exception as E: - self.log.critical("Failed to import module %s: %s", module_name, E) - self.log.info("MODULE variable was \"%s\"", ".".join(modules)) - self.log.info("Traceback: ") - self.log.info(traceback.format_exc()) + _logger.critical("Failed to import module %s: %s", module_name, E) + _logger.info("MODULE variable was \"%s\"", ".".join(modules)) + _logger.info("Traceback: ") + _logger.info(traceback.format_exc()) raise if test_str: @@ -163,13 +187,13 @@ def discover_tests(self) -> Iterable[Test]: try: test = getattr(module, test_name) except AttributeError: - self.log.error("Requested test %s wasn't found in module %s", test_name, module_name) + _logger.error("Requested test %s wasn't found in module %s", test_name, module_name) err = AttributeError("Test %s doesn't exist in %s" % (test_name, module_name)) raise err from None # discard nested traceback if not hasattr(test, "im_test"): - self.log.error("Requested %s from module %s isn't a cocotb.test decorated coroutine", - test_name, module_name) + _logger.error("Requested %s from module %s isn't a cocotb.test decorated coroutine", + test_name, module_name) raise ImportError("Failed to find requested test %s" % test_name) yield test @@ -181,13 +205,18 @@ def discover_tests(self) -> Iterable[Test]: if hasattr(thing, "im_test"): yield thing - def discover_hooks(self) -> Iterable[Hook]: + @staticmethod + def _discover_hooks() -> Iterable[Hook]: + """ + Discovers hooks automatically. + See :envvar:`COCOTB_HOOKS` for details on how hooks are discovered. + """ hooks_str = os.getenv('COCOTB_HOOKS', '') hooks = [s.strip() for s in hooks_str.split(',') if s.strip()] for module_name in hooks: - self.log.info("Loading hook from module '" + module_name + "'") + _logger.info("Loading hook from module '" + module_name + "'") module = _my_import(module_name) for thing in vars(module).values(): @@ -619,7 +648,7 @@ def __init__(self, test_function, *args, **kwargs): self.args = args self.kwargs_constant = kwargs self.kwargs = {} - self.log = SimLog("cocotb.regression") + self.log = _logger def add_option(self, name, optionlist): """Add a named option to the test. From beb42627bdc6d6d0271b51f2903a885706eeb5cd Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Tue, 21 Apr 2020 10:44:47 -0500 Subject: [PATCH 2190/2656] Fix bug in RegressionManager test initialization If test initialization failed, the exception and stacktrace were not logged correctly. --- cocotb/regression.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index fb8934da..9031207d 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -315,7 +315,8 @@ def _init_test(self, test: Test) -> Optional[RunningTask]: test_init_outcome = cocotb.outcomes.capture(test, self._dut) if isinstance(test_init_outcome, cocotb.outcomes.Error): - self.log.error("Failed to initialize test %s" % test.__qualname__, exc_info=True) + self.log.error("Failed to initialize test %s" % test.__qualname__, + exc_info=test_init_outcome.error) self._record_result(test, test_init_outcome, 0, 0) return From ad977c1dd26569a9360fc478567ec0595bd8f0a0 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Mon, 27 Apr 2020 13:01:06 +0200 Subject: [PATCH 2191/2656] Remove COCOTB_SIM from makefiles (#1713) Set the COCOTB_SIM environment variable during GPI startup instead. Making the user set this just creates extra work for them, and makes the makefiles harder to read. In future, we may be able to eliminate this altogether, but this at least localizes the ugliness in one place until then. We can't use `setenv` because that doesn't work on windows. --- cocotb/share/lib/gpi/GpiCommon.cpp | 5 +++++ cocotb/share/makefiles/simulators/Makefile.activehdl | 2 +- cocotb/share/makefiles/simulators/Makefile.aldec | 2 +- cocotb/share/makefiles/simulators/Makefile.cvc | 8 ++++---- cocotb/share/makefiles/simulators/Makefile.ghdl | 2 +- cocotb/share/makefiles/simulators/Makefile.icarus | 6 ++---- cocotb/share/makefiles/simulators/Makefile.ius | 4 +--- cocotb/share/makefiles/simulators/Makefile.questa | 2 +- cocotb/share/makefiles/simulators/Makefile.vcs | 3 +-- cocotb/share/makefiles/simulators/Makefile.verilator | 6 ++---- cocotb/share/makefiles/simulators/Makefile.xcelium | 4 +--- tests/test_cases/issue_253/Makefile | 6 +++--- 12 files changed, 23 insertions(+), 27 deletions(-) diff --git a/cocotb/share/lib/gpi/GpiCommon.cpp b/cocotb/share/lib/gpi/GpiCommon.cpp index bf48478a..52dec40d 100644 --- a/cocotb/share/lib/gpi/GpiCommon.cpp +++ b/cocotb/share/lib/gpi/GpiCommon.cpp @@ -208,6 +208,11 @@ void gpi_load_extra_libs() /* Lets look at what other libs we were asked to load too */ char *lib_env = getenv("GPI_EXTRA"); + /* inform python that we are in simulation + * TODO[gh-1566]: Eliminate the need for this completely. */ + static char cocotb_sim_env[] = "COCOTB_SIM=1"; + putenv(cocotb_sim_env); // putenv requires the array lifetime to be as long as the program lifetime, hence the static + if (lib_env) { std::string lib_list = lib_env; std::string const delim = ","; diff --git a/cocotb/share/makefiles/simulators/Makefile.activehdl b/cocotb/share/makefiles/simulators/Makefile.activehdl index ee71dc81..06866cb3 100644 --- a/cocotb/share/makefiles/simulators/Makefile.activehdl +++ b/cocotb/share/makefiles/simulators/Makefile.activehdl @@ -62,7 +62,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.do $(CUSTOM_COMPILE_DEPS) $(CUSTOM_S -@rm -f $(COCOTB_RESULTS_FILE) set -o pipefail; GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 $(CMD) $(RUN_ARGS) -do $(SIM_BUILD)/runsim.do | tee $(SIM_BUILD)/sim.log + $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) $(RUN_ARGS) -do $(SIM_BUILD)/runsim.do | tee $(SIM_BUILD)/sim.log $(call check_for_results_file) diff --git a/cocotb/share/makefiles/simulators/Makefile.aldec b/cocotb/share/makefiles/simulators/Makefile.aldec index 9bbf89a1..65c4177a 100644 --- a/cocotb/share/makefiles/simulators/Makefile.aldec +++ b/cocotb/share/makefiles/simulators/Makefile.aldec @@ -146,7 +146,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.tcl $(CUSTOM_COMPILE_DEPS) $(CUSTOM_ -@rm -f $(COCOTB_RESULTS_FILE) set -o pipefail; GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 $(CMD) $(RUN_ARGS) -do $(SIM_BUILD)/runsim.tcl | tee $(SIM_BUILD)/sim.log + $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) $(RUN_ARGS) -do $(SIM_BUILD)/runsim.tcl | tee $(SIM_BUILD)/sim.log $(call check_for_results_file) diff --git a/cocotb/share/makefiles/simulators/Makefile.cvc b/cocotb/share/makefiles/simulators/Makefile.cvc index e68bcf39..d65e251a 100644 --- a/cocotb/share/makefiles/simulators/Makefile.cvc +++ b/cocotb/share/makefiles/simulators/Makefile.cvc @@ -62,7 +62,7 @@ endif # Compilation phase $(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(CMD) $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=$(LIB_DIR)/libcocotbvpi_modelsim:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) # Execution phase @@ -71,7 +71,7 @@ ifeq ($(CVC_ITERP),1) else $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) endif @@ -79,12 +79,12 @@ endif ifeq ($(CVC_ITERP),1) debug: $(CUSTOM_SIM_DEPS) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ gdb --args $(CMD) $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=$(LIB_DIR)/modelsim/libcocotbvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) else debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ + TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ gdb --args $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) endif diff --git a/cocotb/share/makefiles/simulators/Makefile.ghdl b/cocotb/share/makefiles/simulators/Makefile.ghdl index 5acb9073..de9d2e4f 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ghdl +++ b/cocotb/share/makefiles/simulators/Makefile.ghdl @@ -73,7 +73,7 @@ analyse: $(VHDL_SOURCES) $(SIM_BUILD) $(CUSTOM_COMPILE_DEPS) $(COCOTB_RESULTS_FILE): analyse $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) - MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ + MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(CMD) -r $(GHDL_ARGS) --workdir=$(SIM_BUILD) --work=$(RTL_LIBRARY) $(TOPLEVEL) --vpi=$(LIB_DIR)/libcocotbvpi_ghdl.$(LIB_EXT) $(SIM_ARGS) $(PLUSARGS) $(call check_for_results_file) diff --git a/cocotb/share/makefiles/simulators/Makefile.icarus b/cocotb/share/makefiles/simulators/Makefile.icarus index 4edb24a0..3ca0d083 100644 --- a/cocotb/share/makefiles/simulators/Makefile.icarus +++ b/cocotb/share/makefiles/simulators/Makefile.icarus @@ -66,8 +66,7 @@ $(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) - MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ + MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(ICARUS_BIN_DIR)/vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_ARGS) $(EXTRA_ARGS) $(SIM_BUILD)/sim.vvp $(PLUSARGS) $(call check_for_results_file) @@ -75,8 +74,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) - MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ + MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ gdb --args $(ICARUS_BIN_DIR)/vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) $(call check_for_results_file) diff --git a/cocotb/share/makefiles/simulators/Makefile.ius b/cocotb/share/makefiles/simulators/Makefile.ius index a49530f6..9d968c48 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ius +++ b/cocotb/share/makefiles/simulators/Makefile.ius @@ -101,9 +101,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUS -@rm -f $(COCOTB_RESULTS_FILE) set -o pipefail; \ - MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ - GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - \ + MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(CMD) -timescale $(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) \ $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) 2>&1 | tee $(SIM_BUILD)/sim.log diff --git a/cocotb/share/makefiles/simulators/Makefile.questa b/cocotb/share/makefiles/simulators/Makefile.questa index 9c1de376..2ca85b4d 100644 --- a/cocotb/share/makefiles/simulators/Makefile.questa +++ b/cocotb/share/makefiles/simulators/Makefile.questa @@ -121,7 +121,7 @@ endif $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.do -@rm -f $(COCOTB_RESULTS_FILE) - set -o pipefail; MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ + set -o pipefail; MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(CMD) $(RUN_ARGS) -do $(SIM_BUILD)/runsim.do $(PLUSARGS) 2>&1 | tee $(SIM_BUILD)/sim.log diff --git a/cocotb/share/makefiles/simulators/Makefile.vcs b/cocotb/share/makefiles/simulators/Makefile.vcs index 5374a215..47b1ac9a 100644 --- a/cocotb/share/makefiles/simulators/Makefile.vcs +++ b/cocotb/share/makefiles/simulators/Makefile.vcs @@ -81,8 +81,7 @@ $(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(CUSTOM $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/simv $(PYTHON_FILES) $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) - -MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ - TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ + -MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(SIM_BUILD)/simv +define+COCOTB_SIM=1 $(SIM_ARGS) $(EXTRA_ARGS) $(call check_for_results_file) diff --git a/cocotb/share/makefiles/simulators/Makefile.verilator b/cocotb/share/makefiles/simulators/Makefile.verilator index ed798f31..ccb1ceb9 100644 --- a/cocotb/share/makefiles/simulators/Makefile.verilator +++ b/cocotb/share/makefiles/simulators/Makefile.verilator @@ -48,8 +48,7 @@ $(SIM_BUILD)/$(TOPLEVEL): $(SIM_BUILD)/Vtop.mk $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) - MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ + MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $< $(PLUSARGS) $(call check_for_results_file) @@ -57,8 +56,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) debug: $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) - MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) COCOTB_SIM=1 \ + MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ gdb --args $< $(PLUSARGS) $(call check_for_results_file) diff --git a/cocotb/share/makefiles/simulators/Makefile.xcelium b/cocotb/share/makefiles/simulators/Makefile.xcelium index 3453e139..e005c5f9 100644 --- a/cocotb/share/makefiles/simulators/Makefile.xcelium +++ b/cocotb/share/makefiles/simulators/Makefile.xcelium @@ -108,9 +108,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUS -@rm -f $(COCOTB_RESULTS_FILE) set -o pipefail; \ - MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) COCOTB_SIM=1 \ - GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - \ + MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(CMD) -timescale $(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) \ $(EXTRA_ARGS) $(GPI_ARGS) $(INCDIRS) -access +rwc -createdebugdb $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) 2>&1 | tee $(SIM_BUILD)/sim.log diff --git a/tests/test_cases/issue_253/Makefile b/tests/test_cases/issue_253/Makefile index 379505f1..8a325483 100644 --- a/tests/test_cases/issue_253/Makefile +++ b/tests/test_cases/issue_253/Makefile @@ -45,19 +45,19 @@ $(COCOTB_RESULTS_FILE): notset_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) MODULE=$(MODULE) \ - COCOTB_SIM=1 TESTCASE=issue_253_notset TOPLEVEL= \ + TESTCASE=issue_253_notset TOPLEVEL= \ vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) mkdir -p $@_result && mv results.xml $@_result/ empty_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) MODULE=$(MODULE) \ - COCOTB_SIM=1 TESTCASE=issue_253_empty TOPLEVEL="" \ + TESTCASE=issue_253_empty TOPLEVEL="" \ vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) mkdir -p $@_result && mv results.xml $@_result/ no_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) $(LIB_LOAD) MODULE=$(MODULE) \ - COCOTB_SIM=1 TESTCASE=issue_253_none \ + TESTCASE=issue_253_none \ vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) mkdir -p $@_result && mv results.xml $@_result/ From 4f46778dcb5e0dbb30b66e3a4fedad0a6492ce3f Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 27 Apr 2020 11:20:56 -0500 Subject: [PATCH 2192/2656] Move build_libs.py to root (#1719) To prevent the running of the cocotb package when importing. --- MANIFEST.in | 1 + cocotb/_build_libs.py => cocotb_build_libs.py | 2 +- setup.py | 7 +++---- 3 files changed, 5 insertions(+), 5 deletions(-) rename cocotb/_build_libs.py => cocotb_build_libs.py (99%) diff --git a/MANIFEST.in b/MANIFEST.in index d1e955b0..2d086f9a 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,3 +2,4 @@ recursive-include cocotb/share *.c* *.h *.def Makefile.* Makefile recursive-include examples *.v *.py Makefile *.tcl *.cfg include version include README.md +include cocotb_build_libs.py diff --git a/cocotb/_build_libs.py b/cocotb_build_libs.py similarity index 99% rename from cocotb/_build_libs.py rename to cocotb_build_libs.py index 0ac5779b..d5a9eb87 100755 --- a/cocotb/_build_libs.py +++ b/cocotb_build_libs.py @@ -16,7 +16,7 @@ logger = logging.getLogger(__name__) -cocotb_share_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "share")) +cocotb_share_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "cocotb", "share")) def _get_lib_ext_name(): diff --git a/setup.py b/setup.py index 4e93fa80..6c449c2f 100755 --- a/setup.py +++ b/setup.py @@ -53,10 +53,9 @@ from os import path, walk from io import StringIO -# note: cocotb is not installed properly yet, but we can import it anyway -# because it's in the current directory. We'll need to change this if we -# add `install_requires` to the `setup()` call. -from cocotb._build_libs import get_ext, build_ext +# Note: cocotb is not installed properly yet and is missing dependencies and binaries +# We can still import other files next to setup.py, as long as they're in MANIFEST.in +from cocotb_build_libs import get_ext, build_ext def read_file(fname): with open(path.join(path.dirname(__file__), fname), encoding='utf8') as f: From d78f6443a629954527f05e7cfa67bb2dc58b5818 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 28 Apr 2020 00:05:19 +0200 Subject: [PATCH 2193/2656] Fix path to test_discovery.py --- documentation/source/simulator_support.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index ea26b19b..e90561e2 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -29,7 +29,7 @@ Accessing bits of a vector doesn't work: dut.stream_in_data[2] <= 1 -See the ``access_single_bit`` test in :file:`examples/functionality/tests/test_discovery.py`. +See the ``access_single_bit`` test in :file:`tests/test_cases/test_discovery/test_discovery.py`. .. _sim-icarus-waveforms: From b379a9e293f30ec9b899fede8bce47ee19c9a1f7 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Tue, 28 Apr 2020 16:15:06 +0200 Subject: [PATCH 2194/2656] Remove unused SIM_DEFINE makefile variable. --- cocotb/share/makefiles/Makefile.sim | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/cocotb/share/makefiles/Makefile.sim b/cocotb/share/makefiles/Makefile.sim index 29c4ad9f..7a870071 100644 --- a/cocotb/share/makefiles/Makefile.sim +++ b/cocotb/share/makefiles/Makefile.sim @@ -105,18 +105,6 @@ endif # Default to Icarus if no simulator is defined SIM ?= icarus -SIM_DEFINE := $(shell echo $(SIM) | tr a-z A-Z) - -# Use a common define for Questa and Modelsim and cvc -ifeq ($(SIM_DEFINE),$(filter $(SIM_DEFINE),QUESTA CVC)) - SIM_DEFINE = MODELSIM -endif - -# Use a common define for Xcelium and IUS -ifeq ($(SIM_DEFINE),XCELIUM) - SIM_DEFINE = IUS -endif - # Maintain backwards compatibility by supporting upper and lower case SIM variable SIM_LOWERCASE := $(shell echo $(SIM) | tr A-Z a-z) From 0f5ae631741dc47153f6e6e807a149860f9eadd5 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Tue, 28 Apr 2020 11:23:12 -0500 Subject: [PATCH 2195/2656] Add is_set method to Event (#1723) This brings it in line with `asyncio.Event`, `threading.Event`, and `trio.Event`. --- cocotb/triggers.py | 4 ++++ documentation/source/newsfragments/1723.feature.rst | 1 + .../test_cocotb/test_concurrency_primitives.py | 11 +++++++++++ 3 files changed, 16 insertions(+) create mode 100644 documentation/source/newsfragments/1723.feature.rst diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 23e81b81..0b759708 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -420,6 +420,10 @@ def clear(self): :meth:`~cocotb.triggers.Event.set` is called again.""" self.fired = False + def is_set(self) -> bool: + """ Return true if event has been set """ + return self.fired + def __repr__(self): if self.name is None: fmt = "<{0} at {2}>" diff --git a/documentation/source/newsfragments/1723.feature.rst b/documentation/source/newsfragments/1723.feature.rst new file mode 100644 index 00000000..e55fa56e --- /dev/null +++ b/documentation/source/newsfragments/1723.feature.rst @@ -0,0 +1 @@ +Added :meth:`~cocotb.triggers.Event.is_set`, so users may check if an :class:`~cocotb.triggers.Event` has fired. diff --git a/tests/test_cases/test_cocotb/test_concurrency_primitives.py b/tests/test_cases/test_cocotb/test_concurrency_primitives.py index fb4f77b3..ea50e5fa 100644 --- a/tests/test_cases/test_cocotb/test_concurrency_primitives.py +++ b/tests/test_cases/test_cocotb/test_concurrency_primitives.py @@ -145,3 +145,14 @@ def do_something(delay): crs = [cocotb.fork(do_something(dly)) for dly in [10, 30, 20]] yield Combine(*(cr.join() for cr in crs)) + + +@cocotb.test() +async def test_event_is_set(dut): + e = Event() + + assert not e.is_set() + e.set() + assert e.is_set() + e.clear() + assert not e.is_set() From 776a24a448d1c7162fedc7c2f736613efcdcd0f0 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Tue, 28 Apr 2020 18:24:16 +0200 Subject: [PATCH 2196/2656] Remove unused LIB_LOAD makefile variable. (#1726) The setting of this variable was removed in a previous change. --- cocotb/share/makefiles/simulators/Makefile.activehdl | 2 +- cocotb/share/makefiles/simulators/Makefile.aldec | 2 +- tests/test_cases/issue_253/Makefile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.activehdl b/cocotb/share/makefiles/simulators/Makefile.activehdl index 06866cb3..de7a5c08 100644 --- a/cocotb/share/makefiles/simulators/Makefile.activehdl +++ b/cocotb/share/makefiles/simulators/Makefile.activehdl @@ -62,7 +62,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.do $(CUSTOM_COMPILE_DEPS) $(CUSTOM_S -@rm -f $(COCOTB_RESULTS_FILE) set -o pipefail; GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) $(RUN_ARGS) -do $(SIM_BUILD)/runsim.do | tee $(SIM_BUILD)/sim.log + MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) $(RUN_ARGS) -do $(SIM_BUILD)/runsim.do | tee $(SIM_BUILD)/sim.log $(call check_for_results_file) diff --git a/cocotb/share/makefiles/simulators/Makefile.aldec b/cocotb/share/makefiles/simulators/Makefile.aldec index 65c4177a..f3269bb2 100644 --- a/cocotb/share/makefiles/simulators/Makefile.aldec +++ b/cocotb/share/makefiles/simulators/Makefile.aldec @@ -146,7 +146,7 @@ $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.tcl $(CUSTOM_COMPILE_DEPS) $(CUSTOM_ -@rm -f $(COCOTB_RESULTS_FILE) set -o pipefail; GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(LIB_LOAD) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) $(RUN_ARGS) -do $(SIM_BUILD)/runsim.tcl | tee $(SIM_BUILD)/sim.log + MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) $(RUN_ARGS) -do $(SIM_BUILD)/runsim.tcl | tee $(SIM_BUILD)/sim.log $(call check_for_results_file) diff --git a/tests/test_cases/issue_253/Makefile b/tests/test_cases/issue_253/Makefile index 8a325483..3c6258ae 100644 --- a/tests/test_cases/issue_253/Makefile +++ b/tests/test_cases/issue_253/Makefile @@ -56,7 +56,7 @@ empty_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) mkdir -p $@_result && mv results.xml $@_result/ no_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) - $(LIB_LOAD) MODULE=$(MODULE) \ + MODULE=$(MODULE) \ TESTCASE=issue_253_none \ vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) mkdir -p $@_result && mv results.xml $@_result/ From 650ff80c724674ad3d39469d9493fa1eb5624ea7 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 28 Apr 2020 18:40:50 +0200 Subject: [PATCH 2197/2656] Fix typo in error message --- cocotb/share/lib/embed/gpi_embed.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/share/lib/embed/gpi_embed.cpp b/cocotb/share/lib/embed/gpi_embed.cpp index 6b65a3ed..c6c3fd4c 100644 --- a/cocotb/share/lib/embed/gpi_embed.cpp +++ b/cocotb/share/lib/embed/gpi_embed.cpp @@ -113,7 +113,7 @@ extern "C" void embed_init_python(void) FENTER; #ifndef PYTHON_SO_LIB -#error "Python version needs passing in with -DPYTHON_SO_VERSION=libpython.so" +#error "Python version needs passing in with -DPYTHON_SO_LIB=libpython.so" #else #define PY_SO_LIB xstr(PYTHON_SO_LIB) #endif From 1262260ba21be225f4ea8cc4831f1028667942da Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 22 Apr 2020 22:06:35 +0200 Subject: [PATCH 2198/2656] De-duplicate doc index entries --- documentation/source/library_reference.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/source/library_reference.rst b/documentation/source/library_reference.rst index b9611266..301ee68b 100644 --- a/documentation/source/library_reference.rst +++ b/documentation/source/library_reference.rst @@ -48,8 +48,6 @@ Interacting with the Simulator :members: :member-order: bysource -.. autoclass:: cocotb.clock.Clock - .. autofunction:: cocotb.fork .. autofunction:: cocotb.decorators.RunningTask.join @@ -195,7 +193,9 @@ Simulation Object Handles :member-order: bysource :show-inheritance: :synopsis: Classes for simulation objects. - + :exclude-members: Deposit, Force, Freeze, Release +.. + Excluding the Assignment Methods that are getting their own section below .. _assignment-methods: From c3b2287e4749f0ede470f33863a4c588fdc2324e Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 21 Apr 2020 23:55:50 +0200 Subject: [PATCH 2199/2656] Mention that this is the complete simulator list --- documentation/source/simulator_support.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index e90561e2..0c5c87d4 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -4,7 +4,8 @@ Simulator Support ***************** -This page documents specifics, limitations, workarounds etc. in the various simulators. +This page lists all simulators that cocotb provides support for +and documents specifics, limitations, workarounds etc. .. _sim-icarus: From 0f7d0b08c58a5101d9d1d98fb924b9ae4001897f Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 28 Apr 2020 18:43:05 +0200 Subject: [PATCH 2200/2656] Slight rewording --- documentation/source/simulator_support.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 0c5c87d4..b5081dff 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -4,7 +4,7 @@ Simulator Support ***************** -This page lists all simulators that cocotb provides support for +This page lists the simulators that cocotb can be used with and documents specifics, limitations, workarounds etc. From 934ec942d66f9663eb62b85fcc138d3356b9cfcb Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Tue, 28 Apr 2020 14:21:54 +0200 Subject: [PATCH 2201/2656] Add Questa/ModelSim error on missing FLI library Co-Authored-By: Eric Wieser Co-Authored-By: Colin Marquardt --- cocotb/share/makefiles/simulators/Makefile.questa | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.questa b/cocotb/share/makefiles/simulators/Makefile.questa index 2ca85b4d..0cd4465a 100644 --- a/cocotb/share/makefiles/simulators/Makefile.questa +++ b/cocotb/share/makefiles/simulators/Makefile.questa @@ -67,9 +67,17 @@ else VSIM_ARGS += -onfinish exit endif +FLI_LIB := $(LIB_DIR)/libcocotbfli_modelsim.$(LIB_EXT) +# if this target is run, then cocotb did not build the library +$(FLI_LIB): + @echo -e "ERROR: cocotb was not installed with an FLI library, as the mti.h header could not be located.\n\ + If you installed an FLI-capable simulator after cocotb, you will need to reinstall cocotb.\n\ + Please check the cocotb documentation on ModelSim support." >&2 && exit 1 + GPI_EXTRA:= ifeq ($(TOPLEVEL_LANG),vhdl) - VSIM_ARGS += -foreign \"cocotb_init $(call to_tcl_path,$(LIB_DIR)/libcocotbfli_modelsim.$(LIB_EXT))\" + CUSTOM_COMPILE_DEPS += $(FLI_LIB) + VSIM_ARGS += -foreign \"cocotb_init $(call to_tcl_path,$(FLI_LIB))\" ifneq ($(VERILOG_SOURCES),) GPI_EXTRA = cocotbvpi_modelsim:cocotbvpi_entry_point endif From ffd4716ba72c98aed420b4f4879642f5fe5e7c71 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Wed, 29 Apr 2020 17:42:38 +0200 Subject: [PATCH 2202/2656] Use conda to build documentation (#1706) Conda is built with `--enable-shared` by default, which `libcocotb` requires. Now that we use conda, we can `pip install` ourselves before building the docs, meaning we no longer need to mess with the path in `conf.py`. Closes gh-1569 --- .readthedocs.yml | 8 ++++++-- documentation/conda.yml | 9 +++++++++ documentation/source/conf.py | 5 ----- 3 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 documentation/conda.yml diff --git a/.readthedocs.yml b/.readthedocs.yml index 15810e6d..358468be 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -4,10 +4,14 @@ version: 2 +# Use conda because default Python is not compiled with --enable-shared +conda: + environment: documentation/conda.yml + python: - version: 3.7 install: - - requirements: documentation/requirements.txt + - method: pip + path: . sphinx: configuration: documentation/source/conf.py diff --git a/documentation/conda.yml b/documentation/conda.yml new file mode 100644 index 00000000..c9c4aa68 --- /dev/null +++ b/documentation/conda.yml @@ -0,0 +1,9 @@ +# Python environment and dependencies to build the documentation + +name: cocotb-docs +dependencies: + - python=3.8 + - pip + # Packages installed from PyPI + - pip: + - -r requirements.txt diff --git a/documentation/source/conf.py b/documentation/source/conf.py index c4058482..17e79fd0 100644 --- a/documentation/source/conf.py +++ b/documentation/source/conf.py @@ -12,11 +12,6 @@ import subprocess import sys -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath('../..')) - # Add in-tree extensions to path sys.path.insert(0, os.path.abspath('../sphinxext')) From ab949997751dcbcfac79bf8f81a23d3a5c47622b Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 29 Apr 2020 19:20:10 +0200 Subject: [PATCH 2203/2656] Remove obsolete comment about Icarus and COCOTB_HDL_TIME* We support COCOTB_HDL_TIMEUNIT and COCOTB_HDL_TIMEPRECISION for Icarus since #1711 --- documentation/source/building.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/documentation/source/building.rst b/documentation/source/building.rst index e7eb3d8c..09297620 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -105,8 +105,6 @@ and Allowed values are 1, 10, and 100. Allowed units are ``s``, ``ms``, ``us``, ``ns``, ``ps``, ``fs``. - NOTE: Icarus Verilog does not support this variable - .. versionadded:: 1.3 .. make:var:: COCOTB_HDL_TIMEPRECISION @@ -116,8 +114,6 @@ and Allowed values are 1, 10, and 100. Allowed units are ``s``, ``ms``, ``us``, ``ns``, ``ps``, ``fs``. - NOTE: Icarus Verilog does not support this variable - .. versionadded:: 1.3 .. make:var:: CUSTOM_COMPILE_DEPS From 083eb682686dbcc882e93fc3d270f59384278dbc Mon Sep 17 00:00:00 2001 From: Botnic Date: Wed, 29 Apr 2020 19:59:43 +0200 Subject: [PATCH 2204/2656] Honor NO_COLOR to disable colored output (#1438) The NO_COLOR environment variable is proposed as a standardized way to disable all colored output (https://no-color.org/). Closes #1309. --- cocotb/utils.py | 2 ++ documentation/source/building.rst | 8 +++++++- documentation/source/newsfragments/1309.feature.rst | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 documentation/source/newsfragments/1309.feature.rst diff --git a/cocotb/utils.py b/cocotb/utils.py index a72581e3..187dc896 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -524,6 +524,8 @@ def want_color_output(): Colored output can be explicitly requested by setting :envvar:`COCOTB_ANSI_OUTPUT` to ``1``. """ want_color = sys.stdout.isatty() # default to color for TTYs + if os.getenv("NO_COLOR") is not None: + want_color = False if os.getenv("COCOTB_ANSI_OUTPUT", default='0') == '1': want_color = True if os.getenv("GUI", default='0') == '1': diff --git a/documentation/source/building.rst b/documentation/source/building.rst index 09297620..33d92b60 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -161,10 +161,16 @@ Environment Variables Use this to override the default behavior of annotating cocotb output with ANSI color codes if the output is a terminal (``isatty()``). - ``COCOTB_ANSI_OUTPUT=1`` forces output to be ANSI regardless of the type of ``stdout`` + ``COCOTB_ANSI_OUTPUT=1`` forces output to be ANSI regardless of the type of ``stdout`` or the presence of :envvar:`NO_COLOR`. ``COCOTB_ANSI_OUTPUT=0`` suppresses the ANSI output in the log messages +.. envvar:: NO_COLOR + From http://no-color.org, + + All command-line software which outputs text with ANSI color added should check for the presence + of a ``NO_COLOR`` environment variable that, when present (regardless of its value), prevents the addition of ANSI color. + .. envvar:: COCOTB_REDUCED_LOG_FMT If defined, log lines displayed in the terminal will be shorter. It will print only diff --git a/documentation/source/newsfragments/1309.feature.rst b/documentation/source/newsfragments/1309.feature.rst new file mode 100644 index 00000000..89f6067b --- /dev/null +++ b/documentation/source/newsfragments/1309.feature.rst @@ -0,0 +1 @@ +The colored output can now be disabled by the :envvar:`NO_COLOR` environment variable. From 5ab94f03c179c839899badb741e7113c30a34923 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Wed, 29 Apr 2020 12:41:42 -0700 Subject: [PATCH 2205/2656] Document multiple newsfragment support. --- documentation/source/newsfragments/README.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/documentation/source/newsfragments/README.rst b/documentation/source/newsfragments/README.rst index 354643d9..6b38d9e5 100644 --- a/documentation/source/newsfragments/README.rst +++ b/documentation/source/newsfragments/README.rst @@ -26,6 +26,14 @@ and do not use a bullet point at the beginning of the file. Use Sphinx references (see https://sphinx-tutorial.readthedocs.io/cheatsheet/) if you refer to added classes, methods etc. +Additional newsfragments of the same ```` for a single ```` are +supported, using the name format ``..<#>.rst``. + +Example: + +* ``.bugfix.rst`` +* ``.bugfix.1.rst`` + An example file could consist of the content between the marks: --8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8<--8< From c2a6285432fee5090b2a4c1b890544be094451f5 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 29 Apr 2020 15:39:39 -0500 Subject: [PATCH 2206/2656] Add cocotb to list of packages to build for documentation --- documentation/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/Makefile b/documentation/Makefile index 87c6a68a..542aa5f8 100644 --- a/documentation/Makefile +++ b/documentation/Makefile @@ -21,6 +21,7 @@ help: .venv python3 -m venv .venv .venv/bin/pip -q install --upgrade pip .venv/bin/pip -q install -r requirements.txt + .venv/bin/pip -q install .. .PHONY: clean clean: .venv Makefile From 20982d7192e5159084a7c1a05aa3b4109d8a46f8 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 1 May 2020 11:14:39 +0200 Subject: [PATCH 2207/2656] Use raw string for docstring with escape codes (#1741) `\ext_id\` and `\!\` are used as signal names in this docstring, which should not be interpreted as escape codes. --- tests/test_cases/test_array/test_array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py index 13cb493f..0a5a3a4b 100644 --- a/tests/test_cases/test_array/test_array.py +++ b/tests/test_cases/test_array/test_array.py @@ -240,7 +240,7 @@ def test_gen_loop(dut): @cocotb.test() def test_discover_all(dut): - """Discover everything in the DUT: + r"""Discover everything in the DUT: dut TYPE CNT NOTES EXCEPTIONS parameters: 7/2 (base types) (VHDL/Verilog) From db7078af71b8c65c7e717a202b208bbbaf11d6fb Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Thu, 9 Apr 2020 23:12:10 +0200 Subject: [PATCH 2208/2656] Add simple_dff example --- examples/Makefile | 1 + examples/simple_dff/.gitignore | 45 +++++++++++++++++++++++++++++++++ examples/simple_dff/Makefile | 15 +++++++++++ examples/simple_dff/dff.sv | 15 +++++++++++ examples/simple_dff/dff.vhdl | 21 +++++++++++++++ examples/simple_dff/test_dff.py | 21 +++++++++++++++ 6 files changed, 118 insertions(+) create mode 100644 examples/simple_dff/.gitignore create mode 100644 examples/simple_dff/Makefile create mode 100644 examples/simple_dff/dff.sv create mode 100644 examples/simple_dff/dff.vhdl create mode 100644 examples/simple_dff/test_dff.py diff --git a/examples/Makefile b/examples/Makefile index 83640695..ad0f4f05 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -30,6 +30,7 @@ EXAMPLES := adder/tests \ axi_lite_slave/tests \ dff/tests \ + simple_dff \ endian_swapper/tests \ mean/tests \ mixed_language/tests diff --git a/examples/simple_dff/.gitignore b/examples/simple_dff/.gitignore new file mode 100644 index 00000000..5004c67f --- /dev/null +++ b/examples/simple_dff/.gitignore @@ -0,0 +1,45 @@ +# Python +__pycache__ + +# Waveforms +*.vcd + +# Results +results.xml +sim_build + +# VCS files +*.tab +ucli.key + +# Cadence Incisive/Xcelium +*.elog +irun.log +xrun.log +irun.key +xrun.key +irun.history +xrun.history +INCA_libs +xcelium.d +ncelab_*.err +xmelab_*.err +ncsim_*.err +xmsim_*.err +bpad_*.err +.bpad/ +.simvision/ +waves.shm/ + +# Mentor Modelsim/Questa +modelsim.ini +transcript +*.wlf + +# Riviera +library.cfg +dataset.asdb +compile +library.cfg +dataset.asdb +compile diff --git a/examples/simple_dff/Makefile b/examples/simple_dff/Makefile new file mode 100644 index 00000000..2957531d --- /dev/null +++ b/examples/simple_dff/Makefile @@ -0,0 +1,15 @@ +# This file is public domain, it can be freely copied without restrictions. +# SPDX-License-Identifier: CC0-1.0 + +TOPLEVEL_LANG ?= verilog + +ifeq ($(TOPLEVEL_LANG),verilog) + VERILOG_SOURCES = $(shell pwd)/dff.sv +else ifeq ($(TOPLEVEL_LANG),vhdl) + VHDL_SOURCES = $(shell pwd)/dff.vhdl +endif + +MODULE = test_dff +TOPLEVEL = dff + +include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/examples/simple_dff/dff.sv b/examples/simple_dff/dff.sv new file mode 100644 index 00000000..442389f7 --- /dev/null +++ b/examples/simple_dff/dff.sv @@ -0,0 +1,15 @@ +// This file is public domain, it can be freely copied without restrictions. +// SPDX-License-Identifier: CC0-1.0 + +`timescale 1us/1us + +module dff ( + input logic clk, d, + output logic q +); + +always @(posedge clk) begin + q <= d; +end + +endmodule diff --git a/examples/simple_dff/dff.vhdl b/examples/simple_dff/dff.vhdl new file mode 100644 index 00000000..50478cb5 --- /dev/null +++ b/examples/simple_dff/dff.vhdl @@ -0,0 +1,21 @@ +-- 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 is +port( + clk: in std_logic; + d: in std_logic; + q: out std_logic); +end dff; + +architecture behavioral of dff is +begin + process (clk) begin + if rising_edge(clk) then + q <= d; + end if; + end process; +end behavioral; diff --git a/examples/simple_dff/test_dff.py b/examples/simple_dff/test_dff.py new file mode 100644 index 00000000..e3e3c94e --- /dev/null +++ b/examples/simple_dff/test_dff.py @@ -0,0 +1,21 @@ +# 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.triggers import FallingEdge + +@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 + + 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 == val, "output q was incorrect on the {}th cycle".format(i) From 955ab1f5d17624cb62b712c061969156be5e93da Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Fri, 1 May 2020 13:34:06 +0200 Subject: [PATCH 2209/2656] Remove Makefile.doc --- cocotb/share/makefiles/Makefile.doc | 52 ----------------------------- 1 file changed, 52 deletions(-) delete mode 100644 cocotb/share/makefiles/Makefile.doc diff --git a/cocotb/share/makefiles/Makefile.doc b/cocotb/share/makefiles/Makefile.doc deleted file mode 100644 index 49d8f9e2..00000000 --- a/cocotb/share/makefiles/Makefile.doc +++ /dev/null @@ -1,52 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - - -# Set of common rules for building documentation - -# Assumes a subfolder called "documentation" containing: -# JSON waveforms named xxx.wavedrom -# Diagrams in SVG format named xxx.svg -# Markdown documents named xxx.md - -# Find the dependencies -WAVEFORMS = $(wildcard documentation/*.wavedrom) -DIAGRAMS = $(wildcard documentation/*.svg) - -# Define the default target as the documentation/$(dirname).html -documentation/$(shell basename $(shell pwd)).html : - - -documentation/%.html: documentation/%.md $(WAVEFORMS) $(DIAGRAMS) - cd documentation && python -m markdown -x wavedrom $(notdir $<) > $(notdir $@) - -clean:: - rm -rf documentation/*.html - - From fdbbbc6d75e7859f89386db38a9f1f3b0100490a Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 1 May 2020 13:54:09 +0200 Subject: [PATCH 2210/2656] Add info that examples should get the CC0/PD SPDX header --- CONTRIBUTING.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d5132a6e..2c07a10e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -72,12 +72,17 @@ All changes which should go into the main codebase of cocotb must follow this se Use this text to discuss things which are not obvious from the code, especially *why* changes were made. Include the GitHub issue number (if one exists) in the form "Fixes #nnn" ([read more about that](https://help.github.com/articles/closing-issues-using-keywords/)). Keep each description line below 72 characters. -- Use the following header for new files: +- Use the following header for new non-example files: ```python # Copyright cocotb contributors # Licensed under the Revised BSD License, see LICENSE for details. # SPDX-License-Identifier: BSD-3-Clause ``` +- Use the following header for new example files: + ```python + # This file is public domain, it can be freely copied without restrictions. + # SPDX-License-Identifier: CC0-1.0 + ``` Managing of Issues and Pull Requests ------------------------------------ From df1c2b0683af4895fe9bfe19a28f5ea788bba932 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Tue, 21 Apr 2020 15:24:18 +0200 Subject: [PATCH 2211/2656] Add gitpod integration Co-Authored-By: Colin Marquardt --- .gitpod.Dockerfile | 45 +++++++++++++++++++ .gitpod.yml | 25 +++++++++++ README.md | 1 + .../source/newsfragments/1683.feature.rst | 1 + 4 files changed, 72 insertions(+) create mode 100644 .gitpod.Dockerfile create mode 100644 .gitpod.yml create mode 100644 documentation/source/newsfragments/1683.feature.rst diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile new file mode 100644 index 00000000..28520c89 --- /dev/null +++ b/.gitpod.Dockerfile @@ -0,0 +1,45 @@ +FROM gitpod/workspace-full-vnc + +USER gitpod + +## Install Python with --enable-shared +ARG PYTHON_VERSION=3.8.2 +RUN rm -rf ${HOME}/.pyenv/versions/${PYTHON_VERSION} +RUN PYTHON_CONFIGURE_OPTS="--enable-shared" pyenv install ${PYTHON_VERSION} +RUN pyenv global ${PYTHON_VERSION} + +RUN pip3 install --upgrade pip + +# Install linters +RUN pip3 install -U flake8 pylint + +# Re-synchronize the package index +RUN sudo apt-get update + +# Install needed packages +RUN sudo apt-get install -y flex gnat gtkwave swig +RUN sudo rm -rf /var/lib/apt/lists/* + +## Install Icarus Verilog +RUN brew install icarus-verilog + +## Install Verilator +RUN brew install verilator + +## Install GHDL +ENV GHDL_BRANCH=v0.37 +RUN git clone https://github.com/ghdl/ghdl.git --depth=1 --branch ${GHDL_BRANCH} ghdl \ + && cd ghdl \ + && ./configure \ + && make -s \ + && sudo make -s install \ + && cd .. \ + && rm -rf ghdl + +# Install cvc +RUN git clone https://github.com/cambridgehackers/open-src-cvc.git --depth=1 cvc \ + && cd cvc/src \ + && make -f makefile.cvc64 -s \ + && sudo cp cvc64 /usr/local/bin \ + && cd ../.. \ + && rm -rf cvc diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 00000000..31f9f761 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,25 @@ +image: + file: .gitpod.Dockerfile + +tasks: + - before: > + pip3 install -e . && + export COCOTB_REDUCED_LOG_FMT=1 + init: > + gp preview https://docs.cocotb.org/ && + cd /workspace/cocotb/examples/dff/tests && + gp open ../hdl/dff.v && + gp open ../hdl/dff.vhdl && + gp open Makefile && + gp open dff_cocotb.py && + make SIM=icarus + command: > + history -s make SIM=cvc && + history -s make SIM=ghdl TOPLEVEL_LANG=vhdl && + history -s make SIM=verilator && + history -s make SIM=icarus + +vscode: + extensions: + - mshr-h.veriloghdl@1.0.6:RPslnvyzniF7C66mxHT+Hg== + - puorc.awesome-vhdl@0.0.1:w0lXwxIDreee5Mbtg9XSfg== diff --git a/README.md b/README.md index 2e5f0fa7..65229afa 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ [![Documentation Status](https://readthedocs.org/projects/cocotb/badge/?version=latest)](https://docs.cocotb.org/en/latest/) [![Build Status](https://travis-ci.org/cocotb/cocotb.svg?branch=master)](https://travis-ci.org/cocotb/cocotb) [![PyPI](https://img.shields.io/pypi/dm/cocotb.svg?label=PyPI%20downloads)](https://pypi.org/project/cocotb/) +[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/cocotb/cocotb) * Read the [documentation](https://docs.cocotb.org) * Get involved: diff --git a/documentation/source/newsfragments/1683.feature.rst b/documentation/source/newsfragments/1683.feature.rst new file mode 100644 index 00000000..2645cc20 --- /dev/null +++ b/documentation/source/newsfragments/1683.feature.rst @@ -0,0 +1 @@ +Add Gitpod support. Can by tried under https://gitpod.io/#https://github.com/cocotb/cocotb \ No newline at end of file From cc19639c876597264f3e758c6fa7322253b2b4b4 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Wed, 29 Apr 2020 17:53:45 +0200 Subject: [PATCH 2212/2656] Delete 1683.feature.rst --- documentation/source/newsfragments/1683.feature.rst | 1 - 1 file changed, 1 deletion(-) delete mode 100644 documentation/source/newsfragments/1683.feature.rst diff --git a/documentation/source/newsfragments/1683.feature.rst b/documentation/source/newsfragments/1683.feature.rst deleted file mode 100644 index 2645cc20..00000000 --- a/documentation/source/newsfragments/1683.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Add Gitpod support. Can by tried under https://gitpod.io/#https://github.com/cocotb/cocotb \ No newline at end of file From 3e49789b0cb276289a352f002c6ab8c853f7935f Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Sat, 2 May 2020 13:59:33 +0200 Subject: [PATCH 2213/2656] Update .gitpod.Dockerfile Co-authored-by: Eric Wieser --- .gitpod.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile index 28520c89..b365f4ef 100644 --- a/.gitpod.Dockerfile +++ b/.gitpod.Dockerfile @@ -37,7 +37,7 @@ RUN git clone https://github.com/ghdl/ghdl.git --depth=1 --branch ${GHDL_BRANCH} && rm -rf ghdl # Install cvc -RUN git clone https://github.com/cambridgehackers/open-src-cvc.git --depth=1 cvc \ +RUN git clone https://github.com/cambridgehackers/open-src-cvc.git --depth=1 cvc \ && cd cvc/src \ && make -f makefile.cvc64 -s \ && sudo cp cvc64 /usr/local/bin \ From df79be4338b8c59e4a088785ededfd14e5c6ef35 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 1 May 2020 23:07:40 +0000 Subject: [PATCH 2214/2656] Enable flake8 for examples and tests --- .../tests/test_axi_lite_slave.py | 22 +++--- examples/dff/tests/dff_cocotb.py | 9 +-- .../tests/test_endian_swapper.py | 4 +- .../ping_tun_tap/tests/test_icmp_reply.py | 3 +- setup.cfg | 3 +- tests/pytest/test_binary_value.py | 9 +-- tests/test_cases/issue_120/issue_120.py | 3 +- tests/test_cases/issue_134/test_integers.py | 6 +- tests/test_cases/issue_134/test_reals.py | 9 +-- tests/test_cases/issue_142/issue_142.py | 2 +- tests/test_cases/issue_253/issue_253.py | 4 +- tests/test_cases/issue_330/issue_330.py | 5 +- tests/test_cases/issue_348/issue_348.py | 4 +- tests/test_cases/issue_588/issue_588.py | 4 +- tests/test_cases/issue_768_a/issue_768.py | 1 - tests/test_cases/issue_893/issue_893.py | 5 +- tests/test_cases/test_array/test_array.py | 28 +++---- .../test_array_simple/test_array_simple.py | 1 - tests/test_cases/test_avalon/test_avalon.py | 10 +-- .../test_avalon_stream/test_avalon_stream.py | 4 +- .../test_cocotb/test_generator_coroutines.py | 4 +- tests/test_cases/test_cocotb/test_handle.py | 3 +- .../test_cases/test_cocotb/test_scheduler.py | 2 +- tests/test_cases/test_cocotb/test_tests.py | 2 +- .../test_configuration/test_configurations.py | 1 - .../test_discovery/test_discovery.py | 17 ++-- .../test_discovery/test_vhdl_block.py | 5 +- .../test_discovery/test_vhdl_indexed_name.py | 6 +- tests/test_cases/test_exit_error/test_exit.py | 6 +- .../test_cases/test_external/test_external.py | 4 +- .../test_iteration.py | 3 +- .../test_iteration_es.py | 4 +- .../test_iteration_vhdl/test_iteration.py | 9 ++- .../test_cocotb_array.py | 79 +++++++++---------- tests/test_cases/test_plusargs/plusargs.py | 5 +- .../test_verilog_access.py | 4 +- .../test_vhdl_access/test_vhdl_access.py | 4 +- 37 files changed, 122 insertions(+), 172 deletions(-) mode change 100644 => 100755 tests/test_cases/test_avalon_stream/test_avalon_stream.py diff --git a/examples/axi_lite_slave/tests/test_axi_lite_slave.py b/examples/axi_lite_slave/tests/test_axi_lite_slave.py index c2e3ead7..dc07799b 100644 --- a/examples/axi_lite_slave/tests/test_axi_lite_slave.py +++ b/examples/axi_lite_slave/tests/test_axi_lite_slave.py @@ -1,12 +1,8 @@ import os -import sys import cocotb -import logging from cocotb.result import TestFailure from cocotb.result import TestSuccess from cocotb.clock import Clock -import time -from array import array as Array from cocotb.triggers import Timer from cocotb.drivers.amba import AXI4LiteMaster from cocotb.drivers.amba import AXIProtocolError @@ -20,7 +16,7 @@ def setup_dut(dut): cocotb.fork(Clock(dut.clk, CLK_PERIOD_NS, units='ns').start()) # Write to address 0 and verify that value got through -@cocotb.test(skip = False) +@cocotb.test() def write_address_0(dut): """Write to the register at address 0, verify the value has changed. @@ -32,7 +28,7 @@ def write_address_0(dut): """ # Reset - dut.rst <= 1 + dut.rst <= 1 dut.test_id <= 0 axim = AXI4LiteMaster(dut, "AXIML", dut.clk) setup_dut(dut) @@ -55,7 +51,7 @@ def write_address_0(dut): # Read back a value at address 0x01 -@cocotb.test(skip = False) +@cocotb.test() def read_address_1(dut): """Use cocotb to set the value of the register at address 0x01. Use AXIML to read the contents of that register and @@ -67,7 +63,7 @@ def read_address_1(dut): The value read from the register is the same as the value written. """ # Reset - dut.rst <= 1 + dut.rst <= 1 dut.test_id <= 1 axim = AXI4LiteMaster(dut, "AXIML", dut.clk) setup_dut(dut) @@ -92,7 +88,7 @@ def read_address_1(dut): -@cocotb.test(skip = False) +@cocotb.test() def write_and_read(dut): """Write to the register at address 0. Read back from that register and verify the value is the same. @@ -104,7 +100,7 @@ def write_and_read(dut): """ # Reset - dut.rst <= 1 + dut.rst <= 1 dut.test_id <= 2 axim = AXI4LiteMaster(dut, "AXIML", dut.clk) setup_dut(dut) @@ -130,7 +126,7 @@ def write_and_read(dut): dut._log.info("Write 0x%08X to address 0x%08X" % (int(value), ADDRESS)) -@cocotb.test(skip = False) +@cocotb.test() def write_fail(dut): """Attempt to write data to an address that doesn't exist. This test should fail. @@ -142,7 +138,7 @@ def write_fail(dut): to write to an invalid address. """ # Reset - dut.rst <= 1 + dut.rst <= 1 dut.test_id <= 3 axim = AXI4LiteMaster(dut, "AXIML", dut.clk) setup_dut(dut) @@ -162,7 +158,7 @@ def write_fail(dut): raise TestFailure("AXI bus should have raised an error when writing to \ an invalid address") -@cocotb.test(skip = False) +@cocotb.test() def read_fail(dut): """Attempt to read data from an address that doesn't exist. This test should fail. diff --git a/examples/dff/tests/dff_cocotb.py b/examples/dff/tests/dff_cocotb.py index 328e15a3..9bbcfbf1 100644 --- a/examples/dff/tests/dff_cocotb.py +++ b/examples/dff/tests/dff_cocotb.py @@ -10,13 +10,13 @@ # License: # ============================================================================== # Copyright 2016 Technische Universitaet Dresden - Germany -# Chair for VLSI-Design, Diagnostics and Architecture +# 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 +# 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, @@ -30,13 +30,12 @@ import cocotb from cocotb.clock import Clock from cocotb.decorators import coroutine -from cocotb.triggers import Timer, RisingEdge, ReadOnly +from cocotb.triggers import RisingEdge 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 # dut # ________ @@ -93,7 +92,7 @@ def __init__(self, dut, init_val): self.output_mon = BitMonitor(name="output", signal=dut.q, clk=dut.c) # Create a scoreboard on the outputs - self.expected_output = [ init_val ] # a list with init_val as the first element + self.expected_output = [init_val] # a list with init_val as the first element self.scoreboard = Scoreboard(dut) self.scoreboard.add_interface(self.output_mon, self.expected_output) diff --git a/examples/endian_swapper/tests/test_endian_swapper.py b/examples/endian_swapper/tests/test_endian_swapper.py index 35fcd1ec..b9f61d9a 100644 --- a/examples/endian_swapper/tests/test_endian_swapper.py +++ b/examples/endian_swapper/tests/test_endian_swapper.py @@ -192,5 +192,5 @@ def wavedrom_test(dut): yield tb.csr.read(0) yield RisingEdge(dut.clk) yield RisingEdge(dut.clk) - dut._log.info(waves.dumpj(header = {'text':'WaveDrom example', 'tick':0})) - waves.write('wavedrom.json', header = {'tick':0}, config = {'hscale':3}) + dut._log.info(waves.dumpj(header={'text':'WaveDrom example', 'tick':0})) + waves.write('wavedrom.json', header={'tick':0}, config={'hscale':3}) diff --git a/examples/ping_tun_tap/tests/test_icmp_reply.py b/examples/ping_tun_tap/tests/test_icmp_reply.py index f2089bd0..8f516a7b 100644 --- a/examples/ping_tun_tap/tests/test_icmp_reply.py +++ b/examples/ping_tun_tap/tests/test_icmp_reply.py @@ -24,7 +24,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import random import logging import fcntl @@ -62,7 +61,7 @@ def create_tun(name="tun0", ip="192.168.255.1"): # Errno 16 if tun device already exists, otherwise this # failed for different reason. if e.errno != 16: - raise e + raise e tun_num += 1 diff --git a/setup.cfg b/setup.cfg index b57975a9..905cdbd2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -2,10 +2,9 @@ exclude = bin documentation - examples makefiles - tests venv + examples/endian_swapper/cosim # TODO: fix some of these ignore = diff --git a/tests/pytest/test_binary_value.py b/tests/pytest/test_binary_value.py index 318da381..af803f39 100644 --- a/tests/pytest/test_binary_value.py +++ b/tests/pytest/test_binary_value.py @@ -3,7 +3,6 @@ # SPDX-License-Identifier: BSD-3-Clause import pytest -import cocotb from cocotb.binary import BinaryValue, BinaryRepresentation @@ -162,8 +161,8 @@ def test_init_short_binstr_value(): def test_defaults(): bin1 = BinaryValue(17) assert bin1.binaryRepresentation == BinaryRepresentation.UNSIGNED - assert bin1.big_endian == True - assert bin1._n_bits == None + assert bin1.big_endian is True + assert bin1._n_bits is None assert bin1.integer == 17 def test_index(): @@ -209,12 +208,12 @@ def test_general(): vec = BinaryValue(value=0, n_bits=16) assert vec.n_bits == 16 - assert vec.big_endian == True + assert vec.big_endian is True assert vec.integer == 0 # Checking single index assignment works as expected on a Little Endian BinaryValue vec = BinaryValue(value=0, n_bits=16, bigEndian=False) - assert vec.big_endian == False + assert vec.big_endian is False for idx in range(vec.n_bits): vec[idx] = '1' expected_value = 2**(idx+1) - 1 diff --git a/tests/test_cases/issue_120/issue_120.py b/tests/test_cases/issue_120/issue_120.py index f9e54353..62cd1a20 100644 --- a/tests/test_cases/issue_120/issue_120.py +++ b/tests/test_cases/issue_120/issue_120.py @@ -2,9 +2,8 @@ import cocotb from cocotb.clock import Clock -from cocotb.triggers import RisingEdge, Timer, ReadOnly +from cocotb.triggers import RisingEdge, ReadOnly from cocotb.result import TestFailure -from cocotb.binary import BinaryValue @cocotb.coroutine diff --git a/tests/test_cases/issue_134/test_integers.py b/tests/test_cases/issue_134/test_integers.py index 517186be..1d7dc08d 100644 --- a/tests/test_cases/issue_134/test_integers.py +++ b/tests/test_cases/issue_134/test_integers.py @@ -1,12 +1,8 @@ import logging -import random -import sys import cocotb -from cocotb.clock import Clock -from cocotb.triggers import RisingEdge, Timer, ReadOnly +from cocotb.triggers import Timer from cocotb.result import TestFailure -from cocotb.binary import BinaryValue @cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) diff --git a/tests/test_cases/issue_134/test_reals.py b/tests/test_cases/issue_134/test_reals.py index 8ab3920f..5712be87 100644 --- a/tests/test_cases/issue_134/test_reals.py +++ b/tests/test_cases/issue_134/test_reals.py @@ -1,12 +1,9 @@ import logging import random -import sys import cocotb -from cocotb.clock import Clock -from cocotb.triggers import RisingEdge, Timer, ReadOnly +from cocotb.triggers import Timer from cocotb.result import TestFailure -from cocotb.binary import BinaryValue @cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) @@ -21,7 +18,7 @@ def assign_double(dut): log.info("Setting the value %g" % val) dut.stream_in_real = val yield Timer(1) - yield Timer(1) # Workaround for VHPI scheduling - needs investigation + yield Timer(1) # FIXME: Workaround for VHPI scheduling - needs investigation got = float(dut.stream_out_real) log.info("Read back value %g" % got) if got != val: @@ -39,7 +36,7 @@ def assign_int(dut): log.info("Setting the value %i" % val) dut.stream_in_real <= val yield Timer(1) - yield Timer(1) # Workaround for VHPI scheduling - needs investigation + yield Timer(1) # FIXME: Workaround for VHPI scheduling - needs investigation got = dut.stream_out_real log.info("Read back value %d" % got) if got != float(val): diff --git a/tests/test_cases/issue_142/issue_142.py b/tests/test_cases/issue_142/issue_142.py index 10973e29..d0b287bd 100644 --- a/tests/test_cases/issue_142/issue_142.py +++ b/tests/test_cases/issue_142/issue_142.py @@ -2,7 +2,7 @@ import cocotb from cocotb.clock import Clock -from cocotb.triggers import RisingEdge, Timer, ReadOnly +from cocotb.triggers import RisingEdge from cocotb.result import TestFailure from cocotb.binary import BinaryValue diff --git a/tests/test_cases/issue_253/issue_253.py b/tests/test_cases/issue_253/issue_253.py index 25cf7ef3..630fde39 100644 --- a/tests/test_cases/issue_253/issue_253.py +++ b/tests/test_cases/issue_253/issue_253.py @@ -1,10 +1,8 @@ # A set of regression tests for open issues import cocotb -from cocotb.clock import Clock -from cocotb.triggers import RisingEdge, Timer, ReadOnly +from cocotb.triggers import Timer from cocotb.result import TestFailure -from cocotb.binary import BinaryValue @cocotb.coroutine def toggle_clock(dut): diff --git a/tests/test_cases/issue_330/issue_330.py b/tests/test_cases/issue_330/issue_330.py index c14bc50d..efbc7bd2 100644 --- a/tests/test_cases/issue_330/issue_330.py +++ b/tests/test_cases/issue_330/issue_330.py @@ -2,10 +2,8 @@ import cocotb import logging -from cocotb.clock import Clock -from cocotb.triggers import RisingEdge, Timer, ReadOnly +from cocotb.triggers import Timer from cocotb.result import TestFailure -from cocotb.binary import BinaryValue @cocotb.test(skip=cocotb.SIM_NAME in ["Icarus Verilog"]) def issue_330_direct(dut): @@ -38,4 +36,3 @@ def issue_330_iteration(dut): if count != 2: raise TestFailure("There should have been two members of the structure") - diff --git a/tests/test_cases/issue_348/issue_348.py b/tests/test_cases/issue_348/issue_348.py index 85539001..c4b51ad6 100644 --- a/tests/test_cases/issue_348/issue_348.py +++ b/tests/test_cases/issue_348/issue_348.py @@ -1,10 +1,8 @@ import cocotb from cocotb.log import SimLog -from cocotb.triggers import Timer, Edge, RisingEdge, FallingEdge, Join +from cocotb.triggers import Timer, Edge, RisingEdge, FallingEdge from cocotb.result import TestFailure -import sys - @cocotb.coroutine def clock_gen(signal, num): for x in range(num): diff --git a/tests/test_cases/issue_588/issue_588.py b/tests/test_cases/issue_588/issue_588.py index 311fad77..0c5e8825 100644 --- a/tests/test_cases/issue_588/issue_588.py +++ b/tests/test_cases/issue_588/issue_588.py @@ -2,9 +2,7 @@ # This is a very simple test; it just makes sure we can yield a list of both. import cocotb -from cocotb import triggers, result, utils, clock - -import sys +from cocotb import triggers, result, utils @cocotb.coroutine def sample_coroutine(dut): diff --git a/tests/test_cases/issue_768_a/issue_768.py b/tests/test_cases/issue_768_a/issue_768.py index d5cf3cb6..0c789dad 100644 --- a/tests/test_cases/issue_768_a/issue_768.py +++ b/tests/test_cases/issue_768_a/issue_768.py @@ -8,7 +8,6 @@ """ import cocotb from cocotb.triggers import Timer, ReadOnly -from cocotb.binary import BinaryValue # this line is different between the two files value = 0 diff --git a/tests/test_cases/issue_893/issue_893.py b/tests/test_cases/issue_893/issue_893.py index 020fdd74..1f43dd01 100644 --- a/tests/test_cases/issue_893/issue_893.py +++ b/tests/test_cases/issue_893/issue_893.py @@ -1,13 +1,12 @@ import cocotb -from cocotb.triggers import Timer, NullTrigger +from cocotb.triggers import Timer @cocotb.coroutine def coroutine_with_undef(): - new_variable = undefined_variable + new_variable = undefined_variable # noqa yield Timer(1, units='ns') @cocotb.test(expect_error=True) def fork_erroring_coroutine(dut): cocotb.fork(coroutine_with_undef()) yield Timer(10, units='ns') - diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py index 0a5a3a4b..98215dc3 100644 --- a/tests/test_cases/test_array/test_array.py +++ b/tests/test_cases/test_array/test_array.py @@ -6,7 +6,7 @@ import cocotb from cocotb.clock import Clock -from cocotb.triggers import Timer, RisingEdge +from cocotb.triggers import Timer from cocotb.result import TestError, TestFailure from cocotb.handle import HierarchyObject, HierarchyArrayObject, ModifiableObject, NonHierarchyIndexableObject, ConstantObject @@ -61,11 +61,11 @@ def test_read_write(dut): _check_logic(tlog, dut.param_logic_vec, 0xDA) if cocotb.LANGUAGE in ["vhdl"]: - _check_int (tlog, dut.param_bool, 1) - _check_int (tlog, dut.param_int , 6) + _check_int(tlog, dut.param_bool, 1) + _check_int(tlog, dut.param_int , 6) _check_real(tlog, dut.param_real, 3.14) - _check_int (tlog, dut.param_char, ord('p')) - _check_str (tlog, dut.param_str , b"ARRAYMOD") + _check_int(tlog, dut.param_char, ord('p')) + _check_str(tlog, dut.param_str , b"ARRAYMOD") if not cocotb.SIM_NAME.lower().startswith(("riviera")): _check_logic(tlog, dut.param_rec.a , 0) @@ -86,11 +86,11 @@ def test_read_write(dut): _check_logic(tlog, dut.const_logic_vec, 0x3D) if cocotb.LANGUAGE in ["vhdl"]: - _check_int (tlog, dut.const_bool, 0) - _check_int (tlog, dut.const_int , 12) + _check_int(tlog, dut.const_bool, 0) + _check_int(tlog, dut.const_int , 12) _check_real(tlog, dut.const_real, 6.28) - _check_int (tlog, dut.const_char, ord('c')) - _check_str (tlog, dut.const_str , b"MODARRAY") + _check_int(tlog, dut.const_char, ord('c')) + _check_str(tlog, dut.const_str , b"MODARRAY") if not cocotb.SIM_NAME.lower().startswith(("riviera")): _check_logic(tlog, dut.const_rec.a , 1) @@ -152,11 +152,11 @@ def test_read_write(dut): _check_logic(tlog, dut.sig_t4[3][7], 0xCC) if cocotb.LANGUAGE in ["vhdl"]: - _check_int (tlog, dut.port_bool_out, 1) - _check_int (tlog, dut.port_int_out , 5000) + _check_int(tlog, dut.port_bool_out, 1) + _check_int(tlog, dut.port_int_out , 5000) _check_real(tlog, dut.port_real_out, 22.54) - _check_int (tlog, dut.port_char_out, ord('Z')) - _check_str (tlog, dut.port_str_out , b"Testing") + _check_int(tlog, dut.port_char_out, ord('Z')) + _check_str(tlog, dut.port_str_out , b"Testing") _check_logic(tlog, dut.port_rec_out.a , 1) _check_logic(tlog, dut.port_rec_out.b[0] , 0x01) @@ -320,7 +320,7 @@ def test_discover_all(dut): # # DO NOT REMOVE. Aldec cannot iterate over the complex records due to bugs in the VPI interface. if (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")) and - cocotb.SIM_VERSION.startswith(("2016.02"))) : + cocotb.SIM_VERSION.startswith(("2016.02"))) : if len(dut._sub_handles) != 0: dut._sub_handles = {} diff --git a/tests/test_cases/test_array_simple/test_array_simple.py b/tests/test_cases/test_array_simple/test_array_simple.py index 5a029a54..fe81b37a 100644 --- a/tests/test_cases/test_array_simple/test_array_simple.py +++ b/tests/test_cases/test_array_simple/test_array_simple.py @@ -6,7 +6,6 @@ import logging import cocotb -from cocotb.binary import BinaryValue from cocotb.clock import Clock from cocotb.result import TestFailure from cocotb.triggers import Timer diff --git a/tests/test_cases/test_avalon/test_avalon.py b/tests/test_cases/test_avalon/test_avalon.py index 705b3fd8..34645751 100755 --- a/tests/test_cases/test_avalon/test_avalon.py +++ b/tests/test_cases/test_avalon/test_avalon.py @@ -33,16 +33,11 @@ Also used as regression test of cocotb capabilities """ -import logging - import cocotb from cocotb.drivers.avalon import AvalonMemory -from cocotb.triggers import (Timer, Join, RisingEdge, FallingEdge, Edge, - ReadOnly, ReadWrite) +from cocotb.triggers import Timer, RisingEdge from cocotb.clock import Clock -from cocotb.result import TestFailure, TestError, TestSuccess - - +from cocotb.result import TestFailure class BurstAvlReadTest(object): """ class to test avalon burst """ @@ -60,6 +55,7 @@ def __init__(self, dut, avlproperties={}): memory=self.memdict, readlatency_min=0, avl_properties=avlproperties) + @cocotb.coroutine def init_sig(self, burstcount_w, address): """ Initialize all signals """ diff --git a/tests/test_cases/test_avalon_stream/test_avalon_stream.py b/tests/test_cases/test_avalon_stream/test_avalon_stream.py old mode 100644 new mode 100755 index bf288b92..5145b825 --- a/tests/test_cases/test_avalon_stream/test_avalon_stream.py +++ b/tests/test_cases/test_avalon_stream/test_avalon_stream.py @@ -1,10 +1,8 @@ #!/usr/bin/env python """Test to demonstrate functionality of the avalon basic streaming interface""" -import logging import random import struct -import sys import cocotb from cocotb.drivers import BitDriver @@ -55,7 +53,7 @@ def test_avalon_stream(dut): tb.backpressure.start(wave()) for _ in range(20): - data = random.randint(0,(2^7)-1) + data = random.randint(0, (2**7)-1) yield tb.send_data(data) yield tb.clkedge diff --git a/tests/test_cases/test_cocotb/test_generator_coroutines.py b/tests/test_cases/test_cocotb/test_generator_coroutines.py index f720b41f..8f530e4a 100644 --- a/tests/test_cases/test_cocotb/test_generator_coroutines.py +++ b/tests/test_cases/test_cocotb/test_generator_coroutines.py @@ -50,7 +50,7 @@ def test_function_not_decorated(dut): except TypeError as exc: assert "yielded" in str(exc) assert "scheduler can't handle" in str(exc) - except: + else: raise TestFailure @@ -94,7 +94,7 @@ def test_yield_list(dut): @cocotb.coroutine def syntax_error(): yield Timer(100) - fail + fail # noqa @cocotb.test(expect_error=True) diff --git a/tests/test_cases/test_cocotb/test_handle.py b/tests/test_cases/test_cocotb/test_handle.py index 15516b67..4a64b996 100644 --- a/tests/test_cases/test_cocotb/test_handle.py +++ b/tests/test_cases/test_cocotb/test_handle.py @@ -26,7 +26,8 @@ def test_lessthan_raises_error(dut): ) # to make this a generator - if False: yield + if False: + yield @cocotb.test() diff --git a/tests/test_cases/test_cocotb/test_scheduler.py b/tests/test_cases/test_cocotb/test_scheduler.py index 200c586f..e5670695 100644 --- a/tests/test_cases/test_cocotb/test_scheduler.py +++ b/tests/test_cases/test_cocotb/test_scheduler.py @@ -10,7 +10,7 @@ """ import cocotb -from cocotb.triggers import Join, Timer, RisingEdge, Trigger, NullTrigger, ReadOnly +from cocotb.triggers import Join, Timer, RisingEdge, Trigger, NullTrigger from cocotb.result import TestFailure from cocotb.clock import Clock from common import clock_gen diff --git a/tests/test_cases/test_cocotb/test_tests.py b/tests/test_cases/test_cocotb/test_tests.py index 170f0662..391ad0b2 100644 --- a/tests/test_cases/test_cocotb/test_tests.py +++ b/tests/test_cases/test_cocotb/test_tests.py @@ -18,7 +18,7 @@ def test_syntax_error(dut): """Syntax error in the test""" yield clock_gen(dut.clk) - fail + fail # noqa @cocotb.test() diff --git a/tests/test_cases/test_configuration/test_configurations.py b/tests/test_cases/test_configuration/test_configurations.py index 9fdb135d..9321c471 100644 --- a/tests/test_cases/test_configuration/test_configurations.py +++ b/tests/test_cases/test_configuration/test_configurations.py @@ -36,4 +36,3 @@ def iterate(dut): yield Timer(100) sub_iterate(dut) yield Timer(100) - diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index fe73ff1b..e86a8c13 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -38,6 +38,7 @@ def recursive_discover(dut): """Discover absolutely everything in the DUT""" yield Timer(0) + def _discover(obj): for thing in obj: dut._log.info("Found %s (%s)", thing._name, type(thing)) @@ -118,10 +119,10 @@ def access_single_bit(dut): (str(dut.stream_in_data), len(dut.stream_in_data))) dut.stream_in_data[2] <= 1 yield Timer(10) - if dut.stream_out_data_comb.value.integer != (1<<2): + if dut.stream_out_data_comb.value.integer != (1 << 2): raise TestError("%s.%s != %d" % (str(dut.stream_out_data_comb), - dut.stream_out_data_comb.value.integer, (1<<2))) + dut.stream_out_data_comb.value.integer, (1 << 2))) @cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"], @@ -138,10 +139,10 @@ def access_single_bit_assignment(dut): (str(dut.stream_in_data), len(dut.stream_in_data))) dut.stream_in_data[2] = 1 yield Timer(10) - if dut.stream_out_data_comb.value.integer != (1<<2): + if dut.stream_out_data_comb.value.integer != (1 << 2): raise TestError("%s.%s != %d" % (str(dut.stream_out_data_comb), - dut.stream_out_data_comb.value.integer, (1<<2))) + dut.stream_out_data_comb.value.integer, (1 << 2))) @cocotb.test(expect_error=True) @@ -336,8 +337,8 @@ def access_boolean(dut): return - #if not isinstance(boolean, IntegerObject): - # raise TestFailure("dut.stream_in_boolean is not a IntegerObject is %s" % type(boolean)) + # if not isinstance(boolean, IntegerObject): + # raise TestFailure("dut.stream_in_boolean is not a IntegerObject is %s" % type(boolean)) try: bit = boolean[3] @@ -462,11 +463,11 @@ def type_check_verilog(dut): ] if cocotb.SIM_NAME.lower().startswith(("icarus")): - test_handles.append((dut.logic_a, "GPI_NET")) # https://github.com/steveicarus/iverilog/issues/312 + test_handles.append((dut.logic_a, "GPI_NET")) # https://github.com/steveicarus/iverilog/issues/312 else: test_handles.append((dut.logic_a, "GPI_REGISTER")) for handle in test_handles: - tlog.info("Handle %s" % (handle[0]._fullname,)) + tlog.info("Handle %s" % (handle[0]._fullname,)) if handle[0]._type != handle[1]: raise TestFailure("Expected %s found %s for %s" % (handle[1], handle[0]._type, handle[0]._fullname)) diff --git a/tests/test_cases/test_discovery/test_vhdl_block.py b/tests/test_cases/test_discovery/test_vhdl_block.py index 07c74bb4..9ccd4e12 100644 --- a/tests/test_cases/test_discovery/test_vhdl_block.py +++ b/tests/test_cases/test_discovery/test_vhdl_block.py @@ -26,11 +26,8 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import cocotb -import logging -from cocotb.handle import ModifiableObject from cocotb.triggers import Timer -from cocotb.result import TestError, TestFailure -from cocotb.handle import IntegerObject +from cocotb.result import TestFailure @cocotb.test() diff --git a/tests/test_cases/test_discovery/test_vhdl_indexed_name.py b/tests/test_cases/test_discovery/test_vhdl_indexed_name.py index 9c662a31..cd45ec17 100644 --- a/tests/test_cases/test_discovery/test_vhdl_indexed_name.py +++ b/tests/test_cases/test_discovery/test_vhdl_indexed_name.py @@ -24,17 +24,14 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import cocotb -import logging from cocotb.triggers import Timer -from cocotb.result import TestError, TestFailure -from cocotb.handle import IntegerObject - @cocotb.test() def index_name_iter(dut): """Access a non local indexed name""" yield Timer(0) total_count = 0 + def _discover(obj): count = 0 for thing in obj: @@ -45,4 +42,3 @@ def _discover(obj): total_count = _discover(dut.isample_module1) dut._log.info("Number of objects in non local vhpiIndexedNameK is %d" % total_count) - diff --git a/tests/test_cases/test_exit_error/test_exit.py b/tests/test_cases/test_exit_error/test_exit.py index b02a3f8c..e1e64848 100644 --- a/tests/test_cases/test_exit_error/test_exit.py +++ b/tests/test_cases/test_exit_error/test_exit.py @@ -3,6 +3,6 @@ @cocotb.test() def typosyntax_error(): - # this syntax error makes the whole file unimportable, so the file contents - # don't really matter. - yield Timer(100)a + # this syntax error makes the whole file unimportable, so the file contents + # don't really matter. + yield Timer(100)a # noqa diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index eb3ad39c..8a9ae90d 100755 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -34,11 +34,9 @@ """ import threading -import time import cocotb -import pdb from cocotb.result import TestFailure -from cocotb.triggers import Timer, Join, RisingEdge, ReadOnly, Edge, ReadWrite +from cocotb.triggers import Timer, RisingEdge, ReadOnly from cocotb.clock import Clock from cocotb.decorators import external from cocotb.utils import get_sim_time diff --git a/tests/test_cases/test_iteration_mixedlang/test_iteration.py b/tests/test_cases/test_iteration_mixedlang/test_iteration.py index 8ed645ab..27ae1be1 100644 --- a/tests/test_cases/test_iteration_mixedlang/test_iteration.py +++ b/tests/test_cases/test_iteration_mixedlang/test_iteration.py @@ -27,7 +27,7 @@ import cocotb from cocotb.triggers import Timer -from cocotb.result import TestError, TestFailure +from cocotb.result import TestFailure def recursive_dump(parent, log): @@ -104,4 +104,3 @@ def recursive_discovery_boundary(dut): tlog.info("Found a total of %d things", total) if total != pass_total: raise TestFailure("Expected %d objects but found %d" % (pass_total, total)) - diff --git a/tests/test_cases/test_iteration_verilog/test_iteration_es.py b/tests/test_cases/test_iteration_verilog/test_iteration_es.py index 92052a92..8afbf5fd 100644 --- a/tests/test_cases/test_iteration_verilog/test_iteration_es.py +++ b/tests/test_cases/test_iteration_verilog/test_iteration_es.py @@ -27,7 +27,7 @@ import cocotb from cocotb.triggers import Timer -from cocotb.result import TestError, TestFailure +from cocotb.result import TestFailure @cocotb.test(expect_fail=cocotb.SIM_NAME in ["Icarus Verilog"]) def recursive_discovery(dut): @@ -45,6 +45,7 @@ def recursive_discovery(dut): tlog = logging.getLogger("cocotb.test") yield Timer(100) + def dump_all_the_things(parent): count = 0 for thing in parent: @@ -69,4 +70,3 @@ def dual_iteration(dut): loop_two = cocotb.fork(iteration_loop(dut)) yield [loop_one.join(), loop_two.join()] - diff --git a/tests/test_cases/test_iteration_vhdl/test_iteration.py b/tests/test_cases/test_iteration_vhdl/test_iteration.py index 4b8bb54e..18413dda 100644 --- a/tests/test_cases/test_iteration_vhdl/test_iteration.py +++ b/tests/test_cases/test_iteration_vhdl/test_iteration.py @@ -35,7 +35,7 @@ def recursive_discovery(dut): Recursively discover every single object in the design """ if (cocotb.SIM_NAME.lower().startswith(("ncsim", "xmsim", "modelsim")) or - (cocotb.SIM_NAME.lower().startswith("riviera") and not cocotb.SIM_VERSION.startswith("2016.02"))): + (cocotb.SIM_NAME.lower().startswith("riviera") and not cocotb.SIM_VERSION.startswith("2016.02"))): # Finds regions, signal, generics, constants, varibles and ports. pass_total = 34569 else: @@ -43,6 +43,7 @@ def recursive_discovery(dut): tlog = logging.getLogger("cocotb.test") yield Timer(100) + def dump_all_the_things(parent): count = 0 for thing in parent: @@ -62,8 +63,8 @@ def discovery_all(dut): yield Timer(0) for thing in dut: thing._log.info("Found something: %s", thing._fullname) - #for subthing in thing: - # thing._log.info("Found something: %s" % thing._fullname) + # for subthing in thing: + # thing._log.info("Found something: %s" % thing._fullname) dut._log.info("length of dut.inst_acs is %d", len(dut.gen_acs)) item = dut.gen_acs[3] @@ -89,7 +90,7 @@ def get_clock(dut): yield Timer(1) dut.aclk <= 1 yield Timer(1) - if int(dut.aclk) is not 1: + if int(dut.aclk) != 1: raise TestFailure("dut.aclk is not what we expected (got %d)" % int(dut.aclk)) @cocotb.test() diff --git a/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py b/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py index f474fa8b..b28dd00d 100644 --- a/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py +++ b/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py @@ -1,12 +1,7 @@ -import logging -import random - import cocotb from cocotb.result import TestFailure from cocotb.triggers import Timer -from cocotb import simulator - @cocotb.test() def test_in_vect_packed(dut): yield Timer(10) @@ -14,7 +9,7 @@ def test_in_vect_packed(dut): dut.in_vect_packed = 0x5 yield Timer(10) print("Getting: dut.out_vect_packed type %s" % type(dut.out_vect_packed)) - if dut.out_vect_packed != 0x5: + if dut.out_vect_packed != 0x5: raise TestFailure("Failed to readback dut.out_vect_packed") @cocotb.test() @@ -23,8 +18,8 @@ def test_in_vect_unpacked(dut): print("Setting: dut.in_vect_unpacked type %s" % type(dut.in_vect_unpacked)) dut.in_vect_unpacked = [0x1, 0x0, 0x1] yield Timer(10) - print("Getting: dut.out_vect_unpacked type %s" % type( dut.out_vect_unpacked)) - if dut.out_vect_unpacked != [0x1, 0x0, 0x1]: + print("Getting: dut.out_vect_unpacked type %s" % type(dut.out_vect_unpacked)) + if dut.out_vect_unpacked != [0x1, 0x0, 0x1]: raise TestFailure("Failed to readback dut.out_vect_unpacked") @cocotb.test() @@ -33,8 +28,8 @@ def test_in_arr(dut): print("Setting: dut.in_arr type %s" % type(dut.in_arr)) dut.in_arr = 0x5 yield Timer(10) - print("Getting: dut.out_arr type %s" % type( dut.out_arr)) - if dut.out_arr != 0x5: + print("Getting: dut.out_arr type %s" % type(dut.out_arr)) + if dut.out_arr != 0x5: raise TestFailure("Failed to readback dut.out_arr") @cocotb.test() @@ -43,8 +38,8 @@ def test_in_2d_vect_packed_packed(dut): print("Setting: dut.in_2d_vect_packed_packed type %s" % type(dut.in_2d_vect_packed_packed)) dut.in_2d_vect_packed_packed = (0x5 << 6) | (0x5 << 3) | 0x5 yield Timer(10) - print("Getting: dut.out_2d_vect_packed_packed type %s" % type( dut.out_2d_vect_packed_packed)) - if dut.out_2d_vect_packed_packed != (0x5 << 6) | (0x5 << 3) | 0x5: + print("Getting: dut.out_2d_vect_packed_packed type %s" % type(dut.out_2d_vect_packed_packed)) + if dut.out_2d_vect_packed_packed != (0x5 << 6) | (0x5 << 3) | 0x5: raise TestFailure("Failed to readback dut.out_2d_vect_packed_packed") @cocotb.test() @@ -53,8 +48,8 @@ def test_in_2d_vect_packed_unpacked(dut): print("Setting: dut.in_2d_vect_packed_unpacked type %s" % type(dut.in_2d_vect_packed_unpacked)) dut.in_2d_vect_packed_unpacked = [0x5, 0x5, 0x5] yield Timer(10) - print("Getting: dut.out_2d_vect_packed_unpacked type %s" % type( dut.out_2d_vect_packed_unpacked)) - if dut.out_2d_vect_packed_unpacked != [0x5, 0x5, 0x5]: + print("Getting: dut.out_2d_vect_packed_unpacked type %s" % type(dut.out_2d_vect_packed_unpacked)) + if dut.out_2d_vect_packed_unpacked != [0x5, 0x5, 0x5]: raise TestFailure("Failed to readback dut.out_2d_vect_packed_unpacked") @cocotb.test() @@ -63,8 +58,8 @@ def test_in_2d_vect_unpacked_unpacked(dut): print("Setting: dut.in_2d_vect_unpacked_unpacked type %s" % type(dut.in_2d_vect_unpacked_unpacked)) dut.in_2d_vect_unpacked_unpacked = 3*[[0x1, 0x0, 0x1]] yield Timer(10) - print("Getting: dut.out_2d_vect_unpacked_unpacked type %s" % type( dut.out_2d_vect_unpacked_unpacked)) - if dut.out_2d_vect_unpacked_unpacked != 3*[[0x1, 0x0, 0x1]]: + print("Getting: dut.out_2d_vect_unpacked_unpacked type %s" % type(dut.out_2d_vect_unpacked_unpacked)) + if dut.out_2d_vect_unpacked_unpacked != 3*[[0x1, 0x0, 0x1]]: raise TestFailure("Failed to readback dut.out_2d_vect_unpacked_unpacked") @cocotb.test() @@ -73,8 +68,8 @@ def test_in_arr_packed(dut): print("Setting: dut.in_arr_packed type %s" % type(dut.in_arr_packed)) dut.in_arr_packed = 365 yield Timer(10) - print("Getting: dut.out_arr_packed type %s" % type( dut.out_arr_packed)) - if dut.out_arr_packed != 365: + print("Getting: dut.out_arr_packed type %s" % type(dut.out_arr_packed)) + if dut.out_arr_packed != 365: raise TestFailure("Failed to readback dut.out_arr_packed") @cocotb.test() @@ -83,8 +78,8 @@ def test_in_arr_unpacked(dut): print("Setting: dut.in_arr_unpackedtype %s" % type(dut.in_arr_unpacked)) dut.in_arr_unpacked = [0x5, 0x5, 0x5] yield Timer(10) - print("Getting: dut.out_arr_unpackedtype %s" % type( dut.out_arr_unpacked)) - if dut.out_arr_unpacked != [0x5, 0x5, 0x5]: + print("Getting: dut.out_arr_unpackedtype %s" % type(dut.out_arr_unpacked)) + if dut.out_arr_unpacked != [0x5, 0x5, 0x5]: raise TestFailure("Failed to readback dut.out_arr_unpacked") @cocotb.test() @@ -93,8 +88,8 @@ def test_in_2d_arr(dut): print("Setting: dut.in_2d_arr type %s" % type(dut.in_2d_arr)) dut.in_2d_arr = 365 yield Timer(10) - print("Getting: dut.out_2d_arr type %s" % type( dut.out_2d_arr)) - if dut.out_2d_arr != 365: + print("Getting: dut.out_2d_arr type %s" % type(dut.out_2d_arr)) + if dut.out_2d_arr != 365: raise TestFailure("Failed to readback dut.out_2d_arr") @cocotb.test() @@ -103,8 +98,8 @@ def test_in_vect_packed_packed_packed(dut): print("Setting: dut.in_vect_packed_packed_packed type %s" % type(dut.in_vect_packed_packed_packed)) dut.in_vect_packed_packed_packed = 95869805 yield Timer(10) - print("Getting: dut.out_vect_packed_packed_packed type %s" % type( dut.out_vect_packed_packed_packed)) - if dut.out_vect_packed_packed_packed != 95869805: + print("Getting: dut.out_vect_packed_packed_packed type %s" % type(dut.out_vect_packed_packed_packed)) + if dut.out_vect_packed_packed_packed != 95869805: raise TestFailure("Failed to readback dut.out_vect_packed_packed_packed") @cocotb.test() @@ -113,8 +108,8 @@ def test_in_vect_packed_packed_unpacked(dut): print("Setting: dut.in_vect_packed_packed_unpacked type %s" % type(dut.in_vect_packed_packed_unpacked)) dut.in_vect_packed_packed_unpacked = [95869805, 95869805, 95869805] yield Timer(10) - print("Getting: dut.out_vect_packed_packed_unpacked type %s" % type( dut.out_vect_packed_packed_unpacked)) - if dut.out_vect_packed_packed_unpacked != [365, 365, 365]: + print("Getting: dut.out_vect_packed_packed_unpacked type %s" % type(dut.out_vect_packed_packed_unpacked)) + if dut.out_vect_packed_packed_unpacked != [365, 365, 365]: raise TestFailure("Failed to readback dut.out_vect_packed_packed_unpacked") @cocotb.test() @@ -123,8 +118,8 @@ def test_in_vect_packed_unpacked_unpacked(dut): print("Setting: dut.in_vect_packed_unpacked_unpacked type %s" % type(dut.in_vect_packed_unpacked_unpacked)) dut.in_vect_packed_unpacked_unpacked = 3 *[3 * [5] ] yield Timer(10) - print("Getting: dut.out_vect_packed_unpacked_unpacked type %s" % type( dut.out_vect_packed_unpacked_unpacked)) - if dut.out_vect_packed_unpacked_unpacked != 3 *[3 * [5] ]: + print("Getting: dut.out_vect_packed_unpacked_unpacked type %s" % type(dut.out_vect_packed_unpacked_unpacked)) + if dut.out_vect_packed_unpacked_unpacked != 3 *[3 * [5] ]: raise TestFailure("Failed to readback dut.out_vect_packed_unpacked_unpacked") @cocotb.test() @@ -133,8 +128,8 @@ def test_in_vect_unpacked_unpacked_unpacked(dut): print("Setting: dut.in_vect_unpacked_unpacked_unpacked type %s" % type(dut.in_vect_unpacked_unpacked_unpacked)) dut.in_vect_unpacked_unpacked_unpacked = 3 *[3 * [[1, 0, 1]]] yield Timer(10) - print("Getting: dut.out_vect_unpacked_unpacked_unpacked type %s" % type( dut.out_vect_unpacked_unpacked_unpacked)) - if dut.out_vect_unpacked_unpacked_unpacked != 3 *[3 * [[1, 0, 1]]]: + print("Getting: dut.out_vect_unpacked_unpacked_unpacked type %s" % type(dut.out_vect_unpacked_unpacked_unpacked)) + if dut.out_vect_unpacked_unpacked_unpacked != 3 *[3 * [[1, 0, 1]]]: raise TestFailure("Failed to readback dut.out_vect_unpacked_unpacked_unpacked") @cocotb.test() @@ -143,8 +138,8 @@ def test_in_arr_packed_packed(dut): print("Setting: dut.in_arr_packed_packed type %s" % type(dut.in_arr_packed_packed)) dut.in_arr_packed_packed = (365 << 18) | (365 << 9) | (365) yield Timer(10) - print("Getting: dut.out_arr_packed_packed type %s" % type( dut.out_arr_packed_packed)) - if dut.out_arr_packed_packed != (365 << 18) | (365 << 9) | (365): + print("Getting: dut.out_arr_packed_packed type %s" % type(dut.out_arr_packed_packed)) + if dut.out_arr_packed_packed != (365 << 18) | (365 << 9) | (365): raise TestFailure("Failed to readback dut.out_arr_packed_packed") @cocotb.test() @@ -153,8 +148,8 @@ def test_in_arr_packed_unpacked(dut): print("Setting: dut.in_arr_packed_unpacked type %s" % type(dut.in_arr_packed_unpacked)) dut.in_arr_packed_unpacked = [365, 365, 365] yield Timer(10) - print("Getting: dut.out_arr_packed_unpacked type %s" % type( dut.out_arr_packed_unpacked)) - if dut.out_arr_packed_unpacked != [365, 365, 365]: + print("Getting: dut.out_arr_packed_unpacked type %s" % type(dut.out_arr_packed_unpacked)) + if dut.out_arr_packed_unpacked != [365, 365, 365]: raise TestFailure("Failed to readback dut.out_arr_packed_unpacked") @cocotb.test() @@ -163,8 +158,8 @@ def test_in_arr_unpacked_unpacked(dut): print("Setting: dut.in_arr_unpacked_unpacked type %s" % type(dut.in_arr_unpacked_unpacked)) dut.in_arr_unpacked_unpacked = 3 *[3 * [5] ] yield Timer(10) - print("Getting: dut.out_arr_unpacked_unpacked type %s" % type( dut.out_arr_unpacked_unpacked)) - if dut.out_arr_unpacked_unpacked != 3 *[3 * [5] ]: + print("Getting: dut.out_arr_unpacked_unpacked type %s" % type(dut.out_arr_unpacked_unpacked)) + if dut.out_arr_unpacked_unpacked != 3 *[3 * [5] ]: raise TestFailure("Failed to readback dut.out_arr_unpacked_unpacked") @cocotb.test() @@ -173,8 +168,8 @@ def test_in_2d_arr_packed(dut): print("Setting: dut.in_2d_arr_packed type %s" % type(dut.in_2d_arr_packed)) dut.in_2d_arr_packed = (365 << 18) | (365 << 9) | (365) yield Timer(10) - print("Getting: dut.out_2d_arr_packed type %s" % type( dut.out_2d_arr_packed)) - if dut.out_2d_arr_packed != (365 << 18) | (365 << 9) | (365): + print("Getting: dut.out_2d_arr_packed type %s" % type(dut.out_2d_arr_packed)) + if dut.out_2d_arr_packed != (365 << 18) | (365 << 9) | (365): raise TestFailure("Failed to readback dut.out_2d_arr_packed") @cocotb.test() @@ -183,8 +178,8 @@ def test_in_2d_arr_unpacked(dut): print("Setting: dut.in_2d_arr_unpacked type %s" % type(dut.in_2d_arr_unpacked)) dut.in_2d_arr_unpacked = [365, 365, 365] yield Timer(10) - print("Getting: dut.out_2d_arr_unpacked type %s" % type( dut.out_2d_arr_unpacked)) - if dut.out_2d_arr_unpacked != [365, 365, 365]: + print("Getting: dut.out_2d_arr_unpacked type %s" % type(dut.out_2d_arr_unpacked)) + if dut.out_2d_arr_unpacked != [365, 365, 365]: raise TestFailure("Failed to readback dut.out_2d_arr_unpacked") @cocotb.test() @@ -193,6 +188,6 @@ def test_in_3d_arr(dut): print("Setting: dut.in_3d_arr type %s" % type(dut.in_3d_arr)) dut.in_3d_arr = (365 << 18) | (365 << 9) | (365) yield Timer(10) - print("Getting: dut.out_3d_arr type %s" % type( dut.out_3d_arr)) - if dut.out_3d_arr != (365 << 18) | (365 << 9) | (365): + print("Getting: dut.out_3d_arr type %s" % type(dut.out_3d_arr)) + if dut.out_3d_arr != (365 << 18) | (365 << 9) | (365): raise TestFailure("Failed to readback dut.out_3d_arr") diff --git a/tests/test_cases/test_plusargs/plusargs.py b/tests/test_cases/test_plusargs/plusargs.py index 9af9f981..9d20bdc3 100644 --- a/tests/test_cases/test_plusargs/plusargs.py +++ b/tests/test_cases/test_plusargs/plusargs.py @@ -32,11 +32,8 @@ from __future__ import print_function import cocotb -from cocotb.decorators import coroutine from cocotb.result import TestFailure -from cocotb.triggers import Timer, Edge, Event - -import sys +from cocotb.triggers import Timer @cocotb.test() diff --git a/tests/test_cases/test_verilog_access/test_verilog_access.py b/tests/test_cases/test_verilog_access/test_verilog_access.py index 596985c9..92270f95 100644 --- a/tests/test_cases/test_verilog_access/test_verilog_access.py +++ b/tests/test_cases/test_verilog_access/test_verilog_access.py @@ -26,9 +26,9 @@ import logging import cocotb -from cocotb.handle import HierarchyObject, ModifiableObject, RealObject, IntegerObject, ConstantObject +from cocotb.handle import HierarchyObject, ModifiableObject from cocotb.triggers import Timer -from cocotb.result import TestError, TestFailure +from cocotb.result import TestFailure @cocotb.test() def port_not_hierarchy(dut): diff --git a/tests/test_cases/test_vhdl_access/test_vhdl_access.py b/tests/test_cases/test_vhdl_access/test_vhdl_access.py index b9979f61..47640e15 100644 --- a/tests/test_cases/test_vhdl_access/test_vhdl_access.py +++ b/tests/test_cases/test_vhdl_access/test_vhdl_access.py @@ -26,9 +26,9 @@ import logging import cocotb -from cocotb.handle import HierarchyObject, ModifiableObject, RealObject, IntegerObject, ConstantObject, EnumObject +from cocotb.handle import HierarchyObject, ModifiableObject, IntegerObject, ConstantObject, EnumObject from cocotb.triggers import Timer -from cocotb.result import TestError, TestFailure +from cocotb.result import TestFailure @cocotb.test() def check_enum_object(dut): From 29f09020c674483273c2e2f98f3ea8893ade17bf Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sun, 3 May 2020 14:37:19 +0200 Subject: [PATCH 2215/2656] Remove E131 from flake8 ignore list No occurrences has to be fixed. Refs #1547. --- setup.cfg | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 905cdbd2..8221432d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -12,7 +12,6 @@ ignore = E126 # continuation line over-indented for hanging indent E127 # continuation line over-indented for visual indent E128 # continuation line under-indented for visual indent - E131 # continuation line unaligned for hanging indent E202 # whitespace before ')' E203 # whitespace before ',' / ':' E221 # multiple spaces before operator From ec69145fe3468d743769f8343cc13f1f485cdf50 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sun, 3 May 2020 14:33:41 +0200 Subject: [PATCH 2216/2656] Remove E123 from flake8 ignore list Fix occurrence of this error. Refs #1547. --- cocotb/binary.py | 4 ++-- cocotb/drivers/avalon.py | 2 +- cocotb/handle.py | 2 +- setup.cfg | 1 - 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index a1a4b07b..d22145d8 100755 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -129,13 +129,13 @@ def __init__(self, value=None, n_bits=None, bigEndian=True, BinaryRepresentation.UNSIGNED : self._convert_to_unsigned , BinaryRepresentation.SIGNED_MAGNITUDE : self._convert_to_signed_mag , BinaryRepresentation.TWOS_COMPLEMENT : self._convert_to_twos_comp , - } + } self._convert_from = { BinaryRepresentation.UNSIGNED : self._convert_from_unsigned , BinaryRepresentation.SIGNED_MAGNITUDE : self._convert_from_signed_mag , BinaryRepresentation.TWOS_COMPLEMENT : self._convert_from_twos_comp , - } + } if value is not None: self.assign(value) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 2057eac5..3425574f 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -242,7 +242,7 @@ class AvalonMemory(BusDriver): "readLatency": 1, # number of cycles "WriteBurstWaitReq": True, # generate random waitrequest "MaxWaitReqLen": 4, # maximum value of waitrequest - } + } def __init__(self, entity, name, clock, readlatency_min=1, readlatency_max=1, memory=None, avl_properties={}, **kwargs): diff --git a/cocotb/handle.py b/cocotb/handle.py index 3a4aab9d..130a7aab 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -62,7 +62,7 @@ class SimHandleBase: "log" : "_log", "fullname" : "_fullname", "name" : "_name", - } + } def __init__(self, handle, path): """ diff --git a/setup.cfg b/setup.cfg index 8221432d..4436f787 100644 --- a/setup.cfg +++ b/setup.cfg @@ -8,7 +8,6 @@ exclude = # TODO: fix some of these ignore = - E123 # closing bracket does not match indentation of opening bracket's line E126 # continuation line over-indented for hanging indent E127 # continuation line over-indented for visual indent E128 # continuation line under-indented for visual indent From 6ee921fca74883ac2560a5474ceceab8329122ca Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sun, 3 May 2020 14:11:00 +0200 Subject: [PATCH 2217/2656] Remove E302, E303, E305 from flake8 ignore list Fix occurrences of these errors. Refs #1547. --- cocotb/_py_compat.py | 1 + cocotb/binary.py | 2 ++ cocotb/bus.py | 2 ++ cocotb/clock.py | 1 + cocotb/decorators.py | 7 +++++++ cocotb/drivers/__init__.py | 2 ++ cocotb/drivers/amba.py | 1 + cocotb/drivers/avalon.py | 4 +--- cocotb/handle.py | 19 ++++++++++++++++++- cocotb/log.py | 1 - cocotb/monitors/avalon.py | 2 ++ cocotb/result.py | 3 +++ cocotb/scheduler.py | 7 ++++--- cocotb/triggers.py | 4 ++++ cocotb/utils.py | 4 +--- cocotb/wavedrom.py | 1 + cocotb_build_libs.py | 2 +- .../tests/test_axi_lite_slave.py | 4 +++- examples/dff/tests/dff_cocotb.py | 2 ++ examples/simple_dff/test_dff.py | 1 + setup.cfg | 3 --- setup.py | 3 +++ tests/pytest/test_binary_value.py | 4 ++++ tests/test_cases/issue_253/issue_253.py | 4 ++++ tests/test_cases/issue_330/issue_330.py | 2 ++ tests/test_cases/issue_348/issue_348.py | 4 +++- tests/test_cases/issue_588/issue_588.py | 2 ++ tests/test_cases/issue_768_a/issue_768.py | 1 + tests/test_cases/issue_768_b/issue_768.py | 1 + tests/test_cases/issue_892/issue_892.py | 2 ++ tests/test_cases/issue_893/issue_893.py | 2 ++ tests/test_cases/issue_957/issue_957.py | 3 +++ tests/test_cases/test_array/test_array.py | 11 ++++++++++- .../test_array_simple/test_array_simple.py | 3 +++ tests/test_cases/test_avalon/test_avalon.py | 2 ++ .../test_avalon_stream/test_avalon_stream.py | 3 +++ .../test_cases/test_cocotb/test_deprecated.py | 1 - .../test_configuration/test_configurations.py | 2 ++ .../test_discovery/test_discovery.py | 9 +++++++++ .../test_discovery/test_vhdl_indexed_name.py | 1 + tests/test_cases/test_exit_error/test_exit.py | 1 + .../test_cases/test_external/test_external.py | 12 +++++++++++- .../test_iteration_es.py | 3 +++ .../test_iteration_vhdl/test_iteration.py | 5 +++++ .../test_cocotb_array.py | 19 +++++++++++++++++++ .../test_verilog_access.py | 1 + .../test_vhdl_access/test_vhdl_access.py | 3 +++ 47 files changed, 157 insertions(+), 20 deletions(-) diff --git a/cocotb/_py_compat.py b/cocotb/_py_compat.py index 687930c7..d6a3a092 100644 --- a/cocotb/_py_compat.py +++ b/cocotb/_py_compat.py @@ -51,6 +51,7 @@ def __enter__(self): def __exit__(self, *excinfo): pass + # On python 3.7 onwards, `dict` is guaranteed to preserve insertion order. # Since `OrderedDict` is a little slower that `dict`, we prefer the latter # when possible. diff --git a/cocotb/binary.py b/cocotb/binary.py index d22145d8..74b89a6e 100755 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -33,6 +33,7 @@ resolve_x_to = os.getenv('COCOTB_RESOLVE_X', "VALUE_ERROR") + def resolve(string): for char in BinaryValue._resolve_to_0: string = string.replace(char, "0") @@ -715,6 +716,7 @@ def __setitem__(self, key, val): else: self.binstr = self.binstr[0:self._n_bits-index-1] + val + self.binstr[self._n_bits-index:self._n_bits] + if __name__ == "__main__": import doctest doctest.testmod() diff --git a/cocotb/bus.py b/cocotb/bus.py index 20f8e182..c9ed3dd3 100755 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -32,6 +32,7 @@ """ from cocotb.handle import _AssignmentResult + def _build_sig_attr_dict(signals): if isinstance(signals, dict): return signals @@ -51,6 +52,7 @@ class Bus: TODO: Support for ``struct``/``record`` ports where signals are member names. """ + def __init__(self, entity, name, signals, optional_signals=[], bus_separator="_", array_idx=None): """ Args: diff --git a/cocotb/clock.py b/cocotb/clock.py index 438d7e31..1bbb69c5 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -36,6 +36,7 @@ class BaseClock: """Base class to derive from.""" + def __init__(self, signal): self.signal = signal diff --git a/cocotb/decorators.py b/cocotb/decorators.py index eadb289a..00029b99 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -59,6 +59,7 @@ def public(f): all.append(f.__name__) return f + public(public) # Emulate decorating ourself @@ -69,6 +70,7 @@ class CoroutineComplete(Exception): exception that the scheduler catches and the callbacks are attached here. """ + def __init__(self, text=""): Exception.__init__(self, text) @@ -206,6 +208,7 @@ class RunningCoroutine(RunningTask): All this class does is provide some extra attributes. """ + def __init__(self, inst, parent): RunningTask.__init__(self, inst) self._parent = parent @@ -323,6 +326,7 @@ class function: in other words, to internally block while externally appear to yield. """ + def __init__(self, func): self._coro = cocotb.coroutine(func) @@ -338,6 +342,7 @@ def __get__(self, obj, owner=None): and standalone functions""" return type(self)(self._coro._func.__get__(obj, owner)) + @public class external: """Decorator to apply to an external function to enable calling from cocotb. @@ -347,6 +352,7 @@ class external: called. Scope for this to be streamlined to a queue in future. """ + def __init__(self, func): self._func = func self._log = SimLog("cocotb.external.%s" % self._func.__qualname__, id(self)) @@ -391,6 +397,7 @@ class hook(coroutine, metaclass=_decorator_helper): All hooks are run at the beginning of a cocotb test suite, prior to any test code being run.""" + def __init__(self, f): super(hook, self).__init__(f) self.im_hook = True diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index b9fd4118..d9fd84c5 100755 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -44,6 +44,7 @@ class BitDriver: Useful for exercising ready/valid flags. """ + def __init__(self, signal, clk, generator=None): self._signal = signal self._clk = clk @@ -95,6 +96,7 @@ class Driver: The driver is responsible for serializing transactions onto the physical pins of the interface. This may consume simulation time. """ + def __init__(self): """Constructor for a driver instance.""" self._pending = Event(name="Driver._pending") diff --git a/cocotb/drivers/amba.py b/cocotb/drivers/amba.py index 08f64503..41ffab48 100644 --- a/cocotb/drivers/amba.py +++ b/cocotb/drivers/amba.py @@ -206,6 +206,7 @@ def read(self, address, sync=True): def __len__(self): return 2**len(self.bus.ARADDR) + class AXI4Slave(BusDriver): ''' AXI4 Slave diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 3425574f..572a10f0 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -58,7 +58,6 @@ class AvalonMM(BusDriver): "writedata", "readdatavalid", "byteenable", "cs"] - def __init__(self, entity, name, clock, **kwargs): BusDriver.__init__(self, entity, name, clock, **kwargs) self._can_read = False @@ -95,6 +94,7 @@ def write(self, address, value): class AvalonMaster(AvalonMM): """Avalon Memory Mapped Interface (Avalon-MM) Master.""" + def __init__(self, entity, name, clock, **kwargs): AvalonMM.__init__(self, entity, name, clock, **kwargs) self.log.debug("AvalonMaster created") @@ -308,7 +308,6 @@ def __init__(self, entity, name, clock, readlatency_min=1, else: self.bus.waitrequest <= 0 - if hasattr(self.bus, "readdatavalid"): self.bus.readdatavalid.setimmediatevalue(0) @@ -383,7 +382,6 @@ def _waitrequest(self): self.bus.waitrequest <= 0 - @coroutine def _respond(self): """Coroutine to respond to the actual requests.""" diff --git a/cocotb/handle.py b/cocotb/handle.py index 130a7aab..e96e1b12 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -158,6 +158,7 @@ class RegionObject(SimHandleBase): Region objects don't have values, they are effectively scopes or namespaces. """ + def __init__(self, handle, path): SimHandleBase.__init__(self, handle, path) self._discovered = False @@ -245,7 +246,6 @@ def __get_sub_handle_by_name(self, name): self._sub_handles[name] = sub_handle return sub_handle - def __setattr__(self, name, value): """Provide transparent access to signals via the hierarchy. @@ -357,6 +357,7 @@ class _AssignmentResult: An object that exists solely to provide an error message if the caller is not aware of cocotb's meaning of ``<=``. """ + def __init__(self, signal, value): self._signal = signal self._value = value @@ -447,6 +448,7 @@ class ConstantObject(NonHierarchyObject): The value is cached in the class since it is fixed at elaboration time and won't change within a simulation. """ + def __init__(self, handle, path, handle_type): """ Args: @@ -510,6 +512,7 @@ class NonHierarchyIndexableObject(NonHierarchyObject): - **Wrong**: ``dut.some_array.value[0] = 1`` (gets value as a list then updates index 0) - **Correct**: ``dut.some_array[0].value = 1`` """ + def __init__(self, handle, path): NonHierarchyObject.__init__(self, handle, path) self._range = self._handle.get_range() @@ -589,36 +592,48 @@ def loads(self): """An iterator for gathering all loads on a signal.""" return self._handle.iterate(simulator.LOADS) + class _SetAction: """Base class representing the type of action used while write-accessing a handle.""" pass + class _SetValueAction(_SetAction): __slots__ = ("value",) """Base class representing the type of action used while write-accessing a handle with a value.""" + def __init__(self, value): self.value = value + class Deposit(_SetValueAction): """Action used for placing a value into a given handle.""" + def _as_gpi_args_for(self, hdl): return self.value, 0 # GPI_DEPOSIT + class Force(_SetValueAction): """Action used to force a handle to a given value until a release is applied.""" + def _as_gpi_args_for(self, hdl): return self.value, 1 # GPI_FORCE + class Freeze(_SetAction): """Action used to make a handle keep its current value until a release is used.""" + def _as_gpi_args_for(self, hdl): return hdl.value, 1 # GPI_FORCE + class Release(_SetAction): """Action used to stop the effects of a previously applied force/freeze action.""" + def _as_gpi_args_for(self, hdl): return 0, 2 # GPI_RELEASE + class ModifiableObject(NonConstantObject): """Base class for simulator objects whose values can be modified.""" @@ -827,8 +842,10 @@ def _set_value(self, value, call_sim): def value(self) -> bytes: return self._handle.get_signal_val_str() + _handle2obj = {} + def SimHandle(handle, path=None): """Factory function to create the correct type of `SimHandle` object. diff --git a/cocotb/log.py b/cocotb/log.py index ac3c0e13..bf251b7d 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -78,7 +78,6 @@ def default_config(): else: hdlr.setFormatter(SimLogFormatter()) - logging.setLoggerClass(SimBaseLog) # For backwards compatibility logging.basicConfig() logging.getLogger().handlers = [hdlr] # overwrite default handlers diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index b664c102..bd8b0b2e 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -40,6 +40,7 @@ from cocotb.triggers import RisingEdge, ReadOnly from cocotb.binary import BinaryValue + class AvalonProtocolError(Exception): pass @@ -234,6 +235,7 @@ def valid(): "In-Packet Timeout. Didn't receive any valid data for %d cycles!" % invalid_cyclecount) + class AvalonSTPktsWithChannel(AvalonSTPkts): """Packetized AvalonST bus using channel. diff --git a/cocotb/result.py b/cocotb/result.py index 3cec0acd..a7467c8f 100644 --- a/cocotb/result.py +++ b/cocotb/result.py @@ -33,6 +33,7 @@ """Exceptions and functions for simulation result handling.""" + def raise_error(obj, msg): """Create a :exc:`TestError` exception and raise it after printing a traceback. @@ -102,6 +103,7 @@ def __init__(self, retval): class TestComplete(Exception): """Exception showing that the test was completed. Sub-exceptions detail the exit status.""" + def __init__(self, *args, **kwargs): super(TestComplete, self).__init__(*args, **kwargs) self.stdout = StringIO() @@ -110,6 +112,7 @@ def __init__(self, *args, **kwargs): class ExternalException(Exception): """Exception thrown by :class:`cocotb.external` functions.""" + def __init__(self, exception): self.exception = exception diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index c93c91c8..ec21de07 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -73,6 +73,7 @@ class InternalError(RuntimeError): class profiling_context: """ Context manager that profiles its contents """ + def __enter__(self): _profile.enable() @@ -82,12 +83,14 @@ def __exit__(self, *excinfo): from cocotb import outcomes + class external_state: INIT = 0 RUNNING = 1 PAUSED = 2 EXITED = 3 + @cocotb.decorators.public class external_waiter: @@ -150,6 +153,7 @@ def thread_wait(self): return self.state + class Scheduler: """The main scheduler. @@ -347,7 +351,6 @@ def react(self, trigger): finally: self._is_reacting = False - def _event_loop(self, trigger): """ Run an event loop triggered by the given trigger. @@ -422,7 +425,6 @@ def _event_loop(self, trigger): del trigger continue - if _debug: debugstr = "\n\t".join([coro.__qualname__ for coro in scheduling]) if len(scheduling): @@ -463,7 +465,6 @@ def _event_loop(self, trigger): self.log.debug("All coroutines scheduled, handing control back" " to simulator") - def unschedule(self, coro): """Unschedule a coroutine. Unprime any pending triggers""" diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 0b759708..c7b465dc 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -59,6 +59,7 @@ def _pointer_str(obj): class TriggerException(Exception): pass + class Trigger(Awaitable): """Base class to derive from.""" @@ -160,6 +161,7 @@ def unprime(self): class Timer(GPITrigger): """Fires after the specified simulation time period has elapsed.""" + def __init__(self, time_ps, units=None): """ Args: @@ -540,6 +542,7 @@ class NullTrigger(Trigger): Primarily for internal scheduler use. """ + def __init__(self, name=None, outcome=None): super(NullTrigger, self).__init__() self._callback = None @@ -766,6 +769,7 @@ def on_done(ret): class ClockCycles(Waitable): """Fires after *num_cycles* transitions of *signal* from ``0`` to ``1``.""" + def __init__(self, signal, num_cycles, rising=True): """ Args: diff --git a/cocotb/utils.py b/cocotb/utils.py index 187dc896..5d5c59da 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -295,7 +295,6 @@ def highlight(string: str, colour=ANSI.COLOR_HILITE_HEXDIFF_DEFAULT) -> str: else: return string - x_is_str = isinstance(x, str) y_is_str = isinstance(y, str) if x_is_str or y_is_str: @@ -426,8 +425,6 @@ def highlight(string: str, colour=ANSI.COLOR_HILITE_HEXDIFF_DEFAULT) -> str: return rs - - class ParametrizedSingleton(type): """A metaclass that allows class construction to reuse an existing instance. @@ -503,6 +500,7 @@ class lazy_property: This should be used for expensive members of objects that are not always used. """ + def __init__(self, fget): self.fget = fget diff --git a/cocotb/wavedrom.py b/cocotb/wavedrom.py index fc6dbb51..7b21af2c 100644 --- a/cocotb/wavedrom.py +++ b/cocotb/wavedrom.py @@ -33,6 +33,7 @@ class Wavedrom: """Base class for a WaveDrom compatible tracer.""" + def __init__(self, obj): self._hdls = OrderedDict() diff --git a/cocotb_build_libs.py b/cocotb_build_libs.py index d5a9eb87..29c4ec1a 100755 --- a/cocotb_build_libs.py +++ b/cocotb_build_libs.py @@ -188,8 +188,8 @@ def _get_python_lib(): # Make PRI* format macros available with C++11 compiler but older libc, e.g. on RHEL6. _extra_cxx_compile_args += ["-D__STDC_FORMAT_MACROS"] -def _get_common_lib_ext(include_dir, share_lib_dir): +def _get_common_lib_ext(include_dir, share_lib_dir): """ Defines common libraries. diff --git a/examples/axi_lite_slave/tests/test_axi_lite_slave.py b/examples/axi_lite_slave/tests/test_axi_lite_slave.py index dc07799b..288e4d93 100644 --- a/examples/axi_lite_slave/tests/test_axi_lite_slave.py +++ b/examples/axi_lite_slave/tests/test_axi_lite_slave.py @@ -12,6 +12,7 @@ MODULE_PATH = os.path.join(os.path.dirname(__file__), os.pardir, "hdl") MODULE_PATH = os.path.abspath(MODULE_PATH) + def setup_dut(dut): cocotb.fork(Clock(dut.clk, CLK_PERIOD_NS, units='ns').start()) @@ -87,7 +88,6 @@ def read_address_1(dut): dut._log.info("Read: 0x%08X From Address: 0x%08X" % (int(value), ADDRESS)) - @cocotb.test() def write_and_read(dut): """Write to the register at address 0. @@ -126,6 +126,7 @@ def write_and_read(dut): dut._log.info("Write 0x%08X to address 0x%08X" % (int(value), ADDRESS)) + @cocotb.test() def write_fail(dut): """Attempt to write data to an address that doesn't exist. This test @@ -158,6 +159,7 @@ def write_fail(dut): raise TestFailure("AXI bus should have raised an error when writing to \ an invalid address") + @cocotb.test() def read_fail(dut): """Attempt to read data from an address that doesn't exist. This test diff --git a/examples/dff/tests/dff_cocotb.py b/examples/dff/tests/dff_cocotb.py index 9bbcfbf1..ddaea016 100644 --- a/examples/dff/tests/dff_cocotb.py +++ b/examples/dff/tests/dff_cocotb.py @@ -45,8 +45,10 @@ # --|>c | # |______| + class BitMonitor(Monitor): """Observe a single-bit input or output of the DUT.""" + def __init__(self, name, signal, clk, callback=None, event=None): self.name = name self.signal = signal diff --git a/examples/simple_dff/test_dff.py b/examples/simple_dff/test_dff.py index e3e3c94e..d108ff4b 100644 --- a/examples/simple_dff/test_dff.py +++ b/examples/simple_dff/test_dff.py @@ -6,6 +6,7 @@ from cocotb.clock import Clock from cocotb.triggers import FallingEdge + @cocotb.test() async def test_dff_simple(dut): """ Test that d propagates to q """ diff --git a/setup.cfg b/setup.cfg index 4436f787..d8338d29 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,9 +19,6 @@ ignore = E228 # missing whitespace around modulo operator E231 # missing whitespace after ... E241 # multiple spaces after ... - E302 # expected 2 blank lines, found ... - E303 # too many blank lines (...) - E305 # expected 2 blank lines after class or function definition, found ... E402 # module level import not at top of file E501 # line too long (... > 79 characters) E741 # ambiguous variable name 'l' diff --git a/setup.py b/setup.py index 6c449c2f..9d3e2588 100755 --- a/setup.py +++ b/setup.py @@ -57,10 +57,12 @@ # We can still import other files next to setup.py, as long as they're in MANIFEST.in from cocotb_build_libs import get_ext, build_ext + def read_file(fname): with open(path.join(path.dirname(__file__), fname), encoding='utf8') as f: return f.read() + def package_files(directory): paths = [] for (fpath, directories, filenames) in walk(directory): @@ -68,6 +70,7 @@ def package_files(directory): paths.append(path.join('..', fpath, filename)) return paths + # this sets the __version__ variable exec(read_file(path.join('cocotb', '_version.py'))) diff --git a/tests/pytest/test_binary_value.py b/tests/pytest/test_binary_value.py index af803f39..1f5f0b61 100644 --- a/tests/pytest/test_binary_value.py +++ b/tests/pytest/test_binary_value.py @@ -158,6 +158,7 @@ def test_init_short_binstr_value(): bin4.binstr == "10000000" bin4.integer == 1 + def test_defaults(): bin1 = BinaryValue(17) assert bin1.binaryRepresentation == BinaryRepresentation.UNSIGNED @@ -165,6 +166,7 @@ def test_defaults(): assert bin1._n_bits is None assert bin1.integer == 17 + def test_index(): bin1 = BinaryValue(value=-980, n_bits=32, bigEndian=False, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) @@ -200,6 +202,7 @@ def test_index(): with pytest.raises(IndexError): bin3[2:-2] + def test_general(): """ Test out the cocotb supplied BinaryValue class for manipulating @@ -226,6 +229,7 @@ def test_general(): assert vec.binstr == '1111111100110101' assert vec[7:0].binstr == '00110101' + def test_backwards_compatibility(): """ Test backwards-compatibility wrappers for BinaryValue diff --git a/tests/test_cases/issue_253/issue_253.py b/tests/test_cases/issue_253/issue_253.py index 630fde39..7f66294c 100644 --- a/tests/test_cases/issue_253/issue_253.py +++ b/tests/test_cases/issue_253/issue_253.py @@ -4,6 +4,7 @@ from cocotb.triggers import Timer from cocotb.result import TestFailure + @cocotb.coroutine def toggle_clock(dut): dut.clk = 0 @@ -15,14 +16,17 @@ def toggle_clock(dut): if dut.clk.value.integer != 1: raise TestFailure("Clock not set to 1 as expected") + @cocotb.test() def issue_253_empty(dut): yield toggle_clock(dut) + @cocotb.test() def issue_253_none(dut): yield toggle_clock(dut) + @cocotb.test() def issue_253_notset(dut): yield toggle_clock(dut) diff --git a/tests/test_cases/issue_330/issue_330.py b/tests/test_cases/issue_330/issue_330.py index efbc7bd2..2d2fdf49 100644 --- a/tests/test_cases/issue_330/issue_330.py +++ b/tests/test_cases/issue_330/issue_330.py @@ -5,6 +5,7 @@ from cocotb.triggers import Timer from cocotb.result import TestFailure + @cocotb.test(skip=cocotb.SIM_NAME in ["Icarus Verilog"]) def issue_330_direct(dut): """ @@ -18,6 +19,7 @@ def issue_330_direct(dut): tlog.info("Value of inout_if => a_in = %s ; b_out = %s" % (structure.a_in, structure.b_out)) + @cocotb.test(skip=cocotb.SIM_NAME in ["Icarus Verilog"]) def issue_330_iteration(dut): """ diff --git a/tests/test_cases/issue_348/issue_348.py b/tests/test_cases/issue_348/issue_348.py index c4b51ad6..473a34db 100644 --- a/tests/test_cases/issue_348/issue_348.py +++ b/tests/test_cases/issue_348/issue_348.py @@ -3,6 +3,7 @@ from cocotb.triggers import Timer, Edge, RisingEdge, FallingEdge from cocotb.result import TestFailure + @cocotb.coroutine def clock_gen(signal, num): for x in range(num): @@ -11,6 +12,7 @@ def clock_gen(signal, num): signal <= 1 yield Timer(500) + @cocotb.coroutine def signal_mon(signal, idx, edge): log = SimLog("cocotb.signal_mon.%d.%s" % (idx, signal._name)) @@ -24,6 +26,7 @@ def signal_mon(signal, idx, edge): return edges + class DualMonitor: def __init__(self, edge, signal): self.log = SimLog("cocotb.%s.%s" % (edge, signal)) @@ -52,7 +55,6 @@ def start(self): raise TestFailure("Monitor saw nothing") - # Cadence simulators: "Unable set up RisingEdge(ModifiableObject(sample_module.clk)) Trigger" with VHDL (see #1076) @cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else False) def issue_348_rising(dut): diff --git a/tests/test_cases/issue_588/issue_588.py b/tests/test_cases/issue_588/issue_588.py index 0c5e8825..9c9be953 100644 --- a/tests/test_cases/issue_588/issue_588.py +++ b/tests/test_cases/issue_588/issue_588.py @@ -4,12 +4,14 @@ import cocotb from cocotb import triggers, result, utils + @cocotb.coroutine def sample_coroutine(dut): """ Very simple coroutine that waits 5 ns.""" yield triggers.Timer(5, "ns") dut._log.info("Sample coroutine yielded.") + @cocotb.test() def issue_588_coroutine_list(dut): """ Yield a list of triggers and coroutines.""" diff --git a/tests/test_cases/issue_768_a/issue_768.py b/tests/test_cases/issue_768_a/issue_768.py index 0c789dad..a3158b2d 100644 --- a/tests/test_cases/issue_768_a/issue_768.py +++ b/tests/test_cases/issue_768_a/issue_768.py @@ -12,6 +12,7 @@ # this line is different between the two files value = 0 + @cocotb.test() def test(dut): dut.stream_in_data.setimmediatevalue(value) diff --git a/tests/test_cases/issue_768_b/issue_768.py b/tests/test_cases/issue_768_b/issue_768.py index 29db4d4c..f751eff7 100644 --- a/tests/test_cases/issue_768_b/issue_768.py +++ b/tests/test_cases/issue_768_b/issue_768.py @@ -12,6 +12,7 @@ # this line is different between the two files value = BinaryValue(0, n_bits=8) + @cocotb.test() def do_test(dut): dut.stream_in_data.setimmediatevalue(value) diff --git a/tests/test_cases/issue_892/issue_892.py b/tests/test_cases/issue_892/issue_892.py index 2365fe46..15b6e087 100644 --- a/tests/test_cases/issue_892/issue_892.py +++ b/tests/test_cases/issue_892/issue_892.py @@ -2,11 +2,13 @@ from cocotb.triggers import Timer from cocotb.result import TestSuccess + @cocotb.coroutine def raise_test_success(): yield Timer(1, units='ns') raise TestSuccess("TestSuccess") + @cocotb.test() def error_test(dut): cocotb.fork(raise_test_success()) diff --git a/tests/test_cases/issue_893/issue_893.py b/tests/test_cases/issue_893/issue_893.py index 1f43dd01..5fd8ff9d 100644 --- a/tests/test_cases/issue_893/issue_893.py +++ b/tests/test_cases/issue_893/issue_893.py @@ -1,11 +1,13 @@ import cocotb from cocotb.triggers import Timer + @cocotb.coroutine def coroutine_with_undef(): new_variable = undefined_variable # noqa yield Timer(1, units='ns') + @cocotb.test(expect_error=True) def fork_erroring_coroutine(dut): cocotb.fork(coroutine_with_undef()) diff --git a/tests/test_cases/issue_957/issue_957.py b/tests/test_cases/issue_957/issue_957.py index b89ea1ce..15b780de 100644 --- a/tests/test_cases/issue_957/issue_957.py +++ b/tests/test_cases/issue_957/issue_957.py @@ -1,6 +1,7 @@ import cocotb from cocotb.triggers import Timer, RisingEdge, First + @cocotb.coroutine def wait_edge(dut): # this trigger never fires @@ -8,10 +9,12 @@ def wait_edge(dut): RisingEdge(dut.stream_out_ready) ) + @cocotb.test() def test1(dut): cocotb.fork(wait_edge(dut)) yield Timer(1000) + test2 = test1 diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py index 98215dc3..121616e6 100644 --- a/tests/test_cases/test_array/test_array.py +++ b/tests/test_cases/test_array/test_array.py @@ -10,36 +10,42 @@ from cocotb.result import TestError, TestFailure from cocotb.handle import HierarchyObject, HierarchyArrayObject, ModifiableObject, NonHierarchyIndexableObject, ConstantObject + def _check_type(tlog, hdl, expected): if not isinstance(hdl, expected): raise TestFailure(">{0!r} ({1})< should be >{2}<".format(hdl, hdl._type, expected)) else: tlog.info(" Found %r (%s) with length=%d", hdl, hdl._type, len(hdl)) + def _check_int(tlog, hdl, expected): if int(hdl) != expected: raise TestFailure("{2!r}: Expected >{0}< but got >{1}<".format(expected, int(hdl), hdl)) else: tlog.info(" Found {0!r} ({1}) with value={2}".format(hdl, hdl._type, int(hdl))) + def _check_logic(tlog, hdl, expected): if int(hdl) != expected: raise TestFailure("{2!r}: Expected >0x{0:X}< but got >0x{1:X}<".format(expected, int(hdl), hdl)) else: tlog.info(" Found {0!r} ({1}) with value=0x{2:X}".format(hdl, hdl._type, int(hdl))) + def _check_str(tlog, hdl, expected): if hdl.value != expected: raise TestFailure("{2!r}: Expected >{0}< but got >{1}<".format(expected, str(hdl), hdl)) else: tlog.info(" Found {0!r} ({1}) with value={2}".format(hdl, hdl._type, str(hdl))) + def _check_real(tlog, hdl, expected): if float(hdl) != expected: raise TestFailure("{2!r}: Expected >{0}< but got >{1}<".format(expected, float(hdl), hdl)) else: tlog.info(" Found {0!r} ({1}) with value={2}".format(hdl, hdl._type, float(hdl))) + def _check_value(tlog, hdl, expected): if hdl.value != expected: raise TestFailure("{2!r}: Expected >{0}< but got >{1}<".format(expected, hdl.value, hdl)) @@ -200,6 +206,7 @@ def test_read_write(dut): _check_logic(tlog, dut.port_rec_out.b[1] , 0xA3) _check_logic(tlog, dut.port_cmplx_out[1].b[1], 0xEE) + @cocotb.test() def test_gen_loop(dut): """Test accessing Generate Loops""" @@ -238,6 +245,7 @@ def test_gen_loop(dut): for gens in dut.asc_gen: tlog.info("Iterate access found %s", gens) + @cocotb.test() def test_discover_all(dut): r"""Discover everything in the DUT: @@ -370,6 +378,7 @@ def _discover(obj, indent): if total != pass_total: raise TestFailure("Expected {0} objects but found {1}".format(pass_total, total)) + @cocotb.test(skip=(cocotb.LANGUAGE in ["verilog"] or cocotb.SIM_NAME.lower().startswith(("riviera")))) def test_direct_constant_indexing(dut): """Test directly accessing constant/parameter data in arrays, i.e. not iterating""" @@ -475,7 +484,6 @@ def test_direct_signal_indexing(dut): _check_type(tlog, dut.sig_t8[1], NonHierarchyIndexableObject) _check_type(tlog, dut.sig_t8[0][3], ModifiableObject) - # Riviera has a bug and finds dut.sig_cmplx[1], but the type returned is a vpiBitVar # only true for version 2016.02 if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")) and @@ -498,6 +506,7 @@ def test_direct_signal_indexing(dut): _check_type(tlog, dut.sig_rec.b[1], ModifiableObject) _check_type(tlog, dut.sig_rec.b[1][2], ModifiableObject) + @cocotb.test(skip=(cocotb.LANGUAGE in ["verilog"])) def test_extended_identifiers(dut): """Test accessing extended identifiers""" diff --git a/tests/test_cases/test_array_simple/test_array_simple.py b/tests/test_cases/test_array_simple/test_array_simple.py index fe81b37a..e2a02b62 100644 --- a/tests/test_cases/test_array_simple/test_array_simple.py +++ b/tests/test_cases/test_array_simple/test_array_simple.py @@ -12,6 +12,7 @@ tlog = logging.getLogger("cocotb.test") + def _check_value(tlog, hdl, expected): if hdl.value != expected: raise TestFailure("{2!r}: Expected >{0}< but got >{1}<".format(expected, hdl.value, hdl)) @@ -174,6 +175,7 @@ async def test_ndim_array_indexes(dut): _check_value(tlog, dut.array_2d[0][30], 0xBE) _check_value(tlog, dut.array_2d[1] , [0xDE, 0xAD, 0x12, 0xEF]) + @contextlib.contextmanager def assert_raises(exc_type): try: @@ -183,6 +185,7 @@ def assert_raises(exc_type): else: raise AssertionError("{} was not raised".format(exc_type.__name__)) + @cocotb.test() async def test_exceptions(dut): """Test that correct Exceptions are raised.""" diff --git a/tests/test_cases/test_avalon/test_avalon.py b/tests/test_cases/test_avalon/test_avalon.py index 34645751..6a886def 100755 --- a/tests/test_cases/test_avalon/test_avalon.py +++ b/tests/test_cases/test_avalon/test_avalon.py @@ -39,6 +39,7 @@ from cocotb.clock import Clock from cocotb.result import TestFailure + class BurstAvlReadTest(object): """ class to test avalon burst """ @@ -68,6 +69,7 @@ def init_sig(self, burstcount_w, address): self.dut.control_go = 0 self.dut.master_waitrequest = 0 + @cocotb.test(expect_fail=False) def test_burst_read(dut): """ Testing burst read """ diff --git a/tests/test_cases/test_avalon_stream/test_avalon_stream.py b/tests/test_cases/test_avalon_stream/test_avalon_stream.py index 5145b825..94a251b0 100755 --- a/tests/test_cases/test_avalon_stream/test_avalon_stream.py +++ b/tests/test_cases/test_avalon_stream/test_avalon_stream.py @@ -13,8 +13,10 @@ from cocotb.scoreboard import Scoreboard from cocotb.generators.bit import wave + class AvalonSTTB(object): """Testbench for avalon basic stream""" + def __init__(self, dut): self.dut = dut @@ -44,6 +46,7 @@ def send_data(self, data): self.expected_output.append(exp_data) yield self.stream_in.send(data) + @cocotb.test(expect_fail=False) def test_avalon_stream(dut): """Test stream of avalon data""" diff --git a/tests/test_cases/test_cocotb/test_deprecated.py b/tests/test_cases/test_cocotb/test_deprecated.py index 19df58aa..3cf327ff 100644 --- a/tests/test_cases/test_cocotb/test_deprecated.py +++ b/tests/test_cases/test_cocotb/test_deprecated.py @@ -20,7 +20,6 @@ def assert_deprecated(): assert issubclass(warns[0].category, DeprecationWarning), "Expected DeprecationWarning" - @cocotb.test() async def test_returnvalue_deprecated(dut): diff --git a/tests/test_cases/test_configuration/test_configurations.py b/tests/test_cases/test_configuration/test_configurations.py index 9321c471..d9008c88 100644 --- a/tests/test_cases/test_configuration/test_configurations.py +++ b/tests/test_cases/test_configuration/test_configurations.py @@ -26,11 +26,13 @@ import cocotb from cocotb.triggers import Timer + def sub_iterate(unit): for thing in unit: thing._log.info("Found %s.%s %s" % (unit._name, thing._name, type(thing))) sub_iterate(thing) + @cocotb.test() def iterate(dut): yield Timer(100) diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index e86a8c13..1b86d742 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -45,6 +45,7 @@ def _discover(obj): _discover(thing) _discover(dut) + @cocotb.test() def discover_module_values(dut): """Discover everything in the DUT""" @@ -56,6 +57,7 @@ def discover_module_values(dut): if count < 2: raise TestFailure("Expected to discover things in the DUT") + @cocotb.test(skip=True) def ipython_embed(dut): yield Timer(0) @@ -179,6 +181,7 @@ def access_integer(dut): if length != 1: raise TestFailure("Length should be 1 not %d" % length) + @cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) def access_ulogic(dut): """Access a std_ulogic as enum""" @@ -201,6 +204,7 @@ def access_constant_integer(dut): if constant_integer != 7: raise TestFailure("EXAMPLE_WIDTH was not 7") + @cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) def access_string_vhdl(dut): """Access to a string, both constant and signal.""" @@ -327,6 +331,7 @@ def access_constant_boolean(dut): tlog.info("Value of %s is %d" % (constant_boolean, constant_boolean)) + @cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) def access_boolean(dut): """Test access to a boolean""" @@ -368,6 +373,7 @@ def access_boolean(dut): if (int(curr_val) == int(output_bool)): raise TestFailure("Value did not propagate") + @cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"]) def access_internal_register_array(dut): """Test access to an internal register array""" @@ -382,6 +388,7 @@ def access_internal_register_array(dut): if (dut.register_array[1].value != 4): raise TestFailure("Failed to set internal register array value") + @cocotb.test(skip=True) def skip_a_test(dut): """This test shouldn't execute""" @@ -407,6 +414,7 @@ def access_gate(dut): if not isinstance(gate, HierarchyObject): raise TestFailure("Gate should be HierarchyObject") + @cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) def custom_type(dut): """ @@ -441,6 +449,7 @@ def _discover(obj): if expected_top != count: raise TestFailure("Expected %d found %d for cosLut" % (expected_top, count)) + @cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"]) def type_check_verilog(dut): """ diff --git a/tests/test_cases/test_discovery/test_vhdl_indexed_name.py b/tests/test_cases/test_discovery/test_vhdl_indexed_name.py index cd45ec17..09b6356f 100644 --- a/tests/test_cases/test_discovery/test_vhdl_indexed_name.py +++ b/tests/test_cases/test_discovery/test_vhdl_indexed_name.py @@ -26,6 +26,7 @@ import cocotb from cocotb.triggers import Timer + @cocotb.test() def index_name_iter(dut): """Access a non local indexed name""" diff --git a/tests/test_cases/test_exit_error/test_exit.py b/tests/test_cases/test_exit_error/test_exit.py index e1e64848..33e4ab0c 100644 --- a/tests/test_cases/test_exit_error/test_exit.py +++ b/tests/test_cases/test_exit_error/test_exit.py @@ -1,6 +1,7 @@ import cocotb from cocotb.triggers import Timer + @cocotb.test() def typosyntax_error(): # this syntax error makes the whole file unimportable, so the file contents diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index 8a9ae90d..769f6d1e 100755 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -42,7 +42,6 @@ from cocotb.utils import get_sim_time - # Tests relating to calling convention and operation def return_two(dut): @@ -84,6 +83,7 @@ def clock_monitor(dut): yield Timer(1000) count += 1 + @cocotb.test() def test_time_in_external(dut): """Test that the simulation time does not advance if the wrapped external @@ -102,11 +102,13 @@ def test_time_in_external(dut): if time != time_now: raise TestFailure("Time has elapsed over external call") + @cocotb.function def wait_cycles(dut, n): for _ in range(n): yield RisingEdge(dut.clk) + def wait_cycles_wrapper(dut, n): return wait_cycles(dut, n) @@ -137,6 +139,7 @@ def test_ext_call_return(dut): value = yield external(return_two)(dut) assert value == 2 + @cocotb.test() def test_multiple_externals(dut): clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) @@ -178,6 +181,7 @@ def test_external_and_continue(dut): yield Timer(10, "ns") yield RisingEdge(dut.clk) + @cocotb.coroutine def run_external(dut): value = yield external(calls_cocotb_function)(dut) @@ -194,6 +198,7 @@ def test_external_from_fork(dut): dut._log.info("Back from join") + @cocotb.test(expect_fail=True, skip=True) def test_ext_exit_error(dut): """Test that a premature exit of the sim at its request still results in @@ -219,6 +224,7 @@ def func(): else: raise TestFailure('Exception was not thrown') + @cocotb.test() def test_external_returns_exception(dut): """ Test that exceptions can be returned by @external functions """ @@ -237,6 +243,7 @@ def func(): if not isinstance(result, ValueError): raise TestFailure('Exception was not returned') + @cocotb.test() def test_function_raised_exception(dut): """ Test that exceptions thrown by @function coroutines can be caught """ @@ -259,6 +266,7 @@ def ext(): else: raise TestFailure('Exception was not thrown') + @cocotb.test() def test_function_returns_exception(dut): """ Test that exceptions can be returned by @function coroutines """ @@ -282,6 +290,7 @@ def ext(): if not isinstance(result, ValueError): raise TestFailure('Exception was not returned') + @cocotb.test() def test_function_from_weird_thread_fails(dut): """ @@ -328,6 +337,7 @@ def ext(): yield task.join() + @cocotb.test() def test_function_called_in_parallel(dut): """ diff --git a/tests/test_cases/test_iteration_verilog/test_iteration_es.py b/tests/test_cases/test_iteration_verilog/test_iteration_es.py index 8afbf5fd..326924d3 100644 --- a/tests/test_cases/test_iteration_verilog/test_iteration_es.py +++ b/tests/test_cases/test_iteration_verilog/test_iteration_es.py @@ -29,6 +29,7 @@ from cocotb.triggers import Timer from cocotb.result import TestFailure + @cocotb.test(expect_fail=cocotb.SIM_NAME in ["Icarus Verilog"]) def recursive_discovery(dut): """ @@ -58,12 +59,14 @@ def dump_all_the_things(parent): if total != pass_total: raise TestFailure("Expected %d objects but found %d" % (pass_total, total)) + @cocotb.coroutine def iteration_loop(dut): for thing in dut: thing._log.info("Found something: %s" % thing._fullname) yield Timer(1) + @cocotb.test() def dual_iteration(dut): loop_one = cocotb.fork(iteration_loop(dut)) diff --git a/tests/test_cases/test_iteration_vhdl/test_iteration.py b/tests/test_cases/test_iteration_vhdl/test_iteration.py index 18413dda..9929c97b 100644 --- a/tests/test_cases/test_iteration_vhdl/test_iteration.py +++ b/tests/test_cases/test_iteration_vhdl/test_iteration.py @@ -29,6 +29,7 @@ from cocotb.triggers import Timer from cocotb.result import TestFailure + @cocotb.test() def recursive_discovery(dut): """ @@ -70,12 +71,14 @@ def discovery_all(dut): item = dut.gen_acs[3] item._log.info("this is item") + @cocotb.coroutine def iteration_loop(dut): for thing in dut: thing._log.info("Found something: %s", thing._fullname) yield Timer(1) + @cocotb.test() def dual_iteration(dut): loop_one = cocotb.fork(iteration_loop(dut)) @@ -83,6 +86,7 @@ def dual_iteration(dut): yield [loop_one.join(), loop_two.join()] + @cocotb.test() def get_clock(dut): dut._log.info("dut.aclk is %s", type(dut.aclk).__name__) @@ -93,6 +97,7 @@ def get_clock(dut): if int(dut.aclk) != 1: raise TestFailure("dut.aclk is not what we expected (got %d)" % int(dut.aclk)) + @cocotb.test() def test_n_dimension_array(dut): tlog = logging.getLogger("cocotb.test") diff --git a/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py b/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py index b28dd00d..107a46d3 100644 --- a/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py +++ b/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py @@ -2,6 +2,7 @@ from cocotb.result import TestFailure from cocotb.triggers import Timer + @cocotb.test() def test_in_vect_packed(dut): yield Timer(10) @@ -12,6 +13,7 @@ def test_in_vect_packed(dut): if dut.out_vect_packed != 0x5: raise TestFailure("Failed to readback dut.out_vect_packed") + @cocotb.test() def test_in_vect_unpacked(dut): yield Timer(10) @@ -22,6 +24,7 @@ def test_in_vect_unpacked(dut): if dut.out_vect_unpacked != [0x1, 0x0, 0x1]: raise TestFailure("Failed to readback dut.out_vect_unpacked") + @cocotb.test() def test_in_arr(dut): yield Timer(10) @@ -32,6 +35,7 @@ def test_in_arr(dut): if dut.out_arr != 0x5: raise TestFailure("Failed to readback dut.out_arr") + @cocotb.test() def test_in_2d_vect_packed_packed(dut): yield Timer(10) @@ -42,6 +46,7 @@ def test_in_2d_vect_packed_packed(dut): if dut.out_2d_vect_packed_packed != (0x5 << 6) | (0x5 << 3) | 0x5: raise TestFailure("Failed to readback dut.out_2d_vect_packed_packed") + @cocotb.test() def test_in_2d_vect_packed_unpacked(dut): yield Timer(10) @@ -52,6 +57,7 @@ def test_in_2d_vect_packed_unpacked(dut): if dut.out_2d_vect_packed_unpacked != [0x5, 0x5, 0x5]: raise TestFailure("Failed to readback dut.out_2d_vect_packed_unpacked") + @cocotb.test() def test_in_2d_vect_unpacked_unpacked(dut): yield Timer(10) @@ -62,6 +68,7 @@ def test_in_2d_vect_unpacked_unpacked(dut): if dut.out_2d_vect_unpacked_unpacked != 3*[[0x1, 0x0, 0x1]]: raise TestFailure("Failed to readback dut.out_2d_vect_unpacked_unpacked") + @cocotb.test() def test_in_arr_packed(dut): yield Timer(10) @@ -72,6 +79,7 @@ def test_in_arr_packed(dut): if dut.out_arr_packed != 365: raise TestFailure("Failed to readback dut.out_arr_packed") + @cocotb.test() def test_in_arr_unpacked(dut): yield Timer(10) @@ -82,6 +90,7 @@ def test_in_arr_unpacked(dut): if dut.out_arr_unpacked != [0x5, 0x5, 0x5]: raise TestFailure("Failed to readback dut.out_arr_unpacked") + @cocotb.test() def test_in_2d_arr(dut): yield Timer(10) @@ -92,6 +101,7 @@ def test_in_2d_arr(dut): if dut.out_2d_arr != 365: raise TestFailure("Failed to readback dut.out_2d_arr") + @cocotb.test() def test_in_vect_packed_packed_packed(dut): yield Timer(10) @@ -102,6 +112,7 @@ def test_in_vect_packed_packed_packed(dut): if dut.out_vect_packed_packed_packed != 95869805: raise TestFailure("Failed to readback dut.out_vect_packed_packed_packed") + @cocotb.test() def test_in_vect_packed_packed_unpacked(dut): yield Timer(10) @@ -112,6 +123,7 @@ def test_in_vect_packed_packed_unpacked(dut): if dut.out_vect_packed_packed_unpacked != [365, 365, 365]: raise TestFailure("Failed to readback dut.out_vect_packed_packed_unpacked") + @cocotb.test() def test_in_vect_packed_unpacked_unpacked(dut): yield Timer(10) @@ -122,6 +134,7 @@ def test_in_vect_packed_unpacked_unpacked(dut): if dut.out_vect_packed_unpacked_unpacked != 3 *[3 * [5] ]: raise TestFailure("Failed to readback dut.out_vect_packed_unpacked_unpacked") + @cocotb.test() def test_in_vect_unpacked_unpacked_unpacked(dut): yield Timer(10) @@ -132,6 +145,7 @@ def test_in_vect_unpacked_unpacked_unpacked(dut): if dut.out_vect_unpacked_unpacked_unpacked != 3 *[3 * [[1, 0, 1]]]: raise TestFailure("Failed to readback dut.out_vect_unpacked_unpacked_unpacked") + @cocotb.test() def test_in_arr_packed_packed(dut): yield Timer(10) @@ -142,6 +156,7 @@ def test_in_arr_packed_packed(dut): if dut.out_arr_packed_packed != (365 << 18) | (365 << 9) | (365): raise TestFailure("Failed to readback dut.out_arr_packed_packed") + @cocotb.test() def test_in_arr_packed_unpacked(dut): yield Timer(10) @@ -152,6 +167,7 @@ def test_in_arr_packed_unpacked(dut): if dut.out_arr_packed_unpacked != [365, 365, 365]: raise TestFailure("Failed to readback dut.out_arr_packed_unpacked") + @cocotb.test() def test_in_arr_unpacked_unpacked(dut): yield Timer(10) @@ -162,6 +178,7 @@ def test_in_arr_unpacked_unpacked(dut): if dut.out_arr_unpacked_unpacked != 3 *[3 * [5] ]: raise TestFailure("Failed to readback dut.out_arr_unpacked_unpacked") + @cocotb.test() def test_in_2d_arr_packed(dut): yield Timer(10) @@ -172,6 +189,7 @@ def test_in_2d_arr_packed(dut): if dut.out_2d_arr_packed != (365 << 18) | (365 << 9) | (365): raise TestFailure("Failed to readback dut.out_2d_arr_packed") + @cocotb.test() def test_in_2d_arr_unpacked(dut): yield Timer(10) @@ -182,6 +200,7 @@ def test_in_2d_arr_unpacked(dut): if dut.out_2d_arr_unpacked != [365, 365, 365]: raise TestFailure("Failed to readback dut.out_2d_arr_unpacked") + @cocotb.test() def test_in_3d_arr(dut): yield Timer(10) diff --git a/tests/test_cases/test_verilog_access/test_verilog_access.py b/tests/test_cases/test_verilog_access/test_verilog_access.py index 92270f95..d1053197 100644 --- a/tests/test_cases/test_verilog_access/test_verilog_access.py +++ b/tests/test_cases/test_verilog_access/test_verilog_access.py @@ -30,6 +30,7 @@ from cocotb.triggers import Timer from cocotb.result import TestFailure + @cocotb.test() def port_not_hierarchy(dut): """ diff --git a/tests/test_cases/test_vhdl_access/test_vhdl_access.py b/tests/test_cases/test_vhdl_access/test_vhdl_access.py index 47640e15..2ea6ada2 100644 --- a/tests/test_cases/test_vhdl_access/test_vhdl_access.py +++ b/tests/test_cases/test_vhdl_access/test_vhdl_access.py @@ -30,6 +30,7 @@ from cocotb.triggers import Timer from cocotb.result import TestFailure + @cocotb.test() def check_enum_object(dut): """ @@ -41,6 +42,7 @@ def check_enum_object(dut): if not isinstance(dut.inst_ram_ctrl.write_ram_fsm, EnumObject): raise TestFailure("Expected the FSM enum to be an EnumObject") + @cocotb.test() def check_objects(dut): """ @@ -92,6 +94,7 @@ def check_instance(obj, objtype): if fails: raise TestFailure("%d Failures during the test" % fails) + @cocotb.test() def port_not_hierarchy(dut): """ From 78df29c3cc68fb3f6cfb59ba82d5ce2dc4784788 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sun, 3 May 2020 14:19:12 +0200 Subject: [PATCH 2218/2656] Remove F403 from flake8 ignore list Fix occurrence of this error. Refs #1547. --- cocotb/generators/bit.py | 2 +- setup.cfg | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/cocotb/generators/bit.py b/cocotb/generators/bit.py index 117f9162..07beead2 100644 --- a/cocotb/generators/bit.py +++ b/cocotb/generators/bit.py @@ -35,7 +35,7 @@ cycles (ON,OFF) """ from cocotb.decorators import public -from cocotb.generators import * +from cocotb.generators import gaussian, sine_wave, repeat def bit_toggler(gen_on, gen_off): diff --git a/setup.cfg b/setup.cfg index d8338d29..67ea3e4e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -22,7 +22,6 @@ ignore = E402 # module level import not at top of file E501 # line too long (... > 79 characters) E741 # ambiguous variable name 'l' - F403 # 'from ... import *' used; unable to detect F405 # ... may be undefined, or defined from star F841 # local variable ... is assigned to but never used W504 # line break after binary operator From 792297f844dc3fa1aa5a10fd525da5647b6e0105 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 15 Mar 2020 21:03:59 -0500 Subject: [PATCH 2219/2656] Change to_python/to_simulator to use GPI log --- cocotb/share/include/cocotb_utils.h | 24 ++++++++++++++++++++++-- cocotb/share/lib/utils/cocotb_utils.cpp | 19 ------------------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/cocotb/share/include/cocotb_utils.h b/cocotb/share/include/cocotb_utils.h index 211b49e8..ea559018 100644 --- a/cocotb/share/include/cocotb_utils.h +++ b/cocotb/share/include/cocotb_utils.h @@ -30,6 +30,8 @@ #ifndef COCOTB_UTILS_H_ #define COCOTB_UTILS_H_ +#include + #ifdef __cplusplus extern "C" { #endif @@ -42,8 +44,26 @@ extern void* utils_dyn_sym(void *handle, const char* sym_name); extern int is_python_context; -void to_python(void); -void to_simulator(void); +// to_python and to_simulator are implemented as macros instead of functions so +// that the logs reference the user's lineno and filename + +#define to_python() do { \ + if (is_python_context) { \ + LOG_ERROR("FATAL: We are calling up again"); \ + exit(1); \ + } \ + ++is_python_context; \ + LOG_DEBUG("Returning to Python"); \ +} while (0) + +#define to_simulator() do { \ + if (!is_python_context) { \ + LOG_ERROR("FATAL: We have returned twice from python\n"); \ + exit(1); \ + } \ + --is_python_context; \ + LOG_DEBUG("Returning to simulator"); \ +} while (0) #define COCOTB_UNUSED(x) ((void)x) diff --git a/cocotb/share/lib/utils/cocotb_utils.cpp b/cocotb/share/lib/utils/cocotb_utils.cpp index 8c29e456..0a6e778f 100644 --- a/cocotb/share/lib/utils/cocotb_utils.cpp +++ b/cocotb/share/lib/utils/cocotb_utils.cpp @@ -40,25 +40,6 @@ // Tracks if we are in the context of Python or Simulator int is_python_context = 0; -extern "C" void to_python(void) { - if (is_python_context) { - fprintf(stderr, "FATAL: We are calling up again\n"); - exit(1); - } - ++is_python_context; - //fprintf(stderr, "INFO: Calling up to python %d\n", is_python_context); -} - -extern "C" void to_simulator(void) { - if (!is_python_context) { - fprintf(stderr, "FATAL: We have returned twice from python\n"); - exit(1); - } - - --is_python_context; - //fprintf(stderr, "INFO: Returning back to simulator %d\n", is_python_context); -} - extern "C" void* utils_dyn_open(const char* lib_name) { void *ret = NULL; From 356763d4efb6d34a727be6ffda7d2c288265f992 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Thu, 30 Apr 2020 23:18:47 -0500 Subject: [PATCH 2220/2656] Use LOG_* macros in cocotb_utils --- cocotb/share/lib/utils/cocotb_utils.cpp | 18 +++++++++--------- cocotb_build_libs.py | 7 +++++-- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/cocotb/share/lib/utils/cocotb_utils.cpp b/cocotb/share/lib/utils/cocotb_utils.cpp index 0a6e778f..92cd7c17 100644 --- a/cocotb/share/lib/utils/cocotb_utils.cpp +++ b/cocotb/share/lib/utils/cocotb_utils.cpp @@ -28,7 +28,7 @@ ******************************************************************************/ #include -#include +#include #include #if defined(__linux__) || defined(__APPLE__) @@ -47,17 +47,17 @@ extern "C" void* utils_dyn_open(const char* lib_name) SetErrorMode(0); ret = static_cast(LoadLibrary(lib_name)); if (!ret) { - printf("Unable to open lib %s", lib_name); + const char *log_fmt = "Unable to open lib %s%s%s\n"; LPSTR msg_ptr; if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT), (LPSTR)&msg_ptr, 255, NULL)) { - printf(": %s", msg_ptr); + LOG_ERROR(log_fmt, lib_name, ": ", msg_ptr); LocalFree(msg_ptr); } else { - printf("\n"); + LOG_ERROR(log_fmt, lib_name, "", ""); } } #else @@ -66,7 +66,7 @@ extern "C" void* utils_dyn_open(const char* lib_name) ret = dlopen(lib_name, RTLD_LAZY | RTLD_GLOBAL); if (!ret) { - printf("Unable to open lib %s (%s)\n", lib_name, dlerror()); + LOG_ERROR("Unable to open lib %s: %s\n", lib_name, dlerror()); } #endif return ret; @@ -78,23 +78,23 @@ extern "C" void* utils_dyn_sym(void *handle, const char* sym_name) #if ! defined(__linux__) && ! defined(__APPLE__) entry_point = reinterpret_cast(GetProcAddress(static_cast(handle), sym_name)); if (!entry_point) { - printf("Unable to find symbol %s", sym_name); + const char *log_fmt = "Unable to find symbol %s%s%s\n"; LPSTR msg_ptr; if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT), (LPSTR)&msg_ptr, 255, NULL)) { - printf(": %s", msg_ptr); + LOG_ERROR(log_fmt, sym_name, ": ", msg_ptr); LocalFree(msg_ptr); } else { - printf("\n"); + LOG_ERROR(log_fmt, sym_name, "", ""); } } #else entry_point = dlsym(handle, sym_name); if (!entry_point) { - printf("Unable to find symbol %s (%s)\n", sym_name, dlerror()); + LOG_ERROR("Unable to find symbol %s: %s\n", sym_name, dlerror()); } #endif return entry_point; diff --git a/cocotb_build_libs.py b/cocotb_build_libs.py index 29c4ec1a..55308c17 100755 --- a/cocotb_build_libs.py +++ b/cocotb_build_libs.py @@ -203,6 +203,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): libcocotbutils = Extension( os.path.join("cocotb", "libs", "libcocotbutils"), include_dirs=[include_dir], + libraries=["gpilog"], sources=[os.path.join(share_lib_dir, "utils", "cocotb_utils.cpp")], extra_link_args=_extra_link_args(lib_name="libcocotbutils", rpath="$ORIGIN"), extra_compile_args=_extra_cxx_compile_args, @@ -218,7 +219,6 @@ def _get_common_lib_ext(include_dir, share_lib_dir): libgpilog = Extension( os.path.join("cocotb", "libs", "libgpilog"), include_dirs=[include_dir], - libraries=["cocotbutils"], library_dirs=python_lib_dirs, sources=[os.path.join(share_lib_dir, "gpi_log", "gpi_logging.cpp")], extra_link_args=_extra_link_args(lib_name="libgpilog", rpath="$ORIGIN"), @@ -268,7 +268,10 @@ def _get_common_lib_ext(include_dir, share_lib_dir): extra_link_args=_extra_link_args(rpath="$ORIGIN/libs"), ) - return [libcocotbutils, libgpilog, libcocotb, libgpi, libsim] + # The libraries in this list are compiled in order of their appearance. + # If there is a linking dependency on one library to another, + # the linked library must be built first. + return [libgpilog, libcocotbutils, libcocotb, libgpi, libsim] def _get_vpi_lib_ext( From d1d5a5535334cef38c539882d276f706b7445f58 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Tue, 5 May 2020 17:18:55 +1000 Subject: [PATCH 2221/2656] Move issue_134 tests to test_handle (#1765) Add test_real_ prefix to assign_double and assign_int test names --- tests/test_cases/issue_134/Makefile | 30 ----------- tests/test_cases/issue_134/test_integers.py | 23 -------- tests/test_cases/issue_134/test_reals.py | 43 --------------- tests/test_cases/test_cocotb/test_handle.py | 58 +++++++++++++++++++++ 4 files changed, 58 insertions(+), 96 deletions(-) delete mode 100644 tests/test_cases/issue_134/Makefile delete mode 100644 tests/test_cases/issue_134/test_integers.py delete mode 100644 tests/test_cases/issue_134/test_reals.py diff --git a/tests/test_cases/issue_134/Makefile b/tests/test_cases/issue_134/Makefile deleted file mode 100644 index 95b104e8..00000000 --- a/tests/test_cases/issue_134/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -include ../../designs/sample_module/Makefile - -MODULE ?= test_reals,test_integers diff --git a/tests/test_cases/issue_134/test_integers.py b/tests/test_cases/issue_134/test_integers.py deleted file mode 100644 index 1d7dc08d..00000000 --- a/tests/test_cases/issue_134/test_integers.py +++ /dev/null @@ -1,23 +0,0 @@ -import logging - -import cocotb -from cocotb.triggers import Timer -from cocotb.result import TestFailure - - -@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) -def test_integer(dut): - """ - Test access to integers - """ - log = logging.getLogger("cocotb.test") - yield Timer(10) - dut.stream_in_int = 4 - yield Timer(10) - yield Timer(10) - got_in = int(dut.stream_out_int) - got_out = int(dut.stream_in_int) - log.info("dut.stream_out_int = %d" % got_out) - log.info("dut.stream_in_int = %d" % got_in) - if got_in != got_out: - raise TestFailure("stream_in_int and stream_out_int should not match") diff --git a/tests/test_cases/issue_134/test_reals.py b/tests/test_cases/issue_134/test_reals.py deleted file mode 100644 index 5712be87..00000000 --- a/tests/test_cases/issue_134/test_reals.py +++ /dev/null @@ -1,43 +0,0 @@ -import logging -import random - -import cocotb -from cocotb.triggers import Timer -from cocotb.result import TestFailure - - -@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) -def assign_double(dut): - """ - Assign a random floating point value, read it back from the DUT and check - it matches what we assigned - """ - val = random.uniform(-1e307, 1e307) - log = logging.getLogger("cocotb.test") - yield Timer(1) - log.info("Setting the value %g" % val) - dut.stream_in_real = val - yield Timer(1) - yield Timer(1) # FIXME: Workaround for VHPI scheduling - needs investigation - got = float(dut.stream_out_real) - log.info("Read back value %g" % got) - if got != val: - raise TestFailure("Values didn't match!") - - -@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) -def assign_int(dut): - """Assign a random integer value to ensure we can write types convertible to - int, read it back from the DUT and check it matches what we assigned. - """ - val = random.randint(-2**31, 2**31 - 1) - log = logging.getLogger("cocotb.test") - yield Timer(1) - log.info("Setting the value %i" % val) - dut.stream_in_real <= val - yield Timer(1) - yield Timer(1) # FIXME: Workaround for VHPI scheduling - needs investigation - got = dut.stream_out_real - log.info("Read back value %d" % got) - if got != float(val): - raise TestFailure("Values didn't match!") diff --git a/tests/test_cases/test_cocotb/test_handle.py b/tests/test_cases/test_cocotb/test_handle.py index 4a64b996..d6c87464 100644 --- a/tests/test_cases/test_cocotb/test_handle.py +++ b/tests/test_cases/test_cocotb/test_handle.py @@ -4,7 +4,10 @@ """ Tests for handles """ +import logging +import random import cocotb +from cocotb.triggers import Timer from cocotb.result import TestFailure from common import assert_raises @@ -65,3 +68,58 @@ async def test_delayed_assignment_still_errors(dut): dut.stream_in_int <= "1010 not a real binary string" with assert_raises(TypeError): dut.stream_in_int <= [] + + +@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) +def test_integer(dut): + """ + Test access to integers + """ + log = logging.getLogger("cocotb.test") + yield Timer(10) + dut.stream_in_int = 4 + yield Timer(10) + yield Timer(10) + got_in = int(dut.stream_out_int) + got_out = int(dut.stream_in_int) + log.info("dut.stream_out_int = %d" % got_out) + log.info("dut.stream_in_int = %d" % got_in) + if got_in != got_out: + raise TestFailure("stream_in_int and stream_out_int should not match") + + +@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) +def test_real_assign_double(dut): + """ + Assign a random floating point value, read it back from the DUT and check + it matches what we assigned + """ + val = random.uniform(-1e307, 1e307) + log = logging.getLogger("cocotb.test") + yield Timer(1) + log.info("Setting the value %g" % val) + dut.stream_in_real = val + yield Timer(1) + yield Timer(1) # FIXME: Workaround for VHPI scheduling - needs investigation + got = float(dut.stream_out_real) + log.info("Read back value %g" % got) + if got != val: + raise TestFailure("Values didn't match!") + + +@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) +def test_real_assign_int(dut): + """Assign a random integer value to ensure we can write types convertible to + int, read it back from the DUT and check it matches what we assigned. + """ + val = random.randint(-2**31, 2**31 - 1) + log = logging.getLogger("cocotb.test") + yield Timer(1) + log.info("Setting the value %i" % val) + dut.stream_in_real <= val + yield Timer(1) + yield Timer(1) # FIXME: Workaround for VHPI scheduling - needs investigation + got = dut.stream_out_real + log.info("Read back value %d" % got) + if got != float(val): + raise TestFailure("Values didn't match!") From c1c6c677e5e5b471d4c83fd265b840ec314b1075 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Tue, 5 May 2020 09:45:00 +0100 Subject: [PATCH 2222/2656] Add support for launching IPython with support for await (#1649) --- cocotb/ipython_support.py | 89 +++++++++++++++++++ documentation/requirements.txt | 1 + .../source/newsfragments/1649.feature.rst | 1 + documentation/source/troubleshooting.rst | 16 ++++ examples/dff/tests/Makefile | 3 - 5 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 cocotb/ipython_support.py create mode 100644 documentation/source/newsfragments/1649.feature.rst diff --git a/cocotb/ipython_support.py b/cocotb/ipython_support.py new file mode 100644 index 00000000..02662ebd --- /dev/null +++ b/cocotb/ipython_support.py @@ -0,0 +1,89 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause +import IPython +from IPython.terminal.ipapp import load_default_config +from IPython.terminal.prompts import Prompts, Token + +import cocotb + + +class SimTimePrompt(Prompts): + """ custom prompt that shows the sim time after a trigger fires """ + _show_time = 1 + + def in_prompt_tokens(self, cli=None): + tokens = super().in_prompt_tokens() + if self._show_time == self.shell.execution_count: + tokens = [ + (Token.Comment, "sim time: {}".format(cocotb.utils.get_sim_time())), + (Token.Text, "\n"), + ] + tokens + return tokens + + +def _runner(shell, x): + """ Handler for async functions """ + ret = cocotb.scheduler.queue_function(x) + shell.prompts._show_time = shell.execution_count + return ret + + +async def embed(user_ns: dict = {}): + """ + Start an ipython shell in the current coroutine. + + Unlike using :func:`IPython.embed` directly, the :keyword:`await` keyword + can be used directly from the shell to wait for triggers. + The :keyword:`yield` keyword from the legacy :ref:`yield-syntax` is not supported. + + This coroutine will complete only when the user exits the interactive session. + + Args: + user_ns: + The variables to have made available in the shell. + Passing ``locals()`` is often a good idea. + ``cocotb`` will automatically be included. + + Notes: + + If your simulator does not provide an appropriate ``stdin``, you may + find you cannot type in the resulting shell. Using simulators in batch + or non-GUI mode may resolve this. This feature is experimental, and + not all simulators are supported. + """ + # ensure cocotb is in the namespace, for convenience + default_ns = dict(cocotb=cocotb) + default_ns.update(user_ns) + + # build the config to enable `await` + c = load_default_config() + c.TerminalInteractiveShell.loop_runner = lambda x: _runner(shell, x) + c.TerminalInteractiveShell.autoawait = True + + # create a shell with access to the dut, and cocotb pre-imported + shell = IPython.terminal.embed.InteractiveShellEmbed( + user_ns=default_ns, + config=c, + ) + + # add our custom prompts + shell.prompts = SimTimePrompt(shell) + + # start the shell in a background thread + @cocotb.external + def run_shell(): + shell() + await run_shell() + + +@cocotb.test() +async def run_ipython(dut): + """ A test that launches an interactive Python shell. + + Do not call this directly - use this as ``make MODULE=cocotb.ipython_support``. + + Within the shell, a global ``dut`` variable pointing to the design will be present. + """ + await cocotb.triggers.Timer(0) # workaround for gh-637 + await embed(user_ns=dict(dut=dut)) diff --git a/documentation/requirements.txt b/documentation/requirements.txt index 2a7790fc..fec731a7 100644 --- a/documentation/requirements.txt +++ b/documentation/requirements.txt @@ -10,3 +10,4 @@ pyenchant sphinx-issues sphinx-argparse towncrier +IPython diff --git a/documentation/source/newsfragments/1649.feature.rst b/documentation/source/newsfragments/1649.feature.rst new file mode 100644 index 00000000..c3e49f0d --- /dev/null +++ b/documentation/source/newsfragments/1649.feature.rst @@ -0,0 +1 @@ +Support for using ``await`` inside an embedded IPython terminal, using :mod:`cocotb.ipython_support`. diff --git a/documentation/source/troubleshooting.rst b/documentation/source/troubleshooting.rst index 276df8fe..a0d6a1c0 100644 --- a/documentation/source/troubleshooting.rst +++ b/documentation/source/troubleshooting.rst @@ -64,3 +64,19 @@ accessible via a TCP socket: .. code:: shell telnet 127.0.0.1 4000 + + +Embedding an IPython shell +========================== + +.. module:: cocotb.ipython_support + +.. versionadded:: 1.4 + +A prebuilt test is included to easily launch an IPython shell in an existing design. + +.. autofunction:: run_ipython + +To embed a shell within an existing test, where it can inspect local variables, the :func:`embed` function can be used. + +.. autofunction:: embed diff --git a/examples/dff/tests/Makefile b/examples/dff/tests/Makefile index 439ef338..84e365f2 100755 --- a/examples/dff/tests/Makefile +++ b/examples/dff/tests/Makefile @@ -31,6 +31,3 @@ ifneq ($(filter $(SIM),ius xcelium),) endif include $(shell cocotb-config --makefiles)/Makefile.sim - -# list all required Python files here -sim: $(MODULE).py From 26d8e18ed9071bf1bed016e37c2a7641c60a665d Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Tue, 5 May 2020 23:07:57 +0100 Subject: [PATCH 2223/2656] Revert "Advertise user survey" This reverts commit d5cd62faa8f96fe2e0e45825724370810bb370d3. The user survey has ended, remove the ads for it. --- README.md | 5 ----- documentation/source/_templates/layout.html | 14 -------------- 2 files changed, 19 deletions(-) delete mode 100644 documentation/source/_templates/layout.html diff --git a/README.md b/README.md index 65229afa..91274276 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,3 @@ -**Your input is needed!** Please help out by taking 10 minutes to fill out this year's cocotb user survey. This survey gives the development community important feedback to steer the future of cocotb into the right direction for your use case. -[**Take the cocotb user survey now**](https://docs.google.com/forms/d/e/1FAIpQLSfD36PldzszbuZjysss3AMvxkf6XCtSbDTVh9qVNNYDaHTZ_w/viewform). - ---- - **cocotb** is a coroutine based cosimulation library for writing VHDL and Verilog testbenches in Python. [![Documentation Status](https://readthedocs.org/projects/cocotb/badge/?version=latest)](https://docs.cocotb.org/en/latest/) diff --git a/documentation/source/_templates/layout.html b/documentation/source/_templates/layout.html deleted file mode 100644 index b779c3f8..00000000 --- a/documentation/source/_templates/layout.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends "!layout.html" %} - -{% block document %} - -
      -

      The cocotb user survey 2020 is awaiting your answers. - Please take 10 minutes to help cocotb developers understand your use of cocotb better, and influence the direction of the project.

      - -
      - -{{ super() }} -{% endblock %} From 6e415a21b29686f0f3b28d55648bceb9ca1c46ca Mon Sep 17 00:00:00 2001 From: Marlon James Date: Fri, 24 Apr 2020 21:06:46 -0700 Subject: [PATCH 2224/2656] Start tests with `react`. Currently tests are started with `add`. This causes problems with how certain Triggers behave, namely `Event` and `NullTrigger`. They behave differently at the beginning of a test than after the test awaits a `GPITrigger`. Using `react` to start the test makes the triggers behave the same during the entirety of the test, due to being in the event loop. --- cocotb/scheduler.py | 22 +++---- .../source/newsfragments/1705.bugfix.rst | 1 + .../test_cases/test_cocotb/test_scheduler.py | 59 ++++++++++++++++++- 3 files changed, 71 insertions(+), 11 deletions(-) create mode 100644 documentation/source/newsfragments/1705.bugfix.rst diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index ec21de07..fc177e98 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -520,8 +520,8 @@ def _schedule_write(self, handle, write_func, *args): self._write_calls.append((write_func, args)) self._writes_pending.set() - def _coroutine_yielded(self, coro, trigger): - """Prime the trigger and update our internal mappings.""" + def _resume_coro_upon(self, coro, trigger): + """Schedule `coro` to be resumed when `trigger` fires.""" self._coro2trigger[coro] = trigger trigger_coros = self._trigger2coros.setdefault(trigger, []) @@ -548,12 +548,10 @@ def _coroutine_yielded(self, coro, trigger): self._trigger2coros.pop(trigger) # replace it with a new trigger that throws back the exception - error_trigger = NullTrigger(outcome=outcomes.Error(e)) - self._coro2trigger[coro] = error_trigger - self._trigger2coros[error_trigger] = [coro] - - # wake up the coroutines - error_trigger.prime(self.react) + self._resume_coro_upon( + coro, + NullTrigger(name="Trigger.prime() Error", outcome=outcomes.Error(e)) + ) def queue(self, coroutine): """Queue a coroutine for execution""" @@ -675,8 +673,12 @@ def add_test(self, test_coro): """Called by the regression manager to queue the next test""" if self._test is not None: raise InternalError("Test was added while another was in progress") + self._test = test_coro - return self.add(test_coro) + self._resume_coro_upon( + test_coro, + NullTrigger(name="Start {!s}".format(test_coro), outcome=outcomes.Value(None)) + ) # This collection of functions parses a trigger out of the object # that was yielded by a coroutine, converting `list` -> `Waitable`, @@ -784,7 +786,7 @@ def schedule(self, coroutine, trigger=None): # it wasn't allowed to yield that result = NullTrigger(outcome=outcomes.Error(exc)) - self._coroutine_yielded(coroutine, result) + self._resume_coro_upon(coroutine, result) # We do not return from here until pending threads have completed, but only # from the main thread, this seems like it could be problematic in cases diff --git a/documentation/source/newsfragments/1705.bugfix.rst b/documentation/source/newsfragments/1705.bugfix.rst new file mode 100644 index 00000000..fdda0d57 --- /dev/null +++ b/documentation/source/newsfragments/1705.bugfix.rst @@ -0,0 +1 @@ +Scheduling behavior is now consistent before and after the first :keyword:`await` of a :class:`~cocotb.triggers.GPITrigger`. diff --git a/tests/test_cases/test_cocotb/test_scheduler.py b/tests/test_cases/test_cocotb/test_scheduler.py index e5670695..e696ad69 100644 --- a/tests/test_cases/test_cocotb/test_scheduler.py +++ b/tests/test_cases/test_cocotb/test_scheduler.py @@ -10,7 +10,7 @@ """ import cocotb -from cocotb.triggers import Join, Timer, RisingEdge, Trigger, NullTrigger +from cocotb.triggers import Join, Timer, RisingEdge, Trigger, NullTrigger, Combine, Event from cocotb.result import TestFailure from cocotb.clock import Clock from common import clock_gen @@ -206,3 +206,60 @@ async def killer(): dut.clk <= 1 await Timer(1) assert not victim_resumed + + +@cocotb.test() +async def test_nulltrigger_reschedule(dut): + """ + Test that NullTrigger doesn't immediately reschedule the waiting coroutine. + + The NullTrigger will be added to the end of the list of pending triggers. + """ + + last_fork = None + + @cocotb.coroutine # TODO: Remove once Combine accepts bare coroutines + async def reschedule(n): + nonlocal last_fork + for i in range(4): + cocotb.log.info("Fork {}, iteration {}, last fork was {}".format(n, i, last_fork)) + assert last_fork != n + last_fork = n + await NullTrigger() + + # Test should start in event loop + await Combine(*(reschedule(i) for i in range(4))) + + await Timer(1) + + # Event loop when simulator returns + await Combine(*(reschedule(i) for i in range(4))) + + +@cocotb.test() +async def test_event_set_schedule(dut): + """ + Test that Event.set() doesn't cause an immediate reschedule. + + The coroutine waiting with Event.wait() will be woken after + the current coroutine awaits a trigger. + """ + + e = Event() + waiter_scheduled = False + + async def waiter(event): + await event.wait() + nonlocal waiter_scheduled + waiter_scheduled = True + + cocotb.fork(waiter(e)) + + e.set() + + # waiter() shouldn't run until after test awaits a trigger + assert waiter_scheduled is False + + await NullTrigger() + + assert waiter_scheduled is True From 571386945a7fc46769e7ac20db0d7526313b49b1 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Mon, 27 Apr 2020 11:23:22 -0700 Subject: [PATCH 2225/2656] Cleanup `test_closedown`. Fork clock coroutine. Add units for `Timer` triggers. Add test to be run after failed test. Remove references to `$fail_test()` as it no longer exists. --- tests/designs/sample_module/sample_module.sv | 3 -- .../test_closedown/test_closedown.py | 38 ++++++++++--------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/tests/designs/sample_module/sample_module.sv b/tests/designs/sample_module/sample_module.sv index 84ae22d5..45083409 100644 --- a/tests/designs/sample_module/sample_module.sv +++ b/tests/designs/sample_module/sample_module.sv @@ -95,9 +95,6 @@ and test_and_gate(and_output, stream_in_ready, stream_in_valid); initial begin $dumpfile("waveform.vcd"); $dumpvars(0,sample_module); - -// TODO: Move into a separate test -// #500000 $fail_test("Test timed out, failing..."); end reg[3:0] temp; diff --git a/tests/test_cases/test_closedown/test_closedown.py b/tests/test_cases/test_closedown/test_closedown.py index 2d0a2135..9b8be599 100644 --- a/tests/test_cases/test_closedown/test_closedown.py +++ b/tests/test_cases/test_closedown/test_closedown.py @@ -27,12 +27,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -""" -A set of tests that demonstrate cocotb functionality - -Also used a regression test of cocotb capabilities -""" - import cocotb from cocotb.triggers import Timer, RisingEdge from cocotb.clock import Clock @@ -46,23 +40,31 @@ def test_read(dut): test_count += 1 -@cocotb.coroutine -def run_external(dut): - yield cocotb.external(test_read)(dut) +async def run_external(dut): + await cocotb.external(test_read)(dut) -@cocotb.coroutine -def clock_mon(dut): - yield RisingEdge(dut.clk) +async def clock_mon(dut): + await RisingEdge(dut.clk) @cocotb.test(expect_fail=True, - expect_error=cocotb.SIM_NAME.lower().startswith(("modelsim", # $fail_test() fails hard on Questa + expect_error=cocotb.SIM_NAME.lower().startswith(("modelsim", # $fatal() fails hard on Questa ))) -def test_failure_from_system_task(dut): - """Allow the dut to call $fail_test() from verilog""" - clock = Clock(dut.clk, 100) - clock.start() +async def test_failure_from_system_task(dut): + """ + Allow the dut to call system tasks from verilog. + $fatal() will fail the test, and scheduler will cleanup forked coroutines. + """ + cocotb.fork(Clock(dut.clk, 100, units='ns').start()) cocotb.fork(clock_mon(dut)) cocotb.fork(run_external(dut)) - yield Timer(10000000) + await Timer(10000000, units='ns') + + +@cocotb.test() +async def test_after_system_task_fail(dut): + """ + Test to run after failed test. + """ + await Timer(1, units='ns') From 611b06f0718a68bb8337fdb4df1f1353cd8c1a9e Mon Sep 17 00:00:00 2001 From: Marlon James Date: Wed, 29 Apr 2020 10:57:53 -0700 Subject: [PATCH 2226/2656] Fix `finish_test`. When using `react` to start a test, there is no handling of the test result by the `RegressionManager` when a test is failed from `finish_test`. This change fixes that and allows the test result to be handled and any further tests to be run. Fixes `test_closedown`. --- cocotb/scheduler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index fc177e98..dc38e4ae 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -811,6 +811,7 @@ def schedule(self, coroutine, trigger=None): def finish_test(self, exc): self._test.abort(exc) + self._check_termination() def finish_scheduler(self, exc): """Directly call into the regression manager and end test From e0f7ea70c735d4196180f7c66b991c683edb7016 Mon Sep 17 00:00:00 2001 From: Mehul Tikekar Date: Thu, 7 May 2020 15:54:20 +0530 Subject: [PATCH 2227/2656] Fixes building libraries on MacOS (#1714) * Replace $ORIGIN to @loader_path * Add install_name to vpi/vhpi/fli libraries * Fix libcocotb not finding libpython for conda python by adding libbpython's path to libcocotb rpath * Remove use of install_name_tool from build_ext --- cocotb_build_libs.py | 49 ++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/cocotb_build_libs.py b/cocotb_build_libs.py index 55308c17..4903c65d 100755 --- a/cocotb_build_libs.py +++ b/cocotb_build_libs.py @@ -38,24 +38,6 @@ def run(self): super().run() - # On osx need extra extra rpath with install_name_tool to find libraries - if sys.platform == "darwin": - def build_extension(self, ext): - super().build_extension(ext) - - subprocess.run([ - "install_name_tool", - "-add_rpath", - "@loader_path", - os.path.join(self.build_lib, ext._file_name), - ], check=True) - subprocess.run([ - "install_name_tool", - "-add_rpath", - "@loader_path/libs", - os.path.join(self.build_lib, ext._file_name), - ], check=True) - # Needed for Windows to not assume python module (generate interface in def file) def get_export_symbols(self, ext): return None @@ -137,7 +119,7 @@ def _gen_import_libs(self, def_dir): ) -def _extra_link_args(lib_name=None, rpath=None): +def _extra_link_args(lib_name=None, rpaths=[]): """ Add linker argument to load dependencies from the directory where vpi/vhpi/fli library is located On osx use `install_name`. @@ -147,7 +129,9 @@ def _extra_link_args(lib_name=None, rpath=None): args = [] if sys.platform == "darwin" and lib_name is not None: args += ["-Wl,-install_name,@rpath/%s.so" % lib_name] - if rpath is not None: + for rpath in rpaths: + if sys.platform == "darwin": + rpath = rpath.replace("$ORIGIN", "@loader_path") args += ["-Wl,-rpath,%s" % rpath] return args @@ -205,7 +189,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): include_dirs=[include_dir], libraries=["gpilog"], sources=[os.path.join(share_lib_dir, "utils", "cocotb_utils.cpp")], - extra_link_args=_extra_link_args(lib_name="libcocotbutils", rpath="$ORIGIN"), + extra_link_args=_extra_link_args(lib_name="libcocotbutils", rpaths=["$ORIGIN"]), extra_compile_args=_extra_cxx_compile_args, ) @@ -221,7 +205,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): include_dirs=[include_dir], library_dirs=python_lib_dirs, sources=[os.path.join(share_lib_dir, "gpi_log", "gpi_logging.cpp")], - extra_link_args=_extra_link_args(lib_name="libgpilog", rpath="$ORIGIN"), + extra_link_args=_extra_link_args(lib_name="libgpilog", rpaths=["$ORIGIN"]), extra_compile_args=_extra_cxx_compile_args, ) @@ -235,7 +219,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): libraries=[_get_python_lib_link(), "gpilog", "cocotbutils"], library_dirs=python_lib_dirs, sources=[os.path.join(share_lib_dir, "embed", "gpi_embed.cpp")], - extra_link_args=_extra_link_args(lib_name="libcocotb", rpath="$ORIGIN"), + extra_link_args=_extra_link_args(lib_name="libcocotb", rpaths=["$ORIGIN"] + python_lib_dirs), extra_compile_args=_extra_cxx_compile_args, ) @@ -251,7 +235,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): os.path.join(share_lib_dir, "gpi", "GpiCbHdl.cpp"), os.path.join(share_lib_dir, "gpi", "GpiCommon.cpp"), ], - extra_link_args=_extra_link_args(lib_name="libgpi", rpath="$ORIGIN"), + extra_link_args=_extra_link_args(lib_name="libgpi", rpaths=["$ORIGIN"]), extra_compile_args=_extra_cxx_compile_args, ) @@ -265,7 +249,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): library_dirs=python_lib_dirs, sources=[os.path.join(share_lib_dir, "simulator", "simulatormodule.cpp")], extra_compile_args=_extra_cxx_compile_args, - extra_link_args=_extra_link_args(rpath="$ORIGIN/libs"), + extra_link_args=_extra_link_args(rpaths=["$ORIGIN/libs"]), ) # The libraries in this list are compiled in order of their appearance. @@ -277,8 +261,9 @@ def _get_common_lib_ext(include_dir, share_lib_dir): def _get_vpi_lib_ext( include_dir, share_lib_dir, sim_define, extra_lib=[], extra_lib_dir=[] ): + lib_name = "libcocotbvpi_" + sim_define.lower() libcocotbvpi = Extension( - os.path.join("cocotb", "libs", "libcocotbvpi_" + sim_define.lower()), + os.path.join("cocotb", "libs", lib_name), define_macros=[("VPI_CHECKING", "1")] + [(sim_define, "")], include_dirs=[include_dir], libraries=["gpi", "gpilog"] + extra_lib, @@ -287,7 +272,7 @@ def _get_vpi_lib_ext( os.path.join(share_lib_dir, "vpi", "VpiImpl.cpp"), os.path.join(share_lib_dir, "vpi", "VpiCbHdl.cpp"), ], - extra_link_args=_extra_link_args(rpath="$ORIGIN"), + extra_link_args=_extra_link_args(lib_name=lib_name, rpaths=["$ORIGIN"]), extra_compile_args=_extra_cxx_compile_args, ) @@ -297,8 +282,9 @@ def _get_vpi_lib_ext( def _get_vhpi_lib_ext( include_dir, share_lib_dir, sim_define, extra_lib=[], extra_lib_dir=[] ): + lib_name = "libcocotbvhpi_" + sim_define.lower() libcocotbvhpi = Extension( - os.path.join("cocotb", "libs", "libcocotbvhpi_" + sim_define.lower()), + os.path.join("cocotb", "libs", lib_name), include_dirs=[include_dir], define_macros=[("VHPI_CHECKING", 1)] + [(sim_define, "")], libraries=["gpi", "gpilog", "stdc++"] + extra_lib, @@ -307,7 +293,7 @@ def _get_vhpi_lib_ext( os.path.join(share_lib_dir, "vhpi", "VhpiImpl.cpp"), os.path.join(share_lib_dir, "vhpi", "VhpiCbHdl.cpp"), ], - extra_link_args=_extra_link_args(rpath="$ORIGIN"), + extra_link_args=_extra_link_args(lib_name=lib_name, rpaths=["$ORIGIN"]), extra_compile_args=_extra_cxx_compile_args, ) @@ -379,8 +365,9 @@ def get_ext(): modelsim_include_dir = os.path.join(modelsim_dir, "include") mti_path = os.path.join(modelsim_include_dir, "mti.h") if os.path.isfile(mti_path): + lib_name = "libcocotbfli_modelsim" fli_ext = Extension( - os.path.join("cocotb", "libs", "libcocotbfli_modelsim"), + os.path.join("cocotb", "libs", lib_name), include_dirs=[include_dir, modelsim_include_dir], libraries=["gpi", "gpilog", "stdc++"] + modelsim_extra_lib, library_dirs=modelsim_extra_lib_path, @@ -389,7 +376,7 @@ def get_ext(): os.path.join(share_lib_dir, "fli", "FliCbHdl.cpp"), os.path.join(share_lib_dir, "fli", "FliObjHdl.cpp"), ], - extra_link_args=_extra_link_args(rpath="$ORIGIN"), + extra_link_args=_extra_link_args(lib_name=lib_name, rpaths=["$ORIGIN"]), extra_compile_args=_extra_cxx_compile_args, ) From de04f558efd0cfd80311b7cf705c6a8606e78db6 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Thu, 7 May 2020 10:53:05 -0700 Subject: [PATCH 2228/2656] Remove leftover gh-637 workarounds. Now that gh-1705 is merged, these workarounds are no longer needed. --- cocotb/ipython_support.py | 1 - .../test_cases/test_external/test_external.py | 23 ------------------- 2 files changed, 24 deletions(-) diff --git a/cocotb/ipython_support.py b/cocotb/ipython_support.py index 02662ebd..45d1fc64 100644 --- a/cocotb/ipython_support.py +++ b/cocotb/ipython_support.py @@ -85,5 +85,4 @@ async def run_ipython(dut): Within the shell, a global ``dut`` variable pointing to the design will be present. """ - await cocotb.triggers.Timer(0) # workaround for gh-637 await embed(user_ns=dict(dut=dut)) diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index 769f6d1e..2daac182 100755 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -88,7 +88,6 @@ def clock_monitor(dut): def test_time_in_external(dut): """Test that the simulation time does not advance if the wrapped external routine does not itself yield""" - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) yield Timer(10, 'ns') time = get_sim_time('ns') dut._log.info("Time at start of test = %d" % time) @@ -142,8 +141,6 @@ def test_ext_call_return(dut): @cocotb.test() def test_multiple_externals(dut): - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) - value = yield external(return_two)(dut) dut._log.info("First one completed") assert value == 2 @@ -155,8 +152,6 @@ def test_multiple_externals(dut): @cocotb.test() def test_external_from_readonly(dut): - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) - yield ReadOnly() dut._log.info("In readonly") value = yield external(return_two)(dut) @@ -210,9 +205,6 @@ def test_ext_exit_error(dut): @cocotb.test() def test_external_raised_exception(dut): """ Test that exceptions thrown by @external functions can be caught """ - # workaround for gh-637 - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) - @external def func(): raise ValueError() @@ -228,9 +220,6 @@ def func(): @cocotb.test() def test_external_returns_exception(dut): """ Test that exceptions can be returned by @external functions """ - # workaround for gh-637 - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) - @external def func(): return ValueError() @@ -247,9 +236,6 @@ def func(): @cocotb.test() def test_function_raised_exception(dut): """ Test that exceptions thrown by @function coroutines can be caught """ - # workaround for gh-637 - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) - @cocotb.function def func(): raise ValueError() @@ -270,9 +256,6 @@ def ext(): @cocotb.test() def test_function_returns_exception(dut): """ Test that exceptions can be returned by @function coroutines """ - # workaround for gh-637 - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) - @cocotb.function def func(): return ValueError() @@ -296,9 +279,6 @@ def test_function_from_weird_thread_fails(dut): """ Test that background threads caling a @function do not hang forever """ - # workaround for gh-637 - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) - func_started = False caller_resumed = False raised = False @@ -344,9 +324,6 @@ def test_function_called_in_parallel(dut): Test that the same `@function` can be called from two parallel background threads. """ - # workaround for gh-637 - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) - @cocotb.function def function(x): yield Timer(1) From 70b52846b449495a91377c4dfe7459ca993f784f Mon Sep 17 00:00:00 2001 From: Marlon James Date: Thu, 7 May 2020 11:06:27 -0700 Subject: [PATCH 2229/2656] Cleanup test_external module. --- .../test_cases/test_external/test_external.py | 258 ++++++++++-------- 1 file changed, 151 insertions(+), 107 deletions(-) diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index 2daac182..4b47245a 100755 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -32,8 +32,8 @@ Also used a regression test of cocotb capabilities """ - import threading + import cocotb from cocotb.result import TestFailure from cocotb.triggers import Timer, RisingEdge, ReadOnly @@ -42,25 +42,21 @@ from cocotb.utils import get_sim_time -# Tests relating to calling convention and operation - def return_two(dut): - # dut._log.info("Sleeping") return 2 @cocotb.function -def yield_to_readwrite(dut): - yield RisingEdge(dut.clk) - dut._log.info("Returning from yield_to_readwrite") - yield RisingEdge(dut.clk) - dut._log.info("Returning from yield_to_readwrite") - yield Timer(1, "ns") +async def await_two_clock_edges(dut): + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + await Timer(1, units='ns') + dut._log.info("Returning from await_two_clock_edges") return 2 def calls_cocotb_function(dut): - return yield_to_readwrite(dut) + return await_two_clock_edges(dut) def print_sim_time(dut, base_time): @@ -75,142 +71,185 @@ def print_sim_time(dut, base_time): dut._log.info("external function has ended") -@cocotb.coroutine -def clock_monitor(dut): - count = 0 - while True: - yield RisingEdge(dut.clk) - yield Timer(1000) - count += 1 - - @cocotb.test() -def test_time_in_external(dut): - """Test that the simulation time does not advance if the wrapped external - routine does not itself yield""" - yield Timer(10, 'ns') +async def test_time_in_external(dut): + """ + Test that the simulation time does not advance if the wrapped external + routine does not call @function + """ + await Timer(10, units='ns') time = get_sim_time('ns') dut._log.info("Time at start of test = %d" % time) for i in range(100): dut._log.info("Loop call %d" % i) - yield external(print_sim_time)(dut, time) + await external(print_sim_time)(dut, time) time_now = get_sim_time('ns') - yield Timer(10, 'ns') + await Timer(10, units='ns') if time != time_now: raise TestFailure("Time has elapsed over external call") -@cocotb.function -def wait_cycles(dut, n): - for _ in range(n): - yield RisingEdge(dut.clk) - - -def wait_cycles_wrapper(dut, n): - return wait_cycles(dut, n) - # Cadence simulators: "Unable set up RisingEdge(...) Trigger" with VHDL (see #1076) @cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else False) -def test_time_in_external_yield(dut): - """Test that an external function calling back into a cocotb function - takes the expected amount of time""" - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) - yield Timer(10, 'ns') +async def test_time_in_function(dut): + """ + Test that an @external function calling back into a cocotb @function + takes the expected amount of time + """ + @cocotb.function + def wait_cycles(dut, n): + for _ in range(n): + yield RisingEdge(dut.clk) + + @external + def wait_cycles_wrapper(dut, n): + return wait_cycles(dut, n) + + clk_gen = cocotb.fork(Clock(dut.clk, 100, units='ns').start()) + await Timer(10, units='ns') for n in range(5): for i in range(20): - yield RisingEdge(dut.clk) - time = get_sim_time() + await RisingEdge(dut.clk) + time = get_sim_time('ns') expected_after = time + 100*n - yield external(wait_cycles_wrapper)(dut, n) - time_after = get_sim_time() + await wait_cycles_wrapper(dut, n) + time_after = get_sim_time('ns') if expected_after != time_after: raise TestFailure("Wrong time elapsed in external call") + # Cadence simulators: "Unable set up RisingEdge(...) Trigger" with VHDL (see #1076) @cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else False) -def test_ext_call_return(dut): - """Test ability to yield on an external non cocotb coroutine decorated - function""" - mon = cocotb.scheduler.queue(clock_monitor(dut)) - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) - value = yield external(return_two)(dut) +async def test_external_call_return(dut): + """ + Test ability to await an external function that is not a coroutine using @external + """ + async def clock_monitor(dut): + count = 0 + while True: + await RisingEdge(dut.clk) + await Timer(1000, units='ns') + count += 1 + + mon = cocotb.fork(clock_monitor(dut)) + clk_gen = cocotb.fork(Clock(dut.clk, 100, units='ns').start()) + value = await external(return_two)(dut) assert value == 2 @cocotb.test() -def test_multiple_externals(dut): - value = yield external(return_two)(dut) +async def test_consecutive_externals(dut): + """ + Test that multiple @external functions can be called in the same test + """ + value = await external(return_two)(dut) dut._log.info("First one completed") assert value == 2 - value = yield external(return_two)(dut) + value = await external(return_two)(dut) dut._log.info("Second one completed") assert value == 2 @cocotb.test() -def test_external_from_readonly(dut): - yield ReadOnly() +async def test_external_from_readonly(dut): + """ + Test that @external functions that don't consume simulation time + can be called from ReadOnly state + """ + await ReadOnly() dut._log.info("In readonly") - value = yield external(return_two)(dut) + value = await external(return_two)(dut) assert value == 2 + +@cocotb.test() +async def test_function_from_readonly(dut): + """ + Test that @external functions that call @functions that await Triggers + can be called from ReadOnly state + """ + clk_gen = cocotb.fork(Clock(dut.clk, 100, units='ns').start()) + + await ReadOnly() + dut._log.info("In readonly") + value = await external(calls_cocotb_function)(dut) + assert value == 2 + + # Cadence simulators: "Unable set up RisingEdge(...) Trigger" with VHDL (see #1076) @cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else False) -def test_external_that_yields(dut): - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) +async def test_function_that_awaits(dut): + """ + Test that @external functions can call @function coroutines that + awaits Triggers and return values back through to + the test + """ + clk_gen = cocotb.fork(Clock(dut.clk, 100, units='ns').start()) - value = yield external(calls_cocotb_function)(dut) + value = await external(calls_cocotb_function)(dut) assert value == 2 + # Cadence simulators: "Unable set up RisingEdge(...) Trigger" with VHDL (see #1076) @cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else False) -def test_external_and_continue(dut): - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) +async def test_await_after_function(dut): + """ + Test that awaiting a Trigger works after returning + from @external functions that call @functions that consume + simulation time + """ + clk_gen = cocotb.fork(Clock(dut.clk, 100, units='ns').start()) - value = yield external(calls_cocotb_function)(dut) + value = await external(calls_cocotb_function)(dut) assert value == 2 - yield Timer(10, "ns") - yield RisingEdge(dut.clk) + await Timer(10, units="ns") + await RisingEdge(dut.clk) -@cocotb.coroutine -def run_external(dut): - value = yield external(calls_cocotb_function)(dut) - return value - # Cadence simulators: "Unable set up RisingEdge(...) Trigger" with VHDL (see #1076) @cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else False) -def test_external_from_fork(dut): - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) +async def test_external_from_fork(dut): + """ + Test that @external functions work when awaited from a forked + task + """ + async def run_function(dut): + value = await external(calls_cocotb_function)(dut) + return value - coro = cocotb.fork(run_external(dut)) - value = yield coro.join() - assert value == 2 + async def run_external(dut): + value = await external(return_two)(dut) + return value - dut._log.info("Back from join") + clk_gen = cocotb.fork(Clock(dut.clk, 100, units='ns').start()) + coro1 = cocotb.fork(run_function(dut)) + value = await coro1.join() + assert value == 2 + dut._log.info("Back from join 1") -@cocotb.test(expect_fail=True, skip=True) -def test_ext_exit_error(dut): - """Test that a premature exit of the sim at its request still results in - the clean close down of the sim world""" - yield external(return_two)(dut) - yield Timer(1000) + value = 0 + coro2 = cocotb.fork(run_external(dut)) + value = await coro2.join() + assert value == 2 + dut._log.info("Back from join 2") @cocotb.test() -def test_external_raised_exception(dut): - """ Test that exceptions thrown by @external functions can be caught """ +async def test_external_raised_exception(dut): + """ + Test that exceptions thrown by @external functions can be caught + """ @external def func(): raise ValueError() try: - yield func() + await func() except ValueError: pass else: @@ -218,14 +257,16 @@ def func(): @cocotb.test() -def test_external_returns_exception(dut): - """ Test that exceptions can be returned by @external functions """ +async def test_external_returns_exception(dut): + """ + Test that exceptions can be returned by @external functions + """ @external def func(): return ValueError() try: - result = yield func() + result = await func() except ValueError: raise TestFailure('Exception should not have been thrown') @@ -234,19 +275,20 @@ def func(): @cocotb.test() -def test_function_raised_exception(dut): - """ Test that exceptions thrown by @function coroutines can be caught """ +async def test_function_raised_exception(dut): + """ + Test that exceptions thrown by @function coroutines can be caught + """ @cocotb.function - def func(): + async def func(): raise ValueError() - yield @external def ext(): return func() try: - yield ext() + await ext() except ValueError: pass else: @@ -254,19 +296,21 @@ def ext(): @cocotb.test() -def test_function_returns_exception(dut): - """ Test that exceptions can be returned by @function coroutines """ +async def test_function_returns_exception(dut): + """ + Test that exceptions can be returned by @function coroutines + """ @cocotb.function - def func(): + def gen_func(): return ValueError() yield @external def ext(): - return func() + return gen_func() try: - result = yield ext() + result = await ext() except ValueError: raise TestFailure('Exception should not have been thrown') @@ -275,7 +319,7 @@ def ext(): @cocotb.test() -def test_function_from_weird_thread_fails(dut): +async def test_function_from_weird_thread_fails(dut): """ Test that background threads caling a @function do not hang forever """ @@ -284,10 +328,10 @@ def test_function_from_weird_thread_fails(dut): raised = False @cocotb.function - def func(): + async def func(): nonlocal func_started func_started = True - yield Timer(10) + await Timer(10, units='ns') def function_caller(): nonlocal raised @@ -309,24 +353,24 @@ def ext(): task = cocotb.fork(ext()) - yield Timer(20) + await Timer(20, units='ns') assert caller_resumed, "Caller was never resumed" assert not func_started, "Function should never have started" assert raised, "No exception was raised to warn the user" - yield task.join() + await task.join() @cocotb.test() -def test_function_called_in_parallel(dut): +async def test_function_called_in_parallel(dut): """ Test that the same `@function` can be called from two parallel background threads. """ @cocotb.function - def function(x): - yield Timer(1) + async def function(x): + await Timer(1, units='ns') return x @cocotb.external @@ -335,7 +379,7 @@ def call_function(x): t1 = cocotb.fork(call_function(1)) t2 = cocotb.fork(call_function(2)) - v1 = yield t1 - v2 = yield t2 + v1 = await t1 + v2 = await t2 assert v1 == 1, v1 assert v2 == 2, v2 From 2dd85346bbdc670df786055f5da7d9a352f9d81b Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 27 Apr 2020 23:22:12 +0200 Subject: [PATCH 2230/2656] Add requirements for organization-hosted extensions Based on discussion in the maintainer's call --- documentation/source/extensions.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/documentation/source/extensions.rst b/documentation/source/extensions.rst index 661e3ad7..142f6f78 100644 --- a/documentation/source/extensions.rst +++ b/documentation/source/extensions.rst @@ -111,3 +111,12 @@ Code hosting The source code of cocotb extensions can be hosted anywhere. If authors wish to do so, extensions can also be hosted on GitHub in the `cocotb GitHub organization `_ (e.g. ``github.com/cocotb/cocotbext-EXTNAME``). Please file a `GitHub issue in the cocotb repository `_ if you'd like to discuss that. + +Note that hosting extensions within the cocotb organization is decided on a case-by-case basis by the cocotb maintainers. +At least, a cocotb-hosted extension needs to fulfill the following requirements: + +* It needs tests that can be run in order to see that the extension works + and continues to work as cocotb itself changes, especially when a new release is upcoming. +* It needs documentation (preferably in Sphinx) so that users know how to use the extension. +* We must have access to the PyPi project so that we can continue to upload new releases + in case the extension maintainer ("Owner") becomes unresponsive. From a4eb270a82e9b0e4f07243ba311d60bde21ad866 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 4 May 2020 18:03:17 +0200 Subject: [PATCH 2231/2656] Fix broken reporting of available SIM settings This duplicates two lines from Makefile.sim which is not included here. --- Makefile | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 686a58c1..bf757a1e 100644 --- a/Makefile +++ b/Makefile @@ -55,6 +55,9 @@ jenkins: do_tests test: $(MAKE) do_tests; ret=$$?; ./bin/combine_results.py && exit $$ret +COCOTB_MAKEFILES_DIR = $(realpath $(shell cocotb-config --makefiles)) +AVAILABLE_SIMULATORS = $(patsubst .%,%,$(suffix $(wildcard $(COCOTB_MAKEFILES_DIR)/simulators/Makefile.*))) + .PHONY: help help: @echo "" @@ -66,8 +69,10 @@ help: @echo " (return error code 1 if any failure was found)" @echo "clean - remove build directory and all simulation artefacts" @echo "" - @echo "Default simulator is Icarus. To use another set environment variable SIM as below" - @for X in $(shell ls cocotb/share/makefiles/simulators/); do \ - echo $$X | sed 's/^[^.]*./export SIM=/';\ + @echo "The default simulator is Icarus Verilog." + @echo "To use another, set the environment variable SIM as below." + @echo "Available simulators:" + @for X in $(sort $(AVAILABLE_SIMULATORS)); do \ + echo export SIM=$$X; \ done @echo "" From 1aae26c9f6cd367b07d94f2e29864f758edc9a3a Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sun, 10 May 2020 00:18:39 +0200 Subject: [PATCH 2232/2656] Make expect_error more specific and link Icarus issue --- .../test_discovery/test_discovery.py | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 1b86d742..c8468574 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -107,14 +107,12 @@ def access_signal(dut): dut.stream_in_data.value.integer, 1)) -@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"], - skip=cocotb.LANGUAGE in ["vhdl"]) +@cocotb.test( + # Icarus 10.3 doesn't support bit-selects, see https://github.com/steveicarus/iverilog/issues/323 + expect_error=IndexError if cocotb.SIM_NAME.lower().startswith("icarus") else False, + skip=cocotb.LANGUAGE in ["vhdl"]) def access_single_bit(dut): - """ - Access a single bit in a vector of the DUT - - Icarus v0.96 doesn't support single bit access to vectors - """ + """Access a single bit in a vector of the DUT""" dut.stream_in_data <= 0 yield Timer(10) dut._log.info("%s = %d bits" % @@ -127,14 +125,12 @@ def access_single_bit(dut): dut.stream_out_data_comb.value.integer, (1 << 2))) -@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"], - skip=cocotb.LANGUAGE in ["vhdl"]) +@cocotb.test( + # Icarus 10.3 doesn't support bit-selects, see https://github.com/steveicarus/iverilog/issues/323 + expect_error=IndexError if cocotb.SIM_NAME.lower().startswith("icarus") else False, + skip=cocotb.LANGUAGE in ["vhdl"]) def access_single_bit_assignment(dut): - """ - Access a single bit in a vector of the DUT using the assignment mechanism - - Icarus v0.96 doesn't support single bit access to vectors - """ + """Access a single bit in a vector of the DUT using the assignment mechanism""" dut.stream_in_data = 0 yield Timer(10) dut._log.info("%s = %d bits" % From aba57c01b9549082b66b6fe86b45691139f963a3 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 7 May 2020 23:34:49 +0200 Subject: [PATCH 2233/2656] Fix warning of Verilator linter Warning/error is %Warning-COMBDLY: /workspace/cocotb/tests/test_cases/test_cocotb/../../../tests/designs/sample_module/sample_module.sv:88:20: Delayed assignments (<=) in non-clocked (non flop or latch) block : ... Suggest blocking assignments (=) 88 | stream_out_int <= stream_in_int; | ^~ ... Use "/* verilator lint_off COMBDLY */" and lint_on around source to disable this message. *** See the manual before disabling this, else you may end up with different sim results. %Error: Exiting due to 1 warning(s) --- tests/designs/sample_module/sample_module.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/designs/sample_module/sample_module.sv b/tests/designs/sample_module/sample_module.sv index 45083409..670f1c80 100644 --- a/tests/designs/sample_module/sample_module.sv +++ b/tests/designs/sample_module/sample_module.sv @@ -85,7 +85,7 @@ always @(stream_in_real) stream_out_real = stream_in_real; always @(stream_in_int) - stream_out_int <= stream_in_int; + stream_out_int = stream_in_int; test_if struct_var; `endif From 0ce2996bc5a5bf50f51fe1820ed534a2163e0149 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 27 Apr 2020 15:09:11 +0100 Subject: [PATCH 2234/2656] Rename SIM=aldec to SIM=riviera Closes #1691 --- cocotb/share/makefiles/Makefile.deprecations | 5 +++++ .../simulators/{Makefile.aldec => Makefile.riviera} | 0 documentation/source/newsfragments/1691.change.rst | 1 + documentation/source/simulator_support.rst | 9 +++++++-- 4 files changed, 13 insertions(+), 2 deletions(-) rename cocotb/share/makefiles/simulators/{Makefile.aldec => Makefile.riviera} (100%) create mode 100644 documentation/source/newsfragments/1691.change.rst diff --git a/cocotb/share/makefiles/Makefile.deprecations b/cocotb/share/makefiles/Makefile.deprecations index 56d05807..1b08fa82 100644 --- a/cocotb/share/makefiles/Makefile.deprecations +++ b/cocotb/share/makefiles/Makefile.deprecations @@ -5,3 +5,8 @@ ifdef VERILATOR_TRACE $(warning VERILATOR_TRACE is deprecated, see the "Simulator Support" section in the documentation.) endif + +ifeq ($(SIM_LOWERCASE),aldec) + $(warning Using SIM=aldec is deprecated, please use SIM=riviera instead.) + SIM_LOWERCASE := riviera +endif diff --git a/cocotb/share/makefiles/simulators/Makefile.aldec b/cocotb/share/makefiles/simulators/Makefile.riviera similarity index 100% rename from cocotb/share/makefiles/simulators/Makefile.aldec rename to cocotb/share/makefiles/simulators/Makefile.riviera diff --git a/documentation/source/newsfragments/1691.change.rst b/documentation/source/newsfragments/1691.change.rst new file mode 100644 index 00000000..9429ed09 --- /dev/null +++ b/documentation/source/newsfragments/1691.change.rst @@ -0,0 +1 @@ +The :make:var:`SIM` setting for Aldec Riviera-PRO has changed from ``aldec`` to ``riviera``. diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index b5081dff..3bef0cf7 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -129,21 +129,26 @@ cocotb currently only supports VPI for Synopsys VCS, not VHPI. .. _sim-aldec: +.. _sim-riviera: Aldec Riviera-PRO ================= -In order to use this simulator, set :make:var:`SIM` to ``aldec``: +In order to use this simulator, set :make:var:`SIM` to ``riviera``: .. code-block:: bash - make SIM=aldec + make SIM=riviera .. note:: On Windows, do not install the C++ compiler, i.e. unselect it during the installation process of Riviera-PRO. (A workaround is to remove or rename the ``mingw`` directory located in the Riviera-PRO installation directory.) +.. deprecated:: 1.4 + + Support for Riviera-PRO was previously available with ``SIM=aldec``. + The :envvar:`LICENSE_QUEUE` environment variable can be used for this simulator – this setting will be mirrored in the TCL ``license_queue`` variable to control runtime license checkouts. From a1643c044211b4f9ba3f52ae73b72ee730f09543 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Tue, 18 Feb 2020 23:10:38 -0600 Subject: [PATCH 2235/2656] Move product and version information to simulatormodule Previously, this information was gathered during startup, and passed to Python via gpi_embed. This change reduces the responsibility of gpi_embed and allows either C code or Python code to determine the product information. --- cocotb/__init__.py | 8 ++++ cocotb/share/include/embed.h | 2 +- cocotb/share/include/gpi.h | 22 +++++----- cocotb/share/lib/embed/gpi_embed.cpp | 31 +++++--------- cocotb/share/lib/fli/FliCbHdl.cpp | 40 ++++--------------- cocotb/share/lib/fli/FliImpl.cpp | 24 +++++++++++ cocotb/share/lib/fli/FliImpl.h | 2 + cocotb/share/lib/gpi/GpiCommon.cpp | 14 ++++++- cocotb/share/lib/gpi/gpi_priv.h | 7 +++- .../share/lib/simulator/simulatormodule.cpp | 14 +++++++ cocotb/share/lib/simulator/simulatormodule.h | 4 ++ cocotb/share/lib/vhpi/VhpiCbHdl.cpp | 9 +---- cocotb/share/lib/vhpi/VhpiImpl.cpp | 28 +++++++++++++ cocotb/share/lib/vhpi/VhpiImpl.h | 2 + cocotb/share/lib/vpi/VpiCbHdl.cpp | 14 +++---- cocotb/share/lib/vpi/VpiImpl.cpp | 22 ++++++++++ cocotb/share/lib/vpi/VpiImpl.h | 2 + 17 files changed, 161 insertions(+), 84 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index b1cbb992..a1604e43 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -137,6 +137,14 @@ def _initialise_testbench(root_name): """ _rlock.acquire() + from cocotb import simulator + + global SIM_NAME, SIM_VERSION + SIM_NAME = simulator.get_simulator_product() + SIM_VERSION = simulator.get_simulator_version() + + cocotb.log.info("Running on {} version {}".format(SIM_NAME, SIM_VERSION)) + memcheck_port = os.getenv('MEMCHECK') if memcheck_port is not None: mem_debug(int(memcheck_port)) diff --git a/cocotb/share/include/embed.h b/cocotb/share/include/embed.h index 6ef0752f..902c793f 100644 --- a/cocotb/share/include/embed.h +++ b/cocotb/share/include/embed.h @@ -41,7 +41,7 @@ extern "C" { extern void embed_init_python(void); extern void embed_sim_cleanup(void); -extern int embed_sim_init(gpi_sim_info_t *info); +extern int embed_sim_init(int argc, char const* const* argv); extern void embed_sim_event(gpi_event_t level, const char *msg); #ifdef __cplusplus diff --git a/cocotb/share/include/gpi.h b/cocotb/share/include/gpi.h index 647a8bdd..2116020d 100644 --- a/cocotb/share/include/gpi.h +++ b/cocotb/share/include/gpi.h @@ -111,15 +111,6 @@ typedef enum gpi_event_e { SIM_FAIL = 2, } gpi_event_t; -typedef struct gpi_sim_info_s -{ - int32_t argc; - char **argv; - char *product; - char *version; - int32_t *reserved[4]; -} gpi_sim_info_t; - // Functions for controlling/querying the simulation state // Stop the simulator @@ -132,6 +123,19 @@ void gpi_cleanup(void); void gpi_get_sim_time(uint32_t *high, uint32_t *low); void gpi_get_sim_precision(int32_t *precision); +/** + * Returns a string with the running simulator product information + * + * @return simulator product string + */ +const char *gpi_get_simulator_product(void); + +/** + * Returns a string with the running simulator version + * + * @return simulator version string + */ +const char *gpi_get_simulator_version(void); // Functions for extracting a gpi_sim_hdl to an object // Returns a handle to the root simulation object, diff --git a/cocotb/share/lib/embed/gpi_embed.cpp b/cocotb/share/lib/embed/gpi_embed.cpp index c6c3fd4c..31c0c1c3 100644 --- a/cocotb/share/lib/embed/gpi_embed.cpp +++ b/cocotb/share/lib/embed/gpi_embed.cpp @@ -219,7 +219,7 @@ static int get_module_ref(const char *modname, PyObject **mod) return 0; } -extern "C" int embed_sim_init(gpi_sim_info_t *info) +extern "C" int embed_sim_init(int argc, char const * const * argv) { FENTER @@ -257,8 +257,11 @@ extern "C" int embed_sim_init(gpi_sim_info_t *info) PyGILState_STATE gstate = PyGILState_Ensure(); to_python(); - if (get_module_ref("cocotb", &cocotb_module)) + if (get_module_ref("cocotb", &cocotb_module)) { goto cleanup; + } + + LOG_INFO("Python interpreter initialized and cocotb loaded!"); if (get_module_ref("cocotb.log", &cocotb_log_module)) { goto cleanup; @@ -295,16 +298,16 @@ extern "C" int embed_sim_init(gpi_sim_info_t *info) set_log_filter(simlog_func); // Note: This function steals a reference to simlog_func. // Build argv for cocotb module - argv_list = PyList_New(info->argc); // New reference + argv_list = PyList_New(argc); // New reference if (argv_list == NULL) { PyErr_Print(); LOG_ERROR("Unable to create argv list"); goto cleanup; } - for (i = 0; i < info->argc; i++) { + for (i = 0; i < argc; i++) { // Decode, embedding non-decodable bytes using PEP-383. This can only // fail with MemoryError or similar. - PyObject *argv_item = PyUnicode_DecodeLocale(info->argv[i], "surrogateescape"); // New reference + PyObject *argv_item = PyUnicode_DecodeLocale(argv[i], "surrogateescape"); // New reference if (argv_item == NULL) { PyErr_Print(); LOG_ERROR("Unable to convert command line argument %d to Unicode string.", i); @@ -323,28 +326,12 @@ extern "C" int embed_sim_init(gpi_sim_info_t *info) } // Add argc to cocotb module - if (-1 == PyModule_AddIntConstant(cocotb_module, "argc", info->argc)) { + if (-1 == PyModule_AddIntConstant(cocotb_module, "argc", argc)) { PyErr_Print(); LOG_ERROR("Unable to set argc"); goto cleanup; } - LOG_INFO("Running on %s version %s", info->product, info->version); - LOG_INFO("Python interpreter initialized and cocotb loaded!"); - - // Now that logging has been set up ok, we initialize the testbench - if (-1 == PyModule_AddStringConstant(cocotb_module, "SIM_NAME", info->product)) { - PyErr_Print(); - LOG_ERROR("Unable to set SIM_NAME"); - goto cleanup; - } - - if (-1 == PyModule_AddStringConstant(cocotb_module, "SIM_VERSION", info->version)) { - PyErr_Print(); - LOG_ERROR("Unable to set SIM_VERSION"); - goto cleanup; - } - // Set language in use as an attribute to cocotb module, or None if not provided { const char *lang = getenv("TOPLEVEL_LANG"); diff --git a/cocotb/share/lib/fli/FliCbHdl.cpp b/cocotb/share/lib/fli/FliCbHdl.cpp index f9328d71..865c36bb 100644 --- a/cocotb/share/lib/fli/FliCbHdl.cpp +++ b/cocotb/share/lib/fli/FliCbHdl.cpp @@ -192,42 +192,16 @@ static std::vector get_argv() int FliStartupCbHdl::run_callback() { - gpi_sim_info_t sim_info; - char *c_info = mti_GetProductVersion(); // Returned pointer must not be freed - std::string info = c_info; - std::string search = " Version "; - std::size_t found = info.find(search); - - std::string product_str = c_info; - std::string version_str = c_info; - - if (found != std::string::npos) { - product_str = info.substr(0,found); - version_str = info.substr(found+search.length()); - - LOG_DEBUG("Found Version string at %d", found); - LOG_DEBUG(" product: %s", product_str.c_str()); - LOG_DEBUG(" version: %s", version_str.c_str()); - } - - std::vector product(product_str.begin(), product_str.end()); - std::vector version(version_str.begin(), version_str.end()); - product.push_back('\0'); - version.push_back('\0'); - - sim_info.product = &product[0]; - sim_info.version = &version[0]; - - std::vector argv = get_argv(); - std::vector argv_cstr; - for (const auto& arg : argv) { - argv_cstr.push_back(const_cast(arg.c_str())); + std::vector const argv_storage = get_argv(); + std::vector argv_cstr; + for (const auto& arg : argv_storage) { + argv_cstr.push_back(arg.c_str()); } - sim_info.argc = static_cast(argv.size()); - sim_info.argv = argv_cstr.data(); + int argc = static_cast(argv_storage.size()); + const char** argv = argv_cstr.data(); - gpi_embed_init(&sim_info); + gpi_embed_init(argc, argv); return 0; } diff --git a/cocotb/share/lib/fli/FliImpl.cpp b/cocotb/share/lib/fli/FliImpl.cpp index c69e76b3..09ab5a79 100644 --- a/cocotb/share/lib/fli/FliImpl.cpp +++ b/cocotb/share/lib/fli/FliImpl.cpp @@ -434,6 +434,30 @@ void FliImpl::get_sim_precision(int32_t *precision) *precision = mti_GetResolutionLimit(); } +const char *FliImpl::get_simulator_product() +{ + if (m_product.empty() && m_version.empty()) { + const std::string info = mti_GetProductVersion(); // Returned pointer must not be freed, does not fail + const std::string search = " Version "; + const std::size_t found = info.find(search); + + if (found != std::string::npos) { + m_product = info.substr(0, found); + m_version = info.substr(found + search.length()); + } else { + m_product = info; + m_version = "UNKNOWN"; + } + } + return m_product.c_str(); +} + +const char *FliImpl::get_simulator_version() +{ + get_simulator_product(); + return m_version.c_str(); +} + /** * @name Find the root handle * @brief Find the root handle using an optional name diff --git a/cocotb/share/lib/fli/FliImpl.h b/cocotb/share/lib/fli/FliImpl.h index a2c17198..434bbae5 100644 --- a/cocotb/share/lib/fli/FliImpl.h +++ b/cocotb/share/lib/fli/FliImpl.h @@ -472,6 +472,8 @@ class FliImpl : public GpiImplInterface { void sim_end() override; void get_sim_time(uint32_t *high, uint32_t *low) override; void get_sim_precision(int32_t *precision) override; + const char *get_simulator_product() override; + const char *get_simulator_version() override; /* Hierachy related */ GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent) override; diff --git a/cocotb/share/lib/gpi/GpiCommon.cpp b/cocotb/share/lib/gpi/GpiCommon.cpp index 52dec40d..d453188f 100644 --- a/cocotb/share/lib/gpi/GpiCommon.cpp +++ b/cocotb/share/lib/gpi/GpiCommon.cpp @@ -124,9 +124,9 @@ int gpi_register_impl(GpiImplInterface *func_tbl) return 0; } -void gpi_embed_init(gpi_sim_info_t *info) +void gpi_embed_init(int argc, char const* const* argv) { - if (embed_sim_init(info)) + if (embed_sim_init(argc, argv)) gpi_embed_end(); } @@ -258,6 +258,16 @@ void gpi_get_sim_precision(int32_t *precision) } +const char *gpi_get_simulator_product() +{ + return registered_impls[0]->get_simulator_product(); +} + +const char *gpi_get_simulator_version() +{ + return registered_impls[0]->get_simulator_version(); +} + gpi_sim_hdl gpi_get_root_handle(const char *name) { /* May need to look over all the implementations that are registered diff --git a/cocotb/share/lib/gpi/gpi_priv.h b/cocotb/share/lib/gpi/gpi_priv.h index 429fd468..1e33872c 100644 --- a/cocotb/share/lib/gpi/gpi_priv.h +++ b/cocotb/share/lib/gpi/gpi_priv.h @@ -289,6 +289,8 @@ class GpiImplInterface { virtual void sim_end() = 0; virtual void get_sim_time(uint32_t *high, uint32_t *low) = 0; virtual void get_sim_precision(int32_t *precision) = 0; + virtual const char* get_simulator_product() = 0; + virtual const char* get_simulator_version() = 0; /* Hierarchy related */ virtual GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent) = 0; @@ -309,12 +311,15 @@ class GpiImplInterface { private: std::string m_name; +protected: + std::string m_product; + std::string m_version; }; /* Called from implementation layers back up the stack */ int gpi_register_impl(GpiImplInterface *func_tbl); -void gpi_embed_init(gpi_sim_info_t *info); +void gpi_embed_init(int argc, char const* const* argv); void gpi_cleanup(); void gpi_embed_end(); void gpi_embed_event(gpi_event_t level, const char *msg); diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index ca61efad..32eb4abf 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -761,6 +761,20 @@ static PyObject *get_precision(PyObject *self, PyObject *args) return PyLong_FromLong(precision); } +static PyObject *get_simulator_product(PyObject *m, PyObject *args) +{ + COCOTB_UNUSED(m); + COCOTB_UNUSED(args); + return Py_BuildValue("s", gpi_get_simulator_product()); +} + +static PyObject *get_simulator_version(PyObject *m, PyObject *args) +{ + COCOTB_UNUSED(m); + COCOTB_UNUSED(args); + return Py_BuildValue("s", gpi_get_simulator_version()); +} + static PyObject *get_num_elems(gpi_hdl_Object *self, PyObject *args) { COCOTB_UNUSED(args); diff --git a/cocotb/share/lib/simulator/simulatormodule.h b/cocotb/share/lib/simulator/simulatormodule.h index 61f072ca..25d9042f 100644 --- a/cocotb/share/lib/simulator/simulatormodule.h +++ b/cocotb/share/lib/simulator/simulatormodule.h @@ -64,6 +64,8 @@ static PyObject *stop_simulator(PyObject *self, PyObject *args); static PyObject *get_sim_time(PyObject *self, PyObject *args); static PyObject *get_precision(PyObject *self, PyObject *args); +static PyObject *get_simulator_product(PyObject *self, PyObject *args); +static PyObject *get_simulator_version(PyObject *self, PyObject *args); static PyObject *log_level(PyObject *self, PyObject *args); @@ -80,6 +82,8 @@ static PyMethodDef SimulatorMethods[] = { {"get_sim_time", get_sim_time, METH_NOARGS, "Get the current simulation time as an int tuple"}, {"get_precision", get_precision, METH_NOARGS, "Get the precision of the simulator"}, + {"get_simulator_product", get_simulator_product, METH_NOARGS, "Simulator product information"}, + {"get_simulator_version", get_simulator_version, METH_NOARGS, "Simulator product version information"}, {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp index e5f9bff5..2268223c 100644 --- a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp +++ b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp @@ -833,16 +833,11 @@ VhpiStartupCbHdl::VhpiStartupCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), int VhpiStartupCbHdl::run_callback() { vhpiHandleT tool, argv_iter, argv_hdl; - gpi_sim_info_t sim_info; char **tool_argv = NULL; int tool_argc = 0; int i = 0; tool = vhpi_handle(vhpiTool, NULL); - - sim_info.product = const_cast(static_cast(vhpi_get_str(vhpiNameP, tool))); - sim_info.version = const_cast(static_cast(vhpi_get_str(vhpiToolVersionP, tool))); - if (tool) { tool_argc = static_cast(vhpi_get(vhpiArgcP, tool)); tool_argv = new char*[tool_argc]; @@ -856,13 +851,11 @@ int VhpiStartupCbHdl::run_callback() { } vhpi_release_handle(argv_iter); } - sim_info.argc = tool_argc; - sim_info.argv = tool_argv; vhpi_release_handle(tool); } - gpi_embed_init(&sim_info); + gpi_embed_init(tool_argc, tool_argv); delete [] tool_argv; return 0; diff --git a/cocotb/share/lib/vhpi/VhpiImpl.cpp b/cocotb/share/lib/vhpi/VhpiImpl.cpp index c94a5092..371d142a 100644 --- a/cocotb/share/lib/vhpi/VhpiImpl.cpp +++ b/cocotb/share/lib/vhpi/VhpiImpl.cpp @@ -110,6 +110,34 @@ void VhpiImpl::get_sim_precision(int32_t *precision) *precision = log10int(femtoseconds) - 15; } +const char *VhpiImpl::get_simulator_product() +{ + if (m_product.empty()) { + vhpiHandleT tool = vhpi_handle(vhpiTool, NULL); + if (tool) { + m_product = vhpi_get_str(vhpiNameP, tool); + vhpi_release_handle(tool); + } else { + m_product = "UNKNOWN"; + } + } + return m_product.c_str(); +} + +const char *VhpiImpl::get_simulator_version() +{ + if (m_version.empty()) { + vhpiHandleT tool = vhpi_handle(vhpiTool, NULL); + if (tool) { + m_version = vhpi_get_str(vhpiToolVersionP, tool); + vhpi_release_handle(tool); + } else { + m_version = "UNKNOWN"; + } + } + return m_version.c_str(); +} + // Determine whether a VHPI object type is a constant or not bool is_const(vhpiHandleT hdl) { diff --git a/cocotb/share/lib/vhpi/VhpiImpl.h b/cocotb/share/lib/vhpi/VhpiImpl.h index 9fa3010a..68d68e2c 100644 --- a/cocotb/share/lib/vhpi/VhpiImpl.h +++ b/cocotb/share/lib/vhpi/VhpiImpl.h @@ -247,6 +247,8 @@ class VhpiImpl : public GpiImplInterface { void sim_end() override; void get_sim_time(uint32_t *high, uint32_t *low) override; void get_sim_precision(int32_t *precision) override; + const char *get_simulator_product() override; + const char *get_simulator_version() override; /* Hierachy related */ GpiObjHdl *get_root_handle(const char *name) override; diff --git a/cocotb/share/lib/vpi/VpiCbHdl.cpp b/cocotb/share/lib/vpi/VpiCbHdl.cpp index 6c887665..38b7be37 100644 --- a/cocotb/share/lib/vpi/VpiCbHdl.cpp +++ b/cocotb/share/lib/vpi/VpiCbHdl.cpp @@ -486,16 +486,14 @@ VpiStartupCbHdl::VpiStartupCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), int VpiStartupCbHdl::run_callback() { s_vpi_vlog_info info; - gpi_sim_info_t sim_info; - vpi_get_vlog_info(&info); - - sim_info.argc = info.argc; - sim_info.argv = info.argv; - sim_info.product = info.product; - sim_info.version = info.version; + if (!vpi_get_vlog_info(&info)) { + LOG_WARN("Unable to get argv and argc from simulator"); + info.argc = 0; + info.argv = nullptr; + } - gpi_embed_init(&sim_info); + gpi_embed_init(info.argc, info.argv); return 0; } diff --git a/cocotb/share/lib/vpi/VpiImpl.cpp b/cocotb/share/lib/vpi/VpiImpl.cpp index 6bde91b5..8e42c4f6 100644 --- a/cocotb/share/lib/vpi/VpiImpl.cpp +++ b/cocotb/share/lib/vpi/VpiImpl.cpp @@ -72,6 +72,28 @@ void VpiImpl::get_sim_precision(int32_t *precision) *precision = vpi_get(vpiTimePrecision, NULL); } +const char *VpiImpl::get_simulator_product() +{ + if (m_product.empty() && m_version.empty()) { + s_vpi_vlog_info info; + if (!vpi_get_vlog_info(&info)) { + LOG_WARN("Could not obtain info about the simulator"); + m_product = "UNKNOWN"; + m_version = "UNKNOWN"; + } else { + m_product = info.product; + m_version = info.version; + } + } + return m_product.c_str(); +} + +const char *VpiImpl::get_simulator_version() +{ + get_simulator_product(); + return m_version.c_str(); +} + gpi_objtype_t to_gpi_objtype(int32_t vpitype) { switch (vpitype) { diff --git a/cocotb/share/lib/vpi/VpiImpl.h b/cocotb/share/lib/vpi/VpiImpl.h index ae20fdf5..9f6839e1 100644 --- a/cocotb/share/lib/vpi/VpiImpl.h +++ b/cocotb/share/lib/vpi/VpiImpl.h @@ -241,6 +241,8 @@ class VpiImpl : public GpiImplInterface { void sim_end(void) override; void get_sim_time(uint32_t *high, uint32_t *low) override; void get_sim_precision(int32_t *precision) override; + const char *get_simulator_product() override; + const char *get_simulator_version() override; /* Hierarchy related */ GpiObjHdl *get_root_handle(const char *name) override; From d60d1fb97ced5723a1d8cde2538946bbf7e4e30c Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 6 May 2020 05:52:52 -0700 Subject: [PATCH 2236/2656] Update cocotb/share/lib/simulator/simulatormodule.cpp Co-authored-by: Eric Wieser --- cocotb/share/lib/simulator/simulatormodule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index 32eb4abf..4e98be20 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -765,7 +765,7 @@ static PyObject *get_simulator_product(PyObject *m, PyObject *args) { COCOTB_UNUSED(m); COCOTB_UNUSED(args); - return Py_BuildValue("s", gpi_get_simulator_product()); + return PyUnicode_FromString(gpi_get_simulator_product()); } static PyObject *get_simulator_version(PyObject *m, PyObject *args) From f87361ca4018dfebf143b55eef677f15a23e69ff Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 6 May 2020 05:53:01 -0700 Subject: [PATCH 2237/2656] Update cocotb/share/lib/simulator/simulatormodule.cpp Co-authored-by: Eric Wieser --- cocotb/share/lib/simulator/simulatormodule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index 4e98be20..1d89010e 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -772,7 +772,7 @@ static PyObject *get_simulator_version(PyObject *m, PyObject *args) { COCOTB_UNUSED(m); COCOTB_UNUSED(args); - return Py_BuildValue("s", gpi_get_simulator_version()); + return PyUnicode_FromString(gpi_get_simulator_version()); } static PyObject *get_num_elems(gpi_hdl_Object *self, PyObject *args) From 322e5ec992f541ef92402aa75eeb3174b19f63c6 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 11 May 2020 18:49:06 +0200 Subject: [PATCH 2238/2656] Remove E128 from flake8 ignore list Fix occurrences of this error. Refs #1547. --- cocotb/drivers/amba.py | 4 ++-- cocotb/triggers.py | 2 +- examples/mean/tests/test_mean.py | 4 ++-- setup.cfg | 2 -- tests/test_cases/test_discovery/test_discovery.py | 8 ++++---- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/cocotb/drivers/amba.py b/cocotb/drivers/amba.py index 41ffab48..a1419fe4 100644 --- a/cocotb/drivers/amba.py +++ b/cocotb/drivers/amba.py @@ -155,7 +155,7 @@ def write(self, address, value, byte_enable=0xf, address_latency=0, if int(result): raise AXIProtocolError("Write to address 0x%08x failed with BRESP: %d" - % (address, int(result))) + % (address, int(result))) return result @@ -199,7 +199,7 @@ def read(self, address, sync=True): if int(result): raise AXIProtocolError("Read address 0x%08x failed with RRESP: %d" % - (address, int(result))) + (address, int(result))) return data diff --git a/cocotb/triggers.py b/cocotb/triggers.py index c7b465dc..14816667 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -504,7 +504,7 @@ def release(self): """Release the lock.""" if not self.locked: raise TriggerException("Attempt to release an unacquired Lock %s" % - (str(self))) + (str(self))) self.locked = False diff --git a/examples/mean/tests/test_mean.py b/examples/mean/tests/test_mean.py index e897ca7f..fb2774a9 100644 --- a/examples/mean/tests/test_mean.py +++ b/examples/mean/tests/test_mean.py @@ -44,7 +44,7 @@ def value_test(dut, num): data_width = int(dut.DATA_WIDTH.value) bus_width = int(dut.BUS_WIDTH.value) dut._log.info('Detected DATA_WIDTH = %d, BUS_WIDTH = %d' % - (data_width, bus_width)) + (data_width, bus_width)) cocotb.fork(clock_gen(dut.clk, period=CLK_PERIOD_NS, units='ns')) @@ -99,7 +99,7 @@ def mean_randomised_test(dut): data_width = int(dut.DATA_WIDTH.value) bus_width = int(dut.BUS_WIDTH.value) dut._log.info('Detected DATA_WIDTH = %d, BUS_WIDTH = %d' % - (data_width, bus_width)) + (data_width, bus_width)) cocotb.fork(clock_gen(dut.clk, period=CLK_PERIOD_NS, units='ns')) diff --git a/setup.cfg b/setup.cfg index 67ea3e4e..774c31ea 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,11 +6,9 @@ exclude = venv examples/endian_swapper/cosim -# TODO: fix some of these ignore = E126 # continuation line over-indented for hanging indent E127 # continuation line over-indented for visual indent - E128 # continuation line under-indented for visual indent E202 # whitespace before ')' E203 # whitespace before ',' / ':' E221 # multiple spaces before operator diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index c8468574..4aa501f4 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -116,7 +116,7 @@ def access_single_bit(dut): dut.stream_in_data <= 0 yield Timer(10) dut._log.info("%s = %d bits" % - (str(dut.stream_in_data), len(dut.stream_in_data))) + (str(dut.stream_in_data), len(dut.stream_in_data))) dut.stream_in_data[2] <= 1 yield Timer(10) if dut.stream_out_data_comb.value.integer != (1 << 2): @@ -134,7 +134,7 @@ def access_single_bit_assignment(dut): dut.stream_in_data = 0 yield Timer(10) dut._log.info("%s = %d bits" % - (str(dut.stream_in_data), len(dut.stream_in_data))) + (str(dut.stream_in_data), len(dut.stream_in_data))) dut.stream_in_data[2] = 1 yield Timer(10) if dut.stream_out_data_comb.value.integer != (1 << 2): @@ -148,7 +148,7 @@ def access_single_bit_erroneous(dut): """Access a non-existent single bit""" yield Timer(10) dut._log.info("%s = %d bits" % - (str(dut.stream_in_data), len(dut.stream_in_data))) + (str(dut.stream_in_data), len(dut.stream_in_data))) bit = len(dut.stream_in_data) + 4 dut.stream_in_data[bit] <= 1 yield Timer(10) @@ -390,7 +390,7 @@ def skip_a_test(dut): """This test shouldn't execute""" yield Timer(10) dut._log.info("%s = %d bits" % - (str(dut.stream_in_data), len(dut.stream_in_data))) + (str(dut.stream_in_data), len(dut.stream_in_data))) bit = len(dut.stream_in_data) + 4 dut.stream_in_data[bit] <= 1 yield Timer(10) From bca7c4e7b3834a09a666a955bc6d9b98521808a1 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Mon, 11 May 2020 12:15:44 +1000 Subject: [PATCH 2239/2656] Issue #1778: Move makefile of mean example to tests_mixedlang - Fix missing parameter in clock gen - Allow running mixed-lang example with modelsim --- examples/mean/tests/test_mean.py | 6 +++--- examples/mean/{tests => tests_mixedlang}/Makefile | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) rename examples/mean/{tests => tests_mixedlang}/Makefile (79%) diff --git a/examples/mean/tests/test_mean.py b/examples/mean/tests/test_mean.py index fb2774a9..c7022fef 100644 --- a/examples/mean/tests/test_mean.py +++ b/examples/mean/tests/test_mean.py @@ -29,12 +29,12 @@ def _monitor_recv(self): @cocotb.coroutine -def clock_gen(signal, period=10): +def clock_gen(signal, period=10, units='ns'): while True: signal <= 0 - yield Timer(period/2, units='ns') + yield Timer(period/2, units=units) signal <= 1 - yield Timer(period/2, units='ns') + yield Timer(period/2, units=units) @cocotb.coroutine diff --git a/examples/mean/tests/Makefile b/examples/mean/tests_mixedlang/Makefile similarity index 79% rename from examples/mean/tests/Makefile rename to examples/mean/tests_mixedlang/Makefile index 2326f8cf..3975c3d5 100644 --- a/examples/mean/tests/Makefile +++ b/examples/mean/tests_mixedlang/Makefile @@ -13,11 +13,13 @@ TOPLEVEL := mean_sv PWD=$(shell pwd) +export PYTHONPATH := $(PWD)/../tests:$(PYTHONPATH) + VHDL_SOURCES = $(PWD)/../hdl/mean_pkg.vhd VHDL_SOURCES += $(PWD)/../hdl/mean.vhd -ifeq ($(SIM),questa) -COMPILE_ARGS = -mixedsvvh +ifneq ($(filter $(SIM),questa modelsim),) + COMPILE_ARGS += -mixedsvvh endif VERILOG_SOURCES = $(PWD)/../hdl/mean_sv.sv From 753dd750feb5d751b6603de0dece13aad66a80ef Mon Sep 17 00:00:00 2001 From: elgorwi Date: Mon, 11 May 2020 11:50:31 +1000 Subject: [PATCH 2240/2656] Issue #1778: Add SystemVerilog version of mean example --- examples/mean/hdl/mean.sv | 72 ++++++++++++++++++++++++++++++++++++ examples/mean/tests/Makefile | 24 ++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 examples/mean/hdl/mean.sv create mode 100644 examples/mean/tests/Makefile diff --git a/examples/mean/hdl/mean.sv b/examples/mean/hdl/mean.sv new file mode 100644 index 00000000..3a6e53b6 --- /dev/null +++ b/examples/mean/hdl/mean.sv @@ -0,0 +1,72 @@ +// This file is public domain, it can be freely copied without restrictions. +// SPDX-License-Identifier: CC0-1.0 + +//----------------------------------------------------------------------------- +// Calculates mean of data input bus +//----------------------------------------------------------------------------- + +`timescale 1ns/1ps + + +module mean #( + parameter int BUS_WIDTH = 4, + parameter int DATA_WIDTH = 6 +) ( + input logic clk, + input logic rst, + input logic i_valid, + input logic [DATA_WIDTH-1:0] i_data [0:BUS_WIDTH-1], + output logic o_valid, + output logic [DATA_WIDTH-1:0] o_data + ); + + + localparam int SUM_WIDTH = DATA_WIDTH + $clog2(BUS_WIDTH); +// localparam int SUM_WIDTH = DATA_WIDTH + $clog2(BUS_WIDTH) - 1; // introduce bug + + logic [SUM_WIDTH-1:0] v_sum; + logic [SUM_WIDTH-1:0] s_sum; + logic s_valid; + + initial begin + if (BUS_WIDTH != 2**($clog2(BUS_WIDTH))) begin + $fatal(1, "parameter BUS_WIDTH should be a power of 2!"); + end + end + + + always @* begin + v_sum = '0; + + for (int i = 0; i < BUS_WIDTH; i++) begin + v_sum = v_sum + i_data[i]; + end + end + + always @(posedge clk) begin + s_valid <= i_valid; + + if (i_valid) begin + s_sum <= v_sum; + end + + if (rst) begin + s_sum <= '0; + s_valid <= 1'b0; + end + end + + assign o_valid = s_valid; + assign o_data = s_sum >> $clog2(BUS_WIDTH); + + + initial begin + int idx; + $dumpfile("dump.vcd"); + $dumpvars(0, mean); + for (idx = 0; idx < BUS_WIDTH; idx++) + $dumpvars(0, i_data[idx]); + #1; + end + +endmodule diff --git a/examples/mean/tests/Makefile b/examples/mean/tests/Makefile new file mode 100644 index 00000000..f4bf5d1c --- /dev/null +++ b/examples/mean/tests/Makefile @@ -0,0 +1,24 @@ +# This file is public domain, it can be freely copied without restrictions. +# SPDX-License-Identifier: CC0-1.0 + +TOPLEVEL_LANG ?= verilog + +PWD=$(shell pwd) + +ifeq ($(TOPLEVEL_LANG),verilog) + VERILOG_SOURCES = $(PWD)/../hdl/mean.sv +else ifeq ($(TOPLEVEL_LANG),vhdl) + VHDL_SOURCES = $(PWD)/../hdl/mean_pkg.vhd + VHDL_SOURCES += $(PWD)/../hdl/mean.vhd +else + $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") +endif + +TOPLEVEL := mean +MODULE := test_mean + +ifneq ($(filter $(SIM),ius xcelium),) + SIM_ARGS += -v93 +endif + +include $(shell cocotb-config --makefiles)/Makefile.sim From 8eb450bb1e8acbcc82527e556d6cc4037c2214c4 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Sun, 10 May 2020 10:16:45 +1000 Subject: [PATCH 2241/2656] Issue #1778: Fix mean example when TOPLEVEL_LANG=vhdl and align mean.vhd with mean.sv --- examples/mean/hdl/mean.vhd | 9 ++++++--- examples/mean/hdl/mean_pkg.vhd | 4 ++-- examples/mean/hdl/mean_sv.sv | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/examples/mean/hdl/mean.vhd b/examples/mean/hdl/mean.vhd index 1b79ca41..cdf45ef5 100644 --- a/examples/mean/hdl/mean.vhd +++ b/examples/mean/hdl/mean.vhd @@ -9,7 +9,7 @@ use work.mean_pkg.all; entity mean is generic ( - BUS_WIDTH : natural := 2); + BUS_WIDTH : natural := 4); port ( clk : in std_logic; rst : in std_logic; @@ -23,8 +23,11 @@ end mean; architecture RTL of mean is - signal s_sum : unsigned(DATA_WIDTH + clog2(BUS_WIDTH-1)-1 downto 0) := (others => '0'); -- introduce bug --- signal s_sum : unsigned(DATA_WIDTH + clog2(BUS_WIDTH)-1 downto 0) := (others => '0'); + constant DATA_WIDTH : natural := C_DATA_WIDTH; + constant SUM_WIDTH : natural := DATA_WIDTH + clog2(BUS_WIDTH); +-- constant SUM_WIDTH : natural := DATA_WIDTH + clog2(BUS_WIDTH) - 1; -- introduce bug + + signal s_sum : unsigned(SUM_WIDTH-1 downto 0) := (others => '0'); signal s_valid : std_logic := '0'; begin diff --git a/examples/mean/hdl/mean_pkg.vhd b/examples/mean/hdl/mean_pkg.vhd index d28faa32..db500bbd 100644 --- a/examples/mean/hdl/mean_pkg.vhd +++ b/examples/mean/hdl/mean_pkg.vhd @@ -9,9 +9,9 @@ use ieee.math_real.all; package mean_pkg is - constant DATA_WIDTH : natural := 6; + constant C_DATA_WIDTH : natural := 6; - subtype t_data is unsigned(DATA_WIDTH-1 downto 0); + subtype t_data is unsigned(C_DATA_WIDTH-1 downto 0); type t_data_array is array (natural range <>) of t_data; function clog2(n : positive) return natural; diff --git a/examples/mean/hdl/mean_sv.sv b/examples/mean/hdl/mean_sv.sv index 66724369..a6bced0d 100644 --- a/examples/mean/hdl/mean_sv.sv +++ b/examples/mean/hdl/mean_sv.sv @@ -17,7 +17,7 @@ module mean_sv #( // make constant from package visible -parameter DATA_WIDTH = data_width; +parameter DATA_WIDTH = c_data_width; // VHDL DUT mean #( From cc6aa4e0cc6a80c3ea4ae666d47e6eab40147f73 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Mon, 11 May 2020 12:04:04 +1000 Subject: [PATCH 2242/2656] Issue #1778: Add missing PD/CC0 SPDX header to files of mean example --- examples/mean/hdl/mean.vhd | 3 +++ examples/mean/hdl/mean_pkg.vhd | 3 +++ examples/mean/hdl/mean_sv.sv | 3 +++ examples/mean/tests/test_mean.py | 3 +++ examples/mean/tests_mixedlang/Makefile | 2 ++ 5 files changed, 14 insertions(+) diff --git a/examples/mean/hdl/mean.vhd b/examples/mean/hdl/mean.vhd index cdf45ef5..c2f7c43c 100644 --- a/examples/mean/hdl/mean.vhd +++ b/examples/mean/hdl/mean.vhd @@ -1,3 +1,6 @@ +-- This file is public domain, it can be freely copied without restrictions. +-- SPDX-License-Identifier: CC0-1.0 + ------------------------------------------------------------------------------- -- Calculates mean of data input bus ------------------------------------------------------------------------------- diff --git a/examples/mean/hdl/mean_pkg.vhd b/examples/mean/hdl/mean_pkg.vhd index db500bbd..1fc00fd9 100644 --- a/examples/mean/hdl/mean_pkg.vhd +++ b/examples/mean/hdl/mean_pkg.vhd @@ -1,3 +1,6 @@ +-- This file is public domain, it can be freely copied without restrictions. +-- SPDX-License-Identifier: CC0-1.0 + ------------------------------------------------------------------------------- -- Package with constants, types & functions for mean.vhd ------------------------------------------------------------------------------- diff --git a/examples/mean/hdl/mean_sv.sv b/examples/mean/hdl/mean_sv.sv index a6bced0d..61444af9 100644 --- a/examples/mean/hdl/mean_sv.sv +++ b/examples/mean/hdl/mean_sv.sv @@ -1,3 +1,6 @@ +// This file is public domain, it can be freely copied without restrictions. +// SPDX-License-Identifier: CC0-1.0 + //----------------------------------------------------------------------------- // sv wrapper for mean.vhd //----------------------------------------------------------------------------- diff --git a/examples/mean/tests/test_mean.py b/examples/mean/tests/test_mean.py index c7022fef..e2cc2287 100644 --- a/examples/mean/tests/test_mean.py +++ b/examples/mean/tests/test_mean.py @@ -1,3 +1,6 @@ +# This file is public domain, it can be freely copied without restrictions. +# SPDX-License-Identifier: CC0-1.0 + from __future__ import print_function from __future__ import division diff --git a/examples/mean/tests_mixedlang/Makefile b/examples/mean/tests_mixedlang/Makefile index 3975c3d5..7e7faddd 100644 --- a/examples/mean/tests_mixedlang/Makefile +++ b/examples/mean/tests_mixedlang/Makefile @@ -1,3 +1,5 @@ +# This file is public domain, it can be freely copied without restrictions. +# SPDX-License-Identifier: CC0-1.0 TOPLEVEL_LANG ?= verilog From cacc21f89a5628d03c9bf4200c00a627870e050e Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 3 Apr 2020 00:06:22 +0200 Subject: [PATCH 2243/2656] Mention proper way of setting make vars Document that users should use the syntax EXTRA_ARGS=... make instead of make EXTRA_ARGS=... because some Makefiles append to these variables internally, and the second syntax would break them. Closes #1552 --- documentation/source/building.rst | 25 +++++++++++++++++++++++- documentation/source/troubleshooting.rst | 21 ++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/documentation/source/building.rst b/documentation/source/building.rst index 33d92b60..5a2de0ce 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -1,3 +1,5 @@ +.. _building: + *************************************** Build options and Environment Variables *************************************** @@ -73,10 +75,20 @@ and Any arguments or flags to pass to the compile stage of the simulation. + .. note:: + For use on the command line, set this as an environment variable: + ``COMPILE_ARGS="..." make`` (for fish and csh: ``env COMPILE_ARGS="..." make``). + See :ref:`troubleshooting-make-vars` for details. + .. make:var:: SIM_ARGS Any arguments or flags to pass to the execution of the compiled simulation. + .. note:: + For use on the command line, set this as an environment variable: + ``SIM_ARGS="..." make`` (for fish and csh: ``env SIM_ARGS="..." make``). + See :ref:`troubleshooting-make-vars` for details. + .. make:var:: RUN_ARGS Any argument to be passed to the "first" invocation of a simulator that runs via a TCL script. @@ -85,7 +97,13 @@ and .. make:var:: EXTRA_ARGS - Passed to both the compile and execute phases of simulators with two rules, or passed to the single compile and run command for simulators which don't have a distinct compilation stage. + Passed to both the compile and execute phases of simulators with two rules, + or passed to the single compile and run command for simulators which don't have a distinct compilation stage. + + .. note:: + For use on the command line, set this as an environment variable: + ``EXTRA_ARGS="..." make`` (for fish and csh: ``env EXTRA_ARGS="..." make``). + See :ref:`troubleshooting-make-vars` for details. .. make:var:: PLUSARGS @@ -98,6 +116,11 @@ and to set the random seed value if :envvar:`RANDOM_SEED` is not set. If both ``+ntb_random_seed`` and ``+seed`` are set, ``+ntb_random_seed`` is used. + .. note:: + For use on the command line, set this as an environment variable: + ``PLUSARGS="..." make`` (for fish and csh: ``env PLUSARGS="..." make``). + See :ref:`troubleshooting-make-vars` for details. + .. make:var:: COCOTB_HDL_TIMEUNIT The default time unit that should be assumed for simulation when not specified by modules in the design. diff --git a/documentation/source/troubleshooting.rst b/documentation/source/troubleshooting.rst index a0d6a1c0..9c0dee83 100644 --- a/documentation/source/troubleshooting.rst +++ b/documentation/source/troubleshooting.rst @@ -80,3 +80,24 @@ A prebuilt test is included to easily launch an IPython shell in an existing des To embed a shell within an existing test, where it can inspect local variables, the :func:`embed` function can be used. .. autofunction:: embed + + +.. _troubleshooting-make-vars: + +Setting make variables on the command line +========================================== + +When trying to set one of the make variables listed in :ref:`building` from the command line, +it is strongly recommended to use an environment variable, i.e. +``EXTRA_ARGS="..." make`` (for the fish and csh shells: ``env EXTRA_ARGS="..." make``) +and *not* ``make EXTRA_ARGS=...``. + +This is because in the case of the disrecommended ``make EXTRA_ARGS=...``, +if one of the involved Makefiles contains lines to assign (``=``) or append (``+=``) to ``EXTRA_ARGS`` internally, +such lines will be ignored. +These lines are needed for the operation of cocotb however, +and having them ignored is likely to lead to strange errors. + +As a side note, +when you need to *clear* a Makefile variable from the command line, +use the syntax ``make EXTRA_ARGS=``. From 73e5d6de84a2d0cd4cf2b7faab84ca149e177d13 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 11 May 2020 18:41:47 +0200 Subject: [PATCH 2244/2656] Remove E126 and E127 from flake8 ignore list Fix occurrences of E127; E126 did not occur. Refs #1547. --- cocotb/binary.py | 12 ++++++------ cocotb/drivers/avalon.py | 10 +++++----- cocotb/log.py | 6 +++--- cocotb/regression.py | 8 ++++---- setup.cfg | 2 -- tests/pytest/test_binary_value.py | 2 +- tests/test_cases/test_avalon/test_avalon.py | 4 ++-- 7 files changed, 21 insertions(+), 23 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 74b89a6e..4aec8455 100755 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -127,15 +127,15 @@ def __init__(self, value=None, n_bits=None, bigEndian=True, self._n_bits = n_bits self._convert_to = { - BinaryRepresentation.UNSIGNED : self._convert_to_unsigned , - BinaryRepresentation.SIGNED_MAGNITUDE : self._convert_to_signed_mag , - BinaryRepresentation.TWOS_COMPLEMENT : self._convert_to_twos_comp , + BinaryRepresentation.UNSIGNED : self._convert_to_unsigned , + BinaryRepresentation.SIGNED_MAGNITUDE : self._convert_to_signed_mag , + BinaryRepresentation.TWOS_COMPLEMENT : self._convert_to_twos_comp , } self._convert_from = { - BinaryRepresentation.UNSIGNED : self._convert_from_unsigned , - BinaryRepresentation.SIGNED_MAGNITUDE : self._convert_from_signed_mag , - BinaryRepresentation.TWOS_COMPLEMENT : self._convert_from_twos_comp , + BinaryRepresentation.UNSIGNED : self._convert_from_unsigned , + BinaryRepresentation.SIGNED_MAGNITUDE : self._convert_from_signed_mag , + BinaryRepresentation.TWOS_COMPLEMENT : self._convert_from_twos_comp , } if value is not None: diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 572a10f0..02d3c308 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -237,11 +237,11 @@ class AvalonMemory(BusDriver): _optional_signals = ["write", "read", "writedata", "readdatavalid", "readdata", "waitrequest", "burstcount", "byteenable"] _avalon_properties = { - "burstCountUnits": "symbols", # symbols or words - "addressUnits": "symbols", # symbols or words - "readLatency": 1, # number of cycles - "WriteBurstWaitReq": True, # generate random waitrequest - "MaxWaitReqLen": 4, # maximum value of waitrequest + "burstCountUnits": "symbols", # symbols or words + "addressUnits": "symbols", # symbols or words + "readLatency": 1, # number of cycles + "WriteBurstWaitReq": True, # generate random waitrequest + "MaxWaitReqLen": 4, # maximum value of waitrequest } def __init__(self, entity, name, clock, readlatency_min=1, diff --git a/cocotb/log.py b/cocotb/log.py index bf251b7d..4e8c5f31 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -188,9 +188,9 @@ def _format(self, level, record, msg, coloured=False): prefix = sim_time_str.rjust(11) + ' ' + level + ' ' if not _suppress: prefix += self.ljust(record.name, _RECORD_CHARS) + \ - self.rjust(os.path.split(record.filename)[1], _FILENAME_CHARS) + \ - ':' + self.ljust(str(record.lineno), _LINENO_CHARS) + \ - ' in ' + self.ljust(str(record.funcName), _FUNCNAME_CHARS) + ' ' + self.rjust(os.path.split(record.filename)[1], _FILENAME_CHARS) + \ + ':' + self.ljust(str(record.lineno), _LINENO_CHARS) + \ + ' in ' + self.ljust(str(record.funcName), _FUNCNAME_CHARS) + ' ' # these lines are copied from the builtin logger if record.exc_info: diff --git a/cocotb/regression.py b/cocotb/regression.py index 9031207d..472433af 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -706,14 +706,14 @@ def generate_tests(self, prefix="", postfix=""): doc += "\t%s: %s\n" % (optname, repr(optvalue)) self.log.debug("Adding generated test \"%s\" to module \"%s\"" % - (name, mod.__name__)) + (name, mod.__name__)) kwargs = {} kwargs.update(self.kwargs_constant) kwargs.update(testoptions) if hasattr(mod, name): self.log.error("Overwriting %s in module %s. " - "This causes a previously defined testcase " - "not to be run. Consider setting/changing " - "name_postfix" % (name, mod)) + "This causes a previously defined testcase " + "not to be run. Consider setting/changing " + "name_postfix" % (name, mod)) setattr(mod, name, _create_test(self.test_function, name, doc, mod, *self.args, **kwargs)) diff --git a/setup.cfg b/setup.cfg index 774c31ea..f7da9e25 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,8 +7,6 @@ exclude = examples/endian_swapper/cosim ignore = - E126 # continuation line over-indented for hanging indent - E127 # continuation line over-indented for visual indent E202 # whitespace before ')' E203 # whitespace before ',' / ':' E221 # multiple spaces before operator diff --git a/tests/pytest/test_binary_value.py b/tests/pytest/test_binary_value.py index 1f5f0b61..041da268 100644 --- a/tests/pytest/test_binary_value.py +++ b/tests/pytest/test_binary_value.py @@ -152,7 +152,7 @@ def test_init_short_binstr_value(): assert bin3.integer == 1 bin4 = BinaryValue(value="1", n_bits=8, - bigEndian=True, binaryRepresentation=BinaryRepresentation.SIGNED_MAGNITUDE) + bigEndian=True, binaryRepresentation=BinaryRepresentation.SIGNED_MAGNITUDE) # 1 digit is too small for Signed Magnitude representation, so setting binstr will fail, falling back to buff bin4._str == "10000000" bin4.binstr == "10000000" diff --git a/tests/test_cases/test_avalon/test_avalon.py b/tests/test_cases/test_avalon/test_avalon.py index 6a886def..7e32e4d2 100755 --- a/tests/test_cases/test_avalon/test_avalon.py +++ b/tests/test_cases/test_avalon/test_avalon.py @@ -95,8 +95,8 @@ def test_burst_read(dut): yield RisingEdge(dut.clk) value = dut.user_buffer_data.value for i in range(databuswidthB): - read_mem[address + burst*databuswidthB + i] =\ - (value >> i*8)& 0xFF + read_mem[address + burst*databuswidthB + i] = \ + (value >> i*8)& 0xFF burst += 1 dut.user_read_buffer = 0 From d57064771394269cc9d3e62eab6581b1c85f13bd Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 12 May 2020 00:21:10 +0200 Subject: [PATCH 2245/2656] Fix additional E302 problems --- examples/axi_lite_slave/tests/test_axi_lite_slave.py | 1 + tests/test_cases/issue_120/issue_120.py | 1 + tests/test_cases/issue_348/issue_348.py | 2 ++ tests/test_cases/test_array/test_array.py | 1 + tests/test_cases/test_discovery/test_discovery.py | 2 ++ 5 files changed, 7 insertions(+) diff --git a/examples/axi_lite_slave/tests/test_axi_lite_slave.py b/examples/axi_lite_slave/tests/test_axi_lite_slave.py index 288e4d93..005b6dc8 100644 --- a/examples/axi_lite_slave/tests/test_axi_lite_slave.py +++ b/examples/axi_lite_slave/tests/test_axi_lite_slave.py @@ -16,6 +16,7 @@ def setup_dut(dut): cocotb.fork(Clock(dut.clk, CLK_PERIOD_NS, units='ns').start()) + # Write to address 0 and verify that value got through @cocotb.test() def write_address_0(dut): diff --git a/tests/test_cases/issue_120/issue_120.py b/tests/test_cases/issue_120/issue_120.py index 62cd1a20..ba289090 100644 --- a/tests/test_cases/issue_120/issue_120.py +++ b/tests/test_cases/issue_120/issue_120.py @@ -21,6 +21,7 @@ def monitor(dut): if not dut.stream_in_valid.value.integer: raise TestFailure("stream_in_valid should be high on the 5th cycle") + # Cadence simulators: "Unable set up RisingEdge(...) Trigger" with VHDL (see #1076) @cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else False) def issue_120_scheduling(dut): diff --git a/tests/test_cases/issue_348/issue_348.py b/tests/test_cases/issue_348/issue_348.py index 473a34db..eb213727 100644 --- a/tests/test_cases/issue_348/issue_348.py +++ b/tests/test_cases/issue_348/issue_348.py @@ -61,12 +61,14 @@ def issue_348_rising(dut): """ Start two monitors on RisingEdge """ yield DualMonitor(RisingEdge, dut.clk).start() + # Cadence simulators: "Unable set up FallingEdge(ModifiableObject(sample_module.clk)) Trigger" with VHDL (see #1076) @cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else False) def issue_348_falling(dut): """ Start two monitors on FallingEdge """ yield DualMonitor(FallingEdge, dut.clk).start() + # Cadence simulators: "Unable set up Edge(ModifiableObject(sample_module.clk)) Trigger" with VHDL (see #1076) @cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else False) def issue_348_either(dut): diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py index 121616e6..757ab6a6 100644 --- a/tests/test_cases/test_array/test_array.py +++ b/tests/test_cases/test_array/test_array.py @@ -52,6 +52,7 @@ def _check_value(tlog, hdl, expected): else: tlog.info(" Found {0!r} ({1}) with value={2}".format(hdl, hdl._type, hdl.value)) + # NOTE: simulator-specific handling is done in this test itself, not via expect_error in the decorator @cocotb.test() def test_read_write(dut): diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 4aa501f4..246dabc0 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -153,6 +153,7 @@ def access_single_bit_erroneous(dut): dut.stream_in_data[bit] <= 1 yield Timer(10) + @cocotb.test(expect_error=cocotb.SIM_NAME.lower().startswith(("icarus", "chronologic simulation vcs")), expect_fail=cocotb.SIM_NAME.lower().startswith(("riviera")) and cocotb.LANGUAGE in ["verilog"]) def access_integer(dut): @@ -395,6 +396,7 @@ def skip_a_test(dut): dut.stream_in_data[bit] <= 1 yield Timer(10) + @cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"], expect_error=cocotb.SIM_NAME.lower().startswith(("icarus"))) def access_gate(dut): From 8ed8da17bbeb4d1bf57c8965f6bd2be6eadd3d37 Mon Sep 17 00:00:00 2001 From: tpambor <1379478+tpambor@users.noreply.github.com> Date: Tue, 12 May 2020 16:06:48 +0200 Subject: [PATCH 2246/2656] Remove unused includes in embed.h (#1797) --- cocotb/share/include/embed.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/cocotb/share/include/embed.h b/cocotb/share/include/embed.h index 902c793f..677e2a6f 100644 --- a/cocotb/share/include/embed.h +++ b/cocotb/share/include/embed.h @@ -31,9 +31,6 @@ #define COCOTB_EMBED_H_ #include -#include -#include -#include #ifdef __cplusplus extern "C" { From c7da01ce2d09aec7f76da9f1b802a3bd679942fc Mon Sep 17 00:00:00 2001 From: tpambor <1379478+tpambor@users.noreply.github.com> Date: Tue, 12 May 2020 17:40:08 +0200 Subject: [PATCH 2247/2656] Don't include unistd.h on windows (#1795) `unistd.h` is a POSIX header. While it happens to be shipped with mingw, most windows C/C++ compilers (including MSVC) do not come with this header. On windows only `getpid()` is used from `unistd.h`, which can be replaced with `GetCurrentProcessId()`. --- cocotb/share/lib/embed/gpi_embed.cpp | 4 +++- cocotb/share/lib/gpi/GpiCommon.cpp | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cocotb/share/lib/embed/gpi_embed.cpp b/cocotb/share/lib/embed/gpi_embed.cpp index 31c0c1c3..55da3f33 100644 --- a/cocotb/share/lib/embed/gpi_embed.cpp +++ b/cocotb/share/lib/embed/gpi_embed.cpp @@ -30,7 +30,6 @@ // Embed Python into the simulator using GPI #include -#include #include #include "embed.h" #include "locale.h" @@ -38,9 +37,12 @@ #if defined(_WIN32) #include #define sleep(n) Sleep(1000 * n) +#define getpid() GetCurrentProcessId() #ifndef PATH_MAX #define PATH_MAX MAX_PATH #endif +#else +#include #endif static PyThreadState *gtstate = NULL; diff --git a/cocotb/share/lib/gpi/GpiCommon.cpp b/cocotb/share/lib/gpi/GpiCommon.cpp index d453188f..789f25ac 100644 --- a/cocotb/share/lib/gpi/GpiCommon.cpp +++ b/cocotb/share/lib/gpi/GpiCommon.cpp @@ -30,7 +30,6 @@ #include "gpi_priv.h" #include #include -#include #include #include #include From e6e98a85f49de3d5f50410392f02c06ea1c4f11c Mon Sep 17 00:00:00 2001 From: mathFPGAseek Date: Tue, 12 May 2020 15:03:12 -0700 Subject: [PATCH 2248/2656] Make sure half_period is an integer number of ns With this, no extra SIM_ARGS to select a finer simulator precision are necessary. --- examples/endian_swapper/tests/test_endian_swapper.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/endian_swapper/tests/test_endian_swapper.py b/examples/endian_swapper/tests/test_endian_swapper.py index b9f61d9a..edb53c83 100644 --- a/examples/endian_swapper/tests/test_endian_swapper.py +++ b/examples/endian_swapper/tests/test_endian_swapper.py @@ -101,7 +101,7 @@ def model(self, transaction): self.pkts_sent += 1 @cocotb.coroutine - def reset(self, duration=10): + def reset(self, duration=20): self.dut._log.debug("Resetting DUT") self.dut.reset_n <= 0 self.stream_in.bus.valid <= 0 @@ -115,7 +115,7 @@ def reset(self, duration=10): def run_test(dut, data_in=None, config_coroutine=None, idle_inserter=None, backpressure_inserter=None): - cocotb.fork(Clock(dut.clk, 5, units='ns').start()) + cocotb.fork(Clock(dut.clk, 10, units='ns').start()) tb = EndianSwapperTB(dut) yield tb.reset() @@ -182,7 +182,7 @@ def wavedrom_test(dut): """ Generate a JSON wavedrom diagram of a trace and save it to wavedrom.json """ - cocotb.fork(Clock(dut.clk, 5, units='ns').start()) + cocotb.fork(Clock(dut.clk, 10, units='ns').start()) yield RisingEdge(dut.clk) tb = EndianSwapperTB(dut) yield tb.reset() From e2081585018d5f06e50d9708eee70be3e94e8bc0 Mon Sep 17 00:00:00 2001 From: ajesh Date: Tue, 12 May 2020 17:09:24 -0400 Subject: [PATCH 2249/2656] Fix avalon monitor hexdump debug log --- cocotb/monitors/avalon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index bd8b0b2e..dc4a7d6b 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -217,7 +217,7 @@ def valid(): if self.bus.endofpacket.value: self.log.info("Received a packet of %d bytes", len(pkt)) - self.log.debug(hexdump(str((pkt)))) + self.log.debug(hexdump(pkt)) self.channel = channel if self.report_channel: self._recv({"data": pkt, "channel": channel}) From 0ff8341b64fcd8e88f4c45ff794775017733b515 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 11 Jan 2020 22:52:16 -0600 Subject: [PATCH 2250/2656] Move cocotb.LANGUAGE to python --- cocotb/__init__.py | 2 ++ cocotb/share/lib/embed/gpi_embed.cpp | 24 ------------------------ 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index a1604e43..f7f0966a 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -117,6 +117,8 @@ def _reopen_stream_with_buffering(stream_name): # FIXME is this really required? _rlock = threading.RLock() +LANGUAGE = os.getenv("TOPLEVEL_LANG") + def mem_debug(port): import cocotb.memdebug diff --git a/cocotb/share/lib/embed/gpi_embed.cpp b/cocotb/share/lib/embed/gpi_embed.cpp index 55da3f33..2479b9dd 100644 --- a/cocotb/share/lib/embed/gpi_embed.cpp +++ b/cocotb/share/lib/embed/gpi_embed.cpp @@ -334,30 +334,6 @@ extern "C" int embed_sim_init(int argc, char const * const * argv) goto cleanup; } - // Set language in use as an attribute to cocotb module, or None if not provided - { - const char *lang = getenv("TOPLEVEL_LANG"); - PyObject *PyLang; - if (lang) { - PyLang = PyUnicode_FromString(lang); // New reference - } else { - Py_INCREF(Py_None); - PyLang = Py_None; - } - if (PyLang == NULL) { - PyErr_Print(); - LOG_ERROR("Unable to create Python object for cocotb.LANGUAGE"); - goto cleanup; - } - if (-1 == PyObject_SetAttrString(cocotb_module, "LANGUAGE", PyLang)) { - PyErr_Print(); - LOG_ERROR("Unable to set LANGUAGE"); - Py_DECREF(PyLang); - goto cleanup; - } - Py_DECREF(PyLang); - } - pEventFn = PyObject_GetAttrString(cocotb_module, "_sim_event"); // New reference if (pEventFn == NULL) { PyErr_Print(); From ee2018f18033fcc01b9da9cd64b1b76de3c35687 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 11 Jan 2020 23:16:19 -0600 Subject: [PATCH 2251/2656] Move root_handle to Python --- cocotb/__init__.py | 10 ++++++++- cocotb/share/lib/embed/gpi_embed.cpp | 32 +--------------------------- 2 files changed, 10 insertions(+), 32 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index f7f0966a..52d24401 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -125,7 +125,7 @@ def mem_debug(port): cocotb.memdebug.start(port) -def _initialise_testbench(root_name): +def _initialise_testbench(): """Initialize testbench. This function is called after the simulator has elaborated all @@ -139,6 +139,14 @@ def _initialise_testbench(root_name): """ _rlock.acquire() + root_name = os.getenv("TOPLEVEL") + if root_name is not None: + if root_name == "": + root_name = None + elif '.' in root_name: + # Skip any library component of the toplevel + root_name = root_name.split(".", 1)[1] + from cocotb import simulator global SIM_NAME, SIM_VERSION diff --git a/cocotb/share/lib/embed/gpi_embed.cpp b/cocotb/share/lib/embed/gpi_embed.cpp index 2479b9dd..0c48e7db 100644 --- a/cocotb/share/lib/embed/gpi_embed.cpp +++ b/cocotb/share/lib/embed/gpi_embed.cpp @@ -232,22 +232,6 @@ extern "C" int embed_sim_init(int argc, char const * const * argv) if (pEventFn) return ret; - // Find the simulation root - const char *dut = getenv("TOPLEVEL"); - - if (dut != NULL) { - if (!strcmp("", dut)) { - /* Empty string passed in, treat as NULL */ - dut = NULL; - } else { - // Skip any library component of the toplevel - const char *dot = strchr(dut, '.'); - if (dot != NULL) { - dut += (dot - dut + 1); - } - } - } - PyObject *cocotb_module, *cocotb_init, *cocotb_retval; PyObject *cocotb_log_module = NULL; PyObject *simlog_func; @@ -359,21 +343,7 @@ extern "C" int embed_sim_init(int argc, char const * const * argv) goto cleanup; } - PyObject *dut_arg; - if (dut == NULL) { - Py_INCREF(Py_None); - dut_arg = Py_None; - } else { - dut_arg = PyUnicode_FromString(dut); // New reference - } - if (dut_arg == NULL) { - PyErr_Print(); - LOG_ERROR("Unable to create Python object for dut argument of _initialise_testbench"); - goto cleanup; - } - - cocotb_retval = PyObject_CallFunctionObjArgs(cocotb_init, dut_arg, NULL); - Py_DECREF(dut_arg); + cocotb_retval = PyObject_CallFunctionObjArgs(cocotb_init, NULL); Py_DECREF(cocotb_init); if (cocotb_retval != NULL) { From bae4d47114a7f21a1ffa9b1f61163f99324f5871 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 19 Feb 2020 22:01:38 -0600 Subject: [PATCH 2252/2656] Move argv to _initialize_testbench --- cocotb/__init__.py | 6 ++- cocotb/share/lib/embed/gpi_embed.cpp | 59 +++++++++++----------------- 2 files changed, 28 insertions(+), 37 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 52d24401..436f1a7f 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -125,7 +125,7 @@ def mem_debug(port): cocotb.memdebug.start(port) -def _initialise_testbench(): +def _initialise_testbench(argv_): """Initialize testbench. This function is called after the simulator has elaborated all @@ -139,6 +139,10 @@ def _initialise_testbench(): """ _rlock.acquire() + global argc, argv + argv = argv_ + argc = len(argv) + root_name = os.getenv("TOPLEVEL") if root_name is not None: if root_name == "": diff --git a/cocotb/share/lib/embed/gpi_embed.cpp b/cocotb/share/lib/embed/gpi_embed.cpp index 0c48e7db..640d6453 100644 --- a/cocotb/share/lib/embed/gpi_embed.cpp +++ b/cocotb/share/lib/embed/gpi_embed.cpp @@ -283,41 +283,6 @@ extern "C" int embed_sim_init(int argc, char const * const * argv) set_log_filter(simlog_func); // Note: This function steals a reference to simlog_func. - // Build argv for cocotb module - argv_list = PyList_New(argc); // New reference - if (argv_list == NULL) { - PyErr_Print(); - LOG_ERROR("Unable to create argv list"); - goto cleanup; - } - for (i = 0; i < argc; i++) { - // Decode, embedding non-decodable bytes using PEP-383. This can only - // fail with MemoryError or similar. - PyObject *argv_item = PyUnicode_DecodeLocale(argv[i], "surrogateescape"); // New reference - if (argv_item == NULL) { - PyErr_Print(); - LOG_ERROR("Unable to convert command line argument %d to Unicode string.", i); - Py_DECREF(argv_list); - goto cleanup; - } - PyList_SET_ITEM(argv_list, i, argv_item); // Note: This function steals the reference to argv_item - } - - // Add argv list to cocotb module - if (-1 == PyModule_AddObject(cocotb_module, "argv", argv_list)) { // Note: This function steals the reference to argv_list if successful - PyErr_Print(); - LOG_ERROR("Unable to set argv"); - Py_DECREF(argv_list); - goto cleanup; - } - - // Add argc to cocotb module - if (-1 == PyModule_AddIntConstant(cocotb_module, "argc", argc)) { - PyErr_Print(); - LOG_ERROR("Unable to set argc"); - goto cleanup; - } - pEventFn = PyObject_GetAttrString(cocotb_module, "_sim_event"); // New reference if (pEventFn == NULL) { PyErr_Print(); @@ -343,7 +308,29 @@ extern "C" int embed_sim_init(int argc, char const * const * argv) goto cleanup; } - cocotb_retval = PyObject_CallFunctionObjArgs(cocotb_init, NULL); + // Build argv for cocotb module + argv_list = PyList_New(argc); // New reference + if (argv_list == NULL) { + PyErr_Print(); + LOG_ERROR("Unable to create argv list"); + goto cleanup; + } + for (i = 0; i < argc; i++) { + // Decode, embedding non-decodable bytes using PEP-383. This can only + // fail with MemoryError or similar. + PyObject *argv_item = PyUnicode_DecodeLocale(argv[i], "surrogateescape"); // New reference + if (argv_item == NULL) { + PyErr_Print(); + LOG_ERROR("Unable to convert command line argument %d to Unicode string.", i); + Py_DECREF(argv_list); + goto cleanup; + } + PyList_SET_ITEM(argv_list, i, argv_item); // Note: This function steals the reference to argv_item + } + + + cocotb_retval = PyObject_CallFunctionObjArgs(cocotb_init, argv_list, NULL); + Py_DECREF(argv_list); Py_DECREF(cocotb_init); if (cocotb_retval != NULL) { From 7bcd0f8ea86520377de7d99445dd2093f36caf0f Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 19 Feb 2020 22:37:08 -0600 Subject: [PATCH 2253/2656] Remove pointless callable checks on known functions --- cocotb/share/lib/embed/gpi_embed.cpp | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/cocotb/share/lib/embed/gpi_embed.cpp b/cocotb/share/lib/embed/gpi_embed.cpp index 640d6453..ff86f99a 100644 --- a/cocotb/share/lib/embed/gpi_embed.cpp +++ b/cocotb/share/lib/embed/gpi_embed.cpp @@ -260,11 +260,6 @@ extern "C" int embed_sim_init(int argc, char const * const * argv) LOG_ERROR("Failed to get the _log_from_c function"); goto cleanup; } - if (!PyCallable_Check(simlog_func)) { - LOG_ERROR("_log_from_c is not callable"); - Py_DECREF(simlog_func); - goto cleanup; - } set_log_handler(simlog_func); // Note: This function steals a reference to simlog_func. @@ -275,11 +270,6 @@ extern "C" int embed_sim_init(int argc, char const * const * argv) LOG_ERROR("Failed to get the _filter_from_c method"); goto cleanup; } - if (!PyCallable_Check(simlog_func)) { - LOG_ERROR("_filter_from_c is not callable"); - Py_DECREF(simlog_func); - goto cleanup; - } set_log_filter(simlog_func); // Note: This function steals a reference to simlog_func. @@ -289,12 +279,6 @@ extern "C" int embed_sim_init(int argc, char const * const * argv) LOG_ERROR("Failed to get the _sim_event method"); goto cleanup; } - if (!PyCallable_Check(pEventFn)) { - LOG_ERROR("cocotb._sim_event is not callable"); - Py_DECREF(pEventFn); - pEventFn = NULL; - goto cleanup; - } cocotb_init = PyObject_GetAttrString(cocotb_module, "_initialise_testbench"); // New reference if (cocotb_init == NULL) { @@ -302,11 +286,6 @@ extern "C" int embed_sim_init(int argc, char const * const * argv) LOG_ERROR("Failed to get the _initialise_testbench method"); goto cleanup; } - if (!PyCallable_Check(cocotb_init)) { - LOG_ERROR("cocotb._initialise_testbench is not callable"); - Py_DECREF(cocotb_init); - goto cleanup; - } // Build argv for cocotb module argv_list = PyList_New(argc); // New reference From b14d2b75f3a92ec8410783a53b240c0418a6adb8 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 29 Feb 2020 09:33:20 -0600 Subject: [PATCH 2254/2656] Remove unused FENTER/FEXIT macros --- cocotb/share/include/gpi_logging.h | 8 -------- cocotb/share/lib/embed/gpi_embed.cpp | 12 ++---------- cocotb/share/lib/fli/FliImpl.cpp | 4 ---- cocotb/share/lib/simulator/simulatormodule.cpp | 12 ------------ cocotb/share/lib/vhpi/VhpiImpl.cpp | 4 ---- cocotb/share/lib/vpi/VpiCbHdl.cpp | 5 ----- 6 files changed, 2 insertions(+), 43 deletions(-) diff --git a/cocotb/share/include/gpi_logging.h b/cocotb/share/include/gpi_logging.h index c8570fcd..118911cc 100644 --- a/cocotb/share/include/gpi_logging.h +++ b/cocotb/share/include/gpi_logging.h @@ -58,14 +58,6 @@ enum gpi_log_levels { exit(1); \ } while (0) -// #ifdef DEBUG -// #define FENTER LOG_DEBUG(__func__) -// #define FEXIT LOG_DEBUG(__func__) -// #else -#define FENTER -#define FEXIT -// #endif - void set_log_handler(void *handler); void clear_log_handler(void); void set_log_filter(void *filter); diff --git a/cocotb/share/lib/embed/gpi_embed.cpp b/cocotb/share/lib/embed/gpi_embed.cpp index ff86f99a..4f16b28a 100644 --- a/cocotb/share/lib/embed/gpi_embed.cpp +++ b/cocotb/share/lib/embed/gpi_embed.cpp @@ -112,7 +112,6 @@ static void set_program_name_in_venv(void) extern "C" void embed_init_python(void) { - FENTER; #ifndef PYTHON_SO_LIB #error "Python version needs passing in with -DPYTHON_SO_LIB=libpython.so" @@ -148,19 +147,17 @@ extern "C" void embed_init_python(void) as well as correct parses that would be sliced by the narrowing cast */ if (errno == ERANGE || sleep_time >= UINT_MAX) { LOG_ERROR("COCOTB_ATTACH only needs to be set to ~30 seconds"); - goto out; + return; } if ((errno != 0 && sleep_time == 0) || (sleep_time <= 0)) { LOG_ERROR("COCOTB_ATTACH must be set to an integer base 10 or omitted"); - goto out; + return; } LOG_ERROR("Waiting for %lu seconds - attach to PID %d with your debugger\n", sleep_time, getpid()); sleep((unsigned int)sleep_time); } -out: - FEXIT; } /** @@ -223,7 +220,6 @@ static int get_module_ref(const char *modname, PyObject **mod) extern "C" int embed_sim_init(int argc, char const * const * argv) { - FENTER int i; int ret = 0; @@ -321,7 +317,6 @@ extern "C" int embed_sim_init(int argc, char const * const * argv) goto cleanup; } - FEXIT goto ok; cleanup: @@ -338,7 +333,6 @@ extern "C" int embed_sim_init(int argc, char const * const * argv) extern "C" void embed_sim_event(gpi_event_t level, const char *msg) { - FENTER /* Indicate to the upper layer a sim event occurred */ if (pEventFn) { @@ -359,6 +353,4 @@ extern "C" void embed_sim_event(gpi_event_t level, const char *msg) PyGILState_Release(gstate); to_simulator(); } - - FEXIT } diff --git a/cocotb/share/lib/fli/FliImpl.cpp b/cocotb/share/lib/fli/FliImpl.cpp index 09ab5a79..b3cb7d76 100644 --- a/cocotb/share/lib/fli/FliImpl.cpp +++ b/cocotb/share/lib/fli/FliImpl.cpp @@ -1039,18 +1039,14 @@ void handle_fli_callback(void *data) static void register_initial_callback() { - FENTER sim_init_cb = new FliStartupCbHdl(fli_table); sim_init_cb->arm_callback(); - FEXIT } static void register_final_callback() { - FENTER sim_finish_cb = new FliShutdownCbHdl(fli_table); sim_finish_cb->arm_callback(); - FEXIT } static void register_embed() diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index 1d89010e..d4986188 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -294,7 +294,6 @@ static callback_data *callback_data_new(PyObject *func, PyObject *args, PyObject static PyObject *register_readonly_callback(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); - FENTER Py_ssize_t numargs = PyTuple_Size(args); @@ -325,7 +324,6 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) gpi_cb_hdl hdl = gpi_register_readonly_callback((gpi_function_t)handle_gpi_callback, cb_data); PyObject *rv = gpi_hdl_New(hdl); - FEXIT return rv; } @@ -334,7 +332,6 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); - FENTER Py_ssize_t numargs = PyTuple_Size(args); @@ -366,7 +363,6 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) (gpi_function_t)handle_gpi_callback, cb_data); PyObject *rv = gpi_hdl_New(hdl); - FEXIT return rv; } @@ -375,7 +371,6 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); - FENTER Py_ssize_t numargs = PyTuple_Size(args); @@ -407,7 +402,6 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) (gpi_function_t)handle_gpi_callback, cb_data); PyObject *rv = gpi_hdl_New(hdl); - FEXIT return rv; } @@ -420,7 +414,6 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) static PyObject *register_timed_callback(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); - FENTER Py_ssize_t numargs = PyTuple_Size(args); @@ -467,7 +460,6 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) // Check success PyObject *rv = gpi_hdl_New(hdl); - FEXIT return rv; } @@ -480,7 +472,6 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) static PyObject *register_value_change_callback(PyObject *self, PyObject *args) //, PyObject *keywds) { COCOTB_UNUSED(self); - FENTER Py_ssize_t numargs = PyTuple_Size(args); @@ -523,7 +514,6 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) // Check success PyObject *rv = gpi_hdl_New(hdl); - FEXIT return rv; } @@ -811,11 +801,9 @@ static PyObject *stop_simulator(PyObject *self, PyObject *args) static PyObject *deregister(gpi_hdl_Object *self, PyObject *args) { COCOTB_UNUSED(args); - FENTER gpi_deregister_callback(self->hdl); - FEXIT Py_RETURN_NONE; } diff --git a/cocotb/share/lib/vhpi/VhpiImpl.cpp b/cocotb/share/lib/vhpi/VhpiImpl.cpp index 371d142a..577f9e04 100644 --- a/cocotb/share/lib/vhpi/VhpiImpl.cpp +++ b/cocotb/share/lib/vhpi/VhpiImpl.cpp @@ -957,18 +957,14 @@ void handle_vhpi_callback(const vhpiCbDataT *cb_data) static void register_initial_callback() { - FENTER sim_init_cb = new VhpiStartupCbHdl(vhpi_table); sim_init_cb->arm_callback(); - FEXIT } static void register_final_callback() { - FENTER sim_finish_cb = new VhpiShutdownCbHdl(vhpi_table); sim_finish_cb->arm_callback(); - FEXIT } static void register_embed() diff --git a/cocotb/share/lib/vpi/VpiCbHdl.cpp b/cocotb/share/lib/vpi/VpiCbHdl.cpp index 38b7be37..dd2665c8 100644 --- a/cocotb/share/lib/vpi/VpiCbHdl.cpp +++ b/cocotb/share/lib/vpi/VpiCbHdl.cpp @@ -283,7 +283,6 @@ int VpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { const char* VpiSignalObjHdl::get_signal_value_binstr() { - FENTER s_vpi_value value_s = {vpiBinStrVal, {NULL}}; vpi_get_value(GpiObjHdl::get_handle(), &value_s); @@ -304,7 +303,6 @@ const char* VpiSignalObjHdl::get_signal_value_str() double VpiSignalObjHdl::get_signal_value_real() { - FENTER s_vpi_value value_s = {vpiRealVal, {NULL}}; vpi_get_value(GpiObjHdl::get_handle(), &value_s); @@ -315,7 +313,6 @@ double VpiSignalObjHdl::get_signal_value_real() long VpiSignalObjHdl::get_signal_value_long() { - FENTER s_vpi_value value_s = {vpiIntVal, {NULL}}; vpi_get_value(GpiObjHdl::get_handle(), &value_s); @@ -373,7 +370,6 @@ int VpiSignalObjHdl::set_signal_value_str(std::string &value, gpi_set_action_t a int VpiSignalObjHdl::set_signal_value(s_vpi_value value_s, gpi_set_action_t action) { - FENTER PLI_INT32 vpi_put_flag = -1; s_vpi_time vpi_time_s; @@ -411,7 +407,6 @@ int VpiSignalObjHdl::set_signal_value(s_vpi_value value_s, gpi_set_action_t acti check_vpi_error(); - FEXIT return 0; } From e99eb8aac745dbd61a048403df6950866e9d84bd Mon Sep 17 00:00:00 2001 From: Marlon James <47790688+garmin-mjames@users.noreply.github.com> Date: Wed, 13 May 2020 02:53:02 -0700 Subject: [PATCH 2255/2656] Use last scheduled write to set handle value. (#1780) Before #1661, scheduled writes were stored in a dictionary which was keyed by the signal handle. This meant that additional writes scheduled to the same handle overwrote the previous one. This partially reverts #1661 by returning to a dictionary, which only stores one scheduled write per handle. Add `test_last_scheduled_write_wins` to ensure this behavior. --- cocotb/handle.py | 20 +++++----- cocotb/scheduler.py | 11 +++--- .../test_cases/test_cocotb/test_scheduler.py | 39 ++++++++++++++++++- 3 files changed, 53 insertions(+), 17 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index e96e1b12..c20b5f38 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -391,13 +391,11 @@ def value(self): @value.setter def value(self, value): - def _call_on_readwrite(f, *args): - cocotb.scheduler._schedule_write(self, f, *args) - self._set_value(value, _call_on_readwrite) + self._set_value(value, cocotb.scheduler._schedule_write) def setimmediatevalue(self, value): """ Assign a value to this simulation object immediately. """ - def _call_now(f, *args): + def _call_now(handle, f, *args): f(*args) self._set_value(value, _call_now) @@ -407,7 +405,7 @@ def _set_value(self, value, call_sim): This is used to implement both the setter for :attr:`value`, and the :meth:`setimmediatevalue` method. - ``call_sim(f, *args)`` should be used to schedule simulator writes, + ``call_sim(handle, f, *args)`` should be used to schedule simulator writes, rather than performing them directly as ``f(*args)``. """ raise TypeError("Not permissible to set values on object %s of type %s" % (self._name, type(self))) @@ -657,7 +655,7 @@ def _set_value(self, value, call_sim): value, set_action = self._check_for_set_action(value) if isinstance(value, int) and value < 0x7fffffff and len(self) <= 32: - call_sim(self._handle.set_signal_val_long, set_action, value) + call_sim(self, self._handle.set_signal_val_long, set_action, value) return if isinstance(value, ctypes.Structure): value = BinaryValue(value=cocotb.utils.pack(value), n_bits=len(self)) @@ -681,7 +679,7 @@ def _set_value(self, value, call_sim): "Unsupported type for value assignment: {} ({!r})" .format(type(value), value)) - call_sim(self._handle.set_signal_val_binstr, set_action, value.binstr) + call_sim(self, self._handle.set_signal_val_binstr, set_action, value.binstr) def _check_for_set_action(self, value): if not isinstance(value, _SetAction): @@ -726,7 +724,7 @@ def _set_value(self, value, call_sim): "Unsupported type for real value assignment: {} ({!r})" .format(type(value), value)) - call_sim(self._handle.set_signal_val_real, set_action, value) + call_sim(self, self._handle.set_signal_val_real, set_action, value) @ModifiableObject.value.getter def value(self) -> float: @@ -761,7 +759,7 @@ def _set_value(self, value, call_sim): "Unsupported type for enum value assignment: {} ({!r})" .format(type(value), value)) - call_sim(self._handle.set_signal_val_long, set_action, value) + call_sim(self, self._handle.set_signal_val_long, set_action, value) @ModifiableObject.value.getter def value(self) -> int: @@ -793,7 +791,7 @@ def _set_value(self, value, call_sim): "Unsupported type for integer value assignment: {} ({!r})" .format(type(value), value)) - call_sim(self._handle.set_signal_val_long, set_action, value) + call_sim(self, self._handle.set_signal_val_long, set_action, value) @ModifiableObject.value.getter def value(self) -> int: @@ -836,7 +834,7 @@ def _set_value(self, value, call_sim): "Unsupported type for string value assignment: {} ({!r})" .format(type(value), value)) - call_sim(self._handle.set_signal_val_str, set_action, value) + call_sim(self, self._handle.set_signal_val_str, set_action, value) @ModifiableObject.value.getter def value(self) -> bytes: diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index dc38e4ae..82c24a3a 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -233,8 +233,9 @@ def __init__(self): # Our main state self._mode = Scheduler._MODE_NORMAL - # A list of pending (write_func, args) - self._write_calls = [] + # A dictionary of pending (write_func, args), keyed by handle. Only the last scheduled write + # in a timestep is performed, all the rest are discarded in python. + self._write_calls = _py_compat.insertion_ordered_dict() self._pending_coros = [] self._pending_triggers = [] @@ -260,7 +261,7 @@ async def _do_writes(self): await self._read_write while self._write_calls: - func, args = self._write_calls.pop() + handle, (func, args) = self._write_calls.popitem() func(*args) self._writes_pending.clear() @@ -286,7 +287,7 @@ def _check_termination(self): self._trigger2coros = _py_compat.insertion_ordered_dict() self._coro2trigger = _py_compat.insertion_ordered_dict() self._terminate = False - self._write_calls = [] + self._write_calls = _py_compat.insertion_ordered_dict() self._writes_pending.clear() self._mode = Scheduler._MODE_TERM @@ -517,7 +518,7 @@ def _schedule_write(self, handle, write_func, *args): if self._write_coro_inst is None: self._write_coro_inst = self.add(self._do_writes()) - self._write_calls.append((write_func, args)) + self._write_calls[handle] = (write_func, args) self._writes_pending.set() def _resume_coro_upon(self, coro, trigger): diff --git a/tests/test_cases/test_cocotb/test_scheduler.py b/tests/test_cases/test_cocotb/test_scheduler.py index e696ad69..49562419 100644 --- a/tests/test_cases/test_cocotb/test_scheduler.py +++ b/tests/test_cases/test_cocotb/test_scheduler.py @@ -10,7 +10,7 @@ """ import cocotb -from cocotb.triggers import Join, Timer, RisingEdge, Trigger, NullTrigger, Combine, Event +from cocotb.triggers import Join, Timer, RisingEdge, Trigger, NullTrigger, Combine, Event, ReadOnly from cocotb.result import TestFailure from cocotb.clock import Clock from common import clock_gen @@ -263,3 +263,40 @@ async def waiter(event): await NullTrigger() assert waiter_scheduled is True + + +@cocotb.test() +async def test_last_scheduled_write_wins(dut): + """ + Test that the last scheduled write for a signal handle is the value that is written. + """ + e = Event() + dut.stream_in_data.setimmediatevalue(0) + + @cocotb.coroutine # TODO: Remove once Combine accepts bare coroutines + async def first(): + await Timer(1) + dut._log.info("scheduling stream_in_data <= 1") + dut.stream_in_data <= 1 + e.set() + + @cocotb.coroutine # TODO: Remove once Combine accepts bare coroutines + async def second(): + await Timer(1) + await e.wait() + dut._log.info("scheduling stream_in_data <= 2") + dut.stream_in_data <= 2 + + await Combine(first(), second()) + + await ReadOnly() + + assert dut.stream_in_data.value.integer == 2 + + await Timer(1) + dut.array_7_downto_4 <= [1, 2, 3, 4] + dut.array_7_downto_4[7] <= 10 + + await ReadOnly() + + assert dut.array_7_downto_4.value == [10, 2, 3, 4] From b08bf0ce74144886c17e6f824438890d08503a71 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 13 May 2020 12:04:06 +0200 Subject: [PATCH 2256/2656] Add howto section "Rotating Log Files" (#1732) --- cocotb/log.py | 10 ++++--- documentation/source/index.rst | 1 + .../source/newsfragments/1400.doc.rst | 1 + documentation/source/rotating_logger.rst | 27 +++++++++++++++++++ 4 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 documentation/source/newsfragments/1400.doc.rst create mode 100644 documentation/source/rotating_logger.rst diff --git a/cocotb/log.py b/cocotb/log.py index 4e8c5f31..927f2b31 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -67,8 +67,10 @@ def default_config(): If desired, this logging configuration can be overwritten by calling ``logging.basicConfig(..., force=True)`` (in Python 3.8 onwards), or by - manually resetting the root logger instance, for which examples can be - found online. + manually resetting the root logger instance. + An example of this can be found in the section on :ref:`rotating-logger`. + + .. versionadded:: 1.4 """ # construct an appropriate handler hdlr = logging.StreamHandler(sys.stdout) @@ -134,11 +136,13 @@ class SimTimeContextFilter(logging.Filter): This uses the approach described in the :ref:`Python logging cookbook `. This adds the :attr:`~logging.LogRecord.created_sim_time` attribute. + + .. versionadded:: 1.4 """ # needed to make our docs render well def __init__(self): - """ Takes no arguments """ + """""" super().__init__() def filter(self, record): diff --git a/documentation/source/index.rst b/documentation/source/index.rst index 36ef9702..36ee6dc3 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -120,6 +120,7 @@ A test can spawn multiple coroutines, allowing for independent flows of executio coroutines triggers testbench_tools + rotating_logger .. todo:: - Add WaveDrom, IPython sections diff --git a/documentation/source/newsfragments/1400.doc.rst b/documentation/source/newsfragments/1400.doc.rst new file mode 100644 index 00000000..015c979d --- /dev/null +++ b/documentation/source/newsfragments/1400.doc.rst @@ -0,0 +1 @@ +A new section :ref:`rotating-logger` has been added. diff --git a/documentation/source/rotating_logger.rst b/documentation/source/rotating_logger.rst new file mode 100644 index 00000000..2d69b049 --- /dev/null +++ b/documentation/source/rotating_logger.rst @@ -0,0 +1,27 @@ +.. _rotating-logger: + +****************** +Rotating Log Files +****************** + +The following is an example of how to support rotation of log files. +It will keep the newest 3 files which are at most 5 MiB in size. + +See also :ref:`logging-reference-section`, +and the Python documentation for :class:`logging.handlers.RotatingFileHandler`. + +.. code-block:: python3 + + from logging.handlers import RotatingFileHandler + + root_logger = logging.getLogger() + + # undo the setup cocotb did + for handler in root_logger.handlers: + root_logger.remove_handler(handler) + handler.close() + + # do whatever configuration you want instead + file_handler = RotatingFileHandler(logfile, maxBytes=(5 * 1024 * 1024), backupCount=2) + file_handler.setFormatter(cocotb.log.SimLogFormatter()) + root_logger.addHandler(file_handler) From 8d9a7641df3565c359dbbcd316aaf7266dc4cd56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kruszewski?= Date: Fri, 24 Apr 2020 19:54:17 +0200 Subject: [PATCH 2257/2656] Allow coroutine functions defined with async to be passed to TestFactory. Also adds detection for async generators to produce a helpful error message. --- cocotb/regression.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 472433af..7ab6ef4b 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -641,10 +641,14 @@ class TestFactory: """ def __init__(self, test_function, *args, **kwargs): - if not isinstance(test_function, cocotb.coroutine): + if sys.version_info > (3, 6) and inspect.isasyncgenfunction(test_function): + raise TypeError("Expected a coroutine function, but got the async generator '{}'. " + "Did you forget to convert a `yield` to an `await`?" + .format(test_function.__qualname__)) + if not (isinstance(test_function, cocotb.coroutine) or inspect.iscoroutinefunction(test_function)): raise TypeError("TestFactory requires a cocotb coroutine") self.test_function = test_function - self.name = self.test_function._func.__qualname__ + self.name = self.test_function.__qualname__ self.args = args self.kwargs_constant = kwargs From 4cc4f7688abfa93d4808874042481b61d7648640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kruszewski?= Date: Sun, 3 May 2020 13:55:43 +0200 Subject: [PATCH 2258/2656] Make dff example use async def instead of @cocotb.coroutine --- examples/dff/tests/dff_cocotb.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/examples/dff/tests/dff_cocotb.py b/examples/dff/tests/dff_cocotb.py index ddaea016..8bcf31ba 100644 --- a/examples/dff/tests/dff_cocotb.py +++ b/examples/dff/tests/dff_cocotb.py @@ -29,7 +29,6 @@ import cocotb from cocotb.clock import Clock -from cocotb.decorators import coroutine from cocotb.triggers import RisingEdge from cocotb.monitors import Monitor from cocotb.drivers import BitDriver @@ -55,13 +54,12 @@ def __init__(self, name, signal, clk, callback=None, event=None): self.clk = clk Monitor.__init__(self, callback, event) - @coroutine - def _monitor_recv(self): + async def _monitor_recv(self): clkedge = RisingEdge(self.clk) while True: # Capture signal at rising edge of clock - yield clkedge + await clkedge vec = self.signal.value self._recv(vec) @@ -131,8 +129,7 @@ def stop(self): self.stopped = True -@cocotb.coroutine -def run_test(dut): +async def run_test(dut): """Setup testbench and run a test.""" cocotb.fork(Clock(dut.c, 10, 'us').start(start_high=False)) @@ -144,12 +141,12 @@ def run_test(dut): # Apply random input data by input_gen via BitDriver for 100 clock cycles. tb.start() for _ in range(100): - yield clkedge + await clkedge # Stop generation of input data. One more clock cycle is needed to capture # the resulting output of the DUT. tb.stop() - yield clkedge + await clkedge # Print result of scoreboard. raise tb.scoreboard.result From e0d6486e31a580b730f36e35c3e64b3e79ed6b79 Mon Sep 17 00:00:00 2001 From: tpambor <1379478+tpambor@users.noreply.github.com> Date: Wed, 13 May 2020 14:20:12 +0200 Subject: [PATCH 2259/2656] Fix segfault due to uninitialized PyTypeObject (#1806) --- cocotb/share/lib/simulator/simulatormodule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index d4986188..d57d0b19 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -113,7 +113,7 @@ namespace { // Initialize the python type slots template PyTypeObject fill_common_slots() { - PyTypeObject type; + PyTypeObject type = {}; type.ob_base = {PyObject_HEAD_INIT(NULL) 0}; type.tp_basicsize = sizeof(gpi_hdl_Object); type.tp_repr = (reprfunc)gpi_hdl_repr; From 2f0a3b6d1f8efeab915224f97802ad986a2adeb5 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 13 May 2020 07:29:39 -0700 Subject: [PATCH 2260/2656] Avoid calling gpi_load_extra_libs reentrantly (#1781) --- cocotb/share/lib/embed/gpi_embed.cpp | 5 ++--- cocotb/share/lib/fli/FliImpl.cpp | 2 +- cocotb/share/lib/gpi/GpiCommon.cpp | 6 ------ cocotb/share/lib/vhpi/VhpiImpl.cpp | 4 ++-- cocotb/share/lib/vpi/VpiImpl.cpp | 2 +- 5 files changed, 6 insertions(+), 13 deletions(-) diff --git a/cocotb/share/lib/embed/gpi_embed.cpp b/cocotb/share/lib/embed/gpi_embed.cpp index 4f16b28a..37d9d330 100644 --- a/cocotb/share/lib/embed/gpi_embed.cpp +++ b/cocotb/share/lib/embed/gpi_embed.cpp @@ -33,6 +33,7 @@ #include #include "embed.h" #include "locale.h" +#include #if defined(_WIN32) #include @@ -119,9 +120,7 @@ extern "C" void embed_init_python(void) #define PY_SO_LIB xstr(PYTHON_SO_LIB) #endif - // Don't initialize Python if already running - if (gtstate) - return; + assert(!gtstate); // this function should not be called twice void * lib_handle = utils_dyn_open(PY_SO_LIB); if (!lib_handle) { diff --git a/cocotb/share/lib/fli/FliImpl.cpp b/cocotb/share/lib/fli/FliImpl.cpp index b3cb7d76..e1bffe04 100644 --- a/cocotb/share/lib/fli/FliImpl.cpp +++ b/cocotb/share/lib/fli/FliImpl.cpp @@ -1053,7 +1053,6 @@ static void register_embed() { fli_table = new FliImpl("FLI"); gpi_register_impl(fli_table); - gpi_load_extra_libs(); } @@ -1061,6 +1060,7 @@ void cocotb_init() { LOG_INFO("cocotb_init called\n"); register_embed(); + gpi_load_extra_libs(); register_initial_callback(); register_final_callback(); } diff --git a/cocotb/share/lib/gpi/GpiCommon.cpp b/cocotb/share/lib/gpi/GpiCommon.cpp index 789f25ac..a6ad57e5 100644 --- a/cocotb/share/lib/gpi/GpiCommon.cpp +++ b/cocotb/share/lib/gpi/GpiCommon.cpp @@ -199,11 +199,6 @@ static void gpi_load_libs(std::vector to_load) void gpi_load_extra_libs() { - static bool loading = false; - - if (loading) - return; - /* Lets look at what other libs we were asked to load too */ char *lib_env = getenv("GPI_EXTRA"); @@ -228,7 +223,6 @@ void gpi_load_extra_libs() to_load.push_back(lib_list); } - loading = true; gpi_load_libs(to_load); } diff --git a/cocotb/share/lib/vhpi/VhpiImpl.cpp b/cocotb/share/lib/vhpi/VhpiImpl.cpp index 577f9e04..9fecb8e6 100644 --- a/cocotb/share/lib/vhpi/VhpiImpl.cpp +++ b/cocotb/share/lib/vhpi/VhpiImpl.cpp @@ -971,15 +971,15 @@ static void register_embed() { vhpi_table = new VhpiImpl("VHPI"); gpi_register_impl(vhpi_table); - gpi_load_extra_libs(); } // pre-defined VHPI registration table void (*vhpi_startup_routines[])() = { register_embed, + gpi_load_extra_libs, register_initial_callback, register_final_callback, - 0 + nullptr }; // For non-VHPI compliant applications that cannot find vhpi_startup_routines diff --git a/cocotb/share/lib/vpi/VpiImpl.cpp b/cocotb/share/lib/vpi/VpiImpl.cpp index 8e42c4f6..2d22a9a4 100644 --- a/cocotb/share/lib/vpi/VpiImpl.cpp +++ b/cocotb/share/lib/vpi/VpiImpl.cpp @@ -601,7 +601,6 @@ static void register_embed() { vpi_table = new VpiImpl("VPI"); gpi_register_impl(vpi_table); - gpi_load_extra_libs(); } @@ -717,6 +716,7 @@ static void register_system_functions() void (*vlog_startup_routines[])() = { register_embed, + gpi_load_extra_libs, register_system_functions, register_initial_callback, register_final_callback, From cf22bfdc4a8caa0c71273890a30394bcf71a3f75 Mon Sep 17 00:00:00 2001 From: Ben Reynwar Date: Wed, 13 May 2020 07:33:26 -0700 Subject: [PATCH 2261/2656] Use InternalError rather than assert (#1346) Using assert means that the user thinks their test just failed, when actually it was an internal cocotb issue. --- cocotb/decorators.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 00029b99..71b1994c 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -270,7 +270,10 @@ def abort(self, exc): `exc` is the exception that the test should report as its reason for aborting. """ - assert self._outcome is None + if self._outcome is not None: + # imported here to avoid circular imports + from cocotb.scheduler import InternalError + raise InternalError("Outcome already has a value, but is being set again.") outcome = outcomes.Error(exc) if _debug: self.log.debug("outcome forced to {}".format(outcome)) From beff946fe43a9a73c4c352b5ca0986308446a41a Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 13 May 2020 18:51:23 +0200 Subject: [PATCH 2262/2656] Add test for accessing _underscore_name (#1634) Reinstate small part of code removal that happened in #1510. --- cocotb/handle.py | 2 ++ tests/designs/sample_module/sample_module.sv | 8 ++++++++ tests/test_cases/test_cocotb/test_handle.py | 20 ++++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/cocotb/handle.py b/cocotb/handle.py index c20b5f38..a5dc6f4d 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -275,6 +275,8 @@ def __getattr__(self, name): """Query the simulator for a object with the specified name and cache the result to build a tree of objects. """ + if name.startswith("_"): + return SimHandleBase.__getattr__(self, name) handle = self.__get_sub_handle_by_name(name) if handle is not None: diff --git a/tests/designs/sample_module/sample_module.sv b/tests/designs/sample_module/sample_module.sv index 670f1c80..bf35f938 100644 --- a/tests/designs/sample_module/sample_module.sv +++ b/tests/designs/sample_module/sample_module.sv @@ -135,4 +135,12 @@ assign logic_a = stream_in_valid; always@* logic_b = stream_in_valid; always@(posedge clk) logic_c <= stream_in_valid; +reg _underscore_name; +`ifdef __ICARUS__ + // By default, a variable must be used in some way in order + // to be visible to VPI in Icarus Verilog. + // See https://github.com/steveicarus/iverilog/issues/322 + assign _underscore_name = 0; +`endif + endmodule diff --git a/tests/test_cases/test_cocotb/test_handle.py b/tests/test_cases/test_cocotb/test_handle.py index d6c87464..0abdf490 100644 --- a/tests/test_cases/test_cocotb/test_handle.py +++ b/tests/test_cases/test_cocotb/test_handle.py @@ -123,3 +123,23 @@ def test_real_assign_int(dut): log.info("Read back value %d" % got) if got != float(val): raise TestFailure("Values didn't match!") + + +# identifiers starting with `_` are illegal in VHDL +@cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"]) +async def test_access_underscore_name(dut): + """Test accessing HDL name starting with an underscore""" + # direct access does not work because we consider such names cocotb-internal + with assert_raises(AttributeError): + dut._underscore_name + + # indirect access works + dut._id("_underscore_name", extended=False) <= 0 + await Timer(1, 'ns') + assert dut._id("_underscore_name", extended=False).value == 0 + dut._id("_underscore_name", extended=False) <= 1 + await Timer(1, 'ns') + assert dut._id("_underscore_name", extended=False).value == 1 + dut._id("_underscore_name", extended=False) <= 0 + await Timer(1, 'ns') + assert dut._id("_underscore_name", extended=False).value == 0 From 35fcda7795c33e6c50580538f948e88c6068cd31 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 13 May 2020 19:17:44 +0200 Subject: [PATCH 2263/2656] Split installation instructions out of quickstart (#1679) Move Quickstart to the very beginning. Move Index to the sidebar. Use sphinx-tabs extension for the different OSes. --- README.md | 2 +- documentation/requirements.txt | 3 + documentation/source/conf.py | 1 + documentation/source/genindex.rst | 7 + documentation/source/index.rst | 24 ++-- documentation/source/install.rst | 123 ++++++++++++++++ documentation/source/install_devel.rst | 49 +++++++ .../source/newsfragments/1445.change.rst | 2 +- documentation/source/py-modindex.rst | 8 ++ documentation/source/quickstart.rst | 134 ++---------------- 10 files changed, 221 insertions(+), 132 deletions(-) create mode 100644 documentation/source/genindex.rst create mode 100644 documentation/source/install.rst create mode 100644 documentation/source/install_devel.rst create mode 100644 documentation/source/py-modindex.rst diff --git a/README.md b/README.md index 91274276..2449160c 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ After installing these dependencies, the latest stable version of cocotb can be pip install cocotb ``` -For more details, including how to install a development version of cocotb, see [the documentation](https://docs.cocotb.org/en/latest/quickstart.html#pre-requisites). +For more details, including how to install a development version of cocotb, see [the documentation](https://docs.cocotb.org/en/latest/install.html). ## Usage diff --git a/documentation/requirements.txt b/documentation/requirements.txt index fec731a7..492c36cd 100644 --- a/documentation/requirements.txt +++ b/documentation/requirements.txt @@ -11,3 +11,6 @@ sphinx-issues sphinx-argparse towncrier IPython +sphinx-tabs +IPython +sphinx-tabs diff --git a/documentation/source/conf.py b/documentation/source/conf.py index 17e79fd0..086ec38e 100644 --- a/documentation/source/conf.py +++ b/documentation/source/conf.py @@ -43,6 +43,7 @@ 'sphinx_issues', 'sphinxarg.ext', 'sphinxcontrib.spelling', + 'sphinx_tabs.tabs', ] intersphinx_mapping = {'python': ('https://docs.python.org/3', None)} diff --git a/documentation/source/genindex.rst b/documentation/source/genindex.rst new file mode 100644 index 00000000..ef607279 --- /dev/null +++ b/documentation/source/genindex.rst @@ -0,0 +1,7 @@ +.. + This file is a placeholder and will be replaced by Sphinx + See https://stackoverflow.com/a/42310803 + +***** +Index +***** diff --git a/documentation/source/index.rst b/documentation/source/index.rst index 36ee6dc3..3a2fcd1f 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -69,8 +69,12 @@ The :keyword:`await` keyword is used to indicate when to pass control of executi A test can spawn multiple coroutines, allowing for independent flows of execution. -.. todo:: - - Move "Installation" section to here +.. toctree:: + :maxdepth: 1 + :hidden: + + quickstart + install .. Tutorials - lessons that take the reader by the hand through a series of steps to complete a project @@ -91,7 +95,6 @@ A test can spawn multiple coroutines, allowing for independent flows of executio :name: tutorials :hidden: - quickstart endian_swapper ping_tun_tap hal_cosimulation @@ -144,6 +147,7 @@ A test can spawn multiple coroutines, allowing for independent flows of executio :name: key_topics :hidden: + install_devel troubleshooting .. todo:: @@ -194,11 +198,11 @@ A test can spawn multiple coroutines, allowing for independent flows of executio - In Contributing, add explanation on how to provide a PR, how to test existing PRs, etc. - merge `further_resources` into Contributing +.. toctree:: + :maxdepth: 1 + :caption: Index + :name: index + :hidden: - -####### -Indices -####### - -* :ref:`Index of Classes, Methods, Variables etc.` -* :ref:`Index of Python Modules ` + Classes, Methods, Variables etc. + Python Modules diff --git a/documentation/source/install.rst b/documentation/source/install.rst new file mode 100644 index 00000000..98e13698 --- /dev/null +++ b/documentation/source/install.rst @@ -0,0 +1,123 @@ +.. _install: + +************ +Installation +************ + +Using cocotb requires installation of prerequisites and installation of cocotb itself. +In this document, we are assuming that you already have a +:ref:`supported simulator` available in ``PATH``. + + +.. _install-prerequisites: + +Installation of Prerequisites +============================= + +Cocotb has the following requirements: + +* Python 3.5+ +* Python-dev packages +* GCC 4.8.1+ or Clang 3.3+ and associated development packages +* GNU Make +* A Verilog or VHDL simulator, depending on your RTL source code + +.. versionchanged:: 1.4 Dropped Python 2 support + +.. note:: In order to use a 32-bit simulator you need to use a 32-bit version of Python. + +The installation instructions vary depending on your operating system: + +.. tabs:: + + .. group-tab:: Windows + + `Conda `_ is an open-source package and environment management system that we recommend for Windows. + + Download and install `Miniconda `_ from https://conda.io/. + From an Anaconda Prompt, use the following line to install a compiler (GCC or Clang) and GNU Make: + + .. code-block:: + + conda install -c msys2 m2-base m2-make m2w64-toolchain libpython + + .. group-tab:: Linux - Debian + + In a terminal, run + + .. code-block:: bash + + sudo apt-get install make gcc g++ python3 python3-dev python3-pip + + .. group-tab:: Linux - Red Hat + + In a terminal, run + + .. code-block:: bash + + sudo yum install make gcc gcc-c++ libstdc++-devel python3 python3-devel python3-pip + + .. group-tab:: macOS + + We recommmend using the `Homebrew `_ package manager. + After installing it, run the following line in a terminal: + + .. code-block:: bash + + brew install python icarus-verilog gtkwave + + +.. _install-cocotb: +.. _installation-via-pip: + +Installation of cocotb +====================== + +.. tabs:: + + .. group-tab:: Windows + + The **latest release** of cocotb can be installed by running + + .. code-block:: bash + + pip install --global-option build_ext --global-option --compiler=mingw32 cocotb + + .. group-tab:: Linux - Debian + + The **latest release** of cocotb can be installed by running + + .. code-block:: bash + + pip install cocotb + + .. group-tab:: Linux - Red Hat + + The **latest release** of cocotb can be installed by running + + .. code-block:: bash + + pip install cocotb + + .. group-tab:: macOS + + The **latest release** of cocotb can be installed by running + + .. code-block:: bash + + pip install cocotb + +.. warning:: + + ``pip`` may belong to a different Python installation to what you expect. + Use ``pip -V`` to check. + If this prints "(python 2.7)", use ``pip3`` or ``python3 -m pip`` in place of ``pip`` in the command shown. + +If you want to install the **development version** of cocotb, :ref:`instructions are here`. + +After installation, you should be able to execute ``cocotb-config``. +If it is not found, you need to append its location to the ``PATH`` environment variable. +This may happen when you use the ``--user`` option to ``pip``, in which case the location is documented :ref:`here `. + + +For more installation options, please see `our Wiki `_. diff --git a/documentation/source/install_devel.rst b/documentation/source/install_devel.rst new file mode 100644 index 00000000..46f4a591 --- /dev/null +++ b/documentation/source/install_devel.rst @@ -0,0 +1,49 @@ +.. _install-devel: + +********************************** +Installing the Development Version +********************************** + +We are assuming here that you have :ref:`installed the prerequisites`. + +The instructions for installing the development version of cocotb vary depending on your operating system: + +.. tabs:: + + .. group-tab:: Windows + + The development version of cocotb can be installed globally by running + + .. code-block:: bash + + pip install --global-option build_ext --global-option --compiler=mingw32 https://github.com/cocotb/cocotb/archive/master.zip + + See also the `pip User Guide `_. + + .. group-tab:: Linux and macOS + + The development version of cocotb can be installed globally by running + + .. code-block:: bash + + pip install https://github.com/cocotb/cocotb/archive/master.zip + + This requires administrator permissions. + + In order to install only for your current user, run + + .. code-block:: bash + + pip install https://github.com/cocotb/cocotb/archive/master.zip --user + + See also the `pip User Guide `_. + +.. warning:: + + ``pip`` may belong to a different Python installation to what you expect. + Use ``pip -V`` to check. + If this prints "(python 2.7)", use ``pip3`` or ``python3 -m pip`` in place of ``pip`` in the command shown. + +After installation, you should be able to execute ``cocotb-config``. +If it is not found, you need to append its location to the ``PATH`` environment variable. +This may happen when you use the ``--user`` option to ``pip``, in which case the location is documented :ref:`here `. diff --git a/documentation/source/newsfragments/1445.change.rst b/documentation/source/newsfragments/1445.change.rst index cfa48003..8968ea8d 100644 --- a/documentation/source/newsfragments/1445.change.rst +++ b/documentation/source/newsfragments/1445.change.rst @@ -1 +1 @@ -Cocotb must now be :ref:`installed ` before it can be used. +Cocotb must now be :ref:`installed ` before it can be used. diff --git a/documentation/source/py-modindex.rst b/documentation/source/py-modindex.rst new file mode 100644 index 00000000..98d01c98 --- /dev/null +++ b/documentation/source/py-modindex.rst @@ -0,0 +1,8 @@ +.. + This file is a placeholder and will be replaced by Sphinx + This is a hack to put the module index in the toctree + See https://stackoverflow.com/a/42310803 + +***** +Index +***** diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index 09758dc3..f03d53ac 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -1,138 +1,32 @@ +.. _quickstart: + **************** Quickstart Guide **************** -Installing cocotb -================= - -Installation of Pre-requisites ------------------------------- - -Cocotb has the following requirements: - -* Python 3.5+ -* Python-dev packages -* GCC 4.8.1+ or Clang 3.3+ and associated development packages -* GNU Make -* A Verilog or VHDL simulator, depending on your RTL source code - -.. versionchanged:: 1.4 Dropped Python 2 support - -.. note:: In order to use a 32-bit simulator you need to use a 32-bit version of Python. - -Installation with conda (all OS) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -`Conda `_ is an open-source package and environment management system that runs on Windows, macOS and Linux. - -Download and install `Miniconda `_ from https://conda.io/ for your OS and architecture. - -You will also need to install a compiler (GCC or Clang) and GNU Make. - -On Windows you can easily do this with conda: - -.. code-block:: bash - - conda install -c msys2 m2-base m2-make m2w64-toolchain libpython - -Native Installation for Debian/Ubuntu-based Systems -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - - sudo apt-get install make gcc g++ python3 python3-dev python3-pip - sudo apt-get install swig # optional, needed for one of the examples - -Native Installation for Red Hat-based Systems -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - - sudo yum install make gcc gcc-c++ libstdc++-devel python3 python3-devel python3-pip - sudo yum install swig # optional, needed for one of the examples - -Installation for macOS -^^^^^^^^^^^^^^^^^^^^^^ - -You need a few packages installed to get cocotb running on macOS. -Installing a package manager really helps things out here. - -`Brew `_ seems to be the most popular, so we'll assume you have that installed. - -.. code-block:: bash - - brew install python icarus-verilog gtkwave - -.. seealso:: - - For more installation options (also for 32-bit) please see `our Wiki `_. - - -Simulator Installation -^^^^^^^^^^^^^^^^^^^^^^ - -For detailed instructions and the list of supported simulators see :ref:`simulator-support`. - -.. note:: - - Ensure that path to the simulator executable is appended to the ``PATH`` environment variable. - - -.. _installation_via_pip:: - -Cocotb Installation via PIP ---------------------------- - -.. versionadded:: 1.2 - -Cocotb can be installed by running - -.. code-block:: bash - - pip install cocotb - -.. seealso:: - - For user-local installation, follow the `pip User Guide `_. - -To install the development version of cocotb: - -.. code-block:: bash - - pip install https://github.com/cocotb/cocotb/archive/master.zip - -.. warning:: - - ``pip`` may belong to a different python installation to what you expect. - Use ``pip -V`` to check. - If this shows Python 2.7, use ``pip3`` or ``python3 -m pip`` in place of ``pip`` in the commands below. - -.. note:: - - After installation, you should be able to execute ``cocotb-config``. - If it is not found, you need to append its location to the ``PATH`` environment variable. - This may happen when you use the ``--user`` option to ``pip``, in which case the location is documented :ref:`here `. - Running your first Example ========================== -Download and extract the cocotb source files according to the version you are using from -https://github.com/cocotb/cocotb/releases -You can check your cocotb version with ``cocotb-config --version``. +Make sure you have the :ref:`prerequisites` +(Python with development packages, a C++ compiler with development packages, GNU Make, +a :ref:`supported simulator`) and cocotb itself (``pip install cocotb``) available. + +Download and extract the cocotb source files according to the *release version* you are using from +https://github.com/cocotb/cocotb/releases - you can check your cocotb version with ``cocotb-config --version``. -The sources for cocotb's development version are available from +The sources for cocotb's *development version* are available from https://github.com/cocotb/cocotb/archive/master.zip -Assuming you have installed the prerequisites as above, -the following lines are all you need to run a first simulation with cocotb: +The following lines are all you need to run a first simulation with cocotb: .. code-block:: bash cd cocotb/examples/endian_swapper/tests make -Selecting a different simulator is as easy as: +This was running with the default simulator, Icarus Verilog, +but selecting a different simulator is as easy as: .. code-block:: bash @@ -145,7 +39,7 @@ Running the same example as VHDL The ``endian_swapper`` example includes both a VHDL and a Verilog RTL implementation. The cocotb testbench can execute against either implementation using VPI for Verilog and VHPI/FLI for VHDL. To run the test suite against the VHDL -implementation use the following command (a VHPI or FLI capable simulator must +implementation, use the following command (a VHPI or FLI capable simulator must be used): .. code-block:: bash @@ -256,7 +150,7 @@ The syntax ``sig <= new_value`` is a short form of ``sig.value = new_value``. It not only resembles HDL syntax, but also has the same semantics: writes are not applied immediately, but delayed until the next write cycle. Use ``sig.setimmediatevalue(new_val)`` to set a new value immediately -(see :meth:`~cocotb.handle.ModifiableObject.setimmediatevalue`). +(see :meth:`~cocotb.handle.NonHierarchyObject.setimmediatevalue`). In addition to regular value assignments (deposits), signals can be forced to a predetermined value or frozen at their current value. To achieve this, From f64b90ff7587c00b9ccb08d8fdb70e5d72f695ed Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 13 May 2020 19:29:36 +0200 Subject: [PATCH 2264/2656] Fix contact information Replace dead `author_email` with `maintainer_email` pointing to the mailinglist. --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9d3e2588..98a565ea 100755 --- a/setup.py +++ b/setup.py @@ -92,7 +92,8 @@ def package_files(directory): long_description=read_file('README.md'), long_description_content_type='text/markdown', author='Chris Higgs, Stuart Hodgson', - author_email='cocotb@potentialventures.com', + maintainer='cocotb contributors', + maintainer_email='cocotb@lists.librecores.org', install_requires=[], python_requires='>=3.5', packages=find_packages(), From e74d508e30027c16778b95ef0985b6bcbc5207c2 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Wed, 13 May 2020 18:04:07 +0100 Subject: [PATCH 2265/2656] Make Kaleb maintainer Kaleb has consistently shown great work in various parts of cocotb, and we're happy that he has agreed to become co-maintainer of cocotb. --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2c07a10e..fdf86096 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -134,6 +134,7 @@ Maintainers Cocotb uses a shared maintainer model. Most maintainers are experts in part of the cocotb codebase, and are primarily responsible for reviews in this area. +- Kaleb Barrett (@ktbarrett) - Julius Baxter (@juliusbaxter) - Luke Darnell (@lukedarnell) - Tomasz Hemperek (@themperek) From eeffad6ef723408279350f2a253f9c636ac0189e Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 13 May 2020 20:25:00 +0200 Subject: [PATCH 2266/2656] Use https://docs.cocotb.org for `url` temporarily The original `https://cocotb.org` only works as http (not https) right now. Closes #1809 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 98a565ea..91255d04 100755 --- a/setup.py +++ b/setup.py @@ -87,7 +87,7 @@ def package_files(directory): cmdclass={'build_ext': build_ext}, version=__version__, # noqa: F821 description='cocotb is a coroutine based cosimulation library for writing VHDL and Verilog testbenches in Python.', - url='https://cocotb.org', + url='https://docs.cocotb.org', license='BSD', long_description=read_file('README.md'), long_description_content_type='text/markdown', From db766477b3da291b1c278ecdba88dd77eb920b34 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 13 May 2020 01:19:28 +0200 Subject: [PATCH 2267/2656] Ignore binary files in CI whitespace check --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 35301e1e..8f4919b1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ jobs: - stage: Lint name: whitespace os: linux - script: "[[ -z \"$(git ls-files | xargs egrep -l '\\s+$')\" ]]" + script: "! git --no-pager grep --color --line-number --full-name --extended-regexp -I '\\s+$'" - stage: Lint name: flake8 From ed2c954e9f0ad55eafc368432887dc58003227f3 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 14 May 2020 00:08:58 +0200 Subject: [PATCH 2268/2656] Fix doc rendering problem at NO_COLOR --- documentation/source/building.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/source/building.rst b/documentation/source/building.rst index 5a2de0ce..4346b914 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -189,6 +189,7 @@ Environment Variables ``COCOTB_ANSI_OUTPUT=0`` suppresses the ANSI output in the log messages .. envvar:: NO_COLOR + From http://no-color.org, All command-line software which outputs text with ANSI color added should check for the presence From 9d367e44f096bf01a2795786f7191cf71c3d2981 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Thu, 14 May 2020 11:21:42 +1000 Subject: [PATCH 2269/2656] Remove duplicate always block and fix alignment --- tests/designs/sample_module/sample_module.sv | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/designs/sample_module/sample_module.sv b/tests/designs/sample_module/sample_module.sv index bf35f938..a0c10647 100644 --- a/tests/designs/sample_module/sample_module.sv +++ b/tests/designs/sample_module/sample_module.sv @@ -43,12 +43,12 @@ module sample_module ( output reg stream_in_ready, input stream_in_valid, `ifndef __ICARUS__ - input real stream_in_real, + input real stream_in_real, input integer stream_in_int, output real stream_out_real, output integer stream_out_int, input test_if inout_if, - input string stream_in_string, + input string stream_in_string, `endif input [7:0] stream_in_data, input [63:0] stream_in_data_wide, @@ -74,9 +74,6 @@ always @(posedge clk) always @(stream_in_data) stream_out_data_comb = stream_in_data; -always @(stream_in_data) - stream_out_data_comb = stream_in_data; - always @(stream_out_ready) stream_in_ready = stream_out_ready; From 7f27c77e675b4beb4dde085c3a6cf2af53336b1d Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 26 Apr 2020 12:52:58 -0500 Subject: [PATCH 2270/2656] Move comment to correct location --- cocotb/__init__.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 436f1a7f..b7b15d30 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -47,11 +47,6 @@ # Things we want in the cocotb namespace from cocotb.decorators import test, coroutine, hook, function, external # noqa: F401 -# Singleton scheduler instance -# NB this cheekily ensures a singleton since we're replacing the reference -# so that cocotb.scheduler gives you the singleton instance and not the -# scheduler package - from ._version import __version__ # GPI logging instance @@ -103,6 +98,12 @@ def _reopen_stream_with_buffering(stream_name): if not sys.warnoptions: warnings.simplefilter("default") + +# Singleton scheduler instance +# NB this cheekily ensures a singleton since we're replacing the reference +# so that cocotb.scheduler gives you the singleton instance and not the +# scheduler package + scheduler = Scheduler() """The global scheduler instance.""" From c3c5edb7b9fcd7006079c59d56b95ee0461ac73e Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 26 Apr 2020 13:02:33 -0500 Subject: [PATCH 2271/2656] Move scheduler init to _initialize_testbench --- cocotb/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index b7b15d30..1dd63316 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -104,7 +104,7 @@ def _reopen_stream_with_buffering(stream_name): # so that cocotb.scheduler gives you the singleton instance and not the # scheduler package -scheduler = Scheduler() +scheduler = None """The global scheduler instance.""" regression_manager = None @@ -171,6 +171,9 @@ def _initialise_testbench(argv_): process_plusargs() + global scheduler + scheduler = Scheduler() + # Seed the Python random number generator to make this repeatable global RANDOM_SEED RANDOM_SEED = os.getenv('RANDOM_SEED') From 29220b4e6fb014d73bbbc6a4257926e92eae4336 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 26 Apr 2020 13:27:46 -0500 Subject: [PATCH 2272/2656] Make fork a function instead of alias --- cocotb/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 1dd63316..bfdd4fdf 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -112,8 +112,10 @@ def _reopen_stream_with_buffering(stream_name): plusargs = {} """A dictionary of "plusargs" handed to the simulation.""" -# To save typing provide an alias to scheduler.add -fork = scheduler.add + +def fork(coro): + """ Schedule a coroutine to be run concurrently. See :ref:`coroutines` for details on it's use. """ + return scheduler.add(coro) # FIXME is this really required? _rlock = threading.RLock() From c1a88599223407f2bae05f713be393cd0083f6df Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 26 Apr 2020 13:33:35 -0500 Subject: [PATCH 2273/2656] move logging setup to _initialize_testbench --- cocotb/__init__.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index bfdd4fdf..c0dd66bb 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -49,13 +49,9 @@ from ._version import __version__ -# GPI logging instance -if "COCOTB_SIM" in os.environ: - # sys.path normally includes "" (the current directory), but does not appear to when python is embedded. - # Add it back because users expect to be able to import files in their test directory. - # TODO: move this to gpi_embed.cpp - sys.path.insert(0, "") +def _setup_logging(): + global log def _reopen_stream_with_buffering(stream_name): try: @@ -92,12 +88,6 @@ def _reopen_stream_with_buffering(stream_name): del _stderr_buffer_result, _stdout_buffer_result - # From https://www.python.org/dev/peps/pep-0565/#recommended-filter-settings-for-test-runners - # If the user doesn't want to see these, they can always change the global - # warning settings in their test module. - if not sys.warnoptions: - warnings.simplefilter("default") - # Singleton scheduler instance # NB this cheekily ensures a singleton since we're replacing the reference @@ -117,6 +107,7 @@ def fork(coro): """ Schedule a coroutine to be run concurrently. See :ref:`coroutines` for details on it's use. """ return scheduler.add(coro) + # FIXME is this really required? _rlock = threading.RLock() @@ -154,6 +145,19 @@ def _initialise_testbench(argv_): # Skip any library component of the toplevel root_name = root_name.split(".", 1)[1] + # sys.path normally includes "" (the current directory), but does not appear to when python is embedded. + # Add it back because users expect to be able to import files in their test directory. + # TODO: move this to gpi_embed.cpp + sys.path.insert(0, "") + + _setup_logging() + + # From https://www.python.org/dev/peps/pep-0565/#recommended-filter-settings-for-test-runners + # If the user doesn't want to see these, they can always change the global + # warning settings in their test module. + if not sys.warnoptions: + warnings.simplefilter("default") + from cocotb import simulator global SIM_NAME, SIM_VERSION From 3a0bf9460515ae0c2269890b75ddbeaa80085cff Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 26 Apr 2020 13:41:09 -0500 Subject: [PATCH 2274/2656] Import simulator module unconditionally --- cocotb/handle.py | 8 +------- cocotb/log.py | 5 ++--- cocotb/regression.py | 7 ++----- cocotb/triggers.py | 7 +------ cocotb/utils.py | 13 ++++--------- 5 files changed, 10 insertions(+), 30 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index a5dc6f4d..14f64c08 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -32,14 +32,8 @@ import ctypes import warnings -import os - -if "COCOTB_SIM" in os.environ: - from cocotb import simulator -else: - simulator = None - import cocotb +from cocotb import simulator from cocotb.binary import BinaryValue from cocotb.log import SimLog from cocotb.result import TestError diff --git a/cocotb/log.py b/cocotb/log.py index 927f2b31..5c72e973 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -96,9 +96,8 @@ def default_config(): # Notify GPI of log level, which it uses as an optimization to avoid # calling into Python. - if "COCOTB_SIM" in os.environ: - from cocotb import simulator - simulator.log_level(_default_log) + from cocotb import simulator + simulator.log_level(_default_log) class SimBaseLog(logging.getLoggerClass()): diff --git a/cocotb/regression.py b/cocotb/regression.py index 7ab6ef4b..9825395b 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -46,16 +46,13 @@ from cocotb.outcomes import Outcome, Error from cocotb.handle import SimHandle +from cocotb import simulator + if "COCOTB_PDB_ON_EXCEPTION" in os.environ: _pdb_on_exception = True else: _pdb_on_exception = False -if "COCOTB_SIM" in os.environ: - from cocotb import simulator -else: - simulator = None - # Optional support for coverage collection of testbench files coverage = None if "COVERAGE" in os.environ: diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 14816667..064d4805 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -27,15 +27,10 @@ """A collections of triggers which a testbench can await.""" -import os import abc from collections.abc import Awaitable -if "COCOTB_SIM" in os.environ: - from cocotb import simulator -else: - simulator = None - +from cocotb import simulator from cocotb.log import SimLog from cocotb.utils import ( get_sim_steps, get_time_from_sim_steps, ParametrizedSingleton, diff --git a/cocotb/utils.py b/cocotb/utils.py index 5d5c59da..3b0fca6d 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -35,12 +35,7 @@ import functools import warnings -if "COCOTB_SIM" in os.environ: - from cocotb import simulator - _LOG_SIM_PRECISION = simulator.get_precision() # request once and cache -else: - simulator = None - _LOG_SIM_PRECISION = -15 +from cocotb import simulator def get_python_integer_types(): @@ -94,7 +89,7 @@ def get_time_from_sim_steps(steps, units): Returns: The simulation time in the specified units. """ - return _ldexp10(steps, _LOG_SIM_PRECISION - _get_log_time_scale(units)) + return _ldexp10(steps, simulator.get_precision() - _get_log_time_scale(units)) def get_sim_steps(time, units=None): @@ -114,14 +109,14 @@ def get_sim_steps(time, units=None): """ result = time if units is not None: - result = _ldexp10(result, _get_log_time_scale(units) - _LOG_SIM_PRECISION) + result = _ldexp10(result, _get_log_time_scale(units) - simulator.get_precision()) result_rounded = math.floor(result) if result_rounded != result: raise ValueError("Unable to accurately represent {0}({1}) with the " "simulator precision of 1e{2}".format( - time, units, _LOG_SIM_PRECISION)) + time, units, simulator.get_precision())) return int(result_rounded) From 91fead59fe5e84c56c74f96403025e7026097d47 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Tue, 28 Apr 2020 12:33:03 -0500 Subject: [PATCH 2275/2656] Cache simulator precision --- cocotb/utils.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/cocotb/utils.py b/cocotb/utils.py index 3b0fca6d..2c437d45 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -38,6 +38,14 @@ from cocotb import simulator +def _get_simulator_precision(): + # cache and replace this function + precision = simulator.get_precision() + global _get_simulator_precision + _get_simulator_precision = precision.__int__ + return _get_simulator_precision() + + def get_python_integer_types(): warnings.warn( "This is an internal cocotb function, use six.integer_types instead", @@ -89,7 +97,7 @@ def get_time_from_sim_steps(steps, units): Returns: The simulation time in the specified units. """ - return _ldexp10(steps, simulator.get_precision() - _get_log_time_scale(units)) + return _ldexp10(steps, _get_simulator_precision() - _get_log_time_scale(units)) def get_sim_steps(time, units=None): @@ -109,14 +117,14 @@ def get_sim_steps(time, units=None): """ result = time if units is not None: - result = _ldexp10(result, _get_log_time_scale(units) - simulator.get_precision()) + result = _ldexp10(result, _get_log_time_scale(units) - _get_simulator_precision()) result_rounded = math.floor(result) if result_rounded != result: raise ValueError("Unable to accurately represent {0}({1}) with the " "simulator precision of 1e{2}".format( - time, units, simulator.get_precision())) + time, units, _get_simulator_precision())) return int(result_rounded) From 1bf1f495937f207d7fee1d0e54e1162e7ad68bcb Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 29 Apr 2020 08:43:22 -0500 Subject: [PATCH 2276/2656] Remove COCOTB_SIM from C sources --- cocotb/share/lib/gpi/GpiCommon.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cocotb/share/lib/gpi/GpiCommon.cpp b/cocotb/share/lib/gpi/GpiCommon.cpp index a6ad57e5..dcf2ee6f 100644 --- a/cocotb/share/lib/gpi/GpiCommon.cpp +++ b/cocotb/share/lib/gpi/GpiCommon.cpp @@ -202,11 +202,6 @@ void gpi_load_extra_libs() /* Lets look at what other libs we were asked to load too */ char *lib_env = getenv("GPI_EXTRA"); - /* inform python that we are in simulation - * TODO[gh-1566]: Eliminate the need for this completely. */ - static char cocotb_sim_env[] = "COCOTB_SIM=1"; - putenv(cocotb_sim_env); // putenv requires the array lifetime to be as long as the program lifetime, hence the static - if (lib_env) { std::string lib_list = lib_env; std::string const delim = ","; From 950f8f4aec7b3fcc18576cd14c7ce7f11acc1db3 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Thu, 30 Apr 2020 16:19:36 -0500 Subject: [PATCH 2277/2656] Add built libs to PATH whenever cocotb is imported This is necessary for Windows which does not support RPATH, causing it to not be able to locate the simulator module, which is now imported unconditionally. --- cocotb/__init__.py | 1 + cocotb/_os_compat.py | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 cocotb/_os_compat.py diff --git a/cocotb/__init__.py b/cocotb/__init__.py index c0dd66bb..435413cf 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -38,6 +38,7 @@ import time import warnings +import cocotb._os_compat # must appear first, before the first import of cocotb.simulator import cocotb.handle import cocotb.log from cocotb.scheduler import Scheduler diff --git a/cocotb/_os_compat.py b/cocotb/_os_compat.py new file mode 100644 index 00000000..c43e482e --- /dev/null +++ b/cocotb/_os_compat.py @@ -0,0 +1,21 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause +""" +OS-specific hacks +""" +import os +import sys + +# This is necessary for Windows, which does not support RPATH, causing it +# to not be able to locate the simulator module, which is imported +# unconditionally. + +extra_dll_dir = os.path.join(os.path.dirname(__file__), 'libs') + +if sys.platform == 'win32' and os.path.isdir(extra_dll_dir): + if sys.version_info >= (3, 8): + os.add_dll_directory(extra_dll_dir) + else: + os.environ.setdefault('PATH', '') + os.environ['PATH'] += os.pathsep + extra_dll_dir From 0405da1750d88f813af28c7228e50ec088da6178 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 2 May 2020 09:54:19 -0500 Subject: [PATCH 2278/2656] Update Timer so it doesn't call into the simulator --- cocotb/triggers.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 064d4805..d039e195 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -33,7 +33,7 @@ from cocotb import simulator from cocotb.log import SimLog from cocotb.utils import ( - get_sim_steps, get_time_from_sim_steps, ParametrizedSingleton, + get_sim_steps, ParametrizedSingleton, lazy_property, remove_traceback_frames, ) from cocotb import outcomes @@ -196,7 +196,13 @@ def __init__(self, time_ps, units=None): :func:`~cocotb.utils.get_sim_steps` """ GPITrigger.__init__(self) - self.sim_steps = get_sim_steps(time_ps, units) + self._time_ps = time_ps + self._units = units + + @lazy_property + def sim_steps(self): + # lazy so we don't call into the simulator until we need to + return get_sim_steps(self._time_ps, self._units) def prime(self, callback): """Register for a timed callback.""" @@ -208,9 +214,10 @@ def prime(self, callback): GPITrigger.prime(self, callback) def __repr__(self): - return "<{} of {:1.2f}ps at {}>".format( + return "<{} of {:1.2f}{} at {}>".format( type(self).__qualname__, - get_time_from_sim_steps(self.sim_steps, units='ps'), + self._time_ps, + self._units, _pointer_str(self) ) From a8dec314dc50b47c4a5b21c6362c2b5a64afec06 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 2 May 2020 09:55:06 -0500 Subject: [PATCH 2279/2656] Make documentation install cocotb in editable mode --- documentation/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Makefile b/documentation/Makefile index 542aa5f8..41f75dce 100644 --- a/documentation/Makefile +++ b/documentation/Makefile @@ -21,7 +21,7 @@ help: .venv python3 -m venv .venv .venv/bin/pip -q install --upgrade pip .venv/bin/pip -q install -r requirements.txt - .venv/bin/pip -q install .. + .venv/bin/pip -q install -e .. .PHONY: clean clean: .venv Makefile From 56586c4c3fd878f9ada6fc170b9a4e46dfee15a9 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 1 May 2020 08:33:55 -0500 Subject: [PATCH 2280/2656] Fed towncrier project info to prevent import Need to feed towncrier the project name and version number to prevent it from attemping to import the project to get the name and version. It currently does so by importing frm the source tree, this is no longer supported because we unconditionally import the simulator module. --- documentation/source/conf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/documentation/source/conf.py b/documentation/source/conf.py index 086ec38e..aa67107d 100644 --- a/documentation/source/conf.py +++ b/documentation/source/conf.py @@ -337,7 +337,8 @@ # -- Extra setup for towncrier ------------------------------------------------- # see also https://towncrier.readthedocs.io/en/actual-freaking-docs/ -in_progress_notes = subprocess.check_output(['towncrier', '--draft'], +# we pass the name and version directly, to avoid towncrier failing to import the non-installed version +in_progress_notes = subprocess.check_output(['towncrier', '--draft', '--name', 'cocotb', '--version', release], cwd='../..', universal_newlines=True) with open('generated/master-notes.rst', 'w') as f: From 53740b7978c4fb96004e2c59c997a383dcd09422 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 29 Apr 2020 00:02:26 +0200 Subject: [PATCH 2281/2656] Partition make- and environment variables Closes #1477 --- cocotb/share/makefiles/Makefile.sim | 60 ++--- documentation/source/building.rst | 361 ++++++++++++++-------------- 2 files changed, 219 insertions(+), 202 deletions(-) diff --git a/cocotb/share/makefiles/Makefile.sim b/cocotb/share/makefiles/Makefile.sim index 7a870071..536f8502 100644 --- a/cocotb/share/makefiles/Makefile.sim +++ b/cocotb/share/makefiles/Makefile.sim @@ -42,8 +42,37 @@ regression Run simulator when dependencies have changes clean Remove build and simulation artefacts help This help text -Make Variables --------------- +GPI +--- +GPI_EXTRA Extra libraries to load at runtime (comma-separated) + +Cocotb +------ +TOPLEVEL Instance in the hierarchy to use as the DUT +RANDOM_SEED Random seed, to recreate a previous test stimulus +COCOTB_ANSI_OUTPUT Force cocotb to print or not print in color +COCOTB_REDUCED_LOG_FMT Display log lines shorter +COCOTB_ATTACH Pause time value in seconds before the simulator start +COCOTB_ENABLE_PROFILING Performance analysis of the Python portion of cocotb +COCOTB_HOOKS Comma-separated module list to be executed before test +COCOTB_LOG_LEVEL Default logging level (default INFO) +COCOTB_RESOLVE_X How to resolve X, Z, U, W on integer conversion +MEMCHECK HTTP port to use for debugging Python memory usage + +Regression Manager +------------------ +COCOTB_PDB_ON_EXCEPTION Drop into the Python debugger (pdb) on exception +MODULE Modules to search for test functions (comma-separated) +TESTCASE Test function(s) to run (comma-separated list) +COCOTB_RESULTS_FILE File name for xUnit XML tests results +COVERAGE Report Python coverage (also HDL for some simulators) + +Scheduler +--------- +COCOTB_SCHEDULER_DEBUG Enable additional output of coroutine scheduler + +Makefile-based Test Scripts +--------------------------- GUI Set this to 1 to enable the GUI mode in the simulator SIM Selects which simulator Makefile to use WAVES Enable wave traces dump for Riviera-PRO and Questa @@ -54,36 +83,13 @@ COMPILE_ARGS Arguments to pass to compile stage of simulation SIM_ARGS Arguments to pass to execution of compiled simulation EXTRA_ARGS Arguments for compile and execute phases PLUSARGS Plusargs to pass to the simulator -GPI_EXTRA A list of extra libraries that are dynamically loaded at runtime COCOTB_HDL_TIMEUNIT Default time unit for simulation COCOTB_HDL_TIMEPRECISION Default time precision for simulation CUSTOM_COMPILE_DEPS Add additional dependencies to the compilation target CUSTOM_SIM_DEPS Add additional dependencies to the simulation target SIM_BUILD Define a scratch directory for use by the simulator - -Environment Variables ---------------------- -TOPLEVEL Instance in the hierarchy to use as the DUT -RANDOM_SEED Random seed, to recreate a previous test stimulus -COCOTB_ANSI_OUTPUT Force cocotb to print or not print in color -COCOTB_REDUCED_LOG_FMT Display log lines shorter -COCOTB_PDB_ON_EXCEPTION Drop into the Python debugger (pdb) on exception -MODULE Modules to search for test functions (comma-separated) -TESTCASE Test function(s) to run (comma-separated list) -COCOTB_RESULTS_FILE File name for xUnit XML tests results SCRIPT_FILE Simulator script to run (for e.g. wave traces) -Additional Environment Variables --------------------------------- -COCOTB_ATTACH Pause time value in seconds before the simulator start -COCOTB_ENABLE_PROFILING Performance analysis of the Python portion of cocotb -COCOTB_HOOKS Comma-separated module list to be executed before test -COCOTB_LOG_LEVEL Default logging level (default INFO) -COCOTB_RESOLVE_X How to resolve X, Z, U, W on integer conversion -COCOTB_SCHEDULER_DEBUG Enable additional output of coroutine scheduler -COVERAGE Report Python coverage (also HDL for some simulators) -MEMCHECK HTTP port to use for debugging Python memory usage - For details, see $(DOCLINK) endef @@ -93,9 +99,9 @@ ifeq ($(MAKECMDGOALS),help) _VERSION := $(shell cocotb-config -v) # for non-intuitive use of ifneq see https://www.gnu.org/software/make/manual/make.html#Testing-Flags ifneq (,$(findstring dev,$(_VERSION))) - DOCLINK := https://cocotb.readthedocs.io/en/latest/building.html + DOCLINK := https://docs.cocotb.org/en/latest/building.html else - DOCLINK := https://cocotb.readthedocs.io/en/v$(_VERSION)/building.html + DOCLINK := https://docs.cocotb.org/en/v$(_VERSION)/building.html endif $(info $(helpmsg)) # is there a cleaner way to exit here? diff --git a/documentation/source/building.rst b/documentation/source/building.rst index 4346b914..958d4329 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -8,7 +8,7 @@ Make System =========== Makefiles are provided for a variety of simulators in :file:`cocotb/share/makefiles/simulators`. -The common Makefile :file:`cocotb/share/makefiles/Makefile.sim` includes the appropriate simulator Makefile based on the contents of the ``SIM`` variable. +The common Makefile :file:`cocotb/share/makefiles/Makefile.sim` includes the appropriate simulator Makefile based on the contents of the :make:var:`SIM` variable. Make Targets ------------ @@ -29,132 +29,16 @@ Typically the makefiles provided with cocotb for various simulators use a separa This allows for a rapid re-running of a simulator if none of the RTL source files have changed and therefore the simulator does not need to recompile the RTL. -.. - If you edit the following sections, please also update the "helpmsg" text in cocotb/share/makefiles/Makefile.sim - -Make Variables --------------- - -In addition to the below variables, one may pass additional options to the build process using the `conventional variables `_ for C and C++ compilation and linking: -`CXXFLAGS`, -`CFLAGS`, -`CPPFLAGS`, -`LDFLAGS`, -and -`LDLIBS` -. - -.. make:var:: GUI - - Set this to 1 to enable the GUI mode in the simulator (if supported). - -.. make:var:: SIM - - Selects which simulator Makefile to use. Attempts to include a simulator specific makefile from :file:`cocotb/share/makefiles/simulators/makefile.$(SIM)` - -.. make:var:: WAVES - - Set this to 1 to enable wave traces dump for the Aldec Riviera-PRO and Mentor Graphics Questa simulators. - To get wave traces in Icarus Verilog see :ref:`sim-icarus-waveforms`. - -.. make:var:: VERILOG_SOURCES - - A list of the Verilog source files to include. - Paths can be absolute or relative; if relative, they are interpreted as relative to the Makefile's location. - -.. make:var:: VHDL_SOURCES - - A list of the VHDL source files to include. - Paths can be absolute or relative; if relative, they are interpreted as relative to the Makefile's location. - -.. make:var:: VHDL_SOURCES_ - - A list of the VHDL source files to include in the VHDL library *lib* (currently for the GHDL simulator only). - -.. make:var:: COMPILE_ARGS - - Any arguments or flags to pass to the compile stage of the simulation. - - .. note:: - For use on the command line, set this as an environment variable: - ``COMPILE_ARGS="..." make`` (for fish and csh: ``env COMPILE_ARGS="..." make``). - See :ref:`troubleshooting-make-vars` for details. - -.. make:var:: SIM_ARGS - - Any arguments or flags to pass to the execution of the compiled simulation. - - .. note:: - For use on the command line, set this as an environment variable: - ``SIM_ARGS="..." make`` (for fish and csh: ``env SIM_ARGS="..." make``). - See :ref:`troubleshooting-make-vars` for details. - -.. make:var:: RUN_ARGS - - Any argument to be passed to the "first" invocation of a simulator that runs via a TCL script. - One motivating usage is to pass `-noautoldlibpath` to Questa to prevent it from loading the out-of-date libraries it ships with. - Used by Aldec Riviera-PRO and Mentor Graphics Questa simulator. - -.. make:var:: EXTRA_ARGS - - Passed to both the compile and execute phases of simulators with two rules, - or passed to the single compile and run command for simulators which don't have a distinct compilation stage. - - .. note:: - For use on the command line, set this as an environment variable: - ``EXTRA_ARGS="..." make`` (for fish and csh: ``env EXTRA_ARGS="..." make``). - See :ref:`troubleshooting-make-vars` for details. - -.. make:var:: PLUSARGS +Variables +========= - "Plusargs" are options that are starting with a plus (``+``) sign. - They are passed to the simulator and are also available within cocotb as :data:`cocotb.plusargs`. - In the simulator, they can be read by the Verilog/SystemVerilog system functions - ``$test$plusargs`` and ``$value$plusargs``. - - The special plusargs ``+ntb_random_seed`` and ``+seed``, if present, are evaluated - to set the random seed value if :envvar:`RANDOM_SEED` is not set. - If both ``+ntb_random_seed`` and ``+seed`` are set, ``+ntb_random_seed`` is used. - - .. note:: - For use on the command line, set this as an environment variable: - ``PLUSARGS="..." make`` (for fish and csh: ``env PLUSARGS="..." make``). - See :ref:`troubleshooting-make-vars` for details. - -.. make:var:: COCOTB_HDL_TIMEUNIT - - The default time unit that should be assumed for simulation when not specified by modules in the design. - If this isn't specified then it is assumed to be ``1ns``. - Allowed values are 1, 10, and 100. - Allowed units are ``s``, ``ms``, ``us``, ``ns``, ``ps``, ``fs``. - - .. versionadded:: 1.3 - -.. make:var:: COCOTB_HDL_TIMEPRECISION - - The default time precision that should be assumed for simulation when not specified by modules in the design. - If this isn't specified then it is assumed to be ``1ps``. - Allowed values are 1, 10, and 100. - Allowed units are ``s``, ``ms``, ``us``, ``ns``, ``ps``, ``fs``. - - .. versionadded:: 1.3 - -.. make:var:: CUSTOM_COMPILE_DEPS - - Use to add additional dependencies to the compilation target; useful for defining additional rules to run pre-compilation or if the compilation phase depends on files other than the RTL sources listed in :make:var:`VERILOG_SOURCES` or :make:var:`VHDL_SOURCES`. - -.. make:var:: CUSTOM_SIM_DEPS - - Use to add additional dependencies to the simulation target. - -.. make:var:: SIM_BUILD - - Use to define a scratch directory for use by the simulator. The path is relative to the Makefile location. - If not provided, the default scratch directory is :file:`sim_build`. +The following sections document makefile variables and environment variables according to their owner/consumer. +.. + If you edit the following sections, please also update the "helpmsg" text in cocotb/share/makefiles/Makefile.sim -Environment Variables -===================== +Cocotb +------ .. envvar:: TOPLEVEL @@ -200,39 +84,6 @@ Environment Variables If defined, log lines displayed in the terminal will be shorter. It will print only time, message type (``INFO``, ``WARNING``, ``ERROR``, ...) and the log message itself. -.. envvar:: COCOTB_PDB_ON_EXCEPTION - - If defined, cocotb will drop into the Python debugger (:mod:`pdb`) if a test fails with an exception. - -.. envvar:: MODULE - - The name of the module(s) to search for test functions. - Multiple modules can be specified using a comma-separated list. - All tests will be run from each specified module in order of the module's appearance in this list. - -.. envvar:: TESTCASE - - The name of the test function(s) to run. If this variable is not defined cocotb - discovers and executes all functions decorated with the :class:`cocotb.test` decorator in the supplied :envvar:`MODULE` list. - - Multiple test functions can be specified using a comma-separated list. - -.. envvar:: COCOTB_RESULTS_FILE - - The file name where xUnit XML tests results are stored. If not provided, the default is :file:`results.xml`. - - .. versionadded:: 1.3 - -.. envvar:: SCRIPT_FILE - - The name of a simulator script that is run as part of the simulation, e.g. for setting up wave traces. - You can usually write out such a file from the simulator's GUI. - This is currently supported for the Mentor Questa, Mentor ModelSim and Aldec Riviera simulators. - - -Additional Environment Variables --------------------------------- - .. envvar:: COCOTB_ATTACH In order to give yourself time to attach a debugger to the simulator process before it starts to run, @@ -248,11 +99,6 @@ Additional Environment Variables From this, a callgraph diagram can be generated with `gprof2dot `_ and ``graphviz``. See the ``profile`` Make target in the ``endian_swapper`` example on how to set this up. -.. envvar:: COCOTB_HOOKS - - A comma-separated list of modules that should be executed before the first test. - You can also use the :class:`cocotb.hook` decorator to mark a function to be run before test code. - .. envvar:: COCOTB_LOG_LEVEL The default logging level to use. This is set to ``INFO`` unless overridden. @@ -274,9 +120,50 @@ Additional Environment Variables Set to ``VALUE_ERROR`` by default. -.. envvar:: COCOTB_SCHEDULER_DEBUG +.. envvar:: MEMCHECK - Enable additional log output of the coroutine scheduler. + HTTP port to use for debugging Python's memory usage. + When set to e.g. ``8088``, data will be presented at ``_. + + This needs the :mod:`cherrypy` and :mod:`dowser` Python modules installed. + +.. envvar:: COCOTB_PY_DIR + + Path to the directory containing the cocotb Python package in the :file:`cocotb` subdirectory; + for cocotb-internal use. + +.. envvar:: COCOTB_SHARE_DIR + + Path to the directory containing the cocotb Makefiles and simulator libraries in the subdirectories + :file:`lib`, :file:`include`, and :file:`makefiles`; + for cocotb-internal use. + + +Regression Manager +~~~~~~~~~~~~~~~~~~ + +.. envvar:: COCOTB_PDB_ON_EXCEPTION + + If defined, cocotb will drop into the Python debugger (:mod:`pdb`) if a test fails with an exception. + +.. envvar:: MODULE + + The name of the module(s) to search for test functions. + Multiple modules can be specified using a comma-separated list. + All tests will be run from each specified module in order of the module's appearance in this list. + +.. envvar:: TESTCASE + + The name of the test function(s) to run. If this variable is not defined cocotb + discovers and executes all functions decorated with the :class:`cocotb.test` decorator in the supplied :envvar:`MODULE` list. + + Multiple test functions can be specified using a comma-separated list. + +.. envvar:: COCOTB_RESULTS_FILE + + The file name where xUnit XML tests results are stored. If not provided, the default is :file:`results.xml`. + + .. versionadded:: 1.3 .. envvar:: COVERAGE @@ -284,23 +171,22 @@ Additional Environment Variables This needs the :mod:`coverage` Python module to be installed. -.. envvar:: MEMCHECK +.. envvar:: COCOTB_HOOKS - HTTP port to use for debugging Python's memory usage. - When set to e.g. ``8088``, data will be presented at ``_. + A comma-separated list of modules that should be executed before the first test. + You can also use the :class:`cocotb.hook` decorator to mark a function to be run before test code. - This needs the :mod:`cherrypy` and :mod:`dowser` Python modules installed. -.. envvar:: COCOTB_PY_DIR +Scheduler +~~~~~~~~~ - Path to the directory containing the cocotb Python package in the :file:`cocotb` subdirectory. - You don't normally need to modify this. +.. envvar:: COCOTB_SCHEDULER_DEBUG -.. envvar:: COCOTB_SHARE_DIR + Enable additional log output of the coroutine scheduler. - Path to the directory containing the cocotb Makefiles and simulator libraries in the subdirectories - :file:`lib`, :file:`include`, and :file:`makefiles`. - You don't normally need to modify this. + +GPI +--- .. envvar:: GPI_EXTRA @@ -317,3 +203,128 @@ Additional Environment Variables .. versionchanged:: 1.4.0 Support for the custom entry point via ``:`` was added. Previously ``:`` was used as a separator between libraries instead of ``,``. + + +Makefile-based Test Scripts +--------------------------- + +.. make:var:: GUI + + Set this to 1 to enable the GUI mode in the simulator (if supported). + +.. make:var:: SIM + + Selects which simulator Makefile to use. Attempts to include a simulator specific makefile from :file:`cocotb/share/makefiles/simulators/makefile.$(SIM)` + +.. make:var:: WAVES + + Set this to 1 to enable wave traces dump for the Aldec Riviera-PRO and Mentor Graphics Questa simulators. + To get wave traces in Icarus Verilog see :ref:`sim-icarus-waveforms`. + +.. make:var:: VERILOG_SOURCES + + A list of the Verilog source files to include. + Paths can be absolute or relative; if relative, they are interpreted as relative to the Makefile's location. + +.. make:var:: VHDL_SOURCES + + A list of the VHDL source files to include. + Paths can be absolute or relative; if relative, they are interpreted as relative to the Makefile's location. + +.. make:var:: VHDL_SOURCES_ + + A list of the VHDL source files to include in the VHDL library *lib* (currently for the GHDL simulator only). + +.. make:var:: COMPILE_ARGS + + Any arguments or flags to pass to the compile stage of the simulation. + +.. make:var:: SIM_ARGS + + Any arguments or flags to pass to the execution of the compiled simulation. + +.. make:var:: RUN_ARGS + + Any argument to be passed to the "first" invocation of a simulator that runs via a TCL script. + One motivating usage is to pass `-noautoldlibpath` to Questa to prevent it from loading the out-of-date libraries it ships with. + Used by Aldec Riviera-PRO and Mentor Graphics Questa simulator. + +.. make:var:: EXTRA_ARGS + + Passed to both the compile and execute phases of simulators with two rules, or passed to the single compile and run command for simulators which don't have a distinct compilation stage. + +.. make:var:: PLUSARGS + + "Plusargs" are options that are starting with a plus (``+``) sign. + They are passed to the simulator and are also available within cocotb as :data:`cocotb.plusargs`. + In the simulator, they can be read by the Verilog/SystemVerilog system functions + ``$test$plusargs`` and ``$value$plusargs``. + + The special plusargs ``+ntb_random_seed`` and ``+seed``, if present, are evaluated + to set the random seed value if :envvar:`RANDOM_SEED` is not set. + If both ``+ntb_random_seed`` and ``+seed`` are set, ``+ntb_random_seed`` is used. + +.. make:var:: COCOTB_HDL_TIMEUNIT + + The default time unit that should be assumed for simulation when not specified by modules in the design. + If this isn't specified then it is assumed to be ``1ns``. + Allowed values are 1, 10, and 100. + Allowed units are ``s``, ``ms``, ``us``, ``ns``, ``ps``, ``fs``. + + .. versionadded:: 1.3 + +.. make:var:: COCOTB_HDL_TIMEPRECISION + + The default time precision that should be assumed for simulation when not specified by modules in the design. + If this isn't specified then it is assumed to be ``1ps``. + Allowed values are 1, 10, and 100. + Allowed units are ``s``, ``ms``, ``us``, ``ns``, ``ps``, ``fs``. + + .. versionadded:: 1.3 + +.. make:var:: CUSTOM_COMPILE_DEPS + + Use to add additional dependencies to the compilation target; useful for defining additional rules to run pre-compilation or if the compilation phase depends on files other than the RTL sources listed in :make:var:`VERILOG_SOURCES` or :make:var:`VHDL_SOURCES`. + +.. make:var:: CUSTOM_SIM_DEPS + + Use to add additional dependencies to the simulation target. + +.. make:var:: SIM_BUILD + + Use to define a scratch directory for use by the simulator. The path is relative to the Makefile location. + If not provided, the default scratch directory is :file:`sim_build`. + +.. envvar:: SCRIPT_FILE + + The name of a simulator script that is run as part of the simulation, e.g. for setting up wave traces. + You can usually write out such a file from the simulator's GUI. + This is currently supported for the Mentor Questa, Mentor ModelSim and Aldec Riviera simulators. + + +Library Build Process +--------------------- + +You can pass additional options to the library build process +(which is usually happening as part of the installation with ``pip``) using the +`conventional variables `_ +for C and C++ compilation and linking: +`CFLAGS`, +`CPPFLAGS`, +and +`LDFLAGS`. + +.. + `CXXFLAGS`, `LDLIBS` are not supported by distutils/pip + + +.. + TODO + + Build Defines + ------------- + + SINGLETON_HANDLES + PYTHON_SO_LIB + + simulator sim defines From dd30e2305897c9b53b45d89811f02d5fac9843cf Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 29 Apr 2020 00:02:26 +0200 Subject: [PATCH 2282/2656] Partition make- and environment variables Closes #1477 --- cocotb/share/makefiles/Makefile.sim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/share/makefiles/Makefile.sim b/cocotb/share/makefiles/Makefile.sim index 536f8502..3dd713a9 100644 --- a/cocotb/share/makefiles/Makefile.sim +++ b/cocotb/share/makefiles/Makefile.sim @@ -54,7 +54,6 @@ COCOTB_ANSI_OUTPUT Force cocotb to print or not print in color COCOTB_REDUCED_LOG_FMT Display log lines shorter COCOTB_ATTACH Pause time value in seconds before the simulator start COCOTB_ENABLE_PROFILING Performance analysis of the Python portion of cocotb -COCOTB_HOOKS Comma-separated module list to be executed before test COCOTB_LOG_LEVEL Default logging level (default INFO) COCOTB_RESOLVE_X How to resolve X, Z, U, W on integer conversion MEMCHECK HTTP port to use for debugging Python memory usage @@ -66,6 +65,7 @@ MODULE Modules to search for test functions (comma-separated) TESTCASE Test function(s) to run (comma-separated list) COCOTB_RESULTS_FILE File name for xUnit XML tests results COVERAGE Report Python coverage (also HDL for some simulators) +COCOTB_HOOKS Comma-separated module list to be executed before test Scheduler --------- From f6ccb40426e14361f8587da5bc7f3cbaccb2acc1 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 14 May 2020 18:51:42 +0200 Subject: [PATCH 2283/2656] Remove nonexisting PYTHON_FILES as a dependency --- cocotb/share/makefiles/simulators/Makefile.vcs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.vcs b/cocotb/share/makefiles/simulators/Makefile.vcs index 47b1ac9a..5135f378 100644 --- a/cocotb/share/makefiles/simulators/Makefile.vcs +++ b/cocotb/share/makefiles/simulators/Makefile.vcs @@ -78,7 +78,7 @@ $(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(CUSTOM $(EXTRA_ARGS) -debug -load $(LIB_DIR)/libcocotbvpi_vcs.so $(COMPILE_ARGS) $(VERILOG_SOURCES) # Execution phase -$(COCOTB_RESULTS_FILE): $(SIM_BUILD)/simv $(PYTHON_FILES) $(CUSTOM_SIM_DEPS) +$(COCOTB_RESULTS_FILE): $(SIM_BUILD)/simv $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) -MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ From b34befbcd5e3d7b02560f91fc637e82d23b5818e Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 13 May 2020 20:57:55 +0200 Subject: [PATCH 2284/2656] Give more context when truncating a `BinaryValue` Print original and truncated value with a reworded message. Add more tests for the different `BinaryRepresentation`s. --- cocotb/binary.py | 19 ++++---- tests/pytest/test_binary_value.py | 72 +++++++++++++++++++++---------- 2 files changed, 60 insertions(+), 31 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 4aec8455..35269c3f 100755 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -237,12 +237,12 @@ def _adjust_unsigned(self, x): else: rv = '0' * (self._n_bits - l) + x elif l > self._n_bits: - print("WARNING: truncating value to match requested number of bits " - "(%d -> %d)" % (l, self._n_bits)) if self.big_endian: rv = x[l - self._n_bits:] else: rv = x[:l - self._n_bits] + warnings.warn("{}-bit value requested, truncating value {!r} ({} bits) to {!r}".format( + self._n_bits, x, l, rv), category=RuntimeWarning, stacklevel=3) return rv def _adjust_signed_mag(self, x): @@ -258,12 +258,12 @@ def _adjust_signed_mag(self, x): rv = '0' * (self._n_bits - 1 - l) + x[1:] rv = x[0] + rv elif l > self._n_bits: - print("WARNING: truncating value to match requested number of bits " - "(%d -> %d)" % (l, self._n_bits)) if self.big_endian: rv = x[l - self._n_bits:] else: rv = x[:-(l - self._n_bits)] + warnings.warn("{}-bit value requested, truncating value {!r} ({} bits) to {!r}".format( + self._n_bits, x, l, rv), category=RuntimeWarning, stacklevel=3) else: rv = x return rv @@ -278,12 +278,12 @@ def _adjust_twos_comp(self, x): else: rv = x[0] * (self._n_bits - l) + x elif l > self._n_bits: - print("WARNING: truncating value to match requested number of bits " - "(%d -> %d)" % (l, self._n_bits)) if self.big_endian: rv = x[l - self._n_bits:] else: rv = x[:-(l - self._n_bits)] + warnings.warn("{}-bit value requested, truncating value {!r} ({} bits) to {!r}".format( + self._n_bits, x, l, rv), category=RuntimeWarning, stacklevel=3) else: rv = x return rv @@ -380,9 +380,10 @@ def _adjust(self): else: self._str = "0" * (self._n_bits - l) + self._str elif l > self._n_bits: - print("WARNING: truncating value to match requested number of bits " - "(%d -> %d)" % (l, self._n_bits)) - self._str = self._str[l - self._n_bits:] + rv = self._str[l - self._n_bits:] + warnings.warn("{}-bit value requested, truncating value {!r} ({} bits) to {!r}".format( + self._n_bits, self._str, l, rv), category=RuntimeWarning, stacklevel=3) + self._str = rv get_buff = buff.fget set_buff = buff.fset diff --git a/tests/pytest/test_binary_value.py b/tests/pytest/test_binary_value.py index 041da268..5258964b 100644 --- a/tests/pytest/test_binary_value.py +++ b/tests/pytest/test_binary_value.py @@ -5,6 +5,8 @@ from cocotb.binary import BinaryValue, BinaryRepresentation +TRUNCATION_MATCH = r"\d+-bit value requested, truncating value" + def test_init_big_endian_twos_comp(): bin1 = BinaryValue(value=-1, n_bits=2, bigEndian=True, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) @@ -111,22 +113,44 @@ def test_init_unsigned_negative_value(): pytest.fail("Expected ValueError when assigning negative number to unsigned BinaryValue") -def test_init_not_enough_bits(capsys): - bin1 = BinaryValue(value=128, n_bits=7, bigEndian=True, binaryRepresentation=BinaryRepresentation.UNSIGNED) - assert bin1._str == "0000000" - assert bin1.binstr == "0000000" - assert bin1.integer == 0 - captured = capsys.readouterr() - assert captured.out == "WARNING: truncating value to match requested number of bits (8 -> 7)\n" - - bin2 = BinaryValue(value="1111110000101100", n_bits=12, bigEndian=False, - binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) +def test_init_not_enough_bits(): + with pytest.warns(RuntimeWarning, match=TRUNCATION_MATCH): + bin1_unsigned = BinaryValue(value=128, n_bits=7, bigEndian=True, + binaryRepresentation=BinaryRepresentation.UNSIGNED) + assert bin1_unsigned._str == "0000000" + assert bin1_unsigned.binstr == "0000000" + assert bin1_unsigned.integer == 0 + + with pytest.warns(RuntimeWarning, match=TRUNCATION_MATCH): + bin1_sigmag = BinaryValue(value=128, n_bits=7, bigEndian=True, + binaryRepresentation=BinaryRepresentation.SIGNED_MAGNITUDE) + assert bin1_sigmag._str == "0000000" + assert bin1_sigmag.binstr == "0000000" + assert bin1_sigmag.integer == 0 + + with pytest.warns(RuntimeWarning, match=TRUNCATION_MATCH): + bin1_twoscomp = BinaryValue(value=128, n_bits=7, bigEndian=True, + binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) + assert bin1_twoscomp._str == "0000000" + assert bin1_twoscomp.binstr == "0000000" + assert bin1_twoscomp.integer == 0 + + with pytest.warns(RuntimeWarning, match=TRUNCATION_MATCH): + bin1_binstr = BinaryValue(value="110000000", n_bits=7, bigEndian=True) + assert bin1_binstr._str == "0000000" + assert bin1_binstr.binstr == "0000000" + assert bin1_binstr.integer == 0 + + with pytest.warns(RuntimeWarning, match=TRUNCATION_MATCH): + bin2 = BinaryValue(value="1111110000101100", n_bits=12, bigEndian=False, + binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) assert bin2._str == "110000101100" assert bin2.binstr == "110000101100" assert bin2.integer == -980 - bin3 = BinaryValue(value="1111110000101100", n_bits=11, bigEndian=False, - binaryRepresentation=BinaryRepresentation.SIGNED_MAGNITUDE) + with pytest.warns(RuntimeWarning, match=TRUNCATION_MATCH): + bin3 = BinaryValue(value="1111110000101100", n_bits=11, bigEndian=False, + binaryRepresentation=BinaryRepresentation.SIGNED_MAGNITUDE) assert bin3._str == "10000101100" assert bin3.binstr == "10000101100" assert bin3.integer == -44 @@ -248,36 +272,40 @@ def test_backwards_compatibility(): def test_buff_big_endian(): - orig_str = "011011001001" + orig_str = "0110"+"1100"+"1001" orig_bytes = b'\x06\xC9' # padding is the high bits of the first byte v = BinaryValue(value=orig_str, n_bits=12, bigEndian=True) assert v.buff == orig_bytes - # should be unchanged - v.buff = orig_bytes + with pytest.warns(RuntimeWarning, match=TRUNCATION_MATCH): + # the binstr is truncated, but its value should be unchanged + v.buff = orig_bytes assert v.buff == orig_bytes assert v.binstr == orig_str - # extra bits are stripped because they don't fit into the 12 bits - v.buff = b'\xF6\xC9' + with pytest.warns(RuntimeWarning, match=TRUNCATION_MATCH): + # extra bits are stripped because they don't fit into the 12 bits + v.buff = b'\xF6\xC9' assert v.buff == orig_bytes assert v.binstr == orig_str def test_buff_little_endian(): - orig_str = "011011001001" + orig_str = "0110"+"1100"+"1001" orig_bytes = b'\xC9\x06' # padding is the high bits of the last byte v = BinaryValue(value=orig_str, n_bits=12, bigEndian=False) assert v.buff == orig_bytes - # should be unchanged - v.buff = orig_bytes + with pytest.warns(RuntimeWarning, match=TRUNCATION_MATCH): + # the binstr is truncated, but its value should be unchanged + v.buff = orig_bytes assert v.buff == orig_bytes assert v.binstr == orig_str -# extra bits are stripped because they don't fit into the 12 bits - v.buff = b'\xC9\xF6' + with pytest.warns(RuntimeWarning, match=TRUNCATION_MATCH): + # extra bits are stripped because they don't fit into the 12 bits + v.buff = b'\xC9\xF6' assert v.buff == orig_bytes assert v.binstr == orig_str From cea743c761f7d654f8fc6220836c4a7e50577720 Mon Sep 17 00:00:00 2001 From: Tim Pambor Date: Mon, 11 May 2020 14:45:32 +0200 Subject: [PATCH 2285/2656] vpiFinish and vhpiFinish require an argument --- cocotb/share/lib/vhpi/VhpiImpl.cpp | 2 +- cocotb/share/lib/vpi/VpiImpl.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/share/lib/vhpi/VhpiImpl.cpp b/cocotb/share/lib/vhpi/VhpiImpl.cpp index 9fecb8e6..5f5b12d9 100644 --- a/cocotb/share/lib/vhpi/VhpiImpl.cpp +++ b/cocotb/share/lib/vhpi/VhpiImpl.cpp @@ -921,7 +921,7 @@ int VhpiImpl::deregister_callback(GpiCbHdl *gpi_hdl) void VhpiImpl::sim_end() { sim_finish_cb->set_call_state(GPI_DELETE); - vhpi_control(vhpiFinish); + vhpi_control(vhpiFinish, 1); check_vhpi_error(); } diff --git a/cocotb/share/lib/vpi/VpiImpl.cpp b/cocotb/share/lib/vpi/VpiImpl.cpp index 2d22a9a4..12087107 100644 --- a/cocotb/share/lib/vpi/VpiImpl.cpp +++ b/cocotb/share/lib/vpi/VpiImpl.cpp @@ -556,7 +556,7 @@ void VpiImpl::sim_end() */ if (GPI_DELETE != sim_finish_cb->get_call_state()) { sim_finish_cb->set_call_state(GPI_DELETE); - vpi_control(vpiFinish); + vpi_control(vpiFinish, 1); check_vpi_error(); } } From c8a28578c1adced4495e742f6e51afb46f6f5e7b Mon Sep 17 00:00:00 2001 From: Tim Pambor Date: Fri, 15 May 2020 10:22:44 +0200 Subject: [PATCH 2286/2656] Use constant for vpi_finish/vhpi_finish --- cocotb/share/include/vhpi_user_ext.h | 26 ++++++++++++++++++++++++++ cocotb/share/include/vpi_user_ext.h | 6 ++++++ cocotb/share/lib/vhpi/VhpiImpl.cpp | 2 +- cocotb/share/lib/vhpi/VhpiImpl.h | 1 + cocotb/share/lib/vpi/VpiImpl.cpp | 2 +- 5 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 cocotb/share/include/vhpi_user_ext.h diff --git a/cocotb/share/include/vhpi_user_ext.h b/cocotb/share/include/vhpi_user_ext.h new file mode 100644 index 00000000..97532d7c --- /dev/null +++ b/cocotb/share/include/vhpi_user_ext.h @@ -0,0 +1,26 @@ +// Copyright cocotb contributors +// Licensed under the Revised BSD License, see LICENSE for details. +// SPDX-License-Identifier: BSD-3-Clause + +/* extensions to vhpi_user.h */ + +#ifndef VHPI_USER_EXT_H +#define VHPI_USER_EXT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* arguments for vhpiStop or vhpiFinish */ +typedef enum +{ + vhpiDiagNone = 0, /* prints nothing */ + vhpiDiagTimeLoc = 1, /* prints simulation time and location */ + vhpiDiagTimeLocCPUMem = 2 /* prints simulation time, location and statistics about CPU and memory usage */ +} vhpiDiagT; + +#ifdef __cplusplus +} +#endif + +#endif /* VPI_USER_EXT_H */ diff --git a/cocotb/share/include/vpi_user_ext.h b/cocotb/share/include/vpi_user_ext.h index 59c2b9be..cd00cada 100644 --- a/cocotb/share/include/vpi_user_ext.h +++ b/cocotb/share/include/vpi_user_ext.h @@ -39,6 +39,12 @@ extern "C" { #define vpiInterconnectNet 533 #define vpiInterconnectArray 534 +/* arguments for vpiStop or vpiFinish */ +#define vpiDiagNone 0 /* prints nothing */ +#define vpiDiagTimeLoc 1 /* prints simulation time and location */ +#define vpiDiagTimeLocCPUMem 2 /* prints simulation time, location and + statistics about CPU and memory usage */ + #ifdef __cplusplus } #endif diff --git a/cocotb/share/lib/vhpi/VhpiImpl.cpp b/cocotb/share/lib/vhpi/VhpiImpl.cpp index 5f5b12d9..ac3d0fb0 100644 --- a/cocotb/share/lib/vhpi/VhpiImpl.cpp +++ b/cocotb/share/lib/vhpi/VhpiImpl.cpp @@ -921,7 +921,7 @@ int VhpiImpl::deregister_callback(GpiCbHdl *gpi_hdl) void VhpiImpl::sim_end() { sim_finish_cb->set_call_state(GPI_DELETE); - vhpi_control(vhpiFinish, 1); + vhpi_control(vhpiFinish, vhpiDiagTimeLoc); check_vhpi_error(); } diff --git a/cocotb/share/lib/vhpi/VhpiImpl.h b/cocotb/share/lib/vhpi/VhpiImpl.h index 68d68e2c..490861da 100644 --- a/cocotb/share/lib/vhpi/VhpiImpl.h +++ b/cocotb/share/lib/vhpi/VhpiImpl.h @@ -30,6 +30,7 @@ #include "../gpi/gpi_priv.h" #include +#include #include #include diff --git a/cocotb/share/lib/vpi/VpiImpl.cpp b/cocotb/share/lib/vpi/VpiImpl.cpp index 12087107..cdfeee95 100644 --- a/cocotb/share/lib/vpi/VpiImpl.cpp +++ b/cocotb/share/lib/vpi/VpiImpl.cpp @@ -556,7 +556,7 @@ void VpiImpl::sim_end() */ if (GPI_DELETE != sim_finish_cb->get_call_state()) { sim_finish_cb->set_call_state(GPI_DELETE); - vpi_control(vpiFinish, 1); + vpi_control(vpiFinish, vpiDiagTimeLoc); check_vpi_error(); } } From bd4f5c4ef7827758c99a54ae8ad41f0f035c70a4 Mon Sep 17 00:00:00 2001 From: Tim Pambor Date: Fri, 15 May 2020 10:34:03 +0200 Subject: [PATCH 2287/2656] Don't ignore exit code for VCS VCS exited with error due to missing paramter for vpiFinish. With the fix it is no longer required to ingore the exit code. --- cocotb/share/makefiles/simulators/Makefile.vcs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.vcs b/cocotb/share/makefiles/simulators/Makefile.vcs index 5135f378..7785f35c 100644 --- a/cocotb/share/makefiles/simulators/Makefile.vcs +++ b/cocotb/share/makefiles/simulators/Makefile.vcs @@ -81,7 +81,7 @@ $(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(CUSTOM $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/simv $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) - -MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ + MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(SIM_BUILD)/simv +define+COCOTB_SIM=1 $(SIM_ARGS) $(EXTRA_ARGS) $(call check_for_results_file) From e4aff106177d95a8fda7e3dfc4d2f4cfb06341be Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Fri, 15 May 2020 15:42:25 +0200 Subject: [PATCH 2288/2656] =?UTF-8?q?Update=C2=A0news=C2=A0fragments=C2=A0?= =?UTF-8?q?with=C2=A0makefile=C2=A0changes=20(#1818)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GHDL and Riviera-PRO makefiles have been aligned with others. --- documentation/source/newsfragments/1063.change.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/newsfragments/1063.change.rst b/documentation/source/newsfragments/1063.change.rst index d8f86d24..bf6f11a4 100644 --- a/documentation/source/newsfragments/1063.change.rst +++ b/documentation/source/newsfragments/1063.change.rst @@ -1 +1 @@ -Questa now interprets relative paths to source files as relative to the Makefile, the same behavior used by other simulators. +Mentor Questa, Aldec Riviera-PRO and GHDL are now started in the directory containing the Makefile and also save :file:`results.xml` there, bringing them in line with the behavior used by other simulators. (:pr:`1598`) (:pr:`1599`) From 6cd467da5b5e5816fba7abe274439c687a63b481 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Fri, 15 May 2020 15:39:10 +0200 Subject: [PATCH 2289/2656] Use simple_dff as example in quickstart guide --- documentation/source/quickstart.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index f03d53ac..e098df16 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -22,7 +22,7 @@ The following lines are all you need to run a first simulation with cocotb: .. code-block:: bash - cd cocotb/examples/endian_swapper/tests + cd cocotb/examples/simple_dff make This was running with the default simulator, Icarus Verilog, @@ -36,7 +36,7 @@ but selecting a different simulator is as easy as: Running the same example as VHDL -------------------------------- -The ``endian_swapper`` example includes both a VHDL and a Verilog RTL implementation. +The ``simple_dff`` example includes both a VHDL and a Verilog RTL implementation. The cocotb testbench can execute against either implementation using VPI for Verilog and VHPI/FLI for VHDL. To run the test suite against the VHDL implementation, use the following command (a VHPI or FLI capable simulator must From 9a850f9774301e319786db4d24592742f859cdfd Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 15 May 2020 00:17:19 +0200 Subject: [PATCH 2290/2656] Force doctest for `cocotb.utils` to run uncolored When color output had been requested via environment variables, `test_utils` would fail because of the ANSI codes being put into the output of `hexdiffs()`. Thus, force this test to run in an uncolored environment. --- tests/test_cases/test_cocotb/test_doctests.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_cases/test_cocotb/test_doctests.py b/tests/test_cases/test_cocotb/test_doctests.py index 03c8aac8..e3524e83 100644 --- a/tests/test_cases/test_cocotb/test_doctests.py +++ b/tests/test_cases/test_cocotb/test_doctests.py @@ -2,12 +2,15 @@ # Licensed under the Revised BSD License, see LICENSE for details. # SPDX-License-Identifier: BSD-3-Clause import doctest +import os import cocotb @cocotb.test() async def test_utils(dut): + # prevent failure in case colored output is requested from the environment + os.environ['COCOTB_ANSI_OUTPUT'] = "0" failures, n = doctest.testmod(cocotb.utils, verbose=True) assert failures == 0 From 7100214129b6c7a0fc9eb800fd45d5f3f3e55907 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 15 May 2020 17:43:28 -0500 Subject: [PATCH 2291/2656] Fixes #1831 Update Questa access options in makefile Questa 2020 errors when using the 'm' access option. Using the the full access option is more likely to be forwards and backwards compatible. --- cocotb/share/makefiles/simulators/Makefile.questa | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.questa b/cocotb/share/makefiles/simulators/Makefile.questa index 0cd4465a..28ed2eed 100644 --- a/cocotb/share/makefiles/simulators/Makefile.questa +++ b/cocotb/share/makefiles/simulators/Makefile.questa @@ -47,7 +47,7 @@ RTL_LIBRARY ?= work TOPLEVEL := "$(RTL_LIBRARY).$(TOPLEVEL)" ifndef VLOG_ARGS - VLOG_ARGS = -timescale $(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) -mfcu +acc=rmb + VLOG_ARGS = -timescale $(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) -mfcu +acc endif ifdef VERILOG_INCLUDE_DIRS From 1e270ac95cb6a146dc021ce2fc90f1f04e448971 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 13 May 2020 21:35:27 -0500 Subject: [PATCH 2292/2656] Fixes #1815 Use isinstance to check for tests Previously tests were detected by checking if module-level objects had the `im_test` attribute. This was not strong enough of an identity and false positives caused bugs. This was changed to check if an object is an instance of cocotb.test, which is a stronger identity check. --- cocotb/regression.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 9825395b..bf238af3 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -188,7 +188,7 @@ def _discover_tests() -> Iterable[Test]: err = AttributeError("Test %s doesn't exist in %s" % (test_name, module_name)) raise err from None # discard nested traceback - if not hasattr(test, "im_test"): + if not isinstance(test, Test): _logger.error("Requested %s from module %s isn't a cocotb.test decorated coroutine", test_name, module_name) raise ImportError("Failed to find requested test %s" % test_name) @@ -199,7 +199,7 @@ def _discover_tests() -> Iterable[Test]: # auto-discover for thing in vars(module).values(): - if hasattr(thing, "im_test"): + if isinstance(thing, Test): yield thing @staticmethod From 644fff0b3886c09470ad08a9a0e63c37f3aea2c0 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 15 May 2020 00:44:31 +0200 Subject: [PATCH 2293/2656] Add cocotb.simulator to Python library reference --- documentation/source/library_reference.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/documentation/source/library_reference.rst b/documentation/source/library_reference.rst index 301ee68b..1a1080a1 100644 --- a/documentation/source/library_reference.rst +++ b/documentation/source/library_reference.rst @@ -346,6 +346,19 @@ The Scheduler :member-order: bysource +The ``cocotb.simulator`` module +------------------------------- + +This module is a Python wrapper to libgpi. +It should not be considered public API, but is documented here for developers +of cocotb. + +.. automodule:: cocotb.simulator + :members: + :undoc-members: + :member-order: bysource + + The ``cocotb-config`` script ---------------------------- From 4831ba8a7059eaaaa2577672b362283564ec7ba0 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 16 May 2020 14:43:35 +0100 Subject: [PATCH 2294/2656] Expose type objects in the `cocotb.simulator` namespace (#1837) This means that python code can now use `isinstance(obj, cocotb.simulator.gpi_sim_hdl)`. Perhaps more usefully, it also means these types will appear in the docs. This also fixes an incorrect use of `tp_name`, which should have contained the fully-qualified name. --- .../share/lib/simulator/simulatormodule.cpp | 41 +++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index d57d0b19..4e70541e 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -849,6 +849,35 @@ static int add_module_constants(PyObject* simulator) return 0; } +// Add the extension types as entries in the module namespace +static int add_module_types(PyObject* simulator) +{ + PyObject *typ; + + typ = (PyObject *)&gpi_hdl_Object::py_type; + Py_INCREF(typ); + if (PyModule_AddObject(simulator, "gpi_sim_hdl", typ) < 0) { + Py_DECREF(typ); + return -1; + } + + typ = (PyObject *)&gpi_hdl_Object::py_type; + Py_INCREF(typ); + if (PyModule_AddObject(simulator, "gpi_cb_hdl", typ) < 0) { + Py_DECREF(typ); + return -1; + } + + typ = (PyObject *)&gpi_hdl_Object::py_type; + Py_INCREF(typ); + if (PyModule_AddObject(simulator, "gpi_iterator_hdl", typ) < 0) { + Py_DECREF(typ); + return -1; + } + + return 0; +} + static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, MODULE_NAME, @@ -883,6 +912,12 @@ PyMODINIT_FUNC PyInit_simulator(void) Py_DECREF(simulator); return NULL; } + + if (add_module_types(simulator) < 0) { + Py_DECREF(simulator); + return NULL; + } + return simulator; } @@ -951,7 +986,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { template<> PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { auto type = fill_common_slots(); - type.tp_name = "gpi_sim_hdl"; + type.tp_name = "cocotb.simulator.gpi_sim_hdl"; type.tp_methods = gpi_sim_hdl_methods; return type; }(); @@ -959,7 +994,7 @@ PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { template<> PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { auto type = fill_common_slots(); - type.tp_name = "gpi_iterator_hdl"; + type.tp_name = "cocotb.simulator.gpi_iterator_hdl"; type.tp_iter = PyObject_SelfIter; type.tp_iternext = (iternextfunc)next; return type; @@ -975,7 +1010,7 @@ static PyMethodDef gpi_cb_hdl_methods[] = { template<> PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { auto type = fill_common_slots(); - type.tp_name = "gpi_cb_hdl"; + type.tp_name = "cocotb.simulator.gpi_cb_hdl"; type.tp_methods = gpi_cb_hdl_methods; return type; }(); From 058ecf590ac5db7ed0b8bda21d5a3c399f1b3567 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 16 May 2020 11:09:41 +0100 Subject: [PATCH 2295/2656] Use the PyDoc_STR macro As recommended by the python docs, this means that if someone compiles python without docstrings, they can save a (small) amount of binary size. --- .../share/lib/simulator/simulatormodule.cpp | 40 +++++++++---------- cocotb/share/lib/simulator/simulatormodule.h | 39 ++++++++++++------ 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index 4e70541e..5d622bfd 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -924,61 +924,61 @@ PyMODINIT_FUNC PyInit_simulator(void) static PyMethodDef gpi_sim_hdl_methods[] = { {"get_signal_val_long", (PyCFunction)get_signal_val_long, METH_NOARGS, - "Get the value of a signal as a long"}, + PyDoc_STR("Get the value of a signal as a long")}, {"get_signal_val_str", (PyCFunction)get_signal_val_str, METH_NOARGS, - "Get the value of a signal as an ASCII string"}, + PyDoc_STR("Get the value of a signal as an ASCII string")}, {"get_signal_val_binstr", (PyCFunction)get_signal_val_binstr, METH_NOARGS, - "Get the value of a signal as a binary string"}, + PyDoc_STR("Get the value of a signal as a binary string")}, {"get_signal_val_real", (PyCFunction)get_signal_val_real, METH_NOARGS, - "Get the value of a signal as a double precision float"}, + PyDoc_STR("Get the value of a signal as a double precision float")}, {"set_signal_val_long", (PyCFunction)set_signal_val_long, METH_VARARGS, - "Set the value of a signal using a long"}, + PyDoc_STR("Set the value of a signal using a long")}, {"set_signal_val_str", (PyCFunction)set_signal_val_str, METH_VARARGS, - "Set the value of a signal using an NUL-terminated 8-bit string"}, + PyDoc_STR("Set the value of a signal using an NUL-terminated 8-bit string")}, {"set_signal_val_binstr", (PyCFunction)set_signal_val_binstr, METH_VARARGS, - "Set the value of a signal using a string with a character per bit"}, + PyDoc_STR("Set the value of a signal using a string with a character per bit")}, {"set_signal_val_real", (PyCFunction)set_signal_val_real, METH_VARARGS, - "Set the value of a signal using a double precision float"}, + PyDoc_STR("Set the value of a signal using a double precision float")}, {"get_definition_name", (PyCFunction)get_definition_name, METH_NOARGS, - "Get the name of a GPI object's definition"}, + PyDoc_STR("Get the name of a GPI object's definition")}, {"get_definition_file", (PyCFunction)get_definition_file, METH_NOARGS, - "Get the file that sources the object's definition"}, + PyDoc_STR("Get the file that sources the object's definition")}, {"get_handle_by_name", (PyCFunction)get_handle_by_name, METH_VARARGS, - "Get handle of a named object"}, + PyDoc_STR("Get handle of a named object")}, {"get_handle_by_index", (PyCFunction)get_handle_by_index, METH_VARARGS, - "Get handle of a object at an index in a parent"}, + PyDoc_STR("Get handle of a object at an index in a parent")}, {"get_name_string", (PyCFunction)get_name_string, METH_NOARGS, - "Get the name of an object as a string"}, + PyDoc_STR("Get the name of an object as a string")}, {"get_type_string", (PyCFunction)get_type_string, METH_NOARGS, - "Get the type of an object as a string"}, + PyDoc_STR("Get the type of an object as a string")}, {"get_type", (PyCFunction)get_type, METH_NOARGS, - "Get the type of an object, mapped to a GPI enumeration"}, + PyDoc_STR("Get the type of an object, mapped to a GPI enumeration")}, {"get_const", (PyCFunction)get_const, METH_NOARGS, - "Get a flag indicating whether the object is a constant"}, + PyDoc_STR("Get a flag indicating whether the object is a constant")}, {"get_num_elems", (PyCFunction)get_num_elems, METH_NOARGS, - "Get the number of elements contained in the handle"}, + PyDoc_STR("Get the number of elements contained in the handle")}, {"get_range", (PyCFunction)get_range, METH_NOARGS, - "Get the range of elements (tuple) contained in the handle, returns None if not indexable"}, + PyDoc_STR("Get the range of elements (tuple) contained in the handle, returns None if not indexable")}, {"iterate", (PyCFunction)iterate, METH_VARARGS, - "Get an iterator handle to loop over all members in an object"}, + PyDoc_STR("Get an iterator handle to loop over all members in an object")}, {NULL, NULL, 0, NULL} /* Sentinel */ }; @@ -1003,7 +1003,7 @@ PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { static PyMethodDef gpi_cb_hdl_methods[] = { {"deregister", (PyCFunction)deregister, METH_NOARGS, - "De-register this callback"}, + PyDoc_STR("De-register this callback")}, {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/cocotb/share/lib/simulator/simulatormodule.h b/cocotb/share/lib/simulator/simulatormodule.h index 25d9042f..71f01b33 100644 --- a/cocotb/share/lib/simulator/simulatormodule.h +++ b/cocotb/share/lib/simulator/simulatormodule.h @@ -70,20 +70,33 @@ static PyObject *get_simulator_version(PyObject *self, PyObject *args); static PyObject *log_level(PyObject *self, PyObject *args); static PyMethodDef SimulatorMethods[] = { - {"log_msg", log_msg, METH_VARARGS, "Log a message"}, - {"get_root_handle", get_root_handle, METH_VARARGS, "Get the root handle"}, - {"register_timed_callback", register_timed_callback, METH_VARARGS, "Register a timed callback"}, - {"register_value_change_callback", register_value_change_callback, METH_VARARGS, "Register a signal change callback"}, - {"register_readonly_callback", register_readonly_callback, METH_VARARGS, "Register a callback for the read-only section"}, - {"register_nextstep_callback", register_nextstep_callback, METH_VARARGS, "Register a callback for the NextSimTime callback"}, - {"register_rwsynch_callback", register_rwsynch_callback, METH_VARARGS, "Register a callback for the read-write section"}, - {"stop_simulator", stop_simulator, METH_VARARGS, "Instruct the attached simulator to stop"}, - {"log_level", log_level, METH_VARARGS, "Set the log level for GPI"}, + {"log_msg", log_msg, METH_VARARGS, + PyDoc_STR("Log a message")}, + {"get_root_handle", get_root_handle, METH_VARARGS, + PyDoc_STR("Get the root handle")}, + {"register_timed_callback", register_timed_callback, METH_VARARGS, + PyDoc_STR("Register a timed callback")}, + {"register_value_change_callback", register_value_change_callback, METH_VARARGS, + PyDoc_STR("Register a signal change callback")}, + {"register_readonly_callback", register_readonly_callback, METH_VARARGS, + PyDoc_STR("Register a callback for the read-only section")}, + {"register_nextstep_callback", register_nextstep_callback, METH_VARARGS, + PyDoc_STR("Register a callback for the NextSimTime callback")}, + {"register_rwsynch_callback", register_rwsynch_callback, METH_VARARGS, + PyDoc_STR("Register a callback for the read-write section")}, + {"stop_simulator", stop_simulator, METH_VARARGS, + PyDoc_STR("Instruct the attached simulator to stop")}, + {"log_level", log_level, METH_VARARGS, + PyDoc_STR("Set the log level for GPI")}, - {"get_sim_time", get_sim_time, METH_NOARGS, "Get the current simulation time as an int tuple"}, - {"get_precision", get_precision, METH_NOARGS, "Get the precision of the simulator"}, - {"get_simulator_product", get_simulator_product, METH_NOARGS, "Simulator product information"}, - {"get_simulator_version", get_simulator_version, METH_NOARGS, "Simulator product version information"}, + {"get_sim_time", get_sim_time, METH_NOARGS, + PyDoc_STR("Get the current simulation time as an int tuple")}, + {"get_precision", get_precision, METH_NOARGS, + PyDoc_STR("Get the precision of the simulator")}, + {"get_simulator_product", get_simulator_product, METH_NOARGS, + PyDoc_STR("Simulator product information")}, + {"get_simulator_version", get_simulator_version, METH_NOARGS, + PyDoc_STR("Simulator product version information")}, {NULL, NULL, 0, NULL} /* Sentinel */ }; From 38bbb354f0f2259d9489191da4fd24a134beb7ae Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 16 May 2020 11:29:34 +0100 Subject: [PATCH 2296/2656] Add argument names to docstrings Note that `/` is special syntax meaning "positional-only". C docstrings of the form ``` the_func(the_args) -- the docstring ``` and ``` the_method($self, the_args) -- the docstring ``` automatically populate the `__text_signature__` attribute, which is read by `inspect.signature`. --- .../share/lib/simulator/simulatormodule.cpp | 159 +++++++++++++----- cocotb/share/lib/simulator/simulatormodule.h | 91 +++++++--- 2 files changed, 184 insertions(+), 66 deletions(-) diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index 5d622bfd..37c4fa8b 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -923,62 +923,138 @@ PyMODINIT_FUNC PyInit_simulator(void) static PyMethodDef gpi_sim_hdl_methods[] = { {"get_signal_val_long", - (PyCFunction)get_signal_val_long, METH_NOARGS, - PyDoc_STR("Get the value of a signal as a long")}, + (PyCFunction)get_signal_val_long, METH_NOARGS, PyDoc_STR( + "get_signal_val_long($self)\n" + "--\n\n" + "Get the value of a signal as a long" + ) + }, {"get_signal_val_str", - (PyCFunction)get_signal_val_str, METH_NOARGS, - PyDoc_STR("Get the value of a signal as an ASCII string")}, + (PyCFunction)get_signal_val_str, METH_NOARGS, PyDoc_STR( + "get_signal_val_str($self)\n" + "--\n\n" + "Get the value of a signal as an ASCII string" + ) + }, {"get_signal_val_binstr", - (PyCFunction)get_signal_val_binstr, METH_NOARGS, - PyDoc_STR("Get the value of a signal as a binary string")}, + (PyCFunction)get_signal_val_binstr, METH_NOARGS, PyDoc_STR( + "get_signal_val_binstr($self)\n" + "--\n\n" + "Get the value of a signal as a binary string" + ) + }, {"get_signal_val_real", - (PyCFunction)get_signal_val_real, METH_NOARGS, - PyDoc_STR("Get the value of a signal as a double precision float")}, + (PyCFunction)get_signal_val_real, METH_NOARGS, PyDoc_STR( + "get_signal_val_real($self)\n" + "--\n\n" + "Get the value of a signal as a double precision float" + ) + }, {"set_signal_val_long", - (PyCFunction)set_signal_val_long, METH_VARARGS, - PyDoc_STR("Set the value of a signal using a long")}, + (PyCFunction)set_signal_val_long, METH_VARARGS, PyDoc_STR( + "set_signal_val_long($self, action, value, /)\n" + "--\n\n" + "Set the value of a signal using a long" + ) + }, {"set_signal_val_str", - (PyCFunction)set_signal_val_str, METH_VARARGS, - PyDoc_STR("Set the value of a signal using an NUL-terminated 8-bit string")}, + (PyCFunction)set_signal_val_str, METH_VARARGS, PyDoc_STR( + "set_signal_val_str($self, action, value, /)\n" + "--\n\n" + "Set the value of a signal using an NUL-terminated 8-bit string" + ) + }, {"set_signal_val_binstr", - (PyCFunction)set_signal_val_binstr, METH_VARARGS, - PyDoc_STR("Set the value of a signal using a string with a character per bit")}, + (PyCFunction)set_signal_val_binstr, METH_VARARGS, PyDoc_STR( + "set_signal_val_binstr($self, action, value, /)\n" + "--\n\n" + "Set the value of a signal using a string with a character per bit" + ) + }, {"set_signal_val_real", - (PyCFunction)set_signal_val_real, METH_VARARGS, - PyDoc_STR("Set the value of a signal using a double precision float")}, + (PyCFunction)set_signal_val_real, METH_VARARGS, PyDoc_STR( + "set_signal_val_real($self, action, value, /)\n" + "--\n\n" + "Set the value of a signal using a double precision float" + ) + }, {"get_definition_name", - (PyCFunction)get_definition_name, METH_NOARGS, - PyDoc_STR("Get the name of a GPI object's definition")}, + (PyCFunction)get_definition_name, METH_NOARGS, PyDoc_STR( + "get_definition_name($self)\n" + "--\n\n" + "Get the name of a GPI object's definition" + ) + }, {"get_definition_file", - (PyCFunction)get_definition_file, METH_NOARGS, - PyDoc_STR("Get the file that sources the object's definition")}, + (PyCFunction)get_definition_file, METH_NOARGS, PyDoc_STR( + "get_definition_file($self)\n" + "--\n\n" + "Get the file that sources the object's definition" + ) + }, {"get_handle_by_name", - (PyCFunction)get_handle_by_name, METH_VARARGS, - PyDoc_STR("Get handle of a named object")}, + (PyCFunction)get_handle_by_name, METH_VARARGS, PyDoc_STR( + "get_handle_by_name($self, name, /)\n" + "--\n\n" + "Get handle of a named object" + ) + }, {"get_handle_by_index", - (PyCFunction)get_handle_by_index, METH_VARARGS, - PyDoc_STR("Get handle of a object at an index in a parent")}, + (PyCFunction)get_handle_by_index, METH_VARARGS, PyDoc_STR( + "get_handle_by_index($self, index, /)\n" + "--\n\n" + "Get handle of a object at an index in a parent" + ) + }, {"get_name_string", - (PyCFunction)get_name_string, METH_NOARGS, - PyDoc_STR("Get the name of an object as a string")}, + (PyCFunction)get_name_string, METH_NOARGS, PyDoc_STR( + "get_name_string($self)\n" + "--\n\n" + "Get the name of an object as a string" + ) + }, {"get_type_string", - (PyCFunction)get_type_string, METH_NOARGS, - PyDoc_STR("Get the type of an object as a string")}, + (PyCFunction)get_type_string, METH_NOARGS, PyDoc_STR( + "get_type_string($self)\n" + "--\n\n" + "Get the type of an object as a string" + ) + }, {"get_type", - (PyCFunction)get_type, METH_NOARGS, - PyDoc_STR("Get the type of an object, mapped to a GPI enumeration")}, + (PyCFunction)get_type, METH_NOARGS, PyDoc_STR( + "get_type($self)\n" + "--\n\n" + "Get the type of an object, mapped to a GPI enumeration" + ) + }, {"get_const", - (PyCFunction)get_const, METH_NOARGS, - PyDoc_STR("Get a flag indicating whether the object is a constant")}, + (PyCFunction)get_const, METH_NOARGS, PyDoc_STR( + "get_const($self)\n" + "--\n\n" + "Get a flag indicating whether the object is a constant" + ) + }, {"get_num_elems", - (PyCFunction)get_num_elems, METH_NOARGS, - PyDoc_STR("Get the number of elements contained in the handle")}, + (PyCFunction)get_num_elems, METH_NOARGS, PyDoc_STR( + "get_num_elems($self)\n" + "--\n\n" + "Get the number of elements contained in the handle" + ) + }, {"get_range", - (PyCFunction)get_range, METH_NOARGS, - PyDoc_STR("Get the range of elements (tuple) contained in the handle, returns None if not indexable")}, + (PyCFunction)get_range, METH_NOARGS, PyDoc_STR( + "get_range($self)\n" + "--\n\n" + "Get the range of elements (tuple) contained in the handle, returns None if not indexable" + ) + }, {"iterate", - (PyCFunction)iterate, METH_VARARGS, - PyDoc_STR("Get an iterator handle to loop over all members in an object")}, + (PyCFunction)iterate, METH_VARARGS, PyDoc_STR( + "iterate($self, mode, /)\n" + "--\n\n" + "Get an iterator handle to loop over all members in an object" + ) + }, {NULL, NULL, 0, NULL} /* Sentinel */ }; @@ -1002,8 +1078,11 @@ PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { static PyMethodDef gpi_cb_hdl_methods[] = { {"deregister", - (PyCFunction)deregister, METH_NOARGS, - PyDoc_STR("De-register this callback")}, + (PyCFunction)deregister, METH_NOARGS, PyDoc_STR( + "deregister($self)\n" + "--\n\n" + "De-register this callback" + )}, {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/cocotb/share/lib/simulator/simulatormodule.h b/cocotb/share/lib/simulator/simulatormodule.h index 71f01b33..dd37b59a 100644 --- a/cocotb/share/lib/simulator/simulatormodule.h +++ b/cocotb/share/lib/simulator/simulatormodule.h @@ -70,33 +70,72 @@ static PyObject *get_simulator_version(PyObject *self, PyObject *args); static PyObject *log_level(PyObject *self, PyObject *args); static PyMethodDef SimulatorMethods[] = { - {"log_msg", log_msg, METH_VARARGS, - PyDoc_STR("Log a message")}, - {"get_root_handle", get_root_handle, METH_VARARGS, - PyDoc_STR("Get the root handle")}, - {"register_timed_callback", register_timed_callback, METH_VARARGS, - PyDoc_STR("Register a timed callback")}, - {"register_value_change_callback", register_value_change_callback, METH_VARARGS, - PyDoc_STR("Register a signal change callback")}, - {"register_readonly_callback", register_readonly_callback, METH_VARARGS, - PyDoc_STR("Register a callback for the read-only section")}, - {"register_nextstep_callback", register_nextstep_callback, METH_VARARGS, - PyDoc_STR("Register a callback for the NextSimTime callback")}, - {"register_rwsynch_callback", register_rwsynch_callback, METH_VARARGS, - PyDoc_STR("Register a callback for the read-write section")}, - {"stop_simulator", stop_simulator, METH_VARARGS, - PyDoc_STR("Instruct the attached simulator to stop")}, - {"log_level", log_level, METH_VARARGS, - PyDoc_STR("Set the log level for GPI")}, + {"log_msg", log_msg, METH_VARARGS, PyDoc_STR( + "log_msg(name, path, funcname, lineno, msg, /)\n" + "--\n\n" + "Log a message" + )}, + {"get_root_handle", get_root_handle, METH_VARARGS, PyDoc_STR( + "get_root_handle(name, /)\n" + "--\n\n" + "Get the root handle" + )}, + {"register_timed_callback", register_timed_callback, METH_VARARGS, PyDoc_STR( + "register_timed_callback(time, func, /, *args)\n" + "--\n\n" + "Register a timed callback" + )}, + {"register_value_change_callback", register_value_change_callback, METH_VARARGS, PyDoc_STR( + "register_value_change_callback(signal, func, edge, /, *args)\n" + "--\n\n" + "Register a signal change callback" + )}, + {"register_readonly_callback", register_readonly_callback, METH_VARARGS, PyDoc_STR( + "register_readonly_callback(func, /, *args)\n" + "--\n\n" + "Register a callback for the read-only section" + )}, + {"register_nextstep_callback", register_nextstep_callback, METH_VARARGS, PyDoc_STR( + "register_nextstep_callback(func, /, *args)\n" + "--\n\n" + "Register a callback for the NextSimTime callback" + )}, + {"register_rwsynch_callback", register_rwsynch_callback, METH_VARARGS, PyDoc_STR( + "register_rwsynch_callback(func, /, *args)\n" + "--\n\n" + "Register a callback for the read-write section" + )}, + {"stop_simulator", stop_simulator, METH_VARARGS, PyDoc_STR( + "stop_simulator()\n" + "--\n\n" + "Instruct the attached simulator to stop" + )}, + {"log_level", log_level, METH_VARARGS, PyDoc_STR( + "log_level(level, /)\n" + "--\n\n" + "Set the log level for GPI" + )}, - {"get_sim_time", get_sim_time, METH_NOARGS, - PyDoc_STR("Get the current simulation time as an int tuple")}, - {"get_precision", get_precision, METH_NOARGS, - PyDoc_STR("Get the precision of the simulator")}, - {"get_simulator_product", get_simulator_product, METH_NOARGS, - PyDoc_STR("Simulator product information")}, - {"get_simulator_version", get_simulator_version, METH_NOARGS, - PyDoc_STR("Simulator product version information")}, + {"get_sim_time", get_sim_time, METH_NOARGS, PyDoc_STR( + "get_sim_time()\n" + "--\n\n" + "Get the current simulation time as an int tuple" + )}, + {"get_precision", get_precision, METH_NOARGS, PyDoc_STR( + "get_precision()\n" + "--\n\n" + "Get the precision of the simulator" + )}, + {"get_simulator_product", get_simulator_product, METH_NOARGS, PyDoc_STR( + "get_simulator_product()\n" + "--\n\n" + "Simulator product information" + )}, + {"get_simulator_version", get_simulator_version, METH_NOARGS, PyDoc_STR( + "get_simulator_version()\n" + "--\n\n" + "Simulator product version information" + )}, {NULL, NULL, 0, NULL} /* Sentinel */ }; From b54acce7e1879c49d18dd10ea4499b24a4a3b653 Mon Sep 17 00:00:00 2001 From: elgorwi Date: Sun, 17 May 2020 17:26:04 +1000 Subject: [PATCH 2297/2656] Issue #1829: Fix ImportError from cocotb import simulator --- cocotb/_os_compat.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cocotb/_os_compat.py b/cocotb/_os_compat.py index c43e482e..c7f55e8a 100644 --- a/cocotb/_os_compat.py +++ b/cocotb/_os_compat.py @@ -14,8 +14,6 @@ extra_dll_dir = os.path.join(os.path.dirname(__file__), 'libs') if sys.platform == 'win32' and os.path.isdir(extra_dll_dir): - if sys.version_info >= (3, 8): - os.add_dll_directory(extra_dll_dir) - else: - os.environ.setdefault('PATH', '') - os.environ['PATH'] += os.pathsep + extra_dll_dir + # TODO[gh-1829]: investigate making os.add_dll_directory work here on Python 3.8 + os.environ.setdefault('PATH', '') + os.environ['PATH'] += os.pathsep + extra_dll_dir From 7f21860af114d1ef0ccc64568d4979c29c23c0d6 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Mon, 18 May 2020 11:33:45 -0700 Subject: [PATCH 2298/2656] Fix #1519 newsfragment to correctly display `str`. --- documentation/source/newsfragments/1519.removal.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/newsfragments/1519.removal.rst b/documentation/source/newsfragments/1519.removal.rst index 5c7b0415..b0f7a4cb 100644 --- a/documentation/source/newsfragments/1519.removal.rst +++ b/documentation/source/newsfragments/1519.removal.rst @@ -1 +1 @@ -Passing :type:`str` instances to :func:`cocotb.utils.hexdump` and :func:`cocotb.utils.hexdiffs` is deprecated. :class:`bytes` objects should be passed instead. +Passing :class:`str` instances to :func:`cocotb.utils.hexdump` and :func:`cocotb.utils.hexdiffs` is deprecated. :class:`bytes` objects should be passed instead. From c238e5edde9349a1b02ed5797e08b3abe8ac344b Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 17 May 2020 12:03:14 -0500 Subject: [PATCH 2299/2656] Improve simulator docstrings --- .../share/lib/simulator/simulatormodule.cpp | 40 +++++++++---------- cocotb/share/lib/simulator/simulatormodule.h | 30 ++++++++------ 2 files changed, 37 insertions(+), 33 deletions(-) diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index 37c4fa8b..6ce4e5a9 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -926,133 +926,133 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_signal_val_long, METH_NOARGS, PyDoc_STR( "get_signal_val_long($self)\n" "--\n\n" - "Get the value of a signal as a long" + "Get the value of a signal as an integer." ) }, {"get_signal_val_str", (PyCFunction)get_signal_val_str, METH_NOARGS, PyDoc_STR( "get_signal_val_str($self)\n" "--\n\n" - "Get the value of a signal as an ASCII string" + "Get the value of a signal as a string." ) }, {"get_signal_val_binstr", (PyCFunction)get_signal_val_binstr, METH_NOARGS, PyDoc_STR( "get_signal_val_binstr($self)\n" "--\n\n" - "Get the value of a signal as a binary string" + "Get the value of a logic vector signal as a string of (``0``, ``1``, ``X``, etc.), one element per character." ) }, {"get_signal_val_real", (PyCFunction)get_signal_val_real, METH_NOARGS, PyDoc_STR( "get_signal_val_real($self)\n" "--\n\n" - "Get the value of a signal as a double precision float" + "Get the value of a signal as a float." ) }, {"set_signal_val_long", (PyCFunction)set_signal_val_long, METH_VARARGS, PyDoc_STR( "set_signal_val_long($self, action, value, /)\n" "--\n\n" - "Set the value of a signal using a long" + "Set the value of a signal using an integer.\n" ) }, {"set_signal_val_str", (PyCFunction)set_signal_val_str, METH_VARARGS, PyDoc_STR( "set_signal_val_str($self, action, value, /)\n" "--\n\n" - "Set the value of a signal using an NUL-terminated 8-bit string" + "Set the value of a signal using a user-encoded string." ) }, {"set_signal_val_binstr", (PyCFunction)set_signal_val_binstr, METH_VARARGS, PyDoc_STR( "set_signal_val_binstr($self, action, value, /)\n" "--\n\n" - "Set the value of a signal using a string with a character per bit" + "Set the value of a logic vector signal using a string of (``0``, ``1``, ``X``, etc.), one element per character." ) }, {"set_signal_val_real", (PyCFunction)set_signal_val_real, METH_VARARGS, PyDoc_STR( "set_signal_val_real($self, action, value, /)\n" "--\n\n" - "Set the value of a signal using a double precision float" + "Set the value of a signal using a float." ) }, {"get_definition_name", (PyCFunction)get_definition_name, METH_NOARGS, PyDoc_STR( "get_definition_name($self)\n" "--\n\n" - "Get the name of a GPI object's definition" + "Get the name of a GPI object's definition." ) }, {"get_definition_file", (PyCFunction)get_definition_file, METH_NOARGS, PyDoc_STR( "get_definition_file($self)\n" "--\n\n" - "Get the file that sources the object's definition" + "Get the file that sources the object's definition." ) }, {"get_handle_by_name", (PyCFunction)get_handle_by_name, METH_VARARGS, PyDoc_STR( "get_handle_by_name($self, name, /)\n" "--\n\n" - "Get handle of a named object" + "Get a handle to a child object by name." ) }, {"get_handle_by_index", (PyCFunction)get_handle_by_index, METH_VARARGS, PyDoc_STR( "get_handle_by_index($self, index, /)\n" "--\n\n" - "Get handle of a object at an index in a parent" + "Get a handle to a child object by index." ) }, {"get_name_string", (PyCFunction)get_name_string, METH_NOARGS, PyDoc_STR( "get_name_string($self)\n" "--\n\n" - "Get the name of an object as a string" + "Get the name of an object as a string." ) }, {"get_type_string", (PyCFunction)get_type_string, METH_NOARGS, PyDoc_STR( "get_type_string($self)\n" "--\n\n" - "Get the type of an object as a string" + "Get the GPI type of an object as a string." ) }, {"get_type", (PyCFunction)get_type, METH_NOARGS, PyDoc_STR( "get_type($self)\n" "--\n\n" - "Get the type of an object, mapped to a GPI enumeration" + "Get the GPI type of an object as an enum." ) }, {"get_const", (PyCFunction)get_const, METH_NOARGS, PyDoc_STR( "get_const($self)\n" "--\n\n" - "Get a flag indicating whether the object is a constant" + "Return ``True`` if the object is a constant." ) }, {"get_num_elems", (PyCFunction)get_num_elems, METH_NOARGS, PyDoc_STR( "get_num_elems($self)\n" "--\n\n" - "Get the number of elements contained in the handle" + "Get the number of elements contained in the handle." ) }, {"get_range", (PyCFunction)get_range, METH_NOARGS, PyDoc_STR( "get_range($self)\n" "--\n\n" - "Get the range of elements (tuple) contained in the handle, returns None if not indexable" + "Get the range of elements (tuple) contained in the handle, return ``None`` if not indexable." ) }, {"iterate", (PyCFunction)iterate, METH_VARARGS, PyDoc_STR( "iterate($self, mode, /)\n" "--\n\n" - "Get an iterator handle to loop over all members in an object" + "Get an iterator handle to loop over all members in an object." ) }, {NULL, NULL, 0, NULL} /* Sentinel */ @@ -1081,7 +1081,7 @@ static PyMethodDef gpi_cb_hdl_methods[] = { (PyCFunction)deregister, METH_NOARGS, PyDoc_STR( "deregister($self)\n" "--\n\n" - "De-register this callback" + "De-register this callback." )}, {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/cocotb/share/lib/simulator/simulatormodule.h b/cocotb/share/lib/simulator/simulatormodule.h index dd37b59a..94d2bcb0 100644 --- a/cocotb/share/lib/simulator/simulatormodule.h +++ b/cocotb/share/lib/simulator/simulatormodule.h @@ -73,68 +73,72 @@ static PyMethodDef SimulatorMethods[] = { {"log_msg", log_msg, METH_VARARGS, PyDoc_STR( "log_msg(name, path, funcname, lineno, msg, /)\n" "--\n\n" - "Log a message" + "Log a message." )}, {"get_root_handle", get_root_handle, METH_VARARGS, PyDoc_STR( "get_root_handle(name, /)\n" "--\n\n" - "Get the root handle" + "Get the root handle." )}, {"register_timed_callback", register_timed_callback, METH_VARARGS, PyDoc_STR( "register_timed_callback(time, func, /, *args)\n" "--\n\n" - "Register a timed callback" + "Register a timed callback." )}, {"register_value_change_callback", register_value_change_callback, METH_VARARGS, PyDoc_STR( "register_value_change_callback(signal, func, edge, /, *args)\n" "--\n\n" - "Register a signal change callback" + "Register a signal change callback." )}, {"register_readonly_callback", register_readonly_callback, METH_VARARGS, PyDoc_STR( "register_readonly_callback(func, /, *args)\n" "--\n\n" - "Register a callback for the read-only section" + "Register a callback for the read-only section." )}, {"register_nextstep_callback", register_nextstep_callback, METH_VARARGS, PyDoc_STR( "register_nextstep_callback(func, /, *args)\n" "--\n\n" - "Register a callback for the NextSimTime callback" + "Register a callback for the NextSimTime callback." )}, {"register_rwsynch_callback", register_rwsynch_callback, METH_VARARGS, PyDoc_STR( "register_rwsynch_callback(func, /, *args)\n" "--\n\n" - "Register a callback for the read-write section" + "Register a callback for the read-write section." )}, {"stop_simulator", stop_simulator, METH_VARARGS, PyDoc_STR( "stop_simulator()\n" "--\n\n" - "Instruct the attached simulator to stop" + "Instruct the attached simulator to stop. Users should not call this function." )}, {"log_level", log_level, METH_VARARGS, PyDoc_STR( "log_level(level, /)\n" "--\n\n" - "Set the log level for GPI" + "Set the log level for GPI." )}, {"get_sim_time", get_sim_time, METH_NOARGS, PyDoc_STR( "get_sim_time()\n" "--\n\n" - "Get the current simulation time as an int tuple" + "Get the current simulation time.\n" + "\n" + "Time is represented as a tuple of 32 bit integers ([low32, high32]) comprising a single 64 bit integer." )}, {"get_precision", get_precision, METH_NOARGS, PyDoc_STR( "get_precision()\n" "--\n\n" - "Get the precision of the simulator" + "Get the precision of the simulator in powers of 10.\n" + "\n" + "For example, if ``-12`` is returned, the simulator's time precision is 10**-12 or 1 ps." )}, {"get_simulator_product", get_simulator_product, METH_NOARGS, PyDoc_STR( "get_simulator_product()\n" "--\n\n" - "Simulator product information" + "Get the simulator's product string." )}, {"get_simulator_version", get_simulator_version, METH_NOARGS, PyDoc_STR( "get_simulator_version()\n" "--\n\n" - "Simulator product version information" + "Get the simulator's product version string." )}, {NULL, NULL, 0, NULL} /* Sentinel */ }; From 0709c3f308562346fabec72a3eaf500e67fdd885 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 17 May 2020 14:07:45 -0500 Subject: [PATCH 2300/2656] Add type annotations to simulator module --- .../share/lib/simulator/simulatormodule.cpp | 29 ++++++++++++++++++- cocotb/share/lib/simulator/simulatormodule.h | 20 +++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index 6ce4e5a9..c7562245 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -921,11 +921,19 @@ PyMODINIT_FUNC PyInit_simulator(void) return simulator; } +/* NOTE: in the following docstrings we are specifying the parameters twice, but this is necessary. + * The first docstring before the long '--' line specifies the __text_signature__ that is used + * by the help() function. And the second after the '--' line contains type annotations used by + * the `autodoc_docstring_signature` setting of sphinx.ext.autodoc for generating documentation + * because type annotations are not supported in __text_signature__. + */ + static PyMethodDef gpi_sim_hdl_methods[] = { {"get_signal_val_long", (PyCFunction)get_signal_val_long, METH_NOARGS, PyDoc_STR( "get_signal_val_long($self)\n" "--\n\n" + "get_signal_val_long(self) -> int\n" "Get the value of a signal as an integer." ) }, @@ -933,13 +941,15 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_signal_val_str, METH_NOARGS, PyDoc_STR( "get_signal_val_str($self)\n" "--\n\n" - "Get the value of a signal as a string." + "get_signal_val_str(self) -> bytes\n" + "Get the value of a signal as a byte string." ) }, {"get_signal_val_binstr", (PyCFunction)get_signal_val_binstr, METH_NOARGS, PyDoc_STR( "get_signal_val_binstr($self)\n" "--\n\n" + "get_signal_val_binstr(self) -> str\n" "Get the value of a logic vector signal as a string of (``0``, ``1``, ``X``, etc.), one element per character." ) }, @@ -947,6 +957,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_signal_val_real, METH_NOARGS, PyDoc_STR( "get_signal_val_real($self)\n" "--\n\n" + "get_signal_val_real(self) -> float\n" "Get the value of a signal as a float." ) }, @@ -954,6 +965,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)set_signal_val_long, METH_VARARGS, PyDoc_STR( "set_signal_val_long($self, action, value, /)\n" "--\n\n" + "set_signal_val_long(self, action: int, value: int) -> None\n" "Set the value of a signal using an integer.\n" ) }, @@ -961,6 +973,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)set_signal_val_str, METH_VARARGS, PyDoc_STR( "set_signal_val_str($self, action, value, /)\n" "--\n\n" + "set_signal_val_str(self, action: int, value: bytes) -> None\n" "Set the value of a signal using a user-encoded string." ) }, @@ -968,6 +981,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)set_signal_val_binstr, METH_VARARGS, PyDoc_STR( "set_signal_val_binstr($self, action, value, /)\n" "--\n\n" + "set_signal_val_binstr(self, action: int, value: str) -> None\n" "Set the value of a logic vector signal using a string of (``0``, ``1``, ``X``, etc.), one element per character." ) }, @@ -975,6 +989,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)set_signal_val_real, METH_VARARGS, PyDoc_STR( "set_signal_val_real($self, action, value, /)\n" "--\n\n" + "set_signal_val_real(self, action: int, value: float) -> None\n" "Set the value of a signal using a float." ) }, @@ -982,6 +997,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_definition_name, METH_NOARGS, PyDoc_STR( "get_definition_name($self)\n" "--\n\n" + "get_definition_name(self) -> str\n" "Get the name of a GPI object's definition." ) }, @@ -989,6 +1005,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_definition_file, METH_NOARGS, PyDoc_STR( "get_definition_file($self)\n" "--\n\n" + "get_definition_file(self) -> str\n" "Get the file that sources the object's definition." ) }, @@ -996,6 +1013,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_handle_by_name, METH_VARARGS, PyDoc_STR( "get_handle_by_name($self, name, /)\n" "--\n\n" + "get_handle_by_name(self, name: str) -> cocotb.simulator.gpi_sim_hdl\n" "Get a handle to a child object by name." ) }, @@ -1003,6 +1021,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_handle_by_index, METH_VARARGS, PyDoc_STR( "get_handle_by_index($self, index, /)\n" "--\n\n" + "get_handle_by_index(self, index: int) -> cocotb.simulator.gpi_sim_hdl\n" "Get a handle to a child object by index." ) }, @@ -1010,6 +1029,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_name_string, METH_NOARGS, PyDoc_STR( "get_name_string($self)\n" "--\n\n" + "get_name_string(self) -> str\n" "Get the name of an object as a string." ) }, @@ -1017,6 +1037,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_type_string, METH_NOARGS, PyDoc_STR( "get_type_string($self)\n" "--\n\n" + "get_type_string(self) -> str\n" "Get the GPI type of an object as a string." ) }, @@ -1024,6 +1045,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_type, METH_NOARGS, PyDoc_STR( "get_type($self)\n" "--\n\n" + "get_type(self) -> int\n" "Get the GPI type of an object as an enum." ) }, @@ -1031,6 +1053,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_const, METH_NOARGS, PyDoc_STR( "get_const($self)\n" "--\n\n" + "get_const(self) -> bool\n" "Return ``True`` if the object is a constant." ) }, @@ -1038,6 +1061,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_num_elems, METH_NOARGS, PyDoc_STR( "get_num_elems($self)\n" "--\n\n" + "get_num_elems(self) -> int\n" "Get the number of elements contained in the handle." ) }, @@ -1045,6 +1069,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_range, METH_NOARGS, PyDoc_STR( "get_range($self)\n" "--\n\n" + "get_range(self) -> Tuple[int, int]\n" "Get the range of elements (tuple) contained in the handle, return ``None`` if not indexable." ) }, @@ -1052,6 +1077,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)iterate, METH_VARARGS, PyDoc_STR( "iterate($self, mode, /)\n" "--\n\n" + "iterate(self, mode: int) -> cocotb.simulator.gpi_iterator_hdl\n" "Get an iterator handle to loop over all members in an object." ) }, @@ -1081,6 +1107,7 @@ static PyMethodDef gpi_cb_hdl_methods[] = { (PyCFunction)deregister, METH_NOARGS, PyDoc_STR( "deregister($self)\n" "--\n\n" + "deregister(self) -> None\n" "De-register this callback." )}, {NULL, NULL, 0, NULL} /* Sentinel */ diff --git a/cocotb/share/lib/simulator/simulatormodule.h b/cocotb/share/lib/simulator/simulatormodule.h index 94d2bcb0..3fe003b3 100644 --- a/cocotb/share/lib/simulator/simulatormodule.h +++ b/cocotb/share/lib/simulator/simulatormodule.h @@ -69,56 +69,73 @@ static PyObject *get_simulator_version(PyObject *self, PyObject *args); static PyObject *log_level(PyObject *self, PyObject *args); +/* NOTE: in the following docstrings we are specifying the parameters twice, but this is necessary. + * The first docstring before the long '--' line specifies the __text_signature__ that is used + * by the help() function. And the second after the '--' line contains type annotations used by + * the `autodoc_docstring_signature` setting of sphinx.ext.autodoc for generating documentation + * because type annotations are not supported in __text_signature__. + */ + static PyMethodDef SimulatorMethods[] = { {"log_msg", log_msg, METH_VARARGS, PyDoc_STR( "log_msg(name, path, funcname, lineno, msg, /)\n" "--\n\n" + "log_msg(name: str, path: str, funcname: str, lineno: int, msg: str) -> None\n" "Log a message." )}, {"get_root_handle", get_root_handle, METH_VARARGS, PyDoc_STR( "get_root_handle(name, /)\n" "--\n\n" + "get_root_handle(name: str) -> cocotb.simulator.gpi_sim_hdl\n" "Get the root handle." )}, {"register_timed_callback", register_timed_callback, METH_VARARGS, PyDoc_STR( "register_timed_callback(time, func, /, *args)\n" "--\n\n" + "register_timed_callback(time: int, func: Callable[..., None], *args: Any) -> cocotb.simulator.gpi_cb_hdl\n" "Register a timed callback." )}, {"register_value_change_callback", register_value_change_callback, METH_VARARGS, PyDoc_STR( "register_value_change_callback(signal, func, edge, /, *args)\n" "--\n\n" + "register_value_change_callback(signal: cocotb.simulator.gpi_sim_hdl, func: Callable[..., None], edge: int, *args: Any) -> cocotb.simulator.gpi_cb_hdl\n" "Register a signal change callback." )}, {"register_readonly_callback", register_readonly_callback, METH_VARARGS, PyDoc_STR( "register_readonly_callback(func, /, *args)\n" "--\n\n" + "register_readonly_callback(func: Callable[..., None], *args: Any) -> cocotb.simulator.gpi_cb_hdl\n" "Register a callback for the read-only section." )}, {"register_nextstep_callback", register_nextstep_callback, METH_VARARGS, PyDoc_STR( "register_nextstep_callback(func, /, *args)\n" "--\n\n" + "register_nextstep_callback(func: Callable[..., None], *args: Any) -> cocotb.simulator.gpi_cb_hdl\n" "Register a callback for the NextSimTime callback." )}, {"register_rwsynch_callback", register_rwsynch_callback, METH_VARARGS, PyDoc_STR( "register_rwsynch_callback(func, /, *args)\n" "--\n\n" + "register_rwsynch_callback(func: Callable[..., None], *args: Any) -> cocotb.simulator.gpi_cb_hdl\n" "Register a callback for the read-write section." )}, {"stop_simulator", stop_simulator, METH_VARARGS, PyDoc_STR( "stop_simulator()\n" "--\n\n" + "stop_simulator() -> None\n" "Instruct the attached simulator to stop. Users should not call this function." )}, {"log_level", log_level, METH_VARARGS, PyDoc_STR( "log_level(level, /)\n" "--\n\n" + "log_level(level: int) -> None\n" "Set the log level for GPI." )}, {"get_sim_time", get_sim_time, METH_NOARGS, PyDoc_STR( "get_sim_time()\n" "--\n\n" + "get_sim_time() -> Tuple[int, int]\n" "Get the current simulation time.\n" "\n" "Time is represented as a tuple of 32 bit integers ([low32, high32]) comprising a single 64 bit integer." @@ -126,6 +143,7 @@ static PyMethodDef SimulatorMethods[] = { {"get_precision", get_precision, METH_NOARGS, PyDoc_STR( "get_precision()\n" "--\n\n" + "get_precision() -> int\n" "Get the precision of the simulator in powers of 10.\n" "\n" "For example, if ``-12`` is returned, the simulator's time precision is 10**-12 or 1 ps." @@ -133,11 +151,13 @@ static PyMethodDef SimulatorMethods[] = { {"get_simulator_product", get_simulator_product, METH_NOARGS, PyDoc_STR( "get_simulator_product()\n" "--\n\n" + "get_simulator_product() -> str\n" "Get the simulator's product string." )}, {"get_simulator_version", get_simulator_version, METH_NOARGS, PyDoc_STR( "get_simulator_version()\n" "--\n\n" + "get_simulator_version() -> str\n" "Get the simulator's product version string." )}, {NULL, NULL, 0, NULL} /* Sentinel */ From e4b316b3f2d25d471ac294041f0051a3f8363e9f Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 17 May 2020 14:09:02 -0500 Subject: [PATCH 2301/2656] Add docstrings to gpi_hdl_Objects in simulator module --- cocotb/share/lib/simulator/simulatormodule.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index c7562245..c841a238 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -1089,6 +1089,9 @@ template<> PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { auto type = fill_common_slots(); type.tp_name = "cocotb.simulator.gpi_sim_hdl"; + type.tp_doc = "GPI object handle\n" + "\n" + "Contains methods for getting and setting the value of a GPI object, and introspection."; type.tp_methods = gpi_sim_hdl_methods; return type; }(); @@ -1097,6 +1100,7 @@ template<> PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { auto type = fill_common_slots(); type.tp_name = "cocotb.simulator.gpi_iterator_hdl"; + type.tp_doc = "GPI iterator handle."; type.tp_iter = PyObject_SelfIter; type.tp_iternext = (iternextfunc)next; return type; @@ -1117,6 +1121,7 @@ template<> PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { auto type = fill_common_slots(); type.tp_name = "cocotb.simulator.gpi_cb_hdl"; + type.tp_doc = "GPI callback handle"; type.tp_methods = gpi_cb_hdl_methods; return type; }(); From d5d3f78e8cd39886719ec59c0d28f76a3e88bf63 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 17 May 2020 16:29:19 -0500 Subject: [PATCH 2302/2656] Update documentation makefile Adds a target to rebuild the cocotb package. This is useful when updating documentation of C extensions. Also uses `.` to source shell files which is more portable and fixes a bug. --- documentation/Makefile | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/documentation/Makefile b/documentation/Makefile index 41f75dce..c4bdbd51 100644 --- a/documentation/Makefile +++ b/documentation/Makefile @@ -15,6 +15,12 @@ BUILDDIR = build # Put it first so that "make" without argument is like "make help". help: .venv @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + @echo "Other targets" + @echo " .venv Create virtual environment used to generate documentation" + @echo " rebuild_cocotb Rebuilds the cocotb package and installs in the virtual environment" + @echo " clean Deletes Sphinx-build artifacts" + @echo " distclean Deletes the virtual environment and Sphinx-build artifacts" + @echo " help Prints this help message" .venv: requirements.txt @echo Creating Python venv for Sphinx build @@ -23,6 +29,10 @@ help: .venv .venv/bin/pip -q install -r requirements.txt .venv/bin/pip -q install -e .. +.PHONY: rebuild_cocotb +rebuild_cocotb: + .venv/bin/pip -q install -e .. + .PHONY: clean clean: .venv Makefile @$(SPHINXBUILD) -M clean "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) @@ -41,4 +51,4 @@ distclean: # We hack around that by calling the target explicitly. make .venv - source .venv/bin/activate; $(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + . .venv/bin/activate; $(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) From 5937a57486eb6ccfc2a062d67e80238ee04f7e39 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Thu, 14 May 2020 10:41:04 +0100 Subject: [PATCH 2303/2656] Editorial fixes to CONTRIBUTING --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fdf86096..88bc299b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,7 +43,8 @@ Follow the steps below to get your changes merged, i.e. integrated into the main If any of them turns "red," i.e. reports a failure, you most likely need to fix your code before it can be merged. 7. The pull request needs to be reviewed by at least one maintainer. We aim to give feedback to all pull requests within a week, but as so often, life can get in the way. - If you receive no feedback from a maintainer within that time, please contact him/her directly (e.g. on [Gitter](https://gitter.im/cocotb) or email). You can find a [list of all maintainers](#maintainers) and their main area of expertise [below](#maintainers). + If you receive no feedback from a maintainer within that time, please contact them directly (e.g. on [Gitter](https://gitter.im/cocotb) or email). + You can find a [list of all maintainers](#maintainers) below. If a maintainer asks you to explain or modify code, try to do so. 8. Once your code has at least one positive review from a maintainer and no maintainer strongly objects it your code is ready to be merged into the `master` branch. @@ -127,7 +128,6 @@ cocotb aims to keep the `master` branch always in a releasable state. At least four times a year an official release should be created. It is the job of the maintainers to find a suitable time for a release, to communicate it to the community, and to coordinate it. - Maintainers ----------- From f44ca68d5cf1d6be2019bc926694863bee8a69dc Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Thu, 14 May 2020 10:44:13 +0100 Subject: [PATCH 2304/2656] Update maintainers section in CONTRIBUTING We want the maintainers section to accurately reflect who's doing reviews and merges on a day-to-day basis. This helps new contributors to reach out to maintainers if needed. Luke and Julius have not been active for quite a while, remove them from the list for now. Stu and Chris have been moved to a "founders" list, which better reflects their status within the project today. --- CONTRIBUTING.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 88bc299b..ed88f027 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -135,17 +135,16 @@ Cocotb uses a shared maintainer model. Most maintainers are experts in part of the cocotb codebase, and are primarily responsible for reviews in this area. - Kaleb Barrett (@ktbarrett) -- Julius Baxter (@juliusbaxter) -- Luke Darnell (@lukedarnell) - Tomasz Hemperek (@themperek) -- Chris Higgs (@chiggs). - Founder of cocotb. -- Stuart Hodgson (@stuarthodgson). - Founder of cocotb. - Colin Marquardt (@cmarqu) - Philipp Wagner (@imphil) - Eric Wieser (@eric-wieser) +Founders + +- Chris Higgs (@chiggs) +- Stuart Hodgson (@stuarthodgson) + Code of Conduct --------------- From eec741be8e0a2145d7e09d780478e2c250a645c7 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 22 May 2020 00:07:25 -0500 Subject: [PATCH 2305/2656] Fix type annotations on simulatormodule --- .../share/lib/simulator/simulatormodule.cpp | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index c841a238..2662a96a 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -933,7 +933,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_signal_val_long, METH_NOARGS, PyDoc_STR( "get_signal_val_long($self)\n" "--\n\n" - "get_signal_val_long(self) -> int\n" + "get_signal_val_long() -> int\n" "Get the value of a signal as an integer." ) }, @@ -941,7 +941,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_signal_val_str, METH_NOARGS, PyDoc_STR( "get_signal_val_str($self)\n" "--\n\n" - "get_signal_val_str(self) -> bytes\n" + "get_signal_val_str() -> bytes\n" "Get the value of a signal as a byte string." ) }, @@ -949,7 +949,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_signal_val_binstr, METH_NOARGS, PyDoc_STR( "get_signal_val_binstr($self)\n" "--\n\n" - "get_signal_val_binstr(self) -> str\n" + "get_signal_val_binstr() -> str\n" "Get the value of a logic vector signal as a string of (``0``, ``1``, ``X``, etc.), one element per character." ) }, @@ -957,7 +957,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_signal_val_real, METH_NOARGS, PyDoc_STR( "get_signal_val_real($self)\n" "--\n\n" - "get_signal_val_real(self) -> float\n" + "get_signal_val_real() -> float\n" "Get the value of a signal as a float." ) }, @@ -965,7 +965,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)set_signal_val_long, METH_VARARGS, PyDoc_STR( "set_signal_val_long($self, action, value, /)\n" "--\n\n" - "set_signal_val_long(self, action: int, value: int) -> None\n" + "set_signal_val_long(action: int, value: int) -> None\n" "Set the value of a signal using an integer.\n" ) }, @@ -973,7 +973,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)set_signal_val_str, METH_VARARGS, PyDoc_STR( "set_signal_val_str($self, action, value, /)\n" "--\n\n" - "set_signal_val_str(self, action: int, value: bytes) -> None\n" + "set_signal_val_str(action: int, value: bytes) -> None\n" "Set the value of a signal using a user-encoded string." ) }, @@ -981,7 +981,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)set_signal_val_binstr, METH_VARARGS, PyDoc_STR( "set_signal_val_binstr($self, action, value, /)\n" "--\n\n" - "set_signal_val_binstr(self, action: int, value: str) -> None\n" + "set_signal_val_binstr(action: int, value: str) -> None\n" "Set the value of a logic vector signal using a string of (``0``, ``1``, ``X``, etc.), one element per character." ) }, @@ -989,7 +989,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)set_signal_val_real, METH_VARARGS, PyDoc_STR( "set_signal_val_real($self, action, value, /)\n" "--\n\n" - "set_signal_val_real(self, action: int, value: float) -> None\n" + "set_signal_val_real(action: int, value: float) -> None\n" "Set the value of a signal using a float." ) }, @@ -997,7 +997,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_definition_name, METH_NOARGS, PyDoc_STR( "get_definition_name($self)\n" "--\n\n" - "get_definition_name(self) -> str\n" + "get_definition_name() -> str\n" "Get the name of a GPI object's definition." ) }, @@ -1005,7 +1005,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_definition_file, METH_NOARGS, PyDoc_STR( "get_definition_file($self)\n" "--\n\n" - "get_definition_file(self) -> str\n" + "get_definition_file() -> str\n" "Get the file that sources the object's definition." ) }, @@ -1013,7 +1013,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_handle_by_name, METH_VARARGS, PyDoc_STR( "get_handle_by_name($self, name, /)\n" "--\n\n" - "get_handle_by_name(self, name: str) -> cocotb.simulator.gpi_sim_hdl\n" + "get_handle_by_name(name: str) -> cocotb.simulator.gpi_sim_hdl\n" "Get a handle to a child object by name." ) }, @@ -1021,7 +1021,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_handle_by_index, METH_VARARGS, PyDoc_STR( "get_handle_by_index($self, index, /)\n" "--\n\n" - "get_handle_by_index(self, index: int) -> cocotb.simulator.gpi_sim_hdl\n" + "get_handle_by_index(index: int) -> cocotb.simulator.gpi_sim_hdl\n" "Get a handle to a child object by index." ) }, @@ -1029,7 +1029,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_name_string, METH_NOARGS, PyDoc_STR( "get_name_string($self)\n" "--\n\n" - "get_name_string(self) -> str\n" + "get_name_string() -> str\n" "Get the name of an object as a string." ) }, @@ -1037,7 +1037,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_type_string, METH_NOARGS, PyDoc_STR( "get_type_string($self)\n" "--\n\n" - "get_type_string(self) -> str\n" + "get_type_string() -> str\n" "Get the GPI type of an object as a string." ) }, @@ -1045,7 +1045,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_type, METH_NOARGS, PyDoc_STR( "get_type($self)\n" "--\n\n" - "get_type(self) -> int\n" + "get_type() -> int\n" "Get the GPI type of an object as an enum." ) }, @@ -1053,7 +1053,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_const, METH_NOARGS, PyDoc_STR( "get_const($self)\n" "--\n\n" - "get_const(self) -> bool\n" + "get_const() -> bool\n" "Return ``True`` if the object is a constant." ) }, @@ -1061,7 +1061,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_num_elems, METH_NOARGS, PyDoc_STR( "get_num_elems($self)\n" "--\n\n" - "get_num_elems(self) -> int\n" + "get_num_elems() -> int\n" "Get the number of elements contained in the handle." ) }, @@ -1069,7 +1069,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)get_range, METH_NOARGS, PyDoc_STR( "get_range($self)\n" "--\n\n" - "get_range(self) -> Tuple[int, int]\n" + "get_range() -> Tuple[int, int]\n" "Get the range of elements (tuple) contained in the handle, return ``None`` if not indexable." ) }, @@ -1077,7 +1077,7 @@ static PyMethodDef gpi_sim_hdl_methods[] = { (PyCFunction)iterate, METH_VARARGS, PyDoc_STR( "iterate($self, mode, /)\n" "--\n\n" - "iterate(self, mode: int) -> cocotb.simulator.gpi_iterator_hdl\n" + "iterate(mode: int) -> cocotb.simulator.gpi_iterator_hdl\n" "Get an iterator handle to loop over all members in an object." ) }, @@ -1111,7 +1111,7 @@ static PyMethodDef gpi_cb_hdl_methods[] = { (PyCFunction)deregister, METH_NOARGS, PyDoc_STR( "deregister($self)\n" "--\n\n" - "deregister(self) -> None\n" + "deregister() -> None\n" "De-register this callback." )}, {NULL, NULL, 0, NULL} /* Sentinel */ From 697f23573eb4f28c75cdc0527fff34dccbd29ba1 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 20 May 2020 08:33:08 -0500 Subject: [PATCH 2306/2656] Add RegressionManager to documentation --- documentation/source/library_reference.rst | 24 +++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/documentation/source/library_reference.rst b/documentation/source/library_reference.rst index 1a1080a1..0fad77af 100644 --- a/documentation/source/library_reference.rst +++ b/documentation/source/library_reference.rst @@ -326,17 +326,16 @@ Signal Tracer for WaveDrom :synopsis: A signal tracer for WaveDrom. -Developer-focused -================= +Implementation Details +====================== + +.. note:: + In general, nothing in this section should be interacted with directly - + these components work mostly behind the scenes. The Scheduler ------------- -.. note:: - The scheduler object should generally not be interacted with directly - - the only part of it that a user will need is encapsulated in :func:`~cocotb.fork`, - everything else works behind the scenes. - .. currentmodule:: cocotb.scheduler .. autodata:: cocotb.scheduler @@ -345,6 +344,17 @@ The Scheduler :members: :member-order: bysource +The Regression Manager +---------------------- + +.. currentmodule:: cocotb.regression + +.. autodata:: cocotb.regression_manager + +.. autoclass:: RegressionManager + :members: + :member-order: bysource + The ``cocotb.simulator`` module ------------------------------- From 2fbe68efb6c7d13de05e473646f0c912f02234b2 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 20 May 2020 08:59:13 -0500 Subject: [PATCH 2307/2656] Document module-level variables --- cocotb/__init__.py | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 435413cf..81380525 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -37,6 +37,7 @@ import random import time import warnings +from typing import Dict, List, Union import cocotb._os_compat # must appear first, before the first import of cocotb.simulator import cocotb.handle @@ -95,13 +96,35 @@ def _reopen_stream_with_buffering(stream_name): # so that cocotb.scheduler gives you the singleton instance and not the # scheduler package -scheduler = None +scheduler = None # type: cocotb.scheduler.Scheduler """The global scheduler instance.""" -regression_manager = None +regression_manager = None # type: cocotb.regression.RegressionManager +"""The global regression manager instance.""" -plusargs = {} -"""A dictionary of "plusargs" handed to the simulation.""" +argv = None # type: List[str] +"""The argument list as seen by the simulator""" + +argc = None # type: int +"""The length of :data:`cocotb.argv`""" + +plusargs = None # type: Dict[str, Union[bool, str]] +"""A dictionary of "plusargs" handed to the simulation. See :make:var:`PLUSARGS` for details.""" + +LANGUAGE = os.getenv("TOPLEVEL_LANG") # type: str +"""The value of :make:var:`TOPLEVEL_LANG`""" + +SIM_NAME = None # type: str +"""The running simulator product information. ``None`` if :mod:`cocotb` was not loaded from a simulator""" + +SIM_VERSION = None # type: str +"""The version of the running simulator. ``None`` if :mod:`cocotb` was not loaded from a simulator""" + +RANDOM_SEED = None # type: int +""" +The value passed to the Python default random number generator. +See :envvar:`RANDOM_SEED` for details on how the value is computed. +""" def fork(coro): @@ -112,8 +135,6 @@ def fork(coro): # FIXME is this really required? _rlock = threading.RLock() -LANGUAGE = os.getenv("TOPLEVEL_LANG") - def mem_debug(port): import cocotb.memdebug From 141bb364247ca24c50edaff93bbe520997407271 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 20 May 2020 09:02:27 -0500 Subject: [PATCH 2308/2656] Add cocotb module variables to documentation --- documentation/source/library_reference.rst | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/documentation/source/library_reference.rst b/documentation/source/library_reference.rst index 0fad77af..96360e9e 100644 --- a/documentation/source/library_reference.rst +++ b/documentation/source/library_reference.rst @@ -141,8 +141,6 @@ Clock Utilities ========= -.. autodata:: cocotb.plusargs - .. automodule:: cocotb.utils :members: :member-order: bysource @@ -317,6 +315,22 @@ XGMII Miscellaneous ============= +Other Runtime Information +------------------------- + +.. autodata:: cocotb.argv + +.. autodata:: cocotb.SIM_NAME + +.. autodata:: cocotb.SIM_VERSION + +.. autodata:: cocotb.RANDOM_SEED + +.. autodata:: cocotb.plusargs + +.. autodata:: cocotb.LANGUAGE + + Signal Tracer for WaveDrom -------------------------- From a253ee28df6f003421eedda71133b30de5c49033 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 20 May 2020 09:01:38 -0500 Subject: [PATCH 2309/2656] Fix broken link to PLUSARGS in RANDOM_SEED doc --- documentation/source/building.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/building.rst b/documentation/source/building.rst index 958d4329..28e7e48c 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -61,7 +61,7 @@ Cocotb make RANDOM_SEED=1377424946 - See also: :envvar:`PLUSARGS` + See also: :make:var:`PLUSARGS` .. envvar:: COCOTB_ANSI_OUTPUT From 785bfc56c39281e91db2061beb8f969cdbc73b9f Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sat, 2 May 2020 23:32:31 +0200 Subject: [PATCH 2310/2656] Move non-make variable help text to cocotb-config --- cocotb/config.py | 49 ++++++++++++++++++++++++ cocotb/share/makefiles/Makefile.sim | 58 ++++++++--------------------- documentation/source/building.rst | 2 +- 3 files changed, 66 insertions(+), 43 deletions(-) diff --git a/cocotb/config.py b/cocotb/config.py index 033a9924..d495095d 100755 --- a/cocotb/config.py +++ b/cocotb/config.py @@ -41,6 +41,7 @@ import argparse import os import sys +import textwrap import cocotb __all__ = ["share_dir", "makefiles_dir"] @@ -50,6 +51,48 @@ makefiles_dir = os.path.join(os.path.dirname(cocotb.__file__), "share", "makefiles") +def help_vars_text(): + if "dev" in cocotb.__version__: + doclink = "https://docs.cocotb.org/en/latest/building.html" + else: + doclink = "https://docs.cocotb.org/en/v{}/building.html".format(cocotb.__version__) + + # NOTE: make sure to keep "helpmsg" aligned with documentation/source/building.rst + # Also keep it at 80 chars. + helpmsg = textwrap.dedent("""\ + Cocotb + ------ + TOPLEVEL Instance in the hierarchy to use as the DUT + RANDOM_SEED Random seed, to recreate a previous test stimulus + COCOTB_ANSI_OUTPUT Force cocotb to print or not print in color + COCOTB_REDUCED_LOG_FMT Display log lines shorter + COCOTB_ATTACH Pause time value in seconds before the simulator start + COCOTB_ENABLE_PROFILING Performance analysis of the Python portion of cocotb + COCOTB_LOG_LEVEL Default logging level (default INFO) + COCOTB_RESOLVE_X How to resolve X, Z, U, W on integer conversion + MEMCHECK HTTP port to use for debugging Python memory usage + + Regression Manager + ------------------ + COCOTB_PDB_ON_EXCEPTION Drop into the Python debugger (pdb) on exception + MODULE Modules to search for test functions (comma-separated) + TESTCASE Test function(s) to run (comma-separated list) + COCOTB_RESULTS_FILE File name for xUnit XML tests results + COVERAGE Report Python coverage (also HDL for some simulators) + COCOTB_HOOKS Comma-separated module list to be executed before test + + GPI + --- + GPI_EXTRA Extra libraries to load at runtime (comma-separated) + + Scheduler + --------- + COCOTB_SCHEDULER_DEBUG Enable additional output of coroutine scheduler + + For details, see {}""").format(doclink) + return helpmsg + + class PrintAction(argparse.Action): def __init__(self, option_strings, dest, text=None, **kwargs): super(PrintAction, self).__init__(option_strings, dest, nargs=0, **kwargs) @@ -90,6 +133,12 @@ def get_parser(): action=PrintAction, text=python_bin, ) + parser.add_argument( + "--help-vars", + help="show help about supported variables", + action=PrintAction, + text=help_vars_text(), + ) parser.add_argument( "-v", "--version", diff --git a/cocotb/share/makefiles/Makefile.sim b/cocotb/share/makefiles/Makefile.sim index 3dd713a9..8bf27023 100644 --- a/cocotb/share/makefiles/Makefile.sim +++ b/cocotb/share/makefiles/Makefile.sim @@ -32,45 +32,17 @@ .PHONY: all all: sim -# NOTE: make sure to keep "helpmsg" aligned with documentation/source/building.rst -# Also keep it at 80 chars. -define helpmsg = +# NOTE: keep this at 80 chars. +define help_targets = Targets -------- +======= sim Unconditionally re-run the simulator (default) regression Run simulator when dependencies have changes clean Remove build and simulation artefacts help This help text -GPI ---- -GPI_EXTRA Extra libraries to load at runtime (comma-separated) - -Cocotb ------- -TOPLEVEL Instance in the hierarchy to use as the DUT -RANDOM_SEED Random seed, to recreate a previous test stimulus -COCOTB_ANSI_OUTPUT Force cocotb to print or not print in color -COCOTB_REDUCED_LOG_FMT Display log lines shorter -COCOTB_ATTACH Pause time value in seconds before the simulator start -COCOTB_ENABLE_PROFILING Performance analysis of the Python portion of cocotb -COCOTB_LOG_LEVEL Default logging level (default INFO) -COCOTB_RESOLVE_X How to resolve X, Z, U, W on integer conversion -MEMCHECK HTTP port to use for debugging Python memory usage - -Regression Manager ------------------- -COCOTB_PDB_ON_EXCEPTION Drop into the Python debugger (pdb) on exception -MODULE Modules to search for test functions (comma-separated) -TESTCASE Test function(s) to run (comma-separated list) -COCOTB_RESULTS_FILE File name for xUnit XML tests results -COVERAGE Report Python coverage (also HDL for some simulators) -COCOTB_HOOKS Comma-separated module list to be executed before test - -Scheduler ---------- -COCOTB_SCHEDULER_DEBUG Enable additional output of coroutine scheduler - +Variables +========= Makefile-based Test Scripts --------------------------- GUI Set this to 1 to enable the GUI mode in the simulator @@ -90,20 +62,22 @@ CUSTOM_SIM_DEPS Add additional dependencies to the simulation target SIM_BUILD Define a scratch directory for use by the simulator SCRIPT_FILE Simulator script to run (for e.g. wave traces) - For details, see $(DOCLINK) +endef + + +# NOTE: keep *two* empty lines between "define" and "endef": +define newline + endef # this cannot be a regular target because of the way Makefile.$(SIM) is included ifeq ($(MAKECMDGOALS),help) - _VERSION := $(shell cocotb-config -v) - # for non-intuitive use of ifneq see https://www.gnu.org/software/make/manual/make.html#Testing-Flags - ifneq (,$(findstring dev,$(_VERSION))) - DOCLINK := https://docs.cocotb.org/en/latest/building.html - else - DOCLINK := https://docs.cocotb.org/en/v$(_VERSION)/building.html - endif - $(info $(helpmsg)) + $(info $(help_targets)) + # hack to get newlines in output, see https://stackoverflow.com/a/54539610 + # NOTE: the output of the command must not include a '%' sign, otherwise the formatting will break + help_vars := $(subst %,${newline},$(shell cocotb-config --help-vars | tr \\n %)) + $(info ${help_vars}) # is there a cleaner way to exit here? $(error "Stopping after printing help") endif diff --git a/documentation/source/building.rst b/documentation/source/building.rst index 28e7e48c..1a8bc99d 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -35,7 +35,7 @@ Variables The following sections document makefile variables and environment variables according to their owner/consumer. .. - If you edit the following sections, please also update the "helpmsg" text in cocotb/share/makefiles/Makefile.sim + If you edit the following sections, please also update the "helpmsg" text in cocotb/config.py Cocotb ------ From 1645804cf6c232001759c74769023054630bd461 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 23 May 2020 22:03:19 +0100 Subject: [PATCH 2311/2656] drivers: Move type information from docstrings to annotations (#1849) Only the functions which had typesin their docstrings are touched here. Some instances of `str` were corrected to `bytes`, since those were my fault in a previous PR. Everything else is left as in the docstring. --- cocotb/drivers/__init__.py | 63 ++++++++++++++++++++++---------------- cocotb/drivers/amba.py | 28 +++++++++-------- cocotb/drivers/avalon.py | 31 ++++++++++--------- cocotb/drivers/opb.py | 17 +++++----- cocotb/drivers/xgmii.py | 29 +++++++++--------- 5 files changed, 92 insertions(+), 76 deletions(-) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index d9fd84c5..e974cfa0 100755 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -30,6 +30,7 @@ """Set of common driver base classes.""" from collections import deque +from typing import Iterable, Tuple, Any, Optional, Callable import cocotb from cocotb.decorators import coroutine @@ -37,6 +38,7 @@ Edge) from cocotb.bus import Bus from cocotb.log import SimLog +from cocotb.handle import SimHandleBase class BitDriver: @@ -50,11 +52,11 @@ def __init__(self, signal, clk, generator=None): self._clk = clk self._generator = generator - def start(self, generator=None): + def start(self, generator: Iterable[Tuple[int, int]] = None) -> None: """Start generating data. Args: - generator (generator, optional): Generator yielding data. + generator: Generator yielding data. The generator should yield tuples ``(on, off)`` with the number of cycles to be on, followed by the number of cycles to be off. @@ -115,17 +117,20 @@ def kill(self): self._thread.kill() self._thread = None - def append(self, transaction, callback=None, event=None, **kwargs): + def append( + self, transaction: Any, callback: Callable[[Any], Any] = None, + event: Event = None, **kwargs: Any + ) -> None: """Queue up a transaction to be sent over the bus. Mechanisms are provided to permit the caller to know when the transaction is processed. Args: - transaction (any): The transaction to be sent. - callback (callable, optional): Optional function to be called + transaction: The transaction to be sent. + callback: Optional function to be called when the transaction has been sent. - event (optional): :class:`~cocotb.triggers.Event` to be set + event: :class:`~cocotb.triggers.Event` to be set when the transaction has been sent. **kwargs: Any additional arguments used in child class' :any:`_driver_send` method. @@ -138,43 +143,46 @@ def clear(self): self._sendQ = deque() @coroutine - def send(self, transaction, sync=True, **kwargs): + def send(self, transaction: Any, sync: bool = True, **kwargs: Any) -> None: """Blocking send call (hence must be "yielded" rather than called). Sends the transaction over the bus. Args: - transaction (any): The transaction to be sent. - sync (bool, optional): Synchronize the transfer by waiting for a rising edge. - **kwargs (dict): Additional arguments used in child class' + transaction: The transaction to be sent. + sync: Synchronize the transfer by waiting for a rising edge. + **kwargs: Additional arguments used in child class' :any:`_driver_send` method. """ yield self._send(transaction, None, None, sync=sync, **kwargs) - def _driver_send(self, transaction, sync=True, **kwargs): + def _driver_send(self, transaction: Any, sync: bool = True, **kwargs: Any) -> None: """Actual implementation of the send. Sub-classes should override this method to implement the actual :meth:`~cocotb.drivers.Driver.send` routine. Args: - transaction (any): The transaction to be sent. - sync (bool, optional): Synchronize the transfer by waiting for a rising edge. + transaction: The transaction to be sent. + sync: Synchronize the transfer by waiting for a rising edge. **kwargs: Additional arguments if required for protocol implemented in a sub-class. """ raise NotImplementedError("Sub-classes of Driver should define a " "_driver_send coroutine") @coroutine - def _send(self, transaction, callback, event, sync=True, **kwargs): + def _send( + self, transaction: Any, callback: Callable[[Any], Any], event: Event, + sync: bool = True, **kwargs + ) -> None: """Send coroutine. Args: - transaction (any): The transaction to be sent. - callback (callable, optional): Optional function to be called + transaction: The transaction to be sent. + callback: Optional function to be called when the transaction has been sent. - event (optional): event to be set when the transaction has been sent. - sync (bool, optional): Synchronize the transfer by waiting for a rising edge. + event: event to be set when the transaction has been sent. + sync: Synchronize the transfer by waiting for a rising edge. **kwargs: Any additional arguments used in child class' :any:`_driver_send` method. """ @@ -217,19 +225,19 @@ class BusDriver(Driver): * an entity Args: - entity (SimHandle): A handle to the simulator entity. - name (str or None): Name of this bus. ``None`` for a nameless bus, e.g. + entity: A handle to the simulator entity. + name: Name of this bus. ``None`` for a nameless bus, e.g. bus-signals in an interface or a ``modport``. (untested on ``struct``/``record``, but could work here as well). - clock (SimHandle): A handle to the clock associated with this bus. - **kwargs (dict): Keyword arguments forwarded to :class:`cocotb.Bus`, + clock: A handle to the clock associated with this bus. + **kwargs: Keyword arguments forwarded to :class:`cocotb.Bus`, see docs for that class for more information. """ _optional_signals = [] - def __init__(self, entity, name, clock, **kwargs): + def __init__(self, entity: SimHandleBase, name: Optional[str], clock: SimHandleBase, **kwargs: Any): index = kwargs.get("array_idx", None) self.log = SimLog("cocotb.%s.%s" % (entity._name, name)) @@ -245,12 +253,12 @@ def __init__(self, entity, name, clock, **kwargs): self.name = name if index is None else "%s_%d" % (name, index) @coroutine - def _driver_send(self, transaction, sync=True): + def _driver_send(self, transaction, sync: bool = True) -> None: """Implementation for BusDriver. Args: transaction: The transaction to send. - sync (bool, optional): Synchronize the transfer by waiting for a rising edge. + sync: Synchronize the transfer by waiting for a rising edge. """ if sync: yield RisingEdge(self.clock) @@ -301,7 +309,10 @@ class ValidatedBusDriver(BusDriver): ``(valid, invalid)`` cycles to insert. """ - def __init__(self, entity, name, clock, *, valid_generator=None, **kwargs): + def __init__( + self, entity: SimHandleBase, name: str, clock: SimHandleBase, *, + valid_generator: Iterable[Tuple[int, int]] = None, **kwargs: Any + ) -> None: BusDriver.__init__(self, entity, name, clock, **kwargs) self.set_valid_generator(valid_generator=valid_generator) diff --git a/cocotb/drivers/amba.py b/cocotb/drivers/amba.py index a1419fe4..2117ba8e 100644 --- a/cocotb/drivers/amba.py +++ b/cocotb/drivers/amba.py @@ -107,24 +107,26 @@ def _send_write_data(self, data, delay=0, byte_enable=0xF): self.write_data_busy.release() @cocotb.coroutine - def write(self, address, value, byte_enable=0xf, address_latency=0, - data_latency=0, sync=True): + def write( + self, address: int, value: int, byte_enable: int = 0xf, + address_latency: int = 0, data_latency: int = 0, sync: bool = True + ) -> BinaryValue: """Write a value to an address. Args: - address (int): The address to write to. - value (int): The data value to write. - byte_enable (int, optional): Which bytes in value to actually write. + address: The address to write to. + value: The data value to write. + byte_enable: Which bytes in value to actually write. Default is to write all bytes. - address_latency (int, optional): Delay before setting the address (in clock cycles). + address_latency: Delay before setting the address (in clock cycles). Default is no delay. - data_latency (int, optional): Delay before setting the data value (in clock cycles). + data_latency: Delay before setting the data value (in clock cycles). Default is no delay. - sync (bool, optional): Wait for rising edge on clock initially. + sync: Wait for rising edge on clock initially. Defaults to True. Returns: - BinaryValue: The write response value. + The write response value. Raises: AXIProtocolError: If write response from AXI is not ``OKAY``. @@ -160,16 +162,16 @@ def write(self, address, value, byte_enable=0xf, address_latency=0, return result @cocotb.coroutine - def read(self, address, sync=True): + def read(self, address: int, sync: bool = True) -> BinaryValue: """Read from an address. Args: - address (int): The address to read from. - sync (bool, optional): Wait for rising edge on clock initially. + address: The address to read from. + sync: Wait for rising edge on clock initially. Defaults to True. Returns: - BinaryValue: The read data value. + The read data value. Raises: AXIProtocolError: If read response from AXI is not ``OKAY``. diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 02d3c308..90d2d8b3 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -33,6 +33,7 @@ """ import random +from typing import Iterable, Union, Optional import cocotb from cocotb.decorators import coroutine @@ -116,19 +117,19 @@ def _release_lock(self): self.busy_event.set() @coroutine - def read(self, address, sync=True): + def read(self, address: int, sync: bool = True) -> BinaryValue: """Issue a request to the bus and block until this comes back. Simulation time still progresses but syntactically it blocks. Args: - address (int): The address to read from. - sync (bool, optional): Wait for rising edge on clock initially. + address: The address to read from. + sync: Wait for rising edge on clock initially. Defaults to True. Returns: - BinaryValue: The read data value. + The read data value. Raises: :any:`TestError`: If master is write-only. @@ -183,13 +184,13 @@ def read(self, address, sync=True): return data @coroutine - def write(self, address, value): + def write(self, address: int, value: int) -> None: """Issue a write to the given address with the specified value. Args: - address (int): The address to write to. - value (int): The data value to write. + address: The address to write to. + value: The data value to write. Raises: :any:`TestError`: If master is read-only. @@ -671,10 +672,10 @@ def _wait_ready(self): yield ReadOnly() @coroutine - def _send_string(self, string, sync=True, channel=None): + def _send_string(self, string: bytes, sync: bool = True, channel: Optional[int] = None) -> None: """Args: - string (bytes): A string of bytes to send over the bus. - channel (int): Channel to send the data on. + string: A string of bytes to send over the bus. + channel: Channel to send the data on. """ # Avoid spurious object creation by recycling clkedge = RisingEdge(self.clock) @@ -774,9 +775,9 @@ def _send_string(self, string, sync=True, channel=None): self.bus.channel <= channel_value @coroutine - def _send_iterable(self, pkt, sync=True): + def _send_iterable(self, pkt: Iterable, sync: bool = True) -> None: """Args: - pkt (iterable): Will yield objects with attributes matching the + pkt: Will yield objects with attributes matching the signal names for each individual bus cycle. """ clkedge = RisingEdge(self.clock) @@ -815,12 +816,12 @@ def _send_iterable(self, pkt, sync=True): self.bus.valid <= 0 @coroutine - def _driver_send(self, pkt, sync=True, channel=None): + def _driver_send(self, pkt: Union[bytes, Iterable], sync: bool = True, channel: Optional[int] = None): """Send a packet over the bus. Args: - pkt (str or iterable): Packet to drive onto the bus. - channel (None or int): Channel attributed to the packet. + pkt: Packet to drive onto the bus. + channel: Channel attributed to the packet. If ``pkt`` is a string, we simply send it word by word diff --git a/cocotb/drivers/opb.py b/cocotb/drivers/opb.py index 58faf81d..cf06daa9 100644 --- a/cocotb/drivers/opb.py +++ b/cocotb/drivers/opb.py @@ -33,6 +33,7 @@ import cocotb from cocotb.triggers import RisingEdge, ReadOnly, Event from cocotb.drivers import BusDriver +from cocotb.binary import BinaryValue class OPBException(Exception): @@ -65,18 +66,18 @@ def _release_lock(self): self.busy_event.set() @cocotb.coroutine - def read(self, address, sync=True): + def read(self, address: int, sync: bool = True) -> BinaryValue: """Issue a request to the bus and block until this comes back. Simulation time still progresses but syntactically it blocks. Args: - address (int): The address to read from. - sync (bool, optional): Wait for rising edge on clock initially. + address: The address to read from. + sync: Wait for rising edge on clock initially. Defaults to True. Returns: - BinaryValue: The read data value. + The read data value. Raises: OPBException: If read took longer than 16 cycles. @@ -110,13 +111,13 @@ def read(self, address, sync=True): return data @cocotb.coroutine - def write(self, address, value, sync=True): + def write(self, address: int, value: int, sync: bool = True) -> None: """Issue a write to the given address with the specified value. Args: - address (int): The address to read from. - value (int): The data value to write. - sync (bool, optional): Wait for rising edge on clock initially. + address: The address to read from. + value: The data value to write. + sync: Wait for rising edge on clock initially. Defaults to True. Raises: diff --git a/cocotb/drivers/xgmii.py b/cocotb/drivers/xgmii.py index 9c4c9622..20d84c20 100644 --- a/cocotb/drivers/xgmii.py +++ b/cocotb/drivers/xgmii.py @@ -33,6 +33,7 @@ from cocotb.drivers import Driver from cocotb.utils import hexdump from cocotb.binary import BinaryValue +from cocotb.handle import SimHandleBase _XGMII_IDLE = 0x07 # noqa _XGMII_START = 0xFB # noqa @@ -57,12 +58,12 @@ class _XGMIIBus: >>> xgmii[1] = (b"\x55", False) # Data byte """ - def __init__(self, nbytes, interleaved=True): + def __init__(self, nbytes: int, interleaved: bool = True): """Args: - nbytes (int): The number of bytes transferred per clock cycle + nbytes: The number of bytes transferred per clock cycle (usually 8 for SDR, 4 for DDR). - interleaved (bool, optional): The arrangement of control bits on the bus. + interleaved: The arrangement of control bits on the bus. If interleaved we have a bus with 9-bits per byte, the control bit being the 9th bit of each @@ -118,12 +119,12 @@ def __len__(self): class XGMII(Driver): """XGMII (10 Gigabit Media Independent Interface) driver.""" - def __init__(self, signal, clock, interleaved=True): + def __init__(self, signal: SimHandleBase, clock: SimHandleBase, interleaved: bool = True): """Args: - signal (SimHandle): The XGMII data bus. - clock (SimHandle): The associated clock (assumed to be + signal: The XGMII data bus. + clock: The associated clock (assumed to be driven by another coroutine). - interleaved (bool, optional): Whether control bits are interleaved + interleaved: Whether control bits are interleaved with the data bytes or not. If interleaved the bus is @@ -139,16 +140,16 @@ def __init__(self, signal, clock, interleaved=True): Driver.__init__(self) @staticmethod - def layer1(packet): + def layer1(packet: bytes) -> bytes: """Take an Ethernet packet (as a string) and format as a layer 1 packet. Pad to 64 bytes, prepend preamble and append 4-byte CRC on the end. Args: - packet (str): The Ethernet packet to format. + packet: The Ethernet packet to format. Returns: - str: The formatted layer 1 packet. + The formatted layer 1 packet. """ if len(packet) < 60: padding = b"\x00" * (60 - len(packet)) @@ -162,11 +163,11 @@ def idle(self): self.bus[i] = (_XGMII_IDLE, True) self.signal <= self.bus.value - def terminate(self, index): + def terminate(self, index: int) -> None: """Helper function to terminate from a provided lane index. Args: - index (int): The index to terminate. + index: The index to terminate. """ self.bus[index] = (_XGMII_TERMINATE, True) @@ -176,11 +177,11 @@ def terminate(self, index): self.bus[rem] = (_XGMII_IDLE, True) @cocotb.coroutine - def _driver_send(self, pkt, sync=True): + def _driver_send(self, pkt: bytes, sync: bool = True) -> None: """Send a packet over the bus. Args: - pkt (bytes): The Ethernet packet to drive onto the bus. + pkt: The Ethernet packet to drive onto the bus. """ pkt = self.layer1(bytes(pkt)) From f987d9cc1b729f35a3c9355cc333f350afe5a59f Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Sat, 23 May 2020 20:46:22 +0100 Subject: [PATCH 2312/2656] Tox: Whitelist common license file variables Proprietary EDA tools need a license which is typically provided through FlexLM. The correct license file is found through an environment variable which we need to pass through to the tool. --- tox.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tox.ini b/tox.ini index 80b00125..887f794c 100644 --- a/tox.ini +++ b/tox.ini @@ -7,6 +7,9 @@ setenv = CXXFLAGS = -Werror passenv = SIM + ALDEC_LICENSE_FILE + SYNOPSYS_LICENSE_FILE + LM_LICENSE_FILE deps = coverage From b0f12dc786f4361ed5aa15827dfe54afb95f8a70 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Sat, 23 May 2020 19:09:32 +0100 Subject: [PATCH 2313/2656] Add private CI trigger This Azure Pipelines configuration triggers a private CI run. The configuration for the private CI is contained in a non-public repository, as we cannot share the tools or their output publicly due to licensing restrictions. --- .azp-private.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .azp-private.yml diff --git a/.azp-private.yml b/.azp-private.yml new file mode 100644 index 00000000..27c32ae5 --- /dev/null +++ b/.azp-private.yml @@ -0,0 +1,27 @@ +# Private CI trigger. Used to run tooling that can't currently be shared +# publicly. + +trigger: + batch: true + branches: + include: + - '*' + tags: + include: + - "*" +pr: + branches: + include: + - '*' + +# The runner used for private CI enforces the use of the template below. All +# build steps need to be placed into the template. +resources: + repositories: + - repository: cocotb-private-ci + type: github + endpoint: cocotb + name: cocotb/cocotb-private-ci + +extends: + template: jobs.yml@cocotb-private-ci From 2c5c7fb4d54cf52c6863ee494205747043d2ef53 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Sat, 23 May 2020 22:00:58 +0100 Subject: [PATCH 2314/2656] Fix typo in comment --- tests/test_cases/test_array/test_array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py index 757ab6a6..003bee2e 100644 --- a/tests/test_cases/test_array/test_array.py +++ b/tests/test_cases/test_array/test_array.py @@ -341,7 +341,7 @@ def test_discover_all(dut): dummy = dut.sig_rec dummy = dut.port_rec_out - # Riviera-Pro's VHPI implementation does not fine signal declarations when iterating + # Riviera-Pro's VHPI implementation does not find signal declarations when iterating if cocotb.LANGUAGE in ["vhdl"] and cocotb.SIM_NAME.lower().startswith(("riviera")): for hdl in dut.asc_gen: dummy = hdl.sig From 9892b4760daa0353682217c17fd505e59263c6ab Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Sat, 23 May 2020 21:08:43 +0100 Subject: [PATCH 2315/2656] Disable test_iteration.recursive_discovery on Riviera This test causes Riviera-PRO 2019.10 to segfault and the whole test to hang (Riviera doesn't shut down in this case). The issue itself is tracked in #1854 --- tests/test_cases/test_iteration_vhdl/test_iteration.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_cases/test_iteration_vhdl/test_iteration.py b/tests/test_cases/test_iteration_vhdl/test_iteration.py index 9929c97b..20d98075 100644 --- a/tests/test_cases/test_iteration_vhdl/test_iteration.py +++ b/tests/test_cases/test_iteration_vhdl/test_iteration.py @@ -30,11 +30,14 @@ from cocotb.result import TestFailure -@cocotb.test() +# This test crashes Riviera-PRO 2019.10 (at least); skip to avoid hanging the +# tests. See issue #1854 for details. +@cocotb.test(skip=cocotb.SIM_NAME.lower().startswith("riviera") and cocotb.SIM_VERSION.startswith("2019.10")) def recursive_discovery(dut): """ Recursively discover every single object in the design """ + if (cocotb.SIM_NAME.lower().startswith(("ncsim", "xmsim", "modelsim")) or (cocotb.SIM_NAME.lower().startswith("riviera") and not cocotb.SIM_VERSION.startswith("2016.02"))): # Finds regions, signal, generics, constants, varibles and ports. From 150a91c6861bd61b0cafa7460a4c3d26b8856e1b Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Sat, 23 May 2020 21:39:55 +0100 Subject: [PATCH 2316/2656] Skip test_vhdl_access.port_not_hierarchy in Riviera This tests hangs Riviera-PRO and with it the whole test setup (the simulator never returns to the Makefile). Skip the test until we have diagnosed the problem. See #1857 --- tests/test_cases/test_vhdl_access/test_vhdl_access.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_cases/test_vhdl_access/test_vhdl_access.py b/tests/test_cases/test_vhdl_access/test_vhdl_access.py index 2ea6ada2..3c664f83 100644 --- a/tests/test_cases/test_vhdl_access/test_vhdl_access.py +++ b/tests/test_cases/test_vhdl_access/test_vhdl_access.py @@ -95,7 +95,9 @@ def check_instance(obj, objtype): raise TestFailure("%d Failures during the test" % fails) -@cocotb.test() +# This test crashes Riviera-PRO 2019.10 (at least); skip to avoid hanging the +# tests. See issue #1857 for details. +@cocotb.test(skip=cocotb.SIM_NAME.lower().startswith("riviera") and cocotb.SIM_VERSION.startswith("2019.10")) def port_not_hierarchy(dut): """ Test for issue raised by Luke - iteration causes a toplevel port type to From 409768a2123c662c58e35c28d3d7033884eee570 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Sat, 23 May 2020 21:11:31 +0100 Subject: [PATCH 2317/2656] Be more helpful if test_iteration.test_n_dimension_array fails The failing test didn't display the values it got, only the expected ones. Use asserts to show the actual values. (I wish we'd have pytest-style assert handlers ...) --- tests/test_cases/test_iteration_vhdl/test_iteration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_cases/test_iteration_vhdl/test_iteration.py b/tests/test_cases/test_iteration_vhdl/test_iteration.py index 20d98075..b0f5a411 100644 --- a/tests/test_cases/test_iteration_vhdl/test_iteration.py +++ b/tests/test_cases/test_iteration_vhdl/test_iteration.py @@ -115,5 +115,5 @@ def test_n_dimension_array(dut): inner_count += 1 outer_count += 1 - if inner_count != 14 or outer_count != 2: - raise TestFailure("dut.inst_ram_ctrl.config should have a total of 14 elems over 2 loops") + assert outer_count == 2, outer_count + assert inner_count == 14, inner_count From e932d1cf6b40eba590c034664932d01907003661 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Sat, 23 May 2020 22:01:11 +0100 Subject: [PATCH 2318/2656] test_array.test_discover_all: Adjust for Riviera 2019.10 Riviera-PRO 2019.10 returns 947 objects. It's unfortunately not quite clear where the logic in these different numbers of objects lies, the test now has an interesting collection of pass criteria. This commit only sets the status quo to avoid regressions, further investigation is required, likely together with Aldec, to figure out expectations and deviations from it. See #1858 --- tests/test_cases/test_array/test_array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py index 003bee2e..11e49d7d 100644 --- a/tests/test_cases/test_array/test_array.py +++ b/tests/test_cases/test_array/test_array.py @@ -357,7 +357,7 @@ def test_discover_all(dut): pass_total = 803 elif cocotb.SIM_VERSION.startswith(("2016.06", "2016.10", "2017.02")): pass_total = 813 - elif cocotb.SIM_VERSION.startswith(("2016.02")): + elif cocotb.SIM_VERSION.startswith(("2016.02", "2019.10")): pass_total = 947 else: pass_total = 1038 From 95d50ccd5aa2032d136c1ced5f4e35d2eef3923a Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Sat, 23 May 2020 22:14:13 +0100 Subject: [PATCH 2319/2656] Riviera: Disable test_discovery.access_const_string_verilog Riviera-Pro 2019.10 doesn't make STRING_CONST available from the DUT, failing the test. ``` COUT: 0.08ns INFO Running test 14/22: access_const_string_verilog COUT: 0.08ns INFO Starting test: "access_const_string_verilog" COUT: Description: Access to a const Verilog string. COUT: 0.08ns ERROR Test errored with unexpected type: access_const_string_verilog (result was AttributeError) COUT: Traceback (most recent call last): COUT: File "/home/philipp/src/cocotb/tests/test_cases/test_discovery/test_discovery.py", line 282, in access_const_string_verilog COUT: string_const = dut.STRING_CONST COUT: File "/home/philipp/.local/lib/python3.6/site-packages/cocotb/handle.py", line 282, in __getattr__ COUT: raise AttributeError("%s contains no object named %s" % (self._name, name)) COUT: AttributeError: sample_module contains no object named STRING_CONST ``` --- tests/test_cases/test_discovery/test_discovery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 246dabc0..0857efff 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -274,7 +274,7 @@ def access_string_vhdl(dut): # TODO: add tests for Verilog "string_input_port" and "STRING_LOCALPARAM" (see issue #802) -@cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"] or cocotb.SIM_NAME.lower().startswith("icarus"), +@cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"] or cocotb.SIM_NAME.lower().startswith(("icarus", "riviera")), expect_error=cocotb.result.TestFailure if cocotb.SIM_NAME.lower().startswith(("xmsim", "ncsim", "modelsim", "chronologic simulation vcs")) else False) def access_const_string_verilog(dut): """Access to a const Verilog string.""" From 6e86c3bd4c5959023080b185b28af73f47b7a95a Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Sat, 23 May 2020 22:24:13 +0100 Subject: [PATCH 2320/2656] Mark test_iteration_vhdl.test_n_dimension_array fail in Riviera In Riviera-PRO 2019.2, outer_count is 1 instead of 2 as expected. --- tests/test_cases/test_iteration_vhdl/test_iteration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/test_iteration_vhdl/test_iteration.py b/tests/test_cases/test_iteration_vhdl/test_iteration.py index b0f5a411..4442262c 100644 --- a/tests/test_cases/test_iteration_vhdl/test_iteration.py +++ b/tests/test_cases/test_iteration_vhdl/test_iteration.py @@ -101,7 +101,7 @@ def get_clock(dut): raise TestFailure("dut.aclk is not what we expected (got %d)" % int(dut.aclk)) -@cocotb.test() +@cocotb.test(expect_fail=cocotb.SIM_NAME.lower().startswith("riviera") and cocotb.SIM_VERSION.startswith("2019.10")) def test_n_dimension_array(dut): tlog = logging.getLogger("cocotb.test") inner_count = 0 From e20432fa492e88e6d7496d1696a2d952fe58579f Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Tue, 19 May 2020 11:41:55 -0500 Subject: [PATCH 2321/2656] Add ability to check for a simulator to the GPI interface --- cocotb/share/include/gpi.h | 7 +++++++ cocotb/share/lib/gpi/GpiCommon.cpp | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/cocotb/share/include/gpi.h b/cocotb/share/include/gpi.h index 2116020d..35d4915b 100644 --- a/cocotb/share/include/gpi.h +++ b/cocotb/share/include/gpi.h @@ -113,6 +113,13 @@ typedef enum gpi_event_e { // Functions for controlling/querying the simulation state +/** + * Returns 1 if there is a registered GPI implementation, 0 otherwise. + * + * Useful for checking if a simulator is running. + */ +bool gpi_has_registered_impl(void); + // Stop the simulator void gpi_sim_end(void); diff --git a/cocotb/share/lib/gpi/GpiCommon.cpp b/cocotb/share/lib/gpi/GpiCommon.cpp index dcf2ee6f..b4d84d1f 100644 --- a/cocotb/share/lib/gpi/GpiCommon.cpp +++ b/cocotb/share/lib/gpi/GpiCommon.cpp @@ -123,6 +123,11 @@ int gpi_register_impl(GpiImplInterface *func_tbl) return 0; } +bool gpi_has_registered_impl() +{ + return registered_impls.size() > 0; +} + void gpi_embed_init(int argc, char const* const* argv) { if (embed_sim_init(argc, argv)) From 8e5a0e0dcbcfb3286847a7c55d169d353b2328e7 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 17 May 2020 15:13:04 -0500 Subject: [PATCH 2322/2656] Fix #1839 Prevent segfault when using cocotb when no simulator is running When a simulator is not running, meaning cocotb was not started by a simulator, simulatormodule methods throw an exception. `get_sim_precision` is an exception, it's previous behavior was to return -15 when no simulator was present. This old behavior was returned, it was previously broken. --- .../share/lib/simulator/simulatormodule.cpp | 65 +++++++++++++++++++ .../source/newsfragments/1843.change.rst | 1 + 2 files changed, 66 insertions(+) create mode 100644 documentation/source/newsfragments/1843.change.rst diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index 2662a96a..fdac0dab 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -255,6 +255,7 @@ int handle_gpi_callback(void *user_data) static PyObject *log_msg(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); + const char *name; const char *path; const char *msg; @@ -295,6 +296,11 @@ static PyObject *register_readonly_callback(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); + if (!gpi_has_registered_impl()) { + PyErr_SetString(PyExc_RuntimeError, "No simulator available!"); + return NULL; + } + Py_ssize_t numargs = PyTuple_Size(args); if (numargs < 1) { @@ -333,6 +339,11 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); + if (!gpi_has_registered_impl()) { + PyErr_SetString(PyExc_RuntimeError, "No simulator available!"); + return NULL; + } + Py_ssize_t numargs = PyTuple_Size(args); if (numargs < 1) { @@ -372,6 +383,11 @@ static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); + if (!gpi_has_registered_impl()) { + PyErr_SetString(PyExc_RuntimeError, "No simulator available!"); + return NULL; + } + Py_ssize_t numargs = PyTuple_Size(args); if (numargs < 1) { @@ -415,6 +431,11 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); + if (!gpi_has_registered_impl()) { + PyErr_SetString(PyExc_RuntimeError, "No simulator available!"); + return NULL; + } + Py_ssize_t numargs = PyTuple_Size(args); if (numargs < 2) { @@ -473,6 +494,11 @@ static PyObject *register_value_change_callback(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); + if (!gpi_has_registered_impl()) { + PyErr_SetString(PyExc_RuntimeError, "No simulator available!"); + return NULL; + } + Py_ssize_t numargs = PyTuple_Size(args); if (numargs < 3) { @@ -675,6 +701,11 @@ static PyObject *get_root_handle(PyObject *self, PyObject *args) COCOTB_UNUSED(self); const char *name; + if (!gpi_has_registered_impl()) { + PyErr_SetString(PyExc_RuntimeError, "No simulator available!"); + return NULL; + } + if (!PyArg_ParseTuple(args, "z:get_root_handle", &name)) { return NULL; } @@ -725,6 +756,12 @@ static PyObject *get_sim_time(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); COCOTB_UNUSED(args); + + if (!gpi_has_registered_impl()) { + PyErr_SetString(PyExc_RuntimeError, "No simulator available!"); + return NULL; + } + struct sim_time local_time; if (is_python_context) { @@ -744,6 +781,15 @@ static PyObject *get_precision(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); COCOTB_UNUSED(args); + + if (!gpi_has_registered_impl()) { + char const * msg = "Simulator is not available! Defaulting precision to 1 fs."; + if (PyErr_WarnEx(PyExc_RuntimeWarning, msg, 1) < 0) { + return NULL; + } + return PyLong_FromLong(-15); // preserves old behavior + } + int32_t precision; gpi_get_sim_precision(&precision); @@ -755,6 +801,12 @@ static PyObject *get_simulator_product(PyObject *m, PyObject *args) { COCOTB_UNUSED(m); COCOTB_UNUSED(args); + + if (!gpi_has_registered_impl()) { + PyErr_SetString(PyExc_RuntimeError, "No simulator available!"); + return NULL; + } + return PyUnicode_FromString(gpi_get_simulator_product()); } @@ -762,6 +814,12 @@ static PyObject *get_simulator_version(PyObject *m, PyObject *args) { COCOTB_UNUSED(m); COCOTB_UNUSED(args); + + if (!gpi_has_registered_impl()) { + PyErr_SetString(PyExc_RuntimeError, "No simulator available!"); + return NULL; + } + return PyUnicode_FromString(gpi_get_simulator_version()); } @@ -792,6 +850,12 @@ static PyObject *stop_simulator(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); COCOTB_UNUSED(args); + + if (!gpi_has_registered_impl()) { + PyErr_SetString(PyExc_RuntimeError, "No simulator available!"); + return NULL; + } + gpi_sim_end(); sim_ending = 1; Py_RETURN_NONE; @@ -810,6 +874,7 @@ static PyObject *deregister(gpi_hdl_Object *self, PyObject *args) static PyObject *log_level(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); + long l_level; if (!PyArg_ParseTuple(args, "l:log_level", &l_level)) { diff --git a/documentation/source/newsfragments/1843.change.rst b/documentation/source/newsfragments/1843.change.rst new file mode 100644 index 00000000..33ec042a --- /dev/null +++ b/documentation/source/newsfragments/1843.change.rst @@ -0,0 +1 @@ +Certain methods on the :mod:`cocotb.simulator` Python module now throw a :exc`RuntimeError` when no simulator is present, making it safe to use :mod:`cocotb` without a simulator present. From 48edfda871b1019315072f94c86679584b8ca454 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 22 May 2020 13:28:56 -0500 Subject: [PATCH 2323/2656] Add simulator.is_running method to simulator module --- cocotb/share/lib/simulator/simulatormodule.cpp | 8 ++++++++ cocotb/share/lib/simulator/simulatormodule.h | 11 +++++++++-- documentation/source/newsfragments/1843.feature.rst | 1 + 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 documentation/source/newsfragments/1843.feature.rst diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index fdac0dab..a8b7ecbf 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -749,6 +749,14 @@ static PyObject *get_type_string(gpi_hdl_Object *self, PyObject *ar } +static PyObject *is_running(PyObject *self, PyObject *args) +{ + COCOTB_UNUSED(self); + COCOTB_UNUSED(args); + + return PyBool_FromLong(gpi_has_registered_impl()); +} + // Returns a high, low, tuple of simulator time // Note we can never log from this function since the logging mechanism calls this to annotate // log messages with the current simulation time diff --git a/cocotb/share/lib/simulator/simulatormodule.h b/cocotb/share/lib/simulator/simulatormodule.h index 3fe003b3..8a7a1c49 100644 --- a/cocotb/share/lib/simulator/simulatormodule.h +++ b/cocotb/share/lib/simulator/simulatormodule.h @@ -61,7 +61,7 @@ static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args); static PyObject *get_root_handle(PyObject *self, PyObject *args); static PyObject *stop_simulator(PyObject *self, PyObject *args); - +static PyObject *is_running(PyObject *self, PyObject *args); static PyObject *get_sim_time(PyObject *self, PyObject *args); static PyObject *get_precision(PyObject *self, PyObject *args); static PyObject *get_simulator_product(PyObject *self, PyObject *args); @@ -131,7 +131,14 @@ static PyMethodDef SimulatorMethods[] = { "log_level(level: int) -> None\n" "Set the log level for GPI." )}, - + {"is_running", is_running, METH_NOARGS, PyDoc_STR( + "is_running()\n" + "--\n\n" + "is_running() -> bool\n" + "Returns ``True`` if the caller is running within a simulator.\n" + "\n" + ".. versionadded:: 1.4" + )}, {"get_sim_time", get_sim_time, METH_NOARGS, PyDoc_STR( "get_sim_time()\n" "--\n\n" diff --git a/documentation/source/newsfragments/1843.feature.rst b/documentation/source/newsfragments/1843.feature.rst new file mode 100644 index 00000000..1c72c23b --- /dev/null +++ b/documentation/source/newsfragments/1843.feature.rst @@ -0,0 +1 @@ +The :func:`cocotb.simulator.is_running` function was added so a user of cocotb could determine if they are running within a simulator. From 765a187406e4adafbed15d21899f1cb20d842ea7 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Wed, 27 May 2020 21:25:19 +0100 Subject: [PATCH 2324/2656] Consistently use await/async in quickstart --- documentation/source/quickstart.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index e098df16..0a6e3213 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -94,15 +94,15 @@ following: from cocotb.triggers import Timer @cocotb.test() - def my_first_test(dut): + async def my_first_test(dut): """Try accessing the design.""" dut._log.info("Running test!") for cycle in range(10): dut.clk = 0 - yield Timer(1, units='ns') + await Timer(1, units='ns') dut.clk = 1 - yield Timer(1, units='ns') + await Timer(1, units='ns') dut._log.info("Running test!") This will drive a square wave clock onto the ``clk`` port of the toplevel. From 46504b500394506fb13c2155d98f90b9506f587c Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Wed, 27 May 2020 21:25:41 +0100 Subject: [PATCH 2325/2656] Quickstart: Comment on a function being a coroutine Previously, the `cocotb.coroutine` decorator made this fact quite clear. Now that this decorator is gone, we need to give a bit a stronger hint to our users that a `async` function is actually a coroutine. (That term is in the name of cocotb, after all.) --- documentation/source/quickstart.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index 0a6e3213..babf03c0 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -214,6 +214,7 @@ The following example shows these in action: .. code-block:: python3 + # A coroutine async def reset_dut(reset_n, duration_ns): reset_n <= 0 await Timer(duration_ns, units='ns') From a8f73065b684c0ed26bbd37af11594c9922e9a03 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 15 May 2020 22:45:23 -0500 Subject: [PATCH 2326/2656] Fix #1803 prevent printing summary multiple times When a sim fails, it sets the outcome of the remaining tests to `SimFailure`, which causes the `tear_down` procedure to re-enter and print the summary multiple times. We add a bit of logic to prevent `tear_down` from being run more than once. This is a temporary fix, a more permanent solution is pending in #1782. --- cocotb/regression.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cocotb/regression.py b/cocotb/regression.py index bf238af3..29c615b8 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -95,6 +95,7 @@ def __init__(self, dut: SimHandle, tests: Iterable[Test], hooks: Iterable[Hook]) self.count = 0 self.skipped = 0 self.failures = 0 + self._tearing_down = False # Setup XUnit ################### @@ -229,6 +230,12 @@ def _init_hook(self, hook: Hook) -> Optional[RunningTask]: return cocotb.scheduler.add(test) def tear_down(self) -> None: + # prevent re-entering the tear down procedure + if not self._tearing_down: + self._tearing_down = True + else: + return + # fail remaining tests while True: test = self.next_test() From 2a73373030f1c194261ac944d3fa2fa6a45bb67a Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Tue, 26 May 2020 15:00:53 +0200 Subject: [PATCH 2327/2656] Disable failing tests on Riviera due to #1859 --- tests/test_cases/issue_1279/issue_1279.py | 12 ++++++++++-- tests/test_cases/test_closedown/test_closedown.py | 10 ++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/tests/test_cases/issue_1279/issue_1279.py b/tests/test_cases/issue_1279/issue_1279.py index 6a97e39c..6104969f 100644 --- a/tests/test_cases/issue_1279/issue_1279.py +++ b/tests/test_cases/issue_1279/issue_1279.py @@ -4,13 +4,21 @@ import cocotb -@cocotb.test(expect_error=cocotb.result.SimFailure, stage=1) +@cocotb.test( + skip=cocotb.SIM_NAME.lower().startswith("riviera"), # gh-1859 + expect_error=cocotb.result.SimFailure, + stage=1, +) def test_sim_failure_a(dut): # invoke a deadlock, as nothing is driving this clock yield cocotb.triggers.RisingEdge(dut.clk) -@cocotb.test(expect_error=cocotb.result.SimFailure, stage=2) +@cocotb.test( + skip=cocotb.SIM_NAME.lower().startswith("riviera"), # gh-1859 + expect_error=cocotb.result.SimFailure, + stage=2, +) def test_sim_failure_b(dut): yield cocotb.triggers.NullTrigger() raise cocotb.result.TestFailure("This test should never run") diff --git a/tests/test_cases/test_closedown/test_closedown.py b/tests/test_cases/test_closedown/test_closedown.py index 9b8be599..750f98ab 100644 --- a/tests/test_cases/test_closedown/test_closedown.py +++ b/tests/test_cases/test_closedown/test_closedown.py @@ -48,9 +48,11 @@ async def clock_mon(dut): await RisingEdge(dut.clk) -@cocotb.test(expect_fail=True, - expect_error=cocotb.SIM_NAME.lower().startswith(("modelsim", # $fatal() fails hard on Questa - ))) +@cocotb.test( + expect_fail=True, + skip=cocotb.SIM_NAME.lower().startswith("riviera"), # gh-1859 + expect_error=cocotb.SIM_NAME.lower().startswith("modelsim") # $fatal() fails hard on Questa +) async def test_failure_from_system_task(dut): """ Allow the dut to call system tasks from verilog. @@ -62,7 +64,7 @@ async def test_failure_from_system_task(dut): await Timer(10000000, units='ns') -@cocotb.test() +@cocotb.test(skip=cocotb.SIM_NAME.lower().startswith("riviera")) # gh-1859 async def test_after_system_task_fail(dut): """ Test to run after failed test. From 924f35a3b7d39543118b7bfaed77dd4808e6612b Mon Sep 17 00:00:00 2001 From: Marlon James Date: Tue, 26 May 2020 12:49:53 -0700 Subject: [PATCH 2328/2656] Active-HDL: Overwrite runsim.do instead of appending The first line of runsim.do should use > rather than >> to overwrite any existing contents of the file. --- cocotb/share/makefiles/simulators/Makefile.activehdl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.activehdl b/cocotb/share/makefiles/simulators/Makefile.activehdl index de7a5c08..cc292cee 100644 --- a/cocotb/share/makefiles/simulators/Makefile.activehdl +++ b/cocotb/share/makefiles/simulators/Makefile.activehdl @@ -46,7 +46,7 @@ endif # Create a DO script (Tcl-like but not fully compatible) based on the list of $(VERILOG_SOURCES) $(SIM_BUILD)/runsim.do : $(VERILOG_SOURCES) $(VHDL_SOURCES) $(SIM_BUILD) - @echo "alib $(RTL_LIBRARY)" >> $@ + @echo "alib $(RTL_LIBRARY)" > $@ @echo "set worklib $(RTL_LIBRARY)" >> $@ ifneq ($(VHDL_SOURCES),) @echo "acom $(ACOM_ARGS) $(call to_tcl_path,$(VHDL_SOURCES))" >> $@ From 6202a3c454e815fdf938465ba420c3135525a7b2 Mon Sep 17 00:00:00 2001 From: Lance Eftink Date: Mon, 4 Mar 2019 12:09:08 -0700 Subject: [PATCH 2329/2656] Clean-up constructors for GpiObjHdl to use default arguments --- cocotb/share/lib/fli/FliImpl.h | 26 ++++++++------------ cocotb/share/lib/gpi/gpi_priv.h | 43 ++++++++++++--------------------- 2 files changed, 26 insertions(+), 43 deletions(-) diff --git a/cocotb/share/lib/fli/FliImpl.h b/cocotb/share/lib/fli/FliImpl.h index 434bbae5..a8a02d1a 100644 --- a/cocotb/share/lib/fli/FliImpl.h +++ b/cocotb/share/lib/fli/FliImpl.h @@ -162,22 +162,16 @@ class FliObj { class FliObjHdl : public GpiObjHdl, public FliObj { public: - FliObjHdl(GpiImplInterface *impl, - void *hdl, - gpi_objtype_t objtype, - int acc_type, - int acc_full_type) : - GpiObjHdl(impl, hdl, objtype, false), - FliObj(acc_type, acc_full_type) { } - - FliObjHdl(GpiImplInterface *impl, - void *hdl, - gpi_objtype_t objtype, - int acc_type, - int acc_full_type, - bool is_const) : - GpiObjHdl(impl, hdl, objtype, is_const), - FliObj(acc_type, acc_full_type) { } + FliObjHdl( + GpiImplInterface *impl, + void *hdl, + gpi_objtype_t objtype, + int acc_type, + int acc_full_type, + bool is_const = false + ) : + GpiObjHdl(impl, hdl, objtype, is_const), + FliObj(acc_type, acc_full_type) { } int initialise(std::string &name, std::string &fq_name) override; diff --git a/cocotb/share/lib/gpi/gpi_priv.h b/cocotb/share/lib/gpi/gpi_priv.h index 1e33872c..a690300a 100644 --- a/cocotb/share/lib/gpi/gpi_priv.h +++ b/cocotb/share/lib/gpi/gpi_priv.h @@ -52,8 +52,7 @@ class GpiCbHdl; /* Base GPI class others are derived from */ class GpiHdl { public: - GpiHdl(GpiImplInterface *impl) : m_impl(impl), m_obj_hdl(NULL) { } - GpiHdl(GpiImplInterface *impl, void *hdl) : m_impl(impl), m_obj_hdl(hdl) { } + GpiHdl(GpiImplInterface *impl, void *hdl = NULL) : m_impl(impl), m_obj_hdl(hdl) { } virtual ~GpiHdl() = default; @@ -81,31 +80,21 @@ class GpiHdl { // that construct an object derived from GpiSignalObjHdl or GpiObjHdl class GpiObjHdl : public GpiHdl { public: - GpiObjHdl(GpiImplInterface *impl) : GpiHdl(impl, NULL), - m_num_elems(0), - m_indexable(false), - m_range_left(-1), - m_range_right(-1), - m_fullname("unknown"), - m_type(GPI_UNKNOWN), - m_const(false) { } - GpiObjHdl(GpiImplInterface *impl, void *hdl, gpi_objtype_t objtype) : GpiHdl(impl, hdl), - m_num_elems(0), - m_indexable(false), - m_range_left(-1), - m_range_right(-1), - m_fullname("unknown"), - m_type(objtype), - m_const(false) { } - GpiObjHdl(GpiImplInterface *impl, void *hdl, gpi_objtype_t objtype, bool is_const) : - GpiHdl(impl, hdl), - m_num_elems(0), - m_indexable(false), - m_range_left(-1), - m_range_right(-1), - m_fullname("unknown"), - m_type(objtype), - m_const(is_const) { } + GpiObjHdl( + GpiImplInterface *impl, + void *hdl = nullptr, + gpi_objtype_t objtype = GPI_UNKNOWN, + bool is_const = false + ) : + GpiHdl(impl, hdl), + m_num_elems(0), + m_indexable(false), + m_range_left(-1), + m_range_right(-1), + m_fullname("unknown"), + m_type(objtype), + m_const(is_const) { } + virtual ~GpiObjHdl() = default; virtual const char* get_name_str(); From 9f34cd32dd9a86a60b541ce1d7b2f8db9c6d4753 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Fri, 17 Apr 2020 21:32:50 +0100 Subject: [PATCH 2330/2656] Use C++11-isms to further reduce constructor boilerplate Member initializers can now go at the declaration, rather than needing to be in the initialization list. Forwarding constructors can now be written with `using Base::Base`. --- cocotb/share/lib/fli/FliImpl.h | 39 +++++++++++++-------------------- cocotb/share/lib/gpi/gpi_priv.h | 34 +++++++++++----------------- cocotb/share/lib/vpi/VpiImpl.h | 5 ++--- 3 files changed, 30 insertions(+), 48 deletions(-) diff --git a/cocotb/share/lib/fli/FliImpl.h b/cocotb/share/lib/fli/FliImpl.h index a8a02d1a..de62b756 100644 --- a/cocotb/share/lib/fli/FliImpl.h +++ b/cocotb/share/lib/fli/FliImpl.h @@ -219,9 +219,7 @@ class FliValueObjHdl : public FliSignalObjHdl { mtiTypeKindT typeKind) : FliSignalObjHdl(impl, hdl, objtype, is_const, acc_type, acc_full_type, is_var), m_fli_type(typeKind), - m_val_type(valType), - m_val_buff(NULL), - m_sub_hdls(NULL) { } + m_val_type(valType) { } ~FliValueObjHdl() override { if (m_val_buff != NULL) @@ -250,8 +248,8 @@ class FliValueObjHdl : public FliSignalObjHdl { protected: mtiTypeKindT m_fli_type; mtiTypeIdT m_val_type; - char *m_val_buff; - void **m_sub_hdls; + char *m_val_buff = nullptr; + void **m_sub_hdls = nullptr; }; class FliEnumObjHdl : public FliValueObjHdl { @@ -265,9 +263,7 @@ class FliEnumObjHdl : public FliValueObjHdl { bool is_var, mtiTypeIdT valType, mtiTypeKindT typeKind) : - FliValueObjHdl(impl, hdl, objtype, is_const, acc_type, acc_full_type, is_var, valType, typeKind), - m_value_enum(NULL), - m_num_enum(0) { } + FliValueObjHdl(impl, hdl, objtype, is_const, acc_type, acc_full_type, is_var, valType, typeKind) { } const char* get_signal_value_str() override; @@ -279,8 +275,8 @@ class FliEnumObjHdl : public FliValueObjHdl { int initialise(std::string &name, std::string &fq_name) override; private: - char **m_value_enum; // Do Not Free - mtiInt32T m_num_enum; + char **m_value_enum = nullptr; // Do Not Free + mtiInt32T m_num_enum = 0; }; class FliLogicObjHdl : public FliValueObjHdl { @@ -302,11 +298,8 @@ class FliLogicObjHdl : public FliValueObjHdl { acc_full_type, is_var, valType, - typeKind), - m_mti_buff(NULL), - m_value_enum(NULL), - m_num_enum(0), - m_enum_map() { } + typeKind) + { } ~FliLogicObjHdl() override { if (m_mti_buff != NULL) @@ -323,9 +316,9 @@ class FliLogicObjHdl : public FliValueObjHdl { private: - char *m_mti_buff; - char **m_value_enum; // Do Not Free - mtiInt32T m_num_enum; + char *m_mti_buff = nullptr; + char **m_value_enum = nullptr; // Do Not Free + mtiInt32T m_num_enum = 0; std::map m_enum_map; }; @@ -363,8 +356,7 @@ class FliRealObjHdl : public FliValueObjHdl { bool is_var, mtiTypeIdT valType, mtiTypeKindT typeKind) : - FliValueObjHdl(impl, hdl, objtype, is_const, acc_type, acc_full_type, is_var, valType, typeKind), - m_mti_buff(NULL) { } + FliValueObjHdl(impl, hdl, objtype, is_const, acc_type, acc_full_type, is_var, valType, typeKind) { } ~FliRealObjHdl() override { if (m_mti_buff != NULL) @@ -379,7 +371,7 @@ class FliRealObjHdl : public FliValueObjHdl { int initialise(std::string &name, std::string &fq_name) override; private: - double *m_mti_buff; + double *m_mti_buff = nullptr; }; class FliStringObjHdl : public FliValueObjHdl { @@ -393,8 +385,7 @@ class FliStringObjHdl : public FliValueObjHdl { bool is_var, mtiTypeIdT valType, mtiTypeKindT typeKind) : - FliValueObjHdl(impl, hdl, objtype, is_const, acc_type, acc_full_type, is_var, valType, typeKind), - m_mti_buff(NULL) { } + FliValueObjHdl(impl, hdl, objtype, is_const, acc_type, acc_full_type, is_var, valType, typeKind) { } ~FliStringObjHdl() override { if (m_mti_buff != NULL) @@ -409,7 +400,7 @@ class FliStringObjHdl : public FliValueObjHdl { int initialise(std::string &name, std::string &fq_name) override; private: - char *m_mti_buff; + char *m_mti_buff = nullptr; }; class FliTimerCache { diff --git a/cocotb/share/lib/gpi/gpi_priv.h b/cocotb/share/lib/gpi/gpi_priv.h index a690300a..e41dcbca 100644 --- a/cocotb/share/lib/gpi/gpi_priv.h +++ b/cocotb/share/lib/gpi/gpi_priv.h @@ -87,11 +87,6 @@ class GpiObjHdl : public GpiHdl { bool is_const = false ) : GpiHdl(impl, hdl), - m_num_elems(0), - m_indexable(false), - m_range_left(-1), - m_range_right(-1), - m_fullname("unknown"), m_type(objtype), m_const(is_const) { } @@ -120,12 +115,12 @@ class GpiObjHdl : public GpiHdl { virtual int initialise(std::string &name, std::string &full_name); protected: - int m_num_elems; - bool m_indexable; - int m_range_left; - int m_range_right; + int m_num_elems = 0; + bool m_indexable = false; + int m_range_left = -1; + int m_range_right = -1; std::string m_name; - std::string m_fullname; + std::string m_fullname = "unknown"; std::string m_definition_name; std::string m_definition_file; @@ -141,9 +136,8 @@ class GpiObjHdl : public GpiHdl { // value of the signal (which doesn't apply to non signal items in the hierarchy class GpiSignalObjHdl : public GpiObjHdl { public: - GpiSignalObjHdl(GpiImplInterface *impl, void *hdl, gpi_objtype_t objtype, bool is_const) : - GpiObjHdl(impl, hdl, objtype, is_const), - m_length(0) { } + using GpiObjHdl::GpiObjHdl; + virtual ~GpiSignalObjHdl() = default; // Provide public access to the implementation (composition vs inheritance) virtual const char* get_signal_value_binstr() = 0; @@ -151,7 +145,7 @@ class GpiSignalObjHdl : public GpiObjHdl { virtual double get_signal_value_real() = 0; virtual long get_signal_value_long() = 0; - int m_length; + int m_length = 0; virtual int set_signal_value(const long value, gpi_set_action_t action) = 0; virtual int set_signal_value(const double value, gpi_set_action_t action) = 0; @@ -169,10 +163,8 @@ class GpiSignalObjHdl : public GpiObjHdl { // vpiHandle/vhpiHandleT for instance. The class GpiCbHdl : public GpiHdl { public: - GpiCbHdl(GpiImplInterface *impl) : GpiHdl(impl, NULL), - gpi_function(NULL), - m_cb_data(NULL), - m_state(GPI_FREE) { } + GpiCbHdl(GpiImplInterface *impl) : GpiHdl(impl) { } + // Pure virtual functions for derived classes virtual int arm_callback() = 0; // Register with simulator virtual int run_callback(); // Entry point from simulator @@ -188,9 +180,9 @@ class GpiCbHdl : public GpiHdl { virtual ~GpiCbHdl(); protected: - int (*gpi_function)(const void *); // GPI function to callback - const void *m_cb_data; // GPI data supplied to "gpi_function" - gpi_cb_state_e m_state; // GPI state of the callback through its cycle + int (*gpi_function)(const void *) = nullptr; // GPI function to callback + const void *m_cb_data = nullptr; // GPI data supplied to "gpi_function" + gpi_cb_state_e m_state = GPI_FREE; // GPI state of the callback through its cycle }; class GpiValueCbHdl : public virtual GpiCbHdl { diff --git a/cocotb/share/lib/vpi/VpiImpl.h b/cocotb/share/lib/vpi/VpiImpl.h index 9f6839e1..8974bc12 100644 --- a/cocotb/share/lib/vpi/VpiImpl.h +++ b/cocotb/share/lib/vpi/VpiImpl.h @@ -211,8 +211,7 @@ class VpiSingleIterator : public GpiIterator { public: VpiSingleIterator(GpiImplInterface *impl, GpiObjHdl *hdl, - int32_t vpitype) : GpiIterator(impl, hdl), - m_iterator(NULL) + int32_t vpitype) : GpiIterator(impl, hdl) { vpiHandle vpi_hdl = m_parent->get_handle(); @@ -226,7 +225,7 @@ class VpiSingleIterator : public GpiIterator { Status next_handle(std::string &name, GpiObjHdl **hdl, void **raw_hdl) override; protected: - vpiHandle m_iterator; + vpiHandle m_iterator = nullptr; }; From 0c695ae4494866ca221b5fac89af41bf5bdf09df Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 30 May 2020 13:05:39 +0100 Subject: [PATCH 2331/2656] Use order-only prerequisites for SIM_BUILD (#1876) This means that modifing the date of the sim_build directory does not cause a total rebuild. --- cocotb/share/makefiles/simulators/Makefile.activehdl | 2 +- cocotb/share/makefiles/simulators/Makefile.cvc | 2 +- cocotb/share/makefiles/simulators/Makefile.ghdl | 2 +- cocotb/share/makefiles/simulators/Makefile.icarus | 2 +- cocotb/share/makefiles/simulators/Makefile.ius | 2 +- cocotb/share/makefiles/simulators/Makefile.questa | 2 +- cocotb/share/makefiles/simulators/Makefile.riviera | 2 +- cocotb/share/makefiles/simulators/Makefile.vcs | 4 ++-- cocotb/share/makefiles/simulators/Makefile.verilator | 2 +- cocotb/share/makefiles/simulators/Makefile.xcelium | 2 +- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.activehdl b/cocotb/share/makefiles/simulators/Makefile.activehdl index cc292cee..942a1399 100644 --- a/cocotb/share/makefiles/simulators/Makefile.activehdl +++ b/cocotb/share/makefiles/simulators/Makefile.activehdl @@ -45,7 +45,7 @@ else endif # Create a DO script (Tcl-like but not fully compatible) based on the list of $(VERILOG_SOURCES) -$(SIM_BUILD)/runsim.do : $(VERILOG_SOURCES) $(VHDL_SOURCES) $(SIM_BUILD) +$(SIM_BUILD)/runsim.do : $(VERILOG_SOURCES) $(VHDL_SOURCES) | $(SIM_BUILD) @echo "alib $(RTL_LIBRARY)" > $@ @echo "set worklib $(RTL_LIBRARY)" >> $@ ifneq ($(VHDL_SOURCES),) diff --git a/cocotb/share/makefiles/simulators/Makefile.cvc b/cocotb/share/makefiles/simulators/Makefile.cvc index d65e251a..c0644293 100644 --- a/cocotb/share/makefiles/simulators/Makefile.cvc +++ b/cocotb/share/makefiles/simulators/Makefile.cvc @@ -60,7 +60,7 @@ ifeq ($(CVC_ITERP),1) endif # Compilation phase -$(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) +$(SIM_BUILD)/sim.vvp: $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) | $(SIM_BUILD) MODULE=$(MODULE) \ TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ $(CMD) $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=$(LIB_DIR)/libcocotbvpi_modelsim:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) diff --git a/cocotb/share/makefiles/simulators/Makefile.ghdl b/cocotb/share/makefiles/simulators/Makefile.ghdl index de9d2e4f..f8e16abe 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ghdl +++ b/cocotb/share/makefiles/simulators/Makefile.ghdl @@ -64,7 +64,7 @@ endif .PHONY: analyse # Compilation phase -analyse: $(VHDL_SOURCES) $(SIM_BUILD) $(CUSTOM_COMPILE_DEPS) +analyse: $(VHDL_SOURCES) $(CUSTOM_COMPILE_DEPS) | $(SIM_BUILD) $(foreach SOURCES_VAR, $(filter VHDL_SOURCES_%, $(.VARIABLES)), \ $(CMD) -i $(GHDL_ARGS) $(COMPILE_ARGS) --workdir=$(SIM_BUILD) --work=$(SOURCES_VAR:VHDL_SOURCES_%=%) $($(SOURCES_VAR)) && ) \ $(CMD) -i $(GHDL_ARGS) $(COMPILE_ARGS) --workdir=$(SIM_BUILD) --work=$(RTL_LIBRARY) $(VHDL_SOURCES) && \ diff --git a/cocotb/share/makefiles/simulators/Makefile.icarus b/cocotb/share/makefiles/simulators/Makefile.icarus index 3ca0d083..725662cc 100644 --- a/cocotb/share/makefiles/simulators/Makefile.icarus +++ b/cocotb/share/makefiles/simulators/Makefile.icarus @@ -57,7 +57,7 @@ endif COMPILE_ARGS += -f $(SIM_BUILD)/cmds.f -g2012 # Default to latest SystemVerilog standard # Compilation phase -$(SIM_BUILD)/sim.vvp: $(SIM_BUILD) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) +$(SIM_BUILD)/sim.vvp: $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) | $(SIM_BUILD) @echo "+timescale+$(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION)" > $(SIM_BUILD)/cmds.f $(CMD) -o $(SIM_BUILD)/sim.vvp -D COCOTB_SIM=1 -s $(TOPLEVEL) $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) diff --git a/cocotb/share/makefiles/simulators/Makefile.ius b/cocotb/share/makefiles/simulators/Makefile.ius index 9d968c48..33560ef5 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ius +++ b/cocotb/share/makefiles/simulators/Makefile.ius @@ -97,7 +97,7 @@ else $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") endif -$(COCOTB_RESULTS_FILE): $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) +$(COCOTB_RESULTS_FILE): $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) | $(SIM_BUILD) -@rm -f $(COCOTB_RESULTS_FILE) set -o pipefail; \ diff --git a/cocotb/share/makefiles/simulators/Makefile.questa b/cocotb/share/makefiles/simulators/Makefile.questa index 28ed2eed..8c6e88a0 100644 --- a/cocotb/share/makefiles/simulators/Makefile.questa +++ b/cocotb/share/makefiles/simulators/Makefile.questa @@ -92,7 +92,7 @@ else $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") endif -$(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) $(SIM_BUILD) +$(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) | $(SIM_BUILD) @echo "# Autogenerated file" > $@ @echo "onerror {" >> $@ @echo " quit -f -code 1" >> $@ diff --git a/cocotb/share/makefiles/simulators/Makefile.riviera b/cocotb/share/makefiles/simulators/Makefile.riviera index f3269bb2..622da0f8 100644 --- a/cocotb/share/makefiles/simulators/Makefile.riviera +++ b/cocotb/share/makefiles/simulators/Makefile.riviera @@ -102,7 +102,7 @@ else endif # Create a TCL script based on the list of $(VERILOG_SOURCES) -$(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(VHDL_SOURCES) $(SIM_BUILD) +$(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(VHDL_SOURCES) | $(SIM_BUILD) @echo "onerror {" > $@ @echo " puts [read [open sim.log r]]" >> $@ @echo " quit -code 1" >> $@ diff --git a/cocotb/share/makefiles/simulators/Makefile.vcs b/cocotb/share/makefiles/simulators/Makefile.vcs index 7785f35c..7a02988e 100644 --- a/cocotb/share/makefiles/simulators/Makefile.vcs +++ b/cocotb/share/makefiles/simulators/Makefile.vcs @@ -66,11 +66,11 @@ endif # Can't do this using an argument, we have to create a PLI table file # enabling write access to the design -$(SIM_BUILD)/pli.tab : +$(SIM_BUILD)/pli.tab : | $(SIM_BUILD) echo "acc+=rw,wn:*" > $@ # Compilation phase -$(SIM_BUILD)/simv: $(SIM_BUILD) $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(CUSTOM_COMPILE_DEPS) +$(SIM_BUILD)/simv: $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(CUSTOM_COMPILE_DEPS) | $(SIM_BUILD) cd $(SIM_BUILD) && \ TOPLEVEL=$(TOPLEVEL) \ $(CMD) -top $(TOPLEVEL) $(PLUSARGS) +acc+1 +vpi -P pli.tab +define+COCOTB_SIM=1 -sverilog \ diff --git a/cocotb/share/makefiles/simulators/Makefile.verilator b/cocotb/share/makefiles/simulators/Makefile.verilator index ccb1ceb9..25f55c67 100644 --- a/cocotb/share/makefiles/simulators/Makefile.verilator +++ b/cocotb/share/makefiles/simulators/Makefile.verilator @@ -38,7 +38,7 @@ SIM_BUILD_FLAGS += -std=c++11 COMPILE_ARGS += --vpi --public-flat-rw --prefix Vtop -o $(TOPLEVEL) -LDFLAGS "-Wl,-rpath,$(LIB_DIR) -L$(LIB_DIR) -lcocotbvpi_verilator -lgpi -lcocotb -lgpilog -lcocotbutils" -$(SIM_BUILD)/Vtop.mk: $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(COCOTB_SHARE_DIR)/lib/verilator/verilator.cpp +$(SIM_BUILD)/Vtop.mk: $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(COCOTB_SHARE_DIR)/lib/verilator/verilator.cpp | $(SIM_BUILD) $(CMD) -cc --exe -Mdir $(SIM_BUILD) -DCOCOTB_SIM=1 --top-module $(TOPLEVEL) $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) $(COCOTB_SHARE_DIR)/lib/verilator/verilator.cpp # Compilation phase diff --git a/cocotb/share/makefiles/simulators/Makefile.xcelium b/cocotb/share/makefiles/simulators/Makefile.xcelium index e005c5f9..7f772b28 100644 --- a/cocotb/share/makefiles/simulators/Makefile.xcelium +++ b/cocotb/share/makefiles/simulators/Makefile.xcelium @@ -104,7 +104,7 @@ else $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") endif -$(COCOTB_RESULTS_FILE): $(SIM_BUILD) $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) +$(COCOTB_RESULTS_FILE): $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) | $(SIM_BUILD) -@rm -f $(COCOTB_RESULTS_FILE) set -o pipefail; \ From 74b829bb0f302c6eed11e4d3eb7096f2947caa1b Mon Sep 17 00:00:00 2001 From: Regis Cattenoz Date: Sat, 30 May 2020 18:03:46 +0200 Subject: [PATCH 2332/2656] Fix typo in scoreboard error message Scoreboard use to report message as : cocotb.result.TestFailure: Received transaction differed from expectedtransaction with as missing space. Just correcting adding a space. --- cocotb/scoreboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index 082007ea..e6518aa0 100755 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -161,7 +161,7 @@ def compare(self, got, exp, log, strict_type=True): pass log.warning("Difference:\n%s" % hexdiffs(strexp, strgot)) if self._imm: - raise TestFailure("Received transaction differed from expected" + raise TestFailure("Received transaction differed from expected " "transaction") else: # Don't want to fail the test From eb91b64e71a7743a54c6b81019fecdfb242c14cb Mon Sep 17 00:00:00 2001 From: Marlon James Date: Tue, 26 May 2020 14:59:02 -0700 Subject: [PATCH 2333/2656] Document non-working versions of Active-HDL --- documentation/source/simulator_support.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 3bef0cf7..33597fc8 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -164,6 +164,14 @@ In order to use this simulator, set :make:var:`SIM` to ``activehdl``: make SIM=activehdl +.. warning:: + + cocotb does not work with some versions of Active-HDL (see :issue:`1494`). + + Known affected versions: + + - Aldec Active-HDL 10.4a + - Aldec Active-HDL 10.5a .. _sim-questa: From 6f9bfe6fb3122aba961ceba68b9a8fd0e4a7312a Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Sat, 30 May 2020 22:05:47 +0100 Subject: [PATCH 2334/2656] Remove superfluous quotation marks from Makefile The `warning` function in Make doesn't need quotation marks; if they are still present, they show up in the output. Before: ``` $ make sim a/b/c/Makefile.inc:121: "Including Makefile.inc from a user makefile is a no-op and deprecated. Remove the Makefile.inc inclusion from your makef ... ``` After: ``` $ make sim a/b/c/Makefile.inc:121: Including Makefile.inc from a user makefile is a no-op and deprecated. Remove the Makefile.inc inclusion from your makef ... ``` --- cocotb/share/makefiles/Makefile.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/share/makefiles/Makefile.inc b/cocotb/share/makefiles/Makefile.inc index 71698d08..b2b43581 100644 --- a/cocotb/share/makefiles/Makefile.inc +++ b/cocotb/share/makefiles/Makefile.inc @@ -118,5 +118,5 @@ $(SIM_BUILD): regression: $(COCOTB_RESULTS_FILE) else - $(warning "Including Makefile.inc from a user makefile is a no-op and deprecated. Remove the Makefile.inc inclusion from your makefile, and only leave the Makefile.sim include.") + $(warning Including Makefile.inc from a user makefile is a no-op and deprecated. Remove the Makefile.inc inclusion from your makefile, and only leave the Makefile.sim include.) endif # COCOTB_MAKEFILE_INC_INCLUDED From e1a4ac479a4c90c664556f33b97f03cb9c4780e4 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Sat, 30 May 2020 23:39:31 +0100 Subject: [PATCH 2335/2656] Fix docstring typo --- cocotb/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 81380525..fd372ab5 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -128,7 +128,7 @@ def _reopen_stream_with_buffering(stream_name): def fork(coro): - """ Schedule a coroutine to be run concurrently. See :ref:`coroutines` for details on it's use. """ + """ Schedule a coroutine to be run concurrently. See :ref:`coroutines` for details on its use. """ return scheduler.add(coro) From 03ff02c96b2489944e2847140b155c83c2124def Mon Sep 17 00:00:00 2001 From: Matthias Seidel Date: Sun, 31 May 2020 11:35:43 +0200 Subject: [PATCH 2336/2656] xgmii: fix crash in monitor due to misuse of bytes (#1881) The monitor uses `.append`, so we need to store a mutable byte sequence like `bytearray` in `self._pkt`, not an immutable `bytes` object. `bytes` and `bytearray` are otherwise interchangeable, so this should be safe. --- cocotb/monitors/xgmii.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cocotb/monitors/xgmii.py b/cocotb/monitors/xgmii.py index 50fc1649..7d8499a0 100644 --- a/cocotb/monitors/xgmii.py +++ b/cocotb/monitors/xgmii.py @@ -114,7 +114,7 @@ def _add_payload(self, ctrl, bytes): " ".join(["%02X" % b for b in bytes])) self.log.info("ctrl = :" + " ".join(["%s" % str(c) for c in ctrl])) - self._pkt = b"" + self._pkt = bytearray() return False self._pkt.append(byte) @@ -123,7 +123,7 @@ def _add_payload(self, ctrl, bytes): @cocotb.coroutine def _monitor_recv(self): clk = RisingEdge(self.clock) - self._pkt = b"" + self._pkt = bytearray() while True: yield clk @@ -154,7 +154,7 @@ def _monitor_recv(self): self.log.error("Received a runt frame!") if len(self._pkt) < 12: self.log.error("No data to extract") - self._pkt = b"" + self._pkt = bytearray() continue preamble_sfd = self._pkt[0:7] @@ -164,7 +164,7 @@ def _monitor_recv(self): if preamble_sfd != _PREAMBLE_SFD: self.log.error("Got a frame with unknown preamble/SFD") self.log.error(hexdump(preamble_sfd)) - self._pkt = b"" + self._pkt = bytearray() continue expected_crc = struct.pack(" Date: Sun, 31 May 2020 04:37:09 -0500 Subject: [PATCH 2337/2656] Cleanup MANIFEST and include LICENSE (#1883) We didn't include the LICENSE file, the version file no longer exists, and the patterns on the inclusion of example was out of date and the examples wouldn't run. As a reminder, this file controls what makes it into the wheel and tarball from PyPI. --- MANIFEST.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 2d086f9a..6e63680c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,5 @@ -recursive-include cocotb/share *.c* *.h *.def Makefile.* Makefile -recursive-include examples *.v *.py Makefile *.tcl *.cfg -include version +recursive-include cocotb/share * +recursive-include examples * include README.md +include LICENSE include cocotb_build_libs.py From 3b80022b13bd1b5876b45158cfb8c7d064cf51a2 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Sun, 31 May 2020 18:51:38 +0100 Subject: [PATCH 2338/2656] Use logger.warning() instead of logger.warn() `.warn()` is deprecated and leads to deprecation warnings, see https://docs.python.org/3/library/logging.html#logging.Logger.warning. --- cocotb/scheduler.py | 2 +- cocotb/scoreboard.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 82c24a3a..a3034704 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -846,4 +846,4 @@ def cleanup(self): raise Exception("Cleanup() called outside of the main thread") for ext in self._pending_threads: - self.log.warn("Waiting for %s to exit", ext.thread) + self.log.warning("Waiting for %s to exit", ext.thread) diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index e6518aa0..6efc06f1 100755 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -82,8 +82,8 @@ def result(self): "than a list" % str(monitor)) continue if len(expected_output): - self.log.warn("Still expecting %d transactions on %s" % - (len(expected_output), str(monitor))) + self.log.warning("Still expecting %d transactions on %s" % + (len(expected_output), str(monitor))) for index, transaction in enumerate(expected_output): self.log.info("Expecting %d:\n%s" % (index, hexdump(str(transaction)))) From c48b3b71997748ab47fe64aefbcb50b98a3ee5d4 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Sun, 31 May 2020 12:58:14 +0100 Subject: [PATCH 2339/2656] Including the top-level Makefiles is outdated Including these makefiles doesn't work any more -- this functionality isn't deprecated, it's outdated. Users need to take action. Point them to the documentation to get that fixed on their side, which often also involves installing cocotb (instead of running it directly from the source tree). This also removes the fragile check for cocotb-config, which was used as a proxy for "is cocotb installed". The intention is to gently push such users towards the documentation, since they really need to read up on the updated installation instructions and cannot simply move along by going from error message to error message. --- makefiles/Makefile.inc | 14 ++++---------- makefiles/Makefile.sim | 15 ++++----------- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc index 6edc9d76..bc71307c 100644 --- a/makefiles/Makefile.inc +++ b/makefiles/Makefile.inc @@ -2,13 +2,7 @@ # Licensed under the Revised BSD License, see LICENSE for details. # SPDX-License-Identifier: BSD-3-Clause -COCOTB_CONFIG_CMD := $(shell :; command -v cocotb-config) -ifeq ($(COCOTB_CONFIG_CMD),) - $(error 'cocotb-config not found! Cocotb is not installed (or is installed but not on the PATH). \ - Please refer to https://cocotb.readthedocs.io/en/latest/quickstart.html#installing-cocotb \ - for installation instructions.') -endif - -$(error 'Deprecated cocotb file structure detected. \ - Please refer to https://cocotb.readthedocs.io/en/latest/quickstart.html#creating-a-makefile \ - for instructions.') +$(error Outdated use of cocotb detected. \ + Please install cocotb, and modify your makefile to \ + "include $$(shell cocotb-config --makefile)/Makefile.sim" instead. \ + Please refer to the documentation at https://docs.cocotb.org.) diff --git a/makefiles/Makefile.sim b/makefiles/Makefile.sim index 47866fec..bc71307c 100644 --- a/makefiles/Makefile.sim +++ b/makefiles/Makefile.sim @@ -2,14 +2,7 @@ # Licensed under the Revised BSD License, see LICENSE for details. # SPDX-License-Identifier: BSD-3-Clause -COCOTB_CONFIG_CMD := $(shell :; command -v cocotb-config) -ifeq ($(COCOTB_CONFIG_CMD),) - $(error 'cocotb-config not found! Cocotb is not installed (or is installed but not on the PATH). \ - Please refer to https://cocotb.readthedocs.io/en/latest/quickstart.html#installing-cocotb \ - for installation instructions.') -endif - -$(error 'Deprecated cocotb file structure detected. \ - Use "include $$(shell cocotb-config --makefile)/Makefile.sim" instead. \ - Please refer to https://cocotb.readthedocs.io/en/latest/quickstart.html#creating-a-makefile \ - for instructions.') +$(error Outdated use of cocotb detected. \ + Please install cocotb, and modify your makefile to \ + "include $$(shell cocotb-config --makefile)/Makefile.sim" instead. \ + Please refer to the documentation at https://docs.cocotb.org.) From 43421561b8f856d3a11dc958eb748521810bbaed Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Sun, 31 May 2020 22:45:39 +0100 Subject: [PATCH 2340/2656] Collect coverage for user code in a path containing the word cocotb The Python coverage collection (enabled with `COVERAGE=1`) excludes all paths containing `cocotb` in any form. For example, users put their code in in `/home/joe/myproject/cocotb-tests`, no coverage would be collected for their code. Change the file filter in the coverage collection to omit all code in the cocotb installation directory. --- cocotb/regression.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cocotb/regression.py b/cocotb/regression.py index 29c615b8..352b49ff 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -115,7 +115,9 @@ def __init__(self, dut: SimHandle, tests: Iterable[Test], hooks: Iterable[Hook]) if coverage is not None: self.log.info("Enabling coverage collection of Python code") - self._cov = coverage.coverage(branch=True, omit=["*cocotb*"]) + # Exclude cocotb itself from coverage collection. + cocotb_package_dir = os.path.dirname(__file__) + self._cov = coverage.coverage(branch=True, omit=["{}/*".format(cocotb_package_dir)]) self._cov.start() # Test Discovery From 3b47f72477257bcc5f5928f20588986821fd2023 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 24 May 2020 12:00:59 -0500 Subject: [PATCH 2341/2656] Change LOG_CRITICAL to LOG_ERROR in C code called by cocotb --- cocotb/share/lib/fli/FliObjHdl.cpp | 18 +++++++++--------- cocotb/share/lib/vpi/VpiCbHdl.cpp | 9 ++++++--- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/cocotb/share/lib/fli/FliObjHdl.cpp b/cocotb/share/lib/fli/FliObjHdl.cpp index e7d8f1f2..7ae9143a 100644 --- a/cocotb/share/lib/fli/FliObjHdl.cpp +++ b/cocotb/share/lib/fli/FliObjHdl.cpp @@ -83,7 +83,7 @@ int FliObjHdl::initialise(std::string &name, std::string &fq_name) m_num_elems = 1; break; default: - LOG_CRITICAL("Invalid object type for FliObjHdl. (%s (%s))", name.c_str(), get_type_str()); + LOG_ERROR("Invalid object type for FliObjHdl. (%s (%s))", name.c_str(), get_type_str()); return -1; } @@ -228,7 +228,7 @@ long FliEnumObjHdl::get_signal_value_long() int FliEnumObjHdl::set_signal_value(const long value, const gpi_set_action_t action) { if (action != GPI_DEPOSIT) { - LOG_CRITICAL("Force or release action not supported for FLI."); + LOG_ERROR("Force or release action not supported for FLI."); return -1; } @@ -269,7 +269,7 @@ int FliLogicObjHdl::initialise(std::string &name, std::string &fq_name) } break; default: - LOG_CRITICAL("Object type is not 'logic' for %s (%d)", name.c_str(), m_fli_type); + LOG_ERROR("Object type is not 'logic' for %s (%d)", name.c_str(), m_fli_type); return -1; } @@ -306,7 +306,7 @@ const char* FliLogicObjHdl::get_signal_value_binstr() } break; default: - LOG_CRITICAL("Object type is not 'logic' for %s (%d)", m_name.c_str(), m_fli_type); + LOG_ERROR("Object type is not 'logic' for %s (%d)", m_name.c_str(), m_fli_type); return NULL; } @@ -318,7 +318,7 @@ const char* FliLogicObjHdl::get_signal_value_binstr() int FliLogicObjHdl::set_signal_value(const long value, const gpi_set_action_t action) { if (action != GPI_DEPOSIT) { - LOG_CRITICAL("Force or release action not supported for FLI."); + LOG_ERROR("Force or release action not supported for FLI."); return -1; } @@ -351,7 +351,7 @@ int FliLogicObjHdl::set_signal_value(const long value, const gpi_set_action_t ac int FliLogicObjHdl::set_signal_value_binstr(std::string &value, const gpi_set_action_t action) { if (action != GPI_DEPOSIT) { - LOG_CRITICAL("Force or release action not supported for FLI."); + LOG_ERROR("Force or release action not supported for FLI."); return -1; } @@ -435,7 +435,7 @@ long FliIntObjHdl::get_signal_value_long() int FliIntObjHdl::set_signal_value(const long value, const gpi_set_action_t action) { if (action != GPI_DEPOSIT) { - LOG_CRITICAL("Force or release action not supported for FLI."); + LOG_ERROR("Force or release action not supported for FLI."); return -1; } @@ -474,7 +474,7 @@ double FliRealObjHdl::get_signal_value_real() int FliRealObjHdl::set_signal_value(const double value, const gpi_set_action_t action) { if (action != GPI_DEPOSIT) { - LOG_CRITICAL("Force or release action not supported for FLI."); + LOG_ERROR("Force or release action not supported for FLI."); return -1; } @@ -522,7 +522,7 @@ const char* FliStringObjHdl::get_signal_value_str() int FliStringObjHdl::set_signal_value_str(std::string &value, const gpi_set_action_t action) { if (action != GPI_DEPOSIT) { - LOG_CRITICAL("Force or release action not supported for FLI."); + LOG_ERROR("Force or release action not supported for FLI."); return -1; } diff --git a/cocotb/share/lib/vpi/VpiCbHdl.cpp b/cocotb/share/lib/vpi/VpiCbHdl.cpp index dd2665c8..0b78c609 100644 --- a/cocotb/share/lib/vpi/VpiCbHdl.cpp +++ b/cocotb/share/lib/vpi/VpiCbHdl.cpp @@ -164,7 +164,8 @@ int VpiArrayObjHdl::initialise(std::string &name, std::string &fq_name) { } if (rangeHdl == NULL) { - LOG_CRITICAL("Unable to get range for indexable object"); + LOG_ERROR("Unable to get range for indexable object"); + return -1; } else { vpi_free_object(iter); // Need to free iterator since exited early @@ -185,7 +186,8 @@ int VpiArrayObjHdl::initialise(std::string &name, std::string &fq_name) { check_vpi_error(); m_range_right = val.value.integer; } else { - LOG_CRITICAL("Unable to get range for indexable object"); + LOG_ERROR("Unable to get range for indexable object"); + return -1; } /* vpiSize will return a size that is incorrect for multi-dimensional arrays so use the range @@ -260,7 +262,8 @@ int VpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { check_vpi_error(); m_range_right = val.value.integer; } else { - LOG_CRITICAL("Unable to get range for indexable object"); + LOG_ERROR("Unable to get range for indexable object"); + return -1; } } else { From c72e4c381b80f27d9b28596fa72616d21f7bdcab Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 25 May 2020 12:51:41 -0500 Subject: [PATCH 2342/2656] Make GPI_CRITICAL not exit --- cocotb/share/include/gpi_logging.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cocotb/share/include/gpi_logging.h b/cocotb/share/include/gpi_logging.h index 118911cc..86b9cec3 100644 --- a/cocotb/share/include/gpi_logging.h +++ b/cocotb/share/include/gpi_logging.h @@ -53,10 +53,7 @@ enum gpi_log_levels { #define LOG_INFO(...) gpi_log("cocotb.gpi", GPIInfo, __FILE__, __func__, __LINE__, __VA_ARGS__); #define LOG_WARN(...) gpi_log("cocotb.gpi", GPIWarning, __FILE__, __func__, __LINE__, __VA_ARGS__); #define LOG_ERROR(...) gpi_log("cocotb.gpi", GPIError, __FILE__, __func__, __LINE__, __VA_ARGS__); -#define LOG_CRITICAL(...) do { \ - gpi_log("cocotb.gpi", GPICritical, __FILE__, __func__, __LINE__, __VA_ARGS__); \ - exit(1); \ -} while (0) +#define LOG_CRITICAL(...) gpi_log("cocotb.gpi", GPICritical, __FILE__, __func__, __LINE__, __VA_ARGS__); void set_log_handler(void *handler); void clear_log_handler(void); From 2a2fe8c07a31bf07cea8a0a6e05651e29f38ca18 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 24 May 2020 23:30:51 -0500 Subject: [PATCH 2343/2656] Replace usage of LOG_CRITICAL in simulator-called code --- cocotb/share/lib/fli/FliImpl.cpp | 2 ++ cocotb/share/lib/vhpi/VhpiImpl.cpp | 7 +++++-- cocotb/share/lib/vpi/VpiImpl.cpp | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/cocotb/share/lib/fli/FliImpl.cpp b/cocotb/share/lib/fli/FliImpl.cpp index e1bffe04..aafd7368 100644 --- a/cocotb/share/lib/fli/FliImpl.cpp +++ b/cocotb/share/lib/fli/FliImpl.cpp @@ -1016,6 +1016,8 @@ void handle_fli_callback(void *data) if (!cb_hdl) { LOG_CRITICAL("FLI: Callback data corrupted: ABORTING"); + gpi_embed_end(); + return; } gpi_cb_state_e old_state = cb_hdl->get_call_state(); diff --git a/cocotb/share/lib/vhpi/VhpiImpl.cpp b/cocotb/share/lib/vhpi/VhpiImpl.cpp index ac3d0fb0..ca587b76 100644 --- a/cocotb/share/lib/vhpi/VhpiImpl.cpp +++ b/cocotb/share/lib/vhpi/VhpiImpl.cpp @@ -932,8 +932,11 @@ void handle_vhpi_callback(const vhpiCbDataT *cb_data) { VhpiCbHdl *cb_hdl = (VhpiCbHdl*)cb_data->user_data; - if (!cb_hdl) - LOG_CRITICAL("VHPI: Callback data corrupted"); + if (!cb_hdl) { + LOG_CRITICAL("VHPI: Callback data corrupted: ABORTING"); + gpi_embed_end(); + return; + } gpi_cb_state_e old_state = cb_hdl->get_call_state(); diff --git a/cocotb/share/lib/vpi/VpiImpl.cpp b/cocotb/share/lib/vpi/VpiImpl.cpp index cdfeee95..3db9ae7c 100644 --- a/cocotb/share/lib/vpi/VpiImpl.cpp +++ b/cocotb/share/lib/vpi/VpiImpl.cpp @@ -572,6 +572,8 @@ int32_t handle_vpi_callback(p_cb_data cb_data) if (!cb_hdl) { LOG_CRITICAL("VPI: Callback data corrupted: ABORTING"); + gpi_embed_end(); + return -1; } gpi_cb_state_e old_state = cb_hdl->get_call_state(); From 97e28803308d09d2bd885e4279d5036ef9861d42 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 24 May 2020 23:34:42 -0500 Subject: [PATCH 2344/2656] Cleanup usage of LOG_CRITICAL in cleanup_callback in VPI --- cocotb/share/lib/vpi/VpiCbHdl.cpp | 12 ++++++++---- cocotb/share/lib/vpi/VpiImpl.cpp | 9 +++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/cocotb/share/lib/vpi/VpiCbHdl.cpp b/cocotb/share/lib/vpi/VpiCbHdl.cpp index 0b78c609..7f305e50 100644 --- a/cocotb/share/lib/vpi/VpiCbHdl.cpp +++ b/cocotb/share/lib/vpi/VpiCbHdl.cpp @@ -96,11 +96,13 @@ int VpiCbHdl::cleanup_callback() if (m_state == GPI_PRIMED) { if (!m_obj_hdl) { - LOG_CRITICAL("VPI: passed a NULL pointer : ABORTING"); + LOG_ERROR("VPI: passed a NULL pointer"); + return -1; } if (!(vpi_remove_cb(get_handle()))) { - LOG_CRITICAL("VPI: unable to remove callback : ABORTING"); + LOG_ERROR("VPI: unable to remove callback"); + return -1; } check_vpi_error(); @@ -108,7 +110,8 @@ int VpiCbHdl::cleanup_callback() #ifndef MODELSIM /* This is disabled for now, causes a small leak going to put back in */ if (!(vpi_free_object(get_handle()))) { - LOG_CRITICAL("VPI: unable to free handle : ABORTING"); + LOG_ERROR("VPI: unable to free handle"); + return -1; } #endif } @@ -461,7 +464,8 @@ int VpiValueCbHdl::cleanup_callback() /* This is a recurring callback so just remove when * not wanted */ if (!(vpi_remove_cb(get_handle()))) { - LOG_CRITICAL("VPI: unbale to remove callback : ABORTING"); + LOG_ERROR("VPI: unable to remove callback"); + return -1; } m_obj_hdl = NULL; diff --git a/cocotb/share/lib/vpi/VpiImpl.cpp b/cocotb/share/lib/vpi/VpiImpl.cpp index 3db9ae7c..d945fa67 100644 --- a/cocotb/share/lib/vpi/VpiImpl.cpp +++ b/cocotb/share/lib/vpi/VpiImpl.cpp @@ -543,8 +543,7 @@ GpiCbHdl *VpiImpl::register_nexttime_callback() int VpiImpl::deregister_callback(GpiCbHdl *gpi_hdl) { - gpi_hdl->cleanup_callback(); - return 0; + return gpi_hdl->cleanup_callback(); } // If the Python world wants things to shut down then unregister @@ -587,13 +586,15 @@ int32_t handle_vpi_callback(p_cb_data cb_data) /* We have re-primed in the handler */ if (new_state != GPI_PRIMED) - if (cb_hdl->cleanup_callback()) + if (cb_hdl->cleanup_callback()) { delete cb_hdl; + } } else { /* Issue #188: This is a work around for a modelsim */ - if (cb_hdl->cleanup_callback()) + if (cb_hdl->cleanup_callback()) { delete cb_hdl; + } } return rv; From 8bb292145bd7bc5f1b94abf5ff4b287eba346f40 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 24 May 2020 13:05:19 -0500 Subject: [PATCH 2345/2656] Prevent AXI Lite tests from running when TOPLEVEL_LANG isn't verilog --- examples/axi_lite_slave/tests/Makefile | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/examples/axi_lite_slave/tests/Makefile b/examples/axi_lite_slave/tests/Makefile index 7d8f50a3..e32300c8 100644 --- a/examples/axi_lite_slave/tests/Makefile +++ b/examples/axi_lite_slave/tests/Makefile @@ -1,5 +1,13 @@ TOPLEVEL_LANG ?= verilog + +ifneq ($(TOPLEVEL_LANG),verilog) +all: + @echo "Skipping; this example does not support VHDL at the top-level" +clean:: + +else + PWD=$(shell pwd) TOPDIR=$(PWD)/.. PYTHONPATH := ./model:$(PYTHONPATH) @@ -31,3 +39,4 @@ MODULE=test_axi_lite_slave include $(shell cocotb-config --makefiles)/Makefile.sim +endif From 49c71718d0a6696052a9a80afaa50abace84e591 Mon Sep 17 00:00:00 2001 From: Ben Rosser Date: Sat, 6 Jun 2020 07:41:52 -0400 Subject: [PATCH 2346/2656] Replace xml.etree.cElementTree with ElementTree for Python 3.9 (#1910) The class `xml.etree.cElementTree` has been deprecated since Python 3.3 in favor of just `xml.etree.ElementTree`, and as of 3.9 has been removed. https://docs.python.org/3.9/whatsnew/3.9.html#removed --- bin/combine_results.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/combine_results.py b/bin/combine_results.py index 49247a25..db4c7650 100755 --- a/bin/combine_results.py +++ b/bin/combine_results.py @@ -8,7 +8,7 @@ import os import sys import argparse -from xml.etree import cElementTree as ET +from xml.etree import ElementTree as ET def find_all(name, path): From dd88c00c9fa82d94d498c0c89b9176edb8990d67 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Mon, 8 Jun 2020 19:02:10 +0100 Subject: [PATCH 2347/2656] Force an up-to-date virtualenv to avoid CI warnings (#1915) The old 16.0.0 release of virtualenv emits `DeprecationWarning: 'U' mode is deprecated` from `site.py` at startup. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8f4919b1..f1b88f6e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,7 +45,7 @@ jobs: - sudo apt-get -y install gperf swig - if [[ ! -e "./iverilog/README.txt" ]]; then rm -rf iverilog; git clone https://github.com/steveicarus/iverilog.git --depth=1 --branch v10_3; fi - cd iverilog && autoconf && ./configure && make -j2 && sudo make install && cd .. - - pip install tox + - pip install --upgrade tox virtualenv - pyenv global system $TRAVIS_PYTHON_VERSION script: - echo 'Running cocotb tests with Python 3.7 ...' && echo -en 'travis_fold:start:cocotb_py37' From 168877235ba5cb007b8f9c09766a882d2d525a74 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Tue, 2 Jun 2020 09:34:43 +0100 Subject: [PATCH 2348/2656] Provide better user experience for setting log levels The log level for the "cocotb" logger can be set with the environment variable COCOTB_LOG_LEVEL. This variable is documented to accept only uppercase log level names, and that works. However, the checking code was slightly fragile. If the `logging` module happened to have an attribute named like a value of COCOTB_LOG_LEVEL, the error checking wouldn't catch it and instead a TypeError would be thrown. See #1888 for a full stacktrace. Hitting this behavior is quite easy by passing in `debug` instead of `DEBUG`, for example. This patch improves this behavior by: - Always converting log level names passed in through the environment variable to upper case. That's a convenience function which is intentionally not documented to encourage our users to do the right thing (TM). It's tested, however. - Strengthening the error checking by relying in `logger.setLevel()`, which accepts a string value. - Instead of printing an error and continuing with an INFO log level in case of an invalid environment variable, raise a ValueError and force the user to fix their Makefile/environment. - Making the error message more actionable by mentioning (a) where the invalid value is coming from, (b) showing which value is used instead, and (c) giving other valid options to select from. - Adding a test. Fixes #1888 --- cocotb/log.py | 24 +++++++---- .../source/newsfragments/1898.change.rst | 2 + tests/test_cases/test_cocotb/test_logging.py | 41 +++++++++++++++++++ 3 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 documentation/source/newsfragments/1898.change.rst diff --git a/cocotb/log.py b/cocotb/log.py index 5c72e973..9b75309c 100644 --- a/cocotb/log.py +++ b/cocotb/log.py @@ -52,6 +52,9 @@ _LINENO_CHARS = 4 # noqa _FUNCNAME_CHARS = 31 # noqa +# Default log level if not overwritten by the user. +_COCOTB_LOG_LEVEL_DEFAULT = "INFO" + def default_config(): """ Apply the default cocotb log formatting to the root logger. @@ -86,18 +89,25 @@ def default_config(): # apply level settings for cocotb log = logging.getLogger('cocotb') - level = os.getenv("COCOTB_LOG_LEVEL", "INFO") + + try: + # All log levels are upper case, convert the user input for convenience. + level = os.environ["COCOTB_LOG_LEVEL"].upper() + except KeyError: + level = _COCOTB_LOG_LEVEL_DEFAULT + try: - _default_log = getattr(logging, level) - except AttributeError: - log.error("Unable to set logging level to %r" % level) - _default_log = logging.INFO - log.setLevel(_default_log) + log.setLevel(level) + except ValueError: + valid_levels = ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG') + raise ValueError("Invalid log level %r passed through the " + "COCOTB_LOG_LEVEL environment variable. Valid log " + "levels: %s" % (level, ', '.join(valid_levels))) # Notify GPI of log level, which it uses as an optimization to avoid # calling into Python. from cocotb import simulator - simulator.log_level(_default_log) + simulator.log_level(log.getEffectiveLevel()) class SimBaseLog(logging.getLoggerClass()): diff --git a/documentation/source/newsfragments/1898.change.rst b/documentation/source/newsfragments/1898.change.rst new file mode 100644 index 00000000..6ddaf5f8 --- /dev/null +++ b/documentation/source/newsfragments/1898.change.rst @@ -0,0 +1,2 @@ +Invalid values of the environment variable :envvar:`COCOTB_LOG_LEVEL` are no longer ignored. +They now raise an exception with instructions how to fix the problem. diff --git a/tests/test_cases/test_cocotb/test_logging.py b/tests/test_cases/test_cocotb/test_logging.py index 07d2583b..fc4f3ca4 100644 --- a/tests/test_cases/test_cocotb/test_logging.py +++ b/tests/test_cases/test_cocotb/test_logging.py @@ -4,9 +4,14 @@ """ Tests for the cocotb logger """ + import cocotb +import cocotb.log from cocotb.triggers import Timer + +from common import assert_raises import logging +import os class StrCallCounter(object): @@ -39,3 +44,39 @@ def test_logging_with_args(dut): dut._log.warning("Testing multiple line\nmessage") yield Timer(100) # Make it do something with time + + +@cocotb.test() +def test_logging_default_config(dut): + # The cocotb.log module is shadowed by an instance of + # cocotb.log.SimBaseLog() + from cocotb.log import default_config as log_default_config + + cocotb_log = logging.getLogger('cocotb') + + # Save pre-test configuration + log_level_prev = cocotb_log.level + os_environ_prev = os.environ.copy() + + try: + # Set a valid log level + os.environ['COCOTB_LOG_LEVEL'] = 'DEBUG' + log_default_config() + assert cocotb_log.level == logging.DEBUG, cocotb_log.level + + # Try to set log level to an invalid log level + os.environ['COCOTB_LOG_LEVEL'] = 'INVALID_LOG_LEVEL' + with assert_raises(ValueError): + log_default_config() + + # Try to set log level to a valid log level with wrong capitalization + os.environ['COCOTB_LOG_LEVEL'] = 'error' + log_default_config() + assert cocotb_log.level == logging.ERROR, cocotb_log.level + + finally: + # Restore pre-test configuration + os.environ = os_environ_prev + cocotb_log.level = log_level_prev + + yield Timer(1) From cf36c4b3ba1c2dac848bc58f5a7de60d1276adcf Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Tue, 2 Jun 2020 21:44:00 +0100 Subject: [PATCH 2349/2656] Fix typo in newsfragment --- documentation/source/newsfragments/1843.change.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/newsfragments/1843.change.rst b/documentation/source/newsfragments/1843.change.rst index 33ec042a..1b04e14f 100644 --- a/documentation/source/newsfragments/1843.change.rst +++ b/documentation/source/newsfragments/1843.change.rst @@ -1 +1 @@ -Certain methods on the :mod:`cocotb.simulator` Python module now throw a :exc`RuntimeError` when no simulator is present, making it safe to use :mod:`cocotb` without a simulator present. +Certain methods on the :mod:`cocotb.simulator` Python module now throw a :exc:`RuntimeError` when no simulator is present, making it safe to use :mod:`cocotb` without a simulator present. From 4d9c1fab62059b68f437e966496cab211ddc5c99 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 6 Jun 2020 14:46:48 +0100 Subject: [PATCH 2350/2656] Collect coverage information for the pytest tests. This does not attempt to collect coverage for the simulator tests. --- .travis.yml | 6 +++++- README.md | 1 + setup.cfg | 2 +- tox.ini | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index f1b88f6e..8d750eb0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,12 +45,14 @@ jobs: - sudo apt-get -y install gperf swig - if [[ ! -e "./iverilog/README.txt" ]]; then rm -rf iverilog; git clone https://github.com/steveicarus/iverilog.git --depth=1 --branch v10_3; fi - cd iverilog && autoconf && ./configure && make -j2 && sudo make install && cd .. - - pip install --upgrade tox virtualenv + - pip install --upgrade tox virtualenv codecov - pyenv global system $TRAVIS_PYTHON_VERSION script: - echo 'Running cocotb tests with Python 3.7 ...' && echo -en 'travis_fold:start:cocotb_py37' - tox -e py37 - echo 'travis_fold:end:cocotb_py37' + after_success: &travis_linux_post + - codecov - stage: SimTests python: 3.8 @@ -59,6 +61,7 @@ jobs: - echo 'Running cocotb tests with Python 3.8 ...' && echo -en 'travis_fold:start:cocotb_py38' - tox -e py38 - echo 'travis_fold:end:cocotb_py38' + after_success: *travis_linux_post - stage: SimTests python: 3.6.7 @@ -67,6 +70,7 @@ jobs: - echo 'Running cocotb tests with Python 3.6.7 ...' && echo -en 'travis_fold:start:cocotb_py36' - tox -e py36 - echo 'travis_fold:end:cocotb_py36' + after_success: *travis_linux_post - stage: SimTests python: 3.7 diff --git a/README.md b/README.md index 2449160c..d01c1f62 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![Build Status](https://travis-ci.org/cocotb/cocotb.svg?branch=master)](https://travis-ci.org/cocotb/cocotb) [![PyPI](https://img.shields.io/pypi/dm/cocotb.svg?label=PyPI%20downloads)](https://pypi.org/project/cocotb/) [![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/cocotb/cocotb) +[![codecov](https://codecov.io/gh/cocotb/cocotb/branch/master/graph/badge.svg)](https://codecov.io/gh/cocotb/cocotb) * Read the [documentation](https://docs.cocotb.org) * Get involved: diff --git a/setup.cfg b/setup.cfg index f7da9e25..9078ed71 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,7 +23,7 @@ ignore = W504 # line break after binary operator [tool:pytest] -addopts = -v +addopts = -v --cov=cocotb --cov-branch testpaths = tests/pytest confcutdir = tests/pytest # log_cli = true diff --git a/tox.ini b/tox.ini index 887f794c..31249264 100644 --- a/tox.ini +++ b/tox.ini @@ -15,6 +15,7 @@ deps = coverage xunitparser pytest + pytest-cov commands = pytest From dcee54483e4ca6fc6bbc7b4aeb54fb0080c8453e Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 6 Jun 2020 15:07:43 +0100 Subject: [PATCH 2351/2656] Use an editable install to test cocotb Hopefully a non-editable install is tested by a different CI run. This is necessary for coverage collection to know which files to include. --- tox.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tox.ini b/tox.ini index 31249264..5a076481 100644 --- a/tox.ini +++ b/tox.ini @@ -24,6 +24,9 @@ commands = whitelist_externals = make +# needed for coverage to work +usedevelop=True + [testenv:py3_mingw] install_command = python -m pip install --global-option build_ext --global-option --compiler=mingw32 {opts} {packages} From 40f4d343630727916ef9a55e2973e48d2dc39733 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Mon, 8 Jun 2020 19:07:32 +0100 Subject: [PATCH 2352/2656] Add a very brief note on using tox. I don't use it, so can't actually comment beyond what I can guess from CI. --- CONTRIBUTING.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ed88f027..7c3fe4b6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -85,6 +85,13 @@ All changes which should go into the main codebase of cocotb must follow this se # SPDX-License-Identifier: CC0-1.0 ``` +Running tests locally +--------------------- + +Our tests are managed by `tox`, which runs both `pytest` and our system of makefiles. +This exercises the contents of both the `tests` and `examples` directories. + + Managing of Issues and Pull Requests ------------------------------------ From be15494e35fdea52825ad7633b9c7dadfc566cfd Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Tue, 9 Jun 2020 23:32:09 +0100 Subject: [PATCH 2353/2656] Fix duplicated requirements (#1919) --- documentation/requirements.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/documentation/requirements.txt b/documentation/requirements.txt index 492c36cd..4b7c2c92 100644 --- a/documentation/requirements.txt +++ b/documentation/requirements.txt @@ -12,5 +12,3 @@ sphinx-argparse towncrier IPython sphinx-tabs -IPython -sphinx-tabs From 17a70057338288d92cb0c5a0752794b8786ca0cb Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Tue, 9 Jun 2020 21:08:38 +0100 Subject: [PATCH 2354/2656] Do not fail tests for DeprecationWarnings in virtualenv Users of `tests/Makefile` get a Python warning setting which fails hard in case of any warning, including DeprecationWarnings from third-party libraries. This is problematic for code using virtualenv, such as our tox-based testing infrastructure, where we have no way to work around such warnings (except for updating dependencies) as they are "injected" into our environment. --- tests/Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) mode change 100644 => 100755 tests/Makefile diff --git a/tests/Makefile b/tests/Makefile old mode 100644 new mode 100755 index 935cbc0f..f32d13b1 --- a/tests/Makefile +++ b/tests/Makefile @@ -25,8 +25,9 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -# distutils warnings are caused by an old version of virtualenv in travis -export PYTHONWARNINGS = error,ignore::DeprecationWarning:distutils +# Do not fail on DeprecationWarning caused by virtualenv, which might come from +# the distutils or site modules. +export PYTHONWARNINGS = error,ignore::DeprecationWarning:distutils,ignore::DeprecationWarning:site REGRESSIONS := $(shell ls test_cases/) From d25d9f257ec37bfd23f5e0992a14794294abce30 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Tue, 9 Jun 2020 22:15:45 -0500 Subject: [PATCH 2355/2656] Revert "Update Timer so it doesn't call into the simulator" This reverts commit 0405da1750d88f813af28c7228e50ec088da6178. We don't need to worry about this now that #1843 is in. This change made it so invalid waits threw exceptions in the scheduler, instead of where the Timer was instanced. --- cocotb/triggers.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index d039e195..064d4805 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -33,7 +33,7 @@ from cocotb import simulator from cocotb.log import SimLog from cocotb.utils import ( - get_sim_steps, ParametrizedSingleton, + get_sim_steps, get_time_from_sim_steps, ParametrizedSingleton, lazy_property, remove_traceback_frames, ) from cocotb import outcomes @@ -196,13 +196,7 @@ def __init__(self, time_ps, units=None): :func:`~cocotb.utils.get_sim_steps` """ GPITrigger.__init__(self) - self._time_ps = time_ps - self._units = units - - @lazy_property - def sim_steps(self): - # lazy so we don't call into the simulator until we need to - return get_sim_steps(self._time_ps, self._units) + self.sim_steps = get_sim_steps(time_ps, units) def prime(self, callback): """Register for a timed callback.""" @@ -214,10 +208,9 @@ def prime(self, callback): GPITrigger.prime(self, callback) def __repr__(self): - return "<{} of {:1.2f}{} at {}>".format( + return "<{} of {:1.2f}ps at {}>".format( type(self).__qualname__, - self._time_ps, - self._units, + get_time_from_sim_steps(self.sim_steps, units='ps'), _pointer_str(self) ) From 43c2bc6f79e60fec03693f4285351a179970f1ca Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Thu, 4 Jun 2020 22:16:35 -0500 Subject: [PATCH 2356/2656] Add +acc to default VHDL compile options for Questa This is required for regressions to pass and is already default for Verilog compilation. --- cocotb/share/makefiles/simulators/Makefile.questa | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.questa b/cocotb/share/makefiles/simulators/Makefile.questa index 8c6e88a0..d55ad365 100644 --- a/cocotb/share/makefiles/simulators/Makefile.questa +++ b/cocotb/share/makefiles/simulators/Makefile.questa @@ -47,9 +47,11 @@ RTL_LIBRARY ?= work TOPLEVEL := "$(RTL_LIBRARY).$(TOPLEVEL)" ifndef VLOG_ARGS - VLOG_ARGS = -timescale $(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) -mfcu +acc + VLOG_ARGS = -timescale $(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) -mfcu endif +COMPILE_ARGS += +acc + ifdef VERILOG_INCLUDE_DIRS VLOG_ARGS += +incdir+$(VERILOG_INCLUDE_DIRS) endif From 22572657792c2afca4d3cc4b8f94d367b3b13b5d Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Wed, 10 Jun 2020 18:13:05 +0100 Subject: [PATCH 2357/2656] Bump Sphinx to 3.1.0 (#1923) This release of sphinx contains a bunch of improvements to autodoc. --- documentation/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/requirements.txt b/documentation/requirements.txt index 4b7c2c92..744411ef 100644 --- a/documentation/requirements.txt +++ b/documentation/requirements.txt @@ -1,6 +1,6 @@ # Python dependencies to build the documentation -Sphinx ~= 3.0 +Sphinx ~= 3.1 breathe sphinx-rtd-theme cairosvg From fe40295dfc26b7ba8649a4df1ec4229cc661748d Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Thu, 11 Jun 2020 09:50:05 -0500 Subject: [PATCH 2358/2656] Add TOPLEVEL_LANG passthrough to tox --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 5a076481..d0960179 100644 --- a/tox.ini +++ b/tox.ini @@ -7,6 +7,7 @@ setenv = CXXFLAGS = -Werror passenv = SIM + TOPLEVEL_LANG ALDEC_LICENSE_FILE SYNOPSYS_LICENSE_FILE LM_LICENSE_FILE From 00ee3ce7c5e4eb5de4aad13078f17b7d435e07fa Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Thu, 11 Jun 2020 09:50:45 -0500 Subject: [PATCH 2359/2656] Supply TOPLEVEL_LANG=vhdl to GHDL CI run --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8d750eb0..f4cb1e2c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -105,7 +105,7 @@ jobs: - stage: SimTests python: 3.7 - env: SIM=ghdl + env: SIM=ghdl TOPLEVEL_LANG=vhdl before_install: - sudo apt-get install gnat - git clone https://github.com/ghdl/ghdl.git --depth=1 --branch v0.36 @@ -132,4 +132,4 @@ jobs: allow_failures: - stage: SimTests python: 3.7 - env: SIM=ghdl + env: SIM=ghdl TOPLEVEL_LANG=vhdl From 95dca7387ecad5c7ed4ff9deda94f1e250965ac6 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Thu, 11 Jun 2020 10:03:23 -0500 Subject: [PATCH 2360/2656] Add blurb to CONTRIBUTING.md about regression env vars --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7c3fe4b6..3f26e277 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -90,6 +90,7 @@ Running tests locally Our tests are managed by `tox`, which runs both `pytest` and our system of makefiles. This exercises the contents of both the `tests` and `examples` directories. +`tox` supports the usage of the environment variables `SIM` and `TOPLEVEL_LANG` to direct how to run the regression. Managing of Issues and Pull Requests From c42bbe488f4f76a0628d99b358134e2a50c33fd6 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 22 May 2020 00:12:00 -0500 Subject: [PATCH 2361/2656] Don't install unneeded objects with python package --- setup.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 91255d04..3d11bfa6 100755 --- a/setup.py +++ b/setup.py @@ -97,8 +97,13 @@ def package_files(directory): install_requires=[], python_requires='>=3.5', packages=find_packages(), - include_package_data=True, - package_data={'cocotb': package_files('cocotb/share')}, + package_data={ + 'cocotb': ( + package_files('cocotb/share/makefiles') + # noqa: W504 + package_files('cocotb/share/include') + # noqa: W504 + package_files('cocotb/share/def') + ) + }, ext_modules=get_ext(), entry_points={ 'console_scripts': [ From 92a9bcc3c7bce5c66c74b196900bed449e887afd Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Thu, 11 Jun 2020 18:59:37 -0500 Subject: [PATCH 2362/2656] Add Python 3.9 to Travis CI --- .travis.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.travis.yml b/.travis.yml index f4cb1e2c..2a001042 100644 --- a/.travis.yml +++ b/.travis.yml @@ -72,6 +72,15 @@ jobs: - echo 'travis_fold:end:cocotb_py36' after_success: *travis_linux_post + - stage: SimTests + python: 3.9-dev + before_install: *travis_linx_prep + script: + - echo 'Running cocotb tests with Python 3.9-dev ...' && echo -en 'travis_fold:start:cocotb_py39' + - tox -e py39 + - echo 'travis_fold:end:cocotb_py39' + after_success: *travis_linux_post + - stage: SimTests python: 3.7 os: windows From 6faabf859cdb8707f798b1d949dd9d5f43a089eb Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Thu, 11 Jun 2020 19:33:28 -0500 Subject: [PATCH 2363/2656] Remove unnecessary deprecated C API call to support Python 3.9 This call was probably never necessary, especially since the next call was to PyEval_SaveThread. According to the Python documentation "It is not needed before calling PyEval_SaveThread()..." --- cocotb/share/lib/embed/gpi_embed.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/cocotb/share/lib/embed/gpi_embed.cpp b/cocotb/share/lib/embed/gpi_embed.cpp index 37d9d330..6c7a02a2 100644 --- a/cocotb/share/lib/embed/gpi_embed.cpp +++ b/cocotb/share/lib/embed/gpi_embed.cpp @@ -131,7 +131,6 @@ extern "C" void embed_init_python(void) set_program_name_in_venv(); Py_Initialize(); /* Initialize the interpreter */ PySys_SetArgvEx(1, argv, 0); - PyEval_InitThreads(); /* Create (and acquire) the interpreter lock */ /* Swap out and return current thread state and release the GIL */ gtstate = PyEval_SaveThread(); From 04c8f7be823f62537638e3d754b2d9ba21bd4790 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 12 Jun 2020 08:06:10 -0500 Subject: [PATCH 2364/2656] Add Python 3.9 as a supported version to setup.py --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 3d11bfa6..1d7ebbd7 100755 --- a/setup.py +++ b/setup.py @@ -117,6 +117,7 @@ def package_files(directory): "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", "License :: OSI Approved :: BSD License", "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", ], From 8ba2335d941933f73749c2af01843fc53e9f3c1f Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 12 Jun 2020 09:50:58 -0500 Subject: [PATCH 2365/2656] Document TOPLEVEL_LANG in makefile variable reference (#1924) --- documentation/source/building.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/documentation/source/building.rst b/documentation/source/building.rst index 1a8bc99d..4da9c363 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -221,6 +221,14 @@ Makefile-based Test Scripts Set this to 1 to enable wave traces dump for the Aldec Riviera-PRO and Mentor Graphics Questa simulators. To get wave traces in Icarus Verilog see :ref:`sim-icarus-waveforms`. +.. make:var:: TOPLEVEL_LANG + + Used to inform the makefile scripts which HDL language the top-level design element is written in. + Currently it supports the values ``verilog`` for Verilog or SystemVerilog tops, and ``vhdl`` for VHDL tops. + This is used by simulators that support more than one interface (VPI, VHPI, or FLI) to select the appropriate interface to start cocotb. + + The variable is also made available to cocotb tests conveniently as :data:`cocotb.LANGUAGE`. + .. make:var:: VERILOG_SOURCES A list of the Verilog source files to include. From ab5eed3481a6cd771a978944b497dd24122651d8 Mon Sep 17 00:00:00 2001 From: Matthias Seidel Date: Fri, 12 Jun 2020 17:01:39 +0200 Subject: [PATCH 2366/2656] XGMII Driver: fix corrupted first word (#1905) The `_XGMII_Bus` helper class set its value to `IDLE` during the `__init__`, but this value is never read during initialization of `XGMII`. Since reading the value clears the internal `_integer` buffer of the helper class, the first word of the first transaction (actually the PREAMBLE and START byte) is OR'ed with the IDLE value, which corrupted the transmission. Using the existing `idle()` helper ensures that the internal buffer is correctly cleared. --- cocotb/drivers/xgmii.py | 5 +---- documentation/source/newsfragments/1905.bugfix.rst | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) create mode 100644 documentation/source/newsfragments/1905.bugfix.rst diff --git a/cocotb/drivers/xgmii.py b/cocotb/drivers/xgmii.py index 20d84c20..a165d938 100644 --- a/cocotb/drivers/xgmii.py +++ b/cocotb/drivers/xgmii.py @@ -78,10 +78,6 @@ def __init__(self, nbytes: int, interleaved: bool = True): self._interleaved = interleaved self._nbytes = nbytes - # Default to idle - for i in range(nbytes): - self[i] = (0x07, True) - def __setitem__(self, index, value): byte, ctrl = value @@ -138,6 +134,7 @@ def __init__(self, signal: SimHandleBase, clock: SimHandleBase, interleaved: boo self.clock = clock self.bus = _XGMIIBus(len(signal)//9, interleaved=interleaved) Driver.__init__(self) + self.idle() @staticmethod def layer1(packet: bytes) -> bytes: diff --git a/documentation/source/newsfragments/1905.bugfix.rst b/documentation/source/newsfragments/1905.bugfix.rst new file mode 100644 index 00000000..06fc3879 --- /dev/null +++ b/documentation/source/newsfragments/1905.bugfix.rst @@ -0,0 +1 @@ +The :class:`~cocotb.drivers.xgmii.XGMII` driver no longer emits a corrupted word on the first transfer. From 5061fa04e09c79144633ad202b6ebc4b965e468a Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Sat, 13 Jun 2020 22:49:21 +0200 Subject: [PATCH 2367/2656] Skip mixed_language example if top not verilog --- examples/mixed_language/tests/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/mixed_language/tests/Makefile b/examples/mixed_language/tests/Makefile index 740514fa..d9afcdd4 100644 --- a/examples/mixed_language/tests/Makefile +++ b/examples/mixed_language/tests/Makefile @@ -6,6 +6,10 @@ else ifeq ($(shell echo $(SIM) | tr A-Z a-z),icarus) all: @echo "Skipping example mixed_language since Icarus doesn't support this" clean:: +else ifneq ($(shell echo $(TOPLEVEL_LANG) | tr A-Z a-z),verilog) +all: + @echo "Skipping example mixed_language since only Verilog toplevel implemented" +clean:: else # Override this variable to use a VHDL toplevel instead of SystemVerilog From b4050b2f25a11a60ccda872150801a6b412dd455 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Thu, 18 Jun 2020 04:59:50 -0500 Subject: [PATCH 2368/2656] Update README with various fixes (#1929) Change links to point to stable. Fix link for how to install dev versions. Add installation warning to README for Windows users. --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d01c1f62..2f03b907 100644 --- a/README.md +++ b/README.md @@ -26,13 +26,17 @@ After installing these dependencies, the latest stable version of cocotb can be pip install cocotb ``` -For more details, including how to install a development version of cocotb, see [the documentation](https://docs.cocotb.org/en/latest/install.html). +**!!! Windows Users !!!** See [here](https://docs.cocotb.org/en/stable/install.html) for installation instructions. + +For more details on installation, including prerequisites, see [the documentation](https://docs.cocotb.org/en/stable/install.html). + +For detail on how to install the *development* version of cocotb, see [the lastest documentation](https://docs.cocotb.org/en/latest/install_devel.html#install-devel). ## Usage As a first trivial introduction to cocotb, the following example "tests" a flip-flop. -First, we need a hardware design which we can test. For this example, create a file `dff.sv` with SystemVerilog code for a simple [D flip-flop](https://en.wikipedia.org/wiki/Flip-flop_(electronics)#D_flip-flop). You could also use any other language a [cocotb-supported simulator](https://docs.cocotb.org/en/latest/simulator_support.html) understands, e.g. VHDL. +First, we need a hardware design which we can test. For this example, create a file `dff.sv` with SystemVerilog code for a simple [D flip-flop](https://en.wikipedia.org/wiki/Flip-flop_(electronics)#D_flip-flop). You could also use any other language a [cocotb-supported simulator](https://docs.cocotb.org/en/stable/simulator_support.html) understands, e.g. VHDL. ```systemverilog // dff.sv @@ -100,8 +104,8 @@ For more information please see the [cocotb documentation](https://docs.cocotb.o ## Tutorials, examples and related projects -* [Endian Swapper tutorial](https://docs.cocotb.org/en/latest/endian_swapper.html) -* [Ping using TUN/TAP tutorial](https://docs.cocotb.org/en/latest/ping_tun_tap.html) +* [Endian Swapper tutorial](https://docs.cocotb.org/en/stable/endian_swapper.html) +* [Ping using TUN/TAP tutorial](https://docs.cocotb.org/en/stable/ping_tun_tap.html) * [Cocotb based USB 1.1 test suite for FPGA IP, with testbenches for a variety of open source USB cores](https://github.com/antmicro/usb-test-suite-build) * [Functional Coverage and Constrained Randomization Extensions for Cocotb](https://github.com/mciepluc/cocotb-coverage) * [UVM 1.2 port to Python](https://github.com/tpoikela/uvm-python) From cdfee3694474a0e4daa4d73d85c17f3abe483fc6 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 18 Jun 2020 12:08:35 +0100 Subject: [PATCH 2369/2656] Deprecate implicit conversion of handle values to strings. (#1890) Note that most visibly, this will make code like the following emit warnings: ```python log.info("The value of X is %s", dut.X) ``` unless users change it to ```python log.info("The value of X is %s", dut.X.value) ``` Looking at the examples which needed to be changed, it seems many tests were already expecting the behavior of the latter. --- cocotb/handle.py | 26 ++++++++++++++- tests/test_cases/issue_348/issue_348.py | 2 +- .../test_cases/test_cocotb/test_deprecated.py | 28 ++++++++++++++-- .../test_cocotb/test_edge_triggers.py | 8 ++--- .../test_discovery/test_discovery.py | 32 +++++++++---------- 5 files changed, 72 insertions(+), 24 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 14f64c08..2c61e8af 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -479,7 +479,12 @@ def value(self): return self._value def __str__(self): - return str(self.value) + if isinstance(self.value, bytes): + StringObject._emit_str_warning(self) + return self.value.decode('ascii') + else: + ModifiableObject._emit_str_warning(self) + return str(self.value) class NonHierarchyIndexableObject(NonHierarchyObject): @@ -691,7 +696,15 @@ def value(self) -> BinaryValue: def __int__(self): return int(self.value) + def _emit_str_warning(self): + warnings.warn( + "`str({t})` is deprecated, and in future will return `{t}._path`. " + "To get a string representation of the value, use `str({t}.value)`." + .format(t=type(self).__qualname__), + FutureWarning, stacklevel=3) + def __str__(self): + self._emit_str_warning() return str(self.value) @@ -836,6 +849,17 @@ def _set_value(self, value, call_sim): def value(self) -> bytes: return self._handle.get_signal_val_str() + def _emit_str_warning(self): + warnings.warn( + "`str({t})` is deprecated, and in future will return `{t}._path`. " + "To access the `bytes` value of this handle, use `{t}.value`." + .format(t=type(self).__qualname__), + FutureWarning, stacklevel=3) + + def __str__(self): + self._emit_str_warning() + return self.value.decode('ascii') + _handle2obj = {} diff --git a/tests/test_cases/issue_348/issue_348.py b/tests/test_cases/issue_348/issue_348.py index eb213727..e6c4d948 100644 --- a/tests/test_cases/issue_348/issue_348.py +++ b/tests/test_cases/issue_348/issue_348.py @@ -29,7 +29,7 @@ def signal_mon(signal, idx, edge): class DualMonitor: def __init__(self, edge, signal): - self.log = SimLog("cocotb.%s.%s" % (edge, signal)) + self.log = SimLog("cocotb.%s.%s" % (edge, signal._path)) self.edge_type = edge self.monitor_edges = [0, 0] self.signal = signal diff --git a/tests/test_cases/test_cocotb/test_deprecated.py b/tests/test_cases/test_cocotb/test_deprecated.py index 3cf327ff..e80fbad9 100644 --- a/tests/test_cases/test_cocotb/test_deprecated.py +++ b/tests/test_cases/test_cocotb/test_deprecated.py @@ -8,7 +8,7 @@ @contextmanager -def assert_deprecated(): +def assert_deprecated(warning_category=DeprecationWarning): warns = [] try: with warnings.catch_warnings(record=True) as warns: @@ -17,7 +17,8 @@ def assert_deprecated(): yield warns # note: not a cocotb yield, but a contextlib one! finally: assert len(warns) == 1 - assert issubclass(warns[0].category, DeprecationWarning), "Expected DeprecationWarning" + msg = "Expected {}".format(warning_category.__qualname__) + assert issubclass(warns[0].category, warning_category), msg @cocotb.test() @@ -43,6 +44,29 @@ async def test_unicode_handle_assignment_deprecated(dut): assert "bytes" in str(warns[0].message) +@cocotb.test() +async def test_convert_handle_to_string_deprecated(dut): + dut.stream_in_data <= 0 + await cocotb.triggers.Timer(1, units='ns') + + with assert_deprecated(FutureWarning) as warns: + as_str = str(dut.stream_in_data) + assert "_path" in str(warns[0].message) + + # in future this will be ` == dut._path` + assert as_str == str(dut.stream_in_data.value) + + if cocotb.LANGUAGE == "verilog": + # the `NUM_OF_MODULES` parameter is only present in the verilog design + with assert_deprecated(FutureWarning) as warns: + as_str = str(dut.NUM_OF_MODULES) + + assert "_path" in str(warns[0].message) + + # in future this will be ` == dut._path` + assert as_str == str(dut.NUM_OF_MODULES.value) + + @cocotb.test() async def test_create_error_deprecated(dut): with assert_deprecated(): diff --git a/tests/test_cases/test_cocotb/test_edge_triggers.py b/tests/test_cases/test_cocotb/test_edge_triggers.py index 34c6bc8b..e7a01fb8 100644 --- a/tests/test_cases/test_cocotb/test_edge_triggers.py +++ b/tests/test_cases/test_cocotb/test_edge_triggers.py @@ -29,17 +29,17 @@ def count_edges_cycles(signal, edges): def do_single_edge_check(dut, level): """Do test for rising edge""" old_value = dut.clk.value.integer - dut._log.info("Value of %s is %d" % (dut.clk, old_value)) + dut._log.info("Value of %s is %d" % (dut.clk._path, old_value)) if old_value is level: - raise TestError("%s not to %d start with" % (dut.clk, not level)) + raise TestError("%s not to %d start with" % (dut.clk._path, not level)) if level == 1: yield RisingEdge(dut.clk) else: yield FallingEdge(dut.clk) new_value = dut.clk.value.integer - dut._log.info("Value of %s is %d" % (dut.clk, new_value)) + dut._log.info("Value of %s is %d" % (dut.clk._path, new_value)) if new_value is not level: - raise TestError("%s not %d at end" % (dut.clk, level)) + raise TestError("%s not %d at end" % (dut.clk._path, level)) @cocotb.test() diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 0857efff..9bdd2434 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -103,7 +103,7 @@ def access_signal(dut): yield Timer(10) if dut.stream_in_data.value.integer != 1: raise TestError("%s.%s != %d" % - (str(dut.stream_in_data), + (dut.stream_in_data._path, dut.stream_in_data.value.integer, 1)) @@ -116,12 +116,12 @@ def access_single_bit(dut): dut.stream_in_data <= 0 yield Timer(10) dut._log.info("%s = %d bits" % - (str(dut.stream_in_data), len(dut.stream_in_data))) + (dut.stream_in_data._path, len(dut.stream_in_data))) dut.stream_in_data[2] <= 1 yield Timer(10) if dut.stream_out_data_comb.value.integer != (1 << 2): raise TestError("%s.%s != %d" % - (str(dut.stream_out_data_comb), + (dut.stream_out_data_comb._path, dut.stream_out_data_comb.value.integer, (1 << 2))) @@ -134,12 +134,12 @@ def access_single_bit_assignment(dut): dut.stream_in_data = 0 yield Timer(10) dut._log.info("%s = %d bits" % - (str(dut.stream_in_data), len(dut.stream_in_data))) + (dut.stream_in_data._path, len(dut.stream_in_data))) dut.stream_in_data[2] = 1 yield Timer(10) if dut.stream_out_data_comb.value.integer != (1 << 2): raise TestError("%s.%s != %d" % - (str(dut.stream_out_data_comb), + (dut.stream_out_data_comb._path, dut.stream_out_data_comb.value.integer, (1 << 2))) @@ -148,7 +148,7 @@ def access_single_bit_erroneous(dut): """Access a non-existent single bit""" yield Timer(10) dut._log.info("%s = %d bits" % - (str(dut.stream_in_data), len(dut.stream_in_data))) + (dut.stream_in_data._path, len(dut.stream_in_data))) bit = len(dut.stream_in_data) + 4 dut.stream_in_data[bit] <= 1 yield Timer(10) @@ -208,7 +208,7 @@ def access_string_vhdl(dut): tlog = logging.getLogger("cocotb.test") yield Timer(10) constant_string = dut.isample_module1.EXAMPLE_STRING - tlog.info("%r is %s" % (constant_string, str(constant_string))) + tlog.info("%r is %s" % (constant_string, constant_string.value)) if not isinstance(constant_string, ConstantObject): raise TestFailure("EXAMPLE_STRING was not constant") if constant_string != b"TESTING": @@ -226,7 +226,7 @@ def access_string_vhdl(dut): yield Timer(10) if variable_string != test_string: - raise TestFailure("%r %s != '%s'" % (variable_string, str(variable_string), test_string)) + raise TestFailure("%r %s != '%s'" % (variable_string, variable_string.value, test_string)) test_string = b"longer_than_the_array" tlog.info("Test writing over size with '%s'" % test_string) @@ -239,7 +239,7 @@ def access_string_vhdl(dut): test_string = test_string[:len(variable_string)] if variable_string != test_string: - raise TestFailure("%r %s != '%s'" % (variable_string, str(variable_string), test_string)) + raise TestFailure("%r %s != '%s'" % (variable_string, variable_string.value, test_string)) tlog.info("Test read access to a string character") @@ -266,7 +266,7 @@ def access_string_vhdl(dut): test_string = test_string.upper() - result = str(variable_string) + result = variable_string.value tlog.info("After setting bytes of string value is %s" % result) if variable_string != test_string: raise TestFailure("%r %s != '%s'" % (variable_string, result, test_string)) @@ -282,7 +282,7 @@ def access_const_string_verilog(dut): string_const = dut.STRING_CONST yield Timer(10, 'ns') - tlog.info("%r is %s" % (string_const, str(string_const))) + tlog.info("%r is %s" % (string_const, string_const.value)) if not isinstance(string_const, StringObject): raise TestFailure("STRING_CONST was not StringObject") if string_const != b"TESTING_CONST": @@ -303,7 +303,7 @@ def access_var_string_verilog(dut): string_var = dut.STRING_VAR yield Timer(10, 'ns') - tlog.info("%r is %s" % (string_var, str(string_var))) + tlog.info("%r is %s" % (string_var, string_var.value)) if not isinstance(string_var, StringObject): raise TestFailure("STRING_VAR was not StringObject") if string_var != b"TESTING_VAR": @@ -326,7 +326,7 @@ def access_constant_boolean(dut): if not isinstance(constant_boolean, ConstantObject): raise TestFailure("dut.stream_in_int.EXAMPLE_BOOL is not a ConstantObject") - tlog.info("Value of %s is %d" % (constant_boolean, constant_boolean)) + tlog.info("Value of %s is %d" % (constant_boolean._path, constant_boolean.value)) @cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) @@ -355,7 +355,7 @@ def access_boolean(dut): if length != 1: raise TestFailure("Length should be 1 not %d" % length) - tlog.info("Value of %s is %d" % (boolean, boolean)) + tlog.info("Value of %s is %d" % (boolean._path, boolean.value)) curr_val = int(boolean) output_bool = dut.stream_out_bool @@ -366,7 +366,7 @@ def access_boolean(dut): yield Timer(1) - tlog.info("Value of %s is now %d" % (output_bool, output_bool)) + tlog.info("Value of %s is now %d" % (output_bool._path, output_bool.value)) if (int(curr_val) == int(output_bool)): raise TestFailure("Value did not propagate") @@ -391,7 +391,7 @@ def skip_a_test(dut): """This test shouldn't execute""" yield Timer(10) dut._log.info("%s = %d bits" % - (str(dut.stream_in_data), len(dut.stream_in_data))) + (dut.stream_in_data._path, len(dut.stream_in_data))) bit = len(dut.stream_in_data) + 4 dut.stream_in_data[bit] <= 1 yield Timer(10) From a883f5482190405fb18898870bec0f78fe14d17f Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 5 Jun 2020 00:36:36 -0500 Subject: [PATCH 2370/2656] Initial coverage support --- cocotb/__init__.py | 13 +++++++++++++ cocotb/regression.py | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index fd372ab5..e2c41e42 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -126,6 +126,9 @@ def _reopen_stream_with_buffering(stream_name): See :envvar:`RANDOM_SEED` for details on how the value is computed. """ +_library_coverage = None +""" used for cocotb library coverage """ + def fork(coro): """ Schedule a coroutine to be run concurrently. See :ref:`coroutines` for details on its use. """ @@ -155,6 +158,16 @@ def _initialise_testbench(argv_): """ _rlock.acquire() + if "COCOTB_LIBRARY_COVERAGE" in os.environ: + import coverage + + global _library_coverage + _library_coverage = coverage.coverage( + data_file=".coverage.cocotb", + branch=True, + include=["{}/*".format(os.path.dirname(__file__))]) + _library_coverage.start() + global argc, argv argv = argv_ argc = len(argv) diff --git a/cocotb/regression.py b/cocotb/regression.py index 352b49ff..6092bc6f 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -261,6 +261,10 @@ def tear_down(self) -> None: self.log.info("Writing coverage data") self._cov.save() self._cov.html_report() + if cocotb._library_coverage is not None: + # TODO: move this once we have normal shutdown behavior to _sim_event + cocotb._library_coverage.stop() + cocotb._library_coverage.save() # Setup simulator finalization simulator.stop_simulator() From 247c4b8065501d171fcc80a718b1413ad5a58957 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 5 Jun 2020 15:36:00 -0500 Subject: [PATCH 2371/2656] Always run cocotb library coverage on tox --- tox.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tox.ini b/tox.ini index d0960179..bcb3ea4f 100644 --- a/tox.ini +++ b/tox.ini @@ -5,6 +5,7 @@ envlist = py3 setenv = CFLAGS = -Werror CXXFLAGS = -Werror + COCOTB_LIBRARY_COVERAGE = 1 passenv = SIM TOPLEVEL_LANG @@ -21,9 +22,11 @@ deps = commands = pytest make test + bash -c 'find . -type f -name "\.coverage\.cocotb" | xargs coverage combine --append' whitelist_externals = make + bash # needed for coverage to work usedevelop=True From e97005b350e91198960f23c8a396862320da0370 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 17 Jun 2020 23:11:44 -0500 Subject: [PATCH 2372/2656] Hack to get coverage to work on Windows for CI --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 2a001042..0d2d15bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -95,6 +95,8 @@ jobs: - export CONDA_PATH="\$env:Path = ';C:\miniconda3;C:\miniconda3\Library\mingw-w64\bin;C:\miniconda3\Library\usr\bin;C:\miniconda3\Library\bin;C:\miniconda3\Scripts;C:\miniconda3\bin;C:\miniconda3\condabin;C:\iverilog\bin;' + \$env:Path" - powershell "$CONDA_PATH; conda install --yes -c msys2 m2-base m2-make m2w64-toolchain libpython" - powershell "$CONDA_PATH; conda install --yes virtualenv" + # needed to make the builtin sqlite3 module (used by coverage) work - see gh-1909 + - powershell '$CONDA_PATH; cp C:\miniconda3\Library\bin\sqlite3.dll C:\miniconda3\DLLs\' - powershell "$CONDA_PATH; pip install tox" script: # TODO[gh-1372]: the default compiler on windows, msvc, is not supported From 4b94230aea938c51572edff37e8669cc496336f7 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Thu, 28 May 2020 15:31:32 -0700 Subject: [PATCH 2373/2656] Don't release handles of VHPI pseudo-region objects. The handles for pseudo-regions are the handle of the parent region object containing the `for generate` statement. The problem occurs when iterating over the parent region's objects: each of the `vhpiForGenerateK` objects attempts to create the pseudo-region, relying on the `SINGLETON_HANDLES` functionality to ensure only one exists. The `GpiHandleStore` deletes the duplicate pseudo-region object and so the shared underlying handle is released when it shouldn't be. When iterating continues, the parent region's object handle is used, but it has been released. The fix is to not release the handle when deleting the pseudo-region object. --- cocotb/share/lib/vhpi/VhpiCbHdl.cpp | 9 ++++++--- documentation/source/newsfragments/1882.bugfix.rst | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 documentation/source/newsfragments/1882.bugfix.rst diff --git a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp index 2268223c..3eb1abc9 100644 --- a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp +++ b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp @@ -45,9 +45,12 @@ VhpiArrayObjHdl::~VhpiArrayObjHdl() VhpiObjHdl::~VhpiObjHdl() { - LOG_DEBUG("Releasing VhpiObjHdl handle at %p\n", (void *)get_handle()); - if (vhpi_release_handle(get_handle())) - check_vhpi_error(); + /* Don't release handles for pseudo-regions, as they borrow the handle of the containing region */ + if (m_type != GPI_GENARRAY) { + LOG_DEBUG("Releasing VhpiObjHdl handle at %p\n", (void *)get_handle()); + if (vhpi_release_handle(get_handle())) + check_vhpi_error(); + } } VhpiSignalObjHdl::~VhpiSignalObjHdl() diff --git a/documentation/source/newsfragments/1882.bugfix.rst b/documentation/source/newsfragments/1882.bugfix.rst new file mode 100644 index 00000000..535dc7a8 --- /dev/null +++ b/documentation/source/newsfragments/1882.bugfix.rst @@ -0,0 +1 @@ +Iterating over ``for generate`` statements using VHPI has been fixed. This bug caused some simulators to crash, and was a regression in version 1.3. From 1cda45e1812f78fbb498204953209a53aaa8ac0d Mon Sep 17 00:00:00 2001 From: Marlon James Date: Thu, 11 Jun 2020 17:03:03 -0700 Subject: [PATCH 2374/2656] Strip leading/trailing whitespace in simulator info. Active-HDL 11.1 gives a SIM_VERSION of " 11.1.260" (with leading space). --- cocotb/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index e2c41e42..ffefc694 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -196,8 +196,8 @@ def _initialise_testbench(argv_): from cocotb import simulator global SIM_NAME, SIM_VERSION - SIM_NAME = simulator.get_simulator_product() - SIM_VERSION = simulator.get_simulator_version() + SIM_NAME = simulator.get_simulator_product().strip() + SIM_VERSION = simulator.get_simulator_version().strip() cocotb.log.info("Running on {} version {}".format(SIM_NAME, SIM_VERSION)) From 20a9e0cc72bae985813791a08b8fead96d5d3faf Mon Sep 17 00:00:00 2001 From: Marlon James Date: Thu, 28 May 2020 16:15:12 -0700 Subject: [PATCH 2375/2656] Cleanup test_iteration_vhdl and re-enable tests for Riviera. Provide justification for differences between simulators/versions. Skip recursive_discovery if expected object count is not known. --- .../test_iteration_vhdl/test_iteration.py | 101 ++++++++++-------- .../test_vhdl_access/test_vhdl_access.py | 4 +- 2 files changed, 57 insertions(+), 48 deletions(-) diff --git a/tests/test_cases/test_iteration_vhdl/test_iteration.py b/tests/test_cases/test_iteration_vhdl/test_iteration.py index 4442262c..caf5e55d 100644 --- a/tests/test_cases/test_iteration_vhdl/test_iteration.py +++ b/tests/test_cases/test_iteration_vhdl/test_iteration.py @@ -26,27 +26,52 @@ import logging import cocotb -from cocotb.triggers import Timer +from cocotb.triggers import Timer, Combine from cocotb.result import TestFailure -# This test crashes Riviera-PRO 2019.10 (at least); skip to avoid hanging the -# tests. See issue #1854 for details. -@cocotb.test(skip=cocotb.SIM_NAME.lower().startswith("riviera") and cocotb.SIM_VERSION.startswith("2019.10")) -def recursive_discovery(dut): - """ - Recursively discover every single object in the design - """ +def total_object_count(): + """Return the total object count based on simulator.""" + SIM_NAME = cocotb.SIM_NAME.lower() + SIM_VERSION = cocotb.SIM_VERSION.lower() - if (cocotb.SIM_NAME.lower().startswith(("ncsim", "xmsim", "modelsim")) or - (cocotb.SIM_NAME.lower().startswith("riviera") and not cocotb.SIM_VERSION.startswith("2016.02"))): - # Finds regions, signal, generics, constants, varibles and ports. - pass_total = 34569 - else: - pass_total = 32393 + if SIM_NAME.startswith( + ( + "ncsim", + "xmsim", + "modelsim", + ) + ): + return 34569 + + # Riviera-PRO + if SIM_NAME.startswith("riviera"): + if SIM_VERSION.startswith("2019.10"): + return 27359 + if SIM_VERSION.startswith("2016.02"): + return 32393 + return 34569 + + # Active-HDL + if SIM_NAME.startswith("aldec"): + if SIM_VERSION.startswith("11.1"): + # Active-HDL 11.1 only finds 'inbranch_tdata_low' inside the gen_acs for generate block + return 27359 + if SIM_VERSION.startswith("10.01"): + # Active-HDL 10.1 doesn't find any signals declared inside the gen_acs for generate block + return 26911 + + return 0 + + +@cocotb.test(skip=(total_object_count() == 0)) +async def recursive_discovery(dut): + """Recursively discover every single object in the design.""" + + pass_total = total_object_count() tlog = logging.getLogger("cocotb.test") - yield Timer(100) + await Timer(100) def dump_all_the_things(parent): count = 0 @@ -62,51 +87,37 @@ def dump_all_the_things(parent): @cocotb.test() -def discovery_all(dut): - dut._log.info("Trying to discover") - yield Timer(0) +async def discovery_all(dut): + """Discover everything on top-level.""" + dut._log.info("Iterating over top-level to discover objects") for thing in dut: thing._log.info("Found something: %s", thing._fullname) - # for subthing in thing: - # thing._log.info("Found something: %s" % thing._fullname) dut._log.info("length of dut.inst_acs is %d", len(dut.gen_acs)) item = dut.gen_acs[3] item._log.info("this is item") -@cocotb.coroutine -def iteration_loop(dut): - for thing in dut: - thing._log.info("Found something: %s", thing._fullname) - yield Timer(1) - - @cocotb.test() -def dual_iteration(dut): - loop_one = cocotb.fork(iteration_loop(dut)) - loop_two = cocotb.fork(iteration_loop(dut)) +async def dual_iteration(dut): + """Test iteration over top-level in two forked coroutines.""" + async def iteration_loop(): + for thing in dut: + thing._log.info("Found something: %s", thing._fullname) + await Timer(1) - yield [loop_one.join(), loop_two.join()] + loop_one = cocotb.fork(iteration_loop()) + loop_two = cocotb.fork(iteration_loop()) + await Combine(loop_one, loop_two) -@cocotb.test() -def get_clock(dut): - dut._log.info("dut.aclk is %s", type(dut.aclk).__name__) - dut.aclk <= 0 - yield Timer(1) - dut.aclk <= 1 - yield Timer(1) - if int(dut.aclk) != 1: - raise TestFailure("dut.aclk is not what we expected (got %d)" % int(dut.aclk)) - - -@cocotb.test(expect_fail=cocotb.SIM_NAME.lower().startswith("riviera") and cocotb.SIM_VERSION.startswith("2019.10")) -def test_n_dimension_array(dut): + +@cocotb.test(expect_fail=(cocotb.SIM_NAME.lower().startswith("riviera") and cocotb.SIM_VERSION.startswith("2019.10")) or cocotb.SIM_NAME.lower().startswith("aldec")) +async def test_n_dimension_array(dut): + """Test iteration over multi-dimensional array.""" tlog = logging.getLogger("cocotb.test") inner_count = 0 outer_count = 0 - yield Timer(0) config = dut.inst_ram_ctrl.config # This signal is a 2 x 7 vhpiEnumVecVal for thing in config: diff --git a/tests/test_cases/test_vhdl_access/test_vhdl_access.py b/tests/test_cases/test_vhdl_access/test_vhdl_access.py index 3c664f83..2ea6ada2 100644 --- a/tests/test_cases/test_vhdl_access/test_vhdl_access.py +++ b/tests/test_cases/test_vhdl_access/test_vhdl_access.py @@ -95,9 +95,7 @@ def check_instance(obj, objtype): raise TestFailure("%d Failures during the test" % fails) -# This test crashes Riviera-PRO 2019.10 (at least); skip to avoid hanging the -# tests. See issue #1857 for details. -@cocotb.test(skip=cocotb.SIM_NAME.lower().startswith("riviera") and cocotb.SIM_VERSION.startswith("2019.10")) +@cocotb.test() def port_not_hierarchy(dut): """ Test for issue raised by Luke - iteration causes a toplevel port type to From a30492edd72049743eaf022695ec596f101cc0c5 Mon Sep 17 00:00:00 2001 From: Mehul Tikekar Date: Fri, 19 Jun 2020 03:50:17 +0530 Subject: [PATCH 2376/2656] add share/lib/verilator/verilator.cpp to package --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1d7ebbd7..d0c84998 100755 --- a/setup.py +++ b/setup.py @@ -101,7 +101,8 @@ def package_files(directory): 'cocotb': ( package_files('cocotb/share/makefiles') + # noqa: W504 package_files('cocotb/share/include') + # noqa: W504 - package_files('cocotb/share/def') + package_files('cocotb/share/def') + + package_files('cocotb/share/lib/verilator') ) }, ext_modules=get_ext(), From 9cccac444ed7b1a98b4521879636c821510fe91d Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 19 Jun 2020 08:37:24 -0500 Subject: [PATCH 2377/2656] Mention C++ compiler must be a C++11 compiler specifically --- README.md | 2 +- documentation/source/quickstart.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2f03b907..4c9bfaa8 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Cocotb requires: - Python 3.5+ -- A C++ compiler +- A C++11 compiler - An HDL simulator (such as [Icarus Verilog](http://iverilog.icarus.com/)) After installing these dependencies, the latest stable version of cocotb can be installed with pip. diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index babf03c0..bb590b69 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -9,7 +9,7 @@ Running your first Example ========================== Make sure you have the :ref:`prerequisites` -(Python with development packages, a C++ compiler with development packages, GNU Make, +(Python with development packages, a C++11 compiler with development packages, GNU Make, a :ref:`supported simulator`) and cocotb itself (``pip install cocotb``) available. Download and extract the cocotb source files according to the *release version* you are using from From 853a726a50a74c90ef38fca1273410de0db70959 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 19 Jun 2020 09:40:20 -0500 Subject: [PATCH 2378/2656] Add troubleshooting docs on debugging C++ library env issues --- documentation/source/troubleshooting.rst | 43 ++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/documentation/source/troubleshooting.rst b/documentation/source/troubleshooting.rst index 9c0dee83..e2767d02 100644 --- a/documentation/source/troubleshooting.rst +++ b/documentation/source/troubleshooting.rst @@ -101,3 +101,46 @@ and having them ignored is likely to lead to strange errors. As a side note, when you need to *clear* a Makefile variable from the command line, use the syntax ``make EXTRA_ARGS=``. + +``GLIBCXX_3.4.XX`` not found +============================ + +This error can occur on Linux, and will raise ``ImportError: /some/libstdc++.so.6: version `GLIBCXX_3.4.XX' not found``. +This occurs because an older non-C++11 version of libstdc++ is being loaded by the simulator or cocotb. +It is usually an issue with your environment, but sometimes can occur when using very old version of certain simulators. + +Check your environment +---------------------- + +To see if your environment is the issue, look at the value of the ``LD_LIBRARY_PATH`` environment variable. +Ensure the first path in the colon-deliminated list is the path to the libstdc++ that shipped with the compiler you used to build cocotb. + +.. code:: shell + + echo $LD_LIBRARY_PATH + +This variable might be empty, in which case the loader looks in the system's libraries. +If the library you built cocotb with is not first, prepend that path to the list. + +.. code:: shell + + export LD_LIBRARY_PATH=/path/to/newer/libraries/:$LD_LIBRARY_PATH + +Check your simulator +-------------------- + +Sometimes, simulators modify the ``LD_LIBRARY_PATH`` so they point to the libraries that are shipped with instead of the system libraries. +If you are running an old simulator, the packaged libraries may include a pre-C++11 libstdc++. +To see if your simulator is modifying the ``LD_LIBRARY_PATH``, open the simulator up to an internal console and obtain the environment variable. + +For example, with Questa one could open Questa to a TCL console and run the ``env`` command to list the current environment. +The ``LD_LIBRARY`` path should appear in the list. + +If the simulator does modify the ``LD_LIBRARY_PATH``, refer to the simulator documenation on how to prevent or work around this issue. + +For example, Questa ships with GCC. +Sometimes that version of GCC is old enough to not support C++11 (<4.8). +When you install cocotb, ``pip`` uses the system (or some other) compiler that supports C++11. +But when you try to run cocotb with the older Questa, it prepends the older libraries Questa ships with to ``LD_LIBRARY_PATH``. +This causes the older libstdc++ Questa ships with to be loaded, resuling in the error message. +For Questa, you can use the ``-noautoldlibpath`` option to turn off the ``LD_LIBRARY_PATH`` prepend to resolve this issue. From 9406e1901c5b88fe5ffabe5ecc8062a8abe49c00 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 20 Jun 2020 22:08:09 -0500 Subject: [PATCH 2379/2656] Fixes to troubleshooting documentation Co-authored-by: Colin Marquardt --- documentation/source/troubleshooting.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/source/troubleshooting.rst b/documentation/source/troubleshooting.rst index e2767d02..3e4aed4d 100644 --- a/documentation/source/troubleshooting.rst +++ b/documentation/source/troubleshooting.rst @@ -107,7 +107,7 @@ use the syntax ``make EXTRA_ARGS=``. This error can occur on Linux, and will raise ``ImportError: /some/libstdc++.so.6: version `GLIBCXX_3.4.XX' not found``. This occurs because an older non-C++11 version of libstdc++ is being loaded by the simulator or cocotb. -It is usually an issue with your environment, but sometimes can occur when using very old version of certain simulators. +It is usually an issue with your environment, but sometimes can occur when using a very old version of certain simulators. Check your environment ---------------------- @@ -133,10 +133,10 @@ Sometimes, simulators modify the ``LD_LIBRARY_PATH`` so they point to the librar If you are running an old simulator, the packaged libraries may include a pre-C++11 libstdc++. To see if your simulator is modifying the ``LD_LIBRARY_PATH``, open the simulator up to an internal console and obtain the environment variable. -For example, with Questa one could open Questa to a TCL console and run the ``env`` command to list the current environment. +For example, with Mentor Questa and Cadence Xcelium, one could open a Tcl console and run the ``env`` command to list the current environment. The ``LD_LIBRARY`` path should appear in the list. -If the simulator does modify the ``LD_LIBRARY_PATH``, refer to the simulator documenation on how to prevent or work around this issue. +If the simulator does modify the ``LD_LIBRARY_PATH``, refer to the simulator documentation on how to prevent or work around this issue. For example, Questa ships with GCC. Sometimes that version of GCC is old enough to not support C++11 (<4.8). From 743ca9fb0565aa08b0d7d37e07094ebc5c8e7c7b Mon Sep 17 00:00:00 2001 From: Ludwig Rogiers Date: Tue, 23 Jun 2020 18:04:16 +1000 Subject: [PATCH 2380/2656] Update mean example test (#1730) (#1902) - Cleanup and remove python 2 statements - Change to 'async def' syntax - Add 'mean_range_test' --- examples/mean/tests/test_mean.py | 103 +++++++++++++++---------------- 1 file changed, 50 insertions(+), 53 deletions(-) diff --git a/examples/mean/tests/test_mean.py b/examples/mean/tests/test_mean.py index e2cc2287..2b3bbbf0 100644 --- a/examples/mean/tests/test_mean.py +++ b/examples/mean/tests/test_mean.py @@ -1,12 +1,9 @@ # This file is public domain, it can be freely copied without restrictions. # SPDX-License-Identifier: CC0-1.0 -from __future__ import print_function -from __future__ import division - import cocotb -from cocotb.triggers import Timer, RisingEdge, ReadOnly -from cocotb.result import TestFailure +from cocotb.clock import Clock +from cocotb.triggers import RisingEdge, ReadOnly from cocotb.monitors import BusMonitor from cocotb.scoreboard import Scoreboard @@ -20,73 +17,70 @@ class StreamBusMonitor(BusMonitor): _signals = ["valid", "data"] - @cocotb.coroutine - def _monitor_recv(self): + async def _monitor_recv(self): """Watch the pins and reconstruct transactions.""" while True: - yield RisingEdge(self.clock) - yield ReadOnly() + await RisingEdge(self.clock) + await ReadOnly() if self.bus.valid.value: self._recv(int(self.bus.data.value)) -@cocotb.coroutine -def clock_gen(signal, period=10, units='ns'): - while True: - signal <= 0 - yield Timer(period/2, units=units) - signal <= 1 - yield Timer(period/2, units=units) - - -@cocotb.coroutine -def value_test(dut, num): - """Test n*num/n = num""" - - data_width = int(dut.DATA_WIDTH.value) - bus_width = int(dut.BUS_WIDTH.value) +async def value_test(dut, nums): + """Test sum(nums)/n""" + DATA_WIDTH = int(dut.DATA_WIDTH.value) + BUS_WIDTH = int(dut.BUS_WIDTH.value) dut._log.info('Detected DATA_WIDTH = %d, BUS_WIDTH = %d' % - (data_width, bus_width)) + (DATA_WIDTH, BUS_WIDTH)) - cocotb.fork(clock_gen(dut.clk, period=CLK_PERIOD_NS, units='ns')) + cocotb.fork(Clock(dut.clk, CLK_PERIOD_NS, units='ns').start()) dut.rst <= 1 - for i in range(bus_width): + for i in range(BUS_WIDTH): dut.i_data[i] <= 0 dut.i_valid <= 0 - yield RisingEdge(dut.clk) - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) dut.rst <= 0 - for i in range(bus_width): - dut.i_data[i] <= num + for i in range(BUS_WIDTH): + dut.i_data[i] <= nums[i] dut.i_valid <= 1 - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) dut.i_valid <= 0 - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) got = int(dut.o_data.value) - if got != num: - raise TestFailure( - 'Mismatch detected: got %d, exp %d!' % (got, num)) + exp = sum(nums) // BUS_WIDTH + + assert got == exp, "Mismatch detected: got {}, expected {}!".format(got, exp) @cocotb.test() -def mean_basic_test(dut): +async def mean_basic_test(dut): """Test n*5/n = 5""" - yield value_test(dut, 5) + BUS_WIDTH = int(dut.BUS_WIDTH.value) + await value_test(dut, [5] * BUS_WIDTH) @cocotb.test() -def mean_overflow_test(dut): +async def mean_range_test(dut): + """Test range(n)/n""" + BUS_WIDTH = int(dut.BUS_WIDTH.value) + await value_test(dut, range(1, BUS_WIDTH + 1)) + + +@cocotb.test() +async def mean_overflow_test(dut): """Test for overflow n*max_val/n = max_val""" - data_width = int(dut.DATA_WIDTH.value) - yield value_test(dut, 2**data_width - 1) + BUS_WIDTH = int(dut.BUS_WIDTH.value) + DATA_WIDTH = int(dut.DATA_WIDTH.value) + await value_test(dut, [2**DATA_WIDTH - 1] * BUS_WIDTH) @cocotb.test() -def mean_randomised_test(dut): +async def mean_randomised_test(dut): """Test mean of random numbers multiple times""" # dut_in = StreamBusMonitor(dut, "i", dut.clk) # this doesn't work: @@ -99,30 +93,33 @@ def mean_randomised_test(dut): scoreboard = Scoreboard(dut) scoreboard.add_interface(dut_out, exp_out) - data_width = int(dut.DATA_WIDTH.value) - bus_width = int(dut.BUS_WIDTH.value) + DATA_WIDTH = int(dut.DATA_WIDTH.value) + BUS_WIDTH = int(dut.BUS_WIDTH.value) dut._log.info('Detected DATA_WIDTH = %d, BUS_WIDTH = %d' % - (data_width, bus_width)) + (DATA_WIDTH, BUS_WIDTH)) - cocotb.fork(clock_gen(dut.clk, period=CLK_PERIOD_NS, units='ns')) + cocotb.fork(Clock(dut.clk, CLK_PERIOD_NS, units='ns').start()) dut.rst <= 1 - for i in range(bus_width): + for i in range(BUS_WIDTH): dut.i_data[i] = 0 dut.i_valid <= 0 - yield RisingEdge(dut.clk) - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) dut.rst <= 0 for j in range(10): nums = [] - for i in range(bus_width): - x = random.randint(0, 2**data_width - 1) + for i in range(BUS_WIDTH): + x = random.randint(0, 2**DATA_WIDTH - 1) dut.i_data[i] = x nums.append(x) dut.i_valid <= 1 - nums_mean = sum(nums) // bus_width + nums_mean = sum(nums) // BUS_WIDTH exp_out.append(nums_mean) - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) dut.i_valid <= 0 + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) From 1e33fc61472f0c89b3ddd8e01dccc933130723ac Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Mon, 22 Jun 2020 21:23:54 +0100 Subject: [PATCH 2381/2656] Fix SSL cert error in Travis builds on Windows An updated pip from conda seems to solve the problem. This also updates setuptools and requests, not sure which component actually solves the problem. In Travis builds on Windows we get the following error. ``` ERROR: Command errored out with exit status 1: command: 'C:\Users\travis\build\cocotb\cocotb\.tox\py3_mingw\Scripts\python.EXE' -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'C:\\Users\\travis\\AppData\\Local\\Temp\\pip-install-7zlcuzsx\\py\\setup.py'"'"'; __file__='"'"'C:\\Users\\travis\\AppData\\Local\\Temp\\pip-install-7zlcuzsx\\py\\setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base pip-egg-info cwd: C:\Users\travis\AppData\Local\Temp\pip-install-7zlcuzsx\py\ Complete output (85 lines): Traceback (most recent call last): File "c:\miniconda3\Lib\urllib\request.py", line 1319, in do_open encode_chunked=req.has_header('Transfer-encoding')) File "c:\miniconda3\Lib\http\client.py", line 1252, in request self._send_request(method, url, body, headers, encode_chunked) File "c:\miniconda3\Lib\http\client.py", line 1298, in _send_request self.endheaders(body, encode_chunked=encode_chunked) File "c:\miniconda3\Lib\http\client.py", line 1247, in endheaders self._send_output(message_body, encode_chunked=encode_chunked) File "c:\miniconda3\Lib\http\client.py", line 1026, in _send_output self.send(msg) File "c:\miniconda3\Lib\http\client.py", line 966, in send self.connect() File "c:\miniconda3\Lib\http\client.py", line 1422, in connect server_hostname=server_hostname) File "c:\miniconda3\Lib\ssl.py", line 423, in wrap_socket session=session File "c:\miniconda3\Lib\ssl.py", line 870, in _create self.do_handshake() File "c:\miniconda3\Lib\ssl.py", line 1139, in do_handshake self._sslobj.do_handshake() ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1076) During handling of the above exception, another exception occurred: Traceback (most recent call last): File "C:\Users\travis\build\cocotb\cocotb\.tox\py3_mingw\lib\site-packages\setuptools\package_index.py", line 766, in open_url return open_with_auth(url, self.opener) File "C:\Users\travis\build\cocotb\cocotb\.tox\py3_mingw\lib\site-packages\setuptools\package_index.py", line 961, in _socket_timeout return func(*args, **kwargs) File "C:\Users\travis\build\cocotb\cocotb\.tox\py3_mingw\lib\site-packages\setuptools\package_index.py", line 1080, in open_with_auth fp = opener(request) File "c:\miniconda3\Lib\urllib\request.py", line 222, in urlopen return opener.open(url, data, timeout) File "c:\miniconda3\Lib\urllib\request.py", line 525, in open response = self._open(req, data) File "c:\miniconda3\Lib\urllib\request.py", line 543, in _open '_open', req) File "c:\miniconda3\Lib\urllib\request.py", line 503, in _call_chain result = func(*args) File "c:\miniconda3\Lib\urllib\request.py", line 1362, in https_open context=self._context, check_hostname=self._check_hostname) File "c:\miniconda3\Lib\urllib\request.py", line 1321, in do_open raise URLError(err) urllib.error.URLError: During handling of the above exception, another exception occurred: Traceback (most recent call last): File "", line 1, in File "C:\Users\travis\AppData\Local\Temp\pip-install-7zlcuzsx\py\setup.py", line 41, in main() File "C:\Users\travis\AppData\Local\Temp\pip-install-7zlcuzsx\py\setup.py", line 37, in main zip_safe=False, File "C:\Users\travis\build\cocotb\cocotb\.tox\py3_mingw\lib\site-packages\setuptools\__init__.py", line 144, in setup _install_setup_requires(attrs) File "C:\Users\travis\build\cocotb\cocotb\.tox\py3_mingw\lib\site-packages\setuptools\__init__.py", line 139, in _install_setup_requires dist.fetch_build_eggs(dist.setup_requires) File "C:\Users\travis\build\cocotb\cocotb\.tox\py3_mingw\lib\site-packages\setuptools\dist.py", line 719, in fetch_build_eggs replace_conflicting=True, File "C:\Users\travis\build\cocotb\cocotb\.tox\py3_mingw\lib\site-packages\pkg_resources\__init__.py", line 782, in resolve replace_conflicting=replace_conflicting File "C:\Users\travis\build\cocotb\cocotb\.tox\py3_mingw\lib\site-packages\pkg_resources\__init__.py", line 1065, in best_match return self.obtain(req, installer) File "C:\Users\travis\build\cocotb\cocotb\.tox\py3_mingw\lib\site-packages\pkg_resources\__init__.py", line 1077, in obtain return installer(requirement) File "C:\Users\travis\build\cocotb\cocotb\.tox\py3_mingw\lib\site-packages\setuptools\dist.py", line 786, in fetch_build_egg return cmd.easy_install(req) File "C:\Users\travis\build\cocotb\cocotb\.tox\py3_mingw\lib\site-packages\setuptools\command\easy_install.py", line 667, in easy_install not self.always_copy, self.local_index File "C:\Users\travis\build\cocotb\cocotb\.tox\py3_mingw\lib\site-packages\setuptools\package_index.py", line 655, in fetch_distribution dist = find(requirement) File "C:\Users\travis\build\cocotb\cocotb\.tox\py3_mingw\lib\site-packages\setuptools\package_index.py", line 635, in find loc = self.download(dist.location, tmpdir) File "C:\Users\travis\build\cocotb\cocotb\.tox\py3_mingw\lib\site-packages\setuptools\package_index.py", line 579, in download found = self._download_url(scheme.group(1), spec, tmpdir) File "C:\Users\travis\build\cocotb\cocotb\.tox\py3_mingw\lib\site-packages\setuptools\package_index.py", line 824, in _download_url return self._attempt_download(url, filename) File "C:\Users\travis\build\cocotb\cocotb\.tox\py3_mingw\lib\site-packages\setuptools\package_index.py", line 830, in _attempt_download headers = self._download_to(url, filename) File "C:\Users\travis\build\cocotb\cocotb\.tox\py3_mingw\lib\site-packages\setuptools\package_index.py", line 729, in _download_to fp = self.open_url(url) File "C:\Users\travis\build\cocotb\cocotb\.tox\py3_mingw\lib\site-packages\setuptools\package_index.py", line 780, in open_url % (url, v.reason)) distutils.errors.DistutilsError: Download error for https://files.pythonhosted.org/packages/ad/d3/e54f8b4cde0f6fb4f231629f570c1a33ded18515411dee6df6fe363d976f/setuptools_scm-4.1.2-py2.py3-none-any.whl#sha256=69258e2eeba5f7ce1ed7a5f109519580fa3578250f8e4d6684859f86d1b15826: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1076) ``` --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 0d2d15bf..da77741b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -95,6 +95,8 @@ jobs: - export CONDA_PATH="\$env:Path = ';C:\miniconda3;C:\miniconda3\Library\mingw-w64\bin;C:\miniconda3\Library\usr\bin;C:\miniconda3\Library\bin;C:\miniconda3\Scripts;C:\miniconda3\bin;C:\miniconda3\condabin;C:\iverilog\bin;' + \$env:Path" - powershell "$CONDA_PATH; conda install --yes -c msys2 m2-base m2-make m2w64-toolchain libpython" - powershell "$CONDA_PATH; conda install --yes virtualenv" + - powershell "$CONDA_PATH; conda update --yes pip" + - powershell "$CONDA_PATH; conda info; conda list --show-channel-urls" # needed to make the builtin sqlite3 module (used by coverage) work - see gh-1909 - powershell '$CONDA_PATH; cp C:\miniconda3\Library\bin\sqlite3.dll C:\miniconda3\DLLs\' - powershell "$CONDA_PATH; pip install tox" From 609cd3ad20e6ecfc1fbcb180356a44958535148c Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Mon, 22 Jun 2020 21:40:10 +0100 Subject: [PATCH 2382/2656] Travis: Fix command call Double quotes need to be used when $CONDA_PATH should actually be expanded, but it's not even used here. Using double quotes would also require escaping of the backslash, so keep the single quotes. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index da77741b..ae215e38 100644 --- a/.travis.yml +++ b/.travis.yml @@ -98,7 +98,7 @@ jobs: - powershell "$CONDA_PATH; conda update --yes pip" - powershell "$CONDA_PATH; conda info; conda list --show-channel-urls" # needed to make the builtin sqlite3 module (used by coverage) work - see gh-1909 - - powershell '$CONDA_PATH; cp C:\miniconda3\Library\bin\sqlite3.dll C:\miniconda3\DLLs\' + - powershell 'cp C:\miniconda3\Library\bin\sqlite3.dll C:\miniconda3\DLLs\sqlite3.dll' - powershell "$CONDA_PATH; pip install tox" script: # TODO[gh-1372]: the default compiler on windows, msvc, is not supported From c2acc9f4608660271f7035a178eb0cc9273ce065 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Tue, 23 Jun 2020 09:24:35 +0100 Subject: [PATCH 2383/2656] Update version number to 1.4.0rc1 --- cocotb/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/_version.py b/cocotb/_version.py index 46ea3afc..66348936 100644 --- a/cocotb/_version.py +++ b/cocotb/_version.py @@ -5,4 +5,4 @@ # 1) we don't load dependencies by storing it in __init__.py # 2) we can import it in setup.py for the same reason # 3) we can import it into your module -__version__ = '1.4.0.dev0' +__version__ = '1.4.0rc1' From 2fed4af8f9e2d9b2f6da071c2418ba8a4dac621c Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Tue, 23 Jun 2020 09:15:35 +0100 Subject: [PATCH 2384/2656] Convert the mixed-language example to use async/await --- examples/mixed_language/tests/test_mixed_language.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/mixed_language/tests/test_mixed_language.py b/examples/mixed_language/tests/test_mixed_language.py index 666a6c40..85a8ee51 100644 --- a/examples/mixed_language/tests/test_mixed_language.py +++ b/examples/mixed_language/tests/test_mixed_language.py @@ -4,9 +4,9 @@ @cocotb.test() -def mixed_language_test(dut): +async def mixed_language_test(dut): """Try accessing handles and setting values in a mixed language environment.""" - yield Timer(100, units='ns') + await Timer(100, units='ns') verilog = dut.i_swapper_sv dut._log.info("Got: %s" % repr(verilog._name)) @@ -15,10 +15,10 @@ def mixed_language_test(dut): dut._log.info("Got: %s" % repr(vhdl._name)) verilog.reset_n <= 1 - yield Timer(100, units='ns') + await Timer(100, units='ns') vhdl.reset_n <= 1 - yield Timer(100, units='ns') + await Timer(100, units='ns') if int(verilog.reset_n) == int(vhdl.reset_n): dut._log.info("Both signals read as %d" % int(vhdl.reset_n)) From 0c66996c8795f36e6499f88945fd53ee4b4d34bf Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Tue, 23 Jun 2020 09:18:00 +0100 Subject: [PATCH 2385/2656] Convert the endian_swapper example to use async/await --- .../tests/test_endian_swapper.py | 48 +++++++++---------- .../tests/test_endian_swapper_hal.py | 23 +++++---- 2 files changed, 33 insertions(+), 38 deletions(-) diff --git a/examples/endian_swapper/tests/test_endian_swapper.py b/examples/endian_swapper/tests/test_endian_swapper.py index edb53c83..7f2877a9 100644 --- a/examples/endian_swapper/tests/test_endian_swapper.py +++ b/examples/endian_swapper/tests/test_endian_swapper.py @@ -46,16 +46,15 @@ random_50_percent) -@cocotb.coroutine -def stream_out_config_setter(dut, stream_out, stream_in): +async def stream_out_config_setter(dut, stream_out, stream_in): """Coroutine to monitor the DUT configuration at the start of each packet transfer and set the endianness of the output stream accordingly""" edge = RisingEdge(dut.stream_in_startofpacket) ro = ReadOnly() while True: - yield edge - yield ro + await edge + await ro if dut.byteswapping.value: stream_out.config['firstSymbolInHighOrderBits'] = \ not stream_in.config['firstSymbolInHighOrderBits'] @@ -100,25 +99,23 @@ def model(self, transaction): self.expected_output.append(transaction) self.pkts_sent += 1 - @cocotb.coroutine - def reset(self, duration=20): + async def reset(self, duration=20): self.dut._log.debug("Resetting DUT") self.dut.reset_n <= 0 self.stream_in.bus.valid <= 0 - yield Timer(duration, units='ns') - yield RisingEdge(self.dut.clk) + await Timer(duration, units='ns') + await RisingEdge(self.dut.clk) self.dut.reset_n <= 1 self.dut._log.debug("Out of reset") -@cocotb.coroutine -def run_test(dut, data_in=None, config_coroutine=None, idle_inserter=None, - backpressure_inserter=None): +async def run_test(dut, data_in=None, config_coroutine=None, idle_inserter=None, + backpressure_inserter=None): cocotb.fork(Clock(dut.clk, 10, units='ns').start()) tb = EndianSwapperTB(dut) - yield tb.reset() + await tb.reset() dut.stream_out_ready <= 1 # Start off any optional coroutines @@ -131,15 +128,15 @@ def run_test(dut, data_in=None, config_coroutine=None, idle_inserter=None, # Send in the packets for transaction in data_in(): - yield tb.stream_in.send(transaction) + await tb.stream_in.send(transaction) # Wait at least 2 cycles where output ready is low before ending the test for i in range(2): - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) while not dut.stream_out_ready.value: - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) - pkt_count = yield tb.csr.read(1) + pkt_count = await tb.csr.read(1) if pkt_count.integer != tb.pkts_sent: raise TestFailure("DUT recorded %d packets but tb counted %d" % ( @@ -156,11 +153,10 @@ def random_packet_sizes(min_size=1, max_size=150, npackets=10): yield get_bytes(random.randint(min_size, max_size), random_data()) -@cocotb.coroutine -def randomly_switch_config(csr): +async def randomly_switch_config(csr): """Twiddle the byteswapping config register""" while True: - yield csr.write(0, random.randint(0, 1)) + await csr.write(0, random.randint(0, 1)) factory = TestFactory(run_test) @@ -178,19 +174,19 @@ def randomly_switch_config(csr): @cocotb.test() -def wavedrom_test(dut): +async def wavedrom_test(dut): """ Generate a JSON wavedrom diagram of a trace and save it to wavedrom.json """ cocotb.fork(Clock(dut.clk, 10, units='ns').start()) - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) tb = EndianSwapperTB(dut) - yield tb.reset() + await tb.reset() with cocotb.wavedrom.trace(dut.reset_n, tb.csr.bus, clk=dut.clk) as waves: - yield RisingEdge(dut.clk) - yield tb.csr.read(0) - yield RisingEdge(dut.clk) - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) + await tb.csr.read(0) + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) dut._log.info(waves.dumpj(header={'text':'WaveDrom example', 'tick':0})) waves.write('wavedrom.json', header={'tick':0}, config={'hscale':3}) diff --git a/examples/endian_swapper/tests/test_endian_swapper_hal.py b/examples/endian_swapper/tests/test_endian_swapper_hal.py index c930c7c3..b1e158c9 100644 --- a/examples/endian_swapper/tests/test_endian_swapper_hal.py +++ b/examples/endian_swapper/tests/test_endian_swapper_hal.py @@ -36,23 +36,22 @@ import io_module -@cocotb.coroutine -def reset(dut, duration=10): +async def reset(dut, duration=10): dut._log.debug("Resetting DUT") dut.reset_n = 0 dut.stream_in_valid = 0 - yield Timer(duration, units='ns') - yield RisingEdge(dut.clk) + await Timer(duration, units='ns') + await RisingEdge(dut.clk) dut.reset_n = 1 dut._log.debug("Out of reset") @cocotb.test() -def initial_hal_test(dut, debug=True): +async def initial_hal_test(dut, debug=True): """Example of using the software HAL against cosim testbench""" cocotb.fork(Clock(dut.clk, 5, units='ns').start()) - yield reset(dut) + await reset(dut) # Create the avalon master and direct our HAL calls to that master = AvalonMaster(dut, "csr", dut.clk) @@ -60,16 +59,16 @@ def initial_hal_test(dut, debug=True): master.log.setLevel(logging.DEBUG) @cocotb.function - def read(address): + async def read(address): master.log.debug("External source: reading address 0x%08X" % address) - value = yield master.read(address) + value = await master.read(address) master.log.debug("Reading complete: got value 0x%08x" % value) return value @cocotb.function - def write(address, value): + async def write(address, value): master.log.debug("Write called for 0x%08X -> %d" % (address, value)) - yield master.write(address, value) + await master.write(address, value) master.log.debug("Write complete") io_module.set_write_function(write) @@ -83,9 +82,9 @@ def write(address, value): if dut.byteswapping.value: raise TestFailure("Byteswapping is enabled but haven't configured DUT") - yield cocotb.external(hal.endian_swapper_enable)(state) + await cocotb.external(hal.endian_swapper_enable)(state) - yield ReadOnly() + await ReadOnly() if not dut.byteswapping.value: raise TestFailure("Byteswapping wasn't enabled after calling " From 8de3acedcb4bb1e5ca3726ec75f034c1e7fe7712 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Tue, 23 Jun 2020 09:19:07 +0100 Subject: [PATCH 2386/2656] Convert the axi_lite_slave example to use async/await --- .../tests/test_axi_lite_slave.py | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/examples/axi_lite_slave/tests/test_axi_lite_slave.py b/examples/axi_lite_slave/tests/test_axi_lite_slave.py index 005b6dc8..837cc27f 100644 --- a/examples/axi_lite_slave/tests/test_axi_lite_slave.py +++ b/examples/axi_lite_slave/tests/test_axi_lite_slave.py @@ -19,7 +19,7 @@ def setup_dut(dut): # Write to address 0 and verify that value got through @cocotb.test() -def write_address_0(dut): +async def write_address_0(dut): """Write to the register at address 0, verify the value has changed. Test ID: 0 @@ -34,14 +34,14 @@ def write_address_0(dut): dut.test_id <= 0 axim = AXI4LiteMaster(dut, "AXIML", dut.clk) setup_dut(dut) - yield Timer(CLK_PERIOD_NS * 10, units='ns') + await Timer(CLK_PERIOD_NS * 10, units='ns') dut.rst <= 0 ADDRESS = 0x00 DATA = 0xAB - yield axim.write(ADDRESS, DATA) - yield Timer(CLK_PERIOD_NS * 10, units='ns') + await axim.write(ADDRESS, DATA) + await Timer(CLK_PERIOD_NS * 10, units='ns') value = dut.dut.r_temp_0 if value != DATA: @@ -54,7 +54,7 @@ def write_address_0(dut): # Read back a value at address 0x01 @cocotb.test() -def read_address_1(dut): +async def read_address_1(dut): """Use cocotb to set the value of the register at address 0x01. Use AXIML to read the contents of that register and compare the values. @@ -69,17 +69,17 @@ def read_address_1(dut): dut.test_id <= 1 axim = AXI4LiteMaster(dut, "AXIML", dut.clk) setup_dut(dut) - yield Timer(CLK_PERIOD_NS * 10, units='ns') + await Timer(CLK_PERIOD_NS * 10, units='ns') dut.rst <= 0 - yield Timer(CLK_PERIOD_NS, units='ns') + await Timer(CLK_PERIOD_NS, units='ns') ADDRESS = 0x01 DATA = 0xCD dut.dut.r_temp_1 <= DATA - yield Timer(CLK_PERIOD_NS * 10, units='ns') + await Timer(CLK_PERIOD_NS * 10, units='ns') - value = yield axim.read(ADDRESS) - yield Timer(CLK_PERIOD_NS * 10, units='ns') + value = await axim.read(ADDRESS) + await Timer(CLK_PERIOD_NS * 10, units='ns') if value != DATA: # Fail @@ -90,7 +90,7 @@ def read_address_1(dut): @cocotb.test() -def write_and_read(dut): +async def write_and_read(dut): """Write to the register at address 0. Read back from that register and verify the value is the same. @@ -105,19 +105,19 @@ def write_and_read(dut): dut.test_id <= 2 axim = AXI4LiteMaster(dut, "AXIML", dut.clk) setup_dut(dut) - yield Timer(CLK_PERIOD_NS * 10, units='ns') + await Timer(CLK_PERIOD_NS * 10, units='ns') dut.rst <= 0 ADDRESS = 0x00 DATA = 0xAB # Write to the register - yield axim.write(ADDRESS, DATA) - yield Timer(CLK_PERIOD_NS * 10, units='ns') + await axim.write(ADDRESS, DATA) + await Timer(CLK_PERIOD_NS * 10, units='ns') # Read back the value - value = yield axim.read(ADDRESS) - yield Timer(CLK_PERIOD_NS * 10, units='ns') + value = await axim.read(ADDRESS) + await Timer(CLK_PERIOD_NS * 10, units='ns') value = dut.dut.r_temp_0 if value != DATA: @@ -129,7 +129,7 @@ def write_and_read(dut): @cocotb.test() -def write_fail(dut): +async def write_fail(dut): """Attempt to write data to an address that doesn't exist. This test should fail. @@ -144,15 +144,15 @@ def write_fail(dut): dut.test_id <= 3 axim = AXI4LiteMaster(dut, "AXIML", dut.clk) setup_dut(dut) - yield Timer(CLK_PERIOD_NS * 10, units='ns') + await Timer(CLK_PERIOD_NS * 10, units='ns') dut.rst <= 0 ADDRESS = 0x02 DATA = 0xAB try: - yield axim.write(ADDRESS, DATA) - yield Timer(CLK_PERIOD_NS * 10, units='ns') + await axim.write(ADDRESS, DATA) + await Timer(CLK_PERIOD_NS * 10, units='ns') except AXIProtocolError as e: print("Exception: %s" % str(e)) dut._log.info("Bus successfully raised an error") @@ -162,7 +162,7 @@ def write_fail(dut): @cocotb.test() -def read_fail(dut): +async def read_fail(dut): """Attempt to read data from an address that doesn't exist. This test should fail. @@ -177,15 +177,15 @@ def read_fail(dut): dut.test_id <= 4 axim = AXI4LiteMaster(dut, "AXIML", dut.clk) setup_dut(dut) - yield Timer(CLK_PERIOD_NS * 10, units='ns') + await Timer(CLK_PERIOD_NS * 10, units='ns') dut.rst <= 0 ADDRESS = 0x02 DATA = 0xAB try: - yield axim.read(ADDRESS, DATA) - yield Timer(CLK_PERIOD_NS * 10, units='ns') + await axim.read(ADDRESS, DATA) + await Timer(CLK_PERIOD_NS * 10, units='ns') except AXIProtocolError as e: print("Exception: %s" % str(e)) dut._log.info("Bus Successfully Raised an Error") From e5e7876d1edac2a46122fce5d4af1a3b858c7aac Mon Sep 17 00:00:00 2001 From: elgorwi Date: Thu, 4 Jun 2020 20:59:48 +1000 Subject: [PATCH 2387/2656] Issue #1851: improve VPI log debug messages --- cocotb/share/lib/vpi/VpiCbHdl.cpp | 6 +++--- cocotb/share/lib/vpi/VpiImpl.cpp | 2 +- cocotb/share/lib/vpi/VpiImpl.h | 5 ++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/cocotb/share/lib/vpi/VpiCbHdl.cpp b/cocotb/share/lib/vpi/VpiCbHdl.cpp index 7f305e50..4a7f918a 100644 --- a/cocotb/share/lib/vpi/VpiCbHdl.cpp +++ b/cocotb/share/lib/vpi/VpiCbHdl.cpp @@ -713,10 +713,10 @@ VpiIterator::VpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(i return; } - LOG_DEBUG("Created iterator working from type %d %s (%s)", - *one2many, + LOG_DEBUG("Created iterator working from '%s' with type %s(%d)", vpi_get_str(vpiFullName, vpi_hdl), - vpi_get_str(vpiType, vpi_hdl)); + vpi_get_str(vpiType, vpi_hdl), + type); m_iterator = iterator; } diff --git a/cocotb/share/lib/vpi/VpiImpl.cpp b/cocotb/share/lib/vpi/VpiImpl.cpp index d945fa67..a6c2d8ba 100644 --- a/cocotb/share/lib/vpi/VpiImpl.cpp +++ b/cocotb/share/lib/vpi/VpiImpl.cpp @@ -241,7 +241,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, new_obj->initialise(name, fq_name); - LOG_DEBUG("VPI: Created object with type was %s(%d)", + LOG_DEBUG("VPI: Created GPI object from type %s(%d)", vpi_get_str(vpiType, new_hdl), type); return new_obj; diff --git a/cocotb/share/lib/vpi/VpiImpl.h b/cocotb/share/lib/vpi/VpiImpl.h index 8974bc12..6fdeffb2 100644 --- a/cocotb/share/lib/vpi/VpiImpl.h +++ b/cocotb/share/lib/vpi/VpiImpl.h @@ -65,9 +65,8 @@ static inline int __check_vpi_error(const char *file, const char *func, long lin loglevel = GPIWarning; } - gpi_log("cocotb.gpi", loglevel, file, func, line, - "VPI Error %s\nPROD %s\nCODE %s\nFILE %s", - info.message, info.product, info.code, info.file); + gpi_log("cocotb.gpi", loglevel, file, func, line, "VPI error"); + gpi_log("cocotb.gpi", loglevel, info.file, info.product, info.line, info.message); #endif return level; From f94ec110ca6a6d3fbfa84bcd05187f205262353b Mon Sep 17 00:00:00 2001 From: elgorwi Date: Wed, 3 Jun 2020 10:19:55 +1000 Subject: [PATCH 2388/2656] Issue #1851: fix mean example verilator issues --- examples/mean/hdl/mean.sv | 18 ++++++++++++++---- examples/mean/tests/Makefile | 4 ++++ examples/mean/tests/verilator_waiver.vlt | 7 +++++++ 3 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 examples/mean/tests/verilator_waiver.vlt diff --git a/examples/mean/hdl/mean.sv b/examples/mean/hdl/mean.sv index 3a6e53b6..4cb35868 100644 --- a/examples/mean/hdl/mean.sv +++ b/examples/mean/hdl/mean.sv @@ -7,10 +7,16 @@ `timescale 1ns/1ps +`ifdef VERILATOR // make parameter readable from VPI + `define VL_RD /*verilator public_flat_rd*/ +`else + `define VL_RD +`endif + module mean #( - parameter int BUS_WIDTH = 4, - parameter int DATA_WIDTH = 6 + parameter int BUS_WIDTH `VL_RD = 4, + parameter int DATA_WIDTH `VL_RD = 6 ) ( input logic clk, input logic rst, @@ -60,13 +66,17 @@ module mean #( assign o_data = s_sum >> $clog2(BUS_WIDTH); +`ifndef VERILATOR initial begin int idx; $dumpfile("dump.vcd"); $dumpvars(0, mean); - for (idx = 0; idx < BUS_WIDTH; idx++) + `ifdef __ICARUS__ + for (idx = 0; idx < BUS_WIDTH; idx++) begin $dumpvars(0, i_data[idx]); - #1; + end + `endif end +`endif endmodule diff --git a/examples/mean/tests/Makefile b/examples/mean/tests/Makefile index f4bf5d1c..7e1f55c1 100644 --- a/examples/mean/tests/Makefile +++ b/examples/mean/tests/Makefile @@ -21,4 +21,8 @@ ifneq ($(filter $(SIM),ius xcelium),) SIM_ARGS += -v93 endif +ifeq ($(SIM),verilator) + EXTRA_ARGS += $(PWD)/verilator_waiver.vlt +endif + include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/examples/mean/tests/verilator_waiver.vlt b/examples/mean/tests/verilator_waiver.vlt new file mode 100644 index 00000000..ebfc8ec2 --- /dev/null +++ b/examples/mean/tests/verilator_waiver.vlt @@ -0,0 +1,7 @@ +// This file is public domain, it can be freely copied without restrictions. +// SPDX-License-Identifier: CC0-1.0 + +// waivers for verilator + +`verilator_config +lint_off -rule WIDTH From afaf14a4198807417382197f34bfd886f2ddce47 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Thu, 14 May 2020 21:54:00 -0500 Subject: [PATCH 2389/2656] Cleanup adder example vhdl --- examples/adder/hdl/adder.vhdl | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/examples/adder/hdl/adder.vhdl b/examples/adder/hdl/adder.vhdl index b6d00e58..cce11a23 100644 --- a/examples/adder/hdl/adder.vhdl +++ b/examples/adder/hdl/adder.vhdl @@ -1,24 +1,26 @@ +-- This file is public domain, it can be freely copied without restrictions. +-- SPDX-License-Identifier: CC0-1.0 -- Adder DUT -library ieee ; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; +library ieee; + use ieee.std_logic_1164.all; + use ieee.numeric_ + std.all; entity adder is -generic( + generic ( DATA_WIDTH : positive := 4); -port( - A : in unsigned(DATA_WIDTH-1 downto 0); - B : in unsigned(DATA_WIDTH-1 downto 0); - X : out unsigned(DATA_WIDTH downto 0) - ); -end adder; + port ( + A : in unsigned(DATA_WIDTH-1 downto 0); + B : in unsigned(DATA_WIDTH-1 downto 0); + X : out unsigned(DATA_WIDTH downto 0)); +end entity adder; -architecture RTL of adder is +architecture rtl of adder is begin - process(A, B) - begin - X <= resize(A, X'length) + B; - end process; + add_proc : process (A, B) is + begin + X <= resize(A, X'length) + B; + end process add_proc; -end RTL; +end architecture rtl; From 55b06e79d381920429e85403ae6f726d5bfa979e Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Thu, 14 May 2020 21:55:03 -0500 Subject: [PATCH 2390/2656] Cleanup adder example verilog --- examples/adder/hdl/adder.v | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/examples/adder/hdl/adder.v b/examples/adder/hdl/adder.v index f9adced6..e8a81ab2 100644 --- a/examples/adder/hdl/adder.v +++ b/examples/adder/hdl/adder.v @@ -1,25 +1,22 @@ +// This file is public domain, it can be freely copied without restrictions. +// SPDX-License-Identifier: CC0-1.0 // Adder DUT - `timescale 1ns/1ps module adder #( - parameter DATA_WIDTH = 4 + parameter integer DATA_WIDTH = 4 ) ( - input [DATA_WIDTH-1:0] A, - input [DATA_WIDTH-1:0] B, - output reg [DATA_WIDTH:0] X - ); + input logic unsigned [DATA_WIDTH-1:0] A, + input logic unsigned [DATA_WIDTH-1:0] B, + output logic unsigned [DATA_WIDTH:0] X +); - always @(A or B) begin - X = A + B; - end + assign X = A + B; -`ifndef VERILATOR // traced differently // Dump waves initial begin $dumpfile("dump.vcd"); $dumpvars(1, adder); end -`endif endmodule From 36c10943f8e58449fb4ebbac968883572ac3fc19 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Thu, 14 May 2020 22:12:04 -0500 Subject: [PATCH 2391/2656] Update adder example test and model --- examples/adder/model/adder_model.py | 6 ++++- examples/adder/tests/test_adder.py | 37 ++++++++++++----------------- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/examples/adder/model/adder_model.py b/examples/adder/model/adder_model.py index 14d379e4..61eb547c 100644 --- a/examples/adder/model/adder_model.py +++ b/examples/adder/model/adder_model.py @@ -1,3 +1,7 @@ -def adder_model(a, b): +# This file is public domain, it can be freely copied without restrictions. +# SPDX-License-Identifier: CC0-1.0 + + +def adder_model(a: int, b: int) -> int: """ model of adder """ return a + b diff --git a/examples/adder/tests/test_adder.py b/examples/adder/tests/test_adder.py index e29698fb..67c5e77e 100644 --- a/examples/adder/tests/test_adder.py +++ b/examples/adder/tests/test_adder.py @@ -1,47 +1,40 @@ +# This file is public domain, it can be freely copied without restrictions. +# SPDX-License-Identifier: CC0-1.0 # Simple tests for an adder module import cocotb from cocotb.triggers import Timer -from cocotb.result import TestFailure from adder_model import adder_model import random @cocotb.test() -def adder_basic_test(dut): +async def adder_basic_test(dut): """Test for 5 + 10""" - yield Timer(2, units='ns') + A = 5 B = 10 - dut.A = A - dut.B = B + dut.A <= A + dut.B <= B - yield Timer(2, units='ns') + await Timer(2, units='ns') - if int(dut.X) != adder_model(A, B): - raise TestFailure( - "Adder result is incorrect: %s != 15" % str(dut.X)) - else: # these last two lines are not strictly necessary - dut._log.info("Ok!") + assert int(dut.X) == adder_model(A, B), "Adder result is incorrect: {} != 15".format(dut.X) @cocotb.test() -def adder_randomised_test(dut): +async def adder_randomised_test(dut): """Test for adding 2 random numbers multiple times""" - yield Timer(2, units='ns') for i in range(10): + A = random.randint(0, 15) B = random.randint(0, 15) - dut.A = A - dut.B = B + dut.A <= A + dut.B <= B - yield Timer(2, units='ns') + await Timer(2, units='ns') - if int(dut.X) != adder_model(A, B): - raise TestFailure( - "Randomised test failed with: %s + %s = %s" % - (int(dut.A), int(dut.B), int(dut.X))) - else: # these last two lines are not strictly necessary - dut._log.info("Ok!") + assert int(dut.X) == adder_model(A, B), "Randomised test failed with: {A} + {B} = {X}".format( + A=int(dut.A), B=int(dut.B), X=int(dut.X)) From 213369e7ced53a7f0b4be88991d5368eea30aca3 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 10 Jun 2020 17:15:51 -0500 Subject: [PATCH 2392/2656] Use .value instead of int casts in adder example tests --- examples/adder/tests/test_adder.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/adder/tests/test_adder.py b/examples/adder/tests/test_adder.py index 67c5e77e..33a7082d 100644 --- a/examples/adder/tests/test_adder.py +++ b/examples/adder/tests/test_adder.py @@ -19,7 +19,7 @@ async def adder_basic_test(dut): await Timer(2, units='ns') - assert int(dut.X) == adder_model(A, B), "Adder result is incorrect: {} != 15".format(dut.X) + assert dut.X.value == adder_model(A, B), "Adder result is incorrect: {} != 15".format(dut.X.value) @cocotb.test() @@ -36,5 +36,5 @@ async def adder_randomised_test(dut): await Timer(2, units='ns') - assert int(dut.X) == adder_model(A, B), "Randomised test failed with: {A} + {B} = {X}".format( - A=int(dut.A), B=int(dut.B), X=int(dut.X)) + assert dut.X.value == adder_model(A, B), "Randomised test failed with: {A} + {B} = {X}".format( + A=dut.A.value, B=dut.B.value, X=dut.X.value) From 68b08c99c1de7230809b8a5ebda9f09f13b0f7d3 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Thu, 11 Jun 2020 19:42:52 -0500 Subject: [PATCH 2393/2656] Change adder example verilog extension to .sv --- examples/adder/hdl/{adder.v => adder.sv} | 0 examples/adder/tests/Makefile | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename examples/adder/hdl/{adder.v => adder.sv} (100%) diff --git a/examples/adder/hdl/adder.v b/examples/adder/hdl/adder.sv similarity index 100% rename from examples/adder/hdl/adder.v rename to examples/adder/hdl/adder.sv diff --git a/examples/adder/tests/Makefile b/examples/adder/tests/Makefile index 1865e8ca..d49e45fe 100644 --- a/examples/adder/tests/Makefile +++ b/examples/adder/tests/Makefile @@ -34,7 +34,7 @@ PWD=$(shell pwd) export PYTHONPATH := $(PWD)/../model:$(PYTHONPATH) ifeq ($(TOPLEVEL_LANG),verilog) - VERILOG_SOURCES = $(PWD)/../hdl/adder.v + VERILOG_SOURCES = $(PWD)/../hdl/adder.sv else ifeq ($(TOPLEVEL_LANG),vhdl) VHDL_SOURCES = $(PWD)/../hdl/adder.vhdl else From f802b2ed817534d9988086f6da983c12ab89ae5b Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 7 Jul 2020 22:51:19 +0200 Subject: [PATCH 2394/2656] Mention Icarus versions not supporting bit access Closes #1952. --- documentation/source/simulator_support.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 33597fc8..ccd5f979 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -10,8 +10,8 @@ and documents specifics, limitations, workarounds etc. .. _sim-icarus: -Icarus -====== +Icarus Verilog +============== In order to use this simulator, set :make:var:`SIM` to ``icarus``: @@ -24,13 +24,13 @@ In order to use this simulator, set :make:var:`SIM` to ``icarus``: Accessing bits in a vector -------------------------- -Accessing bits of a vector doesn't work: +Accessing bits of a vector directly was not possible until (including) version 10.3: .. code-block:: python3 dut.stream_in_data[2] <= 1 -See the ``access_single_bit`` test in :file:`tests/test_cases/test_discovery/test_discovery.py`. +See also https://github.com/steveicarus/iverilog/issues/323. .. _sim-icarus-waveforms: From b16e612c67601ac84cf3660016c390e6d494b4b0 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Tue, 7 Jul 2020 21:14:28 +0100 Subject: [PATCH 2395/2656] Document known issues with multiple Python installs Using cocotb with multiple Python environments requires extra care not to re-use build artifacts. Fixes #1948 --- documentation/source/troubleshooting.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/documentation/source/troubleshooting.rst b/documentation/source/troubleshooting.rst index 3e4aed4d..abb7b700 100644 --- a/documentation/source/troubleshooting.rst +++ b/documentation/source/troubleshooting.rst @@ -144,3 +144,16 @@ When you install cocotb, ``pip`` uses the system (or some other) compiler that s But when you try to run cocotb with the older Questa, it prepends the older libraries Questa ships with to ``LD_LIBRARY_PATH``. This causes the older libstdc++ Questa ships with to be loaded, resuling in the error message. For Questa, you can use the ``-noautoldlibpath`` option to turn off the ``LD_LIBRARY_PATH`` prepend to resolve this issue. + + +Using cocotb with more than one Python installation +=================================================== + +Users of cocotb with more than one installation of a single Python version (including conda env users) must take care not to re-use cached versions of the installed cocotb package. +If this isn't done, running simulations fails with errors like ``libpython3.7m.so.1.0: cannot open shared object file: No such file or directory``. + +Cocotb builds binary libraries during its installation process. +These libraries are tailored to the installation of Python used when installing cocotb. +When switching between Python installations, cocotb needs to be re-installed without using cached build artifacts, e.g. with ``pip install --no-cache-dir cocotb``. + +Further details are available in :issue:`1943`. From 50e48f0bb30c235f81a4a261c8cfdd685917058c Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Tue, 7 Jul 2020 23:37:35 +0100 Subject: [PATCH 2396/2656] Update version number to 1.4.0rc2 --- cocotb/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/_version.py b/cocotb/_version.py index 66348936..63ef9a7c 100644 --- a/cocotb/_version.py +++ b/cocotb/_version.py @@ -5,4 +5,4 @@ # 1) we don't load dependencies by storing it in __init__.py # 2) we can import it in setup.py for the same reason # 3) we can import it into your module -__version__ = '1.4.0rc1' +__version__ = '1.4.0rc2' From 9d66a893d395d992fbdec074dbfee227d3266ba6 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Wed, 8 Jul 2020 18:22:44 +0100 Subject: [PATCH 2397/2656] Documentation: Fix error in coroutines doc --- documentation/source/coroutines.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/coroutines.rst b/documentation/source/coroutines.rst index 7c2f0fca..855eaef0 100644 --- a/documentation/source/coroutines.rst +++ b/documentation/source/coroutines.rst @@ -61,7 +61,7 @@ Coroutines can be used with :func:`~cocotb.fork` for concurrent operation. await Timer(10, units='ns') print("Reset is still active: %d" % dut.rstn) await Timer(15, units='ns') - await("Reset has gone inactive: %d" % dut.rstn) + print("Reset has gone inactive: %d" % dut.rstn) Forked coroutines can be used in an :keyword:`await` statement to block until the forked coroutine finishes. From 16fd991c5475aa065d4c13955171eb56c3b16189 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sun, 12 Apr 2020 00:28:43 +0200 Subject: [PATCH 2398/2656] Document private members in cocotb.handle These members are starting with an underscore so as to not collide with regular HDL names, though most are not really private in the usual meaning but are useful for user code. Some type hints are added to aid documentation. Closes #1636 --- cocotb/handle.py | 76 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 24 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 2c61e8af..0e1e0814 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -67,18 +67,42 @@ def __init__(self, handle, path): path (str): Path to this handle, ``None`` if root. """ self._handle = handle - self._len = None - self._sub_handles = {} # Dictionary of children - self._invalid_sub_handles = set() # Set of invalid queries - - self._name = self._handle.get_name_string() - self._type = self._handle.get_type_string() - self._fullname = self._name + "(%s)" % self._type - self._path = self._name if path is None else path + self._len = None # type: int + """The "length" (the number of elements) of the underlying object. For vectors this is the number of bits.""" + self._sub_handles = {} # type: dict + """Dictionary of this handle's children.""" + self._invalid_sub_handles = set() # type: set + """Python :class:`set` of invalid queries, for caching purposes.""" + self._name = self._handle.get_name_string() # type: str + """The name of an object. + + :meta public: + """ + self._type = self._handle.get_type_string() # type: str + """The type of an object as a string. + + :meta public: + """ + self._fullname = self._name + "(%s)" % self._type # type: str + """The name of an object with its type appended in parentheses.""" + self._path = self._name if path is None else path # type: str + """The path to this handle, or its name if this is the root handle. + + :meta public: + """ self._log = SimLog("cocotb.%s" % self._name) + """The logging object.""" self._log.debug("Created") - self._def_name = self._handle.get_definition_name() - self._def_file = self._handle.get_definition_file() + self._def_name = self._handle.get_definition_name() # type: str + """The name of a GPI object's definition. + + :meta public: + """ + self._def_file = self._handle.get_definition_file() # type: str + """The file that sources the object's definition. + + :meta public: + """ def get_definition_name(self): return self._def_name @@ -90,7 +114,7 @@ def __hash__(self): return hash(self._handle) def __len__(self): - """Returns the 'length' of the underlying object. + """Return the "length" (the number of elements) of the underlying object. For vectors this is the number of bits. """ @@ -155,7 +179,7 @@ class RegionObject(SimHandleBase): def __init__(self, handle, path): SimHandleBase.__init__(self, handle, path) - self._discovered = False + self._discovered = False # True if this object has already been discovered def __iter__(self): """Iterate over all known objects in this layer of hierarchy.""" @@ -176,8 +200,8 @@ def __iter__(self): yield handle def _discover_all(self): - """When iterating or performing tab completion, we run through ahead of - time and discover all possible children, populating the ``_sub_handles`` + """When iterating or performing IPython tab completion, we run through ahead of + time and discover all possible children, populating the :any:`_sub_handles` mapping. Hierarchy can't change after elaboration so we only have to do this once. """ @@ -202,12 +226,12 @@ def _discover_all(self): self._discovered = True - def _child_path(self, name): - """Returns a string of the path of the child :any:`SimHandle` for a given *name*.""" + def _child_path(self, name) -> str: + """Return a string of the path of the child :any:`SimHandle` for a given *name*.""" return self._path + "." + name def _sub_handle_key(self, name): - """Translates the handle name to a key to use in ``_sub_handles`` dictionary.""" + """Translate the handle name to a key to use in :any:`_sub_handles` dictionary.""" return name.split(".")[-1] def __dir__(self): @@ -266,7 +290,7 @@ def __setattr__(self, name, value): raise AttributeError("%s contains no object named %s" % (self._name, name)) def __getattr__(self, name): - """Query the simulator for a object with the specified name + """Query the simulator for an object with the specified name and cache the result to build a tree of objects. """ if name.startswith("_"): @@ -281,10 +305,14 @@ def __getattr__(self, name): raise AttributeError("%s contains no object named %s" % (self._name, name)) - def _id(self, name, extended=True): - """Query the simulator for a object with the specified name, - including extended identifiers, + def _id(self, name, extended: bool = True): + """Query the simulator for an object with the specified *name*, and cache the result to build a tree of objects. + + If *extended* is ``True``, run the query only for VHDL extended identifiers. + For Verilog, only ``extended=False`` is supported. + + :meta public: """ if extended: name = "\\"+name+"\\" @@ -300,7 +328,7 @@ class HierarchyArrayObject(RegionObject): """Hierarchy Arrays are containers of Hierarchy Objects.""" def _sub_handle_key(self, name): - """Translates the handle name to a key to use in ``_sub_handles`` dictionary.""" + """Translate the handle name to a key to use in :any:`_sub_handles` dictionary.""" # This is slightly hacky, but we need to extract the index from the name # # FLI and VHPI(IUS): _name(X) where X is the index @@ -319,7 +347,7 @@ def _sub_handle_key(self, name): raise ValueError("Unable to match an index pattern: {}".format(name)) def __len__(self): - """Returns the 'length' of the generate block.""" + """Return the "length" of the generate block.""" if self._len is None: if not self._discovered: self._discover_all() @@ -340,7 +368,7 @@ def __getitem__(self, index): return self._sub_handles[index] def _child_path(self, name): - """Returns a string of the path of the child :any:`SimHandle` for a given name.""" + """Return a string of the path of the child :any:`SimHandle` for a given name.""" index = self._sub_handle_key(name) return self._path + "[" + str(index) + "]" From 9b930054027f9497dde712b29c5237eb9230920c Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Wed, 8 Jul 2020 19:24:46 +0100 Subject: [PATCH 2399/2656] Update version number to 1.4.0 --- cocotb/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/_version.py b/cocotb/_version.py index 63ef9a7c..42822237 100644 --- a/cocotb/_version.py +++ b/cocotb/_version.py @@ -5,4 +5,4 @@ # 1) we don't load dependencies by storing it in __init__.py # 2) we can import it in setup.py for the same reason # 3) we can import it into your module -__version__ = '1.4.0rc2' +__version__ = '1.4.0' From 7bc1d9af33770f0b4ff8195b2f2c73d5840dae1c Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Wed, 8 Jul 2020 21:43:44 +0100 Subject: [PATCH 2400/2656] Update version number to 1.5.0.dev0 --- cocotb/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/_version.py b/cocotb/_version.py index 42822237..102fd7f6 100644 --- a/cocotb/_version.py +++ b/cocotb/_version.py @@ -5,4 +5,4 @@ # 1) we don't load dependencies by storing it in __init__.py # 2) we can import it in setup.py for the same reason # 3) we can import it into your module -__version__ = '1.4.0' +__version__ = '1.5.0.dev0' From df52226db8d0bfe8f948a8872094d0c4cead387d Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 24 Jun 2020 19:20:53 -0500 Subject: [PATCH 2401/2656] Add release notes for 1.3.2 --- documentation/source/release_notes.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/documentation/source/release_notes.rst b/documentation/source/release_notes.rst index f587e38f..bcdf8ee7 100644 --- a/documentation/source/release_notes.rst +++ b/documentation/source/release_notes.rst @@ -8,6 +8,15 @@ All releases are available from the `GitHub Releases Page Date: Wed, 8 Jul 2020 22:45:05 +0100 Subject: [PATCH 2402/2656] Update release notes for 1.3.2 --- documentation/source/release_notes.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/documentation/source/release_notes.rst b/documentation/source/release_notes.rst index bcdf8ee7..7cdf591e 100644 --- a/documentation/source/release_notes.rst +++ b/documentation/source/release_notes.rst @@ -11,6 +11,8 @@ All releases are available from the `GitHub Releases Page Date: Fri, 13 Mar 2020 09:47:34 -0700 Subject: [PATCH 2403/2656] Update VHPI callbacks. The current use of the `vhpiCbRepEndOfProcesses` callback for `ReadWrite` triggers can cause subtle issues in simulation. Signal values can take multiple delta cycles to propogate through designs, and `vhpiCbRepEndOfProcesses` is called at the end of the current delta cycle. This means that depending on when signal values are cached when using the `<=` operator, signal values may be set at the wrong time relative to other signals. Changing `ReadWrite` to `vhpiCbRepLastKnownDeltaCycle` and `ReadOnly` to `vhpiCbRepEndOfTimeStep` brings VHPI behavior more in-line with VPI. See the following references for further details: IEEE Std 1076-2008 VHDL Language Ref Manual Section 14.7.5 and 21.3.6.1 IEEE Std 1364-2001 Verilog Hardware Description Language Section 27.33.2 --- cocotb/share/lib/vhpi/VhpiCbHdl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp index 3eb1abc9..c13ed693 100644 --- a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp +++ b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp @@ -901,14 +901,14 @@ int VhpiTimedCbHdl::cleanup_callback() VhpiReadwriteCbHdl::VhpiReadwriteCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), VhpiCbHdl(impl) { - cb_data.reason = vhpiCbRepEndOfProcesses; + cb_data.reason = vhpiCbRepLastKnownDeltaCycle; cb_data.time = &vhpi_time; } VhpiReadOnlyCbHdl::VhpiReadOnlyCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), VhpiCbHdl(impl) { - cb_data.reason = vhpiCbRepLastKnownDeltaCycle; + cb_data.reason = vhpiCbRepEndOfTimeStep; cb_data.time = &vhpi_time; } From d86bfa56c38c7e483fc246e10b7f2c61289a6490 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Thu, 9 Jul 2020 16:15:33 +0200 Subject: [PATCH 2404/2656] fix vhdl error in adder example --- examples/adder/hdl/adder.vhdl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/adder/hdl/adder.vhdl b/examples/adder/hdl/adder.vhdl index cce11a23..dcce35f3 100644 --- a/examples/adder/hdl/adder.vhdl +++ b/examples/adder/hdl/adder.vhdl @@ -3,8 +3,7 @@ -- Adder DUT library ieee; use ieee.std_logic_1164.all; - use ieee.numeric_ - std.all; + use ieee.numeric_std.all; entity adder is generic ( From 15762d29f6dda1a229c966a204ed6959138225b6 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Thu, 28 May 2020 12:01:02 +0200 Subject: [PATCH 2405/2656] testing riviera vhdl CI --- tests/test_cases/test_array/test_array.py | 2 +- tests/test_cases/test_cocotb/test_timing_triggers.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py index 11e49d7d..04d9b6bd 100644 --- a/tests/test_cases/test_array/test_array.py +++ b/tests/test_cases/test_array/test_array.py @@ -349,7 +349,7 @@ def test_discover_all(dut): dummy = hdl.sig if cocotb.LANGUAGE in ["vhdl"] and cocotb.SIM_NAME.lower().startswith(("riviera")): - pass_total = 818 + pass_total = 571 elif cocotb.LANGUAGE in ["vhdl"]: pass_total = 856 elif cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")): diff --git a/tests/test_cases/test_cocotb/test_timing_triggers.py b/tests/test_cases/test_cocotb/test_timing_triggers.py index 2e312abe..19b7eaa2 100644 --- a/tests/test_cases/test_cocotb/test_timing_triggers.py +++ b/tests/test_cases/test_cocotb/test_timing_triggers.py @@ -118,6 +118,7 @@ def do_test_afterdelay_in_readonly(dut, delay): @cocotb.test(expect_error=True, + skip=cocotb.LANGUAGE in ["vhdl"] and cocotb.SIM_NAME.lower().startswith(("riviera")), # gh-1245 expect_fail=cocotb.SIM_NAME.lower().startswith(("icarus", "riviera", "modelsim", From f2f41bf0f9403c6e8398c1948aa10190e2958f17 Mon Sep 17 00:00:00 2001 From: Tim Pambor Date: Fri, 15 May 2020 11:06:17 +0200 Subject: [PATCH 2406/2656] Add Icarus master on Windows to CI --- .travis.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.travis.yml b/.travis.yml index ae215e38..ff95960e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -104,6 +104,31 @@ jobs: # TODO[gh-1372]: the default compiler on windows, msvc, is not supported - powershell "$CONDA_PATH; tox -e py3_mingw" + - stage: SimTests + name: "Py 3.7, Icarus devel" + env: ALLOW_FAIL=yes + python: 3.7 + os: windows + language: shell + before_install: + - powershell "Invoke-WebRequest -outfile miniconda3.exe https://repo.continuum.io/miniconda/Miniconda3-latest-Windows-x86_64.exe" + - powershell "Start-Process .\miniconda3.exe -ArgumentList '/S /D=C:\miniconda3' -Wait" + + - export CONDA_PATH="\$env:Path = ';C:\miniconda3;C:\miniconda3\Library\mingw-w64\bin;C:\miniconda3\Library\usr\bin;C:\miniconda3\Library\bin;C:\miniconda3\Scripts;C:\miniconda3\bin;C:\miniconda3\condabin;C:\miniconda3\Library\usr\local\bin;' + \$env:Path" + - powershell "$CONDA_PATH; conda install --yes -c msys2 m2-base m2-make m2-autoconf m2-flex m2-bison m2-gperf m2w64-toolchain libpython" + - powershell "$CONDA_PATH; conda install --yes virtualenv" + - powershell "$CONDA_PATH; pip install tox" + + - powershell "$CONDA_PATH; if (!(Test-Path iverilog/README.txt)) { Get-ChildItem -Path iverilog -Recurse | Remove-Item -Force -Recurse -ErrorAction SilentlyContinue; Remove-Item iverilog -Force -Recurse -ErrorAction SilentlyContinue; git clone --depth=1 https://github.com/steveicarus/iverilog.git } else { cd iverilog; git fetch origin; cd ..}" + - powershell "$CONDA_PATH; cd iverilog; git reset --hard origin/master; git clean -fdx; cd .." + - powershell "$CONDA_PATH; cd iverilog; bash ./autoconf.sh; cd .." + - powershell "$CONDA_PATH; cd iverilog; bash ./configure --host=x86_64-w64-mingw32; cd .." + - powershell "$CONDA_PATH; cd iverilog; make; cd .." + - powershell "$CONDA_PATH; cd iverilog; make install; cd .." + script: + # TODO[gh-1372]: the default compiler on windows, msvc, is not supported + - powershell "$CONDA_PATH; tox -e py3_mingw" + - stage: SimTests os: osx osx_image: xcode11.2 @@ -146,3 +171,4 @@ jobs: - stage: SimTests python: 3.7 env: SIM=ghdl TOPLEVEL_LANG=vhdl + - env: ALLOW_FAIL=yes From 1ed0bfd1e6410e684f7abcc2ddd84728e8fc8cfa Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Wed, 10 Jun 2020 16:44:11 +0100 Subject: [PATCH 2407/2656] Use tox for building the documentation instead of a Makefile We already use tox for the tests, it makes it easier if we use it for the docs too. This also pins the virtualenv version to an appropriate one within tox. --- documentation/Makefile | 54 ----------------------------------------- documentation/README.md | 41 ++++++------------------------- tox.ini | 16 ++++++++++++ 3 files changed, 24 insertions(+), 87 deletions(-) delete mode 100644 documentation/Makefile diff --git a/documentation/Makefile b/documentation/Makefile deleted file mode 100644 index c4bdbd51..00000000 --- a/documentation/Makefile +++ /dev/null @@ -1,54 +0,0 @@ -# Build the cocotb documentation -# -# Note: this Makefile is *not* used by Read The Docs, it exec()'s the conf.py -# file directly. Hence, all special build steps here are only relevant for -# local builds, not for RTD builds. Add all build steps which should be executed -# in RTD builds as Python code into the conf.py file. -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = .venv/bin/sphinx-build -SOURCEDIR = source -BUILDDIR = build - -# Put it first so that "make" without argument is like "make help". -help: .venv - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - @echo "Other targets" - @echo " .venv Create virtual environment used to generate documentation" - @echo " rebuild_cocotb Rebuilds the cocotb package and installs in the virtual environment" - @echo " clean Deletes Sphinx-build artifacts" - @echo " distclean Deletes the virtual environment and Sphinx-build artifacts" - @echo " help Prints this help message" - -.venv: requirements.txt - @echo Creating Python venv for Sphinx build - python3 -m venv .venv - .venv/bin/pip -q install --upgrade pip - .venv/bin/pip -q install -r requirements.txt - .venv/bin/pip -q install -e .. - -.PHONY: rebuild_cocotb -rebuild_cocotb: - .venv/bin/pip -q install -e .. - -.PHONY: clean -clean: .venv Makefile - @$(SPHINXBUILD) -M clean "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: distclean -distclean: - -rm -rf .venv build sphinxext/__pycache__ - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -.DEFAULT: -# Dependencies for this special target are ignored by make, as discussed in -# http://stackoverflow.com/questions/26875072/dependencies-for-special-make-target-default-not-firing -# We hack around that by calling the target explicitly. - make .venv - - . .venv/bin/activate; $(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/documentation/README.md b/documentation/README.md index e42f4599..4656eb1f 100644 --- a/documentation/README.md +++ b/documentation/README.md @@ -1,38 +1,13 @@ Cocotb documentation ==================== -This directory contains the documentation of cocotb. +This directory contains the documentation of cocotb, which is built with Sphinx. +These docs are automatically built and uploaded with every commit. The built +version corresponding to the `master` branch can be found +[here](https://docs.cocotb.org/en/latest/) -Build the HTML documentation to view it in a browser: +`tox -e docs` cana be used to create an appropriate virtual environment and +invoke `sphinx-build` to generate the HTML docs. -```sh -# build the HTML documentation -make html - -# view it in your browser -xdg-open build/html/index.html -``` - -It is also possible to build the documentation as PDF file. -Building the PDFs requires a texlive installation, the example below assumes Ubuntu or Debian distributions -Replace the commands for your distribution as necessary. - -```sh -# install build dependencies -apt-get install texlive - -# build the PDF documentation -make latexpdf - -# open the file in your PDF viewer -xdg-open build/latex/cocotb.pdf -``` - -To clean the build tree run - -```sh -make clean - -# or to clean even more build output -make distclean -``` +In addition to the python dependencies managed by `tox`, `doxygen` must be +installed. diff --git a/tox.ini b/tox.ini index bcb3ea4f..65b390c5 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,11 @@ [tox] envlist = py3 +# for the requires key +minversion = 3.2.0 +# virtualenv is used by tox; versions below 16.1.0 cause a DeprecationWarning +# to be shown for all code which uses virtualenv, which is the case for all code +# we run through tox. (https://github.com/pypa/virtualenv/pull/1064) +requires = virtualenv >= 16.1 [testenv] setenv = @@ -34,3 +40,13 @@ usedevelop=True [testenv:py3_mingw] install_command = python -m pip install --global-option build_ext --global-option --compiler=mingw32 {opts} {packages} + +# Note: this target is *not* used by Read The Docs, it runs sphinx-build +# directly. Hence, all special build steps here are only relevant for +# local builds, not for RTD builds. Add all build steps which should be executed +# in RTD builds as Python code into the conf.py file. +[testenv:docs] +description = invoke sphinx-build to build the HTML docs +deps = -rdocumentation/requirements.txt +commands = sphinx-build -d "{toxworkdir}/docs_doctree" ./documentation/source "{toxworkdir}/docs_out" --color -bhtml {posargs} + python -c 'import pathlib; print("documentation available under file://\{0\}".format(pathlib.Path(r"{toxworkdir}") / "docs_out" / "index.html"))' From e66dd9ee0eb6ee42f3aa8beba266789e248ae24c Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Fri, 12 Jun 2020 16:11:11 +0100 Subject: [PATCH 2408/2656] Update documentation/README.md Co-authored-by: Kaleb Barrett --- documentation/README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/documentation/README.md b/documentation/README.md index 4656eb1f..2eedbb97 100644 --- a/documentation/README.md +++ b/documentation/README.md @@ -1,10 +1,9 @@ Cocotb documentation ==================== -This directory contains the documentation of cocotb, which is built with Sphinx. -These docs are automatically built and uploaded with every commit. The built -version corresponding to the `master` branch can be found -[here](https://docs.cocotb.org/en/latest/) +This directory contains the documentation of cocotb, which is built with Doxygen and Sphinx. +The documentation is automatically built and uploaded with every pull request. +The documentation for the `master` branch can be found [here](https://docs.cocotb.org/en/latest/). `tox -e docs` cana be used to create an appropriate virtual environment and invoke `sphinx-build` to generate the HTML docs. From d25bd2ccb6dcaa2136bace81a8276f6df840f876 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Fri, 12 Jun 2020 23:05:00 +0100 Subject: [PATCH 2409/2656] Update documentation/README.md --- documentation/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/README.md b/documentation/README.md index 2eedbb97..b2cb6b3b 100644 --- a/documentation/README.md +++ b/documentation/README.md @@ -5,7 +5,7 @@ This directory contains the documentation of cocotb, which is built with Doxygen The documentation is automatically built and uploaded with every pull request. The documentation for the `master` branch can be found [here](https://docs.cocotb.org/en/latest/). -`tox -e docs` cana be used to create an appropriate virtual environment and +`tox -e docs` can be used to create an appropriate virtual environment and invoke `sphinx-build` to generate the HTML docs. In addition to the python dependencies managed by `tox`, `doxygen` must be From 1904d450a22181176284fb29ce97fbc7f5ed167e Mon Sep 17 00:00:00 2001 From: Tim Pambor Date: Fri, 10 Jul 2020 10:47:58 +0200 Subject: [PATCH 2410/2656] Fix CI for icarus devel on Windows --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index ff95960e..ead3f0b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -117,6 +117,10 @@ jobs: - export CONDA_PATH="\$env:Path = ';C:\miniconda3;C:\miniconda3\Library\mingw-w64\bin;C:\miniconda3\Library\usr\bin;C:\miniconda3\Library\bin;C:\miniconda3\Scripts;C:\miniconda3\bin;C:\miniconda3\condabin;C:\miniconda3\Library\usr\local\bin;' + \$env:Path" - powershell "$CONDA_PATH; conda install --yes -c msys2 m2-base m2-make m2-autoconf m2-flex m2-bison m2-gperf m2w64-toolchain libpython" - powershell "$CONDA_PATH; conda install --yes virtualenv" + - powershell "$CONDA_PATH; conda update --yes pip" + - powershell "$CONDA_PATH; conda info; conda list --show-channel-urls" + # needed to make the builtin sqlite3 module (used by coverage) work - see gh-1909 + - powershell 'cp C:\miniconda3\Library\bin\sqlite3.dll C:\miniconda3\DLLs\sqlite3.dll' - powershell "$CONDA_PATH; pip install tox" - powershell "$CONDA_PATH; if (!(Test-Path iverilog/README.txt)) { Get-ChildItem -Path iverilog -Recurse | Remove-Item -Force -Recurse -ErrorAction SilentlyContinue; Remove-Item iverilog -Force -Recurse -ErrorAction SilentlyContinue; git clone --depth=1 https://github.com/steveicarus/iverilog.git } else { cd iverilog; git fetch origin; cd ..}" From 133ef5678b541a965aa2ebbede7b76ac09a97743 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 4 Jun 2020 15:04:54 +0100 Subject: [PATCH 2411/2656] Use std::initializer_list in place of GpiIteratorMapping The helper functions this type provided are subsumed by C++11 syntax in the standard library. --- cocotb/share/lib/fli/FliImpl.cpp | 99 +++++++------- cocotb/share/lib/fli/FliImpl.h | 3 +- cocotb/share/lib/gpi/gpi_priv.h | 34 ----- cocotb/share/lib/vhpi/VhpiCbHdl.cpp | 137 +++++++++---------- cocotb/share/lib/vhpi/VhpiImpl.h | 2 +- cocotb/share/lib/vpi/VpiCbHdl.cpp | 197 +++++++++++++--------------- cocotb/share/lib/vpi/VpiImpl.h | 2 +- 7 files changed, 204 insertions(+), 270 deletions(-) diff --git a/cocotb/share/lib/fli/FliImpl.cpp b/cocotb/share/lib/fli/FliImpl.cpp index aafd7368..41ebaf5e 100644 --- a/cocotb/share/lib/fli/FliImpl.cpp +++ b/cocotb/share/lib/fli/FliImpl.cpp @@ -576,63 +576,60 @@ GpiIterator *FliImpl::iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type return new_iter; } -void fli_mappings(GpiIteratorMapping &map) -{ - FliIterator::OneToMany region_options[] = { - FliIterator::OTM_CONSTANTS, - FliIterator::OTM_SIGNALS, - FliIterator::OTM_REGIONS, - FliIterator::OTM_END, - }; - map.add_to_options(accArchitecture, ®ion_options[0]); - map.add_to_options(accEntityVitalLevel0, ®ion_options[0]); - map.add_to_options(accArchVitalLevel0, ®ion_options[0]); - map.add_to_options(accArchVitalLevel1, ®ion_options[0]); - map.add_to_options(accBlock, ®ion_options[0]); - map.add_to_options(accCompInst, ®ion_options[0]); - map.add_to_options(accDirectInst, ®ion_options[0]); - map.add_to_options(accinlinedBlock, ®ion_options[0]); - map.add_to_options(accinlinedinnerBlock, ®ion_options[0]); - map.add_to_options(accGenerate, ®ion_options[0]); - map.add_to_options(accIfGenerate, ®ion_options[0]); +decltype(FliIterator::iterate_over) FliIterator::iterate_over = []{ + std::initializer_list region_options; + std::initializer_list signal_options; + std::initializer_list variable_options; + + return decltype(FliIterator::iterate_over) { + {accArchitecture, region_options = { + FliIterator::OTM_CONSTANTS, + FliIterator::OTM_SIGNALS, + FliIterator::OTM_REGIONS, + }}, + {accEntityVitalLevel0, region_options}, + {accArchVitalLevel0, region_options}, + {accArchVitalLevel1, region_options}, + {accBlock, region_options}, + {accCompInst, region_options}, + {accDirectInst, region_options}, + {accinlinedBlock, region_options}, + {accinlinedinnerBlock, region_options}, + {accGenerate, region_options}, + {accIfGenerate, region_options}, #ifdef accElsifGenerate - map.add_to_options(accElsifGenerate, ®ion_options[0]); + {accElsifGenerate, region_options}, #endif #ifdef accElseGenerate - map.add_to_options(accElseGenerate, ®ion_options[0]); + {accElseGenerate, region_options}, #endif #ifdef accCaseGenerate - map.add_to_options(accCaseGenerate, ®ion_options[0]); + {accCaseGenerate, region_options}, #endif #ifdef accCaseOTHERSGenerate - map.add_to_options(accCaseOTHERSGenerate, ®ion_options[0]); + {accCaseOTHERSGenerate, region_options}, #endif - map.add_to_options(accForGenerate, ®ion_options[0]); - map.add_to_options(accConfiguration, ®ion_options[0]); - - FliIterator::OneToMany signal_options[] = { - FliIterator::OTM_SIGNAL_SUB_ELEMENTS, - FliIterator::OTM_END, - }; - map.add_to_options(accSignal, &signal_options[0]); - map.add_to_options(accSignalBit, &signal_options[0]); - map.add_to_options(accSignalSubComposite, &signal_options[0]); - map.add_to_options(accAliasSignal, &signal_options[0]); - - FliIterator::OneToMany variable_options[] = { - FliIterator::OTM_VARIABLE_SUB_ELEMENTS, - FliIterator::OTM_END, + {accForGenerate, region_options}, + {accConfiguration, region_options}, + + {accSignal, signal_options = { + FliIterator::OTM_SIGNAL_SUB_ELEMENTS, + }}, + {accSignalBit, signal_options}, + {accSignalSubComposite, signal_options}, + {accAliasSignal, signal_options}, + + {accVariable, variable_options = { + FliIterator::OTM_VARIABLE_SUB_ELEMENTS, + }}, + {accGeneric, variable_options}, + {accGenericConstant, variable_options}, + {accAliasConstant, variable_options}, + {accAliasGeneric, variable_options}, + {accAliasVariable, variable_options}, + {accVHDLConstant, variable_options}, }; - map.add_to_options(accVariable, &variable_options[0]); - map.add_to_options(accGeneric, &variable_options[0]); - map.add_to_options(accGenericConstant, &variable_options[0]); - map.add_to_options(accAliasConstant, &variable_options[0]); - map.add_to_options(accAliasGeneric, &variable_options[0]); - map.add_to_options(accAliasVariable, &variable_options[0]); - map.add_to_options(accVHDLConstant, &variable_options[0]); -} - -GpiIteratorMapping FliIterator::iterate_over(fli_mappings); +}(); FliIterator::FliIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(impl, hdl), m_vars(), @@ -645,9 +642,13 @@ FliIterator::FliIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(i LOG_DEBUG("fli_iterator::Create iterator for %s of type %d:%s", m_parent->get_fullname().c_str(), type, acc_fetch_type_str(type)); - if (NULL == (selected = iterate_over.get_options(type))) { + try { + selected = &iterate_over.at(type); + } + catch (std::out_of_range const&) { LOG_WARN("FLI: Implementation does not know how to iterate over %s(%d)", acc_fetch_type_str(type), type); + selected = nullptr; return; } diff --git a/cocotb/share/lib/fli/FliImpl.h b/cocotb/share/lib/fli/FliImpl.h index de62b756..e7b213e0 100644 --- a/cocotb/share/lib/fli/FliImpl.h +++ b/cocotb/share/lib/fli/FliImpl.h @@ -418,7 +418,6 @@ class FliTimerCache { class FliIterator : public GpiIterator { public: enum OneToMany { - OTM_END = 0, OTM_CONSTANTS, // include Generics OTM_SIGNALS, OTM_REGIONS, @@ -434,7 +433,7 @@ class FliIterator : public GpiIterator { void populate_handle_list(OneToMany childType); private: - static GpiIteratorMapping iterate_over; /* Possible mappings */ + static std::map> iterate_over; /* Possible mappings */ std::vector *selected; /* Mapping currently in use */ std::vector::iterator one2many; diff --git a/cocotb/share/lib/gpi/gpi_priv.h b/cocotb/share/lib/gpi/gpi_priv.h index e41dcbca..da72d1b4 100644 --- a/cocotb/share/lib/gpi/gpi_priv.h +++ b/cocotb/share/lib/gpi/gpi_priv.h @@ -224,40 +224,6 @@ class GpiIterator : public GpiHdl { GpiObjHdl *m_parent; }; -template class GpiIteratorMapping { -public: - GpiIteratorMapping(void(*populate)(GpiIteratorMapping&)) { - populate(*this); - } -public: - std::vector* get_options(Ti type); - void add_to_options(Ti type, Tm *options); -private: - std::map > options_map; -}; - -template void GpiIteratorMapping::add_to_options(Ti type, Tm *options) -{ - std::vector option_vec; - Tm *ptr = options; - while (*ptr) { - option_vec.push_back(*ptr); - ptr++; - } - options_map[type] = option_vec; -} - -template std::vector * GpiIteratorMapping::get_options(Ti type) -{ - typename std::map >::iterator valid = options_map.find(type); - - if (options_map.end() == valid) { - return NULL; - } else { - return &valid->second; - } -} - class GpiImplInterface { public: diff --git a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp index c13ed693..ad0bdd98 100644 --- a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp +++ b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp @@ -919,81 +919,64 @@ VhpiNextPhaseCbHdl::VhpiNextPhaseCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), cb_data.time = &vhpi_time; } -void vhpi_mappings(GpiIteratorMapping &map) -{ - /* vhpiRootInstK */ - vhpiOneToManyT root_options[] = { - vhpiInternalRegions, - vhpiSigDecls, - vhpiVarDecls, - vhpiPortDecls, - vhpiGenericDecls, - vhpiConstDecls, - // vhpiIndexedNames, - vhpiCompInstStmts, - vhpiBlockStmts, - (vhpiOneToManyT)0, - }; - map.add_to_options(vhpiRootInstK, &root_options[0]); - - /* vhpiSigDeclK */ - vhpiOneToManyT sig_options[] = { - vhpiIndexedNames, - vhpiSelectedNames, - (vhpiOneToManyT)0, - }; - map.add_to_options(vhpiGenericDeclK, &sig_options[0]); - map.add_to_options(vhpiSigDeclK, &sig_options[0]); - - /* vhpiIndexedNameK */ - map.add_to_options(vhpiSelectedNameK, &sig_options[0]); - map.add_to_options(vhpiIndexedNameK, &sig_options[0]); - - /* vhpiCompInstStmtK */ - map.add_to_options(vhpiCompInstStmtK, &root_options[0]); - - /* vhpiSimpleSigAssignStmtK */ - vhpiOneToManyT simplesig_options[] = { - vhpiDecls, - vhpiInternalRegions, - vhpiSensitivitys, - vhpiStmts, - (vhpiOneToManyT)0, - }; - map.add_to_options(vhpiCondSigAssignStmtK, &simplesig_options[0]); - map.add_to_options(vhpiSimpleSigAssignStmtK, &simplesig_options[0]); - map.add_to_options(vhpiSelectSigAssignStmtK, &simplesig_options[0]); - - /* vhpiPortDeclK */ - map.add_to_options(vhpiPortDeclK, &sig_options[0]); - - /* vhpiForGenerateK */ - vhpiOneToManyT gen_options[] = { - vhpiDecls, - vhpiInternalRegions, - vhpiSigDecls, - vhpiVarDecls, - vhpiConstDecls, - vhpiCompInstStmts, - vhpiBlockStmts, - (vhpiOneToManyT)0, +decltype(VhpiIterator::iterate_over) VhpiIterator::iterate_over = []{ + /* for reused lists */ + std::initializer_list root_options; + std::initializer_list sig_options; + std::initializer_list simplesig_options; + std::initializer_list gen_options; + + return decltype(VhpiIterator::iterate_over) { + {vhpiRootInstK, root_options = { + vhpiInternalRegions, + vhpiSigDecls, + vhpiVarDecls, + vhpiPortDecls, + vhpiGenericDecls, + vhpiConstDecls, + // vhpiIndexedNames, + vhpiCompInstStmts, + vhpiBlockStmts, + }}, + {vhpiCompInstStmtK, root_options}, + + {vhpiGenericDeclK, sig_options = { + vhpiIndexedNames, + vhpiSelectedNames, + }}, + {vhpiSigDeclK, sig_options}, + {vhpiSelectedNameK, sig_options}, + {vhpiIndexedNameK, sig_options}, + {vhpiPortDeclK, sig_options}, + + {vhpiCondSigAssignStmtK, simplesig_options = { + vhpiDecls, + vhpiInternalRegions, + vhpiSensitivitys, + vhpiStmts, + }}, + {vhpiSimpleSigAssignStmtK, simplesig_options}, + {vhpiSelectSigAssignStmtK, simplesig_options}, + + {vhpiForGenerateK, gen_options = { + vhpiDecls, + vhpiInternalRegions, + vhpiSigDecls, + vhpiVarDecls, + vhpiConstDecls, + vhpiCompInstStmts, + vhpiBlockStmts, + }}, + {vhpiIfGenerateK, gen_options}, + {vhpiBlockStmtK, gen_options}, + + {vhpiConstDeclK, { + vhpiAttrSpecs, + vhpiIndexedNames, + vhpiSelectedNames, + }}, }; - map.add_to_options(vhpiForGenerateK, &gen_options[0]); - map.add_to_options(vhpiIfGenerateK, &gen_options[0]); - map.add_to_options(vhpiBlockStmtK, &gen_options[0]); - - /* vhpiConstDeclK */ - vhpiOneToManyT const_options[] = { - vhpiAttrSpecs, - vhpiIndexedNames, - vhpiSelectedNames, - (vhpiOneToManyT)0, - }; - map.add_to_options(vhpiConstDeclK, &const_options[0]); - -} - -GpiIteratorMapping VhpiIterator::iterate_over(vhpi_mappings); +}(); VhpiIterator::VhpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(impl, hdl), m_iterator(NULL), @@ -1003,9 +986,13 @@ VhpiIterator::VhpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator vhpiHandleT vhpi_hdl = m_parent->get_handle(); vhpiClassKindT type = (vhpiClassKindT)vhpi_get(vhpiKindP, vhpi_hdl); - if (NULL == (selected = iterate_over.get_options(type))) { + try { + selected = &iterate_over.at(type); + } + catch (std::out_of_range const&) { LOG_WARN("VHPI: Implementation does not know how to iterate over %s(%d)", vhpi_get_str(vhpiKindStrP, vhpi_hdl), type); + selected = nullptr; return; } diff --git a/cocotb/share/lib/vhpi/VhpiImpl.h b/cocotb/share/lib/vhpi/VhpiImpl.h index 490861da..fc1d5dc9 100644 --- a/cocotb/share/lib/vhpi/VhpiImpl.h +++ b/cocotb/share/lib/vhpi/VhpiImpl.h @@ -232,7 +232,7 @@ class VhpiIterator : public GpiIterator { private: vhpiHandleT m_iterator; vhpiHandleT m_iter_obj; - static GpiIteratorMapping iterate_over; /* Possible mappings */ + static std::map> iterate_over; /* Possible mappings */ std::vector *selected; /* Mapping currently in use */ std::vector::iterator one2many; }; diff --git a/cocotb/share/lib/vpi/VpiCbHdl.cpp b/cocotb/share/lib/vpi/VpiCbHdl.cpp index 4a7f918a..1377a161 100644 --- a/cocotb/share/lib/vpi/VpiCbHdl.cpp +++ b/cocotb/share/lib/vpi/VpiCbHdl.cpp @@ -559,117 +559,94 @@ VpiNextPhaseCbHdl::VpiNextPhaseCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), cb_data.reason = cbNextSimTime; } -void vpi_mappings(GpiIteratorMapping &map) -{ - /* vpiModule */ - int32_t module_options[] = { - //vpiModule, // Aldec SEGV on mixed language - //vpiModuleArray, // Aldec SEGV on mixed language - //vpiIODecl, // Don't care about these - vpiNet, - vpiNetArray, - vpiReg, - vpiRegArray, - vpiMemory, - vpiIntegerVar, - vpiRealVar, - vpiRealNet, - vpiStructVar, - vpiStructNet, - //vpiVariables // Aldec SEGV on plain Verilog - vpiNamedEvent, - vpiNamedEventArray, - vpiParameter, - //vpiSpecParam, // Don't care - //vpiParamAssign, // Aldec SEGV on mixed language - //vpiDefParam, // Don't care - vpiPrimitive, - vpiPrimitiveArray, - //vpiContAssign, // Don't care - vpiProcess, // Don't care - vpiModPath, - vpiTchk, - vpiAttribute, - vpiPort, - vpiInternalScope, - //vpiInterface, // Aldec SEGV on mixed language - //vpiInterfaceArray, // Aldec SEGV on mixed language - 0 - }; - map.add_to_options(vpiModule, &module_options[0]); - map.add_to_options(vpiGenScope, &module_options[0]); - - int32_t struct_options[] = { - vpiNet, +decltype(VpiIterator::iterate_over) VpiIterator::iterate_over = []{ + /* for reused lists */ + std::initializer_list module_options; + std::initializer_list struct_options; + + return decltype(VpiIterator::iterate_over) { + {vpiModule, module_options = { + //vpiModule, // Aldec SEGV on mixed language + //vpiModuleArray, // Aldec SEGV on mixed language + //vpiIODecl, // Don't care about these + vpiNet, + vpiNetArray, + vpiReg, + vpiRegArray, + vpiMemory, + vpiIntegerVar, + vpiRealVar, + vpiRealNet, + vpiStructVar, + vpiStructNet, + //vpiVariables // Aldec SEGV on plain Verilog + vpiNamedEvent, + vpiNamedEventArray, + vpiParameter, + //vpiSpecParam, // Don't care + //vpiParamAssign, // Aldec SEGV on mixed language + //vpiDefParam, // Don't care + vpiPrimitive, + vpiPrimitiveArray, + //vpiContAssign, // Don't care + vpiProcess, // Don't care + vpiModPath, + vpiTchk, + vpiAttribute, + vpiPort, + vpiInternalScope, + //vpiInterface, // Aldec SEGV on mixed language + //vpiInterfaceArray, // Aldec SEGV on mixed language + }}, + {vpiGenScope, module_options}, + + {vpiStructVar, struct_options = { + vpiNet, #ifndef IUS - vpiNetArray, + vpiNetArray, #endif - vpiReg, - vpiRegArray, - vpiMemory, - vpiParameter, - vpiPrimitive, - vpiPrimitiveArray, - vpiAttribute, - vpiMember, - 0 - }; - map.add_to_options(vpiStructVar, &struct_options[0]); - map.add_to_options(vpiStructNet, &struct_options[0]); - - /* vpiNet */ - int32_t net_options[] = { - //vpiContAssign, // Driver and load handled separately - //vpiPrimTerm, - //vpiPathTerm, - //vpiTchkTerm, - //vpiDriver, - //vpiLocalDriver, - //vpiLoad, - //vpiLocalLoad, - vpiNetBit, - 0 - }; - map.add_to_options(vpiNet, &net_options[0]); - - /* vpiNetArray */ - int32_t netarray_options[] = { - vpiNet, - 0 - }; - map.add_to_options(vpiNetArray, &netarray_options[0]); - - /* vpiRegArray */ - int32_t regarray_options[] = { - vpiReg, - 0 + vpiReg, + vpiRegArray, + vpiMemory, + vpiParameter, + vpiPrimitive, + vpiPrimitiveArray, + vpiAttribute, + vpiMember, + }}, + {vpiStructNet, struct_options}, + + {vpiNet, { + //vpiContAssign, // Driver and load handled separately + //vpiPrimTerm, + //vpiPathTerm, + //vpiTchkTerm, + //vpiDriver, + //vpiLocalDriver, + //vpiLoad, + //vpiLocalLoad, + vpiNetBit, + }}, + {vpiNetArray, { + vpiNet, + }}, + {vpiRegArray, { + vpiReg, + }}, + {vpiMemory, { + vpiMemoryWord, + }}, + {vpiPort, { + vpiPortBit, + }}, + {vpiGate, { + vpiPrimTerm, + vpiTableEntry, + vpiUdpDefn, + }}, }; - map.add_to_options(vpiRegArray, ®array_options[0]); +}(); - /* vpiMemory */ - int32_t memory_options[] = { - vpiMemoryWord, - 0 - }; - map.add_to_options(vpiMemory, &memory_options[0]); - - /* vpiPort */ - int32_t port_options[] = { - vpiPortBit, - 0 - }; - map.add_to_options(vpiPort, &port_options[0]); - - int32_t gate_options[] = { - vpiPrimTerm, - vpiTableEntry, - vpiUdpDefn, - 0 - }; - map.add_to_options(vpiGate, &gate_options[0]); -} - -GpiIteratorMapping VpiIterator::iterate_over(vpi_mappings); VpiIterator::VpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(impl, hdl), m_iterator(NULL) @@ -678,9 +655,13 @@ VpiIterator::VpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(i vpiHandle vpi_hdl = m_parent->get_handle(); int type = vpi_get(vpiType, vpi_hdl); - if (NULL == (selected = iterate_over.get_options(type))) { + try { + selected = &iterate_over.at(type); + } + catch (std::out_of_range const&) { LOG_WARN("VPI: Implementation does not know how to iterate over %s(%d)", vpi_get_str(vpiType, vpi_hdl), type); + selected = nullptr; return; } diff --git a/cocotb/share/lib/vpi/VpiImpl.h b/cocotb/share/lib/vpi/VpiImpl.h index 6fdeffb2..887b966e 100644 --- a/cocotb/share/lib/vpi/VpiImpl.h +++ b/cocotb/share/lib/vpi/VpiImpl.h @@ -200,7 +200,7 @@ class VpiIterator : public GpiIterator { private: vpiHandle m_iterator; - static GpiIteratorMapping iterate_over; /* Possible mappings */ + static std::map> iterate_over; /* Possible mappings */ std::vector *selected; /* Mapping currently in use */ std::vector::iterator one2many; }; From 97407ccb6ba3a5455419ad5e79511b0e593a3255 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 10 Jul 2020 18:55:26 +0200 Subject: [PATCH 2412/2656] Add Glossary section Closes #1966. --- documentation/source/glossary.rst | 64 +++++++++++++++++++++++++++++++ documentation/source/index.rst | 4 +- 2 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 documentation/source/glossary.rst diff --git a/documentation/source/glossary.rst b/documentation/source/glossary.rst new file mode 100644 index 00000000..68dee7db --- /dev/null +++ b/documentation/source/glossary.rst @@ -0,0 +1,64 @@ +.. _glossary: + +Glossary +======== + +.. glossary:: + + coroutine function + The definition of a function that, when called, returns a coroutine object. + Implemented using :keyword:`async` functions. + See also the :term:`Python glossary `. + coroutine + The result of calling a :term:`coroutine function`. + Coroutines are not run immediately, you must either + :keyword:`await` on them which blocks the awaiting coroutine until it is finished; + or fork the coroutine, turning it into a :term:`task`, which runs concurrently. + See also the :term:`Python glossary `. + + DUT + Design under Test + + DUV + Design under Verification + + FLI + Foreign Language Interface. Mentor Graphics' equivalent to :term:`VHPI` + + GPI + General Procedural Interface, cocotb's abstraction over :term:`VPI`, :term:`VHPI`, and :term:`FLI`. + + HDL + Hardware Description Language + + MDV + Metric-driven Verification + + RTL + Register Transfer Level + + task + The result of forking a :term:`coroutine`. A task represents a concurrently running coroutine. + + UVM + Universal Verification Methodology + + VHPI + The VHDL Procedural Interface, an application-programming interface to VHDL tools. + + VIP + Verification IP + + VPI + The Verilog Procedural Interface, an application-programming interface to (System)Verilog tools. + Its original name was "PLI 2.0". + +.. + Driver + TBD + + Monitor + TBD + + Scoreboard + TBD diff --git a/documentation/source/index.rst b/documentation/source/index.rst index 3a2fcd1f..2112e0f7 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -179,9 +179,6 @@ A test can spawn multiple coroutines, allowing for independent flows of executio simulator_support extensions -.. todo:: - - *Maybe* add a glossary (Coroutine, Driver, Monitor, Scoreboard, HDL, RTL, GPI, V(H)PI, FLI, VIP, UVM, MDV, DUT/DUV) - .. toctree:: :maxdepth: 1 :caption: Development & Community @@ -206,3 +203,4 @@ A test can spawn multiple coroutines, allowing for independent flows of executio Classes, Methods, Variables etc. Python Modules + glossary From d4b915adde21d76c3b9ce3087e2a24d39f2d64dd Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 10 Jul 2020 23:48:26 +0200 Subject: [PATCH 2413/2656] Add link to 'good first issue' label --- documentation/source/roadmap.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/documentation/source/roadmap.rst b/documentation/source/roadmap.rst index bfabcdac..f9fde946 100644 --- a/documentation/source/roadmap.rst +++ b/documentation/source/roadmap.rst @@ -5,6 +5,7 @@ Roadmap cocotb is in active development. We use GitHub issues to track our pending tasks. -Take a look at the `open Feature List `_ to see the work that's lined up. +Take a look at the `open feature list `_ to see the work that's lined up, +or the `list of good first issues `_ to get your feet wet. If you have a GitHub account you can also `raise an enhancement request `_ to suggest new features. From 597327e8742bb2dd7fb7d67d410ab24e6469338f Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sun, 12 Jul 2020 22:09:42 +0200 Subject: [PATCH 2414/2656] Add link to issues for the respective simulator --- documentation/source/simulator_support.rst | 79 ++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index ccd5f979..ab9ed64f 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -60,6 +60,13 @@ to the top component as shown in the example below: `endif endmodule +.. _sim-icarus-issues: + +Issues for this simulator +------------------------- + +* `All issues with label category:simulators:icarus `_ + .. _sim-verilator: @@ -114,6 +121,14 @@ To get waveforms in VCD format, add Verilator's trace option(s) to the To set the same options on the command line, use ``EXTRA_ARGS="--trace --trace-structs" make ...``. A VCD file named ``dump.vcd`` will be generated in the current directory. +.. _sim-verilator-issues: + +Issues for this simulator +------------------------- + +* `All issues with label category:simulators:verilator `_ + + .. _sim-vcs: Synopsys VCS @@ -127,6 +142,13 @@ In order to use this simulator, set :make:var:`SIM` to ``vcs``: cocotb currently only supports VPI for Synopsys VCS, not VHPI. +.. _sim-vcs-issues: + +Issues for this simulator +------------------------- + +* `All issues with label category:simulators:vcs `_ + .. _sim-aldec: .. _sim-riviera: @@ -152,6 +174,13 @@ In order to use this simulator, set :make:var:`SIM` to ``riviera``: The :envvar:`LICENSE_QUEUE` environment variable can be used for this simulator – this setting will be mirrored in the TCL ``license_queue`` variable to control runtime license checkouts. +.. _sim-aldec-issues: + +Issues for this simulator +------------------------- + +* `All issues with label category:simulators:riviera `_ + .. _sim-activehdl: @@ -173,6 +202,14 @@ In order to use this simulator, set :make:var:`SIM` to ``activehdl``: - Aldec Active-HDL 10.4a - Aldec Active-HDL 10.5a +.. _sim-activehdl-issues: + +Issues for this simulator +------------------------- + +* `All issues with label category:simulators:activehdl `_ + + .. _sim-questa: Mentor Questa @@ -186,6 +223,13 @@ In order to use this simulator, set :make:var:`SIM` to ``questa``: For more information, see :ref:`sim-modelsim`. +.. _sim-questa-issues: + +Issues for this simulator +------------------------- + +* `All issues with label category:simulators:questa `_ + .. _sim-modelsim: @@ -212,6 +256,13 @@ If you try to use them with FLI, you will see a ``vsim-FLI-3155`` error: ModelSim DE and SE (and Questa, of course) support the FLI. +.. _sim-modelsim-issues: + +Issues for this simulator +------------------------- + +* `All issues with label category:simulators:modelsim `_ + .. _sim-incisive: @@ -226,6 +277,13 @@ In order to use this simulator, set :make:var:`SIM` to ``ius``: For more information, see :ref:`sim-xcelium`. +.. _sim-incisive-issues: + +Issues for this simulator +------------------------- + +* `All issues with label category:simulators:ius `_ + .. _sim-xcelium: @@ -240,6 +298,13 @@ In order to use this simulator, set :make:var:`SIM` to ``xcelium``: The simulator automatically loads VPI even when only VHPI is requested. +.. _sim-xcelium-issues: + +Issues for this simulator +------------------------- + +* `All issues with label category:simulators:xcelium `_ + .. _sim-ghdl: @@ -255,6 +320,13 @@ In order to use this simulator, set :make:var:`SIM` to ``ghdl``: Support is preliminary. Noteworthy is that despite GHDL being a VHDL simulator, it implements the VPI interface. +.. _sim-ghdl-issues: + +Issues for this simulator +------------------------- + +* `All issues with label category:simulators:ghdl `_ + .. _sim-cvc: @@ -269,3 +341,10 @@ set :make:var:`SIM` to ``cvc``: make SIM=cvc Note that cocotb's makefile is using CVC's interpreted mode. + +.. _sim-cvc-issues: + +Issues for this simulator +------------------------- + +* `All issues with label category:simulators:cvc `_ From c24e64997c2a65901c18f2155b27332c29bfe423 Mon Sep 17 00:00:00 2001 From: jwrr Date: Fri, 10 Jul 2020 19:39:29 -0400 Subject: [PATCH 2415/2656] Add Verilator and GHDL to example simulators in README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4c9bfaa8..f61dde58 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,10 @@ Cocotb requires: - Python 3.5+ - A C++11 compiler -- An HDL simulator (such as [Icarus Verilog](http://iverilog.icarus.com/)) +- An HDL simulator (such as [Icarus Verilog](http://iverilog.icarus.com/), +[Verilator](https://www.veripool.org/wiki/verilator), +[GHDL](http://ghdl.free.fr/) or a +[Commercial Simulators](https://docs.cocotb.org/en/stable/simulator_support.html)) After installing these dependencies, the latest stable version of cocotb can be installed with pip. From e12041cd9f40b16e82ac83bf59278b17f159e3fe Mon Sep 17 00:00:00 2001 From: jwrr Date: Fri, 10 Jul 2020 19:52:05 -0400 Subject: [PATCH 2416/2656] Add Verilator and GHDL as example simulators in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f61dde58..87697403 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Cocotb requires: - An HDL simulator (such as [Icarus Verilog](http://iverilog.icarus.com/), [Verilator](https://www.veripool.org/wiki/verilator), [GHDL](http://ghdl.free.fr/) or a -[Commercial Simulators](https://docs.cocotb.org/en/stable/simulator_support.html)) +[commercial simulator](https://docs.cocotb.org/en/stable/simulator_support.html)) After installing these dependencies, the latest stable version of cocotb can be installed with pip. From 117e858e53aa87e130306e7beb3d24f5f0c45f10 Mon Sep 17 00:00:00 2001 From: jwrr Date: Sat, 11 Jul 2020 08:12:49 -0400 Subject: [PATCH 2417/2656] Change 'commercial' to 'other' in README.md The linked page describes the limitations of all simulators, not just commercial simulators. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 87697403..3fe42be9 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,8 @@ Cocotb requires: - A C++11 compiler - An HDL simulator (such as [Icarus Verilog](http://iverilog.icarus.com/), [Verilator](https://www.veripool.org/wiki/verilator), -[GHDL](http://ghdl.free.fr/) or a -[commercial simulator](https://docs.cocotb.org/en/stable/simulator_support.html)) +[GHDL](http://ghdl.free.fr/) or +[other simulator](https://docs.cocotb.org/en/stable/simulator_support.html)) After installing these dependencies, the latest stable version of cocotb can be installed with pip. From cf1a95507be3e4a03a794f7cc9ac2afbad02e3ae Mon Sep 17 00:00:00 2001 From: jwrr Date: Sun, 12 Jul 2020 00:02:28 -0400 Subject: [PATCH 2418/2656] Add GHDL waveform sub-section to simulato_support doc --- documentation/source/simulator_support.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index ab9ed64f..4d20189e 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -328,6 +328,27 @@ Issues for this simulator * `All issues with label category:simulators:ghdl `_ +.. _sim-ghdl-waveforms: + +Waveforms +--------- + +To get waveforms in VCD format, set the :make:var:`SIM_ARGS` option to ``--vcd=anyname.vcd``, +for example in a Makefile: + + .. code-block:: make + + SIM_ARGS=--vcd=anyname.vcd + +The option can be set on the command line, as shown in the following example. + + .. code-block:: bash + + make SIM=ghdl SIM_ARGS=--vcd=anyname.vcd + +A VCD file named ``anyname.vcd`` will be generated in the current directory. + + .. _sim-cvc: Tachyon DA CVC From a42b24857078f00d0fcdc98847c34dfed1b1fea5 Mon Sep 17 00:00:00 2001 From: jwrr Date: Sun, 12 Jul 2020 18:43:05 -0400 Subject: [PATCH 2419/2656] Undo the unrelated changes This change leaves just the README.md update that adds Verilator and GHDL, along with iVerilog. The unrelated changes to simulator_support have been moved to a different PR. Closes #1271 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3fe42be9..4bc2ebb1 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Cocotb requires: - A C++11 compiler - An HDL simulator (such as [Icarus Verilog](http://iverilog.icarus.com/), [Verilator](https://www.veripool.org/wiki/verilator), -[GHDL](http://ghdl.free.fr/) or +[GHDL](http://ghdl.free.fr/) or [other simulator](https://docs.cocotb.org/en/stable/simulator_support.html)) After installing these dependencies, the latest stable version of cocotb can be installed with pip. From b4682e0139d49d7961e68435c12a12f2d0d20854 Mon Sep 17 00:00:00 2001 From: jwrr Date: Sun, 12 Jul 2020 18:57:22 -0400 Subject: [PATCH 2420/2656] Undo the unrelated changes This change cleans up a previous commit and leaves just the README.md update that adds Verilator and GHDL, along with iVerilog. The unrelated changes to simulator_support have been moved to a different PR. Closes #1271 --- documentation/source/simulator_support.rst | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 4d20189e..ab9ed64f 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -328,27 +328,6 @@ Issues for this simulator * `All issues with label category:simulators:ghdl `_ -.. _sim-ghdl-waveforms: - -Waveforms ---------- - -To get waveforms in VCD format, set the :make:var:`SIM_ARGS` option to ``--vcd=anyname.vcd``, -for example in a Makefile: - - .. code-block:: make - - SIM_ARGS=--vcd=anyname.vcd - -The option can be set on the command line, as shown in the following example. - - .. code-block:: bash - - make SIM=ghdl SIM_ARGS=--vcd=anyname.vcd - -A VCD file named ``anyname.vcd`` will be generated in the current directory. - - .. _sim-cvc: Tachyon DA CVC From d553a1c8da04ef57cd6cf9216ef124483247d86f Mon Sep 17 00:00:00 2001 From: jwrr Date: Sun, 12 Jul 2020 22:06:03 -0400 Subject: [PATCH 2421/2656] Update GHDL and Verilator links in README.md Link to cocotb's simulator support page. Remove extra spaces at end of line. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4bc2ebb1..ffd714f2 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,8 @@ Cocotb requires: - Python 3.5+ - A C++11 compiler - An HDL simulator (such as [Icarus Verilog](http://iverilog.icarus.com/), -[Verilator](https://www.veripool.org/wiki/verilator), -[GHDL](http://ghdl.free.fr/) or +[Verilator](https://docs.cocotb.org/en/stable/simulator_support.html#verilator), +[GHDL](https://docs.cocotb.org/en/stable/simulator_support.html#ghdl) or [other simulator](https://docs.cocotb.org/en/stable/simulator_support.html)) After installing these dependencies, the latest stable version of cocotb can be installed with pip. From 09d24af2c74a5ac115b09d4b8b0e49ba16458f2c Mon Sep 17 00:00:00 2001 From: jwrr Date: Sun, 12 Jul 2020 22:15:15 -0400 Subject: [PATCH 2422/2656] Update Icarus Verilog link to cocotb's simulator support page --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ffd714f2..8ffaa7a0 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Cocotb requires: - Python 3.5+ - A C++11 compiler -- An HDL simulator (such as [Icarus Verilog](http://iverilog.icarus.com/), +- An HDL simulator (such as [Icarus Verilog](https://docs.cocotb.org/en/stable/simulator_support.html#icarus-verilog), [Verilator](https://docs.cocotb.org/en/stable/simulator_support.html#verilator), [GHDL](https://docs.cocotb.org/en/stable/simulator_support.html#ghdl) or [other simulator](https://docs.cocotb.org/en/stable/simulator_support.html)) From 9495edb153bdb44657dabd4b68557ec1dc4f28af Mon Sep 17 00:00:00 2001 From: jwrr Date: Sun, 12 Jul 2020 18:27:22 -0400 Subject: [PATCH 2423/2656] Add GHDL waveform documentation Describe how to generate vcd waves for cocotb+ghdl using SIM_ARGS option. Closes #1977 --- documentation/source/simulator_support.rst | 29 ++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index ab9ed64f..0b5f8452 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -328,6 +328,35 @@ Issues for this simulator * `All issues with label category:simulators:ghdl `_ +.. _sim-ghdl-waveforms: + +Waveforms +--------- + +To get waveforms in VCD format, set the :make:var:`SIM_ARGS` option to ``--vcd=anyname.vcd``, +for example in a Makefile: + + .. code-block:: make + + SIM_ARGS+=--vcd=anyname.vcd + +The option can be set on the command line, as shown in the following example. + + .. code-block:: bash + + make SIM=ghdl SIM_ARGS=--vcd=anyname.vcd + + # or define the SIM_ARGS environment variable in bash + + export SIM_ARGS=--vcd=anyname.vhd; make SIM=ghdl + + # or in csh + + setenv SIM_ARGS=--vcd=anyname.vhd; make SIM=ghdl + +A VCD file named ``anyname.vcd`` will be generated in the current directory. + + .. _sim-cvc: Tachyon DA CVC From 799e853338dfa6931b377fe36519ff593c611668 Mon Sep 17 00:00:00 2001 From: "jwrr.com" <35662439+jwrr@users.noreply.github.com> Date: Mon, 13 Jul 2020 23:46:36 -0400 Subject: [PATCH 2424/2656] Update documentation/source/simulator_support.rst Co-authored-by: Eric Wieser --- documentation/source/simulator_support.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 0b5f8452..31d79587 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -336,7 +336,7 @@ Waveforms To get waveforms in VCD format, set the :make:var:`SIM_ARGS` option to ``--vcd=anyname.vcd``, for example in a Makefile: - .. code-block:: make +.. code-block:: make SIM_ARGS+=--vcd=anyname.vcd From 1888926765aed44a5e6ac8cc1fca6bb3252fe933 Mon Sep 17 00:00:00 2001 From: "jwrr.com" <35662439+jwrr@users.noreply.github.com> Date: Mon, 13 Jul 2020 23:47:27 -0400 Subject: [PATCH 2425/2656] Update documentation/source/simulator_support.rst Co-authored-by: Eric Wieser --- documentation/source/simulator_support.rst | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 31d79587..5c299854 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -342,17 +342,9 @@ for example in a Makefile: The option can be set on the command line, as shown in the following example. - .. code-block:: bash - - make SIM=ghdl SIM_ARGS=--vcd=anyname.vcd - - # or define the SIM_ARGS environment variable in bash - - export SIM_ARGS=--vcd=anyname.vhd; make SIM=ghdl - - # or in csh +.. code-block:: bash - setenv SIM_ARGS=--vcd=anyname.vhd; make SIM=ghdl + SIM_ARGS=--vcd=anyname.vhd make SIM=ghdl A VCD file named ``anyname.vcd`` will be generated in the current directory. From 4001a86e1a52d297e541c6abeccaa439429d2378 Mon Sep 17 00:00:00 2001 From: jwrr Date: Tue, 14 Jul 2020 00:20:21 -0400 Subject: [PATCH 2426/2656] Mention other GHDL waveform formats A link to GHDL's wavform documentation was added along with a brief description of the fst and ghw waveform formats. --- documentation/source/simulator_support.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 5c299854..610e20b8 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -348,6 +348,11 @@ The option can be set on the command line, as shown in the following example. A VCD file named ``anyname.vcd`` will be generated in the current directory. +The :make:var:`SIM_ARGS` option allows access to all +[GHDL Command Line Run (-r) options](https://ghdl.readthedocs.io/en/latest/using/Simulation.html#using-simulation). +This includes the generation of [other waveform formats](https://ghdl.readthedocs.io/en/latest/using/Simulation.html#export-waveforms) +such as the compressed fst format and the ghw format which supports any VHDL type. + .. _sim-cvc: From d6956f75b934bd1d5a774d573c7fdfebebcb9a0a Mon Sep 17 00:00:00 2001 From: jwrr Date: Tue, 14 Jul 2020 01:16:45 -0400 Subject: [PATCH 2427/2656] Change links to intersphinx The links were in Markdown. Correct case of FST and GHW wave formats. --- documentation/source/simulator_support.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 610e20b8..c74f8c62 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -348,10 +348,11 @@ The option can be set on the command line, as shown in the following example. A VCD file named ``anyname.vcd`` will be generated in the current directory. -The :make:var:`SIM_ARGS` option allows access to all -[GHDL Command Line Run (-r) options](https://ghdl.readthedocs.io/en/latest/using/Simulation.html#using-simulation). -This includes the generation of [other waveform formats](https://ghdl.readthedocs.io/en/latest/using/Simulation.html#export-waveforms) -such as the compressed fst format and the ghw format which supports any VHDL type. +The :make:var:`SIM_ARGS` option allows access to all GHDL Command Line Run (-r) +options listed in :ghdl:`USING:Simulation`. This includes other waveform +generation options including the compressed Fast Signal Trace (FST) format and +the GHDL Wave File (GHW) which supports any VHDL type. For more information +see :ghdl:`export_waves`. .. _sim-cvc: From 26c7ce2d970ca4703c57dafbbe267919a13389af Mon Sep 17 00:00:00 2001 From: jwrr Date: Tue, 14 Jul 2020 07:46:12 -0400 Subject: [PATCH 2428/2656] Update intersphinx_mapping to include ghdl Added ghdl to intersphinx_mapping in conf.py Changed link to GHDL's simulation options in simulator_support.rst --- documentation/source/conf.py | 5 ++++- documentation/source/simulator_support.rst | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/documentation/source/conf.py b/documentation/source/conf.py index aa67107d..403a85d0 100644 --- a/documentation/source/conf.py +++ b/documentation/source/conf.py @@ -46,7 +46,10 @@ 'sphinx_tabs.tabs', ] -intersphinx_mapping = {'python': ('https://docs.python.org/3', None)} +intersphinx_mapping = { + 'python': ('https://docs.python.org/3', None), + 'ghdl': ('https://ghdl.readthedocs.io/en/latest', None) +} # Github repo issues_github_path = "cocotb/cocotb" diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index c74f8c62..68d671b6 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -349,7 +349,7 @@ The option can be set on the command line, as shown in the following example. A VCD file named ``anyname.vcd`` will be generated in the current directory. The :make:var:`SIM_ARGS` option allows access to all GHDL Command Line Run (-r) -options listed in :ghdl:`USING:Simulation`. This includes other waveform +options listed in :ghdl:`simulation-options`. This includes other waveform generation options including the compressed Fast Signal Trace (FST) format and the GHDL Wave File (GHW) which supports any VHDL type. For more information see :ghdl:`export_waves`. From 7bb575469cd5fe186e1939aa05b56b54e9e0a470 Mon Sep 17 00:00:00 2001 From: jwrr Date: Tue, 14 Jul 2020 08:14:27 -0400 Subject: [PATCH 2429/2656] Correct ghdl intersphinx link format --- documentation/source/simulator_support.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 68d671b6..574d791a 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -349,10 +349,10 @@ The option can be set on the command line, as shown in the following example. A VCD file named ``anyname.vcd`` will be generated in the current directory. The :make:var:`SIM_ARGS` option allows access to all GHDL Command Line Run (-r) -options listed in :ghdl:`simulation-options`. This includes other waveform +options listed in :ref:`ghdl:simulation-options`. This includes other waveform generation options including the compressed Fast Signal Trace (FST) format and the GHDL Wave File (GHW) which supports any VHDL type. For more information -see :ghdl:`export_waves`. +see :ref:`ghdl:export_waves`. .. _sim-cvc: From f03273e1a19f80fe16dfe2d066d69ee66027a252 Mon Sep 17 00:00:00 2001 From: jwrr Date: Tue, 14 Jul 2020 08:47:40 -0400 Subject: [PATCH 2430/2656] Correct intersphinx for ghdl simulation options Changed from ghdl:simulation-options to ghdl:simulation_options --- documentation/source/simulator_support.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 574d791a..4e44d228 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -349,7 +349,7 @@ The option can be set on the command line, as shown in the following example. A VCD file named ``anyname.vcd`` will be generated in the current directory. The :make:var:`SIM_ARGS` option allows access to all GHDL Command Line Run (-r) -options listed in :ref:`ghdl:simulation-options`. This includes other waveform +options listed in :ref:`ghdl:simulation_options`. This includes other waveform generation options including the compressed Fast Signal Trace (FST) format and the GHDL Wave File (GHW) which supports any VHDL type. For more information see :ref:`ghdl:export_waves`. From 7e9fa86d5f2c04f66f1ea50ddf58a01563427469 Mon Sep 17 00:00:00 2001 From: "jwrr.com" <35662439+jwrr@users.noreply.github.com> Date: Tue, 14 Jul 2020 09:59:31 -0400 Subject: [PATCH 2431/2656] Update documentation/source/simulator_support.rst Co-authored-by: Kaleb Barrett --- documentation/source/simulator_support.rst | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 4e44d228..463fbcb0 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -348,11 +348,7 @@ The option can be set on the command line, as shown in the following example. A VCD file named ``anyname.vcd`` will be generated in the current directory. -The :make:var:`SIM_ARGS` option allows access to all GHDL Command Line Run (-r) -options listed in :ref:`ghdl:simulation_options`. This includes other waveform -generation options including the compressed Fast Signal Trace (FST) format and -the GHDL Wave File (GHW) which supports any VHDL type. For more information -see :ref:`ghdl:export_waves`. +:make:var:`SIM_ARGS` can also be used to pass command line arguments related to other waveform formats supported by GHDL like :ref:`Fast Signal Trace (FST) `, which is compressed; or :ref:`GHDL Wave File (GHW) `, which supports any VHDL type. .. _sim-cvc: From dfb9187a458eaca5ba823d327ce4af39ff97a2df Mon Sep 17 00:00:00 2001 From: "jwrr.com" <35662439+jwrr@users.noreply.github.com> Date: Tue, 14 Jul 2020 12:46:14 -0400 Subject: [PATCH 2432/2656] Update documentation/source/simulator_support.rst Streamline other GHDL wave formats Co-authored-by: Eric Wieser --- documentation/source/simulator_support.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 463fbcb0..b0458690 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -348,7 +348,7 @@ The option can be set on the command line, as shown in the following example. A VCD file named ``anyname.vcd`` will be generated in the current directory. -:make:var:`SIM_ARGS` can also be used to pass command line arguments related to other waveform formats supported by GHDL like :ref:`Fast Signal Trace (FST) `, which is compressed; or :ref:`GHDL Wave File (GHW) `, which supports any VHDL type. +:make:var:`SIM_ARGS` can also be used to pass command line arguments related to :ref:`other waveform formats supported by GHDL `. .. _sim-cvc: From 80253cc41d36b62d1c9be6c24dcc9d74b8b50340 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 19 Jul 2020 12:28:58 -0500 Subject: [PATCH 2433/2656] Merge simulatormodule.h into simulatormodule.cpp simulatormodule.h isn't a usable header file and the separation isn't necessary. --- .../share/lib/simulator/simulatormodule.cpp | 122 +++++++++++- cocotb/share/lib/simulator/simulatormodule.h | 173 ------------------ 2 files changed, 121 insertions(+), 174 deletions(-) delete mode 100644 cocotb/share/lib/simulator/simulatormodule.h diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index a8b7ecbf..f8eac0f6 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -39,10 +39,28 @@ static int releases = 0; static int sim_ending = 0; -#include "simulatormodule.h" #include // COCOTB_UNUSED #include +#include +#include "gpi_logging.h" +#include "gpi.h" +// This file defines the routines available to Python + +#define COCOTB_ACTIVE_ID 0xC0C07B // User data flag to indicate callback is active +#define COCOTB_INACTIVE_ID 0xDEADB175 // User data flag set when callback has been de-registered + +#define MODULE_NAME "simulator" + +// callback user data +struct callback_data { + PyThreadState *_saved_thread_state; // Thread state of the calling thread FIXME is this required? + uint32_t id_value; // COCOTB_ACTIVE_ID or COCOTB_INACTIVE_ID + PyObject *function; // Function to call when the callback fires + PyObject *args; // The arguments to call the function with + PyObject *kwargs; // Keyword arguments to call the function with + gpi_sim_hdl cb_hdl; +}; /* define the extension types as templates */ namespace { @@ -951,6 +969,108 @@ static int add_module_types(PyObject* simulator) return 0; } + +/* NOTE: in the following docstrings we are specifying the parameters twice, but this is necessary. + * The first docstring before the long '--' line specifies the __text_signature__ that is used + * by the help() function. And the second after the '--' line contains type annotations used by + * the `autodoc_docstring_signature` setting of sphinx.ext.autodoc for generating documentation + * because type annotations are not supported in __text_signature__. + */ + +static PyMethodDef SimulatorMethods[] = { + {"log_msg", log_msg, METH_VARARGS, PyDoc_STR( + "log_msg(name, path, funcname, lineno, msg, /)\n" + "--\n\n" + "log_msg(name: str, path: str, funcname: str, lineno: int, msg: str) -> None\n" + "Log a message." + )}, + {"get_root_handle", get_root_handle, METH_VARARGS, PyDoc_STR( + "get_root_handle(name, /)\n" + "--\n\n" + "get_root_handle(name: str) -> cocotb.simulator.gpi_sim_hdl\n" + "Get the root handle." + )}, + {"register_timed_callback", register_timed_callback, METH_VARARGS, PyDoc_STR( + "register_timed_callback(time, func, /, *args)\n" + "--\n\n" + "register_timed_callback(time: int, func: Callable[..., None], *args: Any) -> cocotb.simulator.gpi_cb_hdl\n" + "Register a timed callback." + )}, + {"register_value_change_callback", register_value_change_callback, METH_VARARGS, PyDoc_STR( + "register_value_change_callback(signal, func, edge, /, *args)\n" + "--\n\n" + "register_value_change_callback(signal: cocotb.simulator.gpi_sim_hdl, func: Callable[..., None], edge: int, *args: Any) -> cocotb.simulator.gpi_cb_hdl\n" + "Register a signal change callback." + )}, + {"register_readonly_callback", register_readonly_callback, METH_VARARGS, PyDoc_STR( + "register_readonly_callback(func, /, *args)\n" + "--\n\n" + "register_readonly_callback(func: Callable[..., None], *args: Any) -> cocotb.simulator.gpi_cb_hdl\n" + "Register a callback for the read-only section." + )}, + {"register_nextstep_callback", register_nextstep_callback, METH_VARARGS, PyDoc_STR( + "register_nextstep_callback(func, /, *args)\n" + "--\n\n" + "register_nextstep_callback(func: Callable[..., None], *args: Any) -> cocotb.simulator.gpi_cb_hdl\n" + "Register a callback for the NextSimTime callback." + )}, + {"register_rwsynch_callback", register_rwsynch_callback, METH_VARARGS, PyDoc_STR( + "register_rwsynch_callback(func, /, *args)\n" + "--\n\n" + "register_rwsynch_callback(func: Callable[..., None], *args: Any) -> cocotb.simulator.gpi_cb_hdl\n" + "Register a callback for the read-write section." + )}, + {"stop_simulator", stop_simulator, METH_VARARGS, PyDoc_STR( + "stop_simulator()\n" + "--\n\n" + "stop_simulator() -> None\n" + "Instruct the attached simulator to stop. Users should not call this function." + )}, + {"log_level", log_level, METH_VARARGS, PyDoc_STR( + "log_level(level, /)\n" + "--\n\n" + "log_level(level: int) -> None\n" + "Set the log level for GPI." + )}, + {"is_running", is_running, METH_NOARGS, PyDoc_STR( + "is_running()\n" + "--\n\n" + "is_running() -> bool\n" + "Returns ``True`` if the caller is running within a simulator.\n" + "\n" + ".. versionadded:: 1.4" + )}, + {"get_sim_time", get_sim_time, METH_NOARGS, PyDoc_STR( + "get_sim_time()\n" + "--\n\n" + "get_sim_time() -> Tuple[int, int]\n" + "Get the current simulation time.\n" + "\n" + "Time is represented as a tuple of 32 bit integers ([low32, high32]) comprising a single 64 bit integer." + )}, + {"get_precision", get_precision, METH_NOARGS, PyDoc_STR( + "get_precision()\n" + "--\n\n" + "get_precision() -> int\n" + "Get the precision of the simulator in powers of 10.\n" + "\n" + "For example, if ``-12`` is returned, the simulator's time precision is 10**-12 or 1 ps." + )}, + {"get_simulator_product", get_simulator_product, METH_NOARGS, PyDoc_STR( + "get_simulator_product()\n" + "--\n\n" + "get_simulator_product() -> str\n" + "Get the simulator's product string." + )}, + {"get_simulator_version", get_simulator_version, METH_NOARGS, PyDoc_STR( + "get_simulator_version()\n" + "--\n\n" + "get_simulator_version() -> str\n" + "Get the simulator's product version string." + )}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, MODULE_NAME, diff --git a/cocotb/share/lib/simulator/simulatormodule.h b/cocotb/share/lib/simulator/simulatormodule.h deleted file mode 100644 index 8a7a1c49..00000000 --- a/cocotb/share/lib/simulator/simulatormodule.h +++ /dev/null @@ -1,173 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2013 Potential Ventures Ltd -* Copyright (c) 2013 SolarFlare Communications Inc -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd, -* SolarFlare Communications Inc nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#ifndef _SIMULATOR_MODULE_H -#define _SIMULATOR_MODULE_H - -#include -#include "gpi_logging.h" -#include "gpi.h" - -// This file defines the routines available to Python - -#define COCOTB_ACTIVE_ID 0xC0C07B // User data flag to indicate callback is active -#define COCOTB_INACTIVE_ID 0xDEADB175 // User data flag set when callback has been de-registered - -#define MODULE_NAME "simulator" - -// callback user data -struct callback_data { - PyThreadState *_saved_thread_state; // Thread state of the calling thread FIXME is this required? - uint32_t id_value; // COCOTB_ACTIVE_ID or COCOTB_INACTIVE_ID - PyObject *function; // Function to call when the callback fires - PyObject *args; // The arguments to call the function with - PyObject *kwargs; // Keyword arguments to call the function with - gpi_sim_hdl cb_hdl; -}; - -static PyObject *log_msg(PyObject *self, PyObject *args); - -static PyObject *register_timed_callback(PyObject *self, PyObject *args); -static PyObject *register_value_change_callback(PyObject *self, PyObject *args); -static PyObject *register_readonly_callback(PyObject *self, PyObject *args); -static PyObject *register_nextstep_callback(PyObject *self, PyObject *args); -static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args); - -static PyObject *get_root_handle(PyObject *self, PyObject *args); -static PyObject *stop_simulator(PyObject *self, PyObject *args); -static PyObject *is_running(PyObject *self, PyObject *args); -static PyObject *get_sim_time(PyObject *self, PyObject *args); -static PyObject *get_precision(PyObject *self, PyObject *args); -static PyObject *get_simulator_product(PyObject *self, PyObject *args); -static PyObject *get_simulator_version(PyObject *self, PyObject *args); - -static PyObject *log_level(PyObject *self, PyObject *args); - -/* NOTE: in the following docstrings we are specifying the parameters twice, but this is necessary. - * The first docstring before the long '--' line specifies the __text_signature__ that is used - * by the help() function. And the second after the '--' line contains type annotations used by - * the `autodoc_docstring_signature` setting of sphinx.ext.autodoc for generating documentation - * because type annotations are not supported in __text_signature__. - */ - -static PyMethodDef SimulatorMethods[] = { - {"log_msg", log_msg, METH_VARARGS, PyDoc_STR( - "log_msg(name, path, funcname, lineno, msg, /)\n" - "--\n\n" - "log_msg(name: str, path: str, funcname: str, lineno: int, msg: str) -> None\n" - "Log a message." - )}, - {"get_root_handle", get_root_handle, METH_VARARGS, PyDoc_STR( - "get_root_handle(name, /)\n" - "--\n\n" - "get_root_handle(name: str) -> cocotb.simulator.gpi_sim_hdl\n" - "Get the root handle." - )}, - {"register_timed_callback", register_timed_callback, METH_VARARGS, PyDoc_STR( - "register_timed_callback(time, func, /, *args)\n" - "--\n\n" - "register_timed_callback(time: int, func: Callable[..., None], *args: Any) -> cocotb.simulator.gpi_cb_hdl\n" - "Register a timed callback." - )}, - {"register_value_change_callback", register_value_change_callback, METH_VARARGS, PyDoc_STR( - "register_value_change_callback(signal, func, edge, /, *args)\n" - "--\n\n" - "register_value_change_callback(signal: cocotb.simulator.gpi_sim_hdl, func: Callable[..., None], edge: int, *args: Any) -> cocotb.simulator.gpi_cb_hdl\n" - "Register a signal change callback." - )}, - {"register_readonly_callback", register_readonly_callback, METH_VARARGS, PyDoc_STR( - "register_readonly_callback(func, /, *args)\n" - "--\n\n" - "register_readonly_callback(func: Callable[..., None], *args: Any) -> cocotb.simulator.gpi_cb_hdl\n" - "Register a callback for the read-only section." - )}, - {"register_nextstep_callback", register_nextstep_callback, METH_VARARGS, PyDoc_STR( - "register_nextstep_callback(func, /, *args)\n" - "--\n\n" - "register_nextstep_callback(func: Callable[..., None], *args: Any) -> cocotb.simulator.gpi_cb_hdl\n" - "Register a callback for the NextSimTime callback." - )}, - {"register_rwsynch_callback", register_rwsynch_callback, METH_VARARGS, PyDoc_STR( - "register_rwsynch_callback(func, /, *args)\n" - "--\n\n" - "register_rwsynch_callback(func: Callable[..., None], *args: Any) -> cocotb.simulator.gpi_cb_hdl\n" - "Register a callback for the read-write section." - )}, - {"stop_simulator", stop_simulator, METH_VARARGS, PyDoc_STR( - "stop_simulator()\n" - "--\n\n" - "stop_simulator() -> None\n" - "Instruct the attached simulator to stop. Users should not call this function." - )}, - {"log_level", log_level, METH_VARARGS, PyDoc_STR( - "log_level(level, /)\n" - "--\n\n" - "log_level(level: int) -> None\n" - "Set the log level for GPI." - )}, - {"is_running", is_running, METH_NOARGS, PyDoc_STR( - "is_running()\n" - "--\n\n" - "is_running() -> bool\n" - "Returns ``True`` if the caller is running within a simulator.\n" - "\n" - ".. versionadded:: 1.4" - )}, - {"get_sim_time", get_sim_time, METH_NOARGS, PyDoc_STR( - "get_sim_time()\n" - "--\n\n" - "get_sim_time() -> Tuple[int, int]\n" - "Get the current simulation time.\n" - "\n" - "Time is represented as a tuple of 32 bit integers ([low32, high32]) comprising a single 64 bit integer." - )}, - {"get_precision", get_precision, METH_NOARGS, PyDoc_STR( - "get_precision()\n" - "--\n\n" - "get_precision() -> int\n" - "Get the precision of the simulator in powers of 10.\n" - "\n" - "For example, if ``-12`` is returned, the simulator's time precision is 10**-12 or 1 ps." - )}, - {"get_simulator_product", get_simulator_product, METH_NOARGS, PyDoc_STR( - "get_simulator_product()\n" - "--\n\n" - "get_simulator_product() -> str\n" - "Get the simulator's product string." - )}, - {"get_simulator_version", get_simulator_version, METH_NOARGS, PyDoc_STR( - "get_simulator_version()\n" - "--\n\n" - "get_simulator_version() -> str\n" - "Get the simulator's product version string." - )}, - {NULL, NULL, 0, NULL} /* Sentinel */ -}; - -#endif From 67e39efef94b9d16a90247a8681094b18085c743 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 11 Jul 2020 20:09:57 -0500 Subject: [PATCH 2434/2656] Fix warning introduced by recent change GCC 9.3 considers the clever hack introduced in gh-1906 problematic and throws a warning. See gh-1975 for a treatment of the issue. --- cocotb/share/lib/fli/FliImpl.cpp | 28 ++++----- cocotb/share/lib/vhpi/VhpiCbHdl.cpp | 68 ++++++++++---------- cocotb/share/lib/vpi/VpiCbHdl.cpp | 98 ++++++++++++++--------------- 3 files changed, 97 insertions(+), 97 deletions(-) diff --git a/cocotb/share/lib/fli/FliImpl.cpp b/cocotb/share/lib/fli/FliImpl.cpp index 41ebaf5e..c89f6b51 100644 --- a/cocotb/share/lib/fli/FliImpl.cpp +++ b/cocotb/share/lib/fli/FliImpl.cpp @@ -577,16 +577,20 @@ GpiIterator *FliImpl::iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type } decltype(FliIterator::iterate_over) FliIterator::iterate_over = []{ - std::initializer_list region_options; - std::initializer_list signal_options; - std::initializer_list variable_options; + std::initializer_list region_options = { + FliIterator::OTM_CONSTANTS, + FliIterator::OTM_SIGNALS, + FliIterator::OTM_REGIONS, + }; + std::initializer_list signal_options = { + FliIterator::OTM_SIGNAL_SUB_ELEMENTS, + }; + std::initializer_list variable_options = { + FliIterator::OTM_VARIABLE_SUB_ELEMENTS, + }; return decltype(FliIterator::iterate_over) { - {accArchitecture, region_options = { - FliIterator::OTM_CONSTANTS, - FliIterator::OTM_SIGNALS, - FliIterator::OTM_REGIONS, - }}, + {accArchitecture, region_options}, {accEntityVitalLevel0, region_options}, {accArchVitalLevel0, region_options}, {accArchVitalLevel1, region_options}, @@ -612,16 +616,12 @@ decltype(FliIterator::iterate_over) FliIterator::iterate_over = []{ {accForGenerate, region_options}, {accConfiguration, region_options}, - {accSignal, signal_options = { - FliIterator::OTM_SIGNAL_SUB_ELEMENTS, - }}, + {accSignal, signal_options}, {accSignalBit, signal_options}, {accSignalSubComposite, signal_options}, {accAliasSignal, signal_options}, - {accVariable, variable_options = { - FliIterator::OTM_VARIABLE_SUB_ELEMENTS, - }}, + {accVariable, variable_options}, {accGeneric, variable_options}, {accGenericConstant, variable_options}, {accAliasConstant, variable_options}, diff --git a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp index ad0bdd98..87b654cc 100644 --- a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp +++ b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp @@ -921,52 +921,52 @@ VhpiNextPhaseCbHdl::VhpiNextPhaseCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), decltype(VhpiIterator::iterate_over) VhpiIterator::iterate_over = []{ /* for reused lists */ - std::initializer_list root_options; - std::initializer_list sig_options; - std::initializer_list simplesig_options; - std::initializer_list gen_options; + std::initializer_list root_options = { + vhpiInternalRegions, + vhpiSigDecls, + vhpiVarDecls, + vhpiPortDecls, + vhpiGenericDecls, + vhpiConstDecls, + // vhpiIndexedNames, + vhpiCompInstStmts, + vhpiBlockStmts, + }; + std::initializer_list sig_options = { + vhpiIndexedNames, + vhpiSelectedNames, + }; + std::initializer_list simplesig_options = { + vhpiDecls, + vhpiInternalRegions, + vhpiSensitivitys, + vhpiStmts, + }; + std::initializer_list gen_options = { + vhpiDecls, + vhpiInternalRegions, + vhpiSigDecls, + vhpiVarDecls, + vhpiConstDecls, + vhpiCompInstStmts, + vhpiBlockStmts, + }; return decltype(VhpiIterator::iterate_over) { - {vhpiRootInstK, root_options = { - vhpiInternalRegions, - vhpiSigDecls, - vhpiVarDecls, - vhpiPortDecls, - vhpiGenericDecls, - vhpiConstDecls, - // vhpiIndexedNames, - vhpiCompInstStmts, - vhpiBlockStmts, - }}, + {vhpiRootInstK, root_options}, {vhpiCompInstStmtK, root_options}, - {vhpiGenericDeclK, sig_options = { - vhpiIndexedNames, - vhpiSelectedNames, - }}, + {vhpiGenericDeclK, sig_options}, {vhpiSigDeclK, sig_options}, {vhpiSelectedNameK, sig_options}, {vhpiIndexedNameK, sig_options}, {vhpiPortDeclK, sig_options}, - {vhpiCondSigAssignStmtK, simplesig_options = { - vhpiDecls, - vhpiInternalRegions, - vhpiSensitivitys, - vhpiStmts, - }}, + {vhpiCondSigAssignStmtK, simplesig_options}, {vhpiSimpleSigAssignStmtK, simplesig_options}, {vhpiSelectSigAssignStmtK, simplesig_options}, - {vhpiForGenerateK, gen_options = { - vhpiDecls, - vhpiInternalRegions, - vhpiSigDecls, - vhpiVarDecls, - vhpiConstDecls, - vhpiCompInstStmts, - vhpiBlockStmts, - }}, + {vhpiForGenerateK, gen_options}, {vhpiIfGenerateK, gen_options}, {vhpiBlockStmtK, gen_options}, diff --git a/cocotb/share/lib/vpi/VpiCbHdl.cpp b/cocotb/share/lib/vpi/VpiCbHdl.cpp index 1377a161..364e05c6 100644 --- a/cocotb/share/lib/vpi/VpiCbHdl.cpp +++ b/cocotb/share/lib/vpi/VpiCbHdl.cpp @@ -561,59 +561,59 @@ VpiNextPhaseCbHdl::VpiNextPhaseCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), decltype(VpiIterator::iterate_over) VpiIterator::iterate_over = []{ /* for reused lists */ - std::initializer_list module_options; - std::initializer_list struct_options; + std::initializer_list module_options = { + //vpiModule, // Aldec SEGV on mixed language + //vpiModuleArray, // Aldec SEGV on mixed language + //vpiIODecl, // Don't care about these + vpiNet, + vpiNetArray, + vpiReg, + vpiRegArray, + vpiMemory, + vpiIntegerVar, + vpiRealVar, + vpiRealNet, + vpiStructVar, + vpiStructNet, + //vpiVariables // Aldec SEGV on plain Verilog + vpiNamedEvent, + vpiNamedEventArray, + vpiParameter, + //vpiSpecParam, // Don't care + //vpiParamAssign, // Aldec SEGV on mixed language + //vpiDefParam, // Don't care + vpiPrimitive, + vpiPrimitiveArray, + //vpiContAssign, // Don't care + vpiProcess, // Don't care + vpiModPath, + vpiTchk, + vpiAttribute, + vpiPort, + vpiInternalScope, + //vpiInterface, // Aldec SEGV on mixed language + //vpiInterfaceArray, // Aldec SEGV on mixed language + }; + std::initializer_list struct_options = { + vpiNet, +#ifndef IUS + vpiNetArray, +#endif + vpiReg, + vpiRegArray, + vpiMemory, + vpiParameter, + vpiPrimitive, + vpiPrimitiveArray, + vpiAttribute, + vpiMember, + }; return decltype(VpiIterator::iterate_over) { - {vpiModule, module_options = { - //vpiModule, // Aldec SEGV on mixed language - //vpiModuleArray, // Aldec SEGV on mixed language - //vpiIODecl, // Don't care about these - vpiNet, - vpiNetArray, - vpiReg, - vpiRegArray, - vpiMemory, - vpiIntegerVar, - vpiRealVar, - vpiRealNet, - vpiStructVar, - vpiStructNet, - //vpiVariables // Aldec SEGV on plain Verilog - vpiNamedEvent, - vpiNamedEventArray, - vpiParameter, - //vpiSpecParam, // Don't care - //vpiParamAssign, // Aldec SEGV on mixed language - //vpiDefParam, // Don't care - vpiPrimitive, - vpiPrimitiveArray, - //vpiContAssign, // Don't care - vpiProcess, // Don't care - vpiModPath, - vpiTchk, - vpiAttribute, - vpiPort, - vpiInternalScope, - //vpiInterface, // Aldec SEGV on mixed language - //vpiInterfaceArray, // Aldec SEGV on mixed language - }}, + {vpiModule, module_options}, {vpiGenScope, module_options}, - {vpiStructVar, struct_options = { - vpiNet, -#ifndef IUS - vpiNetArray, -#endif - vpiReg, - vpiRegArray, - vpiMemory, - vpiParameter, - vpiPrimitive, - vpiPrimitiveArray, - vpiAttribute, - vpiMember, - }}, + {vpiStructVar, struct_options}, {vpiStructNet, struct_options}, {vpiNet, { From 73b2aaa592d6d4ba19432c06d305f83d5e0194d2 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Tue, 21 Jul 2020 21:38:46 -0500 Subject: [PATCH 2435/2656] Bump release notes via towncrier --- .../source/newsfragments/1031.feature.rst | 1 - .../source/newsfragments/1063.change.rst | 1 - .../source/newsfragments/1107.feature.rst | 1 - .../source/newsfragments/1180.feature.rst | 1 - .../source/newsfragments/1244.feature.rst | 1 - .../source/newsfragments/1253.bugfix.rst | 1 - .../source/newsfragments/1255.feature.rst | 13 -- .../source/newsfragments/1266.feature.rst | 14 -- .../source/newsfragments/1279.bugfix.rst | 2 - .../source/newsfragments/1282.feature.rst | 1 - .../source/newsfragments/1309.feature.rst | 1 - .../source/newsfragments/1314.bugfix.rst | 1 - .../source/newsfragments/1318.doc.rst | 1 - .../source/newsfragments/1339.removal.rst | 2 - .../source/newsfragments/1380.change.rst | 1 - .../source/newsfragments/1381.removal.rst | 1 - .../source/newsfragments/1400.doc.rst | 1 - .../source/newsfragments/1403.feature.rst | 15 -- .../source/newsfragments/1410.bugfix.rst | 1 - .../source/newsfragments/1411.feature.rst | 4 - .../source/newsfragments/1424.feature.rst | 3 - .../source/newsfragments/1425.change.rst | 4 - .../source/newsfragments/1445.change.rst | 1 - .../source/newsfragments/1457.feature.rst | 2 - .../source/newsfragments/1482.doc.rst | 2 - .../source/newsfragments/1489.removal.rst | 1 - .../source/newsfragments/1495.removal.rst | 4 - .../source/newsfragments/1507.bugfix.rst | 1 - .../source/newsfragments/1507.change.rst | 2 - .../source/newsfragments/1507.feature.rst | 13 -- .../source/newsfragments/1511.removal.rst | 1 - .../source/newsfragments/1514.change.rst | 8 - .../source/newsfragments/1519.removal.rst | 1 - .../source/newsfragments/1545.bugfix.rst | 1 - .../source/newsfragments/1574.change.rst | 1 - .../source/newsfragments/1601.feature.rst | 1 - .../source/newsfragments/1629.feature.rst | 1 - .../source/newsfragments/1632.removal.rst | 3 - .../source/newsfragments/1649.feature.rst | 1 - .../source/newsfragments/1661.bugfix.rst | 1 - .../source/newsfragments/1691.change.rst | 1 - .../source/newsfragments/1693.removal.rst | 1 - .../source/newsfragments/1705.bugfix.rst | 1 - .../source/newsfragments/1723.feature.rst | 1 - .../source/newsfragments/1843.change.rst | 1 - .../source/newsfragments/1843.feature.rst | 1 - .../source/newsfragments/1882.bugfix.rst | 1 - .../source/newsfragments/1898.change.rst | 2 - .../source/newsfragments/1905.bugfix.rst | 1 - .../source/newsfragments/767.change.rst | 3 - documentation/source/release_notes.rst | 157 ++++++++++++++++++ 51 files changed, 157 insertions(+), 129 deletions(-) delete mode 100644 documentation/source/newsfragments/1031.feature.rst delete mode 100644 documentation/source/newsfragments/1063.change.rst delete mode 100644 documentation/source/newsfragments/1107.feature.rst delete mode 100644 documentation/source/newsfragments/1180.feature.rst delete mode 100644 documentation/source/newsfragments/1244.feature.rst delete mode 100644 documentation/source/newsfragments/1253.bugfix.rst delete mode 100644 documentation/source/newsfragments/1255.feature.rst delete mode 100644 documentation/source/newsfragments/1266.feature.rst delete mode 100644 documentation/source/newsfragments/1279.bugfix.rst delete mode 100644 documentation/source/newsfragments/1282.feature.rst delete mode 100644 documentation/source/newsfragments/1309.feature.rst delete mode 100644 documentation/source/newsfragments/1314.bugfix.rst delete mode 100644 documentation/source/newsfragments/1318.doc.rst delete mode 100644 documentation/source/newsfragments/1339.removal.rst delete mode 100644 documentation/source/newsfragments/1380.change.rst delete mode 100644 documentation/source/newsfragments/1381.removal.rst delete mode 100644 documentation/source/newsfragments/1400.doc.rst delete mode 100644 documentation/source/newsfragments/1403.feature.rst delete mode 100644 documentation/source/newsfragments/1410.bugfix.rst delete mode 100644 documentation/source/newsfragments/1411.feature.rst delete mode 100644 documentation/source/newsfragments/1424.feature.rst delete mode 100644 documentation/source/newsfragments/1425.change.rst delete mode 100644 documentation/source/newsfragments/1445.change.rst delete mode 100644 documentation/source/newsfragments/1457.feature.rst delete mode 100644 documentation/source/newsfragments/1482.doc.rst delete mode 100644 documentation/source/newsfragments/1489.removal.rst delete mode 100644 documentation/source/newsfragments/1495.removal.rst delete mode 100644 documentation/source/newsfragments/1507.bugfix.rst delete mode 100644 documentation/source/newsfragments/1507.change.rst delete mode 100644 documentation/source/newsfragments/1507.feature.rst delete mode 100644 documentation/source/newsfragments/1511.removal.rst delete mode 100644 documentation/source/newsfragments/1514.change.rst delete mode 100644 documentation/source/newsfragments/1519.removal.rst delete mode 100644 documentation/source/newsfragments/1545.bugfix.rst delete mode 100644 documentation/source/newsfragments/1574.change.rst delete mode 100644 documentation/source/newsfragments/1601.feature.rst delete mode 100644 documentation/source/newsfragments/1629.feature.rst delete mode 100644 documentation/source/newsfragments/1632.removal.rst delete mode 100644 documentation/source/newsfragments/1649.feature.rst delete mode 100644 documentation/source/newsfragments/1661.bugfix.rst delete mode 100644 documentation/source/newsfragments/1691.change.rst delete mode 100644 documentation/source/newsfragments/1693.removal.rst delete mode 100644 documentation/source/newsfragments/1705.bugfix.rst delete mode 100644 documentation/source/newsfragments/1723.feature.rst delete mode 100644 documentation/source/newsfragments/1843.change.rst delete mode 100644 documentation/source/newsfragments/1843.feature.rst delete mode 100644 documentation/source/newsfragments/1882.bugfix.rst delete mode 100644 documentation/source/newsfragments/1898.change.rst delete mode 100644 documentation/source/newsfragments/1905.bugfix.rst delete mode 100644 documentation/source/newsfragments/767.change.rst diff --git a/documentation/source/newsfragments/1031.feature.rst b/documentation/source/newsfragments/1031.feature.rst deleted file mode 100644 index c2ed77e4..00000000 --- a/documentation/source/newsfragments/1031.feature.rst +++ /dev/null @@ -1 +0,0 @@ -:class:`~cocotb.triggers.Lock` can now be used in :keyword:`async with` statements. diff --git a/documentation/source/newsfragments/1063.change.rst b/documentation/source/newsfragments/1063.change.rst deleted file mode 100644 index bf6f11a4..00000000 --- a/documentation/source/newsfragments/1063.change.rst +++ /dev/null @@ -1 +0,0 @@ -Mentor Questa, Aldec Riviera-PRO and GHDL are now started in the directory containing the Makefile and also save :file:`results.xml` there, bringing them in line with the behavior used by other simulators. (:pr:`1598`) (:pr:`1599`) diff --git a/documentation/source/newsfragments/1107.feature.rst b/documentation/source/newsfragments/1107.feature.rst deleted file mode 100644 index c161fb9b..00000000 --- a/documentation/source/newsfragments/1107.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Add support for distinguishing between NET (vpiNet) and REG (vpiReg) type when using vpi interface. \ No newline at end of file diff --git a/documentation/source/newsfragments/1180.feature.rst b/documentation/source/newsfragments/1180.feature.rst deleted file mode 100644 index fabdaa4d..00000000 --- a/documentation/source/newsfragments/1180.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Support for dropping into :mod:`pdb` upon failure, via the new :envvar:`COCOTB_PDB_ON_EXCEPTION` environment variable \ No newline at end of file diff --git a/documentation/source/newsfragments/1244.feature.rst b/documentation/source/newsfragments/1244.feature.rst deleted file mode 100644 index ee7bcfdf..00000000 --- a/documentation/source/newsfragments/1244.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Simulators run through a TCL script (Aldec Riviera Pro and Mentor simulators) now support a new :make:var:`RUN_ARGS` Makefile variable, which is passed to the first invocation of the tool during runtime. diff --git a/documentation/source/newsfragments/1253.bugfix.rst b/documentation/source/newsfragments/1253.bugfix.rst deleted file mode 100644 index 0a6017d6..00000000 --- a/documentation/source/newsfragments/1253.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Tests which fail at initialization, for instance due to no ``yield`` being present, are no longer silently ignored \ No newline at end of file diff --git a/documentation/source/newsfragments/1255.feature.rst b/documentation/source/newsfragments/1255.feature.rst deleted file mode 100644 index f5aa64c8..00000000 --- a/documentation/source/newsfragments/1255.feature.rst +++ /dev/null @@ -1,13 +0,0 @@ -Cocotb now supports the following example of forking a *non-decorated* :ref:`async coroutine `. - -.. code-block:: python3 - - async def example(): - for i in range(10): - await cocotb.triggers.Timer(10, "ns") - - cocotb.fork(example()) - -.. - towncrier will append the issue number taken from the file name here: -Issue diff --git a/documentation/source/newsfragments/1266.feature.rst b/documentation/source/newsfragments/1266.feature.rst deleted file mode 100644 index 14740fd1..00000000 --- a/documentation/source/newsfragments/1266.feature.rst +++ /dev/null @@ -1,14 +0,0 @@ -The cocotb log configuration is now less intrusive, and only configures the root logger instance, ``logging.getLogger()``, as part of :func:`cocotb.log.default_config` (:pr:`1266`). - -As such, it is now possible to override the default cocotb logging behavior with something like:: - - # remove the cocotb log handler and formatting - root = logging.getLogger() - for h in root.handlers[:]: - root.remove_handler(h) - h.close() - - # add your own - logging.basicConfig() - -.. consume the towncrier issue number on this line. diff --git a/documentation/source/newsfragments/1279.bugfix.rst b/documentation/source/newsfragments/1279.bugfix.rst deleted file mode 100644 index aa912499..00000000 --- a/documentation/source/newsfragments/1279.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -Tests that were not run because predecessors threw :class:`cocotb.result.SimFailure`, and caused the simulator to exit, are now recorded with an outcome of :class:`cocotb.result.SimFailure`. -Previously, these tests were ignored. diff --git a/documentation/source/newsfragments/1282.feature.rst b/documentation/source/newsfragments/1282.feature.rst deleted file mode 100644 index d9d32335..00000000 --- a/documentation/source/newsfragments/1282.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Support for ``vpiRealNet`` \ No newline at end of file diff --git a/documentation/source/newsfragments/1309.feature.rst b/documentation/source/newsfragments/1309.feature.rst deleted file mode 100644 index 89f6067b..00000000 --- a/documentation/source/newsfragments/1309.feature.rst +++ /dev/null @@ -1 +0,0 @@ -The colored output can now be disabled by the :envvar:`NO_COLOR` environment variable. diff --git a/documentation/source/newsfragments/1314.bugfix.rst b/documentation/source/newsfragments/1314.bugfix.rst deleted file mode 100644 index 1ecf776e..00000000 --- a/documentation/source/newsfragments/1314.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Makefiles now correctly fail if the simulation crashes before a ``results.xml`` file can be written. \ No newline at end of file diff --git a/documentation/source/newsfragments/1318.doc.rst b/documentation/source/newsfragments/1318.doc.rst deleted file mode 100644 index efcb48c4..00000000 --- a/documentation/source/newsfragments/1318.doc.rst +++ /dev/null @@ -1 +0,0 @@ -If a makefile uses cocotb's :file:`Makefile.sim`, ``make help`` now lists the supported targets and variables. diff --git a/documentation/source/newsfragments/1339.removal.rst b/documentation/source/newsfragments/1339.removal.rst deleted file mode 100644 index adea304d..00000000 --- a/documentation/source/newsfragments/1339.removal.rst +++ /dev/null @@ -1,2 +0,0 @@ -:func:`cocotb.utils.reject_remaining_kwargs` is deprecated, as it is no longer -needed now that we only support Python 3.5 and newer. diff --git a/documentation/source/newsfragments/1380.change.rst b/documentation/source/newsfragments/1380.change.rst deleted file mode 100644 index 4dbfd255..00000000 --- a/documentation/source/newsfragments/1380.change.rst +++ /dev/null @@ -1 +0,0 @@ -Tests are now evaluated in order of their appearance in the :envvar:`MODULE` environment variable, their stage, and the order of invocation of the :class:`cocotb.test` decorator within a module. diff --git a/documentation/source/newsfragments/1381.removal.rst b/documentation/source/newsfragments/1381.removal.rst deleted file mode 100644 index baa04db6..00000000 --- a/documentation/source/newsfragments/1381.removal.rst +++ /dev/null @@ -1 +0,0 @@ -The value of :class:`cocotb.handle.StringObject`\ s is now of type :class:`bytes`, instead of :class:`str` with an implied ASCII encoding scheme. diff --git a/documentation/source/newsfragments/1400.doc.rst b/documentation/source/newsfragments/1400.doc.rst deleted file mode 100644 index 015c979d..00000000 --- a/documentation/source/newsfragments/1400.doc.rst +++ /dev/null @@ -1 +0,0 @@ -A new section :ref:`rotating-logger` has been added. diff --git a/documentation/source/newsfragments/1403.feature.rst b/documentation/source/newsfragments/1403.feature.rst deleted file mode 100644 index 1d9b769e..00000000 --- a/documentation/source/newsfragments/1403.feature.rst +++ /dev/null @@ -1,15 +0,0 @@ -Cocotb now supports deposit/force/release/freeze actions on simulator handles, exposing functionality similar to the respective Verilog/VHDL assignments. - -.. code-block:: python3 - - from cocotb.handle import Deposit, Force, Release, Freeze - - dut.q <= 1 # A regular value deposit - dut.q <= Deposit(1) # The same, higher verbosity - dut.q <= Force(1) # Force value of q to 1 - dut.q <= Release() # Release q from a Force - dut.q <= Freeze() # Freeze the current value of q - -.. - towncrier will append the issue number taken from the file name here: -Issue diff --git a/documentation/source/newsfragments/1410.bugfix.rst b/documentation/source/newsfragments/1410.bugfix.rst deleted file mode 100644 index 3a1c8db3..00000000 --- a/documentation/source/newsfragments/1410.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Logging of non-string messages with colored log output is now working. diff --git a/documentation/source/newsfragments/1411.feature.rst b/documentation/source/newsfragments/1411.feature.rst deleted file mode 100644 index 6e795557..00000000 --- a/documentation/source/newsfragments/1411.feature.rst +++ /dev/null @@ -1,4 +0,0 @@ -Custom logging handlers can now access the simulator time using -:attr:`logging.LogRecord.created_sim_time`, provided the -:class:`~cocotb.log.SimTimeContextFilter` filter added by -:func:`~cocotb.log.default_config` is not removed from the logger instance. diff --git a/documentation/source/newsfragments/1424.feature.rst b/documentation/source/newsfragments/1424.feature.rst deleted file mode 100644 index 03a8efde..00000000 --- a/documentation/source/newsfragments/1424.feature.rst +++ /dev/null @@ -1,3 +0,0 @@ -Questa now supports :envvar:`PLUSARGS`. -This requires that ``tcl.h`` be present on the system. -This is likely included in your installation of Questa, otherwise, specify ``CFLAGS=-I/path/to/tcl/includedir``. diff --git a/documentation/source/newsfragments/1425.change.rst b/documentation/source/newsfragments/1425.change.rst deleted file mode 100644 index 375c4caa..00000000 --- a/documentation/source/newsfragments/1425.change.rst +++ /dev/null @@ -1,4 +0,0 @@ -All libraries are compiled during installation to the ``cocotb/libs`` directory. -The interface libraries ``libcocotbvpi`` and ``libcocotbvhpi`` have been renamed to have a ``_simulator_name`` postfix. -The ``simulator`` module has moved to :mod:`cocotb.simulator`. -The ``LD_LIBRARY_PATH`` environment variable no longer needs to be set by the makefiles, as the libraries now discover each other via ``RPATH`` settings. diff --git a/documentation/source/newsfragments/1445.change.rst b/documentation/source/newsfragments/1445.change.rst deleted file mode 100644 index 8968ea8d..00000000 --- a/documentation/source/newsfragments/1445.change.rst +++ /dev/null @@ -1 +0,0 @@ -Cocotb must now be :ref:`installed ` before it can be used. diff --git a/documentation/source/newsfragments/1457.feature.rst b/documentation/source/newsfragments/1457.feature.rst deleted file mode 100644 index 11e7e1a5..00000000 --- a/documentation/source/newsfragments/1457.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -The name of the entry point symbol for libraries in :envvar:`GPI_EXTRA` can now be customized. -The delimiter between each library in the list has changed from ``:`` to ``,``. diff --git a/documentation/source/newsfragments/1482.doc.rst b/documentation/source/newsfragments/1482.doc.rst deleted file mode 100644 index c78af0d7..00000000 --- a/documentation/source/newsfragments/1482.doc.rst +++ /dev/null @@ -1,2 +0,0 @@ -The documentation at http://docs.cocotb.org/ has been restructured, -making it easier to find relevant information. diff --git a/documentation/source/newsfragments/1489.removal.rst b/documentation/source/newsfragments/1489.removal.rst deleted file mode 100644 index 78ef1d8a..00000000 --- a/documentation/source/newsfragments/1489.removal.rst +++ /dev/null @@ -1 +0,0 @@ -:class:`ReturnValue` is now deprecated. Use a :keyword:`return` statement instead; this works in all supported versions of Python. diff --git a/documentation/source/newsfragments/1495.removal.rst b/documentation/source/newsfragments/1495.removal.rst deleted file mode 100644 index 89e4275e..00000000 --- a/documentation/source/newsfragments/1495.removal.rst +++ /dev/null @@ -1,4 +0,0 @@ -The makefile variable :make:var:`VERILATOR_TRACE` -that was not supported for all simulators has been deprecated. -Using it prints a deprecation warning and points to the documentation section -:ref:`simulator-support` explaining how to get the same effect by other means. diff --git a/documentation/source/newsfragments/1507.bugfix.rst b/documentation/source/newsfragments/1507.bugfix.rst deleted file mode 100644 index cd151972..00000000 --- a/documentation/source/newsfragments/1507.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Getting and setting the value of a :class:`~cocotb.handle.NonHierarchyIndexableObject` now iterates through the correct range of the simulation object, so arrays that do not start/end at index 0 are supported. \ No newline at end of file diff --git a/documentation/source/newsfragments/1507.change.rst b/documentation/source/newsfragments/1507.change.rst deleted file mode 100644 index b28d53f6..00000000 --- a/documentation/source/newsfragments/1507.change.rst +++ /dev/null @@ -1,2 +0,0 @@ -:attr:`cocotb.handle.NonHierarchyIndexableObject.value` is now a list in left-to-right range order of the underlying simulation object. -Previously the list was always ordered low-to-high. \ No newline at end of file diff --git a/documentation/source/newsfragments/1507.feature.rst b/documentation/source/newsfragments/1507.feature.rst deleted file mode 100644 index 395d1f0f..00000000 --- a/documentation/source/newsfragments/1507.feature.rst +++ /dev/null @@ -1,13 +0,0 @@ -New methods for setting the value of a :class:`~cocotb.handle.NonHierarchyIndexableObject` (HDL arrays). (:pr:`1507`) - -.. code-block:: python3 - - # Now supported - dut.some_array <= [0xAA, 0xBB, 0xCC] - dut.some_array.value = [0xAA, 0xBB, 0xCC] - - # For simulators that support n-dimensional arrays - dut.some_2d_array <= [[0xAA, 0xBB], [0xCC, 0xDD]] - dut.some_2d_array.value = [[0xAA, 0xBB], [0xCC, 0xDD]] - -.. consume the towncrier issue number on this line. diff --git a/documentation/source/newsfragments/1511.removal.rst b/documentation/source/newsfragments/1511.removal.rst deleted file mode 100644 index b0ef1b3c..00000000 --- a/documentation/source/newsfragments/1511.removal.rst +++ /dev/null @@ -1 +0,0 @@ -``BinaryValue.get_hex_buff`` produced nonsense and has been removed. diff --git a/documentation/source/newsfragments/1514.change.rst b/documentation/source/newsfragments/1514.change.rst deleted file mode 100644 index f05b4706..00000000 --- a/documentation/source/newsfragments/1514.change.rst +++ /dev/null @@ -1,8 +0,0 @@ -Various binary representations have changed type from :class:`str` to :class:`bytes`. These include: - -* :attr:`cocotb.binary.BinaryValue.buff`, which as a consequence means :meth:`cocotb.binary.BinaryValue.assign` no longer accepts malformed ``10xz``-style :class:`str`\ s (which were treated as binary). -* The objects produced by :mod:`cocotb.generators.byte`, which means that single bytes are represented by :class:`int` instead of 1-character :class:`str`\ s. -* The packets produced by the :class:`~cocotb.drivers.avalon.AvalonSTPkts`. - -Code working with these objects may find it needs to switch from creating :class:`str` objects like ``"this"`` to :class:`bytes` objects like ``b"this"``. -This change is a consequence of the move to Python 3. diff --git a/documentation/source/newsfragments/1519.removal.rst b/documentation/source/newsfragments/1519.removal.rst deleted file mode 100644 index b0f7a4cb..00000000 --- a/documentation/source/newsfragments/1519.removal.rst +++ /dev/null @@ -1 +0,0 @@ -Passing :class:`str` instances to :func:`cocotb.utils.hexdump` and :func:`cocotb.utils.hexdiffs` is deprecated. :class:`bytes` objects should be passed instead. diff --git a/documentation/source/newsfragments/1545.bugfix.rst b/documentation/source/newsfragments/1545.bugfix.rst deleted file mode 100644 index 14ace368..00000000 --- a/documentation/source/newsfragments/1545.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -The :class:`~cocotb.monitors.xgmii.XGMII` monitor no longer crashes on Python 3, and now assembles packets as :class:`bytes` instead of :class:`str`. The :class:`~cocotb.drivers.xgmii.XGMII` driver has expected :class:`bytes` since cocotb 1.2.0. diff --git a/documentation/source/newsfragments/1574.change.rst b/documentation/source/newsfragments/1574.change.rst deleted file mode 100644 index 4d7384c8..00000000 --- a/documentation/source/newsfragments/1574.change.rst +++ /dev/null @@ -1 +0,0 @@ -There's no longer any need to set the ``PYTHON_BIN`` makefile variable, the Python executable automatically matches the one cocotb was installed into. \ No newline at end of file diff --git a/documentation/source/newsfragments/1601.feature.rst b/documentation/source/newsfragments/1601.feature.rst deleted file mode 100644 index 01b93453..00000000 --- a/documentation/source/newsfragments/1601.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Added support for Aldec's Active-HDL simulator. diff --git a/documentation/source/newsfragments/1629.feature.rst b/documentation/source/newsfragments/1629.feature.rst deleted file mode 100644 index f2bc8af8..00000000 --- a/documentation/source/newsfragments/1629.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Including ``Makefile.inc`` from user makefiles is now a no-op and deprecated. Lines like ``include $(shell cocotb-config --makefiles)/Makefile.inc`` can be removed from user makefiles without loss in functionality. diff --git a/documentation/source/newsfragments/1632.removal.rst b/documentation/source/newsfragments/1632.removal.rst deleted file mode 100644 index 4a027180..00000000 --- a/documentation/source/newsfragments/1632.removal.rst +++ /dev/null @@ -1,3 +0,0 @@ -``Makefile.pylib``, which provided helpers for building C extension modules for Python, has been removed. -Users of the ``PYTHON_LIBDIR`` and ``PYTHON_INCLUDEDIR`` variables will now have to compute these values themselves. -See the ``endian_swapper`` example for how to do this. diff --git a/documentation/source/newsfragments/1649.feature.rst b/documentation/source/newsfragments/1649.feature.rst deleted file mode 100644 index c3e49f0d..00000000 --- a/documentation/source/newsfragments/1649.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Support for using ``await`` inside an embedded IPython terminal, using :mod:`cocotb.ipython_support`. diff --git a/documentation/source/newsfragments/1661.bugfix.rst b/documentation/source/newsfragments/1661.bugfix.rst deleted file mode 100644 index a21aa042..00000000 --- a/documentation/source/newsfragments/1661.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -``signal <= value_of_wrong_type`` no longer breaks the scheduler, and throws an error immediately. diff --git a/documentation/source/newsfragments/1691.change.rst b/documentation/source/newsfragments/1691.change.rst deleted file mode 100644 index 9429ed09..00000000 --- a/documentation/source/newsfragments/1691.change.rst +++ /dev/null @@ -1 +0,0 @@ -The :make:var:`SIM` setting for Aldec Riviera-PRO has changed from ``aldec`` to ``riviera``. diff --git a/documentation/source/newsfragments/1693.removal.rst b/documentation/source/newsfragments/1693.removal.rst deleted file mode 100644 index 1fdb6eea..00000000 --- a/documentation/source/newsfragments/1693.removal.rst +++ /dev/null @@ -1 +0,0 @@ -Makefile and documentation for the NVC simulator which has never worked have been removed. diff --git a/documentation/source/newsfragments/1705.bugfix.rst b/documentation/source/newsfragments/1705.bugfix.rst deleted file mode 100644 index fdda0d57..00000000 --- a/documentation/source/newsfragments/1705.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Scheduling behavior is now consistent before and after the first :keyword:`await` of a :class:`~cocotb.triggers.GPITrigger`. diff --git a/documentation/source/newsfragments/1723.feature.rst b/documentation/source/newsfragments/1723.feature.rst deleted file mode 100644 index e55fa56e..00000000 --- a/documentation/source/newsfragments/1723.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Added :meth:`~cocotb.triggers.Event.is_set`, so users may check if an :class:`~cocotb.triggers.Event` has fired. diff --git a/documentation/source/newsfragments/1843.change.rst b/documentation/source/newsfragments/1843.change.rst deleted file mode 100644 index 1b04e14f..00000000 --- a/documentation/source/newsfragments/1843.change.rst +++ /dev/null @@ -1 +0,0 @@ -Certain methods on the :mod:`cocotb.simulator` Python module now throw a :exc:`RuntimeError` when no simulator is present, making it safe to use :mod:`cocotb` without a simulator present. diff --git a/documentation/source/newsfragments/1843.feature.rst b/documentation/source/newsfragments/1843.feature.rst deleted file mode 100644 index 1c72c23b..00000000 --- a/documentation/source/newsfragments/1843.feature.rst +++ /dev/null @@ -1 +0,0 @@ -The :func:`cocotb.simulator.is_running` function was added so a user of cocotb could determine if they are running within a simulator. diff --git a/documentation/source/newsfragments/1882.bugfix.rst b/documentation/source/newsfragments/1882.bugfix.rst deleted file mode 100644 index 535dc7a8..00000000 --- a/documentation/source/newsfragments/1882.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Iterating over ``for generate`` statements using VHPI has been fixed. This bug caused some simulators to crash, and was a regression in version 1.3. diff --git a/documentation/source/newsfragments/1898.change.rst b/documentation/source/newsfragments/1898.change.rst deleted file mode 100644 index 6ddaf5f8..00000000 --- a/documentation/source/newsfragments/1898.change.rst +++ /dev/null @@ -1,2 +0,0 @@ -Invalid values of the environment variable :envvar:`COCOTB_LOG_LEVEL` are no longer ignored. -They now raise an exception with instructions how to fix the problem. diff --git a/documentation/source/newsfragments/1905.bugfix.rst b/documentation/source/newsfragments/1905.bugfix.rst deleted file mode 100644 index 06fc3879..00000000 --- a/documentation/source/newsfragments/1905.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -The :class:`~cocotb.drivers.xgmii.XGMII` driver no longer emits a corrupted word on the first transfer. diff --git a/documentation/source/newsfragments/767.change.rst b/documentation/source/newsfragments/767.change.rst deleted file mode 100644 index 1b7837bb..00000000 --- a/documentation/source/newsfragments/767.change.rst +++ /dev/null @@ -1,3 +0,0 @@ -Cocotb no longer supports Python 2, at least Python 3.5 is now required. -Users of Python 2.7 can still use cocotb 1.3, but are heavily encouraged to update. -It is recommended to use the latest release of Python 3 for improved performance over older Python 3 versions. diff --git a/documentation/source/release_notes.rst b/documentation/source/release_notes.rst index 7cdf591e..d401a26e 100644 --- a/documentation/source/release_notes.rst +++ b/documentation/source/release_notes.rst @@ -8,6 +8,163 @@ All releases are available from the `GitHub Releases Page `. + + .. code-block:: python3 + + async def example(): + for i in range(10): + await cocotb.triggers.Timer(10, "ns") + + cocotb.fork(example()) + + .. + towncrier will append the issue number taken from the file name here: + Issue (:pr:`1255`) +- The cocotb log configuration is now less intrusive, and only configures the root logger instance, ``logging.getLogger()``, as part of :func:`cocotb.log.default_config` (:pr:`1266`). + + As such, it is now possible to override the default cocotb logging behavior with something like:: + + # remove the cocotb log handler and formatting + root = logging.getLogger() + for h in root.handlers[:]: + root.remove_handler(h) + h.close() + + # add your own + logging.basicConfig() + + .. consume the towncrier issue number on this line. (:pr:`1266`) +- Support for ``vpiRealNet`` (:pr:`1282`) +- The colored output can now be disabled by the :envvar:`NO_COLOR` environment variable. (:pr:`1309`) +- Cocotb now supports deposit/force/release/freeze actions on simulator handles, exposing functionality similar to the respective Verilog/VHDL assignments. + + .. code-block:: python3 + + from cocotb.handle import Deposit, Force, Release, Freeze + + dut.q <= 1 # A regular value deposit + dut.q <= Deposit(1) # The same, higher verbosity + dut.q <= Force(1) # Force value of q to 1 + dut.q <= Release() # Release q from a Force + dut.q <= Freeze() # Freeze the current value of q + + .. + towncrier will append the issue number taken from the file name here: + Issue (:pr:`1403`) +- Custom logging handlers can now access the simulator time using + :attr:`logging.LogRecord.created_sim_time`, provided the + :class:`~cocotb.log.SimTimeContextFilter` filter added by + :func:`~cocotb.log.default_config` is not removed from the logger instance. (:pr:`1411`) +- Questa now supports :envvar:`PLUSARGS`. + This requires that ``tcl.h`` be present on the system. + This is likely included in your installation of Questa, otherwise, specify ``CFLAGS=-I/path/to/tcl/includedir``. (:pr:`1424`) +- The name of the entry point symbol for libraries in :envvar:`GPI_EXTRA` can now be customized. + The delimiter between each library in the list has changed from ``:`` to ``,``. (:pr:`1457`) +- New methods for setting the value of a :class:`~cocotb.handle.NonHierarchyIndexableObject` (HDL arrays). (:pr:`1507`) + + .. code-block:: python3 + + # Now supported + dut.some_array <= [0xAA, 0xBB, 0xCC] + dut.some_array.value = [0xAA, 0xBB, 0xCC] + + # For simulators that support n-dimensional arrays + dut.some_2d_array <= [[0xAA, 0xBB], [0xCC, 0xDD]] + dut.some_2d_array.value = [[0xAA, 0xBB], [0xCC, 0xDD]] + + .. consume the towncrier issue number on this line. (:pr:`1507`) +- Added support for Aldec's Active-HDL simulator. (:pr:`1601`) +- Including ``Makefile.inc`` from user makefiles is now a no-op and deprecated. Lines like ``include $(shell cocotb-config --makefiles)/Makefile.inc`` can be removed from user makefiles without loss in functionality. (:pr:`1629`) +- Support for using ``await`` inside an embedded IPython terminal, using :mod:`cocotb.ipython_support`. (:pr:`1649`) +- Added :meth:`~cocotb.triggers.Event.is_set`, so users may check if an :class:`~cocotb.triggers.Event` has fired. (:pr:`1723`) +- The :func:`cocotb.simulator.is_running` function was added so a user of cocotb could determine if they are running within a simulator. (:pr:`1843`) + + +Bugfixes +-------- + +- Tests which fail at initialization, for instance due to no ``yield`` being present, are no longer silently ignored (:pr:`1253`) +- Tests that were not run because predecessors threw :class:`cocotb.result.SimFailure`, and caused the simulator to exit, are now recorded with an outcome of :class:`cocotb.result.SimFailure`. + Previously, these tests were ignored. (:pr:`1279`) +- Makefiles now correctly fail if the simulation crashes before a ``results.xml`` file can be written. (:pr:`1314`) +- Logging of non-string messages with colored log output is now working. (:pr:`1410`) +- Getting and setting the value of a :class:`~cocotb.handle.NonHierarchyIndexableObject` now iterates through the correct range of the simulation object, so arrays that do not start/end at index 0 are supported. (:pr:`1507`) +- The :class:`~cocotb.monitors.xgmii.XGMII` monitor no longer crashes on Python 3, and now assembles packets as :class:`bytes` instead of :class:`str`. The :class:`~cocotb.drivers.xgmii.XGMII` driver has expected :class:`bytes` since cocotb 1.2.0. (:pr:`1545`) +- ``signal <= value_of_wrong_type`` no longer breaks the scheduler, and throws an error immediately. (:pr:`1661`) +- Scheduling behavior is now consistent before and after the first :keyword:`await` of a :class:`~cocotb.triggers.GPITrigger`. (:pr:`1705`) +- Iterating over ``for generate`` statements using VHPI has been fixed. This bug caused some simulators to crash, and was a regression in version 1.3. (:pr:`1882`) +- The :class:`~cocotb.drivers.xgmii.XGMII` driver no longer emits a corrupted word on the first transfer. (:pr:`1905`) + + +Improved Documentation +---------------------- + +- If a makefile uses cocotb's :file:`Makefile.sim`, ``make help`` now lists the supported targets and variables. (:pr:`1318`) +- A new section :ref:`rotating-logger` has been added. (:pr:`1400`) +- The documentation at http://docs.cocotb.org/ has been restructured, + making it easier to find relevant information. (:pr:`1482`) + + +Deprecations and Removals +------------------------- + +- :func:`cocotb.utils.reject_remaining_kwargs` is deprecated, as it is no longer + needed now that we only support Python 3.5 and newer. (:pr:`1339`) +- The value of :class:`cocotb.handle.StringObject`\ s is now of type :class:`bytes`, instead of :class:`str` with an implied ASCII encoding scheme. (:pr:`1381`) +- :class:`ReturnValue` is now deprecated. Use a :keyword:`return` statement instead; this works in all supported versions of Python. (:pr:`1489`) +- The makefile variable :make:var:`VERILATOR_TRACE` + that was not supported for all simulators has been deprecated. + Using it prints a deprecation warning and points to the documentation section + :ref:`simulator-support` explaining how to get the same effect by other means. (:pr:`1495`) +- ``BinaryValue.get_hex_buff`` produced nonsense and has been removed. (:pr:`1511`) +- Passing :class:`str` instances to :func:`cocotb.utils.hexdump` and :func:`cocotb.utils.hexdiffs` is deprecated. :class:`bytes` objects should be passed instead. (:pr:`1519`) +- ``Makefile.pylib``, which provided helpers for building C extension modules for Python, has been removed. + Users of the ``PYTHON_LIBDIR`` and ``PYTHON_INCLUDEDIR`` variables will now have to compute these values themselves. + See the ``endian_swapper`` example for how to do this. (:pr:`1632`) +- Makefile and documentation for the NVC simulator which has never worked have been removed. (:pr:`1693`) + + +Changes +------- + +- Cocotb no longer supports Python 2, at least Python 3.5 is now required. + Users of Python 2.7 can still use cocotb 1.3, but are heavily encouraged to update. + It is recommended to use the latest release of Python 3 for improved performance over older Python 3 versions. (:pr:`767`) +- Mentor Questa, Aldec Riviera-PRO and GHDL are now started in the directory containing the Makefile and also save :file:`results.xml` there, bringing them in line with the behavior used by other simulators. (:pr:`1598`) (:pr:`1599`) (:pr:`1063`) +- Tests are now evaluated in order of their appearance in the :envvar:`MODULE` environment variable, their stage, and the order of invocation of the :class:`cocotb.test` decorator within a module. (:pr:`1380`) +- All libraries are compiled during installation to the ``cocotb/libs`` directory. + The interface libraries ``libcocotbvpi`` and ``libcocotbvhpi`` have been renamed to have a ``_simulator_name`` postfix. + The ``simulator`` module has moved to :mod:`cocotb.simulator`. + The ``LD_LIBRARY_PATH`` environment variable no longer needs to be set by the makefiles, as the libraries now discover each other via ``RPATH`` settings. (:pr:`1425`) +- Cocotb must now be :ref:`installed ` before it can be used. (:pr:`1445`) +- :attr:`cocotb.handle.NonHierarchyIndexableObject.value` is now a list in left-to-right range order of the underlying simulation object. + Previously the list was always ordered low-to-high. (:pr:`1507`) +- Various binary representations have changed type from :class:`str` to :class:`bytes`. These include: + + * :attr:`cocotb.binary.BinaryValue.buff`, which as a consequence means :meth:`cocotb.binary.BinaryValue.assign` no longer accepts malformed ``10xz``-style :class:`str`\ s (which were treated as binary). + * The objects produced by :mod:`cocotb.generators.byte`, which means that single bytes are represented by :class:`int` instead of 1-character :class:`str`\ s. + * The packets produced by the :class:`~cocotb.drivers.avalon.AvalonSTPkts`. + + Code working with these objects may find it needs to switch from creating :class:`str` objects like ``"this"`` to :class:`bytes` objects like ``b"this"``. + This change is a consequence of the move to Python 3. (:pr:`1514`) +- There's no longer any need to set the ``PYTHON_BIN`` makefile variable, the Python executable automatically matches the one cocotb was installed into. (:pr:`1574`) +- The :make:var:`SIM` setting for Aldec Riviera-PRO has changed from ``aldec`` to ``riviera``. (:pr:`1691`) +- Certain methods on the :mod:`cocotb.simulator` Python module now throw a :exc:`RuntimeError` when no simulator is present, making it safe to use :mod:`cocotb` without a simulator present. (:pr:`1843`) +- Invalid values of the environment variable :envvar:`COCOTB_LOG_LEVEL` are no longer ignored. + They now raise an exception with instructions how to fix the problem. (:pr:`1898`) + + cocotb 1.3.2 ============ From 7825719a4b205815d75eb810ba5f3a43cb3073b6 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sat, 11 Jul 2020 00:28:46 +0200 Subject: [PATCH 2436/2656] Fix flake8 F841 reports for core code Ignore this ("local variable ... is assigned to but never used") for tests and examples where names are useful for didactic purposes. Refs #1547. --- cocotb/__init__.py | 2 +- cocotb/config.py | 2 +- cocotb/drivers/amba.py | 2 ++ cocotb/drivers/avalon.py | 2 +- cocotb/scheduler.py | 2 +- cocotb/utils.py | 1 - cocotb/xunit_reporter.py | 4 ++-- setup.cfg | 7 ++++++- 8 files changed, 14 insertions(+), 8 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index ffefc694..7236910c 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -252,7 +252,7 @@ def _initialise_testbench(argv_): def _sim_event(level, message): """Function that can be called externally to signal an event.""" - SIM_INFO = 0 + # SIM_INFO = 0 SIM_TEST_FAIL = 1 SIM_FAIL = 2 from cocotb.result import TestFailure, SimFailure diff --git a/cocotb/config.py b/cocotb/config.py index d495095d..18ccee8a 100755 --- a/cocotb/config.py +++ b/cocotb/config.py @@ -157,7 +157,7 @@ def main(): parser.print_help(sys.stderr) sys.exit(1) - args = parser.parse_args() + parser.parse_args() if __name__ == "__main__": diff --git a/cocotb/drivers/amba.py b/cocotb/drivers/amba.py index 2117ba8e..3ade2810 100644 --- a/cocotb/drivers/amba.py +++ b/cocotb/drivers/amba.py @@ -292,6 +292,7 @@ def _write_data(self): "AWLEN %d\n" % _awlen + "AWSIZE %d\n" % _awsize + "AWBURST %d\n" % _awburst + + "AWPROT %d\n" % _awprot + "BURST_LENGTH %d\n" % burst_length + "Bytes in beat %d\n" % bytes_in_beat) @@ -341,6 +342,7 @@ def _read_data(self): "ARLEN %d\n" % _arlen + "ARSIZE %d\n" % _arsize + "ARBURST %d\n" % _arburst + + "ARPROT %d\n" % _arprot + "BURST_LENGTH %d\n" % burst_length + "Bytes in beat %d\n" % bytes_in_beat) diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index 90d2d8b3..fd38826a 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -213,7 +213,7 @@ def write(self, address: int, value: int) -> None: # Wait for waitrequest to be low if hasattr(self.bus, "waitrequest"): - count = yield self._wait_for_nsignal(self.bus.waitrequest) + yield self._wait_for_nsignal(self.bus.waitrequest) # Deassert write yield RisingEdge(self.clock) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index a3034704..8bf29582 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -763,7 +763,7 @@ def schedule(self, coroutine, trigger=None): self.log.debug("Coroutine %s yielded %s (mode %d)" % (coroutine.__qualname__, str(result), self._mode)) - except cocotb.decorators.CoroutineComplete as exc: + except cocotb.decorators.CoroutineComplete: if _debug: self.log.debug("Coroutine {} completed with {}".format( coroutine, coroutine._outcome diff --git a/cocotb/utils.py b/cocotb/utils.py index 2c437d45..371e7547 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -347,7 +347,6 @@ def highlight(string: str, colour=ANSI.COLOR_HILITE_HEXDIFF_DEFAULT) -> str: doy = 0 l = len(backtrackx) while i < l: - separate = 0 linex = backtrackx[i:i+16] liney = backtracky[i:i+16] xx = sum(len(k) for k in linex) diff --git a/cocotb/xunit_reporter.py b/cocotb/xunit_reporter.py index bd8a1efa..f0749ba4 100644 --- a/cocotb/xunit_reporter.py +++ b/cocotb/xunit_reporter.py @@ -112,12 +112,12 @@ def add_log(self, logfile, testcase=None): def add_failure(self, testcase=None, **kwargs): if testcase is None: testcase = self.last_testcase - log = SubElement(testcase, "failure", **kwargs) + SubElement(testcase, "failure", **kwargs) def add_skipped(self, testcase=None, **kwargs): if testcase is None: testcase = self.last_testcase - log = SubElement(testcase, "skipped", **kwargs) + SubElement(testcase, "skipped", **kwargs) def indent(self, elem, level=0): i = "\n" + level*" " diff --git a/setup.cfg b/setup.cfg index 9078ed71..3cf66c22 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,6 +4,7 @@ exclude = documentation makefiles venv + .tox examples/endian_swapper/cosim ignore = @@ -19,9 +20,13 @@ ignore = E501 # line too long (... > 79 characters) E741 # ambiguous variable name 'l' F405 # ... may be undefined, or defined from star - F841 # local variable ... is assigned to but never used W504 # line break after binary operator +per-file-ignores = + # F841 - local variable ... is assigned to but never used + tests/*: F841 + examples/*: F841 + [tool:pytest] addopts = -v --cov=cocotb --cov-branch testpaths = tests/pytest From a11b72e97b409fb64698fe0f84e05fcfd5bc1d03 Mon Sep 17 00:00:00 2001 From: tpambor <1379478+tpambor@users.noreply.github.com> Date: Fri, 24 Jul 2020 09:39:32 +0200 Subject: [PATCH 2437/2656] Fix build with gcc 10 (#2003) --- cocotb/share/lib/vhpi/VhpiCbHdl.cpp | 1 + cocotb/share/lib/vpi/VpiCbHdl.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp index 87b654cc..f37e5fb6 100644 --- a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp +++ b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp @@ -29,6 +29,7 @@ #include "VhpiImpl.h" #include // numeric_limits #include // fixed-size int types and format strings +#include namespace { using bufSize_type = decltype(vhpiValueT::bufSize); diff --git a/cocotb/share/lib/vpi/VpiCbHdl.cpp b/cocotb/share/lib/vpi/VpiCbHdl.cpp index 364e05c6..61ffb1b1 100644 --- a/cocotb/share/lib/vpi/VpiCbHdl.cpp +++ b/cocotb/share/lib/vpi/VpiCbHdl.cpp @@ -29,6 +29,7 @@ #include #include "VpiImpl.h" +#include extern "C" int32_t handle_vpi_callback(p_cb_data cb_data); From 79792d1b37353bd87d3b51abdafbf2d7aa1b70a3 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 24 Jul 2020 02:40:35 -0500 Subject: [PATCH 2438/2656] Remove unnecessary code that is causing Travis to fail (#2001) The code may have been intended to source the correct version of python before this was done automatically, or perhaps as a way to ensure the correct version of Python was being used. It's now causing Travis CI to fail, likely because Python 3.7 isn't globally installed on the Travis instances. --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ead3f0b7..e7393359 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,7 +46,6 @@ jobs: - if [[ ! -e "./iverilog/README.txt" ]]; then rm -rf iverilog; git clone https://github.com/steveicarus/iverilog.git --depth=1 --branch v10_3; fi - cd iverilog && autoconf && ./configure && make -j2 && sudo make install && cd .. - pip install --upgrade tox virtualenv codecov - - pyenv global system $TRAVIS_PYTHON_VERSION script: - echo 'Running cocotb tests with Python 3.7 ...' && echo -en 'travis_fold:start:cocotb_py37' - tox -e py37 @@ -153,7 +152,6 @@ jobs: - git clone https://github.com/ghdl/ghdl.git --depth=1 --branch v0.36 - cd ghdl && ./configure && make -j2 && sudo make install && cd .. - pip install tox - - pyenv global system $TRAVIS_PYTHON_VERSION script: - tox -e py37 From 110bc37e618f11dad3100d35b4e587f6fa40f08d Mon Sep 17 00:00:00 2001 From: Andrew Powell Date: Tue, 28 Jul 2020 15:31:48 -0500 Subject: [PATCH 2439/2656] Modified the assert_raises context manager to also check for string pattern. --- tests/test_cases/test_cocotb/common.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/test_cases/test_cocotb/common.py b/tests/test_cases/test_cocotb/common.py index 2c0494aa..aa86831c 100644 --- a/tests/test_cases/test_cocotb/common.py +++ b/tests/test_cases/test_cocotb/common.py @@ -45,10 +45,13 @@ def _check_traceback(running_coro, exc_type, pattern): @contextmanager -def assert_raises(exc_type): +def assert_raises(exc_type, pattern=None): try: yield - except exc_type: + except exc_type as e: + if pattern: + assert re.match(pattern, str(e)), \ + "Correct exception type caught, but message did not match pattern" pass else: raise AssertionError("{} was not raised".format(exc_type.__name__)) From effdff6b31254278d000b50eaf5defb76b1355f5 Mon Sep 17 00:00:00 2001 From: Andrew Powell Date: Tue, 28 Jul 2020 15:31:27 -0500 Subject: [PATCH 2440/2656] Added checks for detecting when coroutine functions are passed into functions that expect coroutine objects. Fixed a pep8 problem. --- cocotb/decorators.py | 5 ++++ cocotb/scheduler.py | 7 ++++++ .../source/newsfragments/1593.feature.rst | 1 + .../test_cocotb/test_async_coroutines.py | 23 +++++++++++++++++++ 4 files changed, 36 insertions(+) create mode 100644 documentation/source/newsfragments/1593.feature.rst diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 71b1994c..0d5e9be8 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -93,6 +93,11 @@ def __init__(self, inst): self._natively_awaitable = True elif inspect.isgenerator(inst): self._natively_awaitable = False + elif inspect.iscoroutinefunction(inst): + raise TypeError( + "Coroutine function {} should be called prior to being " + "scheduled." + .format(inst)) elif sys.version_info >= (3, 6) and inspect.isasyncgen(inst): raise TypeError( "{} is an async generator, not a coroutine. " diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 8bf29582..704a5f73 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -638,6 +638,13 @@ def add(self, coroutine): Just a wrapper around self.schedule which provides some debug and useful error messages in the event of common gotchas. """ + + if inspect.iscoroutinefunction(coroutine): + raise TypeError( + "Coroutine function {} should be called prior to being " + "scheduled." + .format(coroutine)) + if isinstance(coroutine, cocotb.decorators.coroutine): raise TypeError( "Attempt to schedule a coroutine that hasn't started: {}.\n" diff --git a/documentation/source/newsfragments/1593.feature.rst b/documentation/source/newsfragments/1593.feature.rst new file mode 100644 index 00000000..1720a84d --- /dev/null +++ b/documentation/source/newsfragments/1593.feature.rst @@ -0,0 +1 @@ +:meth:`cocotb.fork` will now raise a descriptive :class:`TypeError` if a coroutine function is passed into them. \ No newline at end of file diff --git a/tests/test_cases/test_cocotb/test_async_coroutines.py b/tests/test_cases/test_cocotb/test_async_coroutines.py index 8c06b898..32de705a 100644 --- a/tests/test_cases/test_cocotb/test_async_coroutines.py +++ b/tests/test_cases/test_cocotb/test_async_coroutines.py @@ -8,6 +8,7 @@ import cocotb from cocotb.triggers import Timer from cocotb.outcomes import Value, Error +from common import assert_raises class produce: @@ -135,3 +136,25 @@ async def example(): yield example() assert ran + + +@cocotb.test() +async def test_fork_coroutine_function_exception(dut): + async def coro(): + pass + + pattern = "Coroutine function {} should be called " \ + "prior to being scheduled.".format(coro) + with assert_raises(TypeError, pattern): + cocotb.fork(coro) + + +@cocotb.test() +async def test_task_coroutine_function_exception(dut): + async def coro(dut): + pass + + pattern = "Coroutine function {} should be called " \ + "prior to being scheduled.".format(coro) + with assert_raises(TypeError, pattern): + cocotb.decorators.RunningTask(coro) From 10d668bcd7da8c908a72f3cf5a23e28986f611d4 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Fri, 24 Apr 2020 12:22:20 -0700 Subject: [PATCH 2441/2656] Move tracking of pending trigger to RunningTask. --- cocotb/decorators.py | 1 + cocotb/scheduler.py | 16 +++++----------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 0d5e9be8..63202909 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -112,6 +112,7 @@ def __init__(self, inst): self._started = False self._callbacks = [] self._outcome = None + self._trigger = None @lazy_property def log(self): diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 704a5f73..7cb88fec 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -227,9 +227,6 @@ def __init__(self): # indexed by trigger self._trigger2coros = _py_compat.insertion_ordered_dict() - # A dictionary mapping coroutines to the trigger they are waiting for - self._coro2trigger = _py_compat.insertion_ordered_dict() - # Our main state self._mode = Scheduler._MODE_NORMAL @@ -285,7 +282,6 @@ def _check_termination(self): self._timer1.prime(self._test_completed) self._trigger2coros = _py_compat.insertion_ordered_dict() - self._coro2trigger = _py_compat.insertion_ordered_dict() self._terminate = False self._write_calls = _py_compat.insertion_ordered_dict() self._writes_pending.clear() @@ -470,12 +466,9 @@ def unschedule(self, coro): """Unschedule a coroutine. Unprime any pending triggers""" # Unprime the trigger this coroutine is waiting on - try: - trigger = self._coro2trigger.pop(coro) - except KeyError: - # coroutine probably finished - pass - else: + trigger = coro._trigger + if trigger is not None: + coro._trigger = None if coro in self._trigger2coros.setdefault(trigger, []): self._trigger2coros[trigger].remove(coro) if not self._trigger2coros[trigger]: @@ -523,7 +516,7 @@ def _schedule_write(self, handle, write_func, *args): def _resume_coro_upon(self, coro, trigger): """Schedule `coro` to be resumed when `trigger` fires.""" - self._coro2trigger[coro] = trigger + coro._trigger = trigger trigger_coros = self._trigger2coros.setdefault(trigger, []) if coro is self._write_coro_inst: @@ -765,6 +758,7 @@ def schedule(self, coroutine, trigger=None): coro_completed = False try: + coroutine._trigger = None result = coroutine._advance(send_outcome) if _debug: self.log.debug("Coroutine %s yielded %s (mode %d)" % From 1672d406f8439bc1342931400fde8cdf51c7ce74 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Fri, 24 Apr 2020 12:52:29 -0700 Subject: [PATCH 2442/2656] Add context manager for currently scheduled task. --- cocotb/scheduler.py | 120 +++++++++++++++++++++++++------------------- 1 file changed, 67 insertions(+), 53 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 7cb88fec..bb40bb53 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -33,6 +33,7 @@ also have pending writes we have to schedule the ReadWrite callback before the ReadOnly (and this is invalid, at least in Modelsim). """ +from contextlib import contextmanager import os import sys import logging @@ -243,6 +244,8 @@ def __init__(self): self._test = None self._main_thread = threading.current_thread() + self._current_task = None + self._is_reacting = False self._write_coro_inst = None @@ -741,6 +744,16 @@ def _trigger_from_any(self, result) -> Trigger: .format(type(result), result) ) + @contextmanager + def _task_context(self, task): + """Context manager for the currently running task.""" + old_task = self._current_task + self._current_task = task + try: + yield + finally: + self._current_task = old_task + def schedule(self, coroutine, trigger=None): """Schedule a coroutine by calling the send method. @@ -749,67 +762,68 @@ def schedule(self, coroutine, trigger=None): trigger (cocotb.triggers.Trigger): The trigger that caused this coroutine to be scheduled. """ - if trigger is None: - send_outcome = outcomes.Value(None) - else: - send_outcome = trigger._outcome - if _debug: - self.log.debug("Scheduling with {}".format(send_outcome)) - - coro_completed = False - try: - coroutine._trigger = None - result = coroutine._advance(send_outcome) - if _debug: - self.log.debug("Coroutine %s yielded %s (mode %d)" % - (coroutine.__qualname__, str(result), self._mode)) - - except cocotb.decorators.CoroutineComplete: + with self._task_context(coroutine): + if trigger is None: + send_outcome = outcomes.Value(None) + else: + send_outcome = trigger._outcome if _debug: - self.log.debug("Coroutine {} completed with {}".format( - coroutine, coroutine._outcome - )) - coro_completed = True + self.log.debug("Scheduling with {}".format(send_outcome)) - # this can't go in the else above, as that causes unwanted exception - # chaining - if coro_completed: - self.unschedule(coroutine) + coro_completed = False + try: + coroutine._trigger = None + result = coroutine._advance(send_outcome) + if _debug: + self.log.debug("Coroutine %s yielded %s (mode %d)" % + (coroutine.__qualname__, str(result), self._mode)) - # Don't handle the result if we're shutting down - if self._terminate: - return + except cocotb.decorators.CoroutineComplete: + if _debug: + self.log.debug("Coroutine {} completed with {}".format( + coroutine, coroutine._outcome + )) + coro_completed = True + + # this can't go in the else above, as that causes unwanted exception + # chaining + if coro_completed: + self.unschedule(coroutine) + + # Don't handle the result if we're shutting down + if self._terminate: + return - if not coro_completed: - try: - result = self._trigger_from_any(result) - except TypeError as exc: - # restart this coroutine with an exception object telling it that - # it wasn't allowed to yield that - result = NullTrigger(outcome=outcomes.Error(exc)) + if not coro_completed: + try: + result = self._trigger_from_any(result) + except TypeError as exc: + # restart this coroutine with an exception object telling it that + # it wasn't allowed to yield that + result = NullTrigger(outcome=outcomes.Error(exc)) - self._resume_coro_upon(coroutine, result) + self._resume_coro_upon(coroutine, result) - # We do not return from here until pending threads have completed, but only - # from the main thread, this seems like it could be problematic in cases - # where a sim might change what this thread is. + # We do not return from here until pending threads have completed, but only + # from the main thread, this seems like it could be problematic in cases + # where a sim might change what this thread is. - if self._main_thread is threading.current_thread(): + if self._main_thread is threading.current_thread(): - for ext in self._pending_threads: - ext.thread_start() - if _debug: - self.log.debug("Blocking from %s on %s" % (threading.current_thread(), ext.thread)) - state = ext.thread_wait() - if _debug: - self.log.debug("Back from wait on self %s with newstate %d" % (threading.current_thread(), state)) - if state == external_state.EXITED: - self._pending_threads.remove(ext) - self._pending_events.append(ext.event) - - # Handle any newly queued coroutines that need to be scheduled - while self._pending_coros: - self.add(self._pending_coros.pop(0)) + for ext in self._pending_threads: + ext.thread_start() + if _debug: + self.log.debug("Blocking from %s on %s" % (threading.current_thread(), ext.thread)) + state = ext.thread_wait() + if _debug: + self.log.debug("Back from wait on self %s with newstate %d" % (threading.current_thread(), state)) + if state == external_state.EXITED: + self._pending_threads.remove(ext) + self._pending_events.append(ext.event) + + # Handle any newly queued coroutines that need to be scheduled + while self._pending_coros: + self.add(self._pending_coros.pop(0)) def finish_test(self, exc): self._test.abort(exc) From 3c35805c128d9ff6e1d987d4d1ace6f55734df6e Mon Sep 17 00:00:00 2001 From: Marlon James Date: Thu, 9 Apr 2020 14:37:36 -0700 Subject: [PATCH 2443/2656] Improve __repr__ for RunningTask objects. Displays task name, status, and current coroutine. If the task is pending on a trigger, displays the trigger. If the task is finished, displays the outcome. --- cocotb/decorators.py | 62 +++++++++++++++++++++++++++++++++++++++----- cocotb/utils.py | 37 ++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 6 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 63202909..881cc427 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -35,7 +35,7 @@ import cocotb from cocotb.log import SimLog from cocotb.result import ReturnValue -from cocotb.utils import get_sim_time, lazy_property, remove_traceback_frames +from cocotb.utils import get_sim_time, lazy_property, remove_traceback_frames, extract_coro_stack from cocotb import outcomes # Sadly the Python standard logging module is very slow so it's better not to @@ -87,6 +87,8 @@ class RunningTask: triggers to fire. """ + _id_count = 0 # used by the scheduler for debug + def __init__(self, inst): if inspect.iscoroutine(inst): @@ -107,13 +109,16 @@ def __init__(self, inst): raise TypeError( "%s isn't a valid coroutine! Did you forget to use the yield keyword?" % inst) self._coro = inst - self.__name__ = inst.__name__ - self.__qualname__ = inst.__qualname__ self._started = False self._callbacks = [] self._outcome = None self._trigger = None + self._task_id = self._id_count + RunningTask._id_count += 1 + self.__name__ = "Task %d" % self._task_id + self.__qualname__ = self.__name__ + @lazy_property def log(self): # Creating a logger is expensive, only do it if we actually plan to @@ -134,7 +139,48 @@ def __iter__(self): return self def __str__(self): - return str(self.__qualname__) + return "<{}>".format(self.__name__) + + def _get_coro_stack(self): + """Get the coroutine callstack of this Task.""" + coro_stack = extract_coro_stack(self._coro) + + # Remove Trigger.__await__() from the stack, as it's not really useful + if self._natively_awaitable and len(coro_stack): + if coro_stack[-1].name == '__await__': + coro_stack.pop() + + return coro_stack + + def __repr__(self): + coro_stack = self._get_coro_stack() + + if cocotb.scheduler._current_task is self: + fmt = "<{name} running coro={coro}()>" + elif self._finished: + fmt = "<{name} finished coro={coro}() outcome={outcome}>" + elif self._trigger is not None: + fmt = "<{name} pending coro={coro}() trigger={trigger}>" + elif not self._started: + fmt = "<{name} created coro={coro}()>" + else: + fmt = "<{name} adding coro={coro}()>" + + try: + coro_name = coro_stack[-1].name + # coro_stack may be empty if: + # - exhausted generator + # - finished coroutine + except IndexError: + coro_name = self._coro.__name__ + + repr_string = fmt.format( + name=self.__name__, + coro=coro_name, + trigger=self._trigger, + outcome=self._outcome + ) + return repr_string def _advance(self, outcome): """Advance to the next yield in this coroutine. @@ -243,7 +289,7 @@ def handle(self, record): def __init__(self, inst, parent): self.error_messages = [] RunningCoroutine.__init__(self, inst, parent) - self.log = SimLog("cocotb.test.%s" % self.__qualname__, id(self)) + self.log = SimLog("cocotb.test.%s" % inst.__qualname__, id(self)) self.started = False self.start_time = 0 self.start_sim_time = 0 @@ -251,11 +297,15 @@ def __init__(self, inst, parent): self.expect_error = parent.expect_error self.skip = parent.skip self.stage = parent.stage - self._id = parent._id + self.__name__ = "Test %s" % inst.__name__ + self.__qualname__ = "Test %s" % inst.__qualname__ # make sure not to create a circular reference here self.handler = RunningTest.ErrorLogHandler(self.error_messages.append) + def __str__(self): + return "<{}>".format(self.__name__) + def _advance(self, outcome): if not self.started: self.log.info("Starting test: \"%s\"\nDescription: %s" % diff --git a/cocotb/utils.py b/cocotb/utils.py index 371e7547..45d7e0fe 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -31,6 +31,7 @@ import math import os import sys +import traceback import weakref import functools import warnings @@ -580,3 +581,39 @@ def remove_traceback_frames(tb_or_exc, frame_names): assert tb.tb_frame.f_code.co_name == frame_name tb = tb.tb_next return tb + + +def walk_coro_stack(coro): + """Walk down the coroutine stack, starting at *coro*. + + Supports coroutines and generators. + """ + while coro is not None: + try: + f = getattr(coro, 'cr_frame') + coro = coro.cr_await + except AttributeError: + try: + f = getattr(coro, 'gi_frame') + coro = coro.gi_yieldfrom + except AttributeError: + f = None + coro = None + if f is not None: + yield (f, f.f_lineno) + + +def extract_coro_stack(coro, limit=None): + """Create a list of pre-processed entries from the coroutine stack. + + This is based on :func:`traceback.extract_tb`. + + If *limit* is omitted or ``None``, all entries are extracted. + The list is a :class:`traceback.StackSummary` object, and + each entry in the list is a :class:`traceback.FrameSummary` object + containing attributes ``filename``, ``lineno``, ``name``, and ``line`` + representing the information that is usually printed for a stack + trace. The line is a string with leading and trailing + whitespace stripped; if the source is not available it is ``None``. + """ + return traceback.StackSummary.extract(walk_coro_stack(coro), limit=limit) From cab9e754d104e9611b803a3524acbd87c1446dfd Mon Sep 17 00:00:00 2001 From: Marlon James Date: Tue, 19 May 2020 09:28:52 -0700 Subject: [PATCH 2444/2656] Simplify __repr__ for Join trigger. Replaces verbose RunningTask.__repr__ with RunningTask.__str__. For example, Join(>) becomes Join() --- cocotb/triggers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 064d4805..35710517 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -619,7 +619,7 @@ def prime(self, callback): super(Join, self).prime(callback) def __repr__(self): - return "{}({!r})".format(type(self).__qualname__, self._coroutine) + return "{}({!s})".format(type(self).__qualname__, self._coroutine) class Waitable(Awaitable): From 45cfeec9a0a887f4366d1b84a9fb7abf117a8b52 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Thu, 9 Apr 2020 14:49:21 -0700 Subject: [PATCH 2445/2656] Improve debugging for First and Combine triggers. The new _InternalEvent class is used by First and Combine triggers. It provides similar utility to Event, but only one coroutine can wait on it, and it has a pass-through __repr__ that shows the __repr__ output from the First/Combine parent trigger for a cleaner debugging experience. Add test_internalevent. --- cocotb/triggers.py | 53 +++++++++++++++-- .../test_synchronization_primitives.py | 58 ++++++++++++++++++- 2 files changed, 106 insertions(+), 5 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 35710517..8632e081 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -429,6 +429,51 @@ def __repr__(self): return fmt.format(type(self).__qualname__, self.name, _pointer_str(self)) +class _InternalEvent(PythonTrigger): + """Event used internally for triggers that need cross-coroutine synchronization. + + This Event can only be waited on once, by a single coroutine. + + Provides transparent __repr__ pass-through to the Trigger using this event, + providing a better debugging experience. + """ + def __init__(self, parent): + PythonTrigger.__init__(self) + self.parent = parent + self._callback = None + self.fired = False + self.data = None + + def prime(self, callback): + if self._callback is not None: + raise RuntimeError("This Trigger may only be awaited once") + self._callback = callback + Trigger.prime(self, callback) + if self.fired: + self._callback(self) + + def set(self, data=None): + """Wake up coroutine blocked on this event.""" + self.fired = True + self.data = data + + if self._callback is not None: + self._callback(self) + + def is_set(self) -> bool: + """Return true if event has been set.""" + return self.fired + + def __await__(self): + if self.primed: + raise RuntimeError("Only one coroutine may await this Trigger") + # hand the trigger back to the scheduler trampoline + return (yield self) + + def __repr__(self): + return repr(self.parent) + + class _Lock(PythonTrigger): """Unique instance used by the Lock object. @@ -691,7 +736,7 @@ class Combine(_AggregateWaitable): async def _wait(self): waiters = [] - e = Event() + e = _InternalEvent(self) triggers = list(self.triggers) # start a parallel task for each trigger @@ -705,7 +750,7 @@ def on_done(ret, t=t): waiters.append(cocotb.fork(_wait_callback(t, on_done))) # wait for the last waiter to complete - await e.wait() + await e return self @@ -734,7 +779,7 @@ class First(_AggregateWaitable): async def _wait(self): waiters = [] - e = Event() + e = _InternalEvent(self) triggers = list(self.triggers) completed = [] # start a parallel task for each trigger @@ -745,7 +790,7 @@ def on_done(ret): waiters.append(cocotb.fork(_wait_callback(t, on_done))) # wait for a waiter to complete - await e.wait() + await e # kill all the other waiters # TODO: Should this kill the coroutines behind any Join triggers? diff --git a/tests/test_cases/test_cocotb/test_synchronization_primitives.py b/tests/test_cases/test_cocotb/test_synchronization_primitives.py index 79455e4f..f0d4aef3 100644 --- a/tests/test_cases/test_cocotb/test_synchronization_primitives.py +++ b/tests/test_cases/test_cocotb/test_synchronization_primitives.py @@ -5,7 +5,10 @@ Tests for synchronization primitives like Lock and Event """ import cocotb -from cocotb.triggers import Lock, Timer +from cocotb.triggers import Lock, Timer, _InternalEvent +from cocotb.utils import get_sim_time + +from common import assert_raises @cocotb.test() @@ -51,3 +54,56 @@ async def test_except_lock(dut): pass async with lock: pass + + +@cocotb.test() +async def test_internalevent(dut): + """Test _InternalEvent trigger.""" + e = _InternalEvent('test parent') + assert repr(e) == "'test parent'" + + async def set_internalevent(): + await Timer(1, units='ns') + e.set('data') + + # Test waiting more than once + cocotb.fork(set_internalevent()) + time_ns = get_sim_time(units='ns') + await e + assert e.is_set() + assert e.data == 'data' + assert get_sim_time(units='ns') == time_ns + 1 + # _InternalEvent can only be awaited once + with assert_raises(RuntimeError): + await e + + e = _InternalEvent(None) + assert repr(e) == 'None' + ran = False + + async def await_internalevent(): + nonlocal ran + await e + ran = True + + # Test multiple coroutines waiting + cocotb.fork(await_internalevent()) + assert not e.is_set() + assert not ran + # _InternalEvent can only be awaited by one coroutine + with assert_raises(RuntimeError): + await e + e.set() + await Timer(1) + assert e.is_set() + assert ran + + # Test waiting after set + e = _InternalEvent(None) + assert not e.is_set() + cocotb.fork(set_internalevent()) + await Timer(2, units='ns') + assert e.is_set() + time_ns = get_sim_time(units='ns') + await e + assert get_sim_time(units='ns') == time_ns From 6ed1c6aef1eba0525eed672938035d02862b0344 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Tue, 19 May 2020 11:06:14 -0700 Subject: [PATCH 2446/2656] Show Join for tasks in __repr__ for First/Combine. When First or Combine is used to wait on a task, show Join(task) rather than repr(task), since the Join trigger is what is actually being awaited. --- cocotb/triggers.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 8632e081..794f8fc7 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -709,7 +709,12 @@ def __repr__(self): # no _pointer_str here, since this is not a trigger, so identity # doesn't matter. return "{}({})".format( - type(self).__qualname__, ", ".join(repr(t) for t in self.triggers) + type(self).__qualname__, + ", ".join( + repr(Join(t)) if isinstance(t, cocotb.decorators.RunningTask) + else repr(t) + for t in self.triggers + ) ) From 80f5b449905eb018172b1d4a5fada9655a253e8e Mon Sep 17 00:00:00 2001 From: Marlon James Date: Tue, 12 May 2020 14:02:21 -0700 Subject: [PATCH 2447/2656] Add test_task_repr and cleanup test loggers. --- .../test_cases/test_cocotb/test_scheduler.py | 117 +++++++++++++++++- 1 file changed, 111 insertions(+), 6 deletions(-) diff --git a/tests/test_cases/test_cocotb/test_scheduler.py b/tests/test_cases/test_cocotb/test_scheduler.py index 49562419..44e21efe 100644 --- a/tests/test_cases/test_cocotb/test_scheduler.py +++ b/tests/test_cases/test_cocotb/test_scheduler.py @@ -8,9 +8,11 @@ * join * kill """ +import logging +import re import cocotb -from cocotb.triggers import Join, Timer, RisingEdge, Trigger, NullTrigger, Combine, Event, ReadOnly +from cocotb.triggers import Join, Timer, RisingEdge, Trigger, NullTrigger, Combine, Event, ReadOnly, First from cocotb.result import TestFailure from cocotb.clock import Clock from common import clock_gen @@ -62,6 +64,7 @@ def clock_two(dut): @cocotb.test(expect_fail=False) def test_coroutine_close_down(dut): + log = logging.getLogger("cocotb.test") clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) coro_one = cocotb.fork(clock_one(dut)) @@ -70,7 +73,7 @@ def test_coroutine_close_down(dut): yield Join(coro_one) yield Join(coro_two) - dut._log.info("Back from joins") + log.info("Back from joins") @cocotb.test() @@ -215,14 +218,14 @@ async def test_nulltrigger_reschedule(dut): The NullTrigger will be added to the end of the list of pending triggers. """ - + log = logging.getLogger("cocotb.test") last_fork = None @cocotb.coroutine # TODO: Remove once Combine accepts bare coroutines async def reschedule(n): nonlocal last_fork for i in range(4): - cocotb.log.info("Fork {}, iteration {}, last fork was {}".format(n, i, last_fork)) + log.info("Fork {}, iteration {}, last fork was {}".format(n, i, last_fork)) assert last_fork != n last_fork = n await NullTrigger() @@ -270,13 +273,14 @@ async def test_last_scheduled_write_wins(dut): """ Test that the last scheduled write for a signal handle is the value that is written. """ + log = logging.getLogger("cocotb.test") e = Event() dut.stream_in_data.setimmediatevalue(0) @cocotb.coroutine # TODO: Remove once Combine accepts bare coroutines async def first(): await Timer(1) - dut._log.info("scheduling stream_in_data <= 1") + log.info("scheduling stream_in_data <= 1") dut.stream_in_data <= 1 e.set() @@ -284,7 +288,7 @@ async def first(): async def second(): await Timer(1) await e.wait() - dut._log.info("scheduling stream_in_data <= 2") + log.info("scheduling stream_in_data <= 2") dut.stream_in_data <= 2 await Combine(first(), second()) @@ -300,3 +304,104 @@ async def second(): await ReadOnly() assert dut.array_7_downto_4.value == [10, 2, 3, 4] + + +@cocotb.test() +async def test_task_repr(dut): + """Test RunningTask.__repr__.""" + log = logging.getLogger("cocotb.test") + gen_e = Event('generator_coro_inner') + + def generator_coro_inner(): + gen_e.set() + yield Timer(1, units='ns') + raise ValueError("inner") + + @cocotb.coroutine + def generator_coro_outer(): + yield from generator_coro_inner() + + gen_task = generator_coro_outer() + + log.info(repr(gen_task)) + assert re.match(r"", repr(gen_task)) + + cocotb.fork(gen_task) + + await gen_e.wait() + + log.info(repr(gen_task)) + assert re.match(r">", repr(gen_task)) + + try: + await Join(gen_task) + except ValueError: + pass + + log.info(repr(gen_task)) + assert re.match(r"", repr(gen_task)) + + coro_e = Event('coroutine_inner') + + async def coroutine_forked(task): + log.info(repr(task)) + assert re.match(r"", repr(task)) + + @cocotb.coroutine + async def coroutine_wait(): + await Timer(1, units='ns') + + async def coroutine_inner(): + await coro_e.wait() + this_task = coro_e.data + # cr_await is None while the coroutine is running, so we can't get the stack... + log.info(repr(this_task)) + assert re.match(r"", repr(this_task)) + + cocotb.fork(coroutine_forked(this_task)) + await Combine(*(coroutine_wait() for _ in range(2))) + + return "Combine done" + + async def coroutine_middle(): + return await coroutine_inner() + + async def coroutine_outer(): + return await coroutine_middle() + + coro_task = cocotb.fork(coroutine_outer()) + + coro_e.set(coro_task) + + await NullTrigger() + + log.info(repr(coro_task)) + assert re.match( + r"\), Join\(\)\)>", + repr(coro_task) + ) + + await Timer(2, units='ns') + + log.info(repr(coro_task)) + assert re.match(r"\), \)>", + repr(coro_task) + ) + + async def coroutine_timer(): + await Timer(1, units='ns') + + coro_task = cocotb.fork(coroutine_timer()) + + # Trigger.__await__ should be popped from the coroutine stack + log.info(repr(coro_task)) + assert re.match(r">", repr(coro_task)) From 2fec5ceff0aa4879cb4fe9ec33214079a775f476 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Mon, 27 Jul 2020 11:49:15 -0700 Subject: [PATCH 2448/2656] Update scheduler debug log messages to show coroutine name. RunningTask.__qualname__ now returns the form '', so in order for debug messages to show the coroutine name we must get it from the coroutine object that is wrapped by RunningTask. --- cocotb/scheduler.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index bb40bb53..ab9d9a71 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -426,7 +426,7 @@ def _event_loop(self, trigger): continue if _debug: - debugstr = "\n\t".join([coro.__qualname__ for coro in scheduling]) + debugstr = "\n\t".join([coro._coro.__qualname__ for coro in scheduling]) if len(scheduling): debugstr = "\n\t" + debugstr self.log.debug("%d pending coroutines for event %s%s" % @@ -440,10 +440,10 @@ def _event_loop(self, trigger): # coroutine was killed by another coroutine waiting on the same trigger continue if _debug: - self.log.debug("Scheduling coroutine %s" % (coro.__qualname__)) + self.log.debug("Scheduling coroutine %s" % (coro._coro.__qualname__)) self.schedule(coro, trigger=trigger) if _debug: - self.log.debug("Scheduled coroutine %s" % (coro.__qualname__)) + self.log.debug("Scheduled coroutine %s" % (coro._coro.__qualname__)) # Schedule may have queued up some events so we'll burn through those while self._pending_events: @@ -667,7 +667,7 @@ def add(self, coroutine): ) if _debug: - self.log.debug("Adding new coroutine %s" % coroutine.__qualname__) + self.log.debug("Adding new coroutine %s" % coroutine._coro.__qualname__) self.schedule(coroutine) self._check_termination() @@ -693,14 +693,14 @@ def add_test(self, test_coro): def _trigger_from_started_coro(self, result: cocotb.decorators.RunningTask) -> Trigger: if _debug: self.log.debug("Joining to already running coroutine: %s" % - result.__qualname__) + result._coro.__qualname__) return result.join() def _trigger_from_unstarted_coro(self, result: cocotb.decorators.RunningTask) -> Trigger: self.queue(result) if _debug: self.log.debug("Scheduling nested coroutine: %s" % - result.__qualname__) + result._coro.__qualname__) return result.join() def _trigger_from_waitable(self, result: cocotb.triggers.Waitable) -> Trigger: @@ -776,7 +776,7 @@ def schedule(self, coroutine, trigger=None): result = coroutine._advance(send_outcome) if _debug: self.log.debug("Coroutine %s yielded %s (mode %d)" % - (coroutine.__qualname__, str(result), self._mode)) + (coroutine._coro.__qualname__, str(result), self._mode)) except cocotb.decorators.CoroutineComplete: if _debug: From 77d4607d5904332fc240fc805c0c28308ea3fd0a Mon Sep 17 00:00:00 2001 From: Marlon James Date: Mon, 3 Aug 2020 09:30:06 -0700 Subject: [PATCH 2449/2656] Define signature for Triggers. Use __signature__ attribute on ParametrizedSingleton to get the correct signature. This fixes the output from help(). For example: class Edge(_EdgeBase) | Edge(*args, **kwargs) becomes class Edge(_EdgeBase) | Edge(signal) --- cocotb/utils.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cocotb/utils.py b/cocotb/utils.py index 45d7e0fe..06ec5483 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -28,6 +28,7 @@ """Collection of handy functions.""" import ctypes +import inspect import math import os import sys @@ -459,6 +460,10 @@ def __call__(cls, *args, **kwargs): cls.__instances[key] = self return self + @property + def __signature__(cls): + return inspect.signature(cls.__singleton_key__) + def reject_remaining_kwargs(name, kwargs): """ From d566d19a8a400e99ab5a298844e9fa7b86940a68 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Wed, 5 Aug 2020 09:19:11 -0700 Subject: [PATCH 2450/2656] Cleanup documentation for Trigger __init__ parameters. Sphinx autodoc doesn't use __signature__ on class definitions correctly. --- documentation/source/triggers.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/documentation/source/triggers.rst b/documentation/source/triggers.rst index 06d87bf0..fccd5cf0 100644 --- a/documentation/source/triggers.rst +++ b/documentation/source/triggers.rst @@ -22,11 +22,11 @@ Simulator Triggers Signals ------- -.. autoclass:: cocotb.triggers.Edge +.. autoclass:: cocotb.triggers.Edge(signal) -.. autoclass:: cocotb.triggers.RisingEdge +.. autoclass:: cocotb.triggers.RisingEdge(signal) -.. autoclass:: cocotb.triggers.FallingEdge +.. autoclass:: cocotb.triggers.FallingEdge(signal) .. autoclass:: cocotb.triggers.ClockCycles @@ -36,11 +36,11 @@ Timing .. autoclass:: cocotb.triggers.Timer -.. autoclass:: cocotb.triggers.ReadOnly +.. autoclass:: cocotb.triggers.ReadOnly() -.. autoclass:: cocotb.triggers.ReadWrite +.. autoclass:: cocotb.triggers.ReadWrite() -.. autoclass:: cocotb.triggers.NextTimeStep +.. autoclass:: cocotb.triggers.NextTimeStep() Python Triggers @@ -50,7 +50,7 @@ Python Triggers .. autoclass:: cocotb.triggers.First -.. autoclass:: cocotb.triggers.Join +.. autoclass:: cocotb.triggers.Join(coroutine) :members: retval From 9d98a4b44f7bc5aa953fc074dbbf7918fc59a480 Mon Sep 17 00:00:00 2001 From: Tim Pambor Date: Sat, 16 May 2020 11:24:53 +0200 Subject: [PATCH 2451/2656] Support rpath-like "probing privatePath" on Windows by converting the libraries to SxS assemblies --- cocotb/__init__.py | 1 - cocotb/_os_compat.py | 19 --- cocotb/share/makefiles/Makefile.inc | 6 - cocotb_build_libs.py | 182 ++++++++++++++++++++++++---- 4 files changed, 161 insertions(+), 47 deletions(-) delete mode 100644 cocotb/_os_compat.py diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 7236910c..dc0595e8 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -39,7 +39,6 @@ import warnings from typing import Dict, List, Union -import cocotb._os_compat # must appear first, before the first import of cocotb.simulator import cocotb.handle import cocotb.log from cocotb.scheduler import Scheduler diff --git a/cocotb/_os_compat.py b/cocotb/_os_compat.py deleted file mode 100644 index c7f55e8a..00000000 --- a/cocotb/_os_compat.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -""" -OS-specific hacks -""" -import os -import sys - -# This is necessary for Windows, which does not support RPATH, causing it -# to not be able to locate the simulator module, which is imported -# unconditionally. - -extra_dll_dir = os.path.join(os.path.dirname(__file__), 'libs') - -if sys.platform == 'win32' and os.path.isdir(extra_dll_dir): - # TODO[gh-1829]: investigate making os.add_dll_directory work here on Python 3.8 - os.environ.setdefault('PATH', '') - os.environ['PATH'] += os.pathsep + extra_dll_dir diff --git a/cocotb/share/makefiles/Makefile.inc b/cocotb/share/makefiles/Makefile.inc index b2b43581..87e3b00a 100644 --- a/cocotb/share/makefiles/Makefile.inc +++ b/cocotb/share/makefiles/Makefile.inc @@ -79,12 +79,6 @@ endif # Set PYTHONHOME to properly populate sys.path in embedded python interpreter export PYTHONHOME := $(shell $(PYTHON_BIN) -c 'from distutils.sysconfig import get_config_var; print(get_config_var("prefix"))') -ifeq ($(OS),Msys) - # On Windows there is no support for @rpath/LD_LIBRARY_PATH so we need to set PATH to find cocotb libraries - # https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order?redirectedfrom=MSDN#standard-search-order-for-desktop-applications - export PATH := $(LIB_DIR):$(PATH) -endif - ifeq ($(OS),Msys) to_tcl_path = $(shell cygpath -m $(1) ) else diff --git a/cocotb_build_libs.py b/cocotb_build_libs.py index 4903c65d..833a8444 100755 --- a/cocotb_build_libs.py +++ b/cocotb_build_libs.py @@ -8,17 +8,113 @@ import logging import distutils import subprocess +import textwrap from setuptools import Extension from distutils.spawn import find_executable from setuptools.command.build_ext import build_ext as _build_ext from distutils.file_util import copy_file +from typing import List logger = logging.getLogger(__name__) cocotb_share_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "cocotb", "share")) +def create_sxs_assembly_manifest(name: str, filename: str, libraries: List[str]) -> str: + """ + Create side-by-side (sxs) assembly manifest + + It contains dependencies to other assemblies (in our case the assemblies are equal to the other libraries). + For more details see: + - https://docs.microsoft.com/en-us/windows/win32/sbscs/assembly-manifests + - https://docs.microsoft.com/en-us/windows/win32/sbscs/using-side-by-side-assemblies + + Args: + name: The name of the assembly for which the manifest is generated, e.g. ``libcocotbutils``. + filename: The filename of the library, e.g. ``libcocotbutils.dll``. + libraries: A list of names of dependent manifests, e.g. ``["libgpilog"]``. + """ + + architecture = "amd64" if sys.maxsize > 2**32 else "x86" + dependencies = [] + + for lib in libraries: + dependencies.append(textwrap.dedent('''\ + + + + + + ''') % (lib, architecture)) + + manifest_body = textwrap.dedent('''\ + + + + + %s + + ''') % (name, architecture, filename, textwrap.indent("".join(dependencies), ' ').strip()) + + return manifest_body + + +def create_sxs_appconfig(filename): + """ + Create side-by-side (sxs) application configuration file. + + The application configuration specifies additional search paths for manifests. + For more details see: https://docs.microsoft.com/en-us/windows/win32/sbscs/application-configuration-files + """ + + config_body = textwrap.dedent('''\ + + + + + + + + + ''') + + dirpath = os.path.dirname(filename) + os.makedirs(dirpath, exist_ok=True) + + with open(filename + ".2.config", "w", encoding='utf-8') as f: + f.write(config_body) + + +def create_rc_file(name, filename, libraries): + """ + Creates windows resource definition script to embed the side-by-side assembly manifest into the libraries. + + For more details see: https://docs.microsoft.com/en-us/windows/win32/menurc/about-resource-files + """ + + manifest = create_sxs_assembly_manifest(name, filename, libraries) + + # Escape double quotes and put every line between double quotes for embedding into rc file + manifest = manifest.replace('"', '""') + manifest = '\n'.join(['"%s\\r\\n"' % x for x in manifest.splitlines()]) + + rc_body = textwrap.dedent('''\ + #pragma code_page(65001) // UTF-8 + #include + + LANGUAGE 0x00, 0x00 + + ISOLATIONAWARE_MANIFEST_RESOURCE_ID RT_MANIFEST + BEGIN + %s + END + ''') % manifest + + with open(name + ".rc", "w", encoding='utf-8') as f: + f.write(rc_body) + + def _get_lib_ext_name(): """ Get name of default library file extension on given OS. """ @@ -36,6 +132,18 @@ def run(self): def_dir = os.path.join(cocotb_share_dir, "def") self._gen_import_libs(def_dir) + if os.name == "nt": + create_sxs_appconfig(self.get_ext_fullpath(os.path.join("cocotb", "simulator"))) + + ext_names = {os.path.split(ext.name)[-1] for ext in self.extensions} + for ext in self.extensions: + fullname = self.get_ext_fullname(ext.name) + filename = self.get_ext_filename(fullname) + name = os.path.split(fullname)[-1] + filename = os.path.split(filename)[-1] + libraries = {"lib" + lib for lib in ext.libraries}.intersection(ext_names) + create_rc_file(name, filename, libraries) + super().run() # Needed for Windows to not assume python module (generate interface in def file) @@ -184,11 +292,16 @@ def _get_common_lib_ext(include_dir, share_lib_dir): # # libcocotbutils # + libcocotbutils_sources = [ + os.path.join(share_lib_dir, "utils", "cocotb_utils.cpp") + ] + if os.name == "nt": + libcocotbutils_sources += ["libcocotbutils.rc"] libcocotbutils = Extension( os.path.join("cocotb", "libs", "libcocotbutils"), include_dirs=[include_dir], libraries=["gpilog"], - sources=[os.path.join(share_lib_dir, "utils", "cocotb_utils.cpp")], + sources=libcocotbutils_sources, extra_link_args=_extra_link_args(lib_name="libcocotbutils", rpaths=["$ORIGIN"]), extra_compile_args=_extra_cxx_compile_args, ) @@ -200,11 +313,16 @@ def _get_common_lib_ext(include_dir, share_lib_dir): if sys.platform == "darwin": python_lib_dirs = [sysconfig.get_config_var("LIBDIR")] + libgpilog_sources = [ + os.path.join(share_lib_dir, "gpi_log", "gpi_logging.cpp") + ] + if os.name == "nt": + libgpilog_sources += ["libgpilog.rc"] libgpilog = Extension( os.path.join("cocotb", "libs", "libgpilog"), include_dirs=[include_dir], library_dirs=python_lib_dirs, - sources=[os.path.join(share_lib_dir, "gpi_log", "gpi_logging.cpp")], + sources=libgpilog_sources, extra_link_args=_extra_link_args(lib_name="libgpilog", rpaths=["$ORIGIN"]), extra_compile_args=_extra_cxx_compile_args, ) @@ -212,13 +330,18 @@ def _get_common_lib_ext(include_dir, share_lib_dir): # # libcocotb # + libcocotb_sources = [ + os.path.join(share_lib_dir, "embed", "gpi_embed.cpp") + ] + if os.name == "nt": + libcocotb_sources += ["libcocotb.rc"] libcocotb = Extension( os.path.join("cocotb", "libs", "libcocotb"), define_macros=[("PYTHON_SO_LIB", _get_python_lib())], include_dirs=[include_dir], libraries=[_get_python_lib_link(), "gpilog", "cocotbutils"], library_dirs=python_lib_dirs, - sources=[os.path.join(share_lib_dir, "embed", "gpi_embed.cpp")], + sources=libcocotb_sources, extra_link_args=_extra_link_args(lib_name="libcocotb", rpaths=["$ORIGIN"] + python_lib_dirs), extra_compile_args=_extra_cxx_compile_args, ) @@ -226,15 +349,18 @@ def _get_common_lib_ext(include_dir, share_lib_dir): # # libgpi # + libgpi_sources=[ + os.path.join(share_lib_dir, "gpi", "GpiCbHdl.cpp"), + os.path.join(share_lib_dir, "gpi", "GpiCommon.cpp"), + ] + if os.name == "nt": + libgpi_sources += ["libgpi.rc"] libgpi = Extension( os.path.join("cocotb", "libs", "libgpi"), define_macros=[("LIB_EXT", _get_lib_ext_name()), ("SINGLETON_HANDLES", "")], include_dirs=[include_dir], libraries=["cocotbutils", "gpilog", "cocotb", "stdc++"], - sources=[ - os.path.join(share_lib_dir, "gpi", "GpiCbHdl.cpp"), - os.path.join(share_lib_dir, "gpi", "GpiCommon.cpp"), - ], + sources=libgpi_sources, extra_link_args=_extra_link_args(lib_name="libgpi", rpaths=["$ORIGIN"]), extra_compile_args=_extra_cxx_compile_args, ) @@ -242,12 +368,17 @@ def _get_common_lib_ext(include_dir, share_lib_dir): # # simulator # + simulator_sources=[ + os.path.join(share_lib_dir, "simulator", "simulatormodule.cpp"), + ] + if os.name == "nt": + simulator_sources += ["simulator.rc"] libsim = Extension( os.path.join("cocotb", "simulator"), include_dirs=[include_dir], libraries=["cocotbutils", "gpilog", "gpi"], library_dirs=python_lib_dirs, - sources=[os.path.join(share_lib_dir, "simulator", "simulatormodule.cpp")], + sources=simulator_sources, extra_compile_args=_extra_cxx_compile_args, extra_link_args=_extra_link_args(rpaths=["$ORIGIN/libs"]), ) @@ -262,16 +393,19 @@ def _get_vpi_lib_ext( include_dir, share_lib_dir, sim_define, extra_lib=[], extra_lib_dir=[] ): lib_name = "libcocotbvpi_" + sim_define.lower() + libcocotbvpi_sources = [ + os.path.join(share_lib_dir, "vpi", "VpiImpl.cpp"), + os.path.join(share_lib_dir, "vpi", "VpiCbHdl.cpp"), + ] + if os.name == "nt": + libcocotbvpi_sources += [lib_name + ".rc"] libcocotbvpi = Extension( os.path.join("cocotb", "libs", lib_name), define_macros=[("VPI_CHECKING", "1")] + [(sim_define, "")], include_dirs=[include_dir], libraries=["gpi", "gpilog"] + extra_lib, library_dirs=extra_lib_dir, - sources=[ - os.path.join(share_lib_dir, "vpi", "VpiImpl.cpp"), - os.path.join(share_lib_dir, "vpi", "VpiCbHdl.cpp"), - ], + sources=libcocotbvpi_sources, extra_link_args=_extra_link_args(lib_name=lib_name, rpaths=["$ORIGIN"]), extra_compile_args=_extra_cxx_compile_args, ) @@ -283,16 +417,19 @@ def _get_vhpi_lib_ext( include_dir, share_lib_dir, sim_define, extra_lib=[], extra_lib_dir=[] ): lib_name = "libcocotbvhpi_" + sim_define.lower() + libcocotbvhpi_sources = [ + os.path.join(share_lib_dir, "vhpi", "VhpiImpl.cpp"), + os.path.join(share_lib_dir, "vhpi", "VhpiCbHdl.cpp"), + ] + if os.name == "nt": + libcocotbvhpi_sources += [lib_name + ".rc"] libcocotbvhpi = Extension( os.path.join("cocotb", "libs", lib_name), include_dirs=[include_dir], define_macros=[("VHPI_CHECKING", 1)] + [(sim_define, "")], libraries=["gpi", "gpilog", "stdc++"] + extra_lib, library_dirs=extra_lib_dir, - sources=[ - os.path.join(share_lib_dir, "vhpi", "VhpiImpl.cpp"), - os.path.join(share_lib_dir, "vhpi", "VhpiCbHdl.cpp"), - ], + sources=libcocotbvhpi_sources, extra_link_args=_extra_link_args(lib_name=lib_name, rpaths=["$ORIGIN"]), extra_compile_args=_extra_cxx_compile_args, ) @@ -366,16 +503,19 @@ def get_ext(): mti_path = os.path.join(modelsim_include_dir, "mti.h") if os.path.isfile(mti_path): lib_name = "libcocotbfli_modelsim" + fli_sources = [ + os.path.join(share_lib_dir, "fli", "FliImpl.cpp"), + os.path.join(share_lib_dir, "fli", "FliCbHdl.cpp"), + os.path.join(share_lib_dir, "fli", "FliObjHdl.cpp"), + ] + if os.name == "nt": + fli_sources += [lib_name + ".rc"] fli_ext = Extension( os.path.join("cocotb", "libs", lib_name), include_dirs=[include_dir, modelsim_include_dir], libraries=["gpi", "gpilog", "stdc++"] + modelsim_extra_lib, library_dirs=modelsim_extra_lib_path, - sources=[ - os.path.join(share_lib_dir, "fli", "FliImpl.cpp"), - os.path.join(share_lib_dir, "fli", "FliCbHdl.cpp"), - os.path.join(share_lib_dir, "fli", "FliObjHdl.cpp"), - ], + sources=fli_sources, extra_link_args=_extra_link_args(lib_name=lib_name, rpaths=["$ORIGIN"]), extra_compile_args=_extra_cxx_compile_args, ) From 816b1a84fa972fbe0fe94baed6ebe56c9aa90058 Mon Sep 17 00:00:00 2001 From: Tim Pambor Date: Wed, 20 May 2020 08:33:23 +0200 Subject: [PATCH 2452/2656] Add visibility attributes for functions/classes/variables to be exported --- cocotb/share/include/cocotb_utils.h | 13 ++- cocotb/share/include/embed.h | 15 +++- cocotb/share/include/exports.h | 19 +++++ cocotb/share/include/gpi.h | 85 ++++++++++--------- cocotb/share/include/gpi_logging.h | 19 +++-- cocotb/share/lib/fli/FliImpl.h | 9 +- cocotb/share/lib/gpi/gpi_priv.h | 33 ++++--- .../share/lib/simulator/simulatormodule.cpp | 7 ++ cocotb/share/lib/vhpi/VhpiImpl.cpp | 4 +- cocotb/share/lib/vhpi/VhpiImpl.h | 7 ++ cocotb/share/lib/vpi/VpiImpl.cpp | 4 +- cocotb/share/lib/vpi/VpiImpl.h | 7 ++ cocotb_build_libs.py | 16 ++-- 13 files changed, 163 insertions(+), 75 deletions(-) create mode 100644 cocotb/share/include/exports.h diff --git a/cocotb/share/include/cocotb_utils.h b/cocotb/share/include/cocotb_utils.h index ea559018..799a0721 100644 --- a/cocotb/share/include/cocotb_utils.h +++ b/cocotb/share/include/cocotb_utils.h @@ -30,6 +30,13 @@ #ifndef COCOTB_UTILS_H_ #define COCOTB_UTILS_H_ +#include +#ifdef COCOTBUTILS_EXPORTS +#define COCOTBUTILS_EXPORT COCOTB_EXPORT +#else +#define COCOTBUTILS_EXPORT COCOTB_IMPORT +#endif + #include #ifdef __cplusplus @@ -39,10 +46,10 @@ extern "C" { #define xstr(a) str(a) #define str(a) #a -extern void* utils_dyn_open(const char* lib_name); -extern void* utils_dyn_sym(void *handle, const char* sym_name); +extern COCOTBUTILS_EXPORT void* utils_dyn_open(const char* lib_name); +extern COCOTBUTILS_EXPORT void* utils_dyn_sym(void *handle, const char* sym_name); -extern int is_python_context; +extern COCOTBUTILS_EXPORT int is_python_context; // to_python and to_simulator are implemented as macros instead of functions so // that the logs reference the user's lineno and filename diff --git a/cocotb/share/include/embed.h b/cocotb/share/include/embed.h index 677e2a6f..99916113 100644 --- a/cocotb/share/include/embed.h +++ b/cocotb/share/include/embed.h @@ -30,16 +30,23 @@ #ifndef COCOTB_EMBED_H_ #define COCOTB_EMBED_H_ +#include +#ifdef COCOTB_EMBED_EXPORTS +#define COCOTB_EMBED_EXPORT COCOTB_EXPORT +#else +#define COCOTB_EMBED_EXPORT COCOTB_IMPORT +#endif + #include #ifdef __cplusplus extern "C" { #endif -extern void embed_init_python(void); -extern void embed_sim_cleanup(void); -extern int embed_sim_init(int argc, char const* const* argv); -extern void embed_sim_event(gpi_event_t level, const char *msg); +extern COCOTB_EMBED_EXPORT void embed_init_python(void); +extern COCOTB_EMBED_EXPORT void embed_sim_cleanup(void); +extern COCOTB_EMBED_EXPORT int embed_sim_init(int argc, char const* const* argv); +extern COCOTB_EMBED_EXPORT void embed_sim_event(gpi_event_t level, const char *msg); #ifdef __cplusplus } diff --git a/cocotb/share/include/exports.h b/cocotb/share/include/exports.h new file mode 100644 index 00000000..2dcf70e4 --- /dev/null +++ b/cocotb/share/include/exports.h @@ -0,0 +1,19 @@ +// Copyright cocotb contributors +// Licensed under the Revised BSD License, see LICENSE for details. +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef COCOTB_EXPORTS_H_ +#define COCOTB_EXPORTS_H_ + +// Make cocotb work correctly when the default visibility is changed to hidden +// Changing the default visibility to hidden has the advantage of significantly reducing the code size, +// load times as well as letting the optimizer produce better code +#if defined(__linux__) || defined(__APPLE__) +#define COCOTB_EXPORT __attribute__ ((visibility ("default"))) +#define COCOTB_IMPORT +#else +#define COCOTB_EXPORT __declspec(dllexport) +#define COCOTB_IMPORT __declspec(dllimport) +#endif + +#endif diff --git a/cocotb/share/include/gpi.h b/cocotb/share/include/gpi.h index 35d4915b..d0ef1ddd 100644 --- a/cocotb/share/include/gpi.h +++ b/cocotb/share/include/gpi.h @@ -51,6 +51,13 @@ we have to create a process with the signal on the sensitivity list to imitate a */ +#include +#ifdef GPI_EXPORTS +#define GPI_EXPORT COCOTB_EXPORT +#else +#define GPI_EXPORT COCOTB_IMPORT +#endif + #include #include #include @@ -118,39 +125,39 @@ typedef enum gpi_event_e { * * Useful for checking if a simulator is running. */ -bool gpi_has_registered_impl(void); +GPI_EXPORT bool gpi_has_registered_impl(void); // Stop the simulator -void gpi_sim_end(void); +GPI_EXPORT void gpi_sim_end(void); // Cleanup GPI resources during sim shutdown -void gpi_cleanup(void); +GPI_EXPORT void gpi_cleanup(void); // Returns simulation time as two uints. Units are default sim units -void gpi_get_sim_time(uint32_t *high, uint32_t *low); -void gpi_get_sim_precision(int32_t *precision); +GPI_EXPORT void gpi_get_sim_time(uint32_t *high, uint32_t *low); +GPI_EXPORT void gpi_get_sim_precision(int32_t *precision); /** * Returns a string with the running simulator product information * * @return simulator product string */ -const char *gpi_get_simulator_product(void); +GPI_EXPORT const char *gpi_get_simulator_product(void); /** * Returns a string with the running simulator version * * @return simulator version string */ -const char *gpi_get_simulator_version(void); +GPI_EXPORT const char *gpi_get_simulator_version(void); // Functions for extracting a gpi_sim_hdl to an object // Returns a handle to the root simulation object, // Should be freed with gpi_free_handle -gpi_sim_hdl gpi_get_root_handle(const char *name); -gpi_sim_hdl gpi_get_handle_by_name(gpi_sim_hdl parent, const char *name); -gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, int32_t index); -void gpi_free_handle(gpi_sim_hdl gpi_hdl); +GPI_EXPORT gpi_sim_hdl gpi_get_root_handle(const char *name); +GPI_EXPORT gpi_sim_hdl gpi_get_handle_by_name(gpi_sim_hdl parent, const char *name); +GPI_EXPORT gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, int32_t index); +GPI_EXPORT void gpi_free_handle(gpi_sim_hdl gpi_hdl); // Types that can be passed to the iterator. // @@ -190,49 +197,49 @@ typedef enum gpi_set_action_e { // Unlike `vpi_iterate` the iterator handle may only be NULL if the `type` is // not supported, If no objects of the requested type are found, an empty // iterator is returned. -gpi_iterator_hdl gpi_iterate(gpi_sim_hdl base, gpi_iterator_sel_t type); +GPI_EXPORT gpi_iterator_hdl gpi_iterate(gpi_sim_hdl base, gpi_iterator_sel_t type); // Returns NULL when there are no more objects -gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator); +GPI_EXPORT gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator); // Returns the number of objects in the collection of the handle -int gpi_get_num_elems(gpi_sim_hdl gpi_sim_hdl); +GPI_EXPORT int gpi_get_num_elems(gpi_sim_hdl gpi_sim_hdl); // Returns the left side of the range constraint -int gpi_get_range_left(gpi_sim_hdl gpi_sim_hdl); +GPI_EXPORT int gpi_get_range_left(gpi_sim_hdl gpi_sim_hdl); // Returns the right side of the range constraint -int gpi_get_range_right(gpi_sim_hdl gpi_sim_hdl); +GPI_EXPORT int gpi_get_range_right(gpi_sim_hdl gpi_sim_hdl); // Functions for querying the properties of a handle // Caller responsible for freeing the returned string. // This is all slightly verbose but it saves having to enumerate various value types // We only care about a limited subset of values. -const char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl); -const char *gpi_get_signal_value_str(gpi_sim_hdl gpi_hdl); -double gpi_get_signal_value_real(gpi_sim_hdl gpi_hdl); -long gpi_get_signal_value_long(gpi_sim_hdl gpi_hdl); -const char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl); -const char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl); +GPI_EXPORT const char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl); +GPI_EXPORT const char *gpi_get_signal_value_str(gpi_sim_hdl gpi_hdl); +GPI_EXPORT double gpi_get_signal_value_real(gpi_sim_hdl gpi_hdl); +GPI_EXPORT long gpi_get_signal_value_long(gpi_sim_hdl gpi_hdl); +GPI_EXPORT const char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl); +GPI_EXPORT const char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl); // Returns one of the types defined above e.g. gpiMemory etc. -gpi_objtype_t gpi_get_object_type(gpi_sim_hdl gpi_hdl); +GPI_EXPORT gpi_objtype_t gpi_get_object_type(gpi_sim_hdl gpi_hdl); // Get information about the definition of a handle -const char* gpi_get_definition_name(gpi_sim_hdl gpi_hdl); -const char* gpi_get_definition_file(gpi_sim_hdl gpi_hdl); +GPI_EXPORT const char* gpi_get_definition_name(gpi_sim_hdl gpi_hdl); +GPI_EXPORT const char* gpi_get_definition_file(gpi_sim_hdl gpi_hdl); // Determine whether an object value is constant (parameters / generics etc) -int gpi_is_constant(gpi_sim_hdl gpi_hdl); +GPI_EXPORT int gpi_is_constant(gpi_sim_hdl gpi_hdl); // Determine whether an object is indexable -int gpi_is_indexable(gpi_sim_hdl gpi_hdl); +GPI_EXPORT int gpi_is_indexable(gpi_sim_hdl gpi_hdl); // Functions for setting the properties of a handle -void gpi_set_signal_value_real(gpi_sim_hdl gpi_hdl, double value, gpi_set_action_t action); -void gpi_set_signal_value_long(gpi_sim_hdl gpi_hdl, long value, gpi_set_action_t action); -void gpi_set_signal_value_binstr(gpi_sim_hdl gpi_hdl, const char *str, gpi_set_action_t action); // String of binary char(s) [1, 0, x, z] -void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str, gpi_set_action_t action); // String of ASCII char(s) +GPI_EXPORT void gpi_set_signal_value_real(gpi_sim_hdl gpi_hdl, double value, gpi_set_action_t action); +GPI_EXPORT void gpi_set_signal_value_long(gpi_sim_hdl gpi_hdl, long value, gpi_set_action_t action); +GPI_EXPORT void gpi_set_signal_value_binstr(gpi_sim_hdl gpi_hdl, const char *str, gpi_set_action_t action); // String of binary char(s) [1, 0, x, z] +GPI_EXPORT void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str, gpi_set_action_t action); // String of ASCII char(s) typedef enum gpi_edge { GPI_RISING = 1, @@ -240,23 +247,23 @@ typedef enum gpi_edge { } gpi_edge_e; // The callback registering functions -gpi_cb_hdl gpi_register_timed_callback (int (*gpi_function)(const void *), void *gpi_cb_data, uint64_t time_ps); -gpi_cb_hdl gpi_register_value_change_callback (int (*gpi_function)(const void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl, int edge); -gpi_cb_hdl gpi_register_readonly_callback (int (*gpi_function)(const void *), void *gpi_cb_data); -gpi_cb_hdl gpi_register_nexttime_callback (int (*gpi_function)(const void *), void *gpi_cb_data); -gpi_cb_hdl gpi_register_readwrite_callback (int (*gpi_function)(const void *), void *gpi_cb_data); +GPI_EXPORT gpi_cb_hdl gpi_register_timed_callback (int (*gpi_function)(const void *), void *gpi_cb_data, uint64_t time_ps); +GPI_EXPORT gpi_cb_hdl gpi_register_value_change_callback (int (*gpi_function)(const void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl, int edge); +GPI_EXPORT gpi_cb_hdl gpi_register_readonly_callback (int (*gpi_function)(const void *), void *gpi_cb_data); +GPI_EXPORT gpi_cb_hdl gpi_register_nexttime_callback (int (*gpi_function)(const void *), void *gpi_cb_data); +GPI_EXPORT gpi_cb_hdl gpi_register_readwrite_callback (int (*gpi_function)(const void *), void *gpi_cb_data); // Calling convention is that 0 = success and negative numbers a failure // For implementers of GPI the provided macro GPI_RET(x) is provided -void gpi_deregister_callback(gpi_cb_hdl gpi_hdl); +GPI_EXPORT void gpi_deregister_callback(gpi_cb_hdl gpi_hdl); // Because the internal structures may be different for different implementations // of GPI we provide a convenience function to extract the callback data -void *gpi_get_callback_data(gpi_cb_hdl gpi_hdl); +GPI_EXPORT void *gpi_get_callback_data(gpi_cb_hdl gpi_hdl); // Print out what implementations are registered. Python needs to be loaded for this, // Returns the number of libs -size_t gpi_print_registered_impl(void); +GPI_EXPORT size_t gpi_print_registered_impl(void); #define GPI_RET(_code) \ if (_code == 1) \ diff --git a/cocotb/share/include/gpi_logging.h b/cocotb/share/include/gpi_logging.h index 86b9cec3..e228331e 100644 --- a/cocotb/share/include/gpi_logging.h +++ b/cocotb/share/include/gpi_logging.h @@ -30,6 +30,13 @@ #ifndef COCOTB_GPI_LOGGING_H_ #define COCOTB_GPI_LOGGING_H_ +#include +#ifdef GPILOG_EXPORTS +#define GPILOG_EXPORT COCOTB_EXPORT +#else +#define GPILOG_EXPORT COCOTB_IMPORT +#endif + #ifdef __cplusplus # define EXTERN_C_START extern "C" { # define EXTERN_C_END } @@ -55,13 +62,13 @@ enum gpi_log_levels { #define LOG_ERROR(...) gpi_log("cocotb.gpi", GPIError, __FILE__, __func__, __LINE__, __VA_ARGS__); #define LOG_CRITICAL(...) gpi_log("cocotb.gpi", GPICritical, __FILE__, __func__, __LINE__, __VA_ARGS__); -void set_log_handler(void *handler); -void clear_log_handler(void); -void set_log_filter(void *filter); -void clear_log_filter(void); -void set_log_level(enum gpi_log_levels new_level); +GPILOG_EXPORT void set_log_handler(void *handler); +GPILOG_EXPORT void clear_log_handler(void); +GPILOG_EXPORT void set_log_filter(void *filter); +GPILOG_EXPORT void clear_log_filter(void); +GPILOG_EXPORT void set_log_level(enum gpi_log_levels new_level); -void gpi_log(const char *name, enum gpi_log_levels level, const char *pathname, const char *funcname, long lineno, const char *msg, ...); +GPILOG_EXPORT void gpi_log(const char *name, enum gpi_log_levels level, const char *pathname, const char *funcname, long lineno, const char *msg, ...); EXTERN_C_END diff --git a/cocotb/share/lib/fli/FliImpl.h b/cocotb/share/lib/fli/FliImpl.h index e7b213e0..6bd08073 100644 --- a/cocotb/share/lib/fli/FliImpl.h +++ b/cocotb/share/lib/fli/FliImpl.h @@ -28,6 +28,13 @@ #ifndef COCOTB_FLI_IMPL_H_ #define COCOTB_FLI_IMPL_H_ +#include +#ifdef COCOTBFLI_EXPORTS +#define COCOTBFLI_EXPORT COCOTB_EXPORT +#else +#define COCOTBFLI_EXPORT COCOTB_IMPORT +#endif + #include "../gpi/gpi_priv.h" #include "mti.h" @@ -35,7 +42,7 @@ #include extern "C" { -void cocotb_init(); +COCOTBFLI_EXPORT void cocotb_init(); void handle_fli_callback(void *data); } diff --git a/cocotb/share/lib/gpi/gpi_priv.h b/cocotb/share/lib/gpi/gpi_priv.h index da72d1b4..09e5b02a 100644 --- a/cocotb/share/lib/gpi/gpi_priv.h +++ b/cocotb/share/lib/gpi/gpi_priv.h @@ -29,6 +29,13 @@ #ifndef COCOTB_GPI_PRIV_H_ #define COCOTB_GPI_PRIV_H_ +#include +#ifdef GPI_EXPORTS +#define GPI_EXPORT COCOTB_EXPORT +#else +#define GPI_EXPORT COCOTB_IMPORT +#endif + #include #include #include @@ -50,7 +57,7 @@ class GpiIterator; class GpiCbHdl; /* Base GPI class others are derived from */ -class GpiHdl { +class GPI_EXPORT GpiHdl { public: GpiHdl(GpiImplInterface *impl, void *hdl = NULL) : m_impl(impl), m_obj_hdl(hdl) { } virtual ~GpiHdl() = default; @@ -78,7 +85,7 @@ class GpiHdl { // Subsequent operations to get children go through this handle. // GpiObjHdl::get_handle_by_name/get_handle_by_index are really factories // that construct an object derived from GpiSignalObjHdl or GpiObjHdl -class GpiObjHdl : public GpiHdl { +class GPI_EXPORT GpiObjHdl : public GpiHdl { public: GpiObjHdl( GpiImplInterface *impl, @@ -134,7 +141,7 @@ class GpiObjHdl : public GpiHdl { // // Identical to an object but adds additional methods for getting/setting the // value of the signal (which doesn't apply to non signal items in the hierarchy -class GpiSignalObjHdl : public GpiObjHdl { +class GPI_EXPORT GpiSignalObjHdl : public GpiObjHdl { public: using GpiObjHdl::GpiObjHdl; @@ -161,7 +168,7 @@ class GpiSignalObjHdl : public GpiObjHdl { /* GPI Callback handle */ // To set a callback it needs the signal to do this on, // vpiHandle/vhpiHandleT for instance. The -class GpiCbHdl : public GpiHdl { +class GPI_EXPORT GpiCbHdl : public GpiHdl { public: GpiCbHdl(GpiImplInterface *impl) : GpiHdl(impl) { } @@ -185,7 +192,7 @@ class GpiCbHdl : public GpiHdl { gpi_cb_state_e m_state = GPI_FREE; // GPI state of the callback through its cycle }; -class GpiValueCbHdl : public virtual GpiCbHdl { +class GPI_EXPORT GpiValueCbHdl : public virtual GpiCbHdl { public: GpiValueCbHdl(GpiImplInterface *impl, GpiSignalObjHdl *signal, int edge); int run_callback() override; @@ -195,7 +202,7 @@ class GpiValueCbHdl : public virtual GpiCbHdl { GpiSignalObjHdl *m_signal; }; -class GpiIterator : public GpiHdl { +class GPI_EXPORT GpiIterator : public GpiHdl { public: enum Status { NATIVE, // Fully resolved object was created @@ -225,7 +232,7 @@ class GpiIterator : public GpiHdl { }; -class GpiImplInterface { +class GPI_EXPORT GpiImplInterface { public: GpiImplInterface(const std::string& name) : m_name(name) { } const char *get_name_c(); @@ -264,13 +271,13 @@ class GpiImplInterface { }; /* Called from implementation layers back up the stack */ -int gpi_register_impl(GpiImplInterface *func_tbl); +GPI_EXPORT int gpi_register_impl(GpiImplInterface *func_tbl); -void gpi_embed_init(int argc, char const* const* argv); -void gpi_cleanup(); -void gpi_embed_end(); -void gpi_embed_event(gpi_event_t level, const char *msg); -void gpi_load_extra_libs(); +GPI_EXPORT void gpi_embed_init(int argc, char const* const* argv); +GPI_EXPORT void gpi_cleanup(); +GPI_EXPORT void gpi_embed_end(); +GPI_EXPORT void gpi_embed_event(gpi_event_t level, const char *msg); +GPI_EXPORT void gpi_load_extra_libs(); typedef void (*layer_entry_func)(); diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index f8eac0f6..02b14f9a 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -1083,6 +1083,13 @@ static struct PyModuleDef moduledef = { NULL }; +#if defined(__linux__) || defined(__APPLE__) +// Only required for Python < 3.9, default for 3.9+ (bpo-11410) +#pragma GCC visibility push(default) +PyMODINIT_FUNC PyInit_simulator(void); +#pragma GCC visibility pop +#endif + PyMODINIT_FUNC PyInit_simulator(void) { /* initialize the extension types */ diff --git a/cocotb/share/lib/vhpi/VhpiImpl.cpp b/cocotb/share/lib/vhpi/VhpiImpl.cpp index ca587b76..146fe146 100644 --- a/cocotb/share/lib/vhpi/VhpiImpl.cpp +++ b/cocotb/share/lib/vhpi/VhpiImpl.cpp @@ -977,7 +977,7 @@ static void register_embed() } // pre-defined VHPI registration table -void (*vhpi_startup_routines[])() = { +COCOTBVHPI_EXPORT void (*vhpi_startup_routines[])() = { register_embed, gpi_load_extra_libs, register_initial_callback, @@ -986,7 +986,7 @@ void (*vhpi_startup_routines[])() = { }; // For non-VHPI compliant applications that cannot find vhpi_startup_routines -void vhpi_startup_routines_bootstrap() { +COCOTBVHPI_EXPORT void vhpi_startup_routines_bootstrap() { void (*routine)(); int i; routine = vhpi_startup_routines[0]; diff --git a/cocotb/share/lib/vhpi/VhpiImpl.h b/cocotb/share/lib/vhpi/VhpiImpl.h index fc1d5dc9..5d0007e7 100644 --- a/cocotb/share/lib/vhpi/VhpiImpl.h +++ b/cocotb/share/lib/vhpi/VhpiImpl.h @@ -28,6 +28,13 @@ #ifndef COCOTB_VHPI_IMPL_H_ #define COCOTB_VHPI_IMPL_H_ +#include +#ifdef COCOTBVHPI_EXPORTS +#define COCOTBVHPI_EXPORT COCOTB_EXPORT +#else +#define COCOTBVHPI_EXPORT COCOTB_IMPORT +#endif + #include "../gpi/gpi_priv.h" #include #include diff --git a/cocotb/share/lib/vpi/VpiImpl.cpp b/cocotb/share/lib/vpi/VpiImpl.cpp index a6c2d8ba..ec9cc942 100644 --- a/cocotb/share/lib/vpi/VpiImpl.cpp +++ b/cocotb/share/lib/vpi/VpiImpl.cpp @@ -717,7 +717,7 @@ static void register_system_functions() } -void (*vlog_startup_routines[])() = { +COCOTBVPI_EXPORT void (*vlog_startup_routines[])() = { register_embed, gpi_load_extra_libs, register_system_functions, @@ -728,7 +728,7 @@ void (*vlog_startup_routines[])() = { // For non-VPI compliant applications that cannot find vlog_startup_routines symbol -void vlog_startup_routines_bootstrap() { +COCOTBVPI_EXPORT void vlog_startup_routines_bootstrap() { // call each routine in turn like VPI would for (auto it = &vlog_startup_routines[0]; *it != nullptr; it++) { auto routine = *it; diff --git a/cocotb/share/lib/vpi/VpiImpl.h b/cocotb/share/lib/vpi/VpiImpl.h index 887b966e..6c5c3265 100644 --- a/cocotb/share/lib/vpi/VpiImpl.h +++ b/cocotb/share/lib/vpi/VpiImpl.h @@ -28,6 +28,13 @@ #ifndef COCOTB_VPI_IMPL_H_ #define COCOTB_VPI_IMPL_H_ +#include +#ifdef COCOTBVPI_EXPORTS +#define COCOTBVPI_EXPORT COCOTB_EXPORT +#else +#define COCOTBVPI_EXPORT COCOTB_IMPORT +#endif + #include "../gpi/gpi_priv.h" #include #include diff --git a/cocotb_build_libs.py b/cocotb_build_libs.py index 833a8444..2bfaf4cd 100755 --- a/cocotb_build_libs.py +++ b/cocotb_build_libs.py @@ -241,6 +241,9 @@ def _extra_link_args(lib_name=None, rpaths=[]): if sys.platform == "darwin": rpath = rpath.replace("$ORIGIN", "@loader_path") args += ["-Wl,-rpath,%s" % rpath] + if os.name == "nt": + # Align behavior of gcc with msvc and export only symbols marked with __declspec(dllexport) + args += ["-Wl,--exclude-all-symbols"] return args @@ -275,7 +278,7 @@ def _get_python_lib(): # TODO [gh-1372]: make this work for MSVC which has a different flag syntax _base_warns = ["-Wall", "-Wextra", "-Wcast-qual", "-Wwrite-strings", "-Wconversion"] _ccx_warns = _base_warns + ["-Wnon-virtual-dtor", "-Woverloaded-virtual"] -_extra_cxx_compile_args = ["-std=c++11"] + _ccx_warns +_extra_cxx_compile_args = ["-std=c++11", "-fvisibility=hidden", "-fvisibility-inlines-hidden"] + _ccx_warns # Make PRI* format macros available with C++11 compiler but older libc, e.g. on RHEL6. _extra_cxx_compile_args += ["-D__STDC_FORMAT_MACROS"] @@ -299,6 +302,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): libcocotbutils_sources += ["libcocotbutils.rc"] libcocotbutils = Extension( os.path.join("cocotb", "libs", "libcocotbutils"), + define_macros=[("COCOTBUTILS_EXPORTS", "")], include_dirs=[include_dir], libraries=["gpilog"], sources=libcocotbutils_sources, @@ -320,6 +324,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): libgpilog_sources += ["libgpilog.rc"] libgpilog = Extension( os.path.join("cocotb", "libs", "libgpilog"), + define_macros=[("GPILOG_EXPORTS", "")], include_dirs=[include_dir], library_dirs=python_lib_dirs, sources=libgpilog_sources, @@ -337,7 +342,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): libcocotb_sources += ["libcocotb.rc"] libcocotb = Extension( os.path.join("cocotb", "libs", "libcocotb"), - define_macros=[("PYTHON_SO_LIB", _get_python_lib())], + define_macros=[("COCOTB_EMBED_EXPORTS", ""), ("PYTHON_SO_LIB", _get_python_lib())], include_dirs=[include_dir], libraries=[_get_python_lib_link(), "gpilog", "cocotbutils"], library_dirs=python_lib_dirs, @@ -357,7 +362,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): libgpi_sources += ["libgpi.rc"] libgpi = Extension( os.path.join("cocotb", "libs", "libgpi"), - define_macros=[("LIB_EXT", _get_lib_ext_name()), ("SINGLETON_HANDLES", "")], + define_macros=[("GPI_EXPORTS", ""), ("LIB_EXT", _get_lib_ext_name()), ("SINGLETON_HANDLES", "")], include_dirs=[include_dir], libraries=["cocotbutils", "gpilog", "cocotb", "stdc++"], sources=libgpi_sources, @@ -401,7 +406,7 @@ def _get_vpi_lib_ext( libcocotbvpi_sources += [lib_name + ".rc"] libcocotbvpi = Extension( os.path.join("cocotb", "libs", lib_name), - define_macros=[("VPI_CHECKING", "1")] + [(sim_define, "")], + define_macros=[("COCOTBVPI_EXPORTS", ""), ("VPI_CHECKING", "1")] + [(sim_define, "")], include_dirs=[include_dir], libraries=["gpi", "gpilog"] + extra_lib, library_dirs=extra_lib_dir, @@ -426,7 +431,7 @@ def _get_vhpi_lib_ext( libcocotbvhpi = Extension( os.path.join("cocotb", "libs", lib_name), include_dirs=[include_dir], - define_macros=[("VHPI_CHECKING", 1)] + [(sim_define, "")], + define_macros=[("COCOTBVHPI_EXPORTS", ""), ("VHPI_CHECKING", 1)] + [(sim_define, "")], libraries=["gpi", "gpilog", "stdc++"] + extra_lib, library_dirs=extra_lib_dir, sources=libcocotbvhpi_sources, @@ -512,6 +517,7 @@ def get_ext(): fli_sources += [lib_name + ".rc"] fli_ext = Extension( os.path.join("cocotb", "libs", lib_name), + define_macros=[("COCOTBFLI_EXPORTS", "")], include_dirs=[include_dir, modelsim_include_dir], libraries=["gpi", "gpilog", "stdc++"] + modelsim_extra_lib, library_dirs=modelsim_extra_lib_path, From 8a37f94188b338f577db19ded92d8ff39afc7fd9 Mon Sep 17 00:00:00 2001 From: Tim Pambor Date: Fri, 10 Jul 2020 11:37:38 +0200 Subject: [PATCH 2453/2656] Enable Link Time Optimization --- cocotb_build_libs.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cocotb_build_libs.py b/cocotb_build_libs.py index 2bfaf4cd..5056a3f9 100755 --- a/cocotb_build_libs.py +++ b/cocotb_build_libs.py @@ -244,6 +244,8 @@ def _extra_link_args(lib_name=None, rpaths=[]): if os.name == "nt": # Align behavior of gcc with msvc and export only symbols marked with __declspec(dllexport) args += ["-Wl,--exclude-all-symbols"] + else: + args += ["-flto"] return args @@ -279,6 +281,8 @@ def _get_python_lib(): _base_warns = ["-Wall", "-Wextra", "-Wcast-qual", "-Wwrite-strings", "-Wconversion"] _ccx_warns = _base_warns + ["-Wnon-virtual-dtor", "-Woverloaded-virtual"] _extra_cxx_compile_args = ["-std=c++11", "-fvisibility=hidden", "-fvisibility-inlines-hidden"] + _ccx_warns +if os.name != "nt": + _extra_cxx_compile_args += ["-flto"] # Make PRI* format macros available with C++11 compiler but older libc, e.g. on RHEL6. _extra_cxx_compile_args += ["-D__STDC_FORMAT_MACROS"] From d60b45877dcc0e054af015deef29ebc990ecac83 Mon Sep 17 00:00:00 2001 From: Tim Pambor Date: Fri, 14 Aug 2020 20:31:52 +0200 Subject: [PATCH 2454/2656] Do not explicitly link with libstdc++ --- cocotb_build_libs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cocotb_build_libs.py b/cocotb_build_libs.py index 5056a3f9..ebc48fa4 100755 --- a/cocotb_build_libs.py +++ b/cocotb_build_libs.py @@ -368,7 +368,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): os.path.join("cocotb", "libs", "libgpi"), define_macros=[("GPI_EXPORTS", ""), ("LIB_EXT", _get_lib_ext_name()), ("SINGLETON_HANDLES", "")], include_dirs=[include_dir], - libraries=["cocotbutils", "gpilog", "cocotb", "stdc++"], + libraries=["cocotbutils", "gpilog", "cocotb"], sources=libgpi_sources, extra_link_args=_extra_link_args(lib_name="libgpi", rpaths=["$ORIGIN"]), extra_compile_args=_extra_cxx_compile_args, @@ -436,7 +436,7 @@ def _get_vhpi_lib_ext( os.path.join("cocotb", "libs", lib_name), include_dirs=[include_dir], define_macros=[("COCOTBVHPI_EXPORTS", ""), ("VHPI_CHECKING", 1)] + [(sim_define, "")], - libraries=["gpi", "gpilog", "stdc++"] + extra_lib, + libraries=["gpi", "gpilog"] + extra_lib, library_dirs=extra_lib_dir, sources=libcocotbvhpi_sources, extra_link_args=_extra_link_args(lib_name=lib_name, rpaths=["$ORIGIN"]), @@ -523,7 +523,7 @@ def get_ext(): os.path.join("cocotb", "libs", lib_name), define_macros=[("COCOTBFLI_EXPORTS", "")], include_dirs=[include_dir, modelsim_include_dir], - libraries=["gpi", "gpilog", "stdc++"] + modelsim_extra_lib, + libraries=["gpi", "gpilog"] + modelsim_extra_lib, library_dirs=modelsim_extra_lib_path, sources=fli_sources, extra_link_args=_extra_link_args(lib_name=lib_name, rpaths=["$ORIGIN"]), From 6bcfb8350983bcdbc66cb2ff446338ace60c4e93 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 19 Aug 2020 19:04:54 +0200 Subject: [PATCH 2455/2656] Reflect renaming of status:on-hold to status:blocked --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3f26e277..0032e99d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -106,7 +106,7 @@ The `type` labels define the type of issue or PR: The `status` labels give a quick impression of the current status of the issue or PR: - `status:worksforme`: the issue it not reproducible, or intended behavior (i.e. not a bug) -- `status:on-hold`: further progress is blocked by a dependency, e.g. other code which must be commited first. +- `status:blocked`: further progress is blocked by a dependency, e.g. other code which must be commited first. - `status:needinfo`: feedback from someone is required. The issue/PR text gives more details. - `status:duplicate`: the same issue is already being handled in another issue/PR. From 280125e925fe7e4293d8025323272305d271fe99 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 19 Aug 2020 19:02:13 +0200 Subject: [PATCH 2456/2656] Make error messages more consistent --- cocotb/share/lib/vhpi/VhpiCbHdl.cpp | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp index f37e5fb6..ca2959b6 100644 --- a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp +++ b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp @@ -190,7 +190,7 @@ int VhpiArrayObjHdl::initialise(std::string &name, std::string &fq_name) { } if (NULL == type) { - LOG_ERROR("Unable to get vhpiBaseType for %s", fq_name.c_str()); + LOG_ERROR("VHPI: Unable to get vhpiBaseType for %s", fq_name.c_str()); return -1; } @@ -270,7 +270,7 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { vhpiHandleT handle = GpiObjHdl::get_handle(); if (0 > vhpi_get_value(get_handle(), &m_value)) { - LOG_ERROR("vhpi_get_value failed for %s (%s)", fq_name.c_str(), vhpi_get_str(vhpiKindStrP, handle)); + LOG_ERROR("VHPI: vhpi_get_value failed for %s (%s)", fq_name.c_str(), vhpi_get_str(vhpiKindStrP, handle)); return -1; } @@ -305,7 +305,7 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { } default: { - LOG_ERROR("Unable to determine property for %s (%d) format object", + LOG_ERROR("VHPI: Unable to determine property for %s (%d) format object", ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format), m_value.format); return -1; } @@ -437,14 +437,14 @@ int VhpiCbHdl::arm_callback() if (!new_hdl) { check_vhpi_error(); - LOG_ERROR("VHPI: Unable to register callback a handle for VHPI type %s(%d)", + LOG_ERROR("VHPI: Unable to register a callback handle for VHPI type %s(%d)", m_impl->reason_to_string(cb_data.reason), cb_data.reason); goto error; } cbState = (vhpiStateT)vhpi_get(vhpiStateP, new_hdl); if (vhpiEnable != cbState) { - LOG_ERROR("VHPI ERROR: Registered callback isn't enabled! Got %d\n", cbState); + LOG_ERROR("VHPI: Registered callback isn't enabled! Got %d\n", cbState); goto error; } @@ -581,7 +581,7 @@ int VhpiSignalObjHdl::set_signal_value(long value, gpi_set_action_t action) case vhpiEnumVal: { using EnumLimits = std::numeric_limits; if ((value > EnumLimits::max()) || (value < EnumLimits::min())) { - LOG_ERROR("Data loss detected"); + LOG_ERROR("VHPI: Data loss detected"); return -1; } m_value.value.enumv = static_cast(value); @@ -591,7 +591,7 @@ int VhpiSignalObjHdl::set_signal_value(long value, gpi_set_action_t action) case vhpiIntVal: { using IntLimits = std::numeric_limits; if ((value > IntLimits::max()) || (value < IntLimits::min())) { - LOG_ERROR("Data loss detected"); + LOG_ERROR("VHPI: Data loss detected"); return -1; } m_value.value.intg = static_cast(value); @@ -601,7 +601,7 @@ int VhpiSignalObjHdl::set_signal_value(long value, gpi_set_action_t action) case vhpiCharVal: { using CharLimits = std::numeric_limits; if ((value > CharLimits::max()) || (value < CharLimits::min())) { - LOG_ERROR("Data loss detected"); + LOG_ERROR("VHPI: Data loss detected"); return -1; } m_value.value.ch = static_cast(value); @@ -679,7 +679,7 @@ int VhpiSignalObjHdl::set_signal_value_binstr(std::string &value, gpi_set_action } default: { - LOG_ERROR("VHPI: Unable to handle this format type %s", + LOG_ERROR("VHPI: Unable to handle this format type: %s", ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); return -1; } @@ -706,7 +706,7 @@ int VhpiSignalObjHdl::set_signal_value_str(std::string &value, gpi_set_action_t } default: { - LOG_ERROR("VHPI: Unable to handle this format type %s", + LOG_ERROR("VHPI: Unable to handle this format type: %s", ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); return -1; } @@ -732,7 +732,7 @@ const char* VhpiSignalObjHdl::get_signal_value_binstr() int ret = vhpi_get_value(GpiObjHdl::get_handle(), &m_binvalue); if (ret) { check_vhpi_error(); - LOG_ERROR("Size of m_binvalue.value.str was not large enough: req=%d have=%d for type %s", + LOG_ERROR("VHPI: Size of m_binvalue.value.str was not large enough: req=%d have=%d for type %s", ret, m_binvalue.bufSize, ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); @@ -750,7 +750,7 @@ const char* VhpiSignalObjHdl::get_signal_value_str() int ret = vhpi_get_value(GpiObjHdl::get_handle(), &m_value); if (ret) { check_vhpi_error(); - LOG_ERROR("Size of m_value.value.str was not large enough req=%d have=%d for type %s", + LOG_ERROR("VHPI: Size of m_value.value.str was not large enough: req=%d have=%d for type %s", ret, m_value.bufSize, ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); @@ -758,7 +758,7 @@ const char* VhpiSignalObjHdl::get_signal_value_str() break; } default: { - LOG_ERROR("Reading strings not valid for this handle"); + LOG_ERROR("VHPI: Reading strings not valid for this handle"); return ""; } } @@ -773,7 +773,7 @@ double VhpiSignalObjHdl::get_signal_value_real() if (vhpi_get_value(GpiObjHdl::get_handle(), &m_value)) { check_vhpi_error(); - LOG_ERROR("failed to get real value"); + LOG_ERROR("VHPI: Failed to get value of type real"); } return m_value.value.real; } @@ -786,7 +786,7 @@ long VhpiSignalObjHdl::get_signal_value_long() if (vhpi_get_value(GpiObjHdl::get_handle(), &value)) { check_vhpi_error(); - LOG_ERROR("failed to get long value"); + LOG_ERROR("VHPI: Failed to get value of type long"); } return value.value.intg; From d4bc0d18f91c2d35eb197d26a4a8da3ddd39d3a4 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Wed, 19 Aug 2020 10:09:18 +0100 Subject: [PATCH 2457/2656] Replace yield with await in most of the monitors and drivers Note that we cannot remove `@cocotb.coroutine` decorators from public functions yet, as that would break users passing them to `First` or `Combine`. --- cocotb/drivers/__init__.py | 58 ++++++++-------- cocotb/drivers/avalon.py | 128 +++++++++++++++++------------------- cocotb/drivers/opb.py | 25 ++++--- cocotb/drivers/xgmii.py | 14 ++-- cocotb/monitors/__init__.py | 12 ++-- cocotb/monitors/avalon.py | 17 ++--- cocotb/monitors/xgmii.py | 10 ++- 7 files changed, 121 insertions(+), 143 deletions(-) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index e974cfa0..a81e59d6 100755 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -72,8 +72,7 @@ def stop(self): """Stop generating data.""" self._cr.kill() - @cocotb.coroutine - def _cr_twiddler(self, generator=None): + async def _cr_twiddler(self, generator=None): if generator is None and self._generator is None: raise Exception("No generator provided!") if generator is not None: @@ -86,10 +85,10 @@ def _cr_twiddler(self, generator=None): on, off = next(self._generator) self._signal <= 1 for _ in range(on): - yield edge + await edge self._signal <= 0 for _ in range(off): - yield edge + await edge class Driver: @@ -143,8 +142,8 @@ def clear(self): self._sendQ = deque() @coroutine - def send(self, transaction: Any, sync: bool = True, **kwargs: Any) -> None: - """Blocking send call (hence must be "yielded" rather than called). + async def send(self, transaction: Any, sync: bool = True, **kwargs: Any) -> None: + """Blocking send call (hence must be "awaited" rather than called). Sends the transaction over the bus. @@ -154,9 +153,9 @@ def send(self, transaction: Any, sync: bool = True, **kwargs: Any) -> None: **kwargs: Additional arguments used in child class' :any:`_driver_send` method. """ - yield self._send(transaction, None, None, sync=sync, **kwargs) + await self._send(transaction, None, None, sync=sync, **kwargs) - def _driver_send(self, transaction: Any, sync: bool = True, **kwargs: Any) -> None: + async def _driver_send(self, transaction: Any, sync: bool = True, **kwargs: Any) -> None: """Actual implementation of the send. Sub-classes should override this method to implement the actual @@ -170,8 +169,7 @@ def _driver_send(self, transaction: Any, sync: bool = True, **kwargs: Any) -> No raise NotImplementedError("Sub-classes of Driver should define a " "_driver_send coroutine") - @coroutine - def _send( + async def _send( self, transaction: Any, callback: Callable[[Any], Any], event: Event, sync: bool = True, **kwargs ) -> None: @@ -186,7 +184,7 @@ def _send( **kwargs: Any additional arguments used in child class' :any:`_driver_send` method. """ - yield self._driver_send(transaction, sync=sync, **kwargs) + await self._driver_send(transaction, sync=sync, **kwargs) # Notify the world that this transaction is complete if event: @@ -194,14 +192,13 @@ def _send( if callback: callback(transaction) - @coroutine - def _send_thread(self): + async def _send_thread(self): while True: # Sleep until we have something to send while not self._sendQ: self._pending.clear() - yield self._pending.wait() + await self._pending.wait() synchronised = False @@ -210,7 +207,7 @@ def _send_thread(self): while self._sendQ: transaction, callback, event, kwargs = self._sendQ.popleft() self.log.debug("Sending queued packet...") - yield self._send(transaction, callback, event, + await self._send(transaction, callback, event, sync=not synchronised, **kwargs) synchronised = True @@ -252,8 +249,7 @@ def __init__(self, entity: SimHandleBase, name: Optional[str], clock: SimHandleB # Give this instance a unique name self.name = name if index is None else "%s_%d" % (name, index) - @coroutine - def _driver_send(self, transaction, sync: bool = True) -> None: + async def _driver_send(self, transaction, sync: bool = True) -> None: """Implementation for BusDriver. Args: @@ -261,36 +257,36 @@ def _driver_send(self, transaction, sync: bool = True) -> None: sync: Synchronize the transfer by waiting for a rising edge. """ if sync: - yield RisingEdge(self.clock) + await RisingEdge(self.clock) self.bus <= transaction @coroutine - def _wait_for_signal(self, signal): + async def _wait_for_signal(self, signal): """This method will return when the specified signal has hit logic ``1``. The state will be in the :class:`~cocotb.triggers.ReadOnly` phase so sim will need to move to :class:`~cocotb.triggers.NextTimeStep` before registering more callbacks can occur. """ - yield ReadOnly() + await ReadOnly() while signal.value.integer != 1: - yield RisingEdge(signal) - yield ReadOnly() - yield NextTimeStep() + await RisingEdge(signal) + await ReadOnly() + await NextTimeStep() @coroutine - def _wait_for_nsignal(self, signal): + async def _wait_for_nsignal(self, signal): """This method will return when the specified signal has hit logic ``0``. The state will be in the :class:`~cocotb.triggers.ReadOnly` phase so sim will need to move to :class:`~cocotb.triggers.NextTimeStep` before registering more callbacks can occur. """ - yield ReadOnly() + await ReadOnly() while signal.value.integer != 0: - yield Edge(signal) - yield ReadOnly() - yield NextTimeStep() + await Edge(signal) + await ReadOnly() + await NextTimeStep() def __str__(self): """Provide the name of the bus""" @@ -349,8 +345,8 @@ def set_valid_generator(self, valid_generator=None): self._next_valids() -@cocotb.coroutine -def polled_socket_attachment(driver, sock): +@coroutine +async def polled_socket_attachment(driver, sock): """Non-blocking socket attachment that queues any payload received from the socket to be queued for sending into the driver. """ @@ -359,7 +355,7 @@ def polled_socket_attachment(driver, sock): sock.setblocking(False) driver.log.info("Listening for data from %s" % repr(sock)) while True: - yield RisingEdge(driver.clock) + await RisingEdge(driver.clock) try: data = sock.recv(4096) except socket.error as e: diff --git a/cocotb/drivers/avalon.py b/cocotb/drivers/avalon.py index fd38826a..17c869a7 100644 --- a/cocotb/drivers/avalon.py +++ b/cocotb/drivers/avalon.py @@ -105,10 +105,9 @@ def __init__(self, entity, name, clock, **kwargs): def __len__(self): return 2**len(self.bus.address) - @coroutine - def _acquire_lock(self): + async def _acquire_lock(self): if self.busy: - yield self.busy_event.wait() + await self.busy_event.wait() self.busy_event.clear() self.busy = True @@ -117,7 +116,7 @@ def _release_lock(self): self.busy_event.set() @coroutine - def read(self, address: int, sync: bool = True) -> BinaryValue: + async def read(self, address: int, sync: bool = True) -> BinaryValue: """Issue a request to the bus and block until this comes back. Simulation time still progresses @@ -138,11 +137,11 @@ def read(self, address: int, sync: bool = True) -> BinaryValue: self.log.error("Cannot read - have no read signal") raise TestError("Attempt to read on a write-only AvalonMaster") - yield self._acquire_lock() + await self._acquire_lock() # Apply values for next clock edge if sync: - yield RisingEdge(self.clock) + await RisingEdge(self.clock) self.bus.address <= address self.bus.read <= 1 if hasattr(self.bus, "byteenable"): @@ -152,8 +151,8 @@ def read(self, address: int, sync: bool = True) -> BinaryValue: # Wait for waitrequest to be low if hasattr(self.bus, "waitrequest"): - yield self._wait_for_nsignal(self.bus.waitrequest) - yield RisingEdge(self.clock) + await self._wait_for_nsignal(self.bus.waitrequest) + await RisingEdge(self.clock) # Deassert read self.bus.read <= 0 @@ -167,15 +166,15 @@ def read(self, address: int, sync: bool = True) -> BinaryValue: if hasattr(self.bus, "readdatavalid"): while True: - yield ReadOnly() + await ReadOnly() if int(self.bus.readdatavalid): break - yield RisingEdge(self.clock) + await RisingEdge(self.clock) else: # Assume readLatency = 1 if no readdatavalid # FIXME need to configure this, # should take a dictionary of Avalon properties. - yield ReadOnly() + await ReadOnly() # Get the data data = self.bus.readdata.value @@ -184,7 +183,7 @@ def read(self, address: int, sync: bool = True) -> BinaryValue: return data @coroutine - def write(self, address: int, value: int) -> None: + async def write(self, address: int, value: int) -> None: """Issue a write to the given address with the specified value. @@ -199,10 +198,10 @@ def write(self, address: int, value: int) -> None: self.log.error("Cannot write - have no write signal") raise TestError("Attempt to write on a read-only AvalonMaster") - yield self._acquire_lock() + await self._acquire_lock() # Apply values to bus - yield RisingEdge(self.clock) + await RisingEdge(self.clock) self.bus.address <= address self.bus.writedata <= value self.bus.write <= 1 @@ -213,10 +212,10 @@ def write(self, address: int, value: int) -> None: # Wait for waitrequest to be low if hasattr(self.bus, "waitrequest"): - yield self._wait_for_nsignal(self.bus.waitrequest) + await self._wait_for_nsignal(self.bus.waitrequest) # Deassert write - yield RisingEdge(self.clock) + await RisingEdge(self.clock) self.bus.write <= 0 if hasattr(self.bus, "byteenable"): self.bus.byteenable <= 0 @@ -358,18 +357,16 @@ def _write_burst_addr(self): return (addr, byteenable, burstcount) - @coroutine - def _writing_byte_value(self, byteaddr): + async def _writing_byte_value(self, byteaddr): """Writing value in _mem with byteaddr size.""" - yield FallingEdge(self.clock) + await FallingEdge(self.clock) for i in range(self.dataByteSize): data = self.bus.writedata.value.integer addrtmp = byteaddr + i datatmp = (data >> (i*8)) & 0xff self._mem[addrtmp] = datatmp - @coroutine - def _waitrequest(self): + async def _waitrequest(self): """Generate waitrequest randomly.""" if self._avalon_properties.get("WriteBurstWaitReq", True): if random.choice([True, False, False, False]): @@ -377,21 +374,20 @@ def _waitrequest(self): waitingtime = range(random.randint(0, randmax)) for waitreq in waitingtime: self.bus.waitrequest <= 1 - yield RisingEdge(self.clock) + await RisingEdge(self.clock) else: - yield NextTimeStep() + await NextTimeStep() self.bus.waitrequest <= 0 - @coroutine - def _respond(self): + async def _respond(self): """Coroutine to respond to the actual requests.""" edge = RisingEdge(self.clock) while True: - yield edge + await edge self._do_response() - yield ReadOnly() + await ReadOnly() if self._readable and self.bus.read.value: if not self._burstread: @@ -424,15 +420,15 @@ def _respond(self): # toggle waitrequest # TODO: configure waitrequest time with Avalon properties - yield NextTimeStep() # can't write during read-only phase + await NextTimeStep() # can't write during read-only phase self.bus.waitrequest <= 1 - yield edge - yield edge + await edge + await edge self.bus.waitrequest <= 0 # wait for read data for i in range(self._avalon_properties["readLatency"]): - yield edge + await edge for count in range(burstcount): if (addr + count)*self.dataByteSize not in self._mem: self.log.warning("Attempt to burst read from uninitialized " @@ -447,7 +443,7 @@ def _respond(self): self.log.debug("Read from address 0x%x returning 0x%x", (addr + count) * self.dataByteSize, value) self._responses.append(value) - yield edge + await edge self._do_response() if self._writeable and self.bus.write.value: @@ -483,21 +479,21 @@ def _respond(self): else: self.log.debug("writing burst") # maintain waitrequest high randomly - yield self._waitrequest() + await self._waitrequest() addr, byteenable, burstcount = self._write_burst_addr() for count in range(burstcount): while self.bus.write.value == 0: - yield NextTimeStep() + await NextTimeStep() # self._mem is aligned on 8 bits words - yield self._writing_byte_value(addr + count*self.dataByteSize) + await self._writing_byte_value(addr + count*self.dataByteSize) self.log.debug("writing %016X @ %08X", self.bus.writedata.value.integer, addr + count * self.dataByteSize) - yield edge + await edge # generate waitrequest randomly - yield self._waitrequest() + await self._waitrequest() if self._avalon_properties.get("WriteBurstWaitReq", True): self.bus.waitrequest <= 1 @@ -526,21 +522,19 @@ def __init__(self, entity, name, clock, *, config={}, **kwargs): self.bus.valid <= 0 self.bus.data <= word - @coroutine - def _wait_ready(self): + async def _wait_ready(self): """Wait for a ready cycle on the bus before continuing. Can no longer drive values this cycle... FIXME assumes readyLatency of 0 """ - yield ReadOnly() + await ReadOnly() while not self.bus.ready.value: - yield RisingEdge(self.clock) - yield ReadOnly() + await RisingEdge(self.clock) + await ReadOnly() - @coroutine - def _driver_send(self, value, sync=True): + async def _driver_send(self, value, sync=True): """Send a transmission over the bus. Args: @@ -557,13 +551,13 @@ def _driver_send(self, value, sync=True): self.bus.valid <= 0 if sync: - yield clkedge + await clkedge # Insert a gap where valid is low if not self.on: self.bus.valid <= 0 for _ in range(self.off): - yield clkedge + await clkedge # Grab the next set of on/off values self._next_valids() @@ -580,9 +574,9 @@ def _driver_send(self, value, sync=True): # If this is a bus with a ready signal, wait for this word to # be acknowledged if hasattr(self.bus, "ready"): - yield self._wait_ready() + await self._wait_ready() - yield clkedge + await clkedge self.bus.valid <= 0 word.binstr = "x" * len(self.bus.data) self.bus.data <= word @@ -658,21 +652,19 @@ def __init__(self, entity, name, clock, *, config={}, **kwargs): value="x" * len(self.bus.channel)) self.bus.channel <= channel - @coroutine - def _wait_ready(self): + async def _wait_ready(self): """Wait for a ready cycle on the bus before continuing. Can no longer drive values this cycle... FIXME assumes readyLatency of 0 """ - yield ReadOnly() + await ReadOnly() while not self.bus.ready.value: - yield RisingEdge(self.clock) - yield ReadOnly() + await RisingEdge(self.clock) + await ReadOnly() - @coroutine - def _send_string(self, string: bytes, sync: bool = True, channel: Optional[int] = None) -> None: + async def _send_string(self, string: bytes, sync: bool = True, channel: Optional[int] = None) -> None: """Args: string: A string of bytes to send over the bus. channel: Channel to send the data on. @@ -707,13 +699,13 @@ def _send_string(self, string: bytes, sync: bool = True, channel: Optional[int] while string: if not firstword or (firstword and sync): - yield clkedge + await clkedge # Insert a gap where valid is low if not self.on: self.bus.valid <= 0 for _ in range(self.off): - yield clkedge + await clkedge # Grab the next set of on/off values self._next_valids() @@ -755,9 +747,9 @@ def _send_string(self, string: bytes, sync: bool = True, channel: Optional[int] # If this is a bus with a ready signal, wait for this word to # be acknowledged if hasattr(self.bus, "ready"): - yield self._wait_ready() + await self._wait_ready() - yield clkedge + await clkedge self.bus.valid <= 0 self.bus.endofpacket <= 0 word.binstr = "x" * len(self.bus.data) @@ -774,8 +766,7 @@ def _send_string(self, string: bytes, sync: bool = True, channel: Optional[int] value="x" * len(self.bus.channel)) self.bus.channel <= channel_value - @coroutine - def _send_iterable(self, pkt: Iterable, sync: bool = True) -> None: + async def _send_iterable(self, pkt: Iterable, sync: bool = True) -> None: """Args: pkt: Will yield objects with attributes matching the signal names for each individual bus cycle. @@ -785,7 +776,7 @@ def _send_iterable(self, pkt: Iterable, sync: bool = True) -> None: for word in pkt: if not firstword or (firstword and sync): - yield clkedge + await clkedge firstword = False @@ -793,7 +784,7 @@ def _send_iterable(self, pkt: Iterable, sync: bool = True) -> None: if not self.on: self.bus.valid <= 0 for _ in range(self.off): - yield clkedge + await clkedge # Grab the next set of on/off values self._next_valids() @@ -810,13 +801,12 @@ def _send_iterable(self, pkt: Iterable, sync: bool = True) -> None: # Wait for valid words to be acknowledged if not hasattr(word, "valid") or word.valid: if hasattr(self.bus, "ready"): - yield self._wait_ready() + await self._wait_ready() - yield clkedge + await clkedge self.bus.valid <= 0 - @coroutine - def _driver_send(self, pkt: Union[bytes, Iterable], sync: bool = True, channel: Optional[int] = None): + async def _driver_send(self, pkt: Union[bytes, Iterable], sync: bool = True, channel: Optional[int] = None): """Send a packet over the bus. Args: @@ -833,11 +823,11 @@ def _driver_send(self, pkt: Union[bytes, Iterable], sync: bool = True, channel: if isinstance(pkt, bytes): self.log.debug("Sending packet of length %d bytes", len(pkt)) self.log.debug(hexdump(pkt)) - yield self._send_string(pkt, sync=sync, channel=channel) + await self._send_string(pkt, sync=sync, channel=channel) self.log.debug("Successfully sent packet of length %d bytes", len(pkt)) elif isinstance(pkt, str): raise TypeError("pkt must be a bytestring, not a unicode string") else: if channel is not None: self.log.warning("%s is ignoring channel=%d because pkt is an iterable", self.name, channel) - yield self._send_iterable(pkt, sync=sync) + await self._send_iterable(pkt, sync=sync) diff --git a/cocotb/drivers/opb.py b/cocotb/drivers/opb.py index cf06daa9..0a31ca14 100644 --- a/cocotb/drivers/opb.py +++ b/cocotb/drivers/opb.py @@ -54,10 +54,9 @@ def __init__(self, entity, name, clock, **kwargs): self.busy_event = Event("%s_busy" % name) self.busy = False - @cocotb.coroutine - def _acquire_lock(self): + async def _acquire_lock(self): if self.busy: - yield self.busy_event.wait() + await self.busy_event.wait() self.busy_event.clear() self.busy = True @@ -66,7 +65,7 @@ def _release_lock(self): self.busy_event.set() @cocotb.coroutine - def read(self, address: int, sync: bool = True) -> BinaryValue: + async def read(self, address: int, sync: bool = True) -> BinaryValue: """Issue a request to the bus and block until this comes back. Simulation time still progresses but syntactically it blocks. @@ -82,11 +81,11 @@ def read(self, address: int, sync: bool = True) -> BinaryValue: Raises: OPBException: If read took longer than 16 cycles. """ - yield self._acquire_lock() + await self._acquire_lock() # Apply values for next clock edge if sync: - yield RisingEdge(self.clock) + await RisingEdge(self.clock) self.bus.ABus <= address self.bus.select <= 1 self.bus.RNW <= 1 @@ -94,8 +93,8 @@ def read(self, address: int, sync: bool = True) -> BinaryValue: count = 0 while not int(self.bus.xferAck.value): - yield RisingEdge(self.clock) - yield ReadOnly() + await RisingEdge(self.clock) + await ReadOnly() if int(self.bus.toutSup.value): count = 0 else: @@ -111,7 +110,7 @@ def read(self, address: int, sync: bool = True) -> BinaryValue: return data @cocotb.coroutine - def write(self, address: int, value: int, sync: bool = True) -> None: + async def write(self, address: int, value: int, sync: bool = True) -> None: """Issue a write to the given address with the specified value. Args: @@ -123,10 +122,10 @@ def write(self, address: int, value: int, sync: bool = True) -> None: Raises: OPBException: If write took longer than 16 cycles. """ - yield self._acquire_lock() + await self._acquire_lock() if sync: - yield RisingEdge(self.clock) + await RisingEdge(self.clock) self.bus.ABus <= address self.bus.select <= 1 self.bus.RNW <= 0 @@ -135,8 +134,8 @@ def write(self, address: int, value: int, sync: bool = True) -> None: count = 0 while not int(self.bus.xferAck.value): - yield RisingEdge(self.clock) - yield ReadOnly() + await RisingEdge(self.clock) + await ReadOnly() if int(self.bus.toutSup.value): count = 0 else: diff --git a/cocotb/drivers/xgmii.py b/cocotb/drivers/xgmii.py index a165d938..288ba52c 100644 --- a/cocotb/drivers/xgmii.py +++ b/cocotb/drivers/xgmii.py @@ -28,7 +28,6 @@ import struct import zlib -import cocotb from cocotb.triggers import RisingEdge from cocotb.drivers import Driver from cocotb.utils import hexdump @@ -173,8 +172,7 @@ def terminate(self, index: int) -> None: for rem in range(index + 1, len(self.bus)): self.bus[rem] = (_XGMII_IDLE, True) - @cocotb.coroutine - def _driver_send(self, pkt: bytes, sync: bool = True) -> None: + async def _driver_send(self, pkt: bytes, sync: bool = True) -> None: """Send a packet over the bus. Args: @@ -187,7 +185,7 @@ def _driver_send(self, pkt: bytes, sync: bool = True) -> None: clkedge = RisingEdge(self.clock) if sync: - yield clkedge + await clkedge self.bus[0] = (_XGMII_START, True) @@ -196,7 +194,7 @@ def _driver_send(self, pkt: bytes, sync: bool = True) -> None: pkt = pkt[len(self.bus)-1:] self.signal <= self.bus.value - yield clkedge + await clkedge done = False @@ -211,14 +209,14 @@ def _driver_send(self, pkt: bytes, sync: bool = True) -> None: self.bus[i] = (pkt[i], False) self.signal <= self.bus.value - yield clkedge + await clkedge pkt = pkt[len(self.bus):] if not done: self.terminate(0) self.signal <= self.bus.value - yield clkedge + await clkedge self.idle() - yield clkedge + await clkedge self.log.debug("Successfully sent packet") diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index 2ebcfa9a..4f532958 100755 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -39,7 +39,7 @@ from cocotb.bus import Bus from cocotb.decorators import coroutine from cocotb.log import SimLog -from cocotb.triggers import Event, Timer +from cocotb.triggers import Event, Timer, First class MonitorStatistics: @@ -59,7 +59,7 @@ class Monitor: method should capture some behavior of the pins, form a transaction, and pass this transaction to the internal :any:`_recv` method. The :any:`_monitor_recv` method is added to the cocotb scheduler during the ``__init__`` phase, so it - should not be yielded anywhere. + should not be awaited anywhere. The primary use of a Monitor is as an interface for a :class:`~cocotb.scoreboard.Scoreboard`. @@ -113,7 +113,7 @@ def add_callback(self, callback): self._callbacks.append(callback) @coroutine - def wait_for_recv(self, timeout=None): + async def wait_for_recv(self, timeout=None): """With *timeout*, :meth:`.wait` for transaction to arrive on monitor and return its data. @@ -126,15 +126,15 @@ def wait_for_recv(self, timeout=None): """ if timeout: t = Timer(timeout) - fired = yield [self._wait_event.wait(), t] + fired = await First(self._wait_event.wait(), t) if fired is t: return None else: - yield self._wait_event.wait() + await self._wait_event.wait() return self._wait_event.data - @coroutine + # this is not `async` so that we fail in `__init__` on subclasses without it def _monitor_recv(self): """Actual implementation of the receiver. diff --git a/cocotb/monitors/avalon.py b/cocotb/monitors/avalon.py index dc4a7d6b..1aebd471 100644 --- a/cocotb/monitors/avalon.py +++ b/cocotb/monitors/avalon.py @@ -35,7 +35,6 @@ import warnings from cocotb.utils import hexdump -from cocotb.decorators import coroutine from cocotb.monitors import BusMonitor from cocotb.triggers import RisingEdge, ReadOnly from cocotb.binary import BinaryValue @@ -65,8 +64,7 @@ def __init__(self, entity, name, clock, *, config={}, **kwargs): self.config[configoption] = value self.log.debug("Setting config option %s to %s", configoption, str(value)) - @coroutine - def _monitor_recv(self): + async def _monitor_recv(self): """Watch the pins and reconstruct transactions.""" # Avoid spurious object creation by recycling @@ -78,10 +76,10 @@ def valid(): return self.bus.valid.value and self.bus.ready.value return self.bus.valid.value - # NB could yield on valid here more efficiently? + # NB could await on valid here more efficiently? while True: - yield clkedge - yield rdonly + await clkedge + await rdonly if valid(): vec = self.bus.data.value vec.big_endian = self.config["firstSymbolInHighOrderBits"] @@ -147,8 +145,7 @@ def __init__(self, entity, name, clock, *, config={}, report_channel=False, **kw "(2**channel_width)-1=%d, channel_width=%d" % (self.name, self.config['maxChannel'], maxChannel, len(self.bus.channel))) - @coroutine - def _monitor_recv(self): + async def _monitor_recv(self): """Watch the pins and reconstruct transactions.""" # Avoid spurious object creation by recycling @@ -165,8 +162,8 @@ def valid(): return self.bus.valid.value while True: - yield clkedge - yield rdonly + await clkedge + await rdonly if self.in_reset: continue diff --git a/cocotb/monitors/xgmii.py b/cocotb/monitors/xgmii.py index 7d8499a0..e501a312 100644 --- a/cocotb/monitors/xgmii.py +++ b/cocotb/monitors/xgmii.py @@ -35,7 +35,6 @@ import struct import zlib -import cocotb from cocotb.utils import hexdump from cocotb.monitors import Monitor from cocotb.triggers import RisingEdge @@ -120,13 +119,12 @@ def _add_payload(self, ctrl, bytes): self._pkt.append(byte) return True - @cocotb.coroutine - def _monitor_recv(self): + async def _monitor_recv(self): clk = RisingEdge(self.clock) self._pkt = bytearray() while True: - yield clk + await clk ctrl, bytes = self._get_bytes() if ctrl[0] and bytes[0] == _XGMII_START: @@ -134,7 +132,7 @@ def _monitor_recv(self): ctrl, bytes = ctrl[1:], bytes[1:] while self._add_payload(ctrl, bytes): - yield clk + await clk ctrl, bytes = self._get_bytes() elif self.bytes == 8 : @@ -143,7 +141,7 @@ def _monitor_recv(self): ctrl, bytes = ctrl[5:], bytes[5:] while self._add_payload(ctrl, bytes): - yield clk + await clk ctrl, bytes = self._get_bytes() if self._pkt: From c208dee3b7bd8351bb5bd020acf14a1c52e06504 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Wed, 19 Aug 2020 10:21:32 +0100 Subject: [PATCH 2458/2656] Change the Monitor docs to use `await` too --- documentation/source/testbench_tools.rst | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/documentation/source/testbench_tools.rst b/documentation/source/testbench_tools.rst index 372b93f6..f024f7f0 100644 --- a/documentation/source/testbench_tools.rst +++ b/documentation/source/testbench_tools.rst @@ -104,13 +104,13 @@ example: self.dut = dut self.stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) - def run_test(dut, data_in=None, config_coroutine=None, idle_inserter=None, + async def run_test(dut, data_in=None, config_coroutine=None, idle_inserter=None, backpressure_inserter=None): cocotb.fork(Clock(dut.clk, 5000).start()) tb = EndianSwapperTB(dut) - yield tb.reset() + await tb.reset() dut.stream_out_ready <= 1 if idle_inserter is not None: @@ -118,7 +118,7 @@ example: # Send in the packets for transaction in data_in(): - yield tb.stream_in.send(transaction) + await tb.stream_in.send(transaction) Monitoring Buses @@ -149,13 +149,12 @@ class. self.clock = clock Monitor.__init__(self, callback, event) - @coroutine - def _monitor_recv(self): + async def _monitor_recv(self): clkedge = RisingEdge(self.clock) while True: # Capture signal at rising edge of clock - yield clkedge + await clkedge vec = self.signal.value self._recv(vec) From dcb22c6c5f099f52e3764e493e4d2bfcb162ccb1 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 27 Jul 2020 21:21:16 -0500 Subject: [PATCH 2459/2656] Fix enough to allow Verilator to finish a regression --- tests/test_cases/issue_1279/Makefile | 14 ++++++++++++++ tests/test_cases/test_exit_error/Makefile | 10 ++++++++++ 2 files changed, 24 insertions(+) diff --git a/tests/test_cases/issue_1279/Makefile b/tests/test_cases/issue_1279/Makefile index 4fd25641..42664f4f 100644 --- a/tests/test_cases/issue_1279/Makefile +++ b/tests/test_cases/issue_1279/Makefile @@ -1,3 +1,17 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause + +ifeq ($(SIM),verilator) + +all: + @echo "Skipping test due to Verilator hanging when prematurely shutting down" # gh-2008 +clean:: + +else + include ../../designs/sample_module/Makefile MODULE = issue_1279 + +endif diff --git a/tests/test_cases/test_exit_error/Makefile b/tests/test_cases/test_exit_error/Makefile index 7176a31a..0531e866 100644 --- a/tests/test_cases/test_exit_error/Makefile +++ b/tests/test_cases/test_exit_error/Makefile @@ -25,6 +25,14 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +ifeq ($(SIM),verilator) + +all: + @echo "Skipping test due to Verilator hanging when prematurely shutting down" # gh-2008 +clean:: + +else + # detecting an expected failure has to happen here, as the python code is invalid .PHONY: override_for_this_test override_for_this_test: @@ -33,3 +41,5 @@ override_for_this_test: include ../../designs/sample_module/Makefile MODULE = test_exit + +endif From 3fb7865118f97cf62e3b5c69263373909530641b Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 27 Jul 2020 21:46:03 -0500 Subject: [PATCH 2460/2656] Add verilator to Travis CI regression --- .travis.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.travis.yml b/.travis.yml index e7393359..33a1ea87 100644 --- a/.travis.yml +++ b/.travis.yml @@ -155,6 +155,18 @@ jobs: script: - tox -e py37 + - stage: SimTests + python: 3.7 + name: "Py 3.7, Linux, Verilator devel" + env: SIM=verilator TOPLEVEL_LANG=verilog + before_install: + - sudo apt install autoconf make g++ bison flex libfl-dev + - git clone https://github.com/verilator/verilator --depth=1 verilator_devel + - cd verilator_devel && autoconf && ./configure && make -j2 && sudo make install && cd .. + - pip install tox + script: + - tox -e py37 + - stage: DeployPyPI python: 3.7 if: tag IS present @@ -174,3 +186,5 @@ jobs: python: 3.7 env: SIM=ghdl TOPLEVEL_LANG=vhdl - env: ALLOW_FAIL=yes + - stage: SimTests + env: SIM=verilator TOPLEVEL_LANG=verilog From 606f2485b082c917195f20b7e5b23ad8dcacd82a Mon Sep 17 00:00:00 2001 From: Tim Pambor Date: Sat, 15 Aug 2020 08:27:24 +0200 Subject: [PATCH 2461/2656] Distutils takes care of linking with libpython --- cocotb_build_libs.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cocotb_build_libs.py b/cocotb_build_libs.py index ebc48fa4..c6215de6 100755 --- a/cocotb_build_libs.py +++ b/cocotb_build_libs.py @@ -330,7 +330,6 @@ def _get_common_lib_ext(include_dir, share_lib_dir): os.path.join("cocotb", "libs", "libgpilog"), define_macros=[("GPILOG_EXPORTS", "")], include_dirs=[include_dir], - library_dirs=python_lib_dirs, sources=libgpilog_sources, extra_link_args=_extra_link_args(lib_name="libgpilog", rpaths=["$ORIGIN"]), extra_compile_args=_extra_cxx_compile_args, @@ -348,8 +347,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): os.path.join("cocotb", "libs", "libcocotb"), define_macros=[("COCOTB_EMBED_EXPORTS", ""), ("PYTHON_SO_LIB", _get_python_lib())], include_dirs=[include_dir], - libraries=[_get_python_lib_link(), "gpilog", "cocotbutils"], - library_dirs=python_lib_dirs, + libraries=["gpilog", "cocotbutils"], sources=libcocotb_sources, extra_link_args=_extra_link_args(lib_name="libcocotb", rpaths=["$ORIGIN"] + python_lib_dirs), extra_compile_args=_extra_cxx_compile_args, @@ -386,7 +384,6 @@ def _get_common_lib_ext(include_dir, share_lib_dir): os.path.join("cocotb", "simulator"), include_dirs=[include_dir], libraries=["cocotbutils", "gpilog", "gpi"], - library_dirs=python_lib_dirs, sources=simulator_sources, extra_compile_args=_extra_cxx_compile_args, extra_link_args=_extra_link_args(rpaths=["$ORIGIN/libs"]), From ef181522e26c4da6d52ed9443fcbf775155098d5 Mon Sep 17 00:00:00 2001 From: Tim Pambor Date: Sat, 15 Aug 2020 12:25:54 +0200 Subject: [PATCH 2462/2656] Fix runtime loading of python on macos --- cocotb_build_libs.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/cocotb_build_libs.py b/cocotb_build_libs.py index c6215de6..883ced3b 100755 --- a/cocotb_build_libs.py +++ b/cocotb_build_libs.py @@ -270,9 +270,17 @@ def _get_python_lib(): """ Get the library for embedded the python interpreter """ if os.name == "nt": - python_lib = _get_python_lib_link() + "." + _get_lib_ext_name() + python_lib = _get_python_lib_link() + ".dll" else: - python_lib = "lib" + _get_python_lib_link() + "." + _get_lib_ext_name() + python_lib = "lib" + _get_python_lib_link() + "." + if sys.platform == "darwin": + python_lib = os.path.join(sysconfig.get_config_var("LIBDIR"), python_lib) + if os.path.exists(python_lib + "dylib"): + python_lib += "dylib" + else: + python_lib += "so" + else: + python_lib += "so" return python_lib @@ -317,10 +325,6 @@ def _get_common_lib_ext(include_dir, share_lib_dir): # # libgpilog # - python_lib_dirs = [] - if sys.platform == "darwin": - python_lib_dirs = [sysconfig.get_config_var("LIBDIR")] - libgpilog_sources = [ os.path.join(share_lib_dir, "gpi_log", "gpi_logging.cpp") ] @@ -349,7 +353,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): include_dirs=[include_dir], libraries=["gpilog", "cocotbutils"], sources=libcocotb_sources, - extra_link_args=_extra_link_args(lib_name="libcocotb", rpaths=["$ORIGIN"] + python_lib_dirs), + extra_link_args=_extra_link_args(lib_name="libcocotb", rpaths=["$ORIGIN"]), extra_compile_args=_extra_cxx_compile_args, ) From 3948a8e51ee044ea217af6cdc1ab7ef5bbf4a449 Mon Sep 17 00:00:00 2001 From: Tim Pambor Date: Sat, 15 Aug 2020 12:49:34 +0200 Subject: [PATCH 2463/2656] Use define_macros for __STDC_FORMAT_MACROS definition --- cocotb_build_libs.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/cocotb_build_libs.py b/cocotb_build_libs.py index 883ced3b..71c3a847 100755 --- a/cocotb_build_libs.py +++ b/cocotb_build_libs.py @@ -293,7 +293,7 @@ def _get_python_lib(): _extra_cxx_compile_args += ["-flto"] # Make PRI* format macros available with C++11 compiler but older libc, e.g. on RHEL6. -_extra_cxx_compile_args += ["-D__STDC_FORMAT_MACROS"] +_extra_defines = [("__STDC_FORMAT_MACROS", "")] def _get_common_lib_ext(include_dir, share_lib_dir): @@ -314,7 +314,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): libcocotbutils_sources += ["libcocotbutils.rc"] libcocotbutils = Extension( os.path.join("cocotb", "libs", "libcocotbutils"), - define_macros=[("COCOTBUTILS_EXPORTS", "")], + define_macros=[("COCOTBUTILS_EXPORTS", "")] + _extra_defines, include_dirs=[include_dir], libraries=["gpilog"], sources=libcocotbutils_sources, @@ -332,7 +332,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): libgpilog_sources += ["libgpilog.rc"] libgpilog = Extension( os.path.join("cocotb", "libs", "libgpilog"), - define_macros=[("GPILOG_EXPORTS", "")], + define_macros=[("GPILOG_EXPORTS", "")] + _extra_defines, include_dirs=[include_dir], sources=libgpilog_sources, extra_link_args=_extra_link_args(lib_name="libgpilog", rpaths=["$ORIGIN"]), @@ -349,7 +349,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): libcocotb_sources += ["libcocotb.rc"] libcocotb = Extension( os.path.join("cocotb", "libs", "libcocotb"), - define_macros=[("COCOTB_EMBED_EXPORTS", ""), ("PYTHON_SO_LIB", _get_python_lib())], + define_macros=[("COCOTB_EMBED_EXPORTS", ""), ("PYTHON_SO_LIB", _get_python_lib())] + _extra_defines, include_dirs=[include_dir], libraries=["gpilog", "cocotbutils"], sources=libcocotb_sources, @@ -368,7 +368,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): libgpi_sources += ["libgpi.rc"] libgpi = Extension( os.path.join("cocotb", "libs", "libgpi"), - define_macros=[("GPI_EXPORTS", ""), ("LIB_EXT", _get_lib_ext_name()), ("SINGLETON_HANDLES", "")], + define_macros=[("GPI_EXPORTS", ""), ("LIB_EXT", _get_lib_ext_name()), ("SINGLETON_HANDLES", "")] + _extra_defines, include_dirs=[include_dir], libraries=["cocotbutils", "gpilog", "cocotb"], sources=libgpi_sources, @@ -386,6 +386,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): simulator_sources += ["simulator.rc"] libsim = Extension( os.path.join("cocotb", "simulator"), + define_macros=_extra_defines, include_dirs=[include_dir], libraries=["cocotbutils", "gpilog", "gpi"], sources=simulator_sources, @@ -411,7 +412,7 @@ def _get_vpi_lib_ext( libcocotbvpi_sources += [lib_name + ".rc"] libcocotbvpi = Extension( os.path.join("cocotb", "libs", lib_name), - define_macros=[("COCOTBVPI_EXPORTS", ""), ("VPI_CHECKING", "1")] + [(sim_define, "")], + define_macros=[("COCOTBVPI_EXPORTS", ""), ("VPI_CHECKING", "1")] + [(sim_define, "")] + _extra_defines, include_dirs=[include_dir], libraries=["gpi", "gpilog"] + extra_lib, library_dirs=extra_lib_dir, @@ -436,7 +437,7 @@ def _get_vhpi_lib_ext( libcocotbvhpi = Extension( os.path.join("cocotb", "libs", lib_name), include_dirs=[include_dir], - define_macros=[("COCOTBVHPI_EXPORTS", ""), ("VHPI_CHECKING", 1)] + [(sim_define, "")], + define_macros=[("COCOTBVHPI_EXPORTS", ""), ("VHPI_CHECKING", 1)] + [(sim_define, "")] + _extra_defines, libraries=["gpi", "gpilog"] + extra_lib, library_dirs=extra_lib_dir, sources=libcocotbvhpi_sources, @@ -522,7 +523,7 @@ def get_ext(): fli_sources += [lib_name + ".rc"] fli_ext = Extension( os.path.join("cocotb", "libs", lib_name), - define_macros=[("COCOTBFLI_EXPORTS", "")], + define_macros=[("COCOTBFLI_EXPORTS", "")] + _extra_defines, include_dirs=[include_dir, modelsim_include_dir], libraries=["gpi", "gpilog"] + modelsim_extra_lib, library_dirs=modelsim_extra_lib_path, From 5a53f6b119b1447c5c396f877471e8e055ddabbc Mon Sep 17 00:00:00 2001 From: Nicola Corna Date: Thu, 20 Aug 2020 11:12:17 +0200 Subject: [PATCH 2464/2656] Replace yield with await for AMBA drivers (#2026) Follows d4bc0d18f91c2d35eb197d26a4a8da3ddd39d3a4 (gh-2022). Again, the `@coroutine` decorators cannot yet be removed from the public API. --- cocotb/drivers/amba.py | 82 ++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/cocotb/drivers/amba.py b/cocotb/drivers/amba.py index 3ade2810..ca0e3c56 100644 --- a/cocotb/drivers/amba.py +++ b/cocotb/drivers/amba.py @@ -65,49 +65,47 @@ def __init__(self, entity, name, clock, **kwargs): self.read_address_busy = Lock("%s_rabusy" % name) self.write_data_busy = Lock("%s_wbusy" % name) - @cocotb.coroutine - def _send_write_address(self, address, delay=0): + async def _send_write_address(self, address, delay=0): """ Send the write address, with optional delay (in clocks) """ - yield self.write_address_busy.acquire() + await self.write_address_busy.acquire() for cycle in range(delay): - yield RisingEdge(self.clock) + await RisingEdge(self.clock) self.bus.AWADDR <= address self.bus.AWVALID <= 1 while True: - yield ReadOnly() + await ReadOnly() if self.bus.AWREADY.value: break - yield RisingEdge(self.clock) - yield RisingEdge(self.clock) + await RisingEdge(self.clock) + await RisingEdge(self.clock) self.bus.AWVALID <= 0 self.write_address_busy.release() - @cocotb.coroutine - def _send_write_data(self, data, delay=0, byte_enable=0xF): + async def _send_write_data(self, data, delay=0, byte_enable=0xF): """Send the write address, with optional delay (in clocks).""" - yield self.write_data_busy.acquire() + await self.write_data_busy.acquire() for cycle in range(delay): - yield RisingEdge(self.clock) + await RisingEdge(self.clock) self.bus.WDATA <= data self.bus.WVALID <= 1 self.bus.WSTRB <= byte_enable while True: - yield ReadOnly() + await ReadOnly() if self.bus.WREADY.value: break - yield RisingEdge(self.clock) - yield RisingEdge(self.clock) + await RisingEdge(self.clock) + await RisingEdge(self.clock) self.bus.WVALID <= 0 self.write_data_busy.release() @cocotb.coroutine - def write( + async def write( self, address: int, value: int, byte_enable: int = 0xf, address_latency: int = 0, data_latency: int = 0, sync: bool = True ) -> BinaryValue: @@ -132,7 +130,7 @@ def write( AXIProtocolError: If write response from AXI is not ``OKAY``. """ if sync: - yield RisingEdge(self.clock) + await RisingEdge(self.clock) c_addr = cocotb.fork(self._send_write_address(address, delay=address_latency)) @@ -141,19 +139,19 @@ def write( delay=data_latency)) if c_addr: - yield c_addr.join() + await c_addr.join() if c_data: - yield c_data.join() + await c_data.join() # Wait for the response while True: - yield ReadOnly() + await ReadOnly() if self.bus.BVALID.value and self.bus.BREADY.value: result = self.bus.BRESP.value break - yield RisingEdge(self.clock) + await RisingEdge(self.clock) - yield RisingEdge(self.clock) + await RisingEdge(self.clock) if int(result): raise AXIProtocolError("Write to address 0x%08x failed with BRESP: %d" @@ -162,7 +160,7 @@ def write( return result @cocotb.coroutine - def read(self, address: int, sync: bool = True) -> BinaryValue: + async def read(self, address: int, sync: bool = True) -> BinaryValue: """Read from an address. Args: @@ -177,27 +175,27 @@ def read(self, address: int, sync: bool = True) -> BinaryValue: AXIProtocolError: If read response from AXI is not ``OKAY``. """ if sync: - yield RisingEdge(self.clock) + await RisingEdge(self.clock) self.bus.ARADDR <= address self.bus.ARVALID <= 1 while True: - yield ReadOnly() + await ReadOnly() if self.bus.ARREADY.value: break - yield RisingEdge(self.clock) + await RisingEdge(self.clock) - yield RisingEdge(self.clock) + await RisingEdge(self.clock) self.bus.ARVALID <= 0 while True: - yield ReadOnly() + await ReadOnly() if self.bus.RVALID.value and self.bus.RREADY.value: data = self.bus.RDATA.value result = self.bus.RRESP.value break - yield RisingEdge(self.clock) + await RisingEdge(self.clock) if int(result): raise AXIProtocolError("Read address 0x%08x failed with RRESP: %d" % @@ -263,20 +261,19 @@ def _size_to_bytes_in_beat(self, AxSIZE): return 2 ** AxSIZE return None - @cocotb.coroutine - def _write_data(self): + async def _write_data(self): clock_re = RisingEdge(self.clock) while True: while True: self.bus.WREADY <= 0 - yield ReadOnly() + await ReadOnly() if self.bus.AWVALID.value: self.bus.WREADY <= 1 break - yield clock_re + await clock_re - yield ReadOnly() + await ReadOnly() _awaddr = int(self.bus.AWADDR) _awlen = int(self.bus.AWLEN) _awsize = int(self.bus.AWSIZE) @@ -298,7 +295,7 @@ def _write_data(self): burst_count = burst_length - yield clock_re + await clock_re while True: if self.bus.WVALID.value: @@ -311,20 +308,19 @@ def _write_data(self): burst_count -= 1 if burst_count == 0: break - yield clock_re + await clock_re - @cocotb.coroutine - def _read_data(self): + async def _read_data(self): clock_re = RisingEdge(self.clock) while True: while True: - yield ReadOnly() + await ReadOnly() if self.bus.ARVALID.value: break - yield clock_re + await clock_re - yield ReadOnly() + await ReadOnly() _araddr = int(self.bus.ARADDR) _arlen = int(self.bus.ARLEN) _arsize = int(self.bus.ARSIZE) @@ -348,11 +344,11 @@ def _read_data(self): burst_count = burst_length - yield clock_re + await clock_re while True: self.bus.RVALID <= 1 - yield ReadOnly() + await ReadOnly() if self.bus.RREADY.value: _burst_diff = burst_length - burst_count _st = _araddr + (_burst_diff * bytes_in_beat) @@ -361,7 +357,7 @@ def _read_data(self): self.bus.RDATA <= word if burst_count == 1: self.bus.RLAST <= 1 - yield clock_re + await clock_re burst_count -= 1 self.bus.RLAST <= 0 if burst_count == 0: From 554ea56c53bb20a02b42e993c8673c20e9c79ca7 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 20 Aug 2020 20:50:21 +0200 Subject: [PATCH 2465/2656] Align with currently used GitHub issue/PR labels --- CONTRIBUTING.md | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0032e99d..ab6cc89b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -103,23 +103,48 @@ The `type` labels define the type of issue or PR: - `type:bug`: a bug in existing functionality - `type:feature`: new functionality - `type:question`: a support question +- `type:cleanup`: cleanup or refactoring on code, documentation, or other areas +- `type:deprecation`: API that should warn and eventually be removed The `status` labels give a quick impression of the current status of the issue or PR: - `status:worksforme`: the issue it not reproducible, or intended behavior (i.e. not a bug) - `status:blocked`: further progress is blocked by a dependency, e.g. other code which must be commited first. -- `status:needinfo`: feedback from someone is required. The issue/PR text gives more details. +- `status:needs-info`: feedback from someone is required. The issue/PR text gives more details. - `status:duplicate`: the same issue is already being handled in another issue/PR. +- `status:close?`: issues which can probably be closed, but need a second pair of eyes +- `status:needs-proprietary-testing`: Help needed testing on a proprietary tool +- `status:out-of-scope`: An issue or PR that was closed because the feature or bug was deemed to be out of scope For the use in pull requests the following additional status labels are defined: -- `status:review-needed`: this PR needs at least one review +- `status:needs-review`: this PR needs at least one review - `status:changes-requested`: changes are requested to the code - `status:ready-for-merge`: this PR is ready (according to the [Patch Requirements](#patch-requirements)) to be merged +- `status:needs-rebase`: needs a git rebase +- `status:needs-newsfragment`: Needs a towncrier newsfragment for the changelog The `category` labels help maintainers to filter issues which are relevant to their area of expertise: -- `category:windows`: Microsoft Windows-specific issues +- `category:OS:MacOS`: Mac OS/OS X specific issues +- `category:OS:Linux`: Linux specific issues +- `category:OS:Windows`: Microsoft Windows-specific issues - `category:simulators`: simulator support, including VPI/GPI/etc. +- `category:simulators:activehdl`: Aldec Active-HDL +- `category:simulators:cvc`: Tachyon CVC +- `category:simulators:ghdl`: GHDL +- `category:simulators:icarus`: Icarus Verilog (iverilog) +- `category:simulators:ius`: Cadence Incisive (IUS) +- `category:simulators:modelsim`: Mentor Modelsim +- `category:simulators:questa`: Mentor Questa +- `category:simulators:riviera`: Aldec Riviera-PRO +- `category:simulators:vcs`: Synopsys VCS +- `category:simulators:verilator`: Verilator +- `category:simulators:xcelium`: Cadence Xcelium - `category:packaging`: issues related to (PyPi) packaging, etc. - `category:docs`: documentation issues and fixes +- `category:extensions`: cocotb extensions +- `category:performance`: performance topics +- `category:core`: Issues in the core of cocotb (scheduler, error reporting, etc.) +- `category:tests-ci`: continuous integration and unit tests +- `category:test-runner`: regarding the code for automating test runs To help new contributors find a good issue to work on one more label is used (following [GitHub standard practices](#https://help.github.com/articles/helping-new-contributors-find-your-project-with-labels/)): - `good first issue`: this issue is a good starting point for new contributors. From ed08f66129382ac27194c38d5ebde430cafe7a51 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 20 Aug 2020 20:27:43 +0200 Subject: [PATCH 2466/2656] Add term BFM to glossary --- documentation/source/glossary.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/documentation/source/glossary.rst b/documentation/source/glossary.rst index 68dee7db..bc58d9a1 100644 --- a/documentation/source/glossary.rst +++ b/documentation/source/glossary.rst @@ -5,10 +5,14 @@ Glossary .. glossary:: + BFM + Bus Functional Model + coroutine function The definition of a function that, when called, returns a coroutine object. Implemented using :keyword:`async` functions. See also the :term:`Python glossary `. + coroutine The result of calling a :term:`coroutine function`. Coroutines are not run immediately, you must either From c302b2bc60fe1898f6ae094f239e878abc5aa87a Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 20 Aug 2020 20:23:01 +0200 Subject: [PATCH 2467/2656] Add README explaining directory contents --- documentation/source/diagrams/README.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 documentation/source/diagrams/README.md diff --git a/documentation/source/diagrams/README.md b/documentation/source/diagrams/README.md new file mode 100644 index 00000000..2d4add6f --- /dev/null +++ b/documentation/source/diagrams/README.md @@ -0,0 +1,4 @@ +The sources for the vector diagrams (in `xml/`) can be edited with +[diagrams.net](https://app.diagrams.net/) - formerly called "draw.io". + +The SVG files in `svg/` are exported versions of those. From ad6dae71229ee7b698f7d4e05054f1f7c5035745 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Sun, 23 Aug 2020 12:21:01 +0100 Subject: [PATCH 2468/2656] Export functions created with GPI_ENTRY_POINT Fixes #2035 --- cocotb/share/lib/gpi/gpi_priv.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/share/lib/gpi/gpi_priv.h b/cocotb/share/lib/gpi/gpi_priv.h index 09e5b02a..0020ed0c 100644 --- a/cocotb/share/lib/gpi/gpi_priv.h +++ b/cocotb/share/lib/gpi/gpi_priv.h @@ -284,7 +284,7 @@ typedef void (*layer_entry_func)(); /* Use this macro in an implementation layer to define an entry point */ #define GPI_ENTRY_POINT(NAME, func) \ extern "C" { \ - void NAME##_entry_point() \ + COCOTB_EXPORT void NAME##_entry_point() \ { \ func(); \ } \ From 1ded05fc80f8c1b3ef68620c165c729e6a27612e Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 24 Aug 2020 22:14:08 +0200 Subject: [PATCH 2469/2656] Add doc for accessing names starting w/ underscore Closes #1315. --- documentation/source/writing_testbenches.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/documentation/source/writing_testbenches.rst b/documentation/source/writing_testbenches.rst index 8c1ed402..541e036d 100644 --- a/documentation/source/writing_testbenches.rst +++ b/documentation/source/writing_testbenches.rst @@ -13,3 +13,16 @@ It may not be clear when to raise an exception and when to use ``log.error()``. Even if you do eventually throw an exception (if you weren't able to do it immediately), you should also ``log.error()`` so that the simulation time of when things went wrong is recorded. TL;DR: ``log.error()`` is for humans only, ``raise`` is for both humans and code. + + +Accessing Identifiers Starting with an Underscore +================================================= + +The attribute syntax of ``dut._some_signal`` cannot be used to access +an identifier that starts with an underscore (``_``, as is valid in Verilog) +because we reserve such names for cocotb-internals, +thus the access will raise an :exc:`AttributeError`. + +A workaround is to use indirect access using +:meth:`~cocotb.handle.HierarchyObject._id` like in the following example: +``dut._id("_some_signal", extended=False)``. From bb88aff0cb6d1ce601d1c0a80f9aa55e095c96a2 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Thu, 27 Aug 2020 08:13:47 -0500 Subject: [PATCH 2470/2656] Revert "Distutils takes care of linking with libpython" This reverts commit 606f2485b082c917195f20b7e5b23ad8dcacd82a. --- cocotb_build_libs.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cocotb_build_libs.py b/cocotb_build_libs.py index 71c3a847..a602976c 100755 --- a/cocotb_build_libs.py +++ b/cocotb_build_libs.py @@ -334,6 +334,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): os.path.join("cocotb", "libs", "libgpilog"), define_macros=[("GPILOG_EXPORTS", "")] + _extra_defines, include_dirs=[include_dir], + library_dirs=python_lib_dirs, sources=libgpilog_sources, extra_link_args=_extra_link_args(lib_name="libgpilog", rpaths=["$ORIGIN"]), extra_compile_args=_extra_cxx_compile_args, @@ -351,7 +352,8 @@ def _get_common_lib_ext(include_dir, share_lib_dir): os.path.join("cocotb", "libs", "libcocotb"), define_macros=[("COCOTB_EMBED_EXPORTS", ""), ("PYTHON_SO_LIB", _get_python_lib())] + _extra_defines, include_dirs=[include_dir], - libraries=["gpilog", "cocotbutils"], + libraries=[_get_python_lib_link(), "gpilog", "cocotbutils"], + library_dirs=python_lib_dirs, sources=libcocotb_sources, extra_link_args=_extra_link_args(lib_name="libcocotb", rpaths=["$ORIGIN"]), extra_compile_args=_extra_cxx_compile_args, @@ -389,6 +391,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): define_macros=_extra_defines, include_dirs=[include_dir], libraries=["cocotbutils", "gpilog", "gpi"], + library_dirs=python_lib_dirs, sources=simulator_sources, extra_compile_args=_extra_cxx_compile_args, extra_link_args=_extra_link_args(rpaths=["$ORIGIN/libs"]), From 05d58bc4da6ec039280bb110759059cd21d6e87a Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Thu, 27 Aug 2020 20:05:14 -0500 Subject: [PATCH 2471/2656] Revert "Fix runtime loading of python on macos" This reverts commit 62858880cc560d165bee9a7059140215b4873a2c. --- cocotb_build_libs.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/cocotb_build_libs.py b/cocotb_build_libs.py index a602976c..887f717f 100755 --- a/cocotb_build_libs.py +++ b/cocotb_build_libs.py @@ -270,17 +270,9 @@ def _get_python_lib(): """ Get the library for embedded the python interpreter """ if os.name == "nt": - python_lib = _get_python_lib_link() + ".dll" + python_lib = _get_python_lib_link() + "." + _get_lib_ext_name() else: - python_lib = "lib" + _get_python_lib_link() + "." - if sys.platform == "darwin": - python_lib = os.path.join(sysconfig.get_config_var("LIBDIR"), python_lib) - if os.path.exists(python_lib + "dylib"): - python_lib += "dylib" - else: - python_lib += "so" - else: - python_lib += "so" + python_lib = "lib" + _get_python_lib_link() + "." + _get_lib_ext_name() return python_lib @@ -325,6 +317,10 @@ def _get_common_lib_ext(include_dir, share_lib_dir): # # libgpilog # + python_lib_dirs = [] + if sys.platform == "darwin": + python_lib_dirs = [sysconfig.get_config_var("LIBDIR")] + libgpilog_sources = [ os.path.join(share_lib_dir, "gpi_log", "gpi_logging.cpp") ] @@ -355,7 +351,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): libraries=[_get_python_lib_link(), "gpilog", "cocotbutils"], library_dirs=python_lib_dirs, sources=libcocotb_sources, - extra_link_args=_extra_link_args(lib_name="libcocotb", rpaths=["$ORIGIN"]), + extra_link_args=_extra_link_args(lib_name="libcocotb", rpaths=["$ORIGIN"] + python_lib_dirs), extra_compile_args=_extra_cxx_compile_args, ) From 85e2cfe596a304204f217d25baa41fca77260aa2 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sat, 29 Aug 2020 18:55:47 +0200 Subject: [PATCH 2472/2656] Add references to terms in glossary (#2040) --- documentation/source/building.rst | 14 ++++++------ documentation/source/endian_swapper.rst | 12 +++++----- documentation/source/examples.rst | 6 ++--- documentation/source/glossary.rst | 3 +++ documentation/source/hal_cosimulation.rst | 26 +++++++++++----------- documentation/source/index.rst | 10 ++++----- documentation/source/install.rst | 2 +- documentation/source/ping_tun_tap.rst | 14 ++++++------ documentation/source/quickstart.rst | 18 +++++++-------- documentation/source/simulator_support.rst | 16 ++++++------- documentation/source/testbench_tools.rst | 14 ++++++------ documentation/source/troubleshooting.rst | 4 ++-- 12 files changed, 71 insertions(+), 68 deletions(-) diff --git a/documentation/source/building.rst b/documentation/source/building.rst index 4da9c363..7d39ffce 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -26,7 +26,7 @@ Make Phases ----------- Typically the makefiles provided with cocotb for various simulators use a separate ``compile`` and ``run`` target. -This allows for a rapid re-running of a simulator if none of the RTL source files have changed and therefore the simulator does not need to recompile the RTL. +This allows for a rapid re-running of a simulator if none of the :term:`RTL` source files have changed and therefore the simulator does not need to recompile the :term:`RTL`. Variables @@ -42,7 +42,7 @@ Cocotb .. envvar:: TOPLEVEL - Use this to indicate the instance in the hierarchy to use as the DUT. + Use this to indicate the instance in the hierarchy to use as the :term:`DUT`. If this isn't defined then the first root instance is used. .. envvar:: RANDOM_SEED @@ -167,7 +167,7 @@ Regression Manager .. envvar:: COVERAGE - Enable to report Python coverage data. For some simulators, this will also report HDL coverage. + Enable to report Python coverage data. For some simulators, this will also report :term:`HDL` coverage. This needs the :mod:`coverage` Python module to be installed. @@ -192,7 +192,7 @@ GPI A comma-separated list of extra libraries that are dynamically loaded at runtime. A function from each of these libraries will be called as an entry point prior to elaboration, allowing these libraries to register - system functions and callbacks. Note that HDL objects cannot be accessed at this time. + system functions and callbacks. Note that :term:`HDL` objects cannot be accessed at this time. The function name defaults to ``{library_name}_entry_point``, but a custom name can be specified using a ``:``, which follows an existing simulator convention. For example: @@ -223,9 +223,9 @@ Makefile-based Test Scripts .. make:var:: TOPLEVEL_LANG - Used to inform the makefile scripts which HDL language the top-level design element is written in. + Used to inform the makefile scripts which :term:`HDL` language the top-level design element is written in. Currently it supports the values ``verilog`` for Verilog or SystemVerilog tops, and ``vhdl`` for VHDL tops. - This is used by simulators that support more than one interface (VPI, VHPI, or FLI) to select the appropriate interface to start cocotb. + This is used by simulators that support more than one interface (:term:`VPI`, :term:`VHPI`, or :term:`FLI`) to select the appropriate interface to start cocotb. The variable is also made available to cocotb tests conveniently as :data:`cocotb.LANGUAGE`. @@ -292,7 +292,7 @@ Makefile-based Test Scripts .. make:var:: CUSTOM_COMPILE_DEPS - Use to add additional dependencies to the compilation target; useful for defining additional rules to run pre-compilation or if the compilation phase depends on files other than the RTL sources listed in :make:var:`VERILOG_SOURCES` or :make:var:`VHDL_SOURCES`. + Use to add additional dependencies to the compilation target; useful for defining additional rules to run pre-compilation or if the compilation phase depends on files other than the :term:`RTL` sources listed in :make:var:`VERILOG_SOURCES` or :make:var:`VHDL_SOURCES`. .. make:var:: CUSTOM_SIM_DEPS diff --git a/documentation/source/endian_swapper.rst b/documentation/source/endian_swapper.rst index 2699230d..23f5e521 100644 --- a/documentation/source/endian_swapper.rst +++ b/documentation/source/endian_swapper.rst @@ -16,12 +16,12 @@ You can run this example from a fresh checkout:: Design ====== -We have a relatively simplistic RTL block called the ``endian_swapper``. -The DUT has three interfaces, all conforming to the Avalon standard: +We have a relatively simplistic :term:`RTL` block called the ``endian_swapper``. +The :term:`DUT` has three interfaces, all conforming to the Avalon standard: .. image:: diagrams/svg/endian_swapper_design.svg -The DUT will swap the endianness of packets on the Avalon-ST bus if a configuration bit is set. +The :term:`DUT` will swap the endianness of packets on the Avalon-ST bus if a configuration bit is set. For every packet arriving on the ``stream_in`` interface the entire packet will be endian swapped if the configuration bit is set, otherwise the entire packet will pass through unmodified. @@ -97,7 +97,7 @@ We do the same to create the :class:`monitor ` to determine the status of the test. diff --git a/documentation/source/examples.rst b/documentation/source/examples.rst index 6ae51589..5bcc0569 100644 --- a/documentation/source/examples.rst +++ b/documentation/source/examples.rst @@ -9,7 +9,7 @@ the directory :file:`cocotb/examples/` contains some more smaller modules you ma Adder ===== -The directory :file:`cocotb/examples/adder/` contains an ``adder`` RTL in both Verilog and VHDL, +The directory :file:`cocotb/examples/adder/` contains an ``adder`` :term:`RTL` in both Verilog and VHDL, an ``adder_model`` implemented in Python, and the cocotb testbench with two defined tests ­ a simple :func:`adder_basic_test` and a slightly more advanced :func:`adder_randomised_test`. @@ -22,7 +22,7 @@ D Flip-Flop The directory :file:`cocotb/examples/dff/` contains a simple D flip-flop, implemented in both VDHL and Verilog. -The HDL has the data input port ``d``, the clock port ``c``, and the data output ``q`` with an initial state of ``0``. +The :term:`HDL` has the data input port ``d``, the clock port ``c``, and the data output ``q`` with an initial state of ``0``. No reset port exists. The cocotb testbench checks the initial state first, then applies random data to the data input. @@ -52,7 +52,7 @@ feed a :class:`.Scoreboard` with the collected transactions on input bus ``i``. Mixed Language ============== -The directory :file:`cocotb/examples/mixed_language/` contains two toplevel HDL files, +The directory :file:`cocotb/examples/mixed_language/` contains two toplevel :term:`HDL` files, one in VHDL, one in SystemVerilog, that each instantiate the ``endian_swapper`` in SystemVerilog and VHDL in parallel and chains them together so that the endianness is swapped twice. diff --git a/documentation/source/glossary.rst b/documentation/source/glossary.rst index bc58d9a1..2cd653ff 100644 --- a/documentation/source/glossary.rst +++ b/documentation/source/glossary.rst @@ -32,6 +32,9 @@ Glossary GPI General Procedural Interface, cocotb's abstraction over :term:`VPI`, :term:`VHPI`, and :term:`FLI`. + HAL + Hardware Abstraction Layer + HDL Hardware Description Language diff --git a/documentation/source/hal_cosimulation.rst b/documentation/source/hal_cosimulation.rst index f55c5d2e..dc6dd0ca 100644 --- a/documentation/source/hal_cosimulation.rst +++ b/documentation/source/hal_cosimulation.rst @@ -34,20 +34,20 @@ Calling the HAL from a test --------------------------- Typically the software component (often referred to as a Hardware Abstraction -Layer or HAL) is written in C. We need to call this software from our test +Layer or :term:`HAL`) is written in C. We need to call this software from our test written in Python. There are multiple ways to call C code from Python, in -this tutorial we'll use `SWIG`_ to generate Python bindings for our HAL. +this tutorial we'll use `SWIG`_ to generate Python bindings for our :term:`HAL`. Blocking in the driver ---------------------- -Another difficulty to overcome is the fact that the HAL is expecting to call +Another difficulty to overcome is the fact that the :term:`HAL` is expecting to call a low-level function to access the hardware, often something like ``ioread32``. We need this call to block while simulation time advances and a value is -either read or written on the bus. To achieve this we link the HAL against +either read or written on the bus. To achieve this we link the :term:`HAL` against a C library that provides the low level read/write functions. These functions -in turn call into cocotb and perform the relevant access on the DUT. +in turn call into cocotb and perform the relevant access on the :term:`DUT`. Cocotb infrastructure @@ -87,8 +87,8 @@ The endian swapper has a very simple register map: HAL --- -To keep things simple we use the same RTL from the :doc:`endian_swapper`. We -write a simplistic HAL which provides the following functions: +To keep things simple we use the same :term:`RTL` from the :doc:`endian_swapper`. We +write a simplistic :term:`HAL` which provides the following functions: .. code-block:: c @@ -104,8 +104,8 @@ NIOS framework. IO Module --------- -This module acts as the bridge between the C HAL and the Python testbench. It -exposes the ``IORD`` and ``IOWR`` calls to link the HAL against, but also +This module acts as the bridge between the C :term:`HAL` and the Python testbench. It +exposes the ``IORD`` and ``IOWR`` calls to link the :term:`HAL` against, but also provides a Python interface to allow the read/write bindings to be dynamically set (through ``set_write_function`` and ``set_read_function`` module functions). @@ -118,9 +118,9 @@ Testbench --------- First of all we set up a clock, create an :class:`Avalon Master ` -interface and reset the DUT. +interface and reset the :term:`DUT`. Then we create two functions that are wrapped with the :class:`cocotb.function` decorator -to be called when the HAL attempts to perform a read or write. +to be called when the :term:`HAL` attempts to perform a read or write. These are then passed to the `IO Module`_: @@ -144,7 +144,7 @@ These are then passed to the `IO Module`_: io_module.set_read_function(read) -We can then initialize the HAL and call functions, using the :class:`cocotb.external` +We can then initialize the :term:`HAL` and call functions, using the :class:`cocotb.external` decorator to turn the normal function into a blocking coroutine that we can :keyword:`yield`: @@ -154,7 +154,7 @@ decorator to turn the normal function into a blocking coroutine that we can yield cocotb.external(hal.endian_swapper_enable)(state) -The HAL will perform whatever calls it needs, accessing the DUT through the +The :term:`HAL` will perform whatever calls it needs, accessing the :term:`DUT` through the :class:`Avalon-MM driver `, and control will return to the testbench when the function returns. diff --git a/documentation/source/index.rst b/documentation/source/index.rst index 2112e0f7..65b6477d 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -15,11 +15,11 @@ Welcome to cocotb's documentation! What is cocotb? *************** -**cocotb** is a *COroutine* based *COsimulation* *TestBench* environment for verifying VHDL and SystemVerilog RTL using `Python `_. +**cocotb** is a *COroutine* based *COsimulation* *TestBench* environment for verifying VHDL and SystemVerilog :term:`RTL` using `Python `_. cocotb is completely free, open source (under the `BSD License `_) and hosted on `GitHub `_. -cocotb requires a simulator to simulate the HDL design +cocotb requires a simulator to simulate the :term:`HDL` design and has been used with a variety of simulators on Linux, Windows and macOS. Please check the :ref:`simulator-support` page for specifics. @@ -56,9 +56,9 @@ All verification is done using Python which has various advantages over using Sy How does cocotb work? ********************* -A typical cocotb testbench requires no additional RTL code. -The Design Under Test (DUT) is instantiated as the toplevel in the simulator without any wrapper code. -cocotb drives stimulus onto the inputs to the DUT (or further down the hierarchy) and monitors the outputs directly from Python. +A typical cocotb testbench requires no additional :term:`RTL` code. +The Design Under Test (:term:`DUT`) is instantiated as the toplevel in the simulator without any wrapper code. +cocotb drives stimulus onto the inputs to the :term:`DUT` (or further down the hierarchy) and monitors the outputs directly from Python. .. image:: diagrams/svg/cocotb_overview.svg diff --git a/documentation/source/install.rst b/documentation/source/install.rst index 98e13698..4a1ccb72 100644 --- a/documentation/source/install.rst +++ b/documentation/source/install.rst @@ -20,7 +20,7 @@ Cocotb has the following requirements: * Python-dev packages * GCC 4.8.1+ or Clang 3.3+ and associated development packages * GNU Make -* A Verilog or VHDL simulator, depending on your RTL source code +* A Verilog or VHDL simulator, depending on your :term:`RTL` source code .. versionchanged:: 1.4 Dropped Python 2 support diff --git a/documentation/source/ping_tun_tap.rst b/documentation/source/ping_tun_tap.rst index 3940ed86..8e031876 100644 --- a/documentation/source/ping_tun_tap.rst +++ b/documentation/source/ping_tun_tap.rst @@ -4,7 +4,7 @@ Tutorial: Ping One of the benefits of Python is the ease with which interfacing is possible. In this tutorial we'll look at interfacing the standard GNU `ping`_ command -to the simulator. Using Python we can ping our DUT with fewer than 50 lines of +to the simulator. Using Python we can ping our :term:`DUT` with fewer than 50 lines of code. For the impatient this tutorial is provided as an example with cocotb. You can @@ -22,13 +22,13 @@ run this example from a fresh checkout: Architecture ============ -We have a simple RTL block that takes ICMP echo requests and generates an ICMP +We have a simple :term:`RTL` block that takes ICMP echo requests and generates an ICMP echo response. To verify this behavior we want to run the `ping`_ utility -against our RTL running in the simulator. +against our :term:`RTL` running in the simulator. In order to achieve this we need to capture the packets that are created by -ping, drive them onto the pins of our DUT in simulation, monitor the output of -the DUT and send any responses back to the ping process. +ping, drive them onto the pins of our :term:`DUT` in simulation, monitor the output of +the :term:`DUT` and send any responses back to the ping process. Linux has a `TUN/TAP`_ virtual network device which we can use for this purpose, allowing `ping`_ to run unmodified and unaware that it is @@ -63,7 +63,7 @@ we write a function that will create our virtual interface: Now we can get started on the actual test. First of all we'll create a clock signal and connect up the :class:`Avalon driver ` and -:class:`monitor ` to the DUT. To help debug +:class:`monitor ` to the :term:`DUT`. To help debug the testbench we'll enable verbose debug on the drivers and monitors by setting the log level to ``logging.DEBUG``. @@ -86,7 +86,7 @@ the log level to ``logging.DEBUG``. stream_out.log.setLevel(logging.DEBUG) -We also need to reset the DUT and drive some default values onto some of the +We also need to reset the :term:`DUT` and drive some default values onto some of the bus signals. Note that we'll need to import the :class:`~.triggers.Timer` and :class:`~.triggers.RisingEdge` triggers. diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index bb590b69..478ebaac 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -36,10 +36,10 @@ but selecting a different simulator is as easy as: Running the same example as VHDL -------------------------------- -The ``simple_dff`` example includes both a VHDL and a Verilog RTL implementation. -The cocotb testbench can execute against either implementation using VPI for -Verilog and VHPI/FLI for VHDL. To run the test suite against the VHDL -implementation, use the following command (a VHPI or FLI capable simulator must +The ``simple_dff`` example includes both a VHDL and a Verilog :term:`RTL` implementation. +The cocotb testbench can execute against either implementation using :term:`VPI` for +Verilog and :term:`VHPI`/:term:`FLI` for VHDL. To run the test suite against the VHDL +implementation, use the following command (a :term:`VHPI` or :term:`FLI` capable simulator must be used): .. code-block:: bash @@ -50,10 +50,10 @@ be used): Using cocotb ============ -A typical cocotb testbench requires no additional HDL code (though nothing prevents you from adding testbench helper code). -The Design Under Test (DUT) is instantiated as the toplevel in the simulator +A typical cocotb testbench requires no additional :term:`HDL` code (though nothing prevents you from adding testbench helper code). +The Design Under Test (:term:`DUT`) is instantiated as the toplevel in the simulator without any wrapper code. -Cocotb drives stimulus onto the inputs to the DUT and monitors the outputs +Cocotb drives stimulus onto the inputs to the :term:`DUT` and monitors the outputs directly from Python. @@ -84,7 +84,7 @@ Creating a test The test is written in Python. Cocotb wraps your top level with the handle you pass it. In this documentation, and most of the examples in the project, that handle is ``dut``, but you can pass your own preferred name in instead. The -handle is used in all Python files referencing your RTL project. Assuming we +handle is used in all Python files referencing your :term:`RTL` project. Assuming we have a toplevel port called ``clk`` we could create a test file containing the following: @@ -147,7 +147,7 @@ or using direct assignment while traversing the hierarchy. The syntax ``sig <= new_value`` is a short form of ``sig.value = new_value``. -It not only resembles HDL syntax, but also has the same semantics: +It not only resembles :term:`HDL` syntax, but also has the same semantics: writes are not applied immediately, but delayed until the next write cycle. Use ``sig.setimmediatevalue(new_val)`` to set a new value immediately (see :meth:`~cocotb.handle.NonHierarchyObject.setimmediatevalue`). diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index b0458690..f1bf7365 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -98,7 +98,7 @@ If your design's clocks vary in precision, the performance of the simulation can Coverage -------- -To enable HDL code coverage, add Verilator's coverage option(s) to the :make:var:`EXTRA_ARGS` make variable, for example: +To enable :term:`HDL` code coverage, add Verilator's coverage option(s) to the :make:var:`EXTRA_ARGS` make variable, for example: .. code-block:: make @@ -140,7 +140,7 @@ In order to use this simulator, set :make:var:`SIM` to ``vcs``: make SIM=vcs -cocotb currently only supports VPI for Synopsys VCS, not VHPI. +cocotb currently only supports :term:`VPI` for Synopsys VCS, not :term:`VHPI`. .. _sim-vcs-issues: @@ -244,17 +244,17 @@ In order to use this simulator, set :make:var:`SIM` to ``modelsim``: .. note:: - In order to use FLI (for VHDL), a ``vdbg`` executable from the simulator installation directory needs to be available on the ``PATH`` during cocotb installation. + In order to use :term:`FLI` (for VHDL), a ``vdbg`` executable from the simulator installation directory needs to be available on the ``PATH`` during cocotb installation. This is needed to access the proprietary ``mti.h`` header file. -Any ModelSim PE or ModelSim PE derivatives (like the ModelSim Microsemi, Intel, Lattice Editions) do not support the VHDL FLI feature. -If you try to use them with FLI, you will see a ``vsim-FLI-3155`` error: +Any ModelSim PE or ModelSim PE derivatives (like the ModelSim Microsemi, Intel, Lattice Editions) do not support the VHDL :term:`FLI` feature. +If you try to use them with :term:`FLI`, you will see a ``vsim-FLI-3155`` error: .. code-block:: bash ** Error (suppressible): (vsim-FLI-3155) The FLI is not enabled in this version of ModelSim. -ModelSim DE and SE (and Questa, of course) support the FLI. +ModelSim DE and SE (and Questa, of course) support the :term:`FLI`. .. _sim-modelsim-issues: @@ -296,7 +296,7 @@ In order to use this simulator, set :make:var:`SIM` to ``xcelium``: make SIM=xcelium -The simulator automatically loads VPI even when only VHPI is requested. +The simulator automatically loads :term:`VPI` even when only :term:`VHPI` is requested. .. _sim-xcelium-issues: @@ -318,7 +318,7 @@ In order to use this simulator, set :make:var:`SIM` to ``ghdl``: make SIM=ghdl Support is preliminary. -Noteworthy is that despite GHDL being a VHDL simulator, it implements the VPI interface. +Noteworthy is that despite GHDL being a VHDL simulator, it implements the :term:`VPI` interface. .. _sim-ghdl-issues: diff --git a/documentation/source/testbench_tools.rst b/documentation/source/testbench_tools.rst index f024f7f0..1b3ee8ae 100644 --- a/documentation/source/testbench_tools.rst +++ b/documentation/source/testbench_tools.rst @@ -6,11 +6,11 @@ Logging ======= Cocotb uses the builtin :mod:`logging` library, with some configuration described in :ref:`logging-reference-section` to provide some sensible defaults. -Each DUT, monitor, driver, and scoreboard (as well as any other function using the coroutine decorator) holds a :class:`logging.Logger`, and each can be set to its own logging level. -Within a DUT, each hierarchical object can also have individual logging levels set. +Each :term:`DUT`, monitor, driver, and scoreboard (as well as any other function using the coroutine decorator) holds a :class:`logging.Logger`, and each can be set to its own logging level. +Within a :term:`DUT`, each hierarchical object can also have individual logging levels set. -When logging HDL objects, beware that ``_log`` is the preferred way to use -logging. This helps minimize the change of name collisions with an HDL log +When logging :term:`HDL` objects, beware that ``_log`` is the preferred way to use +logging. This helps minimize the change of name collisions with an :term:`HDL` log component with the Python logging functionality. Log printing levels can also be set on a per-object basis. @@ -131,9 +131,9 @@ Monitor class is a base class which you are expected to derive for your particular purpose. You must create a :any:`_monitor_recv()` function which is responsible for determining 1) at what points in simulation to call the :any:`_recv()` function, and 2) what transaction values to pass to be stored in the -monitors receiving queue. Monitors are good for both outputs of the DUT for -verification, and for the inputs of the DUT, to drive a test model of the DUT -to be compared to the actual DUT. For this purpose, input monitors will often +monitors receiving queue. Monitors are good for both outputs of the :term:`DUT` for +verification, and for the inputs of the :term:`DUT`, to drive a test model of the :term:`DUT` +to be compared to the actual :term:`DUT`. For this purpose, input monitors will often have a callback function passed that is a model. This model will often generate expected transactions, which are then compared using the :class:`.Scoreboard` class. diff --git a/documentation/source/troubleshooting.rst b/documentation/source/troubleshooting.rst index abb7b700..6032e5d0 100644 --- a/documentation/source/troubleshooting.rst +++ b/documentation/source/troubleshooting.rst @@ -12,7 +12,7 @@ i.e. without using :keyword:`await` or :keyword:`yield`? Increasing Verbosity ==================== -If things fail in the VPI/VHPI/FLI area, check your simulator's documentation to see if it has options to +If things fail in the :term:`VPI`/:term:`VHPI`/:term:`FLI` area, check your simulator's documentation to see if it has options to increase its verbosity about what may be wrong. You can then set these options on the :command:`make` command line as :make:var:`COMPILE_ARGS`, :make:var:`SIM_ARGS` or :make:var:`EXTRA_ARGS` (see :doc:`building` for details). If things fail from within Python, or coroutines aren't being called when you expect, the @@ -36,7 +36,7 @@ Python ------ When executing the Makefile to run a cocotb test, a Python shell interpreter is called from within the -VPI/VHPI/FLI library. +:term:`VPI`/:term:`VHPI`/:term:`FLI` library. Hence it is not possible to directly attach a Python debugger to the Python process being part of the simulator that uses the aforementioned library. Using ``import pdb; pdb.set_trace()`` directly is also frequently not possible, due to the way that simulators interfere with stdin. From 78126f00de57313cb4366d1422ab407fb1c99a2a Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 3 Sep 2020 19:47:14 +0200 Subject: [PATCH 2473/2656] Document generators in Python library reference --- cocotb/generators/__init__.py | 10 +++--- cocotb/generators/bit.py | 16 ++++----- cocotb/generators/byte.py | 8 ++--- documentation/source/library_reference.rst | 39 ++++++++++++++++++++++ 4 files changed, 54 insertions(+), 19 deletions(-) mode change 100644 => 100755 cocotb/generators/byte.py diff --git a/cocotb/generators/__init__.py b/cocotb/generators/__init__.py index 600fc87e..a0965430 100644 --- a/cocotb/generators/__init__.py +++ b/cocotb/generators/__init__.py @@ -40,9 +40,7 @@ def repeat(obj, nrepeat=None): Args: obj (any): The object to yield - - Kwargs: - nrepeat (int): The number of times to repeatedly yield obj + nrepeat (int, optional): The number of times to repeatedly yield *obj* """ if nrepeat is None: return itertools.repeat(obj) @@ -69,7 +67,7 @@ def gaussian(mean, sigma): Args: mean (int/float): mean value - signma (int/float): Standard deviation + sigma (int/float): Standard deviation """ while True: yield random.gauss(mean, sigma) @@ -81,7 +79,7 @@ def sine_wave(amplitude, w, offset=0): Generates a sine wave that repeats forever Args: - amplitude (int/float): peak deviation of the function from zero + amplitude (int/float): peak deviation of the function from zero w (int/float): is the rate of change of the function argument @@ -95,7 +93,7 @@ def sine_wave(amplitude, w, offset=0): def get_generators(module): - """Return an iterator which yields all the generators in a module + """Return an iterator which yields all the generators in a module Args: module (python module): The module to get the generators from diff --git a/cocotb/generators/bit.py b/cocotb/generators/bit.py index 07beead2..92ac3e00 100644 --- a/cocotb/generators/bit.py +++ b/cocotb/generators/bit.py @@ -32,7 +32,7 @@ cycles onto a bus. These yield a tuple which is intended to be interpreted as a number of - cycles (ON,OFF) + cycles ``(ON,OFF)`` """ from cocotb.decorators import public from cocotb.generators import gaussian, sine_wave, repeat @@ -43,7 +43,6 @@ def bit_toggler(gen_on, gen_off): Args: gen_on (generator): generator that yields number of cycles on - gen_off (generator): generator that yields number of cycles off """ for n_on, n_off in zip(gen_on, gen_off): @@ -54,10 +53,9 @@ def bit_toggler(gen_on, gen_off): def intermittent_single_cycles(mean=10, sigma=None): """Generator to intermittently insert a single cycle pulse - Kwargs: - mean (int): Average number of cycles in between single cycle gaps - - sigma (int): Standard deviation of gaps. mean/4 if sigma is None + Args: + mean (int, optional): Average number of cycles in between single cycle gaps + sigma (int, optional): Standard deviation of gaps. mean/4 if sigma is None """ if sigma is None: sigma = mean / 4.0 @@ -68,10 +66,10 @@ def intermittent_single_cycles(mean=10, sigma=None): @public def random_50_percent(mean=10, sigma=None): """50% duty cycle with random width - Kwargs: - mean (int): Average number of cycles on/off - sigma (int): Standard deviation of gaps. mean/4 if sigma is None + Args: + mean (int, optional): Average number of cycles on/off + sigma (int, optional): Standard deviation of gaps. mean/4 if sigma is None """ if sigma is None: sigma = mean / 4.0 diff --git a/cocotb/generators/byte.py b/cocotb/generators/byte.py old mode 100644 new mode 100755 index 6d34b7f4..9189c165 --- a/cocotb/generators/byte.py +++ b/cocotb/generators/byte.py @@ -29,7 +29,7 @@ """ - Collection of generators for creating byte streams + Collection of generators for creating byte streams. Note that on Python 3, individual bytes are represented with integers. """ @@ -44,7 +44,7 @@ def get_bytes(nbytes: int, generator: Iterator[int]) -> bytes: """Get nbytes from generator .. versionchanged:: 1.4.0 - This now returns :type:`bytes` not :type:`str`. """ + This now returns :class:`bytes`, not :class:`str`. """ return bytes(next(generator) for i in range(nbytes)) @@ -53,7 +53,7 @@ def random_data() -> Iterator[int]: r"""Random bytes .. versionchanged:: 1.4.0 - This now returns integers not single-character :type:`str`\ s. """ + This now returns integers, not single-character :class:`str`\ s. """ while True: yield random.randrange(256) @@ -63,7 +63,7 @@ def incrementing_data(increment=1) -> Iterator[int]: r"""Incrementing bytes .. versionchanged:: 1.4.0 - This now returns integers not single-character :type:`str`\ s. """ + This now returns integers, not single-character :class:`str`\ s. """ val = 0 while True: yield val diff --git a/documentation/source/library_reference.rst b/documentation/source/library_reference.rst index 96360e9e..21f364e1 100644 --- a/documentation/source/library_reference.rst +++ b/documentation/source/library_reference.rst @@ -130,6 +130,45 @@ Scoreboard :show-inheritance: :synopsis: Class for scoreboards. +Generators +---------- + +.. currentmodule:: cocotb.generators + +.. automodule:: cocotb.generators + :members: + :member-order: bysource + :show-inheritance: + :synopsis: Class for generators. + +Bit +^^^ + +.. automodule:: cocotb.generators.bit + :members: + :member-order: bysource + :show-inheritance: + + +Byte +^^^^ + +.. automodule:: cocotb.generators.byte + :members: + :member-order: bysource + :show-inheritance: + +.. + Needs scapy + + Packet + ^^^^^^ + + .. automodule:: cocotb.generators.packet + :members: + :member-order: bysource + :show-inheritance: + Clock ----- From f167af2c27797b176961e5049140d8af4a2bd060 Mon Sep 17 00:00:00 2001 From: Fabio Garzetti Date: Wed, 26 Aug 2020 18:50:48 +0200 Subject: [PATCH 2474/2656] Fix GHDL Makefile for builds with multiple libraries Commit a3fb4b5 changed the Makefile of GHDL, replacing a `cd $(SIM_BUILD)` with the `--workdir` argument of GHDL, which however changes only the path of the current WORK library. Add the `-P$(SIM_DIR)` argument to specify the path for all the other libraries. --- cocotb/share/makefiles/simulators/Makefile.ghdl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.ghdl b/cocotb/share/makefiles/simulators/Makefile.ghdl index f8e16abe..1c8a6550 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ghdl +++ b/cocotb/share/makefiles/simulators/Makefile.ghdl @@ -68,13 +68,13 @@ analyse: $(VHDL_SOURCES) $(CUSTOM_COMPILE_DEPS) | $(SIM_BUILD) $(foreach SOURCES_VAR, $(filter VHDL_SOURCES_%, $(.VARIABLES)), \ $(CMD) -i $(GHDL_ARGS) $(COMPILE_ARGS) --workdir=$(SIM_BUILD) --work=$(SOURCES_VAR:VHDL_SOURCES_%=%) $($(SOURCES_VAR)) && ) \ $(CMD) -i $(GHDL_ARGS) $(COMPILE_ARGS) --workdir=$(SIM_BUILD) --work=$(RTL_LIBRARY) $(VHDL_SOURCES) && \ - $(CMD) -m $(GHDL_ARGS) $(COMPILE_ARGS) --workdir=$(SIM_BUILD) --work=$(RTL_LIBRARY) $(TOPLEVEL) + $(CMD) -m $(GHDL_ARGS) $(COMPILE_ARGS) --workdir=$(SIM_BUILD) -P$(SIM_BUILD) --work=$(RTL_LIBRARY) $(TOPLEVEL) $(COCOTB_RESULTS_FILE): analyse $(CUSTOM_SIM_DEPS) -@rm -f $(COCOTB_RESULTS_FILE) MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(CMD) -r $(GHDL_ARGS) --workdir=$(SIM_BUILD) --work=$(RTL_LIBRARY) $(TOPLEVEL) --vpi=$(LIB_DIR)/libcocotbvpi_ghdl.$(LIB_EXT) $(SIM_ARGS) $(PLUSARGS) + $(CMD) -r $(GHDL_ARGS) --workdir=$(SIM_BUILD) -P$(SIM_BUILD) --work=$(RTL_LIBRARY) $(TOPLEVEL) --vpi=$(LIB_DIR)/libcocotbvpi_ghdl.$(LIB_EXT) $(SIM_ARGS) $(PLUSARGS) $(call check_for_results_file) From 8261c3cbba30e781c12f75fb7445d3085a45ebb9 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 13 Jul 2020 21:16:06 -0500 Subject: [PATCH 2475/2656] Add detection of TOPLEVEL_LANG based on *_SOURCES if not specified --- cocotb/share/makefiles/Makefile.inc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cocotb/share/makefiles/Makefile.inc b/cocotb/share/makefiles/Makefile.inc index 87e3b00a..27ab7333 100644 --- a/cocotb/share/makefiles/Makefile.inc +++ b/cocotb/share/makefiles/Makefile.inc @@ -111,6 +111,17 @@ $(SIM_BUILD): .PHONY: regression regression: $(COCOTB_RESULTS_FILE) +# Attempt to detect TOPLEVEL_LANG based on available sources if not set +ifeq ($(TOPLEVEL_LANG),) + +ifneq ($(and $(VHDL_SOURCES),$(if $(VERILOG_SOURCES),,1),) + TOPLEVEL_LANG := vhdl +else ifneq ($(and $(VERILOG_SOURCES),$(if $(VHDL_SOURCES),,1),) + TOPLEVEL_LANG := verilog +endif + +endif + else $(warning Including Makefile.inc from a user makefile is a no-op and deprecated. Remove the Makefile.inc inclusion from your makefile, and only leave the Makefile.sim include.) endif # COCOTB_MAKEFILE_INC_INCLUDED From 91f8dc2d8c0fa8c7552c7f749e45a6eca4db09e4 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 13 Jul 2020 21:16:41 -0500 Subject: [PATCH 2476/2656] Detect invalid TOPLEVEL_LANG in Icarus, GHDL, and Verilator Using the *_SOURCES variables to determine if the wrong language is used is not effective, since those variables will be ignored. However, setting TOPLEVEL_LANG incorrectly could cause in tests that support more than language throug the use of : --- cocotb/share/makefiles/simulators/Makefile.ghdl | 6 ++++-- cocotb/share/makefiles/simulators/Makefile.icarus | 6 ++++-- cocotb/share/makefiles/simulators/Makefile.verilator | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.ghdl b/cocotb/share/makefiles/simulators/Makefile.ghdl index 1c8a6550..e474ff49 100644 --- a/cocotb/share/makefiles/simulators/Makefile.ghdl +++ b/cocotb/share/makefiles/simulators/Makefile.ghdl @@ -26,12 +26,14 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +TOPLEVEL_LANG ?= vhdl + include $(shell cocotb-config --makefiles)/Makefile.inc -ifneq ($(VERILOG_SOURCES),) +ifneq ($(or $(filter-out $(TOPLEVEL_LANG),vhdl),$(VERILOG_SOURCES)),) $(COCOTB_RESULTS_FILE): - @echo "Skipping simulation as Verilog is not supported on simulator=$(SIM)" + @echo "Skipping simulation as only VHDL is supported on simulator=$(SIM)" clean:: else diff --git a/cocotb/share/makefiles/simulators/Makefile.icarus b/cocotb/share/makefiles/simulators/Makefile.icarus index 725662cc..07a7444a 100644 --- a/cocotb/share/makefiles/simulators/Makefile.icarus +++ b/cocotb/share/makefiles/simulators/Makefile.icarus @@ -27,12 +27,14 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### +TOPLEVEL_LANG ?= verilog + include $(shell cocotb-config --makefiles)/Makefile.inc -ifneq ($(VHDL_SOURCES),) +ifneq ($(or $(filter-out $(TOPLEVEL_LANG),verilog),$(VHDL_SOURCES)),) $(COCOTB_RESULTS_FILE): - @echo "Skipping simulation as VHDL is not supported on simulator=$(SIM)" + @echo "Skipping simulation as only Verilog is supported on simulator=$(SIM)" debug: $(COCOTB_RESULTS_FILE) clean:: diff --git a/cocotb/share/makefiles/simulators/Makefile.verilator b/cocotb/share/makefiles/simulators/Makefile.verilator index 25f55c67..c890ffd1 100644 --- a/cocotb/share/makefiles/simulators/Makefile.verilator +++ b/cocotb/share/makefiles/simulators/Makefile.verilator @@ -2,12 +2,14 @@ # Licensed under the Revised BSD License, see LICENSE for details. # SPDX-License-Identifier: BSD-3-Clause +TOPLEVEL_LANG ?= verilog + include $(shell cocotb-config --makefiles)/Makefile.inc -ifneq ($(VHDL_SOURCES),) +ifneq ($(or $(filter-out $(TOPLEVEL_LANG),verilog),$(VHDL_SOURCES)),) results.xml: - @echo "Skipping simulation as VHDL is not supported on simulator=$(SIM)" + @echo "Skipping simulation as only Verilog is supported on simulator=$(SIM)" debug: results.xml clean:: From f062f450738c48da65775197ed45050c36a019d7 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 5 Sep 2020 12:15:16 -0500 Subject: [PATCH 2477/2656] Add newsfragment for #1982 --- documentation/source/newsfragments/1982.feature.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 documentation/source/newsfragments/1982.feature.rst diff --git a/documentation/source/newsfragments/1982.feature.rst b/documentation/source/newsfragments/1982.feature.rst new file mode 100644 index 00000000..f3094f27 --- /dev/null +++ b/documentation/source/newsfragments/1982.feature.rst @@ -0,0 +1,2 @@ +Makefiles now automatically deduce :make:var:`TOPLEVEL_LANG` based on the value of :make:var:`VERILOG_SOURCES` and :make:var:`VHDL_SOURCES`. +Makefiles also detect incorrect usage of :make:var:`TOPLEVEL_LANG` for simulators that only support one language. From c475e6470a916ed4176529580b94b31ec5a5654b Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 5 Sep 2020 12:39:40 -0500 Subject: [PATCH 2478/2656] Add newsfragment for #2038 --- documentation/source/newsfragments/2038.bugfix.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 documentation/source/newsfragments/2038.bugfix.rst diff --git a/documentation/source/newsfragments/2038.bugfix.rst b/documentation/source/newsfragments/2038.bugfix.rst new file mode 100644 index 00000000..a01f2676 --- /dev/null +++ b/documentation/source/newsfragments/2038.bugfix.rst @@ -0,0 +1 @@ +Fix GHDL's library search path, allowing libraries other than *work* to be used in simulation. From fc63a902e99187932b270ea30f61faefae81b4c9 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 5 Sep 2020 12:44:03 -0500 Subject: [PATCH 2479/2656] Add newsfragment for #2022 --- documentation/source/newsfragments/2022.change.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 documentation/source/newsfragments/2022.change.rst diff --git a/documentation/source/newsfragments/2022.change.rst b/documentation/source/newsfragments/2022.change.rst new file mode 100644 index 00000000..dec33846 --- /dev/null +++ b/documentation/source/newsfragments/2022.change.rst @@ -0,0 +1 @@ +Updated :class:`~cocotb.drivers.Driver`, :class:`~cocotb.monitors.Monitor`, and all their subclasses to use the :keyword:`async`/:keyword:`await` syntax instead of the :keyword:`yield` syntax. From 6b5d5b1b155adec95d7e04922f2aaebde19778cc Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 30 Aug 2020 12:15:14 -0500 Subject: [PATCH 2480/2656] Add deprecation warning to Scoreboard --- cocotb/scoreboard.py | 9 +++++++++ examples/dff/tests/dff_cocotb.py | 9 ++++++--- examples/endian_swapper/tests/test_endian_swapper.py | 5 ++++- examples/mean/tests/test_mean.py | 5 ++++- .../test_cases/test_avalon_stream/test_avalon_stream.py | 5 ++++- 5 files changed, 27 insertions(+), 6 deletions(-) diff --git a/cocotb/scoreboard.py b/cocotb/scoreboard.py index 6efc06f1..91702993 100755 --- a/cocotb/scoreboard.py +++ b/cocotb/scoreboard.py @@ -30,6 +30,7 @@ """Common scoreboarding capability.""" import logging +import warnings from cocotb.utils import hexdump, hexdiffs from cocotb.log import SimLog @@ -57,6 +58,8 @@ class Scoreboard: fail_immediately (bool, optional): Raise :any:`TestFailure` immediately when something is wrong instead of just recording an error. Default is ``True``. + + .. deprecated:: 1.4.1 """ def __init__(self, dut, reorder_depth=0, fail_immediately=True): # FIXME: reorder_depth needed here? @@ -66,6 +69,12 @@ def __init__(self, dut, reorder_depth=0, fail_immediately=True): # FIXME: reord self.expected = {} self._imm = fail_immediately + warnings.warn( + "This Scoreboard implementation has been deprecated and will be removed soon.\n" + "If this implementation works for you, copy the implementation into your project, " + "while following cocotb's license agreement.", + DeprecationWarning) + @property def result(self): """Determine the test result, do we have any pending data remaining? diff --git a/examples/dff/tests/dff_cocotb.py b/examples/dff/tests/dff_cocotb.py index 8bcf31ba..c35775cd 100644 --- a/examples/dff/tests/dff_cocotb.py +++ b/examples/dff/tests/dff_cocotb.py @@ -1,7 +1,7 @@ # ============================================================================== -# Authors: Martin Zabel +# Authors: Martin Zabel # -# Cocotb Testbench: For D flip-flop +# Cocotb Testbench: For D flip-flop # # Description: # ------------------------------------ @@ -26,6 +26,7 @@ # ============================================================================== import random +import warnings import cocotb from cocotb.clock import Clock @@ -93,7 +94,9 @@ def __init__(self, dut, init_val): # Create a scoreboard on the outputs self.expected_output = [init_val] # a list with init_val as the first element - self.scoreboard = Scoreboard(dut) + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + self.scoreboard = Scoreboard(dut) self.scoreboard.add_interface(self.output_mon, self.expected_output) # Use the input monitor to reconstruct the transactions from the pins diff --git a/examples/endian_swapper/tests/test_endian_swapper.py b/examples/endian_swapper/tests/test_endian_swapper.py index 7f2877a9..80758bb1 100644 --- a/examples/endian_swapper/tests/test_endian_swapper.py +++ b/examples/endian_swapper/tests/test_endian_swapper.py @@ -27,6 +27,7 @@ import random import logging +import warnings import cocotb @@ -81,7 +82,9 @@ def __init__(self, dut, debug=False): # Create a scoreboard on the stream_out bus self.pkts_sent = 0 self.expected_output = [] - self.scoreboard = Scoreboard(dut) + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + self.scoreboard = Scoreboard(dut) self.scoreboard.add_interface(self.stream_out, self.expected_output) # Reconstruct the input transactions from the pins diff --git a/examples/mean/tests/test_mean.py b/examples/mean/tests/test_mean.py index 2b3bbbf0..7f2af7e3 100644 --- a/examples/mean/tests/test_mean.py +++ b/examples/mean/tests/test_mean.py @@ -8,6 +8,7 @@ from cocotb.scoreboard import Scoreboard import random +import warnings CLK_PERIOD_NS = 100 @@ -90,7 +91,9 @@ async def mean_randomised_test(dut): dut_out = StreamBusMonitor(dut, "o", dut.clk) exp_out = [] - scoreboard = Scoreboard(dut) + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + scoreboard = Scoreboard(dut) scoreboard.add_interface(dut_out, exp_out) DATA_WIDTH = int(dut.DATA_WIDTH.value) diff --git a/tests/test_cases/test_avalon_stream/test_avalon_stream.py b/tests/test_cases/test_avalon_stream/test_avalon_stream.py index 94a251b0..fe01015f 100755 --- a/tests/test_cases/test_avalon_stream/test_avalon_stream.py +++ b/tests/test_cases/test_avalon_stream/test_avalon_stream.py @@ -3,6 +3,7 @@ import random import struct +import warnings import cocotb from cocotb.drivers import BitDriver @@ -24,7 +25,9 @@ def __init__(self, dut): self.stream_in = AvalonSTDriver(self.dut, "asi", dut.clk) self.stream_out = AvalonSTMonitor(self.dut, "aso", dut.clk) - self.scoreboard = Scoreboard(self.dut, fail_immediately=True) + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + self.scoreboard = Scoreboard(self.dut, fail_immediately=True) self.expected_output = [] self.scoreboard.add_interface(self.stream_out, self.expected_output) From 7efe494b0a52b5e393c9346bb37987b41dc7db50 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 2 Sep 2020 08:24:49 -0500 Subject: [PATCH 2481/2656] Add deprecation warning to cocotb.generators package --- cocotb/generators/__init__.py | 21 ++++++++++++ cocotb/generators/bit.py | 8 +++++ cocotb/generators/byte.py | 32 ++++++++++++++----- cocotb/generators/packet.py | 18 +++++++++-- .../tests/test_endian_swapper.py | 7 ++-- .../test_avalon_stream/test_avalon_stream.py | 5 ++- 6 files changed, 76 insertions(+), 15 deletions(-) diff --git a/cocotb/generators/__init__.py b/cocotb/generators/__init__.py index a0965430..b2e17748 100644 --- a/cocotb/generators/__init__.py +++ b/cocotb/generators/__init__.py @@ -31,9 +31,20 @@ import math import random import itertools +import warnings + from cocotb.decorators import public +warnings.warn( + "The contents of the cocotb.generators package will soon be removed.\n" + "Most of the functionality can be replaced with utilities provided " + "by other packages or the standard library.\n Alternatively, you can " + "copy this package or individual functions into your project, if you " + "follow cocotb's license agreement.", + DeprecationWarning) + + @public def repeat(obj, nrepeat=None): """Generator to repeatedly yield the same object @@ -41,6 +52,8 @@ def repeat(obj, nrepeat=None): Args: obj (any): The object to yield nrepeat (int, optional): The number of times to repeatedly yield *obj* + + .. deprecated:: 1.4.1 """ if nrepeat is None: return itertools.repeat(obj) @@ -55,6 +68,8 @@ def combine(generators): Args: generators (iterable): Generators to combine together + + .. deprecated:: 1.4.1 """ return itertools.chain.from_iterable(generators) @@ -68,6 +83,8 @@ def gaussian(mean, sigma): mean (int/float): mean value sigma (int/float): Standard deviation + + .. deprecated:: 1.4.1 """ while True: yield random.gauss(mean, sigma) @@ -85,6 +102,8 @@ def sine_wave(amplitude, w, offset=0): Yields: floats that form a sine wave + + .. deprecated:: 1.4.1 """ twoPiF_DIV_sampleRate = math.pi * 2 while True: @@ -97,5 +116,7 @@ def get_generators(module): Args: module (python module): The module to get the generators from + + .. deprecated:: 1.4.1 """ return (getattr(module, gen) for gen in module.__all__) diff --git a/cocotb/generators/bit.py b/cocotb/generators/bit.py index 92ac3e00..4e09732e 100644 --- a/cocotb/generators/bit.py +++ b/cocotb/generators/bit.py @@ -44,6 +44,8 @@ def bit_toggler(gen_on, gen_off): Args: gen_on (generator): generator that yields number of cycles on gen_off (generator): generator that yields number of cycles off + + .. deprecated:: 1.4.1 """ for n_on, n_off in zip(gen_on, gen_off): yield int(abs(n_on)), int(abs(n_off)) @@ -56,6 +58,8 @@ def intermittent_single_cycles(mean=10, sigma=None): Args: mean (int, optional): Average number of cycles in between single cycle gaps sigma (int, optional): Standard deviation of gaps. mean/4 if sigma is None + + .. deprecated:: 1.4.1 """ if sigma is None: sigma = mean / 4.0 @@ -70,6 +74,8 @@ def random_50_percent(mean=10, sigma=None): Args: mean (int, optional): Average number of cycles on/off sigma (int, optional): Standard deviation of gaps. mean/4 if sigma is None + + .. deprecated:: 1.4.1 """ if sigma is None: sigma = mean / 4.0 @@ -84,6 +90,8 @@ def wave(on_ampl=30, on_freq=200, off_ampl=10, off_freq=100): TODO: Adjust args so we just specify a repeat duration and overall throughput + + .. deprecated:: 1.4.1 """ return bit_toggler(sine_wave(on_ampl, on_freq), sine_wave(off_ampl, off_freq)) diff --git a/cocotb/generators/byte.py b/cocotb/generators/byte.py index 9189c165..e1ce0588 100755 --- a/cocotb/generators/byte.py +++ b/cocotb/generators/byte.py @@ -41,29 +41,41 @@ @public def get_bytes(nbytes: int, generator: Iterator[int]) -> bytes: - """Get nbytes from generator + """ + Get nbytes from generator .. versionchanged:: 1.4.0 - This now returns :class:`bytes`, not :class:`str`. """ + This now returns :class:`bytes`, not :class:`str`. + + .. deprecated:: 1.4.1 + """ return bytes(next(generator) for i in range(nbytes)) @public def random_data() -> Iterator[int]: - r"""Random bytes + r""" + Random bytes .. versionchanged:: 1.4.0 - This now returns integers, not single-character :class:`str`\ s. """ + This now returns integers, not single-character :class:`str`\ s. + + .. deprecated:: 1.4.1 + """ while True: yield random.randrange(256) @public def incrementing_data(increment=1) -> Iterator[int]: - r"""Incrementing bytes + r""" + Incrementing bytes .. versionchanged:: 1.4.0 - This now returns integers, not single-character :class:`str`\ s. """ + This now returns integers, not single-character :class:`str`\ s. + + .. deprecated:: 1.4.1 + """ val = 0 while True: yield val @@ -72,6 +84,10 @@ def incrementing_data(increment=1) -> Iterator[int]: @public -def repeating_bytes(pattern : bytes = b"\x00") -> Iterator[int]: - """Repeat a pattern of bytes""" +def repeating_bytes(pattern: bytes = b"\x00") -> Iterator[int]: + """ + Repeat a pattern of bytes + + .. deprecated:: 1.4.1 + """ return itertools.cycle(pattern) diff --git a/cocotb/generators/packet.py b/cocotb/generators/packet.py index 256380a2..ca478ed3 100644 --- a/cocotb/generators/packet.py +++ b/cocotb/generators/packet.py @@ -51,7 +51,11 @@ # UDP packet generators @public def udp_all_sizes(max_size=1500, payload=_default_payload()): - """UDP packets of every supported size""" + """ + UDP packets of every supported size + + .. deprecated:: 1.4.1 + """ header = Ether() / IP() / UDP() for size in range(0, max_size - len(header)): @@ -60,7 +64,11 @@ def udp_all_sizes(max_size=1500, payload=_default_payload()): @public def udp_random_sizes(npackets=100, payload=_default_payload()): - """UDP packets with random sizes""" + """ + UDP packets with random sizes + + .. deprecated:: 1.4.1 + """ header = Ether() / IP() / UDP() max_size = 1500 - len(header) @@ -71,6 +79,10 @@ def udp_random_sizes(npackets=100, payload=_default_payload()): # IPV4 generator @public def ipv4_small_packets(npackets=100, payload=_default_payload()): - """Small (<100bytes payload) IPV4 packets""" + """ + Small (<100bytes payload) IPV4 packets + + .. deprecated:: 1.4.1 + """ for pkt in range(npackets): yield Ether() / IP() / get_bytes(random.randint(0, 100), payload) diff --git a/examples/endian_swapper/tests/test_endian_swapper.py b/examples/endian_swapper/tests/test_endian_swapper.py index 80758bb1..0af5dc51 100644 --- a/examples/endian_swapper/tests/test_endian_swapper.py +++ b/examples/endian_swapper/tests/test_endian_swapper.py @@ -42,9 +42,10 @@ from cocotb.result import TestFailure # Data generators -from cocotb.generators.byte import random_data, get_bytes -from cocotb.generators.bit import (wave, intermittent_single_cycles, - random_50_percent) +with warnings.catch_warnings(): + warnings.simplefilter('ignore') + from cocotb.generators.byte import random_data, get_bytes + from cocotb.generators.bit import wave, intermittent_single_cycles, random_50_percent async def stream_out_config_setter(dut, stream_out, stream_in): diff --git a/tests/test_cases/test_avalon_stream/test_avalon_stream.py b/tests/test_cases/test_avalon_stream/test_avalon_stream.py index fe01015f..294b599a 100755 --- a/tests/test_cases/test_avalon_stream/test_avalon_stream.py +++ b/tests/test_cases/test_avalon_stream/test_avalon_stream.py @@ -12,7 +12,10 @@ from cocotb.triggers import RisingEdge from cocotb.clock import Clock from cocotb.scoreboard import Scoreboard -from cocotb.generators.bit import wave + +with warnings.catch_warnings(): + warnings.simplefilter('ignore') + from cocotb.generators.bit import wave class AvalonSTTB(object): From ca9eca16c9eb3e410efd74f308bfe1af63c5d42e Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Thu, 3 Sep 2020 08:44:50 -0500 Subject: [PATCH 2482/2656] Don't source python version on Windows CI We must use conda on Windows to build the cocotb package for CI. We shouldn't have Travis source a python installation because we aren't using it. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 33a1ea87..6c7e8c12 100644 --- a/.travis.yml +++ b/.travis.yml @@ -81,7 +81,6 @@ jobs: after_success: *travis_linux_post - stage: SimTests - python: 3.7 os: windows language: shell before_install: From 0d0306462cd8dedd6bd29d43e3bdea56b7f9f931 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 5 Sep 2020 12:58:27 -0500 Subject: [PATCH 2483/2656] Add newsfragment for #2047 --- documentation/source/newsfragments/2047.removal.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 documentation/source/newsfragments/2047.removal.rst diff --git a/documentation/source/newsfragments/2047.removal.rst b/documentation/source/newsfragments/2047.removal.rst new file mode 100644 index 00000000..b8b18b84 --- /dev/null +++ b/documentation/source/newsfragments/2047.removal.rst @@ -0,0 +1 @@ +:class:`~cocotb.scoreboard.Scoreboard` and all contents of :mod:`cocotb.generators` have been deprecated. From 3f95526b17e22fd00dc724e1394952f97a663ec3 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sun, 6 Sep 2020 15:14:03 +0200 Subject: [PATCH 2484/2656] Remove outdated Sorter example from docs Closes #2049. --- documentation/source/examples.rst | 42 ------------------- .../source/newsfragments/2049.removal.rst | 1 + 2 files changed, 1 insertion(+), 42 deletions(-) create mode 100644 documentation/source/newsfragments/2049.removal.rst diff --git a/documentation/source/examples.rst b/documentation/source/examples.rst index 5bcc0569..04ed3b19 100644 --- a/documentation/source/examples.rst +++ b/documentation/source/examples.rst @@ -77,45 +77,3 @@ The directory :file:`cocotb/examples/axi_lite_slave/` contains ... .. todo:: Write documentation, see :file:`README.md` - - -Sorter -====== - -Example testbench for snippet of code from `comp.lang.verilog `_: - -.. code-block:: python3 - - @cocotb.coroutine - def run_test(dut, data_generator=random_data, delay_cycles=2): - """Send data through the DUT and check it is sorted output.""" - cocotb.fork(Clock(dut.clk, 100).start()) - - # Don't check until valid output - expected = [None] * delay_cycles - - for index, values in enumerate(data_generator(bits=len(dut.in1))): - expected.append(sorted(values)) - - yield RisingEdge(dut.clk) - dut.in1 = values[0] - dut.in2 = values[1] - dut.in3 = values[2] - dut.in4 = values[3] - dut.in5 = values[4] - - yield ReadOnly() - expect = expected.pop(0) - - if expect is None: - continue - - got = [int(dut.out5), int(dut.out4), int(dut.out3), - int(dut.out2), int(dut.out1)] - - if got != expect: - dut._log.error('Expected %s' % expect) - dut._log.error('Got %s' % got) - raise TestFailure("Output didn't match") - - dut._log.info('Sucessfully sent %d cycles of data' % (index + 1)) diff --git a/documentation/source/newsfragments/2049.removal.rst b/documentation/source/newsfragments/2049.removal.rst new file mode 100644 index 00000000..b7fa5690 --- /dev/null +++ b/documentation/source/newsfragments/2049.removal.rst @@ -0,0 +1 @@ +The outdated "Sorter" example has been removed from the documentation. From 83f5cf106371eb2e4d225bc71e681cebd68bbb77 Mon Sep 17 00:00:00 2001 From: Christian Svensson Date: Mon, 7 Sep 2020 22:45:53 +0200 Subject: [PATCH 2485/2656] Describe GUI option for ModelSim This option allows the ModelSim simulator to be launched with the waveform visible. --- documentation/source/simulator_support.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index f1bf7365..ab01a551 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -256,6 +256,9 @@ If you try to use them with :term:`FLI`, you will see a ``vsim-FLI-3155`` error: ModelSim DE and SE (and Questa, of course) support the :term:`FLI`. +In order to start ModelSim or Questa with the graphical interface and for the simulator to remain active after the tests have completed, set :make:var:`GUI=1`. +If you have previously launched a test without this setting you might have to delete the ``sim_build`` directory to get the correct behavior. + .. _sim-modelsim-issues: Issues for this simulator From 8744492c8af4aaa73681362f86fe62be6665bb4d Mon Sep 17 00:00:00 2001 From: Christian Svensson Date: Mon, 7 Sep 2020 23:32:29 +0200 Subject: [PATCH 2486/2656] Improve grammar for ModelSim GUI paragraph Co-authored-by: Colin Marquardt --- documentation/source/simulator_support.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index ab01a551..0d974f61 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -257,7 +257,7 @@ If you try to use them with :term:`FLI`, you will see a ``vsim-FLI-3155`` error: ModelSim DE and SE (and Questa, of course) support the :term:`FLI`. In order to start ModelSim or Questa with the graphical interface and for the simulator to remain active after the tests have completed, set :make:var:`GUI=1`. -If you have previously launched a test without this setting you might have to delete the ``sim_build`` directory to get the correct behavior. +If you have previously launched a test without this setting, you might have to delete the :make:var:`SIM_BUILD` directory (``sim_build`` by default) to get the correct behavior. .. _sim-modelsim-issues: From c77d1ca761b91e5dfab2f0d2703f86a48f6ae139 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Mon, 16 Mar 2020 18:48:34 -0700 Subject: [PATCH 2487/2656] Add matrix_multiplier to examples. Add matrix multiplication example and test. The test is heavily weighted towards use of the BinaryValue class. The random seed is set to a default value to provide a deterministic test for performance regression. --- documentation/source/examples.rst | 41 +++++ .../source/newsfragments/1502.doc.rst | 1 + examples/Makefile | 1 + .../hdl/matrix_multiplier.sv | 78 +++++++++ .../hdl/matrix_multiplier.vhd | 75 +++++++++ .../hdl/matrix_multiplier_pkg.vhd | 13 ++ examples/matrix_multiplier/tests/Makefile | 82 ++++++++++ .../tests/test_matrix_multiplier.py | 148 ++++++++++++++++++ 8 files changed, 439 insertions(+) create mode 100644 documentation/source/newsfragments/1502.doc.rst create mode 100644 examples/matrix_multiplier/hdl/matrix_multiplier.sv create mode 100644 examples/matrix_multiplier/hdl/matrix_multiplier.vhd create mode 100644 examples/matrix_multiplier/hdl/matrix_multiplier_pkg.vhd create mode 100644 examples/matrix_multiplier/tests/Makefile create mode 100644 examples/matrix_multiplier/tests/test_matrix_multiplier.py diff --git a/documentation/source/examples.rst b/documentation/source/examples.rst index 04ed3b19..8dd322d4 100644 --- a/documentation/source/examples.rst +++ b/documentation/source/examples.rst @@ -35,6 +35,47 @@ to start and stop generation of input data. A :class:`.TestFactory` is used to generate the random tests. +.. _matrix_multiplier: + +Matrix Multiplier +================= + +The directory :file:`cocotb/examples/matrix_multiplier` +contains a module for multiplying two matrices together, +implemented in both **VHDL** and **SystemVerilog**. + +The module takes two matrices ``a_i`` and ``b_i`` as inputs +and provides the resulting matrix ``c_o`` as an output. +On each rising clock edge, +``c_o`` is calculated and output. +When input ``valid_i`` is high +and ``c_o`` is calculated, +``valid_o`` goes high to signal a valid output value. + +The testbench defines ``MatrixInMonitor`` and ``MatrixOutMonitor`` +(both sub-classes of :class:`.BusMonitor`) +and a test case ``test_multiply``. + +``MatrixInMonitor`` watches for valid input matrices, +then does the multiplication in Python +and stores the result as the expected output matrix. + +``MatrixOutMonitor`` watches for valid output matrices +and compares the result to the expected value. + +The testbench makes use of :class:`.TestFactory` +and random data generators +to test many sets of matrices, +and :class:`.Scoreboard` to compare expected and actual results. + +The number of data bits for each entry in the matrices, +as well as the row and column counts for each matrix, +are configurable in the Makefile. + +.. note:: + The example module uses one-dimensional arrays in the port definition to represent the matrices. + + Mean ==== diff --git a/documentation/source/newsfragments/1502.doc.rst b/documentation/source/newsfragments/1502.doc.rst new file mode 100644 index 00000000..2f349754 --- /dev/null +++ b/documentation/source/newsfragments/1502.doc.rst @@ -0,0 +1 @@ +New example :ref:`matrix_multiplier`. \ No newline at end of file diff --git a/examples/Makefile b/examples/Makefile index ad0f4f05..3a7df6b8 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -32,6 +32,7 @@ EXAMPLES := adder/tests \ dff/tests \ simple_dff \ endian_swapper/tests \ + matrix_multiplier/tests \ mean/tests \ mixed_language/tests # requires root permissions: ping_tun_tap/tests diff --git a/examples/matrix_multiplier/hdl/matrix_multiplier.sv b/examples/matrix_multiplier/hdl/matrix_multiplier.sv new file mode 100644 index 00000000..ee193c5c --- /dev/null +++ b/examples/matrix_multiplier/hdl/matrix_multiplier.sv @@ -0,0 +1,78 @@ +// This file is public domain, it can be freely copied without restrictions. +// SPDX-License-Identifier: CC0-1.0 + +// Matrix Multiplier DUT +`timescale 1ns/1ps + +module matrix_multiplier #( + parameter int DATA_WIDTH = 8, + parameter int A_ROWS = 8, + parameter int B_COLUMNS = 5, + parameter int A_COLUMNS_B_ROWS = 4, + parameter int C_DATA_WIDTH = (2 * DATA_WIDTH) + $clog2(A_COLUMNS_B_ROWS) +) ( + input clk_i, + input reset_i, + input valid_i, + output logic valid_o, + input [DATA_WIDTH-1:0] a_i[A_ROWS * A_COLUMNS_B_ROWS], + input [DATA_WIDTH-1:0] b_i[A_COLUMNS_B_ROWS * B_COLUMNS], + output logic [C_DATA_WIDTH-1:0] c_o[A_ROWS * B_COLUMNS] +); + + logic [C_DATA_WIDTH-1:0] c_calc[A_ROWS * B_COLUMNS]; + + always @(*) begin + logic [C_DATA_WIDTH-1:0] c_element; + for (int i = 0; i < A_ROWS; i = i + 1) begin + for (int j = 0; j < B_COLUMNS; j = j + 1) begin + c_element = 0; + for (int k = 0; k < A_COLUMNS_B_ROWS; k = k + 1) begin + c_element = c_element + (a_i[(i * A_COLUMNS_B_ROWS) + k] * b_i[(k * B_COLUMNS) + j]); + end + c_calc[(i * B_COLUMNS) + j] = c_element; + end + end + end + + always @(posedge clk_i) begin + if (reset_i) begin + valid_o <= 1'b0; + + for (int idx = 0; idx < (A_ROWS * B_COLUMNS); idx++) begin + c_o[idx] <= '0; + end + end else begin + valid_o <= valid_i; + + for (int idx = 0; idx < (A_ROWS * B_COLUMNS); idx++) begin + c_o[idx] <= c_calc[idx]; + end + end + end + + // Dump waves +`ifndef VERILATOR + initial begin + integer idx; + $dumpfile("dump.vcd"); + $dumpvars(1, matrix_multiplier); + `ifdef __ICARUS__ + for (idx = 0; idx < (A_ROWS * A_COLUMNS_B_ROWS); idx++) begin + $dumpvars(1, a_i[idx]); + end + for (idx = 0; idx < (A_COLUMNS_B_ROWS * B_COLUMNS); idx++) begin + $dumpvars(1, b_i[idx]); + end + for (idx = 0; idx < (A_ROWS * B_COLUMNS); idx++) begin + $dumpvars(1, c_o[idx]); + end + `else + $dumpvars(1, a_i); + $dumpvars(1, b_i); + $dumpvars(1, c_o); + `endif + end +`endif + +endmodule diff --git a/examples/matrix_multiplier/hdl/matrix_multiplier.vhd b/examples/matrix_multiplier/hdl/matrix_multiplier.vhd new file mode 100644 index 00000000..e1591346 --- /dev/null +++ b/examples/matrix_multiplier/hdl/matrix_multiplier.vhd @@ -0,0 +1,75 @@ +-- This file is public domain, it can be freely copied without restrictions. +-- SPDX-License-Identifier: CC0-1.0 + +-- Matrix Multiplier DUT +library ieee ; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.matrix_multiplier_pkg.all; +use work.mean_pkg.clog2; + +entity matrix_multiplier is + generic ( + DATA_WIDTH : positive := 8; + A_ROWS : positive := 8; + B_COLUMNS : positive := 5; + A_COLUMNS_B_ROWS : positive := 4 + ); + port ( + clk_i : in std_logic; + reset_i : in std_logic; + valid_i : in std_logic; + valid_o : out std_logic; + a_i : in flat_matrix_type(0 to (A_ROWS * A_COLUMNS_B_ROWS) - 1)(DATA_WIDTH - 1 downto 0); + b_i : in flat_matrix_type(0 to (A_COLUMNS_B_ROWS * B_COLUMNS) - 1)(DATA_WIDTH - 1 downto 0); + c_o : out flat_matrix_type(0 to (A_ROWS * B_COLUMNS) - 1)((2 * DATA_WIDTH) + clog2(A_COLUMNS_B_ROWS) - 1 downto 0) + ); +end entity matrix_multiplier; + +architecture rtl of matrix_multiplier is + + signal c_calc : flat_matrix_type(c_o'RANGE)(c_o(0)'RANGE); + +begin + + multiply : process (all) is + + variable c_var : flat_matrix_type(c_o'RANGE)(c_o(0)'RANGE); + + begin + + c_var := (others => (others => '0')); + + C_ROWS : for i in 0 to A_ROWS-1 loop + C_COLUMNS : for j in 0 to B_COLUMNS-1 loop + DOT_PRODUCT : for k in 0 to A_COLUMNS_B_ROWS-1 loop + c_var((i * B_COLUMNS) + j) := c_var((i * B_COLUMNS) + j) + (a_i((i * A_COLUMNS_B_ROWS) + k) * b_i((k * B_COLUMNS) + j)); + end loop; + end loop; + end loop; + + c_calc <= c_var; + + end process multiply; + + proc_reg : process (clk_i) is + begin + + if (rising_edge(clk_i)) then + if (reset_i) then + valid_o <= '0'; + c_o <= (others => (others => '0')); + else + valid_o <= valid_i; + + if (valid_i) then + c_o <= c_calc; + else + c_o <= (others => (others => 'X')); + end if; + end if; + end if; + + end process proc_reg; + +end architecture rtl; diff --git a/examples/matrix_multiplier/hdl/matrix_multiplier_pkg.vhd b/examples/matrix_multiplier/hdl/matrix_multiplier_pkg.vhd new file mode 100644 index 00000000..0dcd94ba --- /dev/null +++ b/examples/matrix_multiplier/hdl/matrix_multiplier_pkg.vhd @@ -0,0 +1,13 @@ +-- 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; +use ieee.numeric_std.all; + +package matrix_multiplier_pkg is + + -- VHDL-2008 required for unconstrained array types + type flat_matrix_type is array (integer range <>) of unsigned; + +end package; diff --git a/examples/matrix_multiplier/tests/Makefile b/examples/matrix_multiplier/tests/Makefile new file mode 100644 index 00000000..73adc424 --- /dev/null +++ b/examples/matrix_multiplier/tests/Makefile @@ -0,0 +1,82 @@ +# This file is public domain, it can be freely copied without restrictions. +# SPDX-License-Identifier: CC0-1.0 + +TOPLEVEL_LANG ?= verilog +SIM ?= icarus + +PWD=$(shell pwd) + +# Matrix parameters +DATA_WIDTH ?= 32 +A_ROWS ?= 10 +B_COLUMNS ?= 4 +A_COLUMNS_B_ROWS ?= 6 + +ifeq ($(TOPLEVEL_LANG),verilog) + VERILOG_SOURCES = $(PWD)/../hdl/matrix_multiplier.sv + + # Set module parameters + ifeq ($(SIM),icarus) + COMPILE_ARGS += -Pmatrix_multiplier.DATA_WIDTH=$(DATA_WIDTH) -Pmatrix_multiplier.A_ROWS=$(A_ROWS) -Pmatrix_multiplier.B_COLUMNS=$(B_COLUMNS) -Pmatrix_multiplier.A_COLUMNS_B_ROWS=$(A_COLUMNS_B_ROWS) + else ifneq ($(filter $(SIM),questa modelsim riviera activehdl),) + SIM_ARGS += -gDATA_WIDTH=$(DATA_WIDTH) -gA_ROWS=$(A_ROWS) -gB_COLUMNS=$(B_COLUMNS) -gA_COLUMNS_B_ROWS=$(A_COLUMNS_B_ROWS) + else ifeq ($(SIM),vcs) + COMPILE_ARGS += -pvalue+matrix_multiplier/DATA_WIDTH=$(DATA_WIDTH) -pvalue+matrix_multiplier/A_ROWS=$(A_ROWS) -pvalue+matrix_multiplier/B_COLUMNS=$(B_COLUMNS) -pvalue+matrix_multiplier/A_COLUMNS_B_ROWS=$(A_COLUMNS_B_ROWS) + else ifeq ($(SIM),verilator) + COMPILE_ARGS += -GDATA_WIDTH=$(DATA_WIDTH) -GA_ROWS=$(A_ROWS) -GB_COLUMNS=$(B_COLUMNS) -GA_COLUMNS_B_ROWS=$(A_COLUMNS_B_ROWS) + else ifneq ($(filter $(SIM),ius xcelium),) + EXTRA_ARGS += -defparam "matrix_multiplier.DATA_WIDTH=$(DATA_WIDTH)" -defparam "matrix_multiplier.A_ROWS=$(A_ROWS)" -defparam "matrix_multiplier.B_COLUMNS=$(B_COLUMNS)" -defparam "matrix_multiplier.A_COLUMNS_B_ROWS=$(A_COLUMNS_B_ROWS)" + endif + + ifneq ($(filter $(SIM),riviera activehdl),) + COMPILE_ARGS += -sv2k12 + endif + +else ifeq ($(TOPLEVEL_LANG),vhdl) + VHDL_SOURCES = $(PWD)/../../mean/hdl/mean_pkg.vhd $(PWD)/../hdl/matrix_multiplier_pkg.vhd $(PWD)/../hdl/matrix_multiplier.vhd + + ifneq ($(filter $(SIM),ghdl questa modelsim riviera activehdl),) + # ghdl, questa, and aldec all use SIM_ARGS with '-g' for setting generics + SIM_ARGS += -gDATA_WIDTH=$(DATA_WIDTH) -gA_ROWS=$(A_ROWS) -gB_COLUMNS=$(B_COLUMNS) -gA_COLUMNS_B_ROWS=$(A_COLUMNS_B_ROWS) + else ifneq ($(filter $(SIM),ius xcelium),) + SIM_ARGS += -generic "matrix_multiplier:DATA_WIDTH=>$(DATA_WIDTH)" -generic "matrix_multiplier:A_ROWS=>$(A_ROWS)" -generic "matrix_multiplier:B_COLUMNS=>$(B_COLUMNS)" -generic "matrix_multiplier:A_COLUMNS_B_ROWS=>$(A_COLUMNS_B_ROWS)" + endif + + ifeq ($(SIM),ghdl) + EXTRA_ARGS += --std=08 + SIM_ARGS += --wave=wave.ghw + else ifneq ($(filter $(SIM),questa modelsim riviera activehdl),) + COMPILE_ARGS += -2008 + endif +else + $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") +endif + +# Fix the seed to ensure deterministic tests +export RANDOM_SEED := 123456789 + +# Export generics to test +# Can't use plusargs since they aren't supported by ghdl +export DATA_WIDTH +export A_ROWS +export B_COLUMNS +export A_COLUMNS_B_ROWS + +TOPLEVEL := matrix_multiplier +MODULE := test_matrix_multiplier + +include $(shell cocotb-config --makefiles)/Makefile.sim + + +# Profiling + +DOT_BINARY ?= dot + +test_profile.pstat: sim + +callgraph.svg: test_profile.pstat + $(shell cocotb-config --python-bin) -m gprof2dot -f pstats ./$< | $(DOT_BINARY) -Tsvg -o $@ + +.PHONY: profile +profile: + COCOTB_ENABLE_PROFILING=1 $(MAKE) callgraph.svg diff --git a/examples/matrix_multiplier/tests/test_matrix_multiplier.py b/examples/matrix_multiplier/tests/test_matrix_multiplier.py new file mode 100644 index 00000000..d6e8229a --- /dev/null +++ b/examples/matrix_multiplier/tests/test_matrix_multiplier.py @@ -0,0 +1,148 @@ +# This file is public domain, it can be freely copied without restrictions. +# SPDX-License-Identifier: CC0-1.0 + +import math +import os +from random import getrandbits + +import cocotb +from cocotb.binary import BinaryValue +from cocotb.clock import Clock +from cocotb.monitors import BusMonitor +from cocotb.regression import TestFactory +from cocotb.scoreboard import Scoreboard +from cocotb.triggers import RisingEdge, ReadOnly + +NUM_SAMPLES = int(os.environ.get('NUM_SAMPLES', 3000)) +DATA_WIDTH = int(os.environ['DATA_WIDTH']) +A_ROWS = int(os.environ['A_ROWS']) +B_COLUMNS = int(os.environ['B_COLUMNS']) +A_COLUMNS_B_ROWS = int(os.environ['A_COLUMNS_B_ROWS']) + + +class MatrixMonitor(BusMonitor): + """Base class for monitoring inputs/outputs of Matrix Multiplier.""" + def __init__(self, dut, callback=None, event=None): + super().__init__(dut, "", dut.clk_i, callback=callback, event=event) + + +class MatrixInMonitor(MatrixMonitor): + """Monitor inputs to Matrix Multiplier module. + + Generate expected results for each multiplication operation. + """ + _signals = {"A": "a_i", "B": "b_i", "valid": "valid_i"} + + async def _monitor_recv(self): + while True: + await RisingEdge(self.clock) + await ReadOnly() + + if self.bus.valid.value: + a_matrix = self.bus.A.value + b_matrix = self.bus.B.value + + # Calculate the expected result of C + c_expected = [ + BinaryValue( + sum( + [ + a_matrix[(i * A_COLUMNS_B_ROWS) + n] * b_matrix[(n * B_COLUMNS) + j] + for n in range(A_COLUMNS_B_ROWS) + ] + ), + n_bits=(DATA_WIDTH * 2) + math.ceil(math.log2(A_COLUMNS_B_ROWS)), + bigEndian=False + ) + for i in range(A_ROWS) + for j in range(B_COLUMNS) + ] + + self._recv(c_expected) + + +class MatrixOutMonitor(MatrixMonitor): + """Monitor outputs from Matrix Multiplier module. + + Capture result matrix for each multiplication operation. + """ + _signals = {"C": "c_o", "valid": "valid_o"} + + async def _monitor_recv(self): + while True: + await RisingEdge(self.clock) + await ReadOnly() + + if self.bus.valid.value: + c_actual = self.bus.C.value + self._recv(c_actual) + + +async def test_multiply(dut, a_data, b_data): + """Test multiplication of many matrices.""" + + cocotb.fork(Clock(dut.clk_i, 10, units='ns').start()) + + # Configure Scoreboard to compare module results to expected + expected_output = [] + + in_monitor = MatrixInMonitor(dut, callback=expected_output.append) + out_monitor = MatrixOutMonitor(dut) + + scoreboard = Scoreboard(dut) + scoreboard.add_interface(out_monitor, expected_output) + + # Initial values + dut.valid_i <= 0 + dut.a_i <= create_a(lambda x: 0) + dut.b_i <= create_b(lambda x: 0) + + # Reset DUT + dut.reset_i <= 1 + for _ in range(3): + await RisingEdge(dut.clk_i) + dut.reset_i <= 0 + + # Do multiplication operations + for A, B in zip(a_data(), b_data()): + await RisingEdge(dut.clk_i) + dut.a_i <= A + dut.b_i <= B + dut.valid_i <= 1 + + await RisingEdge(dut.clk_i) + dut.valid_i <= 0 + + await RisingEdge(dut.clk_i) + + raise scoreboard.result + + +def create_matrix(func, rows, cols): + return [func(DATA_WIDTH) for row in range(rows) for col in range(cols)] + + +def create_a(func): + return create_matrix(func, A_ROWS, A_COLUMNS_B_ROWS) + + +def create_b(func): + return create_matrix(func, A_COLUMNS_B_ROWS, B_COLUMNS) + + +def gen_a(num_samples=NUM_SAMPLES, func=getrandbits): + """Generate random matrix data for A""" + for _ in range(num_samples): + yield create_a(func) + + +def gen_b(num_samples=NUM_SAMPLES, func=getrandbits): + """Generate random matrix data for B""" + for _ in range(num_samples): + yield create_b(func) + + +factory = TestFactory(test_multiply) +factory.add_option('a_data', [gen_a]) +factory.add_option('b_data', [gen_b]) +factory.generate_tests() From ef3c212f21f35caeb1d246079d4521a0ed2ca505 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 11 Jul 2020 20:03:40 -0500 Subject: [PATCH 2488/2656] Deprecate GPI_PARAMETER and remove it from VPI GPI_PARAMETER represents a parameter in Verilog or a constant in VHDL, but it doesn't say anything about what *kind* of constant value it is. VHPI and FLI already don't support GPI_PARAMETER because of this issue, but the VPI does. This commit implements the small amount of features necessary to map parameters to the correct GPI type. Constness is handled in the VpiSignalObj class. It also deprecates the GPI_PARAMETER constant --- cocotb/share/include/gpi.h | 2 +- cocotb/share/lib/gpi/GpiCbHdl.cpp | 2 +- .../share/lib/simulator/simulatormodule.cpp | 2 +- cocotb/share/lib/vpi/VpiImpl.cpp | 32 ++++++++++++++++--- .../test_discovery/test_discovery.py | 2 +- 5 files changed, 31 insertions(+), 9 deletions(-) diff --git a/cocotb/share/include/gpi.h b/cocotb/share/include/gpi.h index d0ef1ddd..46740129 100644 --- a/cocotb/share/include/gpi.h +++ b/cocotb/share/include/gpi.h @@ -167,7 +167,7 @@ typedef enum gpi_objtype_e { GPI_MEMORY = 1, GPI_MODULE = 2, GPI_NET = 3, - GPI_PARAMETER = 4, + // GPI_PARAMETER = 4, // Deprecated GPI_REGISTER = 5, GPI_ARRAY = 6, GPI_ENUM = 7, diff --git a/cocotb/share/lib/gpi/GpiCbHdl.cpp b/cocotb/share/lib/gpi/GpiCbHdl.cpp index 9b3b8946..744c8b4b 100644 --- a/cocotb/share/lib/gpi/GpiCbHdl.cpp +++ b/cocotb/share/lib/gpi/GpiCbHdl.cpp @@ -57,7 +57,7 @@ const char * GpiObjHdl::get_type_str() CASE_OPTION(GPI_MEMORY); CASE_OPTION(GPI_MODULE); CASE_OPTION(GPI_NET); - CASE_OPTION(GPI_PARAMETER); + // CASE_OPTION(GPI_PARAMETER); // Deprecated CASE_OPTION(GPI_REGISTER); CASE_OPTION(GPI_ARRAY); CASE_OPTION(GPI_ENUM); diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index 02b14f9a..3384442a 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -920,7 +920,7 @@ static int add_module_constants(PyObject* simulator) PyModule_AddIntConstant(simulator, "MEMORY", GPI_MEMORY ) < 0 || PyModule_AddIntConstant(simulator, "MODULE", GPI_MODULE ) < 0 || PyModule_AddIntConstant(simulator, "NET", GPI_NET ) < 0 || - PyModule_AddIntConstant(simulator, "PARAMETER", GPI_PARAMETER ) < 0 || + // PyModule_AddIntConstant(simulator, "PARAMETER", GPI_PARAMETER ) < 0 || // Deprecated PyModule_AddIntConstant(simulator, "REG", GPI_REGISTER ) < 0 || PyModule_AddIntConstant(simulator, "NETARRAY", GPI_ARRAY ) < 0 || PyModule_AddIntConstant(simulator, "ENUM", GPI_ENUM ) < 0 || diff --git a/cocotb/share/lib/vpi/VpiImpl.cpp b/cocotb/share/lib/vpi/VpiImpl.cpp index ec9cc942..39a2c05c 100644 --- a/cocotb/share/lib/vpi/VpiImpl.cpp +++ b/cocotb/share/lib/vpi/VpiImpl.cpp @@ -94,7 +94,7 @@ const char *VpiImpl::get_simulator_version() return m_version.c_str(); } -gpi_objtype_t to_gpi_objtype(int32_t vpitype) +static gpi_objtype_t to_gpi_objtype(int32_t vpitype) { switch (vpitype) { case vpiNet: @@ -127,9 +127,6 @@ gpi_objtype_t to_gpi_objtype(int32_t vpitype) case vpiIntegerNet: return GPI_INTEGER; - case vpiParameter: - return GPI_PARAMETER; - case vpiStructVar: case vpiStructNet: case vpiUnionVar: @@ -157,6 +154,27 @@ gpi_objtype_t to_gpi_objtype(int32_t vpitype) } } +static gpi_objtype_t const_type_to_gpi_objtype(int32_t const_type) +{ + switch (const_type) + { + case vpiDecConst: + case vpiBinaryConst: + case vpiOctConst: + case vpiHexConst: + case vpiIntConst: + return GPI_INTEGER; + case vpiRealConst: + return GPI_REAL; + case vpiStringConst: + return GPI_STRING; + //case vpiTimeConst: // Not implemented + default: + LOG_DEBUG("Unable to map vpiConst type %d onto GPI type", const_type); + return GPI_UNKNOWN; + } +} + GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, std::string &name, std::string &fq_name) @@ -187,8 +205,12 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, new_obj = new VpiSignalObjHdl(this, new_hdl, to_gpi_objtype(type), false); break; case vpiParameter: - new_obj = new VpiSignalObjHdl(this, new_hdl, to_gpi_objtype(type), true); + case vpiConstant: + { + auto const_type = vpi_get(vpiConstType, new_hdl); + new_obj = new VpiSignalObjHdl(this, new_hdl, const_type_to_gpi_objtype(const_type), true); break; + } case vpiRegArray: case vpiNetArray: case vpiInterfaceArray: diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 9bdd2434..bba03664 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -461,7 +461,7 @@ def type_check_verilog(dut): test_handles = [ (dut.stream_in_ready, "GPI_REGISTER"), (dut.register_array, "GPI_ARRAY"), - (dut.NUM_OF_MODULES, "GPI_PARAMETER"), + (dut.NUM_OF_MODULES, "GPI_INTEGER"), (dut.temp, "GPI_REGISTER"), (dut.and_output, "GPI_NET"), (dut.stream_in_data, "GPI_NET"), From a9cd7ad2625cd8cbfcde3fb5959fb7316b33b4a2 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 19 Jul 2020 22:33:53 -0500 Subject: [PATCH 2489/2656] Add other type parameters to test_discovery --- tests/designs/sample_module/sample_module.sv | 6 +++++- tests/test_cases/test_discovery/test_discovery.py | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/designs/sample_module/sample_module.sv b/tests/designs/sample_module/sample_module.sv index a0c10647..914e7b7d 100644 --- a/tests/designs/sample_module/sample_module.sv +++ b/tests/designs/sample_module/sample_module.sv @@ -37,7 +37,11 @@ typedef struct packed } test_if; `endif -module sample_module ( +module sample_module #( + parameter INT_PARAM = 12, + parameter REAL_PARAM = 3.14, + parameter STRING_PARAM = "Test" +)( input clk, output reg stream_in_ready, diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index bba03664..eb059d51 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -461,12 +461,14 @@ def type_check_verilog(dut): test_handles = [ (dut.stream_in_ready, "GPI_REGISTER"), (dut.register_array, "GPI_ARRAY"), - (dut.NUM_OF_MODULES, "GPI_INTEGER"), (dut.temp, "GPI_REGISTER"), (dut.and_output, "GPI_NET"), (dut.stream_in_data, "GPI_NET"), (dut.logic_b, "GPI_REGISTER"), (dut.logic_c, "GPI_REGISTER"), + (dut.INT_PARAM, "GPI_INTEGER"), + (dut.REAL_PARAM, "GPI_REAL"), + (dut.STRING_PARAM, "GPI_STRING") ] if cocotb.SIM_NAME.lower().startswith(("icarus")): From 9a7d107590dcd2e79bbd2c942f637cd97d9c59de Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 9 Sep 2020 18:09:32 -0500 Subject: [PATCH 2490/2656] Update label descriptions in CONTRIBUTING.md --- CONTRIBUTING.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ab6cc89b..1f250d1d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -138,13 +138,19 @@ The `category` labels help maintainers to filter issues which are relevant to th - `category:simulators:vcs`: Synopsys VCS - `category:simulators:verilator`: Verilator - `category:simulators:xcelium`: Cadence Xcelium +- `category:codebase:gpi`: relating to the GPI or one of the implementation +- `category:codebase:pygpi`: relating to the Python wrapper around the GPI (embed library and simulator module) +- `category:codebase:scheduler`: relating to the coroutine scheduler, triggers, or coroutine objects +- `category:codebase:test-runner`: relating to code for automating test runs (regression manager) +- `category:codebase:handle`: relating to handles or handle types (BinaryValue) +- `category:codebase:project-automation`: relating to included project automation (makefiles) +- `category:codebase:testbenching`: relating to testbenching components (Drivers, Monitors, etc.) +- `category:building`: relating to build C/C++ libraries and extension modules - `category:packaging`: issues related to (PyPi) packaging, etc. - `category:docs`: documentation issues and fixes - `category:extensions`: cocotb extensions - `category:performance`: performance topics -- `category:core`: Issues in the core of cocotb (scheduler, error reporting, etc.) - `category:tests-ci`: continuous integration and unit tests -- `category:test-runner`: regarding the code for automating test runs To help new contributors find a good issue to work on one more label is used (following [GitHub standard practices](#https://help.github.com/articles/helping-new-contributors-find-your-project-with-labels/)): - `good first issue`: this issue is a good starting point for new contributors. From b5ed67bc7db1586c1a9e830f4018a640c64b4f47 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 25 Jan 2019 23:18:10 +0100 Subject: [PATCH 2491/2656] Add mixed-signal examples. --- .../source/diagrams/svg/example_regulator.svg | 3 + .../source/diagrams/svg/example_rescap.svg | 3 + .../source/diagrams/xml/example_regulator.xml | 109 +++++++++++++ .../source/diagrams/xml/example_rescap.xml | 88 ++++++++++ documentation/source/examples.rst | 22 +++ .../source/newsfragments/1051.feature.rst | 3 + documentation/source/regulator.png | Bin 0 -> 18510 bytes documentation/source/regulator.rst | 120 ++++++++++++++ documentation/source/rescap.png | Bin 0 -> 76553 bytes documentation/source/rescap.rst | 130 +++++++++++++++ examples/mixed_signal/.gitignore | 1 + .../mixed_signal/hdl/analog_probe_cadence.sv | 38 +++++ .../mixed_signal/hdl/analog_probe_synopsys.sv | 22 +++ examples/mixed_signal/hdl/capacitor.vams | 20 +++ .../mixed_signal/hdl/nettypes_pkg_cadence.sv | 10 ++ .../mixed_signal/hdl/nettypes_pkg_synopsys.sv | 13 ++ examples/mixed_signal/hdl/regulator.sv | 28 ++++ examples/mixed_signal/hdl/regulator.vams | 29 ++++ .../mixed_signal/hdl/regulator_block.vams | 29 ++++ examples/mixed_signal/hdl/rescap.sv | 25 +++ examples/mixed_signal/hdl/resistor.vams | 18 +++ examples/mixed_signal/hdl/tb_regulator.sv | 25 +++ examples/mixed_signal/hdl/tb_rescap.sv | 25 +++ examples/mixed_signal/tests/Makefile | 89 +++++++++++ examples/mixed_signal/tests/run.scs | 11 ++ .../mixed_signal/tests/test_regulator_plot.py | 69 ++++++++ .../mixed_signal/tests/test_regulator_trim.py | 123 ++++++++++++++ examples/mixed_signal/tests/test_rescap.py | 150 ++++++++++++++++++ .../tests/test_rescap_minimalist.py | 27 ++++ examples/mixed_signal/tests/vcsAD.init | 4 + 30 files changed, 1234 insertions(+) create mode 100644 documentation/source/diagrams/svg/example_regulator.svg create mode 100644 documentation/source/diagrams/svg/example_rescap.svg create mode 100644 documentation/source/diagrams/xml/example_regulator.xml create mode 100644 documentation/source/diagrams/xml/example_rescap.xml create mode 100644 documentation/source/newsfragments/1051.feature.rst create mode 100644 documentation/source/regulator.png create mode 100644 documentation/source/regulator.rst create mode 100644 documentation/source/rescap.png create mode 100644 documentation/source/rescap.rst create mode 100644 examples/mixed_signal/.gitignore create mode 100644 examples/mixed_signal/hdl/analog_probe_cadence.sv create mode 100644 examples/mixed_signal/hdl/analog_probe_synopsys.sv create mode 100644 examples/mixed_signal/hdl/capacitor.vams create mode 100644 examples/mixed_signal/hdl/nettypes_pkg_cadence.sv create mode 100644 examples/mixed_signal/hdl/nettypes_pkg_synopsys.sv create mode 100644 examples/mixed_signal/hdl/regulator.sv create mode 100644 examples/mixed_signal/hdl/regulator.vams create mode 100644 examples/mixed_signal/hdl/regulator_block.vams create mode 100644 examples/mixed_signal/hdl/rescap.sv create mode 100644 examples/mixed_signal/hdl/resistor.vams create mode 100644 examples/mixed_signal/hdl/tb_regulator.sv create mode 100644 examples/mixed_signal/hdl/tb_rescap.sv create mode 100644 examples/mixed_signal/tests/Makefile create mode 100644 examples/mixed_signal/tests/run.scs create mode 100644 examples/mixed_signal/tests/test_regulator_plot.py create mode 100644 examples/mixed_signal/tests/test_regulator_trim.py create mode 100644 examples/mixed_signal/tests/test_rescap.py create mode 100644 examples/mixed_signal/tests/test_rescap_minimalist.py create mode 100644 examples/mixed_signal/tests/vcsAD.init diff --git a/documentation/source/diagrams/svg/example_regulator.svg b/documentation/source/diagrams/svg/example_regulator.svg new file mode 100644 index 00000000..1a21e27e --- /dev/null +++ b/documentation/source/diagrams/svg/example_regulator.svg @@ -0,0 +1,3 @@ + + +
      tb_regulator
      tb_regulator
      i_regulator
      i_regulator
      vout
      vout
      vss
      vss
      vdd
      vdd
      i_analog_probe
      i_analog_probe
      i_regulator_block
      i_regulator_block
      i_resistor
      i_resistor
      trim[3:0]
      trim[3:...
      Viewer does not support full SVG 1.1
      \ No newline at end of file diff --git a/documentation/source/diagrams/svg/example_rescap.svg b/documentation/source/diagrams/svg/example_rescap.svg new file mode 100644 index 00000000..e5b9e4a2 --- /dev/null +++ b/documentation/source/diagrams/svg/example_rescap.svg @@ -0,0 +1,3 @@ + + +
      tb_rescap
      tb_rescap
      i_rescap
      i_rescap
      vout
      vout
      vss
      vss
      vdd
      vdd
      i_analog_probe
      i_analog_probe
      i_resistor
      i_resistor
      i_capacitor
      i_capacitor
      Viewer does not support full SVG 1.1
      \ No newline at end of file diff --git a/documentation/source/diagrams/xml/example_regulator.xml b/documentation/source/diagrams/xml/example_regulator.xml new file mode 100644 index 00000000..48f8ac4e --- /dev/null +++ b/documentation/source/diagrams/xml/example_regulator.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documentation/source/diagrams/xml/example_rescap.xml b/documentation/source/diagrams/xml/example_rescap.xml new file mode 100644 index 00000000..aaf7ae59 --- /dev/null +++ b/documentation/source/diagrams/xml/example_rescap.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documentation/source/examples.rst b/documentation/source/examples.rst index 8dd322d4..450c2fd2 100644 --- a/documentation/source/examples.rst +++ b/documentation/source/examples.rst @@ -118,3 +118,25 @@ The directory :file:`cocotb/examples/axi_lite_slave/` contains ... .. todo:: Write documentation, see :file:`README.md` + + +Mixed-signal (analog/digital) +============================= + +This example with two different designs shows +how cocotb can be used in an analog-mixed signal (AMS) simulation, +provided your simulator supports this. +Such an AMS setup involves a digital and an analog simulation kernel, +and also provides means to transfer data between the digital and the analog domain. + +The "-AMS" variants of the common digital HDLs (VHDL-AMS, Verilog-AMS and SystemVerilog-AMS) +and languages like Spice can be used to express the analog behavior of your circuit. + +Due to limitations of the underlying simulator interfaces (VPI, VHPI, FLI), +cocotb cannot directly access the analog domain but has to resort to e.g. HDL helper code. +Thus, unlike the other examples, +part of this testbench is implemented with cocotb and the helper part with HDL. + +.. toctree:: + rescap + regulator diff --git a/documentation/source/newsfragments/1051.feature.rst b/documentation/source/newsfragments/1051.feature.rst new file mode 100644 index 00000000..ff5b8a96 --- /dev/null +++ b/documentation/source/newsfragments/1051.feature.rst @@ -0,0 +1,3 @@ +The ``mixed_signal`` example has been added, +showing how to use HDL helper modules in cocotb testbenches that exercise +two mixed-signal (i.e. analog and digital) designs. diff --git a/documentation/source/regulator.png b/documentation/source/regulator.png new file mode 100644 index 0000000000000000000000000000000000000000..39380251d6287952bac1d3691762432437c4ecba GIT binary patch literal 18510 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
    1. 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. + +.. image:: regulator.png + +To run this testcase, call: + +.. code-block:: bash + + make SIM=xcelium TOPLEVEL=tb_regulator MODULE=test_regulator_plot + + +test_regulator_trim +------------------- + +This testcase runs an automatic trimming routine :meth:`~test_regulator.Regulator_TB.find_trim_val()` +to find the best trim value for a given target voltage. +The determined trim value and the resulting regulator output voltage +are printed to the console. + +.. code-block:: bash + + 0.00ns INFO Running trimming algorithm for target voltage 3.013 V + 3.00ns INFO Best trimming value is -1 --> voltage is 3.1 V (difference to target is 0.087 V) + +Note that the output voltage does not exactly hit the target value because of +the discrete nature of the trim steps. + +To run this testcase, call: + +.. code-block:: bash + + make SIM=xcelium TOPLEVEL=tb_regulator MODULE=test_regulator_trim diff --git a/documentation/source/rescap.png b/documentation/source/rescap.png new file mode 100644 index 0000000000000000000000000000000000000000..1fdc9532b14f7a87aa952b8d46da3b8e01dcc0d7 GIT binary patch literal 76553 zcmdqJbyQXF_b$3oDFsA9T0#&JX{Eab0Ra&NX;8Ymk?s@;X+#MLrCS;V6hx%E8>IWp z_4$5(=XdV!o^j6~_ndLZxO)uUy5rqzz3YAFeCG2!a|J6ZNaJ25yNW`gaAh7#D5FqU z(Bsj_@ZFc#+3m@`e2~ ztJn5UdbY1n7JBy9=2rIRCi>KluWap1tSle0u(Pl;Q5)OaTl2B9{^$EFR<=g0_^*jc zQ7CGZjD)C)bMo4_i{|fvbK$K)Bkx4LKs3V}SCk|kW8IQeY2l3D&Ep~n~hH!jY?I|eysBF4XqhINt>2vKwh|SK^3rC^bXXa8+eQIQ2WU`&m zqWeUe$rT~1A1+>WG=8WX|9&iYuJEJo{Oge^jY~i;@$ZLJ0vpZ0U*~A@!~XZjqi@j5 z1^xRG#-=Cs?++XLVH5oO!*^)q67c{1X;Qr=zkh$Y1MB}EL%aGA-nup5goT_q?xlau zDKgh(XF)WE?!$)(YAd%G#fdmUzF|V=@>^pgT9$OAl!Ai7daVp{ zlmo?|H&JjEavX7Q^AVz6egp-tk9M6a8cHFV=S@xy!wYqFy_uHa zt(~1_|7*m>HnUPpnw9t{>#Zr#)AN%}5_bJ7wRLsT@7@K>cg1I!4HiC@k$Dpy9xht@ z<;xdqTid2umz}7jq!4&__E+|k#|jDobTSSn3$TXz2%g*&3p&R0fy1Y)hgsG;KRqJh zwWX^++kffd_A^z`X0{ENLckgSs{bVvwV%l1j})7o1^Hw_HzE{15fO%!m6iEOY3_)f zgtYYRQePI2!wO;2Ba=)kVzH%I^1FBM7MGWEkyKbs3Q0&w>F=!!IB&NSqb;c>)b9Rz zVmw;TyuUGi?ZJZwsBeK(ov_)|7#m9fGp^6E*%C~!*Lf3_!13xT6EpLd zS6%N;+%6-#Yvc02mSNRL`S&Xqp6917STy*=#F*UN+`htA4QE`askU%(lsEb{B5H1K zVrGp}k;jj*UiT#V6Wmi+TC8^7zJp4Cp0Da8;3Xr2z3?OD8Wj~41L4)m>guMR9^9&` zs*bL%hrB}6)ZeDcQc`XU2nx!ZrXqWMfc_5>BwV|nYfWbin4+PI>h{T84>m0J4;Omg zUq)5iFZ*00W|?ZI@@n~0@G?3o$~#i%80+G|>mqP_wtevM^w}qc){t8d*bLfm;0iSL z_1%b}(`yO3aqNto871dGu|!bx^y~}`rK+mBG+eT%6{(i1;=G(4zxC@0YesP~zE-t; zBdj50nVxBWQb?(urC9w_(44?-Q0%;|@A1;aL2-wYWNZTvOto65UfyLOFaFhQr<{{ADf@L>|+i&HzejH^1VI`tH{ZrxfM zt+2u^ih;9QYP9i(c-lv>(En6e5>~~Hn>T&13FyB~PErjP>9kMOx!KHrzeOSJNrA$r zpa?6}s>!q%EmzD{LAD>vW8l!xi!;qM_rKT>XTrEFMs#8S8IP3S|MlxvvCGbD3Vuf% zRBwZqm;2e?ptZfdgtavrEaXrf&m(je7M9nu&+-RgGhhbbk_Y_$t-|!O90y|10)Z_Y zT#f=e64iJ8u<`KPGQdYQ6wK+R>D773Xd-txkCp*wL?E}UN%&75Xy@yAjC~}+c+LGqp** zUIQQOSd8!EHnaAz6=(}v&{r`Le{`&ts*{T?oW@qPgeD^6YEtOh7c(|bVV*vl>B)&o6<_>pD z!s^K>F8jiCnH zG!0s`3Su8TI3m~Y-t#yQAhGjEYoKX6E&llw&iOaKj2M>Eq-bwljqUtZcX#)w)Kt7p z=~GN)3zPf528-JJeyhBF@lrfGJ`h5mv;B%+x1gK#-#^T6d3jbww6vxN${wJmMt-F5 zB*yu3NMkbm{hJ>)Twvy8?R}@=Yn_4gxTd;#b=BV;{7l<7Y89h)cV`XZ%Y1xdiO7g* zmC{l(#}jotE~}1iEMl5k)1433~=#zbdXwp`}>Lu#OZON*97>Te)W{OT~~PZD&zNbkx-=LdX&pnx=f8x zdqg$6+y0b_W4UF$&!tl3+;g(nlyGIY8q<*rzVo6g&-UEWFSiKOaTLQF< z?Q4@(&S&t?ky6oxXj*z?b-h{qS}?hjucbLPHRGdrAhks*K*Evz7p>kGa?pUBc(rh5 zfbRpjoIfU^AyM)1kv=o}STyNnr4kc8@2zE?O2ZR_5<~icib#GG?dBimN~7^hsJT~0 z84|hPs`>t^xgy#MT$&FHX;|+O-<35pMo)jPK@0yb9JHcl<-UHGtGxF+ow0>QbbLJK z!KwS>sHpg~KxKY@<5wJSM}}#NsHrS@hdUm0Nay+rmcAomaIlxq7Nrr8l||7+sVN32 zOG_IUe75(Hl(g>ZlGavYm%W{0zpkLr@qi`Zr6}58Sy{!Ewa;c1#Uk1=*aDnnkHyl{ zO9@SlZc$hGj5OUu&wgdi$}B3XPWn6VyW@i=gm2>-pU|U;rHg1`l;vMbR}?9T%1Zx? zv$0}J_%>dAeF39mUhx4T1H-fCF0q5BRq0~ShCg|0Wc&{AGgeCP6B+N&;+8TgFdI}Y zuTd)>9ZD}Q#o_S3bd!$Igj3PYb?MmLLPs0zxdni7oHAa|_&Xr8NK8644sf0x21p`%Eb*O8OSK1$m@Q`K4Xt+ySWW{pb8`qc6D^ zT4aph=i{{Js4#pfXE)&{%~Dj^kNftk75y$3S8sKt3h6VCnz43i?!nGjPOAP7w%xh0 zz13vlHV&e}1@hseW*ln%;hc;iA&Lu`V%pk5WW>Y||0%q1i3sRbRt?L`t*wg#zZTbZJuBD6!#3u~=wT$0FPE&|M6bvc%X0J@l9X&L=*=0Q zq~cm1y^PW<(rPYa^e8-+*Bi|x(A?R9?{WFdJ6BqE$2|z}@boNRC&3=9)r@s~zg1q% zz8oKZ?5;;Xj8<61`*Hm?-#5p={cdSo-4NAnzw|j5Q7~5-jDC z`bem#hDyya<6l;6jBO|nZ>;04_9~8R60;`l4*OO3clD6JZu3)#s8ZZvr-?IQQHfB> zwfE>DYp%9R+l{?(Juqor@U_ErPd&(9SxFjw1>1-i>zAJ^iugT|DuPa{e&vZbOm$1|EajcI+=Oc$Sr0EyWAhi?92HB?09(9Lc+of z;^GTyCHSgaq<6972_$uUtcWZt=YO)$#`ug<_bMuY)QFNs6p>5Tl08G2W zk5%mib=ToiR^--7eo<3nV@7>*6>aCQn}7EAqdq0S$;w7Qt*xCRy@j4H6|OtGhBNw^ zX^4ohX;8A_cB^u>kD-Z4c~Ld{b-Tg<=l`BmH7EZw*Iws{|GV;h2xjYI0c|T}h(3Z$nsF+n=0-AlTov)rtWVGLm9}<%Dqg`5+gLR#mU#J6cW#cLqmi>gkd^%98XqoW=Z z5$#c6K_Vc_{o8P=g#)uEi1A`KZpAN0EatTabPpwU^ofh3>*j(U zw|QGl4|B}%`}?l5+HV#3=4S+kI~A#LTNbe)M90$h-RCUq|3FJq6c=SffR_cI@3P?K zVi!}RD{dnOyYVMB`NiF9+kvdF3`UZ&NGLaanfv)>6F-fyZ?B3!?qTYs+9~R}f4H|M z@BT;tr=gZC{7XvG%sTF;FU<3AK?4>*zFipfvpCnS*IkUe^Q;R~`^|f*5hCKe9N#yMEX>~vT_D0-F+xjGz>}ki1 z8=P0>6B8LmDyZfYKXzWX^Qc<=GyPN4*Jic>!#eexCOWTG_sc=4%`WGlO^rVo2>*VIJ_cRbRze~&W+jGd zvHIZKd;&@G>O?d3QFN?IcF{{G!}C?04vjB22WJ-U+`or;#-c5u?8sHv<1uRf)qhDl zjGP;9wA`XO?tz-zvFhg+Z??9q{Q?5+(9odW=6CcfvKs$`)oc8ym#a5P(9zM++Qz1; z=!X5;HINwXP}Cd)$25mc{-?6O*Z8J5r8I#m4-XCXTV$b#Ax#yeYt+_VT)z$niw3 zlwYrEA=~sjiaVJacEb5$dGu0WRSU(Y3$?1}q#etJj%*whD9+t2v3@wLMuhR&ov(Ct zivAg#oN7DS6u)`tK}kupYGK>=HS@LnPLF>rbh+FM$tfM1bxcNd_O@u8D>{Nzdh!RBxY zW=2MaB4gZAZ^mVX6u#$K&Z>o)*!S+;Gao8OYiw*(WFV|`S3lr=`uMSkq9XoV*}EF$ z=E1;?EVt(ln6|XFHTCxs$Y$S!`V@oP2B?gLMX>M82j>`KLBzF8x<87=O0Zg zoe5l0@s}^6z>UZICk>zoLgR$g8XC69=$?N`sS>?iSTu%>zV`J4azA$lsfM_6t?oVh6s?9cJR z=5Nvn<}xv*+Pb zSP!d6#cdj+da8F&_0oY1Q;-6pPc;q1)}ST5s=?0}8|Hw%v!?6}i=j?fi<|h|G5_i>8)wU%^1)qa-u3mm+ zdOTy~BUFaVtAn(6O>RRq7dX(@=7GTXk#J;1em92*mp-e=5aJNMYo$cPpW zdwP2M`26tD8DZ{#pzi4C5RsC?fYNaHtmJi1(@blq4NwHYrRsh2Mq^-QEmD$eZEbbF zI6FXisQyn+vy`%=fQV=rtFrrIJuQ~ZYlpE}f5I{|GgE9jz=-+={DTiJIhXGGNZDg~ zc}ac!JEy0oD3nG4k;ln?YpS5zRuA_iH5|!i-2o*N6B7!Rot=%4hFn$?VL)sltSG`K z+Dv^z^Q}}Z&_I9kS{cY+`jvf+@G83F z+R&Tk!0QHULtk=oa!~1`s_d`3N!BLn$l*uCMMO}?d#iyyK9_te;Viet?0!xkZqJf% zni5Kdk-T|m^dq4z063pJKt1w1ZTzlyWn?tFnC8`%qm+e00dw05kYsD4W^<-DLwsv@ z_t9fdv~MuaUu@@P-S-C6hU(n7zXAj5dFC3sQwEGNCLTqO;}fg_JU2Ht9;gj2fK@DM z=N;|PqluNfT06nSKLO6`R$@VMauV?2dA1Zvh@`%sCRK3rK3JwZnNJN zzffd#uV0-JujI?K^CA1NyK9do^5N5`K_>>z%z9hNxwGBaUR9OfP7FZG`|#0<6K;&( z`%OxUvCb_5lF57j$c1)Q$iFcxR?Ox8s5 z^nWbi8MxD1TqK#!GkRC!!3;RVI#ZsbaV0>ig;rcwA zl&j|VzYe=tT%hR2) zuAi5Uek9*OwEz#iXQ8B|G~-XKjW32}v%92(CZbvIKCG*0Z(o`q(HpK3`4}@+oER8?u>nTnXH=?7X#jt2TDx@%0J8iO* zY7}%PUz|){_{q@|*`gGgo|+Q34Bpe(SaY^`=eH& zpVB<5r54E_z7Df?aw2~9>eX@ilXwhvARr6|1{OyT2n&84;XCOvK<@>9>uWR{Hhnn zB|n7-w6(Rxvih)S{DQ*52w59Wzjh`+pM3E2>C^2-OsXZF`1ts1z~!@BPl@>2v`11A zqa-vmG{Os`DZ9_csLs|WYg)%HQhzu6F)qEMKg~MRpR(9Zd0{)F$L`pQ=3CyFwn0>K zVFwc;;Tf!PSpP+4?Qpibk^SOy+RvFrPbYjfQ#$Zf-7C|j;5%0;=J5Ms3j`h#tdu^9 zIyq|k-aH$zjsTXCu?iZKGW?h;gEFz{I9J7%sl2BC{GEUNy{8p2AH*{f4lg0Ym@C+9 zFOJx|JF)*R^yrPVP`Jpf${mxP?W9%m+0pB(FrI0O(fjs)dsA#{)7V>OWsnd{;3tPs zgub))#f3S+s{E%(2mXr+i_p)v9z<1eVXJDV?rn4R3;rS^$~4}bAfqa}&+x4$_FlBn zD|8D{A=$1jn;lpBWC{vhr7GKWdUIM@z3HVFSq3j54@TVPpUE$(7aJ_pvH@9OZKUkl zH6n;pZ{NO!zhqy9|D+-%&Aq6R%@)(u)h)}v8P+*(3y@?*hxpd5X~=IR+?EvdatY~H zML9X%2J@V4EiLJ4McP<6I1*_>9tg!Ou+#kzWTGS-%CyU*qaPG>_wGTuBZuJ_ zSXlm8y~%S;X)B6^rM2!5M#g!SfL`v~*H>NXiVRV}FW;b*5>Y*RxRUtLhz%s0SYE3l z+0L#maY!A&w(g$2d-o3O`gQK%cLo*~xFQvd!Ibi|{M@bGA7-((9mNtNY3s*Q0`wiw z1Ih;q9=q+G7+5>0%!CUuqoEA=kMs_WPPSUDi2Dj&(V7c*;@&G-(CeFYNHcTGzI7)l zuh_VY6v*(Yb@Ol!;*za2S~a#$UeR~Jd{$9*saWExvw9E%9Z2b z!6ns?A+cE+C+9FTn=90@N+49Pr{IveG-q29d?Q{>yRG0=mwXHrrJem#oX)dpVR{d) zg655c)8h3p(v>IiGzS*?*33>TC4aZPEez>o_o+lCyuAFhR8&&x4(z=Q9UY4~tEX!T zfJGex)a)`#|6S#WqT%AVf@t!!%uMQ!ZbDwQ5e@fE_!N&N{XVT29 zEHqSMVfDSIb8~awdV1DV7wV|~{P}|+{F9J~=n|^_aORrtdt=nGgd~UsTaZU)AfhzE z=JwHR4#+V2nfju_GUfbZH6NAdAKmz9-qI%~anfdw&# zLdboHj>gVyva@P$&_dM=phLuW(Qh(w*U}S2Q3q0OSL72=T;y zYL${r7v+aPRO|8(2>C#*8pr9ZNFmpB-u&2<6kL?U->MHM52s964T*EN`^rly)fEG% zSusD8jZ#|fS2J^|**(EIudCR5PU9(I#x#)>a{VIys<0Zl`?)^PCBe15b|#N{PN75I zTMz4nL&j!oA32t{eV%yZOye`ELVgD>tpZj7Q53tnm_%e?P2ttt7(ykTnvY&SVM1se z_wdvosAjI6y3ozP9$9Gm4==#{*}!@K(#5?Q^}Ni3LrZ_G-pR_aNE=sP?*`MFxqM`nbbz6r(rg?hN_ToGsO_h~<>5jt zknsYutO%T?Bpbhv*AZo1K?ER>foDgp8d?EFu)}scla~YZG&MEl{q`-P?*N`{6-Cnb zPrIa0YEdOU>Km_xi#@eATmlKBx@ZWHGy;J`iCI@xbrWXcThq;f28UbIBpilV$GgkE zYis5ZPi6qZ@wn}O9)3>mw!hZ+?ak%wrfaOI-T?t#w|yobUtF)3bzT>qv1T|}Sk4Uj zPr6Njl6gdiYdKc=CWoI++QZ!)*{pJ@0;G^&+K6@B-{d~~bm!3{!hF>NF&CGre-8}X zythb47$xHB%4a#w!JJeDDpVMy;K-B%D1!On(zz-#n_d_EHwg%kcz6W^BNLL<@p*bW z_1a{^q~VWIz}?f!%fV+S2U(EoQ1Y}ZlAd=6J9CMJ$QPzojiB56*D@O*tz zpJte(=*jh);AenpL&(?Yx|>bB`A*fn$isX;x-5Q%9=G_(wxunQ5vDl z;mwtNHTjSnJUd&T?mulGyjN1nzrPUPBm68NVEXF$V~5~qH&vI*W`p_nzSbXJLVsD& zw7ls{^bo&ketv!&Dj^D(6)8_oA$@)QT@Q{#93)5k?mZW7 zL`1|=|EKHUzkj!0>ZL)hHWU!8F?XL#!MuR@C{73MPyrcq#@wy+Jgb&zn)^H7e2YvOTd zr5tt*Ej>L3472n*mjGE$@W$cE2|2~igxe+!H8qO)WqAwCTGadZW;aKH&e(lf6qKW< zFN&Q=MH%T*qb`8rn)H7?D)dzf`@2LXVoL?)Lus>>?r`1OR;|IRX;yJYCb+P@SsTa zf0RdCN*zJa8nu7`1(ZzU8X9Csv0LcPWxZ&gLyA4(7s#<$@;zLXePiR$=b#{hOw*h;V3|tw=#nN_}za^I@bQ?ShzYmOnhW&lvtoT6n^Ec30+3gnOVDlng z6&~lUbkDsAsjS8aMYTS#k&%T5IHXtDrVWfT{EllPdU`aFtNO|f+KR(JgpeI>7Ad#?VYjrR{06qim`kP8xY}zu)sfod6c9T-dj^(1tbVfY~3^mq`=K3x5Mo;IghV|+1 z|Goag#aZNaLG2>h9g#ikD2zmsT!r~}6f{>LN$O2zVgBVh#frzCJ$m~n&p}U54{(NC zI`8rRdMM08;=0>T5BK)Mdn=*d(@4e|eu1(jq$Dy>Qsb~XJ>1^Aed&ha6(r8<_Nm=* z4ca(}7-k4&4;yH$?r7UrY z(dmQ_k1XgE`@NDYANrqy)V#^5AX91}1Yr}rKFK2r_xKIyR=D>N0QU3I!AhH1%%mEw zFJDnN2Dtu#jAh;}WX%#PcARlik*<2$~6h$v{qv(LB z@q6>-tHP6k)f#^3FJCFEJNI*O+$1V2>=@mmC7}OJE%3eb&B$-4@Q?A{1T96k_+bxV zXY`4E)dgx>Zrq_PNDd>C*GiO>Bq$RMDm#;DWb{&=W2mZ6_vpUu-l%(c{^sv)mnO4v zyrEYCTQn2%^x;}b$7m_`M#*c@JojTn)=(1v2BYF;oaK{H-%z@1hpt{QTEWfS#^;3w zb<+!tq^DuE+ZcsEl1;wGNNkK=DZjB^rSSnzCgw`UnOS1(Z2FrH?Xs)>Zu{Tr%?6LJ zg&}SX%5A)ND*>wctQe0ao~HiomP3{~Jo%Q7qN|^1)dqc7GuKQZ2n_W&P9ijjlesA{ zq3-=!*y2r8Cw`W4<4@Vbr(r{-c)o)^M)$FjSX z4=1!|dA)=P&*kMS20sIPgo(+J=6Y3(gSlgc%Yo5cqvG{=bU>agE`dnAMAu|Bt_%=) z7eB=p>v4fEIJyWy#xtq)&E{*&ERu*Mo@M4*C?C`e4QM2NAhz|+M1)INfG9KWh(7HiH6c8zVUdbYE$%c@yPzjXPH#;-ky>~AQqE$y{ z=bPBr*m{p2$-D?n))q;1U0S-`H7qK~G{mVGxq{7p*h-fTYC|O6#?yL@vI69

      Y$H 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 literal 0 HcmV?d00001 diff --git a/documentation/source/rescap.rst b/documentation/source/rescap.rst new file mode 100644 index 00000000..00cbe4e5 --- /dev/null +++ b/documentation/source/rescap.rst @@ -0,0 +1,130 @@ +############################### +The cocotb ``rescap`` Testbench +############################### + +.. versionadded:: 1.5 + +This is the testbench :mod:`test_rescap` for the design ``rescap`` showing +how cocotb can be used in an analog-mixed signal (AMS) simulation. + +.. note:: The analog probe module used in this testcase is currently only implemented for the + Cadence Incisive and Xcelium simulators (with the AMS option available). + Help is appreciated to provide equivalent modules for other simulators. + +****************** +Overview Schematic +****************** + +.. image:: diagrams/svg/example_rescap.svg + :width: 100% + + +********** +The Design +********** + +The design consists of a resistor and capacitor model (both written in Verilog-AMS) connected +in series in a SystemVerilog module: + +.. literalinclude:: ../../examples/mixed_signal/hdl/rescap.sv + :caption: rescap.sv + :language: systemverilog + :start-at: // the design-under-test + + +************* +The Testbench +************* + +The testbench consists of both an :term:`HDL` part and a Python/cocotb part. +While having HDL as part of the testbench is not common with cocotb, it is perfectly possible. + +The HDL part of the Testbench +============================= + +The testbench :term:`HDL` part is written in SystemVerilog and instantiates the design described above +as ``i_rescap``. +It also contains a probe module for analog values as instance ``i_analog_probe`` — +imagine this being a multimeter that you quickly connect to different nodes in the design, +measuring either voltage or current. + +.. literalinclude:: ../../examples/mixed_signal/hdl/tb_rescap.sv + :caption: tb_rescap.sv + :language: systemverilog + :start-at: import nettypes_pkg + +The probe module can capture voltage and current of a node specified by ``node_to_probe`` +(a string in this module containing a hierarchical path). +The capturing occurs whenever there is an edge on the logic signals +``probe_voltage_toggle`` or ``probe_current_toggle``. +The captured values can be read on real-value signals ``voltage`` and ``current`` in this module. + +Here is the capture code for ``voltage`` with the "user-interface" highlighted: + +.. literalinclude:: ../../examples/mixed_signal/hdl/analog_probe_cadence.sv + :caption: analog_probe_cadence.sv + :language: systemverilog + :start-at: var string node_to_probe + :end-at: end // probe_voltage + :emphasize-lines: 1-4 + :dedent: 2 + +The cocotb part of the Testbench +================================ + +test_rescap_minimalist +---------------------- + +This is a very minimalist testcase. +To run it, call: + +.. code-block:: bash + + make SIM=xcelium TOPLEVEL=tb_rescap MODULE=test_rescap_minimalist + + +The testcase supplies ``vdd``, +measures some pairs of voltage and current at ``vout`` +(same as the ``p`` terminal of the capacitor), +spaced 50 ns apart, +and prints the values as shown below. + +.. code-block:: bash + + 50.00ns INFO tb_hdl.i_analog_probe@tb_rescap.i_rescap.vout=2.03e-14 V -2.996e-07 A + 100.00ns INFO tb_hdl.i_analog_probe@tb_rescap.i_rescap.vout=3.029 V 2.67e-18 A + 150.00ns INFO tb_hdl.i_analog_probe@tb_rescap.i_rescap.vout=4.862 V 3.574e-18 A + 200.00ns INFO tb_hdl.i_analog_probe@tb_rescap.i_rescap.vout=5.975 V 6.285e-18 A + 250.00ns INFO tb_hdl.i_analog_probe@tb_rescap.i_rescap.vout=6.652 V 6.171e-18 A + 300.01ns INFO tb_hdl.i_analog_probe@tb_rescap.i_rescap.vout=7.063 V 6.033e-18 A + +There is no current flowing out of the ``vout`` terminal, +so the current measurement always yields zero +(within the accuracy of the analog solver). + +test_rescap +----------- + +This is a more advanced testcase. + +.. note:: This testcase depends on `matplotlib `_. + +The cocotb part of the testbench provides functions to: + +* do the sampling of voltage and current of a given node (:meth:`~test_rescap.ResCap_TB.get_sample_data()`), +* plot the sampled data to a file (:meth:`~test_rescap.ResCap_TB.plot_data()`). + +The testcase supplies first a positive voltage to the circuit at ``vdd``, followed by a negative voltage, +thus charging the capacitor in opposite directions. +The following graph shows the charge curve. + +.. image:: rescap.png + +There is no current flowing out of this output voltage terminal, +so the current measurement always yields zero. + +To run this testcase, call: + +.. code-block:: bash + + make SIM=xcelium TOPLEVEL=tb_rescap MODULE=test_rescap diff --git a/examples/mixed_signal/.gitignore b/examples/mixed_signal/.gitignore new file mode 100644 index 00000000..c30a375b --- /dev/null +++ b/examples/mixed_signal/.gitignore @@ -0,0 +1 @@ +run.raw diff --git a/examples/mixed_signal/hdl/analog_probe_cadence.sv b/examples/mixed_signal/hdl/analog_probe_cadence.sv new file mode 100644 index 00000000..1018ed89 --- /dev/null +++ b/examples/mixed_signal/hdl/analog_probe_cadence.sv @@ -0,0 +1,38 @@ +// This file is public domain, it can be freely copied without restrictions. +// SPDX-License-Identifier: CC0-1.0 + +// see also https://www.eetimes.com/document.asp?doc_id=1279150 + +module analog_probe; + + var string node_to_probe = ""; + + logic probe_voltage_toggle = 0; + real voltage; + + always @(probe_voltage_toggle) begin: probe_voltage + if ($cds_analog_is_valid(node_to_probe, "potential")) begin + voltage = $cds_get_analog_value(node_to_probe, "potential"); + // $display("%m: node_to_probe=%s has voltage of %e V", node_to_probe, voltage); + end else begin + voltage = 1.234567; + $display("%m: Warning: node_to_probe=%s is not valid for $cds_get_analog_value, returning %f V", + node_to_probe, voltage); + end + end // probe_voltage + + logic probe_current_toggle = 0; + real current; + + always @(probe_current_toggle) begin: probe_current + if ($cds_analog_is_valid(node_to_probe, "flow")) begin + current = $cds_get_analog_value(node_to_probe, "flow"); + // $display("%m: node_to_probe=%s has current of %e A", node_to_probe, current); + end else begin + current = 0.123456; + $display("%m: Warning: node_to_probe=%s is not valid for $cds_get_analog_value, returning %f A", + node_to_probe, current); + end + end // probe_current + +endmodule diff --git a/examples/mixed_signal/hdl/analog_probe_synopsys.sv b/examples/mixed_signal/hdl/analog_probe_synopsys.sv new file mode 100644 index 00000000..611a4166 --- /dev/null +++ b/examples/mixed_signal/hdl/analog_probe_synopsys.sv @@ -0,0 +1,22 @@ +// This file is public domain, it can be freely copied without restrictions. +// SPDX-License-Identifier: CC0-1.0 + +module analog_probe; + + var string node_to_probe = ""; + + logic probe_voltage_toggle = 0; + real voltage; + + always @(probe_voltage_toggle) begin: probe_voltage + voltage = $snps_get_volt(node_to_probe); + end + + logic probe_current_toggle = 0; + real current; + + always @(probe_current_toggle) begin: probe_current + current = $snps_get_port_current(node_to_probe); + end + +endmodule diff --git a/examples/mixed_signal/hdl/capacitor.vams b/examples/mixed_signal/hdl/capacitor.vams new file mode 100644 index 00000000..84ce0fca --- /dev/null +++ b/examples/mixed_signal/hdl/capacitor.vams @@ -0,0 +1,20 @@ +// This file is public domain, it can be freely copied without restrictions. +// SPDX-License-Identifier: CC0-1.0 + +`include "disciplines.vams" +`include "constants.vams" + +module capacitor(p, n); + + inout p; electrical p; + inout n; electrical n; + + parameter real capacitance=1 from [0:inf); // capacitance value, must be >= 0 + + branch (p, n) cap; + + analog begin + I(cap) <+ capacitance * ddt(V(cap)); + end + +endmodule diff --git a/examples/mixed_signal/hdl/nettypes_pkg_cadence.sv b/examples/mixed_signal/hdl/nettypes_pkg_cadence.sv new file mode 100644 index 00000000..222fcfc6 --- /dev/null +++ b/examples/mixed_signal/hdl/nettypes_pkg_cadence.sv @@ -0,0 +1,10 @@ +// This file is public domain, it can be freely copied without restrictions. +// SPDX-License-Identifier: CC0-1.0 + +import cds_rnm_pkg::*; + +package nettypes_pkg; + + nettype wreal1driver voltage_net; + +endpackage diff --git a/examples/mixed_signal/hdl/nettypes_pkg_synopsys.sv b/examples/mixed_signal/hdl/nettypes_pkg_synopsys.sv new file mode 100644 index 00000000..104c0877 --- /dev/null +++ b/examples/mixed_signal/hdl/nettypes_pkg_synopsys.sv @@ -0,0 +1,13 @@ +// This file is public domain, it can be freely copied without restrictions. +// SPDX-License-Identifier: CC0-1.0 + +package nettypes_pkg; + + nettype real voltage_net with r_res; + + function automatic real r_res(input real drivers []); + r_res = 0.0; + foreach(drivers[k]) r_res += drivers[k]; + endfunction + +endpackage diff --git a/examples/mixed_signal/hdl/regulator.sv b/examples/mixed_signal/hdl/regulator.sv new file mode 100644 index 00000000..5d356ca6 --- /dev/null +++ b/examples/mixed_signal/hdl/regulator.sv @@ -0,0 +1,28 @@ +// This file is public domain, it can be freely copied without restrictions. +// SPDX-License-Identifier: CC0-1.0 + +module regulator(input vdd, + input [3:0] trim, + output vout, + input vss); + + regulator_block + #(.vout_abs (3.3), + .trim_step (0.2)) + i_regulator_block + ( + .vdd (vdd), + .trim (trim), + .vout (vout), + .vss (vss) + ); + + resistor + #(.resistance (100)) + i_resistor + ( + .p (vout), + .n (vss) + ); + +endmodule diff --git a/examples/mixed_signal/hdl/regulator.vams b/examples/mixed_signal/hdl/regulator.vams new file mode 100644 index 00000000..440d530b --- /dev/null +++ b/examples/mixed_signal/hdl/regulator.vams @@ -0,0 +1,29 @@ +// This file is public domain, it can be freely copied without restrictions. +// SPDX-License-Identifier: CC0-1.0 + +`include "disciplines.vams" +`include "constants.vams" + +module regulator(vdd, trim, vout, vss); + + input signed [3:0] trim; logic trim; + inout vdd; electrical vdd; + inout vout; electrical vout; + inout vss; electrical vss; + + parameter real vout_abs=3.3 from (0:inf); // must be > 0 + parameter real trim_step=0.2 from (0:inf); // must be > 0 + + real trim_volt; + + always @(trim) begin + trim_volt = trim * trim_step; + // $display("%m: trim=%0d --> trim_volt=%f", trim, trim_volt); + end + + analog begin + // FIXME: must limit voltage at vdd/vss + V(vout, vss) <+ transition(vout_abs + trim_volt, 0, 20p); + end + +endmodule diff --git a/examples/mixed_signal/hdl/regulator_block.vams b/examples/mixed_signal/hdl/regulator_block.vams new file mode 100644 index 00000000..1c729964 --- /dev/null +++ b/examples/mixed_signal/hdl/regulator_block.vams @@ -0,0 +1,29 @@ +// This file is public domain, it can be freely copied without restrictions. +// SPDX-License-Identifier: CC0-1.0 + +`include "disciplines.vams" +`include "constants.vams" + +module regulator_block(vdd, trim, vout, vss); + + input signed [3:0] trim; logic trim; + inout vdd; electrical vdd; + inout vout; electrical vout; + inout vss; electrical vss; + + parameter real vout_abs=3.3 from (0:inf); // must be > 0 + parameter real trim_step=0.2 from (0:inf); // must be > 0 + + real trim_volt; + + always @(trim) begin + trim_volt = trim * trim_step; + // $display("%m: trim=%0d --> trim_volt=%f", trim, trim_volt); + end + + analog begin + // TODO: limit voltage to vdd/vss + V(vout, vss) <+ transition(vout_abs + trim_volt, 0, 20p); + end + +endmodule diff --git a/examples/mixed_signal/hdl/rescap.sv b/examples/mixed_signal/hdl/rescap.sv new file mode 100644 index 00000000..87bbec1c --- /dev/null +++ b/examples/mixed_signal/hdl/rescap.sv @@ -0,0 +1,25 @@ +// This file is public domain, it can be freely copied without restrictions. +// SPDX-License-Identifier: CC0-1.0 + +// the design-under-test +module rescap(input interconnect vdd, + output interconnect vout, + input interconnect vss); + + resistor + #(.resistance (1e5)) + i_resistor + ( + .p (vdd), + .n (vout) + ); + + capacitor + #(.capacitance (1e-12)) + i_capacitor + ( + .p (vout), + .n (vss) + ); + +endmodule diff --git a/examples/mixed_signal/hdl/resistor.vams b/examples/mixed_signal/hdl/resistor.vams new file mode 100644 index 00000000..5f315dba --- /dev/null +++ b/examples/mixed_signal/hdl/resistor.vams @@ -0,0 +1,18 @@ +// This file is public domain, it can be freely copied without restrictions. +// SPDX-License-Identifier: CC0-1.0 + +`include "disciplines.vams" +`include "constants.vams" + +module resistor(p, n); + + inout p; electrical p; + inout n; electrical n; + + parameter real resistance=1 from (0:inf); // resistance value, must be > 0 + + analog begin + V(p,n) <+ I(p,n) * resistance; + end + +endmodule diff --git a/examples/mixed_signal/hdl/tb_regulator.sv b/examples/mixed_signal/hdl/tb_regulator.sv new file mode 100644 index 00000000..3e6f8170 --- /dev/null +++ b/examples/mixed_signal/hdl/tb_regulator.sv @@ -0,0 +1,25 @@ +// This file is public domain, it can be freely copied without restrictions. +// SPDX-License-Identifier: CC0-1.0 + +import nettypes_pkg::*; + +module tb_regulator; + + voltage_net vdd, vss, vout; + real vdd_val, vss_val = 0.0; + logic signed [3:0] trim_val = 0; + + assign vdd = vdd_val; + assign vss = vss_val; + + // the design + regulator i_regulator (.vdd (vdd), + .trim (trim_val), + .vout (vout), + .vss (vss) + ); + + // the "multimeter" + analog_probe i_analog_probe (); + +endmodule diff --git a/examples/mixed_signal/hdl/tb_rescap.sv b/examples/mixed_signal/hdl/tb_rescap.sv new file mode 100644 index 00000000..e0a1b31a --- /dev/null +++ b/examples/mixed_signal/hdl/tb_rescap.sv @@ -0,0 +1,25 @@ +// This file is public domain, it can be freely copied without restrictions. +// SPDX-License-Identifier: CC0-1.0 + +import nettypes_pkg::*; + +module tb_rescap; + + voltage_net vdd, vss; + real vdd_val, vss_val = 0.0; + + assign vdd = vdd_val; + assign vss = vss_val; + + interconnect vout; + + // the design + rescap i_rescap (.vdd (vdd), + .vout (vout), + .vss (vss) + ); + + // the "multimeter" + analog_probe i_analog_probe (); + +endmodule diff --git a/examples/mixed_signal/tests/Makefile b/examples/mixed_signal/tests/Makefile new file mode 100644 index 00000000..2a120ed1 --- /dev/null +++ b/examples/mixed_signal/tests/Makefile @@ -0,0 +1,89 @@ +# Override this variable to use a VHDL toplevel instead of (System)Verilog +TOPLEVEL_LANG ?= verilog + +TOPLEVEL ?= tb_rescap + +ifeq ($(TOPLEVEL),tb_rescap) + MODULE ?= test_rescap,test_rescap_minimalist +else ifeq ($(TOPLEVEL),tb_regulator) + MODULE ?= test_regulator_plot,test_regulator_trim +else + $(error Given TOPLEVEL '$(TOPLEVEL)' not supported) +endif + +ifeq ($(OS),Msys) + WPWD=$(shell sh -c 'pwd -W') +else + WPWD=$(shell pwd) +endif + + +# Files + +VERILOG_SOURCES += $(WPWD)/../hdl/resistor.vams +VERILOG_SOURCES += $(WPWD)/../hdl/capacitor.vams +VERILOG_SOURCES += $(WPWD)/../hdl/regulator_block.vams + +ifeq ($(SIM),$(filter $(SIM),ius xcelium)) + VERILOG_SOURCES += $(WPWD)/../hdl/analog_probe_cadence.sv + ifeq ($(TOPLEVEL_LANG),verilog) + VERILOG_SOURCES += $(WPWD)/../hdl/nettypes_pkg_cadence.sv + VERILOG_SOURCES += $(WPWD)/../hdl/rescap.sv + VERILOG_SOURCES += $(WPWD)/../hdl/regulator.sv + VERILOG_SOURCES += $(WPWD)/../hdl/tb_rescap.sv + VERILOG_SOURCES += $(WPWD)/../hdl/tb_regulator.sv + else ifeq ($(TOPLEVEL_LANG),vhdl) + $(error "VHDL toplevel not yet implemented") + VHDL_SOURCES += $(WPWD)/../hdl/rescap.vhdl + VHDL_SOURCES += $(WPWD)/../hdl/regulator.vhdl + else + $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") + endif +else ifeq ($(SIM),vcs) + VERILOG_SOURCES += $(WPWD)/../hdl/analog_probe_synopsys.sv + ifeq ($(TOPLEVEL_LANG),verilog) + VERILOG_SOURCES += $(WPWD)/../hdl/nettypes_pkg_synopsys.sv + VERILOG_SOURCES += $(WPWD)/../hdl/rescap.sv + VERILOG_SOURCES += $(WPWD)/../hdl/regulator.sv + VERILOG_SOURCES += $(WPWD)/../hdl/tb_rescap.sv + VERILOG_SOURCES += $(WPWD)/../hdl/tb_regulator.sv + else ifeq ($(TOPLEVEL_LANG),vhdl) + $(error "VHDL toplevel not yet implemented") + VHDL_SOURCES += $(WPWD)/../hdl/rescap.vhdl + VHDL_SOURCES += $(WPWD)/../hdl/regulator.vhdl + else + $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") + endif +else + $(error "This example does not have support for SIM=$(SIM)") +endif + + +# Options + +COCOTB_HDL_TIMEUNIT=1ns +COCOTB_HDL_TIMEPRECISION=1ps + +ifeq ($(SIM),$(filter $(SIM),ius xcelium)) + SIM_ARGS += -discipline logic + SIM_ARGS += -amsconnrules ConnRules_full_fast + SIM_ARGS += +dr_info + SIM_ARGS += -iereport + SIM_ARGS += -ieinfo + SIM_ARGS += -ieinfo_log $(SIM_BUILD)/ams_ieinfo.log + SIM_ARGS += -spectre_args "+lqtimeout 7200" + SIM_ARGS += run.scs + SIM_ARGS += $(EXTRA_SIM_ARGS) +else ifeq ($(SIM),vcs) + COMPILE_ARGS += -ad=./vcsAD.init + COMPILE_ARGS += -ams + COMPILE_ARGS += -ams_iereport + COMPILE_ARGS += -xlrm coerce_nettype +# COMPILE_ARGS += -ams_discipline + SIM_ARGS += $(EXTRA_SIM_ARGS) +else + $(error "This example does not have support for SIM=$(SIM)") +endif + + +include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/examples/mixed_signal/tests/run.scs b/examples/mixed_signal/tests/run.scs new file mode 100644 index 00000000..8a84f126 --- /dev/null +++ b/examples/mixed_signal/tests/run.scs @@ -0,0 +1,11 @@ +// Cadence Spectre control file for analog solver +//tran tran stop=100u step=5n maxstep=5n relref=alllocal checklimitdest=both errpreset=conservative +tran tran stop=100u maxstep=5n + +// connectmodule selection when not done via xrun -amsconnrules +// amsd { +// ie vsup=5.0 +// } + +// saveOptions options save=all pwr=all currents=all saveahdlvars=all depth=3 +// save foo:currents diff --git a/examples/mixed_signal/tests/test_regulator_plot.py b/examples/mixed_signal/tests/test_regulator_plot.py new file mode 100644 index 00000000..4a607ad2 --- /dev/null +++ b/examples/mixed_signal/tests/test_regulator_plot.py @@ -0,0 +1,69 @@ +# This file is public domain, it can be freely copied without restrictions. +# SPDX-License-Identifier: CC0-1.0 + +import cocotb +from cocotb.triggers import Timer +import math +import matplotlib.pyplot as plt + + +@cocotb.test() +async def test_trim_vals(tb_hdl): + """Set trim value of regulator and measure resulting voltage.""" + + probed_node = "tb_regulator.i_regulator.vout" + tb_hdl.vdd_val <= 7.7 + tb_hdl.vss_val <= 0.0 + + probedata = [] + for trim_val in [0, 3, -5]: + tb_hdl.trim_val <= trim_val + await Timer(1, units="ns") + trimmed_volt = await get_voltage(tb_hdl, probed_node) + tb_hdl._log.info( + "trim_val={} results in {}={:.4} V".format( + tb_hdl.trim_val.value.signed_integer, probed_node, trimmed_volt + ) + ) + # sanity check: output voltage can not exceed supply + assert tb_hdl.vss_val.value <= trimmed_volt <= tb_hdl.vdd_val.value + probedata.append((tb_hdl.trim_val.value.signed_integer, trimmed_volt)) + + plot_data(tb_hdl, datasets=probedata, graphfile="regulator.png") + + +async def get_voltage(tb_hdl, node): + """Measure voltage on *node*.""" + await Timer(1, units="ps") # let trim_val take effect + tb_hdl.i_analog_probe.node_to_probe <= node.encode("ascii") + tb_hdl.i_analog_probe.probe_voltage_toggle <= ~int(tb_hdl.i_analog_probe.probe_voltage_toggle) + await Timer(1, units="ps") # waiting time needed for the analog values to be updated + tb_hdl._log.debug("Voltage on node {} is {:.4} V".format( + node, tb_hdl.i_analog_probe.voltage.value)) + return tb_hdl.i_analog_probe.voltage.value + + +def plot_data(tb_hdl, datasets, graphfile="cocotb_plot.png"): + """Save a graph to file *graphfile*. + + Trim and voltage value are contained in *datasets*. + """ + trim, voltage = zip(*datasets) + trim_round = range(1, len(trim)+1) + + fig = plt.figure() + ax = plt.axes() + ax.set_title("Probed node: {}".format(tb_hdl.i_analog_probe.node_to_probe.value.decode("ascii"))) + ax.set_ylabel("Voltage (V)") + ax.set_ylim([0, math.ceil(max(voltage))+1]) + ax.step(trim_round, voltage, where="mid") + ax.plot(trim_round, voltage, 'C0o', alpha=0.5) + for i, j, k in zip(trim_round, trim, voltage): + ax.annotate("trim={}".format(j), xy=(i, k), xytext=(0, 5), textcoords='offset points', ha='center') + ax.xaxis.set_major_locator(plt.NullLocator()) + ax.xaxis.set_major_formatter(plt.NullFormatter()) + fig.tight_layout() + fig.set_size_inches(11, 6) + + tb_hdl._log.info("Writing file {}".format(graphfile)) + fig.savefig(graphfile) diff --git a/examples/mixed_signal/tests/test_regulator_trim.py b/examples/mixed_signal/tests/test_regulator_trim.py new file mode 100644 index 00000000..52f89edb --- /dev/null +++ b/examples/mixed_signal/tests/test_regulator_trim.py @@ -0,0 +1,123 @@ +# This file is public domain, it can be freely copied without restrictions. +# SPDX-License-Identifier: CC0-1.0 + +import cocotb +from cocotb.triggers import Timer + +import itertools + + +class Regulator_TB(object): + """Class for collecting testbench objects. + + Args: + tb_hdl: The toplevel of the design-under-test. + In this mixed cocotb/HDL testbench environment, it is the HDL testbench. + settling_time_ns (int): Time in nanoseconds to wait before sample is taken. + """ + + def __init__(self, tb_hdl, settling_time_ns=1): + self.tb_hdl = tb_hdl + self.settling_time_ns = settling_time_ns + self.analog_probe = ( + tb_hdl.i_analog_probe + ) #: The instance name of the analog probe module. + self._bit_toggle_stream = itertools.cycle( + [0, 1] + ) #: Produce bitstream that toggles between ``0`` and ``1``. + + async def get_voltage(self, node): + """Measure voltage on *node*.""" + toggle = next(self._bit_toggle_stream) + self.tb_hdl.i_analog_probe.node_to_probe <= node.encode("ascii") + self.analog_probe.probe_voltage_toggle <= toggle + await Timer(1, units="ps") # waiting time needed for the analog values to be updated + self.tb_hdl._log.debug( + "trim value={}: {}={:.4} V".format( + self.tb_hdl.trim_val.value.signed_integer, + self.analog_probe.node_to_probe.value.decode("ascii"), + self.analog_probe.voltage.value, + ) + ) + return self.analog_probe.voltage.value + + async def find_trim_val(self, probed_node, target_volt, trim_val_node): + """Calculate best trimming value for *target_volt*. + Algorithm is to measure voltage of *probed_node* at + lowest and highest trim value, + then calculate the slope and finally the target trim value from slope. + Assumes a linear behaviour. + + Args: + probed_node: The node to probe for the trimmed voltage. + target_volt (float): The target voltage at *probed_node*. + trim_val_node: The node to apply the trim value to. + + Yields: + float: The calculated best value for *trim_val_node*. + """ + # assuming two's complement + trim_val_min = -2 ** (trim_val_node.value.n_bits - 1) + trim_val_max = 2 ** (trim_val_node.value.n_bits - 1) - 1 + # the actual trimming procedure: + # minimum values + trim_val_node <= trim_val_min + await Timer(self.settling_time_ns, units="ns") + volt_min = await self.get_voltage(probed_node) + # maximum values + trim_val_node <= trim_val_max + await Timer(self.settling_time_ns, units="ns") + volt_max = await self.get_voltage(probed_node) + if target_volt > volt_max: + self.tb_hdl._log.debug( + "target_volt={} > volt_max={}, returning minimum trim value {}".format( + target_volt, volt_max, trim_val_max + ) + ) + return trim_val_max + if target_volt < volt_min: + self.tb_hdl._log.debug( + "target_volt={} < volt_min={}, returning maximum trim value {}".format( + target_volt, volt_min, trim_val_min + ) + ) + return trim_val_min + # do the calculation: + slope = (trim_val_max - trim_val_min) / (volt_max - volt_min) + target_trim = (target_volt - volt_min) * slope + trim_val_min + return target_trim + + +@cocotb.test() +async def run_test(tb_hdl): + """Run test for mixed signal simulation - automatic trimming of a voltage regulator.""" + + tb_py = Regulator_TB(tb_hdl) + node = "tb_regulator.i_regulator.vout" + + _ = await tb_py.get_voltage( + node + ) # NOTE: dummy read apparently needed because of $cds_get_analog_value in analog_probe + + # show automatic trimming + target_volt = 3.013 + tb_py.tb_hdl._log.info( + "Running trimming algorithm for target voltage {:.4} V".format( + target_volt + ) + ) + best_trim_float = await tb_py.find_trim_val( + probed_node=node, target_volt=target_volt, trim_val_node=tb_py.tb_hdl.trim_val + ) + best_trim_rounded = round(best_trim_float) + tb_py.tb_hdl.trim_val <= best_trim_rounded + await Timer(tb_py.settling_time_ns, units="ns") + trimmed_volt = await tb_py.get_voltage(node) + tb_py.tb_hdl._log.info( + "Best trimming value is {} " + "--> voltage is {:.4} V (difference to target is {:.4} V)".format( + best_trim_rounded, trimmed_volt, trimmed_volt-target_volt + ) + ) + best_trim_rounded_exp = -1 + assert best_trim_rounded == best_trim_rounded_exp diff --git a/examples/mixed_signal/tests/test_rescap.py b/examples/mixed_signal/tests/test_rescap.py new file mode 100644 index 00000000..6f958739 --- /dev/null +++ b/examples/mixed_signal/tests/test_rescap.py @@ -0,0 +1,150 @@ +# This file is public domain, it can be freely copied without restrictions. +# SPDX-License-Identifier: CC0-1.0 + +import cocotb +from cocotb.triggers import Timer +from cocotb.utils import get_sim_time + +from collections import namedtuple, defaultdict + +from itertools import cycle + +import matplotlib.pyplot as plt + +Dataset = namedtuple("Dataset", "time, voltage, current") + + +class ResCap_TB(object): + """The testbench class for the rescap design.""" + def __init__(self, tb_hdl): + self.tb_hdl = tb_hdl + self.analog_probe = ( + tb_hdl.i_analog_probe + ) #: The instance name of the analog probe module. + self.togglestream = cycle(range(2)) # toggle between 0 and 1 + + async def _get_single_sample(self, node): + toggle = next(self.togglestream) + self.tb_hdl.i_analog_probe.node_to_probe <= node.encode('ascii') + self.analog_probe.probe_voltage_toggle <= toggle + self.analog_probe.probe_current_toggle <= toggle + await Timer(1, units="ps") # waiting time needed for the analog values to be updated + dataset = Dataset( + time=get_sim_time(units="ns"), + voltage=self.analog_probe.voltage.value, + current=self.analog_probe.current.value * 1000.0 # in mA + ) + self.tb_hdl._log.debug( + "{}={:.4} V, {:.4} mA".format( + self.analog_probe.node_to_probe.value.decode("ascii"), dataset.voltage, dataset.current + ) + ) + return dataset + + async def get_sample_data(self, nodes, num=1, delay_ns=1): + """For all *nodes*, get *num* samples, spaced *delay_ns* apart. + + Yields: + list: List (*num* samples long) of :any:`Dataset` for all *nodes*. + """ + if not isinstance(nodes, list): # single element? make it a list + _nodes = [nodes] + else: + _nodes = nodes + datasets = defaultdict(list) + for idx in range(num): + for node in _nodes: + dataset = await self._get_single_sample(node) + datasets[node].append(dataset) + if idx != num: + await Timer(delay_ns, units="ns") + return datasets + + def plot_data(self, datasets, nodes, graphfile="cocotb_plot.png"): + """Save a charge graph of *nodes* to file *graphfile*. + + Voltage and current value are contained in *datasets*. + """ + fig, ax_volt = plt.subplots() + color_volt = "tab:red" + color_curr = "tab:blue" + ax_volt.set_title("rescap") + ax_volt.set_xlabel("Time (ns)") + ax_volt.set_ylabel("Voltage (V)", color=color_volt) + ax_curr = ax_volt.twinx() # instantiate a second axis that shares the same x-axis + ax_curr.set_ylabel("Current (mA)", color=color_curr) # we already handled the x-label with ax_volt + + for node in nodes: + time, voltage, current = zip(*(datasets[node])) + if node.endswith("vout"): + alpha = 1.0 + else: + alpha = 0.333 + ax_volt.plot(time, voltage, color=color_volt, alpha=alpha, + marker=".", markerfacecolor="black", linewidth=1, label="V({})".format(node)) + ax_curr.plot(time, current, color=color_curr, alpha=alpha, + marker=".", markerfacecolor="black", linewidth=1, label="I({})".format(node)) + + ax_volt.tick_params(axis="y", labelcolor=color_volt) + ax_curr.tick_params(axis="y", labelcolor=color_curr) + ax_volt.axhline(linestyle=":", color="gray") + + mpl_align_yaxis(ax_volt, 0, ax_curr, 0) + fig.tight_layout() # otherwise the right y-label is slightly clipped + fig.set_size_inches(11, 6) + fig.legend(loc="upper right", bbox_to_anchor=(0.8, 0.9), frameon=False) + + self.tb_hdl._log.info("Writing file {}".format(graphfile)) + fig.savefig(graphfile) + + +@cocotb.test() +async def run_test(tb_hdl): + """Run test for mixed signal resistor/capacitor simulation.""" + + tb_py = ResCap_TB(tb_hdl) + + nodes_to_probe = ["tb_rescap.i_rescap.vdd", "tb_rescap.i_rescap.vout"] + # nodes_to_probe = ["tb_rescap.i_rescap.vdd", "tb_rescap.i_rescap.i_capacitor.p"] + + probedata = defaultdict(list) + + vdd = 0.0 + tb_py.tb_hdl.vdd_val <= vdd + tb_py.tb_hdl.vss_val <= 0.0 + tb_py.tb_hdl._log.info("Setting vdd={:.4} V".format(vdd)) + # dummy read appears to be necessary for the analog solver + _ = await tb_py.get_sample_data(nodes=nodes_to_probe) + + for vdd in [5.55, -3.33]: + tb_py.tb_hdl.vdd_val <= vdd + tb_py.tb_hdl.vss_val <= 0.0 + tb_py.tb_hdl._log.info("Setting vdd={:.4} V".format(vdd)) + data = await tb_py.get_sample_data(num=60, delay_ns=5, nodes=nodes_to_probe) + for node in nodes_to_probe: + probedata[node].extend(data[node]) + + tb_py.plot_data(datasets=probedata, nodes=nodes_to_probe, graphfile="rescap.png") + + +def mpl_align_yaxis(ax1, v1, ax2, v2): + """Adjust ax2 ylimit so that v2 in ax2 is aligned to v1 in ax1.""" + _, y1 = ax1.transData.transform((0, v1)) + _, y2 = ax2.transData.transform((0, v2)) + mpl_adjust_yaxis(ax2, (y1 - y2) / 2, v2) + mpl_adjust_yaxis(ax1, (y2 - y1) / 2, v1) + + +def mpl_adjust_yaxis(ax, ydif, v): + """Shift axis ax by ydiff, maintaining point v at the same location.""" + inv = ax.transData.inverted() + _, dy = inv.transform((0, 0)) - inv.transform((0, ydif)) + miny, maxy = ax.get_ylim() + miny, maxy = miny - v, maxy - v + if -miny > maxy or (-miny == maxy and dy > 0): + nminy = miny + nmaxy = miny * (maxy + dy) / (miny + dy) + else: + nmaxy = maxy + nminy = maxy * (miny + dy) / (maxy + dy) + ax.set_ylim(nminy + v, nmaxy + v) diff --git a/examples/mixed_signal/tests/test_rescap_minimalist.py b/examples/mixed_signal/tests/test_rescap_minimalist.py new file mode 100644 index 00000000..80b39bd8 --- /dev/null +++ b/examples/mixed_signal/tests/test_rescap_minimalist.py @@ -0,0 +1,27 @@ +# This file is public domain, it can be freely copied without restrictions. +# SPDX-License-Identifier: CC0-1.0 + +import cocotb +from cocotb.triggers import Timer + + +@cocotb.test() +async def rescap_minimalist_test(tb_hdl): + """Mixed signal resistor/capacitor simulation, minimalistic.""" + + tb_hdl.vdd_val <= 7.7 + tb_hdl.vss_val <= 0.0 + tb_hdl.i_analog_probe.node_to_probe <= "tb_rescap.i_rescap.vout".encode("ascii") + + for toggle in [1, 0, 1, 0, 1, 0]: + await Timer(50, units="ns") + tb_hdl.i_analog_probe.probe_voltage_toggle <= toggle + tb_hdl.i_analog_probe.probe_current_toggle <= toggle + await Timer(1, units="ps") # waiting time needed for the analog values to be updated + tb_hdl._log.info( + "tb_hdl.i_analog_probe@{}={:.4} V {:.4} A".format( + tb_hdl.i_analog_probe.node_to_probe.value.decode("ascii"), + tb_hdl.i_analog_probe.voltage.value, + tb_hdl.i_analog_probe.current.value + ) + ) diff --git a/examples/mixed_signal/tests/vcsAD.init b/examples/mixed_signal/tests/vcsAD.init new file mode 100644 index 00000000..c1282fb3 --- /dev/null +++ b/examples/mixed_signal/tests/vcsAD.init @@ -0,0 +1,4 @@ +choose hsim spice/foo.cdl; +partition -cell subckt1 subckt2; +set bus_format [%d]; + From 4f4f6a9eda034678007f958ceea4385d94cead61 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 10 Sep 2020 22:30:12 +0200 Subject: [PATCH 2492/2656] Change newsfragment category and link mixed signal example --- documentation/source/examples.rst | 2 ++ .../source/newsfragments/{1051.feature.rst => 1051.doc.rst} | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) rename documentation/source/newsfragments/{1051.feature.rst => 1051.doc.rst} (72%) diff --git a/documentation/source/examples.rst b/documentation/source/examples.rst index 450c2fd2..20a7eeff 100644 --- a/documentation/source/examples.rst +++ b/documentation/source/examples.rst @@ -120,6 +120,8 @@ The directory :file:`cocotb/examples/axi_lite_slave/` contains ... Write documentation, see :file:`README.md` +.. _mixed_signal: + Mixed-signal (analog/digital) ============================= diff --git a/documentation/source/newsfragments/1051.feature.rst b/documentation/source/newsfragments/1051.doc.rst similarity index 72% rename from documentation/source/newsfragments/1051.feature.rst rename to documentation/source/newsfragments/1051.doc.rst index ff5b8a96..67525f0f 100644 --- a/documentation/source/newsfragments/1051.feature.rst +++ b/documentation/source/newsfragments/1051.doc.rst @@ -1,3 +1,3 @@ -The ``mixed_signal`` example has been added, +The :ref:`mixed_signal` example has been added, showing how to use HDL helper modules in cocotb testbenches that exercise two mixed-signal (i.e. analog and digital) designs. From 3262ea8ba7e586331d0f58bbf0e5b3e6f237ed5b Mon Sep 17 00:00:00 2001 From: Ben Rosser Date: Fri, 4 Sep 2020 12:44:05 -0400 Subject: [PATCH 2493/2656] Make it possible to run skip=True tests by setting TESTCASE The ```@cocotb.test()``` decorator takes an optional ```skip``` argument, which defaults to False. If skip is set to True, the test will be skipped when tests are ran. However, in cocotb 1.3 (and earlier versions), these automatically skipped tests could still be ran explicitly by setting ```TESTCASE=``` on the command line. As of cocotb 1.4+, that's no longer possible. If a test has skip=True in its constructor, it cannot be ran at all, even using the TESTCASE= environment variable. It would be useful to have some supported way of overriding the skip setting and explicitly running a test, and restoring the pre-1.4 behavior seems like a reasonable way to do that. This commit makes it so that setting ```TESTCASE``` on the command line sets ```test.skip = False```, meaning that the test will always be ran. If ```TESTCASE``` is not set, then nothing is changed-- the test will be ran or skipped depending on the value set in the test decorator. --- cocotb/regression.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cocotb/regression.py b/cocotb/regression.py index 6092bc6f..291e0143 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -195,6 +195,10 @@ def _discover_tests() -> Iterable[Test]: _logger.error("Requested %s from module %s isn't a cocotb.test decorated coroutine", test_name, module_name) raise ImportError("Failed to find requested test %s" % test_name) + + # If we request a test manually, it should be ran even if skip=True is set. + test.skip = False + yield test # only look in first module for all functions and don't complain if all functions are not found From 462cf89b18788eac4349bd7ca3aeaeab4495b481 Mon Sep 17 00:00:00 2001 From: Ben Rosser Date: Fri, 4 Sep 2020 16:15:48 -0400 Subject: [PATCH 2494/2656] Add test that skip=True tests can be ran by setting TESTCASE The test creates a temporay file, "ran_skipped_test~" (I gave it the vim temporary file ending so that it would already be covered by the cocotb gitignore), when it is ran, and does nothing else. The test has skip=True set in its constructor. The Makefile for this test then sets ```TESTCASE=test_skipped``` to ensure that the test is ran, and overrides the default make target so that we can test if the file was created. If the file wasn't created, then we exit with status code 1. --- tests/test_cases/test_skipped/Makefile | 22 +++++++++++++++++++ tests/test_cases/test_skipped/test_skipped.py | 15 +++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 tests/test_cases/test_skipped/Makefile create mode 100644 tests/test_cases/test_skipped/test_skipped.py diff --git a/tests/test_cases/test_skipped/Makefile b/tests/test_cases/test_skipped/Makefile new file mode 100644 index 00000000..4573df0c --- /dev/null +++ b/tests/test_cases/test_skipped/Makefile @@ -0,0 +1,22 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause + +include ../../designs/sample_module/Makefile + +SKIPPED_TEST_FILE = ran_skipped_test~ + +clean:: + @rm -rf ${SKIPPED_TEST_FILE} + +# Override the default target. We need to run clean (to remove the cached test file) +# and then test to make sure it is recreated. +.DEFAULT_GOAL := override +.PHONY: override +override: clean all + @test -f $(SKIPPED_TEST_FILE) || (echo "ERROR: skip=True test was not ran!" >&2 && exit 1) + +# Set TESTCASE; run test_skipped even though skip=True is set. +TESTCASE = test_skipped + +MODULE = test_skipped diff --git a/tests/test_cases/test_skipped/test_skipped.py b/tests/test_cases/test_skipped/test_skipped.py new file mode 100644 index 00000000..6e8e613f --- /dev/null +++ b/tests/test_cases/test_skipped/test_skipped.py @@ -0,0 +1,15 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause + +import pathlib + +import cocotb + +skipped_file_name = "ran_skipped_test~" + + +@cocotb.test(skip=True) +async def test_skipped(dut): + """ Touch a file so we can check that this test has ran.""" + pathlib.Path(skipped_file_name).touch() From fb02018e9b44100a22a43e841d0cd2293cac3627 Mon Sep 17 00:00:00 2001 From: Ben Rosser Date: Tue, 8 Sep 2020 15:11:00 -0400 Subject: [PATCH 2495/2656] Create newsfragment and update documentation for skip=True tests I created a newsfragment for this issue and also added a sentence to the @cocotb.test() decorator's documentation explicitly stating that skip=True tests are still runnable by setting TESTCASE. Also fix some earlier grammatical errors. --- cocotb/decorators.py | 3 ++- cocotb/regression.py | 2 +- documentation/source/newsfragments/2045.bugfix.rst | 1 + tests/test_cases/test_skipped/test_skipped.py | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 documentation/source/newsfragments/2045.bugfix.rst diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 881cc427..76d76a92 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -510,7 +510,8 @@ async def my_test(dut): .. versionchanged:: 1.3 Specific exception types can be expected skip (bool, optional): - Don't execute this test as part of the regression. + Don't execute this test as part of the regression. Test can still be run + manually by setting :make:var:`TESTCASE`. stage (int, optional) Order tests logically into stages, where multiple tests can share a stage. """ diff --git a/cocotb/regression.py b/cocotb/regression.py index 291e0143..d5420b90 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -196,7 +196,7 @@ def _discover_tests() -> Iterable[Test]: test_name, module_name) raise ImportError("Failed to find requested test %s" % test_name) - # If we request a test manually, it should be ran even if skip=True is set. + # If we request a test manually, it should be run even if skip=True is set. test.skip = False yield test diff --git a/documentation/source/newsfragments/2045.bugfix.rst b/documentation/source/newsfragments/2045.bugfix.rst new file mode 100644 index 00000000..ddcd1a06 --- /dev/null +++ b/documentation/source/newsfragments/2045.bugfix.rst @@ -0,0 +1 @@ +Tests skipped by default (created with `skip=True`) can again be run manually by setting the :envvar:`TESTCASE` variable. diff --git a/tests/test_cases/test_skipped/test_skipped.py b/tests/test_cases/test_skipped/test_skipped.py index 6e8e613f..eabe8dbb 100644 --- a/tests/test_cases/test_skipped/test_skipped.py +++ b/tests/test_cases/test_skipped/test_skipped.py @@ -11,5 +11,5 @@ @cocotb.test(skip=True) async def test_skipped(dut): - """ Touch a file so we can check that this test has ran.""" + """ Touch a file so we can check that this test has run.""" pathlib.Path(skipped_file_name).touch() From 17299474e7806c0acf90c0a95a1a6f8a0def04d8 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 23 Sep 2020 22:12:24 +0200 Subject: [PATCH 2496/2656] Add info about diagrams.net saving to GitHub --- documentation/source/diagrams/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/documentation/source/diagrams/README.md b/documentation/source/diagrams/README.md index 2d4add6f..d5a9b2ba 100644 --- a/documentation/source/diagrams/README.md +++ b/documentation/source/diagrams/README.md @@ -2,3 +2,6 @@ The sources for the vector diagrams (in `xml/`) can be edited with [diagrams.net](https://app.diagrams.net/) - formerly called "draw.io". The SVG files in `svg/` are exported versions of those. + +Note that diagrams.net allows you to directly save and export the files +to your cocotb fork/branch on GitHub, producing git commits. From cbe52238769d340232ffc3ba92482538552b29e3 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 23 Sep 2020 20:09:53 +0200 Subject: [PATCH 2497/2656] Detail content of _def_name and _def_file Closes #1958 --- cocotb/handle.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 0e1e0814..0bc2402f 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -96,10 +96,18 @@ def __init__(self, handle, path): self._def_name = self._handle.get_definition_name() # type: str """The name of a GPI object's definition. + This is the value of ``vpiDefName`` for VPI, ``vhpiNameP`` for VHPI, + and ``mti_GetPrimaryName`` for FLI. + Support for this depends on the specific object type and simulator used. + :meta public: """ self._def_file = self._handle.get_definition_file() # type: str - """The file that sources the object's definition. + """The name of the file that sources the object's definition. + + This is the value of ``vpiDefFile`` for VPI, ``vhpiFileNameP`` for VHPI, + and ``mti_GetRegionSourceName`` for FLI. + Support for this depends on the specific object type and simulator used. :meta public: """ From b2eda5325a1e75ef6bdde4e6523bcb0ac81a3137 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 25 Sep 2020 21:36:35 -0500 Subject: [PATCH 2498/2656] Replace Travis Windows CI with Github Actions --- .github/workflows/regression-tests.yml | 53 ++++++++++++++++++++++++++ .travis.yml | 51 ------------------------- 2 files changed, 53 insertions(+), 51 deletions(-) create mode 100644 .github/workflows/regression-tests.yml diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml new file mode 100644 index 00000000..052b08e8 --- /dev/null +++ b/.github/workflows/regression-tests.yml @@ -0,0 +1,53 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause +name: Regression Tests + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + + tests: + name: ${{matrix.sim}} (${{matrix.sim-version}}) | ${{matrix.os}} | Python ${{matrix.python-version}} + runs-on: ${{matrix.os}} + strategy: + fail-fast: false + matrix: + sim-version: ["v10_3", "master"] + sim: ['icarus'] + lang: ['verilog'] + python-version: [3.8] + os: ['windows-latest'] + env: + SIM: ${{matrix.sim}} + TOPLEVEL_LANG: ${{matrix.lang}} + PYTHON: ${{matrix.python-version}} + OS: ${{matrix.os}} + steps: + - uses: actions/checkout@v2 + - name: Set up Anaconda ${{matrix.python-version}} + uses: conda-incubator/setup-miniconda@v1 + with: + auto-update-conda: true + python-version: ${{matrix.python-version}} + - name: Set up cocotb build dependencies + run: | + conda install -c msys2 m2-base m2-make m2-autoconf m2-flex m2-bison m2-gperf m2w64-toolchain libpython + - name: Build and install Icarus + run: | + git clone https://github.com/steveicarus/iverilog.git -b "${{matrix.sim-version}}" + cd iverilog + bash ./autoconf.sh + ./configure + make -j $(nproc) + make install + - name: Install Python testing dependencies + run: | + pip install tox tox-gh-actions + - name: Test with tox + run: | + tox -e py3_mingw diff --git a/.travis.yml b/.travis.yml index 6c7e8c12..2f7d9eac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -80,57 +80,6 @@ jobs: - echo 'travis_fold:end:cocotb_py39' after_success: *travis_linux_post - - stage: SimTests - os: windows - language: shell - before_install: - - powershell "Invoke-WebRequest -outfile miniconda3.exe https://repo.continuum.io/miniconda/Miniconda3-latest-Windows-x86_64.exe" - - powershell "Start-Process .\miniconda3.exe -ArgumentList '/S /D=C:\miniconda3' -Wait" - - - powershell "Invoke-WebRequest -Uri http://bleyer.org/icarus/iverilog-v11-20190809-x64_setup.exe -OutFile iverilog-x64_setup.exe" - - powershell "Start-Process .\iverilog-x64_setup -ArgumentList '/VERYSILENT' -Wait" - - - export CONDA_PATH="\$env:Path = ';C:\miniconda3;C:\miniconda3\Library\mingw-w64\bin;C:\miniconda3\Library\usr\bin;C:\miniconda3\Library\bin;C:\miniconda3\Scripts;C:\miniconda3\bin;C:\miniconda3\condabin;C:\iverilog\bin;' + \$env:Path" - - powershell "$CONDA_PATH; conda install --yes -c msys2 m2-base m2-make m2w64-toolchain libpython" - - powershell "$CONDA_PATH; conda install --yes virtualenv" - - powershell "$CONDA_PATH; conda update --yes pip" - - powershell "$CONDA_PATH; conda info; conda list --show-channel-urls" - # needed to make the builtin sqlite3 module (used by coverage) work - see gh-1909 - - powershell 'cp C:\miniconda3\Library\bin\sqlite3.dll C:\miniconda3\DLLs\sqlite3.dll' - - powershell "$CONDA_PATH; pip install tox" - script: - # TODO[gh-1372]: the default compiler on windows, msvc, is not supported - - powershell "$CONDA_PATH; tox -e py3_mingw" - - - stage: SimTests - name: "Py 3.7, Icarus devel" - env: ALLOW_FAIL=yes - python: 3.7 - os: windows - language: shell - before_install: - - powershell "Invoke-WebRequest -outfile miniconda3.exe https://repo.continuum.io/miniconda/Miniconda3-latest-Windows-x86_64.exe" - - powershell "Start-Process .\miniconda3.exe -ArgumentList '/S /D=C:\miniconda3' -Wait" - - - export CONDA_PATH="\$env:Path = ';C:\miniconda3;C:\miniconda3\Library\mingw-w64\bin;C:\miniconda3\Library\usr\bin;C:\miniconda3\Library\bin;C:\miniconda3\Scripts;C:\miniconda3\bin;C:\miniconda3\condabin;C:\miniconda3\Library\usr\local\bin;' + \$env:Path" - - powershell "$CONDA_PATH; conda install --yes -c msys2 m2-base m2-make m2-autoconf m2-flex m2-bison m2-gperf m2w64-toolchain libpython" - - powershell "$CONDA_PATH; conda install --yes virtualenv" - - powershell "$CONDA_PATH; conda update --yes pip" - - powershell "$CONDA_PATH; conda info; conda list --show-channel-urls" - # needed to make the builtin sqlite3 module (used by coverage) work - see gh-1909 - - powershell 'cp C:\miniconda3\Library\bin\sqlite3.dll C:\miniconda3\DLLs\sqlite3.dll' - - powershell "$CONDA_PATH; pip install tox" - - - powershell "$CONDA_PATH; if (!(Test-Path iverilog/README.txt)) { Get-ChildItem -Path iverilog -Recurse | Remove-Item -Force -Recurse -ErrorAction SilentlyContinue; Remove-Item iverilog -Force -Recurse -ErrorAction SilentlyContinue; git clone --depth=1 https://github.com/steveicarus/iverilog.git } else { cd iverilog; git fetch origin; cd ..}" - - powershell "$CONDA_PATH; cd iverilog; git reset --hard origin/master; git clean -fdx; cd .." - - powershell "$CONDA_PATH; cd iverilog; bash ./autoconf.sh; cd .." - - powershell "$CONDA_PATH; cd iverilog; bash ./configure --host=x86_64-w64-mingw32; cd .." - - powershell "$CONDA_PATH; cd iverilog; make; cd .." - - powershell "$CONDA_PATH; cd iverilog; make install; cd .." - script: - # TODO[gh-1372]: the default compiler on windows, msvc, is not supported - - powershell "$CONDA_PATH; tox -e py3_mingw" - - stage: SimTests os: osx osx_image: xcode11.2 From 0c557883b3c50edbe239fcfd38374d60c097fe8a Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sun, 27 Sep 2020 23:35:51 +0200 Subject: [PATCH 2499/2656] Document envvar COCOTB_LIBRARY_COVERAGE. Closes #2083 --- documentation/source/building.rst | 32 ++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/documentation/source/building.rst b/documentation/source/building.rst index 7d39ffce..766b5332 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -127,17 +127,6 @@ Cocotb This needs the :mod:`cherrypy` and :mod:`dowser` Python modules installed. -.. envvar:: COCOTB_PY_DIR - - Path to the directory containing the cocotb Python package in the :file:`cocotb` subdirectory; - for cocotb-internal use. - -.. envvar:: COCOTB_SHARE_DIR - - Path to the directory containing the cocotb Makefiles and simulator libraries in the subdirectories - :file:`lib`, :file:`include`, and :file:`makefiles`; - for cocotb-internal use. - Regression Manager ~~~~~~~~~~~~~~~~~~ @@ -326,6 +315,27 @@ and `CXXFLAGS`, `LDLIBS` are not supported by distutils/pip +Internal Variables +------------------ + +The following variables are used for cocotb internals. +They may change at any time, and users should not rely on them. + +.. envvar:: COCOTB_PY_DIR + + Path to the directory containing the cocotb Python package in the :file:`cocotb` subdirectory. + +.. envvar:: COCOTB_SHARE_DIR + + Path to the directory containing the cocotb Makefiles and simulator libraries in the subdirectories + :file:`lib`, :file:`include`, and :file:`makefiles`. + +.. envvar:: COCOTB_LIBRARY_COVERAGE + + Enable code coverage collection for cocotb internals. + When set, a file :file:`.coverage.cocotb` will be written which contains statistics about the code coverage. + This is mainly useful for cocotb's own Continuous Integration setup. + .. TODO From 1836c16cf400323aae06af83c5d1e2252f116067 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 9 Sep 2020 17:49:17 -0500 Subject: [PATCH 2500/2656] Remove examples from sdist --- MANIFEST.in | 1 - 1 file changed, 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 6e63680c..c594aee1 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,4 @@ recursive-include cocotb/share * -recursive-include examples * include README.md include LICENSE include cocotb_build_libs.py From 5c25ab84ca256a6d5eaac5faf979afbbcced701a Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Thu, 10 Sep 2020 20:37:27 -0500 Subject: [PATCH 2501/2656] Split endian_swapper_hal_test from others This way it's not in the regression. --- examples/endian_swapper/hal_tests/Makefile | 82 +++++++++++++++++++ .../test_endian_swapper_hal.py | 0 examples/endian_swapper/tests/Makefile | 21 +---- 3 files changed, 83 insertions(+), 20 deletions(-) create mode 100644 examples/endian_swapper/hal_tests/Makefile rename examples/endian_swapper/{tests => hal_tests}/test_endian_swapper_hal.py (100%) diff --git a/examples/endian_swapper/hal_tests/Makefile b/examples/endian_swapper/hal_tests/Makefile new file mode 100644 index 00000000..aa05ef3a --- /dev/null +++ b/examples/endian_swapper/hal_tests/Makefile @@ -0,0 +1,82 @@ +############################################################################### +# Copyright (c) 2013, 2018 Potential Ventures Ltd +# Copyright (c) 2013 SolarFlare Communications Inc +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# SolarFlare Communications Inc nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +# Default to verilog +TOPLEVEL_LANG ?= verilog + +PWD=$(shell pwd) + +export PYTHONPATH := $(PWD)/../cosim:$(PYTHONPATH) + +ifeq ($(TOPLEVEL_LANG),verilog) + VERILOG_SOURCES = $(PWD)/../hdl/endian_swapper.sv + TOPLEVEL = endian_swapper_sv +else ifeq ($(TOPLEVEL_LANG),vhdl) + VHDL_SOURCES = $(PWD)/../hdl/endian_swapper.vhdl + TOPLEVEL = endian_swapper_vhdl + ifneq ($(filter $(SIM),ius xcelium),) + SIM_ARGS += -v93 + endif +else + $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") +endif + +MODULE := test_endian_swapper_hal +CUSTOM_COMPILE_DEPS = hal +LD_LIBRARY_PATH := $(PWD)/../cosim:$(LD_LIBRARY_PATH) +export LD_LIBRARY_PATH + +include $(shell cocotb-config --makefiles)/Makefile.sim + +ifeq ($(OS),Linux) +.PHONY: hal +hal: + cd ../cosim && make + +clean:: + -cd ../cosim && make clean +else +$(error This test only supports Linux) +endif + +# Stuff below is useful for profiling +# Need gprof2dot from https://github.com/jrfonseca/gprof2dot +test_profile.pstat: sim + +callgraph.svg: test_profile.pstat + gprof2dot -f pstats $< | dot -Tsvg -o $@ + +.PHONY: profile +profile: + COCOTB_ENABLE_PROFILING=1 $(MAKE) callgraph.svg + + +clean:: + -rm -rf test_profile.pstat + -rm -rf callgraph.svg diff --git a/examples/endian_swapper/tests/test_endian_swapper_hal.py b/examples/endian_swapper/hal_tests/test_endian_swapper_hal.py similarity index 100% rename from examples/endian_swapper/tests/test_endian_swapper_hal.py rename to examples/endian_swapper/hal_tests/test_endian_swapper_hal.py diff --git a/examples/endian_swapper/tests/Makefile b/examples/endian_swapper/tests/Makefile index 6402e28d..49d7c2c7 100644 --- a/examples/endian_swapper/tests/Makefile +++ b/examples/endian_swapper/tests/Makefile @@ -32,8 +32,6 @@ TOPLEVEL_LANG ?= verilog PWD=$(shell pwd) -export PYTHONPATH := $(PWD)/../cosim:$(PYTHONPATH) - ifeq ($(TOPLEVEL_LANG),verilog) VERILOG_SOURCES = $(PWD)/../hdl/endian_swapper.sv TOPLEVEL = endian_swapper_sv @@ -49,31 +47,14 @@ endif MODULE := test_endian_swapper -ifeq ($(OS),Linux) - # TODO: work out how to build the extension module on windows - MODULE := $(MODULE),test_endian_swapper_hal - CUSTOM_COMPILE_DEPS = hal - LD_LIBRARY_PATH := $(PWD)/../cosim:$(LD_LIBRARY_PATH) - export LD_LIBRARY_PATH -endif - include $(shell cocotb-config --makefiles)/Makefile.sim -ifeq ($(OS),Linux) -.PHONY: hal -hal: - cd ../cosim && make - -clean:: - -cd ../cosim && make clean -endif - # Stuff below is useful for profiling # Need gprof2dot from https://github.com/jrfonseca/gprof2dot test_profile.pstat: sim callgraph.svg: test_profile.pstat - gprof2dot.py -f pstats $< | dot -Tsvg -o $@ + gprof2dot -f pstats $< | dot -Tsvg -o $@ .PHONY: profile profile: From 7351ccf3fe345d60f5158979d03aa7e670064e52 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Wed, 23 Sep 2020 21:23:30 -0700 Subject: [PATCH 2502/2656] Discover all sub-handles with iteration when initial sub-handle lookup fails. Sub-handle lookup may fail when looking for pseudo-regions if iteration has not been done on the parent handle. This happens because the pseudo-regions are not actually present in the HDL design, so attempting to get the handle by name will fail. Iteration over the parent handle will find the generate blocks and construct the pseudo-region, adding it as a sub-handle to the parent HierarchyObject. --- cocotb/handle.py | 4 ++++ documentation/source/newsfragments/2079.bugfix.rst | 8 ++++++++ 2 files changed, 12 insertions(+) create mode 100644 documentation/source/newsfragments/2079.bugfix.rst diff --git a/cocotb/handle.py b/cocotb/handle.py index 0bc2402f..bf0e0399 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -257,6 +257,10 @@ def __get_sub_handle_by_name(self, name): except KeyError: pass + if not self._discovered: + self._discover_all() + return self.__get_sub_handle_by_name(name) + # Cache to avoid a call to the simulator if we already know the name is # invalid. Unclear if we care, but we had this before. if name in self._invalid_sub_handles: diff --git a/documentation/source/newsfragments/2079.bugfix.rst b/documentation/source/newsfragments/2079.bugfix.rst new file mode 100644 index 00000000..ba12193b --- /dev/null +++ b/documentation/source/newsfragments/2079.bugfix.rst @@ -0,0 +1,8 @@ +Generate blocks are now accessible directly via lookup without having to iterate over parent handle. (:pr:`2079`) + + .. code-block:: python3 + + # Example pseudo-region + dut.genblk1 # + + .. consume the towncrier issue number on this line. From 3e83fa754501bd48c9c33b6188ec721dc9c49f53 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Wed, 23 Sep 2020 21:25:28 -0700 Subject: [PATCH 2503/2656] Add vpiVariables iterators for Module and Generator Scope handles. In part this ensures that the underlying variable should be found and a handle to that is stored instead of the vpiPort of the same name. --- cocotb/share/lib/vpi/VpiCbHdl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/share/lib/vpi/VpiCbHdl.cpp b/cocotb/share/lib/vpi/VpiCbHdl.cpp index 61ffb1b1..454457ca 100644 --- a/cocotb/share/lib/vpi/VpiCbHdl.cpp +++ b/cocotb/share/lib/vpi/VpiCbHdl.cpp @@ -576,7 +576,7 @@ decltype(VpiIterator::iterate_over) VpiIterator::iterate_over = []{ vpiRealNet, vpiStructVar, vpiStructNet, - //vpiVariables // Aldec SEGV on plain Verilog + vpiVariables, vpiNamedEvent, vpiNamedEventArray, vpiParameter, From 1aa76f15d7274d86a804ca3b91ca84a8cac72e38 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Fri, 25 Sep 2020 14:24:27 -0700 Subject: [PATCH 2504/2656] Update iteration tests to account for vpiVariables iterator. Riviera finds more objects now. --- tests/test_cases/test_array/test_array.py | 8 ++++++-- .../test_cases/test_iteration_mixedlang/test_iteration.py | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py index 04d9b6bd..35f03650 100644 --- a/tests/test_cases/test_array/test_array.py +++ b/tests/test_cases/test_array/test_array.py @@ -336,7 +336,7 @@ def test_discover_all(dut): # Modelsim/Questa VPI will not find a vpiStructVar from vpiModule so we set a dummy variable # to ensure the handle is in the dut "sub_handles" for iterating # - # DO NOT ADD FOR ALDEC. Does not iterate over properly + # DO NOT ADD FOR ALDEC. Older Versions do not iterate over properly if cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("modelsim", "ncsim", "xmsim")): dummy = dut.sig_rec dummy = dut.port_rec_out @@ -353,12 +353,16 @@ def test_discover_all(dut): elif cocotb.LANGUAGE in ["vhdl"]: pass_total = 856 elif cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")): + # Numbers for versions before 2019.10 may be outdated if cocotb.SIM_VERSION.startswith(("2017.10.61")): pass_total = 803 elif cocotb.SIM_VERSION.startswith(("2016.06", "2016.10", "2017.02")): pass_total = 813 - elif cocotb.SIM_VERSION.startswith(("2016.02", "2019.10")): + elif cocotb.SIM_VERSION.startswith(("2016.02")): pass_total = 947 + elif cocotb.SIM_VERSION.startswith(("2019.10")): + # vpiVariables finds port_rec_out and sig_rec + pass_total = 1006 else: pass_total = 1038 else: diff --git a/tests/test_cases/test_iteration_mixedlang/test_iteration.py b/tests/test_cases/test_iteration_mixedlang/test_iteration.py index 27ae1be1..dced184d 100644 --- a/tests/test_cases/test_iteration_mixedlang/test_iteration.py +++ b/tests/test_cases/test_iteration_mixedlang/test_iteration.py @@ -71,7 +71,7 @@ def recursive_discovery(dut): elif cocotb.SIM_NAME.lower().startswith(("modelsim")): pass_total = 933 else: - pass_total = 966 + pass_total = 1024 tlog = logging.getLogger("cocotb.test") yield Timer(100) From aa956f83e119e4ca4615c5b8660eb15c4dfa8623 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Fri, 25 Sep 2020 23:10:56 -0700 Subject: [PATCH 2505/2656] Add test for accessing pseudo-region explicitly before iteration of parent. --- tests/designs/sample_module/sample_module.sv | 2 +- tests/designs/sample_module/sample_module.vhdl | 12 ++++++++++++ tests/test_cases/test_discovery/test_discovery.py | 14 ++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/tests/designs/sample_module/sample_module.sv b/tests/designs/sample_module/sample_module.sv index 914e7b7d..7823a343 100644 --- a/tests/designs/sample_module/sample_module.sv +++ b/tests/designs/sample_module/sample_module.sv @@ -98,8 +98,8 @@ initial begin $dumpvars(0,sample_module); end -reg[3:0] temp; parameter NUM_OF_MODULES = 4; +reg[NUM_OF_MODULES-1:0] temp; genvar idx; generate for (idx = 0; idx < NUM_OF_MODULES; idx=idx+1) begin diff --git a/tests/designs/sample_module/sample_module.vhdl b/tests/designs/sample_module/sample_module.vhdl index 394ccb1a..bf943165 100644 --- a/tests/designs/sample_module/sample_module.vhdl +++ b/tests/designs/sample_module/sample_module.vhdl @@ -107,8 +107,20 @@ architecture impl of sample_module is type twoDimArrayType is array (natural range <>) of unsignedArrayType(31 downto 28); signal array_2d : twoDimArrayType(0 to 1); + constant NUM_OF_MODULES : natural := 4; + signal temp : std_logic_vector(NUM_OF_MODULES-1 downto 0); + begin + genblk1: for i in NUM_OF_MODULES - 1 downto 0 generate + begin + process (clk) begin + if rising_edge(clk) then + temp(i) <= '0'; + end if; + end process; + end generate; + process (clk) begin if rising_edge(clk) then stream_out_data_registered <= stream_in_data; diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index eb059d51..b8d30705 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -34,6 +34,20 @@ from cocotb.handle import IntegerObject, ConstantObject, HierarchyObject, StringObject +@cocotb.test() +async def pseudo_region_access(dut): + """Test that pseudo-regions are accessible before iteration""" + + # Ensure pseudo-region lookup will fail + if len(dut._sub_handles) != 0: + dut._sub_handles = {} + + pseudo_region = dut.genblk1 + dut._log.info("Found %s (%s)", pseudo_region._name, type(pseudo_region)) + first_generate_instance = pseudo_region[0] + dut._log.info("Found %s (%s)", first_generate_instance._name, type(first_generate_instance)) + + @cocotb.test() def recursive_discover(dut): """Discover absolutely everything in the DUT""" From c66bfd810f081af8e7afb842438a9bda3a2db96b Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 28 Sep 2020 00:07:04 +0200 Subject: [PATCH 2506/2656] Add details to the roadmap section Mention the release cycle and add links to deprecation issues/PRs. --- documentation/source/roadmap.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/documentation/source/roadmap.rst b/documentation/source/roadmap.rst index f9fde946..01504db0 100644 --- a/documentation/source/roadmap.rst +++ b/documentation/source/roadmap.rst @@ -2,10 +2,17 @@ Roadmap ******* -cocotb is in active development. +cocotb is under development; +however there is no "master plan". +We depend upon users to contribute new features and improvements +while maintainers focus mostly on reviewing PRs, project management, and bugfixes. +Releases will occur approximately every 6 months. We use GitHub issues to track our pending tasks. Take a look at the `open feature list `_ to see the work that's lined up, or the `list of good first issues `_ to get your feet wet. If you have a GitHub account you can also `raise an enhancement request `_ to suggest new features. + +You may be interested in discussions about `planned deprecations `_ +or review the `list of already applied deprecations `_. From f0e04bd93878b4a091d505ff90b7e17ab5592e1c Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 26 Sep 2020 20:03:42 -0500 Subject: [PATCH 2507/2656] Fix GHA regression tests Currently it passes because it's testing nothing. --- .github/workflows/regression-tests.yml | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml index 052b08e8..72d307fa 100644 --- a/.github/workflows/regression-tests.yml +++ b/.github/workflows/regression-tests.yml @@ -17,11 +17,19 @@ jobs: strategy: fail-fast: false matrix: - sim-version: ["v10_3", "master"] - sim: ['icarus'] - lang: ['verilog'] - python-version: [3.8] - os: ['windows-latest'] + include: + - sim-version: "v10_3" + sim: 'icarus' + lang: 'verilog' + python-version: 3.8 + os: 'windows-latest' + - sim-version: "master" + sim: 'icarus' + lang: 'verilog' + python-version: 3.8 + os: 'windows-latest' + may_fail: true + continue-on-error: ${{matrix.may_fail || false}} env: SIM: ${{matrix.sim}} TOPLEVEL_LANG: ${{matrix.lang}} @@ -36,18 +44,19 @@ jobs: python-version: ${{matrix.python-version}} - name: Set up cocotb build dependencies run: | - conda install -c msys2 m2-base m2-make m2-autoconf m2-flex m2-bison m2-gperf m2w64-toolchain libpython + conda install -c msys2 m2-base m2-make m2-autoconf m2-flex m2-bison m2-gperf m2w64-toolchain libpython virtualenv=16 - name: Build and install Icarus run: | git clone https://github.com/steveicarus/iverilog.git -b "${{matrix.sim-version}}" cd iverilog bash ./autoconf.sh - ./configure + bash ./configure --host=x86_64-w64-mingw32 --prefix=/c/iverilog make -j $(nproc) make install + echo "::add-path::C:\iverilog\bin" - name: Install Python testing dependencies run: | - pip install tox tox-gh-actions + pip install tox - name: Test with tox run: | tox -e py3_mingw From 5f40a63dbff67267a24f93f25f731722e8cab38d Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 30 Sep 2020 18:15:08 -0500 Subject: [PATCH 2508/2656] Don't use tox for Windows regressions --- .github/workflows/regression-tests.yml | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml index 72d307fa..af064c62 100644 --- a/.github/workflows/regression-tests.yml +++ b/.github/workflows/regression-tests.yml @@ -29,7 +29,6 @@ jobs: python-version: 3.8 os: 'windows-latest' may_fail: true - continue-on-error: ${{matrix.may_fail || false}} env: SIM: ${{matrix.sim}} TOPLEVEL_LANG: ${{matrix.lang}} @@ -44,7 +43,7 @@ jobs: python-version: ${{matrix.python-version}} - name: Set up cocotb build dependencies run: | - conda install -c msys2 m2-base m2-make m2-autoconf m2-flex m2-bison m2-gperf m2w64-toolchain libpython virtualenv=16 + conda install -c msys2 m2-base m2-make m2-autoconf m2-flex m2-bison m2-gperf m2w64-toolchain libpython - name: Build and install Icarus run: | git clone https://github.com/steveicarus/iverilog.git -b "${{matrix.sim-version}}" @@ -56,7 +55,21 @@ jobs: echo "::add-path::C:\iverilog\bin" - name: Install Python testing dependencies run: | - pip install tox - - name: Test with tox + pip install coverage xunitparser pytest pytest-cov + - name: Install cocotb run: | - tox -e py3_mingw + python -m pip install --global-option build_ext --global-option --compiler=mingw32 -e . + - name: Test + continue-on-error: ${{matrix.may_fail || false}} + run: | + pytest + $pass = $? + make test + exit -not ($pass -and $?) + # inverted the pass for exit because the boolean is converted to 0/1, but an exit code of 1 is a failure + - name: Upload coverage + if: steps.Test.outcome != 'failure' + run: | + pip install codecov + bash -c 'find . -type f -name "\.coverage\.cocotb" | xargs coverage combine --append' + codecov From e28ecefc44da423dc37c324cb71f8315831dea48 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 8 Oct 2020 20:09:37 +0200 Subject: [PATCH 2509/2656] Ignore collected code coverage data --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index f3d2eb66..b0e37864 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,12 @@ # C objects *.o +# Python and C++ code coverage +.coverage +*.gcno +*.gcda +*.gcov + # Packaging *.egg *.egg-info From a77f8b01328a3c233acb98001e6ded551ea02362 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 8 Oct 2020 20:07:38 +0200 Subject: [PATCH 2510/2656] Fix some typos --- cocotb/share/lib/vpi/VpiImpl.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cocotb/share/lib/vpi/VpiImpl.cpp b/cocotb/share/lib/vpi/VpiImpl.cpp index 39a2c05c..aec15d9e 100644 --- a/cocotb/share/lib/vpi/VpiImpl.cpp +++ b/cocotb/share/lib/vpi/VpiImpl.cpp @@ -362,9 +362,9 @@ GpiObjHdl* VpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) * wire [7:0] sig_t4 [0:1][0:2]; * * Assume vpi_hdl is for "sig_t4": - * vpi_handl_by_index(vpi_hdl, 0); // Returns a handle to sig_t4[0] for IUS, but NULL on Questa + * vpi_handle_by_index(vpi_hdl, 0); // Returns a handle to sig_t4[0] for IUS, but NULL on Questa * - * Questa only works when both indicies are provided, i.e. will need a pseudo-handle to behave like the first index. + * Questa only works when both indices are provided, i.e. will need a pseudo-handle to behave like the first index. */ if (new_hdl == NULL) { int left = parent->get_range_left(); @@ -393,7 +393,7 @@ GpiObjHdl* VpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) std::string act_hdl_name = vpi_get_str(vpiName, p_hdl); - /* Removing the act_hdl_name from the parent->get_name() will leave the psuedo-indices */ + /* Removing the act_hdl_name from the parent->get_name() will leave the pseudo-indices */ if (act_hdl_name.length() < parent->get_name().length()) { std::string idx_str = parent->get_name().substr(act_hdl_name.length()); From 89e7bd77b1e9ceb3172b4659e267bc4c42217a0f Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 6 Oct 2020 22:23:07 +0200 Subject: [PATCH 2511/2656] Print object type info in VpiSingleIterator --- cocotb/share/lib/vpi/VpiImpl.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cocotb/share/lib/vpi/VpiImpl.h b/cocotb/share/lib/vpi/VpiImpl.h index 6c5c3265..6b074b5e 100644 --- a/cocotb/share/lib/vpi/VpiImpl.h +++ b/cocotb/share/lib/vpi/VpiImpl.h @@ -223,7 +223,8 @@ class VpiSingleIterator : public GpiIterator { vpiHandle vpi_hdl = m_parent->get_handle(); m_iterator = vpi_iterate(vpitype, vpi_hdl); if (NULL == m_iterator) { - LOG_WARN("vpi_iterate returned NULL for %d", vpitype); + LOG_WARN("vpi_iterate returned NULL for type %d for object %s(%d)", + vpitype, vpi_get_str(vpiType, vpi_hdl), vpi_get(vpiType, vpi_hdl)); return; } } From 744dd2f46cec7e6619da3117ffc0fd254934753b Mon Sep 17 00:00:00 2001 From: Marlon James Date: Thu, 8 Oct 2020 17:04:53 -0700 Subject: [PATCH 2512/2656] Use realpath when ACTIVEHDL_BIN_DIR is set. Without realpath, when ACTIVEHDL_BIN_DIR is set to a Windows-style path, you can end up with a blank CMD variable and the simulation run will fail. --- cocotb/share/makefiles/simulators/Makefile.activehdl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.activehdl b/cocotb/share/makefiles/simulators/Makefile.activehdl index 942a1399..65e22a20 100644 --- a/cocotb/share/makefiles/simulators/Makefile.activehdl +++ b/cocotb/share/makefiles/simulators/Makefile.activehdl @@ -9,7 +9,7 @@ include $(shell cocotb-config --makefiles)/Makefile.inc CMD_BIN := vsimsa ifdef ACTIVEHDL_BIN_DIR - CMD := $(shell :; command -v $(ACTIVEHDL_BIN_DIR)/$(CMD_BIN) 2>/dev/null) + CMD := $(shell :; command -v $(realpath $(ACTIVEHDL_BIN_DIR))/$(CMD_BIN) 2>/dev/null) else # auto-detect bin dir from system path CMD := $(shell :; command -v $(CMD_BIN) 2>/dev/null) From 286a4a0b6f3dd9eefb0e27b7e221e5ae78bacbc3 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 9 Oct 2020 23:30:00 +0200 Subject: [PATCH 2513/2656] Use async/await syntax and assertions --- tests/test_cases/test_array/test_array.py | 73 +++++++++-------------- 1 file changed, 27 insertions(+), 46 deletions(-) diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py index 35f03650..d52ab037 100644 --- a/tests/test_cases/test_array/test_array.py +++ b/tests/test_cases/test_array/test_array.py @@ -12,56 +12,44 @@ def _check_type(tlog, hdl, expected): - if not isinstance(hdl, expected): - raise TestFailure(">{0!r} ({1})< should be >{2}<".format(hdl, hdl._type, expected)) - else: - tlog.info(" Found %r (%s) with length=%d", hdl, hdl._type, len(hdl)) + assert isinstance(hdl, expected), ">{0!r} ({1})< should be >{2}<".format(hdl, hdl._type, expected) + tlog.info(" Found %r (%s) with length=%d", hdl, hdl._type, len(hdl)) def _check_int(tlog, hdl, expected): - if int(hdl) != expected: - raise TestFailure("{2!r}: Expected >{0}< but got >{1}<".format(expected, int(hdl), hdl)) - else: - tlog.info(" Found {0!r} ({1}) with value={2}".format(hdl, hdl._type, int(hdl))) + assert int(hdl) == expected, "{2!r}: Expected >{0}< but got >{1}<".format(expected, int(hdl), hdl) + tlog.info(" Found {0!r} ({1}) with value={2}".format(hdl, hdl._type, int(hdl))) def _check_logic(tlog, hdl, expected): - if int(hdl) != expected: - raise TestFailure("{2!r}: Expected >0x{0:X}< but got >0x{1:X}<".format(expected, int(hdl), hdl)) - else: - tlog.info(" Found {0!r} ({1}) with value=0x{2:X}".format(hdl, hdl._type, int(hdl))) + assert int(hdl) == expected, "{2!r}: Expected >0x{0:X}< but got >0x{1:X}<".format(expected, int(hdl), hdl) + tlog.info(" Found {0!r} ({1}) with value=0x{2:X}".format(hdl, hdl._type, int(hdl))) def _check_str(tlog, hdl, expected): - if hdl.value != expected: - raise TestFailure("{2!r}: Expected >{0}< but got >{1}<".format(expected, str(hdl), hdl)) - else: - tlog.info(" Found {0!r} ({1}) with value={2}".format(hdl, hdl._type, str(hdl))) + assert hdl.value == expected, "{2!r}: Expected >{0}< but got >{1}<".format(expected, str(hdl), hdl) + tlog.info(" Found {0!r} ({1}) with value={2}".format(hdl, hdl._type, str(hdl))) def _check_real(tlog, hdl, expected): - if float(hdl) != expected: - raise TestFailure("{2!r}: Expected >{0}< but got >{1}<".format(expected, float(hdl), hdl)) - else: - tlog.info(" Found {0!r} ({1}) with value={2}".format(hdl, hdl._type, float(hdl))) + assert float(hdl) == expected, "{2!r}: Expected >{0}< but got >{1}<".format(expected, float(hdl), hdl) + tlog.info(" Found {0!r} ({1}) with value={2}".format(hdl, hdl._type, float(hdl))) def _check_value(tlog, hdl, expected): - if hdl.value != expected: - raise TestFailure("{2!r}: Expected >{0}< but got >{1}<".format(expected, hdl.value, hdl)) - else: - tlog.info(" Found {0!r} ({1}) with value={2}".format(hdl, hdl._type, hdl.value)) + assert hdl.value == expected, "{2!r}: Expected >{0}< but got >{1}<".format(expected, hdl.value, hdl) + tlog.info(" Found {0!r} ({1}) with value={2}".format(hdl, hdl._type, hdl.value)) # NOTE: simulator-specific handling is done in this test itself, not via expect_error in the decorator @cocotb.test() -def test_read_write(dut): +async def test_read_write(dut): """Test handle inheritance""" tlog = logging.getLogger("cocotb.test") - cocotb.fork(Clock(dut.clk, 1000).start()) + cocotb.fork(Clock(dut.clk, 10, "ns").start()) - yield Timer(1000) + await Timer(10, "ns") tlog.info("Checking Generics/Parameters:") _check_logic(tlog, dut.param_logic , 1) @@ -115,7 +103,7 @@ def test_read_write(dut): dut.select_in = 2 - yield Timer(1000) + await Timer(10, "ns") tlog.info("Writing the signals!!!") dut.sig_logic = 1 @@ -147,7 +135,7 @@ def test_read_write(dut): dut.sig_cmplx[1].b[1] = 0xEF dut.sig_cmplx[1].b[2] = 0x55 - yield Timer(1000) + await Timer(10, "ns") tlog.info("Checking writes:") _check_logic(tlog, dut.port_logic_out , 1) @@ -191,7 +179,7 @@ def test_read_write(dut): dut.sig_rec.b[1][7] = 1 dut.sig_cmplx[1].b[1][0] = 0 - yield Timer(1000) + await Timer(10, "ns") tlog.info("Checking writes (2):") _check_logic(tlog, dut.port_logic_vec_out, 0xC8) @@ -209,12 +197,10 @@ def test_read_write(dut): @cocotb.test() -def test_gen_loop(dut): +async def test_gen_loop(dut): """Test accessing Generate Loops""" tlog = logging.getLogger("cocotb.test") - yield Timer(1000) - asc_gen_20 = dut.asc_gen[20] desc_gen = dut.desc_gen @@ -248,7 +234,7 @@ def test_gen_loop(dut): @cocotb.test() -def test_discover_all(dut): +async def test_discover_all(dut): r"""Discover everything in the DUT: dut TYPE CNT NOTES EXCEPTIONS @@ -323,7 +309,7 @@ def test_discover_all(dut): tlog = logging.getLogger("cocotb.test") - yield Timer(1000) + await Timer(10, "ns") # Need to clear sub_handles so won't attempt to iterate over handles like sig_rec and sig_rec_array # @@ -385,13 +371,11 @@ def _discover(obj, indent): @cocotb.test(skip=(cocotb.LANGUAGE in ["verilog"] or cocotb.SIM_NAME.lower().startswith(("riviera")))) -def test_direct_constant_indexing(dut): +async def test_direct_constant_indexing(dut): """Test directly accessing constant/parameter data in arrays, i.e. not iterating""" tlog = logging.getLogger("cocotb.test") - yield Timer(2000) - tlog.info("Checking Types of complex array structures in constants/parameters.") _check_type(tlog, dut.param_rec, HierarchyObject) _check_type(tlog, dut.param_rec.a, ConstantObject) @@ -417,24 +401,24 @@ def test_direct_constant_indexing(dut): @cocotb.test() -def test_direct_signal_indexing(dut): +async def test_direct_signal_indexing(dut): """Test directly accessing signal/net data in arrays, i.e. not iterating""" tlog = logging.getLogger("cocotb.test") - cocotb.fork(Clock(dut.clk, 1000).start()) + cocotb.fork(Clock(dut.clk, 10, "ns").start()) dut.port_desc_in <= 0 dut.port_asc_in <= 0 dut.port_ofst_in <= 0 - yield Timer(2000) + await Timer(20, "ns") dut.port_desc_in[2] <= 1 dut.port_asc_in[2] <= 1 dut.port_ofst_in[2] <= 1 - yield Timer(2000) + await Timer(20, "ns") tlog.info("Checking bit mapping from input to generate loops.") if int(dut.desc_gen[2].sig) != 1: @@ -513,13 +497,10 @@ def test_direct_signal_indexing(dut): @cocotb.test(skip=(cocotb.LANGUAGE in ["verilog"])) -def test_extended_identifiers(dut): +async def test_extended_identifiers(dut): """Test accessing extended identifiers""" tlog = logging.getLogger("cocotb.test") - - yield Timer(2000) - tlog.info("Checking extended identifiers.") _check_type(tlog, dut._id("\\ext_id\\", extended=False), ModifiableObject) _check_type(tlog, dut._id("!"), ModifiableObject) From ef5c9297757a39aac3842ddb083ac7fa33471a4e Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 6 Oct 2020 22:16:03 +0200 Subject: [PATCH 2514/2656] Add test for the .loads() iterator --- cocotb/handle.py | 10 ++++++++-- .../test_iteration_mixedlang/test_iteration.py | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index bf0e0399..25714e35 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -624,11 +624,17 @@ class NonConstantObject(NonHierarchyIndexableObject): # FIXME: what is the difference to ModifiableObject? Explain in docstring. def drivers(self): - """An iterator for gathering all drivers for a signal.""" + """An iterator for gathering all drivers for a signal. + + Few simulators implement this. + """ return self._handle.iterate(simulator.DRIVERS) def loads(self): - """An iterator for gathering all loads on a signal.""" + """An iterator for gathering all loads on a signal. + + Few simulators implement this. + """ return self._handle.iterate(simulator.LOADS) diff --git a/tests/test_cases/test_iteration_mixedlang/test_iteration.py b/tests/test_cases/test_iteration_mixedlang/test_iteration.py index dced184d..bfc16b98 100644 --- a/tests/test_cases/test_iteration_mixedlang/test_iteration.py +++ b/tests/test_cases/test_iteration_mixedlang/test_iteration.py @@ -60,6 +60,22 @@ def test_drivers(dut): raise TestFailure("No drivers found for dut.i_verilog.uart1.uart_rx_1.rx_data") +@cocotb.test(expect_fail=True) +def test_loads(dut): + """ + Try iterating over loads of a signal. + + Seems that few simulators implement vpiLoad + """ + tlog = logging.getLogger("cocotb.test") + yield Timer(100) + for load in dut.i_verilog.uart1.ser_in.loads(): + tlog.info("Found %s" % repr(load)) + break + else: + raise TestFailure("No loads found for dut.i_verilog.uart1.ser_in") + + @cocotb.test() def recursive_discovery(dut): """ From 25214bad1275a90f9e919d167ec696a7bb68607c Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 8 Oct 2020 23:00:16 +0200 Subject: [PATCH 2515/2656] Convert testcase to async def --- .../test_iteration.py | 38 ++++++++----------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/tests/test_cases/test_iteration_mixedlang/test_iteration.py b/tests/test_cases/test_iteration_mixedlang/test_iteration.py index bfc16b98..78d938a6 100644 --- a/tests/test_cases/test_iteration_mixedlang/test_iteration.py +++ b/tests/test_cases/test_iteration_mixedlang/test_iteration.py @@ -45,39 +45,33 @@ def recursive_dump(parent, log): @cocotb.test(expect_fail=True) -def test_drivers(dut): - """ - Try iterating over drivers of a signal. +async def test_drivers(dut): + """Try iterating over drivers of a signal. - Seems that few simulators implement vpiDriver + Seems that few simulators implement ``vpiDriver``. """ tlog = logging.getLogger("cocotb.test") - yield Timer(100) - for driver in dut.i_verilog.uart1.uart_rx_1.rx_data.drivers(): + drivers = list(dut.i_verilog.uart1.uart_rx_1.rx_data.drivers()) + assert drivers, "No drivers found for dut.i_verilog.uart1.uart_rx_1.rx_data" + for driver in drivers: tlog.info("Found %s" % repr(driver)) - break - else: - raise TestFailure("No drivers found for dut.i_verilog.uart1.uart_rx_1.rx_data") @cocotb.test(expect_fail=True) -def test_loads(dut): - """ - Try iterating over loads of a signal. +async def test_loads(dut): + """Try iterating over loads of a signal. - Seems that few simulators implement vpiLoad + Seems that few simulators implement ``vpiLoad``. """ tlog = logging.getLogger("cocotb.test") - yield Timer(100) - for load in dut.i_verilog.uart1.ser_in.loads(): + loads = list(dut.i_verilog.uart1.ser_in.loads()) + assert loads, "No loads found for dut.i_verilog.uart1.ser_in" + for load in loads: tlog.info("Found %s" % repr(load)) - break - else: - raise TestFailure("No loads found for dut.i_verilog.uart1.ser_in") @cocotb.test() -def recursive_discovery(dut): +async def recursive_discovery(dut): """ Recursively discover every single object in the design """ @@ -90,7 +84,6 @@ def recursive_discovery(dut): pass_total = 1024 tlog = logging.getLogger("cocotb.test") - yield Timer(100) total = recursive_dump(dut, tlog) if pass_total != total: @@ -105,9 +98,9 @@ def recursive_discovery(dut): @cocotb.test() -def recursive_discovery_boundary(dut): +async def recursive_discovery_boundary(dut): """ - Iteration though the boundary works but this just double checks + Iteration through the boundary works but this just double checks """ if cocotb.SIM_NAME.lower().startswith(("ncsim", "xmsim")): pass_total = 462 @@ -115,7 +108,6 @@ def recursive_discovery_boundary(dut): pass_total = 478 tlog = logging.getLogger("cocotb.test") - yield Timer(100) total = recursive_dump(dut.i_vhdl, tlog) tlog.info("Found a total of %d things", total) if total != pass_total: From bd2a862d3007db54d97cf90920a9fb1d00706255 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 8 Oct 2020 23:03:41 +0200 Subject: [PATCH 2516/2656] Print all discovered drivers/loads if any --- .../test_iteration.py | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/tests/test_cases/test_iteration_mixedlang/test_iteration.py b/tests/test_cases/test_iteration_mixedlang/test_iteration.py index 78d938a6..a74254dc 100644 --- a/tests/test_cases/test_iteration_mixedlang/test_iteration.py +++ b/tests/test_cases/test_iteration_mixedlang/test_iteration.py @@ -26,8 +26,6 @@ import logging import cocotb -from cocotb.triggers import Timer -from cocotb.result import TestFailure def recursive_dump(parent, log): @@ -72,9 +70,7 @@ async def test_loads(dut): @cocotb.test() async def recursive_discovery(dut): - """ - Recursively discover every single object in the design - """ + """Recursively discover every single object in the design.""" if cocotb.SIM_NAME.lower().startswith(("ncsim", "xmsim")): # vpiAlways = 31 and vpiStructVar = 2 do not show up in IUS/Xcelium pass_total = 917 @@ -86,22 +82,17 @@ async def recursive_discovery(dut): tlog = logging.getLogger("cocotb.test") total = recursive_dump(dut, tlog) - if pass_total != total: - raise TestFailure("Expected %d but found %d" % (pass_total, total)) - else: - tlog.info("Found a total of %d things", total) + assert pass_total == total, "Expected %d but found %d" % (pass_total, total) + tlog.info("Found a total of %d things", total) - if not isinstance(dut.i_verilog.uart1.baud_gen_1.baud_freq, cocotb.handle.ModifiableObject): - tlog.error("Expected dut.i_verilog.uart1.baud_gen_1.baud_freq to be modifiable") - tlog.error("but it was %s" % type(dut.i_verilog.uart1.baud_gen_1.baud_freq).__name__) - raise TestFailure() + assert isinstance(dut.i_verilog.uart1.baud_gen_1.baud_freq, cocotb.handle.ModifiableObject), \ + ("Expected dut.i_verilog.uart1.baud_gen_1.baud_freq to be modifiable" + " but it was %s" % type(dut.i_verilog.uart1.baud_gen_1.baud_freq).__name__) @cocotb.test() async def recursive_discovery_boundary(dut): - """ - Iteration through the boundary works but this just double checks - """ + """Iteration through the boundary works but this just double checks.""" if cocotb.SIM_NAME.lower().startswith(("ncsim", "xmsim")): pass_total = 462 else: @@ -110,5 +101,4 @@ async def recursive_discovery_boundary(dut): tlog = logging.getLogger("cocotb.test") total = recursive_dump(dut.i_vhdl, tlog) tlog.info("Found a total of %d things", total) - if total != pass_total: - raise TestFailure("Expected %d objects but found %d" % (pass_total, total)) + assert total == pass_total, "Expected %d objects but found %d" % (pass_total, total) From 444446fde9725ccaa5ff172c484debbedcfcc6e3 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sun, 11 Oct 2020 22:59:14 +0200 Subject: [PATCH 2517/2656] Fix typo 'seqfault' --- tests/designs/close_module/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/designs/close_module/Makefile b/tests/designs/close_module/Makefile index 9cfff4a4..a336690b 100644 --- a/tests/designs/close_module/Makefile +++ b/tests/designs/close_module/Makefile @@ -49,7 +49,7 @@ ifneq ($(SIM),vcs) include $(shell cocotb-config --makefiles)/Makefile.sim else all: - @echo "Skipping test as system call overrides seqfault VCS" + @echo "Skipping test as system call overrides segfault VCS" endif endif From 6be15a7fea1b0c18a0d394eae031e9e1ccfdf99a Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 8 Oct 2020 23:07:55 +0200 Subject: [PATCH 2518/2656] Adapt number of discovered objects for Cadence Xcelium --- tests/test_cases/test_iteration_mixedlang/test_iteration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/test_iteration_mixedlang/test_iteration.py b/tests/test_cases/test_iteration_mixedlang/test_iteration.py index a74254dc..9db0da5e 100644 --- a/tests/test_cases/test_iteration_mixedlang/test_iteration.py +++ b/tests/test_cases/test_iteration_mixedlang/test_iteration.py @@ -73,7 +73,7 @@ async def recursive_discovery(dut): """Recursively discover every single object in the design.""" if cocotb.SIM_NAME.lower().startswith(("ncsim", "xmsim")): # vpiAlways = 31 and vpiStructVar = 2 do not show up in IUS/Xcelium - pass_total = 917 + pass_total = 975 elif cocotb.SIM_NAME.lower().startswith(("modelsim")): pass_total = 933 else: From 6eb637ae1ec4110fb3aa10ef04051fe8ef163108 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 15 Oct 2020 19:15:39 +0200 Subject: [PATCH 2519/2656] Remove explicit newlines, LOG_*() already end with one --- cocotb/share/include/cocotb_utils.h | 2 +- cocotb/share/lib/embed/gpi_embed.cpp | 6 +++--- cocotb/share/lib/fli/FliImpl.cpp | 2 +- cocotb/share/lib/fli/FliObjHdl.cpp | 2 +- cocotb/share/lib/utils/cocotb_utils.cpp | 8 ++++---- cocotb/share/lib/vhpi/VhpiCbHdl.cpp | 12 ++++++------ cocotb/share/lib/vpi/VpiCbHdl.cpp | 4 ++-- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/cocotb/share/include/cocotb_utils.h b/cocotb/share/include/cocotb_utils.h index 799a0721..f366e01a 100644 --- a/cocotb/share/include/cocotb_utils.h +++ b/cocotb/share/include/cocotb_utils.h @@ -65,7 +65,7 @@ extern COCOTBUTILS_EXPORT int is_python_context; #define to_simulator() do { \ if (!is_python_context) { \ - LOG_ERROR("FATAL: We have returned twice from python\n"); \ + LOG_ERROR("FATAL: We have returned twice from Python"); \ exit(1); \ } \ --is_python_context; \ diff --git a/cocotb/share/lib/embed/gpi_embed.cpp b/cocotb/share/lib/embed/gpi_embed.cpp index 6c7a02a2..6b0735d7 100644 --- a/cocotb/share/lib/embed/gpi_embed.cpp +++ b/cocotb/share/lib/embed/gpi_embed.cpp @@ -124,7 +124,7 @@ extern "C" void embed_init_python(void) void * lib_handle = utils_dyn_open(PY_SO_LIB); if (!lib_handle) { - LOG_ERROR("Failed to find Python shared library\n"); + LOG_ERROR("Failed to find Python shared library"); } to_python(); @@ -153,7 +153,7 @@ extern "C" void embed_init_python(void) return; } - LOG_ERROR("Waiting for %lu seconds - attach to PID %d with your debugger\n", sleep_time, getpid()); + LOG_ERROR("Waiting for %lu seconds - attach to PID %d with your debugger", sleep_time, getpid()); sleep((unsigned int)sleep_time); } } @@ -208,7 +208,7 @@ static int get_module_ref(const char *modname, PyObject **mod) if (pModule == NULL) { PyErr_Print(); - LOG_ERROR("Failed to load Python module \"%s\"\n", modname); + LOG_ERROR("Failed to load Python module \"%s\"", modname); return -1; } diff --git a/cocotb/share/lib/fli/FliImpl.cpp b/cocotb/share/lib/fli/FliImpl.cpp index c89f6b51..84f25301 100644 --- a/cocotb/share/lib/fli/FliImpl.cpp +++ b/cocotb/share/lib/fli/FliImpl.cpp @@ -1061,7 +1061,7 @@ static void register_embed() void cocotb_init() { - LOG_INFO("cocotb_init called\n"); + LOG_INFO("cocotb_init called"); register_embed(); gpi_load_extra_libs(); register_initial_callback(); diff --git a/cocotb/share/lib/fli/FliObjHdl.cpp b/cocotb/share/lib/fli/FliObjHdl.cpp index 7ae9143a..b1bc6f7c 100644 --- a/cocotb/share/lib/fli/FliObjHdl.cpp +++ b/cocotb/share/lib/fli/FliObjHdl.cpp @@ -233,7 +233,7 @@ int FliEnumObjHdl::set_signal_value(const long value, const gpi_set_action_t act } if (value > m_num_enum || value < 0) { - LOG_ERROR("Attempted to set a enum with range [0,%d] with invalid value %d!\n", m_num_enum, value); + LOG_ERROR("Attempted to set an enum with range [0,%d] with invalid value %d!", m_num_enum, value); return -1; } diff --git a/cocotb/share/lib/utils/cocotb_utils.cpp b/cocotb/share/lib/utils/cocotb_utils.cpp index 92cd7c17..0e852c14 100644 --- a/cocotb/share/lib/utils/cocotb_utils.cpp +++ b/cocotb/share/lib/utils/cocotb_utils.cpp @@ -47,7 +47,7 @@ extern "C" void* utils_dyn_open(const char* lib_name) SetErrorMode(0); ret = static_cast(LoadLibrary(lib_name)); if (!ret) { - const char *log_fmt = "Unable to open lib %s%s%s\n"; + const char *log_fmt = "Unable to open lib %s%s%s"; LPSTR msg_ptr; if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, @@ -66,7 +66,7 @@ extern "C" void* utils_dyn_open(const char* lib_name) ret = dlopen(lib_name, RTLD_LAZY | RTLD_GLOBAL); if (!ret) { - LOG_ERROR("Unable to open lib %s: %s\n", lib_name, dlerror()); + LOG_ERROR("Unable to open lib %s: %s", lib_name, dlerror()); } #endif return ret; @@ -78,7 +78,7 @@ extern "C" void* utils_dyn_sym(void *handle, const char* sym_name) #if ! defined(__linux__) && ! defined(__APPLE__) entry_point = reinterpret_cast(GetProcAddress(static_cast(handle), sym_name)); if (!entry_point) { - const char *log_fmt = "Unable to find symbol %s%s%s\n"; + const char *log_fmt = "Unable to find symbol %s%s%s"; LPSTR msg_ptr; if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, @@ -94,7 +94,7 @@ extern "C" void* utils_dyn_sym(void *handle, const char* sym_name) #else entry_point = dlsym(handle, sym_name); if (!entry_point) { - LOG_ERROR("Unable to find symbol %s: %s\n", sym_name, dlerror()); + LOG_ERROR("Unable to find symbol %s: %s", sym_name, dlerror()); } #endif return entry_point; diff --git a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp index ca2959b6..43bced10 100644 --- a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp +++ b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp @@ -39,7 +39,7 @@ extern "C" void handle_vhpi_callback(const vhpiCbDataT *cb_data); VhpiArrayObjHdl::~VhpiArrayObjHdl() { - LOG_DEBUG("Releasing VhpiArrayObjHdl handle at %p\n", (void *)get_handle()); + LOG_DEBUG("Releasing VhpiArrayObjHdl handle at %p", (void *)get_handle()); if (vhpi_release_handle(get_handle())) check_vhpi_error(); } @@ -48,7 +48,7 @@ VhpiObjHdl::~VhpiObjHdl() { /* Don't release handles for pseudo-regions, as they borrow the handle of the containing region */ if (m_type != GPI_GENARRAY) { - LOG_DEBUG("Releasing VhpiObjHdl handle at %p\n", (void *)get_handle()); + LOG_DEBUG("Releasing VhpiObjHdl handle at %p", (void *)get_handle()); if (vhpi_release_handle(get_handle())) check_vhpi_error(); } @@ -68,7 +68,7 @@ VhpiSignalObjHdl::~VhpiSignalObjHdl() if (m_binvalue.value.str) delete [] m_binvalue.value.str; - LOG_DEBUG("Releasing VhpiSignalObjHdl handle at %p\n", (void *)get_handle()); + LOG_DEBUG("Releasing VhpiSignalObjHdl handle at %p", (void *)get_handle()); if (vhpi_release_handle(get_handle())) check_vhpi_error(); } @@ -420,8 +420,8 @@ int VhpiCbHdl::arm_callback() if (m_state == GPI_PRIMED) return 0; - /* Do we already have a handle, if so and it is disabled then - just re-enable it */ + /* Do we already have a handle? If so and it is disabled then + just re-enable it. */ if (get_handle()) { cbState = (vhpiStateT)vhpi_get(vhpiStateP, get_handle()); @@ -444,7 +444,7 @@ int VhpiCbHdl::arm_callback() cbState = (vhpiStateT)vhpi_get(vhpiStateP, new_hdl); if (vhpiEnable != cbState) { - LOG_ERROR("VHPI: Registered callback isn't enabled! Got %d\n", cbState); + LOG_ERROR("VHPI: Registered callback isn't enabled! Got %d", cbState); goto error; } diff --git a/cocotb/share/lib/vpi/VpiCbHdl.cpp b/cocotb/share/lib/vpi/VpiCbHdl.cpp index 454457ca..fe89e6b6 100644 --- a/cocotb/share/lib/vpi/VpiCbHdl.cpp +++ b/cocotb/share/lib/vpi/VpiCbHdl.cpp @@ -529,11 +529,11 @@ int VpiTimedCbHdl::cleanup_callback() /* Issue #188: Work around for modelsim that is harmless to others too, we tag the time as delete, let it fire then do not pass up */ - LOG_DEBUG("Not removing PRIMED timer %d\n", vpi_time.low); + LOG_DEBUG("Not removing PRIMED timer %d", vpi_time.low); m_state = GPI_DELETE; return 0; case GPI_DELETE: - LOG_DEBUG("Removing DELETE timer %d\n", vpi_time.low); + LOG_DEBUG("Removing DELETE timer %d", vpi_time.low); default: break; } From 09eeb83ef5cd677c20f667407daad0486e8a840f Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 14 Oct 2020 21:25:51 +0200 Subject: [PATCH 2520/2656] Update issue templates --- .github/ISSUE_TEMPLATE/information-wanted.md | 22 ++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/information-wanted.md diff --git a/.github/ISSUE_TEMPLATE/information-wanted.md b/.github/ISSUE_TEMPLATE/information-wanted.md new file mode 100644 index 00000000..b167ba74 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/information-wanted.md @@ -0,0 +1,22 @@ +--- +name: Information wanted +about: Remind reporters to provide information about their environment in a Markdown + comment +title: '' +labels: '' +assignees: '' + +--- + + From 5acfc229e27a0e4697c971503db86a8425cc4d61 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 14 Oct 2020 21:27:21 +0200 Subject: [PATCH 2521/2656] Update information-wanted.md --- .github/ISSUE_TEMPLATE/information-wanted.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/information-wanted.md b/.github/ISSUE_TEMPLATE/information-wanted.md index b167ba74..e87b0f8a 100644 --- a/.github/ISSUE_TEMPLATE/information-wanted.md +++ b/.github/ISSUE_TEMPLATE/information-wanted.md @@ -1,7 +1,6 @@ --- name: Information wanted -about: Remind reporters to provide information about their environment in a Markdown - comment +about: Remind reporters to provide information about their environment title: '' labels: '' assignees: '' @@ -9,7 +8,8 @@ assignees: '' --- From 78957628501cf0f8898affee1f07d1265190c51b Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Thu, 15 Oct 2020 23:53:42 -0500 Subject: [PATCH 2524/2656] Fix VHPI cast issue #2126 vhpiIntT before the 2008 standard was defined as an unsigned integer in vhpi_user.h. However, the standard states that all integers are 32-bit integers with two's complement representation. The issue occurs when vhpiIntT is defined as uint32_t and long is a signed 64 bit integer. Negative values are represented as large unsigned values in two's complement. When this value is returned as a long, it is implicitly converted ("safely") to the larger signed representation. To prevent this issue we convert the vhpiIntT into a 32-bit two's complement integer type (int32_t) before allowing the conversion to take place. --- cocotb/share/lib/vhpi/VhpiCbHdl.cpp | 2 +- documentation/source/newsfragments/2129.bugfix.rst | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 documentation/source/newsfragments/2129.bugfix.rst diff --git a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp index 43bced10..e003d7a1 100644 --- a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp +++ b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp @@ -789,7 +789,7 @@ long VhpiSignalObjHdl::get_signal_value_long() LOG_ERROR("VHPI: Failed to get value of type long"); } - return value.value.intg; + return static_cast(value.value.intg); } diff --git a/documentation/source/newsfragments/2129.bugfix.rst b/documentation/source/newsfragments/2129.bugfix.rst new file mode 100644 index 00000000..3434fd66 --- /dev/null +++ b/documentation/source/newsfragments/2129.bugfix.rst @@ -0,0 +1 @@ +Fixed issue on Mac OS and Linux where negative integers were returned as large positive values. From 365212ec67f362676aefa87351f9c69351110a0e Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 16 Oct 2020 08:45:47 -0500 Subject: [PATCH 2525/2656] Apply suggestions from code review Co-authored-by: Colin Marquardt --- documentation/source/newsfragments/2129.bugfix.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/newsfragments/2129.bugfix.rst b/documentation/source/newsfragments/2129.bugfix.rst index 3434fd66..0778160e 100644 --- a/documentation/source/newsfragments/2129.bugfix.rst +++ b/documentation/source/newsfragments/2129.bugfix.rst @@ -1 +1 @@ -Fixed issue on Mac OS and Linux where negative integers were returned as large positive values. +Fixed an issue with VHPI on Mac OS and Linux where negative integers were returned as large positive values. From 8982c93014e997b00391c69780c8038c1bf73462 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 16 Oct 2020 18:24:07 -0500 Subject: [PATCH 2526/2656] Remove functional tests from Travis CI --- .travis.yml | 116 ---------------------------------------------------- 1 file changed, 116 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2f7d9eac..7f93f6d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,119 +1,11 @@ -# See Dockerfile for the full build instructions -sudo: required language: python dist: xenial -cache: - directories: - - iverilog git: quiet: true -services: - - docker - jobs: include: - - stage: Lint - name: whitespace - os: linux - script: "! git --no-pager grep --color --line-number --full-name --extended-regexp -I '\\s+$'" - - - stage: Lint - name: flake8 - os: linux - python: 3.5 - install: pip install flake8 - script: flake8 - - - stage: SimTests - os: linux - python: 3.5 - before_install: &docker_prep - - docker build -t cocotb . - - docker images -a - - docker run -d --name cocotb cocotb tail -f /dev/null - - docker ps -a - script: - - echo 'Running cocotb tests with Python 3.5 ...' && echo -en 'travis_fold:start:cocotb_py35' - - docker exec -it -e TRAVIS=true cocotb bash -lc 'pip3 install tox; cd /src; tox -e py35' - - echo 'travis_fold:end:cocotb_py35' - - - stage: SimTests - python: 3.7 - before_install: &travis_linx_prep - - sudo apt-get -y install gperf swig - - if [[ ! -e "./iverilog/README.txt" ]]; then rm -rf iverilog; git clone https://github.com/steveicarus/iverilog.git --depth=1 --branch v10_3; fi - - cd iverilog && autoconf && ./configure && make -j2 && sudo make install && cd .. - - pip install --upgrade tox virtualenv codecov - script: - - echo 'Running cocotb tests with Python 3.7 ...' && echo -en 'travis_fold:start:cocotb_py37' - - tox -e py37 - - echo 'travis_fold:end:cocotb_py37' - after_success: &travis_linux_post - - codecov - - - stage: SimTests - python: 3.8 - before_install: *travis_linx_prep - script: - - echo 'Running cocotb tests with Python 3.8 ...' && echo -en 'travis_fold:start:cocotb_py38' - - tox -e py38 - - echo 'travis_fold:end:cocotb_py38' - after_success: *travis_linux_post - - - stage: SimTests - python: 3.6.7 - before_install: *travis_linx_prep - script: - - echo 'Running cocotb tests with Python 3.6.7 ...' && echo -en 'travis_fold:start:cocotb_py36' - - tox -e py36 - - echo 'travis_fold:end:cocotb_py36' - after_success: *travis_linux_post - - - stage: SimTests - python: 3.9-dev - before_install: *travis_linx_prep - script: - - echo 'Running cocotb tests with Python 3.9-dev ...' && echo -en 'travis_fold:start:cocotb_py39' - - tox -e py39 - - echo 'travis_fold:end:cocotb_py39' - after_success: *travis_linux_post - - - stage: SimTests - os: osx - osx_image: xcode11.2 - language: shell - python: 3.7 - before_install: &osx_prep - - brew update-reset - - brew install icarus-verilog - - pip3 install tox - script: - - tox -e py37 - - - stage: SimTests - python: 3.7 - env: SIM=ghdl TOPLEVEL_LANG=vhdl - before_install: - - sudo apt-get install gnat - - git clone https://github.com/ghdl/ghdl.git --depth=1 --branch v0.36 - - cd ghdl && ./configure && make -j2 && sudo make install && cd .. - - pip install tox - script: - - tox -e py37 - - - stage: SimTests - python: 3.7 - name: "Py 3.7, Linux, Verilator devel" - env: SIM=verilator TOPLEVEL_LANG=verilog - before_install: - - sudo apt install autoconf make g++ bison flex libfl-dev - - git clone https://github.com/verilator/verilator --depth=1 verilator_devel - - cd verilator_devel && autoconf && ./configure && make -j2 && sudo make install && cd .. - - pip install tox - script: - - tox -e py37 - stage: DeployPyPI python: 3.7 @@ -128,11 +20,3 @@ jobs: tags: true repo: cocotb/cocotb distributions: sdist - - allow_failures: - - stage: SimTests - python: 3.7 - env: SIM=ghdl TOPLEVEL_LANG=vhdl - - env: ALLOW_FAIL=yes - - stage: SimTests - env: SIM=verilator TOPLEVEL_LANG=verilog From 6c951814b26aafbe138ad5fcae5d6559aab871ee Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 25 Jul 2020 23:53:55 -0500 Subject: [PATCH 2527/2656] Add functional tests with Github Actions The current set of tests in is more extensive than what exists on Travis CI. Simultaneously this decreases the CI run time by 3-4x. --- .github/workflows/regression-tests.yml | 281 ++++++++++++++++++++++--- tox.ini | 25 ++- 2 files changed, 269 insertions(+), 37 deletions(-) diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml index af064c62..738dc82b 100644 --- a/.github/workflows/regression-tests.yml +++ b/.github/workflows/regression-tests.yml @@ -11,65 +11,278 @@ on: jobs: + lint-whitespace: + runs-on: ubuntu-latest + name: whitespace + strategy: + fail-fast: false + steps: + - uses: actions/checkout@v2 + - name: whitespace + run: | + ! git --no-pager grep --color --line-number --full-name --extended-regexp -I '\\s+$' + + lint-flake8: + runs-on: ubuntu-latest + name: flake8 + strategy: + fail-fast: false + matrix: + python-version: [3.5, 3.9] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{matrix.python-version}} + uses: actions/setup-python@v2 + with: + python-version: ${{matrix.python-version}} + - name: flake8 + run: | + pip install flake8 + flake8 + tests: - name: ${{matrix.sim}} (${{matrix.sim-version}}) | ${{matrix.os}} | Python ${{matrix.python-version}} + + name: ${{matrix.extra_name}}${{matrix.sim}} (${{matrix.sim-version}}) | ${{matrix.os}} | Python ${{matrix.python-version}} ${{matrix.may_fail && '| May Fail' || ''}} runs-on: ${{matrix.os}} + env: + SIM: ${{matrix.sim}} + TOPLEVEL_LANG: ${{matrix.lang}} + CXX: ${{matrix.cxx || 'g++'}} + CC: ${{matrix.cc || 'gcc'}} + OS: ${{matrix.os}} + strategy: fail-fast: false matrix: include: - - sim-version: "v10_3" - sim: 'icarus' - lang: 'verilog' + + # Test different Python versions with package managed Icarus on Ubuntu + + - sim: icarus + sim-version: apt + lang: verilog + python-version: 3.5 + os: ubuntu-20.04 + + - sim: icarus + sim-version: apt + lang: verilog + python-version: 3.6 + os: ubuntu-20.04 + + - sim: icarus + sim-version: apt + lang: verilog + python-version: 3.7 + os: ubuntu-20.04 + + - sim: icarus + sim-version: apt + lang: verilog python-version: 3.8 - os: 'windows-latest' - - sim-version: "master" - sim: 'icarus' - lang: 'verilog' + os: ubuntu-20.04 + + - sim: icarus + sim-version: apt + lang: verilog + python-version: 3.9 + os: ubuntu-20.04 + + # Test Icarus dev on Ubuntu + + - sim: icarus + sim-version: master + lang: verilog python-version: 3.8 - os: 'windows-latest' + os: ubuntu-20.04 may_fail: true - env: - SIM: ${{matrix.sim}} - TOPLEVEL_LANG: ${{matrix.lang}} - PYTHON: ${{matrix.python-version}} - OS: ${{matrix.os}} + + # Test GHDL on Ubuntu + + - sim: ghdl + sim-version: apt + lang: vhdl + python-version: 3.8 + os: ubuntu-20.04 + may_fail: true + + - sim: ghdl + sim-version: nightly + lang: vhdl + python-version: 3.8 + os: ubuntu-latest + may_fail: true + + # Test Verilator on Ubuntu + + - sim: verilator + sim-version: apt + lang: verilog + python-version: 3.8 + os: ubuntu-20.04 + may_fail: true + + - sim: verilator + sim-version: master + lang: verilog + python-version: 3.8 + os: ubuntu-20.04 + may_fail: true + + # Test other OSes + + - sim: icarus # Icarus homebrew --HEAD + sim-version: homebrew-HEAD + lang: verilog + python-version: 3.8 + os: macos-latest + may_fail: true + + - sim: icarus # Icarus homebrew stable + sim-version: homebrew + lang: verilog + python-version: 3.8 + os: macos-latest + + - sim: icarus # Icarus windows master from source + sim-version: master + lang: verilog + python-version: 3.8 + os: windows-latest + may_fail: true + + - sim: icarus # Icarus windows 10.3 from source + sim-version: v10_3 + lang: verilog + python-version: 3.8 + os: windows-latest + + # Other + + - sim: icarus # use clang instead of gcc + sim-version: v10_3 + lang: verilog + python-version: 3.8 + os: ubuntu-latest + cxx: clang++ + cc: clang + extra_name: "clang | " + steps: - uses: actions/checkout@v2 - - name: Set up Anaconda ${{matrix.python-version}} + + # Install Python + - name: Set up Python ${{matrix.python-version}} + if: startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos') + uses: actions/setup-python@v2 + with: + python-version: ${{matrix.python-version}} + - name: Set up Anaconda ${{matrix.python-version}} (Windows) + if: startsWith(matrix.os, 'windows') uses: conda-incubator/setup-miniconda@v1 with: auto-update-conda: true python-version: ${{matrix.python-version}} - - name: Set up cocotb build dependencies + + # Install Icarus + - name: Set up Icarus (Ubuntu - apt) + if: startsWith(matrix.os, 'ubuntu') && matrix.sim == 'icarus' && matrix.sim-version == 'apt' + run: | + sudo apt install -y --no-install-recommends iverilog + - name: Set up Icarus (Ubuntu - source) + if: startsWith(matrix.os, 'ubuntu') && matrix.sim == 'icarus' && matrix.sim-version != 'apt' run: | - conda install -c msys2 m2-base m2-make m2-autoconf m2-flex m2-bison m2-gperf m2w64-toolchain libpython - - name: Build and install Icarus + sudo apt install -y --no-install-recommends g++ gperf flex bison make autoconf + git clone https://github.com/steveicarus/iverilog.git -b ${{matrix.sim-version}} + cd iverilog + bash ./autoconf.sh + bash ./configure + make -j $(nproc) + sudo make install + - name: Set up Icarus (Windows - source) + if: startsWith(matrix.os, 'windows') && matrix.sim == 'icarus' run: | - git clone https://github.com/steveicarus/iverilog.git -b "${{matrix.sim-version}}" + conda install -c msys2 m2-base m2-make m2-autoconf m2-flex m2-bison m2-gperf m2w64-toolchain + git clone https://github.com/steveicarus/iverilog.git -b ${{matrix.sim-version}} cd iverilog bash ./autoconf.sh bash ./configure --host=x86_64-w64-mingw32 --prefix=/c/iverilog make -j $(nproc) make install - echo "::add-path::C:\iverilog\bin" - - name: Install Python testing dependencies + echo "C:\iverilog\bin" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8 + - name: Set up Icarus (MacOS - homebrew --HEAD) + if: startsWith(matrix.os, 'macos') && matrix.sim == 'icarus' && matrix.sim-version == 'homebrew-HEAD' run: | - pip install coverage xunitparser pytest pytest-cov - - name: Install cocotb + brew install icarus-verilog --HEAD + - name: Set up Icarus (MacOS - homebrew) + if: startsWith(matrix.os, 'macos') && matrix.sim == 'icarus' && matrix.sim-version == 'homebrew' + run: | + brew install icarus-verilog + + # Install GHDL + - name: Set up GHDL (Ubuntu - apt) + if: startsWith(matrix.os, 'ubuntu') && matrix.sim == 'ghdl' && matrix.sim-version == 'apt' + run: | + sudo apt install -y --no-install-recommends ghdl-mcode ghdl + - name : Set up GHDL (Ubunutu - nightly) + if: startsWith(matrix.os, 'ubuntu') && matrix.sim == 'ghdl' && matrix.sim-version == 'nightly' + uses: ghdl/setup-ghdl-ci@nightly + with: + backend: mcode + + # Install Verilator + - name: Set up Verilator (Ubuntu - apt) + if: startsWith(matrix.os, 'ubuntu') && matrix.sim == 'verilator' && matrix.sim-version == 'apt' run: | - python -m pip install --global-option build_ext --global-option --compiler=mingw32 -e . - - name: Test + sudo apt install -y --no-install-recommends verilator + - name: Set up Verilator (Ubunutu - source) + if: startsWith(matrix.os, 'ubuntu') && matrix.sim == 'verilator' && matrix.sim-version != 'apt' + run: | + sudo apt install -y --no-install-recommends make g++ perl python3 autoconf flex bison libfl2 libfl-dev zlibc zlib1g zlib1g-dev + git clone https://github.com/verilator/verilator.git -b ${{matrix.sim-version}} + cd verilator + autoconf + ./configure + make -j $(nproc) + sudo make install + + # Windows Testing + - name: Install cocotb build dependencies (Windows) + if: startsWith(matrix.os, 'windows') + run: conda install --yes -c msys2 m2-base m2-make m2w64-toolchain libpython + - name: Install cocotb (Windows) + if: startsWith(matrix.os, 'windows') + run: python -m pip install --global-option build_ext --global-option --compiler=mingw32 -e . + - name: Install Python testing dependencies (Windows) + run: pip install coverage xunitparser pytest pytest-cov + - name: Test (Windows) + if: startsWith(matrix.os, 'windows') continue-on-error: ${{matrix.may_fail || false}} + timeout-minutes: 15 run: | pytest - $pass = $? make test - exit -not ($pass -and $?) - # inverted the pass for exit because the boolean is converted to 0/1, but an exit code of 1 is a failure - - name: Upload coverage - if: steps.Test.outcome != 'failure' - run: | - pip install codecov - bash -c 'find . -type f -name "\.coverage\.cocotb" | xargs coverage combine --append' - codecov + + # Ubuntu / MacOS Testing + - name: Install cocotb build dependencies (Ubuntu - g++) + if: startsWith(matrix.os, 'ubuntu') && (!matrix.cxx || matrix.cxx == 'g++') + run: | + sudo apt install g++ + - name: Install cocotb build dependencies (Ubuntu - clang++) + if: startsWith(matrix.os, 'ubuntu') && matrix.cxx == 'clang++' + run: | + sudo apt install clang + - name: Install cocotb build dependencies (MacOS) + if: startsWith(matrix.os, 'macos') + run: | + g++ --version + - name: Install Python testing dependencies (Ubuntu, MacOS) + if: startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos') + run: | + pip install tox tox-gh-actions + - name: Test (Ubuntu, MacOS) + if: startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos') + continue-on-error: ${{matrix.may_fail || false}} + timeout-minutes: 15 + run: | + tox diff --git a/tox.ini b/tox.ini index 65b390c5..ebd1414a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py3 +envlist = py{35,36,37,38,39}-{linux,macos,windows} # for the requires key minversion = 3.2.0 # virtualenv is used by tox; versions below 16.1.0 cause a DeprecationWarning @@ -9,7 +9,7 @@ requires = virtualenv >= 16.1 [testenv] setenv = - CFLAGS = -Werror + CFLAGS = -Werror -Wno-deprecated-declarations CXXFLAGS = -Werror COCOTB_LIBRARY_COVERAGE = 1 passenv = @@ -37,7 +37,7 @@ whitelist_externals = # needed for coverage to work usedevelop=True -[testenv:py3_mingw] +[testenv:py{35,36,37,38,39}-windows] install_command = python -m pip install --global-option build_ext --global-option --compiler=mingw32 {opts} {packages} @@ -50,3 +50,22 @@ description = invoke sphinx-build to build the HTML docs deps = -rdocumentation/requirements.txt commands = sphinx-build -d "{toxworkdir}/docs_doctree" ./documentation/source "{toxworkdir}/docs_out" --color -bhtml {posargs} python -c 'import pathlib; print("documentation available under file://\{0\}".format(pathlib.Path(r"{toxworkdir}") / "docs_out" / "index.html"))' + +[gh-actions] +python = + 3.5: py35 + 3.6: py36 + 3.7: py37 + 3.8: py38 + 3.9: py39 + +[gh-actions:env] +OS = + ubuntu-latest: linux + ubuntu-20.04: linux + ubuntu-18.04: linux + ubuntu-16.04: linux + macos-latest: macos + macos-10.15: macos + windows-latest: windows + windows-2019: windows From 49668772bc75722f97825fabda09f0ae8078a4b0 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 8 Oct 2020 20:35:24 +0200 Subject: [PATCH 2528/2656] Show Timer object reuse instead of re-creation --- documentation/source/coroutines.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/documentation/source/coroutines.rst b/documentation/source/coroutines.rst index 855eaef0..aace9713 100644 --- a/documentation/source/coroutines.rst +++ b/documentation/source/coroutines.rst @@ -164,9 +164,10 @@ including triggers like :class:`~cocotb.triggers.Timer`. @cocotb.coroutine def simple_clock(signal, half_period, half_period_units): signal <= 0 + timer = Timer(half_period, half_period_units) while True: # in generator-based coroutines triggers are yielded - yield Timer(half_period, half_period_units) + yield timer signal <= ~signal Likewise, any place that will accept async coroutines will also accept generator-based coroutines; From 7fb7b624c57726abf20d1bd90e7000f8739fe0c0 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Mon, 19 Oct 2020 12:27:33 -0700 Subject: [PATCH 2529/2656] Prevent warnings from collection of TestFactories by unit testing frameworks. --- cocotb/regression.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cocotb/regression.py b/cocotb/regression.py index d5420b90..0595e963 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -654,6 +654,9 @@ class TestFactory: the test description) includes the name and description of each generator. """ + # Prevent warnings from collection of TestFactories by unit testing frameworks. + __test__ = False + def __init__(self, test_function, *args, **kwargs): if sys.version_info > (3, 6) and inspect.isasyncgenfunction(test_function): raise TypeError("Expected a coroutine function, but got the async generator '{}'. " From 2258cad8f5b8fbb3453217f2dd86a2e7f9a339cf Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 19 Aug 2020 08:01:28 -0500 Subject: [PATCH 2530/2656] Add type annotations to cocotb.fork --- cocotb/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cocotb/__init__.py b/cocotb/__init__.py index dc0595e8..424d75f6 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -38,12 +38,13 @@ import time import warnings from typing import Dict, List, Union +from collections.abc import Coroutine import cocotb.handle import cocotb.log from cocotb.scheduler import Scheduler from cocotb.regression import RegressionManager - +from cocotb.decorators import RunningTask # Things we want in the cocotb namespace from cocotb.decorators import test, coroutine, hook, function, external # noqa: F401 @@ -129,7 +130,7 @@ def _reopen_stream_with_buffering(stream_name): """ used for cocotb library coverage """ -def fork(coro): +def fork(coro: Union[RunningTask, Coroutine]) -> RunningTask: """ Schedule a coroutine to be run concurrently. See :ref:`coroutines` for details on its use. """ return scheduler.add(coro) From 07ed4be7e14d2b093d560e7fd129d2c5d86b5e44 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 19 Aug 2020 19:48:14 -0500 Subject: [PATCH 2531/2656] Fix PR number on newsfragment --- .../source/newsfragments/{1593.feature.rst => 2006.feature.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename documentation/source/newsfragments/{1593.feature.rst => 2006.feature.rst} (100%) diff --git a/documentation/source/newsfragments/1593.feature.rst b/documentation/source/newsfragments/2006.feature.rst similarity index 100% rename from documentation/source/newsfragments/1593.feature.rst rename to documentation/source/newsfragments/2006.feature.rst From 9d577f0d84fbbae80970255c8a31c9c713ce1b92 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 19 Aug 2020 08:00:53 -0500 Subject: [PATCH 2532/2656] Implement start_soon start_soon behaves like fork, but instead of scheduling the coroutine immediately, it schedules the coroutine to start after the current coroutine yields control. --- cocotb/scheduler.py | 68 +++++++++++++------ .../source/newsfragments/2023.feature.rst | 2 + .../test_cases/test_cocotb/test_scheduler.py | 31 +++++++++ 3 files changed, 79 insertions(+), 22 deletions(-) create mode 100644 documentation/source/newsfragments/2023.feature.rst diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index ab9d9a71..1846e31e 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -39,6 +39,8 @@ import logging import threading import inspect +from typing import Any, Union +from collections.abc import Coroutine # Debug mode controlled by environment variables if "COCOTB_ENABLE_PROFILING" in os.environ: @@ -59,6 +61,7 @@ import cocotb import cocotb.decorators +from cocotb.decorators import RunningTask from cocotb.triggers import (Trigger, GPITrigger, Timer, ReadOnly, NextTimeStep, ReadWrite, Event, Join, NullTrigger) from cocotb.log import SimLog @@ -628,19 +631,19 @@ async def wrapper(): return wrapper() - def add(self, coroutine): - """Add a new coroutine. - - Just a wrapper around self.schedule which provides some debug and - useful error messages in the event of common gotchas. - """ + @staticmethod + def create_task(coroutine: Any) -> RunningTask: + """ Checks to see if the given object is a schedulable coroutine object """ + if isinstance(coroutine, RunningTask): + return coroutine + if inspect.iscoroutine(coroutine): + return RunningTask(coroutine) if inspect.iscoroutinefunction(coroutine): raise TypeError( "Coroutine function {} should be called prior to being " "scheduled." .format(coroutine)) - if isinstance(coroutine, cocotb.decorators.coroutine): raise TypeError( "Attempt to schedule a coroutine that hasn't started: {}.\n" @@ -648,30 +651,51 @@ def add(self, coroutine): "decorator?" .format(coroutine) ) - - if inspect.iscoroutine(coroutine): - return self.add(cocotb.decorators.RunningTask(coroutine)) - - elif sys.version_info >= (3, 6) and inspect.isasyncgen(coroutine): + if sys.version_info >= (3, 6) and inspect.isasyncgen(coroutine): raise TypeError( "{} is an async generator, not a coroutine. " "You likely used the yield keyword instead of await.".format( coroutine.__qualname__)) + raise TypeError( + "Attempt to add a object of type {} to the scheduler, which " + "isn't a coroutine: {!r}\n" + "Did you forget to use the @cocotb.coroutine decorator?" + .format(type(coroutine), coroutine) + ) - elif not isinstance(coroutine, cocotb.decorators.RunningTask): - raise TypeError( - "Attempt to add a object of type {} to the scheduler, which " - "isn't a coroutine: {!r}\n" - "Did you forget to use the @cocotb.coroutine decorator?" - .format(type(coroutine), coroutine) - ) + def add(self, coroutine: Union[RunningTask, Coroutine]) -> RunningTask: + """Add a new coroutine. + + Just a wrapper around self.schedule which provides some debug and + useful error messages in the event of common gotchas. + """ + + task = self.create_task(coroutine) if _debug: - self.log.debug("Adding new coroutine %s" % coroutine._coro.__qualname__) + self.log.debug("Adding new coroutine %s" % task._coro.__qualname__) - self.schedule(coroutine) + self.schedule(task) self._check_termination() - return coroutine + return task + + def start_soon(self, coro: Union[Coroutine, RunningTask]) -> RunningTask: + """ + Schedule a coroutine to be run concurrently, starting after the current coroutine yields control. + + :func:`~cocotb.fork` starts the given coroutine immediately. This function + starts the given coroutine only after the current coroutine yields control. + This is useful when the forked coroutine has logic before the first + :keyword:`await` that may not be safe to execute immediately. + """ + + task = self.create_task(coro) + + if _debug: + self.log.debug("queueing a new coroutine %s" % task._coro.__qualname__) + + self.queue(task) + return task def add_test(self, test_coro): """Called by the regression manager to queue the next test""" diff --git a/documentation/source/newsfragments/2023.feature.rst b/documentation/source/newsfragments/2023.feature.rst new file mode 100644 index 00000000..935ff839 --- /dev/null +++ b/documentation/source/newsfragments/2023.feature.rst @@ -0,0 +1,2 @@ +Added :func:`cocotb.scheduler.start_soon` which schedules a coroutine to start *after* the current coroutine yields control. +This behavior is distinct from :func:`cocotb.fork` which schedules the given coroutine immediately. diff --git a/tests/test_cases/test_cocotb/test_scheduler.py b/tests/test_cases/test_cocotb/test_scheduler.py index 44e21efe..30a0cc72 100644 --- a/tests/test_cases/test_cocotb/test_scheduler.py +++ b/tests/test_cases/test_cocotb/test_scheduler.py @@ -405,3 +405,34 @@ async def coroutine_timer(): # Trigger.__await__ should be popped from the coroutine stack log.info(repr(coro_task)) assert re.match(r">", repr(coro_task)) + + +@cocotb.test() +async def test_start_soon_async(_): + """ Tests start_soon works with coroutines """ + a = 0 + + async def example(): + nonlocal a + a = 1 + + cocotb.scheduler.start_soon(example()) + assert a == 0 + await NullTrigger() + assert a == 1 + + +@cocotb.test() +async def test_start_soon_decorator(_): + """ Tests start_soon works with RunningTasks """ + a = 0 + + @cocotb.coroutine + async def example(): + nonlocal a + a = 1 + + cocotb.scheduler.start_soon(example()) + assert a == 0 + await NullTrigger() + assert a == 1 From 8e63985ffd460b6e90434e0b8ca96fad102745e1 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Tue, 13 Oct 2020 08:43:35 -0500 Subject: [PATCH 2533/2656] Apply suggestions from code review Co-authored-by: Colin Marquardt --- cocotb/scheduler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 1846e31e..c65e7c4f 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -683,7 +683,7 @@ def start_soon(self, coro: Union[Coroutine, RunningTask]) -> RunningTask: """ Schedule a coroutine to be run concurrently, starting after the current coroutine yields control. - :func:`~cocotb.fork` starts the given coroutine immediately. This function + In contrast to :func:`~cocotb.fork` which starts the given coroutine immediately, this function starts the given coroutine only after the current coroutine yields control. This is useful when the forked coroutine has logic before the first :keyword:`await` that may not be safe to execute immediately. @@ -692,7 +692,7 @@ def start_soon(self, coro: Union[Coroutine, RunningTask]) -> RunningTask: task = self.create_task(coro) if _debug: - self.log.debug("queueing a new coroutine %s" % task._coro.__qualname__) + self.log.debug("Queueing a new coroutine %s" % task._coro.__qualname__) self.queue(task) return task From ea5b4de59a6a9bc806c699e43f215ffc207d18b1 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Tue, 13 Oct 2020 09:56:35 -0500 Subject: [PATCH 2534/2656] Apply suggestions from code review Co-authored-by: Colin Marquardt --- cocotb/scheduler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index c65e7c4f..a6dfa371 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -633,7 +633,7 @@ async def wrapper(): @staticmethod def create_task(coroutine: Any) -> RunningTask: - """ Checks to see if the given object is a schedulable coroutine object """ + """ Checks to see if the given object is a schedulable coroutine object and if so, returns it """ if isinstance(coroutine, RunningTask): return coroutine @@ -685,7 +685,7 @@ def start_soon(self, coro: Union[Coroutine, RunningTask]) -> RunningTask: In contrast to :func:`~cocotb.fork` which starts the given coroutine immediately, this function starts the given coroutine only after the current coroutine yields control. - This is useful when the forked coroutine has logic before the first + This is useful when the coroutine to be forked has logic before the first :keyword:`await` that may not be safe to execute immediately. """ From 21f2ce48c5e6e30f4b2abe1859628e7ed2d37d07 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Tue, 13 Oct 2020 14:18:50 -0500 Subject: [PATCH 2535/2656] Apply suggestions from code review Co-authored-by: Colin Marquardt --- cocotb/scheduler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index a6dfa371..5e9ab15d 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -657,7 +657,7 @@ def create_task(coroutine: Any) -> RunningTask: "You likely used the yield keyword instead of await.".format( coroutine.__qualname__)) raise TypeError( - "Attempt to add a object of type {} to the scheduler, which " + "Attempt to add an object of type {} to the scheduler, which " "isn't a coroutine: {!r}\n" "Did you forget to use the @cocotb.coroutine decorator?" .format(type(coroutine), coroutine) From cad71a250a95e11a667dd066534544a94182a4ff Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 30 Sep 2020 00:12:56 -0500 Subject: [PATCH 2536/2656] Add build system requirements to pyproject.toml Specifying 'setuptools.build_meta' changes the build backend from the legacy backend to the new backend. The new backend does not create an sdist first, so MANIFEST.in isn't respected when building the build environment. We have to add the path to setup.py to sys.path so we can import cocotb_build_libs.py that exists beside it. --- documentation/source/newsfragments/2091.changes.rst | 1 + pyproject.toml | 4 ++++ setup.py | 2 ++ 3 files changed, 7 insertions(+) create mode 100644 documentation/source/newsfragments/2091.changes.rst diff --git a/documentation/source/newsfragments/2091.changes.rst b/documentation/source/newsfragments/2091.changes.rst new file mode 100644 index 00000000..c9b1567c --- /dev/null +++ b/documentation/source/newsfragments/2091.changes.rst @@ -0,0 +1 @@ +The package build process is now fully PEP517 compliant. diff --git a/pyproject.toml b/pyproject.toml index bc046f5e..325abbb7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,7 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" + [tool.towncrier] package = "cocotb" directory = "documentation/source/newsfragments" diff --git a/setup.py b/setup.py index d0c84998..ec46987c 100755 --- a/setup.py +++ b/setup.py @@ -55,6 +55,8 @@ # Note: cocotb is not installed properly yet and is missing dependencies and binaries # We can still import other files next to setup.py, as long as they're in MANIFEST.in +# The below line is necessary for PEP517 support +sys.path.append(path.dirname(__file__)) from cocotb_build_libs import get_ext, build_ext From 02fd320c701b56fb2cb22cb781c4659344e42748 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 30 Sep 2020 00:18:55 -0500 Subject: [PATCH 2537/2656] Remove unsupported pytest option causing warnings --- setup.cfg | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 3cf66c22..d182658d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -30,6 +30,5 @@ per-file-ignores = [tool:pytest] addopts = -v --cov=cocotb --cov-branch testpaths = tests/pytest -confcutdir = tests/pytest # log_cli = true # log_cli_level = DEBUG From ae90c1b5e7c54ff9becdc82328792cd540c0f5eb Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Tue, 20 Oct 2020 18:38:19 +0100 Subject: [PATCH 2538/2656] Make Marlon maintainer We are happy that Marlon has accepted the invitation to become cocotb maintainer. This commit adds him to the list. Thank you Marlon for your great work so far, and we're looking forward to more of it! --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1f250d1d..c7278158 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -175,6 +175,7 @@ Most maintainers are experts in part of the cocotb codebase, and are primarily r - Kaleb Barrett (@ktbarrett) - Tomasz Hemperek (@themperek) +- Marlon James (@garmin-mjames) - Colin Marquardt (@cmarqu) - Philipp Wagner (@imphil) - Eric Wieser (@eric-wieser) From 66293c5875d65a98ba8e6bc005f15962059c0b29 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 20 Oct 2020 20:29:46 +0200 Subject: [PATCH 2539/2656] Remove shebang line and executable flag from test files --- tests/test_cases/test_avalon/test_avalon.py | 2 -- tests/test_cases/test_avalon_stream/test_avalon_stream.py | 1 - tests/test_cases/test_closedown/test_closedown.py | 2 -- tests/test_cases/test_external/test_external.py | 2 -- 4 files changed, 7 deletions(-) mode change 100755 => 100644 tests/test_cases/test_avalon/test_avalon.py mode change 100755 => 100644 tests/test_cases/test_avalon_stream/test_avalon_stream.py mode change 100755 => 100644 tests/test_cases/test_external/test_external.py diff --git a/tests/test_cases/test_avalon/test_avalon.py b/tests/test_cases/test_avalon/test_avalon.py old mode 100755 new mode 100644 index 7e32e4d2..9ccf39de --- a/tests/test_cases/test_avalon/test_avalon.py +++ b/tests/test_cases/test_avalon/test_avalon.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - # Copyright (c) 2013, 2018 Potential Ventures Ltd # Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. diff --git a/tests/test_cases/test_avalon_stream/test_avalon_stream.py b/tests/test_cases/test_avalon_stream/test_avalon_stream.py old mode 100755 new mode 100644 index 294b599a..684aa28f --- a/tests/test_cases/test_avalon_stream/test_avalon_stream.py +++ b/tests/test_cases/test_avalon_stream/test_avalon_stream.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python """Test to demonstrate functionality of the avalon basic streaming interface""" import random diff --git a/tests/test_cases/test_closedown/test_closedown.py b/tests/test_cases/test_closedown/test_closedown.py index 750f98ab..94fdb7ea 100644 --- a/tests/test_cases/test_closedown/test_closedown.py +++ b/tests/test_cases/test_closedown/test_closedown.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - # Copyright (c) 2013 Potential Ventures Ltd # Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py old mode 100755 new mode 100644 index 4b47245a..ff8b86d7 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - # Copyright (c) 2013, 2018 Potential Ventures Ltd # Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. From 3fe93af0db635b32cc1579932f9735bdf6fa26b7 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 20 Oct 2020 20:40:37 +0200 Subject: [PATCH 2540/2656] Document that only VPI implements drivers/loads --- cocotb/handle.py | 6 ++++-- cocotb/share/lib/fli/FliImpl.cpp | 8 +++++++- cocotb/share/lib/vhpi/VhpiImpl.cpp | 8 +++++++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 25714e35..a53c25cd 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -626,14 +626,16 @@ class NonConstantObject(NonHierarchyIndexableObject): def drivers(self): """An iterator for gathering all drivers for a signal. - Few simulators implement this. + This is currently only available for VPI. + Also, only a few simulators implement this. """ return self._handle.iterate(simulator.DRIVERS) def loads(self): """An iterator for gathering all loads on a signal. - Few simulators implement this. + This is currently only available for VPI. + Also, only a few simulators implement this. """ return self._handle.iterate(simulator.LOADS) diff --git a/cocotb/share/lib/fli/FliImpl.cpp b/cocotb/share/lib/fli/FliImpl.cpp index 84f25301..3a17f741 100644 --- a/cocotb/share/lib/fli/FliImpl.cpp +++ b/cocotb/share/lib/fli/FliImpl.cpp @@ -568,8 +568,14 @@ GpiIterator *FliImpl::iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type case GPI_OBJECTS: new_iter = new FliIterator(this, obj_hdl); break; + case GPI_DRIVERS: + LOG_WARN("FLI: Drivers iterator not implemented yet"); + break; + case GPI_LOADS: + LOG_WARN("FLI: Loads iterator not implemented yet"); + break; default: - LOG_WARN("Other iterator types not implemented yet"); + LOG_WARN("FLI: Other iterator types not implemented yet"); break; } diff --git a/cocotb/share/lib/vhpi/VhpiImpl.cpp b/cocotb/share/lib/vhpi/VhpiImpl.cpp index 146fe146..84509516 100644 --- a/cocotb/share/lib/vhpi/VhpiImpl.cpp +++ b/cocotb/share/lib/vhpi/VhpiImpl.cpp @@ -869,8 +869,14 @@ GpiIterator *VhpiImpl::iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t typ case GPI_OBJECTS: new_iter = new VhpiIterator(this, obj_hdl); break; + case GPI_DRIVERS: + LOG_WARN("VHPI: Drivers iterator not implemented yet"); + break; + case GPI_LOADS: + LOG_WARN("VHPI: Loads iterator not implemented yet"); + break; default: - LOG_WARN("Other iterator types not implemented yet"); + LOG_WARN("VHPI: Other iterator types not implemented yet"); break; } return new_iter; From d55c5679d43822b97e8309a57ea812cc9da70d93 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 15 Oct 2020 22:13:05 +0200 Subject: [PATCH 2541/2656] Convert some but not all tests to async/await syntax Refs #1731 --- tests/test_cases/test_avalon/test_avalon.py | 27 ++-- .../test_avalon_stream/test_avalon_stream.py | 24 ++-- .../test_closedown/test_closedown.py | 4 +- tests/test_cases/test_compare/test_compare.py | 23 ++-- .../test_configuration/test_configurations.py | 6 +- .../test_discovery/test_discovery.py | 123 +++++------------- .../test_discovery/test_vhdl_block.py | 4 +- .../test_discovery/test_vhdl_indexed_name.py | 4 +- tests/test_cases/test_exit_error/test_exit.py | 4 +- .../test_iteration_es.py | 13 +- .../test_cocotb_array.py | 114 ++++++++-------- tests/test_cases/test_plusargs/plusargs.py | 11 +- .../test_verilog_access.py | 4 +- .../test_vhdl_access/test_vhdl_access.py | 10 +- 14 files changed, 148 insertions(+), 223 deletions(-) diff --git a/tests/test_cases/test_avalon/test_avalon.py b/tests/test_cases/test_avalon/test_avalon.py index 9ccf39de..290acbc8 100644 --- a/tests/test_cases/test_avalon/test_avalon.py +++ b/tests/test_cases/test_avalon/test_avalon.py @@ -55,10 +55,9 @@ def __init__(self, dut, avlproperties={}): readlatency_min=0, avl_properties=avlproperties) - @cocotb.coroutine - def init_sig(self, burstcount_w, address): + async def init_sig(self, burstcount_w, address): """ Initialize all signals """ - yield Timer(10) + await Timer(1, "ns") self.dut.reset = 0 self.dut.user_read_buffer = 0 self.dut.control_read_base = address @@ -68,29 +67,29 @@ def init_sig(self, burstcount_w, address): self.dut.master_waitrequest = 0 -@cocotb.test(expect_fail=False) -def test_burst_read(dut): +@cocotb.test() +async def test_burst_read(dut): """ Testing burst read """ wordburstcount = 16 address = 10*wordburstcount bart = BurstAvlReadTest(dut, {"readLatency": 10}) - yield bart.init_sig(wordburstcount, address) - yield Timer(100) + await bart.init_sig(wordburstcount, address) + await Timer(100, "ns") # Begin master burst read dut.control_go = 1 - yield Timer(10) + await Timer(10, "ns") dut.control_go = 0 - yield Timer(200) + await Timer(200, "ns") # read back values dut.user_read_buffer = 1 - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) read_mem = {} databuswidthB = len(dut.master_byteenable) burst = 0 while dut.user_data_available == 1: - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) value = dut.user_buffer_data.value for i in range(databuswidthB): read_mem[address + burst*databuswidthB + i] = \ @@ -98,7 +97,7 @@ def test_burst_read(dut): burst += 1 dut.user_read_buffer = 0 - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) print(str(read_mem)) print(str(len(read_mem)) + " 8bits values read") @@ -116,6 +115,6 @@ def test_burst_read(dut): hex(value) + " must be " + memdictvalue) - yield Timer(10) + await Timer(1, "ns") dut.user_read_buffer = 0 - yield Timer(10) + await Timer(1, "ns") diff --git a/tests/test_cases/test_avalon_stream/test_avalon_stream.py b/tests/test_cases/test_avalon_stream/test_avalon_stream.py index 684aa28f..be0c6f65 100644 --- a/tests/test_cases/test_avalon_stream/test_avalon_stream.py +++ b/tests/test_cases/test_avalon_stream/test_avalon_stream.py @@ -36,36 +36,34 @@ def __init__(self, dut): self.backpressure = BitDriver(self.dut.aso_ready, self.dut.clk) - @cocotb.coroutine - def initialise(self): + async def initialise(self): self.dut.reset <= 0 cocotb.fork(Clock(self.dut.clk, 10).start()) for _ in range(3): - yield self.clkedge + await self.clkedge self.dut.reset <= 1 - yield self.clkedge + await self.clkedge - @cocotb.coroutine - def send_data(self, data): + async def send_data(self, data): exp_data = struct.pack("B",data) self.expected_output.append(exp_data) - yield self.stream_in.send(data) + await self.stream_in.send(data) -@cocotb.test(expect_fail=False) -def test_avalon_stream(dut): +@cocotb.test() +async def test_avalon_stream(dut): """Test stream of avalon data""" tb = AvalonSTTB(dut) - yield tb.initialise() + await tb.initialise() tb.backpressure.start(wave()) for _ in range(20): data = random.randint(0, (2**7)-1) - yield tb.send_data(data) - yield tb.clkedge + await tb.send_data(data) + await tb.clkedge for _ in range(5): - yield tb.clkedge + await tb.clkedge raise tb.scoreboard.result diff --git a/tests/test_cases/test_closedown/test_closedown.py b/tests/test_cases/test_closedown/test_closedown.py index 94fdb7ea..57a47ba0 100644 --- a/tests/test_cases/test_closedown/test_closedown.py +++ b/tests/test_cases/test_closedown/test_closedown.py @@ -30,11 +30,11 @@ from cocotb.clock import Clock -def test_read(dut): +async def test_read(dut): global test_count dut._log.info("Inside test_read") while test_count != 5: - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) test_count += 1 diff --git a/tests/test_cases/test_compare/test_compare.py b/tests/test_cases/test_compare/test_compare.py index d430efc8..e53f17cf 100644 --- a/tests/test_cases/test_compare/test_compare.py +++ b/tests/test_cases/test_compare/test_compare.py @@ -17,25 +17,24 @@ def __init__(self, dut): self.dut = dut self.clkedge = RisingEdge(dut.clk) - @cocotb.coroutine - def initialise(self): + async def initialise(self): """Initalise the testbench""" cocotb.fork(Clock(self.dut.clk, 10).start()) self.dut.reset <= 0 for _ in range(2): - yield self.clkedge + await self.clkedge self.dut.reset <= 1 for _ in range(3): - yield self.clkedge + await self.clkedge @cocotb.test() -def test_compare_simhandlebase(dut): +async def test_compare_simhandlebase(dut): """Test for SimHandleBase comparisons""" tb = Testbench(dut) - yield tb.initialise() + await tb.initialise() for _ in range(3): - yield tb.clkedge + await tb.clkedge # Want to check the __eq__ comparator in SimHandleBase # (overridden in NonHierarchyObject) @@ -56,12 +55,12 @@ def test_compare_simhandlebase(dut): @cocotb.test() -def test_compare_nonhierarchy(dut): +async def test_compare_nonhierarchy(dut): """Test for NonHierarchyObject comparisons""" tb = Testbench(dut) - yield tb.initialise() + await tb.initialise() for _ in range(3): - yield tb.clkedge + await tb.clkedge # Check that all these signals are NonHierarchyObject children assert isinstance(dut.counter_plus_two, NonHierarchyObject) @@ -78,9 +77,9 @@ def test_compare_nonhierarchy(dut): assert dut.clk != dut.i_module_a.clk # A handle and a value # Because one is a value, it is compared against the value of the handle - yield tb.clkedge + await tb.clkedge assert dut.clk == 1 assert dut.clk != 0 - yield FallingEdge(tb.dut.clk) + await FallingEdge(tb.dut.clk) assert dut.clk == 0 assert dut.clk != 1 diff --git a/tests/test_cases/test_configuration/test_configurations.py b/tests/test_cases/test_configuration/test_configurations.py index d9008c88..f21e3cdd 100644 --- a/tests/test_cases/test_configuration/test_configurations.py +++ b/tests/test_cases/test_configuration/test_configurations.py @@ -34,7 +34,7 @@ def sub_iterate(unit): @cocotb.test() -def iterate(dut): - yield Timer(100) +async def iterate(dut): + await Timer(1, "ns") sub_iterate(dut) - yield Timer(100) + await Timer(1, "ns") diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index b8d30705..1990ec12 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -27,8 +27,6 @@ import cocotb import logging -import os -import textwrap from cocotb.triggers import Timer from cocotb.result import TestError, TestFailure from cocotb.handle import IntegerObject, ConstantObject, HierarchyObject, StringObject @@ -49,9 +47,8 @@ async def pseudo_region_access(dut): @cocotb.test() -def recursive_discover(dut): +async def recursive_discover(dut): """Discover absolutely everything in the DUT""" - yield Timer(0) def _discover(obj): for thing in obj: @@ -61,9 +58,8 @@ def _discover(obj): @cocotb.test() -def discover_module_values(dut): +async def discover_module_values(dut): """Discover everything in the DUT""" - yield Timer(0) count = 0 for thing in dut: thing._log.info("Found something: %s" % thing._fullname) @@ -72,49 +68,21 @@ def discover_module_values(dut): raise TestFailure("Expected to discover things in the DUT") -@cocotb.test(skip=True) -def ipython_embed(dut): - yield Timer(0) - import IPython - IPython.embed() - - -@cocotb.test(skip=True) -def ipython_embed_kernel(dut): - """Start an interactive Python shell.""" - yield Timer(0) - import IPython - print(textwrap.dedent(""" - ############################################################################### - Running IPython embed_kernel() - - You can now send this process into the background with "Ctrl-Z bg" and run - jupyter console --existing - or - jupyter qtconsole --existing - or - jupyter console --existing kernel-{}.json - ###############################################################################""".format(os.getpid()))) - IPython.embed_kernel() - - @cocotb.test(expect_error=True) -def discover_value_not_in_dut(dut): +async def discover_value_not_in_dut(dut): """Try and get a value from the DUT that is not there""" - yield Timer(0) fake_signal = dut.fake_signal - yield Timer(0) @cocotb.test() -def access_signal(dut): +async def access_signal(dut): """Access a signal using the assignment mechanism""" tlog = logging.getLogger("cocotb.test") signal = dut.stream_in_data tlog.info("Signal is %s" % type(signal)) dut.stream_in_data.setimmediatevalue(1) - yield Timer(10) + await Timer(1, "ns") if dut.stream_in_data.value.integer != 1: raise TestError("%s.%s != %d" % (dut.stream_in_data._path, @@ -125,14 +93,14 @@ def access_signal(dut): # Icarus 10.3 doesn't support bit-selects, see https://github.com/steveicarus/iverilog/issues/323 expect_error=IndexError if cocotb.SIM_NAME.lower().startswith("icarus") else False, skip=cocotb.LANGUAGE in ["vhdl"]) -def access_single_bit(dut): +async def access_single_bit(dut): """Access a single bit in a vector of the DUT""" dut.stream_in_data <= 0 - yield Timer(10) + await Timer(1, "ns") dut._log.info("%s = %d bits" % (dut.stream_in_data._path, len(dut.stream_in_data))) dut.stream_in_data[2] <= 1 - yield Timer(10) + await Timer(1, "ns") if dut.stream_out_data_comb.value.integer != (1 << 2): raise TestError("%s.%s != %d" % (dut.stream_out_data_comb._path, @@ -143,14 +111,14 @@ def access_single_bit(dut): # Icarus 10.3 doesn't support bit-selects, see https://github.com/steveicarus/iverilog/issues/323 expect_error=IndexError if cocotb.SIM_NAME.lower().startswith("icarus") else False, skip=cocotb.LANGUAGE in ["vhdl"]) -def access_single_bit_assignment(dut): +async def access_single_bit_assignment(dut): """Access a single bit in a vector of the DUT using the assignment mechanism""" dut.stream_in_data = 0 - yield Timer(10) + await Timer(1, "ns") dut._log.info("%s = %d bits" % (dut.stream_in_data._path, len(dut.stream_in_data))) dut.stream_in_data[2] = 1 - yield Timer(10) + await Timer(1, "ns") if dut.stream_out_data_comb.value.integer != (1 << 2): raise TestError("%s.%s != %d" % (dut.stream_out_data_comb._path, @@ -158,23 +126,20 @@ def access_single_bit_assignment(dut): @cocotb.test(expect_error=True) -def access_single_bit_erroneous(dut): +async def access_single_bit_erroneous(dut): """Access a non-existent single bit""" - yield Timer(10) dut._log.info("%s = %d bits" % (dut.stream_in_data._path, len(dut.stream_in_data))) bit = len(dut.stream_in_data) + 4 dut.stream_in_data[bit] <= 1 - yield Timer(10) @cocotb.test(expect_error=cocotb.SIM_NAME.lower().startswith(("icarus", "chronologic simulation vcs")), expect_fail=cocotb.SIM_NAME.lower().startswith(("riviera")) and cocotb.LANGUAGE in ["verilog"]) -def access_integer(dut): +async def access_integer(dut): """Integer should show as an IntegerObject""" bitfail = False tlog = logging.getLogger("cocotb.test") - yield Timer(10) test_int = dut.stream_in_int if not isinstance(test_int, IntegerObject): raise TestFailure("dut.stream_in_int is not an integer but {} instead".format(type(test_int))) @@ -194,20 +159,17 @@ def access_integer(dut): @cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) -def access_ulogic(dut): +async def access_ulogic(dut): """Access a std_ulogic as enum""" - tlog = logging.getLogger("cocotb.test") - yield Timer(10) constant_integer = dut.stream_in_valid @cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) -def access_constant_integer(dut): +async def access_constant_integer(dut): """ Access a constant integer """ tlog = logging.getLogger("cocotb.test") - yield Timer(10) constant_integer = dut.isample_module1.EXAMPLE_WIDTH tlog.info("Value of EXAMPLE_WIDTH is %d" % constant_integer) if not isinstance(constant_integer, ConstantObject): @@ -217,10 +179,9 @@ def access_constant_integer(dut): @cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) -def access_string_vhdl(dut): +async def access_string_vhdl(dut): """Access to a string, both constant and signal.""" tlog = logging.getLogger("cocotb.test") - yield Timer(10) constant_string = dut.isample_module1.EXAMPLE_STRING tlog.info("%r is %s" % (constant_string, constant_string.value)) if not isinstance(constant_string, ConstantObject): @@ -237,7 +198,7 @@ def access_string_vhdl(dut): if variable_string != b'': raise TestFailure("%r not \'\'" % variable_string) - yield Timer(10) + await Timer(1, "ns") if variable_string != test_string: raise TestFailure("%r %s != '%s'" % (variable_string, variable_string.value, test_string)) @@ -248,7 +209,7 @@ def access_string_vhdl(dut): dut.stream_in_string.setimmediatevalue(test_string) variable_string = dut.stream_out_string - yield Timer(10) + await Timer(1, "ns") test_string = test_string[:len(variable_string)] @@ -257,7 +218,7 @@ def access_string_vhdl(dut): tlog.info("Test read access to a string character") - yield Timer(10) + await Timer(1, "ns") idx = 3 @@ -269,14 +230,14 @@ def access_string_vhdl(dut): tlog.info("Test write access to a string character") - yield Timer(10) + await Timer(1, "ns") for i in variable_string: lower = chr(i) upper = lower.upper() i.setimmediatevalue(ord(upper)) - yield Timer(10) + await Timer(1, "ns") test_string = test_string.upper() @@ -290,12 +251,12 @@ def access_string_vhdl(dut): @cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"] or cocotb.SIM_NAME.lower().startswith(("icarus", "riviera")), expect_error=cocotb.result.TestFailure if cocotb.SIM_NAME.lower().startswith(("xmsim", "ncsim", "modelsim", "chronologic simulation vcs")) else False) -def access_const_string_verilog(dut): +async def access_const_string_verilog(dut): """Access to a const Verilog string.""" tlog = logging.getLogger("cocotb.test") string_const = dut.STRING_CONST - yield Timer(10, 'ns') + await Timer(10, "ns") tlog.info("%r is %s" % (string_const, string_const.value)) if not isinstance(string_const, StringObject): raise TestFailure("STRING_CONST was not StringObject") @@ -304,19 +265,19 @@ def access_const_string_verilog(dut): tlog.info("Modifying const string") string_const <= b"MODIFIED" - yield Timer(10, 'ns') + await Timer(10, "ns") if string_const != b"TESTING_CONST": raise TestFailure("STRING_CONST was not still b\'TESTING_CONST\' after modification but {} instead".format(string_const)) @cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"], expect_error=cocotb.SIM_NAME.lower().startswith("icarus")) -def access_var_string_verilog(dut): +async def access_var_string_verilog(dut): """Access to a var Verilog string.""" tlog = logging.getLogger("cocotb.test") string_var = dut.STRING_VAR - yield Timer(10, 'ns') + await Timer(10, "ns") tlog.info("%r is %s" % (string_var, string_var.value)) if not isinstance(string_var, StringObject): raise TestFailure("STRING_VAR was not StringObject") @@ -325,17 +286,16 @@ def access_var_string_verilog(dut): tlog.info("Modifying var string") string_var <= b"MODIFIED" - yield Timer(10, 'ns') + await Timer(10, "ns") if string_var != b"MODIFIED": raise TestFailure("STRING_VAR was not == b\'MODIFIED\' after modification but {} instead".format(string_var)) @cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) -def access_constant_boolean(dut): +async def access_constant_boolean(dut): """Test access to a constant boolean""" tlog = logging.getLogger("cocotb.test") - yield Timer(10) constant_boolean = dut.isample_module1.EXAMPLE_BOOL if not isinstance(constant_boolean, ConstantObject): raise TestFailure("dut.stream_in_int.EXAMPLE_BOOL is not a ConstantObject") @@ -344,11 +304,10 @@ def access_constant_boolean(dut): @cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) -def access_boolean(dut): +async def access_boolean(dut): """Test access to a boolean""" tlog = logging.getLogger("cocotb.test") - yield Timer(10) boolean = dut.stream_in_bool return @@ -378,7 +337,7 @@ def access_boolean(dut): boolean.setimmediatevalue(not curr_val) - yield Timer(1) + await Timer(1, "ns") tlog.info("Value of %s is now %d" % (output_bool._path, output_bool.value)) if (int(curr_val) == int(output_bool)): @@ -386,7 +345,7 @@ def access_boolean(dut): @cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"]) -def access_internal_register_array(dut): +async def access_internal_register_array(dut): """Test access to an internal register array""" if (dut.register_array[0].value.binstr != "xxxxxxxx"): @@ -394,33 +353,27 @@ def access_internal_register_array(dut): dut.register_array[1].setimmediatevalue(4) - yield Timer(1) + await Timer(1, "ns") if (dut.register_array[1].value != 4): raise TestFailure("Failed to set internal register array value") @cocotb.test(skip=True) -def skip_a_test(dut): +async def skip_a_test(dut): """This test shouldn't execute""" - yield Timer(10) dut._log.info("%s = %d bits" % (dut.stream_in_data._path, len(dut.stream_in_data))) bit = len(dut.stream_in_data) + 4 dut.stream_in_data[bit] <= 1 - yield Timer(10) @cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"], expect_error=cocotb.SIM_NAME.lower().startswith(("icarus"))) -def access_gate(dut): +async def access_gate(dut): """ Test access to a gate Object """ - tlog = logging.getLogger("cocotb.test") - - yield Timer(10) - gate = dut.test_and_gate if not isinstance(gate, HierarchyObject): @@ -428,14 +381,12 @@ def access_gate(dut): @cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) -def custom_type(dut): +async def custom_type(dut): """ Test iteration over a custom type """ tlog = logging.getLogger("cocotb.test") - yield Timer(10) - new_type = dut.cosLut tlog.info("cosLut object %s %s" % (new_type, type(new_type))) @@ -463,15 +414,13 @@ def _discover(obj): @cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"]) -def type_check_verilog(dut): +async def type_check_verilog(dut): """ Test if types are recognized """ tlog = logging.getLogger("cocotb.test") - yield Timer(1) - test_handles = [ (dut.stream_in_ready, "GPI_REGISTER"), (dut.register_array, "GPI_ARRAY"), diff --git a/tests/test_cases/test_discovery/test_vhdl_block.py b/tests/test_cases/test_discovery/test_vhdl_block.py index 9ccd4e12..3950393b 100644 --- a/tests/test_cases/test_discovery/test_vhdl_block.py +++ b/tests/test_cases/test_discovery/test_vhdl_block.py @@ -26,14 +26,12 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import cocotb -from cocotb.triggers import Timer from cocotb.result import TestFailure @cocotb.test() -def block_iter(dut): +async def block_iter(dut): """Access a VHDL block statement""" - yield Timer(0) try: dut._log.info("Block: {} ({})".format(dut.isample_module1.SAMPLE_BLOCK._name, diff --git a/tests/test_cases/test_discovery/test_vhdl_indexed_name.py b/tests/test_cases/test_discovery/test_vhdl_indexed_name.py index 09b6356f..b8ecfd5f 100644 --- a/tests/test_cases/test_discovery/test_vhdl_indexed_name.py +++ b/tests/test_cases/test_discovery/test_vhdl_indexed_name.py @@ -24,13 +24,11 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import cocotb -from cocotb.triggers import Timer @cocotb.test() -def index_name_iter(dut): +async def index_name_iter(dut): """Access a non local indexed name""" - yield Timer(0) total_count = 0 def _discover(obj): diff --git a/tests/test_cases/test_exit_error/test_exit.py b/tests/test_cases/test_exit_error/test_exit.py index 33e4ab0c..864b3a11 100644 --- a/tests/test_cases/test_exit_error/test_exit.py +++ b/tests/test_cases/test_exit_error/test_exit.py @@ -3,7 +3,7 @@ @cocotb.test() -def typosyntax_error(): +async def typosyntax_error(): # this syntax error makes the whole file unimportable, so the file contents # don't really matter. - yield Timer(100)a # noqa + await Timer(100)a # noqa diff --git a/tests/test_cases/test_iteration_verilog/test_iteration_es.py b/tests/test_cases/test_iteration_verilog/test_iteration_es.py index 326924d3..5e5e9d8f 100644 --- a/tests/test_cases/test_iteration_verilog/test_iteration_es.py +++ b/tests/test_cases/test_iteration_verilog/test_iteration_es.py @@ -26,12 +26,12 @@ import logging import cocotb -from cocotb.triggers import Timer +from cocotb.triggers import First from cocotb.result import TestFailure @cocotb.test(expect_fail=cocotb.SIM_NAME in ["Icarus Verilog"]) -def recursive_discovery(dut): +async def recursive_discovery(dut): """ Recursively discover every single object in the design """ @@ -45,7 +45,6 @@ def recursive_discovery(dut): pass_total = 265 tlog = logging.getLogger("cocotb.test") - yield Timer(100) def dump_all_the_things(parent): count = 0 @@ -60,16 +59,14 @@ def dump_all_the_things(parent): raise TestFailure("Expected %d objects but found %d" % (pass_total, total)) -@cocotb.coroutine -def iteration_loop(dut): +async def iteration_loop(dut): for thing in dut: thing._log.info("Found something: %s" % thing._fullname) - yield Timer(1) @cocotb.test() -def dual_iteration(dut): +async def dual_iteration(dut): loop_one = cocotb.fork(iteration_loop(dut)) loop_two = cocotb.fork(iteration_loop(dut)) - yield [loop_one.join(), loop_two.join()] + await First(loop_one.join(), loop_two.join()) diff --git a/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py b/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py index 107a46d3..9512db18 100644 --- a/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py +++ b/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py @@ -4,209 +4,209 @@ @cocotb.test() -def test_in_vect_packed(dut): - yield Timer(10) +async def test_in_vect_packed(dut): + await Timer(1, "ns") print("Setting: dut.in_vect_packed type %s" % type(dut.in_vect_packed)) dut.in_vect_packed = 0x5 - yield Timer(10) + await Timer(1, "ns") print("Getting: dut.out_vect_packed type %s" % type(dut.out_vect_packed)) if dut.out_vect_packed != 0x5: raise TestFailure("Failed to readback dut.out_vect_packed") @cocotb.test() -def test_in_vect_unpacked(dut): - yield Timer(10) +async def test_in_vect_unpacked(dut): + await Timer(1, "ns") print("Setting: dut.in_vect_unpacked type %s" % type(dut.in_vect_unpacked)) dut.in_vect_unpacked = [0x1, 0x0, 0x1] - yield Timer(10) + await Timer(1, "ns") print("Getting: dut.out_vect_unpacked type %s" % type(dut.out_vect_unpacked)) if dut.out_vect_unpacked != [0x1, 0x0, 0x1]: raise TestFailure("Failed to readback dut.out_vect_unpacked") @cocotb.test() -def test_in_arr(dut): - yield Timer(10) +async def test_in_arr(dut): + await Timer(1, "ns") print("Setting: dut.in_arr type %s" % type(dut.in_arr)) dut.in_arr = 0x5 - yield Timer(10) + await Timer(1, "ns") print("Getting: dut.out_arr type %s" % type(dut.out_arr)) if dut.out_arr != 0x5: raise TestFailure("Failed to readback dut.out_arr") @cocotb.test() -def test_in_2d_vect_packed_packed(dut): - yield Timer(10) +async def test_in_2d_vect_packed_packed(dut): + await Timer(1, "ns") print("Setting: dut.in_2d_vect_packed_packed type %s" % type(dut.in_2d_vect_packed_packed)) dut.in_2d_vect_packed_packed = (0x5 << 6) | (0x5 << 3) | 0x5 - yield Timer(10) + await Timer(1, "ns") print("Getting: dut.out_2d_vect_packed_packed type %s" % type(dut.out_2d_vect_packed_packed)) if dut.out_2d_vect_packed_packed != (0x5 << 6) | (0x5 << 3) | 0x5: raise TestFailure("Failed to readback dut.out_2d_vect_packed_packed") @cocotb.test() -def test_in_2d_vect_packed_unpacked(dut): - yield Timer(10) +async def test_in_2d_vect_packed_unpacked(dut): + await Timer(1, "ns") print("Setting: dut.in_2d_vect_packed_unpacked type %s" % type(dut.in_2d_vect_packed_unpacked)) dut.in_2d_vect_packed_unpacked = [0x5, 0x5, 0x5] - yield Timer(10) + await Timer(1, "ns") print("Getting: dut.out_2d_vect_packed_unpacked type %s" % type(dut.out_2d_vect_packed_unpacked)) if dut.out_2d_vect_packed_unpacked != [0x5, 0x5, 0x5]: raise TestFailure("Failed to readback dut.out_2d_vect_packed_unpacked") @cocotb.test() -def test_in_2d_vect_unpacked_unpacked(dut): - yield Timer(10) +async def test_in_2d_vect_unpacked_unpacked(dut): + await Timer(1, "ns") print("Setting: dut.in_2d_vect_unpacked_unpacked type %s" % type(dut.in_2d_vect_unpacked_unpacked)) dut.in_2d_vect_unpacked_unpacked = 3*[[0x1, 0x0, 0x1]] - yield Timer(10) + await Timer(1, "ns") print("Getting: dut.out_2d_vect_unpacked_unpacked type %s" % type(dut.out_2d_vect_unpacked_unpacked)) if dut.out_2d_vect_unpacked_unpacked != 3*[[0x1, 0x0, 0x1]]: raise TestFailure("Failed to readback dut.out_2d_vect_unpacked_unpacked") @cocotb.test() -def test_in_arr_packed(dut): - yield Timer(10) +async def test_in_arr_packed(dut): + await Timer(1, "ns") print("Setting: dut.in_arr_packed type %s" % type(dut.in_arr_packed)) dut.in_arr_packed = 365 - yield Timer(10) + await Timer(1, "ns") print("Getting: dut.out_arr_packed type %s" % type(dut.out_arr_packed)) if dut.out_arr_packed != 365: raise TestFailure("Failed to readback dut.out_arr_packed") @cocotb.test() -def test_in_arr_unpacked(dut): - yield Timer(10) +async def test_in_arr_unpacked(dut): + await Timer(1, "ns") print("Setting: dut.in_arr_unpackedtype %s" % type(dut.in_arr_unpacked)) dut.in_arr_unpacked = [0x5, 0x5, 0x5] - yield Timer(10) + await Timer(1, "ns") print("Getting: dut.out_arr_unpackedtype %s" % type(dut.out_arr_unpacked)) if dut.out_arr_unpacked != [0x5, 0x5, 0x5]: raise TestFailure("Failed to readback dut.out_arr_unpacked") @cocotb.test() -def test_in_2d_arr(dut): - yield Timer(10) +async def test_in_2d_arr(dut): + await Timer(1, "ns") print("Setting: dut.in_2d_arr type %s" % type(dut.in_2d_arr)) dut.in_2d_arr = 365 - yield Timer(10) + await Timer(1, "ns") print("Getting: dut.out_2d_arr type %s" % type(dut.out_2d_arr)) if dut.out_2d_arr != 365: raise TestFailure("Failed to readback dut.out_2d_arr") @cocotb.test() -def test_in_vect_packed_packed_packed(dut): - yield Timer(10) +async def test_in_vect_packed_packed_packed(dut): + await Timer(1, "ns") print("Setting: dut.in_vect_packed_packed_packed type %s" % type(dut.in_vect_packed_packed_packed)) dut.in_vect_packed_packed_packed = 95869805 - yield Timer(10) + await Timer(1, "ns") print("Getting: dut.out_vect_packed_packed_packed type %s" % type(dut.out_vect_packed_packed_packed)) if dut.out_vect_packed_packed_packed != 95869805: raise TestFailure("Failed to readback dut.out_vect_packed_packed_packed") @cocotb.test() -def test_in_vect_packed_packed_unpacked(dut): - yield Timer(10) +async def test_in_vect_packed_packed_unpacked(dut): + await Timer(1, "ns") print("Setting: dut.in_vect_packed_packed_unpacked type %s" % type(dut.in_vect_packed_packed_unpacked)) dut.in_vect_packed_packed_unpacked = [95869805, 95869805, 95869805] - yield Timer(10) + await Timer(1, "ns") print("Getting: dut.out_vect_packed_packed_unpacked type %s" % type(dut.out_vect_packed_packed_unpacked)) if dut.out_vect_packed_packed_unpacked != [365, 365, 365]: raise TestFailure("Failed to readback dut.out_vect_packed_packed_unpacked") @cocotb.test() -def test_in_vect_packed_unpacked_unpacked(dut): - yield Timer(10) +async def test_in_vect_packed_unpacked_unpacked(dut): + await Timer(1, "ns") print("Setting: dut.in_vect_packed_unpacked_unpacked type %s" % type(dut.in_vect_packed_unpacked_unpacked)) dut.in_vect_packed_unpacked_unpacked = 3 *[3 * [5] ] - yield Timer(10) + await Timer(1, "ns") print("Getting: dut.out_vect_packed_unpacked_unpacked type %s" % type(dut.out_vect_packed_unpacked_unpacked)) if dut.out_vect_packed_unpacked_unpacked != 3 *[3 * [5] ]: raise TestFailure("Failed to readback dut.out_vect_packed_unpacked_unpacked") @cocotb.test() -def test_in_vect_unpacked_unpacked_unpacked(dut): - yield Timer(10) +async def test_in_vect_unpacked_unpacked_unpacked(dut): + await Timer(1, "ns") print("Setting: dut.in_vect_unpacked_unpacked_unpacked type %s" % type(dut.in_vect_unpacked_unpacked_unpacked)) dut.in_vect_unpacked_unpacked_unpacked = 3 *[3 * [[1, 0, 1]]] - yield Timer(10) + await Timer(1, "ns") print("Getting: dut.out_vect_unpacked_unpacked_unpacked type %s" % type(dut.out_vect_unpacked_unpacked_unpacked)) if dut.out_vect_unpacked_unpacked_unpacked != 3 *[3 * [[1, 0, 1]]]: raise TestFailure("Failed to readback dut.out_vect_unpacked_unpacked_unpacked") @cocotb.test() -def test_in_arr_packed_packed(dut): - yield Timer(10) +async def test_in_arr_packed_packed(dut): + await Timer(1, "ns") print("Setting: dut.in_arr_packed_packed type %s" % type(dut.in_arr_packed_packed)) dut.in_arr_packed_packed = (365 << 18) | (365 << 9) | (365) - yield Timer(10) + await Timer(1, "ns") print("Getting: dut.out_arr_packed_packed type %s" % type(dut.out_arr_packed_packed)) if dut.out_arr_packed_packed != (365 << 18) | (365 << 9) | (365): raise TestFailure("Failed to readback dut.out_arr_packed_packed") @cocotb.test() -def test_in_arr_packed_unpacked(dut): - yield Timer(10) +async def test_in_arr_packed_unpacked(dut): + await Timer(1, "ns") print("Setting: dut.in_arr_packed_unpacked type %s" % type(dut.in_arr_packed_unpacked)) dut.in_arr_packed_unpacked = [365, 365, 365] - yield Timer(10) + await Timer(1, "ns") print("Getting: dut.out_arr_packed_unpacked type %s" % type(dut.out_arr_packed_unpacked)) if dut.out_arr_packed_unpacked != [365, 365, 365]: raise TestFailure("Failed to readback dut.out_arr_packed_unpacked") @cocotb.test() -def test_in_arr_unpacked_unpacked(dut): - yield Timer(10) +async def test_in_arr_unpacked_unpacked(dut): + await Timer(1, "ns") print("Setting: dut.in_arr_unpacked_unpacked type %s" % type(dut.in_arr_unpacked_unpacked)) dut.in_arr_unpacked_unpacked = 3 *[3 * [5] ] - yield Timer(10) + await Timer(1, "ns") print("Getting: dut.out_arr_unpacked_unpacked type %s" % type(dut.out_arr_unpacked_unpacked)) if dut.out_arr_unpacked_unpacked != 3 *[3 * [5] ]: raise TestFailure("Failed to readback dut.out_arr_unpacked_unpacked") @cocotb.test() -def test_in_2d_arr_packed(dut): - yield Timer(10) +async def test_in_2d_arr_packed(dut): + await Timer(1, "ns") print("Setting: dut.in_2d_arr_packed type %s" % type(dut.in_2d_arr_packed)) dut.in_2d_arr_packed = (365 << 18) | (365 << 9) | (365) - yield Timer(10) + await Timer(1, "ns") print("Getting: dut.out_2d_arr_packed type %s" % type(dut.out_2d_arr_packed)) if dut.out_2d_arr_packed != (365 << 18) | (365 << 9) | (365): raise TestFailure("Failed to readback dut.out_2d_arr_packed") @cocotb.test() -def test_in_2d_arr_unpacked(dut): - yield Timer(10) +async def test_in_2d_arr_unpacked(dut): + await Timer(1, "ns") print("Setting: dut.in_2d_arr_unpacked type %s" % type(dut.in_2d_arr_unpacked)) dut.in_2d_arr_unpacked = [365, 365, 365] - yield Timer(10) + await Timer(1, "ns") print("Getting: dut.out_2d_arr_unpacked type %s" % type(dut.out_2d_arr_unpacked)) if dut.out_2d_arr_unpacked != [365, 365, 365]: raise TestFailure("Failed to readback dut.out_2d_arr_unpacked") @cocotb.test() -def test_in_3d_arr(dut): - yield Timer(10) +async def test_in_3d_arr(dut): + await Timer(1, "ns") print("Setting: dut.in_3d_arr type %s" % type(dut.in_3d_arr)) dut.in_3d_arr = (365 << 18) | (365 << 9) | (365) - yield Timer(10) + await Timer(1, "ns") print("Getting: dut.out_3d_arr type %s" % type(dut.out_3d_arr)) if dut.out_3d_arr != (365 << 18) | (365 << 9) | (365): raise TestFailure("Failed to readback dut.out_3d_arr") diff --git a/tests/test_cases/test_plusargs/plusargs.py b/tests/test_cases/test_plusargs/plusargs.py index 9d20bdc3..e3011739 100644 --- a/tests/test_cases/test_plusargs/plusargs.py +++ b/tests/test_cases/test_plusargs/plusargs.py @@ -29,23 +29,16 @@ plusarg testing """ -from __future__ import print_function - import cocotb from cocotb.result import TestFailure -from cocotb.triggers import Timer @cocotb.test() -def plusargs_test(dut): - """Demonstrates plusarg access from Python test""" - - yield Timer(10000) +async def plusargs_test(dut): + """Demonstrate plusarg access from Python test""" for name in cocotb.plusargs: print("COCOTB:", name, cocotb.plusargs[name]) if cocotb.plusargs['foo'] != 'bar': raise TestFailure("plusargs 'foo' value '{}' does not match expected 'bar'".format(cocotb.plusargs['foo'])) - - yield Timer(10000) diff --git a/tests/test_cases/test_verilog_access/test_verilog_access.py b/tests/test_cases/test_verilog_access/test_verilog_access.py index d1053197..702a8047 100644 --- a/tests/test_cases/test_verilog_access/test_verilog_access.py +++ b/tests/test_cases/test_verilog_access/test_verilog_access.py @@ -27,19 +27,17 @@ import cocotb from cocotb.handle import HierarchyObject, ModifiableObject -from cocotb.triggers import Timer from cocotb.result import TestFailure @cocotb.test() -def port_not_hierarchy(dut): +async def port_not_hierarchy(dut): """ Test for issue raised by Luke - iteration causes a toplevel port type to change from ModifiableObject to HierarchyObject """ fails = 0 tlog = logging.getLogger("cocotb.test") - yield Timer(100) def check_instance(obj, objtype): if not isinstance(obj, objtype): diff --git a/tests/test_cases/test_vhdl_access/test_vhdl_access.py b/tests/test_cases/test_vhdl_access/test_vhdl_access.py index 2ea6ada2..cb929281 100644 --- a/tests/test_cases/test_vhdl_access/test_vhdl_access.py +++ b/tests/test_cases/test_vhdl_access/test_vhdl_access.py @@ -27,30 +27,27 @@ import cocotb from cocotb.handle import HierarchyObject, ModifiableObject, IntegerObject, ConstantObject, EnumObject -from cocotb.triggers import Timer from cocotb.result import TestFailure @cocotb.test() -def check_enum_object(dut): +async def check_enum_object(dut): """ Enumerations currently behave as normal signals TODO: Implement an EnumObject class and detect valid string mappings """ - yield Timer(100) if not isinstance(dut.inst_ram_ctrl.write_ram_fsm, EnumObject): raise TestFailure("Expected the FSM enum to be an EnumObject") @cocotb.test() -def check_objects(dut): +async def check_objects(dut): """ Check the types of objects that are returned """ tlog = logging.getLogger("cocotb.test") fails = 0 - yield Timer(100) def check_instance(obj, objtype): if not isinstance(obj, objtype): @@ -96,13 +93,12 @@ def check_instance(obj, objtype): @cocotb.test() -def port_not_hierarchy(dut): +async def port_not_hierarchy(dut): """ Test for issue raised by Luke - iteration causes a toplevel port type to change from ModifiableObject to HierarchyObject """ tlog = logging.getLogger("cocotb.test") - yield Timer(100) if not isinstance(dut.aclk, ModifiableObject): tlog.error("dut.aclk should be ModifiableObject but got %s", type(dut.aclk).__name__) else: From 4b6d15289a353e1670cba28a1c2b9348bac527c2 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 16 Oct 2020 21:54:45 -0500 Subject: [PATCH 2542/2656] Add global alias for DUT parameter This feature will allow users to parameterize module-level classes and functions, and arguments to `TestFactory`s. --- cocotb/__init__.py | 15 +++++++++++++-- documentation/source/building.rst | 3 +++ documentation/source/library_reference.rst | 2 ++ .../source/newsfragments/2134.feature.rst | 1 + documentation/source/quickstart.rst | 2 ++ 5 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 documentation/source/newsfragments/2134.feature.rst diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 424d75f6..214ab297 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -129,6 +129,16 @@ def _reopen_stream_with_buffering(stream_name): _library_coverage = None """ used for cocotb library coverage """ +top = None # type: cocotb.handle.SimHandleBase +r""" +A handle to the :envvar:`TOPLEVEL` entity/module. + +This is equivalent to the :term:`DUT` parameter given to cocotb tests, so it can be used wherever that variable can be used. +It is particularly useful for extracting information about the :term:`DUT` in module-level class and function definitions; +and in parameters to :class:`.TestFactory`\ s. +``None`` if :mod:`cocotb` was not loaded from a simulator. +""" + def fork(coro: Union[RunningTask, Coroutine]) -> RunningTask: """ Schedule a coroutine to be run concurrently. See :ref:`coroutines` for details on its use. """ @@ -239,11 +249,12 @@ def _initialise_testbench(argv_): if not handle: raise RuntimeError("Can not find root handle ({})".format(root_name)) - dut = cocotb.handle.SimHandle(handle) + global top + top = cocotb.handle.SimHandle(handle) # start Regression Manager global regression_manager - regression_manager = RegressionManager.from_discovery(dut) + regression_manager = RegressionManager.from_discovery(top) regression_manager.execute() _rlock.release() diff --git a/documentation/source/building.rst b/documentation/source/building.rst index 766b5332..9f93e766 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -45,6 +45,9 @@ Cocotb Use this to indicate the instance in the hierarchy to use as the :term:`DUT`. If this isn't defined then the first root instance is used. + The DUT is available in cocotb tests as a Python object at :data:`cocotb.top`; + and is also passed to all cocotb tests as the :ref:`first and only parameter `. + .. envvar:: RANDOM_SEED Seed the Python random module to recreate a previous test stimulus. diff --git a/documentation/source/library_reference.rst b/documentation/source/library_reference.rst index 21f364e1..e57ff39c 100644 --- a/documentation/source/library_reference.rst +++ b/documentation/source/library_reference.rst @@ -369,6 +369,8 @@ Other Runtime Information .. autodata:: cocotb.LANGUAGE +.. autodata:: cocotb.top + Signal Tracer for WaveDrom -------------------------- diff --git a/documentation/source/newsfragments/2134.feature.rst b/documentation/source/newsfragments/2134.feature.rst new file mode 100644 index 00000000..8cc98144 --- /dev/null +++ b/documentation/source/newsfragments/2134.feature.rst @@ -0,0 +1 @@ +The handle to :envvar:`TOPLEVEL`, typically seen as the first argument to a cocotb test function, is now available globally as :data:`cocotb.top`. diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index 478ebaac..e9300296 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -78,6 +78,8 @@ Python test script to load. We would then create a file called ``test_my_design.py`` containing our tests. +.. _quickstart_creating_a_test: + Creating a test --------------- From ef817176971fb88edb83686adac9f3f6ded5e7ca Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 16 Oct 2020 00:26:02 -0500 Subject: [PATCH 2543/2656] Add configuration for stale bot --- .github/stale.yml | 59 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 00000000..32842186 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,59 @@ +# Configuration for probot-stale - https://github.com/probot/stale + +# Number of days of inactivity before an Issue or Pull Request becomes stale +daysUntilStale: 60 + +# Number of days of inactivity before an Issue or Pull Request with the stale label is closed. +# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. +daysUntilClose: 7 + +# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) +onlyLabels: + - "type:question" + +# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable +exemptLabels: + - "type:bug" # inquiry was later confirmed a bug of valid feature and 'question' label was not removed + - "type:feature" + +# Set to true to ignore issues in a project (defaults to false) +exemptProjects: false + +# Set to true to ignore issues in a milestone (defaults to false) +exemptMilestones: false + +# Set to true to ignore issues with an assignee (defaults to false) +exemptAssignees: false + +# Label to use when marking as stale +staleLabel: "status:close?" + +# Comment to post when marking as stale. Set to `false` to disable +markComment: > +Has your question or report been resolved? If so please close this issue. If not, you may need to provide more information. +If no more activity on this issue occurs in 7 days, it will be closed. + +# Comment to post when removing the stale label. +# unmarkComment: > +# Your comment here. + +# Comment to post when closing a stale Issue or Pull Request. +# closeComment: > + +# Limit the number of actions per hour, from 1-30. Default is 30 +limitPerRun: 30 + +# Limit to only `issues` or `pulls` +only: issues + +# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': +# pulls: +# daysUntilStale: 30 +# markComment: > +# This pull request has been automatically marked as stale because it has not had +# recent activity. It will be closed if no further activity occurs. Thank you +# for your contributions. + +# issues: +# exemptLabels: +# - confirmed From 21424558ca4e256573df40e962959526e9b7b9fa Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 20 Oct 2020 22:55:11 +0200 Subject: [PATCH 2544/2656] Add template for GitHub issues --- .github/issue_template.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/issue_template.md diff --git a/.github/issue_template.md b/.github/issue_template.md new file mode 100644 index 00000000..99580e0c --- /dev/null +++ b/.github/issue_template.md @@ -0,0 +1,11 @@ + From 19a275e53170ee98334d4aedc5fa446960027f05 Mon Sep 17 00:00:00 2001 From: Markus Krause Date: Wed, 21 Oct 2020 22:45:42 +0200 Subject: [PATCH 2545/2656] check for sane Timer value (#2107) Prevent the creation of negative Timer triggers. Warn when creating Timer triggers of 0 time as it is undefined behavior. Co-authored-by: Kaleb Barrett Co-authored-by: Eric Wieser Co-authored-by: Marlon James <47790688+garmin-mjames@users.noreply.github.com> --- cocotb/triggers.py | 15 ++++++++ tests/test_cases/test_cocotb/test_handle.py | 7 ++-- .../test_cocotb/test_timing_triggers.py | 35 ++++++++++--------- .../test_discovery/test_discovery.py | 1 - 4 files changed, 38 insertions(+), 20 deletions(-) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 794f8fc7..df163a7e 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -28,6 +28,7 @@ """A collections of triggers which a testbench can await.""" import abc +import warnings from collections.abc import Awaitable from cocotb import simulator @@ -194,8 +195,22 @@ def __init__(self, time_ps, units=None): See Also: :func:`~cocotb.utils.get_sim_steps` + + Raises: + TriggerException: If a negative value is passed for Timer setup. + + .. versionchanged:: 1.5 + Raise an exception when Timer uses a negative value as it is undefined behavior. + Warn for 0 as this will cause erratic behavior in some simulators as well. """ GPITrigger.__init__(self) + if time_ps <= 0: + if time_ps == 0: + warnings.warn("Timer setup with value 0, which might exhibit undefined behavior in some simulators", + category=RuntimeWarning, + stacklevel=2) + else: + raise TriggerException("Timer value time_ps must not be negative") self.sim_steps = get_sim_steps(time_ps, units) def prime(self, callback): diff --git a/tests/test_cases/test_cocotb/test_handle.py b/tests/test_cases/test_cocotb/test_handle.py index 0abdf490..f4dd3289 100644 --- a/tests/test_cases/test_cocotb/test_handle.py +++ b/tests/test_cases/test_cocotb/test_handle.py @@ -34,8 +34,11 @@ def test_lessthan_raises_error(dut): @cocotb.test() -def test_bad_attr(dut): - yield cocotb.triggers.NullTrigger() +async def test_bad_attr(dut): + + with assert_raises(AttributeError): + fake_signal = dut.fake_signal + try: _ = dut.stream_in_data.whoops except AttributeError as e: diff --git a/tests/test_cases/test_cocotb/test_timing_triggers.py b/tests/test_cases/test_cocotb/test_timing_triggers.py index 19b7eaa2..3060ec25 100644 --- a/tests/test_cases/test_cocotb/test_timing_triggers.py +++ b/tests/test_cases/test_cocotb/test_timing_triggers.py @@ -11,11 +11,14 @@ * with_timeout """ import cocotb -from cocotb.triggers import Timer, RisingEdge, ReadOnly, ReadWrite, Join, NextTimeStep +import warnings +from cocotb.triggers import Timer, RisingEdge, ReadOnly, ReadWrite, Join, NextTimeStep, TriggerException from cocotb.utils import get_sim_time from cocotb.result import TestFailure from cocotb.clock import Clock +from common import assert_raises + from fractions import Fraction from decimal import Decimal @@ -154,24 +157,9 @@ def test_cached_write_in_readonly(dut): raise TestFailure -@cocotb.test(expect_fail=cocotb.SIM_NAME.lower().startswith(("icarus", - "chronologic simulation vcs")), - skip=cocotb.SIM_NAME.lower().startswith(("ncsim", "xmsim"))) -def test_afterdelay_in_readonly(dut): - """Test doing invalid sim operation""" - global exited - exited = False - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) - coro = cocotb.fork(do_test_afterdelay_in_readonly(dut, 0)) - yield [Join(coro), Timer(1000)] - clk_gen.kill() - if exited is not True: - raise TestFailure - - @cocotb.test() def test_afterdelay_in_readonly_valid(dut): - """Same as test_afterdelay_in_readonly but with valid delay > 0""" + """Test Timer delay after ReadOnly phase""" global exited exited = False clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) @@ -244,3 +232,16 @@ async def test_singleton_isinstance(dut): assert isinstance(NextTimeStep(), NextTimeStep) assert isinstance(ReadOnly(), ReadOnly) assert isinstance(ReadWrite(), ReadWrite) + + +@cocotb.test() +async def test_neg_timer(dut): + """Test negative timer values are forbidden""" + with assert_raises(TriggerException): + Timer(-42) # no need to even `await`, constructing it is an error + # handle 0 special case + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + Timer(0) + assert "Timer setup with value 0, which might exhibit undefined behavior in some simulators" in str(w[-1].message) + assert issubclass(w[-1].category, RuntimeWarning) diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 1990ec12..07e29aec 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -49,7 +49,6 @@ async def pseudo_region_access(dut): @cocotb.test() async def recursive_discover(dut): """Discover absolutely everything in the DUT""" - def _discover(obj): for thing in obj: dut._log.info("Found %s (%s)", thing._name, type(thing)) From dc6ad51ebb387d36ed78389d7971717ab9935ec7 Mon Sep 17 00:00:00 2001 From: Markus Krause Date: Thu, 22 Oct 2020 19:40:28 +0200 Subject: [PATCH 2546/2656] add support for FST format tracing with verilator (#2102) Co-authored-by: Philipp Wagner Co-authored-by: Kaleb Barrett --- cocotb/share/lib/verilator/verilator.cpp | 18 ++++++++++++++++-- .../makefiles/simulators/Makefile.verilator | 1 + documentation/source/simulator_support.rst | 12 ++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/cocotb/share/lib/verilator/verilator.cpp b/cocotb/share/lib/verilator/verilator.cpp index 2b39ace0..b3377865 100644 --- a/cocotb/share/lib/verilator/verilator.cpp +++ b/cocotb/share/lib/verilator/verilator.cpp @@ -8,8 +8,17 @@ #include +#ifndef VM_TRACE_FST + //emulate new verilator behavior for legacy versions + #define VM_TRACE_FST 0 +#endif + #if VM_TRACE -# include +#if VM_TRACE_FST +#include +#else +#include +#endif #endif vluint64_t main_time = 0; // Current simulation time @@ -36,10 +45,15 @@ int main(int argc, char** argv) { #if VM_TRACE Verilated::traceEverOn(true); - +#if VM_TRACE_FST + std::unique_ptr tfp(new VerilatedFstC); + top->trace(tfp.get(), 99); + tfp->open("dump.fst"); +#else std::unique_ptr tfp(new VerilatedVcdC); top->trace(tfp.get(), 99); tfp->open("dump.vcd"); +#endif #endif while (!Verilated::gotFinish()) { diff --git a/cocotb/share/makefiles/simulators/Makefile.verilator b/cocotb/share/makefiles/simulators/Makefile.verilator index c890ffd1..d35e441a 100644 --- a/cocotb/share/makefiles/simulators/Makefile.verilator +++ b/cocotb/share/makefiles/simulators/Makefile.verilator @@ -66,5 +66,6 @@ debug: $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) clean:: @rm -rf $(SIM_BUILD) @rm -f dump.vcd + @rm -f dump.fst endif diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 0d974f61..2e005670 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -121,6 +121,18 @@ To get waveforms in VCD format, add Verilator's trace option(s) to the To set the same options on the command line, use ``EXTRA_ARGS="--trace --trace-structs" make ...``. A VCD file named ``dump.vcd`` will be generated in the current directory. +Verilator can produce waveform traces in the FST format, the native format of GTKWave. +FST traces are much smaller and more efficient to write, but require the use of GTKWave. + +To enable FST tracing, add `--trace-fst -CFLAGS -DVM_TRACE_FST=1` to `EXTRA_ARGS` as shown below. +For Verilator 4.102 and above, the `-CFLAGS -DVM_TRACE_FST=1` argument is no longer necessary. + + .. code-block:: make + + EXTRA_ARGS += --trace-fst --trace-structs -CFLAGS -DVM_TRACE_FST=1 + +The resulting file will be ``dump.fst`` and can be opened by ``gtkwave dump.fst``. + .. _sim-verilator-issues: Issues for this simulator From 4edd09dc093b90846c3fe1fced7dba936d6c3f55 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 23 Oct 2020 17:40:12 +0200 Subject: [PATCH 2547/2656] Replace Travis badge with GHA's --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8ffaa7a0..daa74c59 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ **cocotb** is a coroutine based cosimulation library for writing VHDL and Verilog testbenches in Python. [![Documentation Status](https://readthedocs.org/projects/cocotb/badge/?version=latest)](https://docs.cocotb.org/en/latest/) -[![Build Status](https://travis-ci.org/cocotb/cocotb.svg?branch=master)](https://travis-ci.org/cocotb/cocotb) +[![Build Status](https://github.com/cocotb/cocotb/workflows/Regression%20Tests/badge.svg)](https://github.com/cocotb/cocotb/actions?query=workflow%3A%22Regression+Tests%22) [![PyPI](https://img.shields.io/pypi/dm/cocotb.svg?label=PyPI%20downloads)](https://pypi.org/project/cocotb/) [![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/cocotb/cocotb) [![codecov](https://codecov.io/gh/cocotb/cocotb/branch/master/graph/badge.svg)](https://codecov.io/gh/cocotb/cocotb) From a83873cda4df5ace6670acd9b37bc6a80377565c Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 16 Oct 2020 19:51:21 -0500 Subject: [PATCH 2548/2656] Remove the builtin system task overloads for VPI These system tasks overload tasks defined by in the Verilog LRM. This makes many simulators very unhappy; they do not allow you to overload some or none of the functions and either do not work, or protest (#2095). These system tasks never properly parsed additional arguments correctly (#130, #2093). Also they were never supported with either the VHPI or FLI interfaces. Their main purpose was to hook HDL reporting into the Python logger. Since the overload is global, this is fairly dubious design decision to begin with. If this behavior is supported and desired, we leave it to a GPI extension to reintroduce. --- cocotb/share/lib/vpi/VpiImpl.cpp | 99 ------------------- .../source/newsfragments/2133.removal.rst | 1 + tests/test_cases/test_closedown/Makefile | 30 ------ .../test_closedown/test_closedown.py | 70 ------------- 4 files changed, 1 insertion(+), 199 deletions(-) create mode 100644 documentation/source/newsfragments/2133.removal.rst delete mode 100644 tests/test_cases/test_closedown/Makefile delete mode 100644 tests/test_cases/test_closedown/test_closedown.py diff --git a/cocotb/share/lib/vpi/VpiImpl.cpp b/cocotb/share/lib/vpi/VpiImpl.cpp index aec15d9e..e574504f 100644 --- a/cocotb/share/lib/vpi/VpiImpl.cpp +++ b/cocotb/share/lib/vpi/VpiImpl.cpp @@ -641,108 +641,9 @@ static void register_final_callback() sim_finish_cb->arm_callback(); } -// Called at compile time to validate the arguments to the system functions -// we redefine (info, warning, error, fatal). -// -// Expect either no arguments or a single string -static int system_function_compiletf(char *userdata) -{ - COCOTB_UNUSED(userdata); - vpiHandle systf_handle, arg_iterator, arg_handle; - int tfarg_type; - - systf_handle = vpi_handle(vpiSysTfCall, NULL); - arg_iterator = vpi_iterate(vpiArgument, systf_handle); - - if (arg_iterator == NULL) - return 0; - - arg_handle = vpi_scan(arg_iterator); - tfarg_type = vpi_get(vpiType, arg_handle); - - // FIXME: HACK for some reason Icarus returns a vpiRealVal type for strings? - if (vpiStringVal != tfarg_type && vpiRealVal != tfarg_type) { - vpi_printf("ERROR: $[info|warning|error|fatal] argument wrong type: %d\n", - tfarg_type); - vpi_free_object(arg_iterator); - vpi_control(vpiFinish, 1); - return -1; - } - return 0; -} - -static int systf_info_level = GPIInfo; -static int systf_warning_level = GPIWarning; -static int systf_error_level = GPIError; -static int systf_fatal_level = GPICritical; - -// System function to permit code in the simulator to fail a test -// TODO: Pass in an error string -static int system_function_overload(char *userdata) -{ - vpiHandle systfref, args_iter, argh; - struct t_vpi_value argval; - const char *msg = "*** NO MESSAGE PROVIDED ***"; - - // Obtain a handle to the argument list - systfref = vpi_handle(vpiSysTfCall, NULL); - args_iter = vpi_iterate(vpiArgument, systfref); - - // The first argument to fatal is the FinishNum which we discard - if (args_iter && *userdata == systf_fatal_level) { - vpi_scan(args_iter); - } - - if (args_iter) { - // Grab the value of the first argument - argh = vpi_scan(args_iter); - argval.format = vpiStringVal; - vpi_get_value(argh, &argval); - vpi_free_object(args_iter); - msg = argval.value.str; - } - - enum gpi_log_levels userdata_as_loglevel = (enum gpi_log_levels)*userdata; - - gpi_log("cocotb.simulator", userdata_as_loglevel, vpi_get_str(vpiFile, systfref), "", (long)vpi_get(vpiLineNo, systfref), "%s", msg ); - - // Fail the test for critical errors - if (GPICritical == userdata_as_loglevel) - gpi_embed_event(SIM_TEST_FAIL, argval.value.str); - - return 0; -} - -static void register_system_functions() -{ - s_vpi_systf_data tfData = { vpiSysTask, vpiSysTask, NULL, NULL, NULL, NULL, NULL }; - - tfData.sizetf = NULL; - tfData.compiletf = system_function_compiletf; - tfData.calltf = system_function_overload; - - tfData.user_data = (char *)&systf_info_level; - tfData.tfname = "$info"; - vpi_register_systf( &tfData ); - - tfData.user_data = (char *)&systf_warning_level; - tfData.tfname = "$warning"; - vpi_register_systf( &tfData ); - - tfData.user_data = (char *)&systf_error_level; - tfData.tfname = "$error"; - vpi_register_systf( &tfData ); - - tfData.user_data = (char *)&systf_fatal_level; - tfData.tfname = "$fatal"; - vpi_register_systf( &tfData ); - -} - COCOTBVPI_EXPORT void (*vlog_startup_routines[])() = { register_embed, gpi_load_extra_libs, - register_system_functions, register_initial_callback, register_final_callback, nullptr diff --git a/documentation/source/newsfragments/2133.removal.rst b/documentation/source/newsfragments/2133.removal.rst new file mode 100644 index 00000000..ea276694 --- /dev/null +++ b/documentation/source/newsfragments/2133.removal.rst @@ -0,0 +1 @@ +The system task overloads for ``$info``, ``$warn``, ``$error`` and ``$fatal`` in Verilog and mixed language testbenches have been removed. diff --git a/tests/test_cases/test_closedown/Makefile b/tests/test_cases/test_closedown/Makefile deleted file mode 100644 index 4cf069a6..00000000 --- a/tests/test_cases/test_closedown/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -############################################################################### -# Copyright (c) 2015 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -include ../../designs/close_module/Makefile - -MODULE=test_closedown diff --git a/tests/test_cases/test_closedown/test_closedown.py b/tests/test_cases/test_closedown/test_closedown.py deleted file mode 100644 index 57a47ba0..00000000 --- a/tests/test_cases/test_closedown/test_closedown.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import cocotb -from cocotb.triggers import Timer, RisingEdge -from cocotb.clock import Clock - - -async def test_read(dut): - global test_count - dut._log.info("Inside test_read") - while test_count != 5: - await RisingEdge(dut.clk) - test_count += 1 - - -async def run_external(dut): - await cocotb.external(test_read)(dut) - - -async def clock_mon(dut): - await RisingEdge(dut.clk) - - -@cocotb.test( - expect_fail=True, - skip=cocotb.SIM_NAME.lower().startswith("riviera"), # gh-1859 - expect_error=cocotb.SIM_NAME.lower().startswith("modelsim") # $fatal() fails hard on Questa -) -async def test_failure_from_system_task(dut): - """ - Allow the dut to call system tasks from verilog. - $fatal() will fail the test, and scheduler will cleanup forked coroutines. - """ - cocotb.fork(Clock(dut.clk, 100, units='ns').start()) - cocotb.fork(clock_mon(dut)) - cocotb.fork(run_external(dut)) - await Timer(10000000, units='ns') - - -@cocotb.test(skip=cocotb.SIM_NAME.lower().startswith("riviera")) # gh-1859 -async def test_after_system_task_fail(dut): - """ - Test to run after failed test. - """ - await Timer(1, units='ns') From ae8a7ecd8f6c37c9328ff6c8ce04c1161db7633e Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 19 Oct 2020 21:12:18 -0500 Subject: [PATCH 2549/2656] Add test of $fatal cleaning shutting down cocotb $fatal stops the simulator. This should cause cocotb's regression manager to record failure for the current and all remaining sims. --- tests/designs/close_module/Makefile | 55 ------------------- tests/designs/close_module/close_module.v | 67 ----------------------- tests/test_cases/test_fatal/Makefile | 23 ++++++++ tests/test_cases/test_fatal/fatal.sv | 16 ++++++ tests/test_cases/test_fatal/fatal.vhd | 18 ++++++ tests/test_cases/test_fatal/test_fatal.py | 15 +++++ 6 files changed, 72 insertions(+), 122 deletions(-) delete mode 100644 tests/designs/close_module/Makefile delete mode 100644 tests/designs/close_module/close_module.v create mode 100644 tests/test_cases/test_fatal/Makefile create mode 100644 tests/test_cases/test_fatal/fatal.sv create mode 100644 tests/test_cases/test_fatal/fatal.vhd create mode 100644 tests/test_cases/test_fatal/test_fatal.py diff --git a/tests/designs/close_module/Makefile b/tests/designs/close_module/Makefile deleted file mode 100644 index a336690b..00000000 --- a/tests/designs/close_module/Makefile +++ /dev/null @@ -1,55 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -TOPLEVEL_LANG ?= verilog - -ifneq ($(TOPLEVEL_LANG),verilog) - -all: - @echo "Skipping test due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being verilog" -clean:: - -else - -TOPLEVEL = close_module - -PWD=$(shell pwd) - -COCOTB?=$(PWD)/../../.. - -VERILOG_SOURCES = $(COCOTB)/tests/designs/close_module/close_module.v - -ifneq ($(SIM),vcs) -include $(shell cocotb-config --makefiles)/Makefile.sim -else -all: - @echo "Skipping test as system call overrides segfault VCS" -endif - -endif diff --git a/tests/designs/close_module/close_module.v b/tests/designs/close_module/close_module.v deleted file mode 100644 index eb5d5d21..00000000 --- a/tests/designs/close_module/close_module.v +++ /dev/null @@ -1,67 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2013 Potential Ventures Ltd -// Copyright (c) 2013 SolarFlare Communications Inc -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of Potential Ventures Ltd, -// Copyright (c) 2013 SolarFlare Communications Inc nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -//----------------------------------------------------------------------------- - - -`timescale 1 ps / 1 ps - -module close_module ( - input clk, - - output reg stream_in_ready, - input stream_in_valid, - input [7:0] stream_in_data, - - input stream_out_ready, - output reg [7:0] stream_out_data_comb, - output reg [7:0] stream_out_data_registered -); - -always @(posedge clk) - stream_out_data_registered <= stream_in_data; - -always @(stream_in_data) - stream_out_data_comb = stream_in_data; - -always @(stream_out_ready) - stream_in_ready = stream_out_ready; - - -initial begin - $dumpfile("waveform.vcd"); - $dumpvars(0,close_module); - #10 $info; - #10 $info("This is an info message"); - #10 $warning; - #10 $warning("This is a warning message"); - #10 $error; - #10 $error("This is an error message"); - #1000 $fatal(1, "This is a a fatal message that fails the test"); -end - -endmodule diff --git a/tests/test_cases/test_fatal/Makefile b/tests/test_cases/test_fatal/Makefile new file mode 100644 index 00000000..d74c369e --- /dev/null +++ b/tests/test_cases/test_fatal/Makefile @@ -0,0 +1,23 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +TOPLEVEL_LANG ?= verilog +TOPLEVEL := fatal +MODULE := test_fatal + +ifeq ($(TOPLEVEL_LANG),verilog) + VERILOG_SOURCES := fatal.sv +else ifeq ($(TOPLEVEL_LANG),vhdl) + VHDL_SOURCES := fatal.vhd +endif + +# squash error code from simulator and ensure the cocotb test passed +.PHONY: override_for_this_test +override_for_this_test: + -$(MAKE) all + $(call check_for_results_file) + ../../../bin/combine_results.py &> /dev/null + +include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/tests/test_cases/test_fatal/fatal.sv b/tests/test_cases/test_fatal/fatal.sv new file mode 100644 index 00000000..8ffecd91 --- /dev/null +++ b/tests/test_cases/test_fatal/fatal.sv @@ -0,0 +1,16 @@ +//----------------------------------------------------------------------------- +// Copyright cocotb contributors +// Licensed under the Revised BSD License, see LICENSE for details. +// SPDX-License-Identifier: BSD-3-Clause USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//----------------------------------------------------------------------------- + +`timescale 1 ps / 1 ps + +module fatal; + +initial begin + #10 $fatal(1, "This is a fatal message that finishes the test"); +end + +endmodule diff --git a/tests/test_cases/test_fatal/fatal.vhd b/tests/test_cases/test_fatal/fatal.vhd new file mode 100644 index 00000000..49edd145 --- /dev/null +++ b/tests/test_cases/test_fatal/fatal.vhd @@ -0,0 +1,18 @@ +-- Copyright cocotb contributors +-- Licensed under the Revised BSD License, see LICENSE for details. +-- SPDX-License-Identifier: BSD-3-Clause USE OF THIS +-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +entity fatal is +end entity fatal; + +architecture behav of fatal is +begin + + fatal_proc : process is + begin + wait for 10 ns; + report "This is a fatal message that finishes the test" severity FAILURE; + end process fatal_proc; + +end architecture behav; diff --git a/tests/test_cases/test_fatal/test_fatal.py b/tests/test_cases/test_fatal/test_fatal.py new file mode 100644 index 00000000..22095de2 --- /dev/null +++ b/tests/test_cases/test_fatal/test_fatal.py @@ -0,0 +1,15 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import cocotb +from cocotb.triggers import Timer +from cocotb.result import SimFailure + + +@cocotb.test( + expect_error=SimFailure, + skip=cocotb.SIM_NAME.lower().startswith("riviera")) +async def test_fatal(_): + await Timer(100, 'ns') From 4d4fffaeedd3557601f706e1b5d872b15b0a7a42 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Mon, 19 Oct 2020 22:38:15 -0700 Subject: [PATCH 2550/2656] GPI: iterate over parent if VPI gen scope array not found Icarus does not support vpiGenScopeArray, so vpi_handle_by_name lookup fails when trying to access pseudo-region. Iterate over parent handle to check if a vpiGenScope object with a matching prefix exists, and create pseudo-region if it does. For example, if searching for dut.genblk by name in Icarus, no handle will be found. However, dut.genblk[0] is discovered by iteration so the pseudo-region can be inferred. Update newsfragment. --- cocotb/handle.py | 4 -- cocotb/share/lib/vpi/VpiImpl.cpp | 50 +++++++++++++++++-- .../source/newsfragments/2079.bugfix.rst | 2 +- 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index a53c25cd..f5035ed8 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -257,10 +257,6 @@ def __get_sub_handle_by_name(self, name): except KeyError: pass - if not self._discovered: - self._discover_all() - return self.__get_sub_handle_by_name(name) - # Cache to avoid a call to the simulator if we already know the name is # invalid. Unclear if we care, but we had this before. if name in self._invalid_sub_handles: diff --git a/cocotb/share/lib/vpi/VpiImpl.cpp b/cocotb/share/lib/vpi/VpiImpl.cpp index e574504f..a5a9de7c 100644 --- a/cocotb/share/lib/vpi/VpiImpl.cpp +++ b/cocotb/share/lib/vpi/VpiImpl.cpp @@ -26,6 +26,7 @@ ******************************************************************************/ #include "VpiImpl.h" +#include #include // COCOTB_UNUSED extern "C" { @@ -296,13 +297,52 @@ GpiObjHdl* VpiImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) { vpiHandle new_hdl; + const vpiHandle parent_hdl = parent->get_handle(); std::string fq_name = parent->get_fullname() + "." + name; - std::vector writable(fq_name.begin(), fq_name.end()); - writable.push_back('\0'); - new_hdl = vpi_handle_by_name(&writable[0], NULL); + new_hdl = vpi_handle_by_name(const_cast(fq_name.c_str()), NULL); + +#ifdef ICARUS + /* Icarus does not support vpiGenScopeArray, only vpiGenScope. + * If handle is not found by name, look for a generate block with + * a matching prefix. + * For Example: + * genvar idx; + * generate + * for (idx = 0; idx < 5; idx = idx + 1) begin + * ... + * end + * endgenerate + * + * genblk1 => vpiGenScopeArray (not found) + * genblk1[0] => vpiGenScope + * ... + * genblk1[4] => vpiGenScope + * + * genblk1 is not found directly, but if genblk1[n] is found, + * genblk1 must exist, so create the pseudo-region object for it. + */ + if (new_hdl == NULL) { + vpiHandle iter = vpi_iterate(vpiInternalScope, parent_hdl); + if (iter == NULL) { + goto skip_iterate; + } + + for (auto rgn = vpi_scan(iter); rgn != NULL; rgn = vpi_scan(iter)) { + if (vpi_get(vpiType, rgn) == vpiGenScope) { + auto rgn_name = vpi_get_str(vpiName, rgn); + /* Check if name is a prefix of rgn_name */ + if (rgn_name && name.length() > 0 && std::strncmp(name.c_str(), rgn_name, name.length()) == 0) { + new_hdl = parent_hdl; + vpi_free_object(iter); + break; + } + } + } + } +skip_iterate: +#endif - /* No need to iterate to look for generate loops as the tools will at least find vpiGenScopeArray */ if (new_hdl == NULL) { LOG_DEBUG("Unable to query vpi_get_handle_by_name %s", fq_name.c_str()); return NULL; @@ -319,7 +359,7 @@ GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) if (vpi_get(vpiType, new_hdl) == vpiGenScopeArray) { vpi_free_object(new_hdl); - new_hdl = parent->get_handle(); + new_hdl = parent_hdl; } diff --git a/documentation/source/newsfragments/2079.bugfix.rst b/documentation/source/newsfragments/2079.bugfix.rst index ba12193b..88b7336e 100644 --- a/documentation/source/newsfragments/2079.bugfix.rst +++ b/documentation/source/newsfragments/2079.bugfix.rst @@ -1,4 +1,4 @@ -Generate blocks are now accessible directly via lookup without having to iterate over parent handle. (:pr:`2079`) +In :ref:`Icarus Verilog `, generate blocks are now accessible directly via lookup without having to iterate over parent handle. (:pr:`2079`, :pr:`2143`) .. code-block:: python3 From a3eaeb8c3870df157aa906014a2e0152fe3fe20c Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 26 Oct 2020 19:26:22 +0100 Subject: [PATCH 2551/2656] Define TOPLEVEL_LANG before checking it Without this change, leaving TOPLEVEL_LANG unset would lead to an error. --- examples/mixed_language/tests/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/mixed_language/tests/Makefile b/examples/mixed_language/tests/Makefile index d9afcdd4..723a89ca 100644 --- a/examples/mixed_language/tests/Makefile +++ b/examples/mixed_language/tests/Makefile @@ -1,3 +1,6 @@ +# Override this variable to use a VHDL toplevel instead of SystemVerilog +TOPLEVEL_LANG ?= verilog + ifeq ($(SIM),) all: @echo "Skipping example mixed_language since Icarus doesn't support this" @@ -12,9 +15,6 @@ all: clean:: else -# Override this variable to use a VHDL toplevel instead of SystemVerilog -TOPLEVEL_LANG ?= verilog - TOPLEVEL = endian_swapper_mixed PWD=$(shell pwd) From be5ff392600a45aecc644c663c2e04ecf9846665 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 26 Oct 2020 19:16:43 +0100 Subject: [PATCH 2552/2656] Use absolute path for VCS and compile as VHDL-93 for Xcelium --- tests/test_cases/test_fatal/Makefile | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/test_cases/test_fatal/Makefile b/tests/test_cases/test_fatal/Makefile index d74c369e..9f3a7804 100644 --- a/tests/test_cases/test_fatal/Makefile +++ b/tests/test_cases/test_fatal/Makefile @@ -7,10 +7,15 @@ TOPLEVEL_LANG ?= verilog TOPLEVEL := fatal MODULE := test_fatal +PWD=$(shell pwd) + ifeq ($(TOPLEVEL_LANG),verilog) - VERILOG_SOURCES := fatal.sv + VERILOG_SOURCES := $(PWD)/fatal.sv else ifeq ($(TOPLEVEL_LANG),vhdl) - VHDL_SOURCES := fatal.vhd + VHDL_SOURCES := $(PWD)/fatal.vhd + ifneq ($(filter $(SIM),ius xcelium),) + SIM_ARGS += -v93 + endif endif # squash error code from simulator and ensure the cocotb test passed From c26420944587fcec865211e534fcb9165af0aa55 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 26 Oct 2020 18:59:17 +0100 Subject: [PATCH 2553/2656] Fix lexer name for doctest examples See https://pygments.org/docs/lexers/#pygments.lexers.python.PythonConsoleLexer --- documentation/source/quickstart.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index e9300296..5e073d57 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -181,11 +181,10 @@ Accessing the :attr:`~cocotb.handle.NonHierarchyObject.value` property of a hand Any unresolved bits are preserved and can be accessed using the :attr:`~cocotb.binary.BinaryValue.binstr` attribute, or a resolved integer value can be accessed using the :attr:`~cocotb.binary.BinaryValue.integer` attribute. -.. code-block:: python3 +.. code-block:: pycon >>> # Read a value back from the DUT >>> count = dut.counter.value - >>> >>> print(count.binstr) 1X1010 >>> # Resolve the value to an integer (X or Z treated as 0) @@ -197,7 +196,7 @@ or a resolved integer value can be accessed using the :attr:`~cocotb.binary.Bina We can also cast the signal handle directly to an integer: -.. code-block:: python3 +.. code-block:: pycon >>> print(int(dut.counter)) 42 From b2549a825e095dc78a4fe60d93077db10a1cdea8 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 26 Oct 2020 22:44:51 +0100 Subject: [PATCH 2554/2656] Mention LD_DEBUG=libs in Troubleshooting section (#2161) Mention LD_DEBUG=libs in Troubleshooting section Co-authored-by: Kaleb Barrett --- documentation/source/troubleshooting.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/documentation/source/troubleshooting.rst b/documentation/source/troubleshooting.rst index 6032e5d0..c779fad0 100644 --- a/documentation/source/troubleshooting.rst +++ b/documentation/source/troubleshooting.rst @@ -5,8 +5,8 @@ Troubleshooting Simulation Hangs ================ -Did you directly call a function that is decorated as a :class:`~cocotb.coroutine`, -i.e. without using :keyword:`await` or :keyword:`yield`? +Did you directly call an ``async def`` function without using :keyword:`await`; +or a :class:`~cocotb.coroutine` without using :keyword:`yield`? Increasing Verbosity @@ -156,4 +156,8 @@ Cocotb builds binary libraries during its installation process. These libraries are tailored to the installation of Python used when installing cocotb. When switching between Python installations, cocotb needs to be re-installed without using cached build artifacts, e.g. with ``pip install --no-cache-dir cocotb``. +On Linux distributions, setting ``LD_DEBUG=libs`` (example: ``LD_DEBUG=libs make SIM=verilator``) prints detailed output about which libraries are loaded from where. +On Mac OS, you can use ``DYLD_PRINT_LIBRARIES=1`` instead of ``LD_DEBUG=libs`` to get similar information. +On Windows, use `Process Explorer `_. + Further details are available in :issue:`1943`. From e661dc6dd9bae33e8b5dcc4d170917a3b6cf328a Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 26 Oct 2020 15:19:49 -0500 Subject: [PATCH 2555/2656] Update recursive_discovery for Questa newly found objects --- tests/test_cases/test_iteration_mixedlang/test_iteration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/test_iteration_mixedlang/test_iteration.py b/tests/test_cases/test_iteration_mixedlang/test_iteration.py index 9db0da5e..311c29e5 100644 --- a/tests/test_cases/test_iteration_mixedlang/test_iteration.py +++ b/tests/test_cases/test_iteration_mixedlang/test_iteration.py @@ -75,7 +75,7 @@ async def recursive_discovery(dut): # vpiAlways = 31 and vpiStructVar = 2 do not show up in IUS/Xcelium pass_total = 975 elif cocotb.SIM_NAME.lower().startswith(("modelsim")): - pass_total = 933 + pass_total = 991 else: pass_total = 1024 From b21111617f9eaa5dafbf94b5ef75da29c31bcccc Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 30 Oct 2020 15:43:57 -0500 Subject: [PATCH 2556/2656] Improve contributors documentation on running tests locally (#2167) * Use normal heading syntax in CONTRIBUTING.md * Add section about setting up a development environment to CONTRIBUTING.md * Minor improvements to tox.ini * Added 'docs' env to envlist so it shows up in `tox -l`. * Added platform key to skip running tests if an invalid platform is selected. It will print a wanring about it, so the user should be able to diagnose the error. * Changed how install_command is specialized for windows targets so we don't have to repeat all the python names, and so we can implicitly support other python versions like pypy3-windows. * Improve section of CONTRIBUTING.md on running tests/docs locally Co-authored-by: Colin Marquardt --- CONTRIBUTING.md | 136 +++++++++++++++++++++++++++++++++++++++--------- tox.ini | 19 +++++-- 2 files changed, 126 insertions(+), 29 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c7278158..bb35d026 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,13 +1,115 @@ -Cocotb Contribution Guidelines -============================== +# Cocotb Contribution Guidelines Welcome to the cocotb development! We are an inclusive community with the common goal of improving the cocotb, a coroutine based cosimulation library for writing VHDL and Verilog testbenches in Python. This guide explains how to contribute to cocotb, and documents the processes we agreed on to manage the project. All processes in this document are designed to streamline the development effort, to avoid bottlenecks, and to ultimately give a pleasant experience to all involved. -Architecture and Scope of Cocotb --------------------------------- + +## Setting Up a Development Environment + +Assuming you have used cocotb prior to reading this guide, you will already have the cocotb [installation prerequisites](https://docs.cocotb.org/en/latest/install.html) and standard development tools (editor, shell, git, etc.) installed. + +Additionally, you will need [doxygen](https://www.doxygen.nl/index.html), for building documentation, and [tox](https://pypi.org/project/tox/), for building documentation and running regression tests. + +We recommend if you are using a Linux distribution to use your system package manager to install doxygen. +Likewise, doxygen can be installed using the homebrew package manager on Mac OS. +Windows contributors should download a binary distribution installer from the main website. + +`tox` is a Python project and can be installed with `pip`: + +```command +pip install tox +``` + +Finally, you should [fork and clone](https://guides.github.com/activities/forking/) the cocotb repo. +This will allow you to make changes to the cocotb source code, and run regressions and build documentation locally. + +Now you are ready to contribute! + + +## Running Tests Locally + +First, [set up your development environment](#setting-up-a-development-environment). + +Our tests are managed by `tox`, which runs both `pytest` tests and our system of makefiles. +The regression does not end on the first failure, but continues until all tests in the `/tests` and `/examples` directories have been run. + +To run the tests locally with `tox`, you will need to select an appropriate test environment. +Valid test environments are formatted as `{your python version}-{your OS}`. +Valid python version values are `py35`, `py36`, `py37`, `py38`, or `py39`; +and valid OS values are `linux`, `macos`, or `windows`. +For example, a valid test environment is `py38-linux`. +You can see the list of valid test environments by running the below command: + +```command +tox -l +``` + +Once you know the test environment you wish to use, call `tox`. + +```command +tox -e py38-linux +``` + +At the end of the regression, if there were any test failures, the tests that failed will be printed. +Otherwise, tox will print a green `:)`. + +### Selecting a Language and Simulator for Regression + +`tox` supports the usage of the environment variables [`SIM`](https://docs.cocotb.org/en/latest/building.html#var-SIM) and [`TOPLEVEL_LANG`](https://docs.cocotb.org/en/latest/building.html#var-TOPLEVEL_LANG) to select a simulator and language to run the regression. +By default the tests will attempt to run with the Icarus Verilog simulator. + +For example, if you wanted to run tests with GHDL on Linux with Python 3.8, you would issue the following command: + +```command +SIM=ghdl TOPLEVEL_LANG=vhdl tox -e py38-linux +``` + +### Running Individual Tests Locally + +Each test under `/tests/test_cases/*/` and `/examples/*/tests/` can be run individually. +This is particularly useful if you want to run a particular test that fails the regression. + +First you must install cocotb from source by navigating to the project root directory and issuing the following command: + +```command +python -m pip install . +``` + +On Windows, you must instead install cocotb from source like so: + +```command +python -m pip install --global-option build_ext --global-option --compiler=mingw32 . +``` + +Once that has been done, you can navigate to the directory containing the test you wish to run. +Then you may issue an [appropriate]https://docs.cocotb.org/en/latest/building.html#makefile-based-test-scripts) `make` command: + +```command +make SIM=icarus +``` + + +## Building Documentation Locally + +First, [set up your development environment](#setting-up-a-development-environment). + +Documentation is built locally using `tox`. +The last message in the output will contain a URL to the documentation you just built. +Simply copy and paste the link into your browser to view it. +The documentation will be built in the same location on your hard drive on every run, so you only have to refresh the page to see new changes. + +To build the documentation locally on Linux or Mac, issue the following command: + +```command +tox -e docs +``` + +Building the documentation is not currently supported on Windows. + + +## Architecture and Scope of Cocotb Cocotb has seen adoption in a wide variety of scenarios with sometimes conflicting requirements. To foster experimentation and to decentralize the development process the architecture of cocotb is highly modular. @@ -24,8 +126,7 @@ However, none of these rules are set in stone. They can and should be challenged at times to ensure the project stays relevant to the majority of its users. -How to Get Changes Merged -------------------------- +## How to Get Changes Merged Have you fixed a bug in cocotb, or want to add new functionality to it? Cocotb follows the typical [GitHub flow](https://guides.github.com/introduction/flow/) and makes use of pull requests and reviews. @@ -49,8 +150,7 @@ Follow the steps below to get your changes merged, i.e. integrated into the main 8. Once your code has at least one positive review from a maintainer and no maintainer strongly objects it your code is ready to be merged into the `master` branch. -Patch Requirements ------------------- +## Patch Requirements All changes which should go into the main codebase of cocotb must follow this set of requirements. @@ -85,16 +185,7 @@ All changes which should go into the main codebase of cocotb must follow this se # SPDX-License-Identifier: CC0-1.0 ``` -Running tests locally ---------------------- - -Our tests are managed by `tox`, which runs both `pytest` and our system of makefiles. -This exercises the contents of both the `tests` and `examples` directories. -`tox` supports the usage of the environment variables `SIM` and `TOPLEVEL_LANG` to direct how to run the regression. - - -Managing of Issues and Pull Requests ------------------------------------- +## Managing of Issues and Pull Requests The cocotb project makes use of GitHub labels attached to issues and pull requests to structure the development process. Each issue and pull request can have multiple labels assigned. @@ -160,15 +251,13 @@ cocotb explicitly uses no priority labels, as experience indicates that they pro Issues and pull requests which are invalid, or where feedback is lacking for four weeks, should be closed. -Cocotb Releases ---------------- +## Cocotb Releases cocotb aims to keep the `master` branch always in a releasable state. At least four times a year an official release should be created. It is the job of the maintainers to find a suitable time for a release, to communicate it to the community, and to coordinate it. -Maintainers ------------ +## Maintainers Cocotb uses a shared maintainer model. Most maintainers are experts in part of the cocotb codebase, and are primarily responsible for reviews in this area. @@ -185,8 +274,7 @@ Founders - Chris Higgs (@chiggs) - Stuart Hodgson (@stuarthodgson) -Code of Conduct ---------------- +## Code of Conduct The cocotb development community aims to be welcoming to everyone. The [FOSSi Foundation Code of Conduct](https://www.fossi-foundation.org/code-of-conduct) applies. diff --git a/tox.ini b/tox.ini index ebd1414a..c598ada1 100644 --- a/tox.ini +++ b/tox.ini @@ -1,17 +1,26 @@ [tox] -envlist = py{35,36,37,38,39}-{linux,macos,windows} +# when changing this list, adapt CONTRIBUTING.md accordingly: +envlist = py{35,36,37,38,39}-{linux,macos,windows},docs + # for the requires key minversion = 3.2.0 + # virtualenv is used by tox; versions below 16.1.0 cause a DeprecationWarning # to be shown for all code which uses virtualenv, which is the case for all code # we run through tox. (https://github.com/pypa/virtualenv/pull/1064) requires = virtualenv >= 16.1 [testenv] +platform = + linux: linux|cygwin + macos: darwin + windows: win32 + setenv = CFLAGS = -Werror -Wno-deprecated-declarations CXXFLAGS = -Werror COCOTB_LIBRARY_COVERAGE = 1 + passenv = SIM TOPLEVEL_LANG @@ -25,6 +34,10 @@ deps = pytest pytest-cov +install_command = + windows: python -m pip install --global-option build_ext --global-option --compiler=mingw32 {opts} {packages} + python -m pip install {opts} {packages} + commands = pytest make test @@ -37,10 +50,6 @@ whitelist_externals = # needed for coverage to work usedevelop=True -[testenv:py{35,36,37,38,39}-windows] -install_command = - python -m pip install --global-option build_ext --global-option --compiler=mingw32 {opts} {packages} - # Note: this target is *not* used by Read The Docs, it runs sphinx-build # directly. Hence, all special build steps here are only relevant for # local builds, not for RTD builds. Add all build steps which should be executed From 0312889dd1568ca60d0caa23c7553f217c74437a Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 26 Oct 2020 19:29:08 +0100 Subject: [PATCH 2557/2656] Convert examples to use assert --- .../tests/test_axi_lite_slave.py | 47 +++++++------------ .../tests/test_endian_swapper.py | 8 +--- .../tests/test_mixed_language.py | 10 ++-- 3 files changed, 21 insertions(+), 44 deletions(-) diff --git a/examples/axi_lite_slave/tests/test_axi_lite_slave.py b/examples/axi_lite_slave/tests/test_axi_lite_slave.py index 837cc27f..80add4c3 100644 --- a/examples/axi_lite_slave/tests/test_axi_lite_slave.py +++ b/examples/axi_lite_slave/tests/test_axi_lite_slave.py @@ -1,7 +1,5 @@ import os import cocotb -from cocotb.result import TestFailure -from cocotb.result import TestSuccess from cocotb.clock import Clock from cocotb.triggers import Timer from cocotb.drivers.amba import AXI4LiteMaster @@ -44,11 +42,8 @@ async def write_address_0(dut): await Timer(CLK_PERIOD_NS * 10, units='ns') value = dut.dut.r_temp_0 - if value != DATA: - # Fail - raise TestFailure("Register at address 0x%08X should have been: \ - 0x%08X but was 0x%08X" % (ADDRESS, DATA, int(value))) - + assert value == DATA, ("Register at address 0x%08X should have been " + "0x%08X but was 0x%08X" % (ADDRESS, DATA, int(value))) dut.log.info("Write 0x%08X to address 0x%08X" % (int(value), ADDRESS)) @@ -81,11 +76,8 @@ async def read_address_1(dut): value = await axim.read(ADDRESS) await Timer(CLK_PERIOD_NS * 10, units='ns') - if value != DATA: - # Fail - raise TestFailure("Register at address 0x%08X should have been: \ - 0x%08X but was 0x%08X" % (ADDRESS, DATA, int(value))) - + assert value == DATA, ("Register at address 0x%08X should have been " + "0x%08X but was 0x%08X" % (ADDRESS, DATA, int(value))) dut._log.info("Read: 0x%08X From Address: 0x%08X" % (int(value), ADDRESS)) @@ -120,24 +112,20 @@ async def write_and_read(dut): await Timer(CLK_PERIOD_NS * 10, units='ns') value = dut.dut.r_temp_0 - if value != DATA: - # Fail - raise TestFailure("Register at address 0x%08X should have been: \ - 0x%08X but was 0x%08X" % (ADDRESS, DATA, int(value))) - + assert value == DATA, ("Register at address 0x%08X should have been " + "0x%08X but was 0x%08X" % (ADDRESS, DATA, int(value))) dut._log.info("Write 0x%08X to address 0x%08X" % (int(value), ADDRESS)) @cocotb.test() async def write_fail(dut): - """Attempt to write data to an address that doesn't exist. This test - should fail. + """Attempt to write data to an address that doesn't exist. Test ID: 3 Expected Results: - The AXIML bus should throw an exception because the user attempted - to write to an invalid address. + The AXIML bus should throw an AXIProtocolError exception because + the user attempted to write to an invalid address. """ # Reset dut.rst <= 1 @@ -156,21 +144,19 @@ async def write_fail(dut): except AXIProtocolError as e: print("Exception: %s" % str(e)) dut._log.info("Bus successfully raised an error") - raise TestSuccess() - raise TestFailure("AXI bus should have raised an error when writing to \ - an invalid address") + else: + assert False, "AXI bus should have raised an error when writing to an invalid address" @cocotb.test() async def read_fail(dut): - """Attempt to read data from an address that doesn't exist. This test - should fail. + """Attempt to read data from an address that doesn't exist. Test ID: 4 Expected Results: - The AXIML bus should throw an exception because the user attempted - to read from an invalid address. + The AXIML bus should throw an AXIProtocolError exception because + the user attempted to read from an invalid address. """ # Reset dut.rst <= 1 @@ -189,6 +175,5 @@ async def read_fail(dut): except AXIProtocolError as e: print("Exception: %s" % str(e)) dut._log.info("Bus Successfully Raised an Error") - raise TestSuccess() - raise TestFailure("AXI bus should have raised an error when reading from \ - an invalid address") + else: + assert False, "AXI bus should have raised an error when reading from an invalid address" diff --git a/examples/endian_swapper/tests/test_endian_swapper.py b/examples/endian_swapper/tests/test_endian_swapper.py index 0af5dc51..66938bbe 100644 --- a/examples/endian_swapper/tests/test_endian_swapper.py +++ b/examples/endian_swapper/tests/test_endian_swapper.py @@ -39,7 +39,6 @@ from cocotb.monitors.avalon import AvalonSTPkts as AvalonSTMonitor from cocotb.regression import TestFactory from cocotb.scoreboard import Scoreboard -from cocotb.result import TestFailure # Data generators with warnings.catch_warnings(): @@ -142,11 +141,8 @@ async def run_test(dut, data_in=None, config_coroutine=None, idle_inserter=None, pkt_count = await tb.csr.read(1) - if pkt_count.integer != tb.pkts_sent: - raise TestFailure("DUT recorded %d packets but tb counted %d" % ( - pkt_count.integer, tb.pkts_sent)) - else: - dut._log.info("DUT correctly counted %d packets" % pkt_count.integer) + assert pkt_count.integer == tb.pkts_sent, "DUT recorded %d packets but tb counted %d" % (pkt_count.integer, tb.pkts_sent) + dut._log.info("DUT correctly counted %d packets" % pkt_count.integer) raise tb.scoreboard.result diff --git a/examples/mixed_language/tests/test_mixed_language.py b/examples/mixed_language/tests/test_mixed_language.py index 85a8ee51..a1cc90d2 100644 --- a/examples/mixed_language/tests/test_mixed_language.py +++ b/examples/mixed_language/tests/test_mixed_language.py @@ -1,6 +1,5 @@ import cocotb from cocotb.triggers import Timer -from cocotb.result import TestFailure @cocotb.test() @@ -20,11 +19,8 @@ async def mixed_language_test(dut): vhdl.reset_n <= 1 await Timer(100, units='ns') - if int(verilog.reset_n) == int(vhdl.reset_n): - dut._log.info("Both signals read as %d" % int(vhdl.reset_n)) - else: - raise TestFailure("reset_n signals were different") + assert int(verilog.reset_n) == int(vhdl.reset_n), "reset_n signals were different" # Try accessing an object other than a port... - verilog_flush = str(verilog.flush_pipe) - vhdl_flush = str(vhdl.flush_pipe) + verilog_flush = str(verilog.flush_pipe.value) + vhdl_flush = str(vhdl.flush_pipe.value) From 2fe93360d5bcf21bbf3756b48a0177c18e780b06 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 2 Nov 2020 22:32:11 +0100 Subject: [PATCH 2558/2656] Convert test_cocotb to use assert --- tests/test_cases/test_cocotb/common.py | 20 ++++---- tests/test_cases/test_cocotb/test_clock.py | 5 +- .../test_cocotb/test_edge_triggers.py | 46 ++++++------------- .../test_cocotb/test_generator_coroutines.py | 14 +++--- tests/test_cases/test_cocotb/test_handle.py | 14 ++---- .../test_cases/test_cocotb/test_scheduler.py | 9 ++-- tests/test_cases/test_cocotb/test_tests.py | 2 +- 7 files changed, 38 insertions(+), 72 deletions(-) diff --git a/tests/test_cases/test_cocotb/common.py b/tests/test_cases/test_cocotb/common.py index aa86831c..502aec0c 100644 --- a/tests/test_cases/test_cocotb/common.py +++ b/tests/test_cases/test_cocotb/common.py @@ -9,7 +9,6 @@ from contextlib import contextmanager import cocotb -from cocotb.result import TestFailure from cocotb.triggers import Timer @@ -31,17 +30,14 @@ def _check_traceback(running_coro, exc_type, pattern): except exc_type: tb_text = traceback.format_exc() else: - raise TestFailure("Exception was not raised") + assert False, "Exception was not raised" - if not re.match(pattern, tb_text): - raise TestFailure( - ( - "Traceback didn't match - got:\n\n" - "{}\n" - "which did not match the pattern:\n\n" - "{}" - ).format(tb_text, pattern) - ) + assert re.match(pattern, tb_text), ( + "Traceback didn't match - got:\n\n" + "{}\n" + "which did not match the pattern:\n\n" + "{}" + ).format(tb_text, pattern) @contextmanager @@ -54,4 +50,4 @@ def assert_raises(exc_type, pattern=None): "Correct exception type caught, but message did not match pattern" pass else: - raise AssertionError("{} was not raised".format(exc_type.__name__)) + assert False, "{} was not raised".format(exc_type.__name__) diff --git a/tests/test_cases/test_cocotb/test_clock.py b/tests/test_cases/test_cocotb/test_clock.py index 9c2e038b..61c4cec3 100644 --- a/tests/test_cases/test_cocotb/test_clock.py +++ b/tests/test_cases/test_cocotb/test_clock.py @@ -71,9 +71,8 @@ def test_clock_with_units(dut): @cocotb.test(expect_fail=False) -def test_anternal_clock(dut): - """Test ability to yield on an external non cocotb coroutine decorated - function""" +def test_external_clock(dut): + """Test ability to yield on an external non-cocotb coroutine decorated function""" clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) count = 0 while count != 100: diff --git a/tests/test_cases/test_cocotb/test_edge_triggers.py b/tests/test_cases/test_cocotb/test_edge_triggers.py index e7a01fb8..6bd6f6d9 100644 --- a/tests/test_cases/test_cocotb/test_edge_triggers.py +++ b/tests/test_cases/test_cocotb/test_edge_triggers.py @@ -12,7 +12,6 @@ import cocotb from cocotb.triggers import RisingEdge, FallingEdge, Edge, Timer, ClockCycles from cocotb.clock import Clock -from cocotb.result import TestError, TestFailure @cocotb.coroutine @@ -30,16 +29,14 @@ def do_single_edge_check(dut, level): """Do test for rising edge""" old_value = dut.clk.value.integer dut._log.info("Value of %s is %d" % (dut.clk._path, old_value)) - if old_value is level: - raise TestError("%s not to %d start with" % (dut.clk._path, not level)) + assert old_value != level, "%s not to %d start with" % (dut.clk._path, not level) if level == 1: yield RisingEdge(dut.clk) else: yield FallingEdge(dut.clk) new_value = dut.clk.value.integer dut._log.info("Value of %s is %d" % (dut.clk._path, new_value)) - if new_value is not level: - raise TestError("%s not %d at end" % (dut.clk._path, level)) + assert new_value == level, "%s not %d at end" % (dut.clk._path, level) @cocotb.test() @@ -52,8 +49,7 @@ def test_rising_edge(dut): dut.clk <= 1 fail_timer = Timer(1000) result = yield [fail_timer, test.join()] - if result is fail_timer: - raise TestError("Test timed out") + assert result is not fail_timer, "Test timed out" @cocotb.test() @@ -66,8 +62,7 @@ def test_falling_edge(dut): dut.clk <= 0 fail_timer = Timer(1000) result = yield [fail_timer, test.join()] - if result is fail_timer: - raise TestError("Test timed out") + assert result is not fail_timer, "Test timed out" @cocotb.test() @@ -77,33 +72,27 @@ def test_either_edge(dut): yield Timer(1) dut.clk <= 1 yield Edge(dut.clk) - if dut.clk.value.integer != 1: - raise TestError("Value should be 0") + assert dut.clk.value.integer == 1 yield Timer(10) dut.clk <= 0 yield Edge(dut.clk) - if dut.clk.value.integer != 0: - raise TestError("Value should be 0") + assert dut.clk.value.integer == 0 yield Timer(10) dut.clk <= 1 yield Edge(dut.clk) - if dut.clk.value.integer != 1: - raise TestError("Value should be 0") + assert dut.clk.value.integer == 1 yield Timer(10) dut.clk <= 0 yield Edge(dut.clk) - if dut.clk.value.integer != 0: - raise TestError("Value should be 0") + assert dut.clk.value.integer == 0 yield Timer(10) dut.clk <= 1 yield Edge(dut.clk) - if dut.clk.value.integer != 1: - raise TestError("Value should be 0") + assert dut.clk.value.integer == 1 yield Timer(10) dut.clk <= 0 yield Edge(dut.clk) - if dut.clk.value.integer != 0: - raise TestError("Value should be 0") + assert dut.clk.value.integer == 0 @cocotb.test() @@ -120,19 +109,14 @@ def test_fork_and_monitor(dut, period=1000, clocks=6): while True: result = yield [timer, task.join()] - if count > expect: - raise TestFailure("Task didn't complete in expected time") + assert count <= expect, "Task didn't complete in expected time" if result is timer: dut._log.info("Count %d: Task still running" % count) count += 1 else: break - if count != expect: - raise TestFailure("Expected to monitor the task %d times but got %d" % - (expect, count)) - if result != clocks: - raise TestFailure("Expected task to return %d but got %s" % - (clocks, repr(result))) + assert count == expect, "Expected to monitor the task %d times but got %d" % (expect, count) + assert result == clocks, "Expected task to return %d but got %s" % (clocks, repr(result)) @cocotb.coroutine @@ -169,9 +153,7 @@ def test_edge_count(dut): yield Timer(clk_period * (edge_count + 1)) - if edge_count is not edges_seen: - raise TestFailure("Correct edge count failed - saw %d wanted %d" % - (edges_seen, edge_count)) + assert edge_count == edges_seen, "Correct edge count failed - saw %d, wanted %d" % (edges_seen, edge_count) @cocotb.test() diff --git a/tests/test_cases/test_cocotb/test_generator_coroutines.py b/tests/test_cases/test_cocotb/test_generator_coroutines.py index 8f530e4a..56ea4da6 100644 --- a/tests/test_cases/test_cocotb/test_generator_coroutines.py +++ b/tests/test_cases/test_cocotb/test_generator_coroutines.py @@ -6,7 +6,6 @@ """ import cocotb from cocotb.triggers import Timer -from cocotb.result import TestFailure from common import clock_gen, _check_traceback import textwrap @@ -36,7 +35,7 @@ def test_function_not_a_coroutine(dut): except TypeError as exc: assert "isn't a valid coroutine" in str(exc) else: - raise TestFailure + assert False def normal_function(dut): @@ -51,7 +50,7 @@ def test_function_not_decorated(dut): assert "yielded" in str(exc) assert "scheduler can't handle" in str(exc) else: - raise TestFailure + assert False @cocotb.test() @@ -63,7 +62,7 @@ def test_function_not_decorated_fork(dut): except TypeError as exc: assert "isn't a coroutine" in str(exc) else: - raise TestFailure() + assert False yield Timer(500) @@ -78,7 +77,7 @@ def test_adding_a_coroutine_without_starting(dut): except TypeError as exc: assert "a coroutine that hasn't started" in str(exc) else: - raise TestFailure + assert False @cocotb.test(expect_fail=False) @@ -123,8 +122,7 @@ def return_it(x): yield ret = yield return_it(42) - if ret != 42: - raise TestFailure("Return statement did not work") + assert ret == 42, "Return statement did not work" @cocotb.test() @@ -149,7 +147,7 @@ def immediate_exception(): except ValueError: pass else: - raise TestFailure("Exception was not raised") + assert False, "Exception was not raised" @cocotb.test() diff --git a/tests/test_cases/test_cocotb/test_handle.py b/tests/test_cases/test_cocotb/test_handle.py index f4dd3289..1ed13ed6 100644 --- a/tests/test_cases/test_cocotb/test_handle.py +++ b/tests/test_cases/test_cocotb/test_handle.py @@ -8,7 +8,6 @@ import random import cocotb from cocotb.triggers import Timer -from cocotb.result import TestFailure from common import assert_raises @@ -24,9 +23,7 @@ def test_lessthan_raises_error(dut): except TypeError: pass else: - raise TestFailure( - "No exception was raised when confusing comparison with assignment" - ) + assert False, "No exception was raised when confusing comparison with assignment" # to make this a generator if False: @@ -87,8 +84,7 @@ def test_integer(dut): got_out = int(dut.stream_in_int) log.info("dut.stream_out_int = %d" % got_out) log.info("dut.stream_in_int = %d" % got_in) - if got_in != got_out: - raise TestFailure("stream_in_int and stream_out_int should not match") + assert got_in == got_out, "stream_in_int and stream_out_int should not match" @cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) @@ -106,8 +102,7 @@ def test_real_assign_double(dut): yield Timer(1) # FIXME: Workaround for VHPI scheduling - needs investigation got = float(dut.stream_out_real) log.info("Read back value %g" % got) - if got != val: - raise TestFailure("Values didn't match!") + assert got == val, "Values didn't match!" @cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) @@ -124,8 +119,7 @@ def test_real_assign_int(dut): yield Timer(1) # FIXME: Workaround for VHPI scheduling - needs investigation got = dut.stream_out_real log.info("Read back value %d" % got) - if got != float(val): - raise TestFailure("Values didn't match!") + assert got == float(val), "Values didn't match!" # identifiers starting with `_` are illegal in VHDL diff --git a/tests/test_cases/test_cocotb/test_scheduler.py b/tests/test_cases/test_cocotb/test_scheduler.py index 30a0cc72..2880e6ef 100644 --- a/tests/test_cases/test_cocotb/test_scheduler.py +++ b/tests/test_cases/test_cocotb/test_scheduler.py @@ -13,7 +13,6 @@ import cocotb from cocotb.triggers import Join, Timer, RisingEdge, Trigger, NullTrigger, Combine, Event, ReadOnly, First -from cocotb.result import TestFailure from cocotb.clock import Clock from common import clock_gen @@ -37,11 +36,9 @@ def test_coroutine_kill(dut): clk_gen_two = cocotb.fork(clock_yield(clk_gen)) yield Timer(100) clk_gen.kill() - if test_flag is not False: - raise TestFailure + assert not test_flag yield Timer(1000) - if test_flag is not True: - raise TestFailure + assert test_flag @cocotb.coroutine @@ -163,7 +160,7 @@ def prime(self, callback): except RuntimeError as exc: assert "oops" in str(exc) else: - raise TestFailure + assert False @cocotb.test() diff --git a/tests/test_cases/test_cocotb/test_tests.py b/tests/test_cases/test_cocotb/test_tests.py index 391ad0b2..f915cfcd 100644 --- a/tests/test_cases/test_cocotb/test_tests.py +++ b/tests/test_cases/test_cocotb/test_tests.py @@ -35,7 +35,7 @@ def test_tests_are_tests(dut): @cocotb.test(expect_fail=True) async def test_async_test_can_fail(dut): await Timer(1) - raise TestFailure + raise TestFailure # explicitly do not use assert here @cocotb.test() From e991f1971953a52e41a6c0fa5eb6b54907f6f76a Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 22 Oct 2020 21:03:23 +0200 Subject: [PATCH 2559/2656] Convert more tests to async/await syntax Refs #1731 --- tests/test_cases/test_cocotb/common.py | 6 +- .../test_cocotb/test_async_coroutines.py | 2 +- .../test_cocotb/test_async_generators.py | 4 + tests/test_cases/test_cocotb/test_clock.py | 53 +++---- .../test_concurrency_primitives.py | 67 ++++----- .../test_cocotb/test_edge_triggers.py | 114 +++++++-------- .../test_cocotb/test_generator_coroutines.py | 2 +- tests/test_cases/test_cocotb/test_handle.py | 30 ++-- tests/test_cases/test_cocotb/test_logging.py | 9 +- .../test_cases/test_cocotb/test_scheduler.py | 101 +++++++------- tests/test_cases/test_cocotb/test_tests.py | 35 ++--- .../test_cocotb/test_timing_triggers.py | 132 ++++++++---------- 12 files changed, 257 insertions(+), 298 deletions(-) diff --git a/tests/test_cases/test_cocotb/common.py b/tests/test_cases/test_cocotb/common.py index 502aec0c..02d53081 100644 --- a/tests/test_cases/test_cocotb/common.py +++ b/tests/test_cases/test_cocotb/common.py @@ -2,7 +2,7 @@ # Licensed under the Revised BSD License, see LICENSE for details. # SPDX-License-Identifier: BSD-3-Clause """ -Common utilities shared my many tests in this directory +Common utilities shared by many tests in this directory """ import re import traceback @@ -17,9 +17,9 @@ def clock_gen(clock): """Example clock gen for test use""" for i in range(5): clock <= 0 - yield Timer(100) + yield Timer(100, "ns") clock <= 1 - yield Timer(100) + yield Timer(100, "ns") clock._log.warning("Clock generator finished!") diff --git a/tests/test_cases/test_cocotb/test_async_coroutines.py b/tests/test_cases/test_cocotb/test_async_coroutines.py index 32de705a..fed97d6e 100644 --- a/tests/test_cases/test_cocotb/test_async_coroutines.py +++ b/tests/test_cases/test_cocotb/test_async_coroutines.py @@ -2,7 +2,7 @@ # Licensed under the Revised BSD License, see LICENSE for details. # SPDX-License-Identifier: BSD-3-Clause """ -Test function and substitutablility of async coroutines +Test function and substitutability of async coroutines """ import cocotb diff --git a/tests/test_cases/test_cocotb/test_async_generators.py b/tests/test_cases/test_cocotb/test_async_generators.py index 523ebc37..dde3c3fe 100644 --- a/tests/test_cases/test_cocotb/test_async_generators.py +++ b/tests/test_cases/test_cocotb/test_async_generators.py @@ -1,3 +1,7 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause + import cocotb diff --git a/tests/test_cases/test_cocotb/test_clock.py b/tests/test_cases/test_cocotb/test_clock.py index 61c4cec3..3fc28e7e 100644 --- a/tests/test_cases/test_cocotb/test_clock.py +++ b/tests/test_cases/test_cocotb/test_clock.py @@ -7,44 +7,36 @@ import cocotb from cocotb.clock import Clock from cocotb.triggers import Timer, RisingEdge -from cocotb.result import TestFailure from cocotb.utils import get_sim_time from math import isclose -@cocotb.test(expect_fail=False) -def test_clock_with_units(dut): +@cocotb.test() +async def test_clock_with_units(dut): clk_1mhz = Clock(dut.clk, 1.0, units='us') clk_250mhz = Clock(dut.clk, 4.0, units='ns') - if str(clk_1mhz) != "Clock(1.0 MHz)": - raise TestFailure("{} != 'Clock(1.0 MHz)'".format(str(clk_1mhz))) - else: - dut._log.info('Created clock >{}<'.format(str(clk_1mhz))) + assert str(clk_1mhz) == "Clock(1.0 MHz)" + dut._log.info('Created clock >{}<'.format(str(clk_1mhz))) - if str(clk_250mhz) != "Clock(250.0 MHz)": - raise TestFailure("{} != 'Clock(250.0 MHz)'".format(str(clk_250mhz))) - else: - dut._log.info('Created clock >{}<'.format(str(clk_250mhz))) + assert str(clk_250mhz) == "Clock(250.0 MHz)" + dut._log.info('Created clock >{}<'.format(str(clk_250mhz))) clk_gen = cocotb.fork(clk_1mhz.start()) start_time_ns = get_sim_time(units='ns') - yield Timer(1) - - yield RisingEdge(dut.clk) + await Timer(1, "ns") + await RisingEdge(dut.clk) edge_time_ns = get_sim_time(units='ns') - if not isclose(edge_time_ns, start_time_ns + 1000.0): - raise TestFailure("Expected a period of 1 us") + assert isclose(edge_time_ns, start_time_ns + 1000.0), "Expected a period of 1 us" start_time_ns = edge_time_ns - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) edge_time_ns = get_sim_time(units='ns') - if not isclose(edge_time_ns, start_time_ns + 1000.0): - raise TestFailure("Expected a period of 1 us") + assert isclose(edge_time_ns, start_time_ns + 1000.0), "Expected a period of 1 us" clk_gen.kill() @@ -52,30 +44,27 @@ def test_clock_with_units(dut): start_time_ns = get_sim_time(units='ns') - yield Timer(1) - - yield RisingEdge(dut.clk) + await Timer(1, "ns") + await RisingEdge(dut.clk) edge_time_ns = get_sim_time(units='ns') - if not isclose(edge_time_ns, start_time_ns + 4.0): - raise TestFailure("Expected a period of 4 ns") + assert isclose(edge_time_ns, start_time_ns + 4.0), "Expected a period of 4 ns" start_time_ns = edge_time_ns - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) edge_time_ns = get_sim_time(units='ns') - if not isclose(edge_time_ns, start_time_ns + 4.0): - raise TestFailure("Expected a period of 4 ns") + assert isclose(edge_time_ns, start_time_ns + 4.0), "Expected a period of 4 ns" clk_gen.kill() -@cocotb.test(expect_fail=False) -def test_external_clock(dut): - """Test ability to yield on an external non-cocotb coroutine decorated function""" - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) +@cocotb.test() +async def test_external_clock(dut): + """Test awaiting on an external non-cocotb coroutine decorated function""" + clk_gen = cocotb.fork(Clock(dut.clk, 100, "ns").start()) count = 0 while count != 100: - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) count += 1 clk_gen.kill() diff --git a/tests/test_cases/test_cocotb/test_concurrency_primitives.py b/tests/test_cases/test_cocotb/test_concurrency_primitives.py index ea50e5fa..58acb409 100644 --- a/tests/test_cases/test_cocotb/test_concurrency_primitives.py +++ b/tests/test_cases/test_cocotb/test_concurrency_primitives.py @@ -11,68 +11,63 @@ @cocotb.test() -def test_yield_list_stale(dut): - """ Test that a trigger yielded as part of a list can't cause a spurious wakeup """ +async def test_await_list_stale(dut): + """ Test that a trigger awaited as part of a list can't cause a spurious wakeup """ # gh-843 events = [Event() for i in range(3)] waiters = [e.wait() for e in events] - @cocotb.coroutine - def wait_for_lists(): - ret_i = waiters.index((yield [waiters[0], waiters[1]])) + async def wait_for_lists(): + ret_i = waiters.index((await First(waiters[0], waiters[1]))) assert ret_i == 0, "Expected event 0 to fire, not {}".format(ret_i) - ret_i = waiters.index((yield [waiters[2]])) + ret_i = waiters.index((await First(waiters[2]))) assert ret_i == 2, "Expected event 2 to fire, not {}".format(ret_i) - @cocotb.coroutine - def wait_for_e1(): + async def wait_for_e1(): """ wait on the event that didn't wake `wait_for_lists` """ - ret_i = waiters.index((yield waiters[1])) + ret_i = waiters.index((await waiters[1])) assert ret_i == 1, "Expected event 1 to fire, not {}".format(ret_i) - @cocotb.coroutine - def fire_events(): + async def fire_events(): """ fire the events in order """ for e in events: - yield Timer(1) + await Timer(1, "ns") e.set() fire_task = cocotb.fork(fire_events()) e1_task = cocotb.fork(wait_for_e1()) - yield wait_for_lists() + await wait_for_lists() # make sure the other tasks finish - yield fire_task.join() - yield e1_task.join() + await fire_task.join() + await e1_task.join() @cocotb.test() -def test_nested_first(dut): +async def test_nested_first(dut): """ Test that nested First triggers behave as expected """ events = [Event() for i in range(3)] waiters = [e.wait() for e in events] - @cocotb.coroutine - def fire_events(): + async def fire_events(): """ fire the events in order """ for e in events: - yield Timer(1) + await Timer(1, "ns") e.set() - @cocotb.coroutine - def wait_for_nested_first(): + async def wait_for_nested_first(): inner_first = First(waiters[0], waiters[1]) - ret = yield First(inner_first, waiters[2]) + ret = await First(inner_first, waiters[2]) # should unpack completely, rather than just by one level assert ret is not inner_first assert ret is waiters[0] fire_task = cocotb.fork(fire_events()) - yield wait_for_nested_first() - yield fire_task.join() + await wait_for_nested_first() + await fire_task.join() @cocotb.test() @@ -100,17 +95,16 @@ async def coro(): @cocotb.test() -def test_exceptions_first(dut): +async def test_exceptions_first(dut): """ Test exception propagation via cocotb.triggers.First """ @cocotb.coroutine def raise_inner(): - yield Timer(10) + yield Timer(10, "ns") raise ValueError('It is soon now') - @cocotb.coroutine - def raise_soon(): - yield Timer(1) - yield cocotb.triggers.First(raise_inner()) + async def raise_soon(): + await Timer(1, "ns") + await cocotb.triggers.First(raise_inner()) # it's ok to change this value if the traceback changes - just make sure # that when changed, it doesn't become harder to read. @@ -119,7 +113,7 @@ def raise_soon(): File ".*common\.py", line \d+, in _check_traceback yield running_coro File ".*test_concurrency_primitives\.py", line \d+, in raise_soon - yield cocotb\.triggers\.First\(raise_inner\(\)\) + await cocotb\.triggers\.First\(raise_inner\(\)\) File ".*triggers\.py", line \d+, in _wait return await first_trigger[^\n]* File ".*triggers.py", line \d+, in __await__ @@ -130,21 +124,20 @@ def raise_soon(): raise ValueError\('It is soon now'\) ValueError: It is soon now""").strip() - yield _check_traceback(raise_soon(), ValueError, expected) + await _check_traceback(raise_soon(), ValueError, expected) @cocotb.test() -def test_combine(dut): +async def test_combine(dut): """ Test the Combine trigger. """ # gh-852 - @cocotb.coroutine - def do_something(delay): - yield Timer(delay) + async def do_something(delay): + await Timer(delay, "ns") crs = [cocotb.fork(do_something(dly)) for dly in [10, 30, 20]] - yield Combine(*(cr.join() for cr in crs)) + await Combine(*(cr.join() for cr in crs)) @cocotb.test() diff --git a/tests/test_cases/test_cocotb/test_edge_triggers.py b/tests/test_cases/test_cocotb/test_edge_triggers.py index 6bd6f6d9..f14e6418 100644 --- a/tests/test_cases/test_cocotb/test_edge_triggers.py +++ b/tests/test_cases/test_cocotb/test_edge_triggers.py @@ -10,105 +10,105 @@ * ClockCycles """ import cocotb -from cocotb.triggers import RisingEdge, FallingEdge, Edge, Timer, ClockCycles +from cocotb.triggers import RisingEdge, FallingEdge, Edge, Timer, ClockCycles, First from cocotb.clock import Clock @cocotb.coroutine -def count_edges_cycles(signal, edges): +async def count_edges_cycles(signal, edges): edge = RisingEdge(signal) for i in range(edges): - yield edge + await edge signal._log.info("Rising edge %d detected" % i) signal._log.info("Finished, returning %d" % edges) return edges @cocotb.coroutine -def do_single_edge_check(dut, level): +async def do_single_edge_check(dut, level): """Do test for rising edge""" old_value = dut.clk.value.integer dut._log.info("Value of %s is %d" % (dut.clk._path, old_value)) assert old_value != level, "%s not to %d start with" % (dut.clk._path, not level) if level == 1: - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) else: - yield FallingEdge(dut.clk) + await FallingEdge(dut.clk) new_value = dut.clk.value.integer dut._log.info("Value of %s is %d" % (dut.clk._path, new_value)) assert new_value == level, "%s not %d at end" % (dut.clk._path, level) @cocotb.test() -def test_rising_edge(dut): - """Test that a rising edge can be yielded on""" +async def test_rising_edge(dut): + """Test that a rising edge can be awaited on""" dut.clk <= 0 - yield Timer(1) + await Timer(1, "ns") test = cocotb.fork(do_single_edge_check(dut, 1)) - yield Timer(10) + await Timer(10, "ns") dut.clk <= 1 - fail_timer = Timer(1000) - result = yield [fail_timer, test.join()] + fail_timer = Timer(1000, "ns") + result = await First(fail_timer, test.join()) assert result is not fail_timer, "Test timed out" @cocotb.test() -def test_falling_edge(dut): - """Test that a falling edge can be yielded on""" +async def test_falling_edge(dut): + """Test that a falling edge can be awaited on""" dut.clk <= 1 - yield Timer(1) + await Timer(1, "ns") test = cocotb.fork(do_single_edge_check(dut, 0)) - yield Timer(10) + await Timer(10, "ns") dut.clk <= 0 - fail_timer = Timer(1000) - result = yield [fail_timer, test.join()] + fail_timer = Timer(1000, "ns") + result = await First(fail_timer, test.join()) assert result is not fail_timer, "Test timed out" @cocotb.test() -def test_either_edge(dut): +async def test_either_edge(dut): """Test that either edge can be triggered on""" dut.clk <= 0 - yield Timer(1) + await Timer(1, "ns") dut.clk <= 1 - yield Edge(dut.clk) + await Edge(dut.clk) assert dut.clk.value.integer == 1 - yield Timer(10) + await Timer(10, "ns") dut.clk <= 0 - yield Edge(dut.clk) + await Edge(dut.clk) assert dut.clk.value.integer == 0 - yield Timer(10) + await Timer(10, "ns") dut.clk <= 1 - yield Edge(dut.clk) + await Edge(dut.clk) assert dut.clk.value.integer == 1 - yield Timer(10) + await Timer(10, "ns") dut.clk <= 0 - yield Edge(dut.clk) + await Edge(dut.clk) assert dut.clk.value.integer == 0 - yield Timer(10) + await Timer(10, "ns") dut.clk <= 1 - yield Edge(dut.clk) + await Edge(dut.clk) assert dut.clk.value.integer == 1 - yield Timer(10) + await Timer(10, "ns") dut.clk <= 0 - yield Edge(dut.clk) + await Edge(dut.clk) assert dut.clk.value.integer == 0 @cocotb.test() -def test_fork_and_monitor(dut, period=1000, clocks=6): - cocotb.fork(Clock(dut.clk, period).start()) +async def test_fork_and_monitor(dut, period=1000, clocks=6): + cocotb.fork(Clock(dut.clk, period, "ns").start()) # Ensure the clock has started - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) - timer = Timer(period + 10) + timer = Timer(period + 10, "ns") task = cocotb.fork(count_edges_cycles(dut.clk, clocks)) count = 0 expect = clocks - 1 while True: - result = yield [timer, task.join()] + result = await First(timer, task.join()) assert count <= expect, "Task didn't complete in expected time" if result is timer: dut._log.info("Count %d: Task still running" % count) @@ -120,29 +120,29 @@ def test_fork_and_monitor(dut, period=1000, clocks=6): @cocotb.coroutine -def do_clock(dut, limit, period): +async def do_clock(dut, limit, period): """Simple clock with a limit""" wait_period = period / 2 while limit: - yield Timer(wait_period) + await Timer(wait_period, "ns") dut.clk <= 0 - yield Timer(wait_period) + await Timer(wait_period, "ns") dut.clk <= 1 limit -= 1 @cocotb.coroutine -def do_edge_count(dut, signal): +async def do_edge_count(dut, signal): """Count the edges""" global edges_seen count = 0 while True: - yield RisingEdge(signal) + await RisingEdge(signal) edges_seen += 1 @cocotb.test() -def test_edge_count(dut): +async def test_edge_count(dut): """Count the number of edges is as expected""" global edges_seen edges_seen = 0 @@ -151,13 +151,13 @@ def test_edge_count(dut): clock = cocotb.fork(do_clock(dut, edge_count, clk_period)) test = cocotb.fork(do_edge_count(dut, dut.clk)) - yield Timer(clk_period * (edge_count + 1)) + await Timer(clk_period * (edge_count + 1), "ns") assert edge_count == edges_seen, "Correct edge count failed - saw %d, wanted %d" % (edges_seen, edge_count) @cocotb.test() -def test_edge_identity(dut): +async def test_edge_identity(dut): """ Test that Edge triggers returns the same object each time """ @@ -172,11 +172,11 @@ def test_edge_identity(dut): # check they are all unique assert len({re, fe, e}) == 3 - yield Timer(1) + await Timer(1, "ns") @cocotb.test() -def test_singleton_isinstance(dut): +async def test_singleton_isinstance(dut): """ Test that the result of trigger expression have a predictable type """ @@ -184,40 +184,40 @@ def test_singleton_isinstance(dut): assert isinstance(FallingEdge(dut.clk), FallingEdge) assert isinstance(Edge(dut.clk), Edge) - yield Timer(1) + await Timer(1, "ns") @cocotb.test() -def test_clock_cycles(dut): +async def test_clock_cycles(dut): """ Test the ClockCycles Trigger """ clk = dut.clk - clk_gen = cocotb.fork(Clock(clk, 100).start()) + clk_gen = cocotb.fork(Clock(clk, 100, "ns").start()) - yield RisingEdge(clk) + await RisingEdge(clk) dut._log.info("After one edge") - yield ClockCycles(clk, 10) + await ClockCycles(clk, 10) dut._log.info("After 10 edges") @cocotb.test() -def test_clock_cycles_forked(dut): +async def test_clock_cycles_forked(dut): """ Test that ClockCycles can be used in forked coroutines """ # gh-520 - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + clk_gen = cocotb.fork(Clock(dut.clk, 100, "ns").start()) @cocotb.coroutine - def wait_ten(): - yield ClockCycles(dut.clk, 10) + async def wait_ten(): + await ClockCycles(dut.clk, 10) a = cocotb.fork(wait_ten()) b = cocotb.fork(wait_ten()) - yield a.join() - yield b.join() + await a.join() + await b.join() diff --git a/tests/test_cases/test_cocotb/test_generator_coroutines.py b/tests/test_cases/test_cocotb/test_generator_coroutines.py index 56ea4da6..4a89eba7 100644 --- a/tests/test_cases/test_cocotb/test_generator_coroutines.py +++ b/tests/test_cases/test_cocotb/test_generator_coroutines.py @@ -2,7 +2,7 @@ # Licensed under the Revised BSD License, see LICENSE for details. # SPDX-License-Identifier: BSD-3-Clause """ -Tests that sepcifically test generator-based coroutines +Tests that specifically test generator-based coroutines """ import cocotb from cocotb.triggers import Timer diff --git a/tests/test_cases/test_cocotb/test_handle.py b/tests/test_cases/test_cocotb/test_handle.py index 1ed13ed6..99fb7cf5 100644 --- a/tests/test_cases/test_cocotb/test_handle.py +++ b/tests/test_cases/test_cocotb/test_handle.py @@ -13,7 +13,7 @@ @cocotb.test() -def test_lessthan_raises_error(dut): +async def test_lessthan_raises_error(dut): """ Test that trying to use <= as if it were a comparison produces an error """ @@ -25,10 +25,6 @@ def test_lessthan_raises_error(dut): else: assert False, "No exception was raised when confusing comparison with assignment" - # to make this a generator - if False: - yield - @cocotb.test() async def test_bad_attr(dut): @@ -71,15 +67,15 @@ async def test_delayed_assignment_still_errors(dut): @cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) -def test_integer(dut): +async def test_integer(dut): """ Test access to integers """ log = logging.getLogger("cocotb.test") - yield Timer(10) + await Timer(10, "ns") dut.stream_in_int = 4 - yield Timer(10) - yield Timer(10) + await Timer(10, "ns") + await Timer(10, "ns") got_in = int(dut.stream_out_int) got_out = int(dut.stream_in_int) log.info("dut.stream_out_int = %d" % got_out) @@ -88,35 +84,35 @@ def test_integer(dut): @cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) -def test_real_assign_double(dut): +async def test_real_assign_double(dut): """ Assign a random floating point value, read it back from the DUT and check it matches what we assigned """ val = random.uniform(-1e307, 1e307) log = logging.getLogger("cocotb.test") - yield Timer(1) + await Timer(1) log.info("Setting the value %g" % val) dut.stream_in_real = val - yield Timer(1) - yield Timer(1) # FIXME: Workaround for VHPI scheduling - needs investigation + await Timer(1) + await Timer(1) # FIXME: Workaround for VHPI scheduling - needs investigation got = float(dut.stream_out_real) log.info("Read back value %g" % got) assert got == val, "Values didn't match!" @cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) -def test_real_assign_int(dut): +async def test_real_assign_int(dut): """Assign a random integer value to ensure we can write types convertible to int, read it back from the DUT and check it matches what we assigned. """ val = random.randint(-2**31, 2**31 - 1) log = logging.getLogger("cocotb.test") - yield Timer(1) + await Timer(1) log.info("Setting the value %i" % val) dut.stream_in_real <= val - yield Timer(1) - yield Timer(1) # FIXME: Workaround for VHPI scheduling - needs investigation + await Timer(1) + await Timer(1) # FIXME: Workaround for VHPI scheduling - needs investigation got = dut.stream_out_real log.info("Read back value %d" % got) assert got == float(val), "Values didn't match!" diff --git a/tests/test_cases/test_cocotb/test_logging.py b/tests/test_cases/test_cocotb/test_logging.py index fc4f3ca4..a2b080bb 100644 --- a/tests/test_cases/test_cocotb/test_logging.py +++ b/tests/test_cases/test_cocotb/test_logging.py @@ -7,7 +7,6 @@ import cocotb import cocotb.log -from cocotb.triggers import Timer from common import assert_raises import logging @@ -24,7 +23,7 @@ def __str__(self): @cocotb.test() -def test_logging_with_args(dut): +async def test_logging_with_args(dut): counter = StrCallCounter() dut._log.setLevel(logging.INFO) # To avoid logging debug message, to make next line run without error dut._log.debug("%s", counter) @@ -43,11 +42,9 @@ def test_logging_with_args(dut): dut._log.warning("Testing multiple line\nmessage") - yield Timer(100) # Make it do something with time - @cocotb.test() -def test_logging_default_config(dut): +async def test_logging_default_config(dut): # The cocotb.log module is shadowed by an instance of # cocotb.log.SimBaseLog() from cocotb.log import default_config as log_default_config @@ -78,5 +75,3 @@ def test_logging_default_config(dut): # Restore pre-test configuration os.environ = os_environ_prev cocotb_log.level = log_level_prev - - yield Timer(1) diff --git a/tests/test_cases/test_cocotb/test_scheduler.py b/tests/test_cases/test_cocotb/test_scheduler.py index 2880e6ef..bd89ec85 100644 --- a/tests/test_cases/test_cocotb/test_scheduler.py +++ b/tests/test_cases/test_cocotb/test_scheduler.py @@ -20,61 +20,58 @@ test_flag = False -@cocotb.coroutine -def clock_yield(generator): +async def clock_yield(generator): global test_flag - yield Join(generator) + await Join(generator) test_flag = True -@cocotb.test(expect_fail=False) -def test_coroutine_kill(dut): +@cocotb.test() +async def test_coroutine_kill(dut): """Test that killing a coroutine causes pending routine continue""" global test_flag clk_gen = cocotb.scheduler.add(clock_gen(dut.clk)) - yield Timer(100) + await Timer(100, "ns") clk_gen_two = cocotb.fork(clock_yield(clk_gen)) - yield Timer(100) + await Timer(100, "ns") clk_gen.kill() assert not test_flag - yield Timer(1000) + await Timer(1000, "ns") assert test_flag -@cocotb.coroutine -def clock_one(dut): +async def clock_one(dut): count = 0 while count != 50: - yield RisingEdge(dut.clk) - yield Timer(1000) + await RisingEdge(dut.clk) + await Timer(1000, "ns") count += 1 -@cocotb.coroutine -def clock_two(dut): +async def clock_two(dut): count = 0 while count != 50: - yield RisingEdge(dut.clk) - yield Timer(10000) + await RisingEdge(dut.clk) + await Timer(10000, "ns") count += 1 -@cocotb.test(expect_fail=False) -def test_coroutine_close_down(dut): +@cocotb.test() +async def test_coroutine_close_down(dut): log = logging.getLogger("cocotb.test") - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + clk_gen = cocotb.fork(Clock(dut.clk, 100, "ns").start()) coro_one = cocotb.fork(clock_one(dut)) coro_two = cocotb.fork(clock_two(dut)) - yield Join(coro_one) - yield Join(coro_two) + await Join(coro_one) + await Join(coro_two) log.info("Back from joins") @cocotb.test() -def join_finished(dut): +async def join_finished(dut): """ Test that joining a coroutine that has already been joined gives the same result as it did the first time. @@ -82,34 +79,32 @@ def join_finished(dut): retval = None - @cocotb.coroutine - def some_coro(): - yield Timer(1) + async def some_coro(): + await Timer(1, "ns") return retval coro = cocotb.fork(some_coro()) retval = 1 - x = yield coro.join() + x = await coro.join() assert x == 1 # joining the second time should give the same result. # we change retval here to prove it does not run again retval = 2 - x = yield coro.join() + x = await coro.join() assert x == 1 @cocotb.test() -def consistent_join(dut): +async def consistent_join(dut): """ Test that joining a coroutine returns the finished value """ - @cocotb.coroutine - def wait_for(clk, cycles): + async def wait_for(clk, cycles): rising_edge = RisingEdge(clk) for _ in range(cycles): - yield rising_edge + await rising_edge return 3 cocotb.fork(Clock(dut.clk, 2000, 'ps').start()) @@ -117,46 +112,46 @@ def wait_for(clk, cycles): short_wait = cocotb.fork(wait_for(dut.clk, 10)) long_wait = cocotb.fork(wait_for(dut.clk, 30)) - yield wait_for(dut.clk, 20) - a = yield short_wait.join() - b = yield long_wait.join() + await wait_for(dut.clk, 20) + a = await short_wait.join() + b = await long_wait.join() assert a == b == 3 @cocotb.test() -def test_kill_twice(dut): +async def test_kill_twice(dut): """ Test that killing a coroutine that has already been killed does not crash """ - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) - yield Timer(1) + clk_gen = cocotb.fork(Clock(dut.clk, 100, "ns").start()) + await Timer(1, "ns") clk_gen.kill() - yield Timer(1) + await Timer(1, "ns") clk_gen.kill() @cocotb.test() -def test_join_identity(dut): +async def test_join_identity(dut): """ Test that Join() returns the same object each time """ - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + clk_gen = cocotb.fork(Clock(dut.clk, 100, "ns").start()) assert Join(clk_gen) is Join(clk_gen) - yield Timer(1) + await Timer(1, "ns") clk_gen.kill() @cocotb.test() -def test_trigger_with_failing_prime(dut): +async def test_trigger_with_failing_prime(dut): """ Test that a trigger failing to prime throws """ class ABadTrigger(Trigger): def prime(self, callback): raise RuntimeError("oops") - yield Timer(1) + await Timer(1, "ns") try: - yield ABadTrigger() + await ABadTrigger() except RuntimeError as exc: assert "oops" in str(exc) else: @@ -164,20 +159,20 @@ def prime(self, callback): @cocotb.test() -def test_stack_overflow(dut): +async def test_stack_overflow(dut): """ Test against stack overflows when starting many coroutines that terminate before passing control to the simulator. """ # gh-637 @cocotb.coroutine - def null_coroutine(): - yield NullTrigger() + async def null_coroutine(): + await NullTrigger() for _ in range(10000): - yield null_coroutine() + await null_coroutine() - yield Timer(100) + await Timer(100, "ns") @cocotb.test() @@ -230,7 +225,7 @@ async def reschedule(n): # Test should start in event loop await Combine(*(reschedule(i) for i in range(4))) - await Timer(1) + await Timer(1, "ns") # Event loop when simulator returns await Combine(*(reschedule(i) for i in range(4))) @@ -276,14 +271,14 @@ async def test_last_scheduled_write_wins(dut): @cocotb.coroutine # TODO: Remove once Combine accepts bare coroutines async def first(): - await Timer(1) + await Timer(1, "ns") log.info("scheduling stream_in_data <= 1") dut.stream_in_data <= 1 e.set() @cocotb.coroutine # TODO: Remove once Combine accepts bare coroutines async def second(): - await Timer(1) + await Timer(1, "ns") await e.wait() log.info("scheduling stream_in_data <= 2") dut.stream_in_data <= 2 @@ -294,7 +289,7 @@ async def second(): assert dut.stream_in_data.value.integer == 2 - await Timer(1) + await Timer(1, "ns") dut.array_7_downto_4 <= [1, 2, 3, 4] dut.array_7_downto_4[7] <= 10 diff --git a/tests/test_cases/test_cocotb/test_tests.py b/tests/test_cases/test_cocotb/test_tests.py index f915cfcd..99856dd5 100644 --- a/tests/test_cases/test_cocotb/test_tests.py +++ b/tests/test_cases/test_cocotb/test_tests.py @@ -15,18 +15,18 @@ @cocotb.test(expect_error=True) -def test_syntax_error(dut): +async def test_syntax_error(dut): """Syntax error in the test""" - yield clock_gen(dut.clk) + await clock_gen(dut.clk) fail # noqa @cocotb.test() -def test_tests_are_tests(dut): +async def test_tests_are_tests(dut): """ Test that things annotated with cocotb.test are tests """ - yield Timer(1) + await Timer(1, "ns") assert isinstance(test_tests_are_tests, cocotb.test) @@ -34,20 +34,17 @@ def test_tests_are_tests(dut): # just to be sure... @cocotb.test(expect_fail=True) async def test_async_test_can_fail(dut): - await Timer(1) - raise TestFailure # explicitly do not use assert here + raise TestFailure @cocotb.test() -def test_immediate_test(dut): +async def test_immediate_test(dut): """ Test that tests can return immediately """ return - yield @cocotb.test(expect_fail=True) -def test_assertion_is_failure(dut): - yield Timer(1) +async def test_assertion_is_failure(dut): assert False @@ -56,31 +53,29 @@ class MyException(Exception): @cocotb.test(expect_error=MyException) -def test_expect_particular_exception(dut): - yield Timer(1) +async def test_expect_particular_exception(dut): raise MyException() @cocotb.test(expect_error=(MyException, ValueError)) -def test_expect_exception_list(dut): - yield Timer(1) +async def test_expect_exception_list(dut): raise MyException() @cocotb.test(expect_error=cocotb.result.SimTimeoutError, timeout_time=1, timeout_unit='ns') -def test_timeout_testdec_fail(dut): - yield Timer(10, 'ns') +async def test_timeout_testdec_fail(dut): + await Timer(10, 'ns') @cocotb.test(timeout_time=100, timeout_unit='ns') -def test_timeout_testdec_pass(dut): - yield Timer(10, 'ns') +async def test_timeout_testdec_pass(dut): + await Timer(10, 'ns') @cocotb.test(timeout_time=10, timeout_unit='ns') -def test_timeout_testdec_simultaneous(dut): +async def test_timeout_testdec_simultaneous(dut): try: - yield cocotb.triggers.with_timeout(Timer(1, 'ns'), timeout_time=1, timeout_unit='ns') + await cocotb.triggers.with_timeout(Timer(1, 'ns'), timeout_time=1, timeout_unit='ns') except cocotb.result.SimTimeoutError: pass else: diff --git a/tests/test_cases/test_cocotb/test_timing_triggers.py b/tests/test_cases/test_cocotb/test_timing_triggers.py index 3060ec25..833ca29d 100644 --- a/tests/test_cases/test_cocotb/test_timing_triggers.py +++ b/tests/test_cases/test_cocotb/test_timing_triggers.py @@ -12,7 +12,7 @@ """ import cocotb import warnings -from cocotb.triggers import Timer, RisingEdge, ReadOnly, ReadWrite, Join, NextTimeStep, TriggerException +from cocotb.triggers import Timer, RisingEdge, ReadOnly, ReadWrite, Join, NextTimeStep, First, TriggerException from cocotb.utils import get_sim_time from cocotb.result import TestFailure from cocotb.clock import Clock @@ -23,70 +23,66 @@ from decimal import Decimal -@cocotb.test(expect_fail=False) -def test_function_reentrant_clock(dut): - """Test yielding a reentrant clock""" +@cocotb.test() +async def test_function_reentrant_clock(dut): + """Test awaiting a reentrant clock""" clock = dut.clk - timer = Timer(100) + timer = Timer(100, "ns") for i in range(10): clock <= 0 - yield timer + await timer clock <= 1 - yield timer + await timer -@cocotb.test(expect_fail=False) -def test_timer_with_units(dut): +@cocotb.test() +async def test_timer_with_units(dut): time_fs = get_sim_time(units='fs') - # Yield for one simulation time step - yield Timer(1) + # Await for one simulation time step + await Timer(1) # NOTE: explicitly no units argument here! time_step = get_sim_time(units='fs') - time_fs try: - # Yield for 2.5 timesteps, should throw exception - yield Timer(2.5*time_step, units='fs') + # Await for 2.5 timesteps, should throw exception + await Timer(2.5*time_step, units='fs') raise TestFailure("Timers should throw exception if time cannot be achieved with simulator resolution") except ValueError: dut._log.info("As expected, unable to create a timer of 2.5 simulator time steps") time_fs = get_sim_time(units='fs') - yield Timer(3, "ns") + await Timer(3, "ns") - if get_sim_time(units='fs') != time_fs+3000000.0: - raise TestFailure("Expected a delay of 3 ns") + assert get_sim_time(units='fs') == time_fs+3000000.0, "Expected a delay of 3 ns" time_fs = get_sim_time(units='fs') - yield Timer(1.5, "ns") + await Timer(1.5, "ns") - if get_sim_time(units='fs') != time_fs+1500000.0: - raise TestFailure("Expected a delay of 1.5 ns") + assert get_sim_time(units='fs') == time_fs+1500000.0, "Expected a delay of 1.5 ns" time_fs = get_sim_time(units='fs') - yield Timer(10.0, "ps") + await Timer(10.0, "ps") - if get_sim_time(units='fs') != time_fs+10000.0: - raise TestFailure("Expected a delay of 10 ps") + assert get_sim_time(units='fs') == time_fs+10000.0, "Expected a delay of 10 ps" time_fs = get_sim_time(units='fs') - yield Timer(1.0, "us") + await Timer(1.0, "us") - if get_sim_time(units='fs') != time_fs+1000000000.0: - raise TestFailure("Expected a delay of 1 us") + assert get_sim_time(units='fs') == time_fs+1000000000.0, "Expected a delay of 1 us" @cocotb.test() -def test_timer_with_rational_units(dut): +async def test_timer_with_rational_units(dut): """ Test that rounding errors are not introduced in exact values """ # now with fractions time_fs = get_sim_time(units='fs') - yield Timer(Fraction(1, int(1e9)), units='sec') + await Timer(Fraction(1, int(1e9)), units='sec') assert get_sim_time(units='fs') == time_fs + 1000000.0, "Expected a delay of 1 ns" # now with decimals time_fs = get_sim_time(units='fs') - yield Timer(Decimal('1e-9'), units='sec') + await Timer(Decimal('1e-9'), units='sec') assert get_sim_time(units='fs') == time_fs + 1000000.0, "Expected a delay of 1 ns" @@ -94,29 +90,29 @@ def test_timer_with_rational_units(dut): @cocotb.coroutine -def do_test_readwrite_in_readonly(dut): +async def do_test_readwrite_in_readonly(dut): global exited - yield RisingEdge(dut.clk) - yield ReadOnly() - yield ReadWrite() + await RisingEdge(dut.clk) + await ReadOnly() + await ReadWrite() exited = True @cocotb.coroutine -def do_test_cached_write_in_readonly(dut): +async def do_test_cached_write_in_readonly(dut): global exited - yield RisingEdge(dut.clk) - yield ReadOnly() + await RisingEdge(dut.clk) + await ReadOnly() dut.clk <= 0 exited = True @cocotb.coroutine -def do_test_afterdelay_in_readonly(dut, delay): +async def do_test_afterdelay_in_readonly(dut, delay): global exited - yield RisingEdge(dut.clk) - yield ReadOnly() - yield Timer(delay) + await RisingEdge(dut.clk) + await ReadOnly() + await Timer(delay, "ns") exited = True @@ -127,16 +123,15 @@ def do_test_afterdelay_in_readonly(dut, delay): "modelsim", "ncsim", "xmsim"))) -def test_readwrite_in_readonly(dut): +async def test_readwrite_in_readonly(dut): """Test doing invalid sim operation""" global exited exited = False - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + clk_gen = cocotb.fork(Clock(dut.clk, 100, "ns").start()) coro = cocotb.fork(do_test_readwrite_in_readonly(dut)) - yield [Join(coro), Timer(10000)] + await First(Join(coro), Timer(10000, "ns")) clk_gen.kill() - if exited is not True: - raise TestFailure + assert exited @cocotb.test(expect_error=True, @@ -145,64 +140,61 @@ def test_readwrite_in_readonly(dut): "modelsim", "ncsim", "xmsim"))) -def test_cached_write_in_readonly(dut): +async def test_cached_write_in_readonly(dut): """Test doing invalid sim operation""" global exited exited = False - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + clk_gen = cocotb.fork(Clock(dut.clk, 100, "ns").start()) coro = cocotb.fork(do_test_cached_write_in_readonly(dut)) - yield [Join(coro), Timer(10000)] + await First(Join(coro), Timer(10000, "ns")) clk_gen.kill() - if exited is not True: - raise TestFailure + assert exited @cocotb.test() -def test_afterdelay_in_readonly_valid(dut): +async def test_afterdelay_in_readonly_valid(dut): """Test Timer delay after ReadOnly phase""" global exited exited = False - clk_gen = cocotb.fork(Clock(dut.clk, 100).start()) + clk_gen = cocotb.fork(Clock(dut.clk, 100, "ns").start()) coro = cocotb.fork(do_test_afterdelay_in_readonly(dut, 1)) - yield [Join(coro), Timer(100000)] + await First(Join(coro), Timer(100000, "ns")) clk_gen.kill() - if exited is not True: - raise TestFailure + assert exited @cocotb.test() -def test_writes_have_taken_effect_after_readwrite(dut): +async def test_writes_have_taken_effect_after_readwrite(dut): """ Test that ReadWrite fires first for the background write coro """ dut.stream_in_data.setimmediatevalue(0) - @cocotb.coroutine - def write_manually(): - yield ReadWrite() + async def write_manually(): + await ReadWrite() # this should overwrite the write written below dut.stream_in_data.setimmediatevalue(2) - # queue a backround task to do a manual write + # queue a background task to do a manual write waiter = cocotb.fork(write_manually()) # do a delayed write. This will be overwritten dut.stream_in_data <= 3 - yield waiter + await waiter # check that the write we expected took precedence - yield ReadOnly() + await ReadOnly() assert dut.stream_in_data.value == 2 @cocotb.coroutine -def example(): - yield Timer(10, 'ns') +async def example(): + await Timer(10, 'ns') return 1 @cocotb.test() -def test_timeout_func_fail(dut): +async def test_timeout_func_fail(dut): try: - yield cocotb.triggers.with_timeout(example(), timeout_time=1, timeout_unit='ns') + await cocotb.triggers.with_timeout(example(), timeout_time=1, timeout_unit='ns') except cocotb.result.SimTimeoutError: pass else: @@ -210,18 +202,18 @@ def test_timeout_func_fail(dut): @cocotb.test() -def test_timeout_func_pass(dut): - res = yield cocotb.triggers.with_timeout(example(), timeout_time=100, timeout_unit='ns') +async def test_timeout_func_pass(dut): + res = await cocotb.triggers.with_timeout(example(), timeout_time=100, timeout_unit='ns') assert res == 1 @cocotb.test() -def test_readwrite(dut): +async def test_readwrite(dut): """ Test that ReadWrite can be waited on """ # gh-759 - yield Timer(1) + await Timer(1, "ns") dut.clk <= 1 - yield ReadWrite() + await ReadWrite() @cocotb.test() From 96175ec04eff836b2d524483f6e8162bb15c248a Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 26 Oct 2020 23:10:48 +0100 Subject: [PATCH 2560/2656] Update tests/test_cases/test_cocotb/test_concurrency_primitives.py Co-authored-by: Marlon James --- tests/test_cases/test_cocotb/test_concurrency_primitives.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_cases/test_cocotb/test_concurrency_primitives.py b/tests/test_cases/test_cocotb/test_concurrency_primitives.py index 58acb409..d8d2304f 100644 --- a/tests/test_cases/test_cocotb/test_concurrency_primitives.py +++ b/tests/test_cases/test_cocotb/test_concurrency_primitives.py @@ -12,7 +12,8 @@ @cocotb.test() async def test_await_list_stale(dut): - """ Test that a trigger awaited as part of a list can't cause a spurious wakeup """ + async def test_unfired_first_triggers(dut): + """ Test that un-fired trigger(s) in First don't later cause a spurious wakeup """ # gh-843 events = [Event() for i in range(3)] From 35ab43f640f394f9f4cd4597111642ff154bcbbf Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 26 Oct 2020 23:34:33 +0100 Subject: [PATCH 2561/2656] Update tests/test_cases/test_cocotb/test_concurrency_primitives.py Co-authored-by: Marlon James --- .../test_cocotb/test_concurrency_primitives.py | 9 ++++----- tests/test_cases/test_cocotb/test_edge_triggers.py | 8 -------- tests/test_cases/test_cocotb/test_tests.py | 2 -- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/tests/test_cases/test_cocotb/test_concurrency_primitives.py b/tests/test_cases/test_cocotb/test_concurrency_primitives.py index d8d2304f..1ebbd2e3 100644 --- a/tests/test_cases/test_cocotb/test_concurrency_primitives.py +++ b/tests/test_cases/test_cocotb/test_concurrency_primitives.py @@ -11,15 +11,14 @@ @cocotb.test() -async def test_await_list_stale(dut): - async def test_unfired_first_triggers(dut): +async def test_unfired_first_triggers(dut): """ Test that un-fired trigger(s) in First don't later cause a spurious wakeup """ # gh-843 events = [Event() for i in range(3)] waiters = [e.wait() for e in events] - async def wait_for_lists(): + async def wait_for_firsts(): ret_i = waiters.index((await First(waiters[0], waiters[1]))) assert ret_i == 0, "Expected event 0 to fire, not {}".format(ret_i) @@ -27,7 +26,7 @@ async def wait_for_lists(): assert ret_i == 2, "Expected event 2 to fire, not {}".format(ret_i) async def wait_for_e1(): - """ wait on the event that didn't wake `wait_for_lists` """ + """ wait on the event that didn't wake `wait_for_firsts` """ ret_i = waiters.index((await waiters[1])) assert ret_i == 1, "Expected event 1 to fire, not {}".format(ret_i) @@ -39,7 +38,7 @@ async def fire_events(): fire_task = cocotb.fork(fire_events()) e1_task = cocotb.fork(wait_for_e1()) - await wait_for_lists() + await wait_for_firsts() # make sure the other tasks finish await fire_task.join() diff --git a/tests/test_cases/test_cocotb/test_edge_triggers.py b/tests/test_cases/test_cocotb/test_edge_triggers.py index f14e6418..e63e806b 100644 --- a/tests/test_cases/test_cocotb/test_edge_triggers.py +++ b/tests/test_cases/test_cocotb/test_edge_triggers.py @@ -135,7 +135,6 @@ async def do_clock(dut, limit, period): async def do_edge_count(dut, signal): """Count the edges""" global edges_seen - count = 0 while True: await RisingEdge(signal) edges_seen += 1 @@ -152,7 +151,6 @@ async def test_edge_count(dut): test = cocotb.fork(do_edge_count(dut, dut.clk)) await Timer(clk_period * (edge_count + 1), "ns") - assert edge_count == edges_seen, "Correct edge count failed - saw %d, wanted %d" % (edges_seen, edge_count) @@ -192,17 +190,11 @@ async def test_clock_cycles(dut): """ Test the ClockCycles Trigger """ - clk = dut.clk - clk_gen = cocotb.fork(Clock(clk, 100, "ns").start()) - await RisingEdge(clk) - dut._log.info("After one edge") - await ClockCycles(clk, 10) - dut._log.info("After 10 edges") diff --git a/tests/test_cases/test_cocotb/test_tests.py b/tests/test_cases/test_cocotb/test_tests.py index 99856dd5..267b42f9 100644 --- a/tests/test_cases/test_cocotb/test_tests.py +++ b/tests/test_cases/test_cocotb/test_tests.py @@ -26,8 +26,6 @@ async def test_tests_are_tests(dut): """ Test that things annotated with cocotb.test are tests """ - await Timer(1, "ns") - assert isinstance(test_tests_are_tests, cocotb.test) From e4ed046fbb43639865b204ccb5ef4ee0b29f8577 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 5 Nov 2020 22:15:50 +0100 Subject: [PATCH 2562/2656] Add more mentions of pip's --user option --- documentation/source/install.rst | 8 +++++++- documentation/source/install_devel.rst | 20 +++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/documentation/source/install.rst b/documentation/source/install.rst index 4a1ccb72..b56b17a4 100644 --- a/documentation/source/install.rst +++ b/documentation/source/install.rst @@ -107,6 +107,12 @@ Installation of cocotb pip install cocotb +.. note:: + + If your user does not have permissions to install cocotb using the instructions above, + try adding the ``--user`` option to ``pip`` + (see :ref:`the pip documentation`). + .. warning:: ``pip`` may belong to a different Python installation to what you expect. @@ -117,7 +123,7 @@ If you want to install the **development version** of cocotb, :ref:`instructions After installation, you should be able to execute ``cocotb-config``. If it is not found, you need to append its location to the ``PATH`` environment variable. -This may happen when you use the ``--user`` option to ``pip``, in which case the location is documented :ref:`here `. +This may happen when you use the ``--user`` option to ``pip``, in which case the location is documented :ref:`here`. For more installation options, please see `our Wiki `_. diff --git a/documentation/source/install_devel.rst b/documentation/source/install_devel.rst index 46f4a591..17cd8507 100644 --- a/documentation/source/install_devel.rst +++ b/documentation/source/install_devel.rst @@ -12,31 +12,25 @@ The instructions for installing the development version of cocotb vary depending .. group-tab:: Windows - The development version of cocotb can be installed globally by running + The development version of cocotb can be installed by running .. code-block:: bash pip install --global-option build_ext --global-option --compiler=mingw32 https://github.com/cocotb/cocotb/archive/master.zip - See also the `pip User Guide `_. - .. group-tab:: Linux and macOS - The development version of cocotb can be installed globally by running + The development version of cocotb can be installed by running .. code-block:: bash pip install https://github.com/cocotb/cocotb/archive/master.zip - This requires administrator permissions. - - In order to install only for your current user, run - - .. code-block:: bash - - pip install https://github.com/cocotb/cocotb/archive/master.zip --user +.. note:: - See also the `pip User Guide `_. + If your user does not have permissions to install cocotb using the instructions above, + try adding the ``--user`` option to ``pip`` + (see :ref:`the pip documentation`). .. warning:: @@ -46,4 +40,4 @@ The instructions for installing the development version of cocotb vary depending After installation, you should be able to execute ``cocotb-config``. If it is not found, you need to append its location to the ``PATH`` environment variable. -This may happen when you use the ``--user`` option to ``pip``, in which case the location is documented :ref:`here `. +This may happen when you use the ``--user`` option to ``pip``, in which case the location is documented :ref:`here`. From 9154475c6b8087820719c9b5a34f54d864411380 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Wed, 4 Nov 2020 20:34:07 -0800 Subject: [PATCH 2563/2656] GHA: Fix lint-whitespace command --- .github/workflows/regression-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml index 738dc82b..080fc1bb 100644 --- a/.github/workflows/regression-tests.yml +++ b/.github/workflows/regression-tests.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v2 - name: whitespace run: | - ! git --no-pager grep --color --line-number --full-name --extended-regexp -I '\\s+$' + ! git --no-pager grep --color --line-number --full-name --extended-regexp -I '\s+$' lint-flake8: runs-on: ubuntu-latest From 9fbf63911f02556ca939b34431c42387b207c3eb Mon Sep 17 00:00:00 2001 From: Marlon James Date: Wed, 4 Nov 2020 21:32:46 -0800 Subject: [PATCH 2564/2656] Remove trailing whitespace --- documentation/source/simulator_support.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index 2e005670..a8e46ee1 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -121,7 +121,7 @@ To get waveforms in VCD format, add Verilator's trace option(s) to the To set the same options on the command line, use ``EXTRA_ARGS="--trace --trace-structs" make ...``. A VCD file named ``dump.vcd`` will be generated in the current directory. -Verilator can produce waveform traces in the FST format, the native format of GTKWave. +Verilator can produce waveform traces in the FST format, the native format of GTKWave. FST traces are much smaller and more efficient to write, but require the use of GTKWave. To enable FST tracing, add `--trace-fst -CFLAGS -DVM_TRACE_FST=1` to `EXTRA_ARGS` as shown below. From 93e5a46f9873253d00f98c2de7ce407771d5d868 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 8 Nov 2020 16:49:39 -0600 Subject: [PATCH 2565/2656] Cleanup TestError (#2177) * Use a more appropriate exception when a GPI object cannot be mapped to a Python type `TestError` was unnecessary here. It was used to fail the test if a signal could not be mapped to a Python SimHandle type. This could be easily done with a standard exception. This cleans up the last usage of `TestError` in core code and will allow for it's deprecation. * Deprecate TestError TestError is no longer used internally, and it was never a good idea for a user to raise a TestError rather than a TestFailure, so it is being deprecated. Co-authored-by: Eric Wieser Co-authored-by: Marlon James --- cocotb/handle.py | 10 +++---- cocotb/result.py | 27 ++++++++++++------- .../source/newsfragments/2177.removal.rst | 1 + .../test_cases/test_cocotb/test_deprecated.py | 2 +- 4 files changed, 25 insertions(+), 15 deletions(-) create mode 100644 documentation/source/newsfragments/2177.removal.rst diff --git a/cocotb/handle.py b/cocotb/handle.py index f5035ed8..976afd9f 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -36,7 +36,6 @@ from cocotb import simulator from cocotb.binary import BinaryValue from cocotb.log import SimLog -from cocotb.result import TestError # Only issue a warning for each deprecated attribute access _deprecation_warned = set() @@ -218,9 +217,10 @@ def _discover_all(self): self._log.debug("Discovering all on %s", self._name) for thing in self._handle.iterate(simulator.OBJECTS): name = thing.get_name_string() + path = self._child_path(name) try: - hdl = SimHandle(thing, self._child_path(name)) - except TestError as e: + hdl = SimHandle(thing, path) + except NotImplementedError as e: self._log.debug("%s", e) continue @@ -919,7 +919,7 @@ def SimHandle(handle, path=None): The `SimHandle` object. Raises: - TestError: If no matching object for GPI type could be found. + NotImplementedError: If no matching object for GPI type could be found. """ _type2cls = { simulator.MODULE: HierarchyObject, @@ -956,7 +956,7 @@ def SimHandle(handle, path=None): return obj if t not in _type2cls: - raise TestError("Couldn't find a matching object for GPI type %d (path=%s)" % (t, path)) + raise NotImplementedError("Couldn't find a matching object for GPI type %s(%d) (path=%s)" % (handle.get_type_string(), t, path)) obj = _type2cls[t](handle, path) _handle2obj[handle] = obj return obj diff --git a/cocotb/result.py b/cocotb/result.py index a7467c8f..67207bcf 100644 --- a/cocotb/result.py +++ b/cocotb/result.py @@ -38,16 +38,15 @@ def raise_error(obj, msg): """Create a :exc:`TestError` exception and raise it after printing a traceback. .. deprecated:: 1.3 - Use ``raise TestError(msg)`` instead of this function. A stacktrace will - be printed by cocotb automatically if the exception is unhandled. + Raise a standard Python exception instead of calling this function. + A stacktrace will be printed by cocotb automatically if the exception is unhandled. Args: obj: Object with a log method. msg (str): The log message. """ warnings.warn( - "``raise_error`` is deprecated - use ``raise TestError(msg)`` (or any " - "other exception type) instead", + "``raise_error`` is deprecated - raise a standard Exception instead", DeprecationWarning, stacklevel=2) _raise_error(obj, msg) @@ -67,15 +66,14 @@ def create_error(obj, msg): simply to avoid too many levels of nested `try/except` blocks. .. deprecated:: 1.3 - Use ``TestError(msg)`` directly instead of this function. + Raise a standard Python exception instead of calling this function. Args: obj: Object with a log method. msg (str): The log message. """ warnings.warn( - "``create_error`` is deprecated - use ``TestError(msg)`` directly " - "(or any other exception type) instead", + "``create_error`` is deprecated - raise a standard Exception instead", DeprecationWarning, stacklevel=2) try: # use the private version to avoid multiple warnings @@ -118,8 +116,19 @@ def __init__(self, exception): class TestError(TestComplete): - """Exception showing that the test was completed with severity Error.""" - pass + """ + Exception showing that the test was completed with severity Error. + + .. deprecated:: 1.5 + Raise a standard Python exception instead. + A stacktrace will be printed by cocotb automatically if the exception is unhandled. + """ + + def __init__(self, *args, **kwargs): + warnings.warn( + "TestError is deprecated - raise a standard Exception instead", + DeprecationWarning, stacklevel=2) + super().__init__(*args, **kwargs) class TestFailure(TestComplete, AssertionError): diff --git a/documentation/source/newsfragments/2177.removal.rst b/documentation/source/newsfragments/2177.removal.rst new file mode 100644 index 00000000..dc05f287 --- /dev/null +++ b/documentation/source/newsfragments/2177.removal.rst @@ -0,0 +1 @@ +:class:`~cocotb.result.TestError` has been deprecated, use a standard :ref:`Python Exception `. diff --git a/tests/test_cases/test_cocotb/test_deprecated.py b/tests/test_cases/test_cocotb/test_deprecated.py index e80fbad9..f39a9ab8 100644 --- a/tests/test_cases/test_cocotb/test_deprecated.py +++ b/tests/test_cases/test_cocotb/test_deprecated.py @@ -16,7 +16,7 @@ def assert_deprecated(warning_category=DeprecationWarning): warnings.simplefilter("always") yield warns # note: not a cocotb yield, but a contextlib one! finally: - assert len(warns) == 1 + assert len(warns) >= 1 msg = "Expected {}".format(warning_category.__qualname__) assert issubclass(warns[0].category, warning_category), msg From 43adc60479c193d9dc700c3851c7db2907034e35 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 2 Nov 2020 23:11:06 +0100 Subject: [PATCH 2566/2656] Add units="step" ("simulator time step") and use it in cocotb_test. Closes #2171. --- cocotb/clock.py | 21 +++++++-- cocotb/decorators.py | 14 ++++-- cocotb/triggers.py | 35 ++++++++++---- cocotb/utils.py | 18 ++++++-- .../source/newsfragments/2171.feature.rst | 6 +++ tests/test_cases/test_cocotb/test_handle.py | 14 +++--- .../test_cases/test_cocotb/test_scheduler.py | 6 +-- .../test_cocotb/test_timing_triggers.py | 46 ++++++++++++++----- 8 files changed, 118 insertions(+), 42 deletions(-) create mode 100644 documentation/source/newsfragments/2171.feature.rst diff --git a/cocotb/clock.py b/cocotb/clock.py index 1bbb69c5..df3bed42 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -28,6 +28,7 @@ """A clock class.""" import itertools +import warnings from cocotb.log import SimLog from cocotb.triggers import Timer @@ -66,10 +67,9 @@ class Clock(BaseClock): period (int): The clock period. Must convert to an even number of timesteps. units (str, optional): One of - ``None``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``. - When no *units* is given (``None``) the timestep is determined by - the simulator. - + ``'step'``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``. + When *units* is ``'step'``, + the timestep is determined by the simulator (see :make:var:`COCOTB_HDL_TIMEPRECISION`). If you need more features like a phase shift and an asymmetric duty cycle, it is simple to create your own clock generator (that you then :func:`fork`): @@ -106,10 +106,21 @@ async def custom_clock(): await Timer(1000, units="ns") high_delay = low_delay = 10 # change the clock speed await Timer(1000, units="ns") + + .. versionchanged:: 1.5 + Support ``'step'`` as the the *units* argument to mean "simulator time step". + + .. deprecation:: 1.5 + Using None as the the *units* argument is deprecated, use ``'step'`` instead. """ - def __init__(self, signal, period, units=None): + def __init__(self, signal, period, units="step"): BaseClock.__init__(self, signal) + if units is None: + warnings.warn( + 'Using units=None is deprecated, use units="step" instead.', + DeprecationWarning, stacklevel=2) + units="step" # don't propagate deprecated value self.period = get_sim_steps(period, units) self.half_period = get_sim_steps(period / 2.0, units) self.frequency = 1.0 / get_time_from_sim_steps(self.period, units='us') diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 76d76a92..4dbdd866 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -485,11 +485,14 @@ class test(coroutine, metaclass=_decorator_helper): Test timeout is intended for protection against deadlock. Users should use :class:`~cocotb.triggers.with_timeout` if they require a more general-purpose timeout mechanism. - timeout_unit (str or None, optional): + timeout_unit (str, optional): Units of timeout_time, accepts any units that :class:`~cocotb.triggers.Timer` does. .. versionadded:: 1.3 - expect_fail (bool, optional): + + .. deprecation:: 1.5 + Using None as the the *timeout_unit* argument is deprecated, use ``'step'`` instead. + expect_fail (bool, optional): Don't mark the result as a failure if the test fails. expect_error (bool or exception type or tuple of exception types, optional): If ``True``, consider this test passing if it raises *any* :class:`Exception`, and failing if it does not. @@ -518,10 +521,15 @@ async def my_test(dut): _id_count = 0 # used by the RegressionManager to sort tests in definition order - def __init__(self, f, timeout_time=None, timeout_unit=None, + def __init__(self, f, timeout_time=None, timeout_unit="step", expect_fail=False, expect_error=False, skip=False, stage=None): + if timeout_unit is None: + warnings.warn( + 'Using timeout_unit=None is deprecated, use timeout_unit="step" instead.', + DeprecationWarning, stacklevel=2) + timeout_unit="step" # don't propagate deprecated value self._id = self._id_count type(self)._id_count += 1 diff --git a/cocotb/triggers.py b/cocotb/triggers.py index df163a7e..1fa1e0ef 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -158,16 +158,16 @@ def unprime(self): class Timer(GPITrigger): """Fires after the specified simulation time period has elapsed.""" - def __init__(self, time_ps, units=None): + def __init__(self, time_ps, units="step"): """ Args: time_ps (numbers.Real or decimal.Decimal): The time value. Note that despite the name this is not actually in picoseconds but depends on the *units* argument. - units (str or None, optional): One of - ``None``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``. - When no *units* is given (``None``) the timestep is determined by - the simulator. + units (str, optional): One of + ``'step'``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``. + When *units* is ``'step'``, + the timestep is determined by the simulator (see :make:var:`COCOTB_HDL_TIMEPRECISION`). Examples: @@ -202,6 +202,12 @@ def __init__(self, time_ps, units=None): .. versionchanged:: 1.5 Raise an exception when Timer uses a negative value as it is undefined behavior. Warn for 0 as this will cause erratic behavior in some simulators as well. + + .. versionchanged:: 1.5 + Support ``'step'`` as the the *units* argument to mean "simulator time step". + + .. deprecation:: 1.5 + Using None as the the *units* argument is deprecated, use ``'step'`` instead. """ GPITrigger.__init__(self) if time_ps <= 0: @@ -211,6 +217,11 @@ def __init__(self, time_ps, units=None): stacklevel=2) else: raise TriggerException("Timer value time_ps must not be negative") + if units is None: + warnings.warn( + 'Using units=None is deprecated, use units="step" instead.', + DeprecationWarning, stacklevel=2) + units="step" # don't propagate deprecated value self.sim_steps = get_sim_steps(time_ps, units) def prime(self, callback): @@ -861,7 +872,7 @@ def __repr__(self): return fmt.format(type(self).__qualname__, self.signal, self.num_cycles) -async def with_timeout(trigger, timeout_time, timeout_unit=None): +async def with_timeout(trigger, timeout_time, timeout_unit="step"): """ Waits on triggers, throws an exception if it waits longer than the given time. @@ -877,7 +888,7 @@ async def with_timeout(trigger, timeout_time, timeout_unit=None): A single object that could be right of an :keyword:`await` expression in cocotb. timeout_time (numbers.Real or decimal.Decimal): Simulation time duration before timeout occurs. - timeout_unit (str or None, optional): + timeout_unit (str, optional): Units of timeout_time, accepts any units that :class:`~cocotb.triggers.Timer` does. Returns: @@ -887,8 +898,16 @@ async def with_timeout(trigger, timeout_time, timeout_unit=None): :exc:`SimTimeoutError`: If timeout occurs. .. versionadded:: 1.3 - """ + .. deprecation:: 1.5 + Using None as the the *timeout_unit* argument is deprecated, use ``'step'`` instead. + """ + + if timeout_unit is None: + warnings.warn( + 'Using timeout_unit=None is deprecated, use timeout_unit="step" instead.', + DeprecationWarning, stacklevel=2) + timeout_unit="step" # don't propagate deprecated value timeout_timer = cocotb.triggers.Timer(timeout_time, timeout_unit) res = await First(timeout_timer, trigger) if res is timeout_timer: diff --git a/cocotb/utils.py b/cocotb/utils.py index 06ec5483..b1117023 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -102,24 +102,32 @@ def get_time_from_sim_steps(steps, units): return _ldexp10(steps, _get_simulator_precision() - _get_log_time_scale(units)) -def get_sim_steps(time, units=None): +def get_sim_steps(time, units="step"): """Calculates the number of simulation time steps for a given amount of *time*. Args: time (numbers.Real or decimal.Decimal): The value to convert to simulation time steps. - units (str or None, optional): String specifying the units of the result - (one of ``None``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``). - ``None`` means time is already in simulation time steps. + units (str, optional): String specifying the units of the result + (one of ``'step'``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``). + ``'step'`` means time is already in simulation time steps. Returns: int: The number of simulation time steps. Raises: :exc:`ValueError`: If given *time* cannot be represented by simulator precision. + + .. versionchanged:: 1.5 + Support ``'step'`` as the the *units* argument to mean "simulator time step". """ result = time - if units is not None: + if units not in (None, "step"): result = _ldexp10(result, _get_log_time_scale(units) - _get_simulator_precision()) + if units is None: + warnings.warn( + 'Using units=None is deprecated, use units="step" instead.', + DeprecationWarning, stacklevel=2) + units="step" # don't propagate deprecated value result_rounded = math.floor(result) diff --git a/documentation/source/newsfragments/2171.feature.rst b/documentation/source/newsfragments/2171.feature.rst new file mode 100644 index 00000000..7b19daf4 --- /dev/null +++ b/documentation/source/newsfragments/2171.feature.rst @@ -0,0 +1,6 @@ +The ``units`` argument to :class:`cocotb.triggers.Timer`, +:class:`cocotb.clock.Clock` and :func:`cocotb.utils.get_sim_steps`, +and the ``timeout_unit`` argument to +:func:`cocotb.triggers.with_timeout` and :class:`cocotb.test` +now accepts ``'step'`` to mean the simulator time step. +This used to be expressed using ``None``, which is now deprecated. diff --git a/tests/test_cases/test_cocotb/test_handle.py b/tests/test_cases/test_cocotb/test_handle.py index 99fb7cf5..f33ed1c3 100644 --- a/tests/test_cases/test_cocotb/test_handle.py +++ b/tests/test_cases/test_cocotb/test_handle.py @@ -91,11 +91,12 @@ async def test_real_assign_double(dut): """ val = random.uniform(-1e307, 1e307) log = logging.getLogger("cocotb.test") - await Timer(1) + timer_shortest = Timer(1, "step") + await timer_shortest log.info("Setting the value %g" % val) dut.stream_in_real = val - await Timer(1) - await Timer(1) # FIXME: Workaround for VHPI scheduling - needs investigation + await timer_shortest + await timer_shortest # FIXME: Workaround for VHPI scheduling - needs investigation got = float(dut.stream_out_real) log.info("Read back value %g" % got) assert got == val, "Values didn't match!" @@ -108,11 +109,12 @@ async def test_real_assign_int(dut): """ val = random.randint(-2**31, 2**31 - 1) log = logging.getLogger("cocotb.test") - await Timer(1) + timer_shortest = Timer(1, "step") + await timer_shortest log.info("Setting the value %i" % val) dut.stream_in_real <= val - await Timer(1) - await Timer(1) # FIXME: Workaround for VHPI scheduling - needs investigation + await timer_shortest + await timer_shortest # FIXME: Workaround for VHPI scheduling - needs investigation got = dut.stream_out_real log.info("Read back value %d" % got) assert got == float(val), "Values didn't match!" diff --git a/tests/test_cases/test_cocotb/test_scheduler.py b/tests/test_cases/test_cocotb/test_scheduler.py index bd89ec85..995e8792 100644 --- a/tests/test_cases/test_cocotb/test_scheduler.py +++ b/tests/test_cases/test_cocotb/test_scheduler.py @@ -186,7 +186,7 @@ async def test_kill_coroutine_waiting_on_the_same_trigger(dut): victim_resumed = False async def victim(): - await Timer(1) # prevent scheduling of RisingEdge before killer + await Timer(1, "step") # prevent scheduling of RisingEdge before killer await RisingEdge(dut.clk) nonlocal victim_resumed victim_resumed = True @@ -197,9 +197,9 @@ async def killer(): victim_task.kill() cocotb.fork(killer()) - await Timer(2) # allow Timer in victim to pass making it schedule RisingEdge after the killer + await Timer(2, "step") # allow Timer in victim to pass making it schedule RisingEdge after the killer dut.clk <= 1 - await Timer(1) + await Timer(1, "step") assert not victim_resumed diff --git a/tests/test_cases/test_cocotb/test_timing_triggers.py b/tests/test_cases/test_cocotb/test_timing_triggers.py index 833ca29d..b8a5a09d 100644 --- a/tests/test_cases/test_cocotb/test_timing_triggers.py +++ b/tests/test_cases/test_cocotb/test_timing_triggers.py @@ -13,8 +13,7 @@ import cocotb import warnings from cocotb.triggers import Timer, RisingEdge, ReadOnly, ReadWrite, Join, NextTimeStep, First, TriggerException -from cocotb.utils import get_sim_time -from cocotb.result import TestFailure +from cocotb.utils import get_sim_time, get_sim_steps from cocotb.clock import Clock from common import assert_raises @@ -39,35 +38,33 @@ async def test_function_reentrant_clock(dut): async def test_timer_with_units(dut): time_fs = get_sim_time(units='fs') - # Await for one simulation time step + # Await for one simulator time step await Timer(1) # NOTE: explicitly no units argument here! time_step = get_sim_time(units='fs') - time_fs - try: - # Await for 2.5 timesteps, should throw exception + pattern = "Unable to accurately represent .* with the simulator precision of .*" + with assert_raises(ValueError, pattern): await Timer(2.5*time_step, units='fs') - raise TestFailure("Timers should throw exception if time cannot be achieved with simulator resolution") - except ValueError: - dut._log.info("As expected, unable to create a timer of 2.5 simulator time steps") + dut._log.info("As expected, unable to create a timer of 2.5 simulator time steps") time_fs = get_sim_time(units='fs') - await Timer(3, "ns") + await Timer(3, 'ns') assert get_sim_time(units='fs') == time_fs+3000000.0, "Expected a delay of 3 ns" time_fs = get_sim_time(units='fs') - await Timer(1.5, "ns") + await Timer(1.5, 'ns') assert get_sim_time(units='fs') == time_fs+1500000.0, "Expected a delay of 1.5 ns" time_fs = get_sim_time(units='fs') - await Timer(10.0, "ps") + await Timer(10.0, 'ps') assert get_sim_time(units='fs') == time_fs+10000.0, "Expected a delay of 10 ps" time_fs = get_sim_time(units='fs') - await Timer(1.0, "us") + await Timer(1.0, 'us') assert get_sim_time(units='fs') == time_fs+1000000000.0, "Expected a delay of 1 us" @@ -237,3 +234,28 @@ async def test_neg_timer(dut): Timer(0) assert "Timer setup with value 0, which might exhibit undefined behavior in some simulators" in str(w[-1].message) assert issubclass(w[-1].category, RuntimeWarning) + + +@cocotb.test() +async def test_time_units_eq_None(dut): + """Test deprecation warning when time units are None""" + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + Timer(1, units=None) + assert issubclass(w[-1].category, DeprecationWarning) + assert 'Using units=None is deprecated, use units="step" instead.' in str(w[-1].message) + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + Clock(dut.clk, 2, units=None) + assert issubclass(w[-1].category, DeprecationWarning) + assert 'Using units=None is deprecated, use units="step" instead.' in str(w[-1].message) + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + get_sim_steps(222, units=None) + assert issubclass(w[-1].category, DeprecationWarning) + assert 'Using units=None is deprecated, use units="step" instead.' in str(w[-1].message) + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + await cocotb.triggers.with_timeout(example(), timeout_time=222222, timeout_unit=None) + assert issubclass(w[-1].category, DeprecationWarning) + assert 'Using timeout_unit=None is deprecated, use timeout_unit="step" instead.' in str(w[-1].message) From 60e3ed61e149ebbf23534a2d85ffba4d90e55115 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sat, 7 Nov 2020 21:37:49 +0100 Subject: [PATCH 2567/2656] Add config for pylint Add some cocotb-specific settings to the generated template, and ignore some of the less-useful messages. This is *not* meant as a config with which we should eventually strive to have no pylint messages left or use in CI, just as a starting point for improving the code now and then. --- .pylintrc | 528 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 528 insertions(+) create mode 100644 .pylintrc diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 00000000..79c0abf4 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,528 @@ +[MASTER] + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. +extension-pkg-whitelist=cocotb.simulator + +# Specify a score threshold to be exceeded before program exits with error. +fail-under=10.0 + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Add files or directories matching the regex patterns to the blacklist. The +# regex matches against base names, not paths. +ignore-patterns= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the +# number of processors available to use. +jobs=0 + +# Control the amount of potential inferred values when inferring a single +# object. This can help the performance when dealing with large functions or +# complex, nested conditions. +limit-inference-results=100 + +# List of plugins (as comma separated values of python module names) to load, +# usually to register additional checkers. +load-plugins= + +# Pickle collected data for later comparisons. +persistent=yes + +# When enabled, pylint would attempt to guess common misconfiguration and emit +# user-friendly hints instead of false-positive error messages. +suggestion-mode=yes + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. +confidence= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once). You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use "--disable=all --enable=classes +# --disable=W". +disable=no-member, + invalid-name, + too-many-locals, + too-many-nested-blocks, + too-many-instance-attributes, + too-many-arguments, + too-many-statements, + too-many-branches, + pointless-statement, + logging-not-lazy, + logging-format-interpolation, + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable=c-extension-no-member + + +[REPORTS] + +# Python expression which should return a score less than or equal to 10. You +# have access to the variables 'error', 'warning', 'refactor', and 'convention' +# which contain the number of messages in each category, as well as 'statement' +# which is the total number of statements analyzed. This score is used by the +# global evaluation report (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details. +#msg-template= + +# Set the output format. Available formats are text, parseable, colorized, json +# and msvs (visual studio). You can also give a reporter class, e.g. +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Tells whether to display a full report or only the messages. +reports=no + +# Activate the evaluation score. +score=yes + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + +# Complete name of functions that never returns. When checking for +# inconsistent-return-statements if a never returning function is called then +# it will be considered as an explicit return statement and no message will be +# printed. +never-returning-functions=sys.exit + + +[BASIC] + +# Naming style matching correct argument names. +argument-naming-style=snake_case + +# Regular expression matching correct argument names. Overrides argument- +# naming-style. +#argument-rgx= + +# Naming style matching correct attribute names. +attr-naming-style=snake_case + +# Regular expression matching correct attribute names. Overrides attr-naming- +# style. +#attr-rgx= + +# Bad variable names which should always be refused, separated by a comma. +bad-names=foo, + bar, + baz, + toto, + tutu, + tata + +# Bad variable names regexes, separated by a comma. If names match any regex, +# they will always be refused +bad-names-rgxs= + +# Naming style matching correct class attribute names. +class-attribute-naming-style=any + +# Regular expression matching correct class attribute names. Overrides class- +# attribute-naming-style. +#class-attribute-rgx= + +# Naming style matching correct class names. +class-naming-style=PascalCase + +# Regular expression matching correct class names. Overrides class-naming- +# style. +#class-rgx= + +# Naming style matching correct constant names. +const-naming-style=UPPER_CASE + +# Regular expression matching correct constant names. Overrides const-naming- +# style. +#const-rgx= + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming style matching correct function names. +function-naming-style=snake_case + +# Regular expression matching correct function names. Overrides function- +# naming-style. +#function-rgx= + +# Good variable names which should always be accepted, separated by a comma. +good-names=i, + j, + k, + ex, + Run, + _ + +# Good variable names regexes, separated by a comma. If names match any regex, +# they will always be accepted +good-names-rgxs= + +# Include a hint for the correct naming format with invalid-name. +include-naming-hint=no + +# Naming style matching correct inline iteration names. +inlinevar-naming-style=any + +# Regular expression matching correct inline iteration names. Overrides +# inlinevar-naming-style. +#inlinevar-rgx= + +# Naming style matching correct method names. +method-naming-style=snake_case + +# Regular expression matching correct method names. Overrides method-naming- +# style. +#method-rgx= + +# Naming style matching correct module names. +module-naming-style=snake_case + +# Regular expression matching correct module names. Overrides module-naming- +# style. +#module-rgx= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +# These decorators are taken in consideration only for invalid-name. +property-classes=abc.abstractproperty + +# Naming style matching correct variable names. +variable-naming-style=snake_case + +# Regular expression matching correct variable names. Overrides variable- +# naming-style. +#variable-rgx= + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=120 + +# Maximum number of lines in a module. +max-module-lines=1000 + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[LOGGING] + +# The type of string formatting that logging methods do. `old` means using % +# formatting, `new` is for `{}` formatting. +logging-format-style=old + +# Logging modules to check that the string format arguments are in logging +# function parameter format. +logging-modules=logging + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME, + XXX, + TODO + +# Regular expression of note tags to take in consideration. +#notes-rgx= + + +[SIMILARITIES] + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[SPELLING] + +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions=4 + +# Spelling dictionary name. Available dictionaries: none. To make it work, +# install the python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains the private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to the private dictionary (see the +# --spelling-private-dict-file option) instead of raising a message. +spelling-store-unknown-words=no + + +[STRING] + +# This flag controls whether inconsistent-quotes generates a warning when the +# character used as a quote delimiter is used inconsistently within a module. +check-quote-consistency=no + +# This flag controls whether the implicit-str-concat should generate a warning +# on implicit string concatenation in sequences defined over several lines. +check-str-concat-over-line-jumps=no + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# Tells whether to warn about missing members when the owner of the attribute +# is inferred to be None. +ignore-none=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis). It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + +# List of decorators that change the signature of a decorated function. +signature-mutators= + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid defining new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_, + _cb + +# A regular expression matching the name of dummy variables (i.e. expected to +# not be used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore. +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp, + __post_init__ + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict, + _fields, + _replace, + _source, + _make, + _coro, + _trigger, + _outcome, + _advance, + _wait, + _name, + _id, + _library_coverage, + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=cls + + +[DESIGN] + +# Maximum number of arguments for function / method. +max-args=5 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Maximum number of boolean expressions in an if statement (see R0916). +max-bool-expr=5 + +# Maximum number of branch for function / method body. +max-branches=12 + +# Maximum number of locals for function / method body. +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body. +max-returns=6 + +# Maximum number of statements in function / method body. +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + + +[IMPORTS] + +# List of modules that can be imported at any level, not just the top level +# one. +allow-any-import-level= + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Deprecated modules which should not be used, separated by a comma. +deprecated-modules=optparse,tkinter.tix + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled). +ext-import-graph= + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled). +import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled). +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + +# Couples of modules and preferred modules, separated by a comma. +preferred-modules= + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "BaseException, Exception". +overgeneral-exceptions=BaseException, + Exception From 149de9bc78804df49caa564f0b2ce1c96388bcc7 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sun, 8 Nov 2020 11:30:20 +0100 Subject: [PATCH 2568/2656] Clean up some pylint findings --- cocotb/drivers/__init__.py | 6 +++- cocotb/regression.py | 23 ++++++------- cocotb/scheduler.py | 70 ++++++++++++++++++++------------------ 3 files changed, 52 insertions(+), 47 deletions(-) diff --git a/cocotb/drivers/__init__.py b/cocotb/drivers/__init__.py index a81e59d6..3597b070 100755 --- a/cocotb/drivers/__init__.py +++ b/cocotb/drivers/__init__.py @@ -51,6 +51,7 @@ def __init__(self, signal, clk, generator=None): self._signal = signal self._clk = clk self._generator = generator + self._cr = None def start(self, generator: Iterable[Tuple[int, int]] = None) -> None: """Start generating data. @@ -310,6 +311,9 @@ def __init__( valid_generator: Iterable[Tuple[int, int]] = None, **kwargs: Any ) -> None: BusDriver.__init__(self, entity, name, clock, **kwargs) + self.on = None + self.off = None + # keep this line after the on/off attributes since it overwrites them self.set_valid_generator(valid_generator=valid_generator) def _next_valids(self): @@ -364,7 +368,7 @@ async def polled_socket_attachment(driver, sock): else: driver.log.error(repr(e)) raise - if not len(data): + if not len(data) > 0: driver.log.info("Remote end closed the connection") break driver.append(data) diff --git a/cocotb/regression.py b/cocotb/regression.py index 0595e963..1ee62b61 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -48,10 +48,7 @@ from cocotb import simulator -if "COCOTB_PDB_ON_EXCEPTION" in os.environ: - _pdb_on_exception = True -else: - _pdb_on_exception = False +_pdb_on_exception = "COCOTB_PDB_ON_EXCEPTION" in os.environ # Optional support for coverage collection of testbench files coverage = None @@ -87,7 +84,10 @@ def __init__(self, dut: SimHandle, tests: Iterable[Test], hooks: Iterable[Hook]) hooks (Iterable[Hook]): hooks to tun """ self._dut = dut + self._test = None self._test_task = None + self._test_start_time = None + self._test_start_sim_time = None self._cov = None self.log = _logger self.start_time = time.time() @@ -305,12 +305,11 @@ def handle_result(self, test: RunningTask) -> None: self.execute() def _init_test(self, test: Test) -> Optional[RunningTask]: - """ - Initializes a test. + """Initialize a test. - Records outcome if the initialization fails. - Records skip if the test is skipped. - Saves the initialized test if it successfully initializes. + Record outcome if the initialization fails. + Record skip if the test is skipped. + Save the initialized test if it successfully initializes. """ if test.skip: @@ -324,7 +323,7 @@ def _init_test(self, test: Test) -> Optional[RunningTask]: hilight_end, test.__qualname__)) self._record_result(test, None, 0, 0) - return + return None test_init_outcome = cocotb.outcomes.capture(test, self._dut) @@ -332,7 +331,7 @@ def _init_test(self, test: Test) -> Optional[RunningTask]: self.log.error("Failed to initialize test %s" % test.__qualname__, exc_info=test_init_outcome.error) self._record_result(test, test_init_outcome, 0, 0) - return + return None test = test_init_outcome.get() return test @@ -450,7 +449,7 @@ def execute(self) -> None: return self.tear_down() self._test_task = self._init_test(self._test) - if self._test_task: + if self._test_task is not None: return self._start_test() def _start_test(self) -> None: diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 5e9ab15d..18ac2e23 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -42,23 +42,6 @@ from typing import Any, Union from collections.abc import Coroutine -# Debug mode controlled by environment variables -if "COCOTB_ENABLE_PROFILING" in os.environ: - import cProfile - import pstats - _profile = cProfile.Profile() - _profiling = True -else: - _profiling = False - -# Sadly the Python standard logging module is very slow so it's better not to -# make any calls by testing a boolean flag first -if "COCOTB_SCHEDULER_DEBUG" in os.environ: - _debug = True -else: - _debug = False - - import cocotb import cocotb.decorators from cocotb.decorators import RunningTask @@ -67,7 +50,19 @@ from cocotb.log import SimLog from cocotb.result import TestComplete from cocotb.utils import remove_traceback_frames -from cocotb import _py_compat +from cocotb import outcomes, _py_compat + + +# Debug mode controlled by environment variables +_profiling = "COCOTB_ENABLE_PROFILING" in os.environ +if _profiling: + import cProfile + import pstats + _profile = cProfile.Profile() + +# Sadly the Python standard logging module is very slow so it's better not to +# make any calls by testing a boolean flag first +_debug = "COCOTB_SCHEDULER_DEBUG" in os.environ class InternalError(RuntimeError): @@ -85,9 +80,6 @@ def __exit__(self, *excinfo): _profile.disable() -from cocotb import outcomes - - class external_state: INIT = 0 RUNNING = 1 @@ -113,7 +105,8 @@ def result(self): def _propagate_state(self, new_state): with self.cond: if _debug: - self._log.debug("Changing state from %d -> %d from %s" % (self.state, new_state, threading.current_thread())) + self._log.debug("Changing state from %d -> %d from %s" % ( + self.state, new_state, threading.current_thread())) self.state = new_state self.cond.notify() @@ -146,14 +139,18 @@ def thread_wait(self): if _debug: if self.state == external_state.EXITED: - self._log.debug("Thread %s has exited from %s" % (self.thread, threading.current_thread())) + self._log.debug("Thread %s has exited from %s" % ( + self.thread, threading.current_thread())) elif self.state == external_state.PAUSED: - self._log.debug("Thread %s has called yield from %s" % (self.thread, threading.current_thread())) + self._log.debug("Thread %s has called yield from %s" % ( + self.thread, threading.current_thread())) elif self.state == external_state.RUNNING: - self._log.debug("Thread %s is in RUNNING from %d" % (self.thread, threading.current_thread())) + self._log.debug("Thread %s is in RUNNING from %d" % ( + self.thread, threading.current_thread())) if self.state == external_state.INIT: - raise Exception("Thread %s state was not allowed from %s" % (self.thread, threading.current_thread())) + raise Exception("Thread %s state was not allowed from %s" % ( + self.thread, threading.current_thread())) return self.state @@ -294,8 +291,7 @@ def _check_termination(self): self._mode = Scheduler._MODE_TERM def _test_completed(self, trigger=None): - """Called after a test and its cleanup have completed - """ + """Called after a test and its cleanup have completed""" if _debug: self.log.debug("begin_test called with trigger: %s" % (str(trigger))) @@ -323,7 +319,7 @@ def _test_completed(self, trigger=None): if _debug: self.log.debug("Issue test result to regression object") - # this may scheduler another test + # this may schedule another test cocotb.regression_manager.handle_result(test) # if it did, make sure we handle the test completing @@ -430,7 +426,7 @@ def _event_loop(self, trigger): if _debug: debugstr = "\n\t".join([coro._coro.__qualname__ for coro in scheduling]) - if len(scheduling): + if len(scheduling) > 0: debugstr = "\n\t" + debugstr self.log.debug("%d pending coroutines for event %s%s" % (len(scheduling), str(trigger), debugstr)) @@ -448,6 +444,11 @@ def _event_loop(self, trigger): if _debug: self.log.debug("Scheduled coroutine %s" % (coro._coro.__qualname__)) + # remove our reference to the objects at the end of each loop, + # to try and avoid them being destroyed at a weird time (as + # happened in gh-957) + del coro + # Schedule may have queued up some events so we'll burn through those while self._pending_events: if _debug: @@ -459,7 +460,6 @@ def _event_loop(self, trigger): # to try and avoid them being destroyed at a weird time (as # happened in gh-957) del trigger - del coro del scheduling # no more pending triggers @@ -837,10 +837,12 @@ def schedule(self, coroutine, trigger=None): for ext in self._pending_threads: ext.thread_start() if _debug: - self.log.debug("Blocking from %s on %s" % (threading.current_thread(), ext.thread)) + self.log.debug("Blocking from %s on %s" % ( + threading.current_thread(), ext.thread)) state = ext.thread_wait() if _debug: - self.log.debug("Back from wait on self %s with newstate %d" % (threading.current_thread(), state)) + self.log.debug("Back from wait on self %s with newstate %d" % ( + threading.current_thread(), state)) if state == external_state.EXITED: self._pending_threads.remove(ext) self._pending_events.append(ext.event) @@ -868,7 +870,7 @@ def finish_scheduler(self, exc): def cleanup(self): """Clear up all our state. - Unprime all pending triggers and kill off any coroutines stop all externals. + Unprime all pending triggers and kill off any coroutines, stop all externals. """ # copy since we modify this in kill items = list(self._trigger2coros.items()) From 9df43e5c0a797299ff5abc994da7cbbb31756e5e Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 11 Nov 2020 08:07:07 +0100 Subject: [PATCH 2569/2656] Add missing import of the warnings module --- cocotb/decorators.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 4dbdd866..c59ebb31 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -31,6 +31,7 @@ import functools import inspect import os +import warnings import cocotb from cocotb.log import SimLog From b5ff50addbfe69657125ef6dab507d8f6d8f457f Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Thu, 12 Nov 2020 20:44:09 +0100 Subject: [PATCH 2570/2656] Add pass_total value for VCS in test_array.test_discover_all --- tests/test_cases/test_array/test_array.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py index d52ab037..f3482749 100644 --- a/tests/test_cases/test_array/test_array.py +++ b/tests/test_cases/test_array/test_array.py @@ -351,6 +351,8 @@ async def test_discover_all(dut): pass_total = 1006 else: pass_total = 1038 + elif cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("chronologic simulation vcs")): + pass_total = 606 else: pass_total = 1078 From fa1786d87490ce6674c994cdaa416c4ba188acd1 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 16 Nov 2020 00:01:25 -0600 Subject: [PATCH 2571/2656] Upload coverage results to codecov --- .github/workflows/regression-tests.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml index 080fc1bb..92b23fff 100644 --- a/.github/workflows/regression-tests.yml +++ b/.github/workflows/regression-tests.yml @@ -257,6 +257,7 @@ jobs: run: pip install coverage xunitparser pytest pytest-cov - name: Test (Windows) if: startsWith(matrix.os, 'windows') + id: windowstesting continue-on-error: ${{matrix.may_fail || false}} timeout-minutes: 15 run: | @@ -281,8 +282,16 @@ jobs: run: | pip install tox tox-gh-actions - name: Test (Ubuntu, MacOS) + id: unixtesting if: startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos') continue-on-error: ${{matrix.may_fail || false}} timeout-minutes: 15 run: | tox + + # codecov + - name: Upload to codecov + if: steps.windowstesting.outcome == 'success' || steps.unixtesting.outcome == 'success' + run: | + pip install codecov + codecov From 563f690c227eaf27df1ca5508eaa0ea2b47a1de0 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 15 Nov 2020 10:44:04 -0600 Subject: [PATCH 2572/2656] Replace probot/stale bot with a GHA action --- .github/stale.yml | 59 ------------------------------------- .github/workflows/stale.yml | 21 +++++++++++++ 2 files changed, 21 insertions(+), 59 deletions(-) delete mode 100644 .github/stale.yml create mode 100644 .github/workflows/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index 32842186..00000000 --- a/.github/stale.yml +++ /dev/null @@ -1,59 +0,0 @@ -# Configuration for probot-stale - https://github.com/probot/stale - -# Number of days of inactivity before an Issue or Pull Request becomes stale -daysUntilStale: 60 - -# Number of days of inactivity before an Issue or Pull Request with the stale label is closed. -# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. -daysUntilClose: 7 - -# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) -onlyLabels: - - "type:question" - -# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable -exemptLabels: - - "type:bug" # inquiry was later confirmed a bug of valid feature and 'question' label was not removed - - "type:feature" - -# Set to true to ignore issues in a project (defaults to false) -exemptProjects: false - -# Set to true to ignore issues in a milestone (defaults to false) -exemptMilestones: false - -# Set to true to ignore issues with an assignee (defaults to false) -exemptAssignees: false - -# Label to use when marking as stale -staleLabel: "status:close?" - -# Comment to post when marking as stale. Set to `false` to disable -markComment: > -Has your question or report been resolved? If so please close this issue. If not, you may need to provide more information. -If no more activity on this issue occurs in 7 days, it will be closed. - -# Comment to post when removing the stale label. -# unmarkComment: > -# Your comment here. - -# Comment to post when closing a stale Issue or Pull Request. -# closeComment: > - -# Limit the number of actions per hour, from 1-30. Default is 30 -limitPerRun: 30 - -# Limit to only `issues` or `pulls` -only: issues - -# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': -# pulls: -# daysUntilStale: 30 -# markComment: > -# This pull request has been automatically marked as stale because it has not had -# recent activity. It will be closed if no further activity occurs. Thank you -# for your contributions. - -# issues: -# exemptLabels: -# - confirmed diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 00000000..f8f0d22d --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,21 @@ +name: "Stale Questions" +on: + schedule: + - cron: "00 02 * * *" + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v3 + with: + repo-token: ${{secrets.GITHUB_TOKEN}} + days-before-stale: 30 + days-before-close: 7 + stale-issue-message: > + Has your question been resolved? If so please close this issue. + If it has not been resolved, you may need to provide more information. + If no more activity on this issue occurs in 7 days, it will be closed. + stale-issue-label: "status:stale" + only-labels: "type:question" + exempt-labels: "type:bug,type:feature" From 805d2d5a4482214b943e619b6cb42206f5a1275a Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Tue, 17 Nov 2020 03:04:20 -0600 Subject: [PATCH 2573/2656] Fix sphinx issues (#2205) * Fix sphinx directive spelling * Fix broken intersphinx link * Fix broken link in documentation --- cocotb/clock.py | 2 +- cocotb/decorators.py | 2 +- cocotb/triggers.py | 4 ++-- documentation/source/install.rst | 2 +- documentation/source/newsfragments/2177.removal.rst | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cocotb/clock.py b/cocotb/clock.py index df3bed42..cc268152 100644 --- a/cocotb/clock.py +++ b/cocotb/clock.py @@ -110,7 +110,7 @@ async def custom_clock(): .. versionchanged:: 1.5 Support ``'step'`` as the the *units* argument to mean "simulator time step". - .. deprecation:: 1.5 + .. deprecated:: 1.5 Using None as the the *units* argument is deprecated, use ``'step'`` instead. """ diff --git a/cocotb/decorators.py b/cocotb/decorators.py index c59ebb31..69f463c7 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -491,7 +491,7 @@ class test(coroutine, metaclass=_decorator_helper): .. versionadded:: 1.3 - .. deprecation:: 1.5 + .. deprecated:: 1.5 Using None as the the *timeout_unit* argument is deprecated, use ``'step'`` instead. expect_fail (bool, optional): Don't mark the result as a failure if the test fails. diff --git a/cocotb/triggers.py b/cocotb/triggers.py index 1fa1e0ef..dbaf6046 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -206,7 +206,7 @@ def __init__(self, time_ps, units="step"): .. versionchanged:: 1.5 Support ``'step'`` as the the *units* argument to mean "simulator time step". - .. deprecation:: 1.5 + .. deprecated:: 1.5 Using None as the the *units* argument is deprecated, use ``'step'`` instead. """ GPITrigger.__init__(self) @@ -899,7 +899,7 @@ async def with_timeout(trigger, timeout_time, timeout_unit="step"): .. versionadded:: 1.3 - .. deprecation:: 1.5 + .. deprecated:: 1.5 Using None as the the *timeout_unit* argument is deprecated, use ``'step'`` instead. """ diff --git a/documentation/source/install.rst b/documentation/source/install.rst index b56b17a4..c48476d2 100644 --- a/documentation/source/install.rst +++ b/documentation/source/install.rst @@ -111,7 +111,7 @@ Installation of cocotb If your user does not have permissions to install cocotb using the instructions above, try adding the ``--user`` option to ``pip`` - (see :ref:`the pip documentation`). + (see `the pip documentation `_). .. warning:: diff --git a/documentation/source/newsfragments/2177.removal.rst b/documentation/source/newsfragments/2177.removal.rst index dc05f287..3580da28 100644 --- a/documentation/source/newsfragments/2177.removal.rst +++ b/documentation/source/newsfragments/2177.removal.rst @@ -1 +1 @@ -:class:`~cocotb.result.TestError` has been deprecated, use a standard :ref:`Python Exception `. +:class:`~cocotb.result.TestError` has been deprecated, use :ref:`python:bltin-exceptions`. From e83d2b8feef1a116c63ee2b9fb2fab46b72ed0c4 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 16 Nov 2020 21:18:37 -0600 Subject: [PATCH 2574/2656] Reduce number of operations per stale action from 30 to 5 --- .github/workflows/stale.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index f8f0d22d..01968dd2 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -19,3 +19,4 @@ jobs: stale-issue-label: "status:stale" only-labels: "type:question" exempt-labels: "type:bug,type:feature" + operations-per-run: 5 From f1a74eab2fef0f946e15c8018d51ef289d6eff0f Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Tue, 17 Nov 2020 10:41:56 -0600 Subject: [PATCH 2575/2656] Deprecate cocotb.hook (#2201) Co-authored-by: Eric Wieser --- cocotb/decorators.py | 16 ++++++++++++++-- documentation/source/building.rst | 3 +++ .../source/newsfragments/2201.removal.rst | 2 ++ tests/test_cases/test_cocotb/test_deprecated.py | 8 ++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 documentation/source/newsfragments/2201.removal.rst diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 69f463c7..b2a92366 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -451,15 +451,27 @@ def decorator(f): @public class hook(coroutine, metaclass=_decorator_helper): - """Decorator to mark a function as a hook for cocotb. + r""" + Decorator to mark a function as a hook for cocotb. Used as ``@cocotb.hook()``. All hooks are run at the beginning of a cocotb test suite, prior to any - test code being run.""" + test code being run. + + .. deprecated:: 1.5 + Hooks are deprecated. + Their functionality can be replaced with module-level Python code, + higher-priority tests using the ``stage`` argument to :func:`cocotb.test`\ s, + or custom decorators which perform work before and after the tests + they decorate. + """ def __init__(self, f): super(hook, self).__init__(f) + warnings.warn( + "Hooks have been deprecated. See the documentation for suggestions on alternatives.", + DeprecationWarning, stacklevel=2) self.im_hook = True self.name = self._func.__name__ diff --git a/documentation/source/building.rst b/documentation/source/building.rst index 9f93e766..062b389e 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -168,6 +168,9 @@ Regression Manager A comma-separated list of modules that should be executed before the first test. You can also use the :class:`cocotb.hook` decorator to mark a function to be run before test code. + .. deprecated:: 1.5 + :class:`cocotb.hook` is deprecated, and in the future this variable will be ignored. + Scheduler ~~~~~~~~~ diff --git a/documentation/source/newsfragments/2201.removal.rst b/documentation/source/newsfragments/2201.removal.rst new file mode 100644 index 00000000..36b2b71c --- /dev/null +++ b/documentation/source/newsfragments/2201.removal.rst @@ -0,0 +1,2 @@ +Deprecated :class:`cocotb.hook` and :envvar:`COCOTB_HOOKS`. +See the documentation for :class:`cocotb.hook` for suggestions on alternatives. diff --git a/tests/test_cases/test_cocotb/test_deprecated.py b/tests/test_cases/test_cocotb/test_deprecated.py index f39a9ab8..73725ea1 100644 --- a/tests/test_cases/test_cocotb/test_deprecated.py +++ b/tests/test_cases/test_cocotb/test_deprecated.py @@ -78,3 +78,11 @@ async def test_raise_error_deprecated(dut): with assert_deprecated(): with assert_raises(cocotb.result.TestError): cocotb.result.raise_error(cocotb.triggers.Timer(1), "A test exception") + + +@cocotb.test() +async def test_hook_deprecated(_): + async def example(): + pass + with assert_deprecated(): + cocotb.hook()(example) From 03a1dc78bbd97e321112c2f617543570d3faa9e9 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Tue, 17 Nov 2020 17:33:14 -0600 Subject: [PATCH 2576/2656] Update miniconda-action to v2 (#2210) --- .github/workflows/regression-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml index 92b23fff..db840fc6 100644 --- a/.github/workflows/regression-tests.yml +++ b/.github/workflows/regression-tests.yml @@ -179,7 +179,7 @@ jobs: python-version: ${{matrix.python-version}} - name: Set up Anaconda ${{matrix.python-version}} (Windows) if: startsWith(matrix.os, 'windows') - uses: conda-incubator/setup-miniconda@v1 + uses: conda-incubator/setup-miniconda@v2 with: auto-update-conda: true python-version: ${{matrix.python-version}} From d484ffae94d8b72ea86946798c06cbfc5cfe909d Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 20 Nov 2020 22:46:35 +0100 Subject: [PATCH 2577/2656] Mention Verilog's join/join_any in triggers.py --- cocotb/triggers.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cocotb/triggers.py b/cocotb/triggers.py index dbaf6046..a99d7f3e 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -762,6 +762,8 @@ class Combine(_AggregateWaitable): Fires when all of *triggers* have fired. Like most triggers, this simply returns itself. + + This is similar to Verilog's ``join``. """ __slots__ = () @@ -791,6 +793,8 @@ class First(_AggregateWaitable): Returns the result of the trigger that fired. + This is similar to Verilog's ``join_any``. + .. note:: The event loop is single threaded, so while events may be simultaneous in simulation time, they can never be simultaneous in real time. From 889ac3c22c85af64b6e11379d70de123455eee58 Mon Sep 17 00:00:00 2001 From: Nicola Corna Date: Sun, 22 Nov 2020 17:17:09 +0100 Subject: [PATCH 2578/2656] Add AXI4Master driver (#1248) * Add AXI4Master driver This is based on the existing AXI4Lite driver, which has been extended to use the AXI4 signals. The existing AXI4LiteMaster has been modified to wrap the AXI4Master methods, providing so a backwards-compatible class. * Add return_rresp argument to AXI4 Master driver With return_rresp true, the list of RRESP values for each beat is returned, instead of just raising an exception if any RRESP is not OKAY. * Check the number of read words in the AXI4 master driver * Change default value of byte_enable from 0xf to None in the AXI4Master driver 0xf makes sense only for 32-bit wide data buses. The default behavior is left unchanged, as None is interpreted as "write all the bytes" (WSTRB all ones). * Implement locks in the AXI4 master driver for all the channels * Add xresp attribute to AXIProtocolError * Add tests for AXI4 and AXI4-Lite master drivers * Add typing informations for AXI4Master and AXI4LiteMaster * Add AXI4Master to library_reference.rst * Fix concurrent read operations in the AXI4Master driver The "read data" phase did not awaited the last clock rising edge, leading to a double read of the same word by two subsequent reads. Additionally, change the test register types to better highlight possible bugs. * Fix narrow bursts in the AXI4Master driver * Add more tests to the AXI4Master driver * Correctly handle AXI4 unaligned reads and writes With this commit, unaligned transfers are automatically re-aligned by the driver (for INCR or WRAP bursts) or only the high-order bits are used (for FIXED bursts). This might break some tests where the user manually performs the realignment of the words, which is now automatically done by the driver, or when it uses the low-order bits on FIXED reads (or the first word of a INCR/WRAP), which, by AXI4 specification, should not should be used. * Align the addresses in the axi_lite_slave test The axi_lite_slave design uses unaligned addresses but returns the data always on the same byte lane, which is not conformant to the AXI specification. This causes a failure on the corresponding test, as the new AXI4Master driver now handles the unaligned transfers, re-aligning them for the user. Fix it by changing the addresses in the design and in the test to 0, 4 and 8, which are aligned and do not cause any re-alignement from the driver. --- cocotb/drivers/amba.py | 590 ++++++++--- documentation/source/library_reference.rst | 5 +- examples/axi_lite_slave/hdl/axi_lite_demo.v | 2 +- .../tests/test_axi_lite_slave.py | 12 +- tests/designs/axi4_ram/Makefile | 32 + tests/designs/axi4_ram/README.md | 3 + tests/designs/axi4_ram/arbiter.v | 153 +++ tests/designs/axi4_ram/axi_interconnect.v | 941 ++++++++++++++++++ tests/designs/axi4_ram/axi_ram.v | 370 +++++++ tests/designs/axi4_ram/axi_register.v | 320 ++++++ tests/designs/axi4_ram/axi_register_rd.v | 526 ++++++++++ tests/designs/axi4_ram/axi_register_wr.v | 687 +++++++++++++ tests/designs/axi4_ram/priority_encoder.v | 84 ++ tests/designs/axi4_ram/top.v | 467 +++++++++ tests/test_cases/test_axi4/Makefile | 3 + tests/test_cases/test_axi4/test_axi4.py | 532 ++++++++++ 16 files changed, 4606 insertions(+), 121 deletions(-) create mode 100644 tests/designs/axi4_ram/Makefile create mode 100644 tests/designs/axi4_ram/README.md create mode 100644 tests/designs/axi4_ram/arbiter.v create mode 100644 tests/designs/axi4_ram/axi_interconnect.v create mode 100644 tests/designs/axi4_ram/axi_ram.v create mode 100644 tests/designs/axi4_ram/axi_register.v create mode 100644 tests/designs/axi4_ram/axi_register_rd.v create mode 100644 tests/designs/axi4_ram/axi_register_wr.v create mode 100644 tests/designs/axi4_ram/priority_encoder.v create mode 100644 tests/designs/axi4_ram/top.v create mode 100644 tests/test_cases/test_axi4/Makefile create mode 100644 tests/test_cases/test_axi4/test_axi4.py diff --git a/cocotb/drivers/amba.py b/cocotb/drivers/amba.py index ca0e3c56..d2cca2f5 100644 --- a/cocotb/drivers/amba.py +++ b/cocotb/drivers/amba.py @@ -26,31 +26,61 @@ """Drivers for Advanced Microcontroller Bus Architecture.""" +import array +import collections.abc +import enum +import itertools +from typing import Any, List, Optional, Sequence, Tuple, Union + import cocotb -from cocotb.triggers import RisingEdge, ReadOnly, Lock -from cocotb.drivers import BusDriver from cocotb.binary import BinaryValue +from cocotb.drivers import BusDriver +from cocotb.handle import SimHandleBase +from cocotb.triggers import ClockCycles, Combine, Lock, ReadOnly, RisingEdge -import array + +class AXIBurst(enum.IntEnum): + FIXED = 0b00 + INCR = 0b01 + WRAP = 0b10 + + +class AXIxRESP(enum.IntEnum): + OKAY = 0b00 + EXOKAY = 0b01 + SLVERR = 0b10 + DECERR = 0b11 class AXIProtocolError(Exception): + def __init__(self, message: str, xresp: AXIxRESP): + super().__init__(message) + self.xresp = xresp + + +class AXIReadBurstLengthMismatch(Exception): pass -class AXI4LiteMaster(BusDriver): - """AXI4-Lite Master. +class AXI4Master(BusDriver): + """AXI4 Master TODO: Kill all pending transactions if reset is asserted. """ - _signals = ["AWVALID", "AWADDR", "AWREADY", # Write address channel - "WVALID", "WREADY", "WDATA", "WSTRB", # Write data channel - "BVALID", "BREADY", "BRESP", # Write response channel - "ARVALID", "ARADDR", "ARREADY", # Read address channel - "RVALID", "RREADY", "RRESP", "RDATA"] # Read data channel - - def __init__(self, entity, name, clock, **kwargs): + _signals = [ + "AWVALID", "AWADDR", "AWREADY", "AWID", "AWLEN", "AWSIZE", "AWBURST", + "WVALID", "WREADY", "WDATA", "WSTRB", + "BVALID", "BREADY", "BRESP", "BID", + "ARVALID", "ARADDR", "ARREADY", "ARID", "ARLEN", "ARSIZE", "ARBURST", + "RVALID", "RREADY", "RRESP", "RDATA", "RID", "RLAST"] + + _optional_signals = ["AWREGION", "AWLOCK", "AWCACHE", "AWPROT", "AWQOS", + "WLAST", + "ARREGION", "ARLOCK", "ARCACHE", "ARPROT", "ARQOS"] + + def __init__(self, entity: SimHandleBase, name: str, clock: SimHandleBase, + **kwargs: Any): BusDriver.__init__(self, entity, name, clock, **kwargs) # Drive some sensible defaults (setimmediatevalue to avoid x asserts) @@ -60,153 +90,487 @@ def __init__(self, entity, name, clock, **kwargs): self.bus.BREADY.setimmediatevalue(1) self.bus.RREADY.setimmediatevalue(1) - # Mutex for each channel that we master to prevent contention - self.write_address_busy = Lock("%s_wabusy" % name) - self.read_address_busy = Lock("%s_rabusy" % name) - self.write_data_busy = Lock("%s_wbusy" % name) - - async def _send_write_address(self, address, delay=0): - """ - Send the write address, with optional delay (in clocks) - """ - await self.write_address_busy.acquire() - for cycle in range(delay): - await RisingEdge(self.clock) - - self.bus.AWADDR <= address - self.bus.AWVALID <= 1 - - while True: - await ReadOnly() - if self.bus.AWREADY.value: - break - await RisingEdge(self.clock) - await RisingEdge(self.clock) - self.bus.AWVALID <= 0 - self.write_address_busy.release() - - async def _send_write_data(self, data, delay=0, byte_enable=0xF): - """Send the write address, with optional delay (in clocks).""" - await self.write_data_busy.acquire() - for cycle in range(delay): + # Set the default value (0) for the unsupported signals, which + # translate to: + # * Transaction IDs to 0 + # * Region identifier to 0 + # * Normal (non-exclusive) access + # * Device non-bufferable access + # * Unprivileged, secure data access + # * No QoS + unsupported_signals = [ + "AWID", "AWREGION", "AWLOCK", "AWCACHE", "AWPROT", "AWQOS", + "ARID", "ARREGION", "ARLOCK", "ARCACHE", "ARPROT", "ARQOS"] + for signal in unsupported_signals: + try: + getattr(self.bus, signal).setimmediatevalue(0) + except AttributeError: + pass + + # Mutex for each channel to prevent contention + self.write_address_busy = Lock(name + "_awbusy") + self.read_address_busy = Lock(name + "_arbusy") + self.write_data_busy = Lock(name + "_wbusy") + self.read_data_busy = Lock(name + "_rbusy") + self.write_response_busy = Lock(name + "_bbusy") + + @staticmethod + def _check_length(length: int, burst: AXIBurst) -> None: + """Check that the provided burst length is valid.""" + if length <= 0: + raise ValueError("Burst length must be a positive integer") + + if burst is AXIBurst.INCR: + if length > 256: + raise ValueError("Maximum burst length for INCR bursts is 256") + elif burst is AXIBurst.WRAP: + if length not in (1, 2, 4, 8, 16): + raise ValueError("Burst length for WRAP bursts must be 1, 2, " + "4, 8 or 16") + else: + if length > 16: + raise ValueError("Maximum burst length for FIXED bursts is 16") + + @staticmethod + def _check_size(size: int, data_bus_width: int) -> None: + """Check that the provided transfer size is valid.""" + if size > data_bus_width: + raise ValueError("Beat size ({} B) is larger than the bus width " + "({} B)".format(size, data_bus_width)) + elif size <= 0 or size & (size - 1) != 0: + raise ValueError("Beat size must be a positive power of 2") + + @staticmethod + def _check_4kB_boundary_crossing(address: int, burst: AXIBurst, size: int, + length: int) -> None: + """Check that the provided burst does not cross a 4kB boundary.""" + if burst is AXIBurst.INCR: + last_address = address + size * (length - 1) + if address & ~0xfff != last_address & ~0xfff: + raise ValueError( + "INCR burst with start address {:#x} and last address " + "{:#x} crosses the 4kB boundary {:#x}, which is forbidden " + "in a single burst" + .format(address, last_address, + (address & ~0xfff) + 0x1000)) + + async def _send_write_address( + self, address: int, length: int, burst: AXIBurst, size: int, + delay: int, sync: bool + ) -> None: + """Send the write address, with optional delay (in clocks)""" + async with self.write_address_busy: + if sync: + await RisingEdge(self.clock) + + await ClockCycles(self.clock, delay) + + # Set the address and, if present on the bus, burst, length and + # size + self.bus.AWADDR <= address + self.bus.AWVALID <= 1 + + if hasattr(self.bus, "AWBURST"): + self.bus.AWBURST <= burst.value + + if hasattr(self.bus, "AWLEN"): + self.bus.AWLEN <= length - 1 + + if hasattr(self.bus, "AWSIZE"): + self.bus.AWSIZE <= size.bit_length() - 1 + + # Wait until acknowledged + while True: + await ReadOnly() + if self.bus.AWREADY.value: + break + await RisingEdge(self.clock) await RisingEdge(self.clock) + self.bus.AWVALID <= 0 + + async def _send_write_data( + self, address, data: Sequence[int], burst: AXIBurst, size: int, + delay: int, byte_enable: Sequence[Optional[int]], sync: bool + ) -> None: + """Send the write data, with optional delay (in clocks).""" + + # Helper function for narrow bursts + def mask_and_shift(value: int, block_size: int, block_num: int) -> int: + return (value & (2**block_size - 1)) << (block_num * block_size) + + # [0x33221100, 0x77665544] --> [0x221100XX, 0x66554433] + def unalign_data( + data: Sequence[int], size_bits: int, shift: int + ) -> List[int]: + padded_data = (0,) + tuple(value for value in data) + low_mask = 2**(size_bits - shift) - 1 + high_mask = (2**shift - 1) << (size_bits - shift) + + return [(padded_data[i] & high_mask) >> (size_bits - shift) | + (padded_data[i + 1] & low_mask) << shift + for i in range(len(data))] + + strobes = [] + byte_enable_iterator = iter(byte_enable) + try: + for i in range(len(data)): + current_byte_enable = next(byte_enable_iterator) + strobes.append(2**size - 1 if current_byte_enable is None + else current_byte_enable) + except StopIteration: + # Fill the remaining strobes with the last one if we have reached + # the end of the iterator + strobes += [strobes[-1]] * (len(data) - i) + + # Unalign the words and strobes (if unaligned and not FIXED) + if address % size != 0: + shift = (address % size) * 8 + if burst is AXIBurst.FIXED: + data = [(word << shift) & (2**(size * 8) - 1) for word in data] + strobes = \ + [(strb << shift // 8) & (2**size - 1) for strb in strobes] + else: + data = unalign_data(data, size * 8, shift) + strobes = unalign_data(strobes, size, address % size) + + async with self.write_data_busy: + if sync: + await RisingEdge(self.clock) + + wdata_bytes = len(self.bus.WDATA) // 8 + narrow_block = (address % wdata_bytes) // size + + for beat_num, (word, strobe) in enumerate(zip(data, strobes)): + await ClockCycles(self.clock, delay) + + self.bus.WVALID <= 1 + self.bus.WDATA <= mask_and_shift(word, size * 8, narrow_block) + self.bus.WSTRB <= mask_and_shift(strobe, size, narrow_block) + + if burst is not AXIBurst.FIXED: + narrow_block = (narrow_block + 1) % (wdata_bytes // size) + + if hasattr(self.bus, "WLAST"): + if beat_num == len(data) - 1: + self.bus.WLAST <= 1 + else: + self.bus.WLAST <= 0 + + while True: + await RisingEdge(self.clock) + if self.bus.WREADY.value: + break - self.bus.WDATA <= data - self.bus.WVALID <= 1 - self.bus.WSTRB <= byte_enable - - while True: - await ReadOnly() - if self.bus.WREADY.value: - break - await RisingEdge(self.clock) - await RisingEdge(self.clock) - self.bus.WVALID <= 0 - self.write_data_busy.release() + if beat_num == len(data) - 1: + self.bus.WVALID <= 0 @cocotb.coroutine async def write( - self, address: int, value: int, byte_enable: int = 0xf, + self, address: int, value: Union[int, Sequence[int]], *, + size: Optional[int] = None, burst: AXIBurst = AXIBurst.INCR, + byte_enable: Union[Optional[int], Sequence[Optional[int]]] = None, address_latency: int = 0, data_latency: int = 0, sync: bool = True - ) -> BinaryValue: + ) -> None: """Write a value to an address. + With unaligned writes (when ``address`` is not a multiple of ``size``), + only the low ``size - address % size`` Bytes are written for: + * the last element of ``value`` for INCR or WRAP bursts, or + * every element of ``value`` for FIXED bursts. + Args: address: The address to write to. - value: The data value to write. - byte_enable: Which bytes in value to actually write. - Default is to write all bytes. - address_latency: Delay before setting the address (in clock cycles). - Default is no delay. - data_latency: Delay before setting the data value (in clock cycles). + value: The data value(s) to write. + size: The size (in bytes) of each beat. Defaults to None (width of + the data bus). + burst: The burst type, either ``FIXED``, ``INCR`` or ``WRAP``. + Defaults to ``INCR``. + byte_enable: Which bytes in value to actually write. Defaults to + None (write all bytes). + address_latency: Delay before setting the address (in clock + cycles). Default is no delay. + data_latency: Delay before setting the data value (in clock + cycles). Default is no delay. sync: Wait for rising edge on clock initially. Defaults to True. - Returns: - The write response value. - Raises: + ValueError: If any of the input parameters is invalid. AXIProtocolError: If write response from AXI is not ``OKAY``. """ - if sync: - await RisingEdge(self.clock) - c_addr = cocotb.fork(self._send_write_address(address, - delay=address_latency)) - c_data = cocotb.fork(self._send_write_data(value, - byte_enable=byte_enable, - delay=data_latency)) + if not isinstance(value, collections.abc.Sequence): + value = (value,) # If value is not a sequence, make it - if c_addr: - await c_addr.join() - if c_data: - await c_data.join() + if not isinstance(byte_enable, collections.abc.Sequence): + byte_enable = (byte_enable,) # Same for byte_enable - # Wait for the response - while True: - await ReadOnly() - if self.bus.BVALID.value and self.bus.BREADY.value: - result = self.bus.BRESP.value - break - await RisingEdge(self.clock) + if size is None: + size = len(self.bus.WDATA) // 8 + else: + AXI4Master._check_size(size, len(self.bus.WDATA) // 8) + + AXI4Master._check_length(len(value), burst) + AXI4Master._check_4kB_boundary_crossing(address, burst, size, + len(value)) - await RisingEdge(self.clock) + write_address = self._send_write_address(address, len(value), burst, + size, address_latency, sync) - if int(result): - raise AXIProtocolError("Write to address 0x%08x failed with BRESP: %d" - % (address, int(result))) + write_data = self._send_write_data(address, value, burst, size, + data_latency, byte_enable, sync) - return result + await Combine(cocotb.fork(write_address), cocotb.fork(write_data)) + + async with self.write_response_busy: + # Wait for the response + while True: + await ReadOnly() + if self.bus.BVALID.value and self.bus.BREADY.value: + result = AXIxRESP(self.bus.BRESP.value.integer) + break + await RisingEdge(self.clock) + + await RisingEdge(self.clock) + + if result is not AXIxRESP.OKAY: + err_msg = "Write to address {0:#x}" + if len(value) != 1: + err_msg += " ({1} beats, {2} burst)" + err_msg += " failed with BRESP: {3} ({4})" + + raise AXIProtocolError( + err_msg.format(address, len(value), burst.name, + result.value, result.name), result) @cocotb.coroutine - async def read(self, address: int, sync: bool = True) -> BinaryValue: + async def read( + self, address: int, length: int = 1, *, + size: Optional[int] = None, burst: AXIBurst = AXIBurst.INCR, + return_rresp: bool = False, sync: bool = True + ) -> Union[List[BinaryValue], List[Tuple[BinaryValue, AXIxRESP]]]: """Read from an address. + With unaligned reads (when ``address`` is not a multiple of ``size``) + with INCR or WRAP bursts, the last element of the returned read data + will be only the low-order ``size - address % size`` Bytes of the last + word. + With unaligned reads with FIXED bursts, every element of the returned + read data will be only the low-order ``size - address % size`` Bytes. + Args: address: The address to read from. - sync: Wait for rising edge on clock initially. - Defaults to True. + length: Number of words to transfer. Defaults to 1. + size: The size (in bytes) of each beat. Defaults to None (width of + the data bus). + burst: The burst type, either ``FIXED``, ``INCR`` or ``WRAP``. + Defaults to ``INCR``. + return_rresp: Return the list of RRESP values, instead of raising + an AXIProtocolError in case of not OKAY. Defaults to False. + sync: Wait for rising edge on clock initially. Defaults to True. Returns: - The read data value. + The read data values or, if *return_rresp* is True, a list of pairs + each containing the data and RRESP values.- Raises: - AXIProtocolError: If read response from AXI is not ``OKAY``. + ValueError: If any of the input parameters is invalid. + AXIProtocolError: If read response from AXI is not ``OKAY`` and + *return_rresp* is False + AXIReadBurstLengthMismatch: If the received number of words does + not match the requested one. """ - if sync: - await RisingEdge(self.clock) - self.bus.ARADDR <= address - self.bus.ARVALID <= 1 + # Helper function for narrow bursts + def shift_and_mask(binvalue: BinaryValue, bytes_num: int, + byte_shift: int) -> BinaryValue: + start = byte_shift * 8 + end = (bytes_num + byte_shift) * 8 + return binvalue[len(binvalue) - end:len(binvalue) - start - 1] - while True: - await ReadOnly() - if self.bus.ARREADY.value: - break - await RisingEdge(self.clock) + # [0x221100XX, 0x66554433] --> [0x33221100, 0x665544] + def realign_data( + data: Sequence[BinaryValue], size_bits: int, shift: int + ) -> List[BinaryValue]: + binstr_join = "".join([word.binstr[::-1] for word in data]) + binstr_join = binstr_join[shift:] + data_binstr = [binstr_join[i * size_bits:(i + 1) * size_bits][::-1] + for i in range(len(data))] + return [BinaryValue(value=binstr, n_bits=len(binstr)) + for binstr in data_binstr] - await RisingEdge(self.clock) - self.bus.ARVALID <= 0 + if size is None: + size = len(self.bus.RDATA) // 8 + else: + AXI4Master._check_size(size, len(self.bus.RDATA) // 8) + + AXI4Master._check_length(length, burst) + AXI4Master._check_4kB_boundary_crossing(address, burst, size, length) + + rdata_bytes = len(self.bus.RDATA) // 8 + byte_offset = (address % rdata_bytes) // size * size + + async with self.read_address_busy: + if sync: + await RisingEdge(self.clock) + + self.bus.ARADDR <= address + self.bus.ARVALID <= 1 + + if hasattr(self.bus, "ARLEN"): + self.bus.ARLEN <= length - 1 + + if hasattr(self.bus, "ARSIZE"): + self.bus.ARSIZE <= size.bit_length() - 1 + + if hasattr(self.bus, "ARBURST"): + self.bus.ARBURST <= burst.value + + while True: + await ReadOnly() + if self.bus.ARREADY.value: + break + await RisingEdge(self.clock) - while True: - await ReadOnly() - if self.bus.RVALID.value and self.bus.RREADY.value: - data = self.bus.RDATA.value - result = self.bus.RRESP.value - break await RisingEdge(self.clock) + self.bus.ARVALID <= 0 + + async with self.read_data_busy: + data = [] + rresp = [] - if int(result): - raise AXIProtocolError("Read address 0x%08x failed with RRESP: %d" % - (address, int(result))) + for beat_num in itertools.count(): + while True: + await ReadOnly() + if self.bus.RVALID.value and self.bus.RREADY.value: + # Shift and mask to correctly handle narrow bursts + beat_value = shift_and_mask(self.bus.RDATA.value, + size, byte_offset) - return data + data.append(beat_value) + rresp.append(AXIxRESP(self.bus.RRESP.value.integer)) + + if burst is not AXIBurst.FIXED: + byte_offset = (byte_offset + size) % rdata_bytes + + break + await RisingEdge(self.clock) + + if not hasattr(self.bus, "RLAST") or self.bus.RLAST.value: + break + + await RisingEdge(self.clock) + + await RisingEdge(self.clock) + + if len(data) != length: + raise AXIReadBurstLengthMismatch( + "AXI4 slave returned {} data than expected (requested {} " + "words, received {})" + .format("more" if len(data) > length else "less", + length, len(data))) + + # Re-align the words + if address % size != 0: + shift = (address % size) * 8 + if burst is AXIBurst.FIXED: + data = [word[0:size * 8 - shift - 1] for word in data] + else: + data = realign_data(data, size * 8, shift) + + if return_rresp: + return list(zip(data, rresp)) + else: + for beat_number, beat_result in enumerate(rresp): + if beat_result is not AXIxRESP.OKAY: + err_msg = "Read on address {0:#x}" + if length != 1: + err_msg += " (beat {1} of {2}, {3} burst)" + err_msg += " failed with RRESP: {4} ({5})" + + err_msg = err_msg.format( + address, beat_number + 1, length, burst, + beat_result.value, beat_result.name) + + raise AXIProtocolError(err_msg, beat_result) + + return data def __len__(self): return 2**len(self.bus.ARADDR) +class AXI4LiteMaster(AXI4Master): + """AXI4-Lite Master""" + + _signals = ["AWVALID", "AWADDR", "AWREADY", # Write address channel + "WVALID", "WREADY", "WDATA", "WSTRB", # Write data channel + "BVALID", "BREADY", "BRESP", # Write response channel + "ARVALID", "ARADDR", "ARREADY", # Read address channel + "RVALID", "RREADY", "RRESP", "RDATA"] # Read data channel + + _optional_signals = [] + + @cocotb.coroutine + async def write( + self, address: int, value: int, byte_enable: Optional[int] = None, + address_latency: int = 0, data_latency: int = 0, sync: bool = True + ) -> BinaryValue: + """Write a value to an address. + + Args: + address: The address to write to. + value: The data value to write. + byte_enable: Which bytes in value to actually write. Defaults to + None (write all bytes). + address_latency: Delay before setting the address (in clock + cycles). Default is no delay. + data_latency: Delay before setting the data value (in clock + cycles). Default is no delay. + sync: Wait for rising edge on clock initially. Defaults to True. + + Returns: + The write response value. + + Raises: + ValueError: If any of the input parameters is invalid. + AXIProtocolError: If write response from AXI is not ``OKAY``. + """ + + if isinstance(value, collections.abc.Sequence): + raise ValueError("AXI4-Lite does not support burst transfers") + + await super().write( + address=address, value=value, size=None, burst=AXIBurst.INCR, + byte_enable=byte_enable, address_latency=address_latency, + data_latency=data_latency, sync=sync) + + # Needed for backwards compatibility + return BinaryValue(value=AXIxRESP.OKAY.value, n_bits=2) + + @cocotb.coroutine + async def read(self, address: int, sync: bool = True) -> BinaryValue: + """Read from an address. + + Args: + address: The address to read from. + length: Number of words to transfer + sync: Wait for rising edge on clock initially. Defaults to True. + + Returns: + The read data value. + + Raises: + AXIProtocolError: If read response from AXI is not ``OKAY``. + """ + + ret = await super().read(address=address, length=1, size=None, + burst=AXIBurst.INCR, return_rresp=False, + sync=sync) + return ret[0] + + class AXI4Slave(BusDriver): ''' AXI4 Slave diff --git a/documentation/source/library_reference.rst b/documentation/source/library_reference.rst index e57ff39c..b597a3f2 100644 --- a/documentation/source/library_reference.rst +++ b/documentation/source/library_reference.rst @@ -263,10 +263,13 @@ Advanced Microcontroller Bus Architecture. .. currentmodule:: cocotb.drivers.amba -.. autoclass:: AXI4LiteMaster +.. autoclass:: AXI4Master :members: :member-order: bysource +.. autoclass:: AXI4LiteMaster + :members: + :member-order: bysource .. autoclass:: AXI4Slave :members: diff --git a/examples/axi_lite_slave/hdl/axi_lite_demo.v b/examples/axi_lite_slave/hdl/axi_lite_demo.v index 5a591dab..e4da83c5 100644 --- a/examples/axi_lite_slave/hdl/axi_lite_demo.v +++ b/examples/axi_lite_slave/hdl/axi_lite_demo.v @@ -69,7 +69,7 @@ module axi_lite_demo #( //Address Map localparam ADDR_0 = 0; -localparam ADDR_1 = 1; +localparam ADDR_1 = 4; localparam MAX_ADDR = ADDR_1; diff --git a/examples/axi_lite_slave/tests/test_axi_lite_slave.py b/examples/axi_lite_slave/tests/test_axi_lite_slave.py index 80add4c3..93ca4124 100644 --- a/examples/axi_lite_slave/tests/test_axi_lite_slave.py +++ b/examples/axi_lite_slave/tests/test_axi_lite_slave.py @@ -47,10 +47,10 @@ async def write_address_0(dut): dut.log.info("Write 0x%08X to address 0x%08X" % (int(value), ADDRESS)) -# Read back a value at address 0x01 +# Read back a value at address 0x04 @cocotb.test() -async def read_address_1(dut): - """Use cocotb to set the value of the register at address 0x01. +async def read_address_4(dut): + """Use cocotb to set the value of the register at address 0x04. Use AXIML to read the contents of that register and compare the values. @@ -67,7 +67,7 @@ async def read_address_1(dut): await Timer(CLK_PERIOD_NS * 10, units='ns') dut.rst <= 0 await Timer(CLK_PERIOD_NS, units='ns') - ADDRESS = 0x01 + ADDRESS = 0x04 DATA = 0xCD dut.dut.r_temp_1 <= DATA @@ -135,7 +135,7 @@ async def write_fail(dut): await Timer(CLK_PERIOD_NS * 10, units='ns') dut.rst <= 0 - ADDRESS = 0x02 + ADDRESS = 0x08 DATA = 0xAB try: @@ -166,7 +166,7 @@ async def read_fail(dut): await Timer(CLK_PERIOD_NS * 10, units='ns') dut.rst <= 0 - ADDRESS = 0x02 + ADDRESS = 0x08 DATA = 0xAB try: diff --git a/tests/designs/axi4_ram/Makefile b/tests/designs/axi4_ram/Makefile new file mode 100644 index 00000000..d4ebda9d --- /dev/null +++ b/tests/designs/axi4_ram/Makefile @@ -0,0 +1,32 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause + +TOPLEVEL_LANG ?= verilog + +ifneq ($(TOPLEVEL_LANG),verilog) + +all: + @echo "Skipping test due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being verilog" +clean:: + +else + +TOPLEVEL := top + +PWD=$(shell pwd) + +COCOTB?=$(PWD)/../../.. + +VERILOG_SOURCES = $(COCOTB)/tests/designs/axi4_ram/axi_register_rd.v +VERILOG_SOURCES += $(COCOTB)/tests/designs/axi4_ram/axi_register_wr.v +VERILOG_SOURCES += $(COCOTB)/tests/designs/axi4_ram/axi_register.v +VERILOG_SOURCES += $(COCOTB)/tests/designs/axi4_ram/priority_encoder.v +VERILOG_SOURCES += $(COCOTB)/tests/designs/axi4_ram/arbiter.v +VERILOG_SOURCES += $(COCOTB)/tests/designs/axi4_ram/axi_interconnect.v +VERILOG_SOURCES += $(COCOTB)/tests/designs/axi4_ram/axi_ram.v +VERILOG_SOURCES += $(COCOTB)/tests/designs/axi4_ram/top.v + +include $(shell cocotb-config --makefiles)/Makefile.sim + +endif diff --git a/tests/designs/axi4_ram/README.md b/tests/designs/axi4_ram/README.md new file mode 100644 index 00000000..12a41c82 --- /dev/null +++ b/tests/designs/axi4_ram/README.md @@ -0,0 +1,3 @@ +# Verilog-AXI + +Verilog files taken taken from https://github.com/alexforencich/verilog-axi diff --git a/tests/designs/axi4_ram/arbiter.v b/tests/designs/axi4_ram/arbiter.v new file mode 100644 index 00000000..8b0443fd --- /dev/null +++ b/tests/designs/axi4_ram/arbiter.v @@ -0,0 +1,153 @@ +/* + +Copyright (c) 2014-2018 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. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Arbiter module + */ +module arbiter # +( + parameter PORTS = 4, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter TYPE = "PRIORITY", + // block type: "NONE", "REQUEST", "ACKNOWLEDGE" + parameter BLOCK = "NONE", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "LOW" +) +( + input wire clk, + input wire rst, + + input wire [PORTS-1:0] request, + input wire [PORTS-1:0] acknowledge, + + output wire [PORTS-1:0] grant, + output wire grant_valid, + output wire [$clog2(PORTS)-1:0] grant_encoded +); + +reg [PORTS-1:0] grant_reg = 0, grant_next; +reg grant_valid_reg = 0, grant_valid_next; +reg [$clog2(PORTS)-1:0] grant_encoded_reg = 0, grant_encoded_next; + +assign grant_valid = grant_valid_reg; +assign grant = grant_reg; +assign grant_encoded = grant_encoded_reg; + +wire request_valid; +wire [$clog2(PORTS)-1:0] request_index; +wire [PORTS-1:0] request_mask; + +priority_encoder #( + .WIDTH(PORTS), + .LSB_PRIORITY(LSB_PRIORITY) +) +priority_encoder_inst ( + .input_unencoded(request), + .output_valid(request_valid), + .output_encoded(request_index), + .output_unencoded(request_mask) +); + +reg [PORTS-1:0] mask_reg = 0, mask_next; + +wire masked_request_valid; +wire [$clog2(PORTS)-1:0] masked_request_index; +wire [PORTS-1:0] masked_request_mask; + +priority_encoder #( + .WIDTH(PORTS), + .LSB_PRIORITY(LSB_PRIORITY) +) +priority_encoder_masked ( + .input_unencoded(request & mask_reg), + .output_valid(masked_request_valid), + .output_encoded(masked_request_index), + .output_unencoded(masked_request_mask) +); + +always @* begin + grant_next = 0; + grant_valid_next = 0; + grant_encoded_next = 0; + mask_next = mask_reg; + + if (BLOCK == "REQUEST" && grant_reg & request) begin + // granted request still asserted; hold it + grant_valid_next = grant_valid_reg; + grant_next = grant_reg; + grant_encoded_next = grant_encoded_reg; + end else if (BLOCK == "ACKNOWLEDGE" && grant_valid && !(grant_reg & acknowledge)) begin + // granted request not yet acknowledged; hold it + grant_valid_next = grant_valid_reg; + grant_next = grant_reg; + grant_encoded_next = grant_encoded_reg; + end else if (request_valid) begin + if (TYPE == "PRIORITY") begin + grant_valid_next = 1; + grant_next = request_mask; + grant_encoded_next = request_index; + end else if (TYPE == "ROUND_ROBIN") begin + if (masked_request_valid) begin + grant_valid_next = 1; + grant_next = masked_request_mask; + grant_encoded_next = masked_request_index; + if (LSB_PRIORITY == "LOW") begin + mask_next = {PORTS{1'b1}} >> (PORTS - masked_request_index); + end else begin + mask_next = {PORTS{1'b1}} << (masked_request_index + 1); + end + end else begin + grant_valid_next = 1; + grant_next = request_mask; + grant_encoded_next = request_index; + if (LSB_PRIORITY == "LOW") begin + mask_next = {PORTS{1'b1}} >> (PORTS - request_index); + end else begin + mask_next = {PORTS{1'b1}} << (request_index + 1); + end + end + end + end +end + +always @(posedge clk) begin + if (rst) begin + grant_reg <= 0; + grant_valid_reg <= 0; + grant_encoded_reg <= 0; + mask_reg <= 0; + end else begin + grant_reg <= grant_next; + grant_valid_reg <= grant_valid_next; + grant_encoded_reg <= grant_encoded_next; + mask_reg <= mask_next; + end +end + +endmodule diff --git a/tests/designs/axi4_ram/axi_interconnect.v b/tests/designs/axi4_ram/axi_interconnect.v new file mode 100644 index 00000000..bf060c8a --- /dev/null +++ b/tests/designs/axi4_ram/axi_interconnect.v @@ -0,0 +1,941 @@ +/* + +Copyright (c) 2018 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. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 interconnect + */ +module axi_interconnect # +( + // Number of AXI inputs (slave interfaces) + parameter S_COUNT = 4, + // Number of AXI outputs (master interfaces) + parameter M_COUNT = 4, + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Propagate awuser signal + parameter AWUSER_ENABLE = 0, + // Width of awuser signal + parameter AWUSER_WIDTH = 1, + // Propagate wuser signal + parameter WUSER_ENABLE = 0, + // Width of wuser signal + parameter WUSER_WIDTH = 1, + // Propagate buser signal + parameter BUSER_ENABLE = 0, + // Width of buser signal + parameter BUSER_WIDTH = 1, + // Propagate aruser signal + parameter ARUSER_ENABLE = 0, + // Width of aruser signal + parameter ARUSER_WIDTH = 1, + // Propagate ruser signal + parameter RUSER_ENABLE = 0, + // Width of ruser signal + parameter RUSER_WIDTH = 1, + // Propagate ID field + parameter FORWARD_ID = 0, + // Number of regions per master interface + parameter M_REGIONS = 1, + // Master interface base addresses + // M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_WIDTH bits + // set to zero for default addressing based on M_ADDR_WIDTH + parameter M_BASE_ADDR = 0, + // Master interface address widths + // M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits + parameter M_ADDR_WIDTH = {M_COUNT{{M_REGIONS{32'd24}}}}, + // Read connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT_READ = {M_COUNT{{S_COUNT{1'b1}}}}, + // Write connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT_WRITE = {M_COUNT{{S_COUNT{1'b1}}}}, + // Secure master (fail operations based on awprot/arprot) + // M_COUNT bits + parameter M_SECURE = {M_COUNT{1'b0}} +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interfaces + */ + input wire [S_COUNT*ID_WIDTH-1:0] s_axi_awid, + input wire [S_COUNT*ADDR_WIDTH-1:0] s_axi_awaddr, + input wire [S_COUNT*8-1:0] s_axi_awlen, + input wire [S_COUNT*3-1:0] s_axi_awsize, + input wire [S_COUNT*2-1:0] s_axi_awburst, + input wire [S_COUNT-1:0] s_axi_awlock, + input wire [S_COUNT*4-1:0] s_axi_awcache, + input wire [S_COUNT*3-1:0] s_axi_awprot, + input wire [S_COUNT*4-1:0] s_axi_awqos, + input wire [S_COUNT*AWUSER_WIDTH-1:0] s_axi_awuser, + input wire [S_COUNT-1:0] s_axi_awvalid, + output wire [S_COUNT-1:0] s_axi_awready, + input wire [S_COUNT*DATA_WIDTH-1:0] s_axi_wdata, + input wire [S_COUNT*STRB_WIDTH-1:0] s_axi_wstrb, + input wire [S_COUNT-1:0] s_axi_wlast, + input wire [S_COUNT*WUSER_WIDTH-1:0] s_axi_wuser, + input wire [S_COUNT-1:0] s_axi_wvalid, + output wire [S_COUNT-1:0] s_axi_wready, + output wire [S_COUNT*ID_WIDTH-1:0] s_axi_bid, + output wire [S_COUNT*2-1:0] s_axi_bresp, + output wire [S_COUNT*BUSER_WIDTH-1:0] s_axi_buser, + output wire [S_COUNT-1:0] s_axi_bvalid, + input wire [S_COUNT-1:0] s_axi_bready, + input wire [S_COUNT*ID_WIDTH-1:0] s_axi_arid, + input wire [S_COUNT*ADDR_WIDTH-1:0] s_axi_araddr, + input wire [S_COUNT*8-1:0] s_axi_arlen, + input wire [S_COUNT*3-1:0] s_axi_arsize, + input wire [S_COUNT*2-1:0] s_axi_arburst, + input wire [S_COUNT-1:0] s_axi_arlock, + input wire [S_COUNT*4-1:0] s_axi_arcache, + input wire [S_COUNT*3-1:0] s_axi_arprot, + input wire [S_COUNT*4-1:0] s_axi_arqos, + input wire [S_COUNT*ARUSER_WIDTH-1:0] s_axi_aruser, + input wire [S_COUNT-1:0] s_axi_arvalid, + output wire [S_COUNT-1:0] s_axi_arready, + output wire [S_COUNT*ID_WIDTH-1:0] s_axi_rid, + output wire [S_COUNT*DATA_WIDTH-1:0] s_axi_rdata, + output wire [S_COUNT*2-1:0] s_axi_rresp, + output wire [S_COUNT-1:0] s_axi_rlast, + output wire [S_COUNT*RUSER_WIDTH-1:0] s_axi_ruser, + output wire [S_COUNT-1:0] s_axi_rvalid, + input wire [S_COUNT-1:0] s_axi_rready, + + /* + * AXI master interfaces + */ + output wire [M_COUNT*ID_WIDTH-1:0] m_axi_awid, + output wire [M_COUNT*ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [M_COUNT*8-1:0] m_axi_awlen, + output wire [M_COUNT*3-1:0] m_axi_awsize, + output wire [M_COUNT*2-1:0] m_axi_awburst, + output wire [M_COUNT-1:0] m_axi_awlock, + output wire [M_COUNT*4-1:0] m_axi_awcache, + output wire [M_COUNT*3-1:0] m_axi_awprot, + output wire [M_COUNT*4-1:0] m_axi_awqos, + output wire [M_COUNT*4-1:0] m_axi_awregion, + output wire [M_COUNT*AWUSER_WIDTH-1:0] m_axi_awuser, + output wire [M_COUNT-1:0] m_axi_awvalid, + input wire [M_COUNT-1:0] m_axi_awready, + output wire [M_COUNT*DATA_WIDTH-1:0] m_axi_wdata, + output wire [M_COUNT*STRB_WIDTH-1:0] m_axi_wstrb, + output wire [M_COUNT-1:0] m_axi_wlast, + output wire [M_COUNT*WUSER_WIDTH-1:0] m_axi_wuser, + output wire [M_COUNT-1:0] m_axi_wvalid, + input wire [M_COUNT-1:0] m_axi_wready, + input wire [M_COUNT*ID_WIDTH-1:0] m_axi_bid, + input wire [M_COUNT*2-1:0] m_axi_bresp, + input wire [M_COUNT*BUSER_WIDTH-1:0] m_axi_buser, + input wire [M_COUNT-1:0] m_axi_bvalid, + output wire [M_COUNT-1:0] m_axi_bready, + output wire [M_COUNT*ID_WIDTH-1:0] m_axi_arid, + output wire [M_COUNT*ADDR_WIDTH-1:0] m_axi_araddr, + output wire [M_COUNT*8-1:0] m_axi_arlen, + output wire [M_COUNT*3-1:0] m_axi_arsize, + output wire [M_COUNT*2-1:0] m_axi_arburst, + output wire [M_COUNT-1:0] m_axi_arlock, + output wire [M_COUNT*4-1:0] m_axi_arcache, + output wire [M_COUNT*3-1:0] m_axi_arprot, + output wire [M_COUNT*4-1:0] m_axi_arqos, + output wire [M_COUNT*4-1:0] m_axi_arregion, + output wire [M_COUNT*ARUSER_WIDTH-1:0] m_axi_aruser, + output wire [M_COUNT-1:0] m_axi_arvalid, + input wire [M_COUNT-1:0] m_axi_arready, + input wire [M_COUNT*ID_WIDTH-1:0] m_axi_rid, + input wire [M_COUNT*DATA_WIDTH-1:0] m_axi_rdata, + input wire [M_COUNT*2-1:0] m_axi_rresp, + input wire [M_COUNT-1:0] m_axi_rlast, + input wire [M_COUNT*RUSER_WIDTH-1:0] m_axi_ruser, + input wire [M_COUNT-1:0] m_axi_rvalid, + output wire [M_COUNT-1:0] m_axi_rready +); + +parameter CL_S_COUNT = $clog2(S_COUNT); +parameter CL_M_COUNT = $clog2(M_COUNT); + +parameter AUSER_WIDTH = AWUSER_WIDTH > ARUSER_WIDTH ? AWUSER_WIDTH : ARUSER_WIDTH; + +// default address computation +function [M_COUNT*M_REGIONS*ADDR_WIDTH-1:0] calcBaseAddrs(input [31:0] dummy); + integer i; + reg [ADDR_WIDTH-1:0] base; + begin + calcBaseAddrs = {M_COUNT*M_REGIONS*ADDR_WIDTH{1'b0}}; + base = 0; + for (i = 1; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32]) begin + base = base + 2**M_ADDR_WIDTH[(i-1)*32 +: 32]; // increment + base = base - (base % 2**M_ADDR_WIDTH[i*32 +: 32]); // align + calcBaseAddrs[i * ADDR_WIDTH +: ADDR_WIDTH] = base; + end + end + end +endfunction + +parameter M_BASE_ADDR_INT = M_BASE_ADDR ? M_BASE_ADDR : calcBaseAddrs(0); + +integer i, j; + +// check configuration +initial begin + if (M_REGIONS < 1 || M_REGIONS > 16) begin + $error("Error: M_REGIONS must be between 1 and 16 (instance %m)"); + $finish; + end + + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32] && (M_ADDR_WIDTH[i*32 +: 32] < 12 || M_ADDR_WIDTH[i*32 +: 32] > ADDR_WIDTH)) begin + $error("Error: address width out of range (instance %m)"); + $finish; + end + end + + $display("Addressing configuration for axi_interconnect instance %m"); + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32]) begin + $display("%2d (%2d): %x / %2d -- %x-%x", i/M_REGIONS, i%M_REGIONS, M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[i*32 +: 32], M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))); + end + end + + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + for (j = i+1; j < M_COUNT*M_REGIONS; j = j + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32] && M_ADDR_WIDTH[j*32 +: 32]) begin + if (((M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32])) <= (M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32])))) && ((M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32])) <= (M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))))) begin + $display("Overlapping regions:"); + $display("%2d (%2d): %x / %2d -- %x-%x", i/M_REGIONS, i%M_REGIONS, M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[i*32 +: 32], M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))); + $display("%2d (%2d): %x / %2d -- %x-%x", j/M_REGIONS, j%M_REGIONS, M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[j*32 +: 32], M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32]), M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32]))); + $error("Error: address ranges overlap (instance %m)"); + $finish; + end + end + end + end +end + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_DECODE = 3'd1, + STATE_WRITE = 3'd2, + STATE_WRITE_RESP = 3'd3, + STATE_WRITE_DROP = 3'd4, + STATE_READ = 3'd5, + STATE_READ_DROP = 3'd6, + STATE_WAIT_IDLE = 3'd7; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +reg match; + +reg [CL_M_COUNT-1:0] m_select_reg = 2'd0, m_select_next; +reg [ID_WIDTH-1:0] axi_id_reg = {ID_WIDTH{1'b0}}, axi_id_next; +reg [ADDR_WIDTH-1:0] axi_addr_reg = {ADDR_WIDTH{1'b0}}, axi_addr_next; +reg axi_addr_valid_reg = 1'b0, axi_addr_valid_next; +reg [7:0] axi_len_reg = 8'd0, axi_len_next; +reg [2:0] axi_size_reg = 3'd0, axi_size_next; +reg [1:0] axi_burst_reg = 2'd0, axi_burst_next; +reg axi_lock_reg = 1'b0, axi_lock_next; +reg [3:0] axi_cache_reg = 4'd0, axi_cache_next; +reg [2:0] axi_prot_reg = 3'b000, axi_prot_next; +reg [3:0] axi_qos_reg = 4'd0, axi_qos_next; +reg [3:0] axi_region_reg = 4'd0, axi_region_next; +reg [AUSER_WIDTH-1:0] axi_auser_reg = {AUSER_WIDTH{1'b0}}, axi_auser_next; +reg [1:0] axi_bresp_reg = 2'b00, axi_bresp_next; +reg [BUSER_WIDTH-1:0] axi_buser_reg = {BUSER_WIDTH{1'b0}}, axi_buser_next; + +reg [S_COUNT-1:0] s_axi_awready_reg = 0, s_axi_awready_next; +reg [S_COUNT-1:0] s_axi_wready_reg = 0, s_axi_wready_next; +reg [S_COUNT-1:0] s_axi_bvalid_reg = 0, s_axi_bvalid_next; +reg [S_COUNT-1:0] s_axi_arready_reg = 0, s_axi_arready_next; + +reg [M_COUNT-1:0] m_axi_awvalid_reg = 0, m_axi_awvalid_next; +reg [M_COUNT-1:0] m_axi_bready_reg = 0, m_axi_bready_next; +reg [M_COUNT-1:0] m_axi_arvalid_reg = 0, m_axi_arvalid_next; +reg [M_COUNT-1:0] m_axi_rready_reg = 0, m_axi_rready_next; + +// internal datapath +reg [ID_WIDTH-1:0] s_axi_rid_int; +reg [DATA_WIDTH-1:0] s_axi_rdata_int; +reg [1:0] s_axi_rresp_int; +reg s_axi_rlast_int; +reg [RUSER_WIDTH-1:0] s_axi_ruser_int; +reg s_axi_rvalid_int; +reg s_axi_rready_int_reg = 1'b0; +wire s_axi_rready_int_early; + +reg [DATA_WIDTH-1:0] m_axi_wdata_int; +reg [STRB_WIDTH-1:0] m_axi_wstrb_int; +reg m_axi_wlast_int; +reg [WUSER_WIDTH-1:0] m_axi_wuser_int; +reg m_axi_wvalid_int; +reg m_axi_wready_int_reg = 1'b0; +wire m_axi_wready_int_early; + +assign s_axi_awready = s_axi_awready_reg; +assign s_axi_wready = s_axi_wready_reg; +assign s_axi_bid = {S_COUNT{axi_id_reg}}; +assign s_axi_bresp = {S_COUNT{axi_bresp_reg}}; +assign s_axi_buser = {S_COUNT{BUSER_ENABLE ? axi_buser_reg : {BUSER_WIDTH{1'b0}}}}; +assign s_axi_bvalid = s_axi_bvalid_reg; +assign s_axi_arready = s_axi_arready_reg; + +assign m_axi_awid = {M_COUNT{FORWARD_ID ? axi_id_reg : {ID_WIDTH{1'b0}}}}; +assign m_axi_awaddr = {M_COUNT{axi_addr_reg}}; +assign m_axi_awlen = {M_COUNT{axi_len_reg}}; +assign m_axi_awsize = {M_COUNT{axi_size_reg}}; +assign m_axi_awburst = {M_COUNT{axi_burst_reg}}; +assign m_axi_awlock = {M_COUNT{axi_lock_reg}}; +assign m_axi_awcache = {M_COUNT{axi_cache_reg}}; +assign m_axi_awprot = {M_COUNT{axi_prot_reg}}; +assign m_axi_awqos = {M_COUNT{axi_qos_reg}}; +assign m_axi_awregion = {M_COUNT{axi_region_reg}}; +assign m_axi_awuser = {M_COUNT{AWUSER_ENABLE ? axi_auser_reg[AWUSER_WIDTH-1:0] : {AWUSER_WIDTH{1'b0}}}}; +assign m_axi_awvalid = m_axi_awvalid_reg; +assign m_axi_bready = m_axi_bready_reg; +assign m_axi_arid = {M_COUNT{FORWARD_ID ? axi_id_reg : {ID_WIDTH{1'b0}}}}; +assign m_axi_araddr = {M_COUNT{axi_addr_reg}}; +assign m_axi_arlen = {M_COUNT{axi_len_reg}}; +assign m_axi_arsize = {M_COUNT{axi_size_reg}}; +assign m_axi_arburst = {M_COUNT{axi_burst_reg}}; +assign m_axi_arlock = {M_COUNT{axi_lock_reg}}; +assign m_axi_arcache = {M_COUNT{axi_cache_reg}}; +assign m_axi_arprot = {M_COUNT{axi_prot_reg}}; +assign m_axi_arqos = {M_COUNT{axi_qos_reg}}; +assign m_axi_arregion = {M_COUNT{axi_region_reg}}; +assign m_axi_aruser = {M_COUNT{ARUSER_ENABLE ? axi_auser_reg[ARUSER_WIDTH-1:0] : {ARUSER_WIDTH{1'b0}}}}; +assign m_axi_arvalid = m_axi_arvalid_reg; +assign m_axi_rready = m_axi_rready_reg; + +// slave side mux +wire [(CL_S_COUNT > 0 ? CL_S_COUNT-1 : 0):0] s_select; + +wire [ID_WIDTH-1:0] current_s_axi_awid = s_axi_awid[s_select*ID_WIDTH +: ID_WIDTH]; +wire [ADDR_WIDTH-1:0] current_s_axi_awaddr = s_axi_awaddr[s_select*ADDR_WIDTH +: ADDR_WIDTH]; +wire [7:0] current_s_axi_awlen = s_axi_awlen[s_select*8 +: 8]; +wire [2:0] current_s_axi_awsize = s_axi_awsize[s_select*3 +: 3]; +wire [1:0] current_s_axi_awburst = s_axi_awburst[s_select*2 +: 2]; +wire current_s_axi_awlock = s_axi_awlock[s_select]; +wire [3:0] current_s_axi_awcache = s_axi_awcache[s_select*4 +: 4]; +wire [2:0] current_s_axi_awprot = s_axi_awprot[s_select*3 +: 3]; +wire [3:0] current_s_axi_awqos = s_axi_awqos[s_select*4 +: 4]; +wire [AWUSER_WIDTH-1:0] current_s_axi_awuser = s_axi_awuser[s_select*AWUSER_WIDTH +: AWUSER_WIDTH]; +wire current_s_axi_awvalid = s_axi_awvalid[s_select]; +wire current_s_axi_awready = s_axi_awready[s_select]; +wire [DATA_WIDTH-1:0] current_s_axi_wdata = s_axi_wdata[s_select*DATA_WIDTH +: DATA_WIDTH]; +wire [STRB_WIDTH-1:0] current_s_axi_wstrb = s_axi_wstrb[s_select*STRB_WIDTH +: STRB_WIDTH]; +wire current_s_axi_wlast = s_axi_wlast[s_select]; +wire [WUSER_WIDTH-1:0] current_s_axi_wuser = s_axi_wuser[s_select*WUSER_WIDTH +: WUSER_WIDTH]; +wire current_s_axi_wvalid = s_axi_wvalid[s_select]; +wire current_s_axi_wready = s_axi_wready[s_select]; +wire [ID_WIDTH-1:0] current_s_axi_bid = s_axi_bid[s_select*ID_WIDTH +: ID_WIDTH]; +wire [1:0] current_s_axi_bresp = s_axi_bresp[s_select*2 +: 2]; +wire [BUSER_WIDTH-1:0] current_s_axi_buser = s_axi_buser[s_select*BUSER_WIDTH +: BUSER_WIDTH]; +wire current_s_axi_bvalid = s_axi_bvalid[s_select]; +wire current_s_axi_bready = s_axi_bready[s_select]; +wire [ID_WIDTH-1:0] current_s_axi_arid = s_axi_arid[s_select*ID_WIDTH +: ID_WIDTH]; +wire [ADDR_WIDTH-1:0] current_s_axi_araddr = s_axi_araddr[s_select*ADDR_WIDTH +: ADDR_WIDTH]; +wire [7:0] current_s_axi_arlen = s_axi_arlen[s_select*8 +: 8]; +wire [2:0] current_s_axi_arsize = s_axi_arsize[s_select*3 +: 3]; +wire [1:0] current_s_axi_arburst = s_axi_arburst[s_select*2 +: 2]; +wire current_s_axi_arlock = s_axi_arlock[s_select]; +wire [3:0] current_s_axi_arcache = s_axi_arcache[s_select*4 +: 4]; +wire [2:0] current_s_axi_arprot = s_axi_arprot[s_select*3 +: 3]; +wire [3:0] current_s_axi_arqos = s_axi_arqos[s_select*4 +: 4]; +wire [ARUSER_WIDTH-1:0] current_s_axi_aruser = s_axi_aruser[s_select*ARUSER_WIDTH +: ARUSER_WIDTH]; +wire current_s_axi_arvalid = s_axi_arvalid[s_select]; +wire current_s_axi_arready = s_axi_arready[s_select]; +wire [ID_WIDTH-1:0] current_s_axi_rid = s_axi_rid[s_select*ID_WIDTH +: ID_WIDTH]; +wire [DATA_WIDTH-1:0] current_s_axi_rdata = s_axi_rdata[s_select*DATA_WIDTH +: DATA_WIDTH]; +wire [1:0] current_s_axi_rresp = s_axi_rresp[s_select*2 +: 2]; +wire current_s_axi_rlast = s_axi_rlast[s_select]; +wire [RUSER_WIDTH-1:0] current_s_axi_ruser = s_axi_ruser[s_select*RUSER_WIDTH +: RUSER_WIDTH]; +wire current_s_axi_rvalid = s_axi_rvalid[s_select]; +wire current_s_axi_rready = s_axi_rready[s_select]; + +// master side mux +wire [ID_WIDTH-1:0] current_m_axi_awid = m_axi_awid[m_select_reg*ID_WIDTH +: ID_WIDTH]; +wire [ADDR_WIDTH-1:0] current_m_axi_awaddr = m_axi_awaddr[m_select_reg*ADDR_WIDTH +: ADDR_WIDTH]; +wire [7:0] current_m_axi_awlen = m_axi_awlen[m_select_reg*8 +: 8]; +wire [2:0] current_m_axi_awsize = m_axi_awsize[m_select_reg*3 +: 3]; +wire [1:0] current_m_axi_awburst = m_axi_awburst[m_select_reg*2 +: 2]; +wire current_m_axi_awlock = m_axi_awlock[m_select_reg]; +wire [3:0] current_m_axi_awcache = m_axi_awcache[m_select_reg*4 +: 4]; +wire [2:0] current_m_axi_awprot = m_axi_awprot[m_select_reg*3 +: 3]; +wire [3:0] current_m_axi_awqos = m_axi_awqos[m_select_reg*4 +: 4]; +wire [3:0] current_m_axi_awregion = m_axi_awregion[m_select_reg*4 +: 4]; +wire [AWUSER_WIDTH-1:0] current_m_axi_awuser = m_axi_awuser[m_select_reg*AWUSER_WIDTH +: AWUSER_WIDTH]; +wire current_m_axi_awvalid = m_axi_awvalid[m_select_reg]; +wire current_m_axi_awready = m_axi_awready[m_select_reg]; +wire [DATA_WIDTH-1:0] current_m_axi_wdata = m_axi_wdata[m_select_reg*DATA_WIDTH +: DATA_WIDTH]; +wire [STRB_WIDTH-1:0] current_m_axi_wstrb = m_axi_wstrb[m_select_reg*STRB_WIDTH +: STRB_WIDTH]; +wire current_m_axi_wlast = m_axi_wlast[m_select_reg]; +wire [WUSER_WIDTH-1:0] current_m_axi_wuser = m_axi_wuser[m_select_reg*WUSER_WIDTH +: WUSER_WIDTH]; +wire current_m_axi_wvalid = m_axi_wvalid[m_select_reg]; +wire current_m_axi_wready = m_axi_wready[m_select_reg]; +wire [ID_WIDTH-1:0] current_m_axi_bid = m_axi_bid[m_select_reg*ID_WIDTH +: ID_WIDTH]; +wire [1:0] current_m_axi_bresp = m_axi_bresp[m_select_reg*2 +: 2]; +wire [BUSER_WIDTH-1:0] current_m_axi_buser = m_axi_buser[m_select_reg*BUSER_WIDTH +: BUSER_WIDTH]; +wire current_m_axi_bvalid = m_axi_bvalid[m_select_reg]; +wire current_m_axi_bready = m_axi_bready[m_select_reg]; +wire [ID_WIDTH-1:0] current_m_axi_arid = m_axi_arid[m_select_reg*ID_WIDTH +: ID_WIDTH]; +wire [ADDR_WIDTH-1:0] current_m_axi_araddr = m_axi_araddr[m_select_reg*ADDR_WIDTH +: ADDR_WIDTH]; +wire [7:0] current_m_axi_arlen = m_axi_arlen[m_select_reg*8 +: 8]; +wire [2:0] current_m_axi_arsize = m_axi_arsize[m_select_reg*3 +: 3]; +wire [1:0] current_m_axi_arburst = m_axi_arburst[m_select_reg*2 +: 2]; +wire current_m_axi_arlock = m_axi_arlock[m_select_reg]; +wire [3:0] current_m_axi_arcache = m_axi_arcache[m_select_reg*4 +: 4]; +wire [2:0] current_m_axi_arprot = m_axi_arprot[m_select_reg*3 +: 3]; +wire [3:0] current_m_axi_arqos = m_axi_arqos[m_select_reg*4 +: 4]; +wire [3:0] current_m_axi_arregion = m_axi_arregion[m_select_reg*4 +: 4]; +wire [ARUSER_WIDTH-1:0] current_m_axi_aruser = m_axi_aruser[m_select_reg*ARUSER_WIDTH +: ARUSER_WIDTH]; +wire current_m_axi_arvalid = m_axi_arvalid[m_select_reg]; +wire current_m_axi_arready = m_axi_arready[m_select_reg]; +wire [ID_WIDTH-1:0] current_m_axi_rid = m_axi_rid[m_select_reg*ID_WIDTH +: ID_WIDTH]; +wire [DATA_WIDTH-1:0] current_m_axi_rdata = m_axi_rdata[m_select_reg*DATA_WIDTH +: DATA_WIDTH]; +wire [1:0] current_m_axi_rresp = m_axi_rresp[m_select_reg*2 +: 2]; +wire current_m_axi_rlast = m_axi_rlast[m_select_reg]; +wire [RUSER_WIDTH-1:0] current_m_axi_ruser = m_axi_ruser[m_select_reg*RUSER_WIDTH +: RUSER_WIDTH]; +wire current_m_axi_rvalid = m_axi_rvalid[m_select_reg]; +wire current_m_axi_rready = m_axi_rready[m_select_reg]; + +// arbiter instance +wire [S_COUNT*2-1:0] request; +wire [S_COUNT*2-1:0] acknowledge; +wire [S_COUNT*2-1:0] grant; +wire grant_valid; +wire [CL_S_COUNT:0] grant_encoded; + +wire read = grant_encoded[0]; +assign s_select = grant_encoded >> 1; + +arbiter #( + .PORTS(S_COUNT*2), + .TYPE("ROUND_ROBIN"), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY("HIGH") +) +arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +genvar n; + +// request generation +generate +for (n = 0; n < S_COUNT; n = n + 1) begin + assign request[2*n] = s_axi_awvalid[n]; + assign request[2*n+1] = s_axi_arvalid[n]; +end +endgenerate + +// acknowledge generation +generate +for (n = 0; n < S_COUNT; n = n + 1) begin + assign acknowledge[2*n] = grant[2*n] && s_axi_bvalid[n] && s_axi_bready[n]; + assign acknowledge[2*n+1] = grant[2*n+1] && s_axi_rvalid[n] && s_axi_rready[n] && s_axi_rlast[n]; +end +endgenerate + +always @* begin + state_next = STATE_IDLE; + + match = 1'b0; + + m_select_next = m_select_reg; + axi_id_next = axi_id_reg; + axi_addr_next = axi_addr_reg; + axi_addr_valid_next = axi_addr_valid_reg; + axi_len_next = axi_len_reg; + axi_size_next = axi_size_reg; + axi_burst_next = axi_burst_reg; + axi_lock_next = axi_lock_reg; + axi_cache_next = axi_cache_reg; + axi_prot_next = axi_prot_reg; + axi_qos_next = axi_qos_reg; + axi_region_next = axi_region_reg; + axi_auser_next = axi_auser_reg; + axi_bresp_next = axi_bresp_reg; + axi_buser_next = axi_buser_reg; + + s_axi_awready_next = 0; + s_axi_wready_next = 0; + s_axi_bvalid_next = s_axi_bvalid_reg & ~s_axi_bready; + s_axi_arready_next = 0; + + m_axi_awvalid_next = m_axi_awvalid_reg & ~m_axi_awready; + m_axi_bready_next = 0; + m_axi_arvalid_next = m_axi_arvalid_reg & ~m_axi_arready; + m_axi_rready_next = 0; + + s_axi_rid_int = axi_id_reg; + s_axi_rdata_int = current_m_axi_rdata; + s_axi_rresp_int = current_m_axi_rresp; + s_axi_rlast_int = current_m_axi_rlast; + s_axi_ruser_int = current_m_axi_ruser; + s_axi_rvalid_int = 1'b0; + + m_axi_wdata_int = current_s_axi_wdata; + m_axi_wstrb_int = current_s_axi_wstrb; + m_axi_wlast_int = current_s_axi_wlast; + m_axi_wuser_int = current_s_axi_wuser; + m_axi_wvalid_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state; wait for arbitration + + if (grant_valid) begin + + axi_addr_valid_next = 1'b1; + + if (read) begin + // reading + axi_addr_next = current_s_axi_araddr; + axi_prot_next = current_s_axi_arprot; + axi_id_next = current_s_axi_arid; + axi_addr_next = current_s_axi_araddr; + axi_len_next = current_s_axi_arlen; + axi_size_next = current_s_axi_arsize; + axi_burst_next = current_s_axi_arburst; + axi_lock_next = current_s_axi_arlock; + axi_cache_next = current_s_axi_arcache; + axi_prot_next = current_s_axi_arprot; + axi_qos_next = current_s_axi_arqos; + axi_auser_next = current_s_axi_aruser; + s_axi_arready_next[s_select] = 1'b1; + end else begin + // writing + axi_addr_next = current_s_axi_awaddr; + axi_prot_next = current_s_axi_awprot; + axi_id_next = current_s_axi_awid; + axi_addr_next = current_s_axi_awaddr; + axi_len_next = current_s_axi_awlen; + axi_size_next = current_s_axi_awsize; + axi_burst_next = current_s_axi_awburst; + axi_lock_next = current_s_axi_awlock; + axi_cache_next = current_s_axi_awcache; + axi_prot_next = current_s_axi_awprot; + axi_qos_next = current_s_axi_awqos; + axi_auser_next = current_s_axi_awuser; + s_axi_awready_next[s_select] = 1'b1; + end + + state_next = STATE_DECODE; + end else begin + state_next = STATE_IDLE; + end + end + STATE_DECODE: begin + // decode state; determine master interface + + match = 1'b0; + for (i = 0; i < M_COUNT; i = i + 1) begin + for (j = 0; j < M_REGIONS; j = j + 1) begin + if (M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32] && (!M_SECURE[i] || !axi_prot_reg[1]) && ((read ? M_CONNECT_READ : M_CONNECT_WRITE) & (1 << (s_select+i*S_COUNT))) && (axi_addr_reg >> M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32]) == (M_BASE_ADDR_INT[(i*M_REGIONS+j)*ADDR_WIDTH +: ADDR_WIDTH] >> M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32])) begin + m_select_next = i; + axi_region_next = j; + match = 1'b1; + end + end + end + + if (match) begin + if (read) begin + // reading + m_axi_rready_next[m_select_reg] = s_axi_rready_int_early; + state_next = STATE_READ; + end else begin + // writing + s_axi_wready_next[s_select] = m_axi_wready_int_early; + state_next = STATE_WRITE; + end + end else begin + // no match; return decode error + if (read) begin + // reading + state_next = STATE_READ_DROP; + end else begin + // writing + axi_bresp_next = 2'b11; + s_axi_wready_next[s_select] = 1'b1; + state_next = STATE_WRITE_DROP; + end + end + end + STATE_WRITE: begin + // write state; store and forward write data + s_axi_wready_next[s_select] = m_axi_wready_int_early; + + if (axi_addr_valid_reg) begin + m_axi_awvalid_next[m_select_reg] = 1'b1; + end + axi_addr_valid_next = 1'b0; + + if (current_s_axi_wready && current_s_axi_wvalid) begin + m_axi_wdata_int = current_s_axi_wdata; + m_axi_wstrb_int = current_s_axi_wstrb; + m_axi_wlast_int = current_s_axi_wlast; + m_axi_wuser_int = current_s_axi_wuser; + m_axi_wvalid_int = 1'b1; + + if (current_s_axi_wlast) begin + s_axi_wready_next[s_select] = 1'b0; + m_axi_bready_next[m_select_reg] = 1'b1; + state_next = STATE_WRITE_RESP; + end else begin + state_next = STATE_WRITE; + end + end else begin + state_next = STATE_WRITE; + end + end + STATE_WRITE_RESP: begin + // write response state; store and forward write response + m_axi_bready_next[m_select_reg] = 1'b1; + + if (current_m_axi_bready && current_m_axi_bvalid) begin + m_axi_bready_next[m_select_reg] = 1'b0; + axi_bresp_next = current_m_axi_bresp; + s_axi_bvalid_next[s_select] = 1'b1; + state_next = STATE_WAIT_IDLE; + end else begin + state_next = STATE_WRITE_RESP; + end + end + STATE_WRITE_DROP: begin + // write drop state; drop write data + s_axi_wready_next[s_select] = 1'b1; + + axi_addr_valid_next = 1'b0; + + if (current_s_axi_wready && current_s_axi_wvalid && current_s_axi_wlast) begin + s_axi_wready_next[s_select] = 1'b0; + s_axi_bvalid_next[s_select] = 1'b1; + state_next = STATE_WAIT_IDLE; + end else begin + state_next = STATE_WRITE_DROP; + end + end + STATE_READ: begin + // read state; store and forward read response + m_axi_rready_next[m_select_reg] = s_axi_rready_int_early; + + if (axi_addr_valid_reg) begin + m_axi_arvalid_next[m_select_reg] = 1'b1; + end + axi_addr_valid_next = 1'b0; + + if (current_m_axi_rready && current_m_axi_rvalid) begin + s_axi_rid_int = axi_id_reg; + s_axi_rdata_int = current_m_axi_rdata; + s_axi_rresp_int = current_m_axi_rresp; + s_axi_rlast_int = current_m_axi_rlast; + s_axi_ruser_int = current_m_axi_ruser; + s_axi_rvalid_int = 1'b1; + + if (current_m_axi_rlast) begin + m_axi_rready_next[m_select_reg] = 1'b0; + state_next = STATE_WAIT_IDLE; + end else begin + state_next = STATE_READ; + end + end else begin + state_next = STATE_READ; + end + end + STATE_READ_DROP: begin + // read drop state; generate decode error read response + + s_axi_rid_int = axi_id_reg; + s_axi_rdata_int = {DATA_WIDTH{1'b0}}; + s_axi_rresp_int = 2'b11; + s_axi_rlast_int = axi_len_reg == 0; + s_axi_ruser_int = {RUSER_WIDTH{1'b0}}; + s_axi_rvalid_int = 1'b1; + + if (s_axi_rready_int_reg) begin + axi_len_next = axi_len_reg - 1; + if (axi_len_reg == 0) begin + state_next = STATE_WAIT_IDLE; + end else begin + state_next = STATE_READ_DROP; + end + end else begin + state_next = STATE_READ_DROP; + end + end + STATE_WAIT_IDLE: begin + // wait for idle state; wait untl grant valid is deasserted + + if (!grant_valid || acknowledge) begin + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_IDLE; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + s_axi_awready_reg <= 0; + s_axi_wready_reg <= 0; + s_axi_bvalid_reg <= 0; + s_axi_arready_reg <= 0; + + m_axi_awvalid_reg <= 0; + m_axi_bready_reg <= 0; + m_axi_arvalid_reg <= 0; + m_axi_rready_reg <= 0; + end else begin + state_reg <= state_next; + + s_axi_awready_reg <= s_axi_awready_next; + s_axi_wready_reg <= s_axi_wready_next; + s_axi_bvalid_reg <= s_axi_bvalid_next; + s_axi_arready_reg <= s_axi_arready_next; + + m_axi_awvalid_reg <= m_axi_awvalid_next; + m_axi_bready_reg <= m_axi_bready_next; + m_axi_arvalid_reg <= m_axi_arvalid_next; + m_axi_rready_reg <= m_axi_rready_next; + end + + m_select_reg <= m_select_next; + axi_id_reg <= axi_id_next; + axi_addr_reg <= axi_addr_next; + axi_addr_valid_reg <= axi_addr_valid_next; + axi_len_reg <= axi_len_next; + axi_size_reg <= axi_size_next; + axi_burst_reg <= axi_burst_next; + axi_lock_reg <= axi_lock_next; + axi_cache_reg <= axi_cache_next; + axi_prot_reg <= axi_prot_next; + axi_qos_reg <= axi_qos_next; + axi_region_reg <= axi_region_next; + axi_auser_reg <= axi_auser_next; + axi_bresp_reg <= axi_bresp_next; + axi_buser_reg <= axi_buser_next; +end + +// output datapath logic (R channel) +reg [ID_WIDTH-1:0] s_axi_rid_reg = {ID_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] s_axi_rdata_reg = {DATA_WIDTH{1'b0}}; +reg [1:0] s_axi_rresp_reg = 2'd0; +reg s_axi_rlast_reg = 1'b0; +reg [RUSER_WIDTH-1:0] s_axi_ruser_reg = 1'b0; +reg [S_COUNT-1:0] s_axi_rvalid_reg = 1'b0, s_axi_rvalid_next; + +reg [ID_WIDTH-1:0] temp_s_axi_rid_reg = {ID_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] temp_s_axi_rdata_reg = {DATA_WIDTH{1'b0}}; +reg [1:0] temp_s_axi_rresp_reg = 2'd0; +reg temp_s_axi_rlast_reg = 1'b0; +reg [RUSER_WIDTH-1:0] temp_s_axi_ruser_reg = 1'b0; +reg temp_s_axi_rvalid_reg = 1'b0, temp_s_axi_rvalid_next; + +// datapath control +reg store_axi_r_int_to_output; +reg store_axi_r_int_to_temp; +reg store_axi_r_temp_to_output; + +assign s_axi_rid = {S_COUNT{s_axi_rid_reg}}; +assign s_axi_rdata = {S_COUNT{s_axi_rdata_reg}}; +assign s_axi_rresp = {S_COUNT{s_axi_rresp_reg}}; +assign s_axi_rlast = {S_COUNT{s_axi_rlast_reg}}; +assign s_axi_ruser = {S_COUNT{RUSER_ENABLE ? s_axi_ruser_reg : {RUSER_WIDTH{1'b0}}}}; +assign s_axi_rvalid = s_axi_rvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign s_axi_rready_int_early = current_s_axi_rready | (~temp_s_axi_rvalid_reg & (~current_s_axi_rvalid | ~s_axi_rvalid_int)); + +always @* begin + // transfer sink ready state to source + s_axi_rvalid_next = s_axi_rvalid_reg; + temp_s_axi_rvalid_next = temp_s_axi_rvalid_reg; + + store_axi_r_int_to_output = 1'b0; + store_axi_r_int_to_temp = 1'b0; + store_axi_r_temp_to_output = 1'b0; + + if (s_axi_rready_int_reg) begin + // input is ready + if (current_s_axi_rready | ~current_s_axi_rvalid) begin + // output is ready or currently not valid, transfer data to output + s_axi_rvalid_next[s_select] = s_axi_rvalid_int; + store_axi_r_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_s_axi_rvalid_next = s_axi_rvalid_int; + store_axi_r_int_to_temp = 1'b1; + end + end else if (current_s_axi_rready) begin + // input is not ready, but output is ready + s_axi_rvalid_next[s_select] = temp_s_axi_rvalid_reg; + temp_s_axi_rvalid_next = 1'b0; + store_axi_r_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_rvalid_reg <= 1'b0; + s_axi_rready_int_reg <= 1'b0; + temp_s_axi_rvalid_reg <= 1'b0; + end else begin + s_axi_rvalid_reg <= s_axi_rvalid_next; + s_axi_rready_int_reg <= s_axi_rready_int_early; + temp_s_axi_rvalid_reg <= temp_s_axi_rvalid_next; + end + + // datapath + if (store_axi_r_int_to_output) begin + s_axi_rid_reg <= s_axi_rid_int; + s_axi_rdata_reg <= s_axi_rdata_int; + s_axi_rresp_reg <= s_axi_rresp_int; + s_axi_rlast_reg <= s_axi_rlast_int; + s_axi_ruser_reg <= s_axi_ruser_int; + end else if (store_axi_r_temp_to_output) begin + s_axi_rid_reg <= temp_s_axi_rid_reg; + s_axi_rdata_reg <= temp_s_axi_rdata_reg; + s_axi_rresp_reg <= temp_s_axi_rresp_reg; + s_axi_rlast_reg <= temp_s_axi_rlast_reg; + s_axi_ruser_reg <= temp_s_axi_ruser_reg; + end + + if (store_axi_r_int_to_temp) begin + temp_s_axi_rid_reg <= s_axi_rid_int; + temp_s_axi_rdata_reg <= s_axi_rdata_int; + temp_s_axi_rresp_reg <= s_axi_rresp_int; + temp_s_axi_rlast_reg <= s_axi_rlast_int; + temp_s_axi_ruser_reg <= s_axi_ruser_int; + end +end + +// output datapath logic (W channel) +reg [DATA_WIDTH-1:0] m_axi_wdata_reg = {DATA_WIDTH{1'b0}}; +reg [STRB_WIDTH-1:0] m_axi_wstrb_reg = {STRB_WIDTH{1'b0}}; +reg m_axi_wlast_reg = 1'b0; +reg [WUSER_WIDTH-1:0] m_axi_wuser_reg = 1'b0; +reg [M_COUNT-1:0] m_axi_wvalid_reg = 1'b0, m_axi_wvalid_next; + +reg [DATA_WIDTH-1:0] temp_m_axi_wdata_reg = {DATA_WIDTH{1'b0}}; +reg [STRB_WIDTH-1:0] temp_m_axi_wstrb_reg = {STRB_WIDTH{1'b0}}; +reg temp_m_axi_wlast_reg = 1'b0; +reg [WUSER_WIDTH-1:0] temp_m_axi_wuser_reg = 1'b0; +reg temp_m_axi_wvalid_reg = 1'b0, temp_m_axi_wvalid_next; + +// datapath control +reg store_axi_w_int_to_output; +reg store_axi_w_int_to_temp; +reg store_axi_w_temp_to_output; + +assign m_axi_wdata = {M_COUNT{m_axi_wdata_reg}}; +assign m_axi_wstrb = {M_COUNT{m_axi_wstrb_reg}}; +assign m_axi_wlast = {M_COUNT{m_axi_wlast_reg}}; +assign m_axi_wuser = {M_COUNT{WUSER_ENABLE ? m_axi_wuser_reg : {WUSER_WIDTH{1'b0}}}}; +assign m_axi_wvalid = m_axi_wvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axi_wready_int_early = current_m_axi_wready | (~temp_m_axi_wvalid_reg & (~current_m_axi_wvalid | ~m_axi_wvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axi_wvalid_next = m_axi_wvalid_reg; + temp_m_axi_wvalid_next = temp_m_axi_wvalid_reg; + + store_axi_w_int_to_output = 1'b0; + store_axi_w_int_to_temp = 1'b0; + store_axi_w_temp_to_output = 1'b0; + + if (m_axi_wready_int_reg) begin + // input is ready + if (current_m_axi_wready | ~current_m_axi_wvalid) begin + // output is ready or currently not valid, transfer data to output + m_axi_wvalid_next[m_select_reg] = m_axi_wvalid_int; + store_axi_w_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axi_wvalid_next = m_axi_wvalid_int; + store_axi_w_int_to_temp = 1'b1; + end + end else if (current_m_axi_wready) begin + // input is not ready, but output is ready + m_axi_wvalid_next[m_select_reg] = temp_m_axi_wvalid_reg; + temp_m_axi_wvalid_next = 1'b0; + store_axi_w_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axi_wvalid_reg <= 1'b0; + m_axi_wready_int_reg <= 1'b0; + temp_m_axi_wvalid_reg <= 1'b0; + end else begin + m_axi_wvalid_reg <= m_axi_wvalid_next; + m_axi_wready_int_reg <= m_axi_wready_int_early; + temp_m_axi_wvalid_reg <= temp_m_axi_wvalid_next; + end + + // datapath + if (store_axi_w_int_to_output) begin + m_axi_wdata_reg <= m_axi_wdata_int; + m_axi_wstrb_reg <= m_axi_wstrb_int; + m_axi_wlast_reg <= m_axi_wlast_int; + m_axi_wuser_reg <= m_axi_wuser_int; + end else if (store_axi_w_temp_to_output) begin + m_axi_wdata_reg <= temp_m_axi_wdata_reg; + m_axi_wstrb_reg <= temp_m_axi_wstrb_reg; + m_axi_wlast_reg <= temp_m_axi_wlast_reg; + m_axi_wuser_reg <= temp_m_axi_wuser_reg; + end + + if (store_axi_w_int_to_temp) begin + temp_m_axi_wdata_reg <= m_axi_wdata_int; + temp_m_axi_wstrb_reg <= m_axi_wstrb_int; + temp_m_axi_wlast_reg <= m_axi_wlast_int; + temp_m_axi_wuser_reg <= m_axi_wuser_int; + end +end + +endmodule diff --git a/tests/designs/axi4_ram/axi_ram.v b/tests/designs/axi4_ram/axi_ram.v new file mode 100644 index 00000000..35b87e7c --- /dev/null +++ b/tests/designs/axi4_ram/axi_ram.v @@ -0,0 +1,370 @@ +/* + +Copyright (c) 2018 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. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 RAM + */ +module axi_ram # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 16, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Extra pipeline register on output + parameter PIPELINE_OUTPUT = 0 +) +( + input wire clk, + input wire rst, + + input wire [ID_WIDTH-1:0] s_axi_awid, + input wire [ADDR_WIDTH-1:0] s_axi_awaddr, + input wire [7:0] s_axi_awlen, + input wire [2:0] s_axi_awsize, + input wire [1:0] s_axi_awburst, + input wire s_axi_awlock, + input wire [3:0] s_axi_awcache, + input wire [2:0] s_axi_awprot, + input wire s_axi_awvalid, + output wire s_axi_awready, + input wire [DATA_WIDTH-1:0] s_axi_wdata, + input wire [STRB_WIDTH-1:0] s_axi_wstrb, + input wire s_axi_wlast, + input wire s_axi_wvalid, + output wire s_axi_wready, + output wire [ID_WIDTH-1:0] s_axi_bid, + output wire [1:0] s_axi_bresp, + output wire s_axi_bvalid, + input wire s_axi_bready, + input wire [ID_WIDTH-1:0] s_axi_arid, + input wire [ADDR_WIDTH-1:0] s_axi_araddr, + input wire [7:0] s_axi_arlen, + input wire [2:0] s_axi_arsize, + input wire [1:0] s_axi_arburst, + input wire s_axi_arlock, + input wire [3:0] s_axi_arcache, + input wire [2:0] s_axi_arprot, + input wire s_axi_arvalid, + output wire s_axi_arready, + output wire [ID_WIDTH-1:0] s_axi_rid, + output wire [DATA_WIDTH-1:0] s_axi_rdata, + output wire [1:0] s_axi_rresp, + output wire s_axi_rlast, + output wire s_axi_rvalid, + input wire s_axi_rready +); + +parameter VALID_ADDR_WIDTH = ADDR_WIDTH - $clog2(STRB_WIDTH); +parameter WORD_WIDTH = STRB_WIDTH; +parameter WORD_SIZE = DATA_WIDTH/WORD_WIDTH; + +// bus width assertions +initial begin + if (WORD_SIZE * STRB_WIDTH != DATA_WIDTH) begin + $error("Error: AXI data width not evenly divisble (instance %m)"); + $finish; + end + + if (2**$clog2(WORD_WIDTH) != WORD_WIDTH) begin + $error("Error: AXI word width must be even power of two (instance %m)"); + $finish; + end +end + +localparam [0:0] + READ_STATE_IDLE = 1'd0, + READ_STATE_BURST = 1'd1; + +reg [0:0] read_state_reg = READ_STATE_IDLE, read_state_next; + +localparam [1:0] + WRITE_STATE_IDLE = 2'd0, + WRITE_STATE_BURST = 2'd1, + WRITE_STATE_RESP = 2'd2; + +reg [1:0] write_state_reg = WRITE_STATE_IDLE, write_state_next; + +reg mem_wr_en; +reg mem_rd_en; + +reg [ID_WIDTH-1:0] read_id_reg = {ID_WIDTH{1'b0}}, read_id_next; +reg [ADDR_WIDTH-1:0] read_addr_reg = {ADDR_WIDTH{1'b0}}, read_addr_next; +reg [7:0] read_count_reg = 8'd0, read_count_next; +reg [2:0] read_size_reg = 3'd0, read_size_next; +reg [1:0] read_burst_reg = 2'd0, read_burst_next; +reg [ID_WIDTH-1:0] write_id_reg = {ID_WIDTH{1'b0}}, write_id_next; +reg [ADDR_WIDTH-1:0] write_addr_reg = {ADDR_WIDTH{1'b0}}, write_addr_next; +reg [7:0] write_count_reg = 8'd0, write_count_next; +reg [2:0] write_size_reg = 3'd0, write_size_next; +reg [1:0] write_burst_reg = 2'd0, write_burst_next; + +reg s_axi_awready_reg = 1'b0, s_axi_awready_next; +reg s_axi_wready_reg = 1'b0, s_axi_wready_next; +reg [ID_WIDTH-1:0] s_axi_bid_reg = {ID_WIDTH{1'b0}}, s_axi_bid_next; +reg s_axi_bvalid_reg = 1'b0, s_axi_bvalid_next; +reg s_axi_arready_reg = 1'b0, s_axi_arready_next; +reg [ID_WIDTH-1:0] s_axi_rid_reg = {ID_WIDTH{1'b0}}, s_axi_rid_next; +reg [DATA_WIDTH-1:0] s_axi_rdata_reg = {DATA_WIDTH{1'b0}}, s_axi_rdata_next; +reg s_axi_rlast_reg = 1'b0, s_axi_rlast_next; +reg s_axi_rvalid_reg = 1'b0, s_axi_rvalid_next; +reg [ID_WIDTH-1:0] s_axi_rid_pipe_reg = {ID_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] s_axi_rdata_pipe_reg = {DATA_WIDTH{1'b0}}; +reg s_axi_rlast_pipe_reg = 1'b0; +reg s_axi_rvalid_pipe_reg = 1'b0; + +// (* RAM_STYLE="BLOCK" *) +reg [DATA_WIDTH-1:0] mem[(2**VALID_ADDR_WIDTH)-1:0]; + +wire [VALID_ADDR_WIDTH-1:0] s_axi_awaddr_valid = s_axi_awaddr >> (ADDR_WIDTH - VALID_ADDR_WIDTH); +wire [VALID_ADDR_WIDTH-1:0] s_axi_araddr_valid = s_axi_araddr >> (ADDR_WIDTH - VALID_ADDR_WIDTH); +wire [VALID_ADDR_WIDTH-1:0] read_addr_valid = read_addr_reg >> (ADDR_WIDTH - VALID_ADDR_WIDTH); +wire [VALID_ADDR_WIDTH-1:0] write_addr_valid = write_addr_reg >> (ADDR_WIDTH - VALID_ADDR_WIDTH); + +assign s_axi_awready = s_axi_awready_reg; +assign s_axi_wready = s_axi_wready_reg; +assign s_axi_bid = s_axi_bid_reg; +assign s_axi_bresp = 2'b00; +assign s_axi_bvalid = s_axi_bvalid_reg; +assign s_axi_arready = s_axi_arready_reg; +assign s_axi_rid = PIPELINE_OUTPUT ? s_axi_rid_pipe_reg : s_axi_rid_reg; +assign s_axi_rdata = PIPELINE_OUTPUT ? s_axi_rdata_pipe_reg : s_axi_rdata_reg; +assign s_axi_rresp = 2'b00; +assign s_axi_rlast = PIPELINE_OUTPUT ? s_axi_rlast_pipe_reg : s_axi_rlast_reg; +assign s_axi_rvalid = PIPELINE_OUTPUT ? s_axi_rvalid_pipe_reg : s_axi_rvalid_reg; + +integer i, j; + +initial begin + // two nested loops for smaller number of iterations per loop + // workaround for synthesizer complaints about large loop counts + for (i = 0; i < 2**VALID_ADDR_WIDTH; i = i + 2**(VALID_ADDR_WIDTH/2)) begin + for (j = i; j < i + 2**(VALID_ADDR_WIDTH/2); j = j + 1) begin + mem[j] = 0; + end + end +end + +always @* begin + write_state_next = WRITE_STATE_IDLE; + + mem_wr_en = 1'b0; + + write_id_next = write_id_reg; + write_addr_next = write_addr_reg; + write_count_next = write_count_reg; + write_size_next = write_size_reg; + write_burst_next = write_burst_reg; + + s_axi_awready_next = 1'b0; + s_axi_wready_next = 1'b0; + s_axi_bid_next = s_axi_bid_reg; + s_axi_bvalid_next = s_axi_bvalid_reg && !s_axi_bready; + + case (write_state_reg) + WRITE_STATE_IDLE: begin + s_axi_awready_next = 1'b1; + + if (s_axi_awready && s_axi_awvalid) begin + write_id_next = s_axi_awid; + write_addr_next = s_axi_awaddr; + write_count_next = s_axi_awlen; + write_size_next = s_axi_awsize < $clog2(STRB_WIDTH) ? s_axi_awsize : $clog2(STRB_WIDTH); + write_burst_next = s_axi_awburst; + + s_axi_awready_next = 1'b0; + s_axi_wready_next = 1'b1; + write_state_next = WRITE_STATE_BURST; + end else begin + write_state_next = WRITE_STATE_IDLE; + end + end + WRITE_STATE_BURST: begin + s_axi_wready_next = 1'b1; + + if (s_axi_wready && s_axi_wvalid) begin + mem_wr_en = 1'b1; + if (write_burst_reg != 2'b00) begin + write_addr_next = write_addr_reg + (1 << write_size_reg); + end + write_count_next = write_count_reg - 1; + if (write_count_reg > 0) begin + write_state_next = WRITE_STATE_BURST; + end else begin + s_axi_wready_next = 1'b0; + if (s_axi_bready || !s_axi_bvalid) begin + s_axi_bid_next = write_id_reg; + s_axi_bvalid_next = 1'b1; + s_axi_awready_next = 1'b1; + write_state_next = WRITE_STATE_IDLE; + end else begin + write_state_next = WRITE_STATE_RESP; + end + end + end else begin + write_state_next = WRITE_STATE_BURST; + end + end + WRITE_STATE_RESP: begin + if (s_axi_bready || !s_axi_bvalid) begin + s_axi_bid_next = write_id_reg; + s_axi_bvalid_next = 1'b1; + s_axi_awready_next = 1'b1; + write_state_next = WRITE_STATE_IDLE; + end else begin + write_state_next = WRITE_STATE_RESP; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + write_state_reg <= WRITE_STATE_IDLE; + s_axi_awready_reg <= 1'b0; + s_axi_wready_reg <= 1'b0; + s_axi_bvalid_reg <= 1'b0; + end else begin + write_state_reg <= write_state_next; + s_axi_awready_reg <= s_axi_awready_next; + s_axi_wready_reg <= s_axi_wready_next; + s_axi_bvalid_reg <= s_axi_bvalid_next; + end + + write_id_reg <= write_id_next; + write_addr_reg <= write_addr_next; + write_count_reg <= write_count_next; + write_size_reg <= write_size_next; + write_burst_reg <= write_burst_next; + + s_axi_bid_reg <= s_axi_bid_next; + + for (i = 0; i < WORD_WIDTH; i = i + 1) begin + if (mem_wr_en & s_axi_wstrb[i]) begin + mem[write_addr_valid][WORD_SIZE*i +: WORD_SIZE] <= s_axi_wdata[WORD_SIZE*i +: WORD_SIZE]; + end + end +end + +always @* begin + read_state_next = READ_STATE_IDLE; + + mem_rd_en = 1'b0; + + s_axi_rid_next = s_axi_rid_reg; + s_axi_rlast_next = s_axi_rlast_reg; + s_axi_rvalid_next = s_axi_rvalid_reg && !(s_axi_rready || (PIPELINE_OUTPUT && !s_axi_rvalid_pipe_reg)); + + read_id_next = read_id_reg; + read_addr_next = read_addr_reg; + read_count_next = read_count_reg; + read_size_next = read_size_reg; + read_burst_next = read_burst_reg; + + s_axi_arready_next = 1'b0; + + case (read_state_reg) + READ_STATE_IDLE: begin + s_axi_arready_next = 1'b1; + + if (s_axi_arready && s_axi_arvalid) begin + read_id_next = s_axi_arid; + read_addr_next = s_axi_araddr; + read_count_next = s_axi_arlen; + read_size_next = s_axi_arsize < $clog2(STRB_WIDTH) ? s_axi_arsize : $clog2(STRB_WIDTH); + read_burst_next = s_axi_arburst; + + s_axi_arready_next = 1'b0; + read_state_next = READ_STATE_BURST; + end else begin + read_state_next = READ_STATE_IDLE; + end + end + READ_STATE_BURST: begin + if (s_axi_rready || (PIPELINE_OUTPUT && !s_axi_rvalid_pipe_reg) || !s_axi_rvalid_reg) begin + mem_rd_en = 1'b1; + s_axi_rvalid_next = 1'b1; + s_axi_rid_next = read_id_reg; + s_axi_rlast_next = read_count_reg == 0; + if (read_burst_reg != 2'b00) begin + read_addr_next = read_addr_reg + (1 << read_size_reg); + end + read_count_next = read_count_reg - 1; + if (read_count_reg > 0) begin + read_state_next = READ_STATE_BURST; + end else begin + s_axi_arready_next = 1'b1; + read_state_next = READ_STATE_IDLE; + end + end else begin + read_state_next = READ_STATE_BURST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + read_state_reg <= READ_STATE_IDLE; + s_axi_arready_reg <= 1'b0; + s_axi_rvalid_reg <= 1'b0; + s_axi_rvalid_pipe_reg <= 1'b0; + end else begin + read_state_reg <= read_state_next; + s_axi_arready_reg <= s_axi_arready_next; + s_axi_rvalid_reg <= s_axi_rvalid_next; + + if (!s_axi_rvalid_pipe_reg || s_axi_rready) begin + s_axi_rvalid_pipe_reg <= s_axi_rvalid_reg; + end + end + + read_id_reg <= read_id_next; + read_addr_reg <= read_addr_next; + read_count_reg <= read_count_next; + read_size_reg <= read_size_next; + read_burst_reg <= read_burst_next; + + s_axi_rid_reg <= s_axi_rid_next; + s_axi_rlast_reg <= s_axi_rlast_next; + + if (mem_rd_en) begin + s_axi_rdata_reg <= mem[read_addr_valid]; + end + + if (!s_axi_rvalid_pipe_reg || s_axi_rready) begin + s_axi_rid_pipe_reg <= s_axi_rid_reg; + s_axi_rdata_pipe_reg <= s_axi_rdata_reg; + s_axi_rlast_pipe_reg <= s_axi_rlast_reg; + end +end + +endmodule diff --git a/tests/designs/axi4_ram/axi_register.v b/tests/designs/axi4_ram/axi_register.v new file mode 100644 index 00000000..040e5af4 --- /dev/null +++ b/tests/designs/axi4_ram/axi_register.v @@ -0,0 +1,320 @@ +/* + +Copyright (c) 2018 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. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 register + */ +module axi_register # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Propagate awuser signal + parameter AWUSER_ENABLE = 0, + // Width of awuser signal + parameter AWUSER_WIDTH = 1, + // Propagate wuser signal + parameter WUSER_ENABLE = 0, + // Width of wuser signal + parameter WUSER_WIDTH = 1, + // Propagate buser signal + parameter BUSER_ENABLE = 0, + // Width of buser signal + parameter BUSER_WIDTH = 1, + // Propagate aruser signal + parameter ARUSER_ENABLE = 0, + // Width of aruser signal + parameter ARUSER_WIDTH = 1, + // Propagate ruser signal + parameter RUSER_ENABLE = 0, + // Width of ruser signal + parameter RUSER_WIDTH = 1, + // AW channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter AW_REG_TYPE = 1, + // W channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter W_REG_TYPE = 2, + // B channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter B_REG_TYPE = 1, + // AR channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter AR_REG_TYPE = 1, + // R channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter R_REG_TYPE = 2 +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interface + */ + input wire [ID_WIDTH-1:0] s_axi_awid, + input wire [ADDR_WIDTH-1:0] s_axi_awaddr, + input wire [7:0] s_axi_awlen, + input wire [2:0] s_axi_awsize, + input wire [1:0] s_axi_awburst, + input wire s_axi_awlock, + input wire [3:0] s_axi_awcache, + input wire [2:0] s_axi_awprot, + input wire [3:0] s_axi_awqos, + input wire [3:0] s_axi_awregion, + input wire [AWUSER_WIDTH-1:0] s_axi_awuser, + input wire s_axi_awvalid, + output wire s_axi_awready, + input wire [DATA_WIDTH-1:0] s_axi_wdata, + input wire [STRB_WIDTH-1:0] s_axi_wstrb, + input wire s_axi_wlast, + input wire [WUSER_WIDTH-1:0] s_axi_wuser, + input wire s_axi_wvalid, + output wire s_axi_wready, + output wire [ID_WIDTH-1:0] s_axi_bid, + output wire [1:0] s_axi_bresp, + output wire [BUSER_WIDTH-1:0] s_axi_buser, + output wire s_axi_bvalid, + input wire s_axi_bready, + input wire [ID_WIDTH-1:0] s_axi_arid, + input wire [ADDR_WIDTH-1:0] s_axi_araddr, + input wire [7:0] s_axi_arlen, + input wire [2:0] s_axi_arsize, + input wire [1:0] s_axi_arburst, + input wire s_axi_arlock, + input wire [3:0] s_axi_arcache, + input wire [2:0] s_axi_arprot, + input wire [3:0] s_axi_arqos, + input wire [3:0] s_axi_arregion, + input wire [ARUSER_WIDTH-1:0] s_axi_aruser, + input wire s_axi_arvalid, + output wire s_axi_arready, + output wire [ID_WIDTH-1:0] s_axi_rid, + output wire [DATA_WIDTH-1:0] s_axi_rdata, + output wire [1:0] s_axi_rresp, + output wire s_axi_rlast, + output wire [RUSER_WIDTH-1:0] s_axi_ruser, + output wire s_axi_rvalid, + input wire s_axi_rready, + + /* + * AXI master interface + */ + output wire [ID_WIDTH-1:0] m_axi_awid, + output wire [ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [7:0] m_axi_awlen, + output wire [2:0] m_axi_awsize, + output wire [1:0] m_axi_awburst, + output wire m_axi_awlock, + output wire [3:0] m_axi_awcache, + output wire [2:0] m_axi_awprot, + output wire [3:0] m_axi_awqos, + output wire [3:0] m_axi_awregion, + output wire [AWUSER_WIDTH-1:0] m_axi_awuser, + output wire m_axi_awvalid, + input wire m_axi_awready, + output wire [DATA_WIDTH-1:0] m_axi_wdata, + output wire [STRB_WIDTH-1:0] m_axi_wstrb, + output wire m_axi_wlast, + output wire [WUSER_WIDTH-1:0] m_axi_wuser, + output wire m_axi_wvalid, + input wire m_axi_wready, + input wire [ID_WIDTH-1:0] m_axi_bid, + input wire [1:0] m_axi_bresp, + input wire [BUSER_WIDTH-1:0] m_axi_buser, + input wire m_axi_bvalid, + output wire m_axi_bready, + output wire [ID_WIDTH-1:0] m_axi_arid, + output wire [ADDR_WIDTH-1:0] m_axi_araddr, + output wire [7:0] m_axi_arlen, + output wire [2:0] m_axi_arsize, + output wire [1:0] m_axi_arburst, + output wire m_axi_arlock, + output wire [3:0] m_axi_arcache, + output wire [2:0] m_axi_arprot, + output wire [3:0] m_axi_arqos, + output wire [3:0] m_axi_arregion, + output wire [ARUSER_WIDTH-1:0] m_axi_aruser, + output wire m_axi_arvalid, + input wire m_axi_arready, + input wire [ID_WIDTH-1:0] m_axi_rid, + input wire [DATA_WIDTH-1:0] m_axi_rdata, + input wire [1:0] m_axi_rresp, + input wire m_axi_rlast, + input wire [RUSER_WIDTH-1:0] m_axi_ruser, + input wire m_axi_rvalid, + output wire m_axi_rready +); + +axi_register_wr #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .ID_WIDTH(ID_WIDTH), + .AWUSER_ENABLE(AWUSER_ENABLE), + .AWUSER_WIDTH(AWUSER_WIDTH), + .WUSER_ENABLE(WUSER_ENABLE), + .WUSER_WIDTH(WUSER_WIDTH), + .BUSER_ENABLE(BUSER_ENABLE), + .BUSER_WIDTH(BUSER_WIDTH), + .AW_REG_TYPE(AW_REG_TYPE), + .W_REG_TYPE(W_REG_TYPE), + .B_REG_TYPE(B_REG_TYPE) +) +axi_register_wr_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI slave interface + */ + .s_axi_awid(s_axi_awid), + .s_axi_awaddr(s_axi_awaddr), + .s_axi_awlen(s_axi_awlen), + .s_axi_awsize(s_axi_awsize), + .s_axi_awburst(s_axi_awburst), + .s_axi_awlock(s_axi_awlock), + .s_axi_awcache(s_axi_awcache), + .s_axi_awprot(s_axi_awprot), + .s_axi_awqos(s_axi_awqos), + .s_axi_awregion(s_axi_awregion), + .s_axi_awuser(s_axi_awuser), + .s_axi_awvalid(s_axi_awvalid), + .s_axi_awready(s_axi_awready), + .s_axi_wdata(s_axi_wdata), + .s_axi_wstrb(s_axi_wstrb), + .s_axi_wlast(s_axi_wlast), + .s_axi_wuser(s_axi_wuser), + .s_axi_wvalid(s_axi_wvalid), + .s_axi_wready(s_axi_wready), + .s_axi_bid(s_axi_bid), + .s_axi_bresp(s_axi_bresp), + .s_axi_buser(s_axi_buser), + .s_axi_bvalid(s_axi_bvalid), + .s_axi_bready(s_axi_bready), + + /* + * AXI master interface + */ + .m_axi_awid(m_axi_awid), + .m_axi_awaddr(m_axi_awaddr), + .m_axi_awlen(m_axi_awlen), + .m_axi_awsize(m_axi_awsize), + .m_axi_awburst(m_axi_awburst), + .m_axi_awlock(m_axi_awlock), + .m_axi_awcache(m_axi_awcache), + .m_axi_awprot(m_axi_awprot), + .m_axi_awqos(m_axi_awqos), + .m_axi_awregion(m_axi_awregion), + .m_axi_awuser(m_axi_awuser), + .m_axi_awvalid(m_axi_awvalid), + .m_axi_awready(m_axi_awready), + .m_axi_wdata(m_axi_wdata), + .m_axi_wstrb(m_axi_wstrb), + .m_axi_wlast(m_axi_wlast), + .m_axi_wuser(m_axi_wuser), + .m_axi_wvalid(m_axi_wvalid), + .m_axi_wready(m_axi_wready), + .m_axi_bid(m_axi_bid), + .m_axi_bresp(m_axi_bresp), + .m_axi_buser(m_axi_buser), + .m_axi_bvalid(m_axi_bvalid), + .m_axi_bready(m_axi_bready) +); + +axi_register_rd #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .ID_WIDTH(ID_WIDTH), + .ARUSER_ENABLE(ARUSER_ENABLE), + .ARUSER_WIDTH(ARUSER_WIDTH), + .RUSER_ENABLE(RUSER_ENABLE), + .RUSER_WIDTH(RUSER_WIDTH), + .AR_REG_TYPE(AR_REG_TYPE), + .R_REG_TYPE(R_REG_TYPE) +) +axi_register_rd_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI slave interface + */ + .s_axi_arid(s_axi_arid), + .s_axi_araddr(s_axi_araddr), + .s_axi_arlen(s_axi_arlen), + .s_axi_arsize(s_axi_arsize), + .s_axi_arburst(s_axi_arburst), + .s_axi_arlock(s_axi_arlock), + .s_axi_arcache(s_axi_arcache), + .s_axi_arprot(s_axi_arprot), + .s_axi_arqos(s_axi_arqos), + .s_axi_arregion(s_axi_arregion), + .s_axi_aruser(s_axi_aruser), + .s_axi_arvalid(s_axi_arvalid), + .s_axi_arready(s_axi_arready), + .s_axi_rid(s_axi_rid), + .s_axi_rdata(s_axi_rdata), + .s_axi_rresp(s_axi_rresp), + .s_axi_rlast(s_axi_rlast), + .s_axi_ruser(s_axi_ruser), + .s_axi_rvalid(s_axi_rvalid), + .s_axi_rready(s_axi_rready), + + /* + * AXI master interface + */ + .m_axi_arid(m_axi_arid), + .m_axi_araddr(m_axi_araddr), + .m_axi_arlen(m_axi_arlen), + .m_axi_arsize(m_axi_arsize), + .m_axi_arburst(m_axi_arburst), + .m_axi_arlock(m_axi_arlock), + .m_axi_arcache(m_axi_arcache), + .m_axi_arprot(m_axi_arprot), + .m_axi_arqos(m_axi_arqos), + .m_axi_arregion(m_axi_arregion), + .m_axi_aruser(m_axi_aruser), + .m_axi_arvalid(m_axi_arvalid), + .m_axi_arready(m_axi_arready), + .m_axi_rid(m_axi_rid), + .m_axi_rdata(m_axi_rdata), + .m_axi_rresp(m_axi_rresp), + .m_axi_rlast(m_axi_rlast), + .m_axi_ruser(m_axi_ruser), + .m_axi_rvalid(m_axi_rvalid), + .m_axi_rready(m_axi_rready) +); + +endmodule diff --git a/tests/designs/axi4_ram/axi_register_rd.v b/tests/designs/axi4_ram/axi_register_rd.v new file mode 100644 index 00000000..56648b4e --- /dev/null +++ b/tests/designs/axi4_ram/axi_register_rd.v @@ -0,0 +1,526 @@ +/* + +Copyright (c) 2018 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. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 register (read) + */ +module axi_register_rd # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Propagate aruser signal + parameter ARUSER_ENABLE = 0, + // Width of aruser signal + parameter ARUSER_WIDTH = 1, + // Propagate ruser signal + parameter RUSER_ENABLE = 0, + // Width of ruser signal + parameter RUSER_WIDTH = 1, + // AR channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter AR_REG_TYPE = 1, + // R channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter R_REG_TYPE = 2 +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interface + */ + input wire [ID_WIDTH-1:0] s_axi_arid, + input wire [ADDR_WIDTH-1:0] s_axi_araddr, + input wire [7:0] s_axi_arlen, + input wire [2:0] s_axi_arsize, + input wire [1:0] s_axi_arburst, + input wire s_axi_arlock, + input wire [3:0] s_axi_arcache, + input wire [2:0] s_axi_arprot, + input wire [3:0] s_axi_arqos, + input wire [3:0] s_axi_arregion, + input wire [ARUSER_WIDTH-1:0] s_axi_aruser, + input wire s_axi_arvalid, + output wire s_axi_arready, + output wire [ID_WIDTH-1:0] s_axi_rid, + output wire [DATA_WIDTH-1:0] s_axi_rdata, + output wire [1:0] s_axi_rresp, + output wire s_axi_rlast, + output wire [RUSER_WIDTH-1:0] s_axi_ruser, + output wire s_axi_rvalid, + input wire s_axi_rready, + + /* + * AXI master interface + */ + output wire [ID_WIDTH-1:0] m_axi_arid, + output wire [ADDR_WIDTH-1:0] m_axi_araddr, + output wire [7:0] m_axi_arlen, + output wire [2:0] m_axi_arsize, + output wire [1:0] m_axi_arburst, + output wire m_axi_arlock, + output wire [3:0] m_axi_arcache, + output wire [2:0] m_axi_arprot, + output wire [3:0] m_axi_arqos, + output wire [3:0] m_axi_arregion, + output wire [ARUSER_WIDTH-1:0] m_axi_aruser, + output wire m_axi_arvalid, + input wire m_axi_arready, + input wire [ID_WIDTH-1:0] m_axi_rid, + input wire [DATA_WIDTH-1:0] m_axi_rdata, + input wire [1:0] m_axi_rresp, + input wire m_axi_rlast, + input wire [RUSER_WIDTH-1:0] m_axi_ruser, + input wire m_axi_rvalid, + output wire m_axi_rready +); + +generate + +// AR channel + +if (AR_REG_TYPE > 1) begin +// skid buffer, no bubble cycles + +// datapath registers +reg s_axi_arready_reg = 1'b0; + +reg [ID_WIDTH-1:0] m_axi_arid_reg = {ID_WIDTH{1'b0}}; +reg [ADDR_WIDTH-1:0] m_axi_araddr_reg = {ADDR_WIDTH{1'b0}}; +reg [7:0] m_axi_arlen_reg = 8'd0; +reg [2:0] m_axi_arsize_reg = 3'd0; +reg [1:0] m_axi_arburst_reg = 2'd0; +reg m_axi_arlock_reg = 1'b0; +reg [3:0] m_axi_arcache_reg = 4'd0; +reg [2:0] m_axi_arprot_reg = 3'd0; +reg [3:0] m_axi_arqos_reg = 4'd0; +reg [3:0] m_axi_arregion_reg = 4'd0; +reg [ARUSER_WIDTH-1:0] m_axi_aruser_reg = {ARUSER_WIDTH{1'b0}}; +reg m_axi_arvalid_reg = 1'b0, m_axi_arvalid_next; + +reg [ID_WIDTH-1:0] temp_m_axi_arid_reg = {ID_WIDTH{1'b0}}; +reg [ADDR_WIDTH-1:0] temp_m_axi_araddr_reg = {ADDR_WIDTH{1'b0}}; +reg [7:0] temp_m_axi_arlen_reg = 8'd0; +reg [2:0] temp_m_axi_arsize_reg = 3'd0; +reg [1:0] temp_m_axi_arburst_reg = 2'd0; +reg temp_m_axi_arlock_reg = 1'b0; +reg [3:0] temp_m_axi_arcache_reg = 4'd0; +reg [2:0] temp_m_axi_arprot_reg = 3'd0; +reg [3:0] temp_m_axi_arqos_reg = 4'd0; +reg [3:0] temp_m_axi_arregion_reg = 4'd0; +reg [ARUSER_WIDTH-1:0] temp_m_axi_aruser_reg = {ARUSER_WIDTH{1'b0}}; +reg temp_m_axi_arvalid_reg = 1'b0, temp_m_axi_arvalid_next; + +// datapath control +reg store_axi_ar_input_to_output; +reg store_axi_ar_input_to_temp; +reg store_axi_ar_temp_to_output; + +assign s_axi_arready = s_axi_arready_reg; + +assign m_axi_arid = m_axi_arid_reg; +assign m_axi_araddr = m_axi_araddr_reg; +assign m_axi_arlen = m_axi_arlen_reg; +assign m_axi_arsize = m_axi_arsize_reg; +assign m_axi_arburst = m_axi_arburst_reg; +assign m_axi_arlock = m_axi_arlock_reg; +assign m_axi_arcache = m_axi_arcache_reg; +assign m_axi_arprot = m_axi_arprot_reg; +assign m_axi_arqos = m_axi_arqos_reg; +assign m_axi_arregion = m_axi_arregion_reg; +assign m_axi_aruser = ARUSER_ENABLE ? m_axi_aruser_reg : {ARUSER_WIDTH{1'b0}}; +assign m_axi_arvalid = m_axi_arvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +wire s_axi_arready_early = m_axi_arready | (~temp_m_axi_arvalid_reg & (~m_axi_arvalid_reg | ~s_axi_arvalid)); + +always @* begin + // transfer sink ready state to source + m_axi_arvalid_next = m_axi_arvalid_reg; + temp_m_axi_arvalid_next = temp_m_axi_arvalid_reg; + + store_axi_ar_input_to_output = 1'b0; + store_axi_ar_input_to_temp = 1'b0; + store_axi_ar_temp_to_output = 1'b0; + + if (s_axi_arready_reg) begin + // input is ready + if (m_axi_arready | ~m_axi_arvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axi_arvalid_next = s_axi_arvalid; + store_axi_ar_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axi_arvalid_next = s_axi_arvalid; + store_axi_ar_input_to_temp = 1'b1; + end + end else if (m_axi_arready) begin + // input is not ready, but output is ready + m_axi_arvalid_next = temp_m_axi_arvalid_reg; + temp_m_axi_arvalid_next = 1'b0; + store_axi_ar_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_arready_reg <= 1'b0; + m_axi_arvalid_reg <= 1'b0; + temp_m_axi_arvalid_reg <= 1'b0; + end else begin + s_axi_arready_reg <= s_axi_arready_early; + m_axi_arvalid_reg <= m_axi_arvalid_next; + temp_m_axi_arvalid_reg <= temp_m_axi_arvalid_next; + end + + // datapath + if (store_axi_ar_input_to_output) begin + m_axi_arid_reg <= s_axi_arid; + m_axi_araddr_reg <= s_axi_araddr; + m_axi_arlen_reg <= s_axi_arlen; + m_axi_arsize_reg <= s_axi_arsize; + m_axi_arburst_reg <= s_axi_arburst; + m_axi_arlock_reg <= s_axi_arlock; + m_axi_arcache_reg <= s_axi_arcache; + m_axi_arprot_reg <= s_axi_arprot; + m_axi_arqos_reg <= s_axi_arqos; + m_axi_arregion_reg <= s_axi_arregion; + m_axi_aruser_reg <= s_axi_aruser; + end else if (store_axi_ar_temp_to_output) begin + m_axi_arid_reg <= temp_m_axi_arid_reg; + m_axi_araddr_reg <= temp_m_axi_araddr_reg; + m_axi_arlen_reg <= temp_m_axi_arlen_reg; + m_axi_arsize_reg <= temp_m_axi_arsize_reg; + m_axi_arburst_reg <= temp_m_axi_arburst_reg; + m_axi_arlock_reg <= temp_m_axi_arlock_reg; + m_axi_arcache_reg <= temp_m_axi_arcache_reg; + m_axi_arprot_reg <= temp_m_axi_arprot_reg; + m_axi_arqos_reg <= temp_m_axi_arqos_reg; + m_axi_arregion_reg <= temp_m_axi_arregion_reg; + m_axi_aruser_reg <= temp_m_axi_aruser_reg; + end + + if (store_axi_ar_input_to_temp) begin + temp_m_axi_arid_reg <= s_axi_arid; + temp_m_axi_araddr_reg <= s_axi_araddr; + temp_m_axi_arlen_reg <= s_axi_arlen; + temp_m_axi_arsize_reg <= s_axi_arsize; + temp_m_axi_arburst_reg <= s_axi_arburst; + temp_m_axi_arlock_reg <= s_axi_arlock; + temp_m_axi_arcache_reg <= s_axi_arcache; + temp_m_axi_arprot_reg <= s_axi_arprot; + temp_m_axi_arqos_reg <= s_axi_arqos; + temp_m_axi_arregion_reg <= s_axi_arregion; + temp_m_axi_aruser_reg <= s_axi_aruser; + end +end + +end else if (AR_REG_TYPE == 1) begin +// simple register, inserts bubble cycles + +// datapath registers +reg s_axi_arready_reg = 1'b0; + +reg [ID_WIDTH-1:0] m_axi_arid_reg = {ID_WIDTH{1'b0}}; +reg [ADDR_WIDTH-1:0] m_axi_araddr_reg = {ADDR_WIDTH{1'b0}}; +reg [7:0] m_axi_arlen_reg = 8'd0; +reg [2:0] m_axi_arsize_reg = 3'd0; +reg [1:0] m_axi_arburst_reg = 2'd0; +reg m_axi_arlock_reg = 1'b0; +reg [3:0] m_axi_arcache_reg = 4'd0; +reg [2:0] m_axi_arprot_reg = 3'd0; +reg [3:0] m_axi_arqos_reg = 4'd0; +reg [3:0] m_axi_arregion_reg = 4'd0; +reg [ARUSER_WIDTH-1:0] m_axi_aruser_reg = {ARUSER_WIDTH{1'b0}}; +reg m_axi_arvalid_reg = 1'b0, m_axi_arvalid_next; + +// datapath control +reg store_axi_ar_input_to_output; + +assign s_axi_arready = s_axi_arready_reg; + +assign m_axi_arid = m_axi_arid_reg; +assign m_axi_araddr = m_axi_araddr_reg; +assign m_axi_arlen = m_axi_arlen_reg; +assign m_axi_arsize = m_axi_arsize_reg; +assign m_axi_arburst = m_axi_arburst_reg; +assign m_axi_arlock = m_axi_arlock_reg; +assign m_axi_arcache = m_axi_arcache_reg; +assign m_axi_arprot = m_axi_arprot_reg; +assign m_axi_arqos = m_axi_arqos_reg; +assign m_axi_arregion = m_axi_arregion_reg; +assign m_axi_aruser = ARUSER_ENABLE ? m_axi_aruser_reg : {ARUSER_WIDTH{1'b0}}; +assign m_axi_arvalid = m_axi_arvalid_reg; + +// enable ready input next cycle if output buffer will be empty +wire s_axi_arready_early = !m_axi_arvalid_next; + +always @* begin + // transfer sink ready state to source + m_axi_arvalid_next = m_axi_arvalid_reg; + + store_axi_ar_input_to_output = 1'b0; + + if (s_axi_arready_reg) begin + m_axi_arvalid_next = s_axi_arvalid; + store_axi_ar_input_to_output = 1'b1; + end else if (m_axi_arready) begin + m_axi_arvalid_next = 1'b0; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_arready_reg <= 1'b0; + m_axi_arvalid_reg <= 1'b0; + end else begin + s_axi_arready_reg <= s_axi_arready_early; + m_axi_arvalid_reg <= m_axi_arvalid_next; + end + + // datapath + if (store_axi_ar_input_to_output) begin + m_axi_arid_reg <= s_axi_arid; + m_axi_araddr_reg <= s_axi_araddr; + m_axi_arlen_reg <= s_axi_arlen; + m_axi_arsize_reg <= s_axi_arsize; + m_axi_arburst_reg <= s_axi_arburst; + m_axi_arlock_reg <= s_axi_arlock; + m_axi_arcache_reg <= s_axi_arcache; + m_axi_arprot_reg <= s_axi_arprot; + m_axi_arqos_reg <= s_axi_arqos; + m_axi_arregion_reg <= s_axi_arregion; + m_axi_aruser_reg <= s_axi_aruser; + end +end + +end else begin + + // bypass AR channel + assign m_axi_arid = s_axi_arid; + assign m_axi_araddr = s_axi_araddr; + assign m_axi_arlen = s_axi_arlen; + assign m_axi_arsize = s_axi_arsize; + assign m_axi_arburst = s_axi_arburst; + assign m_axi_arlock = s_axi_arlock; + assign m_axi_arcache = s_axi_arcache; + assign m_axi_arprot = s_axi_arprot; + assign m_axi_arqos = s_axi_arqos; + assign m_axi_arregion = s_axi_arregion; + assign m_axi_aruser = ARUSER_ENABLE ? s_axi_aruser : {ARUSER_WIDTH{1'b0}}; + assign m_axi_arvalid = s_axi_arvalid; + assign s_axi_arready = m_axi_arready; + +end + +// R channel + +if (R_REG_TYPE > 1) begin +// skid buffer, no bubble cycles + +// datapath registers +reg m_axi_rready_reg = 1'b0; + +reg [ID_WIDTH-1:0] s_axi_rid_reg = {ID_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] s_axi_rdata_reg = {DATA_WIDTH{1'b0}}; +reg [1:0] s_axi_rresp_reg = 2'b0; +reg s_axi_rlast_reg = 1'b0; +reg [RUSER_WIDTH-1:0] s_axi_ruser_reg = {RUSER_WIDTH{1'b0}}; +reg s_axi_rvalid_reg = 1'b0, s_axi_rvalid_next; + +reg [ID_WIDTH-1:0] temp_s_axi_rid_reg = {ID_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] temp_s_axi_rdata_reg = {DATA_WIDTH{1'b0}}; +reg [1:0] temp_s_axi_rresp_reg = 2'b0; +reg temp_s_axi_rlast_reg = 1'b0; +reg [RUSER_WIDTH-1:0] temp_s_axi_ruser_reg = {RUSER_WIDTH{1'b0}}; +reg temp_s_axi_rvalid_reg = 1'b0, temp_s_axi_rvalid_next; + +// datapath control +reg store_axi_r_input_to_output; +reg store_axi_r_input_to_temp; +reg store_axi_r_temp_to_output; + +assign m_axi_rready = m_axi_rready_reg; + +assign s_axi_rid = s_axi_rid_reg; +assign s_axi_rdata = s_axi_rdata_reg; +assign s_axi_rresp = s_axi_rresp_reg; +assign s_axi_rlast = s_axi_rlast_reg; +assign s_axi_ruser = RUSER_ENABLE ? s_axi_ruser_reg : {RUSER_WIDTH{1'b0}}; +assign s_axi_rvalid = s_axi_rvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +wire m_axi_rready_early = s_axi_rready | (~temp_s_axi_rvalid_reg & (~s_axi_rvalid_reg | ~m_axi_rvalid)); + +always @* begin + // transfer sink ready state to source + s_axi_rvalid_next = s_axi_rvalid_reg; + temp_s_axi_rvalid_next = temp_s_axi_rvalid_reg; + + store_axi_r_input_to_output = 1'b0; + store_axi_r_input_to_temp = 1'b0; + store_axi_r_temp_to_output = 1'b0; + + if (m_axi_rready_reg) begin + // input is ready + if (s_axi_rready | ~s_axi_rvalid_reg) begin + // output is ready or currently not valid, transfer data to output + s_axi_rvalid_next = m_axi_rvalid; + store_axi_r_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_s_axi_rvalid_next = m_axi_rvalid; + store_axi_r_input_to_temp = 1'b1; + end + end else if (s_axi_rready) begin + // input is not ready, but output is ready + s_axi_rvalid_next = temp_s_axi_rvalid_reg; + temp_s_axi_rvalid_next = 1'b0; + store_axi_r_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axi_rready_reg <= 1'b0; + s_axi_rvalid_reg <= 1'b0; + temp_s_axi_rvalid_reg <= 1'b0; + end else begin + m_axi_rready_reg <= m_axi_rready_early; + s_axi_rvalid_reg <= s_axi_rvalid_next; + temp_s_axi_rvalid_reg <= temp_s_axi_rvalid_next; + end + + // datapath + if (store_axi_r_input_to_output) begin + s_axi_rid_reg <= m_axi_rid; + s_axi_rdata_reg <= m_axi_rdata; + s_axi_rresp_reg <= m_axi_rresp; + s_axi_rlast_reg <= m_axi_rlast; + s_axi_ruser_reg <= m_axi_ruser; + end else if (store_axi_r_temp_to_output) begin + s_axi_rid_reg <= temp_s_axi_rid_reg; + s_axi_rdata_reg <= temp_s_axi_rdata_reg; + s_axi_rresp_reg <= temp_s_axi_rresp_reg; + s_axi_rlast_reg <= temp_s_axi_rlast_reg; + s_axi_ruser_reg <= temp_s_axi_ruser_reg; + end + + if (store_axi_r_input_to_temp) begin + temp_s_axi_rid_reg <= m_axi_rid; + temp_s_axi_rdata_reg <= m_axi_rdata; + temp_s_axi_rresp_reg <= m_axi_rresp; + temp_s_axi_rlast_reg <= m_axi_rlast; + temp_s_axi_ruser_reg <= m_axi_ruser; + end +end + +end else if (R_REG_TYPE == 1) begin +// simple register, inserts bubble cycles + +// datapath registers +reg m_axi_rready_reg = 1'b0; + +reg [ID_WIDTH-1:0] s_axi_rid_reg = {ID_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] s_axi_rdata_reg = {DATA_WIDTH{1'b0}}; +reg [1:0] s_axi_rresp_reg = 2'b0; +reg s_axi_rlast_reg = 1'b0; +reg [RUSER_WIDTH-1:0] s_axi_ruser_reg = {RUSER_WIDTH{1'b0}}; +reg s_axi_rvalid_reg = 1'b0, s_axi_rvalid_next; + +// datapath control +reg store_axi_r_input_to_output; + +assign m_axi_rready = m_axi_rready_reg; + +assign s_axi_rid = s_axi_rid_reg; +assign s_axi_rdata = s_axi_rdata_reg; +assign s_axi_rresp = s_axi_rresp_reg; +assign s_axi_rlast = s_axi_rlast_reg; +assign s_axi_ruser = RUSER_ENABLE ? s_axi_ruser_reg : {RUSER_WIDTH{1'b0}}; +assign s_axi_rvalid = s_axi_rvalid_reg; + +// enable ready input next cycle if output buffer will be empty +wire m_axi_rready_early = !s_axi_rvalid_next; + +always @* begin + // transfer sink ready state to source + s_axi_rvalid_next = s_axi_rvalid_reg; + + store_axi_r_input_to_output = 1'b0; + + if (m_axi_rready_reg) begin + s_axi_rvalid_next = m_axi_rvalid; + store_axi_r_input_to_output = 1'b1; + end else if (s_axi_rready) begin + s_axi_rvalid_next = 1'b0; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axi_rready_reg <= 1'b0; + s_axi_rvalid_reg <= 1'b0; + end else begin + m_axi_rready_reg <= m_axi_rready_early; + s_axi_rvalid_reg <= s_axi_rvalid_next; + end + + // datapath + if (store_axi_r_input_to_output) begin + s_axi_rid_reg <= m_axi_rid; + s_axi_rdata_reg <= m_axi_rdata; + s_axi_rresp_reg <= m_axi_rresp; + s_axi_rlast_reg <= m_axi_rlast; + s_axi_ruser_reg <= m_axi_ruser; + end +end + +end else begin + + // bypass R channel + assign s_axi_rid = m_axi_rid; + assign s_axi_rdata = m_axi_rdata; + assign s_axi_rresp = m_axi_rresp; + assign s_axi_rlast = m_axi_rlast; + assign s_axi_ruser = RUSER_ENABLE ? m_axi_ruser : {RUSER_WIDTH{1'b0}}; + assign s_axi_rvalid = m_axi_rvalid; + assign m_axi_rready = s_axi_rready; + +end + +endgenerate + +endmodule diff --git a/tests/designs/axi4_ram/axi_register_wr.v b/tests/designs/axi4_ram/axi_register_wr.v new file mode 100644 index 00000000..4d83a0ab --- /dev/null +++ b/tests/designs/axi4_ram/axi_register_wr.v @@ -0,0 +1,687 @@ +/* + +Copyright (c) 2018 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. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 register (write) + */ +module axi_register_wr # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Propagate awuser signal + parameter AWUSER_ENABLE = 0, + // Width of awuser signal + parameter AWUSER_WIDTH = 1, + // Propagate wuser signal + parameter WUSER_ENABLE = 0, + // Width of wuser signal + parameter WUSER_WIDTH = 1, + // Propagate buser signal + parameter BUSER_ENABLE = 0, + // Width of buser signal + parameter BUSER_WIDTH = 1, + // AW channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter AW_REG_TYPE = 1, + // W channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter W_REG_TYPE = 2, + // B channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter B_REG_TYPE = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interface + */ + input wire [ID_WIDTH-1:0] s_axi_awid, + input wire [ADDR_WIDTH-1:0] s_axi_awaddr, + input wire [7:0] s_axi_awlen, + input wire [2:0] s_axi_awsize, + input wire [1:0] s_axi_awburst, + input wire s_axi_awlock, + input wire [3:0] s_axi_awcache, + input wire [2:0] s_axi_awprot, + input wire [3:0] s_axi_awqos, + input wire [3:0] s_axi_awregion, + input wire [AWUSER_WIDTH-1:0] s_axi_awuser, + input wire s_axi_awvalid, + output wire s_axi_awready, + input wire [DATA_WIDTH-1:0] s_axi_wdata, + input wire [STRB_WIDTH-1:0] s_axi_wstrb, + input wire s_axi_wlast, + input wire [WUSER_WIDTH-1:0] s_axi_wuser, + input wire s_axi_wvalid, + output wire s_axi_wready, + output wire [ID_WIDTH-1:0] s_axi_bid, + output wire [1:0] s_axi_bresp, + output wire [BUSER_WIDTH-1:0] s_axi_buser, + output wire s_axi_bvalid, + input wire s_axi_bready, + + /* + * AXI master interface + */ + output wire [ID_WIDTH-1:0] m_axi_awid, + output wire [ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [7:0] m_axi_awlen, + output wire [2:0] m_axi_awsize, + output wire [1:0] m_axi_awburst, + output wire m_axi_awlock, + output wire [3:0] m_axi_awcache, + output wire [2:0] m_axi_awprot, + output wire [3:0] m_axi_awqos, + output wire [3:0] m_axi_awregion, + output wire [AWUSER_WIDTH-1:0] m_axi_awuser, + output wire m_axi_awvalid, + input wire m_axi_awready, + output wire [DATA_WIDTH-1:0] m_axi_wdata, + output wire [STRB_WIDTH-1:0] m_axi_wstrb, + output wire m_axi_wlast, + output wire [WUSER_WIDTH-1:0] m_axi_wuser, + output wire m_axi_wvalid, + input wire m_axi_wready, + input wire [ID_WIDTH-1:0] m_axi_bid, + input wire [1:0] m_axi_bresp, + input wire [BUSER_WIDTH-1:0] m_axi_buser, + input wire m_axi_bvalid, + output wire m_axi_bready +); + +generate + +// AW channel + +if (AW_REG_TYPE > 1) begin +// skid buffer, no bubble cycles + +// datapath registers +reg s_axi_awready_reg = 1'b0; + +reg [ID_WIDTH-1:0] m_axi_awid_reg = {ID_WIDTH{1'b0}}; +reg [ADDR_WIDTH-1:0] m_axi_awaddr_reg = {ADDR_WIDTH{1'b0}}; +reg [7:0] m_axi_awlen_reg = 8'd0; +reg [2:0] m_axi_awsize_reg = 3'd0; +reg [1:0] m_axi_awburst_reg = 2'd0; +reg m_axi_awlock_reg = 1'b0; +reg [3:0] m_axi_awcache_reg = 4'd0; +reg [2:0] m_axi_awprot_reg = 3'd0; +reg [3:0] m_axi_awqos_reg = 4'd0; +reg [3:0] m_axi_awregion_reg = 4'd0; +reg [AWUSER_WIDTH-1:0] m_axi_awuser_reg = {AWUSER_WIDTH{1'b0}}; +reg m_axi_awvalid_reg = 1'b0, m_axi_awvalid_next; + +reg [ID_WIDTH-1:0] temp_m_axi_awid_reg = {ID_WIDTH{1'b0}}; +reg [ADDR_WIDTH-1:0] temp_m_axi_awaddr_reg = {ADDR_WIDTH{1'b0}}; +reg [7:0] temp_m_axi_awlen_reg = 8'd0; +reg [2:0] temp_m_axi_awsize_reg = 3'd0; +reg [1:0] temp_m_axi_awburst_reg = 2'd0; +reg temp_m_axi_awlock_reg = 1'b0; +reg [3:0] temp_m_axi_awcache_reg = 4'd0; +reg [2:0] temp_m_axi_awprot_reg = 3'd0; +reg [3:0] temp_m_axi_awqos_reg = 4'd0; +reg [3:0] temp_m_axi_awregion_reg = 4'd0; +reg [AWUSER_WIDTH-1:0] temp_m_axi_awuser_reg = {AWUSER_WIDTH{1'b0}}; +reg temp_m_axi_awvalid_reg = 1'b0, temp_m_axi_awvalid_next; + +// datapath control +reg store_axi_aw_input_to_output; +reg store_axi_aw_input_to_temp; +reg store_axi_aw_temp_to_output; + +assign s_axi_awready = s_axi_awready_reg; + +assign m_axi_awid = m_axi_awid_reg; +assign m_axi_awaddr = m_axi_awaddr_reg; +assign m_axi_awlen = m_axi_awlen_reg; +assign m_axi_awsize = m_axi_awsize_reg; +assign m_axi_awburst = m_axi_awburst_reg; +assign m_axi_awlock = m_axi_awlock_reg; +assign m_axi_awcache = m_axi_awcache_reg; +assign m_axi_awprot = m_axi_awprot_reg; +assign m_axi_awqos = m_axi_awqos_reg; +assign m_axi_awregion = m_axi_awregion_reg; +assign m_axi_awuser = AWUSER_ENABLE ? m_axi_awuser_reg : {AWUSER_WIDTH{1'b0}}; +assign m_axi_awvalid = m_axi_awvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +wire s_axi_awready_early = m_axi_awready | (~temp_m_axi_awvalid_reg & (~m_axi_awvalid_reg | ~s_axi_awvalid)); + +always @* begin + // transfer sink ready state to source + m_axi_awvalid_next = m_axi_awvalid_reg; + temp_m_axi_awvalid_next = temp_m_axi_awvalid_reg; + + store_axi_aw_input_to_output = 1'b0; + store_axi_aw_input_to_temp = 1'b0; + store_axi_aw_temp_to_output = 1'b0; + + if (s_axi_awready_reg) begin + // input is ready + if (m_axi_awready | ~m_axi_awvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axi_awvalid_next = s_axi_awvalid; + store_axi_aw_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axi_awvalid_next = s_axi_awvalid; + store_axi_aw_input_to_temp = 1'b1; + end + end else if (m_axi_awready) begin + // input is not ready, but output is ready + m_axi_awvalid_next = temp_m_axi_awvalid_reg; + temp_m_axi_awvalid_next = 1'b0; + store_axi_aw_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_awready_reg <= 1'b0; + m_axi_awvalid_reg <= 1'b0; + temp_m_axi_awvalid_reg <= 1'b0; + end else begin + s_axi_awready_reg <= s_axi_awready_early; + m_axi_awvalid_reg <= m_axi_awvalid_next; + temp_m_axi_awvalid_reg <= temp_m_axi_awvalid_next; + end + + // datapath + if (store_axi_aw_input_to_output) begin + m_axi_awid_reg <= s_axi_awid; + m_axi_awaddr_reg <= s_axi_awaddr; + m_axi_awlen_reg <= s_axi_awlen; + m_axi_awsize_reg <= s_axi_awsize; + m_axi_awburst_reg <= s_axi_awburst; + m_axi_awlock_reg <= s_axi_awlock; + m_axi_awcache_reg <= s_axi_awcache; + m_axi_awprot_reg <= s_axi_awprot; + m_axi_awqos_reg <= s_axi_awqos; + m_axi_awregion_reg <= s_axi_awregion; + m_axi_awuser_reg <= s_axi_awuser; + end else if (store_axi_aw_temp_to_output) begin + m_axi_awid_reg <= temp_m_axi_awid_reg; + m_axi_awaddr_reg <= temp_m_axi_awaddr_reg; + m_axi_awlen_reg <= temp_m_axi_awlen_reg; + m_axi_awsize_reg <= temp_m_axi_awsize_reg; + m_axi_awburst_reg <= temp_m_axi_awburst_reg; + m_axi_awlock_reg <= temp_m_axi_awlock_reg; + m_axi_awcache_reg <= temp_m_axi_awcache_reg; + m_axi_awprot_reg <= temp_m_axi_awprot_reg; + m_axi_awqos_reg <= temp_m_axi_awqos_reg; + m_axi_awregion_reg <= temp_m_axi_awregion_reg; + m_axi_awuser_reg <= temp_m_axi_awuser_reg; + end + + if (store_axi_aw_input_to_temp) begin + temp_m_axi_awid_reg <= s_axi_awid; + temp_m_axi_awaddr_reg <= s_axi_awaddr; + temp_m_axi_awlen_reg <= s_axi_awlen; + temp_m_axi_awsize_reg <= s_axi_awsize; + temp_m_axi_awburst_reg <= s_axi_awburst; + temp_m_axi_awlock_reg <= s_axi_awlock; + temp_m_axi_awcache_reg <= s_axi_awcache; + temp_m_axi_awprot_reg <= s_axi_awprot; + temp_m_axi_awqos_reg <= s_axi_awqos; + temp_m_axi_awregion_reg <= s_axi_awregion; + temp_m_axi_awuser_reg <= s_axi_awuser; + end +end + +end else if (AW_REG_TYPE == 1) begin +// simple register, inserts bubble cycles + +// datapath registers +reg s_axi_awready_reg = 1'b0; + +reg [ID_WIDTH-1:0] m_axi_awid_reg = {ID_WIDTH{1'b0}}; +reg [ADDR_WIDTH-1:0] m_axi_awaddr_reg = {ADDR_WIDTH{1'b0}}; +reg [7:0] m_axi_awlen_reg = 8'd0; +reg [2:0] m_axi_awsize_reg = 3'd0; +reg [1:0] m_axi_awburst_reg = 2'd0; +reg m_axi_awlock_reg = 1'b0; +reg [3:0] m_axi_awcache_reg = 4'd0; +reg [2:0] m_axi_awprot_reg = 3'd0; +reg [3:0] m_axi_awqos_reg = 4'd0; +reg [3:0] m_axi_awregion_reg = 4'd0; +reg [AWUSER_WIDTH-1:0] m_axi_awuser_reg = {AWUSER_WIDTH{1'b0}}; +reg m_axi_awvalid_reg = 1'b0, m_axi_awvalid_next; + +// datapath control +reg store_axi_aw_input_to_output; + +assign s_axi_awready = s_axi_awready_reg; + +assign m_axi_awid = m_axi_awid_reg; +assign m_axi_awaddr = m_axi_awaddr_reg; +assign m_axi_awlen = m_axi_awlen_reg; +assign m_axi_awsize = m_axi_awsize_reg; +assign m_axi_awburst = m_axi_awburst_reg; +assign m_axi_awlock = m_axi_awlock_reg; +assign m_axi_awcache = m_axi_awcache_reg; +assign m_axi_awprot = m_axi_awprot_reg; +assign m_axi_awqos = m_axi_awqos_reg; +assign m_axi_awregion = m_axi_awregion_reg; +assign m_axi_awuser = AWUSER_ENABLE ? m_axi_awuser_reg : {AWUSER_WIDTH{1'b0}}; +assign m_axi_awvalid = m_axi_awvalid_reg; + +// enable ready input next cycle if output buffer will be empty +wire s_axi_awready_eawly = !m_axi_awvalid_next; + +always @* begin + // transfer sink ready state to source + m_axi_awvalid_next = m_axi_awvalid_reg; + + store_axi_aw_input_to_output = 1'b0; + + if (s_axi_awready_reg) begin + m_axi_awvalid_next = s_axi_awvalid; + store_axi_aw_input_to_output = 1'b1; + end else if (m_axi_awready) begin + m_axi_awvalid_next = 1'b0; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_awready_reg <= 1'b0; + m_axi_awvalid_reg <= 1'b0; + end else begin + s_axi_awready_reg <= s_axi_awready_eawly; + m_axi_awvalid_reg <= m_axi_awvalid_next; + end + + // datapath + if (store_axi_aw_input_to_output) begin + m_axi_awid_reg <= s_axi_awid; + m_axi_awaddr_reg <= s_axi_awaddr; + m_axi_awlen_reg <= s_axi_awlen; + m_axi_awsize_reg <= s_axi_awsize; + m_axi_awburst_reg <= s_axi_awburst; + m_axi_awlock_reg <= s_axi_awlock; + m_axi_awcache_reg <= s_axi_awcache; + m_axi_awprot_reg <= s_axi_awprot; + m_axi_awqos_reg <= s_axi_awqos; + m_axi_awregion_reg <= s_axi_awregion; + m_axi_awuser_reg <= s_axi_awuser; + end +end + +end else begin + + // bypass AW channel + assign m_axi_awid = s_axi_awid; + assign m_axi_awaddr = s_axi_awaddr; + assign m_axi_awlen = s_axi_awlen; + assign m_axi_awsize = s_axi_awsize; + assign m_axi_awburst = s_axi_awburst; + assign m_axi_awlock = s_axi_awlock; + assign m_axi_awcache = s_axi_awcache; + assign m_axi_awprot = s_axi_awprot; + assign m_axi_awqos = s_axi_awqos; + assign m_axi_awregion = s_axi_awregion; + assign m_axi_awuser = AWUSER_ENABLE ? s_axi_awuser : {AWUSER_WIDTH{1'b0}}; + assign m_axi_awvalid = s_axi_awvalid; + assign s_axi_awready = m_axi_awready; + +end + +// W channel + +if (W_REG_TYPE > 1) begin +// skid buffer, no bubble cycles + +// datapath registers +reg s_axi_wready_reg = 1'b0; + +reg [DATA_WIDTH-1:0] m_axi_wdata_reg = {DATA_WIDTH{1'b0}}; +reg [STRB_WIDTH-1:0] m_axi_wstrb_reg = {STRB_WIDTH{1'b0}}; +reg m_axi_wlast_reg = 1'b0; +reg [WUSER_WIDTH-1:0] m_axi_wuser_reg = {WUSER_WIDTH{1'b0}}; +reg m_axi_wvalid_reg = 1'b0, m_axi_wvalid_next; + +reg [DATA_WIDTH-1:0] temp_m_axi_wdata_reg = {DATA_WIDTH{1'b0}}; +reg [STRB_WIDTH-1:0] temp_m_axi_wstrb_reg = {STRB_WIDTH{1'b0}}; +reg temp_m_axi_wlast_reg = 1'b0; +reg [WUSER_WIDTH-1:0] temp_m_axi_wuser_reg = {WUSER_WIDTH{1'b0}}; +reg temp_m_axi_wvalid_reg = 1'b0, temp_m_axi_wvalid_next; + +// datapath control +reg store_axi_w_input_to_output; +reg store_axi_w_input_to_temp; +reg store_axi_w_temp_to_output; + +assign s_axi_wready = s_axi_wready_reg; + +assign m_axi_wdata = m_axi_wdata_reg; +assign m_axi_wstrb = m_axi_wstrb_reg; +assign m_axi_wlast = m_axi_wlast_reg; +assign m_axi_wuser = WUSER_ENABLE ? m_axi_wuser_reg : {WUSER_WIDTH{1'b0}}; +assign m_axi_wvalid = m_axi_wvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +wire s_axi_wready_early = m_axi_wready | (~temp_m_axi_wvalid_reg & (~m_axi_wvalid_reg | ~s_axi_wvalid)); + +always @* begin + // transfer sink ready state to source + m_axi_wvalid_next = m_axi_wvalid_reg; + temp_m_axi_wvalid_next = temp_m_axi_wvalid_reg; + + store_axi_w_input_to_output = 1'b0; + store_axi_w_input_to_temp = 1'b0; + store_axi_w_temp_to_output = 1'b0; + + if (s_axi_wready_reg) begin + // input is ready + if (m_axi_wready | ~m_axi_wvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axi_wvalid_next = s_axi_wvalid; + store_axi_w_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axi_wvalid_next = s_axi_wvalid; + store_axi_w_input_to_temp = 1'b1; + end + end else if (m_axi_wready) begin + // input is not ready, but output is ready + m_axi_wvalid_next = temp_m_axi_wvalid_reg; + temp_m_axi_wvalid_next = 1'b0; + store_axi_w_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_wready_reg <= 1'b0; + m_axi_wvalid_reg <= 1'b0; + temp_m_axi_wvalid_reg <= 1'b0; + end else begin + s_axi_wready_reg <= s_axi_wready_early; + m_axi_wvalid_reg <= m_axi_wvalid_next; + temp_m_axi_wvalid_reg <= temp_m_axi_wvalid_next; + end + + // datapath + if (store_axi_w_input_to_output) begin + m_axi_wdata_reg <= s_axi_wdata; + m_axi_wstrb_reg <= s_axi_wstrb; + m_axi_wlast_reg <= s_axi_wlast; + m_axi_wuser_reg <= s_axi_wuser; + end else if (store_axi_w_temp_to_output) begin + m_axi_wdata_reg <= temp_m_axi_wdata_reg; + m_axi_wstrb_reg <= temp_m_axi_wstrb_reg; + m_axi_wlast_reg <= temp_m_axi_wlast_reg; + m_axi_wuser_reg <= temp_m_axi_wuser_reg; + end + + if (store_axi_w_input_to_temp) begin + temp_m_axi_wdata_reg <= s_axi_wdata; + temp_m_axi_wstrb_reg <= s_axi_wstrb; + temp_m_axi_wlast_reg <= s_axi_wlast; + temp_m_axi_wuser_reg <= s_axi_wuser; + end +end + +end else if (W_REG_TYPE == 1) begin +// simple register, inserts bubble cycles + +// datapath registers +reg s_axi_wready_reg = 1'b0; + +reg [DATA_WIDTH-1:0] m_axi_wdata_reg = {DATA_WIDTH{1'b0}}; +reg [STRB_WIDTH-1:0] m_axi_wstrb_reg = {STRB_WIDTH{1'b0}}; +reg m_axi_wlast_reg = 1'b0; +reg [WUSER_WIDTH-1:0] m_axi_wuser_reg = {WUSER_WIDTH{1'b0}}; +reg m_axi_wvalid_reg = 1'b0, m_axi_wvalid_next; + +// datapath control +reg store_axi_w_input_to_output; + +assign s_axi_wready = s_axi_wready_reg; + +assign m_axi_wdata = m_axi_wdata_reg; +assign m_axi_wstrb = m_axi_wstrb_reg; +assign m_axi_wlast = m_axi_wlast_reg; +assign m_axi_wuser = WUSER_ENABLE ? m_axi_wuser_reg : {WUSER_WIDTH{1'b0}}; +assign m_axi_wvalid = m_axi_wvalid_reg; + +// enable ready input next cycle if output buffer will be empty +wire s_axi_wready_ewly = !m_axi_wvalid_next; + +always @* begin + // transfer sink ready state to source + m_axi_wvalid_next = m_axi_wvalid_reg; + + store_axi_w_input_to_output = 1'b0; + + if (s_axi_wready_reg) begin + m_axi_wvalid_next = s_axi_wvalid; + store_axi_w_input_to_output = 1'b1; + end else if (m_axi_wready) begin + m_axi_wvalid_next = 1'b0; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_wready_reg <= 1'b0; + m_axi_wvalid_reg <= 1'b0; + end else begin + s_axi_wready_reg <= s_axi_wready_ewly; + m_axi_wvalid_reg <= m_axi_wvalid_next; + end + + // datapath + if (store_axi_w_input_to_output) begin + m_axi_wdata_reg <= s_axi_wdata; + m_axi_wstrb_reg <= s_axi_wstrb; + m_axi_wlast_reg <= s_axi_wlast; + m_axi_wuser_reg <= s_axi_wuser; + end +end + +end else begin + + // bypass W channel + assign m_axi_wdata = s_axi_wdata; + assign m_axi_wstrb = s_axi_wstrb; + assign m_axi_wlast = s_axi_wlast; + assign m_axi_wuser = WUSER_ENABLE ? s_axi_wuser : {WUSER_WIDTH{1'b0}}; + assign m_axi_wvalid = s_axi_wvalid; + assign s_axi_wready = m_axi_wready; + +end + +// B channel + +if (B_REG_TYPE > 1) begin +// skid buffer, no bubble cycles + +// datapath registers +reg m_axi_bready_reg = 1'b0; + +reg [ID_WIDTH-1:0] s_axi_bid_reg = {ID_WIDTH{1'b0}}; +reg [1:0] s_axi_bresp_reg = 2'b0; +reg [BUSER_WIDTH-1:0] s_axi_buser_reg = {BUSER_WIDTH{1'b0}}; +reg s_axi_bvalid_reg = 1'b0, s_axi_bvalid_next; + +reg [ID_WIDTH-1:0] temp_s_axi_bid_reg = {ID_WIDTH{1'b0}}; +reg [1:0] temp_s_axi_bresp_reg = 2'b0; +reg [BUSER_WIDTH-1:0] temp_s_axi_buser_reg = {BUSER_WIDTH{1'b0}}; +reg temp_s_axi_bvalid_reg = 1'b0, temp_s_axi_bvalid_next; + +// datapath control +reg store_axi_b_input_to_output; +reg store_axi_b_input_to_temp; +reg store_axi_b_temp_to_output; + +assign m_axi_bready = m_axi_bready_reg; + +assign s_axi_bid = s_axi_bid_reg; +assign s_axi_bresp = s_axi_bresp_reg; +assign s_axi_buser = BUSER_ENABLE ? s_axi_buser_reg : {BUSER_WIDTH{1'b0}}; +assign s_axi_bvalid = s_axi_bvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +wire m_axi_bready_early = s_axi_bready | (~temp_s_axi_bvalid_reg & (~s_axi_bvalid_reg | ~m_axi_bvalid)); + +always @* begin + // transfer sink ready state to source + s_axi_bvalid_next = s_axi_bvalid_reg; + temp_s_axi_bvalid_next = temp_s_axi_bvalid_reg; + + store_axi_b_input_to_output = 1'b0; + store_axi_b_input_to_temp = 1'b0; + store_axi_b_temp_to_output = 1'b0; + + if (m_axi_bready_reg) begin + // input is ready + if (s_axi_bready | ~s_axi_bvalid_reg) begin + // output is ready or currently not valid, transfer data to output + s_axi_bvalid_next = m_axi_bvalid; + store_axi_b_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_s_axi_bvalid_next = m_axi_bvalid; + store_axi_b_input_to_temp = 1'b1; + end + end else if (s_axi_bready) begin + // input is not ready, but output is ready + s_axi_bvalid_next = temp_s_axi_bvalid_reg; + temp_s_axi_bvalid_next = 1'b0; + store_axi_b_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axi_bready_reg <= 1'b0; + s_axi_bvalid_reg <= 1'b0; + temp_s_axi_bvalid_reg <= 1'b0; + end else begin + m_axi_bready_reg <= m_axi_bready_early; + s_axi_bvalid_reg <= s_axi_bvalid_next; + temp_s_axi_bvalid_reg <= temp_s_axi_bvalid_next; + end + + // datapath + if (store_axi_b_input_to_output) begin + s_axi_bid_reg <= m_axi_bid; + s_axi_bresp_reg <= m_axi_bresp; + s_axi_buser_reg <= m_axi_buser; + end else if (store_axi_b_temp_to_output) begin + s_axi_bid_reg <= temp_s_axi_bid_reg; + s_axi_bresp_reg <= temp_s_axi_bresp_reg; + s_axi_buser_reg <= temp_s_axi_buser_reg; + end + + if (store_axi_b_input_to_temp) begin + temp_s_axi_bid_reg <= m_axi_bid; + temp_s_axi_bresp_reg <= m_axi_bresp; + temp_s_axi_buser_reg <= m_axi_buser; + end +end + +end else if (B_REG_TYPE == 1) begin +// simple register, inserts bubble cycles + +// datapath registers +reg m_axi_bready_reg = 1'b0; + +reg [ID_WIDTH-1:0] s_axi_bid_reg = {ID_WIDTH{1'b0}}; +reg [1:0] s_axi_bresp_reg = 2'b0; +reg [BUSER_WIDTH-1:0] s_axi_buser_reg = {BUSER_WIDTH{1'b0}}; +reg s_axi_bvalid_reg = 1'b0, s_axi_bvalid_next; + +// datapath control +reg store_axi_b_input_to_output; + +assign m_axi_bready = m_axi_bready_reg; + +assign s_axi_bid = s_axi_bid_reg; +assign s_axi_bresp = s_axi_bresp_reg; +assign s_axi_buser = BUSER_ENABLE ? s_axi_buser_reg : {BUSER_WIDTH{1'b0}}; +assign s_axi_bvalid = s_axi_bvalid_reg; + +// enable ready input next cycle if output buffer will be empty +wire m_axi_bready_early = !s_axi_bvalid_next; + +always @* begin + // transfer sink ready state to source + s_axi_bvalid_next = s_axi_bvalid_reg; + + store_axi_b_input_to_output = 1'b0; + + if (m_axi_bready_reg) begin + s_axi_bvalid_next = m_axi_bvalid; + store_axi_b_input_to_output = 1'b1; + end else if (s_axi_bready) begin + s_axi_bvalid_next = 1'b0; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axi_bready_reg <= 1'b0; + s_axi_bvalid_reg <= 1'b0; + end else begin + m_axi_bready_reg <= m_axi_bready_early; + s_axi_bvalid_reg <= s_axi_bvalid_next; + end + + // datapath + if (store_axi_b_input_to_output) begin + s_axi_bid_reg <= m_axi_bid; + s_axi_bresp_reg <= m_axi_bresp; + s_axi_buser_reg <= m_axi_buser; + end +end + +end else begin + + // bypass B channel + assign s_axi_bid = m_axi_bid; + assign s_axi_bresp = m_axi_bresp; + assign s_axi_buser = BUSER_ENABLE ? m_axi_buser : {BUSER_WIDTH{1'b0}}; + assign s_axi_bvalid = m_axi_bvalid; + assign m_axi_bready = s_axi_bready; + +end + +endgenerate + +endmodule diff --git a/tests/designs/axi4_ram/priority_encoder.v b/tests/designs/axi4_ram/priority_encoder.v new file mode 100644 index 00000000..8735a0e3 --- /dev/null +++ b/tests/designs/axi4_ram/priority_encoder.v @@ -0,0 +1,84 @@ +/* + +Copyright (c) 2014-2018 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. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Priority encoder module + */ +module priority_encoder # +( + parameter WIDTH = 4, + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "LOW" +) +( + input wire [WIDTH-1:0] input_unencoded, + output wire output_valid, + output wire [$clog2(WIDTH)-1:0] output_encoded, + output wire [WIDTH-1:0] output_unencoded +); + +parameter LEVELS = WIDTH > 2 ? $clog2(WIDTH) : 1; +parameter W = 2**LEVELS; + +// pad input to even power of two +wire [W-1:0] input_padded = {{W-WIDTH{1'b0}}, input_unencoded}; + +wire [W/2-1:0] stage_valid[LEVELS-1:0]; +wire [W/2-1:0] stage_enc[LEVELS-1:0]; + +generate + genvar l, n; + + // process input bits; generate valid bit and encoded bit for each pair + for (n = 0; n < W/2; n = n + 1) begin : loop_in + assign stage_valid[0][n] = |input_padded[n*2+1:n*2]; + if (LSB_PRIORITY == "LOW") begin + assign stage_enc[0][n] = input_padded[n*2+1]; + end else begin + assign stage_enc[0][n] = !input_padded[n*2+0]; + end + end + + // compress down to single valid bit and encoded bus + for (l = 1; l < LEVELS; l = l + 1) begin : loop_levels + for (n = 0; n < W/(2*2**l); n = n + 1) begin : loop_compress + assign stage_valid[l][n] = |stage_valid[l-1][n*2+1:n*2]; + if (LSB_PRIORITY == "LOW") begin + assign stage_enc[l][(n+1)*(l+1)-1:n*(l+1)] = stage_valid[l-1][n*2+1] ? {1'b1, stage_enc[l-1][(n*2+2)*l-1:(n*2+1)*l]} : {1'b0, stage_enc[l-1][(n*2+1)*l-1:(n*2+0)*l]}; + end else begin + assign stage_enc[l][(n+1)*(l+1)-1:n*(l+1)] = stage_valid[l-1][n*2+0] ? {1'b0, stage_enc[l-1][(n*2+1)*l-1:(n*2+0)*l]} : {1'b1, stage_enc[l-1][(n*2+2)*l-1:(n*2+1)*l]}; + end + end + end +endgenerate + +assign output_valid = stage_valid[LEVELS-1]; +assign output_encoded = stage_enc[LEVELS-1]; +assign output_unencoded = 1 << output_encoded; + +endmodule diff --git a/tests/designs/axi4_ram/top.v b/tests/designs/axi4_ram/top.v new file mode 100644 index 00000000..88a35027 --- /dev/null +++ b/tests/designs/axi4_ram/top.v @@ -0,0 +1,467 @@ +/* + * Copyright cocotb contributors + * Licensed under the Revised BSD License, see LICENSE for details. + * SPDX-License-Identifier: BSD-3-Clause + */ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 ram and interconnect + */ + + module top # + ( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Width of awuser signal + parameter AWUSER_WIDTH = 8, + // Width of wuser signal + parameter WUSER_WIDTH = 8, + // Width of buser signal + parameter BUSER_WIDTH = 8, + // Width of aruser signal + parameter ARUSER_WIDTH = 8, + // Width of ruser signal + parameter RUSER_WIDTH = 8, + // Base RAM address + parameter RAM_BASE_ADDRESS = 32'h4000, + // RAM width (RAM size = 2^RAM_WIDTH) + parameter RAM_WIDTH = 13, + // Extra pipeline register on RAM output + parameter RAM_PIPELINE_OUTPUT = 1, + // AW channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter AW_REG_TYPE = 2, + // W channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter W_REG_TYPE = 0, + // B channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter B_REG_TYPE = 1, + // AR channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter AR_REG_TYPE = 2, + // R channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter R_REG_TYPE = 0 + ) + ( + input wire clk, + input wire rstn, + + input wire [ID_WIDTH-1:0] S_AXI_AWID, + input wire [ADDR_WIDTH-1:0] S_AXI_AWADDR, + input wire [7:0] S_AXI_AWLEN, + input wire [2:0] S_AXI_AWSIZE, + input wire [1:0] S_AXI_AWBURST, + input wire S_AXI_AWLOCK, + input wire [3:0] S_AXI_AWCACHE, + input wire [2:0] S_AXI_AWPROT, + input wire [3:0] S_AXI_AWQOS, + input wire [AWUSER_WIDTH-1:0] S_AXI_AWUSER, + input wire S_AXI_AWVALID, + output wire S_AXI_AWREADY, + input wire [DATA_WIDTH-1:0] S_AXI_WDATA, + input wire [STRB_WIDTH-1:0] S_AXI_WSTRB, + input wire S_AXI_WLAST, + input wire [WUSER_WIDTH-1:0] S_AXI_WUSER, + input wire S_AXI_WVALID, + output wire S_AXI_WREADY, + output wire [ID_WIDTH-1:0] S_AXI_BID, + output wire [1:0] S_AXI_BRESP, + output wire [BUSER_WIDTH-1:0] S_AXI_BUSER, + output wire S_AXI_BVALID, + input wire S_AXI_BREADY, + input wire [ID_WIDTH-1:0] S_AXI_ARID, + input wire [ADDR_WIDTH-1:0] S_AXI_ARADDR, + input wire [7:0] S_AXI_ARLEN, + input wire [2:0] S_AXI_ARSIZE, + input wire [1:0] S_AXI_ARBURST, + input wire S_AXI_ARLOCK, + input wire [3:0] S_AXI_ARCACHE, + input wire [2:0] S_AXI_ARPROT, + input wire [3:0] S_AXI_ARQOS, + input wire [ARUSER_WIDTH-1:0] S_AXI_ARUSER, + input wire S_AXI_ARVALID, + output wire S_AXI_ARREADY, + output wire [ID_WIDTH-1:0] S_AXI_RID, + output wire [DATA_WIDTH-1:0] S_AXI_RDATA, + output wire [1:0] S_AXI_RRESP, + output wire S_AXI_RLAST, + output wire [RUSER_WIDTH-1:0] S_AXI_RUSER, + output wire S_AXI_RVALID, + input wire S_AXI_RREADY +); + +wire [ID_WIDTH-1:0] reg_axi_awid; +wire [ADDR_WIDTH-1:0] reg_axi_awaddr; +wire [7:0] reg_axi_awlen; +wire [2:0] reg_axi_awsize; +wire [1:0] reg_axi_awburst; +wire reg_axi_awlock; +wire [3:0] reg_axi_awcache; +wire [2:0] reg_axi_awprot; +wire [3:0] reg_axi_awqos; +wire [3:0] reg_axi_awregion; +wire [AWUSER_WIDTH-1:0] reg_axi_awuser; +wire reg_axi_awvalid; +wire reg_axi_awready; +wire [DATA_WIDTH-1:0] reg_axi_wdata; +wire [STRB_WIDTH-1:0] reg_axi_wstrb; +wire reg_axi_wlast; +wire [WUSER_WIDTH-1:0] reg_axi_wuser; +wire reg_axi_wvalid; +wire reg_axi_wready; +wire [ID_WIDTH-1:0] reg_axi_bid; +wire [1:0] reg_axi_bresp; +wire [BUSER_WIDTH-1:0] reg_axi_buser; +wire reg_axi_bvalid; +wire reg_axi_bready; +wire [ID_WIDTH-1:0] reg_axi_arid; +wire [ADDR_WIDTH-1:0] reg_axi_araddr; +wire [7:0] reg_axi_arlen; +wire [2:0] reg_axi_arsize; +wire [1:0] reg_axi_arburst; +wire reg_axi_arlock; +wire [3:0] reg_axi_arcache; +wire [2:0] reg_axi_arprot; +wire [3:0] reg_axi_arqos; +wire [3:0] reg_axi_arregion; +wire [ARUSER_WIDTH-1:0] reg_axi_aruser; +wire reg_axi_arvalid; +wire reg_axi_arready; +wire [ID_WIDTH-1:0] reg_axi_rid; +wire [DATA_WIDTH-1:0] reg_axi_rdata; +wire [1:0] reg_axi_rresp; +wire reg_axi_rlast; +wire [RUSER_WIDTH-1:0] reg_axi_ruser; +wire reg_axi_rvalid; +wire reg_axi_rready; + +wire [ID_WIDTH-1:0] int_axi_awid; +wire [ADDR_WIDTH-1:0] int_axi_awaddr; +wire [7:0] int_axi_awlen; +wire [2:0] int_axi_awsize; +wire [1:0] int_axi_awburst; +wire int_axi_awlock; +wire [3:0] int_axi_awcache; +wire [2:0] int_axi_awprot; +wire [3:0] int_axi_awregion; +wire int_axi_awvalid; +wire int_axi_awready; +wire [DATA_WIDTH-1:0] int_axi_wdata; +wire [STRB_WIDTH-1:0] int_axi_wstrb; +wire int_axi_wlast; +wire int_axi_wvalid; +wire int_axi_wready; +wire [ID_WIDTH-1:0] int_axi_bid; +wire [1:0] int_axi_bresp; +wire int_axi_bvalid; +wire int_axi_bready; +wire [ID_WIDTH-1:0] int_axi_arid; +wire [ADDR_WIDTH-1:0] int_axi_araddr; +wire [7:0] int_axi_arlen; +wire [2:0] int_axi_arsize; +wire [1:0] int_axi_arburst; +wire int_axi_arlock; +wire [3:0] int_axi_arcache; +wire [2:0] int_axi_arprot; +wire [3:0] int_axi_arregion; +wire int_axi_arvalid; +wire int_axi_arready; +wire [ID_WIDTH-1:0] int_axi_rid; +wire [DATA_WIDTH-1:0] int_axi_rdata; +wire [1:0] int_axi_rresp; +wire int_axi_rlast; +wire int_axi_rvalid; +wire int_axi_rready; + +axi_register # +( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .ID_WIDTH(ID_WIDTH), + .AWUSER_ENABLE(1), + .AWUSER_WIDTH(AWUSER_WIDTH), + .WUSER_ENABLE(1), + .WUSER_WIDTH(WUSER_WIDTH), + .BUSER_ENABLE(1), + .BUSER_WIDTH(BUSER_WIDTH), + .ARUSER_ENABLE(1), + .ARUSER_WIDTH(ARUSER_WIDTH), + .RUSER_ENABLE(1), + .RUSER_WIDTH(RUSER_WIDTH), + .AW_REG_TYPE(AW_REG_TYPE), + .W_REG_TYPE(W_REG_TYPE), + .B_REG_TYPE(B_REG_TYPE), + .AR_REG_TYPE(AR_REG_TYPE), + .R_REG_TYPE(R_REG_TYPE) +) +axi_register_inst ( + .clk(clk), + .rst(~rstn), + + .s_axi_awid(S_AXI_AWID), + .s_axi_awaddr(S_AXI_AWADDR), + .s_axi_awlen(S_AXI_AWLEN), + .s_axi_awsize(S_AXI_AWSIZE), + .s_axi_awburst(S_AXI_AWBURST), + .s_axi_awlock(S_AXI_AWLOCK), + .s_axi_awcache(S_AXI_AWCACHE), + .s_axi_awprot(S_AXI_AWPROT), + .s_axi_awqos(S_AXI_AWQOS), + .s_axi_awuser(S_AXI_AWUSER), + .s_axi_awvalid(S_AXI_AWVALID), + .s_axi_awready(S_AXI_AWREADY), + .s_axi_wdata(S_AXI_WDATA), + .s_axi_wstrb(S_AXI_WSTRB), + .s_axi_wlast(S_AXI_WLAST), + .s_axi_wuser(S_AXI_WUSER), + .s_axi_wvalid(S_AXI_WVALID), + .s_axi_wready(S_AXI_WREADY), + .s_axi_bid(S_AXI_BID), + .s_axi_bresp(S_AXI_BRESP), + .s_axi_buser(S_AXI_BUSER), + .s_axi_bvalid(S_AXI_BVALID), + .s_axi_bready(S_AXI_BREADY), + .s_axi_arid(S_AXI_ARID), + .s_axi_araddr(S_AXI_ARADDR), + .s_axi_arlen(S_AXI_ARLEN), + .s_axi_arsize(S_AXI_ARSIZE), + .s_axi_arburst(S_AXI_ARBURST), + .s_axi_arlock(S_AXI_ARLOCK), + .s_axi_arcache(S_AXI_ARCACHE), + .s_axi_arprot(S_AXI_ARPROT), + .s_axi_arqos(S_AXI_ARQOS), + .s_axi_aruser(S_AXI_ARUSER), + .s_axi_arvalid(S_AXI_ARVALID), + .s_axi_arready(S_AXI_ARREADY), + .s_axi_rid(S_AXI_RID), + .s_axi_rdata(S_AXI_RDATA), + .s_axi_rresp(S_AXI_RRESP), + .s_axi_rlast(S_AXI_RLAST), + .s_axi_ruser(S_AXI_RUSER), + .s_axi_rvalid(S_AXI_RVALID), + .s_axi_rready(S_AXI_RREADY), + + .m_axi_awid(reg_axi_awid), + .m_axi_awaddr(reg_axi_awaddr), + .m_axi_awlen(reg_axi_awlen), + .m_axi_awsize(reg_axi_awsize), + .m_axi_awburst(reg_axi_awburst), + .m_axi_awlock(reg_axi_awlock), + .m_axi_awcache(reg_axi_awcache), + .m_axi_awprot(reg_axi_awprot), + .m_axi_awqos(reg_axi_awqos), + .m_axi_awuser(reg_axi_awuser), + .m_axi_awvalid(reg_axi_awvalid), + .m_axi_awready(reg_axi_awready), + .m_axi_wdata(reg_axi_wdata), + .m_axi_wstrb(reg_axi_wstrb), + .m_axi_wlast(reg_axi_wlast), + .m_axi_wuser(reg_axi_wuser), + .m_axi_wvalid(reg_axi_wvalid), + .m_axi_wready(reg_axi_wready), + .m_axi_bid(reg_axi_bid), + .m_axi_bresp(reg_axi_bresp), + .m_axi_buser(reg_axi_buser), + .m_axi_bvalid(reg_axi_bvalid), + .m_axi_bready(reg_axi_bready), + .m_axi_arid(reg_axi_arid), + .m_axi_araddr(reg_axi_araddr), + .m_axi_arlen(reg_axi_arlen), + .m_axi_arsize(reg_axi_arsize), + .m_axi_arburst(reg_axi_arburst), + .m_axi_arlock(reg_axi_arlock), + .m_axi_arcache(reg_axi_arcache), + .m_axi_arprot(reg_axi_arprot), + .m_axi_arqos(reg_axi_arqos), + .m_axi_aruser(reg_axi_aruser), + .m_axi_arvalid(reg_axi_arvalid), + .m_axi_arready(reg_axi_arready), + .m_axi_rid(reg_axi_rid), + .m_axi_rdata(reg_axi_rdata), + .m_axi_rresp(reg_axi_rresp), + .m_axi_rlast(reg_axi_rlast), + .m_axi_ruser(reg_axi_ruser), + .m_axi_rvalid(reg_axi_rvalid), + .m_axi_rready(reg_axi_rready) +); + +axi_interconnect #( + .S_COUNT(1), + .M_COUNT(1), + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .ID_WIDTH(ID_WIDTH), + .AWUSER_ENABLE(1), + .AWUSER_WIDTH(AWUSER_WIDTH), + .WUSER_ENABLE(1), + .WUSER_WIDTH(WUSER_WIDTH), + .BUSER_ENABLE(1), + .BUSER_WIDTH(BUSER_WIDTH), + .ARUSER_ENABLE(1), + .ARUSER_WIDTH(ARUSER_WIDTH), + .RUSER_ENABLE(1), + .RUSER_WIDTH(RUSER_WIDTH), + .FORWARD_ID(1), + .M_REGIONS(1), + .M_BASE_ADDR({RAM_BASE_ADDRESS}), + .M_ADDR_WIDTH({RAM_WIDTH}), + .M_CONNECT_READ(1'b1), + .M_CONNECT_WRITE(1'b1), + .M_SECURE(1'b0) +) +axi_interconnect_inst ( + .clk(clk), + .rst(~rstn), + + .s_axi_awid(reg_axi_awid), + .s_axi_awaddr(reg_axi_awaddr), + .s_axi_awlen(reg_axi_awlen), + .s_axi_awsize(reg_axi_awsize), + .s_axi_awburst(reg_axi_awburst), + .s_axi_awlock(reg_axi_awlock), + .s_axi_awcache(reg_axi_awcache), + .s_axi_awprot(reg_axi_awprot), + .s_axi_awqos(reg_axi_awqos), + .s_axi_awuser(reg_axi_awuser), + .s_axi_awvalid(reg_axi_awvalid), + .s_axi_awready(reg_axi_awready), + .s_axi_wdata(reg_axi_wdata), + .s_axi_wstrb(reg_axi_wstrb), + .s_axi_wlast(reg_axi_wlast), + .s_axi_wuser(reg_axi_wuser), + .s_axi_wvalid(reg_axi_wvalid), + .s_axi_wready(reg_axi_wready), + .s_axi_bid(reg_axi_bid), + .s_axi_bresp(reg_axi_bresp), + .s_axi_buser(reg_axi_buser), + .s_axi_bvalid(reg_axi_bvalid), + .s_axi_bready(reg_axi_bready), + .s_axi_arid(reg_axi_arid), + .s_axi_araddr(reg_axi_araddr), + .s_axi_arlen(reg_axi_arlen), + .s_axi_arsize(reg_axi_arsize), + .s_axi_arburst(reg_axi_arburst), + .s_axi_arlock(reg_axi_arlock), + .s_axi_arcache(reg_axi_arcache), + .s_axi_arprot(reg_axi_arprot), + .s_axi_arqos(reg_axi_arqos), + .s_axi_aruser(reg_axi_aruser), + .s_axi_arvalid(reg_axi_arvalid), + .s_axi_arready(reg_axi_arready), + .s_axi_rid(reg_axi_rid), + .s_axi_rdata(reg_axi_rdata), + .s_axi_rresp(reg_axi_rresp), + .s_axi_rlast(reg_axi_rlast), + .s_axi_ruser(reg_axi_ruser), + .s_axi_rvalid(reg_axi_rvalid), + .s_axi_rready(reg_axi_rready), + + .m_axi_awid(int_axi_awid), + .m_axi_awaddr(int_axi_awaddr), + .m_axi_awlen(int_axi_awlen), + .m_axi_awsize(int_axi_awsize), + .m_axi_awburst(int_axi_awburst), + .m_axi_awlock(int_axi_awlock), + .m_axi_awcache(int_axi_awcache), + .m_axi_awprot(int_axi_awprot), + .m_axi_awregion(int_axi_awregion), + .m_axi_awvalid(int_axi_awvalid), + .m_axi_awready(int_axi_awready), + .m_axi_wdata(int_axi_wdata), + .m_axi_wstrb(int_axi_wstrb), + .m_axi_wlast(int_axi_wlast), + .m_axi_wvalid(int_axi_wvalid), + .m_axi_wready(int_axi_wready), + .m_axi_bid(int_axi_bid), + .m_axi_bresp(int_axi_bresp), + .m_axi_bvalid(int_axi_bvalid), + .m_axi_bready(int_axi_bready), + .m_axi_arid(int_axi_arid), + .m_axi_araddr(int_axi_araddr), + .m_axi_arlen(int_axi_arlen), + .m_axi_arsize(int_axi_arsize), + .m_axi_arburst(int_axi_arburst), + .m_axi_arlock(int_axi_arlock), + .m_axi_arcache(int_axi_arcache), + .m_axi_arprot(int_axi_arprot), + .m_axi_arregion(int_axi_arregion), + .m_axi_arvalid(int_axi_arvalid), + .m_axi_arready(int_axi_arready), + .m_axi_rid(int_axi_rid), + .m_axi_rdata(int_axi_rdata), + .m_axi_rresp(int_axi_rresp), + .m_axi_rlast(int_axi_rlast), + .m_axi_rvalid(int_axi_rvalid), + .m_axi_rready(int_axi_rready) +); + +axi_ram #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(RAM_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .ID_WIDTH(ID_WIDTH), + .PIPELINE_OUTPUT(RAM_PIPELINE_OUTPUT) +) +axi_ram_inst ( + .clk(clk), + .rst(~rstn), + + .s_axi_awid(int_axi_awid), + .s_axi_awaddr(int_axi_awaddr[RAM_WIDTH-1:0]), + .s_axi_awlen(int_axi_awlen), + .s_axi_awsize(int_axi_awsize), + .s_axi_awburst(int_axi_awburst), + .s_axi_awlock(int_axi_awlock), + .s_axi_awcache(int_axi_awcache), + .s_axi_awprot(int_axi_awprot), + .s_axi_awvalid(int_axi_awvalid), + .s_axi_awready(int_axi_awready), + .s_axi_wdata(int_axi_wdata), + .s_axi_wstrb(int_axi_wstrb), + .s_axi_wlast(int_axi_wlast), + .s_axi_wvalid(int_axi_wvalid), + .s_axi_wready(int_axi_wready), + .s_axi_bid(int_axi_bid), + .s_axi_bresp(int_axi_bresp), + .s_axi_bvalid(int_axi_bvalid), + .s_axi_bready(int_axi_bready), + .s_axi_arid(int_axi_arid), + .s_axi_araddr(int_axi_araddr[RAM_WIDTH-1:0]), + .s_axi_arlen(int_axi_arlen), + .s_axi_arsize(int_axi_arsize), + .s_axi_arburst(int_axi_arburst), + .s_axi_arlock(int_axi_arlock), + .s_axi_arcache(int_axi_arcache), + .s_axi_arprot(int_axi_arprot), + .s_axi_arvalid(int_axi_arvalid), + .s_axi_arready(int_axi_arready), + .s_axi_rid(int_axi_rid), + .s_axi_rdata(int_axi_rdata), + .s_axi_rresp(int_axi_rresp), + .s_axi_rlast(int_axi_rlast), + .s_axi_rvalid(int_axi_rvalid), + .s_axi_rready(int_axi_rready) +); + +`ifdef COCOTB_SIM +initial begin + $dumpfile ("waveforms.vcd"); + $dumpvars; +end +`endif + +endmodule diff --git a/tests/test_cases/test_axi4/Makefile b/tests/test_cases/test_axi4/Makefile new file mode 100644 index 00000000..46a73996 --- /dev/null +++ b/tests/test_cases/test_axi4/Makefile @@ -0,0 +1,3 @@ +include ../../designs/axi4_ram/Makefile + +MODULE = test_axi4 diff --git a/tests/test_cases/test_axi4/test_axi4.py b/tests/test_cases/test_axi4/test_axi4.py new file mode 100644 index 00000000..31ad2f9b --- /dev/null +++ b/tests/test_cases/test_axi4/test_axi4.py @@ -0,0 +1,532 @@ +#!/usr/bin/env python +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause + +"""Test to demonstrate functionality of the AXI4 master interface""" + +from random import randint, randrange, getrandbits + +import cocotb +from cocotb.clock import Clock +from cocotb.drivers.amba import (AXIBurst, AXI4LiteMaster, AXI4Master, + AXIProtocolError, AXIReadBurstLengthMismatch, + AXIxRESP) +from cocotb.regression import TestFactory +from cocotb.result import TestFailure +from cocotb.triggers import ClockCycles, Combine, Join, RisingEdge + + +CLK_PERIOD = (10, "ns") +AXI_PREFIX = "S_AXI" + + +def get_parameters(dut): + address_width = dut.ADDR_WIDTH.value // 8 + data_width = dut.DATA_WIDTH.value // 8 + ram_start = dut.RAM_BASE_ADDRESS.value + ram_stop = ram_start + 2**dut.RAM_WIDTH.value + return address_width, data_width, ram_start, ram_stop + + +def add_wstrb_mask(data_width, previous_value, write_value, wstrb): + result = 0 + for i in range(data_width): + source = write_value if wstrb & (1 << i) else previous_value + result |= (source & (0xff << (i * 8))) + + return result + + +def compare_read_values(expected_values, read_values, burst, burst_length, + address): + for i, (expected, read) in enumerate(zip(expected_values, read_values)): + if expected != read: + raise TestFailure( + "Read {:#x} at beat {}/{} of {} burst with starting address " + "{:#x}, but was expecting {:#x})" + .format(read.integer, i + 1, burst_length, burst.name, address, + expected)) + + +async def setup_dut(dut): + cocotb.fork(Clock(dut.clk, *CLK_PERIOD).start()) + dut.rstn <= 0 + await ClockCycles(dut.clk, 2) + dut.rstn <= 1 + await ClockCycles(dut.clk, 2) + + +async def test_single_beat(dut, driver, address_latency, data_latency): + """Test single read/write""" + + axim = driver(dut, AXI_PREFIX, dut.clk) + _, data_width, ram_start, ram_stop = get_parameters(dut) + + await setup_dut(dut) + + address = randrange(ram_start, ram_stop, data_width) + write_value = randrange(0, 2**(data_width * 8)) + strobe = randrange(1, 2**data_width) + + previous_value = await axim.read(address) + await axim.write(address, write_value, byte_enable=strobe, + address_latency=address_latency, + data_latency=data_latency) + read_value = await axim.read(address) + + if isinstance(read_value, list): + previous_value = previous_value[0] + read_value = read_value[0] + + expected_value = add_wstrb_mask(data_width, previous_value.integer, + write_value, strobe) + + if read_value != expected_value: + raise TestFailure("Read {:#x} from {:#x} but was expecting {:#x} " + "({:#x} with {:#x} as strobe and {:#x} as previous " + "value)" + .format(read_value.integer, address, expected_value, + write_value, strobe, previous_value)) + + +async def test_incr_burst(dut, size, return_rresp): + """Test burst reads/writes""" + + axim = AXI4Master(dut, AXI_PREFIX, dut.clk) + _, data_width, ram_start, ram_stop = get_parameters(dut) + size = size if size else data_width + + await setup_dut(dut) + + burst_length = randrange(2, 256) + base = randrange(ram_start, ram_stop, 4096) + offset = randrange(0, 4096 - (burst_length - 1) * size, size) + address = base + offset + write_values = [randrange(0, 2**(size * 8)) for i in range(burst_length)] + + # Make strobe one item less than data, to test also that driver behavior + strobes = [randrange(0, 2**size) for i in range(len(write_values) - 1)] + + previous_values = await axim.read(address, len(write_values), size=size) + await axim.write(address, write_values, byte_enable=strobes, size=size) + read_values = await axim.read(address, len(write_values), + return_rresp=return_rresp, size=size) + + if return_rresp: + read_values, rresp_list = zip(*read_values) + for i, rresp in enumerate(rresp_list): + if rresp is not AXIxRESP.OKAY: + raise TestFailure( + "Read at beat {}/{} with starting address {:#x} failed " + "with RRESP {} ({})" + .format(i + 1, len(rresp_list), address, rresp.value, + rresp.name)) + + strobes += [strobes[-1]] + expected_values = \ + [add_wstrb_mask(size, previous_value, write_value, wstrb) + for previous_value, write_value, wstrb + in zip(previous_values, write_values, strobes)] + + for i in range(len(read_values)): + if expected_values[i] != read_values[i]: + raise TestFailure( + "Read {:#x} at beat {}/{} with starting address {:#x}, but " + "was expecting {:#x} ({:#x} with {:#x} as strobe and {:#x} as " + "previous value)" + .format(read_values[i].integer, i + 1, burst_length, address, + expected_values[i], write_values[i], strobes[i], + previous_values[i].integer)) + + +async def test_fixed_wrap_burst(dut, size, burst_length=16): + """Test FIXED and WRAP read/writes""" + + axim = AXI4Master(dut, AXI_PREFIX, dut.clk) + _, data_width, ram_start, ram_stop = get_parameters(dut) + size = size if size else data_width + + await setup_dut(dut) + + base = randrange(ram_start, ram_stop, 4096) + offset = randrange(0, 4096 - (burst_length - 1) * size, size) + address = base + offset + + for burst in (AXIBurst.FIXED, AXIBurst.WRAP): + + write_values = \ + [randrange(0, 2**(size * 8)) for i in range(burst_length)] + + await axim.write(address, write_values, burst=burst, size=size) + + if burst is AXIBurst.FIXED: + # A FIXED write on a memory is like writing the last element, + # reading it with a FIXED burst returns always the same value. + expected_values = [write_values[-1]] * burst_length + else: + # Regardless of the boundary, reading with a WRAP from the same + # address with the same length returns the same sequence. + # This RAM implementation does not support WRAP bursts and treats + # them as INCR. + expected_values = write_values + + read_values = await axim.read(address, burst_length, burst=burst, + size=size) + + compare_read_values(expected_values, read_values, burst, burst_length, + address) + + +@cocotb.test() +async def test_narrow_burst(dut): + """Test that narrow writes and full reads match""" + + axim = AXI4Master(dut, AXI_PREFIX, dut.clk) + _, data_width, ram_start, ram_stop = get_parameters(dut) + + await setup_dut(dut) + + burst_length = (randrange(2, 256) // 2) * 2 + address = ram_start + write_values = \ + [randrange(0, 2**(data_width * 8 // 2)) for i in range(burst_length)] + + await axim.write(address, write_values, size=data_width // 2) + + read_values = await axim.read(address, burst_length // 2) + + expected_values = [write_values[i + 1] << 16 | write_values[i] + for i in range(0, burst_length // 2, 2)] + + compare_read_values(expected_values, read_values, AXIBurst.INCR, + burst_length // 2, address) + + +async def test_unaligned(dut, size, burst): + """Test that unaligned read and writes are performed correctly""" + + def get_random_words(length, size, num_bits): + r_bytes = getrandbits(num_bits).to_bytes(size * length, 'little') + r_words = [r_bytes[i * size:(i + 1) * size] for i in range(length)] + r_ints = [int.from_bytes(w, 'little') for w in r_words] + return r_bytes, r_ints + + axim = AXI4Master(dut, AXI_PREFIX, dut.clk) + _, data_width, ram_start, ram_stop = get_parameters(dut) + size = size if size else data_width + + await setup_dut(dut) + + burst_length = randrange(2, 16 if burst is AXIBurst.FIXED else 256) + + if burst is AXIBurst.FIXED: + address = randrange(ram_start, ram_stop, size) + else: + base = randrange(ram_start, ram_stop, 4096) + offset = randrange(0, 4096 - (burst_length - 1) * size, size) + address = base + offset + + unaligned_addr = address + randrange(1, size) + shift = unaligned_addr % size + + # Write aligned, read unaligned + write_bytes, write_values = \ + get_random_words(burst_length, size, size * burst_length * 8) + + await axim.write(address, write_values, burst=burst, size=size) + read_values = await axim.read(unaligned_addr, burst_length, burst=burst, + size=size) + + if burst is AXIBurst.FIXED: + mask = 2**((size - shift) * 8) - 1 + expected_values = \ + [(write_values[-1] >> (shift * 8)) & mask] * burst_length + else: + expected_bytes = write_bytes[shift:] + expected_words = [expected_bytes[i * size:(i + 1) * size] + for i in range(burst_length)] + expected_values = [int.from_bytes(w, 'little') for w in expected_words] + + compare_read_values(expected_values, read_values, burst, burst_length, + unaligned_addr) + + # Write unaligned, read aligned + write_bytes, write_values = \ + get_random_words(burst_length, size, + (size * burst_length - shift) * 8) + + first_word = randrange(0, 2**(size * 8)) + await axim.write(address, first_word, burst=burst, size=size) + await axim.write(unaligned_addr, write_values, burst=burst, size=size) + read_values = await axim.read(address, burst_length, burst=burst, + size=size) + + mask_low = 2**(shift * 8) - 1 + mask_high = (2**((size - shift) * 8) - 1) << (shift * 8) + + if burst is AXIBurst.FIXED: + last_val = \ + first_word & mask_low | (write_values[-1] << shift * 8) & mask_high + expected_values = [last_val] * burst_length + else: + first_bytes = (first_word & mask_low).to_bytes(shift, 'little') + expected_bytes = first_bytes + write_bytes + expected_words = [expected_bytes[i * size:(i + 1) * size] + for i in range(burst_length - 1)] + expected_values = [int.from_bytes(w, 'little') for w in expected_words] + + compare_read_values(expected_values, read_values, burst, burst_length, + unaligned_addr) + + +async def test_unmapped(dut, driver): + """Check whether a read/write over an unmapped address returns a DECERR""" + + axim = driver(dut, AXI_PREFIX, dut.clk) + address_width, data_width, ram_start, ram_stop = get_parameters(dut) + + await setup_dut(dut) + + for rw in ("Read", "Write"): + try: + address = randrange(0, ram_start - data_width, data_width) + except ValueError: + # If the RAM is mapped at the beginning of the address space, use + # an address after the end of the RAM + address = randrange(ram_stop, 2**(address_width * 8) - data_width, + data_width) + + try: + if rw == "Read": + try: + await axim.read(address, length=2) + except TypeError: + await axim.read(address) + else: + write_data = [randrange(0, 2**(data_width * 8))] * 2 + try: + await axim.write(address, write_data) + except ValueError: + await axim.write(address, write_data[0]) + + raise TestFailure("{} at {:#x} should have failed, but did not" + .format(rw, address)) + + except AXIProtocolError as e: + if e.xresp is not AXIxRESP.DECERR: + raise TestFailure("Was expecting DECERR, but received {}" + .format(e.xresp.name)) + + +@cocotb.test() +async def test_illegal_operations(dut): + """Test whether illegal operations are correctly refused by the driver""" + + axim = AXI4Master(dut, AXI_PREFIX, dut.clk) + _, data_width, ram_start, _ = get_parameters(dut) + + illegal_operations = ( + (AXIBurst.INCR, -1, None), + (AXIBurst.INCR, 0, None), + (AXIBurst.FIXED, 17, None), + (AXIBurst.INCR, 257, None), + (AXIBurst.WRAP, 3, None), + (AXIBurst.INCR, 4, -1), + (AXIBurst.INCR, 4, data_width - 1), + (AXIBurst.INCR, 4, data_width * 2), + ) + + await setup_dut(dut) + + for rw in ("Read", "Write"): + for burst, length, size in illegal_operations: + try: + if rw == "Read": + await axim.read(ram_start, length, size=size, burst=burst) + else: + values = [randrange(0, 2**(data_width * 8)) + for i in range(length)] + await axim.write(ram_start, values, size=size, burst=burst) + + raise TestFailure( + "{} with length={}, size={} and burst={} has been " + "performed by the driver, but it is an illegal " + "operation".format(rw, length, size, burst.name)) + + except ValueError: + pass + + +@cocotb.test() +async def test_4kB_boundary(dut): + """ + Check that the driver raises an error when trying to cross a 4kB boundary + """ + + axim = AXI4Master(dut, AXI_PREFIX, dut.clk) + _, data_width, ram_start, ram_stop = get_parameters(dut) + + await setup_dut(dut) + + for rw in ("Read", "Write"): + burst_length = randint(2, 256) + base = randrange(ram_start, ram_stop - 4096, 4096) + offset = randrange(-(burst_length - 1) * data_width, 0, data_width) + address = base + offset + write_values = \ + [randrange(0, 2**(data_width * 8)) for i in range(burst_length)] + + try: + if rw == "Read": + await axim.read(address, burst_length) + else: + await axim.write(address, write_values) + + raise TestFailure( + "{} from {:#x} to {:#x} crosses a 4kB boundary, but the " + "driver allowed it" + .format(rw, address, + address + (burst_length - 1) * data_width)) + except ValueError: + pass + + +async def test_simultaneous(dut, sync, num=5): + """Test simultaneous reads/writes""" + + axim = AXI4Master(dut, AXI_PREFIX, dut.clk) + _, data_width, ram_start, _ = get_parameters(dut) + + await setup_dut(dut) + + # Avoid crossing the 4kB boundary by using just the first 4kB block + base_address = randrange(ram_start, + ram_start + 4096 - 2 * num * data_width, + data_width) + + # Clear the memory cells + await axim.write(base_address, [0] * num) + + addresses = [base_address + i * data_width for i in range(num)] + write_values = [randrange(0, 2**(data_width * 8)) for i in range(num)] + + writers = [axim.write(address, value, sync=sync) + for address, value in zip(addresses, write_values)] + + await Combine(*writers) + + readers = [cocotb.fork(axim.read(address, sync=sync)) + for address in addresses] + + dummy_addrs = [base_address + (num + i) * data_width for i in range(num)] + dummy_writers = [cocotb.fork(axim.write(address, value, sync=sync)) + for address, value in zip(dummy_addrs, write_values)] + + read_values = [] + for reader in readers: + read_values.append((await Join(reader))[0]) + await Combine(*[Join(writer) for writer in dummy_writers]) + + for i, (written, read) in enumerate(zip(write_values, read_values)): + if written != read: + raise TestFailure("#{}: wrote {:#x} but read back {:#x}" + .format(i, written, read.integer)) + + +@cocotb.test() +async def test_axi4lite_write_burst(dut): + """Test that write bursts are correctly refused by the AXI4-Lite driver""" + + axim = AXI4LiteMaster(dut, AXI_PREFIX, dut.clk) + _, data_width, ram_start, _ = get_parameters(dut) + length = randint(2, 16) + + await setup_dut(dut) + + try: + await axim.write(ram_start, [randrange(0, 2**(data_width * 8)) + for i in range(length)]) + + raise TestFailure( + "Write with length={} has been performed by the driver, but " + "burst operations are not allowed on AXI4-Lite".format(length)) + + except ValueError: + pass + + +@cocotb.test() +async def test_read_length_mismatch(dut): + """Test that a mismatch in the read data length is correctly identified""" + + class AXI4Master_unmanaged_arlen(AXI4Master): + _signals = [signal for signal in AXI4Master._signals + if signal != "ARLEN"] + + axim = AXI4Master_unmanaged_arlen(dut, AXI_PREFIX, dut.clk) + _, data_width, ram_start, ram_stop = get_parameters(dut) + length = randint(2, 16) + + burst_length = randint(2, 255) + base = randrange(ram_start, ram_stop, 4096) + offset = randrange(0, 4096 - (burst_length - 1) * data_width, data_width) + address = base + offset + + await setup_dut(dut) + + for length_delta in (-1, 1): + try: + # Override the driver's ARLEN value, forcing a wrong one + await RisingEdge(dut.clk) + arlen = burst_length - 1 + length_delta + getattr(dut, AXI_PREFIX + '_ARLEN') <= arlen + await axim.read(address, burst_length) + + raise TestFailure("Mismatch between ARLEN value ({}) and number " + "of read words ({}), but the driver did not " + "raise an exception" + .format(arlen, burst_length)) + + except AXIReadBurstLengthMismatch: + pass + + +single_beat = TestFactory(test_single_beat) +single_beat.add_option('driver', (AXI4Master, AXI4LiteMaster)) +single_beat.add_option('address_latency', (0,)) +single_beat.add_option('data_latency', (0,)) +single_beat.generate_tests() + +single_beat_with_latency = TestFactory(test_single_beat) +single_beat_with_latency.add_option('driver', (AXI4Master,)) +single_beat_with_latency.add_option('address_latency', (0, 5)) +single_beat_with_latency.add_option('data_latency', (1, 10)) +single_beat_with_latency.generate_tests(postfix="_latency") + +incr_burst = TestFactory(test_incr_burst) +incr_burst.add_option('return_rresp', (True, False)) +incr_burst.add_option('size', (None,)) +incr_burst.generate_tests() + +incr_burst_size = TestFactory(test_incr_burst) +incr_burst_size.add_option('return_rresp', (False,)) +incr_burst_size.add_option('size', (1, 2)) +incr_burst_size.generate_tests(postfix="_size") + +fixed_wrap_burst = TestFactory(test_fixed_wrap_burst) +fixed_wrap_burst.add_option('size', (None, 1, 2)) +fixed_wrap_burst.generate_tests() + +unaligned = TestFactory(test_unaligned) +unaligned.add_option('size', (None, 2)) +unaligned.add_option('burst', (AXIBurst.FIXED, AXIBurst.INCR)) +unaligned.generate_tests() + +unmapped = TestFactory(test_unmapped) +unmapped.add_option('driver', [AXI4Master, AXI4LiteMaster]) +unmapped.generate_tests() + +simultaneous = TestFactory(test_simultaneous) +simultaneous.add_option('sync', [True, False]) +simultaneous.generate_tests() From 7873b2aac2da3479934fa5323dc456c6fd55d5d6 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 22 Nov 2020 11:46:21 -0600 Subject: [PATCH 2579/2656] Increase coverage in xunit_reporter.py (#2200) * Add test for a test failure that isn't expected * Remove dead code in xunit_reporter.py Co-authored-by: Eric Wieser --- cocotb/xunit_reporter.py | 58 ------------------- .../source/newsfragments/2200.removal.rst | 1 + tests/test_cases/test_failure/Makefile | 14 +++++ tests/test_cases/test_failure/test_failure.py | 15 +++++ 4 files changed, 30 insertions(+), 58 deletions(-) create mode 100644 documentation/source/newsfragments/2200.removal.rst create mode 100644 tests/test_cases/test_failure/Makefile create mode 100644 tests/test_cases/test_failure/test_failure.py diff --git a/cocotb/xunit_reporter.py b/cocotb/xunit_reporter.py index f0749ba4..cb08e487 100644 --- a/cocotb/xunit_reporter.py +++ b/cocotb/xunit_reporter.py @@ -28,40 +28,6 @@ from xml.etree.ElementTree import Element, SubElement import xml.etree.ElementTree as ET -import mmap -from io import StringIO - -TRUNCATE_LINES = 100 - - -# file from http://stackoverflow.com/questions/136168/get-last-n-lines-of-a-file-with-python-similar-to-tail -class File(StringIO): - - def countlines(self): - buf = mmap.mmap(self.fileno(), 0) - lines = 0 - while buf.readline(): - lines += 1 - return lines - - def head(self, lines_2find=1): - self.seek(0) # Rewind file - return [self.next() for x in range(lines_2find)] - - def tail(self, lines_2find=1): - self.seek(0, 2) # go to end of file - bytes_in_file = self.tell() - lines_found, total_bytes_scanned = 0, 0 - while (lines_2find+1 > lines_found and - bytes_in_file > total_bytes_scanned): - byte_block = min(1024, bytes_in_file-total_bytes_scanned) - self.seek(-(byte_block+total_bytes_scanned), 2) - total_bytes_scanned += byte_block - lines_found += self.read(1024).count('\n') - self.seek(-total_bytes_scanned, 2) - line_list = list(self.readlines()) - return line_list[-lines_2find:] - class XUnitReporter: @@ -85,30 +51,6 @@ def add_property(self, testsuite=None, **kwargs): self.last_property = SubElement(testsuite, "property", **kwargs) return self.last_property - def update_testsuite(self, testsuite=None, **kwargs): - if testsuite is None: - testsuite = self.last_testsuite - for k in kwargs: - testsuite.set(k, str(kwargs[k])) - - def update_testsuites(self, **kwargs): - for k in kwargs: - self.results.set(k, str(kwargs[k])) - - def add_log(self, logfile, testcase=None): - if testcase is None: - testcase = self.last_testcase - log = SubElement(testcase, "system-out") - f = File(logfile, 'r+') - lines = f.countlines() - if lines > (TRUNCATE_LINES * 2): - head = f.head(TRUNCATE_LINES) - tail = f.tail(TRUNCATE_LINES) - log.text = "".join(head + list("[...truncated %d lines...]\n" % - ((lines - (TRUNCATE_LINES*2)))) + tail) - else: - log.text = "".join(f.readlines()) - def add_failure(self, testcase=None, **kwargs): if testcase is None: testcase = self.last_testcase diff --git a/documentation/source/newsfragments/2200.removal.rst b/documentation/source/newsfragments/2200.removal.rst new file mode 100644 index 00000000..a8ca2546 --- /dev/null +++ b/documentation/source/newsfragments/2200.removal.rst @@ -0,0 +1 @@ +The undocumented class ``cocotb.xunit_reporter.File`` has been removed. diff --git a/tests/test_cases/test_failure/Makefile b/tests/test_cases/test_failure/Makefile new file mode 100644 index 00000000..e1678430 --- /dev/null +++ b/tests/test_cases/test_failure/Makefile @@ -0,0 +1,14 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause + +MODULE := test_failure + +# ensure the test runs, squash any error code, and ensure a failing test was reported +.PHONY: override_for_this_test +override_for_this_test: + -$(MAKE) all + $(call check_for_results_file) + test $$(../../../bin/combine_results.py | grep "Failure in testsuite" | wc -l) -eq 1 && rm -f results.xml + +include ../../designs/sample_module/Makefile diff --git a/tests/test_cases/test_failure/test_failure.py b/tests/test_cases/test_failure/test_failure.py new file mode 100644 index 00000000..77b2943d --- /dev/null +++ b/tests/test_cases/test_failure/test_failure.py @@ -0,0 +1,15 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause +""" +Tests the failure path for tests in the regression.py and xunit_reporter.py. + +The Makefile in this folder is specially set up to squash any error code due +to a failing test and ensures the failing test is reported properly. +""" +import cocotb + + +@cocotb.test() +async def test_fail(): + assert False From 67c17f9ffd9665a1a5baaa8652c5ec9c165e4b43 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sun, 22 Nov 2020 23:40:37 +0100 Subject: [PATCH 2580/2656] Add gcov/gcovr to GHA for C++ code coverage collection (#2155) Add gcov to GHA for C++ code coverage collection Co-authored-by: Marlon James --- .github/workflows/regression-tests.yml | 2 +- cocotb_build_libs.py | 10 ++++++++++ tox.ini | 3 ++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml index db840fc6..37e091b1 100644 --- a/.github/workflows/regression-tests.yml +++ b/.github/workflows/regression-tests.yml @@ -294,4 +294,4 @@ jobs: if: steps.windowstesting.outcome == 'success' || steps.unixtesting.outcome == 'success' run: | pip install codecov - codecov + codecov --gcov-args='-rl' diff --git a/cocotb_build_libs.py b/cocotb_build_libs.py index 887f717f..d815e97f 100755 --- a/cocotb_build_libs.py +++ b/cocotb_build_libs.py @@ -146,6 +146,16 @@ def run(self): super().run() + def build_extension(self, ext): + """Build each extension in its own temp directory to make gcov happy. + + A normal PEP 517 install still works as the temp directories are discarded anyway. + """ + old_build_temp = self.build_temp + self.build_temp = os.path.join(self.build_temp, ext.name) + super().build_extension(ext) + self.build_temp = old_build_temp + # Needed for Windows to not assume python module (generate interface in def file) def get_export_symbols(self, ext): return None diff --git a/tox.ini b/tox.ini index c598ada1..779c9d91 100644 --- a/tox.ini +++ b/tox.ini @@ -17,7 +17,8 @@ platform = windows: win32 setenv = - CFLAGS = -Werror -Wno-deprecated-declarations + CFLAGS = -Werror -Wno-deprecated-declarations -g --coverage + LDFLAGS = --coverage CXXFLAGS = -Werror COCOTB_LIBRARY_COVERAGE = 1 From 4d78bc87da2129ae62d195a24806c75dfc156a14 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 23 Nov 2020 22:58:45 +0100 Subject: [PATCH 2581/2656] Make docstring for BinaryValue.is_resolvable clearer (#2219) Make docstring for BinaryValue.is_resolvable clearer Refs #2215. Co-authored-by: Kaleb Barrett --- cocotb/binary.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 35269c3f..cba0eb00 100755 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -327,8 +327,16 @@ def signed_integer(self, val): get_value_signed = signed_integer.fget @property - def is_resolvable(self): - """Does the value contain any ``X``'s? Inquiring minds want to know.""" + def is_resolvable(self) -> bool: + """ + Return whether the value contains only resolvable (i.e. no "unknown") bits. + + By default the values ``X``, ``Z``, ``U`` and ``W`` are considered unresolvable. + This can be configured with :envvar:`COCOTB_RESOLVE_X`. + + This is similar to the SystemVerilog Assertion ``$isunknown`` system function + or the VHDL function ``is_x`` (with an inverted meaning). + """ return not any(char in self._str for char in BinaryValue._resolve_to_error) @property From 75c694cee7327b2c5546ae8745fdc63e868087b8 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 20 Nov 2020 22:44:53 +0100 Subject: [PATCH 2582/2656] gitpod: Run verilator with trace, improve general usability Injecting commands into the shell history now works correctly. Only the VNC port is offered to open a preview or browser tab with. Configure the Theia IDE to show leading and trailing whitespace. --- .gitpod.yml | 22 +++++++++++++++++----- .theia/settings.json | 4 ++++ 2 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 .theia/settings.json diff --git a/.gitpod.yml b/.gitpod.yml index 31f9f761..b0caa180 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -12,12 +12,24 @@ tasks: gp open ../hdl/dff.vhdl && gp open Makefile && gp open dff_cocotb.py && - make SIM=icarus + make SIM=icarus && + EXTRA_ARGS="-CFLAGS -DVM_TRACE_FST=1 --trace-fst --trace-structs" make SIM=verilator && + gtkwave dump.fst & command: > - history -s make SIM=cvc && - history -s make SIM=ghdl TOPLEVEL_LANG=vhdl && - history -s make SIM=verilator && - history -s make SIM=icarus + cd /workspace/cocotb/examples/dff/tests ; + history -s make SIM=cvc ; + history -s make SIM=ghdl TOPLEVEL_LANG=vhdl ; + history -s make SIM=icarus ; + history -s EXTRA_ARGS="-CFLAGS -DVM_TRACE_FST=1 --trace-fst --trace-structs" make SIM=verilator ; + history -s gtkwave dump.fst ; + history -d 3 + +# https://www.gitpod.io/docs/config-ports/ +ports: + - port: 6080 # VNC for e.g. gtkwave + onOpen: notify + - port: 5900 + onOpen: ignore vscode: extensions: diff --git a/.theia/settings.json b/.theia/settings.json new file mode 100644 index 00000000..3f23439e --- /dev/null +++ b/.theia/settings.json @@ -0,0 +1,4 @@ +{ + // show leading and trailing whitespace + "editor.renderWhitespace": "boundary" +} From 79d6c78429628f47cb62010fd073dfd6626cd1cf Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 23 Nov 2020 23:35:17 +0100 Subject: [PATCH 2583/2656] Improve VHPI logging output and some comments (#2148) Improve VHPI logging output and some comments Co-authored-by: Kaleb Barrett --- cocotb/share/lib/vhpi/VhpiCbHdl.cpp | 61 ++++++++++++++------------ cocotb/share/lib/vhpi/VhpiImpl.cpp | 68 ++++++++++++++--------------- 2 files changed, 66 insertions(+), 63 deletions(-) diff --git a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp index e003d7a1..931bfb9f 100644 --- a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp +++ b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp @@ -39,7 +39,7 @@ extern "C" void handle_vhpi_callback(const vhpiCbDataT *cb_data); VhpiArrayObjHdl::~VhpiArrayObjHdl() { - LOG_DEBUG("Releasing VhpiArrayObjHdl handle at %p", (void *)get_handle()); + LOG_DEBUG("VHPI: Releasing VhpiArrayObjHdl handle at %p", (void *)get_handle()); if (vhpi_release_handle(get_handle())) check_vhpi_error(); } @@ -48,7 +48,7 @@ VhpiObjHdl::~VhpiObjHdl() { /* Don't release handles for pseudo-regions, as they borrow the handle of the containing region */ if (m_type != GPI_GENARRAY) { - LOG_DEBUG("Releasing VhpiObjHdl handle at %p", (void *)get_handle()); + LOG_DEBUG("VHPI: Releasing VhpiObjHdl handle at %p", (void *)get_handle()); if (vhpi_release_handle(get_handle())) check_vhpi_error(); } @@ -68,7 +68,7 @@ VhpiSignalObjHdl::~VhpiSignalObjHdl() if (m_binvalue.value.str) delete [] m_binvalue.value.str; - LOG_DEBUG("Releasing VhpiSignalObjHdl handle at %p", (void *)get_handle()); + LOG_DEBUG("VHPI: Releasing VhpiSignalObjHdl handle at %p", (void *)get_handle()); if (vhpi_release_handle(get_handle())) check_vhpi_error(); } @@ -220,7 +220,7 @@ int VhpiArrayObjHdl::initialise(std::string &name, std::string &fq_name) { bool error = get_range(handle, dim_idx, &m_range_left, &m_range_right); if (error) { - LOG_ERROR("Unable to obtain constraints for an indexable object %s.", fq_name.c_str()); + LOG_ERROR("VHPI: Unable to obtain constraints for an indexable object %s.", fq_name.c_str()); return -1; } @@ -274,7 +274,7 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { return -1; } - LOG_DEBUG("Found %s of format type %s (%d) format object with %d elems buffsize %d size %d", + LOG_DEBUG("VHPI: Found %s of format type %s (%d) format object with %d elems buffsize %d size %d", name.c_str(), ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format), m_value.format, @@ -300,7 +300,7 @@ int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { m_value.bufSize = static_cast(bufSize); m_value.value.str = new vhpiCharT[bufSize]; m_value.numElems = m_num_elems; - LOG_DEBUG("Overriding num_elems to %d", m_num_elems); + LOG_DEBUG("VHPI: Overriding num_elems to %d", m_num_elems); break; } @@ -354,7 +354,7 @@ int VhpiLogicSignalObjHdl::initialise(std::string &name, std::string &fq_name) { m_num_elems = static_cast(vhpi_get(vhpiSizeP, handle)); if (m_num_elems == 0) { - LOG_DEBUG("Null vector... Delete object") + LOG_DEBUG("VHPI: Null vector... Delete object") return -1; } @@ -438,13 +438,17 @@ int VhpiCbHdl::arm_callback() if (!new_hdl) { check_vhpi_error(); LOG_ERROR("VHPI: Unable to register a callback handle for VHPI type %s(%d)", - m_impl->reason_to_string(cb_data.reason), cb_data.reason); + m_impl->reason_to_string(cb_data.reason), cb_data.reason); goto error; } - cbState = (vhpiStateT)vhpi_get(vhpiStateP, new_hdl); - if (vhpiEnable != cbState) { - LOG_ERROR("VHPI: Registered callback isn't enabled! Got %d", cbState); + // don't cast to vhpiStateT immediately because vhpiUndefined is not in the enum + vhpiIntT cbState_raw = vhpi_get(vhpiStateP, new_hdl); + if ((vhpiIntT)vhpiUndefined == cbState_raw) { + LOG_ERROR("VHPI: Registered callback isn't enabled! Got vhpiStateP=vhpiUndefined(%d)", vhpiUndefined); + goto error; + } else if (vhpiEnable != (vhpiStateT)cbState_raw) { + LOG_ERROR("VHPI: Registered callback isn't enabled! Got vhpiStateP=%d", (vhpiStateT)cbState_raw); goto error; } @@ -724,7 +728,7 @@ const char* VhpiSignalObjHdl::get_signal_value_binstr() { switch (m_value.format) { case vhpiRealVal: - LOG_INFO("get_signal_value_binstr not supported for %s", + LOG_INFO("VHPI: get_signal_value_binstr not supported for %s", ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); return ""; default: { @@ -1004,7 +1008,7 @@ VhpiIterator::VhpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator /* GPI_GENARRAY are pseudo-regions and all that should be searched for are the sub-regions */ if (m_parent->get_type() == GPI_GENARRAY && *one2many != vhpiInternalRegions) { - LOG_DEBUG("vhpi_iterator vhpiOneToManyT=%d skipped for GPI_GENARRAY type", *one2many); + LOG_DEBUG("VHPI: vhpi_iterator vhpiOneToManyT=%d skipped for GPI_GENARRAY type", *one2many); continue; } @@ -1013,11 +1017,11 @@ VhpiIterator::VhpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator if (iterator) break; - LOG_DEBUG("vhpi_iterate vhpiOneToManyT=%d returned NULL", *one2many); + LOG_DEBUG("VHPI: vhpi_iterate vhpiOneToManyT=%d returned NULL", *one2many); } if (NULL == iterator) { - LOG_DEBUG("vhpi_iterate return NULL for all relationships on %s (%d) kind:%s", + LOG_DEBUG("VHPI: vhpi_iterate return NULL for all relationships on %s (%d) kind:%s", vhpi_get_str(vhpiCaseNameP, vhpi_hdl), type, vhpi_get_str(vhpiKindStrP, vhpi_hdl)); @@ -1025,7 +1029,7 @@ VhpiIterator::VhpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator return; } - LOG_DEBUG("Created iterator working from scope %d (%s)", + LOG_DEBUG("VHPI: Created iterator working from scope %d (%s)", vhpi_get(vhpiKindP, vhpi_hdl), vhpi_get_str(vhpiKindStrP, vhpi_hdl)); @@ -1087,23 +1091,23 @@ GpiIterator::Status VhpiIterator::next_handle(std::string &name, vhpiCondSigAssignStmtK == vhpi_get(vhpiKindP, obj) || vhpiSimpleSigAssignStmtK == vhpi_get(vhpiKindP, obj) || vhpiSelectSigAssignStmtK == vhpi_get(vhpiKindP, obj))) { - LOG_DEBUG("Skipping %s (%s)", vhpi_get_str(vhpiFullNameP, obj), + LOG_DEBUG("VHPI: Skipping %s (%s)", vhpi_get_str(vhpiFullNameP, obj), vhpi_get_str(vhpiKindStrP, obj)); obj=NULL; continue; } if (obj != NULL) { - LOG_DEBUG("Found an item %s", vhpi_get_str(vhpiFullNameP, obj)); + LOG_DEBUG("VHPI: Found an item %s", vhpi_get_str(vhpiFullNameP, obj)); break; } else { - LOG_DEBUG("vhpi_scan on %d returned NULL", *one2many); + LOG_DEBUG("VHPI: vhpi_scan on vhpiOneToManyT=%d returned NULL", *one2many); } - LOG_DEBUG("End of vhpiOneToManyT=%d iteration", *one2many); + LOG_DEBUG("VHPI: End of vhpiOneToManyT=%d iteration", *one2many); m_iterator = NULL; } else { - LOG_DEBUG("No valid vhpiOneToManyT=%d iterator", *one2many); + LOG_DEBUG("VHPI: No valid vhpiOneToManyT=%d iterator", *one2many); } if (++one2many >= selected->end()) { @@ -1113,7 +1117,7 @@ GpiIterator::Status VhpiIterator::next_handle(std::string &name, /* GPI_GENARRAY are pseudo-regions and all that should be searched for are the sub-regions */ if (obj_type == GPI_GENARRAY && *one2many != vhpiInternalRegions) { - LOG_DEBUG("vhpi_iterator vhpiOneToManyT=%d skipped for GPI_GENARRAY type", *one2many); + LOG_DEBUG("VHPI: vhpi_iterator vhpiOneToManyT=%d skipped for GPI_GENARRAY type", *one2many); continue; } @@ -1122,7 +1126,7 @@ GpiIterator::Status VhpiIterator::next_handle(std::string &name, } while (!obj); if (NULL == obj) { - LOG_DEBUG("No more children, all relationships tested"); + LOG_DEBUG("VHPI: No more children, all relationships have been tested"); return GpiIterator::END; } @@ -1135,7 +1139,7 @@ GpiIterator::Status VhpiIterator::next_handle(std::string &name, return GpiIterator::NOT_NATIVE_NO_NAME; } - LOG_DEBUG("Unable to get the name for this object of type " PRIu32, type); + LOG_DEBUG("VHPI: Unable to get the name for this object of type " PRIu32, type); return GpiIterator::NATIVE_NO_NAME; } @@ -1155,14 +1159,14 @@ GpiIterator::Status VhpiIterator::next_handle(std::string &name, name = idx_str.substr(0,found); obj = m_parent->get_handle(); } else { - LOG_WARN("Unhandled Generate Loop Format - %s", name.c_str()); + LOG_WARN("VHPI: Unhandled Generate Loop Format - %s", name.c_str()); name = c_name; } } else { name = c_name; } - LOG_DEBUG("vhpi_scan found %s (%d) kind:%s name:%s", name.c_str(), + LOG_DEBUG("VHPI: vhpi_scan found %s (%d) kind:%s name:%s", name.c_str(), vhpi_get(vhpiKindP, obj), vhpi_get_str(vhpiKindStrP, obj), vhpi_get_str(vhpiCaseNameP, obj)); @@ -1179,7 +1183,7 @@ GpiIterator::Status VhpiIterator::next_handle(std::string &name, if (found != std::string::npos) { fq_name += name.substr(found); } else { - LOG_WARN("Unhandled Sub-Element Format - %s", name.c_str()); + LOG_WARN("VHPI: Unhandled Sub-Element Format - %s", name.c_str()); fq_name += "." + name; } } else if (obj_type == GPI_STRUCTURE) { @@ -1189,7 +1193,7 @@ GpiIterator::Status VhpiIterator::next_handle(std::string &name, fq_name += name.substr(found); name = name.substr(found+1); } else { - LOG_WARN("Unhandled Sub-Element Format - %s", name.c_str()); + LOG_WARN("VHPI: Unhandled Sub-Element Format - %s", name.c_str()); fq_name += "." + name; } } else { @@ -1204,4 +1208,3 @@ GpiIterator::Status VhpiIterator::next_handle(std::string &name, else return GpiIterator::NOT_NATIVE; } - diff --git a/cocotb/share/lib/vhpi/VhpiImpl.cpp b/cocotb/share/lib/vhpi/VhpiImpl.cpp index 84509516..f003485d 100644 --- a/cocotb/share/lib/vhpi/VhpiImpl.cpp +++ b/cocotb/share/lib/vhpi/VhpiImpl.cpp @@ -266,7 +266,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, GpiObjHdl *new_obj = NULL; if (vhpiVerilog == (type = vhpi_get(vhpiKindP, new_hdl))) { - LOG_DEBUG("vhpiVerilog returned from vhpi_get(vhpiType, ...)") + LOG_DEBUG("VHPI: vhpiVerilog returned from vhpi_get(vhpiType, ...)") return NULL; } @@ -301,7 +301,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, vhpiIntT num_dim = vhpi_get(vhpiNumDimensionsP, query_hdl); if (num_dim > 1) { - LOG_DEBUG("Detected a MULTI-DIMENSIONAL ARRAY type %s", fq_name.c_str()); + LOG_DEBUG("VHPI: Detected a MULTI-DIMENSIONAL ARRAY type %s", fq_name.c_str()); gpi_type = GPI_ARRAY; } else { vhpiHandleT elem_base_type_hdl = NULL; @@ -319,21 +319,21 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, elem_base_type = vhpi_get(vhpiKindP, elem_base_type_hdl); if (elem_base_type == vhpiEnumTypeDeclK) { if (is_enum_logic(elem_base_type_hdl)) { - LOG_DEBUG("Detected a LOGIC VECTOR type %s", fq_name.c_str()); + LOG_DEBUG("VHPI: Detected a LOGIC VECTOR type %s", fq_name.c_str()); gpi_type = GPI_REGISTER; } else if (is_enum_char(elem_base_type_hdl)) { - LOG_DEBUG("Detected a STRING type %s", fq_name.c_str()); + LOG_DEBUG("VHPI: Detected a STRING type %s", fq_name.c_str()); gpi_type = GPI_STRING; } else { - LOG_DEBUG("Detected a NON-LOGIC ENUM VECTOR type %s", fq_name.c_str()); + LOG_DEBUG("VHPI: Detected a NON-LOGIC ENUM VECTOR type %s", fq_name.c_str()); gpi_type = GPI_ARRAY; } } else { - LOG_DEBUG("Detected a NON-ENUM VECTOR type %s", fq_name.c_str()); + LOG_DEBUG("VHPI: Detected a NON-ENUM VECTOR type %s", fq_name.c_str()); gpi_type = GPI_ARRAY; } } else { - LOG_ERROR("Unable to determine the Array Element Base Type for %s. Defaulting to GPI_ARRAY.", vhpi_get_str(vhpiFullCaseNameP, new_hdl)); + LOG_ERROR("VHPI: Unable to determine the Array Element Base Type for %s. Defaulting to GPI_ARRAY.", vhpi_get_str(vhpiFullCaseNameP, new_hdl)); gpi_type = GPI_ARRAY; } } @@ -342,35 +342,35 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, case vhpiEnumTypeDeclK: { if (is_enum_logic(query_hdl)) { - LOG_DEBUG("Detected a LOGIC type %s", fq_name.c_str()); + LOG_DEBUG("VHPI: Detected a LOGIC type %s", fq_name.c_str()); gpi_type = GPI_REGISTER; } else if (is_enum_char(query_hdl)) { - LOG_DEBUG("Detected a CHAR type %s", fq_name.c_str()); + LOG_DEBUG("VHPI: Detected a CHAR type %s", fq_name.c_str()); gpi_type = GPI_INTEGER; } else if (is_enum_boolean(query_hdl)) { - LOG_DEBUG("Detected a BOOLEAN/INTEGER type %s", fq_name.c_str()); + LOG_DEBUG("VHPI: Detected a BOOLEAN/INTEGER type %s", fq_name.c_str()); gpi_type = GPI_INTEGER; } else { - LOG_DEBUG("Detected an ENUM type %s", fq_name.c_str()); + LOG_DEBUG("VHPI: Detected an ENUM type %s", fq_name.c_str()); gpi_type = GPI_ENUM; } break; } case vhpiIntTypeDeclK: { - LOG_DEBUG("Detected an INT type %s", fq_name.c_str()); + LOG_DEBUG("VHPI: Detected an INT type %s", fq_name.c_str()); gpi_type = GPI_INTEGER; break; } case vhpiFloatTypeDeclK: { - LOG_DEBUG("Detected a REAL type %s", fq_name.c_str()); + LOG_DEBUG("VHPI: Detected a REAL type %s", fq_name.c_str()); gpi_type = GPI_REAL; break; } case vhpiRecordTypeDeclK: { - LOG_DEBUG("Detected a STRUCTURE type %s", fq_name.c_str()); + LOG_DEBUG("VHPI: Detected a STRUCTURE type %s", fq_name.c_str()); gpi_type = GPI_STRUCTURE; break; } @@ -403,7 +403,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, } if (name != hdl_name) { - LOG_DEBUG("Found pseudo-region %s", fq_name.c_str()); + LOG_DEBUG("VHPI: Found pseudo-region %s", fq_name.c_str()); gpi_type = GPI_GENARRAY; } else { gpi_type = GPI_MODULE; @@ -412,7 +412,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, } default: { - LOG_ERROR("Not able to map type (%s) %u to object", + LOG_ERROR("VHPI: Not able to map type (%s) %u to object", vhpi_get_str(vhpiKindStrP, query_hdl), type); new_obj = NULL; goto out; @@ -420,7 +420,7 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, } create: - LOG_DEBUG("Creating %s of type %d (%s)", + LOG_DEBUG("VHPI: Creating %s of type %d (%s)", vhpi_get_str(vhpiFullCaseNameP, new_hdl), gpi_type, vhpi_get_str(vhpiKindStrP, query_hdl)); @@ -450,14 +450,14 @@ GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, GpiObjHdl *VhpiImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) { - LOG_DEBUG("Trying to convert raw to VHPI handle"); + LOG_DEBUG("VHPI: Trying to convert raw to VHPI handle"); vhpiHandleT new_hdl = (vhpiHandleT)raw_hdl; std::string fq_name = parent->get_fullname(); const char *c_name = vhpi_get_str(vhpiCaseNameP, new_hdl); if (!c_name) { - LOG_DEBUG("Unable to query name of passed in handle"); + LOG_DEBUG("VHPI: Unable to query name of passed in handle"); return NULL; } @@ -472,7 +472,7 @@ GpiObjHdl *VhpiImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { vhpi_release_handle(new_hdl); - LOG_DEBUG("Unable to fetch object %s", fq_name.c_str()); + LOG_DEBUG("VHPI: Unable to fetch object %s", fq_name.c_str()); return NULL; } @@ -531,12 +531,12 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) } } if (new_hdl == NULL) { - LOG_DEBUG("Unable to query vhpi_handle_by_name %s", fq_name.c_str()); + LOG_DEBUG("VHPI: Unable to query vhpi_handle_by_name %s", fq_name.c_str()); return NULL; } } - /* Generate Loops have inconsistent behavior across vhpi. A "name" + /* Generate Loops have inconsistent behavior across VHPI. A "name" * without an index, i.e. dut.loop vs dut.loop(0), may or may not map to * to the start index. If it doesn't then it won't find anything. * @@ -552,7 +552,7 @@ GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { vhpi_release_handle(new_hdl); - LOG_DEBUG("Unable to fetch object %s", fq_name.c_str()); + LOG_DEBUG("VHPI: Unable to fetch object %s", fq_name.c_str()); return NULL; } @@ -570,7 +570,7 @@ GpiObjHdl *VhpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) gpi_objtype_t obj_type = parent->get_type(); if (obj_type == GPI_GENARRAY) { - LOG_DEBUG("Native check create for index %d of parent %s (pseudo-region)", + LOG_DEBUG("VHPI: Native check create for index %d of parent %s (pseudo-region)", index, parent->get_name_str()); @@ -585,7 +585,7 @@ GpiObjHdl *VhpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) new_hdl = vhpi_handle_by_name(&writable[0], NULL); } else if (obj_type == GPI_REGISTER || obj_type == GPI_ARRAY || obj_type == GPI_STRING) { - LOG_DEBUG("Native check create for index %d of parent %s (%s)", + LOG_DEBUG("VHPI: Native check create for index %d of parent %s (%s)", index, parent->get_fullname_str(), vhpi_get_str(vhpiKindStrP, vhpi_hdl)); @@ -608,7 +608,7 @@ GpiObjHdl *VhpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) } if (base_hdl == NULL) { - LOG_ERROR("Unable to get the vhpiBaseType of %s", parent->get_fullname_str()); + LOG_ERROR("VHPI: Unable to get the vhpiBaseType of %s", parent->get_fullname_str()); return NULL; } @@ -647,7 +647,7 @@ GpiObjHdl *VhpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) if (indices.size() == num_dim) { #ifdef IUS - /* IUS/Xcelium does not appear to set the vhpiIsUnconstrainedP property. IUS Docs say will return + /* IUS/Xcelium does not appear to set the vhpiIsUnconstrainedP property. IUS Docs say it will return * -1 if unconstrained, but with vhpiIntT being unsigned, the value returned is below. */ const vhpiIntT UNCONSTRAINED = 2147483647; @@ -728,7 +728,7 @@ GpiObjHdl *VhpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) constraints.pop_back(); } } else { - LOG_ERROR("Unable to access all constraints for %s", parent->get_fullname_str()); + LOG_ERROR("VHPI: Unable to access all constraints for %s", parent->get_fullname_str()); return NULL; } @@ -766,7 +766,7 @@ GpiObjHdl *VhpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) } if (new_hdl != NULL) { - LOG_DEBUG("Index (%d->%d) found %s (%s)", index, idx, vhpi_get_str(vhpiCaseNameP, new_hdl), vhpi_get_str(vhpiKindStrP, new_hdl)); + LOG_DEBUG("VHPI: Index (%d->%d) found %s (%s)", index, idx, vhpi_get_str(vhpiCaseNameP, new_hdl), vhpi_get_str(vhpiKindStrP, new_hdl)); } } } else { @@ -776,14 +776,14 @@ GpiObjHdl *VhpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) if (new_hdl == NULL) { - LOG_DEBUG("Unable to query vhpi_handle_by_index %d", index); + LOG_DEBUG("VHPI: Unable to query vhpi_handle_by_index %d", index); return NULL; } GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); if (new_obj == NULL) { vhpi_release_handle(new_hdl); - LOG_DEBUG("Could not fetch object below entity (%s) at index (%d)", + LOG_DEBUG("VHPI: Could not fetch object below entity (%s) at index (%d)", parent->get_name_str(), index); return NULL; } @@ -829,9 +829,9 @@ GpiObjHdl *VhpiImpl::get_root_handle(const char* name) return NULL; } - /* if this matches the name then it is what we want, but we - use the handle two levels up as the dut as do not want an - object of type vhpiEntityDeclK as the dut */ + /* If this matches the name then it is what we want, but we + use the handle two levels up as the DUT as we do not want an + object of type vhpiEntityDeclK as the DUT */ found = vhpi_get_str(vhpiCaseNameP, dut); dut = root; From e892a3ea48bef4ab1781034681c25660aa4fe4ae Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Tue, 24 Nov 2020 14:18:10 -0800 Subject: [PATCH 2584/2656] Add support for adding sets of parameters with TestFactory.add_option (#2175) --- cocotb/regression.py | 58 ++++++++++++++----- .../source/newsfragments/2175.feature.rst | 1 + tests/test_cases/test_cocotb/Makefile | 1 + .../test_cocotb/test_testfactory.py | 30 ++++++++++ 4 files changed, 76 insertions(+), 14 deletions(-) create mode 100644 documentation/source/newsfragments/2175.feature.rst create mode 100644 tests/test_cases/test_cocotb/test_testfactory.py diff --git a/cocotb/regression.py b/cocotb/regression.py index 1ee62b61..3a075e78 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -627,30 +627,38 @@ class TestFactory: This Factory allows us to generate sets of tests based on the different permutations of the possible arguments to the test function. - For example if we have a module that takes backpressure and idles and - have some packet generation routines ``gen_a`` and ``gen_b``: + For example, if we have a module that takes backpressure, has two configurable + features where enabling ``feature_b`` requires ``feature_a`` to be active, and + need to test against data generation routines ``gen_a`` and ``gen_b``: >>> tf = TestFactory(test_function=run_test) >>> tf.add_option(name='data_in', optionlist=[gen_a, gen_b]) >>> tf.add_option('backpressure', [None, random_backpressure]) - >>> tf.add_option('idles', [None, random_idles]) + >>> tf.add_option(('feature_a', 'feature_b'), [(False, False), (True, False), (True, True)]) >>> tf.generate_tests() We would get the following tests: - * ``gen_a`` with no backpressure and no idles - * ``gen_a`` with no backpressure and ``random_idles`` - * ``gen_a`` with ``random_backpressure`` and no idles - * ``gen_a`` with ``random_backpressure`` and ``random_idles`` - * ``gen_b`` with no backpressure and no idles - * ``gen_b`` with no backpressure and ``random_idles`` - * ``gen_b`` with ``random_backpressure`` and no idles - * ``gen_b`` with ``random_backpressure`` and ``random_idles`` + * ``gen_a`` with no backpressure and both features disabled + * ``gen_a`` with no backpressure and only ``feature_a`` enabled + * ``gen_a`` with no backpressure and both features enabled + * ``gen_a`` with ``random_backpressure`` and both features disabled + * ``gen_a`` with ``random_backpressure`` and only ``feature_a`` enabled + * ``gen_a`` with ``random_backpressure`` and both features enabled + * ``gen_b`` with no backpressure and both features disabled + * ``gen_b`` with no backpressure and only ``feature_a`` enabled + * ``gen_b`` with no backpressure and both features enabled + * ``gen_b`` with ``random_backpressure`` and both features disabled + * ``gen_b`` with ``random_backpressure`` and only ``feature_a`` enabled + * ``gen_b`` with ``random_backpressure`` and both features enabled The tests are appended to the calling module for auto-discovery. Tests are simply named ``test_function_N``. The docstring for the test (hence the test description) includes the name and description of each generator. + + .. versionchanged:: 1.5 + Groups of options are now supported """ # Prevent warnings from collection of TestFactories by unit testing frameworks. @@ -675,11 +683,22 @@ def add_option(self, name, optionlist): """Add a named option to the test. Args: - name (str): Name of the option. Passed to test as a keyword - argument. + name (str or iterable of str): An option name, or an iterable of + several option names. Passed to test as keyword arguments. optionlist (list): A list of possible options for this test knob. + If N names were specified, this must be a list of N-tuples or + lists, where each element specifies a value for its respective + option. + + .. versionchanged:: 1.5 + Groups of options are now supported """ + if not isinstance(name, str): + name = tuple(name) + for opt in optionlist: + if len(name) != len(opt): + raise ValueError("Mismatch between number of options and number of option values in group") self.kwargs[name] = optionlist def generate_tests(self, prefix="", postfix=""): @@ -714,7 +733,18 @@ def generate_tests(self, prefix="", postfix=""): name = "%s%s%s_%03d" % (prefix, self.name, postfix, index + 1) doc = "Automatically generated test\n\n" + # preprocess testoptions to split tuples + testoptions_split = {} for optname, optvalue in testoptions.items(): + if isinstance(optname, str): + testoptions_split[optname] = optvalue + else: + # previously checked in add_option; ensure nothing has changed + assert len(optname) == len(optvalue) + for n, v in zip(optname, optvalue): + testoptions_split[n] = v + + for optname, optvalue in testoptions_split.items(): if callable(optvalue): if not optvalue.__doc__: desc = "No docstring supplied" @@ -729,7 +759,7 @@ def generate_tests(self, prefix="", postfix=""): (name, mod.__name__)) kwargs = {} kwargs.update(self.kwargs_constant) - kwargs.update(testoptions) + kwargs.update(testoptions_split) if hasattr(mod, name): self.log.error("Overwriting %s in module %s. " "This causes a previously defined testcase " diff --git a/documentation/source/newsfragments/2175.feature.rst b/documentation/source/newsfragments/2175.feature.rst new file mode 100644 index 00000000..03bfd017 --- /dev/null +++ b/documentation/source/newsfragments/2175.feature.rst @@ -0,0 +1 @@ +:func:`cocotb.regression.TestFactory.add_option` now supports groups of options when a full Cartesian product is not desired diff --git a/tests/test_cases/test_cocotb/Makefile b/tests/test_cases/test_cocotb/Makefile index 58e125a8..bc628519 100644 --- a/tests/test_cases/test_cocotb/Makefile +++ b/tests/test_cases/test_cocotb/Makefile @@ -10,6 +10,7 @@ MODULE := "\ test_synchronization_primitives,\ test_concurrency_primitives,\ test_tests,\ + test_testfactory,\ test_generator_coroutines,\ test_timing_triggers,\ test_scheduler,\ diff --git a/tests/test_cases/test_cocotb/test_testfactory.py b/tests/test_cases/test_cocotb/test_testfactory.py new file mode 100644 index 00000000..b09ee661 --- /dev/null +++ b/tests/test_cases/test_cocotb/test_testfactory.py @@ -0,0 +1,30 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause +""" +Tests of cocotb.regression.TestFactory functionality +""" +import cocotb +from cocotb.regression import TestFactory + + +testfactory_test_args = set() + + +async def run_testfactory_test(dut, arg1, arg2, arg3): + testfactory_test_args.add((arg1, arg2, arg3)) + +factory = TestFactory(run_testfactory_test) +factory.add_option("arg1", ["a1v1", "a1v2"]) +factory.add_option(("arg2", "arg3"), [("a2v1", "a3v1"), ("a2v2", "a3v2")]) +factory.generate_tests() + + +@cocotb.test() +async def test_testfactory_verify_args(dut): + assert testfactory_test_args == set([ + ("a1v1", "a2v1", "a3v1"), + ("a1v2", "a2v1", "a3v1"), + ("a1v1", "a2v2", "a3v2"), + ("a1v2", "a2v2", "a3v2"), + ]) From 954aeeb32e7875de6d4698cde018e9367c99e9bd Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Tue, 24 Nov 2020 21:12:56 -0600 Subject: [PATCH 2585/2656] Update stale.yml (#2226) --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 01968dd2..191a03b7 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -18,5 +18,5 @@ jobs: If no more activity on this issue occurs in 7 days, it will be closed. stale-issue-label: "status:stale" only-labels: "type:question" - exempt-labels: "type:bug,type:feature" + exempt-issue-labels: "type:bug,type:feature" operations-per-run: 5 From 488de766148d9f7a82df150c1f06611122400699 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 23 Nov 2020 23:39:56 +0100 Subject: [PATCH 2586/2656] Make _compat_mapping report a DeprecationWarning (old: UserWarning) Add test for _compat_mapping. Fix one remaining use of 'log' in test_axi_lite_slave. --- cocotb/handle.py | 6 ++++-- .../tests/test_axi_lite_slave.py | 2 +- .../test_cases/test_cocotb/test_deprecated.py | 20 +++++++++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 976afd9f..ccc84338 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -162,7 +162,8 @@ def __str__(self): def __setattr__(self, name, value): if name in self._compat_mapping: if name not in _deprecation_warned: - warnings.warn("Use of attribute %r is deprecated, use %r instead" % (name, self._compat_mapping[name])) + warnings.warn("Use of attribute %r is deprecated, use %r instead" % (name, self._compat_mapping[name]), + DeprecationWarning, stacklevel=3) _deprecation_warned.add(name) return setattr(self, self._compat_mapping[name], value) else: @@ -171,7 +172,8 @@ def __setattr__(self, name, value): def __getattr__(self, name): if name in self._compat_mapping: if name not in _deprecation_warned: - warnings.warn("Use of attribute %r is deprecated, use %r instead" % (name, self._compat_mapping[name])) + warnings.warn("Use of attribute %r is deprecated, use %r instead" % (name, self._compat_mapping[name]), + DeprecationWarning, stacklevel=3) _deprecation_warned.add(name) return getattr(self, self._compat_mapping[name]) else: diff --git a/examples/axi_lite_slave/tests/test_axi_lite_slave.py b/examples/axi_lite_slave/tests/test_axi_lite_slave.py index 93ca4124..61e5d43f 100644 --- a/examples/axi_lite_slave/tests/test_axi_lite_slave.py +++ b/examples/axi_lite_slave/tests/test_axi_lite_slave.py @@ -44,7 +44,7 @@ async def write_address_0(dut): value = dut.dut.r_temp_0 assert value == DATA, ("Register at address 0x%08X should have been " "0x%08X but was 0x%08X" % (ADDRESS, DATA, int(value))) - dut.log.info("Write 0x%08X to address 0x%08X" % (int(value), ADDRESS)) + dut._log.info("Write 0x%08X to address 0x%08X" % (int(value), ADDRESS)) # Read back a value at address 0x04 diff --git a/tests/test_cases/test_cocotb/test_deprecated.py b/tests/test_cases/test_cocotb/test_deprecated.py index 73725ea1..10ba37a7 100644 --- a/tests/test_cases/test_cocotb/test_deprecated.py +++ b/tests/test_cases/test_cocotb/test_deprecated.py @@ -86,3 +86,23 @@ async def example(): pass with assert_deprecated(): cocotb.hook()(example) + + +@cocotb.test() +async def test_handle_compat_mapping(dut): + """ + Test DeprecationWarnings for _compat_mapping. + + Note that these only warn once per attribute. + """ + # log + with assert_deprecated(): + dut.log.info("'log' is deprecated") + # name + with assert_deprecated(): + dut.name = "myname" + assert dut.name == "myname" + # fullname + with assert_deprecated(): + dut.fullname = "myfullname" + assert dut.fullname == "myfullname" From a49b260a1190ae3d6f7f7ee6fa331a3a76a433fe Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 24 Nov 2020 22:53:57 +0100 Subject: [PATCH 2587/2656] gitpod: Compile Verilator stable; small UX tweaks Homebrew doesn't keep up with Verilator releases. --- .gitignore | 2 ++ .gitpod.Dockerfile | 28 +++++++++++++++++++--------- .gitpod.yml | 10 ++++++---- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index b0e37864..f585ba56 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,8 @@ __pycache__ # Waveforms *.vcd +*.fst +*.fst.hier # Results results.xml diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile index b365f4ef..c308aee8 100644 --- a/.gitpod.Dockerfile +++ b/.gitpod.Dockerfile @@ -10,36 +10,46 @@ RUN pyenv global ${PYTHON_VERSION} RUN pip3 install --upgrade pip -# Install linters -RUN pip3 install -U flake8 pylint +# Install extra packages +RUN pip3 install -U flake8 pylint pytype mypy gcovr cherrypy dowser -# Re-synchronize the package index +# Re-synchronize the OS package index RUN sudo apt-get update -# Install needed packages -RUN sudo apt-get install -y flex gnat gtkwave swig +# Install all needed packages for all simulators +RUN sudo apt-get install -y perl make flex gnat gtkwave swig autoconf g++ bison libfl2 libfl-dev ccache libgoogle-perftools-dev numactl perl-doc RUN sudo rm -rf /var/lib/apt/lists/* ## Install Icarus Verilog RUN brew install icarus-verilog ## Install Verilator -RUN brew install verilator +ENV VERILATOR_BRANCH=stable + +RUN git clone https://github.com/verilator/verilator.git --branch ${VERILATOR_BRANCH} verilator \ + && unset VERILATOR_ROOT \ + && cd verilator \ + && autoconf \ + && ./configure \ + && make --silent \ + && sudo make --silent install \ + && cd .. \ + && rm -rf verilator ## Install GHDL ENV GHDL_BRANCH=v0.37 RUN git clone https://github.com/ghdl/ghdl.git --depth=1 --branch ${GHDL_BRANCH} ghdl \ && cd ghdl \ && ./configure \ - && make -s \ - && sudo make -s install \ + && make --silent \ + && sudo make --silent install \ && cd .. \ && rm -rf ghdl # Install cvc RUN git clone https://github.com/cambridgehackers/open-src-cvc.git --depth=1 cvc \ && cd cvc/src \ - && make -f makefile.cvc64 -s \ + && make -f makefile.cvc64 --silent \ && sudo cp cvc64 /usr/local/bin \ && cd ../.. \ && rm -rf cvc diff --git a/.gitpod.yml b/.gitpod.yml index b0caa180..0aacb208 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -13,17 +13,19 @@ tasks: gp open Makefile && gp open dff_cocotb.py && make SIM=icarus && - EXTRA_ARGS="-CFLAGS -DVM_TRACE_FST=1 --trace-fst --trace-structs" make SIM=verilator && - gtkwave dump.fst & + EXTRA_ARGS="--trace-fst --trace-structs" make SIM=verilator && + gtkwave dump.fst --rcvar 'initial_window_x 1920' --rcvar 'initial_window_y 1061' --rcvar 'do_initial_zoom_fit yes' & command: > cd /workspace/cocotb/examples/dff/tests ; history -s make SIM=cvc ; history -s make SIM=ghdl TOPLEVEL_LANG=vhdl ; history -s make SIM=icarus ; - history -s EXTRA_ARGS="-CFLAGS -DVM_TRACE_FST=1 --trace-fst --trace-structs" make SIM=verilator ; - history -s gtkwave dump.fst ; + history -s EXTRA_ARGS=\"--trace-fst --trace-structs\" make SIM=verilator ; + history -s gtkwave dump.fst --rcvar \'initial_window_x 1920\' --rcvar \'initial_window_y 1061\' --rcvar \'do_initial_zoom_fit yes\' ; history -d 3 +# NOTE: the geometry for gtkwave's fullscreen size can be found with xwininfo -root + # https://www.gitpod.io/docs/config-ports/ ports: - port: 6080 # VNC for e.g. gtkwave From e7815bb4e5a0144a55a17dbfe86feb6df5ec725d Mon Sep 17 00:00:00 2001 From: Marlon James Date: Wed, 25 Nov 2020 13:14:16 -0800 Subject: [PATCH 2588/2656] Allow tuning of matrix_multiplier test length in tox environment --- tox.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tox.ini b/tox.ini index 779c9d91..a8973b0c 100644 --- a/tox.ini +++ b/tox.ini @@ -28,6 +28,8 @@ passenv = ALDEC_LICENSE_FILE SYNOPSYS_LICENSE_FILE LM_LICENSE_FILE + # allow tuning of matrix_multiplier test length + NUM_SAMPLES deps = coverage From 631d57126434d20be3a0e968f1410d98a8522591 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 11 Nov 2020 21:24:19 +0100 Subject: [PATCH 2589/2656] Fix deprecation warning for str(handle) An example occurrence is: FutureWarning: `str(ConstantObject)` is deprecated, and in future will return `ConstantObject._path`. To get a string representation of the value, use `str(ConstantObject.value)`. --- cocotb/handle.py | 2 +- cocotb/share/lib/vpi/VpiCbHdl.cpp | 2 +- tests/test_cases/issue_330/issue_330.py | 4 ++-- tests/test_cases/test_discovery/test_discovery.py | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index ccc84338..6f94c437 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -205,7 +205,7 @@ def __iter__(self): self._log.debug("Yielding index %d from %s (%s)", subindex, name, type(subhdl)) yield subhdl else: - self._log.debug("Yielding %s (%s)", name, handle) + self._log.debug("Yielding %s of type %s (%s)", name, type(handle), handle._path) yield handle def _discover_all(self): diff --git a/cocotb/share/lib/vpi/VpiCbHdl.cpp b/cocotb/share/lib/vpi/VpiCbHdl.cpp index fe89e6b6..4029ff61 100644 --- a/cocotb/share/lib/vpi/VpiCbHdl.cpp +++ b/cocotb/share/lib/vpi/VpiCbHdl.cpp @@ -822,7 +822,7 @@ GpiIterator::Status VpiIterator::next_handle(std::string &name, GpiObjHdl **hdl, /* Simulators vary here. Some will allow the name to be accessed across boundary. We can simply return this up and allow the object to be created. Others do not. In this case - we see if the object is in out type range and if not + we see if the object is in our type range and if not return the raw_hdl up */ const char *c_name = vpi_get_str(vpiName, obj); diff --git a/tests/test_cases/issue_330/issue_330.py b/tests/test_cases/issue_330/issue_330.py index 2d2fdf49..8e482a15 100644 --- a/tests/test_cases/issue_330/issue_330.py +++ b/tests/test_cases/issue_330/issue_330.py @@ -17,7 +17,7 @@ def issue_330_direct(dut): structure = dut.inout_if - tlog.info("Value of inout_if => a_in = %s ; b_out = %s" % (structure.a_in, structure.b_out)) + tlog.info("Value of inout_if => a_in = %s ; b_out = %s" % (structure.a_in.value, structure.b_out.value)) @cocotb.test(skip=cocotb.SIM_NAME in ["Icarus Verilog"]) @@ -33,7 +33,7 @@ def issue_330_iteration(dut): count = 0 for member in structure: - tlog.info("Found %s" % member) + tlog.info("Found %s" % member._path) count += 1 if count != 2: diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 07e29aec..d269ad2b 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -260,13 +260,13 @@ async def access_const_string_verilog(dut): if not isinstance(string_const, StringObject): raise TestFailure("STRING_CONST was not StringObject") if string_const != b"TESTING_CONST": - raise TestFailure("Initial value of STRING_CONST was not == b\'TESTING_CONST\' but {} instead".format(string_const)) + raise TestFailure("Initial value of STRING_CONST was not == b\'TESTING_CONST\' but {} instead".format(string_const.value)) tlog.info("Modifying const string") string_const <= b"MODIFIED" await Timer(10, "ns") if string_const != b"TESTING_CONST": - raise TestFailure("STRING_CONST was not still b\'TESTING_CONST\' after modification but {} instead".format(string_const)) + raise TestFailure("STRING_CONST was not still b\'TESTING_CONST\' after modification but {} instead".format(string_const.value)) @cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"], @@ -281,13 +281,13 @@ async def access_var_string_verilog(dut): if not isinstance(string_var, StringObject): raise TestFailure("STRING_VAR was not StringObject") if string_var != b"TESTING_VAR": - raise TestFailure("Initial value of STRING_VAR was not == b\'TESTING_VAR\' but {} instead".format(string_var)) + raise TestFailure("Initial value of STRING_VAR was not == b\'TESTING_VAR\' but {} instead".format(string_var.value)) tlog.info("Modifying var string") string_var <= b"MODIFIED" await Timer(10, "ns") if string_var != b"MODIFIED": - raise TestFailure("STRING_VAR was not == b\'MODIFIED\' after modification but {} instead".format(string_var)) + raise TestFailure("STRING_VAR was not == b\'MODIFIED\' after modification but {} instead".format(string_var.value)) @cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) From 0a1c46aa48d786a1df6fefedc756ba3f5c312a1f Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 27 Nov 2020 10:26:32 -0600 Subject: [PATCH 2590/2656] Fix some sphinx warnings (#2235) --- cocotb/decorators.py | 7 ++++++- cocotb/drivers/amba.py | 4 ++-- cocotb/monitors/__init__.py | 20 +++++++++----------- documentation/source/install_devel.rst | 2 +- documentation/source/release_notes.rst | 2 ++ 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/cocotb/decorators.py b/cocotb/decorators.py index b2a92366..335ccd43 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -498,6 +498,7 @@ class test(coroutine, metaclass=_decorator_helper): Test timeout is intended for protection against deadlock. Users should use :class:`~cocotb.triggers.with_timeout` if they require a more general-purpose timeout mechanism. + timeout_unit (str, optional): Units of timeout_time, accepts any units that :class:`~cocotb.triggers.Timer` does. @@ -505,8 +506,10 @@ class test(coroutine, metaclass=_decorator_helper): .. deprecated:: 1.5 Using None as the the *timeout_unit* argument is deprecated, use ``'step'`` instead. - expect_fail (bool, optional): + + expect_fail (bool, optional): Don't mark the result as a failure if the test fails. + expect_error (bool or exception type or tuple of exception types, optional): If ``True``, consider this test passing if it raises *any* :class:`Exception`, and failing if it does not. If given an exception type or tuple of exception types, catching *only* a listed exception type is considered passing. @@ -525,9 +528,11 @@ async def my_test(dut): .. versionchanged:: 1.3 Specific exception types can be expected + skip (bool, optional): Don't execute this test as part of the regression. Test can still be run manually by setting :make:var:`TESTCASE`. + stage (int, optional) Order tests logically into stages, where multiple tests can share a stage. """ diff --git a/cocotb/drivers/amba.py b/cocotb/drivers/amba.py index d2cca2f5..1307ec71 100644 --- a/cocotb/drivers/amba.py +++ b/cocotb/drivers/amba.py @@ -275,8 +275,8 @@ async def write( With unaligned writes (when ``address`` is not a multiple of ``size``), only the low ``size - address % size`` Bytes are written for: - * the last element of ``value`` for INCR or WRAP bursts, or - * every element of ``value`` for FIXED bursts. + * the last element of ``value`` for INCR or WRAP bursts, or + * every element of ``value`` for FIXED bursts. Args: address: The address to write to. diff --git a/cocotb/monitors/__init__.py b/cocotb/monitors/__init__.py index 4f532958..e5b27fc3 100755 --- a/cocotb/monitors/__init__.py +++ b/cocotb/monitors/__init__.py @@ -53,23 +53,21 @@ class Monitor: """Base class for Monitor objects. Monitors are passive 'listening' objects that monitor pins going in or out of a DUT. - This class should not be used - directly, but should be sub-classed and the internal :any:`_monitor_recv` method - should be overridden and decorated as a :any:`coroutine`. This :any:`_monitor_recv` - method should capture some behavior of the pins, form a transaction, and - pass this transaction to the internal :any:`_recv` method. The :any:`_monitor_recv` - method is added to the cocotb scheduler during the ``__init__`` phase, so it - should not be awaited anywhere. + This class should not be used directly, + but should be sub-classed and the internal :meth:`_monitor_recv` method should be overridden. + This :meth:`_monitor_recv` method should capture some behavior of the pins, form a transaction, + and pass this transaction to the internal :meth:`_recv` method. + The :meth:`_monitor_recv` method is added to the cocotb scheduler during the ``__init__`` phase, + so it should not be awaited anywhere. - The primary use of a Monitor is as an interface for a - :class:`~cocotb.scoreboard.Scoreboard`. + The primary use of a Monitor is as an interface for a :class:`~cocotb.scoreboard.Scoreboard`. Args: callback (callable): Callback to be called with each recovered transaction as the argument. If the callback isn't used, received transactions will be placed on a queue and the event used to notify any consumers. event (cocotb.triggers.Event): Event that will be called when a transaction - is received through the internal :any:`_recv` method. + is received through the internal :meth:`_recv` method. `Event.data` is set to the received transaction. """ @@ -139,7 +137,7 @@ def _monitor_recv(self): """Actual implementation of the receiver. Sub-classes should override this method to implement the actual receive - routine and call :any:`_recv` with the recovered transaction. + routine and call :meth:`_recv` with the recovered transaction. """ raise NotImplementedError("Attempt to use base monitor class without " "providing a ``_monitor_recv`` method") diff --git a/documentation/source/install_devel.rst b/documentation/source/install_devel.rst index 17cd8507..8736aeed 100644 --- a/documentation/source/install_devel.rst +++ b/documentation/source/install_devel.rst @@ -30,7 +30,7 @@ The instructions for installing the development version of cocotb vary depending If your user does not have permissions to install cocotb using the instructions above, try adding the ``--user`` option to ``pip`` - (see :ref:`the pip documentation`). + (see `the pip documentation `_). .. warning:: diff --git a/documentation/source/release_notes.rst b/documentation/source/release_notes.rst index d401a26e..e3529bee 100644 --- a/documentation/source/release_notes.rst +++ b/documentation/source/release_notes.rst @@ -30,6 +30,7 @@ Features .. towncrier will append the issue number taken from the file name here: + Issue (:pr:`1255`) - The cocotb log configuration is now less intrusive, and only configures the root logger instance, ``logging.getLogger()``, as part of :func:`cocotb.log.default_config` (:pr:`1266`). @@ -61,6 +62,7 @@ Features .. towncrier will append the issue number taken from the file name here: + Issue (:pr:`1403`) - Custom logging handlers can now access the simulator time using :attr:`logging.LogRecord.created_sim_time`, provided the From c83fe306d33d91a2703d0a6e0e5ce4620ce92205 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 29 Nov 2020 14:08:45 -0600 Subject: [PATCH 2591/2656] Remove test_doctests and use 'pytest --doctest-modules' instead (#2237) --- setup.cfg | 7 +++++-- tests/test_cases/test_cocotb/Makefile | 1 - tests/test_cases/test_cocotb/test_doctests.py | 21 ------------------- 3 files changed, 5 insertions(+), 24 deletions(-) delete mode 100644 tests/test_cases/test_cocotb/test_doctests.py diff --git a/setup.cfg b/setup.cfg index d182658d..c7c452ce 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,7 +28,10 @@ per-file-ignores = examples/*: F841 [tool:pytest] -addopts = -v --cov=cocotb --cov-branch -testpaths = tests/pytest +addopts = -v --cov=cocotb --cov-branch --doctest-modules +testpaths = + tests/pytest + cocotb/utils.py + cocotb/binary.py # log_cli = true # log_cli_level = DEBUG diff --git a/tests/test_cases/test_cocotb/Makefile b/tests/test_cases/test_cocotb/Makefile index bc628519..bbd4c68a 100644 --- a/tests/test_cases/test_cocotb/Makefile +++ b/tests/test_cases/test_cocotb/Makefile @@ -6,7 +6,6 @@ include ../../designs/sample_module/Makefile MODULE := "\ test_deprecated,\ - test_doctests,\ test_synchronization_primitives,\ test_concurrency_primitives,\ test_tests,\ diff --git a/tests/test_cases/test_cocotb/test_doctests.py b/tests/test_cases/test_cocotb/test_doctests.py deleted file mode 100644 index e3524e83..00000000 --- a/tests/test_cases/test_cocotb/test_doctests.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -import doctest -import os - -import cocotb - - -@cocotb.test() -async def test_utils(dut): - # prevent failure in case colored output is requested from the environment - os.environ['COCOTB_ANSI_OUTPUT'] = "0" - failures, n = doctest.testmod(cocotb.utils, verbose=True) - assert failures == 0 - - -@cocotb.test() -async def test_binary(dut): - failures, n = doctest.testmod(cocotb.binary, verbose=True) - assert failures == 0 From 3855f677117cf9f4105fe19b3d1f5dafc9a05341 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 29 Nov 2020 14:09:14 -0600 Subject: [PATCH 2592/2656] Remove dead code in utils.py (#2228) --- cocotb/utils.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/cocotb/utils.py b/cocotb/utils.py index b1117023..0dfee7d1 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -547,23 +547,6 @@ def want_color_output(): return want_color -if __name__ == "__main__": - import random - a = "" - for char in range(random.randint(250, 500)): - a += chr(random.randint(0, 255)) - b = a - for error in range(random.randint(2, 9)): - offset = random.randint(0, len(a)) - b = b[:offset] + chr(random.randint(0, 255)) + b[offset+1:] - - diff = hexdiffs(a, b) - print(diff) - - space = '\n' + (" " * 20) - print(space.join(diff.split('\n'))) - - def remove_traceback_frames(tb_or_exc, frame_names): """ Strip leading frames from a traceback From 6fb7a9a57b23dc9aad006fe82b7c33ea8ce4271c Mon Sep 17 00:00:00 2001 From: Marlon James Date: Mon, 30 Nov 2020 09:54:55 -0800 Subject: [PATCH 2593/2656] Cleanup matrix_multiplier example Replace environment variables with parameters from cocotb.top Replace Scoreboard with comparison function Make parameters readable for Verilator Add SystemVerilog block names to match VHDL Add log messages --- .../hdl/matrix_multiplier.sv | 24 ++++++++----- examples/matrix_multiplier/tests/Makefile | 7 ---- .../tests/test_matrix_multiplier.py | 34 ++++++++++++------- 3 files changed, 37 insertions(+), 28 deletions(-) diff --git a/examples/matrix_multiplier/hdl/matrix_multiplier.sv b/examples/matrix_multiplier/hdl/matrix_multiplier.sv index ee193c5c..54f588e0 100644 --- a/examples/matrix_multiplier/hdl/matrix_multiplier.sv +++ b/examples/matrix_multiplier/hdl/matrix_multiplier.sv @@ -4,11 +4,17 @@ // Matrix Multiplier DUT `timescale 1ns/1ps +`ifdef VERILATOR // make parameter readable from VPI + `define VL_RD /*verilator public_flat_rd*/ +`else + `define VL_RD +`endif + module matrix_multiplier #( - parameter int DATA_WIDTH = 8, - parameter int A_ROWS = 8, - parameter int B_COLUMNS = 5, - parameter int A_COLUMNS_B_ROWS = 4, + parameter int DATA_WIDTH `VL_RD = 8, + parameter int A_ROWS `VL_RD = 8, + parameter int B_COLUMNS `VL_RD = 5, + parameter int A_COLUMNS_B_ROWS `VL_RD = 4, parameter int C_DATA_WIDTH = (2 * DATA_WIDTH) + $clog2(A_COLUMNS_B_ROWS) ) ( input clk_i, @@ -22,12 +28,12 @@ module matrix_multiplier #( logic [C_DATA_WIDTH-1:0] c_calc[A_ROWS * B_COLUMNS]; - always @(*) begin + always @(*) begin : multiply logic [C_DATA_WIDTH-1:0] c_element; - for (int i = 0; i < A_ROWS; i = i + 1) begin - for (int j = 0; j < B_COLUMNS; j = j + 1) begin + for (int i = 0; i < A_ROWS; i = i + 1) begin : C_ROWS + for (int j = 0; j < B_COLUMNS; j = j + 1) begin : C_COLUMNS c_element = 0; - for (int k = 0; k < A_COLUMNS_B_ROWS; k = k + 1) begin + for (int k = 0; k < A_COLUMNS_B_ROWS; k = k + 1) begin : DOT_PRODUCT c_element = c_element + (a_i[(i * A_COLUMNS_B_ROWS) + k] * b_i[(k * B_COLUMNS) + j]); end c_calc[(i * B_COLUMNS) + j] = c_element; @@ -35,7 +41,7 @@ module matrix_multiplier #( end end - always @(posedge clk_i) begin + always @(posedge clk_i) begin : proc_reg if (reset_i) begin valid_o <= 1'b0; diff --git a/examples/matrix_multiplier/tests/Makefile b/examples/matrix_multiplier/tests/Makefile index 73adc424..ac800219 100644 --- a/examples/matrix_multiplier/tests/Makefile +++ b/examples/matrix_multiplier/tests/Makefile @@ -55,13 +55,6 @@ endif # Fix the seed to ensure deterministic tests export RANDOM_SEED := 123456789 -# Export generics to test -# Can't use plusargs since they aren't supported by ghdl -export DATA_WIDTH -export A_ROWS -export B_COLUMNS -export A_COLUMNS_B_ROWS - TOPLEVEL := matrix_multiplier MODULE := test_matrix_multiplier diff --git a/examples/matrix_multiplier/tests/test_matrix_multiplier.py b/examples/matrix_multiplier/tests/test_matrix_multiplier.py index d6e8229a..4c104801 100644 --- a/examples/matrix_multiplier/tests/test_matrix_multiplier.py +++ b/examples/matrix_multiplier/tests/test_matrix_multiplier.py @@ -10,14 +10,13 @@ from cocotb.clock import Clock from cocotb.monitors import BusMonitor from cocotb.regression import TestFactory -from cocotb.scoreboard import Scoreboard from cocotb.triggers import RisingEdge, ReadOnly NUM_SAMPLES = int(os.environ.get('NUM_SAMPLES', 3000)) -DATA_WIDTH = int(os.environ['DATA_WIDTH']) -A_ROWS = int(os.environ['A_ROWS']) -B_COLUMNS = int(os.environ['B_COLUMNS']) -A_COLUMNS_B_ROWS = int(os.environ['A_COLUMNS_B_ROWS']) +DATA_WIDTH = int(cocotb.top.DATA_WIDTH) +A_ROWS = int(cocotb.top.A_ROWS) +B_COLUMNS = int(cocotb.top.B_COLUMNS) +A_COLUMNS_B_ROWS = int(cocotb.top.A_COLUMNS_B_ROWS) class MatrixMonitor(BusMonitor): @@ -83,14 +82,22 @@ async def test_multiply(dut, a_data, b_data): cocotb.fork(Clock(dut.clk_i, 10, units='ns').start()) - # Configure Scoreboard to compare module results to expected + # Configure checker to compare module results to expected expected_output = [] + dut._log.info("Configure monitors") + in_monitor = MatrixInMonitor(dut, callback=expected_output.append) - out_monitor = MatrixOutMonitor(dut) - scoreboard = Scoreboard(dut) - scoreboard.add_interface(out_monitor, expected_output) + def check_output(transaction): + if not expected_output: + raise RuntimeError("Monitor captured unexpected multiplication operation") + exp = expected_output.pop(0) + assert transaction == exp, "Multiplication result {} did not match expected result {}".format(transaction, exp) + + out_monitor = MatrixOutMonitor(dut, callback=check_output) + + dut._log.info("Initialize and reset model") # Initial values dut.valid_i <= 0 @@ -103,8 +110,10 @@ async def test_multiply(dut, a_data, b_data): await RisingEdge(dut.clk_i) dut.reset_i <= 0 + dut._log.info("Test multiplication operations") + # Do multiplication operations - for A, B in zip(a_data(), b_data()): + for i, (A, B) in enumerate(zip(a_data(), b_data())): await RisingEdge(dut.clk_i) dut.a_i <= A dut.b_i <= B @@ -113,9 +122,10 @@ async def test_multiply(dut, a_data, b_data): await RisingEdge(dut.clk_i) dut.valid_i <= 0 - await RisingEdge(dut.clk_i) + if i % 100 == 0: + dut._log.info("{} / {}".format(i, NUM_SAMPLES)) - raise scoreboard.result + await RisingEdge(dut.clk_i) def create_matrix(func, rows, cols): From caa1646a19c1b3d0282c50efc43cc623a6135897 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 30 Nov 2020 16:48:36 -0600 Subject: [PATCH 2594/2656] update issue tests for async/await, assert, and Timer with units (#2236) --- tests/test_cases/issue_120/issue_120.py | 40 ++++++++--------- tests/test_cases/issue_1279/issue_1279.py | 9 ++-- tests/test_cases/issue_142/issue_142.py | 18 ++++---- tests/test_cases/issue_253/issue_253.py | 30 ++++++------- tests/test_cases/issue_330/issue_330.py | 15 +++---- tests/test_cases/issue_348/issue_348.py | 53 ++++++++++------------- tests/test_cases/issue_588/issue_588.py | 16 +++---- tests/test_cases/issue_768_a/issue_768.py | 6 +-- tests/test_cases/issue_768_b/issue_768.py | 6 +-- tests/test_cases/issue_857/issue_857.py | 5 +-- tests/test_cases/issue_892/issue_892.py | 9 ++-- tests/test_cases/issue_893/issue_893.py | 8 ++-- tests/test_cases/issue_957/issue_957.py | 12 ++--- 13 files changed, 98 insertions(+), 129 deletions(-) diff --git a/tests/test_cases/issue_120/issue_120.py b/tests/test_cases/issue_120/issue_120.py index ba289090..d66d3083 100644 --- a/tests/test_cases/issue_120/issue_120.py +++ b/tests/test_cases/issue_120/issue_120.py @@ -3,46 +3,42 @@ import cocotb from cocotb.clock import Clock from cocotb.triggers import RisingEdge, ReadOnly -from cocotb.result import TestFailure -@cocotb.coroutine -def send_data(dut): +async def send_data(dut): dut.stream_in_valid = 1 - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) dut.stream_in_valid = 0 -@cocotb.coroutine -def monitor(dut): +async def monitor(dut): for i in range(4): - yield RisingEdge(dut.clk) - yield ReadOnly() - if not dut.stream_in_valid.value.integer: - raise TestFailure("stream_in_valid should be high on the 5th cycle") + await RisingEdge(dut.clk) + await ReadOnly() + assert dut.stream_in_valid.value.integer, "stream_in_valid should be high on the 5th cycle" # Cadence simulators: "Unable set up RisingEdge(...) Trigger" with VHDL (see #1076) @cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else False) -def issue_120_scheduling(dut): +async def issue_120_scheduling(dut): - cocotb.fork(Clock(dut.clk, 2500).start()) + cocotb.fork(Clock(dut.clk, 10, 'ns').start()) cocotb.fork(monitor(dut)) - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) # First attempt, not from coroutine - works as expected for i in range(2): - dut.stream_in_valid = 1 - yield RisingEdge(dut.clk) - dut.stream_in_valid = 0 + dut.stream_in_valid <= 1 + await RisingEdge(dut.clk) + dut.stream_in_valid <= 0 - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) # Failure - we don't drive valid on the rising edge even though # behaviour should be identical to the above - yield send_data(dut) - dut.stream_in_valid = 1 - yield RisingEdge(dut.clk) - dut.stream_in_valid = 0 + await send_data(dut) + dut.stream_in_valid <= 1 + await RisingEdge(dut.clk) + dut.stream_in_valid <= 0 - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) diff --git a/tests/test_cases/issue_1279/issue_1279.py b/tests/test_cases/issue_1279/issue_1279.py index 6104969f..d573233f 100644 --- a/tests/test_cases/issue_1279/issue_1279.py +++ b/tests/test_cases/issue_1279/issue_1279.py @@ -9,9 +9,9 @@ expect_error=cocotb.result.SimFailure, stage=1, ) -def test_sim_failure_a(dut): +async def test_sim_failure_a(dut): # invoke a deadlock, as nothing is driving this clock - yield cocotb.triggers.RisingEdge(dut.clk) + await cocotb.triggers.RisingEdge(dut.clk) @cocotb.test( @@ -19,6 +19,5 @@ def test_sim_failure_a(dut): expect_error=cocotb.result.SimFailure, stage=2, ) -def test_sim_failure_b(dut): - yield cocotb.triggers.NullTrigger() - raise cocotb.result.TestFailure("This test should never run") +async def test_sim_failure_b(dut): + assert False, "This test should never run" diff --git a/tests/test_cases/issue_142/issue_142.py b/tests/test_cases/issue_142/issue_142.py index d0b287bd..14a0c685 100644 --- a/tests/test_cases/issue_142/issue_142.py +++ b/tests/test_cases/issue_142/issue_142.py @@ -3,29 +3,27 @@ import cocotb from cocotb.clock import Clock from cocotb.triggers import RisingEdge -from cocotb.result import TestFailure from cocotb.binary import BinaryValue # Cadence simulators: "Unable set up RisingEdge(...) Trigger" with VHDL (see #1076) @cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else False) -def issue_142_overflow_error(dut): +async def issue_142_overflow_error(dut): """Tranparently convert ints too long to pass through the GPI interface natively into BinaryValues""" - cocotb.fork(Clock(dut.clk, 2500).start()) + cocotb.fork(Clock(dut.clk, 10, 'ns').start()) def _compare(value): - if int(dut.stream_in_data_wide.value) != int(value): - raise TestFailure("Expecting 0x%x but got 0x%x on %s" % ( - int(value), int(dut.stream_in_data_wide.value), - str(dut.stream_in_data_wide))) + assert int(dut.stream_in_data_wide.value) == int(value), "Expecting 0x%x but got 0x%x on %s" % ( + int(value), int(dut.stream_in_data_wide.value), + str(dut.stream_in_data_wide)) # Wider values are transparently converted to BinaryValues for value in [0, 0x7FFFFFFF, 0x7FFFFFFFFFFF, BinaryValue(0x7FFFFFFFFFFFFF,len(dut.stream_in_data_wide),bigEndian=False)]: dut.stream_in_data_wide <= value - yield RisingEdge(dut.clk) + await RisingEdge(dut.clk) _compare(value) - dut.stream_in_data_wide = value - yield RisingEdge(dut.clk) + dut.stream_in_data_wide <= value + await RisingEdge(dut.clk) _compare(value) diff --git a/tests/test_cases/issue_253/issue_253.py b/tests/test_cases/issue_253/issue_253.py index 7f66294c..0a93c7ac 100644 --- a/tests/test_cases/issue_253/issue_253.py +++ b/tests/test_cases/issue_253/issue_253.py @@ -2,31 +2,27 @@ import cocotb from cocotb.triggers import Timer -from cocotb.result import TestFailure -@cocotb.coroutine -def toggle_clock(dut): - dut.clk = 0 - yield Timer(10) - if dut.clk.value.integer != 0: - raise TestFailure("Clock not set to 0 as expected") - dut.clk = 1 - yield Timer(10) - if dut.clk.value.integer != 1: - raise TestFailure("Clock not set to 1 as expected") +async def toggle_clock(dut): + dut.clk <= 0 + await Timer(10, 'ns') + assert dut.clk.value.integer == 0, "Clock not set to 0 as expected" + dut.clk <= 1 + await Timer(10, 'ns') + assert dut.clk.value.integer == 1, "Clock not set to 1 as expected" @cocotb.test() -def issue_253_empty(dut): - yield toggle_clock(dut) +async def issue_253_empty(dut): + await toggle_clock(dut) @cocotb.test() -def issue_253_none(dut): - yield toggle_clock(dut) +async def issue_253_none(dut): + await toggle_clock(dut) @cocotb.test() -def issue_253_notset(dut): - yield toggle_clock(dut) +async def issue_253_notset(dut): + await toggle_clock(dut) diff --git a/tests/test_cases/issue_330/issue_330.py b/tests/test_cases/issue_330/issue_330.py index 8e482a15..583f0d39 100644 --- a/tests/test_cases/issue_330/issue_330.py +++ b/tests/test_cases/issue_330/issue_330.py @@ -2,32 +2,28 @@ import cocotb import logging -from cocotb.triggers import Timer -from cocotb.result import TestFailure -@cocotb.test(skip=cocotb.SIM_NAME in ["Icarus Verilog"]) -def issue_330_direct(dut): +@cocotb.test(expect_error=AttributeError if cocotb.SIM_NAME in ["Icarus Verilog"] else ()) +async def issue_330_direct(dut): """ Access a structure """ tlog = logging.getLogger("cocotb.test") - yield Timer(10) structure = dut.inout_if tlog.info("Value of inout_if => a_in = %s ; b_out = %s" % (structure.a_in.value, structure.b_out.value)) -@cocotb.test(skip=cocotb.SIM_NAME in ["Icarus Verilog"]) -def issue_330_iteration(dut): +@cocotb.test(expect_error=AttributeError if cocotb.SIM_NAME in ["Icarus Verilog"] else ()) +async def issue_330_iteration(dut): """ Access a structure via issue_330_iteration """ tlog = logging.getLogger("cocotb.test") - yield Timer(10) structure = dut.inout_if @@ -36,5 +32,4 @@ def issue_330_iteration(dut): tlog.info("Found %s" % member._path) count += 1 - if count != 2: - raise TestFailure("There should have been two members of the structure") + assert count == 2, "There should have been two members of the structure" diff --git a/tests/test_cases/issue_348/issue_348.py b/tests/test_cases/issue_348/issue_348.py index e6c4d948..e6e7d07a 100644 --- a/tests/test_cases/issue_348/issue_348.py +++ b/tests/test_cases/issue_348/issue_348.py @@ -1,27 +1,23 @@ import cocotb from cocotb.log import SimLog from cocotb.triggers import Timer, Edge, RisingEdge, FallingEdge -from cocotb.result import TestFailure -@cocotb.coroutine -def clock_gen(signal, num): +async def clock_gen(signal, num): for x in range(num): signal <= 0 - yield Timer(500) + await Timer(5, 'ns') signal <= 1 - yield Timer(500) + await Timer(5, 'ns') -@cocotb.coroutine -def signal_mon(signal, idx, edge): - log = SimLog("cocotb.signal_mon.%d.%s" % (idx, signal._name)) - value = signal.value - +async def signal_mon(signal, idx, edge): + _ = SimLog("cocotb.signal_mon.%d.%s" % (idx, signal._name)) + _ = signal.value edges = 0 while True: - yield edge(signal) + await edge(signal) edges += 1 return edges @@ -34,43 +30,40 @@ def __init__(self, edge, signal): self.monitor_edges = [0, 0] self.signal = signal - @cocotb.coroutine - def signal_mon(self, signal, idx, edge): + async def signal_mon(self, signal, idx, edge): while True: - yield edge(signal) + await edge(signal) self.monitor_edges[idx] += 1 - @cocotb.coroutine - def start(self): + async def start(self): clock_edges = 10 cocotb.fork(clock_gen(self.signal, clock_edges)) - first = cocotb.fork(self.signal_mon(self.signal, 0, self.edge_type)) - second = cocotb.fork(self.signal_mon(self.signal, 1, self.edge_type)) + _ = cocotb.fork(self.signal_mon(self.signal, 0, self.edge_type)) + _ = cocotb.fork(self.signal_mon(self.signal, 1, self.edge_type)) - yield Timer(10000) + await Timer(100, 'ns') for mon in self.monitor_edges: - if not mon: - raise TestFailure("Monitor saw nothing") + assert mon, "Monitor saw nothing" # Cadence simulators: "Unable set up RisingEdge(ModifiableObject(sample_module.clk)) Trigger" with VHDL (see #1076) -@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else False) -def issue_348_rising(dut): +@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else ()) +async def issue_348_rising(dut): """ Start two monitors on RisingEdge """ - yield DualMonitor(RisingEdge, dut.clk).start() + await DualMonitor(RisingEdge, dut.clk).start() # Cadence simulators: "Unable set up FallingEdge(ModifiableObject(sample_module.clk)) Trigger" with VHDL (see #1076) -@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else False) -def issue_348_falling(dut): +@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else ()) +async def issue_348_falling(dut): """ Start two monitors on FallingEdge """ - yield DualMonitor(FallingEdge, dut.clk).start() + await DualMonitor(FallingEdge, dut.clk).start() # Cadence simulators: "Unable set up Edge(ModifiableObject(sample_module.clk)) Trigger" with VHDL (see #1076) -@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else False) -def issue_348_either(dut): +@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else ()) +async def issue_348_either(dut): """ Start two monitors on Edge """ - yield DualMonitor(Edge, dut.clk).start() + await DualMonitor(Edge, dut.clk).start() diff --git a/tests/test_cases/issue_588/issue_588.py b/tests/test_cases/issue_588/issue_588.py index 9c9be953..71909515 100644 --- a/tests/test_cases/issue_588/issue_588.py +++ b/tests/test_cases/issue_588/issue_588.py @@ -2,28 +2,28 @@ # This is a very simple test; it just makes sure we can yield a list of both. import cocotb -from cocotb import triggers, result, utils +from cocotb import triggers, utils -@cocotb.coroutine -def sample_coroutine(dut): +async def sample_coroutine(dut): """ Very simple coroutine that waits 5 ns.""" - yield triggers.Timer(5, "ns") + await triggers.Timer(5, "ns") dut._log.info("Sample coroutine yielded.") @cocotb.test() -def issue_588_coroutine_list(dut): +async def issue_588_coroutine_list(dut): """ Yield a list of triggers and coroutines.""" # Record simulation time. current_time = utils.get_sim_time("ns") # Yield a list, containing a RisingEdge trigger and a coroutine. - yield [sample_coroutine(dut), triggers.Timer(100, "ns")] + coro = cocotb.fork(sample_coroutine(dut)) + await triggers.First(coro, triggers.Timer(100, "ns")) + coro.kill() # Make sure that only 5 ns passed, because the sample coroutine # terminated first. new_time = utils.get_sim_time("ns") - if int(new_time - current_time) != 5: - raise result.TestFailure("Did not yield coroutine in list.") + assert int(new_time - current_time) == 5, "Did not yield coroutine in list." diff --git a/tests/test_cases/issue_768_a/issue_768.py b/tests/test_cases/issue_768_a/issue_768.py index a3158b2d..c4b320f0 100644 --- a/tests/test_cases/issue_768_a/issue_768.py +++ b/tests/test_cases/issue_768_a/issue_768.py @@ -14,8 +14,8 @@ @cocotb.test() -def test(dut): +async def test(dut): dut.stream_in_data.setimmediatevalue(value) - yield Timer(1) + await Timer(1, 'step') assert dut.stream_in_data.value == 0 - yield ReadOnly() + await ReadOnly() diff --git a/tests/test_cases/issue_768_b/issue_768.py b/tests/test_cases/issue_768_b/issue_768.py index f751eff7..ed23e1eb 100644 --- a/tests/test_cases/issue_768_b/issue_768.py +++ b/tests/test_cases/issue_768_b/issue_768.py @@ -14,8 +14,8 @@ @cocotb.test() -def do_test(dut): +async def do_test(dut): dut.stream_in_data.setimmediatevalue(value) - yield Timer(1) + await Timer(1, 'step') assert dut.stream_in_data.value == 0 - yield ReadOnly() + await ReadOnly() diff --git a/tests/test_cases/issue_857/issue_857.py b/tests/test_cases/issue_857/issue_857.py index 7d4acd94..fbf98337 100644 --- a/tests/test_cases/issue_857/issue_857.py +++ b/tests/test_cases/issue_857/issue_857.py @@ -3,9 +3,8 @@ import cocotb.triggers -@cocotb.coroutine -def dummy_coroutine(dut): - yield cocotb.triggers.Timer(10, "ns") +async def dummy_coroutine(dut): + await cocotb.triggers.Timer(10, "ns") # Try to instantiate the TestFactory class using its full specifier name. diff --git a/tests/test_cases/issue_892/issue_892.py b/tests/test_cases/issue_892/issue_892.py index 15b6e087..1e2727c5 100644 --- a/tests/test_cases/issue_892/issue_892.py +++ b/tests/test_cases/issue_892/issue_892.py @@ -3,13 +3,12 @@ from cocotb.result import TestSuccess -@cocotb.coroutine -def raise_test_success(): - yield Timer(1, units='ns') +async def raise_test_success(): + await Timer(1, units='ns') raise TestSuccess("TestSuccess") @cocotb.test() -def error_test(dut): +async def error_test(dut): cocotb.fork(raise_test_success()) - yield Timer(10, units='ns') + await Timer(10, units='ns') diff --git a/tests/test_cases/issue_893/issue_893.py b/tests/test_cases/issue_893/issue_893.py index 5fd8ff9d..59b25f04 100644 --- a/tests/test_cases/issue_893/issue_893.py +++ b/tests/test_cases/issue_893/issue_893.py @@ -2,13 +2,11 @@ from cocotb.triggers import Timer -@cocotb.coroutine -def coroutine_with_undef(): +async def coroutine_with_undef(): new_variable = undefined_variable # noqa - yield Timer(1, units='ns') @cocotb.test(expect_error=True) -def fork_erroring_coroutine(dut): +async def fork_erroring_coroutine(dut): cocotb.fork(coroutine_with_undef()) - yield Timer(10, units='ns') + await Timer(10, units='ns') diff --git a/tests/test_cases/issue_957/issue_957.py b/tests/test_cases/issue_957/issue_957.py index 15b780de..2f42028b 100644 --- a/tests/test_cases/issue_957/issue_957.py +++ b/tests/test_cases/issue_957/issue_957.py @@ -2,19 +2,15 @@ from cocotb.triggers import Timer, RisingEdge, First -@cocotb.coroutine -def wait_edge(dut): +async def wait_edge(dut): # this trigger never fires - yield First( - RisingEdge(dut.stream_out_ready) - ) + await First(RisingEdge(dut.stream_out_ready)) @cocotb.test() -def test1(dut): +async def test1(dut): cocotb.fork(wait_edge(dut)) - - yield Timer(1000) + await Timer(10, 'ns') test2 = test1 From 38f199c8fbcaaa6147f3a5da815880cb8d16f36e Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 2 Dec 2020 08:00:11 +0100 Subject: [PATCH 2595/2656] Provide Version classes to compare simulator versions (#1987) Provide `Version` classes to compare simulator versions Currently, these are directly using `distutils.version.LooseVersion`. The intended use case is for setting `expect_errors` for testcases. Closes #1951 --- .github/workflows/regression-tests.yml | 3 - cocotb/_sim_versions.py | 119 ++++++++++++++++++ setup.cfg | 1 + .../test_discovery/test_discovery.py | 9 +- 4 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 cocotb/_sim_versions.py diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml index 37e091b1..5de897d4 100644 --- a/.github/workflows/regression-tests.yml +++ b/.github/workflows/regression-tests.yml @@ -95,7 +95,6 @@ jobs: lang: verilog python-version: 3.8 os: ubuntu-20.04 - may_fail: true # Test GHDL on Ubuntu @@ -136,7 +135,6 @@ jobs: lang: verilog python-version: 3.8 os: macos-latest - may_fail: true - sim: icarus # Icarus homebrew stable sim-version: homebrew @@ -149,7 +147,6 @@ jobs: lang: verilog python-version: 3.8 os: windows-latest - may_fail: true - sim: icarus # Icarus windows 10.3 from source sim-version: v10_3 diff --git a/cocotb/_sim_versions.py b/cocotb/_sim_versions.py new file mode 100644 index 00000000..2288c8e5 --- /dev/null +++ b/cocotb/_sim_versions.py @@ -0,0 +1,119 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause + +""" +Classes to compare simulation versions. + +These are for cocotb-internal use only. + +.. warning:: + These classes silently allow comparing versions of different simulators. +""" + +from distutils.version import LooseVersion + + +class ActivehdlVersion(LooseVersion): + """Version numbering class for Aldec Active-HDL. + + NOTE: unsupported versions exist, e.g. + ActivehdlVersion("10.5a.12.6914") > ActivehdlVersion("10.5.216.6767") + """ + pass + + +class CvcVersion(LooseVersion): + """Version numbering class for Tachyon DA CVC. + + Example: + >>> CvcVersion("OSS_CVC_7.00b-x86_64-rhel6x of 07/07/14 (Linux-elf)") > CvcVersion("OSS_CVC_7.00a-x86_64-rhel6x of 07/07/14 (Linux-elf)") + True + """ + pass + + +class GhdlVersion(LooseVersion): + """Version numbering class for GHDL.""" + pass + + +class IcarusVersion(LooseVersion): + """Version numbering class for Icarus Verilog. + + Example: + >>> IcarusVersion("11.0 (devel)") > IcarusVersion("10.3 (stable)") + True + >>> IcarusVersion("10.3 (stable)") <= IcarusVersion("10.3 (stable)") + True + """ + pass + + +class ModelsimVersion(LooseVersion): + """Version numbering class for Mentor ModelSim.""" + pass + + +class QuestaVersion(LooseVersion): + """Version numbering class for Mentor Questa. + + Example: + >>> QuestaVersion("10.7c 2018.08") > QuestaVersion("10.7b 2018.06") + True + >>> QuestaVersion("2020.1 2020.01") > QuestaVersion("10.7c 2018.08") + True + """ + pass + + +class RivieraVersion(LooseVersion): + """Version numbering class for Aldec Riviera-PRO. + + Example: + >>> RivieraVersion("2019.10.138.7537") == RivieraVersion("2019.10.138.7537") + True + """ + pass + + +class VcsVersion(LooseVersion): + """Version numbering class for Synopsys VCS. + + Example: + >>> VcsVersion("Q-2020.03-1_Full64") > VcsVersion("K-2015.09_Full64") + True + """ + pass + + +class VerilatorVersion(LooseVersion): + """Version numbering class for Verilator. + + Example: + >>> VerilatorVersion("4.032 2020-04-04") > VerilatorVersion("4.031 devel") + True + """ + pass + + +class XceliumVersion(LooseVersion): + """Version numbering class for Cadence Xcelium. + + Example: + >>> XceliumVersion("20.06-g183") > XceliumVersion("20.03-s002") + True + >>> XceliumVersion("20.07-e501") > XceliumVersion("20.06-g183") + True + """ + pass + + +class IusVersion(XceliumVersion): # inherit everything from Xcelium + """Version numbering class for Cadence IUS. + + Example: + >>> IusVersion("15.20-s050") > IusVersion("15.20-s049") + True + """ + pass diff --git a/setup.cfg b/setup.cfg index c7c452ce..aba87846 100644 --- a/setup.cfg +++ b/setup.cfg @@ -33,5 +33,6 @@ testpaths = tests/pytest cocotb/utils.py cocotb/binary.py + cocotb/_sim_versions.py # log_cli = true # log_cli_level = DEBUG diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index d269ad2b..08ea0e3b 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -30,6 +30,7 @@ from cocotb.triggers import Timer from cocotb.result import TestError, TestFailure from cocotb.handle import IntegerObject, ConstantObject, HierarchyObject, StringObject +from cocotb._sim_versions import IcarusVersion @cocotb.test() @@ -89,8 +90,8 @@ async def access_signal(dut): @cocotb.test( - # Icarus 10.3 doesn't support bit-selects, see https://github.com/steveicarus/iverilog/issues/323 - expect_error=IndexError if cocotb.SIM_NAME.lower().startswith("icarus") else False, + # Icarus up to (including) 10.3 doesn't support bit-selects, see https://github.com/steveicarus/iverilog/issues/323 + expect_error=IndexError if (cocotb.SIM_NAME.lower().startswith("icarus") and (IcarusVersion(cocotb.SIM_VERSION) <= IcarusVersion("10.3 (stable)"))) else False, skip=cocotb.LANGUAGE in ["vhdl"]) async def access_single_bit(dut): """Access a single bit in a vector of the DUT""" @@ -107,8 +108,8 @@ async def access_single_bit(dut): @cocotb.test( - # Icarus 10.3 doesn't support bit-selects, see https://github.com/steveicarus/iverilog/issues/323 - expect_error=IndexError if cocotb.SIM_NAME.lower().startswith("icarus") else False, + # Icarus up to (including) 10.3 doesn't support bit-selects, see https://github.com/steveicarus/iverilog/issues/323 + expect_error=IndexError if (cocotb.SIM_NAME.lower().startswith("icarus") and (IcarusVersion(cocotb.SIM_VERSION) <= IcarusVersion("10.3 (stable)"))) else False, skip=cocotb.LANGUAGE in ["vhdl"]) async def access_single_bit_assignment(dut): """Access a single bit in a vector of the DUT using the assignment mechanism""" From 3f7627fbf68b5430cd757290a3ac0de7021a583b Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Thu, 3 Dec 2020 14:58:20 -0600 Subject: [PATCH 2596/2656] Documentation cleanup (#2254) * Run doxygen -u on Doxyfile * Use breathe directives rather than breathe-apidoc * Remove unused GPI_RET macro function * Remove unnecessary macro definitions for C++ * Remove __attribute__ define * Silence C declaration parser warnings * Fix broken links in scheduler * Apply suggestions from code review Co-authored-by: Marlon James Co-authored-by: Marlon James --- cocotb/scheduler.py | 4 +- cocotb/share/include/gpi.h | 26 +- documentation/.gitignore | 2 +- documentation/Doxyfile | 301 +++++++++++++------ documentation/source/conf.py | 12 +- documentation/source/index.rst | 2 +- documentation/source/library_reference_c.rst | 15 +- documentation/source/release_notes.rst | 2 +- 8 files changed, 234 insertions(+), 130 deletions(-) diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 18ac2e23..4b8518fb 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -184,11 +184,11 @@ class Scheduler: - Any pending writes are cached and do not happen immediately ReadOnly mode - - Corresponds to :any:`cbReadOnlySynch` (VPI) or :any:`vhpiCbLastKnownDeltaCycle` + - Corresponds to ``cbReadOnlySynch`` (VPI) or ``vhpiCbRepEndOfTimeStep`` (VHPI). In this state we are not allowed to perform writes. Write mode - - Corresponds to :any:`cbReadWriteSynch` (VPI) or :c:macro:`vhpiCbEndOfProcesses` (VHPI) + - Corresponds to ``cbReadWriteSynch`` (VPI) or ``vhpiCbRepLastKnownDeltaCycle`` (VHPI) In this mode we play back all the cached write updates. We can legally transition from Normal to Write by registering a :class:`~cocotb.triggers.ReadWrite` diff --git a/cocotb/share/include/gpi.h b/cocotb/share/include/gpi.h index 46740129..cb3d44d4 100644 --- a/cocotb/share/include/gpi.h +++ b/cocotb/share/include/gpi.h @@ -66,18 +66,6 @@ we have to create a process with the signal on the sensitivity list to imitate a #include -#ifdef __cplusplus -# define EXTERN_C_START extern "C" { -# define EXTERN_C_END } -#else -# define EXTERN_C_START -# define EXTERN_C_END -#endif - -#ifndef __GNUC__ -# undef __attribute__ -# define __attribute__(x) -#endif /* * Declare the handle types. @@ -110,7 +98,9 @@ we have to create a process with the signal on the sensitivity list to imitate a typedef struct GpiIterator *gpi_iterator_hdl; #endif -EXTERN_C_START +#ifdef __cplusplus +extern "C" { +#endif typedef enum gpi_event_e { SIM_INFO = 0, @@ -265,12 +255,8 @@ GPI_EXPORT void *gpi_get_callback_data(gpi_cb_hdl gpi_hdl); // Returns the number of libs GPI_EXPORT size_t gpi_print_registered_impl(void); -#define GPI_RET(_code) \ - if (_code == 1) \ - return 0; \ - else \ - return -1 - -EXTERN_C_END +#ifdef __cplusplus +} +#endif #endif /* COCOTB_GPI_H_ */ diff --git a/documentation/.gitignore b/documentation/.gitignore index 9504ecc8..4d9b5b56 100644 --- a/documentation/.gitignore +++ b/documentation/.gitignore @@ -1,3 +1,3 @@ .venv -source/generated +source/master-notes.rst source/doxygen diff --git a/documentation/Doxyfile b/documentation/Doxyfile index 56a79ad4..fb58ccf1 100644 --- a/documentation/Doxyfile +++ b/documentation/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.8.11 +# Doxyfile 1.8.20 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -17,11 +17,11 @@ # Project related configuration options #--------------------------------------------------------------------------- -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all text -# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv -# for the list of possible encodings. +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 @@ -93,6 +93,14 @@ ALLOW_UNICODE_NAMES = NO OUTPUT_LANGUAGE = English +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. @@ -179,6 +187,16 @@ SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus @@ -199,6 +217,14 @@ QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. @@ -226,16 +252,15 @@ TAB_SIZE = 4 # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines. +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) ALIASES = -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = - # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all @@ -264,17 +289,26 @@ OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: -# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: -# Fortran. In the later case the parser tries to guess whether the code is fixed -# or free formatted code, this is the default for Fortran type files), VHDL. For -# instance to make doxygen treat .inc files as Fortran files (default is PHP), -# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # @@ -285,7 +319,7 @@ EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. +# documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. @@ -293,6 +327,15 @@ EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or @@ -318,7 +361,7 @@ BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. @@ -404,6 +447,19 @@ TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 +# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which efficively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- @@ -424,6 +480,12 @@ EXTRACT_ALL = NO EXTRACT_PRIVATE = NO +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. @@ -478,8 +540,8 @@ HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO, these declarations will be -# included in the documentation. +# declarations. If set to NO, these declarations will be included in the +# documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO @@ -502,7 +564,7 @@ INTERNAL_DOCS = NO # names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. +# (including Cygwin) and Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = YES @@ -689,7 +751,7 @@ LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. @@ -721,7 +783,7 @@ WARNINGS = YES # will automatically be disabled. # The default value is: YES. -WARN_IF_UNDOCUMENTED = YES +WARN_IF_UNDOCUMENTED = NO # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters @@ -734,7 +796,8 @@ WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. # The default value is: NO. WARN_NO_PARAMDOC = NO @@ -771,12 +834,12 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = ../cocotb/share/lib/ ../cocotb/share/include/ +INPUT = ../cocotb/share/include/gpi.h # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# documentation (see: https://www.gnu.org/software/libiconv/) for the list of # possible encodings. # The default value is: UTF-8. @@ -793,8 +856,10 @@ INPUT_ENCODING = UTF-8 # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f, *.for, *.tcl, -# *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js. +# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), +# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen +# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, +# *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = @@ -949,7 +1014,7 @@ INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. +# entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO @@ -981,12 +1046,12 @@ SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version +# (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # @@ -1014,7 +1079,7 @@ VERBATIM_HEADERS = YES # rich C++ code for which doxygen's built-in parser lacks the necessary type # information. # Note: The availability of this option depends on whether or not doxygen was -# generated with the -Duse-libclang=ON option for CMake. +# generated with the -Duse_libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO @@ -1027,6 +1092,19 @@ CLANG_ASSISTED_PARSING = NO CLANG_OPTIONS = +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the "-p" option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -1145,7 +1223,7 @@ HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. @@ -1181,6 +1259,17 @@ HTML_COLORSTYLE_GAMMA = 80 HTML_TIMESTAMP = NO +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. @@ -1204,13 +1293,13 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# environment (see: https://developer.apple.com/xcode/), introduced with OSX +# 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1249,7 +1338,7 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output @@ -1280,7 +1369,7 @@ CHM_FILE = HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated -# (YES) or that it should be included in the master .chm file (NO). +# (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. @@ -1325,7 +1414,7 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1333,7 +1422,7 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1342,7 +1431,7 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1350,7 +1439,7 @@ QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1358,7 +1447,7 @@ QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = @@ -1442,6 +1531,17 @@ TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML @@ -1451,7 +1551,7 @@ EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # @@ -1462,8 +1562,14 @@ FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering +# https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path @@ -1490,8 +1596,8 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest @@ -1533,7 +1639,7 @@ MATHJAX_CODEFILE = SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a web server instead of a web client using Javascript. There +# implemented using a web server instead of a web client using JavaScript. There # are two flavors of web server based searching depending on the EXTERNAL_SEARCH # setting. When disabled, doxygen will generate a PHP script for searching and # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing @@ -1552,7 +1658,7 @@ SERVER_BASED_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). +# Xapian (see: https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1565,7 +1671,7 @@ EXTERNAL_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). See the section "External Indexing and +# Xapian (see: https://xapian.org/). See the section "External Indexing and # Searching" for details. # This tag requires that the tag SEARCHENGINE is set to YES. @@ -1617,21 +1723,35 @@ LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. # -# Note that when enabling USE_PDFLATEX this option is only used for generating -# bitmaps for formulas in the HTML output, but not in the Makefile that is -# written to the output directory. -# The default file is: latex. +# Note that when not enabling USE_PDFLATEX the default is latex when enabling +# USE_PDFLATEX the default is pdflatex and when in the later case latex is +# chosen this is overwritten by pdflatex. For specific output languages the +# default can have been set differently, this depends on the implementation of +# the output language. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate # index for LaTeX. +# Note: This tag is used in the Makefile / make.bat. +# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file +# (.tex). # The default file is: makeindex. # This tag requires that the tag GENERATE_LATEX is set to YES. MAKEINDEX_CMD_NAME = makeindex +# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to +# generate index for LaTeX. In case there is no backslash (\) as first character +# it will be automatically added in the LaTeX code. +# Note: This tag is used in the generated output file (.tex). +# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. +# The default value is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_MAKEINDEX_CMD = makeindex + # If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX # documents. This may be useful for small projects and may help to save some # trees in general. @@ -1716,9 +1836,11 @@ LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES -# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate -# the PDF file directly from the LaTeX files. Set this option to YES, to get a -# higher quality PDF documentation. +# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as +# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX +# files. Set this option to YES, to get a higher quality PDF documentation. +# +# See also section LATEX_CMD_NAME for selecting the engine. # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1752,7 +1874,7 @@ LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See -# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. # The default value is: plain. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1766,6 +1888,14 @@ LATEX_BIB_STYLE = plain LATEX_TIMESTAMP = NO +# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) +# path from which the emoji images will be read. If a relative path is entered, +# it will be relative to the LATEX_OUTPUT directory. If left blank the +# LATEX_OUTPUT directory will be used. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EMOJI_DIRECTORY = + #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- @@ -1805,9 +1935,9 @@ COMPACT_RTF = NO RTF_HYPERLINKS = NO -# Load stylesheet definitions from file. Syntax is similar to doxygen's config -# file, i.e. a series of assignments. You only have to provide replacements, -# missing definitions are set to their default value. +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# configuration file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. # # See also section "Doxygen usage" for information on how to generate the # default style sheet that doxygen normally uses. @@ -1816,8 +1946,8 @@ RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an RTF document. Syntax is -# similar to doxygen's config file. A template extensions file can be generated -# using doxygen -e rtf extensionFile. +# similar to doxygen's configuration file. A template extensions file can be +# generated using doxygen -e rtf extensionFile. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_EXTENSIONS_FILE = @@ -1903,6 +2033,13 @@ XML_OUTPUT = _xml XML_PROGRAMLISTING = NO +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# namespace members in file scope as well, matching the HTML output. +# The default value is: NO. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_NS_MEMB_FILE_SCOPE = NO + #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- @@ -1935,9 +2072,9 @@ DOCBOOK_PROGRAMLISTING = NO #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an -# AutoGen Definitions (see http://autogen.sf.net) file that captures the -# structure of the code including all documentation. Note that this feature is -# still experimental and incomplete at the moment. +# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# the structure of the code including all documentation. Note that this feature +# is still experimental and incomplete at the moment. # The default value is: NO. GENERATE_AUTOGEN_DEF = NO @@ -2104,12 +2241,6 @@ EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of 'which perl'). -# The default file (with absolute path) is: /usr/bin/perl. - -PERL_PATH = /usr/bin/perl - #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- @@ -2123,15 +2254,6 @@ PERL_PATH = /usr/bin/perl CLASS_DIAGRAMS = YES -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see: -# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. @@ -2361,6 +2483,11 @@ DIAFILE_DIRS = PLANTUML_JAR_PATH = +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + # When using plantuml, the specified paths are searched for files specified by # the !include statement in a plantuml block. @@ -2424,4 +2551,4 @@ GENERATE_LEGEND = YES # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_CLEANUP = YES \ No newline at end of file +DOT_CLEANUP = YES diff --git a/documentation/source/conf.py b/documentation/source/conf.py index 403a85d0..b3a3a31d 100644 --- a/documentation/source/conf.py +++ b/documentation/source/conf.py @@ -306,13 +306,11 @@ # -- Extra setup for C documentation with Doxygen and breathe ------------------ # see also https://breathe.readthedocs.io/en/latest/readthedocs.html +subprocess.run('doxygen', cwd='..') -env = os.environ.copy() -env['PATH'] += ':.venv/bin' -subprocess.call('doxygen', cwd='..') -subprocess.call(['breathe-apidoc', '-o', 'source/generated', 'source/doxygen/_xml', '-f'], env=env, cwd='..') - - +cpp_id_attributes = [ + 'GPI_EXPORT' +] breathe_projects = { "cocotb": "doxygen/_xml" } breathe_default_project = "cocotb" breathe_domain_by_extension = { @@ -344,5 +342,5 @@ in_progress_notes = subprocess.check_output(['towncrier', '--draft', '--name', 'cocotb', '--version', release], cwd='../..', universal_newlines=True) -with open('generated/master-notes.rst', 'w') as f: +with open('master-notes.rst', 'w') as f: f.write(in_progress_notes) diff --git a/documentation/source/index.rst b/documentation/source/index.rst index 65b6477d..cc62d96a 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -175,7 +175,7 @@ A test can spawn multiple coroutines, allowing for independent flows of executio building Python Code Library Reference - C/C++ Code Library Reference + GPI Library Reference simulator_support extensions diff --git a/documentation/source/library_reference_c.rst b/documentation/source/library_reference_c.rst index bd8a4e06..337c7c36 100644 --- a/documentation/source/library_reference_c.rst +++ b/documentation/source/library_reference_c.rst @@ -1,6 +1,6 @@ -************************ -C Code Library Reference -************************ +********************* +GPI Library Reference +********************* Cocotb contains a library called ``GPI`` (in directory :file:`cocotb/share/lib/gpi/`) written in C++ that is an abstraction layer for the VPI, VHPI, and FLI simulator interfaces. @@ -11,11 +11,4 @@ The interaction between Python and GPI is via a Python extension module called ` (in directory :file:`cocotb/share/lib/simulator/`) which provides routines for traversing the hierarchy, getting/setting an object's value, registering callbacks etc. -API Documentation -================= - -.. toctree:: - - generated/classlist - generated/filelist - generated/structlist +.. doxygenfile:: gpi.h diff --git a/documentation/source/release_notes.rst b/documentation/source/release_notes.rst index e3529bee..b75e0532 100644 --- a/documentation/source/release_notes.rst +++ b/documentation/source/release_notes.rst @@ -4,7 +4,7 @@ Release Notes All releases are available from the `GitHub Releases Page `_. -.. include:: generated/master-notes.rst +.. include:: master-notes.rst .. towncrier release notes start From 0312036e0dbc8be667285494ad6735474a16f65b Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 4 Dec 2020 08:09:56 +0100 Subject: [PATCH 2597/2656] Use Verilator's --timescale option (#2257) Closes #1640 --- cocotb/share/makefiles/simulators/Makefile.verilator | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.verilator b/cocotb/share/makefiles/simulators/Makefile.verilator index d35e441a..e7c83973 100644 --- a/cocotb/share/makefiles/simulators/Makefile.verilator +++ b/cocotb/share/makefiles/simulators/Makefile.verilator @@ -32,9 +32,7 @@ ifeq ($(VERILATOR_TRACE),1) EXTRA_ARGS += --trace --trace-structs endif -ifdef COCOTB_HDL_TIMEPRECISION - SIM_BUILD_FLAGS += -DVL_TIME_PRECISION_STR=$(COCOTB_HDL_TIMEPRECISION) -endif +EXTRA_ARGS += --timescale $(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) SIM_BUILD_FLAGS += -std=c++11 From d97e9414cf7721e3e1b0ef272cbda21f06521d62 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 4 Dec 2020 08:10:54 +0100 Subject: [PATCH 2598/2656] Add test for writing and reading from struct members (#2256) --- tests/designs/sample_module/sample_module.sv | 4 ++++ .../test_array_simple/test_array_simple.py | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/tests/designs/sample_module/sample_module.sv b/tests/designs/sample_module/sample_module.sv index 7823a343..de2d9795 100644 --- a/tests/designs/sample_module/sample_module.sv +++ b/tests/designs/sample_module/sample_module.sv @@ -136,6 +136,10 @@ assign logic_a = stream_in_valid; always@* logic_b = stream_in_valid; always@(posedge clk) logic_c <= stream_in_valid; +`ifndef __ICARUS__ +assign inout_if.b_out = inout_if.a_in; +`endif + reg _underscore_name; `ifdef __ICARUS__ // By default, a variable must be used in some way in order diff --git a/tests/test_cases/test_array_simple/test_array_simple.py b/tests/test_cases/test_array_simple/test_array_simple.py index e2a02b62..8523441d 100644 --- a/tests/test_cases/test_array_simple/test_array_simple.py +++ b/tests/test_cases/test_array_simple/test_array_simple.py @@ -176,6 +176,20 @@ async def test_ndim_array_indexes(dut): _check_value(tlog, dut.array_2d[1] , [0xDE, 0xAD, 0x12, 0xEF]) +@cocotb.test(expect_error=AttributeError if cocotb.SIM_NAME.lower().startswith(("icarus", "ghdl")) else ()) +async def test_struct(dut): + """Test setting and getting values of structs.""" + cocotb.fork(Clock(dut.clk, 1000, 'ns').start()) + dut.inout_if.a_in <= 1 + await Timer(1000, 'ns') + _check_value(tlog, dut.inout_if.a_in, 1) + _check_value(tlog, dut.inout_if.b_out, 1) + dut.inout_if.a_in <= 0 + await Timer(1000, 'ns') + _check_value(tlog, dut.inout_if.a_in, 0) + _check_value(tlog, dut.inout_if.b_out, 0) + + @contextlib.contextmanager def assert_raises(exc_type): try: From 2e99e31762556eaefd8619e41eb8ea6cd6497c45 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 23 Nov 2020 22:19:01 -0600 Subject: [PATCH 2599/2656] Fix test_failure --- tests/test_cases/test_failure/test_failure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases/test_failure/test_failure.py b/tests/test_cases/test_failure/test_failure.py index 77b2943d..c7629dfb 100644 --- a/tests/test_cases/test_failure/test_failure.py +++ b/tests/test_cases/test_failure/test_failure.py @@ -11,5 +11,5 @@ @cocotb.test() -async def test_fail(): +async def test_fail(_): assert False From 033fb612b020093ec9419c29af3579d92d045e4a Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 23 Nov 2020 22:24:34 -0600 Subject: [PATCH 2600/2656] Deprecate passing bool as expect_error in cocotb.test --- cocotb/decorators.py | 16 ++++++++++---- .../source/newsfragments/2117.removal.rst | 2 ++ tests/test_cases/issue_120/issue_120.py | 2 +- tests/test_cases/issue_142/issue_142.py | 2 +- tests/test_cases/issue_893/issue_893.py | 2 +- .../test_cases/test_cocotb/test_deprecated.py | 10 +++++++++ .../test_cocotb/test_generator_coroutines.py | 21 ++++++++++--------- tests/test_cases/test_cocotb/test_handle.py | 6 +++--- tests/test_cases/test_cocotb/test_tests.py | 6 +++--- .../test_cocotb/test_timing_triggers.py | 11 ++-------- .../test_discovery/test_discovery.py | 16 +++++++------- .../test_cases/test_external/test_external.py | 10 ++++----- 12 files changed, 59 insertions(+), 45 deletions(-) create mode 100644 documentation/source/newsfragments/2117.removal.rst diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 335ccd43..32934449 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -510,9 +510,8 @@ class test(coroutine, metaclass=_decorator_helper): expect_fail (bool, optional): Don't mark the result as a failure if the test fails. - expect_error (bool or exception type or tuple of exception types, optional): - If ``True``, consider this test passing if it raises *any* :class:`Exception`, and failing if it does not. - If given an exception type or tuple of exception types, catching *only* a listed exception type is considered passing. + expect_error (exception type or tuple of exception types, optional): + Mark the result as a pass only if one of the exception types is raised in the test. This is primarily for cocotb internal regression use for when a simulator error is expected. Users are encouraged to use the following idiom instead:: @@ -529,6 +528,10 @@ async def my_test(dut): .. versionchanged:: 1.3 Specific exception types can be expected + .. deprecated:: 1.5 + Passing a :class:`bool` value is now deprecated. + Pass a specific :class:`Exception` or a tuple of Exceptions instead. + skip (bool, optional): Don't execute this test as part of the regression. Test can still be run manually by setting :make:var:`TESTCASE`. @@ -540,7 +543,7 @@ async def my_test(dut): _id_count = 0 # used by the RegressionManager to sort tests in definition order def __init__(self, f, timeout_time=None, timeout_unit="step", - expect_fail=False, expect_error=False, + expect_fail=False, expect_error=(), skip=False, stage=None): if timeout_unit is None: @@ -571,6 +574,11 @@ async def f(*args, **kwargs): self.timeout_time = timeout_time self.timeout_unit = timeout_unit self.expect_fail = expect_fail + if isinstance(expect_error, bool): + warnings.warn( + "Passing bool values to `except_error` option of `cocotb.test` is deprecated. " + "Pass a specific Exception type instead", + DeprecationWarning, stacklevel=2) if expect_error is True: expect_error = (Exception,) elif expect_error is False: diff --git a/documentation/source/newsfragments/2117.removal.rst b/documentation/source/newsfragments/2117.removal.rst new file mode 100644 index 00000000..92bcd21d --- /dev/null +++ b/documentation/source/newsfragments/2117.removal.rst @@ -0,0 +1,2 @@ +Passing :class:`bool` values to ``expect_error`` option of :class:`cocotb.test` is deprecated. +Pass a specific :class:`Exception` or a tuple of Exceptions instead. diff --git a/tests/test_cases/issue_120/issue_120.py b/tests/test_cases/issue_120/issue_120.py index d66d3083..95ac0aa1 100644 --- a/tests/test_cases/issue_120/issue_120.py +++ b/tests/test_cases/issue_120/issue_120.py @@ -19,7 +19,7 @@ async def monitor(dut): # Cadence simulators: "Unable set up RisingEdge(...) Trigger" with VHDL (see #1076) -@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else False) +@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else ()) async def issue_120_scheduling(dut): cocotb.fork(Clock(dut.clk, 10, 'ns').start()) diff --git a/tests/test_cases/issue_142/issue_142.py b/tests/test_cases/issue_142/issue_142.py index 14a0c685..1d1a730d 100644 --- a/tests/test_cases/issue_142/issue_142.py +++ b/tests/test_cases/issue_142/issue_142.py @@ -7,7 +7,7 @@ # Cadence simulators: "Unable set up RisingEdge(...) Trigger" with VHDL (see #1076) -@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else False) +@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else ()) async def issue_142_overflow_error(dut): """Tranparently convert ints too long to pass through the GPI interface natively into BinaryValues""" diff --git a/tests/test_cases/issue_893/issue_893.py b/tests/test_cases/issue_893/issue_893.py index 59b25f04..7df83721 100644 --- a/tests/test_cases/issue_893/issue_893.py +++ b/tests/test_cases/issue_893/issue_893.py @@ -6,7 +6,7 @@ async def coroutine_with_undef(): new_variable = undefined_variable # noqa -@cocotb.test(expect_error=True) +@cocotb.test(expect_error=NameError) async def fork_erroring_coroutine(dut): cocotb.fork(coroutine_with_undef()) await Timer(10, units='ns') diff --git a/tests/test_cases/test_cocotb/test_deprecated.py b/tests/test_cases/test_cocotb/test_deprecated.py index 10ba37a7..a726f9e4 100644 --- a/tests/test_cases/test_cocotb/test_deprecated.py +++ b/tests/test_cases/test_cocotb/test_deprecated.py @@ -106,3 +106,13 @@ async def test_handle_compat_mapping(dut): with assert_deprecated(): dut.fullname = "myfullname" assert dut.fullname == "myfullname" + + +@cocotb.test() +async def test_expect_error_bool_deprecated(_): + async def t(): + pass + with assert_deprecated(): + cocotb.test(expect_error=True)(t) + with assert_deprecated(): + cocotb.test(expect_error=False)(t) diff --git a/tests/test_cases/test_cocotb/test_generator_coroutines.py b/tests/test_cases/test_cocotb/test_generator_coroutines.py index 4a89eba7..718da314 100644 --- a/tests/test_cases/test_cocotb/test_generator_coroutines.py +++ b/tests/test_cases/test_cocotb/test_generator_coroutines.py @@ -6,7 +6,7 @@ """ import cocotb from cocotb.triggers import Timer -from common import clock_gen, _check_traceback +from common import clock_gen, _check_traceback, assert_raises import textwrap @@ -91,23 +91,24 @@ def test_yield_list(dut): @cocotb.coroutine -def syntax_error(): +def erroring_coro(): yield Timer(100) fail # noqa -@cocotb.test(expect_error=True) -def test_coroutine_syntax_error(dut): - """Syntax error in a coroutine that we yield""" +@cocotb.test() +def test_coroutine_error(dut): + """Error in a coroutine that we yield""" yield clock_gen(dut.clk) - yield syntax_error() + with assert_raises(NameError): + yield erroring_coro() -@cocotb.test(expect_error=True) -def test_fork_syntax_error(dut): - """Syntax error in a coroutine that we fork""" +@cocotb.test(expect_error=NameError) +def test_fork_error(dut): + """Error in a coroutine that we fork""" yield clock_gen(dut.clk) - cocotb.fork(syntax_error()) + cocotb.fork(erroring_coro()) yield clock_gen(dut.clk) diff --git a/tests/test_cases/test_cocotb/test_handle.py b/tests/test_cases/test_cocotb/test_handle.py index f33ed1c3..8c0dd53e 100644 --- a/tests/test_cases/test_cocotb/test_handle.py +++ b/tests/test_cases/test_cocotb/test_handle.py @@ -66,7 +66,7 @@ async def test_delayed_assignment_still_errors(dut): dut.stream_in_int <= [] -@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) +@cocotb.test(expect_error=AttributeError if cocotb.SIM_NAME in ["Icarus Verilog"] else ()) async def test_integer(dut): """ Test access to integers @@ -83,7 +83,7 @@ async def test_integer(dut): assert got_in == got_out, "stream_in_int and stream_out_int should not match" -@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) +@cocotb.test(expect_error=AttributeError if cocotb.SIM_NAME in ["Icarus Verilog"] else ()) async def test_real_assign_double(dut): """ Assign a random floating point value, read it back from the DUT and check @@ -102,7 +102,7 @@ async def test_real_assign_double(dut): assert got == val, "Values didn't match!" -@cocotb.test(expect_error=cocotb.SIM_NAME in ["Icarus Verilog"]) +@cocotb.test(expect_error=AttributeError if cocotb.SIM_NAME in ["Icarus Verilog"] else ()) async def test_real_assign_int(dut): """Assign a random integer value to ensure we can write types convertible to int, read it back from the DUT and check it matches what we assigned. diff --git a/tests/test_cases/test_cocotb/test_tests.py b/tests/test_cases/test_cocotb/test_tests.py index 267b42f9..761c203e 100644 --- a/tests/test_cases/test_cocotb/test_tests.py +++ b/tests/test_cases/test_cocotb/test_tests.py @@ -14,9 +14,9 @@ from common import clock_gen -@cocotb.test(expect_error=True) -async def test_syntax_error(dut): - """Syntax error in the test""" +@cocotb.test(expect_error=NameError) +async def test_error(dut): + """Error in the test""" await clock_gen(dut.clk) fail # noqa diff --git a/tests/test_cases/test_cocotb/test_timing_triggers.py b/tests/test_cases/test_cocotb/test_timing_triggers.py index b8a5a09d..9d60dcdc 100644 --- a/tests/test_cases/test_cocotb/test_timing_triggers.py +++ b/tests/test_cases/test_cocotb/test_timing_triggers.py @@ -113,10 +113,8 @@ async def do_test_afterdelay_in_readonly(dut, delay): exited = True -@cocotb.test(expect_error=True, - skip=cocotb.LANGUAGE in ["vhdl"] and cocotb.SIM_NAME.lower().startswith(("riviera")), # gh-1245 +@cocotb.test(expect_error=TriggerException if cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")) else (), expect_fail=cocotb.SIM_NAME.lower().startswith(("icarus", - "riviera", "modelsim", "ncsim", "xmsim"))) @@ -131,12 +129,7 @@ async def test_readwrite_in_readonly(dut): assert exited -@cocotb.test(expect_error=True, - expect_fail=cocotb.SIM_NAME.lower().startswith(("icarus", - "riviera", - "modelsim", - "ncsim", - "xmsim"))) +@cocotb.test(expect_error=Exception) async def test_cached_write_in_readonly(dut): """Test doing invalid sim operation""" global exited diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index 08ea0e3b..cbc814f1 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -68,7 +68,7 @@ async def discover_module_values(dut): raise TestFailure("Expected to discover things in the DUT") -@cocotb.test(expect_error=True) +@cocotb.test(expect_error=AttributeError) async def discover_value_not_in_dut(dut): """Try and get a value from the DUT that is not there""" fake_signal = dut.fake_signal @@ -91,7 +91,7 @@ async def access_signal(dut): @cocotb.test( # Icarus up to (including) 10.3 doesn't support bit-selects, see https://github.com/steveicarus/iverilog/issues/323 - expect_error=IndexError if (cocotb.SIM_NAME.lower().startswith("icarus") and (IcarusVersion(cocotb.SIM_VERSION) <= IcarusVersion("10.3 (stable)"))) else False, + expect_error=IndexError if (cocotb.SIM_NAME.lower().startswith("icarus") and (IcarusVersion(cocotb.SIM_VERSION) <= IcarusVersion("10.3 (stable)"))) else (), skip=cocotb.LANGUAGE in ["vhdl"]) async def access_single_bit(dut): """Access a single bit in a vector of the DUT""" @@ -109,7 +109,7 @@ async def access_single_bit(dut): @cocotb.test( # Icarus up to (including) 10.3 doesn't support bit-selects, see https://github.com/steveicarus/iverilog/issues/323 - expect_error=IndexError if (cocotb.SIM_NAME.lower().startswith("icarus") and (IcarusVersion(cocotb.SIM_VERSION) <= IcarusVersion("10.3 (stable)"))) else False, + expect_error=IndexError if (cocotb.SIM_NAME.lower().startswith("icarus") and (IcarusVersion(cocotb.SIM_VERSION) <= IcarusVersion("10.3 (stable)"))) else (), skip=cocotb.LANGUAGE in ["vhdl"]) async def access_single_bit_assignment(dut): """Access a single bit in a vector of the DUT using the assignment mechanism""" @@ -125,7 +125,7 @@ async def access_single_bit_assignment(dut): dut.stream_out_data_comb.value.integer, (1 << 2))) -@cocotb.test(expect_error=True) +@cocotb.test(expect_error=IndexError) async def access_single_bit_erroneous(dut): """Access a non-existent single bit""" dut._log.info("%s = %d bits" % @@ -134,7 +134,7 @@ async def access_single_bit_erroneous(dut): dut.stream_in_data[bit] <= 1 -@cocotb.test(expect_error=cocotb.SIM_NAME.lower().startswith(("icarus", "chronologic simulation vcs")), +@cocotb.test(expect_error=AttributeError if cocotb.SIM_NAME.lower().startswith(("icarus", "chronologic simulation vcs")) else (), expect_fail=cocotb.SIM_NAME.lower().startswith(("riviera")) and cocotb.LANGUAGE in ["verilog"]) async def access_integer(dut): """Integer should show as an IntegerObject""" @@ -250,7 +250,7 @@ async def access_string_vhdl(dut): # TODO: add tests for Verilog "string_input_port" and "STRING_LOCALPARAM" (see issue #802) @cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"] or cocotb.SIM_NAME.lower().startswith(("icarus", "riviera")), - expect_error=cocotb.result.TestFailure if cocotb.SIM_NAME.lower().startswith(("xmsim", "ncsim", "modelsim", "chronologic simulation vcs")) else False) + expect_error=cocotb.result.TestFailure if cocotb.SIM_NAME.lower().startswith(("xmsim", "ncsim", "modelsim", "chronologic simulation vcs")) else ()) async def access_const_string_verilog(dut): """Access to a const Verilog string.""" tlog = logging.getLogger("cocotb.test") @@ -271,7 +271,7 @@ async def access_const_string_verilog(dut): @cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"], - expect_error=cocotb.SIM_NAME.lower().startswith("icarus")) + expect_error=AttributeError if cocotb.SIM_NAME.lower().startswith("icarus") else ()) async def access_var_string_verilog(dut): """Access to a var Verilog string.""" tlog = logging.getLogger("cocotb.test") @@ -369,7 +369,7 @@ async def skip_a_test(dut): @cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"], - expect_error=cocotb.SIM_NAME.lower().startswith(("icarus"))) + expect_error=AttributeError if cocotb.SIM_NAME.lower().startswith(("icarus")) else ()) async def access_gate(dut): """ Test access to a gate Object diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py index ff8b86d7..d5509bed 100644 --- a/tests/test_cases/test_external/test_external.py +++ b/tests/test_cases/test_external/test_external.py @@ -90,7 +90,7 @@ async def test_time_in_external(dut): # Cadence simulators: "Unable set up RisingEdge(...) Trigger" with VHDL (see #1076) -@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else False) +@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else ()) async def test_time_in_function(dut): """ Test that an @external function calling back into a cocotb @function @@ -119,7 +119,7 @@ def wait_cycles_wrapper(dut, n): # Cadence simulators: "Unable set up RisingEdge(...) Trigger" with VHDL (see #1076) -@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else False) +@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else ()) async def test_external_call_return(dut): """ Test ability to await an external function that is not a coroutine using @external @@ -178,7 +178,7 @@ async def test_function_from_readonly(dut): # Cadence simulators: "Unable set up RisingEdge(...) Trigger" with VHDL (see #1076) -@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else False) +@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else ()) async def test_function_that_awaits(dut): """ Test that @external functions can call @function coroutines that @@ -192,7 +192,7 @@ async def test_function_that_awaits(dut): # Cadence simulators: "Unable set up RisingEdge(...) Trigger" with VHDL (see #1076) -@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else False) +@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else ()) async def test_await_after_function(dut): """ Test that awaiting a Trigger works after returning @@ -209,7 +209,7 @@ async def test_await_after_function(dut): # Cadence simulators: "Unable set up RisingEdge(...) Trigger" with VHDL (see #1076) -@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else False) +@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else ()) async def test_external_from_fork(dut): """ Test that @external functions work when awaited from a forked From 3b21a5194088a3dffa8357e327f86f331539a1b1 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 4 Dec 2020 10:02:51 -0600 Subject: [PATCH 2601/2656] Remove old CI tests (#2261) * Remove testing old verilator in CI Only the most recent versions of Verilator (4.106+) will work. So there is no reason to test whatever old version is packaged with Ubuntu 20.04. * Remove testing old Icarus versions on other OSes Now that Icarus master passes on Windows and MacOS, there is not really a reason to test the older 10.3 branch that is no longer being updated. --- .github/workflows/regression-tests.yml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml index 5de897d4..1cc4a236 100644 --- a/.github/workflows/regression-tests.yml +++ b/.github/workflows/regression-tests.yml @@ -114,13 +114,6 @@ jobs: # Test Verilator on Ubuntu - - sim: verilator - sim-version: apt - lang: verilog - python-version: 3.8 - os: ubuntu-20.04 - may_fail: true - - sim: verilator sim-version: master lang: verilog @@ -136,24 +129,12 @@ jobs: python-version: 3.8 os: macos-latest - - sim: icarus # Icarus homebrew stable - sim-version: homebrew - lang: verilog - python-version: 3.8 - os: macos-latest - - sim: icarus # Icarus windows master from source sim-version: master lang: verilog python-version: 3.8 os: windows-latest - - sim: icarus # Icarus windows 10.3 from source - sim-version: v10_3 - lang: verilog - python-version: 3.8 - os: windows-latest - # Other - sim: icarus # use clang instead of gcc From 5bfc5c23ef7835f3353ed3c8be6c5bd9dfb8b33a Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 4 Dec 2020 20:30:34 -0600 Subject: [PATCH 2602/2656] Deprecated cocotb.utils.pack and cocotb.utils.unpack (#2203) * Deprecated cocotb.utils.pack and cocotb.utils.unpack The functionality is available in the standard library struct.Struct. The current implementation conflates strings and bytes and will only work in Python 2. * Deprecate assigning ctypes.Structure to signals --- cocotb/handle.py | 11 +++++- cocotb/utils.py | 14 +++++++ .../source/newsfragments/2203.removal.rst | 1 + tests/pytest/test_utils.py | 39 +++++++++++++++++++ .../test_cases/test_cocotb/test_deprecated.py | 22 +++++++++++ 5 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 documentation/source/newsfragments/2203.removal.rst diff --git a/cocotb/handle.py b/cocotb/handle.py index 6f94c437..002ec25c 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -692,12 +692,17 @@ def _set_value(self, value, call_sim): because assigning integers less than 32 bits is faster. Args: - value (ctypes.Structure, cocotb.binary.BinaryValue, int, double): + value (cocotb.binary.BinaryValue, int): The value to drive onto the simulator object. Raises: TypeError: If target is not wide enough or has an unsupported type for value assignment. + + .. deprecated:: 1.5 + :class:`ctypes.Structure` objects are no longer accepted as values for assignment. + Convert the struct object to a :class:`~cocotb.binary.BinaryValue` before assignment using + ``BinaryValue(value=bytes(struct_obj), n_bits=len(signal))`` instead. """ value, set_action = self._check_for_set_action(value) @@ -705,6 +710,10 @@ def _set_value(self, value, call_sim): call_sim(self, self._handle.set_signal_val_long, set_action, value) return if isinstance(value, ctypes.Structure): + warnings.warn( + "`ctypes.Structure` values are no longer accepted for value assignment. " + "Use `BinaryValue(value=bytes(struct_obj), n_bits=len(signal))` instead", + DeprecationWarning, stacklevel=3) value = BinaryValue(value=cocotb.utils.pack(value), n_bits=len(self)) elif isinstance(value, int): value = BinaryValue(value=value, n_bits=len(self), bigEndian=False) diff --git a/cocotb/utils.py b/cocotb/utils.py index 0dfee7d1..54caabea 100644 --- a/cocotb/utils.py +++ b/cocotb/utils.py @@ -174,7 +174,13 @@ def pack(ctypes_obj): Returns: New Python string containing the bytes from memory holding *ctypes_obj*. + + .. deprecated:: 1.5 + This function is deprecated, use ``bytes(ctypes_obj)`` instead. """ + warnings.warn( + "This function is deprecated and will be removed, use ``bytes(ctypes_obj)`` instead.", + DeprecationWarning, stacklevel=2) return ctypes.string_at(ctypes.addressof(ctypes_obj), ctypes.sizeof(ctypes_obj)) @@ -196,7 +202,15 @@ def unpack(ctypes_obj, string, bytes=None): :exc:`ValueError`: If length of *string* and size of *ctypes_obj* are not equal. :exc:`MemoryError`: If *bytes* is longer than size of *ctypes_obj*. + + .. deprecated:: 1.5 + Converting bytes to a ctypes object should be done with :meth:`~ctypes._CData.from_buffer_copy`. + If you need to assign bytes into an *existing* ctypes object, use ``memoryview(ctypes_obj).cast('B')[:bytes] = string``, + see :class:`memoryview` for details. """ + warnings.warn( + "This function is being removed, use ``memoryview(ctypes_obj).cast('B')[:bytes] = string`` instead.", + DeprecationWarning, stacklevel=2) if bytes is None: if len(string) != ctypes.sizeof(ctypes_obj): raise ValueError("Attempt to unpack a string of size %d into a \ diff --git a/documentation/source/newsfragments/2203.removal.rst b/documentation/source/newsfragments/2203.removal.rst new file mode 100644 index 00000000..fd44e807 --- /dev/null +++ b/documentation/source/newsfragments/2203.removal.rst @@ -0,0 +1 @@ +Deprecate :func:`~cocotb.utils.pack` and :func:`~cocotb.utils.unpack` and the use of :class:`python:ctypes.Structure` in signal assignments. diff --git a/tests/pytest/test_utils.py b/tests/pytest/test_utils.py index a0b32b51..66bf1a74 100644 --- a/tests/pytest/test_utils.py +++ b/tests/pytest/test_utils.py @@ -5,6 +5,8 @@ import cocotb.utils +import ctypes + class TestHexDump: def test_int_illegal(dut): @@ -36,3 +38,40 @@ def test_str_deprecated(dut): diff_bytes = cocotb.utils.hexdiffs(b'\x20\x65\x00\xff', b'\x20\x00\x65') assert diff_bytes == diff_str + + +def test_pack_deprecated(): + + class Example(ctypes.Structure): + _fields_ = [ + ("a", ctypes.c_byte), + ("b", ctypes.c_byte)] + + e = Example(a=0xCC, b=0x55) + + with pytest.warns(DeprecationWarning): + a = cocotb.utils.pack(e) + + assert a == bytes(e) + + +def test_unpack_deprecated(): + + class Example(ctypes.Structure): + _fields_ = [ + ("a", ctypes.c_byte), + ("b", ctypes.c_byte)] + + e = Example(a=0xCC, b=0x55) + f = Example(a=0xCC, b=0x55) + + b = b'\x01\x02' + + with pytest.warns(DeprecationWarning): + cocotb.utils.unpack(e, b) + + assert e.a == 1 and e.b == 2 + + memoryview(f).cast('B')[:] = b + + assert f.a == 1 and f.b == 2 diff --git a/tests/test_cases/test_cocotb/test_deprecated.py b/tests/test_cases/test_cocotb/test_deprecated.py index a726f9e4..6c7aceb2 100644 --- a/tests/test_cases/test_cocotb/test_deprecated.py +++ b/tests/test_cases/test_cocotb/test_deprecated.py @@ -2,7 +2,10 @@ # Licensed under the Revised BSD License, see LICENSE for details. # SPDX-License-Identifier: BSD-3-Clause import cocotb +from cocotb.triggers import Timer +from cocotb.binary import BinaryValue import warnings +import ctypes from contextlib import contextmanager from common import assert_raises @@ -108,6 +111,25 @@ async def test_handle_compat_mapping(dut): assert dut.fullname == "myfullname" +@cocotb.test() +async def test_assigning_structure_deprecated(dut): + """signal <= ctypes.Structure assignment is deprecated""" + + class Example(ctypes.Structure): + _fields_ = [ + ("a", ctypes.c_byte), + ("b", ctypes.c_uint32)] + + e = Example(a=0xCC, b=0x12345678) + + with assert_deprecated(): + dut.stream_in_data_wide <= e + + await Timer(1, 'step') + + assert dut.stream_in_data_wide == BinaryValue(value=bytes(e), n_bits=len(dut.stream_in_data_wide)) + + @cocotb.test() async def test_expect_error_bool_deprecated(_): async def t(): From 38f4b91e94e3a1894b68011b9eb8421028ff3677 Mon Sep 17 00:00:00 2001 From: Tomasz Hemperek Date: Fri, 4 Dec 2020 19:19:02 +0100 Subject: [PATCH 2603/2656] Fix error on dynamic libpython loding on mac --- cocotb_build_libs.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cocotb_build_libs.py b/cocotb_build_libs.py index d815e97f..785af14c 100755 --- a/cocotb_build_libs.py +++ b/cocotb_build_libs.py @@ -281,6 +281,12 @@ def _get_python_lib(): if os.name == "nt": python_lib = _get_python_lib_link() + "." + _get_lib_ext_name() + elif sys.platform == "darwin": + python_lib = os.path.join(sysconfig.get_config_var("LIBDIR"), "lib" + _get_python_lib_link() + ".") + if os.path.exists(python_lib + "dylib"): + python_lib += "dylib" + else: + python_lib += "so" else: python_lib = "lib" + _get_python_lib_link() + "." + _get_lib_ext_name() From c89e1de085f1204ee67e60fbe7e4d68192b4cf9f Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sat, 5 Dec 2020 09:44:21 +0100 Subject: [PATCH 2604/2656] Remove erroneous assignment to input port (#2265) Interestingly, both Xcelium and Questa allow that while Verilator and Riviera show an error. Closes #2262. --- tests/designs/sample_module/sample_module.sv | 4 ---- tests/test_cases/test_array_simple/test_array_simple.py | 2 -- 2 files changed, 6 deletions(-) diff --git a/tests/designs/sample_module/sample_module.sv b/tests/designs/sample_module/sample_module.sv index de2d9795..7823a343 100644 --- a/tests/designs/sample_module/sample_module.sv +++ b/tests/designs/sample_module/sample_module.sv @@ -136,10 +136,6 @@ assign logic_a = stream_in_valid; always@* logic_b = stream_in_valid; always@(posedge clk) logic_c <= stream_in_valid; -`ifndef __ICARUS__ -assign inout_if.b_out = inout_if.a_in; -`endif - reg _underscore_name; `ifdef __ICARUS__ // By default, a variable must be used in some way in order diff --git a/tests/test_cases/test_array_simple/test_array_simple.py b/tests/test_cases/test_array_simple/test_array_simple.py index 8523441d..8e21b008 100644 --- a/tests/test_cases/test_array_simple/test_array_simple.py +++ b/tests/test_cases/test_array_simple/test_array_simple.py @@ -183,11 +183,9 @@ async def test_struct(dut): dut.inout_if.a_in <= 1 await Timer(1000, 'ns') _check_value(tlog, dut.inout_if.a_in, 1) - _check_value(tlog, dut.inout_if.b_out, 1) dut.inout_if.a_in <= 0 await Timer(1000, 'ns') _check_value(tlog, dut.inout_if.a_in, 0) - _check_value(tlog, dut.inout_if.b_out, 0) @contextlib.contextmanager From f8c0e402dd4e4f0193be81e9e4c37e0e2402f95e Mon Sep 17 00:00:00 2001 From: Marlon James Date: Sat, 5 Dec 2020 18:16:18 -0800 Subject: [PATCH 2605/2656] Update the verilator main control loop. (#2105) * Update the verilator main control loop Use return value of callValueCbs and callCbs to determine when a time step is finished. Call value change callbacks whenever a signal may have changed value. Don't process every time step, instead skip ahead to the next registered Timer trigger. If there are no more Timer triggers registered, end the simulation so Verilator doesn't get stuck in an infinite loop. Move the cbAfterDelay callbacks (for Timer triggers) to the beginning of the time step (Pre-Active region). Requires Verilator 4.106 or later. * Re-enable tests that made Verilator hang --- cocotb/share/lib/verilator/verilator.cpp | 71 +++++++++++++++---- .../makefiles/simulators/Makefile.verilator | 7 ++ .../source/newsfragments/2105.change.rst | 1 + documentation/source/simulator_support.rst | 10 ++- tests/test_cases/issue_1279/Makefile | 10 --- tests/test_cases/test_exit_error/Makefile | 10 --- 6 files changed, 68 insertions(+), 41 deletions(-) create mode 100644 documentation/source/newsfragments/2105.change.rst diff --git a/cocotb/share/lib/verilator/verilator.cpp b/cocotb/share/lib/verilator/verilator.cpp index b3377865..b9b0754b 100644 --- a/cocotb/share/lib/verilator/verilator.cpp +++ b/cocotb/share/lib/verilator/verilator.cpp @@ -21,7 +21,7 @@ #endif #endif -vluint64_t main_time = 0; // Current simulation time +static vluint64_t main_time = 0; // Current simulation time double sc_time_stamp() { // Called by $time in Verilog return main_time; // converts to double, to match @@ -32,6 +32,20 @@ extern "C" { void vlog_startup_routines_bootstrap(void); } +static inline bool settle_value_callbacks() { + bool cbs_called, again; + + // Call Value Change callbacks + // These can modify signal values so we loop + // until there are no more changes + cbs_called = again = VerilatedVpi::callValueCbs(); + while (again) { + again = VerilatedVpi::callValueCbs(); + } + + return cbs_called; +} + int main(int argc, char** argv) { Verilated::commandArgs(argc, argv); #ifdef VERILATOR_SIM_DEBUG @@ -40,6 +54,10 @@ int main(int argc, char** argv) { std::unique_ptr top(new Vtop("")); Verilated::fatalOnVpiError(false); // otherwise it will fail on systemtf +#ifdef VERILATOR_SIM_DEBUG + Verilated::internalsDump(); +#endif + vlog_startup_routines_bootstrap(); VerilatedVpi::callCbs(cbStartOfSimulation); @@ -57,43 +75,66 @@ int main(int argc, char** argv) { #endif while (!Verilated::gotFinish()) { - bool again = true; + // Call registered timed callbacks (e.g. clock timer) + // These are called at the beginning of the time step + // before the iterative regions (IEEE 1800-2012 4.4.1) + VerilatedVpi::callTimedCbs(); + + // Call Value Change callbacks triggered by Timer callbacks + // These can modify signal values + settle_value_callbacks(); // We must evaluate whole design until we process all 'events' + bool again = true; while (again) { // Evaluate design top->eval(); - // Call Value Change callbacks as eval() - // can modify signals values - VerilatedVpi::callValueCbs(); + // Call Value Change callbacks triggered by eval() + // These can modify signal values + again = settle_value_callbacks(); - // Call registered Read-Write callbacks - again = VerilatedVpi::callCbs(cbReadWriteSynch); + // Call registered ReadWrite callbacks + again |= VerilatedVpi::callCbs(cbReadWriteSynch); - // Call Value Change callbacks as cbReadWriteSynch - // can modify signals values - VerilatedVpi::callValueCbs(); + // Call Value Change callbacks triggered by ReadWrite callbacks + // These can modify signal values + again |= settle_value_callbacks(); } // Call ReadOnly callbacks VerilatedVpi::callCbs(cbReadOnlySynch); - // Call registered timed callbacks (e.g. clock timer) - VerilatedVpi::callTimedCbs(); - #if VM_TRACE tfp->dump(main_time); #endif - main_time++; + // cocotb controls the clock inputs using cbAfterDelay so + // skip ahead to the next registered callback + vluint64_t next_time = VerilatedVpi::cbNextDeadline(); + + // If there are no more cbAfterDelay callbacks, + // the next deadline is max value, so end the simulation now + if (next_time == static_cast(~0ULL)) { + vl_finish(__FILE__, __LINE__, ""); + break; + } else { + main_time = next_time; + } // Call registered NextSimTime - // It should be called in new slot before everything else + // It should be called in simulation cycle before everything else + // but not on first cycle VerilatedVpi::callCbs(cbNextSimTime); + + // Call Value Change callbacks triggered by NextTimeStep callbacks + // These can modify signal values + settle_value_callbacks(); } VerilatedVpi::callCbs(cbEndOfSimulation); + top->final(); + #if VM_TRACE tfp->close(); #endif diff --git a/cocotb/share/makefiles/simulators/Makefile.verilator b/cocotb/share/makefiles/simulators/Makefile.verilator index e7c83973..831d8ae2 100644 --- a/cocotb/share/makefiles/simulators/Makefile.verilator +++ b/cocotb/share/makefiles/simulators/Makefile.verilator @@ -22,6 +22,13 @@ ifeq ($(shell which $(CMD) 2>/dev/null),) $(error Cannot find verilator.) endif +VLT_MIN := 4.106 +VLT_VERSION := $(shell $(CMD) --version | cut -d " " -f 2) +MIN_VERSION := $(shell printf "%s\n%s\n" "$(VLT_MIN)" "$(VLT_VERSION)" | sort -g | head -1) +ifneq ($(MIN_VERSION),$(VLT_MIN)) + $(error cocotb requires Verilator $(VLT_MIN) or later, but using $(VLT_VERSION)) +endif + ifeq ($(VERILATOR_SIM_DEBUG), 1) COMPILE_ARGS += --debug PLUSARGS += +verilator+debug diff --git a/documentation/source/newsfragments/2105.change.rst b/documentation/source/newsfragments/2105.change.rst new file mode 100644 index 00000000..b1cdd425 --- /dev/null +++ b/documentation/source/newsfragments/2105.change.rst @@ -0,0 +1 @@ +Improved support and performance for :ref:`sim-verilator` (version 4.106 or later now required). diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst index a8e46ee1..541c1f79 100644 --- a/documentation/source/simulator_support.rst +++ b/documentation/source/simulator_support.rst @@ -79,22 +79,20 @@ In order to use this simulator, set :make:var:`SIM` to ``verilator``: make SIM=verilator -cocotb supports Verilator 4.020 and above. Verilator converts Verilog code to C++ code that is compiled. It does not support VHDL. One major limitation compared to standard Verilog simulators is that it does not support delayed assignments. To run cocotb with Verilator, you need ``verilator`` in your PATH. -Finally, cocotb currently generates a Verilator toplevel C++ simulation loop which is timed at the highest precision. -If your design's clocks vary in precision, the performance of the simulation can be improved in the same order of magnitude by adjusting the precision in the Makefile, e.g., - -.. code-block:: makefile +.. note:: - COCOTB_HDL_TIMEPRECISION = 1us # Set precision to 10^-6s + cocotb requires Verilator 4.106 or later. .. versionadded:: 1.3 +.. versionchanged:: 1.5 Improved cocotb support and greatly improved performance when using a higher time precision. Verilator 4.106 or later is required. + Coverage -------- diff --git a/tests/test_cases/issue_1279/Makefile b/tests/test_cases/issue_1279/Makefile index 42664f4f..6958dbd7 100644 --- a/tests/test_cases/issue_1279/Makefile +++ b/tests/test_cases/issue_1279/Makefile @@ -2,16 +2,6 @@ # Licensed under the Revised BSD License, see LICENSE for details. # SPDX-License-Identifier: BSD-3-Clause -ifeq ($(SIM),verilator) - -all: - @echo "Skipping test due to Verilator hanging when prematurely shutting down" # gh-2008 -clean:: - -else - include ../../designs/sample_module/Makefile MODULE = issue_1279 - -endif diff --git a/tests/test_cases/test_exit_error/Makefile b/tests/test_cases/test_exit_error/Makefile index 0531e866..7176a31a 100644 --- a/tests/test_cases/test_exit_error/Makefile +++ b/tests/test_cases/test_exit_error/Makefile @@ -25,14 +25,6 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -ifeq ($(SIM),verilator) - -all: - @echo "Skipping test due to Verilator hanging when prematurely shutting down" # gh-2008 -clean:: - -else - # detecting an expected failure has to happen here, as the python code is invalid .PHONY: override_for_this_test override_for_this_test: @@ -41,5 +33,3 @@ override_for_this_test: include ../../designs/sample_module/Makefile MODULE = test_exit - -endif From a6638a01af7c978fcdef6760aaf784b8927e92bb Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Sat, 12 Dec 2020 16:49:27 +0100 Subject: [PATCH 2606/2656] Use the lowest possible Python version for 3.5-3.7 in CI Closes #2284. --- .github/workflows/regression-tests.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml index 1cc4a236..36f508c6 100644 --- a/.github/workflows/regression-tests.yml +++ b/.github/workflows/regression-tests.yml @@ -61,19 +61,22 @@ jobs: - sim: icarus sim-version: apt lang: verilog - python-version: 3.5 + # lowest version according to https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json + python-version: 3.5.4 os: ubuntu-20.04 - sim: icarus sim-version: apt lang: verilog - python-version: 3.6 + # lowest version according to https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json + python-version: 3.6.7 os: ubuntu-20.04 - sim: icarus sim-version: apt lang: verilog - python-version: 3.7 + # lowest version according to https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json + python-version: 3.7.5 os: ubuntu-20.04 - sim: icarus From ff1fccb93e4c8ed35b9e11fb2c1775ae8b667b9a Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 14 Dec 2020 21:07:38 +0100 Subject: [PATCH 2607/2656] Differentiate docs for stable and latest version more (#2285) Differentiate docs for stable and latest version more Co-authored-by: Kaleb Barrett --- README.md | 24 +++++++++++++----------- documentation/source/install.rst | 2 +- documentation/source/install_devel.rst | 19 +++++++++++++++++-- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index daa74c59..990e43a0 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ ## Installation -Cocotb requires: +The current stable version of cocotb requires: - Python 3.5+ - A C++11 compiler @@ -31,9 +31,11 @@ pip install cocotb **!!! Windows Users !!!** See [here](https://docs.cocotb.org/en/stable/install.html) for installation instructions. -For more details on installation, including prerequisites, see [the documentation](https://docs.cocotb.org/en/stable/install.html). +For more details on installation, including prerequisites, +see [the documentation](https://docs.cocotb.org/en/stable/install.html). -For detail on how to install the *development* version of cocotb, see [the lastest documentation](https://docs.cocotb.org/en/latest/install_devel.html#install-devel). +For details on how to install the *development* version of cocotb, +see [the preliminary documentation of the future release](https://docs.cocotb.org/en/latest/install_devel.html#install-devel). ## Usage @@ -103,14 +105,14 @@ make SIM=icarus [![asciicast](https://asciinema.org/a/317220.svg)](https://asciinema.org/a/317220) -For more information please see the [cocotb documentation](https://docs.cocotb.org/) and the [wiki](https://github.com/cocotb/cocotb/wiki). +For more information please see the [cocotb documentation](https://docs.cocotb.org/) +and [our wiki](https://github.com/cocotb/cocotb/wiki). ## Tutorials, examples and related projects -* [Endian Swapper tutorial](https://docs.cocotb.org/en/stable/endian_swapper.html) -* [Ping using TUN/TAP tutorial](https://docs.cocotb.org/en/stable/ping_tun_tap.html) -* [Cocotb based USB 1.1 test suite for FPGA IP, with testbenches for a variety of open source USB cores](https://github.com/antmicro/usb-test-suite-build) -* [Functional Coverage and Constrained Randomization Extensions for Cocotb](https://github.com/mciepluc/cocotb-coverage) -* [UVM 1.2 port to Python](https://github.com/tpoikela/uvm-python) - -For more related resources please check the [wiki](https://github.com/cocotb/cocotb/wiki/Further-Resources) and the [list of projects depending on cocotb](https://github.com/cocotb/cocotb/network/dependents). +* the tutorial section [in the official documentation](https://docs.cocotb.org/) +* [cocotb-based USB 1.1 test suite](https://github.com/antmicro/usb-test-suite-build) for FPGA IP, with testbenches for a variety of open source USB cores +* [`cocotb-coverage`](https://github.com/mciepluc/cocotb-coverage), an extension for Functional Coverage and Constrained Randomization +* [`uvm-python`](https://github.com/tpoikela/uvm-python), an almost 1:1 port of UVM 1.2 to Python +* our wiki [on extension modules](https://github.com/cocotb/cocotb/wiki/Further-Resources#extension-modules-cocotbext) +* the list of [GitHub projects depending on cocotb](https://github.com/cocotb/cocotb/network/dependents) diff --git a/documentation/source/install.rst b/documentation/source/install.rst index c48476d2..28fde934 100644 --- a/documentation/source/install.rst +++ b/documentation/source/install.rst @@ -14,7 +14,7 @@ In this document, we are assuming that you already have a Installation of Prerequisites ============================= -Cocotb has the following requirements: +The current stable version of cocotb requires: * Python 3.5+ * Python-dev packages diff --git a/documentation/source/install_devel.rst b/documentation/source/install_devel.rst index 8736aeed..9cb9034b 100644 --- a/documentation/source/install_devel.rst +++ b/documentation/source/install_devel.rst @@ -4,9 +4,24 @@ Installing the Development Version ********************************** -We are assuming here that you have :ref:`installed the prerequisites`. +The development version of cocotb may have differerent prerequisites +than the stable version: -The instructions for installing the development version of cocotb vary depending on your operating system: +.. + Likely changes after 1.5: + * Python 3.6+ + * pytest + +* Python 3.5+ +* Python-dev packages +* GCC 4.8.1+ or Clang 3.3+ and associated development packages +* GNU Make +* A Verilog or VHDL simulator, depending on your :term:`RTL` source code + +Please refer to :ref:`install-prerequisites` for details. + + +The instructions for installing the development version of cocotb itself vary depending on your operating system: .. tabs:: From 2832e3f20369e397889a63a93e39c0766dfb13ec Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 15 Dec 2020 22:40:49 +0100 Subject: [PATCH 2608/2656] Use Python 3.7.1 which is new on GitHub Actions See https://github.com/actions/virtual-environments/issues/2253 for details about problems going all the way down to 3.7.0 and 3.6.0. Refs #2287. --- .github/workflows/regression-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml index 36f508c6..805b7b6c 100644 --- a/.github/workflows/regression-tests.yml +++ b/.github/workflows/regression-tests.yml @@ -76,7 +76,7 @@ jobs: sim-version: apt lang: verilog # lowest version according to https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json - python-version: 3.7.5 + python-version: 3.7.1 os: ubuntu-20.04 - sim: icarus From 4eaf429c67739886fab45afd647b4eeb37436694 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 16 Dec 2020 10:02:19 -0600 Subject: [PATCH 2609/2656] Remove last occurrences of legacy coroutine syntax from tests (#2288) * Remove last offending uses of cocotb.coroutine and comment rest All uses of cocotb.coroutine and legacy generator syntax in cocotb.test have been removed, except those that are critical to the test being performed. Grepping for uses will show only lines with commented justifications. --- tests/test_cases/test_cocotb/common.py | 13 +++++-------- .../test_cocotb/test_async_coroutines.py | 10 +++++----- .../test_cocotb/test_async_generators.py | 4 ++-- .../test_cocotb/test_concurrency_primitives.py | 8 ++++---- tests/test_cases/test_cocotb/test_deprecated.py | 2 +- .../test_cocotb/test_edge_triggers.py | 5 ----- .../test_cocotb/test_generator_coroutines.py | 17 +++++++++++++---- tests/test_cases/test_cocotb/test_scheduler.py | 6 ++---- .../test_cocotb/test_timing_triggers.py | 5 +---- 9 files changed, 33 insertions(+), 37 deletions(-) diff --git a/tests/test_cases/test_cocotb/common.py b/tests/test_cases/test_cocotb/common.py index 02d53081..ff76b86f 100644 --- a/tests/test_cases/test_cocotb/common.py +++ b/tests/test_cases/test_cocotb/common.py @@ -8,25 +8,22 @@ import traceback from contextlib import contextmanager -import cocotb from cocotb.triggers import Timer -@cocotb.coroutine -def clock_gen(clock): +async def clock_gen(clock): """Example clock gen for test use""" for i in range(5): clock <= 0 - yield Timer(100, "ns") + await Timer(100, "ns") clock <= 1 - yield Timer(100, "ns") + await Timer(100, "ns") clock._log.warning("Clock generator finished!") -@cocotb.coroutine -def _check_traceback(running_coro, exc_type, pattern): +async def _check_traceback(running_coro, exc_type, pattern): try: - yield running_coro + await running_coro except exc_type: tb_text = traceback.format_exc() else: diff --git a/tests/test_cases/test_cocotb/test_async_coroutines.py b/tests/test_cases/test_cocotb/test_async_coroutines.py index fed97d6e..f435af11 100644 --- a/tests/test_cases/test_cocotb/test_async_coroutines.py +++ b/tests/test_cases/test_cocotb/test_async_coroutines.py @@ -14,13 +14,13 @@ class produce: """ Test helpers that produce a value / exception in different ways """ @staticmethod - @cocotb.coroutine + @cocotb.coroutine # testing legacy coroutine against async func def coro(outcome): yield Timer(1) return outcome.get() @staticmethod - @cocotb.coroutine + @cocotb.coroutine # testing coroutine decorator on async func async def async_annotated(outcome): await Timer(1) return outcome.get() @@ -36,7 +36,7 @@ class SomeException(Exception): pass -@cocotb.test() +@cocotb.test() # test yielding decorated async coroutine in legacy coroutine def test_annotated_async_from_coro(dut): """ Test that normal coroutines are able to call async functions annotated @@ -112,7 +112,7 @@ async def test_await_causes_start(dut): assert coro.has_started() -@cocotb.test() +@cocotb.test() # test forking undecorated async coroutine in legacy coroutine def test_undecorated_coroutine_fork(dut): ran = False @@ -125,7 +125,7 @@ async def example(): assert ran -@cocotb.test() +@cocotb.test() # test yielding undecorated async coroutine in legacy coroutine def test_undecorated_coroutine_yield(dut): ran = False diff --git a/tests/test_cases/test_cocotb/test_async_generators.py b/tests/test_cases/test_cocotb/test_async_generators.py index dde3c3fe..78481b97 100644 --- a/tests/test_cases/test_cocotb/test_async_generators.py +++ b/tests/test_cases/test_cocotb/test_async_generators.py @@ -10,7 +10,7 @@ async def whoops_async_generator(): yield cocotb.triggers.Timer(1) -@cocotb.test() +@cocotb.test() # testing async generator in legacy coroutine syntax def test_yielding_accidental_async_generator(dut): # this test deliberately does not use `async def`, as we are testing the behavior of `yield` try: @@ -31,7 +31,7 @@ async def test_forking_accidental_async_generator(dut): assert False, "should have thrown" -@cocotb.coroutine +@cocotb.coroutine # testing cocotb.coroutine decorated async generator async def whoops_async_generator_decorated(): yield cocotb.triggers.Timer(1) diff --git a/tests/test_cases/test_cocotb/test_concurrency_primitives.py b/tests/test_cases/test_cocotb/test_concurrency_primitives.py index 1ebbd2e3..938118f9 100644 --- a/tests/test_cases/test_cocotb/test_concurrency_primitives.py +++ b/tests/test_cases/test_cocotb/test_concurrency_primitives.py @@ -75,8 +75,7 @@ async def test_first_does_not_kill(dut): """ Test that `First` does not kill coroutines that did not finish first """ ran = False - # decorating `async def` is required to use `First` - @cocotb.coroutine + @cocotb.coroutine # decorating `async def` is required to use `First` async def coro(): nonlocal ran await Timer(2, units='ns') @@ -97,7 +96,8 @@ async def coro(): @cocotb.test() async def test_exceptions_first(dut): """ Test exception propagation via cocotb.triggers.First """ - @cocotb.coroutine + + @cocotb.coroutine # decorating `async def` is required to use `First` def raise_inner(): yield Timer(10, "ns") raise ValueError('It is soon now') @@ -111,7 +111,7 @@ async def raise_soon(): expected = textwrap.dedent(r""" Traceback \(most recent call last\): File ".*common\.py", line \d+, in _check_traceback - yield running_coro + await running_coro File ".*test_concurrency_primitives\.py", line \d+, in raise_soon await cocotb\.triggers\.First\(raise_inner\(\)\) File ".*triggers\.py", line \d+, in _wait diff --git a/tests/test_cases/test_cocotb/test_deprecated.py b/tests/test_cases/test_cocotb/test_deprecated.py index 6c7aceb2..2969a48f 100644 --- a/tests/test_cases/test_cocotb/test_deprecated.py +++ b/tests/test_cases/test_cocotb/test_deprecated.py @@ -27,7 +27,7 @@ def assert_deprecated(warning_category=DeprecationWarning): @cocotb.test() async def test_returnvalue_deprecated(dut): - @cocotb.coroutine + @cocotb.coroutine # testing ReturnValue deprecated def get_value(): yield cocotb.triggers.Timer(1, units='ns') raise cocotb.result.ReturnValue(42) diff --git a/tests/test_cases/test_cocotb/test_edge_triggers.py b/tests/test_cases/test_cocotb/test_edge_triggers.py index e63e806b..24233921 100644 --- a/tests/test_cases/test_cocotb/test_edge_triggers.py +++ b/tests/test_cases/test_cocotb/test_edge_triggers.py @@ -14,7 +14,6 @@ from cocotb.clock import Clock -@cocotb.coroutine async def count_edges_cycles(signal, edges): edge = RisingEdge(signal) for i in range(edges): @@ -24,7 +23,6 @@ async def count_edges_cycles(signal, edges): return edges -@cocotb.coroutine async def do_single_edge_check(dut, level): """Do test for rising edge""" old_value = dut.clk.value.integer @@ -119,7 +117,6 @@ async def test_fork_and_monitor(dut, period=1000, clocks=6): assert result == clocks, "Expected task to return %d but got %s" % (clocks, repr(result)) -@cocotb.coroutine async def do_clock(dut, limit, period): """Simple clock with a limit""" wait_period = period / 2 @@ -131,7 +128,6 @@ async def do_clock(dut, limit, period): limit -= 1 -@cocotb.coroutine async def do_edge_count(dut, signal): """Count the edges""" global edges_seen @@ -205,7 +201,6 @@ async def test_clock_cycles_forked(dut): clk_gen = cocotb.fork(Clock(dut.clk, 100, "ns").start()) - @cocotb.coroutine async def wait_ten(): await ClockCycles(dut.clk, 10) diff --git a/tests/test_cases/test_cocotb/test_generator_coroutines.py b/tests/test_cases/test_cocotb/test_generator_coroutines.py index 718da314..420b251e 100644 --- a/tests/test_cases/test_cocotb/test_generator_coroutines.py +++ b/tests/test_cases/test_cocotb/test_generator_coroutines.py @@ -5,7 +5,7 @@ Tests that specifically test generator-based coroutines """ import cocotb -from cocotb.triggers import Timer +from cocotb.triggers import Timer, NullTrigger from common import clock_gen, _check_traceback, assert_raises import textwrap @@ -67,13 +67,18 @@ def test_function_not_decorated_fork(dut): yield Timer(500) +@cocotb.coroutine +def example(): + yield NullTrigger() + + @cocotb.test() def test_adding_a_coroutine_without_starting(dut): """Catch (and provide useful error) for attempts to fork coroutines incorrectly""" yield Timer(100) try: - forked = cocotb.fork(clock_gen) + cocotb.fork(example) except TypeError as exc: assert "a coroutine that hasn't started" in str(exc) else: @@ -169,7 +174,9 @@ def raise_soon(): expected = textwrap.dedent(r""" Traceback \(most recent call last\): File ".*common\.py", line \d+, in _check_traceback - yield running_coro + await running_coro + File ".*cocotb[\\\/]decorators.py", line \d+, in __await__ + return \(yield self\) File ".*test_generator_coroutines\.py", line \d+, in raise_soon yield raise_inner\(\) File ".*test_generator_coroutines\.py", line \d+, in raise_inner @@ -198,7 +205,9 @@ def raise_soon(): expected = textwrap.dedent(r""" Traceback \(most recent call last\): File ".*common\.py", line \d+, in _check_traceback - yield running_coro + await running_coro + File ".*cocotb[\\\/]decorators.py", line \d+, in __await__ + return \(yield self\) File ".*test_generator_coroutines\.py", line \d+, in raise_soon yield coro\.join\(\) File ".*test_generator_coroutines\.py", line \d+, in raise_inner diff --git a/tests/test_cases/test_cocotb/test_scheduler.py b/tests/test_cases/test_cocotb/test_scheduler.py index 995e8792..ef27ffbf 100644 --- a/tests/test_cases/test_cocotb/test_scheduler.py +++ b/tests/test_cases/test_cocotb/test_scheduler.py @@ -165,7 +165,6 @@ async def test_stack_overflow(dut): before passing control to the simulator. """ # gh-637 - @cocotb.coroutine async def null_coroutine(): await NullTrigger() @@ -309,7 +308,7 @@ def generator_coro_inner(): yield Timer(1, units='ns') raise ValueError("inner") - @cocotb.coroutine + @cocotb.coroutine # testing debug with legacy coroutine syntax def generator_coro_outer(): yield from generator_coro_inner() @@ -339,7 +338,7 @@ async def coroutine_forked(task): log.info(repr(task)) assert re.match(r"", repr(task)) - @cocotb.coroutine + @cocotb.coroutine # Combine requires use of cocotb.coroutine async def coroutine_wait(): await Timer(1, units='ns') @@ -419,7 +418,6 @@ async def test_start_soon_decorator(_): """ Tests start_soon works with RunningTasks """ a = 0 - @cocotb.coroutine async def example(): nonlocal a a = 1 diff --git a/tests/test_cases/test_cocotb/test_timing_triggers.py b/tests/test_cases/test_cocotb/test_timing_triggers.py index 9d60dcdc..868523c1 100644 --- a/tests/test_cases/test_cocotb/test_timing_triggers.py +++ b/tests/test_cases/test_cocotb/test_timing_triggers.py @@ -86,7 +86,6 @@ async def test_timer_with_rational_units(dut): exited = False -@cocotb.coroutine async def do_test_readwrite_in_readonly(dut): global exited await RisingEdge(dut.clk) @@ -95,7 +94,6 @@ async def do_test_readwrite_in_readonly(dut): exited = True -@cocotb.coroutine async def do_test_cached_write_in_readonly(dut): global exited await RisingEdge(dut.clk) @@ -104,7 +102,6 @@ async def do_test_cached_write_in_readonly(dut): exited = True -@cocotb.coroutine async def do_test_afterdelay_in_readonly(dut, delay): global exited await RisingEdge(dut.clk) @@ -175,7 +172,7 @@ async def write_manually(): assert dut.stream_in_data.value == 2 -@cocotb.coroutine +@cocotb.coroutine # cocotb.coroutine necessary to use in with_timeout async def example(): await Timer(10, 'ns') return 1 From 9b442d07870751ff656eb1ed53d24a3d806e9610 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 21 Dec 2020 22:43:22 -0500 Subject: [PATCH 2610/2656] Improve CONTRIBUTING.md (#2090) Add sections * Getting Help * What to Work On * Maintainer Pre-approval * How to Contact Maintainers Co-authored-by: Colin Marquardt Co-authored-by: Marlon James --- CONTRIBUTING.md | 231 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 229 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bb35d026..adb27e2c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,6 +6,212 @@ This guide explains how to contribute to cocotb, and documents the processes we All processes in this document are designed to streamline the development effort, to avoid bottlenecks, and to ultimately give a pleasant experience to all involved. +## Getting Help + +Cocotb is a diverse and challenging project to contribute to. +If you ever feel lost, out of your depth, or simply want to know more, the [cocotb Gitter channel](https://gitter.im/cocotb/Lobby) is actively watched by many cocotb users, contributors, and maintainers. +It is a good idea if you are unsure whether your problem or question is worthy of a Github Issue to first post it to the Gitter channel. +You may also ask questions in [Github issues](https://github.com/cocotb/cocotb/issues). +If you don't receive any response on the Gitter channel or a Github issue, or you want help privately, you may directly contact a [maintainer](#maintainers). + + +## What to Work On + +There is *a lot* of work to do on this project, no matter your area of expertise or skill level. + +If you are a beginner there are several [Github issues](https://github.com/cocotb/cocotb/issues) marked [`good first issue`](https://github.com/cocotb/cocotb/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) you can look at. +There are also a number of things you can work on that aren't in Github issues and don't require in-depth knowledge of the cocotb internals. +They including the following: + +* Documentation improvements +* Managing Github issues and the Gitter channel +* Testing and coverage improvements + +Cocotb is still not perfect. There are plenty of [bug fixes](https://github.com/cocotb/cocotb/issues?q=is%3Aopen+is%3Aissue+label%3Atype%3Abug) and [features](https://github.com/cocotb/cocotb/issues?q=is%3Aopen+is%3Aissue+label%3Atype%3Afeature) that can be worked on. +Most of these are recorded as Github issues. + + +### Documentation + +Cocotb's documentation is always open to improvements. +Improving documentation will help users better understand and use cocotb; +and may decrease the number of questions the Gitter channel and Github issue page. +Updating documentation requires knowledge of: + +* [reStructuredText](https://docutils.sourceforge.io/rst.html) +* [Sphinx documentation generator](https://www.sphinx-doc.org/en/master/) +* [Markdown](https://www.markdownguide.org/) +* [How to architect documentation](https://documentation.divio.com/) + +Some documentation should be located in the official documentation on [Read the Docs/RTD](https://docs.cocotb.org/en/latest/), +while the rest belongs on the [Wiki](https://github.com/cocotb/cocotb/wiki). +There are several ways to improve the documentation: + +* Better documenting core functionality (RTD) +* Documenting common "gotchas" (RTD) +* Documenting difficult and niche use cases (Wiki) +* Documenting common design patterns (Wiki) +* Documenting internal components (Wiki) + +See the documentation on [building the documentation](#building-documentation-locally) and the [guidelines on submitting pull requests](#patch-requirements). +Documentation improvements typically require no maintainer pre-approval; you can simply work on the documentation and open a pull request. +Documentation on the Wiki does not require a pull request; any user with a Github account can contribute to it. +Please be responsible with that power. + + +### Project Management + +The cocotb project is [fairly popular](https://larsasplund.github.io/github-facts/verification-practices.html#frameworks) and the +[Gitter channel](https://gitter.im/cocotb/Lobby) and +[Github issues](https://github.com/cocotb/cocotb) page receive a fair amount of traffic; +which is only expected to increase. +People are needed to categorize issues and pull requests, and respond to questions. +Working this task is the quickest way to learn how cocotb works. +Tending to this task requires the following: + +* people skills +* an understanding of the scope of cocotb +* general understanding about how cocotb works + +Someone working this task should set notifications on the Gitter channel to be notified of any new comments. +They should also "watch" the Github repo by selecting the "Watching" notification level button in the upper right corner of the main Github page. +Finally, they should notify the maintainers that they are able and willing to accept questions. + +To be able to add labels and close issues and PRs you will need special permissions. +Contact a [maintainer](#maintainer) if you are interested in receiving these permissions. +They will be granted according to the project's need and the requestor's familiarity with cocotb. +Once you have those permissions, see the guidelines on [managing issues and pull requests](#Managing-of-Issues-and-Pull-Requests). + +This task can also be done without special repo permissions, by just commenting on the issue or PR. +This is especially helpful for Github issues about bugs. +If you can duplicate the bug or confirm the bug report is invalid, that helps maintainers *a lot*. + + +### Tests and Coverage + +Cocotb has a suite of unit tests (located in the `tests` directory) and examples (located in the `examples` directory) which are functional acceptance tests. +If a pull request cannot pass *all* of these tests, it will likely be rejected. +To ensure cocotb only includes the highest quality code, these test should be exhaustive. +We use code coverage as a quantifiable metric of the "exhaustiveness" of these tests, and wish to improve this metric. + +Working on this task requires a familiarity with: + +* Cocotb's core functionality +* How to write Verilog and VHDL +* How to write cocotb tests in Python +* (Optionally) [codecov](https://docs.codecov.io/docs); coverage aggregator and Github bot +* (Optionally) the [coverage](https://coverage.readthedocs.io/en/latest/) module, for Python code coverage +* (Optionally) [gcov](https://gcc.gnu.org/onlinedocs/gcc/Gcov.html), for C++ code coverage +* (Optionally) [Github Actions](https://docs.github.com/en/free-pro-team@latest/actions), for automatic acceptance testing + +Cocotb's regression tests can be improved by: + +* Testing more of cocotb's core functionality +* Testing corner cases left out of the current set of tests (identified by looking at code coverage) +* Increasing the matrix of simulators, operating system, and Python installations tested in CI + +Testing improvements don't require maintainer pre-approval, but require a pull request. +Please see the [guidelines on submitting pull requests](#patch-requirements). + + +### Features + +Cocotb is still in development and new features are still welcome and appreciated; +as long as they stay [in scope](#Architecture-and-Scope-of-Cocotb). +Cocotb is comprised of several major codebases, each requiring different sets of skills and development process. +Instead of including that breakdown here, it is done in the [internal documentation](https://github.com/cocotb/cocotb/wiki/cocotb-Internals). + +Small improvements to existing features generally do not require maintainer pre-approval. +Large changes, approximately >150 LOC changed, and new features generally require maintainer pre-approval. +If a change is deemed too large for the main repo, or out of scope, +please feel free to make it an [extension](https://docs.cocotb.org/en/latest/extensions.html). + +***New features must not break existing features.*** + +Feature changes require full coverage of the added feature. +This likely requires adding new unit tests to the `tests` directory. +Issue-specific test directories will not be accepted, unless a special HDL entity is required. +Instead, place the test in an existing test suite (`test_cocotb`, `test_discovery`, etc.). + +Features should generally follow the following design principles: + +* Something the user cannot do without assistance of cocotb-specific code +* Orthogonal to existing features +* Easily composed with existing features +* Limited in scope and impervious to scope creep + + +### Bugfixes + +**!WARNING!** Bugfixing cocotb is not for the faint of heart! + +Bugs happen. +cocotb supports many simulators that have inconsistent support for the procedural interfaces cocotb depends on, +and it has a number of features that aren't wholly tested yet. +There are likely many bugs lurking, waiting to be found; +which is why increasing testing and code coverage is important. +Working on bugfixing can be very challenging, depending on the cause of the bug. +In general, bugfixing requires knowledge of: + +* How cocotb works +* [cocotb's debugging utilities](https://github.com/cocotb/cocotb/wiki/Debugging-HOW-TOs#cocotb-debugging-functionality) +* (Optional) Simulator interfaces (VPI, VHPI, and FLI) +* (Optional) Python debugging tools ([pdb](https://github.com/cocotb/cocotb/wiki/Debugging-HOW-TOs#using-a-remote-python-debugger), [dowser](https://github.com/cocotb/cocotb/wiki/Debugging-HOW-TOs#debugging-python-memory-usage)) +* (Optional) C/C++ debugging tools ([gdb](https://github.com/cocotb/cocotb/wiki/Debugging-HOW-TOs#using-a-remote-cc-debugger), [valgrind](https://github.com/cocotb/cocotb/wiki/Debugging-HOW-TOs#debugging-cc-memory-usage)) +* (Optional) Specific simulators (sometimes the bug exists in the simulator and not cocotb) + +Fixing a bug follows the procedure: + +1. Locate buggy behavior, make a Github issue + * Maintainers may be able to offer more information, confirm it as a bug, or confirm it as expected behavior +2. Make a Minimum Reproducible Failing Example (MRFE, pronounced like Murphy, like the law :) + * Confirms the bug + * Add to [regressions](#running-tests-locally) +3. Open a new draft pull request with the MRFE test + * It should cause CI to fail +4. Determine scope of the bug, and add that detail to the pull request + * Which simulators/interfaces are affected? + * Which Python versions? + * Which operating systems? +5. Determine the cause of the bug, and add that detail to the pull request + * May require Python or C debugging, or the builtin cocotb debugging utilities +6. Make a fix, and push it up on the PR branch + * It should cause the CI to pass + * The fix should not break other existing functionality + +Details on how to debug cocotb can be found on the [Wiki](https://github.com/cocotb/cocotb/wiki/Debugging-HOW-TOs). + + +### Deprecations and Removals + +Cocotb's treatment of deprecations and removal follows guidelines laid out [here](https://symfony.com/doc/current/setup/upgrade_major.html#make-your-code-deprecation-free). +Deprecations serve the following purposes: + +* Remove legacy code that has been deemed out of scope +* Remove support for a simulator, OS, or Python version that is past end-of-life +* Remove potentially dangerous, broken, and misunderstood interfaces (usually accompanied with a superior alternative) + +Deprecations can be incorporated at any time. +They are typically implemented by [issuing a `DeprecationWarning`](https://docs.python.org/3/library/warnings.html#warnings.warn) in Python code; +or [issuing a LOG_WARN](https://docs.cocotb.org/en/stable/generated/file/gpi__logging_8h.html?highlight=LOG_WARN#c.LOG_WARN) with `DEPRECATED` in the message in C++ code. + +Removals only occur on major version bumps. +One can create removal pull requests at any time, on the condition they will not be accepted until the next release is known to be a major version release. + + +### Breaking Changes + +Breaking changes are changes to the interface or behavior of a user-facing entity. +They are necessary when a user-facing interfaces are broken in a way that cannot be changed without changing the behavior of user's code. +In these situations it is ideal to be able to implement a switch between new better behavior and the old broken behavior. +On major version bumps, this switch will be deprecated and the new behavior will become the default. + +In cases where behavioral switches are not easy to implement, breaking changes will attempt to be broadcasted to user by [issuing a `DeprecationWarning`](https://docs.python.org/3/library/warnings.html#warnings.warn) when the to-be-changed behavior is invoked. +Before major releases, pending breaking changes will be incorporated. + +One can create pull requests with breaking changes at any time, on the condition they will not be accepted until the next release is known to be a major version release. + + ## Setting Up a Development Environment Assuming you have used cocotb prior to reading this guide, you will already have the cocotb [installation prerequisites](https://docs.cocotb.org/en/latest/install.html) and standard development tools (editor, shell, git, etc.) installed. @@ -84,10 +290,11 @@ python -m pip install --global-option build_ext --global-option --compiler=mingw ``` Once that has been done, you can navigate to the directory containing the test you wish to run. -Then you may issue an [appropriate]https://docs.cocotb.org/en/latest/building.html#makefile-based-test-scripts) `make` command: +Then you may issue an [appropriate](https://docs.cocotb.org/en/latest/building.html#makefile-based-test-scripts) `make` command. +For example, if you want to test with Icarus using Verilog sources: ```command -make SIM=icarus +make SIM=icarus TOPLEVEL_LANG=verilog ``` @@ -126,6 +333,18 @@ However, none of these rules are set in stone. They can and should be challenged at times to ensure the project stays relevant to the majority of its users. +## Maintainer Pre-approval + +After making changes to cocotb, changes must be approved by at least one maintainer before being included. +Out-of-scope and breaking changes ***will not be accepted***. +Also a maintainer could object to a change due to implementation approach or code quality reasons. +To potentially save you frustration and time, it is a good idea to get maintainer pre-approval on the task before starting it. + +The best way to get maintainer pre-approval is to make a [Github issue](https://github.com/cocotb/cocotb/issues). +These issues can be a place for maintainers, as well as other users, to voice opinions on a proposed change before the task is worked. +You may also propose changes on the [Gitter channel](https://gitter.im/cocotb/Lobby) or by directly contacting a [maintainer](#maintainer). + + ## How to Get Changes Merged Have you fixed a bug in cocotb, or want to add new functionality to it? @@ -274,6 +493,14 @@ Founders - Chris Higgs (@chiggs) - Stuart Hodgson (@stuarthodgson) +### Getting in Contact with a Maintainer + +All of the maintainers are active on the [Gitter channel](https://github.com/cocotb/cocotb). +They prefer inquiries go through direct messages on Gitter, +or by mentioning them in the main [cocotb Gitter channel](https://gitter.im/cocotb/Lobby) using `@{maintainer name}`. +Maintainers are unpaid volunteers, so it might take a while for a maintainer to get back to you. + + ## Code of Conduct The cocotb development community aims to be welcoming to everyone. From 16262709489c1eee8514481afcd26389e4eca6f7 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 23 Dec 2020 12:53:49 +0100 Subject: [PATCH 2611/2656] Remove unused Dockerfile - was used only in the obsolete Travis CI setup --- Dockerfile | 41 ----------------------------------------- 1 file changed, 41 deletions(-) delete mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index ea0c0b51..00000000 --- a/Dockerfile +++ /dev/null @@ -1,41 +0,0 @@ -FROM ubuntu:16.04 - -# travis-ci only provides 2 -ARG MAKE_JOBS=-j2 - -# Simulation -ARG ICARUS_VERILOG_VERSION=10_3 - -RUN apt-get -qq update && apt-get -qq install -y --no-install-recommends \ - wget \ - git \ - gperf \ - make \ - autoconf \ - g++ \ - flex \ - bison \ - python3-dev\ - python3-pip \ - python3-setuptools \ - python3 \ - virtualenv \ - python3-venv \ - swig \ - && rm -rf /var/lib/apt/lists/* \ - && apt-get clean \ - && pip3 install --upgrade pip \ - && g++ --version - -# Icarus Verilog -ENV ICARUS_VERILOG_VERSION=${ICARUS_VERILOG_VERSION} -WORKDIR /usr/src/iverilog -RUN git clone https://github.com/steveicarus/iverilog.git --depth=1 --branch v${ICARUS_VERILOG_VERSION} . \ - && sh autoconf.sh \ - && ./configure \ - && make -s ${MAKE_JOBS} \ - && make -s install \ - && rm -r /usr/src/iverilog - -# make sources available in docker image - one copy per python version -COPY . /src From 50418e54f000316257404134a57557ffb877e2c8 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Fri, 25 Dec 2020 23:56:51 +0100 Subject: [PATCH 2612/2656] Fix example code for rotating logger Correct method name removeHandler(). Prevent collision with our log alias for _log. Give an explicity logfile name to make example more standalone. --- documentation/source/rotating_logger.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/documentation/source/rotating_logger.rst b/documentation/source/rotating_logger.rst index 2d69b049..d6f9c8a3 100644 --- a/documentation/source/rotating_logger.rst +++ b/documentation/source/rotating_logger.rst @@ -13,15 +13,16 @@ and the Python documentation for :class:`logging.handlers.RotatingFileHandler`. .. code-block:: python3 from logging.handlers import RotatingFileHandler + from cocotb.log import SimLogFormatter root_logger = logging.getLogger() # undo the setup cocotb did for handler in root_logger.handlers: - root_logger.remove_handler(handler) + root_logger.removeHandler(handler) handler.close() # do whatever configuration you want instead - file_handler = RotatingFileHandler(logfile, maxBytes=(5 * 1024 * 1024), backupCount=2) - file_handler.setFormatter(cocotb.log.SimLogFormatter()) + file_handler = RotatingFileHandler("rotating.log", maxBytes=(5 * 1024 * 1024), backupCount=2) + file_handler.setFormatter(SimLogFormatter()) root_logger.addHandler(file_handler) From ccbdc8c30cfce14ccfff10518336bf6d2bb6764f Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 20 Jul 2020 22:41:09 -0500 Subject: [PATCH 2613/2656] Move Python GPI logger out of gpi_logging library Moves the Python implementation of the GPI log handler to a different library. Refactors the interface to allow users to register GPI log handlers with the gpi_log library. This new interface is designed with the Python implementation in mind, but it is more flexible. The pygpilog library now handles the Python logger and filter functions directly rather than delegating it to the gpi_logging library. This removes the dependency on Python from the GPI. --- cocotb/share/include/gpi_logging.h | 189 ++++++++++++-- cocotb/share/include/py_gpi_logging.h | 31 +++ cocotb/share/lib/embed/gpi_embed.cpp | 23 +- cocotb/share/lib/gpi_log/gpi_logging.cpp | 244 +++++++----------- .../share/lib/py_gpi_log/py_gpi_logging.cpp | 169 ++++++++++++ .../share/lib/simulator/simulatormodule.cpp | 12 +- cocotb_build_libs.py | 27 +- 7 files changed, 498 insertions(+), 197 deletions(-) create mode 100644 cocotb/share/include/py_gpi_logging.h create mode 100644 cocotb/share/lib/py_gpi_log/py_gpi_logging.cpp diff --git a/cocotb/share/include/gpi_logging.h b/cocotb/share/include/gpi_logging.h index e228331e..b8620338 100644 --- a/cocotb/share/include/gpi_logging.h +++ b/cocotb/share/include/gpi_logging.h @@ -37,39 +37,180 @@ #define GPILOG_EXPORT COCOTB_IMPORT #endif +#include + #ifdef __cplusplus -# define EXTERN_C_START extern "C" { -# define EXTERN_C_END } -#else -# define EXTERN_C_START -# define EXTERN_C_END +extern "C" { #endif -EXTERN_C_START - +/** Named logging level + * + * The native logger only logs level names at these log level values. + * They were specifically chosen to align with the default level values in the Python logging module. + * Implementers of custom loggers should emit human readable level names for these value, but may support other values + */ enum gpi_log_levels { - GPIDebug = 10, - GPIInfo = 20, - GPIWarning = 30, - GPIError = 40, - GPICritical = 50 + GPIDebug = 10, ///< Prints `DEBUG` by default. Verbose information, useful for debugging + GPIInfo = 20, ///< Prints `INFO` by default. Information about major events in the current program + GPIWarning = 30, ///< Prints `WARN` by default. Encountered a recoverable bug, or information about surprising behavior + GPIError = 40, ///< Prints `ERROR` by default. An unrecoverable error + GPICritical = 50 ///< Prints `CRITICAL` by default. An unrecoverable error, to be followed by immediate simulator shutdown }; +/** Logs a message at the given log level using the current log handler. + Automatically populates arguments using information in the called context. + @param level The level at which to log the message + */ +#define LOG_(level, ...) \ + gpi_log("cocotb.gpi", level, __FILE__, __func__, __LINE__, __VA_ARGS__); + +/** Logs a message at DEBUG log level using the current log handler. + Automatically populates arguments using information in the called context. + */ +#define LOG_DEBUG(...) LOG_(GPIDebug, __VA_ARGS__) + +/** Logs a message at INFO log level using the current log handler. + Automatically populates arguments using information in the called context. + */ +#define LOG_INFO(...) LOG_(GPIInfo, __VA_ARGS__); + +/** Logs a message at WARN log level using the current log handler. + Automatically populates arguments using information in the called context. + */ +#define LOG_WARN(...) LOG_(GPIWarning, __VA_ARGS__); + +/** Logs a message at ERROR log level using the current log handler. + Automatically populates arguments using information in the called context. + */ +#define LOG_ERROR(...) LOG_(GPIError, __VA_ARGS__); + +/** Logs a message at CRITICAL log level using the current log handler. + Automatically populates arguments using information in the called context. + */ +#define LOG_CRITICAL(...) LOG_(GPICritical, __VA_ARGS__); -#define LOG_DEBUG(...) gpi_log("cocotb.gpi", GPIDebug, __FILE__, __func__, __LINE__, __VA_ARGS__); -#define LOG_INFO(...) gpi_log("cocotb.gpi", GPIInfo, __FILE__, __func__, __LINE__, __VA_ARGS__); -#define LOG_WARN(...) gpi_log("cocotb.gpi", GPIWarning, __FILE__, __func__, __LINE__, __VA_ARGS__); -#define LOG_ERROR(...) gpi_log("cocotb.gpi", GPIError, __FILE__, __func__, __LINE__, __VA_ARGS__); -#define LOG_CRITICAL(...) gpi_log("cocotb.gpi", GPICritical, __FILE__, __func__, __LINE__, __VA_ARGS__); +/** Type of a log handler function. + @param userdata private implementation data registered with this function + @param name Name of the logger + @param level Level at which to log the message + @param pathname Name of the file where the call site is located + @param funcname Name of the function where the call site is located + @param lineno Line number of the call site + @param msg The message to log, uses C-sprintf-style format specifier + @param args Additional arguments; formatted and inserted in message according to format specifier in msg argument + */ +typedef void (gpi_log_handler_type)( + void *userdata, + const char *name, + int level, + const char *pathname, + const char *funcname, + long lineno, + const char *msg, + va_list args); -GPILOG_EXPORT void set_log_handler(void *handler); -GPILOG_EXPORT void clear_log_handler(void); -GPILOG_EXPORT void set_log_filter(void *filter); -GPILOG_EXPORT void clear_log_filter(void); -GPILOG_EXPORT void set_log_level(enum gpi_log_levels new_level); +/** Log a message using the currently registered log handler. + User is expected to populate all arguments to this function. + @param name Name of the logger + @param level Level at which to log the message + @param pathname Name of the file where the call site is located + @param funcname Name of the function where the call site is located + @param lineno Line number of the call site + @param msg The message to log, uses C-sprintf-style format specifier + @param ... Additional arguments; formatted and inserted in message according to format specifier in msg argument + */ +GPILOG_EXPORT void gpi_log( + const char *name, + int level, + const char *pathname, + const char *funcname, + long lineno, + const char *msg, + ...); -GPILOG_EXPORT void gpi_log(const char *name, enum gpi_log_levels level, const char *pathname, const char *funcname, long lineno, const char *msg, ...); +/** Log a message using the currently registered log handler. + User is expected to populate all arguments to this function. + @param name Name of the logger + @param level Level at which to log the message + @param pathname Name of the file where the call site is located + @param funcname Name of the function where the call site is located + @param lineno Line number of the call site + @param msg The message to log, uses C-sprintf-style format specifier + @param args Additional arguments; formatted and inserted in message according to format specifier in msg argument + */ +GPILOG_EXPORT void gpi_vlog( + const char *name, + int level, + const char *pathname, + const char *funcname, + long lineno, + const char *msg, + va_list args); -EXTERN_C_END +/** Retrieve the current log handler. + @param handler Location to return current log handler. If no custom logger is registered this will be `NULL`. + @param userdata Location to return log handler userdata + */ +GPILOG_EXPORT void gpi_get_log_handler(gpi_log_handler_type **handler, void **userdata); + +/** Set custom log handler + @param handler Handler function to call when the GPI logs a message + @param userdata Data to pass to the handler function when logging a message + */ +GPILOG_EXPORT void gpi_set_log_handler(gpi_log_handler_type *handler, void *userdata); + +/** Clear the current custom log handler and use native logger + */ +GPILOG_EXPORT void gpi_clear_log_handler(void); + +/** Log a message using the native log handler. + User is expected to populate all arguments to this function. + @param name Name of the logger + @param level Level at which to log the message + @param pathname Name of the file where the call site is located + @param funcname Name of the function where the call site is located + @param lineno Line number of the call site + @param msg The message to log, uses C-sprintf-style format specifier + @param ... Additional arguments; formatted and inserted in message according to format specifier in msg argument + */ +GPILOG_EXPORT void gpi_native_logger_log( + const char *name, + int level, + const char *pathname, + const char *funcname, + long lineno, + const char *msg, + ...); + +/** Log a message using the native log handler. + User is expected to populate all arguments to this function. + @param name Name of the logger + @param level Level at which to log the message + @param pathname Name of the file where the call site is located + @param funcname Name of the function where the call site is located + @param lineno Line number of the call site + @param msg The message to log, uses C-sprintf-style format specifier + @param args Additional arguments; formatted and inserted in message according to format specifier in msg argument + */ +GPILOG_EXPORT void gpi_native_logger_vlog( + const char *name, + int level, + const char *pathname, + const char *funcname, + long lineno, + const char *msg, + va_list args); + +/** Set minimum logging level of the native logger. + If a logging request occurs where the logging level is lower than the level set by this function, it is not logged. + Only affects the native logger. + @param level Logging level + @return Previous logging level + */ +GPILOG_EXPORT int gpi_native_logger_set_level(int level); + +#ifdef __cplusplus +} +#endif #endif /* COCOTB_GPI_LOGGING_H_ */ diff --git a/cocotb/share/include/py_gpi_logging.h b/cocotb/share/include/py_gpi_logging.h new file mode 100644 index 00000000..bbfd8749 --- /dev/null +++ b/cocotb/share/include/py_gpi_logging.h @@ -0,0 +1,31 @@ +// Copyright cocotb contributors +// Licensed under the Revised BSD License, see LICENSE for details. +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef PY_GPI_LOGGING_H +#define PY_GPI_LOGGING_H + +#include +#ifdef PYGPILOG_EXPORTS +#define PYGPILOG_EXPORT COCOTB_EXPORT +#else +#define PYGPILOG_EXPORT COCOTB_IMPORT +#endif + +#define PY_GPI_LOG_SIZE 1024 + +#ifdef __cplusplus +extern "C" { +#endif + +PYGPILOG_EXPORT void py_gpi_logger_set_level(int level); + +PYGPILOG_EXPORT void py_gpi_logger_initialize(PyObject * handler, PyObject * filter); + +PYGPILOG_EXPORT void py_gpi_logger_finalize(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cocotb/share/lib/embed/gpi_embed.cpp b/cocotb/share/lib/embed/gpi_embed.cpp index 6b0735d7..6447179c 100644 --- a/cocotb/share/lib/embed/gpi_embed.cpp +++ b/cocotb/share/lib/embed/gpi_embed.cpp @@ -31,7 +31,9 @@ #include #include -#include "embed.h" +#include // LOG_* macros +#include // py_gpi_logger_set_level, py_gpi_logger_initialize, py_gpi_logger_finalize +#include // gpi_event_t #include "locale.h" #include @@ -181,8 +183,7 @@ extern "C" void embed_sim_cleanup(void) PyGILState_Ensure(); // Don't save state as we are calling Py_Finalize Py_DecRef(pEventFn); pEventFn = NULL; - clear_log_handler(); - clear_log_filter(); + py_gpi_logger_finalize(); Py_Finalize(); to_simulator(); } @@ -228,7 +229,8 @@ extern "C" int embed_sim_init(int argc, char const * const * argv) PyObject *cocotb_module, *cocotb_init, *cocotb_retval; PyObject *cocotb_log_module = NULL; - PyObject *simlog_func; + PyObject *log_func; + PyObject *filter_func; PyObject *argv_list; cocotb_module = NULL; @@ -248,24 +250,23 @@ extern "C" int embed_sim_init(int argc, char const * const * argv) } // Obtain the function to use when logging from C code - simlog_func = PyObject_GetAttrString(cocotb_log_module, "_log_from_c"); // New reference - if (simlog_func == NULL) { + log_func = PyObject_GetAttrString(cocotb_log_module, "_log_from_c"); // New reference + if (log_func == NULL) { PyErr_Print(); LOG_ERROR("Failed to get the _log_from_c function"); goto cleanup; } - set_log_handler(simlog_func); // Note: This function steals a reference to simlog_func. - // Obtain the function to check whether to call log function - simlog_func = PyObject_GetAttrString(cocotb_log_module, "_filter_from_c"); // New reference - if (simlog_func == NULL) { + filter_func = PyObject_GetAttrString(cocotb_log_module, "_filter_from_c"); // New reference + if (filter_func == NULL) { + Py_DECREF(log_func); PyErr_Print(); LOG_ERROR("Failed to get the _filter_from_c method"); goto cleanup; } - set_log_filter(simlog_func); // Note: This function steals a reference to simlog_func. + py_gpi_logger_initialize(log_func, filter_func); // Note: This function steals references to log_func and filter_func. pEventFn = PyObject_GetAttrString(cocotb_module, "_sim_event"); // New reference if (pEventFn == NULL) { diff --git a/cocotb/share/lib/gpi_log/gpi_logging.cpp b/cocotb/share/lib/gpi_log/gpi_logging.cpp index 29855740..2006bf2a 100644 --- a/cocotb/share/lib/gpi_log/gpi_logging.cpp +++ b/cocotb/share/lib/gpi_log/gpi_logging.cpp @@ -27,41 +27,103 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ -#include #include +#include +#include +#include -// Used to log using the standard python mechanism -static PyObject *pLogHandler = NULL; -static PyObject *pLogFilter = NULL; -static enum gpi_log_levels local_level = GPIInfo; -extern "C" void set_log_handler(void *handler) +static gpi_log_handler_type *current_handler = nullptr; +static void *current_userdata = nullptr; + + +extern "C" void gpi_log( + const char *name, + int level, + const char *pathname, + const char *funcname, + long lineno, + const char *msg, + ...) { - pLogHandler = (PyObject *)handler; // Note: This function steals a reference to handler. + va_list argp; + va_start(argp, msg); + gpi_vlog(name, level, pathname, funcname, lineno, msg, argp); + va_end(argp); } -extern "C" void clear_log_handler(void) + +extern "C" void gpi_vlog( + const char *name, + int level, + const char *pathname, + const char *funcname, + long lineno, + const char *msg, + va_list argp) { - Py_XDECREF(pLogHandler); - pLogHandler = NULL; + if (current_handler) { + (*current_handler)( + current_userdata, + name, + level, + pathname, + funcname, + lineno, + msg, + argp); + } else { + gpi_native_logger_vlog(name, + level, + pathname, + funcname, + lineno, + msg, + argp); + } } -extern "C" void set_log_filter(void *filter) + +extern "C" void gpi_get_log_handler(gpi_log_handler_type **handler, void **userdata) { - pLogFilter = (PyObject *)filter; // Note: This function steals a reference to filter. + *handler = current_handler; + *userdata = current_userdata; } -extern "C" void clear_log_filter(void) + +extern "C" void gpi_set_log_handler(gpi_log_handler_type *handler, void *userdata) { - Py_XDECREF(pLogFilter); - pLogFilter = NULL; + current_handler = handler; + current_userdata = userdata; } -extern "C" void set_log_level(enum gpi_log_levels new_level) + +extern "C" void gpi_clear_log_handler(void) +{ + current_handler = nullptr; + current_userdata = nullptr; +} + + +static int current_native_logger_level = GPIInfo; + + +extern "C" void gpi_native_logger_log( + const char *name, + int level, + const char *pathname, + const char *funcname, + long lineno, + const char *msg, + ...) { - local_level = new_level; + va_list argp; + va_start(argp, msg); + gpi_native_logger_vlog(name, level, pathname, funcname, lineno, msg, argp); + va_end(argp); } + // Decode the level into a string matching the Python interpretation struct _log_level_table { long level; @@ -98,16 +160,16 @@ const char *log_level(long level) #define LOG_SIZE 512 static char log_buff[LOG_SIZE]; - -/** - * Log without going through the python hook. - * - * This is needed for both when the hook isn't connected yet, and for when a - * python exception occurs while trying to use the hook. - */ -static void gpi_log_native_v(const char *name, enum gpi_log_levels level, const char *pathname, const char *funcname, long lineno, const char *msg, va_list argp) +extern "C" void gpi_native_logger_vlog( + const char *name, + int level, + const char *pathname, + const char *funcname, + long lineno, + const char *msg, + va_list argp) { - if (level < GPIInfo) { + if (level < current_native_logger_level) { return; } @@ -135,132 +197,10 @@ static void gpi_log_native_v(const char *name, enum gpi_log_levels level, const fflush(stdout); } -static void gpi_log_native(const char *name, enum gpi_log_levels level, const char *pathname, const char *funcname, long lineno, const char *msg, ...) -{ - va_list argp; - va_start(argp, msg); - gpi_log_native_v(name, level, pathname, funcname, lineno, msg, argp); - va_end(argp); -} - -/** - * @name GPI logging - * @brief Write a log message using cocotb SimLog class - * @ingroup python_c_api - * - * GILState before calling: Unknown - * - * GILState after calling: Unknown - * - * Makes one call to PyGILState_Ensure and one call to PyGILState_Release - * - * If the Python logging mechanism is not initialised, dumps to `stderr`. - * - */ -static void gpi_log_v(const char *name, enum gpi_log_levels level, const char *pathname, const char *funcname, long lineno, const char *msg, va_list argp) -{ - /* We first check that the log level means this will be printed - * before going to the expense of formatting the variable arguments - */ - if (!pLogHandler) { - gpi_log_native_v(name, level, pathname, funcname, lineno, msg, argp); - return; - } - - if (level < local_level) - return; - - PyGILState_STATE gstate = PyGILState_Ensure(); - - // Declared here in order to be initialized before any goto statements and refcount cleanup - PyObject *logger_name_arg = NULL, *filename_arg = NULL, *lineno_arg = NULL, *msg_arg = NULL, *function_arg = NULL; - - PyObject *level_arg = PyLong_FromLong(level); // New reference - if (level_arg == NULL) { - goto error; - } - - logger_name_arg = PyUnicode_FromString(name); // New reference - if (logger_name_arg == NULL) { - goto error; - } - - { - // check if log level is enabled - PyObject *filter_ret = PyObject_CallFunctionObjArgs(pLogFilter, logger_name_arg, level_arg, NULL); - if (filter_ret == NULL) { - goto error; - } - - int is_enabled = PyObject_IsTrue(filter_ret); - Py_DECREF(filter_ret); - if (is_enabled < 0) { - /* A python exception occured while converting `filter_ret` to bool */ - goto error; - } - - if (!is_enabled) { - goto ok; - } - } - - // Ignore truncation - { - int n = vsnprintf(log_buff, LOG_SIZE, msg, argp); - if (n < 0 || n >= LOG_SIZE) { - fprintf(stderr, "Log message construction failed\n"); - } - } - - filename_arg = PyUnicode_FromString(pathname); // New reference - if (filename_arg == NULL) { - goto error; - } - - lineno_arg = PyLong_FromLong(lineno); // New reference - if (lineno_arg == NULL) { - goto error; - } - - msg_arg = PyUnicode_FromString(log_buff); // New reference - if (msg_arg == NULL) { - goto error; - } - - function_arg = PyUnicode_FromString(funcname); // New reference - if (function_arg == NULL) { - goto error; - } - { - // Log function args are logger_name, level, filename, lineno, msg, function - PyObject *handler_ret = PyObject_CallFunctionObjArgs(pLogHandler, logger_name_arg, level_arg, filename_arg, lineno_arg, msg_arg, function_arg, NULL); - if (handler_ret == NULL) { - goto error; - } - Py_DECREF(handler_ret); - } - - goto ok; -error: - /* Note: don't call the LOG_ERROR macro because that might recurse */ - gpi_log_native_v(name, level, pathname, funcname, lineno, msg, argp); - gpi_log_native("cocotb.gpi", GPIError, __FILE__, __func__, __LINE__, "Error calling Python logging function from C while logging the above"); - PyErr_Print(); -ok: - Py_XDECREF(logger_name_arg); - Py_XDECREF(level_arg); - Py_XDECREF(filename_arg); - Py_XDECREF(lineno_arg); - Py_XDECREF(msg_arg); - Py_XDECREF(function_arg); - PyGILState_Release(gstate); -} - -extern "C" void gpi_log(const char *name, enum gpi_log_levels level, const char *pathname, const char *funcname, long lineno, const char *msg, ...) +extern "C" int gpi_native_logger_set_level(int level) { - va_list argp; - va_start(argp, msg); - gpi_log_v(name, level, pathname, funcname, lineno, msg, argp); - va_end(argp); + int old_level = current_native_logger_level; + current_native_logger_level = level; + return old_level; } diff --git a/cocotb/share/lib/py_gpi_log/py_gpi_logging.cpp b/cocotb/share/lib/py_gpi_log/py_gpi_logging.cpp new file mode 100644 index 00000000..df47990f --- /dev/null +++ b/cocotb/share/lib/py_gpi_log/py_gpi_logging.cpp @@ -0,0 +1,169 @@ +// Copyright cocotb contributors +// Licensed under the Revised BSD License, see LICENSE for details. +// SPDX-License-Identifier: BSD-3-Clause + +#include // all things Python +#include // all things GPI logging +#include // PY_GPI_LOG_SIZE +#include // va_list, va_copy, va_end +#include // fprintf, vsnprintf + + +static PyObject *pLogHandler = nullptr; + +static PyObject *pLogFilter = nullptr; + +static int py_gpi_log_level = GPIInfo; + + +static void py_gpi_log_handler( + void *, + const char *name, + int level, + const char *pathname, + const char *funcname, + long lineno, + const char *msg, + va_list argp) +{ + if (level < py_gpi_log_level) { + return; + } + + va_list argp_copy; + va_copy(argp_copy, argp); + + PyGILState_STATE gstate = PyGILState_Ensure(); + + // Declared here in order to be initialized before any goto statements and refcount cleanup + PyObject *logger_name_arg = NULL, *filename_arg = NULL, *lineno_arg = NULL, *msg_arg = NULL, *function_arg = NULL; + + PyObject *level_arg = PyLong_FromLong(level); // New reference + if (level_arg == NULL) { + // LCOV_EXCL_START + goto error; + // LCOV_EXCL_STOP + } + + logger_name_arg = PyUnicode_FromString(name); // New reference + if (logger_name_arg == NULL) { + // LCOV_EXCL_START + goto error; + // LCOV_EXCL_STOP + } + + { + // check if log level is enabled + PyObject *filter_ret = PyObject_CallFunctionObjArgs(pLogFilter, logger_name_arg, level_arg, NULL); + if (filter_ret == NULL) { + // LCOV_EXCL_START + goto error; + // LCOV_EXCL_STOP + } + + int is_enabled = PyObject_IsTrue(filter_ret); + Py_DECREF(filter_ret); + if (is_enabled < 0) { + // LCOV_EXCL_START + /* A Python exception occured while converting `filter_ret` to bool */ + goto error; + // LCOV_EXCL_STOP + } + + if (!is_enabled) { + goto ok; + } + } + + static char log_buff[PY_GPI_LOG_SIZE]; + + // Ignore truncation + { + int n = vsnprintf(log_buff, sizeof(log_buff), msg, argp); + if (n < 0 || n >= (int)sizeof(log_buff)) { + // LCOV_EXCL_START + fprintf(stderr, "Log message construction failed\n"); + // LCOV_EXCL_STOP + } + } + + filename_arg = PyUnicode_FromString(pathname); // New reference + if (filename_arg == NULL) { + // LCOV_EXCL_START + goto error; + // LCOV_EXCL_STOP + } + + lineno_arg = PyLong_FromLong(lineno); // New reference + if (lineno_arg == NULL) { + // LCOV_EXCL_START + goto error; + // LCOV_EXCL_STOP + } + + msg_arg = PyUnicode_FromString(log_buff); // New reference + if (msg_arg == NULL) { + // LCOV_EXCL_START + goto error; + // LCOV_EXCL_STOP + } + + function_arg = PyUnicode_FromString(funcname); // New reference + if (function_arg == NULL) { + // LCOV_EXCL_START + goto error; + // LCOV_EXCL_STOP + } + + { + // Log function args are logger_name, level, filename, lineno, msg, function + PyObject *handler_ret = PyObject_CallFunctionObjArgs(pLogHandler, logger_name_arg, level_arg, filename_arg, lineno_arg, msg_arg, function_arg, NULL); + if (handler_ret == NULL) { + // LCOV_EXCL_START + goto error; + // LCOV_EXCL_STOP + } + Py_DECREF(handler_ret); + } + + goto ok; +error: + // LCOV_EXCL_START + /* Note: don't call the LOG_ERROR macro because that might recurse */ + gpi_native_logger_vlog(name, level, pathname, funcname, lineno, msg, argp_copy); + gpi_native_logger_log("cocotb.gpi", GPIError, __FILE__, __func__, __LINE__, "Error calling Python logging function from C++ while logging the above"); + PyErr_Print(); + // LCOV_EXCL_STOP +ok: + va_end(argp_copy); + Py_XDECREF(logger_name_arg); + Py_XDECREF(level_arg); + Py_XDECREF(filename_arg); + Py_XDECREF(lineno_arg); + Py_XDECREF(msg_arg); + Py_XDECREF(function_arg); + PyGILState_Release(gstate); +} + + +extern "C" void py_gpi_logger_set_level(int level) +{ + py_gpi_log_level = level; + gpi_native_logger_set_level(level); +} + + +extern "C" void py_gpi_logger_initialize(PyObject * handler, PyObject * filter) +{ + pLogHandler = handler; + pLogFilter = filter; + gpi_set_log_handler(py_gpi_log_handler, nullptr); +} + + +extern "C" void py_gpi_logger_finalize() +{ + gpi_clear_log_handler(); + Py_XDECREF(pLogHandler); + Py_XDECREF(pLogFilter); +} diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index 3384442a..11b611a0 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -39,10 +39,11 @@ static int releases = 0; static int sim_ending = 0; -#include // COCOTB_UNUSED +#include // COCOTB_UNUSED #include #include -#include "gpi_logging.h" +#include // LOG_* macros +#include // py_gpi_logger_set_level #include "gpi.h" // This file defines the routines available to Python @@ -897,17 +898,18 @@ static PyObject *deregister(gpi_hdl_Object *self, PyObject *args) Py_RETURN_NONE; } + static PyObject *log_level(PyObject *self, PyObject *args) { COCOTB_UNUSED(self); - long l_level; + int l_level; - if (!PyArg_ParseTuple(args, "l:log_level", &l_level)) { + if (!PyArg_ParseTuple(args, "i:log_level", &l_level)) { return NULL; } - set_log_level((enum gpi_log_levels)l_level); + py_gpi_logger_set_level(l_level); Py_RETURN_NONE; } diff --git a/cocotb_build_libs.py b/cocotb_build_libs.py index 785af14c..1559ebe0 100755 --- a/cocotb_build_libs.py +++ b/cocotb_build_libs.py @@ -344,14 +344,31 @@ def _get_common_lib_ext(include_dir, share_lib_dir): libgpilog_sources += ["libgpilog.rc"] libgpilog = Extension( os.path.join("cocotb", "libs", "libgpilog"), - define_macros=[("GPILOG_EXPORTS", "")] + _extra_defines, + define_macros=[("GPILOG_EXPORTS", "")] + _extra_defines, include_dirs=[include_dir], - library_dirs=python_lib_dirs, sources=libgpilog_sources, extra_link_args=_extra_link_args(lib_name="libgpilog", rpaths=["$ORIGIN"]), extra_compile_args=_extra_cxx_compile_args, ) + # + # libpygpilog + # + libpygpilog_sources = [ + os.path.join(share_lib_dir, "py_gpi_log", "py_gpi_logging.cpp") + ] + if os.name == "nt": + libpygpilog_sources += ["libpygpilog.rc"] + libpygpilog = Extension( + os.path.join("cocotb", "libs", "libpygpilog"), + define_macros=[("PYGPILOG_EXPORTS", "")] + _extra_defines, + include_dirs=[include_dir], + libraries=["gpilog"], + sources=libpygpilog_sources, + extra_link_args=_extra_link_args(lib_name="libpygpilog", rpaths=["$ORIGIN"]), + extra_compile_args=_extra_cxx_compile_args, + ) + # # libcocotb # @@ -364,7 +381,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): os.path.join("cocotb", "libs", "libcocotb"), define_macros=[("COCOTB_EMBED_EXPORTS", ""), ("PYTHON_SO_LIB", _get_python_lib())] + _extra_defines, include_dirs=[include_dir], - libraries=[_get_python_lib_link(), "gpilog", "cocotbutils"], + libraries=[_get_python_lib_link(), "gpilog", "cocotbutils", "pygpilog"], library_dirs=python_lib_dirs, sources=libcocotb_sources, extra_link_args=_extra_link_args(lib_name="libcocotb", rpaths=["$ORIGIN"] + python_lib_dirs), @@ -402,7 +419,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): os.path.join("cocotb", "simulator"), define_macros=_extra_defines, include_dirs=[include_dir], - libraries=["cocotbutils", "gpilog", "gpi"], + libraries=["cocotbutils", "gpilog", "gpi", "pygpilog"], library_dirs=python_lib_dirs, sources=simulator_sources, extra_compile_args=_extra_cxx_compile_args, @@ -412,7 +429,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): # The libraries in this list are compiled in order of their appearance. # If there is a linking dependency on one library to another, # the linked library must be built first. - return [libgpilog, libcocotbutils, libcocotb, libgpi, libsim] + return [libgpilog, libpygpilog, libcocotbutils, libcocotb, libgpi, libsim] def _get_vpi_lib_ext( From 9369731c8966fdeb40cf79a07daef6fb4eb759df Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 5 Dec 2020 12:04:47 -0600 Subject: [PATCH 2614/2656] Break load-time dependency on libcocotb and load libpython at runtime libcocotb A.K.A gpi_embed calls Python functions. To support Windows and some security-hardened Linuxes, these functions must be resolved before libcocotb is loaded. However, libpython is attempted to be loaded *in* libcocotb. This works on most Linuxes, but not all, and definitely not Windows. So instead we load libpython as a linked dependency. This is less than ideal since some simulators monkey with LD_LIBRARY_PATH and the wrong libpython could be loaded. Setting the RPATH is an effective work-around, but makes the binaries not relocatable. The best solution is to load libpython at runtime using the absolute path to the libpython associated with the cocotb install. To prevent libpython being loaded as a dependency, we must break the link dependency between the GPI and libcocotb. To accomplish that we create a small stub library to fill in for the "embed" library. This stub library runtime-loads libpython using an absolute path to the library. The stub library then runtime-loads the "implementation" embed library and proxies calls to it. This forces the correct libpython to be loaded, and all dependent libraries to load strictly after that. Co-authored-by: Tim Pambor --- cocotb/share/lib/embed/embed.cpp | 160 +++++++++++++++++++++++++++ cocotb/share/lib/embed/gpi_embed.cpp | 42 ++++--- cocotb/share/makefiles/Makefile.inc | 23 ++++ cocotb_build_libs.py | 35 +++++- setup.py | 4 +- 5 files changed, 240 insertions(+), 24 deletions(-) create mode 100644 cocotb/share/lib/embed/embed.cpp diff --git a/cocotb/share/lib/embed/embed.cpp b/cocotb/share/lib/embed/embed.cpp new file mode 100644 index 00000000..87ed0f5f --- /dev/null +++ b/cocotb/share/lib/embed/embed.cpp @@ -0,0 +1,160 @@ +// Copyright cocotb contributors +// Licensed under the Revised BSD License, see LICENSE for details. +// SPDX-License-Identifier: BSD-3-Clause + +#include +#include // xstr, utils_dyn_open, utils_dyn_sym +#include // gpi_event_t +#include // getenv +#if ! defined(__linux__) && ! defined(__APPLE__) +#include // Win32 API for loading the embed impl library +#include // string +#endif + +#ifndef PYTHON_LIB +#error "Name of Python library required" +#else +#define PYTHON_LIB_STR xstr(PYTHON_LIB) +#endif + +#ifndef EMBED_IMPL_LIB +#error "Name of embed implementation library required" +#else +#define EMBED_IMPL_LIB_STR xstr(EMBED_IMPL_LIB) +#endif + + +static void (*_embed_init_python)(); +static void (*_embed_sim_cleanup)(); +static int (*_embed_sim_init)(int argc, char const * const * argv); +static void (*_embed_sim_event)(gpi_event_t level, const char *msg); + +static bool init_failed = false; + + +#if ! defined(__linux__) && ! defined(__APPLE__) +HANDLE act_ctx = NULL; + +BOOL WINAPI DllMain(HINSTANCE, DWORD fdwReason, LPVOID) +{ + + if (fdwReason == DLL_PROCESS_ATTACH) { + // Save current activation context. + GetCurrentActCtx(&act_ctx); + } else if (fdwReason == DLL_PROCESS_DETACH) { + if (act_ctx) { + ReleaseActCtx(act_ctx); + } + } + + return TRUE; +} +#endif + + +extern "C" void embed_init_python(void) +{ + // preload python library + char const * libpython_path = getenv("LIBPYTHON_LOC"); + if (!libpython_path) { + // default to libpythonX.X.so + libpython_path = PYTHON_LIB_STR; + } + auto loaded = utils_dyn_open(libpython_path); + if (!loaded) { + // LCOV_EXCL_START + init_failed = true; + return; + // LCOV_EXCL_STOP + } + +#if ! defined(__linux__) && ! defined(__APPLE__) + if (!act_ctx) { + // LCOV_EXCL_START + init_failed = true; + return; + // LCOV_EXCL_STOP + } + + ULONG_PTR Cookie; + if(!ActivateActCtx(act_ctx, &Cookie)) { + // LCOV_EXCL_START + init_failed = true; + return; + // LCOV_EXCL_STOP + } +#endif + + // load embed implementation library and functions + void * embed_impl_lib_handle; + if (!(embed_impl_lib_handle = utils_dyn_open(EMBED_IMPL_LIB_STR))) { + // LCOV_EXCL_START + init_failed = true; + return; + // LCOV_EXCL_STOP + } + if (!(_embed_init_python = reinterpret_cast(utils_dyn_sym(embed_impl_lib_handle, "_embed_init_python")))) { + // LCOV_EXCL_START + init_failed = true; + return; + // LCOV_EXCL_STOP + } + if (!(_embed_sim_cleanup = reinterpret_cast(utils_dyn_sym(embed_impl_lib_handle, "_embed_sim_cleanup")))) { + // LCOV_EXCL_START + init_failed = true; + return; + // LCOV_EXCL_STOP + } + if (!(_embed_sim_init = reinterpret_cast(utils_dyn_sym(embed_impl_lib_handle, "_embed_sim_init")))) { + // LCOV_EXCL_START + init_failed = true; + return; + // LCOV_EXCL_STOP + } + if (!(_embed_sim_event = reinterpret_cast(utils_dyn_sym(embed_impl_lib_handle, "_embed_sim_event")))) { + // LCOV_EXCL_START + init_failed = true; + return; + // LCOV_EXCL_STOP + } + +#if ! defined(__linux__) && ! defined(__APPLE__) + if(!DeactivateActCtx(0, Cookie)) { + // LCOV_EXCL_START + init_failed = true; + return; + // LCOV_EXCL_STOP + } +#endif + + // call to embed library impl + _embed_init_python(); +} + + +extern "C" void embed_sim_cleanup(void) +{ + if (!init_failed) { + _embed_sim_cleanup(); + } +} + + +extern "C" int embed_sim_init(int argc, char const * const * argv) +{ + if (init_failed) { + // LCOV_EXCL_START + return -1; + // LCOV_EXCL_STOP + } else { + return _embed_sim_init(argc, argv); + } +} + + +extern "C" void embed_sim_event(gpi_event_t level, const char *msg) +{ + if (!init_failed) { + _embed_sim_event(level, msg); + } +} diff --git a/cocotb/share/lib/embed/gpi_embed.cpp b/cocotb/share/lib/embed/gpi_embed.cpp index 6447179c..ab6af0a3 100644 --- a/cocotb/share/lib/embed/gpi_embed.cpp +++ b/cocotb/share/lib/embed/gpi_embed.cpp @@ -36,6 +36,7 @@ #include // gpi_event_t #include "locale.h" #include +#include #if defined(_WIN32) #include @@ -113,22 +114,11 @@ static void set_program_name_in_venv(void) * Stores the thread state for cocotb in static variable gtstate */ -extern "C" void embed_init_python(void) +extern "C" COCOTB_EXPORT void _embed_init_python(void) { -#ifndef PYTHON_SO_LIB -#error "Python version needs passing in with -DPYTHON_SO_LIB=libpython.so" -#else -#define PY_SO_LIB xstr(PYTHON_SO_LIB) -#endif - assert(!gtstate); // this function should not be called twice - void * lib_handle = utils_dyn_open(PY_SO_LIB); - if (!lib_handle) { - LOG_ERROR("Failed to find Python shared library"); - } - to_python(); set_program_name_in_venv(); Py_Initialize(); /* Initialize the interpreter */ @@ -173,7 +163,7 @@ extern "C" void embed_init_python(void) * * Cleans up reference counts for Python objects and calls Py_Finalize function. */ -extern "C" void embed_sim_cleanup(void) +extern "C" COCOTB_EXPORT void _embed_sim_cleanup(void) { // If initialization fails, this may be called twice: // Before the initial callback returns and in the final callback. @@ -208,16 +198,18 @@ static int get_module_ref(const char *modname, PyObject **mod) PyObject *pModule = PyImport_ImportModule(modname); if (pModule == NULL) { + // LCOV_EXCL_START PyErr_Print(); LOG_ERROR("Failed to load Python module \"%s\"", modname); return -1; + // LCOV_EXCL_STOP } *mod = pModule; return 0; } -extern "C" int embed_sim_init(int argc, char const * const * argv) +extern "C" COCOTB_EXPORT int _embed_sim_init(int argc, char const * const * argv) { int i; @@ -240,64 +232,80 @@ extern "C" int embed_sim_init(int argc, char const * const * argv) to_python(); if (get_module_ref("cocotb", &cocotb_module)) { + // LCOV_EXCL_START goto cleanup; + // LCOV_EXCL_STOP } LOG_INFO("Python interpreter initialized and cocotb loaded!"); if (get_module_ref("cocotb.log", &cocotb_log_module)) { + // LCOV_EXCL_START goto cleanup; + // LCOV_EXCL_STOP } // Obtain the function to use when logging from C code log_func = PyObject_GetAttrString(cocotb_log_module, "_log_from_c"); // New reference if (log_func == NULL) { + // LCOV_EXCL_START PyErr_Print(); LOG_ERROR("Failed to get the _log_from_c function"); goto cleanup; + // LCOV_EXCL_STOP } // Obtain the function to check whether to call log function filter_func = PyObject_GetAttrString(cocotb_log_module, "_filter_from_c"); // New reference if (filter_func == NULL) { + // LCOV_EXCL_START Py_DECREF(log_func); PyErr_Print(); LOG_ERROR("Failed to get the _filter_from_c method"); goto cleanup; + // LCOV_EXCL_STOP } py_gpi_logger_initialize(log_func, filter_func); // Note: This function steals references to log_func and filter_func. pEventFn = PyObject_GetAttrString(cocotb_module, "_sim_event"); // New reference if (pEventFn == NULL) { + // LCOV_EXCL_START PyErr_Print(); LOG_ERROR("Failed to get the _sim_event method"); goto cleanup; + // LCOV_EXCL_STOP } cocotb_init = PyObject_GetAttrString(cocotb_module, "_initialise_testbench"); // New reference if (cocotb_init == NULL) { + // LCOV_EXCL_START PyErr_Print(); LOG_ERROR("Failed to get the _initialise_testbench method"); goto cleanup; + // LCOV_EXCL_STOP } // Build argv for cocotb module argv_list = PyList_New(argc); // New reference if (argv_list == NULL) { + // LCOV_EXCL_START PyErr_Print(); LOG_ERROR("Unable to create argv list"); goto cleanup; + // LCOV_EXCL_STOP } for (i = 0; i < argc; i++) { // Decode, embedding non-decodable bytes using PEP-383. This can only // fail with MemoryError or similar. PyObject *argv_item = PyUnicode_DecodeLocale(argv[i], "surrogateescape"); // New reference if (argv_item == NULL) { + // LCOV_EXCL_START PyErr_Print(); LOG_ERROR("Unable to convert command line argument %d to Unicode string.", i); Py_DECREF(argv_list); goto cleanup; + // LCOV_EXCL_STOP } PyList_SET_ITEM(argv_list, i, argv_item); // Note: This function steals the reference to argv_item } @@ -311,9 +319,11 @@ extern "C" int embed_sim_init(int argc, char const * const * argv) LOG_DEBUG("_initialise_testbench successful"); Py_DECREF(cocotb_retval); } else { + // LCOV_EXCL_START PyErr_Print(); LOG_ERROR("cocotb initialization failed - exiting"); goto cleanup; + // LCOV_EXCL_STOP } goto ok; @@ -330,9 +340,9 @@ extern "C" int embed_sim_init(int argc, char const * const * argv) return ret; } -extern "C" void embed_sim_event(gpi_event_t level, const char *msg) +extern "C" COCOTB_EXPORT void _embed_sim_event(gpi_event_t level, const char *msg) { - /* Indicate to the upper layer a sim event occurred */ + /* Indicate to the upper layer that a sim event occurred */ if (pEventFn) { PyGILState_STATE gstate; diff --git a/cocotb/share/makefiles/Makefile.inc b/cocotb/share/makefiles/Makefile.inc index 27ab7333..f3403716 100644 --- a/cocotb/share/makefiles/Makefile.inc +++ b/cocotb/share/makefiles/Makefile.inc @@ -122,6 +122,29 @@ endif endif +define find_libpython_errmsg = + + +find_libpython was not able to find a libpython in the current Python environment. Ensure +the Python development packages are installed. If they are installed and find_libpython +is not finding the path to libpython, file an upstream bug in find_libpython; then +manually override the LIBPYTHON_LOC make variable with the aboslute path to libpython.so +(or libpython.dll on Windows). + +endef + +# get the path to libpython and the return code from the script +# adapted from https://stackoverflow.com/a/24658961/6614127 +FIND_LIBPYTHON_RES := $(shell find_libpython; echo; echo $$?) +FIND_LIBPYTHON_RC := $(lastword $(FIND_LIBPYTHON_RES)) +LIBPYTHON_LOC := $(strip $(subst $(FIND_LIBPYTHON_RC)QQQQ,,$(FIND_LIBPYTHON_RES)QQQQ)) + +# complain if libpython isn't found, and export otherwise +ifneq ($(FIND_LIBPYTHON_RC),0) + $(error $(find_libpython_errmsg)) +endif +export LIBPYTHON_LOC + else $(warning Including Makefile.inc from a user makefile is a no-op and deprecated. Remove the Makefile.inc inclusion from your makefile, and only leave the Makefile.sim include.) endif # COCOTB_MAKEFILE_INC_INCLUDED diff --git a/cocotb_build_libs.py b/cocotb_build_libs.py index 1559ebe0..7e28f0dc 100755 --- a/cocotb_build_libs.py +++ b/cocotb_build_libs.py @@ -142,6 +142,9 @@ def run(self): name = os.path.split(fullname)[-1] filename = os.path.split(filename)[-1] libraries = {"lib" + lib for lib in ext.libraries}.intersection(ext_names) + # Add the runtime dependency on libcocotb to libembed dependencies + if name == "libembed": + libraries.add("libcocotb") create_rc_file(name, filename, libraries) super().run() @@ -369,6 +372,27 @@ def _get_common_lib_ext(include_dir, share_lib_dir): extra_compile_args=_extra_cxx_compile_args, ) + # + # libembed + # + libembed_sources = [ + os.path.join(share_lib_dir, "embed", "embed.cpp") + ] + if os.name == "nt": + libembed_sources += ["libembed.rc"] + libembed = Extension( + os.path.join("cocotb", "libs", "libembed"), + define_macros=[ + ("COCOTB_EMBED_EXPORTS", ""), + ("EMBED_IMPL_LIB", "libcocotb." + _get_lib_ext_name()), + ("PYTHON_LIB", _get_python_lib())] + _extra_defines, + include_dirs=[include_dir], + libraries=["gpilog", "cocotbutils"], + sources=libembed_sources, + extra_link_args=_extra_link_args(lib_name="libembed", rpaths=["$ORIGIN"]), + extra_compile_args=_extra_cxx_compile_args, + ) + # # libcocotb # @@ -379,12 +403,11 @@ def _get_common_lib_ext(include_dir, share_lib_dir): libcocotb_sources += ["libcocotb.rc"] libcocotb = Extension( os.path.join("cocotb", "libs", "libcocotb"), - define_macros=[("COCOTB_EMBED_EXPORTS", ""), ("PYTHON_SO_LIB", _get_python_lib())] + _extra_defines, + define_macros=_extra_defines, include_dirs=[include_dir], - libraries=[_get_python_lib_link(), "gpilog", "cocotbutils", "pygpilog"], - library_dirs=python_lib_dirs, + libraries=["gpilog", "cocotbutils", "pygpilog"], sources=libcocotb_sources, - extra_link_args=_extra_link_args(lib_name="libcocotb", rpaths=["$ORIGIN"] + python_lib_dirs), + extra_link_args=_extra_link_args(lib_name="libcocotb", rpaths=["$ORIGIN"]), extra_compile_args=_extra_cxx_compile_args, ) @@ -401,7 +424,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): os.path.join("cocotb", "libs", "libgpi"), define_macros=[("GPI_EXPORTS", ""), ("LIB_EXT", _get_lib_ext_name()), ("SINGLETON_HANDLES", "")] + _extra_defines, include_dirs=[include_dir], - libraries=["cocotbutils", "gpilog", "cocotb"], + libraries=["cocotbutils", "gpilog", "embed"], sources=libgpi_sources, extra_link_args=_extra_link_args(lib_name="libgpi", rpaths=["$ORIGIN"]), extra_compile_args=_extra_cxx_compile_args, @@ -429,7 +452,7 @@ def _get_common_lib_ext(include_dir, share_lib_dir): # The libraries in this list are compiled in order of their appearance. # If there is a linking dependency on one library to another, # the linked library must be built first. - return [libgpilog, libpygpilog, libcocotbutils, libcocotb, libgpi, libsim] + return [libgpilog, libpygpilog, libcocotbutils, libembed, libgpi, libcocotb, libsim] def _get_vpi_lib_ext( diff --git a/setup.py b/setup.py index ec46987c..a74092d9 100755 --- a/setup.py +++ b/setup.py @@ -96,14 +96,14 @@ def package_files(directory): author='Chris Higgs, Stuart Hodgson', maintainer='cocotb contributors', maintainer_email='cocotb@lists.librecores.org', - install_requires=[], + install_requires=['find_libpython'], python_requires='>=3.5', packages=find_packages(), package_data={ 'cocotb': ( package_files('cocotb/share/makefiles') + # noqa: W504 package_files('cocotb/share/include') + # noqa: W504 - package_files('cocotb/share/def') + + package_files('cocotb/share/def') + # noqa: W504 package_files('cocotb/share/lib/verilator') ) }, From 35972d9d693a4d0ac97078353c2636e56b330f52 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Mon, 7 Dec 2020 20:34:47 -0600 Subject: [PATCH 2615/2656] Add cocotb-config --libpython option which returns libpython path --- cocotb/config.py | 7 +++++++ cocotb/share/makefiles/Makefile.inc | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/cocotb/config.py b/cocotb/config.py index 18ccee8a..96e4d4f9 100755 --- a/cocotb/config.py +++ b/cocotb/config.py @@ -43,6 +43,7 @@ import sys import textwrap import cocotb +import find_libpython __all__ = ["share_dir", "makefiles_dir"] @@ -146,6 +147,12 @@ def get_parser(): action=PrintAction, text=version, ) + parser.add_argument( + "--libpython", + help="prints the absolute path to the libpython associated with the current Python installation", + action=PrintAction, + text=find_libpython.find_libpython(), + ) return parser diff --git a/cocotb/share/makefiles/Makefile.inc b/cocotb/share/makefiles/Makefile.inc index f3403716..14842e87 100644 --- a/cocotb/share/makefiles/Makefile.inc +++ b/cocotb/share/makefiles/Makefile.inc @@ -135,7 +135,7 @@ endef # get the path to libpython and the return code from the script # adapted from https://stackoverflow.com/a/24658961/6614127 -FIND_LIBPYTHON_RES := $(shell find_libpython; echo; echo $$?) +FIND_LIBPYTHON_RES := $(shell cocotb-config --libpython; echo $$?) FIND_LIBPYTHON_RC := $(lastword $(FIND_LIBPYTHON_RES)) LIBPYTHON_LOC := $(strip $(subst $(FIND_LIBPYTHON_RC)QQQQ,,$(FIND_LIBPYTHON_RES)QQQQ)) From 218fec9f1c65fde96e81254c69d34d5867775043 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Thu, 10 Dec 2020 08:23:59 -0600 Subject: [PATCH 2616/2656] Vendor in find_libpython --- cocotb/config.py | 2 +- cocotb/vendor/__init__.py | 0 cocotb/vendor/find_libpython/__init__.py | 383 +++++++++++++++++++++++ setup.cfg | 1 + setup.py | 2 +- 5 files changed, 386 insertions(+), 2 deletions(-) create mode 100644 cocotb/vendor/__init__.py create mode 100644 cocotb/vendor/find_libpython/__init__.py diff --git a/cocotb/config.py b/cocotb/config.py index 96e4d4f9..d2f6d968 100755 --- a/cocotb/config.py +++ b/cocotb/config.py @@ -43,7 +43,7 @@ import sys import textwrap import cocotb -import find_libpython +import cocotb.vendor.find_libpython as find_libpython __all__ = ["share_dir", "makefiles_dir"] diff --git a/cocotb/vendor/__init__.py b/cocotb/vendor/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/cocotb/vendor/find_libpython/__init__.py b/cocotb/vendor/find_libpython/__init__.py new file mode 100644 index 00000000..1a5d4b6e --- /dev/null +++ b/cocotb/vendor/find_libpython/__init__.py @@ -0,0 +1,383 @@ +""" +Locate libpython associated with this Python executable. +""" + +# License +# +# Copyright 2018, Takafumi Arakaki +# +# 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. + +from logging import getLogger +import ctypes.util +import functools +import os +import sys +import sysconfig + +logger = getLogger("find_libpython") + +is_windows = os.name == "nt" +is_apple = sys.platform == "darwin" + +SHLIB_SUFFIX = sysconfig.get_config_var("SHLIB_SUFFIX") +if SHLIB_SUFFIX is None: + if is_windows: + SHLIB_SUFFIX = ".dll" + else: + SHLIB_SUFFIX = ".so" +if is_apple: + # sysconfig.get_config_var("SHLIB_SUFFIX") can be ".so" in macOS. + # Let's not use the value from sysconfig. + SHLIB_SUFFIX = ".dylib" + + +def linked_libpython(): + """ + Find the linked libpython using dladdr (in *nix). + + Calling this in Windows always return `None` at the moment. + + Returns + ------- + path : str or None + A path to linked libpython. Return `None` if statically linked. + """ + if is_windows: + return None + return _linked_libpython_unix() + + +class Dl_info(ctypes.Structure): + _fields_ = [ + ("dli_fname", ctypes.c_char_p), + ("dli_fbase", ctypes.c_void_p), + ("dli_sname", ctypes.c_char_p), + ("dli_saddr", ctypes.c_void_p), + ] + + +def _linked_libpython_unix(): + libdl = ctypes.CDLL(ctypes.util.find_library("dl")) + libdl.dladdr.argtypes = [ctypes.c_void_p, ctypes.POINTER(Dl_info)] + libdl.dladdr.restype = ctypes.c_int + + dlinfo = Dl_info() + retcode = libdl.dladdr( + ctypes.cast(ctypes.pythonapi.Py_GetVersion, ctypes.c_void_p), + ctypes.pointer(dlinfo), + ) + if retcode == 0: # means error + return None + path = os.path.realpath(dlinfo.dli_fname.decode()) + if path == os.path.realpath(sys.executable): + return None + return path + + +def library_name(name, suffix=SHLIB_SUFFIX, is_windows=is_windows): + """ + Convert a file basename `name` to a library name (no "lib" and ".so" etc.) + + >>> library_name("libpython3.7m.so") # doctest: +SKIP + 'python3.7m' + >>> library_name("libpython3.7m.so", suffix=".so", is_windows=False) + 'python3.7m' + >>> library_name("libpython3.7m.dylib", suffix=".dylib", is_windows=False) + 'python3.7m' + >>> library_name("python37.dll", suffix=".dll", is_windows=True) + 'python37' + """ + if not is_windows and name.startswith("lib"): + name = name[len("lib") :] + if suffix and name.endswith(suffix): + name = name[: -len(suffix)] + return name + + +def append_truthy(list, item): + if item: + list.append(item) + + +def uniquifying(items): + """ + Yield items while excluding the duplicates and preserving the order. + + >>> list(uniquifying([1, 2, 1, 2, 3])) + [1, 2, 3] + """ + seen = set() + for x in items: + if x not in seen: + yield x + seen.add(x) + + +def uniquified(func): + """ Wrap iterator returned from `func` by `uniquifying`. """ + + @functools.wraps(func) + def wrapper(*args, **kwds): + return uniquifying(func(*args, **kwds)) + + return wrapper + + +@uniquified +def candidate_names(suffix=SHLIB_SUFFIX): + """ + Iterate over candidate file names of libpython. + + Yields + ------ + name : str + Candidate name libpython. + """ + LDLIBRARY = sysconfig.get_config_var("LDLIBRARY") + if LDLIBRARY: + yield LDLIBRARY + + LIBRARY = sysconfig.get_config_var("LIBRARY") + if LIBRARY: + yield os.path.splitext(LIBRARY)[0] + suffix + + dlprefix = "" if is_windows else "lib" + sysdata = dict( + v=sys.version_info, + # VERSION is X.Y in Linux/macOS and XY in Windows: + VERSION=( + sysconfig.get_config_var("VERSION") + or "{v.major}.{v.minor}".format(v=sys.version_info) + ), + ABIFLAGS=( + sysconfig.get_config_var("ABIFLAGS") + or sysconfig.get_config_var("abiflags") + or "" + ), + ) + + for stem in [ + "python{VERSION}{ABIFLAGS}".format(**sysdata), + "python{VERSION}".format(**sysdata), + "python{v.major}".format(**sysdata), + "python", + ]: + yield dlprefix + stem + suffix + + +@uniquified +def candidate_paths(suffix=SHLIB_SUFFIX): + """ + Iterate over candidate paths of libpython. + + Yields + ------ + path : str or None + Candidate path to libpython. The path may not be a fullpath + and may not exist. + """ + + yield linked_libpython() + + # List candidates for directories in which libpython may exist + lib_dirs = [] + append_truthy(lib_dirs, sysconfig.get_config_var("LIBPL")) + append_truthy(lib_dirs, sysconfig.get_config_var("srcdir")) + append_truthy(lib_dirs, sysconfig.get_config_var("LIBDIR")) + + # LIBPL seems to be the right config_var to use. It is the one + # used in python-config when shared library is not enabled: + # https://github.com/python/cpython/blob/v3.7.0/Misc/python-config.in#L55-L57 + # + # But we try other places just in case. + + if is_windows: + lib_dirs.append(os.path.join(os.path.dirname(sys.executable))) + else: + lib_dirs.append( + os.path.join(os.path.dirname(os.path.dirname(sys.executable)), "lib") + ) + + # For macOS: + append_truthy(lib_dirs, sysconfig.get_config_var("PYTHONFRAMEWORKPREFIX")) + + lib_dirs.append(sys.exec_prefix) + lib_dirs.append(os.path.join(sys.exec_prefix, "lib")) + + lib_basenames = list(candidate_names(suffix=suffix)) + + for directory in lib_dirs: + for basename in lib_basenames: + yield os.path.join(directory, basename) + + # In macOS and Windows, ctypes.util.find_library returns a full path: + for basename in lib_basenames: + yield ctypes.util.find_library(library_name(basename)) + + +# Possibly useful links: +# * https://packages.ubuntu.com/bionic/amd64/libpython3.6/filelist +# * https://github.com/Valloric/ycmd/issues/518 +# * https://github.com/Valloric/ycmd/pull/519 + + +def normalize_path(path, suffix=SHLIB_SUFFIX, is_apple=is_apple): + """ + Normalize shared library `path` to a real path. + + If `path` is not a full path, `None` is returned. If `path` does + not exists, append `SHLIB_SUFFIX` and check if it exists. + Finally, the path is canonicalized by following the symlinks. + + Parameters + ---------- + path : str ot None + A candidate path to a shared library. + """ + if not path: + return None + if not os.path.isabs(path): + return None + if os.path.exists(path): + return os.path.realpath(path) + if os.path.exists(path + suffix): + return os.path.realpath(path + suffix) + if is_apple: + return normalize_path(_remove_suffix_apple(path), suffix=".so", is_apple=False) + return None + + +def _remove_suffix_apple(path): + """ + Strip off .so or .dylib. + + >>> _remove_suffix_apple("libpython.so") + 'libpython' + >>> _remove_suffix_apple("libpython.dylib") + 'libpython' + >>> _remove_suffix_apple("libpython3.7") + 'libpython3.7' + """ + if path.endswith(".dylib"): + return path[: -len(".dylib")] + if path.endswith(".so"): + return path[: -len(".so")] + return path + + +@uniquified +def finding_libpython(): + """ + Iterate over existing libpython paths. + + The first item is likely to be the best one. + + Yields + ------ + path : str + Existing path to a libpython. + """ + logger.debug("is_windows = %s", is_windows) + logger.debug("is_apple = %s", is_apple) + for path in candidate_paths(): + logger.debug("Candidate: %s", path) + normalized = normalize_path(path) + if normalized: + logger.debug("Found: %s", normalized) + yield normalized + else: + logger.debug("Not found.") + + +def find_libpython(): + """ + Return a path (`str`) to libpython or `None` if not found. + + Parameters + ---------- + path : str or None + Existing path to the (supposedly) correct libpython. + """ + for path in finding_libpython(): + return os.path.realpath(path) + + +def print_all(items): + for x in items: + print(x) + + +def cli_find_libpython(cli_op, verbose): + import logging + + # Importing `logging` module here so that using `logging.debug` + # instead of `logger.debug` outside of this function becomes an + # error. + + if verbose: + logging.basicConfig(format="%(levelname)s %(message)s", level=logging.DEBUG) + + if cli_op == "list-all": + print_all(finding_libpython()) + elif cli_op == "candidate-names": + print_all(candidate_names()) + elif cli_op == "candidate-paths": + print_all(p for p in candidate_paths() if p and os.path.isabs(p)) + else: + path = find_libpython() + if path is None: + return 1 + print(path, end="") + + +def main(args=None): + import argparse + + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--verbose", "-v", action="store_true", help="Print debugging information." + ) + + group = parser.add_mutually_exclusive_group() + group.add_argument( + "--list-all", + action="store_const", + dest="cli_op", + const="list-all", + help="Print list of all paths found.", + ) + group.add_argument( + "--candidate-names", + action="store_const", + dest="cli_op", + const="candidate-names", + help="Print list of candidate names of libpython.", + ) + group.add_argument( + "--candidate-paths", + action="store_const", + dest="cli_op", + const="candidate-paths", + help="Print list of candidate paths of libpython.", + ) + + ns = parser.parse_args(args) + parser.exit(cli_find_libpython(**vars(ns))) diff --git a/setup.cfg b/setup.cfg index aba87846..b1331df9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,6 +6,7 @@ exclude = venv .tox examples/endian_swapper/cosim + vendor ignore = E202 # whitespace before ')' diff --git a/setup.py b/setup.py index a74092d9..dfc565f9 100755 --- a/setup.py +++ b/setup.py @@ -96,7 +96,7 @@ def package_files(directory): author='Chris Higgs, Stuart Hodgson', maintainer='cocotb contributors', maintainer_email='cocotb@lists.librecores.org', - install_requires=['find_libpython'], + install_requires=[], python_requires='>=3.5', packages=find_packages(), package_data={ From 331b879e39c587e94837f73eb05f73c04ec20c6e Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 30 Dec 2020 15:24:32 -0600 Subject: [PATCH 2617/2656] Auto-close issues labeled "status:close?" --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 191a03b7..7dd1b4b5 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -17,6 +17,6 @@ jobs: If it has not been resolved, you may need to provide more information. If no more activity on this issue occurs in 7 days, it will be closed. stale-issue-label: "status:stale" - only-labels: "type:question" + only-labels: "type:question,status:close?" exempt-issue-labels: "type:bug,type:feature" operations-per-run: 5 From 2e392918c84e6222922e15ef59d28f5abdc3cdb7 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 28 Dec 2020 22:49:35 +0100 Subject: [PATCH 2618/2656] Use '<=' in places where 'dut.sig = val' was used --- documentation/source/quickstart.rst | 4 ++-- .../endian_swapper/hal_tests/test_endian_swapper_hal.py | 6 +++--- examples/mean/tests/test_mean.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index 5e073d57..1e9f5fb1 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -101,9 +101,9 @@ following: dut._log.info("Running test!") for cycle in range(10): - dut.clk = 0 + dut.clk <= 0 await Timer(1, units='ns') - dut.clk = 1 + dut.clk <= 1 await Timer(1, units='ns') dut._log.info("Running test!") diff --git a/examples/endian_swapper/hal_tests/test_endian_swapper_hal.py b/examples/endian_swapper/hal_tests/test_endian_swapper_hal.py index b1e158c9..57c31ba4 100644 --- a/examples/endian_swapper/hal_tests/test_endian_swapper_hal.py +++ b/examples/endian_swapper/hal_tests/test_endian_swapper_hal.py @@ -38,11 +38,11 @@ async def reset(dut, duration=10): dut._log.debug("Resetting DUT") - dut.reset_n = 0 - dut.stream_in_valid = 0 + dut.reset_n <= 0 + dut.stream_in_valid <= 0 await Timer(duration, units='ns') await RisingEdge(dut.clk) - dut.reset_n = 1 + dut.reset_n <= 1 dut._log.debug("Out of reset") diff --git a/examples/mean/tests/test_mean.py b/examples/mean/tests/test_mean.py index 7f2af7e3..7c810a31 100644 --- a/examples/mean/tests/test_mean.py +++ b/examples/mean/tests/test_mean.py @@ -105,7 +105,7 @@ async def mean_randomised_test(dut): dut.rst <= 1 for i in range(BUS_WIDTH): - dut.i_data[i] = 0 + dut.i_data[i] <= 0 dut.i_valid <= 0 await RisingEdge(dut.clk) await RisingEdge(dut.clk) @@ -115,7 +115,7 @@ async def mean_randomised_test(dut): nums = [] for i in range(BUS_WIDTH): x = random.randint(0, 2**DATA_WIDTH - 1) - dut.i_data[i] = x + dut.i_data[i] <= x nums.append(x) dut.i_valid <= 1 From 0e0767779dee4d5ca77e9c84103b77063f2cd902 Mon Sep 17 00:00:00 2001 From: Tim Pambor Date: Sun, 3 Jan 2021 13:54:46 +0100 Subject: [PATCH 2619/2656] Move SxS dependency on libcocotb to runtime --- cocotb/share/lib/embed/embed.cpp | 38 ++++++++++++++++++--------- cocotb_build_libs.py | 44 +++++++++++++++++++++++--------- 2 files changed, 58 insertions(+), 24 deletions(-) diff --git a/cocotb/share/lib/embed/embed.cpp b/cocotb/share/lib/embed/embed.cpp index 87ed0f5f..e982f7f8 100644 --- a/cocotb/share/lib/embed/embed.cpp +++ b/cocotb/share/lib/embed/embed.cpp @@ -33,18 +33,22 @@ static bool init_failed = false; #if ! defined(__linux__) && ! defined(__APPLE__) -HANDLE act_ctx = NULL; - -BOOL WINAPI DllMain(HINSTANCE, DWORD fdwReason, LPVOID) +static ACTCTX act_ctx = { + /* cbSize */ sizeof(ACTCTX), + /* dwFlags */ ACTCTX_FLAG_HMODULE_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID, + /* lpSource */ NULL, + /* wProcessorArchitecture */ 0, + /* wLangId */ 0, + /* lpAssemblyDirectory */ NULL, + /* lpResourceName */ MAKEINTRESOURCE(1000), + /* lpApplicationName */ NULL, + /* hModule */ 0 +}; + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID) { - if (fdwReason == DLL_PROCESS_ATTACH) { - // Save current activation context. - GetCurrentActCtx(&act_ctx); - } else if (fdwReason == DLL_PROCESS_DETACH) { - if (act_ctx) { - ReleaseActCtx(act_ctx); - } + act_ctx.hModule = hinstDLL; } return TRUE; @@ -69,7 +73,15 @@ extern "C" void embed_init_python(void) } #if ! defined(__linux__) && ! defined(__APPLE__) - if (!act_ctx) { + if (!act_ctx.hModule) { + // LCOV_EXCL_START + init_failed = true; + return; + // LCOV_EXCL_STOP + } + + HANDLE hact_ctx = CreateActCtx(&act_ctx); + if(hact_ctx == INVALID_HANDLE_VALUE) { // LCOV_EXCL_START init_failed = true; return; @@ -77,7 +89,7 @@ extern "C" void embed_init_python(void) } ULONG_PTR Cookie; - if(!ActivateActCtx(act_ctx, &Cookie)) { + if(!ActivateActCtx(hact_ctx, &Cookie)) { // LCOV_EXCL_START init_failed = true; return; @@ -125,6 +137,8 @@ extern "C" void embed_init_python(void) return; // LCOV_EXCL_STOP } + + ReleaseActCtx(hact_ctx); #endif // call to embed library impl diff --git a/cocotb_build_libs.py b/cocotb_build_libs.py index 7e28f0dc..64a34081 100755 --- a/cocotb_build_libs.py +++ b/cocotb_build_libs.py @@ -21,7 +21,7 @@ cocotb_share_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "cocotb", "share")) -def create_sxs_assembly_manifest(name: str, filename: str, libraries: List[str]) -> str: +def create_sxs_assembly_manifest(name: str, filename: str, libraries: List[str], dependency_only=False) -> str: """ Create side-by-side (sxs) assembly manifest @@ -48,14 +48,22 @@ def create_sxs_assembly_manifest(name: str, filename: str, libraries: List[str]) ''') % (lib, architecture)) - manifest_body = textwrap.dedent('''\ - - - - - %s - - ''') % (name, architecture, filename, textwrap.indent("".join(dependencies), ' ').strip()) + if not dependency_only: + manifest_body = textwrap.dedent('''\ + + + + + %s + + ''') % (name, architecture, filename, textwrap.indent("".join(dependencies), ' ').strip()) + else: + manifest_body = textwrap.dedent('''\ + + + %s + + ''') % (textwrap.indent("".join(dependencies), ' ').strip()) return manifest_body @@ -111,6 +119,21 @@ def create_rc_file(name, filename, libraries): END ''') % manifest + # Add the runtime dependency on libcocotb to libembed dependencies + if name == "libembed": + manifest = create_sxs_assembly_manifest(name, filename, ["libcocotb"], dependency_only=True) + + # Escape double quotes and put every line between double quotes for embedding into rc file + manifest = manifest.replace('"', '""') + manifest = '\n'.join(['"%s\\r\\n"' % x for x in manifest.splitlines()]) + + rc_body += textwrap.dedent('''\ + 1000 RT_MANIFEST + BEGIN + %s + END + ''') % manifest + with open(name + ".rc", "w", encoding='utf-8') as f: f.write(rc_body) @@ -142,9 +165,6 @@ def run(self): name = os.path.split(fullname)[-1] filename = os.path.split(filename)[-1] libraries = {"lib" + lib for lib in ext.libraries}.intersection(ext_names) - # Add the runtime dependency on libcocotb to libembed dependencies - if name == "libembed": - libraries.add("libcocotb") create_rc_file(name, filename, libraries) super().run() From e165373ef73fad74271f5ecbf90b20709e7a97f8 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 4 Jan 2021 21:12:59 +0100 Subject: [PATCH 2620/2656] Increase log level of unhandled VPI types to WARN --- cocotb/share/lib/vpi/VpiImpl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocotb/share/lib/vpi/VpiImpl.cpp b/cocotb/share/lib/vpi/VpiImpl.cpp index a5a9de7c..ad9faaba 100644 --- a/cocotb/share/lib/vpi/VpiImpl.cpp +++ b/cocotb/share/lib/vpi/VpiImpl.cpp @@ -255,9 +255,9 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, const char *type_name = vpi_get_str(vpiType, new_hdl); std::string unknown = "vpiUnknown"; if (type_name && (unknown != type_name)) { - LOG_DEBUG("VPI: Not able to map type %s(%d) to object.", type_name, type); + LOG_WARN("VPI: Not able to map type %s(%d) to object.", type_name, type); } else { - LOG_DEBUG("VPI: Simulator does not know this type (%d) via VPI", type); + LOG_WARN("VPI: Simulator does not know this type (%d) via VPI", type); } return NULL; } From 6d303742eb45a9b893ab1456e88a33fe2f908bfa Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 4 Jan 2021 21:54:16 +0100 Subject: [PATCH 2621/2656] Fix reference to start_soon() in newsfragment --- documentation/source/newsfragments/2023.feature.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/newsfragments/2023.feature.rst b/documentation/source/newsfragments/2023.feature.rst index 935ff839..2270cca5 100644 --- a/documentation/source/newsfragments/2023.feature.rst +++ b/documentation/source/newsfragments/2023.feature.rst @@ -1,2 +1,2 @@ -Added :func:`cocotb.scheduler.start_soon` which schedules a coroutine to start *after* the current coroutine yields control. +Added :meth:`cocotb.scheduler.start_soon ` which schedules a coroutine to start *after* the current coroutine yields control. This behavior is distinct from :func:`cocotb.fork` which schedules the given coroutine immediately. From d235ece58bd161d671c3c26f4e6c54912f3b80a5 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Mon, 4 Jan 2021 13:15:14 -0800 Subject: [PATCH 2622/2656] Fix newsfragment for #2091 --- documentation/source/newsfragments/2091.change.rst | 1 + documentation/source/newsfragments/2091.changes.rst | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 documentation/source/newsfragments/2091.change.rst delete mode 100644 documentation/source/newsfragments/2091.changes.rst diff --git a/documentation/source/newsfragments/2091.change.rst b/documentation/source/newsfragments/2091.change.rst new file mode 100644 index 00000000..be5f3d38 --- /dev/null +++ b/documentation/source/newsfragments/2091.change.rst @@ -0,0 +1 @@ +The package build process is now fully :pep:`517` compliant. diff --git a/documentation/source/newsfragments/2091.changes.rst b/documentation/source/newsfragments/2091.changes.rst deleted file mode 100644 index c9b1567c..00000000 --- a/documentation/source/newsfragments/2091.changes.rst +++ /dev/null @@ -1 +0,0 @@ -The package build process is now fully PEP517 compliant. From 1be51548fdbd7e7655d8dfdbc5ae1589ff432194 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Mon, 6 Jan 2020 15:13:16 -0800 Subject: [PATCH 2623/2656] Convert resolve() to str.translate(). New translation table class _ResolveTable provides resolve functionality and improves performance. This preserves current behavior of COCOTB_RESOLVE_X = RANDOM, where all instances of the character are translated to the same value. --- cocotb/binary.py | 77 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 23 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index cba0eb00..a6761302 100755 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -29,27 +29,61 @@ import os import random +import re import warnings + +_RESOLVE_TO_0 = "-lL" +_RESOLVE_TO_1 = "hH" +_RESOLVE_TO_CHOICE = "xXzZuUwW" resolve_x_to = os.getenv('COCOTB_RESOLVE_X', "VALUE_ERROR") -def resolve(string): - for char in BinaryValue._resolve_to_0: - string = string.replace(char, "0") - for char in BinaryValue._resolve_to_1: - string = string.replace(char, "1") - for char in BinaryValue._resolve_to_error: - if resolve_x_to == "VALUE_ERROR" and char in string: - raise ValueError("Unable to resolve to binary >%s<" % string) +class _ResolveTable(dict): + """Translation table class for resolving binary strings. + + For use with :func:`str.translate()`, which indexes into table with Unicode ordinals. + """ + def __init__(self): + self.update({ord("0"): ord("0"), ord("1"): ord("1")}) + self.update({ord(k): ord("0") for k in _RESOLVE_TO_0}) + self.update({ord(k): ord("1") for k in _RESOLVE_TO_1}) + + # Do not resolve if resolve_x_to is not set to one of the supported values + def no_resolve(key): + return key + self.resolve_x = no_resolve + + if resolve_x_to == "VALUE_ERROR": + def resolve_error(key): + raise ValueError("Unresolvable bit in binary string: '{}'".format(chr(key))) + self.resolve_x = resolve_error elif resolve_x_to == "ZEROS": - string = string.replace(char, "0") + self.update({ord(k): ord("0") for k in _RESOLVE_TO_CHOICE}) elif resolve_x_to == "ONES": - string = string.replace(char, "1") + self.update({ord(k): ord("1") for k in _RESOLVE_TO_CHOICE}) elif resolve_x_to == "RANDOM": - bits = "{0:b}".format(random.getrandbits(1)) - string = string.replace(char, bits) - return string + def resolve_random(key): + # convert to correct Unicode ordinal: + # ord('0') = 48 + # ord('1') = 49 + return random.getrandbits(1) + 48 + self.resolve_x = resolve_random + + self._resolve_to_choice = {ord(c) for c in _RESOLVE_TO_CHOICE} + + def __missing__(self, key): + if key in self._resolve_to_choice: + return self.resolve_x(key) + else: + return key + + +_resolve_table = _ResolveTable() + + +def resolve(string): + return string.translate(_resolve_table) def _clog2(val): @@ -89,10 +123,7 @@ class BinaryValue: b'*' """ - _resolve_to_0 = "-lL" # noqa - _resolve_to_1 = "hH" # noqa - _resolve_to_error = "xXzZuUwW" # Resolve to a ValueError() since these usually mean something is wrong - _permitted_chars = _resolve_to_0 +_resolve_to_1 + _resolve_to_error + "01" # noqa + _permitted_chars = _RESOLVE_TO_0 +_RESOLVE_TO_1 + _RESOLVE_TO_CHOICE + "01" # noqa def __init__(self, value=None, n_bits=None, bigEndian=True, binaryRepresentation=BinaryRepresentation.UNSIGNED, @@ -197,10 +228,10 @@ def _convert_to_twos_comp(self, x): return binstr def _convert_from_unsigned(self, x): - return int(resolve(x), 2) + return int(x.translate(_resolve_table), 2) def _convert_from_signed_mag(self, x): - rv = int(resolve(self._str[1:]), 2) + rv = int(self._str[1:].translate(_resolve_table), 2) if self._str[0] == '1': rv = rv * -1 return rv @@ -213,7 +244,7 @@ def _convert_from_twos_comp(self, x): if x[0] == '1': rv = rv * -1 else: - rv = int(resolve(x), 2) + rv = int(x.translate(_resolve_table), 2) return rv def _invert(self, x): @@ -312,7 +343,7 @@ def value(self, val): @property def signed_integer(self): """The signed integer representation of the underlying vector.""" - ival = int(resolve(self._str), 2) + ival = int(self._str.translate(_resolve_table), 2) bits = len(self._str) signbit = (1 << (bits - 1)) if (ival & signbit) == 0: @@ -337,7 +368,7 @@ def is_resolvable(self) -> bool: This is similar to the SystemVerilog Assertion ``$isunknown`` system function or the VHDL function ``is_x`` (with an inverted meaning). """ - return not any(char in self._str for char in BinaryValue._resolve_to_error) + return not any(char in self._str for char in _RESOLVE_TO_CHOICE) @property def buff(self) -> bytes: @@ -351,7 +382,7 @@ def buff(self) -> bytes: Note that for older versions used with Python 2 these types were indistinguishable. """ - bits = resolve(self._str) + bits = self._str.translate(_resolve_table) if len(bits) % 8: bits = "0" * (8 - len(bits) % 8) + bits From 50a43044707204005353ebb8b336907bb7af3a18 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Mon, 6 Jan 2020 15:14:49 -0800 Subject: [PATCH 2624/2656] Change BinaryValue conversions to bound methods. Move dictionaries into the class body and create bound methods in __init__(). This removes the overhead of dictionary creation for each new BinaryValue object. A side effect of this change is that once the BinaryValue object is created, the conversion method is set and cannot be changed. To change the conversion method, a new BinaryValue object must be created. --- cocotb/binary.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index a6761302..567d799f 100755 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -157,17 +157,9 @@ def __init__(self, value=None, n_bits=None, bigEndian=True, self._n_bits = n_bits - self._convert_to = { - BinaryRepresentation.UNSIGNED : self._convert_to_unsigned , - BinaryRepresentation.SIGNED_MAGNITUDE : self._convert_to_signed_mag , - BinaryRepresentation.TWOS_COMPLEMENT : self._convert_to_twos_comp , - } - - self._convert_from = { - BinaryRepresentation.UNSIGNED : self._convert_from_unsigned , - BinaryRepresentation.SIGNED_MAGNITUDE : self._convert_from_signed_mag , - BinaryRepresentation.TWOS_COMPLEMENT : self._convert_from_twos_comp , - } + self._convert_to = self._convert_to_map[self.binaryRepresentation].__get__(self, self.__class__) + + self._convert_from = self._convert_from_map[self.binaryRepresentation].__get__(self, self.__class__) if value is not None: self.assign(value) @@ -247,6 +239,18 @@ def _convert_from_twos_comp(self, x): rv = int(x.translate(_resolve_table), 2) return rv + _convert_to_map = { + BinaryRepresentation.UNSIGNED : _convert_to_unsigned, + BinaryRepresentation.SIGNED_MAGNITUDE : _convert_to_signed_mag, + BinaryRepresentation.TWOS_COMPLEMENT : _convert_to_twos_comp, + } + + _convert_from_map = { + BinaryRepresentation.UNSIGNED : _convert_from_unsigned, + BinaryRepresentation.SIGNED_MAGNITUDE : _convert_from_signed_mag, + BinaryRepresentation.TWOS_COMPLEMENT : _convert_from_twos_comp, + } + def _invert(self, x): inverted = '' for bit in x: @@ -322,11 +326,11 @@ def _adjust_twos_comp(self, x): @property def integer(self): """The integer representation of the underlying vector.""" - return self._convert_from[self.binaryRepresentation](self._str) + return self._convert_from(self._str) @integer.setter def integer(self, val): - self._str = self._convert_to[self.binaryRepresentation](val) + self._str = self._convert_to(val) @property def value(self): From 1ce3a9422793f3ee18251341198b69142b3b5666 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Mon, 6 Jan 2020 15:29:09 -0800 Subject: [PATCH 2625/2656] Skip BinaryValue.assign() in handle.value. In ModifiableObject.value getter, we can skip assign() and set binstr directly. This is already done in ConstantObject. --- cocotb/handle.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cocotb/handle.py b/cocotb/handle.py index 002ec25c..217c8bee 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -745,7 +745,9 @@ def _check_for_set_action(self, value): @NonConstantObject.value.getter def value(self) -> BinaryValue: binstr = self._handle.get_signal_val_binstr() - result = BinaryValue(binstr, len(binstr)) + # Skip BinaryValue.assign() as we know we are using a binstr + result = BinaryValue(n_bits=len(binstr)) + result.binstr = binstr return result def __int__(self): From 856dc28699b9c43c719c069253617ee2accd5e82 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Mon, 6 Jan 2020 15:31:06 -0800 Subject: [PATCH 2626/2656] Skip BinaryValue permitted chars check in handle.value. We trust the binstr value from the simulator, so skip the permitted chars check with new _set_trusted_binstr() method. --- cocotb/binary.py | 3 +++ cocotb/handle.py | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 567d799f..0e8c0b24 100755 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -448,6 +448,9 @@ def binstr(self, string): get_binstr = binstr.fget set_binstr = binstr.fset + def _set_trusted_binstr(self, string): + self._str = string + @property def n_bits(self): """The number of bits of the binary value.""" diff --git a/cocotb/handle.py b/cocotb/handle.py index 217c8bee..db4e0502 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -747,7 +747,8 @@ def value(self) -> BinaryValue: binstr = self._handle.get_signal_val_binstr() # Skip BinaryValue.assign() as we know we are using a binstr result = BinaryValue(n_bits=len(binstr)) - result.binstr = binstr + # Skip the permitted characters check as we trust the simulator + result._set_trusted_binstr(binstr) return result def __int__(self): From 1cd3b72f88f2a0ec2bd0775276e24811d4cd52bb Mon Sep 17 00:00:00 2001 From: Marlon James Date: Wed, 8 Jan 2020 13:40:44 -0800 Subject: [PATCH 2627/2656] Improve BinaryValue performance. Use str.translate() in _invert(). Remove redundant negative check in _convert_from_twos_comp(). Use str.join() in BinaryValue.buff setter. Use re.search() for permitted chars check in BinaryValue.binstr setter. --- cocotb/binary.py | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/cocotb/binary.py b/cocotb/binary.py index 0e8c0b24..b182a5b9 100755 --- a/cocotb/binary.py +++ b/cocotb/binary.py @@ -233,8 +233,7 @@ def _convert_from_twos_comp(self, x): binstr = x[1:] binstr = self._invert(binstr) rv = int(binstr, 2) + 1 - if x[0] == '1': - rv = rv * -1 + rv = rv * -1 else: rv = int(x.translate(_resolve_table), 2) return rv @@ -251,16 +250,10 @@ def _convert_from_twos_comp(self, x): BinaryRepresentation.TWOS_COMPLEMENT : _convert_from_twos_comp, } + _invert_table = str.maketrans({'0': '1', '1': '0'}) + def _invert(self, x): - inverted = '' - for bit in x: - if bit == '0': - inverted = inverted + '1' - elif bit == '1': - inverted = inverted + '0' - else: - inverted = inverted + bit - return inverted + return x.translate(self._invert_table) def _adjust_unsigned(self, x): if self._n_bits is None: @@ -404,12 +397,9 @@ def buff(self) -> bytes: @buff.setter def buff(self, val: bytes): - self._str = "" - for char in val: - if self.big_endian: - self._str += "{0:08b}".format(char) - else: - self._str = "{0:08b}".format(char) + self._str + if not self.big_endian: + val = reversed(val) + self._str = ''.join([format(char, "08b") for char in val]) self._adjust() def _adjust(self): @@ -436,12 +426,14 @@ def binstr(self): """ The binary representation stored as a string of ``0``, ``1``, and possibly ``x``, ``z``, and other states. """ return self._str + _non_permitted_regex = re.compile("[^{}]".format(_permitted_chars)) + @binstr.setter def binstr(self, string): - for char in string: - if char not in BinaryValue._permitted_chars: - raise ValueError("Attempting to assign character %s to a %s" % - (char, type(self).__qualname__)) + match = self._non_permitted_regex.search(string) + if match: + raise ValueError("Attempting to assign character %s to a %s" % + (match.group(), self.__class__.__name__)) self._str = string self._adjust() From 8ff91fa9e47ec6a49d3a0bb7f947a62f813cc184 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Mon, 4 Jan 2021 15:50:11 -0800 Subject: [PATCH 2628/2656] Improve BinaryValue testing/coverage. --- tests/pytest/test_binary_value.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/pytest/test_binary_value.py b/tests/pytest/test_binary_value.py index 5258964b..0a6556af 100644 --- a/tests/pytest/test_binary_value.py +++ b/tests/pytest/test_binary_value.py @@ -309,3 +309,11 @@ def test_buff_little_endian(): v.buff = b'\xC9\xF6' assert v.buff == orig_bytes assert v.binstr == orig_str + + +def test_bad_binstr(): + with pytest.raises(ValueError, match=r'Attempting to assign character 4 to a BinaryValue'): + BinaryValue(value="01XZ4") + + with pytest.raises(ValueError, match=r'Attempting to assign character % to a BinaryValue'): + BinaryValue(value="Uu%") From 710bf54f676744e61a4e170a3372a28f308eb691 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Wed, 6 Jan 2021 08:24:22 -0600 Subject: [PATCH 2629/2656] Fix libcocotbutils on Linux and Mac OS (#2330) libcocotbutils on Linux and Mac OS depends on the loader library "dl", however this dependency was never added and cocotb works fine. This is because libpython loaded libdl.so as a dependency, so it was resolved transitively. As of #2268, this dependency on libpython as a load-time dependency was severed. This continued to not be a porblem because simulators link against libdl to load VPI library (like cocotb). However, verilator is different, it is not a AoT compiled simulator that dynamically links in cocotb, but compiles cocotb support into each simulation, so it doesn't have the libdl dependency. The libdl dependency by libcocotbutils is now explicitly added for Linux and Mac OS. This is not a problem for Windows as all extension modules link against kernel32.dll which provides the equivalent dependency on Windows. --- cocotb/share/makefiles/simulators/Makefile.verilator | 2 +- cocotb_build_libs.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cocotb/share/makefiles/simulators/Makefile.verilator b/cocotb/share/makefiles/simulators/Makefile.verilator index 831d8ae2..a752e8ab 100644 --- a/cocotb/share/makefiles/simulators/Makefile.verilator +++ b/cocotb/share/makefiles/simulators/Makefile.verilator @@ -43,7 +43,7 @@ EXTRA_ARGS += --timescale $(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) SIM_BUILD_FLAGS += -std=c++11 -COMPILE_ARGS += --vpi --public-flat-rw --prefix Vtop -o $(TOPLEVEL) -LDFLAGS "-Wl,-rpath,$(LIB_DIR) -L$(LIB_DIR) -lcocotbvpi_verilator -lgpi -lcocotb -lgpilog -lcocotbutils" +COMPILE_ARGS += --vpi --public-flat-rw --prefix Vtop -o $(TOPLEVEL) -LDFLAGS "-Wl,-rpath,$(LIB_DIR) -L$(LIB_DIR) -lcocotbvpi_verilator" $(SIM_BUILD)/Vtop.mk: $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(COCOTB_SHARE_DIR)/lib/verilator/verilator.cpp | $(SIM_BUILD) $(CMD) -cc --exe -Mdir $(SIM_BUILD) -DCOCOTB_SIM=1 --top-module $(TOPLEVEL) $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) $(COCOTB_SHARE_DIR)/lib/verilator/verilator.cpp diff --git a/cocotb_build_libs.py b/cocotb_build_libs.py index 64a34081..fb987721 100755 --- a/cocotb_build_libs.py +++ b/cocotb_build_libs.py @@ -343,11 +343,14 @@ def _get_common_lib_ext(include_dir, share_lib_dir): ] if os.name == "nt": libcocotbutils_sources += ["libcocotbutils.rc"] + libcocotbutils_libraries = ["gpilog"] + if os.name != "nt": + libcocotbutils_libraries.append("dl") # dlopen, dlerror, dlsym libcocotbutils = Extension( os.path.join("cocotb", "libs", "libcocotbutils"), define_macros=[("COCOTBUTILS_EXPORTS", "")] + _extra_defines, include_dirs=[include_dir], - libraries=["gpilog"], + libraries=libcocotbutils_libraries, sources=libcocotbutils_sources, extra_link_args=_extra_link_args(lib_name="libcocotbutils", rpaths=["$ORIGIN"]), extra_compile_args=_extra_cxx_compile_args, From 97a91fdbc630f2d4f639c64a6e38bf5d0f478092 Mon Sep 17 00:00:00 2001 From: Pierre-Henri Horrein Date: Thu, 7 Jan 2021 16:06:54 +0100 Subject: [PATCH 2630/2656] Fix array_idx implementation of Buses (#2304) When creating a bus, in the case of unpacked arrays, it is possible to define the bus for a single index using the array_idx parameters (useful for drivers). Previously, this was done by using the "signame[i]" form of the signal name. However, this does not work for Verilator. A possible fix is to get the whole signal as a list, and get the correct index, which is implemented here. It also works on VHDL, with QuestaSim only (not with GHDL) --- cocotb/bus.py | 19 ++--- tests/test_cases/test_array_buses/Makefile | 17 ++++ .../test_array_buses/array_buses.sv | 32 ++++++++ .../test_array_buses/array_buses.vhd | 43 ++++++++++ .../test_array_buses/test_array_buses.py | 79 +++++++++++++++++++ 5 files changed, 179 insertions(+), 11 deletions(-) create mode 100644 tests/test_cases/test_array_buses/Makefile create mode 100644 tests/test_cases/test_array_buses/array_buses.sv create mode 100644 tests/test_cases/test_array_buses/array_buses.vhd create mode 100644 tests/test_cases/test_array_buses/test_array_buses.py diff --git a/cocotb/bus.py b/cocotb/bus.py index c9ed3dd3..12211deb 100755 --- a/cocotb/bus.py +++ b/cocotb/bus.py @@ -82,9 +82,7 @@ def __init__(self, entity, name, signals, optional_signals=[], bus_separator="_" else: signame = sig_name - if array_idx is not None: - signame += "[{:d}]".format(array_idx) - self._add_signal(attr_name, signame) + self._add_signal(attr_name, signame, array_idx) # Also support a set of optional signals that don't have to be present for attr_name, sig_name in _build_sig_attr_dict(optional_signals).items(): @@ -93,19 +91,18 @@ def __init__(self, entity, name, signals, optional_signals=[], bus_separator="_" else: signame = sig_name - if array_idx is not None: - signame += "[{:d}]".format(array_idx) - - self._entity._log.debug("Signal name {}".format(signame)) if hasattr(entity, signame): - self._add_signal(attr_name, signame) + self._add_signal(attr_name, signame, array_idx) else: self._entity._log.debug("Ignoring optional missing signal " "%s on bus %s" % (sig_name, name)) - def _add_signal(self, attr_name, signame): - self._entity._log.debug("Signal name {}".format(signame)) - setattr(self, attr_name, getattr(self._entity, signame)) + def _add_signal(self, attr_name, signame, array_idx=None): + self._entity._log.debug("Signal name {}, idx {}".format(signame, array_idx)) + handle = getattr(self._entity, signame) + if array_idx is not None: + handle = handle[array_idx] + setattr(self, attr_name, handle) self._signals[attr_name] = getattr(self, attr_name) def drive(self, obj, strict=False): diff --git a/tests/test_cases/test_array_buses/Makefile b/tests/test_cases/test_array_buses/Makefile new file mode 100644 index 00000000..94c7a28c --- /dev/null +++ b/tests/test_cases/test_array_buses/Makefile @@ -0,0 +1,17 @@ +TOPLEVEL_LANG ?= verilog +PWD=$(shell pwd) +TOPLEVEL := array_buses + +ifeq ($(TOPLEVEL_LANG),verilog) + VERILOG_SOURCES = $(PWD)/array_buses.sv +else +ifeq ($(TOPLEVEL_LANG), vhdl) + VHDL_SOURCES = $(PWD)/array_buses.vhd +else + $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") +endif +endif +include $(shell cocotb-config --makefiles)/Makefile.sim + + +MODULE = test_array_buses diff --git a/tests/test_cases/test_array_buses/array_buses.sv b/tests/test_cases/test_array_buses/array_buses.sv new file mode 100644 index 00000000..61509af1 --- /dev/null +++ b/tests/test_cases/test_array_buses/array_buses.sv @@ -0,0 +1,32 @@ +module array_buses + ( + input clk, + input [7:0] in_data [0:1], + input in_valid[0:1], + output [7:0] out_data [0:1], + output out_valid[0:1] + ); + + reg [7:0] tmp_data [1:0]; + reg tmp_valid [1:0]; + + initial begin + tmp_data[0] = '0; + tmp_data[1] = '0; + tmp_valid[0] = 1'b0; + tmp_valid[1] = 1'b0; + end + + always @(posedge clk) begin + tmp_data[0] <= in_data[0]; + tmp_data[1] <= in_data[1]; + tmp_valid[0] <= in_valid[0]; + tmp_valid[1] <= in_valid[1]; + end + + assign out_data[0] = tmp_data[0]; + assign out_data[1] = tmp_data[1]; + assign out_valid[0] = tmp_valid[0]; + assign out_valid[1] = tmp_valid[1]; +endmodule + diff --git a/tests/test_cases/test_array_buses/array_buses.vhd b/tests/test_cases/test_array_buses/array_buses.vhd new file mode 100644 index 00000000..90d8e0c7 --- /dev/null +++ b/tests/test_cases/test_array_buses/array_buses.vhd @@ -0,0 +1,43 @@ +library ieee; + +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +package array_types is + type io_array is array (0 to 1) of std_logic_vector(7 downto 0); +end package array_types; + +library ieee; + +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.array_types.all; + +entity array_buses is + port ( + clk : in std_logic; + in_data : in io_array; + in_valid : in std_logic_vector(0 to 1); + out_data : out io_array; + out_valid : out std_logic_vector(0 to 1) + ); +end; + +architecture impl of array_buses is + signal tmp_data: io_array := (others => (others => '0')); + signal tmp_valid: std_logic_vector(0 to 1) := (others => '0'); +begin + process (clk) + begin + if (rising_edge(clk)) then + tmp_data <= in_data; + tmp_valid <= in_valid; + end if; + end process; + + out_data(0) <= tmp_data(0); + out_data(1) <= tmp_data(1); + out_valid(0) <= tmp_valid(0); + out_valid(1) <= tmp_valid(1); + +end architecture; diff --git a/tests/test_cases/test_array_buses/test_array_buses.py b/tests/test_cases/test_array_buses/test_array_buses.py new file mode 100644 index 00000000..9329bab1 --- /dev/null +++ b/tests/test_cases/test_array_buses/test_array_buses.py @@ -0,0 +1,79 @@ +import cocotb +from cocotb.clock import Clock +from cocotb.drivers import BusDriver +from cocotb.monitors import BusMonitor +from cocotb.triggers import RisingEdge + + +class TestDriver(BusDriver): + _signals = ["data", "valid"] + + def __init__(self, entity, name, clock, **kwargs): + BusDriver.__init__(self, entity, name, clock, **kwargs) + self.bus.valid <= 0 + + async def _driver_send(self, transaction, sync=True): + clkedge = RisingEdge(self.clock) + if sync: + await clkedge + + self.log.info("Sending {}".format(transaction)) + self.bus.valid <= 1 + self.bus.data <= transaction + + await clkedge + self.bus.valid <= 0 + + +class TestMonitor(BusMonitor): + + _signals = ["data", "valid"] + + def __init__(self, entity, name, clock, bank=0, **kwargs): + BusMonitor.__init__(self, entity, name, clock, **kwargs) + self.bank = bank + self.add_callback(self._get_result) + self.expected = [] + + async def _monitor_recv(self): + clkedge = RisingEdge(self.clock) + while True: + await clkedge + if self.bus.valid.value: + self._recv(int(self.bus.data)) + + def _get_result(self, transaction): + self.log.info("Received {} on bank {}".format(transaction, self.bank)) + exp = self.expected.pop(0) + assert exp == int(transaction) + + def add_expected(self, transaction): + self.expected.append(int(transaction)) + + +@cocotb.test(expect_error=AttributeError if cocotb.SIM_NAME.lower().startswith('ghdl') else ()) +async def test_array_buses(dut): + clock = Clock(dut.clk, 10, units="ns") + cocotb.fork(clock.start()) + clkedge = RisingEdge(dut.clk) + in_data_0 = TestDriver(dut, "in", dut.clk, array_idx=0) + in_data_1 = TestDriver(dut, "in", dut.clk, array_idx=1) + out_data_0 = TestMonitor(dut, "in", dut.clk, array_idx=0, bank=0) + out_data_1 = TestMonitor(dut, "in", dut.clk, array_idx=1, bank=1) + out_data_0.add_expected(10) + out_data_0.add_expected(30) + out_data_1.add_expected(20) + out_data_1.add_expected(40) + + await in_data_0.send(10) + await clkedge + await in_data_1.send(20) + await clkedge + await in_data_0.send(30) + await clkedge + await in_data_1.send(40) + await clkedge + await clkedge + + assert not out_data_0.expected + assert not out_data_1.expected From 7ee86d5f3205a902ff89a11fd87ce1d884a41739 Mon Sep 17 00:00:00 2001 From: Ludwig Rogiers Date: Sun, 10 Jan 2021 03:19:47 +1100 Subject: [PATCH 2631/2656] set_signal_value_long() bugfixes and improvements (#2316) * Issue #1202: Change gpi_set_signal_value_long(long) to gpi_set_signal_value_int(int32_t) - vpi_put_value() is limited to 32-bit, so use int32_t instead of long. - Fix 32-bit platform compilation warning: comparison of integer expressions of different signedness. (cherry picked from commit 99079fa70b5f7b41bd68f52dd1ec9fc0e4e3b4b2) * Issue #1202: set IntegerObject using set_signal_value_int() (cherry picked from commit 9080c0d3c9fb575cfffc6aab573cca513a5f4695) * Issue #1202: Improve setting IntegerObject values - Check if value for IntegerObject is in 32-bit range - Raise OverflowError for out of range values - Update integer tests for the supported range and out of range (cherry picked from commit b15be8a66bc0490e30df00183739f634d14ef895) * Issue #1202: Replace set_signal_val_long() with set_signal_val_int() (cherry picked from commit 2ac766f4f59b4f14ea91fb5c130bce29d31228c2) * Issue #913: Fix issue setting ints to signals wider than 32-bit - Fix handling of negative numbers for signal widths wider than 32-bit - Add value range check - Add testcases - Add documentation about signed and unsigned values and supported range - Add newsfragment (cherry picked from commit 8548afccf66158db7a1402d957ba4e96e0166012) * Issue #268: use full 32-bit range to set signals - Performance improvement for setting 32-bit wide signals. - For signals up to 32-bit wide, set_signal_val_int() is used to set the full signed min negative to unsigned max positive data range from a python integer value. - Add max supported signal width check and signal width dependent overflow check in set_signal_val_int(). (cherry picked from commit 6e56a5e3da111412c1eab9e717bd703944e2e6a4) * Address review comments: - Consolidate integer and int tests and use assert_raises to check OverflowError - Perform value range check in handle.py rather than simulatormodule.cpp - Fix riviera regression test failures - Add newsfragment --- cocotb/handle.py | 72 ++++++- cocotb/share/include/gpi.h | 2 +- cocotb/share/lib/fli/FliImpl.h | 8 +- cocotb/share/lib/fli/FliObjHdl.cpp | 22 +- cocotb/share/lib/gpi/GpiCommon.cpp | 2 +- cocotb/share/lib/gpi/gpi_priv.h | 2 +- .../share/lib/simulator/simulatormodule.cpp | 18 +- cocotb/share/lib/vhpi/VhpiCbHdl.cpp | 18 +- cocotb/share/lib/vhpi/VhpiImpl.h | 4 +- cocotb/share/lib/vpi/VpiCbHdl.cpp | 4 +- cocotb/share/lib/vpi/VpiImpl.h | 2 +- .../source/newsfragments/913.bugfix.rst | 1 + .../source/newsfragments/913.change.rst | 2 + documentation/source/quickstart.rst | 36 ++++ tests/designs/sample_module/sample_module.sv | 3 + .../designs/sample_module/sample_module.vhdl | 3 + tests/test_cases/test_cocotb/test_handle.py | 204 ++++++++++++++++-- .../test_cocotb_array.py | 2 +- 18 files changed, 335 insertions(+), 70 deletions(-) create mode 100644 documentation/source/newsfragments/913.bugfix.rst create mode 100644 documentation/source/newsfragments/913.change.rst diff --git a/cocotb/handle.py b/cocotb/handle.py index db4e0502..a6163cdf 100755 --- a/cocotb/handle.py +++ b/cocotb/handle.py @@ -31,16 +31,40 @@ import ctypes import warnings +import enum +from functools import lru_cache import cocotb from cocotb import simulator -from cocotb.binary import BinaryValue +from cocotb.binary import BinaryValue, BinaryRepresentation from cocotb.log import SimLog # Only issue a warning for each deprecated attribute access _deprecation_warned = set() +class _Limits(enum.IntEnum): + SIGNED_NBIT = 1 + UNSIGNED_NBIT = 2 + VECTOR_NBIT = 3 + + +@lru_cache(maxsize=None) +def _value_limits(n_bits, limits): + """Calculate min/max for given number of bits and limits class""" + if limits == _Limits.SIGNED_NBIT: + min_val = -2**(n_bits-1) + max_val = 2**(n_bits-1) - 1 + elif limits == _Limits.UNSIGNED_NBIT: + min_val = 0 + max_val = 2**n_bits - 1 + else: + min_val = -2**(n_bits-1) + max_val = 2**n_bits - 1 + + return min_val, max_val + + class SimHandleBase: """Base class for all simulation objects. @@ -696,8 +720,10 @@ def _set_value(self, value, call_sim): The value to drive onto the simulator object. Raises: - TypeError: If target is not wide enough or has an unsupported type - for value assignment. + TypeError: If target has an unsupported type for value assignment. + + OverflowError: If int value is out of the range that can be represented + by the target. -2**(Nbits - 1) <= value <= 2**Nbits - 1 .. deprecated:: 1.5 :class:`ctypes.Structure` objects are no longer accepted as values for assignment. @@ -706,17 +732,28 @@ def _set_value(self, value, call_sim): """ value, set_action = self._check_for_set_action(value) - if isinstance(value, int) and value < 0x7fffffff and len(self) <= 32: - call_sim(self, self._handle.set_signal_val_long, set_action, value) - return + if isinstance(value, int): + min_val, max_val = _value_limits(len(self), _Limits.VECTOR_NBIT) + if min_val <= value <= max_val: + if len(self) <= 32: + call_sim(self, self._handle.set_signal_val_int, set_action, value) + return + + if value < 0: + value = BinaryValue(value=value, n_bits=len(self), bigEndian=False, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) + else: + value = BinaryValue(value=value, n_bits=len(self), bigEndian=False, binaryRepresentation=BinaryRepresentation.UNSIGNED) + else: + raise OverflowError( + "Int value ({!r}) out of range for assignment of {!r}-bit signal ({!r})" + .format(value, len(self), self._name)) + if isinstance(value, ctypes.Structure): warnings.warn( "`ctypes.Structure` values are no longer accepted for value assignment. " "Use `BinaryValue(value=bytes(struct_obj), n_bits=len(signal))` instead", DeprecationWarning, stacklevel=3) value = BinaryValue(value=cocotb.utils.pack(value), n_bits=len(self)) - elif isinstance(value, int): - value = BinaryValue(value=value, n_bits=len(self), bigEndian=False) elif isinstance(value, dict): # We're given a dictionary with a list of values and a bit size... num = 0 @@ -826,7 +863,13 @@ def _set_value(self, value, call_sim): "Unsupported type for enum value assignment: {} ({!r})" .format(type(value), value)) - call_sim(self, self._handle.set_signal_val_long, set_action, value) + min_val, max_val = _value_limits(32, _Limits.UNSIGNED_NBIT) + if min_val <= value <= max_val: + call_sim(self, self._handle.set_signal_val_int, set_action, value) + else: + raise OverflowError( + "Int value ({!r}) out of range for assignment of enum signal ({!r})" + .format(value, self._name)) @ModifiableObject.value.getter def value(self) -> int: @@ -848,6 +891,9 @@ def _set_value(self, value, call_sim): Raises: TypeError: If target has an unsupported type for integer value assignment. + + OverflowError: If value is out of range for assignment + of 32-bit IntegerObject. """ value, set_action = self._check_for_set_action(value) @@ -858,7 +904,13 @@ def _set_value(self, value, call_sim): "Unsupported type for integer value assignment: {} ({!r})" .format(type(value), value)) - call_sim(self, self._handle.set_signal_val_long, set_action, value) + min_val, max_val = _value_limits(32, _Limits.SIGNED_NBIT) + if min_val <= value <= max_val: + call_sim(self, self._handle.set_signal_val_int, set_action, value) + else: + raise OverflowError( + "Int value ({!r}) out of range for assignment of integer signal ({!r})" + .format(value, self._name)) @ModifiableObject.value.getter def value(self) -> int: diff --git a/cocotb/share/include/gpi.h b/cocotb/share/include/gpi.h index cb3d44d4..30282932 100644 --- a/cocotb/share/include/gpi.h +++ b/cocotb/share/include/gpi.h @@ -227,7 +227,7 @@ GPI_EXPORT int gpi_is_indexable(gpi_sim_hdl gpi_hdl); // Functions for setting the properties of a handle GPI_EXPORT void gpi_set_signal_value_real(gpi_sim_hdl gpi_hdl, double value, gpi_set_action_t action); -GPI_EXPORT void gpi_set_signal_value_long(gpi_sim_hdl gpi_hdl, long value, gpi_set_action_t action); +GPI_EXPORT void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int32_t value, gpi_set_action_t action); GPI_EXPORT void gpi_set_signal_value_binstr(gpi_sim_hdl gpi_hdl, const char *str, gpi_set_action_t action); // String of binary char(s) [1, 0, x, z] GPI_EXPORT void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str, gpi_set_action_t action); // String of ASCII char(s) diff --git a/cocotb/share/lib/fli/FliImpl.h b/cocotb/share/lib/fli/FliImpl.h index 6bd08073..24f7f09a 100644 --- a/cocotb/share/lib/fli/FliImpl.h +++ b/cocotb/share/lib/fli/FliImpl.h @@ -240,7 +240,7 @@ class FliValueObjHdl : public FliSignalObjHdl { double get_signal_value_real() override; long get_signal_value_long() override; - int set_signal_value(long value, gpi_set_action_t action) override; + int set_signal_value(int32_t value, gpi_set_action_t action) override; int set_signal_value(double value, gpi_set_action_t action) override; int set_signal_value_str(std::string &value, gpi_set_action_t action) override; int set_signal_value_binstr(std::string &value, gpi_set_action_t action) override; @@ -277,7 +277,7 @@ class FliEnumObjHdl : public FliValueObjHdl { long get_signal_value_long() override; using FliValueObjHdl::set_signal_value; - int set_signal_value(long value, gpi_set_action_t action) override; + int set_signal_value(int32_t value, gpi_set_action_t action) override; int initialise(std::string &name, std::string &fq_name) override; @@ -316,7 +316,7 @@ class FliLogicObjHdl : public FliValueObjHdl { const char* get_signal_value_binstr() override; using FliValueObjHdl::set_signal_value; - int set_signal_value(long value, gpi_set_action_t action) override; + int set_signal_value(int32_t value, gpi_set_action_t action) override; int set_signal_value_binstr(std::string &value, gpi_set_action_t action) override; int initialise(std::string &name, std::string &fq_name) override; @@ -347,7 +347,7 @@ class FliIntObjHdl : public FliValueObjHdl { long get_signal_value_long() override; using FliValueObjHdl::set_signal_value; - int set_signal_value(long value, gpi_set_action_t action) override; + int set_signal_value(int32_t value, gpi_set_action_t action) override; int initialise(std::string &name, std::string &fq_name) override; }; diff --git a/cocotb/share/lib/fli/FliObjHdl.cpp b/cocotb/share/lib/fli/FliObjHdl.cpp index b1bc6f7c..ce3c17bc 100644 --- a/cocotb/share/lib/fli/FliObjHdl.cpp +++ b/cocotb/share/lib/fli/FliObjHdl.cpp @@ -140,11 +140,11 @@ long FliValueObjHdl::get_signal_value_long() return -1; } -int FliValueObjHdl::set_signal_value(long value, gpi_set_action_t action) +int FliValueObjHdl::set_signal_value(int32_t value, gpi_set_action_t action) { COCOTB_UNUSED(value); COCOTB_UNUSED(action); - LOG_ERROR("Setting signal/variable value via long not supported for %s of type %d", m_fullname.c_str(), m_type); + LOG_ERROR("Setting signal/variable value via int32_t not supported for %s of type %d", m_fullname.c_str(), m_type); return -1; } @@ -225,7 +225,7 @@ long FliEnumObjHdl::get_signal_value_long() } } -int FliEnumObjHdl::set_signal_value(const long value, const gpi_set_action_t action) +int FliEnumObjHdl::set_signal_value(const int32_t value, const gpi_set_action_t action) { if (action != GPI_DEPOSIT) { LOG_ERROR("Force or release action not supported for FLI."); @@ -238,9 +238,9 @@ int FliEnumObjHdl::set_signal_value(const long value, const gpi_set_action_t act } if (m_is_var) { - mti_SetVarValue(get_handle(), value); + mti_SetVarValue(get_handle(), static_cast(value)); } else { - mti_SetSignalValue(get_handle(), value); + mti_SetSignalValue(get_handle(), static_cast(value)); } return 0; @@ -315,7 +315,7 @@ const char* FliLogicObjHdl::get_signal_value_binstr() return m_val_buff; } -int FliLogicObjHdl::set_signal_value(const long value, const gpi_set_action_t action) +int FliLogicObjHdl::set_signal_value(const int32_t value, const gpi_set_action_t action) { if (action != GPI_DEPOSIT) { LOG_ERROR("Force or release action not supported for FLI."); @@ -331,9 +331,9 @@ int FliLogicObjHdl::set_signal_value(const long value, const gpi_set_action_t ac mti_SetSignalValue(get_handle(), enumVal); } } else { - LOG_DEBUG("set_signal_value(long)::0x%016x", value); + LOG_DEBUG("set_signal_value(int32_t)::0x%08x", value); for (int i = 0, idx = m_num_elems-1; i < m_num_elems; i++, idx--) { - mtiInt32T enumVal = value&(1L<(), value); + mti_SetVarValue(get_handle(), static_cast(value)); } else { - mti_SetSignalValue(get_handle(), value); + mti_SetSignalValue(get_handle(), static_cast(value)); } return 0; diff --git a/cocotb/share/lib/gpi/GpiCommon.cpp b/cocotb/share/lib/gpi/GpiCommon.cpp index b4d84d1f..50bc8a91 100644 --- a/cocotb/share/lib/gpi/GpiCommon.cpp +++ b/cocotb/share/lib/gpi/GpiCommon.cpp @@ -509,7 +509,7 @@ int gpi_is_indexable(gpi_sim_hdl obj_hdl) return 0; } -void gpi_set_signal_value_long(gpi_sim_hdl sig_hdl, long value, gpi_set_action_t action) +void gpi_set_signal_value_int(gpi_sim_hdl sig_hdl, int32_t value, gpi_set_action_t action) { GpiSignalObjHdl *obj_hdl = static_cast(sig_hdl); diff --git a/cocotb/share/lib/gpi/gpi_priv.h b/cocotb/share/lib/gpi/gpi_priv.h index 0020ed0c..7d7d16fb 100644 --- a/cocotb/share/lib/gpi/gpi_priv.h +++ b/cocotb/share/lib/gpi/gpi_priv.h @@ -154,7 +154,7 @@ class GPI_EXPORT GpiSignalObjHdl : public GpiObjHdl { int m_length = 0; - virtual int set_signal_value(const long value, gpi_set_action_t action) = 0; + virtual int set_signal_value(const int32_t value, gpi_set_action_t action) = 0; virtual int set_signal_value(const double value, gpi_set_action_t action) = 0; virtual int set_signal_value_str(std::string &value, gpi_set_action_t action) = 0; virtual int set_signal_value_binstr(std::string &value, gpi_set_action_t action) = 0; diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index 11b611a0..61461ae5 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -41,6 +41,7 @@ static int sim_ending = 0; #include // COCOTB_UNUSED #include +#include #include #include // LOG_* macros #include // py_gpi_logger_set_level @@ -662,16 +663,16 @@ static PyObject *set_signal_val_real(gpi_hdl_Object *self, PyObject Py_RETURN_NONE; } -static PyObject *set_signal_val_long(gpi_hdl_Object *self, PyObject *args) +static PyObject *set_signal_val_int(gpi_hdl_Object *self, PyObject *args) { - long value; + long long value; gpi_set_action_t action; - if (!PyArg_ParseTuple(args, "il:set_signal_val_long", &action, &value)) { + if (!PyArg_ParseTuple(args, "iL:set_signal_val_int", &action, &value)) { return NULL; } - gpi_set_signal_value_long(self->hdl, value, action); + gpi_set_signal_value_int(self->hdl, static_cast(value), action); Py_RETURN_NONE; } @@ -1163,12 +1164,11 @@ static PyMethodDef gpi_sim_hdl_methods[] = { "Get the value of a signal as a float." ) }, - {"set_signal_val_long", - (PyCFunction)set_signal_val_long, METH_VARARGS, PyDoc_STR( - "set_signal_val_long($self, action, value, /)\n" + {"set_signal_val_int", + (PyCFunction)set_signal_val_int, METH_VARARGS, PyDoc_STR( + "set_signal_val_int($self, action, value, /)\n" "--\n\n" - "set_signal_val_long(action: int, value: int) -> None\n" - "Set the value of a signal using an integer.\n" + "Set the value of a signal using an int" ) }, {"set_signal_val_str", diff --git a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp index 931bfb9f..e7f52224 100644 --- a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp +++ b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp @@ -486,7 +486,7 @@ vhpiEnumT VhpiSignalObjHdl::chr2vhpi(const char value) } // Value related functions -int VhpiLogicSignalObjHdl::set_signal_value(long value, gpi_set_action_t action) +int VhpiLogicSignalObjHdl::set_signal_value(int32_t value, gpi_set_action_t action) { switch (m_value.format) { case vhpiEnumVal: @@ -499,7 +499,7 @@ int VhpiLogicSignalObjHdl::set_signal_value(long value, gpi_set_action_t action) case vhpiLogicVecVal: { int i; for (i=0; i; - if ((value > EnumLimits::max()) || (value < EnumLimits::min())) { - LOG_ERROR("VHPI: Data loss detected"); - return -1; - } m_value.value.enumv = static_cast(value); break; } case vhpiIntVal: { - using IntLimits = std::numeric_limits; - if ((value > IntLimits::max()) || (value < IntLimits::min())) { - LOG_ERROR("VHPI: Data loss detected"); - return -1; - } m_value.value.intg = static_cast(value); break; } diff --git a/cocotb/share/lib/vhpi/VhpiImpl.h b/cocotb/share/lib/vhpi/VhpiImpl.h index 5d0007e7..2dfc4d70 100644 --- a/cocotb/share/lib/vhpi/VhpiImpl.h +++ b/cocotb/share/lib/vhpi/VhpiImpl.h @@ -195,7 +195,7 @@ class VhpiSignalObjHdl : public GpiSignalObjHdl { long get_signal_value_long() override; using GpiSignalObjHdl::set_signal_value; - int set_signal_value(long value, gpi_set_action_t action) override; + int set_signal_value(int32_t value, gpi_set_action_t action) override; int set_signal_value(double value, gpi_set_action_t action) override; int set_signal_value_str(std::string &value, gpi_set_action_t action) override; int set_signal_value_binstr(std::string &value, gpi_set_action_t action) override; @@ -222,7 +222,7 @@ class VhpiLogicSignalObjHdl : public VhpiSignalObjHdl { using GpiSignalObjHdl::set_signal_value; - int set_signal_value(long value, gpi_set_action_t action) override; + int set_signal_value(int32_t value, gpi_set_action_t action) override; int set_signal_value_binstr(std::string &value, gpi_set_action_t action) override; int initialise(std::string &name, std::string &fq_name) override; diff --git a/cocotb/share/lib/vpi/VpiCbHdl.cpp b/cocotb/share/lib/vpi/VpiCbHdl.cpp index 4029ff61..7ff279d2 100644 --- a/cocotb/share/lib/vpi/VpiCbHdl.cpp +++ b/cocotb/share/lib/vpi/VpiCbHdl.cpp @@ -329,11 +329,11 @@ long VpiSignalObjHdl::get_signal_value_long() } // Value related functions -int VpiSignalObjHdl::set_signal_value(long value, gpi_set_action_t action) +int VpiSignalObjHdl::set_signal_value(int32_t value, gpi_set_action_t action) { s_vpi_value value_s; - value_s.value.integer = (int)value; + value_s.value.integer = static_cast(value); value_s.format = vpiIntVal; return set_signal_value(value_s, action); diff --git a/cocotb/share/lib/vpi/VpiImpl.h b/cocotb/share/lib/vpi/VpiImpl.h index 6b074b5e..08c23777 100644 --- a/cocotb/share/lib/vpi/VpiImpl.h +++ b/cocotb/share/lib/vpi/VpiImpl.h @@ -179,7 +179,7 @@ class VpiSignalObjHdl : public GpiSignalObjHdl { double get_signal_value_real() override; long get_signal_value_long() override; - int set_signal_value(const long value, gpi_set_action_t action) override; + int set_signal_value(const int32_t value, gpi_set_action_t action) override; int set_signal_value(const double value, gpi_set_action_t action) override; int set_signal_value_binstr(std::string &value, gpi_set_action_t action) override; int set_signal_value_str(std::string &value, gpi_set_action_t action) override; diff --git a/documentation/source/newsfragments/913.bugfix.rst b/documentation/source/newsfragments/913.bugfix.rst new file mode 100644 index 00000000..2ec78f72 --- /dev/null +++ b/documentation/source/newsfragments/913.bugfix.rst @@ -0,0 +1 @@ +Assigning Python ints to signals greater than 32 bits wide will now work correctly for negative values. diff --git a/documentation/source/newsfragments/913.change.rst b/documentation/source/newsfragments/913.change.rst new file mode 100644 index 00000000..3277cf3f --- /dev/null +++ b/documentation/source/newsfragments/913.change.rst @@ -0,0 +1,2 @@ +Assigning out-of-range Python ints to signals would previously truncate the value silently for signal widths <= 32 bits and truncate the value with a warning for signal widths > 32 bits. +Assigning out-of-range Python ints to signals will now raise an :exc:`OverflowError`. diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index 1e9f5fb1..68004eae 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -154,6 +154,42 @@ writes are not applied immediately, but delayed until the next write cycle. Use ``sig.setimmediatevalue(new_val)`` to set a new value immediately (see :meth:`~cocotb.handle.NonHierarchyObject.setimmediatevalue`). +Signed and unsigned values +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Both signed and unsigned values can be assigned to signals using a Python int. +Cocotb makes no assumptions regarding the signedness of the signal. It only +considers the width of the signal, so it will allow values in the range from +the minimum negative value for a signed number up to the maximum positive +value for an unsigned number: ``-2**(Nbits - 1) <= value <= 2**Nbits - 1`` +Note: assigning out-of-range values will raise an :exc:`OverflowError`. + +A :class:`BinaryValue` object can be used instead of a Python int to assign a +value to signals with more fine-grained control (e.g. signed values only). + +.. code-block:: verilog + + module my_module ( + input logic clk, + input logic rst, + input logic [2:0] data_in, + output logic [2:0] data_out + ); + +.. code-block:: python3 + + # assignment of negative value + dut.data_in <= -4 + + # assignment of positive value + dut.data_in <= 7 + + # assignment of out-of-range values + dut.data_in <= 8 # raises OverflowError + dut.data_in <= -5 # raises OverflowError + +Forcing and freezing signals +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In addition to regular value assignments (deposits), signals can be forced to a predetermined value or frozen at their current value. To achieve this, the various actions described in :ref:`assignment-methods` can be used. diff --git a/tests/designs/sample_module/sample_module.sv b/tests/designs/sample_module/sample_module.sv index 7823a343..b371749a 100644 --- a/tests/designs/sample_module/sample_module.sv +++ b/tests/designs/sample_module/sample_module.sv @@ -55,7 +55,10 @@ module sample_module #( input string stream_in_string, `endif input [7:0] stream_in_data, + input [31:0] stream_in_data_dword, + input [38:0] stream_in_data_39bit, input [63:0] stream_in_data_wide, + input [127:0] stream_in_data_dqword, input stream_out_ready, output reg [7:0] stream_out_data_comb, diff --git a/tests/designs/sample_module/sample_module.vhdl b/tests/designs/sample_module/sample_module.vhdl index bf943165..48fe4db4 100644 --- a/tests/designs/sample_module/sample_module.vhdl +++ b/tests/designs/sample_module/sample_module.vhdl @@ -42,7 +42,10 @@ entity sample_module is clk : in std_ulogic; stream_in_data : in std_ulogic_vector(7 downto 0); + stream_in_data_dword : in std_ulogic_vector(31 downto 0); + stream_in_data_39bit : in std_ulogic_vector(38 downto 0); stream_in_data_wide : in std_ulogic_vector(63 downto 0); + stream_in_data_dqword : in std_ulogic_vector(127 downto 0); stream_in_valid : in std_ulogic; stream_in_func_en : in std_ulogic; stream_in_ready : out std_ulogic; diff --git a/tests/test_cases/test_cocotb/test_handle.py b/tests/test_cases/test_cocotb/test_handle.py index 8c0dd53e..260f5173 100644 --- a/tests/test_cases/test_cocotb/test_handle.py +++ b/tests/test_cases/test_cocotb/test_handle.py @@ -10,6 +10,7 @@ from cocotb.triggers import Timer from common import assert_raises +from cocotb.handle import _Limits @cocotb.test() @@ -66,21 +67,198 @@ async def test_delayed_assignment_still_errors(dut): dut.stream_in_int <= [] +async def int_values_test(signal, n_bits, limits=_Limits.VECTOR_NBIT): + """Test integer access to a signal.""" + log = logging.getLogger("cocotb.test") + values = gen_int_test_values(n_bits, limits) + for val in values: + signal <= val + await Timer(1, 'ns') + + if limits == _Limits.VECTOR_NBIT: + if val < 0: + got = signal.value.signed_integer + else: + got = signal.value.integer + else: + got = signal.value + + assert got == val, "Expected value {}, got value {}!".format(val, got) + + +def gen_int_test_values(n_bits, limits=_Limits.VECTOR_NBIT): + """Generates a list of int test values for a given number of bits.""" + unsigned_min = 0 + unsigned_max = 2**n_bits-1 + signed_min = -2**(n_bits-1) + signed_max = 2**(n_bits-1)-1 + + if limits == _Limits.VECTOR_NBIT: + return [1, -1, 4, -4, unsigned_min, unsigned_max, signed_min, signed_max] + elif limits == _Limits.SIGNED_NBIT: + return [1, -1, 4, -4, signed_min, signed_max] + else: + return [1, -1, 4, -4, unsigned_min, unsigned_max] + + +async def int_overflow_test(signal, n_bits, test_mode, limits=_Limits.VECTOR_NBIT): + """Test integer overflow.""" + if test_mode == "ovfl": + value = gen_int_ovfl_value(n_bits, limits) + elif test_mode == "unfl": + value = gen_int_unfl_value(n_bits, limits) + else: + value = None + + with assert_raises(OverflowError): + signal <= value + + +def gen_int_ovfl_value(n_bits, limits=_Limits.VECTOR_NBIT): + unsigned_max = 2**n_bits-1 + signed_max = 2**(n_bits-1)-1 + + if limits == _Limits.SIGNED_NBIT: + return signed_max + 1 + elif limits == _Limits.UNSIGNED_NBIT: + return unsigned_max + 1 + else: + return unsigned_max + 1 + + +def gen_int_unfl_value(n_bits, limits=_Limits.VECTOR_NBIT): + unsigned_min = 0 + signed_min = -2**(n_bits-1) + + if limits == _Limits.SIGNED_NBIT: + return signed_min - 1 + elif limits == _Limits.UNSIGNED_NBIT: + return unsigned_min - 1 + else: + return signed_min - 1 + + +@cocotb.test() +async def test_int_8bit(dut): + """Test int access to 8-bit vector.""" + await int_values_test(dut.stream_in_data, len(dut.stream_in_data)) + + +@cocotb.test() +async def test_int_8bit_overflow(dut): + """Test 8-bit vector overflow.""" + await int_overflow_test(dut.stream_in_data, len(dut.stream_in_data), "ovfl") + + +@cocotb.test() +async def test_int_8bit_underflow(dut): + """Test 8-bit vector underflow.""" + await int_overflow_test(dut.stream_in_data, len(dut.stream_in_data), "unfl") + + +@cocotb.test() +async def test_int_32bit(dut): + """Test int access to 32-bit vector.""" + await int_values_test(dut.stream_in_data_dword, len(dut.stream_in_data_dword)) + + +@cocotb.test() +async def test_int_32bit_overflow(dut): + """Test 32-bit vector overflow.""" + await int_overflow_test(dut.stream_in_data_dword, len(dut.stream_in_data_dword), "ovfl") + + +@cocotb.test() +async def test_int_32bit_underflow(dut): + """Test 32-bit vector underflow.""" + await int_overflow_test(dut.stream_in_data_dword, len(dut.stream_in_data_dword), "unfl") + + +@cocotb.test() +async def test_int_39bit(dut): + """Test int access to 39-bit vector.""" + await int_values_test(dut.stream_in_data_39bit, len(dut.stream_in_data_39bit)) + + +@cocotb.test() +async def test_int_39bit_overflow(dut): + """Test 39-bit vector overflow.""" + await int_overflow_test(dut.stream_in_data_39bit, len(dut.stream_in_data_39bit), "ovfl") + + +@cocotb.test() +async def test_int_39bit_underflow(dut): + """Test 39-bit vector underflow.""" + await int_overflow_test(dut.stream_in_data_39bit, len(dut.stream_in_data_39bit), "unfl") + + +@cocotb.test() +async def test_int_64bit(dut): + """Test int access to 64-bit vector.""" + await int_values_test(dut.stream_in_data_wide, len(dut.stream_in_data_wide)) + + +@cocotb.test() +async def test_int_64bit_overflow(dut): + """Test 64-bit vector overflow.""" + await int_overflow_test(dut.stream_in_data_wide, len(dut.stream_in_data_wide), "ovfl") + + +@cocotb.test() +async def test_int_64bit_underflow(dut): + """Test 64-bit vector underflow.""" + await int_overflow_test(dut.stream_in_data_wide, len(dut.stream_in_data_wide), "unfl") + + +@cocotb.test() +async def test_int_128bit(dut): + """Test int access to 128-bit vector.""" + await int_values_test(dut.stream_in_data_dqword, len(dut.stream_in_data_dqword)) + + +@cocotb.test() +async def test_int_128bit_overflow(dut): + """Test 128-bit vector overflow.""" + await int_overflow_test(dut.stream_in_data_dqword, len(dut.stream_in_data_dqword), "ovfl") + + +@cocotb.test() +async def test_int_128bit_underflow(dut): + """Test 128-bit vector underflow.""" + await int_overflow_test(dut.stream_in_data_dqword, len(dut.stream_in_data_dqword), "unfl") + + @cocotb.test(expect_error=AttributeError if cocotb.SIM_NAME in ["Icarus Verilog"] else ()) async def test_integer(dut): - """ - Test access to integers - """ - log = logging.getLogger("cocotb.test") - await Timer(10, "ns") - dut.stream_in_int = 4 - await Timer(10, "ns") - await Timer(10, "ns") - got_in = int(dut.stream_out_int) - got_out = int(dut.stream_in_int) - log.info("dut.stream_out_int = %d" % got_out) - log.info("dut.stream_in_int = %d" % got_in) - assert got_in == got_out, "stream_in_int and stream_out_int should not match" + """Test access to integers.""" + if cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith("riviera"): + limits = _Limits.VECTOR_NBIT # stream_in_int is ModifiableObject in riviera, not IntegerObject + else: + limits = _Limits.SIGNED_NBIT + + await int_values_test(dut.stream_in_int, 32, limits) + + +@cocotb.test(expect_error=AttributeError if cocotb.SIM_NAME in ["Icarus Verilog"] else ()) +async def test_integer_overflow(dut): + """Test integer overflow.""" + if cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith("riviera"): + limits = _Limits.VECTOR_NBIT # stream_in_int is ModifiableObject in riviera, not IntegerObject + else: + limits = _Limits.SIGNED_NBIT + + await int_overflow_test(dut.stream_in_int, 32, "ovfl", limits) + + +@cocotb.test(expect_error=AttributeError if cocotb.SIM_NAME in ["Icarus Verilog"] else ()) +async def test_integer_underflow(dut): + """Test integer underflow.""" + if cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith("riviera"): + limits = _Limits.VECTOR_NBIT # stream_in_int is ModifiableObject in riviera, not IntegerObject + else: + limits = _Limits.SIGNED_NBIT + + await int_overflow_test(dut.stream_in_int, 32, "unfl", limits) @cocotb.test(expect_error=AttributeError if cocotb.SIM_NAME in ["Icarus Verilog"] else ()) diff --git a/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py b/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py index 9512db18..62262d06 100644 --- a/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py +++ b/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py @@ -117,7 +117,7 @@ async def test_in_vect_packed_packed_packed(dut): async def test_in_vect_packed_packed_unpacked(dut): await Timer(1, "ns") print("Setting: dut.in_vect_packed_packed_unpacked type %s" % type(dut.in_vect_packed_packed_unpacked)) - dut.in_vect_packed_packed_unpacked = [95869805, 95869805, 95869805] + dut.in_vect_packed_packed_unpacked = [365, 365, 365] await Timer(1, "ns") print("Getting: dut.out_vect_packed_packed_unpacked type %s" % type(dut.out_vect_packed_packed_unpacked)) if dut.out_vect_packed_packed_unpacked != [365, 365, 365]: From b7a756e76c95d36525759beee6e3022f4f6e6b43 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 15 Nov 2020 17:19:34 -0600 Subject: [PATCH 2632/2656] Use setuptools_scm to automatically generate version number --- .github/workflows/regression-tests.yml | 2 ++ .gitignore | 3 +++ cocotb/_version.py | 8 -------- pyproject.toml | 3 ++- setup.py | 10 ++++++---- 5 files changed, 13 insertions(+), 13 deletions(-) delete mode 100644 cocotb/_version.py diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml index 805b7b6c..62e7acc4 100644 --- a/.github/workflows/regression-tests.yml +++ b/.github/workflows/regression-tests.yml @@ -151,6 +151,8 @@ jobs: steps: - uses: actions/checkout@v2 + with: + fetch-depth: 0 # ensure we clone all the tags so we can see setuptools_scm working # Install Python - name: Set up Python ${{matrix.python-version}} diff --git a/.gitignore b/.gitignore index f585ba56..df099930 100644 --- a/.gitignore +++ b/.gitignore @@ -100,3 +100,6 @@ examples/*/tests/compile # Tachyon DA CVC tests/test_cases/*/verilog.log examples/*/tests/verilog.log + +# setuptools_scm artifact +/cocotb/_version.py diff --git a/cocotb/_version.py b/cocotb/_version.py deleted file mode 100644 index 102fd7f6..00000000 --- a/cocotb/_version.py +++ /dev/null @@ -1,8 +0,0 @@ -# Package versioning solution originally found here: -# http://stackoverflow.com/q/458550 - -# Store the version here so: -# 1) we don't load dependencies by storing it in __init__.py -# 2) we can import it in setup.py for the same reason -# 3) we can import it into your module -__version__ = '1.5.0.dev0' diff --git a/pyproject.toml b/pyproject.toml index 325abbb7..b8c511fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools", "wheel"] +requires = ["setuptools", "wheel", "setuptools_scm"] build-backend = "setuptools.build_meta" [tool.towncrier] @@ -36,3 +36,4 @@ build-backend = "setuptools.build_meta" directory = "change" name = "Changes" showcontent = true + diff --git a/setup.py b/setup.py index dfc565f9..e78309e7 100755 --- a/setup.py +++ b/setup.py @@ -73,9 +73,6 @@ def package_files(directory): return paths -# this sets the __version__ variable -exec(read_file(path.join('cocotb', '_version.py'))) - # store log from build_libs and display at the end in verbose mode # see https://github.com/pypa/pip/issues/6634 log_stream = StringIO() @@ -87,7 +84,11 @@ def package_files(directory): setup( name='cocotb', cmdclass={'build_ext': build_ext}, - version=__version__, # noqa: F821 + use_scm_version=dict( + write_to='cocotb/_version.py', + write_to_template='__version__ = {version!r}', + version_scheme='release-branch-semver' + ), description='cocotb is a coroutine based cosimulation library for writing VHDL and Verilog testbenches in Python.', url='https://docs.cocotb.org', license='BSD', @@ -96,6 +97,7 @@ def package_files(directory): author='Chris Higgs, Stuart Hodgson', maintainer='cocotb contributors', maintainer_email='cocotb@lists.librecores.org', + setup_requires=['setuptools_scm'], install_requires=[], python_requires='>=3.5', packages=find_packages(), From 61fe8c470853d9b6af14ef55c50f47aeeeca7c2f Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 9 Jan 2021 11:43:16 -0600 Subject: [PATCH 2633/2656] Remove ping TUN TAP example from repo and docs (#2234) --- .../source/diagrams/svg/ping_tun_tap.svg | 1 - .../source/diagrams/xml/ping_tun_tap.xml | 1 - documentation/source/index.rst | 1 - .../source/newsfragments/2232.removal.rst | 1 + documentation/source/ping_tun_tap.rst | 156 ----------------- examples/Makefile | 1 - examples/ping_tun_tap/hdl/icmp_reply.sv | 164 ------------------ examples/ping_tun_tap/tests/Makefile | 48 ----- .../ping_tun_tap/tests/test_icmp_reply.py | 134 -------------- 9 files changed, 1 insertion(+), 506 deletions(-) delete mode 100644 documentation/source/diagrams/svg/ping_tun_tap.svg delete mode 100644 documentation/source/diagrams/xml/ping_tun_tap.xml create mode 100644 documentation/source/newsfragments/2232.removal.rst delete mode 100644 documentation/source/ping_tun_tap.rst delete mode 100644 examples/ping_tun_tap/hdl/icmp_reply.sv delete mode 100644 examples/ping_tun_tap/tests/Makefile delete mode 100644 examples/ping_tun_tap/tests/test_icmp_reply.py diff --git a/documentation/source/diagrams/svg/ping_tun_tap.svg b/documentation/source/diagrams/svg/ping_tun_tap.svg deleted file mode 100644 index a602a447..00000000 --- a/documentation/source/diagrams/svg/ping_tun_tap.svg +++ /dev/null @@ -1 +0,0 @@ -
      ping
      [Object]
      TUN virtual interface
      [Object]
      File descriptor
      [Object]
      read()write()
      Driver
      [Object]
      Monitor
      [Object]
      DUT
      [Object]
      send()wait_for_recv()SimulatorLinuxTestbench
      \ No newline at end of file diff --git a/documentation/source/diagrams/xml/ping_tun_tap.xml b/documentation/source/diagrams/xml/ping_tun_tap.xml deleted file mode 100644 index f6633699..00000000 --- a/documentation/source/diagrams/xml/ping_tun_tap.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/documentation/source/index.rst b/documentation/source/index.rst index cc62d96a..25836d5e 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -96,7 +96,6 @@ A test can spawn multiple coroutines, allowing for independent flows of executio :hidden: endian_swapper - ping_tun_tap hal_cosimulation examples diff --git a/documentation/source/newsfragments/2232.removal.rst b/documentation/source/newsfragments/2232.removal.rst new file mode 100644 index 00000000..8e84ff6d --- /dev/null +++ b/documentation/source/newsfragments/2232.removal.rst @@ -0,0 +1 @@ +The outdated "ping" example has been removed from the documentation and repo. diff --git a/documentation/source/ping_tun_tap.rst b/documentation/source/ping_tun_tap.rst deleted file mode 100644 index 8e031876..00000000 --- a/documentation/source/ping_tun_tap.rst +++ /dev/null @@ -1,156 +0,0 @@ -************** -Tutorial: Ping -************** - -One of the benefits of Python is the ease with which interfacing is possible. -In this tutorial we'll look at interfacing the standard GNU `ping`_ command -to the simulator. Using Python we can ping our :term:`DUT` with fewer than 50 lines of -code. - -For the impatient this tutorial is provided as an example with cocotb. You can -run this example from a fresh checkout: - -.. code-block:: bash - - cd examples/ping_tun_tap/tests - sudo make - -.. note:: To create a virtual interface the test either needs root permissions or - have ``CAP_NET_ADMIN`` capability. - - -Architecture -============ - -We have a simple :term:`RTL` block that takes ICMP echo requests and generates an ICMP -echo response. To verify this behavior we want to run the `ping`_ utility -against our :term:`RTL` running in the simulator. - -In order to achieve this we need to capture the packets that are created by -ping, drive them onto the pins of our :term:`DUT` in simulation, monitor the output of -the :term:`DUT` and send any responses back to the ping process. - -Linux has a `TUN/TAP`_ virtual network device which we can use for this -purpose, allowing `ping`_ to run unmodified and unaware that it is -communicating with our simulation rather than a remote network endpoint. - -.. image:: diagrams/svg/ping_tun_tap.svg - - -Implementation -============== - -First of all we need to work out how to create a virtual interface. Python has -a huge developer base and a quick search of the web reveals a `TUN example`_ -that looks like an ideal starting point for our testbench. Using this example -we write a function that will create our virtual interface: - -.. code-block:: python3 - - import subprocess, fcntl, struct - - def create_tun(name="tun0", ip="192.168.255.1"): - TUNSETIFF = 0x400454ca - TUNSETOWNER = TUNSETIFF + 2 - IFF_TUN = 0x0001 - IFF_NO_PI = 0x1000 - tun = open('/dev/net/tun', 'r+b') - ifr = struct.pack('16sH', name, IFF_TUN | IFF_NO_PI) - fcntl.ioctl(tun, TUNSETIFF, ifr) - fcntl.ioctl(tun, TUNSETOWNER, 1000) - subprocess.check_call('ifconfig tun0 %s up pointopoint 192.168.255.2 up' % ip, shell=True) - return tun - -Now we can get started on the actual test. First of all we'll create a clock -signal and connect up the :class:`Avalon driver ` and -:class:`monitor ` to the :term:`DUT`. To help debug -the testbench we'll enable verbose debug on the drivers and monitors by setting -the log level to ``logging.DEBUG``. - -.. code-block:: python3 - - import cocotb - from cocotb.clock import Clock - from cocotb.drivers.avalon import AvalonSTPkts as AvalonSTDriver - from cocotb.monitors.avalon import AvalonSTPkts as AvalonSTMonitor - - @cocotb.test() - def tun_tap_example_test(dut): - cocotb.fork(Clock(dut.clk, 5000).start()) - - stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) - stream_out = AvalonSTMonitor(dut, "stream_out", dut.clk) - - # Enable verbose logging on the streaming interfaces - stream_in.log.setLevel(logging.DEBUG) - stream_out.log.setLevel(logging.DEBUG) - - -We also need to reset the :term:`DUT` and drive some default values onto some of the -bus signals. Note that we'll need to import the :class:`~.triggers.Timer` -and :class:`~.triggers.RisingEdge` triggers. - -.. code-block:: python3 - - # Reset the DUT - dut._log.debug("Resetting DUT") - dut.reset_n <= 0 - stream_in.bus.valid <= 0 - yield Timer(10, units='ns') - yield RisingEdge(dut.clk) - dut.reset_n <= 1 - dut.stream_out_ready <= 1 - - -The rest of the test becomes fairly straightforward. We create our TUN -interface using our function defined previously. We'll also use the -:mod:`subprocess` module to actually start the ping command. - -We then wait for a packet by calling a blocking read call on the TUN file -descriptor and simply append that to the queue on the driver. We wait for -a packet to arrive on the monitor by yielding on :meth:`.wait_for_recv()` and then -write the received packet back to the TUN file descriptor. - - -.. code-block:: python3 - - # Create our interface (destroyed at the end of the test) - tun = create_tun() - fd = tun.fileno() - - # Kick off a ping... - subprocess.check_call('ping -c 5 192.168.255.2 &', shell=True) - - # Respond to 5 pings, then quit - for i in range(5): - - cocotb.log.info("Waiting for packets on tun interface") - packet = os.read(fd, 2048) - cocotb.log.info("Received a packet!") - - stream_in.append(packet) - result = yield stream_out.wait_for_recv() - - os.write(fd, str(result)) - - -That's it - simple! - - -Further work -============ - -This example is deliberately simplistic to focus on the fundamentals of -interfacing to the simulator using TUN/TAP. As an exercise for the reader a -useful addition would be to make the file descriptor non-blocking and spawn -out separate coroutines for the monitor / driver, thus decoupling the sending -and receiving of packets. - - -.. _TUN example: https://gist.github.com/glacjay/585369 - -.. _Ping: https://www.gnu.org/software/inetutils/manual/html_node/ping-invocation.html - -.. _TUN/TAP: https://en.wikipedia.org/wiki/TUN/TAP - - diff --git a/examples/Makefile b/examples/Makefile index 3a7df6b8..5449aca3 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -35,7 +35,6 @@ EXAMPLES := adder/tests \ matrix_multiplier/tests \ mean/tests \ mixed_language/tests -# requires root permissions: ping_tun_tap/tests .PHONY: $(EXAMPLES) diff --git a/examples/ping_tun_tap/hdl/icmp_reply.sv b/examples/ping_tun_tap/hdl/icmp_reply.sv deleted file mode 100644 index 9f5fc87e..00000000 --- a/examples/ping_tun_tap/hdl/icmp_reply.sv +++ /dev/null @@ -1,164 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2013 Potential Ventures Ltd -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of Potential Ventures Ltd, -// Copyright (c) 2013 SolarFlare Communications Inc nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -//----------------------------------------------------------------------------- -// -// Simple ICMP echo server to repond to pings. -// -// Does store-and-forward using an array then modifies the checksum in place -// -// Doesn't perform any validation of the packet etc. -// -// Note this is not an example of how to write RTL ;) - -module icmp_reply ( - input clk, - input reset_n, - - input [31:0] stream_in_data, - input [1:0] stream_in_empty, - input stream_in_valid, - input stream_in_startofpacket, - input stream_in_endofpacket, - output reg stream_in_ready, - - output reg [31:0] stream_out_data, - output reg [1:0] stream_out_empty, - output reg stream_out_valid, - output reg stream_out_startofpacket, - output reg stream_out_endofpacket, - input stream_out_ready -); - -parameter S_IDLE = 3'b000; -parameter S_RECV_PACKET = 3'b001; -parameter S_MODIFY_PACKET = 3'b010; -parameter S_SEND_PACKET = 3'b011; - -reg [2:0] state; -reg [31:0] packet_buffer [31:0]; - -reg [4:0] rx_word_ptr, tx_word_ptr; -reg [1:0] empty_saved; - - - -always @(posedge clk or negedge reset_n) begin - if (!reset_n) begin - state <= S_IDLE; - rx_word_ptr <= 0; - tx_word_ptr <= 0; - stream_out_valid <= 1'b0; - stream_in_ready <= 1'b0; - end else begin - - case (state) - S_IDLE: begin - - stream_in_ready <= 1'b1; - - if (stream_in_startofpacket && stream_in_valid && stream_in_ready) begin - state <= S_RECV_PACKET; - rx_word_ptr <= 1; - packet_buffer[0] <= stream_in_data; - end - end - - S_RECV_PACKET: begin - if (stream_in_valid) begin - packet_buffer[rx_word_ptr] <= stream_in_data; - rx_word_ptr <= rx_word_ptr + 1; - - if (stream_in_endofpacket) begin - state <= S_MODIFY_PACKET; - stream_in_ready <= 1'b0; - empty_saved <= stream_in_empty; - end - end - end - - // NB since we do all modification in one cycle this won't - // synthesise as a RAM - code not intended for actual use - S_MODIFY_PACKET: begin - - // Swap src/destination addresses - packet_buffer[3] <= packet_buffer[4]; - packet_buffer[4] <= packet_buffer[3]; - - // Change the ICMP type to Echo Reply - packet_buffer[5][7:0] <= 8'b0; - - // Modify checksum in-place - packet_buffer[5][31:16] <= packet_buffer[5][31:16] - 16'h0800; - - state <= S_SEND_PACKET; - stream_out_startofpacket<= 1'b1; - stream_out_empty <= 0; - end - - S_SEND_PACKET: begin - stream_out_valid <= 1'b1; - stream_out_data <= packet_buffer[tx_word_ptr]; - - if (stream_out_ready) begin - tx_word_ptr <= tx_word_ptr + 1; - - if (tx_word_ptr > 0) - stream_out_startofpacket<= 1'b0; - - if (tx_word_ptr == rx_word_ptr - 1) begin - stream_out_empty <= empty_saved; - stream_out_endofpacket <= 1'b1; - end - - if (tx_word_ptr == rx_word_ptr) begin - state <= S_IDLE; - rx_word_ptr <= 0; - tx_word_ptr <= 0; - stream_out_valid <= 1'b0; - stream_out_endofpacket <= 1'b0; - end - - end - end - default: begin - state <= S_IDLE; - end - endcase - end -end - -`ifdef COCOTB_SIM -`ifndef VERILATOR // traced differently -initial begin - $dumpfile ("waveform.vcd"); - $dumpvars (0,icmp_reply); - #1 $display("Sim running..."); -end -`endif -`endif - -endmodule diff --git a/examples/ping_tun_tap/tests/Makefile b/examples/ping_tun_tap/tests/Makefile deleted file mode 100644 index e5f73e42..00000000 --- a/examples/ping_tun_tap/tests/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -TOPLEVEL_LANG ?= verilog - -ifneq ($(TOPLEVEL_LANG),verilog) - -all: - @echo "Skipping example due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being verilog" -clean:: - -else - -TOPLEVEL = icmp_reply - -PWD=$(shell pwd) - -VERILOG_SOURCES = $(PWD)/../hdl/icmp_reply.sv -MODULE=test_icmp_reply - -include $(shell cocotb-config --makefiles)/Makefile.sim - -endif diff --git a/examples/ping_tun_tap/tests/test_icmp_reply.py b/examples/ping_tun_tap/tests/test_icmp_reply.py deleted file mode 100644 index 8f516a7b..00000000 --- a/examples/ping_tun_tap/tests/test_icmp_reply.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright (c) 2013 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import logging - -import fcntl -import os -import struct -import subprocess - -import cocotb - -from cocotb.clock import Clock -from cocotb.triggers import Timer, RisingEdge -from cocotb.drivers.avalon import AvalonSTPkts as AvalonSTDriver -from cocotb.monitors.avalon import AvalonSTPkts as AvalonSTMonitor - - -def create_tun(name="tun0", ip="192.168.255.1"): - cocotb.log.info("Attempting to create interface %s (%s)" % (name, ip)) - TUNSETIFF = 0x400454ca - TUNSETOWNER = TUNSETIFF + 2 - IFF_TUN = 0x0001 - IFF_NO_PI = 0x1000 - tun = open('/dev/net/tun', 'r+b') - tun_num = int(name.split('tun')[-1]) - - # Try and create tun device until we find a name not in use - # eg. tun0, tun1, tun2... - while True: - try: - name = 'tun{}'.format(tun_num) - ifr = struct.pack('16sH', name, IFF_TUN | IFF_NO_PI) - cocotb.log.info(name) - fcntl.ioctl(tun, TUNSETIFF, ifr) - break - except IOError as e: - # Errno 16 if tun device already exists, otherwise this - # failed for different reason. - if e.errno != 16: - raise e - - tun_num += 1 - - fcntl.ioctl(tun, TUNSETOWNER, 1000) - subprocess.check_call('ifconfig %s %s up pointopoint 192.168.255.2 up' % - (name, ip), shell=True) - cocotb.log.info("Created interface %s (%s)" % (name, ip)) - return tun - - -@cocotb.test() -def tun_tap_example_test(dut): - """Example of a test using TUN/TAP. - - Creates an interface (192.168.255.1) and any packets received are sent - into the DUT. The response output by the DUT is then sent back out on - this interface. - - Note to create the TUN interface this test must be run as root or the user - must have CAP_NET_ADMIN capability. - """ - - cocotb.fork(Clock(dut.clk, 5, units='ns').start()) - - stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) - stream_out = AvalonSTMonitor(dut, "stream_out", dut.clk) - - # Enable verbose logging so we can see what's going on - stream_in.log.setLevel(logging.DEBUG) - stream_out.log.setLevel(logging.DEBUG) - - # Reset the DUT - dut._log.debug("Resetting DUT") - dut.reset_n <= 0 - stream_in.bus.valid <= 0 - yield Timer(10, units='ns') - yield RisingEdge(dut.clk) - dut.reset_n <= 1 - dut.stream_out_ready <= 1 - dut._log.debug("Out of reset") - - # Create our interface (destroyed at the end of the test) - tun = create_tun() - fd = tun.fileno() - - # Kick off a ping... - subprocess.check_call('ping -c 5 192.168.255.2 &', shell=True) - - # Respond to 5 pings, then quit - pingcounter = 0 - while True: - cocotb.log.info("Waiting for packets on tun interface") - packet = os.read(fd, 2048) - cocotb.log.info("Received a packet!") - - if packet[9] == '\x01' and packet[20] == '\x08': - cocotb.log.debug("Packet is an ICMP echo request") - pingcounter += 1 - else: - cocotb.log.info("Packet is no ICMP echo request, throwing away packet") - continue - - stream_in.append(packet) - result = yield stream_out.wait_for_recv() - - cocotb.log.info("Rtl replied!") - os.write(fd, str(result)) - - if pingcounter == 5: - break From ccf4ba4ffb6ed6fae23e79af56df8acb001cffb8 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 9 Jan 2021 18:18:37 -0600 Subject: [PATCH 2634/2656] Make vendor dir private (#2333) --- cocotb/{vendor => _vendor}/__init__.py | 0 cocotb/{vendor => _vendor}/find_libpython/__init__.py | 0 cocotb/config.py | 2 +- setup.cfg | 2 +- 4 files changed, 2 insertions(+), 2 deletions(-) rename cocotb/{vendor => _vendor}/__init__.py (100%) rename cocotb/{vendor => _vendor}/find_libpython/__init__.py (100%) diff --git a/cocotb/vendor/__init__.py b/cocotb/_vendor/__init__.py similarity index 100% rename from cocotb/vendor/__init__.py rename to cocotb/_vendor/__init__.py diff --git a/cocotb/vendor/find_libpython/__init__.py b/cocotb/_vendor/find_libpython/__init__.py similarity index 100% rename from cocotb/vendor/find_libpython/__init__.py rename to cocotb/_vendor/find_libpython/__init__.py diff --git a/cocotb/config.py b/cocotb/config.py index d2f6d968..1f78c056 100755 --- a/cocotb/config.py +++ b/cocotb/config.py @@ -43,7 +43,7 @@ import sys import textwrap import cocotb -import cocotb.vendor.find_libpython as find_libpython +import cocotb._vendor.find_libpython as find_libpython __all__ = ["share_dir", "makefiles_dir"] diff --git a/setup.cfg b/setup.cfg index b1331df9..7f5d1a7a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,7 +6,7 @@ exclude = venv .tox examples/endian_swapper/cosim - vendor + _vendor ignore = E202 # whitespace before ')' From e6a308e9d372aa1fa3651093fa300b09e1f837b7 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 9 Jan 2021 18:28:12 -0600 Subject: [PATCH 2635/2656] Make scheduler interfaces private (#2331) --- cocotb/__init__.py | 8 +- cocotb/decorators.py | 8 +- cocotb/ipython_support.py | 2 +- cocotb/regression.py | 2 +- cocotb/scheduler.py | 101 ++++++++++++++++-- .../source/newsfragments/2278.removal.rst | 4 + 6 files changed, 104 insertions(+), 21 deletions(-) create mode 100644 documentation/source/newsfragments/2278.removal.rst diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 214ab297..8d123a97 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -270,15 +270,13 @@ def _sim_event(level, message): if level is SIM_TEST_FAIL: scheduler.log.error("Failing test at simulator request") - scheduler.finish_test(TestFailure("Failure from external source: %s" % - message)) + scheduler._finish_test(TestFailure("Failure from external source: {}".format(message))) elif level is SIM_FAIL: # We simply return here as the simulator will exit # so no cleanup is needed - msg = ("Failing test at simulator request before test run completion: " - "%s" % message) + msg = "Failing test at simulator request before test run completion: {}".format(message) scheduler.log.error(msg) - scheduler.finish_scheduler(SimFailure(msg)) + scheduler._finish_scheduler(SimFailure(msg)) else: scheduler.log.error("Unsupported sim event") diff --git a/cocotb/decorators.py b/cocotb/decorators.py index 32934449..31ee8f6f 100644 --- a/cocotb/decorators.py +++ b/cocotb/decorators.py @@ -228,7 +228,7 @@ def kill(self): self.log.debug("kill() called on coroutine") # todo: probably better to throw an exception for anyone waiting on the coroutine self._outcome = outcomes.Value(None) - cocotb.scheduler.unschedule(self) + cocotb.scheduler._unschedule(self) def join(self): """Return a trigger that will fire when the wrapped coroutine exits.""" @@ -335,7 +335,7 @@ def abort(self, exc): if _debug: self.log.debug("outcome forced to {}".format(outcome)) self._outcome = outcome - cocotb.scheduler.unschedule(self) + cocotb.scheduler._unschedule(self) def sort_name(self): if self.stage is None: @@ -395,7 +395,7 @@ def log(self): return SimLog("cocotb.function.%s" % self._coro.__qualname__, id(self)) def __call__(self, *args, **kwargs): - return cocotb.scheduler.queue_function(self._coro(*args, **kwargs)) + return cocotb.scheduler._queue_function(self._coro(*args, **kwargs)) def __get__(self, obj, owner=None): """Permit the decorator to be used on class methods @@ -418,7 +418,7 @@ def __init__(self, func): self._log = SimLog("cocotb.external.%s" % self._func.__qualname__, id(self)) def __call__(self, *args, **kwargs): - return cocotb.scheduler.run_in_executor(self._func, *args, **kwargs) + return cocotb.scheduler._run_in_executor(self._func, *args, **kwargs) def __get__(self, obj, owner=None): """Permit the decorator to be used on class methods diff --git a/cocotb/ipython_support.py b/cocotb/ipython_support.py index 45d1fc64..51366810 100644 --- a/cocotb/ipython_support.py +++ b/cocotb/ipython_support.py @@ -24,7 +24,7 @@ def in_prompt_tokens(self, cli=None): def _runner(shell, x): """ Handler for async functions """ - ret = cocotb.scheduler.queue_function(x) + ret = cocotb.scheduler._queue_function(x) shell.prompts._show_time = shell.execution_count return ret diff --git a/cocotb/regression.py b/cocotb/regression.py index 3a075e78..a5b0b6d8 100644 --- a/cocotb/regression.py +++ b/cocotb/regression.py @@ -470,7 +470,7 @@ def _start_test(self) -> None: self._test_start_time = time.time() self._test_start_sim_time = get_sim_time('ns') - cocotb.scheduler.add_test(self._test_task) + cocotb.scheduler._add_test(self._test_task) def _log_test_summary(self) -> None: diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py index 4b8518fb..808fcd5c 100755 --- a/cocotb/scheduler.py +++ b/cocotb/scheduler.py @@ -39,6 +39,7 @@ import logging import threading import inspect +import warnings from typing import Any, Union from collections.abc import Coroutine @@ -326,6 +327,14 @@ def _test_completed(self, trigger=None): self._check_termination() def react(self, trigger): + """ + .. deprecated:: 1.5 + This function is now private. + """ + warnings.warn("This function is now private.", DeprecationWarning, stacklevel=2) + return self._react(trigger) + + def _react(self, trigger): """ Called when a trigger fires. @@ -440,7 +449,7 @@ def _event_loop(self, trigger): continue if _debug: self.log.debug("Scheduling coroutine %s" % (coro._coro.__qualname__)) - self.schedule(coro, trigger=trigger) + self._schedule(coro, trigger=trigger) if _debug: self.log.debug("Scheduled coroutine %s" % (coro._coro.__qualname__)) @@ -469,6 +478,14 @@ def _event_loop(self, trigger): " to simulator") def unschedule(self, coro): + """ + .. deprecated:: 1.5 + This function is now private. + """ + warnings.warn("This function is now private.", DeprecationWarning, stacklevel=2) + return self._unschedule(coro) + + def _unschedule(self, coro): """Unschedule a coroutine. Unprime any pending triggers""" # Unprime the trigger this coroutine is waiting on @@ -489,10 +506,10 @@ def unschedule(self, coro): if not self._terminate: self._terminate = True - self.cleanup() + self._cleanup() elif Join(coro) in self._trigger2coros: - self.react(Join(coro)) + self._react(Join(coro)) else: try: # throws an error if the background coroutine errored @@ -500,11 +517,11 @@ def unschedule(self, coro): coro._outcome.get() except (TestComplete, AssertionError) as e: coro.log.info("Test stopped by this forked coroutine") - e = remove_traceback_frames(e, ['unschedule', 'get']) + e = remove_traceback_frames(e, ['_unschedule', 'get']) self._test.abort(e) except Exception as e: coro.log.error("Exception raised by this forked coroutine") - e = remove_traceback_frames(e, ['unschedule', 'get']) + e = remove_traceback_frames(e, ['_unschedule', 'get']) self._test.abort(e) def _schedule_write(self, handle, write_func, *args): @@ -542,7 +559,7 @@ def _resume_coro_upon(self, coro, trigger): "More than one coroutine waiting on an unprimed trigger") try: - trigger.prime(self.react) + trigger.prime(self._react) except Exception as e: # discard the trigger we associated, it will never fire self._trigger2coros.pop(trigger) @@ -554,10 +571,26 @@ def _resume_coro_upon(self, coro, trigger): ) def queue(self, coroutine): + """ + .. deprecated:: 1.5 + This function is now private. + """ + warnings.warn("This function is now private.", DeprecationWarning, stacklevel=2) + return self._queue(coroutine) + + def _queue(self, coroutine): """Queue a coroutine for execution""" self._pending_coros.append(coroutine) def queue_function(self, coro): + """ + .. deprecated:: 1.5 + This function is now private. + """ + warnings.warn("This function is now private.", DeprecationWarning, stacklevel=2) + return self._queue_function(coro) + + def _queue_function(self, coro): """Queue a coroutine for execution and move the containing thread so that it does not block execution of the main thread any longer. """ @@ -601,6 +634,14 @@ async def wrapper(): return event.outcome.get() def run_in_executor(self, func, *args, **kwargs): + """ + .. deprecated:: 1.5 + This function is now private. + """ + warnings.warn("This function is now private.", DeprecationWarning, stacklevel=2) + return self._run_in_executor(func, *args, **kwargs) + + def _run_in_executor(self, func, *args, **kwargs): """Run the coroutine in a separate execution thread and return an awaitable object for the caller. """ @@ -675,7 +716,7 @@ def add(self, coroutine: Union[RunningTask, Coroutine]) -> RunningTask: if _debug: self.log.debug("Adding new coroutine %s" % task._coro.__qualname__) - self.schedule(task) + self._schedule(task) self._check_termination() return task @@ -694,10 +735,18 @@ def start_soon(self, coro: Union[Coroutine, RunningTask]) -> RunningTask: if _debug: self.log.debug("Queueing a new coroutine %s" % task._coro.__qualname__) - self.queue(task) + self._queue(task) return task def add_test(self, test_coro): + """ + .. deprecated:: 1.5 + This function is now private. + """ + warnings.warn("This function is now private.", DeprecationWarning, stacklevel=2) + return self._add_test(test_coro) + + def _add_test(self, test_coro): """Called by the regression manager to queue the next test""" if self._test is not None: raise InternalError("Test was added while another was in progress") @@ -721,7 +770,7 @@ def _trigger_from_started_coro(self, result: cocotb.decorators.RunningTask) -> T return result.join() def _trigger_from_unstarted_coro(self, result: cocotb.decorators.RunningTask) -> Trigger: - self.queue(result) + self._queue(result) if _debug: self.log.debug("Scheduling nested coroutine: %s" % result._coro.__qualname__) @@ -779,6 +828,14 @@ def _task_context(self, task): self._current_task = old_task def schedule(self, coroutine, trigger=None): + """ + .. deprecated:: 1.5 + This function is now private. + """ + warnings.warn("This function is now private.", DeprecationWarning, stacklevel=2) + return self._schedule(coroutine, trigger) + + def _schedule(self, coroutine, trigger=None): """Schedule a coroutine by calling the send method. Args: @@ -812,7 +869,7 @@ def schedule(self, coroutine, trigger=None): # this can't go in the else above, as that causes unwanted exception # chaining if coro_completed: - self.unschedule(coroutine) + self._unschedule(coroutine) # Don't handle the result if we're shutting down if self._terminate: @@ -852,10 +909,26 @@ def schedule(self, coroutine, trigger=None): self.add(self._pending_coros.pop(0)) def finish_test(self, exc): + """ + .. deprecated:: 1.5 + This function is now private. + """ + warnings.warn("This function is now private.", DeprecationWarning, stacklevel=2) + return self._finish_test(exc) + + def _finish_test(self, exc): self._test.abort(exc) self._check_termination() def finish_scheduler(self, exc): + """ + .. deprecated:: 1.5 + This function is now private. + """ + warnings.warn("This function is now private.", DeprecationWarning, stacklevel=2) + return self._finish_scheduler(exc) + + def _finish_scheduler(self, exc): """Directly call into the regression manager and end test once we return the sim will close us so no cleanup is needed. """ @@ -868,6 +941,14 @@ def finish_scheduler(self, exc): cocotb.regression_manager.handle_result(self._test) def cleanup(self): + """ + .. deprecated:: 1.5 + This function is now private. + """ + warnings.warn("This function is now private.", DeprecationWarning, stacklevel=2) + return self._cleanup() + + def _cleanup(self): """Clear up all our state. Unprime all pending triggers and kill off any coroutines, stop all externals. diff --git a/documentation/source/newsfragments/2278.removal.rst b/documentation/source/newsfragments/2278.removal.rst new file mode 100644 index 00000000..1a822b50 --- /dev/null +++ b/documentation/source/newsfragments/2278.removal.rst @@ -0,0 +1,4 @@ +The access modes of many interfaces in the cocotb core libraries were re-evaluated. +Some interfaces that were previously public are now private and vice versa. +Accessing the methods through their old name will create a :class:`DeprecationWarning`. +In the future, the deprecated names will be removed. From 9f271359d8f77903fc4fb90c0257f27f492a7ab8 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 12 Jan 2021 07:42:41 +0100 Subject: [PATCH 2636/2656] Add support for SystemVerilog type bit; add test for it (#2322) Add support for SystemVerilog type bit; add test for it Co-authored-by: Kaleb Barrett --- cocotb/share/lib/vpi/VpiImpl.cpp | 2 + .../source/newsfragments/2322.feature.rst | 1 + tests/designs/sample_module/sample_module.sv | 18 +++++++ .../test_discovery/test_discovery.py | 50 +++++++++++++++++++ 4 files changed, 71 insertions(+) create mode 100644 documentation/source/newsfragments/2322.feature.rst diff --git a/cocotb/share/lib/vpi/VpiImpl.cpp b/cocotb/share/lib/vpi/VpiImpl.cpp index ad9faaba..52acbd00 100644 --- a/cocotb/share/lib/vpi/VpiImpl.cpp +++ b/cocotb/share/lib/vpi/VpiImpl.cpp @@ -102,6 +102,7 @@ static gpi_objtype_t to_gpi_objtype(int32_t vpitype) case vpiNetBit: return GPI_NET; + case vpiBitVar: case vpiReg: case vpiRegBit: case vpiMemoryWord: @@ -191,6 +192,7 @@ GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, switch (type) { case vpiNet: case vpiNetBit: + case vpiBitVar: case vpiReg: case vpiRegBit: case vpiEnumNet: diff --git a/documentation/source/newsfragments/2322.feature.rst b/documentation/source/newsfragments/2322.feature.rst new file mode 100644 index 00000000..e0f21ad1 --- /dev/null +++ b/documentation/source/newsfragments/2322.feature.rst @@ -0,0 +1 @@ +Support for the SystemVerilog type ``bit`` has been added. diff --git a/tests/designs/sample_module/sample_module.sv b/tests/designs/sample_module/sample_module.sv index b371749a..8d3f461a 100644 --- a/tests/designs/sample_module/sample_module.sv +++ b/tests/designs/sample_module/sample_module.sv @@ -147,4 +147,22 @@ reg _underscore_name; assign _underscore_name = 0; `endif +bit mybit; +bit [1:0] mybits; +bit [1:0] mybits_uninitialized; +initial begin + mybit = 1; + mybits = '1; +end + +always @(*) begin + $display("%m: mybit has been updated, new value is %b", mybit); +end +always @(*) begin + $display("%m: mybits has been updated, new value is %b", mybits); +end +always @(*) begin + $display("%m: mybits_uninitialized has been updated, new value is %b", mybits_uninitialized); +end + endmodule diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py index cbc814f1..ba01d5ec 100644 --- a/tests/test_cases/test_discovery/test_discovery.py +++ b/tests/test_cases/test_discovery/test_discovery.py @@ -27,6 +27,7 @@ import cocotb import logging +from cocotb.binary import BinaryValue from cocotb.triggers import Timer from cocotb.result import TestError, TestFailure from cocotb.handle import IntegerObject, ConstantObject, HierarchyObject, StringObject @@ -89,6 +90,55 @@ async def access_signal(dut): dut.stream_in_data.value.integer, 1)) +@cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"]) +async def access_type_bit_verilog(dut): + """Access type bit in SystemVerilog""" + await Timer(1, "step") + assert dut.mybit.value == 1, "The default value was incorrect" + dut.mybit <= 0 + await Timer(1, "ns") + assert dut.mybit.value == 0, "The assigned value was incorrect" + + assert dut.mybits.value == 0b11, "The default value was incorrect" + dut.mybits <= 0b00 + await Timer(1, "ns") + assert dut.mybits.value == 0b00, "The assigned value was incorrect" + + assert dut.mybits_uninitialized.value == 0b00, "The default value was incorrect" + dut.mybits_uninitialized <= 0b11 + await Timer(1, "ns") + assert dut.mybits_uninitialized.value == 0b11, "The assigned value was incorrect" + + +@cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"]) +async def access_type_bit_verilog_metavalues(dut): + """Access type bit in SystemVerilog with metavalues that the type does not support. + + Note that some simulators (wrongly) allow metavalues even for bits when taking the VPI route. + The metavalues still may show up as `0` and `1` in HDL (Xcelium and Riviera). + """ + await Timer(1, "ns") + dut.mybits <= BinaryValue("XZ") + await Timer(1, "ns") + print(dut.mybits.value.binstr) + if cocotb.SIM_NAME.lower().startswith(("icarus", "ncsim", "xmsim")): + assert dut.mybits.value.binstr.lower() == "xz", "The assigned value was not as expected" + elif cocotb.SIM_NAME.lower().startswith(("riviera",)): + assert dut.mybits.value.binstr.lower() == "10", "The assigned value was not as expected" + else: + assert dut.mybits.value.binstr.lower() == "00", "The assigned value was incorrect" + + dut.mybits <= BinaryValue("ZX") + await Timer(1, "ns") + print(dut.mybits.value.binstr) + if cocotb.SIM_NAME.lower().startswith(("icarus", "ncsim", "xmsim")): + assert dut.mybits.value.binstr.lower() == "zx", "The assigned value was not as expected" + elif cocotb.SIM_NAME.lower().startswith(("riviera",)): + assert dut.mybits.value.binstr.lower() == "01", "The assigned value was not as expected" + else: + assert dut.mybits.value.binstr.lower() == "00", "The assigned value was incorrect" + + @cocotb.test( # Icarus up to (including) 10.3 doesn't support bit-selects, see https://github.com/steveicarus/iverilog/issues/323 expect_error=IndexError if (cocotb.SIM_NAME.lower().startswith("icarus") and (IcarusVersion(cocotb.SIM_VERSION) <= IcarusVersion("10.3 (stable)"))) else (), From ab34940be799b341f5921edc263f7a4e231fa8d4 Mon Sep 17 00:00:00 2001 From: tpambor <1379478+tpambor@users.noreply.github.com> Date: Tue, 12 Jan 2021 16:34:21 +0100 Subject: [PATCH 2637/2656] Fix FliImpl.cpp missing stdexcept include (#2336) --- cocotb/share/lib/fli/FliImpl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cocotb/share/lib/fli/FliImpl.cpp b/cocotb/share/lib/fli/FliImpl.cpp index 3a17f741..06f07765 100644 --- a/cocotb/share/lib/fli/FliImpl.cpp +++ b/cocotb/share/lib/fli/FliImpl.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "FliImpl.h" #include "mti.h" From ad1fe4883b2fc6f75682651fa31c53d161414a29 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Tue, 12 Jan 2021 22:52:14 +0100 Subject: [PATCH 2638/2656] Document the LIBPYTHON_LOC variable --- cocotb/config.py | 1 + cocotb/share/makefiles/Makefile.inc | 4 ++-- documentation/source/building.rst | 6 ++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/cocotb/config.py b/cocotb/config.py index 1f78c056..2f132745 100755 --- a/cocotb/config.py +++ b/cocotb/config.py @@ -72,6 +72,7 @@ def help_vars_text(): COCOTB_LOG_LEVEL Default logging level (default INFO) COCOTB_RESOLVE_X How to resolve X, Z, U, W on integer conversion MEMCHECK HTTP port to use for debugging Python memory usage + LIBPYTHON_LOC Absolute path to libpython Regression Manager ------------------ diff --git a/cocotb/share/makefiles/Makefile.inc b/cocotb/share/makefiles/Makefile.inc index 14842e87..912bde04 100644 --- a/cocotb/share/makefiles/Makefile.inc +++ b/cocotb/share/makefiles/Makefile.inc @@ -128,8 +128,8 @@ define find_libpython_errmsg = find_libpython was not able to find a libpython in the current Python environment. Ensure the Python development packages are installed. If they are installed and find_libpython is not finding the path to libpython, file an upstream bug in find_libpython; then -manually override the LIBPYTHON_LOC make variable with the aboslute path to libpython.so -(or libpython.dll on Windows). +manually override the LIBPYTHON_LOC make variable with the absolute path to libpython.so +(or python.dll on Windows). endef diff --git a/documentation/source/building.rst b/documentation/source/building.rst index 062b389e..1e10df35 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -130,6 +130,12 @@ Cocotb This needs the :mod:`cherrypy` and :mod:`dowser` Python modules installed. +.. envvar:: LIBPYTHON_LOC + + The absolute path the Python library associated with the current Python installation; + i.e. ``libpython.so`` or ``python.dll`` on Windows. + This is determined with ``cocotb-config --libpython`` in cocotb's makefiles. + Regression Manager ~~~~~~~~~~~~~~~~~~ From a20ebc2aae042024dc2387778d7165a92dcc04f1 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Wed, 13 Jan 2021 15:17:26 -0800 Subject: [PATCH 2639/2656] Add test for waiting on both RisingEdge and FallingEdge of a signal simultaneously --- .../test_cocotb/test_edge_triggers.py | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/tests/test_cases/test_cocotb/test_edge_triggers.py b/tests/test_cases/test_cocotb/test_edge_triggers.py index 24233921..4c21b1e9 100644 --- a/tests/test_cases/test_cocotb/test_edge_triggers.py +++ b/tests/test_cases/test_cocotb/test_edge_triggers.py @@ -10,8 +10,9 @@ * ClockCycles """ import cocotb -from cocotb.triggers import RisingEdge, FallingEdge, Edge, Timer, ClockCycles, First +from cocotb.triggers import RisingEdge, FallingEdge, Edge, Timer, ClockCycles, First, Combine from cocotb.clock import Clock +from cocotb.result import SimTimeoutError async def count_edges_cycles(signal, edges): @@ -208,3 +209,27 @@ async def wait_ten(): b = cocotb.fork(wait_ten()) await a.join() await b.join() + + +@cocotb.test( + timeout_time=100, + timeout_unit="ns", + expect_error=( + SimTimeoutError if ( + cocotb.LANGUAGE in ["verilog"] and + cocotb.SIM_NAME.lower().startswith(("riviera", "aldec")) # gh-2344 + ) + else () + ), +) +async def test_both_edge_triggers(dut): + async def wait_rising_edge(): + await RisingEdge(dut.clk) + + async def wait_falling_edge(): + await FallingEdge(dut.clk) + + rising_coro = cocotb.fork(wait_rising_edge()) + falling_coro = cocotb.fork(wait_falling_edge()) + cocotb.fork(Clock(dut.clk, 10, units='ns').start()) + await Combine(rising_coro, falling_coro) From 045d5a981fe9ce0f3a7cd57a8bf4d49477f21e24 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Thu, 14 Jan 2021 10:40:46 -0800 Subject: [PATCH 2640/2656] For Verilator debug, build the Verilator interfaces with debug info --- cocotb/share/makefiles/simulators/Makefile.verilator | 1 + 1 file changed, 1 insertion(+) diff --git a/cocotb/share/makefiles/simulators/Makefile.verilator b/cocotb/share/makefiles/simulators/Makefile.verilator index a752e8ab..78bffc55 100644 --- a/cocotb/share/makefiles/simulators/Makefile.verilator +++ b/cocotb/share/makefiles/simulators/Makefile.verilator @@ -33,6 +33,7 @@ ifeq ($(VERILATOR_SIM_DEBUG), 1) COMPILE_ARGS += --debug PLUSARGS += +verilator+debug SIM_BUILD_FLAGS += -DVL_DEBUG + USER_CPPFLAGS += -g endif ifeq ($(VERILATOR_TRACE),1) From d72c053aff5a474bbc1339265fe8982e80a2cad0 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Fri, 15 Jan 2021 17:56:01 +0000 Subject: [PATCH 2641/2656] Enable assertion rewriting with pytest (#2028) This means that code like `assert dut.signal.value == 3` will now show the actual signal value when the assertion fails. --- cocotb/__init__.py | 17 ++++++++++++++ .../source/newsfragments/2028.feature.rst | 2 ++ tests/test_cases/test_cocotb/Makefile | 1 + tests/test_cases/test_cocotb/test_pytest.py | 22 +++++++++++++++++++ 4 files changed, 42 insertions(+) create mode 100644 documentation/source/newsfragments/2028.feature.rst create mode 100644 tests/test_cases/test_cocotb/test_pytest.py diff --git a/cocotb/__init__.py b/cocotb/__init__.py index 8d123a97..f3e2d333 100644 --- a/cocotb/__init__.py +++ b/cocotb/__init__.py @@ -252,6 +252,23 @@ def _initialise_testbench(argv_): global top top = cocotb.handle.SimHandle(handle) + try: + import pytest + except ImportError: + log.warning("Pytest not found, assertion rewriting will not occur") + else: + try: + # Install the assertion rewriting hook, which must be done before we + # import the test modules. + from _pytest.config import Config + from _pytest.assertion import install_importhook + pytest_conf = Config.fromdictargs([], {}) + install_importhook(pytest_conf) + except Exception: + log.exception( + "Configuring the assertion rewrite hook using pytest {} failed. " + "Please file a bug report!".format(pytest.__version__)) + # start Regression Manager global regression_manager regression_manager = RegressionManager.from_discovery(top) diff --git a/documentation/source/newsfragments/2028.feature.rst b/documentation/source/newsfragments/2028.feature.rst new file mode 100644 index 00000000..78836b68 --- /dev/null +++ b/documentation/source/newsfragments/2028.feature.rst @@ -0,0 +1,2 @@ +If ``pytest`` is installed, its assertion-rewriting framework will be used to +produce more informative tracebacks from the :keyword:`assert` statement. \ No newline at end of file diff --git a/tests/test_cases/test_cocotb/Makefile b/tests/test_cases/test_cocotb/Makefile index bbd4c68a..3a3519b6 100644 --- a/tests/test_cases/test_cocotb/Makefile +++ b/tests/test_cases/test_cocotb/Makefile @@ -18,6 +18,7 @@ MODULE := "\ test_async_coroutines,\ test_handle,\ test_logging,\ + test_pytest,\ " ifeq ($(shell python -c "import sys; print(sys.version_info >= (3, 6))"), "True") diff --git a/tests/test_cases/test_cocotb/test_pytest.py b/tests/test_cases/test_cocotb/test_pytest.py new file mode 100644 index 00000000..b510e7ca --- /dev/null +++ b/tests/test_cases/test_cocotb/test_pytest.py @@ -0,0 +1,22 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause +""" Tests relating to pytest integration """ + +import cocotb + +# pytest is an optional dependency +try: + import pytest +except ImportError: + pytest = None + + +@cocotb.test(skip=pytest is None) +async def test_assertion_rewriting(dut): + """ Test that assertion rewriting hooks take effect in cocotb tests """ + try: + assert 1 != 42 + except AssertionError as e: + assert "42" in str(e), ( + "Assertion rewriting seems not to work, message was {}".format(e)) From 5d2d7c7cc2fdd840590a1891adb9cbacb31701ad Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Wed, 25 Nov 2020 22:43:13 +0000 Subject: [PATCH 2642/2656] Update test expectations for Riviera-PRO 2020.04/10 Riviera-PRO 2020.04 and 2020.10 behave identical to 2019.10 in our discovery tests, update the expectations accordingly. --- tests/test_cases/test_array/test_array.py | 2 +- tests/test_cases/test_iteration_vhdl/test_iteration.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py index f3482749..d012bd09 100644 --- a/tests/test_cases/test_array/test_array.py +++ b/tests/test_cases/test_array/test_array.py @@ -346,7 +346,7 @@ async def test_discover_all(dut): pass_total = 813 elif cocotb.SIM_VERSION.startswith(("2016.02")): pass_total = 947 - elif cocotb.SIM_VERSION.startswith(("2019.10")): + elif cocotb.SIM_VERSION.startswith(("2019.10", "2020.")): # vpiVariables finds port_rec_out and sig_rec pass_total = 1006 else: diff --git a/tests/test_cases/test_iteration_vhdl/test_iteration.py b/tests/test_cases/test_iteration_vhdl/test_iteration.py index caf5e55d..9d29383b 100644 --- a/tests/test_cases/test_iteration_vhdl/test_iteration.py +++ b/tests/test_cases/test_iteration_vhdl/test_iteration.py @@ -46,7 +46,7 @@ def total_object_count(): # Riviera-PRO if SIM_NAME.startswith("riviera"): - if SIM_VERSION.startswith("2019.10"): + if SIM_VERSION.startswith(("2019.10", "2020.")): return 27359 if SIM_VERSION.startswith("2016.02"): return 32393 @@ -112,7 +112,7 @@ async def iteration_loop(): await Combine(loop_one, loop_two) -@cocotb.test(expect_fail=(cocotb.SIM_NAME.lower().startswith("riviera") and cocotb.SIM_VERSION.startswith("2019.10")) or cocotb.SIM_NAME.lower().startswith("aldec")) +@cocotb.test(expect_fail=(cocotb.SIM_NAME.lower().startswith("riviera") and cocotb.SIM_VERSION.startswith(("2019.10", "2020."))) or cocotb.SIM_NAME.lower().startswith("aldec")) async def test_n_dimension_array(dut): """Test iteration over multi-dimensional array.""" tlog = logging.getLogger("cocotb.test") From 36491cb69c1e15cfc276ca171fcd17aa9db8269e Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Tue, 19 Jan 2021 08:49:02 -0600 Subject: [PATCH 2643/2656] Update instructions for installing development version (#2357) Following the move to setuptools_scm installing from master.zip no longer works. setuptools_scm needs a copy of the sources with all of the git information, and master.zip does not contain that information. --- documentation/source/install_devel.rst | 4 ++-- documentation/source/quickstart.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/documentation/source/install_devel.rst b/documentation/source/install_devel.rst index 9cb9034b..d1101573 100644 --- a/documentation/source/install_devel.rst +++ b/documentation/source/install_devel.rst @@ -31,7 +31,7 @@ The instructions for installing the development version of cocotb itself vary de .. code-block:: bash - pip install --global-option build_ext --global-option --compiler=mingw32 https://github.com/cocotb/cocotb/archive/master.zip + pip install --global-option build_ext --global-option --compiler=mingw32 git+https://github.com/cocotb/cocotb@master .. group-tab:: Linux and macOS @@ -39,7 +39,7 @@ The instructions for installing the development version of cocotb itself vary de .. code-block:: bash - pip install https://github.com/cocotb/cocotb/archive/master.zip + pip install git+https://github.com/cocotb/cocotb@master .. note:: diff --git a/documentation/source/quickstart.rst b/documentation/source/quickstart.rst index 68004eae..51497964 100644 --- a/documentation/source/quickstart.rst +++ b/documentation/source/quickstart.rst @@ -15,8 +15,8 @@ a :ref:`supported simulator`) and cocotb itself (``pip instal Download and extract the cocotb source files according to the *release version* you are using from https://github.com/cocotb/cocotb/releases - you can check your cocotb version with ``cocotb-config --version``. -The sources for cocotb's *development version* are available from -https://github.com/cocotb/cocotb/archive/master.zip +The sources for cocotb's *development version* are available from https://github.com/cocotb/cocotb. +See :ref:`install-devel` for more details. The following lines are all you need to run a first simulation with cocotb: From 26304cfc5fc9adbf567f2929910d5b6b9f414331 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Tue, 19 Jan 2021 10:51:53 -0600 Subject: [PATCH 2644/2656] Timer param deprecation (#2352) * Remove time_ps from C++ source * Rename 'time_ps' to 'time' in Timer --- cocotb/share/include/gpi.h | 2 +- cocotb/share/lib/fli/FliCbHdl.cpp | 14 +++++------ cocotb/share/lib/fli/FliImpl.cpp | 10 ++++---- cocotb/share/lib/fli/FliImpl.h | 10 ++++---- cocotb/share/lib/gpi/GpiCommon.cpp | 4 ++-- cocotb/share/lib/gpi/gpi_priv.h | 2 +- .../share/lib/simulator/simulatormodule.cpp | 6 ++--- cocotb/share/lib/vhpi/VhpiCbHdl.cpp | 6 ++--- cocotb/share/lib/vhpi/VhpiImpl.cpp | 4 ++-- cocotb/share/lib/vhpi/VhpiImpl.h | 4 ++-- cocotb/share/lib/vpi/VpiCbHdl.cpp | 6 ++--- cocotb/share/lib/vpi/VpiImpl.cpp | 4 ++-- cocotb/share/lib/vpi/VpiImpl.h | 4 ++-- cocotb/triggers.py | 24 ++++++++++++++----- .../test_cases/test_cocotb/test_deprecated.py | 10 ++++++++ 15 files changed, 66 insertions(+), 44 deletions(-) diff --git a/cocotb/share/include/gpi.h b/cocotb/share/include/gpi.h index 30282932..1ed8cfe2 100644 --- a/cocotb/share/include/gpi.h +++ b/cocotb/share/include/gpi.h @@ -237,7 +237,7 @@ typedef enum gpi_edge { } gpi_edge_e; // The callback registering functions -GPI_EXPORT gpi_cb_hdl gpi_register_timed_callback (int (*gpi_function)(const void *), void *gpi_cb_data, uint64_t time_ps); +GPI_EXPORT gpi_cb_hdl gpi_register_timed_callback (int (*gpi_function)(const void *), void *gpi_cb_data, uint64_t time); GPI_EXPORT gpi_cb_hdl gpi_register_value_change_callback (int (*gpi_function)(const void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl, int edge); GPI_EXPORT gpi_cb_hdl gpi_register_readonly_callback (int (*gpi_function)(const void *), void *gpi_cb_data); GPI_EXPORT gpi_cb_hdl gpi_register_nexttime_callback (int (*gpi_function)(const void *), void *gpi_cb_data); diff --git a/cocotb/share/lib/fli/FliCbHdl.cpp b/cocotb/share/lib/fli/FliCbHdl.cpp index 865c36bb..cb233534 100644 --- a/cocotb/share/lib/fli/FliCbHdl.cpp +++ b/cocotb/share/lib/fli/FliCbHdl.cpp @@ -49,9 +49,9 @@ int FliProcessCbHdl::cleanup_callback() } FliTimedCbHdl::FliTimedCbHdl(GpiImplInterface *impl, - uint64_t time_ps) : GpiCbHdl(impl), + uint64_t time) : GpiCbHdl(impl), FliProcessCbHdl(impl), - m_time_ps(time_ps) + m_time(time) { m_proc_hdl = mti_CreateProcessWithPriority(NULL, handle_fli_callback, (void *)this, MTI_PROC_IMMEDIATE); } @@ -59,10 +59,10 @@ FliTimedCbHdl::FliTimedCbHdl(GpiImplInterface *impl, int FliTimedCbHdl::arm_callback() { #if defined(__LP64__) || defined(_WIN64) - mti_ScheduleWakeup64(m_proc_hdl, static_cast(m_time_ps)); + mti_ScheduleWakeup64(m_proc_hdl, static_cast(m_time)); #else mtiTime64T m_time_union_ps; - MTI_TIME64_ASGN(m_time_union_ps, (mtiInt32T)((m_time_ps) >> 32), (mtiUInt32T)(m_time_ps)); + MTI_TIME64_ASGN(m_time_union_ps, (mtiInt32T)((m_time) >> 32), (mtiUInt32T)(m_time)); mti_ScheduleWakeup64(m_proc_hdl, m_time_union_ps); #endif m_sensitised = true; @@ -77,15 +77,15 @@ int FliTimedCbHdl::cleanup_callback() /* Issue #188: Work around for modelsim that is harmless to othes too, we tag the time as delete, let it fire then do not pass up */ - LOG_DEBUG("Not removing PRIMED timer %p", m_time_ps); + LOG_DEBUG("Not removing PRIMED timer %p", m_time); set_call_state(GPI_DELETE); return 0; case GPI_CALL: - LOG_DEBUG("Not removing CALL timer yet %p", m_time_ps); + LOG_DEBUG("Not removing CALL timer yet %p", m_time); set_call_state(GPI_DELETE); return 0; case GPI_DELETE: - LOG_DEBUG("Removing Postponed DELETE timer %p", m_time_ps); + LOG_DEBUG("Removing Postponed DELETE timer %p", m_time); break; default: break; diff --git a/cocotb/share/lib/fli/FliImpl.cpp b/cocotb/share/lib/fli/FliImpl.cpp index 06f07765..6a56b627 100644 --- a/cocotb/share/lib/fli/FliImpl.cpp +++ b/cocotb/share/lib/fli/FliImpl.cpp @@ -518,9 +518,9 @@ GpiObjHdl *FliImpl::get_root_handle(const char *name) } -GpiCbHdl *FliImpl::register_timed_callback(uint64_t time_ps) +GpiCbHdl *FliImpl::register_timed_callback(uint64_t time) { - FliTimedCbHdl *hdl = cache.get_timer(time_ps); + FliTimedCbHdl *hdl = cache.get_timer(time); if (hdl->arm_callback()) { delete(hdl); @@ -993,16 +993,16 @@ void FliIterator::populate_handle_list(FliIterator::OneToMany childType) } -FliTimedCbHdl* FliTimerCache::get_timer(uint64_t time_ps) +FliTimedCbHdl* FliTimerCache::get_timer(uint64_t time) { FliTimedCbHdl *hdl; if (!free_list.empty()) { hdl = free_list.front(); free_list.pop(); - hdl->reset_time(time_ps); + hdl->reset_time(time); } else { - hdl = new FliTimedCbHdl(impl, time_ps); + hdl = new FliTimedCbHdl(impl, time); } return hdl; diff --git a/cocotb/share/lib/fli/FliImpl.h b/cocotb/share/lib/fli/FliImpl.h index 24f7f09a..48d8f029 100644 --- a/cocotb/share/lib/fli/FliImpl.h +++ b/cocotb/share/lib/fli/FliImpl.h @@ -136,15 +136,15 @@ class FliShutdownCbHdl : public FliProcessCbHdl { class FliTimedCbHdl : public FliProcessCbHdl { public: - FliTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps); + FliTimedCbHdl(GpiImplInterface *impl, uint64_t time); int arm_callback() override; void reset_time(uint64_t new_time) { - m_time_ps = new_time; + m_time = new_time; } int cleanup_callback() override; private: - uint64_t m_time_ps; + uint64_t m_time; }; @@ -414,7 +414,7 @@ class FliTimerCache { public: FliTimerCache(FliImpl* impl) : impl(impl) { } - FliTimedCbHdl* get_timer(uint64_t time_ps); + FliTimedCbHdl* get_timer(uint64_t time); void put_timer(FliTimedCbHdl*); private: @@ -474,7 +474,7 @@ class FliImpl : public GpiImplInterface { GpiIterator *iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) override; /* Callback related, these may (will) return the same handle*/ - GpiCbHdl *register_timed_callback(uint64_t time_ps) override; + GpiCbHdl *register_timed_callback(uint64_t time) override; GpiCbHdl *register_readonly_callback() override; GpiCbHdl *register_nexttime_callback() override; GpiCbHdl *register_readwrite_callback() override; diff --git a/cocotb/share/lib/gpi/GpiCommon.cpp b/cocotb/share/lib/gpi/GpiCommon.cpp index 50bc8a91..44a033b5 100644 --- a/cocotb/share/lib/gpi/GpiCommon.cpp +++ b/cocotb/share/lib/gpi/GpiCommon.cpp @@ -573,9 +573,9 @@ gpi_cb_hdl gpi_register_value_change_callback(int (*gpi_function)(const void *), /* It should not matter which implementation we use for this so just pick the first one */ gpi_cb_hdl gpi_register_timed_callback(int (*gpi_function)(const void *), - void *gpi_cb_data, uint64_t time_ps) + void *gpi_cb_data, uint64_t time) { - GpiCbHdl *gpi_hdl = registered_impls[0]->register_timed_callback(time_ps); + GpiCbHdl *gpi_hdl = registered_impls[0]->register_timed_callback(time); if (!gpi_hdl) { LOG_ERROR("Failed to register a timed callback"); return NULL; diff --git a/cocotb/share/lib/gpi/gpi_priv.h b/cocotb/share/lib/gpi/gpi_priv.h index 7d7d16fb..10f771a0 100644 --- a/cocotb/share/lib/gpi/gpi_priv.h +++ b/cocotb/share/lib/gpi/gpi_priv.h @@ -254,7 +254,7 @@ class GPI_EXPORT GpiImplInterface { virtual GpiIterator *iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) = 0; /* Callback related, these may (will) return the same handle */ - virtual GpiCbHdl *register_timed_callback(uint64_t time_ps) = 0; + virtual GpiCbHdl *register_timed_callback(uint64_t time) = 0; virtual GpiCbHdl *register_readonly_callback() = 0; virtual GpiCbHdl *register_nexttime_callback() = 0; virtual GpiCbHdl *register_readwrite_callback() = 0; diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp index 61461ae5..b1715e5e 100644 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ b/cocotb/share/lib/simulator/simulatormodule.cpp @@ -463,7 +463,7 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) return NULL; } - uint64_t time_ps; + uint64_t time; { // Extract the time PyObject *pTime = PyTuple_GetItem(args, 0); long long pTime_as_longlong = PyLong_AsLongLong(pTime); @@ -473,7 +473,7 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) PyErr_SetString(PyExc_ValueError, "Timer value must be a positive integer"); return NULL; } else { - time_ps = (uint64_t)pTime_as_longlong; + time = (uint64_t)pTime_as_longlong; } } @@ -497,7 +497,7 @@ static PyObject *register_timed_callback(PyObject *self, PyObject *args) } gpi_cb_hdl hdl = gpi_register_timed_callback( - (gpi_function_t)handle_gpi_callback, cb_data, time_ps); + (gpi_function_t)handle_gpi_callback, cb_data, time); // Check success PyObject *rv = gpi_hdl_New(hdl); diff --git a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp index e7f52224..bdca50d7 100644 --- a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp +++ b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp @@ -871,11 +871,11 @@ int VhpiShutdownCbHdl::run_callback() { return 0; } -VhpiTimedCbHdl::VhpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : GpiCbHdl(impl), +VhpiTimedCbHdl::VhpiTimedCbHdl(GpiImplInterface *impl, uint64_t time) : GpiCbHdl(impl), VhpiCbHdl(impl) { - vhpi_time.high = (uint32_t)(time_ps>>32); - vhpi_time.low = (uint32_t)(time_ps); + vhpi_time.high = (uint32_t)(time>>32); + vhpi_time.low = (uint32_t)(time); cb_data.reason = vhpiCbAfterDelay; cb_data.time = &vhpi_time; diff --git a/cocotb/share/lib/vhpi/VhpiImpl.cpp b/cocotb/share/lib/vhpi/VhpiImpl.cpp index f003485d..f5b986aa 100644 --- a/cocotb/share/lib/vhpi/VhpiImpl.cpp +++ b/cocotb/share/lib/vhpi/VhpiImpl.cpp @@ -882,9 +882,9 @@ GpiIterator *VhpiImpl::iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t typ return new_iter; } -GpiCbHdl *VhpiImpl::register_timed_callback(uint64_t time_ps) +GpiCbHdl *VhpiImpl::register_timed_callback(uint64_t time) { - VhpiTimedCbHdl *hdl = new VhpiTimedCbHdl(this, time_ps); + VhpiTimedCbHdl *hdl = new VhpiTimedCbHdl(this, time); if (hdl->arm_callback()) { delete(hdl); diff --git a/cocotb/share/lib/vhpi/VhpiImpl.h b/cocotb/share/lib/vhpi/VhpiImpl.h index 2dfc4d70..5eaff19b 100644 --- a/cocotb/share/lib/vhpi/VhpiImpl.h +++ b/cocotb/share/lib/vhpi/VhpiImpl.h @@ -119,7 +119,7 @@ class VhpiValueCbHdl : public VhpiCbHdl, public GpiValueCbHdl { class VhpiTimedCbHdl : public VhpiCbHdl { public: - VhpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps); + VhpiTimedCbHdl(GpiImplInterface *impl, uint64_t time); int cleanup_callback() override; }; @@ -263,7 +263,7 @@ class VhpiImpl : public GpiImplInterface { GpiIterator *iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) override; /* Callback related, these may (will) return the same handle*/ - GpiCbHdl *register_timed_callback(uint64_t time_ps) override; + GpiCbHdl *register_timed_callback(uint64_t time) override; GpiCbHdl *register_readonly_callback() override; GpiCbHdl *register_nexttime_callback() override; GpiCbHdl *register_readwrite_callback() override; diff --git a/cocotb/share/lib/vpi/VpiCbHdl.cpp b/cocotb/share/lib/vpi/VpiCbHdl.cpp index 7ff279d2..5c628395 100644 --- a/cocotb/share/lib/vpi/VpiCbHdl.cpp +++ b/cocotb/share/lib/vpi/VpiCbHdl.cpp @@ -512,11 +512,11 @@ int VpiShutdownCbHdl::run_callback() { return 0; } -VpiTimedCbHdl::VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps) : GpiCbHdl(impl), +VpiTimedCbHdl::VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time) : GpiCbHdl(impl), VpiCbHdl(impl) { - vpi_time.high = (uint32_t)(time_ps>>32); - vpi_time.low = (uint32_t)(time_ps); + vpi_time.high = (uint32_t)(time>>32); + vpi_time.low = (uint32_t)(time); vpi_time.type = vpiSimTime; cb_data.reason = cbAfterDelay; diff --git a/cocotb/share/lib/vpi/VpiImpl.cpp b/cocotb/share/lib/vpi/VpiImpl.cpp index 52acbd00..6ad9df2b 100644 --- a/cocotb/share/lib/vpi/VpiImpl.cpp +++ b/cocotb/share/lib/vpi/VpiImpl.cpp @@ -569,9 +569,9 @@ GpiIterator *VpiImpl::iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type return new_iter; } -GpiCbHdl *VpiImpl::register_timed_callback(uint64_t time_ps) +GpiCbHdl *VpiImpl::register_timed_callback(uint64_t time) { - VpiTimedCbHdl *hdl = new VpiTimedCbHdl(this, time_ps); + VpiTimedCbHdl *hdl = new VpiTimedCbHdl(this, time); if (hdl->arm_callback()) { delete(hdl); diff --git a/cocotb/share/lib/vpi/VpiImpl.h b/cocotb/share/lib/vpi/VpiImpl.h index 08c23777..3f0d02aa 100644 --- a/cocotb/share/lib/vpi/VpiImpl.h +++ b/cocotb/share/lib/vpi/VpiImpl.h @@ -111,7 +111,7 @@ class VpiValueCbHdl : public VpiCbHdl, public GpiValueCbHdl { class VpiTimedCbHdl : public VpiCbHdl { public: - VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time_ps); + VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time); int cleanup_callback() override; }; @@ -256,7 +256,7 @@ class VpiImpl : public GpiImplInterface { GpiObjHdl *next_handle(GpiIterator *iter); /* Callback related, these may (will) return the same handle*/ - GpiCbHdl *register_timed_callback(uint64_t time_ps) override; + GpiCbHdl *register_timed_callback(uint64_t time) override; GpiCbHdl *register_readonly_callback() override; GpiCbHdl *register_nexttime_callback() override; GpiCbHdl *register_readwrite_callback() override; diff --git a/cocotb/triggers.py b/cocotb/triggers.py index a99d7f3e..833d4cae 100644 --- a/cocotb/triggers.py +++ b/cocotb/triggers.py @@ -158,12 +158,14 @@ def unprime(self): class Timer(GPITrigger): """Fires after the specified simulation time period has elapsed.""" - def __init__(self, time_ps, units="step"): + def __init__(self, time=None, units="step", *, time_ps=None): """ Args: - time_ps (numbers.Real or decimal.Decimal): The time value. + time (numbers.Real or decimal.Decimal): The time value. Note that despite the name this is not actually in picoseconds but depends on the *units* argument. + .. versionchanged:: 1.5.0 + Previously this argument was misleadingly called `time_ps`. units (str, optional): One of ``'step'``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``. When *units* is ``'step'``, @@ -210,8 +212,18 @@ def __init__(self, time_ps, units="step"): Using None as the the *units* argument is deprecated, use ``'step'`` instead. """ GPITrigger.__init__(self) - if time_ps <= 0: - if time_ps == 0: + if time_ps is not None: + if time is not None: + raise TypeError("Gave argument to both the 'time' and deprecated 'time_ps' parameter") + time = time_ps + warnings.warn( + "The parameter name 'time_ps' has been renamed to 'time'. Please update your invocation.", + DeprecationWarning, stacklevel=2) + else: + if time is None: + raise TypeError("Missing required argument 'time'") + if time <= 0: + if time == 0: warnings.warn("Timer setup with value 0, which might exhibit undefined behavior in some simulators", category=RuntimeWarning, stacklevel=2) @@ -221,8 +233,8 @@ def __init__(self, time_ps, units="step"): warnings.warn( 'Using units=None is deprecated, use units="step" instead.', DeprecationWarning, stacklevel=2) - units="step" # don't propagate deprecated value - self.sim_steps = get_sim_steps(time_ps, units) + units = "step" # don't propagate deprecated value + self.sim_steps = get_sim_steps(time, units) def prime(self, callback): """Register for a timed callback.""" diff --git a/tests/test_cases/test_cocotb/test_deprecated.py b/tests/test_cases/test_cocotb/test_deprecated.py index 2969a48f..da0c464a 100644 --- a/tests/test_cases/test_cocotb/test_deprecated.py +++ b/tests/test_cases/test_cocotb/test_deprecated.py @@ -138,3 +138,13 @@ async def t(): cocotb.test(expect_error=True)(t) with assert_deprecated(): cocotb.test(expect_error=False)(t) + + +@cocotb.test() +async def test_time_ps_deprecated(_): + with assert_deprecated(): + Timer(time_ps=7, units='ns') + with assert_raises(TypeError): + Timer(time=0, time_ps=7, units='ns') + with assert_raises(TypeError): + Timer(units='ps') From 5d4ae7af2aa357dbb7d12fcb078732fbf40c4bfc Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Wed, 13 Jan 2021 19:20:33 +0100 Subject: [PATCH 2645/2656] Improve documentation a bit in several places Refs #2354. --- cocotb/config.py | 2 ++ cocotb/share/makefiles/Makefile.sim | 12 ++++++-- documentation/source/building.rst | 30 +++++++++++++------- documentation/source/index.rst | 2 +- documentation/source/install.rst | 15 +++++----- documentation/source/troubleshooting.rst | 36 +++++++++++++++--------- 6 files changed, 64 insertions(+), 33 deletions(-) diff --git a/cocotb/config.py b/cocotb/config.py index 2f132745..a8547298 100755 --- a/cocotb/config.py +++ b/cocotb/config.py @@ -61,6 +61,8 @@ def help_vars_text(): # NOTE: make sure to keep "helpmsg" aligned with documentation/source/building.rst # Also keep it at 80 chars. helpmsg = textwrap.dedent("""\ + The following variables are environment variables: + Cocotb ------ TOPLEVEL Instance in the hierarchy to use as the DUT diff --git a/cocotb/share/makefiles/Makefile.sim b/cocotb/share/makefiles/Makefile.sim index 8bf27023..2b5b6f85 100644 --- a/cocotb/share/makefiles/Makefile.sim +++ b/cocotb/share/makefiles/Makefile.sim @@ -41,8 +41,15 @@ regression Run simulator when dependencies have changes clean Remove build and simulation artefacts help This help text +endef + +# NOTE: keep this at 80 chars. +define help_makevars = Variables ========= + +The following variables are makefile variables: + Makefile-based Test Scripts --------------------------- GUI Set this to 1 to enable the GUI mode in the simulator @@ -74,10 +81,11 @@ endef # this cannot be a regular target because of the way Makefile.$(SIM) is included ifeq ($(MAKECMDGOALS),help) $(info $(help_targets)) + $(info $(help_makevars)) # hack to get newlines in output, see https://stackoverflow.com/a/54539610 # NOTE: the output of the command must not include a '%' sign, otherwise the formatting will break - help_vars := $(subst %,${newline},$(shell cocotb-config --help-vars | tr \\n %)) - $(info ${help_vars}) + help_envvars := $(subst %,${newline},$(shell cocotb-config --help-vars | tr \\n %)) + $(info ${help_envvars}) # is there a cleaner way to exit here? $(error "Stopping after printing help") endif diff --git a/documentation/source/building.rst b/documentation/source/building.rst index 1e10df35..28023ce2 100644 --- a/documentation/source/building.rst +++ b/documentation/source/building.rst @@ -32,7 +32,10 @@ This allows for a rapid re-running of a simulator if none of the :term:`RTL` sou Variables ========= -The following sections document makefile variables and environment variables according to their owner/consumer. +The following sections document environment variables and makefile variables according to their owner/consumer. + +Of the environment variables, only :envvar:`MODULE` is mandatory to be set +(typically done in a makefile or run script), all others are optional. .. If you edit the following sections, please also update the "helpmsg" text in cocotb/config.py @@ -71,9 +74,10 @@ Cocotb Use this to override the default behavior of annotating cocotb output with ANSI color codes if the output is a terminal (``isatty()``). - ``COCOTB_ANSI_OUTPUT=1`` forces output to be ANSI regardless of the type of ``stdout`` or the presence of :envvar:`NO_COLOR`. - - ``COCOTB_ANSI_OUTPUT=0`` suppresses the ANSI output in the log messages + ``COCOTB_ANSI_OUTPUT=1`` + forces output to be ANSI-colored regardless of the type of ``stdout`` or the presence of :envvar:`NO_COLOR` + ``COCOTB_ANSI_OUTPUT=0`` + suppresses the ANSI color output in the log messages .. envvar:: NO_COLOR @@ -132,7 +136,7 @@ Cocotb .. envvar:: LIBPYTHON_LOC - The absolute path the Python library associated with the current Python installation; + The absolute path to the Python library associated with the current Python installation; i.e. ``libpython.so`` or ``python.dll`` on Windows. This is determined with ``cocotb-config --libpython`` in cocotb's makefiles. @@ -140,16 +144,15 @@ Cocotb Regression Manager ~~~~~~~~~~~~~~~~~~ -.. envvar:: COCOTB_PDB_ON_EXCEPTION - - If defined, cocotb will drop into the Python debugger (:mod:`pdb`) if a test fails with an exception. - .. envvar:: MODULE - The name of the module(s) to search for test functions. + The name of the Python module(s) to search for test functions - + if your tests are in a file called ``test_mydesign.py``, ``MODULE`` would be set to ``test_mydesign``. Multiple modules can be specified using a comma-separated list. All tests will be run from each specified module in order of the module's appearance in this list. + The is the only environment variable that is **required** for cocotb, all others are optional. + .. envvar:: TESTCASE The name of the test function(s) to run. If this variable is not defined cocotb @@ -177,6 +180,11 @@ Regression Manager .. deprecated:: 1.5 :class:`cocotb.hook` is deprecated, and in the future this variable will be ignored. +.. envvar:: COCOTB_PDB_ON_EXCEPTION + + If defined, cocotb will drop into the Python debugger (:mod:`pdb`) if a test fails with an exception. + See also the :ref:`troubleshooting-attaching-debugger-python` subsection of :ref:`troubleshooting-attaching-debugger`. + Scheduler ~~~~~~~~~ @@ -209,6 +217,8 @@ GPI Makefile-based Test Scripts --------------------------- +The following variables are makefile variables, not environment variables. + .. make:var:: GUI Set this to 1 to enable the GUI mode in the simulator (if supported). diff --git a/documentation/source/index.rst b/documentation/source/index.rst index 25836d5e..eab18ba9 100644 --- a/documentation/source/index.rst +++ b/documentation/source/index.rst @@ -59,7 +59,7 @@ How does cocotb work? A typical cocotb testbench requires no additional :term:`RTL` code. The Design Under Test (:term:`DUT`) is instantiated as the toplevel in the simulator without any wrapper code. cocotb drives stimulus onto the inputs to the :term:`DUT` (or further down the hierarchy) and monitors the outputs directly from Python. - +Note that cocotb can not instantiate :term:`HDL` blocks - your DUT must be complete. .. image:: diagrams/svg/cocotb_overview.svg diff --git a/documentation/source/install.rst b/documentation/source/install.rst index 28fde934..556fece8 100644 --- a/documentation/source/install.rst +++ b/documentation/source/install.rst @@ -6,7 +6,7 @@ Installation Using cocotb requires installation of prerequisites and installation of cocotb itself. In this document, we are assuming that you already have a -:ref:`supported simulator` available in ``PATH``. +:ref:`supported simulator` available in :envvar:`PATH`. .. _install-prerequisites: @@ -110,20 +110,21 @@ Installation of cocotb .. note:: If your user does not have permissions to install cocotb using the instructions above, - try adding the ``--user`` option to ``pip`` + try adding the :option:`--user` option to :command:`pip` (see `the pip documentation `_). .. warning:: - ``pip`` may belong to a different Python installation to what you expect. + :command:`pip` may belong to a different Python installation to what you expect. Use ``pip -V`` to check. - If this prints "(python 2.7)", use ``pip3`` or ``python3 -m pip`` in place of ``pip`` in the command shown. + If this prints "(python 2.7)", use :command:`pip3` or ``python3 -m pip`` in place of :command:`pip` in the command shown. If you want to install the **development version** of cocotb, :ref:`instructions are here`. -After installation, you should be able to execute ``cocotb-config``. -If it is not found, you need to append its location to the ``PATH`` environment variable. -This may happen when you use the ``--user`` option to ``pip``, in which case the location is documented :ref:`here`. +After installation, you should be able to execute :command:`cocotb-config`. +If it is not found, you need to append its location to the :envvar:`PATH` environment variable. +This may happen when you use the :option:`--user` option to :command:`pip`, +in which case the location is documented :ref:`here`. For more installation options, please see `our Wiki `_. diff --git a/documentation/source/troubleshooting.rst b/documentation/source/troubleshooting.rst index c779fad0..d82b0e5d 100644 --- a/documentation/source/troubleshooting.rst +++ b/documentation/source/troubleshooting.rst @@ -8,6 +8,9 @@ Simulation Hangs Did you directly call an ``async def`` function without using :keyword:`await`; or a :class:`~cocotb.coroutine` without using :keyword:`yield`? +If you want to exit cocotb and the simulator using :kbd:`Control-C` (the Unix signal ``SIGINT``) but this doesn't work, +you can try :kbd:`Control-\\` (the Unix signal ``SIGQUIT``). + Increasing Verbosity ==================== @@ -16,12 +19,16 @@ If things fail in the :term:`VPI`/:term:`VHPI`/:term:`FLI` area, check your simu increase its verbosity about what may be wrong. You can then set these options on the :command:`make` command line as :make:var:`COMPILE_ARGS`, :make:var:`SIM_ARGS` or :make:var:`EXTRA_ARGS` (see :doc:`building` for details). If things fail from within Python, or coroutines aren't being called when you expect, the -:make:var:`COCOTB_SCHEDULER_DEBUG` variable can be used to (greatly) increase the verbosity of the scheduler. +:envvar:`COCOTB_SCHEDULER_DEBUG` variable can be used to (greatly) increase the verbosity of the scheduler. + +.. _troubleshooting-attaching-debugger: Attaching a Debugger ==================== +.. _troubleshooting-attaching-debugger-c: + C and C++ --------- @@ -30,7 +37,9 @@ you can set the environment variable :envvar:`COCOTB_ATTACH` to a pause time val If set, cocotb will print the process ID (PID) to attach to and wait the specified time before actually letting the simulator run. -For the GNU debugger GDB, the command is :command:`attach `. +For the GNU debugger GDB, the command is ``attach ``. + +.. _troubleshooting-attaching-debugger-python: Python ------ @@ -38,7 +47,8 @@ Python When executing the Makefile to run a cocotb test, a Python shell interpreter is called from within the :term:`VPI`/:term:`VHPI`/:term:`FLI` library. Hence it is not possible to directly attach a Python debugger to the Python process being part of the simulator that uses the aforementioned library. -Using ``import pdb; pdb.set_trace()`` directly is also frequently not possible, due to the way that simulators interfere with stdin. +Using ``import pdb; pdb.set_trace()`` directly is also frequently not possible, +due to the way that simulators interfere with stdin. To successfully debug your Python code use the `remote_pdb`_ Python package to create a :command:`pdb` instance accessible via a TCP socket: @@ -93,7 +103,7 @@ it is strongly recommended to use an environment variable, i.e. and *not* ``make EXTRA_ARGS=...``. This is because in the case of the disrecommended ``make EXTRA_ARGS=...``, -if one of the involved Makefiles contains lines to assign (``=``) or append (``+=``) to ``EXTRA_ARGS`` internally, +if one of the involved Makefiles contains lines to assign (``=``) or append (``+=``) to :make:var:`EXTRA_ARGS` internally, such lines will be ignored. These lines are needed for the operation of cocotb however, and having them ignored is likely to lead to strange errors. @@ -112,7 +122,7 @@ It is usually an issue with your environment, but sometimes can occur when using Check your environment ---------------------- -To see if your environment is the issue, look at the value of the ``LD_LIBRARY_PATH`` environment variable. +To see if your environment is the issue, look at the value of the :envvar:`LD_LIBRARY_PATH` environment variable. Ensure the first path in the colon-deliminated list is the path to the libstdc++ that shipped with the compiler you used to build cocotb. .. code:: shell @@ -129,21 +139,21 @@ If the library you built cocotb with is not first, prepend that path to the list Check your simulator -------------------- -Sometimes, simulators modify the ``LD_LIBRARY_PATH`` so they point to the libraries that are shipped with instead of the system libraries. +Sometimes, simulators modify the :envvar:`LD_LIBRARY_PATH` so they point to the libraries that are shipped with instead of the system libraries. If you are running an old simulator, the packaged libraries may include a pre-C++11 libstdc++. -To see if your simulator is modifying the ``LD_LIBRARY_PATH``, open the simulator up to an internal console and obtain the environment variable. +To see if your simulator is modifying the :envvar:`LD_LIBRARY_PATH`, open the simulator up to an internal console and obtain the environment variable. -For example, with Mentor Questa and Cadence Xcelium, one could open a Tcl console and run the ``env`` command to list the current environment. -The ``LD_LIBRARY`` path should appear in the list. +For example, with Mentor Questa and Cadence Xcelium, one could open a Tcl console and run the :command:`env` command to list the current environment. +The :envvar:`LD_LIBRARY_PATH` should appear in the list. -If the simulator does modify the ``LD_LIBRARY_PATH``, refer to the simulator documentation on how to prevent or work around this issue. +If the simulator does modify the :envvar:`LD_LIBRARY_PATH`, refer to the simulator documentation on how to prevent or work around this issue. For example, Questa ships with GCC. Sometimes that version of GCC is old enough to not support C++11 (<4.8). -When you install cocotb, ``pip`` uses the system (or some other) compiler that supports C++11. -But when you try to run cocotb with the older Questa, it prepends the older libraries Questa ships with to ``LD_LIBRARY_PATH``. +When you install cocotb, :command:`pip` uses the system (or some other) compiler that supports C++11. +But when you try to run cocotb with the older Questa, it prepends the older libraries Questa ships with to :envvar:`LD_LIBRARY_PATH`. This causes the older libstdc++ Questa ships with to be loaded, resuling in the error message. -For Questa, you can use the ``-noautoldlibpath`` option to turn off the ``LD_LIBRARY_PATH`` prepend to resolve this issue. +For Questa, you can use the :option:`-noautoldlibpath` option to turn off the :envvar:`LD_LIBRARY_PATH` prepend to resolve this issue. Using cocotb with more than one Python installation From f65e08a652f5f34357bda733ca397f657a73a3c0 Mon Sep 17 00:00:00 2001 From: Marlon James Date: Wed, 20 Jan 2021 12:38:43 -0800 Subject: [PATCH 2646/2656] Remove calls to $display when using Verilator When the always blocks that display value changes to the bit variables are included, Verilator is unable to find a handle to the top-level module through VPI. --- tests/designs/sample_module/sample_module.sv | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/designs/sample_module/sample_module.sv b/tests/designs/sample_module/sample_module.sv index 8d3f461a..b57bb05e 100644 --- a/tests/designs/sample_module/sample_module.sv +++ b/tests/designs/sample_module/sample_module.sv @@ -155,6 +155,7 @@ initial begin mybits = '1; end +`ifndef VERILATOR always @(*) begin $display("%m: mybit has been updated, new value is %b", mybit); end @@ -164,5 +165,6 @@ end always @(*) begin $display("%m: mybits_uninitialized has been updated, new value is %b", mybits_uninitialized); end +`endif endmodule From eb40f270687915e0fce7b033de3f7412fd7510e4 Mon Sep 17 00:00:00 2001 From: Colin Marquardt Date: Mon, 18 Jan 2021 23:12:49 +0100 Subject: [PATCH 2647/2656] Use .value in asserts in example code This is not strictly necessary, but easier to understand for beginners. --- README.md | 2 +- examples/simple_dff/test_dff.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 990e43a0..833bee16 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ async def test_dff_simple(dut): 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 == val, "output q was incorrect on the {}th cycle".format(i) + assert dut.q.value == val, "output q was incorrect on the {}th cycle".format(i) ``` A simple Makefile: diff --git a/examples/simple_dff/test_dff.py b/examples/simple_dff/test_dff.py index d108ff4b..06747eb1 100644 --- a/examples/simple_dff/test_dff.py +++ b/examples/simple_dff/test_dff.py @@ -19,4 +19,4 @@ async def test_dff_simple(dut): 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 == val, "output q was incorrect on the {}th cycle".format(i) + assert dut.q.value == val, "output q was incorrect on the {}th cycle".format(i) From c46a42eb467c179878fd51d343b6af73fd42568a Mon Sep 17 00:00:00 2001 From: Marlon James Date: Mon, 25 Jan 2021 10:04:19 -0800 Subject: [PATCH 2648/2656] Update maintainer username --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index adb27e2c..390bd65e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -483,7 +483,7 @@ Most maintainers are experts in part of the cocotb codebase, and are primarily r - Kaleb Barrett (@ktbarrett) - Tomasz Hemperek (@themperek) -- Marlon James (@garmin-mjames) +- Marlon James (@marlonjames) - Colin Marquardt (@cmarqu) - Philipp Wagner (@imphil) - Eric Wieser (@eric-wieser) From a189ee6542523a0ac2ce32f881540225e21ba11e Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 12 Dec 2020 17:52:27 -0600 Subject: [PATCH 2649/2656] Delete all sources we know we won't keep --- .azp-private.yml | 27 - .github/PULL_REQUEST_TEMPLATE.md | 16 - .github/issue_template.md | 11 - .github/workflows/regression-tests.yml | 280 -- .github/workflows/stale.yml | 22 - .gitignore | 105 - .gitpod.Dockerfile | 55 - .gitpod.yml | 39 - .lgtm.yml | 4 - .pylintrc | 528 ---- .readthedocs.yml | 17 - .theia/settings.json | 4 - .travis.yml | 22 - CONTRIBUTING.md | 508 ---- MANIFEST.in | 4 - README.md | 118 - cocotb/ANSI.py | 90 - cocotb/__init__.py | 315 -- cocotb/_py_compat.py | 62 - cocotb/_sim_versions.py | 119 - cocotb/_vendor/__init__.py | 0 cocotb/_vendor/find_libpython/__init__.py | 383 --- cocotb/binary.py | 761 ----- cocotb/clock.py | 167 -- cocotb/config.py | 174 -- cocotb/decorators.py | 593 ---- cocotb/generators/__init__.py | 122 - cocotb/generators/bit.py | 97 - cocotb/generators/byte.py | 93 - cocotb/generators/packet.py | 88 - cocotb/handle.py | 1028 ------- cocotb/ipython_support.py | 88 - cocotb/log.py | 279 -- cocotb/memdebug.py | 38 - cocotb/outcomes.py | 56 - cocotb/regression.py | 769 ----- cocotb/result.py | 151 - cocotb/scheduler.py | 971 ------- cocotb/share/def/.gitignore | 2 - cocotb/share/def/README.md | 4 - cocotb/share/def/aldec.def | 61 - cocotb/share/def/ghdl.def | 43 - cocotb/share/def/icarus.def | 43 - cocotb/share/def/modelsim.def | 115 - cocotb/share/include/cocotb_utils.h | 81 - cocotb/share/include/embed.h | 55 - cocotb/share/include/exports.h | 19 - cocotb/share/include/gpi.h | 262 -- cocotb/share/include/gpi_logging.h | 216 -- cocotb/share/include/py_gpi_logging.h | 31 - cocotb/share/include/sv_vpi_user.h | 564 ---- cocotb/share/include/vhpi_user.h | 1282 --------- cocotb/share/include/vhpi_user_ext.h | 26 - cocotb/share/include/vpi_user.h | 1005 ------- cocotb/share/include/vpi_user_ext.h | 52 - cocotb/share/lib/embed/embed.cpp | 174 -- cocotb/share/lib/embed/gpi_embed.cpp | 365 --- cocotb/share/lib/fli/FliCbHdl.cpp | 223 -- cocotb/share/lib/fli/FliImpl.cpp | 1081 ------- cocotb/share/lib/fli/FliImpl.h | 506 ---- cocotb/share/lib/fli/FliObjHdl.cpp | 539 ---- cocotb/share/lib/gpi/GpiCbHdl.cpp | 178 -- cocotb/share/lib/gpi/GpiCommon.cpp | 644 ----- cocotb/share/lib/gpi/gpi_priv.h | 293 -- cocotb/share/lib/gpi_log/gpi_logging.cpp | 206 -- .../share/lib/py_gpi_log/py_gpi_logging.cpp | 169 -- .../share/lib/simulator/simulatormodule.cpp | 1329 --------- cocotb/share/lib/utils/cocotb_utils.cpp | 101 - cocotb/share/lib/verilator/verilator.cpp | 149 - cocotb/share/lib/vhpi/VhpiCbHdl.cpp | 1200 -------- cocotb/share/lib/vhpi/VhpiImpl.cpp | 1008 ------- cocotb/share/lib/vhpi/VhpiImpl.h | 288 -- cocotb/share/lib/vpi/VpiCbHdl.cpp | 902 ------ cocotb/share/lib/vpi/VpiImpl.cpp | 706 ----- cocotb/share/lib/vpi/VpiImpl.h | 279 -- cocotb/share/makefiles/Makefile.deprecations | 12 - cocotb/share/makefiles/Makefile.inc | 150 - cocotb/share/makefiles/Makefile.sim | 111 - .../makefiles/simulators/Makefile.activehdl | 72 - .../share/makefiles/simulators/Makefile.cvc | 94 - .../share/makefiles/simulators/Makefile.ghdl | 85 - .../makefiles/simulators/Makefile.icarus | 86 - .../share/makefiles/simulators/Makefile.ius | 114 - .../makefiles/simulators/Makefile.modelsim | 32 - .../makefiles/simulators/Makefile.questa | 141 - .../makefiles/simulators/Makefile.riviera | 157 - .../share/makefiles/simulators/Makefile.vcs | 96 - .../makefiles/simulators/Makefile.verilator | 77 - .../makefiles/simulators/Makefile.xcelium | 121 - cocotb/triggers.py | 932 ------ cocotb/utils.py | 629 ---- cocotb/wavedrom.py | 202 -- cocotb/xunit_reporter.py | 81 - cocotb_build_libs.py | 705 ----- documentation/.gitignore | 3 - documentation/Doxyfile | 2554 ----------------- documentation/README.md | 12 - documentation/conda.yml | 9 - documentation/requirements.txt | 14 - documentation/source/building.rst | 370 --- documentation/source/c_symbols.txt | 84 - documentation/source/conf.py | 346 --- documentation/source/contributors.rst | 19 - documentation/source/coroutines.rst | 234 -- documentation/source/diagrams/README.md | 7 - .../source/diagrams/svg/cocotb_overview.svg | 1 - .../diagrams/svg/endian_swapper_design.svg | 1 - .../diagrams/svg/endian_swapper_testbench.svg | 1 - .../source/diagrams/svg/example_regulator.svg | 3 - .../source/diagrams/svg/example_rescap.svg | 3 - .../source/diagrams/svg/hal_cosimulation.svg | 1 - .../source/diagrams/xml/cocotb_overview.xml | 1 - .../diagrams/xml/endian_swapper_design.xml | 1 - .../diagrams/xml/endian_swapper_testbench.xml | 1 - .../source/diagrams/xml/example_regulator.xml | 109 - .../source/diagrams/xml/example_rescap.xml | 88 - .../source/diagrams/xml/hal_cosimulation.xml | 1 - documentation/source/endian_swapper.rst | 202 -- documentation/source/examples.rst | 144 - documentation/source/extensions.rst | 122 - documentation/source/further_resources.rst | 7 - documentation/source/genindex.rst | 7 - documentation/source/glossary.rst | 71 - documentation/source/hal_cosimulation.rst | 178 -- documentation/source/index.rst | 205 -- documentation/source/install.rst | 130 - documentation/source/install_devel.rst | 58 - documentation/source/library_reference.rst | 436 --- documentation/source/library_reference_c.rst | 14 - .../source/newsfragments/1051.doc.rst | 3 - .../source/newsfragments/1502.doc.rst | 1 - .../source/newsfragments/1982.feature.rst | 2 - .../source/newsfragments/2006.feature.rst | 1 - .../source/newsfragments/2022.change.rst | 1 - .../source/newsfragments/2023.feature.rst | 2 - .../source/newsfragments/2028.feature.rst | 2 - .../source/newsfragments/2038.bugfix.rst | 1 - .../source/newsfragments/2045.bugfix.rst | 1 - .../source/newsfragments/2047.removal.rst | 1 - .../source/newsfragments/2049.removal.rst | 1 - .../source/newsfragments/2079.bugfix.rst | 8 - .../source/newsfragments/2091.change.rst | 1 - .../source/newsfragments/2105.change.rst | 1 - .../source/newsfragments/2117.removal.rst | 2 - .../source/newsfragments/2129.bugfix.rst | 1 - .../source/newsfragments/2133.removal.rst | 1 - .../source/newsfragments/2134.feature.rst | 1 - .../source/newsfragments/2171.feature.rst | 6 - .../source/newsfragments/2175.feature.rst | 1 - .../source/newsfragments/2177.removal.rst | 1 - .../source/newsfragments/2200.removal.rst | 1 - .../source/newsfragments/2201.removal.rst | 2 - .../source/newsfragments/2203.removal.rst | 1 - .../source/newsfragments/2232.removal.rst | 1 - .../source/newsfragments/2278.removal.rst | 4 - .../source/newsfragments/2322.feature.rst | 1 - .../source/newsfragments/913.bugfix.rst | 1 - .../source/newsfragments/913.change.rst | 2 - documentation/source/newsfragments/README.rst | 51 - documentation/source/py-modindex.rst | 8 - documentation/source/quickstart.rst | 280 -- documentation/source/regulator.png | Bin 18510 -> 0 bytes documentation/source/regulator.rst | 120 - documentation/source/release_notes.rst | 361 --- documentation/source/rescap.png | Bin 76553 -> 0 bytes documentation/source/rescap.rst | 130 - documentation/source/roadmap.rst | 18 - documentation/source/rotating_logger.rst | 28 - documentation/source/simulator_support.rst | 386 --- documentation/source/spelling_wordlist.txt | 91 - documentation/source/testbench_tools.rst | 218 -- documentation/source/triggers.rst | 71 - documentation/source/troubleshooting.rst | 173 -- documentation/source/writing_testbenches.rst | 28 - documentation/sphinxext/cairosvgconverter.py | 38 - examples/adder/hdl/adder.sv | 22 - examples/adder/hdl/adder.vhdl | 25 - examples/adder/model/__init__.py | 0 examples/adder/model/adder_model.py | 7 - examples/adder/tests/Makefile | 47 - examples/adder/tests/test_adder.py | 40 - examples/dff/hdl/dff.v | 41 - examples/dff/hdl/dff.vhdl | 42 - examples/dff/tests/Makefile | 33 - examples/dff/tests/dff_cocotb.py | 160 -- .../hdl/matrix_multiplier.sv | 84 - .../hdl/matrix_multiplier.vhd | 75 - .../hdl/matrix_multiplier_pkg.vhd | 13 - examples/matrix_multiplier/tests/Makefile | 75 - .../tests/test_matrix_multiplier.py | 158 - examples/mean/hdl/mean.sv | 82 - examples/mean/hdl/mean.vhd | 68 - examples/mean/hdl/mean_pkg.vhd | 32 - examples/mean/hdl/mean_sv.sv | 38 - examples/mean/tests/Makefile | 28 - examples/mean/tests/test_mean.py | 128 - examples/mean/tests/verilator_waiver.vlt | 7 - examples/mean/tests_mixedlang/Makefile | 37 - examples/mixed_language/hdl/toplevel.sv | 104 - examples/mixed_language/hdl/toplevel.vhdl | 74 - examples/mixed_language/tests/Makefile | 43 - .../tests/test_mixed_language.py | 26 - examples/mixed_signal/.gitignore | 1 - .../mixed_signal/hdl/analog_probe_cadence.sv | 38 - .../mixed_signal/hdl/analog_probe_synopsys.sv | 22 - examples/mixed_signal/hdl/capacitor.vams | 20 - .../mixed_signal/hdl/nettypes_pkg_cadence.sv | 10 - .../mixed_signal/hdl/nettypes_pkg_synopsys.sv | 13 - examples/mixed_signal/hdl/regulator.sv | 28 - examples/mixed_signal/hdl/regulator.vams | 29 - .../mixed_signal/hdl/regulator_block.vams | 29 - examples/mixed_signal/hdl/rescap.sv | 25 - examples/mixed_signal/hdl/resistor.vams | 18 - examples/mixed_signal/hdl/tb_regulator.sv | 25 - examples/mixed_signal/hdl/tb_rescap.sv | 25 - examples/mixed_signal/tests/Makefile | 89 - examples/mixed_signal/tests/run.scs | 11 - .../mixed_signal/tests/test_regulator_plot.py | 69 - .../mixed_signal/tests/test_regulator_trim.py | 123 - examples/mixed_signal/tests/test_rescap.py | 150 - .../tests/test_rescap_minimalist.py | 27 - examples/mixed_signal/tests/vcsAD.init | 4 - examples/simple_dff/.gitignore | 45 - examples/simple_dff/Makefile | 15 - examples/simple_dff/dff.sv | 15 - examples/simple_dff/dff.vhdl | 21 - examples/simple_dff/test_dff.py | 22 - makefiles/Makefile.inc | 8 - makefiles/Makefile.sim | 8 - pyproject.toml | 39 - setup.cfg | 39 - setup.py | 138 - tests/designs/array_module/Makefile | 53 - tests/designs/array_module/array_module.sv | 204 -- tests/designs/array_module/array_module.vhd | 166 -- .../array_module/array_module_aldec.vhd | 170 -- .../array_module/array_module_pack.vhd | 54 - tests/designs/basic_hierarchy_module/Makefile | 25 - .../basic_hierarchy_module.v | 63 - tests/designs/multi_dimension_array/Makefile | 51 - .../multi_dimension_array/cocotb_array.sv | 101 - .../multi_dimension_array/cocotb_array_pkg.sv | 6 - .../cocotb_struct_pkg.sv | 35 - tests/designs/plusargs_module/Makefile | 50 - tests/designs/plusargs_module/tb_top.v | 42 - tests/designs/plusargs_module/tb_top.vhd | 17 - tests/designs/sample_module/Makefile | 50 - tests/designs/sample_module/sample_module.sv | 170 -- .../designs/sample_module/sample_module.vhdl | 154 - .../sample_module/sample_module_1.vhdl | 36 - .../sample_module/sample_module_pack.vhdl | 45 - tests/designs/uart2bus/Makefile | 41 - tests/designs/uart2bus/README | 7 - .../designs/uart2bus/top/verilog_toplevel.sv | 58 - tests/designs/uart2bus/top/vhdl_toplevel.vhdl | 0 tests/designs/uart2bus/verilog/baud_gen.v | 56 - tests/designs/uart2bus/verilog/uart2bus_top.v | 83 - tests/designs/uart2bus/verilog/uart_parser.v | 667 ----- tests/designs/uart2bus/verilog/uart_rx.v | 116 - tests/designs/uart2bus/verilog/uart_top.v | 66 - tests/designs/uart2bus/verilog/uart_tx.v | 94 - tests/designs/uart2bus/vhdl/baudGen.vhd | 48 - tests/designs/uart2bus/vhdl/uart2BusTop.vhd | 77 - .../designs/uart2bus/vhdl/uart2BusTop_pkg.vhd | 90 - tests/designs/uart2bus/vhdl/uartParser.vhd | 576 ---- tests/designs/uart2bus/vhdl/uartRx.vhd | 111 - tests/designs/uart2bus/vhdl/uartTop.vhd | 63 - tests/designs/uart2bus/vhdl/uartTx.vhd | 96 - tests/designs/vhdl_configurations/Makefile | 39 - .../vhdl_configurations/configurations.vhd | 15 - tests/designs/vhdl_configurations/dut.vhd | 42 - .../designs/vhdl_configurations/testbench.sv | 15 - .../designs/vhdl_configurations/testbench.vhd | 28 - tests/designs/viterbi_decoder_axi4s/Makefile | 70 - .../designs/viterbi_decoder_axi4s/gpl-2.0.txt | 339 --- .../packages/pkg_components.vhd | 194 -- .../packages/pkg_helper.vhd | 58 - .../packages/pkg_param.vhd | 78 - .../packages/pkg_param_derived.vhd | 73 - .../packages/pkg_trellis.vhd | 185 -- .../packages/pkg_types.vhd | 37 - .../designs/viterbi_decoder_axi4s/src/acs.vhd | 134 - .../src/axi4s_buffer.vhd | 130 - .../src/branch_distance.vhd | 111 - .../viterbi_decoder_axi4s/src/dec_viterbi.vhd | 399 --- .../src/generic_sp_ram.vhd | 90 - .../viterbi_decoder_axi4s/src/ram_ctrl.vhd | 474 --- .../viterbi_decoder_axi4s/src/recursion.vhd | 96 - .../viterbi_decoder_axi4s/src/reorder.vhd | 129 - .../viterbi_decoder_axi4s/src/traceback.vhd | 101 - tests/pytest/test_binary_value.py | 319 -- tests/pytest/test_utils.py | 77 - tests/test_cases/issue_120/Makefile | 33 - tests/test_cases/issue_120/issue_120.py | 44 - tests/test_cases/issue_1279/Makefile | 7 - tests/test_cases/issue_1279/issue_1279.py | 23 - tests/test_cases/issue_142/Makefile | 33 - tests/test_cases/issue_142/issue_142.py | 29 - tests/test_cases/issue_253/Makefile | 67 - tests/test_cases/issue_253/issue_253.py | 28 - tests/test_cases/issue_330/Makefile | 33 - tests/test_cases/issue_330/issue_330.py | 35 - tests/test_cases/issue_348/Makefile | 31 - tests/test_cases/issue_348/issue_348.py | 69 - tests/test_cases/issue_588/Makefile | 31 - tests/test_cases/issue_588/issue_588.py | 29 - tests/test_cases/issue_768_a/Makefile | 3 - tests/test_cases/issue_768_a/issue_768.py | 21 - tests/test_cases/issue_768_b/Makefile | 3 - tests/test_cases/issue_768_b/issue_768.py | 21 - tests/test_cases/issue_857/Makefile | 31 - tests/test_cases/issue_857/issue_857.py | 28 - tests/test_cases/issue_892/Makefile | 33 - tests/test_cases/issue_892/issue_892.py | 14 - tests/test_cases/issue_893/Makefile | 33 - tests/test_cases/issue_893/issue_893.py | 12 - tests/test_cases/issue_957/Makefile | 3 - tests/test_cases/issue_957/issue_957.py | 16 - tests/test_cases/test_array/Makefile | 50 - tests/test_cases/test_array/test_array.py | 508 ---- tests/test_cases/test_array_simple/Makefile | 3 - .../test_array_simple/test_array_simple.py | 211 -- tests/test_cases/test_cocotb/Makefile | 26 - tests/test_cases/test_cocotb/common.py | 50 - .../test_cocotb/test_async_coroutines.py | 160 -- .../test_cocotb/test_async_generators.py | 46 - tests/test_cases/test_cocotb/test_clock.py | 70 - .../test_concurrency_primitives.py | 151 - .../test_cases/test_cocotb/test_deprecated.py | 150 - .../test_cocotb/test_edge_triggers.py | 235 -- .../test_cocotb/test_generator_coroutines.py | 217 -- tests/test_cases/test_cocotb/test_handle.py | 318 -- tests/test_cases/test_cocotb/test_logging.py | 77 - tests/test_cases/test_cocotb/test_pytest.py | 22 - .../test_cases/test_cocotb/test_scheduler.py | 428 --- .../test_synchronization_primitives.py | 109 - .../test_cocotb/test_testfactory.py | 30 - tests/test_cases/test_cocotb/test_tests.py | 108 - .../test_cocotb/test_timing_triggers.py | 251 -- tests/test_cases/test_compare/Makefile | 7 - tests/test_cases/test_compare/test_compare.py | 85 - tests/test_cases/test_configuration/Makefile | 29 - .../test_configuration/test_configurations.py | 40 - tests/test_cases/test_discovery/Makefile | 35 - .../test_discovery/test_discovery.py | 495 ---- .../test_discovery/test_vhdl_block.py | 42 - .../test_discovery/test_vhdl_indexed_name.py | 43 - tests/test_cases/test_exit_error/Makefile | 35 - tests/test_cases/test_exit_error/test_exit.py | 9 - tests/test_cases/test_external/Makefile | 33 - .../test_cases/test_external/test_external.py | 383 --- tests/test_cases/test_failure/Makefile | 14 - tests/test_cases/test_failure/test_failure.py | 15 - tests/test_cases/test_fatal/Makefile | 28 - tests/test_cases/test_fatal/fatal.sv | 16 - tests/test_cases/test_fatal/fatal.vhd | 18 - tests/test_cases/test_fatal/test_fatal.py | 15 - tests/test_cases/test_force_release/Makefile | 7 - .../test_force_release/test_force_release.py | 34 - .../test_iteration_mixedlang/Makefile | 29 - .../test_iteration.py | 104 - .../test_iteration_verilog/Makefile | 49 - .../test_iteration_es.py | 72 - tests/test_cases/test_iteration_vhdl/Makefile | 37 - .../test_iteration_vhdl/test_iteration.py | 130 - .../test_cases/test_module_var_empty/Makefile | 5 - .../test_cases/test_module_var_messy/Makefile | 5 - .../test_module_var_messy/test_nothing.py | 1 - .../test_module_without_tests/Makefile | 5 - .../test_module_without_tests/test_nothing.py | 1 - .../test_multi_dimension_array/Makefile | 50 - .../test_cocotb_array.py | 212 -- tests/test_cases/test_plusargs/Makefile | 34 - tests/test_cases/test_plusargs/plusargs.py | 44 - tests/test_cases/test_skipped/Makefile | 22 - tests/test_cases/test_skipped/test_skipped.py | 15 - tests/test_cases/test_verilog_access/Makefile | 29 - .../test_verilog_access.py | 67 - tests/test_cases/test_vhdl_access/Makefile | 29 - .../test_vhdl_access/test_vhdl_access.py | 111 - tox.ini | 83 - 381 files changed, 49808 deletions(-) delete mode 100644 .azp-private.yml delete mode 100644 .github/PULL_REQUEST_TEMPLATE.md delete mode 100644 .github/issue_template.md delete mode 100644 .github/workflows/regression-tests.yml delete mode 100644 .github/workflows/stale.yml delete mode 100644 .gitignore delete mode 100644 .gitpod.Dockerfile delete mode 100644 .gitpod.yml delete mode 100644 .lgtm.yml delete mode 100644 .pylintrc delete mode 100644 .readthedocs.yml delete mode 100644 .theia/settings.json delete mode 100644 .travis.yml delete mode 100644 CONTRIBUTING.md delete mode 100644 MANIFEST.in delete mode 100644 README.md delete mode 100644 cocotb/ANSI.py delete mode 100644 cocotb/__init__.py delete mode 100644 cocotb/_py_compat.py delete mode 100644 cocotb/_sim_versions.py delete mode 100644 cocotb/_vendor/__init__.py delete mode 100644 cocotb/_vendor/find_libpython/__init__.py delete mode 100755 cocotb/binary.py delete mode 100644 cocotb/clock.py delete mode 100755 cocotb/config.py delete mode 100644 cocotb/decorators.py delete mode 100644 cocotb/generators/__init__.py delete mode 100644 cocotb/generators/bit.py delete mode 100755 cocotb/generators/byte.py delete mode 100644 cocotb/generators/packet.py delete mode 100755 cocotb/handle.py delete mode 100644 cocotb/ipython_support.py delete mode 100644 cocotb/log.py delete mode 100644 cocotb/memdebug.py delete mode 100644 cocotb/outcomes.py delete mode 100644 cocotb/regression.py delete mode 100644 cocotb/result.py delete mode 100755 cocotb/scheduler.py delete mode 100644 cocotb/share/def/.gitignore delete mode 100644 cocotb/share/def/README.md delete mode 100644 cocotb/share/def/aldec.def delete mode 100644 cocotb/share/def/ghdl.def delete mode 100644 cocotb/share/def/icarus.def delete mode 100644 cocotb/share/def/modelsim.def delete mode 100644 cocotb/share/include/cocotb_utils.h delete mode 100644 cocotb/share/include/embed.h delete mode 100644 cocotb/share/include/exports.h delete mode 100644 cocotb/share/include/gpi.h delete mode 100644 cocotb/share/include/gpi_logging.h delete mode 100644 cocotb/share/include/py_gpi_logging.h delete mode 100644 cocotb/share/include/sv_vpi_user.h delete mode 100644 cocotb/share/include/vhpi_user.h delete mode 100644 cocotb/share/include/vhpi_user_ext.h delete mode 100644 cocotb/share/include/vpi_user.h delete mode 100644 cocotb/share/include/vpi_user_ext.h delete mode 100644 cocotb/share/lib/embed/embed.cpp delete mode 100644 cocotb/share/lib/embed/gpi_embed.cpp delete mode 100644 cocotb/share/lib/fli/FliCbHdl.cpp delete mode 100644 cocotb/share/lib/fli/FliImpl.cpp delete mode 100644 cocotb/share/lib/fli/FliImpl.h delete mode 100644 cocotb/share/lib/fli/FliObjHdl.cpp delete mode 100644 cocotb/share/lib/gpi/GpiCbHdl.cpp delete mode 100644 cocotb/share/lib/gpi/GpiCommon.cpp delete mode 100644 cocotb/share/lib/gpi/gpi_priv.h delete mode 100644 cocotb/share/lib/gpi_log/gpi_logging.cpp delete mode 100644 cocotb/share/lib/py_gpi_log/py_gpi_logging.cpp delete mode 100644 cocotb/share/lib/simulator/simulatormodule.cpp delete mode 100644 cocotb/share/lib/utils/cocotb_utils.cpp delete mode 100644 cocotb/share/lib/verilator/verilator.cpp delete mode 100644 cocotb/share/lib/vhpi/VhpiCbHdl.cpp delete mode 100644 cocotb/share/lib/vhpi/VhpiImpl.cpp delete mode 100644 cocotb/share/lib/vhpi/VhpiImpl.h delete mode 100644 cocotb/share/lib/vpi/VpiCbHdl.cpp delete mode 100644 cocotb/share/lib/vpi/VpiImpl.cpp delete mode 100644 cocotb/share/lib/vpi/VpiImpl.h delete mode 100644 cocotb/share/makefiles/Makefile.deprecations delete mode 100644 cocotb/share/makefiles/Makefile.inc delete mode 100644 cocotb/share/makefiles/Makefile.sim delete mode 100644 cocotb/share/makefiles/simulators/Makefile.activehdl delete mode 100644 cocotb/share/makefiles/simulators/Makefile.cvc delete mode 100644 cocotb/share/makefiles/simulators/Makefile.ghdl delete mode 100644 cocotb/share/makefiles/simulators/Makefile.icarus delete mode 100644 cocotb/share/makefiles/simulators/Makefile.ius delete mode 100644 cocotb/share/makefiles/simulators/Makefile.modelsim delete mode 100644 cocotb/share/makefiles/simulators/Makefile.questa delete mode 100644 cocotb/share/makefiles/simulators/Makefile.riviera delete mode 100644 cocotb/share/makefiles/simulators/Makefile.vcs delete mode 100644 cocotb/share/makefiles/simulators/Makefile.verilator delete mode 100644 cocotb/share/makefiles/simulators/Makefile.xcelium delete mode 100644 cocotb/triggers.py delete mode 100644 cocotb/utils.py delete mode 100644 cocotb/wavedrom.py delete mode 100644 cocotb/xunit_reporter.py delete mode 100755 cocotb_build_libs.py delete mode 100644 documentation/.gitignore delete mode 100644 documentation/Doxyfile delete mode 100644 documentation/README.md delete mode 100644 documentation/conda.yml delete mode 100644 documentation/requirements.txt delete mode 100644 documentation/source/building.rst delete mode 100644 documentation/source/c_symbols.txt delete mode 100644 documentation/source/conf.py delete mode 100644 documentation/source/contributors.rst delete mode 100644 documentation/source/coroutines.rst delete mode 100644 documentation/source/diagrams/README.md delete mode 100644 documentation/source/diagrams/svg/cocotb_overview.svg delete mode 100644 documentation/source/diagrams/svg/endian_swapper_design.svg delete mode 100644 documentation/source/diagrams/svg/endian_swapper_testbench.svg delete mode 100644 documentation/source/diagrams/svg/example_regulator.svg delete mode 100644 documentation/source/diagrams/svg/example_rescap.svg delete mode 100644 documentation/source/diagrams/svg/hal_cosimulation.svg delete mode 100644 documentation/source/diagrams/xml/cocotb_overview.xml delete mode 100644 documentation/source/diagrams/xml/endian_swapper_design.xml delete mode 100644 documentation/source/diagrams/xml/endian_swapper_testbench.xml delete mode 100644 documentation/source/diagrams/xml/example_regulator.xml delete mode 100644 documentation/source/diagrams/xml/example_rescap.xml delete mode 100644 documentation/source/diagrams/xml/hal_cosimulation.xml delete mode 100644 documentation/source/endian_swapper.rst delete mode 100644 documentation/source/examples.rst delete mode 100644 documentation/source/extensions.rst delete mode 100644 documentation/source/further_resources.rst delete mode 100644 documentation/source/genindex.rst delete mode 100644 documentation/source/glossary.rst delete mode 100644 documentation/source/hal_cosimulation.rst delete mode 100644 documentation/source/index.rst delete mode 100644 documentation/source/install.rst delete mode 100644 documentation/source/install_devel.rst delete mode 100644 documentation/source/library_reference.rst delete mode 100644 documentation/source/library_reference_c.rst delete mode 100644 documentation/source/newsfragments/1051.doc.rst delete mode 100644 documentation/source/newsfragments/1502.doc.rst delete mode 100644 documentation/source/newsfragments/1982.feature.rst delete mode 100644 documentation/source/newsfragments/2006.feature.rst delete mode 100644 documentation/source/newsfragments/2022.change.rst delete mode 100644 documentation/source/newsfragments/2023.feature.rst delete mode 100644 documentation/source/newsfragments/2028.feature.rst delete mode 100644 documentation/source/newsfragments/2038.bugfix.rst delete mode 100644 documentation/source/newsfragments/2045.bugfix.rst delete mode 100644 documentation/source/newsfragments/2047.removal.rst delete mode 100644 documentation/source/newsfragments/2049.removal.rst delete mode 100644 documentation/source/newsfragments/2079.bugfix.rst delete mode 100644 documentation/source/newsfragments/2091.change.rst delete mode 100644 documentation/source/newsfragments/2105.change.rst delete mode 100644 documentation/source/newsfragments/2117.removal.rst delete mode 100644 documentation/source/newsfragments/2129.bugfix.rst delete mode 100644 documentation/source/newsfragments/2133.removal.rst delete mode 100644 documentation/source/newsfragments/2134.feature.rst delete mode 100644 documentation/source/newsfragments/2171.feature.rst delete mode 100644 documentation/source/newsfragments/2175.feature.rst delete mode 100644 documentation/source/newsfragments/2177.removal.rst delete mode 100644 documentation/source/newsfragments/2200.removal.rst delete mode 100644 documentation/source/newsfragments/2201.removal.rst delete mode 100644 documentation/source/newsfragments/2203.removal.rst delete mode 100644 documentation/source/newsfragments/2232.removal.rst delete mode 100644 documentation/source/newsfragments/2278.removal.rst delete mode 100644 documentation/source/newsfragments/2322.feature.rst delete mode 100644 documentation/source/newsfragments/913.bugfix.rst delete mode 100644 documentation/source/newsfragments/913.change.rst delete mode 100644 documentation/source/newsfragments/README.rst delete mode 100644 documentation/source/py-modindex.rst delete mode 100644 documentation/source/quickstart.rst delete mode 100644 documentation/source/regulator.png delete mode 100644 documentation/source/regulator.rst delete mode 100644 documentation/source/release_notes.rst delete mode 100644 documentation/source/rescap.png delete mode 100644 documentation/source/rescap.rst delete mode 100644 documentation/source/roadmap.rst delete mode 100644 documentation/source/rotating_logger.rst delete mode 100644 documentation/source/simulator_support.rst delete mode 100644 documentation/source/spelling_wordlist.txt delete mode 100644 documentation/source/testbench_tools.rst delete mode 100644 documentation/source/triggers.rst delete mode 100644 documentation/source/troubleshooting.rst delete mode 100644 documentation/source/writing_testbenches.rst delete mode 100644 documentation/sphinxext/cairosvgconverter.py delete mode 100644 examples/adder/hdl/adder.sv delete mode 100644 examples/adder/hdl/adder.vhdl delete mode 100644 examples/adder/model/__init__.py delete mode 100644 examples/adder/model/adder_model.py delete mode 100644 examples/adder/tests/Makefile delete mode 100644 examples/adder/tests/test_adder.py delete mode 100644 examples/dff/hdl/dff.v delete mode 100644 examples/dff/hdl/dff.vhdl delete mode 100755 examples/dff/tests/Makefile delete mode 100644 examples/dff/tests/dff_cocotb.py delete mode 100644 examples/matrix_multiplier/hdl/matrix_multiplier.sv delete mode 100644 examples/matrix_multiplier/hdl/matrix_multiplier.vhd delete mode 100644 examples/matrix_multiplier/hdl/matrix_multiplier_pkg.vhd delete mode 100644 examples/matrix_multiplier/tests/Makefile delete mode 100644 examples/matrix_multiplier/tests/test_matrix_multiplier.py delete mode 100644 examples/mean/hdl/mean.sv delete mode 100644 examples/mean/hdl/mean.vhd delete mode 100644 examples/mean/hdl/mean_pkg.vhd delete mode 100644 examples/mean/hdl/mean_sv.sv delete mode 100644 examples/mean/tests/Makefile delete mode 100644 examples/mean/tests/test_mean.py delete mode 100644 examples/mean/tests/verilator_waiver.vlt delete mode 100644 examples/mean/tests_mixedlang/Makefile delete mode 100644 examples/mixed_language/hdl/toplevel.sv delete mode 100644 examples/mixed_language/hdl/toplevel.vhdl delete mode 100644 examples/mixed_language/tests/Makefile delete mode 100644 examples/mixed_language/tests/test_mixed_language.py delete mode 100644 examples/mixed_signal/.gitignore delete mode 100644 examples/mixed_signal/hdl/analog_probe_cadence.sv delete mode 100644 examples/mixed_signal/hdl/analog_probe_synopsys.sv delete mode 100644 examples/mixed_signal/hdl/capacitor.vams delete mode 100644 examples/mixed_signal/hdl/nettypes_pkg_cadence.sv delete mode 100644 examples/mixed_signal/hdl/nettypes_pkg_synopsys.sv delete mode 100644 examples/mixed_signal/hdl/regulator.sv delete mode 100644 examples/mixed_signal/hdl/regulator.vams delete mode 100644 examples/mixed_signal/hdl/regulator_block.vams delete mode 100644 examples/mixed_signal/hdl/rescap.sv delete mode 100644 examples/mixed_signal/hdl/resistor.vams delete mode 100644 examples/mixed_signal/hdl/tb_regulator.sv delete mode 100644 examples/mixed_signal/hdl/tb_rescap.sv delete mode 100644 examples/mixed_signal/tests/Makefile delete mode 100644 examples/mixed_signal/tests/run.scs delete mode 100644 examples/mixed_signal/tests/test_regulator_plot.py delete mode 100644 examples/mixed_signal/tests/test_regulator_trim.py delete mode 100644 examples/mixed_signal/tests/test_rescap.py delete mode 100644 examples/mixed_signal/tests/test_rescap_minimalist.py delete mode 100644 examples/mixed_signal/tests/vcsAD.init delete mode 100644 examples/simple_dff/.gitignore delete mode 100644 examples/simple_dff/Makefile delete mode 100644 examples/simple_dff/dff.sv delete mode 100644 examples/simple_dff/dff.vhdl delete mode 100644 examples/simple_dff/test_dff.py delete mode 100644 makefiles/Makefile.inc delete mode 100644 makefiles/Makefile.sim delete mode 100644 pyproject.toml delete mode 100644 setup.cfg delete mode 100755 setup.py delete mode 100644 tests/designs/array_module/Makefile delete mode 100644 tests/designs/array_module/array_module.sv delete mode 100644 tests/designs/array_module/array_module.vhd delete mode 100644 tests/designs/array_module/array_module_aldec.vhd delete mode 100644 tests/designs/array_module/array_module_pack.vhd delete mode 100644 tests/designs/basic_hierarchy_module/Makefile delete mode 100644 tests/designs/basic_hierarchy_module/basic_hierarchy_module.v delete mode 100644 tests/designs/multi_dimension_array/Makefile delete mode 100644 tests/designs/multi_dimension_array/cocotb_array.sv delete mode 100644 tests/designs/multi_dimension_array/cocotb_array_pkg.sv delete mode 100644 tests/designs/multi_dimension_array/cocotb_struct_pkg.sv delete mode 100644 tests/designs/plusargs_module/Makefile delete mode 100644 tests/designs/plusargs_module/tb_top.v delete mode 100644 tests/designs/plusargs_module/tb_top.vhd delete mode 100644 tests/designs/sample_module/Makefile delete mode 100644 tests/designs/sample_module/sample_module.sv delete mode 100644 tests/designs/sample_module/sample_module.vhdl delete mode 100644 tests/designs/sample_module/sample_module_1.vhdl delete mode 100644 tests/designs/sample_module/sample_module_pack.vhdl delete mode 100644 tests/designs/uart2bus/Makefile delete mode 100644 tests/designs/uart2bus/README delete mode 100644 tests/designs/uart2bus/top/verilog_toplevel.sv delete mode 100644 tests/designs/uart2bus/top/vhdl_toplevel.vhdl delete mode 100644 tests/designs/uart2bus/verilog/baud_gen.v delete mode 100644 tests/designs/uart2bus/verilog/uart2bus_top.v delete mode 100644 tests/designs/uart2bus/verilog/uart_parser.v delete mode 100644 tests/designs/uart2bus/verilog/uart_rx.v delete mode 100644 tests/designs/uart2bus/verilog/uart_top.v delete mode 100644 tests/designs/uart2bus/verilog/uart_tx.v delete mode 100644 tests/designs/uart2bus/vhdl/baudGen.vhd delete mode 100644 tests/designs/uart2bus/vhdl/uart2BusTop.vhd delete mode 100644 tests/designs/uart2bus/vhdl/uart2BusTop_pkg.vhd delete mode 100644 tests/designs/uart2bus/vhdl/uartParser.vhd delete mode 100644 tests/designs/uart2bus/vhdl/uartRx.vhd delete mode 100644 tests/designs/uart2bus/vhdl/uartTop.vhd delete mode 100644 tests/designs/uart2bus/vhdl/uartTx.vhd delete mode 100644 tests/designs/vhdl_configurations/Makefile delete mode 100644 tests/designs/vhdl_configurations/configurations.vhd delete mode 100644 tests/designs/vhdl_configurations/dut.vhd delete mode 100644 tests/designs/vhdl_configurations/testbench.sv delete mode 100644 tests/designs/vhdl_configurations/testbench.vhd delete mode 100644 tests/designs/viterbi_decoder_axi4s/Makefile delete mode 100644 tests/designs/viterbi_decoder_axi4s/gpl-2.0.txt delete mode 100644 tests/designs/viterbi_decoder_axi4s/packages/pkg_components.vhd delete mode 100644 tests/designs/viterbi_decoder_axi4s/packages/pkg_helper.vhd delete mode 100644 tests/designs/viterbi_decoder_axi4s/packages/pkg_param.vhd delete mode 100644 tests/designs/viterbi_decoder_axi4s/packages/pkg_param_derived.vhd delete mode 100644 tests/designs/viterbi_decoder_axi4s/packages/pkg_trellis.vhd delete mode 100644 tests/designs/viterbi_decoder_axi4s/packages/pkg_types.vhd delete mode 100644 tests/designs/viterbi_decoder_axi4s/src/acs.vhd delete mode 100644 tests/designs/viterbi_decoder_axi4s/src/axi4s_buffer.vhd delete mode 100644 tests/designs/viterbi_decoder_axi4s/src/branch_distance.vhd delete mode 100644 tests/designs/viterbi_decoder_axi4s/src/dec_viterbi.vhd delete mode 100644 tests/designs/viterbi_decoder_axi4s/src/generic_sp_ram.vhd delete mode 100644 tests/designs/viterbi_decoder_axi4s/src/ram_ctrl.vhd delete mode 100644 tests/designs/viterbi_decoder_axi4s/src/recursion.vhd delete mode 100644 tests/designs/viterbi_decoder_axi4s/src/reorder.vhd delete mode 100644 tests/designs/viterbi_decoder_axi4s/src/traceback.vhd delete mode 100644 tests/pytest/test_binary_value.py delete mode 100644 tests/pytest/test_utils.py delete mode 100644 tests/test_cases/issue_120/Makefile delete mode 100644 tests/test_cases/issue_120/issue_120.py delete mode 100644 tests/test_cases/issue_1279/Makefile delete mode 100644 tests/test_cases/issue_1279/issue_1279.py delete mode 100644 tests/test_cases/issue_142/Makefile delete mode 100644 tests/test_cases/issue_142/issue_142.py delete mode 100644 tests/test_cases/issue_253/Makefile delete mode 100644 tests/test_cases/issue_253/issue_253.py delete mode 100644 tests/test_cases/issue_330/Makefile delete mode 100644 tests/test_cases/issue_330/issue_330.py delete mode 100644 tests/test_cases/issue_348/Makefile delete mode 100644 tests/test_cases/issue_348/issue_348.py delete mode 100644 tests/test_cases/issue_588/Makefile delete mode 100644 tests/test_cases/issue_588/issue_588.py delete mode 100644 tests/test_cases/issue_768_a/Makefile delete mode 100644 tests/test_cases/issue_768_a/issue_768.py delete mode 100644 tests/test_cases/issue_768_b/Makefile delete mode 100644 tests/test_cases/issue_768_b/issue_768.py delete mode 100644 tests/test_cases/issue_857/Makefile delete mode 100644 tests/test_cases/issue_857/issue_857.py delete mode 100644 tests/test_cases/issue_892/Makefile delete mode 100644 tests/test_cases/issue_892/issue_892.py delete mode 100644 tests/test_cases/issue_893/Makefile delete mode 100644 tests/test_cases/issue_893/issue_893.py delete mode 100644 tests/test_cases/issue_957/Makefile delete mode 100644 tests/test_cases/issue_957/issue_957.py delete mode 100644 tests/test_cases/test_array/Makefile delete mode 100644 tests/test_cases/test_array/test_array.py delete mode 100644 tests/test_cases/test_array_simple/Makefile delete mode 100644 tests/test_cases/test_array_simple/test_array_simple.py delete mode 100644 tests/test_cases/test_cocotb/Makefile delete mode 100644 tests/test_cases/test_cocotb/common.py delete mode 100644 tests/test_cases/test_cocotb/test_async_coroutines.py delete mode 100644 tests/test_cases/test_cocotb/test_async_generators.py delete mode 100644 tests/test_cases/test_cocotb/test_clock.py delete mode 100644 tests/test_cases/test_cocotb/test_concurrency_primitives.py delete mode 100644 tests/test_cases/test_cocotb/test_deprecated.py delete mode 100644 tests/test_cases/test_cocotb/test_edge_triggers.py delete mode 100644 tests/test_cases/test_cocotb/test_generator_coroutines.py delete mode 100644 tests/test_cases/test_cocotb/test_handle.py delete mode 100644 tests/test_cases/test_cocotb/test_logging.py delete mode 100644 tests/test_cases/test_cocotb/test_pytest.py delete mode 100644 tests/test_cases/test_cocotb/test_scheduler.py delete mode 100644 tests/test_cases/test_cocotb/test_synchronization_primitives.py delete mode 100644 tests/test_cases/test_cocotb/test_testfactory.py delete mode 100644 tests/test_cases/test_cocotb/test_tests.py delete mode 100644 tests/test_cases/test_cocotb/test_timing_triggers.py delete mode 100644 tests/test_cases/test_compare/Makefile delete mode 100644 tests/test_cases/test_compare/test_compare.py delete mode 100644 tests/test_cases/test_configuration/Makefile delete mode 100644 tests/test_cases/test_configuration/test_configurations.py delete mode 100644 tests/test_cases/test_discovery/Makefile delete mode 100644 tests/test_cases/test_discovery/test_discovery.py delete mode 100644 tests/test_cases/test_discovery/test_vhdl_block.py delete mode 100644 tests/test_cases/test_discovery/test_vhdl_indexed_name.py delete mode 100644 tests/test_cases/test_exit_error/Makefile delete mode 100644 tests/test_cases/test_exit_error/test_exit.py delete mode 100644 tests/test_cases/test_external/Makefile delete mode 100644 tests/test_cases/test_external/test_external.py delete mode 100644 tests/test_cases/test_failure/Makefile delete mode 100644 tests/test_cases/test_failure/test_failure.py delete mode 100644 tests/test_cases/test_fatal/Makefile delete mode 100644 tests/test_cases/test_fatal/fatal.sv delete mode 100644 tests/test_cases/test_fatal/fatal.vhd delete mode 100644 tests/test_cases/test_fatal/test_fatal.py delete mode 100644 tests/test_cases/test_force_release/Makefile delete mode 100644 tests/test_cases/test_force_release/test_force_release.py delete mode 100644 tests/test_cases/test_iteration_mixedlang/Makefile delete mode 100644 tests/test_cases/test_iteration_mixedlang/test_iteration.py delete mode 100644 tests/test_cases/test_iteration_verilog/Makefile delete mode 100644 tests/test_cases/test_iteration_verilog/test_iteration_es.py delete mode 100644 tests/test_cases/test_iteration_vhdl/Makefile delete mode 100644 tests/test_cases/test_iteration_vhdl/test_iteration.py delete mode 100644 tests/test_cases/test_module_var_empty/Makefile delete mode 100644 tests/test_cases/test_module_var_messy/Makefile delete mode 100644 tests/test_cases/test_module_var_messy/test_nothing.py delete mode 100644 tests/test_cases/test_module_without_tests/Makefile delete mode 100644 tests/test_cases/test_module_without_tests/test_nothing.py delete mode 100644 tests/test_cases/test_multi_dimension_array/Makefile delete mode 100644 tests/test_cases/test_multi_dimension_array/test_cocotb_array.py delete mode 100644 tests/test_cases/test_plusargs/Makefile delete mode 100644 tests/test_cases/test_plusargs/plusargs.py delete mode 100644 tests/test_cases/test_skipped/Makefile delete mode 100644 tests/test_cases/test_skipped/test_skipped.py delete mode 100644 tests/test_cases/test_verilog_access/Makefile delete mode 100644 tests/test_cases/test_verilog_access/test_verilog_access.py delete mode 100644 tests/test_cases/test_vhdl_access/Makefile delete mode 100644 tests/test_cases/test_vhdl_access/test_vhdl_access.py delete mode 100644 tox.ini diff --git a/.azp-private.yml b/.azp-private.yml deleted file mode 100644 index 27c32ae5..00000000 --- a/.azp-private.yml +++ /dev/null @@ -1,27 +0,0 @@ -# Private CI trigger. Used to run tooling that can't currently be shared -# publicly. - -trigger: - batch: true - branches: - include: - - '*' - tags: - include: - - "*" -pr: - branches: - include: - - '*' - -# The runner used for private CI enforces the use of the template below. All -# build steps need to be placed into the template. -resources: - repositories: - - repository: cocotb-private-ci - type: github - endpoint: cocotb - name: cocotb/cocotb-private-ci - -extends: - template: jobs.yml@cocotb-private-ci diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index ccfff224..00000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,16 +0,0 @@ - diff --git a/.github/issue_template.md b/.github/issue_template.md deleted file mode 100644 index 99580e0c..00000000 --- a/.github/issue_template.md +++ /dev/null @@ -1,11 +0,0 @@ - diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml deleted file mode 100644 index 62e7acc4..00000000 --- a/.github/workflows/regression-tests.yml +++ /dev/null @@ -1,280 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -name: Regression Tests - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - - lint-whitespace: - runs-on: ubuntu-latest - name: whitespace - strategy: - fail-fast: false - steps: - - uses: actions/checkout@v2 - - name: whitespace - run: | - ! git --no-pager grep --color --line-number --full-name --extended-regexp -I '\s+$' - - lint-flake8: - runs-on: ubuntu-latest - name: flake8 - strategy: - fail-fast: false - matrix: - python-version: [3.5, 3.9] - steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{matrix.python-version}} - uses: actions/setup-python@v2 - with: - python-version: ${{matrix.python-version}} - - name: flake8 - run: | - pip install flake8 - flake8 - - tests: - - name: ${{matrix.extra_name}}${{matrix.sim}} (${{matrix.sim-version}}) | ${{matrix.os}} | Python ${{matrix.python-version}} ${{matrix.may_fail && '| May Fail' || ''}} - runs-on: ${{matrix.os}} - env: - SIM: ${{matrix.sim}} - TOPLEVEL_LANG: ${{matrix.lang}} - CXX: ${{matrix.cxx || 'g++'}} - CC: ${{matrix.cc || 'gcc'}} - OS: ${{matrix.os}} - - strategy: - fail-fast: false - matrix: - include: - - # Test different Python versions with package managed Icarus on Ubuntu - - - sim: icarus - sim-version: apt - lang: verilog - # lowest version according to https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json - python-version: 3.5.4 - os: ubuntu-20.04 - - - sim: icarus - sim-version: apt - lang: verilog - # lowest version according to https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json - python-version: 3.6.7 - os: ubuntu-20.04 - - - sim: icarus - sim-version: apt - lang: verilog - # lowest version according to https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json - python-version: 3.7.1 - os: ubuntu-20.04 - - - sim: icarus - sim-version: apt - lang: verilog - python-version: 3.8 - os: ubuntu-20.04 - - - sim: icarus - sim-version: apt - lang: verilog - python-version: 3.9 - os: ubuntu-20.04 - - # Test Icarus dev on Ubuntu - - - sim: icarus - sim-version: master - lang: verilog - python-version: 3.8 - os: ubuntu-20.04 - - # Test GHDL on Ubuntu - - - sim: ghdl - sim-version: apt - lang: vhdl - python-version: 3.8 - os: ubuntu-20.04 - may_fail: true - - - sim: ghdl - sim-version: nightly - lang: vhdl - python-version: 3.8 - os: ubuntu-latest - may_fail: true - - # Test Verilator on Ubuntu - - - sim: verilator - sim-version: master - lang: verilog - python-version: 3.8 - os: ubuntu-20.04 - may_fail: true - - # Test other OSes - - - sim: icarus # Icarus homebrew --HEAD - sim-version: homebrew-HEAD - lang: verilog - python-version: 3.8 - os: macos-latest - - - sim: icarus # Icarus windows master from source - sim-version: master - lang: verilog - python-version: 3.8 - os: windows-latest - - # Other - - - sim: icarus # use clang instead of gcc - sim-version: v10_3 - lang: verilog - python-version: 3.8 - os: ubuntu-latest - cxx: clang++ - cc: clang - extra_name: "clang | " - - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 # ensure we clone all the tags so we can see setuptools_scm working - - # Install Python - - name: Set up Python ${{matrix.python-version}} - if: startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos') - uses: actions/setup-python@v2 - with: - python-version: ${{matrix.python-version}} - - name: Set up Anaconda ${{matrix.python-version}} (Windows) - if: startsWith(matrix.os, 'windows') - uses: conda-incubator/setup-miniconda@v2 - with: - auto-update-conda: true - python-version: ${{matrix.python-version}} - - # Install Icarus - - name: Set up Icarus (Ubuntu - apt) - if: startsWith(matrix.os, 'ubuntu') && matrix.sim == 'icarus' && matrix.sim-version == 'apt' - run: | - sudo apt install -y --no-install-recommends iverilog - - name: Set up Icarus (Ubuntu - source) - if: startsWith(matrix.os, 'ubuntu') && matrix.sim == 'icarus' && matrix.sim-version != 'apt' - run: | - sudo apt install -y --no-install-recommends g++ gperf flex bison make autoconf - git clone https://github.com/steveicarus/iverilog.git -b ${{matrix.sim-version}} - cd iverilog - bash ./autoconf.sh - bash ./configure - make -j $(nproc) - sudo make install - - name: Set up Icarus (Windows - source) - if: startsWith(matrix.os, 'windows') && matrix.sim == 'icarus' - run: | - conda install -c msys2 m2-base m2-make m2-autoconf m2-flex m2-bison m2-gperf m2w64-toolchain - git clone https://github.com/steveicarus/iverilog.git -b ${{matrix.sim-version}} - cd iverilog - bash ./autoconf.sh - bash ./configure --host=x86_64-w64-mingw32 --prefix=/c/iverilog - make -j $(nproc) - make install - echo "C:\iverilog\bin" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8 - - name: Set up Icarus (MacOS - homebrew --HEAD) - if: startsWith(matrix.os, 'macos') && matrix.sim == 'icarus' && matrix.sim-version == 'homebrew-HEAD' - run: | - brew install icarus-verilog --HEAD - - name: Set up Icarus (MacOS - homebrew) - if: startsWith(matrix.os, 'macos') && matrix.sim == 'icarus' && matrix.sim-version == 'homebrew' - run: | - brew install icarus-verilog - - # Install GHDL - - name: Set up GHDL (Ubuntu - apt) - if: startsWith(matrix.os, 'ubuntu') && matrix.sim == 'ghdl' && matrix.sim-version == 'apt' - run: | - sudo apt install -y --no-install-recommends ghdl-mcode ghdl - - name : Set up GHDL (Ubunutu - nightly) - if: startsWith(matrix.os, 'ubuntu') && matrix.sim == 'ghdl' && matrix.sim-version == 'nightly' - uses: ghdl/setup-ghdl-ci@nightly - with: - backend: mcode - - # Install Verilator - - name: Set up Verilator (Ubuntu - apt) - if: startsWith(matrix.os, 'ubuntu') && matrix.sim == 'verilator' && matrix.sim-version == 'apt' - run: | - sudo apt install -y --no-install-recommends verilator - - name: Set up Verilator (Ubunutu - source) - if: startsWith(matrix.os, 'ubuntu') && matrix.sim == 'verilator' && matrix.sim-version != 'apt' - run: | - sudo apt install -y --no-install-recommends make g++ perl python3 autoconf flex bison libfl2 libfl-dev zlibc zlib1g zlib1g-dev - git clone https://github.com/verilator/verilator.git -b ${{matrix.sim-version}} - cd verilator - autoconf - ./configure - make -j $(nproc) - sudo make install - - # Windows Testing - - name: Install cocotb build dependencies (Windows) - if: startsWith(matrix.os, 'windows') - run: conda install --yes -c msys2 m2-base m2-make m2w64-toolchain libpython - - name: Install cocotb (Windows) - if: startsWith(matrix.os, 'windows') - run: python -m pip install --global-option build_ext --global-option --compiler=mingw32 -e . - - name: Install Python testing dependencies (Windows) - run: pip install coverage xunitparser pytest pytest-cov - - name: Test (Windows) - if: startsWith(matrix.os, 'windows') - id: windowstesting - continue-on-error: ${{matrix.may_fail || false}} - timeout-minutes: 15 - run: | - pytest - make test - - # Ubuntu / MacOS Testing - - name: Install cocotb build dependencies (Ubuntu - g++) - if: startsWith(matrix.os, 'ubuntu') && (!matrix.cxx || matrix.cxx == 'g++') - run: | - sudo apt install g++ - - name: Install cocotb build dependencies (Ubuntu - clang++) - if: startsWith(matrix.os, 'ubuntu') && matrix.cxx == 'clang++' - run: | - sudo apt install clang - - name: Install cocotb build dependencies (MacOS) - if: startsWith(matrix.os, 'macos') - run: | - g++ --version - - name: Install Python testing dependencies (Ubuntu, MacOS) - if: startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos') - run: | - pip install tox tox-gh-actions - - name: Test (Ubuntu, MacOS) - id: unixtesting - if: startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos') - continue-on-error: ${{matrix.may_fail || false}} - timeout-minutes: 15 - run: | - tox - - # codecov - - name: Upload to codecov - if: steps.windowstesting.outcome == 'success' || steps.unixtesting.outcome == 'success' - run: | - pip install codecov - codecov --gcov-args='-rl' diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml deleted file mode 100644 index 7dd1b4b5..00000000 --- a/.github/workflows/stale.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: "Stale Questions" -on: - schedule: - - cron: "00 02 * * *" - -jobs: - stale: - runs-on: ubuntu-latest - steps: - - uses: actions/stale@v3 - with: - repo-token: ${{secrets.GITHUB_TOKEN}} - days-before-stale: 30 - days-before-close: 7 - stale-issue-message: > - Has your question been resolved? If so please close this issue. - If it has not been resolved, you may need to provide more information. - If no more activity on this issue occurs in 7 days, it will be closed. - stale-issue-label: "status:stale" - only-labels: "type:question,status:close?" - exempt-issue-labels: "type:bug,type:feature" - operations-per-run: 5 diff --git a/.gitignore b/.gitignore deleted file mode 100644 index df099930..00000000 --- a/.gitignore +++ /dev/null @@ -1,105 +0,0 @@ -*.py[cod] - -# C extensions -*.so -*.dll - -# C objects -*.o - -# Python and C++ code coverage -.coverage -*.gcno -*.gcda -*.gcov - -# Packaging -*.egg -*.egg-info -dist -build -eggs -parts -sdist -obj -develop-eggs -.installed.cfg -__pycache__ -.tox - -# Vim tmp files -*.swp -*~ - -# Emacs tmp files -\#*\# -\.\#* - -# Mergetool tmp files -*.orig -*.bak - -# Waveforms -*.vcd -*.fst -*.fst.hier - -# Results -results.xml -combined_results.xml - -# Debuggers -.gdb_history - -# VCS files -*.tab -sim_build -ucli.key - -# Pytest -.pytest_cache - -# Cadence Incisive/Xcelium -*.elog -irun.log -xrun.log -irun.key -xrun.key -irun.history -xrun.history -INCA_libs -xcelium.d -ncelab_*.err -xmelab_*.err -ncsim_*.err -xmsim_*.err -bpad_*.err -.bpad/ -.simvision/ -waves.shm/ - -# Interface libraries built on setup -cocotb/libs -pip-wheel-metadata/ - -# Mentor Modelsim/Questa -tests/test_cases/*/modelsim.ini -tests/test_cases/*/transcript -examples/*/tests/modelsim.ini -examples/*/tests/transcript -*.wlf - -# Riviera -tests/test_cases/*/library.cfg -tests/test_cases/*/dataset.asdb -tests/test_cases/*/compile -examples/*/tests/library.cfg -examples/*/tests/dataset.asdb -examples/*/tests/compile - -# Tachyon DA CVC -tests/test_cases/*/verilog.log -examples/*/tests/verilog.log - -# setuptools_scm artifact -/cocotb/_version.py diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile deleted file mode 100644 index c308aee8..00000000 --- a/.gitpod.Dockerfile +++ /dev/null @@ -1,55 +0,0 @@ -FROM gitpod/workspace-full-vnc - -USER gitpod - -## Install Python with --enable-shared -ARG PYTHON_VERSION=3.8.2 -RUN rm -rf ${HOME}/.pyenv/versions/${PYTHON_VERSION} -RUN PYTHON_CONFIGURE_OPTS="--enable-shared" pyenv install ${PYTHON_VERSION} -RUN pyenv global ${PYTHON_VERSION} - -RUN pip3 install --upgrade pip - -# Install extra packages -RUN pip3 install -U flake8 pylint pytype mypy gcovr cherrypy dowser - -# Re-synchronize the OS package index -RUN sudo apt-get update - -# Install all needed packages for all simulators -RUN sudo apt-get install -y perl make flex gnat gtkwave swig autoconf g++ bison libfl2 libfl-dev ccache libgoogle-perftools-dev numactl perl-doc -RUN sudo rm -rf /var/lib/apt/lists/* - -## Install Icarus Verilog -RUN brew install icarus-verilog - -## Install Verilator -ENV VERILATOR_BRANCH=stable - -RUN git clone https://github.com/verilator/verilator.git --branch ${VERILATOR_BRANCH} verilator \ - && unset VERILATOR_ROOT \ - && cd verilator \ - && autoconf \ - && ./configure \ - && make --silent \ - && sudo make --silent install \ - && cd .. \ - && rm -rf verilator - -## Install GHDL -ENV GHDL_BRANCH=v0.37 -RUN git clone https://github.com/ghdl/ghdl.git --depth=1 --branch ${GHDL_BRANCH} ghdl \ - && cd ghdl \ - && ./configure \ - && make --silent \ - && sudo make --silent install \ - && cd .. \ - && rm -rf ghdl - -# Install cvc -RUN git clone https://github.com/cambridgehackers/open-src-cvc.git --depth=1 cvc \ - && cd cvc/src \ - && make -f makefile.cvc64 --silent \ - && sudo cp cvc64 /usr/local/bin \ - && cd ../.. \ - && rm -rf cvc diff --git a/.gitpod.yml b/.gitpod.yml deleted file mode 100644 index 0aacb208..00000000 --- a/.gitpod.yml +++ /dev/null @@ -1,39 +0,0 @@ -image: - file: .gitpod.Dockerfile - -tasks: - - before: > - pip3 install -e . && - export COCOTB_REDUCED_LOG_FMT=1 - init: > - gp preview https://docs.cocotb.org/ && - cd /workspace/cocotb/examples/dff/tests && - gp open ../hdl/dff.v && - gp open ../hdl/dff.vhdl && - gp open Makefile && - gp open dff_cocotb.py && - make SIM=icarus && - EXTRA_ARGS="--trace-fst --trace-structs" make SIM=verilator && - gtkwave dump.fst --rcvar 'initial_window_x 1920' --rcvar 'initial_window_y 1061' --rcvar 'do_initial_zoom_fit yes' & - command: > - cd /workspace/cocotb/examples/dff/tests ; - history -s make SIM=cvc ; - history -s make SIM=ghdl TOPLEVEL_LANG=vhdl ; - history -s make SIM=icarus ; - history -s EXTRA_ARGS=\"--trace-fst --trace-structs\" make SIM=verilator ; - history -s gtkwave dump.fst --rcvar \'initial_window_x 1920\' --rcvar \'initial_window_y 1061\' --rcvar \'do_initial_zoom_fit yes\' ; - history -d 3 - -# NOTE: the geometry for gtkwave's fullscreen size can be found with xwininfo -root - -# https://www.gitpod.io/docs/config-ports/ -ports: - - port: 6080 # VNC for e.g. gtkwave - onOpen: notify - - port: 5900 - onOpen: ignore - -vscode: - extensions: - - mshr-h.veriloghdl@1.0.6:RPslnvyzniF7C66mxHT+Hg== - - puorc.awesome-vhdl@0.0.1:w0lXwxIDreee5Mbtg9XSfg== diff --git a/.lgtm.yml b/.lgtm.yml deleted file mode 100644 index a9d1b46b..00000000 --- a/.lgtm.yml +++ /dev/null @@ -1,4 +0,0 @@ -extraction: - cpp: - index: - build_command: "python3 setup.py build" diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index 79c0abf4..00000000 --- a/.pylintrc +++ /dev/null @@ -1,528 +0,0 @@ -[MASTER] - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code. -extension-pkg-whitelist=cocotb.simulator - -# Specify a score threshold to be exceeded before program exits with error. -fail-under=10.0 - -# Add files or directories to the blacklist. They should be base names, not -# paths. -ignore=CVS - -# Add files or directories matching the regex patterns to the blacklist. The -# regex matches against base names, not paths. -ignore-patterns= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the -# number of processors available to use. -jobs=0 - -# Control the amount of potential inferred values when inferring a single -# object. This can help the performance when dealing with large functions or -# complex, nested conditions. -limit-inference-results=100 - -# List of plugins (as comma separated values of python module names) to load, -# usually to register additional checkers. -load-plugins= - -# Pickle collected data for later comparisons. -persistent=yes - -# When enabled, pylint would attempt to guess common misconfiguration and emit -# user-friendly hints instead of false-positive error messages. -suggestion-mode=yes - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. -confidence= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once). You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use "--disable=all --enable=classes -# --disable=W". -disable=no-member, - invalid-name, - too-many-locals, - too-many-nested-blocks, - too-many-instance-attributes, - too-many-arguments, - too-many-statements, - too-many-branches, - pointless-statement, - logging-not-lazy, - logging-format-interpolation, - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -enable=c-extension-no-member - - -[REPORTS] - -# Python expression which should return a score less than or equal to 10. You -# have access to the variables 'error', 'warning', 'refactor', and 'convention' -# which contain the number of messages in each category, as well as 'statement' -# which is the total number of statements analyzed. This score is used by the -# global evaluation report (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details. -#msg-template= - -# Set the output format. Available formats are text, parseable, colorized, json -# and msvs (visual studio). You can also give a reporter class, e.g. -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Tells whether to display a full report or only the messages. -reports=no - -# Activate the evaluation score. -score=yes - - -[REFACTORING] - -# Maximum number of nested blocks for function / method body -max-nested-blocks=5 - -# Complete name of functions that never returns. When checking for -# inconsistent-return-statements if a never returning function is called then -# it will be considered as an explicit return statement and no message will be -# printed. -never-returning-functions=sys.exit - - -[BASIC] - -# Naming style matching correct argument names. -argument-naming-style=snake_case - -# Regular expression matching correct argument names. Overrides argument- -# naming-style. -#argument-rgx= - -# Naming style matching correct attribute names. -attr-naming-style=snake_case - -# Regular expression matching correct attribute names. Overrides attr-naming- -# style. -#attr-rgx= - -# Bad variable names which should always be refused, separated by a comma. -bad-names=foo, - bar, - baz, - toto, - tutu, - tata - -# Bad variable names regexes, separated by a comma. If names match any regex, -# they will always be refused -bad-names-rgxs= - -# Naming style matching correct class attribute names. -class-attribute-naming-style=any - -# Regular expression matching correct class attribute names. Overrides class- -# attribute-naming-style. -#class-attribute-rgx= - -# Naming style matching correct class names. -class-naming-style=PascalCase - -# Regular expression matching correct class names. Overrides class-naming- -# style. -#class-rgx= - -# Naming style matching correct constant names. -const-naming-style=UPPER_CASE - -# Regular expression matching correct constant names. Overrides const-naming- -# style. -#const-rgx= - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 - -# Naming style matching correct function names. -function-naming-style=snake_case - -# Regular expression matching correct function names. Overrides function- -# naming-style. -#function-rgx= - -# Good variable names which should always be accepted, separated by a comma. -good-names=i, - j, - k, - ex, - Run, - _ - -# Good variable names regexes, separated by a comma. If names match any regex, -# they will always be accepted -good-names-rgxs= - -# Include a hint for the correct naming format with invalid-name. -include-naming-hint=no - -# Naming style matching correct inline iteration names. -inlinevar-naming-style=any - -# Regular expression matching correct inline iteration names. Overrides -# inlinevar-naming-style. -#inlinevar-rgx= - -# Naming style matching correct method names. -method-naming-style=snake_case - -# Regular expression matching correct method names. Overrides method-naming- -# style. -#method-rgx= - -# Naming style matching correct module names. -module-naming-style=snake_case - -# Regular expression matching correct module names. Overrides module-naming- -# style. -#module-rgx= - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=^_ - -# List of decorators that produce properties, such as abc.abstractproperty. Add -# to this list to register other decorators that produce valid properties. -# These decorators are taken in consideration only for invalid-name. -property-classes=abc.abstractproperty - -# Naming style matching correct variable names. -variable-naming-style=snake_case - -# Regular expression matching correct variable names. Overrides variable- -# naming-style. -#variable-rgx= - - -[FORMAT] - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Maximum number of characters on a single line. -max-line-length=120 - -# Maximum number of lines in a module. -max-module-lines=1000 - -# Allow the body of a class to be on the same line as the declaration if body -# contains single statement. -single-line-class-stmt=no - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - - -[LOGGING] - -# The type of string formatting that logging methods do. `old` means using % -# formatting, `new` is for `{}` formatting. -logging-format-style=old - -# Logging modules to check that the string format arguments are in logging -# function parameter format. -logging-modules=logging - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME, - XXX, - TODO - -# Regular expression of note tags to take in consideration. -#notes-rgx= - - -[SIMILARITIES] - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=no - -# Minimum lines number of a similarity. -min-similarity-lines=4 - - -[SPELLING] - -# Limits count of emitted suggestions for spelling mistakes. -max-spelling-suggestions=4 - -# Spelling dictionary name. Available dictionaries: none. To make it work, -# install the python-enchant package. -spelling-dict= - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains the private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to the private dictionary (see the -# --spelling-private-dict-file option) instead of raising a message. -spelling-store-unknown-words=no - - -[STRING] - -# This flag controls whether inconsistent-quotes generates a warning when the -# character used as a quote delimiter is used inconsistently within a module. -check-quote-consistency=no - -# This flag controls whether the implicit-str-concat should generate a warning -# on implicit string concatenation in sequences defined over several lines. -check-str-concat-over-line-jumps=no - - -[TYPECHECK] - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# Tells whether to warn about missing members when the owner of the attribute -# is inferred to be None. -ignore-none=yes - -# This flag controls whether pylint should warn about no-member and similar -# checks whenever an opaque object is returned when inferring. The inference -# can return multiple potential results while evaluating a Python object, but -# some branches might not be evaluated, which results in partial inference. In -# that case, it might be useful to still emit no-member and other checks for -# the rest of the inferred objects. -ignore-on-opaque-inference=yes - -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis). It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules= - -# Show a hint with possible names when a member name was not found. The aspect -# of finding the hint is based on edit distance. -missing-member-hint=yes - -# The minimum edit distance a name should have in order to be considered a -# similar match for a missing member name. -missing-member-hint-distance=1 - -# The total number of similar names that should be taken in consideration when -# showing a hint for a missing member. -missing-member-max-choices=1 - -# List of decorators that change the signature of a decorated function. -signature-mutators= - - -[VARIABLES] - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid defining new builtins when possible. -additional-builtins= - -# Tells whether unused global variables should be treated as a violation. -allow-global-unused-variables=yes - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_, - _cb - -# A regular expression matching the name of dummy variables (i.e. expected to -# not be used). -dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore. -ignored-argument-names=_.*|^ignored_|^unused_ - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io - - -[CLASSES] - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__, - __new__, - setUp, - __post_init__ - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict, - _fields, - _replace, - _source, - _make, - _coro, - _trigger, - _outcome, - _advance, - _wait, - _name, - _id, - _library_coverage, - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=cls - - -[DESIGN] - -# Maximum number of arguments for function / method. -max-args=5 - -# Maximum number of attributes for a class (see R0902). -max-attributes=7 - -# Maximum number of boolean expressions in an if statement (see R0916). -max-bool-expr=5 - -# Maximum number of branch for function / method body. -max-branches=12 - -# Maximum number of locals for function / method body. -max-locals=15 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - -# Maximum number of return / yield for function / method body. -max-returns=6 - -# Maximum number of statements in function / method body. -max-statements=50 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=2 - - -[IMPORTS] - -# List of modules that can be imported at any level, not just the top level -# one. -allow-any-import-level= - -# Allow wildcard imports from modules that define __all__. -allow-wildcard-with-all=no - -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no - -# Deprecated modules which should not be used, separated by a comma. -deprecated-modules=optparse,tkinter.tix - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled). -ext-import-graph= - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled). -import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled). -int-import-graph= - -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= - -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant - -# Couples of modules and preferred modules, separated by a comma. -preferred-modules= - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "BaseException, Exception". -overgeneral-exceptions=BaseException, - Exception diff --git a/.readthedocs.yml b/.readthedocs.yml deleted file mode 100644 index 358468be..00000000 --- a/.readthedocs.yml +++ /dev/null @@ -1,17 +0,0 @@ -# .readthedocs.yml -# Read the Docs configuration file -# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details - -version: 2 - -# Use conda because default Python is not compiled with --enable-shared -conda: - environment: documentation/conda.yml - -python: - install: - - method: pip - path: . - -sphinx: - configuration: documentation/source/conf.py diff --git a/.theia/settings.json b/.theia/settings.json deleted file mode 100644 index 3f23439e..00000000 --- a/.theia/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - // show leading and trailing whitespace - "editor.renderWhitespace": "boundary" -} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 7f93f6d4..00000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: python -dist: xenial - -git: - quiet: true - -jobs: - include: - - - stage: DeployPyPI - python: 3.7 - if: tag IS present - script: skip - deploy: - provider: pypi - user: themperek - password: - secure: "MLCDTXDYX7TKtPDXxUz7hIibXAIHGkz297yVQ7inLToCX/bKAkjdy/uwQZ4W7goRUFKJ6XliqBU8l/tihqPXCPtRaIU4hdLF9LZjR02Z16Ik3vDHBQcX/htfm32WAkFIVdbzBldD0N1NJIPYc83GnwLtgFM2u1fuCffiu2VqkTY=" - on: - tags: true - repo: cocotb/cocotb - distributions: sdist diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 390bd65e..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,508 +0,0 @@ -# Cocotb Contribution Guidelines - -Welcome to the cocotb development! -We are an inclusive community with the common goal of improving the cocotb, a coroutine based cosimulation library for writing VHDL and Verilog testbenches in Python. -This guide explains how to contribute to cocotb, and documents the processes we agreed on to manage the project. -All processes in this document are designed to streamline the development effort, to avoid bottlenecks, and to ultimately give a pleasant experience to all involved. - - -## Getting Help - -Cocotb is a diverse and challenging project to contribute to. -If you ever feel lost, out of your depth, or simply want to know more, the [cocotb Gitter channel](https://gitter.im/cocotb/Lobby) is actively watched by many cocotb users, contributors, and maintainers. -It is a good idea if you are unsure whether your problem or question is worthy of a Github Issue to first post it to the Gitter channel. -You may also ask questions in [Github issues](https://github.com/cocotb/cocotb/issues). -If you don't receive any response on the Gitter channel or a Github issue, or you want help privately, you may directly contact a [maintainer](#maintainers). - - -## What to Work On - -There is *a lot* of work to do on this project, no matter your area of expertise or skill level. - -If you are a beginner there are several [Github issues](https://github.com/cocotb/cocotb/issues) marked [`good first issue`](https://github.com/cocotb/cocotb/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) you can look at. -There are also a number of things you can work on that aren't in Github issues and don't require in-depth knowledge of the cocotb internals. -They including the following: - -* Documentation improvements -* Managing Github issues and the Gitter channel -* Testing and coverage improvements - -Cocotb is still not perfect. There are plenty of [bug fixes](https://github.com/cocotb/cocotb/issues?q=is%3Aopen+is%3Aissue+label%3Atype%3Abug) and [features](https://github.com/cocotb/cocotb/issues?q=is%3Aopen+is%3Aissue+label%3Atype%3Afeature) that can be worked on. -Most of these are recorded as Github issues. - - -### Documentation - -Cocotb's documentation is always open to improvements. -Improving documentation will help users better understand and use cocotb; -and may decrease the number of questions the Gitter channel and Github issue page. -Updating documentation requires knowledge of: - -* [reStructuredText](https://docutils.sourceforge.io/rst.html) -* [Sphinx documentation generator](https://www.sphinx-doc.org/en/master/) -* [Markdown](https://www.markdownguide.org/) -* [How to architect documentation](https://documentation.divio.com/) - -Some documentation should be located in the official documentation on [Read the Docs/RTD](https://docs.cocotb.org/en/latest/), -while the rest belongs on the [Wiki](https://github.com/cocotb/cocotb/wiki). -There are several ways to improve the documentation: - -* Better documenting core functionality (RTD) -* Documenting common "gotchas" (RTD) -* Documenting difficult and niche use cases (Wiki) -* Documenting common design patterns (Wiki) -* Documenting internal components (Wiki) - -See the documentation on [building the documentation](#building-documentation-locally) and the [guidelines on submitting pull requests](#patch-requirements). -Documentation improvements typically require no maintainer pre-approval; you can simply work on the documentation and open a pull request. -Documentation on the Wiki does not require a pull request; any user with a Github account can contribute to it. -Please be responsible with that power. - - -### Project Management - -The cocotb project is [fairly popular](https://larsasplund.github.io/github-facts/verification-practices.html#frameworks) and the -[Gitter channel](https://gitter.im/cocotb/Lobby) and -[Github issues](https://github.com/cocotb/cocotb) page receive a fair amount of traffic; -which is only expected to increase. -People are needed to categorize issues and pull requests, and respond to questions. -Working this task is the quickest way to learn how cocotb works. -Tending to this task requires the following: - -* people skills -* an understanding of the scope of cocotb -* general understanding about how cocotb works - -Someone working this task should set notifications on the Gitter channel to be notified of any new comments. -They should also "watch" the Github repo by selecting the "Watching" notification level button in the upper right corner of the main Github page. -Finally, they should notify the maintainers that they are able and willing to accept questions. - -To be able to add labels and close issues and PRs you will need special permissions. -Contact a [maintainer](#maintainer) if you are interested in receiving these permissions. -They will be granted according to the project's need and the requestor's familiarity with cocotb. -Once you have those permissions, see the guidelines on [managing issues and pull requests](#Managing-of-Issues-and-Pull-Requests). - -This task can also be done without special repo permissions, by just commenting on the issue or PR. -This is especially helpful for Github issues about bugs. -If you can duplicate the bug or confirm the bug report is invalid, that helps maintainers *a lot*. - - -### Tests and Coverage - -Cocotb has a suite of unit tests (located in the `tests` directory) and examples (located in the `examples` directory) which are functional acceptance tests. -If a pull request cannot pass *all* of these tests, it will likely be rejected. -To ensure cocotb only includes the highest quality code, these test should be exhaustive. -We use code coverage as a quantifiable metric of the "exhaustiveness" of these tests, and wish to improve this metric. - -Working on this task requires a familiarity with: - -* Cocotb's core functionality -* How to write Verilog and VHDL -* How to write cocotb tests in Python -* (Optionally) [codecov](https://docs.codecov.io/docs); coverage aggregator and Github bot -* (Optionally) the [coverage](https://coverage.readthedocs.io/en/latest/) module, for Python code coverage -* (Optionally) [gcov](https://gcc.gnu.org/onlinedocs/gcc/Gcov.html), for C++ code coverage -* (Optionally) [Github Actions](https://docs.github.com/en/free-pro-team@latest/actions), for automatic acceptance testing - -Cocotb's regression tests can be improved by: - -* Testing more of cocotb's core functionality -* Testing corner cases left out of the current set of tests (identified by looking at code coverage) -* Increasing the matrix of simulators, operating system, and Python installations tested in CI - -Testing improvements don't require maintainer pre-approval, but require a pull request. -Please see the [guidelines on submitting pull requests](#patch-requirements). - - -### Features - -Cocotb is still in development and new features are still welcome and appreciated; -as long as they stay [in scope](#Architecture-and-Scope-of-Cocotb). -Cocotb is comprised of several major codebases, each requiring different sets of skills and development process. -Instead of including that breakdown here, it is done in the [internal documentation](https://github.com/cocotb/cocotb/wiki/cocotb-Internals). - -Small improvements to existing features generally do not require maintainer pre-approval. -Large changes, approximately >150 LOC changed, and new features generally require maintainer pre-approval. -If a change is deemed too large for the main repo, or out of scope, -please feel free to make it an [extension](https://docs.cocotb.org/en/latest/extensions.html). - -***New features must not break existing features.*** - -Feature changes require full coverage of the added feature. -This likely requires adding new unit tests to the `tests` directory. -Issue-specific test directories will not be accepted, unless a special HDL entity is required. -Instead, place the test in an existing test suite (`test_cocotb`, `test_discovery`, etc.). - -Features should generally follow the following design principles: - -* Something the user cannot do without assistance of cocotb-specific code -* Orthogonal to existing features -* Easily composed with existing features -* Limited in scope and impervious to scope creep - - -### Bugfixes - -**!WARNING!** Bugfixing cocotb is not for the faint of heart! - -Bugs happen. -cocotb supports many simulators that have inconsistent support for the procedural interfaces cocotb depends on, -and it has a number of features that aren't wholly tested yet. -There are likely many bugs lurking, waiting to be found; -which is why increasing testing and code coverage is important. -Working on bugfixing can be very challenging, depending on the cause of the bug. -In general, bugfixing requires knowledge of: - -* How cocotb works -* [cocotb's debugging utilities](https://github.com/cocotb/cocotb/wiki/Debugging-HOW-TOs#cocotb-debugging-functionality) -* (Optional) Simulator interfaces (VPI, VHPI, and FLI) -* (Optional) Python debugging tools ([pdb](https://github.com/cocotb/cocotb/wiki/Debugging-HOW-TOs#using-a-remote-python-debugger), [dowser](https://github.com/cocotb/cocotb/wiki/Debugging-HOW-TOs#debugging-python-memory-usage)) -* (Optional) C/C++ debugging tools ([gdb](https://github.com/cocotb/cocotb/wiki/Debugging-HOW-TOs#using-a-remote-cc-debugger), [valgrind](https://github.com/cocotb/cocotb/wiki/Debugging-HOW-TOs#debugging-cc-memory-usage)) -* (Optional) Specific simulators (sometimes the bug exists in the simulator and not cocotb) - -Fixing a bug follows the procedure: - -1. Locate buggy behavior, make a Github issue - * Maintainers may be able to offer more information, confirm it as a bug, or confirm it as expected behavior -2. Make a Minimum Reproducible Failing Example (MRFE, pronounced like Murphy, like the law :) - * Confirms the bug - * Add to [regressions](#running-tests-locally) -3. Open a new draft pull request with the MRFE test - * It should cause CI to fail -4. Determine scope of the bug, and add that detail to the pull request - * Which simulators/interfaces are affected? - * Which Python versions? - * Which operating systems? -5. Determine the cause of the bug, and add that detail to the pull request - * May require Python or C debugging, or the builtin cocotb debugging utilities -6. Make a fix, and push it up on the PR branch - * It should cause the CI to pass - * The fix should not break other existing functionality - -Details on how to debug cocotb can be found on the [Wiki](https://github.com/cocotb/cocotb/wiki/Debugging-HOW-TOs). - - -### Deprecations and Removals - -Cocotb's treatment of deprecations and removal follows guidelines laid out [here](https://symfony.com/doc/current/setup/upgrade_major.html#make-your-code-deprecation-free). -Deprecations serve the following purposes: - -* Remove legacy code that has been deemed out of scope -* Remove support for a simulator, OS, or Python version that is past end-of-life -* Remove potentially dangerous, broken, and misunderstood interfaces (usually accompanied with a superior alternative) - -Deprecations can be incorporated at any time. -They are typically implemented by [issuing a `DeprecationWarning`](https://docs.python.org/3/library/warnings.html#warnings.warn) in Python code; -or [issuing a LOG_WARN](https://docs.cocotb.org/en/stable/generated/file/gpi__logging_8h.html?highlight=LOG_WARN#c.LOG_WARN) with `DEPRECATED` in the message in C++ code. - -Removals only occur on major version bumps. -One can create removal pull requests at any time, on the condition they will not be accepted until the next release is known to be a major version release. - - -### Breaking Changes - -Breaking changes are changes to the interface or behavior of a user-facing entity. -They are necessary when a user-facing interfaces are broken in a way that cannot be changed without changing the behavior of user's code. -In these situations it is ideal to be able to implement a switch between new better behavior and the old broken behavior. -On major version bumps, this switch will be deprecated and the new behavior will become the default. - -In cases where behavioral switches are not easy to implement, breaking changes will attempt to be broadcasted to user by [issuing a `DeprecationWarning`](https://docs.python.org/3/library/warnings.html#warnings.warn) when the to-be-changed behavior is invoked. -Before major releases, pending breaking changes will be incorporated. - -One can create pull requests with breaking changes at any time, on the condition they will not be accepted until the next release is known to be a major version release. - - -## Setting Up a Development Environment - -Assuming you have used cocotb prior to reading this guide, you will already have the cocotb [installation prerequisites](https://docs.cocotb.org/en/latest/install.html) and standard development tools (editor, shell, git, etc.) installed. - -Additionally, you will need [doxygen](https://www.doxygen.nl/index.html), for building documentation, and [tox](https://pypi.org/project/tox/), for building documentation and running regression tests. - -We recommend if you are using a Linux distribution to use your system package manager to install doxygen. -Likewise, doxygen can be installed using the homebrew package manager on Mac OS. -Windows contributors should download a binary distribution installer from the main website. - -`tox` is a Python project and can be installed with `pip`: - -```command -pip install tox -``` - -Finally, you should [fork and clone](https://guides.github.com/activities/forking/) the cocotb repo. -This will allow you to make changes to the cocotb source code, and run regressions and build documentation locally. - -Now you are ready to contribute! - - -## Running Tests Locally - -First, [set up your development environment](#setting-up-a-development-environment). - -Our tests are managed by `tox`, which runs both `pytest` tests and our system of makefiles. -The regression does not end on the first failure, but continues until all tests in the `/tests` and `/examples` directories have been run. - -To run the tests locally with `tox`, you will need to select an appropriate test environment. -Valid test environments are formatted as `{your python version}-{your OS}`. -Valid python version values are `py35`, `py36`, `py37`, `py38`, or `py39`; -and valid OS values are `linux`, `macos`, or `windows`. -For example, a valid test environment is `py38-linux`. -You can see the list of valid test environments by running the below command: - -```command -tox -l -``` - -Once you know the test environment you wish to use, call `tox`. - -```command -tox -e py38-linux -``` - -At the end of the regression, if there were any test failures, the tests that failed will be printed. -Otherwise, tox will print a green `:)`. - -### Selecting a Language and Simulator for Regression - -`tox` supports the usage of the environment variables [`SIM`](https://docs.cocotb.org/en/latest/building.html#var-SIM) and [`TOPLEVEL_LANG`](https://docs.cocotb.org/en/latest/building.html#var-TOPLEVEL_LANG) to select a simulator and language to run the regression. -By default the tests will attempt to run with the Icarus Verilog simulator. - -For example, if you wanted to run tests with GHDL on Linux with Python 3.8, you would issue the following command: - -```command -SIM=ghdl TOPLEVEL_LANG=vhdl tox -e py38-linux -``` - -### Running Individual Tests Locally - -Each test under `/tests/test_cases/*/` and `/examples/*/tests/` can be run individually. -This is particularly useful if you want to run a particular test that fails the regression. - -First you must install cocotb from source by navigating to the project root directory and issuing the following command: - -```command -python -m pip install . -``` - -On Windows, you must instead install cocotb from source like so: - -```command -python -m pip install --global-option build_ext --global-option --compiler=mingw32 . -``` - -Once that has been done, you can navigate to the directory containing the test you wish to run. -Then you may issue an [appropriate](https://docs.cocotb.org/en/latest/building.html#makefile-based-test-scripts) `make` command. -For example, if you want to test with Icarus using Verilog sources: - -```command -make SIM=icarus TOPLEVEL_LANG=verilog -``` - - -## Building Documentation Locally - -First, [set up your development environment](#setting-up-a-development-environment). - -Documentation is built locally using `tox`. -The last message in the output will contain a URL to the documentation you just built. -Simply copy and paste the link into your browser to view it. -The documentation will be built in the same location on your hard drive on every run, so you only have to refresh the page to see new changes. - -To build the documentation locally on Linux or Mac, issue the following command: - -```command -tox -e docs -``` - -Building the documentation is not currently supported on Windows. - - -## Architecture and Scope of Cocotb - -Cocotb has seen adoption in a wide variety of scenarios with sometimes conflicting requirements. -To foster experimentation and to decentralize the development process the architecture of cocotb is highly modular. -A solid core forms the foundation upon which extensions can provide higher-level functionality. - -The core of cocotb are -- the infrastructure to write testbenches with coroutines, threads, etc., -- the abstraction and interaction with simulators through interfaces like VPI, GPI, etc., -- tooling to run tests, and -- core primitives to interact with the simulation: triggers, data access classes, etc. - -As a general rule, functionality beyond this core set should go into extensions. -However, none of these rules are set in stone. -They can and should be challenged at times to ensure the project stays relevant to the majority of its users. - - -## Maintainer Pre-approval - -After making changes to cocotb, changes must be approved by at least one maintainer before being included. -Out-of-scope and breaking changes ***will not be accepted***. -Also a maintainer could object to a change due to implementation approach or code quality reasons. -To potentially save you frustration and time, it is a good idea to get maintainer pre-approval on the task before starting it. - -The best way to get maintainer pre-approval is to make a [Github issue](https://github.com/cocotb/cocotb/issues). -These issues can be a place for maintainers, as well as other users, to voice opinions on a proposed change before the task is worked. -You may also propose changes on the [Gitter channel](https://gitter.im/cocotb/Lobby) or by directly contacting a [maintainer](#maintainer). - - -## How to Get Changes Merged - -Have you fixed a bug in cocotb, or want to add new functionality to it? -Cocotb follows the typical [GitHub flow](https://guides.github.com/introduction/flow/) and makes use of pull requests and reviews. -Follow the steps below to get your changes merged, i.e. integrated into the main cocotb codebase. - -1. Create an issue ticket on [cocotb's GitHub issue tracker](https://github.com/cocotb/cocotb/issues) describing the problem. - Issues are also a good place to discuss design options with others before writing code. -2. [Fork](https://help.github.com/articles/fork-a-repo/) the [cocotb GitHub repository](https://github.com/cocotb/cocotb) into your personal namespace. -3. Create a new branch off the `master` branch for your set of changes. - Use one branch per "topic," i.e. per set of changes which belong together. -4. Create one or multiple commits to address the issue. - Make sure to read and follow the [Patch Requirements](#patch-requirements) when preparing your commits. -5. Create new [pull request (PR)](https://github.com/cocotb/cocotb/pulls). -6. When you submit (or update) the pull request, a suite of regression tests will run. - If any of them turns "red," i.e. reports a failure, you most likely need to fix your code before it can be merged. -7. The pull request needs to be reviewed by at least one maintainer. - We aim to give feedback to all pull requests within a week, but as so often, life can get in the way. - If you receive no feedback from a maintainer within that time, please contact them directly (e.g. on [Gitter](https://gitter.im/cocotb) or email). - You can find a [list of all maintainers](#maintainers) below. - If a maintainer asks you to explain or modify code, try to do so. -8. Once your code has at least one positive review from a maintainer and no maintainer strongly objects it your code is ready to be merged into the `master` branch. - - -## Patch Requirements - -All changes which should go into the main codebase of cocotb must follow this set of requirements. - -- The code must be within the [scope of cocotb](#architecture-and-scope-of-cocotb). -- All code must be licensed under the [Revised BSD License](https://github.com/cocotb/cocotb/blob/master/LICENSE). - By contributing to this project you signal your agreement with these license terms. -- All code must follow the established coding standards. - For Python code, follow the [PEP 8](https://www.python.org/dev/peps/pep-0008/) style guide. -- All code must pass existing tests. - New functionality must be accompanied by tests, and bug fixes should add tests to increase the test coverage and prevent regressions. -- If code changes or enhances documented behavior the documentation should be updated. -- If a change is user-visible, a newsfragment should be added to `documentation/source/newsfragments`. -- All pull requests must be accepted by at least one maintainer, with no maintainer strongly objecting. - Reviews must be performed by a person other than the primary author of the code. -- All commits should follow established best practices when creating a commit message: - - The first line of the commit message is the short summary of what the code change does. - Keep this line below 50 characters. - - Then have one blank line. - - Now comes the long description of the commit. - Use this text to discuss things which are not obvious from the code, especially *why* changes were made. - Include the GitHub issue number (if one exists) in the form "Fixes #nnn" ([read more about that](https://help.github.com/articles/closing-issues-using-keywords/)). - Keep each description line below 72 characters. -- Use the following header for new non-example files: - ```python - # Copyright cocotb contributors - # Licensed under the Revised BSD License, see LICENSE for details. - # SPDX-License-Identifier: BSD-3-Clause - ``` -- Use the following header for new example files: - ```python - # This file is public domain, it can be freely copied without restrictions. - # SPDX-License-Identifier: CC0-1.0 - ``` - -## Managing of Issues and Pull Requests - -The cocotb project makes use of GitHub labels attached to issues and pull requests to structure the development process. -Each issue and pull request can have multiple labels assigned. - -The `type` labels define the type of issue or PR: -- `type:bug`: a bug in existing functionality -- `type:feature`: new functionality -- `type:question`: a support question -- `type:cleanup`: cleanup or refactoring on code, documentation, or other areas -- `type:deprecation`: API that should warn and eventually be removed - -The `status` labels give a quick impression of the current status of the issue or PR: -- `status:worksforme`: the issue it not reproducible, or intended behavior (i.e. not a bug) -- `status:blocked`: further progress is blocked by a dependency, e.g. other code which must be commited first. -- `status:needs-info`: feedback from someone is required. The issue/PR text gives more details. -- `status:duplicate`: the same issue is already being handled in another issue/PR. -- `status:close?`: issues which can probably be closed, but need a second pair of eyes -- `status:needs-proprietary-testing`: Help needed testing on a proprietary tool -- `status:out-of-scope`: An issue or PR that was closed because the feature or bug was deemed to be out of scope - -For the use in pull requests the following additional status labels are defined: -- `status:needs-review`: this PR needs at least one review -- `status:changes-requested`: changes are requested to the code -- `status:ready-for-merge`: this PR is ready (according to the [Patch Requirements](#patch-requirements)) to be merged -- `status:needs-rebase`: needs a git rebase -- `status:needs-newsfragment`: Needs a towncrier newsfragment for the changelog - -The `category` labels help maintainers to filter issues which are relevant to their area of expertise: -- `category:OS:MacOS`: Mac OS/OS X specific issues -- `category:OS:Linux`: Linux specific issues -- `category:OS:Windows`: Microsoft Windows-specific issues -- `category:simulators`: simulator support, including VPI/GPI/etc. -- `category:simulators:activehdl`: Aldec Active-HDL -- `category:simulators:cvc`: Tachyon CVC -- `category:simulators:ghdl`: GHDL -- `category:simulators:icarus`: Icarus Verilog (iverilog) -- `category:simulators:ius`: Cadence Incisive (IUS) -- `category:simulators:modelsim`: Mentor Modelsim -- `category:simulators:questa`: Mentor Questa -- `category:simulators:riviera`: Aldec Riviera-PRO -- `category:simulators:vcs`: Synopsys VCS -- `category:simulators:verilator`: Verilator -- `category:simulators:xcelium`: Cadence Xcelium -- `category:codebase:gpi`: relating to the GPI or one of the implementation -- `category:codebase:pygpi`: relating to the Python wrapper around the GPI (embed library and simulator module) -- `category:codebase:scheduler`: relating to the coroutine scheduler, triggers, or coroutine objects -- `category:codebase:test-runner`: relating to code for automating test runs (regression manager) -- `category:codebase:handle`: relating to handles or handle types (BinaryValue) -- `category:codebase:project-automation`: relating to included project automation (makefiles) -- `category:codebase:testbenching`: relating to testbenching components (Drivers, Monitors, etc.) -- `category:building`: relating to build C/C++ libraries and extension modules -- `category:packaging`: issues related to (PyPi) packaging, etc. -- `category:docs`: documentation issues and fixes -- `category:extensions`: cocotb extensions -- `category:performance`: performance topics -- `category:tests-ci`: continuous integration and unit tests - -To help new contributors find a good issue to work on one more label is used (following [GitHub standard practices](#https://help.github.com/articles/helping-new-contributors-find-your-project-with-labels/)): -- `good first issue`: this issue is a good starting point for new contributors. - The issue should give an actionable description of what to do to complete this task, along with contact information of a mentor for this task. - -cocotb explicitly uses no priority labels, as experience indicates that they provide little value. - -Issues and pull requests which are invalid, or where feedback is lacking for four weeks, should be closed. - -## Cocotb Releases - -cocotb aims to keep the `master` branch always in a releasable state. -At least four times a year an official release should be created. -It is the job of the maintainers to find a suitable time for a release, to communicate it to the community, and to coordinate it. - -## Maintainers - -Cocotb uses a shared maintainer model. -Most maintainers are experts in part of the cocotb codebase, and are primarily responsible for reviews in this area. - -- Kaleb Barrett (@ktbarrett) -- Tomasz Hemperek (@themperek) -- Marlon James (@marlonjames) -- Colin Marquardt (@cmarqu) -- Philipp Wagner (@imphil) -- Eric Wieser (@eric-wieser) - -Founders - -- Chris Higgs (@chiggs) -- Stuart Hodgson (@stuarthodgson) - -### Getting in Contact with a Maintainer - -All of the maintainers are active on the [Gitter channel](https://github.com/cocotb/cocotb). -They prefer inquiries go through direct messages on Gitter, -or by mentioning them in the main [cocotb Gitter channel](https://gitter.im/cocotb/Lobby) using `@{maintainer name}`. -Maintainers are unpaid volunteers, so it might take a while for a maintainer to get back to you. - - -## Code of Conduct - -The cocotb development community aims to be welcoming to everyone. -The [FOSSi Foundation Code of Conduct](https://www.fossi-foundation.org/code-of-conduct) applies. -Please contact any of the maintainers if you feel uncomfortable in the cocotb development community. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index c594aee1..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,4 +0,0 @@ -recursive-include cocotb/share * -include README.md -include LICENSE -include cocotb_build_libs.py diff --git a/README.md b/README.md deleted file mode 100644 index 833bee16..00000000 --- a/README.md +++ /dev/null @@ -1,118 +0,0 @@ -**cocotb** is a coroutine based cosimulation library for writing VHDL and Verilog testbenches in Python. - -[![Documentation Status](https://readthedocs.org/projects/cocotb/badge/?version=latest)](https://docs.cocotb.org/en/latest/) -[![Build Status](https://github.com/cocotb/cocotb/workflows/Regression%20Tests/badge.svg)](https://github.com/cocotb/cocotb/actions?query=workflow%3A%22Regression+Tests%22) -[![PyPI](https://img.shields.io/pypi/dm/cocotb.svg?label=PyPI%20downloads)](https://pypi.org/project/cocotb/) -[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/cocotb/cocotb) -[![codecov](https://codecov.io/gh/cocotb/cocotb/branch/master/graph/badge.svg)](https://codecov.io/gh/cocotb/cocotb) - -* Read the [documentation](https://docs.cocotb.org) -* Get involved: - * [Raise a bug / request an enhancement](https://github.com/cocotb/cocotb/issues/new) (Requires a GitHub account) - * [Join the mailing list](https://lists.librecores.org/listinfo/cocotb) - * [Join the Gitter chat room](https://gitter.im/cocotb) - -## Installation - -The current stable version of cocotb requires: - -- Python 3.5+ -- A C++11 compiler -- An HDL simulator (such as [Icarus Verilog](https://docs.cocotb.org/en/stable/simulator_support.html#icarus-verilog), -[Verilator](https://docs.cocotb.org/en/stable/simulator_support.html#verilator), -[GHDL](https://docs.cocotb.org/en/stable/simulator_support.html#ghdl) or -[other simulator](https://docs.cocotb.org/en/stable/simulator_support.html)) - -After installing these dependencies, the latest stable version of cocotb can be installed with pip. - -```command -pip install cocotb -``` - -**!!! Windows Users !!!** See [here](https://docs.cocotb.org/en/stable/install.html) for installation instructions. - -For more details on installation, including prerequisites, -see [the documentation](https://docs.cocotb.org/en/stable/install.html). - -For details on how to install the *development* version of cocotb, -see [the preliminary documentation of the future release](https://docs.cocotb.org/en/latest/install_devel.html#install-devel). - -## Usage - -As a first trivial introduction to cocotb, the following example "tests" a flip-flop. - -First, we need a hardware design which we can test. For this example, create a file `dff.sv` with SystemVerilog code for a simple [D flip-flop](https://en.wikipedia.org/wiki/Flip-flop_(electronics)#D_flip-flop). You could also use any other language a [cocotb-supported simulator](https://docs.cocotb.org/en/stable/simulator_support.html) understands, e.g. VHDL. - -```systemverilog -// dff.sv - -`timescale 1us/1ns - -module dff ( - output logic q, - input logic clk, d -); - -always @(posedge clk) begin - q <= d; -end - -endmodule -``` - -An example of a simple randomized cocotb testbench: - -```python -# test_dff.py - -import random -import cocotb -from cocotb.clock import Clock -from cocotb.triggers import FallingEdge - -@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 - - 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) -``` - -A simple Makefile: - -```make -# Makefile - -TOPLEVEL_LANG = verilog -VERILOG_SOURCES = $(shell pwd)/dff.sv -TOPLEVEL = dff -MODULE = test_dff - -include $(shell cocotb-config --makefiles)/Makefile.sim -``` - -In order to run the test with Icarus Verilog, execute: - -```command -make SIM=icarus -``` - -[![asciicast](https://asciinema.org/a/317220.svg)](https://asciinema.org/a/317220) - -For more information please see the [cocotb documentation](https://docs.cocotb.org/) -and [our wiki](https://github.com/cocotb/cocotb/wiki). - -## Tutorials, examples and related projects - -* the tutorial section [in the official documentation](https://docs.cocotb.org/) -* [cocotb-based USB 1.1 test suite](https://github.com/antmicro/usb-test-suite-build) for FPGA IP, with testbenches for a variety of open source USB cores -* [`cocotb-coverage`](https://github.com/mciepluc/cocotb-coverage), an extension for Functional Coverage and Constrained Randomization -* [`uvm-python`](https://github.com/tpoikela/uvm-python), an almost 1:1 port of UVM 1.2 to Python -* our wiki [on extension modules](https://github.com/cocotb/cocotb/wiki/Further-Resources#extension-modules-cocotbext) -* the list of [GitHub projects depending on cocotb](https://github.com/cocotb/cocotb/network/dependents) diff --git a/cocotb/ANSI.py b/cocotb/ANSI.py deleted file mode 100644 index 38804c66..00000000 --- a/cocotb/ANSI.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) 2013, 2018 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -''' - Some constants for doing ANSI stuff. -''' -# flake8: noqa (skip this file for flake8: pypi.python.org/pypi/flake8) -_ESCAPE = "\033[" - -# see https://en.wikipedia.org/wiki/ANSI_escape_code#Colors - -DEFAULT_FG = _ESCAPE + "39m" -DEFAULT_BG = _ESCAPE + "49m" -DEFAULT = DEFAULT_BG + DEFAULT_FG - -BLACK_FG = _ESCAPE + "30m" -RED_FG = _ESCAPE + "31m" -GREEN_FG = _ESCAPE + "32m" -YELLOW_FG = _ESCAPE + "33m" -BLUE_FG = _ESCAPE + "34m" -MAGENTA_FG = _ESCAPE + "35m" -CYAN_FG = _ESCAPE + "36m" -WHITE_FG = _ESCAPE + "37m" - -BLACK_BG = _ESCAPE + "40m" -RED_BG = _ESCAPE + "41m" -GREEN_BG = _ESCAPE + "42m" -YELLOW_BG = _ESCAPE + "43m" -BLUE_BG = _ESCAPE + "44m" -MAGENTA_BG = _ESCAPE + "45m" -CYAN_BG = _ESCAPE + "46m" -WHITE_BG = _ESCAPE + "47m" - -BRIGHT_BLACK_FG = _ESCAPE + "90m" -BRIGHT_RED_FG = _ESCAPE + "91m" -BRIGHT_GREEN_FG = _ESCAPE + "92m" -BRIGHT_YELLOW_FG = _ESCAPE + "93m" -BRIGHT_BLUE_FG = _ESCAPE + "94m" -BRIGHT_MAGENTA_FG = _ESCAPE + "95m" -BRIGHT_CYAN_FG = _ESCAPE + "96m" -BRIGHT_WHITE_FG = _ESCAPE + "97m" - -BRIGHT_BLACK_BG = _ESCAPE + "100m" -BRIGHT_RED_BG = _ESCAPE + "101m" -BRIGHT_GREEN_BG = _ESCAPE + "102m" -BRIGHT_YELLOW_BG = _ESCAPE + "103m" -BRIGHT_BLUE_BG = _ESCAPE + "104m" -BRIGHT_MAGENTA_BG = _ESCAPE + "105m" -BRIGHT_CYAN_BG = _ESCAPE + "106m" -BRIGHT_WHITE_BG = _ESCAPE + "107m" - - -COLOR_DEFAULT = DEFAULT - -COLOR_INFO = BLUE_FG -COLOR_WARNING = YELLOW_FG -COLOR_ERROR = RED_FG -COLOR_CRITICAL = RED_BG + BLACK_FG -COLOR_TEST = BLUE_BG + BLACK_FG - -COLOR_HILITE_SUMMARY = WHITE_FG + RED_BG -COLOR_HILITE_HEXDIFF_DEFAULT = YELLOW_FG -COLOR_HILITE_HEXDIFF_1 = CYAN_FG -COLOR_HILITE_HEXDIFF_2 = RED_FG -COLOR_HILITE_HEXDIFF_3 = MAGENTA_BG -COLOR_HILITE_HEXDIFF_4 = CYAN_BG + BLACK_FG diff --git a/cocotb/__init__.py b/cocotb/__init__.py deleted file mode 100644 index f3e2d333..00000000 --- a/cocotb/__init__.py +++ /dev/null @@ -1,315 +0,0 @@ -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. - -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. - -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -""" -Cocotb is a coroutine, cosimulation framework for writing testbenches in Python. - -See https://docs.cocotb.org for full documentation -""" -import os -import sys -import logging -import threading -import random -import time -import warnings -from typing import Dict, List, Union -from collections.abc import Coroutine - -import cocotb.handle -import cocotb.log -from cocotb.scheduler import Scheduler -from cocotb.regression import RegressionManager -from cocotb.decorators import RunningTask - -# Things we want in the cocotb namespace -from cocotb.decorators import test, coroutine, hook, function, external # noqa: F401 - -from ._version import __version__ - - -def _setup_logging(): - global log - - def _reopen_stream_with_buffering(stream_name): - try: - if not getattr(sys, stream_name).isatty(): - setattr(sys, stream_name, os.fdopen(getattr(sys, stream_name).fileno(), 'w', 1)) - return True - return False - except Exception as e: - return e - - # If stdout/stderr are not TTYs, Python may not have opened them with line - # buffering. In that case, try to reopen them with line buffering - # explicitly enabled. This ensures that prints such as stack traces always - # appear. Continue silently if this fails. - _stdout_buffer_result = _reopen_stream_with_buffering('stdout') - _stderr_buffer_result = _reopen_stream_with_buffering('stderr') - - # Don't set the logging up until we've attempted to fix the standard IO, - # otherwise it will end up connected to the unfixed IO. - cocotb.log.default_config() - log = logging.getLogger(__name__) - - # we can't log these things until the logging is set up! - if _stderr_buffer_result is True: - log.debug("Reopened stderr with line buffering") - if _stdout_buffer_result is True: - log.debug("Reopened stdout with line buffering") - if isinstance(_stdout_buffer_result, Exception) or isinstance(_stderr_buffer_result, Exception): - if isinstance(_stdout_buffer_result, Exception): - log.warning("Failed to ensure that stdout is line buffered", exc_info=_stdout_buffer_result) - if isinstance(_stderr_buffer_result, Exception): - log.warning("Failed to ensure that stderr is line buffered", exc_info=_stderr_buffer_result) - log.warning("Some stack traces may not appear because of this.") - - del _stderr_buffer_result, _stdout_buffer_result - - -# Singleton scheduler instance -# NB this cheekily ensures a singleton since we're replacing the reference -# so that cocotb.scheduler gives you the singleton instance and not the -# scheduler package - -scheduler = None # type: cocotb.scheduler.Scheduler -"""The global scheduler instance.""" - -regression_manager = None # type: cocotb.regression.RegressionManager -"""The global regression manager instance.""" - -argv = None # type: List[str] -"""The argument list as seen by the simulator""" - -argc = None # type: int -"""The length of :data:`cocotb.argv`""" - -plusargs = None # type: Dict[str, Union[bool, str]] -"""A dictionary of "plusargs" handed to the simulation. See :make:var:`PLUSARGS` for details.""" - -LANGUAGE = os.getenv("TOPLEVEL_LANG") # type: str -"""The value of :make:var:`TOPLEVEL_LANG`""" - -SIM_NAME = None # type: str -"""The running simulator product information. ``None`` if :mod:`cocotb` was not loaded from a simulator""" - -SIM_VERSION = None # type: str -"""The version of the running simulator. ``None`` if :mod:`cocotb` was not loaded from a simulator""" - -RANDOM_SEED = None # type: int -""" -The value passed to the Python default random number generator. -See :envvar:`RANDOM_SEED` for details on how the value is computed. -""" - -_library_coverage = None -""" used for cocotb library coverage """ - -top = None # type: cocotb.handle.SimHandleBase -r""" -A handle to the :envvar:`TOPLEVEL` entity/module. - -This is equivalent to the :term:`DUT` parameter given to cocotb tests, so it can be used wherever that variable can be used. -It is particularly useful for extracting information about the :term:`DUT` in module-level class and function definitions; -and in parameters to :class:`.TestFactory`\ s. -``None`` if :mod:`cocotb` was not loaded from a simulator. -""" - - -def fork(coro: Union[RunningTask, Coroutine]) -> RunningTask: - """ Schedule a coroutine to be run concurrently. See :ref:`coroutines` for details on its use. """ - return scheduler.add(coro) - - -# FIXME is this really required? -_rlock = threading.RLock() - - -def mem_debug(port): - import cocotb.memdebug - cocotb.memdebug.start(port) - - -def _initialise_testbench(argv_): - """Initialize testbench. - - This function is called after the simulator has elaborated all - entities and is ready to run the test. - - The test must be defined by the environment variables - :envvar:`MODULE` and :envvar:`TESTCASE`. - - The environment variable :envvar:`COCOTB_HOOKS`, if present, contains a - comma-separated list of modules to be executed before the first test. - """ - _rlock.acquire() - - if "COCOTB_LIBRARY_COVERAGE" in os.environ: - import coverage - - global _library_coverage - _library_coverage = coverage.coverage( - data_file=".coverage.cocotb", - branch=True, - include=["{}/*".format(os.path.dirname(__file__))]) - _library_coverage.start() - - global argc, argv - argv = argv_ - argc = len(argv) - - root_name = os.getenv("TOPLEVEL") - if root_name is not None: - if root_name == "": - root_name = None - elif '.' in root_name: - # Skip any library component of the toplevel - root_name = root_name.split(".", 1)[1] - - # sys.path normally includes "" (the current directory), but does not appear to when python is embedded. - # Add it back because users expect to be able to import files in their test directory. - # TODO: move this to gpi_embed.cpp - sys.path.insert(0, "") - - _setup_logging() - - # From https://www.python.org/dev/peps/pep-0565/#recommended-filter-settings-for-test-runners - # If the user doesn't want to see these, they can always change the global - # warning settings in their test module. - if not sys.warnoptions: - warnings.simplefilter("default") - - from cocotb import simulator - - global SIM_NAME, SIM_VERSION - SIM_NAME = simulator.get_simulator_product().strip() - SIM_VERSION = simulator.get_simulator_version().strip() - - cocotb.log.info("Running on {} version {}".format(SIM_NAME, SIM_VERSION)) - - memcheck_port = os.getenv('MEMCHECK') - if memcheck_port is not None: - mem_debug(int(memcheck_port)) - - log.info("Running tests with cocotb v%s from %s" % - (__version__, os.path.dirname(__file__))) - - # Create the base handle type - - process_plusargs() - - global scheduler - scheduler = Scheduler() - - # Seed the Python random number generator to make this repeatable - global RANDOM_SEED - RANDOM_SEED = os.getenv('RANDOM_SEED') - - if RANDOM_SEED is None: - if 'ntb_random_seed' in plusargs: - RANDOM_SEED = eval(plusargs['ntb_random_seed']) - elif 'seed' in plusargs: - RANDOM_SEED = eval(plusargs['seed']) - else: - RANDOM_SEED = int(time.time()) - log.info("Seeding Python random module with %d" % (RANDOM_SEED)) - else: - RANDOM_SEED = int(RANDOM_SEED) - log.info("Seeding Python random module with supplied seed %d" % (RANDOM_SEED)) - random.seed(RANDOM_SEED) - - # Setup DUT object - from cocotb import simulator - - handle = simulator.get_root_handle(root_name) - if not handle: - raise RuntimeError("Can not find root handle ({})".format(root_name)) - - global top - top = cocotb.handle.SimHandle(handle) - - try: - import pytest - except ImportError: - log.warning("Pytest not found, assertion rewriting will not occur") - else: - try: - # Install the assertion rewriting hook, which must be done before we - # import the test modules. - from _pytest.config import Config - from _pytest.assertion import install_importhook - pytest_conf = Config.fromdictargs([], {}) - install_importhook(pytest_conf) - except Exception: - log.exception( - "Configuring the assertion rewrite hook using pytest {} failed. " - "Please file a bug report!".format(pytest.__version__)) - - # start Regression Manager - global regression_manager - regression_manager = RegressionManager.from_discovery(top) - regression_manager.execute() - - _rlock.release() - return True - - -def _sim_event(level, message): - """Function that can be called externally to signal an event.""" - # SIM_INFO = 0 - SIM_TEST_FAIL = 1 - SIM_FAIL = 2 - from cocotb.result import TestFailure, SimFailure - - if level is SIM_TEST_FAIL: - scheduler.log.error("Failing test at simulator request") - scheduler._finish_test(TestFailure("Failure from external source: {}".format(message))) - elif level is SIM_FAIL: - # We simply return here as the simulator will exit - # so no cleanup is needed - msg = "Failing test at simulator request before test run completion: {}".format(message) - scheduler.log.error(msg) - scheduler._finish_scheduler(SimFailure(msg)) - else: - scheduler.log.error("Unsupported sim event") - - return True - - -def process_plusargs(): - - global plusargs - - plusargs = {} - - for option in cocotb.argv: - if option.startswith('+'): - if option.find('=') != -1: - (name, value) = option[1:].split('=') - plusargs[name] = value - else: - plusargs[option[1:]] = True diff --git a/cocotb/_py_compat.py b/cocotb/_py_compat.py deleted file mode 100644 index d6a3a092..00000000 --- a/cocotb/_py_compat.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) cocotb contributors -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the copyright holder nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -""" -Backports and compatibility shims for newer python features. - -These are for internal use - users should use a third party library like `six` -if they want to use these shims in their own code -""" -import sys - - -# backport of Python 3.7's contextlib.nullcontext -class nullcontext: - """Context manager that does no additional processing. - Used as a stand-in for a normal context manager, when a particular - block of code is only sometimes used with a normal context manager: - - cm = optional_cm if condition else nullcontext() - with cm: - # Perform operation, using optional_cm if condition is True - """ - - def __init__(self, enter_result=None): - self.enter_result = enter_result - - def __enter__(self): - return self.enter_result - - def __exit__(self, *excinfo): - pass - - -# On python 3.7 onwards, `dict` is guaranteed to preserve insertion order. -# Since `OrderedDict` is a little slower that `dict`, we prefer the latter -# when possible. -if sys.version_info[:2] >= (3, 7): - insertion_ordered_dict = dict -else: - import collections - insertion_ordered_dict = collections.OrderedDict diff --git a/cocotb/_sim_versions.py b/cocotb/_sim_versions.py deleted file mode 100644 index 2288c8e5..00000000 --- a/cocotb/_sim_versions.py +++ /dev/null @@ -1,119 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause - -""" -Classes to compare simulation versions. - -These are for cocotb-internal use only. - -.. warning:: - These classes silently allow comparing versions of different simulators. -""" - -from distutils.version import LooseVersion - - -class ActivehdlVersion(LooseVersion): - """Version numbering class for Aldec Active-HDL. - - NOTE: unsupported versions exist, e.g. - ActivehdlVersion("10.5a.12.6914") > ActivehdlVersion("10.5.216.6767") - """ - pass - - -class CvcVersion(LooseVersion): - """Version numbering class for Tachyon DA CVC. - - Example: - >>> CvcVersion("OSS_CVC_7.00b-x86_64-rhel6x of 07/07/14 (Linux-elf)") > CvcVersion("OSS_CVC_7.00a-x86_64-rhel6x of 07/07/14 (Linux-elf)") - True - """ - pass - - -class GhdlVersion(LooseVersion): - """Version numbering class for GHDL.""" - pass - - -class IcarusVersion(LooseVersion): - """Version numbering class for Icarus Verilog. - - Example: - >>> IcarusVersion("11.0 (devel)") > IcarusVersion("10.3 (stable)") - True - >>> IcarusVersion("10.3 (stable)") <= IcarusVersion("10.3 (stable)") - True - """ - pass - - -class ModelsimVersion(LooseVersion): - """Version numbering class for Mentor ModelSim.""" - pass - - -class QuestaVersion(LooseVersion): - """Version numbering class for Mentor Questa. - - Example: - >>> QuestaVersion("10.7c 2018.08") > QuestaVersion("10.7b 2018.06") - True - >>> QuestaVersion("2020.1 2020.01") > QuestaVersion("10.7c 2018.08") - True - """ - pass - - -class RivieraVersion(LooseVersion): - """Version numbering class for Aldec Riviera-PRO. - - Example: - >>> RivieraVersion("2019.10.138.7537") == RivieraVersion("2019.10.138.7537") - True - """ - pass - - -class VcsVersion(LooseVersion): - """Version numbering class for Synopsys VCS. - - Example: - >>> VcsVersion("Q-2020.03-1_Full64") > VcsVersion("K-2015.09_Full64") - True - """ - pass - - -class VerilatorVersion(LooseVersion): - """Version numbering class for Verilator. - - Example: - >>> VerilatorVersion("4.032 2020-04-04") > VerilatorVersion("4.031 devel") - True - """ - pass - - -class XceliumVersion(LooseVersion): - """Version numbering class for Cadence Xcelium. - - Example: - >>> XceliumVersion("20.06-g183") > XceliumVersion("20.03-s002") - True - >>> XceliumVersion("20.07-e501") > XceliumVersion("20.06-g183") - True - """ - pass - - -class IusVersion(XceliumVersion): # inherit everything from Xcelium - """Version numbering class for Cadence IUS. - - Example: - >>> IusVersion("15.20-s050") > IusVersion("15.20-s049") - True - """ - pass diff --git a/cocotb/_vendor/__init__.py b/cocotb/_vendor/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/cocotb/_vendor/find_libpython/__init__.py b/cocotb/_vendor/find_libpython/__init__.py deleted file mode 100644 index 1a5d4b6e..00000000 --- a/cocotb/_vendor/find_libpython/__init__.py +++ /dev/null @@ -1,383 +0,0 @@ -""" -Locate libpython associated with this Python executable. -""" - -# License -# -# Copyright 2018, Takafumi Arakaki -# -# 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. - -from logging import getLogger -import ctypes.util -import functools -import os -import sys -import sysconfig - -logger = getLogger("find_libpython") - -is_windows = os.name == "nt" -is_apple = sys.platform == "darwin" - -SHLIB_SUFFIX = sysconfig.get_config_var("SHLIB_SUFFIX") -if SHLIB_SUFFIX is None: - if is_windows: - SHLIB_SUFFIX = ".dll" - else: - SHLIB_SUFFIX = ".so" -if is_apple: - # sysconfig.get_config_var("SHLIB_SUFFIX") can be ".so" in macOS. - # Let's not use the value from sysconfig. - SHLIB_SUFFIX = ".dylib" - - -def linked_libpython(): - """ - Find the linked libpython using dladdr (in *nix). - - Calling this in Windows always return `None` at the moment. - - Returns - ------- - path : str or None - A path to linked libpython. Return `None` if statically linked. - """ - if is_windows: - return None - return _linked_libpython_unix() - - -class Dl_info(ctypes.Structure): - _fields_ = [ - ("dli_fname", ctypes.c_char_p), - ("dli_fbase", ctypes.c_void_p), - ("dli_sname", ctypes.c_char_p), - ("dli_saddr", ctypes.c_void_p), - ] - - -def _linked_libpython_unix(): - libdl = ctypes.CDLL(ctypes.util.find_library("dl")) - libdl.dladdr.argtypes = [ctypes.c_void_p, ctypes.POINTER(Dl_info)] - libdl.dladdr.restype = ctypes.c_int - - dlinfo = Dl_info() - retcode = libdl.dladdr( - ctypes.cast(ctypes.pythonapi.Py_GetVersion, ctypes.c_void_p), - ctypes.pointer(dlinfo), - ) - if retcode == 0: # means error - return None - path = os.path.realpath(dlinfo.dli_fname.decode()) - if path == os.path.realpath(sys.executable): - return None - return path - - -def library_name(name, suffix=SHLIB_SUFFIX, is_windows=is_windows): - """ - Convert a file basename `name` to a library name (no "lib" and ".so" etc.) - - >>> library_name("libpython3.7m.so") # doctest: +SKIP - 'python3.7m' - >>> library_name("libpython3.7m.so", suffix=".so", is_windows=False) - 'python3.7m' - >>> library_name("libpython3.7m.dylib", suffix=".dylib", is_windows=False) - 'python3.7m' - >>> library_name("python37.dll", suffix=".dll", is_windows=True) - 'python37' - """ - if not is_windows and name.startswith("lib"): - name = name[len("lib") :] - if suffix and name.endswith(suffix): - name = name[: -len(suffix)] - return name - - -def append_truthy(list, item): - if item: - list.append(item) - - -def uniquifying(items): - """ - Yield items while excluding the duplicates and preserving the order. - - >>> list(uniquifying([1, 2, 1, 2, 3])) - [1, 2, 3] - """ - seen = set() - for x in items: - if x not in seen: - yield x - seen.add(x) - - -def uniquified(func): - """ Wrap iterator returned from `func` by `uniquifying`. """ - - @functools.wraps(func) - def wrapper(*args, **kwds): - return uniquifying(func(*args, **kwds)) - - return wrapper - - -@uniquified -def candidate_names(suffix=SHLIB_SUFFIX): - """ - Iterate over candidate file names of libpython. - - Yields - ------ - name : str - Candidate name libpython. - """ - LDLIBRARY = sysconfig.get_config_var("LDLIBRARY") - if LDLIBRARY: - yield LDLIBRARY - - LIBRARY = sysconfig.get_config_var("LIBRARY") - if LIBRARY: - yield os.path.splitext(LIBRARY)[0] + suffix - - dlprefix = "" if is_windows else "lib" - sysdata = dict( - v=sys.version_info, - # VERSION is X.Y in Linux/macOS and XY in Windows: - VERSION=( - sysconfig.get_config_var("VERSION") - or "{v.major}.{v.minor}".format(v=sys.version_info) - ), - ABIFLAGS=( - sysconfig.get_config_var("ABIFLAGS") - or sysconfig.get_config_var("abiflags") - or "" - ), - ) - - for stem in [ - "python{VERSION}{ABIFLAGS}".format(**sysdata), - "python{VERSION}".format(**sysdata), - "python{v.major}".format(**sysdata), - "python", - ]: - yield dlprefix + stem + suffix - - -@uniquified -def candidate_paths(suffix=SHLIB_SUFFIX): - """ - Iterate over candidate paths of libpython. - - Yields - ------ - path : str or None - Candidate path to libpython. The path may not be a fullpath - and may not exist. - """ - - yield linked_libpython() - - # List candidates for directories in which libpython may exist - lib_dirs = [] - append_truthy(lib_dirs, sysconfig.get_config_var("LIBPL")) - append_truthy(lib_dirs, sysconfig.get_config_var("srcdir")) - append_truthy(lib_dirs, sysconfig.get_config_var("LIBDIR")) - - # LIBPL seems to be the right config_var to use. It is the one - # used in python-config when shared library is not enabled: - # https://github.com/python/cpython/blob/v3.7.0/Misc/python-config.in#L55-L57 - # - # But we try other places just in case. - - if is_windows: - lib_dirs.append(os.path.join(os.path.dirname(sys.executable))) - else: - lib_dirs.append( - os.path.join(os.path.dirname(os.path.dirname(sys.executable)), "lib") - ) - - # For macOS: - append_truthy(lib_dirs, sysconfig.get_config_var("PYTHONFRAMEWORKPREFIX")) - - lib_dirs.append(sys.exec_prefix) - lib_dirs.append(os.path.join(sys.exec_prefix, "lib")) - - lib_basenames = list(candidate_names(suffix=suffix)) - - for directory in lib_dirs: - for basename in lib_basenames: - yield os.path.join(directory, basename) - - # In macOS and Windows, ctypes.util.find_library returns a full path: - for basename in lib_basenames: - yield ctypes.util.find_library(library_name(basename)) - - -# Possibly useful links: -# * https://packages.ubuntu.com/bionic/amd64/libpython3.6/filelist -# * https://github.com/Valloric/ycmd/issues/518 -# * https://github.com/Valloric/ycmd/pull/519 - - -def normalize_path(path, suffix=SHLIB_SUFFIX, is_apple=is_apple): - """ - Normalize shared library `path` to a real path. - - If `path` is not a full path, `None` is returned. If `path` does - not exists, append `SHLIB_SUFFIX` and check if it exists. - Finally, the path is canonicalized by following the symlinks. - - Parameters - ---------- - path : str ot None - A candidate path to a shared library. - """ - if not path: - return None - if not os.path.isabs(path): - return None - if os.path.exists(path): - return os.path.realpath(path) - if os.path.exists(path + suffix): - return os.path.realpath(path + suffix) - if is_apple: - return normalize_path(_remove_suffix_apple(path), suffix=".so", is_apple=False) - return None - - -def _remove_suffix_apple(path): - """ - Strip off .so or .dylib. - - >>> _remove_suffix_apple("libpython.so") - 'libpython' - >>> _remove_suffix_apple("libpython.dylib") - 'libpython' - >>> _remove_suffix_apple("libpython3.7") - 'libpython3.7' - """ - if path.endswith(".dylib"): - return path[: -len(".dylib")] - if path.endswith(".so"): - return path[: -len(".so")] - return path - - -@uniquified -def finding_libpython(): - """ - Iterate over existing libpython paths. - - The first item is likely to be the best one. - - Yields - ------ - path : str - Existing path to a libpython. - """ - logger.debug("is_windows = %s", is_windows) - logger.debug("is_apple = %s", is_apple) - for path in candidate_paths(): - logger.debug("Candidate: %s", path) - normalized = normalize_path(path) - if normalized: - logger.debug("Found: %s", normalized) - yield normalized - else: - logger.debug("Not found.") - - -def find_libpython(): - """ - Return a path (`str`) to libpython or `None` if not found. - - Parameters - ---------- - path : str or None - Existing path to the (supposedly) correct libpython. - """ - for path in finding_libpython(): - return os.path.realpath(path) - - -def print_all(items): - for x in items: - print(x) - - -def cli_find_libpython(cli_op, verbose): - import logging - - # Importing `logging` module here so that using `logging.debug` - # instead of `logger.debug` outside of this function becomes an - # error. - - if verbose: - logging.basicConfig(format="%(levelname)s %(message)s", level=logging.DEBUG) - - if cli_op == "list-all": - print_all(finding_libpython()) - elif cli_op == "candidate-names": - print_all(candidate_names()) - elif cli_op == "candidate-paths": - print_all(p for p in candidate_paths() if p and os.path.isabs(p)) - else: - path = find_libpython() - if path is None: - return 1 - print(path, end="") - - -def main(args=None): - import argparse - - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument( - "--verbose", "-v", action="store_true", help="Print debugging information." - ) - - group = parser.add_mutually_exclusive_group() - group.add_argument( - "--list-all", - action="store_const", - dest="cli_op", - const="list-all", - help="Print list of all paths found.", - ) - group.add_argument( - "--candidate-names", - action="store_const", - dest="cli_op", - const="candidate-names", - help="Print list of candidate names of libpython.", - ) - group.add_argument( - "--candidate-paths", - action="store_const", - dest="cli_op", - const="candidate-paths", - help="Print list of candidate paths of libpython.", - ) - - ns = parser.parse_args(args) - parser.exit(cli_find_libpython(**vars(ns))) diff --git a/cocotb/binary.py b/cocotb/binary.py deleted file mode 100755 index b182a5b9..00000000 --- a/cocotb/binary.py +++ /dev/null @@ -1,761 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import os -import random -import re -import warnings - - -_RESOLVE_TO_0 = "-lL" -_RESOLVE_TO_1 = "hH" -_RESOLVE_TO_CHOICE = "xXzZuUwW" -resolve_x_to = os.getenv('COCOTB_RESOLVE_X', "VALUE_ERROR") - - -class _ResolveTable(dict): - """Translation table class for resolving binary strings. - - For use with :func:`str.translate()`, which indexes into table with Unicode ordinals. - """ - def __init__(self): - self.update({ord("0"): ord("0"), ord("1"): ord("1")}) - self.update({ord(k): ord("0") for k in _RESOLVE_TO_0}) - self.update({ord(k): ord("1") for k in _RESOLVE_TO_1}) - - # Do not resolve if resolve_x_to is not set to one of the supported values - def no_resolve(key): - return key - self.resolve_x = no_resolve - - if resolve_x_to == "VALUE_ERROR": - def resolve_error(key): - raise ValueError("Unresolvable bit in binary string: '{}'".format(chr(key))) - self.resolve_x = resolve_error - elif resolve_x_to == "ZEROS": - self.update({ord(k): ord("0") for k in _RESOLVE_TO_CHOICE}) - elif resolve_x_to == "ONES": - self.update({ord(k): ord("1") for k in _RESOLVE_TO_CHOICE}) - elif resolve_x_to == "RANDOM": - def resolve_random(key): - # convert to correct Unicode ordinal: - # ord('0') = 48 - # ord('1') = 49 - return random.getrandbits(1) + 48 - self.resolve_x = resolve_random - - self._resolve_to_choice = {ord(c) for c in _RESOLVE_TO_CHOICE} - - def __missing__(self, key): - if key in self._resolve_to_choice: - return self.resolve_x(key) - else: - return key - - -_resolve_table = _ResolveTable() - - -def resolve(string): - return string.translate(_resolve_table) - - -def _clog2(val): - if val < 0: - raise ValueError("_clog2 can't take a negative") - exp = 0 - while True: - if (1 << exp) >= val: - return exp - exp += 1 - - -class BinaryRepresentation: # noqa - UNSIGNED = 0 #: Unsigned format - SIGNED_MAGNITUDE = 1 #: Sign and magnitude format - TWOS_COMPLEMENT = 2 #: Two's complement format - - -class BinaryValue: - """Representation of values in binary format. - - The underlying value can be set or accessed using these aliasing attributes: - - - :attr:`BinaryValue.integer` is an integer - - :attr:`BinaryValue.signed_integer` is a signed integer - - :attr:`BinaryValue.binstr` is a string of ``01xXzZ`` - - :attr:`BinaryValue.buff` is a binary buffer of bytes - - :attr:`BinaryValue.value` is an integer **deprecated** - - For example: - - >>> vec = BinaryValue() - >>> vec.integer = 42 - >>> print(vec.binstr) - 101010 - >>> print(vec.buff) - b'*' - - """ - _permitted_chars = _RESOLVE_TO_0 +_RESOLVE_TO_1 + _RESOLVE_TO_CHOICE + "01" # noqa - - def __init__(self, value=None, n_bits=None, bigEndian=True, - binaryRepresentation=BinaryRepresentation.UNSIGNED, - bits=None): - """ - Args: - value (str or int or long, optional): Value to assign to the bus. - n_bits (int, optional): Number of bits to use for the underlying - binary representation. - bigEndian (bool, optional): Interpret the binary as big-endian - when converting to/from a string buffer. - binaryRepresentation (BinaryRepresentation): The representation - of the binary value - (one of :any:`UNSIGNED`, :any:`SIGNED_MAGNITUDE`, :any:`TWOS_COMPLEMENT`). - Defaults to unsigned representation. - bits (int, optional): Deprecated: Compatibility wrapper for :attr:`n_bits`. - """ - self._str = "" - self.big_endian = bigEndian - self.binaryRepresentation = binaryRepresentation - - # bits is the deprecated name for n_bits, allow its use for - # backward-compat reasons. - if (bits is not None and n_bits is not None): - raise TypeError("You cannot use n_bits and bits at the same time.") - if bits is not None: - warnings.warn( - "The bits argument to BinaryValue has been renamed to n_bits", - DeprecationWarning, stacklevel=2) - n_bits = bits - - self._n_bits = n_bits - - self._convert_to = self._convert_to_map[self.binaryRepresentation].__get__(self, self.__class__) - - self._convert_from = self._convert_from_map[self.binaryRepresentation].__get__(self, self.__class__) - - if value is not None: - self.assign(value) - - def assign(self, value): - """Decides how best to assign the value to the vector. - - Picks from the type of its argument whether to set :attr:`integer`, - :attr:`binstr`, or :attr:`buff`. - - Args: - value (str or int or bytes): The value to assign. - - .. versionchanged:: 1.4 - - This no longer falls back to setting :attr:`buff` if a :class:`str` - containining any characters that aren't ``0``, ``1``, ``X`` or ``Z`` - is used, since :attr:`buff` now accepts only :class:`bytes`. Instead, - an error is raised. - """ - if isinstance(value, int): - self.integer = value - elif isinstance(value, str): - self.binstr = value - elif isinstance(value, bytes): - self.buff = value - else: - raise TypeError( - "value must be int, str, or bytes, not {!r}" - .format(type(value).__qualname__) - ) - - def _convert_to_unsigned(self, x): - x = bin(x) - if x[0] == '-': - raise ValueError('Attempt to assigned negative number to unsigned ' - 'BinaryValue') - return self._adjust_unsigned(x[2:]) - - def _convert_to_signed_mag(self, x): - x = bin(x) - if x[0] == '-': - binstr = self._adjust_signed_mag('1' + x[3:]) - else: - binstr = self._adjust_signed_mag('0' + x[2:]) - if self.big_endian: - binstr = binstr[::-1] - return binstr - - def _convert_to_twos_comp(self, x): - if x < 0: - binstr = bin(2 ** (_clog2(abs(x)) + 1) + x)[2:] - binstr = self._adjust_twos_comp(binstr) - else: - binstr = self._adjust_twos_comp('0' + bin(x)[2:]) - if self.big_endian: - binstr = binstr[::-1] - return binstr - - def _convert_from_unsigned(self, x): - return int(x.translate(_resolve_table), 2) - - def _convert_from_signed_mag(self, x): - rv = int(self._str[1:].translate(_resolve_table), 2) - if self._str[0] == '1': - rv = rv * -1 - return rv - - def _convert_from_twos_comp(self, x): - if x[0] == '1': - binstr = x[1:] - binstr = self._invert(binstr) - rv = int(binstr, 2) + 1 - rv = rv * -1 - else: - rv = int(x.translate(_resolve_table), 2) - return rv - - _convert_to_map = { - BinaryRepresentation.UNSIGNED : _convert_to_unsigned, - BinaryRepresentation.SIGNED_MAGNITUDE : _convert_to_signed_mag, - BinaryRepresentation.TWOS_COMPLEMENT : _convert_to_twos_comp, - } - - _convert_from_map = { - BinaryRepresentation.UNSIGNED : _convert_from_unsigned, - BinaryRepresentation.SIGNED_MAGNITUDE : _convert_from_signed_mag, - BinaryRepresentation.TWOS_COMPLEMENT : _convert_from_twos_comp, - } - - _invert_table = str.maketrans({'0': '1', '1': '0'}) - - def _invert(self, x): - return x.translate(self._invert_table) - - def _adjust_unsigned(self, x): - if self._n_bits is None: - return x - l = len(x) - if l <= self._n_bits: - if self.big_endian: - rv = x + '0' * (self._n_bits - l) - else: - rv = '0' * (self._n_bits - l) + x - elif l > self._n_bits: - if self.big_endian: - rv = x[l - self._n_bits:] - else: - rv = x[:l - self._n_bits] - warnings.warn("{}-bit value requested, truncating value {!r} ({} bits) to {!r}".format( - self._n_bits, x, l, rv), category=RuntimeWarning, stacklevel=3) - return rv - - def _adjust_signed_mag(self, x): - """Pad/truncate the bit string to the correct length.""" - if self._n_bits is None: - return x - l = len(x) - if l <= self._n_bits: - if self.big_endian: - rv = x[:-1] + '0' * (self._n_bits - 1 - l) - rv = rv + x[-1] - else: - rv = '0' * (self._n_bits - 1 - l) + x[1:] - rv = x[0] + rv - elif l > self._n_bits: - if self.big_endian: - rv = x[l - self._n_bits:] - else: - rv = x[:-(l - self._n_bits)] - warnings.warn("{}-bit value requested, truncating value {!r} ({} bits) to {!r}".format( - self._n_bits, x, l, rv), category=RuntimeWarning, stacklevel=3) - else: - rv = x - return rv - - def _adjust_twos_comp(self, x): - if self._n_bits is None: - return x - l = len(x) - if l <= self._n_bits: - if self.big_endian: - rv = x + x[-1] * (self._n_bits - l) - else: - rv = x[0] * (self._n_bits - l) + x - elif l > self._n_bits: - if self.big_endian: - rv = x[l - self._n_bits:] - else: - rv = x[:-(l - self._n_bits)] - warnings.warn("{}-bit value requested, truncating value {!r} ({} bits) to {!r}".format( - self._n_bits, x, l, rv), category=RuntimeWarning, stacklevel=3) - else: - rv = x - return rv - - @property - def integer(self): - """The integer representation of the underlying vector.""" - return self._convert_from(self._str) - - @integer.setter - def integer(self, val): - self._str = self._convert_to(val) - - @property - def value(self): - """Integer access to the value. **deprecated**""" - return self.integer - - @value.setter - def value(self, val): - self.integer = val - - get_value = value.fget - set_value = value.fset - - @property - def signed_integer(self): - """The signed integer representation of the underlying vector.""" - ival = int(self._str.translate(_resolve_table), 2) - bits = len(self._str) - signbit = (1 << (bits - 1)) - if (ival & signbit) == 0: - return ival - else: - return -1 * (1 + (int(~ival) & (signbit - 1))) - - @signed_integer.setter - def signed_integer(self, val): - self.integer = val - - get_value_signed = signed_integer.fget - - @property - def is_resolvable(self) -> bool: - """ - Return whether the value contains only resolvable (i.e. no "unknown") bits. - - By default the values ``X``, ``Z``, ``U`` and ``W`` are considered unresolvable. - This can be configured with :envvar:`COCOTB_RESOLVE_X`. - - This is similar to the SystemVerilog Assertion ``$isunknown`` system function - or the VHDL function ``is_x`` (with an inverted meaning). - """ - return not any(char in self._str for char in _RESOLVE_TO_CHOICE) - - @property - def buff(self) -> bytes: - r"""The value as a binary string buffer. - - >>> BinaryValue("01000001" + "00101111").buff == b"\x41\x2F" - True - - .. versionchanged:: 1.4 - This changed from :class:`str` to :class:`bytes`. - Note that for older versions used with Python 2 these types were - indistinguishable. - """ - bits = self._str.translate(_resolve_table) - - if len(bits) % 8: - bits = "0" * (8 - len(bits) % 8) + bits - - buff = [] - while bits: - byte = bits[:8] - bits = bits[8:] - val = int(byte, 2) - if self.big_endian: - buff += [val] - else: - buff = [val] + buff - return bytes(buff) - - @buff.setter - def buff(self, val: bytes): - if not self.big_endian: - val = reversed(val) - self._str = ''.join([format(char, "08b") for char in val]) - self._adjust() - - def _adjust(self): - """Pad/truncate the bit string to the correct length.""" - if self._n_bits is None: - return - l = len(self._str) - if l < self._n_bits: - if self.big_endian: - self._str = self._str + "0" * (self._n_bits - l) - else: - self._str = "0" * (self._n_bits - l) + self._str - elif l > self._n_bits: - rv = self._str[l - self._n_bits:] - warnings.warn("{}-bit value requested, truncating value {!r} ({} bits) to {!r}".format( - self._n_bits, self._str, l, rv), category=RuntimeWarning, stacklevel=3) - self._str = rv - - get_buff = buff.fget - set_buff = buff.fset - - @property - def binstr(self): - """ The binary representation stored as a string of ``0``, ``1``, and possibly ``x``, ``z``, and other states. """ - return self._str - - _non_permitted_regex = re.compile("[^{}]".format(_permitted_chars)) - - @binstr.setter - def binstr(self, string): - match = self._non_permitted_regex.search(string) - if match: - raise ValueError("Attempting to assign character %s to a %s" % - (match.group(), self.__class__.__name__)) - self._str = string - self._adjust() - - get_binstr = binstr.fget - set_binstr = binstr.fset - - def _set_trusted_binstr(self, string): - self._str = string - - @property - def n_bits(self): - """The number of bits of the binary value.""" - return self._n_bits - - def hex(self): - try: - return hex(self.integer) - except Exception: - return hex(int(self.binstr, 2)) - - def __le__(self, other): - self.assign(other) - - def __str__(self): - return self.binstr - - def __repr__(self): - return self.__str__() - - def __bool__(self): - """Provide boolean testing of a :attr:`binstr`. - - >>> val = BinaryValue("0000") - >>> if val: print("True") - ... else: print("False") - False - >>> val.integer = 42 - >>> if val: print("True") - ... else: print("False") - True - - """ - for char in self._str: - if char == "1": - return True - return False - - def __eq__(self, other): - if isinstance(other, BinaryValue): - other = other.value - return self.value == other - - def __ne__(self, other): - if isinstance(other, BinaryValue): - other = other.value - return self.value != other - - def __int__(self): - return self.integer - - def __long__(self): - return self.integer - - def __add__(self, other): - return self.integer + int(other) - - def __iadd__(self, other): - self.integer = self.integer + int(other) - return self - - def __radd__(self, other): - return self.integer + other - - def __sub__(self, other): - return self.integer - int(other) - - def __isub__(self, other): - self.integer = self.integer - int(other) - return self - - def __rsub__(self, other): - return other - self.integer - - def __mul__(self, other): - return self.integer * int(other) - - def __imul__(self, other): - self.integer = self.integer * int(other) - return self - - def __rmul__(self, other): - return self.integer * other - - def __floordiv__(self, other): - return self.integer // int(other) - - def __ifloordiv__(self, other): - self.integer = self.__floordiv__(other) - return self - - def __rfloordiv__(self, other): - return other // self.integer - - def __divmod__(self, other): - return (self.integer // other, self.integer % other) - - def __rdivmod__(self, other): - return other // self.integer - - def __mod__(self, other): - return self.integer % int(other) - - def __imod__(self, other): - self.integer = self.integer % int(other) - return self - - def __rmod__(self, other): - return other % self.integer - - def __pow__(self, other, modulo=None): - return pow(self.integer, other) - - def __ipow__(self, other): - self.integer = pow(self.integer, other) - return self - - def __rpow__(self, other): - return pow(other, self.integer) - - def __lshift__(self, other): - return int(self) << int(other) - - def __ilshift__(self, other): - """Preserves X values""" - self.binstr = self.binstr[other:] + self.binstr[:other] - return self - - def __rlshift__(self, other): - return other << self.integer - - def __rshift__(self, other): - return int(self) >> int(other) - - def __irshift__(self, other): - """Preserves X values""" - self.binstr = self.binstr[-other:] + self.binstr[:-other] - return self - - def __rrshift__(self, other): - return other >> self.integer - - def __and__(self, other): - return self.integer & other - - def __iand__(self, other): - self.integer &= other - return self - - def __rand__(self, other): - return self.integer & other - - def __xor__(self, other): - return self.integer ^ other - - def __ixor__(self, other): - self.integer ^= other - return self - - def __rxor__(self, other): - return self.__xor__(other) - - def __or__(self, other): - return self.integer | other - - def __ior__(self, other): - self.integer |= other - return self - - def __ror__(self, other): - return self.__or__(other) - - def __div__(self, other): - return self.integer / other - - def __idiv__(self, other): - self.integer /= other - return self - - def __rdiv__(self, other): - return other / self.integer - - def __neg__(self): - return - self.integer - - def __pos__(self): - return + self.integer - - def __abs__(self): - return abs(self.integer) - - def __invert__(self): - """Preserves X values""" - return self._invert(self.binstr) - - def __oct__(self): - return oct(self.integer) - - def __hex__(self): - return hex(self.integer) - - def __index__(self): - return self.integer - - def __len__(self): - return len(self.binstr) - - def __getitem__(self, key): - """BinaryValue uses Verilog/VHDL style slices as opposed to Python - style""" - if isinstance(key, slice): - first, second = key.start, key.stop - if self.big_endian: - if first < 0 or second < 0: - raise IndexError('BinaryValue does not support negative ' - 'indices') - if second > self._n_bits - 1: - raise IndexError('High index greater than number of bits.') - if first > second: - raise IndexError('Big Endian indices must be specified ' - 'low to high') - _binstr = self.binstr[first:(second + 1)] - else: - if first < 0 or second < 0: - raise IndexError('BinaryValue does not support negative ' - 'indices') - if first > self._n_bits - 1: - raise IndexError('High index greater than number of bits.') - if second > first: - raise IndexError('Litte Endian indices must be specified ' - 'high to low') - high = self._n_bits - second - low = self._n_bits - 1 - first - _binstr = self.binstr[low:high] - else: - index = key - if index > self._n_bits - 1: - raise IndexError('Index greater than number of bits.') - if self.big_endian: - _binstr = self.binstr[index] - else: - _binstr = self.binstr[self._n_bits-1-index] - rv = BinaryValue(n_bits=len(_binstr), bigEndian=self.big_endian, - binaryRepresentation=self.binaryRepresentation) - rv.binstr = _binstr - return rv - - def __setitem__(self, key, val): - """BinaryValue uses Verilog/VHDL style slices as opposed to Python style.""" - if not isinstance(val, str) and not isinstance(val, int): - raise TypeError('BinaryValue slices only accept string or integer values') - - # convert integer to string - if isinstance(val, int): - if isinstance(key, slice): - num_slice_bits = abs(key.start - key.stop) + 1 - else: - num_slice_bits = 1 - if val < 0: - raise ValueError('Integer must be positive') - if val >= 2**num_slice_bits: - raise ValueError('Integer is too large for the specified slice ' - 'length') - val = "{:0{width}b}".format(val, width=num_slice_bits) - - if isinstance(key, slice): - first, second = key.start, key.stop - - if self.big_endian: - if first < 0 or second < 0: - raise IndexError('BinaryValue does not support negative ' - 'indices') - if second > self._n_bits - 1: - raise IndexError('High index greater than number of bits.') - if first > second: - raise IndexError('Big Endian indices must be specified ' - 'low to high') - if len(val) > (second + 1 - first): - raise ValueError('String length must be equal to slice ' - 'length') - slice_1 = self.binstr[:first] - slice_2 = self.binstr[second + 1:] - self.binstr = slice_1 + val + slice_2 - else: - if first < 0 or second < 0: - raise IndexError('BinaryValue does not support negative ' - 'indices') - if first > self._n_bits - 1: - raise IndexError('High index greater than number of bits.') - if second > first: - raise IndexError('Litte Endian indices must be specified ' - 'high to low') - high = self._n_bits - second - low = self._n_bits - 1 - first - if len(val) > (high - low): - raise ValueError('String length must be equal to slice ' - 'length') - slice_1 = self.binstr[:low] - slice_2 = self.binstr[high:] - self.binstr = slice_1 + val + slice_2 - else: - if len(val) != 1: - raise ValueError('String length must be equal to slice ' - 'length') - index = key - if index > self._n_bits - 1: - raise IndexError('Index greater than number of bits.') - if self.big_endian: - self.binstr = self.binstr[:index] + val + self.binstr[index + 1:] - else: - self.binstr = self.binstr[0:self._n_bits-index-1] + val + self.binstr[self._n_bits-index:self._n_bits] - - -if __name__ == "__main__": - import doctest - doctest.testmod() diff --git a/cocotb/clock.py b/cocotb/clock.py deleted file mode 100644 index cc268152..00000000 --- a/cocotb/clock.py +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -"""A clock class.""" - -import itertools -import warnings - -from cocotb.log import SimLog -from cocotb.triggers import Timer -from cocotb.utils import get_sim_steps, get_time_from_sim_steps, lazy_property - - -class BaseClock: - """Base class to derive from.""" - - def __init__(self, signal): - self.signal = signal - - @lazy_property - def log(self): - return SimLog("cocotb.%s.%s" % ( - type(self).__qualname__, self.signal._name - )) - - -class Clock(BaseClock): - r"""Simple 50:50 duty cycle clock driver. - - Instances of this class should call its :meth:`start` method and :func:`fork` the - result. This will create a clocking thread that drives the signal at the - desired period/frequency. - - Example: - - .. code-block:: python - - c = Clock(dut.clk, 10, 'ns') - cocotb.fork(c.start()) - - Args: - signal: The clock pin/signal to be driven. - period (int): The clock period. Must convert to an even number of - timesteps. - units (str, optional): One of - ``'step'``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``. - When *units* is ``'step'``, - the timestep is determined by the simulator (see :make:var:`COCOTB_HDL_TIMEPRECISION`). - - If you need more features like a phase shift and an asymmetric duty cycle, - it is simple to create your own clock generator (that you then :func:`fork`): - - .. code-block:: python - - async def custom_clock(): - # pre-construct triggers for performance - high_time = Timer(high_delay, units="ns") - low_time = Timer(low_delay, units="ns") - await Timer(initial_delay, units="ns") - while True: - dut.clk <= 1 - await high_time - dut.clk <= 0 - await low_time - - If you also want to change the timing during simulation, - use this slightly more inefficient example instead where - the :class:`Timer`\ s inside the while loop are created with - current delay values: - - .. code-block:: python - - async def custom_clock(): - while True: - dut.clk <= 1 - await Timer(high_delay, units="ns") - dut.clk <= 0 - await Timer(low_delay, units="ns") - - high_delay = low_delay = 100 - cocotb.fork(custom_clock()) - await Timer(1000, units="ns") - high_delay = low_delay = 10 # change the clock speed - await Timer(1000, units="ns") - - .. versionchanged:: 1.5 - Support ``'step'`` as the the *units* argument to mean "simulator time step". - - .. deprecated:: 1.5 - Using None as the the *units* argument is deprecated, use ``'step'`` instead. - """ - - def __init__(self, signal, period, units="step"): - BaseClock.__init__(self, signal) - if units is None: - warnings.warn( - 'Using units=None is deprecated, use units="step" instead.', - DeprecationWarning, stacklevel=2) - units="step" # don't propagate deprecated value - self.period = get_sim_steps(period, units) - self.half_period = get_sim_steps(period / 2.0, units) - self.frequency = 1.0 / get_time_from_sim_steps(self.period, units='us') - self.hdl = None - self.signal = signal - self.coro = None - self.mcoro = None - - async def start(self, cycles=None, start_high=True): - r"""Clocking coroutine. Start driving your clock by :func:`fork`\ ing a - call to this. - - Args: - cycles (int, optional): Cycle the clock *cycles* number of times, - or if ``None`` then cycle the clock forever. - Note: ``0`` is not the same as ``None``, as ``0`` will cycle no times. - start_high (bool, optional): Whether to start the clock with a ``1`` - for the first half of the period. - Default is ``True``. - - .. versionadded:: 1.3 - """ - t = Timer(self.half_period) - if cycles is None: - it = itertools.count() - else: - it = range(cycles) - - # branch outside for loop for performance (decision has to be taken only once) - if start_high: - for _ in it: - self.signal <= 1 - await t - self.signal <= 0 - await t - else: - for _ in it: - self.signal <= 0 - await t - self.signal <= 1 - await t - - def __str__(self): - return type(self).__qualname__ + "(%3.1f MHz)" % self.frequency diff --git a/cocotb/config.py b/cocotb/config.py deleted file mode 100755 index a8547298..00000000 --- a/cocotb/config.py +++ /dev/null @@ -1,174 +0,0 @@ -#!/usr/bin/env python -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -""" -Module for querying the cocotb configuration - -This module provides information in module global variables and through a -``main()`` function that is used in the cocotb-config script. - -Global variables: - share_dir: str, path where the cocotb data is stored - makefiles_dir: str, path where the cocotb makefiles are installed -""" -import argparse -import os -import sys -import textwrap -import cocotb -import cocotb._vendor.find_libpython as find_libpython - -__all__ = ["share_dir", "makefiles_dir"] - - -share_dir = os.path.join(os.path.dirname(cocotb.__file__), "share") -makefiles_dir = os.path.join(os.path.dirname(cocotb.__file__), "share", "makefiles") - - -def help_vars_text(): - if "dev" in cocotb.__version__: - doclink = "https://docs.cocotb.org/en/latest/building.html" - else: - doclink = "https://docs.cocotb.org/en/v{}/building.html".format(cocotb.__version__) - - # NOTE: make sure to keep "helpmsg" aligned with documentation/source/building.rst - # Also keep it at 80 chars. - helpmsg = textwrap.dedent("""\ - The following variables are environment variables: - - Cocotb - ------ - TOPLEVEL Instance in the hierarchy to use as the DUT - RANDOM_SEED Random seed, to recreate a previous test stimulus - COCOTB_ANSI_OUTPUT Force cocotb to print or not print in color - COCOTB_REDUCED_LOG_FMT Display log lines shorter - COCOTB_ATTACH Pause time value in seconds before the simulator start - COCOTB_ENABLE_PROFILING Performance analysis of the Python portion of cocotb - COCOTB_LOG_LEVEL Default logging level (default INFO) - COCOTB_RESOLVE_X How to resolve X, Z, U, W on integer conversion - MEMCHECK HTTP port to use for debugging Python memory usage - LIBPYTHON_LOC Absolute path to libpython - - Regression Manager - ------------------ - COCOTB_PDB_ON_EXCEPTION Drop into the Python debugger (pdb) on exception - MODULE Modules to search for test functions (comma-separated) - TESTCASE Test function(s) to run (comma-separated list) - COCOTB_RESULTS_FILE File name for xUnit XML tests results - COVERAGE Report Python coverage (also HDL for some simulators) - COCOTB_HOOKS Comma-separated module list to be executed before test - - GPI - --- - GPI_EXTRA Extra libraries to load at runtime (comma-separated) - - Scheduler - --------- - COCOTB_SCHEDULER_DEBUG Enable additional output of coroutine scheduler - - For details, see {}""").format(doclink) - return helpmsg - - -class PrintAction(argparse.Action): - def __init__(self, option_strings, dest, text=None, **kwargs): - super(PrintAction, self).__init__(option_strings, dest, nargs=0, **kwargs) - self.text = text - - def __call__(self, parser, namespace, values, option_string=None): - print(self.text) - parser.exit() - - -def get_parser(): - prefix_dir = os.path.dirname(os.path.dirname(cocotb.__file__)) - version = cocotb.__version__ - python_bin = sys.executable - - parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter) - parser.add_argument( - "--prefix", - help="echo the package-prefix of cocotb", - action=PrintAction, - text=prefix_dir, - ) - parser.add_argument( - "--share", - help="echo the package-share of cocotb", - action=PrintAction, - text=share_dir, - ) - parser.add_argument( - "--makefiles", - help="echo the package-makefiles of cocotb", - action=PrintAction, - text=makefiles_dir, - ) - parser.add_argument( - "--python-bin", - help="echo the path to the Python binary cocotb is installed for", - action=PrintAction, - text=python_bin, - ) - parser.add_argument( - "--help-vars", - help="show help about supported variables", - action=PrintAction, - text=help_vars_text(), - ) - parser.add_argument( - "-v", - "--version", - help="echo the version of cocotb", - action=PrintAction, - text=version, - ) - parser.add_argument( - "--libpython", - help="prints the absolute path to the libpython associated with the current Python installation", - action=PrintAction, - text=find_libpython.find_libpython(), - ) - - return parser - - -def main(): - parser = get_parser() - - if len(sys.argv) == 1: - parser.print_help(sys.stderr) - sys.exit(1) - - parser.parse_args() - - -if __name__ == "__main__": - main() diff --git a/cocotb/decorators.py b/cocotb/decorators.py deleted file mode 100644 index 31ee8f6f..00000000 --- a/cocotb/decorators.py +++ /dev/null @@ -1,593 +0,0 @@ -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import sys -import time -import logging -import functools -import inspect -import os -import warnings - -import cocotb -from cocotb.log import SimLog -from cocotb.result import ReturnValue -from cocotb.utils import get_sim_time, lazy_property, remove_traceback_frames, extract_coro_stack -from cocotb import outcomes - -# Sadly the Python standard logging module is very slow so it's better not to -# make any calls by testing a boolean flag first -if "COCOTB_SCHEDULER_DEBUG" in os.environ: - _debug = True -else: - _debug = False - - -def public(f): - """Use a decorator to avoid retyping function/class names. - - * Based on an idea by Duncan Booth: - http://groups.google.com/group/comp.lang.python/msg/11cbb03e09611b8a - * Improved via a suggestion by Dave Angel: - http://groups.google.com/group/comp.lang.python/msg/3d400fb22d8a42e1 - """ - all = sys.modules[f.__module__].__dict__.setdefault('__all__', []) - if f.__name__ not in all: # Prevent duplicates if run from an IDE. - all.append(f.__name__) - return f - - -public(public) # Emulate decorating ourself - - -@public -class CoroutineComplete(Exception): - """To ensure that a coroutine has completed before we fire any triggers - that are blocked waiting for the coroutine to end, we create a sub-class - exception that the scheduler catches and the callbacks are attached - here. - """ - - def __init__(self, text=""): - Exception.__init__(self, text) - - -class RunningTask: - """Per instance wrapper around a running generator. - - Provides the following: - - task.join() creates a Trigger that will fire when this coroutine - completes. - - task.kill() will destroy a coroutine instance (and cause any Join - triggers to fire. - """ - - _id_count = 0 # used by the scheduler for debug - - def __init__(self, inst): - - if inspect.iscoroutine(inst): - self._natively_awaitable = True - elif inspect.isgenerator(inst): - self._natively_awaitable = False - elif inspect.iscoroutinefunction(inst): - raise TypeError( - "Coroutine function {} should be called prior to being " - "scheduled." - .format(inst)) - elif sys.version_info >= (3, 6) and inspect.isasyncgen(inst): - raise TypeError( - "{} is an async generator, not a coroutine. " - "You likely used the yield keyword instead of await.".format( - inst.__qualname__)) - else: - raise TypeError( - "%s isn't a valid coroutine! Did you forget to use the yield keyword?" % inst) - self._coro = inst - self._started = False - self._callbacks = [] - self._outcome = None - self._trigger = None - - self._task_id = self._id_count - RunningTask._id_count += 1 - self.__name__ = "Task %d" % self._task_id - self.__qualname__ = self.__name__ - - @lazy_property - def log(self): - # Creating a logger is expensive, only do it if we actually plan to - # log anything - return SimLog("cocotb.coroutine.%s" % self.__qualname__, id(self)) - - @property - def retval(self): - if self._outcome is None: - raise RuntimeError("coroutine is not complete") - return self._outcome.get() - - @property - def _finished(self): - return self._outcome is not None - - def __iter__(self): - return self - - def __str__(self): - return "<{}>".format(self.__name__) - - def _get_coro_stack(self): - """Get the coroutine callstack of this Task.""" - coro_stack = extract_coro_stack(self._coro) - - # Remove Trigger.__await__() from the stack, as it's not really useful - if self._natively_awaitable and len(coro_stack): - if coro_stack[-1].name == '__await__': - coro_stack.pop() - - return coro_stack - - def __repr__(self): - coro_stack = self._get_coro_stack() - - if cocotb.scheduler._current_task is self: - fmt = "<{name} running coro={coro}()>" - elif self._finished: - fmt = "<{name} finished coro={coro}() outcome={outcome}>" - elif self._trigger is not None: - fmt = "<{name} pending coro={coro}() trigger={trigger}>" - elif not self._started: - fmt = "<{name} created coro={coro}()>" - else: - fmt = "<{name} adding coro={coro}()>" - - try: - coro_name = coro_stack[-1].name - # coro_stack may be empty if: - # - exhausted generator - # - finished coroutine - except IndexError: - coro_name = self._coro.__name__ - - repr_string = fmt.format( - name=self.__name__, - coro=coro_name, - trigger=self._trigger, - outcome=self._outcome - ) - return repr_string - - def _advance(self, outcome): - """Advance to the next yield in this coroutine. - - Args: - outcome: The :any:`outcomes.Outcome` object to resume with. - - Returns: - The object yielded from the coroutine - - Raises: - CoroutineComplete: If the coroutine returns or throws an error, self._outcome is set, and - :exc:`CoroutineComplete` is thrown. - """ - try: - self._started = True - return outcome.send(self._coro) - except ReturnValue as e: - self._outcome = outcomes.Value(e.retval) - raise CoroutineComplete() - except StopIteration as e: - self._outcome = outcomes.Value(e.value) - raise CoroutineComplete() - except BaseException as e: - self._outcome = outcomes.Error(remove_traceback_frames(e, ['_advance', 'send'])) - raise CoroutineComplete() - - def send(self, value): - return self._coro.send(value) - - def throw(self, exc): - return self._coro.throw(exc) - - def close(self): - return self._coro.close() - - def kill(self): - """Kill a coroutine.""" - if self._outcome is not None: - # already finished, nothing to kill - return - - if _debug: - self.log.debug("kill() called on coroutine") - # todo: probably better to throw an exception for anyone waiting on the coroutine - self._outcome = outcomes.Value(None) - cocotb.scheduler._unschedule(self) - - def join(self): - """Return a trigger that will fire when the wrapped coroutine exits.""" - return cocotb.triggers.Join(self) - - def has_started(self): - return self._started - - def __bool__(self): - """Provide boolean testing - if the coroutine has finished return false - otherwise return true""" - return not self._finished - - def __await__(self): - # It's tempting to use `return (yield from self._coro)` here, - # which bypasses the scheduler. Unfortunately, this means that - # we can't keep track of the result or state of the coroutine, - # things which we expose in our public API. If you want the - # efficiency of bypassing the scheduler, remove the `@coroutine` - # decorator from your `async` functions. - - # Hand the coroutine back to the scheduler trampoline. - return (yield self) - - -class RunningCoroutine(RunningTask): - """ - The result of calling a :any:`cocotb.coroutine` decorated coroutine. - - All this class does is provide some extra attributes. - """ - - def __init__(self, inst, parent): - RunningTask.__init__(self, inst) - self._parent = parent - self.__doc__ = parent._func.__doc__ - self.module = parent._func.__module__ - self.funcname = parent._func.__name__ - - -class RunningTest(RunningCoroutine): - """Add some useful Test functionality to a RunningCoroutine.""" - - class ErrorLogHandler(logging.Handler): - def __init__(self, fn): - self.fn = fn - logging.Handler.__init__(self, level=logging.DEBUG) - - def handle(self, record): - # For historical reasons, only logs sent directly to the `cocotb` - # logger are recorded - logs to `cocotb.scheduler` for instance - # are not recorded. Changing this behavior may have significant - # memory usage implications, so should not be done without some - # thought. - if record.name == 'cocotb': - self.fn(self.format(record)) - - def __init__(self, inst, parent): - self.error_messages = [] - RunningCoroutine.__init__(self, inst, parent) - self.log = SimLog("cocotb.test.%s" % inst.__qualname__, id(self)) - self.started = False - self.start_time = 0 - self.start_sim_time = 0 - self.expect_fail = parent.expect_fail - self.expect_error = parent.expect_error - self.skip = parent.skip - self.stage = parent.stage - self.__name__ = "Test %s" % inst.__name__ - self.__qualname__ = "Test %s" % inst.__qualname__ - - # make sure not to create a circular reference here - self.handler = RunningTest.ErrorLogHandler(self.error_messages.append) - - def __str__(self): - return "<{}>".format(self.__name__) - - def _advance(self, outcome): - if not self.started: - self.log.info("Starting test: \"%s\"\nDescription: %s" % - (self.funcname, self.__doc__)) - self.start_time = time.time() - self.start_sim_time = get_sim_time('ns') - self.started = True - return super(RunningTest, self)._advance(outcome) - - # like RunningTask.kill(), but with a way to inject a failure - def abort(self, exc): - """Force this test to end early, without executing any cleanup. - - This happens when a background task fails, and is consistent with - how the behavior has always been. In future, we may want to behave - more gracefully to allow the test body to clean up. - - `exc` is the exception that the test should report as its reason for - aborting. - """ - if self._outcome is not None: - # imported here to avoid circular imports - from cocotb.scheduler import InternalError - raise InternalError("Outcome already has a value, but is being set again.") - outcome = outcomes.Error(exc) - if _debug: - self.log.debug("outcome forced to {}".format(outcome)) - self._outcome = outcome - cocotb.scheduler._unschedule(self) - - def sort_name(self): - if self.stage is None: - return "%s.%s" % (self.module, self.funcname) - else: - return "%s.%d.%s" % (self.module, self.stage, self.funcname) - - -class coroutine: - """Decorator class that allows us to provide common coroutine mechanisms: - - ``log`` methods will log to ``cocotb.coroutine.name``. - - :meth:`~cocotb.decorators.RunningTask.join` method returns an event which will fire when the coroutine exits. - - Used as ``@cocotb.coroutine``. - """ - - def __init__(self, func): - self._func = func - functools.update_wrapper(self, func) - - @lazy_property - def log(self): - return SimLog("cocotb.coroutine.%s" % self._func.__qualname__, id(self)) - - def __call__(self, *args, **kwargs): - return RunningCoroutine(self._func(*args, **kwargs), self) - - def __get__(self, obj, owner=None): - """Permit the decorator to be used on class methods - and standalone functions""" - return type(self)(self._func.__get__(obj, owner)) - - def __iter__(self): - return self - - def __str__(self): - return str(self._func.__qualname__) - - -@public -class function: - """Decorator class that allows a function to block. - - This allows a coroutine that consumes simulation time - to be called by a thread started with :class:`cocotb.external`; - in other words, to internally block while externally - appear to yield. - """ - - def __init__(self, func): - self._coro = cocotb.coroutine(func) - - @lazy_property - def log(self): - return SimLog("cocotb.function.%s" % self._coro.__qualname__, id(self)) - - def __call__(self, *args, **kwargs): - return cocotb.scheduler._queue_function(self._coro(*args, **kwargs)) - - def __get__(self, obj, owner=None): - """Permit the decorator to be used on class methods - and standalone functions""" - return type(self)(self._coro._func.__get__(obj, owner)) - - -@public -class external: - """Decorator to apply to an external function to enable calling from cocotb. - - This turns a normal function that isn't a coroutine into a blocking coroutine. - Currently, this creates a new execution thread for each function that is - called. - Scope for this to be streamlined to a queue in future. - """ - - def __init__(self, func): - self._func = func - self._log = SimLog("cocotb.external.%s" % self._func.__qualname__, id(self)) - - def __call__(self, *args, **kwargs): - return cocotb.scheduler._run_in_executor(self._func, *args, **kwargs) - - def __get__(self, obj, owner=None): - """Permit the decorator to be used on class methods - and standalone functions""" - return type(self)(self._func.__get__(obj, owner)) - - -class _decorator_helper(type): - """ - Metaclass that allows a type to be constructed using decorator syntax, - passing the decorated function as the first argument. - - So: - - @MyClass(construction, args='go here') - def this_is_passed_as_f(...): - pass - - ends up calling - - MyClass.__init__(this_is_passed_as_f, construction, args='go here') - """ - def __call__(cls, *args, **kwargs): - def decorator(f): - # fall back to the normal way of constructing an object, now that - # we have all the arguments - return type.__call__(cls, f, *args, **kwargs) - return decorator - - -@public -class hook(coroutine, metaclass=_decorator_helper): - r""" - Decorator to mark a function as a hook for cocotb. - - Used as ``@cocotb.hook()``. - - All hooks are run at the beginning of a cocotb test suite, prior to any - test code being run. - - .. deprecated:: 1.5 - Hooks are deprecated. - Their functionality can be replaced with module-level Python code, - higher-priority tests using the ``stage`` argument to :func:`cocotb.test`\ s, - or custom decorators which perform work before and after the tests - they decorate. - """ - - def __init__(self, f): - super(hook, self).__init__(f) - warnings.warn( - "Hooks have been deprecated. See the documentation for suggestions on alternatives.", - DeprecationWarning, stacklevel=2) - self.im_hook = True - self.name = self._func.__name__ - - -@public -class test(coroutine, metaclass=_decorator_helper): - """Decorator to mark a function as a test. - - All tests are coroutines. The test decorator provides - some common reporting etc., a test timeout and allows - us to mark tests as expected failures. - - Tests are evaluated in the order they are defined in a test module. - - Used as ``@cocotb.test(...)``. - - Args: - timeout_time (numbers.Real or decimal.Decimal, optional): - Simulation time duration before timeout occurs. - - .. versionadded:: 1.3 - - .. note:: - Test timeout is intended for protection against deadlock. - Users should use :class:`~cocotb.triggers.with_timeout` if they require a - more general-purpose timeout mechanism. - - timeout_unit (str, optional): - Units of timeout_time, accepts any units that :class:`~cocotb.triggers.Timer` does. - - .. versionadded:: 1.3 - - .. deprecated:: 1.5 - Using None as the the *timeout_unit* argument is deprecated, use ``'step'`` instead. - - expect_fail (bool, optional): - Don't mark the result as a failure if the test fails. - - expect_error (exception type or tuple of exception types, optional): - Mark the result as a pass only if one of the exception types is raised in the test. - This is primarily for cocotb internal regression use for when a simulator error is expected. - - Users are encouraged to use the following idiom instead:: - - @cocotb.test() - async def my_test(dut): - try: - await thing_that_should_fail() - except ExceptionIExpect: - pass - else: - assert False, "Exception did not occur" - - .. versionchanged:: 1.3 - Specific exception types can be expected - - .. deprecated:: 1.5 - Passing a :class:`bool` value is now deprecated. - Pass a specific :class:`Exception` or a tuple of Exceptions instead. - - skip (bool, optional): - Don't execute this test as part of the regression. Test can still be run - manually by setting :make:var:`TESTCASE`. - - stage (int, optional) - Order tests logically into stages, where multiple tests can share a stage. - """ - - _id_count = 0 # used by the RegressionManager to sort tests in definition order - - def __init__(self, f, timeout_time=None, timeout_unit="step", - expect_fail=False, expect_error=(), - skip=False, stage=None): - - if timeout_unit is None: - warnings.warn( - 'Using timeout_unit=None is deprecated, use timeout_unit="step" instead.', - DeprecationWarning, stacklevel=2) - timeout_unit="step" # don't propagate deprecated value - self._id = self._id_count - type(self)._id_count += 1 - - if timeout_time is not None: - co = coroutine(f) - - @functools.wraps(f) - async def f(*args, **kwargs): - running_co = co(*args, **kwargs) - - try: - res = await cocotb.triggers.with_timeout(running_co, self.timeout_time, self.timeout_unit) - except cocotb.result.SimTimeoutError: - running_co.kill() - raise - else: - return res - - super(test, self).__init__(f) - - self.timeout_time = timeout_time - self.timeout_unit = timeout_unit - self.expect_fail = expect_fail - if isinstance(expect_error, bool): - warnings.warn( - "Passing bool values to `except_error` option of `cocotb.test` is deprecated. " - "Pass a specific Exception type instead", - DeprecationWarning, stacklevel=2) - if expect_error is True: - expect_error = (Exception,) - elif expect_error is False: - expect_error = () - self.expect_error = expect_error - self.skip = skip - self.stage = stage - self.im_test = True # For auto-regressions - self.name = self._func.__name__ - - def __call__(self, *args, **kwargs): - return RunningTest(self._func(*args, **kwargs), self) diff --git a/cocotb/generators/__init__.py b/cocotb/generators/__init__.py deleted file mode 100644 index b2e17748..00000000 --- a/cocotb/generators/__init__.py +++ /dev/null @@ -1,122 +0,0 @@ -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -""" - Set of general generic generators -""" -import math -import random -import itertools -import warnings - -from cocotb.decorators import public - - -warnings.warn( - "The contents of the cocotb.generators package will soon be removed.\n" - "Most of the functionality can be replaced with utilities provided " - "by other packages or the standard library.\n Alternatively, you can " - "copy this package or individual functions into your project, if you " - "follow cocotb's license agreement.", - DeprecationWarning) - - -@public -def repeat(obj, nrepeat=None): - """Generator to repeatedly yield the same object - - Args: - obj (any): The object to yield - nrepeat (int, optional): The number of times to repeatedly yield *obj* - - .. deprecated:: 1.4.1 - """ - if nrepeat is None: - return itertools.repeat(obj) - else: - return itertools.repeat(obj, times=nrepeat) - - -@public -def combine(generators): - """ - Generator for serially combining multiple generators together - - Args: - generators (iterable): Generators to combine together - - .. deprecated:: 1.4.1 - """ - return itertools.chain.from_iterable(generators) - - -@public -def gaussian(mean, sigma): - """ - Generate a guasian distribution indefinitely - - Args: - mean (int/float): mean value - - sigma (int/float): Standard deviation - - .. deprecated:: 1.4.1 - """ - while True: - yield random.gauss(mean, sigma) - - -@public -def sine_wave(amplitude, w, offset=0): - """ - Generates a sine wave that repeats forever - - Args: - amplitude (int/float): peak deviation of the function from zero - - w (int/float): is the rate of change of the function argument - - Yields: - floats that form a sine wave - - .. deprecated:: 1.4.1 - """ - twoPiF_DIV_sampleRate = math.pi * 2 - while True: - for idx in (i / float(w) for i in range(int(w))): - yield amplitude*math.sin(twoPiF_DIV_sampleRate * idx) + offset - - -def get_generators(module): - """Return an iterator which yields all the generators in a module - - Args: - module (python module): The module to get the generators from - - .. deprecated:: 1.4.1 - """ - return (getattr(module, gen) for gen in module.__all__) diff --git a/cocotb/generators/bit.py b/cocotb/generators/bit.py deleted file mode 100644 index 4e09732e..00000000 --- a/cocotb/generators/bit.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -""" - Collection of generators for creating bit signals. - - Typically we use a single bit to control backpressure or insert IDLE - cycles onto a bus. - - These yield a tuple which is intended to be interpreted as a number of - cycles ``(ON,OFF)`` -""" -from cocotb.decorators import public -from cocotb.generators import gaussian, sine_wave, repeat - - -def bit_toggler(gen_on, gen_off): - """Combines two generators to provide cycles_on, cycles_off tuples - - Args: - gen_on (generator): generator that yields number of cycles on - gen_off (generator): generator that yields number of cycles off - - .. deprecated:: 1.4.1 - """ - for n_on, n_off in zip(gen_on, gen_off): - yield int(abs(n_on)), int(abs(n_off)) - - -@public -def intermittent_single_cycles(mean=10, sigma=None): - """Generator to intermittently insert a single cycle pulse - - Args: - mean (int, optional): Average number of cycles in between single cycle gaps - sigma (int, optional): Standard deviation of gaps. mean/4 if sigma is None - - .. deprecated:: 1.4.1 - """ - if sigma is None: - sigma = mean / 4.0 - - return bit_toggler(gaussian(mean, sigma), repeat(1)) - - -@public -def random_50_percent(mean=10, sigma=None): - """50% duty cycle with random width - - Args: - mean (int, optional): Average number of cycles on/off - sigma (int, optional): Standard deviation of gaps. mean/4 if sigma is None - - .. deprecated:: 1.4.1 - """ - if sigma is None: - sigma = mean / 4.0 - for duration in gaussian(mean, sigma): - yield int(abs(duration)), int(abs(duration)) - - -@public -def wave(on_ampl=30, on_freq=200, off_ampl=10, off_freq=100): - """ - Drive a repeating sine_wave pattern - - TODO: - Adjust args so we just specify a repeat duration and overall throughput - - .. deprecated:: 1.4.1 - """ - return bit_toggler(sine_wave(on_ampl, on_freq), - sine_wave(off_ampl, off_freq)) diff --git a/cocotb/generators/byte.py b/cocotb/generators/byte.py deleted file mode 100755 index e1ce0588..00000000 --- a/cocotb/generators/byte.py +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -""" - Collection of generators for creating byte streams. - - Note that on Python 3, individual bytes are represented with integers. -""" -import random -import itertools -from cocotb.decorators import public -from typing import Iterator - - -@public -def get_bytes(nbytes: int, generator: Iterator[int]) -> bytes: - """ - Get nbytes from generator - - .. versionchanged:: 1.4.0 - This now returns :class:`bytes`, not :class:`str`. - - .. deprecated:: 1.4.1 - """ - return bytes(next(generator) for i in range(nbytes)) - - -@public -def random_data() -> Iterator[int]: - r""" - Random bytes - - .. versionchanged:: 1.4.0 - This now returns integers, not single-character :class:`str`\ s. - - .. deprecated:: 1.4.1 - """ - while True: - yield random.randrange(256) - - -@public -def incrementing_data(increment=1) -> Iterator[int]: - r""" - Incrementing bytes - - .. versionchanged:: 1.4.0 - This now returns integers, not single-character :class:`str`\ s. - - .. deprecated:: 1.4.1 - """ - val = 0 - while True: - yield val - val += increment - val = val & 0xFF - - -@public -def repeating_bytes(pattern: bytes = b"\x00") -> Iterator[int]: - """ - Repeat a pattern of bytes - - .. deprecated:: 1.4.1 - """ - return itertools.cycle(pattern) diff --git a/cocotb/generators/packet.py b/cocotb/generators/packet.py deleted file mode 100644 index ca478ed3..00000000 --- a/cocotb/generators/packet.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -""" - Collection of Ethernet Packet generators to use for testdata generation - - Most generators take the keyword argument "payload" which can be - used to control the payload contents if required. Defaults to random data. -""" -import random - -from scapy.all import Ether, IP, UDP - -# Supress SCAPY warning messages -import logging -logging.getLogger("scapy").setLevel(logging.ERROR) - -from cocotb.decorators import public -from cocotb.generators.byte import get_bytes, random_data - -_default_payload = random_data - - -# UDP packet generators -@public -def udp_all_sizes(max_size=1500, payload=_default_payload()): - """ - UDP packets of every supported size - - .. deprecated:: 1.4.1 - """ - header = Ether() / IP() / UDP() - - for size in range(0, max_size - len(header)): - yield header / get_bytes(size, payload) - - -@public -def udp_random_sizes(npackets=100, payload=_default_payload()): - """ - UDP packets with random sizes - - .. deprecated:: 1.4.1 - """ - header = Ether() / IP() / UDP() - max_size = 1500 - len(header) - - for pkt in range(npackets): - yield header / get_bytes(random.randint(0, max_size), payload) - - -# IPV4 generator -@public -def ipv4_small_packets(npackets=100, payload=_default_payload()): - """ - Small (<100bytes payload) IPV4 packets - - .. deprecated:: 1.4.1 - """ - for pkt in range(npackets): - yield Ether() / IP() / get_bytes(random.randint(0, 100), payload) diff --git a/cocotb/handle.py b/cocotb/handle.py deleted file mode 100755 index a6163cdf..00000000 --- a/cocotb/handle.py +++ /dev/null @@ -1,1028 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -# -*- coding: utf-8 -*- - -import ctypes -import warnings -import enum -from functools import lru_cache - -import cocotb -from cocotb import simulator -from cocotb.binary import BinaryValue, BinaryRepresentation -from cocotb.log import SimLog - -# Only issue a warning for each deprecated attribute access -_deprecation_warned = set() - - -class _Limits(enum.IntEnum): - SIGNED_NBIT = 1 - UNSIGNED_NBIT = 2 - VECTOR_NBIT = 3 - - -@lru_cache(maxsize=None) -def _value_limits(n_bits, limits): - """Calculate min/max for given number of bits and limits class""" - if limits == _Limits.SIGNED_NBIT: - min_val = -2**(n_bits-1) - max_val = 2**(n_bits-1) - 1 - elif limits == _Limits.UNSIGNED_NBIT: - min_val = 0 - max_val = 2**n_bits - 1 - else: - min_val = -2**(n_bits-1) - max_val = 2**n_bits - 1 - - return min_val, max_val - - -class SimHandleBase: - """Base class for all simulation objects. - - We maintain a handle which we can use for GPI calls. - """ - - # For backwards compatibility we support a mapping of old member names - # which may alias with the simulator hierarchy. In these cases the - # simulator result takes priority, only falling back to the python member - # if there is no colliding object in the elaborated design. - _compat_mapping = { - "log" : "_log", - "fullname" : "_fullname", - "name" : "_name", - } - - def __init__(self, handle, path): - """ - .. Constructor. This RST comment works around sphinx-doc/sphinx#6885 - - Args: - handle (int): The GPI handle to the simulator object. - path (str): Path to this handle, ``None`` if root. - """ - self._handle = handle - self._len = None # type: int - """The "length" (the number of elements) of the underlying object. For vectors this is the number of bits.""" - self._sub_handles = {} # type: dict - """Dictionary of this handle's children.""" - self._invalid_sub_handles = set() # type: set - """Python :class:`set` of invalid queries, for caching purposes.""" - self._name = self._handle.get_name_string() # type: str - """The name of an object. - - :meta public: - """ - self._type = self._handle.get_type_string() # type: str - """The type of an object as a string. - - :meta public: - """ - self._fullname = self._name + "(%s)" % self._type # type: str - """The name of an object with its type appended in parentheses.""" - self._path = self._name if path is None else path # type: str - """The path to this handle, or its name if this is the root handle. - - :meta public: - """ - self._log = SimLog("cocotb.%s" % self._name) - """The logging object.""" - self._log.debug("Created") - self._def_name = self._handle.get_definition_name() # type: str - """The name of a GPI object's definition. - - This is the value of ``vpiDefName`` for VPI, ``vhpiNameP`` for VHPI, - and ``mti_GetPrimaryName`` for FLI. - Support for this depends on the specific object type and simulator used. - - :meta public: - """ - self._def_file = self._handle.get_definition_file() # type: str - """The name of the file that sources the object's definition. - - This is the value of ``vpiDefFile`` for VPI, ``vhpiFileNameP`` for VHPI, - and ``mti_GetRegionSourceName`` for FLI. - Support for this depends on the specific object type and simulator used. - - :meta public: - """ - - def get_definition_name(self): - return self._def_name - - def get_definition_file(self): - return self._def_file - - def __hash__(self): - return hash(self._handle) - - def __len__(self): - """Return the "length" (the number of elements) of the underlying object. - - For vectors this is the number of bits. - """ - if self._len is None: - self._len = self._handle.get_num_elems() - return self._len - - def __eq__(self, other): - """Equality comparator for handles - - Example usage:: - - if clk == dut.clk: - do_something() - """ - if not isinstance(other, SimHandleBase): - return NotImplemented - return self._handle == other._handle - - def __ne__(self, other): - if not isinstance(other, SimHandleBase): - return NotImplemented - return self._handle != other._handle - - def __repr__(self): - desc = self._path - defname = self._def_name - if defname: - desc += " with definition "+defname - deffile = self._def_file - if deffile: - desc += " (at "+deffile+")" - return type(self).__qualname__ + "(" + desc + ")" - - def __str__(self): - return self._path - - def __setattr__(self, name, value): - if name in self._compat_mapping: - if name not in _deprecation_warned: - warnings.warn("Use of attribute %r is deprecated, use %r instead" % (name, self._compat_mapping[name]), - DeprecationWarning, stacklevel=3) - _deprecation_warned.add(name) - return setattr(self, self._compat_mapping[name], value) - else: - return object.__setattr__(self, name, value) - - def __getattr__(self, name): - if name in self._compat_mapping: - if name not in _deprecation_warned: - warnings.warn("Use of attribute %r is deprecated, use %r instead" % (name, self._compat_mapping[name]), - DeprecationWarning, stacklevel=3) - _deprecation_warned.add(name) - return getattr(self, self._compat_mapping[name]) - else: - return object.__getattribute__(self, name) - - -class RegionObject(SimHandleBase): - """A region object, such as a scope or namespace. - - Region objects don't have values, they are effectively scopes or namespaces. - """ - - def __init__(self, handle, path): - SimHandleBase.__init__(self, handle, path) - self._discovered = False # True if this object has already been discovered - - def __iter__(self): - """Iterate over all known objects in this layer of hierarchy.""" - if not self._discovered: - self._discover_all() - - for name, handle in self._sub_handles.items(): - if isinstance(handle, list): - self._log.debug("Found index list length %d", len(handle)) - for subindex, subhdl in enumerate(handle): - if subhdl is None: - self._log.warning("Index %d doesn't exist in %s.%s", subindex, self._name, name) - continue - self._log.debug("Yielding index %d from %s (%s)", subindex, name, type(subhdl)) - yield subhdl - else: - self._log.debug("Yielding %s of type %s (%s)", name, type(handle), handle._path) - yield handle - - def _discover_all(self): - """When iterating or performing IPython tab completion, we run through ahead of - time and discover all possible children, populating the :any:`_sub_handles` - mapping. Hierarchy can't change after elaboration so we only have to - do this once. - """ - if self._discovered: - return - self._log.debug("Discovering all on %s", self._name) - for thing in self._handle.iterate(simulator.OBJECTS): - name = thing.get_name_string() - path = self._child_path(name) - try: - hdl = SimHandle(thing, path) - except NotImplementedError as e: - self._log.debug("%s", e) - continue - - try: - key = self._sub_handle_key(name) - except ValueError: - self._log.debug("Unable to translate handle >%s< to a valid _sub_handle key", hdl._name) - continue - - self._sub_handles[key] = hdl - - self._discovered = True - - def _child_path(self, name) -> str: - """Return a string of the path of the child :any:`SimHandle` for a given *name*.""" - return self._path + "." + name - - def _sub_handle_key(self, name): - """Translate the handle name to a key to use in :any:`_sub_handles` dictionary.""" - return name.split(".")[-1] - - def __dir__(self): - """Permits IPython tab completion to work.""" - self._discover_all() - return super(RegionObject, self).__dir__() + [str(k) for k in self._sub_handles] - - -class HierarchyObject(RegionObject): - """Hierarchy objects are namespace/scope objects.""" - - def __get_sub_handle_by_name(self, name): - try: - return self._sub_handles[name] - except KeyError: - pass - - # Cache to avoid a call to the simulator if we already know the name is - # invalid. Unclear if we care, but we had this before. - if name in self._invalid_sub_handles: - return None - - new_handle = self._handle.get_handle_by_name(name) - - if not new_handle: - self._invalid_sub_handles.add(name) - return None - - sub_handle = SimHandle(new_handle, self._child_path(name)) - self._sub_handles[name] = sub_handle - return sub_handle - - def __setattr__(self, name, value): - """Provide transparent access to signals via the hierarchy. - - Slightly hacky version of operator overloading in Python. - - Raise an :exc:`AttributeError` if users attempt to create new members which - don't exist in the design. - """ - - # private attributes pass through directly - if name.startswith("_"): - return SimHandleBase.__setattr__(self, name, value) - - # then try handles - sub = self.__get_sub_handle_by_name(name) - if sub is not None: - sub.value = value - return - - # compat behavior - if name in self._compat_mapping: - return SimHandleBase.__setattr__(self, name, value) - - raise AttributeError("%s contains no object named %s" % (self._name, name)) - - def __getattr__(self, name): - """Query the simulator for an object with the specified name - and cache the result to build a tree of objects. - """ - if name.startswith("_"): - return SimHandleBase.__getattr__(self, name) - - handle = self.__get_sub_handle_by_name(name) - if handle is not None: - return handle - - if name in self._compat_mapping: - return SimHandleBase.__getattr__(self, name) - - raise AttributeError("%s contains no object named %s" % (self._name, name)) - - def _id(self, name, extended: bool = True): - """Query the simulator for an object with the specified *name*, - and cache the result to build a tree of objects. - - If *extended* is ``True``, run the query only for VHDL extended identifiers. - For Verilog, only ``extended=False`` is supported. - - :meta public: - """ - if extended: - name = "\\"+name+"\\" - - handle = self.__get_sub_handle_by_name(name) - if handle is not None: - return handle - - raise AttributeError("%s contains no object named %s" % (self._name, name)) - - -class HierarchyArrayObject(RegionObject): - """Hierarchy Arrays are containers of Hierarchy Objects.""" - - def _sub_handle_key(self, name): - """Translate the handle name to a key to use in :any:`_sub_handles` dictionary.""" - # This is slightly hacky, but we need to extract the index from the name - # - # FLI and VHPI(IUS): _name(X) where X is the index - # VHPI(ALDEC): _name__X where X is the index - # VPI: _name[X] where X is the index - import re - result = re.match(r"{0}__(?P\d+)$".format(self._name), name) - if not result: - result = re.match(r"{0}\((?P\d+)\)$".format(self._name), name) - if not result: - result = re.match(r"{0}\[(?P\d+)\]$".format(self._name), name) - - if result: - return int(result.group("index")) - else: - raise ValueError("Unable to match an index pattern: {}".format(name)) - - def __len__(self): - """Return the "length" of the generate block.""" - if self._len is None: - if not self._discovered: - self._discover_all() - - self._len = len(self._sub_handles) - return self._len - - def __getitem__(self, index): - if isinstance(index, slice): - raise IndexError("Slice indexing is not supported") - if index in self._sub_handles: - return self._sub_handles[index] - new_handle = self._handle.get_handle_by_index(index) - if not new_handle: - raise IndexError("%s contains no object at index %d" % (self._name, index)) - path = self._path + "[" + str(index) + "]" - self._sub_handles[index] = SimHandle(new_handle, path) - return self._sub_handles[index] - - def _child_path(self, name): - """Return a string of the path of the child :any:`SimHandle` for a given name.""" - index = self._sub_handle_key(name) - return self._path + "[" + str(index) + "]" - - def __setitem__(self, index, value): - raise TypeError("Not permissible to set %s at index %d" % (self._name, index)) - - -class _AssignmentResult: - """ - An object that exists solely to provide an error message if the caller - is not aware of cocotb's meaning of ``<=``. - """ - - def __init__(self, signal, value): - self._signal = signal - self._value = value - - def __bool__(self): - raise TypeError( - "Attempted to use `{0._signal!r} <= {0._value!r}` (a cocotb " - "delayed write) as if it were a numeric comparison. To perform " - "comparison, use `{0._signal!r}.value <= {0._value!r}` instead." - .format(self) - ) - - -class NonHierarchyObject(SimHandleBase): - """Common base class for all non-hierarchy objects.""" - - def __iter__(self): - return iter(()) - - @property - def value(self): - """The value of this simulation object. - - .. note:: - When setting this property, the value is stored by the :class:`~cocotb.scheduler.Scheduler` - and all stored values are written at the same time at the end of the current simulator time step. - - Use :meth:`setimmediatevalue` to set the value immediately. - """ - raise TypeError("Not permissible to get values of object %s of type %s" % (self._name, type(self))) - - @value.setter - def value(self, value): - self._set_value(value, cocotb.scheduler._schedule_write) - - def setimmediatevalue(self, value): - """ Assign a value to this simulation object immediately. """ - def _call_now(handle, f, *args): - f(*args) - self._set_value(value, _call_now) - - def _set_value(self, value, call_sim): - """ This should be overriden in subclasses. - - This is used to implement both the setter for :attr:`value`, and the - :meth:`setimmediatevalue` method. - - ``call_sim(handle, f, *args)`` should be used to schedule simulator writes, - rather than performing them directly as ``f(*args)``. - """ - raise TypeError("Not permissible to set values on object %s of type %s" % (self._name, type(self))) - - def __le__(self, value): - """Overload less-than-or-equal-to operator to provide an HDL-like shortcut. - - Example: - >>> module.signal <= 2 - """ - self.value = value - return _AssignmentResult(self, value) - - def __eq__(self, other): - """Equality comparator for non-hierarchy objects - - If ``other`` is not a :class:`SimHandleBase` instance the comparision - uses the comparison method of the ``other`` object against our - ``.value``. - """ - if isinstance(other, SimHandleBase): - return SimHandleBase.__eq__(self, other) - return self.value == other - - def __ne__(self, other): - if isinstance(other, SimHandleBase): - return SimHandleBase.__ne__(self, other) - return self.value != other - - # Re-define hash because we defined __eq__ - def __hash__(self): - return SimHandleBase.__hash__(self) - - -class ConstantObject(NonHierarchyObject): - """An object which has a value that can be read, but not set. - - The value is cached in the class since it is fixed at elaboration - time and won't change within a simulation. - """ - - def __init__(self, handle, path, handle_type): - """ - Args: - handle (int): The GPI handle to the simulator object. - path (str): Path to this handle, ``None`` if root. - handle_type: The type of the handle - (``simulator.INTEGER``, ``simulator.ENUM``, - ``simulator.REAL``, ``simulator.STRING``). - """ - NonHierarchyObject.__init__(self, handle, path) - if handle_type in [simulator.INTEGER, simulator.ENUM]: - self._value = self._handle.get_signal_val_long() - elif handle_type == simulator.REAL: - self._value = self._handle.get_signal_val_real() - elif handle_type == simulator.STRING: - self._value = self._handle.get_signal_val_str() - else: - val = self._handle.get_signal_val_binstr() - self._value = BinaryValue(n_bits=len(val)) - try: - self._value.binstr = val - except Exception: - self._value = val - - def __int__(self): - return int(self.value) - - def __float__(self): - return float(self.value) - - @NonHierarchyObject.value.getter - def value(self): - """The value of this simulation object.""" - return self._value - - def __str__(self): - if isinstance(self.value, bytes): - StringObject._emit_str_warning(self) - return self.value.decode('ascii') - else: - ModifiableObject._emit_str_warning(self) - return str(self.value) - - -class NonHierarchyIndexableObject(NonHierarchyObject): - """ A non-hierarchy indexable object. - - Getting and setting the current value of an array is done - by iterating through sub-handles in left-to-right order. - - Given an HDL array ``arr``: - - +--------------+---------------------+--------------------------------------------------------------+ - | Verilog | VHDL | ``arr.value`` is equivalent to | - +==============+=====================+==============================================================+ - | ``arr[4:7]`` | ``arr(4 to 7)`` | ``[arr[4].value, arr[5].value, arr[6].value, arr[7].value]`` | - +--------------+---------------------+--------------------------------------------------------------+ - | ``arr[7:4]`` | ``arr(7 downto 4)`` | ``[arr[7].value, arr[6].value, arr[5].value, arr[4].value]`` | - +--------------+---------------------+--------------------------------------------------------------+ - - When setting the signal as in ``arr.value = ...``, the same index equivalence as noted in the table holds. - - .. warning:: - Assigning a value to a sub-handle: - - - **Wrong**: ``dut.some_array.value[0] = 1`` (gets value as a list then updates index 0) - - **Correct**: ``dut.some_array[0].value = 1`` - """ - - def __init__(self, handle, path): - NonHierarchyObject.__init__(self, handle, path) - self._range = self._handle.get_range() - - def __setitem__(self, index, value): - """Provide transparent assignment to indexed array handles.""" - self[index].value = value - - def __getitem__(self, index): - if isinstance(index, slice): - raise IndexError("Slice indexing is not supported") - if self._range is None: - raise IndexError("%s is not indexable. Unable to get object at index %d" % (self._fullname, index)) - if index in self._sub_handles: - return self._sub_handles[index] - new_handle = self._handle.get_handle_by_index(index) - if not new_handle: - raise IndexError("%s contains no object at index %d" % (self._fullname, index)) - path = self._path + "[" + str(index) + "]" - self._sub_handles[index] = SimHandle(new_handle, path) - return self._sub_handles[index] - - def __iter__(self): - if self._range is None: - return - - self._log.debug("Iterating with range [%d:%d]", self._range[0], self._range[1]) - for i in self._range_iter(self._range[0], self._range[1]): - try: - result = self[i] - yield result - except IndexError: - continue - - def _range_iter(self, left, right): - if left > right: - while left >= right: - yield left - left = left - 1 - else: - while left <= right: - yield left - left = left + 1 - - @NonHierarchyObject.value.getter - def value(self) -> list: - # Don't use self.__iter__, because it has an unwanted `except IndexError` - return [ - self[i].value - for i in self._range_iter(self._range[0], self._range[1]) - ] - - def _set_value(self, value, call_sim): - """Assign value from a list of same length to an array in left-to-right order. - Index 0 of the list maps to the left-most index in the array. - - See the docstring for this class. - """ - if type(value) is not list: - raise TypeError("Assigning non-list value to object %s of type %s" % (self._name, type(self))) - if len(value) != len(self): - raise ValueError("Assigning list of length %d to object %s of length %d" % ( - len(value), self._name, len(self))) - for val_idx, self_idx in enumerate(self._range_iter(self._range[0], self._range[1])): - self[self_idx]._set_value(value[val_idx], call_sim) - - -class NonConstantObject(NonHierarchyIndexableObject): - """ A non-constant object""" - # FIXME: what is the difference to ModifiableObject? Explain in docstring. - - def drivers(self): - """An iterator for gathering all drivers for a signal. - - This is currently only available for VPI. - Also, only a few simulators implement this. - """ - return self._handle.iterate(simulator.DRIVERS) - - def loads(self): - """An iterator for gathering all loads on a signal. - - This is currently only available for VPI. - Also, only a few simulators implement this. - """ - return self._handle.iterate(simulator.LOADS) - - -class _SetAction: - """Base class representing the type of action used while write-accessing a handle.""" - pass - - -class _SetValueAction(_SetAction): - __slots__ = ("value",) - """Base class representing the type of action used while write-accessing a handle with a value.""" - - def __init__(self, value): - self.value = value - - -class Deposit(_SetValueAction): - """Action used for placing a value into a given handle.""" - - def _as_gpi_args_for(self, hdl): - return self.value, 0 # GPI_DEPOSIT - - -class Force(_SetValueAction): - """Action used to force a handle to a given value until a release is applied.""" - - def _as_gpi_args_for(self, hdl): - return self.value, 1 # GPI_FORCE - - -class Freeze(_SetAction): - """Action used to make a handle keep its current value until a release is used.""" - - def _as_gpi_args_for(self, hdl): - return hdl.value, 1 # GPI_FORCE - - -class Release(_SetAction): - """Action used to stop the effects of a previously applied force/freeze action.""" - - def _as_gpi_args_for(self, hdl): - return 0, 2 # GPI_RELEASE - - -class ModifiableObject(NonConstantObject): - """Base class for simulator objects whose values can be modified.""" - - def _set_value(self, value, call_sim): - """Set the value of the underlying simulation object to *value*. - - This operation will fail unless the handle refers to a modifiable - object, e.g. net, signal or variable. - - We determine the library call to make based on the type of the value - because assigning integers less than 32 bits is faster. - - Args: - value (cocotb.binary.BinaryValue, int): - The value to drive onto the simulator object. - - Raises: - TypeError: If target has an unsupported type for value assignment. - - OverflowError: If int value is out of the range that can be represented - by the target. -2**(Nbits - 1) <= value <= 2**Nbits - 1 - - .. deprecated:: 1.5 - :class:`ctypes.Structure` objects are no longer accepted as values for assignment. - Convert the struct object to a :class:`~cocotb.binary.BinaryValue` before assignment using - ``BinaryValue(value=bytes(struct_obj), n_bits=len(signal))`` instead. - """ - value, set_action = self._check_for_set_action(value) - - if isinstance(value, int): - min_val, max_val = _value_limits(len(self), _Limits.VECTOR_NBIT) - if min_val <= value <= max_val: - if len(self) <= 32: - call_sim(self, self._handle.set_signal_val_int, set_action, value) - return - - if value < 0: - value = BinaryValue(value=value, n_bits=len(self), bigEndian=False, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) - else: - value = BinaryValue(value=value, n_bits=len(self), bigEndian=False, binaryRepresentation=BinaryRepresentation.UNSIGNED) - else: - raise OverflowError( - "Int value ({!r}) out of range for assignment of {!r}-bit signal ({!r})" - .format(value, len(self), self._name)) - - if isinstance(value, ctypes.Structure): - warnings.warn( - "`ctypes.Structure` values are no longer accepted for value assignment. " - "Use `BinaryValue(value=bytes(struct_obj), n_bits=len(signal))` instead", - DeprecationWarning, stacklevel=3) - value = BinaryValue(value=cocotb.utils.pack(value), n_bits=len(self)) - elif isinstance(value, dict): - # We're given a dictionary with a list of values and a bit size... - num = 0 - vallist = list(value["values"]) - vallist.reverse() - if len(vallist) * value["bits"] != len(self): - raise TypeError("Unable to set with array length %d of %d bit entries = %d total, target is only %d bits long" % - (len(value["values"]), value["bits"], len(value["values"]) * value["bits"], len(self))) - - for val in vallist: - num = (num << value["bits"]) + val - value = BinaryValue(value=num, n_bits=len(self), bigEndian=False) - - elif not isinstance(value, BinaryValue): - raise TypeError( - "Unsupported type for value assignment: {} ({!r})" - .format(type(value), value)) - - call_sim(self, self._handle.set_signal_val_binstr, set_action, value.binstr) - - def _check_for_set_action(self, value): - if not isinstance(value, _SetAction): - return value, 0 # GPI_DEPOSIT - return value._as_gpi_args_for(self) - - @NonConstantObject.value.getter - def value(self) -> BinaryValue: - binstr = self._handle.get_signal_val_binstr() - # Skip BinaryValue.assign() as we know we are using a binstr - result = BinaryValue(n_bits=len(binstr)) - # Skip the permitted characters check as we trust the simulator - result._set_trusted_binstr(binstr) - return result - - def __int__(self): - return int(self.value) - - def _emit_str_warning(self): - warnings.warn( - "`str({t})` is deprecated, and in future will return `{t}._path`. " - "To get a string representation of the value, use `str({t}.value)`." - .format(t=type(self).__qualname__), - FutureWarning, stacklevel=3) - - def __str__(self): - self._emit_str_warning() - return str(self.value) - - -class RealObject(ModifiableObject): - """Specific object handle for Real signals and variables.""" - - def _set_value(self, value, call_sim): - """Set the value of the underlying simulation object to value. - - This operation will fail unless the handle refers to a modifiable - object, e.g. net, signal or variable. - - Args: - value (float): The value to drive onto the simulator object. - - Raises: - TypeError: If target has an unsupported type for - real value assignment. - """ - value, set_action = self._check_for_set_action(value) - - try: - value = float(value) - except ValueError: - raise TypeError( - "Unsupported type for real value assignment: {} ({!r})" - .format(type(value), value)) - - call_sim(self, self._handle.set_signal_val_real, set_action, value) - - @ModifiableObject.value.getter - def value(self) -> float: - return self._handle.get_signal_val_real() - - def __float__(self): - return float(self.value) - - -class EnumObject(ModifiableObject): - """Specific object handle for enumeration signals and variables.""" - - def _set_value(self, value, call_sim): - """Set the value of the underlying simulation object to *value*. - - This operation will fail unless the handle refers to a modifiable - object, e.g. net, signal or variable. - - Args: - value (int): The value to drive onto the simulator object. - - Raises: - TypeError: If target has an unsupported type for - integer value assignment. - """ - value, set_action = self._check_for_set_action(value) - - if isinstance(value, BinaryValue): - value = int(value) - elif not isinstance(value, int): - raise TypeError( - "Unsupported type for enum value assignment: {} ({!r})" - .format(type(value), value)) - - min_val, max_val = _value_limits(32, _Limits.UNSIGNED_NBIT) - if min_val <= value <= max_val: - call_sim(self, self._handle.set_signal_val_int, set_action, value) - else: - raise OverflowError( - "Int value ({!r}) out of range for assignment of enum signal ({!r})" - .format(value, self._name)) - - @ModifiableObject.value.getter - def value(self) -> int: - return self._handle.get_signal_val_long() - - -class IntegerObject(ModifiableObject): - """Specific object handle for Integer and Enum signals and variables.""" - - def _set_value(self, value, call_sim): - """Set the value of the underlying simulation object to *value*. - - This operation will fail unless the handle refers to a modifiable - object, e.g. net, signal or variable. - - Args: - value (int): The value to drive onto the simulator object. - - Raises: - TypeError: If target has an unsupported type for - integer value assignment. - - OverflowError: If value is out of range for assignment - of 32-bit IntegerObject. - """ - value, set_action = self._check_for_set_action(value) - - if isinstance(value, BinaryValue): - value = int(value) - elif not isinstance(value, int): - raise TypeError( - "Unsupported type for integer value assignment: {} ({!r})" - .format(type(value), value)) - - min_val, max_val = _value_limits(32, _Limits.SIGNED_NBIT) - if min_val <= value <= max_val: - call_sim(self, self._handle.set_signal_val_int, set_action, value) - else: - raise OverflowError( - "Int value ({!r}) out of range for assignment of integer signal ({!r})" - .format(value, self._name)) - - @ModifiableObject.value.getter - def value(self) -> int: - return self._handle.get_signal_val_long() - - -class StringObject(ModifiableObject): - """Specific object handle for String variables.""" - - def _set_value(self, value, call_sim): - """Set the value of the underlying simulation object to *value*. - - This operation will fail unless the handle refers to a modifiable - object, e.g. net, signal or variable. - - Args: - value (bytes): The value to drive onto the simulator object. - - Raises: - TypeError: If target has an unsupported type for - string value assignment. - - .. versionchanged:: 1.4 - Takes :class:`bytes` instead of :class:`str`. - Users are now expected to choose an encoding when using these objects. - As a convenience, when assigning :class:`str` values, ASCII encoding will be used as a safe default. - - """ - value, set_action = self._check_for_set_action(value) - - if isinstance(value, str): - warnings.warn( - "Handles on string objects will soon not accept `str` objects. " - "Please use a bytes object by encoding the string as you see fit. " - "`str.encode('ascii')` is typically sufficient.", DeprecationWarning, stacklevel=2) - value = value.encode('ascii') # may throw UnicodeEncodeError - - if not isinstance(value, bytes): - raise TypeError( - "Unsupported type for string value assignment: {} ({!r})" - .format(type(value), value)) - - call_sim(self, self._handle.set_signal_val_str, set_action, value) - - @ModifiableObject.value.getter - def value(self) -> bytes: - return self._handle.get_signal_val_str() - - def _emit_str_warning(self): - warnings.warn( - "`str({t})` is deprecated, and in future will return `{t}._path`. " - "To access the `bytes` value of this handle, use `{t}.value`." - .format(t=type(self).__qualname__), - FutureWarning, stacklevel=3) - - def __str__(self): - self._emit_str_warning() - return self.value.decode('ascii') - - -_handle2obj = {} - - -def SimHandle(handle, path=None): - """Factory function to create the correct type of `SimHandle` object. - - Args: - handle (int): The GPI handle to the simulator object. - path (str): Path to this handle, ``None`` if root. - - Returns: - The `SimHandle` object. - - Raises: - NotImplementedError: If no matching object for GPI type could be found. - """ - _type2cls = { - simulator.MODULE: HierarchyObject, - simulator.STRUCTURE: HierarchyObject, - simulator.REG: ModifiableObject, - simulator.NET: ModifiableObject, - simulator.NETARRAY: NonHierarchyIndexableObject, - simulator.REAL: RealObject, - simulator.INTEGER: IntegerObject, - simulator.ENUM: EnumObject, - simulator.STRING: StringObject, - simulator.GENARRAY: HierarchyArrayObject, - } - - # Enforce singletons since it's possible to retrieve handles avoiding - # the hierarchy by getting driver/load information - global _handle2obj - try: - return _handle2obj[handle] - except KeyError: - pass - - t = handle.get_type() - - # Special case for constants - if handle.get_const() and t not in [ - simulator.MODULE, - simulator.STRUCTURE, - simulator.NETARRAY, - simulator.GENARRAY, - ]: - obj = ConstantObject(handle, path, t) - _handle2obj[handle] = obj - return obj - - if t not in _type2cls: - raise NotImplementedError("Couldn't find a matching object for GPI type %s(%d) (path=%s)" % (handle.get_type_string(), t, path)) - obj = _type2cls[t](handle, path) - _handle2obj[handle] = obj - return obj diff --git a/cocotb/ipython_support.py b/cocotb/ipython_support.py deleted file mode 100644 index 51366810..00000000 --- a/cocotb/ipython_support.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -import IPython -from IPython.terminal.ipapp import load_default_config -from IPython.terminal.prompts import Prompts, Token - -import cocotb - - -class SimTimePrompt(Prompts): - """ custom prompt that shows the sim time after a trigger fires """ - _show_time = 1 - - def in_prompt_tokens(self, cli=None): - tokens = super().in_prompt_tokens() - if self._show_time == self.shell.execution_count: - tokens = [ - (Token.Comment, "sim time: {}".format(cocotb.utils.get_sim_time())), - (Token.Text, "\n"), - ] + tokens - return tokens - - -def _runner(shell, x): - """ Handler for async functions """ - ret = cocotb.scheduler._queue_function(x) - shell.prompts._show_time = shell.execution_count - return ret - - -async def embed(user_ns: dict = {}): - """ - Start an ipython shell in the current coroutine. - - Unlike using :func:`IPython.embed` directly, the :keyword:`await` keyword - can be used directly from the shell to wait for triggers. - The :keyword:`yield` keyword from the legacy :ref:`yield-syntax` is not supported. - - This coroutine will complete only when the user exits the interactive session. - - Args: - user_ns: - The variables to have made available in the shell. - Passing ``locals()`` is often a good idea. - ``cocotb`` will automatically be included. - - Notes: - - If your simulator does not provide an appropriate ``stdin``, you may - find you cannot type in the resulting shell. Using simulators in batch - or non-GUI mode may resolve this. This feature is experimental, and - not all simulators are supported. - """ - # ensure cocotb is in the namespace, for convenience - default_ns = dict(cocotb=cocotb) - default_ns.update(user_ns) - - # build the config to enable `await` - c = load_default_config() - c.TerminalInteractiveShell.loop_runner = lambda x: _runner(shell, x) - c.TerminalInteractiveShell.autoawait = True - - # create a shell with access to the dut, and cocotb pre-imported - shell = IPython.terminal.embed.InteractiveShellEmbed( - user_ns=default_ns, - config=c, - ) - - # add our custom prompts - shell.prompts = SimTimePrompt(shell) - - # start the shell in a background thread - @cocotb.external - def run_shell(): - shell() - await run_shell() - - -@cocotb.test() -async def run_ipython(dut): - """ A test that launches an interactive Python shell. - - Do not call this directly - use this as ``make MODULE=cocotb.ipython_support``. - - Within the shell, a global ``dut`` variable pointing to the design will be present. - """ - await embed(user_ns=dict(dut=dut)) diff --git a/cocotb/log.py b/cocotb/log.py deleted file mode 100644 index 9b75309c..00000000 --- a/cocotb/log.py +++ /dev/null @@ -1,279 +0,0 @@ -# Copyright (c) 2013, 2018 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -""" -Everything related to logging -""" - -import os -import sys -import logging -import warnings - -from cocotb.utils import ( - get_sim_time, get_time_from_sim_steps, want_color_output -) - -import cocotb.ANSI as ANSI - -if "COCOTB_REDUCED_LOG_FMT" in os.environ: - _suppress = True -else: - _suppress = False - -# Column alignment -_LEVEL_CHARS = len("CRITICAL") # noqa -_RECORD_CHARS = 35 # noqa -_FILENAME_CHARS = 20 # noqa -_LINENO_CHARS = 4 # noqa -_FUNCNAME_CHARS = 31 # noqa - -# Default log level if not overwritten by the user. -_COCOTB_LOG_LEVEL_DEFAULT = "INFO" - - -def default_config(): - """ Apply the default cocotb log formatting to the root logger. - - This hooks up the logger to write to stdout, using either - :class:`SimColourLogFormatter` or :class:`SimLogFormatter` depending - on whether colored output is requested. It also adds a - :class:`SimTimeContextFilter` filter so that - :attr:`~logging.LogRecord.created_sim_time` is available to the formatter. - - The logging level for cocotb logs is set based on the - :envvar:`COCOTB_LOG_LEVEL` environment variable, which defaults to ``INFO``. - - If desired, this logging configuration can be overwritten by calling - ``logging.basicConfig(..., force=True)`` (in Python 3.8 onwards), or by - manually resetting the root logger instance. - An example of this can be found in the section on :ref:`rotating-logger`. - - .. versionadded:: 1.4 - """ - # construct an appropriate handler - hdlr = logging.StreamHandler(sys.stdout) - hdlr.addFilter(SimTimeContextFilter()) - if want_color_output(): - hdlr.setFormatter(SimColourLogFormatter()) - else: - hdlr.setFormatter(SimLogFormatter()) - - logging.setLoggerClass(SimBaseLog) # For backwards compatibility - logging.basicConfig() - logging.getLogger().handlers = [hdlr] # overwrite default handlers - - # apply level settings for cocotb - log = logging.getLogger('cocotb') - - try: - # All log levels are upper case, convert the user input for convenience. - level = os.environ["COCOTB_LOG_LEVEL"].upper() - except KeyError: - level = _COCOTB_LOG_LEVEL_DEFAULT - - try: - log.setLevel(level) - except ValueError: - valid_levels = ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG') - raise ValueError("Invalid log level %r passed through the " - "COCOTB_LOG_LEVEL environment variable. Valid log " - "levels: %s" % (level, ', '.join(valid_levels))) - - # Notify GPI of log level, which it uses as an optimization to avoid - # calling into Python. - from cocotb import simulator - simulator.log_level(log.getEffectiveLevel()) - - -class SimBaseLog(logging.getLoggerClass()): - """ This class only exists for backwards compatibility """ - - @property - def logger(self): - warnings.warn( - "the .logger attribute should not be used now that `SimLog` " - "returns a native logger instance directly.", - DeprecationWarning, stacklevel=2) - return self - - @property - def colour(self): - warnings.warn( - "the .colour attribute may be removed in future, use the " - "equivalent `cocotb.utils.want_color_output()` instead", - DeprecationWarning, stacklevel=2) - return want_color_output() - - -# this used to be a class, hence the unusual capitalization -def SimLog(name, ident=None): - """ Like logging.getLogger, but append a numeric identifier to the name """ - if ident is not None: - name = "%s.0x%x" % (name, ident) - return logging.getLogger(name) - - -class SimTimeContextFilter(logging.Filter): - """ - A filter to inject simulator times into the log records. - - This uses the approach described in the :ref:`Python logging cookbook `. - - This adds the :attr:`~logging.LogRecord.created_sim_time` attribute. - - .. versionadded:: 1.4 - """ - - # needed to make our docs render well - def __init__(self): - """""" - super().__init__() - - def filter(self, record): - try: - record.created_sim_time = get_sim_time() - except RecursionError: - # get_sim_time may try to log - if that happens, we can't - # attach a simulator time to this message. - record.created_sim_time = None - return True - - -class SimLogFormatter(logging.Formatter): - """Log formatter to provide consistent log message handling. - - This will only add simulator timestamps if the handler object this - formatter is attached to has a :class:`SimTimeContextFilter` filter - attached, which cocotb ensures by default. - """ - - # Removes the arguments from the base class. Docstring needed to make - # sphinx happy. - def __init__(self): - """ Takes no arguments. """ - super().__init__() - - # Justify and truncate - @staticmethod - def ljust(string, chars): - if len(string) > chars: - return ".." + string[(chars - 2) * -1:] - return string.ljust(chars) - - @staticmethod - def rjust(string, chars): - if len(string) > chars: - return ".." + string[(chars - 2) * -1:] - return string.rjust(chars) - - def _format(self, level, record, msg, coloured=False): - sim_time = getattr(record, 'created_sim_time', None) - if sim_time is None: - sim_time_str = " -.--ns" - else: - time_ns = get_time_from_sim_steps(sim_time, 'ns') - sim_time_str = "{:6.2f}ns".format(time_ns) - prefix = sim_time_str.rjust(11) + ' ' + level + ' ' - if not _suppress: - prefix += self.ljust(record.name, _RECORD_CHARS) + \ - self.rjust(os.path.split(record.filename)[1], _FILENAME_CHARS) + \ - ':' + self.ljust(str(record.lineno), _LINENO_CHARS) + \ - ' in ' + self.ljust(str(record.funcName), _FUNCNAME_CHARS) + ' ' - - # these lines are copied from the builtin logger - if record.exc_info: - # Cache the traceback text to avoid converting it multiple times - # (it's constant anyway) - if not record.exc_text: - record.exc_text = self.formatException(record.exc_info) - if record.exc_text: - if msg[-1:] != "\n": - msg = msg + "\n" - msg = msg + record.exc_text - - prefix_len = len(prefix) - if coloured: - prefix_len -= (len(level) - _LEVEL_CHARS) - pad = "\n" + " " * (prefix_len) - return prefix + pad.join(msg.split('\n')) - - def format(self, record): - """Prettify the log output, annotate with simulation time""" - - msg = record.getMessage() - level = record.levelname.ljust(_LEVEL_CHARS) - - return self._format(level, record, msg) - - -class SimColourLogFormatter(SimLogFormatter): - """Log formatter to provide consistent log message handling.""" - - loglevel2colour = { - logging.DEBUG : "%s", - logging.INFO : ANSI.COLOR_INFO + "%s" + ANSI.COLOR_DEFAULT, - logging.WARNING : ANSI.COLOR_WARNING + "%s" + ANSI.COLOR_DEFAULT, - logging.ERROR : ANSI.COLOR_ERROR + "%s" + ANSI.COLOR_DEFAULT, - logging.CRITICAL: ANSI.COLOR_CRITICAL + "%s" + ANSI.COLOR_DEFAULT, - } - - def format(self, record): - """Prettify the log output, annotate with simulation time""" - - msg = record.getMessage() - - # Need to colour each line in case coloring is applied in the message - msg = '\n'.join([SimColourLogFormatter.loglevel2colour[record.levelno] % line for line in msg.split('\n')]) - level = (SimColourLogFormatter.loglevel2colour[record.levelno] % - record.levelname.ljust(_LEVEL_CHARS)) - - return self._format(level, record, msg, coloured=True) - - -def _filter_from_c(logger_name, level): - return logging.getLogger(logger_name).isEnabledFor(level) - - -def _log_from_c(logger_name, level, filename, lineno, msg, function_name): - """ - This is for use from the C world, and allows us to insert C stack - information. - """ - logger = logging.getLogger(logger_name) - if logger.isEnabledFor(level): - record = logger.makeRecord( - logger.name, - level, - filename, - lineno, - msg, - None, - None, - function_name - ) - logger.handle(record) diff --git a/cocotb/memdebug.py b/cocotb/memdebug.py deleted file mode 100644 index 797d9057..00000000 --- a/cocotb/memdebug.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import cherrypy -import dowser - - -def start(port): - cherrypy.tree.mount(dowser.Root()) - cherrypy.config.update({ - 'environment': 'embedded', - 'server.socket_port': port - }) - cherrypy.engine.start() diff --git a/cocotb/outcomes.py b/cocotb/outcomes.py deleted file mode 100644 index ed4d0cae..00000000 --- a/cocotb/outcomes.py +++ /dev/null @@ -1,56 +0,0 @@ -""" -Inspired by https://github.com/python-trio/outcome - -An outcome is similar to the builtin `concurrent.futures.Future` -or `asyncio.Future`, but without being tied to a particular task model. -""" -import abc - -from cocotb.utils import remove_traceback_frames - - -def capture(fn, *args, **kwargs): - """ Obtain an `Outcome` representing the result of a function call """ - try: - return Value(fn(*args, **kwargs)) - except BaseException as e: - e = remove_traceback_frames(e, ['capture']) - return Error(e) - - -class Outcome(metaclass=abc.ABCMeta): - @abc.abstractmethod - def send(self, gen): - """ Send or throw this outcome into a generator """ - - @abc.abstractmethod - def get(self, gen): - """ Get the value of this outcome, or throw its exception """ - - -class Value(Outcome): - def __init__(self, value): - self.value = value - - def send(self, gen): - return gen.send(self.value) - - def get(self): - return self.value - - def __repr__(self): - return "Value({!r})".format(self.value) - - -class Error(Outcome): - def __init__(self, error): - self.error = error - - def send(self, gen): - return gen.throw(self.error) - - def get(self): - raise self.error - - def __repr__(self): - return "Error({!r})".format(self.error) diff --git a/cocotb/regression.py b/cocotb/regression.py deleted file mode 100644 index a5b0b6d8..00000000 --- a/cocotb/regression.py +++ /dev/null @@ -1,769 +0,0 @@ -# Copyright (c) 2013, 2018 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -"""All things relating to regression capabilities.""" - -import time -import inspect -from itertools import product -import sys -import os -import traceback -import pdb -from typing import Any, Optional, Tuple, Iterable - -import cocotb -import cocotb.ANSI as ANSI -from cocotb.log import SimLog -from cocotb.result import TestSuccess, SimFailure -from cocotb.utils import get_sim_time, remove_traceback_frames, want_color_output -from cocotb.xunit_reporter import XUnitReporter -from cocotb.decorators import test as Test, hook as Hook, RunningTask -from cocotb.outcomes import Outcome, Error -from cocotb.handle import SimHandle - -from cocotb import simulator - -_pdb_on_exception = "COCOTB_PDB_ON_EXCEPTION" in os.environ - -# Optional support for coverage collection of testbench files -coverage = None -if "COVERAGE" in os.environ: - try: - import coverage - except ImportError as e: - msg = ("Coverage collection requested but coverage module not available" - "\n" - "Import error was: %s\n" % repr(e)) - sys.stderr.write(msg) - - -def _my_import(name: str) -> Any: - mod = __import__(name) - components = name.split('.') - for comp in components[1:]: - mod = getattr(mod, comp) - return mod - - -_logger = SimLog(__name__) - - -class RegressionManager: - """Encapsulates all regression capability into a single place""" - - def __init__(self, dut: SimHandle, tests: Iterable[Test], hooks: Iterable[Hook]): - """ - Args: - dut (SimHandle): The root handle to pass into test functions. - tests (Iterable[Test]): tests to run - hooks (Iterable[Hook]): hooks to tun - """ - self._dut = dut - self._test = None - self._test_task = None - self._test_start_time = None - self._test_start_sim_time = None - self._cov = None - self.log = _logger - self.start_time = time.time() - self.test_results = [] - self.count = 0 - self.skipped = 0 - self.failures = 0 - self._tearing_down = False - - # Setup XUnit - ################### - - results_filename = os.getenv('COCOTB_RESULTS_FILE', "results.xml") - suite_name = os.getenv('RESULT_TESTSUITE', "all") - package_name = os.getenv('RESULT_TESTPACKAGE', "all") - - self.xunit = XUnitReporter(filename=results_filename) - - self.xunit.add_testsuite(name=suite_name, package=package_name) - - self.xunit.add_property(name="random_seed", value=str(cocotb.RANDOM_SEED)) - - # Setup Coverage - #################### - - if coverage is not None: - self.log.info("Enabling coverage collection of Python code") - # Exclude cocotb itself from coverage collection. - cocotb_package_dir = os.path.dirname(__file__) - self._cov = coverage.coverage(branch=True, omit=["{}/*".format(cocotb_package_dir)]) - self._cov.start() - - # Test Discovery - #################### - self._queue = [] - for test in tests: - self.log.info("Found test {}.{}".format(test.__module__, test.__qualname__)) - self._queue.append(test) - self.ntests = len(self._queue) - - if not self._queue: - self.log.warning("No tests were discovered") - - self._queue.sort(key=lambda test: (test.stage, test._id)) - - # Process Hooks - ################### - for hook in hooks: - self.log.info("Found hook {}.{}".format(hook.__module__, hook.__qualname__)) - self._init_hook(hook) - - @classmethod - def from_discovery(cls, dut: SimHandle): - """ - Obtains the test and hook lists by discovery. - - See :envvar:`MODULE` and :envvar:`TESTCASE` for details on how tests are discovered. - - Args: - dut (SimHandle): The root handle to pass into test functions. - """ - tests = cls._discover_tests() - hooks = cls._discover_hooks() - return cls(dut, tests, hooks) - - @staticmethod - def _discover_tests() -> Iterable[Test]: - """ - Discovers tests in files automatically. - - See :envvar:`MODULE` and :envvar:`TESTCASE` for details on how tests are discovered. - """ - module_str = os.getenv('MODULE') - test_str = os.getenv('TESTCASE') - - if module_str is None: - raise ValueError("Environment variable MODULE, which defines the module(s) to execute, is not defined.") - - modules = [s.strip() for s in module_str.split(',') if s.strip()] - - for module_name in modules: - try: - _logger.debug("Python Path: " + ",".join(sys.path)) - _logger.debug("PWD: " + os.getcwd()) - module = _my_import(module_name) - except Exception as E: - _logger.critical("Failed to import module %s: %s", module_name, E) - _logger.info("MODULE variable was \"%s\"", ".".join(modules)) - _logger.info("Traceback: ") - _logger.info(traceback.format_exc()) - raise - - if test_str: - - # Specific functions specified, don't auto-discover - for test_name in test_str.rsplit(','): - try: - test = getattr(module, test_name) - except AttributeError: - _logger.error("Requested test %s wasn't found in module %s", test_name, module_name) - err = AttributeError("Test %s doesn't exist in %s" % (test_name, module_name)) - raise err from None # discard nested traceback - - if not isinstance(test, Test): - _logger.error("Requested %s from module %s isn't a cocotb.test decorated coroutine", - test_name, module_name) - raise ImportError("Failed to find requested test %s" % test_name) - - # If we request a test manually, it should be run even if skip=True is set. - test.skip = False - - yield test - - # only look in first module for all functions and don't complain if all functions are not found - break - - # auto-discover - for thing in vars(module).values(): - if isinstance(thing, Test): - yield thing - - @staticmethod - def _discover_hooks() -> Iterable[Hook]: - """ - Discovers hooks automatically. - - See :envvar:`COCOTB_HOOKS` for details on how hooks are discovered. - """ - hooks_str = os.getenv('COCOTB_HOOKS', '') - hooks = [s.strip() for s in hooks_str.split(',') if s.strip()] - - for module_name in hooks: - _logger.info("Loading hook from module '" + module_name + "'") - module = _my_import(module_name) - - for thing in vars(module).values(): - if hasattr(thing, "im_hook"): - yield thing - - def _init_hook(self, hook: Hook) -> Optional[RunningTask]: - try: - test = hook(self._dut) - except Exception: - self.log.warning("Failed to initialize hook %s" % hook.name, exc_info=True) - else: - return cocotb.scheduler.add(test) - - def tear_down(self) -> None: - # prevent re-entering the tear down procedure - if not self._tearing_down: - self._tearing_down = True - else: - return - - # fail remaining tests - while True: - test = self.next_test() - if test is None: - break - self._record_result( - test=test, - outcome=Error(SimFailure), - wall_time_s=0, - sim_time_ns=0) - - # Write out final log messages - self._log_test_summary() - self._log_sim_summary() - self.log.info("Shutting down...") - - # Generate output reports - self.xunit.write() - if self._cov: - self._cov.stop() - self.log.info("Writing coverage data") - self._cov.save() - self._cov.html_report() - if cocotb._library_coverage is not None: - # TODO: move this once we have normal shutdown behavior to _sim_event - cocotb._library_coverage.stop() - cocotb._library_coverage.save() - - # Setup simulator finalization - simulator.stop_simulator() - - def next_test(self) -> Optional[Test]: - """Get the next test to run""" - if not self._queue: - return None - self.count += 1 - return self._queue.pop(0) - - def handle_result(self, test: RunningTask) -> None: - """Handle a test completing. - - Dump result to XML and schedule the next test (if any). Entered by the scheduler. - - Args: - test: The test that completed - """ - assert test is self._test_task - - real_time = time.time() - self._test_start_time - sim_time_ns = get_sim_time('ns') - self._test_start_sim_time - - # stop capturing log output - cocotb.log.removeHandler(test.handler) - - self._record_result( - test=self._test, - outcome=self._test_task._outcome, - wall_time_s=real_time, - sim_time_ns=sim_time_ns) - - self.execute() - - def _init_test(self, test: Test) -> Optional[RunningTask]: - """Initialize a test. - - Record outcome if the initialization fails. - Record skip if the test is skipped. - Save the initialized test if it successfully initializes. - """ - - if test.skip: - hilight_start = ANSI.COLOR_TEST if want_color_output() else '' - hilight_end = ANSI.COLOR_DEFAULT if want_color_output() else '' - # Want this to stand out a little bit - self.log.info("{}Skipping test {}/{}:{} {}".format( - hilight_start, - self.count, - self.ntests, - hilight_end, - test.__qualname__)) - self._record_result(test, None, 0, 0) - return None - - test_init_outcome = cocotb.outcomes.capture(test, self._dut) - - if isinstance(test_init_outcome, cocotb.outcomes.Error): - self.log.error("Failed to initialize test %s" % test.__qualname__, - exc_info=test_init_outcome.error) - self._record_result(test, test_init_outcome, 0, 0) - return None - - test = test_init_outcome.get() - return test - - def _score_test(self, test: Test, outcome: Outcome) -> Tuple[bool, bool]: - """ - Given a test and the test's outcome, determine if the test met expectations and log pertinent information - """ - - # Helper for logging result - def _result_was(): - result_was = ("{} (result was {})".format - (test.__qualname__, type(result).__qualname__)) - return result_was - - # scoring outcomes - result_pass = True - sim_failed = False - - try: - outcome.get() - except Exception as e: - result = remove_traceback_frames(e, ['_score_test', 'get']) - else: - result = TestSuccess() - - if (isinstance(result, TestSuccess) and - not test.expect_fail and - not test.expect_error): - self.log.info("Test Passed: %s" % test.__qualname__) - - elif (isinstance(result, AssertionError) and - test.expect_fail): - self.log.info("Test failed as expected: " + _result_was()) - - elif (isinstance(result, TestSuccess) and - test.expect_error): - self.log.error("Test passed but we expected an error: " + - _result_was()) - result_pass = False - - elif isinstance(result, TestSuccess): - self.log.error("Test passed but we expected a failure: " + - _result_was()) - result_pass = False - - elif isinstance(result, SimFailure): - if isinstance(result, test.expect_error): - self.log.info("Test errored as expected: " + _result_was()) - else: - self.log.error("Test error has lead to simulator shutting us " - "down", exc_info=result) - result_pass = False - # whether we expected it or not, the simulation has failed unrecoverably - sim_failed = True - - elif test.expect_error: - if isinstance(result, test.expect_error): - self.log.info("Test errored as expected: " + _result_was()) - else: - self.log.error("Test errored with unexpected type: " + _result_was(), exc_info=result) - result_pass = False - - else: - self.log.error("Test Failed: " + _result_was(), exc_info=result) - result_pass = False - - if _pdb_on_exception: - pdb.post_mortem(result.__traceback__) - - return result_pass, sim_failed - - def _record_result( - self, - test: Test, - outcome: Optional[Outcome], - wall_time_s: float, - sim_time_ns: float - ) -> None: - - ratio_time = self._safe_divide(sim_time_ns, wall_time_s) - - self.xunit.add_testcase(name=test.__qualname__, - classname=test.__module__, - time=repr(wall_time_s), - sim_time_ns=repr(sim_time_ns), - ratio_time=repr(ratio_time)) - - if outcome is None: # skipped - test_pass, sim_failed = None, False - self.xunit.add_skipped() - self.skipped += 1 - - else: - test_pass, sim_failed = self._score_test(test, outcome) - if not test_pass: - self.xunit.add_failure() - self.failures += 1 - - self.test_results.append({ - 'test': '.'.join([test.__module__, test.__qualname__]), - 'pass': test_pass, - 'sim': sim_time_ns, - 'real': wall_time_s, - 'ratio': ratio_time}) - - if sim_failed: - self.tear_down() - return - - def execute(self) -> None: - while True: - self._test = self.next_test() - if self._test is None: - return self.tear_down() - - self._test_task = self._init_test(self._test) - if self._test_task is not None: - return self._start_test() - - def _start_test(self) -> None: - start = '' - end = '' - if want_color_output(): - start = ANSI.COLOR_TEST - end = ANSI.COLOR_DEFAULT - # Want this to stand out a little bit - self.log.info("%sRunning test %d/%d:%s %s" % - (start, - self.count, self.ntests, - end, - self._test.__qualname__)) - - # start capturing log output - cocotb.log.addHandler(self._test_task.handler) - - self._test_start_time = time.time() - self._test_start_sim_time = get_sim_time('ns') - cocotb.scheduler._add_test(self._test_task) - - def _log_test_summary(self) -> None: - - if self.failures: - self.log.error("Failed %d out of %d tests (%d skipped)" % - (self.failures, self.count, self.skipped)) - else: - self.log.info("Passed %d tests (%d skipped)" % - (self.count, self.skipped)) - - if len(self.test_results) == 0: - return - - TEST_FIELD = 'TEST' - RESULT_FIELD = 'PASS/FAIL' - SIM_FIELD = 'SIM TIME(NS)' - REAL_FIELD = 'REAL TIME(S)' - RATIO_FIELD = 'RATIO(NS/S)' - - TEST_FIELD_LEN = max(len(TEST_FIELD), len(max([x['test'] for x in self.test_results], key=len))) - RESULT_FIELD_LEN = len(RESULT_FIELD) - SIM_FIELD_LEN = len(SIM_FIELD) - REAL_FIELD_LEN = len(REAL_FIELD) - RATIO_FIELD_LEN = len(RATIO_FIELD) - - header_dict = dict( - a=TEST_FIELD, - b=RESULT_FIELD, - c=SIM_FIELD, - d=REAL_FIELD, - e=RATIO_FIELD, - a_len=TEST_FIELD_LEN, - b_len=RESULT_FIELD_LEN, - c_len=SIM_FIELD_LEN, - d_len=REAL_FIELD_LEN, - e_len=RATIO_FIELD_LEN) - - LINE_LEN = 3 + TEST_FIELD_LEN + 2 + RESULT_FIELD_LEN + 2 + SIM_FIELD_LEN + 2 + \ - REAL_FIELD_LEN + 2 + RATIO_FIELD_LEN + 3 - - LINE_SEP = "*" * LINE_LEN + "\n" - - summary = "" - summary += LINE_SEP - summary += "** {a:<{a_len}} {b:^{b_len}} {c:>{c_len}} {d:>{d_len}} {e:>{e_len}} **\n".format(**header_dict) - summary += LINE_SEP - - test_line = "{start}** {a:<{a_len}} {b:^{b_len}} {c:>{c_len}.2f} {d:>{d_len}.2f} {e:>{e_len}.2f} **\n" - for result in self.test_results: - hilite = '' - - if result['pass'] is None: - pass_fail_str = "N/A" - elif result['pass']: - pass_fail_str = "PASS" - else: - pass_fail_str = "FAIL" - if want_color_output(): - hilite = ANSI.COLOR_HILITE_SUMMARY - - test_dict = dict( - a=result['test'], - b=pass_fail_str, - c=result['sim'], - d=result['real'], - e=result['ratio'], - a_len=TEST_FIELD_LEN, - b_len=RESULT_FIELD_LEN, - c_len=SIM_FIELD_LEN - 1, - d_len=REAL_FIELD_LEN - 1, - e_len=RATIO_FIELD_LEN - 1, - start=hilite) - - summary += test_line.format(**test_dict) - - summary += LINE_SEP - - self.log.info(summary) - - def _log_sim_summary(self) -> None: - real_time = time.time() - self.start_time - sim_time_ns = get_sim_time('ns') - ratio_time = self._safe_divide(sim_time_ns, real_time) - - summary = "" - - summary += "*************************************************************************************\n" - summary += "** ERRORS : {0:<39}**\n".format(self.failures) - summary += "*************************************************************************************\n" - summary += "** SIM TIME : {0:<39}**\n".format('{0:.2f} NS'.format(sim_time_ns)) - summary += "** REAL TIME : {0:<39}**\n".format('{0:.2f} S'.format(real_time)) - summary += "** SIM / REAL TIME : {0:<39}**\n".format('{0:.2f} NS/S'.format(ratio_time)) - summary += "*************************************************************************************\n" - - self.log.info(summary) - - @staticmethod - def _safe_divide(a: float, b: float) -> float: - try: - return a / b - except ZeroDivisionError: - if a == 0: - return float('nan') - else: - return float('inf') - - -def _create_test(function, name, documentation, mod, *args, **kwargs): - """Factory function to create tests, avoids late binding. - - Creates a test dynamically. The test will call the supplied - function with the supplied arguments. - - Args: - function (function): The test function to run. - name (str): The name of the test. - documentation (str): The docstring for the test. - mod (module): The module this function belongs to. - *args: Remaining args to pass to test function. - **kwargs: Passed to the test function. - - Returns: - Decorated test function - """ - async def _my_test(dut): - await function(dut, *args, **kwargs) - - _my_test.__name__ = name - _my_test.__qualname__ = name - _my_test.__doc__ = documentation - _my_test.__module__ = mod.__name__ - return cocotb.test()(_my_test) - - -class TestFactory: - """Factory to automatically generate tests. - - Args: - test_function: The function that executes a test. - Must take *dut* as the first argument. - *args: Remaining arguments are passed directly to the test function. - Note that these arguments are not varied. An argument that - varies with each test must be a keyword argument to the - test function. - **kwargs: Remaining keyword arguments are passed directly to the test function. - Note that these arguments are not varied. An argument that - varies with each test must be a keyword argument to the - test function. - - Assuming we have a common test function that will run a test. This test - function will take keyword arguments (for example generators for each of - the input interfaces) and generate tests that call the supplied function. - - This Factory allows us to generate sets of tests based on the different - permutations of the possible arguments to the test function. - - For example, if we have a module that takes backpressure, has two configurable - features where enabling ``feature_b`` requires ``feature_a`` to be active, and - need to test against data generation routines ``gen_a`` and ``gen_b``: - - >>> tf = TestFactory(test_function=run_test) - >>> tf.add_option(name='data_in', optionlist=[gen_a, gen_b]) - >>> tf.add_option('backpressure', [None, random_backpressure]) - >>> tf.add_option(('feature_a', 'feature_b'), [(False, False), (True, False), (True, True)]) - >>> tf.generate_tests() - - We would get the following tests: - - * ``gen_a`` with no backpressure and both features disabled - * ``gen_a`` with no backpressure and only ``feature_a`` enabled - * ``gen_a`` with no backpressure and both features enabled - * ``gen_a`` with ``random_backpressure`` and both features disabled - * ``gen_a`` with ``random_backpressure`` and only ``feature_a`` enabled - * ``gen_a`` with ``random_backpressure`` and both features enabled - * ``gen_b`` with no backpressure and both features disabled - * ``gen_b`` with no backpressure and only ``feature_a`` enabled - * ``gen_b`` with no backpressure and both features enabled - * ``gen_b`` with ``random_backpressure`` and both features disabled - * ``gen_b`` with ``random_backpressure`` and only ``feature_a`` enabled - * ``gen_b`` with ``random_backpressure`` and both features enabled - - The tests are appended to the calling module for auto-discovery. - - Tests are simply named ``test_function_N``. The docstring for the test (hence - the test description) includes the name and description of each generator. - - .. versionchanged:: 1.5 - Groups of options are now supported - """ - - # Prevent warnings from collection of TestFactories by unit testing frameworks. - __test__ = False - - def __init__(self, test_function, *args, **kwargs): - if sys.version_info > (3, 6) and inspect.isasyncgenfunction(test_function): - raise TypeError("Expected a coroutine function, but got the async generator '{}'. " - "Did you forget to convert a `yield` to an `await`?" - .format(test_function.__qualname__)) - if not (isinstance(test_function, cocotb.coroutine) or inspect.iscoroutinefunction(test_function)): - raise TypeError("TestFactory requires a cocotb coroutine") - self.test_function = test_function - self.name = self.test_function.__qualname__ - - self.args = args - self.kwargs_constant = kwargs - self.kwargs = {} - self.log = _logger - - def add_option(self, name, optionlist): - """Add a named option to the test. - - Args: - name (str or iterable of str): An option name, or an iterable of - several option names. Passed to test as keyword arguments. - - optionlist (list): A list of possible options for this test knob. - If N names were specified, this must be a list of N-tuples or - lists, where each element specifies a value for its respective - option. - - .. versionchanged:: 1.5 - Groups of options are now supported - """ - if not isinstance(name, str): - name = tuple(name) - for opt in optionlist: - if len(name) != len(opt): - raise ValueError("Mismatch between number of options and number of option values in group") - self.kwargs[name] = optionlist - - def generate_tests(self, prefix="", postfix=""): - """ - Generate an exhaustive set of tests using the cartesian product of the - possible keyword arguments. - - The generated tests are appended to the namespace of the calling - module. - - Args: - prefix (str): Text string to append to start of ``test_function`` name - when naming generated test cases. This allows reuse of - a single ``test_function`` with multiple - :class:`TestFactories <.TestFactory>` without name clashes. - postfix (str): Text string to append to end of ``test_function`` name - when naming generated test cases. This allows reuse of - a single ``test_function`` with multiple - :class:`TestFactories <.TestFactory>` without name clashes. - """ - - frm = inspect.stack()[1] - mod = inspect.getmodule(frm[0]) - - d = self.kwargs - - for index, testoptions in enumerate(( - dict(zip(d, v)) for v in - product(*d.values()) - )): - - name = "%s%s%s_%03d" % (prefix, self.name, postfix, index + 1) - doc = "Automatically generated test\n\n" - - # preprocess testoptions to split tuples - testoptions_split = {} - for optname, optvalue in testoptions.items(): - if isinstance(optname, str): - testoptions_split[optname] = optvalue - else: - # previously checked in add_option; ensure nothing has changed - assert len(optname) == len(optvalue) - for n, v in zip(optname, optvalue): - testoptions_split[n] = v - - for optname, optvalue in testoptions_split.items(): - if callable(optvalue): - if not optvalue.__doc__: - desc = "No docstring supplied" - else: - desc = optvalue.__doc__.split('\n')[0] - doc += "\t%s: %s (%s)\n" % (optname, optvalue.__qualname__, - desc) - else: - doc += "\t%s: %s\n" % (optname, repr(optvalue)) - - self.log.debug("Adding generated test \"%s\" to module \"%s\"" % - (name, mod.__name__)) - kwargs = {} - kwargs.update(self.kwargs_constant) - kwargs.update(testoptions_split) - if hasattr(mod, name): - self.log.error("Overwriting %s in module %s. " - "This causes a previously defined testcase " - "not to be run. Consider setting/changing " - "name_postfix" % (name, mod)) - setattr(mod, name, _create_test(self.test_function, name, doc, mod, - *self.args, **kwargs)) diff --git a/cocotb/result.py b/cocotb/result.py deleted file mode 100644 index 67207bcf..00000000 --- a/cocotb/result.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -# TODO: Could use cStringIO? -import traceback -import sys -import warnings -from io import StringIO - -"""Exceptions and functions for simulation result handling.""" - - -def raise_error(obj, msg): - """Create a :exc:`TestError` exception and raise it after printing a traceback. - - .. deprecated:: 1.3 - Raise a standard Python exception instead of calling this function. - A stacktrace will be printed by cocotb automatically if the exception is unhandled. - - Args: - obj: Object with a log method. - msg (str): The log message. - """ - warnings.warn( - "``raise_error`` is deprecated - raise a standard Exception instead", - DeprecationWarning, stacklevel=2) - _raise_error(obj, msg) - - -def _raise_error(obj, msg): - exc_info = sys.exc_info() - buff = StringIO() - traceback.print_exception(*exc_info, file=buff) - obj.log.error("%s\n%s" % (msg, buff.getvalue())) - exception = TestError(msg) - exception.stderr.write(buff.getvalue()) - raise exception - - -def create_error(obj, msg): - """Like :func:`raise_error`, but return the exception rather than raise it, - simply to avoid too many levels of nested `try/except` blocks. - - .. deprecated:: 1.3 - Raise a standard Python exception instead of calling this function. - - Args: - obj: Object with a log method. - msg (str): The log message. - """ - warnings.warn( - "``create_error`` is deprecated - raise a standard Exception instead", - DeprecationWarning, stacklevel=2) - try: - # use the private version to avoid multiple warnings - _raise_error(obj, msg) - except TestError as error: - return error - return TestError("Creating error traceback failed") - - -class ReturnValue(Exception): - """ - Helper exception needed for Python versions prior to 3.3. - - .. deprecated:: 1.4 - Use a :keyword:`return` statement instead; this works in all supported versions of Python. - """ - - def __init__(self, retval): - warnings.warn( - "``ReturnValue`` is deprecated, use a normal return statement instead.", - DeprecationWarning, - stacklevel=2) - self.retval = retval - - -class TestComplete(Exception): - """Exception showing that the test was completed. Sub-exceptions detail the exit status.""" - - def __init__(self, *args, **kwargs): - super(TestComplete, self).__init__(*args, **kwargs) - self.stdout = StringIO() - self.stderr = StringIO() - - -class ExternalException(Exception): - """Exception thrown by :class:`cocotb.external` functions.""" - - def __init__(self, exception): - self.exception = exception - - -class TestError(TestComplete): - """ - Exception showing that the test was completed with severity Error. - - .. deprecated:: 1.5 - Raise a standard Python exception instead. - A stacktrace will be printed by cocotb automatically if the exception is unhandled. - """ - - def __init__(self, *args, **kwargs): - warnings.warn( - "TestError is deprecated - raise a standard Exception instead", - DeprecationWarning, stacklevel=2) - super().__init__(*args, **kwargs) - - -class TestFailure(TestComplete, AssertionError): - """Exception showing that the test was completed with severity Failure.""" - pass - - -class TestSuccess(TestComplete): - """Exception showing that the test was completed successfully.""" - pass - - -class SimFailure(TestComplete): - """Exception showing that the simulator exited unsuccessfully.""" - pass - - -class SimTimeoutError(TimeoutError): - """Exception for when a timeout, in terms of simulation time, occurs.""" - pass diff --git a/cocotb/scheduler.py b/cocotb/scheduler.py deleted file mode 100755 index 808fcd5c..00000000 --- a/cocotb/scheduler.py +++ /dev/null @@ -1,971 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2013, 2018 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -"""Coroutine scheduler. - -FIXME: We have a problem here. If a coroutine schedules a read-only but we -also have pending writes we have to schedule the ReadWrite callback before -the ReadOnly (and this is invalid, at least in Modelsim). -""" -from contextlib import contextmanager -import os -import sys -import logging -import threading -import inspect -import warnings -from typing import Any, Union -from collections.abc import Coroutine - -import cocotb -import cocotb.decorators -from cocotb.decorators import RunningTask -from cocotb.triggers import (Trigger, GPITrigger, Timer, ReadOnly, - NextTimeStep, ReadWrite, Event, Join, NullTrigger) -from cocotb.log import SimLog -from cocotb.result import TestComplete -from cocotb.utils import remove_traceback_frames -from cocotb import outcomes, _py_compat - - -# Debug mode controlled by environment variables -_profiling = "COCOTB_ENABLE_PROFILING" in os.environ -if _profiling: - import cProfile - import pstats - _profile = cProfile.Profile() - -# Sadly the Python standard logging module is very slow so it's better not to -# make any calls by testing a boolean flag first -_debug = "COCOTB_SCHEDULER_DEBUG" in os.environ - - -class InternalError(RuntimeError): - """ An error internal to scheduler. If you see this, report a bug! """ - pass - - -class profiling_context: - """ Context manager that profiles its contents """ - - def __enter__(self): - _profile.enable() - - def __exit__(self, *excinfo): - _profile.disable() - - -class external_state: - INIT = 0 - RUNNING = 1 - PAUSED = 2 - EXITED = 3 - - -@cocotb.decorators.public -class external_waiter: - - def __init__(self): - self._outcome = None - self.thread = None - self.event = Event() - self.state = external_state.INIT - self.cond = threading.Condition() - self._log = SimLog("cocotb.external.thead.%s" % self.thread, id(self)) - - @property - def result(self): - return self._outcome.get() - - def _propagate_state(self, new_state): - with self.cond: - if _debug: - self._log.debug("Changing state from %d -> %d from %s" % ( - self.state, new_state, threading.current_thread())) - self.state = new_state - self.cond.notify() - - def thread_done(self): - if _debug: - self._log.debug("Thread finished from %s" % (threading.current_thread())) - self._propagate_state(external_state.EXITED) - - def thread_suspend(self): - self._propagate_state(external_state.PAUSED) - - def thread_start(self): - if self.state > external_state.INIT: - return - - if not self.thread.is_alive(): - self._propagate_state(external_state.RUNNING) - self.thread.start() - - def thread_resume(self): - self._propagate_state(external_state.RUNNING) - - def thread_wait(self): - if _debug: - self._log.debug("Waiting for the condition lock %s" % threading.current_thread()) - - with self.cond: - while self.state == external_state.RUNNING: - self.cond.wait() - - if _debug: - if self.state == external_state.EXITED: - self._log.debug("Thread %s has exited from %s" % ( - self.thread, threading.current_thread())) - elif self.state == external_state.PAUSED: - self._log.debug("Thread %s has called yield from %s" % ( - self.thread, threading.current_thread())) - elif self.state == external_state.RUNNING: - self._log.debug("Thread %s is in RUNNING from %d" % ( - self.thread, threading.current_thread())) - - if self.state == external_state.INIT: - raise Exception("Thread %s state was not allowed from %s" % ( - self.thread, threading.current_thread())) - - return self.state - - -class Scheduler: - """The main scheduler. - - Here we accept callbacks from the simulator and schedule the appropriate - coroutines. - - A callback fires, causing the :any:`react` method to be called, with the - trigger that caused the callback as the first argument. - - We look up a list of coroutines to schedule (indexed by the trigger) and - schedule them in turn. - - .. attention:: - - Implementors should not depend on the scheduling order! - - Some additional management is required since coroutines can return a list - of triggers, to be scheduled when any one of the triggers fires. To - ensure we don't receive spurious callbacks, we have to un-prime all the - other triggers when any one fires. - - Due to the simulator nuances and fun with delta delays we have the - following modes: - - Normal mode - - Callbacks cause coroutines to be scheduled - - Any pending writes are cached and do not happen immediately - - ReadOnly mode - - Corresponds to ``cbReadOnlySynch`` (VPI) or ``vhpiCbRepEndOfTimeStep`` - (VHPI). In this state we are not allowed to perform writes. - - Write mode - - Corresponds to ``cbReadWriteSynch`` (VPI) or ``vhpiCbRepLastKnownDeltaCycle`` (VHPI) - In this mode we play back all the cached write updates. - - We can legally transition from Normal to Write by registering a :class:`~cocotb.triggers.ReadWrite` - callback, however usually once a simulator has entered the ReadOnly phase - of a given timestep then we must move to a new timestep before performing - any writes. The mechanism for moving to a new timestep may not be - consistent across simulators and therefore we provide an abstraction to - assist with compatibility. - - - Unless a coroutine has explicitly requested to be scheduled in ReadOnly - mode (for example wanting to sample the finally settled value after all - delta delays) then it can reasonably be expected to be scheduled during - "normal mode" i.e. where writes are permitted. - """ - - _MODE_NORMAL = 1 # noqa - _MODE_READONLY = 2 # noqa - _MODE_WRITE = 3 # noqa - _MODE_TERM = 4 # noqa - - # Singleton events, recycled to avoid spurious object creation - _next_time_step = NextTimeStep() - _read_write = ReadWrite() - _read_only = ReadOnly() - _timer1 = Timer(1) - - def __init__(self): - - self.log = SimLog("cocotb.scheduler") - if _debug: - self.log.setLevel(logging.DEBUG) - - # Use OrderedDict here for deterministic behavior (gh-934) - - # A dictionary of pending coroutines for each trigger, - # indexed by trigger - self._trigger2coros = _py_compat.insertion_ordered_dict() - - # Our main state - self._mode = Scheduler._MODE_NORMAL - - # A dictionary of pending (write_func, args), keyed by handle. Only the last scheduled write - # in a timestep is performed, all the rest are discarded in python. - self._write_calls = _py_compat.insertion_ordered_dict() - - self._pending_coros = [] - self._pending_triggers = [] - self._pending_threads = [] - self._pending_events = [] # Events we need to call set on once we've unwound - - self._terminate = False - self._test = None - self._main_thread = threading.current_thread() - - self._current_task = None - - self._is_reacting = False - - self._write_coro_inst = None - self._writes_pending = Event() - - async def _do_writes(self): - """ An internal coroutine that performs pending writes """ - while True: - await self._writes_pending.wait() - if self._mode != Scheduler._MODE_NORMAL: - await self._next_time_step - - await self._read_write - - while self._write_calls: - handle, (func, args) = self._write_calls.popitem() - func(*args) - self._writes_pending.clear() - - def _check_termination(self): - """ - Handle a termination that causes us to move onto the next test. - """ - if self._terminate: - if _debug: - self.log.debug("Test terminating, scheduling Timer") - - if self._write_coro_inst is not None: - self._write_coro_inst.kill() - self._write_coro_inst = None - - for t in self._trigger2coros: - t.unprime() - - if self._timer1.primed: - self._timer1.unprime() - - self._timer1.prime(self._test_completed) - self._trigger2coros = _py_compat.insertion_ordered_dict() - self._terminate = False - self._write_calls = _py_compat.insertion_ordered_dict() - self._writes_pending.clear() - self._mode = Scheduler._MODE_TERM - - def _test_completed(self, trigger=None): - """Called after a test and its cleanup have completed""" - if _debug: - self.log.debug("begin_test called with trigger: %s" % - (str(trigger))) - if _profiling: - ps = pstats.Stats(_profile).sort_stats('cumulative') - ps.dump_stats("test_profile.pstat") - ctx = profiling_context() - else: - ctx = _py_compat.nullcontext() - - with ctx: - self._mode = Scheduler._MODE_NORMAL - if trigger is not None: - trigger.unprime() - - # extract the current test, and clear it - test = self._test - self._test = None - if test is None: - raise InternalError("_test_completed called with no active test") - if test._outcome is None: - raise InternalError("_test_completed called with an incomplete test") - - # Issue previous test result - if _debug: - self.log.debug("Issue test result to regression object") - - # this may schedule another test - cocotb.regression_manager.handle_result(test) - - # if it did, make sure we handle the test completing - self._check_termination() - - def react(self, trigger): - """ - .. deprecated:: 1.5 - This function is now private. - """ - warnings.warn("This function is now private.", DeprecationWarning, stacklevel=2) - return self._react(trigger) - - def _react(self, trigger): - """ - Called when a trigger fires. - - We ensure that we only start the event loop once, rather than - letting it recurse. - """ - if self._is_reacting: - # queue up the trigger, the event loop will get to it - self._pending_triggers.append(trigger) - return - - if self._pending_triggers: - raise InternalError( - "Expected all triggers to be handled but found {}" - .format(self._pending_triggers) - ) - - # start the event loop - self._is_reacting = True - try: - self._event_loop(trigger) - finally: - self._is_reacting = False - - def _event_loop(self, trigger): - """ - Run an event loop triggered by the given trigger. - - The loop will keep running until no further triggers fire. - - This should be triggered by only: - * The beginning of a test, when there is no trigger to react to - * A GPI trigger - """ - if _profiling: - ctx = profiling_context() - else: - ctx = _py_compat.nullcontext() - - with ctx: - # When a trigger fires it is unprimed internally - if _debug: - self.log.debug("Trigger fired: %s" % str(trigger)) - # trigger.unprime() - - if self._mode == Scheduler._MODE_TERM: - if _debug: - self.log.debug("Ignoring trigger %s since we're terminating" % - str(trigger)) - return - - if trigger is self._read_only: - self._mode = Scheduler._MODE_READONLY - # Only GPI triggers affect the simulator scheduling mode - elif isinstance(trigger, GPITrigger): - self._mode = Scheduler._MODE_NORMAL - - # work through triggers one by one - is_first = True - self._pending_triggers.append(trigger) - while self._pending_triggers: - trigger = self._pending_triggers.pop(0) - - if not is_first and isinstance(trigger, GPITrigger): - self.log.warning( - "A GPI trigger occurred after entering react - this " - "should not happen." - ) - assert False - - # this only exists to enable the warning above - is_first = False - - # Scheduled coroutines may append to our waiting list so the first - # thing to do is pop all entries waiting on this trigger. - try: - scheduling = self._trigger2coros.pop(trigger) - except KeyError: - # GPI triggers should only be ever pending if there is an - # associated coroutine waiting on that trigger, otherwise it would - # have been unprimed already - if isinstance(trigger, GPITrigger): - self.log.critical( - "No coroutines waiting on trigger that fired: %s" % - str(trigger)) - - trigger.log.info("I'm the culprit") - # For Python triggers this isn't actually an error - we might do - # event.set() without knowing whether any coroutines are actually - # waiting on this event, for example - elif _debug: - self.log.debug( - "No coroutines waiting on trigger that fired: %s" % - str(trigger)) - - del trigger - continue - - if _debug: - debugstr = "\n\t".join([coro._coro.__qualname__ for coro in scheduling]) - if len(scheduling) > 0: - debugstr = "\n\t" + debugstr - self.log.debug("%d pending coroutines for event %s%s" % - (len(scheduling), str(trigger), debugstr)) - - # This trigger isn't needed any more - trigger.unprime() - - for coro in scheduling: - if coro._outcome is not None: - # coroutine was killed by another coroutine waiting on the same trigger - continue - if _debug: - self.log.debug("Scheduling coroutine %s" % (coro._coro.__qualname__)) - self._schedule(coro, trigger=trigger) - if _debug: - self.log.debug("Scheduled coroutine %s" % (coro._coro.__qualname__)) - - # remove our reference to the objects at the end of each loop, - # to try and avoid them being destroyed at a weird time (as - # happened in gh-957) - del coro - - # Schedule may have queued up some events so we'll burn through those - while self._pending_events: - if _debug: - self.log.debug("Scheduling pending event %s" % - (str(self._pending_events[0]))) - self._pending_events.pop(0).set() - - # remove our reference to the objects at the end of each loop, - # to try and avoid them being destroyed at a weird time (as - # happened in gh-957) - del trigger - del scheduling - - # no more pending triggers - self._check_termination() - if _debug: - self.log.debug("All coroutines scheduled, handing control back" - " to simulator") - - def unschedule(self, coro): - """ - .. deprecated:: 1.5 - This function is now private. - """ - warnings.warn("This function is now private.", DeprecationWarning, stacklevel=2) - return self._unschedule(coro) - - def _unschedule(self, coro): - """Unschedule a coroutine. Unprime any pending triggers""" - - # Unprime the trigger this coroutine is waiting on - trigger = coro._trigger - if trigger is not None: - coro._trigger = None - if coro in self._trigger2coros.setdefault(trigger, []): - self._trigger2coros[trigger].remove(coro) - if not self._trigger2coros[trigger]: - trigger.unprime() - del self._trigger2coros[trigger] - - assert self._test is not None - - if coro is self._test: - if _debug: - self.log.debug("Unscheduling test {}".format(coro)) - - if not self._terminate: - self._terminate = True - self._cleanup() - - elif Join(coro) in self._trigger2coros: - self._react(Join(coro)) - else: - try: - # throws an error if the background coroutine errored - # and no one was monitoring it - coro._outcome.get() - except (TestComplete, AssertionError) as e: - coro.log.info("Test stopped by this forked coroutine") - e = remove_traceback_frames(e, ['_unschedule', 'get']) - self._test.abort(e) - except Exception as e: - coro.log.error("Exception raised by this forked coroutine") - e = remove_traceback_frames(e, ['_unschedule', 'get']) - self._test.abort(e) - - def _schedule_write(self, handle, write_func, *args): - """ Queue `write_func` to be called on the next ReadWrite trigger. """ - if self._mode == Scheduler._MODE_READONLY: - raise Exception("Write to object {0} was scheduled during a read-only sync phase.".format(handle._name)) - - # TODO: we should be able to better keep track of when this needs to - # be scheduled - if self._write_coro_inst is None: - self._write_coro_inst = self.add(self._do_writes()) - - self._write_calls[handle] = (write_func, args) - self._writes_pending.set() - - def _resume_coro_upon(self, coro, trigger): - """Schedule `coro` to be resumed when `trigger` fires.""" - coro._trigger = trigger - - trigger_coros = self._trigger2coros.setdefault(trigger, []) - if coro is self._write_coro_inst: - # Our internal write coroutine always runs before any user coroutines. - # This preserves the behavior prior to the refactoring of writes to - # this coroutine. - trigger_coros.insert(0, coro) - else: - # Everything else joins the back of the queue - trigger_coros.append(coro) - - if not trigger.primed: - - if trigger_coros != [coro]: - # should never happen - raise InternalError( - "More than one coroutine waiting on an unprimed trigger") - - try: - trigger.prime(self._react) - except Exception as e: - # discard the trigger we associated, it will never fire - self._trigger2coros.pop(trigger) - - # replace it with a new trigger that throws back the exception - self._resume_coro_upon( - coro, - NullTrigger(name="Trigger.prime() Error", outcome=outcomes.Error(e)) - ) - - def queue(self, coroutine): - """ - .. deprecated:: 1.5 - This function is now private. - """ - warnings.warn("This function is now private.", DeprecationWarning, stacklevel=2) - return self._queue(coroutine) - - def _queue(self, coroutine): - """Queue a coroutine for execution""" - self._pending_coros.append(coroutine) - - def queue_function(self, coro): - """ - .. deprecated:: 1.5 - This function is now private. - """ - warnings.warn("This function is now private.", DeprecationWarning, stacklevel=2) - return self._queue_function(coro) - - def _queue_function(self, coro): - """Queue a coroutine for execution and move the containing thread - so that it does not block execution of the main thread any longer. - """ - # We should be able to find ourselves inside the _pending_threads list - matching_threads = [ - t - for t in self._pending_threads - if t.thread == threading.current_thread() - ] - if len(matching_threads) == 0: - raise RuntimeError("queue_function called from unrecognized thread") - - # Raises if there is more than one match. This can never happen, since - # each entry always has a unique thread. - t, = matching_threads - - async def wrapper(): - # This function runs in the scheduler thread - try: - _outcome = outcomes.Value(await coro) - except BaseException as e: - _outcome = outcomes.Error(e) - event.outcome = _outcome - # Notify the current (scheduler) thread that we are about to wake - # up the background (`@external`) thread, making sure to do so - # before the background thread gets a chance to go back to sleep by - # calling thread_suspend. - # We need to do this here in the scheduler thread so that no more - # coroutines run until the background thread goes back to sleep. - t.thread_resume() - event.set() - - event = threading.Event() - self._pending_coros.append(cocotb.decorators.RunningTask(wrapper())) - # The scheduler thread blocks in `thread_wait`, and is woken when we - # call `thread_suspend` - so we need to make sure the coroutine is - # queued before that. - t.thread_suspend() - # This blocks the calling `@external` thread until the coroutine finishes - event.wait() - return event.outcome.get() - - def run_in_executor(self, func, *args, **kwargs): - """ - .. deprecated:: 1.5 - This function is now private. - """ - warnings.warn("This function is now private.", DeprecationWarning, stacklevel=2) - return self._run_in_executor(func, *args, **kwargs) - - def _run_in_executor(self, func, *args, **kwargs): - """Run the coroutine in a separate execution thread - and return an awaitable object for the caller. - """ - # Create a thread - # Create a trigger that is called as a result of the thread finishing - # Create an Event object that the caller can await on - # Event object set when the thread finishes execution, this blocks the - # calling coroutine (but not the thread) until the external completes - - def execute_external(func, _waiter): - _waiter._outcome = outcomes.capture(func, *args, **kwargs) - if _debug: - self.log.debug("Execution of external routine done %s" % threading.current_thread()) - _waiter.thread_done() - - async def wrapper(): - waiter = external_waiter() - thread = threading.Thread(group=None, target=execute_external, - name=func.__qualname__ + "_thread", - args=([func, waiter]), kwargs={}) - - waiter.thread = thread - self._pending_threads.append(waiter) - - await waiter.event.wait() - - return waiter.result # raises if there was an exception - - return wrapper() - - @staticmethod - def create_task(coroutine: Any) -> RunningTask: - """ Checks to see if the given object is a schedulable coroutine object and if so, returns it """ - - if isinstance(coroutine, RunningTask): - return coroutine - if inspect.iscoroutine(coroutine): - return RunningTask(coroutine) - if inspect.iscoroutinefunction(coroutine): - raise TypeError( - "Coroutine function {} should be called prior to being " - "scheduled." - .format(coroutine)) - if isinstance(coroutine, cocotb.decorators.coroutine): - raise TypeError( - "Attempt to schedule a coroutine that hasn't started: {}.\n" - "Did you forget to add parentheses to the @cocotb.test() " - "decorator?" - .format(coroutine) - ) - if sys.version_info >= (3, 6) and inspect.isasyncgen(coroutine): - raise TypeError( - "{} is an async generator, not a coroutine. " - "You likely used the yield keyword instead of await.".format( - coroutine.__qualname__)) - raise TypeError( - "Attempt to add an object of type {} to the scheduler, which " - "isn't a coroutine: {!r}\n" - "Did you forget to use the @cocotb.coroutine decorator?" - .format(type(coroutine), coroutine) - ) - - def add(self, coroutine: Union[RunningTask, Coroutine]) -> RunningTask: - """Add a new coroutine. - - Just a wrapper around self.schedule which provides some debug and - useful error messages in the event of common gotchas. - """ - - task = self.create_task(coroutine) - - if _debug: - self.log.debug("Adding new coroutine %s" % task._coro.__qualname__) - - self._schedule(task) - self._check_termination() - return task - - def start_soon(self, coro: Union[Coroutine, RunningTask]) -> RunningTask: - """ - Schedule a coroutine to be run concurrently, starting after the current coroutine yields control. - - In contrast to :func:`~cocotb.fork` which starts the given coroutine immediately, this function - starts the given coroutine only after the current coroutine yields control. - This is useful when the coroutine to be forked has logic before the first - :keyword:`await` that may not be safe to execute immediately. - """ - - task = self.create_task(coro) - - if _debug: - self.log.debug("Queueing a new coroutine %s" % task._coro.__qualname__) - - self._queue(task) - return task - - def add_test(self, test_coro): - """ - .. deprecated:: 1.5 - This function is now private. - """ - warnings.warn("This function is now private.", DeprecationWarning, stacklevel=2) - return self._add_test(test_coro) - - def _add_test(self, test_coro): - """Called by the regression manager to queue the next test""" - if self._test is not None: - raise InternalError("Test was added while another was in progress") - - self._test = test_coro - self._resume_coro_upon( - test_coro, - NullTrigger(name="Start {!s}".format(test_coro), outcome=outcomes.Value(None)) - ) - - # This collection of functions parses a trigger out of the object - # that was yielded by a coroutine, converting `list` -> `Waitable`, - # `Waitable` -> `RunningTask`, `RunningTask` -> `Trigger`. - # Doing them as separate functions allows us to avoid repeating unencessary - # `isinstance` checks. - - def _trigger_from_started_coro(self, result: cocotb.decorators.RunningTask) -> Trigger: - if _debug: - self.log.debug("Joining to already running coroutine: %s" % - result._coro.__qualname__) - return result.join() - - def _trigger_from_unstarted_coro(self, result: cocotb.decorators.RunningTask) -> Trigger: - self._queue(result) - if _debug: - self.log.debug("Scheduling nested coroutine: %s" % - result._coro.__qualname__) - return result.join() - - def _trigger_from_waitable(self, result: cocotb.triggers.Waitable) -> Trigger: - return self._trigger_from_unstarted_coro(cocotb.decorators.RunningTask(result._wait())) - - def _trigger_from_list(self, result: list) -> Trigger: - return self._trigger_from_waitable(cocotb.triggers.First(*result)) - - def _trigger_from_any(self, result) -> Trigger: - """Convert a yielded object into a Trigger instance""" - # note: the order of these can significantly impact performance - - if isinstance(result, Trigger): - return result - - if isinstance(result, cocotb.decorators.RunningTask): - if not result.has_started(): - return self._trigger_from_unstarted_coro(result) - else: - return self._trigger_from_started_coro(result) - - if inspect.iscoroutine(result): - return self._trigger_from_unstarted_coro(cocotb.decorators.RunningTask(result)) - - if isinstance(result, list): - return self._trigger_from_list(result) - - if isinstance(result, cocotb.triggers.Waitable): - return self._trigger_from_waitable(result) - - if sys.version_info >= (3, 6) and inspect.isasyncgen(result): - raise TypeError( - "{} is an async generator, not a coroutine. " - "You likely used the yield keyword instead of await.".format( - result.__qualname__)) - - raise TypeError( - "Coroutine yielded an object of type {}, which the scheduler can't " - "handle: {!r}\n" - "Did you forget to decorate with @cocotb.coroutine?" - .format(type(result), result) - ) - - @contextmanager - def _task_context(self, task): - """Context manager for the currently running task.""" - old_task = self._current_task - self._current_task = task - try: - yield - finally: - self._current_task = old_task - - def schedule(self, coroutine, trigger=None): - """ - .. deprecated:: 1.5 - This function is now private. - """ - warnings.warn("This function is now private.", DeprecationWarning, stacklevel=2) - return self._schedule(coroutine, trigger) - - def _schedule(self, coroutine, trigger=None): - """Schedule a coroutine by calling the send method. - - Args: - coroutine (cocotb.decorators.coroutine): The coroutine to schedule. - trigger (cocotb.triggers.Trigger): The trigger that caused this - coroutine to be scheduled. - """ - with self._task_context(coroutine): - if trigger is None: - send_outcome = outcomes.Value(None) - else: - send_outcome = trigger._outcome - if _debug: - self.log.debug("Scheduling with {}".format(send_outcome)) - - coro_completed = False - try: - coroutine._trigger = None - result = coroutine._advance(send_outcome) - if _debug: - self.log.debug("Coroutine %s yielded %s (mode %d)" % - (coroutine._coro.__qualname__, str(result), self._mode)) - - except cocotb.decorators.CoroutineComplete: - if _debug: - self.log.debug("Coroutine {} completed with {}".format( - coroutine, coroutine._outcome - )) - coro_completed = True - - # this can't go in the else above, as that causes unwanted exception - # chaining - if coro_completed: - self._unschedule(coroutine) - - # Don't handle the result if we're shutting down - if self._terminate: - return - - if not coro_completed: - try: - result = self._trigger_from_any(result) - except TypeError as exc: - # restart this coroutine with an exception object telling it that - # it wasn't allowed to yield that - result = NullTrigger(outcome=outcomes.Error(exc)) - - self._resume_coro_upon(coroutine, result) - - # We do not return from here until pending threads have completed, but only - # from the main thread, this seems like it could be problematic in cases - # where a sim might change what this thread is. - - if self._main_thread is threading.current_thread(): - - for ext in self._pending_threads: - ext.thread_start() - if _debug: - self.log.debug("Blocking from %s on %s" % ( - threading.current_thread(), ext.thread)) - state = ext.thread_wait() - if _debug: - self.log.debug("Back from wait on self %s with newstate %d" % ( - threading.current_thread(), state)) - if state == external_state.EXITED: - self._pending_threads.remove(ext) - self._pending_events.append(ext.event) - - # Handle any newly queued coroutines that need to be scheduled - while self._pending_coros: - self.add(self._pending_coros.pop(0)) - - def finish_test(self, exc): - """ - .. deprecated:: 1.5 - This function is now private. - """ - warnings.warn("This function is now private.", DeprecationWarning, stacklevel=2) - return self._finish_test(exc) - - def _finish_test(self, exc): - self._test.abort(exc) - self._check_termination() - - def finish_scheduler(self, exc): - """ - .. deprecated:: 1.5 - This function is now private. - """ - warnings.warn("This function is now private.", DeprecationWarning, stacklevel=2) - return self._finish_scheduler(exc) - - def _finish_scheduler(self, exc): - """Directly call into the regression manager and end test - once we return the sim will close us so no cleanup is needed. - """ - # If there is an error during cocotb initialization, self._test may not - # have been set yet. Don't cause another Python exception here. - - if self._test: - self.log.debug("Issue sim closedown result to regression object") - self._test.abort(exc) - cocotb.regression_manager.handle_result(self._test) - - def cleanup(self): - """ - .. deprecated:: 1.5 - This function is now private. - """ - warnings.warn("This function is now private.", DeprecationWarning, stacklevel=2) - return self._cleanup() - - def _cleanup(self): - """Clear up all our state. - - Unprime all pending triggers and kill off any coroutines, stop all externals. - """ - # copy since we modify this in kill - items = list(self._trigger2coros.items()) - - # reversing seems to fix gh-928, although the order is still somewhat - # arbitrary. - for trigger, waiting in items[::-1]: - for coro in waiting: - if _debug: - self.log.debug("Killing %s" % str(coro)) - coro.kill() - - if self._main_thread is not threading.current_thread(): - raise Exception("Cleanup() called outside of the main thread") - - for ext in self._pending_threads: - self.log.warning("Waiting for %s to exit", ext.thread) diff --git a/cocotb/share/def/.gitignore b/cocotb/share/def/.gitignore deleted file mode 100644 index 322161e3..00000000 --- a/cocotb/share/def/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Generated import libraries on Windows -*.a diff --git a/cocotb/share/def/README.md b/cocotb/share/def/README.md deleted file mode 100644 index 12e8466f..00000000 --- a/cocotb/share/def/README.md +++ /dev/null @@ -1,4 +0,0 @@ -This directory contains module-definition files (`.def`). -On Windows these are used to generate import libraries (`.a`, but not regular static libraries), which means we can compile our libraries without having the simulator binaries available. -They are not needed on other platforms. -More details on `.def` files can be found at http://www.mingw.org/wiki/msvc_and_mingw_dlls diff --git a/cocotb/share/def/aldec.def b/cocotb/share/def/aldec.def deleted file mode 100644 index 186d2627..00000000 --- a/cocotb/share/def/aldec.def +++ /dev/null @@ -1,61 +0,0 @@ -LIBRARY "aldecpli.dll" -EXPORTS -vpi_chk_error -vpi_compare_objects -vpi_control -vpi_flush -vpi_fopen -vpi_free_object -vpi_get -vpi_get_delays -vpi_get_file -vpi_get_str -vpi_get_systf_info -vpi_get_time -vpi_get_userdata -vpi_get_value -vpi_get_vlog_info -vpi_handle -vpi_handle_by_index -vpi_handle_by_name -vpi_iterate -vpi_mcd_close -vpi_mcd_flush -vpi_mcd_name -vpi_mcd_open -vpi_mcd_printf -vpi_mcd_vprintf -vpi_printf -vpi_put_delays -vpi_put_userdata -vpi_put_value -vpi_register_cb -vpi_register_systf -vpi_remove_cb -vpi_scan -vpi_sim_control -vpi_sim_vcontrol -vpi_vprintf -vpip_count_drivers -vpip_format_strength -vpip_make_systf_system_defined -vpip_mcd_rawwrite -vpip_set_return_value -vhpi_check_error -vhpi_control -vhpi_disable_cb -vhpi_enable_cb -vhpi_get -vhpi_get_phys -vhpi_get_str -vhpi_get_time -vhpi_get_value -vhpi_handle -vhpi_handle_by_index -vhpi_handle_by_name -vhpi_iterator -vhpi_put_value -vhpi_register_cb -vhpi_release_handle -vhpi_remove_cb -vhpi_scan diff --git a/cocotb/share/def/ghdl.def b/cocotb/share/def/ghdl.def deleted file mode 100644 index bc9b1506..00000000 --- a/cocotb/share/def/ghdl.def +++ /dev/null @@ -1,43 +0,0 @@ -LIBRARY "libghdlvpi.dll" -EXPORTS -vpi_compare_objects -vpi_control -vpi_flush -vpi_fopen -vpi_free_object -vpi_get -vpi_get_delays -vpi_get_file -vpi_get_str -vpi_get_systf_info -vpi_get_time -vpi_get_userdata -vpi_get_value -vpi_get_vlog_info -vpi_handle -vpi_handle_by_index -vpi_handle_by_name -vpi_iterate -vpi_mcd_close -vpi_mcd_flush -vpi_mcd_name -vpi_mcd_printf -vpi_mcd_vprintf -vpi_printf -vpi_put_delays -vpi_put_userdata -vpi_put_value -vpi_register_cb -vpi_register_systf -vpi_remove_cb -vpi_scan -vpi_sim_control -vpi_sim_vcontrol -vpi_mcd_open -vpi_vprintf -vpip_count_drivers -vpip_format_strength -vpip_make_systf_system_defined -vpip_mcd_rawwrite -vpip_set_return_value -vpi_chk_error diff --git a/cocotb/share/def/icarus.def b/cocotb/share/def/icarus.def deleted file mode 100644 index f3768948..00000000 --- a/cocotb/share/def/icarus.def +++ /dev/null @@ -1,43 +0,0 @@ -LIBRARY "vvp.exe" -EXPORTS -vpi_compare_objects -vpi_control -vpi_flush -vpi_fopen -vpi_free_object -vpi_get -vpi_get_delays -vpi_get_file -vpi_get_str -vpi_get_systf_info -vpi_get_time -vpi_get_userdata -vpi_get_value -vpi_get_vlog_info -vpi_handle -vpi_handle_by_index -vpi_handle_by_name -vpi_iterate -vpi_mcd_close -vpi_mcd_flush -vpi_mcd_name -vpi_mcd_printf -vpi_mcd_vprintf -vpi_printf -vpi_put_delays -vpi_put_userdata -vpi_put_value -vpi_register_cb -vpi_register_systf -vpi_remove_cb -vpi_scan -vpi_sim_control -vpi_sim_vcontrol -vpi_mcd_open -vpi_vprintf -vpip_count_drivers -vpip_format_strength -vpip_make_systf_system_defined -vpip_mcd_rawwrite -vpip_set_return_value -vpi_chk_error diff --git a/cocotb/share/def/modelsim.def b/cocotb/share/def/modelsim.def deleted file mode 100644 index 8674e179..00000000 --- a/cocotb/share/def/modelsim.def +++ /dev/null @@ -1,115 +0,0 @@ -LIBRARY "mtipli.dll" -EXPORTS -vpi_chk_error -vpi_compare_objects -vpi_control -vpi_flush -vpi_fopen -vpi_free_object -vpi_get -vpi_get_delays -vpi_get_file -vpi_get_str -vpi_get_systf_info -vpi_get_time -vpi_get_userdata -vpi_get_value -vpi_get_vlog_info -vpi_handle -vpi_handle_by_index -vpi_handle_by_name -vpi_iterate -vpi_mcd_close -vpi_mcd_flush -vpi_mcd_name -vpi_mcd_open -vpi_mcd_printf -vpi_mcd_vprintf -vpi_printf -vpi_put_delays -vpi_put_userdata -vpi_put_value -vpi_register_cb -vpi_register_systf -vpi_remove_cb -vpi_scan -vpi_sim_control -vpi_sim_vcontrol -vpi_vprintf -vpip_count_drivers -vpip_format_strength -vpip_make_systf_system_defined -vpip_mcd_rawwrite -vpip_set_return_value -acc_fetch_fullname -acc_fetch_fulltype -acc_fetch_name -acc_fetch_type -acc_fetch_type_str -mti_AddLoadDoneCB -mti_AddQuitCB -mti_Break -mti_CreateProcess -mti_CreateProcessWithPriority -mti_Delta -mti_Desensitize -mti_FindRegion -mti_FindSignal -mti_FindVar -mti_FirstLowerRegion -mti_FirstLowerRegion -mti_FirstSignal -mti_FirstVarByRegion -mti_GetArrayElementType -mti_GetArraySignalValue -mti_GetArrayVarValue -mti_GetEnumValues -mti_GetNumRecordElements -mti_GetPrimaryName -mti_GetProductVersion -mti_GetRegionFullName -mti_GetRegionName -mti_GetRegionSourceName -mti_GetResolutionLimit -mti_GetSignalName -mti_GetSignalNameIndirect -mti_GetSignalSubelements -mti_GetSignalType -mti_GetSignalValue -mti_GetSignalValueIndirect -mti_GetTopRegion -mti_GetTypeKind -mti_GetVarKind -mti_GetVarName -mti_GetVarSubelements -mti_GetVarType -mti_GetVarValue -mti_GetVarValueIndirect -mti_NextRegion -mti_NextSignal -mti_NextVar -mti_Now -mti_NowUpper -mti_Quit -mti_ScheduleWakeup -mti_ScheduleWakeup64 -mti_Sensitize -mti_SetSignalValue -mti_SetVarValue -mti_TickLeft -mti_TickLength -mti_TickRight -mti_VsimFree -mti_Interp -mti_Cmd -Tcl_GetStringResult -Tcl_ResetResult -Tcl_GetObjResult -Tcl_ResetResult -Tcl_ListObjGetElements -Tcl_GetStringResult -TclFreeObj -Tcl_ResetResult -Tcl_ResetResult -Tcl_GetString -TclFreeObj diff --git a/cocotb/share/include/cocotb_utils.h b/cocotb/share/include/cocotb_utils.h deleted file mode 100644 index f366e01a..00000000 --- a/cocotb/share/include/cocotb_utils.h +++ /dev/null @@ -1,81 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2013 Potential Ventures Ltd -* Copyright (c) 2013 SolarFlare Communications Inc -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd, -* SolarFlare Communications Inc nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#ifndef COCOTB_UTILS_H_ -#define COCOTB_UTILS_H_ - -#include -#ifdef COCOTBUTILS_EXPORTS -#define COCOTBUTILS_EXPORT COCOTB_EXPORT -#else -#define COCOTBUTILS_EXPORT COCOTB_IMPORT -#endif - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define xstr(a) str(a) -#define str(a) #a - -extern COCOTBUTILS_EXPORT void* utils_dyn_open(const char* lib_name); -extern COCOTBUTILS_EXPORT void* utils_dyn_sym(void *handle, const char* sym_name); - -extern COCOTBUTILS_EXPORT int is_python_context; - -// to_python and to_simulator are implemented as macros instead of functions so -// that the logs reference the user's lineno and filename - -#define to_python() do { \ - if (is_python_context) { \ - LOG_ERROR("FATAL: We are calling up again"); \ - exit(1); \ - } \ - ++is_python_context; \ - LOG_DEBUG("Returning to Python"); \ -} while (0) - -#define to_simulator() do { \ - if (!is_python_context) { \ - LOG_ERROR("FATAL: We have returned twice from Python"); \ - exit(1); \ - } \ - --is_python_context; \ - LOG_DEBUG("Returning to simulator"); \ -} while (0) - -#define COCOTB_UNUSED(x) ((void)x) - -#ifdef __cplusplus -} -#endif - -#endif /* COCOTB_UTILS_H_ */ diff --git a/cocotb/share/include/embed.h b/cocotb/share/include/embed.h deleted file mode 100644 index 99916113..00000000 --- a/cocotb/share/include/embed.h +++ /dev/null @@ -1,55 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2013 Potential Ventures Ltd -* Copyright (c) 2013 SolarFlare Communications Inc -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd, -* SolarFlare Communications Inc nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#ifndef COCOTB_EMBED_H_ -#define COCOTB_EMBED_H_ - -#include -#ifdef COCOTB_EMBED_EXPORTS -#define COCOTB_EMBED_EXPORT COCOTB_EXPORT -#else -#define COCOTB_EMBED_EXPORT COCOTB_IMPORT -#endif - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -extern COCOTB_EMBED_EXPORT void embed_init_python(void); -extern COCOTB_EMBED_EXPORT void embed_sim_cleanup(void); -extern COCOTB_EMBED_EXPORT int embed_sim_init(int argc, char const* const* argv); -extern COCOTB_EMBED_EXPORT void embed_sim_event(gpi_event_t level, const char *msg); - -#ifdef __cplusplus -} -#endif - -#endif /* COCOTB_EMBED_H_ */ diff --git a/cocotb/share/include/exports.h b/cocotb/share/include/exports.h deleted file mode 100644 index 2dcf70e4..00000000 --- a/cocotb/share/include/exports.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright cocotb contributors -// Licensed under the Revised BSD License, see LICENSE for details. -// SPDX-License-Identifier: BSD-3-Clause - -#ifndef COCOTB_EXPORTS_H_ -#define COCOTB_EXPORTS_H_ - -// Make cocotb work correctly when the default visibility is changed to hidden -// Changing the default visibility to hidden has the advantage of significantly reducing the code size, -// load times as well as letting the optimizer produce better code -#if defined(__linux__) || defined(__APPLE__) -#define COCOTB_EXPORT __attribute__ ((visibility ("default"))) -#define COCOTB_IMPORT -#else -#define COCOTB_EXPORT __declspec(dllexport) -#define COCOTB_IMPORT __declspec(dllimport) -#endif - -#endif diff --git a/cocotb/share/include/gpi.h b/cocotb/share/include/gpi.h deleted file mode 100644 index 1ed8cfe2..00000000 --- a/cocotb/share/include/gpi.h +++ /dev/null @@ -1,262 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2013, 2018 Potential Ventures Ltd -* Copyright (c) 2013 SolarFlare Communications Inc -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd, -* SolarFlare Communications Inc nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#ifndef COCOTB_GPI_H_ -#define COCOTB_GPI_H_ - -/* -Generic Language Interface - -This header file defines a Generic Language Interface into any simulator. -Implementations need to implement the underlying functions in gpi_priv.h - -The functions are essentially a limited subset of VPI/VHPI/FLI. - -Implementation specific notes -============================= - -By amazing coincidence, VPI and VHPI are strikingly similar which is obviously -reflected by this header file. Unfortunately, this means that proprietry, -non-standard, less featured language interfaces (for example Mentor FLI) may have -to resort to some hackery, or may not even be capable of implementing a GPI layer. - -Because of the lack of ability to register a callback on event change using the FLI, -we have to create a process with the signal on the sensitivity list to imitate a callback. - -*/ - -#include -#ifdef GPI_EXPORTS -#define GPI_EXPORT COCOTB_EXPORT -#else -#define GPI_EXPORT COCOTB_IMPORT -#endif - -#include -#include -#include -#include -#include - -#include - - -/* - * Declare the handle types. - * - * We want these handles to be opaque pointers, since their layout is not - * exposed to C. We do this by using incomplete types. The assumption being - * made here is that `sizeof(some_cpp_class*) == sizeof(some_c_struct*)`, which - * is true on all reasonable platforms. - */ -#ifdef __cplusplus - /* In C++, we use forward-declarations of the types in gpi_priv.h as our - * incomplete types, as this avoids the need for any casting in GpiCommon.cpp. - */ - class GpiObjHdl; - class GpiCbHdl; - class GpiIterator; - typedef GpiObjHdl *gpi_sim_hdl; - typedef GpiCbHdl *gpi_cb_hdl; - typedef GpiIterator *gpi_iterator_hdl; -#else - /* In C, we declare some incomplete struct types that we never complete. - * The names of these are irrelevant, but for simplicity they match the C++ - * names. - */ - struct GpiObjHdl; - struct GpiCbHdl; - struct GpiIterator; - typedef struct GpiObjHdl *gpi_sim_hdl; - typedef struct GpiCbHdl *gpi_cb_hdl; - typedef struct GpiIterator *gpi_iterator_hdl; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum gpi_event_e { - SIM_INFO = 0, - SIM_TEST_FAIL = 1, - SIM_FAIL = 2, -} gpi_event_t; - -// Functions for controlling/querying the simulation state - -/** - * Returns 1 if there is a registered GPI implementation, 0 otherwise. - * - * Useful for checking if a simulator is running. - */ -GPI_EXPORT bool gpi_has_registered_impl(void); - -// Stop the simulator -GPI_EXPORT void gpi_sim_end(void); - -// Cleanup GPI resources during sim shutdown -GPI_EXPORT void gpi_cleanup(void); - -// Returns simulation time as two uints. Units are default sim units -GPI_EXPORT void gpi_get_sim_time(uint32_t *high, uint32_t *low); -GPI_EXPORT void gpi_get_sim_precision(int32_t *precision); - -/** - * Returns a string with the running simulator product information - * - * @return simulator product string - */ -GPI_EXPORT const char *gpi_get_simulator_product(void); - -/** - * Returns a string with the running simulator version - * - * @return simulator version string - */ -GPI_EXPORT const char *gpi_get_simulator_version(void); - -// Functions for extracting a gpi_sim_hdl to an object -// Returns a handle to the root simulation object, -// Should be freed with gpi_free_handle -GPI_EXPORT gpi_sim_hdl gpi_get_root_handle(const char *name); -GPI_EXPORT gpi_sim_hdl gpi_get_handle_by_name(gpi_sim_hdl parent, const char *name); -GPI_EXPORT gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl parent, int32_t index); -GPI_EXPORT void gpi_free_handle(gpi_sim_hdl gpi_hdl); - -// Types that can be passed to the iterator. -// -// Note these are strikingly similar to the VPI types... -typedef enum gpi_objtype_e { - GPI_UNKNOWN = 0, - GPI_MEMORY = 1, - GPI_MODULE = 2, - GPI_NET = 3, - // GPI_PARAMETER = 4, // Deprecated - GPI_REGISTER = 5, - GPI_ARRAY = 6, - GPI_ENUM = 7, - GPI_STRUCTURE = 8, - GPI_REAL = 9, - GPI_INTEGER = 10, - GPI_STRING = 11, - GPI_GENARRAY = 12, -} gpi_objtype_t; - -// When iterating, we can chose to either get child objects, drivers or loads -typedef enum gpi_iterator_sel_e { - GPI_OBJECTS = 1, - GPI_DRIVERS = 2, - GPI_LOADS = 3, -} gpi_iterator_sel_t; - -typedef enum gpi_set_action_e { - GPI_DEPOSIT = 0, - GPI_FORCE = 1, - GPI_RELEASE = 2, -} gpi_set_action_t; - -// Functions for iterating over entries of a handle -// Returns an iterator handle which can then be used in gpi_next calls -// -// Unlike `vpi_iterate` the iterator handle may only be NULL if the `type` is -// not supported, If no objects of the requested type are found, an empty -// iterator is returned. -GPI_EXPORT gpi_iterator_hdl gpi_iterate(gpi_sim_hdl base, gpi_iterator_sel_t type); - -// Returns NULL when there are no more objects -GPI_EXPORT gpi_sim_hdl gpi_next(gpi_iterator_hdl iterator); - -// Returns the number of objects in the collection of the handle -GPI_EXPORT int gpi_get_num_elems(gpi_sim_hdl gpi_sim_hdl); - -// Returns the left side of the range constraint -GPI_EXPORT int gpi_get_range_left(gpi_sim_hdl gpi_sim_hdl); - -// Returns the right side of the range constraint -GPI_EXPORT int gpi_get_range_right(gpi_sim_hdl gpi_sim_hdl); - -// Functions for querying the properties of a handle -// Caller responsible for freeing the returned string. -// This is all slightly verbose but it saves having to enumerate various value types -// We only care about a limited subset of values. -GPI_EXPORT const char *gpi_get_signal_value_binstr(gpi_sim_hdl gpi_hdl); -GPI_EXPORT const char *gpi_get_signal_value_str(gpi_sim_hdl gpi_hdl); -GPI_EXPORT double gpi_get_signal_value_real(gpi_sim_hdl gpi_hdl); -GPI_EXPORT long gpi_get_signal_value_long(gpi_sim_hdl gpi_hdl); -GPI_EXPORT const char *gpi_get_signal_name_str(gpi_sim_hdl gpi_hdl); -GPI_EXPORT const char *gpi_get_signal_type_str(gpi_sim_hdl gpi_hdl); - -// Returns one of the types defined above e.g. gpiMemory etc. -GPI_EXPORT gpi_objtype_t gpi_get_object_type(gpi_sim_hdl gpi_hdl); - -// Get information about the definition of a handle -GPI_EXPORT const char* gpi_get_definition_name(gpi_sim_hdl gpi_hdl); -GPI_EXPORT const char* gpi_get_definition_file(gpi_sim_hdl gpi_hdl); - -// Determine whether an object value is constant (parameters / generics etc) -GPI_EXPORT int gpi_is_constant(gpi_sim_hdl gpi_hdl); - -// Determine whether an object is indexable -GPI_EXPORT int gpi_is_indexable(gpi_sim_hdl gpi_hdl); - -// Functions for setting the properties of a handle -GPI_EXPORT void gpi_set_signal_value_real(gpi_sim_hdl gpi_hdl, double value, gpi_set_action_t action); -GPI_EXPORT void gpi_set_signal_value_int(gpi_sim_hdl gpi_hdl, int32_t value, gpi_set_action_t action); -GPI_EXPORT void gpi_set_signal_value_binstr(gpi_sim_hdl gpi_hdl, const char *str, gpi_set_action_t action); // String of binary char(s) [1, 0, x, z] -GPI_EXPORT void gpi_set_signal_value_str(gpi_sim_hdl gpi_hdl, const char *str, gpi_set_action_t action); // String of ASCII char(s) - -typedef enum gpi_edge { - GPI_RISING = 1, - GPI_FALLING = 2, -} gpi_edge_e; - -// The callback registering functions -GPI_EXPORT gpi_cb_hdl gpi_register_timed_callback (int (*gpi_function)(const void *), void *gpi_cb_data, uint64_t time); -GPI_EXPORT gpi_cb_hdl gpi_register_value_change_callback (int (*gpi_function)(const void *), void *gpi_cb_data, gpi_sim_hdl gpi_hdl, int edge); -GPI_EXPORT gpi_cb_hdl gpi_register_readonly_callback (int (*gpi_function)(const void *), void *gpi_cb_data); -GPI_EXPORT gpi_cb_hdl gpi_register_nexttime_callback (int (*gpi_function)(const void *), void *gpi_cb_data); -GPI_EXPORT gpi_cb_hdl gpi_register_readwrite_callback (int (*gpi_function)(const void *), void *gpi_cb_data); - -// Calling convention is that 0 = success and negative numbers a failure -// For implementers of GPI the provided macro GPI_RET(x) is provided -GPI_EXPORT void gpi_deregister_callback(gpi_cb_hdl gpi_hdl); - -// Because the internal structures may be different for different implementations -// of GPI we provide a convenience function to extract the callback data -GPI_EXPORT void *gpi_get_callback_data(gpi_cb_hdl gpi_hdl); - -// Print out what implementations are registered. Python needs to be loaded for this, -// Returns the number of libs -GPI_EXPORT size_t gpi_print_registered_impl(void); - -#ifdef __cplusplus -} -#endif - -#endif /* COCOTB_GPI_H_ */ diff --git a/cocotb/share/include/gpi_logging.h b/cocotb/share/include/gpi_logging.h deleted file mode 100644 index b8620338..00000000 --- a/cocotb/share/include/gpi_logging.h +++ /dev/null @@ -1,216 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2013 Potential Ventures Ltd -* Copyright (c) 2013 SolarFlare Communications Inc -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd, -* SolarFlare Communications Inc nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#ifndef COCOTB_GPI_LOGGING_H_ -#define COCOTB_GPI_LOGGING_H_ - -#include -#ifdef GPILOG_EXPORTS -#define GPILOG_EXPORT COCOTB_EXPORT -#else -#define GPILOG_EXPORT COCOTB_IMPORT -#endif - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Named logging level - * - * The native logger only logs level names at these log level values. - * They were specifically chosen to align with the default level values in the Python logging module. - * Implementers of custom loggers should emit human readable level names for these value, but may support other values - */ -enum gpi_log_levels { - GPIDebug = 10, ///< Prints `DEBUG` by default. Verbose information, useful for debugging - GPIInfo = 20, ///< Prints `INFO` by default. Information about major events in the current program - GPIWarning = 30, ///< Prints `WARN` by default. Encountered a recoverable bug, or information about surprising behavior - GPIError = 40, ///< Prints `ERROR` by default. An unrecoverable error - GPICritical = 50 ///< Prints `CRITICAL` by default. An unrecoverable error, to be followed by immediate simulator shutdown -}; - -/** Logs a message at the given log level using the current log handler. - Automatically populates arguments using information in the called context. - @param level The level at which to log the message - */ -#define LOG_(level, ...) \ - gpi_log("cocotb.gpi", level, __FILE__, __func__, __LINE__, __VA_ARGS__); - -/** Logs a message at DEBUG log level using the current log handler. - Automatically populates arguments using information in the called context. - */ -#define LOG_DEBUG(...) LOG_(GPIDebug, __VA_ARGS__) - -/** Logs a message at INFO log level using the current log handler. - Automatically populates arguments using information in the called context. - */ -#define LOG_INFO(...) LOG_(GPIInfo, __VA_ARGS__); - -/** Logs a message at WARN log level using the current log handler. - Automatically populates arguments using information in the called context. - */ -#define LOG_WARN(...) LOG_(GPIWarning, __VA_ARGS__); - -/** Logs a message at ERROR log level using the current log handler. - Automatically populates arguments using information in the called context. - */ -#define LOG_ERROR(...) LOG_(GPIError, __VA_ARGS__); - -/** Logs a message at CRITICAL log level using the current log handler. - Automatically populates arguments using information in the called context. - */ -#define LOG_CRITICAL(...) LOG_(GPICritical, __VA_ARGS__); - -/** Type of a log handler function. - @param userdata private implementation data registered with this function - @param name Name of the logger - @param level Level at which to log the message - @param pathname Name of the file where the call site is located - @param funcname Name of the function where the call site is located - @param lineno Line number of the call site - @param msg The message to log, uses C-sprintf-style format specifier - @param args Additional arguments; formatted and inserted in message according to format specifier in msg argument - */ -typedef void (gpi_log_handler_type)( - void *userdata, - const char *name, - int level, - const char *pathname, - const char *funcname, - long lineno, - const char *msg, - va_list args); - -/** Log a message using the currently registered log handler. - User is expected to populate all arguments to this function. - @param name Name of the logger - @param level Level at which to log the message - @param pathname Name of the file where the call site is located - @param funcname Name of the function where the call site is located - @param lineno Line number of the call site - @param msg The message to log, uses C-sprintf-style format specifier - @param ... Additional arguments; formatted and inserted in message according to format specifier in msg argument - */ -GPILOG_EXPORT void gpi_log( - const char *name, - int level, - const char *pathname, - const char *funcname, - long lineno, - const char *msg, - ...); - -/** Log a message using the currently registered log handler. - User is expected to populate all arguments to this function. - @param name Name of the logger - @param level Level at which to log the message - @param pathname Name of the file where the call site is located - @param funcname Name of the function where the call site is located - @param lineno Line number of the call site - @param msg The message to log, uses C-sprintf-style format specifier - @param args Additional arguments; formatted and inserted in message according to format specifier in msg argument - */ -GPILOG_EXPORT void gpi_vlog( - const char *name, - int level, - const char *pathname, - const char *funcname, - long lineno, - const char *msg, - va_list args); - -/** Retrieve the current log handler. - @param handler Location to return current log handler. If no custom logger is registered this will be `NULL`. - @param userdata Location to return log handler userdata - */ -GPILOG_EXPORT void gpi_get_log_handler(gpi_log_handler_type **handler, void **userdata); - -/** Set custom log handler - @param handler Handler function to call when the GPI logs a message - @param userdata Data to pass to the handler function when logging a message - */ -GPILOG_EXPORT void gpi_set_log_handler(gpi_log_handler_type *handler, void *userdata); - -/** Clear the current custom log handler and use native logger - */ -GPILOG_EXPORT void gpi_clear_log_handler(void); - -/** Log a message using the native log handler. - User is expected to populate all arguments to this function. - @param name Name of the logger - @param level Level at which to log the message - @param pathname Name of the file where the call site is located - @param funcname Name of the function where the call site is located - @param lineno Line number of the call site - @param msg The message to log, uses C-sprintf-style format specifier - @param ... Additional arguments; formatted and inserted in message according to format specifier in msg argument - */ -GPILOG_EXPORT void gpi_native_logger_log( - const char *name, - int level, - const char *pathname, - const char *funcname, - long lineno, - const char *msg, - ...); - -/** Log a message using the native log handler. - User is expected to populate all arguments to this function. - @param name Name of the logger - @param level Level at which to log the message - @param pathname Name of the file where the call site is located - @param funcname Name of the function where the call site is located - @param lineno Line number of the call site - @param msg The message to log, uses C-sprintf-style format specifier - @param args Additional arguments; formatted and inserted in message according to format specifier in msg argument - */ -GPILOG_EXPORT void gpi_native_logger_vlog( - const char *name, - int level, - const char *pathname, - const char *funcname, - long lineno, - const char *msg, - va_list args); - -/** Set minimum logging level of the native logger. - If a logging request occurs where the logging level is lower than the level set by this function, it is not logged. - Only affects the native logger. - @param level Logging level - @return Previous logging level - */ -GPILOG_EXPORT int gpi_native_logger_set_level(int level); - -#ifdef __cplusplus -} -#endif - -#endif /* COCOTB_GPI_LOGGING_H_ */ diff --git a/cocotb/share/include/py_gpi_logging.h b/cocotb/share/include/py_gpi_logging.h deleted file mode 100644 index bbfd8749..00000000 --- a/cocotb/share/include/py_gpi_logging.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright cocotb contributors -// Licensed under the Revised BSD License, see LICENSE for details. -// SPDX-License-Identifier: BSD-3-Clause - -#ifndef PY_GPI_LOGGING_H -#define PY_GPI_LOGGING_H - -#include -#ifdef PYGPILOG_EXPORTS -#define PYGPILOG_EXPORT COCOTB_EXPORT -#else -#define PYGPILOG_EXPORT COCOTB_IMPORT -#endif - -#define PY_GPI_LOG_SIZE 1024 - -#ifdef __cplusplus -extern "C" { -#endif - -PYGPILOG_EXPORT void py_gpi_logger_set_level(int level); - -PYGPILOG_EXPORT void py_gpi_logger_initialize(PyObject * handler, PyObject * filter); - -PYGPILOG_EXPORT void py_gpi_logger_finalize(); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/cocotb/share/include/sv_vpi_user.h b/cocotb/share/include/sv_vpi_user.h deleted file mode 100644 index 5843af67..00000000 --- a/cocotb/share/include/sv_vpi_user.h +++ /dev/null @@ -1,564 +0,0 @@ -/*************************************************************************** -* sv_vpi_user.h -* -* SystemVerilog VPI extensions. -* -* This file contains the constant definitions, structure definitions, and -* routine declarations used by the SystemVerilog Verification Procedural -* Interface (VPI) access routines. -* -***************************************************************************/ - -/*************************************************************************** -* NOTE: -* The constant values 600 through 999 are reserved for use in this file. -* - the range 600-749 is reserved for SV VPI model extensions -* - the range 750-779 is reserved for the Coverage VPI -* - the range 800-899 is reserved for future use -* Overlaps in the numerical ranges are permitted for different categories -* of identifiers; e.g. -* - object types -* - properties -* - callbacks -**************************************************************************/ - -#ifndef SV_VPI_USER_H -#define SV_VPI_USER_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/****************************** OBJECT TYPES ******************************/ -#define vpiPackage 600 -#define vpiInterface 601 -#define vpiProgram 602 -#define vpiInterfaceArray 603 -#define vpiProgramArray 604 -#define vpiTypespec 605 -#define vpiModport 606 -#define vpiInterfaceTfDecl 607 -#define vpiRefObj 608 -#define vpiTypeParameter 609 - -/* variables */ -#define vpiVarBit vpiRegBit -#define vpiLongIntVar 610 -#define vpiShortIntVar 611 -#define vpiIntVar 612 -#define vpiShortRealVar 613 -#define vpiByteVar 614 -#define vpiClassVar 615 -#define vpiStringVar 616 -#define vpiEnumVar 617 -#define vpiStructVar 618 -#define vpiUnionVar 619 -#define vpiBitVar 620 -#define vpiLogicVar vpiReg -#define vpiArrayVar vpiRegArray -#define vpiClassObj 621 -#define vpiChandleVar 622 -#define vpiPackedArrayVar 623 -#define vpiVirtualInterfaceVar 728 - -/* typespecs */ -#define vpiLongIntTypespec 625 -#define vpiShortRealTypespec 626 -#define vpiByteTypespec 627 -#define vpiShortIntTypespec 628 -#define vpiIntTypespec 629 -#define vpiClassTypespec 630 -#define vpiStringTypespec 631 -#define vpiChandleTypespec 632 -#define vpiEnumTypespec 633 -#define vpiEnumConst 634 -#define vpiIntegerTypespec 635 -#define vpiTimeTypespec 636 -#define vpiRealTypespec 637 -#define vpiStructTypespec 638 -#define vpiUnionTypespec 639 -#define vpiBitTypespec 640 -#define vpiLogicTypespec 641 -#define vpiArrayTypespec 642 -#define vpiVoidTypespec 643 -#define vpiTypespecMember 644 -#define vpiPackedArrayTypespec 692 -#define vpiSequenceTypespec 696 -#define vpiPropertyTypespec 697 -#define vpiEventTypespec 698 - -#define vpiClockingBlock 650 -#define vpiClockingIODecl 651 -#define vpiClassDefn 652 -#define vpiConstraint 653 -#define vpiConstraintOrdering 654 - -#define vpiDistItem 645 -#define vpiAliasStmt 646 -#define vpiThread 647 -#define vpiMethodFuncCall 648 -#define vpiMethodTaskCall 649 - -/* concurrent assertions */ -#define vpiAssert 686 -#define vpiAssume 687 -#define vpiCover 688 -#define vpiRestrict 901 - -#define vpiDisableCondition 689 -#define vpiClockingEvent 690 - -/* property decl, spec */ -#define vpiPropertyDecl 655 -#define vpiPropertySpec 656 -#define vpiPropertyExpr 657 -#define vpiMulticlockSequenceExpr 658 -#define vpiClockedSeq 659 -#define vpiClockedProp 902 -#define vpiPropertyInst 660 -#define vpiSequenceDecl 661 -#define vpiCaseProperty 662 /* property case */ -#define vpiCasePropertyItem 905 /* property case item */ -#define vpiSequenceInst 664 -#define vpiImmediateAssert 665 -#define vpiImmediateAssume 694 -#define vpiImmediateCover 695 -#define vpiReturn 666 -/* pattern */ -#define vpiAnyPattern 667 -#define vpiTaggedPattern 668 -#define vpiStructPattern 669 -/* do .. while */ -#define vpiDoWhile 670 -/* waits */ -#define vpiOrderedWait 671 -#define vpiWaitFork 672 -/* disables */ -#define vpiDisableFork 673 -#define vpiExpectStmt 674 -#define vpiForeachStmt 675 -#define vpiReturnStmt 691 -#define vpiFinal 676 -#define vpiExtends 677 -#define vpiDistribution 678 -#define vpiSeqFormalDecl 679 -#define vpiPropFormalDecl 699 -#define vpiArrayNet vpiNetArray -#define vpiEnumNet 680 -#define vpiIntegerNet 681 -#define vpiLogicNet vpiNet -#define vpiTimeNet 682 -#define vpiStructNet 683 -#define vpiBreak 684 -#define vpiContinue 685 -#define vpiPackedArrayNet 693 -#define vpiConstraintExpr 747 -#define vpiElseConst 748 -#define vpiImplication 749 -#define vpiConstrIf 738 -#define vpiConstrIfElse 739 -#define vpiConstrForEach 736 -#define vpiLetDecl 903 -#define vpiLetExpr 904 - -/******************************** METHODS *********************************/ -/************* methods used to traverse 1 to 1 relationships **************/ -#define vpiActual 700 - -#define vpiTypedefAlias 701 - -#define vpiIndexTypespec 702 -#define vpiBaseTypespec 703 -#define vpiElemTypespec 704 - -#define vpiInputSkew 706 -#define vpiOutputSkew 707 -#define vpiGlobalClocking 708 -#define vpiDefaultClocking 709 -#define vpiDefaultDisableIff 710 - -#define vpiOrigin 713 -#define vpiPrefix 714 -#define vpiWith 715 - -#define vpiProperty 718 - -#define vpiValueRange 720 -#define vpiPattern 721 -#define vpiWeight 722 -#define vpiConstraintItem 746 - -/************ methods used to traverse 1 to many relationships ************/ -#define vpiTypedef 725 -#define vpiImport 726 -#define vpiDerivedClasses 727 -#define vpiInterfaceDecl vpiVirtualInterfaceVar /* interface decl deprecated */ - -#define vpiMethods 730 -#define vpiSolveBefore 731 -#define vpiSolveAfter 732 - -#define vpiWaitingProcesses 734 - -#define vpiMessages 735 -#define vpiLoopVars 737 - -#define vpiConcurrentAssertions 740 -#define vpiMatchItem 741 -#define vpiMember 742 -#define vpiElement 743 - -/************* methods used to traverse 1 to many relationships ***************/ -#define vpiAssertion 744 - -/*********** methods used to traverse both 1-1 and 1-many relations ***********/ -#define vpiInstance 745 - -/**************************************************************************/ -/************************ generic object properties ***********************/ -/**************************************************************************/ - -#define vpiTop 600 - -#define vpiUnit 602 -#define vpiJoinType 603 -#define vpiJoin 0 -#define vpiJoinNone 1 -#define vpiJoinAny 2 -#define vpiAccessType 604 -#define vpiForkJoinAcc 1 -#define vpiExternAcc 2 -#define vpiDPIExportAcc 3 -#define vpiDPIImportAcc 4 - -#define vpiArrayType 606 -#define vpiStaticArray 1 -#define vpiDynamicArray 2 -#define vpiAssocArray 3 -#define vpiQueueArray 4 -#define vpiArrayMember 607 - -#define vpiIsRandomized 608 -#define vpiLocalVarDecls 609 -#define vpiOpStrong 656 /* strength of temporal operator */ -#define vpiRandType 610 -#define vpiNotRand 1 -#define vpiRand 2 -#define vpiRandC 3 -#define vpiPortType 611 -#define vpiInterfacePort 1 -#define vpiModportPort 2 -/* vpiPort is also a port type. It is defined in vpi_user.h */ - -#define vpiConstantVariable 612 -#define vpiStructUnionMember 615 - -#define vpiVisibility 620 -#define vpiPublicVis 1 -#define vpiProtectedVis 2 -#define vpiLocalVis 3 - -/* Return values for vpiConstType property */ -#define vpiOneStepConst 9 -#define vpiUnboundedConst 10 -#define vpiNullConst 11 - -#define vpiAlwaysType 624 -#define vpiAlwaysComb 2 -#define vpiAlwaysFF 3 -#define vpiAlwaysLatch 4 - -#define vpiDistType 625 -#define vpiEqualDist 1 /* constraint equal distribution */ -#define vpiDivDist 2 /* constraint divided distribution */ - -#define vpiPacked 630 -#define vpiTagged 632 -#define vpiRef 6 /* Return value for vpiDirection property */ -#define vpiVirtual 635 -#define vpiHasActual 636 -#define vpiIsConstraintEnabled 638 -#define vpiSoft 639 - - -#define vpiClassType 640 -#define vpiMailboxClass 1 -#define vpiSemaphoreClass 2 -#define vpiUserDefinedClass 3 -#define vpiProcessClass 4 -#define vpiMethod 645 -#define vpiIsClockInferred 649 -#define vpiIsDeferred 657 -#define vpiIsFinal 658 -#define vpiIsCoverSequence 659 -#define vpiQualifier 650 -#define vpiNoQualifier 0 -#define vpiUniqueQualifier 1 -#define vpiPriorityQualifier 2 -#define vpiTaggedQualifier 4 -#define vpiRandQualifier 8 -#define vpiInsideQualifier 16 - -#define vpiInputEdge 651 /* returns vpiNoEdge, vpiPosedge, - vpiNegedge */ -#define vpiOutputEdge 652 /* returns vpiNoEdge, vpiPosedge, - vpiNegedge */ -#define vpiGeneric 653 - -/* Compatibility-mode property and values (object argument == NULL) */ -#define vpiCompatibilityMode 654 -#define vpiMode1364v1995 1 -#define vpiMode1364v2001 2 -#define vpiMode1364v2005 3 -#define vpiMode1800v2005 4 -#define vpiMode1800v2009 5 - -#define vpiPackedArrayMember 655 -#define vpiStartLine 661 -#define vpiColumn 662 -#define vpiEndLine 663 -#define vpiEndColumn 664 - -/* memory allocation scheme for transient objects */ -#define vpiAllocScheme 658 -#define vpiAutomaticScheme 1 -#define vpiDynamicScheme 2 -#define vpiOtherScheme 3 - -#define vpiObjId 660 - -#define vpiDPIPure 665 -#define vpiDPIContext 666 -#define vpiDPICStr 667 -#define vpiDPI 1 -#define vpiDPIC 2 -#define vpiDPICIdentifier 668 - -/******************************** Operators *******************************/ -#define vpiImplyOp 50 /* -> implication operator */ -#define vpiNonOverlapImplyOp 51 /* |=> nonoverlapped implication */ -#define vpiOverlapImplyOp 52 /* |-> overlapped implication operator */ -#define vpiAcceptOnOp 83 /* accept_on operator */ -#define vpiRejectOnOp 84 /* reject_on operator */ -#define vpiSyncAcceptOnOp 85 /* sync_accept_on operator */ -#define vpiSyncRejectOnOp 86 /* sync_reject_on operator */ -#define vpiOverlapFollowedByOp 87 /* overlapped followed_by operator */ -#define vpiNonOverlapFollowedByOp 88 /* nonoverlapped followed_by operator */ -#define vpiNexttimeOp 89 /* nexttime operator */ -#define vpiAlwaysOp 90 /* always operator */ -#define vpiEventuallyOp 91 /* eventually operator */ -#define vpiUntilOp 92 /* until operator */ -#define vpiUntilWithOp 93 /* until_with operator */ - -#define vpiUnaryCycleDelayOp 53 /* binary cycle delay (##) operator */ -#define vpiCycleDelayOp 54 /* binary cycle delay (##) operator */ -#define vpiIntersectOp 55 /* intersection operator */ -#define vpiFirstMatchOp 56 /* first_match operator */ -#define vpiThroughoutOp 57 /* throughout operator */ -#define vpiWithinOp 58 /* within operator */ -#define vpiRepeatOp 59 /* [=] nonconsecutive repetition */ -#define vpiConsecutiveRepeatOp 60 /* [*] consecutive repetition */ -#define vpiGotoRepeatOp 61 /* [->] goto repetition */ - -#define vpiPostIncOp 62 /* ++ post-increment */ -#define vpiPreIncOp 63 /* ++ pre-increment */ -#define vpiPostDecOp 64 /* -- post-decrement */ -#define vpiPreDecOp 65 /* -- pre-decrement */ - -#define vpiMatchOp 66 /* match() operator */ -#define vpiCastOp 67 /* type'() operator */ -#define vpiIffOp 68 /* iff operator */ -#define vpiWildEqOp 69 /* ==? operator */ -#define vpiWildNeqOp 70 /* !=? operator */ - -#define vpiStreamLROp 71 /* left-to-right streaming {>>} operator */ -#define vpiStreamRLOp 72 /* right-to-left streaming {<<} operator */ - -#define vpiMatchedOp 73 /* the .matched sequence operation */ -#define vpiTriggeredOp 74 /* the .triggered sequence operation */ -#define vpiAssignmentPatternOp 75 /* '{} assignment pattern */ -#define vpiMultiAssignmentPatternOp 76 /* '{n{}} multi assignment pattern */ -#define vpiIfOp 77 /* if operator */ -#define vpiIfElseOp 78 /* if/else operator */ -#define vpiCompAndOp 79 /* Composite and operator */ -#define vpiCompOrOp 80 /* Composite or operator */ -#define vpiImpliesOp 94 /* implies operator */ -#define vpiInsideOp 95 /* inside operator */ -#define vpiTypeOp 81 /* type operator */ -#define vpiAssignmentOp 82 /* Normal assignment */ - -/*********************** task/function properties ***********************/ -#define vpiOtherFunc 6 /* returns other types; for property vpiFuncType */ -/* vpiValid,vpiValidTrue,vpiValidFalse are deprecated in 1800-2009 */ - -/*********************** value for vpiValid *****************************/ -#define vpiValidUnknown 2 /* Validity of variable is unknown */ - -/************************** STRUCTURE DEFINITIONS *************************/ - -/***************************** structure *****************************/ - -/**************************** CALLBACK REASONS ****************************/ -#define cbStartOfThread 600 /* callback on thread creation */ -#define cbEndOfThread 601 /* callback on thread termination */ -#define cbEnterThread 602 /* callback on reentering thread */ -#define cbStartOfFrame 603 /* callback on frame creation */ -#define cbEndOfFrame 604 /* callback on frame exit */ -#define cbSizeChange 605 /* callback on array variable size change */ -#define cbCreateObj 700 /* callback on class object creation */ -#define cbReclaimObj 701 /* callback on class object reclaimed by automatic memory management */ - -#define cbEndOfObject 702 /* callback on transient object deletion */ - -/************************* FUNCTION DECLARATIONS **************************/ - - -/**************************************************************************/ -/*************************** Coverage VPI *********************************/ -/**************************************************************************/ - -/* coverage control */ -#define vpiCoverageStart 750 -#define vpiCoverageStOp 751 -#define vpiCoverageReset 752 -#define vpiCoverageCheck 753 -#define vpiCoverageMerge 754 -#define vpiCoverageSave 755 - -/* coverage type properties */ -#define vpiAssertCoverage 760 -#define vpiFsmStateCoverage 761 -#define vpiStatementCoverage 762 -#define vpiToggleCoverage 763 - -/* coverage status properties */ -#define vpiCovered 765 -#define vpiCoverMax 766 -#define vpiCoveredCount 767 - -/* assertion-specific coverage status properties */ -#define vpiAssertAttemptCovered 770 -#define vpiAssertSuccessCovered 771 -#define vpiAssertFailureCovered 772 -#define vpiAssertVacuousSuccessCovered 773 -#define vpiAssertDisableCovered 774 -#define vpiAssertKillCovered 777 - -/* FSM-specific coverage status properties */ -#define vpiFsmStates 775 -#define vpiFsmStateExpression 776 - -/* FSM handle types */ -#define vpiFsm 758 -#define vpiFsmHandle 759 - - -/***************************************************************************/ -/***************************** Assertion VPI *******************************/ -/***************************************************************************/ - -/* assertion callback types */ -#define cbAssertionStart 606 -#define cbAssertionSuccess 607 -#define cbAssertionFailure 608 -#define cbAssertionVacuousSuccess 657 -#define cbAssertionDisabledEvaluation 658 -#define cbAssertionStepSuccess 609 -#define cbAssertionStepFailure 610 -#define cbAssertionLock 661 -#define cbAssertionUnlock 662 -#define cbAssertionDisable 611 -#define cbAssertionEnable 612 -#define cbAssertionReset 613 -#define cbAssertionKill 614 -#define cbAssertionEnablePassAction 645 -#define cbAssertionEnableFailAction 646 -#define cbAssertionDisablePassAction 647 -#define cbAssertionDisableFailAction 648 -#define cbAssertionEnableNonvacuousAction 649 -#define cbAssertionDisableVacuousAction 650 - -/* assertion "system" callback types */ -#define cbAssertionSysInitialized 615 -#define cbAssertionSysOn 616 -#define cbAssertionSysOff 617 -#define cbAssertionSysKill 631 -#define cbAssertionSysLock 659 -#define cbAssertionSysUnlock 660 -#define cbAssertionSysEnd 618 -#define cbAssertionSysReset 619 -#define cbAssertionSysEnablePassAction 651 -#define cbAssertionSysEnableFailAction 652 -#define cbAssertionSysDisablePassAction 653 -#define cbAssertionSysDisableFailAction 654 -#define cbAssertionSysEnableNonvacuousAction 655 -#define cbAssertionSysDisableVacuousAction 656 - -/* assertion control constants */ -#define vpiAssertionLock 645 -#define vpiAssertionUnlock 646 -#define vpiAssertionDisable 620 -#define vpiAssertionEnable 621 -#define vpiAssertionReset 622 -#define vpiAssertionKill 623 -#define vpiAssertionEnableStep 624 -#define vpiAssertionDisableStep 625 -#define vpiAssertionClockSteps 626 -#define vpiAssertionSysLock 647 -#define vpiAssertionSysUnlock 648 -#define vpiAssertionSysOn 627 -#define vpiAssertionSysOff 628 -#define vpiAssertionSysKill 632 -#define vpiAssertionSysEnd 629 -#define vpiAssertionSysReset 630 -#define vpiAssertionDisablePassAction 633 -#define vpiAssertionEnablePassAction 634 -#define vpiAssertionDisableFailAction 635 -#define vpiAssertionEnableFailAction 636 -#define vpiAssertionDisableVacuousAction 637 -#define vpiAssertionEnableNonvacuousAction 638 -#define vpiAssertionSysEnablePassAction 639 -#define vpiAssertionSysEnableFailAction 640 -#define vpiAssertionSysDisablePassAction 641 -#define vpiAssertionSysDisableFailAction 642 -#define vpiAssertionSysEnableNonvacuousAction 643 -#define vpiAssertionSysDisableVacuousAction 644 - - -typedef struct t_vpi_assertion_step_info { - PLI_INT32 matched_expression_count; - vpiHandle *matched_exprs; /* array of expressions */ - PLI_INT32 stateFrom, stateTo; /* identify transition */ -} s_vpi_assertion_step_info, *p_vpi_assertion_step_info; - -typedef struct t_vpi_attempt_info { - union { - vpiHandle failExpr; - p_vpi_assertion_step_info step; - } detail; - s_vpi_time attemptStartTime; /* Time attempt triggered */ -} s_vpi_attempt_info, *p_vpi_attempt_info; - -/* typedef for vpi_register_assertion_cb callback function */ -typedef PLI_INT32(vpi_assertion_callback_func)( - PLI_INT32 reason, /* callback reason */ - p_vpi_time cb_time, /* callback time */ - vpiHandle assertion, /* handle to assertion */ - p_vpi_attempt_info info, /* attempt related information */ - PLI_BYTE8 *user_data /* user data entered upon registration */ -); - -vpiHandle vpi_register_assertion_cb( - vpiHandle assertion, /* handle to assertion */ - PLI_INT32 reason, /* reason for which callbacks needed */ - vpi_assertion_callback_func *cb_rtn, - PLI_BYTE8 *user_data /* user data to be supplied to cb */ -); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/cocotb/share/include/vhpi_user.h b/cocotb/share/include/vhpi_user.h deleted file mode 100644 index f997b30a..00000000 --- a/cocotb/share/include/vhpi_user.h +++ /dev/null @@ -1,1282 +0,0 @@ -/* - * |---------------------------------------------------------------| - * | | - * | This is the VHPI header file | - * | | - * | | - * | | - * | FOR CONFORMANCE WITH THE VHPI STANDARD, A VHPI APPLICATION | - * | OR PROGRAM MUST REFERENCE THIS HEADER FILE | - * | Its contents can be modified to include vendor extensions. | - * | | - * |---------------------------------------------------------------| - */ - -/*** File vhpi_user.h ***/ -/*** This file describes the procedural interface to access VHDL - compiled, instantiated and run-time data. It is derived from - the UML model. ***/ - -#ifndef VHPI_USER_H -#define VHPI_USER_H -#include -#include -/* Ensure that size-critical types are defined on all OS platforms. */ -#if defined(_MSC_VER) - typedef unsigned __int64 uint64_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int8 uint8_t; - typedef signed __int64 int64_t; - typedef signed __int32 int32_t; - typedef signed __int8 int8_t; -#elif defined(__MINGW32__) - #include -#elif defined(__linux) - #include -#else - #include -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#pragma pack(8) - -/*---------------------------------------------------------------------------*/ -/*--------------------------- Portability Help ------------------------------*/ -/*---------------------------------------------------------------------------*/ -/* Use to import/export a symbol */ -#if defined(WIN32) || defined(WIN64) - #ifndef PLI_DLLISPEC - #define PLI_DLLISPEC __declspec(dllimport) - #define VHPI_USER_DEFINED_DLLISPEC 1 - #endif - #ifndef PLI_DLLESPEC - #define PLI_DLLESPEC __declspec(dllexport) - #define VHPI_USER_DEFINED_DLLESPEC 1 - #endif -#else - #ifndef PLI_DLLISPEC - #define PLI_DLLISPEC - #endif - #ifndef PLI_DLLESPEC - #define PLI_DLLESPEC - #endif -#endif - -/* Use to mark a function as external */ -#ifndef PLI_EXTERN - #define PLI_EXTERN -#endif - -/* Use to mark a variable as external */ -#ifndef PLI_VEXTERN - #define PLI_VEXTERN extern -#endif - -#ifndef PLI_PROTOTYPES - #define PLI_PROTOTYPES - /* object is defined imported by the application */ - #define XXTERN PLI_EXTERN PLI_DLLISPEC - /* object is exported by the application */ - #define EETERN PLI_EXTERN PLI_DLLESPEC -#endif - -/* basic typedefs */ -#ifndef VHPI_TYPES - #define VHPI_TYPES - typedef uint32_t *vhpiHandleT; - typedef uint32_t vhpiEnumT; - typedef uint8_t vhpiSmallEnumT; - typedef uint32_t vhpiIntT; - typedef uint64_t vhpiLongIntT; - typedef char vhpiCharT; - typedef double vhpiRealT; - typedef uint32_t vhpiSmallPhysT; - - typedef struct vhpiPhysS - { - int32_t high; - uint32_t low; - } vhpiPhysT; - - /* Sized variables */ - #ifndef PLI_TYPES - #define PLI_TYPES - typedef int PLI_INT32; - typedef unsigned int PLI_UINT32; - typedef short PLI_INT16; - typedef unsigned short PLI_UINT16; - typedef char PLI_BYTE8; - typedef unsigned char PLI_UBYTE8; - typedef void PLI_VOID; - #endif - - /********************** time structure ****************************/ - typedef struct vhpiTimeS - { - uint32_t high; - uint32_t low; - } vhpiTimeT; - - /********************** value structure **************************/ - - /* value formats */ - typedef enum - { - vhpiBinStrVal = 1, /* do not move */ - vhpiOctStrVal = 2, /* do not move */ - vhpiDecStrVal = 3, /* do not move */ - vhpiHexStrVal = 4, /* do not move */ - vhpiEnumVal = 5, - vhpiIntVal = 6, - vhpiLogicVal = 7, - vhpiRealVal = 8, - vhpiStrVal = 9, - vhpiCharVal = 10, - vhpiTimeVal = 11, - vhpiPhysVal = 12, - vhpiObjTypeVal = 13, - vhpiPtrVal = 14, - vhpiEnumVecVal = 15, - vhpiIntVecVal = 16, - vhpiLogicVecVal = 17, - vhpiRealVecVal = 18, - vhpiTimeVecVal = 19, - vhpiPhysVecVal = 20, - vhpiPtrVecVal = 21, - vhpiRawDataVal = 22, - vhpiSmallEnumVal = 23, - vhpiSmallEnumVecVal = 24, - vhpiLongIntVal = 25, - vhpiLongIntVecVal = 26, - vhpiSmallPhysVal = 27, - vhpiSmallPhysVecVal = 28 - - #ifdef VHPIEXTEND_VAL_FORMATS - VHPIEXTEND_VAL_FORMATS - #endif - } vhpiFormatT; - - /* value structure */ - typedef struct vhpiValueS - { - vhpiFormatT format; /* vhpi[Char,[Bin,Oct,Dec,Hex]Str, - [Small]Enum,Logic,Int,Real,[Small]Phys,Time,Ptr, - [Small]EnumVec,LogicVec,IntVect,RealVec,[Small]PhysVec,TimeVec, - PtrVec,ObjType,RawData]Val */ -#ifndef IUS - size_t bufSize; /* the size in bytes of the value buffer; this is set by the user */ -#else - int32_t bufSize; /* IUS/Xcelium defines this as 32-bits, even when running in 64-bit mode */ -#endif - int32_t numElems; - /* different meanings depending on the format: - vhpiStrVal, vhpi{Bin...}StrVal: size of string - array type values: number of array elements - scalar type values: undefined - */ - - vhpiPhysT unit; - union - { - vhpiEnumT enumv, *enumvs; - vhpiSmallEnumT smallenumv, *smallenumvs; - vhpiIntT intg, *intgs; - vhpiLongIntT longintg, *longintgs; - vhpiRealT real, *reals; - vhpiSmallPhysT smallphys, *smallphyss; - vhpiPhysT phys, *physs; - vhpiTimeT time, *times; - vhpiCharT ch, *str; - void *ptr, **ptrs; - } value; - } vhpiValueT; -#endif - -/* Following are the constant definitions. They are divided into - three major areas: - - 1) object types - - 2) access methods - - 3) properties - -*/ - -#ifndef IUS -#define vhpiUndefined -1 -#else -#define vhpiUndefined 1000 -#endif - -/*************** OBJECT KINDS *******************/ -typedef enum -{ - vhpiAccessTypeDeclK = 1001, - vhpiAggregateK = 1002, - vhpiAliasDeclK = 1003, - vhpiAllK = 1004, - vhpiAllocatorK = 1005, - vhpiAnyCollectionK = 1006, - vhpiArchBodyK = 1007, - vhpiArgvK = 1008, - vhpiArrayTypeDeclK = 1009, - vhpiAssertStmtK = 1010, - vhpiAssocElemK = 1011, - vhpiAttrDeclK = 1012, - vhpiAttrSpecK = 1013, - vhpiBinaryExprK = 1014, /* DEPRECATED */ - vhpiBitStringLiteralK = 1015, - vhpiBlockConfigK = 1016, - vhpiBlockStmtK = 1017, - vhpiBranchK = 1018, - vhpiCallbackK = 1019, - vhpiCaseStmtK = 1020, - vhpiCharLiteralK = 1021, - vhpiCompConfigK = 1022, - vhpiCompDeclK = 1023, - vhpiCompInstStmtK = 1024, - vhpiCondSigAssignStmtK = 1025, - vhpiCondWaveformK = 1026, - vhpiConfigDeclK = 1027, - vhpiConstDeclK = 1028, - vhpiConstParamDeclK = 1029, - vhpiConvFuncK = 1030, - vhpiDerefObjK = 1031, - vhpiDisconnectSpecK = 1032, - vhpiDriverK = 1033, - vhpiDriverCollectionK = 1034, - vhpiElemAssocK = 1035, - vhpiElemDeclK = 1036, - vhpiEntityClassEntryK = 1037, - vhpiEntityDeclK = 1038, - vhpiEnumLiteralK = 1039, - vhpiEnumRangeK = 1040, - vhpiEnumTypeDeclK = 1041, - vhpiExitStmtK = 1042, - vhpiFileDeclK = 1043, - vhpiFileParamDeclK = 1044, - vhpiFileTypeDeclK = 1045, - vhpiFloatRangeK = 1046, - vhpiFloatTypeDeclK = 1047, - vhpiForGenerateK = 1048, - vhpiForLoopK = 1049, - vhpiForeignfK = 1050, - vhpiFuncCallK = 1051, - vhpiFuncDeclK = 1052, - vhpiGenericDeclK = 1053, - vhpiGroupDeclK = 1054, - vhpiGroupTempDeclK = 1055, - vhpiIfGenerateK = 1056, - vhpiIfStmtK = 1057, - vhpiInPortK = 1058, - vhpiIndexedNameK = 1059, - vhpiIntLiteralK = 1060, - vhpiIntRangeK = 1061, - vhpiIntTypeDeclK = 1062, - vhpiIteratorK = 1063, - vhpiLibraryDeclK = 1064, - vhpiLoopStmtK = 1065, - vhpiNextStmtK = 1066, - vhpiNullLiteralK = 1067, - vhpiNullStmtK = 1068, - vhpiOperatorK = 1069, - vhpiOthersK = 1070, - vhpiOutPortK = 1071, - vhpiPackBodyK = 1072, - vhpiPackDeclK = 1073, - vhpiPackInstK = 1074, - vhpiParamAttrNameK = 1075, - vhpiPhysLiteralK = 1076, - vhpiPhysRangeK = 1077, - vhpiPhysTypeDeclK = 1078, - vhpiPortDeclK = 1079, - vhpiProcCallStmtK = 1080, - vhpiProcDeclK = 1081, - vhpiProcessStmtK = 1082, - vhpiProtectedTypeK = 1083, - vhpiProtectedTypeBodyK = 1084, - vhpiProtectedTypeDeclK = 1085, - vhpiRealLiteralK = 1086, - vhpiRecordTypeDeclK = 1087, - vhpiReportStmtK = 1088, - vhpiReturnStmtK = 1089, - vhpiRootInstK = 1090, - vhpiSelectSigAssignStmtK = 1091, - vhpiSelectWaveformK = 1092, - vhpiSelectedNameK = 1093, - vhpiSigDeclK = 1094, - vhpiSigParamDeclK = 1095, - vhpiSimpAttrNameK = 1096, - vhpiSimpleSigAssignStmtK = 1097, - vhpiSliceNameK = 1098, - vhpiStringLiteralK = 1099, - vhpiSubpBodyK = 1100, - vhpiSubtypeDeclK = 1101, - vhpiSubtypeIndicK = 1102, /* DEPRECATED */ - vhpiToolK = 1103, - vhpiTransactionK = 1104, - vhpiTypeConvK = 1105, - vhpiUnaryExprK = 1106, /* DEPRECATED */ - vhpiUnitDeclK = 1107, - vhpiUserAttrNameK = 1108, - vhpiVarAssignStmtK = 1109, - vhpiVarDeclK = 1110, - vhpiVarParamDeclK = 1111, - vhpiWaitStmtK = 1112, - vhpiWaveformElemK = 1113, - vhpiWhileLoopK = 1114, - vhpiQualifiedExprK = 1115, -#ifndef IUS - vhpiUseClauseK = 1116, -#else - vhpiUseClauseK = 1200, -#endif - -#ifdef VHPIEXTEND_CLASSES - VHPIEXTEND_CLASSES -#endif - /* private vendor extensions */ - vhpiVerilog = 1117, - vhpiEdifUnit = 1118, - vhpiCollectionK = 1119, - vhpiVHDL = 1120, - vhpiSystemC = 1121 -} vhpiClassKindT; - -/************** methods used to traverse 1 to 1 relationships *******************/ -typedef enum -{ - vhpiAbstractLiteral = 1301, - vhpiActual = 1302, - vhpiAll = 1303, - vhpiAttrDecl = 1304, - vhpiAttrSpec = 1305, - vhpiBaseType = 1306, - vhpiBaseUnit = 1307, - vhpiBasicSignal = 1308, - vhpiBlockConfig = 1309, - vhpiCaseExpr = 1310, - vhpiCondExpr = 1311, - vhpiConfigDecl = 1312, - vhpiConfigSpec = 1313, - vhpiConstraint = 1314, - vhpiContributor = 1315, - vhpiCurCallback = 1316, - vhpiCurEqProcess = 1317, - vhpiCurStackFrame = 1318, - vhpiDerefObj = 1319, - vhpiDecl = 1320, - vhpiDesignUnit = 1321, - vhpiDownStack = 1322, - vhpiElemSubtype = 1323, /* DEPRECATED */ - vhpiEntityAspect = 1324, - vhpiEntityDecl = 1325, - vhpiEqProcessStmt = 1326, - vhpiExpr = 1327, - vhpiFormal = 1328, - vhpiFuncDecl = 1329, - vhpiGroupTempDecl = 1330, - vhpiGuardExpr = 1331, - vhpiGuardSig = 1332, - vhpiImmRegion = 1333, - vhpiInPort = 1334, - vhpiInitExpr = 1335, - vhpiIterScheme = 1336, - vhpiLeftExpr = 1337, - vhpiLexicalScope = 1338, - vhpiLhsExpr = 1339, - vhpiLocal = 1340, - vhpiLogicalExpr = 1341, - vhpiName = 1342, - vhpiOperator = 1343, - vhpiOthers = 1344, - vhpiOutPort = 1345, - vhpiParamDecl = 1346, - vhpiParamExpr = 1347, - vhpiParent = 1348, - vhpiPhysLiteral = 1349, - vhpiPrefix = 1350, - vhpiPrimaryUnit = 1351, - vhpiProtectedTypeBody = 1352, - vhpiProtectedTypeDecl = 1353, - vhpiRejectTime = 1354, - vhpiReportExpr = 1355, - vhpiResolFunc = 1356, - vhpiReturnExpr = 1357, - vhpiReturnTypeMark = 1358, /* DEPRECATED */ - vhpiRhsExpr = 1359, - vhpiRightExpr = 1360, - vhpiRootInst = 1361, - vhpiSelectExpr = 1362, - vhpiSeverityExpr = 1363, - vhpiSimpleName = 1364, - vhpiSubpBody = 1365, - vhpiSubpDecl = 1366, - vhpiSubtype = 1367, /* DEPRECATED */ - vhpiSuffix = 1368, - vhpiTimeExpr = 1369, - vhpiTimeOutExpr = 1370, - vhpiTool = 1371, - vhpiType = 1372, - vhpiTypeMark = 1373, /* DEPRECATED */ - vhpiTypespec , - vhpiUnitDecl = 1374, - vhpiUpStack = 1375, - vhpiUpperRegion = 1376, - vhpiUse = 1377, - vhpiValExpr = 1378, - vhpiValSubtype = 1379, /* DEPRECATED */ - vhpiElemType = 1380, - vhpiFirstNamedType = 1381, - vhpiReturnType = 1382, - vhpiValType = 1383, - vhpiCurRegion = 1384 - -#ifdef VHPIEXTEND_ONE_METHODS - VHPIEXTEND_ONE_METHODS -#endif -} vhpiOneToOneT; - -/************** methods used to traverse 1 to many relationships *******************/ -typedef enum -{ - vhpiAliasDecls = 1501, - vhpiArgvs = 1502, - vhpiAttrDecls = 1503, - vhpiAttrSpecs = 1504, - vhpiBasicSignals = 1505, - vhpiBlockStmts = 1506, - vhpiBranchs = 1507, - /* 1508 */ - vhpiChoices = 1509, - vhpiCompInstStmts = 1510, - vhpiCondExprs = 1511, - vhpiCondWaveforms = 1512, - vhpiConfigItems = 1513, - vhpiConfigSpecs = 1514, - vhpiConstDecls = 1515, - vhpiConstraints = 1516, - vhpiContributors = 1517, - /* 1518 */ - vhpiDecls = 1519, - vhpiDepUnits = 1520, - vhpiDesignUnits = 1521, - vhpiDrivenSigs = 1522, - vhpiDrivers = 1523, - vhpiElemAssocs = 1524, - vhpiEntityClassEntrys = 1525, - vhpiEntityDesignators = 1526, - vhpiEnumLiterals = 1527, - vhpiForeignfs = 1528, - vhpiGenericAssocs = 1529, - vhpiGenericDecls = 1530, - vhpiIndexExprs = 1531, - vhpiIndexedNames = 1532, - vhpiInternalRegions = 1533, - vhpiMembers = 1534, - vhpiPackInsts = 1535, - vhpiParamAssocs = 1536, - vhpiParamDecls = 1537, - vhpiPortAssocs = 1538, - vhpiPortDecls = 1539, - vhpiRecordElems = 1540, - vhpiSelectWaveforms = 1541, - vhpiSelectedNames = 1542, - vhpiSensitivitys = 1543, - vhpiSeqStmts = 1544, - vhpiSigAttrs = 1545, - vhpiSigDecls = 1546, - vhpiSigNames = 1547, - vhpiSignals = 1548, - vhpiSpecNames = 1549, - vhpiSpecs = 1550, - vhpiStmts = 1551, - vhpiTransactions = 1552, - vhpiTypeMarks = 1553, /* DEPRECATED */ - vhpiUnitDecls = 1554, - vhpiUses = 1555, - vhpiVarDecls = 1556, - vhpiWaveformElems = 1557, - vhpiLibraryDecls = 1558, - vhpiLocalLoads = 1559, - vhpiOptimizedLoads = 1560, - vhpiTypes = 1561, -#ifndef IUS - vhpiUseClauses = 1562, -#else - vhpiUseClauses = 1650, -#endif - -#ifdef VHPIEXTEND_MANY_METHODS - VHPIEXTEND_MANY_METHODS -#endif - vhpiCallbacks = 1563, - vhpiCurRegions = 1564 -} vhpiOneToManyT; - -/****************** PROPERTIES *******************/ -/******* INTEGER or BOOLEAN PROPERTIES **********/ -typedef enum -{ - vhpiAccessP = 1001, - vhpiArgcP = 1002, - vhpiAttrKindP = 1003, - vhpiBaseIndexP = 1004, - vhpiBeginLineNoP = 1005, - vhpiEndLineNoP = 1006, - vhpiEntityClassP = 1007, - vhpiForeignKindP = 1008, - vhpiFrameLevelP = 1009, - vhpiGenerateIndexP = 1010, - vhpiIntValP = 1011, - vhpiIsAnonymousP = 1012, - vhpiIsBasicP = 1013, - vhpiIsCompositeP = 1014, - vhpiIsDefaultP = 1015, - vhpiIsDeferredP = 1016, - vhpiIsDiscreteP = 1017, - vhpiIsForcedP = 1018, - vhpiIsForeignP = 1019, - vhpiIsGuardedP = 1020, - vhpiIsImplicitDeclP = 1021, - vhpiIsInvalidP = 1022, /* DEPRECATED */ - vhpiIsLocalP = 1023, - vhpiIsNamedP = 1024, - vhpiIsNullP = 1025, - vhpiIsOpenP = 1026, - vhpiIsPLIP = 1027, - vhpiIsPassiveP = 1028, - vhpiIsPostponedP = 1029, - vhpiIsProtectedTypeP = 1030, - vhpiIsPureP = 1031, - vhpiIsResolvedP = 1032, - vhpiIsScalarP = 1033, - vhpiIsSeqStmtP = 1034, - vhpiIsSharedP = 1035, - vhpiIsTransportP = 1036, - vhpiIsUnaffectedP = 1037, - vhpiIsUnconstrainedP = 1038, - vhpiIsUninstantiatedP = 1039, - vhpiIsUpP = 1040, - vhpiIsVitalP = 1041, - vhpiIteratorTypeP = 1042, - vhpiKindP = 1043, - vhpiLeftBoundP = 1044, - vhpiLevelP = 1045, /* DEPRECATED */ - vhpiLineNoP = 1046, - vhpiLineOffsetP = 1047, - vhpiLoopIndexP = 1048, - vhpiModeP = 1049, - vhpiNumDimensionsP = 1050, - vhpiNumFieldsP = 1051, /* DEPRECATED */ - vhpiNumGensP = 1052, - vhpiNumLiteralsP = 1053, - vhpiNumMembersP = 1054, - vhpiNumParamsP = 1055, - vhpiNumPortsP = 1056, - vhpiOpenModeP = 1057, - vhpiPhaseP = 1058, - vhpiPositionP = 1059, - vhpiPredefAttrP = 1060, - /* 1061 */ - vhpiReasonP = 1062, - vhpiRightBoundP = 1063, - vhpiSigKindP = 1064, - vhpiSizeP = 1065, - vhpiStartLineNoP = 1066, - vhpiStateP = 1067, - vhpiStaticnessP = 1068, - vhpiVHDLversionP = 1069, - vhpiIdP = 1070, - vhpiCapabilitiesP = 1071, - -#ifdef VHPIEXTEND_INT_PROPERTIES - VHPIEXTEND_INT_PROPERTIES -#endif - vhpiIsStdLogicP = 1072, - vhpiIsStdULogicP = 1073, - vhpiIsStdLogicVectorP = 1074, - vhpiIsStdULogicVectorP = 1075, -# - vhpiLanguageP = 1200 -} vhpiIntPropertyT; - -/******* STRING PROPERTIES **********/ -typedef enum -{ - vhpiCaseNameP = 1301, - vhpiCompNameP = 1302, - vhpiDefNameP = 1303, - vhpiFileNameP = 1304, - vhpiFullCaseNameP = 1305, - vhpiFullNameP = 1306, - vhpiKindStrP = 1307, - vhpiLabelNameP = 1308, - vhpiLibLogicalNameP = 1309, - vhpiLibPhysicalNameP = 1310, - vhpiLogicalNameP = 1311, - vhpiLoopLabelNameP = 1312, - vhpiNameP = 1313, - vhpiOpNameP = 1314, - vhpiStrValP = 1315, - vhpiToolVersionP = 1316, - vhpiUnitNameP = 1317, - vhpiSaveRestartLocationP = 1318, - - /* Cadence IUS/Xcelium */ - vhpiFullVlogNameP = 1500, /* Full path name (VHDL names are Verilogized) */ - vhpiFullVHDLNameP = 1501, /* Full path name (Verilog names are vhdlized) */ - vhpiFullLSNameP = 1502, /* Full path name (Upper case) using ':' and '.' separators */ - vhpiFullLSCaseNameP = 1503 /* Full path name (VHDL case sensitive) */ - -#ifdef VHPIEXTEND_STR_PROPERTIES - VHPIEXTEND_STR_PROPERTIES -#endif -} vhpiStrPropertyT; - -/******* REAL PROPERTIES **********/ -typedef enum -{ - vhpiFloatLeftBoundP = 1601, - vhpiFloatRightBoundP = 1602, - vhpiRealValP = 1603 - -#ifdef VHPIEXTEND_REAL_PROPERTIES - VHPIEXTEND_REAL_PROPERTIES -#endif -} vhpiRealPropertyT; - -/******* PHYSICAL PROPERTIES **********/ -typedef enum -{ - vhpiPhysLeftBoundP = 1651, - vhpiPhysPositionP = 1652, - vhpiPhysRightBoundP = 1653, - vhpiPhysValP = 1654, - vhpiPrecisionP = 1655, /* DEPRECATED */ - vhpiSimTimeUnitP = 1656, /* DEPRECATED */ - vhpiResolutionLimitP = 1657 - -#ifdef VHPIEXTEND_PHYS_PROPERTIES - VHPIEXTEND_PHYS_PROPERTIES -#endif -} vhpiPhysPropertyT; - -/******************* PROPERTY VALUES ************************/ - -/* vhpiCapabilitiesP */ -typedef enum -{ - vhpiProvidesHierarchy = 1, - vhpiProvidesStaticAccess = 2, - vhpiProvidesConnectivity = 4, - vhpiProvidesPostAnalysis = 8, - vhpiProvidesForeignModel = 16, - vhpiProvidesAdvancedForeignModel = 32, - vhpiProvidesSaveRestart = 64, - vhpiProvidesReset = 128, - vhpiProvidesDebugRuntime = 256, - vhpiProvidesAdvancedDebugRuntime = 512, - vhpiProvidesDynamicElab = 1024 -} vhpiCapabibilityT; - - -/* vhpiOpenModeP */ -typedef enum -{ - vhpiInOpen = 1001, - vhpiOutOpen = 1002, - vhpiReadOpen = 1003, - vhpiWriteOpen = 1004, - vhpiAppendOpen = 1005 -} vhpiOpenModeT; - -/* vhpiModeP */ -typedef enum -{ - vhpiInMode = 1001, - vhpiOutMode = 1002, - vhpiInoutMode = 1003, - vhpiBufferMode = 1004, - vhpiLinkageMode = 1005 -} vhpiModeT; - -/* vhpiSigKindP */ -typedef enum -{ - vhpiRegister = 1001, - vhpiBus = 1002, - vhpiNormal = 1003 -} vhpiSigKindT; - -/* vhpiStaticnessP */ -typedef enum -{ - vhpiLocallyStatic = 1001, - vhpiGloballyStatic = 1002, - vhpiDynamic = 1003 -} vhpiStaticnessT; - -/* vhpiPredefAttrP */ -typedef enum -{ - vhpiActivePA = 1001, - vhpiAscendingPA = 1002, - vhpiBasePA = 1003, - vhpiDelayedPA = 1004, - vhpiDrivingPA = 1005, - vhpiDriving_valuePA = 1006, - vhpiEventPA = 1007, - vhpiHighPA = 1008, - vhpiImagePA = 1009, - vhpiInstance_namePA = 1010, - vhpiLast_activePA = 1011, - vhpiLast_eventPA = 1012, - vhpiLast_valuePA = 1013, - vhpiLeftPA = 1014, - vhpiLeftofPA = 1015, - vhpiLengthPA = 1016, - vhpiLowPA = 1017, - vhpiPath_namePA = 1018, - vhpiPosPA = 1019, - vhpiPredPA = 1020, - vhpiQuietPA = 1021, - vhpiRangePA = 1022, - vhpiReverse_rangePA = 1023, - vhpiRightPA = 1024, - vhpiRightofPA = 1025, - vhpiSimple_namePA = 1026, - vhpiStablePA = 1027, - vhpiSuccPA = 1028, - vhpiTransactionPA = 1029, - vhpiValPA = 1030, - vhpiValuePA = 1031 -} vhpiPredefAttrT; - -/* vhpiAttrKindP */ -typedef enum -{ - vhpiFunctionAK = 1, - vhpiRangeAK = 2, - vhpiSignalAK = 3, - vhpiTypeAK = 4, - vhpiValueAK = 5 - -#ifdef VHPIEXTEND_INT_ATTR - VHPI_EXTEND_INT_ATTR -#endif -} vhpiAttrKindT; - -/* vhpiEntityClassP */ -typedef enum -{ - vhpiEntityEC = 1001, - vhpiArchitectureEC = 1002, - vhpiConfigurationEC = 1003, - vhpiProcedureEC = 1004, - vhpiFunctionEC = 1005, - vhpiPackageEC = 1006, - vhpiTypeEC = 1007, - vhpiSubtypeEC = 1008, - vhpiConstantEC = 1009, - vhpiSignalEC = 1010, - vhpiVariableEC = 1011, - vhpiComponentEC = 1012, - vhpiLabelEC = 1013, - vhpiLiteralEC = 1014, - vhpiUnitsEC = 1015, - vhpiFileEC = 1016, - vhpiGroupEC = 1017 -} vhpiEntityClassT; - -/* vhpiAccessP */ -typedef enum -{ - vhpiRead = 1, - vhpiWrite = 2, - vhpiConnectivity = 4, - vhpiNoAccess = 8 -} vhpiAccessT; - -/* value for vhpiStateP property for callbacks */ -typedef enum -{ - vhpiEnable, - vhpiDisable, - vhpiMature /* callback has occurred */ -} vhpiStateT; - -/* enumeration type for vhpiCompInstKindP property */ -typedef enum -{ - vhpiDirect, - vhpiComp, - vhpiConfig -} vhpiCompInstKindT; - - -/* the following values are used only for the - vhpiResolutionLimitP property and for setting the unit field of the value - structure; they represent the physical position of a given - VHDL time unit */ -/* time unit physical position values {high, low} */ -PLI_VEXTERN PLI_DLLISPEC const vhpiPhysT vhpiFS; -PLI_VEXTERN PLI_DLLISPEC const vhpiPhysT vhpiPS; -PLI_VEXTERN PLI_DLLISPEC const vhpiPhysT vhpiNS; -PLI_VEXTERN PLI_DLLISPEC const vhpiPhysT vhpiUS; -PLI_VEXTERN PLI_DLLISPEC const vhpiPhysT vhpiMS; -PLI_VEXTERN PLI_DLLISPEC const vhpiPhysT vhpiS; -PLI_VEXTERN PLI_DLLISPEC const vhpiPhysT vhpiMN; -PLI_VEXTERN PLI_DLLISPEC const vhpiPhysT vhpiHR; - -/* IEEE std_logic values */ -#define vhpiU 0 /* uninitialized */ -#define vhpiX 1 /* unknown */ -#define vhpi0 2 /* forcing 0 */ -#define vhpi1 3 /* forcing 1 */ -#define vhpiZ 4 /* high impedance */ -#define vhpiW 5 /* weak unknown */ -#define vhpiL 6 /* weak 0 */ -#define vhpiH 7 /* weak 1 */ -#define vhpiDontCare 8 /* don't care */ - -/* IEEE std bit values */ -#define vhpibit0 0 /* bit 0 */ -#define vhpibit1 1 /* bit 1 */ - -/* IEEE std boolean values */ -#define vhpiFalse 0 /* false */ -#define vhpiTrue 1 /* true */ - -/************** vhpiPhaseP property values *************/ -typedef enum -{ - vhpiRegistrationPhase = 1, - vhpiAnalysisPhase = 2, - vhpiElaborationPhase = 3, - vhpiInitializationPhase = 4, - vhpiSimulationPhase = 5, - vhpiTerminationPhase = 6, - vhpiSavePhase = 7, - vhpiRestartPhase = 8, - vhpiResetPhase = 9 -} vhpiPhaseT ; - -/**************** PLI error information structure ****************/ - -typedef enum -{ - vhpiNote = 1, - vhpiWarning = 2, - vhpiError = 3, - vhpiFailure = 6, - vhpiSystem = 4, - vhpiInternal = 5 -} vhpiSeverityT; - -typedef struct vhpiErrorInfoS -{ - vhpiSeverityT severity; - char *message; - char *str; - char *file; /* Name of the VHDL file where the VHPI error originated */ - int32_t line; /* Line number in the VHDL file */ -} vhpiErrorInfoT; - -/********************* callback structures ************************/ -/* callback user data structure */ - -typedef struct vhpiCbDataS -{ - int32_t reason; /* callback reason */ - void (*cb_rtn) (const struct vhpiCbDataS *); /* call routine */ - vhpiHandleT obj; /* trigger object */ - vhpiTimeT *time; /* callback time */ - vhpiValueT *value; /* trigger object value */ - void *user_data; /* pointer to user data to be passed to the callback function */ -} vhpiCbDataT; - -/**************************** CALLBACK REASONS ****************************/ -/*************************** Simulation object related ***********************/ -/* These are repetitive callbacks */ -#define vhpiCbValueChange 1001 -#define vhpiCbForce 1002 -#define vhpiCbRelease 1003 -#define vhpiCbTransaction 1004 /* optional callback reason */ - -/****************************** Statement related ****************************/ -/* These are repetitive callbacks */ -#define vhpiCbStmt 1005 -#define vhpiCbResume 1006 -#define vhpiCbSuspend 1007 -#define vhpiCbStartOfSubpCall 1008 -#define vhpiCbEndOfSubpCall 1009 - -/****************************** Time related ******************************/ -/* the Rep callback reasons are the repeated versions of the callbacks */ - -#define vhpiCbAfterDelay 1010 -#define vhpiCbRepAfterDelay 1011 - -/*************************** Simulation cycle phase related *****************/ -#define vhpiCbNextTimeStep 1012 -#define vhpiCbRepNextTimeStep 1013 -#define vhpiCbStartOfNextCycle 1014 -#define vhpiCbRepStartOfNextCycle 1015 -#define vhpiCbStartOfProcesses 1016 -#define vhpiCbRepStartOfProcesses 1017 -#define vhpiCbEndOfProcesses 1018 -#define vhpiCbRepEndOfProcesses 1019 -#define vhpiCbLastKnownDeltaCycle 1020 -#define vhpiCbRepLastKnownDeltaCycle 1021 -#define vhpiCbStartOfPostponed 1022 -#define vhpiCbRepStartOfPostponed 1023 -#define vhpiCbEndOfTimeStep 1024 -#define vhpiCbRepEndOfTimeStep 1025 - -/***************************** Action related *****************************/ -/* these are one time callback unless otherwise noted */ -#define vhpiCbStartOfTool 1026 -#define vhpiCbEndOfTool 1027 -#define vhpiCbStartOfAnalysis 1028 -#define vhpiCbEndOfAnalysis 1029 -#define vhpiCbStartOfElaboration 1030 -#define vhpiCbEndOfElaboration 1031 -#define vhpiCbStartOfInitialization 1032 -#define vhpiCbEndOfInitialization 1033 -#define vhpiCbStartOfSimulation 1034 -#define vhpiCbEndOfSimulation 1035 -#define vhpiCbQuiescense 1036 /* repetitive */ -#define vhpiCbPLIError 1037 /* repetitive */ -#define vhpiCbStartOfSave 1038 -#define vhpiCbEndOfSave 1039 -#define vhpiCbStartOfRestart 1040 -#define vhpiCbEndOfRestart 1041 -#define vhpiCbStartOfReset 1042 -#define vhpiCbEndOfReset 1043 -#define vhpiCbEnterInteractive 1044 /* repetitive */ -#define vhpiCbExitInteractive 1045 /* repetitive */ -#define vhpiCbSigInterrupt 1046 /* repetitive */ - -/* Foreign model callbacks */ -#define vhpiCbTimeOut 1047 /* non repetitive */ -#define vhpiCbRepTimeOut 1048 /* repetitive */ -#define vhpiCbSensitivity 1049 /* repetitive */ - -/**************************** CALLBACK FLAGS ******************************/ -#define vhpiReturnCb 0x00000001 -#define vhpiDisableCb 0x00000010 - -/************** vhpiAutomaticRestoreP property values *************/ -typedef enum -{ - vhpiRestoreAll = 1, - vhpiRestoreUserData = 2, - vhpiRestoreHandles = 4, - vhpiRestoreCallbacks = 8 -} vhpiAutomaticRestoreT; - - -/******************** FUNCTION DECLARATIONS *********************/ - -XXTERN int vhpi_assert (vhpiSeverityT severity, - const char *formatmsg, - ...); - -/* callback related */ - -XXTERN vhpiHandleT vhpi_register_cb (vhpiCbDataT *cb_data_p, - int32_t flags); - -XXTERN int vhpi_remove_cb (vhpiHandleT cb_obj); - -XXTERN int vhpi_disable_cb (vhpiHandleT cb_obj); - -XXTERN int vhpi_enable_cb (vhpiHandleT cb_obj); - -XXTERN int vhpi_get_cb_info (vhpiHandleT object, - vhpiCbDataT *cb_data_p); - -/* utilities for sensitivity-set bitmaps */ -/* The replacement text for these macros is implementation defined */ -/* The behavior is specified in G.1 */ -XXTERN int vhpi_sens_first (vhpiValueT *sens); - -XXTERN int vhpi_sens_zero (vhpiValueT *sens); - -XXTERN int vhpi_sens_clr (int obj, - vhpiValueT *sens); - -XXTERN int vhpi_sens_set (int obj, - vhpiValueT *sens); - -XXTERN int vhpi_sens_isset (int obj, - vhpiValueT *sens); - -#define VHPI_SENS_ZERO(sens) vhpi_sens_zero(sens) -#define VHPI_SENS_SET(obj, sens) vhpi_sens_set(obj, sens) -#define VHPI_SENS_CLR(obj, sens) vhpi_sens_clr(obj, sens) -#define VHPI_SENS_ISSET(obj, sens) vhpi_sens_isset(obj, sens) -#define VHPI_SENS_FIRST(sens) vhpi_sens_first(sens) - -/* for obtaining handles */ - -XXTERN vhpiHandleT vhpi_handle_by_name (const char *name, - vhpiHandleT scope); - -XXTERN vhpiHandleT vhpi_handle_by_index (vhpiOneToManyT itRel, - vhpiHandleT parent, - int32_t indx); - -/* for traversing relationships */ - -XXTERN vhpiHandleT vhpi_handle (vhpiOneToOneT type, - vhpiHandleT referenceHandle); - -XXTERN vhpiHandleT vhpi_iterator (vhpiOneToManyT type, - vhpiHandleT referenceHandle); - -XXTERN vhpiHandleT vhpi_scan (vhpiHandleT iterator); - -/* for processsing properties */ - -XXTERN vhpiIntT vhpi_get (vhpiIntPropertyT property, - vhpiHandleT object); - -XXTERN const vhpiCharT * vhpi_get_str (vhpiStrPropertyT property, - vhpiHandleT object); - -XXTERN vhpiRealT vhpi_get_real (vhpiRealPropertyT property, - vhpiHandleT object); - -XXTERN vhpiPhysT vhpi_get_phys (vhpiPhysPropertyT property, - vhpiHandleT object); - -/* for access to protected types */ - -typedef int (*vhpiUserFctT)(void); - -XXTERN int vhpi_protected_call (vhpiHandleT varHdl, - vhpiUserFctT userFct, - void *userData); - -/* value processing */ - -/* vhpi_put_value flags */ -typedef enum -{ - vhpiDeposit, - vhpiDepositPropagate, - vhpiForce, - vhpiForcePropagate, - vhpiRelease, - vhpiSizeConstraint -} vhpiPutValueModeT; - -typedef enum -{ - vhpiInertial, - vhpiTransport -} vhpiDelayModeT; - -XXTERN int vhpi_get_value (vhpiHandleT expr, - vhpiValueT *value_p); - -XXTERN int vhpi_put_value (vhpiHandleT object, - vhpiValueT *value_p, - vhpiPutValueModeT flags); - -XXTERN int vhpi_schedule_transaction (vhpiHandleT drivHdl, - vhpiValueT *value_p, - uint32_t numValues, - vhpiTimeT *delayp, - vhpiDelayModeT delayMode, - vhpiTimeT *pulseRejp); - -XXTERN int vhpi_format_value (const vhpiValueT *in_value_p, - vhpiValueT *out_value_p); - -/* time processing */ - -XXTERN void vhpi_get_time (vhpiTimeT *time_p, - long *cycles); - -#define vhpiNoActivity -1 - -XXTERN int vhpi_get_next_time (vhpiTimeT *time_p); - -/* simulation control */ - -typedef enum -{ - vhpiStop = 0, - vhpiFinish = 1, - vhpiReset = 2 - -#ifdef VHPIEXTEND_CONTROL - VHPIEXTEND_CONTROL -#endif -} vhpiSimControlT; - -XXTERN int vhpi_control (vhpiSimControlT command, - ...); - -XXTERN int vhpi_sim_control (vhpiSimControlT command); /* for compatibility reasons */ - -/* I/O routine */ - -XXTERN int vhpi_printf (const char *format, - ...); - -XXTERN int vhpi_vprintf (const char *format, - va_list args); - -/* utilities to print VHDL strings */ - -XXTERN int vhpi_is_printable(char ch); - - -/* utility routines */ - -XXTERN int vhpi_compare_handles (vhpiHandleT handle1, - vhpiHandleT handle2); - -XXTERN int vhpi_check_error (vhpiErrorInfoT *error_info_p); - -XXTERN int vhpi_release_handle (vhpiHandleT object); - -/* creation functions */ - -XXTERN vhpiHandleT vhpi_create (vhpiClassKindT kind, - vhpiHandleT handle1, - vhpiHandleT handle2); - -/* Foreign model data structures and functions */ - -typedef enum -{ - vhpiArchF = 1, - vhpiArchFK = 1, /* for compatibility reasons */ - vhpiFuncF = 2, - vhpiFuncFK = 2, /* for compatibility reasons */ - vhpiProcF = 3, - vhpiProcFK = 3, /* for compatibility reasons */ - vhpiLibF = 4, - vhpiAppF = 5 -} vhpiForeignT; - -typedef struct vhpiForeignDataS -{ - vhpiForeignT kind; - char * libraryName; - char * modelName; - void (*elabf)(const struct vhpiCbDataS *cb_data_p); - void (*execf)(const struct vhpiCbDataS *cb_data_p); -} vhpiForeignDataT; - -XXTERN vhpiHandleT vhpi_register_foreignf (vhpiForeignDataT *foreignDatap); - -XXTERN int vhpi_get_foreignf_info (vhpiHandleT hdl, - vhpiForeignDataT *foreignDatap); - -/* vhpi_get_foreign_info is DEPRECATED */ -XXTERN int vhpi_get_foreign_info (vhpiHandleT hdl, - vhpiForeignDataT *foreignDatap); - -/* for saving and restoring foreign models data */ - -XXTERN size_t vhpi_get_data (int32_t id, - void *dataLoc, - size_t numBytes); - -XXTERN size_t vhpi_put_data (int32_t id, - void *dataLoc, - size_t numBytes); - -#ifdef VHPIEXTEND_FUNCTIONS - VHPIEXTEND_FUNCTIONS -#endif - -/* Visual Elite integration - Cause & Effect support - Function returns instance handle for specified signal - Warning!!! Function each time allocates new handle for returned instance, so you should free it !!! */ -XXTERN vhpiHandleT vhpi_get_cause_instance (vhpiHandleT sigHandle); - -/* Function returns source point number to code which made event on specified signal */ -XXTERN int vhpi_get_cause (vhpiHandleT sigHandle, - unsigned int** p2MagicNumbersBuffer); - -/* Function returns info about line nr and instance hierarchy path for specified source point nr - and instance handle - returns: - For invalid source point: *pnLineNr == -1 - For insufficient buffer size, returns required buffer size - For invalid args function returns -1 - If all is OK, function returns 0 */ -XXTERN int vhpi_get_cause_info (const unsigned int** pn2MagicNumbers, - int nBufLen, - char* pszHierScopeBuf, - int nFilePathBufLen, - char* pszSourceFilePathBuf, - int* pnLineNr); - -/* -* vhpi_value_size() -* -* Description: -* The vhpi_value_size() function lets you query the size in bytes -* required for a buffer to store the value of the specified object handle -* in the specified format. Use this function to allocate memory for the -* appropriate field of the value structure before using -* vphi_get_value() or vhpi_put_value(). -* -* Returns the size in bytes required for a buffer to store the value of -* the specified object handle in the specified format. -* -* Syntax: vhpi_value_size(vhpiHandleT objHdl, vhpiFormatT format) -* Returns: vhpiIntT Size in bytes -* -* Arguments: -* VhpiHandleT objHdl Object handle whose value is needed. -* VhpiFormatT format Format in which the value needs to be represented -* -*******************************************************************************/ - -XXTERN vhpiIntT vhpi_value_size (vhpiHandleT objHdl, - vhpiFormatT format); - -/**************************** Typedef for VHPI bootstrap functions - ****************************/ - -typedef void (*vhpiBootstrapFctT)(void); - - -#undef PLI_EXTERN -#undef PLI_VEXTERN - -#ifdef VHPI_USER_DEFINED_DLLISPEC - #undef VHPI_USER_DEFINED_DLLISPEC - #undef PLI_DLLISPEC -#endif -#ifdef VHPI_USER_DEFINED_DLLESPEC - #undef VHPI_USER_DEFINED_DLLESPEC - #undef PLI_DLLESPEC -#endif - -#ifdef PLI_PROTOTYPES - #undef PLI_PROTOTYPES - #undef XXTERN - #undef EETERN -#endif - -#ifdef __cplusplus -} -#endif - -#pragma pack() - -#endif /* VHPI_USER_H */ diff --git a/cocotb/share/include/vhpi_user_ext.h b/cocotb/share/include/vhpi_user_ext.h deleted file mode 100644 index 97532d7c..00000000 --- a/cocotb/share/include/vhpi_user_ext.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright cocotb contributors -// Licensed under the Revised BSD License, see LICENSE for details. -// SPDX-License-Identifier: BSD-3-Clause - -/* extensions to vhpi_user.h */ - -#ifndef VHPI_USER_EXT_H -#define VHPI_USER_EXT_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* arguments for vhpiStop or vhpiFinish */ -typedef enum -{ - vhpiDiagNone = 0, /* prints nothing */ - vhpiDiagTimeLoc = 1, /* prints simulation time and location */ - vhpiDiagTimeLocCPUMem = 2 /* prints simulation time, location and statistics about CPU and memory usage */ -} vhpiDiagT; - -#ifdef __cplusplus -} -#endif - -#endif /* VPI_USER_EXT_H */ diff --git a/cocotb/share/include/vpi_user.h b/cocotb/share/include/vpi_user.h deleted file mode 100644 index 00b65d7b..00000000 --- a/cocotb/share/include/vpi_user.h +++ /dev/null @@ -1,1005 +0,0 @@ -/******************************************************************************* -* vpi_user.h -* -* IEEE Std 1800 Programming Language Interface (PLI) -* -* This file contains the constant definitions, structure definitions, and -* routine declarations used by the SystemVerilog Verification Procedural -* Interface (VPI) access routines. -* -******************************************************************************/ - -/******************************************************************************* - * NOTE: the constant values 1 through 299 are reserved for use in this - * vpi_user.h file. - ******************************************************************************/ - -#ifndef VPI_USER_H -#define VPI_USER_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/*----------------------------------------------------------------------------*/ -/*----------------------------- Portability Help -----------------------------*/ -/*----------------------------------------------------------------------------*/ - -#if defined (_MSC_VER) -typedef unsigned __int64 uint64_t; -typedef unsigned __int32 uint32_t; -typedef unsigned __int8 uint8_t; -typedef signed __int64 int64_t; -typedef signed __int32 int32_t; -typedef signed __int8 int8_t; -#elif defined(__MINGW32__) -#include -#elif defined(__linux) -#include -#else -#include -#endif -/* Sized variables */ - - -#ifndef SVPI_TYPES -#define SVPI_TYPES -typedef int64_t PLI_INT64; -typedef uint64_t PLI_UINT64; -#endif - -#ifndef PLI_TYPES -#define PLI_TYPES -typedef int PLI_INT32; -typedef unsigned int PLI_UINT32; -typedef short PLI_INT16; -typedef unsigned short PLI_UINT16; -typedef char PLI_BYTE8; -typedef unsigned char PLI_UBYTE8; -#endif - -/* Use to import a symbol */ - -#if defined(WIN32) || defined(WIN64) -#ifndef PLI_DLLISPEC -#define PLI_DLLISPEC __declspec(dllimport) -#define VPI_USER_DEFINED_DLLISPEC 1 -#endif -#else -#ifndef PLI_DLLISPEC -#define PLI_DLLISPEC -#endif -#endif - -/* Use to export a symbol */ - -#if defined(WIN32) || defined(WIN64) -#ifndef PLI_DLLESPEC -#define PLI_DLLESPEC __declspec(dllexport) -#define VPI_USER_DEFINED_DLLESPEC 1 -#endif -#else -#ifndef PLI_DLLESPEC -#define PLI_DLLESPEC -#endif -#endif - -/* Use to mark a function as external */ - -#ifndef PLI_EXTERN -#define PLI_EXTERN -#endif - -/* Use to mark a variable as external */ - -#ifndef PLI_VEXTERN -#define PLI_VEXTERN extern -#endif - -#ifndef PLI_PROTOTYPES -#define PLI_PROTOTYPES -#define PROTO_PARAMS(params) params - -/* object is defined imported by the application */ - -#define XXTERN PLI_EXTERN PLI_DLLISPEC - -/* object is exported by the application */ - -#define EETERN PLI_EXTERN PLI_DLLESPEC -#endif - -/********************************** TYPEDEFS **********************************/ - -typedef PLI_UINT32 *vpiHandle; - -/******************************** OBJECT TYPES ********************************/ - -#define vpiAlways 1 /* always construct */ -#define vpiAssignStmt 2 /* quasi-continuous assignment */ -#define vpiAssignment 3 /* procedural assignment */ -#define vpiBegin 4 /* block statement */ -#define vpiCase 5 /* case statement */ -#define vpiCaseItem 6 /* case statement item */ -#define vpiConstant 7 /* numerical constant or literal string */ -#define vpiContAssign 8 /* continuous assignment */ -#define vpiDeassign 9 /* deassignment statement */ -#define vpiDefParam 10 /* defparam */ -#define vpiDelayControl 11 /* delay statement (e.g. #10) */ -#define vpiDisable 12 /* named block disable statement */ -#define vpiEventControl 13 /* wait on event, e.g. @e */ -#define vpiEventStmt 14 /* event trigger, e.g. ->e */ -#define vpiFor 15 /* for statement */ -#define vpiForce 16 /* force statement */ -#define vpiForever 17 /* forever statement */ -#define vpiFork 18 /* fork-join block */ -#define vpiFuncCall 19 /* HDL function call */ -#define vpiFunction 20 /* HDL function */ -#define vpiGate 21 /* primitive gate */ -#define vpiIf 22 /* if statement */ -#define vpiIfElse 23 /* if-else statement */ -#define vpiInitial 24 /* initial construct */ -#define vpiIntegerVar 25 /* integer variable */ -#define vpiInterModPath 26 /* intermodule wire delay */ -#define vpiIterator 27 /* iterator */ -#define vpiIODecl 28 /* input/output declaration */ -#define vpiMemory 29 /* behavioral memory */ -#define vpiMemoryWord 30 /* single word of memory */ -#define vpiModPath 31 /* module path for path delays */ -#define vpiModule 32 /* module instance */ -#define vpiNamedBegin 33 /* named block statement */ -#define vpiNamedEvent 34 /* event variable */ -#define vpiNamedFork 35 /* named fork-join block */ -#define vpiNet 36 /* scalar or vector net */ -#define vpiNetBit 37 /* bit of vector net */ -#define vpiNullStmt 38 /* a semicolon. Ie. #10 ; */ -#define vpiOperation 39 /* behavioral operation */ -#define vpiParamAssign 40 /* module parameter assignment */ -#define vpiParameter 41 /* module parameter */ -#define vpiPartSelect 42 /* part-select */ -#define vpiPathTerm 43 /* terminal of module path */ -#define vpiPort 44 /* module port */ -#define vpiPortBit 45 /* bit of vector module port */ -#define vpiPrimTerm 46 /* primitive terminal */ -#define vpiRealVar 47 /* real variable */ -#define vpiReg 48 /* scalar or vector reg */ -#define vpiRegBit 49 /* bit of vector reg */ -#define vpiRelease 50 /* release statement */ -#define vpiRepeat 51 /* repeat statement */ -#define vpiRepeatControl 52 /* repeat control in an assign stmt */ -#define vpiSchedEvent 53 /* vpi_put_value() event */ -#define vpiSpecParam 54 /* specparam */ -#define vpiSwitch 55 /* transistor switch */ -#define vpiSysFuncCall 56 /* system function call */ -#define vpiSysTaskCall 57 /* system task call */ -#define vpiTableEntry 58 /* UDP state table entry */ -#define vpiTask 59 /* HDL task */ -#define vpiTaskCall 60 /* HDL task call */ -#define vpiTchk 61 /* timing check */ -#define vpiTchkTerm 62 /* terminal of timing check */ -#define vpiTimeVar 63 /* time variable */ -#define vpiTimeQueue 64 /* simulation event queue */ -#define vpiUdp 65 /* user-defined primitive */ -#define vpiUdpDefn 66 /* UDP definition */ -#define vpiUserSystf 67 /* user defined system task or function */ -#define vpiVarSelect 68 /* variable array selection */ -#define vpiWait 69 /* wait statement */ -#define vpiWhile 70 /* while statement */ - -/********************** object types added with 1364-2001 *********************/ - -#define vpiAttribute 105 /* attribute of an object */ -#define vpiBitSelect 106 /* Bit-select of parameter, var select */ -#define vpiCallback 107 /* callback object */ -#define vpiDelayTerm 108 /* Delay term which is a load or driver */ -#define vpiDelayDevice 109 /* Delay object within a net */ -#define vpiFrame 110 /* reentrant task/func frame */ -#define vpiGateArray 111 /* gate instance array */ -#define vpiModuleArray 112 /* module instance array */ -#define vpiPrimitiveArray 113 /* vpiprimitiveArray type */ -#define vpiNetArray 114 /* multidimensional net */ -#define vpiRange 115 /* range declaration */ -#define vpiRegArray 116 /* multidimensional reg */ -#define vpiSwitchArray 117 /* switch instance array */ -#define vpiUdpArray 118 /* UDP instance array */ -#define vpiContAssignBit 128 /* Bit of a vector continuous assignment */ -#define vpiNamedEventArray 129 /* multidimensional named event */ - -/********************** object types added with 1364-2005 *********************/ - -#define vpiIndexedPartSelect 130 /* Indexed part-select object */ -#define vpiGenScopeArray 133 /* array of generated scopes */ -#define vpiGenScope 134 /* A generated scope */ -#define vpiGenVar 135 /* Object used to instantiate gen scopes */ - -/*********************************** METHODS **********************************/ -/**************** methods used to traverse 1 to 1 relationships ***************/ - -#define vpiCondition 71 /* condition expression */ -#define vpiDelay 72 /* net or gate delay */ -#define vpiElseStmt 73 /* else statement */ -#define vpiForIncStmt 74 /* increment statement in for loop */ -#define vpiForInitStmt 75 /* initialization statement in for loop */ -#define vpiHighConn 76 /* higher connection to port */ -#define vpiLhs 77 /* left-hand side of assignment */ -#define vpiIndex 78 /* index of var select, bit-select, etc. */ -#define vpiLeftRange 79 /* left range of vector or part-select */ -#define vpiLowConn 80 /* lower connection to port */ -#define vpiParent 81 /* parent object */ -#define vpiRhs 82 /* right-hand side of assignment */ -#define vpiRightRange 83 /* right range of vector or part-select */ -#define vpiScope 84 /* containing scope object */ -#define vpiSysTfCall 85 /* task function call */ -#define vpiTchkDataTerm 86 /* timing check data term */ -#define vpiTchkNotifier 87 /* timing check notifier */ -#define vpiTchkRefTerm 88 /* timing check reference term */ - -/************* methods used to traverse 1 to many relationships ***************/ - -#define vpiArgument 89 /* argument to (system) task/function */ -#define vpiBit 90 /* bit of vector net or port */ -#define vpiDriver 91 /* driver for a net */ -#define vpiInternalScope 92 /* internal scope in module */ -#define vpiLoad 93 /* load on net or reg */ -#define vpiModDataPathIn 94 /* data terminal of a module path */ -#define vpiModPathIn 95 /* Input terminal of a module path */ -#define vpiModPathOut 96 /* output terminal of a module path */ -#define vpiOperand 97 /* operand of expression */ -#define vpiPortInst 98 /* connected port instance */ -#define vpiProcess 99 /* process in module */ -#define vpiVariables 100 /* variables in module */ -#define vpiUse 101 /* usage */ - -/******** methods which can traverse 1 to 1, or 1 to many relationships *******/ - -#define vpiExpr 102 /* connected expression */ -#define vpiPrimitive 103 /* primitive (gate, switch, UDP) */ -#define vpiStmt 104 /* statement in process or task */ - -/************************ methods added with 1364-2001 ************************/ - -#define vpiActiveTimeFormat 119 /* active $timeformat() system task */ -#define vpiInTerm 120 /* To get to a delay device's drivers. */ -#define vpiInstanceArray 121 /* vpiInstance arrays */ -#define vpiLocalDriver 122 /* local drivers (within a module */ -#define vpiLocalLoad 123 /* local loads (within a module */ -#define vpiOutTerm 124 /* To get to a delay device's loads. */ -#define vpiPorts 125 /* Module port */ -#define vpiSimNet 126 /* simulated net after collapsing */ -#define vpiTaskFunc 127 /* HDL task or function */ - -/************************ methods added with 1364-2005 ************************/ - -#define vpiBaseExpr 131 /* Indexed part-select's base expression */ -#define vpiWidthExpr 132 /* Indexed part-select's width expression */ - -/************************ methods added with 1800-2009 ************************/ - -#define vpiAutomatics 136 /* Automatic variables of a frame */ - -/********************************* PROPERTIES *********************************/ -/************************** generic object properties *************************/ - -#define vpiUndefined -1 /* undefined property */ -#define vpiType 1 /* type of object */ -#define vpiName 2 /* local name of object */ -#define vpiFullName 3 /* full hierarchical name */ -#define vpiSize 4 /* size of gate, net, port, etc. */ -#define vpiFile 5 /* File name in which the object is used*/ -#define vpiLineNo 6 /* line number where the object is used */ - -/***************************** module properties ******************************/ - -#define vpiTopModule 7 /* top-level module (boolean) */ -#define vpiCellInstance 8 /* cell (boolean) */ -#define vpiDefName 9 /* module definition name */ -#define vpiProtected 10 /* source protected module (boolean) */ -#define vpiTimeUnit 11 /* module time unit */ -#define vpiTimePrecision 12 /* module time precision */ -#define vpiDefNetType 13 /* default net type */ -#define vpiUnconnDrive 14 /* unconnected port drive strength */ -#define vpiHighZ 1 /* No default drive given */ -#define vpiPull1 2 /* default pull1 drive */ -#define vpiPull0 3 /* default pull0 drive */ -#define vpiDefFile 15 /* File name where the module is defined*/ -#define vpiDefLineNo 16 /* line number for module definition */ -#define vpiDefDelayMode 47 /* Default delay mode for a module */ -#define vpiDelayModeNone 1 /* no delay mode specified */ -#define vpiDelayModePath 2 /* path delay mode */ -#define vpiDelayModeDistrib 3 /* distributed delay mode */ -#define vpiDelayModeUnit 4 /* unit delay mode */ -#define vpiDelayModeZero 5 /* zero delay mode */ -#define vpiDelayModeMTM 6 /* min:typ:max delay mode */ -#define vpiDefDecayTime 48 /* Default decay time for a module */ - -/*************************** port and net properties **************************/ - -#define vpiScalar 17 /* scalar (boolean) */ -#define vpiVector 18 /* vector (boolean) */ -#define vpiExplicitName 19 /* port is explicitly named */ -#define vpiDirection 20 /* direction of port: */ -#define vpiInput 1 /* input */ -#define vpiOutput 2 /* output */ -#define vpiInout 3 /* inout */ -#define vpiMixedIO 4 /* mixed input-output */ -#define vpiNoDirection 5 /* no direction */ -#define vpiConnByName 21 /* connected by name (boolean) */ - -#define vpiNetType 22 /* net subtypes: */ -#define vpiWire 1 /* wire net */ -#define vpiWand 2 /* wire-and net */ -#define vpiWor 3 /* wire-or net */ -#define vpiTri 4 /* three-state net */ -#define vpiTri0 5 /* pull-down net */ -#define vpiTri1 6 /* pull-up net */ -#define vpiTriReg 7 /* tri state reg net */ -#define vpiTriAnd 8 /* three-state wire-and net */ -#define vpiTriOr 9 /* three-state wire-or net */ -#define vpiSupply1 10 /* supply 1 net */ -#define vpiSupply0 11 /* supply zero net */ -#define vpiNone 12 /* no default net type (1364-2001) */ -#define vpiUwire 13 /* unresolved wire net (1364-2005) */ - -#define vpiExplicitScalared 23 /* explicitly scalared (boolean) */ -#define vpiExplicitVectored 24 /* explicitly vectored (boolean) */ -#define vpiExpanded 25 /* expanded vector net (boolean) */ -#define vpiImplicitDecl 26 /* implicitly declared net (boolean) */ -#define vpiChargeStrength 27 /* charge decay strength of net */ - -/* Defined as part of strengths section. -#define vpiLargeCharge 0x10 -#define vpiMediumCharge 0x04 -#define vpiSmallCharge 0x02 -*/ - -#define vpiArray 28 /* variable array (boolean) */ -#define vpiPortIndex 29 /* Port index */ - -/************************ gate and terminal properties ************************/ - -#define vpiTermIndex 30 /* Index of a primitive terminal */ -#define vpiStrength0 31 /* 0-strength of net or gate */ -#define vpiStrength1 32 /* 1-strength of net or gate */ -#define vpiPrimType 33 /* prmitive subtypes: */ -#define vpiAndPrim 1 /* and gate */ -#define vpiNandPrim 2 /* nand gate */ -#define vpiNorPrim 3 /* nor gate */ -#define vpiOrPrim 4 /* or gate */ -#define vpiXorPrim 5 /* xor gate */ -#define vpiXnorPrim 6 /* xnor gate */ -#define vpiBufPrim 7 /* buffer */ -#define vpiNotPrim 8 /* not gate */ -#define vpiBufif0Prim 9 /* zero-enabled buffer */ -#define vpiBufif1Prim 10 /* one-enabled buffer */ -#define vpiNotif0Prim 11 /* zero-enabled not gate */ -#define vpiNotif1Prim 12 /* one-enabled not gate */ -#define vpiNmosPrim 13 /* nmos switch */ -#define vpiPmosPrim 14 /* pmos switch */ -#define vpiCmosPrim 15 /* cmos switch */ -#define vpiRnmosPrim 16 /* resistive nmos switch */ -#define vpiRpmosPrim 17 /* resistive pmos switch */ -#define vpiRcmosPrim 18 /* resistive cmos switch */ -#define vpiRtranPrim 19 /* resistive bidirectional */ -#define vpiRtranif0Prim 20 /* zero-enable resistive bidirectional */ -#define vpiRtranif1Prim 21 /* one-enable resistive bidirectional */ -#define vpiTranPrim 22 /* bidirectional */ -#define vpiTranif0Prim 23 /* zero-enabled bidirectional */ -#define vpiTranif1Prim 24 /* one-enabled bidirectional */ -#define vpiPullupPrim 25 /* pullup */ -#define vpiPulldownPrim 26 /* pulldown */ -#define vpiSeqPrim 27 /* sequential UDP */ -#define vpiCombPrim 28 /* combinational UDP */ - -/**************** path, path terminal, timing check properties ****************/ - -#define vpiPolarity 34 /* polarity of module path... */ -#define vpiDataPolarity 35 /* ...or data path: */ -#define vpiPositive 1 /* positive */ -#define vpiNegative 2 /* negative */ -#define vpiUnknown 3 /* unknown (unspecified) */ - -#define vpiEdge 36 /* edge type of module path: */ -#define vpiNoEdge 0x00 /* no edge */ -#define vpiEdge01 0x01 /* 0 -> 1 */ -#define vpiEdge10 0x02 /* 1 -> 0 */ -#define vpiEdge0x 0x04 /* 0 -> x */ -#define vpiEdgex1 0x08 /* x -> 1 */ -#define vpiEdge1x 0x10 /* 1 -> x */ -#define vpiEdgex0 0x20 /* x -> 0 */ -#define vpiPosedge (vpiEdgex1 | vpiEdge01 | vpiEdge0x) -#define vpiNegedge (vpiEdgex0 | vpiEdge10 | vpiEdge1x) -#define vpiAnyEdge (vpiPosedge | vpiNegedge) - -#define vpiPathType 37 /* path delay connection subtypes: */ -#define vpiPathFull 1 /* ( a *> b ) */ -#define vpiPathParallel 2 /* ( a => b ) */ - -#define vpiTchkType 38 /* timing check subtypes: */ -#define vpiSetup 1 /* $setup */ -#define vpiHold 2 /* $hold */ -#define vpiPeriod 3 /* $period */ -#define vpiWidth 4 /* $width */ -#define vpiSkew 5 /* $skew */ -#define vpiRecovery 6 /* $recovery */ -#define vpiNoChange 7 /* $nochange */ -#define vpiSetupHold 8 /* $setuphold */ -#define vpiFullskew 9 /* $fullskew -- added for 1364-2001 */ -#define vpiRecrem 10 /* $recrem -- added for 1364-2001 */ -#define vpiRemoval 11 /* $removal -- added for 1364-2001 */ -#define vpiTimeskew 12 /* $timeskew -- added for 1364-2001 */ - -/**************************** expression properties ***************************/ - -#define vpiOpType 39 /* operation subtypes: */ -#define vpiMinusOp 1 /* unary minus */ -#define vpiPlusOp 2 /* unary plus */ -#define vpiNotOp 3 /* unary not */ -#define vpiBitNegOp 4 /* bitwise negation */ -#define vpiUnaryAndOp 5 /* bitwise reduction and */ -#define vpiUnaryNandOp 6 /* bitwise reduction nand */ -#define vpiUnaryOrOp 7 /* bitwise reduction or */ -#define vpiUnaryNorOp 8 /* bitwise reduction nor */ -#define vpiUnaryXorOp 9 /* bitwise reduction xor */ -#define vpiUnaryXNorOp 10 /* bitwise reduction xnor */ -#define vpiSubOp 11 /* binary subtraction */ -#define vpiDivOp 12 /* binary division */ -#define vpiModOp 13 /* binary modulus */ -#define vpiEqOp 14 /* binary equality */ -#define vpiNeqOp 15 /* binary inequality */ -#define vpiCaseEqOp 16 /* case (x and z) equality */ -#define vpiCaseNeqOp 17 /* case inequality */ -#define vpiGtOp 18 /* binary greater than */ -#define vpiGeOp 19 /* binary greater than or equal */ -#define vpiLtOp 20 /* binary less than */ -#define vpiLeOp 21 /* binary less than or equal */ -#define vpiLShiftOp 22 /* binary left shift */ -#define vpiRShiftOp 23 /* binary right shift */ -#define vpiAddOp 24 /* binary addition */ -#define vpiMultOp 25 /* binary multiplication */ -#define vpiLogAndOp 26 /* binary logical and */ -#define vpiLogOrOp 27 /* binary logical or */ -#define vpiBitAndOp 28 /* binary bitwise and */ -#define vpiBitOrOp 29 /* binary bitwise or */ -#define vpiBitXorOp 30 /* binary bitwise xor */ -#define vpiBitXNorOp 31 /* binary bitwise xnor */ -#define vpiBitXnorOp vpiBitXNorOp /* added with 1364-2001 */ -#define vpiConditionOp 32 /* ternary conditional */ -#define vpiConcatOp 33 /* n-ary concatenation */ -#define vpiMultiConcatOp 34 /* repeated concatenation */ -#define vpiEventOrOp 35 /* event or */ -#define vpiNullOp 36 /* null operation */ -#define vpiListOp 37 /* list of expressions */ -#define vpiMinTypMaxOp 38 /* min:typ:max: delay expression */ -#define vpiPosedgeOp 39 /* posedge */ -#define vpiNegedgeOp 40 /* negedge */ -#define vpiArithLShiftOp 41 /* arithmetic left shift (1364-2001) */ -#define vpiArithRShiftOp 42 /* arithmetic right shift (1364-2001) */ -#define vpiPowerOp 43 /* arithmetic power op (1364-2001) */ - -#define vpiConstType 40 /* constant subtypes: */ -#define vpiDecConst 1 /* decimal integer */ -#define vpiRealConst 2 /* real */ -#define vpiBinaryConst 3 /* binary integer */ -#define vpiOctConst 4 /* octal integer */ -#define vpiHexConst 5 /* hexadecimal integer */ -#define vpiStringConst 6 /* string literal */ -#define vpiIntConst 7 /* HDL integer constant (1364-2001) */ -#define vpiTimeConst 8 /* time constant */ - -#define vpiBlocking 41 /* blocking assignment (boolean) */ -#define vpiCaseType 42 /* case statement subtypes: */ -#define vpiCaseExact 1 /* exact match */ -#define vpiCaseX 2 /* ignore X's */ -#define vpiCaseZ 3 /* ignore Z's */ -#define vpiNetDeclAssign 43 /* assign part of decl (boolean) */ - -/************************** task/function properties **************************/ - -#define vpiFuncType 44 /* HDL function & system function type */ -#define vpiIntFunc 1 /* returns integer */ -#define vpiRealFunc 2 /* returns real */ -#define vpiTimeFunc 3 /* returns time */ -#define vpiSizedFunc 4 /* returns an arbitrary size */ -#define vpiSizedSignedFunc 5 /* returns sized signed value */ - -/** alias 1364-1995 system function subtypes to 1364-2001 function subtypes ***/ - -#define vpiSysFuncType vpiFuncType -#define vpiSysFuncInt vpiIntFunc -#define vpiSysFuncReal vpiRealFunc -#define vpiSysFuncTime vpiTimeFunc -#define vpiSysFuncSized vpiSizedFunc - -#define vpiUserDefn 45 /*user defined system task/func(boolean)*/ -#define vpiScheduled 46 /* object still scheduled (boolean) */ - -/*********************** properties added with 1364-2001 **********************/ - -#define vpiActive 49 /* reentrant task/func frame is active */ -#define vpiAutomatic 50 /* task/func obj is automatic */ -#define vpiCell 51 /* configuration cell */ -#define vpiConfig 52 /* configuration config file */ -#define vpiConstantSelect 53 /* (boolean) bit-select or part-select - indices are constant expressions */ -#define vpiDecompile 54 /* decompile the object */ -#define vpiDefAttribute 55 /* Attribute defined for the obj */ -#define vpiDelayType 56 /* delay subtype */ -#define vpiModPathDelay 1 /* module path delay */ -#define vpiInterModPathDelay 2 /* intermodule path delay */ -#define vpiMIPDelay 3 /* module input port delay */ -#define vpiIteratorType 57 /* object type of an iterator */ -#define vpiLibrary 58 /* configuration library */ -#define vpiMultiArray 59 /* Object is a multidimensional array */ -#define vpiOffset 60 /* offset from LSB */ -#define vpiResolvedNetType 61 /* net subtype after resolution, returns - same subtypes as vpiNetType */ -#define vpiSaveRestartID 62 /* unique ID for save/restart data */ -#define vpiSaveRestartLocation 63 /* name of save/restart data file */ -#define vpiValid 64 /* reentrant task/func frame or automatic - variable is valid */ -#define vpiValidFalse 0 -#define vpiValidTrue 1 -#define vpiSigned 65 /* TRUE for vpiIODecl and any object in - the expression class if the object - has the signed attribute */ -#define vpiLocalParam 70 /* TRUE when a param is declared as a - localparam */ -#define vpiModPathHasIfNone 71 /* Mod path has an ifnone statement */ - -/*********************** properties added with 1364-2005 **********************/ - -#define vpiIndexedPartSelectType 72 /* Indexed part-select type */ -#define vpiPosIndexed 1 /* +: */ -#define vpiNegIndexed 2 /* -: */ -#define vpiIsMemory 73 /* TRUE for a one-dimensional reg array */ -#define vpiIsProtected 74 /* TRUE for protected design information */ - -/*************** vpi_control() constants (added with 1364-2001) ***************/ - -#define vpiStop 66 /* execute simulator's $stop */ -#define vpiFinish 67 /* execute simulator's $finish */ -#define vpiReset 68 /* execute simulator's $reset */ -#define vpiSetInteractiveScope 69 /* set simulator's interactive scope */ - -/**************************** I/O related defines *****************************/ - -#define VPI_MCD_STDOUT 0x00000001 - -/*************************** STRUCTURE DEFINITIONS ****************************/ - -/******************************* time structure *******************************/ - -typedef struct t_vpi_time -{ - PLI_INT32 type; /* [vpiScaledRealTime, vpiSimTime, - vpiSuppressTime] */ - PLI_UINT32 high, low; /* for vpiSimTime */ - double real; /* for vpiScaledRealTime */ -} s_vpi_time, *p_vpi_time; - -/* time types */ - -#define vpiScaledRealTime 1 -#define vpiSimTime 2 -#define vpiSuppressTime 3 - -/****************************** delay structures ******************************/ - -typedef struct t_vpi_delay -{ - struct t_vpi_time *da; /* pointer to user allocated array of - delay values */ - PLI_INT32 no_of_delays; /* number of delays */ - PLI_INT32 time_type; /* [vpiScaledRealTime, vpiSimTime, - vpiSuppressTime] */ - PLI_INT32 mtm_flag; /* true for mtm values */ - PLI_INT32 append_flag; /* true for append */ - PLI_INT32 pulsere_flag; /* true for pulsere values */ -} s_vpi_delay, *p_vpi_delay; - -/***************************** value structures *******************************/ - -/* vector value */ - -#ifndef VPI_VECVAL /* added in 1364-2005 */ -#define VPI_VECVAL - -typedef struct t_vpi_vecval -{ - /* following fields are repeated enough times to contain vector */ -PLI_INT32 aval, bval; /* bit encoding: ab: 00=0, 10=1, 11=X, 01=Z */ -} s_vpi_vecval, *p_vpi_vecval; - -#endif - -/* strength (scalar) value */ - -typedef struct t_vpi_strengthval -{ - PLI_INT32 logic; /* vpi[0,1,X,Z] */ - PLI_INT32 s0, s1; /* refer to strength coding below */ -} s_vpi_strengthval, *p_vpi_strengthval; - -/* strength values */ - -#define vpiSupplyDrive 0x80 -#define vpiStrongDrive 0x40 -#define vpiPullDrive 0x20 -#define vpiWeakDrive 0x08 -#define vpiLargeCharge 0x10 -#define vpiMediumCharge 0x04 -#define vpiSmallCharge 0x02 -#define vpiHiZ 0x01 - -/* generic value */ - -typedef struct t_vpi_value -{ - PLI_INT32 format; /* vpi[[Bin,Oct,Dec,Hex]Str,Scalar,Int,Real,String, - Vector,Strength,Suppress,Time,ObjType]Val */ - union - { - PLI_BYTE8 *str; /* string value */ - PLI_INT32 scalar; /* vpi[0,1,X,Z] */ - PLI_INT32 integer; /* integer value */ - double real; /* real value */ - struct t_vpi_time *time; /* time value */ - struct t_vpi_vecval *vector; /* vector value */ - struct t_vpi_strengthval *strength; /* strength value */ - PLI_BYTE8 *misc; /* ...other */ - } value; -} s_vpi_value, *p_vpi_value; - -typedef struct t_vpi_arrayvalue -{ - PLI_UINT32 format; - PLI_UINT32 flags; - union - { - PLI_INT32 *integers; - PLI_INT16 *shortints; - PLI_INT64 *longints; - PLI_BYTE8 *rawvals; - struct t_vpi_vecval *vectors; - struct t_vpi_time *times; - double *reals; - float *shortreals; - } value; -} s_vpi_arrayvalue, *p_vpi_arrayvalue; - -/* value formats */ - -#define vpiBinStrVal 1 -#define vpiOctStrVal 2 -#define vpiDecStrVal 3 -#define vpiHexStrVal 4 -#define vpiScalarVal 5 -#define vpiIntVal 6 -#define vpiRealVal 7 -#define vpiStringVal 8 -#define vpiVectorVal 9 -#define vpiStrengthVal 10 -#define vpiTimeVal 11 -#define vpiObjTypeVal 12 -#define vpiSuppressVal 13 -#define vpiShortIntVal 14 -#define vpiLongIntVal 15 -#define vpiShortRealVal 16 -#define vpiRawTwoStateVal 17 -#define vpiRawFourStateVal 18 - -/* delay modes */ - -#define vpiNoDelay 1 -#define vpiInertialDelay 2 -#define vpiTransportDelay 3 -#define vpiPureTransportDelay 4 - -/* force and release flags */ - -#define vpiForceFlag 5 -#define vpiReleaseFlag 6 - -/* scheduled event cancel flag */ - -#define vpiCancelEvent 7 - -/* bit mask for the flags argument to vpi_put_value() */ - -#define vpiReturnEvent 0x1000 - -/* bit flags for vpi_get_value_array flags field */ - -#define vpiUserAllocFlag 0x2000 - -/* bit flags for vpi_put_value_array flags field */ - -#define vpiOneValue 0x4000 -#define vpiPropagateOff 0x8000 - -/* scalar values */ - -#define vpi0 0 -#define vpi1 1 -#define vpiZ 2 -#define vpiX 3 -#define vpiH 4 -#define vpiL 5 -#define vpiDontCare 6 -/* -#define vpiNoChange 7 Defined under vpiTchkType, but - can be used here. -*/ - -/*********************** system task/function structure ***********************/ - -typedef struct t_vpi_systf_data -{ - PLI_INT32 type; /* vpiSysTask, vpiSysFunc */ - PLI_INT32 sysfunctype; /* vpiSysTask, vpi[Int,Real,Time,Sized, - SizedSigned]Func */ - const PLI_BYTE8 *tfname; /* first character must be '$' */ - PLI_INT32 (*calltf)(PLI_BYTE8 *); - PLI_INT32 (*compiletf)(PLI_BYTE8 *); - PLI_INT32 (*sizetf)(PLI_BYTE8 *); /* for sized function callbacks only */ - PLI_BYTE8 *user_data; -} s_vpi_systf_data, *p_vpi_systf_data; - -#define vpiSysTask 1 -#define vpiSysFunc 2 - -/* the subtypes are defined under the vpiFuncType property */ - -/****************** Verilog execution information structure *******************/ - -typedef struct t_vpi_vlog_info -{ - PLI_INT32 argc; - PLI_BYTE8 **argv; - PLI_BYTE8 *product; - PLI_BYTE8 *version; -} s_vpi_vlog_info, *p_vpi_vlog_info; - -/*********************** PLI error information structure **********************/ - -typedef struct t_vpi_error_info -{ - PLI_INT32 state; /* vpi[Compile,PLI,Run] */ - PLI_INT32 level; /* vpi[Notice,Warning,Error,System,Internal] */ - PLI_BYTE8 *message; - PLI_BYTE8 *product; - PLI_BYTE8 *code; - PLI_BYTE8 *file; - PLI_INT32 line; -} s_vpi_error_info, *p_vpi_error_info; - -/* state when error occurred */ - -#define vpiCompile 1 -#define vpiPLI 2 -#define vpiRun 3 - -/* error severity levels */ - -#define vpiNotice 1 -#define vpiWarning 2 -#define vpiError 3 -#define vpiSystem 4 -#define vpiInternal 5 - -/**************************** callback structures *****************************/ - -#define vpiTimePrecision 12 /* module time precision */ - -/* normal callback structure */ - -typedef struct t_cb_data -{ - PLI_INT32 reason; /* callback reason */ - PLI_INT32 (*cb_rtn)(struct t_cb_data *); /* call routine */ - vpiHandle obj; /* trigger object */ - p_vpi_time time; /* callback time */ - p_vpi_value value; /* trigger object value */ - PLI_INT32 index; /* index of the memory word or - var select that changed */ - PLI_BYTE8 *user_data; -} s_cb_data, *p_cb_data; - -/****************************** CALLBACK REASONS ******************************/ -/***************************** Simulation related *****************************/ - -#define cbValueChange 1 -#define cbStmt 2 -#define cbForce 3 -#define cbRelease 4 - -/******************************** Time related ********************************/ - -#define cbAtStartOfSimTime 5 -#define cbReadWriteSynch 6 -#define cbReadOnlySynch 7 -#define cbNextSimTime 8 -#define cbAfterDelay 9 - -/******************************* Action related *******************************/ - -#define cbEndOfCompile 10 -#define cbStartOfSimulation 11 -#define cbEndOfSimulation 12 -#define cbError 13 -#define cbTchkViolation 14 -#define cbStartOfSave 15 -#define cbEndOfSave 16 -#define cbStartOfRestart 17 -#define cbEndOfRestart 18 -#define cbStartOfReset 19 -#define cbEndOfReset 20 -#define cbEnterInteractive 21 -#define cbExitInteractive 22 -#define cbInteractiveScopeChange 23 -#define cbUnresolvedSystf 24 - -/**************************** Added with 1364-2001 ****************************/ - -#define cbAssign 25 -#define cbDeassign 26 -#define cbDisable 27 -#define cbPLIError 28 -#define cbSignal 29 - -/**************************** Added with 1364-2005 ****************************/ -#define cbNBASynch 30 -#define cbAtEndOfSimTime 31 - - -/************************* FUNCTION DECLARATIONS **************************/ - -/* callback related */ - -XXTERN vpiHandle vpi_register_cb PROTO_PARAMS((p_cb_data cb_data_p)); -XXTERN PLI_INT32 vpi_remove_cb PROTO_PARAMS((vpiHandle cb_obj)); -XXTERN void vpi_get_cb_info PROTO_PARAMS((vpiHandle object, - p_cb_data cb_data_p)); -XXTERN vpiHandle vpi_register_systf PROTO_PARAMS((p_vpi_systf_data - systf_data_p)); -XXTERN void vpi_get_systf_info PROTO_PARAMS((vpiHandle object, - p_vpi_systf_data - systf_data_p)); - -/* for obtaining handles */ - -XXTERN vpiHandle vpi_handle_by_name PROTO_PARAMS((PLI_BYTE8 *name, - vpiHandle scope)); -XXTERN vpiHandle vpi_handle_by_index PROTO_PARAMS((vpiHandle object, - PLI_INT32 indx)); - -/* for traversing relationships */ - -XXTERN vpiHandle vpi_handle PROTO_PARAMS((PLI_INT32 type, - vpiHandle refHandle)); -XXTERN vpiHandle vpi_handle_multi PROTO_PARAMS((PLI_INT32 type, - vpiHandle refHandle1, - vpiHandle refHandle2, - ... )); -XXTERN vpiHandle vpi_iterate PROTO_PARAMS((PLI_INT32 type, - vpiHandle refHandle)); -XXTERN vpiHandle vpi_scan PROTO_PARAMS((vpiHandle iterator)); - -/* for processing properties */ - -XXTERN PLI_INT32 vpi_get PROTO_PARAMS((PLI_INT32 property, - vpiHandle object)); -XXTERN PLI_INT64 vpi_get64 PROTO_PARAMS((PLI_INT32 property, - vpiHandle object)); -XXTERN PLI_BYTE8 *vpi_get_str PROTO_PARAMS((PLI_INT32 property, - vpiHandle object)); - -/* delay processing */ - -XXTERN void vpi_get_delays PROTO_PARAMS((vpiHandle object, - p_vpi_delay delay_p)); -XXTERN void vpi_put_delays PROTO_PARAMS((vpiHandle object, - p_vpi_delay delay_p)); - -/* value processing */ - -XXTERN void vpi_get_value PROTO_PARAMS((vpiHandle expr, - p_vpi_value value_p)); -XXTERN vpiHandle vpi_put_value PROTO_PARAMS((vpiHandle object, - p_vpi_value value_p, - p_vpi_time time_p, - PLI_INT32 flags)); -XXTERN void vpi_get_value_array PROTO_PARAMS((vpiHandle expr, - p_vpi_arrayvalue arrayvalue_p, - PLI_INT32 *index_p, - PLI_UINT32 num)); - -XXTERN void vpi_put_value_array PROTO_PARAMS((vpiHandle object, - p_vpi_arrayvalue arrayvalue_p, - PLI_INT32 *index_p, - PLI_UINT32 num)); - -/* time processing */ - -XXTERN void vpi_get_time PROTO_PARAMS((vpiHandle object, - p_vpi_time time_p)); - -/* I/O routines */ - -XXTERN PLI_UINT32 vpi_mcd_open PROTO_PARAMS((const PLI_BYTE8 *fileName)); -XXTERN PLI_UINT32 vpi_mcd_close PROTO_PARAMS((PLI_UINT32 mcd)); -XXTERN PLI_BYTE8 *vpi_mcd_name PROTO_PARAMS((PLI_UINT32 cd)); -XXTERN PLI_INT32 vpi_mcd_printf PROTO_PARAMS((PLI_UINT32 mcd, - const PLI_BYTE8 *format, - ...)); -XXTERN PLI_INT32 vpi_printf PROTO_PARAMS((const PLI_BYTE8 *format, - ...)); - -/* utility routines */ - -XXTERN PLI_INT32 vpi_compare_objects PROTO_PARAMS((vpiHandle object1, - vpiHandle object2)); -XXTERN PLI_INT32 vpi_chk_error PROTO_PARAMS((p_vpi_error_info - error_info_p)); -XXTERN PLI_INT32 vpi_free_object PROTO_PARAMS((vpiHandle object)); -XXTERN PLI_INT32 vpi_release_handle PROTO_PARAMS((vpiHandle object)); -XXTERN PLI_INT32 vpi_get_vlog_info PROTO_PARAMS((p_vpi_vlog_info - vlog_info_p)); - -/* routines added with 1364-2001 */ - -XXTERN PLI_INT32 vpi_get_data PROTO_PARAMS((PLI_INT32 id, - PLI_BYTE8 *dataLoc, - PLI_INT32 numOfBytes)); -XXTERN PLI_INT32 vpi_put_data PROTO_PARAMS((PLI_INT32 id, - PLI_BYTE8 *dataLoc, - PLI_INT32 numOfBytes)); -XXTERN void *vpi_get_userdata PROTO_PARAMS((vpiHandle obj)); -XXTERN PLI_INT32 vpi_put_userdata PROTO_PARAMS((vpiHandle obj, - void *userdata)); -XXTERN PLI_INT32 vpi_vprintf PROTO_PARAMS((const PLI_BYTE8 *format, - va_list ap)); -XXTERN PLI_INT32 vpi_mcd_vprintf PROTO_PARAMS((PLI_UINT32 mcd, - const PLI_BYTE8 *format, - va_list ap)); -XXTERN PLI_INT32 vpi_flush PROTO_PARAMS((void)); -XXTERN PLI_INT32 vpi_mcd_flush PROTO_PARAMS((PLI_UINT32 mcd)); -XXTERN PLI_INT32 vpi_control PROTO_PARAMS((PLI_INT32 operation, - ...)); -XXTERN vpiHandle vpi_handle_by_multi_index PROTO_PARAMS((vpiHandle obj, - PLI_INT32 num_index, - PLI_INT32 *index_array)); - -/****************************** GLOBAL VARIABLES ******************************/ - -PLI_VEXTERN PLI_DLLESPEC void (*vlog_startup_routines[])(void); - - /* array of function pointers, last pointer should be null */ - -#undef PLI_EXTERN -#undef PLI_VEXTERN - -#ifdef VPI_USER_DEFINED_DLLISPEC -#undef VPI_USER_DEFINED_DLLISPEC -#undef PLI_DLLISPEC -#endif -#ifdef VPI_USER_DEFINED_DLLESPEC -#undef VPI_USER_DEFINED_DLLESPEC -#undef PLI_DLLESPEC -#endif - -#ifdef PLI_PROTOTYPES -#undef PLI_PROTOTYPES -#undef PROTO_PARAMS -#undef XXTERN -#undef EETERN -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* VPI_USER_H */ - diff --git a/cocotb/share/include/vpi_user_ext.h b/cocotb/share/include/vpi_user_ext.h deleted file mode 100644 index cd00cada..00000000 --- a/cocotb/share/include/vpi_user_ext.h +++ /dev/null @@ -1,52 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2013, 2019 Potential Ventures Ltd -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -/* extensions to vpi_user.h */ - -#ifndef VPI_USER_EXT_H -#define VPI_USER_EXT_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* used by Cadence Xcelium for Verilog-AMS */ -#define vpiRealNet 526 -#define vpiInterconnectNet 533 -#define vpiInterconnectArray 534 - -/* arguments for vpiStop or vpiFinish */ -#define vpiDiagNone 0 /* prints nothing */ -#define vpiDiagTimeLoc 1 /* prints simulation time and location */ -#define vpiDiagTimeLocCPUMem 2 /* prints simulation time, location and - statistics about CPU and memory usage */ - -#ifdef __cplusplus -} -#endif - -#endif /* VPI_USER_EXT_H */ diff --git a/cocotb/share/lib/embed/embed.cpp b/cocotb/share/lib/embed/embed.cpp deleted file mode 100644 index e982f7f8..00000000 --- a/cocotb/share/lib/embed/embed.cpp +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright cocotb contributors -// Licensed under the Revised BSD License, see LICENSE for details. -// SPDX-License-Identifier: BSD-3-Clause - -#include -#include // xstr, utils_dyn_open, utils_dyn_sym -#include // gpi_event_t -#include // getenv -#if ! defined(__linux__) && ! defined(__APPLE__) -#include // Win32 API for loading the embed impl library -#include // string -#endif - -#ifndef PYTHON_LIB -#error "Name of Python library required" -#else -#define PYTHON_LIB_STR xstr(PYTHON_LIB) -#endif - -#ifndef EMBED_IMPL_LIB -#error "Name of embed implementation library required" -#else -#define EMBED_IMPL_LIB_STR xstr(EMBED_IMPL_LIB) -#endif - - -static void (*_embed_init_python)(); -static void (*_embed_sim_cleanup)(); -static int (*_embed_sim_init)(int argc, char const * const * argv); -static void (*_embed_sim_event)(gpi_event_t level, const char *msg); - -static bool init_failed = false; - - -#if ! defined(__linux__) && ! defined(__APPLE__) -static ACTCTX act_ctx = { - /* cbSize */ sizeof(ACTCTX), - /* dwFlags */ ACTCTX_FLAG_HMODULE_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID, - /* lpSource */ NULL, - /* wProcessorArchitecture */ 0, - /* wLangId */ 0, - /* lpAssemblyDirectory */ NULL, - /* lpResourceName */ MAKEINTRESOURCE(1000), - /* lpApplicationName */ NULL, - /* hModule */ 0 -}; - -BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID) -{ - if (fdwReason == DLL_PROCESS_ATTACH) { - act_ctx.hModule = hinstDLL; - } - - return TRUE; -} -#endif - - -extern "C" void embed_init_python(void) -{ - // preload python library - char const * libpython_path = getenv("LIBPYTHON_LOC"); - if (!libpython_path) { - // default to libpythonX.X.so - libpython_path = PYTHON_LIB_STR; - } - auto loaded = utils_dyn_open(libpython_path); - if (!loaded) { - // LCOV_EXCL_START - init_failed = true; - return; - // LCOV_EXCL_STOP - } - -#if ! defined(__linux__) && ! defined(__APPLE__) - if (!act_ctx.hModule) { - // LCOV_EXCL_START - init_failed = true; - return; - // LCOV_EXCL_STOP - } - - HANDLE hact_ctx = CreateActCtx(&act_ctx); - if(hact_ctx == INVALID_HANDLE_VALUE) { - // LCOV_EXCL_START - init_failed = true; - return; - // LCOV_EXCL_STOP - } - - ULONG_PTR Cookie; - if(!ActivateActCtx(hact_ctx, &Cookie)) { - // LCOV_EXCL_START - init_failed = true; - return; - // LCOV_EXCL_STOP - } -#endif - - // load embed implementation library and functions - void * embed_impl_lib_handle; - if (!(embed_impl_lib_handle = utils_dyn_open(EMBED_IMPL_LIB_STR))) { - // LCOV_EXCL_START - init_failed = true; - return; - // LCOV_EXCL_STOP - } - if (!(_embed_init_python = reinterpret_cast(utils_dyn_sym(embed_impl_lib_handle, "_embed_init_python")))) { - // LCOV_EXCL_START - init_failed = true; - return; - // LCOV_EXCL_STOP - } - if (!(_embed_sim_cleanup = reinterpret_cast(utils_dyn_sym(embed_impl_lib_handle, "_embed_sim_cleanup")))) { - // LCOV_EXCL_START - init_failed = true; - return; - // LCOV_EXCL_STOP - } - if (!(_embed_sim_init = reinterpret_cast(utils_dyn_sym(embed_impl_lib_handle, "_embed_sim_init")))) { - // LCOV_EXCL_START - init_failed = true; - return; - // LCOV_EXCL_STOP - } - if (!(_embed_sim_event = reinterpret_cast(utils_dyn_sym(embed_impl_lib_handle, "_embed_sim_event")))) { - // LCOV_EXCL_START - init_failed = true; - return; - // LCOV_EXCL_STOP - } - -#if ! defined(__linux__) && ! defined(__APPLE__) - if(!DeactivateActCtx(0, Cookie)) { - // LCOV_EXCL_START - init_failed = true; - return; - // LCOV_EXCL_STOP - } - - ReleaseActCtx(hact_ctx); -#endif - - // call to embed library impl - _embed_init_python(); -} - - -extern "C" void embed_sim_cleanup(void) -{ - if (!init_failed) { - _embed_sim_cleanup(); - } -} - - -extern "C" int embed_sim_init(int argc, char const * const * argv) -{ - if (init_failed) { - // LCOV_EXCL_START - return -1; - // LCOV_EXCL_STOP - } else { - return _embed_sim_init(argc, argv); - } -} - - -extern "C" void embed_sim_event(gpi_event_t level, const char *msg) -{ - if (!init_failed) { - _embed_sim_event(level, msg); - } -} diff --git a/cocotb/share/lib/embed/gpi_embed.cpp b/cocotb/share/lib/embed/gpi_embed.cpp deleted file mode 100644 index ab6af0a3..00000000 --- a/cocotb/share/lib/embed/gpi_embed.cpp +++ /dev/null @@ -1,365 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2013, 2018 Potential Ventures Ltd -* Copyright (c) 2013 SolarFlare Communications Inc -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd, -* SolarFlare Communications Inc nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -// Embed Python into the simulator using GPI - -#include -#include -#include // LOG_* macros -#include // py_gpi_logger_set_level, py_gpi_logger_initialize, py_gpi_logger_finalize -#include // gpi_event_t -#include "locale.h" -#include -#include - -#if defined(_WIN32) -#include -#define sleep(n) Sleep(1000 * n) -#define getpid() GetCurrentProcessId() -#ifndef PATH_MAX -#define PATH_MAX MAX_PATH -#endif -#else -#include -#endif -static PyThreadState *gtstate = NULL; - -static wchar_t progname[] = L"cocotb"; -static wchar_t *argv[] = { progname }; - -#if defined(_WIN32) -#if defined(__MINGW32__) || defined (__CYGWIN32__) -const char* PYTHON_INTERPRETER_PATH = "/Scripts/python"; -#else -const char* PYTHON_INTERPRETER_PATH = "\\Scripts\\python"; -#endif -#else -const char* PYTHON_INTERPRETER_PATH = "/bin/python"; -#endif - - -static PyObject *pEventFn = NULL; - - -static void set_program_name_in_venv(void) -{ - static char venv_path[PATH_MAX]; - static wchar_t venv_path_w[PATH_MAX]; - - const char *venv_path_home = getenv("VIRTUAL_ENV"); - if (!venv_path_home) { - LOG_INFO("Did not detect Python virtual environment. Using system-wide Python interpreter"); - return; - } - - strncpy(venv_path, venv_path_home, sizeof(venv_path)-1); - if (venv_path[sizeof(venv_path) - 1]) { - LOG_ERROR("Unable to set Python Program Name using virtual environment. Path to virtual environment too long"); - return; - } - - strncat(venv_path, PYTHON_INTERPRETER_PATH, sizeof(venv_path) - strlen(venv_path) - 1); - if (venv_path[sizeof(venv_path) - 1]) { - LOG_ERROR("Unable to set Python Program Name using virtual environment. Path to interpreter too long"); - return; - } - - wcsncpy(venv_path_w, Py_DecodeLocale(venv_path, NULL), sizeof(venv_path_w)/sizeof(wchar_t)); - - if (venv_path_w[(sizeof(venv_path_w)/sizeof(wchar_t)) - 1]) { - LOG_ERROR("Unable to set Python Program Name using virtual environment. Path to interpreter too long"); - return; - } - - LOG_INFO("Using Python virtual environment interpreter at %ls", venv_path_w); - Py_SetProgramName(venv_path_w); -} - - -/** - * @name Initialize the Python interpreter - * @brief Create and initialize the Python interpreter - * @ingroup python_c_api - * - * GILState before calling: N/A - * - * GILState after calling: released - * - * Stores the thread state for cocotb in static variable gtstate - */ - -extern "C" COCOTB_EXPORT void _embed_init_python(void) -{ - - assert(!gtstate); // this function should not be called twice - - to_python(); - set_program_name_in_venv(); - Py_Initialize(); /* Initialize the interpreter */ - PySys_SetArgvEx(1, argv, 0); - - /* Swap out and return current thread state and release the GIL */ - gtstate = PyEval_SaveThread(); - to_simulator(); - - /* Before returning we check if the user wants pause the simulator thread - such that they can attach */ - const char *pause = getenv("COCOTB_ATTACH"); - if (pause) { - unsigned long sleep_time = strtoul(pause, NULL, 10); - /* This should check for out-of-range parses which returns ULONG_MAX and sets errno, - as well as correct parses that would be sliced by the narrowing cast */ - if (errno == ERANGE || sleep_time >= UINT_MAX) { - LOG_ERROR("COCOTB_ATTACH only needs to be set to ~30 seconds"); - return; - } - if ((errno != 0 && sleep_time == 0) || - (sleep_time <= 0)) { - LOG_ERROR("COCOTB_ATTACH must be set to an integer base 10 or omitted"); - return; - } - - LOG_ERROR("Waiting for %lu seconds - attach to PID %d with your debugger", sleep_time, getpid()); - sleep((unsigned int)sleep_time); - } -} - -/** - * @name Simulator cleanup - * @brief Called by the simulator on shutdown. - * @ingroup python_c_api - * - * GILState before calling: Not held - * - * GILState after calling: Not held - * - * Makes one call to PyGILState_Ensure and one call to Py_Finalize. - * - * Cleans up reference counts for Python objects and calls Py_Finalize function. - */ -extern "C" COCOTB_EXPORT void _embed_sim_cleanup(void) -{ - // If initialization fails, this may be called twice: - // Before the initial callback returns and in the final callback. - // So we check if Python is still initialized before doing cleanup. - if (Py_IsInitialized()) { - to_python(); - PyGILState_Ensure(); // Don't save state as we are calling Py_Finalize - Py_DecRef(pEventFn); - pEventFn = NULL; - py_gpi_logger_finalize(); - Py_Finalize(); - to_simulator(); - } -} - -/** - * @name Initialization - * @brief Called by the simulator on initialization. Load cocotb Python module - * @ingroup python_c_api - * - * GILState before calling: Not held - * - * GILState after calling: Not held - * - * Makes one call to PyGILState_Ensure and one call to PyGILState_Release - * - * Loads the Python module called cocotb and calls the _initialise_testbench function - */ - -static int get_module_ref(const char *modname, PyObject **mod) -{ - PyObject *pModule = PyImport_ImportModule(modname); - - if (pModule == NULL) { - // LCOV_EXCL_START - PyErr_Print(); - LOG_ERROR("Failed to load Python module \"%s\"", modname); - return -1; - // LCOV_EXCL_STOP - } - - *mod = pModule; - return 0; -} - -extern "C" COCOTB_EXPORT int _embed_sim_init(int argc, char const * const * argv) -{ - - int i; - int ret = 0; - - /* Check that we are not already initialized */ - if (pEventFn) - return ret; - - PyObject *cocotb_module, *cocotb_init, *cocotb_retval; - PyObject *cocotb_log_module = NULL; - PyObject *log_func; - PyObject *filter_func; - PyObject *argv_list; - - cocotb_module = NULL; - - // Ensure that the current thread is ready to call the Python C API - PyGILState_STATE gstate = PyGILState_Ensure(); - to_python(); - - if (get_module_ref("cocotb", &cocotb_module)) { - // LCOV_EXCL_START - goto cleanup; - // LCOV_EXCL_STOP - } - - LOG_INFO("Python interpreter initialized and cocotb loaded!"); - - if (get_module_ref("cocotb.log", &cocotb_log_module)) { - // LCOV_EXCL_START - goto cleanup; - // LCOV_EXCL_STOP - } - - // Obtain the function to use when logging from C code - log_func = PyObject_GetAttrString(cocotb_log_module, "_log_from_c"); // New reference - if (log_func == NULL) { - // LCOV_EXCL_START - PyErr_Print(); - LOG_ERROR("Failed to get the _log_from_c function"); - goto cleanup; - // LCOV_EXCL_STOP - } - - // Obtain the function to check whether to call log function - filter_func = PyObject_GetAttrString(cocotb_log_module, "_filter_from_c"); // New reference - if (filter_func == NULL) { - // LCOV_EXCL_START - Py_DECREF(log_func); - PyErr_Print(); - LOG_ERROR("Failed to get the _filter_from_c method"); - goto cleanup; - // LCOV_EXCL_STOP - } - - py_gpi_logger_initialize(log_func, filter_func); // Note: This function steals references to log_func and filter_func. - - pEventFn = PyObject_GetAttrString(cocotb_module, "_sim_event"); // New reference - if (pEventFn == NULL) { - // LCOV_EXCL_START - PyErr_Print(); - LOG_ERROR("Failed to get the _sim_event method"); - goto cleanup; - // LCOV_EXCL_STOP - } - - cocotb_init = PyObject_GetAttrString(cocotb_module, "_initialise_testbench"); // New reference - if (cocotb_init == NULL) { - // LCOV_EXCL_START - PyErr_Print(); - LOG_ERROR("Failed to get the _initialise_testbench method"); - goto cleanup; - // LCOV_EXCL_STOP - } - - // Build argv for cocotb module - argv_list = PyList_New(argc); // New reference - if (argv_list == NULL) { - // LCOV_EXCL_START - PyErr_Print(); - LOG_ERROR("Unable to create argv list"); - goto cleanup; - // LCOV_EXCL_STOP - } - for (i = 0; i < argc; i++) { - // Decode, embedding non-decodable bytes using PEP-383. This can only - // fail with MemoryError or similar. - PyObject *argv_item = PyUnicode_DecodeLocale(argv[i], "surrogateescape"); // New reference - if (argv_item == NULL) { - // LCOV_EXCL_START - PyErr_Print(); - LOG_ERROR("Unable to convert command line argument %d to Unicode string.", i); - Py_DECREF(argv_list); - goto cleanup; - // LCOV_EXCL_STOP - } - PyList_SET_ITEM(argv_list, i, argv_item); // Note: This function steals the reference to argv_item - } - - - cocotb_retval = PyObject_CallFunctionObjArgs(cocotb_init, argv_list, NULL); - Py_DECREF(argv_list); - Py_DECREF(cocotb_init); - - if (cocotb_retval != NULL) { - LOG_DEBUG("_initialise_testbench successful"); - Py_DECREF(cocotb_retval); - } else { - // LCOV_EXCL_START - PyErr_Print(); - LOG_ERROR("cocotb initialization failed - exiting"); - goto cleanup; - // LCOV_EXCL_STOP - } - - goto ok; - -cleanup: - ret = -1; -ok: - Py_XDECREF(cocotb_module); - Py_XDECREF(cocotb_log_module); - - PyGILState_Release(gstate); - to_simulator(); - - return ret; -} - -extern "C" COCOTB_EXPORT void _embed_sim_event(gpi_event_t level, const char *msg) -{ - /* Indicate to the upper layer that a sim event occurred */ - - if (pEventFn) { - PyGILState_STATE gstate; - to_python(); - gstate = PyGILState_Ensure(); - - if (msg == NULL) { - msg = "No message provided"; - } - - PyObject* pValue = PyObject_CallFunction(pEventFn, "ls", level, msg); - if (pValue == NULL) { - PyErr_Print(); - LOG_ERROR("Passing event to upper layer failed"); - } - Py_XDECREF(pValue); - PyGILState_Release(gstate); - to_simulator(); - } -} diff --git a/cocotb/share/lib/fli/FliCbHdl.cpp b/cocotb/share/lib/fli/FliCbHdl.cpp deleted file mode 100644 index cb233534..00000000 --- a/cocotb/share/lib/fli/FliCbHdl.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2015/16 Potential Ventures Ltd -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#include "FliImpl.h" -#include "mti.h" -#include "tcl.h" -#include - -/** - * @name cleanup callback - * @brief Called while unwinding after a GPI callback - * - * We keep the process but desensitize it - * - * NB: need a way to determine if should leave it sensitized... - * - */ -int FliProcessCbHdl::cleanup_callback() -{ - if (m_sensitised) { - mti_Desensitize(m_proc_hdl); - } - m_sensitised = false; - return 0; -} - -FliTimedCbHdl::FliTimedCbHdl(GpiImplInterface *impl, - uint64_t time) : GpiCbHdl(impl), - FliProcessCbHdl(impl), - m_time(time) -{ - m_proc_hdl = mti_CreateProcessWithPriority(NULL, handle_fli_callback, (void *)this, MTI_PROC_IMMEDIATE); -} - -int FliTimedCbHdl::arm_callback() -{ - #if defined(__LP64__) || defined(_WIN64) - mti_ScheduleWakeup64(m_proc_hdl, static_cast(m_time)); - #else - mtiTime64T m_time_union_ps; - MTI_TIME64_ASGN(m_time_union_ps, (mtiInt32T)((m_time) >> 32), (mtiUInt32T)(m_time)); - mti_ScheduleWakeup64(m_proc_hdl, m_time_union_ps); - #endif - m_sensitised = true; - set_call_state(GPI_PRIMED); - return 0; -} - -int FliTimedCbHdl::cleanup_callback() -{ - switch (get_call_state()) { - case GPI_PRIMED: - /* Issue #188: Work around for modelsim that is harmless to othes too, - we tag the time as delete, let it fire then do not pass up - */ - LOG_DEBUG("Not removing PRIMED timer %p", m_time); - set_call_state(GPI_DELETE); - return 0; - case GPI_CALL: - LOG_DEBUG("Not removing CALL timer yet %p", m_time); - set_call_state(GPI_DELETE); - return 0; - case GPI_DELETE: - LOG_DEBUG("Removing Postponed DELETE timer %p", m_time); - break; - default: - break; - } - FliProcessCbHdl::cleanup_callback(); - FliImpl* impl = (FliImpl*)m_impl; - impl->cache.put_timer(this); - return 0; -} - -int FliSignalCbHdl::arm_callback() -{ - if (NULL == m_proc_hdl) { - LOG_DEBUG("Creating a new process to sensitise to signal %s", mti_GetSignalName(m_sig_hdl)); - m_proc_hdl = mti_CreateProcess(NULL, handle_fli_callback, (void *)this); - } - - if (!m_sensitised) { - mti_Sensitize(m_proc_hdl, m_sig_hdl, MTI_EVENT); - m_sensitised = true; - } - set_call_state(GPI_PRIMED); - return 0; -} - -int FliSimPhaseCbHdl::arm_callback() -{ - if (NULL == m_proc_hdl) { - LOG_DEBUG("Creating a new process to sensitise with priority %d", m_priority); - m_proc_hdl = mti_CreateProcessWithPriority(NULL, handle_fli_callback, (void *)this, m_priority); - } - - if (!m_sensitised) { - mti_ScheduleWakeup(m_proc_hdl, 0); - m_sensitised = true; - } - set_call_state(GPI_PRIMED); - return 0; -} - -FliSignalCbHdl::FliSignalCbHdl(GpiImplInterface *impl, - FliSignalObjHdl *sig_hdl, - int edge) : GpiCbHdl(impl), - FliProcessCbHdl(impl), - GpiValueCbHdl(impl, sig_hdl, edge) -{ - m_sig_hdl = m_signal->get_handle(); -} - -int FliStartupCbHdl::arm_callback() -{ - mti_AddLoadDoneCB(handle_fli_callback,(void *)this); - set_call_state(GPI_PRIMED); - - return 0; -} - - -static std::vector get_argv() -{ - /* Necessary to implement PLUSARGS - There is no function available on the FLI to obtain argc+argv directly from the - simulator. To work around this we use the TCL interpreter that ships with Questa, - some TCL commands, and the TCL variable `argv` to obtain the simulator argc+argv. - */ - std::vector argv; - - // obtain a reference to TCL interpreter - Tcl_Interp* interp = reinterpret_cast(mti_Interp()); - - // get argv TCL variable - if (mti_Cmd("return -level 0 $argv") != TCL_OK) { - const char* errmsg = Tcl_GetStringResult(interp); - LOG_WARN("Failed to get reference to argv: %s", errmsg); - Tcl_ResetResult(interp); - return argv; - } - Tcl_Obj* result = Tcl_GetObjResult(interp); - Tcl_IncrRefCount(result); - Tcl_ResetResult(interp); - - // split TCL list into length and element array - int argc; - Tcl_Obj** tcl_argv; - if (Tcl_ListObjGetElements(interp, result, &argc, &tcl_argv) != TCL_OK) { - const char* errmsg = Tcl_GetStringResult(interp); - LOG_WARN("Failed to get argv elements: %s", errmsg); - Tcl_DecrRefCount(result); - Tcl_ResetResult(interp); - return argv; - } - Tcl_ResetResult(interp); - - // get each argv arg and copy into internal storage - for (int i = 0; i < argc; i++) { - const char* arg = Tcl_GetString(tcl_argv[i]); - argv.push_back(arg); - } - Tcl_DecrRefCount(result); - - return argv; -} - - -int FliStartupCbHdl::run_callback() -{ - - std::vector const argv_storage = get_argv(); - std::vector argv_cstr; - for (const auto& arg : argv_storage) { - argv_cstr.push_back(arg.c_str()); - } - int argc = static_cast(argv_storage.size()); - const char** argv = argv_cstr.data(); - - gpi_embed_init(argc, argv); - - return 0; -} - -int FliShutdownCbHdl::arm_callback() -{ - mti_AddQuitCB(handle_fli_callback,(void *)this); - set_call_state(GPI_PRIMED); - - return 0; -} - -int FliShutdownCbHdl::run_callback() -{ - gpi_embed_end(); - - return 0; -} - diff --git a/cocotb/share/lib/fli/FliImpl.cpp b/cocotb/share/lib/fli/FliImpl.cpp deleted file mode 100644 index 6a56b627..00000000 --- a/cocotb/share/lib/fli/FliImpl.cpp +++ /dev/null @@ -1,1081 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2014, 2018 Potential Ventures Ltd -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#include -#include -#include -#include - -#include "FliImpl.h" -#include "mti.h" -#include "acc_vhdl.h" // Messy :( -#include "acc_user.h" - -extern "C" { -static FliProcessCbHdl *sim_init_cb; -static FliProcessCbHdl *sim_finish_cb; -static FliImpl *fli_table; -} - -void FliImpl::sim_end() -{ - if (GPI_DELETE != sim_finish_cb->get_call_state()) { - sim_finish_cb->set_call_state(GPI_DELETE); - if (mti_NowUpper() == 0 && mti_Now() == 0 && mti_Delta() == 0) { - mti_Quit(); - } else { - mti_Break(); - } - } -} - -bool FliImpl::isValueConst(int kind) -{ - return (kind == accGeneric || kind == accVHDLConstant); -} - -bool FliImpl::isValueLogic(mtiTypeIdT type) -{ - mtiInt32T numEnums = mti_TickLength(type); - if (numEnums == 2) { - char **enum_values = mti_GetEnumValues(type); - std::string str0 = enum_values[0]; - std::string str1 = enum_values[1]; - - if (str0.compare("'0'") == 0 && str1.compare("'1'") == 0) { - return true; - } - } else if (numEnums == 9) { - const char enums[9][4] = {"'U'","'X'","'0'","'1'","'Z'","'W'","'L'","'H'","'-'"}; - char **enum_values = mti_GetEnumValues(type); - - for (int i = 0; i < 9; i++) { - std::string str = enum_values[i]; - if (str.compare(enums[i]) != 0) { - return false; - } - } - - return true; - } - - return false; -} - -bool FliImpl::isValueChar(mtiTypeIdT type) -{ - const int NUM_ENUMS_IN_CHAR_TYPE = 256; - return (mti_TickLength(type) == NUM_ENUMS_IN_CHAR_TYPE); -} - -bool FliImpl::isValueBoolean(mtiTypeIdT type) -{ - if (mti_TickLength(type) == 2) { - char **enum_values = mti_GetEnumValues(type); - std::string strFalse = enum_values[0]; - std::string strTrue = enum_values[1]; - - if (strFalse.compare("FALSE") == 0 && strTrue.compare("TRUE") == 0) { - return true; - } - } - - return false; -} - -bool FliImpl::isTypeValue(int type) -{ - return (type == accAlias || type == accVHDLConstant || type == accGeneric - || type == accVariable || type == accSignal); -} - -bool FliImpl::isTypeSignal(int type, int full_type) -{ - return (type == accSignal || full_type == accAliasSignal); -} - -GpiObjHdl *FliImpl::create_gpi_obj_from_handle(void *hdl, std::string &name, std::string &fq_name, int accType, int accFullType) -{ - GpiObjHdl *new_obj = NULL; - - LOG_DEBUG("Attempting to create GPI object from handle (Type=%d, FullType=%d).", accType, accFullType); - if (!VS_TYPE_IS_VHDL(accFullType)) { - LOG_DEBUG("Handle is not a VHDL type."); - return NULL; - } - - if (!isTypeValue(accType)) { - /* Need a Pseudo-region to handle generate loops in a consistent manner across interfaces - * and across the different methods of accessing data. - */ - std::string rgn_name = mti_GetRegionName(static_cast(hdl)); - if (name != rgn_name) { - LOG_DEBUG("Found pseudo-region %s -> %p", fq_name.c_str(), hdl); - new_obj = new FliObjHdl(this, hdl, GPI_GENARRAY, accType, accFullType); - } else { - LOG_DEBUG("Found region %s -> %p", fq_name.c_str(), hdl); - new_obj = new FliObjHdl(this, hdl, GPI_MODULE, accType, accFullType); - } - } else { - bool is_var; - bool is_const; - mtiTypeIdT valType; - mtiTypeKindT typeKind; - - if (isTypeSignal(accType, accFullType)) { - LOG_DEBUG("Found a signal %s -> %p", fq_name.c_str(), hdl); - is_var = false; - is_const = false; - valType = mti_GetSignalType(static_cast(hdl)); - } else { - LOG_DEBUG("Found a variable %s -> %p", fq_name.c_str(), hdl); - is_var = true; - is_const = isValueConst(accFullType); - valType = mti_GetVarType(static_cast(hdl)); - } - - typeKind = mti_GetTypeKind(valType); - - switch (typeKind) { - case MTI_TYPE_ENUM: - if (isValueLogic(valType)) { - new_obj = new FliLogicObjHdl(this, hdl, GPI_REGISTER, is_const, accType, accFullType, is_var, valType, typeKind); - } else if (isValueBoolean(valType) || isValueChar(valType)) { - new_obj = new FliIntObjHdl(this, hdl, GPI_INTEGER, is_const, accType, accFullType, is_var, valType, typeKind); - } else { - new_obj = new FliEnumObjHdl(this, hdl, GPI_ENUM, is_const, accType, accFullType, is_var, valType, typeKind); - } - break; - case MTI_TYPE_SCALAR: - case MTI_TYPE_PHYSICAL: - new_obj = new FliIntObjHdl(this, hdl, GPI_INTEGER, is_const, accType, accFullType, is_var, valType, typeKind); - break; - case MTI_TYPE_REAL: - new_obj = new FliRealObjHdl(this, hdl, GPI_REAL, is_const, accType, accFullType, is_var, valType, typeKind); - break; - case MTI_TYPE_ARRAY: { - mtiTypeIdT elemType = mti_GetArrayElementType(valType); - mtiTypeKindT elemTypeKind = mti_GetTypeKind(elemType); - - switch (elemTypeKind) { - case MTI_TYPE_ENUM: - if (isValueLogic(elemType)) { - new_obj = new FliLogicObjHdl(this, hdl, GPI_REGISTER, is_const, accType, accFullType, is_var, valType, typeKind); // std_logic_vector - } else if (isValueChar(elemType)) { - new_obj = new FliStringObjHdl(this, hdl, GPI_STRING, is_const, accType, accFullType, is_var, valType, typeKind); - } else { - new_obj = new FliValueObjHdl(this, hdl, GPI_ARRAY, false, accType, accFullType, is_var, valType, typeKind); // array of enums - } - break; - default: - new_obj = new FliValueObjHdl(this, hdl, GPI_ARRAY, false, accType, accFullType, is_var, valType, typeKind);// array of (array, Integer, Real, Record, etc.) - } - } - break; - case MTI_TYPE_RECORD: - new_obj = new FliValueObjHdl(this, hdl, GPI_STRUCTURE, false, accType, accFullType, is_var, valType, typeKind); - break; - default: - LOG_ERROR("Unable to handle object type for %s (%d)", name.c_str(), typeKind); - return NULL; - } - } - - if (NULL == new_obj) { - LOG_DEBUG("Didn't find anything named %s", fq_name.c_str()); - return NULL; - } - - if (new_obj->initialise(name,fq_name) < 0) { - LOG_ERROR("Failed to initialize the handle %s", name.c_str()); - delete new_obj; - return NULL; - } - - return new_obj; -} - -GpiObjHdl* FliImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) -{ - COCOTB_UNUSED(parent); - LOG_DEBUG("Trying to convert a raw handle to an FLI Handle."); - - const char * c_name = acc_fetch_name(raw_hdl); - const char * c_fullname = acc_fetch_fullname(raw_hdl); - - if (!c_name) { - LOG_DEBUG("Unable to query the name of the raw handle."); - return NULL; - } - - std::string name = c_name; - std::string fq_name = c_fullname; - - PLI_INT32 accType = acc_fetch_type(raw_hdl); - PLI_INT32 accFullType = acc_fetch_fulltype(raw_hdl); - - return create_gpi_obj_from_handle(raw_hdl, name, fq_name, accType, accFullType); -} - -/** - * @name Native Check Create - * @brief Determine whether a simulation object is native to FLI and create - * a handle if it is - */ -GpiObjHdl* FliImpl::native_check_create(std::string &name, GpiObjHdl *parent) -{ - bool search_rgn = false; - bool search_sig = false; - bool search_var = false; - - std::string fq_name = parent->get_fullname(); - gpi_objtype_t obj_type = parent->get_type(); - - if (fq_name == "/") { - fq_name += name; - search_rgn = true; - search_sig = true; - search_var = true; - } else if (obj_type == GPI_MODULE) { - fq_name += "/" + name; - search_rgn = true; - search_sig = true; - search_var = true; - } else if (obj_type == GPI_STRUCTURE) { - FliValueObjHdl *fli_obj = reinterpret_cast(parent); - - fq_name += "." + name; - search_rgn = false; - search_var = fli_obj->is_var(); - search_sig = !search_var; - } else { - LOG_ERROR("FLI: Parent of type %d must be of type GPI_MODULE or GPI_STRUCTURE to have a child.", obj_type); - return NULL; - } - - LOG_DEBUG("Looking for child %s from %s", name.c_str(), parent->get_name_str()); - - std::vector writable(fq_name.begin(), fq_name.end()); - writable.push_back('\0'); - - HANDLE hdl = NULL; - PLI_INT32 accType; - PLI_INT32 accFullType; - - if (search_rgn && (hdl = mti_FindRegion(&writable[0])) != NULL) { - accType = acc_fetch_type(hdl); - accFullType = acc_fetch_fulltype(hdl); - LOG_DEBUG("Found region %s -> %p", fq_name.c_str(), hdl); - LOG_DEBUG(" Type: %d", accType); - LOG_DEBUG(" Full Type: %d", accFullType); - } else if (search_sig && (hdl = mti_FindSignal(&writable[0])) != NULL) { - accType = acc_fetch_type(hdl); - accFullType = acc_fetch_fulltype(hdl); - LOG_DEBUG("Found a signal %s -> %p", fq_name.c_str(), hdl); - LOG_DEBUG(" Type: %d", accType); - LOG_DEBUG(" Full Type: %d", accFullType); - } else if (search_var && (hdl = mti_FindVar(&writable[0])) != NULL) { - accFullType = accType = mti_GetVarKind(static_cast(hdl)); - LOG_DEBUG("Found a variable %s -> %p", fq_name.c_str(), hdl); - LOG_DEBUG(" Type: %d", accType); - LOG_DEBUG(" Full Type: %d", accFullType); - } else if (search_rgn){ - mtiRegionIdT rgn; - - /* If not found, check to see if the name of a generate loop and create a pseudo-region */ - for (rgn = mti_FirstLowerRegion(parent->get_handle()); rgn != NULL; rgn = mti_NextRegion(rgn)) { - if (acc_fetch_fulltype(rgn) == accForGenerate) { - std::string rgn_name = mti_GetRegionName(static_cast(rgn)); - if (rgn_name.compare(0,name.length(),name) == 0) { - FliObj *fli_obj = dynamic_cast(parent); - return create_gpi_obj_from_handle(parent->get_handle(), name, fq_name, fli_obj->get_acc_type(), fli_obj->get_acc_full_type()); - } - } - } - } - - if (NULL == hdl) { - LOG_DEBUG("Didn't find anything named %s", &writable[0]); - return NULL; - } - - /* Generate Loops have inconsistent behavior across fli. A "name" - * without an index, i.e. dut.loop vs dut.loop(0), will attempt to map - * to index 0, if index 0 exists. If it doesn't then it won't find anything. - * - * If this unique case is hit, we need to create the Pseudo-region, with the handle - * being equivalent to the parent handle. - */ - if (accFullType == accForGenerate) { - FliObj *fli_obj = dynamic_cast(parent); - return create_gpi_obj_from_handle(parent->get_handle(), name, fq_name, fli_obj->get_acc_type(), fli_obj->get_acc_full_type()); - } - - return create_gpi_obj_from_handle(hdl, name, fq_name, accType, accFullType); -} - -/** - * @name Native Check Create - * @brief Determine whether a simulation object is native to FLI and create - * a handle if it is - */ -GpiObjHdl* FliImpl::native_check_create(int32_t index, GpiObjHdl *parent) -{ - gpi_objtype_t obj_type = parent->get_type(); - - HANDLE hdl; - PLI_INT32 accType; - PLI_INT32 accFullType; - char buff[14]; - - if (obj_type == GPI_GENARRAY) { - LOG_DEBUG("Looking for index %d from %s", index, parent->get_name_str()); - - snprintf(buff, 14, "(%d)", index); - - std::string idx = buff; - std::string name = parent->get_name() + idx; - std::string fq_name = parent->get_fullname() + idx; - - std::vector writable(fq_name.begin(), fq_name.end()); - writable.push_back('\0'); - - if ((hdl = mti_FindRegion(&writable[0])) != NULL) { - accType = acc_fetch_type(hdl); - accFullType = acc_fetch_fulltype(hdl); - LOG_DEBUG("Found region %s -> %p", fq_name.c_str(), hdl); - LOG_DEBUG(" Type: %d", accType); - LOG_DEBUG(" Full Type: %d", accFullType); - } else { - LOG_DEBUG("Didn't find anything named %s", &writable[0]); - return NULL; - } - - return create_gpi_obj_from_handle(hdl, name, fq_name, accType, accFullType); - } else if (obj_type == GPI_REGISTER || obj_type == GPI_ARRAY || obj_type == GPI_STRING) { - FliValueObjHdl *fli_obj = reinterpret_cast(parent); - - LOG_DEBUG("Looking for index %u from %s", index, parent->get_name_str()); - - if ((hdl = fli_obj->get_sub_hdl(index)) == NULL) { - LOG_DEBUG("Didn't find the index %d", index); - return NULL; - } - - snprintf(buff, 14, "(%d)", index); - - std::string idx = buff; - std::string name = parent->get_name() + idx; - std::string fq_name = parent->get_fullname() + idx; - - if (!(fli_obj->is_var())) { - accType = acc_fetch_type(hdl); - accFullType = acc_fetch_fulltype(hdl); - LOG_DEBUG("Found a signal %s -> %p", fq_name.c_str(), hdl); - LOG_DEBUG(" Type: %d", accType); - LOG_DEBUG(" Full Type: %d", accFullType); - } else { - accFullType = accType = mti_GetVarKind(static_cast(hdl)); - LOG_DEBUG("Found a variable %s -> %p", fq_name.c_str(), hdl); - LOG_DEBUG(" Type: %d", accType); - LOG_DEBUG(" Full Type: %d", accFullType); - } - return create_gpi_obj_from_handle(hdl, name, fq_name, accType, accFullType); - } else { - LOG_ERROR("FLI: Parent of type %d must be of type GPI_GENARRAY, GPI_REGISTER, GPI_ARRAY, or GPI_STRING to have an index.", obj_type); - return NULL; - } -} - -const char *FliImpl::reason_to_string(int reason) -{ - COCOTB_UNUSED(reason); - return "Who can explain it, who can tell you why?"; -} - - -/** - * @name Get current simulation time - * @brief Get current simulation time - * - * NB units depend on the simulation configuration - */ -void FliImpl::get_sim_time(uint32_t *high, uint32_t *low) -{ - *high = static_cast(mti_NowUpper()); // these functions return a int32_t for some reason - *low = static_cast(mti_Now()); -} - -void FliImpl::get_sim_precision(int32_t *precision) -{ - *precision = mti_GetResolutionLimit(); -} - -const char *FliImpl::get_simulator_product() -{ - if (m_product.empty() && m_version.empty()) { - const std::string info = mti_GetProductVersion(); // Returned pointer must not be freed, does not fail - const std::string search = " Version "; - const std::size_t found = info.find(search); - - if (found != std::string::npos) { - m_product = info.substr(0, found); - m_version = info.substr(found + search.length()); - } else { - m_product = info; - m_version = "UNKNOWN"; - } - } - return m_product.c_str(); -} - -const char *FliImpl::get_simulator_version() -{ - get_simulator_product(); - return m_version.c_str(); -} - -/** - * @name Find the root handle - * @brief Find the root handle using an optional name - * - * Get a handle to the root simulator object. This is usually the toplevel. - * - * If no name is provided, we return the first root instance. - * - * If name is provided, we check the name against the available objects until - * we find a match. If no match is found we return NULL - */ -GpiObjHdl *FliImpl::get_root_handle(const char *name) -{ - mtiRegionIdT root; - char *rgn_name; - char *rgn_fullname; - std::string root_name; - std::string root_fullname; - PLI_INT32 accType; - PLI_INT32 accFullType; - - for (root = mti_GetTopRegion(); root != NULL; root = mti_NextRegion(root)) { - LOG_DEBUG("Iterating over: %s", mti_GetRegionName(root)); - if (name == NULL || !strcmp(name, mti_GetRegionName(root))) - break; - } - - if (!root) { - goto error; - } - - rgn_name = mti_GetRegionName(root); - rgn_fullname = mti_GetRegionFullName(root); - - root_name = rgn_name; - root_fullname = rgn_fullname; - mti_VsimFree(rgn_fullname); - - LOG_DEBUG("Found toplevel: %s, creating handle....", root_name.c_str()); - - accType = acc_fetch_type(root); - accFullType = acc_fetch_fulltype(root); - - return create_gpi_obj_from_handle(root, root_name, root_fullname, accType, accFullType); - -error: - - LOG_ERROR("FLI: Couldn't find root handle %s", name); - - for (root = mti_GetTopRegion(); root != NULL; root = mti_NextRegion(root)) { - if (name == NULL) - break; - - LOG_ERROR("FLI: Toplevel instances: %s != %s...", name, mti_GetRegionName(root)); - } - return NULL; -} - - -GpiCbHdl *FliImpl::register_timed_callback(uint64_t time) -{ - FliTimedCbHdl *hdl = cache.get_timer(time); - - if (hdl->arm_callback()) { - delete(hdl); - hdl = NULL; - } - return hdl; -} - - -GpiCbHdl *FliImpl::register_readonly_callback() -{ - if (m_readonly_cbhdl.arm_callback()) { - return NULL; - } - return &m_readonly_cbhdl; -} - -GpiCbHdl *FliImpl::register_readwrite_callback() -{ - if (m_readwrite_cbhdl.arm_callback()) { - return NULL; - } - return &m_readwrite_cbhdl; -} - -GpiCbHdl *FliImpl::register_nexttime_callback() -{ - if (m_nexttime_cbhdl.arm_callback()) { - return NULL; - } - return &m_nexttime_cbhdl; -} - - -int FliImpl::deregister_callback(GpiCbHdl *gpi_hdl) -{ - return gpi_hdl->cleanup_callback(); -} - - -GpiIterator *FliImpl::iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) -{ - GpiIterator *new_iter = NULL; - - switch (type) { - case GPI_OBJECTS: - new_iter = new FliIterator(this, obj_hdl); - break; - case GPI_DRIVERS: - LOG_WARN("FLI: Drivers iterator not implemented yet"); - break; - case GPI_LOADS: - LOG_WARN("FLI: Loads iterator not implemented yet"); - break; - default: - LOG_WARN("FLI: Other iterator types not implemented yet"); - break; - } - - return new_iter; -} - -decltype(FliIterator::iterate_over) FliIterator::iterate_over = []{ - std::initializer_list region_options = { - FliIterator::OTM_CONSTANTS, - FliIterator::OTM_SIGNALS, - FliIterator::OTM_REGIONS, - }; - std::initializer_list signal_options = { - FliIterator::OTM_SIGNAL_SUB_ELEMENTS, - }; - std::initializer_list variable_options = { - FliIterator::OTM_VARIABLE_SUB_ELEMENTS, - }; - - return decltype(FliIterator::iterate_over) { - {accArchitecture, region_options}, - {accEntityVitalLevel0, region_options}, - {accArchVitalLevel0, region_options}, - {accArchVitalLevel1, region_options}, - {accBlock, region_options}, - {accCompInst, region_options}, - {accDirectInst, region_options}, - {accinlinedBlock, region_options}, - {accinlinedinnerBlock, region_options}, - {accGenerate, region_options}, - {accIfGenerate, region_options}, -#ifdef accElsifGenerate - {accElsifGenerate, region_options}, -#endif -#ifdef accElseGenerate - {accElseGenerate, region_options}, -#endif -#ifdef accCaseGenerate - {accCaseGenerate, region_options}, -#endif -#ifdef accCaseOTHERSGenerate - {accCaseOTHERSGenerate, region_options}, -#endif - {accForGenerate, region_options}, - {accConfiguration, region_options}, - - {accSignal, signal_options}, - {accSignalBit, signal_options}, - {accSignalSubComposite, signal_options}, - {accAliasSignal, signal_options}, - - {accVariable, variable_options}, - {accGeneric, variable_options}, - {accGenericConstant, variable_options}, - {accAliasConstant, variable_options}, - {accAliasGeneric, variable_options}, - {accAliasVariable, variable_options}, - {accVHDLConstant, variable_options}, - }; -}(); - -FliIterator::FliIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(impl, hdl), - m_vars(), - m_sigs(), - m_regs(), - m_currentHandles(NULL) -{ - FliObj *fli_obj = dynamic_cast(m_parent); - int type = fli_obj->get_acc_full_type(); - - LOG_DEBUG("fli_iterator::Create iterator for %s of type %d:%s", m_parent->get_fullname().c_str(), type, acc_fetch_type_str(type)); - - try { - selected = &iterate_over.at(type); - } - catch (std::out_of_range const&) { - LOG_WARN("FLI: Implementation does not know how to iterate over %s(%d)", - acc_fetch_type_str(type), type); - selected = nullptr; - return; - } - - /* Find the first mapping type that yields a valid iterator */ - for (one2many = selected->begin(); one2many != selected->end(); one2many++) { - /* GPI_GENARRAY are pseudo-regions and all that should be searched for are the sub-regions */ - if (m_parent->get_type() == GPI_GENARRAY && *one2many != FliIterator::OTM_REGIONS) { - LOG_DEBUG("fli_iterator OneToMany=%d skipped for GPI_GENARRAY type", *one2many); - continue; - } - - populate_handle_list(*one2many); - - switch (*one2many) { - case FliIterator::OTM_CONSTANTS: - case FliIterator::OTM_VARIABLE_SUB_ELEMENTS: - m_currentHandles = &m_vars; - m_iterator = m_vars.begin(); - break; - case FliIterator::OTM_SIGNALS: - case FliIterator::OTM_SIGNAL_SUB_ELEMENTS: - m_currentHandles = &m_sigs; - m_iterator = m_sigs.begin(); - break; - case FliIterator::OTM_REGIONS: - m_currentHandles = &m_regs; - m_iterator = m_regs.begin(); - break; - default: - LOG_WARN("Unhandled OneToMany Type (%d)", *one2many); - } - - if (m_iterator != m_currentHandles->end()) - break; - - LOG_DEBUG("fli_iterator OneToMany=%d returned NULL", *one2many); - } - - if (m_iterator == m_currentHandles->end()) { - LOG_DEBUG("fli_iterator return NULL for all relationships on %s (%d) kind:%s", - m_parent->get_name_str(), type, acc_fetch_type_str(type)); - selected = NULL; - return; - } - - LOG_DEBUG("Created iterator working from scope %d", - *one2many); -} - -GpiIterator::Status FliIterator::next_handle(std::string &name, GpiObjHdl **hdl, void **raw_hdl) -{ - HANDLE obj; - GpiObjHdl *new_obj; - - if (!selected) - return GpiIterator::END; - - gpi_objtype_t obj_type = m_parent->get_type(); - std::string parent_name = m_parent->get_name(); - - /* We want the next object in the current mapping. - * If the end of mapping is reached then we want to - * try next one until a new object is found - */ - do { - obj = NULL; - - if (m_iterator != m_currentHandles->end()) { - obj = *m_iterator++; - - /* For GPI_GENARRAY, only allow the generate statements through that match the name - * of the generate block. - */ - if (obj_type == GPI_GENARRAY) { - if (acc_fetch_fulltype(obj) == accForGenerate) { - std::string rgn_name = mti_GetRegionName(static_cast(obj)); - if (rgn_name.compare(0,parent_name.length(),parent_name) != 0) { - obj = NULL; - continue; - } - } else { - obj = NULL; - continue; - } - } - - break; - } else { - LOG_DEBUG("No more valid handles in the current OneToMany=%d iterator", *one2many); - } - - if (++one2many >= selected->end()) { - obj = NULL; - break; - } - - /* GPI_GENARRAY are pseudo-regions and all that should be searched for are the sub-regions */ - if (obj_type == GPI_GENARRAY && *one2many != FliIterator::OTM_REGIONS) { - LOG_DEBUG("fli_iterator OneToMany=%d skipped for GPI_GENARRAY type", *one2many); - continue; - } - - populate_handle_list(*one2many); - - switch (*one2many) { - case FliIterator::OTM_CONSTANTS: - case FliIterator::OTM_VARIABLE_SUB_ELEMENTS: - m_currentHandles = &m_vars; - m_iterator = m_vars.begin(); - break; - case FliIterator::OTM_SIGNALS: - case FliIterator::OTM_SIGNAL_SUB_ELEMENTS: - m_currentHandles = &m_sigs; - m_iterator = m_sigs.begin(); - break; - case FliIterator::OTM_REGIONS: - m_currentHandles = &m_regs; - m_iterator = m_regs.begin(); - break; - default: - LOG_WARN("Unhandled OneToMany Type (%d)", *one2many); - } - } while (!obj); - - if (NULL == obj) { - LOG_DEBUG("No more children, all relationships tested"); - return GpiIterator::END; - } - - char *c_name; - PLI_INT32 accType; - PLI_INT32 accFullType; - switch (*one2many) { - case FliIterator::OTM_CONSTANTS: - case FliIterator::OTM_VARIABLE_SUB_ELEMENTS: - c_name = mti_GetVarName(static_cast(obj)); - accFullType = accType = mti_GetVarKind(static_cast(obj)); - break; - case FliIterator::OTM_SIGNALS: - c_name = mti_GetSignalName(static_cast(obj)); - accType = acc_fetch_type(obj); - accFullType = acc_fetch_fulltype(obj); - break; - case FliIterator::OTM_SIGNAL_SUB_ELEMENTS: - c_name = mti_GetSignalNameIndirect(static_cast(obj), NULL, 0); - accType = acc_fetch_type(obj); - accFullType = acc_fetch_fulltype(obj); - break; - case FliIterator::OTM_REGIONS: - c_name = mti_GetRegionName(static_cast(obj)); - accType = acc_fetch_type(obj); - accFullType = acc_fetch_fulltype(obj); - break; - default: - c_name = NULL; - accType = 0; - accFullType = 0; - LOG_WARN("Unhandled OneToMany Type (%d)", *one2many); - } - - if (!c_name) { - if (!VS_TYPE_IS_VHDL(accFullType)) { - *raw_hdl = (void *)obj; - return GpiIterator::NOT_NATIVE_NO_NAME; - } - - return GpiIterator::NATIVE_NO_NAME; - } - - /* - * If the parent is not a generate loop, then watch for generate handles and create - * the pseudo-region. - * - * NOTE: Taking advantage of the "caching" to only create one pseudo-region object. - * Otherwise a list would be required and checked while iterating - */ - if (*one2many == FliIterator::OTM_REGIONS && obj_type != GPI_GENARRAY && accFullType == accForGenerate) { - std::string idx_str = c_name; - std::size_t found = idx_str.find_last_of("("); - - if (found != std::string::npos && found != 0) { - FliObj *fli_obj = dynamic_cast(m_parent); - - name = idx_str.substr(0,found); - obj = m_parent->get_handle(); - accType = fli_obj->get_acc_type(); - accFullType = fli_obj->get_acc_full_type(); - } else { - LOG_WARN("Unhandled Generate Loop Format - %s", name.c_str()); - name = c_name; - } - } else { - name = c_name; - } - - if (*one2many == FliIterator::OTM_SIGNAL_SUB_ELEMENTS) { - mti_VsimFree(c_name); - } - - std::string fq_name = m_parent->get_fullname(); - if (fq_name == "/") { - fq_name += name; - } else if (*one2many == FliIterator::OTM_SIGNAL_SUB_ELEMENTS || - *one2many == FliIterator::OTM_VARIABLE_SUB_ELEMENTS || - obj_type == GPI_GENARRAY) { - std::size_t found; - - if (obj_type == GPI_STRUCTURE) { - found = name.find_last_of("."); - } else { - found = name.find_last_of("("); - } - - if (found != std::string::npos) { - fq_name += name.substr(found); - if (obj_type != GPI_GENARRAY) { - name = name.substr(found+1); - } - } else { - LOG_WARN("Unhandled Sub-Element Format - %s", name.c_str()); - fq_name += "/" + name; - } - } else { - fq_name += "/" + name; - } - - FliImpl *fli_impl = reinterpret_cast(m_impl); - new_obj = fli_impl->create_gpi_obj_from_handle(obj, name, fq_name, accType, accFullType); - if (new_obj) { - *hdl = new_obj; - return GpiIterator::NATIVE; - } else { - return GpiIterator::NOT_NATIVE; - } -} - -void FliIterator::populate_handle_list(FliIterator::OneToMany childType) -{ - switch (childType) { - case FliIterator::OTM_CONSTANTS: { - mtiRegionIdT parent = m_parent->get_handle(); - mtiVariableIdT id; - - for (id = mti_FirstVarByRegion(parent); id; id = mti_NextVar()) { - if (id) { - m_vars.push_back(id); - } - } - } - break; - case FliIterator::OTM_SIGNALS: { - mtiRegionIdT parent = m_parent->get_handle(); - mtiSignalIdT id; - - for (id = mti_FirstSignal(parent); id; id = mti_NextSignal()) { - if (id) { - m_sigs.push_back(id); - } - } - } - break; - case FliIterator::OTM_REGIONS: { - mtiRegionIdT parent = m_parent->get_handle(); - mtiRegionIdT id; - - for (id = mti_FirstLowerRegion(parent); id; id = mti_NextRegion(id)) { - if (id) { - m_regs.push_back(id); - } - } - } - break; - case FliIterator::OTM_SIGNAL_SUB_ELEMENTS: - if (m_parent->get_type() == GPI_STRUCTURE) { - mtiSignalIdT parent = m_parent->get_handle(); - - mtiTypeIdT type = mti_GetSignalType(parent); - mtiSignalIdT *ids = mti_GetSignalSubelements(parent,NULL); - - LOG_DEBUG("GPI_STRUCTURE: %d fields", mti_TickLength(type)); - for (int i = 0; i < mti_TickLength(type); i++) { - m_sigs.push_back(ids[i]); - } - mti_VsimFree(ids); - } else if (m_parent->get_indexable()) { - FliValueObjHdl *fli_obj = reinterpret_cast(m_parent); - - int left = m_parent->get_range_left(); - int right = m_parent->get_range_right(); - - if (left > right) { - for (int i = left; i >= right; i--) { - m_sigs.push_back(static_cast(fli_obj->get_sub_hdl(i))); - } - } else { - for (int i = left; i <= right; i++) { - m_sigs.push_back(static_cast(fli_obj->get_sub_hdl(i))); - } - } - } - break; - case FliIterator::OTM_VARIABLE_SUB_ELEMENTS: - if (m_parent->get_type() == GPI_STRUCTURE) { - mtiVariableIdT parent = m_parent->get_handle(); - - mtiTypeIdT type = mti_GetVarType(parent); - mtiVariableIdT *ids = mti_GetVarSubelements(parent,NULL); - - LOG_DEBUG("GPI_STRUCTURE: %d fields", mti_TickLength(type)); - for (int i = 0; i < mti_TickLength(type); i++) { - m_vars.push_back(ids[i]); - } - - mti_VsimFree(ids); - } else if (m_parent->get_indexable()) { - FliValueObjHdl *fli_obj = reinterpret_cast(m_parent); - - int left = m_parent->get_range_left(); - int right = m_parent->get_range_right(); - - if (left > right) { - for (int i = left; i >= right; i--) { - m_vars.push_back(static_cast(fli_obj->get_sub_hdl(i))); - } - } else { - for (int i = left; i <= right; i++) { - m_vars.push_back(static_cast(fli_obj->get_sub_hdl(i))); - } - } - } - break; - default: - LOG_WARN("Unhandled OneToMany Type (%d)", childType); - } -} - - -FliTimedCbHdl* FliTimerCache::get_timer(uint64_t time) -{ - FliTimedCbHdl *hdl; - - if (!free_list.empty()) { - hdl = free_list.front(); - free_list.pop(); - hdl->reset_time(time); - } else { - hdl = new FliTimedCbHdl(impl, time); - } - - return hdl; -} - -void FliTimerCache::put_timer(FliTimedCbHdl* hdl) -{ - free_list.push(hdl); -} - -extern "C" { - -// Main re-entry point for callbacks from simulator -void handle_fli_callback(void *data) -{ - fflush(stderr); - - FliProcessCbHdl *cb_hdl = (FliProcessCbHdl*)data; - - if (!cb_hdl) { - LOG_CRITICAL("FLI: Callback data corrupted: ABORTING"); - gpi_embed_end(); - return; - } - - gpi_cb_state_e old_state = cb_hdl->get_call_state(); - - if (old_state == GPI_PRIMED) { - - cb_hdl->set_call_state(GPI_CALL); - - cb_hdl->run_callback(); - gpi_cb_state_e new_state = cb_hdl->get_call_state(); - - /* We have re-primed in the handler */ - if (new_state != GPI_PRIMED) - if (cb_hdl->cleanup_callback()) - delete cb_hdl; - } else { - /* Issue #188 seems to appear via FLI as well */ - cb_hdl->cleanup_callback(); - } -}; - -static void register_initial_callback() -{ - sim_init_cb = new FliStartupCbHdl(fli_table); - sim_init_cb->arm_callback(); -} - -static void register_final_callback() -{ - sim_finish_cb = new FliShutdownCbHdl(fli_table); - sim_finish_cb->arm_callback(); -} - -static void register_embed() -{ - fli_table = new FliImpl("FLI"); - gpi_register_impl(fli_table); -} - - -void cocotb_init() -{ - LOG_INFO("cocotb_init called"); - register_embed(); - gpi_load_extra_libs(); - register_initial_callback(); - register_final_callback(); -} - -} // extern "C" - -GPI_ENTRY_POINT(cocotbfli, register_embed); - diff --git a/cocotb/share/lib/fli/FliImpl.h b/cocotb/share/lib/fli/FliImpl.h deleted file mode 100644 index 48d8f029..00000000 --- a/cocotb/share/lib/fli/FliImpl.h +++ /dev/null @@ -1,506 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2014 Potential Ventures Ltd -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#ifndef COCOTB_FLI_IMPL_H_ -#define COCOTB_FLI_IMPL_H_ - -#include -#ifdef COCOTBFLI_EXPORTS -#define COCOTBFLI_EXPORT COCOTB_EXPORT -#else -#define COCOTBFLI_EXPORT COCOTB_IMPORT -#endif - -#include "../gpi/gpi_priv.h" -#include "mti.h" - -#include -#include - -extern "C" { -COCOTBFLI_EXPORT void cocotb_init(); -void handle_fli_callback(void *data); -} - -class FliImpl; -class FliSignalObjHdl; - -// Callback handles - -// In FLI some callbacks require us to register a process -// We use a subclass to track the process state related to the callback -class FliProcessCbHdl : public virtual GpiCbHdl { -public: - FliProcessCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - m_proc_hdl(NULL), - m_sensitised(false) { } - - int cleanup_callback() override; - -protected: - mtiProcessIdT m_proc_hdl; - bool m_sensitised; -}; - -// One class of callbacks uses mti_Sensitize to react to a signal -class FliSignalCbHdl : public FliProcessCbHdl, public GpiValueCbHdl { - -public: - FliSignalCbHdl(GpiImplInterface *impl, - FliSignalObjHdl *sig_hdl, - int edge); - - int arm_callback() override; - int cleanup_callback() override { - return FliProcessCbHdl::cleanup_callback(); - } - -private: - mtiSignalIdT m_sig_hdl; -}; - -// All other callbacks are related to the simulation phasing -class FliSimPhaseCbHdl : public FliProcessCbHdl { - -public: - FliSimPhaseCbHdl(GpiImplInterface *impl, mtiProcessPriorityT priority) : GpiCbHdl(impl), - FliProcessCbHdl(impl), - m_priority(priority) { } - - int arm_callback() override; - -protected: - mtiProcessPriorityT m_priority; -}; - -// FIXME templates? -class FliReadWriteCbHdl : public FliSimPhaseCbHdl { -public: - FliReadWriteCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - FliSimPhaseCbHdl(impl, MTI_PROC_SYNCH) { } -}; - -class FliNextPhaseCbHdl : public FliSimPhaseCbHdl { -public: - FliNextPhaseCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - FliSimPhaseCbHdl(impl, MTI_PROC_IMMEDIATE) { } -}; - -class FliReadOnlyCbHdl : public FliSimPhaseCbHdl { -public: - FliReadOnlyCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - FliSimPhaseCbHdl(impl, MTI_PROC_POSTPONED) { } -}; - -class FliStartupCbHdl : public FliProcessCbHdl { -public: - FliStartupCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - FliProcessCbHdl(impl) { } - - int arm_callback() override; - int run_callback() override; -}; - -class FliShutdownCbHdl : public FliProcessCbHdl { -public: - FliShutdownCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - FliProcessCbHdl(impl) { } - - int arm_callback() override; - int run_callback() override; -}; - -class FliTimedCbHdl : public FliProcessCbHdl { -public: - FliTimedCbHdl(GpiImplInterface *impl, uint64_t time); - - int arm_callback() override; - void reset_time(uint64_t new_time) { - m_time = new_time; - } - int cleanup_callback() override; -private: - uint64_t m_time; -}; - - -// Object Handles -class FliObj { -public: - FliObj(int acc_type, - int acc_full_type) : - m_acc_type(acc_type), - m_acc_full_type(acc_full_type) { } - - virtual ~FliObj() = default; - - int get_acc_type() { return m_acc_type; } - int get_acc_full_type() { return m_acc_full_type; } - - -protected: - int m_acc_type; - int m_acc_full_type; -}; - -class FliObjHdl : public GpiObjHdl, public FliObj { -public: - FliObjHdl( - GpiImplInterface *impl, - void *hdl, - gpi_objtype_t objtype, - int acc_type, - int acc_full_type, - bool is_const = false - ) : - GpiObjHdl(impl, hdl, objtype, is_const), - FliObj(acc_type, acc_full_type) { } - - - int initialise(std::string &name, std::string &fq_name) override; -}; - -class FliSignalObjHdl : public GpiSignalObjHdl, public FliObj { -public: - FliSignalObjHdl(GpiImplInterface *impl, - void *hdl, - gpi_objtype_t objtype, - bool is_const, - int acc_type, - int acc_full_type, - bool is_var) : - GpiSignalObjHdl(impl, hdl, objtype, is_const), - FliObj(acc_type, acc_full_type), - m_is_var(is_var), - m_rising_cb(impl, this, GPI_RISING), - m_falling_cb(impl, this, GPI_FALLING), - m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } - - - GpiCbHdl *value_change_cb(int edge) override; - int initialise(std::string &name, std::string &fq_name) override; - - bool is_var() { return m_is_var; } - -protected: - bool m_is_var; - FliSignalCbHdl m_rising_cb; - FliSignalCbHdl m_falling_cb; - FliSignalCbHdl m_either_cb; -}; - -class FliValueObjHdl : public FliSignalObjHdl { -public: - FliValueObjHdl(GpiImplInterface *impl, - void *hdl, - gpi_objtype_t objtype, - bool is_const, - int acc_type, - int acc_full_type, - bool is_var, - mtiTypeIdT valType, - mtiTypeKindT typeKind) : - FliSignalObjHdl(impl, hdl, objtype, is_const, acc_type, acc_full_type, is_var), - m_fli_type(typeKind), - m_val_type(valType) { } - - ~FliValueObjHdl() override { - if (m_val_buff != NULL) - delete [] m_val_buff; - if (m_sub_hdls != NULL) - mti_VsimFree(m_sub_hdls); - } - - const char* get_signal_value_binstr() override; - const char* get_signal_value_str() override; - double get_signal_value_real() override; - long get_signal_value_long() override; - - int set_signal_value(int32_t value, gpi_set_action_t action) override; - int set_signal_value(double value, gpi_set_action_t action) override; - int set_signal_value_str(std::string &value, gpi_set_action_t action) override; - int set_signal_value_binstr(std::string &value, gpi_set_action_t action) override; - - void *get_sub_hdl(int index); - - int initialise(std::string &name, std::string &fq_name) override; - - mtiTypeKindT get_fli_typekind() { return m_fli_type; } - mtiTypeIdT get_fli_typeid() { return m_val_type; } - -protected: - mtiTypeKindT m_fli_type; - mtiTypeIdT m_val_type; - char *m_val_buff = nullptr; - void **m_sub_hdls = nullptr; -}; - -class FliEnumObjHdl : public FliValueObjHdl { -public: - FliEnumObjHdl(GpiImplInterface *impl, - void *hdl, - gpi_objtype_t objtype, - bool is_const, - int acc_type, - int acc_full_type, - bool is_var, - mtiTypeIdT valType, - mtiTypeKindT typeKind) : - FliValueObjHdl(impl, hdl, objtype, is_const, acc_type, acc_full_type, is_var, valType, typeKind) { } - - - const char* get_signal_value_str() override; - long get_signal_value_long() override; - - using FliValueObjHdl::set_signal_value; - int set_signal_value(int32_t value, gpi_set_action_t action) override; - - int initialise(std::string &name, std::string &fq_name) override; - -private: - char **m_value_enum = nullptr; // Do Not Free - mtiInt32T m_num_enum = 0; -}; - -class FliLogicObjHdl : public FliValueObjHdl { -public: - FliLogicObjHdl(GpiImplInterface *impl, - void *hdl, - gpi_objtype_t objtype, - bool is_const, - int acc_type, - int acc_full_type, - bool is_var, - mtiTypeIdT valType, - mtiTypeKindT typeKind) : - FliValueObjHdl(impl, - hdl, - objtype, - is_const, - acc_type, - acc_full_type, - is_var, - valType, - typeKind) - { } - - ~FliLogicObjHdl() override { - if (m_mti_buff != NULL) - delete [] m_mti_buff; - } - - const char* get_signal_value_binstr() override; - - using FliValueObjHdl::set_signal_value; - int set_signal_value(int32_t value, gpi_set_action_t action) override; - int set_signal_value_binstr(std::string &value, gpi_set_action_t action) override; - - int initialise(std::string &name, std::string &fq_name) override; - - -private: - char *m_mti_buff = nullptr; - char **m_value_enum = nullptr; // Do Not Free - mtiInt32T m_num_enum = 0; - std::map m_enum_map; -}; - -class FliIntObjHdl : public FliValueObjHdl { -public: - FliIntObjHdl(GpiImplInterface *impl, - void *hdl, - gpi_objtype_t objtype, - bool is_const, - int acc_type, - int acc_full_type, - bool is_var, - mtiTypeIdT valType, - mtiTypeKindT typeKind) : - FliValueObjHdl(impl, hdl, objtype, is_const, acc_type, acc_full_type, is_var, valType, typeKind) { } - - - const char* get_signal_value_binstr() override; - long get_signal_value_long() override; - - using FliValueObjHdl::set_signal_value; - int set_signal_value(int32_t value, gpi_set_action_t action) override; - - int initialise(std::string &name, std::string &fq_name) override; -}; - -class FliRealObjHdl : public FliValueObjHdl { -public: - FliRealObjHdl(GpiImplInterface *impl, - void *hdl, - gpi_objtype_t objtype, - bool is_const, - int acc_type, - int acc_full_type, - bool is_var, - mtiTypeIdT valType, - mtiTypeKindT typeKind) : - FliValueObjHdl(impl, hdl, objtype, is_const, acc_type, acc_full_type, is_var, valType, typeKind) { } - - ~FliRealObjHdl() override { - if (m_mti_buff != NULL) - delete m_mti_buff; - } - - double get_signal_value_real() override; - - using FliValueObjHdl::set_signal_value; - int set_signal_value(double value, gpi_set_action_t action) override; - - int initialise(std::string &name, std::string &fq_name) override; - -private: - double *m_mti_buff = nullptr; -}; - -class FliStringObjHdl : public FliValueObjHdl { -public: - FliStringObjHdl(GpiImplInterface *impl, - void *hdl, - gpi_objtype_t objtype, - bool is_const, - int acc_type, - int acc_full_type, - bool is_var, - mtiTypeIdT valType, - mtiTypeKindT typeKind) : - FliValueObjHdl(impl, hdl, objtype, is_const, acc_type, acc_full_type, is_var, valType, typeKind) { } - - ~FliStringObjHdl() override { - if (m_mti_buff != NULL) - delete [] m_mti_buff; - } - - const char* get_signal_value_str() override; - - using FliValueObjHdl::set_signal_value; - int set_signal_value_str(std::string &value, gpi_set_action_t action) override; - - int initialise(std::string &name, std::string &fq_name) override; - -private: - char *m_mti_buff = nullptr; -}; - -class FliTimerCache { -public: - FliTimerCache(FliImpl* impl) : impl(impl) { } - - FliTimedCbHdl* get_timer(uint64_t time); - void put_timer(FliTimedCbHdl*); - -private: - std::queue free_list; - FliImpl *impl; -}; - -class FliIterator : public GpiIterator { -public: - enum OneToMany { - OTM_CONSTANTS, // include Generics - OTM_SIGNALS, - OTM_REGIONS, - OTM_SIGNAL_SUB_ELEMENTS, - OTM_VARIABLE_SUB_ELEMENTS - }; - - FliIterator(GpiImplInterface *impl, GpiObjHdl *hdl); - - Status next_handle(std::string &name, GpiObjHdl **hdl, void **raw_hdl) override; - -private: - void populate_handle_list(OneToMany childType); - -private: - static std::map> iterate_over; /* Possible mappings */ - std::vector *selected; /* Mapping currently in use */ - std::vector::iterator one2many; - - std::vector m_vars; - std::vector m_sigs; - std::vector m_regs; - std::vector *m_currentHandles; - std::vector::iterator m_iterator; -}; - -class FliImpl : public GpiImplInterface { -public: - FliImpl(const std::string& name) : GpiImplInterface(name), - cache(this), - m_readonly_cbhdl(this), - m_nexttime_cbhdl(this), - m_readwrite_cbhdl(this) { } - - /* Sim related */ - void sim_end() override; - void get_sim_time(uint32_t *high, uint32_t *low) override; - void get_sim_precision(int32_t *precision) override; - const char *get_simulator_product() override; - const char *get_simulator_version() override; - - /* Hierachy related */ - GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent) override; - GpiObjHdl* native_check_create(int32_t index, GpiObjHdl *parent) override; - GpiObjHdl* native_check_create(void *raw_hdl, GpiObjHdl *paret) override; - GpiObjHdl *get_root_handle(const char *name) override; - GpiIterator *iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) override; - - /* Callback related, these may (will) return the same handle*/ - GpiCbHdl *register_timed_callback(uint64_t time) override; - GpiCbHdl *register_readonly_callback() override; - GpiCbHdl *register_nexttime_callback() override; - GpiCbHdl *register_readwrite_callback() override; - int deregister_callback(GpiCbHdl *obj_hdl) override; - - /* Method to provide strings from operation types */ - const char *reason_to_string(int reason) override; - - /* Method to provide strings from operation types */ - GpiObjHdl *create_gpi_obj_from_handle(void *hdl, std::string &name, std::string &fq_name, int accType, int accFullType); - -private: - bool isValueConst(int kind); - bool isValueLogic(mtiTypeIdT type); - bool isValueChar(mtiTypeIdT type); - bool isValueBoolean(mtiTypeIdT type); - bool isTypeValue(int type); - bool isTypeSignal(int type, int full_type); - -public: - FliTimerCache cache; - -private: - FliReadOnlyCbHdl m_readonly_cbhdl; - FliNextPhaseCbHdl m_nexttime_cbhdl; - FliReadWriteCbHdl m_readwrite_cbhdl; -}; - -#endif /*COCOTB_FLI_IMPL_H_ */ diff --git a/cocotb/share/lib/fli/FliObjHdl.cpp b/cocotb/share/lib/fli/FliObjHdl.cpp deleted file mode 100644 index ce3c17bc..00000000 --- a/cocotb/share/lib/fli/FliObjHdl.cpp +++ /dev/null @@ -1,539 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2015/16 Potential Ventures Ltd -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#include -#include - -#include "FliImpl.h" -#include "acc_vhdl.h" - -GpiCbHdl *FliSignalObjHdl::value_change_cb(int edge) -{ - FliSignalCbHdl *cb = NULL; - - if (m_is_var) { - return NULL; - } - - switch (edge) { - case 1: - cb = &m_rising_cb; - break; - case 2: - cb = &m_falling_cb; - break; - case 3: - cb = &m_either_cb; - break; - default: - return NULL; - } - - if (cb->arm_callback()) { - return NULL; - } - - return (GpiCbHdl *)cb; -} - -int FliObjHdl::initialise(std::string &name, std::string &fq_name) -{ - bool is_signal = (get_acc_type() == accSignal || get_acc_full_type() == accAliasSignal); - mtiTypeIdT typeId; - char * str; - - switch (get_type()) { - case GPI_STRUCTURE: - if (is_signal) { - typeId = mti_GetSignalType(get_handle()); - } else { - typeId = mti_GetVarType(get_handle()); - } - - m_num_elems = mti_GetNumRecordElements(typeId); - break; - case GPI_GENARRAY: - m_indexable = true; - // fall through - case GPI_MODULE: - m_num_elems = 1; - break; - default: - LOG_ERROR("Invalid object type for FliObjHdl. (%s (%s))", name.c_str(), get_type_str()); - return -1; - } - - str = mti_GetPrimaryName(get_handle()); - if (str != NULL) - m_definition_name = str; - - str = mti_GetRegionSourceName(get_handle()); - if (str != NULL) - m_definition_file = str; - - return GpiObjHdl::initialise(name, fq_name); -} - - -int FliSignalObjHdl::initialise(std::string &name, std::string &fq_name) -{ - return GpiObjHdl::initialise(name, fq_name); -} - -int FliValueObjHdl::initialise(std::string &name, std::string &fq_name) -{ - if (get_type() == GPI_ARRAY) { - m_range_left = mti_TickLeft(m_val_type); - m_range_right = mti_TickRight(m_val_type); - m_num_elems = mti_TickLength(m_val_type); - m_indexable = true; - } - - return FliSignalObjHdl::initialise(name, fq_name); -} - -const char* FliValueObjHdl::get_signal_value_binstr() -{ - LOG_ERROR("Getting signal/variable value as binstr not supported for %s of type %d", m_fullname.c_str(), m_type); - return NULL; -} - -const char* FliValueObjHdl::get_signal_value_str() -{ - LOG_ERROR("Getting signal/variable value as str not supported for %s of type %d", m_fullname.c_str(), m_type); - return NULL; -} - -double FliValueObjHdl::get_signal_value_real() -{ - LOG_ERROR("Getting signal/variable value as double not supported for %s of type %d", m_fullname.c_str(), m_type); - return -1; -} - -long FliValueObjHdl::get_signal_value_long() -{ - LOG_ERROR("Getting signal/variable value as long not supported for %s of type %d", m_fullname.c_str(), m_type); - return -1; -} - -int FliValueObjHdl::set_signal_value(int32_t value, gpi_set_action_t action) -{ - COCOTB_UNUSED(value); - COCOTB_UNUSED(action); - LOG_ERROR("Setting signal/variable value via int32_t not supported for %s of type %d", m_fullname.c_str(), m_type); - return -1; -} - -int FliValueObjHdl::set_signal_value_binstr(std::string &value, gpi_set_action_t action) -{ - COCOTB_UNUSED(value); - COCOTB_UNUSED(action); - LOG_ERROR("Setting signal/variable value via string not supported for %s of type %d", m_fullname.c_str(), m_type); - return -1; -} - -int FliValueObjHdl::set_signal_value_str(std::string &value, gpi_set_action_t action) -{ - COCOTB_UNUSED(value); - COCOTB_UNUSED(action); - LOG_ERROR("Setting signal/variable value via string not supported for %s of type %d", m_fullname.c_str(), m_type); - return -1; -} - -int FliValueObjHdl::set_signal_value(double value, gpi_set_action_t action) -{ - COCOTB_UNUSED(value); - COCOTB_UNUSED(action); - LOG_ERROR("Setting signal/variable value via double not supported for %s of type %d", m_fullname.c_str(), m_type); - return -1; -} - -void *FliValueObjHdl::get_sub_hdl(int index) { - if (!m_indexable) - return NULL; - - if (m_sub_hdls == NULL) { - if (is_var()) { - m_sub_hdls = (void **)mti_GetVarSubelements(get_handle(), NULL); - } else { - m_sub_hdls = (void **)mti_GetSignalSubelements(get_handle(), NULL); - } - } - - int idx; - - if (m_range_left > m_range_right) { - idx = m_range_left - index; - } else { - idx = index - m_range_left; - } - - if (idx < 0 || idx >= m_num_elems) - return NULL; - else - return m_sub_hdls[idx]; -} - -int FliEnumObjHdl::initialise(std::string &name, std::string &fq_name) -{ - m_num_elems = 1; - m_value_enum = mti_GetEnumValues(m_val_type); - m_num_enum = mti_TickLength(m_val_type); - - return FliValueObjHdl::initialise(name, fq_name); -} - -const char* FliEnumObjHdl::get_signal_value_str() -{ - if (m_is_var) { - return m_value_enum[mti_GetVarValue(get_handle())]; - } else { - return m_value_enum[mti_GetSignalValue(get_handle())]; - } -} - -long FliEnumObjHdl::get_signal_value_long() -{ - if (m_is_var) { - return (long)mti_GetVarValue(get_handle()); - } else { - return (long)mti_GetSignalValue(get_handle()); - } -} - -int FliEnumObjHdl::set_signal_value(const int32_t value, const gpi_set_action_t action) -{ - if (action != GPI_DEPOSIT) { - LOG_ERROR("Force or release action not supported for FLI."); - return -1; - } - - if (value > m_num_enum || value < 0) { - LOG_ERROR("Attempted to set an enum with range [0,%d] with invalid value %d!", m_num_enum, value); - return -1; - } - - if (m_is_var) { - mti_SetVarValue(get_handle(), static_cast(value)); - } else { - mti_SetSignalValue(get_handle(), static_cast(value)); - } - - return 0; -} - -int FliLogicObjHdl::initialise(std::string &name, std::string &fq_name) -{ - switch (m_fli_type) { - case MTI_TYPE_ENUM: - m_num_elems = 1; - m_value_enum = mti_GetEnumValues(m_val_type); - m_num_enum = mti_TickLength(m_val_type); - break; - case MTI_TYPE_ARRAY: { - mtiTypeIdT elemType = mti_GetArrayElementType(m_val_type); - - m_range_left = mti_TickLeft(m_val_type); - m_range_right = mti_TickRight(m_val_type); - m_num_elems = mti_TickLength(m_val_type); - m_indexable = true; - - m_value_enum = mti_GetEnumValues(elemType); - m_num_enum = mti_TickLength(elemType); - - m_mti_buff = new char[m_num_elems+1]; - } - break; - default: - LOG_ERROR("Object type is not 'logic' for %s (%d)", name.c_str(), m_fli_type); - return -1; - } - - for (mtiInt32T i = 0; i < m_num_enum; i++) { - m_enum_map[m_value_enum[i][1]] = i; // enum is of the format 'U' or '0', etc. - } - - m_val_buff = new char[m_num_elems+1]; - m_val_buff[m_num_elems] = '\0'; - - return FliValueObjHdl::initialise(name, fq_name); -} - -const char* FliLogicObjHdl::get_signal_value_binstr() -{ - switch (m_fli_type) { - case MTI_TYPE_ENUM: - if (m_is_var) { - m_val_buff[0] = m_value_enum[mti_GetVarValue(get_handle())][1]; - } else { - m_val_buff[0] = m_value_enum[mti_GetSignalValue(get_handle())][1]; - } - break; - case MTI_TYPE_ARRAY: { - if (m_is_var) { - mti_GetArrayVarValue(get_handle(), m_mti_buff); - } else { - mti_GetArraySignalValue(get_handle(), m_mti_buff); - } - - for (int i = 0; i < m_num_elems; i++ ) { - m_val_buff[i] = m_value_enum[(int)m_mti_buff[i]][1]; - } - } - break; - default: - LOG_ERROR("Object type is not 'logic' for %s (%d)", m_name.c_str(), m_fli_type); - return NULL; - } - - LOG_DEBUG("Retrieved \"%s\" for value object %s", m_val_buff, m_name.c_str()); - - return m_val_buff; -} - -int FliLogicObjHdl::set_signal_value(const int32_t value, const gpi_set_action_t action) -{ - if (action != GPI_DEPOSIT) { - LOG_ERROR("Force or release action not supported for FLI."); - return -1; - } - - if (m_fli_type == MTI_TYPE_ENUM) { - mtiInt32T enumVal = value ? m_enum_map['1'] : m_enum_map['0']; - - if (m_is_var) { - mti_SetVarValue(get_handle(), enumVal); - } else { - mti_SetSignalValue(get_handle(), enumVal); - } - } else { - LOG_DEBUG("set_signal_value(int32_t)::0x%08x", value); - for (int i = 0, idx = m_num_elems-1; i < m_num_elems; i++, idx--) { - mtiInt32T enumVal = value&(1<(), (mtiLongT)m_mti_buff); - } else { - mti_SetSignalValue(get_handle(), (mtiLongT)m_mti_buff); - } - } - - return 0; -} - -int FliLogicObjHdl::set_signal_value_binstr(std::string &value, const gpi_set_action_t action) -{ - if (action != GPI_DEPOSIT) { - LOG_ERROR("Force or release action not supported for FLI."); - return -1; - } - - if (m_fli_type == MTI_TYPE_ENUM) { - mtiInt32T enumVal = m_enum_map[value.c_str()[0]]; - - if (m_is_var) { - mti_SetVarValue(get_handle(), enumVal); - } else { - mti_SetSignalValue(get_handle(), enumVal); - } - } else { - - if ((int)value.length() != m_num_elems) { - LOG_ERROR("FLI: Unable to set logic vector due to the string having incorrect length. Length of %d needs to be %d", value.length(), m_num_elems); - return -1; - } - - LOG_DEBUG("set_signal_value(string)::%s", value.c_str()); - - mtiInt32T enumVal; - std::string::iterator valIter; - int i = 0; - - for (valIter = value.begin(); (valIter != value.end()) && (i < m_num_elems); valIter++, i++) { - enumVal = m_enum_map[*valIter]; - m_mti_buff[i] = (char)enumVal; - } - - if (m_is_var) { - mti_SetVarValue(get_handle(), (mtiLongT)m_mti_buff); - } else { - mti_SetSignalValue(get_handle(), (mtiLongT)m_mti_buff); - } - } - - return 0; -} - -int FliIntObjHdl::initialise(std::string &name, std::string &fq_name) -{ - m_num_elems = 1; - - m_val_buff = new char[33]; // Integers are always 32-bits - m_val_buff[m_num_elems] = '\0'; - - return FliValueObjHdl::initialise(name, fq_name); -} - -const char* FliIntObjHdl::get_signal_value_binstr() -{ - mtiInt32T val; - - if (m_is_var) { - val = mti_GetVarValue(get_handle()); - } else { - val = mti_GetSignalValue(get_handle()); - } - - unsigned long tmp = static_cast(val); // only way to keep next line from warning - std::bitset<32> value {tmp}; - std::string bin_str = value.to_string(); - snprintf(m_val_buff, 33, "%s", bin_str.c_str()); - - return m_val_buff; -} - -long FliIntObjHdl::get_signal_value_long() -{ - mtiInt32T value; - - if (m_is_var) { - value = mti_GetVarValue(get_handle()); - } else { - value = mti_GetSignalValue(get_handle()); - } - - return (long)value; -} - -int FliIntObjHdl::set_signal_value(const int32_t value, const gpi_set_action_t action) -{ - if (action != GPI_DEPOSIT) { - LOG_ERROR("Force or release action not supported for FLI."); - return -1; - } - - if (m_is_var) { - mti_SetVarValue(get_handle(), static_cast(value)); - } else { - mti_SetSignalValue(get_handle(), static_cast(value)); - } - - return 0; -} - -int FliRealObjHdl::initialise(std::string &name, std::string &fq_name) -{ - - m_num_elems = 1; - - m_mti_buff = new double; - - return FliValueObjHdl::initialise(name, fq_name); -} - -double FliRealObjHdl::get_signal_value_real() -{ - if (m_is_var) { - mti_GetVarValueIndirect(get_handle(), m_mti_buff); - } else { - mti_GetSignalValueIndirect(get_handle(), m_mti_buff); - } - - LOG_DEBUG("Retrieved \"%f\" for value object %s", m_mti_buff[0], m_name.c_str()); - - return m_mti_buff[0]; -} - -int FliRealObjHdl::set_signal_value(const double value, const gpi_set_action_t action) -{ - if (action != GPI_DEPOSIT) { - LOG_ERROR("Force or release action not supported for FLI."); - return -1; - } - - m_mti_buff[0] = value; - - if (m_is_var) { - mti_SetVarValue(get_handle(), (mtiLongT)m_mti_buff); - } else { - mti_SetSignalValue(get_handle(), (mtiLongT)m_mti_buff); - } - - return 0; -} - -int FliStringObjHdl::initialise(std::string &name, std::string &fq_name) -{ - m_range_left = mti_TickLeft(m_val_type); - m_range_right = mti_TickRight(m_val_type); - m_num_elems = mti_TickLength(m_val_type); - m_indexable = true; - - m_mti_buff = new char[m_num_elems]; - - m_val_buff = new char[m_num_elems+1]; - m_val_buff[m_num_elems] = '\0'; - - return FliValueObjHdl::initialise(name, fq_name); -} - -const char* FliStringObjHdl::get_signal_value_str() -{ - if (m_is_var) { - mti_GetArrayVarValue(get_handle(), m_mti_buff); - } else { - mti_GetArraySignalValue(get_handle(), m_mti_buff); - } - - strncpy(m_val_buff, m_mti_buff, static_cast(m_num_elems)); - - LOG_DEBUG("Retrieved \"%s\" for value object %s", m_val_buff, m_name.c_str()); - - return m_val_buff; -} - -int FliStringObjHdl::set_signal_value_str(std::string &value, const gpi_set_action_t action) -{ - if (action != GPI_DEPOSIT) { - LOG_ERROR("Force or release action not supported for FLI."); - return -1; - } - - strncpy(m_mti_buff, value.c_str(), static_cast(m_num_elems)); - - if (m_is_var) { - mti_SetVarValue(get_handle(), (mtiLongT)m_mti_buff); - } else { - mti_SetSignalValue(get_handle(), (mtiLongT)m_mti_buff); - } - - return 0; -} - diff --git a/cocotb/share/lib/gpi/GpiCbHdl.cpp b/cocotb/share/lib/gpi/GpiCbHdl.cpp deleted file mode 100644 index 744c8b4b..00000000 --- a/cocotb/share/lib/gpi/GpiCbHdl.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2013 Potential Ventures Ltd -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd, -* SolarFlare Communications Inc nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#include "gpi_priv.h" - -const char * GpiObjHdl::get_name_str() -{ - return m_name.c_str(); -} - -const char * GpiObjHdl::get_fullname_str() -{ - return m_fullname.c_str(); -} - -const std::string & GpiObjHdl::get_fullname() -{ - return m_fullname; -} - -const char * GpiObjHdl::get_type_str() -{ -#define CASE_OPTION(_X) \ - case _X: \ - ret = #_X; \ - break - - const char *ret; - - switch (m_type) { - CASE_OPTION(GPI_UNKNOWN); - CASE_OPTION(GPI_MEMORY); - CASE_OPTION(GPI_MODULE); - CASE_OPTION(GPI_NET); - // CASE_OPTION(GPI_PARAMETER); // Deprecated - CASE_OPTION(GPI_REGISTER); - CASE_OPTION(GPI_ARRAY); - CASE_OPTION(GPI_ENUM); - CASE_OPTION(GPI_STRUCTURE); - CASE_OPTION(GPI_REAL); - CASE_OPTION(GPI_INTEGER); - CASE_OPTION(GPI_STRING); - CASE_OPTION(GPI_GENARRAY); - default: - ret = "unknown"; - } - - return ret; -} - -const std::string & GpiObjHdl::get_name() -{ - return m_name; -} - -/* Genertic base clss implementations */ -bool GpiHdl::is_this_impl(GpiImplInterface *impl) -{ - return impl == this->m_impl; -} - -int GpiObjHdl::initialise(std::string &name, std::string &fq_name) -{ - m_name = name; - m_fullname = fq_name; - return 0; -} - -int GpiCbHdl::run_callback() -{ - LOG_DEBUG("Generic run_callback"); - this->gpi_function(m_cb_data); - LOG_DEBUG("Generic run_callback done"); - return 0; -} - -int GpiCbHdl::cleanup_callback() -{ - LOG_WARN("Generic cleanup_handler"); - return 0; -} - -int GpiCbHdl::arm_callback() -{ - LOG_WARN("Generic arm_callback"); - return 0; -} - -int GpiCbHdl::set_user_data(int (*gpi_function)(const void*), const void *data) -{ - if (!gpi_function) { - LOG_ERROR("gpi_function to set_user_data is NULL"); - } - this->gpi_function = gpi_function; - this->m_cb_data = data; - return 0; -} - -const void * GpiCbHdl::get_user_data() -{ - return m_cb_data; -} - -void GpiCbHdl::set_call_state(gpi_cb_state_e new_state) -{ - m_state = new_state; -} - -gpi_cb_state_e GpiCbHdl::get_call_state() -{ - return m_state; -} - -GpiCbHdl::~GpiCbHdl() -{ - -} - -GpiValueCbHdl::GpiValueCbHdl(GpiImplInterface *impl, - GpiSignalObjHdl *signal, - int edge) : GpiCbHdl(impl), - m_signal(signal) -{ - if (edge == (GPI_RISING | GPI_FALLING)) - required_value = "X"; - else if (edge & GPI_RISING) - required_value = "1"; - else if (edge & GPI_FALLING) - required_value = "0"; -} - -int GpiValueCbHdl::run_callback() -{ - std::string current_value; - bool pass = false; - - if (required_value == "X") - pass = true; - else { - current_value = m_signal->get_signal_value_binstr(); - if (current_value == required_value) - pass = true; - } - - if (pass) { - this->gpi_function(m_cb_data); - } else { - cleanup_callback(); - arm_callback(); - } - - return 0; -} diff --git a/cocotb/share/lib/gpi/GpiCommon.cpp b/cocotb/share/lib/gpi/GpiCommon.cpp deleted file mode 100644 index 44a033b5..00000000 --- a/cocotb/share/lib/gpi/GpiCommon.cpp +++ /dev/null @@ -1,644 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2013 Potential Ventures Ltd -* Copyright (c) 2013 SolarFlare Communications Inc -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd, -* SolarFlare Communications Inc nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#include "gpi_priv.h" -#include -#include -#include -#include -#include -#include - -using namespace std; - -static vector registered_impls; - -#ifdef SINGLETON_HANDLES - -class GpiHandleStore { -public: - GpiObjHdl * check_and_store(GpiObjHdl *hdl) { - std::map::iterator it; - - const std::string &name = hdl->get_fullname(); - - LOG_DEBUG("Checking %s exists", name.c_str()); - - it = handle_map.find(name); - if (it == handle_map.end()) { - handle_map[name] = hdl; - return hdl; - } else { - LOG_DEBUG("Found duplicate %s", name.c_str()); - - delete hdl; - return it->second; - } - } - - uint64_t handle_count() { - return handle_map.size(); - } - - void clear() { - std::map::iterator it; - - // Delete the object handles before clearing the map - for (it = handle_map.begin(); it != handle_map.end(); it++) - { - delete (it->second); - } - handle_map.clear(); - } - -private: - std::map handle_map; - -}; - -static GpiHandleStore unique_handles; - -#define CHECK_AND_STORE(_x) unique_handles.check_and_store(_x) -#define CLEAR_STORE() unique_handles.clear() - -#else - -#define CHECK_AND_STORE(_x) _x -#define CLEAR_STORE() (void)0 // No-op - -#endif - - -size_t gpi_print_registered_impl() -{ - vector::iterator iter; - for (iter = registered_impls.begin(); - iter != registered_impls.end(); - iter++) - { - LOG_INFO("%s registered", (*iter)->get_name_c()); - } - return registered_impls.size(); -} - -int gpi_register_impl(GpiImplInterface *func_tbl) -{ - vector::iterator iter; - for (iter = registered_impls.begin(); - iter != registered_impls.end(); - iter++) - { - if ((*iter)->get_name_s() == func_tbl->get_name_s()) { - LOG_WARN("%s already registered, check GPI_EXTRA", func_tbl->get_name_c()); - return -1; - } - } - registered_impls.push_back(func_tbl); - return 0; -} - -bool gpi_has_registered_impl() -{ - return registered_impls.size() > 0; -} - -void gpi_embed_init(int argc, char const* const* argv) -{ - if (embed_sim_init(argc, argv)) - gpi_embed_end(); -} - -void gpi_embed_end() -{ - embed_sim_event(SIM_FAIL, "Simulator shutdown prematurely"); - gpi_cleanup(); -} - -void gpi_sim_end() -{ - registered_impls[0]->sim_end(); -} - -void gpi_cleanup(void) -{ - CLEAR_STORE(); - embed_sim_cleanup(); -} - -void gpi_embed_event(gpi_event_t level, const char *msg) -{ - embed_sim_event(level, msg); -} - -static void gpi_load_libs(std::vector to_load) -{ -#define DOT_LIB_EXT "." xstr(LIB_EXT) - std::vector::iterator iter; - - for (iter = to_load.begin(); - iter != to_load.end(); - iter++) - { - - std::string arg = *iter; - - std::string lib_name; - std::string func_name; - auto it = std::find(arg.begin(), arg.end(), ':'); - bool const has_colon = it == arg.end(); - if (has_colon) { - // no colon in the string, default - lib_name = arg; - func_name = arg + "_entry_point"; - } - else { - lib_name = std::string(arg.begin(), it); - func_name = std::string(it+1, arg.end()); - } - - std::string full_name = "lib" + lib_name + DOT_LIB_EXT; - void *lib_handle = utils_dyn_open(full_name.c_str()); - if (!lib_handle) { - printf("cocotb: Error loading shared library %s\n", full_name.c_str()); - exit(1); - } - - void *entry_point = utils_dyn_sym(lib_handle, func_name.c_str()); - if (!entry_point) { - char const* fmt = "cocotb: Unable to find entry point %s for shared library %s\n%s"; - char const* msg = " Perhaps you meant to use `,` instead of `:` to separate library names, as this changed in cocotb 1.4?\n"; - printf(fmt, func_name.c_str(), full_name.c_str(), has_colon ? msg : ""); - exit(1); - } - - layer_entry_func new_lib_entry = (layer_entry_func)entry_point; - new_lib_entry(); - } -} - -void gpi_load_extra_libs() -{ - /* Lets look at what other libs we were asked to load too */ - char *lib_env = getenv("GPI_EXTRA"); - - if (lib_env) { - std::string lib_list = lib_env; - std::string const delim = ","; - std::vector to_load; - - size_t e_pos = 0; - while (std::string::npos != (e_pos = lib_list.find(delim))) { - std::string lib = lib_list.substr(0, e_pos); - lib_list.erase(0, e_pos + delim.length()); - - to_load.push_back(lib); - } - if (lib_list.length()) { - to_load.push_back(lib_list); - } - - gpi_load_libs(to_load); - } - - /* Finally embed Python */ - embed_init_python(); - gpi_print_registered_impl(); -} - -void gpi_get_sim_time(uint32_t *high, uint32_t *low) -{ - registered_impls[0]->get_sim_time(high, low); -} - -void gpi_get_sim_precision(int32_t *precision) -{ - /* We clamp to sensible values here, 1e-15 min and 1e3 max */ - int32_t val; - registered_impls[0]->get_sim_precision(&val); - if (val > 2 ) - val = 2; - - if (val < -15) - val = -15; - - *precision = val; - -} - -const char *gpi_get_simulator_product() -{ - return registered_impls[0]->get_simulator_product(); -} - -const char *gpi_get_simulator_version() -{ - return registered_impls[0]->get_simulator_version(); -} - -gpi_sim_hdl gpi_get_root_handle(const char *name) -{ - /* May need to look over all the implementations that are registered - to find this handle */ - vector::iterator iter; - - GpiObjHdl *hdl = NULL; - - LOG_DEBUG("Looking for root handle '%s' over %d implementations", name, registered_impls.size()); - - for (iter = registered_impls.begin(); - iter != registered_impls.end(); - iter++) { - if ((hdl = (*iter)->get_root_handle(name))) { - LOG_DEBUG("Got a Root handle (%s) back from %s", - hdl->get_name_str(), - (*iter)->get_name_c()); - break; - } - } - - if (hdl) - return CHECK_AND_STORE(hdl); - else { - LOG_ERROR("No root handle found"); - return hdl; - } -} - -static GpiObjHdl* __gpi_get_handle_by_name(GpiObjHdl *parent, - std::string name, - GpiImplInterface *skip_impl) -{ - vector::iterator iter; - - GpiObjHdl *hdl = NULL; - - LOG_DEBUG("Searching for %s", name.c_str()); - - for (iter = registered_impls.begin(); - iter != registered_impls.end(); - iter++) { - - if (skip_impl && (skip_impl == (*iter))) { - LOG_DEBUG("Skipping %s implementation", (*iter)->get_name_c()); - continue; - } - - LOG_DEBUG("Checking if %s is native through implementation %s", - name.c_str(), - (*iter)->get_name_c()); - - /* If the current interface is not the same as the one that we - are going to query then append the name we are looking for to - the parent, such as .name. This is so that its entity can - be seen discovered even if the parents implementation is not the same - as the one that we are querying through */ - - //std::string &to_query = base->is_this_impl(*iter) ? s_name : fq_name; - if ((hdl = (*iter)->native_check_create(name, parent))) { - LOG_DEBUG("Found %s via %s", name.c_str(), (*iter)->get_name_c()); - break; - } - } - - if (hdl) - return CHECK_AND_STORE(hdl); - else - return hdl; -} - -static GpiObjHdl* __gpi_get_handle_by_raw(GpiObjHdl *parent, - void *raw_hdl, - GpiImplInterface *skip_impl) -{ - vector::iterator iter; - - GpiObjHdl *hdl = NULL; - - for (iter = registered_impls.begin(); - iter != registered_impls.end(); - iter++) { - - if (skip_impl && (skip_impl == (*iter))) { - LOG_DEBUG("Skipping %s implementation", (*iter)->get_name_c()); - continue; - } - - if ((hdl = (*iter)->native_check_create(raw_hdl, parent))) { - LOG_DEBUG("Found %s via %s", hdl->get_name_str(), (*iter)->get_name_c()); - break; - } - } - - if (hdl) - return CHECK_AND_STORE(hdl); - else { - LOG_WARN("Failed to convert a raw handle to valid object via any registered implementation"); - return hdl; - } -} - -gpi_sim_hdl gpi_get_handle_by_name(gpi_sim_hdl base, const char *name) -{ - std::string s_name = name; - GpiObjHdl *hdl = __gpi_get_handle_by_name(base, s_name, NULL); - if (!hdl) { - LOG_DEBUG("Failed to find a handle named %s via any registered implementation", - name); - } - return hdl; -} - -gpi_sim_hdl gpi_get_handle_by_index(gpi_sim_hdl base, int32_t index) -{ - GpiObjHdl *hdl = NULL; - GpiImplInterface *intf = base->m_impl; - - /* Shouldn't need to iterate over interfaces because indexing into a handle shouldn't - * cross the interface boundaries. - * - * NOTE: IUS's VPI interface returned valid VHDL handles, but then couldn't - * use the handle properly. - */ - LOG_DEBUG("Checking if index %d native through implementation %s ", index, intf->get_name_c()); - hdl = intf->native_check_create(index, base); - - if (hdl) - return CHECK_AND_STORE(hdl); - else { - LOG_WARN("Failed to find a handle at index %d via any registered implementation", index); - return hdl; - } -} - -gpi_iterator_hdl gpi_iterate(gpi_sim_hdl obj_hdl, gpi_iterator_sel_t type) -{ - GpiIterator *iter = obj_hdl->m_impl->iterate_handle(obj_hdl, type); - if (!iter) { - return NULL; - } - return iter; -} - -gpi_sim_hdl gpi_next(gpi_iterator_hdl iter) -{ - std::string name; - GpiObjHdl *parent = iter->get_parent(); - - while (true) { - GpiObjHdl *next = NULL; - void *raw_hdl = NULL; - GpiIterator::Status ret = iter->next_handle(name, &next, &raw_hdl); - - switch (ret) { - case GpiIterator::NATIVE: - LOG_DEBUG("Create a native handle"); - return CHECK_AND_STORE(next); - case GpiIterator::NATIVE_NO_NAME: - LOG_DEBUG("Unable to fully setup handle, skipping"); - continue; - case GpiIterator::NOT_NATIVE: - LOG_DEBUG("Found a name but unable to create via native implementation, trying others"); - next = __gpi_get_handle_by_name(parent, name, iter->m_impl); - if (next) { - return next; - } - LOG_WARN("Unable to create %s via any registered implementation", name.c_str()); - continue; - case GpiIterator::NOT_NATIVE_NO_NAME: - LOG_DEBUG("Found an object but not accessible via %s, trying others", iter->m_impl->get_name_c()); - next = __gpi_get_handle_by_raw(parent, raw_hdl, iter->m_impl); - if (next) { - return next; - } - continue; - case GpiIterator::END: - LOG_DEBUG("Reached end of iterator"); - delete iter; - return NULL; - } - } -} - -const char* gpi_get_definition_name(gpi_sim_hdl obj_hdl) -{ - return obj_hdl->get_definition_name(); -} - -const char* gpi_get_definition_file(gpi_sim_hdl obj_hdl) -{ - return obj_hdl->get_definition_file(); -} - -const char *gpi_get_signal_value_binstr(gpi_sim_hdl sig_hdl) -{ - GpiSignalObjHdl *obj_hdl = static_cast(sig_hdl); - return obj_hdl->get_signal_value_binstr(); -} - -const char *gpi_get_signal_value_str(gpi_sim_hdl sig_hdl) -{ - GpiSignalObjHdl *obj_hdl = static_cast(sig_hdl); - return obj_hdl->get_signal_value_str(); -} - -double gpi_get_signal_value_real(gpi_sim_hdl sig_hdl) -{ - GpiSignalObjHdl *obj_hdl = static_cast(sig_hdl); - return obj_hdl->get_signal_value_real(); -} - -long gpi_get_signal_value_long(gpi_sim_hdl sig_hdl) -{ - GpiSignalObjHdl *obj_hdl = static_cast(sig_hdl); - return obj_hdl->get_signal_value_long(); -} - -const char *gpi_get_signal_name_str(gpi_sim_hdl sig_hdl) -{ - GpiSignalObjHdl *obj_hdl = static_cast(sig_hdl); - return obj_hdl->get_name_str(); -} - -const char *gpi_get_signal_type_str(gpi_sim_hdl obj_hdl) -{ - return obj_hdl->get_type_str(); -} - -gpi_objtype_t gpi_get_object_type(gpi_sim_hdl obj_hdl) -{ - return obj_hdl->get_type(); -} - -int gpi_is_constant(gpi_sim_hdl obj_hdl) -{ - if (obj_hdl->get_const()) - return 1; - return 0; -} - -int gpi_is_indexable(gpi_sim_hdl obj_hdl) -{ - if (obj_hdl->get_indexable()) - return 1; - return 0; -} - -void gpi_set_signal_value_int(gpi_sim_hdl sig_hdl, int32_t value, gpi_set_action_t action) -{ - GpiSignalObjHdl *obj_hdl = static_cast(sig_hdl); - - obj_hdl->set_signal_value(value, action); -} - -void gpi_set_signal_value_binstr(gpi_sim_hdl sig_hdl, const char *binstr, gpi_set_action_t action) -{ - std::string value = binstr; - GpiSignalObjHdl *obj_hdl = static_cast(sig_hdl); - obj_hdl->set_signal_value_binstr(value, action); -} - -void gpi_set_signal_value_str(gpi_sim_hdl sig_hdl, const char *str, gpi_set_action_t action) -{ - std::string value = str; - GpiSignalObjHdl *obj_hdl = static_cast(sig_hdl); - obj_hdl->set_signal_value_str(value, action); -} - -void gpi_set_signal_value_real(gpi_sim_hdl sig_hdl, double value, gpi_set_action_t action) -{ - GpiSignalObjHdl *obj_hdl = static_cast(sig_hdl); - obj_hdl->set_signal_value(value, action); -} - -int gpi_get_num_elems(gpi_sim_hdl obj_hdl) -{ - return obj_hdl->get_num_elems(); -} - -int gpi_get_range_left(gpi_sim_hdl obj_hdl) -{ - return obj_hdl->get_range_left(); -} - -int gpi_get_range_right(gpi_sim_hdl obj_hdl) -{ - return obj_hdl->get_range_right(); -} - -gpi_cb_hdl gpi_register_value_change_callback(int (*gpi_function)(const void *), - void *gpi_cb_data, - gpi_sim_hdl sig_hdl, - int edge) -{ - - GpiSignalObjHdl *signal_hdl = static_cast(sig_hdl); - - /* Do something based on int & GPI_RISING | GPI_FALLING */ - GpiCbHdl *gpi_hdl = signal_hdl->value_change_cb(edge); - if (!gpi_hdl) { - LOG_ERROR("Failed to register a value change callback"); - return NULL; - } - - gpi_hdl->set_user_data(gpi_function, gpi_cb_data); - return gpi_hdl; -} - -/* It should not matter which implementation we use for this so just pick the first - one */ -gpi_cb_hdl gpi_register_timed_callback(int (*gpi_function)(const void *), - void *gpi_cb_data, uint64_t time) -{ - GpiCbHdl *gpi_hdl = registered_impls[0]->register_timed_callback(time); - if (!gpi_hdl) { - LOG_ERROR("Failed to register a timed callback"); - return NULL; - } - - gpi_hdl->set_user_data(gpi_function, gpi_cb_data); - return gpi_hdl; -} - -/* It should not matter which implementation we use for this so just pick the first - one -*/ -gpi_cb_hdl gpi_register_readonly_callback(int (*gpi_function)(const void *), - void *gpi_cb_data) -{ - GpiCbHdl *gpi_hdl = registered_impls[0]->register_readonly_callback(); - if (!gpi_hdl) { - LOG_ERROR("Failed to register a readonly callback"); - return NULL; - } - - gpi_hdl->set_user_data(gpi_function, gpi_cb_data); - return gpi_hdl; -} - -gpi_cb_hdl gpi_register_nexttime_callback(int (*gpi_function)(const void *), - void *gpi_cb_data) -{ - GpiCbHdl *gpi_hdl = registered_impls[0]->register_nexttime_callback(); - if (!gpi_hdl) { - LOG_ERROR("Failed to register a nexttime callback"); - return NULL; - } - - gpi_hdl->set_user_data(gpi_function, gpi_cb_data); - return gpi_hdl; -} - -/* It should not matter which implementation we use for this so just pick the first - one -*/ -gpi_cb_hdl gpi_register_readwrite_callback(int (*gpi_function)(const void *), - void *gpi_cb_data) -{ - GpiCbHdl *gpi_hdl = registered_impls[0] ->register_readwrite_callback(); - if (!gpi_hdl) { - LOG_ERROR("Failed to register a readwrite callback"); - return NULL; - } - - gpi_hdl->set_user_data(gpi_function, gpi_cb_data); - return gpi_hdl; -} - -void gpi_deregister_callback(gpi_cb_hdl cb_hdl) -{ - cb_hdl->m_impl->deregister_callback(cb_hdl); -} - -const char* GpiImplInterface::get_name_c() { - return m_name.c_str(); -} - -const string& GpiImplInterface::get_name_s() { - return m_name; -} diff --git a/cocotb/share/lib/gpi/gpi_priv.h b/cocotb/share/lib/gpi/gpi_priv.h deleted file mode 100644 index 10f771a0..00000000 --- a/cocotb/share/lib/gpi/gpi_priv.h +++ /dev/null @@ -1,293 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2013, 2018 Potential Ventures Ltd -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd, -* SolarFlare Communications Inc nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE dGOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#ifndef COCOTB_GPI_PRIV_H_ -#define COCOTB_GPI_PRIV_H_ - -#include -#ifdef GPI_EXPORTS -#define GPI_EXPORT COCOTB_EXPORT -#else -#define GPI_EXPORT COCOTB_IMPORT -#endif - -#include -#include -#include -#include -#include -#include // COCOTB_UNUSED - -typedef enum gpi_cb_state { - GPI_FREE = 0, - GPI_PRIMED = 1, - GPI_CALL = 2, - GPI_REPRIME = 3, - GPI_DELETE = 4, -} gpi_cb_state_e; - -class GpiCbHdl; -class GpiImplInterface; -class GpiIterator; -class GpiCbHdl; - -/* Base GPI class others are derived from */ -class GPI_EXPORT GpiHdl { -public: - GpiHdl(GpiImplInterface *impl, void *hdl = NULL) : m_impl(impl), m_obj_hdl(hdl) { } - virtual ~GpiHdl() = default; - - - template T get_handle() const { - return static_cast(m_obj_hdl); - } - -private: - GpiHdl() { } // Disable default constructor - -public: - GpiImplInterface *m_impl; // VPI/VHPI/FLI routines - bool is_this_impl(GpiImplInterface *impl); // Is the passed interface the one this object uses? - -protected: - void *m_obj_hdl; -}; - -/* GPI object handle, maps to a simulation object */ -// An object is any item in the hierarchy -// Provides methods for iterating through children or finding by name -// Initial object is returned by call to GpiImplInterface::get_root_handle() -// Subsequent operations to get children go through this handle. -// GpiObjHdl::get_handle_by_name/get_handle_by_index are really factories -// that construct an object derived from GpiSignalObjHdl or GpiObjHdl -class GPI_EXPORT GpiObjHdl : public GpiHdl { -public: - GpiObjHdl( - GpiImplInterface *impl, - void *hdl = nullptr, - gpi_objtype_t objtype = GPI_UNKNOWN, - bool is_const = false - ) : - GpiHdl(impl, hdl), - m_type(objtype), - m_const(is_const) { } - - virtual ~GpiObjHdl() = default; - - virtual const char* get_name_str(); - virtual const char* get_fullname_str(); - virtual const char* get_type_str(); - gpi_objtype_t get_type() { return m_type; }; - bool get_const() { return m_const; }; - int get_num_elems() { - LOG_DEBUG("%s has %d elements", m_name.c_str(), m_num_elems); - return m_num_elems; - } - int get_range_left() { return m_range_left; } - int get_range_right() { return m_range_right; } - int get_indexable() { return m_indexable; } - - const std::string & get_name(); - const std::string & get_fullname(); - - virtual const char* get_definition_name() { return m_definition_name.c_str(); }; - virtual const char* get_definition_file() { return m_definition_file.c_str(); }; - - bool is_native_impl(GpiImplInterface *impl); - virtual int initialise(std::string &name, std::string &full_name); - -protected: - int m_num_elems = 0; - bool m_indexable = false; - int m_range_left = -1; - int m_range_right = -1; - std::string m_name; - std::string m_fullname = "unknown"; - - std::string m_definition_name; - std::string m_definition_file; - - gpi_objtype_t m_type; - bool m_const; -}; - - -/* GPI Signal object handle, maps to a simulation object */ -// -// Identical to an object but adds additional methods for getting/setting the -// value of the signal (which doesn't apply to non signal items in the hierarchy -class GPI_EXPORT GpiSignalObjHdl : public GpiObjHdl { -public: - using GpiObjHdl::GpiObjHdl; - - virtual ~GpiSignalObjHdl() = default; - // Provide public access to the implementation (composition vs inheritance) - virtual const char* get_signal_value_binstr() = 0; - virtual const char* get_signal_value_str() = 0; - virtual double get_signal_value_real() = 0; - virtual long get_signal_value_long() = 0; - - int m_length = 0; - - virtual int set_signal_value(const int32_t value, gpi_set_action_t action) = 0; - virtual int set_signal_value(const double value, gpi_set_action_t action) = 0; - virtual int set_signal_value_str(std::string &value, gpi_set_action_t action) = 0; - virtual int set_signal_value_binstr(std::string &value, gpi_set_action_t action) = 0; - //virtual GpiCbHdl monitor_value(bool rising_edge) = 0; this was for the triggers - // but the explicit ones are probably better - - virtual GpiCbHdl *value_change_cb(int edge) = 0; -}; - - -/* GPI Callback handle */ -// To set a callback it needs the signal to do this on, -// vpiHandle/vhpiHandleT for instance. The -class GPI_EXPORT GpiCbHdl : public GpiHdl { -public: - GpiCbHdl(GpiImplInterface *impl) : GpiHdl(impl) { } - - // Pure virtual functions for derived classes - virtual int arm_callback() = 0; // Register with simulator - virtual int run_callback(); // Entry point from simulator - virtual int cleanup_callback() = 0; // Cleanup the callback, arm can be called after - - // Set the data to be used for run callback, separate to arm_callback so data can be re-used - int set_user_data(int (*gpi_function)(const void*), const void *data); - const void *get_user_data(); - - void set_call_state(gpi_cb_state_e new_state); - gpi_cb_state_e get_call_state(); - - virtual ~GpiCbHdl(); - -protected: - int (*gpi_function)(const void *) = nullptr; // GPI function to callback - const void *m_cb_data = nullptr; // GPI data supplied to "gpi_function" - gpi_cb_state_e m_state = GPI_FREE; // GPI state of the callback through its cycle -}; - -class GPI_EXPORT GpiValueCbHdl : public virtual GpiCbHdl { -public: - GpiValueCbHdl(GpiImplInterface *impl, GpiSignalObjHdl *signal, int edge); - int run_callback() override; - -protected: - std::string required_value; - GpiSignalObjHdl *m_signal; -}; - -class GPI_EXPORT GpiIterator : public GpiHdl { -public: - enum Status { - NATIVE, // Fully resolved object was created - NATIVE_NO_NAME, // Native object was found but unable to fully create - NOT_NATIVE, // Non-native object was found but we did get a name - NOT_NATIVE_NO_NAME, // Non-native object was found without a name - END - }; - - GpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiHdl(impl), - m_parent(hdl) { } - virtual ~GpiIterator() = default; - - virtual Status next_handle(std::string &name, GpiObjHdl **hdl, void **raw_hdl) { - COCOTB_UNUSED(raw_hdl); - name = ""; - *hdl = NULL; - return GpiIterator::END; - } - - GpiObjHdl *get_parent() { - return m_parent; - } - -protected: - GpiObjHdl *m_parent; -}; - - -class GPI_EXPORT GpiImplInterface { -public: - GpiImplInterface(const std::string& name) : m_name(name) { } - const char *get_name_c(); - const std::string& get_name_s(); - virtual ~GpiImplInterface() = default; - - /* Sim related */ - virtual void sim_end() = 0; - virtual void get_sim_time(uint32_t *high, uint32_t *low) = 0; - virtual void get_sim_precision(int32_t *precision) = 0; - virtual const char* get_simulator_product() = 0; - virtual const char* get_simulator_version() = 0; - - /* Hierarchy related */ - virtual GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent) = 0; - virtual GpiObjHdl* native_check_create(int32_t index, GpiObjHdl *parent) = 0; - virtual GpiObjHdl* native_check_create(void *raw_hdl, GpiObjHdl *parent) = 0; - virtual GpiObjHdl *get_root_handle(const char *name) = 0; - virtual GpiIterator *iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) = 0; - - /* Callback related, these may (will) return the same handle */ - virtual GpiCbHdl *register_timed_callback(uint64_t time) = 0; - virtual GpiCbHdl *register_readonly_callback() = 0; - virtual GpiCbHdl *register_nexttime_callback() = 0; - virtual GpiCbHdl *register_readwrite_callback() = 0; - virtual int deregister_callback(GpiCbHdl *obj_hdl) = 0; - - /* Method to provide strings from operation types */ - virtual const char * reason_to_string(int reason) = 0; - -private: - std::string m_name; -protected: - std::string m_product; - std::string m_version; -}; - -/* Called from implementation layers back up the stack */ -GPI_EXPORT int gpi_register_impl(GpiImplInterface *func_tbl); - -GPI_EXPORT void gpi_embed_init(int argc, char const* const* argv); -GPI_EXPORT void gpi_cleanup(); -GPI_EXPORT void gpi_embed_end(); -GPI_EXPORT void gpi_embed_event(gpi_event_t level, const char *msg); -GPI_EXPORT void gpi_load_extra_libs(); - -typedef void (*layer_entry_func)(); - -/* Use this macro in an implementation layer to define an entry point */ -#define GPI_ENTRY_POINT(NAME, func) \ - extern "C" { \ - COCOTB_EXPORT void NAME##_entry_point() \ - { \ - func(); \ - } \ - } - -#endif /* COCOTB_GPI_PRIV_H_ */ diff --git a/cocotb/share/lib/gpi_log/gpi_logging.cpp b/cocotb/share/lib/gpi_log/gpi_logging.cpp deleted file mode 100644 index 2006bf2a..00000000 --- a/cocotb/share/lib/gpi_log/gpi_logging.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2013 Potential Ventures Ltd -* Copyright (c) 2013 SolarFlare Communications Inc -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd, -* SolarFlare Communications Inc nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#include -#include -#include -#include - - -static gpi_log_handler_type *current_handler = nullptr; -static void *current_userdata = nullptr; - - -extern "C" void gpi_log( - const char *name, - int level, - const char *pathname, - const char *funcname, - long lineno, - const char *msg, - ...) -{ - va_list argp; - va_start(argp, msg); - gpi_vlog(name, level, pathname, funcname, lineno, msg, argp); - va_end(argp); -} - - -extern "C" void gpi_vlog( - const char *name, - int level, - const char *pathname, - const char *funcname, - long lineno, - const char *msg, - va_list argp) -{ - if (current_handler) { - (*current_handler)( - current_userdata, - name, - level, - pathname, - funcname, - lineno, - msg, - argp); - } else { - gpi_native_logger_vlog(name, - level, - pathname, - funcname, - lineno, - msg, - argp); - } -} - - -extern "C" void gpi_get_log_handler(gpi_log_handler_type **handler, void **userdata) -{ - *handler = current_handler; - *userdata = current_userdata; -} - - -extern "C" void gpi_set_log_handler(gpi_log_handler_type *handler, void *userdata) -{ - current_handler = handler; - current_userdata = userdata; -} - - -extern "C" void gpi_clear_log_handler(void) -{ - current_handler = nullptr; - current_userdata = nullptr; -} - - -static int current_native_logger_level = GPIInfo; - - -extern "C" void gpi_native_logger_log( - const char *name, - int level, - const char *pathname, - const char *funcname, - long lineno, - const char *msg, - ...) -{ - va_list argp; - va_start(argp, msg); - gpi_native_logger_vlog(name, level, pathname, funcname, lineno, msg, argp); - va_end(argp); -} - - -// Decode the level into a string matching the Python interpretation -struct _log_level_table { - long level; - const char *levelname; -}; - -static struct _log_level_table log_level_table [] = { - { 10, "DEBUG" }, - { 20, "INFO" }, - { 30, "WARNING" }, - { 40, "ERROR" }, - { 50, "CRITICAL" }, - { 0, NULL} -}; - -const char *log_level(long level) -{ - struct _log_level_table *p; - const char *str = "------"; - - for (p=log_level_table; p->levelname; p++) { - if (level == p->level) { - str = p->levelname; - break; - } - } - return str; -} - -// We keep this module global to avoid reallocation -// we do not need to worry about locking here as -// are single threaded and can not have multiple calls -// into gpi_log at once. -#define LOG_SIZE 512 -static char log_buff[LOG_SIZE]; - -extern "C" void gpi_native_logger_vlog( - const char *name, - int level, - const char *pathname, - const char *funcname, - long lineno, - const char *msg, - va_list argp) -{ - if (level < current_native_logger_level) { - return; - } - - int n = vsnprintf(log_buff, LOG_SIZE, msg, argp); - - if (n < 0 || n >= LOG_SIZE) { - fprintf(stderr, "Log message construction failed\n"); - } - - fprintf(stdout, " -.--ns "); - fprintf(stdout, "%-9s", log_level(level)); - fprintf(stdout, "%-35s", name); - - size_t pathlen = strlen(pathname); - if (pathlen > 20) { - fprintf(stdout, "..%18s:", (pathname + (pathlen - 18))); - } else { - fprintf(stdout, "%20s:", pathname); - } - - fprintf(stdout, "%-4ld", lineno); - fprintf(stdout, " in %-31s ", funcname); - fprintf(stdout, "%s", log_buff); - fprintf(stdout, "\n"); - fflush(stdout); -} - - -extern "C" int gpi_native_logger_set_level(int level) -{ - int old_level = current_native_logger_level; - current_native_logger_level = level; - return old_level; -} diff --git a/cocotb/share/lib/py_gpi_log/py_gpi_logging.cpp b/cocotb/share/lib/py_gpi_log/py_gpi_logging.cpp deleted file mode 100644 index df47990f..00000000 --- a/cocotb/share/lib/py_gpi_log/py_gpi_logging.cpp +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright cocotb contributors -// Licensed under the Revised BSD License, see LICENSE for details. -// SPDX-License-Identifier: BSD-3-Clause - -#include // all things Python -#include // all things GPI logging -#include // PY_GPI_LOG_SIZE -#include // va_list, va_copy, va_end -#include // fprintf, vsnprintf - - -static PyObject *pLogHandler = nullptr; - -static PyObject *pLogFilter = nullptr; - -static int py_gpi_log_level = GPIInfo; - - -static void py_gpi_log_handler( - void *, - const char *name, - int level, - const char *pathname, - const char *funcname, - long lineno, - const char *msg, - va_list argp) -{ - if (level < py_gpi_log_level) { - return; - } - - va_list argp_copy; - va_copy(argp_copy, argp); - - PyGILState_STATE gstate = PyGILState_Ensure(); - - // Declared here in order to be initialized before any goto statements and refcount cleanup - PyObject *logger_name_arg = NULL, *filename_arg = NULL, *lineno_arg = NULL, *msg_arg = NULL, *function_arg = NULL; - - PyObject *level_arg = PyLong_FromLong(level); // New reference - if (level_arg == NULL) { - // LCOV_EXCL_START - goto error; - // LCOV_EXCL_STOP - } - - logger_name_arg = PyUnicode_FromString(name); // New reference - if (logger_name_arg == NULL) { - // LCOV_EXCL_START - goto error; - // LCOV_EXCL_STOP - } - - { - // check if log level is enabled - PyObject *filter_ret = PyObject_CallFunctionObjArgs(pLogFilter, logger_name_arg, level_arg, NULL); - if (filter_ret == NULL) { - // LCOV_EXCL_START - goto error; - // LCOV_EXCL_STOP - } - - int is_enabled = PyObject_IsTrue(filter_ret); - Py_DECREF(filter_ret); - if (is_enabled < 0) { - // LCOV_EXCL_START - /* A Python exception occured while converting `filter_ret` to bool */ - goto error; - // LCOV_EXCL_STOP - } - - if (!is_enabled) { - goto ok; - } - } - - static char log_buff[PY_GPI_LOG_SIZE]; - - // Ignore truncation - { - int n = vsnprintf(log_buff, sizeof(log_buff), msg, argp); - if (n < 0 || n >= (int)sizeof(log_buff)) { - // LCOV_EXCL_START - fprintf(stderr, "Log message construction failed\n"); - // LCOV_EXCL_STOP - } - } - - filename_arg = PyUnicode_FromString(pathname); // New reference - if (filename_arg == NULL) { - // LCOV_EXCL_START - goto error; - // LCOV_EXCL_STOP - } - - lineno_arg = PyLong_FromLong(lineno); // New reference - if (lineno_arg == NULL) { - // LCOV_EXCL_START - goto error; - // LCOV_EXCL_STOP - } - - msg_arg = PyUnicode_FromString(log_buff); // New reference - if (msg_arg == NULL) { - // LCOV_EXCL_START - goto error; - // LCOV_EXCL_STOP - } - - function_arg = PyUnicode_FromString(funcname); // New reference - if (function_arg == NULL) { - // LCOV_EXCL_START - goto error; - // LCOV_EXCL_STOP - } - - { - // Log function args are logger_name, level, filename, lineno, msg, function - PyObject *handler_ret = PyObject_CallFunctionObjArgs(pLogHandler, logger_name_arg, level_arg, filename_arg, lineno_arg, msg_arg, function_arg, NULL); - if (handler_ret == NULL) { - // LCOV_EXCL_START - goto error; - // LCOV_EXCL_STOP - } - Py_DECREF(handler_ret); - } - - goto ok; -error: - // LCOV_EXCL_START - /* Note: don't call the LOG_ERROR macro because that might recurse */ - gpi_native_logger_vlog(name, level, pathname, funcname, lineno, msg, argp_copy); - gpi_native_logger_log("cocotb.gpi", GPIError, __FILE__, __func__, __LINE__, "Error calling Python logging function from C++ while logging the above"); - PyErr_Print(); - // LCOV_EXCL_STOP -ok: - va_end(argp_copy); - Py_XDECREF(logger_name_arg); - Py_XDECREF(level_arg); - Py_XDECREF(filename_arg); - Py_XDECREF(lineno_arg); - Py_XDECREF(msg_arg); - Py_XDECREF(function_arg); - PyGILState_Release(gstate); -} - - -extern "C" void py_gpi_logger_set_level(int level) -{ - py_gpi_log_level = level; - gpi_native_logger_set_level(level); -} - - -extern "C" void py_gpi_logger_initialize(PyObject * handler, PyObject * filter) -{ - pLogHandler = handler; - pLogFilter = filter; - gpi_set_log_handler(py_gpi_log_handler, nullptr); -} - - -extern "C" void py_gpi_logger_finalize() -{ - gpi_clear_log_handler(); - Py_XDECREF(pLogHandler); - Py_XDECREF(pLogFilter); -} diff --git a/cocotb/share/lib/simulator/simulatormodule.cpp b/cocotb/share/lib/simulator/simulatormodule.cpp deleted file mode 100644 index b1715e5e..00000000 --- a/cocotb/share/lib/simulator/simulatormodule.cpp +++ /dev/null @@ -1,1329 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2013, 2018 Potential Ventures Ltd -* Copyright (c) 2013 SolarFlare Communications Inc -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd, -* SolarFlare Communications Inc nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -/** -* @file simulatormodule.c -* @brief Python extension to provide access to the simulator -* -* Uses GPI calls to interface to the simulator. -*/ - -static int takes = 0; -static int releases = 0; - -static int sim_ending = 0; - -#include // COCOTB_UNUSED -#include -#include -#include -#include // LOG_* macros -#include // py_gpi_logger_set_level -#include "gpi.h" - -// This file defines the routines available to Python - -#define COCOTB_ACTIVE_ID 0xC0C07B // User data flag to indicate callback is active -#define COCOTB_INACTIVE_ID 0xDEADB175 // User data flag set when callback has been de-registered - -#define MODULE_NAME "simulator" - -// callback user data -struct callback_data { - PyThreadState *_saved_thread_state; // Thread state of the calling thread FIXME is this required? - uint32_t id_value; // COCOTB_ACTIVE_ID or COCOTB_INACTIVE_ID - PyObject *function; // Function to call when the callback fires - PyObject *args; // The arguments to call the function with - PyObject *kwargs; // Keyword arguments to call the function with - gpi_sim_hdl cb_hdl; -}; - -/* define the extension types as templates */ -namespace { - template - struct gpi_hdl_Object { - PyObject_HEAD - gpi_hdl hdl; - - // The python type object, in a place that is easy to retrieve in templates - static PyTypeObject py_type; - }; - - /** __repr__ shows the memory address of the internal handle */ - template - static PyObject *gpi_hdl_repr(gpi_hdl_Object *self) { - auto *type = Py_TYPE(self); - return PyUnicode_FromFormat("<%s at %p>", type->tp_name, self->hdl); - } - - /** __hash__ returns the pointer itself */ - template - static Py_hash_t gpi_hdl_hash(gpi_hdl_Object *self) { - auto ret = reinterpret_cast(self->hdl); - // hash must never return -1 - if (ret == (Py_hash_t)-1) { - ret = (Py_hash_t)-2; - } - return ret; - } - - /** - * Create a new python handle object from a pointer, returning None if the - * pointer is NULL. - */ - template - static PyObject *gpi_hdl_New(gpi_hdl hdl) { - if (hdl == NULL) { - Py_RETURN_NONE; - } - auto *obj = PyObject_New(gpi_hdl_Object, &gpi_hdl_Object::py_type); - if (obj == NULL) { - return NULL; - } - obj->hdl = hdl; - return (PyObject *)obj; - } - - /** Comparison checks if the types match, and then compares pointers */ - template - static PyObject *gpi_hdl_richcompare(PyObject *self, PyObject *other, int op) { - if ( - Py_TYPE(self) != &gpi_hdl_Object::py_type || - Py_TYPE(other) != &gpi_hdl_Object::py_type - ) { - Py_RETURN_NOTIMPLEMENTED; - } - - auto self_hdl_obj = reinterpret_cast*>(self); - auto other_hdl_obj = reinterpret_cast*>(other); - - switch (op) { - case Py_EQ: return PyBool_FromLong(self_hdl_obj->hdl == other_hdl_obj->hdl); - case Py_NE: return PyBool_FromLong(self_hdl_obj->hdl != other_hdl_obj->hdl); - default: Py_RETURN_NOTIMPLEMENTED; - } - } - - // Initialize the python type slots - template - PyTypeObject fill_common_slots() { - PyTypeObject type = {}; - type.ob_base = {PyObject_HEAD_INIT(NULL) 0}; - type.tp_basicsize = sizeof(gpi_hdl_Object); - type.tp_repr = (reprfunc)gpi_hdl_repr; - type.tp_hash = (hashfunc)gpi_hdl_hash; - type.tp_flags = Py_TPFLAGS_DEFAULT; - type.tp_richcompare = gpi_hdl_richcompare; - return type; - } - - // these will be initialized later, once the members are all defined - template<> - PyTypeObject gpi_hdl_Object::py_type; - template<> - PyTypeObject gpi_hdl_Object::py_type; - template<> - PyTypeObject gpi_hdl_Object::py_type; -} - - -typedef int (*gpi_function_t)(const void *); - -PyGILState_STATE TAKE_GIL(void) -{ - PyGILState_STATE state = PyGILState_Ensure(); - takes ++; - return state; -} - -void DROP_GIL(PyGILState_STATE state) -{ - PyGILState_Release(state); - releases++; -} - -struct sim_time { - uint32_t high; - uint32_t low; -}; - -static struct sim_time cache_time; - -/** - * @name Callback Handling - * @brief Handle a callback coming from GPI - * @ingroup python_c_api - * - * GILState before calling: Unknown - * - * GILState after calling: Unknown - * - * Makes one call to TAKE_GIL and one call to DROP_GIL - * - * Returns 0 on success or 1 on a failure. - * - * Handles a callback from the simulator, all of which call this function. - * - * We extract the associated context and find the Python function (usually - * cocotb.scheduler.react) calling it with a reference to the trigger that - * fired. The scheduler can then call next() on all the coroutines that - * are waiting on that particular trigger. - * - * TODO: - * - Tidy up return values - * - Ensure cleanup correctly in exception cases - * - */ -int handle_gpi_callback(void *user_data) -{ - int ret = 0; - to_python(); - callback_data *cb_data = (callback_data *)user_data; - - if (cb_data->id_value != COCOTB_ACTIVE_ID) { - fprintf(stderr, "Userdata corrupted!\n"); - ret = 1; - goto err; - } - cb_data->id_value = COCOTB_INACTIVE_ID; - - /* Cache the sim time */ - gpi_get_sim_time(&cache_time.high, &cache_time.low); - - PyGILState_STATE gstate; - gstate = TAKE_GIL(); - - // Python allowed - - if (!PyCallable_Check(cb_data->function)) { - fprintf(stderr, "Callback fired but function isn't callable?!\n"); - ret = 1; - goto out; - } - - { - // Call the callback - PyObject *pValue = PyObject_Call(cb_data->function, cb_data->args, cb_data->kwargs); - - // If the return value is NULL a Python exception has occurred - // The best thing to do here is shutdown as any subsequent - // calls will go back to Python which is now in an unknown state - if (pValue == NULL) { - fprintf(stderr, "ERROR: called callback function threw exception\n"); - PyErr_Print(); - - gpi_sim_end(); - sim_ending = 1; - ret = 0; - goto out; - } - - // Free up our mess - Py_DECREF(pValue); - } - - // Callbacks may have been re-enabled - if (cb_data->id_value == COCOTB_INACTIVE_ID) { - Py_DECREF(cb_data->function); - Py_DECREF(cb_data->args); - - // Free the callback data - free(cb_data); - } - -out: - DROP_GIL(gstate); - -err: - to_simulator(); - - if (sim_ending) { - // This is the last callback of a successful run, - // so call the cleanup function as we'll never return - // to Python - gpi_cleanup(); - } - return ret; -} - -static PyObject *log_msg(PyObject *self, PyObject *args) -{ - COCOTB_UNUSED(self); - - const char *name; - const char *path; - const char *msg; - const char *funcname; - int lineno; - - if (!PyArg_ParseTuple(args, "sssis:log_msg", &name, &path, &funcname, &lineno, &msg)) - return NULL; - - gpi_log(name, GPIInfo, path, funcname, lineno, msg); - - Py_RETURN_NONE; -} - - -static callback_data *callback_data_new(PyObject *func, PyObject *args, PyObject *kwargs) -{ - callback_data *data = (callback_data *)malloc(sizeof(callback_data)); - if (data == NULL) { - PyErr_NoMemory(); - return NULL; - } - - data->_saved_thread_state = PyThreadState_Get(); - data->id_value = COCOTB_ACTIVE_ID; - data->function = func; - data->args = args; - data->kwargs = kwargs; - - return data; -} - - -// Register a callback for read-only state of sim -// First argument is the function to call -// Remaining arguments are keyword arguments to be passed to the callback -static PyObject *register_readonly_callback(PyObject *self, PyObject *args) -{ - COCOTB_UNUSED(self); - - if (!gpi_has_registered_impl()) { - PyErr_SetString(PyExc_RuntimeError, "No simulator available!"); - return NULL; - } - - Py_ssize_t numargs = PyTuple_Size(args); - - if (numargs < 1) { - PyErr_SetString(PyExc_TypeError, "Attempt to register ReadOnly callback without enough arguments!\n"); - return NULL; - } - - // Extract the callback function - PyObject *function = PyTuple_GetItem(args, 0); - if (!PyCallable_Check(function)) { - PyErr_SetString(PyExc_TypeError, "Attempt to register ReadOnly without supplying a callback!\n"); - return NULL; - } - Py_INCREF(function); - - // Remaining args for function - PyObject *fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference - if (fArgs == NULL) { - return NULL; - } - - callback_data *cb_data = callback_data_new(function, fArgs, NULL); - if (cb_data == NULL) { - return NULL; - } - - gpi_cb_hdl hdl = gpi_register_readonly_callback((gpi_function_t)handle_gpi_callback, cb_data); - - PyObject *rv = gpi_hdl_New(hdl); - - return rv; -} - - -static PyObject *register_rwsynch_callback(PyObject *self, PyObject *args) -{ - COCOTB_UNUSED(self); - - if (!gpi_has_registered_impl()) { - PyErr_SetString(PyExc_RuntimeError, "No simulator available!"); - return NULL; - } - - Py_ssize_t numargs = PyTuple_Size(args); - - if (numargs < 1) { - PyErr_SetString(PyExc_TypeError, "Attempt to register ReadWrite callback without enough arguments!\n"); - return NULL; - } - - // Extract the callback function - PyObject *function = PyTuple_GetItem(args, 0); - if (!PyCallable_Check(function)) { - PyErr_SetString(PyExc_TypeError, "Attempt to register ReadWrite without supplying a callback!\n"); - return NULL; - } - Py_INCREF(function); - - // Remaining args for function - PyObject *fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference - if (fArgs == NULL) { - return NULL; - } - - callback_data *cb_data = callback_data_new(function, fArgs, NULL); - if (cb_data == NULL) { - return NULL; - } - - gpi_cb_hdl hdl = gpi_register_readwrite_callback( - (gpi_function_t)handle_gpi_callback, cb_data); - - PyObject *rv = gpi_hdl_New(hdl); - - return rv; -} - - -static PyObject *register_nextstep_callback(PyObject *self, PyObject *args) -{ - COCOTB_UNUSED(self); - - if (!gpi_has_registered_impl()) { - PyErr_SetString(PyExc_RuntimeError, "No simulator available!"); - return NULL; - } - - Py_ssize_t numargs = PyTuple_Size(args); - - if (numargs < 1) { - PyErr_SetString(PyExc_TypeError, "Attempt to register NextStep callback without enough arguments!\n"); - return NULL; - } - - // Extract the callback function - PyObject *function = PyTuple_GetItem(args, 0); - if (!PyCallable_Check(function)) { - PyErr_SetString(PyExc_TypeError, "Attempt to register NextStep without supplying a callback!\n"); - return NULL; - } - Py_INCREF(function); - - // Remaining args for function - PyObject *fArgs = PyTuple_GetSlice(args, 1, numargs); // New reference - if (fArgs == NULL) { - return NULL; - } - - callback_data *cb_data = callback_data_new(function, fArgs, NULL); - if (cb_data == NULL) { - return NULL; - } - - gpi_cb_hdl hdl = gpi_register_nexttime_callback( - (gpi_function_t)handle_gpi_callback, cb_data); - - PyObject *rv = gpi_hdl_New(hdl); - - return rv; -} - - -// Register a timed callback. -// First argument should be the time in picoseconds -// Second argument is the function to call -// Remaining arguments and keyword arguments are to be passed to the callback -static PyObject *register_timed_callback(PyObject *self, PyObject *args) -{ - COCOTB_UNUSED(self); - - if (!gpi_has_registered_impl()) { - PyErr_SetString(PyExc_RuntimeError, "No simulator available!"); - return NULL; - } - - Py_ssize_t numargs = PyTuple_Size(args); - - if (numargs < 2) { - PyErr_SetString(PyExc_TypeError, "Attempt to register timed callback without enough arguments!\n"); - return NULL; - } - - uint64_t time; - { // Extract the time - PyObject *pTime = PyTuple_GetItem(args, 0); - long long pTime_as_longlong = PyLong_AsLongLong(pTime); - if (pTime_as_longlong == -1 && PyErr_Occurred()) { - return NULL; - } else if (pTime_as_longlong < 0) { - PyErr_SetString(PyExc_ValueError, "Timer value must be a positive integer"); - return NULL; - } else { - time = (uint64_t)pTime_as_longlong; - } - } - - // Extract the callback function - PyObject *function = PyTuple_GetItem(args, 1); - if (!PyCallable_Check(function)) { - PyErr_SetString(PyExc_TypeError, "Attempt to register timed callback without passing a callable callback!\n"); - return NULL; - } - Py_INCREF(function); - - // Remaining args for function - PyObject *fArgs = PyTuple_GetSlice(args, 2, numargs); // New reference - if (fArgs == NULL) { - return NULL; - } - - callback_data *cb_data = callback_data_new(function, fArgs, NULL); - if (cb_data == NULL) { - return NULL; - } - - gpi_cb_hdl hdl = gpi_register_timed_callback( - (gpi_function_t)handle_gpi_callback, cb_data, time); - - // Check success - PyObject *rv = gpi_hdl_New(hdl); - - return rv; -} - - -// Register signal change callback -// First argument should be the signal handle -// Second argument is the function to call -// Remaining arguments and keyword arguments are to be passed to the callback -static PyObject *register_value_change_callback(PyObject *self, PyObject *args) //, PyObject *keywds) -{ - COCOTB_UNUSED(self); - - if (!gpi_has_registered_impl()) { - PyErr_SetString(PyExc_RuntimeError, "No simulator available!"); - return NULL; - } - - Py_ssize_t numargs = PyTuple_Size(args); - - if (numargs < 3) { - PyErr_SetString(PyExc_TypeError, "Attempt to register value change callback without enough arguments!\n"); - return NULL; - } - - PyObject *pSigHdl = PyTuple_GetItem(args, 0); - if (Py_TYPE(pSigHdl) != &gpi_hdl_Object::py_type) { - PyErr_SetString(PyExc_TypeError, "First argument must be a gpi_sim_hdl"); - return NULL; - } - gpi_sim_hdl sig_hdl = ((gpi_hdl_Object *)pSigHdl)->hdl; - - // Extract the callback function - PyObject *function = PyTuple_GetItem(args, 1); - if (!PyCallable_Check(function)) { - PyErr_SetString(PyExc_TypeError, "Attempt to register value change callback without passing a callable callback!\n"); - return NULL; - } - Py_INCREF(function); - - PyObject *pedge = PyTuple_GetItem(args, 2); - int edge = (int)PyLong_AsLong(pedge); - - // Remaining args for function - PyObject *fArgs = PyTuple_GetSlice(args, 3, numargs); // New reference - if (fArgs == NULL) { - return NULL; - } - - callback_data *cb_data = callback_data_new(function, fArgs, NULL); - if (cb_data == NULL) { - return NULL; - } - - gpi_cb_hdl hdl = gpi_register_value_change_callback( - (gpi_function_t)handle_gpi_callback, cb_data, sig_hdl, edge); - - // Check success - PyObject *rv = gpi_hdl_New(hdl); - - return rv; -} - - -static PyObject *iterate(gpi_hdl_Object *self, PyObject *args) -{ - int type; - - if (!PyArg_ParseTuple(args, "i:iterate", &type)) { - return NULL; - } - - gpi_iterator_hdl result = gpi_iterate(self->hdl, (gpi_iterator_sel_t)type); - - return gpi_hdl_New(result); -} - - -static PyObject *next(gpi_hdl_Object *self) -{ - gpi_sim_hdl result = gpi_next(self->hdl); - - // Raise StopIteration when we're done - if (!result) { - PyErr_SetNone(PyExc_StopIteration); - return NULL; - } - - return gpi_hdl_New(result); -} - -// Raise an exception on failure -// Return None if for example get bin_string on enum? - -static PyObject *get_signal_val_binstr(gpi_hdl_Object *self, PyObject *args) -{ - COCOTB_UNUSED(args); - const char *result = gpi_get_signal_value_binstr(self->hdl); - return PyUnicode_FromString(result); -} - -static PyObject *get_signal_val_str(gpi_hdl_Object *self, PyObject *args) -{ - COCOTB_UNUSED(args); - const char *result = gpi_get_signal_value_str(self->hdl); - return PyBytes_FromString(result); -} - -static PyObject *get_signal_val_real(gpi_hdl_Object *self, PyObject *args) -{ - COCOTB_UNUSED(args); - double result = gpi_get_signal_value_real(self->hdl); - return PyFloat_FromDouble(result); -} - - -static PyObject *get_signal_val_long(gpi_hdl_Object *self, PyObject *args) -{ - COCOTB_UNUSED(args); - long result = gpi_get_signal_value_long(self->hdl); - return PyLong_FromLong(result); -} - -static PyObject *set_signal_val_binstr(gpi_hdl_Object *self, PyObject *args) -{ - const char *binstr; - gpi_set_action_t action; - - if (!PyArg_ParseTuple(args, "is:set_signal_val_binstr", &action, &binstr)) { - return NULL; - } - - gpi_set_signal_value_binstr(self->hdl, binstr, action); - Py_RETURN_NONE; -} - -static PyObject *set_signal_val_str(gpi_hdl_Object *self, PyObject *args) -{ - gpi_set_action_t action; - const char *str; - - if (!PyArg_ParseTuple(args, "iy:set_signal_val_str", &action, &str)) { - return NULL; - } - - gpi_set_signal_value_str(self->hdl, str, action); - Py_RETURN_NONE; -} - -static PyObject *set_signal_val_real(gpi_hdl_Object *self, PyObject *args) -{ - double value; - gpi_set_action_t action; - - if (!PyArg_ParseTuple(args, "id:set_signal_val_real", &action, &value)) { - return NULL; - } - - gpi_set_signal_value_real(self->hdl, value, action); - Py_RETURN_NONE; -} - -static PyObject *set_signal_val_int(gpi_hdl_Object *self, PyObject *args) -{ - long long value; - gpi_set_action_t action; - - if (!PyArg_ParseTuple(args, "iL:set_signal_val_int", &action, &value)) { - return NULL; - } - - gpi_set_signal_value_int(self->hdl, static_cast(value), action); - Py_RETURN_NONE; -} - -static PyObject *get_definition_name(gpi_hdl_Object *self, PyObject *args) -{ - COCOTB_UNUSED(args); - const char* result = gpi_get_definition_name(self->hdl); - return PyUnicode_FromString(result); -} - -static PyObject *get_definition_file(gpi_hdl_Object *self, PyObject *args) -{ - COCOTB_UNUSED(args); - const char* result = gpi_get_definition_file(self->hdl); - return PyUnicode_FromString(result); -} - -static PyObject *get_handle_by_name(gpi_hdl_Object *self, PyObject *args) -{ - const char *name; - - if (!PyArg_ParseTuple(args, "s:get_handle_by_name", &name)) { - return NULL; - } - - gpi_sim_hdl result = gpi_get_handle_by_name(self->hdl, name); - - return gpi_hdl_New(result); -} - -static PyObject *get_handle_by_index(gpi_hdl_Object *self, PyObject *args) -{ - int32_t index; - - if (!PyArg_ParseTuple(args, "i:get_handle_by_index", &index)) { - return NULL; - } - - gpi_sim_hdl result = gpi_get_handle_by_index(self->hdl, index); - - return gpi_hdl_New(result); -} - -static PyObject *get_root_handle(PyObject *self, PyObject *args) -{ - COCOTB_UNUSED(self); - const char *name; - - if (!gpi_has_registered_impl()) { - PyErr_SetString(PyExc_RuntimeError, "No simulator available!"); - return NULL; - } - - if (!PyArg_ParseTuple(args, "z:get_root_handle", &name)) { - return NULL; - } - - gpi_sim_hdl result = gpi_get_root_handle(name); - if (NULL == result) { - Py_RETURN_NONE; - } - - return gpi_hdl_New(result); -} - - -static PyObject *get_name_string(gpi_hdl_Object *self, PyObject *args) -{ - COCOTB_UNUSED(args); - COCOTB_UNUSED(self); - const char *result = gpi_get_signal_name_str(self->hdl); - return PyUnicode_FromString(result); -} - -static PyObject *get_type(gpi_hdl_Object *self, PyObject *args) -{ - COCOTB_UNUSED(args); - gpi_objtype_t result = gpi_get_object_type(self->hdl); - return PyLong_FromLong(result); -} - -static PyObject *get_const(gpi_hdl_Object *self, PyObject *args) -{ - COCOTB_UNUSED(args); - int result = gpi_is_constant(self->hdl); - return PyBool_FromLong(result); -} - -static PyObject *get_type_string(gpi_hdl_Object *self, PyObject *args) -{ - COCOTB_UNUSED(args); - const char *result = gpi_get_signal_type_str(self->hdl); - return PyUnicode_FromString(result); -} - - -static PyObject *is_running(PyObject *self, PyObject *args) -{ - COCOTB_UNUSED(self); - COCOTB_UNUSED(args); - - return PyBool_FromLong(gpi_has_registered_impl()); -} - -// Returns a high, low, tuple of simulator time -// Note we can never log from this function since the logging mechanism calls this to annotate -// log messages with the current simulation time -static PyObject *get_sim_time(PyObject *self, PyObject *args) -{ - COCOTB_UNUSED(self); - COCOTB_UNUSED(args); - - if (!gpi_has_registered_impl()) { - PyErr_SetString(PyExc_RuntimeError, "No simulator available!"); - return NULL; - } - - struct sim_time local_time; - - if (is_python_context) { - gpi_get_sim_time(&local_time.high, &local_time.low); - } else { - local_time = cache_time; - } - - PyObject *pTuple = PyTuple_New(2); - PyTuple_SetItem(pTuple, 0, PyLong_FromUnsignedLong(local_time.high)); // Note: This function “steals†a reference to o. - PyTuple_SetItem(pTuple, 1, PyLong_FromUnsignedLong(local_time.low)); // Note: This function “steals†a reference to o. - - return pTuple; -} - -static PyObject *get_precision(PyObject *self, PyObject *args) -{ - COCOTB_UNUSED(self); - COCOTB_UNUSED(args); - - if (!gpi_has_registered_impl()) { - char const * msg = "Simulator is not available! Defaulting precision to 1 fs."; - if (PyErr_WarnEx(PyExc_RuntimeWarning, msg, 1) < 0) { - return NULL; - } - return PyLong_FromLong(-15); // preserves old behavior - } - - int32_t precision; - - gpi_get_sim_precision(&precision); - - return PyLong_FromLong(precision); -} - -static PyObject *get_simulator_product(PyObject *m, PyObject *args) -{ - COCOTB_UNUSED(m); - COCOTB_UNUSED(args); - - if (!gpi_has_registered_impl()) { - PyErr_SetString(PyExc_RuntimeError, "No simulator available!"); - return NULL; - } - - return PyUnicode_FromString(gpi_get_simulator_product()); -} - -static PyObject *get_simulator_version(PyObject *m, PyObject *args) -{ - COCOTB_UNUSED(m); - COCOTB_UNUSED(args); - - if (!gpi_has_registered_impl()) { - PyErr_SetString(PyExc_RuntimeError, "No simulator available!"); - return NULL; - } - - return PyUnicode_FromString(gpi_get_simulator_version()); -} - -static PyObject *get_num_elems(gpi_hdl_Object *self, PyObject *args) -{ - COCOTB_UNUSED(args); - int elems = gpi_get_num_elems(self->hdl); - return PyLong_FromLong(elems); -} - -static PyObject *get_range(gpi_hdl_Object *self, PyObject *args) -{ - COCOTB_UNUSED(args); - - int indexable = gpi_is_indexable(self->hdl); - int rng_left = gpi_get_range_left(self->hdl); - int rng_right = gpi_get_range_right(self->hdl); - - if (indexable) { - return Py_BuildValue("(i,i)", rng_left, rng_right); - } - else { - Py_RETURN_NONE; - } -} - -static PyObject *stop_simulator(PyObject *self, PyObject *args) -{ - COCOTB_UNUSED(self); - COCOTB_UNUSED(args); - - if (!gpi_has_registered_impl()) { - PyErr_SetString(PyExc_RuntimeError, "No simulator available!"); - return NULL; - } - - gpi_sim_end(); - sim_ending = 1; - Py_RETURN_NONE; -} - - -static PyObject *deregister(gpi_hdl_Object *self, PyObject *args) -{ - COCOTB_UNUSED(args); - - gpi_deregister_callback(self->hdl); - - Py_RETURN_NONE; -} - - -static PyObject *log_level(PyObject *self, PyObject *args) -{ - COCOTB_UNUSED(self); - - int l_level; - - if (!PyArg_ParseTuple(args, "i:log_level", &l_level)) { - return NULL; - } - - py_gpi_logger_set_level(l_level); - - Py_RETURN_NONE; -} - -static int add_module_constants(PyObject* simulator) -{ - // Make the GPI constants accessible from the C world - if ( - PyModule_AddIntConstant(simulator, "UNKNOWN", GPI_UNKNOWN ) < 0 || - PyModule_AddIntConstant(simulator, "MEMORY", GPI_MEMORY ) < 0 || - PyModule_AddIntConstant(simulator, "MODULE", GPI_MODULE ) < 0 || - PyModule_AddIntConstant(simulator, "NET", GPI_NET ) < 0 || - // PyModule_AddIntConstant(simulator, "PARAMETER", GPI_PARAMETER ) < 0 || // Deprecated - PyModule_AddIntConstant(simulator, "REG", GPI_REGISTER ) < 0 || - PyModule_AddIntConstant(simulator, "NETARRAY", GPI_ARRAY ) < 0 || - PyModule_AddIntConstant(simulator, "ENUM", GPI_ENUM ) < 0 || - PyModule_AddIntConstant(simulator, "STRUCTURE", GPI_STRUCTURE ) < 0 || - PyModule_AddIntConstant(simulator, "REAL", GPI_REAL ) < 0 || - PyModule_AddIntConstant(simulator, "INTEGER", GPI_INTEGER ) < 0 || - PyModule_AddIntConstant(simulator, "STRING", GPI_STRING ) < 0 || - PyModule_AddIntConstant(simulator, "GENARRAY", GPI_GENARRAY ) < 0 || - PyModule_AddIntConstant(simulator, "OBJECTS", GPI_OBJECTS ) < 0 || - PyModule_AddIntConstant(simulator, "DRIVERS", GPI_DRIVERS ) < 0 || - PyModule_AddIntConstant(simulator, "LOADS", GPI_LOADS ) < 0 || - false - ) { - return -1; - } - - return 0; -} - -// Add the extension types as entries in the module namespace -static int add_module_types(PyObject* simulator) -{ - PyObject *typ; - - typ = (PyObject *)&gpi_hdl_Object::py_type; - Py_INCREF(typ); - if (PyModule_AddObject(simulator, "gpi_sim_hdl", typ) < 0) { - Py_DECREF(typ); - return -1; - } - - typ = (PyObject *)&gpi_hdl_Object::py_type; - Py_INCREF(typ); - if (PyModule_AddObject(simulator, "gpi_cb_hdl", typ) < 0) { - Py_DECREF(typ); - return -1; - } - - typ = (PyObject *)&gpi_hdl_Object::py_type; - Py_INCREF(typ); - if (PyModule_AddObject(simulator, "gpi_iterator_hdl", typ) < 0) { - Py_DECREF(typ); - return -1; - } - - return 0; -} - - -/* NOTE: in the following docstrings we are specifying the parameters twice, but this is necessary. - * The first docstring before the long '--' line specifies the __text_signature__ that is used - * by the help() function. And the second after the '--' line contains type annotations used by - * the `autodoc_docstring_signature` setting of sphinx.ext.autodoc for generating documentation - * because type annotations are not supported in __text_signature__. - */ - -static PyMethodDef SimulatorMethods[] = { - {"log_msg", log_msg, METH_VARARGS, PyDoc_STR( - "log_msg(name, path, funcname, lineno, msg, /)\n" - "--\n\n" - "log_msg(name: str, path: str, funcname: str, lineno: int, msg: str) -> None\n" - "Log a message." - )}, - {"get_root_handle", get_root_handle, METH_VARARGS, PyDoc_STR( - "get_root_handle(name, /)\n" - "--\n\n" - "get_root_handle(name: str) -> cocotb.simulator.gpi_sim_hdl\n" - "Get the root handle." - )}, - {"register_timed_callback", register_timed_callback, METH_VARARGS, PyDoc_STR( - "register_timed_callback(time, func, /, *args)\n" - "--\n\n" - "register_timed_callback(time: int, func: Callable[..., None], *args: Any) -> cocotb.simulator.gpi_cb_hdl\n" - "Register a timed callback." - )}, - {"register_value_change_callback", register_value_change_callback, METH_VARARGS, PyDoc_STR( - "register_value_change_callback(signal, func, edge, /, *args)\n" - "--\n\n" - "register_value_change_callback(signal: cocotb.simulator.gpi_sim_hdl, func: Callable[..., None], edge: int, *args: Any) -> cocotb.simulator.gpi_cb_hdl\n" - "Register a signal change callback." - )}, - {"register_readonly_callback", register_readonly_callback, METH_VARARGS, PyDoc_STR( - "register_readonly_callback(func, /, *args)\n" - "--\n\n" - "register_readonly_callback(func: Callable[..., None], *args: Any) -> cocotb.simulator.gpi_cb_hdl\n" - "Register a callback for the read-only section." - )}, - {"register_nextstep_callback", register_nextstep_callback, METH_VARARGS, PyDoc_STR( - "register_nextstep_callback(func, /, *args)\n" - "--\n\n" - "register_nextstep_callback(func: Callable[..., None], *args: Any) -> cocotb.simulator.gpi_cb_hdl\n" - "Register a callback for the NextSimTime callback." - )}, - {"register_rwsynch_callback", register_rwsynch_callback, METH_VARARGS, PyDoc_STR( - "register_rwsynch_callback(func, /, *args)\n" - "--\n\n" - "register_rwsynch_callback(func: Callable[..., None], *args: Any) -> cocotb.simulator.gpi_cb_hdl\n" - "Register a callback for the read-write section." - )}, - {"stop_simulator", stop_simulator, METH_VARARGS, PyDoc_STR( - "stop_simulator()\n" - "--\n\n" - "stop_simulator() -> None\n" - "Instruct the attached simulator to stop. Users should not call this function." - )}, - {"log_level", log_level, METH_VARARGS, PyDoc_STR( - "log_level(level, /)\n" - "--\n\n" - "log_level(level: int) -> None\n" - "Set the log level for GPI." - )}, - {"is_running", is_running, METH_NOARGS, PyDoc_STR( - "is_running()\n" - "--\n\n" - "is_running() -> bool\n" - "Returns ``True`` if the caller is running within a simulator.\n" - "\n" - ".. versionadded:: 1.4" - )}, - {"get_sim_time", get_sim_time, METH_NOARGS, PyDoc_STR( - "get_sim_time()\n" - "--\n\n" - "get_sim_time() -> Tuple[int, int]\n" - "Get the current simulation time.\n" - "\n" - "Time is represented as a tuple of 32 bit integers ([low32, high32]) comprising a single 64 bit integer." - )}, - {"get_precision", get_precision, METH_NOARGS, PyDoc_STR( - "get_precision()\n" - "--\n\n" - "get_precision() -> int\n" - "Get the precision of the simulator in powers of 10.\n" - "\n" - "For example, if ``-12`` is returned, the simulator's time precision is 10**-12 or 1 ps." - )}, - {"get_simulator_product", get_simulator_product, METH_NOARGS, PyDoc_STR( - "get_simulator_product()\n" - "--\n\n" - "get_simulator_product() -> str\n" - "Get the simulator's product string." - )}, - {"get_simulator_version", get_simulator_version, METH_NOARGS, PyDoc_STR( - "get_simulator_version()\n" - "--\n\n" - "get_simulator_version() -> str\n" - "Get the simulator's product version string." - )}, - {NULL, NULL, 0, NULL} /* Sentinel */ -}; - -static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - MODULE_NAME, - NULL, - -1, - SimulatorMethods, - NULL, - NULL, - NULL, - NULL -}; - -#if defined(__linux__) || defined(__APPLE__) -// Only required for Python < 3.9, default for 3.9+ (bpo-11410) -#pragma GCC visibility push(default) -PyMODINIT_FUNC PyInit_simulator(void); -#pragma GCC visibility pop -#endif - -PyMODINIT_FUNC PyInit_simulator(void) -{ - /* initialize the extension types */ - if (PyType_Ready(&gpi_hdl_Object::py_type) < 0) { - return NULL; - } - if (PyType_Ready(&gpi_hdl_Object::py_type) < 0) { - return NULL; - } - if (PyType_Ready(&gpi_hdl_Object::py_type) < 0) { - return NULL; - } - - PyObject* simulator = PyModule_Create(&moduledef); - if (simulator == NULL) { - return NULL; - } - - if (add_module_constants(simulator) < 0) { - Py_DECREF(simulator); - return NULL; - } - - if (add_module_types(simulator) < 0) { - Py_DECREF(simulator); - return NULL; - } - - return simulator; -} - -/* NOTE: in the following docstrings we are specifying the parameters twice, but this is necessary. - * The first docstring before the long '--' line specifies the __text_signature__ that is used - * by the help() function. And the second after the '--' line contains type annotations used by - * the `autodoc_docstring_signature` setting of sphinx.ext.autodoc for generating documentation - * because type annotations are not supported in __text_signature__. - */ - -static PyMethodDef gpi_sim_hdl_methods[] = { - {"get_signal_val_long", - (PyCFunction)get_signal_val_long, METH_NOARGS, PyDoc_STR( - "get_signal_val_long($self)\n" - "--\n\n" - "get_signal_val_long() -> int\n" - "Get the value of a signal as an integer." - ) - }, - {"get_signal_val_str", - (PyCFunction)get_signal_val_str, METH_NOARGS, PyDoc_STR( - "get_signal_val_str($self)\n" - "--\n\n" - "get_signal_val_str() -> bytes\n" - "Get the value of a signal as a byte string." - ) - }, - {"get_signal_val_binstr", - (PyCFunction)get_signal_val_binstr, METH_NOARGS, PyDoc_STR( - "get_signal_val_binstr($self)\n" - "--\n\n" - "get_signal_val_binstr() -> str\n" - "Get the value of a logic vector signal as a string of (``0``, ``1``, ``X``, etc.), one element per character." - ) - }, - {"get_signal_val_real", - (PyCFunction)get_signal_val_real, METH_NOARGS, PyDoc_STR( - "get_signal_val_real($self)\n" - "--\n\n" - "get_signal_val_real() -> float\n" - "Get the value of a signal as a float." - ) - }, - {"set_signal_val_int", - (PyCFunction)set_signal_val_int, METH_VARARGS, PyDoc_STR( - "set_signal_val_int($self, action, value, /)\n" - "--\n\n" - "Set the value of a signal using an int" - ) - }, - {"set_signal_val_str", - (PyCFunction)set_signal_val_str, METH_VARARGS, PyDoc_STR( - "set_signal_val_str($self, action, value, /)\n" - "--\n\n" - "set_signal_val_str(action: int, value: bytes) -> None\n" - "Set the value of a signal using a user-encoded string." - ) - }, - {"set_signal_val_binstr", - (PyCFunction)set_signal_val_binstr, METH_VARARGS, PyDoc_STR( - "set_signal_val_binstr($self, action, value, /)\n" - "--\n\n" - "set_signal_val_binstr(action: int, value: str) -> None\n" - "Set the value of a logic vector signal using a string of (``0``, ``1``, ``X``, etc.), one element per character." - ) - }, - {"set_signal_val_real", - (PyCFunction)set_signal_val_real, METH_VARARGS, PyDoc_STR( - "set_signal_val_real($self, action, value, /)\n" - "--\n\n" - "set_signal_val_real(action: int, value: float) -> None\n" - "Set the value of a signal using a float." - ) - }, - {"get_definition_name", - (PyCFunction)get_definition_name, METH_NOARGS, PyDoc_STR( - "get_definition_name($self)\n" - "--\n\n" - "get_definition_name() -> str\n" - "Get the name of a GPI object's definition." - ) - }, - {"get_definition_file", - (PyCFunction)get_definition_file, METH_NOARGS, PyDoc_STR( - "get_definition_file($self)\n" - "--\n\n" - "get_definition_file() -> str\n" - "Get the file that sources the object's definition." - ) - }, - {"get_handle_by_name", - (PyCFunction)get_handle_by_name, METH_VARARGS, PyDoc_STR( - "get_handle_by_name($self, name, /)\n" - "--\n\n" - "get_handle_by_name(name: str) -> cocotb.simulator.gpi_sim_hdl\n" - "Get a handle to a child object by name." - ) - }, - {"get_handle_by_index", - (PyCFunction)get_handle_by_index, METH_VARARGS, PyDoc_STR( - "get_handle_by_index($self, index, /)\n" - "--\n\n" - "get_handle_by_index(index: int) -> cocotb.simulator.gpi_sim_hdl\n" - "Get a handle to a child object by index." - ) - }, - {"get_name_string", - (PyCFunction)get_name_string, METH_NOARGS, PyDoc_STR( - "get_name_string($self)\n" - "--\n\n" - "get_name_string() -> str\n" - "Get the name of an object as a string." - ) - }, - {"get_type_string", - (PyCFunction)get_type_string, METH_NOARGS, PyDoc_STR( - "get_type_string($self)\n" - "--\n\n" - "get_type_string() -> str\n" - "Get the GPI type of an object as a string." - ) - }, - {"get_type", - (PyCFunction)get_type, METH_NOARGS, PyDoc_STR( - "get_type($self)\n" - "--\n\n" - "get_type() -> int\n" - "Get the GPI type of an object as an enum." - ) - }, - {"get_const", - (PyCFunction)get_const, METH_NOARGS, PyDoc_STR( - "get_const($self)\n" - "--\n\n" - "get_const() -> bool\n" - "Return ``True`` if the object is a constant." - ) - }, - {"get_num_elems", - (PyCFunction)get_num_elems, METH_NOARGS, PyDoc_STR( - "get_num_elems($self)\n" - "--\n\n" - "get_num_elems() -> int\n" - "Get the number of elements contained in the handle." - ) - }, - {"get_range", - (PyCFunction)get_range, METH_NOARGS, PyDoc_STR( - "get_range($self)\n" - "--\n\n" - "get_range() -> Tuple[int, int]\n" - "Get the range of elements (tuple) contained in the handle, return ``None`` if not indexable." - ) - }, - {"iterate", - (PyCFunction)iterate, METH_VARARGS, PyDoc_STR( - "iterate($self, mode, /)\n" - "--\n\n" - "iterate(mode: int) -> cocotb.simulator.gpi_iterator_hdl\n" - "Get an iterator handle to loop over all members in an object." - ) - }, - {NULL, NULL, 0, NULL} /* Sentinel */ -}; - -// putting these at the bottom means that all the functions above are accessible -template<> -PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { - auto type = fill_common_slots(); - type.tp_name = "cocotb.simulator.gpi_sim_hdl"; - type.tp_doc = "GPI object handle\n" - "\n" - "Contains methods for getting and setting the value of a GPI object, and introspection."; - type.tp_methods = gpi_sim_hdl_methods; - return type; -}(); - -template<> -PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { - auto type = fill_common_slots(); - type.tp_name = "cocotb.simulator.gpi_iterator_hdl"; - type.tp_doc = "GPI iterator handle."; - type.tp_iter = PyObject_SelfIter; - type.tp_iternext = (iternextfunc)next; - return type; -}(); - -static PyMethodDef gpi_cb_hdl_methods[] = { - {"deregister", - (PyCFunction)deregister, METH_NOARGS, PyDoc_STR( - "deregister($self)\n" - "--\n\n" - "deregister() -> None\n" - "De-register this callback." - )}, - {NULL, NULL, 0, NULL} /* Sentinel */ -}; - -template<> -PyTypeObject gpi_hdl_Object::py_type = []() -> PyTypeObject { - auto type = fill_common_slots(); - type.tp_name = "cocotb.simulator.gpi_cb_hdl"; - type.tp_doc = "GPI callback handle"; - type.tp_methods = gpi_cb_hdl_methods; - return type; -}(); diff --git a/cocotb/share/lib/utils/cocotb_utils.cpp b/cocotb/share/lib/utils/cocotb_utils.cpp deleted file mode 100644 index 0e852c14..00000000 --- a/cocotb/share/lib/utils/cocotb_utils.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2013 Potential Ventures Ltd -* Copyright (c) 2013 SolarFlare Communications Inc -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd, -* SolarFlare Communications Inc nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#include -#include -#include - -#if defined(__linux__) || defined(__APPLE__) -#include -#else -#include -#endif - -// Tracks if we are in the context of Python or Simulator -int is_python_context = 0; - -extern "C" void* utils_dyn_open(const char* lib_name) -{ - void *ret = NULL; -#if ! defined(__linux__) && ! defined(__APPLE__) - SetErrorMode(0); - ret = static_cast(LoadLibrary(lib_name)); - if (!ret) { - const char *log_fmt = "Unable to open lib %s%s%s"; - LPSTR msg_ptr; - if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, - GetLastError(), - MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT), - (LPSTR)&msg_ptr, 255, NULL)) { - LOG_ERROR(log_fmt, lib_name, ": ", msg_ptr); - LocalFree(msg_ptr); - } else { - LOG_ERROR(log_fmt, lib_name, "", ""); - } - } -#else - /* Clear status */ - dlerror(); - - ret = dlopen(lib_name, RTLD_LAZY | RTLD_GLOBAL); - if (!ret) { - LOG_ERROR("Unable to open lib %s: %s", lib_name, dlerror()); - } -#endif - return ret; -} - -extern "C" void* utils_dyn_sym(void *handle, const char* sym_name) -{ - void *entry_point; -#if ! defined(__linux__) && ! defined(__APPLE__) - entry_point = reinterpret_cast(GetProcAddress(static_cast(handle), sym_name)); - if (!entry_point) { - const char *log_fmt = "Unable to find symbol %s%s%s"; - LPSTR msg_ptr; - if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, - GetLastError(), - MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT), - (LPSTR)&msg_ptr, 255, NULL)) { - LOG_ERROR(log_fmt, sym_name, ": ", msg_ptr); - LocalFree(msg_ptr); - } else { - LOG_ERROR(log_fmt, sym_name, "", ""); - } - } -#else - entry_point = dlsym(handle, sym_name); - if (!entry_point) { - LOG_ERROR("Unable to find symbol %s: %s", sym_name, dlerror()); - } -#endif - return entry_point; -} diff --git a/cocotb/share/lib/verilator/verilator.cpp b/cocotb/share/lib/verilator/verilator.cpp deleted file mode 100644 index b9b0754b..00000000 --- a/cocotb/share/lib/verilator/verilator.cpp +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright cocotb contributors -// Licensed under the Revised BSD License, see LICENSE for details. -// SPDX-License-Identifier: BSD-3-Clause - -#include "Vtop.h" -#include "verilated.h" -#include "verilated_vpi.h" - -#include - -#ifndef VM_TRACE_FST - //emulate new verilator behavior for legacy versions - #define VM_TRACE_FST 0 -#endif - -#if VM_TRACE -#if VM_TRACE_FST -#include -#else -#include -#endif -#endif - -static vluint64_t main_time = 0; // Current simulation time - -double sc_time_stamp() { // Called by $time in Verilog - return main_time; // converts to double, to match - // what SystemC does -} - -extern "C" { -void vlog_startup_routines_bootstrap(void); -} - -static inline bool settle_value_callbacks() { - bool cbs_called, again; - - // Call Value Change callbacks - // These can modify signal values so we loop - // until there are no more changes - cbs_called = again = VerilatedVpi::callValueCbs(); - while (again) { - again = VerilatedVpi::callValueCbs(); - } - - return cbs_called; -} - -int main(int argc, char** argv) { - Verilated::commandArgs(argc, argv); -#ifdef VERILATOR_SIM_DEBUG - Verilated::debug(99); -#endif - std::unique_ptr top(new Vtop("")); - Verilated::fatalOnVpiError(false); // otherwise it will fail on systemtf - -#ifdef VERILATOR_SIM_DEBUG - Verilated::internalsDump(); -#endif - - vlog_startup_routines_bootstrap(); - VerilatedVpi::callCbs(cbStartOfSimulation); - -#if VM_TRACE - Verilated::traceEverOn(true); -#if VM_TRACE_FST - std::unique_ptr tfp(new VerilatedFstC); - top->trace(tfp.get(), 99); - tfp->open("dump.fst"); -#else - std::unique_ptr tfp(new VerilatedVcdC); - top->trace(tfp.get(), 99); - tfp->open("dump.vcd"); -#endif -#endif - - while (!Verilated::gotFinish()) { - // Call registered timed callbacks (e.g. clock timer) - // These are called at the beginning of the time step - // before the iterative regions (IEEE 1800-2012 4.4.1) - VerilatedVpi::callTimedCbs(); - - // Call Value Change callbacks triggered by Timer callbacks - // These can modify signal values - settle_value_callbacks(); - - // We must evaluate whole design until we process all 'events' - bool again = true; - while (again) { - // Evaluate design - top->eval(); - - // Call Value Change callbacks triggered by eval() - // These can modify signal values - again = settle_value_callbacks(); - - // Call registered ReadWrite callbacks - again |= VerilatedVpi::callCbs(cbReadWriteSynch); - - // Call Value Change callbacks triggered by ReadWrite callbacks - // These can modify signal values - again |= settle_value_callbacks(); - } - - // Call ReadOnly callbacks - VerilatedVpi::callCbs(cbReadOnlySynch); - -#if VM_TRACE - tfp->dump(main_time); -#endif - // cocotb controls the clock inputs using cbAfterDelay so - // skip ahead to the next registered callback - vluint64_t next_time = VerilatedVpi::cbNextDeadline(); - - // If there are no more cbAfterDelay callbacks, - // the next deadline is max value, so end the simulation now - if (next_time == static_cast(~0ULL)) { - vl_finish(__FILE__, __LINE__, ""); - break; - } else { - main_time = next_time; - } - - // Call registered NextSimTime - // It should be called in simulation cycle before everything else - // but not on first cycle - VerilatedVpi::callCbs(cbNextSimTime); - - // Call Value Change callbacks triggered by NextTimeStep callbacks - // These can modify signal values - settle_value_callbacks(); - } - - VerilatedVpi::callCbs(cbEndOfSimulation); - - top->final(); - -#if VM_TRACE - tfp->close(); -#endif - -// VM_COVERAGE is a define which is set if Verilator is -// instructed to collect coverage (when compiling the simulation) -#if VM_COVERAGE - VerilatedCov::write("coverage.dat"); -#endif - - return 0; -} diff --git a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp b/cocotb/share/lib/vhpi/VhpiCbHdl.cpp deleted file mode 100644 index bdca50d7..00000000 --- a/cocotb/share/lib/vhpi/VhpiCbHdl.cpp +++ /dev/null @@ -1,1200 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2013 Potential Ventures Ltd -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd, -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#include -#include "VhpiImpl.h" -#include // numeric_limits -#include // fixed-size int types and format strings -#include - -namespace { - using bufSize_type = decltype(vhpiValueT::bufSize); -} - -extern "C" void handle_vhpi_callback(const vhpiCbDataT *cb_data); - -VhpiArrayObjHdl::~VhpiArrayObjHdl() -{ - LOG_DEBUG("VHPI: Releasing VhpiArrayObjHdl handle at %p", (void *)get_handle()); - if (vhpi_release_handle(get_handle())) - check_vhpi_error(); -} - -VhpiObjHdl::~VhpiObjHdl() -{ - /* Don't release handles for pseudo-regions, as they borrow the handle of the containing region */ - if (m_type != GPI_GENARRAY) { - LOG_DEBUG("VHPI: Releasing VhpiObjHdl handle at %p", (void *)get_handle()); - if (vhpi_release_handle(get_handle())) - check_vhpi_error(); - } -} - -VhpiSignalObjHdl::~VhpiSignalObjHdl() -{ - switch (m_value.format) { - case vhpiIntVecVal: - case vhpiEnumVecVal: - case vhpiLogicVecVal: - delete [] m_value.value.enumvs; - default: - break; - } - - if (m_binvalue.value.str) - delete [] m_binvalue.value.str; - - LOG_DEBUG("VHPI: Releasing VhpiSignalObjHdl handle at %p", (void *)get_handle()); - if (vhpi_release_handle(get_handle())) - check_vhpi_error(); -} - -bool get_range(vhpiHandleT hdl, vhpiIntT dim, int *left, int *right) { -#ifdef IUS - /* IUS/Xcelium does not appear to set the vhpiIsUnconstrainedP property. IUS Docs say will return - * -1 if unconstrained, but with vhpiIntT being unsigned, the value returned is below. - */ - const vhpiIntT UNCONSTRAINED = 2147483647; -#endif - - bool error = true; - - vhpiHandleT base_hdl = vhpi_handle(vhpiBaseType, hdl); - - if (base_hdl == NULL) { - vhpiHandleT st_hdl = vhpi_handle(vhpiSubtype, hdl); - - if (st_hdl != NULL) { - base_hdl = vhpi_handle(vhpiBaseType, st_hdl); - vhpi_release_handle(st_hdl); - } - } - - if (base_hdl != NULL) { - vhpiHandleT it = vhpi_iterator(vhpiConstraints, base_hdl); - vhpiIntT curr_idx = 0; - - if (it != NULL) { - vhpiHandleT constraint; - while ((constraint = vhpi_scan(it)) != NULL) { - if (curr_idx == dim) { - - vhpi_release_handle(it); - vhpiIntT l_rng = vhpi_get(vhpiLeftBoundP, constraint); - vhpiIntT r_rng = vhpi_get(vhpiRightBoundP, constraint); -#ifdef IUS - if (l_rng != UNCONSTRAINED && r_rng != UNCONSTRAINED) { -#else - if (vhpi_get(vhpiIsUnconstrainedP, constraint)) { -#endif - error = false; - *left = static_cast(l_rng); - *right = static_cast(r_rng); - } - break; - } - ++curr_idx; - } - } - vhpi_release_handle(base_hdl); - } - - if (error) { - vhpiHandleT sub_type_hdl = vhpi_handle(vhpiSubtype, hdl); - - if (sub_type_hdl != NULL) { - vhpiHandleT it = vhpi_iterator(vhpiConstraints, sub_type_hdl); - vhpiIntT curr_idx = 0; - - if (it != NULL) { - vhpiHandleT constraint; - while ((constraint = vhpi_scan(it)) != NULL) { - if (curr_idx == dim) { - vhpi_release_handle(it); - - /* IUS/Xcelium only sets the vhpiIsUnconstrainedP incorrectly on the base type */ - if (!vhpi_get(vhpiIsUnconstrainedP, constraint)) { - error = false; - *left = static_cast(vhpi_get(vhpiLeftBoundP, constraint)); - *right = static_cast(vhpi_get(vhpiRightBoundP, constraint)); - } - break; - } - ++curr_idx; - } - } - vhpi_release_handle(sub_type_hdl); - } - } - - return error; - -} - -vhpiPutValueModeT map_put_value_mode(gpi_set_action_t action) { - vhpiPutValueModeT put_value_mode = vhpiDeposit; - switch (action) { - case GPI_DEPOSIT: - put_value_mode = vhpiDepositPropagate; - break; - case GPI_FORCE: - put_value_mode = vhpiForcePropagate; - break; - case GPI_RELEASE: - put_value_mode = vhpiRelease; - break; - default: - assert(0); - } - return put_value_mode; -} - -int VhpiArrayObjHdl::initialise(std::string &name, std::string &fq_name) { - vhpiHandleT handle = GpiObjHdl::get_handle(); - - m_indexable = true; - - vhpiHandleT type = vhpi_handle(vhpiBaseType, handle); - - if (type == NULL) { - vhpiHandleT st_hdl = vhpi_handle(vhpiSubtype, handle); - - if (st_hdl != NULL) { - type = vhpi_handle(vhpiBaseType, st_hdl); - vhpi_release_handle(st_hdl); - } - } - - if (NULL == type) { - LOG_ERROR("VHPI: Unable to get vhpiBaseType for %s", fq_name.c_str()); - return -1; - } - - vhpiIntT num_dim = vhpi_get(vhpiNumDimensionsP, type); - vhpiIntT dim_idx = 0; - - /* Need to determine which dimension constraint is needed */ - if (num_dim > 1) { - std::string hdl_name = vhpi_get_str(vhpiCaseNameP, handle); - - if (hdl_name.length() < name.length()) { - std::string pseudo_idx = name.substr(hdl_name.length()); - - while (pseudo_idx.length() > 0) { - std::size_t found = pseudo_idx.find_first_of(")"); - - if (found != std::string::npos) { - ++dim_idx; - pseudo_idx = pseudo_idx.substr(found+1); - } else { - break; - } - } - } - } - - bool error = get_range(handle, dim_idx, &m_range_left, &m_range_right); - - if (error) { - LOG_ERROR("VHPI: Unable to obtain constraints for an indexable object %s.", fq_name.c_str()); - return -1; - } - - if (m_range_left > m_range_right) { - m_num_elems = m_range_left - m_range_right + 1; - } else { - m_num_elems = m_range_right - m_range_left + 1; - } - - return GpiObjHdl::initialise(name, fq_name); -} - -int VhpiObjHdl::initialise(std::string &name, std::string &fq_name) { - vhpiHandleT handle = GpiObjHdl::get_handle(); - if (handle != NULL) { - vhpiHandleT du_handle = vhpi_handle(vhpiDesignUnit, handle); - if (du_handle != NULL) { - vhpiHandleT pu_handle = vhpi_handle(vhpiPrimaryUnit, du_handle); - if (pu_handle != NULL) { - const char * str; - str = vhpi_get_str(vhpiNameP, pu_handle); - if (str != NULL) - m_definition_name = str; - - str = vhpi_get_str(vhpiFileNameP, pu_handle); - if (str != NULL) - m_definition_file = str; - } - } - } - - return GpiObjHdl::initialise(name, fq_name); -} - -int VhpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { - // Determine the type of object, either scalar or vector - m_value.format = vhpiObjTypeVal; - m_value.bufSize = 0; - m_value.value.str = NULL; - m_value.numElems = 0; - /* We also alloc a second value member for use with read string operations */ - m_binvalue.format = vhpiBinStrVal; - m_binvalue.bufSize = 0; - m_binvalue.numElems = 0; - m_binvalue.value.str = NULL; - - vhpiHandleT handle = GpiObjHdl::get_handle(); - - if (0 > vhpi_get_value(get_handle(), &m_value)) { - LOG_ERROR("VHPI: vhpi_get_value failed for %s (%s)", fq_name.c_str(), vhpi_get_str(vhpiKindStrP, handle)); - return -1; - } - - LOG_DEBUG("VHPI: Found %s of format type %s (%d) format object with %d elems buffsize %d size %d", - name.c_str(), - ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format), - m_value.format, - m_value.numElems, - m_value.bufSize, - vhpi_get(vhpiSizeP, handle)); - - // Default - overridden below in certain special cases - m_num_elems = m_value.numElems; - - switch (m_value.format) { - case vhpiIntVal: - case vhpiEnumVal: - case vhpiRealVal: - case vhpiCharVal: { - break; - } - - case vhpiStrVal: { - m_indexable = true; - m_num_elems = static_cast(vhpi_get(vhpiSizeP, handle)); - int bufSize = m_num_elems * static_cast(sizeof(vhpiCharT)) + 1; - m_value.bufSize = static_cast(bufSize); - m_value.value.str = new vhpiCharT[bufSize]; - m_value.numElems = m_num_elems; - LOG_DEBUG("VHPI: Overriding num_elems to %d", m_num_elems); - break; - } - - default: { - LOG_ERROR("VHPI: Unable to determine property for %s (%d) format object", - ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format), m_value.format); - return -1; - } - } - - if (m_indexable && get_range(handle, 0, &m_range_left, &m_range_right)) { - m_indexable = false; - } - - if (m_num_elems) { - int bufSize = m_num_elems * static_cast(sizeof(vhpiCharT)) + 1; - m_binvalue.bufSize = static_cast(bufSize); - m_binvalue.value.str = new vhpiCharT[bufSize]; - } - - return GpiObjHdl::initialise(name, fq_name); -} - -int VhpiLogicSignalObjHdl::initialise(std::string &name, std::string &fq_name) { - - // Determine the type of object, either scalar or vector - m_value.format = vhpiLogicVal; - m_value.bufSize = 0; - m_value.value.str = NULL; - m_value.numElems = 0; - /* We also alloc a second value member for use with read string operations */ - m_binvalue.format = vhpiBinStrVal; - m_binvalue.bufSize = 0; - m_binvalue.numElems = 0; - m_binvalue.value.str = NULL; - - vhpiHandleT handle = GpiObjHdl::get_handle(); - vhpiHandleT base_hdl = vhpi_handle(vhpiBaseType, handle); - - if (base_hdl == NULL) { - vhpiHandleT st_hdl = vhpi_handle(vhpiSubtype, handle); - - if (st_hdl != NULL) { - base_hdl = vhpi_handle(vhpiBaseType, st_hdl); - vhpi_release_handle(st_hdl); - } - } - - vhpiHandleT query_hdl = (base_hdl != NULL) ? base_hdl : handle; - - m_num_elems = static_cast(vhpi_get(vhpiSizeP, handle)); - - if (m_num_elems == 0) { - LOG_DEBUG("VHPI: Null vector... Delete object") - return -1; - } - - if (vhpi_get(vhpiKindP, query_hdl) == vhpiArrayTypeDeclK) { - m_indexable = true; - m_value.format = vhpiLogicVecVal; - int bufSize = m_num_elems * static_cast(sizeof(vhpiEnumT)); - m_value.bufSize = static_cast(bufSize); - m_value.value.enumvs = new vhpiEnumT[bufSize]; - } - - if (m_indexable && get_range(handle, 0, &m_range_left, &m_range_right)) { - m_indexable = false; - } - - if (m_num_elems) { - int bufSize = m_num_elems * static_cast(sizeof(vhpiCharT)) + 1; - m_binvalue.bufSize = static_cast(bufSize); - m_binvalue.value.str = new vhpiCharT[bufSize]; - } - - return GpiObjHdl::initialise(name, fq_name); -} - - -VhpiCbHdl::VhpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl) -{ - cb_data.reason = 0; - cb_data.cb_rtn = handle_vhpi_callback; - cb_data.obj = NULL; - cb_data.time = NULL; - cb_data.value = NULL; - cb_data.user_data = (char *)this; - - vhpi_time.high = 0; - vhpi_time.low = 0; -} - -int VhpiCbHdl::cleanup_callback() -{ - /* For non timer callbacks we disable rather than remove */ - int ret = 0; - if (m_state == GPI_FREE) - return 0; - - vhpiStateT cbState = (vhpiStateT)vhpi_get(vhpiStateP, get_handle()); - if (vhpiEnable == cbState) { - ret = vhpi_disable_cb(get_handle()); - m_state = GPI_FREE; - } - - if (ret) - check_vhpi_error(); - - return 0; -} - -int VhpiCbHdl::arm_callback() -{ - int ret = 0; - vhpiStateT cbState; - - if (m_state == GPI_PRIMED) - return 0; - - /* Do we already have a handle? If so and it is disabled then - just re-enable it. */ - - if (get_handle()) { - cbState = (vhpiStateT)vhpi_get(vhpiStateP, get_handle()); - if (vhpiDisable == cbState) { - if (vhpi_enable_cb(get_handle())) { - check_vhpi_error(); - goto error; - } - } - } else { - - vhpiHandleT new_hdl = vhpi_register_cb(&cb_data, vhpiReturnCb); - - if (!new_hdl) { - check_vhpi_error(); - LOG_ERROR("VHPI: Unable to register a callback handle for VHPI type %s(%d)", - m_impl->reason_to_string(cb_data.reason), cb_data.reason); - goto error; - } - - // don't cast to vhpiStateT immediately because vhpiUndefined is not in the enum - vhpiIntT cbState_raw = vhpi_get(vhpiStateP, new_hdl); - if ((vhpiIntT)vhpiUndefined == cbState_raw) { - LOG_ERROR("VHPI: Registered callback isn't enabled! Got vhpiStateP=vhpiUndefined(%d)", vhpiUndefined); - goto error; - } else if (vhpiEnable != (vhpiStateT)cbState_raw) { - LOG_ERROR("VHPI: Registered callback isn't enabled! Got vhpiStateP=%d", (vhpiStateT)cbState_raw); - goto error; - } - - m_obj_hdl = new_hdl; - } - m_state = GPI_PRIMED; - - return ret; - -error: - m_state = GPI_FREE; - return -1; -} - -// Value related functions -vhpiEnumT VhpiSignalObjHdl::chr2vhpi(const char value) -{ - switch (value) { - case '0': - return vhpi0; - case '1': - return vhpi1; - case 'U': - case 'u': - return vhpiU; - case 'Z': - case 'z': - return vhpiZ; - case 'X': - case 'x': - return vhpiX; - default: - return vhpiDontCare; - } -} - -// Value related functions -int VhpiLogicSignalObjHdl::set_signal_value(int32_t value, gpi_set_action_t action) -{ - switch (m_value.format) { - case vhpiEnumVal: - case vhpiLogicVal: { - m_value.value.enumv = value ? vhpi1 : vhpi0; - break; - } - - case vhpiEnumVecVal: - case vhpiLogicVecVal: { - int i; - for (i=0; i(), &m_value, map_put_value_mode(action))) { - check_vhpi_error(); - return -1; - } - - return 0; -} - -int VhpiLogicSignalObjHdl::set_signal_value_binstr(std::string &value, gpi_set_action_t action) -{ - switch (m_value.format) { - case vhpiEnumVal: - case vhpiLogicVal: { - m_value.value.enumv = chr2vhpi(value.c_str()[0]); - break; - } - - case vhpiEnumVecVal: - case vhpiLogicVecVal: { - - if ((int)value.length() != m_num_elems) { - LOG_ERROR("VHPI: Unable to set logic vector due to the string having incorrect length. Length of %d needs to be %d", value.length(), m_num_elems); - return -1; - } - - m_value.numElems = m_num_elems; - - std::string::iterator iter; - - int i = 0; - for (iter = value.begin(); - (iter != value.end()) && (i < m_num_elems); - iter++, i++) { - m_value.value.enumvs[i] = chr2vhpi(*iter); - } - - break; - } - - default: { - LOG_ERROR("VHPI: Unable to set a std_logic signal with a raw value"); - return -1; - } - } - - if (vhpi_put_value(GpiObjHdl::get_handle(), &m_value, map_put_value_mode(action))) { - check_vhpi_error(); - return -1; - } - - return 0; -} - -// Value related functions -int VhpiSignalObjHdl::set_signal_value(int32_t value, gpi_set_action_t action) -{ - switch (m_value.format) { - case vhpiEnumVecVal: - case vhpiLogicVecVal: { - int i; - for (i=0; i(value); - break; - } - - case vhpiIntVal: { - m_value.value.intg = static_cast(value); - break; - } - - case vhpiCharVal: { - using CharLimits = std::numeric_limits; - if ((value > CharLimits::max()) || (value < CharLimits::min())) { - LOG_ERROR("VHPI: Data loss detected"); - return -1; - } - m_value.value.ch = static_cast(value); - break; - } - - default: { - LOG_ERROR("VHPI: Unable to handle this format type %s", - ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); - return -1; - } - } - if (vhpi_put_value(GpiObjHdl::get_handle(), &m_value, map_put_value_mode(action))) { - check_vhpi_error(); - return -1; - } - - return 0; -} - -int VhpiSignalObjHdl::set_signal_value(double value, gpi_set_action_t action) -{ - switch (m_value.format) { - case vhpiRealVal: - m_value.numElems = 1; - m_value.bufSize = sizeof(value); - m_value.value.real = value; - break; - - default: { - LOG_ERROR("VHPI: Unable to set a Real handle with format type %s", - ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); - return -1; - } - - } - - if (vhpi_put_value(GpiObjHdl::get_handle(), &m_value, map_put_value_mode(action))) { - check_vhpi_error(); - return -1; - } - - return 0; -} - -int VhpiSignalObjHdl::set_signal_value_binstr(std::string &value, gpi_set_action_t action) -{ - switch (m_value.format) { - case vhpiEnumVal: - case vhpiLogicVal: { - m_value.value.enumv = chr2vhpi(value.c_str()[0]); - break; - } - - case vhpiEnumVecVal: - case vhpiLogicVecVal: { - - if ((int)value.length() != m_num_elems) { - LOG_ERROR("VHPI: Unable to set logic vector due to the string having incorrect length. Length of %d needs to be %d", value.length(), m_num_elems); - return -1; - } - - m_value.numElems = m_num_elems; - - std::string::iterator iter; - - int i = 0; - for (iter = value.begin(); - (iter != value.end()) && (i < m_num_elems); - iter++, i++) { - m_value.value.enumvs[i] = chr2vhpi(*iter); - } - - break; - } - - default: { - LOG_ERROR("VHPI: Unable to handle this format type: %s", - ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); - return -1; - } - } - - if (vhpi_put_value(GpiObjHdl::get_handle(), &m_value, map_put_value_mode(action))) { - check_vhpi_error(); - return -1; - } - - return 0; -} - -int VhpiSignalObjHdl::set_signal_value_str(std::string &value, gpi_set_action_t action) -{ - switch (m_value.format) { - - case vhpiStrVal: { - std::vector writable(value.begin(), value.end()); - writable.push_back('\0'); - strncpy(m_value.value.str, &writable[0], static_cast(m_value.numElems)); - m_value.value.str[m_value.numElems] = '\0'; - break; - } - - default: { - LOG_ERROR("VHPI: Unable to handle this format type: %s", - ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); - return -1; - } - } - - if (vhpi_put_value(GpiObjHdl::get_handle(), &m_value, map_put_value_mode(action))) { - check_vhpi_error(); - return -1; - } - - return 0; -} - -const char* VhpiSignalObjHdl::get_signal_value_binstr() -{ - switch (m_value.format) { - case vhpiRealVal: - LOG_INFO("VHPI: get_signal_value_binstr not supported for %s", - ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); - return ""; - default: { - /* Some simulators do not support BinaryValues so we fake up here for them */ - int ret = vhpi_get_value(GpiObjHdl::get_handle(), &m_binvalue); - if (ret) { - check_vhpi_error(); - LOG_ERROR("VHPI: Size of m_binvalue.value.str was not large enough: req=%d have=%d for type %s", - ret, - m_binvalue.bufSize, - ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); - } - - return m_binvalue.value.str; - } - } -} - -const char* VhpiSignalObjHdl::get_signal_value_str() -{ - switch (m_value.format) { - case vhpiStrVal: { - int ret = vhpi_get_value(GpiObjHdl::get_handle(), &m_value); - if (ret) { - check_vhpi_error(); - LOG_ERROR("VHPI: Size of m_value.value.str was not large enough: req=%d have=%d for type %s", - ret, - m_value.bufSize, - ((VhpiImpl*)GpiObjHdl::m_impl)->format_to_string(m_value.format)); - } - break; - } - default: { - LOG_ERROR("VHPI: Reading strings not valid for this handle"); - return ""; - } - } - return m_value.value.str; -} - -double VhpiSignalObjHdl::get_signal_value_real() -{ - m_value.format = vhpiRealVal; - m_value.numElems = 1; - m_value.bufSize = sizeof(double); - - if (vhpi_get_value(GpiObjHdl::get_handle(), &m_value)) { - check_vhpi_error(); - LOG_ERROR("VHPI: Failed to get value of type real"); - } - return m_value.value.real; -} - -long VhpiSignalObjHdl::get_signal_value_long() -{ - vhpiValueT value; - value.format = vhpiIntVal; - value.numElems = 0; - - if (vhpi_get_value(GpiObjHdl::get_handle(), &value)) { - check_vhpi_error(); - LOG_ERROR("VHPI: Failed to get value of type long"); - } - - return static_cast(value.value.intg); -} - - -GpiCbHdl * VhpiSignalObjHdl::value_change_cb(int edge) -{ - VhpiValueCbHdl *cb = NULL; - - switch (edge) { - case 1: - cb = &m_rising_cb; - break; - case 2: - cb = &m_falling_cb; - break; - case 3: - cb = &m_either_cb; - break; - default: - return NULL; - } - - if (cb->arm_callback()) { - return NULL; - } - - return cb; -} - -VhpiValueCbHdl::VhpiValueCbHdl(GpiImplInterface *impl, - VhpiSignalObjHdl *sig, - int edge) : GpiCbHdl(impl), - VhpiCbHdl(impl), - GpiValueCbHdl(impl,sig,edge) -{ - cb_data.reason = vhpiCbValueChange; - cb_data.time = &vhpi_time; - cb_data.obj = m_signal->get_handle(); -} - -VhpiStartupCbHdl::VhpiStartupCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - VhpiCbHdl(impl) -{ - cb_data.reason = vhpiCbStartOfSimulation; -} - -int VhpiStartupCbHdl::run_callback() { - vhpiHandleT tool, argv_iter, argv_hdl; - char **tool_argv = NULL; - int tool_argc = 0; - int i = 0; - - tool = vhpi_handle(vhpiTool, NULL); - if (tool) { - tool_argc = static_cast(vhpi_get(vhpiArgcP, tool)); - tool_argv = new char*[tool_argc]; - assert(tool_argv); - - argv_iter = vhpi_iterator(vhpiArgvs, tool); - if (argv_iter) { - while ((argv_hdl = vhpi_scan(argv_iter))) { - tool_argv[i] = const_cast(static_cast(vhpi_get_str(vhpiStrValP, argv_hdl))); - i++; - } - vhpi_release_handle(argv_iter); - } - - vhpi_release_handle(tool); - } - - gpi_embed_init(tool_argc, tool_argv); - delete [] tool_argv; - - return 0; -} - -VhpiShutdownCbHdl::VhpiShutdownCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - VhpiCbHdl(impl) -{ - cb_data.reason = vhpiCbEndOfSimulation; -} - -int VhpiShutdownCbHdl::run_callback() { - set_call_state(GPI_DELETE); - gpi_embed_end(); - return 0; -} - -VhpiTimedCbHdl::VhpiTimedCbHdl(GpiImplInterface *impl, uint64_t time) : GpiCbHdl(impl), - VhpiCbHdl(impl) -{ - vhpi_time.high = (uint32_t)(time>>32); - vhpi_time.low = (uint32_t)(time); - - cb_data.reason = vhpiCbAfterDelay; - cb_data.time = &vhpi_time; -} - -int VhpiTimedCbHdl::cleanup_callback() -{ - if (m_state == GPI_FREE) - return 1; - - vhpi_remove_cb(get_handle()); - - m_obj_hdl = NULL; - m_state = GPI_FREE; - return 1; -} - -VhpiReadwriteCbHdl::VhpiReadwriteCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - VhpiCbHdl(impl) -{ - cb_data.reason = vhpiCbRepLastKnownDeltaCycle; - cb_data.time = &vhpi_time; -} - -VhpiReadOnlyCbHdl::VhpiReadOnlyCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - VhpiCbHdl(impl) -{ - cb_data.reason = vhpiCbRepEndOfTimeStep; - cb_data.time = &vhpi_time; -} - -VhpiNextPhaseCbHdl::VhpiNextPhaseCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - VhpiCbHdl(impl) -{ - cb_data.reason = vhpiCbRepNextTimeStep; - cb_data.time = &vhpi_time; -} - -decltype(VhpiIterator::iterate_over) VhpiIterator::iterate_over = []{ - /* for reused lists */ - std::initializer_list root_options = { - vhpiInternalRegions, - vhpiSigDecls, - vhpiVarDecls, - vhpiPortDecls, - vhpiGenericDecls, - vhpiConstDecls, - // vhpiIndexedNames, - vhpiCompInstStmts, - vhpiBlockStmts, - }; - std::initializer_list sig_options = { - vhpiIndexedNames, - vhpiSelectedNames, - }; - std::initializer_list simplesig_options = { - vhpiDecls, - vhpiInternalRegions, - vhpiSensitivitys, - vhpiStmts, - }; - std::initializer_list gen_options = { - vhpiDecls, - vhpiInternalRegions, - vhpiSigDecls, - vhpiVarDecls, - vhpiConstDecls, - vhpiCompInstStmts, - vhpiBlockStmts, - }; - - return decltype(VhpiIterator::iterate_over) { - {vhpiRootInstK, root_options}, - {vhpiCompInstStmtK, root_options}, - - {vhpiGenericDeclK, sig_options}, - {vhpiSigDeclK, sig_options}, - {vhpiSelectedNameK, sig_options}, - {vhpiIndexedNameK, sig_options}, - {vhpiPortDeclK, sig_options}, - - {vhpiCondSigAssignStmtK, simplesig_options}, - {vhpiSimpleSigAssignStmtK, simplesig_options}, - {vhpiSelectSigAssignStmtK, simplesig_options}, - - {vhpiForGenerateK, gen_options}, - {vhpiIfGenerateK, gen_options}, - {vhpiBlockStmtK, gen_options}, - - {vhpiConstDeclK, { - vhpiAttrSpecs, - vhpiIndexedNames, - vhpiSelectedNames, - }}, - }; -}(); - -VhpiIterator::VhpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(impl, hdl), - m_iterator(NULL), - m_iter_obj(NULL) -{ - vhpiHandleT iterator; - vhpiHandleT vhpi_hdl = m_parent->get_handle(); - - vhpiClassKindT type = (vhpiClassKindT)vhpi_get(vhpiKindP, vhpi_hdl); - try { - selected = &iterate_over.at(type); - } - catch (std::out_of_range const&) { - LOG_WARN("VHPI: Implementation does not know how to iterate over %s(%d)", - vhpi_get_str(vhpiKindStrP, vhpi_hdl), type); - selected = nullptr; - return; - } - - /* Find the first mapping type that yields a valid iterator */ - for (one2many = selected->begin(); - one2many != selected->end(); - one2many++) { - - /* GPI_GENARRAY are pseudo-regions and all that should be searched for are the sub-regions */ - if (m_parent->get_type() == GPI_GENARRAY && *one2many != vhpiInternalRegions) { - LOG_DEBUG("VHPI: vhpi_iterator vhpiOneToManyT=%d skipped for GPI_GENARRAY type", *one2many); - continue; - } - - iterator = vhpi_iterator(*one2many, vhpi_hdl); - - if (iterator) - break; - - LOG_DEBUG("VHPI: vhpi_iterate vhpiOneToManyT=%d returned NULL", *one2many); - } - - if (NULL == iterator) { - LOG_DEBUG("VHPI: vhpi_iterate return NULL for all relationships on %s (%d) kind:%s", - vhpi_get_str(vhpiCaseNameP, vhpi_hdl), - type, - vhpi_get_str(vhpiKindStrP, vhpi_hdl)); - selected = NULL; - return; - } - - LOG_DEBUG("VHPI: Created iterator working from scope %d (%s)", - vhpi_get(vhpiKindP, vhpi_hdl), - vhpi_get_str(vhpiKindStrP, vhpi_hdl)); - - /* On some simulators (Aldec) vhpiRootInstK is a null level of hierarchy. - * We check that something is going to come back, if not, we try the level down. - */ - m_iter_obj = vhpi_hdl; - m_iterator = iterator; -} - -VhpiIterator::~VhpiIterator() -{ - if (m_iterator) - vhpi_release_handle(m_iterator); -} - -#define VHPI_TYPE_MIN (1000) - -GpiIterator::Status VhpiIterator::next_handle(std::string &name, - GpiObjHdl **hdl, - void **raw_hdl) -{ - vhpiHandleT obj; - GpiObjHdl *new_obj; - - if (!selected) - return GpiIterator::END; - - gpi_objtype_t obj_type = m_parent->get_type(); - std::string parent_name = m_parent->get_name(); - - /* We want the next object in the current mapping. - * If the end of mapping is reached then we want to - * try the next one until a new object is found. - */ - do { - obj = NULL; - - if (m_iterator) { - obj = vhpi_scan(m_iterator); - - /* For GPI_GENARRAY, only allow the generate statements through that match the name - * of the generate block. - */ - if (obj != NULL && obj_type == GPI_GENARRAY) { - if (vhpi_get(vhpiKindP, obj) == vhpiForGenerateK) { - std::string rgn_name = vhpi_get_str(vhpiCaseNameP, obj); - if (rgn_name.compare(0,parent_name.length(),parent_name) != 0) { - obj = NULL; - continue; - } - } else { - obj = NULL; - continue; - } - } - - if (obj != NULL && (vhpiProcessStmtK == vhpi_get(vhpiKindP, obj) || - vhpiCondSigAssignStmtK == vhpi_get(vhpiKindP, obj) || - vhpiSimpleSigAssignStmtK == vhpi_get(vhpiKindP, obj) || - vhpiSelectSigAssignStmtK == vhpi_get(vhpiKindP, obj))) { - LOG_DEBUG("VHPI: Skipping %s (%s)", vhpi_get_str(vhpiFullNameP, obj), - vhpi_get_str(vhpiKindStrP, obj)); - obj=NULL; - continue; - } - - if (obj != NULL) { - LOG_DEBUG("VHPI: Found an item %s", vhpi_get_str(vhpiFullNameP, obj)); - break; - } else { - LOG_DEBUG("VHPI: vhpi_scan on vhpiOneToManyT=%d returned NULL", *one2many); - } - - LOG_DEBUG("VHPI: End of vhpiOneToManyT=%d iteration", *one2many); - m_iterator = NULL; - } else { - LOG_DEBUG("VHPI: No valid vhpiOneToManyT=%d iterator", *one2many); - } - - if (++one2many >= selected->end()) { - obj = NULL; - break; - } - - /* GPI_GENARRAY are pseudo-regions and all that should be searched for are the sub-regions */ - if (obj_type == GPI_GENARRAY && *one2many != vhpiInternalRegions) { - LOG_DEBUG("VHPI: vhpi_iterator vhpiOneToManyT=%d skipped for GPI_GENARRAY type", *one2many); - continue; - } - - m_iterator = vhpi_iterator(*one2many, m_iter_obj); - - } while (!obj); - - if (NULL == obj) { - LOG_DEBUG("VHPI: No more children, all relationships have been tested"); - return GpiIterator::END; - } - - const char *c_name = vhpi_get_str(vhpiCaseNameP, obj); - if (!c_name) { - vhpiIntT type = vhpi_get(vhpiKindP, obj); - - if (type < VHPI_TYPE_MIN) { - *raw_hdl = (void*)obj; - return GpiIterator::NOT_NATIVE_NO_NAME; - } - - LOG_DEBUG("VHPI: Unable to get the name for this object of type " PRIu32, type); - - return GpiIterator::NATIVE_NO_NAME; - } - - /* - * If the parent is not a generate loop, then watch for generate handles and create - * the pseudo-region. - * - * NOTE: Taking advantage of the "caching" to only create one pseudo-region object. - * Otherwise a list would be required and checked while iterating - */ - if (*one2many == vhpiInternalRegions && obj_type != GPI_GENARRAY && vhpi_get(vhpiKindP, obj) == vhpiForGenerateK) { - std::string idx_str = c_name; - std::size_t found = idx_str.rfind(GEN_IDX_SEP_LHS); - - if (found != std::string::npos && found != 0) { - name = idx_str.substr(0,found); - obj = m_parent->get_handle(); - } else { - LOG_WARN("VHPI: Unhandled Generate Loop Format - %s", name.c_str()); - name = c_name; - } - } else { - name = c_name; - } - - LOG_DEBUG("VHPI: vhpi_scan found %s (%d) kind:%s name:%s", name.c_str(), - vhpi_get(vhpiKindP, obj), - vhpi_get_str(vhpiKindStrP, obj), - vhpi_get_str(vhpiCaseNameP, obj)); - - /* We try and create a handle internally, if this is not possible we - return and GPI will try other implementations with the name - */ - std::string fq_name = m_parent->get_fullname(); - if (fq_name == ":") { - fq_name += name; - } else if (obj_type == GPI_GENARRAY) { - std::size_t found = name.rfind(GEN_IDX_SEP_LHS); - - if (found != std::string::npos) { - fq_name += name.substr(found); - } else { - LOG_WARN("VHPI: Unhandled Sub-Element Format - %s", name.c_str()); - fq_name += "." + name; - } - } else if (obj_type == GPI_STRUCTURE) { - std::size_t found = name.rfind("."); - - if (found != std::string::npos) { - fq_name += name.substr(found); - name = name.substr(found+1); - } else { - LOG_WARN("VHPI: Unhandled Sub-Element Format - %s", name.c_str()); - fq_name += "." + name; - } - } else { - fq_name += "." + name; - } - VhpiImpl *vhpi_impl = reinterpret_cast(m_impl); - new_obj = vhpi_impl->create_gpi_obj_from_handle(obj, name, fq_name); - if (new_obj) { - *hdl = new_obj; - return GpiIterator::NATIVE; - } - else - return GpiIterator::NOT_NATIVE; -} diff --git a/cocotb/share/lib/vhpi/VhpiImpl.cpp b/cocotb/share/lib/vhpi/VhpiImpl.cpp deleted file mode 100644 index f5b986aa..00000000 --- a/cocotb/share/lib/vhpi/VhpiImpl.cpp +++ /dev/null @@ -1,1008 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2014, 2018 Potential Ventures Ltd -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd not the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#include "VhpiImpl.h" -#include -#include -#include - -extern "C" { -static VhpiCbHdl *sim_init_cb; -static VhpiCbHdl *sim_finish_cb; -static VhpiImpl *vhpi_table; -} - -#define CASE_STR(_X) \ - case _X: return #_X - -const char * VhpiImpl::format_to_string(int format) -{ - switch (format) { - CASE_STR(vhpiBinStrVal); - CASE_STR(vhpiOctStrVal); - CASE_STR(vhpiDecStrVal); - CASE_STR(vhpiHexStrVal); - CASE_STR(vhpiEnumVal); - CASE_STR(vhpiIntVal); - CASE_STR(vhpiLogicVal); - CASE_STR(vhpiRealVal); - CASE_STR(vhpiStrVal); - CASE_STR(vhpiCharVal); - CASE_STR(vhpiTimeVal); - CASE_STR(vhpiPhysVal); - CASE_STR(vhpiObjTypeVal); - CASE_STR(vhpiPtrVal); - CASE_STR(vhpiEnumVecVal); - CASE_STR(vhpiRawDataVal); - - default: return "unknown"; - } -} - -const char *VhpiImpl::reason_to_string(int reason) -{ - switch (reason) { - CASE_STR(vhpiCbValueChange); - CASE_STR(vhpiCbStartOfNextCycle); - CASE_STR(vhpiCbStartOfPostponed); - CASE_STR(vhpiCbEndOfTimeStep); - CASE_STR(vhpiCbNextTimeStep); - CASE_STR(vhpiCbAfterDelay); - CASE_STR(vhpiCbStartOfSimulation); - CASE_STR(vhpiCbEndOfSimulation); - CASE_STR(vhpiCbEndOfProcesses); - CASE_STR(vhpiCbLastKnownDeltaCycle); - - default: return "unknown"; - } -} - -#undef CASE_STR - -void VhpiImpl::get_sim_time(uint32_t *high, uint32_t *low) -{ - vhpiTimeT vhpi_time_s; - vhpi_get_time(&vhpi_time_s, NULL); - check_vhpi_error(); - *high = vhpi_time_s.high; - *low = vhpi_time_s.low; -} - -static int32_t log10int(uint64_t v) -{ - int32_t i = -1; - do { - v /= 10; - i += 1; - } while (v); - return i; -} - -void VhpiImpl::get_sim_precision(int32_t *precision) -{ - /* The value returned is in number of femtoseconds */ - vhpiPhysT prec = vhpi_get_phys(vhpiResolutionLimitP, NULL); - uint64_t femtoseconds = ((uint64_t)prec.high << 32) | prec.low; - *precision = log10int(femtoseconds) - 15; -} - -const char *VhpiImpl::get_simulator_product() -{ - if (m_product.empty()) { - vhpiHandleT tool = vhpi_handle(vhpiTool, NULL); - if (tool) { - m_product = vhpi_get_str(vhpiNameP, tool); - vhpi_release_handle(tool); - } else { - m_product = "UNKNOWN"; - } - } - return m_product.c_str(); -} - -const char *VhpiImpl::get_simulator_version() -{ - if (m_version.empty()) { - vhpiHandleT tool = vhpi_handle(vhpiTool, NULL); - if (tool) { - m_version = vhpi_get_str(vhpiToolVersionP, tool); - vhpi_release_handle(tool); - } else { - m_version = "UNKNOWN"; - } - } - return m_version.c_str(); -} - -// Determine whether a VHPI object type is a constant or not -bool is_const(vhpiHandleT hdl) -{ - vhpiHandleT tmp = hdl; - - /* Need to walk the prefix's back to the original handle to get a type - * that is not vhpiSelectedNameK or vhpiIndexedNameK - */ - do { - vhpiIntT vhpitype = vhpi_get(vhpiKindP, tmp); - if (vhpiConstDeclK == vhpitype || vhpiGenericDeclK == vhpitype) - return true; - } while ((tmp = vhpi_handle(vhpiPrefix, tmp)) != NULL); - - return false; -} - -bool is_enum_logic(vhpiHandleT hdl) { - const char *type = vhpi_get_str(vhpiNameP, hdl); - - if (0 == strncmp(type, "BIT" , sizeof("BIT")-1) || - 0 == strncmp(type, "STD_ULOGIC", sizeof("STD_ULOGIC")-1) || - 0 == strncmp(type, "STD_LOGIC" , sizeof("STD_LOGIC")-1)) { - return true; - } else { - vhpiIntT num_enum = vhpi_get(vhpiNumLiteralsP, hdl); - - if (2 == num_enum) { - vhpiHandleT it = vhpi_iterator(vhpiEnumLiterals, hdl); - if (it != NULL) { - const char *enums_1[2] = { "0", "1"}; //Aldec does not return the single quotes - const char *enums_2[2] = {"'0'", "'1'"}; - vhpiHandleT enum_hdl; - int cnt = 0; - - while ((enum_hdl = vhpi_scan(it)) != NULL) { - const char *etype = vhpi_get_str(vhpiStrValP, enum_hdl); - if (1 < cnt || - (0 != strncmp(etype, enums_1[cnt], strlen(enums_1[cnt])) && - 0 != strncmp(etype, enums_2[cnt], strlen(enums_2[cnt])))) { - vhpi_release_handle(it); - return false; - } - ++cnt; - } - return true; - } - } else if (9 == num_enum) { - vhpiHandleT it = vhpi_iterator(vhpiEnumLiterals, hdl); - if (it != NULL) { - const char *enums_1[9] = { "U", "X", "0", "1", "Z", "W", "L", "H", "-"}; //Aldec does not return the single quotes - const char *enums_2[9] = {"'U'", "'X'", "'0'", "'1'", "'Z'", "'W'", "'L'", "'H'", "'-'"}; - vhpiHandleT enum_hdl; - int cnt = 0; - - while ((enum_hdl = vhpi_scan(it)) != NULL) { - const char *etype = vhpi_get_str(vhpiStrValP, enum_hdl); - if (8 < cnt || - (0 != strncmp(etype, enums_1[cnt], strlen(enums_1[cnt])) && - 0 != strncmp(etype, enums_2[cnt], strlen(enums_2[cnt])))) { - vhpi_release_handle(it); - return false; - } - ++cnt; - } - return true; - } - } - } - - return false; -} - -bool is_enum_char(vhpiHandleT hdl) { - const vhpiIntT NUM_ENUMS_IN_CHAR_TYPE = 256; - - const char *type = vhpi_get_str(vhpiNameP, hdl); - - if (0 == strncmp(type, "CHARACTER", sizeof("STD_ULOGIC")-1)) { - return true; - } else { - return (vhpi_get(vhpiNumLiteralsP, hdl) == NUM_ENUMS_IN_CHAR_TYPE); - } -} - -bool is_enum_boolean(vhpiHandleT hdl) { - const char *type = vhpi_get_str(vhpiNameP, hdl); - - if (0 == strncmp(type, "BOOLEAN", sizeof("BOOLEAN")-1)) { - return true; - } else { - vhpiIntT num_enum = vhpi_get(vhpiNumLiteralsP, hdl); - - if (2 == num_enum) { - vhpiHandleT it = vhpi_iterator(vhpiEnumLiterals, hdl); - if (it != NULL) { - vhpiHandleT enum_hdl; - int cnt = 0; - - while ((enum_hdl = vhpi_scan(it)) != NULL) { - const char *etype = vhpi_get_str(vhpiStrValP, enum_hdl); - if (((0 == cnt && 0 != strncmp(etype, "FALSE", strlen("FALSE"))) && - (0 == cnt && 0 != strncmp(etype, "false", strlen("false")))) || - ((1 == cnt && 0 != strncmp(etype, "TRUE" , strlen("TRUE"))) && - (1 == cnt && 0 != strncmp(etype, "true" , strlen("true")))) || - 2 <= cnt) { - vhpi_release_handle(it); - return false; - } - ++cnt; - } - return true; - } - } - } - - return false; -} - -GpiObjHdl *VhpiImpl::create_gpi_obj_from_handle(vhpiHandleT new_hdl, - std::string &name, - std::string &fq_name) -{ - vhpiIntT type; - gpi_objtype_t gpi_type; - GpiObjHdl *new_obj = NULL; - - if (vhpiVerilog == (type = vhpi_get(vhpiKindP, new_hdl))) { - LOG_DEBUG("VHPI: vhpiVerilog returned from vhpi_get(vhpiType, ...)") - return NULL; - } - - /* We need to delve further here to determine how to later set - the values of an object */ - vhpiHandleT base_hdl = vhpi_handle(vhpiBaseType, new_hdl); - - if (base_hdl == NULL) { - vhpiHandleT st_hdl = vhpi_handle(vhpiSubtype, new_hdl); - - if (st_hdl != NULL) { - base_hdl = vhpi_handle(vhpiBaseType, st_hdl); - vhpi_release_handle(st_hdl); - } - } - - vhpiHandleT query_hdl = (base_hdl != NULL) ? base_hdl : new_hdl; - - vhpiIntT base_type = vhpi_get(vhpiKindP, query_hdl); - vhpiIntT is_static = vhpi_get(vhpiStaticnessP, query_hdl); - - /* Non locally static objects are not accessible for read/write - so we create this as a GpiObjType - */ - if (is_static == vhpiGloballyStatic) { - gpi_type = GPI_MODULE; - goto create; - } - - switch (base_type) { - case vhpiArrayTypeDeclK: { - vhpiIntT num_dim = vhpi_get(vhpiNumDimensionsP, query_hdl); - - if (num_dim > 1) { - LOG_DEBUG("VHPI: Detected a MULTI-DIMENSIONAL ARRAY type %s", fq_name.c_str()); - gpi_type = GPI_ARRAY; - } else { - vhpiHandleT elem_base_type_hdl = NULL; - vhpiIntT elem_base_type = 0; - - /* vhpiElemSubtype is deprecated. Should be using vhpiElemType, but not supported in all simulators. */ - vhpiHandleT elem_sub_type_hdl = vhpi_handle(vhpiElemSubtype, query_hdl); - - if (elem_sub_type_hdl != NULL) { - elem_base_type_hdl = vhpi_handle(vhpiBaseType, elem_sub_type_hdl); - vhpi_release_handle(elem_sub_type_hdl); - } - - if (elem_base_type_hdl != NULL) { - elem_base_type = vhpi_get(vhpiKindP, elem_base_type_hdl); - if (elem_base_type == vhpiEnumTypeDeclK) { - if (is_enum_logic(elem_base_type_hdl)) { - LOG_DEBUG("VHPI: Detected a LOGIC VECTOR type %s", fq_name.c_str()); - gpi_type = GPI_REGISTER; - } else if (is_enum_char(elem_base_type_hdl)) { - LOG_DEBUG("VHPI: Detected a STRING type %s", fq_name.c_str()); - gpi_type = GPI_STRING; - } else { - LOG_DEBUG("VHPI: Detected a NON-LOGIC ENUM VECTOR type %s", fq_name.c_str()); - gpi_type = GPI_ARRAY; - } - } else { - LOG_DEBUG("VHPI: Detected a NON-ENUM VECTOR type %s", fq_name.c_str()); - gpi_type = GPI_ARRAY; - } - } else { - LOG_ERROR("VHPI: Unable to determine the Array Element Base Type for %s. Defaulting to GPI_ARRAY.", vhpi_get_str(vhpiFullCaseNameP, new_hdl)); - gpi_type = GPI_ARRAY; - } - } - break; - } - - case vhpiEnumTypeDeclK: { - if (is_enum_logic(query_hdl)) { - LOG_DEBUG("VHPI: Detected a LOGIC type %s", fq_name.c_str()); - gpi_type = GPI_REGISTER; - } else if (is_enum_char(query_hdl)) { - LOG_DEBUG("VHPI: Detected a CHAR type %s", fq_name.c_str()); - gpi_type = GPI_INTEGER; - } else if (is_enum_boolean(query_hdl)) { - LOG_DEBUG("VHPI: Detected a BOOLEAN/INTEGER type %s", fq_name.c_str()); - gpi_type = GPI_INTEGER; - } else { - LOG_DEBUG("VHPI: Detected an ENUM type %s", fq_name.c_str()); - gpi_type = GPI_ENUM; - } - break; - } - - case vhpiIntTypeDeclK: { - LOG_DEBUG("VHPI: Detected an INT type %s", fq_name.c_str()); - gpi_type = GPI_INTEGER; - break; - } - - case vhpiFloatTypeDeclK: { - LOG_DEBUG("VHPI: Detected a REAL type %s", fq_name.c_str()); - gpi_type = GPI_REAL; - break; - } - - case vhpiRecordTypeDeclK: { - LOG_DEBUG("VHPI: Detected a STRUCTURE type %s", fq_name.c_str()); - gpi_type = GPI_STRUCTURE; - break; - } - - case vhpiProcessStmtK: - case vhpiSimpleSigAssignStmtK: - case vhpiCondSigAssignStmtK: - case vhpiSelectSigAssignStmtK: { - gpi_type = GPI_MODULE; - break; - } - - case vhpiRootInstK: - case vhpiIfGenerateK: - case vhpiForGenerateK: - case vhpiBlockStmtK: - case vhpiCompInstStmtK: { - std::string hdl_name = vhpi_get_str(vhpiCaseNameP, new_hdl); - - if (base_type == vhpiRootInstK && hdl_name != name) { - vhpiHandleT arch = vhpi_handle(vhpiDesignUnit, new_hdl); - - if (NULL != arch) { - vhpiHandleT prim = vhpi_handle(vhpiPrimaryUnit, arch); - - if (NULL != prim) { - hdl_name = vhpi_get_str(vhpiCaseNameP, prim); - } - } - } - - if (name != hdl_name) { - LOG_DEBUG("VHPI: Found pseudo-region %s", fq_name.c_str()); - gpi_type = GPI_GENARRAY; - } else { - gpi_type = GPI_MODULE; - } - break; - } - - default: { - LOG_ERROR("VHPI: Not able to map type (%s) %u to object", - vhpi_get_str(vhpiKindStrP, query_hdl), type); - new_obj = NULL; - goto out; - } - } - -create: - LOG_DEBUG("VHPI: Creating %s of type %d (%s)", - vhpi_get_str(vhpiFullCaseNameP, new_hdl), - gpi_type, - vhpi_get_str(vhpiKindStrP, query_hdl)); - - if (gpi_type != GPI_ARRAY && gpi_type != GPI_GENARRAY && gpi_type != GPI_MODULE && gpi_type != GPI_STRUCTURE) { - if (gpi_type == GPI_REGISTER) - new_obj = new VhpiLogicSignalObjHdl(this, new_hdl, gpi_type, is_const(new_hdl)); - else - new_obj = new VhpiSignalObjHdl(this, new_hdl, gpi_type, is_const(new_hdl)); - } else if (gpi_type == GPI_ARRAY) { - new_obj = new VhpiArrayObjHdl(this, new_hdl, gpi_type); - } else { - new_obj = new VhpiObjHdl(this, new_hdl, gpi_type); - } - - if (new_obj->initialise(name, fq_name)) { - delete new_obj; - new_obj = NULL; - } - -out: - if (base_hdl != NULL) - vhpi_release_handle(base_hdl); - - return new_obj; -} - -GpiObjHdl *VhpiImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) -{ - LOG_DEBUG("VHPI: Trying to convert raw to VHPI handle"); - - vhpiHandleT new_hdl = (vhpiHandleT)raw_hdl; - - std::string fq_name = parent->get_fullname(); - const char *c_name = vhpi_get_str(vhpiCaseNameP, new_hdl); - if (!c_name) { - LOG_DEBUG("VHPI: Unable to query name of passed in handle"); - return NULL; - } - - std::string name = c_name; - - if (fq_name == ":") { - fq_name += name; - } else { - fq_name += "." + name; - } - - GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); - if (new_obj == NULL) { - vhpi_release_handle(new_hdl); - LOG_DEBUG("VHPI: Unable to fetch object %s", fq_name.c_str()); - return NULL; - } - - return new_obj; -} - -GpiObjHdl *VhpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) -{ - vhpiHandleT vhpi_hdl = parent->get_handle(); - - vhpiHandleT new_hdl; - std::string fq_name = parent->get_fullname(); - if (fq_name == ":") { - fq_name += name; - } else { - fq_name += "." + name; - } - std::vector writable(fq_name.begin(), fq_name.end()); - writable.push_back('\0'); - - new_hdl = vhpi_handle_by_name(&writable[0], NULL); - - if (new_hdl == NULL && parent->get_type() == GPI_STRUCTURE) { - /* vhpi_handle_by_name() doesn't always work for records, specificaly records in generics */ - vhpiHandleT iter = vhpi_iterator(vhpiSelectedNames, vhpi_hdl); - if (iter != NULL) { - while ((new_hdl = vhpi_scan(iter)) != NULL) { - std::string selected_name = vhpi_get_str(vhpiCaseNameP, new_hdl); - std::size_t found = selected_name.find_last_of("."); - - if (found != std::string::npos) { - selected_name = selected_name.substr(found+1); - } - - if (selected_name == name) { - vhpi_release_handle(iter); - break; - } - } - } - } else if (new_hdl == NULL) { - /* If not found, check to see if the name of a generate loop */ - vhpiHandleT iter = vhpi_iterator(vhpiInternalRegions, vhpi_hdl); - - if (iter != NULL) { - vhpiHandleT rgn; - for (rgn = vhpi_scan(iter); rgn != NULL; rgn = vhpi_scan(iter)) { - if (vhpi_get(vhpiKindP, rgn) == vhpiForGenerateK) { - std::string rgn_name = vhpi_get_str(vhpiCaseNameP, rgn); - if (rgn_name.compare(0,name.length(),name) == 0) { - new_hdl = vhpi_hdl; - vhpi_release_handle(iter); - break; - } - } - } - } - if (new_hdl == NULL) { - LOG_DEBUG("VHPI: Unable to query vhpi_handle_by_name %s", fq_name.c_str()); - return NULL; - } - } - - /* Generate Loops have inconsistent behavior across VHPI. A "name" - * without an index, i.e. dut.loop vs dut.loop(0), may or may not map to - * to the start index. If it doesn't then it won't find anything. - * - * If this unique case is hit, we need to create the Pseudo-region, with the handle - * being equivalent to the parent handle. - */ - if (vhpi_get(vhpiKindP, new_hdl) == vhpiForGenerateK) { - vhpi_release_handle(new_hdl); - - new_hdl = vhpi_hdl; - } - - GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); - if (new_obj == NULL) { - vhpi_release_handle(new_hdl); - LOG_DEBUG("VHPI: Unable to fetch object %s", fq_name.c_str()); - return NULL; - } - - return new_obj; -} - -GpiObjHdl *VhpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) -{ - vhpiHandleT vhpi_hdl = parent->get_handle(); - std::string name = parent->get_name(); - std::string fq_name = parent->get_fullname(); - vhpiHandleT new_hdl = NULL; - char buff[14]; // needs to be large enough to hold -2^31 to 2^31-1 in string form ('(''-'10+'')'\0') - - gpi_objtype_t obj_type = parent->get_type(); - - if (obj_type == GPI_GENARRAY) { - LOG_DEBUG("VHPI: Native check create for index %d of parent %s (pseudo-region)", - index, - parent->get_name_str()); - - snprintf(buff, sizeof(buff), "%d", index); - - std::string idx_str = buff; - name += (GEN_IDX_SEP_LHS + idx_str + GEN_IDX_SEP_RHS); - fq_name += (GEN_IDX_SEP_LHS + idx_str + GEN_IDX_SEP_RHS); - - std::vector writable(fq_name.begin(), fq_name.end()); - writable.push_back('\0'); - - new_hdl = vhpi_handle_by_name(&writable[0], NULL); - } else if (obj_type == GPI_REGISTER || obj_type == GPI_ARRAY || obj_type == GPI_STRING) { - LOG_DEBUG("VHPI: Native check create for index %d of parent %s (%s)", - index, - parent->get_fullname_str(), - vhpi_get_str(vhpiKindStrP, vhpi_hdl)); - - snprintf(buff, sizeof(buff), "(%d)", index); - - std::string idx_str = buff; - name += idx_str; - fq_name += idx_str; - - vhpiHandleT base_hdl = vhpi_handle(vhpiBaseType, vhpi_hdl); - - if (base_hdl == NULL) { - vhpiHandleT st_hdl = vhpi_handle(vhpiSubtype, vhpi_hdl); - - if (st_hdl != NULL) { - base_hdl = vhpi_handle(vhpiBaseType, st_hdl); - vhpi_release_handle(st_hdl); - } - } - - if (base_hdl == NULL) { - LOG_ERROR("VHPI: Unable to get the vhpiBaseType of %s", parent->get_fullname_str()); - return NULL; - } - - vhpiIntT num_dim = vhpi_get(vhpiNumDimensionsP, base_hdl); - int idx = 0; - - /* Need to translate the index into a zero-based flattened array index */ - if (num_dim > 1) { - std::string hdl_name = vhpi_get_str(vhpiCaseNameP, vhpi_hdl); - std::vector indices; - - /* Need to determine how many indices have been received. A valid handle will only - * be found when all indices are received, otherwise need a pseudo-handle. - * - * When working with pseudo-handles: - * hdl_name: sig_name - * parent->get_name(): sig_name(x)(y)... where x,y,... are the indices to a multi-dimensional array. - * pseudo_idx: (x)(y)... - */ - if (hdl_name.length() < parent->get_name().length()) { - std::string pseudo_idx = parent->get_name().substr(hdl_name.length()); - - while (pseudo_idx.length() > 0) { - std::size_t found = pseudo_idx.find_first_of(")"); - - if (found != std::string::npos) { - indices.push_back(atoi(pseudo_idx.substr(1,found-1).c_str())); - pseudo_idx = pseudo_idx.substr(found+1); - } else { - break; - } - } - } - - indices.push_back(index); - - if (indices.size() == num_dim) { -#ifdef IUS - /* IUS/Xcelium does not appear to set the vhpiIsUnconstrainedP property. IUS Docs say it will return - * -1 if unconstrained, but with vhpiIntT being unsigned, the value returned is below. - */ - const vhpiIntT UNCONSTRAINED = 2147483647; -#endif - - std::vector constraints; - - /* All necessary indices are available, need to iterate over dimension constraints to - * determine the index into the zero-based flattened array. - * - * Check the constraints on the base type first. (always works for Aldec, but not unconstrained types in IUS/Xcelium) - * If the base type fails, then try the sub-type. (sub-type is listed as deprecated for Aldec) - */ - vhpiHandleT it, constraint; - - it = vhpi_iterator(vhpiConstraints, base_hdl); - - if (it != NULL) { - while ((constraint = vhpi_scan(it)) != NULL) { -#ifdef IUS - vhpiIntT l_rng = vhpi_get(vhpiLeftBoundP, constraint); - vhpiIntT r_rng = vhpi_get(vhpiRightBoundP, constraint); - if (l_rng == UNCONSTRAINED || r_rng == UNCONSTRAINED) { -#else - if (vhpi_get(vhpiIsUnconstrainedP, constraint)) { -#endif - /* Bail and try the sub-type handle */ - vhpi_release_handle(it); - break; - } - constraints.push_back(constraint); - } - } - - /* If all the dimensions were not obtained, try again with the sub-type handle */ - if (constraints.size() != num_dim) { - vhpiHandleT sub_hdl = vhpi_handle(vhpiSubtype, vhpi_hdl);; - - constraints.clear(); - - if (sub_hdl != NULL) { - it = vhpi_iterator(vhpiConstraints, sub_hdl); - - if (it != NULL) { - while ((constraint = vhpi_scan(it)) != NULL) { - /* IUS/Xcelium only sets the vhpiIsUnconstrainedP incorrectly on the base type */ - if (vhpi_get(vhpiIsUnconstrainedP, constraint)) { - vhpi_release_handle(it); - break; - } - constraints.push_back(constraint); - } - } - } - } - - if (constraints.size() == num_dim) { - int scale = 1; - - while (constraints.size() > 0) { - int raw_idx = indices.back(); - constraint = constraints.back(); - - int left = static_cast(vhpi_get(vhpiLeftBoundP, constraint)); - int right = static_cast(vhpi_get(vhpiRightBoundP, constraint)); - int len = 0; - - if (left > right) { - idx += (scale * (left - raw_idx)); - len = left - right + 1; - } else { - idx += (scale * (raw_idx - left)); - len = right - left + 1; - } - scale = scale * len; - - indices.pop_back(); - constraints.pop_back(); - } - } else { - LOG_ERROR("VHPI: Unable to access all constraints for %s", parent->get_fullname_str()); - return NULL; - } - - } else { - new_hdl = vhpi_hdl; // Set to the parent handle to create the pseudo-handle - } - } else { - int left = parent->get_range_left(); - int right = parent->get_range_right(); - - if (left > right) { - idx = left - index; - } else { - idx = index - left; - } - } - - if (new_hdl == NULL) { - new_hdl = vhpi_handle_by_index(vhpiIndexedNames, vhpi_hdl, idx); - if (!new_hdl) { - /* Support for the above seems poor, so if it did not work - try an iteration instead, spotty support for multi-dimensional arrays */ - - vhpiHandleT iter = vhpi_iterator(vhpiIndexedNames, vhpi_hdl); - if (iter != NULL) { - int curr_index = 0; - while ((new_hdl = vhpi_scan(iter)) != NULL) { - if (idx == curr_index) { - vhpi_release_handle(iter); - break; - } - curr_index++; - } - } - } - - if (new_hdl != NULL) { - LOG_DEBUG("VHPI: Index (%d->%d) found %s (%s)", index, idx, vhpi_get_str(vhpiCaseNameP, new_hdl), vhpi_get_str(vhpiKindStrP, new_hdl)); - } - } - } else { - LOG_ERROR("VHPI: Parent of type %s must be of type GPI_GENARRAY, GPI_REGISTER, GPI_ARRAY, or GPI_STRING to have an index.", parent->get_type_str()); - return NULL; - } - - - if (new_hdl == NULL) { - LOG_DEBUG("VHPI: Unable to query vhpi_handle_by_index %d", index); - return NULL; - } - - GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); - if (new_obj == NULL) { - vhpi_release_handle(new_hdl); - LOG_DEBUG("VHPI: Could not fetch object below entity (%s) at index (%d)", - parent->get_name_str(), index); - return NULL; - } - - return new_obj; -} - -GpiObjHdl *VhpiImpl::get_root_handle(const char* name) -{ - vhpiHandleT root = NULL; - vhpiHandleT arch = NULL; - vhpiHandleT dut = NULL; - std::string root_name; - const char *found; - - root = vhpi_handle(vhpiRootInst, NULL); - check_vhpi_error(); - - if (!root) { - LOG_ERROR("VHPI: Attempting to get the vhpiRootInst failed"); - return NULL; - } else { - LOG_DEBUG("VHPI: We have found root='%s'", vhpi_get_str(vhpiCaseNameP, root)); - } - - if (name) { - if (NULL == (dut = vhpi_handle_by_name(name, NULL))) { - LOG_DEBUG("VHPI: Unable to query by name"); - check_vhpi_error(); - } - } - - if (!dut) { - if (NULL == (arch = vhpi_handle(vhpiDesignUnit, root))) { - LOG_DEBUG("VHPI: Unable to get vhpiDesignUnit via root"); - check_vhpi_error(); - return NULL; - } - - if (NULL == (dut = vhpi_handle(vhpiPrimaryUnit, arch))) { - LOG_DEBUG("VHPI: Unable to get vhpiPrimaryUnit via arch"); - check_vhpi_error(); - return NULL; - } - - /* If this matches the name then it is what we want, but we - use the handle two levels up as the DUT as we do not want an - object of type vhpiEntityDeclK as the DUT */ - - found = vhpi_get_str(vhpiCaseNameP, dut); - dut = root; - - } else { - found = vhpi_get_str(vhpiCaseNameP, dut); - } - - if (!dut) { - LOG_ERROR("VHPI: Attempting to get the DUT handle failed"); - return NULL; - } - - if (!found) { - LOG_ERROR("VHPI: Unable to query name for DUT handle"); - return NULL; - } - - if (name != NULL && strcmp(name, found)) { - LOG_WARN("VHPI: DUT '%s' doesn't match requested toplevel %s", found, name); - return NULL; - } - - root_name = found; - - return create_gpi_obj_from_handle(dut, root_name, root_name); - -} - -GpiIterator *VhpiImpl::iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) -{ - GpiIterator *new_iter = NULL; - - switch (type) { - case GPI_OBJECTS: - new_iter = new VhpiIterator(this, obj_hdl); - break; - case GPI_DRIVERS: - LOG_WARN("VHPI: Drivers iterator not implemented yet"); - break; - case GPI_LOADS: - LOG_WARN("VHPI: Loads iterator not implemented yet"); - break; - default: - LOG_WARN("VHPI: Other iterator types not implemented yet"); - break; - } - return new_iter; -} - -GpiCbHdl *VhpiImpl::register_timed_callback(uint64_t time) -{ - VhpiTimedCbHdl *hdl = new VhpiTimedCbHdl(this, time); - - if (hdl->arm_callback()) { - delete(hdl); - hdl = NULL; - } - - return hdl; -} - -GpiCbHdl *VhpiImpl::register_readwrite_callback() -{ - if (m_read_write.arm_callback()) - return NULL; - - return &m_read_write; -} - -GpiCbHdl *VhpiImpl::register_readonly_callback() -{ - if (m_read_only.arm_callback()) - return NULL; - - return &m_read_only; -} - -GpiCbHdl *VhpiImpl::register_nexttime_callback() -{ - if (m_next_phase.arm_callback()) - return NULL; - - return &m_next_phase; -} - -int VhpiImpl::deregister_callback(GpiCbHdl *gpi_hdl) -{ - gpi_hdl->cleanup_callback(); - return 0; -} - -void VhpiImpl::sim_end() -{ - sim_finish_cb->set_call_state(GPI_DELETE); - vhpi_control(vhpiFinish, vhpiDiagTimeLoc); - check_vhpi_error(); -} - -extern "C" { - -// Main entry point for callbacks from simulator -void handle_vhpi_callback(const vhpiCbDataT *cb_data) -{ - VhpiCbHdl *cb_hdl = (VhpiCbHdl*)cb_data->user_data; - - if (!cb_hdl) { - LOG_CRITICAL("VHPI: Callback data corrupted: ABORTING"); - gpi_embed_end(); - return; - } - - gpi_cb_state_e old_state = cb_hdl->get_call_state(); - - if (old_state == GPI_PRIMED) { - - cb_hdl->set_call_state(GPI_CALL); - cb_hdl->run_callback(); - - gpi_cb_state_e new_state = cb_hdl->get_call_state(); - - /* We have re-primed in the handler */ - if (new_state != GPI_PRIMED) - if (cb_hdl->cleanup_callback()) { - delete cb_hdl; - } - - } - - return; -}; - -static void register_initial_callback() -{ - sim_init_cb = new VhpiStartupCbHdl(vhpi_table); - sim_init_cb->arm_callback(); -} - -static void register_final_callback() -{ - sim_finish_cb = new VhpiShutdownCbHdl(vhpi_table); - sim_finish_cb->arm_callback(); -} - -static void register_embed() -{ - vhpi_table = new VhpiImpl("VHPI"); - gpi_register_impl(vhpi_table); -} - -// pre-defined VHPI registration table -COCOTBVHPI_EXPORT void (*vhpi_startup_routines[])() = { - register_embed, - gpi_load_extra_libs, - register_initial_callback, - register_final_callback, - nullptr -}; - -// For non-VHPI compliant applications that cannot find vhpi_startup_routines -COCOTBVHPI_EXPORT void vhpi_startup_routines_bootstrap() { - void (*routine)(); - int i; - routine = vhpi_startup_routines[0]; - for (i = 0, routine = vhpi_startup_routines[i]; - routine; - routine = vhpi_startup_routines[++i]) { - routine(); - } -} - -} - -GPI_ENTRY_POINT(cocotbvhpi, register_embed) diff --git a/cocotb/share/lib/vhpi/VhpiImpl.h b/cocotb/share/lib/vhpi/VhpiImpl.h deleted file mode 100644 index 5eaff19b..00000000 --- a/cocotb/share/lib/vhpi/VhpiImpl.h +++ /dev/null @@ -1,288 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2013 Potential Ventures Ltd -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#ifndef COCOTB_VHPI_IMPL_H_ -#define COCOTB_VHPI_IMPL_H_ - -#include -#ifdef COCOTBVHPI_EXPORTS -#define COCOTBVHPI_EXPORT COCOTB_EXPORT -#else -#define COCOTBVHPI_EXPORT COCOTB_IMPORT -#endif - -#include "../gpi/gpi_priv.h" -#include -#include -#include -#include - -// Define Index separator -#ifdef IUS -#define GEN_IDX_SEP_LHS "(" -#define GEN_IDX_SEP_RHS ")" -#else -#define GEN_IDX_SEP_LHS "__" -#define GEN_IDX_SEP_RHS "" -#endif - -// Should be run after every VHPI call to check error status -static inline int __check_vhpi_error(const char *file, const char *func, long line) -{ - int err_occurred = 0; -#if VHPI_CHECKING - vhpiErrorInfoT info; - enum gpi_log_levels loglevel; - err_occurred = vhpi_check_error(&info); - if (!err_occurred) - return 0; - - switch (info.severity) { - case vhpiNote: - loglevel = GPIInfo; - break; - case vhpiWarning: - loglevel = GPIWarning; - break; - case vhpiError: - loglevel = GPIError; - break; - case vhpiFailure: - case vhpiSystem: - case vhpiInternal: - loglevel = GPICritical; - break; - default: - loglevel = GPIInfo; - break; - } - - gpi_log("cocotb.gpi", loglevel, file, func, line, - "VHPI Error level %d: %s\nFILE %s:%d", - info.severity, info.message, info.file, info.line); - -#endif - return err_occurred; -} - -#define check_vhpi_error() do { \ - __check_vhpi_error(__FILE__, __func__, __LINE__); \ -} while (0) - -class VhpiCbHdl : public virtual GpiCbHdl { -public: - VhpiCbHdl(GpiImplInterface *impl); - - int arm_callback() override; - int cleanup_callback() override; - -protected: - vhpiCbDataT cb_data; - vhpiTimeT vhpi_time; -}; - -class VhpiSignalObjHdl; - -class VhpiValueCbHdl : public VhpiCbHdl, public GpiValueCbHdl { -public: - VhpiValueCbHdl(GpiImplInterface *impl, VhpiSignalObjHdl *sig, int edge); - int cleanup_callback() override { - return VhpiCbHdl::cleanup_callback(); - } -private: - std::string initial_value; -}; - -class VhpiTimedCbHdl : public VhpiCbHdl { -public: - VhpiTimedCbHdl(GpiImplInterface *impl, uint64_t time); - int cleanup_callback() override; -}; - -class VhpiReadOnlyCbHdl : public VhpiCbHdl { -public: - VhpiReadOnlyCbHdl(GpiImplInterface *impl); -}; - -class VhpiNextPhaseCbHdl : public VhpiCbHdl { -public: - VhpiNextPhaseCbHdl(GpiImplInterface *impl); -}; - -class VhpiStartupCbHdl : public VhpiCbHdl { -public: - VhpiStartupCbHdl(GpiImplInterface *impl); - int run_callback() override; - int cleanup_callback() override { - /* Too many simulators get upset with this so we override to do nothing */ - return 0; - } -}; - -class VhpiShutdownCbHdl : public VhpiCbHdl { -public: - VhpiShutdownCbHdl(GpiImplInterface *impl); - int run_callback() override; - int cleanup_callback() override { - /* Too many simulators get upset with this so we override to do nothing */ - return 0; - } -}; - -class VhpiReadwriteCbHdl : public VhpiCbHdl { -public: - VhpiReadwriteCbHdl(GpiImplInterface *impl); -}; - -class VhpiArrayObjHdl : public GpiObjHdl { -public: - VhpiArrayObjHdl(GpiImplInterface *impl, - vhpiHandleT hdl, - gpi_objtype_t objtype) : GpiObjHdl(impl, hdl, objtype) { } - ~VhpiArrayObjHdl() override; - - int initialise(std::string &name, std::string &fq_name) override; -}; - -class VhpiObjHdl : public GpiObjHdl { -public: - VhpiObjHdl(GpiImplInterface *impl, - vhpiHandleT hdl, - gpi_objtype_t objtype) : GpiObjHdl(impl, hdl, objtype) { } - ~VhpiObjHdl() override; - - int initialise(std::string &name, std::string &fq_name) override; -}; - -class VhpiSignalObjHdl : public GpiSignalObjHdl { -public: - VhpiSignalObjHdl(GpiImplInterface *impl, - vhpiHandleT hdl, - gpi_objtype_t objtype, - bool is_const) : GpiSignalObjHdl(impl, hdl, objtype, is_const), - m_rising_cb(impl, this, GPI_RISING), - m_falling_cb(impl, this, GPI_FALLING), - m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } - ~VhpiSignalObjHdl() override; - - const char* get_signal_value_binstr() override; - const char* get_signal_value_str() override; - double get_signal_value_real() override; - long get_signal_value_long() override; - - using GpiSignalObjHdl::set_signal_value; - int set_signal_value(int32_t value, gpi_set_action_t action) override; - int set_signal_value(double value, gpi_set_action_t action) override; - int set_signal_value_str(std::string &value, gpi_set_action_t action) override; - int set_signal_value_binstr(std::string &value, gpi_set_action_t action) override; - - /* Value change callback accessor */ - GpiCbHdl *value_change_cb(int edge) override; - int initialise(std::string &name, std::string &fq_name) override; - -protected: - vhpiEnumT chr2vhpi(char value); - vhpiValueT m_value; - vhpiValueT m_binvalue; - VhpiValueCbHdl m_rising_cb; - VhpiValueCbHdl m_falling_cb; - VhpiValueCbHdl m_either_cb; -}; - -class VhpiLogicSignalObjHdl : public VhpiSignalObjHdl { -public: - VhpiLogicSignalObjHdl(GpiImplInterface *impl, - vhpiHandleT hdl, - gpi_objtype_t objtype, - bool is_const) : VhpiSignalObjHdl(impl, hdl, objtype, is_const) { } - - - using GpiSignalObjHdl::set_signal_value; - int set_signal_value(int32_t value, gpi_set_action_t action) override; - int set_signal_value_binstr(std::string &value, gpi_set_action_t action) override; - - int initialise(std::string &name, std::string &fq_name) override; -}; - -class VhpiIterator : public GpiIterator { -public: - VhpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl); - - ~VhpiIterator() override; - - Status next_handle(std::string &name, GpiObjHdl **hdl, void **raw_hdl) override; - -private: - vhpiHandleT m_iterator; - vhpiHandleT m_iter_obj; - static std::map> iterate_over; /* Possible mappings */ - std::vector *selected; /* Mapping currently in use */ - std::vector::iterator one2many; -}; - -class VhpiImpl : public GpiImplInterface { -public: - VhpiImpl(const std::string& name) : GpiImplInterface(name), - m_read_write(this), - m_next_phase(this), - m_read_only(this) { } - - /* Sim related */ - void sim_end() override; - void get_sim_time(uint32_t *high, uint32_t *low) override; - void get_sim_precision(int32_t *precision) override; - const char *get_simulator_product() override; - const char *get_simulator_version() override; - - /* Hierachy related */ - GpiObjHdl *get_root_handle(const char *name) override; - GpiIterator *iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) override; - - /* Callback related, these may (will) return the same handle*/ - GpiCbHdl *register_timed_callback(uint64_t time) override; - GpiCbHdl *register_readonly_callback() override; - GpiCbHdl *register_nexttime_callback() override; - GpiCbHdl *register_readwrite_callback() override; - int deregister_callback(GpiCbHdl *obj_hdl) override; - GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent) override; - GpiObjHdl* native_check_create(int32_t index, GpiObjHdl *parent) override; - GpiObjHdl* native_check_create(void *raw_hdl, GpiObjHdl *parent) override; - - const char * reason_to_string(int reason) override; - const char * format_to_string(int format); - - GpiObjHdl *create_gpi_obj_from_handle(vhpiHandleT new_hdl, - std::string &name, - std::string &fq_name); - -private: - VhpiReadwriteCbHdl m_read_write; - VhpiNextPhaseCbHdl m_next_phase; - VhpiReadOnlyCbHdl m_read_only; -}; - -#endif /*COCOTB_VHPI_IMPL_H_ */ diff --git a/cocotb/share/lib/vpi/VpiCbHdl.cpp b/cocotb/share/lib/vpi/VpiCbHdl.cpp deleted file mode 100644 index 5c628395..00000000 --- a/cocotb/share/lib/vpi/VpiCbHdl.cpp +++ /dev/null @@ -1,902 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2013, 2018 Potential Ventures Ltd -* Copyright (c) 2013 SolarFlare Communications Inc -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd, -* SolarFlare Communications Inc nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#include -#include "VpiImpl.h" -#include - -extern "C" int32_t handle_vpi_callback(p_cb_data cb_data); - -VpiCbHdl::VpiCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl) -{ - - vpi_time.high = 0; - vpi_time.low = 0; - vpi_time.type = vpiSimTime; - - cb_data.reason = 0; - cb_data.cb_rtn = handle_vpi_callback; - cb_data.obj = NULL; - cb_data.time = &vpi_time; - cb_data.value = NULL; - cb_data.index = 0; - cb_data.user_data = (char*)this; -} - -/* If the user data already has a callback handle then deregister - * before getting the new one - */ -int VpiCbHdl::arm_callback() { - - if (m_state == GPI_PRIMED) { - fprintf(stderr, - "Attempt to prime an already primed trigger for %s!\n", - m_impl->reason_to_string(cb_data.reason)); - } - - // Only a problem if we have not been asked to deregister and register - // in the same simulation callback - if (m_obj_hdl != NULL && m_state != GPI_DELETE) { - fprintf(stderr, - "We seem to already be registered, deregistering %s!\n", - m_impl->reason_to_string(cb_data.reason)); - cleanup_callback(); - } - - vpiHandle new_hdl = vpi_register_cb(&cb_data); - - if (!new_hdl) { - LOG_ERROR("VPI: Unable to register a callback handle for VPI type %s(%d)", - m_impl->reason_to_string(cb_data.reason), cb_data.reason); - check_vpi_error(); - return -1; - - } else { - m_state = GPI_PRIMED; - } - - m_obj_hdl = new_hdl; - - return 0; -} - -int VpiCbHdl::cleanup_callback() -{ - if (m_state == GPI_FREE) - return 0; - - /* If the one-time callback has not come back then - * remove it, it is has then free it. The remove is done - * internally */ - - if (m_state == GPI_PRIMED) { - if (!m_obj_hdl) { - LOG_ERROR("VPI: passed a NULL pointer"); - return -1; - } - - if (!(vpi_remove_cb(get_handle()))) { - LOG_ERROR("VPI: unable to remove callback"); - return -1; - } - - check_vpi_error(); - } else { -#ifndef MODELSIM - /* This is disabled for now, causes a small leak going to put back in */ - if (!(vpi_free_object(get_handle()))) { - LOG_ERROR("VPI: unable to free handle"); - return -1; - } -#endif - } - - - m_obj_hdl = NULL; - m_state = GPI_FREE; - - return 0; -} - -int VpiArrayObjHdl::initialise(std::string &name, std::string &fq_name) { - vpiHandle hdl = GpiObjHdl::get_handle(); - - m_indexable = true; - - int range_idx = 0; - - /* Need to determine if this is a pseudo-handle to be able to select the correct range */ - std::string hdl_name = vpi_get_str(vpiName, hdl); - - /* Removing the hdl_name from the name will leave the pseudo-indices */ - if (hdl_name.length() < name.length()) { - std::string idx_str = name.substr(hdl_name.length()); - - while (idx_str.length() > 0) { - std::size_t found = idx_str.find_first_of("]"); - - if (found != std::string::npos) { - ++range_idx; - idx_str = idx_str.substr(found+1); - } else { - break; - } - } - } - - /* After determining the range_idx, get the range and set the limits */ - vpiHandle iter = vpi_iterate(vpiRange, hdl); - - s_vpi_value val; - val.format = vpiIntVal; - - if (iter != NULL) { - vpiHandle rangeHdl; - int idx = 0; - - while ((rangeHdl = vpi_scan(iter)) != NULL) { - if (idx == range_idx) { - break; - } - ++idx; - } - - if (rangeHdl == NULL) { - LOG_ERROR("Unable to get range for indexable object"); - return -1; - } else { - vpi_free_object(iter); // Need to free iterator since exited early - - vpi_get_value(vpi_handle(vpiLeftRange,rangeHdl),&val); - check_vpi_error(); - m_range_left = val.value.integer; - - vpi_get_value(vpi_handle(vpiRightRange,rangeHdl),&val); - check_vpi_error(); - m_range_right = val.value.integer; - } - } else if (range_idx == 0) { - vpi_get_value(vpi_handle(vpiLeftRange,hdl),&val); - check_vpi_error(); - m_range_left = val.value.integer; - - vpi_get_value(vpi_handle(vpiRightRange,hdl),&val); - check_vpi_error(); - m_range_right = val.value.integer; - } else { - LOG_ERROR("Unable to get range for indexable object"); - return -1; - } - - /* vpiSize will return a size that is incorrect for multi-dimensional arrays so use the range - * to calculate the m_num_elems. - * - * For example: - * wire [7:0] sig_t4 [0:3][7:4] - * - * The size of "sig_t4" will be reported as 16 through the vpi interface. - */ - if (m_range_left > m_range_right) { - m_num_elems = m_range_left - m_range_right + 1; - } else { - m_num_elems = m_range_right - m_range_left + 1; - } - - return GpiObjHdl::initialise(name, fq_name); -} - -int VpiObjHdl::initialise(std::string &name, std::string &fq_name) { - char * str; - vpiHandle hdl = GpiObjHdl::get_handle(); - str = vpi_get_str(vpiDefName, hdl); - if (str != NULL) - m_definition_name = str; - str = vpi_get_str(vpiDefFile, hdl); - if (str != NULL) - m_definition_file = str; - - return GpiObjHdl::initialise(name, fq_name); -} - -int VpiSignalObjHdl::initialise(std::string &name, std::string &fq_name) { - int32_t type = vpi_get(vpiType, GpiObjHdl::get_handle()); - if ((vpiIntVar == type) || - (vpiIntegerVar == type) || - (vpiIntegerNet == type ) || - (vpiRealNet == type)) { - m_num_elems = 1; - } else { - m_num_elems = vpi_get(vpiSize, GpiObjHdl::get_handle()); - - if (GpiObjHdl::get_type() == GPI_STRING) { - m_indexable = false; // Don't want to iterate over indices - m_range_left = 0; - m_range_right = m_num_elems-1; - } else if (GpiObjHdl::get_type() == GPI_REGISTER || GpiObjHdl::get_type() == GPI_NET) { - vpiHandle hdl = GpiObjHdl::get_handle(); - - m_indexable = vpi_get(vpiVector, hdl); - - if (m_indexable) { - s_vpi_value val; - vpiHandle iter; - - val.format = vpiIntVal; - - iter = vpi_iterate(vpiRange, hdl); - - /* Only ever need the first "range" */ - if (iter != NULL) { - vpiHandle rangeHdl = vpi_scan(iter); - - vpi_free_object(iter); - - if (rangeHdl != NULL) { - vpi_get_value(vpi_handle(vpiLeftRange,rangeHdl),&val); - check_vpi_error(); - m_range_left = val.value.integer; - - vpi_get_value(vpi_handle(vpiRightRange,rangeHdl),&val); - check_vpi_error(); - m_range_right = val.value.integer; - } else { - LOG_ERROR("Unable to get range for indexable object"); - return -1; - } - } - else { - vpi_get_value(vpi_handle(vpiLeftRange,hdl),&val); - check_vpi_error(); - m_range_left = val.value.integer; - - vpi_get_value(vpi_handle(vpiRightRange,hdl),&val); - check_vpi_error(); - m_range_right = val.value.integer; - } - - LOG_DEBUG("VPI: Indexable object initialized with range [%d:%d] and length >%d<", m_range_left, m_range_right, m_num_elems); - } - } - } - LOG_DEBUG("VPI: %s initialized with %d elements", name.c_str(), m_num_elems); - return GpiObjHdl::initialise(name, fq_name); -} - -const char* VpiSignalObjHdl::get_signal_value_binstr() -{ - s_vpi_value value_s = {vpiBinStrVal, {NULL}}; - - vpi_get_value(GpiObjHdl::get_handle(), &value_s); - check_vpi_error(); - - return value_s.value.str; -} - -const char* VpiSignalObjHdl::get_signal_value_str() -{ - s_vpi_value value_s = {vpiStringVal, {NULL}}; - - vpi_get_value(GpiObjHdl::get_handle(), &value_s); - check_vpi_error(); - - return value_s.value.str; -} - -double VpiSignalObjHdl::get_signal_value_real() -{ - s_vpi_value value_s = {vpiRealVal, {NULL}}; - - vpi_get_value(GpiObjHdl::get_handle(), &value_s); - check_vpi_error(); - - return value_s.value.real; -} - -long VpiSignalObjHdl::get_signal_value_long() -{ - s_vpi_value value_s = {vpiIntVal, {NULL}}; - - vpi_get_value(GpiObjHdl::get_handle(), &value_s); - check_vpi_error(); - - return value_s.value.integer; -} - -// Value related functions -int VpiSignalObjHdl::set_signal_value(int32_t value, gpi_set_action_t action) -{ - s_vpi_value value_s; - - value_s.value.integer = static_cast(value); - value_s.format = vpiIntVal; - - return set_signal_value(value_s, action); -} - -int VpiSignalObjHdl::set_signal_value(double value, gpi_set_action_t action) -{ - s_vpi_value value_s; - - value_s.value.real = value; - value_s.format = vpiRealVal; - - return set_signal_value(value_s, action); -} - -int VpiSignalObjHdl::set_signal_value_binstr(std::string &value, gpi_set_action_t action) -{ - s_vpi_value value_s; - - std::vector writable(value.begin(), value.end()); - writable.push_back('\0'); - - value_s.value.str = &writable[0]; - value_s.format = vpiBinStrVal; - - return set_signal_value(value_s, action); -} - -int VpiSignalObjHdl::set_signal_value_str(std::string &value, gpi_set_action_t action) -{ - s_vpi_value value_s; - - std::vector writable(value.begin(), value.end()); - writable.push_back('\0'); - - value_s.value.str = &writable[0]; - value_s.format = vpiStringVal; - - return set_signal_value(value_s, action); -} - -int VpiSignalObjHdl::set_signal_value(s_vpi_value value_s, gpi_set_action_t action) -{ - PLI_INT32 vpi_put_flag = -1; - s_vpi_time vpi_time_s; - - vpi_time_s.type = vpiSimTime; - vpi_time_s.high = 0; - vpi_time_s.low = 0; - - switch (action) { - case GPI_DEPOSIT: - if (vpiStringVar == vpi_get(vpiType, GpiObjHdl::get_handle())) { - // assigning to a vpiStringVar only seems to work with vpiNoDelay - vpi_put_flag = vpiNoDelay; - } else { - // Use Inertial delay to schedule an event, thus behaving like a verilog testbench - vpi_put_flag = vpiInertialDelay; - } - break; - case GPI_FORCE: - vpi_put_flag = vpiForceFlag; - break; - case GPI_RELEASE: - // Best to pass its current value to the sim when releasing - vpi_get_value(GpiObjHdl::get_handle(), &value_s); - vpi_put_flag = vpiReleaseFlag; - break; - default: - assert(0); - } - - if (vpi_put_flag == vpiNoDelay) { - vpi_put_value(GpiObjHdl::get_handle(), &value_s, NULL, vpiNoDelay); - } else { - vpi_put_value(GpiObjHdl::get_handle(), &value_s, &vpi_time_s, vpi_put_flag); - } - - check_vpi_error(); - - return 0; -} - -GpiCbHdl * VpiSignalObjHdl::value_change_cb(int edge) -{ - VpiValueCbHdl *cb = NULL; - - switch (edge) { - case 1: - cb = &m_rising_cb; - break; - case 2: - cb = &m_falling_cb; - break; - case 3: - cb = &m_either_cb; - break; - default: - return NULL; - } - - if (cb->arm_callback()) { - return NULL; - } - - return cb; -} - -VpiValueCbHdl::VpiValueCbHdl(GpiImplInterface *impl, - VpiSignalObjHdl *sig, - int edge) :GpiCbHdl(impl), - VpiCbHdl(impl), - GpiValueCbHdl(impl,sig,edge) -{ - vpi_time.type = vpiSuppressTime; - m_vpi_value.format = vpiIntVal; - - cb_data.reason = cbValueChange; - cb_data.time = &vpi_time; - cb_data.value = &m_vpi_value; - cb_data.obj = m_signal->get_handle(); -} - -int VpiValueCbHdl::cleanup_callback() -{ - if (m_state == GPI_FREE) - return 0; - - /* This is a recurring callback so just remove when - * not wanted */ - if (!(vpi_remove_cb(get_handle()))) { - LOG_ERROR("VPI: unable to remove callback"); - return -1; - } - - m_obj_hdl = NULL; - m_state = GPI_FREE; - return 0; -} - -VpiStartupCbHdl::VpiStartupCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - VpiCbHdl(impl) -{ -#ifndef IUS - cb_data.reason = cbStartOfSimulation; -#else - vpi_time.high = (uint32_t)(0); - vpi_time.low = (uint32_t)(0); - vpi_time.type = vpiSimTime; - cb_data.reason = cbAfterDelay; -#endif -} - -int VpiStartupCbHdl::run_callback() { - s_vpi_vlog_info info; - - if (!vpi_get_vlog_info(&info)) { - LOG_WARN("Unable to get argv and argc from simulator"); - info.argc = 0; - info.argv = nullptr; - } - - gpi_embed_init(info.argc, info.argv); - - return 0; -} - -VpiShutdownCbHdl::VpiShutdownCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - VpiCbHdl(impl) -{ - cb_data.reason = cbEndOfSimulation; -} - -int VpiShutdownCbHdl::run_callback() { - gpi_embed_end(); - return 0; -} - -VpiTimedCbHdl::VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time) : GpiCbHdl(impl), - VpiCbHdl(impl) -{ - vpi_time.high = (uint32_t)(time>>32); - vpi_time.low = (uint32_t)(time); - vpi_time.type = vpiSimTime; - - cb_data.reason = cbAfterDelay; -} - -int VpiTimedCbHdl::cleanup_callback() -{ - switch (m_state) { - case GPI_PRIMED: - /* Issue #188: Work around for modelsim that is harmless to others too, - we tag the time as delete, let it fire then do not pass up - */ - LOG_DEBUG("Not removing PRIMED timer %d", vpi_time.low); - m_state = GPI_DELETE; - return 0; - case GPI_DELETE: - LOG_DEBUG("Removing DELETE timer %d", vpi_time.low); - default: - break; - } - VpiCbHdl::cleanup_callback(); - /* Return one so we delete this object */ - return 1; -} - -VpiReadwriteCbHdl::VpiReadwriteCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - VpiCbHdl(impl) -{ - cb_data.reason = cbReadWriteSynch; -} - -VpiReadOnlyCbHdl::VpiReadOnlyCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - VpiCbHdl(impl) -{ - cb_data.reason = cbReadOnlySynch; -} - -VpiNextPhaseCbHdl::VpiNextPhaseCbHdl(GpiImplInterface *impl) : GpiCbHdl(impl), - VpiCbHdl(impl) -{ - cb_data.reason = cbNextSimTime; -} - -decltype(VpiIterator::iterate_over) VpiIterator::iterate_over = []{ - /* for reused lists */ - std::initializer_list module_options = { - //vpiModule, // Aldec SEGV on mixed language - //vpiModuleArray, // Aldec SEGV on mixed language - //vpiIODecl, // Don't care about these - vpiNet, - vpiNetArray, - vpiReg, - vpiRegArray, - vpiMemory, - vpiIntegerVar, - vpiRealVar, - vpiRealNet, - vpiStructVar, - vpiStructNet, - vpiVariables, - vpiNamedEvent, - vpiNamedEventArray, - vpiParameter, - //vpiSpecParam, // Don't care - //vpiParamAssign, // Aldec SEGV on mixed language - //vpiDefParam, // Don't care - vpiPrimitive, - vpiPrimitiveArray, - //vpiContAssign, // Don't care - vpiProcess, // Don't care - vpiModPath, - vpiTchk, - vpiAttribute, - vpiPort, - vpiInternalScope, - //vpiInterface, // Aldec SEGV on mixed language - //vpiInterfaceArray, // Aldec SEGV on mixed language - }; - std::initializer_list struct_options = { - vpiNet, -#ifndef IUS - vpiNetArray, -#endif - vpiReg, - vpiRegArray, - vpiMemory, - vpiParameter, - vpiPrimitive, - vpiPrimitiveArray, - vpiAttribute, - vpiMember, - }; - - return decltype(VpiIterator::iterate_over) { - {vpiModule, module_options}, - {vpiGenScope, module_options}, - - {vpiStructVar, struct_options}, - {vpiStructNet, struct_options}, - - {vpiNet, { - //vpiContAssign, // Driver and load handled separately - //vpiPrimTerm, - //vpiPathTerm, - //vpiTchkTerm, - //vpiDriver, - //vpiLocalDriver, - //vpiLoad, - //vpiLocalLoad, - vpiNetBit, - }}, - {vpiNetArray, { - vpiNet, - }}, - {vpiRegArray, { - vpiReg, - }}, - {vpiMemory, { - vpiMemoryWord, - }}, - {vpiPort, { - vpiPortBit, - }}, - {vpiGate, { - vpiPrimTerm, - vpiTableEntry, - vpiUdpDefn, - }}, - }; -}(); - - -VpiIterator::VpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl) : GpiIterator(impl, hdl), - m_iterator(NULL) -{ - vpiHandle iterator; - vpiHandle vpi_hdl = m_parent->get_handle(); - - int type = vpi_get(vpiType, vpi_hdl); - try { - selected = &iterate_over.at(type); - } - catch (std::out_of_range const&) { - LOG_WARN("VPI: Implementation does not know how to iterate over %s(%d)", - vpi_get_str(vpiType, vpi_hdl), type); - selected = nullptr; - return; - } - - - for (one2many = selected->begin(); - one2many != selected->end(); - one2many++) { - - /* GPI_GENARRAY are pseudo-regions and all that should be searched for are the sub-regions */ - if (m_parent->get_type() == GPI_GENARRAY && *one2many != vpiInternalScope) { - LOG_DEBUG("vpi_iterator vpiOneToManyT=%d skipped for GPI_GENARRAY type", *one2many); - continue; - } - - iterator = vpi_iterate(*one2many, vpi_hdl); - - if (iterator) { - break; - } - - LOG_DEBUG("vpi_iterate type=%d returned NULL", *one2many); - } - - if (NULL == iterator) { - LOG_DEBUG("vpi_iterate return NULL for all relationships on %s (%d) type:%s", - vpi_get_str(vpiName, vpi_hdl), - type, - vpi_get_str(vpiType, vpi_hdl)); - selected = NULL; - return; - } - - LOG_DEBUG("Created iterator working from '%s' with type %s(%d)", - vpi_get_str(vpiFullName, vpi_hdl), - vpi_get_str(vpiType, vpi_hdl), - type); - - m_iterator = iterator; -} - -VpiIterator::~VpiIterator() -{ - if (m_iterator) - vpi_free_object(m_iterator); -} - -#define VPI_TYPE_MAX (1000) - -GpiIterator::Status VpiSingleIterator::next_handle(std::string &name, - GpiObjHdl **hdl, - void **raw_hdl) -{ - GpiObjHdl *new_obj; - vpiHandle obj; - - if (NULL == m_iterator) - return GpiIterator::END; - - obj = vpi_scan(m_iterator); - if (NULL == obj) - return GpiIterator::END; - - const char *c_name = vpi_get_str(vpiName, obj); - if (!c_name) { - int type = vpi_get(vpiType, obj); - - if (type >= VPI_TYPE_MAX) { - *raw_hdl = (void*)obj; - return GpiIterator::NOT_NATIVE_NO_NAME; - } - - LOG_DEBUG("Unable to get the name for this object of type %d", type); - - return GpiIterator::NATIVE_NO_NAME; - } - - std::string fq_name = c_name; - - LOG_DEBUG("vpi_scan found '%s = '%s'", name.c_str(), fq_name.c_str()); - - VpiImpl *vpi_impl = reinterpret_cast(m_impl); - new_obj = vpi_impl->create_gpi_obj_from_handle(obj, name, fq_name); - if (new_obj) { - *hdl = new_obj; - return GpiIterator::NATIVE; - } - else - return GpiIterator::NOT_NATIVE; -} - -GpiIterator::Status VpiIterator::next_handle(std::string &name, GpiObjHdl **hdl, void **raw_hdl) -{ - GpiObjHdl *new_obj; - vpiHandle obj; - vpiHandle iter_obj = m_parent->get_handle(); - - if (!selected) - return GpiIterator::END; - - gpi_objtype_t obj_type = m_parent->get_type(); - std::string parent_name = m_parent->get_name(); - - do { - obj = NULL; - - if (m_iterator) { - obj = vpi_scan(m_iterator); - - /* For GPI_GENARRAY, only allow the generate statements through that match the name - * of the generate block. - */ - if (obj != NULL && obj_type == GPI_GENARRAY) { - if (vpi_get(vpiType, obj) == vpiGenScope) { - std::string rgn_name = vpi_get_str(vpiName, obj); - if (rgn_name.compare(0,parent_name.length(),parent_name) != 0) { - obj = NULL; - continue; - } - } else { - obj = NULL; - continue; - } - } - - if (NULL == obj) { - /* m_iterator will already be free'd internally here */ - m_iterator = NULL; - } else { - break; - } - - LOG_DEBUG("End of type=%d iteration", *one2many); - } else { - LOG_DEBUG("No valid type=%d iterator", *one2many); - } - - if (++one2many >= selected->end()) { - obj = NULL; - break; - } - - /* GPI_GENARRAY are pseudo-regions and all that should be searched for are the sub-regions */ - if (obj_type == GPI_GENARRAY && *one2many != vpiInternalScope) { - LOG_DEBUG("vpi_iterator vpiOneToManyT=%d skipped for GPI_GENARRAY type", *one2many); - continue; - } - - m_iterator = vpi_iterate(*one2many, iter_obj); - - } while (!obj); - - if (NULL == obj) { - LOG_DEBUG("No more children, all relationships tested"); - return GpiIterator::END; - } - - /* Simulators vary here. Some will allow the name to be accessed - across boundary. We can simply return this up and allow - the object to be created. Others do not. In this case - we see if the object is in our type range and if not - return the raw_hdl up */ - - const char *c_name = vpi_get_str(vpiName, obj); - if (!c_name) { - /* This may be another type */ - int type = vpi_get(vpiType, obj); - - if (type >= VPI_TYPE_MAX) { - *raw_hdl = (void*)obj; - return GpiIterator::NOT_NATIVE_NO_NAME; - } - - LOG_DEBUG("Unable to get the name for this object of type %d", type); - - return GpiIterator::NATIVE_NO_NAME; - } - - /* - * If the parent is not a generate loop, then watch for generate handles and create - * the pseudo-region. - * - * NOTE: Taking advantage of the "caching" to only create one pseudo-region object. - * Otherwise a list would be required and checked while iterating - */ - if (*one2many == vpiInternalScope && obj_type != GPI_GENARRAY && vpi_get(vpiType, obj) == vpiGenScope) { - std::string idx_str = c_name; - std::size_t found = idx_str.rfind("["); - - if (found != std::string::npos && found != 0) { - name = idx_str.substr(0,found); - obj = m_parent->get_handle(); - } else { - name = c_name; - } - } else { - name = c_name; - } - - /* We try and create a handle internally, if this is not possible we - return and GPI will try other implementations with the name - */ - - std::string fq_name = m_parent->get_fullname(); - - if (obj_type == GPI_GENARRAY) { - std::size_t found = name.rfind("["); - - if (found != std::string::npos) { - fq_name += name.substr(found); - } else { - LOG_WARN("Unhandled Sub-Element Format - %s", name.c_str()); - fq_name += "." + name; - } - } else if (obj_type == GPI_STRUCTURE) { - std::size_t found = name.rfind("."); - - if (found != std::string::npos) { - fq_name += name.substr(found); - name = name.substr(found+1); - } else { - LOG_WARN("Unhandled Sub-Element Format - %s", name.c_str()); - fq_name += "." + name; - } - } else { - fq_name += "." + name; - } - - LOG_DEBUG("vpi_scan found '%s'", fq_name.c_str()); - VpiImpl *vpi_impl = reinterpret_cast(m_impl); - new_obj = vpi_impl->create_gpi_obj_from_handle(obj, name, fq_name); - if (new_obj) { - *hdl = new_obj; - return GpiIterator::NATIVE; - } - else - return GpiIterator::NOT_NATIVE; -} diff --git a/cocotb/share/lib/vpi/VpiImpl.cpp b/cocotb/share/lib/vpi/VpiImpl.cpp deleted file mode 100644 index 6ad9df2b..00000000 --- a/cocotb/share/lib/vpi/VpiImpl.cpp +++ /dev/null @@ -1,706 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2013, 2018 Potential Ventures Ltd -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#include "VpiImpl.h" -#include -#include // COCOTB_UNUSED - -extern "C" { - -static VpiCbHdl *sim_init_cb; -static VpiCbHdl *sim_finish_cb; -static VpiImpl *vpi_table; - -} - -#define CASE_STR(_X) \ - case _X: return #_X - -const char *VpiImpl::reason_to_string(int reason) -{ - switch (reason) { - CASE_STR(cbValueChange); - CASE_STR(cbAtStartOfSimTime); - CASE_STR(cbReadWriteSynch); - CASE_STR(cbReadOnlySynch); - CASE_STR(cbNextSimTime); - CASE_STR(cbAfterDelay); - CASE_STR(cbStartOfSimulation); - CASE_STR(cbEndOfSimulation); - - default: return "unknown"; - } -} - -#undef CASE_STR - -void VpiImpl::get_sim_time(uint32_t *high, uint32_t *low) -{ - s_vpi_time vpi_time_s; - vpi_time_s.type = vpiSimTime; //vpiSimTime; - vpi_get_time(NULL, &vpi_time_s); - check_vpi_error(); - *high = vpi_time_s.high; - *low = vpi_time_s.low; -} - -void VpiImpl::get_sim_precision(int32_t *precision) -{ - *precision = vpi_get(vpiTimePrecision, NULL); -} - -const char *VpiImpl::get_simulator_product() -{ - if (m_product.empty() && m_version.empty()) { - s_vpi_vlog_info info; - if (!vpi_get_vlog_info(&info)) { - LOG_WARN("Could not obtain info about the simulator"); - m_product = "UNKNOWN"; - m_version = "UNKNOWN"; - } else { - m_product = info.product; - m_version = info.version; - } - } - return m_product.c_str(); -} - -const char *VpiImpl::get_simulator_version() -{ - get_simulator_product(); - return m_version.c_str(); -} - -static gpi_objtype_t to_gpi_objtype(int32_t vpitype) -{ - switch (vpitype) { - case vpiNet: - case vpiNetBit: - return GPI_NET; - - case vpiBitVar: - case vpiReg: - case vpiRegBit: - case vpiMemoryWord: - return GPI_REGISTER; - - case vpiRealNet: - case vpiRealVar: - return GPI_REAL; - - case vpiInterfaceArray: - case vpiPackedArrayVar: - case vpiRegArray: - case vpiNetArray: - case vpiGenScopeArray: - case vpiMemory: - return GPI_ARRAY; - - case vpiEnumNet: - case vpiEnumVar: - return GPI_ENUM; - - case vpiIntVar: - case vpiIntegerVar: - case vpiIntegerNet: - return GPI_INTEGER; - - case vpiStructVar: - case vpiStructNet: - case vpiUnionVar: - return GPI_STRUCTURE; - - case vpiModport: - case vpiInterface: - case vpiModule: - case vpiRefObj: - case vpiPort: - case vpiAlways: - case vpiFunction: - case vpiInitial: - case vpiGate: - case vpiPrimTerm: - case vpiGenScope: - return GPI_MODULE; - - case vpiStringVar: - return GPI_STRING; - - default: - LOG_DEBUG("Unable to map VPI type %d onto GPI type", vpitype); - return GPI_UNKNOWN; - } -} - -static gpi_objtype_t const_type_to_gpi_objtype(int32_t const_type) -{ - switch (const_type) - { - case vpiDecConst: - case vpiBinaryConst: - case vpiOctConst: - case vpiHexConst: - case vpiIntConst: - return GPI_INTEGER; - case vpiRealConst: - return GPI_REAL; - case vpiStringConst: - return GPI_STRING; - //case vpiTimeConst: // Not implemented - default: - LOG_DEBUG("Unable to map vpiConst type %d onto GPI type", const_type); - return GPI_UNKNOWN; - } -} - -GpiObjHdl* VpiImpl::create_gpi_obj_from_handle(vpiHandle new_hdl, - std::string &name, - std::string &fq_name) -{ - int32_t type; - GpiObjHdl *new_obj = NULL; - if (vpiUnknown == (type = vpi_get(vpiType, new_hdl))) { - LOG_DEBUG("vpiUnknown returned from vpi_get(vpiType, ...)") - return NULL; - } - - /* What sort of instance is this ?*/ - switch (type) { - case vpiNet: - case vpiNetBit: - case vpiBitVar: - case vpiReg: - case vpiRegBit: - case vpiEnumNet: - case vpiEnumVar: - case vpiIntVar: - case vpiIntegerVar: - case vpiIntegerNet: - case vpiRealVar: - case vpiRealNet: - case vpiStringVar: - case vpiMemoryWord: - case vpiInterconnectNet: - new_obj = new VpiSignalObjHdl(this, new_hdl, to_gpi_objtype(type), false); - break; - case vpiParameter: - case vpiConstant: - { - auto const_type = vpi_get(vpiConstType, new_hdl); - new_obj = new VpiSignalObjHdl(this, new_hdl, const_type_to_gpi_objtype(const_type), true); - break; - } - case vpiRegArray: - case vpiNetArray: - case vpiInterfaceArray: - case vpiPackedArrayVar: - case vpiMemory: - case vpiInterconnectArray: - new_obj = new VpiArrayObjHdl(this, new_hdl, to_gpi_objtype(type)); - break; - case vpiStructVar: - case vpiStructNet: - case vpiUnionVar: - new_obj = new VpiObjHdl(this, new_hdl, to_gpi_objtype(type)); - break; - case vpiModule: - case vpiInterface: - case vpiModport: - case vpiRefObj: - case vpiPort: - case vpiAlways: - case vpiFunction: - case vpiInitial: - case vpiGate: - case vpiPrimTerm: - case vpiGenScope: - case vpiGenScopeArray: { - std::string hdl_name = vpi_get_str(vpiName, new_hdl); - - if (hdl_name != name) { - LOG_DEBUG("Found pseudo-region %s (hdl_name=%s but name=%s)", fq_name.c_str(), hdl_name.c_str(), name.c_str()); - new_obj = new VpiObjHdl(this, new_hdl, GPI_GENARRAY); - } else { - new_obj = new VpiObjHdl(this, new_hdl, to_gpi_objtype(type)); - } - break; - } - default: - /* We should only print a warning here if the type is really Verilog, - It could be VHDL as some simulators allow querying of both languages - via the same handle - */ - const char *type_name = vpi_get_str(vpiType, new_hdl); - std::string unknown = "vpiUnknown"; - if (type_name && (unknown != type_name)) { - LOG_WARN("VPI: Not able to map type %s(%d) to object.", type_name, type); - } else { - LOG_WARN("VPI: Simulator does not know this type (%d) via VPI", type); - } - return NULL; - } - - new_obj->initialise(name, fq_name); - - LOG_DEBUG("VPI: Created GPI object from type %s(%d)", - vpi_get_str(vpiType, new_hdl), type); - - return new_obj; -} - -GpiObjHdl* VpiImpl::native_check_create(void *raw_hdl, GpiObjHdl *parent) -{ - LOG_DEBUG("Trying to convert raw to VPI handle"); - - vpiHandle new_hdl = (vpiHandle)raw_hdl; - - const char *c_name = vpi_get_str(vpiName, new_hdl); - if (!c_name) { - LOG_DEBUG("Unable to query name of passed in handle"); - return NULL; - } - - std::string name = c_name; - std::string fq_name = parent->get_fullname() + "." + name; - - GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); - if (new_obj == NULL) { - vpi_free_object(new_hdl); - LOG_DEBUG("Unable to fetch object %s", fq_name.c_str()); - return NULL; - } - return new_obj; -} - -GpiObjHdl* VpiImpl::native_check_create(std::string &name, GpiObjHdl *parent) -{ - vpiHandle new_hdl; - const vpiHandle parent_hdl = parent->get_handle(); - std::string fq_name = parent->get_fullname() + "." + name; - - new_hdl = vpi_handle_by_name(const_cast(fq_name.c_str()), NULL); - -#ifdef ICARUS - /* Icarus does not support vpiGenScopeArray, only vpiGenScope. - * If handle is not found by name, look for a generate block with - * a matching prefix. - * For Example: - * genvar idx; - * generate - * for (idx = 0; idx < 5; idx = idx + 1) begin - * ... - * end - * endgenerate - * - * genblk1 => vpiGenScopeArray (not found) - * genblk1[0] => vpiGenScope - * ... - * genblk1[4] => vpiGenScope - * - * genblk1 is not found directly, but if genblk1[n] is found, - * genblk1 must exist, so create the pseudo-region object for it. - */ - if (new_hdl == NULL) { - vpiHandle iter = vpi_iterate(vpiInternalScope, parent_hdl); - if (iter == NULL) { - goto skip_iterate; - } - - for (auto rgn = vpi_scan(iter); rgn != NULL; rgn = vpi_scan(iter)) { - if (vpi_get(vpiType, rgn) == vpiGenScope) { - auto rgn_name = vpi_get_str(vpiName, rgn); - /* Check if name is a prefix of rgn_name */ - if (rgn_name && name.length() > 0 && std::strncmp(name.c_str(), rgn_name, name.length()) == 0) { - new_hdl = parent_hdl; - vpi_free_object(iter); - break; - } - } - } - } -skip_iterate: -#endif - - if (new_hdl == NULL) { - LOG_DEBUG("Unable to query vpi_get_handle_by_name %s", fq_name.c_str()); - return NULL; - } - - /* Generate Loops have inconsistent behavior across vpi tools. A "name" - * without an index, i.e. dut.loop vs dut.loop[0], will find a handle to vpiGenScopeArray, - * but not all tools support iterating over the vpiGenScopeArray. We don't want to create - * a GpiObjHdl to this type of vpiHandle. - * - * If this unique case is hit, we need to create the Pseudo-region, with the handle - * being equivalent to the parent handle. - */ - if (vpi_get(vpiType, new_hdl) == vpiGenScopeArray) { - vpi_free_object(new_hdl); - - new_hdl = parent_hdl; - } - - - GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); - if (new_obj == NULL) { - vpi_free_object(new_hdl); - LOG_DEBUG("Unable to fetch object %s", fq_name.c_str()); - return NULL; - } - return new_obj; -} - -GpiObjHdl* VpiImpl::native_check_create(int32_t index, GpiObjHdl *parent) -{ - vpiHandle vpi_hdl = parent->get_handle(); - vpiHandle new_hdl = NULL; - - char buff[14]; // needs to be large enough to hold -2^31 to 2^31-1 in string form ('['+'-'10+']'+'\0') - - gpi_objtype_t obj_type = parent->get_type(); - - if (obj_type == GPI_GENARRAY) { - snprintf(buff, 14, "[%d]", index); - - LOG_DEBUG("Native check create for index %d of parent %s (pseudo-region)", - index, - parent->get_name_str()); - - std::string idx = buff; - std::string hdl_name = parent->get_fullname() + idx; - std::vector writable(hdl_name.begin(), hdl_name.end()); - writable.push_back('\0'); - - new_hdl = vpi_handle_by_name(&writable[0], NULL); - } else if (obj_type == GPI_REGISTER || obj_type == GPI_NET || obj_type == GPI_ARRAY || obj_type == GPI_STRING) { - new_hdl = vpi_handle_by_index(vpi_hdl, index); - - /* vpi_handle_by_index() doesn't work for all simulators when dealing with a two-dimensional array. - * For example: - * wire [7:0] sig_t4 [0:1][0:2]; - * - * Assume vpi_hdl is for "sig_t4": - * vpi_handle_by_index(vpi_hdl, 0); // Returns a handle to sig_t4[0] for IUS, but NULL on Questa - * - * Questa only works when both indices are provided, i.e. will need a pseudo-handle to behave like the first index. - */ - if (new_hdl == NULL) { - int left = parent->get_range_left(); - int right = parent->get_range_right(); - bool ascending = (left < right); - - LOG_DEBUG("Unable to find handle through vpi_handle_by_index(), attempting second method"); - - if (( ascending && (index < left || index > right)) || - (!ascending && (index > left || index < right))) { - LOG_ERROR("Invalid Index - Index %d is not in the range of [%d:%d]", index, left, right); - return NULL; - } - - /* Get the number of constraints to determine if the index will result in a pseudo-handle or should be found */ - vpiHandle p_hdl = parent->get_handle(); - vpiHandle it = vpi_iterate(vpiRange, p_hdl); - int constraint_cnt = 0; - if (it != NULL) { - while (vpi_scan(it) != NULL) { - ++constraint_cnt; - } - } else { - constraint_cnt = 1; - } - - std::string act_hdl_name = vpi_get_str(vpiName, p_hdl); - - /* Removing the act_hdl_name from the parent->get_name() will leave the pseudo-indices */ - if (act_hdl_name.length() < parent->get_name().length()) { - std::string idx_str = parent->get_name().substr(act_hdl_name.length()); - - while (idx_str.length() > 0) { - std::size_t found = idx_str.find_first_of("]"); - - if (found != std::string::npos) { - --constraint_cnt; - idx_str = idx_str.substr(found+1); - } else { - break; - } - } - } - - snprintf(buff, 14, "[%d]", index); - - std::string idx = buff; - std::string hdl_name = parent->get_fullname() + idx; - - std::vector writable(hdl_name.begin(), hdl_name.end()); - writable.push_back('\0'); - - new_hdl = vpi_handle_by_name(&writable[0], NULL); - - /* Create a pseudo-handle if not the last index into a multi-dimensional array */ - if (new_hdl == NULL && constraint_cnt > 1) { - new_hdl = p_hdl; - } - } - } else { - LOG_ERROR("VPI: Parent of type %s must be of type GPI_GENARRAY, GPI_REGISTER, GPI_NET, GPI_ARRAY, or GPI_STRING to have an index.", parent->get_type_str()); - return NULL; - } - - - if (new_hdl == NULL) { - LOG_DEBUG("Unable to vpi_get_handle_by_index %s[%d]", parent->get_name_str(), index); - return NULL; - } - - snprintf(buff, 14, "[%d]", index); - - std::string idx = buff; - std::string name = parent->get_name()+idx; - std::string fq_name = parent->get_fullname()+idx; - GpiObjHdl* new_obj = create_gpi_obj_from_handle(new_hdl, name, fq_name); - if (new_obj == NULL) { - vpi_free_object(new_hdl); - LOG_DEBUG("Unable to fetch object below entity (%s) at index (%d)", - parent->get_name_str(), index); - return NULL; - } - return new_obj; -} - -GpiObjHdl *VpiImpl::get_root_handle(const char* name) -{ - vpiHandle root; - vpiHandle iterator; - GpiObjHdl *rv; - std::string root_name; - - // vpi_iterate with a ref of NULL returns the top level module - iterator = vpi_iterate(vpiModule, NULL); - check_vpi_error(); - if (!iterator) { - LOG_INFO("Nothing visible via VPI"); - return NULL; - } - - for (root = vpi_scan(iterator); root != NULL; root = vpi_scan(iterator)) { - if (to_gpi_objtype(vpi_get(vpiType, root)) != GPI_MODULE) - continue; - - if (name == NULL || !strcmp(name, vpi_get_str(vpiFullName, root))) - break; - } - - if (!root) { - check_vpi_error(); - goto error; - } - - // Need to free the iterator if it didn't return NULL - if (iterator && !vpi_free_object(iterator)) { - LOG_WARN("VPI: Attempting to free root iterator failed!"); - check_vpi_error(); - } - - root_name = vpi_get_str(vpiFullName, root); - rv = new GpiObjHdl(this, root, to_gpi_objtype(vpi_get(vpiType, root))); - rv->initialise(root_name, root_name); - - return rv; - - error: - - LOG_ERROR("VPI: Couldn't find root handle %s", name); - - iterator = vpi_iterate(vpiModule, NULL); - - for (root = vpi_scan(iterator); root != NULL; root = vpi_scan(iterator)) { - - LOG_ERROR("VPI: Toplevel instances: %s != %s...", name, vpi_get_str(vpiFullName, root)); - - if (name == NULL || !strcmp(name, vpi_get_str(vpiFullName, root))) - break; - } - - return NULL; -} - -GpiIterator *VpiImpl::iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) -{ - GpiIterator *new_iter = NULL; - switch (type) { - case GPI_OBJECTS: - new_iter = new VpiIterator(this, obj_hdl); - break; - case GPI_DRIVERS: - new_iter = new VpiSingleIterator(this, obj_hdl, vpiDriver); - break; - case GPI_LOADS: - new_iter = new VpiSingleIterator(this, obj_hdl, vpiLoad); - break; - default: - LOG_WARN("Other iterator types not implemented yet"); - break; - } - return new_iter; -} - -GpiCbHdl *VpiImpl::register_timed_callback(uint64_t time) -{ - VpiTimedCbHdl *hdl = new VpiTimedCbHdl(this, time); - - if (hdl->arm_callback()) { - delete(hdl); - hdl = NULL; - } - - return hdl; -} - -GpiCbHdl *VpiImpl::register_readwrite_callback() -{ - if (m_read_write.arm_callback()) - return NULL; - - return &m_read_write; -} - -GpiCbHdl *VpiImpl::register_readonly_callback() -{ - if (m_read_only.arm_callback()) - return NULL; - - return &m_read_only; -} - -GpiCbHdl *VpiImpl::register_nexttime_callback() -{ - if (m_next_phase.arm_callback()) - return NULL; - - return &m_next_phase; -} - -int VpiImpl::deregister_callback(GpiCbHdl *gpi_hdl) -{ - return gpi_hdl->cleanup_callback(); -} - -// If the Python world wants things to shut down then unregister -// the callback for end of sim -void VpiImpl::sim_end() -{ - /* Some sims do not seem to be able to deregister the end of sim callback - * so we need to make sure we have tracked this and not call the handler - */ - if (GPI_DELETE != sim_finish_cb->get_call_state()) { - sim_finish_cb->set_call_state(GPI_DELETE); - vpi_control(vpiFinish, vpiDiagTimeLoc); - check_vpi_error(); - } -} - -extern "C" { - -// Main re-entry point for callbacks from simulator -int32_t handle_vpi_callback(p_cb_data cb_data) -{ - int rv = 0; - - VpiCbHdl *cb_hdl = (VpiCbHdl*)cb_data->user_data; - - if (!cb_hdl) { - LOG_CRITICAL("VPI: Callback data corrupted: ABORTING"); - gpi_embed_end(); - return -1; - } - - gpi_cb_state_e old_state = cb_hdl->get_call_state(); - - if (old_state == GPI_PRIMED) { - - cb_hdl->set_call_state(GPI_CALL); - cb_hdl->run_callback(); - - gpi_cb_state_e new_state = cb_hdl->get_call_state(); - - /* We have re-primed in the handler */ - if (new_state != GPI_PRIMED) - if (cb_hdl->cleanup_callback()) { - delete cb_hdl; - } - - } else { - /* Issue #188: This is a work around for a modelsim */ - if (cb_hdl->cleanup_callback()) { - delete cb_hdl; - } - } - - return rv; -} - -static void register_embed() -{ - vpi_table = new VpiImpl("VPI"); - gpi_register_impl(vpi_table); -} - - -static void register_initial_callback() -{ - sim_init_cb = new VpiStartupCbHdl(vpi_table); - sim_init_cb->arm_callback(); -} - -static void register_final_callback() -{ - sim_finish_cb = new VpiShutdownCbHdl(vpi_table); - sim_finish_cb->arm_callback(); -} - -COCOTBVPI_EXPORT void (*vlog_startup_routines[])() = { - register_embed, - gpi_load_extra_libs, - register_initial_callback, - register_final_callback, - nullptr -}; - - -// For non-VPI compliant applications that cannot find vlog_startup_routines symbol -COCOTBVPI_EXPORT void vlog_startup_routines_bootstrap() { - // call each routine in turn like VPI would - for (auto it = &vlog_startup_routines[0]; *it != nullptr; it++) { - auto routine = *it; - routine(); - } -} - -} - -GPI_ENTRY_POINT(cocotbvpi, register_embed) diff --git a/cocotb/share/lib/vpi/VpiImpl.h b/cocotb/share/lib/vpi/VpiImpl.h deleted file mode 100644 index 3f0d02aa..00000000 --- a/cocotb/share/lib/vpi/VpiImpl.h +++ /dev/null @@ -1,279 +0,0 @@ -/****************************************************************************** -* Copyright (c) 2013, 2018 Potential Ventures Ltd -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Potential Ventures Ltd -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -******************************************************************************/ - -#ifndef COCOTB_VPI_IMPL_H_ -#define COCOTB_VPI_IMPL_H_ - -#include -#ifdef COCOTBVPI_EXPORTS -#define COCOTBVPI_EXPORT COCOTB_EXPORT -#else -#define COCOTBVPI_EXPORT COCOTB_IMPORT -#endif - -#include "../gpi/gpi_priv.h" -#include -#include -#include -#include - -// Should be run after every VPI call to check error status -static inline int __check_vpi_error(const char *file, const char *func, long line) -{ - int level=0; -#if VPI_CHECKING - s_vpi_error_info info; - enum gpi_log_levels loglevel; - - memset(&info, 0, sizeof(info)); - level = vpi_chk_error(&info); - if (info.code == 0 && level == 0) - return 0; - - switch (level) { - case vpiNotice: - loglevel = GPIInfo; - break; - case vpiWarning: - loglevel = GPIWarning; - break; - case vpiError: - loglevel = GPIError; - break; - case vpiSystem: - case vpiInternal: - loglevel = GPICritical; - break; - default: - loglevel = GPIWarning; - } - - gpi_log("cocotb.gpi", loglevel, file, func, line, "VPI error"); - gpi_log("cocotb.gpi", loglevel, info.file, info.product, info.line, info.message); - -#endif - return level; -} - -#define check_vpi_error() do { \ - __check_vpi_error(__FILE__, __func__, __LINE__); \ -} while (0) - -class VpiReadwriteCbHdl; -class VpiNextPhaseCbHdl; -class VpiReadOnlyCbHdl; - -class VpiCbHdl : public virtual GpiCbHdl { -public: - VpiCbHdl(GpiImplInterface *impl); - - int arm_callback() override; - int cleanup_callback() override; - -protected: - s_cb_data cb_data; - s_vpi_time vpi_time; -}; - -class VpiSignalObjHdl; - -class VpiValueCbHdl : public VpiCbHdl, public GpiValueCbHdl { -public: - VpiValueCbHdl(GpiImplInterface *impl, VpiSignalObjHdl *sig, int edge); - int cleanup_callback() override; -private: - s_vpi_value m_vpi_value; -}; - -class VpiTimedCbHdl : public VpiCbHdl { -public: - VpiTimedCbHdl(GpiImplInterface *impl, uint64_t time); - int cleanup_callback() override; -}; - -class VpiReadOnlyCbHdl : public VpiCbHdl { -public: - VpiReadOnlyCbHdl(GpiImplInterface *impl); -}; - -class VpiNextPhaseCbHdl : public VpiCbHdl { -public: - VpiNextPhaseCbHdl(GpiImplInterface *impl); -}; - -class VpiReadwriteCbHdl : public VpiCbHdl { -public: - VpiReadwriteCbHdl(GpiImplInterface *impl); -}; - -class VpiStartupCbHdl : public VpiCbHdl { -public: - VpiStartupCbHdl(GpiImplInterface *impl); - int run_callback() override; - int cleanup_callback() override { - /* Too many sims get upset with this so we override to do nothing */ - return 0; - } -}; - -class VpiShutdownCbHdl : public VpiCbHdl { -public: - VpiShutdownCbHdl(GpiImplInterface *impl); - int run_callback() override; - int cleanup_callback() override{ - /* Too many sims get upset with this so we override to do nothing */ - return 0; - } -}; - -class VpiArrayObjHdl : public GpiObjHdl { -public: - VpiArrayObjHdl(GpiImplInterface *impl, vpiHandle hdl, gpi_objtype_t objtype) : - GpiObjHdl(impl, hdl, objtype) { } - - int initialise(std::string &name, std::string &fq_name) override; -}; - -class VpiObjHdl : public GpiObjHdl { -public: - VpiObjHdl(GpiImplInterface *impl, vpiHandle hdl, gpi_objtype_t objtype) : - GpiObjHdl(impl, hdl, objtype) { } - - int initialise(std::string &name, std::string &fq_name) override; -}; - -class VpiSignalObjHdl : public GpiSignalObjHdl { -public: - VpiSignalObjHdl(GpiImplInterface *impl, vpiHandle hdl, gpi_objtype_t objtype, bool is_const) : - GpiSignalObjHdl(impl, hdl, objtype, is_const), - m_rising_cb(impl, this, GPI_RISING), - m_falling_cb(impl, this, GPI_FALLING), - m_either_cb(impl, this, GPI_FALLING | GPI_RISING) { } - - const char* get_signal_value_binstr() override; - const char* get_signal_value_str() override; - double get_signal_value_real() override; - long get_signal_value_long() override; - - int set_signal_value(const int32_t value, gpi_set_action_t action) override; - int set_signal_value(const double value, gpi_set_action_t action) override; - int set_signal_value_binstr(std::string &value, gpi_set_action_t action) override; - int set_signal_value_str(std::string &value, gpi_set_action_t action) override; - - /* Value change callback accessor */ - GpiCbHdl *value_change_cb(int edge) override; - int initialise(std::string &name, std::string &fq_name) override; - -private: - int set_signal_value(s_vpi_value value, gpi_set_action_t action); - - VpiValueCbHdl m_rising_cb; - VpiValueCbHdl m_falling_cb; - VpiValueCbHdl m_either_cb; -}; - - -class VpiIterator : public GpiIterator { -public: - VpiIterator(GpiImplInterface *impl, GpiObjHdl *hdl); - - ~VpiIterator() override; - - Status next_handle(std::string &name, GpiObjHdl **hdl, void **raw_hdl) override; - -private: - vpiHandle m_iterator; - static std::map> iterate_over; /* Possible mappings */ - std::vector *selected; /* Mapping currently in use */ - std::vector::iterator one2many; -}; - -// Base class for simple iterator that only iterates over a single type -class VpiSingleIterator : public GpiIterator { -public: - VpiSingleIterator(GpiImplInterface *impl, - GpiObjHdl *hdl, - int32_t vpitype) : GpiIterator(impl, hdl) - - { - vpiHandle vpi_hdl = m_parent->get_handle(); - m_iterator = vpi_iterate(vpitype, vpi_hdl); - if (NULL == m_iterator) { - LOG_WARN("vpi_iterate returned NULL for type %d for object %s(%d)", - vpitype, vpi_get_str(vpiType, vpi_hdl), vpi_get(vpiType, vpi_hdl)); - return; - } - } - - Status next_handle(std::string &name, GpiObjHdl **hdl, void **raw_hdl) override; - -protected: - vpiHandle m_iterator = nullptr; -}; - - -class VpiImpl : public GpiImplInterface { -public: - VpiImpl(const std::string& name) : GpiImplInterface(name), - m_read_write(this), - m_next_phase(this), - m_read_only(this) { } - - /* Sim related */ - void sim_end(void) override; - void get_sim_time(uint32_t *high, uint32_t *low) override; - void get_sim_precision(int32_t *precision) override; - const char *get_simulator_product() override; - const char *get_simulator_version() override; - - /* Hierarchy related */ - GpiObjHdl *get_root_handle(const char *name) override; - GpiIterator *iterate_handle(GpiObjHdl *obj_hdl, gpi_iterator_sel_t type) override; - GpiObjHdl *next_handle(GpiIterator *iter); - - /* Callback related, these may (will) return the same handle*/ - GpiCbHdl *register_timed_callback(uint64_t time) override; - GpiCbHdl *register_readonly_callback() override; - GpiCbHdl *register_nexttime_callback() override; - GpiCbHdl *register_readwrite_callback() override; - int deregister_callback(GpiCbHdl *obj_hdl) override; - GpiObjHdl* native_check_create(std::string &name, GpiObjHdl *parent) override; - GpiObjHdl* native_check_create(int32_t index, GpiObjHdl *parent) override; - GpiObjHdl* native_check_create(void *raw_hdl, GpiObjHdl *parent) override; - const char * reason_to_string(int reason) override; - GpiObjHdl* create_gpi_obj_from_handle(vpiHandle new_hdl, - std::string &name, - std::string &fq_name); - -private: - /* Singleton callbacks */ - VpiReadwriteCbHdl m_read_write; - VpiNextPhaseCbHdl m_next_phase; - VpiReadOnlyCbHdl m_read_only; -}; - -#endif /*COCOTB_VPI_IMPL_H_ */ diff --git a/cocotb/share/makefiles/Makefile.deprecations b/cocotb/share/makefiles/Makefile.deprecations deleted file mode 100644 index 1b08fa82..00000000 --- a/cocotb/share/makefiles/Makefile.deprecations +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause - -ifdef VERILATOR_TRACE - $(warning VERILATOR_TRACE is deprecated, see the "Simulator Support" section in the documentation.) -endif - -ifeq ($(SIM_LOWERCASE),aldec) - $(warning Using SIM=aldec is deprecated, please use SIM=riviera instead.) - SIM_LOWERCASE := riviera -endif diff --git a/cocotb/share/makefiles/Makefile.inc b/cocotb/share/makefiles/Makefile.inc deleted file mode 100644 index 912bde04..00000000 --- a/cocotb/share/makefiles/Makefile.inc +++ /dev/null @@ -1,150 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################## - -# Common makefile included by everything - -ifndef COCOTB_MAKEFILE_INC_INCLUDED # Protect against multiple includes -COCOTB_MAKEFILE_INC_INCLUDED = 1 - -# Default sim rule will force a re-run of the simulation (though the cocotb library -# and RTL compilation phases are still evaluated by makefile dependencies) -.PHONY: sim -sim: - -@rm -f $(COCOTB_RESULTS_FILE) - $(MAKE) $(COCOTB_RESULTS_FILE) - -# Make sure to use bash for the pipefail option used in many simulator Makefiles -SHELL := bash - -# Directory containing the cocotb Python module (realpath for Windows compatibility) -COCOTB_PY_DIR := $(realpath $(shell cocotb-config --prefix)) - -# Directory containing all support files required to build cocotb-based -# simulations: Makefile fragments, and the simulator libraries. -COCOTB_SHARE_DIR := $(COCOTB_PY_DIR)/cocotb/share - -OS=$(shell uname) -ifneq (, $(findstring MINGW, $(OS))) - OS := Msys -else ifneq (, $(findstring MSYS, $(OS))) - OS := Msys -endif -export OS - -# this ensures we use the same python as the one cocotb was installed into -# realpath to convert windows paths to unix paths, like cygpath -u -PYTHON_BIN ?= $(realpath $(shell cocotb-config --python-bin)) - -include $(COCOTB_SHARE_DIR)/makefiles/Makefile.deprecations - -LIB_DIR=$(COCOTB_PY_DIR)/cocotb/libs - -ifeq ($(OS),Msys) - LIB_EXT := dll -else - LIB_EXT := so -endif - -PYTHON_ARCH := $(shell $(PYTHON_BIN) -c 'from platform import architecture; print(architecture()[0])') -ifeq ($(filter $(PYTHON_ARCH),64bit 32bit),) - $(error Unknown Python architecture: $(PYTHON_ARCH)) -endif - -# Set PYTHONHOME to properly populate sys.path in embedded python interpreter -export PYTHONHOME := $(shell $(PYTHON_BIN) -c 'from distutils.sysconfig import get_config_var; print(get_config_var("prefix"))') - -ifeq ($(OS),Msys) - to_tcl_path = $(shell cygpath -m $(1) ) -else - to_tcl_path = $(1) -endif - -# Check that the COCOTB_RESULTS_FILE was created, since we can't set an exit code from cocotb. -define check_for_results_file - @test -f $(COCOTB_RESULTS_FILE) || (echo "ERROR: $(COCOTB_RESULTS_FILE) was not written by the simulation!" >&2 && exit 1) -endef - -SIM_BUILD ?= sim_build -export SIM_BUILD - -COCOTB_RESULTS_FILE ?= results.xml -COCOTB_HDL_TIMEUNIT ?= 1ns -COCOTB_HDL_TIMEPRECISION ?= 1ps - -# Depend on all Python from the cocotb package. This triggers a -# recompilation of the simulation if cocotb is updated. -CUSTOM_SIM_DEPS += $(shell find $(COCOTB_PY_DIR)/cocotb/ -name "*.py") - -# This triggers a recompilation of the simulation if cocotb library is updated. -CUSTOM_SIM_DEPS += $(shell find $(LIB_DIR)/ -name "*") - -$(SIM_BUILD): - mkdir -p $@ - -# Regression rule uses Make dependencies to determine whether to run the simulation -.PHONY: regression -regression: $(COCOTB_RESULTS_FILE) - -# Attempt to detect TOPLEVEL_LANG based on available sources if not set -ifeq ($(TOPLEVEL_LANG),) - -ifneq ($(and $(VHDL_SOURCES),$(if $(VERILOG_SOURCES),,1),) - TOPLEVEL_LANG := vhdl -else ifneq ($(and $(VERILOG_SOURCES),$(if $(VHDL_SOURCES),,1),) - TOPLEVEL_LANG := verilog -endif - -endif - -define find_libpython_errmsg = - - -find_libpython was not able to find a libpython in the current Python environment. Ensure -the Python development packages are installed. If they are installed and find_libpython -is not finding the path to libpython, file an upstream bug in find_libpython; then -manually override the LIBPYTHON_LOC make variable with the absolute path to libpython.so -(or python.dll on Windows). - -endef - -# get the path to libpython and the return code from the script -# adapted from https://stackoverflow.com/a/24658961/6614127 -FIND_LIBPYTHON_RES := $(shell cocotb-config --libpython; echo $$?) -FIND_LIBPYTHON_RC := $(lastword $(FIND_LIBPYTHON_RES)) -LIBPYTHON_LOC := $(strip $(subst $(FIND_LIBPYTHON_RC)QQQQ,,$(FIND_LIBPYTHON_RES)QQQQ)) - -# complain if libpython isn't found, and export otherwise -ifneq ($(FIND_LIBPYTHON_RC),0) - $(error $(find_libpython_errmsg)) -endif -export LIBPYTHON_LOC - -else - $(warning Including Makefile.inc from a user makefile is a no-op and deprecated. Remove the Makefile.inc inclusion from your makefile, and only leave the Makefile.sim include.) -endif # COCOTB_MAKEFILE_INC_INCLUDED diff --git a/cocotb/share/makefiles/Makefile.sim b/cocotb/share/makefiles/Makefile.sim deleted file mode 100644 index 2b5b6f85..00000000 --- a/cocotb/share/makefiles/Makefile.sim +++ /dev/null @@ -1,111 +0,0 @@ -############################################################################### -# Copyright (c) 2013, 2018 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -# This file includes an appropriate makefile depending on the SIM variable. - -.PHONY: all -all: sim - -# NOTE: keep this at 80 chars. -define help_targets = -Targets -======= -sim Unconditionally re-run the simulator (default) -regression Run simulator when dependencies have changes -clean Remove build and simulation artefacts -help This help text - -endef - -# NOTE: keep this at 80 chars. -define help_makevars = -Variables -========= - -The following variables are makefile variables: - -Makefile-based Test Scripts ---------------------------- -GUI Set this to 1 to enable the GUI mode in the simulator -SIM Selects which simulator Makefile to use -WAVES Enable wave traces dump for Riviera-PRO and Questa -VERILOG_SOURCES A list of the Verilog source files to include -VHDL_SOURCES A list of the VHDL source files to include -VHDL_SOURCES_ VHDL source files to include in *lib* (GHDL only) -COMPILE_ARGS Arguments to pass to compile stage of simulation -SIM_ARGS Arguments to pass to execution of compiled simulation -EXTRA_ARGS Arguments for compile and execute phases -PLUSARGS Plusargs to pass to the simulator -COCOTB_HDL_TIMEUNIT Default time unit for simulation -COCOTB_HDL_TIMEPRECISION Default time precision for simulation -CUSTOM_COMPILE_DEPS Add additional dependencies to the compilation target -CUSTOM_SIM_DEPS Add additional dependencies to the simulation target -SIM_BUILD Define a scratch directory for use by the simulator -SCRIPT_FILE Simulator script to run (for e.g. wave traces) - -endef - - -# NOTE: keep *two* empty lines between "define" and "endef": -define newline - - -endef - -# this cannot be a regular target because of the way Makefile.$(SIM) is included -ifeq ($(MAKECMDGOALS),help) - $(info $(help_targets)) - $(info $(help_makevars)) - # hack to get newlines in output, see https://stackoverflow.com/a/54539610 - # NOTE: the output of the command must not include a '%' sign, otherwise the formatting will break - help_envvars := $(subst %,${newline},$(shell cocotb-config --help-vars | tr \\n %)) - $(info ${help_envvars}) - # is there a cleaner way to exit here? - $(error "Stopping after printing help") -endif - -# Default to Icarus if no simulator is defined -SIM ?= icarus - -# Maintain backwards compatibility by supporting upper and lower case SIM variable -SIM_LOWERCASE := $(shell echo $(SIM) | tr A-Z a-z) - -# Directory containing the cocotb Makfiles (realpath for Windows compatibility) -COCOTB_MAKEFILES_DIR := $(realpath $(shell cocotb-config --makefiles)) - -include $(COCOTB_MAKEFILES_DIR)/Makefile.deprecations - -HAVE_SIMULATOR = $(shell if [ -f $(COCOTB_MAKEFILES_DIR)/simulators/Makefile.$(SIM_LOWERCASE) ]; then echo 1; else echo 0; fi;) -AVAILABLE_SIMULATORS = $(patsubst .%,%,$(suffix $(wildcard $(COCOTB_MAKEFILES_DIR)/simulators/Makefile.*))) - -ifeq ($(HAVE_SIMULATOR),0) - $(error "Couldn't find makefile for simulator: "$(SIM_LOWERCASE)"! Available simulators: $(AVAILABLE_SIMULATORS)") -endif - -include $(COCOTB_MAKEFILES_DIR)/simulators/Makefile.$(SIM_LOWERCASE) diff --git a/cocotb/share/makefiles/simulators/Makefile.activehdl b/cocotb/share/makefiles/simulators/Makefile.activehdl deleted file mode 100644 index 65e22a20..00000000 --- a/cocotb/share/makefiles/simulators/Makefile.activehdl +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause - -# Common Makefile for the Aldec Active-HDL simulator - -include $(shell cocotb-config --makefiles)/Makefile.inc - -CMD_BIN := vsimsa - -ifdef ACTIVEHDL_BIN_DIR - CMD := $(shell :; command -v $(realpath $(ACTIVEHDL_BIN_DIR))/$(CMD_BIN) 2>/dev/null) -else - # auto-detect bin dir from system path - CMD := $(shell :; command -v $(CMD_BIN) 2>/dev/null) -endif - -ALOG_ARGS += -timescale $(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) - -# below allows for maintaining legacy syntax as well as enables using cross-simulator vars COMPILE_ARGS/SIM_ARGS -ALOG_ARGS += $(COMPILE_ARGS) -ACOM_ARGS += $(COMPILE_ARGS) -ASIM_ARGS += $(SIM_ARGS) - -RTL_LIBRARY ?= work -ALOG_ARGS += +define+COCOTB_SIM -dbg -ACOM_ARGS += -dbg - -GPI_EXTRA:= -ifeq ($(TOPLEVEL_LANG),verilog) - # backslashes needed because we embed in `echo` below - GPI_ARGS = -pli \"$(call to_tcl_path,$(LIB_DIR)/libcocotbvpi_aldec)\" -ifneq ($(VHDL_SOURCES),) - GPI_EXTRA = cocotbvhpi_aldec:cocotbvhpi_entry_point -endif - -else ifeq ($(TOPLEVEL_LANG),vhdl) - # backslashes needed because we embed in `echo` below - GPI_ARGS = -loadvhpi \"$(call to_tcl_path,$(LIB_DIR)/libcocotbvhpi_aldec):vhpi_startup_routines_bootstrap\" -ifneq ($(VERILOG_SOURCES),) - GPI_EXTRA = cocotbvpi_aldec:cocotbvpi_entry_point -endif -else - $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") -endif - -# Create a DO script (Tcl-like but not fully compatible) based on the list of $(VERILOG_SOURCES) -$(SIM_BUILD)/runsim.do : $(VERILOG_SOURCES) $(VHDL_SOURCES) | $(SIM_BUILD) - @echo "alib $(RTL_LIBRARY)" > $@ - @echo "set worklib $(RTL_LIBRARY)" >> $@ -ifneq ($(VHDL_SOURCES),) - @echo "acom $(ACOM_ARGS) $(call to_tcl_path,$(VHDL_SOURCES))" >> $@ -endif -ifneq ($(VERILOG_SOURCES),) - @echo "alog $(ALOG_ARGS) $(call to_tcl_path,$(VERILOG_SOURCES))" >> $@ -endif - @echo "asim $(ASIM_ARGS) $(PLUSARGS) +access +w -interceptcoutput -O2 -dbg $(GPI_ARGS) $(TOPLEVEL) $(EXTRA_TOPS)" >> $@ - @echo "run -all" >> $@ - @echo "endsim" >> $@ - -$(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.do $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) - -@rm -f $(COCOTB_RESULTS_FILE) - - set -o pipefail; GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) $(RUN_ARGS) -do $(SIM_BUILD)/runsim.do | tee $(SIM_BUILD)/sim.log - - $(call check_for_results_file) - -clean:: - @rm -rf $(SIM_BUILD) - @rm -rf work - @rm -rf wave.asdb diff --git a/cocotb/share/makefiles/simulators/Makefile.cvc b/cocotb/share/makefiles/simulators/Makefile.cvc deleted file mode 100644 index c0644293..00000000 --- a/cocotb/share/makefiles/simulators/Makefile.cvc +++ /dev/null @@ -1,94 +0,0 @@ -############################################################################### -# Copyright (c) 2014 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -include $(shell cocotb-config --makefiles)/Makefile.inc - -ifneq ($(VHDL_SOURCES),) - -$(COCOTB_RESULTS_FILE): - @echo "Skipping simulation as VHDL is not supported on simulator=$(SIM)" -debug: $(COCOTB_RESULTS_FILE) -clean:: - -else - -CMD_BIN := cvc64 - -ifdef CVC_BIN_DIR - CMD := $(shell :; command -v $(CVC_BIN_DIR)/$(CMD_BIN) 2>/dev/null) -else - # auto-detect bin dir from system path - CMD := $(shell :; command -v $(CMD_BIN) 2>/dev/null) -endif - -ifeq (, $(CMD)) - $(error "Unable to locate command >$(CMD_BIN)<") -else - CVC_BIN_DIR := $(shell dirname $(CMD)) - export CVC_BIN_DIR -endif - -#only interpreted mode works for the moment -CVC_ITERP ?= 1 - -ifeq ($(CVC_ITERP),1) - CVC_ARGS += +interp -endif - -# Compilation phase -$(SIM_BUILD)/sim.vvp: $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) | $(SIM_BUILD) - MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(CMD) $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=$(LIB_DIR)/libcocotbvpi_modelsim:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) - -# Execution phase -ifeq ($(CVC_ITERP),1) - $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp -else - $(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) - MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) -endif - -# Execution phase -ifeq ($(CVC_ITERP),1) - debug: $(CUSTOM_SIM_DEPS) - MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - gdb --args $(CMD) $(CVC_ARGS) +acc+2 -o $(SIM_BUILD)/sim.vvp +define+COCOTB_SIM=1 +loadvpi=$(LIB_DIR)/modelsim/libcocotbvpi:vlog_startup_routines_bootstrap $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) -else - debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) - MODULE=$(MODULE) \ - TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - gdb --args $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) -endif - - -clean:: - -@rm -rf $(SIM_BUILD) -endif diff --git a/cocotb/share/makefiles/simulators/Makefile.ghdl b/cocotb/share/makefiles/simulators/Makefile.ghdl deleted file mode 100644 index e474ff49..00000000 --- a/cocotb/share/makefiles/simulators/Makefile.ghdl +++ /dev/null @@ -1,85 +0,0 @@ -############################################################################### -# Copyright (c) 2014 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -TOPLEVEL_LANG ?= vhdl - -include $(shell cocotb-config --makefiles)/Makefile.inc - -ifneq ($(or $(filter-out $(TOPLEVEL_LANG),vhdl),$(VERILOG_SOURCES)),) - -$(COCOTB_RESULTS_FILE): - @echo "Skipping simulation as only VHDL is supported on simulator=$(SIM)" -clean:: - -else - -CMD_BIN := ghdl - -ifdef GHDL_BIN_DIR - CMD := $(shell :; command -v $(GHDL_BIN_DIR)/$(CMD_BIN) 2>/dev/null) -else - # auto-detect bin dir from system path - CMD := $(shell :; command -v $(CMD_BIN) 2>/dev/null) -endif - -ifeq (, $(CMD)) - $(error "Unable to locate command >$(CMD_BIN)<") -else - GHDL_BIN_DIR := $(shell dirname $(CMD)) - export GHDL_BIN_DIR -endif - -RTL_LIBRARY ?= work -GHDL_ARGS ?= -GHDL_ARGS += $(EXTRA_ARGS) - -# On Windows add GHDL lib folder to PATH to find libraries -ifeq ($(OS),Msys) - export PATH := $(GHDL_BIN_DIR)/../lib:$(PATH) -endif - -.PHONY: analyse - -# Compilation phase -analyse: $(VHDL_SOURCES) $(CUSTOM_COMPILE_DEPS) | $(SIM_BUILD) - $(foreach SOURCES_VAR, $(filter VHDL_SOURCES_%, $(.VARIABLES)), \ - $(CMD) -i $(GHDL_ARGS) $(COMPILE_ARGS) --workdir=$(SIM_BUILD) --work=$(SOURCES_VAR:VHDL_SOURCES_%=%) $($(SOURCES_VAR)) && ) \ - $(CMD) -i $(GHDL_ARGS) $(COMPILE_ARGS) --workdir=$(SIM_BUILD) --work=$(RTL_LIBRARY) $(VHDL_SOURCES) && \ - $(CMD) -m $(GHDL_ARGS) $(COMPILE_ARGS) --workdir=$(SIM_BUILD) -P$(SIM_BUILD) --work=$(RTL_LIBRARY) $(TOPLEVEL) - -$(COCOTB_RESULTS_FILE): analyse $(CUSTOM_SIM_DEPS) - -@rm -f $(COCOTB_RESULTS_FILE) - - MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(CMD) -r $(GHDL_ARGS) --workdir=$(SIM_BUILD) -P$(SIM_BUILD) --work=$(RTL_LIBRARY) $(TOPLEVEL) --vpi=$(LIB_DIR)/libcocotbvpi_ghdl.$(LIB_EXT) $(SIM_ARGS) $(PLUSARGS) - - $(call check_for_results_file) - -clean:: - -@rm -rf $(SIM_BUILD) -endif diff --git a/cocotb/share/makefiles/simulators/Makefile.icarus b/cocotb/share/makefiles/simulators/Makefile.icarus deleted file mode 100644 index 07a7444a..00000000 --- a/cocotb/share/makefiles/simulators/Makefile.icarus +++ /dev/null @@ -1,86 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -TOPLEVEL_LANG ?= verilog - -include $(shell cocotb-config --makefiles)/Makefile.inc - -ifneq ($(or $(filter-out $(TOPLEVEL_LANG),verilog),$(VHDL_SOURCES)),) - -$(COCOTB_RESULTS_FILE): - @echo "Skipping simulation as only Verilog is supported on simulator=$(SIM)" -debug: $(COCOTB_RESULTS_FILE) -clean:: - -else - -CMD_BIN := iverilog - -ifdef ICARUS_BIN_DIR - CMD := $(shell :; command -v $(ICARUS_BIN_DIR)/$(CMD_BIN) 2>/dev/null) -else - # auto-detect bin dir from system path - CMD := $(shell :; command -v $(CMD_BIN) 2>/dev/null) -endif - -ifeq (, $(CMD)) - $(error "Unable to locate command >$(CMD_BIN)<") -else - ICARUS_BIN_DIR := $(shell dirname $(CMD)) - export ICARUS_BIN_DIR -endif - -COMPILE_ARGS += -f $(SIM_BUILD)/cmds.f -g2012 # Default to latest SystemVerilog standard - -# Compilation phase -$(SIM_BUILD)/sim.vvp: $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) | $(SIM_BUILD) - @echo "+timescale+$(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION)" > $(SIM_BUILD)/cmds.f - $(CMD) -o $(SIM_BUILD)/sim.vvp -D COCOTB_SIM=1 -s $(TOPLEVEL) $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) - -# Execution phase - -$(COCOTB_RESULTS_FILE): $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) - -@rm -f $(COCOTB_RESULTS_FILE) - - MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(ICARUS_BIN_DIR)/vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_ARGS) $(EXTRA_ARGS) $(SIM_BUILD)/sim.vvp $(PLUSARGS) - - $(call check_for_results_file) - -debug: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) - -@rm -f $(COCOTB_RESULTS_FILE) - - MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - gdb --args $(ICARUS_BIN_DIR)/vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) - - $(call check_for_results_file) - -clean:: - -@rm -rf $(SIM_BUILD) -endif diff --git a/cocotb/share/makefiles/simulators/Makefile.ius b/cocotb/share/makefiles/simulators/Makefile.ius deleted file mode 100644 index 33560ef5..00000000 --- a/cocotb/share/makefiles/simulators/Makefile.ius +++ /dev/null @@ -1,114 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -# Common Makefile for Cadence Incisive - -include $(shell cocotb-config --makefiles)/Makefile.inc - -CMD_BIN := irun - -ifdef IUS_BIN_DIR - CMD := $(shell :; command -v $(IUS_BIN_DIR)/$(CMD_BIN) 2>/dev/null) -else - # auto-detect bin dir from system path - CMD := $(shell :; command -v $(CMD_BIN) 2>/dev/null) -endif - -ifeq (, $(CMD)) - $(error "Unable to locate command >$(CMD_BIN)<") -else - IUS_BIN_DIR := $(shell dirname $(CMD)) - export IUS_BIN_DIR -endif - -EXTRA_ARGS += $(COMPILE_ARGS) -EXTRA_ARGS += $(SIM_ARGS) -EXTRA_ARGS += -licqueue - -ifeq ($(PYTHON_ARCH),64bit) - EXTRA_ARGS += -64 -endif - -EXTRA_ARGS += -nclibdirpath $(SIM_BUILD) -EXTRA_ARGS += -plinowarn - -ifeq ($(GUI),1) - EXTRA_ARGS += -gui -else - EXTRA_ARGS += -endif - -# IUS errors out if multiple timescales are specified on the command line. -ifneq (,$(findstring timescale,$(EXTRA_ARGS))) - $(error "Please use COCOTB_HDL_TIMEUNIT and COCOTB_HDL_TIMEPRECISION to specify timescale.") -endif - -# Loading the VHPI library causes an error, so we always load the VPI library and supply -# GPI_EXTRA=cocotbvhpi if needed. - -# Xcelium will use default vlog_startup_routines symbol only if vpi library name is libvpi.so -GPI_ARGS = -loadvpi $(LIB_DIR)/libcocotbvpi_ius:vlog_startup_routines_bootstrap - -ifeq ($(TOPLEVEL_LANG),verilog) - EXTRA_ARGS += -v93 - HDL_SOURCES = $(VERILOG_SOURCES) - ROOT_LEVEL = $(TOPLEVEL) -ifneq ($(VHDL_SOURCES),) - HDL_SOURCES += $(VHDL_SOURCES) - GPI_EXTRA = cocotbvhpi_ius:cocotbvhpi_entry_point -endif -else ifeq ($(TOPLEVEL_LANG),vhdl) - GPI_EXTRA = cocotbvhpi_ius:cocotbvhpi_entry_point - EXTRA_ARGS += -v93 - EXTRA_ARGS += -top $(TOPLEVEL) - RTL_LIBRARY ?= $(TOPLEVEL) - MAKE_LIB = -makelib $(RTL_LIBRARY) - HDL_SOURCES = $(VHDL_SOURCES) -ifneq ($(VERILOG_SOURCES),) - HDL_SOURCES += $(VERILOG_SOURCES) -endif -else - $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") -endif - -$(COCOTB_RESULTS_FILE): $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) | $(SIM_BUILD) - -@rm -f $(COCOTB_RESULTS_FILE) - - set -o pipefail; \ - MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(CMD) -timescale $(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) \ - $(EXTRA_ARGS) $(GPI_ARGS) +access+rwc $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) 2>&1 | tee $(SIM_BUILD)/sim.log - - $(call check_for_results_file) - -clean:: - @rm -rf $(SIM_BUILD) - @rm -rf irun.* - @rm -rf ncsim.* - @rm -rf gdb_cmd_ncsim diff --git a/cocotb/share/makefiles/simulators/Makefile.modelsim b/cocotb/share/makefiles/simulators/Makefile.modelsim deleted file mode 100644 index 6efd01ac..00000000 --- a/cocotb/share/makefiles/simulators/Makefile.modelsim +++ /dev/null @@ -1,32 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - - -# Identical to Questa -include $(shell cocotb-config --makefiles)/simulators/Makefile.questa diff --git a/cocotb/share/makefiles/simulators/Makefile.questa b/cocotb/share/makefiles/simulators/Makefile.questa deleted file mode 100644 index d55ad365..00000000 --- a/cocotb/share/makefiles/simulators/Makefile.questa +++ /dev/null @@ -1,141 +0,0 @@ -############################################################################### -# Copyright (c) 2013, 2018 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -include $(shell cocotb-config --makefiles)/Makefile.inc - -CMD_BIN := vsim - -ifdef MODELSIM_BIN_DIR - CMD := $(shell :; command -v $(MODELSIM_BIN_DIR)/$(CMD_BIN) 2>/dev/null) -else - # auto-detect bin dir from system path - CMD := $(shell :; command -v $(CMD_BIN) 2>/dev/null) -endif - -ifeq (, $(CMD)) - $(error "Unable to locate command >$(CMD_BIN)<") -endif - -RTL_LIBRARY ?= work - -TOPLEVEL := "$(RTL_LIBRARY).$(TOPLEVEL)" - -ifndef VLOG_ARGS - VLOG_ARGS = -timescale $(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) -mfcu -endif - -COMPILE_ARGS += +acc - -ifdef VERILOG_INCLUDE_DIRS - VLOG_ARGS += +incdir+$(VERILOG_INCLUDE_DIRS) -endif - -# below allows for maintaining legacy syntax as well as enables using cross-simulator vars COMPILE_ARGS/SIM_ARGS -VLOG_ARGS += $(COMPILE_ARGS) -VCOM_ARGS += $(COMPILE_ARGS) -VSIM_ARGS += $(SIM_ARGS) - -ifeq ($(GUI),1) - CMD += -gui - VSIM_ARGS += -onfinish stop -else - CMD += -c - VSIM_ARGS += -onfinish exit -endif - -FLI_LIB := $(LIB_DIR)/libcocotbfli_modelsim.$(LIB_EXT) -# if this target is run, then cocotb did not build the library -$(FLI_LIB): - @echo -e "ERROR: cocotb was not installed with an FLI library, as the mti.h header could not be located.\n\ - If you installed an FLI-capable simulator after cocotb, you will need to reinstall cocotb.\n\ - Please check the cocotb documentation on ModelSim support." >&2 && exit 1 - -GPI_EXTRA:= -ifeq ($(TOPLEVEL_LANG),vhdl) - CUSTOM_COMPILE_DEPS += $(FLI_LIB) - VSIM_ARGS += -foreign \"cocotb_init $(call to_tcl_path,$(FLI_LIB))\" -ifneq ($(VERILOG_SOURCES),) - GPI_EXTRA = cocotbvpi_modelsim:cocotbvpi_entry_point -endif - -else ifeq ($(TOPLEVEL_LANG),verilog) - VSIM_ARGS += -pli $(call to_tcl_path,$(LIB_DIR)/libcocotbvpi_modelsim.$(LIB_EXT)) -ifneq ($(VHDL_SOURCES),) - GPI_EXTRA = cocotbfli_modelsim:cocotbfli_entry_point -endif - -else - $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") -endif - -$(SIM_BUILD)/runsim.do : $(VHDL_SOURCES) $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) | $(SIM_BUILD) - @echo "# Autogenerated file" > $@ - @echo "onerror {" >> $@ - @echo " quit -f -code 1" >> $@ - @echo "}" >> $@ - @echo "if [file exists $(SIM_BUILD)/$(RTL_LIBRARY)] {vdel -lib $(SIM_BUILD)/$(RTL_LIBRARY) -all}" >> $@ - @echo "vlib $(SIM_BUILD)/$(RTL_LIBRARY)" >> $@ - @echo "vmap -c" >> $@ - @echo "vmap $(RTL_LIBRARY) $(SIM_BUILD)/$(RTL_LIBRARY)" >> $@ -ifneq ($(VHDL_SOURCES),) - @echo "vcom -work $(RTL_LIBRARY) $(VCOM_ARGS) $(call to_tcl_path,$(VHDL_SOURCES))" >> $@ -endif -ifneq ($(VERILOG_SOURCES),) - @echo "vlog -work $(RTL_LIBRARY) +define+COCOTB_SIM -sv $(VLOG_ARGS) $(EXTRA_ARGS) $(call to_tcl_path,$(VERILOG_SOURCES))" >> $@ -endif -ifdef SCRIPT_FILE - @echo "do $(SCRIPT_FILE)" >> $@ -endif - @echo "vsim $(VSIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) $(SIM_BUILD)/$(TOPLEVEL)" >> $@ -ifeq ($(WAVES),1) - @echo "log -recursive /*" >> $@ -endif -ifeq ($(GUI),1) - @echo "add log -r *" >> $@ -else - @echo "onbreak resume" >> $@ - @echo "run -all" >> $@ - @echo "quit" >> $@ -endif - -ifeq ($(PYTHON_ARCH),64bit) - CMD += -64 -endif - -$(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.do - -@rm -f $(COCOTB_RESULTS_FILE) - - set -o pipefail; MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) \ - GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(CMD) $(RUN_ARGS) -do $(SIM_BUILD)/runsim.do $(PLUSARGS) 2>&1 | tee $(SIM_BUILD)/sim.log - - $(call check_for_results_file) - -clean:: - -rm -rf $(SIM_BUILD) diff --git a/cocotb/share/makefiles/simulators/Makefile.riviera b/cocotb/share/makefiles/simulators/Makefile.riviera deleted file mode 100644 index 622da0f8..00000000 --- a/cocotb/share/makefiles/simulators/Makefile.riviera +++ /dev/null @@ -1,157 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -# Common Makefile for Aldec Riviera-PRO simulator - -include $(shell cocotb-config --makefiles)/Makefile.inc - -ifeq ($(GUI),1) - CMD_BIN := riviera -else - CMD_BIN := vsimsa -endif - -ifdef ALDEC_BIN_DIR - CMD := $(shell :; command -v $(ALDEC_BIN_DIR)/$(CMD_BIN) 2>/dev/null) -else - # auto-detect bin dir from system path - CMD := $(shell :; command -v $(CMD_BIN) 2>/dev/null) -endif - -ifeq (, $(CMD)) - $(error "Unable to locate command >$(CMD_BIN)<") -else - ALDEC_BIN_DIR := $(shell dirname $(CMD)) - export ALDEC_BIN_DIR -endif - -ifeq ($(GUI),1) - CMD += -nosplash -endif - -ALOG_ARGS += -timescale $(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) - -# below allows for maintaining legacy syntax as well as enables using cross-simulator vars COMPILE_ARGS/SIM_ARGS -ALOG_ARGS += $(COMPILE_ARGS) -ACOM_ARGS += $(COMPILE_ARGS) -ASIM_ARGS += $(SIM_ARGS) - -# Plusargs need to be passed to ASIM command not vsimsa -ASIM_ARGS += $(PLUSARGS) - -RTL_LIBRARY ?= $(SIM_BUILD)/work -ALOG_ARGS += +define+COCOTB_SIM -dbg -pli libgpi -ACOM_ARGS += -dbg - -# Aldec-specific coverage types: -# - (s)tatement -# - (b)ranch -# - (e)xpression -# - (c)ondition -# - (a)ssertion -# - (p)ath -# - finite state (m)achine -# Documentation: Riviera Pro 2017.02 Documentation - Page 359 -COVERAGE_TYPES ?= sb -ifeq ($(COVERAGE),1) - ASIM_ARGS += -acdb -acdb_cov $(COVERAGE_TYPES) - ALOG_ARGS += -coverage $(COVERAGE_TYPES) -endif - -GPI_EXTRA:= -ifeq ($(TOPLEVEL_LANG),verilog) - GPI_ARGS = -pli $(call to_tcl_path,$(LIB_DIR)/libcocotbvpi_aldec) -ifneq ($(VHDL_SOURCES),) - GPI_EXTRA = cocotbvhpi_aldec:cocotbvhpi_entry_point -endif - -else ifeq ($(TOPLEVEL_LANG),vhdl) - GPI_ARGS = -loadvhpi $(call to_tcl_path,$(LIB_DIR)/libcocotbvhpi_aldec):vhpi_startup_routines_bootstrap -ifneq ($(VERILOG_SOURCES),) - GPI_EXTRA = cocotbvpi_aldec:cocotbvpi_entry_point -endif - -else - $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") -endif - -# Create a TCL script based on the list of $(VERILOG_SOURCES) -$(SIM_BUILD)/runsim.tcl : $(VERILOG_SOURCES) $(VHDL_SOURCES) | $(SIM_BUILD) - @echo "onerror {" > $@ - @echo " puts [read [open sim.log r]]" >> $@ - @echo " quit -code 1" >> $@ - @echo "}" >> $@ - @echo "@if [string length [array get env LICENSE_QUEUE]] {" >> $@ - @echo " set LICENSE_QUEUE $$::env(LICENSE_QUEUE)" >> $@ - @echo "}" >> $@ - @echo "alib $(RTL_LIBRARY)" >> $@ - @echo "set worklib $(RTL_LIBRARY)" >> $@ -ifneq ($(VHDL_SOURCES),) - @echo "acom $(ACOM_ARGS) $(call to_tcl_path,$(VHDL_SOURCES))" >> $@ -endif -ifneq ($(VERILOG_SOURCES),) - @echo "alog $(ALOG_ARGS) $(call to_tcl_path,$(VERILOG_SOURCES))" >> $@ -endif -ifdef SCRIPT_FILE - @echo "do $(SCRIPT_FILE)" >> $@ -endif -ifneq ($(CFG_TOPLEVEL),) - @echo "asim $(ASIM_ARGS) +access +w -interceptcoutput -O2 -dbg $(GPI_ARGS) $(CFG_TOPLEVEL) $(EXTRA_TOPS)" >> $@ -else - @echo "asim $(ASIM_ARGS) +access +w -interceptcoutput -O2 -dbg $(GPI_ARGS) $(TOPLEVEL) $(EXTRA_TOPS)" >> $@ -endif -ifeq ($(WAVES),1) - @echo "log -recursive *" >> $@ -endif -ifeq ($(GUI),1) - @echo "wave -rec *" >> $@ -else - @echo "run -all" >> $@ - @echo "endsim" >> $@ -ifeq ($(COVERAGE),1) - @echo "acdb report -cov $(COVERAGE_TYPES) -db $(RTL_LIBRARY).acdb -html -o coverage/acdb_report.html" >> $@ - @echo "acdb report -cov $(COVERAGE_TYPES) -db $(RTL_LIBRARY).acdb -txt -o coverage/acdb_report.txt" >> $@ -endif -endif - -# Note it's the redirection of the output rather than the 'do' command -# that turns on batch mode (i.e. exit on completion/error) -$(COCOTB_RESULTS_FILE): $(SIM_BUILD)/runsim.tcl $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) - -@rm -f $(COCOTB_RESULTS_FILE) - - set -o pipefail; GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) $(CMD) $(RUN_ARGS) -do $(SIM_BUILD)/runsim.tcl | tee $(SIM_BUILD)/sim.log - - $(call check_for_results_file) - -clean:: - @rm -rf $(SIM_BUILD) - @rm -rf compile - @rm -rf library.cfg - @rm -rf dataset.asdb diff --git a/cocotb/share/makefiles/simulators/Makefile.vcs b/cocotb/share/makefiles/simulators/Makefile.vcs deleted file mode 100644 index 7a02988e..00000000 --- a/cocotb/share/makefiles/simulators/Makefile.vcs +++ /dev/null @@ -1,96 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -include $(shell cocotb-config --makefiles)/Makefile.inc - -ifneq ($(VHDL_SOURCES),) - -$(COCOTB_RESULTS_FILE): - @echo "Skipping simulation as VHDL is not supported on simulator=$(SIM)" -clean:: - -else - -CMD_BIN := vcs - -ifdef VCS_BIN_DIR - CMD := $(shell :; command -v $(VCS_BIN_DIR)/$(CMD_BIN) 2>/dev/null) -else - # auto-detect bin dir from system path - CMD := $(shell :; command -v $(CMD_BIN) 2>/dev/null) -endif - -ifeq (, $(CMD)) - $(error "Unable to locate command >$(CMD_BIN)<") -else - VCS_BIN_DIR := $(shell dirname $(CMD)) - export VCS_BIN_DIR -endif - -ifeq ($(PYTHON_ARCH),64bit) - EXTRA_ARGS += -full64 -endif - -ifeq ($(GUI),1) - EXTRA_ARGS += -gui -endif - -# TODO: -# investigate +vpi+1 option which reduces memory requirements - -# Can't do this using an argument, we have to create a PLI table file -# enabling write access to the design -$(SIM_BUILD)/pli.tab : | $(SIM_BUILD) - echo "acc+=rw,wn:*" > $@ - -# Compilation phase -$(SIM_BUILD)/simv: $(VERILOG_SOURCES) $(SIM_BUILD)/pli.tab $(CUSTOM_COMPILE_DEPS) | $(SIM_BUILD) - cd $(SIM_BUILD) && \ - TOPLEVEL=$(TOPLEVEL) \ - $(CMD) -top $(TOPLEVEL) $(PLUSARGS) +acc+1 +vpi -P pli.tab +define+COCOTB_SIM=1 -sverilog \ - -timescale=$(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) \ - $(EXTRA_ARGS) -debug -load $(LIB_DIR)/libcocotbvpi_vcs.so $(COMPILE_ARGS) $(VERILOG_SOURCES) - -# Execution phase -$(COCOTB_RESULTS_FILE): $(SIM_BUILD)/simv $(CUSTOM_SIM_DEPS) - -@rm -f $(COCOTB_RESULTS_FILE) - - MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(SIM_BUILD)/simv +define+COCOTB_SIM=1 $(SIM_ARGS) $(EXTRA_ARGS) - - $(call check_for_results_file) - -.PHONY: clean -clean:: - -@rm -rf $(SIM_BUILD) - -@rm -rf simv.daidir - -@rm -rf cm.log - -@rm -rf $(COCOTB_RESULTS_FILE) - -@rm -rf ucli.key -endif diff --git a/cocotb/share/makefiles/simulators/Makefile.verilator b/cocotb/share/makefiles/simulators/Makefile.verilator deleted file mode 100644 index 78bffc55..00000000 --- a/cocotb/share/makefiles/simulators/Makefile.verilator +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause - -TOPLEVEL_LANG ?= verilog - -include $(shell cocotb-config --makefiles)/Makefile.inc - -ifneq ($(or $(filter-out $(TOPLEVEL_LANG),verilog),$(VHDL_SOURCES)),) - -results.xml: - @echo "Skipping simulation as only Verilog is supported on simulator=$(SIM)" -debug: results.xml -clean:: - -else - -CMD := verilator - -ifeq ($(shell which $(CMD) 2>/dev/null),) -# Verilator is not in PATH, lets start searching for it -$(error Cannot find verilator.) -endif - -VLT_MIN := 4.106 -VLT_VERSION := $(shell $(CMD) --version | cut -d " " -f 2) -MIN_VERSION := $(shell printf "%s\n%s\n" "$(VLT_MIN)" "$(VLT_VERSION)" | sort -g | head -1) -ifneq ($(MIN_VERSION),$(VLT_MIN)) - $(error cocotb requires Verilator $(VLT_MIN) or later, but using $(VLT_VERSION)) -endif - -ifeq ($(VERILATOR_SIM_DEBUG), 1) - COMPILE_ARGS += --debug - PLUSARGS += +verilator+debug - SIM_BUILD_FLAGS += -DVL_DEBUG - USER_CPPFLAGS += -g -endif - -ifeq ($(VERILATOR_TRACE),1) - EXTRA_ARGS += --trace --trace-structs -endif - -EXTRA_ARGS += --timescale $(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) - -SIM_BUILD_FLAGS += -std=c++11 - -COMPILE_ARGS += --vpi --public-flat-rw --prefix Vtop -o $(TOPLEVEL) -LDFLAGS "-Wl,-rpath,$(LIB_DIR) -L$(LIB_DIR) -lcocotbvpi_verilator" - -$(SIM_BUILD)/Vtop.mk: $(VERILOG_SOURCES) $(CUSTOM_COMPILE_DEPS) $(COCOTB_SHARE_DIR)/lib/verilator/verilator.cpp | $(SIM_BUILD) - $(CMD) -cc --exe -Mdir $(SIM_BUILD) -DCOCOTB_SIM=1 --top-module $(TOPLEVEL) $(COMPILE_ARGS) $(EXTRA_ARGS) $(VERILOG_SOURCES) $(COCOTB_SHARE_DIR)/lib/verilator/verilator.cpp - -# Compilation phase -$(SIM_BUILD)/$(TOPLEVEL): $(SIM_BUILD)/Vtop.mk - CPPFLAGS="$(SIM_BUILD_FLAGS)" make -C $(SIM_BUILD) -f Vtop.mk - -$(COCOTB_RESULTS_FILE): $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) - -@rm -f $(COCOTB_RESULTS_FILE) - - MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $< $(PLUSARGS) - - $(call check_for_results_file) - -debug: $(SIM_BUILD)/$(TOPLEVEL) $(CUSTOM_SIM_DEPS) - -@rm -f $(COCOTB_RESULTS_FILE) - - MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - gdb --args $< $(PLUSARGS) - - $(call check_for_results_file) - -clean:: - @rm -rf $(SIM_BUILD) - @rm -f dump.vcd - @rm -f dump.fst - -endif diff --git a/cocotb/share/makefiles/simulators/Makefile.xcelium b/cocotb/share/makefiles/simulators/Makefile.xcelium deleted file mode 100644 index 7f772b28..00000000 --- a/cocotb/share/makefiles/simulators/Makefile.xcelium +++ /dev/null @@ -1,121 +0,0 @@ -############################################################################### -# Copyright (c) 2013, 2018 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -include $(shell cocotb-config --makefiles)/Makefile.inc - -# Common Makefile for Cadence Xcelium - -CMD_BIN := xrun - -ifdef XCELIUM_BIN_DIR - CMD := $(shell :; command -v $(XCELIUM_BIN_DIR)/$(CMD_BIN) 2>/dev/null) -else - # auto-detect bin dir from system path - CMD := $(shell :; command -v $(CMD_BIN) 2>/dev/null) -endif - -ifeq (, $(CMD)) - $(error "Unable to locate command >$(CMD_BIN)<") -else - XCELIUM_BIN_DIR := $(shell dirname $(CMD)) - export XCELIUM_BIN_DIR -endif - -EXTRA_ARGS += $(COMPILE_ARGS) -EXTRA_ARGS += $(SIM_ARGS) -EXTRA_ARGS += -licqueue - -ifeq ($(PYTHON_ARCH),64bit) - EXTRA_ARGS += -64 -endif - -EXTRA_ARGS += -xmlibdirpath $(SIM_BUILD) -ifeq ($(DEBUG),1) - EXTRA_ARGS += -pliverbose - EXTRA_ARGS += -messages - EXTRA_ARGS += -plidebug # Enhance the profile output with PLI info - EXTRA_ARGS += -plierr_verbose # Expand handle info in PLI/VPI/VHPI messages - EXTRA_ARGS += -vpicompat 1800v2005 # <1364v1995|1364v2001|1364v2005|1800v2005> Specify the IEEE VPI -else - EXTRA_ARGS += -plinowarn -endif - -ifeq ($(GUI),1) - EXTRA_ARGS += -gui -else - EXTRA_ARGS += -endif - -# Xcelium errors out if multiple timescales are specified on the command line. -ifneq (,$(findstring timescale,$(EXTRA_ARGS))) - $(error "Please use COCOTB_HDL_TIMEUNIT and COCOTB_HDL_TIMEPRECISION to specify timescale.") -endif - -# Loading the VHPI library causes an error, so we always load the VPI library and supply -# GPI_EXTRA=cocotbvhpi if needed. - -# Xcelium will use default vlog_startup_routines symbol only if VPI library name is libvpi.so -GPI_ARGS = -loadvpi $(LIB_DIR)/libcocotbvpi_ius:vlog_startup_routines_bootstrap -ifeq ($(TOPLEVEL_LANG),verilog) - HDL_SOURCES = $(VERILOG_SOURCES) - ROOT_LEVEL = $(TOPLEVEL) - EXTRA_ARGS += -top $(TOPLEVEL) - ifneq ($(VHDL_SOURCES),) - HDL_SOURCES += $(VHDL_SOURCES) - GPI_EXTRA = cocotbvhpi_ius:cocotbvhpi_entry_point - endif -else ifeq ($(TOPLEVEL_LANG),vhdl) - # GPI_EXTRA will internally be extended to lib.so - GPI_EXTRA = cocotbvhpi_ius:cocotbvhpi_entry_point - EXTRA_ARGS += -top $(TOPLEVEL) - RTL_LIBRARY ?= $(TOPLEVEL) - MAKE_LIB = -makelib $(RTL_LIBRARY) - HDL_SOURCES = $(VHDL_SOURCES) - ifneq ($(VERILOG_SOURCES),) - HDL_SOURCES += $(VERILOG_SOURCES) - endif -else - $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") -endif - -$(COCOTB_RESULTS_FILE): $(HDL_SOURCES) $(CUSTOM_COMPILE_DEPS) $(CUSTOM_SIM_DEPS) | $(SIM_BUILD) - -@rm -f $(COCOTB_RESULTS_FILE) - - set -o pipefail; \ - MODULE=$(MODULE) TESTCASE=$(TESTCASE) TOPLEVEL=$(TOPLEVEL) GPI_EXTRA=$(GPI_EXTRA) TOPLEVEL_LANG=$(TOPLEVEL_LANG) \ - $(CMD) -timescale $(COCOTB_HDL_TIMEUNIT)/$(COCOTB_HDL_TIMEPRECISION) \ - $(EXTRA_ARGS) $(GPI_ARGS) $(INCDIRS) -access +rwc -createdebugdb $(MAKE_LIB) $(HDL_SOURCES) $(PLUSARGS) 2>&1 | tee $(SIM_BUILD)/sim.log - - $(call check_for_results_file) - -clean:: - @rm -rf $(SIM_BUILD) - @rm -rf xrun.* - @rm -rf xmsim.* - @rm -rf gdb_cmd_xmsim diff --git a/cocotb/triggers.py b/cocotb/triggers.py deleted file mode 100644 index 833d4cae..00000000 --- a/cocotb/triggers.py +++ /dev/null @@ -1,932 +0,0 @@ -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -"""A collections of triggers which a testbench can await.""" - -import abc -import warnings -from collections.abc import Awaitable - -from cocotb import simulator -from cocotb.log import SimLog -from cocotb.utils import ( - get_sim_steps, get_time_from_sim_steps, ParametrizedSingleton, - lazy_property, remove_traceback_frames, -) -from cocotb import outcomes -import cocotb - - -def _pointer_str(obj): - """ - Get the memory address of *obj* as used in :meth:`object.__repr__`. - - This is equivalent to ``sprintf("%p", id(obj))``, but python does not - support ``%p``. - """ - full_repr = object.__repr__(obj) # gives "<{type} object at {address}>" - return full_repr.rsplit(' ', 1)[1][:-1] - - -class TriggerException(Exception): - pass - - -class Trigger(Awaitable): - """Base class to derive from.""" - - # __dict__ is needed here for the `.log` lazy_property below to work. - # The implementation of `_PyObject_GenericGetAttrWithDict` suggests that - # despite its inclusion, __slots__ will overall give speed and memory - # improvements: - # - the `__dict__` is not actually constructed until it's needed, and that - # only happens if the `.log` attribute is used, where performance - # concerns no longer matter. - # - Attribute setting and getting will still go through the slot machinery - # first, as "data descriptors" take priority over dict access - __slots__ = ('primed', '__weakref__', '__dict__') - - def __init__(self): - self.primed = False - - @lazy_property - def log(self): - return SimLog("cocotb.%s" % (type(self).__qualname__), id(self)) - - @abc.abstractmethod - def prime(self, callback): - """Set a callback to be invoked when the trigger fires. - - The callback will be invoked with a single argument, `self`. - - Sub-classes must override this, but should end by calling the base class - method. - - Do not call this directly within coroutines, it is intended to be used - only by the scheduler. - """ - self.primed = True - - def unprime(self): - """Remove the callback, and perform cleanup if necessary. - - After being un-primed, a Trigger may be re-primed again in the future. - Calling `unprime` multiple times is allowed, subsequent calls should be - a no-op. - - Sub-classes may override this, but should end by calling the base class - method. - - Do not call this directly within coroutines, it is intended to be used - only by the scheduler. - """ - self.primed = False - - def __del__(self): - # Ensure if a trigger drops out of scope we remove any pending callbacks - self.unprime() - - @property - def _outcome(self): - """The result that `await this_trigger` produces in a coroutine. - - The default is to produce the trigger itself, which is done for - ease of use with :class:`~cocotb.triggers.First`. - """ - return outcomes.Value(self) - - def __await__(self): - # hand the trigger back to the scheduler trampoline - return (yield self) - - -class PythonTrigger(Trigger): - """Python triggers don't use GPI at all. - - For example: notification of coroutine completion. - """ - - -class GPITrigger(Trigger): - """Base Trigger class for GPI triggers. - - Consumes simulation time. - """ - __slots__ = ('cbhdl',) - - def __init__(self): - Trigger.__init__(self) - - # Required to ensure documentation can build - # if simulator is not None: - # self.cbhdl = simulator.create_callback(self) - # else: - self.cbhdl = None - - def unprime(self): - """Disable a primed trigger, can be re-primed.""" - if self.cbhdl is not None: - self.cbhdl.deregister() - self.cbhdl = None - Trigger.unprime(self) - - -class Timer(GPITrigger): - """Fires after the specified simulation time period has elapsed.""" - - def __init__(self, time=None, units="step", *, time_ps=None): - """ - Args: - time (numbers.Real or decimal.Decimal): The time value. - Note that despite the name this is not actually in picoseconds - but depends on the *units* argument. - .. versionchanged:: 1.5.0 - Previously this argument was misleadingly called `time_ps`. - units (str, optional): One of - ``'step'``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``. - When *units* is ``'step'``, - the timestep is determined by the simulator (see :make:var:`COCOTB_HDL_TIMEPRECISION`). - - Examples: - - >>> await Timer(100, units='ps') - - The time can also be a ``float``: - - >>> await Timer(100e-9, units='sec') - - which is particularly convenient when working with frequencies: - - >>> freq = 10e6 # 10 MHz - >>> await Timer(1 / freq, units='sec') - - Other builtin exact numeric types can be used too: - - >>> from fractions import Fraction - >>> await Timer(Fraction(1, 10), units='ns') - - >>> from decimal import Decimal - >>> await Timer(Decimal('100e-9'), units='sec') - - These are most useful when using computed durations while - avoiding floating point inaccuracies. - - See Also: - :func:`~cocotb.utils.get_sim_steps` - - Raises: - TriggerException: If a negative value is passed for Timer setup. - - .. versionchanged:: 1.5 - Raise an exception when Timer uses a negative value as it is undefined behavior. - Warn for 0 as this will cause erratic behavior in some simulators as well. - - .. versionchanged:: 1.5 - Support ``'step'`` as the the *units* argument to mean "simulator time step". - - .. deprecated:: 1.5 - Using None as the the *units* argument is deprecated, use ``'step'`` instead. - """ - GPITrigger.__init__(self) - if time_ps is not None: - if time is not None: - raise TypeError("Gave argument to both the 'time' and deprecated 'time_ps' parameter") - time = time_ps - warnings.warn( - "The parameter name 'time_ps' has been renamed to 'time'. Please update your invocation.", - DeprecationWarning, stacklevel=2) - else: - if time is None: - raise TypeError("Missing required argument 'time'") - if time <= 0: - if time == 0: - warnings.warn("Timer setup with value 0, which might exhibit undefined behavior in some simulators", - category=RuntimeWarning, - stacklevel=2) - else: - raise TriggerException("Timer value time_ps must not be negative") - if units is None: - warnings.warn( - 'Using units=None is deprecated, use units="step" instead.', - DeprecationWarning, stacklevel=2) - units = "step" # don't propagate deprecated value - self.sim_steps = get_sim_steps(time, units) - - def prime(self, callback): - """Register for a timed callback.""" - if self.cbhdl is None: - self.cbhdl = simulator.register_timed_callback(self.sim_steps, - callback, self) - if self.cbhdl is None: - raise TriggerException("Unable set up %s Trigger" % (str(self))) - GPITrigger.prime(self, callback) - - def __repr__(self): - return "<{} of {:1.2f}ps at {}>".format( - type(self).__qualname__, - get_time_from_sim_steps(self.sim_steps, units='ps'), - _pointer_str(self) - ) - - -# This is needed to make our custom metaclass work with abc.ABCMeta used in the -# `Trigger` base class. -class _ParameterizedSingletonAndABC(ParametrizedSingleton, abc.ABCMeta): - pass - - -class ReadOnly(GPITrigger, metaclass=_ParameterizedSingletonAndABC): - """Fires when the current simulation timestep moves to the read-only phase. - - The read-only phase is entered when the current timestep no longer has any further delta steps. - This will be a point where all the signal values are stable as there are no more RTL events scheduled for the timestep. - The simulator will not allow scheduling of more events in this timestep. - Useful for monitors which need to wait for all processes to execute (both RTL and cocotb) to ensure sampled signal values are final. - """ - __slots__ = () - - @classmethod - def __singleton_key__(cls): - return None - - def __init__(self): - GPITrigger.__init__(self) - - def prime(self, callback): - if self.cbhdl is None: - self.cbhdl = simulator.register_readonly_callback(callback, self) - if self.cbhdl is None: - raise TriggerException("Unable set up %s Trigger" % (str(self))) - GPITrigger.prime(self, callback) - - def __repr__(self): - return "{}()".format(type(self).__qualname__) - - -class ReadWrite(GPITrigger, metaclass=_ParameterizedSingletonAndABC): - """Fires when the read-write portion of the sim cycles is reached.""" - __slots__ = () - - @classmethod - def __singleton_key__(cls): - return None - - def __init__(self): - GPITrigger.__init__(self) - - def prime(self, callback): - if self.cbhdl is None: - # import pdb - # pdb.set_trace() - self.cbhdl = simulator.register_rwsynch_callback(callback, self) - if self.cbhdl is None: - raise TriggerException("Unable set up %s Trigger" % (str(self))) - GPITrigger.prime(self, callback) - - def __repr__(self): - return "{}()".format(type(self).__qualname__) - - -class NextTimeStep(GPITrigger, metaclass=_ParameterizedSingletonAndABC): - """Fires when the next time step is started.""" - __slots__ = () - - @classmethod - def __singleton_key__(cls): - return None - - def __init__(self): - GPITrigger.__init__(self) - - def prime(self, callback): - if self.cbhdl is None: - self.cbhdl = simulator.register_nextstep_callback(callback, self) - if self.cbhdl is None: - raise TriggerException("Unable set up %s Trigger" % (str(self))) - GPITrigger.prime(self, callback) - - def __repr__(self): - return "{}()".format(type(self).__qualname__) - - -class _EdgeBase(GPITrigger, metaclass=_ParameterizedSingletonAndABC): - """Internal base class that fires on a given edge of a signal.""" - __slots__ = ('signal',) - - @classmethod - @property - def _edge_type(self): - """The edge type, as understood by the C code. Must be set in sub-classes.""" - raise NotImplementedError - - @classmethod - def __singleton_key__(cls, signal): - return signal - - def __init__(self, signal): - super(_EdgeBase, self).__init__() - self.signal = signal - - def prime(self, callback): - """Register notification of a value change via a callback""" - if self.cbhdl is None: - self.cbhdl = simulator.register_value_change_callback( - self.signal._handle, callback, type(self)._edge_type, self - ) - if self.cbhdl is None: - raise TriggerException("Unable set up %s Trigger" % (str(self))) - super(_EdgeBase, self).prime(callback) - - def __repr__(self): - return "{}({!r})".format(type(self).__qualname__, self.signal) - - -class RisingEdge(_EdgeBase): - """Fires on the rising edge of *signal*, on a transition from ``0`` to ``1``.""" - __slots__ = () - _edge_type = 1 - - -class FallingEdge(_EdgeBase): - """Fires on the falling edge of *signal*, on a transition from ``1`` to ``0``.""" - __slots__ = () - _edge_type = 2 - - -class Edge(_EdgeBase): - """Fires on any value change of *signal*.""" - __slots__ = () - _edge_type = 3 - - -class _Event(PythonTrigger): - """Unique instance used by the Event object. - - One created for each attempt to wait on the event so that the scheduler - can maintain a dictionary of indexing each individual coroutine. - - FIXME: This will leak - need to use peers to ensure everything is removed - """ - - def __init__(self, parent): - PythonTrigger.__init__(self) - self.parent = parent - - def prime(self, callback): - self._callback = callback - self.parent._prime_trigger(self, callback) - Trigger.prime(self, callback) - - def __call__(self): - self._callback(self) - - def __repr__(self): - return "<{!r}.wait() at {}>".format(self.parent, _pointer_str(self)) - - -class Event: - """Event to permit synchronization between two coroutines. - - Awaiting :meth:`wait()` from one coroutine will block the coroutine until - :meth:`set()` is called somewhere else. - """ - - def __init__(self, name=None): - self._pending = [] - self.name = name - self.fired = False - self.data = None - - def _prime_trigger(self, trigger, callback): - self._pending.append(trigger) - - def set(self, data=None): - """Wake up all coroutines blocked on this event.""" - self.fired = True - self.data = data - - p = self._pending[:] - - self._pending = [] - - for trigger in p: - trigger() - - def wait(self): - """Get a trigger which fires when another coroutine sets the event. - - If the event has already been set, the trigger will fire immediately. - - To reset the event (and enable the use of ``wait`` again), - :meth:`clear` should be called. - """ - if self.fired: - return NullTrigger(name="{}.wait()".format(str(self))) - return _Event(self) - - def clear(self): - """Clear this event that has fired. - - Subsequent calls to :meth:`~cocotb.triggers.Event.wait` will block until - :meth:`~cocotb.triggers.Event.set` is called again.""" - self.fired = False - - def is_set(self) -> bool: - """ Return true if event has been set """ - return self.fired - - def __repr__(self): - if self.name is None: - fmt = "<{0} at {2}>" - else: - fmt = "<{0} for {1} at {2}>" - return fmt.format(type(self).__qualname__, self.name, _pointer_str(self)) - - -class _InternalEvent(PythonTrigger): - """Event used internally for triggers that need cross-coroutine synchronization. - - This Event can only be waited on once, by a single coroutine. - - Provides transparent __repr__ pass-through to the Trigger using this event, - providing a better debugging experience. - """ - def __init__(self, parent): - PythonTrigger.__init__(self) - self.parent = parent - self._callback = None - self.fired = False - self.data = None - - def prime(self, callback): - if self._callback is not None: - raise RuntimeError("This Trigger may only be awaited once") - self._callback = callback - Trigger.prime(self, callback) - if self.fired: - self._callback(self) - - def set(self, data=None): - """Wake up coroutine blocked on this event.""" - self.fired = True - self.data = data - - if self._callback is not None: - self._callback(self) - - def is_set(self) -> bool: - """Return true if event has been set.""" - return self.fired - - def __await__(self): - if self.primed: - raise RuntimeError("Only one coroutine may await this Trigger") - # hand the trigger back to the scheduler trampoline - return (yield self) - - def __repr__(self): - return repr(self.parent) - - -class _Lock(PythonTrigger): - """Unique instance used by the Lock object. - - One created for each attempt to acquire the Lock so that the scheduler - can maintain a dictionary of indexing each individual coroutine. - - FIXME: This will leak - need to use peers to ensure everything is removed. - """ - - def __init__(self, parent): - PythonTrigger.__init__(self) - self.parent = parent - - def prime(self, callback): - self._callback = callback - self.parent._prime_trigger(self, callback) - Trigger.prime(self, callback) - - def __call__(self): - self._callback(self) - - def __repr__(self): - return "<{!r}.acquire() at {}>".format(self.parent, _pointer_str(self)) - - -class Lock: - """Lock primitive (not re-entrant). - - This can be used as:: - - await lock.acquire() - try: - # do some stuff - finally: - lock.release() - - .. versionchanged:: 1.4 - - The lock can be used as an asynchronous context manager in an - :keyword:`async with` statement:: - - async with lock: - # do some stuff - """ - - def __init__(self, name=None): - self._pending_unprimed = [] - self._pending_primed = [] - self.name = name - self.locked = False #: ``True`` if the lock is held. - - def _prime_trigger(self, trigger, callback): - self._pending_unprimed.remove(trigger) - - if not self.locked: - self.locked = True - callback(trigger) - else: - self._pending_primed.append(trigger) - - def acquire(self): - """ Produce a trigger which fires when the lock is acquired. """ - trig = _Lock(self) - self._pending_unprimed.append(trig) - return trig - - def release(self): - """Release the lock.""" - if not self.locked: - raise TriggerException("Attempt to release an unacquired Lock %s" % - (str(self))) - - self.locked = False - - # nobody waiting for this lock - if not self._pending_primed: - return - - trigger = self._pending_primed.pop(0) - self.locked = True - trigger() - - def __repr__(self): - if self.name is None: - fmt = "<{0} [{2} waiting] at {3}>" - else: - fmt = "<{0} for {1} [{2} waiting] at {3}>" - return fmt.format( - type(self).__qualname__, self.name, len(self._pending_primed), - _pointer_str(self) - ) - - def __bool__(self): - """Provide boolean of a Lock""" - return self.locked - - async def __aenter__(self): - return await self.acquire() - - async def __aexit__(self, exc_type, exc, tb): - self.release() - - -class NullTrigger(Trigger): - """Fires immediately. - - Primarily for internal scheduler use. - """ - - def __init__(self, name=None, outcome=None): - super(NullTrigger, self).__init__() - self._callback = None - self.name = name - self.__outcome = outcome - - @property - def _outcome(self): - if self.__outcome is not None: - return self.__outcome - return super(NullTrigger, self)._outcome - - def prime(self, callback): - callback(self) - - def __repr__(self): - if self.name is None: - fmt = "<{0} at {2}>" - else: - fmt = "<{0} for {1} at {2}>" - return fmt.format(type(self).__qualname__, self.name, _pointer_str(self)) - - -class Join(PythonTrigger, metaclass=_ParameterizedSingletonAndABC): - r"""Fires when a :func:`~cocotb.fork`\ ed coroutine completes. - - The result of blocking on the trigger can be used to get the coroutine - result:: - - async def coro_inner(): - await Timer(1, units='ns') - return "Hello world" - - task = cocotb.fork(coro_inner()) - result = await Join(task) - assert result == "Hello world" - - If the coroutine threw an exception, the :keyword:`await` will re-raise it. - - """ - __slots__ = ('_coroutine',) - - @classmethod - def __singleton_key__(cls, coroutine): - return coroutine - - def __init__(self, coroutine): - super(Join, self).__init__() - self._coroutine = coroutine - - @property - def _outcome(self): - return self._coroutine._outcome - - @property - def retval(self): - """The return value of the joined coroutine. - - .. note:: - Typically there is no need to use this attribute - the - following code samples are equivalent:: - - forked = cocotb.fork(mycoro()) - j = Join(forked) - await j - result = j.retval - - :: - - forked = cocotb.fork(mycoro()) - result = await Join(forked) - """ - return self._coroutine.retval - - def prime(self, callback): - if self._coroutine._finished: - callback(self) - else: - super(Join, self).prime(callback) - - def __repr__(self): - return "{}({!s})".format(type(self).__qualname__, self._coroutine) - - -class Waitable(Awaitable): - """ - Base class for trigger-like objects implemented using coroutines. - - This converts a `_wait` abstract method into a suitable `__await__`. - """ - __slots__ = () - - async def _wait(self): - """ - Should be implemented by the sub-class. Called by `await self` to - convert the waitable object into a coroutine. - """ - raise NotImplementedError - - def __await__(self): - return self._wait().__await__() - - -class _AggregateWaitable(Waitable): - """ - Base class for Waitables that take mutiple triggers in their constructor - """ - __slots__ = ('triggers',) - - def __init__(self, *triggers): - self.triggers = tuple(triggers) - - # Do some basic type-checking up front, rather than waiting until we - # await them. - allowed_types = (Trigger, Waitable, cocotb.decorators.RunningTask) - for trigger in self.triggers: - if not isinstance(trigger, allowed_types): - raise TypeError( - "All triggers must be instances of Trigger! Got: {}" - .format(type(trigger).__qualname__) - ) - - def __repr__(self): - # no _pointer_str here, since this is not a trigger, so identity - # doesn't matter. - return "{}({})".format( - type(self).__qualname__, - ", ".join( - repr(Join(t)) if isinstance(t, cocotb.decorators.RunningTask) - else repr(t) - for t in self.triggers - ) - ) - - -async def _wait_callback(trigger, callback): - """ - Wait for a trigger, and call `callback` with the outcome of the await. - """ - trigger = cocotb.scheduler._trigger_from_any(trigger) - try: - ret = outcomes.Value(await trigger) - except BaseException as exc: - # hide this from the traceback - ret = outcomes.Error(remove_traceback_frames(exc, ['_wait_callback'])) - callback(ret) - - -class Combine(_AggregateWaitable): - """ - Fires when all of *triggers* have fired. - - Like most triggers, this simply returns itself. - - This is similar to Verilog's ``join``. - """ - __slots__ = () - - async def _wait(self): - waiters = [] - e = _InternalEvent(self) - triggers = list(self.triggers) - - # start a parallel task for each trigger - for t in triggers: - # t=t is needed for the closure to bind correctly - def on_done(ret, t=t): - triggers.remove(t) - if not triggers: - e.set() - ret.get() # re-raise any exception - waiters.append(cocotb.fork(_wait_callback(t, on_done))) - - # wait for the last waiter to complete - await e - return self - - -class First(_AggregateWaitable): - """ - Fires when the first trigger in *triggers* fires. - - Returns the result of the trigger that fired. - - This is similar to Verilog's ``join_any``. - - .. note:: - The event loop is single threaded, so while events may be simultaneous - in simulation time, they can never be simultaneous in real time. - For this reason, the value of ``t_ret is t1`` in the following example - is implementation-defined, and will vary by simulator:: - - t1 = Timer(10, units='ps') - t2 = Timer(10, units='ps') - t_ret = await First(t1, t2) - - .. note:: - In the old-style :ref:`generator-based coroutines `, ``t = yield [a, b]`` was another spelling of - ``t = yield First(a, b)``. This spelling is no longer available when using :keyword:`await`-based - coroutines. - """ - __slots__ = () - - async def _wait(self): - waiters = [] - e = _InternalEvent(self) - triggers = list(self.triggers) - completed = [] - # start a parallel task for each trigger - for t in triggers: - def on_done(ret): - completed.append(ret) - e.set() - waiters.append(cocotb.fork(_wait_callback(t, on_done))) - - # wait for a waiter to complete - await e - - # kill all the other waiters - # TODO: Should this kill the coroutines behind any Join triggers? - # Right now it does not. - for w in waiters: - w.kill() - - # These lines are the way they are to make tracebacks readable: - # - The comment helps the user understand why they are seeing the - # traceback, even if it is obvious top cocotb maintainers. - # - Using `NullTrigger` here instead of `result = completed[0].get()` - # means we avoid inserting an `outcome.get` frame in the traceback - first_trigger = NullTrigger(outcome=completed[0]) - return await first_trigger # the first of multiple triggers that fired - - -class ClockCycles(Waitable): - """Fires after *num_cycles* transitions of *signal* from ``0`` to ``1``.""" - - def __init__(self, signal, num_cycles, rising=True): - """ - Args: - signal: The signal to monitor. - num_cycles (int): The number of cycles to count. - rising (bool, optional): If ``True``, the default, count rising edges. - Otherwise, count falling edges. - """ - self.signal = signal - self.num_cycles = num_cycles - if rising is True: - self._type = RisingEdge - else: - self._type = FallingEdge - - async def _wait(self): - trigger = self._type(self.signal) - for _ in range(self.num_cycles): - await trigger - return self - - def __repr__(self): - # no _pointer_str here, since this is not a trigger, so identity - # doesn't matter. - if self._type is RisingEdge: - fmt = "{}({!r}, {!r})" - else: - fmt = "{}({!r}, {!r}, rising=False)" - return fmt.format(type(self).__qualname__, self.signal, self.num_cycles) - - -async def with_timeout(trigger, timeout_time, timeout_unit="step"): - """ - Waits on triggers, throws an exception if it waits longer than the given time. - - Usage: - - .. code-block:: python - - await with_timeout(coro, 100, 'ns') - await with_timeout(First(coro, event.wait()), 100, 'ns') - - Args: - trigger (:class:`~cocotb.triggers.Trigger` or :class:`~cocotb.triggers.Waitable` or :class:`~cocotb.decorators.RunningTask`): - A single object that could be right of an :keyword:`await` expression in cocotb. - timeout_time (numbers.Real or decimal.Decimal): - Simulation time duration before timeout occurs. - timeout_unit (str, optional): - Units of timeout_time, accepts any units that :class:`~cocotb.triggers.Timer` does. - - Returns: - First trigger that completed if timeout did not occur. - - Raises: - :exc:`SimTimeoutError`: If timeout occurs. - - .. versionadded:: 1.3 - - .. deprecated:: 1.5 - Using None as the the *timeout_unit* argument is deprecated, use ``'step'`` instead. - """ - - if timeout_unit is None: - warnings.warn( - 'Using timeout_unit=None is deprecated, use timeout_unit="step" instead.', - DeprecationWarning, stacklevel=2) - timeout_unit="step" # don't propagate deprecated value - timeout_timer = cocotb.triggers.Timer(timeout_time, timeout_unit) - res = await First(timeout_timer, trigger) - if res is timeout_timer: - raise cocotb.result.SimTimeoutError - else: - return res diff --git a/cocotb/utils.py b/cocotb/utils.py deleted file mode 100644 index 54caabea..00000000 --- a/cocotb/utils.py +++ /dev/null @@ -1,629 +0,0 @@ -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -"""Collection of handy functions.""" - -import ctypes -import inspect -import math -import os -import sys -import traceback -import weakref -import functools -import warnings - -from cocotb import simulator - - -def _get_simulator_precision(): - # cache and replace this function - precision = simulator.get_precision() - global _get_simulator_precision - _get_simulator_precision = precision.__int__ - return _get_simulator_precision() - - -def get_python_integer_types(): - warnings.warn( - "This is an internal cocotb function, use six.integer_types instead", - DeprecationWarning) - return (int,) - - -# Simulator helper functions -def get_sim_time(units=None): - """Retrieves the simulation time from the simulator. - - Args: - units (str or None, optional): String specifying the units of the result - (one of ``None``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``). - ``None`` will return the raw simulation time. - - Returns: - The simulation time in the specified units. - """ - timeh, timel = simulator.get_sim_time() - - result = (timeh << 32 | timel) - - if units is not None: - result = get_time_from_sim_steps(result, units) - - return result - - -def _ldexp10(frac, exp): - """ Like math.ldexp, but base 10 """ - # using * or / separately prevents rounding errors if `frac` is a - # high-precision type - if exp > 0: - return frac * (10 ** exp) - else: - return frac / (10 ** -exp) - - -def get_time_from_sim_steps(steps, units): - """Calculates simulation time in the specified *units* from the *steps* based - on the simulator precision. - - Args: - steps (int): Number of simulation steps. - units (str): String specifying the units of the result - (one of ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``). - - Returns: - The simulation time in the specified units. - """ - return _ldexp10(steps, _get_simulator_precision() - _get_log_time_scale(units)) - - -def get_sim_steps(time, units="step"): - """Calculates the number of simulation time steps for a given amount of *time*. - - Args: - time (numbers.Real or decimal.Decimal): The value to convert to simulation time steps. - units (str, optional): String specifying the units of the result - (one of ``'step'``, ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``). - ``'step'`` means time is already in simulation time steps. - - Returns: - int: The number of simulation time steps. - - Raises: - :exc:`ValueError`: If given *time* cannot be represented by simulator precision. - - .. versionchanged:: 1.5 - Support ``'step'`` as the the *units* argument to mean "simulator time step". - """ - result = time - if units not in (None, "step"): - result = _ldexp10(result, _get_log_time_scale(units) - _get_simulator_precision()) - if units is None: - warnings.warn( - 'Using units=None is deprecated, use units="step" instead.', - DeprecationWarning, stacklevel=2) - units="step" # don't propagate deprecated value - - result_rounded = math.floor(result) - - if result_rounded != result: - raise ValueError("Unable to accurately represent {0}({1}) with the " - "simulator precision of 1e{2}".format( - time, units, _get_simulator_precision())) - - return int(result_rounded) - - -def _get_log_time_scale(units): - """Retrieves the ``log10()`` of the scale factor for a given time unit. - - Args: - units (str): String specifying the units - (one of ``'fs'``, ``'ps'``, ``'ns'``, ``'us'``, ``'ms'``, ``'sec'``). - - Returns: - The the ``log10()`` of the scale factor for the time unit. - """ - scale = { - 'fs' : -15, - 'ps' : -12, - 'ns' : -9, - 'us' : -6, - 'ms' : -3, - 'sec': 0} - - units_lwr = units.lower() - if units_lwr not in scale: - raise ValueError("Invalid unit ({0}) provided".format(units)) - else: - return scale[units_lwr] - -# Ctypes helper functions - - -def pack(ctypes_obj): - """Convert a :mod:`ctypes` structure into a Python string. - - Args: - ctypes_obj (ctypes.Structure): The :mod:`ctypes` structure to convert to a string. - - Returns: - New Python string containing the bytes from memory holding *ctypes_obj*. - - .. deprecated:: 1.5 - This function is deprecated, use ``bytes(ctypes_obj)`` instead. - """ - warnings.warn( - "This function is deprecated and will be removed, use ``bytes(ctypes_obj)`` instead.", - DeprecationWarning, stacklevel=2) - return ctypes.string_at(ctypes.addressof(ctypes_obj), - ctypes.sizeof(ctypes_obj)) - - -def unpack(ctypes_obj, string, bytes=None): - """Unpack a Python string into a :mod:`ctypes` structure. - - If the length of *string* is not the correct size for the memory - footprint of the :mod:`ctypes` structure then the *bytes* keyword argument - must be used. - - Args: - ctypes_obj (ctypes.Structure): The :mod:`ctypes` structure to pack into. - string (str): String to copy over the *ctypes_obj* memory space. - bytes (int, optional): Number of bytes to copy. - Defaults to ``None``, meaning the length of *string* is used. - - Raises: - :exc:`ValueError`: If length of *string* and size of *ctypes_obj* - are not equal. - :exc:`MemoryError`: If *bytes* is longer than size of *ctypes_obj*. - - .. deprecated:: 1.5 - Converting bytes to a ctypes object should be done with :meth:`~ctypes._CData.from_buffer_copy`. - If you need to assign bytes into an *existing* ctypes object, use ``memoryview(ctypes_obj).cast('B')[:bytes] = string``, - see :class:`memoryview` for details. - """ - warnings.warn( - "This function is being removed, use ``memoryview(ctypes_obj).cast('B')[:bytes] = string`` instead.", - DeprecationWarning, stacklevel=2) - if bytes is None: - if len(string) != ctypes.sizeof(ctypes_obj): - raise ValueError("Attempt to unpack a string of size %d into a \ - struct of size %d" % (len(string), ctypes.sizeof(ctypes_obj))) - bytes = len(string) - - if bytes > ctypes.sizeof(ctypes_obj): - raise MemoryError("Attempt to unpack %d bytes over an object \ - of size %d" % (bytes, ctypes.sizeof(ctypes_obj))) - - ctypes.memmove(ctypes.addressof(ctypes_obj), string, bytes) - - -import cocotb.ANSI as ANSI - - -# A note on the use of latin1 in the deprecations below: -# Latin1 is the only encoding `e` that satisfies -# `all(chr(x).encode(e) == bytes([x]) for x in range(255))` -# Our use of `ord` and `chr` throughout other bits of code make this the most -# compatible choice of encoding. Under this convention, old code can be upgraded -# by changing "binary" strings from `"\x12\x34"` to `b"\x12\x34"`. - - -def _sane(x: bytes) -> str: - r = "" - for j in x: - if (j < 32) or (j >= 127): - r += "." - else: - r += chr(j) - return r - - -def hexdump(x: bytes) -> str: - """Hexdump a buffer. - - Args: - x: Object that supports conversion to :class:`bytes`. - - Returns: - A string containing the hexdump. - - .. deprecated:: 1.4 - Passing a :class:`str` to this function is deprecated, as it - is not an appropriate type for binary data. Doing so anyway - will encode the string to latin1. - - Example: - >>> print(hexdump(b'this somewhat long string')) - 0000 74 68 69 73 20 73 6F 6D 65 77 68 61 74 20 6C 6F this somewhat lo - 0010 6E 67 20 73 74 72 69 6E 67 ng string - - """ - # adapted from scapy.utils.hexdump - rs = "" - if isinstance(x, str): - warnings.warn( - "Passing a string to hexdump is deprecated, pass bytes instead", - DeprecationWarning, stacklevel=2) - x = x.encode('latin1') - x = b"%b" % x - l = len(x) - i = 0 - while i < l: - rs += "%04x " % i - for j in range(16): - if i + j < l: - rs += "%02X " % x[i + j] - else: - rs += " " - if j % 16 == 7: - rs += "" - rs += " " - rs += _sane(x[i:i + 16]) + "\n" - i += 16 - return rs - - -def hexdiffs(x: bytes, y: bytes) -> str: - r"""Return a diff string showing differences between two binary strings. - - Args: - x: Object that supports conversion to :class:`bytes`. - y: Object that supports conversion to :class:`bytes`. - - .. deprecated:: 1.4 - Passing :class:`str`\ s to this function is deprecated, as it - is not an appropriate type for binary data. Doing so anyway - will encode the string to latin1. - - Example: - >>> print(hexdiffs(b'a', b'b')) - 0000 61 a - 0000 62 b - - >>> print(hexdiffs(b'this short thing', b'this also short')) - 0000 746869732073686F 7274207468696E67 this short thing - 0000 7468697320616C73 6F 2073686F7274 this also short - - """ - # adapted from scapy.utils.hexdiff - - def highlight(string: str, colour=ANSI.COLOR_HILITE_HEXDIFF_DEFAULT) -> str: - """Highlight with ANSI colors if possible/requested and not running in GUI.""" - - if want_color_output(): - return colour + string + ANSI.COLOR_DEFAULT - else: - return string - - x_is_str = isinstance(x, str) - y_is_str = isinstance(y, str) - if x_is_str or y_is_str: - warnings.warn( - "Passing strings to hexdiffs is deprecated, pass bytes instead", - DeprecationWarning, stacklevel=2) - if x_is_str: - x = x.encode('latin1') - if y_is_str: - y = y.encode('latin1') - - rs = "" - - x = (b'%b' % x)[::-1] - y = (b'%b' % y)[::-1] - SUBST = 1 - INSERT = 1 - d = {} - d[-1, -1] = 0, (-1, -1) - for j in range(len(y)): - d[-1, j] = d[-1, j - 1][0] + INSERT, (-1, j - 1) - for i in range(len(x)): - d[i, -1] = d[i - 1, -1][0] + INSERT, (i - 1, -1) - - for j in range(len(y)): - for i in range(len(x)): - d[i, j] = min((d[i-1, j-1][0] + SUBST*(x[i] != y[j]), (i-1, j-1)), - (d[i - 1, j][0] + INSERT, (i - 1, j)), - (d[i, j - 1][0] + INSERT, (i, j - 1))) - - backtrackx = [] - backtracky = [] - i = len(x) - 1 - j = len(y) - 1 - while not (i == j == -1): - i2, j2 = d[i, j][1] - backtrackx.append(x[i2+1:i+1]) - backtracky.append(y[j2+1:j+1]) - i, j = i2, j2 - - x = y = i = 0 - colorize = { 0: lambda x: x, # noqa - -1: lambda x: x, # noqa - 1: lambda x: x} # noqa - - dox = 1 - doy = 0 - l = len(backtrackx) - while i < l: - linex = backtrackx[i:i+16] - liney = backtracky[i:i+16] - xx = sum(len(k) for k in linex) - yy = sum(len(k) for k in liney) - if dox and not xx: - dox = 0 - doy = 1 - if dox and linex == liney: - doy = 1 - - if dox: - xd = y - j = 0 - while not linex[j]: - j += 1 - xd -= 1 - if dox != doy: - rs += highlight("%04x" % xd) + " " - else: - rs += highlight("%04x" % xd, colour=ANSI.COLOR_HILITE_HEXDIFF_1) + " " - x += xx - line = linex - else: - rs += " " - if doy: - yd = y - j = 0 - while not liney[j]: - j += 1 - yd -= 1 - if doy - dox != 0: - rs += " " + highlight("%04x" % yd) - else: - rs += highlight("%04x" % yd, colour=ANSI.COLOR_HILITE_HEXDIFF_1) - y += yy - line = liney - else: - rs += " " - - rs += " " - - cl = "" - for j in range(16): - if i + j < l: - if line[j]: - char_j, = line[j] - if linex[j] != liney[j]: - rs += highlight("%02X" % char_j, - colour=ANSI.COLOR_HILITE_HEXDIFF_2) - else: - rs += "%02X" % char_j - if linex[j] == liney[j]: - cl += highlight(_sane(line[j]), - colour=ANSI.COLOR_HILITE_HEXDIFF_3) - else: - cl += highlight(_sane(line[j]), - colour=ANSI.COLOR_HILITE_HEXDIFF_4) - else: - rs += " " - cl += " " - else: - rs += " " - if j == 7: - rs += " " - - rs += " " + cl + '\n' - - if doy or not yy: - doy = 0 - dox = 1 - i += 16 - else: - if yy: - dox = 0 - doy = 1 - else: - i += 16 - return rs - - -class ParametrizedSingleton(type): - """A metaclass that allows class construction to reuse an existing instance. - - We use this so that :class:`RisingEdge(sig) ` and :class:`Join(coroutine) ` always return - the same instance, rather than creating new copies. - """ - - def __init__(cls, *args, **kwargs): - # Attach a lookup table to this class. - # Weak such that if the instance is no longer referenced, it can be - # collected. - cls.__instances = weakref.WeakValueDictionary() - - def __singleton_key__(cls, *args, **kwargs): - """Convert the construction arguments into a normalized representation that - uniquely identifies this singleton. - """ - # Could default to something like this, but it would be slow - # return tuple(inspect.Signature(cls).bind(*args, **kwargs).arguments.items()) - raise NotImplementedError - - def __call__(cls, *args, **kwargs): - key = cls.__singleton_key__(*args, **kwargs) - try: - return cls.__instances[key] - except KeyError: - # construct the object as normal - self = super(ParametrizedSingleton, cls).__call__(*args, **kwargs) - cls.__instances[key] = self - return self - - @property - def __signature__(cls): - return inspect.signature(cls.__singleton_key__) - - -def reject_remaining_kwargs(name, kwargs): - """ - Helper function to emulate Python 3 keyword-only arguments. - - Use as:: - - def func(x1, **kwargs): - a = kwargs.pop('a', 1) - b = kwargs.pop('b', 2) - reject_remaining_kwargs('func', kwargs) - ... - - To emulate the Python 3 syntax:: - - def func(x1, *, a=1, b=2): - ... - - .. deprecated:: 1.4 - Since the minimum supported Python version is now 3.5, this function - is not needed. - """ - warnings.warn( - "reject_remaining_kwargs is deprecated and will be removed, use " - "Python 3 keyword-only arguments directly.", DeprecationWarning, - stacklevel=2) - if kwargs: - # match the error message to what Python 3 produces - bad_arg = next(iter(kwargs)) - raise TypeError( - '{}() got an unexpected keyword argument {!r}'.format(name, bad_arg) - ) - - -class lazy_property: - """ - A property that is executed the first time, then cached forever. - - It does this by replacing itself on the instance, which works because - unlike `@property` it does not define __set__. - - This should be used for expensive members of objects that are not always - used. - """ - - def __init__(self, fget): - self.fget = fget - - # copy the getter function's docstring and other attributes - functools.update_wrapper(self, fget) - - def __get__(self, obj, cls): - if obj is None: - return self - - value = self.fget(obj) - setattr(obj, self.fget.__qualname__, value) - return value - - -def want_color_output(): - """Return ``True`` if colored output is possible/requested and not running in GUI. - - Colored output can be explicitly requested by setting :envvar:`COCOTB_ANSI_OUTPUT` to ``1``. - """ - want_color = sys.stdout.isatty() # default to color for TTYs - if os.getenv("NO_COLOR") is not None: - want_color = False - if os.getenv("COCOTB_ANSI_OUTPUT", default='0') == '1': - want_color = True - if os.getenv("GUI", default='0') == '1': - want_color = False - return want_color - - -def remove_traceback_frames(tb_or_exc, frame_names): - """ - Strip leading frames from a traceback - - Args: - tb_or_exc (Union[traceback, BaseException, exc_info]): - Object to strip frames from. If an exception is passed, creates - a copy of the exception with a new shorter traceback. If a tuple - from `sys.exc_info` is passed, returns the same tuple with the - traceback shortened - frame_names (List[str]): - Names of the frames to strip, which must be present. - """ - # self-invoking overloads - if isinstance(tb_or_exc, BaseException): - exc = tb_or_exc - return exc.with_traceback( - remove_traceback_frames(exc.__traceback__, frame_names) - ) - elif isinstance(tb_or_exc, tuple): - exc_type, exc_value, exc_tb = tb_or_exc - exc_tb = remove_traceback_frames(exc_tb, frame_names) - return exc_type, exc_value, exc_tb - # base case - else: - tb = tb_or_exc - for frame_name in frame_names: - assert tb.tb_frame.f_code.co_name == frame_name - tb = tb.tb_next - return tb - - -def walk_coro_stack(coro): - """Walk down the coroutine stack, starting at *coro*. - - Supports coroutines and generators. - """ - while coro is not None: - try: - f = getattr(coro, 'cr_frame') - coro = coro.cr_await - except AttributeError: - try: - f = getattr(coro, 'gi_frame') - coro = coro.gi_yieldfrom - except AttributeError: - f = None - coro = None - if f is not None: - yield (f, f.f_lineno) - - -def extract_coro_stack(coro, limit=None): - """Create a list of pre-processed entries from the coroutine stack. - - This is based on :func:`traceback.extract_tb`. - - If *limit* is omitted or ``None``, all entries are extracted. - The list is a :class:`traceback.StackSummary` object, and - each entry in the list is a :class:`traceback.FrameSummary` object - containing attributes ``filename``, ``lineno``, ``name``, and ``line`` - representing the information that is usually printed for a stack - trace. The line is a string with leading and trailing - whitespace stripped; if the source is not available it is ``None``. - """ - return traceback.StackSummary.extract(walk_coro_stack(coro), limit=limit) diff --git a/cocotb/wavedrom.py b/cocotb/wavedrom.py deleted file mode 100644 index 7b21af2c..00000000 --- a/cocotb/wavedrom.py +++ /dev/null @@ -1,202 +0,0 @@ -# Copyright (c) 2014 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import json -from collections import OrderedDict, defaultdict - -import cocotb -from cocotb.bus import Bus -from cocotb.triggers import RisingEdge, ReadOnly - - -class Wavedrom: - """Base class for a WaveDrom compatible tracer.""" - - def __init__(self, obj): - - self._hdls = OrderedDict() - if isinstance(obj, Bus): - for name in sorted(obj._signals.keys()): - self._hdls[name] = obj._signals[name] - self._name = obj._name - else: - self._hdls[obj._name.split(".")[-1]] = obj - - self.clear() - - def sample(self): - """Record a sample of the signal value at this point in time.""" - - def _lastval(samples): - for x in range(len(samples)-1, -1, -1): - if samples[x] not in "=.|": - return samples[x] - return None - - for name, hdl in self._hdls.items(): - val = hdl.value - valstr = val.binstr.lower() - - # Decide what character to use to represent this signal - if len(valstr) == 1: - char = valstr - elif "x" in valstr: - char = "x" - elif "u" in valstr: - char = "u" - elif "z" in valstr: - char = "z" - else: - if (len(self._data[name]) and - self._data[name][-1] == int(val) and - self._samples[name][-1] in "=."): - char = "." - else: - char = "=" - self._data[name].append(int(val)) - - # Detect if this is unchanged - if len(valstr) == 1 and char == _lastval(self._samples[name]): - char = "." - self._samples[name].append(char) - - def clear(self): - """Delete all sampled data.""" - self._samples = defaultdict(list) - self._data = defaultdict(list) - - def gap(self): - for name, hdl in self._hdls.items(): - self._samples[name].append("|") - - def get(self, add_clock=True): - """Return the samples as a list suitable for use with WaveDrom.""" - siglist = [] - traces = [] - for name in self._hdls.keys(): - samples = self._samples[name] - traces.append({"name": name, "wave": "".join(samples)}) - if name in self._data: - traces[-1]["data"] = " ".join([repr(s) for s in self._data[name]]) - - if len(traces) > 1: - traces.insert(0, self._name) - siglist.append(traces) - else: - siglist.append(traces[0]) - - if add_clock: - tracelen = len(traces[-1]["wave"]) - siglist.insert(0, {"name": "clk", "wave": "p" + "."*(tracelen-1)}) - - return siglist - - -class trace: - """Context manager to enable tracing of signals. - - Arguments are an arbitrary number of signals or buses to trace. - We also require a clock to sample on, passed in as a keyword argument. - - Usage:: - - with trace(sig1, sig2, a_bus, clk=clk) as waves: - # Stuff happens, we trace it - - # Dump to JSON format compatible with WaveDrom - j = waves.dumpj() - """ - - def __init__(self, *args, clk=None): - self._clock = clk - self._signals = [] - for arg in args: - self._signals.append(Wavedrom(arg)) - self._coro = None - self._clocks = 0 - self._enabled = False - - if self._clock is None: - raise ValueError("Trace requires a clock to sample") - - async def _monitor(self): - self._clocks = 0 - while True: - await RisingEdge(self._clock) - await ReadOnly() - if not self._enabled: - continue - self._clocks += 1 - for sig in self._signals: - sig.sample() - - def insert_gap(self): - self._clocks += 1 - for sig in self._signals: - sig.gap() - - def disable(self): - self._enabled = False - - def enable(self): - self._enabled = True - - def __enter__(self): - for sig in self._signals: - sig.clear() - self.enable() - self._coro = cocotb.fork(self._monitor()) - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self._coro.kill() - for sig in self._signals: - sig.clear() - self.disable() - return None - - def write(self, filename, **kwargs): - with open(filename, "w") as f: - f.write(self.dumpj(**kwargs)) - - def dumpj(self, header="", footer="", config=""): - trace = {"signal": []} - trace["signal"].append( - {"name": "clock", "wave": "p" + "."*(self._clocks-1)}) - for sig in self._signals: - trace["signal"].extend(sig.get(add_clock=False)) - if header: - if isinstance(header, dict): - trace["head"] = header - else: - trace["head"] = {"text": header} - if footer: - if isinstance(footer, dict): - trace["foot"] = footer - else: - trace["foot"] = {"text": footer} - if config: - trace["config"] = config - return json.dumps(trace, indent=4, sort_keys=False) diff --git a/cocotb/xunit_reporter.py b/cocotb/xunit_reporter.py deleted file mode 100644 index cb08e487..00000000 --- a/cocotb/xunit_reporter.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -from xml.etree.ElementTree import Element, SubElement -import xml.etree.ElementTree as ET - - -class XUnitReporter: - - def __init__(self, filename="results.xml"): - self.results = Element("testsuites", name="results") - self.filename = filename - - def add_testsuite(self, **kwargs): - self.last_testsuite = SubElement(self.results, "testsuite", **kwargs) - return self.last_testsuite - - def add_testcase(self, testsuite=None, **kwargs): - if testsuite is None: - testsuite = self.last_testsuite - self.last_testcase = SubElement(testsuite, "testcase", **kwargs) - return self.last_testcase - - def add_property(self, testsuite=None, **kwargs): - if testsuite is None: - testsuite = self.last_testsuite - self.last_property = SubElement(testsuite, "property", **kwargs) - return self.last_property - - def add_failure(self, testcase=None, **kwargs): - if testcase is None: - testcase = self.last_testcase - SubElement(testcase, "failure", **kwargs) - - def add_skipped(self, testcase=None, **kwargs): - if testcase is None: - testcase = self.last_testcase - SubElement(testcase, "skipped", **kwargs) - - def indent(self, elem, level=0): - i = "\n" + level*" " - if len(elem): - if not elem.text or not elem.text.strip(): - elem.text = i + " " - if not elem.tail or not elem.tail.strip(): - elem.tail = i - for elem in elem: - self.indent(elem, level+1) - if not elem.tail or not elem.tail.strip(): - elem.tail = i - else: - if level and (not elem.tail or not elem.tail.strip()): - elem.tail = i - - def write(self): - self.indent(self.results) - ET.ElementTree(self.results).write(self.filename, encoding="UTF-8") diff --git a/cocotb_build_libs.py b/cocotb_build_libs.py deleted file mode 100755 index fb987721..00000000 --- a/cocotb_build_libs.py +++ /dev/null @@ -1,705 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause - -import os -import sys -import sysconfig -import logging -import distutils -import subprocess -import textwrap - -from setuptools import Extension -from distutils.spawn import find_executable -from setuptools.command.build_ext import build_ext as _build_ext -from distutils.file_util import copy_file -from typing import List - - -logger = logging.getLogger(__name__) -cocotb_share_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "cocotb", "share")) - - -def create_sxs_assembly_manifest(name: str, filename: str, libraries: List[str], dependency_only=False) -> str: - """ - Create side-by-side (sxs) assembly manifest - - It contains dependencies to other assemblies (in our case the assemblies are equal to the other libraries). - For more details see: - - https://docs.microsoft.com/en-us/windows/win32/sbscs/assembly-manifests - - https://docs.microsoft.com/en-us/windows/win32/sbscs/using-side-by-side-assemblies - - Args: - name: The name of the assembly for which the manifest is generated, e.g. ``libcocotbutils``. - filename: The filename of the library, e.g. ``libcocotbutils.dll``. - libraries: A list of names of dependent manifests, e.g. ``["libgpilog"]``. - """ - - architecture = "amd64" if sys.maxsize > 2**32 else "x86" - dependencies = [] - - for lib in libraries: - dependencies.append(textwrap.dedent('''\ - - - - - - ''') % (lib, architecture)) - - if not dependency_only: - manifest_body = textwrap.dedent('''\ - - - - - %s - - ''') % (name, architecture, filename, textwrap.indent("".join(dependencies), ' ').strip()) - else: - manifest_body = textwrap.dedent('''\ - - - %s - - ''') % (textwrap.indent("".join(dependencies), ' ').strip()) - - return manifest_body - - -def create_sxs_appconfig(filename): - """ - Create side-by-side (sxs) application configuration file. - - The application configuration specifies additional search paths for manifests. - For more details see: https://docs.microsoft.com/en-us/windows/win32/sbscs/application-configuration-files - """ - - config_body = textwrap.dedent('''\ - - - - - - - - - ''') - - dirpath = os.path.dirname(filename) - os.makedirs(dirpath, exist_ok=True) - - with open(filename + ".2.config", "w", encoding='utf-8') as f: - f.write(config_body) - - -def create_rc_file(name, filename, libraries): - """ - Creates windows resource definition script to embed the side-by-side assembly manifest into the libraries. - - For more details see: https://docs.microsoft.com/en-us/windows/win32/menurc/about-resource-files - """ - - manifest = create_sxs_assembly_manifest(name, filename, libraries) - - # Escape double quotes and put every line between double quotes for embedding into rc file - manifest = manifest.replace('"', '""') - manifest = '\n'.join(['"%s\\r\\n"' % x for x in manifest.splitlines()]) - - rc_body = textwrap.dedent('''\ - #pragma code_page(65001) // UTF-8 - #include - - LANGUAGE 0x00, 0x00 - - ISOLATIONAWARE_MANIFEST_RESOURCE_ID RT_MANIFEST - BEGIN - %s - END - ''') % manifest - - # Add the runtime dependency on libcocotb to libembed dependencies - if name == "libembed": - manifest = create_sxs_assembly_manifest(name, filename, ["libcocotb"], dependency_only=True) - - # Escape double quotes and put every line between double quotes for embedding into rc file - manifest = manifest.replace('"', '""') - manifest = '\n'.join(['"%s\\r\\n"' % x for x in manifest.splitlines()]) - - rc_body += textwrap.dedent('''\ - 1000 RT_MANIFEST - BEGIN - %s - END - ''') % manifest - - with open(name + ".rc", "w", encoding='utf-8') as f: - f.write(rc_body) - - -def _get_lib_ext_name(): - """ Get name of default library file extension on given OS. """ - - if os.name == "nt": - ext_name = "dll" - else: - ext_name = "so" - - return ext_name - - -class build_ext(_build_ext): - def run(self): - - def_dir = os.path.join(cocotb_share_dir, "def") - self._gen_import_libs(def_dir) - - if os.name == "nt": - create_sxs_appconfig(self.get_ext_fullpath(os.path.join("cocotb", "simulator"))) - - ext_names = {os.path.split(ext.name)[-1] for ext in self.extensions} - for ext in self.extensions: - fullname = self.get_ext_fullname(ext.name) - filename = self.get_ext_filename(fullname) - name = os.path.split(fullname)[-1] - filename = os.path.split(filename)[-1] - libraries = {"lib" + lib for lib in ext.libraries}.intersection(ext_names) - create_rc_file(name, filename, libraries) - - super().run() - - def build_extension(self, ext): - """Build each extension in its own temp directory to make gcov happy. - - A normal PEP 517 install still works as the temp directories are discarded anyway. - """ - old_build_temp = self.build_temp - self.build_temp = os.path.join(self.build_temp, ext.name) - super().build_extension(ext) - self.build_temp = old_build_temp - - # Needed for Windows to not assume python module (generate interface in def file) - def get_export_symbols(self, ext): - return None - - # For proper cocotb library naming, based on https://github.com/cython/cython/issues/1740 - def get_ext_filename(self, ext_name): - """ - Like the base class method, but for libraries that are not python extension: - - removes the ``.cpython-36m-x86_64-linux-gnu.`` or ``-cpython-36m.`` part before the extension - - replaces ``.pyd`` with ``.dll`` on windows. - """ - - filename = _build_ext.get_ext_filename(self, ext_name) - - # for the simulator python extension library, leaving suffix in place - if "simulator" == os.path.split(ext_name)[-1]: - return filename - - head, tail = os.path.split(filename) - tail_split = tail.split(".") - - # mingw on msys2 uses `-` as seperator - tail_split = tail_split[0].split("-") - - filename_short = os.path.join(head, tail_split[0] + "." + _get_lib_ext_name()) - - # icarus requires vpl extension - filename_short = filename_short.replace("libcocotbvpi_icarus.so", "libcocotbvpi_icarus.vpl") - filename_short = filename_short.replace("libcocotbvpi_icarus.dll", "libcocotbvpi_icarus.vpl") - - return filename_short - - def finalize_options(self): - """ Like the base class method,but add extra library_dirs path. """ - - super().finalize_options() - - for ext in self.extensions: - ext.library_dirs.append(os.path.join(self.build_lib, "cocotb", "libs")) - - def copy_extensions_to_source(self): - """ Like the base class method, but copy libs into proper directory in develop. """ - - build_py = self.get_finalized_command("build_py") - for ext in self.extensions: - fullname = self.get_ext_fullname(ext.name) - filename = self.get_ext_filename(fullname) - modpath = fullname.split(".") - package = ".".join(modpath[:-1]) - package_dir = build_py.get_package_dir(package) - # unlike the method from `setuptools`, we do not call `os.path.basename` here - dest_filename = os.path.join(package_dir, filename) - src_filename = os.path.join(self.build_lib, filename) - - os.makedirs(os.path.dirname(dest_filename), exist_ok=True) - - copy_file( - src_filename, dest_filename, verbose=self.verbose, dry_run=self.dry_run - ) - if ext._needs_stub: - self.write_stub(package_dir or os.curdir, ext, True) - - def _gen_import_libs(self, def_dir): - """ - On Windows generate import libraries that contains the code required to - load the DLL (.a) based on module definition files (.def) - """ - - if os.name == "nt": - for sim in ["icarus", "modelsim", "aldec", "ghdl"]: - subprocess.run( - [ - "dlltool", - "-d", - os.path.join(def_dir, sim + ".def"), - "-l", - os.path.join(def_dir, "lib" + sim + ".a"), - ] - ) - - -def _extra_link_args(lib_name=None, rpaths=[]): - """ - Add linker argument to load dependencies from the directory where vpi/vhpi/fli library is located - On osx use `install_name`. - Use `rpath` on all platforms - """ - - args = [] - if sys.platform == "darwin" and lib_name is not None: - args += ["-Wl,-install_name,@rpath/%s.so" % lib_name] - for rpath in rpaths: - if sys.platform == "darwin": - rpath = rpath.replace("$ORIGIN", "@loader_path") - args += ["-Wl,-rpath,%s" % rpath] - if os.name == "nt": - # Align behavior of gcc with msvc and export only symbols marked with __declspec(dllexport) - args += ["-Wl,--exclude-all-symbols"] - else: - args += ["-flto"] - return args - - -def _get_python_lib_link(): - """ Get name of python library used for linking """ - - if sys.platform == "darwin": - ld_library = sysconfig.get_config_var("LIBRARY") - else: - ld_library = sysconfig.get_config_var("LDLIBRARY") - - if ld_library is not None: - python_lib_link = os.path.splitext(ld_library)[0][3:] - else: - python_version = sysconfig.get_python_version().replace(".", "") - python_lib_link = "python" + python_version - - return python_lib_link - - -def _get_python_lib(): - """ Get the library for embedded the python interpreter """ - - if os.name == "nt": - python_lib = _get_python_lib_link() + "." + _get_lib_ext_name() - elif sys.platform == "darwin": - python_lib = os.path.join(sysconfig.get_config_var("LIBDIR"), "lib" + _get_python_lib_link() + ".") - if os.path.exists(python_lib + "dylib"): - python_lib += "dylib" - else: - python_lib += "so" - else: - python_lib = "lib" + _get_python_lib_link() + "." + _get_lib_ext_name() - - return python_lib - - -# TODO [gh-1372]: make this work for MSVC which has a different flag syntax -_base_warns = ["-Wall", "-Wextra", "-Wcast-qual", "-Wwrite-strings", "-Wconversion"] -_ccx_warns = _base_warns + ["-Wnon-virtual-dtor", "-Woverloaded-virtual"] -_extra_cxx_compile_args = ["-std=c++11", "-fvisibility=hidden", "-fvisibility-inlines-hidden"] + _ccx_warns -if os.name != "nt": - _extra_cxx_compile_args += ["-flto"] - -# Make PRI* format macros available with C++11 compiler but older libc, e.g. on RHEL6. -_extra_defines = [("__STDC_FORMAT_MACROS", "")] - - -def _get_common_lib_ext(include_dir, share_lib_dir): - """ - Defines common libraries. - - All libraries go into the same directory to enable loading without modifying the library path (e.g. LD_LIBRARY_PATH). - In Makefile `LIB_DIR` (s) is used to point to this directory. - """ - - # - # libcocotbutils - # - libcocotbutils_sources = [ - os.path.join(share_lib_dir, "utils", "cocotb_utils.cpp") - ] - if os.name == "nt": - libcocotbutils_sources += ["libcocotbutils.rc"] - libcocotbutils_libraries = ["gpilog"] - if os.name != "nt": - libcocotbutils_libraries.append("dl") # dlopen, dlerror, dlsym - libcocotbutils = Extension( - os.path.join("cocotb", "libs", "libcocotbutils"), - define_macros=[("COCOTBUTILS_EXPORTS", "")] + _extra_defines, - include_dirs=[include_dir], - libraries=libcocotbutils_libraries, - sources=libcocotbutils_sources, - extra_link_args=_extra_link_args(lib_name="libcocotbutils", rpaths=["$ORIGIN"]), - extra_compile_args=_extra_cxx_compile_args, - ) - - # - # libgpilog - # - python_lib_dirs = [] - if sys.platform == "darwin": - python_lib_dirs = [sysconfig.get_config_var("LIBDIR")] - - libgpilog_sources = [ - os.path.join(share_lib_dir, "gpi_log", "gpi_logging.cpp") - ] - if os.name == "nt": - libgpilog_sources += ["libgpilog.rc"] - libgpilog = Extension( - os.path.join("cocotb", "libs", "libgpilog"), - define_macros=[("GPILOG_EXPORTS", "")] + _extra_defines, - include_dirs=[include_dir], - sources=libgpilog_sources, - extra_link_args=_extra_link_args(lib_name="libgpilog", rpaths=["$ORIGIN"]), - extra_compile_args=_extra_cxx_compile_args, - ) - - # - # libpygpilog - # - libpygpilog_sources = [ - os.path.join(share_lib_dir, "py_gpi_log", "py_gpi_logging.cpp") - ] - if os.name == "nt": - libpygpilog_sources += ["libpygpilog.rc"] - libpygpilog = Extension( - os.path.join("cocotb", "libs", "libpygpilog"), - define_macros=[("PYGPILOG_EXPORTS", "")] + _extra_defines, - include_dirs=[include_dir], - libraries=["gpilog"], - sources=libpygpilog_sources, - extra_link_args=_extra_link_args(lib_name="libpygpilog", rpaths=["$ORIGIN"]), - extra_compile_args=_extra_cxx_compile_args, - ) - - # - # libembed - # - libembed_sources = [ - os.path.join(share_lib_dir, "embed", "embed.cpp") - ] - if os.name == "nt": - libembed_sources += ["libembed.rc"] - libembed = Extension( - os.path.join("cocotb", "libs", "libembed"), - define_macros=[ - ("COCOTB_EMBED_EXPORTS", ""), - ("EMBED_IMPL_LIB", "libcocotb." + _get_lib_ext_name()), - ("PYTHON_LIB", _get_python_lib())] + _extra_defines, - include_dirs=[include_dir], - libraries=["gpilog", "cocotbutils"], - sources=libembed_sources, - extra_link_args=_extra_link_args(lib_name="libembed", rpaths=["$ORIGIN"]), - extra_compile_args=_extra_cxx_compile_args, - ) - - # - # libcocotb - # - libcocotb_sources = [ - os.path.join(share_lib_dir, "embed", "gpi_embed.cpp") - ] - if os.name == "nt": - libcocotb_sources += ["libcocotb.rc"] - libcocotb = Extension( - os.path.join("cocotb", "libs", "libcocotb"), - define_macros=_extra_defines, - include_dirs=[include_dir], - libraries=["gpilog", "cocotbutils", "pygpilog"], - sources=libcocotb_sources, - extra_link_args=_extra_link_args(lib_name="libcocotb", rpaths=["$ORIGIN"]), - extra_compile_args=_extra_cxx_compile_args, - ) - - # - # libgpi - # - libgpi_sources=[ - os.path.join(share_lib_dir, "gpi", "GpiCbHdl.cpp"), - os.path.join(share_lib_dir, "gpi", "GpiCommon.cpp"), - ] - if os.name == "nt": - libgpi_sources += ["libgpi.rc"] - libgpi = Extension( - os.path.join("cocotb", "libs", "libgpi"), - define_macros=[("GPI_EXPORTS", ""), ("LIB_EXT", _get_lib_ext_name()), ("SINGLETON_HANDLES", "")] + _extra_defines, - include_dirs=[include_dir], - libraries=["cocotbutils", "gpilog", "embed"], - sources=libgpi_sources, - extra_link_args=_extra_link_args(lib_name="libgpi", rpaths=["$ORIGIN"]), - extra_compile_args=_extra_cxx_compile_args, - ) - - # - # simulator - # - simulator_sources=[ - os.path.join(share_lib_dir, "simulator", "simulatormodule.cpp"), - ] - if os.name == "nt": - simulator_sources += ["simulator.rc"] - libsim = Extension( - os.path.join("cocotb", "simulator"), - define_macros=_extra_defines, - include_dirs=[include_dir], - libraries=["cocotbutils", "gpilog", "gpi", "pygpilog"], - library_dirs=python_lib_dirs, - sources=simulator_sources, - extra_compile_args=_extra_cxx_compile_args, - extra_link_args=_extra_link_args(rpaths=["$ORIGIN/libs"]), - ) - - # The libraries in this list are compiled in order of their appearance. - # If there is a linking dependency on one library to another, - # the linked library must be built first. - return [libgpilog, libpygpilog, libcocotbutils, libembed, libgpi, libcocotb, libsim] - - -def _get_vpi_lib_ext( - include_dir, share_lib_dir, sim_define, extra_lib=[], extra_lib_dir=[] -): - lib_name = "libcocotbvpi_" + sim_define.lower() - libcocotbvpi_sources = [ - os.path.join(share_lib_dir, "vpi", "VpiImpl.cpp"), - os.path.join(share_lib_dir, "vpi", "VpiCbHdl.cpp"), - ] - if os.name == "nt": - libcocotbvpi_sources += [lib_name + ".rc"] - libcocotbvpi = Extension( - os.path.join("cocotb", "libs", lib_name), - define_macros=[("COCOTBVPI_EXPORTS", ""), ("VPI_CHECKING", "1")] + [(sim_define, "")] + _extra_defines, - include_dirs=[include_dir], - libraries=["gpi", "gpilog"] + extra_lib, - library_dirs=extra_lib_dir, - sources=libcocotbvpi_sources, - extra_link_args=_extra_link_args(lib_name=lib_name, rpaths=["$ORIGIN"]), - extra_compile_args=_extra_cxx_compile_args, - ) - - return libcocotbvpi - - -def _get_vhpi_lib_ext( - include_dir, share_lib_dir, sim_define, extra_lib=[], extra_lib_dir=[] -): - lib_name = "libcocotbvhpi_" + sim_define.lower() - libcocotbvhpi_sources = [ - os.path.join(share_lib_dir, "vhpi", "VhpiImpl.cpp"), - os.path.join(share_lib_dir, "vhpi", "VhpiCbHdl.cpp"), - ] - if os.name == "nt": - libcocotbvhpi_sources += [lib_name + ".rc"] - libcocotbvhpi = Extension( - os.path.join("cocotb", "libs", lib_name), - include_dirs=[include_dir], - define_macros=[("COCOTBVHPI_EXPORTS", ""), ("VHPI_CHECKING", 1)] + [(sim_define, "")] + _extra_defines, - libraries=["gpi", "gpilog"] + extra_lib, - library_dirs=extra_lib_dir, - sources=libcocotbvhpi_sources, - extra_link_args=_extra_link_args(lib_name=lib_name, rpaths=["$ORIGIN"]), - extra_compile_args=_extra_cxx_compile_args, - ) - - return libcocotbvhpi - - -def get_ext(): - - cfg_vars = distutils.sysconfig.get_config_vars() - - if sys.platform == "darwin": - cfg_vars["LDSHARED"] = cfg_vars["LDSHARED"].replace("-bundle", "-dynamiclib") - - share_lib_dir = os.path.relpath(os.path.join(cocotb_share_dir, "lib")) - include_dir = os.path.relpath(os.path.join(cocotb_share_dir, "include")) - share_def_dir = os.path.relpath(os.path.join(cocotb_share_dir, "def")) - - ext = [] - - logger.info("Compiling interface libraries for cocotb ...") - - ext += _get_common_lib_ext(include_dir, share_lib_dir) - - # - # Icarus Verilog - # - icarus_extra_lib = [] - icarus_extra_lib_path = [] - logger.info("Compiling libraries for Icarus Verilog") - if os.name == "nt": - icarus_extra_lib = ["icarus"] - icarus_extra_lib_path = [share_def_dir] - - icarus_vpi_ext = _get_vpi_lib_ext( - include_dir=include_dir, - share_lib_dir=share_lib_dir, - sim_define="ICARUS", - extra_lib=icarus_extra_lib, - extra_lib_dir=icarus_extra_lib_path, - ) - ext.append(icarus_vpi_ext) - - # - # Modelsim/Questa - # - modelsim_extra_lib = [] - modelsim_extra_lib_path = [] - logger.info("Compiling libraries for Modelsim/Questa") - if os.name == "nt": - modelsim_extra_lib = ["modelsim"] - modelsim_extra_lib_path = [share_def_dir] - - modelsim_vpi_ext = _get_vpi_lib_ext( - include_dir=include_dir, - share_lib_dir=share_lib_dir, - sim_define="MODELSIM", - extra_lib=modelsim_extra_lib, - extra_lib_dir=modelsim_extra_lib_path, - ) - ext.append(modelsim_vpi_ext) - - vsim_path = find_executable("vdbg") - if vsim_path is None: - logger.warning( - "Modelsim/Questa executable (vdbg) executable not found. No FLI interface will be available." - ) - else: - modelsim_dir = os.path.dirname(os.path.dirname(vsim_path)) - modelsim_include_dir = os.path.join(modelsim_dir, "include") - mti_path = os.path.join(modelsim_include_dir, "mti.h") - if os.path.isfile(mti_path): - lib_name = "libcocotbfli_modelsim" - fli_sources = [ - os.path.join(share_lib_dir, "fli", "FliImpl.cpp"), - os.path.join(share_lib_dir, "fli", "FliCbHdl.cpp"), - os.path.join(share_lib_dir, "fli", "FliObjHdl.cpp"), - ] - if os.name == "nt": - fli_sources += [lib_name + ".rc"] - fli_ext = Extension( - os.path.join("cocotb", "libs", lib_name), - define_macros=[("COCOTBFLI_EXPORTS", "")] + _extra_defines, - include_dirs=[include_dir, modelsim_include_dir], - libraries=["gpi", "gpilog"] + modelsim_extra_lib, - library_dirs=modelsim_extra_lib_path, - sources=fli_sources, - extra_link_args=_extra_link_args(lib_name=lib_name, rpaths=["$ORIGIN"]), - extra_compile_args=_extra_cxx_compile_args, - ) - - ext.append(fli_ext) - - else: - logger.warning( - "Cannot build FLI interface for Modelsim/Questa - " - "the mti.h header for '{}' was not found at '{}'.".format( - vsim_path, mti_path - ) - ) # some Modelsim version does not include FLI. - - # - # GHDL - # - ghdl_extra_lib = [] - ghdl_extra_lib_path = [] - logger.info("Compiling libraries for GHDL") - if os.name == "nt": - ghdl_extra_lib = ["ghdl"] - ghdl_extra_lib_path = [share_def_dir] - - ghdl_vpi_ext = _get_vpi_lib_ext( - include_dir=include_dir, - share_lib_dir=share_lib_dir, - sim_define="GHDL", - extra_lib=ghdl_extra_lib, - extra_lib_dir=ghdl_extra_lib_path, - ) - ext.append(ghdl_vpi_ext) - - # - # IUS - # - if os.name == "posix": - logger.info("Compiling libraries for Incisive/Xcelium") - ius_vpi_ext = _get_vpi_lib_ext( - include_dir=include_dir, share_lib_dir=share_lib_dir, sim_define="IUS" - ) - ext.append(ius_vpi_ext) - - ius_vhpi_ext = _get_vhpi_lib_ext( - include_dir=include_dir, share_lib_dir=share_lib_dir, sim_define="IUS" - ) - ext.append(ius_vhpi_ext) - - # - # VCS - # - if os.name == "posix": - logger.info("Compiling libraries for VCS") - vcs_vpi_ext = _get_vpi_lib_ext( - include_dir=include_dir, share_lib_dir=share_lib_dir, sim_define="VCS" - ) - ext.append(vcs_vpi_ext) - - # - # Aldec Riviera Pro - # - aldec_extra_lib = [] - aldec_extra_lib_path = [] - logger.info("Compiling libraries for Riviera") - if os.name == "nt": - aldec_extra_lib = ["aldec"] - aldec_extra_lib_path = [share_def_dir] - - aldec_vpi_ext = _get_vpi_lib_ext( - include_dir=include_dir, - share_lib_dir=share_lib_dir, - sim_define="ALDEC", - extra_lib=aldec_extra_lib, - extra_lib_dir=aldec_extra_lib_path, - ) - ext.append(aldec_vpi_ext) - - aldec_vhpi_ext = _get_vhpi_lib_ext( - include_dir=include_dir, - share_lib_dir=share_lib_dir, - sim_define="ALDEC", - extra_lib=aldec_extra_lib, - extra_lib_dir=aldec_extra_lib_path, - ) - ext.append(aldec_vhpi_ext) - - # - # Verilator - # - if os.name == "posix": - logger.info("Compiling libraries for Verilator") - verilator_vpi_ext = _get_vpi_lib_ext( - include_dir=include_dir, share_lib_dir=share_lib_dir, sim_define="VERILATOR" - ) - ext.append(verilator_vpi_ext) - - return ext diff --git a/documentation/.gitignore b/documentation/.gitignore deleted file mode 100644 index 4d9b5b56..00000000 --- a/documentation/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.venv -source/master-notes.rst -source/doxygen diff --git a/documentation/Doxyfile b/documentation/Doxyfile deleted file mode 100644 index fb58ccf1..00000000 --- a/documentation/Doxyfile +++ /dev/null @@ -1,2554 +0,0 @@ -# Doxyfile 1.8.20 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project. -# -# All text after a double hash (##) is considered a comment and is placed in -# front of the TAG it is preceding. -# -# All text after a single hash (#) is considered a comment and will be ignored. -# The format is: -# TAG = value [value, ...] -# For lists, items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (\" \"). - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the configuration -# file that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# https://www.gnu.org/software/libiconv/ for the list of possible encodings. -# The default value is: UTF-8. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by -# double-quotes, unless you are using Doxywizard) that should identify the -# project for which the documentation is generated. This name is used in the -# title of most generated pages and in a few other places. -# The default value is: My Project. - -PROJECT_NAME = "cocotb" - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. This -# could be handy for archiving the generated documentation or if some version -# control system is used. - -PROJECT_NUMBER = - -# Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer a -# quick idea about the purpose of the project. Keep the description short. - -PROJECT_BRIEF = - -# With the PROJECT_LOGO tag one can specify a logo or an icon that is included -# in the documentation. The maximum height of the logo should not exceed 55 -# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy -# the logo to the output directory. - -PROJECT_LOGO = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path -# into which the generated documentation will be written. If a relative path is -# entered, it will be relative to the location where doxygen was started. If -# left blank the current directory will be used. - -OUTPUT_DIRECTORY = source/doxygen - -# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this -# option can be useful when feeding doxygen a huge amount of source files, where -# putting all generated files in the same directory would otherwise causes -# performance problems for the file system. -# The default value is: NO. - -CREATE_SUBDIRS = NO - -# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII -# characters to appear in the names of generated files. If set to NO, non-ASCII -# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode -# U+3044. -# The default value is: NO. - -ALLOW_UNICODE_NAMES = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. -# The default value is: English. - -OUTPUT_LANGUAGE = English - -# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all generated output in the proper direction. -# Possible values are: None, LTR, RTL and Context. -# The default value is: None. - -OUTPUT_TEXT_DIRECTION = None - -# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member -# descriptions after the members that are listed in the file and class -# documentation (similar to Javadoc). Set to NO to disable this. -# The default value is: YES. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief -# description of a member or function before the detailed description -# -# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. -# The default value is: YES. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator that is -# used to form the text in various listings. Each string in this list, if found -# as the leading text of the brief description, will be stripped from the text -# and the result, after processing the whole list, is used as the annotated -# text. Otherwise, the brief description is used as-is. If left blank, the -# following values are used ($name is automatically replaced with the name of -# the entity):The $name class, The $name widget, The $name file, is, provides, -# specifies, contains, represents, a, an and the. - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# doxygen will generate a detailed section even if there is only a brief -# description. -# The default value is: NO. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. -# The default value is: NO. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path -# before files name in the file list and in the header files. If set to NO the -# shortest path that makes the file name unique will be used -# The default value is: YES. - -FULL_PATH_NAMES = NO - -# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. -# Stripping is only done if one of the specified strings matches the left-hand -# part of the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the path to -# strip. -# -# Note that you can specify absolute paths here, but also relative paths, which -# will be relative from the directory where doxygen is started. -# This tag requires that the tag FULL_PATH_NAMES is set to YES. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the -# path mentioned in the documentation of a class, which tells the reader which -# header file to include in order to use a class. If left blank only the name of -# the header file containing the class definition is used. Otherwise one should -# specify the list of include paths that are normally passed to the compiler -# using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but -# less readable) file names. This can be useful is your file systems doesn't -# support long names like on DOS, Mac, or CD-ROM. -# The default value is: NO. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the -# first line (until the first dot) of a Javadoc-style comment as the brief -# description. If set to NO, the Javadoc-style will behave just like regular Qt- -# style comments (thus requiring an explicit @brief command for a brief -# description.) -# The default value is: NO. - -JAVADOC_AUTOBRIEF = YES - -# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line -# such as -# /*************** -# as being the beginning of a Javadoc-style comment "banner". If set to NO, the -# Javadoc-style will behave just like regular comments and it will not be -# interpreted by doxygen. -# The default value is: NO. - -JAVADOC_BANNER = NO - -# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first -# line (until the first dot) of a Qt-style comment as the brief description. If -# set to NO, the Qt-style will behave just like regular Qt-style comments (thus -# requiring an explicit \brief command for a brief description.) -# The default value is: NO. - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a -# multi-line C++ special comment block (i.e. a block of //! or /// comments) as -# a brief description. This used to be the default behavior. The new default is -# to treat a multi-line C++ comment block as a detailed description. Set this -# tag to YES if you prefer the old behavior instead. -# -# Note that setting this tag to YES also means that rational rose comments are -# not recognized any more. -# The default value is: NO. - -MULTILINE_CPP_IS_BRIEF = NO - -# By default Python docstrings are displayed as preformatted text and doxygen's -# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the -# doxygen's special commands can be used and the contents of the docstring -# documentation blocks is shown as doxygen documentation. -# The default value is: YES. - -PYTHON_DOCSTRING = YES - -# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the -# documentation from any documented member that it re-implements. -# The default value is: YES. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new -# page for each member. If set to NO, the documentation of a member will be part -# of the file/class/namespace that contains it. -# The default value is: NO. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen -# uses this value to replace tabs by spaces in code fragments. -# Minimum value: 1, maximum value: 16, default value: 4. - -TAB_SIZE = 4 - -# This tag can be used to specify a number of aliases that act as commands in -# the documentation. An alias has the form: -# name=value -# For example adding -# "sideeffect=@par Side Effects:\n" -# will allow you to put the command \sideeffect (or @sideeffect) in the -# documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines (in the resulting output). You can put ^^ in the value part of an -# alias to insert a newline as if a physical newline was in the original file. -# When you need a literal { or } or , in the value part of an alias you have to -# escape them by means of a backslash (\), this can lead to conflicts with the -# commands \{ and \} for these it is advised to use the version @{ and @} or use -# a double escape (\\{ and \\}) - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources -# only. Doxygen will then generate output that is more tailored for C. For -# instance, some of the names that are used will be different. The list of all -# members will be omitted, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_FOR_C = NO - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or -# Python sources only. Doxygen will then generate output that is more tailored -# for that language. For instance, namespaces will be presented as packages, -# qualified scopes will look different, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources. Doxygen will then generate output that is tailored for Fortran. -# The default value is: NO. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for VHDL. -# The default value is: NO. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice -# sources only. Doxygen will then generate output that is more tailored for that -# language. For instance, namespaces will be presented as modules, types will be -# separated into more groups, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_SLICE = NO - -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given -# extension. Doxygen has a built-in mapping, but you can override or extend it -# using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, -# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, -# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: -# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser -# tries to guess whether the code is fixed or free formatted code, this is the -# default for Fortran type files). For instance to make doxygen treat .inc files -# as Fortran files (default is PHP), and .f files as C (default is Fortran), -# use: inc=Fortran f=C. -# -# Note: For files without extension you can use no_extension as a placeholder. -# -# Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. - -EXTENSION_MAPPING = - -# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments -# according to the Markdown format, which allows for more readable -# documentation. See https://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you can -# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in -# case of backward compatibilities issues. -# The default value is: YES. - -MARKDOWN_SUPPORT = YES - -# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up -# to that level are automatically included in the table of contents, even if -# they do not have an id attribute. -# Note: This feature currently applies only to Markdown headings. -# Minimum value: 0, maximum value: 99, default value: 5. -# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. - -TOC_INCLUDE_HEADINGS = 5 - -# When enabled doxygen tries to link words that correspond to documented -# classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by putting a % sign in front of the word or -# globally by setting AUTOLINK_SUPPORT to NO. -# The default value is: YES. - -AUTOLINK_SUPPORT = YES - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should set this -# tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); -# versus func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. -# The default value is: NO. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. -# The default value is: NO. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen -# will parse them like normal C++ but will assume all classes use public instead -# of private inheritance when no explicit protection keyword is present. -# The default value is: NO. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate -# getter and setter methods for a property. Setting this option to YES will make -# doxygen to replace the get and set methods by a property in the documentation. -# This will only work if the methods are indeed getting or setting a simple -# type. If this is not the case, or you want to show the methods anyway, you -# should set this option to NO. -# The default value is: YES. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. -# The default value is: NO. - -DISTRIBUTE_GROUP_DOC = NO - -# If one adds a struct or class to a group and this option is enabled, then also -# any nested class or struct is added to the same group. By default this option -# is disabled and one has to add nested compounds explicitly via \ingroup. -# The default value is: NO. - -GROUP_NESTED_COMPOUNDS = NO - -# Set the SUBGROUPING tag to YES to allow class member groups of the same type -# (for instance a group of public functions) to be put as a subgroup of that -# type (e.g. under the Public Functions section). Set it to NO to prevent -# subgrouping. Alternatively, this can be done per class using the -# \nosubgrouping command. -# The default value is: YES. - -SUBGROUPING = YES - -# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions -# are shown inside the group in which they are included (e.g. using \ingroup) -# instead of on a separate page (for HTML and Man pages) or section (for LaTeX -# and RTF). -# -# Note that this feature does not work in combination with -# SEPARATE_MEMBER_PAGES. -# The default value is: NO. - -INLINE_GROUPED_CLASSES = NO - -# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions -# with only public data fields or simple typedef fields will be shown inline in -# the documentation of the scope in which they are defined (i.e. file, -# namespace, or group documentation), provided this scope is documented. If set -# to NO, structs, classes, and unions are shown on a separate page (for HTML and -# Man pages) or section (for LaTeX and RTF). -# The default value is: NO. - -INLINE_SIMPLE_STRUCTS = NO - -# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or -# enum is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically be -# useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. -# The default value is: NO. - -TYPEDEF_HIDES_STRUCT = NO - -# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This -# cache is used to resolve symbols given their name and scope. Since this can be -# an expensive process and often the same symbol appears multiple times in the -# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small -# doxygen will become slower. If the cache is too large, memory is wasted. The -# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range -# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 -# symbols. At the end of a run doxygen will report the cache usage and suggest -# the optimal cache size from a speed point of view. -# Minimum value: 0, maximum value: 9, default value: 0. - -LOOKUP_CACHE_SIZE = 0 - -# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use -# during processing. When set to 0 doxygen will based this on the number of -# cores available in the system. You can set it explicitly to a value larger -# than 0 to get more control over the balance between CPU load and processing -# speed. At this moment only the input processing can be done using multiple -# threads. Since this is still an experimental feature the default is set to 1, -# which efficively disables parallel processing. Please report any issues you -# encounter. Generating dot graphs in parallel is controlled by the -# DOT_NUM_THREADS setting. -# Minimum value: 0, maximum value: 32, default value: 1. - -NUM_PROC_THREADS = 1 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in -# documentation are documented, even if no documentation was available. Private -# class members and static file members will be hidden unless the -# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. -# Note: This will also disable the warnings about undocumented members that are -# normally produced when WARNINGS is set to YES. -# The default value is: NO. - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will -# be included in the documentation. -# The default value is: NO. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual -# methods of a class will be included in the documentation. -# The default value is: NO. - -EXTRACT_PRIV_VIRTUAL = NO - -# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal -# scope will be included in the documentation. -# The default value is: NO. - -EXTRACT_PACKAGE = NO - -# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be -# included in the documentation. -# The default value is: NO. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined -# locally in source files will be included in the documentation. If set to NO, -# only classes defined in header files are included. Does not have any effect -# for Java sources. -# The default value is: YES. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. If set to YES, local methods, -# which are defined in the implementation section but not in the interface are -# included in the documentation. If set to NO, only methods in the interface are -# included. -# The default value is: NO. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base name of -# the file that contains the anonymous namespace. By default anonymous namespace -# are hidden. -# The default value is: NO. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all -# undocumented members inside documented classes or files. If set to NO these -# members will be included in the various overviews, but no documentation -# section is generated. This option has no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. If set -# to NO, these classes will be included in the various overviews. This option -# has no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# declarations. If set to NO, these declarations will be included in the -# documentation. -# The default value is: NO. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any -# documentation blocks found inside the body of a function. If set to NO, these -# blocks will be appended to the function's detailed documentation block. -# The default value is: NO. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation that is typed after a -# \internal command is included. If the tag is set to NO then the documentation -# will be excluded. Set it to YES to include the internal documentation. -# The default value is: NO. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES, upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# (including Cygwin) and Mac users are advised to set this option to NO. -# The default value is: system dependent. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES, the -# scope will be hidden. -# The default value is: NO. - -HIDE_SCOPE_NAMES = NO - -# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will -# append additional text to a page's title, such as Class Reference. If set to -# YES the compound reference will be hidden. -# The default value is: NO. - -HIDE_COMPOUND_REFERENCE= NO - -# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of -# the files that are included by a file in the documentation of that file. -# The default value is: YES. - -SHOW_INCLUDE_FILES = NO - -# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each -# grouped member an include statement to the documentation, telling the reader -# which file to include in order to use the member. -# The default value is: NO. - -SHOW_GROUPED_MEMB_INC = NO - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include -# files with double quotes in the documentation rather than with sharp brackets. -# The default value is: NO. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the -# documentation for inline members. -# The default value is: YES. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the -# (detailed) documentation of file and class members alphabetically by member -# name. If set to NO, the members will appear in declaration order. -# The default value is: YES. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief -# descriptions of file, namespace and class members alphabetically by member -# name. If set to NO, the members will appear in declaration order. Note that -# this will also influence the order of the classes in the class list. -# The default value is: NO. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the -# (brief and detailed) documentation of class members so that constructors and -# destructors are listed first. If set to NO the constructors will appear in the -# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. -# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief -# member documentation. -# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting -# detailed member documentation. -# The default value is: NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy -# of group names into alphabetical order. If set to NO the group names will -# appear in their defined order. -# The default value is: NO. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by -# fully-qualified names, including namespaces. If set to NO, the class list will -# be sorted only by class name, not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the alphabetical -# list. -# The default value is: NO. - -SORT_BY_SCOPE_NAME = NO - -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper -# type resolution of all parameters of a function it will reject a match between -# the prototype and the implementation of a member function even if there is -# only one candidate or it is obvious which candidate to choose by doing a -# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still -# accept a match between prototype and implementation in such cases. -# The default value is: NO. - -STRICT_PROTO_MATCHING = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo -# list. This list is created by putting \todo commands in the documentation. -# The default value is: YES. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test -# list. This list is created by putting \test commands in the documentation. -# The default value is: YES. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug -# list. This list is created by putting \bug commands in the documentation. -# The default value is: YES. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) -# the deprecated list. This list is created by putting \deprecated commands in -# the documentation. -# The default value is: YES. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional documentation -# sections, marked by \if ... \endif and \cond -# ... \endcond blocks. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the -# initial value of a variable or macro / define can have for it to appear in the -# documentation. If the initializer consists of more lines than specified here -# it will be hidden. Use a value of 0 to hide initializers completely. The -# appearance of the value of individual variables and macros / defines can be -# controlled using \showinitializer or \hideinitializer command in the -# documentation regardless of this setting. -# Minimum value: 0, maximum value: 10000, default value: 30. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at -# the bottom of the documentation of classes and structs. If set to YES, the -# list will mention the files that were used to generate the documentation. -# The default value is: YES. - -SHOW_USED_FILES = NO - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This -# will remove the Files entry from the Quick Index and from the Folder Tree View -# (if specified). -# The default value is: YES. - -SHOW_FILES = NO - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces -# page. This will remove the Namespaces entry from the Quick Index and from the -# Folder Tree View (if specified). -# The default value is: YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command command input-file, where command is the value of the -# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided -# by doxygen. Whatever the program writes to standard output is used as the file -# version. For an example see the documentation. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. You can -# optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. -# -# Note that if you run doxygen from a directory containing a file called -# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE -# tag is left empty. - -LAYOUT_FILE = - -# The CITE_BIB_FILES tag can be used to specify one or more bib files containing -# the reference definitions. This must be a list of .bib files. The .bib -# extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. -# For LaTeX the style of the bibliography can be controlled using -# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the -# search path. See also \cite for info how to create references. - -CITE_BIB_FILES = - -#--------------------------------------------------------------------------- -# Configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated to -# standard output by doxygen. If QUIET is set to YES this implies that the -# messages are off. -# The default value is: NO. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES -# this implies that the warnings are on. -# -# Tip: Turn warnings on while writing the documentation. -# The default value is: YES. - -WARNINGS = YES - -# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate -# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag -# will automatically be disabled. -# The default value is: YES. - -WARN_IF_UNDOCUMENTED = NO - -# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. -# The default value is: YES. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that -# are documented, but have no documentation for their parameters or return -# value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. If -# EXTRACT_ALL is set to YES then this flag will automatically be disabled. -# The default value is: NO. - -WARN_NO_PARAMDOC = NO - -# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when -# a warning is encountered. -# The default value is: NO. - -WARN_AS_ERROR = NO - -# The WARN_FORMAT tag determines the format of the warning messages that doxygen -# can produce. The string should contain the $file, $line, and $text tags, which -# will be replaced by the file and line number from which the warning originated -# and the warning text. Optionally the format may contain $version, which will -# be replaced by the version of the file (if it could be obtained via -# FILE_VERSION_FILTER) -# The default value is: $file:$line: $text. - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning and error -# messages should be written. If left blank the output is written to standard -# error (stderr). - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# Configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag is used to specify the files and/or directories that contain -# documented source files. You may enter file names like myfile.cpp or -# directories like /usr/src/myproject. Separate the files or directories with -# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING -# Note: If this tag is empty the current directory is searched. - -INPUT = ../cocotb/share/include/gpi.h - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses -# libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: https://www.gnu.org/software/libiconv/) for the list of -# possible encodings. -# The default value is: UTF-8. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. -# -# Note that for custom extensions or not directly supported extensions you also -# need to set EXTENSION_MAPPING for the extension otherwise the files are not -# read by doxygen. -# -# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, -# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, -# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), -# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen -# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, -# *.vhdl, *.ucf, *.qsf and *.ice. - -FILE_PATTERNS = - -# The RECURSIVE tag can be used to specify whether or not subdirectories should -# be searched for input files as well. -# The default value is: NO. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should be -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. -# -# Note that relative paths are relative to the directory from which doxygen is -# run. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded -# from the input. -# The default value is: NO. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or directories -# that contain example code fragments that are included (see the \include -# command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank all -# files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude commands -# irrespective of the value of the RECURSIVE tag. -# The default value is: NO. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or directories -# that contain images that are to be included in the documentation (see the -# \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command: -# -# -# -# where is the value of the INPUT_FILTER tag, and is the -# name of an input file. Doxygen will then use the output that the filter -# program writes to standard output. If FILTER_PATTERNS is specified, this tag -# will be ignored. -# -# Note that the filter must not add or remove lines; it is applied before the -# code is scanned, but not when the output code is generated. If lines are added -# or removed, the anchors will not be placed correctly. -# -# Note that for custom extensions or not directly supported extensions you also -# need to set EXTENSION_MAPPING for the extension otherwise the files are not -# properly processed by doxygen. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: pattern=filter -# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how -# filters are used. If the FILTER_PATTERNS tag is empty or if none of the -# patterns match the file name, INPUT_FILTER is applied. -# -# Note that for custom extensions or not directly supported extensions you also -# need to set EXTENSION_MAPPING for the extension otherwise the files are not -# properly processed by doxygen. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will also be used to filter the input files that are used for -# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). -# The default value is: NO. - -FILTER_SOURCE_FILES = NO - -# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and -# it is also possible to disable source filtering for a specific pattern using -# *.ext= (so without naming a filter). -# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. - -FILTER_SOURCE_PATTERNS = - -# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that -# is part of the input, its contents will be placed on the main page -# (index.html). This can be useful if you have a project on for instance GitHub -# and want to reuse the introduction page also for the doxygen output. - -USE_MDFILE_AS_MAINPAGE = - -#--------------------------------------------------------------------------- -# Configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will be -# generated. Documented entities will be cross-referenced with these sources. -# -# Note: To get rid of all source code in the generated output, make sure that -# also VERBATIM_HEADERS is set to NO. -# The default value is: NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body of functions, -# classes and enums directly into the documentation. -# The default value is: NO. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any -# special comment blocks from generated source code fragments. Normal C, C++ and -# Fortran comments will always remain visible. -# The default value is: YES. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# entity all documented functions referencing it will be listed. -# The default value is: NO. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES then for each documented function -# all documented entities called/used by that function will be listed. -# The default value is: NO. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set -# to YES then the hyperlinks from functions in REFERENCES_RELATION and -# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will -# link to the documentation. -# The default value is: YES. - -REFERENCES_LINK_SOURCE = YES - -# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the -# source code will show a tooltip with additional information such as prototype, -# brief description and links to the definition and documentation. Since this -# will make the HTML file larger and loading of large files a bit slower, you -# can opt to disable this feature. -# The default value is: YES. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -SOURCE_TOOLTIPS = YES - -# If the USE_HTAGS tag is set to YES then the references to source code will -# point to the HTML generated by the htags(1) tool instead of doxygen built-in -# source browser. The htags tool is part of GNU's global source tagging system -# (see https://www.gnu.org/software/global/global.html). You will need version -# 4.8.6 or higher. -# -# To use it do the following: -# - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file -# - Make sure the INPUT points to the root of the source tree -# - Run doxygen as normal -# -# Doxygen will invoke htags (and that will in turn invoke gtags), so these -# tools must be available from the command line (i.e. in the search path). -# -# The result: instead of the source browser generated by doxygen, the links to -# source code will now point to the output of htags. -# The default value is: NO. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a -# verbatim copy of the header file for each class for which an include is -# specified. Set to NO to disable this. -# See also: Section \class. -# The default value is: YES. - -VERBATIM_HEADERS = YES - -# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the -# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the -# cost of reduced performance. This can be particularly helpful with template -# rich C++ code for which doxygen's built-in parser lacks the necessary type -# information. -# Note: The availability of this option depends on whether or not doxygen was -# generated with the -Duse_libclang=ON option for CMake. -# The default value is: NO. - -CLANG_ASSISTED_PARSING = NO - -# If clang assisted parsing is enabled you can provide the compiler with command -# line options that you would normally use when invoking the compiler. Note that -# the include paths will already be set by doxygen for the files and directories -# specified with INPUT and INCLUDE_PATH. -# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. - -CLANG_OPTIONS = - -# If clang assisted parsing is enabled you can provide the clang parser with the -# path to the directory containing a file called compile_commands.json. This -# file is the compilation database (see: -# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the -# options used when the source files were built. This is equivalent to -# specifying the "-p" option to a clang tool, such as clang-check. These options -# will then be passed to the parser. Any options specified with CLANG_OPTIONS -# will be added as well. -# Note: The availability of this option depends on whether or not doxygen was -# generated with the -Duse_libclang=ON option for CMake. - -CLANG_DATABASE_PATH = - -#--------------------------------------------------------------------------- -# Configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all -# compounds will be generated. Enable this if the project contains a lot of -# classes, structs, unions or interfaces. -# The default value is: YES. - -ALPHABETICAL_INDEX = YES - -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output -# The default value is: YES. - -GENERATE_HTML = NO - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a -# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of -# it. -# The default directory is: html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each -# generated HTML page (for example: .htm, .php, .asp). -# The default value is: .html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a user-defined HTML header file for -# each generated HTML page. If the tag is left blank doxygen will generate a -# standard header. -# -# To get valid HTML the header file that includes any scripts and style sheets -# that doxygen needs, which is dependent on the configuration options used (e.g. -# the setting GENERATE_TREEVIEW). It is highly recommended to start with a -# default header using -# doxygen -w html new_header.html new_footer.html new_stylesheet.css -# YourConfigFile -# and then modify the file new_header.html. See also section "Doxygen usage" -# for information on how to generate the default header that doxygen normally -# uses. -# Note: The header is subject to change so you typically have to regenerate the -# default header when upgrading to a newer version of doxygen. For a description -# of the possible markers and block names see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each -# generated HTML page. If the tag is left blank doxygen will generate a standard -# footer. See HTML_HEADER for more information on how to generate a default -# footer and what special commands can be used inside the footer. See also -# section "Doxygen usage" for information on how to generate the default footer -# that doxygen normally uses. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style -# sheet that is used by each HTML page. It can be used to fine-tune the look of -# the HTML output. If left blank doxygen will generate a default style sheet. -# See also section "Doxygen usage" for information on how to generate the style -# sheet that doxygen normally uses. -# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as -# it is more robust and this tag (HTML_STYLESHEET) will in the future become -# obsolete. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_STYLESHEET = - -# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined -# cascading style sheets that are included after the standard style sheets -# created by doxygen. Using this option one can overrule certain style aspects. -# This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefore more robust against future updates. -# Doxygen will copy the style sheet files to the output directory. -# Note: The order of the extra style sheet files is of importance (e.g. the last -# style sheet in the list overrules the setting of the previous ones in the -# list). For an example see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_STYLESHEET = - -# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the HTML output directory. Note -# that these files will be copied to the base HTML output directory. Use the -# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that the -# files will be copied as-is; there are no commands or markers available. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_FILES = - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen -# will adjust the colors in the style sheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see -# https://en.wikipedia.org/wiki/Hue for more information. For instance the value -# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 -# purple, and 360 is red again. -# Minimum value: 0, maximum value: 359, default value: 220. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A -# value of 255 will produce the most vivid colors. -# Minimum value: 0, maximum value: 255, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_SAT = 100 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the -# luminance component of the colors in the HTML output. Values below 100 -# gradually make the output lighter, whereas values above 100 make the output -# darker. The value divided by 100 is the actual gamma applied, so 80 represents -# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not -# change the gamma. -# Minimum value: 40, maximum value: 240, default value: 80. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_GAMMA = 80 - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to YES can help to show when doxygen was last run and thus if the -# documentation is up to date. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_TIMESTAMP = NO - -# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML -# documentation will contain a main index with vertical navigation menus that -# are dynamically created via JavaScript. If disabled, the navigation index will -# consists of multiple levels of tabs that are statically embedded in every HTML -# page. Disable this option to support browsers that do not have JavaScript, -# like the Qt help browser. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_DYNAMIC_MENUS = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_DYNAMIC_SECTIONS = NO - -# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries -# shown in the various tree structured indices initially; the user can expand -# and collapse entries dynamically later on. Doxygen will expand the tree to -# such a level that at most the specified number of entries are visible (unless -# a fully collapsed tree already exceeds this amount). So setting the number of -# entries 1 will produce a full collapsed tree by default. 0 is a special value -# representing an infinite number of entries and will result in a full expanded -# tree by default. -# Minimum value: 0, maximum value: 9999, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_INDEX_NUM_ENTRIES = 100 - -# If the GENERATE_DOCSET tag is set to YES, additional index files will be -# generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: https://developer.apple.com/xcode/), introduced with OSX -# 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy -# genXcode/_index.html for more information. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_DOCSET = NO - -# This tag determines the name of the docset feed. A documentation feed provides -# an umbrella under which multiple documentation sets from a single provider -# (such as a company or product suite) can be grouped. -# The default value is: Doxygen generated docs. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# This tag specifies a string that should uniquely identify the documentation -# set bundle. This should be a reverse domain-name style string, e.g. -# com.mycompany.MyDocSet. Doxygen will append .docset to the name. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify -# the documentation publisher. This should be a reverse domain-name style -# string, e.g. com.mycompany.MyDocSet.documentation. -# The default value is: org.doxygen.Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_ID = org.doxygen.Publisher - -# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. -# The default value is: Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_NAME = Publisher - -# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three -# additional HTML index files: index.hhp, index.hhc, and index.hhk. The -# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. -# -# The HTML Help Workshop contains a compiler that can convert all HTML output -# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML -# files are now used as the Windows 98 help format, and will replace the old -# Windows help format (.hlp) on all Windows platforms in the future. Compressed -# HTML files also contain an index, a table of contents, and you can search for -# words in the documentation. The HTML workshop also contains a viewer for -# compressed HTML files. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_HTMLHELP = NO - -# The CHM_FILE tag can be used to specify the file name of the resulting .chm -# file. You can add a path in front of the file if the result should not be -# written to the html output directory. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_FILE = - -# The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler (hhc.exe). If non-empty, -# doxygen will try to run the HTML help compiler on the generated index.hhp. -# The file has to be specified with full path. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -HHC_LOCATION = - -# The GENERATE_CHI flag controls if a separate .chi index file is generated -# (YES) or that it should be included in the main .chm file (NO). -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -GENERATE_CHI = NO - -# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) -# and project file content. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_INDEX_ENCODING = - -# The BINARY_TOC flag controls whether a binary table of contents is generated -# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it -# enables the Previous and Next buttons. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members to -# the table of contents of the HTML help documentation and to the tree view. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that -# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help -# (.qch) of the generated HTML documentation. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify -# the file name of the resulting .qch file. The path specified is relative to -# the HTML output folder. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help -# Project output. For more information please see Qt Help Project / Namespace -# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt -# Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- -# folders). -# The default value is: doc. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_VIRTUAL_FOLDER = doc - -# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom -# filter to add. For more information please see Qt Help Project / Custom -# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- -# filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- -# filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's filter section matches. Qt Help Project / Filter Attributes (see: -# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_SECT_FILTER_ATTRS = - -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be -# generated, together with the HTML files, they form an Eclipse help plugin. To -# install this plugin and make it available under the help contents menu in -# Eclipse, the contents of the directory containing the HTML and XML files needs -# to be copied into the plugins directory of eclipse. The name of the directory -# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. -# After copying Eclipse needs to be restarted before the help appears. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the Eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have this -# name. Each documentation set should have its own identifier. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# If you want full control over the layout of the generated HTML pages it might -# be necessary to disable the index and replace it with your own. The -# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top -# of each HTML page. A value of NO enables the index and the value YES disables -# it. Since the tabs in the index contain the same information as the navigation -# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -DISABLE_INDEX = NO - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. If the tag -# value is set to YES, a side panel will be generated containing a tree-like -# index structure (just like the one that is generated for HTML Help). For this -# to work a browser that supports JavaScript, DHTML, CSS and frames is required -# (i.e. any modern browser). Windows users are probably better off using the -# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_TREEVIEW = NO - -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that -# doxygen will group on one line in the generated HTML documentation. -# -# Note that a value of 0 will completely suppress the enum values from appearing -# in the overview section. -# Minimum value: 0, maximum value: 20, default value: 4. -# This tag requires that the tag GENERATE_HTML is set to YES. - -ENUM_VALUES_PER_LINE = 4 - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used -# to set the initial width (in pixels) of the frame in which the tree is shown. -# Minimum value: 0, maximum value: 1500, default value: 250. -# This tag requires that the tag GENERATE_HTML is set to YES. - -TREEVIEW_WIDTH = 250 - -# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to -# external symbols imported via tag files in a separate window. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -EXT_LINKS_IN_WINDOW = NO - -# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg -# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see -# https://inkscape.org) to generate formulas as SVG images instead of PNGs for -# the HTML output. These images will generally look nicer at scaled resolutions. -# Possible values are: png (the default) and svg (looks nicer but requires the -# pdf2svg or inkscape tool). -# The default value is: png. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FORMULA_FORMAT = png - -# Use this tag to change the font size of LaTeX formulas included as images in -# the HTML documentation. When you change the font size after a successful -# doxygen run you need to manually remove any form_*.png images from the HTML -# output directory to force them to be regenerated. -# Minimum value: 8, maximum value: 50, default value: 10. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_FONTSIZE = 10 - -# Use the FORMULA_TRANSPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_TRANSPARENT = YES - -# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands -# to create new LaTeX commands to be used in formulas as building blocks. See -# the section "Including formulas" for details. - -FORMULA_MACROFILE = - -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# https://www.mathjax.org) which uses client side JavaScript for the rendering -# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX -# installed or if you want to formulas look prettier in the HTML output. When -# enabled you may also need to install MathJax separately and configure the path -# to it using the MATHJAX_RELPATH option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -USE_MATHJAX = NO - -# When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. -# Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. -# The default value is: HTML-CSS. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_FORMAT = HTML-CSS - -# When MathJax is enabled you need to specify the location relative to the HTML -# output directory using the MATHJAX_RELPATH option. The destination directory -# should contain the MathJax.js script. For instance, if the mathjax directory -# is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax -# Content Delivery Network so you can quickly see the result without installing -# MathJax. However, it is strongly recommended to install a local copy of -# MathJax from https://www.mathjax.org before deployment. -# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest - -# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax -# extension names that should be enabled during MathJax rendering. For example -# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_EXTENSIONS = - -# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces -# of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an -# example see the documentation. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_CODEFILE = - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box for -# the HTML output. The underlying search engine uses javascript and DHTML and -# should work on any modern browser. Note that when using HTML help -# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) -# there is already a search function so this one should typically be disabled. -# For large projects the javascript based search engine can be slow, then -# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to -# search using the keyboard; to jump to the search box use + S -# (what the is depends on the OS and browser, but it is typically -# , /

    2. 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. - -.. image:: regulator.png - -To run this testcase, call: - -.. code-block:: bash - - make SIM=xcelium TOPLEVEL=tb_regulator MODULE=test_regulator_plot - - -test_regulator_trim -------------------- - -This testcase runs an automatic trimming routine :meth:`~test_regulator.Regulator_TB.find_trim_val()` -to find the best trim value for a given target voltage. -The determined trim value and the resulting regulator output voltage -are printed to the console. - -.. code-block:: bash - - 0.00ns INFO Running trimming algorithm for target voltage 3.013 V - 3.00ns INFO Best trimming value is -1 --> voltage is 3.1 V (difference to target is 0.087 V) - -Note that the output voltage does not exactly hit the target value because of -the discrete nature of the trim steps. - -To run this testcase, call: - -.. code-block:: bash - - make SIM=xcelium TOPLEVEL=tb_regulator MODULE=test_regulator_trim diff --git a/documentation/source/release_notes.rst b/documentation/source/release_notes.rst deleted file mode 100644 index b75e0532..00000000 --- a/documentation/source/release_notes.rst +++ /dev/null @@ -1,361 +0,0 @@ -************* -Release Notes -************* - -All releases are available from the `GitHub Releases Page `_. - -.. include:: master-notes.rst - -.. towncrier release notes start - -Cocotb 1.4.0 (2020-07-08) -========================= - -Features --------- - -- :class:`~cocotb.triggers.Lock` can now be used in :keyword:`async with` statements. (:pr:`1031`) -- Add support for distinguishing between NET (vpiNet) and REG (vpiReg) type when using vpi interface. (:pr:`1107`) -- Support for dropping into :mod:`pdb` upon failure, via the new :envvar:`COCOTB_PDB_ON_EXCEPTION` environment variable (:pr:`1180`) -- Simulators run through a TCL script (Aldec Riviera Pro and Mentor simulators) now support a new :make:var:`RUN_ARGS` Makefile variable, which is passed to the first invocation of the tool during runtime. (:pr:`1244`) -- Cocotb now supports the following example of forking a *non-decorated* :ref:`async coroutine `. - - .. code-block:: python3 - - async def example(): - for i in range(10): - await cocotb.triggers.Timer(10, "ns") - - cocotb.fork(example()) - - .. - towncrier will append the issue number taken from the file name here: - - Issue (:pr:`1255`) -- The cocotb log configuration is now less intrusive, and only configures the root logger instance, ``logging.getLogger()``, as part of :func:`cocotb.log.default_config` (:pr:`1266`). - - As such, it is now possible to override the default cocotb logging behavior with something like:: - - # remove the cocotb log handler and formatting - root = logging.getLogger() - for h in root.handlers[:]: - root.remove_handler(h) - h.close() - - # add your own - logging.basicConfig() - - .. consume the towncrier issue number on this line. (:pr:`1266`) -- Support for ``vpiRealNet`` (:pr:`1282`) -- The colored output can now be disabled by the :envvar:`NO_COLOR` environment variable. (:pr:`1309`) -- Cocotb now supports deposit/force/release/freeze actions on simulator handles, exposing functionality similar to the respective Verilog/VHDL assignments. - - .. code-block:: python3 - - from cocotb.handle import Deposit, Force, Release, Freeze - - dut.q <= 1 # A regular value deposit - dut.q <= Deposit(1) # The same, higher verbosity - dut.q <= Force(1) # Force value of q to 1 - dut.q <= Release() # Release q from a Force - dut.q <= Freeze() # Freeze the current value of q - - .. - towncrier will append the issue number taken from the file name here: - - Issue (:pr:`1403`) -- Custom logging handlers can now access the simulator time using - :attr:`logging.LogRecord.created_sim_time`, provided the - :class:`~cocotb.log.SimTimeContextFilter` filter added by - :func:`~cocotb.log.default_config` is not removed from the logger instance. (:pr:`1411`) -- Questa now supports :envvar:`PLUSARGS`. - This requires that ``tcl.h`` be present on the system. - This is likely included in your installation of Questa, otherwise, specify ``CFLAGS=-I/path/to/tcl/includedir``. (:pr:`1424`) -- The name of the entry point symbol for libraries in :envvar:`GPI_EXTRA` can now be customized. - The delimiter between each library in the list has changed from ``:`` to ``,``. (:pr:`1457`) -- New methods for setting the value of a :class:`~cocotb.handle.NonHierarchyIndexableObject` (HDL arrays). (:pr:`1507`) - - .. code-block:: python3 - - # Now supported - dut.some_array <= [0xAA, 0xBB, 0xCC] - dut.some_array.value = [0xAA, 0xBB, 0xCC] - - # For simulators that support n-dimensional arrays - dut.some_2d_array <= [[0xAA, 0xBB], [0xCC, 0xDD]] - dut.some_2d_array.value = [[0xAA, 0xBB], [0xCC, 0xDD]] - - .. consume the towncrier issue number on this line. (:pr:`1507`) -- Added support for Aldec's Active-HDL simulator. (:pr:`1601`) -- Including ``Makefile.inc`` from user makefiles is now a no-op and deprecated. Lines like ``include $(shell cocotb-config --makefiles)/Makefile.inc`` can be removed from user makefiles without loss in functionality. (:pr:`1629`) -- Support for using ``await`` inside an embedded IPython terminal, using :mod:`cocotb.ipython_support`. (:pr:`1649`) -- Added :meth:`~cocotb.triggers.Event.is_set`, so users may check if an :class:`~cocotb.triggers.Event` has fired. (:pr:`1723`) -- The :func:`cocotb.simulator.is_running` function was added so a user of cocotb could determine if they are running within a simulator. (:pr:`1843`) - - -Bugfixes --------- - -- Tests which fail at initialization, for instance due to no ``yield`` being present, are no longer silently ignored (:pr:`1253`) -- Tests that were not run because predecessors threw :class:`cocotb.result.SimFailure`, and caused the simulator to exit, are now recorded with an outcome of :class:`cocotb.result.SimFailure`. - Previously, these tests were ignored. (:pr:`1279`) -- Makefiles now correctly fail if the simulation crashes before a ``results.xml`` file can be written. (:pr:`1314`) -- Logging of non-string messages with colored log output is now working. (:pr:`1410`) -- Getting and setting the value of a :class:`~cocotb.handle.NonHierarchyIndexableObject` now iterates through the correct range of the simulation object, so arrays that do not start/end at index 0 are supported. (:pr:`1507`) -- The :class:`~cocotb.monitors.xgmii.XGMII` monitor no longer crashes on Python 3, and now assembles packets as :class:`bytes` instead of :class:`str`. The :class:`~cocotb.drivers.xgmii.XGMII` driver has expected :class:`bytes` since cocotb 1.2.0. (:pr:`1545`) -- ``signal <= value_of_wrong_type`` no longer breaks the scheduler, and throws an error immediately. (:pr:`1661`) -- Scheduling behavior is now consistent before and after the first :keyword:`await` of a :class:`~cocotb.triggers.GPITrigger`. (:pr:`1705`) -- Iterating over ``for generate`` statements using VHPI has been fixed. This bug caused some simulators to crash, and was a regression in version 1.3. (:pr:`1882`) -- The :class:`~cocotb.drivers.xgmii.XGMII` driver no longer emits a corrupted word on the first transfer. (:pr:`1905`) - - -Improved Documentation ----------------------- - -- If a makefile uses cocotb's :file:`Makefile.sim`, ``make help`` now lists the supported targets and variables. (:pr:`1318`) -- A new section :ref:`rotating-logger` has been added. (:pr:`1400`) -- The documentation at http://docs.cocotb.org/ has been restructured, - making it easier to find relevant information. (:pr:`1482`) - - -Deprecations and Removals -------------------------- - -- :func:`cocotb.utils.reject_remaining_kwargs` is deprecated, as it is no longer - needed now that we only support Python 3.5 and newer. (:pr:`1339`) -- The value of :class:`cocotb.handle.StringObject`\ s is now of type :class:`bytes`, instead of :class:`str` with an implied ASCII encoding scheme. (:pr:`1381`) -- :class:`ReturnValue` is now deprecated. Use a :keyword:`return` statement instead; this works in all supported versions of Python. (:pr:`1489`) -- The makefile variable :make:var:`VERILATOR_TRACE` - that was not supported for all simulators has been deprecated. - Using it prints a deprecation warning and points to the documentation section - :ref:`simulator-support` explaining how to get the same effect by other means. (:pr:`1495`) -- ``BinaryValue.get_hex_buff`` produced nonsense and has been removed. (:pr:`1511`) -- Passing :class:`str` instances to :func:`cocotb.utils.hexdump` and :func:`cocotb.utils.hexdiffs` is deprecated. :class:`bytes` objects should be passed instead. (:pr:`1519`) -- ``Makefile.pylib``, which provided helpers for building C extension modules for Python, has been removed. - Users of the ``PYTHON_LIBDIR`` and ``PYTHON_INCLUDEDIR`` variables will now have to compute these values themselves. - See the ``endian_swapper`` example for how to do this. (:pr:`1632`) -- Makefile and documentation for the NVC simulator which has never worked have been removed. (:pr:`1693`) - - -Changes -------- - -- Cocotb no longer supports Python 2, at least Python 3.5 is now required. - Users of Python 2.7 can still use cocotb 1.3, but are heavily encouraged to update. - It is recommended to use the latest release of Python 3 for improved performance over older Python 3 versions. (:pr:`767`) -- Mentor Questa, Aldec Riviera-PRO and GHDL are now started in the directory containing the Makefile and also save :file:`results.xml` there, bringing them in line with the behavior used by other simulators. (:pr:`1598`) (:pr:`1599`) (:pr:`1063`) -- Tests are now evaluated in order of their appearance in the :envvar:`MODULE` environment variable, their stage, and the order of invocation of the :class:`cocotb.test` decorator within a module. (:pr:`1380`) -- All libraries are compiled during installation to the ``cocotb/libs`` directory. - The interface libraries ``libcocotbvpi`` and ``libcocotbvhpi`` have been renamed to have a ``_simulator_name`` postfix. - The ``simulator`` module has moved to :mod:`cocotb.simulator`. - The ``LD_LIBRARY_PATH`` environment variable no longer needs to be set by the makefiles, as the libraries now discover each other via ``RPATH`` settings. (:pr:`1425`) -- Cocotb must now be :ref:`installed ` before it can be used. (:pr:`1445`) -- :attr:`cocotb.handle.NonHierarchyIndexableObject.value` is now a list in left-to-right range order of the underlying simulation object. - Previously the list was always ordered low-to-high. (:pr:`1507`) -- Various binary representations have changed type from :class:`str` to :class:`bytes`. These include: - - * :attr:`cocotb.binary.BinaryValue.buff`, which as a consequence means :meth:`cocotb.binary.BinaryValue.assign` no longer accepts malformed ``10xz``-style :class:`str`\ s (which were treated as binary). - * The objects produced by :mod:`cocotb.generators.byte`, which means that single bytes are represented by :class:`int` instead of 1-character :class:`str`\ s. - * The packets produced by the :class:`~cocotb.drivers.avalon.AvalonSTPkts`. - - Code working with these objects may find it needs to switch from creating :class:`str` objects like ``"this"`` to :class:`bytes` objects like ``b"this"``. - This change is a consequence of the move to Python 3. (:pr:`1514`) -- There's no longer any need to set the ``PYTHON_BIN`` makefile variable, the Python executable automatically matches the one cocotb was installed into. (:pr:`1574`) -- The :make:var:`SIM` setting for Aldec Riviera-PRO has changed from ``aldec`` to ``riviera``. (:pr:`1691`) -- Certain methods on the :mod:`cocotb.simulator` Python module now throw a :exc:`RuntimeError` when no simulator is present, making it safe to use :mod:`cocotb` without a simulator present. (:pr:`1843`) -- Invalid values of the environment variable :envvar:`COCOTB_LOG_LEVEL` are no longer ignored. - They now raise an exception with instructions how to fix the problem. (:pr:`1898`) - - -cocotb 1.3.2 -============ - -Released on 08 July 2020 - -Notable changes and bug fixes ------------------------------ - -- Iterating over ``for generate`` statements using VHPI has been fixed. - This bug caused some simulators to crash, and was a regression in version 1.3.1. (:pr:`1882`) - -cocotb 1.3.1 -============ - -Released on 15 March 2020 - -Notable changes and bug fixes ------------------------------ -- The Makefiles for the Aldec Riviera and Cadence Incisive simulators have been fixed to use the correct name of the VHPI library (``libcocotbvhpi``). - This bug prevented VHDL designs from being simulated, and was a regression in 1.3.0. (:pr:`1472`) - -cocotb 1.3.0 -============ - -Released on 08 January 2020 - -This will likely be the last release to support Python 2.7. - -New features ------------- - -- Initial support for the :ref:`sim-verilator` simulator (version 4.020 and above). - The integration of Verilator into cocotb is not yet as fast or as powerful as it is for other simulators. - Please use the latest version of Verilator, and `report bugs `_ if you experience problems. -- New makefile variables :make:var:`COCOTB_HDL_TIMEUNIT` and :make:var:`COCOTB_HDL_TIMEPRECISION` for setting the default time unit and precision that should be assumed for simulation when not specified by modules in the design. (:pr:`1113`) -- New ``timeout_time`` and ``timeout_unit`` arguments to :func:`cocotb.test`, for adding test timeouts. (:pr:`1119`) -- :func:`cocotb.triggers.with_timeout`, for a shorthand for waiting for a trigger with a timeout. (:pr:`1119`) -- The ``expect_error`` argument to :func:`cocotb.test` now accepts a specific exception type. (:pr:`1116`) -- New environment variable :envvar:`COCOTB_RESULTS_FILE`, to allow configuration of the xunit XML output filename. (:pr:`1053`) -- A new ``bus_separator`` argument to :class:`cocotb.drivers.BusDriver`. (:pr:`1160`) -- A new ``start_high`` argument to :meth:`cocotb.clock.Clock.start`. (:pr:`1036`) -- A new :data:`cocotb.__version__` constant, which contains the version number of the running cocotb. (:pr:`1196`) - -Notable changes and bug fixes ------------------------------ - -- ``DeprecationWarning``\ s are now shown in the output by default. -- Tracebacks are now preserved correctly for exceptions in Python 2. - The tracebacks in all Python versions are now a little shorter. -- :func:`cocotb.external` and :func:`cocotb.function` now work more reliably and with fewer race conditions. -- A failing ``assert`` will be considered a test failure. Previously, it was considered a test *error*. -- :meth:`~cocotb.handle.NonConstantObject.drivers` and :meth:`~cocotb.handle.NonConstantObject.loads` now also work correctly in Python 3.7 onwards. -- :class:`cocotb.triggers.Timer` can now be used with :class:`decimal.Decimal` instances, allowing constructs like ``Timer(Decimal('1e-9'), units='sec')`` as an alternate spelling for ``Timer(100, units='us')``. (:pr:`1114`) -- Many (editorial) documentation improvements. - -Deprecations ------------- - -- ``cocotb.result.raise_error`` and ``cocotb.result.create_error`` are deprecated in favor of using Python exceptions directly. - :class:`~cocotb.result.TestError` can still be used if the same exception type is desired. (:pr:`1109`) -- The ``AvalonSTPktsWithChannel`` type is deprecated. - Use the ``report_channel`` argument to :class:`~cocotb.monitors.avalon.AvalonSTPkts` instead. -- The ``colour`` attribute of log objects like ``cocotb.log`` or ``some_coro.log`` is deprecated. - Use :func:`cocotb.utils.want_color_output` instead. (:pr:`1231`) - -Other news ----------- - -- cocotb is now packaged for Fedora Linux and available as `python-cocotb `_. (`Fedora bug #1747574 `_) (thanks Ben Rosser) - -cocotb 1.2.0 -============ - -Released on 24 July 2019 - -New features ------------- - -- cocotb is now built as Python package and installable through pip. (:pr:`517`, :pr:`799`, :pr:`800`, :pr:`803`, :pr:`805`) -- Support for ``async`` functions and generators was added (Python 3 only). Please have a look at :ref:`async_functions` for an example how to use this new feature. -- VHDL block statements can be traversed. (:pr:`850`) -- Support for Python 3.7 was added. - -Notable changes and bug fixes ------------------------------ - -- The heart of cocotb, its scheduler, is now even more robust. Many small bugs, inconsistencies and unreliable behavior have been ironed out. -- Exceptions are now correctly propagated between coroutines, giving users the "natural" behavior they'd expect with exceptions. (:pr:`633`) -- The ``setimmediatevalue()`` function now works for values larger than 32 bit. (:pr:`768`) -- The documentation was cleaned up, improved and extended in various places, making it more consistent and complete. -- Tab completion in newer versions of IPython is fixed. (:pr:`825`) -- Python 2.6 is officially not supported any more. cocotb supports Python 2.7 and Python 3.5+. -- The cocotb GitHub project moved from ``potentialventures/cocotb`` to ``cocotb/cocotb``. - Redirects for old URLs are in place. - -Deprecations ------------- - -- The `bits` argument to :class:`~cocotb.binary.BinaryValue`, which is now called `n_bits`. -- The `logger` attribute of log objects like ``cocotb.log`` or ``some_coro.log``, which is now just an alias for ``self``. -- The ``cocotb.utils.get_python_integer_types`` function, which was intended to be private. - -Known issues ------------- - -- Depending on your simulation, cocotb 1.2 might be roughly 20 percent slower than cocotb 1.1. - Much of the work in this release cycle went into fixing correctness bugs in the scheduler, sometimes at the cost of performance. - We are continuing to investigate this in issue :issue:`961`. - Independent of the cocotb version, we recommend using the latest Python 3 version, which is shown to be significantly faster than previous Python 3 versions, and slightly faster than Python 2.7. - -Please have a look at the `issue tracker `_ for more outstanding issues and contribution opportunities. - -cocotb 1.1 -========== - -Released on 24 January 2019. - -This release is the result of four years of work with too many bug fixes, improvements and refactorings to name them all. -Please have a look at the release announcement `on the mailing list `_ for further information. - -cocotb 1.0 -========== - -Released on 15 February 2015. - -New features ------------- - -- FLI support for ModelSim -- Mixed Language, Verilog and VHDL -- Windows -- 300% performance improvement with VHPI interface -- WaveDrom support for wave diagrams. - - -cocotb 0.4 -========== - -Released on 25 February 2014. - - -New features ------------- - -- Issue :issue:`101`: Implement Lock primitive to support mutex -- Issue :issue:`105`: Compatibility with Aldec Riviera-Pro -- Issue :issue:`109`: Combine multiple :file:`results.xml` into a single results file -- Issue :issue:`111`: XGMII drivers and monitors added -- Issue :issue:`113`: Add operators to ``BinaryValue`` class -- Issue :issue:`116`: Native VHDL support by implementing VHPI layer -- Issue :issue:`117`: Added AXI4-Lite Master BFM - -Bugs fixed ----------- - -- Issue :issue:`100`: Functional bug in endian_swapper example RTL -- Issue :issue:`102`: Only 1 coroutine wakes up of multiple coroutines wait() on an Event -- Issue :issue:`114`: Fix build issues with Cadence IUS simulator - -New examples ------------- - -- Issue :issue:`106`: TUN/TAP example using ping - - -cocotb 0.3 -========== - -Released on 27 September 2013. - -This contains a raft of fixes and feature enhancements. - - -cocotb 0.2 -========== - -Released on 19 July 2013. - -New features ------------- - -- Release 0.2 supports more simulators and increases robustness over 0.1. -- A centralized installation is now supported (see documentation) with supporting libraries build when the simulation is run for the first time. - - -cocotb 0.1 -========== - -Released on 9 July 2013. - -- The first release of cocotb. -- Allows installation and running against Icarus, VCS, Aldec simulators. diff --git a/documentation/source/rescap.png b/documentation/source/rescap.png deleted file mode 100644 index 1fdc9532b14f7a87aa952b8d46da3b8e01dcc0d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76553 zcmdqJbyQXF_b$3oDFsA9T0#&JX{Eab0Ra&NX;8Ymk?s@;X+#MLrCS;V6hx%E8>IWp z_4$5(=XdV!o^j6~_ndLZxO)uUy5rqzz3YAFeCG2!a|J6ZNaJ25yNW`gaAh7#D5FqU z(Bsj_@ZFc#+3m@`e2~ ztJn5UdbY1n7JBy9=2rIRCi>KluWap1tSle0u(Pl;Q5)OaTl2B9{^$EFR<=g0_^*jc zQ7CGZjD)C)bMo4_i{|fvbK$K)Bkx4LKs3V}SCk|kW8IQeY2l3D&Ep~n~hH!jY?I|eysBF4XqhINt>2vKwh|SK^3rC^bXXa8+eQIQ2WU`&m zqWeUe$rT~1A1+>WG=8WX|9&iYuJEJo{Oge^jY~i;@$ZLJ0vpZ0U*~A@!~XZjqi@j5 z1^xRG#-=Cs?++XLVH5oO!*^)q67c{1X;Qr=zkh$Y1MB}EL%aGA-nup5goT_q?xlau zDKgh(XF)WE?!$)(YAd%G#fdmUzF|V=@>^pgT9$OAl!Ai7daVp{ zlmo?|H&JjEavX7Q^AVz6egp-tk9M6a8cHFV=S@xy!wYqFy_uHa zt(~1_|7*m>HnUPpnw9t{>#Zr#)AN%}5_bJ7wRLsT@7@K>cg1I!4HiC@k$Dpy9xht@ z<;xdqTid2umz}7jq!4&__E+|k#|jDobTSSn3$TXz2%g*&3p&R0fy1Y)hgsG;KRqJh zwWX^++kffd_A^z`X0{ENLckgSs{bVvwV%l1j})7o1^Hw_HzE{15fO%!m6iEOY3_)f zgtYYRQePI2!wO;2Ba=)kVzH%I^1FBM7MGWEkyKbs3Q0&w>F=!!IB&NSqb;c>)b9Rz zVmw;TyuUGi?ZJZwsBeK(ov_)|7#m9fGp^6E*%C~!*Lf3_!13xT6EpLd zS6%N;+%6-#Yvc02mSNRL`S&Xqp6917STy*=#F*UN+`htA4QE`askU%(lsEb{B5H1K zVrGp}k;jj*UiT#V6Wmi+TC8^7zJp4Cp0Da8;3Xr2z3?OD8Wj~41L4)m>guMR9^9&` zs*bL%hrB}6)ZeDcQc`XU2nx!ZrXqWMfc_5>BwV|nYfWbin4+PI>h{T84>m0J4;Omg zUq)5iFZ*00W|?ZI@@n~0@G?3o$~#i%80+G|>mqP_wtevM^w}qc){t8d*bLfm;0iSL z_1%b}(`yO3aqNto871dGu|!bx^y~}`rK+mBG+eT%6{(i1;=G(4zxC@0YesP~zE-t; zBdj50nVxBWQb?(urC9w_(44?-Q0%;|@A1;aL2-wYWNZTvOto65UfyLOFaFhQr<{{ADf@L>|+i&HzejH^1VI`tH{ZrxfM zt+2u^ih;9QYP9i(c-lv>(En6e5>~~Hn>T&13FyB~PErjP>9kMOx!KHrzeOSJNrA$r zpa?6}s>!q%EmzD{LAD>vW8l!xi!;qM_rKT>XTrEFMs#8S8IP3S|MlxvvCGbD3Vuf% zRBwZqm;2e?ptZfdgtavrEaXrf&m(je7M9nu&+-RgGhhbbk_Y_$t-|!O90y|10)Z_Y zT#f=e64iJ8u<`KPGQdYQ6wK+R>D773Xd-txkCp*wL?E}UN%&75Xy@yAjC~}+c+LGqp** zUIQQOSd8!EHnaAz6=(}v&{r`Le{`&ts*{T?oW@qPgeD^6YEtOh7c(|bVV*vl>B)&o6<_>pD z!s^K>F8jiCnH zG!0s`3Su8TI3m~Y-t#yQAhGjEYoKX6E&llw&iOaKj2M>Eq-bwljqUtZcX#)w)Kt7p z=~GN)3zPf528-JJeyhBF@lrfGJ`h5mv;B%+x1gK#-#^T6d3jbww6vxN${wJmMt-F5 zB*yu3NMkbm{hJ>)Twvy8?R}@=Yn_4gxTd;#b=BV;{7l<7Y89h)cV`XZ%Y1xdiO7g* zmC{l(#}jotE~}1iEMl5k)1433~=#zbdXwp`}>Lu#OZON*97>Te)W{OT~~PZD&zNbkx-=LdX&pnx=f8x zdqg$6+y0b_W4UF$&!tl3+;g(nlyGIY8q<*rzVo6g&-UEWFSiKOaTLQF< z?Q4@(&S&t?ky6oxXj*z?b-h{qS}?hjucbLPHRGdrAhks*K*Evz7p>kGa?pUBc(rh5 zfbRpjoIfU^AyM)1kv=o}STyNnr4kc8@2zE?O2ZR_5<~icib#GG?dBimN~7^hsJT~0 z84|hPs`>t^xgy#MT$&FHX;|+O-<35pMo)jPK@0yb9JHcl<-UHGtGxF+ow0>QbbLJK z!KwS>sHpg~KxKY@<5wJSM}}#NsHrS@hdUm0Nay+rmcAomaIlxq7Nrr8l||7+sVN32 zOG_IUe75(Hl(g>ZlGavYm%W{0zpkLr@qi`Zr6}58Sy{!Ewa;c1#Uk1=*aDnnkHyl{ zO9@SlZc$hGj5OUu&wgdi$}B3XPWn6VyW@i=gm2>-pU|U;rHg1`l;vMbR}?9T%1Zx? zv$0}J_%>dAeF39mUhx4T1H-fCF0q5BRq0~ShCg|0Wc&{AGgeCP6B+N&;+8TgFdI}Y zuTd)>9ZD}Q#o_S3bd!$Igj3PYb?MmLLPs0zxdni7oHAa|_&Xr8NK8644sf0x21p`%Eb*O8OSK1$m@Q`K4Xt+ySWW{pb8`qc6D^ zT4aph=i{{Js4#pfXE)&{%~Dj^kNftk75y$3S8sKt3h6VCnz43i?!nGjPOAP7w%xh0 zz13vlHV&e}1@hseW*ln%;hc;iA&Lu`V%pk5WW>Y||0%q1i3sRbRt?L`t*wg#zZTbZJuBD6!#3u~=wT$0FPE&|M6bvc%X0J@l9X&L=*=0Q zq~cm1y^PW<(rPYa^e8-+*Bi|x(A?R9?{WFdJ6BqE$2|z}@boNRC&3=9)r@s~zg1q% zz8oKZ?5;;Xj8<61`*Hm?-#5p={cdSo-4NAnzw|j5Q7~5-jDC z`bem#hDyya<6l;6jBO|nZ>;04_9~8R60;`l4*OO3clD6JZu3)#s8ZZvr-?IQQHfB> zwfE>DYp%9R+l{?(Juqor@U_ErPd&(9SxFjw1>1-i>zAJ^iugT|DuPa{e&vZbOm$1|EajcI+=Oc$Sr0EyWAhi?92HB?09(9Lc+of z;^GTyCHSgaq<6972_$uUtcWZt=YO)$#`ug<_bMuY)QFNs6p>5Tl08G2W zk5%mib=ToiR^--7eo<3nV@7>*6>aCQn}7EAqdq0S$;w7Qt*xCRy@j4H6|OtGhBNw^ zX^4ohX;8A_cB^u>kD-Z4c~Ld{b-Tg<=l`BmH7EZw*Iws{|GV;h2xjYI0c|T}h(3Z$nsF+n=0-AlTov)rtWVGLm9}<%Dqg`5+gLR#mU#J6cW#cLqmi>gkd^%98XqoW=Z z5$#c6K_Vc_{o8P=g#)uEi1A`KZpAN0EatTabPpwU^ofh3>*j(U zw|QGl4|B}%`}?l5+HV#3=4S+kI~A#LTNbe)M90$h-RCUq|3FJq6c=SffR_cI@3P?K zVi!}RD{dnOyYVMB`NiF9+kvdF3`UZ&NGLaanfv)>6F-fyZ?B3!?qTYs+9~R}f4H|M z@BT;tr=gZC{7XvG%sTF;FU<3AK?4>*zFipfvpCnS*IkUe^Q;R~`^|f*5hCKe9N#yMEX>~vT_D0-F+xjGz>}ki1 z8=P0>6B8LmDyZfYKXzWX^Qc<=GyPN4*Jic>!#eexCOWTG_sc=4%`WGlO^rVo2>*VIJ_cRbRze~&W+jGd zvHIZKd;&@G>O?d3QFN?IcF{{G!}C?04vjB22WJ-U+`or;#-c5u?8sHv<1uRf)qhDl zjGP;9wA`XO?tz-zvFhg+Z??9q{Q?5+(9odW=6CcfvKs$`)oc8ym#a5P(9zM++Qz1; z=!X5;HINwXP}Cd)$25mc{-?6O*Z8J5r8I#m4-XCXTV$b#Ax#yeYt+_VT)z$niw3 zlwYrEA=~sjiaVJacEb5$dGu0WRSU(Y3$?1}q#etJj%*whD9+t2v3@wLMuhR&ov(Ct zivAg#oN7DS6u)`tK}kupYGK>=HS@LnPLF>rbh+FM$tfM1bxcNd_O@u8D>{Nzdh!RBxY zW=2MaB4gZAZ^mVX6u#$K&Z>o)*!S+;Gao8OYiw*(WFV|`S3lr=`uMSkq9XoV*}EF$ z=E1;?EVt(ln6|XFHTCxs$Y$S!`V@oP2B?gLMX>M82j>`KLBzF8x<87=O0Zg zoe5l0@s}^6z>UZICk>zoLgR$g8XC69=$?N`sS>?iSTu%>zV`J4azA$lsfM_6t?oVh6s?9cJR z=5Nvn<}xv*+Pb zSP!d6#cdj+da8F&_0oY1Q;-6pPc;q1)}ST5s=?0}8|Hw%v!?6}i=j?fi<|h|G5_i>8)wU%^1)qa-u3mm+ zdOTy~BUFaVtAn(6O>RRq7dX(@=7GTXk#J;1em92*mp-e=5aJNMYo$cPpW zdwP2M`26tD8DZ{#pzi4C5RsC?fYNaHtmJi1(@blq4NwHYrRsh2Mq^-QEmD$eZEbbF zI6FXisQyn+vy`%=fQV=rtFrrIJuQ~ZYlpE}f5I{|GgE9jz=-+={DTiJIhXGGNZDg~ zc}ac!JEy0oD3nG4k;ln?YpS5zRuA_iH5|!i-2o*N6B7!Rot=%4hFn$?VL)sltSG`K z+Dv^z^Q}}Z&_I9kS{cY+`jvf+@G83F z+R&Tk!0QHULtk=oa!~1`s_d`3N!BLn$l*uCMMO}?d#iyyK9_te;Viet?0!xkZqJf% zni5Kdk-T|m^dq4z063pJKt1w1ZTzlyWn?tFnC8`%qm+e00dw05kYsD4W^<-DLwsv@ z_t9fdv~MuaUu@@P-S-C6hU(n7zXAj5dFC3sQwEGNCLTqO;}fg_JU2Ht9;gj2fK@DM z=N;|PqluNfT06nSKLO6`R$@VMauV?2dA1Zvh@`%sCRK3rK3JwZnNJN zzffd#uV0-JujI?K^CA1NyK9do^5N5`K_>>z%z9hNxwGBaUR9OfP7FZG`|#0<6K;&( z`%OxUvCb_5lF57j$c1)Q$iFcxR?Ox8s5 z^nWbi8MxD1TqK#!GkRC!!3;RVI#ZsbaV0>ig;rcwA zl&j|VzYe=tT%hR2) zuAi5Uek9*OwEz#iXQ8B|G~-XKjW32}v%92(CZbvIKCG*0Z(o`q(HpK3`4}@+oER8?u>nTnXH=?7X#jt2TDx@%0J8iO* zY7}%PUz|){_{q@|*`gGgo|+Q34Bpe(SaY^`=eH& zpVB<5r54E_z7Df?aw2~9>eX@ilXwhvARr6|1{OyT2n&84;XCOvK<@>9>uWR{Hhnn zB|n7-w6(Rxvih)S{DQ*52w59Wzjh`+pM3E2>C^2-OsXZF`1ts1z~!@BPl@>2v`11A zqa-vmG{Os`DZ9_csLs|WYg)%HQhzu6F)qEMKg~MRpR(9Zd0{)F$L`pQ=3CyFwn0>K zVFwc;;Tf!PSpP+4?Qpibk^SOy+RvFrPbYjfQ#$Zf-7C|j;5%0;=J5Ms3j`h#tdu^9 zIyq|k-aH$zjsTXCu?iZKGW?h;gEFz{I9J7%sl2BC{GEUNy{8p2AH*{f4lg0Ym@C+9 zFOJx|JF)*R^yrPVP`Jpf${mxP?W9%m+0pB(FrI0O(fjs)dsA#{)7V>OWsnd{;3tPs zgub))#f3S+s{E%(2mXr+i_p)v9z<1eVXJDV?rn4R3;rS^$~4}bAfqa}&+x4$_FlBn zD|8D{A=$1jn;lpBWC{vhr7GKWdUIM@z3HVFSq3j54@TVPpUE$(7aJ_pvH@9OZKUkl zH6n;pZ{NO!zhqy9|D+-%&Aq6R%@)(u)h)}v8P+*(3y@?*hxpd5X~=IR+?EvdatY~H zML9X%2J@V4EiLJ4McP<6I1*_>9tg!Ou+#kzWTGS-%CyU*qaPG>_wGTuBZuJ_ zSXlm8y~%S;X)B6^rM2!5M#g!SfL`v~*H>NXiVRV}FW;b*5>Y*RxRUtLhz%s0SYE3l z+0L#maY!A&w(g$2d-o3O`gQK%cLo*~xFQvd!Ibi|{M@bGA7-((9mNtNY3s*Q0`wiw z1Ih;q9=q+G7+5>0%!CUuqoEA=kMs_WPPSUDi2Dj&(V7c*;@&G-(CeFYNHcTGzI7)l zuh_VY6v*(Yb@Ol!;*za2S~a#$UeR~Jd{$9*saWExvw9E%9Z2b z!6ns?A+cE+C+9FTn=90@N+49Pr{IveG-q29d?Q{>yRG0=mwXHrrJem#oX)dpVR{d) zg655c)8h3p(v>IiGzS*?*33>TC4aZPEez>o_o+lCyuAFhR8&&x4(z=Q9UY4~tEX!T zfJGex)a)`#|6S#WqT%AVf@t!!%uMQ!ZbDwQ5e@fE_!N&N{XVT29 zEHqSMVfDSIb8~awdV1DV7wV|~{P}|+{F9J~=n|^_aORrtdt=nGgd~UsTaZU)AfhzE z=JwHR4#+V2nfju_GUfbZH6NAdAKmz9-qI%~anfdw&# zLdboHj>gVyva@P$&_dM=phLuW(Qh(w*U}S2Q3q0OSL72=T;y zYL${r7v+aPRO|8(2>C#*8pr9ZNFmpB-u&2<6kL?U->MHM52s964T*EN`^rly)fEG% zSusD8jZ#|fS2J^|**(EIudCR5PU9(I#x#)>a{VIys<0Zl`?)^PCBe15b|#N{PN75I zTMz4nL&j!oA32t{eV%yZOye`ELVgD>tpZj7Q53tnm_%e?P2ttt7(ykTnvY&SVM1se z_wdvosAjI6y3ozP9$9Gm4==#{*}!@K(#5?Q^}Ni3LrZ_G-pR_aNE=sP?*`MFxqM`nbbz6r(rg?hN_ToGsO_h~<>5jt zknsYutO%T?Bpbhv*AZo1K?ER>foDgp8d?EFu)}scla~YZG&MEl{q`-P?*N`{6-Cnb zPrIa0YEdOU>Km_xi#@eATmlKBx@ZWHGy;J`iCI@xbrWXcThq;f28UbIBpilV$GgkE zYis5ZPi6qZ@wn}O9)3>mw!hZ+?ak%wrfaOI-T?t#w|yobUtF)3bzT>qv1T|}Sk4Uj zPr6Njl6gdiYdKc=CWoI++QZ!)*{pJ@0;G^&+K6@B-{d~~bm!3{!hF>NF&CGre-8}X zythb47$xHB%4a#w!JJeDDpVMy;K-B%D1!On(zz-#n_d_EHwg%kcz6W^BNLL<@p*bW z_1a{^q~VWIz}?f!%fV+S2U(EoQ1Y}ZlAd=6J9CMJ$QPzojiB56*D@O*tz zpJte(=*jh);AenpL&(?Yx|>bB`A*fn$isX;x-5Q%9=G_(wxunQ5vDl z;mwtNHTjSnJUd&T?mulGyjN1nzrPUPBm68NVEXF$V~5~qH&vI*W`p_nzSbXJLVsD& zw7ls{^bo&ketv!&Dj^D(6)8_oA$@)QT@Q{#93)5k?mZW7 zL`1|=|EKHUzkj!0>ZL)hHWU!8F?XL#!MuR@C{73MPyrcq#@wy+Jgb&zn)^H7e2YvOTd zr5tt*Ej>L3472n*mjGE$@W$cE2|2~igxe+!H8qO)WqAwCTGadZW;aKH&e(lf6qKW< zFN&Q=MH%T*qb`8rn)H7?D)dzf`@2LXVoL?)Lus>>?r`1OR;|IRX;yJYCb+P@SsTa zf0RdCN*zJa8nu7`1(ZzU8X9Csv0LcPWxZ&gLyA4(7s#<$@;zLXePiR$=b#{hOw*h;V3|tw=#nN_}za^I@bQ?ShzYmOnhW&lvtoT6n^Ec30+3gnOVDlng z6&~lUbkDsAsjS8aMYTS#k&%T5IHXtDrVWfT{EllPdU`aFtNO|f+KR(JgpeI>7Ad#?VYjrR{06qim`kP8xY}zu)sfod6c9T-dj^(1tbVfY~3^mq`=K3x5Mo;IghV|+1 z|Goag#aZNaLG2>h9g#ikD2zmsT!r~}6f{>LN$O2zVgBVh#frzCJ$m~n&p}U54{(NC zI`8rRdMM08;=0>T5BK)Mdn=*d(@4e|eu1(jq$Dy>Qsb~XJ>1^Aed&ha6(r8<_Nm=* z4ca(}7-k4&4;yH$?r7UrY z(dmQ_k1XgE`@NDYANrqy)V#^5AX91}1Yr}rKFK2r_xKIyR=D>N0QU3I!AhH1%%mEw zFJDnN2Dtu#jAh;}WX%#PcARlik*<2$~6h$v{qv(LB z@q6>-tHP6k)f#^3FJCFEJNI*O+$1V2>=@mmC7}OJE%3eb&B$-4@Q?A{1T96k_+bxV zXY`4E)dgx>Zrq_PNDd>C*GiO>Bq$RMDm#;DWb{&=W2mZ6_vpUu-l%(c{^sv)mnO4v zyrEYCTQn2%^x;}b$7m_`M#*c@JojTn)=(1v2BYF;oaK{H-%z@1hpt{QTEWfS#^;3w zb<+!tq^DuE+ZcsEl1;wGNNkK=DZjB^rSSnzCgw`UnOS1(Z2FrH?Xs)>Zu{Tr%?6LJ zg&}SX%5A)ND*>wctQe0ao~HiomP3{~Jo%Q7qN|^1)dqc7GuKQZ2n_W&P9ijjlesA{ zq3-=!*y2r8Cw`W4<4@Vbr(r{-c)o)^M)$FjSX z4=1!|dA)=P&*kMS20sIPgo(+J=6Y3(gSlgc%Yo5cqvG{=bU>agE`dnAMAu|Bt_%=) z7eB=p>v4fEIJyWy#xtq)&E{*&ERu*Mo@M4*C?C`e4QM2NAhz|+M1)INfG9KWh(7HiH6c8zVUdbYE$%c@yPzjXPH#;-ky>~AQqE$y{ z=bPBr*m{p2$-D?n))q;1U0S-`H7qK~G{mVGxq{7p*h-fTYC|O6#?yL@vI69

      Y$H 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 diff --git a/documentation/source/rescap.rst b/documentation/source/rescap.rst deleted file mode 100644 index 00cbe4e5..00000000 --- a/documentation/source/rescap.rst +++ /dev/null @@ -1,130 +0,0 @@ -############################### -The cocotb ``rescap`` Testbench -############################### - -.. versionadded:: 1.5 - -This is the testbench :mod:`test_rescap` for the design ``rescap`` showing -how cocotb can be used in an analog-mixed signal (AMS) simulation. - -.. note:: The analog probe module used in this testcase is currently only implemented for the - Cadence Incisive and Xcelium simulators (with the AMS option available). - Help is appreciated to provide equivalent modules for other simulators. - -****************** -Overview Schematic -****************** - -.. image:: diagrams/svg/example_rescap.svg - :width: 100% - - -********** -The Design -********** - -The design consists of a resistor and capacitor model (both written in Verilog-AMS) connected -in series in a SystemVerilog module: - -.. literalinclude:: ../../examples/mixed_signal/hdl/rescap.sv - :caption: rescap.sv - :language: systemverilog - :start-at: // the design-under-test - - -************* -The Testbench -************* - -The testbench consists of both an :term:`HDL` part and a Python/cocotb part. -While having HDL as part of the testbench is not common with cocotb, it is perfectly possible. - -The HDL part of the Testbench -============================= - -The testbench :term:`HDL` part is written in SystemVerilog and instantiates the design described above -as ``i_rescap``. -It also contains a probe module for analog values as instance ``i_analog_probe`` — -imagine this being a multimeter that you quickly connect to different nodes in the design, -measuring either voltage or current. - -.. literalinclude:: ../../examples/mixed_signal/hdl/tb_rescap.sv - :caption: tb_rescap.sv - :language: systemverilog - :start-at: import nettypes_pkg - -The probe module can capture voltage and current of a node specified by ``node_to_probe`` -(a string in this module containing a hierarchical path). -The capturing occurs whenever there is an edge on the logic signals -``probe_voltage_toggle`` or ``probe_current_toggle``. -The captured values can be read on real-value signals ``voltage`` and ``current`` in this module. - -Here is the capture code for ``voltage`` with the "user-interface" highlighted: - -.. literalinclude:: ../../examples/mixed_signal/hdl/analog_probe_cadence.sv - :caption: analog_probe_cadence.sv - :language: systemverilog - :start-at: var string node_to_probe - :end-at: end // probe_voltage - :emphasize-lines: 1-4 - :dedent: 2 - -The cocotb part of the Testbench -================================ - -test_rescap_minimalist ----------------------- - -This is a very minimalist testcase. -To run it, call: - -.. code-block:: bash - - make SIM=xcelium TOPLEVEL=tb_rescap MODULE=test_rescap_minimalist - - -The testcase supplies ``vdd``, -measures some pairs of voltage and current at ``vout`` -(same as the ``p`` terminal of the capacitor), -spaced 50 ns apart, -and prints the values as shown below. - -.. code-block:: bash - - 50.00ns INFO tb_hdl.i_analog_probe@tb_rescap.i_rescap.vout=2.03e-14 V -2.996e-07 A - 100.00ns INFO tb_hdl.i_analog_probe@tb_rescap.i_rescap.vout=3.029 V 2.67e-18 A - 150.00ns INFO tb_hdl.i_analog_probe@tb_rescap.i_rescap.vout=4.862 V 3.574e-18 A - 200.00ns INFO tb_hdl.i_analog_probe@tb_rescap.i_rescap.vout=5.975 V 6.285e-18 A - 250.00ns INFO tb_hdl.i_analog_probe@tb_rescap.i_rescap.vout=6.652 V 6.171e-18 A - 300.01ns INFO tb_hdl.i_analog_probe@tb_rescap.i_rescap.vout=7.063 V 6.033e-18 A - -There is no current flowing out of the ``vout`` terminal, -so the current measurement always yields zero -(within the accuracy of the analog solver). - -test_rescap ------------ - -This is a more advanced testcase. - -.. note:: This testcase depends on `matplotlib `_. - -The cocotb part of the testbench provides functions to: - -* do the sampling of voltage and current of a given node (:meth:`~test_rescap.ResCap_TB.get_sample_data()`), -* plot the sampled data to a file (:meth:`~test_rescap.ResCap_TB.plot_data()`). - -The testcase supplies first a positive voltage to the circuit at ``vdd``, followed by a negative voltage, -thus charging the capacitor in opposite directions. -The following graph shows the charge curve. - -.. image:: rescap.png - -There is no current flowing out of this output voltage terminal, -so the current measurement always yields zero. - -To run this testcase, call: - -.. code-block:: bash - - make SIM=xcelium TOPLEVEL=tb_rescap MODULE=test_rescap diff --git a/documentation/source/roadmap.rst b/documentation/source/roadmap.rst deleted file mode 100644 index 01504db0..00000000 --- a/documentation/source/roadmap.rst +++ /dev/null @@ -1,18 +0,0 @@ -******* -Roadmap -******* - -cocotb is under development; -however there is no "master plan". -We depend upon users to contribute new features and improvements -while maintainers focus mostly on reviewing PRs, project management, and bugfixes. -Releases will occur approximately every 6 months. - -We use GitHub issues to track our pending tasks. -Take a look at the `open feature list `_ to see the work that's lined up, -or the `list of good first issues `_ to get your feet wet. - -If you have a GitHub account you can also `raise an enhancement request `_ to suggest new features. - -You may be interested in discussions about `planned deprecations `_ -or review the `list of already applied deprecations `_. diff --git a/documentation/source/rotating_logger.rst b/documentation/source/rotating_logger.rst deleted file mode 100644 index d6f9c8a3..00000000 --- a/documentation/source/rotating_logger.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. _rotating-logger: - -****************** -Rotating Log Files -****************** - -The following is an example of how to support rotation of log files. -It will keep the newest 3 files which are at most 5 MiB in size. - -See also :ref:`logging-reference-section`, -and the Python documentation for :class:`logging.handlers.RotatingFileHandler`. - -.. code-block:: python3 - - from logging.handlers import RotatingFileHandler - from cocotb.log import SimLogFormatter - - root_logger = logging.getLogger() - - # undo the setup cocotb did - for handler in root_logger.handlers: - root_logger.removeHandler(handler) - handler.close() - - # do whatever configuration you want instead - file_handler = RotatingFileHandler("rotating.log", maxBytes=(5 * 1024 * 1024), backupCount=2) - file_handler.setFormatter(SimLogFormatter()) - root_logger.addHandler(file_handler) diff --git a/documentation/source/simulator_support.rst b/documentation/source/simulator_support.rst deleted file mode 100644 index 541c1f79..00000000 --- a/documentation/source/simulator_support.rst +++ /dev/null @@ -1,386 +0,0 @@ -.. _simulator-support: - -***************** -Simulator Support -***************** - -This page lists the simulators that cocotb can be used with -and documents specifics, limitations, workarounds etc. - - -.. _sim-icarus: - -Icarus Verilog -============== - -In order to use this simulator, set :make:var:`SIM` to ``icarus``: - -.. code-block:: bash - - make SIM=icarus - -.. _sim-icarus-accessing-bits: - -Accessing bits in a vector --------------------------- - -Accessing bits of a vector directly was not possible until (including) version 10.3: - -.. code-block:: python3 - - dut.stream_in_data[2] <= 1 - -See also https://github.com/steveicarus/iverilog/issues/323. - -.. _sim-icarus-waveforms: - -Waveforms ---------- - -To get waveforms in VCD format some Verilog code must be added -to the top component as shown in the example below: - -.. code-block:: verilog - - module button_deb( - input clk, - input rst, - input button_in, - output button_valid); - - //... Verilog module code here - - // the "macro" to dump signals - `ifdef COCOTB_SIM - initial begin - $dumpfile ("button_deb.vcd"); - $dumpvars (0, button_deb); - #1; - end - `endif - endmodule - -.. _sim-icarus-issues: - -Issues for this simulator -------------------------- - -* `All issues with label category:simulators:icarus `_ - - -.. _sim-verilator: - -Verilator -========= - -In order to use this simulator, set :make:var:`SIM` to ``verilator``: - -.. code-block:: bash - - make SIM=verilator - -Verilator converts Verilog code to C++ code that is compiled. -It does not support VHDL. -One major limitation compared to standard Verilog simulators is that it does not support delayed assignments. - -To run cocotb with Verilator, you need ``verilator`` in your PATH. - -.. note:: - - cocotb requires Verilator 4.106 or later. - -.. versionadded:: 1.3 - -.. versionchanged:: 1.5 Improved cocotb support and greatly improved performance when using a higher time precision. Verilator 4.106 or later is required. - -Coverage --------- - -To enable :term:`HDL` code coverage, add Verilator's coverage option(s) to the :make:var:`EXTRA_ARGS` make variable, for example: - - .. code-block:: make - - EXTRA_ARGS += --coverage - -This will result in coverage data being written to ``coverage.dat``. - -.. _sim-verilator-waveforms: - -Waveforms ---------- - -To get waveforms in VCD format, add Verilator's trace option(s) to the -:make:var:`EXTRA_ARGS` make variable, for example in a Makefile: - - .. code-block:: make - - EXTRA_ARGS += --trace --trace-structs - -To set the same options on the command line, use ``EXTRA_ARGS="--trace --trace-structs" make ...``. -A VCD file named ``dump.vcd`` will be generated in the current directory. - -Verilator can produce waveform traces in the FST format, the native format of GTKWave. -FST traces are much smaller and more efficient to write, but require the use of GTKWave. - -To enable FST tracing, add `--trace-fst -CFLAGS -DVM_TRACE_FST=1` to `EXTRA_ARGS` as shown below. -For Verilator 4.102 and above, the `-CFLAGS -DVM_TRACE_FST=1` argument is no longer necessary. - - .. code-block:: make - - EXTRA_ARGS += --trace-fst --trace-structs -CFLAGS -DVM_TRACE_FST=1 - -The resulting file will be ``dump.fst`` and can be opened by ``gtkwave dump.fst``. - -.. _sim-verilator-issues: - -Issues for this simulator -------------------------- - -* `All issues with label category:simulators:verilator `_ - - -.. _sim-vcs: - -Synopsys VCS -============ - -In order to use this simulator, set :make:var:`SIM` to ``vcs``: - -.. code-block:: bash - - make SIM=vcs - -cocotb currently only supports :term:`VPI` for Synopsys VCS, not :term:`VHPI`. - -.. _sim-vcs-issues: - -Issues for this simulator -------------------------- - -* `All issues with label category:simulators:vcs `_ - - -.. _sim-aldec: -.. _sim-riviera: - -Aldec Riviera-PRO -================= - -In order to use this simulator, set :make:var:`SIM` to ``riviera``: - -.. code-block:: bash - - make SIM=riviera - -.. note:: - - On Windows, do not install the C++ compiler, i.e. unselect it during the installation process of Riviera-PRO. - (A workaround is to remove or rename the ``mingw`` directory located in the Riviera-PRO installation directory.) - -.. deprecated:: 1.4 - - Support for Riviera-PRO was previously available with ``SIM=aldec``. - -The :envvar:`LICENSE_QUEUE` environment variable can be used for this simulator – -this setting will be mirrored in the TCL ``license_queue`` variable to control runtime license checkouts. - -.. _sim-aldec-issues: - -Issues for this simulator -------------------------- - -* `All issues with label category:simulators:riviera `_ - - -.. _sim-activehdl: - -Aldec Active-HDL -================ - -In order to use this simulator, set :make:var:`SIM` to ``activehdl``: - -.. code-block:: bash - - make SIM=activehdl - -.. warning:: - - cocotb does not work with some versions of Active-HDL (see :issue:`1494`). - - Known affected versions: - - - Aldec Active-HDL 10.4a - - Aldec Active-HDL 10.5a - -.. _sim-activehdl-issues: - -Issues for this simulator -------------------------- - -* `All issues with label category:simulators:activehdl `_ - - -.. _sim-questa: - -Mentor Questa -============= - -In order to use this simulator, set :make:var:`SIM` to ``questa``: - -.. code-block:: bash - - make SIM=questa - -For more information, see :ref:`sim-modelsim`. - -.. _sim-questa-issues: - -Issues for this simulator -------------------------- - -* `All issues with label category:simulators:questa `_ - - -.. _sim-modelsim: - -Mentor ModelSim -=============== - -In order to use this simulator, set :make:var:`SIM` to ``modelsim``: - -.. code-block:: bash - - make SIM=modelsim - -.. note:: - - In order to use :term:`FLI` (for VHDL), a ``vdbg`` executable from the simulator installation directory needs to be available on the ``PATH`` during cocotb installation. - This is needed to access the proprietary ``mti.h`` header file. - -Any ModelSim PE or ModelSim PE derivatives (like the ModelSim Microsemi, Intel, Lattice Editions) do not support the VHDL :term:`FLI` feature. -If you try to use them with :term:`FLI`, you will see a ``vsim-FLI-3155`` error: - -.. code-block:: bash - - ** Error (suppressible): (vsim-FLI-3155) The FLI is not enabled in this version of ModelSim. - -ModelSim DE and SE (and Questa, of course) support the :term:`FLI`. - -In order to start ModelSim or Questa with the graphical interface and for the simulator to remain active after the tests have completed, set :make:var:`GUI=1`. -If you have previously launched a test without this setting, you might have to delete the :make:var:`SIM_BUILD` directory (``sim_build`` by default) to get the correct behavior. - -.. _sim-modelsim-issues: - -Issues for this simulator -------------------------- - -* `All issues with label category:simulators:modelsim `_ - - -.. _sim-incisive: - -Cadence Incisive -================ - -In order to use this simulator, set :make:var:`SIM` to ``ius``: - -.. code-block:: bash - - make SIM=ius - -For more information, see :ref:`sim-xcelium`. - -.. _sim-incisive-issues: - -Issues for this simulator -------------------------- - -* `All issues with label category:simulators:ius `_ - - -.. _sim-xcelium: - -Cadence Xcelium -=============== - -In order to use this simulator, set :make:var:`SIM` to ``xcelium``: - -.. code-block:: bash - - make SIM=xcelium - -The simulator automatically loads :term:`VPI` even when only :term:`VHPI` is requested. - -.. _sim-xcelium-issues: - -Issues for this simulator -------------------------- - -* `All issues with label category:simulators:xcelium `_ - - -.. _sim-ghdl: - -GHDL -==== - -In order to use this simulator, set :make:var:`SIM` to ``ghdl``: - -.. code-block:: bash - - make SIM=ghdl - -Support is preliminary. -Noteworthy is that despite GHDL being a VHDL simulator, it implements the :term:`VPI` interface. - -.. _sim-ghdl-issues: - -Issues for this simulator -------------------------- - -* `All issues with label category:simulators:ghdl `_ - - -.. _sim-ghdl-waveforms: - -Waveforms ---------- - -To get waveforms in VCD format, set the :make:var:`SIM_ARGS` option to ``--vcd=anyname.vcd``, -for example in a Makefile: - -.. code-block:: make - - SIM_ARGS+=--vcd=anyname.vcd - -The option can be set on the command line, as shown in the following example. - -.. code-block:: bash - - SIM_ARGS=--vcd=anyname.vhd make SIM=ghdl - -A VCD file named ``anyname.vcd`` will be generated in the current directory. - -:make:var:`SIM_ARGS` can also be used to pass command line arguments related to :ref:`other waveform formats supported by GHDL `. - - -.. _sim-cvc: - -Tachyon DA CVC -============== - -In order to use `Tachyon DA `_'s `CVC `_ simulator, -set :make:var:`SIM` to ``cvc``: - -.. code-block:: bash - - make SIM=cvc - -Note that cocotb's makefile is using CVC's interpreted mode. - -.. _sim-cvc-issues: - -Issues for this simulator -------------------------- - -* `All issues with label category:simulators:cvc `_ diff --git a/documentation/source/spelling_wordlist.txt b/documentation/source/spelling_wordlist.txt deleted file mode 100644 index ef4d53bb..00000000 --- a/documentation/source/spelling_wordlist.txt +++ /dev/null @@ -1,91 +0,0 @@ -AMS -Aldec -Altera -Async -Avalon -AXI -Cosimulation -FLI -GPI -Gigabit -GitHub -HDL -Hexdump -IPython -Indices -JUnit -Lite -Microsemi -MinGW -ModelSim -Msys -Questa -Quickstart -README -Roadmap -Synopsys -SystemVerilog -TODO -Ubuntu -VHDL -VHPI -VPI -Verilator -Verilog -WaveDrom -Wiki -XML -Xcelium -backpressure -callgraph -cartesian -cocotb -coroutine -coroutines -dev -diff -docstring -endian -endianness -gotchas -hexdump -instantiation -installable -libs -loopback -macOS -makefile -makefiles -metaclass -microcontroller -msys -multimeter -mutex -namespace -namespaces -pre -prepend -refactorings -runtime -scoreboarded -scoreboarding -simulatormodule -stacktrace -subclassed -subdirectories -subdirectory -swapper -testbench -testbenches -testcase -testcases -timeout -timestep -timesteps -toplevel -trimmable -tuple -tuples -unhandled -utils -packetized diff --git a/documentation/source/testbench_tools.rst b/documentation/source/testbench_tools.rst deleted file mode 100644 index 1b3ee8ae..00000000 --- a/documentation/source/testbench_tools.rst +++ /dev/null @@ -1,218 +0,0 @@ -*************** -Testbench Tools -*************** - -Logging -======= - -Cocotb uses the builtin :mod:`logging` library, with some configuration described in :ref:`logging-reference-section` to provide some sensible defaults. -Each :term:`DUT`, monitor, driver, and scoreboard (as well as any other function using the coroutine decorator) holds a :class:`logging.Logger`, and each can be set to its own logging level. -Within a :term:`DUT`, each hierarchical object can also have individual logging levels set. - -When logging :term:`HDL` objects, beware that ``_log`` is the preferred way to use -logging. This helps minimize the change of name collisions with an :term:`HDL` log -component with the Python logging functionality. - -Log printing levels can also be set on a per-object basis. - -.. code-block:: python3 - - class EndianSwapperTB(object): - - def __init__(self, dut, debug=False): - self.dut = dut - self.stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) - self.stream_in_recovered = AvalonSTMonitor(dut, "stream_in", dut.clk, - callback=self.model) - - # Set verbosity on our various interfaces - level = logging.DEBUG if debug else logging.WARNING - self.stream_in.log.setLevel(level) - self.stream_in_recovered.log.setLevel(level) - self.dut.reset_n._log.setLevel(logging.DEBUG) - -And when the logging is actually called - -.. code-block:: python3 - - class AvalonSTPkts(BusMonitor): - ... - @coroutine - def _monitor_recv(self): - ... - self.log.info("Received a packet of %d bytes" % len(pkt)) - - class Scoreboard(object): - ... - def add_interface(self): - ... - self.log.info("Created with reorder_depth %d" % reorder_depth) - - class EndianSwapTB(object): - ... - @cocotb.coroutine - def reset(): - self.dut._log.debug("Resetting DUT") - - -will display as something like - -.. code-block:: bash - - 0.00ns INFO cocotb.scoreboard.endian_swapper_sv scoreboard.py:177 in add_interface Created with reorder_depth 0 - 0.00ns DEBUG cocotb.endian_swapper_sv .._endian_swapper.py:106 in reset Resetting DUT - 160000000000000.00ns INFO cocotb.endian_swapper_sv.stream_out avalon.py:151 in _monitor_recv Received a packet of 125 bytes - - -Buses -===== - -Buses are simply defined as collection of signals. The :class:`.Bus` class -will automatically bundle any group of signals together that are named similar -to ``dut.``. For instance, - -.. code-block:: python3 - - dut.stream_in_valid - dut.stream_in_data - -have a bus name of ``stream_in``, a separator of ``_``, and signal names of -``valid`` and ``data``. A list of signal names, or a dictionary mapping attribute -names to signal names is also passed into the :class:`.Bus` class. Buses can -have values driven onto them, be captured (returning a dictionary), or sampled -and stored into a similar object. - -.. code-block:: python3 - - stream_in_bus = Bus(dut, "stream_in", ["valid", "data"]) # '_' is the default separator - - -Driving Buses -============= - -Examples and specific bus implementation bus drivers (AMBA, Avalon, XGMII, and -others) exist in the :class:`.Driver` class enabling a test to append -transactions to perform the serialization of transactions onto a physical -interface. Here is an example using the Avalon bus driver in the ``endian_swapper`` -example: - -.. code-block:: python3 - - class EndianSwapperTB(object): - - def __init__(self, dut, debug=False): - self.dut = dut - self.stream_in = AvalonSTDriver(dut, "stream_in", dut.clk) - - async def run_test(dut, data_in=None, config_coroutine=None, idle_inserter=None, - backpressure_inserter=None): - - cocotb.fork(Clock(dut.clk, 5000).start()) - tb = EndianSwapperTB(dut) - - await tb.reset() - dut.stream_out_ready <= 1 - - if idle_inserter is not None: - tb.stream_in.set_valid_generator(idle_inserter()) - - # Send in the packets - for transaction in data_in(): - await tb.stream_in.send(transaction) - - -Monitoring Buses -================ - -For our testbenches to actually be useful, we have to monitor some of these -buses, and not just drive them. That's where the :class:`.Monitor` class -comes in, with pre-built monitors for Avalon and XGMII buses. The -Monitor class is a base class which you are expected to derive for your -particular purpose. You must create a :any:`_monitor_recv()` function which is -responsible for determining 1) at what points in simulation to call the -:any:`_recv()` function, and 2) what transaction values to pass to be stored in the -monitors receiving queue. Monitors are good for both outputs of the :term:`DUT` for -verification, and for the inputs of the :term:`DUT`, to drive a test model of the :term:`DUT` -to be compared to the actual :term:`DUT`. For this purpose, input monitors will often -have a callback function passed that is a model. This model will often generate -expected transactions, which are then compared using the :class:`.Scoreboard` -class. - -.. code-block:: python3 - - # ============================================================================== - class BitMonitor(Monitor): - """Observes single 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) - - async def _monitor_recv(self): - clkedge = RisingEdge(self.clock) - - while True: - # Capture signal at rising edge of clock - await 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): - - self.dut = dut - - # 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 ] - - # 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) - - -Tracking testbench errors -========================= - -The :class:`.Scoreboard` class is used to compare the actual outputs to -expected outputs. Monitors are added to the scoreboard for the actual outputs, -and the expected outputs can be either a simple list, or a function that -provides a transaction. Here is some code from the ``dff`` example, similar to -above with the scoreboard added. - -.. code-block:: python3 - - class DFF_TB(object): - def __init__(self, dut, init_val): - self.dut = dut - - # 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) diff --git a/documentation/source/triggers.rst b/documentation/source/triggers.rst deleted file mode 100644 index fccd5cf0..00000000 --- a/documentation/source/triggers.rst +++ /dev/null @@ -1,71 +0,0 @@ -******** -Triggers -******** - -Triggers are used to indicate when the cocotb scheduler should resume coroutine execution. -To use a trigger, a coroutine should :keyword:`await` it. -This will cause execution of the current coroutine to pause. -When the trigger fires, execution of the paused coroutine will resume: - -.. code-block:: python3 - - async def coro(): - print("Some time before the edge") - await RisingEdge(clk) - print("Immediately after the edge") - -.. _simulator-triggers: - -Simulator Triggers -================== - -Signals -------- - -.. autoclass:: cocotb.triggers.Edge(signal) - -.. autoclass:: cocotb.triggers.RisingEdge(signal) - -.. autoclass:: cocotb.triggers.FallingEdge(signal) - -.. autoclass:: cocotb.triggers.ClockCycles - - -Timing ------- - -.. autoclass:: cocotb.triggers.Timer - -.. autoclass:: cocotb.triggers.ReadOnly() - -.. autoclass:: cocotb.triggers.ReadWrite() - -.. autoclass:: cocotb.triggers.NextTimeStep() - - -Python Triggers -=============== - -.. autoclass:: cocotb.triggers.Combine - -.. autoclass:: cocotb.triggers.First - -.. autoclass:: cocotb.triggers.Join(coroutine) - :members: retval - - -Synchronization ---------------- - -These are not :class:`Trigger`\ s themselves, but contain methods that can be used as triggers. -These are used to synchronize coroutines with each other. - -.. autoclass:: cocotb.triggers.Event - :members: - :member-order: bysource - -.. autoclass:: cocotb.triggers.Lock - :members: - :member-order: bysource - -.. autofunction:: cocotb.triggers.with_timeout diff --git a/documentation/source/troubleshooting.rst b/documentation/source/troubleshooting.rst deleted file mode 100644 index d82b0e5d..00000000 --- a/documentation/source/troubleshooting.rst +++ /dev/null @@ -1,173 +0,0 @@ -*************** -Troubleshooting -*************** - -Simulation Hangs -================ - -Did you directly call an ``async def`` function without using :keyword:`await`; -or a :class:`~cocotb.coroutine` without using :keyword:`yield`? - -If you want to exit cocotb and the simulator using :kbd:`Control-C` (the Unix signal ``SIGINT``) but this doesn't work, -you can try :kbd:`Control-\\` (the Unix signal ``SIGQUIT``). - - -Increasing Verbosity -==================== - -If things fail in the :term:`VPI`/:term:`VHPI`/:term:`FLI` area, check your simulator's documentation to see if it has options to -increase its verbosity about what may be wrong. You can then set these options on the :command:`make` command line -as :make:var:`COMPILE_ARGS`, :make:var:`SIM_ARGS` or :make:var:`EXTRA_ARGS` (see :doc:`building` for details). -If things fail from within Python, or coroutines aren't being called when you expect, the -:envvar:`COCOTB_SCHEDULER_DEBUG` variable can be used to (greatly) increase the verbosity of the scheduler. - - -.. _troubleshooting-attaching-debugger: - -Attaching a Debugger -==================== - -.. _troubleshooting-attaching-debugger-c: - -C and C++ ---------- - -In order to give yourself time to attach a debugger to the simulator process before it starts to run, -you can set the environment variable :envvar:`COCOTB_ATTACH` to a pause time value in seconds. -If set, cocotb will print the process ID (PID) to attach to and wait the specified time before -actually letting the simulator run. - -For the GNU debugger GDB, the command is ``attach ``. - -.. _troubleshooting-attaching-debugger-python: - -Python ------- - -When executing the Makefile to run a cocotb test, a Python shell interpreter is called from within the -:term:`VPI`/:term:`VHPI`/:term:`FLI` library. -Hence it is not possible to directly attach a Python debugger to the Python process being part of the simulator that uses the aforementioned library. -Using ``import pdb; pdb.set_trace()`` directly is also frequently not possible, -due to the way that simulators interfere with stdin. - -To successfully debug your Python code use the `remote_pdb`_ Python package to create a :command:`pdb` instance -accessible via a TCP socket: - -.. _remote_pdb: https://pypi.org/project/remote-pdb/ - -1. In your code insert the line: - - .. code:: python - - from remote_pdb import RemotePdb; rpdb = RemotePdb("127.0.0.1", 4000) - -2. Then before the line of code you want the debugger to stop the execution, add a breakpoint: - - .. code:: python - - rpdb.set_trace() # <-- debugger stops execution after this line - # <-- next statement being evaluated by the interpreter - -3. Run the Makefile so that the interpreter hits the breakpoint line and *hangs*. -4. Connect to the freshly created socket, for instance through :command:`telnet`: - - .. code:: shell - - telnet 127.0.0.1 4000 - - -Embedding an IPython shell -========================== - -.. module:: cocotb.ipython_support - -.. versionadded:: 1.4 - -A prebuilt test is included to easily launch an IPython shell in an existing design. - -.. autofunction:: run_ipython - -To embed a shell within an existing test, where it can inspect local variables, the :func:`embed` function can be used. - -.. autofunction:: embed - - -.. _troubleshooting-make-vars: - -Setting make variables on the command line -========================================== - -When trying to set one of the make variables listed in :ref:`building` from the command line, -it is strongly recommended to use an environment variable, i.e. -``EXTRA_ARGS="..." make`` (for the fish and csh shells: ``env EXTRA_ARGS="..." make``) -and *not* ``make EXTRA_ARGS=...``. - -This is because in the case of the disrecommended ``make EXTRA_ARGS=...``, -if one of the involved Makefiles contains lines to assign (``=``) or append (``+=``) to :make:var:`EXTRA_ARGS` internally, -such lines will be ignored. -These lines are needed for the operation of cocotb however, -and having them ignored is likely to lead to strange errors. - -As a side note, -when you need to *clear* a Makefile variable from the command line, -use the syntax ``make EXTRA_ARGS=``. - -``GLIBCXX_3.4.XX`` not found -============================ - -This error can occur on Linux, and will raise ``ImportError: /some/libstdc++.so.6: version `GLIBCXX_3.4.XX' not found``. -This occurs because an older non-C++11 version of libstdc++ is being loaded by the simulator or cocotb. -It is usually an issue with your environment, but sometimes can occur when using a very old version of certain simulators. - -Check your environment ----------------------- - -To see if your environment is the issue, look at the value of the :envvar:`LD_LIBRARY_PATH` environment variable. -Ensure the first path in the colon-deliminated list is the path to the libstdc++ that shipped with the compiler you used to build cocotb. - -.. code:: shell - - echo $LD_LIBRARY_PATH - -This variable might be empty, in which case the loader looks in the system's libraries. -If the library you built cocotb with is not first, prepend that path to the list. - -.. code:: shell - - export LD_LIBRARY_PATH=/path/to/newer/libraries/:$LD_LIBRARY_PATH - -Check your simulator --------------------- - -Sometimes, simulators modify the :envvar:`LD_LIBRARY_PATH` so they point to the libraries that are shipped with instead of the system libraries. -If you are running an old simulator, the packaged libraries may include a pre-C++11 libstdc++. -To see if your simulator is modifying the :envvar:`LD_LIBRARY_PATH`, open the simulator up to an internal console and obtain the environment variable. - -For example, with Mentor Questa and Cadence Xcelium, one could open a Tcl console and run the :command:`env` command to list the current environment. -The :envvar:`LD_LIBRARY_PATH` should appear in the list. - -If the simulator does modify the :envvar:`LD_LIBRARY_PATH`, refer to the simulator documentation on how to prevent or work around this issue. - -For example, Questa ships with GCC. -Sometimes that version of GCC is old enough to not support C++11 (<4.8). -When you install cocotb, :command:`pip` uses the system (or some other) compiler that supports C++11. -But when you try to run cocotb with the older Questa, it prepends the older libraries Questa ships with to :envvar:`LD_LIBRARY_PATH`. -This causes the older libstdc++ Questa ships with to be loaded, resuling in the error message. -For Questa, you can use the :option:`-noautoldlibpath` option to turn off the :envvar:`LD_LIBRARY_PATH` prepend to resolve this issue. - - -Using cocotb with more than one Python installation -=================================================== - -Users of cocotb with more than one installation of a single Python version (including conda env users) must take care not to re-use cached versions of the installed cocotb package. -If this isn't done, running simulations fails with errors like ``libpython3.7m.so.1.0: cannot open shared object file: No such file or directory``. - -Cocotb builds binary libraries during its installation process. -These libraries are tailored to the installation of Python used when installing cocotb. -When switching between Python installations, cocotb needs to be re-installed without using cached build artifacts, e.g. with ``pip install --no-cache-dir cocotb``. - -On Linux distributions, setting ``LD_DEBUG=libs`` (example: ``LD_DEBUG=libs make SIM=verilator``) prints detailed output about which libraries are loaded from where. -On Mac OS, you can use ``DYLD_PRINT_LIBRARIES=1`` instead of ``LD_DEBUG=libs`` to get similar information. -On Windows, use `Process Explorer `_. - -Further details are available in :issue:`1943`. diff --git a/documentation/source/writing_testbenches.rst b/documentation/source/writing_testbenches.rst deleted file mode 100644 index 541e036d..00000000 --- a/documentation/source/writing_testbenches.rst +++ /dev/null @@ -1,28 +0,0 @@ -******************* -Writing Testbenches -******************* - -Handling Errors -=============== - -It may not be clear when to raise an exception and when to use ``log.error()``. - -* Use ``raise`` if the caller called your function in an invalid way, and it doesn't make sense to continue. -* Use ``log.error()`` if the hardware itself is misbehaving, and throwing an error immediately would leave it an invalid state. - -Even if you do eventually throw an exception (if you weren't able to do it immediately), you should also ``log.error()`` so that the simulation time of when things went wrong is recorded. - -TL;DR: ``log.error()`` is for humans only, ``raise`` is for both humans and code. - - -Accessing Identifiers Starting with an Underscore -================================================= - -The attribute syntax of ``dut._some_signal`` cannot be used to access -an identifier that starts with an underscore (``_``, as is valid in Verilog) -because we reserve such names for cocotb-internals, -thus the access will raise an :exc:`AttributeError`. - -A workaround is to use indirect access using -:meth:`~cocotb.handle.HierarchyObject._id` like in the following example: -``dut._id("_some_signal", extended=False)``. diff --git a/documentation/sphinxext/cairosvgconverter.py b/documentation/sphinxext/cairosvgconverter.py deleted file mode 100644 index 11bf2fc6..00000000 --- a/documentation/sphinxext/cairosvgconverter.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- -""" -""" -from sphinx.errors import ExtensionError -from sphinx.locale import _ -from sphinx.transforms.post_transforms.images import ImageConverter -from sphinx.util import logging -from sphinx.util.osutil import ENOENT, EPIPE, EINVAL -from cairosvg import svg2pdf - -logger = logging.getLogger(__name__) - -class CairoSvgConverter(ImageConverter): - conversion_rules = [ - ('image/svg+xml', 'application/pdf'), - ] - - def is_available(self): - # type: () -> bool - return True - - def convert(self, _from, _to): - # type: (unicode, unicode) -> bool - """Converts the image to expected one.""" - svg2pdf(url=_from, write_to=_to) - - return True - - -def setup(app): - # type: (Sphinx) -> Dict[unicode, Any] - app.add_post_transform(CairoSvgConverter) - - return { - 'version': 'builtin', - 'parallel_read_safe': True, - 'parallel_write_safe': True, - } diff --git a/examples/adder/hdl/adder.sv b/examples/adder/hdl/adder.sv deleted file mode 100644 index e8a81ab2..00000000 --- a/examples/adder/hdl/adder.sv +++ /dev/null @@ -1,22 +0,0 @@ -// This file is public domain, it can be freely copied without restrictions. -// SPDX-License-Identifier: CC0-1.0 -// Adder DUT -`timescale 1ns/1ps - -module adder #( - parameter integer DATA_WIDTH = 4 -) ( - input logic unsigned [DATA_WIDTH-1:0] A, - input logic unsigned [DATA_WIDTH-1:0] B, - output logic unsigned [DATA_WIDTH:0] X -); - - assign X = A + B; - - // Dump waves - initial begin - $dumpfile("dump.vcd"); - $dumpvars(1, adder); - end - -endmodule diff --git a/examples/adder/hdl/adder.vhdl b/examples/adder/hdl/adder.vhdl deleted file mode 100644 index dcce35f3..00000000 --- a/examples/adder/hdl/adder.vhdl +++ /dev/null @@ -1,25 +0,0 @@ --- This file is public domain, it can be freely copied without restrictions. --- SPDX-License-Identifier: CC0-1.0 --- Adder DUT -library ieee; - use ieee.std_logic_1164.all; - use ieee.numeric_std.all; - -entity adder is - generic ( - DATA_WIDTH : positive := 4); - port ( - A : in unsigned(DATA_WIDTH-1 downto 0); - B : in unsigned(DATA_WIDTH-1 downto 0); - X : out unsigned(DATA_WIDTH downto 0)); -end entity adder; - -architecture rtl of adder is -begin - - add_proc : process (A, B) is - begin - X <= resize(A, X'length) + B; - end process add_proc; - -end architecture rtl; diff --git a/examples/adder/model/__init__.py b/examples/adder/model/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/adder/model/adder_model.py b/examples/adder/model/adder_model.py deleted file mode 100644 index 61eb547c..00000000 --- a/examples/adder/model/adder_model.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is public domain, it can be freely copied without restrictions. -# SPDX-License-Identifier: CC0-1.0 - - -def adder_model(a: int, b: int) -> int: - """ model of adder """ - return a + b diff --git a/examples/adder/tests/Makefile b/examples/adder/tests/Makefile deleted file mode 100644 index d49e45fe..00000000 --- a/examples/adder/tests/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -TOPLEVEL_LANG ?= verilog - -PWD=$(shell pwd) - -export PYTHONPATH := $(PWD)/../model:$(PYTHONPATH) - -ifeq ($(TOPLEVEL_LANG),verilog) - VERILOG_SOURCES = $(PWD)/../hdl/adder.sv -else ifeq ($(TOPLEVEL_LANG),vhdl) - VHDL_SOURCES = $(PWD)/../hdl/adder.vhdl -else - $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") -endif - -TOPLEVEL := adder -MODULE := test_adder - -include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/examples/adder/tests/test_adder.py b/examples/adder/tests/test_adder.py deleted file mode 100644 index 33a7082d..00000000 --- a/examples/adder/tests/test_adder.py +++ /dev/null @@ -1,40 +0,0 @@ -# This file is public domain, it can be freely copied without restrictions. -# SPDX-License-Identifier: CC0-1.0 -# Simple tests for an adder module -import cocotb -from cocotb.triggers import Timer -from adder_model import adder_model -import random - - -@cocotb.test() -async def adder_basic_test(dut): - """Test for 5 + 10""" - - A = 5 - B = 10 - - dut.A <= A - dut.B <= B - - await Timer(2, units='ns') - - assert dut.X.value == adder_model(A, B), "Adder result is incorrect: {} != 15".format(dut.X.value) - - -@cocotb.test() -async def adder_randomised_test(dut): - """Test for adding 2 random numbers multiple times""" - - for i in range(10): - - A = random.randint(0, 15) - B = random.randint(0, 15) - - dut.A <= A - dut.B <= B - - await Timer(2, units='ns') - - assert dut.X.value == adder_model(A, B), "Randomised test failed with: {A} + {B} = {X}".format( - A=dut.A.value, B=dut.B.value, X=dut.X.value) diff --git a/examples/dff/hdl/dff.v b/examples/dff/hdl/dff.v deleted file mode 100644 index 1989e16b..00000000 --- a/examples/dff/hdl/dff.v +++ /dev/null @@ -1,41 +0,0 @@ -// ============================================================================= -// 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. -// ============================================================================= - -`timescale 1ns/1ps - -module dff (c,d,q); - input wire c, d; - output reg q = 1'b0; - - always @(posedge c) - begin - // It is also possible to add an delay of less than one clock period - // here. - q <= d; - end - -endmodule // dff diff --git a/examples/dff/hdl/dff.vhdl b/examples/dff/hdl/dff.vhdl deleted file mode 100644 index c50b28fa..00000000 --- a/examples/dff/hdl/dff.vhdl +++ /dev/null @@ -1,42 +0,0 @@ --- ============================================================================= --- 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. --- ============================================================================= - -library ieee; -use ieee.std_logic_1164.all; - -entity dff is - port ( - c : in std_logic; - d : in std_logic; - q : out std_logic := '0'); -end entity dff; - -architecture rtl of dff 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; diff --git a/examples/dff/tests/Makefile b/examples/dff/tests/Makefile deleted file mode 100755 index 84e365f2..00000000 --- a/examples/dff/tests/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) 2016 Technische Universitaet Dresden, Germany -# Chair for VLSI-Design, Diagnostic and Architecture -# Author: Martin Zabel -# All rights reserved. - -CWD=$(shell pwd) - -TOPLEVEL_LANG ?=verilog - -ifeq ($(TOPLEVEL_LANG),verilog) - VERILOG_SOURCES =$(CWD)/../hdl/dff.v -else ifeq ($(TOPLEVEL_LANG),vhdl) - VHDL_SOURCES =$(CWD)/../hdl/dff.vhdl -else - $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") -endif - -TOPLEVEL = dff -MODULE := $(TOPLEVEL)_cocotb -COCOTB_HDL_TIMEUNIT=1us -COCOTB_HDL_TIMEPRECISION=1us - -CUSTOM_SIM_DEPS=$(CWD)/Makefile - -ifeq ($(SIM),questa) - SIM_ARGS=-t 1ps -endif - -ifneq ($(filter $(SIM),ius xcelium),) - SIM_ARGS += -v93 -endif - -include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/examples/dff/tests/dff_cocotb.py b/examples/dff/tests/dff_cocotb.py deleted file mode 100644 index c35775cd..00000000 --- a/examples/dff/tests/dff_cocotb.py +++ /dev/null @@ -1,160 +0,0 @@ -# ============================================================================== -# Authors: Martin Zabel -# -# Cocotb Testbench: For D flip-flop -# -# Description: -# ------------------------------------ -# Automated testbench for simple D flip-flop. -# -# 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. -# ============================================================================== - -import random -import warnings - -import cocotb -from cocotb.clock import Clock -from cocotb.triggers import RisingEdge -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 - -# dut -# ________ -# | | -# --| d q |-- -# | | -# --|>c | -# |______| - - -class BitMonitor(Monitor): - """Observe a single-bit input or output of the DUT.""" - - def __init__(self, name, signal, clk, callback=None, event=None): - self.name = name - self.signal = signal - self.clk = clk - Monitor.__init__(self, callback, event) - - async def _monitor_recv(self): - clkedge = RisingEdge(self.clk) - - while True: - # Capture signal at rising edge of clock - await clkedge - vec = self.signal.value - self._recv(vec) - - -def input_gen(): - """Generator for input data applied by BitDriver. - - Continually yield a tuple with the number of cycles to be on - followed by the number of cycles to be off. - """ - while True: - yield random.randint(1, 5), random.randint(1, 5) - - -class DFF_TB(object): - def __init__(self, dut, init_val): - """ - Setup the testbench. - - *init_val* signifies the ``BinaryValue`` which must be captured by the - output monitor with the first rising clock edge. - This must match the initial state of the D flip-flop in RTL. - """ - # Some internal state - self.dut = dut - self.stopped = False - - # Create input driver and output monitor - self.input_drv = BitDriver(signal=dut.d, clk=dut.c, generator=input_gen()) - self.output_mon = BitMonitor(name="output", signal=dut.q, clk=dut.c) - - # Create a scoreboard on the outputs - self.expected_output = [init_val] # a list with init_val as the first element - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - self.scoreboard = Scoreboard(dut) - self.scoreboard.add_interface(self.output_mon, self.expected_output) - - # Use the input monitor to reconstruct the transactions from the pins - # and send them to our 'model' of the design. - self.input_mon = BitMonitor(name="input", signal=dut.d, clk=dut.c, - callback=self.model) - - def model(self, transaction): - """Model the DUT based on the input *transaction*. - - For a D flip-flop, what goes in at ``d`` comes out on ``q``, - so the value on ``d`` (put into *transaction* by our ``input_mon``) - can be used as expected output without change. - Thus we can directly append *transaction* to the ``expected_output`` list, - except for the very 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 generating input data.""" - self.input_drv.start() - - def stop(self): - """Stop generating input data. - - Also stop generation of expected output transactions. - One more clock cycle must be executed afterwards so that the output of - the D flip-flop can be checked. - """ - self.input_drv.stop() - self.stopped = True - - -async def run_test(dut): - """Setup testbench and run a test.""" - - cocotb.fork(Clock(dut.c, 10, 'us').start(start_high=False)) - - tb = DFF_TB(dut, init_val=BinaryValue(0)) - - clkedge = RisingEdge(dut.c) - - # Apply random input data by input_gen via BitDriver for 100 clock cycles. - tb.start() - for _ in range(100): - await clkedge - - # Stop generation of input data. One more clock cycle is needed to capture - # the resulting output of the DUT. - tb.stop() - await clkedge - - # Print result of scoreboard. - raise tb.scoreboard.result - - -# Register the test. -factory = TestFactory(run_test) -factory.generate_tests() diff --git a/examples/matrix_multiplier/hdl/matrix_multiplier.sv b/examples/matrix_multiplier/hdl/matrix_multiplier.sv deleted file mode 100644 index 54f588e0..00000000 --- a/examples/matrix_multiplier/hdl/matrix_multiplier.sv +++ /dev/null @@ -1,84 +0,0 @@ -// This file is public domain, it can be freely copied without restrictions. -// SPDX-License-Identifier: CC0-1.0 - -// Matrix Multiplier DUT -`timescale 1ns/1ps - -`ifdef VERILATOR // make parameter readable from VPI - `define VL_RD /*verilator public_flat_rd*/ -`else - `define VL_RD -`endif - -module matrix_multiplier #( - parameter int DATA_WIDTH `VL_RD = 8, - parameter int A_ROWS `VL_RD = 8, - parameter int B_COLUMNS `VL_RD = 5, - parameter int A_COLUMNS_B_ROWS `VL_RD = 4, - parameter int C_DATA_WIDTH = (2 * DATA_WIDTH) + $clog2(A_COLUMNS_B_ROWS) -) ( - input clk_i, - input reset_i, - input valid_i, - output logic valid_o, - input [DATA_WIDTH-1:0] a_i[A_ROWS * A_COLUMNS_B_ROWS], - input [DATA_WIDTH-1:0] b_i[A_COLUMNS_B_ROWS * B_COLUMNS], - output logic [C_DATA_WIDTH-1:0] c_o[A_ROWS * B_COLUMNS] -); - - logic [C_DATA_WIDTH-1:0] c_calc[A_ROWS * B_COLUMNS]; - - always @(*) begin : multiply - logic [C_DATA_WIDTH-1:0] c_element; - for (int i = 0; i < A_ROWS; i = i + 1) begin : C_ROWS - for (int j = 0; j < B_COLUMNS; j = j + 1) begin : C_COLUMNS - c_element = 0; - for (int k = 0; k < A_COLUMNS_B_ROWS; k = k + 1) begin : DOT_PRODUCT - c_element = c_element + (a_i[(i * A_COLUMNS_B_ROWS) + k] * b_i[(k * B_COLUMNS) + j]); - end - c_calc[(i * B_COLUMNS) + j] = c_element; - end - end - end - - always @(posedge clk_i) begin : proc_reg - if (reset_i) begin - valid_o <= 1'b0; - - for (int idx = 0; idx < (A_ROWS * B_COLUMNS); idx++) begin - c_o[idx] <= '0; - end - end else begin - valid_o <= valid_i; - - for (int idx = 0; idx < (A_ROWS * B_COLUMNS); idx++) begin - c_o[idx] <= c_calc[idx]; - end - end - end - - // Dump waves -`ifndef VERILATOR - initial begin - integer idx; - $dumpfile("dump.vcd"); - $dumpvars(1, matrix_multiplier); - `ifdef __ICARUS__ - for (idx = 0; idx < (A_ROWS * A_COLUMNS_B_ROWS); idx++) begin - $dumpvars(1, a_i[idx]); - end - for (idx = 0; idx < (A_COLUMNS_B_ROWS * B_COLUMNS); idx++) begin - $dumpvars(1, b_i[idx]); - end - for (idx = 0; idx < (A_ROWS * B_COLUMNS); idx++) begin - $dumpvars(1, c_o[idx]); - end - `else - $dumpvars(1, a_i); - $dumpvars(1, b_i); - $dumpvars(1, c_o); - `endif - end -`endif - -endmodule diff --git a/examples/matrix_multiplier/hdl/matrix_multiplier.vhd b/examples/matrix_multiplier/hdl/matrix_multiplier.vhd deleted file mode 100644 index e1591346..00000000 --- a/examples/matrix_multiplier/hdl/matrix_multiplier.vhd +++ /dev/null @@ -1,75 +0,0 @@ --- This file is public domain, it can be freely copied without restrictions. --- SPDX-License-Identifier: CC0-1.0 - --- Matrix Multiplier DUT -library ieee ; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use work.matrix_multiplier_pkg.all; -use work.mean_pkg.clog2; - -entity matrix_multiplier is - generic ( - DATA_WIDTH : positive := 8; - A_ROWS : positive := 8; - B_COLUMNS : positive := 5; - A_COLUMNS_B_ROWS : positive := 4 - ); - port ( - clk_i : in std_logic; - reset_i : in std_logic; - valid_i : in std_logic; - valid_o : out std_logic; - a_i : in flat_matrix_type(0 to (A_ROWS * A_COLUMNS_B_ROWS) - 1)(DATA_WIDTH - 1 downto 0); - b_i : in flat_matrix_type(0 to (A_COLUMNS_B_ROWS * B_COLUMNS) - 1)(DATA_WIDTH - 1 downto 0); - c_o : out flat_matrix_type(0 to (A_ROWS * B_COLUMNS) - 1)((2 * DATA_WIDTH) + clog2(A_COLUMNS_B_ROWS) - 1 downto 0) - ); -end entity matrix_multiplier; - -architecture rtl of matrix_multiplier is - - signal c_calc : flat_matrix_type(c_o'RANGE)(c_o(0)'RANGE); - -begin - - multiply : process (all) is - - variable c_var : flat_matrix_type(c_o'RANGE)(c_o(0)'RANGE); - - begin - - c_var := (others => (others => '0')); - - C_ROWS : for i in 0 to A_ROWS-1 loop - C_COLUMNS : for j in 0 to B_COLUMNS-1 loop - DOT_PRODUCT : for k in 0 to A_COLUMNS_B_ROWS-1 loop - c_var((i * B_COLUMNS) + j) := c_var((i * B_COLUMNS) + j) + (a_i((i * A_COLUMNS_B_ROWS) + k) * b_i((k * B_COLUMNS) + j)); - end loop; - end loop; - end loop; - - c_calc <= c_var; - - end process multiply; - - proc_reg : process (clk_i) is - begin - - if (rising_edge(clk_i)) then - if (reset_i) then - valid_o <= '0'; - c_o <= (others => (others => '0')); - else - valid_o <= valid_i; - - if (valid_i) then - c_o <= c_calc; - else - c_o <= (others => (others => 'X')); - end if; - end if; - end if; - - end process proc_reg; - -end architecture rtl; diff --git a/examples/matrix_multiplier/hdl/matrix_multiplier_pkg.vhd b/examples/matrix_multiplier/hdl/matrix_multiplier_pkg.vhd deleted file mode 100644 index 0dcd94ba..00000000 --- a/examples/matrix_multiplier/hdl/matrix_multiplier_pkg.vhd +++ /dev/null @@ -1,13 +0,0 @@ --- 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; -use ieee.numeric_std.all; - -package matrix_multiplier_pkg is - - -- VHDL-2008 required for unconstrained array types - type flat_matrix_type is array (integer range <>) of unsigned; - -end package; diff --git a/examples/matrix_multiplier/tests/Makefile b/examples/matrix_multiplier/tests/Makefile deleted file mode 100644 index ac800219..00000000 --- a/examples/matrix_multiplier/tests/Makefile +++ /dev/null @@ -1,75 +0,0 @@ -# This file is public domain, it can be freely copied without restrictions. -# SPDX-License-Identifier: CC0-1.0 - -TOPLEVEL_LANG ?= verilog -SIM ?= icarus - -PWD=$(shell pwd) - -# Matrix parameters -DATA_WIDTH ?= 32 -A_ROWS ?= 10 -B_COLUMNS ?= 4 -A_COLUMNS_B_ROWS ?= 6 - -ifeq ($(TOPLEVEL_LANG),verilog) - VERILOG_SOURCES = $(PWD)/../hdl/matrix_multiplier.sv - - # Set module parameters - ifeq ($(SIM),icarus) - COMPILE_ARGS += -Pmatrix_multiplier.DATA_WIDTH=$(DATA_WIDTH) -Pmatrix_multiplier.A_ROWS=$(A_ROWS) -Pmatrix_multiplier.B_COLUMNS=$(B_COLUMNS) -Pmatrix_multiplier.A_COLUMNS_B_ROWS=$(A_COLUMNS_B_ROWS) - else ifneq ($(filter $(SIM),questa modelsim riviera activehdl),) - SIM_ARGS += -gDATA_WIDTH=$(DATA_WIDTH) -gA_ROWS=$(A_ROWS) -gB_COLUMNS=$(B_COLUMNS) -gA_COLUMNS_B_ROWS=$(A_COLUMNS_B_ROWS) - else ifeq ($(SIM),vcs) - COMPILE_ARGS += -pvalue+matrix_multiplier/DATA_WIDTH=$(DATA_WIDTH) -pvalue+matrix_multiplier/A_ROWS=$(A_ROWS) -pvalue+matrix_multiplier/B_COLUMNS=$(B_COLUMNS) -pvalue+matrix_multiplier/A_COLUMNS_B_ROWS=$(A_COLUMNS_B_ROWS) - else ifeq ($(SIM),verilator) - COMPILE_ARGS += -GDATA_WIDTH=$(DATA_WIDTH) -GA_ROWS=$(A_ROWS) -GB_COLUMNS=$(B_COLUMNS) -GA_COLUMNS_B_ROWS=$(A_COLUMNS_B_ROWS) - else ifneq ($(filter $(SIM),ius xcelium),) - EXTRA_ARGS += -defparam "matrix_multiplier.DATA_WIDTH=$(DATA_WIDTH)" -defparam "matrix_multiplier.A_ROWS=$(A_ROWS)" -defparam "matrix_multiplier.B_COLUMNS=$(B_COLUMNS)" -defparam "matrix_multiplier.A_COLUMNS_B_ROWS=$(A_COLUMNS_B_ROWS)" - endif - - ifneq ($(filter $(SIM),riviera activehdl),) - COMPILE_ARGS += -sv2k12 - endif - -else ifeq ($(TOPLEVEL_LANG),vhdl) - VHDL_SOURCES = $(PWD)/../../mean/hdl/mean_pkg.vhd $(PWD)/../hdl/matrix_multiplier_pkg.vhd $(PWD)/../hdl/matrix_multiplier.vhd - - ifneq ($(filter $(SIM),ghdl questa modelsim riviera activehdl),) - # ghdl, questa, and aldec all use SIM_ARGS with '-g' for setting generics - SIM_ARGS += -gDATA_WIDTH=$(DATA_WIDTH) -gA_ROWS=$(A_ROWS) -gB_COLUMNS=$(B_COLUMNS) -gA_COLUMNS_B_ROWS=$(A_COLUMNS_B_ROWS) - else ifneq ($(filter $(SIM),ius xcelium),) - SIM_ARGS += -generic "matrix_multiplier:DATA_WIDTH=>$(DATA_WIDTH)" -generic "matrix_multiplier:A_ROWS=>$(A_ROWS)" -generic "matrix_multiplier:B_COLUMNS=>$(B_COLUMNS)" -generic "matrix_multiplier:A_COLUMNS_B_ROWS=>$(A_COLUMNS_B_ROWS)" - endif - - ifeq ($(SIM),ghdl) - EXTRA_ARGS += --std=08 - SIM_ARGS += --wave=wave.ghw - else ifneq ($(filter $(SIM),questa modelsim riviera activehdl),) - COMPILE_ARGS += -2008 - endif -else - $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") -endif - -# Fix the seed to ensure deterministic tests -export RANDOM_SEED := 123456789 - -TOPLEVEL := matrix_multiplier -MODULE := test_matrix_multiplier - -include $(shell cocotb-config --makefiles)/Makefile.sim - - -# Profiling - -DOT_BINARY ?= dot - -test_profile.pstat: sim - -callgraph.svg: test_profile.pstat - $(shell cocotb-config --python-bin) -m gprof2dot -f pstats ./$< | $(DOT_BINARY) -Tsvg -o $@ - -.PHONY: profile -profile: - COCOTB_ENABLE_PROFILING=1 $(MAKE) callgraph.svg diff --git a/examples/matrix_multiplier/tests/test_matrix_multiplier.py b/examples/matrix_multiplier/tests/test_matrix_multiplier.py deleted file mode 100644 index 4c104801..00000000 --- a/examples/matrix_multiplier/tests/test_matrix_multiplier.py +++ /dev/null @@ -1,158 +0,0 @@ -# This file is public domain, it can be freely copied without restrictions. -# SPDX-License-Identifier: CC0-1.0 - -import math -import os -from random import getrandbits - -import cocotb -from cocotb.binary import BinaryValue -from cocotb.clock import Clock -from cocotb.monitors import BusMonitor -from cocotb.regression import TestFactory -from cocotb.triggers import RisingEdge, ReadOnly - -NUM_SAMPLES = int(os.environ.get('NUM_SAMPLES', 3000)) -DATA_WIDTH = int(cocotb.top.DATA_WIDTH) -A_ROWS = int(cocotb.top.A_ROWS) -B_COLUMNS = int(cocotb.top.B_COLUMNS) -A_COLUMNS_B_ROWS = int(cocotb.top.A_COLUMNS_B_ROWS) - - -class MatrixMonitor(BusMonitor): - """Base class for monitoring inputs/outputs of Matrix Multiplier.""" - def __init__(self, dut, callback=None, event=None): - super().__init__(dut, "", dut.clk_i, callback=callback, event=event) - - -class MatrixInMonitor(MatrixMonitor): - """Monitor inputs to Matrix Multiplier module. - - Generate expected results for each multiplication operation. - """ - _signals = {"A": "a_i", "B": "b_i", "valid": "valid_i"} - - async def _monitor_recv(self): - while True: - await RisingEdge(self.clock) - await ReadOnly() - - if self.bus.valid.value: - a_matrix = self.bus.A.value - b_matrix = self.bus.B.value - - # Calculate the expected result of C - c_expected = [ - BinaryValue( - sum( - [ - a_matrix[(i * A_COLUMNS_B_ROWS) + n] * b_matrix[(n * B_COLUMNS) + j] - for n in range(A_COLUMNS_B_ROWS) - ] - ), - n_bits=(DATA_WIDTH * 2) + math.ceil(math.log2(A_COLUMNS_B_ROWS)), - bigEndian=False - ) - for i in range(A_ROWS) - for j in range(B_COLUMNS) - ] - - self._recv(c_expected) - - -class MatrixOutMonitor(MatrixMonitor): - """Monitor outputs from Matrix Multiplier module. - - Capture result matrix for each multiplication operation. - """ - _signals = {"C": "c_o", "valid": "valid_o"} - - async def _monitor_recv(self): - while True: - await RisingEdge(self.clock) - await ReadOnly() - - if self.bus.valid.value: - c_actual = self.bus.C.value - self._recv(c_actual) - - -async def test_multiply(dut, a_data, b_data): - """Test multiplication of many matrices.""" - - cocotb.fork(Clock(dut.clk_i, 10, units='ns').start()) - - # Configure checker to compare module results to expected - expected_output = [] - - dut._log.info("Configure monitors") - - in_monitor = MatrixInMonitor(dut, callback=expected_output.append) - - def check_output(transaction): - if not expected_output: - raise RuntimeError("Monitor captured unexpected multiplication operation") - exp = expected_output.pop(0) - assert transaction == exp, "Multiplication result {} did not match expected result {}".format(transaction, exp) - - out_monitor = MatrixOutMonitor(dut, callback=check_output) - - dut._log.info("Initialize and reset model") - - # Initial values - dut.valid_i <= 0 - dut.a_i <= create_a(lambda x: 0) - dut.b_i <= create_b(lambda x: 0) - - # Reset DUT - dut.reset_i <= 1 - for _ in range(3): - await RisingEdge(dut.clk_i) - dut.reset_i <= 0 - - dut._log.info("Test multiplication operations") - - # Do multiplication operations - for i, (A, B) in enumerate(zip(a_data(), b_data())): - await RisingEdge(dut.clk_i) - dut.a_i <= A - dut.b_i <= B - dut.valid_i <= 1 - - await RisingEdge(dut.clk_i) - dut.valid_i <= 0 - - if i % 100 == 0: - dut._log.info("{} / {}".format(i, NUM_SAMPLES)) - - await RisingEdge(dut.clk_i) - - -def create_matrix(func, rows, cols): - return [func(DATA_WIDTH) for row in range(rows) for col in range(cols)] - - -def create_a(func): - return create_matrix(func, A_ROWS, A_COLUMNS_B_ROWS) - - -def create_b(func): - return create_matrix(func, A_COLUMNS_B_ROWS, B_COLUMNS) - - -def gen_a(num_samples=NUM_SAMPLES, func=getrandbits): - """Generate random matrix data for A""" - for _ in range(num_samples): - yield create_a(func) - - -def gen_b(num_samples=NUM_SAMPLES, func=getrandbits): - """Generate random matrix data for B""" - for _ in range(num_samples): - yield create_b(func) - - -factory = TestFactory(test_multiply) -factory.add_option('a_data', [gen_a]) -factory.add_option('b_data', [gen_b]) -factory.generate_tests() diff --git a/examples/mean/hdl/mean.sv b/examples/mean/hdl/mean.sv deleted file mode 100644 index 4cb35868..00000000 --- a/examples/mean/hdl/mean.sv +++ /dev/null @@ -1,82 +0,0 @@ -// This file is public domain, it can be freely copied without restrictions. -// SPDX-License-Identifier: CC0-1.0 - -//----------------------------------------------------------------------------- -// Calculates mean of data input bus -//----------------------------------------------------------------------------- - -`timescale 1ns/1ps - -`ifdef VERILATOR // make parameter readable from VPI - `define VL_RD /*verilator public_flat_rd*/ -`else - `define VL_RD -`endif - - -module mean #( - parameter int BUS_WIDTH `VL_RD = 4, - parameter int DATA_WIDTH `VL_RD = 6 -) ( - input logic clk, - input logic rst, - input logic i_valid, - input logic [DATA_WIDTH-1:0] i_data [0:BUS_WIDTH-1], - output logic o_valid, - output logic [DATA_WIDTH-1:0] o_data - ); - - - localparam int SUM_WIDTH = DATA_WIDTH + $clog2(BUS_WIDTH); -// localparam int SUM_WIDTH = DATA_WIDTH + $clog2(BUS_WIDTH) - 1; // introduce bug - - logic [SUM_WIDTH-1:0] v_sum; - logic [SUM_WIDTH-1:0] s_sum; - logic s_valid; - - initial begin - if (BUS_WIDTH != 2**($clog2(BUS_WIDTH))) begin - $fatal(1, "parameter BUS_WIDTH should be a power of 2!"); - end - end - - - always @* begin - v_sum = '0; - - for (int i = 0; i < BUS_WIDTH; i++) begin - v_sum = v_sum + i_data[i]; - end - end - - always @(posedge clk) begin - s_valid <= i_valid; - - if (i_valid) begin - s_sum <= v_sum; - end - - if (rst) begin - s_sum <= '0; - s_valid <= 1'b0; - end - end - - assign o_valid = s_valid; - assign o_data = s_sum >> $clog2(BUS_WIDTH); - - -`ifndef VERILATOR - initial begin - int idx; - $dumpfile("dump.vcd"); - $dumpvars(0, mean); - `ifdef __ICARUS__ - for (idx = 0; idx < BUS_WIDTH; idx++) begin - $dumpvars(0, i_data[idx]); - end - `endif - end -`endif - -endmodule diff --git a/examples/mean/hdl/mean.vhd b/examples/mean/hdl/mean.vhd deleted file mode 100644 index c2f7c43c..00000000 --- a/examples/mean/hdl/mean.vhd +++ /dev/null @@ -1,68 +0,0 @@ --- This file is public domain, it can be freely copied without restrictions. --- SPDX-License-Identifier: CC0-1.0 - -------------------------------------------------------------------------------- --- Calculates mean of data input bus -------------------------------------------------------------------------------- -library ieee ; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use work.mean_pkg.all; - - -entity mean is -generic ( - BUS_WIDTH : natural := 4); -port ( - clk : in std_logic; - rst : in std_logic; - i_valid : in std_logic; - i_data : in t_data_array(0 to BUS_WIDTH-1); - o_valid : out std_logic; - o_data : out t_data - ); -end mean; - - -architecture RTL of mean is - - constant DATA_WIDTH : natural := C_DATA_WIDTH; - constant SUM_WIDTH : natural := DATA_WIDTH + clog2(BUS_WIDTH); --- constant SUM_WIDTH : natural := DATA_WIDTH + clog2(BUS_WIDTH) - 1; -- introduce bug - - signal s_sum : unsigned(SUM_WIDTH-1 downto 0) := (others => '0'); - signal s_valid : std_logic := '0'; - -begin - - assert BUS_WIDTH = 2**(clog2(BUS_WIDTH)) - report LF & " BUS_WIDTH = " & integer'image(BUS_WIDTH) & " , should be a power of 2!" - severity Failure; - - - process(clk) - variable v_sum : unsigned(s_sum'range); - begin - if rising_edge(clk) then - s_valid <= i_valid; - - if i_valid = '1' then - v_sum := (others => '0'); - for i in i_data'range loop - v_sum := v_sum + resize(i_data(i), v_sum'length); - end loop; - - s_sum <= v_sum; - end if; - - if rst = '1' then - s_sum <= (others => '0'); - s_valid <= '0'; - end if; - end if; - end process; - - o_valid <= s_valid; - o_data <= resize(shift_right(s_sum, clog2(BUS_WIDTH)), o_data'length); - -end rtl; diff --git a/examples/mean/hdl/mean_pkg.vhd b/examples/mean/hdl/mean_pkg.vhd deleted file mode 100644 index 1fc00fd9..00000000 --- a/examples/mean/hdl/mean_pkg.vhd +++ /dev/null @@ -1,32 +0,0 @@ --- This file is public domain, it can be freely copied without restrictions. --- SPDX-License-Identifier: CC0-1.0 - -------------------------------------------------------------------------------- --- Package with constants, types & functions for mean.vhd -------------------------------------------------------------------------------- -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use ieee.math_real.all; - - -package mean_pkg is - - constant C_DATA_WIDTH : natural := 6; - - subtype t_data is unsigned(C_DATA_WIDTH-1 downto 0); - type t_data_array is array (natural range <>) of t_data; - - function clog2(n : positive) return natural; - -end package; - - -package body mean_pkg is - - function clog2(n : positive) return natural is - begin - return integer(ceil(log2(real(n)))); - end function; - -end package body; diff --git a/examples/mean/hdl/mean_sv.sv b/examples/mean/hdl/mean_sv.sv deleted file mode 100644 index 61444af9..00000000 --- a/examples/mean/hdl/mean_sv.sv +++ /dev/null @@ -1,38 +0,0 @@ -// This file is public domain, it can be freely copied without restrictions. -// SPDX-License-Identifier: CC0-1.0 - -//----------------------------------------------------------------------------- -// sv wrapper for mean.vhd -//----------------------------------------------------------------------------- -import mean_pkg::*; - - -module mean_sv #( - parameter BUS_WIDTH=2) - ( - input logic clk, - input logic rst, - input logic i_valid, - input t_data_array i_data [0:BUS_WIDTH-1], - output logic o_valid, - output t_data o_data - ); - - -// make constant from package visible -parameter DATA_WIDTH = c_data_width; - -// VHDL DUT - mean #( - .BUS_WIDTH (BUS_WIDTH)) - mean ( - .clk (clk), - .rst (rst), - .i_valid (i_valid), - .i_data (i_data), - .o_valid (o_valid), - .o_data (o_data) - ); - - -endmodule diff --git a/examples/mean/tests/Makefile b/examples/mean/tests/Makefile deleted file mode 100644 index 7e1f55c1..00000000 --- a/examples/mean/tests/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -# This file is public domain, it can be freely copied without restrictions. -# SPDX-License-Identifier: CC0-1.0 - -TOPLEVEL_LANG ?= verilog - -PWD=$(shell pwd) - -ifeq ($(TOPLEVEL_LANG),verilog) - VERILOG_SOURCES = $(PWD)/../hdl/mean.sv -else ifeq ($(TOPLEVEL_LANG),vhdl) - VHDL_SOURCES = $(PWD)/../hdl/mean_pkg.vhd - VHDL_SOURCES += $(PWD)/../hdl/mean.vhd -else - $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") -endif - -TOPLEVEL := mean -MODULE := test_mean - -ifneq ($(filter $(SIM),ius xcelium),) - SIM_ARGS += -v93 -endif - -ifeq ($(SIM),verilator) - EXTRA_ARGS += $(PWD)/verilator_waiver.vlt -endif - -include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/examples/mean/tests/test_mean.py b/examples/mean/tests/test_mean.py deleted file mode 100644 index 7c810a31..00000000 --- a/examples/mean/tests/test_mean.py +++ /dev/null @@ -1,128 +0,0 @@ -# This file is public domain, it can be freely copied without restrictions. -# SPDX-License-Identifier: CC0-1.0 - -import cocotb -from cocotb.clock import Clock -from cocotb.triggers import RisingEdge, ReadOnly -from cocotb.monitors import BusMonitor -from cocotb.scoreboard import Scoreboard - -import random -import warnings - -CLK_PERIOD_NS = 100 - - -class StreamBusMonitor(BusMonitor): - """Streaming bus monitor.""" - - _signals = ["valid", "data"] - - async def _monitor_recv(self): - """Watch the pins and reconstruct transactions.""" - - while True: - await RisingEdge(self.clock) - await ReadOnly() - if self.bus.valid.value: - self._recv(int(self.bus.data.value)) - - -async def value_test(dut, nums): - """Test sum(nums)/n""" - DATA_WIDTH = int(dut.DATA_WIDTH.value) - BUS_WIDTH = int(dut.BUS_WIDTH.value) - dut._log.info('Detected DATA_WIDTH = %d, BUS_WIDTH = %d' % - (DATA_WIDTH, BUS_WIDTH)) - - cocotb.fork(Clock(dut.clk, CLK_PERIOD_NS, units='ns').start()) - - dut.rst <= 1 - for i in range(BUS_WIDTH): - dut.i_data[i] <= 0 - dut.i_valid <= 0 - await RisingEdge(dut.clk) - await RisingEdge(dut.clk) - dut.rst <= 0 - - for i in range(BUS_WIDTH): - dut.i_data[i] <= nums[i] - dut.i_valid <= 1 - await RisingEdge(dut.clk) - dut.i_valid <= 0 - await RisingEdge(dut.clk) - got = int(dut.o_data.value) - - exp = sum(nums) // BUS_WIDTH - - assert got == exp, "Mismatch detected: got {}, expected {}!".format(got, exp) - - -@cocotb.test() -async def mean_basic_test(dut): - """Test n*5/n = 5""" - BUS_WIDTH = int(dut.BUS_WIDTH.value) - await value_test(dut, [5] * BUS_WIDTH) - - -@cocotb.test() -async def mean_range_test(dut): - """Test range(n)/n""" - BUS_WIDTH = int(dut.BUS_WIDTH.value) - await value_test(dut, range(1, BUS_WIDTH + 1)) - - -@cocotb.test() -async def mean_overflow_test(dut): - """Test for overflow n*max_val/n = max_val""" - BUS_WIDTH = int(dut.BUS_WIDTH.value) - DATA_WIDTH = int(dut.DATA_WIDTH.value) - await value_test(dut, [2**DATA_WIDTH - 1] * BUS_WIDTH) - - -@cocotb.test() -async def mean_randomised_test(dut): - """Test mean of random numbers multiple times""" - - # dut_in = StreamBusMonitor(dut, "i", dut.clk) # this doesn't work: - # VPI Error vpi_get_value(): - # ERROR - Cannot get a value for an object of type vpiArrayVar. - - dut_out = StreamBusMonitor(dut, "o", dut.clk) - - exp_out = [] - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - scoreboard = Scoreboard(dut) - scoreboard.add_interface(dut_out, exp_out) - - DATA_WIDTH = int(dut.DATA_WIDTH.value) - BUS_WIDTH = int(dut.BUS_WIDTH.value) - dut._log.info('Detected DATA_WIDTH = %d, BUS_WIDTH = %d' % - (DATA_WIDTH, BUS_WIDTH)) - - cocotb.fork(Clock(dut.clk, CLK_PERIOD_NS, units='ns').start()) - - dut.rst <= 1 - for i in range(BUS_WIDTH): - dut.i_data[i] <= 0 - dut.i_valid <= 0 - await RisingEdge(dut.clk) - await RisingEdge(dut.clk) - dut.rst <= 0 - - for j in range(10): - nums = [] - for i in range(BUS_WIDTH): - x = random.randint(0, 2**DATA_WIDTH - 1) - dut.i_data[i] <= x - nums.append(x) - dut.i_valid <= 1 - - nums_mean = sum(nums) // BUS_WIDTH - exp_out.append(nums_mean) - await RisingEdge(dut.clk) - dut.i_valid <= 0 - - await RisingEdge(dut.clk) - await RisingEdge(dut.clk) diff --git a/examples/mean/tests/verilator_waiver.vlt b/examples/mean/tests/verilator_waiver.vlt deleted file mode 100644 index ebfc8ec2..00000000 --- a/examples/mean/tests/verilator_waiver.vlt +++ /dev/null @@ -1,7 +0,0 @@ -// This file is public domain, it can be freely copied without restrictions. -// SPDX-License-Identifier: CC0-1.0 - -// waivers for verilator - -`verilator_config -lint_off -rule WIDTH diff --git a/examples/mean/tests_mixedlang/Makefile b/examples/mean/tests_mixedlang/Makefile deleted file mode 100644 index 7e7faddd..00000000 --- a/examples/mean/tests_mixedlang/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -# This file is public domain, it can be freely copied without restrictions. -# SPDX-License-Identifier: CC0-1.0 - -TOPLEVEL_LANG ?= verilog - -ifneq ($(TOPLEVEL_LANG),verilog) - -all: - @echo "Skipping example due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being verilog" -clean:: - -else - -TOPLEVEL := mean_sv - -PWD=$(shell pwd) - -export PYTHONPATH := $(PWD)/../tests:$(PYTHONPATH) - -VHDL_SOURCES = $(PWD)/../hdl/mean_pkg.vhd -VHDL_SOURCES += $(PWD)/../hdl/mean.vhd - -ifneq ($(filter $(SIM),questa modelsim),) - COMPILE_ARGS += -mixedsvvh -endif - -VERILOG_SOURCES = $(PWD)/../hdl/mean_sv.sv - -MODULE := test_mean - -ifneq ($(filter $(SIM),ius xcelium),) - SIM_ARGS += -v93 -endif - -include $(shell cocotb-config --makefiles)/Makefile.sim - -endif diff --git a/examples/mixed_language/hdl/toplevel.sv b/examples/mixed_language/hdl/toplevel.sv deleted file mode 100644 index f8f6e87f..00000000 --- a/examples/mixed_language/hdl/toplevel.sv +++ /dev/null @@ -1,104 +0,0 @@ -// Example using mixed-language simulation -// -// Here we have a SystemVerilog toplevel that instantiates both SV and VHDL -// sub entities -module endian_swapper_mixed #( - parameter DATA_BYTES = 8 -) ( - input clk, - input reset_n, - - input [DATA_BYTES*8-1:0] stream_in_data, - input [$clog2(DATA_BYTES)-1:0] stream_in_empty, - input stream_in_valid, - input stream_in_startofpacket, - input stream_in_endofpacket, - output reg stream_in_ready, - - output reg [DATA_BYTES*8-1:0] stream_out_data, - output reg [$clog2(DATA_BYTES)-1:0] stream_out_empty, - output reg stream_out_valid, - output reg stream_out_startofpacket, - output reg stream_out_endofpacket, - input stream_out_ready, - - input [1:0] csr_address, - output reg [31:0] csr_readdata, - output reg csr_readdatavalid, - input csr_read, - input csr_write, - output reg csr_waitrequest, - input [31:0] csr_writedata -); - -logic [DATA_BYTES*8-1:0] sv_to_vhdl_data; -logic [$clog2(DATA_BYTES)-1:0] sv_to_vhdl_empty; -logic sv_to_vhdl_valid; -logic sv_to_vhdl_startofpacket; -logic sv_to_vhdl_endofpacket; -logic sv_to_vhdl_ready; - - -endian_swapper_sv #( - .DATA_BYTES (DATA_BYTES) -) i_swapper_sv ( - .clk (clk), - .reset_n (reset_n), - - .stream_in_empty (stream_in_empty), - .stream_in_data (stream_in_data), - .stream_in_endofpacket (stream_in_endofpacket), - .stream_in_startofpacket (stream_in_startofpacket), - .stream_in_valid (stream_in_valid), - .stream_in_ready (stream_in_ready), - - .stream_out_empty (sv_to_vhdl_empty), - .stream_out_data (sv_to_vhdl_data), - .stream_out_endofpacket (sv_to_vhdl_endofpacket), - .stream_out_startofpacket (sv_to_vhdl_startofpacket), - .stream_out_valid (sv_to_vhdl_valid), - .stream_out_ready (sv_to_vhdl_ready), - - .csr_address (csr_address), - .csr_readdata (csr_readdata), - .csr_readdatavalid (csr_readdatavalid), - .csr_read (csr_read), - .csr_write (csr_write), - .csr_waitrequest (csr_waitrequest_sv), - .csr_writedata (csr_writedata) -); - - -endian_swapper_vhdl #( - .DATA_BYTES (DATA_BYTES) -) i_swapper_vhdl ( - .clk (clk), - .reset_n (reset_n), - - .stream_in_empty (sv_to_vhdl_empty), - .stream_in_data (sv_to_vhdl_data), - .stream_in_endofpacket (sv_to_vhdl_endofpacket), - .stream_in_startofpacket (sv_to_vhdl_startofpacket), - .stream_in_valid (sv_to_vhdl_valid), - .stream_in_ready (sv_to_vhdl_ready), - - .stream_out_empty (stream_out_empty), - .stream_out_data (stream_out_data), - .stream_out_endofpacket (stream_out_endofpacket), - .stream_out_startofpacket (stream_out_startofpacket), - .stream_out_valid (stream_out_valid), - .stream_out_ready (stream_out_ready), - - .csr_address (csr_address), - .csr_readdata (), - .csr_readdatavalid (), - .csr_read (csr_read), - .csr_write (csr_write), - .csr_waitrequest (csr_waitrequest_vhdl), - .csr_writedata (~csr_writedata) - -); - -assign csr_waitrequest = csr_waitrequest_sv | csr_waitrequest_vhdl; - -endmodule diff --git a/examples/mixed_language/hdl/toplevel.vhdl b/examples/mixed_language/hdl/toplevel.vhdl deleted file mode 100644 index 192c0737..00000000 --- a/examples/mixed_language/hdl/toplevel.vhdl +++ /dev/null @@ -1,74 +0,0 @@ --- Example using mixed-language simulation --- --- Here we have a VHDL toplevel that instantiates both SV and VHDL --- sub entities -library ieee; - -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -entity endian_swapper_mixed is - generic ( - DATA_BYTES : integer := 8); - port ( - clk : in std_ulogic; - reset_n : in std_ulogic; - - stream_in_data : in std_ulogic_vector(DATA_BYTES*8-1 downto 0); - stream_in_empty : in std_ulogic_vector(2 downto 0); - stream_in_valid : in std_ulogic; - stream_in_startofpacket : in std_ulogic; - stream_in_endofpacket : in std_ulogic; - stream_in_ready : out std_ulogic; - - stream_out_data : out std_ulogic_vector(DATA_BYTES*8-1 downto 0); - stream_out_empty : out std_ulogic_vector(2 downto 0); - stream_out_valid : out std_ulogic; - stream_out_startofpacket: out std_ulogic; - stream_out_endofpacket : out std_ulogic; - stream_out_ready : in std_ulogic; - - csr_address : in std_ulogic_vector(1 downto 0); - csr_readdata : out std_ulogic_vector(31 downto 0); - csr_readdatavalid : out std_ulogic; - csr_read : in std_ulogic; - csr_write : in std_ulogic; - csr_waitrequest : out std_ulogic; - csr_writedata : in std_ulogic_vector(31 downto 0) - ); -end; - -architecture impl of endian_swapper_mixed is begin - -i_swapper_sv : entity work.endian_swapper_sv - generic map ( - DATA_BYTES => DATA_BYTES - ) port map ( - clk => clk, - reset_n => reset_n, - - stream_in_empty => stream_in_empty, - stream_in_data => stream_in_data, - stream_in_endofpacket => stream_in_endofpacket, - stream_in_startofpacket => stream_in_startofpacket, - stream_in_valid => stream_in_valid, - stream_in_ready => stream_in_ready, - - stream_out_empty => stream_out_empty, - stream_out_data => stream_out_data, - stream_out_endofpacket => stream_out_endofpacket, - stream_out_startofpacket=> stream_out_startofpacket, - stream_out_valid => stream_out_valid, - stream_out_ready => stream_out_ready, - - csr_address => csr_address, - csr_readdata => csr_readdata, - csr_readdatavalid => csr_readdatavalid, - csr_read => csr_read, - csr_write => csr_write, - csr_waitrequest => csr_waitrequest, - csr_writedata => csr_writedata - ); - -end architecture; - diff --git a/examples/mixed_language/tests/Makefile b/examples/mixed_language/tests/Makefile deleted file mode 100644 index 723a89ca..00000000 --- a/examples/mixed_language/tests/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -# Override this variable to use a VHDL toplevel instead of SystemVerilog -TOPLEVEL_LANG ?= verilog - -ifeq ($(SIM),) -all: - @echo "Skipping example mixed_language since Icarus doesn't support this" -clean:: -else ifeq ($(shell echo $(SIM) | tr A-Z a-z),icarus) -all: - @echo "Skipping example mixed_language since Icarus doesn't support this" -clean:: -else ifneq ($(shell echo $(TOPLEVEL_LANG) | tr A-Z a-z),verilog) -all: - @echo "Skipping example mixed_language since only Verilog toplevel implemented" -clean:: -else - -TOPLEVEL = endian_swapper_mixed - -PWD=$(shell pwd) - -VERILOG_SOURCES = $(PWD)/../../endian_swapper/hdl/endian_swapper.sv -VHDL_SOURCES = $(PWD)/../../endian_swapper/hdl/endian_swapper.vhdl - -ifeq ($(TOPLEVEL_LANG),verilog) - VERILOG_SOURCES += $(PWD)/../hdl/toplevel.sv -else ifeq ($(TOPLEVEL_LANG),vhdl) - $(error "VHDL toplevel not yet implemented") - VHDL_SOURCES += $(PWD)/../hdl/toplevel.vhdl -else - $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") -endif - - -MODULE=test_mixed_language - -ifneq ($(filter $(SIM),ius xcelium),) - SIM_ARGS += -v93 -endif - -include $(shell cocotb-config --makefiles)/Makefile.sim - -endif diff --git a/examples/mixed_language/tests/test_mixed_language.py b/examples/mixed_language/tests/test_mixed_language.py deleted file mode 100644 index a1cc90d2..00000000 --- a/examples/mixed_language/tests/test_mixed_language.py +++ /dev/null @@ -1,26 +0,0 @@ -import cocotb -from cocotb.triggers import Timer - - -@cocotb.test() -async def mixed_language_test(dut): - """Try accessing handles and setting values in a mixed language environment.""" - await Timer(100, units='ns') - - verilog = dut.i_swapper_sv - dut._log.info("Got: %s" % repr(verilog._name)) - - vhdl = dut.i_swapper_vhdl - dut._log.info("Got: %s" % repr(vhdl._name)) - - verilog.reset_n <= 1 - await Timer(100, units='ns') - - vhdl.reset_n <= 1 - await Timer(100, units='ns') - - assert int(verilog.reset_n) == int(vhdl.reset_n), "reset_n signals were different" - - # Try accessing an object other than a port... - verilog_flush = str(verilog.flush_pipe.value) - vhdl_flush = str(vhdl.flush_pipe.value) diff --git a/examples/mixed_signal/.gitignore b/examples/mixed_signal/.gitignore deleted file mode 100644 index c30a375b..00000000 --- a/examples/mixed_signal/.gitignore +++ /dev/null @@ -1 +0,0 @@ -run.raw diff --git a/examples/mixed_signal/hdl/analog_probe_cadence.sv b/examples/mixed_signal/hdl/analog_probe_cadence.sv deleted file mode 100644 index 1018ed89..00000000 --- a/examples/mixed_signal/hdl/analog_probe_cadence.sv +++ /dev/null @@ -1,38 +0,0 @@ -// This file is public domain, it can be freely copied without restrictions. -// SPDX-License-Identifier: CC0-1.0 - -// see also https://www.eetimes.com/document.asp?doc_id=1279150 - -module analog_probe; - - var string node_to_probe = ""; - - logic probe_voltage_toggle = 0; - real voltage; - - always @(probe_voltage_toggle) begin: probe_voltage - if ($cds_analog_is_valid(node_to_probe, "potential")) begin - voltage = $cds_get_analog_value(node_to_probe, "potential"); - // $display("%m: node_to_probe=%s has voltage of %e V", node_to_probe, voltage); - end else begin - voltage = 1.234567; - $display("%m: Warning: node_to_probe=%s is not valid for $cds_get_analog_value, returning %f V", - node_to_probe, voltage); - end - end // probe_voltage - - logic probe_current_toggle = 0; - real current; - - always @(probe_current_toggle) begin: probe_current - if ($cds_analog_is_valid(node_to_probe, "flow")) begin - current = $cds_get_analog_value(node_to_probe, "flow"); - // $display("%m: node_to_probe=%s has current of %e A", node_to_probe, current); - end else begin - current = 0.123456; - $display("%m: Warning: node_to_probe=%s is not valid for $cds_get_analog_value, returning %f A", - node_to_probe, current); - end - end // probe_current - -endmodule diff --git a/examples/mixed_signal/hdl/analog_probe_synopsys.sv b/examples/mixed_signal/hdl/analog_probe_synopsys.sv deleted file mode 100644 index 611a4166..00000000 --- a/examples/mixed_signal/hdl/analog_probe_synopsys.sv +++ /dev/null @@ -1,22 +0,0 @@ -// This file is public domain, it can be freely copied without restrictions. -// SPDX-License-Identifier: CC0-1.0 - -module analog_probe; - - var string node_to_probe = ""; - - logic probe_voltage_toggle = 0; - real voltage; - - always @(probe_voltage_toggle) begin: probe_voltage - voltage = $snps_get_volt(node_to_probe); - end - - logic probe_current_toggle = 0; - real current; - - always @(probe_current_toggle) begin: probe_current - current = $snps_get_port_current(node_to_probe); - end - -endmodule diff --git a/examples/mixed_signal/hdl/capacitor.vams b/examples/mixed_signal/hdl/capacitor.vams deleted file mode 100644 index 84ce0fca..00000000 --- a/examples/mixed_signal/hdl/capacitor.vams +++ /dev/null @@ -1,20 +0,0 @@ -// This file is public domain, it can be freely copied without restrictions. -// SPDX-License-Identifier: CC0-1.0 - -`include "disciplines.vams" -`include "constants.vams" - -module capacitor(p, n); - - inout p; electrical p; - inout n; electrical n; - - parameter real capacitance=1 from [0:inf); // capacitance value, must be >= 0 - - branch (p, n) cap; - - analog begin - I(cap) <+ capacitance * ddt(V(cap)); - end - -endmodule diff --git a/examples/mixed_signal/hdl/nettypes_pkg_cadence.sv b/examples/mixed_signal/hdl/nettypes_pkg_cadence.sv deleted file mode 100644 index 222fcfc6..00000000 --- a/examples/mixed_signal/hdl/nettypes_pkg_cadence.sv +++ /dev/null @@ -1,10 +0,0 @@ -// This file is public domain, it can be freely copied without restrictions. -// SPDX-License-Identifier: CC0-1.0 - -import cds_rnm_pkg::*; - -package nettypes_pkg; - - nettype wreal1driver voltage_net; - -endpackage diff --git a/examples/mixed_signal/hdl/nettypes_pkg_synopsys.sv b/examples/mixed_signal/hdl/nettypes_pkg_synopsys.sv deleted file mode 100644 index 104c0877..00000000 --- a/examples/mixed_signal/hdl/nettypes_pkg_synopsys.sv +++ /dev/null @@ -1,13 +0,0 @@ -// This file is public domain, it can be freely copied without restrictions. -// SPDX-License-Identifier: CC0-1.0 - -package nettypes_pkg; - - nettype real voltage_net with r_res; - - function automatic real r_res(input real drivers []); - r_res = 0.0; - foreach(drivers[k]) r_res += drivers[k]; - endfunction - -endpackage diff --git a/examples/mixed_signal/hdl/regulator.sv b/examples/mixed_signal/hdl/regulator.sv deleted file mode 100644 index 5d356ca6..00000000 --- a/examples/mixed_signal/hdl/regulator.sv +++ /dev/null @@ -1,28 +0,0 @@ -// This file is public domain, it can be freely copied without restrictions. -// SPDX-License-Identifier: CC0-1.0 - -module regulator(input vdd, - input [3:0] trim, - output vout, - input vss); - - regulator_block - #(.vout_abs (3.3), - .trim_step (0.2)) - i_regulator_block - ( - .vdd (vdd), - .trim (trim), - .vout (vout), - .vss (vss) - ); - - resistor - #(.resistance (100)) - i_resistor - ( - .p (vout), - .n (vss) - ); - -endmodule diff --git a/examples/mixed_signal/hdl/regulator.vams b/examples/mixed_signal/hdl/regulator.vams deleted file mode 100644 index 440d530b..00000000 --- a/examples/mixed_signal/hdl/regulator.vams +++ /dev/null @@ -1,29 +0,0 @@ -// This file is public domain, it can be freely copied without restrictions. -// SPDX-License-Identifier: CC0-1.0 - -`include "disciplines.vams" -`include "constants.vams" - -module regulator(vdd, trim, vout, vss); - - input signed [3:0] trim; logic trim; - inout vdd; electrical vdd; - inout vout; electrical vout; - inout vss; electrical vss; - - parameter real vout_abs=3.3 from (0:inf); // must be > 0 - parameter real trim_step=0.2 from (0:inf); // must be > 0 - - real trim_volt; - - always @(trim) begin - trim_volt = trim * trim_step; - // $display("%m: trim=%0d --> trim_volt=%f", trim, trim_volt); - end - - analog begin - // FIXME: must limit voltage at vdd/vss - V(vout, vss) <+ transition(vout_abs + trim_volt, 0, 20p); - end - -endmodule diff --git a/examples/mixed_signal/hdl/regulator_block.vams b/examples/mixed_signal/hdl/regulator_block.vams deleted file mode 100644 index 1c729964..00000000 --- a/examples/mixed_signal/hdl/regulator_block.vams +++ /dev/null @@ -1,29 +0,0 @@ -// This file is public domain, it can be freely copied without restrictions. -// SPDX-License-Identifier: CC0-1.0 - -`include "disciplines.vams" -`include "constants.vams" - -module regulator_block(vdd, trim, vout, vss); - - input signed [3:0] trim; logic trim; - inout vdd; electrical vdd; - inout vout; electrical vout; - inout vss; electrical vss; - - parameter real vout_abs=3.3 from (0:inf); // must be > 0 - parameter real trim_step=0.2 from (0:inf); // must be > 0 - - real trim_volt; - - always @(trim) begin - trim_volt = trim * trim_step; - // $display("%m: trim=%0d --> trim_volt=%f", trim, trim_volt); - end - - analog begin - // TODO: limit voltage to vdd/vss - V(vout, vss) <+ transition(vout_abs + trim_volt, 0, 20p); - end - -endmodule diff --git a/examples/mixed_signal/hdl/rescap.sv b/examples/mixed_signal/hdl/rescap.sv deleted file mode 100644 index 87bbec1c..00000000 --- a/examples/mixed_signal/hdl/rescap.sv +++ /dev/null @@ -1,25 +0,0 @@ -// This file is public domain, it can be freely copied without restrictions. -// SPDX-License-Identifier: CC0-1.0 - -// the design-under-test -module rescap(input interconnect vdd, - output interconnect vout, - input interconnect vss); - - resistor - #(.resistance (1e5)) - i_resistor - ( - .p (vdd), - .n (vout) - ); - - capacitor - #(.capacitance (1e-12)) - i_capacitor - ( - .p (vout), - .n (vss) - ); - -endmodule diff --git a/examples/mixed_signal/hdl/resistor.vams b/examples/mixed_signal/hdl/resistor.vams deleted file mode 100644 index 5f315dba..00000000 --- a/examples/mixed_signal/hdl/resistor.vams +++ /dev/null @@ -1,18 +0,0 @@ -// This file is public domain, it can be freely copied without restrictions. -// SPDX-License-Identifier: CC0-1.0 - -`include "disciplines.vams" -`include "constants.vams" - -module resistor(p, n); - - inout p; electrical p; - inout n; electrical n; - - parameter real resistance=1 from (0:inf); // resistance value, must be > 0 - - analog begin - V(p,n) <+ I(p,n) * resistance; - end - -endmodule diff --git a/examples/mixed_signal/hdl/tb_regulator.sv b/examples/mixed_signal/hdl/tb_regulator.sv deleted file mode 100644 index 3e6f8170..00000000 --- a/examples/mixed_signal/hdl/tb_regulator.sv +++ /dev/null @@ -1,25 +0,0 @@ -// This file is public domain, it can be freely copied without restrictions. -// SPDX-License-Identifier: CC0-1.0 - -import nettypes_pkg::*; - -module tb_regulator; - - voltage_net vdd, vss, vout; - real vdd_val, vss_val = 0.0; - logic signed [3:0] trim_val = 0; - - assign vdd = vdd_val; - assign vss = vss_val; - - // the design - regulator i_regulator (.vdd (vdd), - .trim (trim_val), - .vout (vout), - .vss (vss) - ); - - // the "multimeter" - analog_probe i_analog_probe (); - -endmodule diff --git a/examples/mixed_signal/hdl/tb_rescap.sv b/examples/mixed_signal/hdl/tb_rescap.sv deleted file mode 100644 index e0a1b31a..00000000 --- a/examples/mixed_signal/hdl/tb_rescap.sv +++ /dev/null @@ -1,25 +0,0 @@ -// This file is public domain, it can be freely copied without restrictions. -// SPDX-License-Identifier: CC0-1.0 - -import nettypes_pkg::*; - -module tb_rescap; - - voltage_net vdd, vss; - real vdd_val, vss_val = 0.0; - - assign vdd = vdd_val; - assign vss = vss_val; - - interconnect vout; - - // the design - rescap i_rescap (.vdd (vdd), - .vout (vout), - .vss (vss) - ); - - // the "multimeter" - analog_probe i_analog_probe (); - -endmodule diff --git a/examples/mixed_signal/tests/Makefile b/examples/mixed_signal/tests/Makefile deleted file mode 100644 index 2a120ed1..00000000 --- a/examples/mixed_signal/tests/Makefile +++ /dev/null @@ -1,89 +0,0 @@ -# Override this variable to use a VHDL toplevel instead of (System)Verilog -TOPLEVEL_LANG ?= verilog - -TOPLEVEL ?= tb_rescap - -ifeq ($(TOPLEVEL),tb_rescap) - MODULE ?= test_rescap,test_rescap_minimalist -else ifeq ($(TOPLEVEL),tb_regulator) - MODULE ?= test_regulator_plot,test_regulator_trim -else - $(error Given TOPLEVEL '$(TOPLEVEL)' not supported) -endif - -ifeq ($(OS),Msys) - WPWD=$(shell sh -c 'pwd -W') -else - WPWD=$(shell pwd) -endif - - -# Files - -VERILOG_SOURCES += $(WPWD)/../hdl/resistor.vams -VERILOG_SOURCES += $(WPWD)/../hdl/capacitor.vams -VERILOG_SOURCES += $(WPWD)/../hdl/regulator_block.vams - -ifeq ($(SIM),$(filter $(SIM),ius xcelium)) - VERILOG_SOURCES += $(WPWD)/../hdl/analog_probe_cadence.sv - ifeq ($(TOPLEVEL_LANG),verilog) - VERILOG_SOURCES += $(WPWD)/../hdl/nettypes_pkg_cadence.sv - VERILOG_SOURCES += $(WPWD)/../hdl/rescap.sv - VERILOG_SOURCES += $(WPWD)/../hdl/regulator.sv - VERILOG_SOURCES += $(WPWD)/../hdl/tb_rescap.sv - VERILOG_SOURCES += $(WPWD)/../hdl/tb_regulator.sv - else ifeq ($(TOPLEVEL_LANG),vhdl) - $(error "VHDL toplevel not yet implemented") - VHDL_SOURCES += $(WPWD)/../hdl/rescap.vhdl - VHDL_SOURCES += $(WPWD)/../hdl/regulator.vhdl - else - $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") - endif -else ifeq ($(SIM),vcs) - VERILOG_SOURCES += $(WPWD)/../hdl/analog_probe_synopsys.sv - ifeq ($(TOPLEVEL_LANG),verilog) - VERILOG_SOURCES += $(WPWD)/../hdl/nettypes_pkg_synopsys.sv - VERILOG_SOURCES += $(WPWD)/../hdl/rescap.sv - VERILOG_SOURCES += $(WPWD)/../hdl/regulator.sv - VERILOG_SOURCES += $(WPWD)/../hdl/tb_rescap.sv - VERILOG_SOURCES += $(WPWD)/../hdl/tb_regulator.sv - else ifeq ($(TOPLEVEL_LANG),vhdl) - $(error "VHDL toplevel not yet implemented") - VHDL_SOURCES += $(WPWD)/../hdl/rescap.vhdl - VHDL_SOURCES += $(WPWD)/../hdl/regulator.vhdl - else - $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") - endif -else - $(error "This example does not have support for SIM=$(SIM)") -endif - - -# Options - -COCOTB_HDL_TIMEUNIT=1ns -COCOTB_HDL_TIMEPRECISION=1ps - -ifeq ($(SIM),$(filter $(SIM),ius xcelium)) - SIM_ARGS += -discipline logic - SIM_ARGS += -amsconnrules ConnRules_full_fast - SIM_ARGS += +dr_info - SIM_ARGS += -iereport - SIM_ARGS += -ieinfo - SIM_ARGS += -ieinfo_log $(SIM_BUILD)/ams_ieinfo.log - SIM_ARGS += -spectre_args "+lqtimeout 7200" - SIM_ARGS += run.scs - SIM_ARGS += $(EXTRA_SIM_ARGS) -else ifeq ($(SIM),vcs) - COMPILE_ARGS += -ad=./vcsAD.init - COMPILE_ARGS += -ams - COMPILE_ARGS += -ams_iereport - COMPILE_ARGS += -xlrm coerce_nettype -# COMPILE_ARGS += -ams_discipline - SIM_ARGS += $(EXTRA_SIM_ARGS) -else - $(error "This example does not have support for SIM=$(SIM)") -endif - - -include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/examples/mixed_signal/tests/run.scs b/examples/mixed_signal/tests/run.scs deleted file mode 100644 index 8a84f126..00000000 --- a/examples/mixed_signal/tests/run.scs +++ /dev/null @@ -1,11 +0,0 @@ -// Cadence Spectre control file for analog solver -//tran tran stop=100u step=5n maxstep=5n relref=alllocal checklimitdest=both errpreset=conservative -tran tran stop=100u maxstep=5n - -// connectmodule selection when not done via xrun -amsconnrules -// amsd { -// ie vsup=5.0 -// } - -// saveOptions options save=all pwr=all currents=all saveahdlvars=all depth=3 -// save foo:currents diff --git a/examples/mixed_signal/tests/test_regulator_plot.py b/examples/mixed_signal/tests/test_regulator_plot.py deleted file mode 100644 index 4a607ad2..00000000 --- a/examples/mixed_signal/tests/test_regulator_plot.py +++ /dev/null @@ -1,69 +0,0 @@ -# This file is public domain, it can be freely copied without restrictions. -# SPDX-License-Identifier: CC0-1.0 - -import cocotb -from cocotb.triggers import Timer -import math -import matplotlib.pyplot as plt - - -@cocotb.test() -async def test_trim_vals(tb_hdl): - """Set trim value of regulator and measure resulting voltage.""" - - probed_node = "tb_regulator.i_regulator.vout" - tb_hdl.vdd_val <= 7.7 - tb_hdl.vss_val <= 0.0 - - probedata = [] - for trim_val in [0, 3, -5]: - tb_hdl.trim_val <= trim_val - await Timer(1, units="ns") - trimmed_volt = await get_voltage(tb_hdl, probed_node) - tb_hdl._log.info( - "trim_val={} results in {}={:.4} V".format( - tb_hdl.trim_val.value.signed_integer, probed_node, trimmed_volt - ) - ) - # sanity check: output voltage can not exceed supply - assert tb_hdl.vss_val.value <= trimmed_volt <= tb_hdl.vdd_val.value - probedata.append((tb_hdl.trim_val.value.signed_integer, trimmed_volt)) - - plot_data(tb_hdl, datasets=probedata, graphfile="regulator.png") - - -async def get_voltage(tb_hdl, node): - """Measure voltage on *node*.""" - await Timer(1, units="ps") # let trim_val take effect - tb_hdl.i_analog_probe.node_to_probe <= node.encode("ascii") - tb_hdl.i_analog_probe.probe_voltage_toggle <= ~int(tb_hdl.i_analog_probe.probe_voltage_toggle) - await Timer(1, units="ps") # waiting time needed for the analog values to be updated - tb_hdl._log.debug("Voltage on node {} is {:.4} V".format( - node, tb_hdl.i_analog_probe.voltage.value)) - return tb_hdl.i_analog_probe.voltage.value - - -def plot_data(tb_hdl, datasets, graphfile="cocotb_plot.png"): - """Save a graph to file *graphfile*. - - Trim and voltage value are contained in *datasets*. - """ - trim, voltage = zip(*datasets) - trim_round = range(1, len(trim)+1) - - fig = plt.figure() - ax = plt.axes() - ax.set_title("Probed node: {}".format(tb_hdl.i_analog_probe.node_to_probe.value.decode("ascii"))) - ax.set_ylabel("Voltage (V)") - ax.set_ylim([0, math.ceil(max(voltage))+1]) - ax.step(trim_round, voltage, where="mid") - ax.plot(trim_round, voltage, 'C0o', alpha=0.5) - for i, j, k in zip(trim_round, trim, voltage): - ax.annotate("trim={}".format(j), xy=(i, k), xytext=(0, 5), textcoords='offset points', ha='center') - ax.xaxis.set_major_locator(plt.NullLocator()) - ax.xaxis.set_major_formatter(plt.NullFormatter()) - fig.tight_layout() - fig.set_size_inches(11, 6) - - tb_hdl._log.info("Writing file {}".format(graphfile)) - fig.savefig(graphfile) diff --git a/examples/mixed_signal/tests/test_regulator_trim.py b/examples/mixed_signal/tests/test_regulator_trim.py deleted file mode 100644 index 52f89edb..00000000 --- a/examples/mixed_signal/tests/test_regulator_trim.py +++ /dev/null @@ -1,123 +0,0 @@ -# This file is public domain, it can be freely copied without restrictions. -# SPDX-License-Identifier: CC0-1.0 - -import cocotb -from cocotb.triggers import Timer - -import itertools - - -class Regulator_TB(object): - """Class for collecting testbench objects. - - Args: - tb_hdl: The toplevel of the design-under-test. - In this mixed cocotb/HDL testbench environment, it is the HDL testbench. - settling_time_ns (int): Time in nanoseconds to wait before sample is taken. - """ - - def __init__(self, tb_hdl, settling_time_ns=1): - self.tb_hdl = tb_hdl - self.settling_time_ns = settling_time_ns - self.analog_probe = ( - tb_hdl.i_analog_probe - ) #: The instance name of the analog probe module. - self._bit_toggle_stream = itertools.cycle( - [0, 1] - ) #: Produce bitstream that toggles between ``0`` and ``1``. - - async def get_voltage(self, node): - """Measure voltage on *node*.""" - toggle = next(self._bit_toggle_stream) - self.tb_hdl.i_analog_probe.node_to_probe <= node.encode("ascii") - self.analog_probe.probe_voltage_toggle <= toggle - await Timer(1, units="ps") # waiting time needed for the analog values to be updated - self.tb_hdl._log.debug( - "trim value={}: {}={:.4} V".format( - self.tb_hdl.trim_val.value.signed_integer, - self.analog_probe.node_to_probe.value.decode("ascii"), - self.analog_probe.voltage.value, - ) - ) - return self.analog_probe.voltage.value - - async def find_trim_val(self, probed_node, target_volt, trim_val_node): - """Calculate best trimming value for *target_volt*. - Algorithm is to measure voltage of *probed_node* at - lowest and highest trim value, - then calculate the slope and finally the target trim value from slope. - Assumes a linear behaviour. - - Args: - probed_node: The node to probe for the trimmed voltage. - target_volt (float): The target voltage at *probed_node*. - trim_val_node: The node to apply the trim value to. - - Yields: - float: The calculated best value for *trim_val_node*. - """ - # assuming two's complement - trim_val_min = -2 ** (trim_val_node.value.n_bits - 1) - trim_val_max = 2 ** (trim_val_node.value.n_bits - 1) - 1 - # the actual trimming procedure: - # minimum values - trim_val_node <= trim_val_min - await Timer(self.settling_time_ns, units="ns") - volt_min = await self.get_voltage(probed_node) - # maximum values - trim_val_node <= trim_val_max - await Timer(self.settling_time_ns, units="ns") - volt_max = await self.get_voltage(probed_node) - if target_volt > volt_max: - self.tb_hdl._log.debug( - "target_volt={} > volt_max={}, returning minimum trim value {}".format( - target_volt, volt_max, trim_val_max - ) - ) - return trim_val_max - if target_volt < volt_min: - self.tb_hdl._log.debug( - "target_volt={} < volt_min={}, returning maximum trim value {}".format( - target_volt, volt_min, trim_val_min - ) - ) - return trim_val_min - # do the calculation: - slope = (trim_val_max - trim_val_min) / (volt_max - volt_min) - target_trim = (target_volt - volt_min) * slope + trim_val_min - return target_trim - - -@cocotb.test() -async def run_test(tb_hdl): - """Run test for mixed signal simulation - automatic trimming of a voltage regulator.""" - - tb_py = Regulator_TB(tb_hdl) - node = "tb_regulator.i_regulator.vout" - - _ = await tb_py.get_voltage( - node - ) # NOTE: dummy read apparently needed because of $cds_get_analog_value in analog_probe - - # show automatic trimming - target_volt = 3.013 - tb_py.tb_hdl._log.info( - "Running trimming algorithm for target voltage {:.4} V".format( - target_volt - ) - ) - best_trim_float = await tb_py.find_trim_val( - probed_node=node, target_volt=target_volt, trim_val_node=tb_py.tb_hdl.trim_val - ) - best_trim_rounded = round(best_trim_float) - tb_py.tb_hdl.trim_val <= best_trim_rounded - await Timer(tb_py.settling_time_ns, units="ns") - trimmed_volt = await tb_py.get_voltage(node) - tb_py.tb_hdl._log.info( - "Best trimming value is {} " - "--> voltage is {:.4} V (difference to target is {:.4} V)".format( - best_trim_rounded, trimmed_volt, trimmed_volt-target_volt - ) - ) - best_trim_rounded_exp = -1 - assert best_trim_rounded == best_trim_rounded_exp diff --git a/examples/mixed_signal/tests/test_rescap.py b/examples/mixed_signal/tests/test_rescap.py deleted file mode 100644 index 6f958739..00000000 --- a/examples/mixed_signal/tests/test_rescap.py +++ /dev/null @@ -1,150 +0,0 @@ -# This file is public domain, it can be freely copied without restrictions. -# SPDX-License-Identifier: CC0-1.0 - -import cocotb -from cocotb.triggers import Timer -from cocotb.utils import get_sim_time - -from collections import namedtuple, defaultdict - -from itertools import cycle - -import matplotlib.pyplot as plt - -Dataset = namedtuple("Dataset", "time, voltage, current") - - -class ResCap_TB(object): - """The testbench class for the rescap design.""" - def __init__(self, tb_hdl): - self.tb_hdl = tb_hdl - self.analog_probe = ( - tb_hdl.i_analog_probe - ) #: The instance name of the analog probe module. - self.togglestream = cycle(range(2)) # toggle between 0 and 1 - - async def _get_single_sample(self, node): - toggle = next(self.togglestream) - self.tb_hdl.i_analog_probe.node_to_probe <= node.encode('ascii') - self.analog_probe.probe_voltage_toggle <= toggle - self.analog_probe.probe_current_toggle <= toggle - await Timer(1, units="ps") # waiting time needed for the analog values to be updated - dataset = Dataset( - time=get_sim_time(units="ns"), - voltage=self.analog_probe.voltage.value, - current=self.analog_probe.current.value * 1000.0 # in mA - ) - self.tb_hdl._log.debug( - "{}={:.4} V, {:.4} mA".format( - self.analog_probe.node_to_probe.value.decode("ascii"), dataset.voltage, dataset.current - ) - ) - return dataset - - async def get_sample_data(self, nodes, num=1, delay_ns=1): - """For all *nodes*, get *num* samples, spaced *delay_ns* apart. - - Yields: - list: List (*num* samples long) of :any:`Dataset` for all *nodes*. - """ - if not isinstance(nodes, list): # single element? make it a list - _nodes = [nodes] - else: - _nodes = nodes - datasets = defaultdict(list) - for idx in range(num): - for node in _nodes: - dataset = await self._get_single_sample(node) - datasets[node].append(dataset) - if idx != num: - await Timer(delay_ns, units="ns") - return datasets - - def plot_data(self, datasets, nodes, graphfile="cocotb_plot.png"): - """Save a charge graph of *nodes* to file *graphfile*. - - Voltage and current value are contained in *datasets*. - """ - fig, ax_volt = plt.subplots() - color_volt = "tab:red" - color_curr = "tab:blue" - ax_volt.set_title("rescap") - ax_volt.set_xlabel("Time (ns)") - ax_volt.set_ylabel("Voltage (V)", color=color_volt) - ax_curr = ax_volt.twinx() # instantiate a second axis that shares the same x-axis - ax_curr.set_ylabel("Current (mA)", color=color_curr) # we already handled the x-label with ax_volt - - for node in nodes: - time, voltage, current = zip(*(datasets[node])) - if node.endswith("vout"): - alpha = 1.0 - else: - alpha = 0.333 - ax_volt.plot(time, voltage, color=color_volt, alpha=alpha, - marker=".", markerfacecolor="black", linewidth=1, label="V({})".format(node)) - ax_curr.plot(time, current, color=color_curr, alpha=alpha, - marker=".", markerfacecolor="black", linewidth=1, label="I({})".format(node)) - - ax_volt.tick_params(axis="y", labelcolor=color_volt) - ax_curr.tick_params(axis="y", labelcolor=color_curr) - ax_volt.axhline(linestyle=":", color="gray") - - mpl_align_yaxis(ax_volt, 0, ax_curr, 0) - fig.tight_layout() # otherwise the right y-label is slightly clipped - fig.set_size_inches(11, 6) - fig.legend(loc="upper right", bbox_to_anchor=(0.8, 0.9), frameon=False) - - self.tb_hdl._log.info("Writing file {}".format(graphfile)) - fig.savefig(graphfile) - - -@cocotb.test() -async def run_test(tb_hdl): - """Run test for mixed signal resistor/capacitor simulation.""" - - tb_py = ResCap_TB(tb_hdl) - - nodes_to_probe = ["tb_rescap.i_rescap.vdd", "tb_rescap.i_rescap.vout"] - # nodes_to_probe = ["tb_rescap.i_rescap.vdd", "tb_rescap.i_rescap.i_capacitor.p"] - - probedata = defaultdict(list) - - vdd = 0.0 - tb_py.tb_hdl.vdd_val <= vdd - tb_py.tb_hdl.vss_val <= 0.0 - tb_py.tb_hdl._log.info("Setting vdd={:.4} V".format(vdd)) - # dummy read appears to be necessary for the analog solver - _ = await tb_py.get_sample_data(nodes=nodes_to_probe) - - for vdd in [5.55, -3.33]: - tb_py.tb_hdl.vdd_val <= vdd - tb_py.tb_hdl.vss_val <= 0.0 - tb_py.tb_hdl._log.info("Setting vdd={:.4} V".format(vdd)) - data = await tb_py.get_sample_data(num=60, delay_ns=5, nodes=nodes_to_probe) - for node in nodes_to_probe: - probedata[node].extend(data[node]) - - tb_py.plot_data(datasets=probedata, nodes=nodes_to_probe, graphfile="rescap.png") - - -def mpl_align_yaxis(ax1, v1, ax2, v2): - """Adjust ax2 ylimit so that v2 in ax2 is aligned to v1 in ax1.""" - _, y1 = ax1.transData.transform((0, v1)) - _, y2 = ax2.transData.transform((0, v2)) - mpl_adjust_yaxis(ax2, (y1 - y2) / 2, v2) - mpl_adjust_yaxis(ax1, (y2 - y1) / 2, v1) - - -def mpl_adjust_yaxis(ax, ydif, v): - """Shift axis ax by ydiff, maintaining point v at the same location.""" - inv = ax.transData.inverted() - _, dy = inv.transform((0, 0)) - inv.transform((0, ydif)) - miny, maxy = ax.get_ylim() - miny, maxy = miny - v, maxy - v - if -miny > maxy or (-miny == maxy and dy > 0): - nminy = miny - nmaxy = miny * (maxy + dy) / (miny + dy) - else: - nmaxy = maxy - nminy = maxy * (miny + dy) / (maxy + dy) - ax.set_ylim(nminy + v, nmaxy + v) diff --git a/examples/mixed_signal/tests/test_rescap_minimalist.py b/examples/mixed_signal/tests/test_rescap_minimalist.py deleted file mode 100644 index 80b39bd8..00000000 --- a/examples/mixed_signal/tests/test_rescap_minimalist.py +++ /dev/null @@ -1,27 +0,0 @@ -# This file is public domain, it can be freely copied without restrictions. -# SPDX-License-Identifier: CC0-1.0 - -import cocotb -from cocotb.triggers import Timer - - -@cocotb.test() -async def rescap_minimalist_test(tb_hdl): - """Mixed signal resistor/capacitor simulation, minimalistic.""" - - tb_hdl.vdd_val <= 7.7 - tb_hdl.vss_val <= 0.0 - tb_hdl.i_analog_probe.node_to_probe <= "tb_rescap.i_rescap.vout".encode("ascii") - - for toggle in [1, 0, 1, 0, 1, 0]: - await Timer(50, units="ns") - tb_hdl.i_analog_probe.probe_voltage_toggle <= toggle - tb_hdl.i_analog_probe.probe_current_toggle <= toggle - await Timer(1, units="ps") # waiting time needed for the analog values to be updated - tb_hdl._log.info( - "tb_hdl.i_analog_probe@{}={:.4} V {:.4} A".format( - tb_hdl.i_analog_probe.node_to_probe.value.decode("ascii"), - tb_hdl.i_analog_probe.voltage.value, - tb_hdl.i_analog_probe.current.value - ) - ) diff --git a/examples/mixed_signal/tests/vcsAD.init b/examples/mixed_signal/tests/vcsAD.init deleted file mode 100644 index c1282fb3..00000000 --- a/examples/mixed_signal/tests/vcsAD.init +++ /dev/null @@ -1,4 +0,0 @@ -choose hsim spice/foo.cdl; -partition -cell subckt1 subckt2; -set bus_format [%d]; - diff --git a/examples/simple_dff/.gitignore b/examples/simple_dff/.gitignore deleted file mode 100644 index 5004c67f..00000000 --- a/examples/simple_dff/.gitignore +++ /dev/null @@ -1,45 +0,0 @@ -# Python -__pycache__ - -# Waveforms -*.vcd - -# Results -results.xml -sim_build - -# VCS files -*.tab -ucli.key - -# Cadence Incisive/Xcelium -*.elog -irun.log -xrun.log -irun.key -xrun.key -irun.history -xrun.history -INCA_libs -xcelium.d -ncelab_*.err -xmelab_*.err -ncsim_*.err -xmsim_*.err -bpad_*.err -.bpad/ -.simvision/ -waves.shm/ - -# Mentor Modelsim/Questa -modelsim.ini -transcript -*.wlf - -# Riviera -library.cfg -dataset.asdb -compile -library.cfg -dataset.asdb -compile diff --git a/examples/simple_dff/Makefile b/examples/simple_dff/Makefile deleted file mode 100644 index 2957531d..00000000 --- a/examples/simple_dff/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# This file is public domain, it can be freely copied without restrictions. -# SPDX-License-Identifier: CC0-1.0 - -TOPLEVEL_LANG ?= verilog - -ifeq ($(TOPLEVEL_LANG),verilog) - VERILOG_SOURCES = $(shell pwd)/dff.sv -else ifeq ($(TOPLEVEL_LANG),vhdl) - VHDL_SOURCES = $(shell pwd)/dff.vhdl -endif - -MODULE = test_dff -TOPLEVEL = dff - -include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/examples/simple_dff/dff.sv b/examples/simple_dff/dff.sv deleted file mode 100644 index 442389f7..00000000 --- a/examples/simple_dff/dff.sv +++ /dev/null @@ -1,15 +0,0 @@ -// This file is public domain, it can be freely copied without restrictions. -// SPDX-License-Identifier: CC0-1.0 - -`timescale 1us/1us - -module dff ( - input logic clk, d, - output logic q -); - -always @(posedge clk) begin - q <= d; -end - -endmodule diff --git a/examples/simple_dff/dff.vhdl b/examples/simple_dff/dff.vhdl deleted file mode 100644 index 50478cb5..00000000 --- a/examples/simple_dff/dff.vhdl +++ /dev/null @@ -1,21 +0,0 @@ --- 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 is -port( - clk: in std_logic; - d: in std_logic; - q: out std_logic); -end dff; - -architecture behavioral of dff is -begin - process (clk) begin - if rising_edge(clk) then - q <= d; - end if; - end process; -end behavioral; diff --git a/examples/simple_dff/test_dff.py b/examples/simple_dff/test_dff.py deleted file mode 100644 index 06747eb1..00000000 --- a/examples/simple_dff/test_dff.py +++ /dev/null @@ -1,22 +0,0 @@ -# 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.triggers import FallingEdge - - -@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 - - 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) diff --git a/makefiles/Makefile.inc b/makefiles/Makefile.inc deleted file mode 100644 index bc71307c..00000000 --- a/makefiles/Makefile.inc +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause - -$(error Outdated use of cocotb detected. \ - Please install cocotb, and modify your makefile to \ - "include $$(shell cocotb-config --makefile)/Makefile.sim" instead. \ - Please refer to the documentation at https://docs.cocotb.org.) diff --git a/makefiles/Makefile.sim b/makefiles/Makefile.sim deleted file mode 100644 index bc71307c..00000000 --- a/makefiles/Makefile.sim +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause - -$(error Outdated use of cocotb detected. \ - Please install cocotb, and modify your makefile to \ - "include $$(shell cocotb-config --makefile)/Makefile.sim" instead. \ - Please refer to the documentation at https://docs.cocotb.org.) diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index b8c511fa..00000000 --- a/pyproject.toml +++ /dev/null @@ -1,39 +0,0 @@ -[build-system] -requires = ["setuptools", "wheel", "setuptools_scm"] -build-backend = "setuptools.build_meta" - -[tool.towncrier] - package = "cocotb" - directory = "documentation/source/newsfragments" - filename = "documentation/source/release_notes.rst" - issue_format = ":pr:`{issue}`" - # The first underline is used for the version/date header, - # the second underline for the subcategories (like 'Features') - underlines = ["=", "-"] - all_bullets = false - - [[tool.towncrier.type]] - directory = "feature" - name = "Features" - showcontent = true - - [[tool.towncrier.type]] - directory = "bugfix" - name = "Bugfixes" - showcontent = true - - [[tool.towncrier.type]] - directory = "doc" - name = "Improved Documentation" - showcontent = true - - [[tool.towncrier.type]] - directory = "removal" - name = "Deprecations and Removals" - showcontent = true - - [[tool.towncrier.type]] - directory = "change" - name = "Changes" - showcontent = true - diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 7f5d1a7a..00000000 --- a/setup.cfg +++ /dev/null @@ -1,39 +0,0 @@ -[flake8] -exclude = - bin - documentation - makefiles - venv - .tox - examples/endian_swapper/cosim - _vendor - -ignore = - E202 # whitespace before ')' - E203 # whitespace before ',' / ':' - E221 # multiple spaces before operator - E225 # missing whitespace around operator - E226 # missing whitespace around arithmetic operator - E228 # missing whitespace around modulo operator - E231 # missing whitespace after ... - E241 # multiple spaces after ... - E402 # module level import not at top of file - E501 # line too long (... > 79 characters) - E741 # ambiguous variable name 'l' - F405 # ... may be undefined, or defined from star - W504 # line break after binary operator - -per-file-ignores = - # F841 - local variable ... is assigned to but never used - tests/*: F841 - examples/*: F841 - -[tool:pytest] -addopts = -v --cov=cocotb --cov-branch --doctest-modules -testpaths = - tests/pytest - cocotb/utils.py - cocotb/binary.py - cocotb/_sim_versions.py -# log_cli = true -# log_cli_level = DEBUG diff --git a/setup.py b/setup.py deleted file mode 100755 index e78309e7..00000000 --- a/setup.py +++ /dev/null @@ -1,138 +0,0 @@ -#!/usr/bin/env python -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -import sys -if sys.version_info[:2] < (3, 5): - msg = [ - "This version of cocotb requires at least Python 3.5,", - "you are running Python %d.%d.%d." % ( - sys.version_info[0], sys.version_info[1], sys.version_info[2]) - ] - if sys.version_info[0] == 2: - msg += [ - "If you have Python 3 installed on your machine try ", - "using 'python3 -m pip' instead of 'pip' to install cocotb." - ] - msg += [ - "For more information please refer to the documentation at ", - "https://cocotb.readthedocs.io." - ] - - raise SystemExit("\n".join(msg)) - -import logging -from setuptools import setup -from setuptools import find_packages -from os import path, walk -from io import StringIO - -# Note: cocotb is not installed properly yet and is missing dependencies and binaries -# We can still import other files next to setup.py, as long as they're in MANIFEST.in -# The below line is necessary for PEP517 support -sys.path.append(path.dirname(__file__)) -from cocotb_build_libs import get_ext, build_ext - - -def read_file(fname): - with open(path.join(path.dirname(__file__), fname), encoding='utf8') as f: - return f.read() - - -def package_files(directory): - paths = [] - for (fpath, directories, filenames) in walk(directory): - for filename in filenames: - paths.append(path.join('..', fpath, filename)) - return paths - - -# store log from build_libs and display at the end in verbose mode -# see https://github.com/pypa/pip/issues/6634 -log_stream = StringIO() -handler = logging.StreamHandler(log_stream) -log = logging.getLogger("cocotb._build_libs") -log.setLevel(logging.INFO) -log.addHandler(handler) - -setup( - name='cocotb', - cmdclass={'build_ext': build_ext}, - use_scm_version=dict( - write_to='cocotb/_version.py', - write_to_template='__version__ = {version!r}', - version_scheme='release-branch-semver' - ), - description='cocotb is a coroutine based cosimulation library for writing VHDL and Verilog testbenches in Python.', - url='https://docs.cocotb.org', - license='BSD', - long_description=read_file('README.md'), - long_description_content_type='text/markdown', - author='Chris Higgs, Stuart Hodgson', - maintainer='cocotb contributors', - maintainer_email='cocotb@lists.librecores.org', - setup_requires=['setuptools_scm'], - install_requires=[], - python_requires='>=3.5', - packages=find_packages(), - package_data={ - 'cocotb': ( - package_files('cocotb/share/makefiles') + # noqa: W504 - package_files('cocotb/share/include') + # noqa: W504 - package_files('cocotb/share/def') + # noqa: W504 - package_files('cocotb/share/lib/verilator') - ) - }, - ext_modules=get_ext(), - entry_points={ - 'console_scripts': [ - 'cocotb-config=cocotb.config:main', - ] - }, - platforms='any', - classifiers=[ - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "License :: OSI Approved :: BSD License", - "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", - ], - - # these appear in the sidebar on PyPI - project_urls={ - "Bug Tracker": "https://github.com/cocotb/cocotb/issues", - "Source Code": "https://github.com/cocotb/cocotb", - "Documentation": "https://docs.cocotb.org", - }, -) - -print(log_stream.getvalue()) diff --git a/tests/designs/array_module/Makefile b/tests/designs/array_module/Makefile deleted file mode 100644 index a340df3d..00000000 --- a/tests/designs/array_module/Makefile +++ /dev/null @@ -1,53 +0,0 @@ -############################################################################### -# Copyright (c) 2016 Potential Ventures Ltd -# Copyright (c) 2016 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -TOPLEVEL_LANG ?= verilog - -TOPLEVEL := array_module - -PWD=$(shell pwd) - -COCOTB?=$(PWD)/../../.. - -ifeq ($(TOPLEVEL_LANG),verilog) - VERILOG_SOURCES = $(COCOTB)/tests/designs/array_module/array_module.sv -else ifeq ($(TOPLEVEL_LANG),vhdl) - ifneq ($(filter $(SIM),ius xcelium),) - SIM_ARGS += -v93 - endif -ifeq ($(shell echo $(SIM) | tr A-Z a-z),aldec) - VHDL_SOURCES = $(COCOTB)/tests/designs/array_module/array_module_pack.vhd $(COCOTB)/tests/designs/array_module/array_module_aldec.vhd -else - VHDL_SOURCES = $(COCOTB)/tests/designs/array_module/array_module_pack.vhd $(COCOTB)/tests/designs/array_module/array_module.vhd -endif -else - $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") -endif - -include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/tests/designs/array_module/array_module.sv b/tests/designs/array_module/array_module.sv deleted file mode 100644 index e222e89a..00000000 --- a/tests/designs/array_module/array_module.sv +++ /dev/null @@ -1,204 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2016 Potential Ventures Ltd -// Copyright (c) 2016 SolarFlare Communications Inc -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of Potential Ventures Ltd, -// Copyright (c) 2013 SolarFlare Communications Inc nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -//----------------------------------------------------------------------------- - -`timescale 1 ps / 1 ps - -typedef struct { - logic a; - logic [7:0] b[0:2]; -} rec_type; - -module array_module ( - input clk, - - input integer select_in, - - input [7:0] port_desc_in, - input [0:7] port_asc_in, - input [1:8] port_ofst_in, - - output [7:0] port_desc_out, - output [0:7] port_asc_out, - output [1:8] port_ofst_out, - - output logic port_logic_out, - output logic [7:0] port_logic_vec_out, - //output bit port_bool_out, - //output integer port_int_out, - //output real port_real_out, - //output byte port_char_out, - //output string port_str_out, - output rec_type port_rec_out, - output rec_type port_cmplx_out[0:1] -); - -parameter logic param_logic = 1'b1; -parameter logic [7:0] param_logic_vec = 8'hDA; -//parameter bit param_bool = 1'b1; -//parameter integer param_int = 6; -//parameter real param_real = 3.14; -//parameter byte param_char = "p"; -//parameter string param_str = "ARRAYMOD"; -//parameter rec_type param_rec = '{a:'0, b:'{8'h00,8'h00,8'h00}}}; -//parameter rec_type param_cmplx [0:1] = '{'{a:'0, b:'{8'h00,8'h00,8'h00}}, '{a:'0, b:'{8'h00,8'h00,8'h00}}}; - -localparam logic const_logic = 1'b0; -localparam logic [7:0] const_logic_vec = 8'h3D; -//localparam bit const_bool = 1'b0; -//localparam integer const_int = 12; -//localparam real const_real = 6.28; -//localparam byte const_char = "c"; -//localparam string const_str = "MODARRAY"; -//localparam rec_type const_rec = '{a:'1, b:'{8'hFF,8'hFF,8'hFF}}}; -//localparam rec_type const_cmplx [1:2] = '{'{a:'1, b:'{8'hFF,8'hFF,8'hFF}}, '{a:'1, b:'{8'hFF,8'hFF,8'hFF}}}; - -wire [0:3] sig_t1; -wire [7:0] sig_t2[7:4]; -wire [7:0] sig_t3a[1:4]; -wire [7:0] sig_t3b[3:0]; -wire [7:0] sig_t4[0:3][7:4]; -wire [7:0] sig_t5[0:2][0:3]; -wire [7:0] sig_t6[0:1][2:4]; - -wire [16:23] sig_asc; -wire [23:16] sig_desc; -wire logic sig_logic; -wire logic [7:0] sig_logic_vec; -// bit sig_bool; -// integer sig_int; -// real sig_real; -// byte sig_char; -// string sig_str; - rec_type sig_rec; - rec_type sig_cmplx [0:1]; - -typedef logic [7:0] uint16_t; - -uint16_t sig_t7 [3:0][3:0]; -uint16_t [3:0][3:0] sig_t8; - -assign port_ofst_out = port_ofst_in; - -//assign port_rec_out = (select_in == 1) ? const_rec : (select_in == 2) ? sig_rec : param_rec; -//assign port_cmplx_out = (select_in == 1) ? const_cmplx : (select_in == 2) ? sig_cmplx : param_cmplx; - -always @(posedge clk) begin - if (select_in == 1) begin - port_logic_out = const_logic; - port_logic_vec_out = const_logic_vec; -// port_bool_out = const_bool; -// port_int_out = const_int; -// port_real_out = const_real; -// port_char_out = const_char; -// port_str_out = const_str; - port_rec_out.a = sig_rec.a; - port_rec_out.b[0] = sig_rec.b[0]; - port_rec_out.b[1] = sig_rec.b[1]; - port_rec_out.b[2] = sig_rec.b[2]; - port_cmplx_out[0].a = sig_cmplx[0].a; - port_cmplx_out[0].b[0] = sig_cmplx[0].b[0]; - port_cmplx_out[0].b[1] = sig_cmplx[0].b[1]; - port_cmplx_out[0].b[2] = sig_cmplx[0].b[2]; - port_cmplx_out[1].a = sig_cmplx[1].a; - port_cmplx_out[1].b[0] = sig_cmplx[1].b[0]; - port_cmplx_out[1].b[1] = sig_cmplx[1].b[1]; - port_cmplx_out[1].b[2] = sig_cmplx[1].b[2]; - end else begin - if (select_in == 2) begin - port_logic_out = sig_logic; - port_logic_vec_out = sig_logic_vec; -// port_bool_out = sig_bool; -// port_int_out = sig_int; -// port_real_out = sig_real; -// port_char_out = sig_char; -// port_str_out = sig_str; - port_rec_out.a = sig_rec.a; - port_rec_out.b[0] = sig_rec.b[0]; - port_rec_out.b[1] = sig_rec.b[1]; - port_rec_out.b[2] = sig_rec.b[2]; - port_cmplx_out[0].a = sig_cmplx[0].a; - port_cmplx_out[0].b[0] = sig_cmplx[0].b[0]; - port_cmplx_out[0].b[1] = sig_cmplx[0].b[1]; - port_cmplx_out[0].b[2] = sig_cmplx[0].b[2]; - port_cmplx_out[1].a = sig_cmplx[1].a; - port_cmplx_out[1].b[0] = sig_cmplx[1].b[0]; - port_cmplx_out[1].b[1] = sig_cmplx[1].b[1]; - port_cmplx_out[1].b[2] = sig_cmplx[1].b[2]; - end else begin - port_logic_out = param_logic; - port_logic_vec_out = param_logic_vec; -// port_bool_out = param_bool; -// port_int_out = param_int; -// port_real_out = param_real; -// port_char_out = param_char; -// port_str_out = param_str; - port_rec_out.a = sig_rec.a; - port_rec_out.b[0] = sig_rec.b[0]; - port_rec_out.b[1] = sig_rec.b[1]; - port_rec_out.b[2] = sig_rec.b[2]; - port_cmplx_out[0].a = sig_cmplx[0].a; - port_cmplx_out[0].b[0] = sig_cmplx[0].b[0]; - port_cmplx_out[0].b[1] = sig_cmplx[0].b[1]; - port_cmplx_out[0].b[2] = sig_cmplx[0].b[2]; - port_cmplx_out[1].a = sig_cmplx[1].a; - port_cmplx_out[1].b[0] = sig_cmplx[1].b[0]; - port_cmplx_out[1].b[1] = sig_cmplx[1].b[1]; - port_cmplx_out[1].b[2] = sig_cmplx[1].b[2]; - end - end -end - -genvar idx1; -generate -for (idx1 = 16; idx1 <= 23; idx1=idx1+1) begin:asc_gen - localparam OFFSET = 16-0; - reg sig; - always @(posedge clk) begin - sig <= port_asc_in[idx1-OFFSET]; - end - assign sig_asc[idx1] = sig; - assign port_asc_out[idx1-OFFSET] = sig_asc[idx1]; -end -endgenerate - -genvar idx2; -generate -for (idx2 = 7; idx2 >= 0; idx2=idx2-1) begin:desc_gen - localparam OFFSET = 23-7; - reg sig; - always @(posedge clk) begin - sig <= port_desc_in[idx2]; - end - assign sig_desc[idx2+OFFSET] = sig; - assign port_desc_out[idx2] = sig_desc[idx2+OFFSET]; -end -endgenerate - -endmodule - diff --git a/tests/designs/array_module/array_module.vhd b/tests/designs/array_module/array_module.vhd deleted file mode 100644 index 115ff918..00000000 --- a/tests/designs/array_module/array_module.vhd +++ /dev/null @@ -1,166 +0,0 @@ -------------------------------------------------------------------------------- --- Copyright (c) 2016 Potential Ventures Ltd --- All rights reserved. --- --- Redistribution and use in source and binary forms, with or without --- modification, are permitted provided that the following conditions are met: --- * Redistributions of source code must retain the above copyright --- notice, this list of conditions and the following disclaimer. --- * Redistributions in binary form must reproduce the above copyright --- notice, this list of conditions and the following disclaimer in the --- documentation and/or other materials provided with the distribution. --- * Neither the name of Potential Ventures Ltd, --- Copyright (c) 2013 SolarFlare Communications Inc nor the --- names of its contributors may be used to endorse or promote products --- derived from this software without specific prior written permission. --- --- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND --- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED --- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE --- DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY --- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES --- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; --- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND --- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT --- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS --- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- - -library ieee; - -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library work; - -use work.array_module_pack.all; - -entity array_module is - generic ( - param_logic : std_logic := '1'; - param_logic_vec : std_logic_vector(7 downto 0) := X"DA"; - param_bool : boolean := TRUE; - param_int : integer := 6; - param_real : real := 3.14; - param_char : character := 'p'; - param_str : string(1 to 8) := "ARRAYMOD"; - param_rec : rec_type := REC_TYPE_ZERO; - param_cmplx : rec_array(0 to 1) := (others=>REC_TYPE_ZERO) - ); - port ( - clk : in std_logic; - - select_in : in integer; - - port_desc_in : in std_logic_vector(7 downto 0); - port_asc_in : in std_logic_vector(0 to 7); - port_ofst_in : in std_logic_vector(1 to 8); - - port_desc_out : out std_logic_vector(7 downto 0); - port_asc_out : out std_logic_vector(0 to 7); - port_ofst_out : out std_logic_vector(1 to 8); - - port_logic_out : out std_logic; - port_logic_vec_out : out std_logic_vector(7 downto 0); - port_bool_out : out boolean; - port_int_out : out integer; - port_real_out : out real; - port_char_out : out character; - port_str_out : out string(1 to 8); - port_rec_out : out rec_type; - port_cmplx_out : out rec_array(0 to 1) - ); -end; - -architecture impl of array_module is - constant const_logic : std_logic := '0'; - constant const_logic_vec : std_logic_vector(7 downto 0) := X"3D"; - constant const_bool : boolean := FALSE; - constant const_int : integer := 12; - constant const_real : real := 6.28; - constant const_char : character := 'c'; - constant const_str : string(1 to 8) := "MODARRAY"; - constant const_rec : rec_type := REC_TYPE_ONE; - constant const_cmplx : rec_array(1 to 2) := (others=>REC_TYPE_ONE); - - signal sig_desc : std_logic_vector(23 downto 16); - signal sig_asc : std_logic_vector(16 to 23); - - signal \ext_id\ : std_logic; - signal \!\ : std_logic; - - signal sig_t1 : t1; - signal sig_t2 : t2; - signal sig_t3a : t3(1 to 4); - signal sig_t3b : t3(3 downto 0); - signal sig_t4 : t4; - signal sig_t5 : t5; - signal sig_t6 : t6(0 to 1, 2 to 4); - - signal sig_logic : std_logic; - signal sig_logic_vec : std_logic_vector(7 downto 0); - signal sig_bool : boolean; - signal sig_int : integer; - signal sig_real : real; - signal sig_char : character; - signal sig_str : string(1 to 8); - signal sig_rec : rec_type; - signal sig_cmplx : rec_array(0 to 1); -begin - port_ofst_out <= port_ofst_in; - - sig_proc : process (clk) - begin - if (rising_edge(clk)) then - if (select_in = 1) then - port_logic_out <= const_logic; - port_logic_vec_out <= const_logic_vec; - port_bool_out <= const_bool; - port_int_out <= const_int; - port_real_out <= const_real; - port_char_out <= const_char; - port_str_out <= const_str; - port_rec_out <= const_rec; - port_cmplx_out <= const_cmplx; - elsif (select_in = 2) then - port_logic_out <= sig_logic; - port_logic_vec_out <= sig_logic_vec; - port_bool_out <= sig_bool; - port_int_out <= sig_int; - port_real_out <= sig_real; - port_char_out <= sig_char; - port_str_out <= sig_str; - port_rec_out <= sig_rec; - port_cmplx_out <= sig_cmplx; - else - port_logic_out <= param_logic; - port_logic_vec_out <= param_logic_vec; - port_bool_out <= param_bool; - port_int_out <= param_int; - port_real_out <= param_real; - port_char_out <= param_char; - port_str_out <= param_str; - port_rec_out <= param_rec; - port_cmplx_out <= param_cmplx; - end if; - end if; - end process sig_proc; - - asc_gen : for idx1 in sig_asc'range generate - constant OFFSET : natural := sig_asc'left - port_asc_in'left; - signal sig : std_logic; - begin - sig <= port_asc_in(idx1-OFFSET) when rising_edge(clk); - sig_asc(idx1) <= sig; - port_asc_out(idx1-OFFSET) <= sig_asc(idx1); - end generate asc_gen; - - desc_gen : for idx2 in port_desc_in'range generate - constant OFFSET : natural := sig_desc'left - port_desc_in'left; - signal sig : std_logic; - begin - sig <= port_desc_in(idx2) when rising_edge(clk); - sig_desc(idx2+OFFSET) <= sig; - port_desc_out(idx2) <= sig_desc(idx2+OFFSET); - end generate desc_gen; -end architecture; diff --git a/tests/designs/array_module/array_module_aldec.vhd b/tests/designs/array_module/array_module_aldec.vhd deleted file mode 100644 index b87ffeda..00000000 --- a/tests/designs/array_module/array_module_aldec.vhd +++ /dev/null @@ -1,170 +0,0 @@ -------------------------------------------------------------------------------- --- Copyright (c) 2016 Potential Ventures Ltd --- All rights reserved. --- --- Redistribution and use in source and binary forms, with or without --- modification, are permitted provided that the following conditions are met: --- * Redistributions of source code must retain the above copyright --- notice, this list of conditions and the following disclaimer. --- * Redistributions in binary form must reproduce the above copyright --- notice, this list of conditions and the following disclaimer in the --- documentation and/or other materials provided with the distribution. --- * Neither the name of Potential Ventures Ltd, --- Copyright (c) 2013 SolarFlare Communications Inc nor the --- names of its contributors may be used to endorse or promote products --- derived from this software without specific prior written permission. --- --- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND --- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED --- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE --- DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY --- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES --- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; --- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND --- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT --- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS --- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- - -library ieee; - -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library work; - -use work.array_module_pack.all; - -entity array_module is - generic ( - param_logic : std_logic := '1'; - param_logic_vec : std_logic_vector(7 downto 0) := X"DA"; - param_bool : boolean := TRUE; - param_int : integer := 6; - param_real : real := 3.14; - param_char : character := 'p'; - param_str : string(1 to 8) := "ARRAYMOD" --; - --param_rec : rec_type := REC_TYPE_ZERO; - --param_cmplx : rec_array(0 to 1) := (others=>REC_TYPE_ZERO) - ); - port ( - clk : in std_logic; - - select_in : in integer; - - port_desc_in : in std_logic_vector(7 downto 0); - port_asc_in : in std_logic_vector(0 to 7); - port_ofst_in : in std_logic_vector(1 to 8); - - port_desc_out : out std_logic_vector(7 downto 0); - port_asc_out : out std_logic_vector(0 to 7); - port_ofst_out : out std_logic_vector(1 to 8); - - port_logic_out : out std_logic; - port_logic_vec_out : out std_logic_vector(7 downto 0); - port_bool_out : out boolean; - port_int_out : out integer; - port_real_out : out real; - port_char_out : out character; - port_str_out : out string(1 to 8); - port_rec_out : out rec_type; - port_cmplx_out : out rec_array(0 to 1) - ); -end; - -architecture impl of array_module is - constant const_logic : std_logic := '0'; - constant const_logic_vec : std_logic_vector(7 downto 0) := X"3D"; - constant const_bool : boolean := FALSE; - constant const_int : integer := 12; - constant const_real : real := 6.28; - constant const_char : character := 'c'; - constant const_str : string(1 to 8) := "MODARRAY"; - --constant const_rec : rec_type := REC_TYPE_ONE; - --constant const_cmplx : rec_array(1 to 2) := (others=>REC_TYPE_ONE); - - signal sig_desc : std_logic_vector(23 downto 16); - signal sig_asc : std_logic_vector(16 to 23); - - signal \ext_id\ : std_logic; - signal \!\ : std_logic; - - signal sig_t1 : t1; - signal sig_t2 : t2; - signal sig_t3a : t3(1 to 4); - signal sig_t3b : t3(3 downto 0); - signal sig_t4 : t4; - signal sig_t5 : t5; - signal sig_t6 : t6(0 to 1, 2 to 4); - - signal sig_logic : std_logic; - signal sig_logic_vec : std_logic_vector(7 downto 0); - signal sig_bool : boolean; - signal sig_int : integer; - signal sig_real : real; - signal sig_char : character; - signal sig_str : string(1 to 8); - signal sig_rec : rec_type; - signal sig_cmplx : rec_array(0 to 1); -begin - port_ofst_out <= port_ofst_in; - - sig_proc : process (clk) - begin - if (rising_edge(clk)) then - if (select_in = 1) then - port_logic_out <= const_logic; - port_logic_vec_out <= const_logic_vec; - port_bool_out <= const_bool; - port_int_out <= const_int; - port_real_out <= const_real; - port_char_out <= const_char; - port_str_out <= const_str; - port_rec_out <= sig_rec; - port_cmplx_out <= sig_cmplx; - --port_rec_out <= const_rec; - --port_cmplx_out <= const_cmplx; - elsif (select_in = 2) then - port_logic_out <= sig_logic; - port_logic_vec_out <= sig_logic_vec; - port_bool_out <= sig_bool; - port_int_out <= sig_int; - port_real_out <= sig_real; - port_char_out <= sig_char; - port_str_out <= sig_str; - port_rec_out <= sig_rec; - port_cmplx_out <= sig_cmplx; - else - port_logic_out <= param_logic; - port_logic_vec_out <= param_logic_vec; - port_bool_out <= param_bool; - port_int_out <= param_int; - port_real_out <= param_real; - port_char_out <= param_char; - port_str_out <= param_str; - port_rec_out <= sig_rec; - port_cmplx_out <= sig_cmplx; - --port_rec_out <= param_rec; - --port_cmplx_out <= param_cmplx; - end if; - end if; - end process sig_proc; - - asc_gen : for idx1 in sig_asc'range generate - constant OFFSET : natural := sig_asc'left - port_asc_in'left; - signal sig : std_logic; - begin - sig <= port_asc_in(idx1-OFFSET) when rising_edge(clk); - sig_asc(idx1) <= sig; - port_asc_out(idx1-OFFSET) <= sig_asc(idx1); - end generate asc_gen; - - desc_gen : for idx2 in port_desc_in'range generate - constant OFFSET : natural := sig_desc'left - port_desc_in'left; - signal sig : std_logic; - begin - sig <= port_desc_in(idx2) when rising_edge(clk); - sig_desc(idx2+OFFSET) <= sig; - port_desc_out(idx2) <= sig_desc(idx2+OFFSET); - end generate desc_gen; -end architecture; diff --git a/tests/designs/array_module/array_module_pack.vhd b/tests/designs/array_module/array_module_pack.vhd deleted file mode 100644 index 48dd0cd5..00000000 --- a/tests/designs/array_module/array_module_pack.vhd +++ /dev/null @@ -1,54 +0,0 @@ -------------------------------------------------------------------------------- --- Copyright (c) 2016 Potential Ventures Ltd --- All rights reserved. --- --- Redistribution and use in source and binary forms, with or without --- modification, are permitted provided that the following conditions are met: --- * Redistributions of source code must retain the above copyright --- notice, this list of conditions and the following disclaimer. --- * Redistributions in binary form must reproduce the above copyright --- notice, this list of conditions and the following disclaimer in the --- documentation and/or other materials provided with the distribution. --- * Neither the name of Potential Ventures Ltd, --- Copyright (c) 2013 SolarFlare Communications Inc nor the --- names of its contributors may be used to endorse or promote products --- derived from this software without specific prior written permission. --- --- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND --- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED --- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE --- DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY --- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES --- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; --- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND --- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT --- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS --- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- - -library ieee; - -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -package array_module_pack is - type t1 is array (0 to 3) of std_logic; - type t2 is array (7 downto 4) of std_logic_vector(7 downto 0); - type t3 is array (natural range <>) of std_logic_vector(7 downto 0); - type t4 is array (0 to 3) of t2; - type t5 is array (0 to 2, 0 to 3) of std_logic_vector(7 downto 0); - type t6 is array (natural range <>, natural range <>) of std_logic_vector(7 downto 0); - - type rec_type is record - a : std_logic; - b : t3(0 to 2); - end record rec_type; - type rec_array is array (natural range <>) of rec_type; - constant REC_TYPE_ZERO : rec_type := ('0', (others=>(others=>'0'))); - constant REC_TYPE_ONE : rec_type := ('1', (others=>(others=>'1'))); - -end package array_module_pack; - -package body array_module_pack is -end package body array_module_pack; - diff --git a/tests/designs/basic_hierarchy_module/Makefile b/tests/designs/basic_hierarchy_module/Makefile deleted file mode 100644 index a34df552..00000000 --- a/tests/designs/basic_hierarchy_module/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause - -TOPLEVEL_LANG ?= verilog - -ifneq ($(TOPLEVEL_LANG),verilog) - -all: - @echo "Skipping test due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being verilog" -clean:: - -else - -TOPLEVEL := basic_hierarchy_module - -PWD=$(shell pwd) - -COCOTB?=$(PWD)/../../.. - -VERILOG_SOURCES = $(COCOTB)/tests/designs/basic_hierarchy_module/basic_hierarchy_module.v - -include $(shell cocotb-config --makefiles)/Makefile.sim - -endif diff --git a/tests/designs/basic_hierarchy_module/basic_hierarchy_module.v b/tests/designs/basic_hierarchy_module/basic_hierarchy_module.v deleted file mode 100644 index 0b762abd..00000000 --- a/tests/designs/basic_hierarchy_module/basic_hierarchy_module.v +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright cocotb contributors -// Licensed under the Revised BSD License, see LICENSE for details. -// SPDX-License-Identifier: BSD-3-Clause - -`timescale 1 ps / 1 ps - -module module_a ( - input clk, - input [31:0] data_in, - output reg [31:0] data_out); - -always @ (posedge clk) -begin - data_out <= data_in + 2; -end - -endmodule - -module module_b ( - input clk, - input [31:0] data_in, - output reg [31:0] data_out -); - -always @ (posedge clk) -begin - data_out <= data_in + 5; -end - -endmodule - -module basic_hierarchy_module ( - input clk, - input reset -); - -reg [31:0] counter; - -always @ (posedge clk or negedge reset) -begin - if (~reset) begin - counter <= 0; - end else begin - counter <= counter + 1; - end -end - -wire [31:0] counter_plus_two; -wire [31:0] counter_plus_five; - -module_a i_module_a ( - .clk (clk), - .data_in (counter), - .data_out (counter_plus_two) -); - -module_b i_module_b ( - .clk (clk), - .data_in (counter), - .data_out (counter_plus_five) -); - -endmodule diff --git a/tests/designs/multi_dimension_array/Makefile b/tests/designs/multi_dimension_array/Makefile deleted file mode 100644 index 9efbd459..00000000 --- a/tests/designs/multi_dimension_array/Makefile +++ /dev/null @@ -1,51 +0,0 @@ -############################################################################### -# Copyright (c) 2016 Potential Ventures Ltd -# Copyright (c) 2016 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -TOPLEVEL_LANG ?= verilog - -TOPLEVEL := cocotb_array - -ifneq ($(TOPLEVEL_LANG),verilog) - -all: - @echo "Skipping test due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being verilog" -clean:: - -else - -PWD=$(shell pwd) - -COCOTB?=$(PWD)/../../.. - -VERILOG_SOURCES = $(COCOTB)/tests/designs/multi_dimension_array/cocotb_array_pkg.sv \ - $(COCOTB)/tests/designs/multi_dimension_array/cocotb_array.sv - -include $(shell cocotb-config --makefiles)/Makefile.sim - -endif diff --git a/tests/designs/multi_dimension_array/cocotb_array.sv b/tests/designs/multi_dimension_array/cocotb_array.sv deleted file mode 100644 index f980a902..00000000 --- a/tests/designs/multi_dimension_array/cocotb_array.sv +++ /dev/null @@ -1,101 +0,0 @@ -import cocotb_array_pkg::*; - -module cocotb_array ( - - //INPUTS - - //Single dimensions - input logic [2:0] in_vect_packed , - input logic in_vect_unpacked[2:0] , - input test_array_entry_t in_arr , - - //2 dimensions - input logic [2:0][2:0] in_2d_vect_packed_packed , - input logic [2:0] in_2d_vect_packed_unpacked[2:0] , - input logic in_2d_vect_unpacked_unpacked[2:0][2:0] , - - input test_array_entry_t [2:0] in_arr_packed , - input test_array_entry_t in_arr_unpacked[2:0] , - input test_2d_array_t in_2d_arr , - - //3 dimensions - input logic [2:0][2:0][2:0] in_vect_packed_packed_packed , - input logic [2:0][2:0] in_vect_packed_packed_unpacked[2:0] , - input logic [2:0] in_vect_packed_unpacked_unpacked[2:0][2:0] , - input logic in_vect_unpacked_unpacked_unpacked[2:0][2:0][2:0] , - - input test_array_entry_t [2:0][2:0] in_arr_packed_packed , - input test_array_entry_t [2:0] in_arr_packed_unpacked[2:0] , - input test_array_entry_t in_arr_unpacked_unpacked[2:0][2:0] , - - input test_2d_array_t [2:0] in_2d_arr_packed , - input test_2d_array_t in_2d_arr_unpacked[2:0] , - - input test_3d_array_t in_3d_arr , - - - //OUTPUTS - //Single dimensions - output logic [2:0] out_vect_packed , - output logic out_vect_unpacked[2:0] , - output test_array_entry_t out_arr , - - //2 dimensions - output logic [2:0][2:0] out_2d_vect_packed_packed , - output logic [2:0] out_2d_vect_packed_unpacked[2:0] , - output logic out_2d_vect_unpacked_unpacked[2:0][2:0] , - - output test_array_entry_t [2:0] out_arr_packed , - output test_array_entry_t out_arr_unpacked[2:0] , - output test_2d_array_t out_2d_arr , - - //3 dimensions - output logic [2:0][2:0][2:0] out_vect_packed_packed_packed , - output logic [2:0][2:0] out_vect_packed_packed_unpacked[2:0] , - output logic [2:0] out_vect_packed_unpacked_unpacked[2:0][2:0] , - output logic out_vect_unpacked_unpacked_unpacked[2:0][2:0][2:0] , - - output test_array_entry_t [2:0][2:0] out_arr_packed_packed , - output test_array_entry_t [2:0] out_arr_packed_unpacked[2:0] , - output test_array_entry_t out_arr_unpacked_unpacked[2:0][2:0] , - - output test_2d_array_t [2:0] out_2d_arr_packed , - output test_2d_array_t out_2d_arr_unpacked[2:0] , - - output test_3d_array_t out_3d_arr - -); - -//Fairly simple passthrough of all the values... - -assign out_vect_packed = in_vect_packed ; -assign out_vect_unpacked = in_vect_unpacked ; -assign out_arr = in_arr ; - - -assign out_2d_vect_packed_packed = in_2d_vect_packed_packed ; -assign out_2d_vect_packed_unpacked = in_2d_vect_packed_unpacked ; -assign out_2d_vect_unpacked_unpacked = in_2d_vect_unpacked_unpacked ; - -assign out_arr_packed = in_arr_packed ; -assign out_arr_unpacked = in_arr_unpacked ; -assign out_2d_arr = in_2d_arr ; - - -assign out_vect_packed_packed_packed = in_vect_packed_packed_packed ; -assign out_vect_packed_packed_unpacked = in_vect_packed_packed_unpacked ; -assign out_vect_packed_unpacked_unpacked = in_vect_packed_unpacked_unpacked ; -assign out_vect_unpacked_unpacked_unpacked = in_vect_unpacked_unpacked_unpacked ; - -assign out_arr_packed_packed = in_arr_packed_packed ; -assign out_arr_packed_unpacked = in_arr_packed_unpacked ; -assign out_arr_unpacked_unpacked = in_arr_unpacked_unpacked ; - -assign out_2d_arr_packed = in_2d_arr_packed ; -assign out_2d_arr_unpacked = in_2d_arr_unpacked ; - -assign out_3d_arr = in_3d_arr ; - - - -endmodule; diff --git a/tests/designs/multi_dimension_array/cocotb_array_pkg.sv b/tests/designs/multi_dimension_array/cocotb_array_pkg.sv deleted file mode 100644 index 65857026..00000000 --- a/tests/designs/multi_dimension_array/cocotb_array_pkg.sv +++ /dev/null @@ -1,6 +0,0 @@ -package cocotb_array_pkg; - - typedef logic [2:0] test_array_entry_t; - typedef test_array_entry_t [2:0] test_2d_array_t; - typedef test_2d_array_t [2:0] test_3d_array_t; -endpackage diff --git a/tests/designs/multi_dimension_array/cocotb_struct_pkg.sv b/tests/designs/multi_dimension_array/cocotb_struct_pkg.sv deleted file mode 100644 index a942b206..00000000 --- a/tests/designs/multi_dimension_array/cocotb_struct_pkg.sv +++ /dev/null @@ -1,35 +0,0 @@ -package cocotb_struct_pkg; - - typedef logic [2:0] test_array_entry_t; - typedef test_array_entry_t [2:0] test_2d_array_t; - typedef test_2d_array_t [2:0] test_3d_array_t; - - typedef struct packed { - logic [2:0] vect_packed; - logic [2:0][2:0] vect_packed_packed; - test_array_entry_t array_packed; - test_array_entry_t [2:0] array_packed_packed; - } struct_packed_t; - - typedef struct_packed_t [2:0] struct_packed_arr_packed_t; - typedef struct_packed_t struct_packed_arr_unpacked_t [2:0]; - - typedef struct_packed_t [2:0][2:0] struct_packed_arr_packed_packed_t; - typedef struct_packed_t [2:0] struct_packed_arr_packed_unpacked_t [2:0]; - typedef struct_packed_t struct_packed_arr_unpacked_unpacked_t [2:0][2:0]; - - typedef struct unpacked { - logic [2:0] vect_packed; - logic vect_unpacked[2:0]; - - logic [2:0] vect_packed_unpacked[2:0]; - logic vect_unpacked_unpacked[2:0]; - - test_array_entry_t array_packed; - test_array_entry_t [2:0] array_packed_packed; - test_array_entry_t array_packed_unpacked[2:0]; - - test_array_entry_t [2:0] array_packed_packed_unpacked[2:0]; - } struct_unpacked_t; - -endpackage diff --git a/tests/designs/plusargs_module/Makefile b/tests/designs/plusargs_module/Makefile deleted file mode 100644 index 1d79946b..00000000 --- a/tests/designs/plusargs_module/Makefile +++ /dev/null @@ -1,50 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -TOPLEVEL_LANG ?= verilog - -TOPLEVEL := tb_top - -PWD=$(shell pwd) - -COCOTB?=$(PWD)/../../.. - -ifeq ($(TOPLEVEL_LANG),verilog) - VERILOG_SOURCES = $(COCOTB)/tests/designs/plusargs_module/tb_top.v -else ifeq ($(TOPLEVEL_LANG),vhdl) - VHDL_SOURCES = $(COCOTB)/tests/designs/plusargs_module/tb_top.vhd -else - $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") -endif - -ifneq ($(filter $(SIM),ius xcelium),) - SIM_ARGS += -v93 -endif - -include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/tests/designs/plusargs_module/tb_top.v b/tests/designs/plusargs_module/tb_top.v deleted file mode 100644 index 613c0252..00000000 --- a/tests/designs/plusargs_module/tb_top.v +++ /dev/null @@ -1,42 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2013 Potential Ventures Ltd -// Copyright (c) 2013 SolarFlare Communications Inc -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of Potential Ventures Ltd, -// Copyright (c) 2013 SolarFlare Communications Inc nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -//----------------------------------------------------------------------------- - -module tb_top ; - - reg [1000:0] foo_string; - integer result; - -initial begin - $display("SIM: Plusargs test"); - result = $value$plusargs("foo=%s", foo_string); - $display("SIM: Plusarg foo has value %0s", foo_string); - #1 $display("SIM: Test running"); -end - -endmodule //: tb_top diff --git a/tests/designs/plusargs_module/tb_top.vhd b/tests/designs/plusargs_module/tb_top.vhd deleted file mode 100644 index c1172b41..00000000 --- a/tests/designs/plusargs_module/tb_top.vhd +++ /dev/null @@ -1,17 +0,0 @@ -library ieee; - -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -entity tb_top is -end; - -architecture impl of tb_top is - signal dummy_sig : std_logic := '0'; -begin - process - begin - wait for 10 ns; - dummy_sig <= '1'; - end process; -end architecture; diff --git a/tests/designs/sample_module/Makefile b/tests/designs/sample_module/Makefile deleted file mode 100644 index 275400ed..00000000 --- a/tests/designs/sample_module/Makefile +++ /dev/null @@ -1,50 +0,0 @@ -############################################################################### -# Copyright (c) 2013, 2018 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -TOPLEVEL_LANG ?= verilog - -TOPLEVEL := sample_module - -PWD=$(shell pwd) - -COCOTB?=$(PWD)/../../.. - -ifeq ($(TOPLEVEL_LANG),verilog) - VERILOG_SOURCES = $(COCOTB)/tests/designs/sample_module/sample_module.sv -else ifeq ($(TOPLEVEL_LANG),vhdl) - VHDL_SOURCES = $(COCOTB)/tests/designs/sample_module/sample_module_pack.vhdl $(COCOTB)/tests/designs/sample_module/sample_module_1.vhdl $(COCOTB)/tests/designs/sample_module/sample_module.vhdl - - ifneq ($(filter $(SIM),ius xcelium),) - SIM_ARGS += -v93 - endif -else - $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") -endif - -include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/tests/designs/sample_module/sample_module.sv b/tests/designs/sample_module/sample_module.sv deleted file mode 100644 index b57bb05e..00000000 --- a/tests/designs/sample_module/sample_module.sv +++ /dev/null @@ -1,170 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2013, 2018 Potential Ventures Ltd -// Copyright (c) 2013 SolarFlare Communications Inc -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of Potential Ventures Ltd, -// Copyright (c) 2013 SolarFlare Communications Inc nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -//----------------------------------------------------------------------------- - -`timescale 1 ps / 1 ps - -`ifndef __ICARUS__ -typedef struct packed -{ - logic a_in; - logic b_out; -} test_if; -`endif - -module sample_module #( - parameter INT_PARAM = 12, - parameter REAL_PARAM = 3.14, - parameter STRING_PARAM = "Test" -)( - input clk, - - output reg stream_in_ready, - input stream_in_valid, -`ifndef __ICARUS__ - input real stream_in_real, - input integer stream_in_int, - output real stream_out_real, - output integer stream_out_int, - input test_if inout_if, - input string stream_in_string, -`endif - input [7:0] stream_in_data, - input [31:0] stream_in_data_dword, - input [38:0] stream_in_data_39bit, - input [63:0] stream_in_data_wide, - input [127:0] stream_in_data_dqword, - - input stream_out_ready, - output reg [7:0] stream_out_data_comb, - output reg [7:0] stream_out_data_registered, - - output and_output - -); - -`ifndef __ICARUS__ -localparam string STRING_LOCALPARAM = "TESTING_LOCALPARAM"; - -var string STRING_VAR = "TESTING_VAR"; -const string STRING_CONST = "TESTING_CONST"; -`endif - -always @(posedge clk) - stream_out_data_registered <= stream_in_data; - -always @(stream_in_data) - stream_out_data_comb = stream_in_data; - -always @(stream_out_ready) - stream_in_ready = stream_out_ready; - -`ifndef __ICARUS__ -always @(stream_in_real) - stream_out_real = stream_in_real; - -always @(stream_in_int) - stream_out_int = stream_in_int; - -test_if struct_var; -`endif - -and test_and_gate(and_output, stream_in_ready, stream_in_valid); - -initial begin - $dumpfile("waveform.vcd"); - $dumpvars(0,sample_module); -end - -parameter NUM_OF_MODULES = 4; -reg[NUM_OF_MODULES-1:0] temp; -genvar idx; -generate - for (idx = 0; idx < NUM_OF_MODULES; idx=idx+1) begin - always @(posedge clk) begin - temp[idx] <= 1'b0; - end - end -endgenerate - -reg [7:0] register_array [1:0]; -always @(posedge clk) begin - // Ensure internal array is not optimized out - register_array[0] <= 0; -end - -//For testing arrays -reg [7:0] array_7_downto_4[7:4]; -reg [7:0] array_4_to_7[4:7]; -reg [7:0] array_3_downto_0[3:0]; -reg [7:0] array_0_to_3[0:3]; -reg [7:0] array_2d[0:1][31:28]; -always @(posedge stream_in_valid) begin - // Ensure internal array is not optimized out - array_7_downto_4[4] <= 0; - array_4_to_7[7] <= 0; - array_3_downto_0[0] <= 0; - array_0_to_3[3] <= 0; - array_2d[1][28] <= 0; -end - -//For testing type assigned to logic -logic logic_a, logic_b, logic_c; -assign logic_a = stream_in_valid; -always@* logic_b = stream_in_valid; -always@(posedge clk) logic_c <= stream_in_valid; - -reg _underscore_name; -`ifdef __ICARUS__ - // By default, a variable must be used in some way in order - // to be visible to VPI in Icarus Verilog. - // See https://github.com/steveicarus/iverilog/issues/322 - assign _underscore_name = 0; -`endif - -bit mybit; -bit [1:0] mybits; -bit [1:0] mybits_uninitialized; -initial begin - mybit = 1; - mybits = '1; -end - -`ifndef VERILATOR -always @(*) begin - $display("%m: mybit has been updated, new value is %b", mybit); -end -always @(*) begin - $display("%m: mybits has been updated, new value is %b", mybits); -end -always @(*) begin - $display("%m: mybits_uninitialized has been updated, new value is %b", mybits_uninitialized); -end -`endif - -endmodule diff --git a/tests/designs/sample_module/sample_module.vhdl b/tests/designs/sample_module/sample_module.vhdl deleted file mode 100644 index 48fe4db4..00000000 --- a/tests/designs/sample_module/sample_module.vhdl +++ /dev/null @@ -1,154 +0,0 @@ -------------------------------------------------------------------------------- --- Copyright (c) 2014 Potential Ventures Ltd --- All rights reserved. --- --- Redistribution and use in source and binary forms, with or without --- modification, are permitted provided that the following conditions are met: --- * Redistributions of source code must retain the above copyright --- notice, this list of conditions and the following disclaimer. --- * Redistributions in binary form must reproduce the above copyright --- notice, this list of conditions and the following disclaimer in the --- documentation and/or other materials provided with the distribution. --- * Neither the name of Potential Ventures Ltd, --- Copyright (c) 2013 SolarFlare Communications Inc nor the --- names of its contributors may be used to endorse or promote products --- derived from this software without specific prior written permission. --- --- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND --- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED --- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE --- DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY --- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES --- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; --- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND --- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT --- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS --- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- - - - - -library ieee; - -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library work; -use work.sample_module_pack.all; - -entity sample_module is - port ( - clk : in std_ulogic; - - stream_in_data : in std_ulogic_vector(7 downto 0); - stream_in_data_dword : in std_ulogic_vector(31 downto 0); - stream_in_data_39bit : in std_ulogic_vector(38 downto 0); - stream_in_data_wide : in std_ulogic_vector(63 downto 0); - stream_in_data_dqword : in std_ulogic_vector(127 downto 0); - stream_in_valid : in std_ulogic; - stream_in_func_en : in std_ulogic; - stream_in_ready : out std_ulogic; - stream_in_real : in real; - stream_in_int : in integer; - stream_in_string : in string(1 to 8); - stream_in_bool : in boolean; - - inout_if : in test_if; - - stream_out_data_comb : out std_ulogic_vector(7 downto 0); - stream_out_data_registered : out std_ulogic_vector(7 downto 0); - stream_out_data_wide : out std_ulogic_vector(63 downto 0); - stream_out_ready : in std_ulogic; - stream_out_real : out real; - stream_out_int : out integer; - stream_out_string : out string(1 to 8); - stream_out_bool : out boolean - ); -end; - -architecture impl of sample_module is - - component sample_module_1 is - generic ( - EXAMPLE_STRING : string; - EXAMPLE_BOOL : boolean; - EXAMPLE_WIDTH : integer - ); - port ( - clk : in std_ulogic; - stream_in_data : in std_ulogic_vector(EXAMPLE_WIDTH downto 0); - stream_out_data_registered : buffer std_ulogic_vector(EXAMPLE_WIDTH downto 0); - stream_out_data_valid : out std_ulogic - ); - end component sample_module_1; - - type lutType is array (0 to 3, 0 to 6) of signed(10 downto 0); - - function afunc(value : std_ulogic_vector) return std_ulogic_vector is - variable i: integer; - variable rv: std_ulogic_vector(7 downto 0); - begin - i := 0; - while i <= 7 loop - rv(i) := value(7-i); - i := i + 1; - end loop; - return rv; - end afunc; - - signal cosLut0, sinLut0 : lutType; - signal cosLut1, sinLut1 : lutType; - signal cosLut, sinLut : lutType; - - type unsignedArrayType is array (natural range <>) of unsigned(7 downto 0); - signal array_7_downto_4 : unsignedArrayType(7 downto 4); - signal array_4_to_7 : unsignedArrayType(4 to 7); - signal array_3_downto_0 : unsignedArrayType(3 downto 0); - signal array_0_to_3 : unsignedArrayType(0 to 3); - - type twoDimArrayType is array (natural range <>) of unsignedArrayType(31 downto 28); - signal array_2d : twoDimArrayType(0 to 1); - - constant NUM_OF_MODULES : natural := 4; - signal temp : std_logic_vector(NUM_OF_MODULES-1 downto 0); - -begin - - genblk1: for i in NUM_OF_MODULES - 1 downto 0 generate - begin - process (clk) begin - if rising_edge(clk) then - temp(i) <= '0'; - end if; - end process; - end generate; - - process (clk) begin - if rising_edge(clk) then - stream_out_data_registered <= stream_in_data; - end if; - end process; - - stream_out_data_comb <= afunc(stream_in_data) when stream_in_func_en = '0' else stream_in_data; - stream_in_ready <= stream_out_ready; - stream_out_real <= stream_in_real; - stream_out_int <= stream_in_int; - stream_out_string <= stream_in_string; - stream_out_bool <= stream_in_bool; - stream_out_data_wide(3 downto 2) <= stream_in_data_wide(3 downto 2); - - isample_module1 : component sample_module_1 - generic map ( - EXAMPLE_STRING => "TESTING", - EXAMPLE_BOOL => true, - EXAMPLE_WIDTH => 7 - ) - port map ( - clk => clk, - stream_in_data => stream_in_data, - stream_out_data_registered => open, - stream_out_data_valid => open - ); - -end architecture; diff --git a/tests/designs/sample_module/sample_module_1.vhdl b/tests/designs/sample_module/sample_module_1.vhdl deleted file mode 100644 index d6221b44..00000000 --- a/tests/designs/sample_module/sample_module_1.vhdl +++ /dev/null @@ -1,36 +0,0 @@ -library ieee; - -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -entity sample_module_1 is - generic ( - EXAMPLE_STRING : string; - EXAMPLE_BOOL : boolean; - EXAMPLE_WIDTH : integer - ); - port ( - clk : in std_ulogic; - stream_in_data : in std_ulogic_vector(EXAMPLE_WIDTH downto 0); - stream_out_data_registered : buffer std_ulogic_vector(EXAMPLE_WIDTH downto 0); - stream_out_data_valid : out std_ulogic - ); -end; - -architecture impl of sample_module_1 is -begin - process (clk) begin - if rising_edge(clk) then - stream_out_data_registered <= stream_in_data; - end if; - end process; - - stream_out_data_valid <= '1' when (stream_out_data_registered(EXAMPLE_WIDTH) = '1') else '0'; - - SAMPLE_BLOCK : block - signal clk_inv : std_ulogic; - begin - clk_inv <= not clk; - end block; - -end architecture; diff --git a/tests/designs/sample_module/sample_module_pack.vhdl b/tests/designs/sample_module/sample_module_pack.vhdl deleted file mode 100644 index d8ac952e..00000000 --- a/tests/designs/sample_module/sample_module_pack.vhdl +++ /dev/null @@ -1,45 +0,0 @@ -------------------------------------------------------------------------------- --- Copyright (c) 2014 Potential Ventures Ltd --- All rights reserved. --- --- Redistribution and use in source and binary forms, with or without --- modification, are permitted provided that the following conditions are met: --- * Redistributions of source code must retain the above copyright --- notice, this list of conditions and the following disclaimer. --- * Redistributions in binary form must reproduce the above copyright --- notice, this list of conditions and the following disclaimer in the --- documentation and/or other materials provided with the distribution. --- * Neither the name of Potential Ventures Ltd, --- Copyright (c) 2013 SolarFlare Communications Inc nor the --- names of its contributors may be used to endorse or promote products --- derived from this software without specific prior written permission. --- --- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND --- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED --- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE --- DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY --- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES --- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; --- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND --- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT --- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS --- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- - - -library ieee; - -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -package sample_module_pack is - - type test_if is record - a_in : std_logic; - b_out : std_logic; - end record test_if; - -end package sample_module_pack; - -package body sample_module_pack is -end package body sample_module_pack; diff --git a/tests/designs/uart2bus/Makefile b/tests/designs/uart2bus/Makefile deleted file mode 100644 index fc0d9606..00000000 --- a/tests/designs/uart2bus/Makefile +++ /dev/null @@ -1,41 +0,0 @@ -TOPLEVEL_LANG ?= verilog - -ifneq ($(TOPLEVEL_LANG),verilog) - -all: - @echo "Skipping test due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being verilog" -clean:: - -else - -PWD=$(shell pwd) - -COCOTB?=$(PWD)/../../.. - -SRC_BASE = $(COCOTB)/tests/designs/uart2bus - -VHDL_SOURCES = $(SRC_BASE)/vhdl/uart2BusTop_pkg.vhd \ - $(SRC_BASE)/vhdl/baudGen.vhd \ - $(SRC_BASE)/vhdl/uartParser.vhd \ - $(SRC_BASE)/vhdl/uartRx.vhd \ - $(SRC_BASE)/vhdl/uartTx.vhd \ - $(SRC_BASE)/vhdl/uartTop.vhd \ - $(SRC_BASE)/vhdl/uart2BusTop.vhd - -VERILOG_SOURCES = $(SRC_BASE)/verilog/baud_gen.v \ - $(SRC_BASE)/verilog/uart_parser.v \ - $(SRC_BASE)/verilog/uart_rx.v \ - $(SRC_BASE)/verilog/uart_tx.v \ - $(SRC_BASE)/verilog/uart_top.v \ - $(SRC_BASE)/verilog/uart2bus_top.v - -VERILOG_SOURCES += $(SRC_BASE)/top/verilog_toplevel.sv -TOPLEVEL = verilog_toplevel - -ifeq ($(SIM),$(filter $(SIM),ius xcelium)) - SIM_ARGS += -v93 -endif - -include $(shell cocotb-config --makefiles)/Makefile.sim - -endif diff --git a/tests/designs/uart2bus/README b/tests/designs/uart2bus/README deleted file mode 100644 index 6e813ea7..00000000 --- a/tests/designs/uart2bus/README +++ /dev/null @@ -1,7 +0,0 @@ -UART to bus design from [OpenCores](http://opencores.org/project,uart2bus) - -BSD Licensed. - -Contains a VHDL and Verilog implementation, suitable for testing mixed -language simulations. - diff --git a/tests/designs/uart2bus/top/verilog_toplevel.sv b/tests/designs/uart2bus/top/verilog_toplevel.sv deleted file mode 100644 index e10a0860..00000000 --- a/tests/designs/uart2bus/top/verilog_toplevel.sv +++ /dev/null @@ -1,58 +0,0 @@ -// We simply connect two UARTs together in different languages -// -// Also define a few different structs etc. to help the testcase - -module verilog_toplevel ( - input clk, - input reset -); - - -// Verilog design -logic serial_v2h, serial_h2v; -logic [7:0] verilog_rd_data, vhdl_rd_data; - -typedef struct packed { - logic [15:0] address; - logic [7:0] wr_data; - logic read; - logic write; -} bus_struct_t; - -bus_struct_t bus_verilog, bus_vhdl; - -uart2bus_top i_verilog ( - .clock (clk), - .reset (reset), - - .ser_in (serial_h2v), - .ser_out (serial_v2h), - - .int_address (bus_verilog.address), - .int_wr_data (bus_verilog.wr_data), - .int_write (bus_verilog.write), - .int_read (bus_verilog.read), - .int_rd_data (verilog_rd_data), - - .int_req (), - .int_gnt () -); - -uart2BusTop i_vhdl ( - .clk (clk), - .clr (reset), - - .serIn (serial_v2h), - .serOut (serial_h2v), - - .intAddress (bus_vhdl.address), - .intWrData (bus_vhdl.wr_data), - .intWrite (bus_vhdl.write), - .intRead (bus_vhdl.read), - .intRdData (vhdl_rd_data), - - .intAccessReq (), - .intAccessGnt () -); - -endmodule diff --git a/tests/designs/uart2bus/top/vhdl_toplevel.vhdl b/tests/designs/uart2bus/top/vhdl_toplevel.vhdl deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/designs/uart2bus/verilog/baud_gen.v b/tests/designs/uart2bus/verilog/baud_gen.v deleted file mode 100644 index 04857389..00000000 --- a/tests/designs/uart2bus/verilog/baud_gen.v +++ /dev/null @@ -1,56 +0,0 @@ -//--------------------------------------------------------------------------------------- -// baud rate generator for uart -// -// this module has been changed to receive the baud rate dividing counter from registers. -// the two registers should be calculated as follows: -// first register: -// baud_freq = 16*baud_rate / gcd(global_clock_freq, 16*baud_rate) -// second register: -// baud_limit = (global_clock_freq / gcd(global_clock_freq, 16*baud_rate)) - baud_freq -// -//--------------------------------------------------------------------------------------- - -module baud_gen -( - clock, reset, - ce_16, baud_freq, baud_limit -); -//--------------------------------------------------------------------------------------- -// modules inputs and outputs -input clock; // global clock input -input reset; // global reset input -output ce_16; // baud rate multiplyed by 16 -input [11:0] baud_freq; // baud rate setting registers - see header description -input [15:0] baud_limit; - -// internal registers -reg ce_16; -reg [15:0] counter; -//--------------------------------------------------------------------------------------- -// module implementation -// baud divider counter -always @ (posedge clock or posedge reset) -begin - if (reset) - counter <= 16'b0; - else if (counter >= baud_limit) - counter <= counter - baud_limit; - else - counter <= counter + baud_freq; -end - -// clock divider output -always @ (posedge clock or posedge reset) -begin - if (reset) - ce_16 <= 1'b0; - else if (counter >= baud_limit) - ce_16 <= 1'b1; - else - ce_16 <= 1'b0; -end - -endmodule -//--------------------------------------------------------------------------------------- -// Th.. Th.. Th.. Thats all folks !!! -//--------------------------------------------------------------------------------------- diff --git a/tests/designs/uart2bus/verilog/uart2bus_top.v b/tests/designs/uart2bus/verilog/uart2bus_top.v deleted file mode 100644 index 73189737..00000000 --- a/tests/designs/uart2bus/verilog/uart2bus_top.v +++ /dev/null @@ -1,83 +0,0 @@ -//--------------------------------------------------------------------------------------- -// uart to internal bus top module -// -//--------------------------------------------------------------------------------------- - -module uart2bus_top -( - // global signals - clock, reset, - // uart serial signals - ser_in, ser_out, - // internal bus to register file - int_address, int_wr_data, int_write, - int_rd_data, int_read, - int_req, int_gnt -); -//--------------------------------------------------------------------------------------- -// modules inputs and outputs -input clock; // global clock input -input reset; // global reset input -input ser_in; // serial data input -output ser_out; // serial data output -output [15:0] int_address; // address bus to register file -output [7:0] int_wr_data; // write data to register file -output int_write; // write control to register file -output int_read; // read control to register file -input [7:0] int_rd_data; // data read from register file -output int_req; // bus access request signal -input int_gnt; // bus access grant signal - -// baud rate configuration, see baud_gen.v for more details. -// baud rate generator parameters for 115200 baud on 40MHz clock -`define D_BAUD_FREQ 12'h90 -`define D_BAUD_LIMIT 16'h0ba5 -// baud rate generator parameters for 115200 baud on 44MHz clock -// `define D_BAUD_FREQ 12'd23 -// `define D_BAUD_LIMIT 16'd527 -// baud rate generator parameters for 9600 baud on 66MHz clock -//`define D_BAUD_FREQ 12'h10 -//`define D_BAUD_LIMIT 16'h1ACB - -// internal wires -wire [7:0] tx_data; // data byte to transmit -wire new_tx_data; // asserted to indicate that there is a new data byte for transmission -wire tx_busy; // signs that transmitter is busy -wire [7:0] rx_data; // data byte received -wire new_rx_data; // signs that a new byte was received -wire [11:0] baud_freq; -wire [15:0] baud_limit; -wire baud_clk; - -//--------------------------------------------------------------------------------------- -// module implementation -// uart top module instance -uart_top uart1 -( - .clock(clock), .reset(reset), - .ser_in(ser_in), .ser_out(ser_out), - .rx_data(rx_data), .new_rx_data(new_rx_data), - .tx_data(tx_data), .new_tx_data(new_tx_data), .tx_busy(tx_busy), - .baud_freq(baud_freq), .baud_limit(baud_limit), - .baud_clk(baud_clk) -); - -// assign baud rate default values -assign baud_freq = `D_BAUD_FREQ; -assign baud_limit = `D_BAUD_LIMIT; - -// uart parser instance -uart_parser #(16) uart_parser1 -( - .clock(clock), .reset(reset), - .rx_data(rx_data), .new_rx_data(new_rx_data), - .tx_data(tx_data), .new_tx_data(new_tx_data), .tx_busy(tx_busy), - .int_address(int_address), .int_wr_data(int_wr_data), .int_write(int_write), - .int_rd_data(int_rd_data), .int_read(int_read), - .int_req(int_req), .int_gnt(int_gnt) -); - -endmodule -//--------------------------------------------------------------------------------------- -// Th.. Th.. Th.. Thats all folks !!! -//--------------------------------------------------------------------------------------- diff --git a/tests/designs/uart2bus/verilog/uart_parser.v b/tests/designs/uart2bus/verilog/uart_parser.v deleted file mode 100644 index 4d0121bb..00000000 --- a/tests/designs/uart2bus/verilog/uart_parser.v +++ /dev/null @@ -1,667 +0,0 @@ -//--------------------------------------------------------------------------------------- -// uart parser module -// -//--------------------------------------------------------------------------------------- - -module uart_parser -( - // global signals - clock, reset, - // transmit and receive internal interface signals from uart interface - rx_data, new_rx_data, - tx_data, new_tx_data, tx_busy, - // internal bus to register file - int_address, int_wr_data, int_write, - int_rd_data, int_read, - int_req, int_gnt -); -//--------------------------------------------------------------------------------------- -// parameters -parameter AW = 8; // address bus width parameter - -// modules inputs and outputs -input clock; // global clock input -input reset; // global reset input -output [7:0] tx_data; // data byte to transmit -output new_tx_data; // asserted to indicate that there is a new data byte for - // transmission -input tx_busy; // signs that transmitter is busy -input [7:0] rx_data; // data byte received -input new_rx_data; // signs that a new byte was received -output [AW-1:0] int_address; // address bus to register file -output [7:0] int_wr_data; // write data to register file -output int_write; // write control to register file -output int_read; // read control to register file -input [7:0] int_rd_data; // data read from register file -output int_req; // bus access request signal -input int_gnt; // bus access grant signal - -// registered outputs -reg [7:0] tx_data; -reg new_tx_data; -reg [AW-1:0] int_address; -reg [7:0] int_wr_data; -reg write_req, read_req, int_write, int_read; - -// internal constants -// define characters used by the parser -`define CHAR_CR 8'h0d -`define CHAR_LF 8'h0a -`define CHAR_SPACE 8'h20 -`define CHAR_TAB 8'h09 -`define CHAR_COMMA 8'h2C -`define CHAR_R_UP 8'h52 -`define CHAR_r_LO 8'h72 -`define CHAR_W_UP 8'h57 -`define CHAR_w_LO 8'h77 -`define CHAR_0 8'h30 -`define CHAR_1 8'h31 -`define CHAR_2 8'h32 -`define CHAR_3 8'h33 -`define CHAR_4 8'h34 -`define CHAR_5 8'h35 -`define CHAR_6 8'h36 -`define CHAR_7 8'h37 -`define CHAR_8 8'h38 -`define CHAR_9 8'h39 -`define CHAR_A_UP 8'h41 -`define CHAR_B_UP 8'h42 -`define CHAR_C_UP 8'h43 -`define CHAR_D_UP 8'h44 -`define CHAR_E_UP 8'h45 -`define CHAR_F_UP 8'h46 -`define CHAR_a_LO 8'h61 -`define CHAR_b_LO 8'h62 -`define CHAR_c_LO 8'h63 -`define CHAR_d_LO 8'h64 -`define CHAR_e_LO 8'h65 -`define CHAR_f_LO 8'h66 - -// main (receive) state machine states -`define MAIN_IDLE 4'b0000 -`define MAIN_WHITE1 4'b0001 -`define MAIN_DATA 4'b0010 -`define MAIN_WHITE2 4'b0011 -`define MAIN_ADDR 4'b0100 -`define MAIN_EOL 4'b0101 -// binary mode extension states -`define MAIN_BIN_CMD 4'b1000 -`define MAIN_BIN_ADRH 4'b1001 -`define MAIN_BIN_ADRL 4'b1010 -`define MAIN_BIN_LEN 4'b1011 -`define MAIN_BIN_DATA 4'b1100 - -// transmit state machine -`define TX_IDLE 3'b000 -`define TX_HI_NIB 3'b001 -`define TX_LO_NIB 3'b100 -`define TX_CHAR_CR 3'b101 -`define TX_CHAR_LF 3'b110 - -// binary extension mode commands - the command is indicated by bits 5:4 of the command byte -`define BIN_CMD_NOP 2'b00 -`define BIN_CMD_READ 2'b01 -`define BIN_CMD_WRITE 2'b10 - -// internal wires and registers -reg [3:0] main_sm; // main state machine -reg read_op; // read operation flag -reg write_op; // write operation flag -reg data_in_hex_range; // indicates that the received data is in the range of hex number -reg [7:0] data_param; // operation data parameter -reg [15:0] addr_param; // operation address parameter -reg [3:0] data_nibble; // data nibble from received character -reg read_done; // internally generated read done flag -reg read_done_s; // sampled read done -reg [7:0] read_data_s; // sampled read data -reg [3:0] tx_nibble; // nibble value for transmission -reg [7:0] tx_char; // transmit byte from nibble to character conversion -reg [2:0] tx_sm; // transmit state machine -reg s_tx_busy; // sampled tx_busy for falling edge detection -reg bin_read_op; // binary mode read operation flag -reg bin_write_op; // binary mode write operation flag -reg addr_auto_inc; // address auto increment mode -reg send_stat_flag; // send status flag -reg [7:0] bin_byte_count; // binary mode byte counter -wire bin_last_byte; // last byte flag indicates that the current byte in the command is the last -wire tx_end_p; // transmission end pulse - -//--------------------------------------------------------------------------------------- -// module implementation -// main state machine -always @ (posedge clock or posedge reset) -begin - if (reset) - main_sm <= `MAIN_IDLE; - else if (new_rx_data) - begin - case (main_sm) - // wait for a read ('r') or write ('w') command - // binary extension - an all zeros byte enabled binary commands - `MAIN_IDLE: - // check received character - if (rx_data == 8'h0) - // an all zeros received byte enters binary mode - main_sm <= `MAIN_BIN_CMD; - else if ((rx_data == `CHAR_r_LO) | (rx_data == `CHAR_R_UP)) - // on read wait to receive only address field - main_sm <= `MAIN_WHITE2; - else if ((rx_data == `CHAR_w_LO) | (rx_data == `CHAR_W_UP)) - // on write wait to receive data and address - main_sm <= `MAIN_WHITE1; - else if ((rx_data == `CHAR_CR) | (rx_data == `CHAR_LF)) - // on new line sta in idle - main_sm <= `MAIN_IDLE; - else - // any other character wait to end of line (EOL) - main_sm <= `MAIN_EOL; - - // wait for white spaces till first data nibble - `MAIN_WHITE1: - // wait in this case until any white space character is received. in any - // valid character for data value switch to data state. a new line or carriage - // return should reset the state machine to idle. - // any other character transitions the state machine to wait for EOL. - if ((rx_data == `CHAR_SPACE) | (rx_data == `CHAR_TAB)) - main_sm <= `MAIN_WHITE1; - else if (data_in_hex_range) - main_sm <= `MAIN_DATA; - else if ((rx_data == `CHAR_CR) | (rx_data == `CHAR_LF)) - main_sm <= `MAIN_IDLE; - else - main_sm <= `MAIN_EOL; - - // receive data field - `MAIN_DATA: - // wait while data in hex range. white space transition to wait white 2 state. - // CR and LF resets the state machine. any other value cause state machine to - // wait til end of line. - if (data_in_hex_range) - main_sm <= `MAIN_DATA; - else if ((rx_data == `CHAR_SPACE) | (rx_data == `CHAR_TAB)) - main_sm <= `MAIN_WHITE2; - else if ((rx_data == `CHAR_CR) | (rx_data == `CHAR_LF)) - main_sm <= `MAIN_IDLE; - else - main_sm <= `MAIN_EOL; - - // wait for white spaces till first address nibble - `MAIN_WHITE2: - // similar to MAIN_WHITE1 - if ((rx_data == `CHAR_SPACE) | (rx_data == `CHAR_TAB)) - main_sm <= `MAIN_WHITE2; - else if (data_in_hex_range) - main_sm <= `MAIN_ADDR; - else if ((rx_data == `CHAR_CR) | (rx_data == `CHAR_LF)) - main_sm <= `MAIN_IDLE; - else - main_sm <= `MAIN_EOL; - - // receive address field - `MAIN_ADDR: - // similar to MAIN_DATA - if (data_in_hex_range) - main_sm <= `MAIN_ADDR; - else if ((rx_data == `CHAR_CR) | (rx_data == `CHAR_LF)) - main_sm <= `MAIN_IDLE; - else - main_sm <= `MAIN_EOL; - - // wait to EOL - `MAIN_EOL: - // wait for CR or LF to move back to idle - if ((rx_data == `CHAR_CR) | (rx_data == `CHAR_LF)) - main_sm <= `MAIN_IDLE; - - // binary extension - // wait for command - one byte - `MAIN_BIN_CMD: - // check if command is a NOP command - if (rx_data[5:4] == `BIN_CMD_NOP) - // if NOP command then switch back to idle state - main_sm <= `MAIN_IDLE; - else - // not a NOP command, continue receiving parameters - main_sm <= `MAIN_BIN_ADRH; - - // wait for address parameter - two bytes - // high address byte - `MAIN_BIN_ADRH: - // switch to next state - main_sm <= `MAIN_BIN_ADRL; - - // low address byte - `MAIN_BIN_ADRL: - // switch to next state - main_sm <= `MAIN_BIN_LEN; - - // wait for length parameter - one byte - `MAIN_BIN_LEN: - // check if write command else command reception ended - if (bin_write_op) - // wait for write data - main_sm <= `MAIN_BIN_DATA; - else - // command reception has ended - main_sm <= `MAIN_IDLE; - - // on write commands wait for data till end of buffer as specified by length parameter - `MAIN_BIN_DATA: - // if this is the last data byte then return to idle - if (bin_last_byte) - main_sm <= `MAIN_IDLE; - - // go to idle - default: - main_sm <= `MAIN_IDLE; - endcase - end -end - -// indicates that the received data is in the range of hex number -always @ (rx_data) -begin - if (((rx_data >= `CHAR_0 ) && (rx_data <= `CHAR_9 )) || - ((rx_data >= `CHAR_A_UP) && (rx_data <= `CHAR_F_UP)) || - ((rx_data >= `CHAR_a_LO) && (rx_data <= `CHAR_f_LO))) - data_in_hex_range <= 1'b1; - else - data_in_hex_range <= 1'b0; -end - -// read operation flag -always @ (posedge clock or posedge reset) -begin - if (reset) - read_op <= 1'b0; - else if ((main_sm == `MAIN_IDLE) && new_rx_data) - begin - // the read operation flag is set when a read command is received in idle state and cleared - // if any other character is received during that state. - if ((rx_data == `CHAR_r_LO) | (rx_data == `CHAR_R_UP)) - read_op <= 1'b1; - else - read_op <= 1'b0; - end -end - -// write operation flag -always @ (posedge clock or posedge reset) -begin - if (reset) - write_op <= 1'b0; - else if ((main_sm == `MAIN_IDLE) & new_rx_data) - begin - // the write operation flag is set when a write command is received in idle state and cleared - // if any other character is received during that state. - if ((rx_data == `CHAR_w_LO) | (rx_data == `CHAR_W_UP)) - write_op <= 1'b1; - else - write_op <= 1'b0; - end -end - -// binary mode read operation flag -always @ (posedge clock or posedge reset) -begin - if (reset) - bin_read_op <= 1'b0; - else if ((main_sm == `MAIN_BIN_CMD) && new_rx_data && (rx_data[5:4] == `BIN_CMD_READ)) - // read command is started on reception of a read command - bin_read_op <= 1'b1; - else if (bin_read_op && tx_end_p && bin_last_byte) - // read command ends on transmission of the last byte read - bin_read_op <= 1'b0; -end - -// binary mode write operation flag -always @ (posedge clock or posedge reset) -begin - if (reset) - bin_write_op <= 1'b0; - else if ((main_sm == `MAIN_BIN_CMD) && new_rx_data && (rx_data[5:4] == `BIN_CMD_WRITE)) - // write command is started on reception of a write command - bin_write_op <= 1'b1; - else if ((main_sm == `MAIN_BIN_DATA) && new_rx_data && bin_last_byte) - bin_write_op <= 1'b0; -end - -// send status flag - used only in binary extension mode -always @ (posedge clock or posedge reset) -begin - if (reset) - send_stat_flag <= 1'b0; - else if ((main_sm == `MAIN_BIN_CMD) && new_rx_data) - begin - // check if a status byte should be sent at the end of the command - if (rx_data[0] == 1'b1) - send_stat_flag <= 1'b1; - else - send_stat_flag <= 1'b0; - end -end - -// address auto increment - used only in binary extension mode -always @ (posedge clock or posedge reset) -begin - if (reset) - addr_auto_inc <= 1'b0; - else if ((main_sm == `MAIN_BIN_CMD) && new_rx_data) - begin - // check if address should be automatically incremented or not. - // Note that when rx_data[1] is set, address auto increment is disabled. - if (rx_data[1] == 1'b0) - addr_auto_inc <= 1'b1; - else - addr_auto_inc <= 1'b0; - end -end - -// operation data parameter -always @ (posedge clock or posedge reset) -begin - if (reset) - data_param <= 8'h0; - else if ((main_sm == `MAIN_WHITE1) & new_rx_data & data_in_hex_range) - data_param <= {4'h0, data_nibble}; - else if ((main_sm == `MAIN_DATA) & new_rx_data & data_in_hex_range) - data_param <= {data_param[3:0], data_nibble}; -end - -// operation address parameter -always @ (posedge clock or posedge reset) -begin - if (reset) - addr_param <= 0; - else if ((main_sm == `MAIN_WHITE2) & new_rx_data & data_in_hex_range) - addr_param <= {12'b0, data_nibble}; - else if ((main_sm == `MAIN_ADDR) & new_rx_data & data_in_hex_range) - addr_param <= {addr_param[11:0], data_nibble}; - // binary extension - else if (main_sm == `MAIN_BIN_ADRH) - addr_param[15:8] <= rx_data; - else if (main_sm == `MAIN_BIN_ADRL) - addr_param[7:0] <= rx_data; -end - -// binary mode command byte counter is loaded with the length parameter and counts down to zero. -// NOTE: a value of zero for the length parameter indicates a command of 256 bytes. -always @ (posedge clock or posedge reset) -begin - if (reset) - bin_byte_count <= 8'b0; - else if ((main_sm == `MAIN_BIN_LEN) && new_rx_data) - bin_byte_count <= rx_data; - else if ((bin_write_op && (main_sm == `MAIN_BIN_DATA) && new_rx_data) || - (bin_read_op && tx_end_p)) - // byte counter is updated on every new data received in write operations and for every - // byte transmitted for read operations. - bin_byte_count <= bin_byte_count - 1; -end -// last byte in command flag -assign bin_last_byte = (bin_byte_count == 8'h01) ? 1'b1 : 1'b0; - -// internal write control and data -always @ (posedge clock or posedge reset) -begin - if (reset) - begin - write_req <= 1'b0; - int_write <= 1'b0; - int_wr_data <= 0; - end - else if (write_op && (main_sm == `MAIN_ADDR) && new_rx_data && !data_in_hex_range) - begin - write_req <= 1'b1; - int_wr_data <= data_param; - end - // binary extension mode - else if (bin_write_op && (main_sm == `MAIN_BIN_DATA) && new_rx_data) - begin - write_req <= 1'b1; - int_wr_data <= rx_data; - end - else if (int_gnt && write_req) - begin - // set internal bus write and clear the write request flag - int_write <= 1'b1; - write_req <= 1'b0; - end - else - int_write <= 1'b0; -end - -// internal read control -always @ (posedge clock or posedge reset) -begin - if (reset) - begin - int_read <= 1'b0; - read_req <= 1'b0; - end - else if (read_op && (main_sm == `MAIN_ADDR) && new_rx_data && !data_in_hex_range) - read_req <= 1'b1; - // binary extension - else if (bin_read_op && (main_sm == `MAIN_BIN_LEN) && new_rx_data) - // the first read request is issued on reception of the length byte - read_req <= 1'b1; - else if (bin_read_op && tx_end_p && !bin_last_byte) - // the next read requests are issued after the previous read value was transmitted and - // this is not the last byte to be read. - read_req <= 1'b1; - else if (int_gnt && read_req) - begin - // set internal bus read and clear the read request flag - int_read <= 1'b1; - read_req <= 1'b0; - end - else - int_read <= 1'b0; -end - -// external request signal is active on read or write request -assign int_req = write_req | read_req; - -// internal address -always @ (posedge clock or posedge reset) -begin - if (reset) - int_address <= 0; - else if ((main_sm == `MAIN_ADDR) && new_rx_data && !data_in_hex_range) - int_address <= addr_param[AW-1:0]; - // binary extension - else if ((main_sm == `MAIN_BIN_LEN) && new_rx_data) - // sample address parameter on reception of length byte - int_address <= addr_param[AW-1:0]; - else if (addr_auto_inc && - ((bin_read_op && tx_end_p && !bin_last_byte) || - (bin_write_op && int_write))) - // address is incremented on every read or write if enabled - int_address <= int_address + 1; -end - -// read done flag and sampled data read -always @ (posedge clock or posedge reset) -begin - if (reset) begin - read_done <= 1'b0; - read_done_s <= 1'b0; - read_data_s <= 8'h0; - end - else - begin - // read done flag - if (int_read) - read_done <= 1'b1; - else - read_done <= 1'b0; - - // sampled read done - read_done_s <= read_done; - - // sampled data read - if (read_done) - read_data_s <= int_rd_data; - end -end - -// transmit state machine and control -always @ (posedge clock or posedge reset) -begin - if (reset) begin - tx_sm <= `TX_IDLE; - tx_data <= 8'h0; - new_tx_data <= 1'b0; - end - else - case (tx_sm) - // wait for read done indication - `TX_IDLE: - // on end of every read operation check how the data read should be transmitted - // according to read type: ascii or binary. - if (read_done_s) - // on binary mode read transmit byte value - if (bin_read_op) - begin - // note that there is no need to change state - tx_data <= read_data_s; - new_tx_data <= 1'b1; - end - else - begin - tx_sm <= `TX_HI_NIB; - tx_data <= tx_char; - new_tx_data <= 1'b1; - end - // check if status byte should be transmitted - else if ((send_stat_flag && bin_read_op && tx_end_p && bin_last_byte) || // end of read command - (send_stat_flag && bin_write_op && new_rx_data && bin_last_byte) || // end of write command - ((main_sm == `MAIN_BIN_CMD) && new_rx_data && (rx_data[5:4] == `BIN_CMD_NOP))) // NOP - begin - // send status byte - currently a constant - tx_data <= 8'h5a; - new_tx_data <= 1'b1; - end - else - new_tx_data <= 1'b0; - - // wait for transmit to end - `TX_HI_NIB: - if (tx_end_p) - begin - tx_sm <= `TX_LO_NIB; - tx_data <= tx_char; - new_tx_data <= 1'b1; - end - else - new_tx_data <= 1'b0; - - // wait for transmit to end - `TX_LO_NIB: - if (tx_end_p) - begin - tx_sm <= `TX_CHAR_CR; - tx_data <= `CHAR_CR; - new_tx_data <= 1'b1; - end - else - new_tx_data <= 1'b0; - - // wait for transmit to end - `TX_CHAR_CR: - if (tx_end_p) - begin - tx_sm <= `TX_CHAR_LF; - tx_data <= `CHAR_LF; - new_tx_data <= 1'b1; - end - else - new_tx_data <= 1'b0; - - // wait for transmit to end - `TX_CHAR_LF: - begin - if (tx_end_p) - tx_sm <= `TX_IDLE; - // clear tx new data flag - new_tx_data <= 1'b0; - end - - // return to idle - default: - tx_sm <= `TX_IDLE; - endcase -end - -// select the nibble to the nibble to character conversion -always @ (tx_sm or read_data_s) -begin - case (tx_sm) - `TX_IDLE: tx_nibble = read_data_s[7:4]; - `TX_HI_NIB: tx_nibble = read_data_s[3:0]; - default: tx_nibble = read_data_s[7:4]; - endcase -end - -// sampled tx_busy -always @ (posedge clock or posedge reset) -begin - if (reset) - s_tx_busy <= 1'b0; - else - s_tx_busy <= tx_busy; -end -// tx end pulse -assign tx_end_p = ~tx_busy & s_tx_busy; - -// character to nibble conversion -always @ (rx_data) -begin - case (rx_data) - `CHAR_0: data_nibble = 4'h0; - `CHAR_1: data_nibble = 4'h1; - `CHAR_2: data_nibble = 4'h2; - `CHAR_3: data_nibble = 4'h3; - `CHAR_4: data_nibble = 4'h4; - `CHAR_5: data_nibble = 4'h5; - `CHAR_6: data_nibble = 4'h6; - `CHAR_7: data_nibble = 4'h7; - `CHAR_8: data_nibble = 4'h8; - `CHAR_9: data_nibble = 4'h9; - `CHAR_A_UP, `CHAR_a_LO: data_nibble = 4'ha; - `CHAR_B_UP, `CHAR_b_LO: data_nibble = 4'hb; - `CHAR_C_UP, `CHAR_c_LO: data_nibble = 4'hc; - `CHAR_D_UP, `CHAR_d_LO: data_nibble = 4'hd; - `CHAR_E_UP, `CHAR_e_LO: data_nibble = 4'he; - `CHAR_F_UP, `CHAR_f_LO: data_nibble = 4'hf; - default: data_nibble = 4'hf; - endcase -end - -// nibble to character conversion -always @ (tx_nibble) -begin - case (tx_nibble) - 4'h0: tx_char = `CHAR_0; - 4'h1: tx_char = `CHAR_1; - 4'h2: tx_char = `CHAR_2; - 4'h3: tx_char = `CHAR_3; - 4'h4: tx_char = `CHAR_4; - 4'h5: tx_char = `CHAR_5; - 4'h6: tx_char = `CHAR_6; - 4'h7: tx_char = `CHAR_7; - 4'h8: tx_char = `CHAR_8; - 4'h9: tx_char = `CHAR_9; - 4'ha: tx_char = `CHAR_A_UP; - 4'hb: tx_char = `CHAR_B_UP; - 4'hc: tx_char = `CHAR_C_UP; - 4'hd: tx_char = `CHAR_D_UP; - 4'he: tx_char = `CHAR_E_UP; - default: tx_char = `CHAR_F_UP; - endcase -end - -endmodule -//--------------------------------------------------------------------------------------- -// Th.. Th.. Th.. Thats all folks !!! -//--------------------------------------------------------------------------------------- diff --git a/tests/designs/uart2bus/verilog/uart_rx.v b/tests/designs/uart2bus/verilog/uart_rx.v deleted file mode 100644 index c6f4aca3..00000000 --- a/tests/designs/uart2bus/verilog/uart_rx.v +++ /dev/null @@ -1,116 +0,0 @@ -//--------------------------------------------------------------------------------------- -// uart receive module -// -//--------------------------------------------------------------------------------------- - -module uart_rx -( - clock, reset, - ce_16, ser_in, - rx_data, new_rx_data -); -//--------------------------------------------------------------------------------------- -// modules inputs and outputs -input clock; // global clock input -input reset; // global reset input -input ce_16; // baud rate multiplyed by 16 - generated by baud module -input ser_in; // serial data input -output [7:0] rx_data; // data byte received -output new_rx_data; // signs that a new byte was received - -// internal wires -wire ce_1; // clock enable at bit rate -wire ce_1_mid; // clock enable at the middle of each bit - used to sample data - -// internal registers -reg [7:0] rx_data; -reg new_rx_data; -reg [1:0] in_sync; -reg rx_busy; -reg [3:0] count16; -reg [3:0] bit_count; -reg [7:0] data_buf; -//--------------------------------------------------------------------------------------- -// module implementation -// input async input is sampled twice -always @ (posedge clock or posedge reset) -begin - if (reset) - in_sync <= 2'b11; - else - in_sync <= {in_sync[0], ser_in}; -end - -// a counter to count 16 pulses of ce_16 to generate the ce_1 and ce_1_mid pulses. -// this counter is used to detect the start bit while the receiver is not receiving and -// signs the sampling cycle during reception. -always @ (posedge clock or posedge reset) -begin - if (reset) - count16 <= 4'b0; - else if (ce_16) - begin - if (rx_busy | (in_sync[1] == 1'b0)) - count16 <= count16 + 4'b1; - else - count16 <= 4'b0; - end -end - -// ce_1 pulse indicating expected end of current bit -assign ce_1 = (count16 == 4'b1111) & ce_16; -// ce_1_mid pulse indication the sampling clock cycle of the current data bit -assign ce_1_mid = (count16 == 4'b0111) & ce_16; - -// receiving busy flag -always @ (posedge clock or posedge reset) -begin - if (reset) - rx_busy <= 1'b0; - else if (~rx_busy & ce_1_mid) - rx_busy <= 1'b1; - else if (rx_busy & (bit_count == 4'h8) & ce_1_mid) - rx_busy <= 1'b0; -end - -// bit counter -always @ (posedge clock or posedge reset) -begin - if (reset) - bit_count <= 4'h0; - else if (~rx_busy) - bit_count <= 4'h0; - else if (rx_busy & ce_1_mid) - bit_count <= bit_count + 4'h1; -end - -// data buffer shift register -always @ (posedge clock or posedge reset) -begin - if (reset) - data_buf <= 8'h0; - else if (rx_busy & ce_1_mid) - data_buf <= {in_sync[1], data_buf[7:1]}; -end - -// data output and flag -always @ (posedge clock or posedge reset) -begin - if (reset) - begin - rx_data <= 8'h0; - new_rx_data <= 1'b0; - end - else if (rx_busy & (bit_count == 4'h8) & ce_1) - begin - rx_data <= data_buf; - new_rx_data <= 1'b1; - end - else - new_rx_data <= 1'b0; -end - -endmodule -//--------------------------------------------------------------------------------------- -// Th.. Th.. Th.. Thats all folks !!! -//--------------------------------------------------------------------------------------- diff --git a/tests/designs/uart2bus/verilog/uart_top.v b/tests/designs/uart2bus/verilog/uart_top.v deleted file mode 100644 index 4cde3cbd..00000000 --- a/tests/designs/uart2bus/verilog/uart_top.v +++ /dev/null @@ -1,66 +0,0 @@ -//--------------------------------------------------------------------------------------- -// uart top level module -// -//--------------------------------------------------------------------------------------- - -module uart_top -( - // global signals - clock, reset, - // uart serial signals - ser_in, ser_out, - // transmit and receive internal interface signals - rx_data, new_rx_data, - tx_data, new_tx_data, tx_busy, - // baud rate configuration register - see baud_gen.v for details - baud_freq, baud_limit, - baud_clk -); -//--------------------------------------------------------------------------------------- -// modules inputs and outputs -input clock; // global clock input -input reset; // global reset input -input ser_in; // serial data input -output ser_out; // serial data output -input [7:0] tx_data; // data byte to transmit -input new_tx_data; // asserted to indicate that there is a new data byte for transmission -output tx_busy; // signs that transmitter is busy -output [7:0] rx_data; // data byte received -output new_rx_data; // signs that a new byte was received -input [11:0] baud_freq; // baud rate setting registers - see header description -input [15:0] baud_limit; -output baud_clk; - -// internal wires -wire ce_16; // clock enable at bit rate - -assign baud_clk = ce_16; -//--------------------------------------------------------------------------------------- -// module implementation -// baud rate generator module -baud_gen baud_gen_1 -( - .clock(clock), .reset(reset), - .ce_16(ce_16), .baud_freq(baud_freq), .baud_limit(baud_limit) -); - -// uart receiver -uart_rx uart_rx_1 -( - .clock(clock), .reset(reset), - .ce_16(ce_16), .ser_in(ser_in), - .rx_data(rx_data), .new_rx_data(new_rx_data) -); - -// uart transmitter -uart_tx uart_tx_1 -( - .clock(clock), .reset(reset), - .ce_16(ce_16), .tx_data(tx_data), .new_tx_data(new_tx_data), - .ser_out(ser_out), .tx_busy(tx_busy) -); - -endmodule -//--------------------------------------------------------------------------------------- -// Th.. Th.. Th.. Thats all folks !!! -//--------------------------------------------------------------------------------------- diff --git a/tests/designs/uart2bus/verilog/uart_tx.v b/tests/designs/uart2bus/verilog/uart_tx.v deleted file mode 100644 index 83113a74..00000000 --- a/tests/designs/uart2bus/verilog/uart_tx.v +++ /dev/null @@ -1,94 +0,0 @@ -//--------------------------------------------------------------------------------------- -// uart transmit module -// -//--------------------------------------------------------------------------------------- - -module uart_tx -( - clock, reset, - ce_16, tx_data, new_tx_data, - ser_out, tx_busy -); -//--------------------------------------------------------------------------------------- -// modules inputs and outputs -input clock; // global clock input -input reset; // global reset input -input ce_16; // baud rate multiplyed by 16 - generated by baud module -input [7:0] tx_data; // data byte to transmit -input new_tx_data; // asserted to indicate that there is a new data byte for transmission -output ser_out; // serial data output -output tx_busy; // signs that transmitter is busy - -// internal wires -wire ce_1; // clock enable at bit rate - -// internal registers -reg ser_out; -reg tx_busy; -reg [3:0] count16; -reg [3:0] bit_count; -reg [8:0] data_buf; -//--------------------------------------------------------------------------------------- -// module implementation -// a counter to count 16 pulses of ce_16 to generate the ce_1 pulse -always @ (posedge clock or posedge reset) -begin - if (reset) - count16 <= 4'b0; - else if (tx_busy & ce_16) - count16 <= count16 + 4'b1; - else if (~tx_busy) - count16 <= 4'b0; -end - -// ce_1 pulse indicating output data bit should be updated -assign ce_1 = (count16 == 4'b1111) & ce_16; - -// tx_busy flag -always @ (posedge clock or posedge reset) -begin - if (reset) - tx_busy <= 1'b0; - else if (~tx_busy & new_tx_data) - tx_busy <= 1'b1; - else if (tx_busy & (bit_count == 4'h9) & ce_1) - tx_busy <= 1'b0; -end - -// output bit counter -always @ (posedge clock or posedge reset) -begin - if (reset) - bit_count <= 4'h0; - else if (tx_busy & ce_1) - bit_count <= bit_count + 4'h1; - else if (~tx_busy) - bit_count <= 4'h0; -end - -// data shift register -always @ (posedge clock or posedge reset) -begin - if (reset) - data_buf <= 9'b0; - else if (~tx_busy) - data_buf <= {tx_data, 1'b0}; - else if (tx_busy & ce_1) - data_buf <= {1'b1, data_buf[8:1]}; -end - -// output data bit -always @ (posedge clock or posedge reset) -begin - if (reset) - ser_out <= 1'b1; - else if (tx_busy) - ser_out <= data_buf[0]; - else - ser_out <= 1'b1; -end - -endmodule -//--------------------------------------------------------------------------------------- -// Th.. Th.. Th.. Thats all folks !!! -//--------------------------------------------------------------------------------------- diff --git a/tests/designs/uart2bus/vhdl/baudGen.vhd b/tests/designs/uart2bus/vhdl/baudGen.vhd deleted file mode 100644 index 2eec8b9d..00000000 --- a/tests/designs/uart2bus/vhdl/baudGen.vhd +++ /dev/null @@ -1,48 +0,0 @@ ------------------------------------------------------------------------------------------ --- baud rate generator for uart --- --- this module has been changed to receive the baud rate dividing counter from registers. --- the two registers should be calculated as follows: --- first register: --- baud_freq = 16*baud_rate / gcd(global_clock_freq, 16*baud_rate) --- second register: --- baud_limit = (global_clock_freq / gcd(global_clock_freq, 16*baud_rate)) - baud_freq --- ------------------------------------------------------------------------------------------ -library ieee; -use ieee.std_logic_1164.all; -use ieee.std_logic_unsigned.all; - -entity baudGen is - port ( clr : in std_logic; -- global reset input - clk : in std_logic; -- global clock input - -- baudFreq = 16 * baudRate / gcd(clkFreq, 16 * baudRate) - baudFreq : in std_logic_vector(11 downto 0); -- baud rate setting registers - see header description - -- baudLimit = clkFreq / gcd(clkFreq, 16 * baudRate) - baudFreq - baudLimit : in std_logic_vector(15 downto 0); -- baud rate setting registers - see header description - ce16 : out std_logic); -- baud rate multiplyed by 16 -end baudGen; - -architecture Behavioral of baudGen is - - signal counter : std_logic_vector(15 downto 0); - - begin - -- baud divider counter - -- clock divider output - process (clr, clk) - begin - if (clr = '1') then - counter <= (others => '0'); - ce16 <= '0'; - elsif (rising_edge(clk)) then - if (counter >= baudLimit) then - counter <= counter - baudLimit; - ce16 <= '1'; - else - counter <= counter + baudFreq; - ce16 <= '0'; - end if; - end if; - end process; - end Behavioral; diff --git a/tests/designs/uart2bus/vhdl/uart2BusTop.vhd b/tests/designs/uart2bus/vhdl/uart2BusTop.vhd deleted file mode 100644 index c719656b..00000000 --- a/tests/designs/uart2bus/vhdl/uart2BusTop.vhd +++ /dev/null @@ -1,77 +0,0 @@ ------------------------------------------------------------------------------------------ --- uart to internal bus top module --- ------------------------------------------------------------------------------------------ -library ieee; -use ieee.std_logic_1164.all; - -library work; -use work.uart2BusTop_pkg.all; - -entity uart2BusTop is - generic ( AW : integer := 16); - port ( -- global signals - clr : in STD_LOGIC; -- global reset input - clk : in STD_LOGIC; -- global clock input - -- uart serial signals - serIn : in STD_LOGIC; -- serial data input - serOut : out STD_LOGIC; -- serial data output - -- internal bus to register file - intAccessReq : out std_logic; -- - intAccessGnt : in std_logic; -- - intRdData : in STD_LOGIC_VECTOR (7 downto 0); -- data read from register file - intAddress : out STD_LOGIC_VECTOR (AW - 1 downto 0); -- address bus to register file - intWrData : out STD_LOGIC_VECTOR (7 downto 0); -- write data to register file - intWrite : out STD_LOGIC; -- write control to register file - intRead : out STD_LOGIC); -- read control to register file -end uart2BusTop; - -architecture Behavioral of uart2BusTop is - - -- baud rate configuration, see baudGen.vhd for more details. - -- baud rate generator parameters for 115200 baud on 25MHz clock - constant baudFreq : std_logic_vector(11 downto 0) := x"480"; - constant baudLimit : std_logic_vector(15 downto 0) := x"3889"; - signal txData : std_logic_vector(7 downto 0); -- data byte to transmit - signal newTxData : std_logic; -- asserted to indicate that there is a new data byte for transmission - signal txBusy : std_logic; -- signs that transmitter is busy - signal rxData : std_logic_vector(7 downto 0); -- data byte received - signal newRxData : std_logic; -- signs that a new byte was received - - begin - -- uart top module instance - ut : uartTop - port map ( - clr => clr, - clk => clk, - serIn => serIn, - txData => txData, - newTxData => newTxData, - baudFreq => baudFreq, - baudLimit => baudLimit, - serOut => serOut, - txBusy => txBusy, - rxData => rxData, - newRxData => newRxData, - baudClk => open); - -- uart parser instance - up : uartParser - generic map ( - AW => AW) - port map ( - clr => clr, - clk => clk, - txBusy => txBusy, - rxData => rxData, - newRxData => newRxData, - intRdData => intRdData, - txData => txData, - newTxData => newTxData, - intReq => intAccessReq, - intGnt => intAccessGnt, - intAddress => intAddress, - intWrData => intWrData, - intWrite => intWrite, - intRead => intRead); - - end Behavioral; diff --git a/tests/designs/uart2bus/vhdl/uart2BusTop_pkg.vhd b/tests/designs/uart2bus/vhdl/uart2BusTop_pkg.vhd deleted file mode 100644 index 303ede98..00000000 --- a/tests/designs/uart2bus/vhdl/uart2BusTop_pkg.vhd +++ /dev/null @@ -1,90 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; - -package uart2BusTop_pkg is - - component baudGen - port ( - clr : in std_logic; - clk : in std_logic; - baudFreq : in std_logic_vector(11 downto 0); - baudLimit : in std_logic_vector(15 downto 0); - ce16 : out std_logic); - end component; - - component uartTx - port ( - clr : in std_logic; - clk : in std_logic; - ce16 : in std_logic; - txData : in std_logic_vector(7 downto 0); - newTxData : in std_logic; - serOut : out std_logic; - txBusy : out std_logic); - end component; - - component uartRx - port ( - clr : in std_logic; - clk : in std_logic; - ce16 : in std_logic; - serIn : in std_logic; - rxData : out std_logic_vector(7 downto 0); - newRxData : out std_logic); - end component; - - component uartTop - port ( clr : in std_logic; - clk : in std_logic; - serIn : in std_logic; - txData : in std_logic_vector(7 downto 0); - newTxData : in std_logic; - baudFreq : in std_logic_vector(11 downto 0); - baudLimit : in std_logic_vector(15 downto 0); - serOut : out std_logic; - txBusy : out std_logic; - rxData : out std_logic_vector(7 downto 0); - newRxData : out std_logic; - baudClk : out std_logic); - end component; - - component uartParser - generic ( AW : integer := 8); - port ( clr : in std_logic; - clk : in std_logic; - txBusy : in std_logic; - rxData : in std_logic_vector(7 downto 0); - newRxData : in std_logic; - intRdData : in std_logic_vector(7 downto 0); - txData : out std_logic_vector(7 downto 0); - newTxData : out std_logic; - intReq : out std_logic; - intGnt : in std_logic; - intAddress : out std_logic_vector(AW - 1 downto 0); - intWrData : out std_logic_vector(7 downto 0); - intWrite : out std_logic; - intRead : out std_logic); - end component; - - component uart2BusTop - generic - ( - AW : integer := 8 - ); - port - ( - clr : in std_logic; - clk : in std_logic; - serIn : in std_logic; - serOut : out std_logic; - intAccessReq : out std_logic; - intAccessGnt : in std_logic; - intRdData : in std_logic_vector(7 downto 0); - intAddress : out std_logic_vector(AW - 1 downto 0); - intWrData : out std_logic_vector(7 downto 0); - intWrite : out std_logic; - intRead : out std_logic - ); - end component; - -end uart2BusTop_pkg; diff --git a/tests/designs/uart2bus/vhdl/uartParser.vhd b/tests/designs/uart2bus/vhdl/uartParser.vhd deleted file mode 100644 index f64f6e52..00000000 --- a/tests/designs/uart2bus/vhdl/uartParser.vhd +++ /dev/null @@ -1,576 +0,0 @@ ------------------------------------------------------------------------------------------ --- uart parser module --- ------------------------------------------------------------------------------------------ -library ieee; -use ieee.std_logic_1164.ALL; -use ieee.std_logic_unsigned.ALL; - -entity uartParser is - generic ( -- parameters - AW : integer := 8); - port ( -- global signals - clr : in std_logic; -- global reset input - clk : in std_logic; -- global clock input - -- transmit and receive internal interface signals from uart interface - txBusy : in std_logic; -- signs that transmitter is busy - rxData : in std_logic_vector(7 downto 0); -- data byte received - newRxData : in std_logic; -- signs that a new byte was received - txData : out std_logic_vector(7 downto 0); -- data byte to transmit - newTxData : out std_logic; -- asserted to indicate that there is a new data byte for transmission - -- internal bus to register file - intReq : out std_logic; -- - intGnt : in std_logic; -- - intRdData : in std_logic_vector(7 downto 0); -- data read from register file - intAddress : out std_logic_vector(AW - 1 downto 0); -- address bus to register file - intWrData : out std_logic_vector(7 downto 0); -- write data to register file - intWrite : out std_logic; -- write control to register file - intRead : out std_logic); -- read control to register file -end uartParser; - -architecture Behavioral of uartParser is - - -- internal constants - -- main (receive) state machine states - signal mainSm : std_logic_vector(3 downto 0); -- main state machine - constant mainIdle : std_logic_vector(mainSm'range) := "0000"; - constant mainWhite1 : std_logic_vector(mainSm'range) := "0001"; - constant mainData : std_logic_vector(mainSm'range) := "0010"; - constant mainWhite2 : std_logic_vector(mainSm'range) := "0011"; - constant mainAddr : std_logic_vector(mainSm'range) := "0100"; - constant mainEol : std_logic_vector(mainSm'range) := "0101"; - -- binary mode extension states - constant mainBinCmd : std_logic_vector(mainSm'range) := "1000"; - constant mainBinAdrh : std_logic_vector(mainSm'range) := "1001"; - constant mainBinAdrl : std_logic_vector(mainSm'range) := "1010"; - constant mainBinLen : std_logic_vector(mainSm'range) := "1011"; - constant mainBinData : std_logic_vector(mainSm'range) := "1100"; - - -- transmit state machine - signal txSm : std_logic_vector(2 downto 0); -- transmit state machine - constant txIdle : std_logic_vector(txSm'range) := "000"; - constant txHiNib : std_logic_vector(txSm'range) := "001"; - constant txLoNib : std_logic_vector(txSm'range) := "100"; - constant txCharCR : std_logic_vector(txSm'range) := "101"; - constant txCharLF : std_logic_vector(txSm'range) := "110"; - - -- define characters used by the parser - constant charNul : std_logic_vector(7 downto 0) := x"00"; - constant charTab : std_logic_vector(7 downto 0) := x"09"; - constant charLF : std_logic_vector(7 downto 0) := x"0A"; - constant charCR : std_logic_vector(7 downto 0) := x"0D"; - constant charSpace : std_logic_vector(7 downto 0) := x"20"; - constant charZero : std_logic_vector(7 downto 0) := x"30"; - constant charOne : std_logic_vector(7 downto 0) := x"31"; - constant charTwo : std_logic_vector(7 downto 0) := x"32"; - constant charThree : std_logic_vector(7 downto 0) := x"33"; - constant charFour : std_logic_vector(7 downto 0) := x"34"; - constant charFive : std_logic_vector(7 downto 0) := x"35"; - constant charSix : std_logic_vector(7 downto 0) := x"36"; - constant charSeven : std_logic_vector(7 downto 0) := x"37"; - constant charEight : std_logic_vector(7 downto 0) := x"38"; - constant charNine : std_logic_vector(7 downto 0) := x"39"; - constant charAHigh : std_logic_vector(7 downto 0) := x"41"; - constant charBHigh : std_logic_vector(7 downto 0) := x"42"; - constant charCHigh : std_logic_vector(7 downto 0) := x"43"; - constant charDHigh : std_logic_vector(7 downto 0) := x"44"; - constant charEHigh : std_logic_vector(7 downto 0) := x"45"; - constant charFHigh : std_logic_vector(7 downto 0) := x"46"; - constant charRHigh : std_logic_vector(7 downto 0) := x"52"; - constant charWHigh : std_logic_vector(7 downto 0) := x"57"; - constant charALow : std_logic_vector(7 downto 0) := x"61"; - constant charBLow : std_logic_vector(7 downto 0) := x"62"; - constant charCLow : std_logic_vector(7 downto 0) := x"63"; - constant charDLow : std_logic_vector(7 downto 0) := x"64"; - constant charELow : std_logic_vector(7 downto 0) := x"65"; - constant charFLow : std_logic_vector(7 downto 0) := x"66"; - constant charRLow : std_logic_vector(7 downto 0) := x"72"; - constant charWLow : std_logic_vector(7 downto 0) := x"77"; - - -- binary extension mode commands - the command is indicated by bits 5:4 of the command byte - constant binCmdNop : std_logic_vector(1 downto 0) := "00"; - constant binCmdRead : std_logic_vector(1 downto 0) := "01"; - constant binCmdWrite : std_logic_vector(1 downto 0) := "10"; - - signal dataInHexRange : std_logic; -- indicates that the received data is in the range of hex number - signal binLastByte : std_logic; -- last byte flag indicates that the current byte in the command is the last - signal txEndP : std_logic; -- transmission end pulse - signal readOp : std_logic; -- read operation flag - signal writeOp : std_logic; -- write operation flag - signal binReadOp : std_logic; -- binary mode read operation flag - signal binWriteOp : std_logic; -- binary mode write operation flag - signal sendStatFlag : std_logic; -- send status flag - signal addrAutoInc : std_logic; -- address auto increment mode - signal dataParam : std_logic_vector(7 downto 0); -- operation data parameter - signal dataNibble : std_logic_vector(3 downto 0); -- data nibble from received character - signal addrParam : std_logic_vector(15 downto 0); -- operation address parameter - signal addrNibble : std_logic_vector(3 downto 0); -- data nibble from received character - signal binByteCount : std_logic_vector(7 downto 0); -- binary mode byte counter - signal iIntAddress : std_logic_vector(intAddress'range); -- - signal iWriteReq : std_logic; -- - signal iIntWrite : std_logic; -- - signal readDone : std_logic; -- internally generated read done flag - signal readDoneS : std_logic; -- sampled read done - signal readDataS : std_logic_vector(7 downto 0); -- sampled read data - signal iReadReq : std_logic; -- - signal iIntRead : std_logic; -- - signal txChar : std_logic_vector(7 downto 0); -- transmit byte from nibble to character conversion - signal sTxBusy : std_logic; -- sampled tx_busy for falling edge detection - signal txNibble : std_logic_vector(3 downto 0); -- nibble value for transmission - - -- module implementation - -- main state machine - begin - process (clr, clk) - begin - if (clr = '1') then - mainSm <= mainIdle; - elsif (rising_edge(clk)) then - if (newRxData = '1') then - case mainSm is - -- wait for a read ('r') or write ('w') command - -- binary extension - an all zeros byte enabled binary commands - when mainIdle => - -- check received character - if (rxData = charNul) then - -- an all zeros received byte enters binary mode - mainSm <= mainBinCmd; - elsif ((rxData = charRLow) or (rxData = charRHigh)) then - -- on read wait to receive only address field - mainSm <= mainWhite2; - elsif ((rxData = charWLow) or (rxData = charWHigh)) then - -- on write wait to receive data and address - mainSm <= mainWhite1; - elsif ((rxData = charCR) or (rxData = charLF)) then - -- on new line sta in idle - mainSm <= mainIdle; - else - -- any other character wait to end of line (EOL) - mainSm <= mainEol; - end if; - -- wait for white spaces till first data nibble - when mainWhite1 => - -- wait in this case until any white space character is received. in any - -- valid character for data value switch to data state. a new line or carriage - -- return should reset the state machine to idle. - -- any other character transitions the state machine to wait for EOL. - if ((rxData = charSpace) or (rxData = charTab)) then - mainSm <= mainWhite1; - elsif (dataInHexRange = '1') then - mainSm <= mainData; - elsif ((rxData = charCR) or (rxData = charLF)) then - mainSm <= mainIdle; - else - mainSm <= mainEol; - end if; - -- receive data field - when mainData => - -- wait while data in hex range. white space transition to wait white 2 state. - -- CR and LF resets the state machine. any other value cause state machine to - -- wait til end of line. - if (dataInHexRange = '1') then - mainSm <= mainData; - elsif ((rxData = charSpace) or (rxData = charTab)) then - mainSm <= mainWhite2; - elsif ((rxData = charCR) or (rxData = charLF)) then - mainSm <= mainIdle; - else - mainSm <= mainEol; - end if; - -- wait for white spaces till first address nibble - when mainWhite2 => - -- similar to MAIN_WHITE1 - if ((rxData = charSpace) or (rxData = charTab)) then - mainSm <= mainWhite2; - elsif (dataInHexRange = '1') then - mainSm <= mainAddr; - elsif ((rxData = charCR) or (rxData = charLF)) then - mainSm <= mainIdle; - else - mainSm <= mainEol; - end if; - -- receive address field - when mainAddr => - -- similar to MAIN_DATA - if (dataInHexRange = '1') then - mainSm <= mainAddr; - elsif ((rxData = charCR) or (rxData = charLF)) then - mainSm <= mainIdle; - else - mainSm <= mainEol; - end if; - -- wait to EOL - when mainEol => - -- wait for CR or LF to move back to idle - if ((rxData = charCR) or (rxData = charLF)) then - mainSm <= mainIdle; - end if; - -- binary extension - -- wait for command - one byte - when mainBinCmd => - -- check if command is a NOP command - if (rxData(5 downto 4) = binCmdNop) then - -- if NOP command then switch back to idle state - mainSm <= mainIdle; - else - -- not a NOP command, continue receiving parameters - mainSm <= mainBinAdrh; - end if; - -- wait for address parameter - two bytes - -- high address byte - when mainBinAdrh => - -- switch to next state - mainSm <= mainBinAdrl; - -- low address byte - when mainBinAdrl => - -- switch to next state - mainSm <= mainBinLen; - -- wait for length parameter - one byte - when mainBinLen => - -- check if write command else command reception ended - if (binWriteOp = '1') then - -- wait for write data - mainSm <= mainBinData; - else - -- command reception has ended - mainSm <= mainIdle; - end if; - -- on write commands wait for data till end of buffer as specified by length parameter - when mainBinData => - -- if this is the last data byte then return to idle - if (binLastByte = '1') then - mainSm <= mainIdle; - end if; - -- go to idle - when others => - mainSm <= mainIdle; - end case; - end if; - end if; - end process; - -- read operation flag - -- write operation flag - -- binary mode read operation flag - -- binary mode write operation flag - process (clr, clk) - begin - if (clr = '1') then - readOp <= '0'; - writeOp <= '0'; - binReadOp <= '0'; - binWriteOp <= '0'; - elsif (rising_edge(clk)) then - if ((mainSm = mainIdle) and (newRxData = '1')) then - -- the read operation flag is set when a read command is received in idle state and cleared - -- if any other character is received during that state. - if ((rxData = charRLow) or (rxData = charRHigh)) then - readOp <= '1'; - else - readOp <= '0'; - end if; - -- the write operation flag is set when a write command is received in idle state and cleared - -- if any other character is received during that state. - if ((rxData = charWLow) or (rxData = charWHigh)) then - writeOp <= '1'; - else - writeOp <= '0'; - end if; - end if; - if ((mainSm = mainBinCmd) and (newRxData = '1') and (rxData(5 downto 4) = binCmdRead)) then - -- read command is started on reception of a read command - binReadOp <= '1'; - elsif ((binReadOp = '1') and (txEndP = '1') and (binLastByte = '1')) then - -- read command ends on transmission of the last byte read - binReadOp <= '0'; - end if; - if ((mainSm = mainBinCmd) and (newRxData = '1') and (rxData(5 downto 4) = binCmdWrite)) then - -- write command is started on reception of a write command - binWriteOp <= '1'; - elsif ((mainSm = mainBinData) and (newRxData = '1') and (binLastByte = '1')) then - binWriteOp <= '0'; - end if; - end if; - end process; - -- send status flag - used only in binary extension mode - -- address auto increment - used only in binary extension mode - process (clr, clk) - begin - if (clr = '1') then - sendStatFlag <= '0'; - addrAutoInc <= '0'; - elsif (rising_edge(clk)) then - if ((mainSm = mainBinCmd) and (newRxData = '1')) then - -- check if a status byte should be sent at the end of the command - sendStatFlag <= rxData(0); - -- check if address should be automatically incremented or not. - -- Note that when rx_data[1] is set, address auto increment is disabled. - addrAutoInc <= not(rxData(1)); - end if; - end if; - end process; - -- operation data parameter - process (clr, clk) - begin - if (clr = '1') then - dataParam <= (others => '0'); - elsif (rising_edge(clk)) then - if ((mainSm = mainWhite1) and (newRxData = '1') and (dataInHexRange = '1')) then - dataParam <= "0000" & dataNibble; - elsif ((mainSm = mainData) and (newRxData = '1') and (dataInHexRange = '1')) then - dataParam <= dataParam(3 downto 0) & dataNibble; - end if; - end if; - end process; - -- operation address parameter - process (clr, clk) - begin - if (clr = '1') then - addrParam <= (others => '0'); - elsif (rising_edge(clk)) then - if ((mainSm = mainWhite2) and (newRxData = '1') and (dataInHexRange = '1')) then - addrParam <= x"000" & dataNibble; - elsif ((mainSm = mainAddr) and (newRxData = '1') and (dataInHexRange = '1')) then - addrParam <= addrParam(11 downto 0) & dataNibble; - -- binary extension - elsif (mainSm = mainBinAdrh) then - addrParam(15 downto 8) <= rxData; - elsif (mainSm = mainBinAdrl) then - addrParam(7 downto 0) <= rxData; - end if; - end if; - end process; - -- binary mode command byte counter is loaded with the length parameter and counts down to zero. - -- NOTE: a value of zero for the length parameter indicates a command of 256 bytes. - process (clr, clk) - begin - if (clr = '1') then - binByteCount <= (others => '0'); - elsif (rising_edge(clk)) then - if ((mainSm = mainBinLen) and (newRxData = '1')) then - binByteCount <= rxData; - elsif (((mainSm = mainBinData) and (binWriteOp = '1') and (newRxData = '1')) or ((binReadOp = '1') and (txEndP = '1'))) then - -- byte counter is updated on every new data received in write operations and for every - -- byte transmitted for read operations. - binByteCount <= binByteCount - 1; - end if; - end if; - end process; - -- internal write control and data - -- internal read control - process (clr, clk) - begin - if (clr = '1') then - iReadReq <= '0'; - iIntRead <= '0'; - iWriteReq <= '0'; - iIntWrite <= '0'; - intWrData <= (others => '0'); - elsif (rising_edge(clk)) then - if ((mainSm = mainAddr) and (writeOp = '1') and (newRxData = '1') and (dataInHexRange = '0')) then - iWriteReq <= '1'; - intWrData <= dataParam; - -- binary extension mode - elsif ((mainSm = mainBinData) and (binWriteOp = '1') and (newRxData = '1')) then - iWriteReq <= '1'; - intWrData <= rxData; - elsif ((intGnt = '1') and (iWriteReq = '1')) then - iWriteReq <= '0'; - iIntWrite <= '1'; - else - iIntWrite <= '0'; - end if; - if ((mainSm = mainAddr) and (readOp = '1') and (newRxData = '1') and (dataInHexRange = '0')) then - iReadReq <= '1'; - -- binary extension - elsif ((mainSm = mainBinLen) and (binReadOp = '1') and (newRxData = '1')) then - -- the first read request is issued on reception of the length byte - iReadReq <= '1'; - elsif ((binReadOp = '1') and (txEndP = '1') and (binLastByte = '0')) then - -- the next read requests are issued after the previous read value was transmitted and - -- this is not the last byte to be read. - iReadReq <= '1'; - elsif ((intGnt = '1') and (iReadReq = '1')) then - iReadReq <= '0'; - iIntRead <= '1'; - else - iIntRead <= '0'; - end if; - end if; - end process; - -- internal address - process (clr, clk) - begin - if (clr = '1') then - iIntAddress <= (others => '0'); - elsif (rising_edge(clk)) then - if ((mainSm = mainAddr) and (newRxData = '1') and (dataInHexRange = '0')) then - iIntAddress <= addrParam(AW - 1 downto 0); - -- binary extension - elsif ((mainSm = mainBinLen) and (newRxData = '1')) then - -- sample address parameter on reception of length byte - iIntAddress <= addrParam(AW - 1 downto 0); - elsif ((addrAutoInc = '1') and (((binReadOp = '1') and (txEndP = '1') and (binLastByte = '0')) or ((binWriteOp = '1') and (iIntWrite = '1')))) then - -- address is incremented on every read or write if enabled - iIntAddress <= iIntAddress + 1; - end if; - end if; - end process; - -- read done flag and sampled data read - process (clr, clk) - begin - if (clr = '1') then - readDone <= '0'; - readDoneS <= '0'; - readDataS <= (others => '0'); - elsif (rising_edge(clk)) then - -- read done flag - readDone <= iIntRead; - -- sampled read done - readDoneS <= readDone; - -- sampled data read - if (readDone = '1') then - readDataS <= intRdData; - end if; - end if; - end process; - -- transmit state machine and control - process (clr, clk) - begin - if (clr = '1') then - txSm <= txIdle; - txData <= (others => '0'); - newTxData <= '0'; - elsif (rising_edge(clk)) then - case txSm is - -- wait for read done indication - when txIdle => - -- on end of every read operation check how the data read should be transmitted - -- according to read type: ascii or binary. - if (readDoneS = '1') then - -- on binary mode read transmit byte value - if (binReadOp = '1') then - -- note that there is no need to change state - txData <= readDataS; - newTxData <= '1'; - else - txSm <= txHiNib; - txData <= txChar; - newTxData <= '1'; - end if; - -- check if status byte should be transmitted - elsif (((sendStatFlag = '1') and (binReadOp = '1') and (txEndP = '1') and (binLastByte = '1')) or ((sendStatFlag = '1') and (binWriteOp = '1') and (newRxData = '1') and (binLastByte = '1')) or ((mainSm = mainBinCmd) and (newRxData = '1') and (rxData(5 downto 4) = binCmdNop))) then - -- send status byte - currently a constant - txData <= x"5A"; - newTxData <= '1'; - else - newTxData <= '0'; - end if; - when txHiNib => - -- wait for transmit to end - if (txEndP = '1') then - txSm <= txLoNib; - txData <= txChar; - newTxData <= '1'; - else - newTxData <= '0'; - end if; - -- wait for transmit to end - when txLoNib => - if (txEndP = '1') then - txSm <= txCharCR; - txData <= charCR; - newTxData <= '1'; - else - newTxData <= '0'; - end if; - -- wait for transmit to end - when txCharCR => - if (txEndP = '1') then - txSm <= txCharLF; - txData <= charLF; - newTxData <= '1'; - else - newTxData <= '0'; - end if; - -- wait for transmit to end - when txCharLF => - if (txEndP = '1') then - txSm <= txIdle; - end if; - -- clear tx new data flag - newTxData <= '0'; - -- return to idle - when others => - txSm <= txIdle; - end case; - end if; - end process; - -- sampled tx_busy - process (clr, clk) - begin - if (clr = '1') then - sTxBusy <= '1'; - elsif (rising_edge(clk)) then - sTxBusy <= txBusy; - end if; - end process; - -- indicates that the received data is in the range of hex number - dataInHexRange <= '1' when (((rxData >= charZero) and (rxData <= charNine)) or - ((rxData >= charAHigh) and (rxData <= charFHigh)) or - ((rxData >= charALow) and (rxData <= charFLow))) else '0'; - -- last byte in command flag - binLastByte <= '1' when (binByteCount = x"01") else '0'; - -- select the nibble to the nibble to character conversion - txNibble <= readDataS(3 downto 0) when (txSm = txHiNib) else readDataS(7 downto 4); - -- tx end pulse - txEndP <= '1' when ((txBusy = '0') and (sTxBusy = '1')) else '0'; - -- character to nibble conversion - with rxData select - dataNibble <= x"0" when charZero, - x"1" when charOne, - x"2" when charTwo, - x"3" when charThree, - x"4" when charFour, - x"5" when charFive, - x"6" when charSix, - x"7" when charSeven, - x"8" when charEight, - x"9" when charNine, - x"A" when charALow, - x"A" when charAHigh, - x"B" when charBLow, - x"B" when charBHigh, - x"C" when charCLow, - x"C" when charCHigh, - x"D" when charDLow, - x"D" when charDHigh, - x"E" when charELow, - x"E" when charEHigh, - x"F" when charFLow, - x"F" when charFHigh, - x"F" when others; - -- nibble to character conversion - with txNibble select - txChar <= charZero when x"0", - charOne when x"1", - charTwo when x"2", - charThree when x"3", - charFour when x"4", - charFive when x"5", - charSix when x"6", - charSeven when x"7", - charEight when x"8", - charNine when x"9", - charAHigh when x"A", - charBHigh when x"B", - charCHigh when x"C", - charDHigh when x"D", - charEHigh when x"E", - charFHigh when x"F", - charFHigh when others; - intAddress <= iIntAddress; - intWrite <= iIntWrite; - intRead <= iIntRead; - intReq <= '1' when (iReadReq = '1') else - '1' when (iWriteReq = '1') else '0'; - end Behavioral; diff --git a/tests/designs/uart2bus/vhdl/uartRx.vhd b/tests/designs/uart2bus/vhdl/uartRx.vhd deleted file mode 100644 index 26fc8bc8..00000000 --- a/tests/designs/uart2bus/vhdl/uartRx.vhd +++ /dev/null @@ -1,111 +0,0 @@ ------------------------------------------------------------------------------------------ --- uart receive module --- ------------------------------------------------------------------------------------------ -library ieee; -use ieee.std_logic_1164.ALL; -use ieee.std_logic_unsigned.ALL; - -entity uartRx is - port ( clr : in std_logic; -- global reset input - clk : in std_logic; -- global clock input - ce16 : in std_logic; -- baud rate multiplyed by 16 - generated by baud module - serIn : in std_logic; -- serial data input - rxData : out std_logic_vector(7 downto 0); -- data byte received - newRxData : out std_logic); -- signs that a new byte was received -end uartRx; - -architecture Behavioral of uartRx is - - signal ce1 : std_logic; -- clock enable at bit rate - signal ce1Mid : std_logic; -- clock enable at the middle of each bit - used to sample data - signal inSync : std_logic_vector(1 downto 0); - signal count16 : std_logic_vector(3 downto 0); - signal rxBusy : std_logic; - signal bitCount : std_logic_vector(3 downto 0); - signal dataBuf : std_logic_vector(7 downto 0); - - begin - -- input async input is sampled twice - process (clr, clk) - begin - if (clr = '1') then - inSync <= (others => '1'); - elsif (rising_edge(clk)) then - inSync <= inSync(0) & serIn; - end if; - end process; - -- a counter to count 16 pulses of ce_16 to generate the ce_1 and ce_1_mid pulses. - -- this counter is used to detect the start bit while the receiver is not receiving and - -- signs the sampling cycle during reception. - process (clr, clk) - begin - if (clr = '1') then - count16 <= (others => '0'); - elsif (rising_edge(clk)) then - if (ce16 = '1') then - if ((rxBusy = '1') or (inSync(1) = '0')) then - count16 <= count16 + 1; - else - count16 <= (others => '0'); - end if; - end if; - end if; - end process; - -- receiving busy flag - process (clr, clk) - begin - if (clr = '1') then - rxBusy <= '0'; - elsif (rising_edge(clk)) then - if ((rxBusy = '0') and (ce1Mid = '1')) then - rxBusy <= '1'; - elsif ((rxBusy = '1') and (bitCount = "1000") and (ce1Mid = '1')) then - rxBusy <= '0'; - end if; - end if; - end process; - -- bit counter - process (clr, clk) - begin - if (clr = '1') then - bitCount <= (others => '0'); - elsif (rising_edge(clk)) then - if (rxBusy = '0') then - bitCount <= (others => '0'); - elsif ((rxBusy = '1') and (ce1Mid = '1')) then - bitCount <= bitCount + 1; - end if; - end if; - end process; - -- data buffer shift register - process (clr, clk) - begin - if (clr = '1') then - dataBuf <= (others => '0'); - elsif (rising_edge(clk)) then - if ((rxBusy = '1') and (ce1Mid = '1')) then - dataBuf <= inSync(1) & dataBuf(7 downto 1); - end if; - end if; - end process; - -- data output and flag - process (clr, clk) - begin - if (clr = '1') then - rxData <= (others => '0'); - newRxData <= '0'; - elsif (rising_edge(clk)) then - if ((rxBusy = '1') and (bitCount = "1000") and (ce1 = '1')) then - rxData <= dataBuf; - newRxData <= '1'; - else - newRxData <= '0'; - end if; - end if; - end process; - -- ce_1 pulse indicating expected end of current bit - ce1 <= '1' when ((count16 = "1111") and (ce16 = '1')) else '0'; - -- ce_1_mid pulse indication the sampling clock cycle of the current data bit - ce1Mid <= '1' when ((count16 = "0111") and (ce16 = '1')) else '0'; - end Behavioral; diff --git a/tests/designs/uart2bus/vhdl/uartTop.vhd b/tests/designs/uart2bus/vhdl/uartTop.vhd deleted file mode 100644 index 2b64cb5a..00000000 --- a/tests/designs/uart2bus/vhdl/uartTop.vhd +++ /dev/null @@ -1,63 +0,0 @@ ------------------------------------------------------------------------------------------ --- uart top level module --- ------------------------------------------------------------------------------------------ -library ieee; -use ieee.std_logic_1164.all; - -library work; -use work.uart2BusTop_pkg.all; - -entity uartTop is - port ( -- global signals - clr : in std_logic; -- global reset input - clk : in std_logic; -- global clock input - -- uart serial signals - serIn : in std_logic; -- serial data input - serOut : out std_logic; -- serial data output - -- transmit and receive internal interface signals - txData : in std_logic_vector(7 downto 0); -- data byte to transmit - newTxData : in std_logic; -- asserted to indicate that there is a new data byte for transmission - txBusy : out std_logic; -- signs that transmitter is busy - rxData : out std_logic_vector(7 downto 0); -- data byte received - newRxData : out std_logic; -- signs that a new byte was received - -- baud rate configuration register - see baudGen.vhd for details - baudFreq : in std_logic_vector(11 downto 0); -- baud rate setting registers - see header description - baudLimit : in std_logic_vector(15 downto 0); -- baud rate setting registers - see header description - baudClk : out std_logic); -- -end uartTop; - -architecture Behavioral of uartTop is - - signal ce16 : std_logic; -- clock enable at bit rate - - begin - -- baud rate generator module - bg : baudGen - port map ( - clr => clr, - clk => clk, - baudFreq => baudFreq, - baudLimit => baudLimit, - ce16 => ce16); - -- uart receiver - ut : uartTx - port map ( - clr => clr, - clk => clk, - ce16 => ce16, - txData => txData, - newTxData => newTxData, - serOut => serOut, - txBusy => txBusy); - -- uart transmitter - ur : uartRx - port map ( - clr => clr, - clk => clk, - ce16 => ce16, - serIn => serIn, - rxData => rxData, - newRxData => newRxData); - baudClk <= ce16; - end Behavioral; diff --git a/tests/designs/uart2bus/vhdl/uartTx.vhd b/tests/designs/uart2bus/vhdl/uartTx.vhd deleted file mode 100644 index e978f506..00000000 --- a/tests/designs/uart2bus/vhdl/uartTx.vhd +++ /dev/null @@ -1,96 +0,0 @@ ------------------------------------------------------------------------------------------ --- uart transmit module --- ------------------------------------------------------------------------------------------ -library ieee; -use ieee.std_logic_1164.all; -use ieee.std_logic_unsigned.all; - -entity uartTx is - port ( clr : in std_logic; -- global reset input - clk : in std_logic; -- global clock input - ce16 : in std_logic; -- baud rate multiplyed by 16 - generated by baud module - txData : in std_logic_vector(7 downto 0); -- data byte to transmit - newTxData : in std_logic; -- asserted to indicate that there is a new data byte for transmission - serOut : out std_logic; -- serial data output - txBusy : out std_logic); -- signs that transmitter is busy -end uartTx; - -architecture Behavioral of uartTx is - - signal iTxBusy : std_logic; - signal ce1 : std_logic; -- clock enable at bit rate - signal count16 : std_logic_vector(3 downto 0); - signal bitCount : std_logic_vector(3 downto 0); - signal dataBuf : std_logic_vector(8 downto 0); - - begin - -- a counter to count 16 pulses of ce_16 to generate the ce_1 pulse - process (clr, clk) - begin - if (clr = '1') then - count16 <= (others => '0'); - elsif (rising_edge(clk)) then - if ((iTxBusy = '1') and (ce16 = '1')) then - count16 <= count16 + 1; - elsif (iTxBusy = '0') then - count16 <= (others => '0'); - end if; - end if; - end process; - -- tx_busy flag - process (clr, clk) - begin - if (clr = '1') then - iTxBusy <= '0'; - elsif (rising_edge(clk)) then - if ((iTxBusy = '0') and (newTxData = '1')) then - iTxBusy <= '1'; - elsif ((iTxBusy = '1') and (bitCount = "1001") and (ce1 = '1')) then - iTxBusy <= '0'; - end if; - end if; - end process; - -- output bit counter - process (clr, clk) - begin - if (clr = '1') then - bitCount <= (others => '0'); - elsif (rising_edge(clk)) then - if ((iTxBusy = '1') and (ce1 = '1')) then - bitCount <= bitCount + 1; - elsif (iTxBusy = '0') then - bitCount <= (others => '0'); - end if; - end if; - end process; - -- data shift register - process (clr, clk) - begin - if (clr = '1') then - dataBuf <= (others => '0'); - elsif (rising_edge(clk)) then - if (iTxBusy = '0') then - dataBuf <= txData & '0'; - elsif ((iTxBusy = '1') and (ce1 = '1')) then - dataBuf <= '1' & dataBuf(8 downto 1); - end if; - end if; - end process; - -- output data bit - process (clr, clk) - begin - if (clr = '1') then - serOut <= '1'; - elsif (rising_edge(clk)) then - if (iTxBusy = '1') then - serOut <= dataBuf(0); - else - serOut <= '1'; - end if; - end if; - end process; - -- ce_1 pulse indicating output data bit should be updated - ce1 <= '1' when ((count16 = "1111") and (ce16 = '1')) else '0'; - txBusy <= iTxBusy; - end Behavioral; diff --git a/tests/designs/vhdl_configurations/Makefile b/tests/designs/vhdl_configurations/Makefile deleted file mode 100644 index af52a372..00000000 --- a/tests/designs/vhdl_configurations/Makefile +++ /dev/null @@ -1,39 +0,0 @@ -TOPLEVEL_LANG ?= vhdl - -ifneq ($(TOPLEVEL_LANG),vhdl) - -# Currently, there is an issue with file dependency when dealing with a Mixed Language Simulation like this. -# With the current process, all the VHDL is compiled first and then all the Verilog which doesn't work with a -# dependency like below. Compile order would need to be: -# 1. dut.vhd -# 2. testbench.vhd or testbench.v -# 3. configurations.vhd -all: - @echo "Skipping test due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being vhdl" -clean:: - -else - -TOPLEVEL = testbench - -PWD=$(shell pwd) - -COCOTB?=$(PWD)/../../.. - -VHDL_SOURCES = $(COCOTB)/tests/designs/vhdl_configurations/dut.vhd -ifeq ($(TOPLEVEL_LANG),verilog) - VERILOG_SOURCES = $(COCOTB)/tests/designs/vhdl_configurations/testbench.sv -else ifeq ($(TOPLEVEL_LANG),vhdl) - VHDL_SOURCES += $(COCOTB)/tests/designs/vhdl_configurations/testbench.vhd -else - $(error "A valid value (verilog or vhdl) was not provided for TOPLEVEL_LANG=$(TOPLEVEL_LANG)") -endif -VHDL_SOURCES += $(COCOTB)/tests/designs/vhdl_configurations/configurations.vhd - -ifneq ($(filter $(SIM),ius xcelium),) - SIM_ARGS += -v93 -endif - -include $(shell cocotb-config --makefiles)/Makefile.sim - -endif diff --git a/tests/designs/vhdl_configurations/configurations.vhd b/tests/designs/vhdl_configurations/configurations.vhd deleted file mode 100644 index e6a8b7d6..00000000 --- a/tests/designs/vhdl_configurations/configurations.vhd +++ /dev/null @@ -1,15 +0,0 @@ -configuration config_single of testbench is - for myconfig - for dut_inst : dut - use entity work.dut(single); - end for; - end for; -end configuration config_single; - -configuration config_double of testbench is - for myconfig - for dut_inst : dut - use entity work.dut(double); - end for; - end for; -end configuration config_double; diff --git a/tests/designs/vhdl_configurations/dut.vhd b/tests/designs/vhdl_configurations/dut.vhd deleted file mode 100644 index 94cb7198..00000000 --- a/tests/designs/vhdl_configurations/dut.vhd +++ /dev/null @@ -1,42 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; - -entity dut is - port( - clk : in std_ulogic; - data_in : in std_ulogic; - data_out : out std_ulogic - ); -end entity dut; - -architecture single of dut is begin - - report_p : process begin - report "this is dut(single)"; - wait; - end process; - - clocked_p : process(clk) is begin - if rising_edge(clk) then - data_out <= data_in; - end if; - end process; -end architecture single; - - -architecture double of dut is - signal data_in_r : std_ulogic; -begin - - report_p : process begin - report "this is dut(double)"; - wait; - end process; - - clocked_p : process(clk) is begin - if rising_edge(clk) then - data_in_r <= data_in; - data_out <= data_in_r; - end if; - end process; -end architecture double; diff --git a/tests/designs/vhdl_configurations/testbench.sv b/tests/designs/vhdl_configurations/testbench.sv deleted file mode 100644 index c93443ab..00000000 --- a/tests/designs/vhdl_configurations/testbench.sv +++ /dev/null @@ -1,15 +0,0 @@ -module testbench ( - input logic clk, - input logic data_in, - output logic data_out -); - -dut i_dut ( - .clk (clk), - .data_in (data_in), - .data_out (data_out) -); - -endmodule - - diff --git a/tests/designs/vhdl_configurations/testbench.vhd b/tests/designs/vhdl_configurations/testbench.vhd deleted file mode 100644 index 82785857..00000000 --- a/tests/designs/vhdl_configurations/testbench.vhd +++ /dev/null @@ -1,28 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; - -entity testbench is -end entity testbench; - -architecture myconfig of testbench is - component dut - port( - clk : in std_ulogic; - data_in : in std_ulogic; - data_out : out std_ulogic); - end component dut; - - signal clk : std_ulogic; - signal data_in : std_ulogic; - signal data_out : std_ulogic; - -begin - - dut_inst : component dut - port map( - clk => clk, - data_in => data_in, - data_out => data_out - ); -end architecture myconfig; - diff --git a/tests/designs/viterbi_decoder_axi4s/Makefile b/tests/designs/viterbi_decoder_axi4s/Makefile deleted file mode 100644 index ffd49411..00000000 --- a/tests/designs/viterbi_decoder_axi4s/Makefile +++ /dev/null @@ -1,70 +0,0 @@ -############################################################################### -# Copyright (c) 2015 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -TOPLEVEL_LANG ?= vhdl - -ifneq ($(TOPLEVEL_LANG),vhdl) - -all: - @echo "Skipping test due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being vhdl" -clean:: - -else - -TOPLEVEL = dec_viterbi_ent - -PWD=$(shell pwd) - -COCOTB?=$(PWD)/../../.. - -SRC_BASE = $(COCOTB)/tests/designs/viterbi_decoder_axi4s - -RTL_LIBRARY = dec_viterbi - -VHDL_SOURCES = $(SRC_BASE)/packages/pkg_helper.vhd \ - $(SRC_BASE)/packages/pkg_param.vhd \ - $(SRC_BASE)/packages/pkg_param_derived.vhd \ - $(SRC_BASE)/packages/pkg_types.vhd \ - $(SRC_BASE)/packages/pkg_components.vhd \ - $(SRC_BASE)/packages/pkg_trellis.vhd \ - $(SRC_BASE)/src/generic_sp_ram.vhd \ - $(SRC_BASE)/src/axi4s_buffer.vhd \ - $(SRC_BASE)/src/branch_distance.vhd \ - $(SRC_BASE)/src/traceback.vhd \ - $(SRC_BASE)/src/acs.vhd \ - $(SRC_BASE)/src/ram_ctrl.vhd \ - $(SRC_BASE)/src/reorder.vhd \ - $(SRC_BASE)/src/recursion.vhd \ - $(SRC_BASE)/src/dec_viterbi.vhd - -ifneq ($(filter $(SIM),ius xcelium),) - SIM_ARGS += -v93 -endif - -include $(shell cocotb-config --makefiles)/Makefile.sim - -endif diff --git a/tests/designs/viterbi_decoder_axi4s/gpl-2.0.txt b/tests/designs/viterbi_decoder_axi4s/gpl-2.0.txt deleted file mode 100644 index d159169d..00000000 --- a/tests/designs/viterbi_decoder_axi4s/gpl-2.0.txt +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/tests/designs/viterbi_decoder_axi4s/packages/pkg_components.vhd b/tests/designs/viterbi_decoder_axi4s/packages/pkg_components.vhd deleted file mode 100644 index 62b097a0..00000000 --- a/tests/designs/viterbi_decoder_axi4s/packages/pkg_components.vhd +++ /dev/null @@ -1,194 +0,0 @@ ---! ---! Copyright (C) 2011 - 2014 Creonic GmbH ---! ---! This file is part of the Creonic Viterbi Decoder, which is distributed ---! under the terms of the GNU General Public License version 2. ---! ---! @file ---! @brief Component declarations for Viterbi decoder ---! @author Markus Fehrenz ---! @date 2011/04/07 ---! ---! - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library dec_viterbi; -use dec_viterbi.pkg_param.all; -use dec_viterbi.pkg_param_derived.all; -use dec_viterbi.pkg_types.all; - -package pkg_components is - - component axi4s_buffer is - generic ( - DATA_WIDTH : natural := 1 - ); - port ( - clk : in std_logic; - rst : in std_logic; - - input : in std_logic_vector(DATA_WIDTH - 1 downto 0); - input_valid : in std_logic; - input_last : in std_logic; - input_accept : out std_logic; - - output : out std_logic_vector(DATA_WIDTH - 1 downto 0); - output_valid : out std_logic; - output_last : out std_logic; - output_accept : in std_logic - ); - end component axi4s_buffer; - - component branch_distance is - generic( - EDGE_WEIGHT : in std_logic_vector(NUMBER_PARITY_BITS - 1 downto 0) - ); - port( - clk : in std_logic; - rst : in std_logic; - - s_axis_input_tvalid : in std_logic; - s_axis_input_tdata : in t_input_block; - s_axis_input_tlast : in std_logic; - s_axis_input_tready : out std_logic; - - m_axis_output_tvalid : out std_logic; - m_axis_output_tdata : out std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); - m_axis_output_tlast : out std_logic; - m_axis_output_tready : in std_logic - - ); - end component branch_distance; - - component acs is - generic( - initialize_value : in signed(BW_MAX_PROBABILITY - 1 downto 0) - ); - port( - clk : in std_logic; - rst : in std_logic; - - s_axis_inbranch_tvalid : in std_logic; - s_axis_inbranch_tdata_low : in std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); - s_axis_inbranch_tdata_high : in std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); - s_axis_inbranch_tlast : in std_logic; - s_axis_inbranch_tready : out std_logic; - - s_axis_inprev_tvalid : in std_logic; - s_axis_inprev_tdata_low : in std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); - s_axis_inprev_tdata_high : in std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); - s_axis_inprev_tready : out std_logic; - - m_axis_outprob_tvalid : out std_logic; - m_axis_outprob_tdata : out std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); - m_axis_outprob_tready : in std_logic; - - m_axis_outdec_tvalid : out std_logic; - m_axis_outdec_tdata : out std_logic; - m_axis_outdec_tlast : out std_logic; - m_axis_outdec_tready : in std_logic - ); - end component acs; - - component ram_ctrl is - port( - clk : in std_logic; - rst : in std_logic; - - s_axis_input_tvalid : in std_logic; - s_axis_input_tdata : in std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); - s_axis_input_tlast : in std_logic; - s_axis_input_tready : out std_logic; - - m_axis_output_tvalid : out std_logic_vector(1 downto 0); - m_axis_output_tdata : out t_ram_rd_data; - m_axis_output_tlast : out std_logic_vector(1 downto 0); - m_axis_output_tready : in std_logic_vector(1 downto 0); - m_axis_output_window_tuser : out std_logic_vector(1 downto 0); - m_axis_output_last_tuser : out std_logic_vector(1 downto 0); - - s_axis_ctrl_tvalid : in std_logic; - s_axis_ctrl_tdata : in std_logic_vector(31 downto 0); - s_axis_ctrl_tready : out std_logic - ); - end component ram_ctrl; - - component generic_sp_ram is - generic( - DISTR_RAM : boolean; - WORDS : integer; - BITWIDTH : integer - ); - port( - clk : in std_logic; - rst : in std_logic; - - wen : in std_logic; - en : in std_logic; - - a : in std_logic_vector(BW_MAX_WINDOW_LENGTH - 1 downto 0); - d : in std_logic_vector(BITWIDTH - 1 downto 0 ); - q : out std_logic_vector(BITWIDTH - 1 downto 0) - ); - end component generic_sp_ram; - - component trellis_traceback is - port( - clk : in std_logic; - rst : in std_logic; - - s_axis_input_tvalid : in std_logic; - s_axis_input_tdata : in std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); - s_axis_input_tlast : in std_logic; - s_axis_input_tready : out std_logic; - s_axis_input_window_tuser : in std_logic; - s_axis_input_last_tuser : in std_logic; - - m_axis_output_tvalid : out std_logic; - m_axis_output_tdata : out std_logic; - m_axis_output_tlast : out std_logic; - m_axis_output_last_tuser : out std_logic; - m_axis_output_tready : in std_logic - ); - end component trellis_traceback; - - component reorder is - port( - clk : in std_logic; - rst : in std_logic; - - s_axis_input_tvalid : in std_logic; - s_axis_input_tdata : in std_logic; - s_axis_input_tlast : in std_logic; - s_axis_input_last_tuser : in std_logic; - s_axis_input_tready : out std_logic; - - m_axis_output_tvalid : out std_logic; - m_axis_output_tdata : out std_logic; - m_axis_output_tlast : out std_logic; - m_axis_output_last_tuser : out std_logic; - m_axis_output_tready : in std_logic - ); - end component reorder; - - component recursionx is - port( - clk : in std_logic; - rst : in std_logic; - - s_axis_input_tvalid : in std_logic; - s_axis_input_tdata : in std_logic; - s_axis_input_tlast : in std_logic; - s_axis_input_tready : out std_logic; - - m_axis_output_tvalid : out std_logic; - m_axis_output_tdata : out std_logic; - m_axis_output_tlast : out std_logic; - m_axis_output_tready : in std_logic - ); - end component recursionx; - -end package pkg_components; diff --git a/tests/designs/viterbi_decoder_axi4s/packages/pkg_helper.vhd b/tests/designs/viterbi_decoder_axi4s/packages/pkg_helper.vhd deleted file mode 100644 index 8fe59743..00000000 --- a/tests/designs/viterbi_decoder_axi4s/packages/pkg_helper.vhd +++ /dev/null @@ -1,58 +0,0 @@ ---! ---! Copyright (C) 2011 - 2014 Creonic GmbH ---! ---! This file is part of the Creonic Viterbi Decoder, which is distributed ---! under the terms of the GNU General Public License version 2. ---! ---! @file ---! @brief Helper package with useful functions ---! @author Markus Fehrenz ---! @date 2011/12/02 ---! - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - - -package pkg_helper is - - --! - --! Return the log_2 of an natural value, i.e. the number of bits required - --! to represent this unsigned value. - --! - function no_bits_natural(value_in : natural) return natural; - - --! Return maximum of two input values - function max(value_in_a, value_in_b : natural) return natural; - -end pkg_helper; - - -package body pkg_helper is - - function no_bits_natural(value_in: natural) return natural is - variable v_n_bit : unsigned(31 downto 0); - begin - if value_in = 0 then - return 0; - end if; - v_n_bit := to_unsigned(value_in, 32); - for i in 31 downto 0 loop - if v_n_bit(i) = '1' then - return i + 1; - end if; - end loop; - return 1; - end no_bits_natural; - - function max(value_in_a, value_in_b : natural) return natural is - begin - if value_in_a > value_in_b then - return value_in_a; - else - return value_in_b; - end if; - end function; - -end pkg_helper; diff --git a/tests/designs/viterbi_decoder_axi4s/packages/pkg_param.vhd b/tests/designs/viterbi_decoder_axi4s/packages/pkg_param.vhd deleted file mode 100644 index 1bc3f660..00000000 --- a/tests/designs/viterbi_decoder_axi4s/packages/pkg_param.vhd +++ /dev/null @@ -1,78 +0,0 @@ ---! ---! Copyright (C) 2011 - 2014 Creonic GmbH ---! ---! This file is part of the Creonic Viterbi Decoder, which is distributed ---! under the terms of the GNU General Public License version 2. ---! ---! @file ---! @brief Parameters ---! @author Markus Fehrenz ---! @date 2011/07/01 ---! ---! @details This is the configuration file of the Viterbi decoder. ---! Any changes for parameters should be done here. ---! Changing parameters somewhere else may result in a malicious ---! behavior. ---! - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - - -package pkg_param is - ----------------------------------- - -- Convolutional Code Parameters -- - ----------------------------------- - - - -- - -- Set the number of parity values - -- This has to correspond to PARITY_POLYNOMIALS - -- - constant NUMBER_PARITY_BITS : natural := 2; - type t_parity is array (NUMBER_PARITY_BITS - 1 downto 0) of natural; - - -- - -- Set parity polynoms in decimal notation - -- NUMBER_PARITY_BITS has to correspond to the number of elements - -- Examples: WiFi : [121,91] or [121,91,101] - -- CDMA : [491,369] or [367,435,369] or [501,441,331,315] - -- GSM : [27,19] or [27,21,31] - -- DAB : [91,121,101,91] - -- WiMAX: [91,121,117] - -- - constant PARITY_POLYNOMIALS : t_parity := (121,91); - - - -- - -- Set a recursive polynomial - -- Set to 0 if no recursion is used - -- Setting this arbitrary may result in a worse error correction ability - -- - constant FEEDBACK_POLYNOMIAL : natural := 0; - - - ----------------------------- - -- Architecture Parameters -- - ----------------------------- - - -- - -- Set bit width of LLR input - -- Recommended values: 3 or 4 - -- - constant BW_LLR_INPUT : natural := 4; - - -- - -- Set the maximum window length which shall be allowed at runtime. - -- Recommended: at least 6 * constraint length - -- - constant MAX_WINDOW_LENGTH : natural := 96; - - -- - -- Set to 'true' if distributed RAM shall be used - -- Set to 'false' if block RAM shall be used - -- - constant DISTRIBUTED_RAM : boolean := true; - -end package pkg_param; diff --git a/tests/designs/viterbi_decoder_axi4s/packages/pkg_param_derived.vhd b/tests/designs/viterbi_decoder_axi4s/packages/pkg_param_derived.vhd deleted file mode 100644 index f6e77cad..00000000 --- a/tests/designs/viterbi_decoder_axi4s/packages/pkg_param_derived.vhd +++ /dev/null @@ -1,73 +0,0 @@ ---! ---! Copyright (C) 2011 - 2014 Creonic GmbH ---! ---! This file is part of the Creonic Viterbi Decoder, which is distributed ---! under the terms of the GNU General Public License version 2. ---! ---! @file ---! @brief Derived parameters ---! @author Markus Fehrenz ---! @date 2011/07/04 ---! ---! @details This constants are derived from constants defined in pkg_param. ---! In order to prevent errors, there is no user choice for these parameters. ---! - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library dec_viterbi; -use dec_viterbi.pkg_param.all; -use dec_viterbi.pkg_helper.all; - - -package pkg_param_derived is - - -- Calculation of constraint length. - function calc_constraint_length return natural; - - -- Memory depth of the encoder shift register. - constant ENCODER_MEMORY_DEPTH : natural; - - -- Number of trellis states corresponds to the nubmer of ACS units. - constant NUMBER_TRELLIS_STATES : natural; - - -- Number of branch units for a single polynomial set - constant NUMBER_BRANCH_UNITS : natural; - - -- Bitwidth constants are needed for type conversions - constant BW_TRELLIS_STATES : natural; - constant BW_MAX_WINDOW_LENGTH : natural; - constant BW_BRANCH_RESULT : natural; - constant BW_MAX_PROBABILITY : natural; - -end package pkg_param_derived; - - -package body pkg_param_derived is - - function calc_constraint_length return natural is - variable v_maximum : natural := 0; - begin - - -- Find the position of the leftmost bit in the polynomials. - for i in NUMBER_PARITY_BITS - 1 downto 0 loop - v_maximum := max(v_maximum, no_bits_natural(PARITY_POLYNOMIALS(i))); - end loop; - v_maximum := max(v_maximum, no_bits_natural(FEEDBACK_POLYNOMIAL)); - return v_maximum; - end function calc_constraint_length; - - - constant ENCODER_MEMORY_DEPTH : natural := calc_constraint_length - 1; - - constant NUMBER_TRELLIS_STATES : natural := 2 ** ENCODER_MEMORY_DEPTH; - - constant NUMBER_BRANCH_UNITS : natural := 2 ** NUMBER_PARITY_BITS; - - constant BW_TRELLIS_STATES : natural := no_bits_natural(NUMBER_TRELLIS_STATES - 1); - constant BW_MAX_WINDOW_LENGTH : natural := no_bits_natural(MAX_WINDOW_LENGTH - 1); - constant BW_BRANCH_RESULT : natural := no_bits_natural((2 ** (BW_LLR_INPUT - 1)) * NUMBER_PARITY_BITS) + 1; - constant BW_MAX_PROBABILITY : natural := no_bits_natural(((2 ** (BW_LLR_INPUT - 1)) * NUMBER_PARITY_BITS) * 4 * ENCODER_MEMORY_DEPTH); -end package body pkg_param_derived; diff --git a/tests/designs/viterbi_decoder_axi4s/packages/pkg_trellis.vhd b/tests/designs/viterbi_decoder_axi4s/packages/pkg_trellis.vhd deleted file mode 100644 index a797d3ac..00000000 --- a/tests/designs/viterbi_decoder_axi4s/packages/pkg_trellis.vhd +++ /dev/null @@ -1,185 +0,0 @@ ---! ---! Copyright (C) 2011 - 2014 Creonic GmbH ---! ---! This file is part of the Creonic Viterbi Decoder, which is distributed ---! under the terms of the GNU General Public License version 2. ---! ---! @file ---! @brief Trellis parameter calculations (e.g., transitions, init values). ---! @author Markus Fehrenz ---! @date 2011/07/27 ---! ---! - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library dec_viterbi; -use dec_viterbi.pkg_param.all; -use dec_viterbi.pkg_param_derived.all; -use dec_viterbi.pkg_types.all; - - -package pkg_trellis is - - type t_prev_base is array (1 downto 0) of std_logic_vector(BW_TRELLIS_STATES - 1 downto 0); - type t_previous_states is array (NUMBER_TRELLIS_STATES - 1 downto 0) of t_prev_base; - - type t_trans_base is array (1 downto 0) of std_logic_vector(NUMBER_PARITY_BITS - 1 downto 0); - type t_transitions is array (NUMBER_TRELLIS_STATES - 1 downto 0) of t_trans_base; - - type t_trans_base_signed is array (1 downto 0) of std_logic_vector(NUMBER_PARITY_BITS downto 0); - type t_transitions_signed is array (NUMBER_TRELLIS_STATES - 1 downto 0) of t_trans_base_signed; - - - -- - -- This function calculates the previous states of each state. - -- The values are used to connect the ACS units. - -- - function calc_previous_states return t_previous_states; - - - -- - -- This function calculates corresponding transitions to a trellis sate. - -- The values are used to connect branch units to ACS units. - -- - function calc_transitions return t_transitions; - - - -- - -- This function calculates the initialization values for trellis metrics. - -- The values are used as a constant and written to the ACS unit, every time a new block arrives. - -- - function calc_initialize return t_node_s; - - constant PREVIOUS_STATES : t_previous_states; - constant TRANSITIONS : t_transitions; - constant INITIALIZE_TRELLIS : t_node_s; - -end package pkg_trellis; - - -package body pkg_trellis is - - - function calc_previous_states return t_previous_states is - variable v_prev_states : t_previous_states := (others=>(others=>(others => '0'))); - variable v_state0, v_state1 : std_logic_vector(BW_TRELLIS_STATES - 1 downto 0); - begin - for i in NUMBER_TRELLIS_STATES - 1 downto 0 loop - v_state0 := std_logic_vector(to_unsigned(i,BW_TRELLIS_STATES)); - v_state1 := v_state0(BW_TRELLIS_STATES - 2 downto 0) & '0'; - v_prev_states(i)(0) := v_state1; - v_state1 := v_state0(BW_TRELLIS_STATES - 2 downto 0) & '1'; - v_prev_states(i)(1) := v_state1; - end loop; - return v_prev_states; - end function calc_previous_states; - - - function calc_transitions return t_transitions is - variable v_transitions : t_transitions_signed := (others => (others => (others => '0'))); - variable v_transitions_out : t_transitions := (others => (others => (others => '0'))); - variable v_one_transition : std_logic_vector(NUMBER_PARITY_BITS - 1 downto 0); - variable v_next_state : unsigned(ENCODER_MEMORY_DEPTH - 1 downto 0) := (others => '0'); - variable v_state, v_states : unsigned(ENCODER_MEMORY_DEPTH downto 0); - variable v_bit : std_logic := '0'; - begin - - -- - -- It is possible to reduce code size at this stage, if feedback is handled differently, - -- but the complexity will increase. - -- - - for i in NUMBER_TRELLIS_STATES - 1 downto 0 loop - - -- - -- for input : 0 - -- determine correct input with feedback - -- - v_next_state := to_unsigned(i,ENCODER_MEMORY_DEPTH) and to_unsigned(FEEDBACK_POLYNOMIAL, ENCODER_MEMORY_DEPTH); - for k in ENCODER_MEMORY_DEPTH - 1 downto 0 loop - v_bit := v_bit xor v_next_state(k); - end loop; - v_state(ENCODER_MEMORY_DEPTH) := v_bit; - v_state(ENCODER_MEMORY_DEPTH - 1 downto 0) := to_unsigned(i,ENCODER_MEMORY_DEPTH); - v_next_state := v_state(ENCODER_MEMORY_DEPTH downto 1); - v_bit := '0'; - - -- determine paritybits - for j in NUMBER_PARITY_BITS - 1 downto 0 loop - v_states := v_state and to_unsigned(PARITY_POLYNOMIALS(j), ENCODER_MEMORY_DEPTH + 1); - for k in ENCODER_MEMORY_DEPTH downto 0 loop - v_bit := v_bit xor v_states(k); - end loop; - v_one_transition(j) := v_bit; - v_bit := '0'; - end loop; - - -- decide where to save the parity result - if v_transitions(to_integer(v_next_state))(1)(NUMBER_PARITY_BITS) = '0' then - v_transitions(to_integer(v_next_state))(1)(NUMBER_PARITY_BITS) := '1'; - v_transitions(to_integer(v_next_state))(1)(NUMBER_PARITY_BITS - 1 downto 0) := v_one_transition; - else - v_transitions(to_integer(v_next_state))(0)(NUMBER_PARITY_BITS - 1 downto 0) := v_one_transition; - end if; - - -- - -- for input: 1 - -- determine correct input with feedback - -- - v_next_state := to_unsigned(i,ENCODER_MEMORY_DEPTH) and to_unsigned(FEEDBACK_POLYNOMIAL, ENCODER_MEMORY_DEPTH); - for k in ENCODER_MEMORY_DEPTH - 1 downto 0 loop - v_bit := v_bit xor v_next_state(k); - end loop; - v_state(ENCODER_MEMORY_DEPTH) := '1' xor v_bit; - v_state(ENCODER_MEMORY_DEPTH - 1 downto 0) := to_unsigned(i,ENCODER_MEMORY_DEPTH); - v_next_state := v_state(ENCODER_MEMORY_DEPTH downto 1); - v_bit := '0'; - - -- determine paritybits - for j in NUMBER_PARITY_BITS - 1 downto 0 loop - v_states := v_state and to_unsigned(PARITY_POLYNOMIALS(j), ENCODER_MEMORY_DEPTH + 1); - for k in ENCODER_MEMORY_DEPTH downto 0 loop - v_bit := v_bit xor v_states(k); - end loop; - v_one_transition(j) := v_bit; - v_bit := '0'; - end loop; - - -- decide where to save parity result - if v_transitions(to_integer(v_next_state))(1)(NUMBER_PARITY_BITS) = '0' then - v_transitions(to_integer(v_next_state))(1)(NUMBER_PARITY_BITS) := '1'; - v_transitions(to_integer(v_next_state))(1)(NUMBER_PARITY_BITS - 1 downto 0) := v_one_transition; - else - v_transitions(to_integer(v_next_state))(0)(NUMBER_PARITY_BITS - 1 downto 0) := v_one_transition; - end if; - end loop; - - -- truncate, the bit, used to decide where to save parity result - for i in NUMBER_TRELLIS_STATES - 1 downto 0 loop - v_transitions_out(i)(1) := v_transitions(i)(1)(NUMBER_PARITY_BITS - 1 downto 0); - v_transitions_out(i)(0) := v_transitions(i)(0)(NUMBER_PARITY_BITS - 1 downto 0); - end loop; - - return v_transitions_out; - end function calc_transitions; - - - function calc_initialize return t_node_s is - variable v_initialize : t_node_s; - begin - v_initialize(0) := to_signed(0, BW_MAX_PROBABILITY); - for i in NUMBER_TRELLIS_STATES - 1 downto 1 loop - v_initialize(i) := to_signed(- 2 ** (BW_MAX_PROBABILITY - 2), BW_MAX_PROBABILITY); - end loop; - return v_initialize; - end function calc_initialize; - - - constant PREVIOUS_STATES : t_previous_states := calc_previous_states; - constant TRANSITIONS : t_transitions := calc_transitions; - constant INITIALIZE_TRELLIS : t_node_s := calc_initialize; - -end package body pkg_trellis; diff --git a/tests/designs/viterbi_decoder_axi4s/packages/pkg_types.vhd b/tests/designs/viterbi_decoder_axi4s/packages/pkg_types.vhd deleted file mode 100644 index 5acdd4eb..00000000 --- a/tests/designs/viterbi_decoder_axi4s/packages/pkg_types.vhd +++ /dev/null @@ -1,37 +0,0 @@ ---! ---! Copyright (C) 2011 - 2014 Creonic GmbH ---! ---! This file is part of the Creonic Viterbi Decoder, which is distributed ---! under the terms of the GNU General Public License version 2. ---! ---! @file ---! @brief Global types for the Viterbi decoder ---! @author Markus Fehrenz ---! @date 2011/07/04 ---! ---! @details Most types are shared and used in different context. ---! Changing single types should be done with adding an additional type. ---! - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library dec_viterbi; -use dec_viterbi.pkg_param.all; -use dec_viterbi.pkg_param_derived.all; - -package pkg_types is - - -- Parity structure: p1_bit, p2_bit, ..., pN_bit - type t_input_block is array (NUMBER_PARITY_BITS - 1 downto 0) of signed(BW_LLR_INPUT - 1 downto 0); - - -- Types are used for bulk information to ACS and branch unit. - type t_node_s is array (NUMBER_TRELLIS_STATES - 1 downto 0) of signed(BW_MAX_PROBABILITY - 1 downto 0); - type t_node is array (NUMBER_TRELLIS_STATES - 1 downto 0) of std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); - type t_branch is array (NUMBER_BRANCH_UNITS - 1 downto 0) of std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); - - -- RAM Data - type t_ram_rd_data is array (1 downto 0) of std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); - -end package pkg_types; diff --git a/tests/designs/viterbi_decoder_axi4s/src/acs.vhd b/tests/designs/viterbi_decoder_axi4s/src/acs.vhd deleted file mode 100644 index 958050a3..00000000 --- a/tests/designs/viterbi_decoder_axi4s/src/acs.vhd +++ /dev/null @@ -1,134 +0,0 @@ ---! ---! Copyright (C) 2011 - 2014 Creonic GmbH ---! ---! This file is part of the Creonic Viterbi Decoder, which is distributed ---! under the terms of the GNU General Public License version 2. ---! ---! @file ---! @brief Add-compare-select unit for trellis processing. ---! @author Markus Fehrenz ---! @date 2011/07/04 ---! ---! @details The ACS decides which path is the the surviving trellis path. ---! In the design there are 2^{K-1} ACS instances. ---! - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library dec_viterbi; -use dec_viterbi.pkg_param.all; -use dec_viterbi.pkg_param_derived.all; -use dec_viterbi.pkg_types.all; -use dec_viterbi.pkg_helper.all; - - -entity acs is - generic( - - -- Reset value - INITIALIZE_VALUE : in signed(BW_MAX_PROBABILITY - 1 downto 0) - ); - port( - clk : in std_logic; - rst : in std_logic; - - -- - -- Values from branch distance, signed values in std_logic_vector - -- high is located in the upper half. - -- - s_axis_inbranch_tvalid : in std_logic; - s_axis_inbranch_tdata_low : in std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); - s_axis_inbranch_tdata_high : in std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); - s_axis_inbranch_tlast : in std_logic; - s_axis_inbranch_tready : out std_logic; - - -- - -- Probabilities from previous nodes, signed values in std_logic_vector - -- high is located in the upper half. - -- - s_axis_inprev_tvalid : in std_logic; - s_axis_inprev_tdata_low : in std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); - s_axis_inprev_tdata_high : in std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); - s_axis_inprev_tready : out std_logic; - - -- probability result of the add compare and select - m_axis_outprob_tvalid : out std_logic; - m_axis_outprob_tdata : out std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); - m_axis_outprob_tready : in std_logic; - - -- decision result of the add compare and select - m_axis_outdec_tvalid : out std_logic; - m_axis_outdec_tdata : out std_logic; - m_axis_outdec_tlast : out std_logic; - m_axis_outdec_tready : in std_logic - ); -end entity acs; - - -architecture rtl of acs is - - signal s_axis_inbranch_tlast_d : std_logic; - signal m_axis_outdec_tvalid_int : std_logic; - signal s_axis_inbranch_tready_int : std_logic; - -begin - - s_axis_inbranch_tready_int <= '1' when m_axis_outdec_tready = '1' or m_axis_outdec_tvalid_int = '0' else - '0'; - s_axis_inbranch_tready <= s_axis_inbranch_tready_int; - m_axis_outdec_tvalid <= m_axis_outdec_tvalid_int; - - -- Add branch to previous, compare both paths and select survivor. - pr_add_compare : process(clk) is - variable v_diff, v_high, v_low : signed(BW_MAX_PROBABILITY - 1 downto 0); - begin - if rising_edge(clk) then - if rst = '1' then - m_axis_outdec_tvalid_int <= '0'; - m_axis_outdec_tdata <= '0'; - m_axis_outdec_tlast <= '0'; - m_axis_outprob_tvalid <= '0'; - s_axis_inprev_tready <= '0'; - s_axis_inbranch_tlast_d <= '0'; - m_axis_outprob_tdata <= std_logic_vector(INITIALIZE_VALUE); - else - -- If this is the last value, prepare for processing of next incoming value. - if s_axis_inbranch_tlast_d = '1' then - m_axis_outprob_tdata <= std_logic_vector(INITIALIZE_VALUE); - s_axis_inbranch_tlast_d <= '0'; - m_axis_outdec_tvalid_int <= '0'; - end if; - if m_axis_outdec_tvalid_int = '1' and m_axis_outdec_tready = '1' then - m_axis_outdec_tvalid_int <= '0'; - end if; - - -- Process only if we receive valid data. - if s_axis_inbranch_tvalid = '1' and s_axis_inbranch_tready_int = '1' then - s_axis_inbranch_tlast_d <= s_axis_inbranch_tlast; - - -- Add. - v_low := signed(s_axis_inbranch_tdata_low) + signed(s_axis_inprev_tdata_low); - v_high := signed(s_axis_inbranch_tdata_high) + signed(s_axis_inprev_tdata_high); - - -- Use modulo normalization, do not extend the sign here! - v_diff := v_low - v_high; - - -- Compare, select the correct path. - if v_diff < 0 then - m_axis_outdec_tdata <= '1'; - m_axis_outprob_tdata <= std_logic_vector(v_high); - else - m_axis_outdec_tdata <= '0'; - m_axis_outprob_tdata <= std_logic_vector(v_low); - end if; - m_axis_outdec_tvalid_int <= '1'; - end if; - - m_axis_outdec_tlast <= s_axis_inbranch_tlast; - end if; - end if; - end process pr_add_compare; - -end architecture rtl; diff --git a/tests/designs/viterbi_decoder_axi4s/src/axi4s_buffer.vhd b/tests/designs/viterbi_decoder_axi4s/src/axi4s_buffer.vhd deleted file mode 100644 index 8e479162..00000000 --- a/tests/designs/viterbi_decoder_axi4s/src/axi4s_buffer.vhd +++ /dev/null @@ -1,130 +0,0 @@ ---! ---! Copyright (C) 2012 - 2014 Creonic GmbH ---! ---! This file is part of the Creonic Viterbi Decoder, which is distributed ---! under the terms of the GNU General Public License version 2. ---! ---! @file ---! @brief AXI4-Stream buffer that allows to buffer the accept-signal. ---! @author Matthias Alles ---! @date 2012/04/18 ---! ---! @details ---! One problem when concatenating multiple AXI4-Stream builind blocks is that ---! the accept signal has to pass from the very last component to the input ---! of the very first component. Only then it is possible to have an interruption ---! free data processing within the whole chain. The drawback of this approach is ---! that the accept signal has a long path and high fanouts. ---! This entity allows to use registers on the accept signals by introducing buffers ---! for storing the input values. It should improve timing of bigger building blocks. ---! - -library ieee; -use ieee.std_logic_1164.all; - - -entity axi4s_buffer is - generic ( - DATA_WIDTH : natural := 1 - ); - port ( - - clk : in std_logic; - rst : in std_logic; - - -- Input data handling - ---------------------- - - input : in std_logic_vector(DATA_WIDTH - 1 downto 0); - input_valid : in std_logic; - input_last : in std_logic; - input_accept : out std_logic; - - - -- Output data handling - ----------------------- - output : out std_logic_vector(DATA_WIDTH - 1 downto 0); - output_valid : out std_logic; - output_last : out std_logic; - output_accept : in std_logic -); -end entity axi4s_buffer; - - -architecture rtl of axi4s_buffer is - - - signal input_accept_int : std_logic; - - signal output_reg : std_logic_vector(DATA_WIDTH - 1 downto 0); - signal output_last_reg : std_logic; - signal output_valid_reg : std_logic; - - signal buffer_full : std_logic; - signal buffer_data : std_logic_vector(DATA_WIDTH - 1 downto 0); - signal buffer_last : std_logic; - -begin - - input_accept <= input_accept_int; - - output <= output_reg; - output_last <= output_last_reg; - output_valid <= output_valid_reg; - - -- - -- This process registers all signals. - -- No combinatorial logic is bypassed from input to output and vice versa. - -- - pr_reg: process(clk) is - begin - if rising_edge(clk) then - if rst = '1' then - output_reg <= (others => '0'); - output_last_reg <= '0'; - output_valid_reg <= '0'; - - input_accept_int <= '1'; - - buffer_full <= '0'; - buffer_data <= (others => '0'); - buffer_last <= '0'; - else - - -- - -- Data is coming, buf output data can't be sent => Store input data in buffer - -- and remove input_accept signal! - -- - if input_valid = '1' and input_accept_int = '1' and output_valid_reg = '1' and output_accept = '0' then - buffer_data <= input; - buffer_last <= input_last; - buffer_full <= '1'; - input_accept_int <= '0'; - end if; - - -- - -- Output data is being read but there is data in the buffer waiting for being sent - -- => Use the buffer data! - -- - if output_accept = '1' and output_valid_reg = '1' and buffer_full = '1' then - output_reg <= buffer_data; - output_last_reg <= buffer_last; - output_valid_reg <= '1'; - buffer_full <= '0'; - input_accept_int <= '1'; - - -- - -- Data is being read and buffer is empty => Use input data directly! - -- Output register is empty => Use input data directly! - -- - elsif (output_accept = '1' and output_valid_reg = '1') or output_valid_reg = '0' then - output_reg <= input; - output_last_reg <= input_last; - output_valid_reg <= input_valid; - end if; - - end if; - end if; - end process pr_reg; - -end architecture rtl; diff --git a/tests/designs/viterbi_decoder_axi4s/src/branch_distance.vhd b/tests/designs/viterbi_decoder_axi4s/src/branch_distance.vhd deleted file mode 100644 index 31aa9a4f..00000000 --- a/tests/designs/viterbi_decoder_axi4s/src/branch_distance.vhd +++ /dev/null @@ -1,111 +0,0 @@ ---! ---! Copyright (C) 2011 - 2014 Creonic GmbH ---! ---! This file is part of the Creonic Viterbi Decoder, which is distributed ---! under the terms of the GNU General Public License version 2. ---! ---! @file ---! @brief Branch distance calculation unit. ---! @author Markus Fehrenz ---! @date 2011/08/04 ---! ---! @details Each branch has to be calculated only once. ---! The branch calculations are configured with a generic. ---! There is no limitation in branch calculations. ---! - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library dec_viterbi; -use dec_viterbi.pkg_param.all; -use dec_viterbi.pkg_param_derived.all; -use dec_viterbi.pkg_types.all; - - -entity branch_distance is - generic( - EDGE_WEIGHT : in std_logic_vector(0 to NUMBER_PARITY_BITS - 1) - ); - port( - clk : in std_logic; - rst : in std_logic; - - -- - -- Input LLR values - -- - s_axis_input_tvalid : in std_logic; - s_axis_input_tdata : in t_input_block; - s_axis_input_tlast : in std_logic; - s_axis_input_tready : out std_logic; - - - -- - -- Output branch metrics, going to ACS unit. - -- - m_axis_output_tvalid : out std_logic; - m_axis_output_tdata : out std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); - m_axis_output_tlast : out std_logic; - m_axis_output_tready : in std_logic - ); -end entity branch_distance; - - -architecture rtl of branch_distance is - - signal m_axis_output_tvalid_int : std_logic; - signal s_axis_input_tready_int : std_logic; - -begin - - -- We are ready, when we are allowed to write to the output, or the output is idle. - s_axis_input_tready_int <= '1' when m_axis_output_tready = '1' else - '0'; - - -- Connect internal versions of signal to output port. - s_axis_input_tready <= s_axis_input_tready_int; - m_axis_output_tvalid <= m_axis_output_tvalid_int; - - - -- Calculation of specific branch distance with a geometric distance function. - pr_branch : process(clk) is - variable v_branch_result : integer; - begin - if rising_edge(clk) then - if rst = '1' then - m_axis_output_tvalid_int <= '0'; - m_axis_output_tdata <= (others => '0'); - m_axis_output_tlast <= '0'; - else - - if m_axis_output_tvalid_int = '1' and m_axis_output_tready = '1' then - m_axis_output_tvalid_int <= '0'; - m_axis_output_tlast <= '0'; - end if; - - if s_axis_input_tready_int = '1' and s_axis_input_tvalid = '1' then - v_branch_result := 0; - - for i in NUMBER_PARITY_BITS - 1 downto 0 loop - - -- - -- Either the value is added or subtracted, depending on - -- the current branch metric we are computing. - -- - if EDGE_WEIGHT(i) = '0' then - v_branch_result := v_branch_result + to_integer(s_axis_input_tdata(i)); - else - v_branch_result := v_branch_result - to_integer(s_axis_input_tdata(i)); - end if; - end loop; - m_axis_output_tdata <= std_logic_vector(to_signed(v_branch_result, BW_BRANCH_RESULT)); - m_axis_output_tvalid_int <= '1'; - m_axis_output_tlast <= s_axis_input_tlast; - end if; - - end if; - end if; - end process pr_branch; - -end architecture rtl; diff --git a/tests/designs/viterbi_decoder_axi4s/src/dec_viterbi.vhd b/tests/designs/viterbi_decoder_axi4s/src/dec_viterbi.vhd deleted file mode 100644 index 8ef32dca..00000000 --- a/tests/designs/viterbi_decoder_axi4s/src/dec_viterbi.vhd +++ /dev/null @@ -1,399 +0,0 @@ ---! ---! Copyright (C) 2011 - 2014 Creonic GmbH ---! ---! This file is part of the Creonic Viterbi Decoder, which is distributed ---! under the terms of the GNU General Public License version 2. ---! ---! @file ---! @brief Viterbi decoder top entity, connecting all decoder units. ---! @author Markus Fehrenz ---! @date 2011/12/05 ---! ---! @details ---! The AXI std_logic_vector input is mapped to an internal information type. ---! Further the correct output order is handled. ---! - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library dec_viterbi; -use dec_viterbi.pkg_param.all; -use dec_viterbi.pkg_param_derived.all; -use dec_viterbi.pkg_types.all; -use dec_viterbi.pkg_components.all; -use dec_viterbi.pkg_trellis.all; - - -entity dec_viterbi_ent is - port( - - -- - -- The core only uses AXI4-Stream interfaces, - -- based on AMBA4 AXI4-Stream Protocol with restrictions according to - -- Xilinx Migration, described in Xilinx AXI Reference UG761 (v13.3). - -- - - aclk : in std_logic; - - -- Synchronous reset, active low. - aresetn : in std_logic; - - - -- - -- Slave (input) data signals - -- Delivers the parity LLR values, one byte per LLR value. - -- - s_axis_input_tvalid : in std_logic; - s_axis_input_tdata : in std_logic_vector(31 downto 0); - s_axis_input_tlast : in std_logic; - s_axis_input_tready : out std_logic; - - - -- - -- Master (output) data signals - -- Delivers the decoded systematic (payload) bits. - -- - m_axis_output_tvalid : out std_logic; - m_axis_output_tdata : out std_logic; - m_axis_output_tlast : out std_logic; - m_axis_output_tready : in std_logic; - - - -- - -- Slave (input) configuration signals - -- Configures window length and acquisition length. - -- - s_axis_ctrl_tvalid : in std_logic; - s_axis_ctrl_tdata : in std_logic_vector(31 downto 0); - s_axis_ctrl_tlast : in std_logic; - s_axis_ctrl_tready : out std_logic -); -end entity dec_viterbi_ent; - - -architecture rtl of dec_viterbi_ent is - - alias clk is aclk; - signal rst : std_logic; - - -- split tdata into input array - signal input : t_input_block; - - -- buffer signals - signal buffer_tdata : std_logic_vector(31 downto 0); - signal buffer_tvalid : std_logic; - signal buffer_tlast : std_logic; - - -- branch signals - signal branch_tvalid : std_logic_vector(NUMBER_BRANCH_UNITS - 1 downto 0); - signal branch_tdata : t_branch; - signal branch_tlast : std_logic_vector(NUMBER_BRANCH_UNITS - 1 downto 0); - signal branch_tready : std_logic_vector(NUMBER_BRANCH_UNITS - 1 downto 0); - - -- acs signals - signal acs_tvalid : std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); - signal acs_tlast : std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); - signal acs_tready : std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); - signal acs_dec_tdata : std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); - signal acs_prob_tdata : t_node; - - -- ram signals - signal ram_tready : std_logic; - signal ram_tvalid, ram_tlast, ram_window_tuser, ram_last_tuser : std_logic_vector(1 downto 0); - signal ram_tdata : t_ram_rd_data; - - -- traceback signals - signal traceback_tvalid, traceback_tdata : std_logic_vector(1 downto 0); - signal traceback_tready, traceback_tlast : std_logic_vector(1 downto 0); - signal traceback_last_tuser : std_logic_vector(1 downto 0); - - -- reorder signals - signal reorder_tready, reorder_tvalid : std_logic_vector(1 downto 0); - signal reorder_tdata, reorder_tlast : std_logic_vector(1 downto 0); - signal reorder_last_tuser : std_logic_vector(1 downto 0); - - -- output signals - signal output_tready : std_logic_vector(1 downto 0); - signal current_active : integer range 1 downto 0; - -begin - - -- - -- There is always one byte of data for each LLR value, even though each - -- LLR value is represented with BW_LLR_INPUT bits. Hence, only - -- BW_LLR_INPUT bits are extracted from the byte. - -- - gen_input_assignment: for i in NUMBER_PARITY_BITS - 1 downto 0 generate - begin - input(i) <= signed(buffer_tdata(8 * i + BW_LLR_INPUT - 1 downto 8 * i)); - end generate gen_input_assignment; - - rst <= not aresetn; - - ------------------------------ - --- Portmapping components --- - ------------------------------ - - ------------------------------------- - -- AXI4S input buffer - -------------------------------------- - - inst_axi4s_buffer: axi4s_buffer - generic map( - DATA_WIDTH => 32 - ) - port map( - clk => clk, - rst => rst, - - input => s_axis_input_tdata, - input_valid => s_axis_input_tvalid, - input_last => s_axis_input_tlast, - input_accept => s_axis_input_tready, - - output => buffer_tdata, - output_valid => buffer_tvalid, - output_last => buffer_tlast, - output_accept => branch_tready(0) - ); - - ------------------------------------- - -- Branch metric unit - -------------------------------------- - - gen_branch_distance : for i in NUMBER_BRANCH_UNITS - 1 downto 0 generate - begin - inst_branch_distance : branch_distance - generic map( - EDGE_WEIGHT => std_logic_vector(to_unsigned(i, NUMBER_PARITY_BITS)) - ) - port map( - clk => clk, - rst => rst, - - s_axis_input_tvalid => buffer_tvalid, - s_axis_input_tdata => input, - s_axis_input_tlast => buffer_tlast, - s_axis_input_tready => branch_tready(i), - - m_axis_output_tvalid => branch_tvalid(i), - m_axis_output_tdata => branch_tdata(i), - m_axis_output_tlast => branch_tlast(i), - m_axis_output_tready => acs_tready(0) - ); - end generate gen_branch_distance; - - - ------------------------------------- - -- ACS unit (path metric calculation) - -------------------------------------- - - gen_acs : for i in 0 to NUMBER_TRELLIS_STATES - 1 generate - signal inbranch_tdata_low : std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); - signal inbranch_tdata_high : std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); - signal inprev_tdata_low : std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); - signal inprev_tdata_high : std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); - begin - inbranch_tdata_low <= branch_tdata(to_integer(unsigned(TRANSITIONS(i)(0)))); - inbranch_tdata_high <= branch_tdata(to_integer(unsigned(TRANSITIONS(i)(1)))); - inprev_tdata_low <= acs_prob_tdata(to_integer(unsigned(PREVIOUS_STATES(i)(0)))); - inprev_tdata_high <= acs_prob_tdata(to_integer(unsigned(PREVIOUS_STATES(i)(1)))); - - inst_acs : acs - generic map( - initialize_value => INITIALIZE_TRELLIS(i) - ) - port map( - clk => clk, - rst => rst, - - s_axis_inbranch_tvalid => branch_tvalid(0), - s_axis_inbranch_tdata_low => inbranch_tdata_low, - s_axis_inbranch_tdata_high => inbranch_tdata_high, - s_axis_inbranch_tlast => branch_tlast(0), - s_axis_inbranch_tready => acs_tready(i), - - s_axis_inprev_tvalid => '1', - s_axis_inprev_tdata_low => inprev_tdata_low, - s_axis_inprev_tdata_high => inprev_tdata_high, - s_axis_inprev_tready => open, - - m_axis_outprob_tvalid => open, - m_axis_outprob_tdata => acs_prob_tdata(i), - m_axis_outprob_tready => '1', - - m_axis_outdec_tvalid => acs_tvalid(i), - m_axis_outdec_tdata => acs_dec_tdata(i), - m_axis_outdec_tlast => acs_tlast(i), - m_axis_outdec_tready => ram_tready - ); - end generate gen_acs; - - - ------------------------------- - -- Traceback - ------------------------------- - - inst_ram_ctrl : ram_ctrl - port map ( - clk => clk, - rst => rst, - - s_axis_input_tvalid => acs_tvalid(0), - s_axis_input_tdata => acs_dec_tdata, - s_axis_input_tlast => acs_tlast(0), - s_axis_input_tready => ram_tready, - - m_axis_output_tvalid => ram_tvalid, - m_axis_output_tdata => ram_tdata, - m_axis_output_tlast => ram_tlast, - m_axis_output_tready => traceback_tready, - m_axis_output_window_tuser => ram_window_tuser, - m_axis_output_last_tuser => ram_last_tuser, - - s_axis_ctrl_tvalid => s_axis_ctrl_tvalid, - s_axis_ctrl_tdata => s_axis_ctrl_tdata, - s_axis_ctrl_tready => s_axis_ctrl_tready - ); - - - gen_inst_trellis_traceback : for i in 1 downto 0 generate - begin - inst_trellis_traceback : trellis_traceback - port map( - clk => clk, - rst => rst, - - s_axis_input_tvalid => ram_tvalid(i), - s_axis_input_tdata => ram_tdata(i), - s_axis_input_tlast => ram_tlast(i), - s_axis_input_tready => traceback_tready(i), - s_axis_input_window_tuser => ram_window_tuser(i), - s_axis_input_last_tuser => ram_last_tuser(i), - - m_axis_output_tvalid => traceback_tvalid(i), - m_axis_output_tdata => traceback_tdata(i), - m_axis_output_tlast => traceback_tlast(i), - m_axis_output_last_tuser => traceback_last_tuser(i), - m_axis_output_tready => reorder_tready(i) - ); - end generate gen_inst_trellis_traceback; - - - ------------------------------- - -- Reverse output order - ------------------------------- - - gen_inst_reorder : for i in 1 downto 0 generate - begin - inst_reorder : reorder - port map( - clk => clk, - rst => rst, - - s_axis_input_tvalid => traceback_tvalid(i), - s_axis_input_tdata => traceback_tdata(i), - s_axis_input_tlast => traceback_tlast(i), - s_axis_input_last_tuser => traceback_last_tuser(i), - s_axis_input_tready => reorder_tready(i), - - m_axis_output_tvalid => reorder_tvalid(i), - m_axis_output_tdata => reorder_tdata(i), - m_axis_output_tlast => reorder_tlast(i), - m_axis_output_last_tuser => reorder_last_tuser(i), - m_axis_output_tready => output_tready(i) - ); - end generate gen_inst_reorder; - - - ------------------------------ - -- Recursive codes handling -- - ------------------------------ - - gen_inst_recursion : if FEEDBACK_POLYNOMIAL /= 0 generate - signal reorder_recursion_tvalid : std_logic; - signal reorder_recursion_tdata : std_logic; - signal reorder_recursion_tlast : std_logic; - signal recursion_tready : std_logic; - begin - inst_recursion : recursionx - port map( - clk => clk, - rst => rst, - - s_axis_input_tvalid => reorder_recursion_tvalid, - s_axis_input_tdata => reorder_recursion_tdata, - s_axis_input_tlast => reorder_recursion_tlast, - s_axis_input_tready => recursion_tready, - - m_axis_output_tvalid => m_axis_output_tvalid, - m_axis_output_tdata => m_axis_output_tdata, - m_axis_output_tlast => m_axis_output_tlast, - m_axis_output_tready => m_axis_output_tready - ); - - ------------------------------- - -- Output interface handling - ------------------------------- - - reorder_recursion_tvalid <= '1' when reorder_tvalid(0) = '1' or reorder_tvalid(1) = '1' else - '0'; - - reorder_recursion_tdata <= reorder_tdata(0) when current_active = 0 else - reorder_tdata(1); - - reorder_recursion_tlast <= '1' when reorder_tlast(0) = '1' or reorder_tlast(1) = '1' else - '0'; - - output_tready(0) <= '1' when (current_active = 0) and m_axis_output_tready = '1' else - '0'; - output_tready(1) <= '1' when (current_active = 1) and m_axis_output_tready = '1' else - '0'; - end generate gen_inst_recursion; - - - - no_recursion: if FEEDBACK_POLYNOMIAL = 0 generate - - ------------------------------- - -- Output interface handling - ------------------------------- - - m_axis_output_tdata <= reorder_tdata(0) when current_active = 0 else - reorder_tdata(1); - - m_axis_output_tvalid <= '1' when reorder_tvalid(0) = '1' or reorder_tvalid(1) = '1' else - '0'; - - m_axis_output_tlast <= '1' when reorder_tlast(0) = '1' or reorder_tlast(1) = '1' else - '0'; - - output_tready(0) <= '1' when (current_active = 0) and m_axis_output_tready = '1' else - '0'; - output_tready(1) <= '1' when (current_active = 1) and m_axis_output_tready = '1' else - '0'; - end generate no_recursion; - - - recursion : if FEEDBACK_POLYNOMIAL /= 0 generate - begin - end generate recursion; - - - -- Check and merge reordering outputs and block if necessary. - pr_reorder_tready : process(clk) is - begin - if rising_edge(clk) then - if rst = '1' then - current_active <= 0; - else - if reorder_tvalid(current_active) = '1' and m_axis_output_tready = '1' and reorder_last_tuser(current_active) = '1' then - current_active <= 1 - current_active; - end if; - end if; - end if; - end process pr_reorder_tready; - -end architecture rtl; diff --git a/tests/designs/viterbi_decoder_axi4s/src/generic_sp_ram.vhd b/tests/designs/viterbi_decoder_axi4s/src/generic_sp_ram.vhd deleted file mode 100644 index 46eba7d3..00000000 --- a/tests/designs/viterbi_decoder_axi4s/src/generic_sp_ram.vhd +++ /dev/null @@ -1,90 +0,0 @@ ---! ---! Copyright (C) 2010 - 2012 Creonic GmbH ---! ---! This file is part of the Creonic Viterbi Decoder, which is distributed ---! under the terms of the GNU General Public License version 2. ---! ---! @file ---! @brief Generic single port RAM with a single read/write port ---! @author Matthias Alles ---! @date 2010/09/28 ---! - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library dec_viterbi; -use dec_viterbi.pkg_helper.all; - - -entity generic_sp_ram is - generic( - DISTR_RAM : boolean := false; - WORDS : integer := 8; - BITWIDTH : integer := 8 - ); - port( - clk : in std_logic; - rst : in std_logic; - - wen : in std_logic; - en : in std_logic; - - a : in std_logic_vector(no_bits_natural(WORDS - 1) - 1 downto 0); - d : in std_logic_vector(BITWIDTH - 1 downto 0); - q : out std_logic_vector(BITWIDTH - 1 downto 0) - ); -end generic_sp_ram; - - -architecture rtl of generic_sp_ram is - - type t_ram is array(WORDS - 1 downto 0) of - std_logic_vector(BITWIDTH - 1 downto 0); - signal sp_ram : t_ram := (others => (others => '0')); - - function get_ram_style_xilinx(dist_ram : in boolean) return string is - begin - if dist_ram then - return "pipe_distributed"; - else - return "block"; - end if; - end function; - - function get_ram_style_altera(dist_ram : in boolean) return string is - begin - if dist_ram then - return "MLAB, no_rw_check"; - else - return "AUTO"; - end if; - end function; - - attribute RAM_STYLE : string; - attribute RAM_STYLE of sp_ram : signal is get_ram_style_xilinx(DISTR_RAM); - - attribute ramstyle : string; - attribute ramstyle of sp_ram : signal is get_ram_style_altera(DISTR_RAM); - -begin - - -- - -- Do not register the address for reading, since the synthesis doesn't - -- recognize then that this is a single-port RAM. - -- - pr_sp_ram_rw: process(clk) - begin - if rising_edge(clk) then - if en = '1' then - if wen = '1' then - sp_ram(to_integer(unsigned(a))) <= d; - else - q <= sp_ram(to_integer(unsigned(a))); - end if; - end if; - end if; - end process pr_sp_ram_rw; - -end rtl; diff --git a/tests/designs/viterbi_decoder_axi4s/src/ram_ctrl.vhd b/tests/designs/viterbi_decoder_axi4s/src/ram_ctrl.vhd deleted file mode 100644 index 71273881..00000000 --- a/tests/designs/viterbi_decoder_axi4s/src/ram_ctrl.vhd +++ /dev/null @@ -1,474 +0,0 @@ ---! ---! Copyright (C) 2011 - 2014 Creonic GmbH ---! ---! This file is part of the Creonic Viterbi Decoder, which is distributed ---! under the terms of the GNU General Public License version 2. ---! ---! @file ---! @brief Viterbi decoder RAM control ---! @author Markus Fehrenz ---! @date 2011/12/13 ---! ---! @details Manage RAM behavior. Write and read data. ---! The decisions are sent to the traceback units ---! It is signaled if the data belongs to acquisition or window phase. ---! - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library dec_viterbi; -use dec_viterbi.pkg_param.all; -use dec_viterbi.pkg_param_derived.all; -use dec_viterbi.pkg_types.all; -use dec_viterbi.pkg_components.all; - - -entity ram_ctrl is - port( - clk : in std_logic; - rst : in std_logic; - - - -- - -- Slave data signals, delivers the LLR parity values. - -- - s_axis_input_tvalid : in std_logic; - s_axis_input_tdata : in std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); - s_axis_input_tlast : in std_logic; - s_axis_input_tready : out std_logic; - - - -- - -- Master data signals for traceback units, delivers the decision vectors. - -- - m_axis_output_tvalid : out std_logic_vector(1 downto 0); - m_axis_output_tdata : out t_ram_rd_data; - m_axis_output_tlast : out std_logic_vector(1 downto 0); - m_axis_output_tready : in std_logic_vector(1 downto 0); - - -- Signals the traceback unit when the decision bits do not belong to an acquisition. - m_axis_output_window_tuser : out std_logic_vector(1 downto 0); - - -- Signals whether this is the last decision vector of the window. - m_axis_output_last_tuser : out std_logic_vector(1 downto 0); - - - -- - -- Slave configuration signals, delivering the configuration data. - -- - - s_axis_ctrl_tvalid : in std_logic; - s_axis_ctrl_tdata : in std_logic_vector(31 downto 0); - s_axis_ctrl_tready : out std_logic -); -end entity ram_ctrl; - - -architecture rtl of ram_ctrl is - - ------------------------ - -- Type definition - ------------------------ - - -- - -- Record contains runtime configuration. - -- The input configuration is stored in a register. - -- It is received from a AXI4-Stream interface from the top entity. - -- - type trec_runtime_param is record - window_length : unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); - acquisition_length : unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); - end record trec_runtime_param; - - -- Types for finite state machines - type t_write_ram_fsm is (CONFIGURE, START, RUN, WAIT_FOR_TRACEBACK, WAIT_FOR_LAST_TRACEBACK); - type t_read_ram_fsm is (WAIT_FOR_WINDOW, TRACEBACK, WAIT_FOR_RAM, FINISH); - type t_read_ram_fsm_array is array (0 to 1) of t_read_ram_fsm; - - -- RAM controling types - type t_ram_data is array (3 downto 0) of std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); - type t_ram_addr is array (3 downto 0) of unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); - type t_ram_rd_addr is array (1 downto 0) of unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); - type t_ram_ptr is array (1 downto 0) of unsigned(1 downto 0); - type t_ram_ptr_int is array (1 downto 0) of integer range 3 downto 0; - - type t_ram_data_cnt is array (1 downto 0) of integer range 2 * MAX_WINDOW_LENGTH downto 0; - - - ------------------------ - -- Signal declaration - ------------------------ - - signal ram_buffer : t_ram_rd_data; - signal ram_buffer_full : std_logic_vector(1 downto 0); - - signal config : trec_runtime_param; - signal write_ram_fsm : t_write_ram_fsm; - signal read_ram_fsm : t_read_ram_fsm_array; - signal wen_ram : std_logic_vector(3 downto 0); - signal addr : t_ram_addr; - signal q_reg : t_ram_data; - - -- ram addess, number and data pointer - signal write_ram_ptr : unsigned(1 downto 0); - signal read_ram_ptr : t_ram_ptr; - signal read_ram_ptr_d : t_ram_ptr; - signal write_addr_ptr : unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); - signal read_addr_ptr : t_ram_rd_addr; - - -- internal signals of outputs - signal m_axis_output_tvalid_int : std_logic_vector(1 downto 0); - signal m_axis_output_tlast_int : std_logic_vector(1 downto 0); - signal m_axis_output_window_tuser_int : std_logic_vector(1 downto 0); - signal m_axis_output_last_tuser_int : std_logic_vector(1 downto 0); - signal s_axis_input_tready_int : std_logic; - signal s_axis_ctrl_tready_int : std_logic; - - signal next_traceback : std_logic_vector(1 downto 0); - signal write_window_complete : std_logic; - signal write_last_window_complete : std_logic; - signal last_of_block : std_logic; - signal read_last_addr_ptr : unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); -begin - - m_axis_output_tvalid <= m_axis_output_tvalid_int; - m_axis_output_tlast <= m_axis_output_tlast_int; - m_axis_output_window_tuser <= m_axis_output_window_tuser_int; - m_axis_output_last_tuser <= m_axis_output_last_tuser_int; - m_axis_output_tdata(0) <= q_reg(to_integer(read_ram_ptr_d(0))) when ram_buffer_full(0) = '0' else ram_buffer(0); - m_axis_output_tdata(1) <= q_reg(to_integer(read_ram_ptr_d(1))) when ram_buffer_full(1) = '0' else ram_buffer(1); - - - -- - -- When the output port is not ready to read the output of the RAM immediately - -- we have to remember the output value of the RAM in an extra register. - -- When the output is ready to read, we first use the ouput of the register - -- and only then the output of the RAM again. - -- - pr_buf_ram_output: process(clk) is - begin - if rising_edge(clk) then - if rst = '1' then - ram_buffer <= (others => (others => '0')); - ram_buffer_full <= (others => '0'); - else - - for i in 0 to 1 loop - if m_axis_output_tvalid_int(i) = '1' and m_axis_output_tready(i) = '0' and ram_buffer_full(i) = '0' then - ram_buffer(i) <= q_reg(to_integer(read_ram_ptr_d(i))); - ram_buffer_full(i) <= '1'; - end if; - - if m_axis_output_tvalid_int(i) = '1' and m_axis_output_tready(i) = '1' and ram_buffer_full(i) = '1' then - ram_buffer_full(i) <= '0'; - end if; - end loop; - - end if; - end if; - end process pr_buf_ram_output; - - ----------------------------- - -- Manage writing from ACS -- - ----------------------------- - s_axis_input_tready_int <= '0' when (write_ram_fsm = CONFIGURE) or - (write_ram_ptr = read_ram_ptr(0) and read_ram_fsm(0) /= WAIT_FOR_WINDOW) or - (write_ram_ptr = read_ram_ptr(1) and read_ram_fsm(1) /= WAIT_FOR_WINDOW) or - write_ram_fsm = WAIT_FOR_TRACEBACK or write_ram_fsm = WAIT_FOR_LAST_TRACEBACK else - '1'; - s_axis_input_tready <= s_axis_input_tready_int; - - s_axis_ctrl_tready_int <= '1' when (read_ram_fsm(0) = WAIT_FOR_WINDOW and read_ram_fsm(1) = WAIT_FOR_WINDOW and write_ram_fsm = CONFIGURE) else - '0'; - s_axis_ctrl_tready <= s_axis_ctrl_tready_int; - - -- Process for writing to the RAM - pr_write_ram: process(clk) is - variable v_window_length : unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); - variable v_acquisition_length : unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); - begin - if rising_edge(clk) then - if rst = '1' then - write_ram_fsm <= CONFIGURE; - write_addr_ptr <= (others => '0'); - write_ram_ptr <= (others => '0'); - wen_ram <= (others => '0'); - write_window_complete <= '0'; - write_last_window_complete <= '0'; - read_last_addr_ptr <= (others => '0'); - else - - case write_ram_fsm is - - -- - -- It is necessary to configure the decoder before each block - -- - when CONFIGURE => - write_window_complete <= '0'; - write_last_window_complete <= '0'; - if s_axis_ctrl_tvalid = '1' and s_axis_ctrl_tready_int = '1' then - v_window_length := unsigned(s_axis_ctrl_tdata(BW_MAX_WINDOW_LENGTH - 1 + 16 downto 16)); - v_acquisition_length := unsigned(s_axis_ctrl_tdata(BW_MAX_WINDOW_LENGTH - 1 downto 0)); - write_addr_ptr <= v_window_length - v_acquisition_length; - config.window_length <= v_window_length; - config.acquisition_length <= v_acquisition_length; - write_ram_fsm <= START; - - wen_ram(to_integer(write_ram_ptr)) <= '1'; - end if; - - - -- - -- After the decoder is configured, the decoder is waiting for a new block. - -- When the AXIS handshake is there the packet transmission begins. - -- The first write is a special case, since writing data starts at the acquisition length. - -- There is no complete window available afterwards. - -- - when START => - if s_axis_input_tvalid = '1' and s_axis_input_tready_int = '1' then - - if write_addr_ptr = config.window_length - 1 then - - -- When we switch to the next RAM, we reset the write addr. - write_addr_ptr <= (others => '0'); - - -- Switch to the next RAM. - write_ram_ptr <= write_ram_ptr + 1; - wen_ram(to_integer(write_ram_ptr)) <= '0'; - wen_ram(to_integer(write_ram_ptr + 1)) <= '1'; - - write_ram_fsm <= RUN; - else - write_addr_ptr <= write_addr_ptr + 1; - end if; - end if; - - - -- - -- The decoder is receiving data from the ACS. - -- - when RUN => - write_window_complete <= '0'; - write_last_window_complete <= '0'; - - if s_axis_input_tvalid = '1' and s_axis_input_tready_int = '1' then - write_addr_ptr <= write_addr_ptr + 1; - - if write_addr_ptr = config.window_length - 1 then - - -- When we switch to the next RAM, we reset the write addr. - write_addr_ptr <= (others => '0'); - - -- Switch to the next RAM. - write_ram_ptr <= write_ram_ptr + 1; - wen_ram(to_integer(write_ram_ptr)) <= '0'; - wen_ram(to_integer(write_ram_ptr + 1)) <= '1'; - - -- Indicate, that a complete window is within the RAM and traceback may start. - write_window_complete <= '1'; - - if read_ram_fsm(0) /= WAIT_FOR_WINDOW and read_ram_fsm(1) /= WAIT_FOR_WINDOW then - write_ram_fsm <= WAIT_FOR_TRACEBACK; - end if; - - else - write_addr_ptr <= write_addr_ptr + 1; - end if; - - if s_axis_input_tlast = '1' then - write_ram_fsm <= CONFIGURE; - wen_ram <= (others => '0'); - - write_last_window_complete <= '1'; - if (read_ram_fsm(0) /= WAIT_FOR_WINDOW and read_ram_fsm(1) /= WAIT_FOR_WINDOW) or write_window_complete = '1' then - write_ram_fsm <= WAIT_FOR_LAST_TRACEBACK; - end if; - read_last_addr_ptr <= write_addr_ptr; - - write_addr_ptr <= (others => '0'); - write_ram_ptr <= write_ram_ptr + 1; - end if; - end if; - - when WAIT_FOR_TRACEBACK => - if read_ram_fsm(0) = WAIT_FOR_WINDOW or read_ram_fsm(1) = WAIT_FOR_WINDOW then - write_ram_fsm <= RUN; - write_window_complete <= '0'; - end if; - - when WAIT_FOR_LAST_TRACEBACK => - if read_ram_fsm(0) = WAIT_FOR_WINDOW or read_ram_fsm(1) = WAIT_FOR_WINDOW then - write_ram_fsm <= CONFIGURE; - write_last_window_complete <= '0'; - end if; - - end case; - end if; - end if; - end process pr_write_ram; - - - ------------------------------------------- - -- Manage reading from RAM for traceback -- - ------------------------------------------- - - gen_read_ram: for i in 0 to 1 generate - pr_read_ram: process(clk) is - begin - if rising_edge(clk) then - if rst = '1' then - read_addr_ptr(i) <= (others => '0'); - read_ram_fsm(i) <= WAIT_FOR_WINDOW; - m_axis_output_tvalid_int(i) <= '0'; - m_axis_output_tlast_int(i) <= '0'; - m_axis_output_window_tuser_int(i) <= '0'; - m_axis_output_last_tuser_int(i) <= '0'; - read_ram_ptr(i) <= (others => '0'); - read_ram_ptr_d(i) <= (others => '0'); - else - - read_ram_ptr_d(i) <= read_ram_ptr(i); - case read_ram_fsm(i) is - - -- Wait for the next window to be ready within the RAM. - when WAIT_FOR_WINDOW => - read_addr_ptr(i) <= config.window_length - 1; - m_axis_output_tlast_int(i) <= '0'; - m_axis_output_tvalid_int(i) <= '0'; - m_axis_output_last_tuser_int(i) <= '0'; - m_axis_output_window_tuser_int(i) <= '0'; - read_ram_ptr(i) <= write_ram_ptr; - - -- We always start from the RAM, which was written last. - if write_window_complete = '1' and next_traceback(i) = '1' then - read_ram_ptr(i) <= write_ram_ptr - 1; - read_addr_ptr(i) <= read_addr_ptr(i) - 1; - read_ram_fsm(i) <= TRACEBACK; - m_axis_output_tvalid_int(i) <= '1'; - end if; - if write_last_window_complete = '1' and next_traceback(i) = '1' then - read_ram_ptr(i) <= write_ram_ptr - 1; - read_addr_ptr(i) <= read_last_addr_ptr; - read_ram_fsm(i) <= TRACEBACK; - m_axis_output_window_tuser_int(i) <= '1'; - end if; - - -- Perform the Traceback on the RAM data of the first RAM we need for acquisition and traceback. - when TRACEBACK => - m_axis_output_tlast_int(i) <= '0'; - m_axis_output_last_tuser_int(i) <= '0'; - m_axis_output_tvalid_int(i) <= '1'; - - if m_axis_output_tready(i) = '1' then - if read_addr_ptr(i) = 0 then - if read_ram_fsm(1 - i) = TRACEBACK and read_ram_ptr(1 - i) = read_ram_ptr(i) - 1 then - read_ram_fsm(i) <= WAIT_FOR_RAM; - else - read_addr_ptr(i) <= config.window_length - 1; - read_ram_ptr(i) <= read_ram_ptr(i) - 1; - read_ram_fsm(i) <= FINISH; - end if; - else - read_addr_ptr(i) <= read_addr_ptr(i) - 1; - end if; - - -- Signal the traceback unit, acquisition is over. - if read_addr_ptr(i) = config.window_length - config.acquisition_length - 1 then - m_axis_output_window_tuser_int(i) <= '1'; - end if; - end if; - - when WAIT_FOR_RAM => - m_axis_output_tvalid_int(i) <= '0'; - if read_ram_fsm(1 - i) /= TRACEBACK or read_ram_ptr(1 - i) /= read_ram_ptr(i) - 1 then - read_addr_ptr(i) <= config.window_length - 1; - read_ram_ptr(i) <= read_ram_ptr(i) - 1; - read_ram_fsm(i) <= FINISH; - end if; - - -- Get the remaining values from the second RAM we need for traceback (no acquisition values in this RAM) - when FINISH => - if m_axis_output_tvalid_int(i) <= '0' then - m_axis_output_tvalid_int(i) <= '1'; - read_addr_ptr(i) <= read_addr_ptr(i) - 1; - end if; - if m_axis_output_tready(i) = '1' then - - if read_addr_ptr(i) = config.window_length - config.acquisition_length then - m_axis_output_last_tuser_int(i) <= '1'; - read_addr_ptr(i) <= config.window_length - 1; - read_ram_fsm(i) <= WAIT_FOR_WINDOW; - - -- Check if the other read process finished processing. - if read_ram_fsm((i+1) mod 2) = WAIT_FOR_WINDOW and last_of_block = '1' then - m_axis_output_tlast_int(i) <= '1'; - end if; - - else - read_addr_ptr(i) <= read_addr_ptr(i) - 1; - end if; - end if; - end case; - end if; - end if; - end process pr_read_ram; - end generate gen_read_ram; - - -- This process decides which traceback unit is the next one to use. - pr_next_traceback: process(clk) is - begin - if rising_edge(clk) then - if rst = '1' then - next_traceback <= "01"; - last_of_block <= '0'; - else - if write_window_complete = '1' then - if next_traceback(0) = '1' then - next_traceback(0) <= '0'; - next_traceback(1) <= '1'; - else - next_traceback(0) <= '1'; - next_traceback(1) <= '0'; - end if; - end if; - - if s_axis_input_tlast = '1' then - last_of_block <= '1'; - end if; - - end if; - end if; - end process pr_next_traceback; - - ------------------------------ - --- Portmapping components --- - ------------------------------ - - gen_generic_sp_ram : for i in 0 to 3 generate - begin - - addr(i) <= write_addr_ptr when (write_ram_fsm = RUN or write_ram_fsm = START) and to_integer(write_ram_ptr) = i else - read_addr_ptr(0) when (to_integer(read_ram_ptr(0)) = i and (read_ram_fsm(0) = TRACEBACK or read_ram_fsm(0) = WAIT_FOR_RAM or read_ram_fsm(0) = FINISH)) or - (next_traceback(0) = '1' and write_window_complete = '1' and to_integer(read_ram_ptr(0)) = i) else - read_addr_ptr(1); - - inst_generic_sp_ram : generic_sp_ram - generic map( - DISTR_RAM => DISTRIBUTED_RAM, - WORDS => MAX_WINDOW_LENGTH, - BITWIDTH => NUMBER_TRELLIS_STATES - ) - port map( - clk => clk, - rst => rst, - wen => wen_ram(i), - en => '1', - a => std_logic_vector(addr(i)), - d => s_axis_input_tdata, - q => q_reg(i) - ); - end generate gen_generic_sp_ram; - -end architecture rtl; diff --git a/tests/designs/viterbi_decoder_axi4s/src/recursion.vhd b/tests/designs/viterbi_decoder_axi4s/src/recursion.vhd deleted file mode 100644 index f6c99856..00000000 --- a/tests/designs/viterbi_decoder_axi4s/src/recursion.vhd +++ /dev/null @@ -1,96 +0,0 @@ ---! ---! Copyright (C) 2011 - 2014 Creonic GmbH ---! ---! This file is part of the Creonic Viterbi Decoder, which is distributed ---! under the terms of the GNU General Public License version 2. ---! ---! @file ---! @brief Recursion unit for recursive code. ---! @author Markus Fehrenz ---! @date 2011/01/12 ---! ---! @details The recusion handling buffers the reorder ouput and ---! calculates the correct output depending on the feedback polynomial. ---! - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library dec_viterbi; -use dec_viterbi.pkg_param.all; -use dec_viterbi.pkg_param_derived.all; - -entity recursionx is - port( - clk : in std_logic; - rst : in std_logic; - - -- - -- Decoded bits input from the reordering units in std_logic - -- - s_axis_input_tvalid : in std_logic; - s_axis_input_tdata : in std_logic; - s_axis_input_tlast : in std_logic; - s_axis_input_tready : out std_logic; - - -- - -- Output decoded bits convolved with the feedback polynomial - -- - m_axis_output_tvalid : out std_logic; - m_axis_output_tdata : out std_logic; - m_axis_output_tlast : out std_logic; - m_axis_output_tready : in std_logic - ); -end entity recursionx; - -architecture rtl of recursionx is - signal recursion_sreg : unsigned(ENCODER_MEMORY_DEPTH downto 0); - signal s_axis_input_tready_int : std_logic; - signal m_axis_output_tvalid_int : std_logic; - -begin - s_axis_input_tready_int <= '1' when m_axis_output_tready = '1' or m_axis_output_tvalid_int = '0' else - '0'; - - s_axis_input_tready <= s_axis_input_tready_int; - m_axis_output_tvalid <= m_axis_output_tvalid_int; - - -- Use the feedback polynomial to convolve the global path. - pr_recursion : process(clk) is - variable v_bit : std_logic := '0'; - variable v_recursion_state : unsigned(ENCODER_MEMORY_DEPTH downto 0); - begin - if rising_edge(clk) then - if rst = '1' then - recursion_sreg <= (others => '0'); - m_axis_output_tdata <= '0'; - m_axis_output_tlast <= '0'; - else - m_axis_output_tvalid_int <= s_axis_input_tvalid; - - if s_axis_input_tvalid = '1' and s_axis_input_tready_int = '1' then - - -- move current decoded output bits into shift register and reset if last flag is valid - if s_axis_input_tlast = '1' then - recursion_sreg <= (others => '0'); - else - recursion_sreg <= s_axis_input_tdata & recursion_sreg(ENCODER_MEMORY_DEPTH downto 1); - end if; - - -- convolve with feedback polynomial with the output register. - v_bit := '0'; - v_recursion_state := (s_axis_input_tdata & recursion_sreg(ENCODER_MEMORY_DEPTH downto 1)) and - ('1' & to_unsigned(FEEDBACK_POLYNOMIAL, ENCODER_MEMORY_DEPTH)); - for i in ENCODER_MEMORY_DEPTH downto 0 loop - v_bit := v_bit xor v_recursion_state(i); - end loop; - m_axis_output_tdata <= v_bit; - - m_axis_output_tlast <= s_axis_input_tlast; - end if; - end if; - end if; - end process pr_recursion; - -end architecture rtl; diff --git a/tests/designs/viterbi_decoder_axi4s/src/reorder.vhd b/tests/designs/viterbi_decoder_axi4s/src/reorder.vhd deleted file mode 100644 index c2906909..00000000 --- a/tests/designs/viterbi_decoder_axi4s/src/reorder.vhd +++ /dev/null @@ -1,129 +0,0 @@ ---! ---! Copyright (C) 2011 - 2014 Creonic GmbH ---! ---! This file is part of the Creonic Viterbi Decoder, which is distributed ---! under the terms of the GNU General Public License version 2. ---! ---! @file ---! @brief Reorder twisted output due to windowing ---! @author Markus Fehrenz ---! @date 2011/05/12 ---! ---! @details The windowing output is twisted. ---! The correct order is simply rebuilt by reversing ---! the output of each traceback unit. ---! - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library dec_viterbi; -use dec_viterbi.pkg_param.all; -use dec_viterbi.pkg_param_derived.all; -use dec_viterbi.pkg_types.all; - - -entity reorder is - port( - clk : in std_logic; - rst : in std_logic; - - -- - -- Traceback unit output, twisted order - -- - s_axis_input_tvalid : in std_logic; - s_axis_input_tdata : in std_logic; - s_axis_input_tlast : in std_logic; - s_axis_input_last_tuser : in std_logic; - s_axis_input_tready : out std_logic; - - -- - -- Viterbi decoder output, original (input) order. - -- - m_axis_output_tvalid : out std_logic; - m_axis_output_tdata : out std_logic; - m_axis_output_tlast : out std_logic; - m_axis_output_last_tuser : out std_logic; -- Last bit of one traceback window - m_axis_output_tready : in std_logic - ); -end entity reorder; - - -architecture rtl of reorder is - - -- used to store one reversed output of a traceback unit - signal buffer_sreg : unsigned(MAX_WINDOW_LENGTH - 1 downto 0); - signal buffer_cnt : unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); - signal buffer_end : integer range ENCODER_MEMORY_DEPTH downto 0; - signal send_output, last_window : boolean; - - signal s_axis_input_tready_int : std_logic; - -begin - - s_axis_input_tready <= s_axis_input_tready_int; - s_axis_input_tready_int <= '1' when not(send_output) else - '0'; - --- m_axis_output_tvalid <= '1' when send_output and m_axis_output_tready= '1' else - m_axis_output_tvalid <= '1' when send_output else - '0'; - m_axis_output_tdata <= buffer_sreg(0); - - m_axis_output_tlast <= '1' when buffer_cnt = ENCODER_MEMORY_DEPTH and last_window else - '0'; - - -- Reorder the global path given from an traceback unit with the help of a shift register. - pr_reorder : process(clk) is - begin - if rising_edge(clk) then - if rst = '1' then - buffer_sreg <= (others => '0'); - buffer_cnt <= (others => '0'); - send_output <= false; - last_window <= false; - buffer_end <= 0; - m_axis_output_last_tuser <= '0'; - else - - -- store output of traceback unit - if s_axis_input_tvalid = '1' and s_axis_input_tready_int = '1' then - if s_axis_input_tlast = '1' then - last_window <= true; - buffer_end <= ENCODER_MEMORY_DEPTH; - end if; - if s_axis_input_last_tuser = '1' then - send_output <= true; - buffer_sreg <= buffer_sreg(MAX_WINDOW_LENGTH - 2 downto 0) & s_axis_input_tdata; - else - buffer_sreg <= buffer_sreg(MAX_WINDOW_LENGTH - 2 downto 0) & s_axis_input_tdata; - buffer_cnt <= buffer_cnt + 1; - end if; - end if; - - -- send reordered data to the output - if m_axis_output_tready = '1' and send_output then - buffer_sreg <= '0' & buffer_sreg(MAX_WINDOW_LENGTH - 1 downto 1); - - -- Next transfer will be the last one of this window. - if buffer_cnt = 1 then - m_axis_output_last_tuser <= '1'; - end if; - - -- This was the last data transfer. Tailbits are cut off - if buffer_cnt = buffer_end then - send_output <= false; - last_window <= false; - buffer_end <= 0; - buffer_cnt <= (others => '0'); - m_axis_output_last_tuser <= '0'; - else - buffer_cnt <= buffer_cnt - 1; - end if; - end if; - end if; - end if; - end process pr_reorder; - -end architecture rtl; diff --git a/tests/designs/viterbi_decoder_axi4s/src/traceback.vhd b/tests/designs/viterbi_decoder_axi4s/src/traceback.vhd deleted file mode 100644 index ef443098..00000000 --- a/tests/designs/viterbi_decoder_axi4s/src/traceback.vhd +++ /dev/null @@ -1,101 +0,0 @@ ---! ---! Copyright (C) 2011 - 2014 Creonic GmbH ---! ---! This file is part of the Creonic Viterbi Decoder, which is distributed ---! under the terms of the GNU General Public License version 2. ---! ---! @file ---! @brief Traceback unit for a viterbi decoder ---! @author Markus Fehrenz ---! @date 2011/07/11 ---! ---! @details The traceback unit only processes a data stream. ---! There is no knowledge about the decoder configuration. ---! The information about acquisition and window lengths is received from ram control. ---! - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library dec_viterbi; -use dec_viterbi.pkg_param.all; -use dec_viterbi.pkg_param_derived.all; - - -entity trellis_traceback is - port( - -- general signals - clk : in std_logic; - rst : in std_logic; - - s_axis_input_tvalid : in std_logic; - s_axis_input_tdata : in std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); - s_axis_input_tlast : in std_logic; - s_axis_input_tready : out std_logic; - s_axis_input_window_tuser : in std_logic; - s_axis_input_last_tuser : in std_logic; - - m_axis_output_tvalid : out std_logic; - m_axis_output_tdata : out std_logic; - m_axis_output_tlast : out std_logic; - m_axis_output_last_tuser : out std_logic; - m_axis_output_tready : in std_logic - ); -end entity trellis_traceback; - - -architecture rtl of trellis_traceback is - - signal current_node : unsigned(BW_TRELLIS_STATES - 1 downto 0); - signal m_axis_output_tvalid_int : std_logic; - signal s_axis_input_tready_int : std_logic; - -begin - s_axis_input_tready_int <= '1' when m_axis_output_tready = '1' or m_axis_output_tvalid_int = '0' else - '0'; - s_axis_input_tready <= s_axis_input_tready_int; - - m_axis_output_tvalid <= m_axis_output_tvalid_int; - - - -- Traceback the ACS local path decisions and output the resulting global path. - pr_traceback : process(clk) is - begin - if rising_edge(clk) then - if rst = '1' then - m_axis_output_tvalid_int <= '0'; - m_axis_output_tdata <= '0'; - m_axis_output_tlast <= '0'; - m_axis_output_last_tuser <= '0'; - current_node <= (others => '0'); - else - - if m_axis_output_tready = '1' then - m_axis_output_tvalid_int <= '0'; - end if; - - -- calculate the decoded bit with an shift register - if s_axis_input_tvalid = '1' and s_axis_input_tready_int = '1' then - - m_axis_output_tlast <= s_axis_input_tlast; - m_axis_output_last_tuser <= s_axis_input_last_tuser; - - -- handle tvalid output signal - if s_axis_input_window_tuser = '1' then - m_axis_output_tvalid_int <= '1'; - m_axis_output_tdata <= current_node(BW_TRELLIS_STATES - 1); - end if; - - -- last value of current window? - if s_axis_input_last_tuser = '1' then - current_node <= to_unsigned(0, BW_TRELLIS_STATES); - else - current_node <= current_node(BW_TRELLIS_STATES - 2 downto 0) - & s_axis_input_tdata(to_integer(current_node(BW_TRELLIS_STATES - 1 downto 0))); - end if; - end if; - end if; - end if; - end process pr_traceback; -end architecture rtl; diff --git a/tests/pytest/test_binary_value.py b/tests/pytest/test_binary_value.py deleted file mode 100644 index 0a6556af..00000000 --- a/tests/pytest/test_binary_value.py +++ /dev/null @@ -1,319 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -import pytest - -from cocotb.binary import BinaryValue, BinaryRepresentation - -TRUNCATION_MATCH = r"\d+-bit value requested, truncating value" - - -def test_init_big_endian_twos_comp(): - bin1 = BinaryValue(value=-1, n_bits=2, bigEndian=True, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) - assert bin1._str == "11" - assert bin1.binstr == "11" - assert bin1.integer == -1 - - -def test_init_little_endian_unsigned(): - bin1 = BinaryValue(value=3, n_bits=3, bigEndian=False, binaryRepresentation=BinaryRepresentation.UNSIGNED) - assert bin1._str == "011" - assert bin1.binstr == "011" - assert bin1.integer == 3 - - bin2 = BinaryValue(value=5, n_bits=5, bigEndian=False, binaryRepresentation=BinaryRepresentation.UNSIGNED) - assert bin2._str == "00101" - assert bin2.binstr == "00101" - assert bin2.integer == 5 - - bin3 = BinaryValue(value=12, n_bits=8, bigEndian=False, binaryRepresentation=BinaryRepresentation.UNSIGNED) - assert bin3._str == "00001100" - assert bin3.binstr == "00001100" - assert bin3.integer == 12 - - bin4 = BinaryValue(value="010110", bigEndian=False, binaryRepresentation=BinaryRepresentation.UNSIGNED) - assert bin4._str == "010110" - assert bin4.binstr == "010110" - assert bin4.integer == 22 - - bin5 = BinaryValue(value="1001011", bigEndian=False, binaryRepresentation=BinaryRepresentation.UNSIGNED) - assert bin5._str == "1001011" - assert bin5.binstr == "1001011" - assert bin5.integer == 75 - - bin6 = BinaryValue(value="11111111111111111111110000101100", n_bits=32, - bigEndian=False, binaryRepresentation=BinaryRepresentation.UNSIGNED) - assert bin6._str == "11111111111111111111110000101100" - assert bin6.binstr == "11111111111111111111110000101100" - assert bin6.signed_integer == -980 - assert bin6.integer == 4294966316 - - -def test_init_little_endian_signed(): - bin1 = BinaryValue(value=3, n_bits=3, bigEndian=False, binaryRepresentation=BinaryRepresentation.SIGNED_MAGNITUDE) - assert bin1._str == "011" - assert bin1.binstr == "011" - assert bin1.integer == 3 - - bin2 = BinaryValue(value=-1, n_bits=2, bigEndian=False, binaryRepresentation=BinaryRepresentation.SIGNED_MAGNITUDE) - assert bin2._str == "11" - assert bin2.binstr == "11" - assert bin2.integer == -1 - - bin3 = BinaryValue(value="1001011", bigEndian=False, binaryRepresentation=BinaryRepresentation.SIGNED_MAGNITUDE) - assert bin3._str == "1001011" - assert bin3.binstr == "1001011" - assert bin3.integer == -11 - - -def test_init_little_endian_twos_comp(): - bin1 = BinaryValue(value=3, n_bits=4, bigEndian=False, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) - assert bin1._str == "0011" - assert bin1.binstr == "0011" - assert bin1.integer == 3 - - bin2 = BinaryValue(value=-1, n_bits=2, bigEndian=False, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) - assert bin2._str == "11" - assert bin2.binstr == "11" - assert bin2.integer == -1 - - bin3 = BinaryValue(value=-65, n_bits=8, bigEndian=False, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) - assert bin3._str == "10111111" - assert bin3.binstr == "10111111" - assert bin3.integer == -65 - - bin4 = BinaryValue(value=1, n_bits=2, bigEndian=False, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) - assert bin4._str == "01" - assert bin4.binstr == "01" - assert bin4.integer == 1 - - bin5 = BinaryValue(value="1001011", bigEndian=False, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) - assert bin5._str == "1001011" - assert bin5.binstr == "1001011" - assert bin5.integer == -53 - - temp_bin = BinaryValue(value="11111111111111111111110000101100", bigEndian=False, - binaryRepresentation=BinaryRepresentation.UNSIGNED) - - # Illegal to construct from another BinaryValue (used to silently fail) - with pytest.raises(TypeError): - BinaryValue(value=temp_bin, n_bits=32, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) - - bin7 = BinaryValue(value=temp_bin.binstr, n_bits=32, - bigEndian=False, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) - assert bin7._str == "11111111111111111111110000101100" - assert bin7.binstr == "11111111111111111111110000101100" - assert bin7.get_value_signed() == -980 - assert bin7.integer == -980 - - -def test_init_unsigned_negative_value(): - with pytest.raises(ValueError): - BinaryValue(value=-8, n_bits=5, bigEndian=True, binaryRepresentation=BinaryRepresentation.UNSIGNED) - pytest.fail("Expected ValueError when assigning negative number to unsigned BinaryValue") - - -def test_init_not_enough_bits(): - with pytest.warns(RuntimeWarning, match=TRUNCATION_MATCH): - bin1_unsigned = BinaryValue(value=128, n_bits=7, bigEndian=True, - binaryRepresentation=BinaryRepresentation.UNSIGNED) - assert bin1_unsigned._str == "0000000" - assert bin1_unsigned.binstr == "0000000" - assert bin1_unsigned.integer == 0 - - with pytest.warns(RuntimeWarning, match=TRUNCATION_MATCH): - bin1_sigmag = BinaryValue(value=128, n_bits=7, bigEndian=True, - binaryRepresentation=BinaryRepresentation.SIGNED_MAGNITUDE) - assert bin1_sigmag._str == "0000000" - assert bin1_sigmag.binstr == "0000000" - assert bin1_sigmag.integer == 0 - - with pytest.warns(RuntimeWarning, match=TRUNCATION_MATCH): - bin1_twoscomp = BinaryValue(value=128, n_bits=7, bigEndian=True, - binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) - assert bin1_twoscomp._str == "0000000" - assert bin1_twoscomp.binstr == "0000000" - assert bin1_twoscomp.integer == 0 - - with pytest.warns(RuntimeWarning, match=TRUNCATION_MATCH): - bin1_binstr = BinaryValue(value="110000000", n_bits=7, bigEndian=True) - assert bin1_binstr._str == "0000000" - assert bin1_binstr.binstr == "0000000" - assert bin1_binstr.integer == 0 - - with pytest.warns(RuntimeWarning, match=TRUNCATION_MATCH): - bin2 = BinaryValue(value="1111110000101100", n_bits=12, bigEndian=False, - binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) - assert bin2._str == "110000101100" - assert bin2.binstr == "110000101100" - assert bin2.integer == -980 - - with pytest.warns(RuntimeWarning, match=TRUNCATION_MATCH): - bin3 = BinaryValue(value="1111110000101100", n_bits=11, bigEndian=False, - binaryRepresentation=BinaryRepresentation.SIGNED_MAGNITUDE) - assert bin3._str == "10000101100" - assert bin3.binstr == "10000101100" - assert bin3.integer == -44 - - -def test_init_short_binstr_value(): - bin1 = BinaryValue(value="0", n_bits=4, bigEndian=True, binaryRepresentation=BinaryRepresentation.UNSIGNED) - assert bin1._str == "0000" - assert bin1.binstr == "0000" - assert bin1.integer == 0 - - bin2 = BinaryValue(value="Z", n_bits=8, bigEndian=False, binaryRepresentation=BinaryRepresentation.UNSIGNED) - assert bin2._str == "0000000Z" - assert bin2.binstr == "0000000Z" - with pytest.raises(ValueError): - bin2.integer - pytest.fail("Expected ValueError when resolving Z to integer") - - bin3 = BinaryValue(value="01", n_bits=8, - bigEndian=False, binaryRepresentation=BinaryRepresentation.SIGNED_MAGNITUDE) - assert bin3._str == "00000001" - assert bin3.binstr == "00000001" - assert bin3.integer == 1 - - bin4 = BinaryValue(value="1", n_bits=8, - bigEndian=True, binaryRepresentation=BinaryRepresentation.SIGNED_MAGNITUDE) - # 1 digit is too small for Signed Magnitude representation, so setting binstr will fail, falling back to buff - bin4._str == "10000000" - bin4.binstr == "10000000" - bin4.integer == 1 - - -def test_defaults(): - bin1 = BinaryValue(17) - assert bin1.binaryRepresentation == BinaryRepresentation.UNSIGNED - assert bin1.big_endian is True - assert bin1._n_bits is None - assert bin1.integer == 17 - - -def test_index(): - bin1 = BinaryValue(value=-980, n_bits=32, - bigEndian=False, binaryRepresentation=BinaryRepresentation.TWOS_COMPLEMENT) - - bin2 = bin1[3:2] - assert bin2.binstr == "11" - assert bin2.integer == -1 - - with pytest.raises(IndexError): - bin1[32] - - with pytest.raises(IndexError): - bin1[1:2] - - with pytest.raises(IndexError): - bin1[-1:4] - - with pytest.raises(IndexError): - bin1[2:-2] - - bin3 = BinaryValue(value=154, n_bits=14, - bigEndian=True, binaryRepresentation=BinaryRepresentation.UNSIGNED) - - with pytest.raises(IndexError): - bin3[14] - - with pytest.raises(IndexError): - bin3[2:1] - - with pytest.raises(IndexError): - bin3[-1:4] - - with pytest.raises(IndexError): - bin3[2:-2] - - -def test_general(): - """ - Test out the cocotb supplied BinaryValue class for manipulating - values in a style familiar to rtl coders. - """ - - vec = BinaryValue(value=0, n_bits=16) - assert vec.n_bits == 16 - assert vec.big_endian is True - assert vec.integer == 0 - - # Checking single index assignment works as expected on a Little Endian BinaryValue - vec = BinaryValue(value=0, n_bits=16, bigEndian=False) - assert vec.big_endian is False - for idx in range(vec.n_bits): - vec[idx] = '1' - expected_value = 2**(idx+1) - 1 - assert vec.integer == expected_value - assert vec[idx] == 1 - - # Checking slice assignment works as expected on a Little Endian BinaryValue - assert vec.integer == 65535 - vec[7:0] = '00110101' - assert vec.binstr == '1111111100110101' - assert vec[7:0].binstr == '00110101' - - -def test_backwards_compatibility(): - """ - Test backwards-compatibility wrappers for BinaryValue - """ - - # bits is deprecated in favor of n_bits - with pytest.deprecated_call(): - vec = BinaryValue(value=0, bits=16) - assert vec.n_bits == 16 - - vec = BinaryValue(0, 16) - assert vec.n_bits == 16 - - with pytest.raises(TypeError): - BinaryValue(value=0, bits=16, n_bits=17) - - -def test_buff_big_endian(): - orig_str = "0110"+"1100"+"1001" - orig_bytes = b'\x06\xC9' # padding is the high bits of the first byte - - v = BinaryValue(value=orig_str, n_bits=12, bigEndian=True) - assert v.buff == orig_bytes - - with pytest.warns(RuntimeWarning, match=TRUNCATION_MATCH): - # the binstr is truncated, but its value should be unchanged - v.buff = orig_bytes - assert v.buff == orig_bytes - assert v.binstr == orig_str - - with pytest.warns(RuntimeWarning, match=TRUNCATION_MATCH): - # extra bits are stripped because they don't fit into the 12 bits - v.buff = b'\xF6\xC9' - assert v.buff == orig_bytes - assert v.binstr == orig_str - - -def test_buff_little_endian(): - orig_str = "0110"+"1100"+"1001" - orig_bytes = b'\xC9\x06' # padding is the high bits of the last byte - - v = BinaryValue(value=orig_str, n_bits=12, bigEndian=False) - assert v.buff == orig_bytes - - with pytest.warns(RuntimeWarning, match=TRUNCATION_MATCH): - # the binstr is truncated, but its value should be unchanged - v.buff = orig_bytes - assert v.buff == orig_bytes - assert v.binstr == orig_str - - with pytest.warns(RuntimeWarning, match=TRUNCATION_MATCH): - # extra bits are stripped because they don't fit into the 12 bits - v.buff = b'\xC9\xF6' - assert v.buff == orig_bytes - assert v.binstr == orig_str - - -def test_bad_binstr(): - with pytest.raises(ValueError, match=r'Attempting to assign character 4 to a BinaryValue'): - BinaryValue(value="01XZ4") - - with pytest.raises(ValueError, match=r'Attempting to assign character % to a BinaryValue'): - BinaryValue(value="Uu%") diff --git a/tests/pytest/test_utils.py b/tests/pytest/test_utils.py deleted file mode 100644 index 66bf1a74..00000000 --- a/tests/pytest/test_utils.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -import pytest - -import cocotb.utils - -import ctypes - - -class TestHexDump: - def test_int_illegal(dut): - # this used to be legal, but deliberately is no longer - with pytest.raises(TypeError): - cocotb.utils.hexdump(1) - - def test_str_deprecated(dut): - with pytest.warns(DeprecationWarning) as w: - dump_str = cocotb.utils.hexdump('\x20\x65\x00\xff') - assert "str" in str(w[-1].message) - assert "bytes instead" in str(w[-1].message) - - dump_bytes = cocotb.utils.hexdump(b'\x20\x65\x00\xff') - assert dump_bytes == dump_str - - -class TestHexDiffs: - def test_int_illegal(dut): - # this used to be legal, but deliberately is no longer - with pytest.raises(TypeError): - cocotb.utils.hexdiffs(0, 1) - - def test_str_deprecated(dut): - with pytest.warns(DeprecationWarning) as w: - diff_str = cocotb.utils.hexdiffs('\x20\x65\x00\xff', '\x20\x00\x65') - assert "str" in str(w[-1].message) - assert "bytes instead" in str(w[-1].message) - - diff_bytes = cocotb.utils.hexdiffs(b'\x20\x65\x00\xff', b'\x20\x00\x65') - assert diff_bytes == diff_str - - -def test_pack_deprecated(): - - class Example(ctypes.Structure): - _fields_ = [ - ("a", ctypes.c_byte), - ("b", ctypes.c_byte)] - - e = Example(a=0xCC, b=0x55) - - with pytest.warns(DeprecationWarning): - a = cocotb.utils.pack(e) - - assert a == bytes(e) - - -def test_unpack_deprecated(): - - class Example(ctypes.Structure): - _fields_ = [ - ("a", ctypes.c_byte), - ("b", ctypes.c_byte)] - - e = Example(a=0xCC, b=0x55) - f = Example(a=0xCC, b=0x55) - - b = b'\x01\x02' - - with pytest.warns(DeprecationWarning): - cocotb.utils.unpack(e, b) - - assert e.a == 1 and e.b == 2 - - memoryview(f).cast('B')[:] = b - - assert f.a == 1 and f.b == 2 diff --git a/tests/test_cases/issue_120/Makefile b/tests/test_cases/issue_120/Makefile deleted file mode 100644 index 9931fd09..00000000 --- a/tests/test_cases/issue_120/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - - -include ../../designs/sample_module/Makefile - -MODULE = issue_120 diff --git a/tests/test_cases/issue_120/issue_120.py b/tests/test_cases/issue_120/issue_120.py deleted file mode 100644 index 95ac0aa1..00000000 --- a/tests/test_cases/issue_120/issue_120.py +++ /dev/null @@ -1,44 +0,0 @@ -# A set of regression tests for open issues - -import cocotb -from cocotb.clock import Clock -from cocotb.triggers import RisingEdge, ReadOnly - - -async def send_data(dut): - dut.stream_in_valid = 1 - await RisingEdge(dut.clk) - dut.stream_in_valid = 0 - - -async def monitor(dut): - for i in range(4): - await RisingEdge(dut.clk) - await ReadOnly() - assert dut.stream_in_valid.value.integer, "stream_in_valid should be high on the 5th cycle" - - -# Cadence simulators: "Unable set up RisingEdge(...) Trigger" with VHDL (see #1076) -@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else ()) -async def issue_120_scheduling(dut): - - cocotb.fork(Clock(dut.clk, 10, 'ns').start()) - cocotb.fork(monitor(dut)) - await RisingEdge(dut.clk) - - # First attempt, not from coroutine - works as expected - for i in range(2): - dut.stream_in_valid <= 1 - await RisingEdge(dut.clk) - dut.stream_in_valid <= 0 - - await RisingEdge(dut.clk) - - # Failure - we don't drive valid on the rising edge even though - # behaviour should be identical to the above - await send_data(dut) - dut.stream_in_valid <= 1 - await RisingEdge(dut.clk) - dut.stream_in_valid <= 0 - - await RisingEdge(dut.clk) diff --git a/tests/test_cases/issue_1279/Makefile b/tests/test_cases/issue_1279/Makefile deleted file mode 100644 index 6958dbd7..00000000 --- a/tests/test_cases/issue_1279/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause - -include ../../designs/sample_module/Makefile - -MODULE = issue_1279 diff --git a/tests/test_cases/issue_1279/issue_1279.py b/tests/test_cases/issue_1279/issue_1279.py deleted file mode 100644 index d573233f..00000000 --- a/tests/test_cases/issue_1279/issue_1279.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -Test that once a SimFailure occurs, no further tests are run -""" -import cocotb - - -@cocotb.test( - skip=cocotb.SIM_NAME.lower().startswith("riviera"), # gh-1859 - expect_error=cocotb.result.SimFailure, - stage=1, -) -async def test_sim_failure_a(dut): - # invoke a deadlock, as nothing is driving this clock - await cocotb.triggers.RisingEdge(dut.clk) - - -@cocotb.test( - skip=cocotb.SIM_NAME.lower().startswith("riviera"), # gh-1859 - expect_error=cocotb.result.SimFailure, - stage=2, -) -async def test_sim_failure_b(dut): - assert False, "This test should never run" diff --git a/tests/test_cases/issue_142/Makefile b/tests/test_cases/issue_142/Makefile deleted file mode 100644 index 7338c712..00000000 --- a/tests/test_cases/issue_142/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -############################################################################### -# Copyright (c) 2015 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - - -include ../../designs/sample_module/Makefile - -MODULE = issue_142 - -export COCOTB_RESOLVE_X=ZEROS diff --git a/tests/test_cases/issue_142/issue_142.py b/tests/test_cases/issue_142/issue_142.py deleted file mode 100644 index 1d1a730d..00000000 --- a/tests/test_cases/issue_142/issue_142.py +++ /dev/null @@ -1,29 +0,0 @@ -# A set of regression tests for open issues - -import cocotb -from cocotb.clock import Clock -from cocotb.triggers import RisingEdge -from cocotb.binary import BinaryValue - - -# Cadence simulators: "Unable set up RisingEdge(...) Trigger" with VHDL (see #1076) -@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else ()) -async def issue_142_overflow_error(dut): - """Tranparently convert ints too long to pass - through the GPI interface natively into BinaryValues""" - cocotb.fork(Clock(dut.clk, 10, 'ns').start()) - - def _compare(value): - assert int(dut.stream_in_data_wide.value) == int(value), "Expecting 0x%x but got 0x%x on %s" % ( - int(value), int(dut.stream_in_data_wide.value), - str(dut.stream_in_data_wide)) - - # Wider values are transparently converted to BinaryValues - for value in [0, 0x7FFFFFFF, 0x7FFFFFFFFFFF, BinaryValue(0x7FFFFFFFFFFFFF,len(dut.stream_in_data_wide),bigEndian=False)]: - - dut.stream_in_data_wide <= value - await RisingEdge(dut.clk) - _compare(value) - dut.stream_in_data_wide <= value - await RisingEdge(dut.clk) - _compare(value) diff --git a/tests/test_cases/issue_253/Makefile b/tests/test_cases/issue_253/Makefile deleted file mode 100644 index 3c6258ae..00000000 --- a/tests/test_cases/issue_253/Makefile +++ /dev/null @@ -1,67 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -ifneq ($(SIM),) -all: - @echo "Skipping issue_253 only runs on icarus" -else - -include ../../designs/sample_module/Makefile - -MODULE = issue_253 - -# Redefine the Icarus rule for results.xml to not specify TOPLEVEL, test should still pass - -all: empty_top_level no_top_level notset_top_level - -$(COCOTB_RESULTS_FILE): - @echo "Skipping" - -notset_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) - MODULE=$(MODULE) \ - TESTCASE=issue_253_notset TOPLEVEL= \ - vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) - mkdir -p $@_result && mv results.xml $@_result/ - -empty_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) - MODULE=$(MODULE) \ - TESTCASE=issue_253_empty TOPLEVEL="" \ - vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) - mkdir -p $@_result && mv results.xml $@_result/ - -no_top_level: $(SIM_BUILD)/sim.vvp $(CUSTOM_SIM_DEPS) - MODULE=$(MODULE) \ - TESTCASE=issue_253_none \ - vvp -M $(LIB_DIR) -m libcocotbvpi_icarus $(SIM_BUILD)/sim.vvp $(SIM_ARGS) $(EXTRA_ARGS) $(PLUSARGS) - mkdir -p $@_result && mv results.xml $@_result/ - -endif - -clean:: - @rm -rf empty_top_level_result no_top_level_result notset_top_level_result diff --git a/tests/test_cases/issue_253/issue_253.py b/tests/test_cases/issue_253/issue_253.py deleted file mode 100644 index 0a93c7ac..00000000 --- a/tests/test_cases/issue_253/issue_253.py +++ /dev/null @@ -1,28 +0,0 @@ -# A set of regression tests for open issues - -import cocotb -from cocotb.triggers import Timer - - -async def toggle_clock(dut): - dut.clk <= 0 - await Timer(10, 'ns') - assert dut.clk.value.integer == 0, "Clock not set to 0 as expected" - dut.clk <= 1 - await Timer(10, 'ns') - assert dut.clk.value.integer == 1, "Clock not set to 1 as expected" - - -@cocotb.test() -async def issue_253_empty(dut): - await toggle_clock(dut) - - -@cocotb.test() -async def issue_253_none(dut): - await toggle_clock(dut) - - -@cocotb.test() -async def issue_253_notset(dut): - await toggle_clock(dut) diff --git a/tests/test_cases/issue_330/Makefile b/tests/test_cases/issue_330/Makefile deleted file mode 100644 index d7d4c67b..00000000 --- a/tests/test_cases/issue_330/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - - -include ../../designs/sample_module/Makefile - -MODULE = issue_330 diff --git a/tests/test_cases/issue_330/issue_330.py b/tests/test_cases/issue_330/issue_330.py deleted file mode 100644 index 583f0d39..00000000 --- a/tests/test_cases/issue_330/issue_330.py +++ /dev/null @@ -1,35 +0,0 @@ -# A set of regression tests for open issues - -import cocotb -import logging - - -@cocotb.test(expect_error=AttributeError if cocotb.SIM_NAME in ["Icarus Verilog"] else ()) -async def issue_330_direct(dut): - """ - Access a structure - """ - - tlog = logging.getLogger("cocotb.test") - - structure = dut.inout_if - - tlog.info("Value of inout_if => a_in = %s ; b_out = %s" % (structure.a_in.value, structure.b_out.value)) - - -@cocotb.test(expect_error=AttributeError if cocotb.SIM_NAME in ["Icarus Verilog"] else ()) -async def issue_330_iteration(dut): - """ - Access a structure via issue_330_iteration - """ - - tlog = logging.getLogger("cocotb.test") - - structure = dut.inout_if - - count = 0 - for member in structure: - tlog.info("Found %s" % member._path) - count += 1 - - assert count == 2, "There should have been two members of the structure" diff --git a/tests/test_cases/issue_348/Makefile b/tests/test_cases/issue_348/Makefile deleted file mode 100644 index 931320d4..00000000 --- a/tests/test_cases/issue_348/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -############################################################################### -# Copyright (c) 2015 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - - -include ../../designs/sample_module/Makefile - -MODULE = issue_348 diff --git a/tests/test_cases/issue_348/issue_348.py b/tests/test_cases/issue_348/issue_348.py deleted file mode 100644 index e6e7d07a..00000000 --- a/tests/test_cases/issue_348/issue_348.py +++ /dev/null @@ -1,69 +0,0 @@ -import cocotb -from cocotb.log import SimLog -from cocotb.triggers import Timer, Edge, RisingEdge, FallingEdge - - -async def clock_gen(signal, num): - for x in range(num): - signal <= 0 - await Timer(5, 'ns') - signal <= 1 - await Timer(5, 'ns') - - -async def signal_mon(signal, idx, edge): - _ = SimLog("cocotb.signal_mon.%d.%s" % (idx, signal._name)) - _ = signal.value - edges = 0 - - while True: - await edge(signal) - edges += 1 - - return edges - - -class DualMonitor: - def __init__(self, edge, signal): - self.log = SimLog("cocotb.%s.%s" % (edge, signal._path)) - self.edge_type = edge - self.monitor_edges = [0, 0] - self.signal = signal - - async def signal_mon(self, signal, idx, edge): - while True: - await edge(signal) - self.monitor_edges[idx] += 1 - - async def start(self): - clock_edges = 10 - - cocotb.fork(clock_gen(self.signal, clock_edges)) - _ = cocotb.fork(self.signal_mon(self.signal, 0, self.edge_type)) - _ = cocotb.fork(self.signal_mon(self.signal, 1, self.edge_type)) - - await Timer(100, 'ns') - - for mon in self.monitor_edges: - assert mon, "Monitor saw nothing" - - -# Cadence simulators: "Unable set up RisingEdge(ModifiableObject(sample_module.clk)) Trigger" with VHDL (see #1076) -@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else ()) -async def issue_348_rising(dut): - """ Start two monitors on RisingEdge """ - await DualMonitor(RisingEdge, dut.clk).start() - - -# Cadence simulators: "Unable set up FallingEdge(ModifiableObject(sample_module.clk)) Trigger" with VHDL (see #1076) -@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else ()) -async def issue_348_falling(dut): - """ Start two monitors on FallingEdge """ - await DualMonitor(FallingEdge, dut.clk).start() - - -# Cadence simulators: "Unable set up Edge(ModifiableObject(sample_module.clk)) Trigger" with VHDL (see #1076) -@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else ()) -async def issue_348_either(dut): - """ Start two monitors on Edge """ - await DualMonitor(Edge, dut.clk).start() diff --git a/tests/test_cases/issue_588/Makefile b/tests/test_cases/issue_588/Makefile deleted file mode 100644 index fa8015e2..00000000 --- a/tests/test_cases/issue_588/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -############################################################################### -# Copyright (c) 2018 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - - -include ../../designs/sample_module/Makefile - -MODULE = issue_588 diff --git a/tests/test_cases/issue_588/issue_588.py b/tests/test_cases/issue_588/issue_588.py deleted file mode 100644 index 71909515..00000000 --- a/tests/test_cases/issue_588/issue_588.py +++ /dev/null @@ -1,29 +0,0 @@ -# Test case for issue 588- yielding both coroutines and triggers in a list. -# This is a very simple test; it just makes sure we can yield a list of both. - -import cocotb -from cocotb import triggers, utils - - -async def sample_coroutine(dut): - """ Very simple coroutine that waits 5 ns.""" - await triggers.Timer(5, "ns") - dut._log.info("Sample coroutine yielded.") - - -@cocotb.test() -async def issue_588_coroutine_list(dut): - """ Yield a list of triggers and coroutines.""" - - # Record simulation time. - current_time = utils.get_sim_time("ns") - - # Yield a list, containing a RisingEdge trigger and a coroutine. - coro = cocotb.fork(sample_coroutine(dut)) - await triggers.First(coro, triggers.Timer(100, "ns")) - coro.kill() - - # Make sure that only 5 ns passed, because the sample coroutine - # terminated first. - new_time = utils.get_sim_time("ns") - assert int(new_time - current_time) == 5, "Did not yield coroutine in list." diff --git a/tests/test_cases/issue_768_a/Makefile b/tests/test_cases/issue_768_a/Makefile deleted file mode 100644 index 09c51217..00000000 --- a/tests/test_cases/issue_768_a/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -include ../../designs/sample_module/Makefile - -MODULE = issue_768 diff --git a/tests/test_cases/issue_768_a/issue_768.py b/tests/test_cases/issue_768_a/issue_768.py deleted file mode 100644 index c4b320f0..00000000 --- a/tests/test_cases/issue_768_a/issue_768.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -Control case. - -This passed before the bug fix, and should continue to pass - -Note that the bug only occurred if the test in question runs first - so -no more tests can be added to this file. -""" -import cocotb -from cocotb.triggers import Timer, ReadOnly - -# this line is different between the two files -value = 0 - - -@cocotb.test() -async def test(dut): - dut.stream_in_data.setimmediatevalue(value) - await Timer(1, 'step') - assert dut.stream_in_data.value == 0 - await ReadOnly() diff --git a/tests/test_cases/issue_768_b/Makefile b/tests/test_cases/issue_768_b/Makefile deleted file mode 100644 index 09c51217..00000000 --- a/tests/test_cases/issue_768_b/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -include ../../designs/sample_module/Makefile - -MODULE = issue_768 diff --git a/tests/test_cases/issue_768_b/issue_768.py b/tests/test_cases/issue_768_b/issue_768.py deleted file mode 100644 index ed23e1eb..00000000 --- a/tests/test_cases/issue_768_b/issue_768.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -Failing case. - -Note that the bug only occurred if the test in question runs first - so -no more tests can be added to this file. -""" - -import cocotb -from cocotb.triggers import Timer, ReadOnly -from cocotb.binary import BinaryValue - -# this line is different between the two files -value = BinaryValue(0, n_bits=8) - - -@cocotb.test() -async def do_test(dut): - dut.stream_in_data.setimmediatevalue(value) - await Timer(1, 'step') - assert dut.stream_in_data.value == 0 - await ReadOnly() diff --git a/tests/test_cases/issue_857/Makefile b/tests/test_cases/issue_857/Makefile deleted file mode 100644 index 4142731e..00000000 --- a/tests/test_cases/issue_857/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -############################################################################### -# Copyright (c) 2015 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - - -include ../../designs/sample_module/Makefile - -MODULE = issue_857 diff --git a/tests/test_cases/issue_857/issue_857.py b/tests/test_cases/issue_857/issue_857.py deleted file mode 100644 index fbf98337..00000000 --- a/tests/test_cases/issue_857/issue_857.py +++ /dev/null @@ -1,28 +0,0 @@ -import cocotb -import cocotb.regression -import cocotb.triggers - - -async def dummy_coroutine(dut): - await cocotb.triggers.Timer(10, "ns") - - -# Try to instantiate the TestFactory class using its full specifier name. -# -# In issue #857, a global variable named "regression" in the cocotb module hide -# the module cocotb.regression, so the TestFactory class is not accessible with -# an import like -# -# >>> import cocotb.regression -# >>> factory = cocotb.regression.FactoryManager() -# -# The class is still accessible by an import like -# -# >>> from cocotb.regression import TestFactory -# >>> factory = TestFactory() -# -# but the discoverer of the bug prefers the former approach. -# -# And in general, it's probably a good idea to not have name conflicts ;) -test_factory = cocotb.regression.TestFactory(dummy_coroutine) -test_factory.generate_tests() diff --git a/tests/test_cases/issue_892/Makefile b/tests/test_cases/issue_892/Makefile deleted file mode 100644 index 0cbf05c3..00000000 --- a/tests/test_cases/issue_892/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - - -include ../../designs/sample_module/Makefile - -MODULE = issue_892 diff --git a/tests/test_cases/issue_892/issue_892.py b/tests/test_cases/issue_892/issue_892.py deleted file mode 100644 index 1e2727c5..00000000 --- a/tests/test_cases/issue_892/issue_892.py +++ /dev/null @@ -1,14 +0,0 @@ -import cocotb -from cocotb.triggers import Timer -from cocotb.result import TestSuccess - - -async def raise_test_success(): - await Timer(1, units='ns') - raise TestSuccess("TestSuccess") - - -@cocotb.test() -async def error_test(dut): - cocotb.fork(raise_test_success()) - await Timer(10, units='ns') diff --git a/tests/test_cases/issue_893/Makefile b/tests/test_cases/issue_893/Makefile deleted file mode 100644 index 6147ade0..00000000 --- a/tests/test_cases/issue_893/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - - -include ../../designs/sample_module/Makefile - -MODULE = issue_893 diff --git a/tests/test_cases/issue_893/issue_893.py b/tests/test_cases/issue_893/issue_893.py deleted file mode 100644 index 7df83721..00000000 --- a/tests/test_cases/issue_893/issue_893.py +++ /dev/null @@ -1,12 +0,0 @@ -import cocotb -from cocotb.triggers import Timer - - -async def coroutine_with_undef(): - new_variable = undefined_variable # noqa - - -@cocotb.test(expect_error=NameError) -async def fork_erroring_coroutine(dut): - cocotb.fork(coroutine_with_undef()) - await Timer(10, units='ns') diff --git a/tests/test_cases/issue_957/Makefile b/tests/test_cases/issue_957/Makefile deleted file mode 100644 index 4a7dc1fe..00000000 --- a/tests/test_cases/issue_957/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -include ../../designs/sample_module/Makefile - -MODULE = issue_957 diff --git a/tests/test_cases/issue_957/issue_957.py b/tests/test_cases/issue_957/issue_957.py deleted file mode 100644 index 2f42028b..00000000 --- a/tests/test_cases/issue_957/issue_957.py +++ /dev/null @@ -1,16 +0,0 @@ -import cocotb -from cocotb.triggers import Timer, RisingEdge, First - - -async def wait_edge(dut): - # this trigger never fires - await First(RisingEdge(dut.stream_out_ready)) - - -@cocotb.test() -async def test1(dut): - cocotb.fork(wait_edge(dut)) - await Timer(10, 'ns') - - -test2 = test1 diff --git a/tests/test_cases/test_array/Makefile b/tests/test_cases/test_array/Makefile deleted file mode 100644 index 973e3ef8..00000000 --- a/tests/test_cases/test_array/Makefile +++ /dev/null @@ -1,50 +0,0 @@ -############################################################################### -# Copyright (c) 2016 Potential Ventures Ltd -# Copyright (c) 2016 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -ifeq ($(SIM),) -all: - @echo "Skipping test_array since icarus doesn't support indexing" - -clean:: -# nothing to clean, just define target in this branch - -else ifeq ($(shell echo $(SIM) | tr A-Z a-z),icarus) -all: - @echo "Skipping test_array since icarus doesn't support indexing" - -clean:: -# nothing to clean, just define target in this branch - -else - -include ../../designs/array_module/Makefile - -MODULE = test_array - -endif diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py deleted file mode 100644 index d012bd09..00000000 --- a/tests/test_cases/test_array/test_array.py +++ /dev/null @@ -1,508 +0,0 @@ -""" -A set of tests that demonstrate Array structure support -""" - -import logging -import cocotb - -from cocotb.clock import Clock -from cocotb.triggers import Timer -from cocotb.result import TestError, TestFailure -from cocotb.handle import HierarchyObject, HierarchyArrayObject, ModifiableObject, NonHierarchyIndexableObject, ConstantObject - - -def _check_type(tlog, hdl, expected): - assert isinstance(hdl, expected), ">{0!r} ({1})< should be >{2}<".format(hdl, hdl._type, expected) - tlog.info(" Found %r (%s) with length=%d", hdl, hdl._type, len(hdl)) - - -def _check_int(tlog, hdl, expected): - assert int(hdl) == expected, "{2!r}: Expected >{0}< but got >{1}<".format(expected, int(hdl), hdl) - tlog.info(" Found {0!r} ({1}) with value={2}".format(hdl, hdl._type, int(hdl))) - - -def _check_logic(tlog, hdl, expected): - assert int(hdl) == expected, "{2!r}: Expected >0x{0:X}< but got >0x{1:X}<".format(expected, int(hdl), hdl) - tlog.info(" Found {0!r} ({1}) with value=0x{2:X}".format(hdl, hdl._type, int(hdl))) - - -def _check_str(tlog, hdl, expected): - assert hdl.value == expected, "{2!r}: Expected >{0}< but got >{1}<".format(expected, str(hdl), hdl) - tlog.info(" Found {0!r} ({1}) with value={2}".format(hdl, hdl._type, str(hdl))) - - -def _check_real(tlog, hdl, expected): - assert float(hdl) == expected, "{2!r}: Expected >{0}< but got >{1}<".format(expected, float(hdl), hdl) - tlog.info(" Found {0!r} ({1}) with value={2}".format(hdl, hdl._type, float(hdl))) - - -def _check_value(tlog, hdl, expected): - assert hdl.value == expected, "{2!r}: Expected >{0}< but got >{1}<".format(expected, hdl.value, hdl) - tlog.info(" Found {0!r} ({1}) with value={2}".format(hdl, hdl._type, hdl.value)) - - -# NOTE: simulator-specific handling is done in this test itself, not via expect_error in the decorator -@cocotb.test() -async def test_read_write(dut): - """Test handle inheritance""" - tlog = logging.getLogger("cocotb.test") - - cocotb.fork(Clock(dut.clk, 10, "ns").start()) - - await Timer(10, "ns") - - tlog.info("Checking Generics/Parameters:") - _check_logic(tlog, dut.param_logic , 1) - _check_logic(tlog, dut.param_logic_vec, 0xDA) - - if cocotb.LANGUAGE in ["vhdl"]: - _check_int(tlog, dut.param_bool, 1) - _check_int(tlog, dut.param_int , 6) - _check_real(tlog, dut.param_real, 3.14) - _check_int(tlog, dut.param_char, ord('p')) - _check_str(tlog, dut.param_str , b"ARRAYMOD") - - if not cocotb.SIM_NAME.lower().startswith(("riviera")): - _check_logic(tlog, dut.param_rec.a , 0) - _check_logic(tlog, dut.param_rec.b[0] , 0) - _check_logic(tlog, dut.param_rec.b[1] , 0) - _check_logic(tlog, dut.param_rec.b[2] , 0) - _check_logic(tlog, dut.param_cmplx[0].a , 0) - _check_logic(tlog, dut.param_cmplx[0].b[0], 0) - _check_logic(tlog, dut.param_cmplx[0].b[1], 0) - _check_logic(tlog, dut.param_cmplx[0].b[2], 0) - _check_logic(tlog, dut.param_cmplx[1].a , 0) - _check_logic(tlog, dut.param_cmplx[1].b[0], 0) - _check_logic(tlog, dut.param_cmplx[1].b[1], 0) - _check_logic(tlog, dut.param_cmplx[1].b[2], 0) - - tlog.info("Checking Constants:") - _check_logic(tlog, dut.const_logic , 0) - _check_logic(tlog, dut.const_logic_vec, 0x3D) - - if cocotb.LANGUAGE in ["vhdl"]: - _check_int(tlog, dut.const_bool, 0) - _check_int(tlog, dut.const_int , 12) - _check_real(tlog, dut.const_real, 6.28) - _check_int(tlog, dut.const_char, ord('c')) - _check_str(tlog, dut.const_str , b"MODARRAY") - - if not cocotb.SIM_NAME.lower().startswith(("riviera")): - _check_logic(tlog, dut.const_rec.a , 1) - _check_logic(tlog, dut.const_rec.b[0] , 0xFF) - _check_logic(tlog, dut.const_rec.b[1] , 0xFF) - _check_logic(tlog, dut.const_rec.b[2] , 0xFF) - _check_logic(tlog, dut.const_cmplx[1].a , 1) - _check_logic(tlog, dut.const_cmplx[1].b[0], 0xFF) - _check_logic(tlog, dut.const_cmplx[1].b[1], 0xFF) - _check_logic(tlog, dut.const_cmplx[1].b[2], 0xFF) - _check_logic(tlog, dut.const_cmplx[2].a , 1) - _check_logic(tlog, dut.const_cmplx[2].b[0], 0xFF) - _check_logic(tlog, dut.const_cmplx[2].b[1], 0xFF) - _check_logic(tlog, dut.const_cmplx[2].b[2], 0xFF) - - dut.select_in = 2 - - await Timer(10, "ns") - - tlog.info("Writing the signals!!!") - dut.sig_logic = 1 - dut.sig_logic_vec = 0xCC - dut.sig_t2 = [0xCC, 0xDD, 0xEE, 0xFF] - dut.sig_t4 = [ - [0x00, 0x11, 0x22, 0x33], - [0x44, 0x55, 0x66, 0x77], - [0x88, 0x99, 0xAA, 0xBB], - [0xCC, 0xDD, 0xEE, 0xFF] - ] - - if cocotb.LANGUAGE in ["vhdl"]: - dut.sig_bool = 1 - dut.sig_int = 5000 - dut.sig_real = 22.54 - dut.sig_char = ord('Z') - dut.sig_str = "Testing" - dut.sig_rec.a = 1 - dut.sig_rec.b[0] = 0x01 - dut.sig_rec.b[1] = 0x23 - dut.sig_rec.b[2] = 0x45 - dut.sig_cmplx[0].a = 0 - dut.sig_cmplx[0].b[0] = 0x67 - dut.sig_cmplx[0].b[1] = 0x89 - dut.sig_cmplx[0].b[2] = 0xAB - dut.sig_cmplx[1].a = 1 - dut.sig_cmplx[1].b[0] = 0xCD - dut.sig_cmplx[1].b[1] = 0xEF - dut.sig_cmplx[1].b[2] = 0x55 - - await Timer(10, "ns") - - tlog.info("Checking writes:") - _check_logic(tlog, dut.port_logic_out , 1) - _check_logic(tlog, dut.port_logic_vec_out, 0xCC) - _check_value(tlog, dut.sig_t2, [0xCC, 0xDD, 0xEE, 0xFF]) - _check_logic(tlog, dut.sig_t2[7], 0xCC) - _check_logic(tlog, dut.sig_t2[4], 0xFF) - _check_logic(tlog, dut.sig_t4[1][5], 0x66) - _check_logic(tlog, dut.sig_t4[3][7], 0xCC) - - if cocotb.LANGUAGE in ["vhdl"]: - _check_int(tlog, dut.port_bool_out, 1) - _check_int(tlog, dut.port_int_out , 5000) - _check_real(tlog, dut.port_real_out, 22.54) - _check_int(tlog, dut.port_char_out, ord('Z')) - _check_str(tlog, dut.port_str_out , b"Testing") - - _check_logic(tlog, dut.port_rec_out.a , 1) - _check_logic(tlog, dut.port_rec_out.b[0] , 0x01) - _check_logic(tlog, dut.port_rec_out.b[1] , 0x23) - _check_logic(tlog, dut.port_rec_out.b[2] , 0x45) - _check_logic(tlog, dut.port_cmplx_out[0].a , 0) - _check_logic(tlog, dut.port_cmplx_out[0].b[0], 0x67) - _check_logic(tlog, dut.port_cmplx_out[0].b[1], 0x89) - _check_logic(tlog, dut.port_cmplx_out[0].b[2], 0xAB) - _check_logic(tlog, dut.port_cmplx_out[1].a , 1) - _check_logic(tlog, dut.port_cmplx_out[1].b[0], 0xCD) - _check_logic(tlog, dut.port_cmplx_out[1].b[1], 0xEF) - _check_logic(tlog, dut.port_cmplx_out[1].b[2], 0x55) - - tlog.info("Writing a few signal sub-indices!!!") - dut.sig_logic_vec[2] = 0 - if cocotb.LANGUAGE in ["vhdl"] or not (cocotb.SIM_NAME.lower().startswith(("ncsim", "xmsim")) or - (cocotb.SIM_NAME.lower().startswith(("riviera")) and - cocotb.SIM_VERSION.startswith(("2016.06", "2016.10", "2017.02")))): - dut.sig_t6[1][3][2] = 1 - dut.sig_t6[0][2][7] = 0 - - if cocotb.LANGUAGE in ["vhdl"]: - dut.sig_str[2] = ord('E') - dut.sig_rec.b[1][7] = 1 - dut.sig_cmplx[1].b[1][0] = 0 - - await Timer(10, "ns") - - tlog.info("Checking writes (2):") - _check_logic(tlog, dut.port_logic_vec_out, 0xC8) - if cocotb.LANGUAGE in ["vhdl"] or not (cocotb.SIM_NAME.lower().startswith(("ncsim", "xmsim")) or - (cocotb.SIM_NAME.lower().startswith(("riviera")) and - cocotb.SIM_VERSION.startswith(("2016.06", "2016.10", "2017.02")))): - _check_logic(tlog, dut.sig_t6[1][3][2], 1) - _check_logic(tlog, dut.sig_t6[0][2][7], 0) - - if cocotb.LANGUAGE in ["vhdl"]: - _check_str(tlog, dut.port_str_out, b"TEsting") # the uppercase "E" from a few lines before - - _check_logic(tlog, dut.port_rec_out.b[1] , 0xA3) - _check_logic(tlog, dut.port_cmplx_out[1].b[1], 0xEE) - - -@cocotb.test() -async def test_gen_loop(dut): - """Test accessing Generate Loops""" - tlog = logging.getLogger("cocotb.test") - - asc_gen_20 = dut.asc_gen[20] - desc_gen = dut.desc_gen - - if not isinstance(dut.asc_gen, HierarchyArrayObject): - raise TestFailure("Generate Loop parent >{!r}< should be HierarchyArrayObject".format(dut.asc_gen)) - - if not isinstance(desc_gen, HierarchyArrayObject): - raise TestFailure("Generate Loop parent >{!r}< should be HierarchyArrayObject".format(desc_gen)) - - if not isinstance(asc_gen_20, HierarchyObject): - raise TestFailure("Generate Loop child >{!r}< should be HierarchyObject".format(asc_gen_20)) - - tlog.info("Direct access found %s", asc_gen_20) - tlog.info("Direct access found %s", desc_gen) - - for gens in desc_gen: - tlog.info("Iterate access found %s", gens) - - if len(desc_gen) != 8: - raise TestError("Length of desc_gen is >{}< and should be 8".format(len(desc_gen))) - else: - tlog.info("Length of desc_gen is %d", len(desc_gen)) - - if len(dut.asc_gen) != 8: - raise TestError("Length of asc_gen is >{}< and should be 8".format(len(dut.asc_gen))) - else: - tlog.info("Length of asc_gen is %d", len(dut.asc_gen)) - - for gens in dut.asc_gen: - tlog.info("Iterate access found %s", gens) - - -@cocotb.test() -async def test_discover_all(dut): - r"""Discover everything in the DUT: - dut - TYPE CNT NOTES EXCEPTIONS - parameters: 7/2 (base types) (VHDL/Verilog) - 6 (param_rec.a, param_rec.b[0:2]) (VHDL only excluding Aldec) - 13 (param_cmplx[0:1].a, param_cmplx[0:1].b[0:2]) (VHDL only excluding Aldec) - ports: 1 (clk) - 1 (select_in) (VPI - Aldec sees as 32 bit register (i.e. cnt = 33) - 9 (port_desc_in) - 9 (port_asc_in) - 9 (port_ofst_in) - 9 (port_desc_out) - 9 (port_asc_out) - 9 (port_ofst_out) - 1 (port_logic_out) - 9 (port_logic_vec_out) - 1 (port_bool_out) (VHDL Only) - 1 (port_int_out) (VHDL Only) - 1 (port_real_out) (VHDL Only) - 1 (port_char_out) (VHDL Only) - 9 (port_str_out) (VHDL Only) - 30 (port_rec_out) (VPI - Aldec sees as a Module and not structure (i.e. cnt = 1)) - 61 (port_cmplx_out) (VPI - Aldec sees as a Module and not structure (i.e. cnt = 1)) - constants: 1 (const_logic) - 1 (const_logic_vec) - 1 (const_bool) (VHDL Only) - 1 (const_int) (VHDL Only) - 1 (const_real) (VHDL Only) - 1 (const_char) (VHDL Only) - 1 (const_str) (VHDL Only) - 6 (const_rec.a, const_rec.b[0:2]) (VHDL only excluding Aldec) - 13 (const_cmplx[1:2].a, const_cmplx[1:2].b[0:2]) (VHDL only excluding Aldec) - signals: 9 (sig_desc) - 9 (sig_asc) - 1 (\ext_id\) (VHDL only) - 1 (\!\) (VHDL only) - 5 (sig_t1) - 37 (sig_t2[7:4][7:0]) - 37 (sig_t3a[1:4][7:0]) - 37 (sig_t3b[3:0][7:0]) - 149 (sig_t4[0:3][7:4][7:0]) - 112 (sig_t5[0:2][0:3][7:0]) - 57 (sig_t6[0:1][2:4][7:0]) - 149 (sig_t7[3:0][3:0]) (VPI Only) - 149 ([3:0][3:0]sig_t8) (VPI Only) - 1 (sig_logic) - 9 (sig_logic_vec) - 1 (sig_bool) (VHDL Only) - 1 (sig_int) (VHDL Only) - 1 (sig_real) (VHDL Only) - 1 (sig_char) (VHDL Only) - 9 (sig_str) (VHDL Only) - 30 (sig_rec.a, sig_rec.b[0:2][7:0]) (VPI doesn't find, added manually, except for Aldec) - 61 (sig_cmplx[0:1].a, sig_cmplx[0:1].b[0:2][7:0]) (VPI - Aldec older than 2017.10.67 doesn't find) - regions: 9 (asc_gen[16:23]) - 8 (asc_gen: signals) (VHPI - Riviera doesn't find, added manually) - 8 (asc_gen: constant) - 8 (asc_gen: variable) - 8 (asc_gen: process "always") (VPI - Aldec only) - 9 (desc_gen[7:0]) - 8 (desc_gen: signals) (VHPI - Riviera doesn't find, added manually) - 8 (desc_gen: constant) - 8 (desc_gen: variable) - 8 (desc_gen: process "always") (VPI - Aldec only) - process: 1 ("always") (VPI - Aldec only) - - TOTAL: 856 (VHDL - Default) - 818 (VHDL - Aldec) - 1078 (Verilog - Default) - 947/1038 (Verilog - Aldec) - """ - - tlog = logging.getLogger("cocotb.test") - - await Timer(10, "ns") - - # Need to clear sub_handles so won't attempt to iterate over handles like sig_rec and sig_rec_array - # - # DO NOT REMOVE. Aldec cannot iterate over the complex records due to bugs in the VPI interface. - if (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")) and - cocotb.SIM_VERSION.startswith(("2016.02"))) : - if len(dut._sub_handles) != 0: - dut._sub_handles = {} - - # Modelsim/Questa VPI will not find a vpiStructVar from vpiModule so we set a dummy variable - # to ensure the handle is in the dut "sub_handles" for iterating - # - # DO NOT ADD FOR ALDEC. Older Versions do not iterate over properly - if cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("modelsim", "ncsim", "xmsim")): - dummy = dut.sig_rec - dummy = dut.port_rec_out - - # Riviera-Pro's VHPI implementation does not find signal declarations when iterating - if cocotb.LANGUAGE in ["vhdl"] and cocotb.SIM_NAME.lower().startswith(("riviera")): - for hdl in dut.asc_gen: - dummy = hdl.sig - for hdl in dut.desc_gen: - dummy = hdl.sig - - if cocotb.LANGUAGE in ["vhdl"] and cocotb.SIM_NAME.lower().startswith(("riviera")): - pass_total = 571 - elif cocotb.LANGUAGE in ["vhdl"]: - pass_total = 856 - elif cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")): - # Numbers for versions before 2019.10 may be outdated - if cocotb.SIM_VERSION.startswith(("2017.10.61")): - pass_total = 803 - elif cocotb.SIM_VERSION.startswith(("2016.06", "2016.10", "2017.02")): - pass_total = 813 - elif cocotb.SIM_VERSION.startswith(("2016.02")): - pass_total = 947 - elif cocotb.SIM_VERSION.startswith(("2019.10", "2020.")): - # vpiVariables finds port_rec_out and sig_rec - pass_total = 1006 - else: - pass_total = 1038 - elif cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("chronologic simulation vcs")): - pass_total = 606 - else: - pass_total = 1078 - - def _discover(obj, indent): - count = 0 - new_indent = indent+"---" - for thing in obj: - count += 1 - tlog.info("%sFound %r (%s)", indent, thing, thing._type) - count += _discover(thing, new_indent) - return count - - tlog.info("Iterating over %r (%s)", dut, dut._type) - total = _discover(dut, "") - tlog.info("Found a total of %d things", total) - if total != pass_total: - raise TestFailure("Expected {0} objects but found {1}".format(pass_total, total)) - - -@cocotb.test(skip=(cocotb.LANGUAGE in ["verilog"] or cocotb.SIM_NAME.lower().startswith(("riviera")))) -async def test_direct_constant_indexing(dut): - """Test directly accessing constant/parameter data in arrays, i.e. not iterating""" - - tlog = logging.getLogger("cocotb.test") - - tlog.info("Checking Types of complex array structures in constants/parameters.") - _check_type(tlog, dut.param_rec, HierarchyObject) - _check_type(tlog, dut.param_rec.a, ConstantObject) - _check_type(tlog, dut.param_rec.b, NonHierarchyIndexableObject) - _check_type(tlog, dut.param_rec.b[1], ConstantObject) - - _check_type(tlog, dut.param_cmplx, NonHierarchyIndexableObject) - _check_type(tlog, dut.param_cmplx[0], HierarchyObject) - _check_type(tlog, dut.param_cmplx[0].a, ConstantObject) - _check_type(tlog, dut.param_cmplx[0].b, NonHierarchyIndexableObject) - _check_type(tlog, dut.param_cmplx[0].b[1], ConstantObject) - - _check_type(tlog, dut.const_rec, HierarchyObject) - _check_type(tlog, dut.const_rec.a, ConstantObject) - _check_type(tlog, dut.const_rec.b, NonHierarchyIndexableObject) - _check_type(tlog, dut.const_rec.b[1], ConstantObject) - - _check_type(tlog, dut.const_cmplx, NonHierarchyIndexableObject) - _check_type(tlog, dut.const_cmplx[1], HierarchyObject) - _check_type(tlog, dut.const_cmplx[1].a, ConstantObject) - _check_type(tlog, dut.const_cmplx[1].b, NonHierarchyIndexableObject) - _check_type(tlog, dut.const_cmplx[1].b[1], ConstantObject) - - -@cocotb.test() -async def test_direct_signal_indexing(dut): - """Test directly accessing signal/net data in arrays, i.e. not iterating""" - - tlog = logging.getLogger("cocotb.test") - - cocotb.fork(Clock(dut.clk, 10, "ns").start()) - - dut.port_desc_in <= 0 - dut.port_asc_in <= 0 - dut.port_ofst_in <= 0 - - await Timer(20, "ns") - - dut.port_desc_in[2] <= 1 - dut.port_asc_in[2] <= 1 - dut.port_ofst_in[2] <= 1 - - await Timer(20, "ns") - - tlog.info("Checking bit mapping from input to generate loops.") - if int(dut.desc_gen[2].sig) != 1: - raise TestFailure("Expected {0!r} to be a 1 but got {1}".format(dut.desc_gen[2].sig, int(dut.desc_gen[2].sig))) - else: - tlog.info(" %r = %d", dut.desc_gen[2].sig, int(dut.desc_gen[2].sig)) - - if int(dut.asc_gen[18].sig) != 1: - raise TestFailure("Expected {0!r} to be a 1 but got {1}".format(dut.asc_gen[18].sig, int(dut.asc_gen[18].sig))) - else: - tlog.info(" %r = %d", dut.asc_gen[18].sig, int(dut.asc_gen[18].sig)) - - tlog.info("Checking indexing of data with offset index.") - if int(dut.port_ofst_out) != 64: - raise TestFailure("Expected {0!r} to be a 64 but got {1}".format(dut.port_ofst_out, int(dut.port_ofst_out))) - else: - tlog.info(" %r = %d (%s)", dut.port_ofst_out, int(dut.port_ofst_out), dut.port_ofst_out.value.binstr) - - tlog.info("Checking Types of complex array structures in signals.") - _check_type(tlog, dut.sig_desc[20], ModifiableObject) - _check_type(tlog, dut.sig_asc[17], ModifiableObject) - _check_type(tlog, dut.sig_t1, ModifiableObject) - _check_type(tlog, dut.sig_t2, NonHierarchyIndexableObject) - _check_type(tlog, dut.sig_t2[5], ModifiableObject) - _check_type(tlog, dut.sig_t2[5][3], ModifiableObject) - _check_type(tlog, dut.sig_t3a[2][3], ModifiableObject) - _check_type(tlog, dut.sig_t3b[3], ModifiableObject) - _check_type(tlog, dut.sig_t3a, NonHierarchyIndexableObject) - _check_type(tlog, dut.sig_t4, NonHierarchyIndexableObject) - _check_type(tlog, dut.sig_t4[3], NonHierarchyIndexableObject) - # the following version cannot index into those arrays and will error out - if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")) and - cocotb.SIM_VERSION.startswith(("2016.06", "2016.10", "2017.02"))): - _check_type(tlog, dut.sig_t4[3][4], ModifiableObject) - _check_type(tlog, dut.sig_t4[3][4][1], ModifiableObject) - _check_type(tlog, dut.sig_t5, NonHierarchyIndexableObject) - _check_type(tlog, dut.sig_t5[1], NonHierarchyIndexableObject) - _check_type(tlog, dut.sig_t5[1][0], ModifiableObject) - _check_type(tlog, dut.sig_t5[1][0][6], ModifiableObject) - _check_type(tlog, dut.sig_t6, NonHierarchyIndexableObject) - _check_type(tlog, dut.sig_t6[1], NonHierarchyIndexableObject) - # the following version cannot index into those arrays and will error out - if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")) and - cocotb.SIM_VERSION.startswith(("2016.06", "2016.10", "2017.02"))): - _check_type(tlog, dut.sig_t6[0][3], ModifiableObject) - _check_type(tlog, dut.sig_t6[0][3][7], ModifiableObject) - _check_type(tlog, dut.sig_cmplx, NonHierarchyIndexableObject) - - if cocotb.LANGUAGE in ["verilog"]: - _check_type(tlog, dut.sig_t7[1], NonHierarchyIndexableObject) - _check_type(tlog, dut.sig_t7[0][3], ModifiableObject) - _check_type(tlog, dut.sig_t8[1], NonHierarchyIndexableObject) - _check_type(tlog, dut.sig_t8[0][3], ModifiableObject) - - # Riviera has a bug and finds dut.sig_cmplx[1], but the type returned is a vpiBitVar - # only true for version 2016.02 - if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")) and - cocotb.SIM_VERSION.startswith(("2016.02"))): - - _check_type(tlog, dut.sig_cmplx[1], HierarchyObject) - _check_type(tlog, dut.sig_cmplx[1].a, ModifiableObject) - _check_type(tlog, dut.sig_cmplx[1].b, NonHierarchyIndexableObject) - _check_type(tlog, dut.sig_cmplx[1].b[1], ModifiableObject) - _check_type(tlog, dut.sig_cmplx[1].b[1][2], ModifiableObject) - - _check_type(tlog, dut.sig_rec, HierarchyObject) - _check_type(tlog, dut.sig_rec.a, ModifiableObject) - _check_type(tlog, dut.sig_rec.b, NonHierarchyIndexableObject) - - # Riviera has a bug and finds dut.sig_rec.b[1], but the type returned is 0 which is unknown - # only true for version 2016.02 - if not (cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")) and - cocotb.SIM_VERSION.startswith(("2016.02"))): - _check_type(tlog, dut.sig_rec.b[1], ModifiableObject) - _check_type(tlog, dut.sig_rec.b[1][2], ModifiableObject) - - -@cocotb.test(skip=(cocotb.LANGUAGE in ["verilog"])) -async def test_extended_identifiers(dut): - """Test accessing extended identifiers""" - - tlog = logging.getLogger("cocotb.test") - tlog.info("Checking extended identifiers.") - _check_type(tlog, dut._id("\\ext_id\\", extended=False), ModifiableObject) - _check_type(tlog, dut._id("!"), ModifiableObject) diff --git a/tests/test_cases/test_array_simple/Makefile b/tests/test_cases/test_array_simple/Makefile deleted file mode 100644 index 69846fdd..00000000 --- a/tests/test_cases/test_array_simple/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -include ../../designs/sample_module/Makefile - -MODULE = test_array_simple diff --git a/tests/test_cases/test_array_simple/test_array_simple.py b/tests/test_cases/test_array_simple/test_array_simple.py deleted file mode 100644 index 8e21b008..00000000 --- a/tests/test_cases/test_array_simple/test_array_simple.py +++ /dev/null @@ -1,211 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -"""Test getting and setting values of arrays""" -import contextlib -import logging - -import cocotb -from cocotb.clock import Clock -from cocotb.result import TestFailure -from cocotb.triggers import Timer - -tlog = logging.getLogger("cocotb.test") - - -def _check_value(tlog, hdl, expected): - if hdl.value != expected: - raise TestFailure("{2!r}: Expected >{0}< but got >{1}<".format(expected, hdl.value, hdl)) - else: - tlog.info(" Found {0!r} ({1}) with value={2}".format(hdl, hdl._type, hdl.value)) - - -@cocotb.test() -async def test_1dim_array_handles(dut): - """Test getting and setting array values using the handle of the full array.""" - - cocotb.fork(Clock(dut.clk, 1000, 'ns').start()) - - # Set values with '<=' operator - dut.array_7_downto_4 <= [0xF0, 0xE0, 0xD0, 0xC0] - dut.array_4_to_7 <= [0xB0, 0xA0, 0x90, 0x80] - dut.array_3_downto_0 <= [0x70, 0x60, 0x50, 0x40] - dut.array_0_to_3 <= [0x30, 0x20, 0x10, 0x00] - - await Timer(1000, 'ns') - - _check_value(tlog, dut.array_7_downto_4, [0xF0, 0xE0, 0xD0, 0xC0]) - _check_value(tlog, dut.array_4_to_7 , [0xB0, 0xA0, 0x90, 0x80]) - _check_value(tlog, dut.array_3_downto_0, [0x70, 0x60, 0x50, 0x40]) - _check_value(tlog, dut.array_0_to_3 , [0x30, 0x20, 0x10, 0x00]) - - # Set values through HierarchyObject.__setattr__ method on 'dut' handle - dut.array_7_downto_4 = [0x00, 0x11, 0x22, 0x33] - dut.array_4_to_7 = [0x44, 0x55, 0x66, 0x77] - dut.array_3_downto_0 = [0x88, 0x99, 0xAA, 0xBB] - dut.array_0_to_3 = [0xCC, 0xDD, 0xEE, 0xFF] - - await Timer(1000, 'ns') - - _check_value(tlog, dut.array_7_downto_4, [0x00, 0x11, 0x22, 0x33]) - _check_value(tlog, dut.array_4_to_7 , [0x44, 0x55, 0x66, 0x77]) - _check_value(tlog, dut.array_3_downto_0, [0x88, 0x99, 0xAA, 0xBB]) - _check_value(tlog, dut.array_0_to_3 , [0xCC, 0xDD, 0xEE, 0xFF]) - - -@cocotb.test(skip=cocotb.SIM_NAME.lower().startswith(("icarus", "ghdl"))) -async def test_ndim_array_handles(dut): - """Test getting and setting multi-dimensional array values using the handle of the full array.""" - - cocotb.fork(Clock(dut.clk, 1000, 'ns').start()) - - # Set values with '<=' operator - dut.array_2d <= [ - [0xF0, 0xE0, 0xD0, 0xC0], - [0xB0, 0xA0, 0x90, 0x80] - ] - - await Timer(1000, 'ns') - - _check_value(tlog, dut.array_2d, [[0xF0, 0xE0, 0xD0, 0xC0], [0xB0, 0xA0, 0x90, 0x80]]) - - # Set values through HierarchyObject.__setattr__ method on 'dut' handle - dut.array_2d = [ - [0x00, 0x11, 0x22, 0x33], - [0x44, 0x55, 0x66, 0x77] - ] - - await Timer(1000, 'ns') - - _check_value(tlog, dut.array_2d, [[0x00, 0x11, 0x22, 0x33], [0x44, 0x55, 0x66, 0x77]]) - - -@cocotb.test() -async def test_1dim_array_indexes(dut): - """Test getting and setting values of array indexes.""" - - cocotb.fork(Clock(dut.clk, 1000, 'ns').start()) - - dut.array_7_downto_4 <= [0xF0, 0xE0, 0xD0, 0xC0] - dut.array_4_to_7 <= [0xB0, 0xA0, 0x90, 0x80] - dut.array_3_downto_0 <= [0x70, 0x60, 0x50, 0x40] - dut.array_0_to_3 <= [0x30, 0x20, 0x10, 0x00] - - await Timer(1000, 'ns') - - # Check indices - _check_value(tlog, dut.array_7_downto_4[7], 0xF0) - _check_value(tlog, dut.array_7_downto_4[4], 0xC0) - _check_value(tlog, dut.array_4_to_7[4] , 0xB0) - _check_value(tlog, dut.array_4_to_7[7] , 0x80) - _check_value(tlog, dut.array_3_downto_0[3], 0x70) - _check_value(tlog, dut.array_3_downto_0[0], 0x40) - _check_value(tlog, dut.array_0_to_3[0] , 0x30) - _check_value(tlog, dut.array_0_to_3[3] , 0x00) - _check_value(tlog, dut.array_0_to_3[1] , 0x20) - - # Get sub-handles through NonHierarchyIndexableObject.__getitem__ - dut.array_7_downto_4[7] <= 0xDE - dut.array_4_to_7[4] <= 0xFC - dut.array_3_downto_0[0] <= 0xAB - dut.array_0_to_3[1] <= 0x7A - dut.array_0_to_3[3] <= 0x42 - - await Timer(1000, 'ns') - - _check_value(tlog, dut.array_7_downto_4[7], 0xDE) - _check_value(tlog, dut.array_4_to_7[4] , 0xFC) - _check_value(tlog, dut.array_3_downto_0[0], 0xAB) - _check_value(tlog, dut.array_0_to_3[1] , 0x7A) - _check_value(tlog, dut.array_0_to_3[3] , 0x42) - - # Set sub-handle values through NonHierarchyIndexableObject.__setitem__ - dut.array_7_downto_4[7] = 0x77 - dut.array_4_to_7[4] = 0x44 - dut.array_3_downto_0[0] = 0x00 - dut.array_0_to_3[1] = 0x11 - dut.array_0_to_3[3] = 0x33 - - await Timer(1000, 'ns') - - _check_value(tlog, dut.array_7_downto_4[7], 0x77) - _check_value(tlog, dut.array_4_to_7[4] , 0x44) - _check_value(tlog, dut.array_3_downto_0[0], 0x00) - _check_value(tlog, dut.array_0_to_3[1] , 0x11) - _check_value(tlog, dut.array_0_to_3[3] , 0x33) - - -@cocotb.test(skip=cocotb.SIM_NAME.lower().startswith(("icarus", "ghdl"))) -async def test_ndim_array_indexes(dut): - """Test getting and setting values of multi-dimensional array indexes.""" - - cocotb.fork(Clock(dut.clk, 1000, 'ns').start()) - - dut.array_2d <= [ - [0xF0, 0xE0, 0xD0, 0xC0], - [0xB0, 0xA0, 0x90, 0x80] - ] - - await Timer(1000, 'ns') - - # Check indices - _check_value(tlog, dut.array_2d[1] , [0xB0, 0xA0, 0x90, 0x80]) - _check_value(tlog, dut.array_2d[0][31], 0xF0) - _check_value(tlog, dut.array_2d[1][29], 0x90) - _check_value(tlog, dut.array_2d[1][28], 0x80) - - # Get sub-handles through NonHierarchyIndexableObject.__getitem__ - dut.array_2d[1] <= [0xDE, 0xAD, 0xBE, 0xEF] - dut.array_2d[0][31] <= 0x0F - - await Timer(1000, 'ns') - - _check_value(tlog, dut.array_2d[0][31], 0x0F) - _check_value(tlog, dut.array_2d[0][29], 0xD0) - _check_value(tlog, dut.array_2d[1][30], 0xAD) - _check_value(tlog, dut.array_2d[1][28], 0xEF) - - # Set sub-handle values through NonHierarchyIndexableObject.__setitem__ - dut.array_2d[0] = [0xBA, 0xBE, 0xCA, 0xFE] - dut.array_2d[1][29] = 0x12 - - await Timer(1000, 'ns') - - _check_value(tlog, dut.array_2d[0][31], 0xBA) - _check_value(tlog, dut.array_2d[0][30], 0xBE) - _check_value(tlog, dut.array_2d[1] , [0xDE, 0xAD, 0x12, 0xEF]) - - -@cocotb.test(expect_error=AttributeError if cocotb.SIM_NAME.lower().startswith(("icarus", "ghdl")) else ()) -async def test_struct(dut): - """Test setting and getting values of structs.""" - cocotb.fork(Clock(dut.clk, 1000, 'ns').start()) - dut.inout_if.a_in <= 1 - await Timer(1000, 'ns') - _check_value(tlog, dut.inout_if.a_in, 1) - dut.inout_if.a_in <= 0 - await Timer(1000, 'ns') - _check_value(tlog, dut.inout_if.a_in, 0) - - -@contextlib.contextmanager -def assert_raises(exc_type): - try: - yield - except exc_type as exc: - tlog.info(" {} raised as expected: {}".format(exc_type.__name__, exc)) - else: - raise AssertionError("{} was not raised".format(exc_type.__name__)) - - -@cocotb.test() -async def test_exceptions(dut): - """Test that correct Exceptions are raised.""" - with assert_raises(TypeError): - dut.array_7_downto_4 <= (0xF0, 0xE0, 0xD0, 0xC0) - with assert_raises(TypeError): - dut.array_4_to_7 = Exception("Exception Object") - with assert_raises(ValueError): - dut.array_3_downto_0 <= [0x70, 0x60, 0x50] - with assert_raises(ValueError): - dut.array_0_to_3 <= [0x40, 0x30, 0x20, 0x10, 0x00] diff --git a/tests/test_cases/test_cocotb/Makefile b/tests/test_cases/test_cocotb/Makefile deleted file mode 100644 index 3a3519b6..00000000 --- a/tests/test_cases/test_cocotb/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause - -include ../../designs/sample_module/Makefile - -MODULE := "\ - test_deprecated,\ - test_synchronization_primitives,\ - test_concurrency_primitives,\ - test_tests,\ - test_testfactory,\ - test_generator_coroutines,\ - test_timing_triggers,\ - test_scheduler,\ - test_clock,\ - test_edge_triggers,\ - test_async_coroutines,\ - test_handle,\ - test_logging,\ - test_pytest,\ - " - -ifeq ($(shell python -c "import sys; print(sys.version_info >= (3, 6))"), "True") -MODULE += test_async_generators, -endif diff --git a/tests/test_cases/test_cocotb/common.py b/tests/test_cases/test_cocotb/common.py deleted file mode 100644 index ff76b86f..00000000 --- a/tests/test_cases/test_cocotb/common.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -""" -Common utilities shared by many tests in this directory -""" -import re -import traceback -from contextlib import contextmanager - -from cocotb.triggers import Timer - - -async def clock_gen(clock): - """Example clock gen for test use""" - for i in range(5): - clock <= 0 - await Timer(100, "ns") - clock <= 1 - await Timer(100, "ns") - clock._log.warning("Clock generator finished!") - - -async def _check_traceback(running_coro, exc_type, pattern): - try: - await running_coro - except exc_type: - tb_text = traceback.format_exc() - else: - assert False, "Exception was not raised" - - assert re.match(pattern, tb_text), ( - "Traceback didn't match - got:\n\n" - "{}\n" - "which did not match the pattern:\n\n" - "{}" - ).format(tb_text, pattern) - - -@contextmanager -def assert_raises(exc_type, pattern=None): - try: - yield - except exc_type as e: - if pattern: - assert re.match(pattern, str(e)), \ - "Correct exception type caught, but message did not match pattern" - pass - else: - assert False, "{} was not raised".format(exc_type.__name__) diff --git a/tests/test_cases/test_cocotb/test_async_coroutines.py b/tests/test_cases/test_cocotb/test_async_coroutines.py deleted file mode 100644 index f435af11..00000000 --- a/tests/test_cases/test_cocotb/test_async_coroutines.py +++ /dev/null @@ -1,160 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -""" -Test function and substitutability of async coroutines -""" - -import cocotb -from cocotb.triggers import Timer -from cocotb.outcomes import Value, Error -from common import assert_raises - - -class produce: - """ Test helpers that produce a value / exception in different ways """ - @staticmethod - @cocotb.coroutine # testing legacy coroutine against async func - def coro(outcome): - yield Timer(1) - return outcome.get() - - @staticmethod - @cocotb.coroutine # testing coroutine decorator on async func - async def async_annotated(outcome): - await Timer(1) - return outcome.get() - - @staticmethod - async def async_(outcome): - await Timer(1) - return outcome.get() - - -class SomeException(Exception): - """ Custom exception to test for that can't be thrown by internals """ - pass - - -@cocotb.test() # test yielding decorated async coroutine in legacy coroutine -def test_annotated_async_from_coro(dut): - """ - Test that normal coroutines are able to call async functions annotated - with `@cocotb.coroutine` - """ - v = yield produce.async_annotated(Value(1)) - assert v == 1 - - try: - yield produce.async_annotated(Error(SomeException)) - except SomeException: - pass - else: - assert False - - -@cocotb.test() -async def test_annotated_async_from_async(dut): - """ Test that async coroutines are able to call themselves """ - v = await produce.async_annotated(Value(1)) - assert v == 1 - - try: - await produce.async_annotated(Error(SomeException)) - except SomeException: - pass - else: - assert False - - -@cocotb.test() -async def test_async_from_async(dut): - """ Test that async coroutines are able to call raw async functions """ - v = await produce.async_(Value(1)) - assert v == 1 - - try: - await produce.async_(Error(SomeException)) - except SomeException: - pass - else: - assert False - - -@cocotb.test() -async def test_coro_from_async(dut): - """ Test that async coroutines are able to call regular ones """ - v = await produce.coro(Value(1)) - assert v == 1 - - try: - await produce.coro(Error(SomeException)) - except SomeException: - pass - else: - assert False - - -@cocotb.test() -async def test_trigger_await_gives_self(dut): - """ Test that await returns the trigger itself for triggers """ - t = Timer(1) - t2 = await t - assert t2 is t - - -@cocotb.test() -async def test_await_causes_start(dut): - """ Test that an annotated async coroutine gets marked as started """ - coro = produce.async_annotated(Value(1)) - assert not coro.has_started() - await coro - assert coro.has_started() - - -@cocotb.test() # test forking undecorated async coroutine in legacy coroutine -def test_undecorated_coroutine_fork(dut): - ran = False - - async def example(): - nonlocal ran - await cocotb.triggers.Timer(1, 'ns') - ran = True - - yield cocotb.fork(example()).join() - assert ran - - -@cocotb.test() # test yielding undecorated async coroutine in legacy coroutine -def test_undecorated_coroutine_yield(dut): - ran = False - - async def example(): - nonlocal ran - await cocotb.triggers.Timer(1, 'ns') - ran = True - - yield example() - assert ran - - -@cocotb.test() -async def test_fork_coroutine_function_exception(dut): - async def coro(): - pass - - pattern = "Coroutine function {} should be called " \ - "prior to being scheduled.".format(coro) - with assert_raises(TypeError, pattern): - cocotb.fork(coro) - - -@cocotb.test() -async def test_task_coroutine_function_exception(dut): - async def coro(dut): - pass - - pattern = "Coroutine function {} should be called " \ - "prior to being scheduled.".format(coro) - with assert_raises(TypeError, pattern): - cocotb.decorators.RunningTask(coro) diff --git a/tests/test_cases/test_cocotb/test_async_generators.py b/tests/test_cases/test_cocotb/test_async_generators.py deleted file mode 100644 index 78481b97..00000000 --- a/tests/test_cases/test_cocotb/test_async_generators.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause - -import cocotb - - -async def whoops_async_generator(): - # the user should have used `await` here, but they wrote `yield` by accident. - yield cocotb.triggers.Timer(1) - - -@cocotb.test() # testing async generator in legacy coroutine syntax -def test_yielding_accidental_async_generator(dut): - # this test deliberately does not use `async def`, as we are testing the behavior of `yield` - try: - yield whoops_async_generator() - except TypeError as e: - assert "async generator" in str(e) - else: - assert False, "should have thrown" - - -@cocotb.test() -async def test_forking_accidental_async_generator(dut): - try: - cocotb.fork(whoops_async_generator()) - except TypeError as e: - assert "async generator" in str(e) - else: - assert False, "should have thrown" - - -@cocotb.coroutine # testing cocotb.coroutine decorated async generator -async def whoops_async_generator_decorated(): - yield cocotb.triggers.Timer(1) - - -@cocotb.test() -async def test_decorating_accidental_async_generator(dut): - try: - await whoops_async_generator_decorated() - except TypeError as e: - assert "async generator" in str(e) - else: - assert False, "should have thrown" diff --git a/tests/test_cases/test_cocotb/test_clock.py b/tests/test_cases/test_cocotb/test_clock.py deleted file mode 100644 index 3fc28e7e..00000000 --- a/tests/test_cases/test_cocotb/test_clock.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -""" -Tests relating to cocotb.clock.Clock -""" -import cocotb -from cocotb.clock import Clock -from cocotb.triggers import Timer, RisingEdge -from cocotb.utils import get_sim_time -from math import isclose - - -@cocotb.test() -async def test_clock_with_units(dut): - clk_1mhz = Clock(dut.clk, 1.0, units='us') - clk_250mhz = Clock(dut.clk, 4.0, units='ns') - - assert str(clk_1mhz) == "Clock(1.0 MHz)" - dut._log.info('Created clock >{}<'.format(str(clk_1mhz))) - - assert str(clk_250mhz) == "Clock(250.0 MHz)" - dut._log.info('Created clock >{}<'.format(str(clk_250mhz))) - - clk_gen = cocotb.fork(clk_1mhz.start()) - - start_time_ns = get_sim_time(units='ns') - - await Timer(1, "ns") - await RisingEdge(dut.clk) - - edge_time_ns = get_sim_time(units='ns') - assert isclose(edge_time_ns, start_time_ns + 1000.0), "Expected a period of 1 us" - - start_time_ns = edge_time_ns - - await RisingEdge(dut.clk) - edge_time_ns = get_sim_time(units='ns') - assert isclose(edge_time_ns, start_time_ns + 1000.0), "Expected a period of 1 us" - - clk_gen.kill() - - clk_gen = cocotb.fork(clk_250mhz.start()) - - start_time_ns = get_sim_time(units='ns') - - await Timer(1, "ns") - await RisingEdge(dut.clk) - - edge_time_ns = get_sim_time(units='ns') - assert isclose(edge_time_ns, start_time_ns + 4.0), "Expected a period of 4 ns" - - start_time_ns = edge_time_ns - - await RisingEdge(dut.clk) - edge_time_ns = get_sim_time(units='ns') - assert isclose(edge_time_ns, start_time_ns + 4.0), "Expected a period of 4 ns" - - clk_gen.kill() - - -@cocotb.test() -async def test_external_clock(dut): - """Test awaiting on an external non-cocotb coroutine decorated function""" - clk_gen = cocotb.fork(Clock(dut.clk, 100, "ns").start()) - count = 0 - while count != 100: - await RisingEdge(dut.clk) - count += 1 - clk_gen.kill() diff --git a/tests/test_cases/test_cocotb/test_concurrency_primitives.py b/tests/test_cases/test_cocotb/test_concurrency_primitives.py deleted file mode 100644 index 938118f9..00000000 --- a/tests/test_cases/test_cocotb/test_concurrency_primitives.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -""" -Tests for concurrency primitives like First and Combine -""" -import cocotb -from cocotb.triggers import Timer, First, Event, Combine -import textwrap -from common import _check_traceback - - -@cocotb.test() -async def test_unfired_first_triggers(dut): - """ Test that un-fired trigger(s) in First don't later cause a spurious wakeup """ - # gh-843 - events = [Event() for i in range(3)] - - waiters = [e.wait() for e in events] - - async def wait_for_firsts(): - ret_i = waiters.index((await First(waiters[0], waiters[1]))) - assert ret_i == 0, "Expected event 0 to fire, not {}".format(ret_i) - - ret_i = waiters.index((await First(waiters[2]))) - assert ret_i == 2, "Expected event 2 to fire, not {}".format(ret_i) - - async def wait_for_e1(): - """ wait on the event that didn't wake `wait_for_firsts` """ - ret_i = waiters.index((await waiters[1])) - assert ret_i == 1, "Expected event 1 to fire, not {}".format(ret_i) - - async def fire_events(): - """ fire the events in order """ - for e in events: - await Timer(1, "ns") - e.set() - - fire_task = cocotb.fork(fire_events()) - e1_task = cocotb.fork(wait_for_e1()) - await wait_for_firsts() - - # make sure the other tasks finish - await fire_task.join() - await e1_task.join() - - -@cocotb.test() -async def test_nested_first(dut): - """ Test that nested First triggers behave as expected """ - events = [Event() for i in range(3)] - waiters = [e.wait() for e in events] - - async def fire_events(): - """ fire the events in order """ - for e in events: - await Timer(1, "ns") - e.set() - - async def wait_for_nested_first(): - inner_first = First(waiters[0], waiters[1]) - ret = await First(inner_first, waiters[2]) - - # should unpack completely, rather than just by one level - assert ret is not inner_first - assert ret is waiters[0] - - fire_task = cocotb.fork(fire_events()) - await wait_for_nested_first() - await fire_task.join() - - -@cocotb.test() -async def test_first_does_not_kill(dut): - """ Test that `First` does not kill coroutines that did not finish first """ - ran = False - - @cocotb.coroutine # decorating `async def` is required to use `First` - async def coro(): - nonlocal ran - await Timer(2, units='ns') - ran = True - - # Coroutine runs for 2ns, so we expect the timer to fire first - timer = Timer(1, units='ns') - t = await First(timer, coro()) - assert t is timer - assert not ran - - # the background routine is still running, but should finish after 1ns - await Timer(2, units='ns') - - assert ran - - -@cocotb.test() -async def test_exceptions_first(dut): - """ Test exception propagation via cocotb.triggers.First """ - - @cocotb.coroutine # decorating `async def` is required to use `First` - def raise_inner(): - yield Timer(10, "ns") - raise ValueError('It is soon now') - - async def raise_soon(): - await Timer(1, "ns") - await cocotb.triggers.First(raise_inner()) - - # it's ok to change this value if the traceback changes - just make sure - # that when changed, it doesn't become harder to read. - expected = textwrap.dedent(r""" - Traceback \(most recent call last\): - File ".*common\.py", line \d+, in _check_traceback - await running_coro - File ".*test_concurrency_primitives\.py", line \d+, in raise_soon - await cocotb\.triggers\.First\(raise_inner\(\)\) - File ".*triggers\.py", line \d+, in _wait - return await first_trigger[^\n]* - File ".*triggers.py", line \d+, in __await__ - return \(yield self\) - File ".*triggers.py", line \d+, in __await__ - return \(yield self\) - File ".*test_concurrency_primitives\.py", line \d+, in raise_inner - raise ValueError\('It is soon now'\) - ValueError: It is soon now""").strip() - - await _check_traceback(raise_soon(), ValueError, expected) - - -@cocotb.test() -async def test_combine(dut): - """ Test the Combine trigger. """ - # gh-852 - - async def do_something(delay): - await Timer(delay, "ns") - - crs = [cocotb.fork(do_something(dly)) for dly in [10, 30, 20]] - - await Combine(*(cr.join() for cr in crs)) - - -@cocotb.test() -async def test_event_is_set(dut): - e = Event() - - assert not e.is_set() - e.set() - assert e.is_set() - e.clear() - assert not e.is_set() diff --git a/tests/test_cases/test_cocotb/test_deprecated.py b/tests/test_cases/test_cocotb/test_deprecated.py deleted file mode 100644 index da0c464a..00000000 --- a/tests/test_cases/test_cocotb/test_deprecated.py +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -import cocotb -from cocotb.triggers import Timer -from cocotb.binary import BinaryValue -import warnings -import ctypes -from contextlib import contextmanager -from common import assert_raises - - -@contextmanager -def assert_deprecated(warning_category=DeprecationWarning): - warns = [] - try: - with warnings.catch_warnings(record=True) as warns: - # Cause all warnings to always be triggered. - warnings.simplefilter("always") - yield warns # note: not a cocotb yield, but a contextlib one! - finally: - assert len(warns) >= 1 - msg = "Expected {}".format(warning_category.__qualname__) - assert issubclass(warns[0].category, warning_category), msg - - -@cocotb.test() -async def test_returnvalue_deprecated(dut): - - @cocotb.coroutine # testing ReturnValue deprecated - def get_value(): - yield cocotb.triggers.Timer(1, units='ns') - raise cocotb.result.ReturnValue(42) - - with assert_deprecated() as warns: - val = await get_value() - assert val == 42 - assert "return statement instead" in str(warns[0].message) - - -# strings are not supported on Icarus -@cocotb.test(skip=cocotb.SIM_NAME.lower().startswith("icarus")) -async def test_unicode_handle_assignment_deprecated(dut): - with assert_deprecated() as warns: - dut.stream_in_string <= "Bad idea" - await cocotb.triggers.ReadWrite() - assert "bytes" in str(warns[0].message) - - -@cocotb.test() -async def test_convert_handle_to_string_deprecated(dut): - dut.stream_in_data <= 0 - await cocotb.triggers.Timer(1, units='ns') - - with assert_deprecated(FutureWarning) as warns: - as_str = str(dut.stream_in_data) - assert "_path" in str(warns[0].message) - - # in future this will be ` == dut._path` - assert as_str == str(dut.stream_in_data.value) - - if cocotb.LANGUAGE == "verilog": - # the `NUM_OF_MODULES` parameter is only present in the verilog design - with assert_deprecated(FutureWarning) as warns: - as_str = str(dut.NUM_OF_MODULES) - - assert "_path" in str(warns[0].message) - - # in future this will be ` == dut._path` - assert as_str == str(dut.NUM_OF_MODULES.value) - - -@cocotb.test() -async def test_create_error_deprecated(dut): - with assert_deprecated(): - _ = cocotb.result.create_error(cocotb.triggers.Timer(1), "A test exception") - - -@cocotb.test() -async def test_raise_error_deprecated(dut): - with assert_deprecated(): - with assert_raises(cocotb.result.TestError): - cocotb.result.raise_error(cocotb.triggers.Timer(1), "A test exception") - - -@cocotb.test() -async def test_hook_deprecated(_): - async def example(): - pass - with assert_deprecated(): - cocotb.hook()(example) - - -@cocotb.test() -async def test_handle_compat_mapping(dut): - """ - Test DeprecationWarnings for _compat_mapping. - - Note that these only warn once per attribute. - """ - # log - with assert_deprecated(): - dut.log.info("'log' is deprecated") - # name - with assert_deprecated(): - dut.name = "myname" - assert dut.name == "myname" - # fullname - with assert_deprecated(): - dut.fullname = "myfullname" - assert dut.fullname == "myfullname" - - -@cocotb.test() -async def test_assigning_structure_deprecated(dut): - """signal <= ctypes.Structure assignment is deprecated""" - - class Example(ctypes.Structure): - _fields_ = [ - ("a", ctypes.c_byte), - ("b", ctypes.c_uint32)] - - e = Example(a=0xCC, b=0x12345678) - - with assert_deprecated(): - dut.stream_in_data_wide <= e - - await Timer(1, 'step') - - assert dut.stream_in_data_wide == BinaryValue(value=bytes(e), n_bits=len(dut.stream_in_data_wide)) - - -@cocotb.test() -async def test_expect_error_bool_deprecated(_): - async def t(): - pass - with assert_deprecated(): - cocotb.test(expect_error=True)(t) - with assert_deprecated(): - cocotb.test(expect_error=False)(t) - - -@cocotb.test() -async def test_time_ps_deprecated(_): - with assert_deprecated(): - Timer(time_ps=7, units='ns') - with assert_raises(TypeError): - Timer(time=0, time_ps=7, units='ns') - with assert_raises(TypeError): - Timer(units='ps') diff --git a/tests/test_cases/test_cocotb/test_edge_triggers.py b/tests/test_cases/test_cocotb/test_edge_triggers.py deleted file mode 100644 index 4c21b1e9..00000000 --- a/tests/test_cases/test_cocotb/test_edge_triggers.py +++ /dev/null @@ -1,235 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -""" -Tests for edge triggers - -* Edge -* RisingEdge -* FallingEdge -* ClockCycles -""" -import cocotb -from cocotb.triggers import RisingEdge, FallingEdge, Edge, Timer, ClockCycles, First, Combine -from cocotb.clock import Clock -from cocotb.result import SimTimeoutError - - -async def count_edges_cycles(signal, edges): - edge = RisingEdge(signal) - for i in range(edges): - await edge - signal._log.info("Rising edge %d detected" % i) - signal._log.info("Finished, returning %d" % edges) - return edges - - -async def do_single_edge_check(dut, level): - """Do test for rising edge""" - old_value = dut.clk.value.integer - dut._log.info("Value of %s is %d" % (dut.clk._path, old_value)) - assert old_value != level, "%s not to %d start with" % (dut.clk._path, not level) - if level == 1: - await RisingEdge(dut.clk) - else: - await FallingEdge(dut.clk) - new_value = dut.clk.value.integer - dut._log.info("Value of %s is %d" % (dut.clk._path, new_value)) - assert new_value == level, "%s not %d at end" % (dut.clk._path, level) - - -@cocotb.test() -async def test_rising_edge(dut): - """Test that a rising edge can be awaited on""" - dut.clk <= 0 - await Timer(1, "ns") - test = cocotb.fork(do_single_edge_check(dut, 1)) - await Timer(10, "ns") - dut.clk <= 1 - fail_timer = Timer(1000, "ns") - result = await First(fail_timer, test.join()) - assert result is not fail_timer, "Test timed out" - - -@cocotb.test() -async def test_falling_edge(dut): - """Test that a falling edge can be awaited on""" - dut.clk <= 1 - await Timer(1, "ns") - test = cocotb.fork(do_single_edge_check(dut, 0)) - await Timer(10, "ns") - dut.clk <= 0 - fail_timer = Timer(1000, "ns") - result = await First(fail_timer, test.join()) - assert result is not fail_timer, "Test timed out" - - -@cocotb.test() -async def test_either_edge(dut): - """Test that either edge can be triggered on""" - dut.clk <= 0 - await Timer(1, "ns") - dut.clk <= 1 - await Edge(dut.clk) - assert dut.clk.value.integer == 1 - await Timer(10, "ns") - dut.clk <= 0 - await Edge(dut.clk) - assert dut.clk.value.integer == 0 - await Timer(10, "ns") - dut.clk <= 1 - await Edge(dut.clk) - assert dut.clk.value.integer == 1 - await Timer(10, "ns") - dut.clk <= 0 - await Edge(dut.clk) - assert dut.clk.value.integer == 0 - await Timer(10, "ns") - dut.clk <= 1 - await Edge(dut.clk) - assert dut.clk.value.integer == 1 - await Timer(10, "ns") - dut.clk <= 0 - await Edge(dut.clk) - assert dut.clk.value.integer == 0 - - -@cocotb.test() -async def test_fork_and_monitor(dut, period=1000, clocks=6): - cocotb.fork(Clock(dut.clk, period, "ns").start()) - - # Ensure the clock has started - await RisingEdge(dut.clk) - - timer = Timer(period + 10, "ns") - task = cocotb.fork(count_edges_cycles(dut.clk, clocks)) - count = 0 - expect = clocks - 1 - - while True: - result = await First(timer, task.join()) - assert count <= expect, "Task didn't complete in expected time" - if result is timer: - dut._log.info("Count %d: Task still running" % count) - count += 1 - else: - break - assert count == expect, "Expected to monitor the task %d times but got %d" % (expect, count) - assert result == clocks, "Expected task to return %d but got %s" % (clocks, repr(result)) - - -async def do_clock(dut, limit, period): - """Simple clock with a limit""" - wait_period = period / 2 - while limit: - await Timer(wait_period, "ns") - dut.clk <= 0 - await Timer(wait_period, "ns") - dut.clk <= 1 - limit -= 1 - - -async def do_edge_count(dut, signal): - """Count the edges""" - global edges_seen - while True: - await RisingEdge(signal) - edges_seen += 1 - - -@cocotb.test() -async def test_edge_count(dut): - """Count the number of edges is as expected""" - global edges_seen - edges_seen = 0 - clk_period = 100 - edge_count = 10 - clock = cocotb.fork(do_clock(dut, edge_count, clk_period)) - test = cocotb.fork(do_edge_count(dut, dut.clk)) - - await Timer(clk_period * (edge_count + 1), "ns") - assert edge_count == edges_seen, "Correct edge count failed - saw %d, wanted %d" % (edges_seen, edge_count) - - -@cocotb.test() -async def test_edge_identity(dut): - """ - Test that Edge triggers returns the same object each time - """ - - re = RisingEdge(dut.clk) - fe = FallingEdge(dut.clk) - e = Edge(dut.clk) - - assert re is RisingEdge(dut.clk) - assert fe is FallingEdge(dut.clk) - assert e is Edge(dut.clk) - - # check they are all unique - assert len({re, fe, e}) == 3 - await Timer(1, "ns") - - -@cocotb.test() -async def test_singleton_isinstance(dut): - """ - Test that the result of trigger expression have a predictable type - """ - assert isinstance(RisingEdge(dut.clk), RisingEdge) - assert isinstance(FallingEdge(dut.clk), FallingEdge) - assert isinstance(Edge(dut.clk), Edge) - - await Timer(1, "ns") - - -@cocotb.test() -async def test_clock_cycles(dut): - """ - Test the ClockCycles Trigger - """ - clk = dut.clk - clk_gen = cocotb.fork(Clock(clk, 100, "ns").start()) - await RisingEdge(clk) - dut._log.info("After one edge") - await ClockCycles(clk, 10) - dut._log.info("After 10 edges") - - -@cocotb.test() -async def test_clock_cycles_forked(dut): - """ Test that ClockCycles can be used in forked coroutines """ - # gh-520 - - clk_gen = cocotb.fork(Clock(dut.clk, 100, "ns").start()) - - async def wait_ten(): - await ClockCycles(dut.clk, 10) - - a = cocotb.fork(wait_ten()) - b = cocotb.fork(wait_ten()) - await a.join() - await b.join() - - -@cocotb.test( - timeout_time=100, - timeout_unit="ns", - expect_error=( - SimTimeoutError if ( - cocotb.LANGUAGE in ["verilog"] and - cocotb.SIM_NAME.lower().startswith(("riviera", "aldec")) # gh-2344 - ) - else () - ), -) -async def test_both_edge_triggers(dut): - async def wait_rising_edge(): - await RisingEdge(dut.clk) - - async def wait_falling_edge(): - await FallingEdge(dut.clk) - - rising_coro = cocotb.fork(wait_rising_edge()) - falling_coro = cocotb.fork(wait_falling_edge()) - cocotb.fork(Clock(dut.clk, 10, units='ns').start()) - await Combine(rising_coro, falling_coro) diff --git a/tests/test_cases/test_cocotb/test_generator_coroutines.py b/tests/test_cases/test_cocotb/test_generator_coroutines.py deleted file mode 100644 index 420b251e..00000000 --- a/tests/test_cases/test_cocotb/test_generator_coroutines.py +++ /dev/null @@ -1,217 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -""" -Tests that specifically test generator-based coroutines -""" -import cocotb -from cocotb.triggers import Timer, NullTrigger -from common import clock_gen, _check_traceback, assert_raises -import textwrap - - -# Tests relating to providing meaningful errors if we forget to use the -# yield keyword correctly to turn a function into a coroutine - -@cocotb.test(expect_error=TypeError) -def test_not_a_coroutine(dut): - """Example of a failing to use the yield keyword in a test""" - dut._log.warning("This test will fail because we don't yield anything") - - -@cocotb.coroutine -def function_not_a_coroutine(): - """If we don't yield, this isn't a coroutine""" - return "This should fail" - - -@cocotb.test() -def test_function_not_a_coroutine(dut): - """Example of trying to yield a coroutine that isn't a coroutine""" - yield Timer(500) - try: - # failure should occur before we even try to yield or fork the coroutine - coro = function_not_a_coroutine() - except TypeError as exc: - assert "isn't a valid coroutine" in str(exc) - else: - assert False - - -def normal_function(dut): - return True - - -@cocotb.test() -def test_function_not_decorated(dut): - try: - yield normal_function(dut) - except TypeError as exc: - assert "yielded" in str(exc) - assert "scheduler can't handle" in str(exc) - else: - assert False - - -@cocotb.test() -def test_function_not_decorated_fork(dut): - """Example of trying to fork a coroutine that isn't a coroutine""" - yield Timer(500) - try: - cocotb.fork(normal_function(dut)) - except TypeError as exc: - assert "isn't a coroutine" in str(exc) - else: - assert False - - yield Timer(500) - - -@cocotb.coroutine -def example(): - yield NullTrigger() - - -@cocotb.test() -def test_adding_a_coroutine_without_starting(dut): - """Catch (and provide useful error) for attempts to fork coroutines - incorrectly""" - yield Timer(100) - try: - cocotb.fork(example) - except TypeError as exc: - assert "a coroutine that hasn't started" in str(exc) - else: - assert False - - -@cocotb.test(expect_fail=False) -def test_yield_list(dut): - """Example of yielding on a list of triggers""" - clock = dut.clk - cocotb.scheduler.add(clock_gen(clock)) - yield [Timer(1000), Timer(2000)] - - yield Timer(10000) - - -@cocotb.coroutine -def erroring_coro(): - yield Timer(100) - fail # noqa - - -@cocotb.test() -def test_coroutine_error(dut): - """Error in a coroutine that we yield""" - yield clock_gen(dut.clk) - with assert_raises(NameError): - yield erroring_coro() - - -@cocotb.test(expect_error=NameError) -def test_fork_error(dut): - """Error in a coroutine that we fork""" - yield clock_gen(dut.clk) - cocotb.fork(erroring_coro()) - yield clock_gen(dut.clk) - - -@cocotb.test() -def test_coroutine_return(dut): - """ Test that the Python 3.3 syntax for returning from generators works """ - @cocotb.coroutine - def return_it(x): - return x - - # this makes `return_it` a coroutine - yield - - ret = yield return_it(42) - assert ret == 42, "Return statement did not work" - - -@cocotb.test() -def test_immediate_coro(dut): - """ - Test that coroutines can return immediately - """ - @cocotb.coroutine - def immediate_value(): - return 42 - yield - - @cocotb.coroutine - def immediate_exception(): - raise ValueError - yield - - assert (yield immediate_value()) == 42 - - try: - yield immediate_exception() - except ValueError: - pass - else: - assert False, "Exception was not raised" - - -@cocotb.test() -def test_exceptions_direct(dut): - """ Test exception propagation via a direct yield statement """ - @cocotb.coroutine - def raise_inner(): - yield Timer(10) - raise ValueError('It is soon now') - - @cocotb.coroutine - def raise_soon(): - yield Timer(1) - yield raise_inner() - - # it's ok to change this value if the traceback changes - just make sure - # that when changed, it doesn't become harder to read. - expected = textwrap.dedent(r""" - Traceback \(most recent call last\): - File ".*common\.py", line \d+, in _check_traceback - await running_coro - File ".*cocotb[\\\/]decorators.py", line \d+, in __await__ - return \(yield self\) - File ".*test_generator_coroutines\.py", line \d+, in raise_soon - yield raise_inner\(\) - File ".*test_generator_coroutines\.py", line \d+, in raise_inner - raise ValueError\('It is soon now'\) - ValueError: It is soon now""").strip() - - yield _check_traceback(raise_soon(), ValueError, expected) - - -@cocotb.test() -def test_exceptions_forked(dut): - """ Test exception propagation via cocotb.fork """ - @cocotb.coroutine - def raise_inner(): - yield Timer(10) - raise ValueError('It is soon now') - - @cocotb.coroutine - def raise_soon(): - yield Timer(1) - coro = cocotb.fork(raise_inner()) - yield coro.join() - - # it's ok to change this value if the traceback changes - just make sure - # that when changed, it doesn't become harder to read. - expected = textwrap.dedent(r""" - Traceback \(most recent call last\): - File ".*common\.py", line \d+, in _check_traceback - await running_coro - File ".*cocotb[\\\/]decorators.py", line \d+, in __await__ - return \(yield self\) - File ".*test_generator_coroutines\.py", line \d+, in raise_soon - yield coro\.join\(\) - File ".*test_generator_coroutines\.py", line \d+, in raise_inner - raise ValueError\('It is soon now'\) - ValueError: It is soon now""").strip() - - yield _check_traceback(raise_soon(), ValueError, expected) diff --git a/tests/test_cases/test_cocotb/test_handle.py b/tests/test_cases/test_cocotb/test_handle.py deleted file mode 100644 index 260f5173..00000000 --- a/tests/test_cases/test_cocotb/test_handle.py +++ /dev/null @@ -1,318 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -""" -Tests for handles -""" -import logging -import random -import cocotb -from cocotb.triggers import Timer - -from common import assert_raises -from cocotb.handle import _Limits - - -@cocotb.test() -async def test_lessthan_raises_error(dut): - """ - Test that trying to use <= as if it were a comparison produces an error - """ - ret = dut.stream_in_data <= 0x12 - try: - bool(ret) - except TypeError: - pass - else: - assert False, "No exception was raised when confusing comparison with assignment" - - -@cocotb.test() -async def test_bad_attr(dut): - - with assert_raises(AttributeError): - fake_signal = dut.fake_signal - - try: - _ = dut.stream_in_data.whoops - except AttributeError as e: - assert 'whoops' in str(e) - else: - assert False, "Expected AttributeError" - - -# strings are not supported on Icarus -@cocotb.test(skip=cocotb.SIM_NAME.lower().startswith("icarus")) -async def test_string_handle_takes_bytes(dut): - dut.stream_in_string.value = b"bytes" - await cocotb.triggers.Timer(10, 'ns') - val = dut.stream_in_string.value - assert isinstance(val, bytes) - assert val == b"bytes" - - -async def test_delayed_assignment_still_errors(dut): - """ Writing a bad value should fail even if the write is scheduled to happen later """ - - # note: all these fail because BinaryValue.assign rejects them - - with assert_raises(ValueError): - dut.stream_in_int.setimmediatevalue("1010 not a real binary string") - with assert_raises(TypeError): - dut.stream_in_int.setimmediatevalue([]) - - with assert_raises(ValueError): - dut.stream_in_int <= "1010 not a real binary string" - with assert_raises(TypeError): - dut.stream_in_int <= [] - - -async def int_values_test(signal, n_bits, limits=_Limits.VECTOR_NBIT): - """Test integer access to a signal.""" - log = logging.getLogger("cocotb.test") - values = gen_int_test_values(n_bits, limits) - for val in values: - signal <= val - await Timer(1, 'ns') - - if limits == _Limits.VECTOR_NBIT: - if val < 0: - got = signal.value.signed_integer - else: - got = signal.value.integer - else: - got = signal.value - - assert got == val, "Expected value {}, got value {}!".format(val, got) - - -def gen_int_test_values(n_bits, limits=_Limits.VECTOR_NBIT): - """Generates a list of int test values for a given number of bits.""" - unsigned_min = 0 - unsigned_max = 2**n_bits-1 - signed_min = -2**(n_bits-1) - signed_max = 2**(n_bits-1)-1 - - if limits == _Limits.VECTOR_NBIT: - return [1, -1, 4, -4, unsigned_min, unsigned_max, signed_min, signed_max] - elif limits == _Limits.SIGNED_NBIT: - return [1, -1, 4, -4, signed_min, signed_max] - else: - return [1, -1, 4, -4, unsigned_min, unsigned_max] - - -async def int_overflow_test(signal, n_bits, test_mode, limits=_Limits.VECTOR_NBIT): - """Test integer overflow.""" - if test_mode == "ovfl": - value = gen_int_ovfl_value(n_bits, limits) - elif test_mode == "unfl": - value = gen_int_unfl_value(n_bits, limits) - else: - value = None - - with assert_raises(OverflowError): - signal <= value - - -def gen_int_ovfl_value(n_bits, limits=_Limits.VECTOR_NBIT): - unsigned_max = 2**n_bits-1 - signed_max = 2**(n_bits-1)-1 - - if limits == _Limits.SIGNED_NBIT: - return signed_max + 1 - elif limits == _Limits.UNSIGNED_NBIT: - return unsigned_max + 1 - else: - return unsigned_max + 1 - - -def gen_int_unfl_value(n_bits, limits=_Limits.VECTOR_NBIT): - unsigned_min = 0 - signed_min = -2**(n_bits-1) - - if limits == _Limits.SIGNED_NBIT: - return signed_min - 1 - elif limits == _Limits.UNSIGNED_NBIT: - return unsigned_min - 1 - else: - return signed_min - 1 - - -@cocotb.test() -async def test_int_8bit(dut): - """Test int access to 8-bit vector.""" - await int_values_test(dut.stream_in_data, len(dut.stream_in_data)) - - -@cocotb.test() -async def test_int_8bit_overflow(dut): - """Test 8-bit vector overflow.""" - await int_overflow_test(dut.stream_in_data, len(dut.stream_in_data), "ovfl") - - -@cocotb.test() -async def test_int_8bit_underflow(dut): - """Test 8-bit vector underflow.""" - await int_overflow_test(dut.stream_in_data, len(dut.stream_in_data), "unfl") - - -@cocotb.test() -async def test_int_32bit(dut): - """Test int access to 32-bit vector.""" - await int_values_test(dut.stream_in_data_dword, len(dut.stream_in_data_dword)) - - -@cocotb.test() -async def test_int_32bit_overflow(dut): - """Test 32-bit vector overflow.""" - await int_overflow_test(dut.stream_in_data_dword, len(dut.stream_in_data_dword), "ovfl") - - -@cocotb.test() -async def test_int_32bit_underflow(dut): - """Test 32-bit vector underflow.""" - await int_overflow_test(dut.stream_in_data_dword, len(dut.stream_in_data_dword), "unfl") - - -@cocotb.test() -async def test_int_39bit(dut): - """Test int access to 39-bit vector.""" - await int_values_test(dut.stream_in_data_39bit, len(dut.stream_in_data_39bit)) - - -@cocotb.test() -async def test_int_39bit_overflow(dut): - """Test 39-bit vector overflow.""" - await int_overflow_test(dut.stream_in_data_39bit, len(dut.stream_in_data_39bit), "ovfl") - - -@cocotb.test() -async def test_int_39bit_underflow(dut): - """Test 39-bit vector underflow.""" - await int_overflow_test(dut.stream_in_data_39bit, len(dut.stream_in_data_39bit), "unfl") - - -@cocotb.test() -async def test_int_64bit(dut): - """Test int access to 64-bit vector.""" - await int_values_test(dut.stream_in_data_wide, len(dut.stream_in_data_wide)) - - -@cocotb.test() -async def test_int_64bit_overflow(dut): - """Test 64-bit vector overflow.""" - await int_overflow_test(dut.stream_in_data_wide, len(dut.stream_in_data_wide), "ovfl") - - -@cocotb.test() -async def test_int_64bit_underflow(dut): - """Test 64-bit vector underflow.""" - await int_overflow_test(dut.stream_in_data_wide, len(dut.stream_in_data_wide), "unfl") - - -@cocotb.test() -async def test_int_128bit(dut): - """Test int access to 128-bit vector.""" - await int_values_test(dut.stream_in_data_dqword, len(dut.stream_in_data_dqword)) - - -@cocotb.test() -async def test_int_128bit_overflow(dut): - """Test 128-bit vector overflow.""" - await int_overflow_test(dut.stream_in_data_dqword, len(dut.stream_in_data_dqword), "ovfl") - - -@cocotb.test() -async def test_int_128bit_underflow(dut): - """Test 128-bit vector underflow.""" - await int_overflow_test(dut.stream_in_data_dqword, len(dut.stream_in_data_dqword), "unfl") - - -@cocotb.test(expect_error=AttributeError if cocotb.SIM_NAME in ["Icarus Verilog"] else ()) -async def test_integer(dut): - """Test access to integers.""" - if cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith("riviera"): - limits = _Limits.VECTOR_NBIT # stream_in_int is ModifiableObject in riviera, not IntegerObject - else: - limits = _Limits.SIGNED_NBIT - - await int_values_test(dut.stream_in_int, 32, limits) - - -@cocotb.test(expect_error=AttributeError if cocotb.SIM_NAME in ["Icarus Verilog"] else ()) -async def test_integer_overflow(dut): - """Test integer overflow.""" - if cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith("riviera"): - limits = _Limits.VECTOR_NBIT # stream_in_int is ModifiableObject in riviera, not IntegerObject - else: - limits = _Limits.SIGNED_NBIT - - await int_overflow_test(dut.stream_in_int, 32, "ovfl", limits) - - -@cocotb.test(expect_error=AttributeError if cocotb.SIM_NAME in ["Icarus Verilog"] else ()) -async def test_integer_underflow(dut): - """Test integer underflow.""" - if cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith("riviera"): - limits = _Limits.VECTOR_NBIT # stream_in_int is ModifiableObject in riviera, not IntegerObject - else: - limits = _Limits.SIGNED_NBIT - - await int_overflow_test(dut.stream_in_int, 32, "unfl", limits) - - -@cocotb.test(expect_error=AttributeError if cocotb.SIM_NAME in ["Icarus Verilog"] else ()) -async def test_real_assign_double(dut): - """ - Assign a random floating point value, read it back from the DUT and check - it matches what we assigned - """ - val = random.uniform(-1e307, 1e307) - log = logging.getLogger("cocotb.test") - timer_shortest = Timer(1, "step") - await timer_shortest - log.info("Setting the value %g" % val) - dut.stream_in_real = val - await timer_shortest - await timer_shortest # FIXME: Workaround for VHPI scheduling - needs investigation - got = float(dut.stream_out_real) - log.info("Read back value %g" % got) - assert got == val, "Values didn't match!" - - -@cocotb.test(expect_error=AttributeError if cocotb.SIM_NAME in ["Icarus Verilog"] else ()) -async def test_real_assign_int(dut): - """Assign a random integer value to ensure we can write types convertible to - int, read it back from the DUT and check it matches what we assigned. - """ - val = random.randint(-2**31, 2**31 - 1) - log = logging.getLogger("cocotb.test") - timer_shortest = Timer(1, "step") - await timer_shortest - log.info("Setting the value %i" % val) - dut.stream_in_real <= val - await timer_shortest - await timer_shortest # FIXME: Workaround for VHPI scheduling - needs investigation - got = dut.stream_out_real - log.info("Read back value %d" % got) - assert got == float(val), "Values didn't match!" - - -# identifiers starting with `_` are illegal in VHDL -@cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"]) -async def test_access_underscore_name(dut): - """Test accessing HDL name starting with an underscore""" - # direct access does not work because we consider such names cocotb-internal - with assert_raises(AttributeError): - dut._underscore_name - - # indirect access works - dut._id("_underscore_name", extended=False) <= 0 - await Timer(1, 'ns') - assert dut._id("_underscore_name", extended=False).value == 0 - dut._id("_underscore_name", extended=False) <= 1 - await Timer(1, 'ns') - assert dut._id("_underscore_name", extended=False).value == 1 - dut._id("_underscore_name", extended=False) <= 0 - await Timer(1, 'ns') - assert dut._id("_underscore_name", extended=False).value == 0 diff --git a/tests/test_cases/test_cocotb/test_logging.py b/tests/test_cases/test_cocotb/test_logging.py deleted file mode 100644 index a2b080bb..00000000 --- a/tests/test_cases/test_cocotb/test_logging.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -""" -Tests for the cocotb logger -""" - -import cocotb -import cocotb.log - -from common import assert_raises -import logging -import os - - -class StrCallCounter(object): - def __init__(self): - self.str_counter = 0 - - def __str__(self): - self.str_counter += 1 - return "__str__ called %d time(s)" % self.str_counter - - -@cocotb.test() -async def test_logging_with_args(dut): - counter = StrCallCounter() - dut._log.setLevel(logging.INFO) # To avoid logging debug message, to make next line run without error - dut._log.debug("%s", counter) - assert counter.str_counter == 0 - - dut._log.info("%s", counter) - assert counter.str_counter == 1 - - # now try again on the root cocotb logger, which unlike nested loggers - # is captured - counter = StrCallCounter() - cocotb.log.info("%s", counter) - assert counter.str_counter == 2 # once for stdout, once for captured logs - - dut._log.info("No substitution") - - dut._log.warning("Testing multiple line\nmessage") - - -@cocotb.test() -async def test_logging_default_config(dut): - # The cocotb.log module is shadowed by an instance of - # cocotb.log.SimBaseLog() - from cocotb.log import default_config as log_default_config - - cocotb_log = logging.getLogger('cocotb') - - # Save pre-test configuration - log_level_prev = cocotb_log.level - os_environ_prev = os.environ.copy() - - try: - # Set a valid log level - os.environ['COCOTB_LOG_LEVEL'] = 'DEBUG' - log_default_config() - assert cocotb_log.level == logging.DEBUG, cocotb_log.level - - # Try to set log level to an invalid log level - os.environ['COCOTB_LOG_LEVEL'] = 'INVALID_LOG_LEVEL' - with assert_raises(ValueError): - log_default_config() - - # Try to set log level to a valid log level with wrong capitalization - os.environ['COCOTB_LOG_LEVEL'] = 'error' - log_default_config() - assert cocotb_log.level == logging.ERROR, cocotb_log.level - - finally: - # Restore pre-test configuration - os.environ = os_environ_prev - cocotb_log.level = log_level_prev diff --git a/tests/test_cases/test_cocotb/test_pytest.py b/tests/test_cases/test_cocotb/test_pytest.py deleted file mode 100644 index b510e7ca..00000000 --- a/tests/test_cases/test_cocotb/test_pytest.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -""" Tests relating to pytest integration """ - -import cocotb - -# pytest is an optional dependency -try: - import pytest -except ImportError: - pytest = None - - -@cocotb.test(skip=pytest is None) -async def test_assertion_rewriting(dut): - """ Test that assertion rewriting hooks take effect in cocotb tests """ - try: - assert 1 != 42 - except AssertionError as e: - assert "42" in str(e), ( - "Assertion rewriting seems not to work, message was {}".format(e)) diff --git a/tests/test_cases/test_cocotb/test_scheduler.py b/tests/test_cases/test_cocotb/test_scheduler.py deleted file mode 100644 index ef27ffbf..00000000 --- a/tests/test_cases/test_cocotb/test_scheduler.py +++ /dev/null @@ -1,428 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -""" -Test for scheduler and coroutine behavior - -* fork -* join -* kill -""" -import logging -import re - -import cocotb -from cocotb.triggers import Join, Timer, RisingEdge, Trigger, NullTrigger, Combine, Event, ReadOnly, First -from cocotb.clock import Clock -from common import clock_gen - - -test_flag = False - - -async def clock_yield(generator): - global test_flag - await Join(generator) - test_flag = True - - -@cocotb.test() -async def test_coroutine_kill(dut): - """Test that killing a coroutine causes pending routine continue""" - global test_flag - clk_gen = cocotb.scheduler.add(clock_gen(dut.clk)) - await Timer(100, "ns") - clk_gen_two = cocotb.fork(clock_yield(clk_gen)) - await Timer(100, "ns") - clk_gen.kill() - assert not test_flag - await Timer(1000, "ns") - assert test_flag - - -async def clock_one(dut): - count = 0 - while count != 50: - await RisingEdge(dut.clk) - await Timer(1000, "ns") - count += 1 - - -async def clock_two(dut): - count = 0 - while count != 50: - await RisingEdge(dut.clk) - await Timer(10000, "ns") - count += 1 - - -@cocotb.test() -async def test_coroutine_close_down(dut): - log = logging.getLogger("cocotb.test") - clk_gen = cocotb.fork(Clock(dut.clk, 100, "ns").start()) - - coro_one = cocotb.fork(clock_one(dut)) - coro_two = cocotb.fork(clock_two(dut)) - - await Join(coro_one) - await Join(coro_two) - - log.info("Back from joins") - - -@cocotb.test() -async def join_finished(dut): - """ - Test that joining a coroutine that has already been joined gives - the same result as it did the first time. - """ - - retval = None - - async def some_coro(): - await Timer(1, "ns") - return retval - - coro = cocotb.fork(some_coro()) - - retval = 1 - x = await coro.join() - assert x == 1 - - # joining the second time should give the same result. - # we change retval here to prove it does not run again - retval = 2 - x = await coro.join() - assert x == 1 - - -@cocotb.test() -async def consistent_join(dut): - """ - Test that joining a coroutine returns the finished value - """ - async def wait_for(clk, cycles): - rising_edge = RisingEdge(clk) - for _ in range(cycles): - await rising_edge - return 3 - - cocotb.fork(Clock(dut.clk, 2000, 'ps').start()) - - short_wait = cocotb.fork(wait_for(dut.clk, 10)) - long_wait = cocotb.fork(wait_for(dut.clk, 30)) - - await wait_for(dut.clk, 20) - a = await short_wait.join() - b = await long_wait.join() - assert a == b == 3 - - -@cocotb.test() -async def test_kill_twice(dut): - """ - Test that killing a coroutine that has already been killed does not crash - """ - clk_gen = cocotb.fork(Clock(dut.clk, 100, "ns").start()) - await Timer(1, "ns") - clk_gen.kill() - await Timer(1, "ns") - clk_gen.kill() - - -@cocotb.test() -async def test_join_identity(dut): - """ - Test that Join() returns the same object each time - """ - clk_gen = cocotb.fork(Clock(dut.clk, 100, "ns").start()) - - assert Join(clk_gen) is Join(clk_gen) - await Timer(1, "ns") - clk_gen.kill() - - -@cocotb.test() -async def test_trigger_with_failing_prime(dut): - """ Test that a trigger failing to prime throws """ - class ABadTrigger(Trigger): - def prime(self, callback): - raise RuntimeError("oops") - - await Timer(1, "ns") - try: - await ABadTrigger() - except RuntimeError as exc: - assert "oops" in str(exc) - else: - assert False - - -@cocotb.test() -async def test_stack_overflow(dut): - """ - Test against stack overflows when starting many coroutines that terminate - before passing control to the simulator. - """ - # gh-637 - async def null_coroutine(): - await NullTrigger() - - for _ in range(10000): - await null_coroutine() - - await Timer(100, "ns") - - -@cocotb.test() -async def test_kill_coroutine_waiting_on_the_same_trigger(dut): - # gh-1348 - # NOTE: this test depends on scheduling priority. - # It assumes that the first task to wait on a trigger will be woken first. - # The fix for gh-1348 should prevent that from mattering. - dut.clk.setimmediatevalue(0) - - victim_resumed = False - - async def victim(): - await Timer(1, "step") # prevent scheduling of RisingEdge before killer - await RisingEdge(dut.clk) - nonlocal victim_resumed - victim_resumed = True - victim_task = cocotb.fork(victim()) - - async def killer(): - await RisingEdge(dut.clk) - victim_task.kill() - cocotb.fork(killer()) - - await Timer(2, "step") # allow Timer in victim to pass making it schedule RisingEdge after the killer - dut.clk <= 1 - await Timer(1, "step") - assert not victim_resumed - - -@cocotb.test() -async def test_nulltrigger_reschedule(dut): - """ - Test that NullTrigger doesn't immediately reschedule the waiting coroutine. - - The NullTrigger will be added to the end of the list of pending triggers. - """ - log = logging.getLogger("cocotb.test") - last_fork = None - - @cocotb.coroutine # TODO: Remove once Combine accepts bare coroutines - async def reschedule(n): - nonlocal last_fork - for i in range(4): - log.info("Fork {}, iteration {}, last fork was {}".format(n, i, last_fork)) - assert last_fork != n - last_fork = n - await NullTrigger() - - # Test should start in event loop - await Combine(*(reschedule(i) for i in range(4))) - - await Timer(1, "ns") - - # Event loop when simulator returns - await Combine(*(reschedule(i) for i in range(4))) - - -@cocotb.test() -async def test_event_set_schedule(dut): - """ - Test that Event.set() doesn't cause an immediate reschedule. - - The coroutine waiting with Event.wait() will be woken after - the current coroutine awaits a trigger. - """ - - e = Event() - waiter_scheduled = False - - async def waiter(event): - await event.wait() - nonlocal waiter_scheduled - waiter_scheduled = True - - cocotb.fork(waiter(e)) - - e.set() - - # waiter() shouldn't run until after test awaits a trigger - assert waiter_scheduled is False - - await NullTrigger() - - assert waiter_scheduled is True - - -@cocotb.test() -async def test_last_scheduled_write_wins(dut): - """ - Test that the last scheduled write for a signal handle is the value that is written. - """ - log = logging.getLogger("cocotb.test") - e = Event() - dut.stream_in_data.setimmediatevalue(0) - - @cocotb.coroutine # TODO: Remove once Combine accepts bare coroutines - async def first(): - await Timer(1, "ns") - log.info("scheduling stream_in_data <= 1") - dut.stream_in_data <= 1 - e.set() - - @cocotb.coroutine # TODO: Remove once Combine accepts bare coroutines - async def second(): - await Timer(1, "ns") - await e.wait() - log.info("scheduling stream_in_data <= 2") - dut.stream_in_data <= 2 - - await Combine(first(), second()) - - await ReadOnly() - - assert dut.stream_in_data.value.integer == 2 - - await Timer(1, "ns") - dut.array_7_downto_4 <= [1, 2, 3, 4] - dut.array_7_downto_4[7] <= 10 - - await ReadOnly() - - assert dut.array_7_downto_4.value == [10, 2, 3, 4] - - -@cocotb.test() -async def test_task_repr(dut): - """Test RunningTask.__repr__.""" - log = logging.getLogger("cocotb.test") - gen_e = Event('generator_coro_inner') - - def generator_coro_inner(): - gen_e.set() - yield Timer(1, units='ns') - raise ValueError("inner") - - @cocotb.coroutine # testing debug with legacy coroutine syntax - def generator_coro_outer(): - yield from generator_coro_inner() - - gen_task = generator_coro_outer() - - log.info(repr(gen_task)) - assert re.match(r"", repr(gen_task)) - - cocotb.fork(gen_task) - - await gen_e.wait() - - log.info(repr(gen_task)) - assert re.match(r">", repr(gen_task)) - - try: - await Join(gen_task) - except ValueError: - pass - - log.info(repr(gen_task)) - assert re.match(r"", repr(gen_task)) - - coro_e = Event('coroutine_inner') - - async def coroutine_forked(task): - log.info(repr(task)) - assert re.match(r"", repr(task)) - - @cocotb.coroutine # Combine requires use of cocotb.coroutine - async def coroutine_wait(): - await Timer(1, units='ns') - - async def coroutine_inner(): - await coro_e.wait() - this_task = coro_e.data - # cr_await is None while the coroutine is running, so we can't get the stack... - log.info(repr(this_task)) - assert re.match(r"", repr(this_task)) - - cocotb.fork(coroutine_forked(this_task)) - await Combine(*(coroutine_wait() for _ in range(2))) - - return "Combine done" - - async def coroutine_middle(): - return await coroutine_inner() - - async def coroutine_outer(): - return await coroutine_middle() - - coro_task = cocotb.fork(coroutine_outer()) - - coro_e.set(coro_task) - - await NullTrigger() - - log.info(repr(coro_task)) - assert re.match( - r"\), Join\(\)\)>", - repr(coro_task) - ) - - await Timer(2, units='ns') - - log.info(repr(coro_task)) - assert re.match(r"\), \)>", - repr(coro_task) - ) - - async def coroutine_timer(): - await Timer(1, units='ns') - - coro_task = cocotb.fork(coroutine_timer()) - - # Trigger.__await__ should be popped from the coroutine stack - log.info(repr(coro_task)) - assert re.match(r">", repr(coro_task)) - - -@cocotb.test() -async def test_start_soon_async(_): - """ Tests start_soon works with coroutines """ - a = 0 - - async def example(): - nonlocal a - a = 1 - - cocotb.scheduler.start_soon(example()) - assert a == 0 - await NullTrigger() - assert a == 1 - - -@cocotb.test() -async def test_start_soon_decorator(_): - """ Tests start_soon works with RunningTasks """ - a = 0 - - async def example(): - nonlocal a - a = 1 - - cocotb.scheduler.start_soon(example()) - assert a == 0 - await NullTrigger() - assert a == 1 diff --git a/tests/test_cases/test_cocotb/test_synchronization_primitives.py b/tests/test_cases/test_cocotb/test_synchronization_primitives.py deleted file mode 100644 index f0d4aef3..00000000 --- a/tests/test_cases/test_cocotb/test_synchronization_primitives.py +++ /dev/null @@ -1,109 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -""" -Tests for synchronization primitives like Lock and Event -""" -import cocotb -from cocotb.triggers import Lock, Timer, _InternalEvent -from cocotb.utils import get_sim_time - -from common import assert_raises - - -@cocotb.test() -async def test_trigger_lock(dut): - """ - Simple test that checks to see if context management is kept. The - resource value is checked at certain points if it equals the expected - amount, which is easily predictable if the context management is working. - """ - resource = 0 - lock = Lock() - - async def co(): - nonlocal resource - await Timer(10, "ns") - async with lock: - for i in range(4): - await Timer(10, "ns") - resource += 1 - - cocotb.fork(co()) - async with lock: - for i in range(4): - resource += 1 - await Timer(10, "ns") - assert resource == 4 - await Timer(10, "ns") - async with lock: - assert resource == 8 - - -@cocotb.test(timeout_time=100, timeout_unit="ns") -async def test_except_lock(dut): - """ - Checks to see if exceptions cause the lock to be - released. - """ - lock = Lock() - try: - async with lock: - raise RuntimeError() - except RuntimeError: - pass - async with lock: - pass - - -@cocotb.test() -async def test_internalevent(dut): - """Test _InternalEvent trigger.""" - e = _InternalEvent('test parent') - assert repr(e) == "'test parent'" - - async def set_internalevent(): - await Timer(1, units='ns') - e.set('data') - - # Test waiting more than once - cocotb.fork(set_internalevent()) - time_ns = get_sim_time(units='ns') - await e - assert e.is_set() - assert e.data == 'data' - assert get_sim_time(units='ns') == time_ns + 1 - # _InternalEvent can only be awaited once - with assert_raises(RuntimeError): - await e - - e = _InternalEvent(None) - assert repr(e) == 'None' - ran = False - - async def await_internalevent(): - nonlocal ran - await e - ran = True - - # Test multiple coroutines waiting - cocotb.fork(await_internalevent()) - assert not e.is_set() - assert not ran - # _InternalEvent can only be awaited by one coroutine - with assert_raises(RuntimeError): - await e - e.set() - await Timer(1) - assert e.is_set() - assert ran - - # Test waiting after set - e = _InternalEvent(None) - assert not e.is_set() - cocotb.fork(set_internalevent()) - await Timer(2, units='ns') - assert e.is_set() - time_ns = get_sim_time(units='ns') - await e - assert get_sim_time(units='ns') == time_ns diff --git a/tests/test_cases/test_cocotb/test_testfactory.py b/tests/test_cases/test_cocotb/test_testfactory.py deleted file mode 100644 index b09ee661..00000000 --- a/tests/test_cases/test_cocotb/test_testfactory.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -""" -Tests of cocotb.regression.TestFactory functionality -""" -import cocotb -from cocotb.regression import TestFactory - - -testfactory_test_args = set() - - -async def run_testfactory_test(dut, arg1, arg2, arg3): - testfactory_test_args.add((arg1, arg2, arg3)) - -factory = TestFactory(run_testfactory_test) -factory.add_option("arg1", ["a1v1", "a1v2"]) -factory.add_option(("arg2", "arg3"), [("a2v1", "a3v1"), ("a2v2", "a3v2")]) -factory.generate_tests() - - -@cocotb.test() -async def test_testfactory_verify_args(dut): - assert testfactory_test_args == set([ - ("a1v1", "a2v1", "a3v1"), - ("a1v2", "a2v1", "a3v1"), - ("a1v1", "a2v2", "a3v2"), - ("a1v2", "a2v2", "a3v2"), - ]) diff --git a/tests/test_cases/test_cocotb/test_tests.py b/tests/test_cases/test_cocotb/test_tests.py deleted file mode 100644 index 761c203e..00000000 --- a/tests/test_cases/test_cocotb/test_tests.py +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -""" -Tests of cocotb.test functionality - -* expect_error -* expect_fail -* timeout -""" -import cocotb -from cocotb.triggers import Timer -from cocotb.result import TestFailure -from common import clock_gen - - -@cocotb.test(expect_error=NameError) -async def test_error(dut): - """Error in the test""" - await clock_gen(dut.clk) - fail # noqa - - -@cocotb.test() -async def test_tests_are_tests(dut): - """ - Test that things annotated with cocotb.test are tests - """ - assert isinstance(test_tests_are_tests, cocotb.test) - - -# just to be sure... -@cocotb.test(expect_fail=True) -async def test_async_test_can_fail(dut): - raise TestFailure - - -@cocotb.test() -async def test_immediate_test(dut): - """ Test that tests can return immediately """ - return - - -@cocotb.test(expect_fail=True) -async def test_assertion_is_failure(dut): - assert False - - -class MyException(Exception): - pass - - -@cocotb.test(expect_error=MyException) -async def test_expect_particular_exception(dut): - raise MyException() - - -@cocotb.test(expect_error=(MyException, ValueError)) -async def test_expect_exception_list(dut): - raise MyException() - - -@cocotb.test(expect_error=cocotb.result.SimTimeoutError, timeout_time=1, timeout_unit='ns') -async def test_timeout_testdec_fail(dut): - await Timer(10, 'ns') - - -@cocotb.test(timeout_time=100, timeout_unit='ns') -async def test_timeout_testdec_pass(dut): - await Timer(10, 'ns') - - -@cocotb.test(timeout_time=10, timeout_unit='ns') -async def test_timeout_testdec_simultaneous(dut): - try: - await cocotb.triggers.with_timeout(Timer(1, 'ns'), timeout_time=1, timeout_unit='ns') - except cocotb.result.SimTimeoutError: - pass - else: - assert False, "Expected a Timeout" - # Whether this test fails or passes depends on the behavior of the - # scheduler, simulator, and the implementation of the timeout function. - # CAUTION: THIS MAY CHANGE - - -# these tests should run in definition order, not lexicographic order -last_ordered_test = None - - -@cocotb.test() -async def test_ordering_3(dut): - global last_ordered_test - val, last_ordered_test = last_ordered_test, 3 - assert val is None - - -@cocotb.test() -async def test_ordering_2(dut): - global last_ordered_test - val, last_ordered_test = last_ordered_test, 2 - assert val == 3 - - -@cocotb.test() -async def test_ordering_1(dut): - global last_ordered_test - val, last_ordered_test = last_ordered_test, 1 - assert val == 2 diff --git a/tests/test_cases/test_cocotb/test_timing_triggers.py b/tests/test_cases/test_cocotb/test_timing_triggers.py deleted file mode 100644 index 868523c1..00000000 --- a/tests/test_cases/test_cocotb/test_timing_triggers.py +++ /dev/null @@ -1,251 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -""" -Tests related to timing triggers - -* Timer -* ReadWrite -* ReadOnly -* NextTimeStep -* with_timeout -""" -import cocotb -import warnings -from cocotb.triggers import Timer, RisingEdge, ReadOnly, ReadWrite, Join, NextTimeStep, First, TriggerException -from cocotb.utils import get_sim_time, get_sim_steps -from cocotb.clock import Clock - -from common import assert_raises - -from fractions import Fraction -from decimal import Decimal - - -@cocotb.test() -async def test_function_reentrant_clock(dut): - """Test awaiting a reentrant clock""" - clock = dut.clk - timer = Timer(100, "ns") - for i in range(10): - clock <= 0 - await timer - clock <= 1 - await timer - - -@cocotb.test() -async def test_timer_with_units(dut): - time_fs = get_sim_time(units='fs') - - # Await for one simulator time step - await Timer(1) # NOTE: explicitly no units argument here! - time_step = get_sim_time(units='fs') - time_fs - - pattern = "Unable to accurately represent .* with the simulator precision of .*" - with assert_raises(ValueError, pattern): - await Timer(2.5*time_step, units='fs') - dut._log.info("As expected, unable to create a timer of 2.5 simulator time steps") - - time_fs = get_sim_time(units='fs') - - await Timer(3, 'ns') - - assert get_sim_time(units='fs') == time_fs+3000000.0, "Expected a delay of 3 ns" - - time_fs = get_sim_time(units='fs') - await Timer(1.5, 'ns') - - assert get_sim_time(units='fs') == time_fs+1500000.0, "Expected a delay of 1.5 ns" - - time_fs = get_sim_time(units='fs') - await Timer(10.0, 'ps') - - assert get_sim_time(units='fs') == time_fs+10000.0, "Expected a delay of 10 ps" - - time_fs = get_sim_time(units='fs') - await Timer(1.0, 'us') - - assert get_sim_time(units='fs') == time_fs+1000000000.0, "Expected a delay of 1 us" - - -@cocotb.test() -async def test_timer_with_rational_units(dut): - """ Test that rounding errors are not introduced in exact values """ - # now with fractions - time_fs = get_sim_time(units='fs') - await Timer(Fraction(1, int(1e9)), units='sec') - assert get_sim_time(units='fs') == time_fs + 1000000.0, "Expected a delay of 1 ns" - - # now with decimals - time_fs = get_sim_time(units='fs') - await Timer(Decimal('1e-9'), units='sec') - assert get_sim_time(units='fs') == time_fs + 1000000.0, "Expected a delay of 1 ns" - - -exited = False - - -async def do_test_readwrite_in_readonly(dut): - global exited - await RisingEdge(dut.clk) - await ReadOnly() - await ReadWrite() - exited = True - - -async def do_test_cached_write_in_readonly(dut): - global exited - await RisingEdge(dut.clk) - await ReadOnly() - dut.clk <= 0 - exited = True - - -async def do_test_afterdelay_in_readonly(dut, delay): - global exited - await RisingEdge(dut.clk) - await ReadOnly() - await Timer(delay, "ns") - exited = True - - -@cocotb.test(expect_error=TriggerException if cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")) else (), - expect_fail=cocotb.SIM_NAME.lower().startswith(("icarus", - "modelsim", - "ncsim", - "xmsim"))) -async def test_readwrite_in_readonly(dut): - """Test doing invalid sim operation""" - global exited - exited = False - clk_gen = cocotb.fork(Clock(dut.clk, 100, "ns").start()) - coro = cocotb.fork(do_test_readwrite_in_readonly(dut)) - await First(Join(coro), Timer(10000, "ns")) - clk_gen.kill() - assert exited - - -@cocotb.test(expect_error=Exception) -async def test_cached_write_in_readonly(dut): - """Test doing invalid sim operation""" - global exited - exited = False - clk_gen = cocotb.fork(Clock(dut.clk, 100, "ns").start()) - coro = cocotb.fork(do_test_cached_write_in_readonly(dut)) - await First(Join(coro), Timer(10000, "ns")) - clk_gen.kill() - assert exited - - -@cocotb.test() -async def test_afterdelay_in_readonly_valid(dut): - """Test Timer delay after ReadOnly phase""" - global exited - exited = False - clk_gen = cocotb.fork(Clock(dut.clk, 100, "ns").start()) - coro = cocotb.fork(do_test_afterdelay_in_readonly(dut, 1)) - await First(Join(coro), Timer(100000, "ns")) - clk_gen.kill() - assert exited - - -@cocotb.test() -async def test_writes_have_taken_effect_after_readwrite(dut): - """ Test that ReadWrite fires first for the background write coro """ - dut.stream_in_data.setimmediatevalue(0) - - async def write_manually(): - await ReadWrite() - # this should overwrite the write written below - dut.stream_in_data.setimmediatevalue(2) - - # queue a background task to do a manual write - waiter = cocotb.fork(write_manually()) - - # do a delayed write. This will be overwritten - dut.stream_in_data <= 3 - await waiter - - # check that the write we expected took precedence - await ReadOnly() - assert dut.stream_in_data.value == 2 - - -@cocotb.coroutine # cocotb.coroutine necessary to use in with_timeout -async def example(): - await Timer(10, 'ns') - return 1 - - -@cocotb.test() -async def test_timeout_func_fail(dut): - try: - await cocotb.triggers.with_timeout(example(), timeout_time=1, timeout_unit='ns') - except cocotb.result.SimTimeoutError: - pass - else: - assert False, "Expected a Timeout" - - -@cocotb.test() -async def test_timeout_func_pass(dut): - res = await cocotb.triggers.with_timeout(example(), timeout_time=100, timeout_unit='ns') - assert res == 1 - - -@cocotb.test() -async def test_readwrite(dut): - """ Test that ReadWrite can be waited on """ - # gh-759 - await Timer(1, "ns") - dut.clk <= 1 - await ReadWrite() - - -@cocotb.test() -async def test_singleton_isinstance(dut): - """ - Test that the result of trigger expression have a predictable type - """ - assert isinstance(NextTimeStep(), NextTimeStep) - assert isinstance(ReadOnly(), ReadOnly) - assert isinstance(ReadWrite(), ReadWrite) - - -@cocotb.test() -async def test_neg_timer(dut): - """Test negative timer values are forbidden""" - with assert_raises(TriggerException): - Timer(-42) # no need to even `await`, constructing it is an error - # handle 0 special case - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - Timer(0) - assert "Timer setup with value 0, which might exhibit undefined behavior in some simulators" in str(w[-1].message) - assert issubclass(w[-1].category, RuntimeWarning) - - -@cocotb.test() -async def test_time_units_eq_None(dut): - """Test deprecation warning when time units are None""" - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - Timer(1, units=None) - assert issubclass(w[-1].category, DeprecationWarning) - assert 'Using units=None is deprecated, use units="step" instead.' in str(w[-1].message) - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - Clock(dut.clk, 2, units=None) - assert issubclass(w[-1].category, DeprecationWarning) - assert 'Using units=None is deprecated, use units="step" instead.' in str(w[-1].message) - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - get_sim_steps(222, units=None) - assert issubclass(w[-1].category, DeprecationWarning) - assert 'Using units=None is deprecated, use units="step" instead.' in str(w[-1].message) - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - await cocotb.triggers.with_timeout(example(), timeout_time=222222, timeout_unit=None) - assert issubclass(w[-1].category, DeprecationWarning) - assert 'Using timeout_unit=None is deprecated, use timeout_unit="step" instead.' in str(w[-1].message) diff --git a/tests/test_cases/test_compare/Makefile b/tests/test_cases/test_compare/Makefile deleted file mode 100644 index ac08cf67..00000000 --- a/tests/test_cases/test_compare/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause - -include ../../designs/basic_hierarchy_module/Makefile - -MODULE=test_compare diff --git a/tests/test_cases/test_compare/test_compare.py b/tests/test_cases/test_compare/test_compare.py deleted file mode 100644 index e53f17cf..00000000 --- a/tests/test_cases/test_compare/test_compare.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause - -"""Test for comparing handle classes -""" - -import cocotb -from cocotb.clock import Clock -from cocotb.handle import SimHandleBase, NonHierarchyObject -from cocotb.triggers import RisingEdge, FallingEdge - - -class Testbench(object): - - def __init__(self, dut): - self.dut = dut - self.clkedge = RisingEdge(dut.clk) - - async def initialise(self): - """Initalise the testbench""" - cocotb.fork(Clock(self.dut.clk, 10).start()) - self.dut.reset <= 0 - for _ in range(2): - await self.clkedge - self.dut.reset <= 1 - for _ in range(3): - await self.clkedge - - -@cocotb.test() -async def test_compare_simhandlebase(dut): - """Test for SimHandleBase comparisons""" - tb = Testbench(dut) - await tb.initialise() - for _ in range(3): - await tb.clkedge - - # Want to check the __eq__ comparator in SimHandleBase - # (overridden in NonHierarchyObject) - assert isinstance(dut.i_module_a, SimHandleBase) - assert not isinstance(dut.i_module_a, NonHierarchyObject) - assert isinstance(dut.i_module_b, SimHandleBase) - assert not isinstance(dut.i_module_b, NonHierarchyObject) - - # Same handle - assert dut.i_module_a == dut.i_module_a - assert not dut.i_module_a != dut.i_module_a - # Different handles - assert not dut.i_module_a == dut.i_module_b - assert dut.i_module_a != dut.i_module_b - # Compare against non-handle not implemented - assert dut.i_module_a.__eq__(1) == NotImplemented - assert dut.i_module_a.__ne__(1) == NotImplemented - - -@cocotb.test() -async def test_compare_nonhierarchy(dut): - """Test for NonHierarchyObject comparisons""" - tb = Testbench(dut) - await tb.initialise() - for _ in range(3): - await tb.clkedge - - # Check that all these signals are NonHierarchyObject children - assert isinstance(dut.counter_plus_two, NonHierarchyObject) - assert isinstance(dut.counter_plus_five, NonHierarchyObject) - assert isinstance(dut.clk, NonHierarchyObject) - assert isinstance(dut.i_module_a.clk, NonHierarchyObject) - - # Two different handles - assert not dut.counter_plus_two == dut.counter_plus_five - assert dut.counter_plus_two != dut.counter_plus_five - # Two different handles with the same value - # Because they are handles, it is checked if they are the same handle - assert not dut.clk == dut.i_module_a.clk - assert dut.clk != dut.i_module_a.clk - # A handle and a value - # Because one is a value, it is compared against the value of the handle - await tb.clkedge - assert dut.clk == 1 - assert dut.clk != 0 - await FallingEdge(tb.dut.clk) - assert dut.clk == 0 - assert dut.clk != 1 diff --git a/tests/test_cases/test_configuration/Makefile b/tests/test_cases/test_configuration/Makefile deleted file mode 100644 index bcfd0acf..00000000 --- a/tests/test_cases/test_configuration/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -############################################################################### -# Copyright (c) 2015 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -include ../../designs/vhdl_configurations/Makefile -MODULE = test_configurations diff --git a/tests/test_cases/test_configuration/test_configurations.py b/tests/test_cases/test_configuration/test_configurations.py deleted file mode 100644 index f21e3cdd..00000000 --- a/tests/test_cases/test_configuration/test_configurations.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (c) 2015 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import cocotb -from cocotb.triggers import Timer - - -def sub_iterate(unit): - for thing in unit: - thing._log.info("Found %s.%s %s" % (unit._name, thing._name, type(thing))) - sub_iterate(thing) - - -@cocotb.test() -async def iterate(dut): - await Timer(1, "ns") - sub_iterate(dut) - await Timer(1, "ns") diff --git a/tests/test_cases/test_discovery/Makefile b/tests/test_cases/test_discovery/Makefile deleted file mode 100644 index 5c3c4685..00000000 --- a/tests/test_cases/test_discovery/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -############################################################################### -# Copyright (c) 2015 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - - -include ../../designs/sample_module/Makefile - -ifeq ($(TOPLEVEL_LANG), vhdl) -MODULE = test_discovery,test_vhdl_indexed_name,test_vhdl_block -else -MODULE = test_discovery -endif diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py deleted file mode 100644 index ba01d5ec..00000000 --- a/tests/test_cases/test_discovery/test_discovery.py +++ /dev/null @@ -1,495 +0,0 @@ -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import cocotb -import logging -from cocotb.binary import BinaryValue -from cocotb.triggers import Timer -from cocotb.result import TestError, TestFailure -from cocotb.handle import IntegerObject, ConstantObject, HierarchyObject, StringObject -from cocotb._sim_versions import IcarusVersion - - -@cocotb.test() -async def pseudo_region_access(dut): - """Test that pseudo-regions are accessible before iteration""" - - # Ensure pseudo-region lookup will fail - if len(dut._sub_handles) != 0: - dut._sub_handles = {} - - pseudo_region = dut.genblk1 - dut._log.info("Found %s (%s)", pseudo_region._name, type(pseudo_region)) - first_generate_instance = pseudo_region[0] - dut._log.info("Found %s (%s)", first_generate_instance._name, type(first_generate_instance)) - - -@cocotb.test() -async def recursive_discover(dut): - """Discover absolutely everything in the DUT""" - def _discover(obj): - for thing in obj: - dut._log.info("Found %s (%s)", thing._name, type(thing)) - _discover(thing) - _discover(dut) - - -@cocotb.test() -async def discover_module_values(dut): - """Discover everything in the DUT""" - count = 0 - for thing in dut: - thing._log.info("Found something: %s" % thing._fullname) - count += 1 - if count < 2: - raise TestFailure("Expected to discover things in the DUT") - - -@cocotb.test(expect_error=AttributeError) -async def discover_value_not_in_dut(dut): - """Try and get a value from the DUT that is not there""" - fake_signal = dut.fake_signal - - -@cocotb.test() -async def access_signal(dut): - """Access a signal using the assignment mechanism""" - tlog = logging.getLogger("cocotb.test") - signal = dut.stream_in_data - - tlog.info("Signal is %s" % type(signal)) - dut.stream_in_data.setimmediatevalue(1) - await Timer(1, "ns") - if dut.stream_in_data.value.integer != 1: - raise TestError("%s.%s != %d" % - (dut.stream_in_data._path, - dut.stream_in_data.value.integer, 1)) - - -@cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"]) -async def access_type_bit_verilog(dut): - """Access type bit in SystemVerilog""" - await Timer(1, "step") - assert dut.mybit.value == 1, "The default value was incorrect" - dut.mybit <= 0 - await Timer(1, "ns") - assert dut.mybit.value == 0, "The assigned value was incorrect" - - assert dut.mybits.value == 0b11, "The default value was incorrect" - dut.mybits <= 0b00 - await Timer(1, "ns") - assert dut.mybits.value == 0b00, "The assigned value was incorrect" - - assert dut.mybits_uninitialized.value == 0b00, "The default value was incorrect" - dut.mybits_uninitialized <= 0b11 - await Timer(1, "ns") - assert dut.mybits_uninitialized.value == 0b11, "The assigned value was incorrect" - - -@cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"]) -async def access_type_bit_verilog_metavalues(dut): - """Access type bit in SystemVerilog with metavalues that the type does not support. - - Note that some simulators (wrongly) allow metavalues even for bits when taking the VPI route. - The metavalues still may show up as `0` and `1` in HDL (Xcelium and Riviera). - """ - await Timer(1, "ns") - dut.mybits <= BinaryValue("XZ") - await Timer(1, "ns") - print(dut.mybits.value.binstr) - if cocotb.SIM_NAME.lower().startswith(("icarus", "ncsim", "xmsim")): - assert dut.mybits.value.binstr.lower() == "xz", "The assigned value was not as expected" - elif cocotb.SIM_NAME.lower().startswith(("riviera",)): - assert dut.mybits.value.binstr.lower() == "10", "The assigned value was not as expected" - else: - assert dut.mybits.value.binstr.lower() == "00", "The assigned value was incorrect" - - dut.mybits <= BinaryValue("ZX") - await Timer(1, "ns") - print(dut.mybits.value.binstr) - if cocotb.SIM_NAME.lower().startswith(("icarus", "ncsim", "xmsim")): - assert dut.mybits.value.binstr.lower() == "zx", "The assigned value was not as expected" - elif cocotb.SIM_NAME.lower().startswith(("riviera",)): - assert dut.mybits.value.binstr.lower() == "01", "The assigned value was not as expected" - else: - assert dut.mybits.value.binstr.lower() == "00", "The assigned value was incorrect" - - -@cocotb.test( - # Icarus up to (including) 10.3 doesn't support bit-selects, see https://github.com/steveicarus/iverilog/issues/323 - expect_error=IndexError if (cocotb.SIM_NAME.lower().startswith("icarus") and (IcarusVersion(cocotb.SIM_VERSION) <= IcarusVersion("10.3 (stable)"))) else (), - skip=cocotb.LANGUAGE in ["vhdl"]) -async def access_single_bit(dut): - """Access a single bit in a vector of the DUT""" - dut.stream_in_data <= 0 - await Timer(1, "ns") - dut._log.info("%s = %d bits" % - (dut.stream_in_data._path, len(dut.stream_in_data))) - dut.stream_in_data[2] <= 1 - await Timer(1, "ns") - if dut.stream_out_data_comb.value.integer != (1 << 2): - raise TestError("%s.%s != %d" % - (dut.stream_out_data_comb._path, - dut.stream_out_data_comb.value.integer, (1 << 2))) - - -@cocotb.test( - # Icarus up to (including) 10.3 doesn't support bit-selects, see https://github.com/steveicarus/iverilog/issues/323 - expect_error=IndexError if (cocotb.SIM_NAME.lower().startswith("icarus") and (IcarusVersion(cocotb.SIM_VERSION) <= IcarusVersion("10.3 (stable)"))) else (), - skip=cocotb.LANGUAGE in ["vhdl"]) -async def access_single_bit_assignment(dut): - """Access a single bit in a vector of the DUT using the assignment mechanism""" - dut.stream_in_data = 0 - await Timer(1, "ns") - dut._log.info("%s = %d bits" % - (dut.stream_in_data._path, len(dut.stream_in_data))) - dut.stream_in_data[2] = 1 - await Timer(1, "ns") - if dut.stream_out_data_comb.value.integer != (1 << 2): - raise TestError("%s.%s != %d" % - (dut.stream_out_data_comb._path, - dut.stream_out_data_comb.value.integer, (1 << 2))) - - -@cocotb.test(expect_error=IndexError) -async def access_single_bit_erroneous(dut): - """Access a non-existent single bit""" - dut._log.info("%s = %d bits" % - (dut.stream_in_data._path, len(dut.stream_in_data))) - bit = len(dut.stream_in_data) + 4 - dut.stream_in_data[bit] <= 1 - - -@cocotb.test(expect_error=AttributeError if cocotb.SIM_NAME.lower().startswith(("icarus", "chronologic simulation vcs")) else (), - expect_fail=cocotb.SIM_NAME.lower().startswith(("riviera")) and cocotb.LANGUAGE in ["verilog"]) -async def access_integer(dut): - """Integer should show as an IntegerObject""" - bitfail = False - tlog = logging.getLogger("cocotb.test") - test_int = dut.stream_in_int - if not isinstance(test_int, IntegerObject): - raise TestFailure("dut.stream_in_int is not an integer but {} instead".format(type(test_int))) - - try: - bit = test_int[3] - except IndexError as e: - tlog.info("Access to bit is an error as expected") - bitfail = True - - if not bitfail: - raise TestFailure("Access into an integer should be invalid") - - length = len(test_int) - if length != 1: - raise TestFailure("Length should be 1 not %d" % length) - - -@cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) -async def access_ulogic(dut): - """Access a std_ulogic as enum""" - constant_integer = dut.stream_in_valid - - -@cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) -async def access_constant_integer(dut): - """ - Access a constant integer - """ - tlog = logging.getLogger("cocotb.test") - constant_integer = dut.isample_module1.EXAMPLE_WIDTH - tlog.info("Value of EXAMPLE_WIDTH is %d" % constant_integer) - if not isinstance(constant_integer, ConstantObject): - raise TestFailure("EXAMPLE_WIDTH was not constant") - if constant_integer != 7: - raise TestFailure("EXAMPLE_WIDTH was not 7") - - -@cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) -async def access_string_vhdl(dut): - """Access to a string, both constant and signal.""" - tlog = logging.getLogger("cocotb.test") - constant_string = dut.isample_module1.EXAMPLE_STRING - tlog.info("%r is %s" % (constant_string, constant_string.value)) - if not isinstance(constant_string, ConstantObject): - raise TestFailure("EXAMPLE_STRING was not constant") - if constant_string != b"TESTING": - raise TestFailure("EXAMPLE_STRING was not == \'TESTING\'") - - tlog.info("Test writing under size") - - test_string = b"cocotb" - dut.stream_in_string.setimmediatevalue(test_string) - - variable_string = dut.stream_out_string - if variable_string != b'': - raise TestFailure("%r not \'\'" % variable_string) - - await Timer(1, "ns") - - if variable_string != test_string: - raise TestFailure("%r %s != '%s'" % (variable_string, variable_string.value, test_string)) - - test_string = b"longer_than_the_array" - tlog.info("Test writing over size with '%s'" % test_string) - - dut.stream_in_string.setimmediatevalue(test_string) - variable_string = dut.stream_out_string - - await Timer(1, "ns") - - test_string = test_string[:len(variable_string)] - - if variable_string != test_string: - raise TestFailure("%r %s != '%s'" % (variable_string, variable_string.value, test_string)) - - tlog.info("Test read access to a string character") - - await Timer(1, "ns") - - idx = 3 - - result_slice = variable_string[idx] - - # String is defined as string(1 to 8) so idx=3 will access the 3rd character - if result_slice != test_string[idx - 1]: - raise TestFailure("Single character did not match {} != {}".format(result_slice, test_string[idx - 1])) - - tlog.info("Test write access to a string character") - - await Timer(1, "ns") - - for i in variable_string: - lower = chr(i) - upper = lower.upper() - i.setimmediatevalue(ord(upper)) - - await Timer(1, "ns") - - test_string = test_string.upper() - - result = variable_string.value - tlog.info("After setting bytes of string value is %s" % result) - if variable_string != test_string: - raise TestFailure("%r %s != '%s'" % (variable_string, result, test_string)) - - -# TODO: add tests for Verilog "string_input_port" and "STRING_LOCALPARAM" (see issue #802) - -@cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"] or cocotb.SIM_NAME.lower().startswith(("icarus", "riviera")), - expect_error=cocotb.result.TestFailure if cocotb.SIM_NAME.lower().startswith(("xmsim", "ncsim", "modelsim", "chronologic simulation vcs")) else ()) -async def access_const_string_verilog(dut): - """Access to a const Verilog string.""" - tlog = logging.getLogger("cocotb.test") - string_const = dut.STRING_CONST - - await Timer(10, "ns") - tlog.info("%r is %s" % (string_const, string_const.value)) - if not isinstance(string_const, StringObject): - raise TestFailure("STRING_CONST was not StringObject") - if string_const != b"TESTING_CONST": - raise TestFailure("Initial value of STRING_CONST was not == b\'TESTING_CONST\' but {} instead".format(string_const.value)) - - tlog.info("Modifying const string") - string_const <= b"MODIFIED" - await Timer(10, "ns") - if string_const != b"TESTING_CONST": - raise TestFailure("STRING_CONST was not still b\'TESTING_CONST\' after modification but {} instead".format(string_const.value)) - - -@cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"], - expect_error=AttributeError if cocotb.SIM_NAME.lower().startswith("icarus") else ()) -async def access_var_string_verilog(dut): - """Access to a var Verilog string.""" - tlog = logging.getLogger("cocotb.test") - string_var = dut.STRING_VAR - - await Timer(10, "ns") - tlog.info("%r is %s" % (string_var, string_var.value)) - if not isinstance(string_var, StringObject): - raise TestFailure("STRING_VAR was not StringObject") - if string_var != b"TESTING_VAR": - raise TestFailure("Initial value of STRING_VAR was not == b\'TESTING_VAR\' but {} instead".format(string_var.value)) - - tlog.info("Modifying var string") - string_var <= b"MODIFIED" - await Timer(10, "ns") - if string_var != b"MODIFIED": - raise TestFailure("STRING_VAR was not == b\'MODIFIED\' after modification but {} instead".format(string_var.value)) - - -@cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) -async def access_constant_boolean(dut): - """Test access to a constant boolean""" - tlog = logging.getLogger("cocotb.test") - - constant_boolean = dut.isample_module1.EXAMPLE_BOOL - if not isinstance(constant_boolean, ConstantObject): - raise TestFailure("dut.stream_in_int.EXAMPLE_BOOL is not a ConstantObject") - - tlog.info("Value of %s is %d" % (constant_boolean._path, constant_boolean.value)) - - -@cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) -async def access_boolean(dut): - """Test access to a boolean""" - tlog = logging.getLogger("cocotb.test") - - boolean = dut.stream_in_bool - - return - - # if not isinstance(boolean, IntegerObject): - # raise TestFailure("dut.stream_in_boolean is not a IntegerObject is %s" % type(boolean)) - - try: - bit = boolean[3] - except TestError as e: - tlog.info("Access to bit is an error as expected") - bitfail = True - - if not bitfail: - raise TestFailure("Access into an integer should be invalid") - - length = len(boolean) - if length != 1: - raise TestFailure("Length should be 1 not %d" % length) - - tlog.info("Value of %s is %d" % (boolean._path, boolean.value)) - - curr_val = int(boolean) - output_bool = dut.stream_out_bool - - tlog.info("Before %d After = %d" % (curr_val, (not curr_val))) - - boolean.setimmediatevalue(not curr_val) - - await Timer(1, "ns") - - tlog.info("Value of %s is now %d" % (output_bool._path, output_bool.value)) - if (int(curr_val) == int(output_bool)): - raise TestFailure("Value did not propagate") - - -@cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"]) -async def access_internal_register_array(dut): - """Test access to an internal register array""" - - if (dut.register_array[0].value.binstr != "xxxxxxxx"): - raise TestFailure("Failed to access internal register array value") - - dut.register_array[1].setimmediatevalue(4) - - await Timer(1, "ns") - - if (dut.register_array[1].value != 4): - raise TestFailure("Failed to set internal register array value") - - -@cocotb.test(skip=True) -async def skip_a_test(dut): - """This test shouldn't execute""" - dut._log.info("%s = %d bits" % - (dut.stream_in_data._path, len(dut.stream_in_data))) - bit = len(dut.stream_in_data) + 4 - dut.stream_in_data[bit] <= 1 - - -@cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"], - expect_error=AttributeError if cocotb.SIM_NAME.lower().startswith(("icarus")) else ()) -async def access_gate(dut): - """ - Test access to a gate Object - """ - gate = dut.test_and_gate - - if not isinstance(gate, HierarchyObject): - raise TestFailure("Gate should be HierarchyObject") - - -@cocotb.test(skip=cocotb.LANGUAGE in ["verilog"]) -async def custom_type(dut): - """ - Test iteration over a custom type - """ - tlog = logging.getLogger("cocotb.test") - - new_type = dut.cosLut - tlog.info("cosLut object %s %s" % (new_type, type(new_type))) - - expected_sub = 84 - expected_top = 4 - - count = 0 - - def _discover(obj): - iter_count = 0 - for elem in obj: - iter_count += 1 - iter_count += _discover(elem) - return iter_count - - for sub in new_type: - tlog.info("Sub object %s %s" % (sub, type(sub))) - sub_count = _discover(sub) - if sub_count != expected_sub: - raise TestFailure("Expected %d found %d under %s" % (expected_sub, sub_count, sub)) - count += 1 - - if expected_top != count: - raise TestFailure("Expected %d found %d for cosLut" % (expected_top, count)) - - -@cocotb.test(skip=cocotb.LANGUAGE in ["vhdl"]) -async def type_check_verilog(dut): - """ - Test if types are recognized - """ - - tlog = logging.getLogger("cocotb.test") - - test_handles = [ - (dut.stream_in_ready, "GPI_REGISTER"), - (dut.register_array, "GPI_ARRAY"), - (dut.temp, "GPI_REGISTER"), - (dut.and_output, "GPI_NET"), - (dut.stream_in_data, "GPI_NET"), - (dut.logic_b, "GPI_REGISTER"), - (dut.logic_c, "GPI_REGISTER"), - (dut.INT_PARAM, "GPI_INTEGER"), - (dut.REAL_PARAM, "GPI_REAL"), - (dut.STRING_PARAM, "GPI_STRING") - ] - - if cocotb.SIM_NAME.lower().startswith(("icarus")): - test_handles.append((dut.logic_a, "GPI_NET")) # https://github.com/steveicarus/iverilog/issues/312 - else: - test_handles.append((dut.logic_a, "GPI_REGISTER")) - - for handle in test_handles: - tlog.info("Handle %s" % (handle[0]._fullname,)) - if handle[0]._type != handle[1]: - raise TestFailure("Expected %s found %s for %s" % (handle[1], handle[0]._type, handle[0]._fullname)) diff --git a/tests/test_cases/test_discovery/test_vhdl_block.py b/tests/test_cases/test_discovery/test_vhdl_block.py deleted file mode 100644 index 3950393b..00000000 --- a/tests/test_cases/test_discovery/test_vhdl_block.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import cocotb -from cocotb.result import TestFailure - - -@cocotb.test() -async def block_iter(dut): - """Access a VHDL block statement""" - - try: - dut._log.info("Block: {} ({})".format(dut.isample_module1.SAMPLE_BLOCK._name, - type(dut.isample_module1.SAMPLE_BLOCK))) - dut._log.info("Signal inside Block: {} ({})".format(dut.isample_module1.SAMPLE_BLOCK.clk_inv._name, - type(dut.isample_module1.SAMPLE_BLOCK.clk_inv))) - except AttributeError: - raise TestFailure("Could not traverse into vhpiBlockStmtK") diff --git a/tests/test_cases/test_discovery/test_vhdl_indexed_name.py b/tests/test_cases/test_discovery/test_vhdl_indexed_name.py deleted file mode 100644 index b8ecfd5f..00000000 --- a/tests/test_cases/test_discovery/test_vhdl_indexed_name.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2015 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import cocotb - - -@cocotb.test() -async def index_name_iter(dut): - """Access a non local indexed name""" - total_count = 0 - - def _discover(obj): - count = 0 - for thing in obj: - count += 1 - dut._log.info("Found %s (%s)", thing._name, type(thing)) - count += _discover(thing) - return count - total_count = _discover(dut.isample_module1) - - dut._log.info("Number of objects in non local vhpiIndexedNameK is %d" % total_count) diff --git a/tests/test_cases/test_exit_error/Makefile b/tests/test_cases/test_exit_error/Makefile deleted file mode 100644 index 7176a31a..00000000 --- a/tests/test_cases/test_exit_error/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -############################################################################### -# Copyright (c) 2015 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -# detecting an expected failure has to happen here, as the python code is invalid -.PHONY: override_for_this_test -override_for_this_test: - if $(MAKE) all; then echo "Expected this to fail"; false; else echo "Failed as expected"; fi - -include ../../designs/sample_module/Makefile - -MODULE = test_exit diff --git a/tests/test_cases/test_exit_error/test_exit.py b/tests/test_cases/test_exit_error/test_exit.py deleted file mode 100644 index 864b3a11..00000000 --- a/tests/test_cases/test_exit_error/test_exit.py +++ /dev/null @@ -1,9 +0,0 @@ -import cocotb -from cocotb.triggers import Timer - - -@cocotb.test() -async def typosyntax_error(): - # this syntax error makes the whole file unimportable, so the file contents - # don't really matter. - await Timer(100)a # noqa diff --git a/tests/test_cases/test_external/Makefile b/tests/test_cases/test_external/Makefile deleted file mode 100644 index 83403ffb..00000000 --- a/tests/test_cases/test_external/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - - -include ../../designs/sample_module/Makefile - -MODULE = test_external diff --git a/tests/test_cases/test_external/test_external.py b/tests/test_cases/test_external/test_external.py deleted file mode 100644 index d5509bed..00000000 --- a/tests/test_cases/test_external/test_external.py +++ /dev/null @@ -1,383 +0,0 @@ -# Copyright (c) 2013, 2018 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -""" -A set of tests that demonstrate cocotb functionality - -Also used a regression test of cocotb capabilities -""" -import threading - -import cocotb -from cocotb.result import TestFailure -from cocotb.triggers import Timer, RisingEdge, ReadOnly -from cocotb.clock import Clock -from cocotb.decorators import external -from cocotb.utils import get_sim_time - - -def return_two(dut): - return 2 - - -@cocotb.function -async def await_two_clock_edges(dut): - await RisingEdge(dut.clk) - await RisingEdge(dut.clk) - await Timer(1, units='ns') - dut._log.info("Returning from await_two_clock_edges") - return 2 - - -def calls_cocotb_function(dut): - return await_two_clock_edges(dut) - - -def print_sim_time(dut, base_time): - # We are not calling out here so time should not advance - # And should also remain consistent - for _ in range(5): - _t = get_sim_time('ns') - dut._log.info("Time reported = %d", _t) - if _t != base_time: - raise TestFailure("Time reported does not match base_time %f != %f" % - (_t, base_time)) - dut._log.info("external function has ended") - - -@cocotb.test() -async def test_time_in_external(dut): - """ - Test that the simulation time does not advance if the wrapped external - routine does not call @function - """ - await Timer(10, units='ns') - time = get_sim_time('ns') - dut._log.info("Time at start of test = %d" % time) - for i in range(100): - dut._log.info("Loop call %d" % i) - await external(print_sim_time)(dut, time) - - time_now = get_sim_time('ns') - await Timer(10, units='ns') - - if time != time_now: - raise TestFailure("Time has elapsed over external call") - - -# Cadence simulators: "Unable set up RisingEdge(...) Trigger" with VHDL (see #1076) -@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else ()) -async def test_time_in_function(dut): - """ - Test that an @external function calling back into a cocotb @function - takes the expected amount of time - """ - @cocotb.function - def wait_cycles(dut, n): - for _ in range(n): - yield RisingEdge(dut.clk) - - @external - def wait_cycles_wrapper(dut, n): - return wait_cycles(dut, n) - - clk_gen = cocotb.fork(Clock(dut.clk, 100, units='ns').start()) - await Timer(10, units='ns') - for n in range(5): - for i in range(20): - await RisingEdge(dut.clk) - time = get_sim_time('ns') - expected_after = time + 100*n - await wait_cycles_wrapper(dut, n) - time_after = get_sim_time('ns') - if expected_after != time_after: - raise TestFailure("Wrong time elapsed in external call") - - -# Cadence simulators: "Unable set up RisingEdge(...) Trigger" with VHDL (see #1076) -@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else ()) -async def test_external_call_return(dut): - """ - Test ability to await an external function that is not a coroutine using @external - """ - async def clock_monitor(dut): - count = 0 - while True: - await RisingEdge(dut.clk) - await Timer(1000, units='ns') - count += 1 - - mon = cocotb.fork(clock_monitor(dut)) - clk_gen = cocotb.fork(Clock(dut.clk, 100, units='ns').start()) - value = await external(return_two)(dut) - assert value == 2 - - -@cocotb.test() -async def test_consecutive_externals(dut): - """ - Test that multiple @external functions can be called in the same test - """ - value = await external(return_two)(dut) - dut._log.info("First one completed") - assert value == 2 - - value = await external(return_two)(dut) - dut._log.info("Second one completed") - assert value == 2 - - -@cocotb.test() -async def test_external_from_readonly(dut): - """ - Test that @external functions that don't consume simulation time - can be called from ReadOnly state - """ - await ReadOnly() - dut._log.info("In readonly") - value = await external(return_two)(dut) - assert value == 2 - - -@cocotb.test() -async def test_function_from_readonly(dut): - """ - Test that @external functions that call @functions that await Triggers - can be called from ReadOnly state - """ - clk_gen = cocotb.fork(Clock(dut.clk, 100, units='ns').start()) - - await ReadOnly() - dut._log.info("In readonly") - value = await external(calls_cocotb_function)(dut) - assert value == 2 - - -# Cadence simulators: "Unable set up RisingEdge(...) Trigger" with VHDL (see #1076) -@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else ()) -async def test_function_that_awaits(dut): - """ - Test that @external functions can call @function coroutines that - awaits Triggers and return values back through to - the test - """ - clk_gen = cocotb.fork(Clock(dut.clk, 100, units='ns').start()) - - value = await external(calls_cocotb_function)(dut) - assert value == 2 - - -# Cadence simulators: "Unable set up RisingEdge(...) Trigger" with VHDL (see #1076) -@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else ()) -async def test_await_after_function(dut): - """ - Test that awaiting a Trigger works after returning - from @external functions that call @functions that consume - simulation time - """ - clk_gen = cocotb.fork(Clock(dut.clk, 100, units='ns').start()) - - value = await external(calls_cocotb_function)(dut) - assert value == 2 - - await Timer(10, units="ns") - await RisingEdge(dut.clk) - - -# Cadence simulators: "Unable set up RisingEdge(...) Trigger" with VHDL (see #1076) -@cocotb.test(expect_error=cocotb.triggers.TriggerException if cocotb.SIM_NAME.startswith(("xmsim", "ncsim")) and cocotb.LANGUAGE in ["vhdl"] else ()) -async def test_external_from_fork(dut): - """ - Test that @external functions work when awaited from a forked - task - """ - async def run_function(dut): - value = await external(calls_cocotb_function)(dut) - return value - - async def run_external(dut): - value = await external(return_two)(dut) - return value - - clk_gen = cocotb.fork(Clock(dut.clk, 100, units='ns').start()) - - coro1 = cocotb.fork(run_function(dut)) - value = await coro1.join() - assert value == 2 - dut._log.info("Back from join 1") - - value = 0 - coro2 = cocotb.fork(run_external(dut)) - value = await coro2.join() - assert value == 2 - dut._log.info("Back from join 2") - - -@cocotb.test() -async def test_external_raised_exception(dut): - """ - Test that exceptions thrown by @external functions can be caught - """ - @external - def func(): - raise ValueError() - - try: - await func() - except ValueError: - pass - else: - raise TestFailure('Exception was not thrown') - - -@cocotb.test() -async def test_external_returns_exception(dut): - """ - Test that exceptions can be returned by @external functions - """ - @external - def func(): - return ValueError() - - try: - result = await func() - except ValueError: - raise TestFailure('Exception should not have been thrown') - - if not isinstance(result, ValueError): - raise TestFailure('Exception was not returned') - - -@cocotb.test() -async def test_function_raised_exception(dut): - """ - Test that exceptions thrown by @function coroutines can be caught - """ - @cocotb.function - async def func(): - raise ValueError() - - @external - def ext(): - return func() - - try: - await ext() - except ValueError: - pass - else: - raise TestFailure('Exception was not thrown') - - -@cocotb.test() -async def test_function_returns_exception(dut): - """ - Test that exceptions can be returned by @function coroutines - """ - @cocotb.function - def gen_func(): - return ValueError() - yield - - @external - def ext(): - return gen_func() - - try: - result = await ext() - except ValueError: - raise TestFailure('Exception should not have been thrown') - - if not isinstance(result, ValueError): - raise TestFailure('Exception was not returned') - - -@cocotb.test() -async def test_function_from_weird_thread_fails(dut): - """ - Test that background threads caling a @function do not hang forever - """ - func_started = False - caller_resumed = False - raised = False - - @cocotb.function - async def func(): - nonlocal func_started - func_started = True - await Timer(10, units='ns') - - def function_caller(): - nonlocal raised - nonlocal caller_resumed - try: - func() - except RuntimeError: - raised = True - finally: - caller_resumed = True - - @external - def ext(): - result = [] - - t = threading.Thread(target=function_caller) - t.start() - t.join() - - task = cocotb.fork(ext()) - - await Timer(20, units='ns') - - assert caller_resumed, "Caller was never resumed" - assert not func_started, "Function should never have started" - assert raised, "No exception was raised to warn the user" - - await task.join() - - -@cocotb.test() -async def test_function_called_in_parallel(dut): - """ - Test that the same `@function` can be called from two parallel background - threads. - """ - @cocotb.function - async def function(x): - await Timer(1, units='ns') - return x - - @cocotb.external - def call_function(x): - return function(x) - - t1 = cocotb.fork(call_function(1)) - t2 = cocotb.fork(call_function(2)) - v1 = await t1 - v2 = await t2 - assert v1 == 1, v1 - assert v2 == 2, v2 diff --git a/tests/test_cases/test_failure/Makefile b/tests/test_cases/test_failure/Makefile deleted file mode 100644 index e1678430..00000000 --- a/tests/test_cases/test_failure/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause - -MODULE := test_failure - -# ensure the test runs, squash any error code, and ensure a failing test was reported -.PHONY: override_for_this_test -override_for_this_test: - -$(MAKE) all - $(call check_for_results_file) - test $$(../../../bin/combine_results.py | grep "Failure in testsuite" | wc -l) -eq 1 && rm -f results.xml - -include ../../designs/sample_module/Makefile diff --git a/tests/test_cases/test_failure/test_failure.py b/tests/test_cases/test_failure/test_failure.py deleted file mode 100644 index c7629dfb..00000000 --- a/tests/test_cases/test_failure/test_failure.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause -""" -Tests the failure path for tests in the regression.py and xunit_reporter.py. - -The Makefile in this folder is specially set up to squash any error code due -to a failing test and ensures the failing test is reported properly. -""" -import cocotb - - -@cocotb.test() -async def test_fail(_): - assert False diff --git a/tests/test_cases/test_fatal/Makefile b/tests/test_cases/test_fatal/Makefile deleted file mode 100644 index 9f3a7804..00000000 --- a/tests/test_cases/test_fatal/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -TOPLEVEL_LANG ?= verilog -TOPLEVEL := fatal -MODULE := test_fatal - -PWD=$(shell pwd) - -ifeq ($(TOPLEVEL_LANG),verilog) - VERILOG_SOURCES := $(PWD)/fatal.sv -else ifeq ($(TOPLEVEL_LANG),vhdl) - VHDL_SOURCES := $(PWD)/fatal.vhd - ifneq ($(filter $(SIM),ius xcelium),) - SIM_ARGS += -v93 - endif -endif - -# squash error code from simulator and ensure the cocotb test passed -.PHONY: override_for_this_test -override_for_this_test: - -$(MAKE) all - $(call check_for_results_file) - ../../../bin/combine_results.py &> /dev/null - -include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/tests/test_cases/test_fatal/fatal.sv b/tests/test_cases/test_fatal/fatal.sv deleted file mode 100644 index 8ffecd91..00000000 --- a/tests/test_cases/test_fatal/fatal.sv +++ /dev/null @@ -1,16 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright cocotb contributors -// Licensed under the Revised BSD License, see LICENSE for details. -// SPDX-License-Identifier: BSD-3-Clause USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -//----------------------------------------------------------------------------- - -`timescale 1 ps / 1 ps - -module fatal; - -initial begin - #10 $fatal(1, "This is a fatal message that finishes the test"); -end - -endmodule diff --git a/tests/test_cases/test_fatal/fatal.vhd b/tests/test_cases/test_fatal/fatal.vhd deleted file mode 100644 index 49edd145..00000000 --- a/tests/test_cases/test_fatal/fatal.vhd +++ /dev/null @@ -1,18 +0,0 @@ --- Copyright cocotb contributors --- Licensed under the Revised BSD License, see LICENSE for details. --- SPDX-License-Identifier: BSD-3-Clause USE OF THIS --- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -entity fatal is -end entity fatal; - -architecture behav of fatal is -begin - - fatal_proc : process is - begin - wait for 10 ns; - report "This is a fatal message that finishes the test" severity FAILURE; - end process fatal_proc; - -end architecture behav; diff --git a/tests/test_cases/test_fatal/test_fatal.py b/tests/test_cases/test_fatal/test_fatal.py deleted file mode 100644 index 22095de2..00000000 --- a/tests/test_cases/test_fatal/test_fatal.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import cocotb -from cocotb.triggers import Timer -from cocotb.result import SimFailure - - -@cocotb.test( - expect_error=SimFailure, - skip=cocotb.SIM_NAME.lower().startswith("riviera")) -async def test_fatal(_): - await Timer(100, 'ns') diff --git a/tests/test_cases/test_force_release/Makefile b/tests/test_cases/test_force_release/Makefile deleted file mode 100644 index 40667d67..00000000 --- a/tests/test_cases/test_force_release/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause - -include ../../designs/sample_module/Makefile - -MODULE ?= test_force_release diff --git a/tests/test_cases/test_force_release/test_force_release.py b/tests/test_cases/test_force_release/test_force_release.py deleted file mode 100644 index 492202dd..00000000 --- a/tests/test_cases/test_force_release/test_force_release.py +++ /dev/null @@ -1,34 +0,0 @@ -import logging - -import cocotb -from cocotb.triggers import Timer -from cocotb.result import TestFailure -from cocotb.handle import Force, Release - - -@cocotb.test(expect_fail=cocotb.SIM_NAME in ["GHDL"]) -async def test_force_release(dut): - """ - Test force and release on simulation handles - """ - log = logging.getLogger("cocotb.test") - await Timer(10, "ns") - dut.stream_in_data = 4 - dut.stream_out_data_comb = Force(5) - await Timer(10, "ns") - got_in = int(dut.stream_in_data) - got_out = int(dut.stream_out_data_comb) - log.info("dut.stream_in_data = %d" % got_in) - log.info("dut.stream_out_data_comb = %d" % got_out) - if got_in == got_out: - raise TestFailure("stream_in_data and stream_out_data_comb should not match when force is active!") - - dut.stream_out_data_comb = Release() - dut.stream_in_data = 3 - await Timer(10, "ns") - got_in = int(dut.stream_in_data) - got_out = int(dut.stream_out_data_comb) - log.info("dut.stream_in_data = %d" % got_in) - log.info("dut.stream_out_data_comb = %d" % got_out) - if got_in != got_out: - raise TestFailure("stream_in_data and stream_out_data_comb should match when output was released!") diff --git a/tests/test_cases/test_iteration_mixedlang/Makefile b/tests/test_cases/test_iteration_mixedlang/Makefile deleted file mode 100644 index ba9a83ed..00000000 --- a/tests/test_cases/test_iteration_mixedlang/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -############################################################################### -# Copyright (c) 2015 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -include ../../designs/uart2bus/Makefile -MODULE = test_iteration diff --git a/tests/test_cases/test_iteration_mixedlang/test_iteration.py b/tests/test_cases/test_iteration_mixedlang/test_iteration.py deleted file mode 100644 index 311c29e5..00000000 --- a/tests/test_cases/test_iteration_mixedlang/test_iteration.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (c) 2015 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import logging - -import cocotb - - -def recursive_dump(parent, log): - """ - Recursively iterate through every object and log a message - - Returns a count of the total number of objects found - """ - count = 0 - for thing in parent: - count += 1 - log.info("Found %s.%s (%s)", parent._name, thing._name, type(thing)) - count += recursive_dump(thing, log) - return count - - -@cocotb.test(expect_fail=True) -async def test_drivers(dut): - """Try iterating over drivers of a signal. - - Seems that few simulators implement ``vpiDriver``. - """ - tlog = logging.getLogger("cocotb.test") - drivers = list(dut.i_verilog.uart1.uart_rx_1.rx_data.drivers()) - assert drivers, "No drivers found for dut.i_verilog.uart1.uart_rx_1.rx_data" - for driver in drivers: - tlog.info("Found %s" % repr(driver)) - - -@cocotb.test(expect_fail=True) -async def test_loads(dut): - """Try iterating over loads of a signal. - - Seems that few simulators implement ``vpiLoad``. - """ - tlog = logging.getLogger("cocotb.test") - loads = list(dut.i_verilog.uart1.ser_in.loads()) - assert loads, "No loads found for dut.i_verilog.uart1.ser_in" - for load in loads: - tlog.info("Found %s" % repr(load)) - - -@cocotb.test() -async def recursive_discovery(dut): - """Recursively discover every single object in the design.""" - if cocotb.SIM_NAME.lower().startswith(("ncsim", "xmsim")): - # vpiAlways = 31 and vpiStructVar = 2 do not show up in IUS/Xcelium - pass_total = 975 - elif cocotb.SIM_NAME.lower().startswith(("modelsim")): - pass_total = 991 - else: - pass_total = 1024 - - tlog = logging.getLogger("cocotb.test") - total = recursive_dump(dut, tlog) - - assert pass_total == total, "Expected %d but found %d" % (pass_total, total) - tlog.info("Found a total of %d things", total) - - assert isinstance(dut.i_verilog.uart1.baud_gen_1.baud_freq, cocotb.handle.ModifiableObject), \ - ("Expected dut.i_verilog.uart1.baud_gen_1.baud_freq to be modifiable" - " but it was %s" % type(dut.i_verilog.uart1.baud_gen_1.baud_freq).__name__) - - -@cocotb.test() -async def recursive_discovery_boundary(dut): - """Iteration through the boundary works but this just double checks.""" - if cocotb.SIM_NAME.lower().startswith(("ncsim", "xmsim")): - pass_total = 462 - else: - pass_total = 478 - - tlog = logging.getLogger("cocotb.test") - total = recursive_dump(dut.i_vhdl, tlog) - tlog.info("Found a total of %d things", total) - assert total == pass_total, "Expected %d objects but found %d" % (pass_total, total) diff --git a/tests/test_cases/test_iteration_verilog/Makefile b/tests/test_cases/test_iteration_verilog/Makefile deleted file mode 100644 index fd4e4c60..00000000 --- a/tests/test_cases/test_iteration_verilog/Makefile +++ /dev/null @@ -1,49 +0,0 @@ -############################################################################### -# Copyright (c) 2015 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -TOPLEVEL_LANG ?= verilog - -ifneq ($(TOPLEVEL_LANG),verilog) - -all: - @echo "Skipping test due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being verilog" -clean:: - -else - -PWD=$(shell pwd) - -COCOTB?=$(PWD)/../../.. - -VERILOG_SOURCES = $(COCOTB)/examples/endian_swapper/hdl/endian_swapper.sv -TOPLEVEL = endian_swapper_sv - -MODULE = test_iteration_es - -include $(shell cocotb-config --makefiles)/Makefile.sim - -endif diff --git a/tests/test_cases/test_iteration_verilog/test_iteration_es.py b/tests/test_cases/test_iteration_verilog/test_iteration_es.py deleted file mode 100644 index 5e5e9d8f..00000000 --- a/tests/test_cases/test_iteration_verilog/test_iteration_es.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) 2015, 2018 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import logging - -import cocotb -from cocotb.triggers import First -from cocotb.result import TestFailure - - -@cocotb.test(expect_fail=cocotb.SIM_NAME in ["Icarus Verilog"]) -async def recursive_discovery(dut): - """ - Recursively discover every single object in the design - """ - if cocotb.SIM_NAME.lower().startswith(("modelsim", - "ncsim", - "xmsim", - "chronologic simulation vcs")): - # vpiAlways does not show up - pass_total = 259 - else: - pass_total = 265 - - tlog = logging.getLogger("cocotb.test") - - def dump_all_the_things(parent): - count = 0 - for thing in parent: - count += 1 - tlog.info("Found %s.%s (%s)", parent._name, thing._name, type(thing)) - count += dump_all_the_things(thing) - return count - total = dump_all_the_things(dut) - tlog.info("Found a total of %d things", total) - if total != pass_total: - raise TestFailure("Expected %d objects but found %d" % (pass_total, total)) - - -async def iteration_loop(dut): - for thing in dut: - thing._log.info("Found something: %s" % thing._fullname) - - -@cocotb.test() -async def dual_iteration(dut): - loop_one = cocotb.fork(iteration_loop(dut)) - loop_two = cocotb.fork(iteration_loop(dut)) - - await First(loop_one.join(), loop_two.join()) diff --git a/tests/test_cases/test_iteration_vhdl/Makefile b/tests/test_cases/test_iteration_vhdl/Makefile deleted file mode 100644 index 65de1604..00000000 --- a/tests/test_cases/test_iteration_vhdl/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -############################################################################### -# Copyright (c) 2015 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -include ../../designs/viterbi_decoder_axi4s/Makefile -MODULE = test_iteration - -ifneq ($(filter $(SIM),ius xcelium),) - SIM_ARGS += -notimezeroasrtmsg -endif - -ifeq ($(SIM),ghdl) - SIM_ARGS += --ieee-asserts=disable-at-0 -endif diff --git a/tests/test_cases/test_iteration_vhdl/test_iteration.py b/tests/test_cases/test_iteration_vhdl/test_iteration.py deleted file mode 100644 index 9d29383b..00000000 --- a/tests/test_cases/test_iteration_vhdl/test_iteration.py +++ /dev/null @@ -1,130 +0,0 @@ -# Copyright (c) 2015, 2018 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import logging - -import cocotb -from cocotb.triggers import Timer, Combine -from cocotb.result import TestFailure - - -def total_object_count(): - """Return the total object count based on simulator.""" - SIM_NAME = cocotb.SIM_NAME.lower() - SIM_VERSION = cocotb.SIM_VERSION.lower() - - if SIM_NAME.startswith( - ( - "ncsim", - "xmsim", - "modelsim", - ) - ): - return 34569 - - # Riviera-PRO - if SIM_NAME.startswith("riviera"): - if SIM_VERSION.startswith(("2019.10", "2020.")): - return 27359 - if SIM_VERSION.startswith("2016.02"): - return 32393 - return 34569 - - # Active-HDL - if SIM_NAME.startswith("aldec"): - if SIM_VERSION.startswith("11.1"): - # Active-HDL 11.1 only finds 'inbranch_tdata_low' inside the gen_acs for generate block - return 27359 - if SIM_VERSION.startswith("10.01"): - # Active-HDL 10.1 doesn't find any signals declared inside the gen_acs for generate block - return 26911 - - return 0 - - -@cocotb.test(skip=(total_object_count() == 0)) -async def recursive_discovery(dut): - """Recursively discover every single object in the design.""" - - pass_total = total_object_count() - - tlog = logging.getLogger("cocotb.test") - await Timer(100) - - def dump_all_the_things(parent): - count = 0 - for thing in parent: - count += 1 - tlog.debug("Found %s.%s (%s)", parent._name, thing._name, type(thing)) - count += dump_all_the_things(thing) - return count - total = dump_all_the_things(dut) - tlog.info("Found a total of %d things", total) - if total != pass_total: - raise TestFailure("Expected %d objects but found %d" % (pass_total, total)) - - -@cocotb.test() -async def discovery_all(dut): - """Discover everything on top-level.""" - dut._log.info("Iterating over top-level to discover objects") - for thing in dut: - thing._log.info("Found something: %s", thing._fullname) - - dut._log.info("length of dut.inst_acs is %d", len(dut.gen_acs)) - item = dut.gen_acs[3] - item._log.info("this is item") - - -@cocotb.test() -async def dual_iteration(dut): - """Test iteration over top-level in two forked coroutines.""" - async def iteration_loop(): - for thing in dut: - thing._log.info("Found something: %s", thing._fullname) - await Timer(1) - - loop_one = cocotb.fork(iteration_loop()) - loop_two = cocotb.fork(iteration_loop()) - - await Combine(loop_one, loop_two) - - -@cocotb.test(expect_fail=(cocotb.SIM_NAME.lower().startswith("riviera") and cocotb.SIM_VERSION.startswith(("2019.10", "2020."))) or cocotb.SIM_NAME.lower().startswith("aldec")) -async def test_n_dimension_array(dut): - """Test iteration over multi-dimensional array.""" - tlog = logging.getLogger("cocotb.test") - inner_count = 0 - outer_count = 0 - config = dut.inst_ram_ctrl.config - # This signal is a 2 x 7 vhpiEnumVecVal - for thing in config: - for sub_thing in thing: - tlog.info("Found %s", sub_thing._name) - inner_count += 1 - outer_count += 1 - - assert outer_count == 2, outer_count - assert inner_count == 14, inner_count diff --git a/tests/test_cases/test_module_var_empty/Makefile b/tests/test_cases/test_module_var_empty/Makefile deleted file mode 100644 index f8552f8a..00000000 --- a/tests/test_cases/test_module_var_empty/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -include ../../designs/sample_module/Makefile - -# test MODULE is empty - -MODULE := " " diff --git a/tests/test_cases/test_module_var_messy/Makefile b/tests/test_cases/test_module_var_messy/Makefile deleted file mode 100644 index dd33a79d..00000000 --- a/tests/test_cases/test_module_var_messy/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -include ../../designs/sample_module/Makefile - -# test MODULE contains leading and trailing separators - -MODULE=" , test_nothing ," diff --git a/tests/test_cases/test_module_var_messy/test_nothing.py b/tests/test_cases/test_module_var_messy/test_nothing.py deleted file mode 100644 index 0b9efadd..00000000 --- a/tests/test_cases/test_module_var_messy/test_nothing.py +++ /dev/null @@ -1 +0,0 @@ -""" This test module is purposefully empty """ diff --git a/tests/test_cases/test_module_without_tests/Makefile b/tests/test_cases/test_module_without_tests/Makefile deleted file mode 100644 index 2799c094..00000000 --- a/tests/test_cases/test_module_without_tests/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -include ../../designs/sample_module/Makefile - -# test MODULE is set, but there are no tests across any of the modules - -MODULE=test_nothing diff --git a/tests/test_cases/test_module_without_tests/test_nothing.py b/tests/test_cases/test_module_without_tests/test_nothing.py deleted file mode 100644 index 0b9efadd..00000000 --- a/tests/test_cases/test_module_without_tests/test_nothing.py +++ /dev/null @@ -1 +0,0 @@ -""" This test module is purposefully empty """ diff --git a/tests/test_cases/test_multi_dimension_array/Makefile b/tests/test_cases/test_multi_dimension_array/Makefile deleted file mode 100644 index bfba7d65..00000000 --- a/tests/test_cases/test_multi_dimension_array/Makefile +++ /dev/null @@ -1,50 +0,0 @@ -############################################################################### -# Copyright (c) 2016, 2018 Potential Ventures Ltd -# Copyright (c) 2016 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -ifeq ($(SIM),) -all: - @echo "Skipping test_multi_dimension_array since icarus doesn't support indexing" - -clean:: -# nothing to clean, just define target in this branch - -else ifeq ($(shell echo $(SIM) | tr A-Z a-z),icarus) -all: - @echo "Skipping test_multi_dimension_array since icarus doesn't support indexing" - -clean:: -# nothing to clean, just define target in this branch - -else - -include ../../designs/multi_dimension_array/Makefile - -MODULE = test_cocotb_array - -endif diff --git a/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py b/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py deleted file mode 100644 index 62262d06..00000000 --- a/tests/test_cases/test_multi_dimension_array/test_cocotb_array.py +++ /dev/null @@ -1,212 +0,0 @@ -import cocotb -from cocotb.result import TestFailure -from cocotb.triggers import Timer - - -@cocotb.test() -async def test_in_vect_packed(dut): - await Timer(1, "ns") - print("Setting: dut.in_vect_packed type %s" % type(dut.in_vect_packed)) - dut.in_vect_packed = 0x5 - await Timer(1, "ns") - print("Getting: dut.out_vect_packed type %s" % type(dut.out_vect_packed)) - if dut.out_vect_packed != 0x5: - raise TestFailure("Failed to readback dut.out_vect_packed") - - -@cocotb.test() -async def test_in_vect_unpacked(dut): - await Timer(1, "ns") - print("Setting: dut.in_vect_unpacked type %s" % type(dut.in_vect_unpacked)) - dut.in_vect_unpacked = [0x1, 0x0, 0x1] - await Timer(1, "ns") - print("Getting: dut.out_vect_unpacked type %s" % type(dut.out_vect_unpacked)) - if dut.out_vect_unpacked != [0x1, 0x0, 0x1]: - raise TestFailure("Failed to readback dut.out_vect_unpacked") - - -@cocotb.test() -async def test_in_arr(dut): - await Timer(1, "ns") - print("Setting: dut.in_arr type %s" % type(dut.in_arr)) - dut.in_arr = 0x5 - await Timer(1, "ns") - print("Getting: dut.out_arr type %s" % type(dut.out_arr)) - if dut.out_arr != 0x5: - raise TestFailure("Failed to readback dut.out_arr") - - -@cocotb.test() -async def test_in_2d_vect_packed_packed(dut): - await Timer(1, "ns") - print("Setting: dut.in_2d_vect_packed_packed type %s" % type(dut.in_2d_vect_packed_packed)) - dut.in_2d_vect_packed_packed = (0x5 << 6) | (0x5 << 3) | 0x5 - await Timer(1, "ns") - print("Getting: dut.out_2d_vect_packed_packed type %s" % type(dut.out_2d_vect_packed_packed)) - if dut.out_2d_vect_packed_packed != (0x5 << 6) | (0x5 << 3) | 0x5: - raise TestFailure("Failed to readback dut.out_2d_vect_packed_packed") - - -@cocotb.test() -async def test_in_2d_vect_packed_unpacked(dut): - await Timer(1, "ns") - print("Setting: dut.in_2d_vect_packed_unpacked type %s" % type(dut.in_2d_vect_packed_unpacked)) - dut.in_2d_vect_packed_unpacked = [0x5, 0x5, 0x5] - await Timer(1, "ns") - print("Getting: dut.out_2d_vect_packed_unpacked type %s" % type(dut.out_2d_vect_packed_unpacked)) - if dut.out_2d_vect_packed_unpacked != [0x5, 0x5, 0x5]: - raise TestFailure("Failed to readback dut.out_2d_vect_packed_unpacked") - - -@cocotb.test() -async def test_in_2d_vect_unpacked_unpacked(dut): - await Timer(1, "ns") - print("Setting: dut.in_2d_vect_unpacked_unpacked type %s" % type(dut.in_2d_vect_unpacked_unpacked)) - dut.in_2d_vect_unpacked_unpacked = 3*[[0x1, 0x0, 0x1]] - await Timer(1, "ns") - print("Getting: dut.out_2d_vect_unpacked_unpacked type %s" % type(dut.out_2d_vect_unpacked_unpacked)) - if dut.out_2d_vect_unpacked_unpacked != 3*[[0x1, 0x0, 0x1]]: - raise TestFailure("Failed to readback dut.out_2d_vect_unpacked_unpacked") - - -@cocotb.test() -async def test_in_arr_packed(dut): - await Timer(1, "ns") - print("Setting: dut.in_arr_packed type %s" % type(dut.in_arr_packed)) - dut.in_arr_packed = 365 - await Timer(1, "ns") - print("Getting: dut.out_arr_packed type %s" % type(dut.out_arr_packed)) - if dut.out_arr_packed != 365: - raise TestFailure("Failed to readback dut.out_arr_packed") - - -@cocotb.test() -async def test_in_arr_unpacked(dut): - await Timer(1, "ns") - print("Setting: dut.in_arr_unpackedtype %s" % type(dut.in_arr_unpacked)) - dut.in_arr_unpacked = [0x5, 0x5, 0x5] - await Timer(1, "ns") - print("Getting: dut.out_arr_unpackedtype %s" % type(dut.out_arr_unpacked)) - if dut.out_arr_unpacked != [0x5, 0x5, 0x5]: - raise TestFailure("Failed to readback dut.out_arr_unpacked") - - -@cocotb.test() -async def test_in_2d_arr(dut): - await Timer(1, "ns") - print("Setting: dut.in_2d_arr type %s" % type(dut.in_2d_arr)) - dut.in_2d_arr = 365 - await Timer(1, "ns") - print("Getting: dut.out_2d_arr type %s" % type(dut.out_2d_arr)) - if dut.out_2d_arr != 365: - raise TestFailure("Failed to readback dut.out_2d_arr") - - -@cocotb.test() -async def test_in_vect_packed_packed_packed(dut): - await Timer(1, "ns") - print("Setting: dut.in_vect_packed_packed_packed type %s" % type(dut.in_vect_packed_packed_packed)) - dut.in_vect_packed_packed_packed = 95869805 - await Timer(1, "ns") - print("Getting: dut.out_vect_packed_packed_packed type %s" % type(dut.out_vect_packed_packed_packed)) - if dut.out_vect_packed_packed_packed != 95869805: - raise TestFailure("Failed to readback dut.out_vect_packed_packed_packed") - - -@cocotb.test() -async def test_in_vect_packed_packed_unpacked(dut): - await Timer(1, "ns") - print("Setting: dut.in_vect_packed_packed_unpacked type %s" % type(dut.in_vect_packed_packed_unpacked)) - dut.in_vect_packed_packed_unpacked = [365, 365, 365] - await Timer(1, "ns") - print("Getting: dut.out_vect_packed_packed_unpacked type %s" % type(dut.out_vect_packed_packed_unpacked)) - if dut.out_vect_packed_packed_unpacked != [365, 365, 365]: - raise TestFailure("Failed to readback dut.out_vect_packed_packed_unpacked") - - -@cocotb.test() -async def test_in_vect_packed_unpacked_unpacked(dut): - await Timer(1, "ns") - print("Setting: dut.in_vect_packed_unpacked_unpacked type %s" % type(dut.in_vect_packed_unpacked_unpacked)) - dut.in_vect_packed_unpacked_unpacked = 3 *[3 * [5] ] - await Timer(1, "ns") - print("Getting: dut.out_vect_packed_unpacked_unpacked type %s" % type(dut.out_vect_packed_unpacked_unpacked)) - if dut.out_vect_packed_unpacked_unpacked != 3 *[3 * [5] ]: - raise TestFailure("Failed to readback dut.out_vect_packed_unpacked_unpacked") - - -@cocotb.test() -async def test_in_vect_unpacked_unpacked_unpacked(dut): - await Timer(1, "ns") - print("Setting: dut.in_vect_unpacked_unpacked_unpacked type %s" % type(dut.in_vect_unpacked_unpacked_unpacked)) - dut.in_vect_unpacked_unpacked_unpacked = 3 *[3 * [[1, 0, 1]]] - await Timer(1, "ns") - print("Getting: dut.out_vect_unpacked_unpacked_unpacked type %s" % type(dut.out_vect_unpacked_unpacked_unpacked)) - if dut.out_vect_unpacked_unpacked_unpacked != 3 *[3 * [[1, 0, 1]]]: - raise TestFailure("Failed to readback dut.out_vect_unpacked_unpacked_unpacked") - - -@cocotb.test() -async def test_in_arr_packed_packed(dut): - await Timer(1, "ns") - print("Setting: dut.in_arr_packed_packed type %s" % type(dut.in_arr_packed_packed)) - dut.in_arr_packed_packed = (365 << 18) | (365 << 9) | (365) - await Timer(1, "ns") - print("Getting: dut.out_arr_packed_packed type %s" % type(dut.out_arr_packed_packed)) - if dut.out_arr_packed_packed != (365 << 18) | (365 << 9) | (365): - raise TestFailure("Failed to readback dut.out_arr_packed_packed") - - -@cocotb.test() -async def test_in_arr_packed_unpacked(dut): - await Timer(1, "ns") - print("Setting: dut.in_arr_packed_unpacked type %s" % type(dut.in_arr_packed_unpacked)) - dut.in_arr_packed_unpacked = [365, 365, 365] - await Timer(1, "ns") - print("Getting: dut.out_arr_packed_unpacked type %s" % type(dut.out_arr_packed_unpacked)) - if dut.out_arr_packed_unpacked != [365, 365, 365]: - raise TestFailure("Failed to readback dut.out_arr_packed_unpacked") - - -@cocotb.test() -async def test_in_arr_unpacked_unpacked(dut): - await Timer(1, "ns") - print("Setting: dut.in_arr_unpacked_unpacked type %s" % type(dut.in_arr_unpacked_unpacked)) - dut.in_arr_unpacked_unpacked = 3 *[3 * [5] ] - await Timer(1, "ns") - print("Getting: dut.out_arr_unpacked_unpacked type %s" % type(dut.out_arr_unpacked_unpacked)) - if dut.out_arr_unpacked_unpacked != 3 *[3 * [5] ]: - raise TestFailure("Failed to readback dut.out_arr_unpacked_unpacked") - - -@cocotb.test() -async def test_in_2d_arr_packed(dut): - await Timer(1, "ns") - print("Setting: dut.in_2d_arr_packed type %s" % type(dut.in_2d_arr_packed)) - dut.in_2d_arr_packed = (365 << 18) | (365 << 9) | (365) - await Timer(1, "ns") - print("Getting: dut.out_2d_arr_packed type %s" % type(dut.out_2d_arr_packed)) - if dut.out_2d_arr_packed != (365 << 18) | (365 << 9) | (365): - raise TestFailure("Failed to readback dut.out_2d_arr_packed") - - -@cocotb.test() -async def test_in_2d_arr_unpacked(dut): - await Timer(1, "ns") - print("Setting: dut.in_2d_arr_unpacked type %s" % type(dut.in_2d_arr_unpacked)) - dut.in_2d_arr_unpacked = [365, 365, 365] - await Timer(1, "ns") - print("Getting: dut.out_2d_arr_unpacked type %s" % type(dut.out_2d_arr_unpacked)) - if dut.out_2d_arr_unpacked != [365, 365, 365]: - raise TestFailure("Failed to readback dut.out_2d_arr_unpacked") - - -@cocotb.test() -async def test_in_3d_arr(dut): - await Timer(1, "ns") - print("Setting: dut.in_3d_arr type %s" % type(dut.in_3d_arr)) - dut.in_3d_arr = (365 << 18) | (365 << 9) | (365) - await Timer(1, "ns") - print("Getting: dut.out_3d_arr type %s" % type(dut.out_3d_arr)) - if dut.out_3d_arr != (365 << 18) | (365 << 9) | (365): - raise TestFailure("Failed to readback dut.out_3d_arr") diff --git a/tests/test_cases/test_plusargs/Makefile b/tests/test_cases/test_plusargs/Makefile deleted file mode 100644 index a93b3c8c..00000000 --- a/tests/test_cases/test_plusargs/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -############################################################################### -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -include ../../designs/plusargs_module/Makefile - -PLUSARGS=+foo=bar +test1 +test2 +options=fubar - -MODULE = plusargs diff --git a/tests/test_cases/test_plusargs/plusargs.py b/tests/test_cases/test_plusargs/plusargs.py deleted file mode 100644 index e3011739..00000000 --- a/tests/test_cases/test_plusargs/plusargs.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2013 Potential Ventures Ltd -# Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -""" - plusarg testing -""" - -import cocotb -from cocotb.result import TestFailure - - -@cocotb.test() -async def plusargs_test(dut): - """Demonstrate plusarg access from Python test""" - - for name in cocotb.plusargs: - print("COCOTB:", name, cocotb.plusargs[name]) - - if cocotb.plusargs['foo'] != 'bar': - raise TestFailure("plusargs 'foo' value '{}' does not match expected 'bar'".format(cocotb.plusargs['foo'])) diff --git a/tests/test_cases/test_skipped/Makefile b/tests/test_cases/test_skipped/Makefile deleted file mode 100644 index 4573df0c..00000000 --- a/tests/test_cases/test_skipped/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause - -include ../../designs/sample_module/Makefile - -SKIPPED_TEST_FILE = ran_skipped_test~ - -clean:: - @rm -rf ${SKIPPED_TEST_FILE} - -# Override the default target. We need to run clean (to remove the cached test file) -# and then test to make sure it is recreated. -.DEFAULT_GOAL := override -.PHONY: override -override: clean all - @test -f $(SKIPPED_TEST_FILE) || (echo "ERROR: skip=True test was not ran!" >&2 && exit 1) - -# Set TESTCASE; run test_skipped even though skip=True is set. -TESTCASE = test_skipped - -MODULE = test_skipped diff --git a/tests/test_cases/test_skipped/test_skipped.py b/tests/test_cases/test_skipped/test_skipped.py deleted file mode 100644 index eabe8dbb..00000000 --- a/tests/test_cases/test_skipped/test_skipped.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright cocotb contributors -# Licensed under the Revised BSD License, see LICENSE for details. -# SPDX-License-Identifier: BSD-3-Clause - -import pathlib - -import cocotb - -skipped_file_name = "ran_skipped_test~" - - -@cocotb.test(skip=True) -async def test_skipped(dut): - """ Touch a file so we can check that this test has run.""" - pathlib.Path(skipped_file_name).touch() diff --git a/tests/test_cases/test_verilog_access/Makefile b/tests/test_cases/test_verilog_access/Makefile deleted file mode 100644 index d89b11a6..00000000 --- a/tests/test_cases/test_verilog_access/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -############################################################################## -# Copyright (c) 2015 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -include ../../designs/uart2bus/Makefile -MODULE = test_verilog_access diff --git a/tests/test_cases/test_verilog_access/test_verilog_access.py b/tests/test_cases/test_verilog_access/test_verilog_access.py deleted file mode 100644 index 702a8047..00000000 --- a/tests/test_cases/test_verilog_access/test_verilog_access.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) 2015 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import logging - -import cocotb -from cocotb.handle import HierarchyObject, ModifiableObject -from cocotb.result import TestFailure - - -@cocotb.test() -async def port_not_hierarchy(dut): - """ - Test for issue raised by Luke - iteration causes a toplevel port type to - change from ModifiableObject to HierarchyObject - """ - fails = 0 - tlog = logging.getLogger("cocotb.test") - - def check_instance(obj, objtype): - if not isinstance(obj, objtype): - tlog.error("Expected %s to be of type %s but got %s" % ( - obj._fullname, objtype.__name__, type(obj).__name__)) - return 1 - tlog.info("%s is %s" % (obj._fullname, type(obj).__name__)) - return 0 - - fails += check_instance(dut.clk, ModifiableObject) - fails += check_instance(dut.i_verilog, HierarchyObject) - fails += check_instance(dut.i_verilog.clock, ModifiableObject) - fails += check_instance(dut.i_verilog.tx_data, ModifiableObject) - - for _ in dut: - pass - - for _ in dut.i_verilog: - pass - - fails += check_instance(dut.clk, ModifiableObject) - fails += check_instance(dut.i_verilog, HierarchyObject) - fails += check_instance(dut.i_verilog.clock, ModifiableObject) - fails += check_instance(dut.i_verilog.tx_data, ModifiableObject) - - if fails: - raise TestFailure("%d Failures during the test" % fails) diff --git a/tests/test_cases/test_vhdl_access/Makefile b/tests/test_cases/test_vhdl_access/Makefile deleted file mode 100644 index d7897648..00000000 --- a/tests/test_cases/test_vhdl_access/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -############################################################################### -# Copyright (c) 2015 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -include ../../designs/viterbi_decoder_axi4s/Makefile -MODULE = test_vhdl_access diff --git a/tests/test_cases/test_vhdl_access/test_vhdl_access.py b/tests/test_cases/test_vhdl_access/test_vhdl_access.py deleted file mode 100644 index cb929281..00000000 --- a/tests/test_cases/test_vhdl_access/test_vhdl_access.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (c) 2015 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import logging - -import cocotb -from cocotb.handle import HierarchyObject, ModifiableObject, IntegerObject, ConstantObject, EnumObject -from cocotb.result import TestFailure - - -@cocotb.test() -async def check_enum_object(dut): - """ - Enumerations currently behave as normal signals - - TODO: Implement an EnumObject class and detect valid string mappings - """ - if not isinstance(dut.inst_ram_ctrl.write_ram_fsm, EnumObject): - raise TestFailure("Expected the FSM enum to be an EnumObject") - - -@cocotb.test() -async def check_objects(dut): - """ - Check the types of objects that are returned - """ - tlog = logging.getLogger("cocotb.test") - fails = 0 - - def check_instance(obj, objtype): - if not isinstance(obj, objtype): - tlog.error("Expected %s to be of type %s but got %s" % ( - obj._fullname, objtype.__name__, type(obj).__name__)) - return 1 - tlog.info("%s is %s" % (obj._fullname, type(obj).__name__)) - return 0 - - # Hierarchy checks - fails += check_instance(dut.inst_axi4s_buffer, HierarchyObject) - fails += check_instance(dut.gen_branch_distance[0], HierarchyObject) - fails += check_instance(dut.gen_branch_distance[0].inst_branch_distance, HierarchyObject) - fails += check_instance(dut.gen_acs[0].inbranch_tdata_low, ModifiableObject) - fails += check_instance(dut.gen_acs[0].inbranch_tdata_low[0], ModifiableObject) - fails += check_instance(dut.aclk, ModifiableObject) - fails += check_instance(dut.s_axis_input_tdata, ModifiableObject) - fails += check_instance(dut.current_active, IntegerObject) - fails += check_instance(dut.inst_axi4s_buffer.DATA_WIDTH, ConstantObject) - fails += check_instance(dut.inst_ram_ctrl, HierarchyObject) - - if dut.inst_axi4s_buffer.DATA_WIDTH != 32: - tlog.error("Expected dut.inst_axi4s_buffer.DATA_WIDTH to be 32 but got %d", - dut.inst_axi4s_buffer.DATA_WIDTH) - fails += 1 - - try: - dut.inst_axi4s_buffer.DATA_WIDTH = 42 - tlog.error("Shouldn't be allowed to set a value on constant object") - fails += 1 - except TypeError as e: - pass - - try: - dut.inst_axi4s_buffer.DATA_WIDTH <= 42 - tlog.error("Shouldn't be allowed to set a value on constant object using __le__") - fails += 1 - except TypeError as e: - pass - - if fails: - raise TestFailure("%d Failures during the test" % fails) - - -@cocotb.test() -async def port_not_hierarchy(dut): - """ - Test for issue raised by Luke - iteration causes a toplevel port type to - change from ModifiableObject to HierarchyObject - """ - tlog = logging.getLogger("cocotb.test") - if not isinstance(dut.aclk, ModifiableObject): - tlog.error("dut.aclk should be ModifiableObject but got %s", type(dut.aclk).__name__) - else: - tlog.info("dut.aclk is ModifiableObject") - for _ in dut: - pass - if not isinstance(dut.aclk, ModifiableObject): - tlog.error("dut.aclk should be ModifiableObject but got %s", type(dut.aclk).__name__) - else: - tlog.info("dut.aclk is ModifiableObject") diff --git a/tox.ini b/tox.ini deleted file mode 100644 index a8973b0c..00000000 --- a/tox.ini +++ /dev/null @@ -1,83 +0,0 @@ -[tox] -# when changing this list, adapt CONTRIBUTING.md accordingly: -envlist = py{35,36,37,38,39}-{linux,macos,windows},docs - -# for the requires key -minversion = 3.2.0 - -# virtualenv is used by tox; versions below 16.1.0 cause a DeprecationWarning -# to be shown for all code which uses virtualenv, which is the case for all code -# we run through tox. (https://github.com/pypa/virtualenv/pull/1064) -requires = virtualenv >= 16.1 - -[testenv] -platform = - linux: linux|cygwin - macos: darwin - windows: win32 - -setenv = - CFLAGS = -Werror -Wno-deprecated-declarations -g --coverage - LDFLAGS = --coverage - CXXFLAGS = -Werror - COCOTB_LIBRARY_COVERAGE = 1 - -passenv = - SIM - TOPLEVEL_LANG - ALDEC_LICENSE_FILE - SYNOPSYS_LICENSE_FILE - LM_LICENSE_FILE - # allow tuning of matrix_multiplier test length - NUM_SAMPLES - -deps = - coverage - xunitparser - pytest - pytest-cov - -install_command = - windows: python -m pip install --global-option build_ext --global-option --compiler=mingw32 {opts} {packages} - python -m pip install {opts} {packages} - -commands = - pytest - make test - bash -c 'find . -type f -name "\.coverage\.cocotb" | xargs coverage combine --append' - -whitelist_externals = - make - bash - -# needed for coverage to work -usedevelop=True - -# Note: this target is *not* used by Read The Docs, it runs sphinx-build -# directly. Hence, all special build steps here are only relevant for -# local builds, not for RTD builds. Add all build steps which should be executed -# in RTD builds as Python code into the conf.py file. -[testenv:docs] -description = invoke sphinx-build to build the HTML docs -deps = -rdocumentation/requirements.txt -commands = sphinx-build -d "{toxworkdir}/docs_doctree" ./documentation/source "{toxworkdir}/docs_out" --color -bhtml {posargs} - python -c 'import pathlib; print("documentation available under file://\{0\}".format(pathlib.Path(r"{toxworkdir}") / "docs_out" / "index.html"))' - -[gh-actions] -python = - 3.5: py35 - 3.6: py36 - 3.7: py37 - 3.8: py38 - 3.9: py39 - -[gh-actions:env] -OS = - ubuntu-latest: linux - ubuntu-20.04: linux - ubuntu-18.04: linux - ubuntu-16.04: linux - macos-latest: macos - macos-10.15: macos - windows-latest: windows - windows-2019: windows From 844b379b28da382f02570fa68dc8b5d81b5f5b90 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 12 Dec 2020 17:57:48 -0600 Subject: [PATCH 2650/2656] Move testbenching files to new directory --- {cocotb => src/cocotb_bus}/bus.py | 0 {cocotb => src/cocotb_bus}/drivers/__init__.py | 0 {cocotb => src/cocotb_bus}/drivers/amba.py | 0 {cocotb => src/cocotb_bus}/drivers/avalon.py | 0 {cocotb => src/cocotb_bus}/drivers/opb.py | 0 {cocotb => src/cocotb_bus}/drivers/xgmii.py | 0 {cocotb => src/cocotb_bus}/monitors/__init__.py | 0 {cocotb => src/cocotb_bus}/monitors/avalon.py | 0 {cocotb => src/cocotb_bus}/monitors/xgmii.py | 0 {cocotb => src/cocotb_bus}/scoreboard.py | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename {cocotb => src/cocotb_bus}/bus.py (100%) rename {cocotb => src/cocotb_bus}/drivers/__init__.py (100%) rename {cocotb => src/cocotb_bus}/drivers/amba.py (100%) rename {cocotb => src/cocotb_bus}/drivers/avalon.py (100%) rename {cocotb => src/cocotb_bus}/drivers/opb.py (100%) rename {cocotb => src/cocotb_bus}/drivers/xgmii.py (100%) rename {cocotb => src/cocotb_bus}/monitors/__init__.py (100%) rename {cocotb => src/cocotb_bus}/monitors/avalon.py (100%) rename {cocotb => src/cocotb_bus}/monitors/xgmii.py (100%) rename {cocotb => src/cocotb_bus}/scoreboard.py (100%) diff --git a/cocotb/bus.py b/src/cocotb_bus/bus.py similarity index 100% rename from cocotb/bus.py rename to src/cocotb_bus/bus.py diff --git a/cocotb/drivers/__init__.py b/src/cocotb_bus/drivers/__init__.py similarity index 100% rename from cocotb/drivers/__init__.py rename to src/cocotb_bus/drivers/__init__.py diff --git a/cocotb/drivers/amba.py b/src/cocotb_bus/drivers/amba.py similarity index 100% rename from cocotb/drivers/amba.py rename to src/cocotb_bus/drivers/amba.py diff --git a/cocotb/drivers/avalon.py b/src/cocotb_bus/drivers/avalon.py similarity index 100% rename from cocotb/drivers/avalon.py rename to src/cocotb_bus/drivers/avalon.py diff --git a/cocotb/drivers/opb.py b/src/cocotb_bus/drivers/opb.py similarity index 100% rename from cocotb/drivers/opb.py rename to src/cocotb_bus/drivers/opb.py diff --git a/cocotb/drivers/xgmii.py b/src/cocotb_bus/drivers/xgmii.py similarity index 100% rename from cocotb/drivers/xgmii.py rename to src/cocotb_bus/drivers/xgmii.py diff --git a/cocotb/monitors/__init__.py b/src/cocotb_bus/monitors/__init__.py similarity index 100% rename from cocotb/monitors/__init__.py rename to src/cocotb_bus/monitors/__init__.py diff --git a/cocotb/monitors/avalon.py b/src/cocotb_bus/monitors/avalon.py similarity index 100% rename from cocotb/monitors/avalon.py rename to src/cocotb_bus/monitors/avalon.py diff --git a/cocotb/monitors/xgmii.py b/src/cocotb_bus/monitors/xgmii.py similarity index 100% rename from cocotb/monitors/xgmii.py rename to src/cocotb_bus/monitors/xgmii.py diff --git a/cocotb/scoreboard.py b/src/cocotb_bus/scoreboard.py similarity index 100% rename from cocotb/scoreboard.py rename to src/cocotb_bus/scoreboard.py From 23e1904272ee1b1102708fa7eca8fa07d8f35725 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 12 Dec 2020 18:19:30 -0600 Subject: [PATCH 2651/2656] Add enough to make cocotb-bus installable --- README.md | 3 +++ pyproject.toml | 3 +++ setup.py | 31 +++++++++++++++++++++++++++++++ src/cocotb_bus/__init__.py | 5 +++++ 4 files changed, 42 insertions(+) create mode 100644 README.md create mode 100644 pyproject.toml create mode 100644 setup.py create mode 100644 src/cocotb_bus/__init__.py diff --git a/README.md b/README.md new file mode 100644 index 00000000..79aeb864 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# cocotb-bus + +The new home of the [cocotb](https://github.com/cocotb) project's pre-packaged testbenching tools and reusable bus interfaces. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..e6ca9c75 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools", "wheel", "setuptools_scm"] +build-backend = "setuptools.build_meta" diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..f657c3a2 --- /dev/null +++ b/setup.py @@ -0,0 +1,31 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause + +from setuptools import setup, find_packages + + +with open("README.md") as file: + long_description = file.read() + + +if __name__ == "__main__": + setup( + name="cocotb-bus", + use_scm_version=dict( + write_to='src/cocotb_bus/_version.py', + version_scheme='release-branch-semver' + ), + author="cocotb maintainers", + author_email="cocotb@lists.librecores.org", + description="", + long_description=long_description, + long_description_content_type="text/markdown", + url="https://github.com/cocotb/cocotb-bus", + packages=find_packages("src"), + package_dir={"": "src"}, + install_requires=[ + "cocotb>1.4,<2.0" + ], + python_requires='>=3.5' + ) diff --git a/src/cocotb_bus/__init__.py b/src/cocotb_bus/__init__.py new file mode 100644 index 00000000..9323dfb5 --- /dev/null +++ b/src/cocotb_bus/__init__.py @@ -0,0 +1,5 @@ +# Copyright cocotb contributors +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause + +from ._version import version as __version__ # noqa From 09cdd14e7ee5d5ad82b89c14dbd226f3b9289d2c Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 12 Dec 2020 18:20:33 -0600 Subject: [PATCH 2652/2656] Remove executable bit --- src/cocotb_bus/bus.py | 0 src/cocotb_bus/drivers/__init__.py | 0 src/cocotb_bus/monitors/__init__.py | 0 src/cocotb_bus/scoreboard.py | 0 tests/Makefile | 0 5 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 src/cocotb_bus/bus.py mode change 100755 => 100644 src/cocotb_bus/drivers/__init__.py mode change 100755 => 100644 src/cocotb_bus/monitors/__init__.py mode change 100755 => 100644 src/cocotb_bus/scoreboard.py mode change 100755 => 100644 tests/Makefile diff --git a/src/cocotb_bus/bus.py b/src/cocotb_bus/bus.py old mode 100755 new mode 100644 diff --git a/src/cocotb_bus/drivers/__init__.py b/src/cocotb_bus/drivers/__init__.py old mode 100755 new mode 100644 diff --git a/src/cocotb_bus/monitors/__init__.py b/src/cocotb_bus/monitors/__init__.py old mode 100755 new mode 100644 diff --git a/src/cocotb_bus/scoreboard.py b/src/cocotb_bus/scoreboard.py old mode 100755 new mode 100644 diff --git a/tests/Makefile b/tests/Makefile old mode 100755 new mode 100644 From ca339cd5b9f84b557b064988880d0c225394d774 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 12 Dec 2020 18:23:21 -0600 Subject: [PATCH 2653/2656] Fix license headers in Python package --- LICENSE | 1 + src/cocotb_bus/bus.py | 29 +++-------------------------- src/cocotb_bus/drivers/__init__.py | 29 +++-------------------------- src/cocotb_bus/drivers/amba.py | 27 +++------------------------ src/cocotb_bus/drivers/avalon.py | 27 +++------------------------ src/cocotb_bus/drivers/opb.py | 27 +++------------------------ src/cocotb_bus/drivers/xgmii.py | 26 +++----------------------- src/cocotb_bus/monitors/__init__.py | 29 +++-------------------------- src/cocotb_bus/monitors/avalon.py | 27 +++------------------------ src/cocotb_bus/monitors/xgmii.py | 26 +++----------------------- src/cocotb_bus/scoreboard.py | 29 +++-------------------------- 11 files changed, 31 insertions(+), 246 deletions(-) diff --git a/LICENSE b/LICENSE index 3d7655a9..d662b491 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ Cocotb is licensed under the Revised BSD License. Full license text below. ############################################################################### +# Copyright cocotb maintainers # Copyright (c) 2013 Potential Ventures Ltd # Copyright (c) 2013 SolarFlare Communications Inc # All rights reserved. diff --git a/src/cocotb_bus/bus.py b/src/cocotb_bus/bus.py index 12211deb..ff6500f2 100644 --- a/src/cocotb_bus/bus.py +++ b/src/cocotb_bus/bus.py @@ -1,31 +1,8 @@ -#!/usr/bin/env python - +# Copyright cocotb contributors # Copyright (c) 2013 Potential Ventures Ltd # Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause """Common bus related functionality. A bus is simply defined as a collection of signals. diff --git a/src/cocotb_bus/drivers/__init__.py b/src/cocotb_bus/drivers/__init__.py index 3597b070..7a04e502 100644 --- a/src/cocotb_bus/drivers/__init__.py +++ b/src/cocotb_bus/drivers/__init__.py @@ -1,31 +1,8 @@ -#!/bin/env python - +# Copyright cocotb contributors # Copyright (c) 2013 Potential Ventures Ltd # Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause """Set of common driver base classes.""" diff --git a/src/cocotb_bus/drivers/amba.py b/src/cocotb_bus/drivers/amba.py index 1307ec71..01c506e1 100644 --- a/src/cocotb_bus/drivers/amba.py +++ b/src/cocotb_bus/drivers/amba.py @@ -1,28 +1,7 @@ +# Copyright cocotb contributors # Copyright (c) 2014 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause """Drivers for Advanced Microcontroller Bus Architecture.""" diff --git a/src/cocotb_bus/drivers/avalon.py b/src/cocotb_bus/drivers/avalon.py index 17c869a7..bf127d62 100644 --- a/src/cocotb_bus/drivers/avalon.py +++ b/src/cocotb_bus/drivers/avalon.py @@ -1,29 +1,8 @@ +# Copyright cocotb contributors # Copyright (c) 2013 Potential Ventures Ltd # Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause """Drivers for Intel Avalon interfaces. diff --git a/src/cocotb_bus/drivers/opb.py b/src/cocotb_bus/drivers/opb.py index 0a31ca14..20299ee5 100644 --- a/src/cocotb_bus/drivers/opb.py +++ b/src/cocotb_bus/drivers/opb.py @@ -1,28 +1,7 @@ +# Copyright cocotb contributors # Copyright (c) 2015 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause """ Drivers for On-chip Peripheral Bus. diff --git a/src/cocotb_bus/drivers/xgmii.py b/src/cocotb_bus/drivers/xgmii.py index 288ba52c..2b073f08 100644 --- a/src/cocotb_bus/drivers/xgmii.py +++ b/src/cocotb_bus/drivers/xgmii.py @@ -1,27 +1,7 @@ +# Copyright cocotb contributors # Copyright (c) 2013 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the names of its -# contributors may be used to endorse or promote products derived from this -# software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause """Drivers for XGMII (10 Gigabit Media Independent Interface).""" diff --git a/src/cocotb_bus/monitors/__init__.py b/src/cocotb_bus/monitors/__init__.py index e5b27fc3..8ed647b6 100644 --- a/src/cocotb_bus/monitors/__init__.py +++ b/src/cocotb_bus/monitors/__init__.py @@ -1,31 +1,8 @@ -#!/bin/env python - +# Copyright cocotb contributors # Copyright (c) 2013 Potential Ventures Ltd # Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause """Class defining the standard interface for a monitor within a testbench. diff --git a/src/cocotb_bus/monitors/avalon.py b/src/cocotb_bus/monitors/avalon.py index 1aebd471..98f94e67 100644 --- a/src/cocotb_bus/monitors/avalon.py +++ b/src/cocotb_bus/monitors/avalon.py @@ -1,29 +1,8 @@ +# Copyright cocotb contributors # Copyright (c) 2013 Potential Ventures Ltd # Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause """Monitors for Intel Avalon interfaces. diff --git a/src/cocotb_bus/monitors/xgmii.py b/src/cocotb_bus/monitors/xgmii.py index e501a312..6f2474c7 100644 --- a/src/cocotb_bus/monitors/xgmii.py +++ b/src/cocotb_bus/monitors/xgmii.py @@ -1,27 +1,7 @@ +# Copyright cocotb contributors # Copyright (c) 2013 Potential Ventures Ltd -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd nor the names of its -# contributors may be used to endorse or promote products derived from this -# software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause """Monitor for XGMII (10 Gigabit Media Independent Interface).""" diff --git a/src/cocotb_bus/scoreboard.py b/src/cocotb_bus/scoreboard.py index 91702993..7f647eaf 100644 --- a/src/cocotb_bus/scoreboard.py +++ b/src/cocotb_bus/scoreboard.py @@ -1,31 +1,8 @@ -#!/usr/bin/env python - +# Copyright cocotb contributors # Copyright (c) 2013 Potential Ventures Ltd # Copyright (c) 2013 SolarFlare Communications Inc -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Potential Ventures Ltd, -# SolarFlare Communications Inc nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# Licensed under the Revised BSD License, see LICENSE for details. +# SPDX-License-Identifier: BSD-3-Clause """Common scoreboarding capability.""" From a4d0783105e5d539932b30ece52b7897a3e191b3 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sun, 13 Dec 2020 15:40:23 -0600 Subject: [PATCH 2654/2656] Update imports to point internally --- src/cocotb_bus/drivers/__init__.py | 3 ++- src/cocotb_bus/drivers/amba.py | 3 ++- src/cocotb_bus/drivers/avalon.py | 3 ++- src/cocotb_bus/drivers/opb.py | 3 ++- src/cocotb_bus/drivers/xgmii.py | 3 ++- src/cocotb_bus/monitors/__init__.py | 3 ++- src/cocotb_bus/monitors/avalon.py | 3 ++- src/cocotb_bus/monitors/xgmii.py | 3 ++- src/cocotb_bus/scoreboard.py | 3 ++- 9 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/cocotb_bus/drivers/__init__.py b/src/cocotb_bus/drivers/__init__.py index 7a04e502..2e402a9f 100644 --- a/src/cocotb_bus/drivers/__init__.py +++ b/src/cocotb_bus/drivers/__init__.py @@ -13,10 +13,11 @@ from cocotb.decorators import coroutine from cocotb.triggers import (Event, RisingEdge, ReadOnly, NextTimeStep, Edge) -from cocotb.bus import Bus from cocotb.log import SimLog from cocotb.handle import SimHandleBase +from cocotb_bus.bus import Bus + class BitDriver: """Drives a signal onto a single bit. diff --git a/src/cocotb_bus/drivers/amba.py b/src/cocotb_bus/drivers/amba.py index 01c506e1..06b3b49d 100644 --- a/src/cocotb_bus/drivers/amba.py +++ b/src/cocotb_bus/drivers/amba.py @@ -13,10 +13,11 @@ import cocotb from cocotb.binary import BinaryValue -from cocotb.drivers import BusDriver from cocotb.handle import SimHandleBase from cocotb.triggers import ClockCycles, Combine, Lock, ReadOnly, RisingEdge +from cocotb_bus.drivers import BusDriver + class AXIBurst(enum.IntEnum): FIXED = 0b00 diff --git a/src/cocotb_bus/drivers/avalon.py b/src/cocotb_bus/drivers/avalon.py index bf127d62..a8788c33 100644 --- a/src/cocotb_bus/drivers/avalon.py +++ b/src/cocotb_bus/drivers/avalon.py @@ -17,11 +17,12 @@ import cocotb from cocotb.decorators import coroutine from cocotb.triggers import RisingEdge, FallingEdge, ReadOnly, NextTimeStep, Event -from cocotb.drivers import BusDriver, ValidatedBusDriver from cocotb.utils import hexdump from cocotb.binary import BinaryValue from cocotb.result import TestError +from cocotb_bus.drivers import BusDriver, ValidatedBusDriver + class AvalonMM(BusDriver): """Avalon Memory Mapped Interface (Avalon-MM) Driver. diff --git a/src/cocotb_bus/drivers/opb.py b/src/cocotb_bus/drivers/opb.py index 20299ee5..cd76c8c9 100644 --- a/src/cocotb_bus/drivers/opb.py +++ b/src/cocotb_bus/drivers/opb.py @@ -11,9 +11,10 @@ import cocotb from cocotb.triggers import RisingEdge, ReadOnly, Event -from cocotb.drivers import BusDriver from cocotb.binary import BinaryValue +from cocotb_bus.drivers import BusDriver + class OPBException(Exception): pass diff --git a/src/cocotb_bus/drivers/xgmii.py b/src/cocotb_bus/drivers/xgmii.py index 2b073f08..77d4bfb9 100644 --- a/src/cocotb_bus/drivers/xgmii.py +++ b/src/cocotb_bus/drivers/xgmii.py @@ -9,11 +9,12 @@ import zlib from cocotb.triggers import RisingEdge -from cocotb.drivers import Driver from cocotb.utils import hexdump from cocotb.binary import BinaryValue from cocotb.handle import SimHandleBase +from cocotb_bus.drivers import Driver + _XGMII_IDLE = 0x07 # noqa _XGMII_START = 0xFB # noqa _XGMII_TERMINATE = 0xFD # noqa diff --git a/src/cocotb_bus/monitors/__init__.py b/src/cocotb_bus/monitors/__init__.py index 8ed647b6..0f6c6df4 100644 --- a/src/cocotb_bus/monitors/__init__.py +++ b/src/cocotb_bus/monitors/__init__.py @@ -13,11 +13,12 @@ from collections import deque import cocotb -from cocotb.bus import Bus from cocotb.decorators import coroutine from cocotb.log import SimLog from cocotb.triggers import Event, Timer, First +from cocotb_bus.bus import Bus + class MonitorStatistics: """Wrapper class for storing Monitor statistics""" diff --git a/src/cocotb_bus/monitors/avalon.py b/src/cocotb_bus/monitors/avalon.py index 98f94e67..e07c842e 100644 --- a/src/cocotb_bus/monitors/avalon.py +++ b/src/cocotb_bus/monitors/avalon.py @@ -14,10 +14,11 @@ import warnings from cocotb.utils import hexdump -from cocotb.monitors import BusMonitor from cocotb.triggers import RisingEdge, ReadOnly from cocotb.binary import BinaryValue +from cocotb_bus.monitors import BusMonitor + class AvalonProtocolError(Exception): pass diff --git a/src/cocotb_bus/monitors/xgmii.py b/src/cocotb_bus/monitors/xgmii.py index 6f2474c7..8d9bef1a 100644 --- a/src/cocotb_bus/monitors/xgmii.py +++ b/src/cocotb_bus/monitors/xgmii.py @@ -16,9 +16,10 @@ import zlib from cocotb.utils import hexdump -from cocotb.monitors import Monitor from cocotb.triggers import RisingEdge +from cocotb_bus.monitors import Monitor + _XGMII_IDLE = 0x07 # noqa _XGMII_START = 0xFB # noqa _XGMII_TERMINATE = 0xFD # noqa diff --git a/src/cocotb_bus/scoreboard.py b/src/cocotb_bus/scoreboard.py index 7f647eaf..ee854771 100644 --- a/src/cocotb_bus/scoreboard.py +++ b/src/cocotb_bus/scoreboard.py @@ -11,9 +11,10 @@ from cocotb.utils import hexdump, hexdiffs from cocotb.log import SimLog -from cocotb.monitors import Monitor from cocotb.result import TestFailure, TestSuccess +from cocotb_bus.monitors import Monitor + class Scoreboard: """Generic scoreboarding class. From 07dc90b5530c6031b41bde8e98fd39bb76bc010f Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Sat, 9 Jan 2021 10:55:25 -0600 Subject: [PATCH 2655/2656] Fix test run --- examples/Makefile | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 5449aca3..e7debd2e 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -27,14 +27,8 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ############################################################################### -EXAMPLES := adder/tests \ - axi_lite_slave/tests \ - dff/tests \ - simple_dff \ +EXAMPLES := axi_lite_slave/tests \ endian_swapper/tests \ - matrix_multiplier/tests \ - mean/tests \ - mixed_language/tests .PHONY: $(EXAMPLES) From d96eee3880a039fc59f17fe868409a0224d1d9a0 Mon Sep 17 00:00:00 2001 From: Kaleb Barrett Date: Fri, 22 Jan 2021 12:32:01 -0600 Subject: [PATCH 2656/2656] Undeprecate Scoreboard --- src/cocotb_bus/scoreboard.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/cocotb_bus/scoreboard.py b/src/cocotb_bus/scoreboard.py index ee854771..abb0ea7d 100644 --- a/src/cocotb_bus/scoreboard.py +++ b/src/cocotb_bus/scoreboard.py @@ -7,7 +7,6 @@ """Common scoreboarding capability.""" import logging -import warnings from cocotb.utils import hexdump, hexdiffs from cocotb.log import SimLog @@ -36,8 +35,6 @@ class Scoreboard: fail_immediately (bool, optional): Raise :any:`TestFailure` immediately when something is wrong instead of just recording an error. Default is ``True``. - - .. deprecated:: 1.4.1 """ def __init__(self, dut, reorder_depth=0, fail_immediately=True): # FIXME: reorder_depth needed here? @@ -47,12 +44,6 @@ def __init__(self, dut, reorder_depth=0, fail_immediately=True): # FIXME: reord self.expected = {} self._imm = fail_immediately - warnings.warn( - "This Scoreboard implementation has been deprecated and will be removed soon.\n" - "If this implementation works for you, copy the implementation into your project, " - "while following cocotb's license agreement.", - DeprecationWarning) - @property def result(self): """Determine the test result, do we have any pending data remaining?

    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⁣US7`(FflN> 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%0DuExN4N;}0Lkay>i{RYF% z8LtPE0doy8r^*?n4`ba{o#I*YHJ>rMFKRnAU_85YYR~Hj?lpYrZD#@W>|gcqw1era zu6n?@l9(q@e@ZZhm1z~23`rxzhFd_H?SQ(znyc3M6oroi-!KV!TE zV;A#mVlJ}xCj)Q@gj$B0pI$sbkPr zX$)E}w^OzPzUFj__vzf4W0k4iXTWwA=heNle9eSqvk$0e#l`)rM*F)$f0vh@O~kFJC6383zZZ6Kd` z=;PSw^$wIE^228xAQ`}X|=5z2@l7n% zoVBBBocFG=LZN!Lo9+)(kR*I!U&cwi|lS{Q{ovIka_6;k+xV~B{;*`H*8Kd`m zs?T9CiGMA%UDr?nmj##ndZ}1QG4(TB`a3PjR@v>ybEs2W3kkpT?Hm54&t(%g8=U$- za0|d?Ho#HcEeEG$mx@{V?i^&X>HTUIbl1}-+dKUT_6zj!>cBZd2IPSnW_C5LwG0Wvl1(?JSOSLt< zlV*lut_9;>+$o+Yj-?jkN$xbbHos88Io2)Jo)LWL!Gf|)$J7fZ4aRQ$m_abUpO)IL zA2bdo_Om_XxD)lUeyR3c0&N^zY7^boeT-wa9DhOGI=gJ= zYGkVtTo&~^AKwew8k-l^f$=Tr)ZR_I@54syv`ZXD^QF5PrcdfoWAT3u77|&&Tz5XI+8p+P~ zUD`chdp;_)vmA_T$1d&pk_81!{(5y1Od3ot#k@q$jDm^p)TKT98ZTh-*RCUD~nK#g7-nJbj!%Tn#w)vQ9CRj-jt&oYo#Oqte?yINueW;wR!B zVB9Ijb^SQ`+_g*WNNp|47*@9fWM{W7aTzfu4;I8VJ%)~f$^NQKd&loG#<2dnp6vWz zm*xJW{2ugI=(ka>zkNdL#{)*)qf2`x-ua}#urX}`7|)(v+B5OLk~2METoql~v;2q3 znN?sMd+Foe*>WZgCJW}RT>D@Q%ik0+dv|Hi7TsWGXx}=cAMsS8eUP0rWAuG%HFO{J z-2I~^PZ{NAe&&J6{zi{wzMP2>vu~HSKU^(mR)WdQ>=F|c%iD5hnC$#kZ>w7lN#mI$ z=68sh?BJ81FR++h_o7_;b(!DG!I&yAin~i&8^gj*1DFJu`)qu5g7NO(CAOtLXkjM- zMm?ZQyhu!?yj*L*_^#IX8EuSVZE%8^D(nl$&drQr6O8YeE|DQS`>vLj%L~SHY?nx(LY@0PYhtKx zMajs|FmX_BN!}}E-noX zfTQ!O%qzYp@7HhXz}+o10`N3AS>VNixyJU=b_^L9lda5Cns0SEURVX}_UU zO<4rL&olST%yaKOcl=(j%;=!+=c7_-4*Sq>&ux6FL5;9t~C z5=`P)?Efj=s*z%uSS;gUe5>4JnT8&M{wcN5Ma%|^WzGZ0-90U$jIQ~5M$OCz6MCve z{2%4z@#l-NFgqc#^Gu5vrE zUF)^WOGP_!O_Oqypm<>CYRU~hg?+ZnO^R%svCMqO%gS*OqH4` zdkAf3k+c&~GgV-cd1j%S34+nily(}`OfwiG&$OtSI2cbz+A-A38ZhBJvr5fmz-02w zu$u8cjJi5Y`WjO+m0(hNCZlHR!1&LWcBa)#7)&D1l)d7pFY3oJFurq^i2ip7O6Q6F;#;z&Ru3c!>ifC<=PyTp&pFqJn6IB z!dS|ngG=POThtu+90V6QU-}%fa0Q=ZV3J^Rc^kDb7N67Lf{l`U&z+k)gnZ<=X*EZ& z)qpcDkg2_~11MHZ&u zlWch(MLwFOt!4{XuvG~r1tw>!+rkuVQN0Jjg)WxdEozQ@E&=DcBwwEvuHbVem@pW# zJ}peaC)pYS=lh+E|2+$rvqj7#7(LH?p=Qb-Lw)C&4PSNCBiZqT35BJv?bXaeFzGzA zx0+c3CUB{=bC{Z02_~6mPF6E1Fxq7@j~A+$bzlrI7a@M{GBs27IO^l~lDR?6RDlWS znY+|X5KJb|JgH`y!33M7uh-N}984EW&P0hr>r1Q)rYGxQrV2QNTu4X2{B=bz4n(;h= z_VWj6=Uz2a0mjHP�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⁣US7`(FflN> zckk}6*3%6py+?UH{a}KBbkqIZE{|2~NrK7jU0%;9n9xi&?Ztg^T<8(~%>wDqJIyLHo^RNp{q|>cGE7W|k8D~+i%=Lo{{nbq$vELnn z6Xy~dxTWBNe{)k8_k({EoUpkXOyX3x{>(a#DdZY#7CryI5`F4C%#&-O>p`JQ#HtO9 z<6<}cgU8+f=9%LCUS@(xBDXJvDOJ1AS`D)G%ni6{ixRq+(Vj(aUyC^6vsD-OXKZf< znDBLON?Yfph;=TEU~=ULr`=e(|Luo71bM0ccNp^MVmB@0dD-p@%{&tIo(2=V)lHjQ z%LHHK`#2`RMG|g0i*@a1*QK$~&mp&$xT%tv6VyxyOm+}`mi4?`)JGSX^st+bVLi}W zsIy#N1H?}n{`+}`lLz=>Q;g3(sE?e7ivqxmf1C>Y1%Zu-m`1L4yInCug7 z`ilFS3q;v0ZB2U~F<6P*XW#D@oZ(Zh{=vkachmNDU|uR_@CIvBpiGBLL2dOUybpd}n4Um~6&Pz3kKWf)PHgW}iNC>+iOjEf_p=Bga0+ zD#ZLVH{H%YT_-r9s|`$c%uUr?$Bzp}^k3}PEO3r-^t&8Wd{@<96ioO>w|*}8tUt{$|rYGX}z^MsLMtFNR!j;HiV#N7D;`mUx*x|Yvl z77Ja5Jq^cA1rwf9NncpYCiKh#6Pj8{@3lbB^J+a&FxsA#G~L=3gq{H~k-aMIYbAnN z4kr4iO8PT=D=L>*n_2@V1?EO813fYaoG-%O-j(z$&zZNl)E4ht`N0Gas-$z8z|2%L zv%n-ztfXmNwtEF*_|1J_6ijArB^_b)$(Yy0ArGBgzFbSe#KEj(J1c}8)R!#R8gQ{_ zC0%H>h5JwP?`?Lxgz@CGN_yJLpqO#Jjp%eg1^w=m{+ zbJ^Az&|&SD5TCp))-%9*&aAYr72+L-EME$F?5s-rJ0)O;<)dt8er3t$X~!WCL*56r zD0hrH@Qt7ypIxb6TLx2T$0GlxgGqw1#X~TiU?S&K>h}doJ>uLj1}0O6;qnZ!o^vbh z&odeRu7o`McMrV+*7=sm2a$K{z$Bq#V`ii;_^jdI{W8WV$cyI!=DGnh)4(|XURm;- zgPAaxB$!PVb)26s^nl5nS4p*s`GVZpu zcVVUe90IW=?tTS*1-L^>_^sj4X<)RzO67Ah`E$E4>p9P(&xd~&J{xt;_1Oa^ zbiSu#ehxq$DRbaw# zWnWi#mURqF2F!WTS!7SNM{gQ!?dtMOJD3a@MZa3GH3v-eAC=SrL(V^_eO>@2d0izP zWA#V0+XNWxdUf097^fhQL%y?BhtU^iAkUVOJ6?mIiz@BwlSbd7L7sv95^fi>)#aGM zI&Q4g_isL4&2+P#n=0uzj>p-85p#@wFyTZc{f=$jAQ(~4Nieb75PQ~hkD3_;f!EQuBrEm(w%<_e34w_{R7qcQAGP7-W?K_JcY%pMjQPa| zV4BoSADGaiV662h^bCXXK31vkCBC)LBj%22FzLrD^)*ZADzvGY`k1fbr*SZ`Cy;j> z!`+1*Vaxvp^5*GE+KQRO>`WW$L81LJa>UBeL4(u3(O>l!H zbZOAl1}5>YqE8UW_qDEN?n8CFa2Xw>w0JrUOgnD7^slq$h+yjFtoere?4<)wWZ z0~gO$>d#9o6P&Q;eGB#YO(k8#_Ey;$w$~0O`E4cr%FKI$K@9SFuYv0Xm%{j0Y7cqt zzhkWHyGm-cmJj_e+e$#5{=SlqwZ-w0qIc4-0vGxbxxnXe-w2;X|2oFHF7)Vo8&~z4 zeU%*l-i97*3;&`QdL|3T(32}a7;gj76h0rGEf|zv&fhv&SAghhYr7Zmj)BQ+ie2cf zydKQEbLa60x?yjK zb?r^m<^*?rp2NA7==(>&B=;fulyyBX7*TgwFo`)t)!b)PUTONBvp2I~&;Jf|9YJ&~ z+nXvlqkQap2N*3v|1Twa*P6!`yR*P~ zuOiynx)x~RdccLRBl_OzpSUK>b{8{u15q8f>8FK%dZ)b?R~Bme&N|5PC#=908Nu)I%-24*Q8|$y9(^C}m|%px5SS>|Ue1BeqFfPu2#@pK;4+tZO1^8g4{~pxM}Oxi z_(IMZ=V3#Try>8)YDf4o0w#5-N59uo>Oi-SNgk$-dftwM(=PWY=b`yE8vlP_56pcE zdvXpM0vGG|(8IPikh|xw&_{KH3tj2a-xDK!!(5ep%jaesT7 z2N#Wd$ZfTYdd|ET0Zv2s7v-c1unbDLoZu7 z5eN2Z9k|qu>io-<{{!fOd^4*aOCAToX^TDj{s+)2^OW_3S$?yJKE~&w_=tMeAe#@) zdyB`ue!7SLt}ga@F_;XPq*a%(wy^^8 z>hv}bonosGGdDGG)4-)}_s}oa7>RldGjoTB;x=8y_h-&$n?oMOx}-jKORc*5$fU|4|#1q=N>RC%nPQ0)9x*Q zJ`)BLyw6i|fAc8h5y(~NeED|9b+QOt=6;W2e_^vdGM56EOnT^X>)Z!*oFl<7w+>u% zsfYe$4q{n_eM4Xqe0uxGkC;^>L{1_Sm+yyTFoQHn2+Qju7_NkZU&wFSep4;Gi z!FS7ic$n?I>d~KllN`#Je-?%Hje<+Q=25=ain;taxE@^Ubr0=p^P+p`TwSmoobK^hjiG$02;-M?8ei-whB;>JAJ#>dPHlhz6 z1rz+tqu;*^J%xPI%M4PF-+FMNFFgADW5-2(810Jv(bl1Tj(O~RKpSIF2=XxGTUq_( zWAPt>JO=pEFtY|s8ceRd9csq$Ir8#5V?HxWFkmlYGx*wRGyiu zX6CZ3?+sfE)J&XhfyvqGS2Ii5R-Rd`W>$j$`#VW5-mhFb$Ju2s?5fQmQ_0%!<0 z;Ql^5hPA<73Y@le6@7*8MZN=)YdY(|dAF^imr9gT!|zpJqK^UdJ8Kz@cWW{e1moSl zs^qyNGqb^@z>IP(tdV(Tu8YhElW4D^FL_PCJz(}lqR(3lCcb+Woy^QWf)VHFDKHu2 zd8svaj?r2$j+s^T9X=Pw=%Pva`_q`K`Y*UYS5b)lK3Uj9EDtr_ouPqi2N(KFmHj>! z;rkpgDKHmu+qyvLGJI!y3&4aAsIt#LkPGrTlRZ@Ex(s`)YZaL2Ayw4I zYmZ66piRg;$g-{@t7vClA9-DHLYMC=#I(DL=2`Oq4AgPzIW+utfQy_|Mb}!}yof^t zjN{}gx`X3TeXXsY+4o*B$uo?++eR>=O|ZRT_G@01zW?tm!3p2fV6x{_(MMdqa|L6G z=>+RqSVc29Ue5|n=n7=fA6*HCMFXE=fS_b&Wx4FcZ|D^*o{?m0r$mWJHzoD1>;Rr(W6{17YGLZdj4F1 z`>OS9@3t!CJs_+n@HOggNfkAqP|jq*^U(sxzSaUAa_ z1!Jk>IJnULRdg@s%bS7|^|BO9c3Bne%J&(5E*KOp-xq7}e+{@`x{6NXc5~+SW;}&1 z$2VvXZ&cCST*k`&*7ffydX06x zC^$nGGmF4PKCGe|j>CThBXVH{nCQAH+MaWvW|3Jw!Juz+B)_bpzp+nS3r6_u{1)|> zMZRzhV7rjNhym7(LF(;&8o11OmHxic-Gwe;FAOH=sHPD`-;vMr9x#p#tLZoFcXwWo zxmKSmKNzpOns&(LUY;@M#BFeM1(O-&c_8~@p~<}EI8ZvF6JKYap>=%3&b-WIYW1DK)k8|!Ysb`^cKk*(o?{|npu$tyU zO_45R9v6f>4f$0Zr&EL;S$;5)ox!lJYbC>jhk1>00l4h0)$|{$Eivy(u)W>jH^=C1 zp)1#>U=+t{_+JH1+r663<38^-!HGH^1C#hewSHc>PB5sKd>wQB`MyWY_o%kN8vyZ? z^Mwv@+Kg(t8G&-rjb=St#)mF&shQRKbuj3+){lmt`oIMDsivRsxhU?Uo`;#4RZZ`3 z88u-~s|zjT3f98paxIZmU36ZzQ(CjD3B0+-EoQ^{B+!6lEbri=MH@y3D^F&G6CI;EPt zoPVdPnF%oA)2eA#wuiE)>Lf6M@||8yTl1K8sa+4pAOt3Lb~PQ5dw!^xk?;2C2B%$G zO&4;kUKX5@pFAG-gGpXqO@4efecC&>o~_}xVaU^vKge0$Z#C=A67#m7QT|_{s~+5mf;0SXk*0oG9 z$SFKZk~?Q)E($KuSVLJ`pMo`>+@(#uT`U3@Z?2);ZJg!$54dz|4UH-4Fl@X(QiI)f z;8K%o2&V*?PYeG9eIjS_dw3Aj)rz zQ_d$e3`3vyS8%gyXqlDM`vzo>Zg)Dk#342G9FN0i7TeADN9RpAd|wZ_QEb2=$g;U((>y3^3t~YfA2ez)UxoB$y1_dR1+!A58qx8akin z*1xEkBp7cT>)w3+(Z0m2Z{hPOn8Zyr%4>Z2^YIBV(Zx0TK4tK)P%k1M*l+DOwT{fq zHMBk7Cv$|_-V88W5_PgQn7`W@uD5P5j`wQlUT!y!3r6%c%*DYa$53w^r<&W%@>#g$ z;BYQR7vq@Xe95hkB5U;Z*bKNVxIQa~^BK11*Z{Hmu7(y{<;WAc)-@Ge=zHUO10R>a zH(Jc!W`V){yX2l>b0N=`k@rKcg=>|^v#-ODdw;0W->=XiVuQAsUpv;oje?7V%e}X0 zrr^XioC#(oYN($3*N9+5`v`1^y7-@==TyN6Jt1a(swunQMHiUJ`WpRSmP>>lVXF^J z?B^P);C6kRV1%t1S9mM!9;(pq1jxn*VW88nDlRI=^n(&`HPzIJK@g; zwYn|upqYolRtK2KhPAZ0bZyZdxu9k*u_Dse<3GKC^RzIKKszmw0#2RbqT@=hLo4H2A z{zSk;T5G96(O>8L;a=7gsHH*hrSuGe2~Mu1mNI%qz=Suhr5H22t9{ObNpDh1dzC4> z&xJT|R!ch~?#_MHdZvSkOs%Ewxa`NMnNDW5LD^Xk%2wzzjQnWBPcbmD?Q7{4)*~5x zye-uI5c{-qE$z>KpQW}p0w%g^Emc{^T9J=gwzp?3{hi0{g+h-pe(;>bSAjU9A5z_S z#Xh$~9z39y)?4esGIn$_cTg?yJC2<<30uOS7?{){wX`X>p>G5u{22t3?W|Qk|H}TZ zggkU)t^Gaq#D41kdEuB%q4%guX z+c~b5ez4Y|7)zMbD#0CJON*?WsB7kCf=hR!j;tK&QJzah!D%Pf(ic`voJ$RW@y@MP zyyH~wpV;1V)^kd&{w@c6FT`8;ui@tmbEnppe2*(LPQn}@T1#j1XZ#m*&04r2wvKXW z_-QH_?KJe!*0wD2bQT!LS?WAw9djWMLH3T)r0RE{yQM|F07>! zSUz9q5My=(Of1jzs+nFeSum?uPh2pmTO?#Rfq@V5AwX>Q6V?t&dfA0nagVF=c04c;_DV+X0E8EH5}I$)Ovcr zq^?FCm&t=gtmhxKbWIs%1(?_X@{Mi5zd}rmxV7P@4BNWCR^R7RGM0AYu0|WUq1OJ6 zPEnWBzyxosExD$kL4TO_fH}E@-|SNl81Ld*I@iird|C`Hese9oVja&!out4-?y99r zpjQ3B6NG4|82yDD()QXMu5iR7)@Nx#^vGrnr4X!KD9Nt3OvPeHMBK zSkJe$)W&`7s=OYv|Kc244kkEZ)R#pM?sv#NRIK6m47k(=Ub>Wh`da8R%Fnu-UX;Ja zORw-}{1@VAStFhXPOJ42-XA#}RBI&~`A3TBbcNV&)z% zT>-mhF6C?$_U2vzCJE+5&dJKV&72f{TZZ-g%WHq`DwiWjUg+mox*ITVKa75o*Gu;i zy7X8=qdw;gf(t(Dr8D{Y@W%uv$~PNK@=Y&2UZULn$i4VTI#5? zOfJs=qXp_{3ERTIDwn&#q`^$Eo+pJZQTBdjHZHFx2`08lo$~$g`MJd?`=iy-YPR); z+SUXZ$CUEr3N&M$x*6hJqE0z4LST|x*Ae$2MP9?1#4ndbX{jZLN9?KZ1}) zA@3@ZV~jWCVaPLOwEKgNdvDp#<=}!lDB7&CPPCf+ zfc$SxOTCCTy`FuUUZ+260G)-rHFN}8F(yEs+xxR1FCG&^U_!gq(WTs%6&Q2w#AWIN zlMdCOh5CXXj${7bo>BJam^#{}%z5erm_&CST~y>#aoq+cBSt6G+1LJr-ytyR z6YFSknf|d0OmuFY@>yo?v$$-1VB#mE?rnN5*)Vqt4j7Jg5?tz(((Nw|dGJ(q`{LuX zkY`V;r(N;6C^roK-i>km>GiaG33(9mDCDJ%$MpwCvdr;O$g^dRkF)(VO50DeeaK7M zPqY0J$7?847V-q*vyZiW#ye@bE_|E7M=(cN8IkwQb$|;;>*#?0hwB38J)_RPm%T+_ zKe!mU+>$$Ryt|(D{W=6rJ|6$Cv@V$^nB1RL;4AjwA z5EsWWcT)|&XJLHZ;oxTU- zgF=Tm2b=>YK3GQsKG<2UW)^@6K7#o!*QNVjvkeM82{4(*>*$Xhx9!!;O4gIAqeh;C z9IR&6feAm0`4H#dg?6S5<*sT&Y@Vy5ci10%FXRnQxbTcJvPaK}c5sQ8>gYVScY|G* zhC}Co(O#?5*At{JF?KEh6M4N(f97D7&?E9R!9K07qwlQkM)ZkXXRDY)on4C0&R>Ks zV_v!r^6**pdLK;pnXxwH?#*$0UfyVzPlY@<-_SqBE}zNALmp-Ozq8A``S`Pq<9D^o z`}lasbI1S5E+69K&oPcauvo5f`T2Orx8XP)CFCOBW6ZpZdgEM*+8NFP?-uA^-mkOo zsUhOc_NIeNgWHkY{KaZronX8l;Cz?!>BeG4-dofQE_-f$$$jG3-Vm6`$C&rBuKSC1 z<d|RUJZoZLX&A@zqOas>eF1p1= zv<{z3*^7XQY`KxXPyGh>n|UYNcrTdL4jbv~b5jH({2l_6+-W2IzTfTC%m_2nH==DD z;ZI1-WWi+i+lb!B#YX4;f)W1srox}5dV2CuFh>hU_%j_$?9_S+bJ@?3jJbbJCz#NA z^>n*6KG^p{!*2^9&q6+6ZQH_50*vDTpMC!UW4^|1ez+@KI(+4)2=A0@6CVfdgU0x=}&aIK7m)6_w!!>e(%QqEF zw!fZUu+AJ%(5po3 z^>x|Y0xr~zeW4Mxn92)58n%Q zY2aSdY*nAENH^jj|9O5u<0H)>HIOJm@&gY|S1pR0Z%IODns$6%2C zxu>2kMP+>`b6hAjup z>n6<20GEEcp1$OGgp2j%@5^LvF1XanditSEoonC*n0v9F&T~Uwm(VBrsO4ZnZ`AAO zCJO~)v=6qo22AALdU}=hqy=Lc%beRFcRs487nxfxI8g^v!8pe1=@?#j+3JC!n8`AR z!9~BTr)s`NdX(U>V}aaLVm`RohCW)(&lL0sPLy#mm{60Ce#_S+Hhs{vDf~*Yy*3|B zx2|=FGICC@1D8S{wmm*O@n6VkW39{mJG70heUxTB-NKgeXBwE;c0M|vueV$y7||z% z!8kg6G>_}J{UOt5p{IxS?BSygIsYC|GmF5)XZh$vIO|+57*W3~*w%qQ_5Mq_aO?g1 zT5zd@e9G~G^TfF=VmRBU-!m&?gVR^6y%hGloCYp^h>s5B@*la>ESsp0Fqq)cKKu1O z{bv8dI4~bv>=>W@y?`iRel3fAOMpv(+k^cq^3A+Io4HlsyvO?N_k$ts^8T|dxMY{l zzP5pP-tv8lx&AK*UB~%oiz1s?8^d>&w?m$QeE%Xj);x_|nhkmOcpu$wl^f?_tYbdw z==M>wmBDu|&mzcEkiTS=BR=`^u$~mS_z6DR+R7R8`V8wSA!j>|?NDEHead||^LCh< z3NCcAPqFVuak~zK%YwVrIyM+NqCr;=nE0t^BlzsZf1xdk_$*?3XQ<=D%lmBeDtW5F?0mB$NU2F;`;)zRxkVvfr-rX(VZo9L?HK` z>9gNwDdN$~%zU5ann3Y&01YfLwY zHf+R&8Q%`*5B~0>Tku&`Kb#NK!FbQ}k;`fejW^%VbBrV4wDWx>_YI3d9)eso_vGUf zkVneMQ;^5X$TN^9%E%q_HI5yGIHFhkD28L4_&0T#*Kb+hOmJCnHP-$~uM1SKKHrama|~eIx5Y|7XJcIhU_#gW z?E6{Zf%@F(78s7#3UIL-efInQA=B``2J#f-xiuwxm%gK$%IR=SN6c^X>F)%3Nc4vo z1K1Z0@+9O-tp1Aqh}oB!;37Bs=vsV6QB7OMIa4>}j$3@H^}s^k+z%#ntIz&?fY{G9 z3C5c!&y0e}fceSlvlzD~m|5bp?_Camxjg<|&~FU-?0aSz<(ba%JAC$cn;ALAIy%8b z?($i#mFx2)mM?%jT|%xwz8Lb*Q0Z}qnG~1|nA|?e&xran{9tA+7{@;i=6yTE_3Yji zeZW0F`LvaPeV7*AI4G%*+Opyw^u_6m22DRyQAvH|e7bO7xc;pT%Gj|MDr{ zZN_$1K%Rm8Pi&`dnK>U9WzT>KKH}5QxEf<~yu+mS7Bftfz;WCk&=b%`9T(NrPD+81bId70jf3`hD~Rf)V~? zz=WPcTVQ|g$}{G5?p*mhz&!1vgE)>)=XVA{gTn=3_zZSTy?*$^k)UQ>`Rz?u;1&{{$#*-U-8kKR$F4; z==?qOfO*E+Ux+wP1(Ql+USFigjH8C%XMu^l=F|6p{8{)TY(>F@-|*>sO;&WmV4mNOXR?OFxmv>99G82fklvqf5f599{XP~$PX_YPFGife7jg~0$Xs&&0rmTzRUsu6*4x zH=Ffo4f^|c;)1iZ%?03+n>En?*a!T}+{~X3X!v=MxwZ!Sp4ZC;g+5V+Rm=n%?DH?Q zm;8DX>&k))Z`VL?m8nDDAJN9!8>k*mIiI)NV?EQsgr_&q{4#ae2`0T;1ASb^Rt!vP z_lC0f%?^Ueh8k#N*7Kh5N96A+Fy1{I=oR+)TfrE8Ci^@FCbKteF@x`gc;luJp7k^1 z-4l9dHPDf~e%bV}**^t@G|;KMUOe5-v_a1tFqy*|Xc4!klLTW~W5=_|j##9D zzTh}sEx26n!6~S^pf%K`@C68_3DA z{6OdtvE&$yfJ^qFop9Z)7o4z{W#-BTdcOtCj*lo~0rVS zHBbfTz+%A&Tb*Dck2O#q$LLML2t6?{*(VyP1Dl>X*9qo8GbabZ1mA9;JJ_GYAGP_+ zFKECQ2hZ=_ePIz;SNfJyGysNe4dt`IvhAIX4; z?xbL#CqGAM!=dh(@M-5p#eQujr-2JHH@(rmzAeiSCcR6eea!^NvyL9f!@D-prN6Pn zN0e<5n9OdC`kcIKg)(a|R{*08PF zjYMU9cKii>>%ol_=NRsM_d2c|H?N@(AsI8%I2i8{EtDNy0B5d2lXpqL&U~A3w6+owooRP_G{SCqu(y9_c;-8T1ON8o#XhR&?S88WnF)4qG_Dp z&|TDQmhS-c#YZ&J zxmH_(nGPm?WD`Blda9qY)d$C@6HMgjCi<1jHAOIJ-}!rn*r#6Rj&GtZx$oa!aE3kB zHN^H#YtrADc#>c&y3*j%XEf0mHi~if3Qoj(98BucCc2pKJ^I?tus#2QXh%0U(Z`(I zmppBjPuS!BG6XI;+@!p>MuR*8dF0+E`+iPF*;q#}8125MlGm6AArC>mG1#K~z;_M* zE7=C*xi$Au;j8Eu)-iK`lYZTQOfaHOs}4f@Nj6c0>%sMmS!PiW*w4$6ezeIxS43R! z%sqM#-KRO=5|200VVobk+I6wt3&1#@YSQ;`-rvq}Y!a+zWt08AV#al<6y&jIOUFM0 zdGNU=+J(#1Eo_KPhsgQMLtU zvbAl^)b()g4S-3#)kHU1^`IW|>tf8U02g^1?bJHvA>cbjOT)uxESG%()x&?buPndhNgUtutg_nT-(s~%$>9A){xn`mm04)Z)!!*K&( zg8ylv+pIc_IC9ySgUNuIX=M;2toP;SmF!OjTqvWs56JMxaR}reG|^2~J>r^c8<-53 z2TL&Q&rC3h51SOvWt;Of=Avxxqb9|EMaA4AaA|P4=X7wKIVPB#hM%!_o+G}ti9WK% zQ{>!QFtJaX=os)${F}PWXOG;6VqOR4U@K$9X)5I5Ptj*u<)Z(X1tztw$?{C8S+`st zQP%TWlk&bdmd7EFLVlRljxoniLY{`)c0OVFnP&OtP4q4IJJ4Y*tA?M(!DJGXOWqsC zjDHSt@T(^My?wWdenXVE15D($$@b?J|GW?{G;}&xPXvr3+oWHge$1|i%iIe_`?`q^ z!RI2MQ6|orLC9l}-)xnO{($w2u%2%edW^nujP*d?W7T8WsX7e4e%nOdaH7Z$G$^c< zj8f~20p4JIHzclI3)!}==7MT1sFAYD>1n2#=iMFb< zlu78C3nt|C)9-Bd%=X1L2f#(^{PwjraZQBlX$81Qz2AP{o)O12kY^x29BfgX@tu!f z&;I!QCG#L~B=keRx#D>Cvjg%tR#?p3rAx}a6SF0VP9D|T+P3q${ z9J3Pg5af^8j$ep}HuXKV)`4;O{mMP}^6S8?%X<`J-|VN)Y-KXnQQE;oTK%+>)n4v8 znlb+clK}IERnMX3HG~CVVw3&!U#lLGGYR%*V?S+Fq(`4~NxxTti*MpDxz@?`FvdP< ze#`r1Fg`=Z^;z{-)C1)66#mFuZwD9Jw6rY^$Ik&1p5nLfJ1NSx08FsWPq!D9O`k7F zdxPLIoBI{_uFxbANfCoQGYH1p?x!Q6 zv&bjo89=tR3QQEt(N@OT!!}p{U@~A-{XXlcIvW1&=(pdmC(Z$aU_v|j?eB>;>Xr4( z1{2%aZ@;hJVAz-WU=q{SzOZ}|r*(+kG)&Gs?b%3_Dxeb+Eq^?8}~pojuh|;8^r2GYsZP zJHzn{fl2P=x4gezZ^QZiiMeiY-kE;;c|78iKc{8C;@~pi4k*zca^(jT`itNGtWUoD zEZ@gZTc9C2&$9c?b|%0?!+ts+pNsNGKtG*j zwPpC*3wh)q#L%ik*)oPKE`j~?^2c@jJHb>L&1!IiG#`2 zMZ>dZ+rjx5rU4GNl?9VH&S17tGrqqer~hU!d#Rb}U{c2$%#muQ6HL0>VCJcr7?{in z26KgA#I?9VFxe9g<_c{?R7G9s^9)hxV~11n$zv|f^$R-U4FYRZbL)NoaVPb z=LVUE|5cC&Ay7l1bzb8U_F470s+{q#%`gTB_d zCz^f29{djP0`%F|^+l91Kc`}D0$lJsKiy_6Be;COt%1`{Kz^R@r(Kx)gF3D=z-Slv zm3w~6zI`sZ_=SGuIz~PYaWKJ){PueYjCy3BmVycQ`t8p*7z~$hH8TtS)M8x+66^QO zIZi}hcd4J2;G>hj7p^N6pOZ}mr(Nc!j5S`CoCt$+^jqr@b%cBA5esC7o@4XD#lhWg zvwP8`{W%!;UBiC@T<8kF_?p`XmdmgbObX27wmQ#kgq@p@GB*Y;7WXUeeOu_n zqcw$jtM4S(yxLDrYwScFOa~JlD9?0)iCpj3uYn(=&Yc+By20opju(vHN90}>%W)V2 z7rfC=+uF)#o|lcVy~Td}`j*%$BMT-2<~-;us&96L^kCE$0{pA*bMLM^V?TEp0T&Y7L1s$_)Z3MC)zyg!GFPbBS!6zhajJ(kZbsDHsn#r zbK~&G!iE@!=YxseWiUUhnZ;n@LyGfyk%uWT;k*5`fwf%gCi#F7-|f^)7>xFypI+qaO^8Jyc0x}NnCuF_e((G5?Rq#57l8>q;ipaT zxk!&u2g@Okl#pvUW;Nt7$hYUX9BQ}0%z7}XC;j?+H%?PCcz22;p7PVj;GLJMnHgYW zPxVjaU?R`@DZ)O#t!9#7QqPr-?nTFJwGi&vN(5D+ZqGo%_znuk(=Jr(AR+3ihbT+t*0GK`a?e*%J;<_B^YD< zwGHK(116Y7*{x%UnEP>zW8l2EPo{nG*_!7XeoH_e|JJYHXZwV(C4629Ci5NYn|*#+ z&8!0xoxnK;`}4kyz0F!A3s+u#3*9Mm9BL7ss;SFWGcoDVV=1gF(D(=!~$6ND}?4`RROfXi;wO#j5^qOyrtE&!9LZ&uvf zW{&9zFph?1+M4g584&i2`e&b3f(f=Zlg91iVZjKWm>UBZ4m9iYpBDv(oRs&G`Fc=) zlbiMNYfUjH?_22rr)}I!%Q&uI2~Ol@1WabjW;%qKU(`%5n9z33^enGeRKIAe9}T__ zu}?cU({;Qq8BjALV3NBx(@t#bw`wK}CcAety@^V3?kX73c75~EzGpQn*3->6u}>Y~ z;`=s}lkYb=Lg*5869JPrteKwUe0fALmU&Pgxag71^cUu!Th$hZ!K5P1_Wfp!wlD&@ zc62ixgu{z;h@8uUiGj)8m-(LXN7R|`OpHUvHq+PKEAt)!;)Xm}qx1 zea8O$lGkJIAHqzG^_Z}8tc2ToFR!Ds!mJu-Fi&6iO7uAW# zkzUrbu$gY+_WPu;mutWJdOG_x3@&v^vwrW$>sAg5!D#p&1()q>rpI7VWs{i+Fv%+r zceeSl&?S5i%tyPts+m6K`$WG}Ga)d+Md*J^r?@)-nlZI+*BE)N2{06HMsgW_rJ<+{LzHV6;b?X$I%oxptr1;Nu|M zN}(Nb-|&oJ^t^>vdcBW;i>z!W57(D0o5-~+>l$gMCm}ClEV=DJ2X*ptGZB~XePL7N zM+cbDtIhhmsJ|DC5hwON0!DkSS>MOd|FYS>Exz}$t~Z+X{h@aioT%SnF!9l5x`pHU zXTb=2X)wXJn)P*xh+tqZKgMbBYXY2iH8_sxe8CA_fpgK0-fPz1V|8<$$)9)T!k)Ws zI0Ibp-_80tEOg6$C2y14!CY|K2hEoE79t;vcF+%b9P%qTH<#t@K`vsRWF4P2(<|1z z6U-=>>}So?z-9ZNTF(R+@0S<{SWm+%X1xeKfd$a>l`$r@shJR%(6`O>GuvucGhNL5 z&|LBvKMk?z0~7tRnLg$7#yy1|Q6Ix#BF+~2hTHdXf)Vo^)-}qUr-k-If;$%rPUxBd zldNr_yTBK<1B)*0@6hFKp^dmso)Wr5oMwQDHMG!+eDA|of)Vz*!GyPLp{IFna`~%f zymR*STw>qj;I!>pD9&+sS#X9fW|o3U?$$z4Zs-2AsY@`cS*;6a=ob2(^?WH9 z;ZKs8t`<6(^Ud*^S$2crK4%n6>i8D@d0~7nv`6@C+@j#n@bh|bnG;&{_Z&-3*wfBO z98Xm+qFVBF?U>mKJM`rdWfITaJ_EklVIAwBwuNvA--S!7&S8!Oyt8B{XFd%!3dw{f{Cv~ zy|6#;dO||jn64uZuz z_KbSrv3n|*P*baYuWw`Qo(Xx--%6)i+le^$nhPe;+)69i4#(aSBMm>r!9=xI`*nGv zKG>I~V6tHTgwI8K;0wz~ArEbe`m)OLUBmx4M4IcP6O8mF5TWrVSe6Z+fg&`g}q@gTBwyC;}{x2ya<8G zcDK@@kQXsVUPK@dpU_Hoa-E-P_nGbVf^kG!=|5bq+w4pmm?1Ek(^~cSx#4@kU-*)5 zJItk-JH6HZtga5Bbb1a*1^izrcLqpkynYp&rzAvY!vox6Sb*0_HCC9jMfzdKI=hz{H1J^)-+ygdV{}z$ET#rPFyn_q3Yn1*1LEO4B($ zU)h;l{ey`-)=H0B{So6eb7|Il%o_+BVG>LS<7@Dp>r;4*Kv(iv86-{O12hQUSNZq?s~vPjqzbH6k*@3hhz+-C0* zjPQLNOz5Ll%X{6-bvgFQe;H!AwpHKLbFI*089PJZA|JOB-#g>1c-xjE8gzAm$$o}- za@*co&GdmueA!BMtY<$pGYls6b*p`SQ``rd2BUq0^207vnK2i6Jr2h4E#?j!?~8;j z5hMTQ@Mj!jq_s{2(*Z{N-q5p1ttSE|^+PKy=f37%HPZ_w_H!%k&SjTA3tL01=Qja; zuk(~z&j^_4h5`HYI6_YrOa{zv%9P93kNR>3XuUOtLeF$&CIx7i^Xxxre>%Y=Y6CQn z$C%)1GdF~u7#MBifWBvuqU>$>Z4gXw(}3O=-zxN2`j8R!Y4ZSGz&gFyM+Imjo~yXtRhDZEOnQERdigx&O*P}a679J^K>y>i zYwy|gaDB8hb3;Ji-!mzgTz#N30;A!-6I|-106oWJ->ZT%V#K;)VB&WMXiF~Jp=(Tg z7F|QE>+S#@h6aK^vt5gLkATtc3(yn1E;3)}5^>6c$^0uo1ALA>VrSSV-&M$wkpTUR z^Jwe$O`j}%UkA9*TLHSU%zQoqCbK4>xW9cNo&ndNk?RGMeLp}CVnB5sFYF1whrlHN z7qIV_D9g{behScD9HWd~50@NO!Jm)#jd7k^+H_q>8 zt(g@2(>ax%;CAhi)b)rs=3j??J$EYEc~9VA!3bMTU;>M$sORP+7t zf(e6(gGoRCEGF~_TPwkY9-c~9agE&ZQ_FS@g0VfOF&;DHLVHKSM4p&Ri+S!pP%x-# znLQ!q@_XU;Q&TP9ADlVXRKPWZOMy$@hqyrK6JsCRp=4x@cFFgi>Y)gw`tqYbbo}cMPR&RQ`Kkgt`m%Ai`yLq zWBX|;Dfpb_55Dui#UT$PE~jw2N6k8zSr0~mj#IgI?((^%FL^h?-z|#hW}Z8*6O3rL z2aI=05e@Tv_mr7oe*<6~I~SRs0|gjk+mMEPip{R^`W_P3Yq6co{PZqqhj=>7^{ z)_@6Nte*BXX2!+#Z$*6kMKsmuk74~?2`;#A5$(tI>;R!lwjWIF&?5ES zS5FE?_#Facn_D#LJ(j&-LSPoMPcNJGaDJ^}J%{HWlk3gd{^+-4!Ps}BCB_Qr0XLy-CzP26j2A;!S@MYMDDBx z6aPyQIk{aqm;{*QMMZQy-5%<%=NqFDWv-cL(RNAamhw1>1o9T%!%dM4ne202jEth(0uO zhU3*yaNfTa(Rn$}d9Y88+hMc7nRAWrg*+BPy#SjPUwr4^2l)4s$U~5)AkVdik3jCY z4*6!(ujK*T=x647 zf2Fma)vPDqN&kQn`?jN&?Mi_0?%^~)$4t9NlJjs3OdQMs_?+dBwl>SZ3prNkv|R7< z?|qQlraBGphg9p90{J}1y^!B;)GuoALNG}%`x_atm%oDb6gk!F+QCi4O^m5Hn1Iu1 z-mBK?bdu$Y)8gG^`Zf0UyD=_{ofhx^)R{^!4%g&uY6X)5bFHxkh}snb6E1gBmvL+& z_MGDp$Zb_l^;%fjW^s(J;NRDP^Vc|Otx=B{QzNX)>!gdpXZ5pqMucr8`~ma6k(sOC zGwuQtoMyqGuW(CSdajxS#y{PuUM~t=6McmZ16*@8u_qh?7w|dhd!yfCO!YF;=%hA| zT8YnDvC^J{$GSXV zBKtVa&u}<2`##fo;DWO(awUE7S^Abv8{1q2&f#~`zqp;32;W33qF@sHI%#i?SFbfQ zzQ z`TU!7`@uNcoRpWtCuX8xlm$+D9Gzm{@*BP8iFsiFO!8zWCBbL+UHX}vXra0e9R(LZ z#Ytlvrz&e*j(fqJiQ2|Kg#;tUAIGW*T&mNlu8Z+~BHs>I%hfr*^&q*M-u{Z4@i|HVmjjf`QmmIqtU$zlFVK zW-f8k>&E$5FhMZU%bZl0W6i0+r=?)x-A+1=eR^Kl62~EZV3aGJbkHPgqaiS%>z!0# zY@6l}&s{0@=LRQv@i{A3@Lj>5;~!`f(8uH+sPV;+FAy@F5A;`UVVNT$D z+#vEutUFR*0;`;~6EjWU>h(nAZ2tXdcb}6Y#&&6AmVIpkW4qsJe*UWF%Y4WKkblW- zX|=Yq2#kNVlTI?mMfeg06MF!8&%P`$>*4qgurD#E`935urkNWB=Y1Gsntj6GL{6f= zC z&Rwcch=j`ZflEB&q&VPn8EjnJT&CS3ij(0C+)z;ag%TZ;KHL$`knh|^Z)8&+Ti~vbKg2?fbH%s zIN`tJpBT4ePI`&wtNqOk`|kszY;w|d#yJi4PeVUFwsWEJhp_-j)xGRnF{U9Id?!x%cbmV^nG9)Eeid_F+3;3;P_Xp>E<(YkV2m@ zvqCVUU5mij4pYoEQuq@E6PT;emz=YMW<8vb17Q3|DduOoKu_ixYvxA5#le~G(-FDG zT>it*H(#N-#{M_tcQd$1P@!{j#GG{n!8lG<)O$aF5dIr%c7sctqR{q6T?U&maLIOs zPD`Ix{Z1e2BKE^z0_P}n64$8~Yi1lwyi1|8?O@KfW(prc?Y~f=1$^H07Hh^2CUUt# zR~r2m{oD>l=~mQxPUAw4V7kFXuU6B!D|i%f{6e7wKHHaMbF#+!!G$-Ve&wjktg9VN@M}eVZ}j!K zb@hO=eWOsA?;z0jLsO84j<>L-^*8ItUxS+6R!ocW zIm;IOWnX-dMRhIWC&PD4}BY zddmtk!}fw;LS4nQkz=r@V8pm!uA8|Fiq-YqVS+;(GIKcl6$2MuSWNA#?-ap_W87gd zsSAtM{ZssABHu(E83z-%xR|04+a-hJDLnrWR<-u07LIn4e1pQkK6?+C&|9#@ZO7k4ziEBTV>1TEF|ukA6{N zxu?YaI!4UGSTV%p8bVBZI6oq68sQgFeM zVtOsd9Lo0kztOf@E5y1gORlPor$`!|fCF>tnzi|KCY&5E;lj%dNt$Z0U285!u3 z^Q<3S^b@qv=({0a9pK{Yt!k$3e-9Yt^I{q^#$JpO?xTL@zAC2Ijj<3M_w@+4)YrvS zVQd4wvz|?m`$vn-_t1*x95d(o7v_|2F?KEN$^C&=aJb-(UNUlG?1sQ3zAILrEftpI zNgW4lub1t8U##Arv|KP~pWLTf3oedaPUpmG!HF@I1QY!Md!%;sbzCsQC;Kzdv$2@` zS#igDLok(KY`42N&C;gj%_-RtZkjr}bcLB_&kCYr(nJOkN!0tGa}qXTQ#{W;|eQ z-V*wm*NWGh8TLEC%&ZdiIm8LUh`wWPA-JHwM7`(rQxk{Ct2r74=bv3dxA3v(Pl7Xy zuR(ChgG=aUZez(Wdc4G(Hwq>^uSC6{_0NJ4u~*<*{<9cg3rgq<)`jmAeWc|B``-k{ z(NRLX^ZGNE(S@9WuFSR63b-IR@Bfw1GM#$ zt}db3?AJ2EiM~!Sv!R4e=RAE%Fd~=7!1!-2p}jcY-?wH8ow1Jw?7Q+j`tR)$^ZnF@{F?(V_D+fV z-rR*kpYW>_O!z~seQ|3h0w$CyF<-x-`ObB{AB^L>lAOmA!;r@zpX?g;aWH|g67%^B zv5qVpK$0X$KPqbET0H;1L`{jB8ZM2ut88BwA z`CnG9WX*rzB6+2B2y-W7xa{NB5SZkYQo4-m^)=Q^FPPBIrSurjf9suT*`O%ITYFeyg0^dsz!Ed{8Nsu+3h)qU8uvv-P6fbtJ4$IQuC1GdJwv@% z2+sfKQhJu>_sY%nHi~h+0*vF*QuBMEw6*+N_6PD@`wAnFN5ZA_2)7Y`6JztxCFy%4 zxR%%k5$nrJ=>~kvVl*2HnFu^NI>CGGr$0Gp7-~Gpa zEd*!#+vFVgPZV4n+)+lG_^#kD4!Lq=spT_uGUrCtv#qO2&DSl7yv=(B>vAxY^$+{w z1sCbb?N0#m6y)!L&B`Z{*IjJu>QeK&nzWqjh1`FQwVY$z4|xpoKk{`1T=+*c#ul-}Ha zZ-P7w`R=B%tDg&0hTzkUx&7w0wSWoTR7zLlv%NP{%k<|-Fw+6Xc608r!%PpDAI|A z$L#feI~d0urL@R`!FF5vUA?TU2VCT?$!k!I?cH6f9viQ+j`c7Y@5)j-7do@rFOG}H z!NmT6Df~C|tSY6QE&R^Z7C)FkUnyN<;haZ{WF#a zAooL_>;8xk^^=l7Oyi}7cX4BEi~el^lLGTH`;;=rUBSP1fQdg_s=j~ycWb5xOyW7@3G2b1 zI&QPq^)WD^=QUdeTbR~O3Vt>W#y?PMe!r^lc^pg%%mBB&+*(iJ8}NA$Yjx<<{n2xh zW9S_t-Gk0WSHf#ac`p11aDl;G#pNG|ubsGlgGTpRlfe zX5K2LPq=@t5{#jLN5DniE~N*Jc_89odlRvGx0IfS-mEr?y-*hz+qzQv!pPvbEOTyx zbH5c_3S5nm!@REG-#Q^z-YZr2O79ka3x6W)&j+Qng!|b3O$6P!4Xvb}}z_C+b}%QYk4uIG~&11>O$(NemL_a*iejPQF7>-h=CBE}k* zzUF($mg)9`iH*Y_J{CD%=n=LeU{YJTXeZuRx>GQkEsk?P>)GCAc^xFnhuN1MTsdpd z801mNb3Laae;9c`$?tuTCn2}GE|uFk4|3crK{jJu5%F5c%+4;uGpI1OMBl7nKfr9u z`MW+7i|l$32b0Kmndf?;XFcoLB{#!4mG?Gcx~q!{F$nDA8C&oNeUr&?Zkrce;!iH> z=Cl*c*rPSg!8M(ooH_Nl@}Te44CjDFoLa|5ia z+C|55?saC{Lj}U=3iViF6r57)qE&qSdSy13na>^XpnVN4_4$ea%rMz8_koG-<)U+W zJPZg%%VW;L`Cz>JBTo37T;>mgT8 zaZ$)97w=fiTZcG=L&1CKle1jr z>n+gMtn+`&{hv#HzXyId(N=?R9pK{Ux#(&hGyU0lC-<}>-~yMp=w#00^@0=q(9gOq zwWzOR{^vG~Ft^w>={ZaVoc(>|&l1?=&-j~YgV+nM1mj=oQr{aeb!&ZmX?}5@wt{gi z%RS~hA&*0@82cLsLGm84UU13FU34^@$?`|UU=90om5WZd(1myTfmFv<5?rjuWxgj* zTa(y6fSzl#c2%3(rJ!9dFt%%5>b2rCt(iGs0uhZl!kXy>6TQwwv$${nEEqZegGt`t zQlF`FjWyE`CVr!fj>|E3u+QtjC@WlaAjj|?YdxF5grY9>`Pd0-Mj63cVwKB$Es)k1 zY^w!~;~p3Nm2LfEt)~M_gk%tb$NeCAp+F=ifdk)6-~oNLVt zgYo~%WnQm}Tpb4!cqVtQ7JP_4kGstKqR3UKK?PU$3H;!q;4VTOjJb;IXwo&_1z86; znb=l$=jtDl+YU9>O93EwBiEBhv{-5CKFcmeYUkZexB z=D_w5#s`=uO-y>>klvS7AgcrydeLQhZAE77&lLLN+DePxt$!lb)KU59mnbG)Io59~z$iTP(a+j`4oex3k2IDL5rex~3r z4le$-mg_&6W5s^22NQe8MUUchRvu`6+mq|93w9EoPm8Cy%vc6T@FV15Nju+9z!hS;9?)SXgc@X?n0Naw;oI? z=~Ayt*+(#%J#Jgx$7tIpnw~QRBlLK{*gn(rTq78vCjch#xr=IzYYV}2v7WCmzh&i` zzQ*SKUe5mb%IE@HW+N;G*BUXgPCF2;cF3yUf~+ zfv!2p2bsjs> zy6uaYah0j>YC6Z7iL$M-GFr`hCf5o^YZuqI0Wgl4WtPX9>G}tG9P(Uyvi8qlV-mRw za@(F|IrmFiAXgx_I(}2&UpwS}$cKzMrPUVpv72@5T}FrDb5=fS>(oBTlaOy@`7Yb) z<3QA>A!ha|qn+4JjWv@36PR5_4ZLAVe$JEU zt(i73fj}AgjqMh;!ffmCGTMikPlO)Pu9aXCN0d<^GrtN(=oti)IDktqmMX-#b(BZe|LO=zCOB)zG0>=n~}Ao30w-?nLPdv5S-X|Y6BBIzKs6Ox{eo& z@F@(&+g7H|sb^a=E5XE0(EPr{ni&KWSx`m^-Z#9;%y8RA*`L$O=t>^{F9;^R7J^e? z6#V6HK-X zCVXz0`h5I-1S4`}5R9!$W9C~kqpatAE!WPqW*lFlR$Ne~?mu587||{tn7~D46yaFk zZ_UgHlf10V{QNMXXAzj-k}|`)Ui5VU*M%q;$I>#(>$$lf;w)cQX5QNowPih+FqoY6 z?<%7vzr1%c#^iCx{w62aLZPa}?(T{wC%GeB^d?Kpuj;3!k&(+8VSQ@)YC^ zMjMD1*3Rj3q@2Ss_NAwcp679n?-O?Tf^zM6OTj;{1DCwIOug50gLNNm0^^OKuc0VQ zm)1vYPx%`8(2Ksdm@{QPm;)}bylm3v>UV+(-cm+w(3@pX`O3`Cch4j}nk8eOObN6Z;#JbAp7xGpf zuSUQn!F^?nli_%E4Y)+Cj2^NWFPSyi2$;wtWwZ;=T+LCodAy*)8;Omqh6Ml;J4uA=MQAS&G zA8zl^VGTPUeR~o~X!gv0CE98!EENYk5znzc= zA>VG2oQ;49{TI1rv?0z_^n>xH%4kPECIB~)Cn6^6zyyBBdVu%ai$zQ{hW*_H#+FxZ zdEB5tUiclx2IP0+bCw+~W-k1@A53tYa_TcO;`|ib>Hz1U@|<&QH{@Q(FU!)SuLaoF zYA|syPh~O4H~1sxqG531ZOds_9QGL zA@|->uAb8?mu*OIIAe*X&T|29jy=mQ_7C*u>L}2+5L~LcoUX-Zd#lu^?@g}&6W+UA zy+`Oc!5EHL2EYYpm8*MOXA4gB!3Hq4+2!i{#gh%tA2nd&?(%M(YYNiNnij8Rz-tJ85}?njrE#4kmbNIX%Y5 z^Veh;wI*cd@Ad3YXE|-j?Rqf7WM9LT_Y?Z}oO0TSbM)y913ki)2TZ7|oGxZPTkUMh zWA4`gnAC;kv>iVWb(Ua+o-Q!9E6eEx)-xy=(O=8KB(4R+^L$mlZcFG{3nsck^Lc?a zlLV8zqny6VF~-?v`$pKhtDJiHGyW#(mskr{f(hPTZoaQxFs)!xU|u!q5$y_riLcCU zi^pOw809|en#1yb$Pb4fVPg+D_NnDQ(JTD)_fB7{{x*=Y6)b5=;z?)wMLNV}R`pmD8{IY!92; z%Q`kN^O|+eusrV<^dscC=ATN)qm%r84&-sjbIm^;kSBBKS75&za@)Vl4cC5h9lIPC zjHvI)TFpLyIm$Se#TZPmo;S*K9%ph}$H0W&EVsNbntjRt74e4r8hp;m4UzXvV4`o8 z(-AzzZWS>X>ps>M1ZR7@oE~BB3Bd`Umx77EQ?BmqerV0~G4n3!2J3;|iChz7V+c&( zz1)7YKPfOtFt_7#mL4%S@;4zq@0Y93n8@2zA7g@P0+afnTzyAFl{M1_CiG!BjkC}D zSum5bDy4JW?b-dB^d7~<>qs^@JH_X41sfeTCSeoxXG+5-F`62=U9tPl0*5w z!QKYgLxAk7to1a3aeSjO|FLG;z@)w_r`LF%+kH1v9Jy^_F!7D$bSE=af>HMmFx#oe zl6~Mpzm?OU*{8zwhgD4aUb`@u@R1et7$3_%AZ&?ttpwvgxq|+|>%dWKW{_>24_oZ>>wmIr z*C^Y%u!4M?L+b@2+U2l0@ZO6G+MfF~Zx3CM#&CZ4z$C7ypq1%yEf~X`-v%!Hw+i}( zbNgt)iCBlhgs!ciFE}3;3r4mdOmKMxRq&X-)tVUu6S=j5KIQg5AQ)k5l>NEAf@bob zz;o7&V>7h>-U_;z^|%Z5_6t2eFv0sP=q>hVmYLx>W@69{p!U&NbX} z_5<=)w}Xv&!iI>;S}>8PE9eSLG4_Y8nIzlUzS8`DLD3%j<__G)RFQM9tSIR9$<@@%6OjFPCZ)WPyj$o-Hv;!7g;?VOs=Vy`R^nI3bFtN8P=u2kyv}OwN0?g<;6|^VU z(>80y4<`9Zh58P3*q*5GBEQ~?5u=52D#Po9_PgXg_Si*Q4MG+al6{U1sZaxzEjkloimwzsva=07L92# zGn^AKFyZ|x)oaxc5DePR@&x25$dBaM9Gxv^e^ZbL4yaW3v`-Uqv8SzoE7%6PcVH!b zhz`YnmbuXkCe~W1?kVPAf?$-nm9#h4kxPU^S$ltcOUx$W`VIU_}M7p z9-jm_@6nZX1p9rruqXO{3{2#>O6uXY^h<&fJ{1uBKBAHL{ zTNmqfKuzAa5{#%zLtqlo zN?OcqzrIBG2fNca4>57y$r!lcs!CeJ_67up3u`4;xGiGv0CJ6EzC&r2zRZ0r3i_r6 zT;xI2-(A4f2u}Fl0meH}slL1EUxE>F=mC=)tRyGTw;u^c#4`rQ_I4$0#qn%+37_=2 zYZ#2@LC1mjdhyaGYBR)T4{cF1Y|7V0C@y*t2vbA zdDyUyL2k7^R3NW}JOOzOpL6-p$~wNOw7dp}b#y|WgnYJ9hj<@T1dR9FN}9>>_`*6K z{a`}>)tFz*47X<;`;w}prAAxmUl;yu9CH76m9z_wf$hrlJTj~ql_twtfU*5vsXjaIFriB@9bgihSJ4k#Pr3vn@~MZJt*YoqX8vK##K3r|ir(b>|F@ap zwhx0*cCN~qb7O1=@_$Z}PX#+*p4qjE_JltB9_4y`@SSZmL7srzDz7=d^ZEC?Rna>B zJ^m)*hFT$jdvYx3`md7Uw zzuK{CgL4R}J42!mDF=n4fz$6bu-Y~Nvn~`m^?*v_kRMGipSXSH6?~?O? z3(u`m?{Uwo(8mPUPMLdW+3q}WvBRrqnB$J`6EQWc2^WEj98*ODyw~Iw`b54(!30mP zqKEnVmvgO|0WiwhRn*RU9ZS)m3m)j{m;w@m^gHN!2Y_; z4EL2SANd-tqDOf=?;{w)JWvTPcx@G(W*jFX-&(;WB2_e-^R3UQOCOhA;FMnY&iV1a z;DqnX+1@QxbQ7=lHVQ`M##%78hpH&a`=W!m7k zW%a9m?G4AW5=`vnD$3_$XS>^Ei~FnZ^-fcAmpJRs?67GYVU~(L+%)>Qm^rVj)_{4&Oc0o_$Rj02QKwv z6|KbQtoCTny&i--_EVMl9%ikkZh&0bXz_b7&raD5Z2@zI(S|to_JRqHR~hz7F<)qQ z0+6R5zu2fl8~Y0Wy$ej_=PC*s8Lf}G|CX~JFpG=~IuBi#zOhSzEDkR8OBH=(cJg)$A_9lxtN%M*OzyrqiTa|flLd%s_$b*nyW%NahO(*0j$kThM zdsgdXNaS_|jQ@XC>OGc+3g#g7U84$g_k)RnvAXA$_YydM>sXJ?ZTWuC%yqe}%eFiB z*9^=13^V%@E-=B(-E<)EtlZPCNo0F-z{J4(Xk^kiwxh@J?+X4x;2d^0t;$hHInKRc z;#<0@6`!;0;XB*vhdlht3|elKr=K;0U~1e5IOW$F=JVJ%$W50DjkEs=zJr@gp90nc z&asu-JU-^~O`=z9!At;56wF=toaMWiXS=`zwsxDZ(bn>)m*tyg7o80Z|huk}f zd>Hb;B=RxHLy-H8eWb-dzYy)4U$~Yr%!5y4Cwv@LgX!=sA!C<1ccn z_YO)%%sF-^>buiTOO5>^m`X4y#ch7JLAt(QZdahYm6>9%k?;wej?tu!;6%uAkw&nxY_Y zDlOWmt<^j%uX59ulh|WD^T0)_-8uK-x*(6|kh8zNkf$Iwoxc$6QoyYR=dE#*%Z@zx zB-1W^-Ojp_V1m=!G&+gTtShe=V_}BH+$N6MJYbSwt_Kg3dcJATS!#tm>~qs?Mq64Q zu|FX&N`sp|G3ww&o%UQ2j^T1}jz+inIxVd))<7PEe9GqVWn-r8SkoPb0iNDsQr8Me zaPgULnr7isrtcNl%PT?6*~?8)3of$`@_>mpyUlxA;yhOXOn7fMjhNb}=NiXhA-Lc^ zZpyc?m+_0^5M_O{+~#$?7Kb?GDad0+J!*YINYyxO02kfYZGL}@q4qdRF;-@~&Brq$ z_t^g?aH0L&>iterJo;Kza0<8}xY+(~derEjmJ`e@WnBll>98CO+v@`ppM#oiWYlvO z=`|ngO0cej-R5Uh&(W`=90LtKlOo_kM_A9JSZ>=C{I;KUfl0q}Q4z5hF0#;XdsY3~~r# zZsNEhzZ`wPz-nIQN{}8>>0|O{aKRI`_Cfzd9%=Qk4e}J^>324Vg+0+vVK9!9u#V$8 zcB?hB5=`h6Z4Q6bni&KWY1f$7t(j3Uu~RkXOKZkafqXvAtv*9;iyCcQ>vM{NX8OQH zf1muAaXuKY&6o4IlKW#3+dAE?z8enTC*p@UV_`s~&sQq==atNzq4~W_#wXNzEsmUv zgJ4o;q7LG-F&A0B0rL2t-E@{wuJu)3CH(DlQ&-k_(c8`bdcef~&#k`O2j3_B#oU!% z2H~IB);w^IbKK^4ern^Q3-T1?>G!iv=z%ST`B(6uQJSkrn)W5jf=v zHyy&)Av_^Caj$0-Oz3JiJpzMSHbt%sfJtrTqx&a`oE_r?{7+5`g!vy)zH)H zrhTwx$g(A3;05Eo)vZ2fM8*J&TzAa}7rf1Stt@jT3@-3@H~q%B@`dnA%L5l|tYqd6 zH_bHqg}GSj8Uhzt>84KBW$-PthD(95-GgOS38wffpd>?t;Z zN&W*qam-5vBl5QmjPd~Xg5XS+Kcer#U=j~$$3r=MUdhZOSnG1T0>YNalR@^U->vS0 zb_hoJJj!|=)%08}n1l5Dq8v5Q^O(lmV9ofzq#oCp`>mPzU~EsgX-oF`d24177{`;E zo_DO7C>Z4_jrrD^835y5YjNx?`g8-B_#ZG+YLRD8Pp-!cCOHX{uK!>{|8kqJ9nt2R zF326vxM`U&2Sr~k2NQkPO&4+ewyM)}P_$_+80C4b&Q_Tj9y>`efdTB>u%5jHldg|Y z21dbOULE@DMK?W*&sn*u?S(Mo0h0ieetsCfPt-r*QvgihW$m~i2h#;6@~YeNKJLso zUCw%jv^nW8;kU50mYLVw^fr&d4r?X}#`}hw)^qLX5sauWb}!=iF4oiVH>>TUUUMux z;9}sS%snJ@iC8i>4_sg!>JVQ?FeJDm^>qz%i@?SIt3&WF5dx=rn#JhqoV4Y{ywwRx{c zwFhmgU(L)-siqZt9uB{o$On~6CzpbMZU-0LrJ6eMIg8Wsoo#l5aqL>HKGPrHCv-sz zrq}fO9v)jUwpmbZ&K+%Sm4G}B`Mz0mp}tmRTVrgiu$n&P7)%o}5dEltE1V9$$)}$0 zZxNi9gY1tVOk#?U4#nrJwrTzZAx}-Mria*{Gt4^J&QdV3;%d5!b99MdM8EZc3073o zLe7;Z1S7`75SV0Dwff8-=#??djH?tFucw;MV}BAtj~Ea6GvH5MwR)f72EmBDWWSri zIi^?BuIX4$*KM)cxbVzs`Um$L zzE8AI)XfCj+pC&(G}cxTr!g>ry{qYHj`u8KPxxKnLkwnDQ<>40(9;Yixt~Q|i~IJ(4|7A{Y=>0S=G^xF zY)-~;44mW8YWgep+n0iq?XQPlfoeLN+rRw`y>EqI&0uUtRFjwOl?lesZ|&f`^N>#* z??VMA>~*ufW2@;q_UT-~h$9F^N`3Sy7|6J3yd{4+BKCUsIZZNqx5 zv$j>xfVQ1dO{a3ry4@~ev zO;4A#o*^*GcD|hBpK~My#=EGRuH|EnB|?wrGv*2!QAZb7(}C%+jep2o7lvcq%&|Fh zesGSVswCuf?IFwJJ^-yqa)xV-PQCJ+wB+riMaQJ3HMa1*SN!n z3E#wgvJOlVOa<%tROk`yRQR_|@IO*b_wXFHW4+#oBF|l5;wzv8tLD;ImzF$eZ*jZ2TK@E5Ld0siw^U+5a!wZf4vo;0D0? z@2jRb=ixPi6Z6LgFt!J(=|^tE->sP`Gr>GwP0w!!W{UXk2+(#|o0{I^EGt>n_&U3(isGp^e<oL%ie4aN`d)9-Cg1Z;LflAA9 zsooUPhJ#MgFiGgI$V z(N|G0js}nUnd55yr-Q+|2Ej!dJ!H@F%aH$IqD>w;gKOfk!k*|S)|J0EY&Lu7F}}w9 zY{7}VZ32^M@en@@#QvgS5PP_vK1a>52!cx<oN^3eMn@2Smt`$fOSz&P&p&`uo3Mr&r6^*rpMi@5*0 zt(kE!v9%uRU}nh7aN7#~=(CqRRKm8V?XCNh&fWCTv!(kVT;NTQ`aYc#1SewI0Vevk zhYshqby_n$U~KPtsA5Mjmu8sk`8Ni}yTL=(^BA`8qx+=UV!wyMB)<004Q$IT7~wY` zyKVv(`UYbYpY1aRhhvj;`}c*dZ;}6Y$PX8CG2WPK0hj#VV?KYt=k(K;+Nt%rler%} z=5yZa`^_ib1KJDD_M=B##|DL6Emzp)8Zd#4$UF8Oe{!uQ$7YhbaSyHII(5F#CHlrb z8}0lZwT$asZ-&7+NKx-9!T7hTp{F^fgBb>WV~9lnoGq`0@|cr;ArR^9B>bC#zlGo& z+tjH2_<2U3zK&vM1(?{B8akKzdaGG_UkktDV7!hR>gRD>E*LTXI9?mTrFN>J57@7R zvN;(q$9~A2{2H1K=XL+H=Mf(mW%n9-0ebE8g)YQ2a}I$0Zv&SCcL#I$o9H8vt6?ya zKh@9}+dEU}GRzNs%sFe+c`zv2a+<$AvvT%fu}eP;9C*_@1NJGe+qjrrL{A_u#{D0MZIVw6jZ$JWqvX66efod<9X40ADX z;S+19jQjY8+}t{Fu~TZ)J@!7q3IAE2?Eu(4t%kNr&x5(SN^pUW8aj`Ujb6{@{feYVKL)-G2 zu0`k)^Nt+ZcOe`fYJ*k&iV@Bnh0xuR@N z){9`O!bR5JHQ4b^ZxI7{dM{9h;h%_Y{=x8VtcDKeJbTW} zuwPAJLci3|3Zp%P|3Pq$Uu) zU}XM-32aqMce1_11Y_u@ad0-OrHc?O`$>Wm`OocC4nfXtUrRRbr!K)kpN#(;<{Y&Y zXS?0moSZX5;QaZu>KqOI6a9W<_S#`NxJW@Q?aFiA--SN0_E`%iR$5DEaNT%YFruvr zOsof|RM*nk+@H1k>#-Ga%WH+bX|?nTGkXgLy3!`0hjn?ug_~;WAdcIg1t)aP1Cwa3 zrAwJvCK%xt$9568z`nI~1<###3Jxc{@D9iHoX0j-f=eD!OV1d`sF-61!9?fQs^jx> zsY^f4GRl62fr*}1OZA-317?Q%CI2wQ`}|s3%z5>eU_}41u4ZtdOKRx_?w73()Z;1a1;O|) ztECN`SGx*EM)V8YTMaIJZ7sdcW8yf$33~}J!P{!-U+mKgGsEp0 z1Ebtgt3HqWVZj*MS2!1b-BqhTtMbTjMhH(yB$p8o>~fWJ~`%S zang?|nCWI)eYNxw*T2#Xlf56a8jS71T3W^oelt-!47oJSK0Q*a-p_rwSr^+I2jf^% zOILCpoNLV#29PUH)~e6Jyv)pSEd5}@&(zXCxot_o7<}pg=Y6)8PGH}E5S$iE*3|QqJR71!K^)0-Wtf z)FE!?H-Zzo;$Zw6Yv~^LYx9G%VwRbo*sl%XQoqzv9^0HMI57^V907Y<)v3>-Tp}1l zTz%l;`E}|s>obDW{9=3anc2OLO86YeuwcZvSOg|jS4VkV|M8oNd=^ZUecH2*E-*48 z1_NNiEp^n-F{nEvD+cm-VicTzP906>d&-X#oUrFO61q;Tqf0qYmzx>R1s@pOsdco1 zbN+q7V7~`*{lvBGZQ!D(!FR65-wIB|JPbw&)v5E=oL1d;L(KcY1vgRL9t=zfm*wukCI30qBIY`53Z!yKmt)=V3i z_}z7MBj@i`f)RZe1{1uePQCWye!+%aMXHjK9L8u)cb}j-KP^3!E)DEe<><`@w|Yty7;(yxUye9%Kss zf{+K-)zQz~CuKq|jwhEgv%XHfSK%HD`YcE~TYmVlC-fu!r;W236_FlS_Yk8M7;|Js4#Y?xco(Bb^+2XdfgGm;8X#k(I z+J^o`jZO!F+tvdvHq}diHO5Nlih=PeUb>ib7r&Y4Uol=-*E-f!>ZOC2+bH}J?b`&# zR^g=)ZlBbpF`VDZeE44DrFMMIVzg^H{g4N05l4>ol*9EnideUUNi}-O&UWToGu>cp z`+$KFdyh4<8jNGMmu{IPClg>|`+2FJ$Ny78k7)N8`!mN&5A&YJzXc=4QNgj$bGVll zGqdv%mhEZ=<3G+zC49c(IBO;dCiG`7JvSFMVny6cvQd@9EVstd^FFPiz1HAU~IR0 z=@`!a+k`F=#~_&CD&z;xTMr3F+HBS(+4K-0%BbSCVZ6MHeqWBO!!+b z?ay&6JKB;-fk|xg(g{4bohTU5uKeTCA6rkOMvl?t876zZ-UKGJ-84FynY%Mg_IkYy zOk(G0=6l8s>x8A?!uiwaDE9kZp-aT54@|OTn)+Ng^v}e&*V@m0I|N2?O`}_R4CNi8 zw_nU_%#DGIS5Bk7I8WwebD8;uxxzN|i)R|Oa1LCY&1Lp96mTuz0=3iV9*)ILf)g?A zU|sdo=poJn{AR*`!SsNM?>kLB-}tJ~BjOMPlWLttZ{sCo_U-5EZ4}Hfm_*MsI)TUB zE5}+g<6ykEPNR!CR`ZV28R1Xi38(?LPosyqU6+~}j*%ZsXw@|Ofth~6h?pzjI+$BM zO?}_oZ-Nv3*8|4?&uO&CSl@&$w%HFZ9-BsYT5y>)KXW5&^WkZjD>kZyH^|dmUYE zx?h?-*0mgr?aOKE{FM?6bY*gseUF0+j83B)?9f$yg04%{h4o-kscGtY{F4PE`a17K z#Nfwiw3=)AZGsUo@PKh_J)PF@+~QcE+Y){Uz$B(jr&HLU!v&MBuK?jQ`@ImHf5+)` zUkSJ#!3ldSz@%nOrz1FzTTZmJ7Y7sAce;8l_bR~{)&m>BdH0)6*!IWu(-{smP53?~ zh<-V8y7`%rh*Rdg2K(*<=a@HL-5+iY>V9eQj;U@i>KfBJNK znYnd>6Y*lMk8O5MCp#X>VlO*Mw+VgN;!f{xGnW9TTt8jy>!pGd@nCKoT;!(dbR&C>>2xlyZ|)Nu?8+KA2VCsV>2$pv+{c0wIo1g#aL;sll$j|f>unHy5CP-3 zf4cfk+9tt>yl4N{fQ$ZfI?ZCg4$0=^SWALSu9;4AIKE2-C-TgGGTQjUbSmeu{ghxt ze^!DCCZ^LWo(r}=MfW`&R}41z#C`?9Io_I1W4tD)6&(D}taX`N$lM3hX))K$C4w{P zi-L=OJe^K3u6ITJS>GTy+h^114mZ{_c?R@iR9*5 zz$MO^L9NEMq8M}B)=qFr*950!wGm5xj;!MsOm>*f0C3=!2da>b=j`3Qp!f7;j_-oyh$;BpCF8oO1)< zBE2)zaq>epm#G)*+d^>uo6)B{=JGnS+R&=iGxjYCE^yZj+PD|EzX(p`;{Y=s&7eB= z4Zl&>R@v*-4Pe5b%pfn%xmTW{+Y@@GoQ61jH-o0~*c~@BJbt`jLOc0rE?JDC3g3jpCzYVa~6SI`IFE5u64s$Ujr^s=u_We0h@9?ooPSEDGAOt)kmjs zpB{3i-tR)60xquu{p#}3PZsgaT=VT=rou;`aeX;S=n`Wy047-Dqqm{c5bvyOqZWem z*ZSxl&d23uUF_2eFsZ$LbdGVItBp&JXB6Mc&~XZn`)tOXZ1&}Y66M9ZZS$PU&N>qObW~y zMqOf^-3lgnh>zk%J)*xtV3by$dhHyziMDC;Z-nhYzAA?fj!!?B=%GIIeQ<^vF#;|y z*GDHAeKywrKSSRxGbepVQY9E=*U6byF!2EF8GRRfuOTr0!+k_XpG1s$!32)*QJql_ zzH`6#Lmq`M{5h+?@Lj>*FyvwUK7H@n3u0^u8{=S7NBYdq1l8(zK_~Kkp3m|bb8NQ> z@(AR)o{=~o^4KKug^(vE(Z3w>)FkpXkUNg@<$T8DI>^0{=kjkH@&M#k&(P!c6`Y0k zPolpG^2j8=pAUI#lHV_cJOOzNY#IBI?JtL1Ioi5?3gl}b_d{;VHSPIutYd_I0F$eY zo7jg*zi{8{}TdP4!#Uj4+tkd>=guJ}VC5nRzS0gpReSPpD&= zwHMnPVtdE=%8^q$72pN+7r55kQMwN=FK+LPUCzi*0jv|!37ui=vL!+7W%j^ zI#^%OM{7-Xmu#Wn-?|}>LvDH|f;MK^)@m?;Q+%}8r~{08skwR#HViKL2OQhn1TNI> zGe56a)Cna7zfZ+F%V-mIf_3;Ik3;^xF;1EeX4=7|I*^CP7zw5ujN?q}bpx+I((PwG zVBAJMh;?Rd&9NQ^r*vAc=UC4e-$r9GrLend&=-z7%Z}b5-HFsBsH@lb&z!gYjPEqa<`@#Zu^L2jjTd zr|waK)8nmPo2B5Ny1}FtS*)8x+gF1LUE-q}@L9I-o!dSHc@*+x#kk zo@6~gi?(ZXU;cUUB_q%Kv%Y55a^44dEbOB$&WqiIT;%h7Fy19T^}3h}Yi1Fc7yfEP+hruLov_6i@9Cxf_Tg!1S%Gj^s zS*U|->lPpF%I8qd6EQ&huM6)O*Mdx7L$jTWfGE z!|glCni&HVdjk2w{#+~=QP&DCfUUJY%4dJ>6^w{eGZ^nPKJ}i3#|0yDiv4Z}7ka@* zp-I}-4JJ9L`JEKHguT^Z0&&5dBA*QQhQXy?)#lP)gf3B&#=(UD?W1GS!C8GQ z>=ph6KE3Hvual@dOV1TykMpGkobs+_Za!Qlw`L+>5>!u{ac(9Bqva;Y zyB|z)O1=4d3fPkOUyXo^ZC9^8=j2DBOZa5F5P80HJ>_wqG==o`i5R%RD0|e?-`S@{ zfiiZUvL{)vNiv%$f;-3GQ7_ z?VOhn3Pz00UN8y2#yHQ_ZD|bqy@vg1saLPJj|fJLO$A&MoNZ1$z0c?SqkrKAy72uLoS>sCpX7QODS?0GQaZ_4F0*>;53@9is2uFt-p~^wfIw+PEy6 z>9y>{z57vc{*HQeKUs2yb`F9|oe6!sH`jcg9y8HDqhKPTdODq%^D<2KKCxpF;&nki zJNQ1TU|rH9THk7mVne zC>W);o;Dg|Zfrku%j;=AbDMQpw;xRSwtCtcPTT)v%{VSWjP9scuh%X< z2`=VrpmTZL?6y$%NwdegB4E6g4eB|oS%OLHLJIqY`Ty5I zw{rVV{HtzL#Crw%WpAXzIF{F%8TLI6#=CVRwQ>I5E10xB1RV_fz5!fhyGC`N@+ZLw z-={2vt{ocbNA8#HF4cWX>q1a9T|VaW8!5ru5rPxC=7X{A-bmN5@8=4}F#f{eB6~Ed z&-S}Xa3T&X!33r@l9yxkfHgA+CS1};2XP$6%nZkB6ilMBQN2dT8P?kepVBF#wJ(1e z#$Qb%J;F8daKVZ8HG%Q^8|f|1g=Yl=U77hxfxaL($J|D`fY*a(U8dUot&R#%S=A8t`0Dc;YNzv!ORtmAr296iFJ*%JLld_xw$pqQeQRF+idf#Y%a4G z&UTZmFQwIx-7eRC6LDoOuN!Usp^*+Y)@}5qy!OuvF7#s~-OlsQETK>2&O9)Vjg9Jc zl1G~vwzm*WVv`n+uwV@Fh=Pl3)ca}|A1KPm__G_Zkxdwy; zC)O+7VB&{0ncr{8@lU6lTE}DHLUWs_z8Jbz2wfun!(d_|#Ekp$vcKtmiE%j&#&KQ~ zy@r_BR|p1el;_lxD=`OwOW$+xpx}hP7BJ!So7DZpPpz2_*7KJp^kfWet8pB^IZII!9ibU4am7@zY4i?V-qc6 z&T*CQ8@SB1r3yF?xX`L5D&ZP+x!|<8G7|t3eV~a>X69)#!}hwsI37bgxvr!HBihNj zR)F&kG|_BclkMB1`z2x@2NO;-(KTELUb1G^v%PnkXc=EK|FM~2pYnR(cg;+?6QAw) zn^>EspFxsdPNvrpUT}fhng37N`GChYW`BGzh@%*kW_N4dA3@MvgP>{t-6WG_GBar> zX;8KpHptpqgCJJfLD*Qqpa_aV5Of&?MKGvsmSPZeDF#ImOeu=u_dRp(WX|0C&Ul{Y zd1iV(bIy6sd)`0yz3+Xm{ar<3ZKjiT?Sp$u+($@VnVVgirHzcPh2UKKRx0)i8Sg4$ zzhYq0V0O3aGTO!DJa-J7wtpq{t2pya7QXkB2A8O)w4a%n-T!$B>iB?4<2@MCce9RJ zM+F$yfv|aYcWe zy)QBf#$8iMJ3&byJt?+Tk2Sy|ww#xuJ$WkW%L2B_;7>W2L`x-w6^wD))aajr;I#HC z``Q2E9ics}3(SkqTgdN0Fwu@GfztVo6 z3T-XBcFT2IavA2Uvk^%tJaxdgML&bfUGa z=Dx`>$iu-(`+HK_a%-4ek7;o3gB5y=juTmKTsM|R5w}AsjrS(tca!l)W#jrXb1lI7 zba26Wm8vtT^X_eTg9{yooJWN_KM`%hY*(yn08I3VO4?qPW8!?;5peOND(PGN2I_=w zxplLZ%!Ddw>tE63xE#57OeJk$jjQmh3{3LR%6=-Zi~(@*uEK4W^C%1^bbR4Fi9#NM zT$P6!ejkTC0eL64=PfSF^)2STP-e!#Xn(~$+yZ4{W<419f=c^7JhR-)Ouqs-cVZ>& zSVWJ`%-kwEsR+{rM*CZ(a&E)zl;hG5CJCm`st0{0=f*JPp>QSr51*}do;@>KgDeRy z+*xJc17MbmZLI_2IHbzhb5tQ>X!g%juS8ouxsqaFt+uj#?@TbMUt#8hiJVeNHx$tm z0i&H-N!Ju%hQK6#g&6}A>M5>g0*vFd;*4t%+WoIE6=3w!ix#r@8JGl^bw!vp%$!+iKYLTynsOC<{(Es-ZZMIviZh*Hw1vf)UNDJY zVHSf4on5@X5?~zXRMKZf%Dx&*8cb#l8(gk0&+We{xfAD8G7FR0n%jKV=HWQrtrb;>- zzsc((%=gG>`2YD}66k0C%GZ4t3BN>4BWw?P_T=l^zeaQYx$u36_1soT$64z}#DRTZ z!9LwlNt%^|z3jem_G=w;$5auPMsa;>x5<5E=^$*LQdRK1E;Aty4p-8Ag{|z|Yd)Cx zot3|O@3IeEXh|h)XVoR{2@ivDomyo-KUu_b1(?XcD~)#^9DlhjmR$BU`xHl8;CaEt zc823P^;-0A4^+}Q?9V-dv0R^3gG-Dc?|3YJR&c_n5SZxFO4^_8eJU7n4c7-I`Di5_ z&w94HLh1J~`}73b9UHq> zl}frCvB=A9%n>oxX6Akz%W81$S1SwdWoIS?MjywVj@#z9!k)Mv&<7^;Rwen0T%!zw ziN8}xm$Dw{RrO&j*w#mt#ycJq4A*U%nKZ@$zNW6YG8b#qtz0K6y&gVosHBUz-p-YlpLST|x&7yH@*@Qjzs~=o^+gbD@KIiGe0z|g$G8boVyIJ&> zl|#6PH3+s~qVIESy#`j7j1BJVbU(RY|-e}9$8Uu`kQ#NW>{-bDbLsx73uka~5U+M-EE}LaPpHkQw0OQ_m7F~b?rFgGpA-q67H8PyuH84QMNkv*_U><1vHgLHL z__KtWgJ;n%@6n{+?|eTrxR})*b{- zyGmInUCfkJ(F@i-K*YNrOl(RO{i#U*wgil0Y8AZ=fAV}1dXivLV4g2hZ|lIsr&SeP zH_^EKF~qX8il)I(AzRg8Tsu`6dyZs`gg+rL@m;Fur6T_Hfr;!^MHNMuVK9k3s_gIH z6}DD@iSAiNpYl5H5mzhgBF(lctLR{E_ou0usW&5r4OR32&&jSkBJr6I zCOo^!*sDJ#^oVzRN7xpa%z2!O81h_f2u$L%D&_NDEMLYtAm0z4^Xkf6k6aD8`?6{Z zogQm(_5L7s*@2KnFcIWJb2|H%6+rMDut7go^^ zR!;Oc)nMFbR~hf2UnA;Q_#6V0IH!uXFOnyHU{dE*(Ln{u&gB|rTYXjbIazl75ArDD z`&`D~YjU~Ii?inYXjo4gToQV0>s@B9xQ0+q7go`Y)^eM<;)Xm1d9AgrnsMP6cY;av zSJ6sqdBpXK25up^#J{Sj!ODpk$H2G-s_2X&edjVT(aWmn2G+B^D7%kKfdVe~WB zTo(I7GW8Em`*#)XYSo2U%Qm(UT=1SME7u*c_b~ceYkr$Parr91gdeH;)n}~cgNr>{MQ7tT2w86KEaDyk zqditd_12h*7!0w!C#y(fpRN&c67!ldFwv)~jC1Fo6pXlLW#7}xJzHgeAE~8nmJTEL zmRHe7*7=Xf&uTF4|5ed&fwsx^LSRy>%<=8Re7~~iTrk%UPJ6e?e!oFnCu!i~;F75- z+Rhp;(MHF?q*qtbmIeG`J?p_Z)>P3R1@elS>35=hpHxu_pJ6Unf1+-5Fs@IL>)an~ zJSh6z+`Wh{FnYSmcrW`dSq9^;rECM>;%lp@iPwt`5S*yzI2gxIRrGhhzdS$7V4M|g zP=l*0!9_N$roHm=IRE-}1KZoYnzrEUfJH)=D4%v0`sgXuG>?6HL@>f94;ZbqnoeOo zs{~`osRiI-J5|$RtZPG-gHIx-qF@|*R?{0iKil@&Ts#Fc0w%RzH7)0PUYVL%2`1i9 zO^f(U!K2j71~9=xs%Zva3k=&C&P(lX#Id`YW^pb}zb@ww%9hH(7BjcE z;6yAJfC-&cP2X|Zngk=t7G>tlYI=*ALBZhNBH8P%5it7ss9S69ig9x#m~dY;J!rk2 z6!o?NOyu%v%ULVgTtb85^5d^;34FSu+TK>oHplY{FPPYs)yjL~tfL$9;G$|;YW3NS z565}{jO*%Z`adfp@`!DXfYYz3R?hKQJ`Q=PfLw!o9pn+n-BvrIu1f!n{28pK_qbi- zH>!1yYA~^zs_plQ#F!ZZqsOXgB~;|=$(4-CS+IPi<+}ad44at0}^HmJsKZjO3MTwUHQmU4+)rvI z#d_YXrqh_w7Uz5y?ZA02#>7?CbS}r#D;QBe4P}}EPD^2q&2c?LaArH;SOvkPS69>F zTsC~4%p2VQkoV1d!396BrcGIwXq=EuYOSxew)c z)fnpwpV(u_HfMm*wyUw9(O?*68#uA75;8I7_(4Bd65R5OPpN)WthH7YneSbvgGVO6r4S{i=R6}uV z9h%pq%OH1zYiQWopPTJc!|&FBiJy#E7Vw4bOnCsg*`v-~md}7ZR6x!)0+2@_-!#ug z{+ufeCV6^|@y;0hW-|XpOcsH0^wt<>l(=uo)s-0Emx4)WnMO6U3QX$E8vDC*g&xNU z%5+u@d91M$V^|rO_`(|6kIQw4uqEo7+eZLg`kWg3*-fa+>^%$(To1U|KWgX$t4*#C zq#n^nWZFMCN36zphiM*{8{e4g1Q)uwhIZz@>AHO1^R9ovMTTmMuII*B*KIY%yBP1y z*Oi?QE(4Put}*8NPpX+UV6?kyD8%axfn(`3*x!=sYv|vQc-C)8G)X)K}=NC28 z2`2t%js0vXp{Ey&`!Tc??jv`J<#Js3vlxv2-x@lGZ3P7*^d#6;vZm<0!_{CM<2Cdm z_c5mkJ*F*g+a(W!dB1SmFNfR>d7(X@UdTh0waRgi^>;&_!tYi4x;SV0+5QJLG;VDZ znL{@+Q?|?*Cvn!Zy2k!4EMrVSWQ_fLE5XIq)X-u@v$9w8nUE_EjP_W{lbQ zGBAnH%)EF|Fy@+}hjmPt@@4sQ4ZrV#JodS1<5eLS_1MokzNn$=!8`Fc*#<>lwFFG? z%Np7wuYb*rCmhEln9$l9YT>r`wX~JHZ^PVrwzsZ^ZpG(3d&mjAdu;N3dDv>V=5pjxw(Ok8Z;yAr67-4G!jALpo%`0Gwb9N<|5SZPpj5%jm z4|x>wj4zwtqO8~HkD;yYP)nCv+mP^A2NRiAOJ6XvqtGM#?E(}24eE=TnQEpVOz?NL zv=x{8aKVT<%n~r6-`5)Fvz{y%;ZKtN*{zlivzA*h>%gSIyv4Tunbng&PbnQmo8PmR zKIXP|W0uLaH4XZ!!Nh0O(iFbdc~~%_>><`uQA>TSXSHC2KYd_=2i4loZ8I6p)nPF1 znYFa3wJu;Q+g_PVvb|Zg^d&w!@i*BP#axB+V?8)mb#1}^y!3JCf!uaK3%}LyHxqJ4 zO|5ay0pBO>i29ljCRSURVSgiFbiI}q;Iq?pYc3Bn&s`Y(z6RMaxH!1&t>qVSTmdFo zUrV-oJAz4rNj22k_mT-_>Jz9BPc8kWKtId=%mkBYuBC}0{>%px_tw(RsEE9}5Vj&< zl7U(}lh+_LQGSs_L+sDoT6&iA?1U_nTQ}r5j)BqU*V1WxO@i-6eSkr|%V&7f;9|#v zE3hu6;iprdgsxL+=?yOb6K>O;grMcGn zH0_l`9z7H7kNXVpllhLi%xvw%e`)v&f{QIw^gre}&bE5Mgw9sCJ=U=Za@RSvbRs@m zY&n}R+$3XXDVWHwFss0%e}!>81z-MA*cSU+1||vSK&wCISmJ@)6{)2st#Xm)-K+yl zvAskCV4~;NQb!RzaWH8x#}{G7!GzAMr4x!U>)9VL#q>;n8hz9G#Tgw;9E|O}IGnG| zb;a@P0+YC)@L1djc^dLUv0)vNo$$R_BIB2^q;llR>(E{-x~G-^341T-(~)0 z?;}om264N%*8UDMvG0+6sQ{-9)Y{K@HuIqq@)+dTfX&NaF_-NHqhC@>XK^2MySd?qk7;es;<-w5h9VX`K7eErxPq zkzm|lLN}u>i>!-xf{EUN@dJf-&Qt5@1>;y!OGgwLV-~ZmdkQmL7YQ(lc&&ZS+02U+ z))$1vYyTd=Ll(KwjoleP3`YdDqPo7zAWuV{gnXVYe%L2tp8d*P>2lOX^4B>XT=eBy`@5Jd_0bJZf3-9kx%MeylmrudyLf%91LH_x-p=)kvRd;@Iumn>F)*pm zYVFSwoBhBV)-i#&z+PUNA?Nxo`7i3{^IGGc>bb&CvHqfgbAtBb%V$~y_$>#Vi1Lyb(*M(LN-!=TLW`7_*jBVX0 z%7y_NZfBU1=PdhAdwU4R^jU*X0Wj%J>*z;j8U!QSOqiLHI^%rSL+uQgZ4sCd z)loUmwNDU?WlSFh7u~9kj^(lVR>6s~rNB6**3nAV^@?D`b*gg(+Rn5(I=@K0mV*iZ zrjGu^wl=>b7b9US04BL}9X-YCtaAio+TyZ>!NmShXY3h&NH9jdfi!Y<5M0DvM^kt% z^19$eEJwjatLo@^B%||tHIo7p((C9lzTVq+&g{!sm-Z5J8qA-q4040#3w+L+2ALO}etey=Zi(+Xo4I~(0T}JCb#y4idG^eG zp=>V-CIaRoo(JUV$<0qR{QpvL;Zy7ANPN!YT8-;2*0l;u5=@t!nY?Ca&Y6VoJ?N9H zoKeQiB9sQ%3~+I9?^`($uOOJ%X?0Y|{SCNW91weijN`>Nd%>l8>+J6p03_EFV(iI5VDrZr%~=cC`T`Y!ZLc^Uc8S4UrP9~ZIf;oNhB z(J!tm{QfJ*qmbvfqn63{V}&74LtdyIMj?+4)KRn5PjfC5=ig7%(Mc>H6+Vh`j57lr z-&pHF#A7`((Ym77Bhz0&zFtvR^j+IJm^7HvthPkE?*bG0ypGObpU3Pzb6oqugs-ln zMr%K3mU|fT#5HwvH`^Jv>(HQM1?#xJ&ba6Gi(o{#)9lL)b@Zv#U%^a$6?t%T9o@?P z$Mn0DIX4rGW5{G0)y#Y_@!RX@D{GlVT}7A~M!DJ62||zXXNYaxT}N-*`ZJ?%;r28J zCiU+++MLIn0jVdy&r5?#KT=08^VyPLN-oz=O&v$Sk*uTTyjSGhyL06?ePVkv!NlG~ zn`58;Cm4+5+2<&k3xSKTs-pv$`#ztO`zQOsrQfL|o#zfaEwS0uplb;jH*(+h9yT+c zoJUD8Ddd`Jorra$Ay0f!N9W+P6MvKa6Z+sr%!OIU)Ys6)zpNv-l`(U%0`f5AwrgwQ z$2>5>w7Q*Z(Ax{SV{M&r?+xE4{V;WK>=uJbepRP<-X;HC49md9zpf+V7$2#Q-5M~7 zZ%pPyH8bUP*!!-|c$e`)HRERH=Q?_Y``Vk-OedJQQ>Twn2IrVyMBev;iEXOWQQS6C zYGyGQ$L2ac&E@)9FrsZF*q<$QnvcqKPW!hqZ&rhGZ>`hr%+#xyk~h#VY^T$K?9Xw6 z5kAiV6Wd-d`V4E3^-R_20xmoLto6$I)dMCC=1ePN&b1an9^OHxg;qJ9_2J)-Ks5Z%L-r0@P8@DLy*6~^QrSiy^3ooj!DV? z;L{g+!TlTMkcS}uhr$m3-V1pOa$CP*_8HxfyLZv)Q_inJ;fLrm2Eb@#y0PB-iC_>n z4K|iQo`8Ia+ruwHF4i8BV8VOo)X7ZUJ-PNE?5qQm+*7B66=h*NQ{RNId+9~To0(u- zf6(b%?g#!N?6l|4i$maI`{^{ubaJsG=?-KE= zISu+2fQxz1H*hWbR9ni_o%%_5Z&Fpx|+){9nxjJ>D0QhGu12h`fB`I*} zLv{KK&uLxpTuekC>Rg4~K2o=zB`)fy9E^61Zk%tuyU-(;0GQ;ly7A0Gz|OGWVKC9K zP8IC;i-NIS_YShI9-TJM>=V9E>Gvp@)Y&>UaM=zLjF@|-z_|N#>f?BJ3PzUyZM3V4 zbz=?pI>87%=oI4l)t!P7br%4WyjG_R+166YuFD`+#GjTuztu3>7Rf+QQ-F)bWTY@ z|7G>|Yg=(I*9|5FrjU<8$YYTI-s&U1a}2^PPwN!7%FQxFA&;!pjdhKEM16tR@E3%lm#sHZ*f*;-d@r}RD8fgcxG>)W)$I;z1$p+mLq z&hq(?#|y~W#zM%$U+V?;=rI!m<64KY7@za}6z{ZL1||(=I`@O%Ci6Gbr=k(#zsm48 z!Mc9b?e{J$+|>7>51gONK3mj5hQk9E(52z88l3(U#*G5)hHZwxz#)2?`^2M#E-{9( zu6}UI&Fg7Do+m63oalp=fN^bEPsgB;&Zh(;>OKi3vQ545F1(bQSqCPyT|K?ewtlfQ zTz91(z@HuKsS=;_Vru5;Ovqi+>Z!si$9MkyJjjEPcU$G=?|UIn6p(Y=1|fHs*3;`; zr@wtL*KdiqF_&OJcCM$ptacES?Ao;k*&4P7?l2w)juiSte5b5NpSv5{4Ko)DM#R?* zCbd_+aaPrYn&|`+{9`>m!u2%ep`7ogE%v(?jBEdT3Ugk}6b#Cj-RsI+3|wSZJvn)- zxHzB7t}}4?#=u2e>giuR7N!L!$~OT<3)a)MoUfjTZGN#H*N2ExM?L+M$L8bIOa++W z{Cc{B+sA*iO#c4Kd0-qz)>9SN->NKwzFfp$AsD@@o=)cRt9)tB9}xrgX^4ILTRoNX zy^u=E^aa~kTZ*blp^+?X1=+j&uK@a+@LhF-m$Q^g8`#knF z2zm6%dU}S-eUY#seC+`fx~|?h=ld=-GYBSfLp`nHx>_k1F((^kJ+XTFi1TcNV1%s{ znBZ;obSak^{!R9cCc|yj`7zr1jKv?=a)hTKUfCC&1c z>iT23YYpo6e`>h~xtrxr)*J6m#dlR%JHcqsaUjRz4pCMSk6thd=*gV*RY1>TFrl~W zX^8c_ZP!zV-zC5Vm(|l6+c=&7eBT;y`hV(aEAAh*eM}kuDW5>si>RXlX2RIOtWjN=1!J+mK6A&*1;4ENPH3LPS! zR)L9pQg7_5dq6NEpB$eeE}s^juW68nATKn>(IJl%kh7m5$YYQ{i_gxNgbyMY`oM%g ztEVfN!QW&~wdM9F41;k^)GODFvitW|fJuQ#SoOdbwsB0}ds@dneO^y#=H8J$<*w7X zElmFmec2c4vUBcLL+<{vo(|^zd#%ueItZF)&THVhSkL$MG@Wy_VlxrnDB>S{cgK>WSm1<@k>jAR>zU9T+%nL4C=>+s_+(6&3t(%1& zF*Y%$Gv{ouj~AAhcY{lSdy~uesL*G*?vH|tQ3GAX{q^RL+hWBwN7&}p4K%=G%pU|} z(Y1Af1L1wnW2YiGD@Q84;G4ff}iGiAfz2S&r+Qg9A;13k-SJ6h;6%f`A^fl2Py zU_AdB7L1rfvM%Qrh(SdIb@5p*{}7z;y&O!U$+VXejIb8~6ZJOG5nMj#oovUjmpyBr z?e&052O6l6xi8ha2Epj<4RkoyvD78%nA>OqTG^Xt z=a*pq)L^{Fn4VPDT{)Q0(GB$NZ(yfd%>hJ*&XDSEJ6j&zTU6+0SW+kz*~7Uk!8#KIY|*7$eKTxISyJ@59T^ z{~%97em(bH`vcY7R&$`E!{xFR$s(Ey*S$f^MaKTXG)EO zNv%cxus`k30hCbS*K`QrXFs8I+!RJ)%8LdF+@-2m2hrC#>tOAn;GiZ&enR8q} zj&-QNpD~syjPoVHP;@jgcWT{g!>f2`rZgW!^zH7eiD&GR&_n;tN+%^U5{ zB$@XI7eO8@X{2_rdF=`A$aUmV=C*)O%$;n{VJ=?^jDs5KX`WA(!XW2Z)1%sG3O+RjQa zv7H-@`B7BOY+yUPG}0*Rxk)f$oYB6+7_wU<9m4)Tsb)N2qUDXo83eBhM$~6FnAF~l z_Vepy{}0CX$42^)$GvZa9^p?MjH9xVhIp*@KAWp^!Hk26H#gFDJa@WTFv6epV3L7G zx|4J8T{Sa(J?d(9BOS)^`O404`_jRrf{n&GsjmOz{1N@O2CkdA_D1q^9CsF+@M!>y z``|{}!I~Q;!}j7}g7X^b6ZYF9^jOA|mEiQl5pN`r^BBP)PT70G?3d$vj=l z2%91X<|@IM1%tyx#Eq+I|yzToUk_tCOU%h z@p|Ulf)Vi?1rvX!k&fYY(aQhk?1?s&0;9cz{-5W7XA4Hya{h>ZYrK)pX6A7_!(}T6 z6MnOi_O!+k^&;1ygWzIsH5$*vtg!3SpsNRr1dAQ9JL8C?M36Z_=`7e35GY0iVd z3prgP=AB?dfAUZh+Y70gUNG@vJ+$1KPeRXPFp=XubRoy!7okVQJORdas)y#ox4axM z_ms2GtHFf9WS&Pj>&2YULQl!hu=NiQt>$`pRxqYNJa3x;Cftu0S#6o)asYDuA`hKw zm5VZk!6a==?s}ZNEVOnWI;4Daenq#@Xzzx!fzk1Rqx9Kg!MW^^nIuXr^7Q<-vCif2AA4 z2IPOS%EkLPtHA_U7iU6XlJMhks~)k3vky#ksfS*&GG-kOK^}UwV_CR2X4_aBe_tP^YbH$^*K?xF9w`~&L#MQ7$Y55*N_)9`;G$lV_mZzFwR zg3o)5vmkC3cEtER3?})4$;9nUCjVJaQqi{{&g{zs*;nMndt`O`n1`;{vqmT08A|Hp(hKpUDgu^6Z*_X zyKS+t2ro#(ti>4PHn6U5(H_|EtJQvMTcFSR!9zDP^ORsj+w_3ZCz@#o-rMn$n&}1; z`k~qW-gH@hFp*z8v=?84{O#pj&X~5iY;iF0ZJS7Ae|{EBX1?N|yiQpOF1bS!9Z)2e z8^E}xHPL$Z`}eQd>~Xv`0<(J)?Zoju*3Pg`9x(1bn~Zn&o-G*kr`f$+tZM$KalVnA@%d69*II>*p7REz9+s zV@vc?^-Xjc$7!cml`$v-6K!ZR-cMCwXE+94FiB4nJ;-_v5sZidb79ui++@!KaM^PU zm>UF_YH2dg3_P*0z67{LYm@Pw>&w)f27PPTKYx>P#_fHA6LFm~1^L(3M639Eb4<;+ z!GsQLq9kf*DbJykW5_aa z>Fy>PDKbx411A30Cfb~th}x$qTOpPwHqoh8pUk}Cvbn+NCpFRSoS(PY^|0TaVA4HJ zbS=m6eZgQ;NcIeP*0m5^G}1($GPlEPwm7k_7?{XF6J5pE<@i21=dt9=7&z@J_yx$h zkI)59zGo#3E_rPeZNZ#HpIpZ(-5U9KLlf=Ab$FQ2C-S8ljBB`wHsW>Wf2x@f810@W z``Im`-|GXT-`hkTyhe4O&?A^(FtI0^XsAfPzXD9~=_Y!J%Qi0cql1Y(*F@KIKjV7cR)@@VfzjV+qU*SxhwTiH z(fw@e{U*8;pY!U)oVyJ}9{!+-9^<^z-^ke!F=j^A zQ=92vZj)<-9>L55qwmyA%?P*iXTg|0a~>`P6a8Z|^{_v0{x4UqVE*34A#kn(n(0=y zxAB`fPL>}`qNbUy;Wji&%}js^wKda2Z0~%*n0~WQuI&(`&SrYcT7L8+vVWKf&T&XH zUBY?vn$RW6Hy=#&PtEi>^ycLVcH(6){J)03K5&uw&BptY@O?6uL_CMVq>pH}oXwY8 zH)K64z{HPirr*JsbCb7}aY}EoJ>_uI`^GZEI)-CXcmipw3|0mu70>x(%ysO@MKPo9O^+ zA1d<5H5LAx+HAb{^K!c$&XEc*p);DvXY=PmC(hzUB#nBV4=xVwDr`n}-Y0Ykdl4|f zE1T(B?mwShWver`Hv}eqQ?v1W``ov4jAe~(1vtko&Bl8Q76?w%Z5oU|)J%JGyvvJ>Zgpy}_K=#H2}a-0Y=4$W*qRR}@l&&9F9_PKm`_E(M1O8Bde2}8O!${(W4|=wW#oT; z+aCktaFqluQMfP)Q5z$9>;CH8T%PY^xUg zd30ucc+6i2CbD&l^4^06`C`@qx$RxP;=Wu0OnBQC<2)GrX0mS(*Fvkoq`d8SQmSDe7yx7W$IQC3Ts(!9Kge#7kSK)~XBNHT(r3PeYzLm+eV=Y|6m&fYE=~ zVm~v{a&5Mlbxm(6cn!<`B*3_T-$LKubDq!m&hiw?cW$Bi);Cs8om}VHKHBkH=&Nd>D6avy1t)wj z1LN=^#t5+UM!|@-?qz093%$S3Q2fJx13p*MMsGUfeT`7pPTXLK(Dm*{Sx zJ9tdkLvX^UrC`FRwHWt@>($IEFs^f3=y3LXP%xtXIDQ8`7ql4fIbSLmQMNKLseiUm zi0kEfHRA=NU({lsHwm8?fYB~)p;y=+#AC9p3R_Vyc#w_G{4JPwge?)r5ipUvkZQ1)=UeR0 zVw&x9HUEA^i~VdV5r>kU(5JuHLR)f7p?9(jCR2w0ngJ&KQVX46Ww00__uO=XOC?(@ z;}!3DHO?(zUA?U9y$mvqfA@Rj+;|J!z@PDFEj#Nd2NMT#vX#Mi4S!z9-LIjJIX0!Mb2%wuyZ}t_ z^%nYS`sJ#G5VlcT4lN$A!+xbyCO9$p2L>w6V2L z%|3$bstko_Z}u{h?7 z!9_l5Dfk@ODC9}VZRg^)^9yXw-&BK+6qxkKEwmh;o%l2AHh=Eq)DWvRE%s;3%{9GD z{e#gK`HXRYZ&ANc$#O5`A;?wtJT=I>Ay0nNLQh!ZY|c|y#{ifpbQF4aaS6*mD?DFX z0eKwqKU!^w{yq&RHDR3#8TXQTO^54e>i;1gpSLLQfn~PMfzj|+4KDOWi~U(hbMFo7 z3W3qSY$46+6E2jpYk|!5gVWRK1G%rp_sKSB$%#0)c!4-`43m(DziUy}1KUYM9)*0C z)gRmyK{tls!^qLnT~J@^TWDJahj+PU4gt`hPY0L&zJ{Ayr8(Qcf ze9mL=o#lOyyMI#4HOPk`4@3S6`*M)TJ&`+OV1mD>%gs90unx#=XWR-MQ+7o^xRICo zz~}r#TQU3E8IVUIUzjJ)-FIL+K`^e3z4T%MrVRhz111ILSt}#@Cgv7{i*Mqk*R8Q_ z%kRUMv0s~c?eAq5b+86ZlDzioBeQHJWr*LFUgL~L*tXWAhX0xYCJDxVc6#nQH3-JN zm6z@VpNk=4gl9Rkd&zsiq`_q7x!_c`2El0Cc@2NT~;5g#*tUdY|sduf`ru7ocOzyx>j(vh6UM~OTZOqA{H=vD4F z%dR<(fYD05#{RdH)p}NfNo1LSsF@95Lcj6S>9#hWt50or>7BgP zTtrVd7=356&M&e1!~MVj7{^sUnq{qXGsa6G4{N9cj&V$_Bgww(;w86L2fl0gOGB>h z>ZPx_Jfn6U%uL+_%pP82F7v)%W*g5;u&*=0=;iRmT4vMNd61{}^wRBMoj(X2qOKQ$ ziSFg49j!XV+&cy)zPHynixj#h+sa&HjOTu08JN%?y>zOzj@okTOw3KNPy2f5L43|@ zoA3!;&*XjaspYud-Oo!MfUI0L2WNtD9OO05l7PNRzs&Nnz4>5Lv%Jdv4f6h7AGm0h z*O;S}d}y0HvaVq;p=vK(YmJo{A2o1E_N&&6`;K;9TzBih#KEX?gymEBgx`?wX0v7N zqp5)0r6WF8xwr>94@|h;Ydi}Bos;n~#~Tg*wGfP>0sWVC9w_E3F)+y%FO691R^-t# zFi}6oExSLPa(!``)_~F4yflE%d3BoY|Mx;nAit7*DYM6o>tF_$;9M{L00ntEGHVZT z8jOa&PUhyB{eefVs~1e{FfVP&_T~u&6KeUc*ckhDq?eB1vYjkA5u;^bTp_Q0zoaks zjMEx0VKBCNn8?>Ddn1lVdyW0MD92=-i5%lNRDer#nK>pow4m(T3Ul+pg^%@8fPIe$ z-(fQ^|5@L0UaBil$E<4@jN=3^UB>GIR|;L0*eAh7`@HmJk#T4pn8-i9MD~1{e7{!d z4Cv`cOzli2r!@Ff4JLgN{AOD}2%khuLtxwkUgIn&{6@w!yWfFz^@EFC;-&v^47UEr z)(0@N1WfR9FFlOpbMC5Ul3-j{d+DFW$`2+s=p|lYaC+=|INqgyK;GSeIh9RM{@hCk zm%hnM{YB!`1txN{mlhPLUoKle>lyOWnOrt}pNx}e=S#qZ@AT5gMQkO(gzomz<$NDV z>JjTl>%chf_u8);VGAjYp}@EXnEprf84q~v=MRhaz*gT(v z9u2Yq*7Km3p5|xU@O?4{Vmyq4Nj~kR&x*ug986-Fm$reDJUt?o>sim3mmcLDkhTOf zeIK-e7txRLeINx>hX2#Sq*i+ARcqa5+GXynv~F;TRbHB5)g{U{04DvBm)5aQhuQN| zgS|L26JC0k^D?St#=#`N^wJ;Lr`y!bdN7W!y|kL|m&>vVf2O-(>u1b^*w#|D9vzHp zqgGnZww_lrU0_nqR{94s@2Z)8Frkvxg8Qzy>`T}d-o%?Z|LrTao+Oya)~&P|uR|9o z*E%rKX{`m{8O`-tx-WeGZ7c1^=cG$LnR-Q#@P8Wqba2|ftu$uqCvx-SE->l+Ta9NK zW__&e+xo%8D_dzBuKT|TM%2d=Fp1h$TFbW1Rx?Rv>Rajmxc%R*X4bJkEv@tw>shX5 zO7{cfYo#!+!Kc(rHJH$$t+WT%>;7wU2!|wg=%K{ z{%A)hw$g1}KIxONrGtr_+-g7HKrmfk(xS8m8-kZ1PT6=)NkU_w8(8gq69Q-=TR1(Vp&s#xd9 zY#^Du7L9>(|J+J(o)df}d=@cW1}3$o&%RDB@@Ea}nc_3f7xjOZYZoHUQw~DexA9Sk z*I>H@BW$_BBzN=~XHMTG7@?;VjJA`HKHyyaSTN$6r5B8AXCJNSa&0t`^GDcP%*-Er zbPvxNTI~$SF##rVkdLNv{+uZo^qaVjnjELrfJ;?_V_laDPS~3=6S>&pqwP3fe-?}= zn;VSQ;iKEx@0p(~bGj2u@+cpD!g;nN%OIzPtzIyRZXcb`evk?c9V~Gx7xvX4Szw%(~#SqK@>WAz=UHyT4L2<`mqS|=*>R+d$vp+ zY-cH$$Spqm`zAzOSAlWd>Z4bzc7!jEYV-?W_OvqiuJIq_t|6cOUbgVX14bM6*`JLS zXHs>8iG#V=YRBvoIj07|=y&>PE8AQ;b5RP5Mmre+7rfg??N(i8?v6w5SmL8&6><&# zw+`|s`T=v6&DZ>Wzix2u5%fV; zPWaOaCIRLED`VDA59Hc|K03RAd=ca^$W`}}x$hc*JPomJ7iI+Q+&)Kq{^C5S?(oyhz01F`xzS>dny#B?IM!a#DBxR7NArC`-pY`{Mlf3Ux+K8Ncv!m#HCaS?Cz>Fw#W$(L$ zz(n5aP`+n^{f$8Ge%xo@y9SwtzZm3U$ZhAFn)S-eGB9Z{AK`N@?z#CMGi$)4p77BN zMaG&b9^`1kN1Zl3#-cFmaf5L^rH)_r-e4z~BpBOsbf(W-54~W5&tU#(8|(AmTMz>m zebz^}SbY-LQp>>T|MA(+ixV+g!#;uWSoMfKPE(rDS1tD`&w0<}KjbOMu_TxuXMET2 z7vSHIVO%N@W7ZJ{6aKGHnGdWZ3V96jUF|w1pV1!ylYGus@Lm?{SqUcnJgyz_IWH$f z+%~YS6+Zj9+h*L@U#%IjeZfb&S@qyM%XP@ZkgNKCuICWsQOJL9Ef20)viE;jPd~W$ zi$421@o;tr%ZDL%zvQD+@HwwMkh7gJ{ypS>wdPY0`DE^KXFX|f+ABWAc_hZYT~`b0 z81jd$c0}ANz{Fm)UYF*twafz(9{17rRz2o5F#Eianb&;uN9+0`m~3Cf+z{Ja>9e1o zfqKf`ui>(-0O$ChkG{3q#C(DEOtAb-MP1-F1s(!Gn2b7|>P3v+GWYu7ca!5GK5F=@ zfII~GIo4Q<_BIbp>TRF>9e^UY7J`YrqquG`=f{g7cfaeS8g%YX*q!u4>~Bnf39t6i zO;$$uvKox*Q{)Cz!|&%o9{kxyYps5Yx!^)(ekp8YG2~ImXIgdOyN16}$di!&&nh=#!EsH2i97uE zv*L`76Qt4Sm9)W^jr??Ao-Xw1_|EYthdd2=ChpB*jnIs#mw&&p-@fltoU^$AjJAoN zF3humd=YIr3MK`{wyq|a5ik*_-~KLAkpnBiI5zbg`?WiSzh-&XL#{(!$G#kyFW2yY zrL*DdW_}8?9DkF2uc*IjFexytR>q9Ue8}CK`|V??>8FO@MZoAKe!AYOLoh>Z2aIh@ z7R(qJjr^ANzx*+I0!$ptvLd!za}e7t{l>nR6GU8utqL%yDSkSX$GwnN#tJwKyn#=y9y`t4^kntIr$2{0)zSK7wy%yYY$ zXTq498ic)Re%jpHf8a0|IhU#i7cKSE9#(zX`iDIATeJzQ9#OUknD}(ReV!_qAuyqx z{IsRhl8b^F0~7u~zkTn&U?#xCcJbT$MZvh*(av}E)8agzb9uz^sQ?q*4f&YIpdDN4 zc0Rb^9)9~=)0|VVt_T=cxnH@?7~t+0jE28qa4~R~;4}Z9(H`@kTUY_cy{BI}pO?DU zflGobcE(p}2jaPx-*}JAq`mw&RD((F?N{u@K|WOu!J~+2Wq906fAHRKl zSk%oDw)ZPc5=_`#T+cc%j(z?1d5Ex8Iu~^e=8C*{=dP)_?yAAK_w&=G);z$&jM@FP z%yofF?61yE$qj%@SNQGwN|9IDy%HMqjerXt=%;af&gDYRHy*E6vTq0ZEo(7g%(l56 z@(ARa^1Ytdr#P`Rm&v*5o#>lpn#{*)MhBAsv&z~Bh&gc=m{=9sl+|BzY~&dAgVC#h zy&c5CCBdy^d+YOjn%tK-4kliQeuu9Kw)!%6&(HLUsE}z)nFVge&bAY{AO~D6nPW^6ZH8h0I{X6Cf88=z(n5eubIKXWA7KbVx?Z=Bb@OSaB( z{SE6`0>%~e(@qGaC6<$Qp9JIX^jr2pcT4R-qJ?AUHgy*9_vfiT> zZ7~hTalGIDyr_Avj>|Q59^wP$XgHGR3(A!}&yBfiaQXtjapoDm%lt>b*204qb6wyZ ze?uRE&w2XLPO_W^*#Ni{xK}t%2WE31zwY8-5-0oVaO;|hXmjIWwA1`_fi>RZT7Es4 z_!)lVUGYA(t?7rM?f%_wJi`J1WcjjdojMrDS%sNQ`N2d`w;P}$ul#t9OJ3UzfQv)d za_cjIX4zO*oOPY=x8F|(BlncA1Q+b{8)sKZ-^JWx0~pso{q}W4)HBxRKpK5v+2LqY z{eF7D+7?92yc?5FhUO#<+-#GE7YUkx(qH#YR z#PK}QUdI}K766lc0R1687t#}EJtO{t_hH!YMPNb?`RU(QJ@8we?=T9^^{}6M3&cRf z?^A5=5%h)Dd@<{obIy4r+FYuG4z=1t&au1|=I9DB; zZZOFw{l*ysw+lwhrv|`;6Mp)ReH|5y7{B7oJng4bxE@{;jA+;6VA9X}X=7%7Rx|6t zIR5LWZ`kME)44gKh|Bb&kkbX?!sV`p+_A!M?2q@`bubeG6M3(Lj^{F+q-OfSq~GtL zYmqe0>($IKnCQnHbP+ROtCQ4JMxMu;0%y>)I7UnbvmL-zQeW^0Jv{DSH{1sRk2;j?8|gExxkFo0$-p*tZ?@6W9Mfc7}cJ z1Cv-^*q0&5ljDARiF5T}yAHN9#&+KD)5&b7%g(T!2{79C9m;(-tYgYCkpItbynD4* z=n!)TH<xL7cjeSh=7B~zHsvp>rPC)(*kFpl^Av_I>5%g(URF);c^emV}H zt$DycC)gjzpI{w7+I29q8cg^zKi!MZc{+qIC4WYJPvClRGcbpKt*px#VB%l-=?Cst z&a*RR&>v*xTR%mtb2!VG(+kf1v!71oI>ql)W6omM6_B8w~!OW%s8sj*B zEf_I}Sq&z#d4QI&o=w&%%U05b@=poS5nQ%|1ta?28DQeu1dOvodj%un7zE?kAwV;@ zT!U(+2aJ1KfF59f9uSQ1c@RwccL8c*<}1MnTccof4Yv3?zx5ksxl&+a$axpX=TtjW zh8Q}JMSkoSpt)@8X2DqQMRH*{>K1a%|1OPbP2zEzyvD-6yfW% z|NBsF`vwj*aJ11LyUtY9;_Ccu0Uw-vZ40 zcBTxm4}(b_6`*aoo$a+g7avRiIS4L&On`RH|rJI|2mZ_O?Fo}}6w37XPRm}vz#HP%p)0p{I&4j^d-2r-^%eDRYx$=v0En>zs zmu}>G?Xfc)$E9Fge?uR_^>MXeko(y+3JqKeTrwP>VXl)W1!v|Fk1@`_pdI}^pxi&m zI?5oAL7wL489ue^V8#n3dVYY;<#O%(L#|w+Pg?*+yC^^>vCkgCh@6hHo{Ixi%6g6$ zj2MGQz{I0y_w3KPg26SQTmx7IE-@G|?!P=KIAPCm0_3*6S@~;L|*@wTQe-fZ&P66@c?bj@!VVZZt2@g{)$*V7@+lex^RtyS1M%J zdS)>5aDWcs`a4wU5`G84XpaWyAnv!qYNiKFC}H-052=|!Fs^3<#@?V01taol6pTI= zprsr~+K{Uk(;v?56d1<~0p)WZ8sv@z&;j{0jt_s&zDH8D$1*UXR|52RX3Fe#H2lm9 zCb8{YdX(eWDj1<>0hq}4bE%h^^X&}RR}@U@jR4)kWy1GKe=Iq)6kK#wfXX>`cb>%JU}V~4qPJOJmIuqVnF02BVr zTw2b|+@EswA($|j=ozlN0`;{LOmLevda4Ms0gUEqqw99C=n?m&w3CoSyS3T- zA)bcboG0^lbmmZF>}4 zYX3Gm*;+>PT74Ddp^7&8ut>adc(5aQ5d5}YuZWy21LLS`qa%y-5neDMPaFM)ZQYnH zSN_`l0x*&0Hv1k`-DHDuigO0HK&cv z=Qi}Fn$b?iICN+m?aK3qDI4eVTIlhBNru{tzPd%tbc1npwb5+W6IL?=V1g&MQ75;v zXVgp_O#I|F<9$S*tC?}ub9$Tc&f965D1BZJMqAiMZnjmR4NX4v9+UP8fO`EVK@^k`B?Cv%?y@)MW5A-Z)qd9r=p!{}R0Ve$K zHadW>U&F$dDAznN@rT>&XTAt#A(-e=^as{*i8)RTjBB)w{>%MSwbPc%Whl!sFsWzS z=o^L4xWAU!*Q|k?VD8yAD(AC~d+fTHah(Qxuc7=dFqf*C3NWD$+Z6j=a&yIbV1ggF z(Vm=#uL(V}{9q!Vv{}w2&W#1^PYg`*(>D5n^K7+UPp16Lq}z=1B)8o(7bDC3d;(l- zZJYghX=84Ms2l6~Q%}d3_Z9j)wpk|h3EyXeNq>jD<(Qu<7?E%D!6d(LqkA|8x2l;4 zn8=1UTEMoJtC=CTwb5+akFP(z%QE?K8Uy3rYBoK?<7(ArxflqaC%`zS&Zgz;&+%%; zbq0LiaW?&z+w9YJhT~lUCZ^4%tGTTmwYjjBe>T*7aQbeui;k@kFzFey=~iw77f4;X zTpD6~?%A|E+xxd*M6AcaBr9gqp4?7e%`%9QU?#vgs%KLx+uFP&=eJ;7y~x|T*>nq+ z|3EvFDL)uT>ulrPx_=19vW77qTypko`h@+yQgFgv1WaP?Y}$^`+8DMoY;Oom>gd^) z_rG$Vl-p;<+zN2fV`h_M59r!*i(L6S^XL2Pn7e4UeQqe`A*E-cjsI&lz0dh{lF%i5 zuLcvmd^RoQ`NI`iCO_sOFyYoN8nccQqU>W}LLbhicD6O3 z)-wSn@fGrjbNg;J<2noeY?w`9E?0rNr~s4NWRCGZsshYBF!4?2(5GzcDYdPIU}D?M zp&fQ9Y%2yPvF{vXUE*z_N0faTGyBh>L#7qhvj&XjpF?dN!?kKXQx>A^v**w&T=w5h z$<>#z^1p|(|WHf#;dp?L+`Aonjbz=W=tLw@#o+E%&vVEoLO z1Eb-u6I}e-IaI>-_7j}&sTWLQXbz3@xc3jih*&QM6T52;9movys``ZlnAE*$^7{g@za1&8x|#(0-K)29q&83h+SE=U8m{vPjq6weH;2A4WMNLzEAoGWY! z-%BDW-w8ou|5i-R%mAbPEl5+i-V%ZljVEzjq+b>-qM_A;ixBrgflbnc7Xg2785u$ID8 z-pn4wl5_47;#Hq&aAy6ou1+xV=Yz(YBEC=dTb4E4h2Rn|1*x3R zW;$NzGV_vk#lWQB3R>O=48Nc&yBCVNF>tQ;OkEb6*}e13rNQZ+2aWs67A|{_g1OT3 z5&N}4I*j9C;iNttT>85p9mv;|lFP&mhZ*DlH2iggb8p^G7w!XYv+Z)ZD%#)xn8bnY zl(w!%3MLLF9%wht`d%gUn0dp#kF%{H7&w&YlO_HeSXXB|mE^V6{M^y{;M2kF#@+$w zHtI5;@qmdQ-cEnX)002P?FQqR-%c@}pKZ3iy8K{be`%*{xDC!1jA#>aF#3t@^apMe zlm6t}8V3_TshyT{-JLD;h&ZliJ*T(RyLmq6muvb3=m0x|b8v$&m(=kh&ax2NHE3&4bLYNrRe zFMh|)aQUKO+;_E;i^tjNQ*(J@T-4WndDIwUg5tCoC@Eee#pf%>}?EN89PN?O^X-sVlPxl}QGULl4`0 zsh!%23zLi}Uy6Ntx1BcQ7$}%B{GYQQ^MeoC zsn*IM=GlAc%*_C&t!cOKNr64N_qY>W65Iux3m=F$$ng(MXri6oXZw?W<#Jua|1Aa+ z{JhK z@)=nltHDL5cUazilOM;Df5GqHchD5p1>KYRChBYk827GVxDR*is4QC$jPCBBH+f9o zUNE9;J#4E6=PhyH)Fl`*M!ZHp2qx9kVL$ILyZ;k%S96E)-pA!ahq;EolI1NObO)E` z9U&KfYygu8bWn`va$8JOmPfl7d3r<#ZNv4ji(rHv514QW&p%BC<5x4?VB$R;bShtC z+^%K@z_`yxo$FwBD9!mJY{kK7%Q~o${rONZB6j0oLXUUS9}&mAy2N)4f9oK3?Kju* ztULPs*4+HNbO1R6=4Y!8kq_0(>_6AOw;*7=cY$*>1SSc_cCH-CgjZlqKHu68E?6;_ z4z>2N<~#G)?h3>m>zW0NPR?W3P2W;m1FywmmT;(1m{{0x_!6!Nk z%GW?1D)Rf1OVQsHk(WargM3$O%gKVjz|AJ>@u@xj!$j$lv} z!9`F6ql#L^prr^#^J|isBsa+EPS?F)s@@VW=alZ|+ zjl-*z&p69JFNbZ7a4bhu+n?iPp4aDi*Mjl-s*UTV@qK*0GxI(92K3pH)g{|A9eNsi zsb|gwpl6|<1=B@i5cat(^PoqM!Zmk1j-`LGU!&pw3HCFqnr^q|#TIHpZ&L zmb{N^l({NJjfriOCysswa{}xZ#U*Nf8kj7YZLQl1F;4}+c;-~oz7A`g`CxoC)wELy zhWjZ2Mz2%nS%aQ}9)rFmf-Q>IjD>Bi29s!1%t2;fxNpRq8mv~n`z(LG1^cQ57e|d> zkI(KMw=K-i;@E~COaiuS>r#{9czW1QsJi6)hZaIlLEn@8;Llo{OQC0>pJdhXo$Zf6 z_cc}1oD%vN^aS+dOX!nsf_(>l8gyTC>EG8tk2~l+&@<34u=+=TX!zek=z2>vO}Ey( z*v2gblhAQJuGI#nIrQ zFEw-vnr^oG%gw{NS54-&kG(qjYrNPd%VU1iz%d$Dltaub(^ ze}}-Oz@28bC)*FkdsDUjStKX(xJA{%4%-@FTi|lXSN<#7XWC+Bm~GusZCu~6`F4fv zlez9;CJV-YYc>7E=Zh)?BkWA-2Xl8d-NeVe0)i3S_vv7=_nUTZ5sa`CV&>s$zK_pam`hOFh4zEXEUl)^tmnDJdk}}&-*bqGZG9~KiDTI8bBwtc zs%dXtzihsJAy3%YBmCTR3p8-$w_(m1!gh##9xS=SyyOGpd9lWL4`fI%A`f9O@t10- zhU?%mHM0Or_GOcKK+Oz-(ch@14z}}#nn{C651Y()YGw?K{}nUlEz1k-H)G~`Wb*B> z^J-1Wdj+RM_x-J!hB(*Lg$=QP7y{#YtD0(TeOt(13{3PLa~*z)up{!f1Wa}W^E%h# zTs5--jQ^u*+GZm#gMtw?JIcORSJS5s@p$e)j=!v?Z`sbzYCF@w`2Si%yE)hiF!S$f z+JoQMcgPNf9E*77gNbciW1mMv+Y?~CWi@mN*Wh!)j$nqsM7OM=6`Y@po#C2a1ID)< z_RqNH_ujD(k2uE1HSbxB_U}|fG2XWO1SkAW1C!aM#&`xtwVDZl@$FtigM6$Pb_?UW z(D(DfWW6;snU5893Ogc42{4|+YRJvUif$E*84tH@2#hwXhUReF4%(^Ew%oXfF)$kb zM#067uc6KGuv&M$;DkTVofxZgYv?F$|ACrmtN2*iG%%U&8Vc}nBY&RB9X}aguLQvO zF0P?-`S|U1YGyu|)Pfp%a2GHMHIo1n{c{by$+h;gU_^fmv9CvJ=u{-yz0uBv_6ueW znB?O%G?Vil)T;^{zbgY#V`_JyXF~-t0_UE9WE{gX5 z!1>>*p`ER^MC^VrvA1hzr32FgMjxrMzcbLZ!|m$_<6Bun6S&`>7X4*eW2L}(cCIy^ zV_#=A=Yt!660WL9Ckat*n6Db$%5KYlQ-&ueHY z$Gw}K;kNaFNqt#E(|Nqk7K~-QE@oT*s-Ymy59bI@_)CG&f2yH{?C&8pvzqO!t1;f| z@}^*9`|knsD}3>>?au@wd{u(cH?DQOXWI`Zvq>$j&vU~GyDD?k117seE$zs2jH1tY zob`iA?pjMd_*}$boN*49LHAbFs@~x`zCXZR2AprV-{&UYiyGOz)_6_ZQ7}VuJ2o` zyp}(2hx?=loNvEc#eR719ByvW&)i~g8E~6$ZR7iRpCCiC&Fgz|?FZ+dT5G?r261Wl z*=p!%=+EPGksWc|YT^=%{{w36$NL3S$;`A`T4bHu1mg$eJ*bv;DpA`UPY)RXA+=P8 zV2a`qW33-d@~~Q3!pyy59EmZz9E^Tct>bmP8II>@XU2U$>StzYhI2dxOd8DPMLERr z9^{zy+0gY_wRBgJjx~X~Umal|$JE-NCup{ZZS;ZBj;*yn@7S!bLG}TCzBT`_kv|4d z0Xrk$GP7%GH(MVRu6feHtpn$&!W_fgC+Zw)51`LaHkmCd3iFv6H~X6j##3Eu?03!* zjAcIW2IsA*r5iZ@8o`M^je|+mB9FXn=u$IF!RWzSx{cSwcjXzZ(aiecm{x*uwWC&f zP5f-0;r=LGH^ez`J&5(n+*<15G4`(D#C}W#7*BUC{e|=NvtUFHW`jweSxdKC$Esy} z9R(LXyOyf0oUpZ!ZS~X|$9g!%{5_e7J%5dohJP;y=Q_WZe&Dz^+s)QD%w(9k2(`d@ z*j3HA2hcB<)zZ%nYxOD2^wrYCyzQT^wo?Trv#{29?<#zc$1VCd3MO@HE&ay*d#tb{ z#{NQP?yRMIxqq)$Gt0n4@2{m6Zu>K8W)+z9KrNll=Ui8*8P}gM7e8G~uW^2k-@TAS z5l;me&kMD5JzMhl*702h+C%75FpqPd8`XAZf=T?NmOkc~uT?Vi~d+^yf1W};KX?E1(RB5&Yzo4 zDfAU=$>WZL;4;6|(s~@5R?NwHVFX4_8EY_Y5V5Ua zXWaXJjE#dugPRR5vtga_PU)cF#Qev3i!!%S9o=J%P0Y*8^@EFVTxXm+iCXRD!pmK^ zn__!qb;dEgXz- zFDDC5Y<~h^Tzl8idp!Oc1Y_yb7`V*zI(mS|&RW6c@>Xc)0J!*}b@U47t!yuAY&lRv z-qPUo!|G@nfqKOeaC zadp(g{$hd?{=#7V)pfLx=h%nr4EtLECfiVFJTq>jn3-kX!^vEd<7%v<-|*S}h2TVg zGq;ktpxMSB1Sj&!bB60t^ixxvaUIdby$g9Y*IXLw&2`3D+Ft1Jm%sj4gPGajvMqIV z9d94Kf)h2-4JH<;Gw$I!T`(3~3&EvMtE1=lhONs5XZ8>KTgJ9-t)rtj?~%$v+@hXW zfk`Lo=waTDJ}emclh^gRldv^VM=!ChcLgVM#Xc*+`5(s^XRdr7n@?u^%q*`f`OZ=9 zn;tOU=j-T1UIRpg9gzp-7IR!L)zMt`w?NIY&mnNxH|l69b7O)NHMj;$^4&W6J2T#W z3$csseA#30_emXTZZPKxM%eL!N&K^pp5p!cTLdHexdx0rR!0Z%*#AHy2ma!LD3)<;M=!`Y8L`roQC%vFvg5-Q;@3 z`yP$=0CAsq!6d*O#A8|dLfi6lBy$09nH}qC$U4vCTom^4ku77p5d)Xh>M4fb6xqw2 zOUoS!$ZgvPz>gfP$4g{wm-d=Et-Ro(DeO|1NYXD4a&w6S@lH3ofnPD*5 zed>+pXTKvDQR7)KX|HMLN5P0ZOq2~i~}89CQwgtJ`eD*u$7xzbHl); z!QKLJ(QrMbc&^@m|3c11-wlHCcGuG$oXe=1NrTbPtf%XFZoNw|qVLAQWP9rAdp@qZ zR4}=i@G>~W!!eaV1@1EBlVkr#aH3y)V0_orlZX2Szp?f&&pTl-{u}D)6nrjXu#MI5 zzh3A`=()ZvpIT_U@G}4=c2hm=?@&j>U{Z_f=^d*dVJ8bFaZ5ev*8Ugyne;Ta`Th0u z8r#`l#Dm(-ug5v=8Q{|3)}m4FqXj4Y>CD_&Z(NJmp=Nr)#P6y%?!7wS&TzdAfbra4 zPhnoaE)k4~iMcem*wT9Hw&uw)F4uy~K3#7-(+j_|_8Z$OUxu1|zTSAZ-Y2Df&IIRq zxt`{5J^inkliP%Dw)gjX>fw6Y>wrQ(BR0t`1eg7=o@R3mHxzUEH5bRX9GovxZ#;AA z48i5<7@Yuyxz*rYtLy0{uF>lSCvw7E*)!;?FY4(6<{m8O^6_clrh)T+RZm;+_B|yy zF;)X$qCeJC7q?;3v_e}&ZOjLg`Cq;9yvQTfOahEPAwWT1SJtSRAuy>;0@TjFZc{UB zz<9R|(0e>zJ*8&Ko<%=y7f@`k3g>LSVEo$$=+AE0`BrVG1`IZ|w2X>u$>;Y zok}qJw1BZTxLeKm!K4lfP&eo25jE2T#(QXheC+FaHPg@W93C*9r-gP}$0xUaIheRF zK!0UBtJHQfVDy;*TFuOV)r@;N`gC@Hw&R$m9;EEADPW>i0lJmiEw3O6Yo1pyc)DYoW)W zzhwQrSrZj(=XvC%IbeTAmYAWp^eI({N4cr(w*W5tK_a-tk`33YPm^-Z7NztYm zU^K0Pz5(xkU-%Q_N(U1=EkJWPH(v-w*y&|oX9SGrKy7$%q5e#U+cp43KRZCZY-fsK z#9YsDr@>{<4bZ)ugBgMobMqLO#5~Ml%+v}-#5DOY==+OM@9eKfFs2=jX$F|=B>{SY zbJQ;wv9@C_3@#Z9(7`-*p0#pt3P!_UAKSY&KyUF}|B2v4>`5@`n=!X?T$R%cxf09> znCPtmdWadnnpq1*yCXo~@Em=rnki3VdvH&H{*Kt(_#3Zl(Z4=08891iE^p4;DZXAN z3?_RY#v+Wk@5?hd7bC{n0`@f!pa}0z3<(Ccn?F{eA%rBj*kh7-44tnCw`9E@WRX3P#u&1QY!wKwI(gf{hO?_!2RvIiAfM=mF04 zu$mbI@Lj`S7P@ar19ez+aevRGA;bdaS}S9o=jS-4 zgUKL{QuliWp!@c0P;P(u_w%6Z&_A~Nz;_LQ{rvmA8XV6}EeE6R-9T?xZHRjYGhnh{ z9w=gFjn6&o%l!&+R@p!&@H~Ek$f+1tQ@~{RX`ro{X%>vgNfj71zmU0yP?M%^gieb2YnEF%0VB7o^jAep}W>PwEs1<-$Ad0?sL$qpz98L zH}sf;-UmJ5pbtV%Iq1XCGYa64L#U=b%?X*B$h3=rISq4|>8uAB3KA(1)RC9Q0A>u5}LWe-rI@&?}+)9P}#a zx`W;gJ?5bIK~Fg7gV0kB`Y`m2gFXt~^@~IM|AzKE=#|iY4tf=I-9hh$9&^z9peG#k zLFg$5eHePiK_7+g`qiQRe@FWr^h)SH2fYfq?x1%=k2&al&=U^&AoP@jJ`6qMppQa# z{pQgAG}`Z=S3>tW=vB~l2fZ75%t7yio^a3yp{E@5VdxnLeH6OO6)d?vFMA8^chD=L z`yBKt=(>a64L# z3_atZk3x6-pF{gsqWunfC3K&IUIkru(7U0>9P~cu2?u=;ddfi`hMsZIN1?kWI<)@- zwBJFmgzj_DtDx%+dN=f#gWd-{;h+yfPdVtr&@&GDD0J6G4(a64L#_~CIl&q*>8TC&`+H44J4-NxsYr60?+ zCn<25gBmQ)AuF6)=W}hV!6XlEP~IQRzT6p{>z&>}|K-mme6gKM<_>9~1FRg{#dfNo zN1=aX)sdV0b?6$j9&q{}8kFzl%-?^~4<hCiL{R5j3JHamrDDto~sPiiopD~aEX$1L(V0LFK612sdowkw}U&Uc#Q0jGKf z9+C~zxF&WjIG6wTIc+s+37lUMKZaPY7&Q2u4K9s5DE#KG{bQyZjCV6%1@94<^%M z`g_Xmk7HWNjNU*`!mc~5X4Zj8gd2=|?Ke1FjDtdLYF{9pQyb_qZX5o_Yg5EC6HFRR zm}A~k*bz*G?VN5h$E%q>FmcpJi{00F4JE;3e}@?X1(TWIKyNeiPdme7y#`DwhHJw3TD}dAD2yv{T`0#rADsW12Ks>S`Po)* zW=w1=!7<&~K>eJ*V+CWmE^-C9^vw;%HTNBY6YU!X6TPj$cpv9AYR2<5)*g!+sEONm zkD8eVCU$oN&EGTtCd5=Ns610rMuuy|Oen9b7bpe6zhT1t-?$ zY)=R0dAWfeWqa!%S;zx8c`wTXaH*jN+Jw2y1t;D=#rBfm(yuho1g@=J1&5~wb(qft zVs0h4%$vB*kK;Q;aH4LQbB!Xu=?1!--#K)$;6$x5Hw9egbBqmqb`JE+D5H& zyc54gpTN(7oX4Z=u`p8!Cc9lD?ZV^i6v2r3$`3}{vC(*@(7A#^q;kJ)9=OELjYQnH zgMtH;+jWCKjynO)=MZ-adUC%;+M4@gyP38ea@?!Iq^CC;*WWh@M#MewJLKo+M&mu3 z3BjPgZpd9V;hb8+ed^g(XaEs=v9sM3Fq=_!N6bsUb9^L!TJ1+ zG=g@xe-RwMbK6qTwNo1DNUoucXQ|r{CR*KS%xT-HnTc6!XKEYiJ-#1cA2m}6#uIF$ zjjZFtY!}Dl2ctDL(o;NNAFsC4111x0q)*w`)oP|6O#0MD`kd#vThz>QFsXAI=>Q&M z>jWdlSO$#ivPL?exBFekDD&g~H^%DKX6-LgGgH8%7BtdfZ08BVnElRkLlqd;4UIIB z^YgA?cwWoBM$qW99&nx;8)+)vpYWaF%sg_x_k)R+sC(9zK~I#>HRvm$`|oI^9{{_L zKeo_rGdFBw9hh_p8?2ZA2kn`xo-= zbo}d}`yKT1?@<>H`V8nv2R#Hm7d7;XB_kdbk7c@<4ZyJIp`VaQ3u`iBl_P#*Py2zbRTrrj-}(% zp?e+l7!7>-i~irKbo?50zk}|B9&^xj=t&1X20i1TC!l+@ z(($FB`yBKP^r(aGT8sX7&^7332i*tVwR7qCbm(3OJqBHO&=b((4tffD%0bUS&pPO? zpV0ril#XA6?sw3A&|?m|4n0{y=Q%zGJ^OkiE#`G#?QGk2nwceF;(u$Tqj|k_ftpzX zCi6}seXuc@ht$j{nA9hY^uU&2HaO1ai|1?4&xi-{+V0CT&u@5H-!*7IRxR!ys{s?M z2vR5P742)8?}g(w&0{-Yrt&&EZjXhT#bCU<2kr0Z5q45w5@4Kstp=lA8>EfdS5o*A z@5Pw-Klu7%kdoGQy;$2;g30s+>2oy7y;9f_j310=kDzg%clq&!91ErgO!~$k9nO7@ zc8>3}3Z@@S5_WRWMDeQaEC&U^M(qT!(q2 zDo6{t{lh2P;$~ZwU@|8LmCrxVp9^5F2At>Q-{<1nxY*~((^n!~v2Wbk7xmyJ%_G1RXB-(=X0oTmUf)O<{3?|kYG@eQH zl$yzc@thu{dbYD#%}n|g@kC*V8PCau90*_2!MJ*YG^;2F#rJ)O!1&J%8s`Tq?RGdv zF)-TsLE4JvHlJWbE;Voi;C%Cg^f2##>4Fn64YR*1f;5Noc$r|#zT^2V3nqDWu;e|* zp5IVwC3KF%3*EaQNNc#=_X;0o9PFb8jAv2M*k{N0@%k|DEAC+*(2L(k%jbxV=d5Y? zNk5qAEsD8MFw4QXZVl2Y;L+rQA921q111S(KPzL-=PuV4F1_D*+*E*Z-4>*P)rL4f zIh*Z(Nm&^_Ice_ua&DsFwA&T!GUJIu_bv|7b2dB1F+aAklx^G@G@j#z@8kK!N3N+6 zj^(Z(?NwBp$gdc8Y-b&~_}xMKJ=chZ_44($aK#cqO5k%*e$7~CK-caG(vVdbHK~Ki zf@y?-A{&C~1>=7pNH=nA!`66v1T(;P27|OMx9LfB-3^2BJc4biH6DED_KdP0=(cU8 z+1|4Cw{WGPH(B#$>Xp#7WYGRDN>i`m-#->4Jf5Jq9j5oQ~X}T z{|4Fr<3ang+sr&wfJuW%Jb`@Mj!|9Z#*@wXG;6L#)`HWY3evmyUX*i7jc6Nef#*Xj zVnUnQpAUKz`mh1t$)LHj!)j04!?@7wBQ zTi~uQ(SEj-1f#tkw7<)N^DW*Tu@YS3cQ_69ToYjLjbO?5(Xh`7W`;37@wvE-h4U56 zRe?)^vtR#&W7@g!%;SIHl5Ym-4<&NXw)(+j{~lDnqd0#YT?3b5Tj`+veSc!^SPdrr zR*;js&u8LTyP>C{ACAxN5#bBpS?}ZDuL|1VRfM@lgFXm7`7zd$wm1r7HVwx0iP@fv z@FCX9V_>3Sj&`s!c_L~#6Qom_85MR!UCjXF{S50EW`46X+$J4N@}F3{<8#s2!FSeU z(9;fj0(us@>Nz7kPEyc4tAq5E)xL2aCAWj4L0iLqz~y4a_whN=e4Y(6WgB7M{32+7 z?ui&DUN9LjwTiKuzs8Z<6#(b^${IJ`7Xpoa&4aE(zZ9R{oB9iDY|&4P*%z2rW_A&b z*w;>h@qcYHQ`O9BFsVFql$x1H=*w?RJ9E@bC748>X;U+PFrHD<&e>|F2TTl1hTC;{ z5rdI$Jb%0&jQ+16?WxFf{#_>;exCvt{|?uea1G-7c)yx`#(l9GjO*V)ItHJM7;_u$ z-WWNA{)kl}*up&lKn>=vyi3F~5GR0+ZP|WZy3_{cv8QU=n2^ zF|Z{mf#WM92@i3Uw6qT*QKJ5bbw`a)yEH$&v2cRdR=iUqRzKB^IBN}EKl_BLj z;qo<_1*7j1qH>42ndCuDctaH9TK`V%YdRR$0U^4AnJwlN#(Gy@z$8xz(a}h(dyU$TYYX&aU5M`Fc2TvmA1lBlf+6~qnVEtS z@yuqXHAGKvJ#-32w5uD89u83j$9#jG;r@t&iFaYU!Ts@~U~=2y+;T3r|Go@dbT0hy z`Hr-egHzbj@RtGSJvT&uWerV4cg zSNZ);4gdCo%Ye%r)2}DPS_sq3`gy;0wpp@Lj{-Z0Mfl zA^UY|<~+zYy1}Ht?19fJ8-38T(0}0LU`L5KMDCKzyd0tz`T8Q*86VGr8DT$fgy=K? zrI@u~yy=kR`JVEv(B`)=R;}@xzAEs4ADGnJA$r`#6!r}@Xc2I+cS7_b*M7aaZGB+$ z_c0gpIO|q3Nw%{pM14FyU{~3GFrI&e?9WCPIa&+G^+|{pvz_bJzRI`8n9hWppZ^Lb z`e}%+byydL!MIk3=#Sj4dxS4h%L~B7zcRNKs{|ur9t7hV4bc+r`~Rq!G?>))=nuXo zZ0kB(EpuOtfr+ixMCWtN#|TFB>*Q?^v%86s;EVH|JIDmm-2Q`0f_t0u*lM@M{=#75 z8#d8P%v@+^xQ-Tp(I+<1BCd}`g0XCek{nlA6J5u#|3z>jPb2Ja(-S`<{YX z3nsQ%6Wz;s8qG5p&!SJtCu5B4&_qv{fhn&qAlqqcqIcNN9)b~e(qPha5i?&8daPiCoiQ-+bDHRJ zo?CkaBkWAx7TecLn&=>|**n$D3^4I4n&{Qtz&s%s;Y$aj|EbA1pNM`|jn`f<+QUus zigjCVp10EQvjH&KWRvmU$RisH`4KS>v#%$b=r^vj8a0z;=E)|yfZNrsW+rWidGFaK z;~dPTYGyi^#B)vbA=|lC&4j?jUTm^I7hS|01Ct$UqA$2lWqw2smvB69G#UHAk`Z=R zfbqPEI^^}h!y+Ej4sU-(nfaiJ=HPRYFLRAuwms%D=wEaGO8D`D(LZV;jn{ZPG#2_n z#9IR<_Rl8Tm)q2!X6AwMe%(Z;!;$+2J5vGwi@_wm#Td25i(>@&YrmNr0_Xqk_qkDU zu51%MWVL53V318?PMcJYISt&=n<2JG?XhY2{d6#RF&h2A^U&mAArGQYLdx} z$QC#Io3sPkzkf4*h|g~P6~=5K?;8F+16+J+GkwJKNTaYN{OMq#hc(mAoToyM;*R+J ztY*5xs*CkoB^ci^&GzSM<2M{b729yoyP<2xHdC9`2flOO`=H06+s{)J-gm8mTgrBh zYo-My#wo|L5=`cJ^|?m2u@<`Pgl0-w=N(bMT};{)TJ(@bfe z2l2g7D}`h7VKA;hGo1-p#c3dS-{mV@&?)J*&F zczRiImNC2C9)Wajq^fTLfTi6mgtz_nLY&*G)V|IpP_k+=%Y&Px@ z+dwbm-4gpeaQckb^y1ZSzQnc#fx7Mj6%dq{91 zw-GS0jauk*j%~!waQuB>vRkxJ7N6bt8=prk{+5EvY}rCJyzbh#y^u38hE{S+JGRiC z%p8$tun#P9p|OuWkc%lT#<~3q^IUQ5RDenC*+QG+Ahr8C!HD+F29r6mh2G)1*f?B> zNif}P=foD;8I5z_t7hV0vZu7rG+ui@ZD)8pvJ_0Tt)=8UM@FD)?JdUjvfRcaH=TIp z`rM{MO>w^0f%Ao1=x2N`%C}`*T`>h*M+@D^V|9;?LfgdnnhhosX`vQo4ze@subb_3 zw;1;!&Jv8#zt}n%^ZP<@t~1alJa%dXC;Tk~<3A6!cs%?`&8!0BxwwUP;+U4H8P}c| zGcnlV+Wo7VsQ{C`vV|_?aqym+nGGhopv5>A`?;OTwI58puZ50aJIKd)zli!~ZXvkz z^)2)vw{1+?D(rvc)<576R9{9G{E*6|OUN?S))Fgt)CSiCkAOb1QNL zBko;03;l1_Hpe_0OzMsn+LUvR@2a+G_;(as`mPrGm2Dkkx5dmtX6|dDDct_Kf-z&_ zwl4#t4T8bvB7gYK`by~j61oO`E%X@lCVVdXy?Jd#1^!>YH|po%7COwj-4ydA+nNc^ z|40kXvDy;n03%?MVD7gv@HgwK4P0!e4~+h3i~aq}!cLOyfElyf8NYU7gqdWE@hl~9 z<9(5fJ2%X7Ia4olvZo7wH^!EVUc)G>@JSH>N6>MW!3teFCD`8_4O!Ao)+Sb}$eCL`e+Xr6xWwd-v`|b{h*?p6mLJnV4^Rz z&}wU(qHh*}(O+&+zWb2#IsiQmeFJM=`FydxvX#<0dou3-y1D7mAQvXb4hU8-%alI z(%edL`rDY-+1}TsIXra86?@m@sH-ryi|ZUXzA4}mD_e|p{a%tQ9G~F)`@y9@LOtNK z8-L?-5Ax4?6uM`XqGru87Uy_SuNPW>Z_dkXV<{NdkGlO?W2TQ0=yCWs+G+#eHT;c1 z&p^M{s+;x5%;f!1vmdw6^|pE|>{HADtHwHbKX`yn~ zgF+XvECG}L)MU<3Gb_MkKWm|LxxX({GoxUlU$oHY%-kXvu^snJMcsVeVjLHGUNDw5 z+H`Q*H<84v!!(D&29q&CoLM~?e6wVe={%mkh0 zF>|S4M2=!$^octCgPD5-BaYQ90pr_PH}==izVR_7*7F+vJ0tCdbsFLv4G3EzM{jod5bvFv89fw(|#_ z4(9f6eQKdUM8B>86P=;cNnF=^+nHSZ!K8dT)$%_0F@nL^&L6wbkf%upVQe3-(`#Hu zR|?LIiET{>lRa6dNxYBtxL_>n937k|fPP`0p9oI0uNO?ZRX3i?TX9;UePZ1^07h>! znW<`Km}AN_J~fjC6Kgl^oTO$Z9gLX6CKFOK)4?RboW*m*se%!G5(1-jqW+ODbc?cX zV_=eC)^HtNBJ7C#EMZ@#>huBk?LsxP0!-{oo%Uxt%LF6h83mI%+nm?lS2LdJ$l*CU zZOwiEwVmOh3~a3Ohb9(M1^J z%v>rM(SKnuvBf%l$?@DH7-44t+qp-l2l;r_(}EFp2Ek~9Ivv6>uM~{1lLn(ds?$^+ zZ$GJ-F))c`IvvI_A9Z@6UBcJoKVVz(7u~r3L*`oengPcDqE6>HR^^48-7|d!Z*v=-dWB^c3^~ z-sYw4ZQ%U&fQh04_X&`z1R#o+V_t@Ll}c3<=Z=VXYviLF#`wP&ueS3}QIE3IeE zzo_MjhhZ&WR+{0sE5Ud+X{B$hbB3iKYuMK2t@Nj&xQmYy%mb6$vXw^I)))CafyvE{ z7>Mwv;V%I$zD+Alu+Ft2_8~Bd@>W{F{^-oY*bvMbFy5V7jeEy-voqYjvcu8$S}SR` zJQa>Ra!k{}C3bG5-|*Reh}u?wnO$0q?IXCtnys+EuHoM?a9Twxt;1(`mE9IIOPJZM zl}_ilTI>wRz5+~q_g4G8+?M_EEV$Snt#n3F-{5$Ks44b2`3Tg^o~`r%=lvYvQ_PVw zz$Er-rDM?D?g7Dwyz6Y|NYpy_^Ul#i--zu@FPO}%R{A^7$1??E`r`HtfJvU%N?mR+ z7YRmeOF8Z|IBiZVZO!{-&kIh(JqE_7x6(o$qX(Z=h)FP$eTb>Em4-N`M!}eEpbu<7bI@C&=Oo5$y)pmx##70}`B?miMF#c>S zea_c(zSpCSdD2m6*DtO1{U7tVBKQ4tFj+9g_s`?K>S8X;9sdBA+NI5SUp;;|J`b4N zqZsteub8_ap|p#14PO(`v{H+1wEo-AMxCUPm zjOfp@qcL{2X`?f_uQofc;LBvVJzi#ZZ7UhCA9@PrKc z0_QurjRyGo;|~QV`gJv!tiO$R=l1P$exYAQ>=S39U+dfGHC{U&sb(s{WLw&38qXVN zsTn^Q@7y+WGxM05=>g+AyN#}5U!SU(elY0^+vrh_xqO~2X0FBMV3L=qYmxO;(DlpP zN*>!^2R#P8)V}vo_^y)U_`NT8T%rKbb0_2{3RjZjS810ER<5=3+YG%^0XxFuEG{)m|p_-Y_%=ISo zoSF$Sb5on+bLnGXvJ2a27WeaKYCB87`0i+<3EZv&FDTT4m{V4O@!#1-*YS9nD;P7+ zJf=p$WbSRFliAKof)Qhnb3JJ`YWRNB*1rWO`fNIw)I)9bEc-kD!a_`f34w_vG52%7 zUZrMYVA7A7%tLmD>o|nt*4i^|6osAf?T6`y?;D&CJ^pN4?!F>J$J~Ygv(^tS1B5&Z*BHx zYlyyC3RHhL9&(Y8S_<;NqwberR|K73$&Z?(}0Ry$^Gat^~_vS4Od8FLNL3qAUF z>3M7kbnTrsnq&PvzH9g!f*yl@g;mEnqvOnT>ubPxM%wJ>-$h@Roq)asV|#`i&bRXT zsf4b-+eY`<{S-ahyat@-y*8T5bq{vDe{j4WmkuMtM%?qk#okxs4nZUIT-y?0(qQhe z*(%JjLtvsGv?-piV=&wYYrtf|l$z@&Rv{KwxTHP>dcr}U4L#+cN1$h*Ylx*NC-~0s zEr9M?ueARG=$eDR0=f^n>Nz3oa}9LQhsZVO@o)BdhMBSx(Z3(H(TOFtRa^sJFv*YG zr~{vi+JUi&Spz+6^lbo~_K!9S;JcN}--|sTOzboCNr}GYyd=OR{)w2Z{>(Wn1>Li{ zjp{k(jJjQ`!9>5n`k0yjshNo9s+w8NcD^_5oTO$Zo(wxb zn08K8GnHVpwPqf#QZs%q$)DTkaqjnf)JzYU=x=StJNrh|Og|W(tDP1i5AM&@%yKXp zFu7;nWYtUtOk%xu;~5*j3Pzk;ar?onZ!%lYFXUM;Q<&MHo!;hk%D#dT+lwkNF={u~ zI{`Hl1(PgmH{Qb%Q8NqK*Jka;{h8OQnPp(KElfLishL$^JX^IJ#}89##&rtDJm!tR zmZ(>*!wN8-$?bF>*Vy0Hc4mV~ZHJg0>~w?imA4!FKCnAp_hKCvXFEH!)1f>zzE%5L z3dXf-yYY^@DHj*|%Wqse#4)b~69tpAGhHyEzt(~AZV;wf+%6f9V6-{tuie|}Z`QFY zn3-T=d$m&!*T&guUlA~_{o5&sZJ+yIHPZ)1Kd8Orc8uGV1d{>t29K{2F^_;r9@w)0cG`xS z{Vyr>m&oA)Fxn~Yif6|aw)2BvJagLVKd@PApAxDm_>w$s*bJTK}!wVhEgsc<`;?oi8~TC^+DPH%GlK3ChB1|~KkOvgKn zn*bQsY3=kX=eR`7^TDLgY^Pp6-ZUnBiDMTDF#1L9bP(G)@ls{&41q~p+HO2=?^-pp zhU2-cownlR5`%W80x_1=p$4yQr+ywco5l*h5Kr!wH!vFhrh!Yv+l^;H;k&9o0$?&X zw$mOC^)Vle=jL|f{(=&A5@39bP-lGH?syTC==&is{=3`hj}E@pa6Acf&Xm5yHmzt~JeI06O6Dk4NU5dcKXg5x5z;NO!CckIu!{h%7Nv4Obnd%cDwyq+TtE(4S5&<=Y6N$ zcsDb?TWgMUHO#i&ZKsoLI+*yU$T@HKV9Oe}hJW?4uP@q-dnZaT1MKT-%w3$%pVhvG!6d$Ir~l)A z+3NB_4T%2Df=PbYPG|FYI#@7fjdLC+HNw}=?R1W!f911BXMjuouboyYxcokj4krFf zJAGDy;cZPXnC$I_i>g$k4_BJDzHU46zirHVEh|}EzbzRaUjtbqhMkbR$Z@| zy9S?Q@dPm^y`tWaWZeth>!ACg`yKQs^r(X#hpufLrai4~#dr3fWc$!n*K2Fg(`+C5 zBluh@S7Yp>ENmR-2#6XmpOMCXCWkPGfhl#oU^;ZyCgGCLIt@VgLeJGA?2XsG*{AcM z>zjs+aeq!ehT{HO3?{l+n2zL{AGZPKBx6lff&Znzq_zwz?wv1OTfy8K_BRRj!SmyF zd4I-vUtEKMN;0n1@HC-bw+_<_Jcry@%;km%Y_YxR;QZT#X{vRNX_=RGwx@-S=UF{1 z?1{SU1ryyRO#fk@ud0~=W_AzTx2IwZ41-DR5vGlJu32lh!*OT9BrC(lyE1pYqA*9~ z;?AWZ*S^Wk$m@P#`Wxr#P{HB+Oa59bw&w#EpBh$PcbmV@Q3DqN=RF`yAM$q7FYJkJ zYabYoFH9>rr%wn*cUWUs^+=bG|gWs8gLtOFCjCQRFKz4Qo1 z-sRcd3*7QV1%!kU{d{I<38$)U_`$~z(nr|Qwz84N5Kd?ePGh}g=vhp6-QjH zj5*27{b5?j@th_Y(+=lxgncE$#`PljK0fy#-}(DsHT-)WxXfc=y0pZ$i5aaG^Trcl zdXMWQE&PeL&1B{|)BxLQUZ9LS0w%pYOv~8LS%Q)62a~|I;T-ry=@#NPx8K~hBpB`0 zFm+iOEG+ZSLgD#)CAc`a#a0gUFY9Ze`~Dg(c^-Rm8|nx8Jcr!O026&JOde*Q6>So^ z(ZR%C57Va{$7^YC58uTU5lhEg~4g8Jwr#Lpb0*p4S$enms&?uM$m`NP( z=k{1MnD(@zmfsB1$vh_L8e3m-A4~(|c_&OKAQ<;ff)Vw_TnL>0e%N@I`;mgPY$JNX zxjqaV*OKFRh4vNp{RhCLKMvCtwzX}683vQhgz0&n8x?jqrYxA`Kf~m;G9q>j+~hFE z#^+)Biv87z*hRh00F(YIOh5Ae>p5yhXXb}6J;lt|YNi)Ve7z2Op7VLgwYHpdJOg0- z8+OpueBEZfV8pl|29w#U!??c~zZuV?XdCBX3|wNH4tkL1qDO@-OTUzNz^B$hi#hgf z|5%7i`1FCv?%hHE;pZ!c1p}Y?YoOR?gt;ToM&1UL^%ZR8e8M0Y=HlRz$8^vIJhxX0 zPT14HEd%GR?x0)P-k$|0`ezlGbVCPy%z6DmFk*bVI^nOm!+5Xh#On%i2|E>Fvf&Q< z`(I2uJceh3(K|Xy&LT;x!$_11yQL^>#i-?+~dF@ck7oQf{g%iIp*n9yZ{%k`bHX3}7z z3S6|igSO#5xkYeho;bEBnE2TpbSICimjwf#a@*7o&U+r}innub3r_UQa%L{;pw-N* ze|@3vM89Of#QHjDHn$Of<2g0A1Fi^a1p135bPamqx(;ek=oXa>o=fI6*}i^3G`^Z!~VVkku$b43@&wJhyC79XsnMy&qBWipRKvj z@V~OT=$o55sN1TG8ufy4-P}>~9VP6ihV6i{)u_4e)5A6vVlK1#5yy=C*#;QfJB-BG zT@EI*sDtKP?U?a$EEzEREgi=BpZ!IRAQsN8>s08scG#cEC63c*z@~spgDZ6nHT$Xp zlj-lUU-Ju%eRV@m-EP*}(IOVqG+qLeyXZm#w-8+Xjt-j4+v!@tiE*?HOmcCD;=LzV zZ&1aXMA%_l8Mbw22mOH0MSU*v=spe1T^$s&GB^*9)9^VvQ^0uc?l7JK5h;pkyw0oG z&OIHplhqEsbAGy^C!y!+{5oO7tcyPW{kza*H%k`CipRIoWdp2e6S0TX?o zgI03>B!kZt_#1=nALyXR@wrHcjr{Q)j<>uU^YNcLC}QPU!`}?(ap(_Pb+bJo=)Q+K zEZ311_VL+93``cxQ@oFrD9#DTnt+~sro(bghG9b_#Njb0ZvU%ZYc)g?Ka~#;u#4`}@8y$3!wf0Ou zQ=rG+#I};x#o)*DV%BB_{#6CW|4s)D;Byg!Sn_?vTn{+cNQdHihK2huIi|(nba1MB z$u;Q9p(mif#4+D0awxWu88FfJJ7^JjOH8?O%kj9+MB7((&?D9y!cKlXu)j)hsSi5n zN2@I{KK;ym*g;FJw#;{p=GqS?wyJ}+w65iI`}w(PFgH#YgG+qULHk(yBzH0fNktqS z*ATe$ryaB}+j~}>hc#eapJOe{v9A$~sFSiN{DCRs*nd_tUNFfoI_Mv4XOp<7+roZG z4Vd_s9aP75c2YC*z+~5S(3#w}scL3182uaMl9^-FObU$uTg*}HtIN)C9jykF`mV#c z$K*=En6>Rb3wpMLj$|ACLKnH20><;74&$1Y$J9&}7~c;aG{*ZCDK!&i=GPA6*>2wn zM%2(kF#g{?JaLo=4U0C=;Ti0nRi|_vkpvVTa%foX0&tA7u$Cl$BhGOW+s@}4xQAx6=FVL zFrw`dFxjb{^eyij(9MOI1=9y6b4VwB!25nv1tZ#?1d}?ZlWMufLUxAdmk}`9$zZtO z@qK)*hOhiQtl{75z{UKX#{S_Y!j_0h>p`E@cGCO2eO{<$W`gmBJ83_wKQnHQDFP@(Wj-m2Kc}yuV>X_V*~bC^*}i#2kMdujf4M zQ-0p5d}ZhnXH`F7Ko@_-u_gzgFu9lfI&p-eTr7dtTVi zaxlr>PWk~x+!w2v3^Ui5Oh(PP&j)jTCoSWck6u)$CowNg0TaE^WG+=RRbadeJ83S* zgZN}WykKF!4J&9k0t;1}1Z7Cmqi7Kw8)lHNFar_CTj`zxe*Q z6xtz#Q9=N~P{9OK|(!<}>v-_P`z;6%SJ z1>^d#lNRwda@YPs`vtQSOybi{`i$%BJi(ZGCD(+lOAt z{yOMshu@c91p9lJ_CEu96uRwPz3D#$Jqx{*|M_gcQvG`kIa$p1p@)j{gPIZZbqY)x zOsO1Yp!@deQeGF&edL;t96RV5^aSR6+j~&ZMh!prLD%=~ay-5gW@f+A49Bs68E=>J z-9W4lK#!NuIgS<3V_%rtw%bI#i@9YKOcu3##G&^TA}me9il< zAKUG4zbC+?|BLkzw||G*3Ui%Ve<|qtPo24YxZKAIUBtVZnV&mpCyuvH%}l%$d0y9P zJfrYbHB$*D`b#I>;m{^O7|*Zf+H=0zP7j!*yGwD*wXhG}4@R5NWjuHPDYc#DVDt^U zXhS~!|C*Y~fQfp#XoT1AlWs5MPvplPL;dX3MGbsx=T0><1x&J{i!SHBdR#E(wF2B% zRbX5Pc9py)s2jQu`T}eI%=xkpdKS9snj@~^LFk@?y6o41VUde-C%FqGG;kwq2i(n8 zKe)c>X!E|RwP4~0chT$I9&oC)TKQ$j@1b3^y~Eh^fr-uNqKzDwFqrsZ80T>0{!Zjh z^uYo!+EM5yW`0#OgJ4|8b{WsXtGdJ1mwaE~0vzv-9cSctyiMj75)2Irp% zjf3S#UoDS+{{0I6{YhPP1U`>5_{iF7XenqHNe#AHbMZ0dlhuC9p}G8BG!Z!(x0!or zXW^O_9~jRmUDS;IzHvsJhtu%yFqmjl7ac0?Acy&5bXKdzno`oK5>7p#> z8Q;f!;jr6m^SCQ>ldnMEhr5jH34_JCmi~O;5*=O2?IXvdLyvWK(Hwj(@@Mu*40;Co zue|;~z1UBFpJ53zr*|30gJO1u{j30!j3QozAJ*5f4e0l?evhzW#xb!MHF{PTJ;`(A zlR_6YS_#JA(`CQbPOL}#V6tFTW0>QJLie4k?jH?$oPQ5J%yHsxynn=Yd?^_HJk%)1 z`no+8+Y8U z*l)09;Bxo!ED^OSu7Ruo<6YWC z_gdo;?VHVZp6sICc|Gy8+Du^uBW!(%H6#0*BskL_$6g7>_f?m19%&EBT;)Dd ztUqg*`?`x7`55*=f)mGQ=CQvYyQqtg-&F}l$Jlo6y$6DE|*N^mK7hLAJ2;B^4MgByNYS`9E5t_>TWw%OO z#{M1qn+GNph#1#_;rn>pmg{a3;Pg<0I)SMCv8^F6X)vppc}Dm{|H^C1M!_YUBJ?5G z9N2N6X0CGUe;hAQbVhRfXyZE8h`eoD4b9&bp`TGlgQ_{UBN_6BD0cw}Gw;5=tV?8lSMcONW+9)&&(wx})U_+cBX!1&Hq#bX@1S_eG^ zeLHI`;yGv94T$%gi2Yg*aom>MH5**~+=%_X4}#;EqToE|myRjUcA(#GZ5JL0l7COg zQZV|w2({Xo@ncIX!KA@lYGurIG{?LSjQ@g&aUA^(Q8(BT%J&6x6>)GEMeO&UpyuT^ zsR~?dKE|RopJJO71>?B{wOV2w!2TA3$;Kkab-ZIDE|E*-mV--P9--UZmKv~dtHJrN zjwr5C%*}tOJmb2kvK!$S+?TM8e-&yRV-oY^I5!PkX2I`sA#mwyexK_Fm%27$zh^7g zj@%~7r6HU_aM3@a#_+i)K5;%W4JHfbI4fiB=VhV$`Xa`8kqr{IIlcnFn{*TU=(>op z|A6n~W8K(hqSuYJ(+qGKaEqmbY5@a`*@!r_Iymtje_$$8Oa?#vvT=;BCdf+w;-;k zBDAe_jyAWw)1XI}Md&rKMRv^oV_yLB_c;?wxg26m98^?Te?Pp&@CIjE9JaJ5uZp9dQB|5j(z<_Fk+v>(~rLWDng^k156ciBx0ThM*kY?59=5Z zOaM&!n+SDrpN$A#Cd2(PAB^wc5#!z1(w7)>8veh8WBNWq8*<#++*io8@V5d?><8?- z@Oh5i1!K0K{f&Z2ZZOw4CfF$$i@!;?Ax{(L(zhIYOmM>1bTH|S=jNWz=YC8umU%@7 zm)&A6oyT?Zy_(}TE&vzZZZ7S+8Ms526xt|!4uVPSIoEjh&7Fcl{Q3PjZs!Qw+IKGf zjqBwd!C7Kk2hMlkT;m?tt?w`R6dcD^aXa!cV=g_*_Kp^u$p36GiKFMzbZ)0E7*Si@ zZ0Gp7^f=q;6O5>dINLd8E`@nMxI-|OK3WFOQ#+UHd4KJ7!HIfb1tzM`r3z*ad!W!h zkyFz+&Ram}A4Y*}*U2dAH5#(#t0L|i>!(r3-3 zey-;y)J#8^XwO`Fcq1_Hs+r|rv`gmFQ@o$)c~IHD%>OfX{_%~C*&h#rdK5u1D0{z@ ztu?#C6vZGYiVi`UVi04{X_MyWM?0Nqr(&ll24Rct?jY=-7%M1>8o{7!5p?NNgdG&M zg0h2I`~95U+uW1f=ib>r@_Hrtyze>ZInR&#++WWHlj&I~{!KaWU<~rfpOYwA0q**R z!cDolpK&Z#Gr_q2yinVxZQpPBgDt+FsR!q{b)mLa#~yM{K~6itWp7(3?%EbUk7gY6 znE;bWE)+ddDrcs^#5QXZZxM4FW7vFg z#%*ZF+cb%@>Ac|I7*o^_J>XL1P2wBkj#(v*X#p|6X%f$F52lMT%wG?f_z_K_kH+uq zA295|-*gVBA11-M=QN3C%E9%F!@8~0#{rb9F>r~wP2xpr&+jvi^~3dG)C-%$EMj)} ztKpN4N6PK!7k@@vu<67nKR&Q~@F}iZaIqB%+KH9qHL~eVTWvuflGqBfyP+; zmib!`#@lbtD0g6ty3d}e0h2}^$Kl4xS0k8Ys7ah^VvMz3mxGBv&?Gj75i2`MFo}nn zEZ?Q6!2T%NdALct&l107WnTxz{YaBoS=_!@eU#sc@jl#SKGtFV)&nLHX%gF*>|EhI zNq>KN1wv>9myDt<(pcZf+8X-z^z}XB;@~ouH;IdA9C)~pn;r)gaD(8yy-ng7lV4+8 z9S7sOxk+>vw}GPlmGdtsLtm4qqB+O04;teD+s9CUnh7pD&?LQog<`KGJC8PL>vZFH zKJQu?ky)*6M!_W?Z!$kOr=JrcTYX@BgH75Rp%%7?OM!Dgfqdb+lXLJky=EK5K0&d8 z%eB#ktc?0`eaT9U1y44KC&BpD ziBYPP)pDj6jQ3eRmNCY#acK~Y>pARGDBjN)!{(dgU@~BG=ZL>&j2<)P&v6&(;`t`+ z9)D-jsCzKe=QfF}1m|0$*V&GY%e4_W1*72O1?PI9NxM&~f^p1e6ii~cN$g5Fs9_A- z*O9Fi;8L$3e&Wt&9LsqMO#Ia*aU1o6#b$EO-U!H4^tKA)oC?-;oFSd;Kl-+PVOV*BZp zb8$f_j|3k7km5t!)yIQ~8dTGp>8k_P#*BqkO7z zrV5NQ*(8RDS!QM^mknStUp1MZFJZYPE(Xr`HO57X`(|dVXk6?E7yqV7yhlDCF64MV z)8N$a;FrqyEaODimd#`4(867)z$eDgHo|(;JKPuO)a%KdKx_`5HhT762awbc@ z4s6!WH@#$LXsj%|7j2-rSvt;>UIpD%-z+`HtU#}a?l`2`eC>smxgAUl%#GlkpPAzz zJ1f8_hc;{LxqL5YQeZN(o3(dmVw-$FW4_jcQ4enxiy;^EMaGoehc*G`U}|5^M~u2; z%uF!uBkg^8!KA?C-Z5+uPZUh@m}YS()#dhbUwsr$ZL?U1fSt$4nIU54G;3>I#pO%} zO#IYlA*fu>%NghW7>`eD*6yWvn=$(H|5WFdU}C8Ap5h$p^YL2f>KV-=)nnj|xT+Cku z7{@%+S24rZR;mT#0W;f%@q>wNfQf@iZNS$6#dEIAm$o)1)z=us1IFq;#C6bp7P^A% zXFPzucOJ$GYB%dxn_zjF4aR+bvvxm7>44E^^mz)!;UmV^Y`$)Fymk#b20a1Y)UKGH zm0&U#Hfw7>R4_k`83vQQs979H?eAbYGYKZ!*sL8>*2x*yUorMIHH$Q2w`#{#V3cOP z+~+epY<|{2jK4W|uds6wV{qMq)`g)QcY;YwGgr!)1lbAbZT%L;=xe1=j@N+6fC(4J ztmk+wbYDxe__$ctubV3PzvMxbJJ>9yY?ztEsP>GPn2Heuwz^gCOd;*Jj)rbFz%jaaiq74m`C-#?W4w+!pb!OCbhCzyiV~ba%K!nyuVpIO6NNFGc$Cr&j8Mk z`ySQCo_rhBj|bCqe*H1j0l2)z#$@9Y?C&fS-m*Yz@xZ#U^rHVf*% zc^zNrckjb!YtRlVu75qY0qDw*TvxFDD0JV`&Emr1?e$|r^0AJ5tZp`+N78-Fcmy_{ zX%;&b+tBy-)zA~EW^p&2&$yB0SFdmKF^_D#DH+>!ADz&ZPnty~*n&2U-xc_*fbM#$ zS^QG0(>-EzngjnU_^csrv{^fEwAo|QHZwuK#+t=NHhrOV0Bz1rz@q{SDups~N-26O%0! zocD`n?K$Ar8K?IRvegU5G1;7Z=U73xitdpcBJL}Be#qB2^d$5OI#>QN^Toy_$7ATn z|HgPsqaI>*_vgk#!;Vqze&}iF@6a*wc7vvR zqIwu18+-e;b%3fEqn%4YjcVh{6u8VjezBVB;bg|)zyRmG^5?S32QkLY^qb$^iaFfz z`W(&!CJSaOit7??OFIusb-DnI`yjuxA5+}j&@<3;>jL0+et&?=CU{-}qn#G_DsZub z{n|6EFEL;2ycyXW1?N4?FP@}pr#~=`ovR~mJvi5~esMRALHj;oa^U$urI`PTpTfe`j>`Bxq^jC{@Hbzy0NuBQ(rNw=j zo!?Thy$@V`8TulX8Nc(sSU>9qlfJ+&ssT7ZHBh3n6E6D z6zqIOW5eE08gDmDiBXK7*EJ=es2ZrOdnqp=L6>r`_1zR;^;Lv zU=)0Mz^M_xSVHw)FZa0$Om+!k2T$7>xx8|d+%64&Dfoc`SeZ61!@VziD?Ve?7lZ{{!V64^==z$)K`?c#2;El3r zv`@Zrtat`ij zn(tt{;<(6G4H!?KU;L_=!8kH~Zk9MdINu-rVsFa%z0*Dmuk%x`dcnnS^NY=k>rd~W z1JDx*zxa^$>se+;Z^Ptk3{32Pzqp9{-9Aql?Hr90?JhsYJ65B7kNCx1D4g>u#_6^w zo(eGOLF@6P272ZbzvUVe3!ulIu-3bwJ3f``3jD8xp1}6Mrg8dSbBt8pVKC_-zu1!Y z)&F7)8;^*~g3CPT7e~@H|C5G{yyUptE;2Xwp79L&$7_Cl-FrDlv3bC`$Iy@H{)a7} zHteza(*j~pKGQW){aB(Ky84-4a(}wf_WQxa|6@Pz83CjG*RP%D9b&Ptb1qph2{5^P zmxtv{SqfwAgkQL5EE;8uUM9+8H5lb9VB(H|bPS<(Is#qYC}6q%z$A1<4M^8ArtM3gMQ*TtCBCCO z7=2x@ryqg3>J28JYmxGun*+Crl6Z1#`lV6jv4@??%a?G;@ zOu>F51uhv1Xy>-xVjMdcw-!ty9I#zOq~tl|E^5!r1QT-x%-5uO`N4QM4M?7$(AqM! zohTU3W&!ajl^-8tOwB!*nVat^_D4Vb^+e|RtEd%23@KYGK5sw#)yEGu)v|*xPTw4Xi?zF%7nbjGKr;qGx9nhXT z+BRkC4+`uLfyr!R?<)f)SFT^0IxcwV6mK95e59dK6fbRk<)U_5&VwEJ&VZp)Y(5m){a$_DO*;+S&puSD?p zU%{saT^-8uH!6YjK+Vg=oai4gwjJ-EH4$l3XfVK|8szPr1K867>>3sv@UYnec zfr2`N`i489MG<3NaCSfkAm?W8W0cTyYm-r$EaTg+Inz~!vflQ z<{8hL##pkYybN2j^?Ez4keRMG;%dRA4i9K^xP_d%aueK|nrkb>so;D^1jIY|?(E{6 z5gT!R-(yU@cHNcY_JVO79T4R-SI6&sn?fBT=={FD9h`D(!19?t1)HuQf5*WewTEw* zKRs^pmm+^B=;Qa{&#^i*@=3NP!1+!Lh?^)bKj(~jN$KBU>*RnK0`I(%GljOQz@@+? zDX!ZY$MV?#Mm;qkn#kXmjA3oL6HKN)AYLRpn>}yD&g>+>B9Ydj zZkzF70!-|jfY_7f^e7*%7aCV6m!%`{H6QyPD*v_IS5f)Fc`giy*U8_loHKIS0LFDu zKzn9*fHQ@*Vr1)L$=Jbiuo6rL%q`^a9d66mZw`ZrH3q~p6gxio@-aJ;c18x^^{*^Xh7+b{vxTgZ&g~B{;9_T^h*UbImo{$I*H8G_=+Dp4W7{ zq|ve-dT${XQVvkPFI&*vWqB zYnXRIH_c(DkAFyChJ8x$_AEv(7;iV)Suum(75J#oUGE_VQymnxi#~8M*vYLkkL`-f zqu{S0Fv-iYFDSOf>O2D`bw!})J=TTiZk?|quUFdpssy9-U_K7N1-aAnOJ%DgJL4NJ zTNGRxw(QE*2PS!yJu?I*es#dSR+fIePcdb{sP6~Fmc=>3V2n#s`8iBU8hz}VfbF_M zGr{<-)%Ou4YmD)V9m|u?dT`1O0rT^L?7TX0o#5g(2E>18Y{Tz-9pn9;)8`Nr{JoO6 zoAhysb1e76U}7Hx%t7!{eZa--wWbm{;I)vKHRWBAGp{p zfueKqXn(AYc7yTt1%#mUd2W_h-5tdZ?5*fWNJ}hAJ_Xs7xct6sQ-M?lGEB6}m^#G1>Ddvq{Fxn-nuL)uv#Qwjy+^kJ1 z&|Gg}3{3{KXWXjIwuq?$qdXiCTT{DL5C>3rmMa;6iE>#2bLyugd5xXD+7 zd_5fy+mo+qIkN_g=NZf?X|6GsG3>h11lf5W^KWAQ#28kurK1=tUkr%<60^xm(sJ24a|(>|aiHjaBD6)l zJ!hb+(EkgzpuOvBypRp&-@*JdAYF5f^a|(+=? z@AHH6d>YX1?ZI~W{)y|H)6W_x_`8?G3q_>u7rmU4cI1ZM4nFfc9P${LaV9+RJP(v9AK+IBG8zj1NpYyMgf!jPIM` zc}3xP(htV*EyfD+b>4JN!00(q;C}>6^80|c7O+J;Sz>+&h)19sF&FwOdk5{|-}dvw zYB0%lIJZRCa(+J@PvN!Pd0^td1jH584+a^-au@@nY}BH?tN2Yhvl2{p(-!SmNqO1m z2W$)+2IJdYXJ*TpNwR}F&#gn-z!(;fYYZ`$w1`jWIrK{z!^V;-FtN@Sv6RYXk>>_t zX0&MUGCBPfBW7l&6O3=K7ST`F=^cNQG6^uQ{aVD2RPXI_W(_g3TEx~=7Z&kMfKjSj z#01&7li6WqFZ~DF*roR6a)WWrZV{#QEb9=n!^%|;MmeQLTi0doh*2))tDSs#Tg>-d zGG+z&I<-Z3D9;hIoeGpS1;%@Ni*Ql7u4N4NYtw6bDBvc*`Oa(+|0ZrVZm;~V0MbGvDSlgUDYBQY2W_)Vh$YzKFDV$xY!LX zqN#Yl%IYKmCVOLxIN5@sJgfoZ>1`412)rQnqPg89xKv+D(b`4EoRn<2-bWw!Q;T?s z+GXwQMy}W~4#i##&V5^p?K3s=z$C%==pNC<%$7b@P#wp>xX>1I&nUi_HvH-P@dWh5 zBl7k}@ur~1pV)94m;jd=XwlvmHp=|4T$g@;`hBcL+)MTQt(nQSe=rXCyBxlqu7L6pVYQMO2XueDZS(d?g>f(Bn^Amw5oXy1K>uo>r7)`dR?l83&gE zcOt$Q_|bE_p5l0>MJ$o%3jQnq5M_bxBYhuM);Ze!Z{))R#+7OjM-YP#FN=boKImTP zrgf$CypxS?@^eT~?2GROHV`M-SP4D$Y)jEGq|t84#xR%+Z0s+wQGx#^pu3)HF|Qqm zGqSnY4uVneab=Kua2MlyK`gjAfBJpCRbU*?qsPgTFm!zvAORWF!2{#ir&+P{=vqa z2{4|Q^nBG8=8N)O@=w?pZV{(YKIazdl&4DQu46V_E-yHB100pB9bD#ReZGD%^T%Rd z0VWMwvT=c88iMYqv5$$$wicZ0Z<4+bjdbT)l-)+JfbJS;F~3g({hi{df$rNt911u; zxFqaMP>wHWWoCUk4kqzhi}wEA+Zcm^efl0p@;OLcxom+Q)!8zj^!%Q&lw_C(Q8k078)7ba1am@=RG1ej|MkhY`aSLB5*DCb1 zg|0x4LwEe6Ma0R+ZgL+3VB+st`xu3uvd}56Dd<_~xpN{1Gau}_HgV;jf*Wtq-Wh@4 z`Lf`=#`Jzx!QVCD67RQ|?_WEeZg5?;DKU*;JRj)oeGc=<`tfow>W4bxH#3yKB$yQ1 znygQdo`#;a(X-H%W39J$eTMPNMt4Jx+2~&ANgG{-p0Uy6&|Sw_$CrfevC-4eRU17E zJz=A}CeS`@bT@Rz@z(Kqp}TE#6}rzxk3)~!=t<})3!U0o8hRG`9@O8jk+-2KFv!BxY^eFUr~ zQRrD4eG0mAigo;DU!neO^eX5v8@(QS(ngO$&sgX*hV?>EWc2;ueeyAE5RA(!F9X>a zgP#5f$Kf;%zAU$~9*oDr2JK7B|AlsBp_7l<(B1#k2P**hP z+s~W>V*1I(XD!ZcrD@~y%T!OMqdFvX`>H8&)DeW z&|Rm?^FjXCL-$zdx%uxm@IQs)9?HdAtc>h@rvf`Q;Jp8C5m!=Q!|(h&nH?82f>AAe zQ7k>s6E=E3^t6pW4Bb(0UDgTcZVO$3|B`Q!%XK)XW>bclV0;!fs0?+`;}$yk@I&|h z)S^AtypH8l&l53mFrJ@VL@%9x-F(y-4;V85CIvrps9vy5alXmU7?{j2E#gi9&fhXS z?0VsPF!4=-;wSQTI%8P9Dc_+D&6Ves+K2~weDk0fp}Z~THjMk3C^wDZJQj8+-X7?_ zErP;FcCKS~*f_WfjA~(nVi|#+5*v#6F0?Uy-z>!v1LtV4&Tj&`Z=0Yvl*;rGw^KMC4TDR# zg4+A1vYa!VyVcWl(+8)L02II4_;f3zqIVj$x+?`^!L4KlO z+~qh%p|-hH&h(KVB`CHaJ1?0T%JC2wb&sH^CgxMd;5_59O>wpa3~_70B`bp3bBB(% zjdB%TYb*T$eP*AaxSOt{p2IkHZPN`Vz8~5c^_d~Yu(nZ8j7^TBg_TA5H}B_2F7ur&diiEE5W!I=*&rSW*Cg`BAscHGm~KA z7wgPba>n%|+SMgG(=TVLz+@YB<|R4P07hxjnNQ_RCm3(D&TRS*X?_x5Vt$>ekTYw* zBm+8gjGUPOlWEbJ`EsW8C$t|`XD*X7ZZMvZ&LreaJs35tGtbDGb})&E&U_$eR)9%I zb>=^ECI!aPrZeU5N^`gtjC+yJ942Q7f5^@^ajt~uB7o%B)Y3FyDZ z_k!(peG0noN^8CBKd5ILy$ZUgCn&uKg#6b-SFQ@0?}69jk3#oa=nA&)g&u>RyB3At z`SP>5;2@av)j{*~eegjx#-O{d37VhV({0eaU_BV+v7mWvd$wjhm8aqt%e%g51OyhvU}&t|BH5r{MbFKkSjm9By7cC*ZI1+d<&qv<3a73`g=^* z%16uF-+1^ECZzU>b~TFlf6z(iHi5A}GpePGVta zhGToYmnkSFpj$Ds!NmR$6i?bPJ}}9rg0}1McZ2ct28Gimo_;Xin}XsVV*br?qtDe8 z#4tk4YJJX(-}(HoW4y;YWK9PF*DRB8o>DO27~VfWyfy?pLXbR=(iT@tlTTWxbMNZ z0t3zum>+gLk|I0z2L&E!F7%_@p;*>}Q69vxLa{HsUpY5HyMg|^OOi+sf&Z}pvm^0xP<&p@6rJO$1Q&lLD8{L5W$&BXDdo8iOy*H}os;f|?m9%Zyw83) z^weRh`Pmd~OWUs^`_Qe{^c{h&%vR0UhV|_yY5S3&<@TkUqW)f$w-fR|6M6#r-uPaS zPkldE2idIt7oYb52$4?Xo7&Kp>4KLXwLdQe(o!mu|MbBMaU1Y!Gy6D>I?H(m@d|-X15H&?WQxv3hWEaQhW=h=c4zp{Jm) zDAv&~r_WOnw*s8+ji3mcxZJrl&{|w;z@@=Ciu)ZV1(-kN?l&cSlf=Cl6ke0PLce93 zqaD4qVQw}!^?%?NfJ<$FqxxMA&NV8}Tdw}0d!V0I9KT+_BhVAj_bJx#o3@`M9qsQJ z()XM0!^h{Il_9;f1oaQyw9dD_{Y>cYBdzs1=&FtGhn}?2my>@ReHHolSoY;1rj6DY?!L2)B&I`LuqpZxkn)nJlfOlvl39XQ`Nec-%fLCKmqh3Ch6 zz{QSMi^f(wmjm}ycm2@a{|IW&GaWYFc5#oN-u8!~`;M{RH%vfRvAt|eBKsv>qwH+W3Z2Tp~pQ?X4!9q8p9qMZmu^n{J>+6rR;^y4MlQ(oQBJ(-~BF4k!u zs6S6ld1xRz;Ak5sKKZ=jtAbBEbmgO<__=tWg-lMb$FPE!e+JFZ+k@fz$u;0o;LOh} z8_#%C9FySGwHw|~mTe906U+llu^HD~$lh#l@lPdnRn*27fb)J9lw4ah+78)U4lXqj zG_NC!7k^DZN2!1t0O$Au^9OoP>3B94=(!_i3{2u1oZH9ug8VaPJ($?HL2;I;oJJie z+h9)iog^2i?9v}y_LU$c+t=B@2+31bXvo?AU z^mwf*n#un?tPHyUe(2t9RB6AU_=lmVZS)D+-ldx7G`jtg?NR;{RBAB-%yh#^~f{zzmYzI{g()oy=7{|t)C>Y0ys(1*zbN3I8_QTFS_JQ&2 zteT%GFBBRp1=Es^<5pX!}6W zS}x{+%YrjKOT_wi42-W_6<5+2vwuM@a{EE5+m&Ea$XA5ox`Z*PI|cu(AwRpT+IZZK)s$sNPL#uz;oFX?-zqMX{vyG&>2 ztfFADd+W@$8KeFg(+9@2uPQc0E}gYzhU#kwjN(?s%eixPjKOt%-13oMTY4?|o27~a zD5ezSbbq=2za!>tb*kmLflBBJ=%)1s5C_Fl3q5|0YQC1h-hV)x3N8aXcj7z3VQtLV zPZHNh_U5SK{Nnv&uD>C0FvO+6x#!u>rxkD$;C$z*w&%r5cS4>{R>i~Mog05-)C+4z zZeq@}wnMqB2a`NS)t(6~XLj^95+xhZ7m}Y!rZYc%V0>OxdQD{dT>lW5G??78qg7@* zl%EWk#Hp%zEqAn$>G!D;S26>0zSC6k0Ok8+W{YtOI5#+@UKQIH*9(ii9*pMV>NGoLR2?(BlhKaS~W3w~9@%hzh>>4#*A?5Nb60vFrgHX!OTq8X z#TAX9A2XWNnAa zR$7iW1ZE%FpWt`CziIc0H(ogMQ`f^nrV7TUz)+6`@cv8ufT_X0K!vV2#8 z@m{KmZo2pR2{S`EY5E)WU)UM_XzpANfm48yui6E=lVQH!M0g2nP*k) zeY^ObZzs%7*{{$})~L4E)~dl|z^s9Z!h9P!ng=HNg1W(LYu(@yFRI$LwMSXpdQ7?T zA5834>`OQC#Hpv*50y__?;vlN8xr|}? zsREOINEOFYef7(k1~9QW%<06+F=U1`r(H6pA+EHuyaCj zJov5PQ}TZpKQa>Ct36*q%uF!e4KQ9X$}2b)0bkHA*tvly7!{0NZu-DvHoy#lNgy7( zcVT6~xJOj+NO3%@9-Moi9>6TKVJgA6KUQtmn5_d7dsRP%hppniMfIhUFEHj~OygXj z0 zhgJv1H-_UZ_${b={kWduQOOsW56n#N7?YS@FtLBAVwC#V9&3$$%Ia*8e7&cNt>DOc zkeQ*rGY%&EPx;)K^!3owpXl|jG8_7QsJsF`K2^1K#xG|&i^BuPHKAIbLnS{8z^I?A z+P(Ssov$bTeKy_D6aP}hH&kA(Ge7;rWL2@;h8Y3l{#F%dYzaI4=6ETVEXA^MNP1qL zbk|;JFPns<#}f*4H*}9PBsn*3jQjOqlD?4bu`t={BwrVVv~|MjJ~7oZ#ghQz+cYE& z#P@>S;5X?h=#I_gx&l1|Jq10C?*-el+&cG0nKrj(DzI%OnA9{AWwGWiCSj5RM#0Am z&OJY5IVMreQSw(3($>}QW482m*9+aXMM%2_b2rny+B)OpXAn#rj33{fk1+;)4X^dh z#WD`YA#~wNrC1!lMtcC0+b_J$82#PB3iQ;4A=`O;4Kc9MO0kg5qqZ)&d}Cw% z5e0uWg7It_5(n7KgO-DdZKcQiDT_saH!j7S1d|2x0=_$cV2qx-G<4s^A@KiPvjv?_^if8-Jq%rq{$;=3exoBw4xpHO%jHf&#j-uS)ldlJT?IHz#Wx>S3TtsvC zJD437PuV_bH+zDi^>|$q(s-)DsJ{(~U8(Hc7c2WbF!A4o!~-^U9s}dqA7ceE<)0hA z^y>r_u)PvYW>!eNhVO;G3fFEL0hg=_i8H8dbC@j_Qx=Ttu#i|*95<``vfp5=ogEUV zk)6locB;WRjtPk`itR98^T1?lLSkPVCI&`1E+o#jVOD~P9Ul@~+c3jm;EaWfrry)ADm3&1E1A#o}lo1FfI(Z=<*LNWD_opZ6@HT4yvZgS%vI5+mKHN`Pu z{3AULJ=-GRf0Lgq^vqxF_szu3_$}gjP{Of$RcZ$?wC~ z`%hv9z_AB++Ti7M)oqjDw&#uwK2AwQZK%H2xx6~VYhZDaQ@jrPONH4rxf&eewYU)+Xr za}?uT?-aOLd#LC=_1b!R)9Wykz4HCgmKKM^@?v|K?qJP4ye3jR*INTF))^9Ov5ga- z{C)@A^gRL!{`P}Ymxjd77Go4KaWL_&kog`_Ha-tfTwuNhU+B*mpT>x}G-P|9`+6{r z%R=UJrmSBoZj=qo+a_PQw`AF-$J4}*a#ah?_xq6fd=Lg!{!U>PoO_wxCJtbA$=XdX z7_}R798=zn@(qIVTp1GcDVKAZ9X(GJ(>R!9Pe|-fOo%b8-6&M9{gJDyLSg~=i!qMn zp%RR5c}RO^726nfoV)immj?xZd%?x83yE!Lo{})zA|?tZaYIPE7X2h+SRMC)@!Y7} zd7UvVPeWwqQTw@R28{Qnkan#ZzKytzTsjXxzq#3-sRZL0wD(m9CJj4Q?+vE1t6&nh zgv6#ceV`XivM(ggvtb6oWS$I(cVW{xDbLS17}uY$FQB?8oixS`mP5xZ*tr#b(x$Cd zfN>;3;#}H)?JKua3&wX>$owu6=F1O8y*nf>u_;%a;<*R&cba>3n0?VZX8bsh?HLN? zuGQx26aVM-f7m{$YXxHibm|jX=*rU}=`%Zg?3+=AIKbteTgG;IKSh1V1J3nKNOW72 ziF_^qlSqZcrBo(v3)c(yyLOj@Q(g***S7$-f|Z@k&68lV>5zB@b_#6id8QnUf=Ry- zGT#FTjpCUi8}Hih+ZAx-2cj>(XT6^##skLvCdMIrcRtKwX7MZllK_(>2A_N`^<%bf z=*s){eXAdg`c_CBVUcGAW=FszNA1 zMC04r=K3ZZ8H(edavRha9Mxzu(4WND0w2i5^mw2^s|4r%9Ah%|;fk-MV`&{2b!|x8 z21Cw67;~C-EJN|AVB%lP;~_tB=#DJrQe*?a^L<6H8)62)WT#L!6w8CmkIv8-8K?8! zKZnFDe9zm!SJGC|d2?vgAM+aOg|uO4vGpO*%Qc(}B5f@+1+2Xa|+~xj_Y(IGFPa7#!bV9z{3nP=0E`WWfZ`&kZ}qzPb^*@4unkdw}vf=C_z? z!i-kF;d%kFGew!i9F9*K4)&db#pLcW)NXJr|hQ^?D^ec_{iNblF%+dK!9a^RV>( zas_%8x>6Drr{jA;`(ksivcr%+Fh^3qI_zJjwoZPk!KAhfo9DY|M-|W;pvOzYqOI7E z)+SJ0+P)|T&b3uoJX_4=?oY{KxxU^HE(30Nipgt^Nx}9bU>w_q#g8;kIp54sTv=jv z3XAW^&MS;5+85854Q^NKJdv&0V0>V5b1?ii>dR=;3jSUI&Ql&1L9}V-hs>v5uf+6# zN$wUFQ>YZ@zswBzTSd$sx*hyB{N+yG?c3_hqW~kPG^iB$1rr)0b$E~SIEyK#R2Am;=Hi7?>Yi?S`{{5vu1s;3XJ!_u!z8> z^Ad9`_0ZGMP5VBa$3|Lm?YW)&927R+_oOqFw-sQrV62{j8KO824r})*beQ89hn|Gq zOmX64v=?JMbsUMdQXLkviy5}weFYdF7*l!l+}A+QLjP4kUh(V_%WEST*P&sNDE7nh zx*SXr%qn88H#c^0zTk{A^ae1hZq`|~zht2QQK>z0V`>g}#I3g^07iO-H0M*7P z#e?>LWLWeRl#R}n;2u-`z7nddT5xGkSj@xsf?Ojasf*-e zKKW~h?z<%{IcI3(g17`YZ{LQwVQ`6yC1ccF<5@9^D+|u^$FO;A8fc`uYB0aE(cREJ zm&p6Mg6+M~6WG3i<`?)~JVp@{1>?9iEG{pu6V{*lz{GF2XNJHiciJ-nmXbhX54M9AZhULeKnc&>_qCe2J4s2IkHnQafqdXKA)#kRFU!#Wd+)h3p z4vVMpy}*{Ko=6pG$f>E9ci+iYj+$Lvw!K9zV+<=&u<;)-$-;3C{Q_SDXnQ<^` zC@i8B&ko;6%jGy8zJ_tUL%vRsGZkR6tzmH**@?=TS}@)vVWCLsefoN!A545CEC$He zt#UhYFlqY_d$MzcoKb4g zpRWzu-Y;1LCVhQa>`LXjL~f^%m^aaWD4r|j%yKZ<8^dBg#dD9Dp?;I3c(#fZJtuBF z2cCu=+uB;sLihX~$3K#OSAqXrCqQ>awEK3`JO<+$ssgSWoO|1db`I$~#_4lDvNaD( z5==k7JO9fVj3{)-K7GBp8=P;uhQkM9NB>w3wF=$AW0#OW4#CG;3{tLB7g>IGKVd&m+ zYx@(>4M96E8UTo)O!7|HxiDxb*+PCBUf_5%c-3 zqUY*{!6|!1#1PfbjPH!`o}C*czgch@a0%k@$&VvN_GX-n{`#K~`^BtDFmJkNvRlzk-jipr4zmjd_KV!uV@C^-dr{>_H%xxu;i z-7wbxE(tDoE!1OOA2D#TnR>l7T66v2Jipy=Inv;=;BxICF1M#37wag#-$lf4ZRRWG zUewus5z%bJc))n~kC=}|*}0DeV6tF#+qkIBvH5-v7*|!q{G6VCJW4UI0^_q`s6D2^ zB*FZE?*;9f&b#Wzg|t6hN8EuC=^CR7^ckn3eH|3h-k-OI<&mAknhhon<_z*RYG%lf z4~*mBh;-c<@)IK)(A&wzTC)veR#Gh05up+@C1-}gB*5hCIH!#M&*Gf~<32>UQ)Xr; zUe{^J3z#Fx7e4v%0pmR7*A3lwXheH{x1zvCe*F^#Tmv}QVG+@U@6JORhnsV!&jEIV ziGiW(ACVhh9nQg6?oQ^gMeYRwK z1^-n-cOM=Rk6E-~@=-@Nj*N&o7B>9QJ)Vg9J!$Oz)Hs+7n1hOK@bf=ll%wQzq+q*I z=sxHd7TaL+$#r1TU}UjUS!UG3$I%iS&`7U_9*5pg>_b0(ZGfJIzR+gu=mg`diHM^t zm|XpXNgf;7;Qe*O;4;TW#I+Pp8*3*t&hzKrz^TV${H9|Su=#ev#-g$_&{j^0Xvf3d z%$CmN$`8gpCnBDgl%Mnl=#G;k=KH4f`BFRd4D?~i_6q*%gPuGkB39JOL-Cv$(VqEzl{3cqUFTfn z2+TPY&-(>T{+XOgFz&e#%jX&tY+DB=)nL!4V7zDBGreFk=R~yUA3m4IGf45wv$r!& z@tkYVI2sVo`Swf&n2b*!3%-}fQ%iO(u(m^O(NA{f>wR>iAB;9#)JJ>4sTW4X#WXH# z!#KVFk*z_pwLqWe)D|++?K;=~!6h!*@c2=37TVHSSX;YQW%jV2$(=TbPX!-0xb(#l zF-7A?598SQQBO=0>W|`|kTdOI6n{j_uo(Nu*9tJ6fHgzqOM&sVM8uXRJI2~yWNQMP z8brUOxSjtta?j!}JsUX)>*JnJ&bYz2TJ>>av6-Qm>cPaoY$&EqaPhW?_&r^(?lIe< zxD#NMPV06>%o;GsrS{AO7|*23y?O&eVhP zfw6lovmH$K_x8*RFtKiXCPndFVb81ulejXXop-D<$4qrrG7tUt-LUyt1vGw|H`9d; z%C#Gu@0y6%h4xD^vn^uk!NjhOXzRPCvKa%Ke>#~&ivx<2k7P4z)> zr@(l7BicG|r!fX`V_3;ukD-_*z`1XVh^;8@CdTRO1CXuKb5Z}dM8qTbj;|b8LKS0Hgj90kavN?ivAZJDYw~2n3q1q9 zm+JMXpG<$=`z!A!uAI`B|F7K`_37i0yNU<6s<*+cORyVtOJXuBUdhi1}jI ziYvglpR%`83nu-vJ>v(XrtFzG7}xXm%mA3=3--(y81Jw>vmQ+L6?;ax0Dau*?GpoQPChVCs7|)mbIhjWb@|<70cnVB*G9o^tu>~JK=1%W3XzVDz z5OwxdL~Krd<5^})A3J74&-^PQzM*k>jOnaSePA5lMznk6{=*nH)^!u}T|~S|@szDM z`a5I#!DPYY?$g~@&WwObO+~cz%Z`>aSunBhkslgc&SDJPca<$bxqga>tEk*h7BbWM zQ4s5FaO!%T*CP(Uc^;>)dHTS_{}T~A5HrSXv9fi8N&bR!cErs1k5PUWQ$I0|sCW); z3Vb0TK0i-`i)|FOd~Cg7wRHsOH@kNmO$(*f_Q{ptV&IMf z;tVl6EFX1XGGKD|2`(*Q@?(z*M%_7T^-hEQ9H5VEforGsa7%%${5gpsifPxV_B``2 zV_4tHkiT-htxuR4%7L>Hb-i0udq%9}zpNf}3CPd=XM$69kBSFrn@Ywpe_k+IF!vC1 zjGT#rN&YITt)YD$W7xU=K8i_+n%DVezJ|c0z?@IM7Rl{oz$pJ06^~LLuPk8ld2}vB zuJ?!vu{D@gjA3)2N-$Y>RO~@^TpO+0GT%;&^Ko@xJO@Ozb9xnWMg`-Z71i#qoh@g2 z!6;Qx?V3uxoEZd@0rM7>{bD&Y4kmSARIH)5zASoKeZnaZ&Lm<#L-%j6AXO^@8!97!@8A&Uv1k83YqQ84Q*08OE^wF%Bku zN>qH7o1ZX7_mwNZAN7A)RP0a8FO0!H1LI$QJgNleI3p^yr?~fbO5?5rqclXt(PV46 zoKeB3XGg{3v_{V;V^~}21>?RL?Tzg0wVBk{AQ)d`RGdWd%#$Criwk;|irg+|E46CzRFv-OjXNub-s}DaIbxBm*O?Kv#7`~XD zI2dKA&fFwt2Ee2*)tUF@%ov#1@1x>Rxg2gG^|hXSU5;^&${vt2N(=IHbyTdOJdewn z8ZfTwqT(H5_7zfJjbO4jM8)P54_+gYA3yc{&|G0T7)NhZ>_ct3mofT$dlhu|O_(E* zpH)m}`Ad_HKSs54=RYuptsOK4#tSC*+}vhc8nG}tGlFPeE6|oHmKr%T8%*+6`+1ZP zj5iS#ACsMSW{0(}ZZMuZqv8^(hx-_#k7o*U(@(zc(c9pjrP7#3z^M0Oen|NVGKRIm zESSs#Ix`?=%2dSsP*glZB<9aMA>Z!fY-%1+KJTNMlw}}bMnHc$cT=#Xe zoLNb}hIHn?a%LDzI)(gD4jZ?Y#xn^f{$fpWvYmI zJ1R0%u8f>%0HeGU6(zYG%9&0u-uI$nitO}kEA^EiW*q&7+E0QptWMW}@x70EC&m0Y zV_1DnfN_1Q&ndjy8NQgE(lBxeCU*|&0y*Ob6Z=fJ6P7deU_291@e;+nj4>>pb};TQ zqGE`czsi{vV6tE8hCAYnGBfJrmf;!VnTAp8AV&&qE*~Ub+Pe|x-YyJ>?FfiC786( znOZqh2PRh9Dt@Ff{<>*KTUTXzj-(Q^O{+M8^8AsU=_Oy=w`$kV&^Pk)J65NIV3ZwN z#XIDydM9aq#=$sdw2Gt2S5nS6T2ZIvt=hP8`V6U^3NWrcTE!W3Pj*ty)Phm>Z57ot zw$$t_wc`hq+`m=aPkCM=XX0dM7V3+}gfBQ_w6Os&88Er~j((CeV_>{hx*f3#i^s6D z9!v^M&dx4!MrlKy4{Q~UG_L-JGe+B}0TVy8RlAQPCTAMKWDaY!J?F3-OzenO@dV{( zfZ1XFA_>OhX%$aWd;XF!tZj^fan!VG$MnbU%F1rUybg>C=3^??sdA=#5&GbXtzteg zKg$^pnDi;F;@`wPQZDtifS5B{MJ2_2_HI(92aMyaR&hCv33thvRbb-2R&f|Ht-DL@ zq{-JMt>PAnXH3pafl&jk;tlFM?Z1-RnbD5+qqb_tg}2F>*6){{fS3 zYZXhVJ@dY-&*@^&eT!PPb!GQbq_M07<67KmUO$A@`7oIHrLAJ)ZQE$mZPngs zv(KJ}FLoXz0mgNk-j)wx42yXU`2rK6a?N4P>Bic{6JQ*-w`$kFFJcU{Q@RB4fXUql z)5VyhjdeHOV6tEqkgppVgZcHb`nsF-V0;O*9b)cg4D;0vCJiQcFVT~XVL4m@#(hVt z_>ji$VaBk!NP%(P-6}3F1>>$Ta>$sqVB!zAitVW`Zf6YhRnmdBJb?WGjXkf+nVDc> zk0It8;$YlwwQBdf&yq6(U@~CJXkUAxoEZb-c(+xIQvNorl*Y53?2NaHWi)r3 zFK3jc=ACQu07kd>le3~Z4r|s zTNQ0$ABt&+F|2H(VBEiL6H$sO@;hnV>%gSkZDLO<|Er8)byj{U+VlZ!+B=ATkux4J z-l{h7k6mEr|MrvmS^&mOfQh%a2@joHEIm-_D@%5k zwTW*j=8qV|e3e}W=IS=FGx@srAj1x0s=;KhZPVsHAIO<`U>tvF6Pr>Sn{%+#P7I9e zrZzE`_Fqfo%t|nxFC=T)uzOU7!6ff*6U|hvS%*k{O@dJ#ZW9a1*H(ub466&*?~%jR zZQ^U(fbOh4Ov+S&iGANDs;C{F<&iQCU{b%di5sbn{h2W=hn--2jzwZ8I&bj`V^};1 z@>Q}(cxb%y9A)@ocGi%uZ5D~6sExfZXC}b7$`*+Zs?(aIO?GIXTe=Ko-)WI{|KBpk z=+`P|LQl_FBwSRcy9;$1o9duvc3vb}$;WD@vshIyzTFmy=V*WOJ!AAT(|Fkn#=YMn zaT3+#6*UF1PG5f~ZU|iLz(rz78948;1_xWy9L2R3oa4kr+WG4fk2g3LS4lT=Shq;* zLi@r$%9)v9GG{IlFP#bI+*-p9t1mB@2$*mV=`c3UoFz!*Yy1v2E*cUUXFcyc9Hh1$_Zy0 z44%uFp3_!>$$+_>#^ulGK z1@~`|GJ|BNv|ap-?0mu)R`zi)-W}V;mDDf3KTB%IaRth?OS{;Z>?}FkV3?f>FxfrZ z#Y1RD&fCwCGPPim2e)hINX|aj#87+ngK-^&m?=N!oo6t`_rFn0$F++)Dem>0GmhT} z!NgB$7qb#zn$I_EvAD;bvG|(Cnlly|3~P@SV6vAk7XIzP zj59{JlPfPfR;(07C>8xB_z)hbc>?KG{oXD<;eC`W%}4D%HSlRS5cxS!ho2aI8M2Eh2{ zFA-x@u6>sqzL=dcFrI}=#JAY7IlqxJ>xsEyiFh<;=X%2q^QByk_HpeJQAcyC#~Gvh zqPnjElfe8WcU*_x#`)UZi_VUnzK6yS&fB*{>`7zIiE*jFIGETSOT<>R|9FKltZV~d z;`c8RT?c}>_y)rcW5&R^K3XE4pnE#@yh+Ne2b2C9^|264_svp9xdyrZ5^F!v{`%V$ zQlVtrWLvK5#oZ#6MAw`c^Tp4FkP$If%4&nd%6>l4sDHhRf&#Al;dLQg=?oh#=4_4sR{JI=B8-w54nqxV3M+35YylQ#M=^bGX9 z5L-ch^!O&AEAy=Nl50`E$2%<76{&=t#P%m!*sq1|IJZMPZ!gJDBXl2hOlJz?!*8m; z9_VrCx#xE~Z!_AE9)CY{*LfY{^y2MxeHgk5z0pFSfS!SVjD=ou9mc`)t?RE6y3atg%(oooNlQ_wMWEG)kse-CuUXRY@`k3pBkufXOo^fYuo&CT&UKL)XV#w0QG zJA|W{!97aHV_iau({(+V3p>o~!Rc|jp{olz%X1GIM0!2+ zw2dBx?hbc|L9hko(c4rn^eps%g+2h?7wIss6`^lG3OxzkYOJ1u?uc6JWq&{$fu4Id z)0r^Ztlqb(psTGN<~2oFzi9xYv~@_|HB32bhaQ9OwJ2jB^fYu?4is!N2tD;ihiIcV zaX5=nZxiHW9E|7h*89Bm(0z+KwChjc^X&>>Y5VeC>@%QSUAvnNJq!D_bR7RGi-C=~ zJ}{oK4()wpjysH8vbNg|#{Is|)X15BFv`a|(<5g_z_>osnGrdY1>>018P}cCc*<@< zUfMgv_cSlAXAGNPRugk+huDFbK{+!IOzd*xpN`{7{$luIc4A<%*$#0ZjcbjJVfDBY zOmaE)2dI;R{(|2MK5L*SzSV6kliQd8leVxybzO2Z+Wb_9cI^+p^KC(I^Oew(&~tI# zCihVXCTrnCfk{7f*R>tmJ@ii$*vQ`>9|z+D^C$}He7S%rcn)=txF68JsO=rK(#S1q zd*fhI>#&cc{6`tX+MeSUw5uCC#O2hFR?C?RFv*)bL<=4N&A7|(#q89Qo!h`r+&9V@ zKbZKPXft&D(F0CqAJ%%0DuExN4N;}0Lkay>i{RYF% z8LtPE0doy8r^*?n4`ba{o#I*YHJ>rMFKRnAU_85YYR~Hj?lpYrZD#@W>|gcqw1era zu6n?@l9(q@e@ZZhm1z~23`rxzhFd_H?SQ(znyc3M6oroi-!KV!TE zV;A#mVlJ}xCj)Q@gj$B0pI$sbkPr zX$)E}w^OzPzUFj__vzf4W0k4iXTWwA=heNle9eSqvk$0e#l`)rM*F)$f0vh@O~kFJC6383zZZ6Kd` z=;PSw^$wIE^228xAQ`}X|=5z2@l7n% zoVBBBocFG=LZN!Lo9+)(kR*I!U&cwi|lS{Q{ovIka_6;k+xV~B{;*`H*8Kd`m zs?T9CiGMA%UDr?nmj##ndZ}1QG4(TB`a3PjR@v>ybEs2W3kkpT?Hm54&t(%g8=U$- za0|d?Ho#HcEeEG$mx@{V?i^&X>HTUIbl1}-+dKUT_6zj!>cBZd2IPSnW_C5LwG0Wvl1(?JSOSLt< zlV*lut_9;>+$o+Yj-?jkN$xbbHos88Io2)Jo)LWL!Gf|)$J7fZ4aRQ$m_abUpO)IL zA2bdo_Om_XxD)lUeyR3c0&N^zY7^boeT-wa9DhOGI=gJ= zYGkVtTo&~^AKwew8k-l^f$=Tr)ZR_I@54syv`ZXD^QF5PrcdfoWAT3u77|&&Tz5XI+8p+P~ zUD`chdp;_)vmA_T$1d&pk_81!{(5y1Od3ot#k@q$jDm^p)TKT98ZTh-*RCUD~nK#g7-nJbj!%Tn#w)vQ9CRj-jt&oYo#Oqte?yINueW;wR!B zVB9Ijb^SQ`+_g*WNNp|47*@9fWM{W7aTzfu4;I8VJ%)~f$^NQKd&loG#<2dnp6vWz zm*xJW{2ugI=(ka>zkNdL#{)*)qf2`x-ua}#urX}`7|)(v+B5OLk~2METoql~v;2q3 znN?sMd+Foe*>WZgCJW}RT>D@Q%ik0+dv|Hi7TsWGXx}=cAMsS8eUP0rWAuG%HFO{J z-2I~^PZ{NAe&&J6{zi{wzMP2>vu~HSKU^(mR)WdQ>=F|c%iD5hnC$#kZ>w7lN#mI$ z=68sh?BJ81FR++h_o7_;b(!DG!I&yAin~i&8^gj*1DFJu`)qu5g7NO(CAOtLXkjM- zMm?ZQyhu!?yj*L*_^#IX8EuSVZE%8^D(nl$&drQr6O8YeE|DQS`>vLj%L~SHY?nx(LY@0PYhtKx zMajs|FmX_BN!}}E-noX zfTQ!O%qzYp@7HhXz}+o10`N3AS>VNixyJU=b_^L9lda5Cns0SEURVX}_UU zO<4rL&olST%yaKOcl=(j%;=!+=c7_-4*Sq>&ux6FL5;9t~C z5=`P)?Efj=s*z%uSS;gUe5>4JnT8&M{wcN5Ma%|^WzGZ0-90U$jIQ~5M$OCz6MCve z{2%4z@#l-NFgqc#^Gu5vrE zUF)^WOGP_!O_Oqypm<>CYRU~hg?+ZnO^R%svCMqO%gS*OqH4` zdkAf3k+c&~GgV-cd1j%S34+nily(}`OfwiG&$OtSI2cbz+A-A38ZhBJvr5fmz-02w zu$u8cjJi5Y`WjO+m0(hNCZlHR!1&LWcBa)#7)&D1l)d7pFY3oJFurq^i2ip7O6Q6F;#;z&Ru3c!>ifC<=PyTp&pFqJn6IB z!dS|ngG=POThtu+90V6QU-}%fa0Q=ZV3J^Rc^kDb7N67Lf{l`U&z+k)gnZ<=X*EZ& z)qpcDkg2_~11MHZ&u zlWch(MLwFOt!4{XuvG~r1tw>!+rkuVQN0Jjg)WxdEozQ@E&=DcBwwEvuHbVem@pW# zJ}peaC)pYS=lh+E|2+$rvqj7#7(LH?p=Qb-Lw)C&4PSNCBiZqT35BJv?bXaeFzGzA zx0+c3CUB{=bC{Z02_~6mPF6E1Fxq7@j~A+$bzlrI7a@M{GBs27IO^l~lDR?6RDlWS znY+|X5KJb|JgH`y!33M7uh-N}984EW&P0hr>r1Q)rYGxQrV2QNTu4X2{B=bz4n(;h= z_VWj6=Uz2a0mjHP�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